Css-in-js는 css-in-css보다 더 강력한 추상화 수준을 제공,
Css-In-Js를 활용하면 자바스크립트로 스타일을 선언적이고 유지보수할 수 있는 방식으로 표현할 수 있다.
💡 인라인 스타일 HTML 요소 내부에 직접 스타일을 적용하는 방식을 말함. HTML 태그의 style 속성을 사용하여 인라인 스타일을 적용할 수 있다.const textStyles={
color:white,
backgroundColor:black
}
const SomeComponent ={
return(
<p style={textStyles}>inline style</p>
)
}
<p style="color:whtie; background-color:black">style</p>
import styled from "styled-components"
const Text= styled.div`
color: white;
background: black;
`
const Example = () => <Text>{Hello css-in-js}</Text>
인라인 스타일은 DOM 노드에 속성으로 스타일을 추가한 반면에 css-in-js는 DOM 상단에
css-in-js를 사용하면 실제로 css가 생성되기 때문에 미디어 쿼리, 슈도 선택자 등과 같은 css 기능을 손쉽게 누릴 수 있다.
컴포넌트로 생각할 수 있다: Css-In-Js는 스타일을 컴포넌트 단위로 추상화하여 생각할 수 있게 해준다.
부모와 분리할 수 있다: css에는 명시적으로 정의하지 않은 경우 부모 요소에서 자동으로 상속되는 속성이 있다. Css-In-Js는 이러한 상속을 받지 않는다. 따라서 각 부모 컴포넌트의 스타일은 부모와 독립되어 독립적으로 동작한다.
스코프를 가진다: CSS는 하나의 전역 네임스페이스를 가지기 때문에 선택자 충돌을 피하기 어렵다. 하나의 프로젝트 내에서는 BEM 같은 내용 컨벤션이 도움을 줄 수 있지만, 서드 파티 코드를 통합할 때는 도움이 되지 않는다. Css-In-Js는 css로 컴파일될 때 고유한 이름을 생성하여 스코프를 만들어준다. 따라서 선택자 충돌을 방지할 수 있다.
자동으로 벤더 프리픽스가 붙는다: Css-In-Js 라이브러리들은 자동으로 벤더 프리픽스를 추가하여 브라우저 호환성을 향상
자바스크립트와 css 상수와 함수를 쉽게 공유할 수 있다: Css-In-Js를 활용하며 자바스크립트 변수, 상수, 함수를 스타일 코드 내에서 쉽게 사용할 수 있다. 이를 통해 스타일과 관련된 로직을 함께 관리할 수 있다.
💡 BEM(Block Element Modifier)
css 클래스 네이밍 컨벤션의 한 형식을 의미한다. BEM은 선택자 충돌과 유지보수 문제를 해결하기 위해 개발된 방법론으로 다음과 같은 구조로 클래스를 작명하고 구성하게 된다
💡 벤더 프리픽스(Vender Profix)
웹 브라우저마다 지원되는 CSS 속성이나 기능이 다를 때 특정 브라우저에서 제대로 동작하도록 하기 위해 추가되는 접두사를 말한다.
규모가 큰 동적인 웹 애플리케이션을 유지보수하기 위해 해결해야 할 CSS의 문제점을 7가지로 분류하여 설명하면서 해결책으로 Css-In-Js 개념을 제시했다.
크리스토퍼 쉬도가 언급한 css의 7가지 문제점은 다음과 같다.
리액트 컴포넌트를 구현할 때 여러 옵션을 props로 받아 유연한 컴포넌트를 구현할 수 있다.
컴포넌트의 background-color, size와 같은 값도 props로 받아와서 상황에 맞는 스타일로 구현하는 경우가 많다. 이때 스타일 관련 props는 Styled-components로 전달되는데 해당 타입을 styled-components에서도 정의해줘야 한다. 보통 styled-compoents에 넘겨주는 타입은 props에서 받은 타입과 동일. 이때 타입스크립트에 제공하는 Pick,Omit같은 유틸리티 타입을 유용하게 활용
수평을 그어주는 HrComponent이다. HrComponent는 className, 수평선의 높이, 색상 값 그리고isFull 속성을 props로 받는다. isFull 속성은 화면 좌우 기본 패딩값을 무시하고 꽉 찬 수평선을 만들고 싶을 때 사용하는 속성. props의 color 타입에서 사용되는 colors는 색상 값만 따로 객체로 관리하고 각 컴포넌트에서 임포트하여 사용하고 있다.
HrComponent의 props 중 height, color, isFull 속성은 styled-components인 HrComponent에 그대로 prop으로 전달되기 때문에 타입이 같다. 여기서는 아래 코드처럼 height, color,isFull에 대한 Styledprops 타입을 새로 정의하여 HrComponent에 적용했다.
이때 props와 똑같은 타입임에도 StyledProps를 따로 정의해줘야 하는 점 때문에 코드 중복이 발생
또한 props의 height, color, isFull 타입이 변경되면 StyledProps도 변경되어야 한다.
import styled from 'styled-components';
interface Props {
height?: string;
color?: keyof typeof colors;
isFull?: boolean;
className?: string;
}
export const Hr: VFC<Props> = ({ height, color, isFull, className }) => {
return <HrComponet height={height} color={color} isFull={isFull} className={className} />;
};
const StyledProps{
height?: string;
color?:keyof typeof colors;
isFull?:boolean;
}
const HrComponent = styled.hr<StyledProps>`
height: ${({height})=>height || "10px"};
margin:0;
background-color: ${({color})=>colors[color || "gary7"]};
border: none;
${({isFull})=>
isFull && css`
margin: 0 -15px;
`}}
`;
이 코드에서는 Pick 유틸리티 타입을 사용하여 styld-component 타입을 작성
이처럼 styled-components에 적용하는 것뿐만 아니라 상속받는 컴포넌트나 부모 컴포넌트에서 자식 컴포넌트로 넘겨주는 props 등의 경우에도 Pick이나 Omit 같은 유틸리티 타입을 활용하면 중복되는 타입을 피할 수 있어 유지보수적인 측면에서 긍정적인 효과를 얻을 수 있다.