본문으로 건너뛰기

안동민 개발노트

본문 시작

Styled-components 기초

styled-components로 CSS를 컴포넌트에 결합하고 props 기반 동적 스타일·태그 변경·스타일 확장을 구현합니다.

우리는 CSS 모듈을 통해 클래스 이름 충돌 문제를 해결하고 컴포넌트 단위 스타일링을 구현하는 방법을 배웠습니다.

이제 5장 스타일링과 CSS의 세 번째 방법으로, CSS-in-JS의 대표 라이브러리 중 하나인 Styled-components를 소개합니다.

Styled-components는 자바스크립트 코드 안에서 CSS를 작성하고, 이를 컴포넌트에 직접 연결해 사용하는 방식입니다.

이 방식은 스타일을 컴포넌트 로직과 함께 한 파일에서 관리할 수 있어, 스타일링을 컴포넌트의 일부로 보는 리액트 철학과 잘 맞아떨어집니다.


CSS-in-JS란?

CSS-in-JS는 말 그대로 JavaScript 코드 안에 CSS를 작성하는 방식을 통칭합니다.

기존에는 CSS 파일을 별도로 분리하여 관리했지만, CSS-in-JS는 컴포넌트 로직과 스타일 로직을 하나의 JavaScript 파일 안에서 함께 작성합니다.

Styled-components의 핵심 아이디어
  • 컴포넌트 기반 스타일링: 스타일이 적용된 리액트 컴포넌트를 생성합니다.
  • 고유한 클래스 이름 자동 생성: CSS 모듈처럼 자동으로 고유한 해시 클래스 이름을 생성하여 클래스 이름 충돌을 방지합니다.
  • 동적 스타일링 용이: JavaScript를 활용하여 props나 state에 따라 스타일을 유연하게 변경할 수 있습니다.
  • 스코프된 스타일: 생성된 스타일은 해당 컴포넌트에만 영향을 미치므로 전역 오염 걱정이 없습니다.

Styled-components 설치

Styled-components를 사용하기 위해서는 먼저 라이브러리를 설치해야 합니다.

터미널에서 다음 명령어를 실행하세요.

npm install styled-components
# 또는
yarn add styled-components

Styled-components 기본 사용법

Styled-components는 태그드 템플릿 리터럴(Tagged Template Literals) 문법을 사용하여 스타일이 적용된 컴포넌트를 생성합니다.

import styled from 'styled-components'; // styled 객체 임포트

// 1. styled.{HTML 태그} 백틱(``) 안에 CSS 작성
const StyledButton = styled.button`
  background-color: #61dafb;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1em;
  transition: all 0.3s ease-in-out;

  &:hover { /* 가상 클래스도 바로 사용 가능 */
    background-color: #21a1f1;
    transform: translateY(-2px);
  }

  &:active {
    transform: translateY(0);
  }
`;

function MyComponent() {
  return (
    <StyledButton>클릭하세요</StyledButton> // 2. 스타일링된 컴포넌트 사용
  );
}

예제: 기본적인 스타일링 컴포넌트 만들기

src/components/StyledComponentsBasic.js 파일을 생성하고 다음 코드를 작성하세요.

src/components/StyledComponentsBasic.js
import React from 'react';
import styled from 'styled-components'; // styled-components 임포트

// 1. 스타일이 적용된 <Container> 컴포넌트 생성
const Container = styled.div`
  background-color: #f0f8ff; /* 연한 하늘색 배경 */
  padding: 30px;
  border-radius: 10px;
  border: 1px solid #dbeafe;
  text-align: center;
  margin-bottom: 30px;
`;

// 2. 스타일이 적용된 <Title> 컴포넌트 생성
const Title = styled.h2`
  color: #1e90ff; /* 진한 파란색 */
  font-size: 2.5em;
  margin-bottom: 15px;
  text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
`;

// 3. 스타일이 적용된 <Paragraph> 컴포넌트 생성
const Paragraph = styled.p`
  color: #4682b4; /* 중간 파란색 */
  font-size: 1.2em;
  line-height: 1.7;
  margin-bottom: 20px;
`;

// 4. 스타일이 적용된 <StyledButton> 컴포넌트 생성
const StyledButton = styled.button`
  background-color: #007bff;
  color: white;
  padding: 12px 25px;
  border: none;
  border-radius: 7px;
  cursor: pointer;
  font-size: 1.1em;
  font-weight: bold;
  transition: background-color 0.3s ease, transform 0.2s ease;

  &:hover { /* 가상 클래스 */
    background-color: #0056b3;
    transform: translateY(-2px);
  }

  &:active {
    transform: translateY(0);
  }
`;

function StyledComponentsBasic() {
  return (
    <Container> {/* 생성한 Styled 컴포넌트 사용 */}
      <Title>Styled-components 기초 예제</Title>
      <Paragraph>
        이것은 styled-components로 스타일링된 단락입니다.
        스타일이 컴포넌트 자체에 포함되어 있습니다.
      </Paragraph>
      <StyledButton>Styled 버튼</StyledButton>
    </Container>
  );
}

export default StyledComponentsBasic;

App.js에서 렌더링

App.js 파일을 수정하여 StyledComponentsBasic 컴포넌트를 렌더링하세요.

src/App.js
import React from 'react';
import './index.css'; // 전역 스타일 유지
import StyledComponentsBasic from './components/StyledComponentsBasic';
// import CssModulesExample from './components/CssModulesExample'; // 필요 없으면 주석 처리 또는 삭제

function App() {
  return (
    <div className="App">
      <h1>Styled-components 학습</h1>
      <StyledComponentsBasic />
    </div>
  );
}

export default App;

브라우저에서 개발자 도구(F12)를 열어 엘리먼트 탭을 확인해 보세요.

각 HTML 요소에 sc-xxx와 같은 프리픽스와 고유한 해시값이 붙은 클래스 이름이 자동으로 생성되어 있는 것을 볼 수 있습니다.


Props를 이용한 동적 스타일링

Styled-components의 가장 큰 장점 중 하나는 props를 이용하여 스타일을 동적으로 변경할 수 있다는 것입니다.

태그드 템플릿 리터럴 내에서 JavaScript 코드를 삽입할 수 있는 것처럼, props를 인자로 받는 함수를 작성하여 동적인 스타일을 적용할 수 있습니다.

src/components/DynamicStyledButton.js
import React from 'react';
import styled from 'styled-components';

// props에 따라 색상이 변하는 버튼
const DynamicButton = styled.button`
  background-color: ${props => props.primary ? '#007bff' : '#6c757d'}; /* (1) props.primary에 따라 배경색 변경 */
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1em;
  margin: 5px;
  transition: background-color 0.3s ease;

  &:hover {
    background-color: ${props => props.primary ? '#0056b3' : '#5a6268'};
  }
`;

// props에 따라 크기가 변하는 텍스트
const ResizableText = styled.p`
  font-size: ${props => props.size || '1em'}; /* (2) props.size에 따라 폰트 크기 변경 */
  color: ${props => props.color || '#333'}; /* (3) props.color에 따라 색상 변경 */
  margin-top: 15px;
`;

function DynamicStyledComponents() {
  return (
    <div style={{ textAlign: 'center', margin: '20px', padding: '20px', border: '1px dashed #ccc', borderRadius: '8px' }}>
      <h2>동적 스타일링 예제</h2>
      <DynamicButton primary>기본 버튼</DynamicButton> {/* primary prop 전달 */}
      <DynamicButton>보조 버튼</DynamicButton> {/* primary prop 없음 */}

      <ResizableText size="1.5em" color="#d9534f">크고 빨간 텍스트</ResizableText>
      <ResizableText size="0.8em" color="#5cb85c">작고 초록 텍스트</ResizableText>
    </div>
  );
}

export default DynamicStyledComponents;

App.jsDynamicStyledComponents 컴포넌트를 추가하고 실행해 보세요.

primary, size, color와 같은 props를 전달하여 컴포넌트의 스타일이 동적으로 변경되는 것을 확인할 수 있습니다.

이 방식은 인라인 스타일링처럼 동적 값을 다루면서도 CSS 문법을 함께 사용할 수 있다는 장점을 가집니다.

styled-components 동적 스타일
  1. prop

    CSS 조건으로 변환되어 컴포넌트 안에 머문다

  2. styled-components

    JavaScript 값을 템플릿 안에서 읽어 버튼 색, 텍스트 크기, hover 규칙을 컴포넌트 단위로 묶는다.

  3. 버튼이면 강조 색

    선택한다.

  4. size 텍스트 크기

    props로 받아 CSS font-size에 반영한다.


as prop을 이용한 태그 변경

Styled-components로 생성된 컴포넌트는 as prop을 사용하여 렌더링될 HTML 태그를 변경할 수 있습니다.

이는 동일한 스타일을 유지하면서 다른 시맨틱 태그를 사용해야 할 때 유용합니다.

src/components/AsPropExample.js
import React from 'react';
import styled from 'styled-components';

const ButtonLike = styled.button`
  background-color: #ffc107;
  color: #333;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 1em;
  margin: 5px;
  transition: background-color 0.3s ease;

  &:hover {
    background-color: #e0a800;
  }
`;

function AsPropExample() {
  return (
    <div style={{ textAlign: 'center', margin: '20px', padding: '20px', border: '1px dashed #ccc', borderRadius: '8px' }}>
      <h2>'as' prop 예제</h2>
      <ButtonLike>나는 button 태그입니다</ButtonLike>
      {/* ButtonLike 컴포넌트의 스타일은 유지하되, 렌더링되는 HTML 태그는 <a>로 변경 */}
      <ButtonLike as="a" href="#" onClick={(e) => e.preventDefault()}>나는 a 태그입니다</ButtonLike>
      {/* ButtonLike 컴포넌트의 스타일은 유지하되, 렌더링되는 HTML 태그는 <div>로 변경 */}
      <ButtonLike as="div">나는 div 태그입니다</ButtonLike>
    </div>
  );
}

export default AsPropExample;

App.jsAsPropExample 컴포넌트를 추가하여 실행해 보세요.

모두 동일한 스타일을 가지고 있지만, 개발자 도구에서 실제 HTML 태그가 다르게 렌더링되는 것을 확인할 수 있습니다.


다른 컴포넌트 확장하기

기존에 스타일링된 컴포넌트의 스타일을 상속받아 새로운 스타일을 추가할 수 있습니다.

Styled-components v4부터는 styled(기존컴포넌트) 문법을 사용하는 것이 권장됩니다.

src/components/ExtendedStyledComponent.js
import React from 'react';
import styled from 'styled-components';

const BaseButton = styled.button`
  padding: 10px 20px;
  font-size: 1em;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.3s ease, color 0.3s ease;
`;

// BaseButton의 스타일을 상속받아 새로운 스타일 추가
const PrimaryButton = styled(BaseButton)`
  background-color: #28a745;
  color: white;
  border: 1px solid #28a745;

  &:hover {
    background-color: #218838;
    border-color: #218838;
  }
`;

// BaseButton의 스타일을 상속받아 또 다른 스타일 추가
const DangerButton = styled(BaseButton)`
  background-color: #dc3545;
  color: white;
  border: 1px solid #dc3545;

  &:hover {
    background-color: #c82333;
    border-color: #c82333;
  }
`;

function ExtendedStyledComponent() {
  return (
    <div style={{ textAlign: 'center', margin: '20px', padding: '20px', border: '1px dashed #ccc', borderRadius: '8px' }}>
      <h2>컴포넌트 확장 예제</h2>
      <BaseButton>기본 버튼</BaseButton>
      <PrimaryButton style={{ marginLeft: '10px' }}>확장된 기본 버튼</PrimaryButton>
      <DangerButton style={{ marginLeft: '10px' }}>확장된 위험 버튼</DangerButton>
    </div>
  );
}

export default ExtendedStyledComponent;

App.jsExtendedStyledComponent 컴포넌트를 추가하여 실행해 보세요.

BaseButton의 기본 스타일을 재사용하면서 각각 다른 색상의 버튼을 효율적으로 생성할 수 있습니다.

Styled-components를 선택할 때는 장점뿐 아니라 런타임 비용과 팀 규칙도 함께 판단해야 합니다.

아래 다이어그램에서 사용 기준을 간단히 정리해 보세요.

Styled-components 선택 기준

컴포넌트 단위 스코프, props 기반 분기, 테마 확장이 필요하면 CSS-in-JS가 유리합니다. 정적 레이아웃 중심이면 CSS 모듈이 더 단순할 수 있습니다.

  1. scope
    충돌 감소

    고유 클래스가 생성되어 컴포넌트 밖 스타일과 섞일 가능성을 줄입니다.

  2. props
    동적 분기

    active, disabled, theme 같은 값으로 색상과 크기를 자연스럽게 바꿉니다.

  3. cost
    런타임 비용

    정적 화면만 많다면 스타일 생성 비용과 라이브러리 의존성이 과할 수 있습니다.

다음 다이어그램은 props, as prop, 확장 컴포넌트가 실제 코드에서 어떻게 이어지는지 보여줍니다.

바꾸려는 대상에 따라 props·as·확장을 나눈다

기본 스타일을 먼저 고정하고, 상태·HTML 의미·컴포넌트 계보 중 무엇이 달라지는지에 맞춰 한 축만 선택한다.

  1. 상태가 다르면 props

    같은 버튼의 크기·강조·비활성처럼 값에 따라 스타일만 분기한다.

  2. 태그 의미가 다르면 as

    모양은 유지하되 링크 등 실제 HTML 역할과 필수 속성을 맞춘다.

  3. 새 계보면 styled(Base)

    공통 상호작용을 물려받고 반복해서 쓸 새 컴포넌트를 만든다.


Styled-components 기초는 여기까지입니다.

이 장에서는 Styled-components의 기본적인 설치와 사용법, props를 이용한 동적 스타일링, as prop을 이용한 태그 변경, 그리고 기존 컴포넌트를 확장하는 방법까지 상세하게 다루었습니다.

Styled-components는 스타일을 컴포넌트 로직과 함께 관리하고, JavaScript 값을 활용하여 동적인 스타일링을 유연하게 구현할 수 있게 해줍니다.

이는 컴포넌트 중심의 리액트 개발에 적합한 방식이며, 많은 현대 리액트 프로젝트에서 사용됩니다.

다음 절에서는 CSS-in-JS의 또 다른 주요 기능인 테마(Theming)전역 스타일(Global Styles)에 대해 알아보겠습니다.


스타일링 방식을 고를 때는 다음 다이어그램처럼 변경 빈도, 테마, 런타임 비용을 함께 비교하면 좋습니다.

스타일링 도구 선택 기준

CSS 모듈, CSS-in-JS, 유틸리티 CSS는 모두 해결하는 문제가 조금씩 다릅니다.

  1. module
    CSS Modules

    컴포넌트별 정적 스타일과 빌드 타임 충돌 방지에 잘 맞습니다.

  2. runtime
    CSS-in-JS

    props 기반 동적 스타일과 디자인 토큰 결합이 많을 때 유리합니다.

  3. utility
    Utility CSS

    빠른 조합과 일관된 spacing 규칙이 중요한 팀에 맞습니다.

  4. system
    Design System

    반복 UI가 많아지면 스타일 도구보다 컴포넌트 계약이 더 중요합니다.


styled-components는 동적 스타일을 쉽게 만들지만, props 변형이 컴포넌트의 공개 계약이 된다는 점을 함께 확인해야 합니다.

styled-components 스타일 경계

CSS-in-JS 방식은 스타일 정의를 컴포넌트 파일 가까이에 두고 props 변화가 className이나 CSS 규칙으로 바뀌는 경로를 코드에 묶습니다.

  1. 설치와 import

    styled-components를 설치한 뒤 styled.button, styled.div 같은 형태로 스타일 컴포넌트를 만듭니다.

  2. 기본 사용

    템플릿 리터럴 안에 CSS를 작성하고 만든 컴포넌트를 JSX에서 일반 컴포넌트처럼 씁니다.

  3. 동적 스타일

    props를 읽어 active, size, variant 같은 시각 차이를 조건부로 적용합니다.

  4. styled

    스타일 컴포넌트 이름이 화면 역할을 설명하는지 본다.

  5. props

    스타일 전용 props가 DOM 속성으로 새지 않는지 확인한다.