넥스트JS 15에서 URL 노출 없이 페이지 간 데이터 전달하기

김현준·2025년 5월 16일
0

넥스트JS 이모저모

목록 보기
9/23

유저 정보 수정 페이지를 만드는데 문제가 생겼다.
리액트에선 useNavigateuseLocation을 이용해 url에 id를 노출시키지 않고 페이지 간 데이터 전달이 가능했다. 그렇다면 넥스트js에선 어떤 방법들을 사용해야 할까?

1. 전역 상태 관리로 전달

// 예: Zustand store
setUserForEdit({
  id: user.id,
  name: user.name,
});
router.push('/profile/edit');
// /profile/edit/page.tsx
const user = useUserForEdit();
  • 장점: URL에 정보 노출 X, 안전함
  • 단점: 새로고침하면 상태 날아감 (필요시 localStorage fallback 추가)

2. Session 기반 저장 (서버 액션/쿠키)

Next.js 15는 서버 액션 + middleware/cookie 조합으로 보안 강화된 전달이 가능

// 유저 정보 수정 시작 전 서버 액션에서 쿠키 저장
// app/actions/user.ts
'use server';

import { cookies } from 'next/headers';

export async function prepareEditUser(userId: string) {
  cookies().set('edit_user_id', userId, { httpOnly: true });
}
// 클라이언트에서 호출
await prepareEditUser(user.id);
router.push('/profile/edit');
// /profile/edit/page.tsx
import { cookies } from 'next/headers';

export default async function Page() {
  const userId = cookies().get('edit_user_id')?.value;
  const user = await getUser(userId); // DB에서 불러오기
}
  • 장점: 새로고침에도 유지, URL에 노출되지 않음, 보안 강도 높음

  • 단점: 서버 액션과 쿠키 관리가 필요함

3. localStorage 또는 sessionStorage

localStorage.setItem('editUserId', user.id);
router.push('/profile/edit');
// /profile/edit/page.tsx → 클라이언트 컴포넌트에서
useEffect(() => {
  const id = localStorage.getItem('editUserId');
  // API 요청 등
}, []);
  • 장점: 간편

  • 단점: 클라이언트 전용이라 SSR 안 됨, 보안에 상대적으로 취약

최종 선택

유저 정보를 다루는 만큼 보안이 중요하다 생각햐여 2번 방법 선택

목표

  1. 유저 정보 수정 버튼 클릭 시 서버 액션으로 쿠키에 userId 저장
  2. /profile/edit 페이지에서 해당 쿠키 값을 읽어 DB에서 유저 정보 가져오기
  3. 수정 완료 후 쿠키 제거

1. 서버 액션: setEditUserId.ts

'use server';

import { cookies } from 'next/headers';

export async function setEditUserId(userId: string) {
  cookies().set('edit_user_id', userId, {
    httpOnly: true,
    path: '/profile/edit', // 해당 경로에서만 쿠키 유효
    maxAge: 60 * 5, // 5분
    sameSite: 'lax',
    secure: process.env.NODE_ENV === 'production',
  });
}

2. 버튼 클릭 시 호출

클라이언트 컴포넌트 (예: EditButton.tsx)

'use client';

import { useRouter } from 'next/navigation';
import { setEditUserId } from '@/app/actions/setEditUserId'; // 경로 맞게 수정

export default function EditButton({ userId }: { userId: string }) {
  const router = useRouter();

  const handleEdit = async () => {
    await setEditUserId(userId); // 서버 액션 호출
    router.push('/profile/edit');
  };

  return <button onClick={handleEdit}>프로필 수정</button>;
}

3. /profile/edit/page.tsx에서 쿠키 읽기 + DB 요청

// app/profile/edit/page.tsx
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { db } from '@/lib/db'; // Prisma Client
import { notFound } from 'next/navigation';

export default async function ProfileEditPage() {
  const userId = cookies().get('edit_user_id')?.value;

  if (!userId) {
    redirect('/'); // 접근 제한
  }

  const user = await db.user.findUnique({
    where: { id: userId },
  });

  if (!user) {
    notFound();
  }

  return (
    <div>
      <h1>프로필 수정</h1>
      <p>닉네임: {user.name}</p>
      {/* 수정 폼 등 */}
    </div>
  );
}

4. 수정 완료 후 쿠키 제거 (선택)

서버 액션에서 제거 가능

'use server';
import { cookies } from 'next/headers';

export async function clearEditUserId() {
  cookies().delete('edit_user_id');
}

수정 완료 시 호출

await clearEditUserId();
router.push('/profile');

주의점

  • httpOnly: true 설정으로 JS에서 직접 접근 불가
  • secure: true는 HTTPS 환경에서만 쿠키 사용 (운영 시 필수)
  • path: /profile/edit을 지정하면 그 경로에만 쿠키 노출됨
profile
기록하자

0개의 댓글