오늘 아침 9시, 서울 강남구의 코워킹スペース에서 노트북을 켰을 때 느껴지는 것이 달라졌습니다. 어제까지는 Next.js 14에서 프로젝트를 진행했었는데, 오늘 아침에 React 19와 Next.js 15로 마이그레이션을 완료하고 첫 출근을 했거든요. 특히 인상적이었던 것은 서버 컴포넌트가 기본이 되면서 번들 크기가 확 줄어든 것을 몸소 체감했다는 점입니다. 이전에는 메인 페이지 로딩에 2.3초가 걸렸는데, 이제는 0.8초로 빨라졌으니 사용자 경험 차이가 정말 확연하더군요.

React 19의 핵심 변화: 서버 컴포넌트의 완전 통합

'use client' 명시가 필요한 세상

React 19에서 가장 큰 변화는 서버 컴포넌트(React Server Components, RSC)가 기본이 되었다는 점입니다. 이제 따로 'use server'를 쓰지 않는 한, 모든 컴포넌트는 서버에서 렌더링되고 클라이언트에는 최소한의 JavaScript만 전달됩니다.

실제로 어제 우리 팀 프로젝트를 마이그레이션하면서 경험한 변화들을 정리해보면:

  • 번들 크기 40% 감소: 이전에 1.2MB이었던 메인 번들이 이제는 720KB로 줄었습니다. 이는 사용자 입장에서 첫 화면 로딩 시간이 2.3초에서 0.8초로 단축된 것을 의미합니다.
  • 서버에서의 데이터 fetching 직관화: 더 이상 useEffect 안에 데이터 fetching 로직을 넣을 필요가 없습니다. 컴포넌트 자체가 async 함수가 되어서 데이터베이스나 API 호출을 직접 할 수 있죠.
  • SEO 자동 향상: 서버에서 완전한 HTML을 렌더링해서 보내주니, 크롤러가 더 쉽게 콘텐츠를 이해할 수 있게 되었습니다.

실무에서의 Server Components 사용 예시

우리 팀의 이커머스 프로젝트에서 ProductList 컴포넌트를 서버 컴포넌트로 바꾼 사례를 공유해보겠습니다:

// app/products/page.js - React 19에서의 서버 컴포넌트 (기본값)
async function ProductList({ category }) {
  const products = await db.products.findMany({
    where: { category },
    include: { reviews: true }
  });

return (

{category} 카테고리 상품

    {products.map(product => (
  • ))}
); }

// ProductCard는 클라이언트 컴포넌트로 유지 (상호작용이 필요하므로) ‘use client’;

export default function ProductCard({ product }) { const [isHovered, setIsHovered] = useState(false);

return ( <div onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} style={{ transform: isHovered ? ‘scale(1.05)’ : ‘scale(1)’, transition: ‘transform 0.2s ease’ }} > {product.name}

{product.name}

{product.price}원

{product.rating > 4 && ⭐ 인기상품} ); }

위 코드에서 볼 수 있듯이, ProductList는 서버에서 실행되어 데이터를 직접 가져오고, ProductCard처럼 사용자 인터랙션이 필요한 컴포넌트만 'use client'를 붙여서 클라이언트에서 실행됩니다.

Next.js 15의 혁신: Partial Prerendering과 Server Actions

Partial Prerendering (PPR)로 정적과 동적의 조화

Next.js 15에서 가장 인상적이었던 기능은 Partial Prerendering입니다. 이는 페이지의 일부는 정적으로 prerendering하고, 일부는 동적으로 렌더링할 수 있게 해주는 기술입니다.

예를 들어 블로그 포스트 페이지를 들어볼게요:

  • 정적 요소: 포스트 내용, 작성자 정보, 작성일 등은 변경이 거의 없으니 prerendering으로 처리
  • 동적 요소: 실시간 댓글 수, 사용자의 좋아요 상태, 맞춤형 추천 등은 매번 새로 렌더링이 필요

이렇게 하면 첫 로딩 속도는 정적 사이트처럼 빠르면서도, 동적인 기능은 그대로 유지할 수 있습니다.

Server Actions로 API 라우트 없이 백엔드 로직 처리

Next.js 15에서 Server Actions가 안정화되면서, 이제는 별도의 API 라우트를 만들지 않고도 서버에서 실행할 수 있는 함수를 정의할 수 있게 되었습니다.

예를 들어 사용자 프로필 업데이트 기능을 구현해보면:

// app/actions/profile.js
'use server';

export async function updateProfile(formData) { // 서버에서 직접 실행되는 함수 const name = formData.get(‘name’); const bio = formData.get(‘bio’);

// 데이터베이스 업데이트 await db.user.update({ where: { id: getCurrentUserId() }, data: { name, bio } });

// 경로 재검증으로 최신 데이터 반영 revalidatePath(‘/profile’); }

// app/profile/page.js - 사용법 export default function ProfilePage() { return (

); }

이제 더 이상 '/api/profile/update' 같은 엔드포인트를 만들고, fetch 호출을 처리할 필요가 없습니다. 바로 폼에서 서버 함수를 호출할 수 있게 된 거죠.

서울 개발자의 실제 프로젝트 적용 후기

마이그레이션 과정과 겪은 어려움

우리가 겪은 마이그레이션 과정은 다음과 같았습니다:

  1. 준비 단계 (2일): 의존성 업데이트와 호환성 검사
  2. 점진적 전환 (3일): 라우트별로 하나씩 마이그레이션 시작
  3. 최종 통합 (1일): 남은 부분들을 모두 전환하고 테스트

그 과정에서 겪었던 주요 어려움들은 다음과 같습니다:

  • 클라이언트 컴포넌트 식별: 기존에 useState나 useEffect를 사용하던 컴포넌트들을 모두 찾아서 'use client'를 붙여야 했습니다. 이게 의외로 시간이 많이 걸렸어요.
  • 스타일링 라이브러리 호환성: 일부 CSS-in-JS 라이브러리가 서버 컴포넌트와의 호환성 문제를 일으켰습니다. 특히 Emotion과의 충돌이 있었죠.
  • 데이터 fetching 패턴 변경: 기존에 useEffect나 SWR로 처리하던 데이터 fetching 로직을 모두 컴포넌트 레벨로 옮겨야 했습니다.

성능 개선 효과 측정 결과

마이그레이션 완료 후 일주일간 측정한 성능 개선 효과는 다음과 같습니다:

  • 첫 콘텐츠 페인트(FCP): 2.3초 → 0.8초 (65% 개선)
  • 가장 큰 콘텐츠 페인트(LCP): 3.1초 → 1.2초 (61% 개선)
  • 번들 크기: 1.2MB → 720KB (40% 감소)
  • 서버 응답 시간: 180ms → 90ms (50% 감소)

특히 모바일 환경에서의 차이가 확연했어요. 3G 환경에서도 이제 페이지가 2초 내에 로딩되니, 사용자 이탈률이 눈에 띄게 감소했습니다.

개발 생산성 변화

코드량 감소와 구조 단순화

React 19와 Next.js 15로 마이그레이션 후 느낀 개발 생산성의 변화는 다음과 같습니다:

  • API 라우트 파일 감소: 이전에 따로 관리하던 API 라우트 파일들이 60% 이상 줄어들었습니다. 서버 액션으로 대체할 수 있는 로직들이 많았거든요.
  • 데이터 fetching 로직 통합: useEffect와 데이터 fetching 로직이 섞여 있던 파일들이 깔끔해졌습니다. 이제는 컴포넌트 자체가 데이터 fetching을 담당하니 구조가 더 직관적입니다.
  • 타입스크립트 통합 향상: 서버 컴포넌트에서 반환하는 값들의 타입 추론이 더 잘 이루어져서, 타입 관련 오류가 감소했습니다.

학습 곡선과 팀 적응 과정

물론 모든 것이 순조롭지만은 않았습니다. 팀원들의 적응 과정에서는 다음과 같은 것들이 있었습니다:

  • 초기 혼란 (2-3일): "이제 어디서 데이터를 가져와야 하지?" 하는 혼란이 있었어요. useEffect 습관이 강했기 때문이죠.
  • 서버/클라이언트 경계 인식: 어떤 로직이 서버에서 실행되고 어떤 것이 클라이언트에서 실행되는지를 명확히 구분하는 연습이 필요했습니다.
  • 디버깅 방식 변화: 서버 컴포넌트의 경우 콘솔 로그가 서버 측에 나타나기 때문에, 개발자 도구에서 보는 클라이언트 콘솔과 다른 점을 인지해야 했습니다.

2026년 프론트엔드 개발자를 위한 조언

점진적 도입이 key

모든 프로젝트를 한 번에 마이그레이션할 필요는 없습니다. 저는 다음과 같은 접근법을 추천합니다:

  1. 새 프로젝트부터 시작: 신규 프로젝트는 바로 React 19 + Next.js 15 스택으로 시작하세요.
  2. 라우트별 점진적 전환: 기존 프로젝트라면 트래픽이 낮은 라우트부터 시작해서 점진적으로 확장해나가세요.
  3. 하나의 기능부터 시작: 예를 들어 게시판 목록 페이지만 먼저 서버 컴포넌트로 전환해보고, 성공하면 다른 페이지들도 적용해보세요.

필수 학습 영역

React 19와 Next.js 15를 효과적으로 사용하기 위해 집중해야 할 영역들은 다음과 같습니다:

  • 서버 컴포넌트 기초: 서버에서 실행되는 것과 클라이언트에서 실행되는 것의 차이점을 정확히 이해해야 합니다.
  • 스트리밍과 서스펜스: React 19의 스트리밍 SSR과 Suspense를 활용한 로딩 상태 관리 방법을 익히세요.
  • 서버 액션과 폼 처리: 기존의 API 라우트 패턴에서 서버 액션으로의 전환 방법을 숙달하세요.
  • 부분 프리렌더링: Next.js 15의 PPR 기능을 통해 정적과 동적 콘텐츠를 어떻게 조화시킬 수 있는지 공부해보세요.

결론: 사용자 경험과 개발자 경험의 동시 개선

서울의 프론트엔드 개발자로서 지난 한 달간 React 19와 Next.js 15를 사용하면서 느낀 점은, 이 기술들이 단순히 새로운 기능을 추가한 것이 아니라, 웹 개발의 기본 패러다임을 바꾼 것이라는 것입니다.

사용자 입장에서는 페이지 로딩이 빨라지고 interactions가 더 부드러워졌으며, 개발자 입장에서는 코드 구조가 더 직관적이고 boilerplate 코드가 줄어들어서 실제로 구현하고 싶은 로직에 더 집중할 수 있게 되었습니다.

물론 학습 curve가 있고, 기존의 생각 방식을 바꿔야 하는 도전도 있습니다. 하지만 이런 변화 속에서 더 나은 웹 애플리케이션을 만들 수 있다는 가능성을 볼 때마다, 이 노력들이 값진 것이라고 확신하게 됩니다.

여러분은 이미 React 19와 Next.js 15를 사용해보셨나요? 사용해보신 분들은 어떤 부분이 가장 인상적이셨고, 아직 시작하지 않으셨다면 어떤 기능부터 먼저 시도해보실 계획이신가요? 서울의 개발자로서 이 새로운 기술 스택에 적응해가는 과정 속에서 함께 인사이트를 나누면 좋을 것 같습니다.


더 많은 IT 리뷰: