Next.js는 리액트 기반의 프레임워크로서 서버 사이드 렌더링(SSR)을 기본적으로 지원한다.
프론트엔드 개발자로서 스타일링 방식을 선택할 때 많은 옵션이 있지만, 그중에서도 스타일드 컴포넌트(styled-components)는 리액트 생태계에서 인기 있는 CSS-in-JS 라이브러리이다.
하지만 Next.js의 SSR 환경에서 스타일드 컴포넌트를 사용할 때 예상치 못한 문제가 발생할 수 있다.
특히 TypeScript를 함께 사용하는 환경에서는 추가적인 타입 관련 설정도 고려해야 한다
이 글에서는 이러한 문제가 왜 발생하는지, 그리고 어떤 영향을 미치는지 직접 실험을 통해 정리해보고자 한다!!!
(styled-components에 대한 기본적인 이해가 있다고 가정하며 진행하겠다.)
Next.js에서의 SSR 개념
먼저 Next.js에서의 SSR 과정과 개념을 이해해야 되지 않을까?
SSR이 무엇인지 모르는 사람들은 아래글을 참고해보면 좋을 것 같다.
[React] CSR, SSR의 개념과 차이점 완벽 이해하기 👍
오늘은 웹 렌더링의 핵심이라고 할 수 있는 CSR과 SSR에 대해 React를 중심으로 정리해보고자 한다.CSR(Client Side Rendering)이란?CSR은 브라우저가 JavaScript를 다운로드, 실행하여 페이지를 렌더링하는 방
bbin-guuuu.tistory.com
서버 사이드 렌더링(SSR)은 웹 페이지가 클라이언트의 브라우저가 아닌 서버에서 렌더링되는 과정을 말한다.
Next.js는 이러한 SSR을 쉽게 구현할 수 있도록 설계되었다.
SSR을 통해 초기 페이지 로드 시간 단축, 검색 엔진 최적화(SEO) 향상 등 여러 이점을 가질 수 있다.
그렇다면 Next.js에서 SSR이 이뤄지는 과정을 순서대로 정리해보자.
- 사용자가 페이지를 요청
- 서버에서 리액트 컴포넌트를 실행하고 HTML 생성
- 생성된 HTML을 사용자에게 전송
- 브라우저에서 HTML을 표시하고 자바스크립트 로드
- 자바스크립트가 로드되면 '하이드레이션(hydration)' 과정을 통해 정적 HTML에 이벤트 리스너 등 동적 기능 부착
여기서 잠깐!! 하이드레이션이 뭔지 궁금할 것이다.
하이드레이션은 서버에서 렌더링된 HTML을 클라이언트에서 '활성화'하는 과정으로,
이 과정이 제대로 이루어지지 않으면 상호작용이 불가능하거나 스타일이 깨지는 현상이 발생할 수 있다.
스타일드 컴포넌트가 SSR에서 발생시키는 문제점
스타일드 컴포넌트는 기본적으로 클라이언트 사이드에서 동작하도록 설계되었기 때문에, SSR 환경에서는 몇 가지 문제가 발생할 수 있다.
가장 대표적인 문제는 'FOUC(Flash of Unstyled Content)'이다.
이는 페이지가 스타일 없이 잠시 렌더링된 후 스타일이 적용되는 현상을 말한다.
이러한 현상이 발생하는 이유는 다음과 같다.
- 서버에서 HTML은 생성되지만, 스타일드 컴포넌트가 생성한 CSS는 제대로 포함되지 않음
- 클라이언트가 HTML을 받아 표시할 때 스타일이 없는 상태로 먼저 표시됨
- 자바스크립트가 로드되고 실행된 후에야 스타일드 컴포넌트가 스타일을 동적으로 주입함
이로 인해 사용자는 짧은 순간이지만 스타일이 적용되지 않은 페이지를 보게 될 수 있다.
UX적으로 너무 안좋은 웹서비스가 되지 않을까?
또한, 하이드레이션 불일치(hydration mismatch) 문제도 발생할 수 있다.
서버와 클라이언트에서 생성된 클래스명이 다르면 React는 두 트리가 일치하지 않는다고 판단하고 경고를 발생시키거나 예상치 못한 동작을 할 수 있다.
문제 재현 실험 (TypeScript 환경)
실제 Next.js 프로젝트에서 이 문제를 재현해보았다.
TypeScript를 사용하여 간단한 Next.js 애플리케이션을 만들고 스타일드 컴포넌트를 사용하여 스타일링을 적용했다.
먼저, 기본적인 Next.js 프로젝트를 TypeScript로 설정했다.
npx create-next-app@latest styled-components-ssr-issue --typescript
cd styled-components-ssr-issue
npm install styled-components @types/styled-components
그리고 간단한 페이지 컴포넌트를 만들었다.
// app/page.tsx
"use client";
import styled from 'styled-components';
const Title = styled.h1`
font-size: 2rem;
color: #3498db;
text-align: center;
margin-top: 2rem;
`;
const Container = styled.div`
max-width: 800px;
margin: 0 auto;
padding: 2rem;
background-color: #f8f9fa;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
`;
interface ContentProps {
text: string;
}
const Content = styled.p<ContentProps>`
font-size: 1rem;
line-height: 1.5;
color: ${props => props.text.length > 50 ? '#333' : '#777'};
`;
export default function Home() {
return (
<Container>
<Title>Next.js와 스타일드 컴포넌트 SSR 테스트</Title>
<Content text="이 텍스트는 스타일드 컴포넌트로 스타일링된 컨테이너 안에 있습니다.">
이 텍스트는 스타일드 컴포넌트로 스타일링된 컨테이너 안에 있습니다.
</Content>
</Container>
);
}
이 코드는 TypeScript의 특성을 살려 Content 컴포넌트에 props의 타입을 정의했다.
주목할 점은 Next.js의 App Router 방식에서는 "use client" 지시문을 추가해야 스타일드 컴포넌트가 작동한다는 것이다.
이는 Next.js의 기본 구성에서 서버 컴포넌트를 사용하기 때문이다.
이 상태로 애플리케이션을 실행하고 개발자 도구의 네트워크 탭을 슬로우 3G로 설정한 후 새로고침을 해보았다.
그 결과, 페이지가 처음 로드될 때 스타일이 적용되지 않은 상태로 잠시 표시되다가 스타일이 적용되는 FOUC 현상을 명확하게 관찰할 수 있었다.
느린 3G 환경
느린 3G 환경에서는 스타일이 약 14초 동안 적용이 되지 않은 것을 명확히 확인해 볼 수 있었다.
느린 4G 환경
느린 3G 환경보다 과연 느린 4G 환경에서도 차이가 날까? 했을때 이 환경에서도 약 4초동안 적용이 되지 않았다.
제한 없는 환경
제한을 두지 않은 환경에서도 새로고침 할 때마다 화면처럼 스타일이 늦게 적용하는 것을 볼 수 있다.
더불어, "use client" 지시문을 사용함으로써 서버 컴포넌트의 이점을 잃게 되는 것 같다.
즉, 스타일드 컴포넌트를 사용하기 위해 SSR의 일부 이점을 포기해야 한다는 것이다....
이럴거면 CSS 모듈이나 Tailwind CSS를 공부해서 사용하는게 좋지 않을까???!!
이 글이 Next.js와 TypeScript를 함께 사용하는 프로젝트를 시작하는 다른 학생들이나 개발자들에게 스타일링 방식을 선택할 때 도움이 되었으면 한다.
'Next.js' 카테고리의 다른 글
[Next.js] ❗️입문자 주목❗️ TypeScript로 구현하는 로그인 시스템 튜토리얼 (0) | 2025.03.14 |
---|---|
[Next.js] App Router vs Pages Router (4) | 2025.03.13 |
[Next.js] SSR이란 무엇인가? - Next.js의 서버 사이드 렌더링 이해하기 (1) | 2025.03.12 |
[Next.js] 파일 시스템 기반 라우팅 개념 정리 (0) | 2025.03.10 |
[Next.js] Next.js + TypeScript 프로젝트 생성하기 ⚒️ (2) | 2025.03.09 |