c-h-w-h / cds

🧊 차가운 디자인 시스템

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Fix: DefaultProps css 속성 이름 변경

prayinforrain opened this issue · comments

👨‍🔧 버그 수정 사항

제가 맡기로 했는데.. 지금은 안 할 거고 까먹을까 봐 미리 이슈로 적어둬요

  • DefaultProps, DefaultPropsWithChildren 타입의 css 속성 이름 수정

이유는 차가운 게시판을 참고해 주세요. 대충 emotion이 css라는 이름의 속성을 사용하고 있기 때문에 의도치 않은 동작을 할 위험이 있어요.
사실 모로 가던 도로 가던 도착은 하는데 지금 상태에서 css라는 속성은 계속 undefined로 넘어오게 돼요. 이상해요

css 속성의 새 이름을 추천받아요. materal-ui에서는 cx였던 것 같아요

📖 참고 사항

지금 각자 브랜치에서 작업중일 수도 있는데 어쩌죠? 마땅한 해결책이 안떠올라요.
그냥 각자 브랜치에 업데이트 받아서 PR 쓰는게 제일 나을 것 같긴 해요 어차피 스쿼시 머지라 ㅋㅋ...

이름만 바꾼다고 되는 게 아니였어요...

우선 DefaultProps의 css property는 Interpolation<Theme> 타입이에요.
그런데 { css } from '@emotion/styled'가 만들어내는 객체는 SerializedStyles 타입이에요.
여태껏 별다른 에러 없이 의도한대로 작동하는것처럼 보였기 때문에 이 두 타입이 어떻게든 형변환이 되나보다 대충 넘겨짚고 있었는데..

지금까지 커스텀 스타일을

css?.toString()

처럼 덧붙이는 형태로 사용했었는데 이 사용법에 문제가 있어요. 참고할만한 사용사례는 Flexbox에요.

앞서 말했듯이 { css } from '@emotion/styled'는 아래와 같은 SerializedStyles 타입의 객체를 반환해요.
image
그런데 이 객체의 toString 메소드는 아래와 같은 문자열을 반환해요.

You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop).

즉 저 css?.toString()이라는 부분은 애초에 아무 기능을 할 수가 없어요.
우리가 지금까지 생각했던 사용방식은 잘못된 것이였어요... 😥

너무 복잡한 상황이라 정리가 깔끔하게 안되는데, 당장 떠오르는 선택지는 두 가지가 있을 것 같아요.

  • css props를 제거한다.
    • 사실 emotion이 css props를 이미 컴포넌트의 최상위 element에 렌더링해 주기 때문에 굳이 우리가 css props를 따로 뚫어 줄 이유가 없어보여요.
    • 아래 케이스도 정말 한정적이고 사실 저런 복잡한 컴포넌트라면 오히려 css를 넘겨준다는 발상 자체가 좀 이상할 것 같아요.
  • 유지하되 React.CSSProperties 타입으로 변경한다.
    • 이러면 커스텀 스타일을 최상위가 아닌 element에 넘겨줘야 할 때 활용할 수 있어요.
    • 실제로 사용되진 않았지만 예시로는 Modal 컴포넌트가 있어요.
      • Modal 컴포넌트에 커스텀 스타일을 넘겨야 한다면 최상위 컴포넌트인 ModalWrapper가 아닌 ModalBox에 css를 넘겨주어야 할 거에요.
      • 이런 상황에서 props로 받은 css를 직접 전달해 줄 수 있어요.
    • 참고로 이렇게 변경될 경우 props에 들어가는 형태는 { 'backgroundColor': 'red' }처럼 key-value object로 넘겨주어야 해요.

그리고.. 의사 결정이나 도움이 필요한 상황을 표시하기 위해 help wanted 라벨을 만들었어요.

세영: 우재님 이거 이번에 하고있는 작업들 싹 머지되면 props 이름 바꾸고 Flexbox 컴포넌트만 수정하면 되는거 아닌가요 ..??!

이우재: 이름을 바꿨더니
적용이 안돼요
그러니까 애초에 멀쩡해보이게 움직였던 이유가 우리가 잘 붙인게 아니라 emotion이 정해준 className이 ...props 스프레딩으로 추가되어서 작동한거였어서
css 프로퍼티 이름을 변경하면 저게 동작하지 않아서 우리가 실제로 css를 컴포넌트에 붙여줘야 하는 상황인데
css``로 만들어지는 object로는 그게 불가능해요

세영: 아아 이해했어요
css로 들어가있던 애들은 사실 이모션 컴파일 단계에서 전부 className으로 변환되는거죠?

이우재: 네맞아요

세영: 음 저거 처음에 만든 이유가
적어두신 > Modal 컴포넌트에 커스텀 스타일을 넘겨야 한다면 최상위 컴포넌트인 ModalWrapper가 아닌 ModalBox에 css를 넘겨주어야 할 거에요.
이런거보다는 기존 컴포넌트에 커스텀 스타일을 추가하고 싶은 경우가 있어서 만들었는데
동작이 이런거면 props 정의안해도 사용 가능한거니까 빼도 괜찮을 것 같은데요?
근데 이건 Props 정의에서 빼는게 아니라 사용한 부분들을 다 뺴면 될 것 같아요
Props에서 빼면 컴포넌트에 css 자체를 아예 못넣을거에요 ..!

이우재: 아 그 확인했는데
타입에서 제거해도 뭔가 기본 프로퍼티로 정의되어있어요

세영: 아하 그래요 ??!

이우재: emotion이 뭔가 조작하나봐요

세영: jsx import source에서 넣어주나봐요

이우재: 아직도 어떻게 되어있는지 모르겠어요.....

세영: 그럼 빼면 될거같아요 !!!!


게더에서 토론한 결과 일단 삭제하는 것으로 정했습니다. css props는 그대로 사용이 가능하기 때문에 큰 변화는 없을 거에요(아마)

commented

생긴것만 보고 css props를 props라고 생각한게 문제였어요.
이번 기회에 CSS-in-JS가 어떻게 컴파일되는지 생각해보면 좋겠네요 ^^

image

https://emotion.sh/docs/css-prop#import-the-jsx-function-from-emotionreact

(cds도 tsconfig에서 jsxImportSource 설정해주고 있어요!)
    "jsx": "react-jsx",
"jsxImportSource": "@emotion/react",

얘가 수상해서 이모션 열어봤더니 css props가 있는 경우에는 일반적인 jsx랑 다른걸 리턴해요.

// @flow
import * as ReactJSXRuntime from 'react/jsx-runtime'
import Emotion, { createEmotionProps } from './emotion-element'
import { hasOwnProperty } from './utils'

export const Fragment = ReactJSXRuntime.Fragment

export function jsx(type: any, props: any, key: any) {
  if (!hasOwnProperty.call(props, 'css')) {
    return ReactJSXRuntime.jsx(type, props, key)
  }

  return ReactJSXRuntime.jsx(Emotion, createEmotionProps(type, props), key)
}

저 Emotion, createEmotionProps 안에 보니까 우재님이 예상한 것처럼 css props를 컴파일해서 내부 style 태그와 className으로 관리하고 있었어요.

const Insertion = ({ cache, serialized, isStringTag }) => {
  registerStyles(cache, serialized, isStringTag)

  const rules = useInsertionEffectAlwaysWithSyncFallback(() =>
    insertStyles(cache, serialized, isStringTag)
  )

  if (!isBrowser && rules !== undefined) {
    let serializedNames = serialized.name
    let next = serialized.next
    while (next !== undefined) {
      serializedNames += ' ' + next.name
      next = next.next
    }
    return (
      <style
        {...{
          [`data-emotion`]: `${cache.key} ${serializedNames}`,
          dangerouslySetInnerHTML: { __html: rules },
          nonce: cache.sheet.nonce
        }}
      />
    )
  }
  return null
}

let Emotion = /* #__PURE__ */ withEmotionCache<any, any>(
  (props, cache, ref) => {
    let cssProp = props.css

...

    return (
      <>
        <Insertion
          cache={cache}
          serialized={serialized}
          isStringTag={typeof WrappedComponent === 'string'}
        />
        <WrappedComponent {...newProps} />
      </>
    )
  }
)

https://github.com/emotion-js/emotion/blob/main/packages/react/src/jsx-runtime.js
https://github.com/emotion-js/emotion/blob/314a5fb08b0f730e7aa88da0c974dfea13cc9b32/packages/react/src/emotion-element.js#L84
https://github.com/emotion-js/emotion/blob/314a5fb08b0f730e7aa88da0c974dfea13cc9b32/packages/react/src/emotion-element.js#L19

  • #44 (comment)
    선민님이 확인해주셔서 봤는데 제가 bind의 사용법을 잘못 이해하고 있었어요. 저 PR로는 해결이 안돼요..

DefaultProps에서 css를 지우면 Template.args에서 TypeError가 나지만 막상 실행해보면 className으로 알아서 변환되어 들어가고 있어요. 이건 우리가 처음 생각한 대로에요.
만약 컴포넌트의 Props에는 css를 명시하지 않고, story의 args 타입에서는 css를 가지고 있도록 할 방법이 있다면 {...props}로 스프레딩해도 괜찮을 것 같은데 그 방법을 모르겠어요. args에 style: {}로 스타일을 지정할 수 있긴 한데 잘...모르겠어요.

그래서 다른 예제가 없을까 찾아봤는데 애초에 추가 스타일을 지정한 예시를 아직 못찾았어요.
생각해 보면 args는 UI의 usecase의 경우의 수를 나타내는? 옵션인데 이걸 극적으로 보여주기 위해 args에 추가 스타일을 지정하는게 잘못된건가 싶기도 하고 답을 못내리겠어요. 흠..
제가 뒤져본 레퍼런스는 여기에요. 스토리북 공홈에서 예제 프로젝트들을 공개해뒀네요..