카테고리 없음

Next.js는 왜 프레임워크일까?

CandDoIt 2026. 1. 18. 18:13

필자는 오픈소스를 활용해 기업에게 솔루션을 제공하는 벤처 회사에 재직중인 프론트엔드 개발자입니다.
현재는 LLM을 활용한 B2B 애플리케이션 서비스 개발에서 프론트엔드 개발을 담당하고 있습니다. 저희 팀은 Next JS 프레임워크를 사용하여 프론트엔드 애플리케이션을 개발하고 있습니다. 저는 실무와 사이드 프로젝트에서 Next.js를 활용해 개발하며 얻은 경험을 바탕으로, Next.js가 프레임워크로서 개발자에게 어떤 개발 경험을 제공하는지 고민하고 정리해보았습니다.

NextJS는 왜 프레임워크일까?

React는 라이브러리 레벨이고, Next JS는 프레임워크 레벨입니다. 그렇다면 이 둘을 구분짓는 경계는 무엇일까요?

https://github.com/reactjs/react.dev/pull/5487

 

Replace Create React App recommendation with Vite by t3dotgg · Pull Request #5487 · reactjs/react.dev

Create React App is not a great recommendation to be making, especially for newer developers. As an educator, I run into countless issues w/ new React devs running into unnecessary issues due to th...

github.com

위 PR은 Create React App의 표준을 Vite로 대체하는 제안과 관련된 PR입니다. 해당 PR에는 리액트는 라이브러리이고, 프레임워크가 되기 위한 기준을 설명합니다.

 

PR에서 리액트 팀은 다음과 같은 이유로 리액트는 라이브러리라고 규정짓습니다.

 

  1. React 자체는 UI를 선언적으로 만들고 업데이트하는 데 집중한다.
  2. "앱을 만들기 위해 필요한 나머지 것들(라우팅, 데이터 로딩, SSR/SSG/RSC, 배포/운영)"은 React 바깥의 선택지다.

프론트엔드 프레임워크가 되기위해서는 다음과 같은 기준이 필요합니다.

  1. 앱의 "공통 패턴"을 내장하거나, 강하게 통합해야 한다.
  2. 랜더링/호스팅/성능 전략을 프레임워크 레벨에서 제공해야 한다.

프론트엔드 개발자는 애플리케이션 개발 시, 다음과 같은 문제를 맞이합니다.

  • "라우팅은 어떻게 할 것인가?"
  • "데이터는 언제/어디서 로딩할 것인가?"
  • "로딩 상태, 에러 상태는 어떻게 표현할 것인가?"
  • "인증 상태에 따라 화면을 어떻게 분기할 것인가?"
  • "코드 스플리팅과 캐싱은 누가 책임질 것인가?"

위와 같은 고민은 리액트를 활용하는 모든 개발자가 공통적으로 고민하는 내용입니다.

 

또한 React의 일부 기능은 리액트 패키지만 설치한다고 사용할 수 없습니다.

예를 들어, Server Component, Suspense 기반 데이터 패칭 기능은 서버와 클라이언트의 경계를 전제로 합니다. 즉, 리액트를 단독 사용할 경우, 개발자는 서버 런타임 환경 및 네트워크 계층에 대해 고민을 직접 해야합니다.

 

프레임워크는 개발자가 이러한 반복적인 고민에서 벗어날 수 있도록 합니다. 라우팅, SSR/SSG를 지원하는 서버 실행 환경, 빌드 방식, 데이터 패칭과 캐싱 전략, 코드 스플리팅과 같은 문제들을 프레임워크 레벨의 프로그래밍 인터페이스로 통합 제공하기 때문입니다. 개발자는 개별 기술을 조합하는 대신, 정해진 규칙 안에서 애플리케이션의 구조와 흐름에 집중할 수 있습니다.

 

이러한 관점에서 보면 React는 앱의 공통 구조를 정의하지 않으며, 최신 기능 또한 스펙과 개념 수준에서만 제공합니다. 반면 프레임워크는 이 스펙을 실제로 동작 가능한 형태로 구현하고, 라우팅·데이터 로딩·렌더링 전략을 하나의 체계로 통합하여 애플리케이션 전체 구조를 책임집니다.

 

Next JS가 프레임워크로서 개발자에게 제공하는 인터페이스 

서버 환경

Next.js는 서버 실행 환경 자체를 프레임워크가 추상화합니다.

  • Route Handler, Server Action을 통해서 서버 환경에서 실행할 인스턴스를 설계하고 개발할 수 있습니다.
  • 클라이언트와 서버 경계를 명시적으로 관리

즉, 단순한 HTML + JS 번들 개발이 아닌 서버 환경에서 실행되는 애플리케이션을 개발을 지원합니다.

 

Proxy

Next.js의 서버 사이드는 rewrite, redirect, middleware를 통해 요청 흐름을 제어하는 인터페이스를 제공합니다.

  • 외부 API 서버와의 브라우저의 사이를 proxy를 통해 추성화
  • 클라이언트(브라우저)입장에서는 직접 백엔드 주소를 알 수 없음
  • HTTP 요청과 정적 리소스에 대한 압축 및 버퍼링 전략(gzip 등)을 적용하여 네트워크 통신 비용을 최적화
  • 인증, 경로 제어, 환경 분리 등에 활용 가능

이를 통해 Next.js는 단순히 UI를 전송하는 서버를 넘어, 요청 흐름의 진입 지점(Gateway 역할)을 담당합니다.

 

Routing

Next.js는 파일 시스템 기반 라우팅을 통해 애플리케이션의 구조를 명확하게 정의합니다.

  • 파일과 폴더 구조가 곧 URL 구조가 됨
  • 페이지, 레이아웃, 중첩 라우팅을 프레임워크가 자동 관리
  • App Router 기준으로 서버 컴포넌트와 결합된 라우팅 제공

개발자는 라우팅 로직을 직접 구현하지 않고, 파일 구조를 설계하는 것만으로 화면 흐름을 정의할 수 있습니다.

 

서버 렌더링

Next.js는 서버 렌더링 전략을 선언적으로 선택할 수 있는 인터페이스를 제공합니다.

  • SSR, SSG, ISR를 페이지 또는 요청 단위로 제어
  • App Router에서는 Server Component 렌더링을 기본 제공
  • SEO, 초기 로딩 성능을 고려한 렌더링 전략 수립 가능

개발자는 “언제, 어디서 렌더링할 것인가”를 코드 레벨에서 표현할 수 있습니다.

 

코드 스플리팅

코드 스플리팅이란 브라우저에서 실행될 JS 코드를 한번에 내려보내지 않고, 필요한 상황에 맞게 내려주는 기법입니다. 리액트는 SPA 기반으로 동작하기 때문에, 애플리케이션 규모가 커질 수록 JS 코드를 한번에 내려받으면 초기 로딩이 느려집니다. 이는 좋지 못한 사용자 경험으로 바로 이어질 수 있습니다.

 

Next.js는 코드 스플리팅을 다음과 같이 프레임워크 수준에서 처리해줍니다.

  • 라우트 단위 자동 코드 스플리팅
  • use client 기준으로 클라이언트 번들 최소화
  • dynamic import를 통한 컴포넌트 단위 분할
  • 서버 컴포넌트 기반으로 불필요한 JS 전송 제거

개발자는 JS 번들 분할을 직접 설계하지 않아도, 코드 스플리팅을 통해 리액트의 고질적인 성능문제 고민에서 벗어날 수 있습니다.

 

프레임워크의 추상화로 인해 겪은 불편함 사례

Next.js는 많은 문제를 프레임워크 레벨에서 추상화해 개발자의 부담을 줄여줍니다.
하지만 이러한 추상화는 항상 장점만 존재하지는 않았습니다. 개발을 하다보면 오히려 원인을 파악하기 어려운 문제도 직면했습니다.

 

Proxy + gzip 환경에서의 Streaming 제약 (SSE, WS, Web-RTC 등)

제가 개발을 담당했던 부분에서는 서버 엔진에서 실행되고 있는 현황을 실시간으로 보여주는 기능들이 존재했습니다.(예를 들어 특정 프로세스의 실시간 로그를 조회하거나, 작업 진행 상황을 스트림 형태로 화면에 반영하는 기능). 이러한 요구사항을 충족하기 위해서는 서버에서 생성되는 데이터를 stream 방식으로 지속적으로 전송하고, 클라이언트가 이를 즉시 처리할 수 있어야 합니다.

 

Next.js는 rewrite, middleware 등을 통해 프록시 서버가 클라이언트 대신 API 요청을 전달하는 구조를 제공합니다. 이 구조는 일반적인 REST API 호출에서는 편리하지만, 내부적으로는 프록시 레이어에서 요청–응답 전체 lifecycle을 추상화하여 처리합니다. 이 과정에서 기본 동작으로 적용되는 응답 압축(gzip) 및 버퍼링 로직은 SSE, WebSocket, WebRTC와 같은 스트리밍 기반 통신에서는 제약 요인이 됩니다.

 

특히 문제였던 지점은, proxy rewrite를 사용하는 경우 스트리밍 응답뿐 아니라 API 요청 단계부터 압축·버퍼링 경로를 타는 것처럼 보이는 현상이 발생했다는 점입니다. 실제로 HTTP 요청 자체가 gzip으로 압축되는 것은 아니지만, 프록시가 요청을 수신하는 시점에서 이미 “응답은 압축 가능한 대상으로 처리된다”는 전제 하에 내부 파이프라인을 구성하면서, 스트림 응답이 gzip/buffered stream으로 래핑되는 구조를 갖게 됩니다. 이로 인해 SSE와 같이 즉시 flush 되어야 할 데이터가 내부 버퍼에 머무르며 지연되거나, ping 이벤트조차 전달되지 않는 문제가 발생했습니다.

 

문제는 이러한 gzip 적용 여부나 버퍼링 동작을 개발자가 명시적으로 제어하기 어렵다는 점이었습니다. Next.js 설정(next.config.js)이나 애플리케이션 코드 레벨에서 일부 제어는 가능하지만, rewrite를 통한 프록시 경로에서는 프레임워크 내부 추상화가 깊게 개입하기 때문에 실제로 어떤 시점에서 압축이 적용되고, 어디에서 버퍼링이 발생하는지 파악하기가 쉽지 않았습니다. 관련 자료나 공식 문서 또한 충분하지 않아, 문제의 원인이 애플리케이션 코드인지, 프레임워크의 내부 동작인지 구분하는 데 많은 시간이 소요되었습니다.

 

결과적으로 이 경험을 통해, Next.js의 프록시 추상화는 스트리밍 통신에 있어 내부 동작이 드러나지 않는다는 점에서 구조적인 한계를 가진다는 인상을 받았습니다. 단순 API 중계에는 유용하지만, 실시간 로그 조회나 상태 스트리밍과 같이 응답의 즉시성과 지속성이 중요한 기능에서는 gzip·버퍼링과 같은 기본 최적화 전략이 오히려 장애 요인이 될 수 있으며, 이 경우에는 프록시 레이어를 우회하거나 스트리밍 전용 서버를 분리하는 아키텍처적 판단이 필요하다는 결론에 이르렀습니다.

 

React Server Component(RSC) 직렬화 이슈

서버 컴포넌트에서 클라이언트 컴포넌트로 데이터를 전달할 때, 데이터는 JSON 형태로 변환되어야 합니다.

이 과정에서는 다음과 같은 기술적 제약이 발생할 수 있습니다.

  • 클래스 인스턴스, 함수, Date 객체, 복잡한 객체를 props로 넘길 수 없는 문제 
  • “왜 이 값이 안 되는지”를 추적하기 어려움 (에러 메시지가 직관적이지 않음)

서버 컴포넌트와 클라이언트 컴포넌트는 네트워크의 경계이기 때문에, "직렬화 가능한 JSON 데이터"만 전달 가능합니다. 같은 객체라도 직렬화하기 어려운 복잡한 데이터를 서버 컴포넌트에서  클라이언트 컴포넌트로 전달할 경우 런타임 에러가 발생할 수 있습니다. 문제는 개발자가 작성하는 코드 수준에서는 이러한 경계가 뚜렷하게 나타나지 않습니다. 또한 왜 이러한 문제가 발생하는지에 대한 뚜렷한 에러메세지가 나타나지 않습니다.

 

마무리하며

Next.js는 프론트엔드 개발자에게 생각보다 훨씬 넓은 영역을 경험하게 해주는 프레임워크라고 느꼈습니다. 라우팅과 렌더링 전략뿐 아니라 서버 환경과 요청 흐름까지 제어할 수 있다는 점에서 강력한 프론트엔드 프레임워크입니다. 하지만 실제로 개발을 진행하면서, 문제가 발생했을 때 그것이 프레임워크의 내부 동작에서 비롯된 것인지, 아니면 내가 작성한 코드의 문제인지 명확하게 판단하기 어려운 순간들도 자주 마주하게 되었습니다. 그만큼 디버깅 과정도 쉽지 않았습니다.

 

이러한 경험을 통해, 프레임워크가 제공하는 편의성은 결국 그 내부 동작을 이해하려는 노력이 함께할 때 비로소 온전히 활용될 수 있다는 점을 느끼게 되었습니다. 동시에 이런 시행착오들은 개인 개발자의 숙제이기도 하지만, 프론트엔드 프레임워크가 앞으로 더 성숙해지기 위해 반드시 거쳐야 할 과정이기도 하다고 생각합니다.

 

그럼에도 불구하고 저는 여전히 Next.js를 선택하고 있습니다. Next.js가 제공하는 추상화가 완벽해서가 아니라, 웹 애플리케이션을 구성하는 문제들을 하나의 프레임워크 안에서 일관되게 다룰 수 있게 해주기 때문입니다. 라우팅, 서버 렌더링, 데이터 패칭, 서버 환경, 코드 스플리팅 등 다양한 요소를 각기 다른 도구로 조합하는 대신, 하나의 프레임워크 안에서 설계할 수 있다는 점은 큰 장점입니다.

 

물론 프레임워크가 모든 상황을 대신 해결해주지는 않습니다. 때로는 내부 동작을 이해하지 못한 채 사용하는 추상화가 오히려 문제의 원인이 되기도 합니다. 하지만 이러한 불편함은 Next.js가 애플리케이션의 구조와 실행 방식을 함께 책임지는 프레임워크이기 때문에 생기는 자연스러운 비용일 수 있습니다.

 

결국 Next.js를 사용한다는 것은 편리한 도구를 선택하는 일이 아니라, 프레임워크가 제시하는 규칙과 추상화를 이해하고 받아들이는 선택입니다. 소프트웨어 시장에서 최선의 선택지는 없지만, 적어도 복잡한 웹 애플리케이션을 다루는 데 있어 충분히 설득력 있는 선택지라고 생각합니다.