궁금한게 있어. nextjs 라우트 핸들러와 SWR을 결합해서 사용중인데,

import { NextRequest, NextResponse } from “next/server”; import { userGroupRouteService } from ”../../route.service”; import { getAccessToken } from ”@/app/api/v1/auth/token.action”;

export const dynamic = “force-dynamic”;

// GET /api/v1/settings/user-group/[id]/fetch - 특정 그룹 정보 조회 export async function GET(request: NextRequest, { params }: { params: { id: string } }) { try { // 쿠키에서 인증 토큰 가져오기 const authToken = await getAccessToken();

if (!authToken) { return NextResponse.json({ error: “인증이 필요합니다.” }, { status: 401 }); }

const { id: groupId } = params; const group = await userGroupRouteService.getGroupById(groupId, authToken); return NextResponse.json(group, { status: 200 }); } catch (error) { console.error(“그룹 정보 조회 오류:”, error);

if (error instanceof Error) { return NextResponse.json({ error: error.message }, { status: 500 }); }

return NextResponse.json({ error: “그룹 정보 조회에 실패했습니다.” }, { status: 500 }); } }

authToken 을 request.cookies.get(‘auth_token’)?.value 에서 가져오는 경우에는 SWR을 사용해서 데이터 mutate 를 하는게 제대로 안됐는데,

“use server”;

import { cookies } from “next/headers”;

export async function getAccessToken() { const cookieStore = cookies(); const token = cookieStore.get(“auth_token”);

if (!token) { throw new Error(“No access token found”); }

return token.value; }

이 서버액션을 사용해서 쿠키를 꺼내면 mutate를 해도 제대로 데이터 mutate 가 되거든 뭐가 달라서 이런 문제가 생기는건지 궁금해

편집

좋은 질문이네요! 이 차이가 생기는 이유는 Next.js의 캐싱 메커니즘과 서버 컴포넌트의 실행 컨텍스트 때문입니다.

핵심 차이점

1. request.cookies.get() 방식

  • Route Handler가 Request 객체에 의존하여 실행됩니다
  • Next.js가 이를 정적으로 분석 가능한 코드로 인식할 수 있습니다
  • 캐싱이 더 적극적으로 적용될 수 있습니다

2. cookies() 서버 액션 방식

  • Dynamic API를 사용하므로 Next.js가 이 Route Handler를 완전히 동적으로 처리합니다
  • cookies() 함수는 런타임에서만 해석 가능하므로 정적 분석이 불가능합니다
  • 캐싱을 우회하고 항상 최신 데이터를 반환합니다

왜 mutate가 다르게 동작하는가?

javascript

// 문제가 있던 방식 - 캐싱이 개입할 수 있음
const authToken = request.cookies.get('auth_token')?.value;
 
// 잘 동작하는 방식 - 항상 동적으로 실행됨  
const authToken = await getAccessToken();

cookies() 서버 액션을 사용하면:

  • Route Handler가 매번 새로 실행됩니다
  • SWR의 mutate가 호출될 때마다 실제로 서버에서 새로운 데이터를 가져옵니다
  • 캐시된 응답을 반환하지 않습니다

해결책

이미 올바른 방법을 사용하고 계시지만, 다른 옵션들도 있습니다:

javascript

// 방법 1: 현재 사용중인 방식 (권장)
export const dynamic = "force-dynamic";
const authToken = await getAccessToken();
 
// 방법 2: revalidate 명시적 설정
export const revalidate = 0;
const authToken = request.cookies.get('auth_token')?.value;
 
// 방법 3: 캐시 헤더 명시적 설정
return NextResponse.json(group, { 
  status: 200,
  headers: {
    'Cache-Control': 'no-cache, no-store, must-revalidate'
  }
});

결론적으로, cookies() 서버 액션을 사용하는 방식이 더 안정적으로 동작하는 이유는 Next.js가 이를 완전히 동적인 코드로 인식하여 캐싱을 우회하기 때문입니다.

force-dynamic 을 사용해도 라우트 핸들러가 동적으로 실행이 안되는건가보네.

편집

맞습니다! 정확히 그 부분이 핵심입니다.

force-dynamic의 한계

export const dynamic = "force-dynamic"을 설정해도, Next.js의 내부 캐싱 메커니즘은 여전히 작동할 수 있습니다:

javascript

// force-dynamic이 있어도...
export const dynamic = "force-dynamic";
 
export async function GET(request: NextRequest) {
  // 이 방식은 여전히 캐싱될 수 있음
  const authToken = request.cookies.get('auth_token')?.value;
  // ...
}

왜 cookies() 서버 액션이 더 확실한가?

javascript

// 이 방식이 더 확실한 이유
const authToken = await getAccessToken(); // cookies() 사용
  1. Runtime Dynamic APIcookies()는 런타임에서만 해석되는 API입니다
  2. Static Analysis 불가: Next.js가 빌드 타임에 이 코드를 분석할 수 없습니다
  3. 강제 Dynamic 렌더링: 이런 API가 있으면 Next.js가 해당 Route Handler를 무조건 동적으로 처리합니다

다른 확실한 방법들

javascript

// 방법 1: headers() 사용 (cookies()와 같은 효과)
import { headers } from "next/headers";
 
export async function GET() {
  const headersList = headers();
  const cookie = headersList.get('cookie');
  // ...
}
 
// 방법 2: 명시적 cache 비활성화
export async function GET(request: NextRequest) {
  const response = await fetch('...', { cache: 'no-store' });
  // ...
}
 
// 방법 3: revalidate + no-cache 헤더 조합
export const revalidate = 0;
export async function GET() {
  return NextResponse.json(data, {
    headers: { 'Cache-Control': 'no-store' }
  });
}

결국 force-dynamic 설정만으로는 모든 캐싱을 완전히 우회하기 어렵고, Dynamic API(cookies()headers() 등)를 사용하는 것이 가장 확실한 방법입니다.