[Next] v15 useSearchParams without Suspense 오류 해결
🚩 소개
안녕하세요! 대학생 개발자 주이어입니다!
오늘은 Next.js v15에서 발생할 수 있는 오류 중 하나인 useSearchParams without Suspense에 대해서 알아보려고 합니다.
저도 프로젝트를 진행하던 도중에 이 오류가 발생하게 되었는데, 미리 이 오류에 대해서 알고 있지 않으면 나중에 많은 수정을 거쳐야 할 수 있기 때문에 이렇게 공유를 해보려고 합니다.
❓ 발생하는 이유는?
이 오류가 발생하는 이유는 오류 이름에서 알 수 있듯이 useSearchParams를 Suspense 없이 사용했을 때 발생하는 오류 입니다.
next를 사용해보신 분이라면 아시겠지만, Suspense는 기본적으로 컴포넌트나 함수의 동적 처리를 기다리고, 기다리는 동안 fallback 함수 등을 사용하여 로딩을 처리해주는 역할을 합니다.
근데 왜 useSearchParams와 같이 사용해야 할까요?
http://localhost:3000?day=1
useSearchParams는 검색 파라미터를 읽어오는 역할을 하는 함수입니다.
위에 링크에서 day=1과 같은 값(쿼리 스트링)을 가져온다고 이해하시면 됩니다.
근데 이러한 쿼리 스트링은 언제 생길까요?
fetch("http://localhost:5000/plan/create", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ user_message: inputInfo }),
})
.then((res) => res.json())
.then((data) => {
const parseResult = JSON.parse(data);
console.log(parseResult);
router.push("?createSchedule=true&day=1");
setResult(parseResult);
});
위 처럼 대부분의 쿼리 스트링은 페이지가 이미 렌더링 된 후, 사용자가 상호작용을 하면서 생기게 됩니다.
즉, 순서를 정리해보면,
빌드 -> 페이지 렌더(정적 페이지일 경우, 빌드 타임때 처리) -> 사용자 상호작용 -> 쿼리스트링 생성
이와 같은 순서로 작동할 것 입니다.
근데 next는 기본적으로 페이지를 정적으로 처리하려고 합니다. 왜냐하면 정적 페이지가 많을 수록 실시간 렌더링 되는 페이지가 적어지니 메모리 효율이나 사용자 경험 면에서 훨씬 좋은 결과를 내기 때문입니다.
하지만 순서 부분에서 언급했듯이 정적 페이지일 경우, 빌드 타임 때 페이지를 처리하게 됩니다.
근데 빌드 타임 때 쿼리 스트링 값을 알 수 있나요?
위에서 말했 듯이 쿼리스트링은 사용자와 상호작용을 하면서 생성되는데, 빌드 타임 때, 즉 페이지를 처음 생성할 때는 쿼리 스트링 값이 무엇인지 알 수 없게 됩니다. 그럼 다이나믹으로 처리를 해야겠죠. 하지만 useSearchParams를 사용하는 부분 때문에 전체 페이지를 다이나믹으로 처리하는건 비효율적입니다. 따라서 next에서는 해당 부분만 Suspense로 감싸달라고 요구하는 것 입니다.
✅ v14 해결방법
module.exports = {
experimental: {
missingSuspenseWithCSRBailout: false,
},
}
v14에서는 Suspense를 사용하여 해결하는 방법도 있지만,
위처럼 next.config.js 파일을 수정하여 오류가 안뜨게 하는 방법도 있습니다.
하지만 이 방법은 next 공식 문서에서 권장하는 방법은 아닙니다.
또한 v15에서는 아래에서 설명하는 Suspense를 활용하는 방법으로만 해결할 수 있습니다.
✅ v15 해결방법
'use client'
import { useSearchParams } from 'next/navigation'
import { Suspense } from 'react'
function Search() {
const searchParams = useSearchParams()
return <input placeholder="Search..." />
}
export function Searchbar() {
return (
// You could have a loading skeleton as the `fallback` too
<Suspense>
<Search />
</Suspense>
)
}
v15에서는 위 코드처럼 useSearchParams를 사용하는 부분을 컴포넌트로 분리하고, Suspense로 감싸는 방법으로만 해결할 수 있습니다.
(가끔 "use client"를 쓰면 다이나믹 페이지로 처리되는 걸로 알고 계신 분들이 있는데, "use client"는 useEffect, useState와 같은 클라이언트 훅을 사용할 수 있도록 해주는 기능이고, 다이나믹 페이지 처리와는 무관 합니다.)
import ClientPage from "./ClientPage";
import { Suspense } from "react";
export default function Page() {
return (
// useSearchParams-without-suspense
<Suspense fallback={<div>loading...</div>}>
<ClientPage />
</Suspense>
);
}
위 코드는 제 프로젝트 메인 페이지 코드입니다.
개발할 때는 위와 같은 오류를 모르고 useSearchParams를 그냥 사용했다가
나중에 빌드 테스트 때 오류가 생겨 그 때 컴포넌트로 분리하는 작업을 해줬습니다.
다양한 컴포넌트와 연결되는 복잡한 페이지 였기 때문에 막막했는데, 막상 분리하는건 그렇게 어렵지 않았습니다.
이후에는 fallback에 들어갈 skeleton UI도 만들어줄 계획입니다.
😊 마무리
이렇게 지금까지 useSearchParams without Suspense 오류에 대해서 발생 이유와 버전별 해결 방법 까지 정리를 해보았습니다.
이번 글이 Next 카테고리에 올리는 첫 번째 글인데 많은 분들이 제글을 보고 도움이 되셨으면 좋겠습니다.
소개 부분에서 얘기했지만 이 오류에 대해서 모르고 개발하다가 빌드 타임 때 고생하는 경우가 생길 수 있으니 한 번쯤 읽고 기억해두시는게 좋을 것 같습니다.
지금까지 읽어주셔서 감사드리고, 다음에는 더 좋은 Next 글로 찾아오도록 하겠습니다!
💬 함께 공부하고 싶은 내용이 있다면 댓글이나 피드백으로 알려주세요!
[Node] 랜덤 공유 링크는 어떻게 생성할까? (nanoid)
[Node] 랜덤 공유 링크는 어떻게 생성할까? (nanoid)
[Express] 레이어드 아키텍쳐 구현하기 (Layered Architecture) [Express] 레이어드 아키텍쳐 구현하기 (Layered Architecture)🚩 소개안녕하세요! 대학생 개발자 주이어입니다!오늘은 레이어드 아키텍쳐 구조화
blog.juyear.dev
추천 글 읽으러 가기!
KYT CODING COMMUNITY Discord 서버에 가입하세요!
Discord에서 KYT CODING COMMUNITY 커뮤니티를 확인하세요. 23명과 어울리며 무료 음성 및 텍스트 채팅을 즐기세요.
discord.com
KYT CODING COMMUNITY 가입하기!