소프트웨어 엔지니어는 항상 확장성과 재사용성을 찾습니다.
해당 기능들은 관심사의 분리 / 합성을 통해 구현됩니다.
위 두 가지 관점에서 Styled Component(Css-in-js 대표)와 Tailwind CSS를 비교해 봅니다.
Styled Components가 확장성 / 재사용성을 구현하는 방법
주 : 몇몇 경우 타입스크립트를 이용한 타입 안전성을 제공하기 위한 hack이 필요합니다.
1. ThemeProvider
마크업 구조, 컴포넌트를 변경하지 않으면서 디자인 토큰만 변경하기 위한 추상화 방법.
css 변수를 직접 임포트해도 되긴 됩니다.
https://styled-components.com/docs/advanced#theming
const Button = styled.button`
color: ${props => props.theme.fg};
border: 2px solid ${props => props.theme.fg};
background: ${props => props.theme.bg};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
`;
2. Variant (Props)
https://stitches.dev/docs/variants#adding-variants
해당 방법을 가장 잘 사용하는 stitches.js의 예시를 가져옴
여러 CTI를 하나로 프롭으로 뭉쳐 디자인의 변형을 제공.
* 클래스명을 사용하지 않음
const Button = styled('button', {
// base styles
variants: {
color: {
violet: {
backgroundColor: 'blueviolet',
color: 'white',
'&:hover': {
backgroundColor: 'darkviolet',
},
},
gray: {
backgroundColor: 'gainsboro',
'&:hover': {
backgroundColor: 'lightgray',
},
},
},
},
});
() => <Button color="violet">Button</Button>;
3. CSS variables
마크업은 그대로 두고 CSS 선언에 바인딩된 값을 바꾸는 방식으로 사용
참고 : https://itchallenger.tistory.com/592
function Backdrop({ opacity, color, children }) {
return (
<Wrapper
style={{
'--color': color,
'--opacity': opacity,
}}
>
{children}
</Wrapper>
)
}
//
4. CSS helper function
css를 관심사에 따라 잘 잘라서 합성할 수 있음
https://styled-components.com/docs/api#css
import styled, { css, keyframes } from 'styled-components'
const pulse = keyframes`
0% {
opacity: 0;
}
100% {
opacity: 1;
}
`
const animation = props =>
css`
${pulse} ${props.animationLength} infinite alternate;
`
const PulseButton = styled.button`
animation: ${animation};
`
5. 캡슐화
테일윈드의 유틸리티 클래스만으로 구성하기 어려운게
아래와 같은 컨텍스트 기반 CSS임
스타일드 컴포넌트를 활용하면 컨텍스트에 영향받는 자기자신의 CSS를 해당 컴포넌트 내에 전부 캡슐화할 수 있음.
해당 방법은, 레이아웃을 포함한 컴포넌트가 좀 더 구체적인 컴포넌트라서,
추상적인 자식 컴포넌트의 스타일을 오버라이딩 한다는 관점에서 생각하면 별로이고
CSS의 캡술화 관점에서 생각하면 더 나은 방법인것 같음.
// TextLink.js
import { Wrapper as AsideWrapper } from '../Aside'
const TextLink = styled.a`
color: var(--color-primary);
font-weight: var(--font-weight-medium);
${AsideWrapper} & {
color: var(--color-text);
font-weight: var(--font-weight-bold);
}
`;
6. 합성
compoition을 이용해 가장 제너럴한 TextLink, Div 등의 컴포넌트를 확장.
프로젝트 내의 모든 TextLink는 해당 컴포넌트를 사용해야 함.
즉, TextLink 컴포넌트가 단일 진실 원천(SSOT)임
(물론 컴포넌트 제약 관점에서 그렇다는거지 실제로 디자인 요구사항의 SSOT는 디자인 토큰임)
import TextLink from '../TextLink';
const SpookyTextLink = styled(TextLink)`
font-family: 'Spooky Font', cursive;
`;
스타일드 컴포넌트 명은 최대한 스타일을 반영하며 일반적으로 만들어야 함.
예를 들어 BioArticleText 이런건 컨텐츠 컨텍스트가 반영되어 재사용성이 떨어짐.
컨텐츠, 컨텍스트, 컴포넌트(ex 카드 컴포넌트)에 대한 정보를 스타일드 컴포넌트에서 제거히야 합성 및 재사용성이 용이함.
Tailwind CSS가 확장성 / 재사용성을 구현하는 방법
테일윈드 CSS는 클래스명이라는 간접 계층을 두어 디자인 세부사항과, 마크업 구조가 독립적으로 진화할 수 있도록 함.
마크업(컴포넌트)은 스타일에 대한 정보와 스트럭처(구조)에 대한 정보가 반영되는 것이 당연하다는 멘탈 모델에서 온 것임
(이는, 스타일드 컴포넌트도 마찬가지며 렌더링 관심사까지 컴포넌트에 함께하면 리액트가 됨)
1. configuration
디자인 토큰을 이용해 설정을 바꿀 수 있음.
클래스명 API는 동일함
마크업(구조, 클래스명 포함)을 그대로 두고 스타일 변경
2. 스타일은 반드시 클래스명
스타일드 컴포넌트의 프롭스, 버라이언트는 마크업에 반영됨(탬플릿을 통한 추상화)
Tailwind CSS는 반드시 클래스명 기반으로 디자인의 관심사를 처리함.
const ConfirmButton = (props) => {
const { className, ...rest } = props;
return (
<button
className={classnames(
'bg-black text-red-400',
className,
)}
{...rest}
></button>
);
});
3. 유틸리티 퍼스트 프레임워크 : (횡단 관심사 / 합성)
각 디자인 관심사를 클래스명으로 잘게 분리하여 합쳐서 사용 (예제는 trivial하므로 생략...)
- Text sizes, colors, and weights
- Border colors, widths, and positions
- Background colors
- Flexbox utilities
- Padding and margin helpers
추가로 tailwind의 장점은 css를 최대한 thin하게 유지할 수 있다는 것임.
스타일드 컴포넌트는 컴포넌트의 증가 > 스타일 규칙의 증가임.
tailwind css는 큐레이팅된 css 규칙 룰 셋을 유지할 수 있음.
마치며 : 컴포넌트와 API
스타일드 컴포넌트, 테일윈드 CSS로 만드는 아토믹 컴포넌트는
데이터, 컨텍스트에 독립적인 것이 좋음.
대신 아토믹한 컴포넌트를 감싸는 컴포넌트를 하나 만들어서,
해당 컴포넌트에서 데이터에 대한 관심사를 처리하여,
하위 컴포넌트에 바인딩(렌더링)하는 로직을 따로 처리하는게 좋음
이를 통해 데이터, 컨텍스트, 통합에 대한 관심사에서
렌더링, 마크업 구조, 스타일에 대한 관심사를 분리할 수 있음.
참고 :
https://itchallenger.tistory.com/594
https://fe-developers.kakaoent.com/2022/220303-tailwind-tips/
https://itchallenger.tistory.com/779
'FrontEnd' 카테고리의 다른 글
Critical Rendering Path 최적화하기 (0) | 2022.10.31 |
---|---|
리액트와 유닛 테스트를 위한 클린 아키텍처 [부제, 의존성 주입은 정말 필요한가?] (0) | 2022.10.30 |
CSR 성능 최적화를 위한 PRPL 패턴 with React (0) | 2022.10.29 |
개발자 관점에서 Next13 간단히 살펴보기 (6) | 2022.10.28 |
[프론트엔드 인터뷰 퀴즈] 정적 클래스 멤버(프로퍼티 / 메서드)를 사용하는 이유는 뭘까요? (0) | 2022.10.28 |