왜 Next.js App Router에서 Emotion을 쓰면 오류가 자주 발생할까?

leave_a_comment·2026년 5월 19일

Next.js App Router 환경에서 Emotion을 사용하다 보면 아래와 같은 오류를 종종 보게 됩니다.

Warning: Prop `className` did not match

또는:

  • Hydration mismatch
  • 스타일 깜빡임(FOUC)
  • 스타일 우선순위 꼬임
  • 서버/클라이언트 스타일 불일치

이번 글에서는 왜 이런 문제가 발생하는지 간단하게 정리해보겠습니다.


App Router는 기본적으로 Server Component 기반

Next.js App Router는 기존 Pages Router와 달리 기본적으로 Server Component 기반 구조입니다.

즉 컴포넌트가:

  1. 서버에서 먼저 렌더링되고
  2. HTML이 전달된 뒤
  3. 클라이언트에서 hydration 되는 방식입니다.

반면 Emotion은:

  • 런타임에서 스타일 생성
  • 동적으로 className 생성
  • 스타일 삽입 순서(insertion order) 관리

같은 특징을 가지고 있습니다.

즉 Emotion은 기본적으로 클라이언트 환경에서 동작하는 CSS-in-JS 방식에 더 가깝습니다.

여기서 App Router와 충돌이 발생할 수 있습니다.


가장 많이 발생하는 문제: className 불일치

Emotion은 렌더링 시점마다 className을 생성합니다.

예를 들면:

css-1abcde
css-92kda

같은 형태의 className이 만들어집니다.

그런데 App Router 환경에서는:

  1. 서버 렌더링
  2. 클라이언트 hydration
  3. 다시 렌더링

과정을 거치면서 스타일 생성 순서가 달라질 수 있습니다.

그 결과:

Warning: Prop `className` did not match

같은 hydration 오류가 발생하게 됩니다.


왜 App Router에서 더 자주 발생할까?

React 18 + App Router는 아래 기능들을 적극적으로 사용합니다.

  • Streaming SSR
  • Suspense
  • Concurrent Rendering

이 환경에서는 컴포넌트 렌더링 순서가 항상 동일하지 않을 수 있습니다.

하지만 Emotion은:

  • 어떤 컴포넌트가 먼저 렌더됐는지
  • 어떤 스타일이 먼저 삽입됐는지

에 영향을 많이 받습니다.

즉:

렌더링 순서가 바뀌는 환경에서 런타임으로 스타일을 생성하다 보니 문제가 발생하는 것

에 가깝습니다.


실제로 나타나는 현상들

1. Hydration mismatch

Warning: Prop `className` did not match

서버와 클라이언트의 className이 달라지는 문제입니다.


2. 스타일 깜빡임 (FOUC)

초기 렌더 시 스타일이 적용되지 않았다가 뒤늦게 적용되는 현상입니다.


3. 스타일 우선순위 꼬임

Emotion의 insertion order가 달라지면서 예상과 다른 스타일이 적용될 수 있습니다.


4. 특정 상황에서 스타일이 사라지는 문제

재현 조건이 일정하지 않은 경우가 많아 디버깅도 쉽지 않습니다.


styled-components도 비슷한데 왜 Emotion 이야기가 더 많을까?

Emotion은 특히:

  • 런타임 의존성
  • cache 관리
  • insertion order

영향을 크게 받습니다.

그래서 App Router 환경에서는 설정이 조금만 어긋나도 문제가 쉽게 발생합니다.

특히 아래 설정이 중요합니다.

CacheProvider
useServerInsertedHTML

이 설정 없이 사용하면 스타일 충돌 가능성이 높아집니다.


해결 방법

보통은 Emotion Registry를 직접 구성해서 사용합니다.

예를 들면:

'use client'

import { CacheProvider } from '@emotion/react'
import createCache from '@emotion/cache'

그리고 Next.js의:

useServerInsertedHTML()

를 사용해 서버 렌더링 시 style 태그를 직접 삽입합니다.

핵심은:

서버와 클라이언트의 스타일 생성 순서를 최대한 동일하게 맞추는 것

입니다.


그래서 요즘은 어떤 방식이 많이 쓰일까?

최근 App Router 환경에서는 아래 스타일링 방식 선호도가 높아지고 있습니다.

  • Tailwind CSS
  • CSS Modules
  • Panda CSS
  • Vanilla Extract

이유는:

  • hydration 문제 감소
  • 서버 컴포넌트 친화적
  • 런타임 비용 감소
  • 번들 크기 감소

같은 장점 때문입니다.


마무리

Emotion 자체가 문제 있는 라이브러리는 아닙니다.

다만 Next.js App Router + React 18 환경에서는:

  • Streaming SSR
  • Concurrent Rendering
  • Server Components

구조와 런타임 스타일 생성 방식이 충돌하기 쉬운 편입니다.

그래서 최근에는 빌드 타임 기반 스타일링 방식도 많이 사용되는 추세입니다.

profile
나도 성장하고파

0개의 댓글