[250314] Next.js가 왜 이렇게 하라고 하는지 알아?

김경민·2025년 3월 14일

TIL

목록 보기
55/64

오늘은 진행을 계획했던 서버 렌더링 내부 클라이언트 컴포넌트 구현(useSearchParams 사용 시)에 대해서 진행하는 도중 생긴 오류입니다.
해당 오류를 트러블슈팅 하겠습니다.

문제상황

컴포넌트 내부에 있는 input 내부의 값을 useSearchParams를 사용해 URL에 추가하여 다른 컴포넌트와 값을 공유하려 했습니다.

해당 작업은 dev 환경에서 이루어졌고, 실제로 잘 동작하며 검색 디바운스도 추가하며 이것저것을 하였습니다. 그렇게 끝났다 자부하며 최적화나 여러 고려사항을 고민하며 다음 작업을 넘어가 모든 페이지를 완성하였습니다.

그런 후 build를 하니...

.
.
.

이런!!
build에 오류가 생기는겁니다!

해당 오류는 마지막 줄에 preRendered가 대문짝에 달려있는 링크를 던져주며 대놓고 챔피언 페이지를 지목하며 났기에 서버 render에서 오류가 생겼나? fetch가 잘못됐나? 순서가 꼬였나? 클라이언트 컴포넌트가 서버 렌더링을 막고 있나? 등 많은 걱정을 하며 이것저것 주석처리를 하며 오류를 찾아나섰습니다.

오류 발견

클라이언트 컴포넌트를 주석처리하니 갑자기 빌드가 잘 되기 시작하는겁니다.

아! 찾았다! 이녀석이 범인이구나 하고 자세히 살펴보니 로직에는 아무런 문제가 없는겁니다.

어? 문제 없는데 뭐지? 내가 문제를 못 보고 있는건가?

그리하여 튜터님을 찾아갔습니다.

그러더니 바로 말씀하셨습니다.

"터미널에 생긴 오류 맨 위로 올라가볼까요?"

그렇게 오류의 첫 줄을 확인하였습니다.

아.

이젠 오류 첫 줄엔 useSearchParams가 대문짝하게 달려있었습니다.

그리하여 해당 오류는 Next.js에서 useSearchParams를 사용할 때에 필요한 규칙을 지키지 않아 생기는 오류임을 알았습니다.

오류 해결

그러면 왜 useSearchParams를 사용하면 오류가 발생하는걸까요?

먼저 이유를 살펴보겠습니다.

useSearchParams는 Next.js에서 제공하는 훅으로, URL 쿼리 스트링(query string)을 읽고 상태로 사용할 때 사용합니다.

클라이언트 컴포넌트에서만 사용할 수 있고 해당 컴포넌트는 "use client"가 선언되어 있겠지요.

클라이언트 컴포넌트는 서버 컴포넌트에서 읽혀지는 과정이 비동기적이기 때문에 서버 렌더링 페이지가 전달 될 때엔 URL, 즉 쿼리 스트링이 없기 때문에 아직 컴포넌트는 준비되지 않은 상태일 수 있습니다.

따라서 쿼리 스트링이 준비될 때 까지 해당 컴포넌트를 지연시켜줘야 합니다.

그래서 그 지연을 Suspense라는 기능을 통해 해야하고 서버 렌더링이 성공적으로 전달되어 쿼리 스트링이 준비되면 렌더링을 재시도하여 성공적인 렌더링이 이루어집니다.

그렇다면 해결 방법은 어떻게 되는걸까요?

크게 두 가지가 있습니다.

1. Suspense

해당 컴포넌트를 Suspense로 감싸 비동기 상태 일 때 로딩 상태로 처리하는 방법입니다.

  • 장점
    • 특정 상태마다 다른 로딩 UI 제공이 가능합니다.
    • 불필요한 렌더링을 방지합니다.
    • 복잡한 상태 관리에 유리합니다.
  • 단점
    • 특정 상태마다 Suspense를 계속 감싸야 해서 코드가 길어질 수 있습니다.
    • 여러 컴포넌트를 관리 해야 하면 복잡해질 수 있습니다.
    • 상위에서 전역 로딩 상태를 처리하기 어렵습니다.

2. loading.tsx

특정 경로에 loading.tsx를 추가해주어 해당 경로에 비동기 상태가 발생할 때에 전역적으로 로딩 상태를 처리하는 방법입니다.

  • 장점
    • 모든 하위 컴포넌트에서 일관된 로딩 상태 제공합니다.
    • 폴더 구조만 잘 잡으면 간단하게 처리가 가능합니다.
    • 코드를 깔끔하게 유지할 수 있습니다.
  • 단점
    • 내부의 모든 컴포넌트가 작업이 끝나기 전까지 로딩 상태를 유지합니다.
    • loading.tsx가 전체 레이아웃에 영향을 줄 수 있습니다.
    • 복잡한 로직이 필요한 경우 비효율적입니다.

Suspense를 사용하여 클라이언트를 지연하는 방법은 공식문서에 있어서 알겠는데...
loading.tsx는 어떠한 원리로 해당 컴포넌트를 지연시키는건가요?

위 이미지는 Next.js 공식 문서에서 loading.tsx의 내부 로직입니다.

해당 경로 아래 있는 컴포넌트를 Suspense로 감싸는 부분에서 전역적으로 지연을 시키고 있다고 보면 되겠습니다.

.
.

그리하여! 모든 정보를 총합하여 지금 나에겐 어떤 방법으로 해결하면 좋을까? 고민하다가 추후 스켈레톤 UI를 제작하기 위해 우선 loading.tsx을 활용하여 오류를 수정하기로 하였습니다!

마치며

Next.js는 프레임워크인 만큼 기본적으로 제공하는 훅을 사용할 때엔 어떤 용도로 사용하는지 뿐만 아닌 어떤 룰을 기반으로 사용하는지 까지 확인하여야 하겠습니다. 이번 트러블슈팅은 거의 하루를 깊게 들여다본 내용인데 생각보다 Next.js는 친절하고 프레임워크로써 룰만 지켜준다면 많은 기능을 편리하게 제공해준다라고 생각을 하게 되었습니다.

profile
김경민입니다.

0개의 댓글