Next.js app router 써야되나요?

Sming·5일 전
8

매우매우 주관적인 글입니다. 저는 정답이 아닙니다. 제가 느낀 거를 작성한 일기라 생각하고 봐주세용

안녕하세요. 저는 next 13.0.0에서부터 production 환경에서 app router를 운영했던, app router를 가장 오랜 기간 사용한 사람 중 한 명입니다. 소박한 Next.js Contributor이기도 하죠.

현재의 app router에서 사용하는 메모리보다 2배 이상 소모될 때부터, 서버 컴포넌트에서 컴포넌트별 ISR이 되지 않았을 때부터 사용하고 있었죠. 물론 현재는 이와 같은 문제가 많이 해결된 상태입니다. 그래서 13.0.0이 나온 지 어느덧 2년이 넘었고, 이제는 사이드 프로젝트에서도, 기업에서도 app router를 많이 채택하고 있습니다.

그렇다면 이 app router, 이제는 쓰면 될까요?

뭐 제가 쓰란다고 쓰고, 제가 쓰지 말라고 안 쓰겠습니까.. 그냥 개인적으로 제가 느끼는 것을 얘기해보려고 합니다. 저는 app router를 사용하는 데에는 많은 트레이드오프를 고려해야 된다고 생각합니다.

먼저 app router를 사용했을 때 얻게 되는 이점은 어떤 게 있을까요? 가장 큰 장점은 컴포넌트별 렌더링 전략을 선택할 수 있다는 것이죠. 기존에는 page 단위로 통째로 SSR, ISR, SSG를 선택해야 했던 것을 컴포넌트별로 선택할 수 있다는 점이 매우 인상적입니다.

또 Parallel Routes, Intercepting Routes, Server Action, fetch 병렬 처리 같은 app router에서만 되는 특수 기능들도 제공하고 있습니다.

그리고 서버 컴포넌트를 이용했을 때 번들 사이즈가 전혀 없다라는 점도 있겠군요.


그렇다면 app router를 썼을 때 단점은 뭐가 있을까요? 사실 저는 이것을 말하려고 이 글을 작성하고 있습니다 :)

먼저 위에 작성한 저 이점들을 크게 이용하고 있지 않습니다.

한 페이지에 ISR, SSR, SSG를 선택적으로 여러 개 이용하는 경우는 크게 없기 때문이죠.

Parallel Routes, Intercepting Routes도 유사합니다. 사용할 일도 그렇게 크게 많지 않으며, 충분히 이러한 UI 형태를 구현하기 위해 app router의 이 기능들을 사용하지 않아도 됩니다. Server action은 Next.js 하나로만 프론트, 백엔드를 모두 처리하는 상황이 아니면 크게 사용할 일은 없다고 생각됩니다.

또한 서버 컴포넌트에 우리가 실제로 넣을 수 있는 DOM은 그렇게 많지 않으며, 이로 얻을 수 있는 zero-bundle의 이점은 거의 없다고 생각됩니다.

또한 단점은 계속 쏟아집니다.

app router의 사용을 위해 아예 스타일 라이브러리를 변경하는 경우도 있습니다.

기존에 scss, module css 같이 사용을 해왔다면 page router → app router의 변경에도 그렇게 critical하지는 않을 것입니다.

하지만 styled-components, emotion과 같은 runtime css-in-js 들을 사용했을 때는 이를 서버 컴포넌트에서 사용할 수 없습니다. 그렇기에 서버 컴포넌트는 오직 데이터를 fetch하고 이를 client component에 넘겨주는 것밖에 할 수 없게 되죠.

그렇다고 이 app router 사용을 위해 기존에 사용하던 스타일 라이브러리를 scss, module css 혹은 tailwind css, panda css, vanilla-extract로 바꾸는 건 옳을까요? 물론 app router를 그 정도로 이용해야 될 이유가 있다면 괜찮다고 생각하지만, 기존에 쓰던 스타일 라이브러리를 모두 마이그레이션하면서까지 app router의 서버 컴포넌트에 스타일을 입혀야 할까요??

또한 공통 라이브러리에서 app router를 한 번 더 고려해야 된다는 것도 생각보다 꽤 큰 문제입니다.

대부분의 회사가 app router를 쓰더라도 page router + app router라고 생각됩니다. 모든 페이지가 app router로 마이그레이션된 곳은 크게 없다고 생각합니다. 이렇게 page router + app router로 되는 순간부터 공통적으로 사용하고 있는 라이브러리에서는 고려해야 될 부분이 2배가 됩니다.

일단 page router, app router 모두 Next.js라는 하나의 프레임워크에 의존성을 가지고 있지만, 단지 어떤 router를 사용하느냐에 따라 내부에서 참조하는 메서드들은 달라지기 때문입니다.

예를 들어 page router에서는 next/router를 이용해 router 처리를 하는데, app router에서는 next/navigation을 이용해 router 처리를 하죠. 그리고 next/navigation의 router에서는 router.on과 같은 router에 이벤트를 걸 수 없어 page router에서 처리하던 방식을 그대로 사용할 수 없죠.

또한 page router에서는 req를 통해 안에서 header 정보들을 가져와 처리하는데, app router에서는 headers(), cookies()라는 메서드를 제공하여 처리를 하죠.

그리고 compound component pattern과 같이 <Select.Option>처럼 사용하는 dot notation이 app router에서는 오류를 내뱉는 문제도 존재합니다. 이러한 이슈 때문에 이미 다른 radix ui와 같은 UI 라이브러리에서는 실제로 app router 대응을 위해서 dot notation을 떼내는 처리도 진행하였습니다. 그렇기에 사내 공통 라이브러리에 compound component pattern으로 dot notation을 사용했다면 제거해야 되는 문제도 존재합니다.

이는 생각보다 공통 라이브러리를 개발할 때 많이 피로를 가져오게 됩니다. Next.js에 의존이 없는 게 최고의 라이브러리겠지만, 어쩔 수 없이 의존이 생기는 경우에는 app router, page router 모두 고려하여 2벌을 만들어야 되기 때문이죠.

무엇보다 그냥 개발이 피로합니다.

프레임워크를 사용한다. 이는 정말 개발의 용이성을 위해서 사용한다고 생각합니다. 실제로 Next.js 같은 경우 내부적으로 제공하고 있는 최적화 옵션이나 next.config.js로 모든 걸 제어하는 편의성은 개발을 많이 편하게 하죠.

하지만 app router는 기본적으로 server component로 사용되며, client component 이용을 위해서는 use client를 붙여야 하는 번거로움이 존재하며, app router를 사용하는 가장 큰 이유인 server component를 사용한 순간부터 개발의 난이도가 확 올라가게 됩니다.

먼저 최상위 page.tsx, layout.tsx는 반드시 서버 컴포넌트로 유지되어 있어야 할 것, 서버 컴포넌트를 이용했다면 클라이언트 컴포넌트에서는 서버 컴포넌트를 import할 수 없기에 반드시 children으로 넣어야 할 것, 이로 인해 하나의 컴포넌트를 만들 때 정말 데이터를 뿌려주는 server component wrapper가 존재하고, 클라이언트 컴포넌트는 그 데이터를 props에서 받아서 그리기만을 진행하죠. 즉, 컴포넌트 하나를 구현하기 위해 쓸데없이 2개의 다른 컴포넌트가 생겨나게 됩니다.

이로 인해서 처음 app router를 개발하는 사람 입장에서는 실수를 많이 진행하고는 합니다.
예를 들어 page.tsx, layout.tsx를 실수로 클라이언트 컴포넌트로 두고 시작하곤 합니다. 이런 경우에는 최상위 컴포넌트가 클라이언트 컴포넌트가 됐기에 절대로 이 route에서는 서버 컴포넌트를 이용할 수 없습니다. (클라이언트 컴포넌트에서는 서버 컴포넌트 import 불가)

그리고 서버 컴포넌트는 클라이언트 컴포넌트에서 반드시 children으로 사용해야 된다는 룰이 있는데, 이는 사실 안 지킨다고 해서 런타임에서 아무런 에러를 내지 않습니다. 그냥 아무도 모르게 동작이 이상하게 됩니다. 서버 컴포넌트인 줄 알고 썼는데 클라이언트 컴포넌트처럼 동작하고는 하죠. Next.js는 이를 에러로 잡아주지 않습니다.


자 여기서 의견을 정리해보겠습니다.

보통 회사에서 app router를 사용하게 되면 page router와 함께 공존하는 경우가 대부분입니다.

  1. 그렇다면 공통 라이브러리 만드는 개발자 A는 Next.js 의존성이 있는 라이브러리를 만들 때마다 app router, page router 두 벌을 만들어야 될 것입니다.
  2. app router를 주로 개발하던 B, page router를 주로 개발하던 C가 만약 팀이 바뀌어 각각 다른 router를 건드리게 된다면 각각의 app router, page router에서 지키고 있던 규칙들, 코딩 방식들을 다시 익혀야 할 것입니다. (같은 Next.js임에도 불구하고)
  3. app router를 위해 스타일 라이브러리를 변경하려고 합니다. 변경을 하면 꽤나 비용은 들겠지만 서버 컴포넌트에서도 스타일을 사용할 수 있게 됩니다. 하지만 useState, useEffect도 사용하지 못하고, event handler도 사용하지 못하고, window 객체에도 접근할 수 없는 초-정적인 서버 컴포넌트에 스타일을 입혀서 써봤자 얼마나 쓸 수 있을까요? 이를 위해 스타일 라이브러리를 변경하는 게 맞을까요?
  4. Parallel Routes, Intercepting Routes, Server action 실제로 많이 사용하나요..? 이것 사용을 위해 app router를 사용하는 게 맞을까요? 게다가 어마어마하게 구린 폴더 구조들을 보게 될 겁니다.. route group, intercepting routes, private folder, parallel routes가 섞인 순간부터 이게 장난치는 것과 같아 보이는 기괴한 폴더 구조의 연속들일 겁니다.

새로운 기능 추가 시 *folder라도 생길지 모름...

아무튼 전 다음과 같은 이유로 app router 사용을 하고 싶지 않습니다. 제가 아직까지 느꼈던 경험으로는 app router를 쓸 이유가 쓰고 싶지 않은 이유보다 크지 않아 보이기 때문입니다.


진짜 문제는 Next.js 그 자체

하지만 app router를 안 쓴다? 그거는 어려울지도 모릅니다.

Next.js 팀은 이제 page router는 단순한 유지보수, 그리고 앞으로 새로운 기능 추가, 개선 같은 것들은 app router 중심적으로 될 예정이기 때문이죠. 하지만 라이브러리가 더 이상 개선되지 않는다… 그건 죽은 라이브러리와 같죠. 결국 Next.js는 점진적인 app router 전환으로 모두를 이끌 것입니다. 아예 React 팀과 협업을 하여 server component를 이끌어가기 때문에 이 app router를 영원히 배척하겠다는 것도 참 어려워 보입니다.

마치 yarn v1, 그리고 yarn berry를 보는 것 같습니다. 대부분의 사람들이 아직 많이 이용하지만 업데이트는 유기된 yarn v1, 그리고 앞으로 모든 업데이트는 zero-install을 내세운 pnp 전략을 지닌 yarn berry가 받게 되죠… 이와 다를 게 없어 보입니다.

개발자들은 yarn berry의 pnp 기능의 사용하기 어려움과 .yarn/cache의 git 저장소에서의 관리, 가끔 충돌하는 의존성 문제로 인해 꺼려하곤 합니다. 그렇기에 그냥 yarn v1도 yarn berry도 아닌 pnpm을 많이 이용하곤 합니다.

이와 같이 Next.js도 사용하기에는 불편하게 하면서 기존 page router 대신 app router를 계속 발전시킨다면 pnpm 마냥 Next.js 대신에 다른 것을 이용하는 게 좋을지도 모릅니다.


Next.js app router 써야 되나요?

앞에서 얘기했듯 사실 app router에서 더 나아가 Next.js를 써야 할까… 라는 의문이 들곤 합니다.

  • 리액트에 이미 의존돼 있는 우리의 프로젝트를 Next.js에 한 번 더 의존하여 나아가는 것이 좋은 방향일까요?
  • Next.js의 app router는 계속 어떤 방향으로 발전할까요?
  • 5년 뒤에 리액트는 쓸 수 있겠지만, 5년 뒤에 Next.js도 동일하게 사용하고 있을까요?
  • 정말 만약에 5년 뒤에 React에서 새로운 라이브러리로 메타가 바뀌면 어떻게 될까요? React만 쓰는 프로젝트는 React를 다른 라이브러리로 마이그레이션하면 될 것입니다. 하지만 Next.js까지 여기 엮여 있는 순간부터는 Next.js 걷어내기 작업까지 2배, 그 이상으로 힘든 마이그레이션이 되지 않을까요?

뭐 그거 외에도 단점이라면 얼마든지 있습니다..

  • Next.js의 내부 블랙박스. 우리가 모르는 너무 많은 게 Next.js 안에 숨어 있다. 사실 React만으로도 돌아가는데 지장은 없으나 너무 많은 것을 제공하고 있다. 최근에도 발생한 Next.js 보안 이슈도 동일하다.
  • 서버를 사용한다. 이 하나만으로 프론트에서 신경 써야 할 것이 매우 많아진다. 백엔드 서버 운영함과 유사하게 프론트엔드의 서버도 지속적인 모니터링이 필요하고, 이에 대응하는 인프라 설정 역시 요구된다. 물론 서버에 대한 추가적인 비용은 당연히 보너스
  • next/image와 같이 성능 최적화하는 것을 제공하면서 그 최적화를 SSR 서버와 동일한 서버가 한다는 거를 잊어서는 안 된다. 최적화를 하는 거는 공짜가 아니다. 그것도 Next의 서버가 하는 것이다. SSR과 이미지 최적화가 하나의 서버에서 같이 처리되고 있다면 메모리 이슈는 물론, 사용자가 많아질 경우 둘 중 하나는 하자가 생길 수 있다.
  • React 빌드에 비해 빌드 속도가 너무 느리다… 로컬 환경도 아무리 개선을 했다 하더라도 느리다… (영겁의 HMR)
  • 버전 업데이트될수록 복잡성만 올라감

결론

뭐 이렇게는 작성했지만 SEO가 정말 정말로 중요한 프로젝트 같은 경우에는 Next.js를 사용할 듯합니다. SEO가 별로 필요 없다면 무조건 React를 사용할 듯하고요. 물론 여유가 있어, 회사에서 내부 SSR 서버를 직접 node로 구축할 수 있다면 Next.js를 버리고 React + SSR 서버 스택으로 갈 듯합니다.

여러분들도 일단 Next.js를 사용하기보다는, 굳이 SEO가 필요하지 않아도 된다면 React를 사용해보면 어떨까요?

profile
딩구르르

4개의 댓글

comment-user-thumbnail
4일 전

한번 더 생각하게 되네요 ..

답글 달기
comment-user-thumbnail
3일 전

"React만 쓰는 프로젝트는 React를 다른 라이브러리로 마이그레이션하면 될 것입니다. 하지만 Next.js까지 여기 엮여 있는 순간부터는 Next.js 걷어내기 작업까지 2배, 그 이상"

을 몇 주 전 까지 한 사람..

답글 달기
comment-user-thumbnail
3일 전

좋은 글 감사합니다 스밍 :)

1개의 답글

관련 채용 정보