styled-components

Hyeon·2024년 8월 20일
0

styled-components란

styled-components란 Javascript 파일 내에서 CSS를 사용할 수 있게 해주는 대표적인 CSS-in-JS 라이브러리로 React를 주요 대상으로 한 라이브러리입니다.

Github: https://github.com/styled-components/styled-components

공식 문서: https://styled-components.com/docs

최신 버전 (2022.05.15 기준): v5.3.5

CSS-in-JS vs CSS-in-CSS

CSS-in-JS?

먼저 CSS in JS의 개념을 짚고 넘어가겠습니다.

CSS-in-JS는 스타일 정의를 CSS 파일이 아닌 JavaScript로 작성된 컴포넌트에 바로 삽입하는 스타일 기법입니다. 컴포넌트 내에 정의되어 있기 때문에 해당 컴포넌트가 렌더링 될 때에만 해당 스타일 정보를 읽어들입니다. 이러한 특징이 장점처럼 보일 수 있겠지만 브라우저의 렌더링이 StyleSheet와 Script로 나누어 병렬처리 되던 것이 오직 Script로 이루어짐에 따라 그만큼 속도가 느려집니다.

또한, CSS가 먼저 제공되어 렌더링 시 형태가 잡혀있는 기존 방식에 비해 컴포넌트가 렌더링 되며 형태가 잡히기 때문에 FOUC 현상이 발생되며 이는 사용자 경험을 저하시킵니다.

FOUC란 Flash Of Unstyled Content 의 약자로 스타일 시트가 적용되기 전 마크업 된 그대로의 모습이 잠깐 보이는 현상을 말합니다.

대표적인 CSS-in-JS 라이브러리가 styled-components 입니다.

CSS-in-CSS

다음으로 CSS-in-CSS 는 무엇인지 알아보겠습니다. CSS-in-CSS는 스타일 정의를 JS 파일과 분리하여 별도의 CSS 파일에서 관리되는 구조입니다. 그렇기 때문에 어떤 컴포넌트의 상태값이 변하더라도 이에 반응하기 쉽지 않으며 첫 렌더링시 브라우저에 보이지 않는 컴포넌트까지 읽어들이기 때문에 불필요한 컴파일 과정이 추가적으로 발생됩니다.

CSS-in-CSS 방식의 대표적인 라이브러리로는 현재 저희 팀에서 사용하고 있는 scss(sass) 가 있습니다.

왜 Styled-Components를 사용할까?

  • Automatic critical CSS

화면에 어떤 컴포넌트가 렌더링 되었는지 추적해서 해당하는 컴포넌트에 대한 스타일을 자동으로 삽입합니다. 따라서 코드를 적절히 분배해 놓으면 사용자가 애플리케이션을 사용할 때 최소한의 코드만으로 화면이 띄워지도록 할 수 있습니다.

  • No class name bugs

컴파일 때 자동으로 고유한 클래스 이름을 생성하여 중복, 오타로 인한 버그를 줄여줍니다.

  • Easier deletion of CSS

모든 스타일 속성이 특정 컴포넌트와 연결되어 있기 때문에 만약 컴포넌트를 더 이상 사용하지 않아 삭제할 경우 이에 대한 스타일 속성도 함께 삭제됩니다.

  • Simple dynamic styling

className을 일일이 수동으로 관리할 필요 없이 React의 props나 전역 속성을 기반으로 컴포넌트에 스타일 속성을 부여하기 때문에 간단하고 직관적이다.

  • Painless maintenance

컴포넌트에 스타일을 상속하는 속성을 찾아 다른 CSS 파일들을 검색하지 않아도 되기 때문에 코드의 크기가 커지더라도 유지보수가 어렵지 않습니다.

  • Automatic vendor prefixing

개별 컴포넌트마다 기존의 CSS를 이용하여 스타일 속성을 정의하기만 하면 Styled Component가 알아서 처리해 줍니다.

styled-components 사용하기

styled-components를 사용해서 React 컴포넌트를 스타일링하는 방법을 살펴보겠습니다.

설치하기

yarn add styled-components @types/styled-components

기본 문법

먼저 위에서 설치한 styled-components 패키지에서 styled 함수를 임포트합니다. styled는 styled-components의 근간이 되는 가장 중요한 녀석인데요. HTML 엘리먼트나 React 컴포넌트에 원하는 스타일을 적용하기 위해서 사용됩니다.

HTML 엘리먼트를 스타일링 할 때 해당 태그명의 속성에 접근하여 원하는 스타일 정의를 해주면 됩니다.

import styled from "styled-components";

const 컴포넌트명 = styled.태그명`
  // <button> HTML 엘리먼트에 대한 스타일 정의
`;

이런 식으로 Styled Components를 이용해서 JavaScript 코드 안에 삽입된 CSS 코드는 글로벌 네임 스페이스를 사용하지 않습니다. 다시 말해, 각 JavaScript 파일마다 고유한 CSS 네임 스페이스를 부여해주기 때문에, 각 React 컴포넌트에 완전히 격리된 스타일을 적용할 수 있게 됩니다.

props를 이용한 스타일 변경하기

styled-components는 React 컴포넌트에 넘어온 props에 따라 다른 스타일을 적용하는 기능을 제공합니다. Button 컴포넌트로 정의된 스타일을 공통적으로 사용하되 배경 색상만 다르게 하도록 props를 넘겨보겠습니다.

interface ButtonProps {
  bgColor: string;
}

const App = () => {
  return (
    <Wrapper>
      <Button bgColor="Yellow">Yellow</Button>
      <Button bgColor="Pink">Pink</Button>
    </Wrapper>
  );
};

const Button = styled.button<ButtonProps>`
  font-size: 1em;
  background-color: ${(props) => props.bgColor};
  color: black;
  border-radius: 3px;
  padding: 0.25em 1em;
`;

bgColor라는 props로 변경할 배경색상을 전달해주었습니다. 이렇게 props를 이용한 방식으로 컴포넌트를 재사용할 수 있게 하며 코드 중복을 제거할 수 있습니다. 이따 props를 받는 컴포넌트에 타입을 지정할 때는 styled.태그명<인터페이스명> 와 같이 사용할 수 있습니다.

스타일 확장

또한 스타일 확장을 위해 기존에 있는 컴포넌트를 이용해 추가적인 스타일을 갖는 새로운 컴포넌트를 만들어낼 수 있습니다. 앞에서는 html tag를 이용한 컴포넌트를 제작해보았는데요. styled(컴포넌트명) 으로 코드를 작성하여 기존에 컴포넌트를 이용한 새로운 컴포넌트를 만들 수 있습니다.

const App = () => {
  return (
    <Wrapper>
      <Button>Pink</Button>
      <YellowButton>Yellow</YellowButton>
    </Wrapper>
  );
};

const Button = styled.button`
  font-size: 1em;
  background-color: pink;
  color: black;
  border-radius: 3px;
  padding: 0.25em 1em;
`;

const YellowButton = styled(Button)`
  background-color: yellow;
`;

위의 코드를 보시면 Button이라는 컴포넌트로 새로운 YellowButton이라는 컴포넌트를 제작해주었습니다. Button에 정의된 스타일을 그대로 사용하며 배경색상만 노란색으로 나올 수 있도록 재정의 해주었습니다.

tag 변경

기존에 선언한 컴포넌트의 tag를 변경하고 싶을 땐 어떻게 해야 하는지 알아봅시다. as 키워드를 사용해 변경하고 싶은 tag명을 선언해주기만 하면 되는데요 아래 코드와 같이 아주 간단합니다.

const App = () => {
  return (
    <Wrapper>
      <Button>Pink</Button>
      <YellowButton as="a">Yellow</YellowButton>
    </Wrapper>
  );
};

as 키워드를 이용해 기존의 button 속성이 a 태그로 변경된 것을 확인할 수 있습니다.

자식 컴포넌트 선택자

이번에는 컴포넌트 안에 구성된 자식 컴포넌트를 생성하지 않고 스타일을 정의하는 방법을 보겠습니다.

const App = () => {
  return (
    <Wrapper>
      <Button>
        <span>Pink</span>
      </Button>
    </Wrapper>
  );
};

const Button = styled.button`
  background-color: pink;
  color: black;
  border-radius: 3px;
  padding: 0.25em 1em;
  span {
    color: green;
    font-size: 1em;
  }
`;

Button 컴포넌트 안에 정의된 span 태그의 스타일을 별도의 컴포넌트 생성 없이 선택자를 활용해 정의한 모습을 볼 수 있습니다.

theme 적용

팀원들과의 협업과 일관성 유지를 위해 테마를 지정해 스타일을적용하는 방법에 대해 알아보도록 하겠습니다.

타입 선언

// src/styles/styled.d.ts

import "styled-components";

declare module "styled-components" {
  export interface DefaultTheme {
    dark: {
      color: string;
      background: string;
    };
    light: {
      color: string;
      background: string;
    };
  }
}

먼저, 사용할 스타일들에 대해 타입을 정의해줍니다.

import { DefaultTheme } from "styled-components";

const theme: DefaultTheme = {
  dark: {
    background: `black`,
    color: `white`,
  },
  light: {
    background: "white",
    color: `black`,
  },
};

export { theme };

공유할 스타일들에 대해 테마로 사용할 폰트, 색상, 레이아웃 등을 선언해줍니다

import React from "react";
import { ThemeProvider } from "styled-components";
import Practice from "./Pratice";
import { theme } from "./styles/theme";

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Practice />
    </ThemeProvider>
  );
};

export default App;

정의한 테마를 사용할 수 있도록 ThemeProvider로 테마를 불러와 컴포넌트를 감싸줍니다.

import React from "react";
import styled from "styled-components";

const Practice = () => {
  return (
    <Wrapper>
      <h1>테마 적용</h1>
    </Wrapper>
  );
};

const Wrapper = styled.div`
  background-color: ${(props) => props.theme.dark.background};
  color: ${(props) => props.theme.dark.color};
`;

export default Practice;

정의된 테마에 대한 스타일을 사용하는 모습입니다.


마치며…

이상으로 React 컴포넌트를 styled-components를 이용해서 스타일하는 기본적인 방법에 대해서 알아보았습니다. styled-components을 응용할 수 있는 방법이 무궁무진해 보이고 props를 활용한 조건부 스타일링이 가능하다는 장점이 마음에 들었습니다. 하지만 번들 크기가 커지고 인터랙션이 늦어진다는 단점도 존재하기 때문에 논의를 통해 CSS-in-JSCSS-in-CSS 방식 중 어떤 것을 채택할지 신중히 결정해봐야 될 거 같습니다.

0개의 댓글