내가 styled-components 방식보다 css props을 더 선호하는 이유

2024-07-26

저는 취업 준비생 시절부터 첫 직장까지 쭉 styled-components로 개발을 해왔어요. 작년에는 이직을 하면서 처음으로 Emotion을 사용하게 되었는데, Emotion은 styled-components와 비슷한 방식으로 코드를 작성할 수 있으며, CSS prop 방식으로도 코드를 작성할 수 있습니다. 두 가지 방식을 모두 사용해본 결과, 개인적으로 Emotion의 CSS prop 방식이 훨씬 더 많은 장점을 가지고 있다고 느꼈습니다. 그 이유를 정리해보았습니다.

styled-components의 불편함

1. 가독성 문제

styled-components는 일반적인 컴포넌트처럼 생겨서, 코드에서 styled-components인지 그냥 컴포넌트인지 구별하기가 어렵습니다. 아래 예시 코드는 단순히 styled 방식으로 버튼을 꾸민 코드이지만, 실무에서는 더 복잡한 코드에서 가독성이 아쉬웠습니다.

1export const App = () => {
2  return (
3    <div>
4      <Button isPrimary>Button</Button>
5      <Button isPrimary={false}>Button</Button>
6    </div>
7  );
8};
9const Button = styled.button<{ isPrimary: boolean }>`
10  padding: 3px 12px;
11  background-color: ${({ isPrimary }) => (isPrimary ? "#BF4F74" : "white")};
12  color: ${({ isPrimary }) => (isPrimary ? "white" : "#BF4F74")};
13  font-size: 18px;
14  border-radius: 3px;
15  border: 2px solid #bf4f74;
16`;

그래서 가독성을 개선하기 위해 S를 붙이는 방식을 사용하기도 했습니다. 이렇게 하면 가독성이 훨씬 개선된다고 느꼈습니다.

1export const App = () => {
2  return (
3    <div
4      css={css`
5        display: flex;
6        justify-content: center;
7        align-items: center;
8        width: 100%;
9        height: 100vh;
10        gap: 30px;
11      `}
12    >
13      <S.Button isPrimary>Button</S.Button>
14      <S.Button isPrimary={false}>Button</S.Button>
15    </div>
16  );
17};
18
19const S = {
20  Button: styled.button<{ isPrimary: boolean }>`
21    padding: 3px 12px;
22    background-color: ${({ isPrimary }) => (isPrimary ? "#BF4F74" : "white")};
23    color: ${({ isPrimary }) => (isPrimary ? "white" : "#BF4F74")};
24    font-size: 18px;
25    border-radius: 3px;
26    border: 2px solid #bf4f74;
27  `,
28};

2. 태그 사용의 불투명성

styled-components를 사용할 때, 어떤 태그를 사용하고 있는지 알아보기 힘들어 불편합니다. 디버깅할 때도 스타일이 지정된 코드를 확인해야 하므로 번거로움이 있습니다.

CSS prop 방식을 사용하면 아래와 같이 작성할 수 있어, 어떤 태그를 사용하고 있는지 한눈에 확인할 수 있습니다.

1export const App = () => {
2  return (
3    <div
4      css={css`
5        display: flex;
6        justify-content: center;
7        align-items: center;
8        width: 100%;
9        height: 100vh;
10        gap: 30px;
11      `}
12    >
13      <button css={[Button.common, Button.primary]}>Button</button>
14      <button css={[Button.common, Button.secondary]}>Button</button>
15    </div>
16  );
17};
18
19const Button = {
20  common: css`
21    padding: 3px 12px;
22    font-size: 18px;
23    border-radius: 3px;
24    border: 2px solid #bf4f74;
25  `,
26
27  primary: css`
28    background-color: #bf4f74;
29    color: white;
30  `,
31
32  secondary: css`
33    background-color: white;
34    color: #bf4f74;
35  `,
36};

CSS Prop 방식의 장점

CSS prop 방식은 분기 처리가 필요한 스타일이 많을 때 특히 유용합니다. 버튼의 사이즈가 small, medium, large가 있고 각각 다른 스타일을 줘야 할 경우, CSS prop 방식을 사용하면 아래와 같이 코드를 작성할 수 있습니다.

1export const App = () => {
2  return (
3    <>
4      <button css={[Button.common, Button['small']]}>button</button>
5      <button css={[Button.common, Button['medium']]}>button</button>
6      <button css={[Button.common, Button['large']]}>button</button>
7    </>
8  );
9};
10
11const Button = {
12  common: css`
13   
14    border: 2px solid #bf4f74;
15    font-weight: 500;
16  `,
17
18  small: css`
19    // small 사이즈에서 사용하는 css속성을 추가
20    font-size: 12px;
21    line-height: 16px;
22    max-height: 28px;
23    padding: 6px 10px;
24    border-width: 1px;
25    border-radius: 6px;
26  `,
27
28  medium: css`
29    // medium 사이즈에서 사용하는 css속성을 추가
30    font-size: 14px;
31    line-height: 18px;
32    max-height: 34px;
33    padding: 8px 12px;
34    border-width: 1px;
35    border-radius: 8px;
36  `,
37
38  large: css`
39    // medium 사이즈에서 사용하는 css속성을 추가
40    font-size: 16px;
41    line-height: 20px;
42    max-height: 42px;
43    padding: 12px 20px;
44    border-width: 1px;
45    border-radius: 8px;
46  `,
47};

물론 styled-components 방식으로도 비슷하게 작성할 수 있지만, handleButtonStyle 함수의 return 문은 그냥 문자열이기 때문에 CSS 자동 완성이 뜨지 않고, Syntax highlighting도 되지 않아 코드 작성 시 불편합니다.

1type ButtonSize = 'small' | 'medium' | 'large';
2
3export const App = () => {
4  return (
5    <>
6      <Button size='small'>button</Button>
7      <Button size='medium'>button</Button>
8      <Button size='large'>button</Button>
9    </>
10  );
11};
12
13const handleButtonStyle = (size: ButtonSize) => {
14  switch (size) {
15    case 'small':
16      return `
17      font-size: 12px;
18      line-height: 16px;
19      max-height: 28px;
20      padding: 6px 10px;
21      border-width: 1px;
22      border-radius: 6px;
23    `;
24
25    case 'medium':
26      return `
27        font-size: 14px;
28        line-height: 18px;
29        max-height: 34px;
30        padding: 8px 12px;
31        border-width: 1px;
32        border-radius: 8px;
33      `;
34    case 'large':
35      return `
36        font-size: 16px;
37        line-height: 20px;
38        max-height: 42px;
39        padding: 12px 20px;
40        border-width: 1px;
41        border-radius: 8px;
42      `;
43  }
44};
45
46const Button = styled.button<{ size: ButtonSize }>`
47  ${({ size }) => `${handleButtonStyle(size)}`}
48  /* common style */
49  padding: 3px 12px;
50  font-size: 18px;
51  border-radius: 3px;
52  border: 2px solid #bf4f74;
53  font-weight: 500;
54`;

가독성을 저는 항상 중요하게 생각합니다. 사소한 것일 수 있지만 나중에 유지보수에 미치는 영향이 크더라구요. 그래서 여러가지 방법을 고민하다가 현재는 이 방식으로 정착을 한 상황입니다. 더 좋은 방법을 발견하게 되면 또 포스팅하겠습니다!

© 2025 Kae All Rights Reserved.