Next.js API 라우트 - dynamic params 가져오기
혼자 사이드 프로젝트를 개발하던 중 Next.js의 API 라우트 기능을 사용할 일이 생겼고
dynamic params가 필요한 상황이었다. 분명 예전에도 써본 기능인데 자꾸 params를 가져오지 못하는 문제 발생
문제 상황 흐름
import { getUserHistory } from "@/data/getUserHistory";
import { formatTime } from "@/lib/time";
export default async function UserHistory({ userId }: { userId: string }) {
const history = await getUserHistory(userId);
return (
// 생략
우선 해당 서버 컴포넌트에서 로그인한 유저의 아이디를 props로 받아 유저 아이디에 따른 history
라는 데이터를 불러올 예정이었다.
// @/data/getUserHistory.ts
"use server";
export async function getUserHistory(userId: string) {
const res = await fetch(`${process.env.BASE_URL}/api/history/${userId}`, {
next: {
tags: ["history"],
},
credentials: "include",
});
const history = await res.json();
return history;
}
단순히 미리 파둔 API 라우트에 fetch 요청을 하는 서버 함수였고 기능이 잘 동작하리라 생각했었다.
문제 코드
// @/app/api/history/[userId]/route.ts
import { db } from "@/db";
import { historyTable } from "@/db/schema";
import { desc, eq } from "drizzle-orm";
import { NextResponse } from "next/server";
export async function GET({ params }: { params: { userId: string } }) {
const { userId } = params;
const history = await db
.select()
.from(historyTable)
.where(eq(historyTable.userId, parseInt(userId)))
.orderBy(desc(historyTable.start));
return NextResponse.json(history);
}
이제 Next.js의 API 라우트 기능이 동작할 차례이다.
dynamic params 처리로 받아온 userId
를 바탕으로 db에서 해당 데이터를 찾아 리턴하려했으나 데이터를 정상적으로 받아오지 못했다.
처음에는 db 연동이 제대로 안되었나 생각했지만 userId
에 콘솔을 찍어보니 값이 들어오지 않았다.
해결 방법
결론적으로 Next.js App Router 등의 route handler 함수 시그니처는 고정되어있는 형태이다.
export async function GET(
request: NextRequest,
context: {
params: { userId: string };
}
); // ...
내부에서 자동으로 매핑되기때문에 req
가 생략되어 params를 제대로 찾지 못한것이었다.
바보.....
import { db } from "@/db";
import { historyTable } from "@/db/schema";
import { desc, eq } from "drizzle-orm";
import { NextRequest, NextResponse } from "next/server";
export async function GET(
_: NextRequest,
{ params }: { params: { userId: string } }
) {
const { userId } = params;
console.log("userId", userId);
const history = await db
.select()
.from(historyTable)
.where(eq(historyTable.userId, parseInt(userId)))
.orderBy(desc(historyTable.start));
console.log("history", history);
return NextResponse.json(history);
db에서 데이터를 잘 끄집어내왔다
추가로 콘솔에 Error: Route "/api/history/[userId]" used params.userId. params should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
와 같은 warning이 뜬다.
아마 Next.js 15 버전 이상부터 페이지 컴포넌트에 params를 받아올때 비동기식으로 접근하게 변경되었는데 동일한 이유의 경고같다.
https://nextjs.org/docs/messages/sync-dynamic-apis
import { db } from "@/db";
import { historyTable } from "@/db/schema";
import { desc, eq } from "drizzle-orm";
import { NextRequest, NextResponse } from "next/server";
export async function GET(
_: NextRequest,
{ params }: { params: Promise<{ userId: string }> }
) {
const { userId } = await params;
const history = await db
.select()
.from(historyTable)
.where(eq(historyTable.userId, parseInt(userId)))
.orderBy(desc(historyTable.start));
return NextResponse.json(history);
}
이렇게 코드를 고치니 콘솔에 warning을 제거할수 있었다.