2024-07-26
저는 취업 준비생 시절부터 첫 직장까지 쭉 styled-components로 개발을 해왔어요. 작년에는 이직을 하면서 처음으로 Emotion을 사용하게 되었는데, Emotion은 styled-components와 비슷한 방식으로 코드를 작성할 수 있으며, CSS prop 방식으로도 코드를 작성할 수 있습니다. 두 가지 방식을 모두 사용해본 결과, 개인적으로 Emotion의 CSS prop 방식이 훨씬 더 많은 장점을 가지고 있다고 느꼈습니다. 그 이유를 정리해보았습니다.
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};
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 방식은 분기 처리가 필요한 스타일이 많을 때 특히 유용합니다. 버튼의 사이즈가 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.