[Typescript] 우아한 타입스크립트(with 리액트) - 11장 css-in-js

rondido·2024년 8월 20일
0

Typescript

목록 보기
13/14

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>
  • Css-in-js 방식
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 기능을 손쉽게 누릴 수 있다.

  1. 컴포넌트로 생각할 수 있다: Css-In-Js는 스타일을 컴포넌트 단위로 추상화하여 생각할 수 있게 해준다.

  2. 부모와 분리할 수 있다: css에는 명시적으로 정의하지 않은 경우 부모 요소에서 자동으로 상속되는 속성이 있다. Css-In-Js는 이러한 상속을 받지 않는다. 따라서 각 부모 컴포넌트의 스타일은 부모와 독립되어 독립적으로 동작한다.

  3. 스코프를 가진다: CSS는 하나의 전역 네임스페이스를 가지기 때문에 선택자 충돌을 피하기 어렵다. 하나의 프로젝트 내에서는 BEM 같은 내용 컨벤션이 도움을 줄 수 있지만, 서드 파티 코드를 통합할 때는 도움이 되지 않는다. Css-In-Js는 css로 컴파일될 때 고유한 이름을 생성하여 스코프를 만들어준다. 따라서 선택자 충돌을 방지할 수 있다.

  4. 자동으로 벤더 프리픽스가 붙는다: Css-In-Js 라이브러리들은 자동으로 벤더 프리픽스를 추가하여 브라우저 호환성을 향상

  5. 자바스크립트와 css 상수와 함수를 쉽게 공유할 수 있다: Css-In-Js를 활용하며 자바스크립트 변수, 상수, 함수를 스타일 코드 내에서 쉽게 사용할 수 있다. 이를 통해 스타일과 관련된 로직을 함께 관리할 수 있다.

💡 BEM(Block Element Modifier)
css 클래스 네이밍 컨벤션의 한 형식을 의미한다. BEM은 선택자 충돌과 유지보수 문제를 해결하기 위해 개발된 방법론으로 다음과 같은 구조로 클래스를 작명하고 구성하게 된다

  • Block(블록): 컴포넌트나 모듈의 최상위 레벨 요소를 클래스 명은 중복되지 않아야 하며, 컴포넌트의 기본 스타일을 정의한다.
  • Element(요소): 블록 내부의 하위 요소를 나타낸다. 클래스 명은 블록의 클래스 명을 접두어로 가지며 블록 내부에서만 의미가 있다.
  • Modifier(수정자): 블록이나 요소의 상태나 특성을 나타내는 클래스를 추가한다. 이로써 특정 상황에 스타일을 변경하거나 동작을 제어

💡 벤더 프리픽스(Vender Profix)
웹 브라우저마다 지원되는 CSS 속성이나 기능이 다를 때 특정 브라우저에서 제대로 동작하도록 하기 위해 추가되는 접두사를 말한다.

Css-In-Js 등장 배경

규모가 큰 동적인 웹 애플리케이션을 유지보수하기 위해 해결해야 할 CSS의 문제점을 7가지로 분류하여 설명하면서 해결책으로 Css-In-Js 개념을 제시했다.

크리스토퍼 쉬도가 언급한 css의 7가지 문제점은 다음과 같다.

  1. Global NameSpace(글로벌 네임스페이스): 모든 스타일이 전역 공간을 공유하므로 중복되지 않는 css 클래스 이름을 고민해야 한다.
  2. Dependencies(의존성): css의 의존성과 자바스크립트의 의존성이 달라서 사용하지 않는 스타일이 포함되거나 꼭 필요한 스타일이 누락되는 문제가 발생(현재는 번덜러의 발전으로 거의 해결)
  3. Dead Copde Elimination(불필요한 코드 제거): 기능 추가 수정삭제 과정에서 불필요한 css를 삭제하기 어렵다.
  4. Minification(최소화): 클래스 이름을 최소화하기 어렵다.
  5. Sharing constans(상수 공유): 자바스크립트와 상태 값을 공유할 수 없다(이에 대한 해결책으로 현재는 css variable이 도입되어 css의 공식 기능으로 제공)
  6. Non-deteministic Resolution(비결정적 해결): css 로드 순서에 따라 스타일 우선 순위가 달라진다.
  7. Isolation(고립): css의 외부 수정을 관리하기 어렵다(캡슐화(

유틸리티 함수를 사용하여 Styele-components의 중복 타입 선언 피하기

리액트 컴포넌트를 구현할 때 여러 옵션을 props로 받아 유연한 컴포넌트를 구현할 수 있다.

컴포넌트의 background-color, size와 같은 값도 props로 받아와서 상황에 맞는 스타일로 구현하는 경우가 많다. 이때 스타일 관련 props는 Styled-components로 전달되는데 해당 타입을 styled-components에서도 정의해줘야 한다. 보통 styled-compoents에 넘겨주는 타입은 props에서 받은 타입과 동일. 이때 타입스크립트에 제공하는 Pick,Omit같은 유틸리티 타입을 유용하게 활용

props 타입과 styeld-componens 타입의 중복 선언 및 문제점

수평을 그어주는 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 같은 유틸리티 타입을 활용하면 중복되는 타입을 피할 수 있어 유지보수적인 측면에서 긍정적인 효과를 얻을 수 있다.

profile
풋살을 좋아하는 프론트엔드 개발자

0개의 댓글