Vercel 이 지들 컨퍼런스 열리는 24일이 채 되기도 전에 부랴부랴인지 뭔지 모르겠지만 Next.js 15 버전을 대뜸 출시했다.
현지하지만, React 19 버전은 RC이다.
참고로 불행 중 다행인 것은, 페이지 라우터 쓰는 개발자들은 React 18 버전 유지하면서 15 버전 업그레이드가 가능하다는 것.
그럼, 좋아. 지금 어떤 꼬라지인지 함께 보도록 하자.
공식 블로그에서 소개한 주요 변경점을 번역하겠다.
fetch
), GET 라우팅 핸들러, 클라이언트 탐색에서 기본값으로 캐시를 사용하지 않음.unstable_after
API (실험적): 응답이 끝난 후 후처리 기능 추가.instrumentation.js
API (Stable): 서버 생명 주기를 감시하는 안정적인 API.next.config
: next.config.ts
파일도 이제 TypeScript 지원.여기서 눈여겨볼 만한(이라 읽고 내가 쓴다) 변경 사항만 자세히 설명해보겠다.
사실 대부분의 기능들은 앱 라우터에서 변경할 사항이 많다.
서버 컴포넌트나 라우팅 핸들러, 서버 액션에서 헤더 및 쿠키를 가져올 때 사용하는 API가 있다.
import { headers, cookies } from 'next/headers';
여태까지 그냥 함수 호출했는데, 이제는 강제 비동기 행이다. 즉, await
붙이든가 then
콜백 쓰든가 해서 비동기로 호출해야 한다.
// Next.js 14 이전
const header = headers();
// Next.js 15 이후
const header = await headers();
만약 비동기로 호출하지 않을 경우 개발 시 콘솔에서 warning 이 뜬다고 한다. 작동은 된다고 한다. 완전 막은 것은 아니다.
이와 함께 앱 라우터에게 절망을 하나 선사했는데, 페이지 컴포넌트(page.js
)에서 받을 수 있는 라우팅 인자(params
)와 검색 인자(searchParams
) 또한 비동기가 강제된다. 따라서 아래 둘 중 하나의 함수 선언을 해야 한다.
// 예시: /my/page/[name]
// 접속: /my/page/foo?name=bar
export default async function MyPageComponent({ params, searchParams }) {
return <ul>
<li>네가 조회한 라우팅 명칭: { params.name }</li>
<li>네가 조회한 검색 조건: { searchParams.name }</li>
</ul>;
}
use
훅을 사용한 동기 함수 컴포넌트 (클라이언트 컴포넌트에 추천)'use client';
import { use } from 'react';
// 예시: /my/page/[name]
// 접속: /my/page/foo?name=bar
export default function MyPageComponent(props) {
const params = use(props.params);
const searchParams = use(props.searchParams);
return <ul>
<li>네가 조회한 라우팅 명칭: { params.name }</li>
<li>네가 조회한 검색 조건: { searchParams.name }</li>
</ul>;
}
서버 액션과 라우팅 핸들러에 설마 동기 함수 고집하는 일은 없겠지? 믿고 넘어가도록 하겠다.
귀찮아서 짧게 설명하겠다. 이제 서버 컴포넌트 및 GET
라우터에 본문 시작 전 export const dynamic = 'force-dynamic
이지랄 할 필요 없다. 기본값이다.
하지만 그래도 난 캐시해야 한다면, export const dynamic = 'force-cache
쓰면 된다. 그럼 이전 버전 기본값처럼 동작한다.
참고로 POST
등의 변경요청 가능한 핸들러와 서버 액션은 처음부터 캐시 안하고 특성 상 하지도 못하니 그리 알도록 한다.
당장 벨로그 돌아다녀도 React 19 관련 글 많으니까 그걸로 대신하기 바란다. 설명 귀찮네 증말.
참고로 이거 쓰고 싶을 경우 SWC 버리고 Babel 써야 한다. 왜냐면 아직 Babel 변환기만 지원하기 때문이다.
물론 작업 하고 있다고 하니 다음 버전을 기다려 보도록 하자...?!
그래도 난 존나 힙하거나 모험, 또는 좋은 행동인 기여를 하고 싶다면 공식 메뉴얼을 참고하라.
after
(실험적)비즈니스에서 많이 요구하는 기능이 드디어 실험적이라도 나왔다. 나도 당장 쓰고싶은 생각에 목마를 지경이다.
어쨌든, 그냥은 못 쓰고, 세팅해야 하기 때문에 사용 방법은 공식 메뉴얼을 참고하기 바란다. 사용 예시는 아래와 같다.
import { unstable_after as after } from 'next/server';
import { getBoardDetail, increaseBoardCount } from '@/server/db/board';
// 게시판 상세 페이지에 대한 서버 컴포넌트
export default async function BoardDetailPage({ params: { boardSeq } }) {
// 부 업무
after(async () => {
// 게시물을 읽었으면 조회수를 늘려야지.
await increaseBoardCount(boardSeq);
});
// 게시물 상세 정보 취득
const detail = await getBoardDetail(boardSeq);
// 주 업무
return <>{/*여기에 게시물 상세 구조 작성*/}</>;
}
사용 가능한 곳은 서버 컴포넌트, 메타데이터, 서버 액션, 라우팅 핸들러, 그리고 middleware
이다.
이게 생기는 이점이라면, 바로 자체 호스팅이냐 클라우드냐 따로따로 고민할 필요 없이 프레임워크 단에서 한방에 후처리를 해결할 수 있는 점이 되겠다.
instrumentation.js
사실 나도 이거 필요했다. 배포한 뒤에 서버 오류를 발견하는 건 꽤 귀찮고 힘든 일이었다.
하지만 Next.js 15 부터는 이런 귀찮은 일을 한방에 해결해 줄 기능이 드디어 안정화가 됐다.
사용 방법은 middleware.js
와 같은 폴더에 instrumentation.js
(또는 ts
파일) 만들고 아래 2개 함수를 작성하면 된다.
자세한 사용 방법은 공식 메뉴얼 참고.
export async function onRequestError(err, request, context) {
// 여기에 매 요청 시 오류났을 때 업무를 구현하도록 한다.
// err: 오류 객체(err.message 로 오류 내용 출력)
// request: 일반 Request 객체보다 너무 축약된 { path, method, header } 만 제공한다.
// context: 호출 주체인데, 이건 좀 설명하기 복잡하므로 공식 메뉴얼 참고하라.
}
export async function register() {
// 여기에 위 onRequestError 호출 전에 '초기화'해야 하는 업무를 구현하면 된다.
}
이 기능을 구현하기 위해 Sentry 라고 앱 모니터링 서비스 업체와 협업했다고 한다.
한국으로 치면 대충 제니퍼 비슷한 곳.
<Form />
컴포넌트Next.js 차원에서 양식 이동 시에도 클라이언트 탐색을 지원하는 <Form>
컴포넌트를 제공하기 시작했다.
이녀석이 왜 있냐고 물어본다면, 주로 조회 페이지에서 검색 양식을 만들 때에 유용하다고 생각하면 쉽다.
사용하고 싶다면, 공식 메뉴얼에 사용 방법을 숙지하도록 한다.
이거 없었을 때 검색 양식 업무를 구현하려면 기존 <form>
태그에 onSubmit
에다가 e.preventDefault()
먼저 걸고 이것저것 로직 처리한 다음 useRouter
훅에서 초기화한 라우팅 이동 함수에다가 라우팅 경로와 인자를 전달하여 처리하는 난리 부르스를 쳤을 것이다.
그리고, 이 컴포넌트로 인해 서버 컴포넌트에서도 손쉽게 검색 양식 같은 업무를 작성할 수 있게 됐다.
특히 이걸 좀 더 발전시켜서 zod
같은 유효성 검사 라이브러리와 시너지 작용도 기대할 수 있다.
Next.js 에서 자체 호스팅 기능을 향상했다고는 하는데, 대충 내용은 이렇다.
sharp
를 별도로 설치할 필요가 없어졌고 Next.js 가 알아서 감지해서 필요 시 작동하도록 바뀌었다.대충 CDN 관련 설정에 대한 불만 및 이슈를 제기해서 나온 결과라 보면 되겠다.
주로 보안 쪽에서 많이 제기한 서버 액션에 대한 악용에 대해 많은 피드백 덕분인지 서버 액션이 이제 아래와 같이 동작하게 된다.
첫번째가 뭔 소린지 모르겠다면, 공식 메뉴얼을 보도록 한다. 네가 평소 CSRF 방어를 위해 조치했던 거와 비슷하다고 보면 된다. 물론 이것도 걱정되면 더해도 된다.
next.config.js
에서,
bundlePagesRouterDependencies
옵션을 설정하면 앱 라우터처럼 외부 패키지 번들링이 이루어진다.serverExternalPackages
배열 속성으로 설정 가능하다이게 쓸모가 없는 건 아닌게, 주로 ssh2
같이 외부 리소스 연계와 관련된 라이브러리 및 무거운 라이브러리가 하나로 합쳐지거나, 하나로 합쳐질 때 문제가 생기는 걸 방지하기 위해 존재하는 옵션이니, 패키지 떡칠한 개발자들 특히 한번씩은 체크하기 바란다.
다들 알다시피 ESLint 설정 형식이 배열 방식으로 바뀌는 등 여러 변경점이 발생했는데, Next.js 14까지는 ESLint 8 당시 형식 말고는 돌아가지 않아 많은 불만이 있었는데, 드디어 이를 Next.js 15에서 해결했다.
참고로 ESLint 기존 형식에서 신규 형식으로 변환하도록 ESLint 에서 제공하고 있기는 하다.
장황하게 Next.js 15 향상점에 대해 얘기를 했는데, 막상 React 19가 아직 정식 출시가 안된 상황이다.
멀리 볼 것도 없이, 당장에 큰 이유는 바로 지난번 SPA 개발자들 빡치게 한 Suspense 커밋 사건이다.
좋아. 그러면 이제 그다음 볼 곳은 바로 React 19 milestone 이다.
보니까, 10월 23일 기준 5개의 과제가 남았는데, 절반은 구형 방식에 대한 Removal 이고, 눈여겨 볼 이슈 2개가 있다면,
전자도 꽤 골치아픈 이슈인데, 제어 컴포넌트의 주요 속성인 value
속성을 동기화 할 것이냐 안할 것이냐다.
만약 한다면, form.reset()
같이 DOM 정식 API에 대한 대응이 유리해지는 등의 일반 DOM 접근성 향상이고,
만약 뺀다면, 제어 컴포넌트의 DOM 동기화로 인해 발생된 여러 react-dom
버그를 해결할 수 있다는 의견이다.
현재 상태는 전자에 해당한다. 현재 공식 팀에서 6년동안 결정한 사항이 없는 거 보니 올해 안에 출시될 지는... 장담 못할 것 같다.
두번째 이슈도 머리아프게 만드는데, 현재 JSX는 HTML 중심으로 대응되어 있으면서도 XML 구조를 강제한다. 하지만 XML 문법을 100% 활용할 수 있는 것도 아니고... 그리고 SSR 상에서는 XML 규칙을 충실히 따르고 있는 터라 아무리 느슨한 HTML이라 해도 <div/>
이지랄 했을 때 브라우저 렌더링 결과도 장담할 수 없으니...
하지만, 다행인 점은 이것들은 Semantic 이슈라 결정되지 않아도 쓰는데 큰 지장이 없는 이슈다. 이 둘의 공통점은,
기존에 이러이러 했는데, 개선할까 말까?
이 한마디로 정리하는 이슈이기 때문이다. 그리고 나머지 이슈도 레거시 제거 관련 이슈이기도 하고...
그래서 지금 React 19 RC는 사용하는데 큰 무리는 없다는 의견이 중론이다.
물론 안정화된 버전은 아니고, 게다가 최초의 메이저 버전으로 출시한다 한들 버그가 없다는 보장은 없으니.
게다가 만약 발견하면 이슈를 제기하면 되긴 하다. 근데 18에서 19로 변경하는 과정에서 생긴 이슈가 아니면 잘 안 볼 테고,
그리고 현재 이슈 개수는 685개. 물론 거르고 거르면 몇백개 되겠지만 Next.js 의 경우 이슈가 자그만치 거의 3000개 돌파.
이정도로 산업계에서 리액트와 넥스트의 인기가 실감이 된다고 볼 수 있겠다.
어쨌든, Next.js 가 이렇게 React 19 나오기도 전에 이렇게 출시한거 괜찮을까?
심지어 공식 블로그에서도 Next.js 15 is officially stable and ready for production
라는 문구까지 써가며 자신있게 내비친 의도는?
아 맞다. <Suspense>
이슈 말이지? 그거 Next.js 같은 SSR 위주로 같이 쓸때는 별 탈 없는 이슈야. 문제가 되는 부분은 SPA 기준이기 때문이지.
물론 이렇게 저지른 Vercel 이 미울 수도 있어. 근데 React 버전업에 자기들 자산까지 써가며 힘을 보태주잖아. 동병상련인가? ㅋㅋ
결국 용두사미 결론이 나오고 말았다. 사실 결론은 별거 없다. 쓰고싶으면 써라 다.
나도 한번 뚜껑을 열어볼 예정이고, 뭐가 어찌될 지 모르겠지만, 확실히 기능 향상 측면에서는 이전보단 더 낫거든.
이게 좋고 나쁜 선악의 관계도 아니고, 프론트엔드는 원래. 선택이란 게 가장 중요한 역할을 가진 개발자거든.
쌩 날것으로 써도 되고, React 필요하면 쓰고, Vue 필요하면 쓰고, Svelte 필요하면 쓰는 거다.
그러나 선택과 집중이 요구될 수 없는 직종이 프론트엔드 개발자긴 하지.
왜냐, 프론트엔드 기술은 너무나도 파편화가 되어 있기 때문에.
물론 리액트는 업무 중심에서의 웹 앱 구축 측면에서, 관리적 측면에서 누가 봐도 탁월한 선택이다.
하지만 프론트엔드의 특징은 결국 무거워질 경우 그 무겁다는 표현을 솔직히 드러내는 게 바로 프론트엔드다.
백엔드는 그 백엔드 앱 하나만 보면 되지만, 프론트엔드는 브라우저 눈치보고 클라이언트 눈치보고 그리고 이용하는 유저 눈치도 봐야 하지. ㅋㅋ
여기서 과연 Next.js 의 과감(?)한 결정은 옳은 선택일까?
조만간 이슈에서 보도록 하자.
끗.
안녕하세요 :)
필력이 좋으셔서 재밌게 보고 팔로우하고 갑니다!
참고해서 저도 정리해보겠습니다!
감사합니다 (_ _)