리액트 인라인 스타일 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스타일은 제거하지 말고, 키보드 탐색 시각 피드백을 반드시 유지.
결론: 이렇게 고르자
- 인라인 — 좌표·회전·실시간 크기처럼 계산값 중심 & 국소적 예외.
- 외부 — 대부분의 화면: 반응형·의사클래스·애니메이션·테마·재사용.
참고 링크
함께 보면 좋은 게시글
- 리액트 JSX 문법 규칙: 인라인 스타일링·className·닫는 태그·주석
- 리액트 스타일링 비교: CSS 모듈 vs styled-components
- React JSX 기초: 표현식·조건부 렌더링·리스트 출력
- React 이벤트 처리 방법: onClick·onChange·onSubmit 예제
- 리액트 useMemo 제대로 쓰기: 언제 이득이고 언제 오버엔지니어링일까
이 글이 도움이 되셨다면 공유 부탁 드립니다.



