리액트 스타일링 비교: CSS 모듈 vs styled-components

리액트 스타일링 비교: CSS 모듈 vs styled-components

CSS 모듈styled-components는 리액트에서 가장 많이 쓰이는 두 가지 스타일링 방식입니다. 이 글에서는 설치부터 기본 사용법, 유지보수/성능/테마링까지 비교하고, 동일 UI를 두 방식으로 구현하는 실습 예제를 제공합니다.

1) 개념 한눈에 보기

항목CSS 모듈styled-components
핵심 아이디어파일 단위 클래스 이름 해싱으로 자동 스코프JS 안에서 styled.* 함수로 컴포넌트화
러닝 커브CSS 친숙 → 진입장벽 낮음템플릿 리터럴 문법+런타임 개념 필요
동적 스타일클래스 토글 / CSS 변수 활용props 기반 조건부 스타일 아주 쉬움
테마(Design System)CSS 변수/전역 토큰ThemeProvider로 컨텍스트 기반
성능/번들빌드 타임 처리(빠름, 오버헤드 적음)런타임 스타일 생성(최적화 옵션 제공)
도구 연동PostCSS/Autoprefixer/전처리기 용이자동 벤더 프리픽스, 타입 가드 등 생태계 풍부

2) 설치

Create React App(CRA)·Vite 등 대부분의 툴체인은 CSS 모듈을 기본 지원합니다. .module.css 네이밍만 맞추면 됩니다.

# styled-components 설치
npm i styled-components
# 타입스크립트면(선택)
npm i -D @types/styled-components
리액트 CSS 모듈 설치 예시

3) 기본 사용법

3-1) CSS 모듈

/* src/styles/Button.module.css */
.btn {
  padding: 8px 12px;
  border-radius: 8px;
  font-weight: 600;
  border: 1px solid #d0d7de;
  background: #f6f8fa;
  cursor: pointer;
}
.primary {
  color: #fff;
  background: #0969da;
  border-color: #0969da;
}
.btn:disabled {
  opacity: .6;
  cursor: not-allowed;
}
// src/components/Button.module.jsx
import React from 'react';
import styles from '../styles/Button.module.css';

export default function Button({ children, variant = 'default', ...props }) {
  const cls = [styles.btn, variant === 'primary' && styles.primary].filter(Boolean).join(' ');
  return <button className={cls} {...props}>{children}</button>;
}
// src/examples/ButtonModuleDemo.jsx
import React from 'react';
import Button from '../components/Button.module.jsx';

export default function ButtonModuleDemo() {
  return (
    <div style={{ display: 'flex', gap: 8 }}>
      <Button>Default</Button>
      <Button variant="primary">Primary</Button>
      <Button disabled>Disabled</Button>
    </div>
  );
}

▼ 실행 화면 설명: Default/Primary/Disabled 버튼이 가로로 나란히 보이고, Primary는 파란 배경/흰 글씨로 표시됩니다. Disabled는 흐리게 보이며 클릭할 수 없습니다.

리액트 "CSS 모듈" 예제 실행 화면

3-2) styled-components

// src/components/SButton.jsx
import styled from 'styled-components';

export const SButton = styled.button`
  padding: 8px 12px;
  border-radius: 8px;
  font-weight: 600;
  border: 1px solid #d0d7de;
  background: #f6f8fa;
  cursor: pointer;

  ${(p) => p.$primary && `
    color: #fff;
    background: #0969da;
    border-color: #0969da;
  `}

  &:disabled { opacity: .6; cursor: not-allowed; }
`;
// src/examples/SButtonDemo.jsx
import React from 'react';
import { SButton } from '../components/SButton'; // 이름 export 임포트

export default function SButtonDemo() {
  return (
    <div style={{ display: 'flex', gap: 8 }}>
      <SButton>Default</SButton>
      <SButton $primary>Primary</SButton>
      <SButton disabled>Disabled</SButton>
    </div>
  );
}

▼ 실행 화면 설명: Default/Primary/Disabled 버튼이 가로로 나란히 보이고, Primary는 파란 배경/흰 글씨로 표시됩니다. Disabled는 흐리게 보이며 클릭할 수 없습니다.

리액트 "styled-components" 예제 실행 화면

4) 성능·번들·SSR 관점

  • CSS 모듈: 빌드 타임에 클래스가 해싱되어 런타임 오버헤드가 적고 캐싱이 유리합니다. 대규모 리스트 렌더링에서도 안정적입니다.
  • styled-components: 런타임에서 스타일을 생성/주입합니다. 조건부 스타일/테마링이 매우 편리하지만, 매우 많은 변형을 렌더링하는 화면에서는 성능 예산을 고려해야 합니다. (바벨 플러그인/스타일 캐싱, as prop 최소화 등 최적화 권장)
  • SSR: CSS 모듈은 일반적으로 문제 없고, styled-components는 ServerStyleSheet로 스타일 수집 후 HTML에 주입하는 패턴을 사용합니다.

5) 무엇을 선택할까? (실무 가이드)

  • 컴포넌트/페이지 수가 많고 성능 예산이 빡빡하다면: CSS 모듈 + 전역 토큰(CSS 변수) 권장
  • 테마/변형/조건부 스타일이 매우 빈번하다면: styled-components + ThemeProvider가 개발 효율이 높음
  • 혼용 전략도 가능: 기본은 CSS 모듈로 두고, 테마·변형이 많은 디자인 시스템 컴포넌트만 styled-components로 제작

요약

  • CSS 모듈: 단순/빠름/도구호환 우수
  • styled-components: 동적 스타일/테마 최강, 러닝 커브·런타임 오버헤드 고려
  • 프로젝트 성격에 따라 혼용이 가장 실용적

참고 링크

함께 보면 좋은 게시글

위로 스크롤