렌더링 원리를 알아야 AI 최적화도 제대로 된다: Next.js 성능 워크플로우

렌더링 원리를 알아야 AI 최적화도 제대로 된다: Next.js 성능 워크플로우

RSC와 App Router의 작동 방식을 이해한 뒤 GPT를 보조 도구로 삼아야 진짜 성능 개선이 시작된다.

Next.js 렌더링 RSC Payload App Router Hydration 최적화 동적 임포트 Provider 구조 GPT 성능 최적화 Core Web Vitals
광고

최적화 전에 먼저 물어야 할 것: "왜 느린가?"

useCallback을 추가하고, memo를 꼼꼼히 챙겨도 Lighthouse 점수는 제자리였다. velog에 공개된 한 사이드 프로젝트 개선 사례처럼, 컴포넌트 단위 최적화와 페이지 전체 로딩 흐름의 최적화는 전혀 다른 레벨의 문제다. 진짜 체감 성능을 결정하는 건 "어떤 코드가 언제 로드되고, 어떤 데이터가 언제 필요한가"라는 구조적 질문이다. 그리고 그 질문에 제대로 답하려면, Next.js 렌더링 원리를 먼저 내 것으로 만들어야 한다.

MPA → SPA → SSR, 그리고 Next.js가 택한 하이브리드

Next.js 렌더링 심화 아티클(velog, @csh001231)이 짚어주는 핵심은 명쾌하다. MPA는 페이지 이동마다 서버에서 새 HTML을 받아 화면이 깜빡인다. SPA는 첫 로딩이 무겁고 SEO에 불리하다. SSR은 이 둘의 단점을 보완하기 위해 등장했지만, Next.js는 여기서 한 발 더 나아간다. 초기 진입은 서버 렌더링으로 FCP와 SEO를 확보하고, 이후 네비게이션은 SPA처럼 클라이언트에서 처리해 전환 경험을 챙기는 하이브리드 방식이다. Pages Router는 이 전략을 페이지 단위로 구현했고, App Router는 컴포넌트 단위로 세분화했다.

App Router가 바꾼 것: 렌더링 단위의 혁신

Pages Router에서는 getServerSideProps를 선언한 순간 그 페이지의 모든 컴포넌트가 SSR로 묶였다. 한 컴포넌트만 동적으로, 나머지는 정적으로 처리하고 싶어도 구조적으로 불가능했다. App Router는 이 제약을 컴포넌트 단위로 해체했다. "use client"를 선언하지 않으면 기본이 서버 컴포넌트이고, 같은 레이아웃 안에서 정적 사이드바와 실시간 위젯이 서로 다른 렌더링 전략을 가져갈 수 있다.

여기서 흔히 생기는 오해 하나. "use client"는 "클라이언트에서만 실행해라"가 아니라 "여기서부터 클라이언트 번들에 포함시켜라"는 번들링 경계 선언이다. 클라이언트 컴포넌트도 초기 렌더링은 서버에서 진행된다. 렌더링 위치가 아닌 번들 경계를 선언하는 것이다.

RSC Payload와 선택적 Hydration이 TTI를 바꾸는 방식

Pages Router SSR의 숨겨진 함정은 FCP와 TTI의 괴리다. 서버에서 완성된 HTML이 내려오니 화면은 빨리 보이지만, 전체 페이지를 Hydration하는 동안 아무것도 클릭되지 않는 구간이 생긴다. App Router는 이 구간을 줄이는 방식으로 설계됐다.

RSC Payload는 서버 컴포넌트의 렌더링 결과를 직렬화한 React 전용 포맷이다. 서버 컴포넌트는 JS 번들에 포함되지 않고, 실행 결과만 클라이언트에 전달된다. Hydration은 "use client"로 선언된 컴포넌트에서만 선택적으로 진행된다. 번들 크기가 줄고, 브라우저가 파싱해야 할 JS가 줄어드니 TTI가 직접적으로 개선된다.

거기에 Suspense 기반 스트리밍까지 더해지면 효과는 배가된다. Pages Router에서는 느린 데이터 하나가 전체 페이지 응답을 블로킹했다. App Router에서는 <Suspense> 경계 안의 느린 컴포넌트만 대기하고, 준비된 헤더와 레이아웃은 즉시 사용자에게 전달된다. TTFB가 같아도 체감 로딩이 달라지는 이유가 여기에 있다.

AI 보조 최적화: GPT는 "무엇을 고쳐야 하는지" 아는 동반자

렌더링 원리를 이해했다면, 이제 AI를 어떻게 성능 최적화에 투입할 수 있을까. velog(@dragonperson_kim)의 사이드 프로젝트 성능 개선 사례는 GPT를 활용한 실전 워크플로우의 좋은 스냅숏을 제공한다. 흥미로운 점은 GPT가 단순히 "이 함수를 memo로 감싸라"고 제안하는 게 아니었다는 것이다. 전체 로딩 경로를 먼저 진단하고, Provider 구조와 데이터 흐름, 코드 분할 전략을 함께 재설계하는 방향으로 대화가 이어졌다.

구체적으로는 세 가지 레이어에서 개선이 일어났다.

첫째, 이미지 로딩 전략. next/imagepriority를 LCP 후보 이미지에만 선별적으로 적용하고, 나머지는 loading="lazy"decoding="async"로 전환해 초기 렌더 구간의 네트워크 경쟁을 줄였다. 남발된 priority가 오히려 초기 로딩을 느리게 만들 수 있다는 역설을 GPT와의 대화에서 짚어낸 것이 포인트다.

둘째, Provider 범위 재설계. 전역에서 한 번에 감싸던 Provider 구조를 기능과 라우트 기준으로 분리했다. SessionProvider를 인가가 필요한 페이지에서만 사용하도록 범위를 좁히고, 모달·토스트 처리 로직을 상태 생성 / 렌더링 / cleanup으로 역할별로 쪼갰다. 이건 성능 숫자 이전에 "어디서 무엇을 책임지는가"가 보이는 구조를 만드는 작업이었다.

셋째, 동적 임포트(dynamic import) 적용. 초기 진입 부담이 큰 write 페이지의 무거운 영역을 동적 임포트로 초기 렌더 경로에서 분리했다. 스켈레톤 UI 대신 opacity 애니메이션으로 자연스러운 전환을 만든 것도 주목할 만하다. 로딩 UX는 기술 결정이면서 동시에 경험 설계다.

시사점: 렌더링 이해 없는 AI 프롬프트는 절반짜리다

두 사례를 나란히 놓으면 하나의 흐름이 보인다. 렌더링 원리 이해 → 병목 지점 진단 → AI 보조 구조 개선. 이 순서가 지켜지지 않으면 GPT에게 아무리 좋은 프롬프트를 던져도 답은 피상적일 수밖에 없다. "성능을 개선해줘"라는 요청과 "App Router에서 클라이언트 번들 크기를 줄이기 위해 어떤 컴포넌트를 서버 컴포넌트로 전환할 수 있는지 분석해줘"라는 요청의 결과물은 질적으로 다르다.

결국 AI 도구는 내가 어디를 보고 있는지 알 때 제 역할을 한다. RSC Payload가 어떻게 동작하는지, Hydration 대상이 어떻게 결정되는지를 알고 있어야 GPT의 제안이 구조에 맞는지 판단할 수 있다. AI는 탐색 속도를 높여주지만, 탐색 방향을 정하는 건 여전히 개발자의 몫이다.

전망: 컴포넌트 단위 전략이 AI 협업의 기본 단위가 된다

App Router의 컴포넌트 단위 렌더링 전략은 AI 보조 개발과 궁합이 좋다. 컴포넌트 경계가 명확할수록 AI에게 "이 컴포넌트를 서버 컴포넌트로 바꾸면 어떤 사이드 이펙트가 생기나"처럼 범위를 좁힌 질문을 던질 수 있다. React Compiler가 성숙해지고 Partial Prerendering이 안정화되면, 개발자는 렌더링 경계 설계에 더 집중하고 실행 최적화는 프레임워크와 AI가 분담하는 구조가 강화될 것이다.

지금 당장 실천할 수 있는 출발점은 간단하다. Lighthouse를 켜고, 어느 구간에서 시간이 가장 많이 소비되는지 먼저 확인하라. FCP는 빠른데 TTI가 느리다면 Hydration 범위를, TTFB가 느리다면 서버 데이터 페칭 구조를 들여다볼 차례다. 진단이 되면 GPT는 훨씬 정확한 동반자가 된다.

출처

더 많은 AI 트렌드를 Seedora 앱에서 확인하세요