React

CSS in JS vs CSS in CSS

Jaymyong66 2025. 5. 16. 13:53

각각의 장단점과 어떨 때 무엇을 쓰면 좋을까?

이전 우테코 프로젝트를 할때는 Emotion CSS를 사용한 바 있고, 해당 기술 사용을 결정할 때 조사했던 부분이 있다.

(Github Wiki)

그럼에도 CSS in JS의 기술적 기반, 배경에는 뭐가 있는지 명확히 알아보았다.

 

비슷한 볼륨의 프로젝트는 아니지만, SCSS 기반보다 Emotion CSS 기반 프로젝트가 초기 로딩 속도가 300ms 느린 것을 확인했다.

왜 그럴까?

각각의 장단점

CSS in CSS

  • 장점
    • 초기 렌더링이 빠르다. CSS는 HTML 파싱 중 병렬로 로드되므로 렌더링을 차단하지 않는다.
    • CSS 정적파일이므로 브라우저 캐싱이 쉽다. (재방문시 속도 빠름)
    • 스타일 적용 시점이 CSS 파일 로드 직후이다.
    • 따라서 런타임 비용이 없다
    • 모든 CSS 파일을 로딩하기에, 빠르게 컴포넌트 상태가 변하는 인터렉티브 웹이라면 스타일 적용이 신속하게 이루어질 수 있다.
    • 브라우저가 스타일을 별도로 처리해 메모리 부담이 적다(스타일 시트는 DOM과 분리)
  • 단점
    • CSS의 특징답게 사용하지 않는 모든 스타일 요소도 로딩하여 비효율적일 수 있다.
    • 동적 스타일링이 어려워 JS 코드와 상호작용이 비효율적이다. 조건부 클래스로 적용할 수는 있다.

CSS in JS

  • 장점
    • 필요한 컴포넌트, 페이지의 CSS 스타일 요소만 로딩한다. (코드 스플리팅)
    • 클래스의 조합이 아닌 props 기반 조건부 스타일링에 적합하다.
    • Emotion 등에서는 스타일 중복 제거를 자동화해준다(내부적으로 캐시/중복 제거 처리)
  • 단점
    • 초기 렌더링 속도가 느리다. JS가 실행되어야 스타일이 적용된다.(styled 컴포넌트는 스타일 생성 → 해시처리 → DOM 삽입까지 한다)
    • 따라서 런타임 비용이 있다.(JS가 스타일을 생성하고 삽입한다)
    • 상태 변경으로 인해 렌더링이 일어날 때마다 JS 파일의 CSS 코드를 로딩(파싱)해야하기 때문에 성능이 떨어질 수 있다.
    • CSS in JS는 추가적인 라이브러리를 설치해야해서 번들링 크기가 커진다.
    • 서버사이드 렌더링을 쓰면 이를 위한 라이브러리를 설치해야하고, Next.JS에선 CSS Module을 권장한다고 한다.
    • JS 번들 안에 포함된다. 스타일은 그대로지만 JS가 변경되면 캐싱이 풀려서 캐싱이 어렵다.

각각 적합한 상황

  • CSS in CSS
    • 스타일시트가 별도로 캐싱되고, 렌더링 성능이 중요할 때
    • 리렌더링이 빈번해도 스타일 변경이 적을때
  • CSS in JS
    • 상태 변경이나 prop 변경에 따라 스타일을 자주 변경할 때
    • 성능이 아주 중요하지 않고 DX, 생산성이 더 중요할 때

CSS in CSS

그전에 pure CSS의 문제점은 여러 문제 중, 모든 스타일이 global에 선언되어 중복되지 않는 classname을 항상 적용해야 한다는 문제가 있었다.

 

pure CSS의 불편함을 해결하기위해 CSS Module, SCSS 등의 CSS in CSS 방식이 있다.

 

  1) CSS Module

이는 한 CSS 파일의 클래스 이름에 해시값이 붙기에 고유해지며 global에서 같은 classname을 쓰더라도 다른 module이라면 중복되지 않는다.

예를 들어 Container.tsx에서 Container.module.css를 사용하고, 내부에 Container classname을 쓰더라도 Container_해시값이 된다.

 

  2) SCSS

SCSS는 Sass의 기능들을 지원한다. Sass는 CSS 전처리기 중 하나인데, 반복문, 조건문 등의 제공으로 CSS 작성을 편리하게 해준다. SCSS는 Sass의 작성 방법 중 하나인데, 보다 CSS 문법에 가깝다. 따라서 기존 코드와 호환성이 좋고 학습곡선이 낮아 많이 사용한다.

(Sass는 들여쓰기 기반이지만 SCSS는 중괄호({})를 사용하는 등)

 

예를 들어, SCSS로 CSS를 작성한 후, 브라우저에 렌더링 시, 웹에서 구동 가능한 CSS로 컴파일되어 적용된다.

 

왜 필요할까?

중첩 구조, 연산(변수 간 조합 계산까지), 재사용(mixin, extend..), 조건문, 반복문 사용 등이 가능하다.

주의할 점은, SCSS는 전처리기이기 때문에 컴파일되면 일반 CSS로 바뀐 뒤 브라우저로 전달된다.

예를 들어, SCSS로 다음과 같이 변수를 만들었다

$main-color: red;

.button {
  background-color: $main-color;
}

하지만 컴파일 후 브라우저에 적용되는 CSS는 다음과 같아, 변수값을 바꿀 수는 없다

/* 브라우저에 적용되는 CSS */
.button {
  background-color: red;
}

하지만 CSS 변수 —main-color는 DOM에 살아있는 변수이기에, 다음과 같은 코드로 변수값을 변경 가능하다

:root {
  --main-color: red;
}

.button {
  background-color: var(--main-color);
}

document.documentElement.style.setProperty('--main-color', 'blue');

따라서 런타임에 변경되는 스타일을 구현하려면, 조건부 클래스를 토글하는 방법이 있겠다.

즉, JS로 클래스를 변경하는 방식이다.

CSS in JS

JS 파일에 CSS를 작성한다.

컴포넌트에 스타일을 적용하여, 라이프 사이클에 따라 동적 스타일 적용이 수월하다.

 

예를 들어, 다음과 같이 styled 컴포넌트를 작성할 수 있다.

const Title = styled.h1 `
	color : palevioletred;
	font-size : 24px;
`;

<Title>
	CSS-in-JS
</Title>

하지만 브라우저 렌더링 시에는 CSS 파일과 classname 기반으로 렌더링 트리를 생성한다.

그렇기에 CSS in JS는 CSS 전처리기를 내장한다.

이는 런타임 시, 각 컴포넌트에 해싱된 동적 classname을 생성한다.

.sc-cMWNzn {
	color : palevioletred;
	font-size : 24px;
}

<h1 class = "sc-cMWNzn>
	CSS-in-JS
</h1>

마치며

개발상으로는 CSS in JS가 편한 상황이 많지만, 렌더링 성능 향상으로 초기 로딩 속도를 높이고자 한다면 CSS in CSS 방식을 고려해야할 것 같다.

DX와 렌더링 성능 향상을 함께 잡기 위해 zero 런타임 라이브러리인 vanilla-extract/css 나 panda css가 주목받는 것 같다.

또 vanilla-extract는 theme을 나누거나 route-based로 코드 스플리팅을 어느정도 지원한다.