리액트 인라인 스타일 vs 외부 스타일: 언제 무엇을 써야 할까?

리액트 인라인 스타일 vs 외부 스타일: 언제 무엇을 써야 할까?

리액트에서 스타일을 적용하는 방법은 크게 두 가지입니다. 인라인 스타일(style prop)외부 스타일(클래스 기반)입니다. 두 방식은 적용 범위·표현력·성능 특성이 꽤 다릅니다. 이 글은 “언제 무엇을 써야 하는지” 선택 기준과 함께, 실습 가능한 짧은 예제를 제공합니다.

요약 선택 기준

  • 인라인 스타일: “즉시 계산된 숫자값·좌표·변환”처럼 런타임 계산이 핵심일 때, 또는 아주 국소적인 1회성 스타일.
  • 외부 스타일: 반응형·의사클래스(:hover/:focus)·미디어쿼리·애니메이션·테마/다크모드·재사용 컴포넌트 전반.

인라인 스타일: 장단점과 예제

장점

  • 런타임 계산값을 바로 적용(예: 동적 width/transform/top/left).
  • 스타일 우선순위가 높아 빠르게 예외처리(override) 가능.
  • 클래스 충돌 없음, CSS 파일 없이도 즉시 적용.

단점

  • 의사클래스/미디어쿼리를 직접 지원하지 않음(:hover, @media 불가).
  • 재사용·관리가 약함(중복 증가, 테마 확장 어려움).
  • 스타일 객체가 매 렌더 새로 생성되면 메모이제이션을 방해해 자식 리렌더를 유발.

예제 1) 동적 진행률 바(런타임 계산값이 핵심)

import React, { useState } from "react";
import Progress from "../components/Progress";

export default function ProgressDemo() {
  const [value, setValue] = useState(25);

  return (
    <div style={{ padding: 24 }}>
      <h2 style={{ marginBottom: 12 }}>Progress (Inline width + External CSS)</h2>

      <div style={{ marginBottom: 12 }}>
        <input
          type="range"
          min="0"
          max="100"
          value={value}
          onChange={(e) => setValue(Number(e.target.value))}
          style={{ width: 300 }}
        />
        <span style={{ marginLeft: 12 }}>{value}%</span>
      </div>

      <Progress value={value} />

      <p style={{ fontSize: 12, color: "#6b7280", marginTop: 8 }}>
        슬라이더를 움직이면 진행률이 부드럽게 변경되는지 확인하세요.
      </p>
    </div>
  );
}

▼ 실행 화면 설명: 값(20→60%)을 바꾸며 진행 바 너비가 부드럽게 변하는 모습.

리액트 "인라인 스타일" 예제 실행 화면

외부 스타일(클래스 기반): 장단점과 예제

여기서는 방식 전체(Global CSS, CSS Modules, styled-components 등)를 포괄해 “클래스 기반”으로 부릅니다.

장점

  • 표현력: :hover, :focus, @media, @keyframes, 중첩·선택자 조합 가능.
  • 재사용·일관성: 컴포넌트 단위로 공통 규칙을 공유, 테마/다크모드 확장 용이.
  • 성능·가독성: 스타일 로직과 UI 로직을 분리해 관리 부담 감소.

단점

  • 초기 구조화(폴더링·네이밍/캡슐화) 필요.
  • 간단한 1회성 예외에는 다소 번거로울 수 있음.

예제 2) CSS Modules로 호버/포커스/반응형

UiDemo.module.css

.btn {
  padding: .5rem 1rem;
  border-radius: .5rem;
  border: 1px solid #e5e7eb;
  background: white;
  color: #111827;
  transition: background .15s ease, box-shadow .15s ease;
}
.btn:hover { background: #f3f4f6; }
.btn:focus { outline: 2px solid #60a5fa; outline-offset: 2px; }

.grid {
  display: grid;
  gap: 1rem;
}
@media (min-width: 768px) {
  .grid { grid-template-columns: repeat(3, 1fr); }
}

사용

import React from "react";
import styles from "../styles/UiDemo.module.css";

export default function CssModulesDemo() {
  return (
    <div style={{ padding: 24 }}>
      <h2 style={{ marginBottom: 12 }}>CSS Modules Demo (hover/focus + responsive)</h2>
      <div className={styles.grid}>
        <button className={styles.btn}>액션</button>
        <button className={styles.btn}>두번째</button>
        <button className={styles.btn}>세번째</button>
      </div>
      <p style={{ fontSize: 12, color: "#6b7280", marginTop: 8 }}>
        데스크톱(≥768px)에서 3열, 모바일에서 1열로 보이며 hover/focus 상태를 확인하세요.
      </p>
    </div>
  );
}

▼ 실행 화면 설명: 모바일 1열 → 데스크톱 3열 전환, :hover/:focus 상태 표시.

리액트 "외부 스타일(클래스 기반)" 예제 실행 화면

성능·유지보수 관점 체크리스트

  • 인라인 스타일은 “숫자값 동적 계산”에만 쓰고, useMemo로 객체 참조를 안정화.
  • 외부 스타일은 상태·반응형·테마·의사클래스가 얽히는 UI의 기본값으로 채택.
  • 컴포넌트 재사용이 잦다면 변형(variant) 설계로 클래스 세트를 표준화.
  • 접근성: :focus 스타일은 제거하지 말고, 키보드 탐색 시각 피드백을 반드시 유지.

결론: 이렇게 고르자

  • 인라인 — 좌표·회전·실시간 크기처럼 계산값 중심 & 국소적 예외.
  • 외부대부분의 화면: 반응형·의사클래스·애니메이션·테마·재사용.

참고 링크

함께 보면 좋은 게시글

위로 스크롤