본문 바로가기
일상이 개발

Next.js 인증 상태 유지 전략 완전 정복 – 세션, 토큰, 쿠키 기반의 UX + 보안 설계 가이드

by 아빠고미 2025. 5. 14.
반응형

Next.js 앱에서 사용자 인증 상태 유지 전략 – 세션, 쿠키, 토큰 기반의 실전 설계 가이드

Next.js로 로그인 기능을 구현했다면 다음으로 고민해야 할 것은 인증 상태를 어떻게 안정적으로 유지할 것인가? 입니다.

Next.js 인증 상태 유지 전략 완전 정복 – 세션, 토큰, 쿠키 기반의 UX + 보안 설계 가이드

 

브라우저 새로고침, 탭 이동, 서버사이드 렌더링 환경, 보안 문제까지… 이 모든 상황에서 인증 정보를 잃지 않고 UX와 보안을 모두 잡기 위해선 탄탄한 인증 상태 유지 전략이 필요합니다.

이번 글에서는 실무에서 자주 쓰이는 인증 유지 방식들을 비교하고, Next.js 환경에 최적화된 설계를 다음과 같이 소개합니다:

  • ✅ 인증 상태 유지 방식 비교 (쿠키, 세션, JWT, Refresh)
  • ✅ 클라이언트/서버 각각에서의 인증 처리
  • ✅ 자동 로그인 유지 흐름
  • ✅ 인증 토큰 갱신 전략
  • ✅ 실전 설계 예시

1. 🔐 인증 상태 유지의 기본 개념

✅ 인증 상태란?

사용자가 로그인된 상태라는 걸 클라이언트/서버가 인지하고, 페이지 새로고침, 이동, SSR에서도 유지하는 것

✅ 주요 상태 유지 방식

방식 저장 위치 특징
세션 서버 메모리 or DB + 쿠키로 식별 안정적, 서버 리소스 소비
JWT 쿠키 or localStorage SPA에 적합, 무상태 구조
Refresh Token 쿠키 (HttpOnly) 토큰 만료 대비, 보안 강화

2. ⚙️ Next.js에서 고려해야 할 점

📌 CSR vs SSR 인증 상태 처리

  • CSR: localStorage or context 기반 → 새로고침 시 초기화됨
  • SSR: getServerSideProps 등에서 쿠키 기반 인증 필요

✅ 쿠키 기반 인증의 장점

  • 서버와 클라이언트 모두에서 접근 가능
  • 브라우저 새로고침, 라우팅에도 상태 유지
  • HttpOnly 옵션으로 보안성 높음

📌 예시: SSR에서 사용자 인증 확인

export async function getServerSideProps(context) {
  const token = context.req.cookies.token;

  if (!token) {
    return {
      redirect: {
        destination: '/login',
        permanent: false,
      },
    };
  }

  return {
    props: { user: decodeToken(token) },
  };
}

➡️ 서버에서 인증을 판단하고, 로그인이 필요한 페이지에서 유저를 보호할 수 있습니다.

3. 🔄 토큰 갱신 전략 – 만료되어도 다시 살아나는 인증

✅ Access Token + Refresh Token 구조

  • Access Token: 짧은 수명 (5~15분)
  • Refresh Token: 긴 수명 (7일~30일)
  • 만료 시 → Refresh Token으로 재발급

📌 저장 위치

  • Access Token: 메모리 or 쿠키
  • Refresh Token: HttpOnly 쿠키 (XSS 보호)

✅ 실전 흐름

  1. 1. 로그인 성공 → 둘 다 발급
  2. 2. API 호출 시 Access Token 사용
  3. 3. 만료되면 → Refresh Token으로 갱신 요청
  4. 4. 새 토큰 저장 후 재시도

📦 예시: Axios 인터셉터로 자동 처리

axios.interceptors.response.use(
  res => res,
  async (error) => {
    if (error.response.status === 401) {
      // refreshToken으로 재발급 시도
      const res = await axios.post('/auth/refresh');
      const newAccessToken = res.data.token;

      axios.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`;
      error.config.headers['Authorization'] = `Bearer ${newAccessToken}`;
      return axios(error.config);
    }
    return Promise.reject(error);
  }
);

4. ✅ 자동 로그인 유지 – UX 향상을 위한 필수 기능

✅ 주요 조건

  • 브라우저 새로고침에도 인증 유지
  • 페이지 진입 시 자동 로그인
  • SSR에서도 동일한 인증 상태 확인 가능

📌 방법

  • 쿠키 기반 인증: 새로고침/SSR도 문제 없음
  • Context or Zustand로 상태 공유

예시: Zustand 기반 사용자 상태

// authStore.ts
import { create } from 'zustand';

export const useAuth = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
  logout: () => set({ user: null }),
}));

✅ 페이지 접근 보호

const { user } = useAuth();

if (!user) {
  router.push('/login');
}

5. 🔁 클라이언트와 서버 상태 통합 흐름

📌 권장 구조

  1. 1. SSR 시 쿠키에서 인증 상태 확인
  2. 2. 클라이언트 진입 후 → 상태 저장소 (Context/Zustand 등)에 적용
  3. 3. 이후 라우팅, 요청, 리액션 등은 클라이언트 상태 기반

➡️ 서버/클라이언트 상태가 분리되지 않고 연결되면 UX와 보안이 모두 안정적으로 유지됩니다.

6. 🔒 인증 상태 유지 시 보안 고려사항

✅ 쿠키 보안 설정

  • HttpOnly: JS에서 접근 불가 (XSS 방지)
  • Secure: HTTPS 연결에서만 쿠키 전송
  • SameSite: CSRF 방지 설정 (Lax or Strict)
Set-Cookie: refreshToken=...; HttpOnly; Secure; SameSite=Lax; Path=/

✅ CSRF 방지

  • REST API 기반: SameSite + CSRF 토큰
  • GraphQL/REST 상관없이 POST 요청은 확인 필요

✅ XSS 방지

  • 토큰을 localStorage에 저장하지 않도록 주의
  • 가능하면 모든 인증은 쿠키 기반으로 처리

7. 🧱 실전 설계 예시 – 전체 흐름 구성

✅ 로그인 시

  • 1. API 호출 → Access + Refresh Token 발급
  • 2. Access Token은 메모리, Refresh는 HttpOnly 쿠키에 저장
  • 3. Zustand 등으로 user 상태 저장

✅ SSR 렌더링 시

  • 1. getServerSideProps에서 쿠키 확인
  • 2. 인증 안 되면 로그인 페이지로 redirect
  • 3. 인증 되면 user 정보 hydrate

✅ API 호출 시

  • Access Token으로 인증 → 401 발생 시 Refresh로 갱신

✅ 자동 로그인 유지

  • Refresh Token이 남아있다면 → 자동 재인증
  • 유지기간 만료되면 로그인 페이지로 이동

8. 🎯 UX 관점에서의 인증 상태 설계 체크리스트

✅ UX/보안 균형 맞추기

  • 로딩 중 표시 → 로그인 확인 중...
  • 에러 발생 시 → 사용자에게 친절한 메시지 제공
  • 보안 상태 로그아웃 처리 → "다시 로그인해주세요."

✅ 사용자 입장에서 중요한 흐름

  • 로그인 후 → 즉시 서비스 접근
  • 새로고침, 탭 이동에도 로그인 유지
  • 장시간 미사용 → 자동 로그아웃 or 알림

긴 글 읽어주셔서 감사합니다! 공감, 댓글, 공유로 응원해주시면 다음 글 제작에 큰 힘이 됩니다 🙌

반응형