Skip to content

httpOnly Cookie, Next.js API route, React query

Won Kim edited this page Nov 4, 2024 · 2 revisions

httpOnly 쿠키를 통해 클라이언트에서 직접 토큰에 접근하지 않고, Next.js API 라우트에서 백엔드 API로 요청을 보내는 방식으로 구현합니다. 이를 통해 클라이언트에서 인증 토큰이 노출되지 않도록 보안을 강화할 수 있습니다.

1. httpOnly 쿠키에 저장된 accessToken과 Next.js API 라우트의 역할

  • httpOnly 쿠키: accessTokenhttpOnly 쿠키에 저장하면, 쿠키는 클라이언트 JavaScript에서 직접 접근할 수 없습니다. 이 방식은 XSS 공격 방지에 효과적이며, 클라이언트와 서버 간 안전하게 토큰을 전달할 수 있습니다.
  • Next.js API 라우트: 클라이언트는 Next.js API 라우트에 요청을 보내면, 서버는 쿠키에 저장된 accessToken을 사용하여 백엔드 API에 인증된 요청을 보냅니다. Next.js API 라우트는 프록시 역할을 하여 클라이언트가 직접 토큰에 접근할 필요가 없도록 합니다.

2. Next.js API 라우트에서 httpOnly 쿠키를 통해 백엔드 API 요청하기

다음과 같이 axios를 사용하여 httpOnly 쿠키에 저장된 accessToken을 포함해 백엔드 API에 요청합니다.

API 라우트 예제 (app/api/data/route.ts)

// app/api/data/route.ts
import { NextRequest, NextResponse } from 'next/server';
import axios from 'axios';

const BACKEND_API_URL = 'https://your-backend-api.com/data';

export async function GET(req: NextRequest) {
  try {
    // 쿠키에서 accessToken을 가져와 Authorization 헤더로 추가
    const accessToken = req.cookies.get('accessToken')?.value;

    if (!accessToken) {
      return NextResponse.json({ error: '인증 토큰이 없습니다.' }, { status: 401 });
    }

    // 백엔드 API 호출
    const response = await axios.get(BACKEND_API_URL, {
      headers: {
        Authorization: `Bearer ${accessToken}`, // 백엔드 API에 토큰 포함
      },
    });

    // 백엔드 API로부터 받은 데이터를 클라이언트로 반환
    return NextResponse.json(response.data);
  } catch (error) {
    console.error('백엔드 API 호출 오류:', error);
    return NextResponse.json({ error: '백엔드 API 호출에 실패했습니다.' }, { status: 500 });
  }
}

위 코드에서는 쿠키에서 accessToken을 읽어와 백엔드 API 호출 시 Authorization 헤더에 포함시킵니다. 인증이 필요한 API 요청에 대해 서버가 안전하게 accessToken을 전달하고 클라이언트는 토큰에 직접 접근할 필요가 없으므로 보안이 강화됩니다.

3. 클라이언트 측 react-query 설정

이제 클라이언트 측에서 react-query를 사용해 Next.js API 라우트를 통해 데이터를 요청하는 훅을 설정합니다.

React Query 훅 (useData.ts)

// src/hooks/useData.ts
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

async function fetchData() {
  const response = await axios.get('/api/data', { withCredentials: true });
  return response.data;
}

export function useData() {
  return useQuery(['data'], fetchData);
}
  • fetchData 함수에서 axios.get 요청에 withCredentials: true 옵션을 추가하여 httpOnly 쿠키가 요청에 자동으로 포함되도록 합니다.
  • react-query는 이 데이터를 캐시하고, 여러 컴포넌트에서 재활용할 수 있습니다.

4. 클라이언트 컴포넌트에서 데이터 사용하기

이제 클라이언트 컴포넌트에서 useData 훅을 사용해 데이터를 가져오고 상태에 따라 화면을 구성합니다.

// src/app/page.tsx
'use client';

import { useData } from '@/hooks/useData';

export default function Page() {
  const { data, isLoading, error } = useData();

  if (isLoading) return <p>로딩 ...</p>;
  if (error) return <p>오류 발생: {error.message}</p>;

  return (
    <div>
      <h1>데이터 목록</h1>
      <ul>
        {data.map((item: { id: number; name: string }) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

클라이언트 컴포넌트에서는 Next.js API 라우트를 통해 데이터를 요청하며, react-query가 로딩, 오류, 성공 상태를 관리합니다.

요약

  • httpOnly 쿠키accessToken을 저장해 보안을 강화하고, 클라이언트에서 토큰에 직접 접근하지 않도록 합니다.
  • Next.js API 라우트가 프록시 역할을 수행하여 쿠키의 토큰을 읽고 백엔드 API와 통신합니다.
  • React query는 클라이언트 측에서 Next.js API 라우트를 통해 데이터를 가져오고 캐시를 관리하여 효율적인 데이터 접근을 제공합니다.

이 구조는 클라이언트에서 직접적으로 accessToken을 다루지 않아 보안이 강화되는 동시에, react-query와 Next.js API 라우트를 사용하여 안정적이고 효율적인 데이터 관리를 지원합니다.