인증의 기본적인 원리
웹 애플리케이션에서 인증의 핵심 아이디어는 항상 동일합니다. 사용자(클라이언트, 브라우저)가 서버에 접근하여 자신의 신원을 증명하고, 서버는 이 신원을 확인하여 보호된 리소스에 대한 접근 권한을 부여하는 것입니다.

- 클라이언트와 서버의 상호작용:
- 사용자가 로그인 폼에 이메일, 비밀번호와 같은 자격 증명을 입력하고 '로그인'을 클릭합니다.
- 클라이언트(브라우저)는 해당 데이터를 담은 요청을 서버로 전송합니다.
- 서버는 데이터베이스에 접근하여 해당 이메일 주소의 계정이 존재하는지, 입력된 비밀번호가 저장된 비밀번호와 일치하는지 검증합니다.
- 검증에 성공하면, 서버는 클라이언트에게 인증 성공 응답을 보냅니다.
- 권한 증명(Permission)의 필요성:
- 로그인 성공 후, 클라이언트는 프로필 페이지 접속이나 비밀번호 변경과 같은 보호된 경로/작업에 대한 API 요청 등을 서버에 보냅니다.
- 이때, 단순한 "네, 저는 인증(로그인)되었습니다"라는 클라이언트의 주장만으로는 충분하지 않습니다. 누구나 요청을 보낼 수 있으므로, 서버는 사용자가 인증되었음을 증명할 수 있는 추가적인 데이터(권한)을 요구합니다.
- 이러한 권한은 위조할 수 없는 형태여야 하며, 이를 위해 주로 서버 측 세션(Server-side Sessions) 또는 인증 토큰(Authentication Tokens)이라는 두 가지 메커니즘을 사용합니다.
두 가지 주요 인증 메커니즘

1. 서버 측 세션 (Server-side Sessions)
서버 세션 방식에서는 서버가 인증 상태를 직접 저장합니다.
- 사용자가 로그인하면 서버는 고유한 세션 ID(Session ID) 를 생성합니다.
- 이 세션 ID는 데이터베이스 또는 메모리(Redis 등)에 저장됩니다.
- 동일한 세션 ID를 클라이언트에게 쿠키(Cookie) 로 전달합니다.
- 이후 클라이언트가 요청을 보낼 때, 브라우저는 자동으로 이 쿠키를 함께 보냅니다.
- 서버는 쿠키에 담긴 세션 ID를 확인하고, DB에 저장된 세션과 일치하면 접근을 허용합니다.
이 방식의 장점은 보안성이 높다는 것입니다.
세션 ID가 서버에 저장되어 있기 때문에, 서버는 항상 인증된 사용자를 식별할 수 있습니다.
하지만 세션 방식에는 서버 부하와 확장성 문제가 있습니다.
여러 서버(멀티 인스턴스)에서 동일한 세션을 공유하려면, 세션 저장소를 중앙화해야 하기 때문입니다.
2. 인증 토큰 (Authentication Tokens)
- 사용자 로그인 성공 시, 서버는 토큰을 생성하여 클라이언트에게 반환합니다.
- 이 토큰은 단순한 문자열이 아니라, 서명이 포함된 암호학적 데이터입니다.
- 일반적으로 서버는 이 토큰 자체나 세션 식별자를 데이터베이스에 저장하지 않습니다 (무상태성).
- 클라이언트는 토큰을 저장하고, 이후 보호된 리소스에 대한 요청 시 토큰을 첨부하여 보냅니다.
- 서버는 이 토큰을 비밀 키(secret key)를 알고 있으므로 “이 토큰이 내가 만든 게 맞는지”를 검증(verify) 방법을 알고 있습니다. 유효하면 접근을 허용합니다.
- 누군가 임의의 토큰 문자열을 만들어 보내도 서버는 유효성을 확인할 수 없어 접근을 거부합니다.
💡 참고:
서버는 일반적으로 JWT 토큰 자체를 저장하지 않지만,
Refresh Token이나 로그아웃 처리를 위한 블랙리스트(토큰 무효화 리스트)는
보안을 위해 데이터베이스나 Redis에 저장하는 경우가 많습니다.
JSON Web Token(JWT)의 구조와 동작 원리
JWT는 3개의 블록 구성되며, 점(.)으로 구분된 긴 문자열 형태입니다.
- 헤더 (Header): 토큰 유형(JWT) 및 서명에 사용된 알고리즘 정보(예: HS256)가 포함됩니다.
- 페이로드 (Payload): 개발자가 추가하는 사용자 정보(예: 사용자 ID)와 발행자 데이터(메타데이터)가 포함됩니다. 이 데이터는 암호화된 것이 아니며 해독 없이 읽을 수 있으므로 민감한 정보는 포함하지 않습니다.
- 서명 (Signature): 헤더와 페이로드를 서버만 아는 비밀 키(Secret Key)와 결합하여 암호화 알고리즘으로 생성한 값입니다.
JWT는 암호화된(encrypted) 것이 아니라 서명(signed) 된 것입니다.
즉, 토큰을 열어보면 데이터는 읽을 수 있지만, 비밀 키를 모르면 유효한 서명을 만들 수 없습니다.
따라서 해커가 임의로 JWT를 조작하거나 생성해도 서버는 서명이 올바르지 않다고 판단해 접근을 거부합니다.
React 및 Next.js에서 토큰 방식 선호 이유

React로 구축된 SPA(Single-Page Application) 및 Next.js 환경에서는 일반적으로 서버 측 세션보다 인증 토큰 방식, 특히 **JSON Web Tokens (JWT)**를 사용하는 것이 선호됩니다.
- 무상태(Stateless) API: SPA를 위한 백엔드 API는 보통 개별 클라이언트의 상태(세션)를 추적하지 않는 무상태 방식으로 설계됩니다.
- 서버 비관여: React/Next.js에서는 첫 페이지 로드 이후 많은 페이지 이동 및 동작이 프론트엔드 JavaScript에 의해 처리되므로, 서버는 클라이언트의 모든 요청/동작에 직접 관여하지 않습니다.
- 프론트엔드/백엔드의 분리: 프론트엔드와 백엔드가 상대적으로 분리되어 있어, 클라이언트가 스스로 인증되었음을 증명할 수 있는 독립적인 권한을 가져야 합니다. JWT가 이러한 독립적인 증명 역할을 합니다.
⚠️ 추가 참고:
Next.js의 App Router(서버 컴포넌트) 환경에서는
NextAuth.js와 같은 서버 세션 기반 인증도 자주 사용됩니다.
즉, SPA라면 JWT가 유리하지만, SSR/Hybrid 구조에서는 세션 방식도 여전히 실무에서 많이 채택됩니다.
JSON Web Tokens (JWT)의 원리
JWT는 인증 토큰의 가장 일반적인 형태이며, 클라이언트가 인증되었음을 서명(Signature)을 통해 증명하는 역할을 합니다.

1. JWT의 구성 요소
JWT는 기본적으로 세 부분으로 구성되며, 점(.)으로 구분된 긴 문자열 형태입니다.
- Issuer Data : 발행자 데이터(메타데이터)
- Custom Data: 개발자가 추가하는 사용자 정보
- Secret Signing Key: 서버만 아는 비밀 키(Secret Key)
2. 토큰의 생성과 검증 (클라이언트와 서버 간 JWT 흐름)
- 사용자가 로그인 정보를 제출합니다.
- 토큰 생성: 로그인 성공 시, 서버는 비밀 키를 사용하여 헤더 및 페이로드를 서명하여 JWT를 생성하고 클라이언트에게 전달합니다. (참고: 서명은 암호화가 아니며, 토큰의 내용을 숨기는 것이 아니라 서버가 토큰을 발행했음을 증명하는 역할)
- 토큰 저장: 클라이언트(브라우저)는 이 토큰을 저장합니다. 저장 시에는 보안상 주의가 필요합니다.
- LocalStorage: XSS 공격에 취약 (지양 권장)
- HTTP-Only Secure Cookie: XSS에는 안전하지만 CSRF 공격에 대비 필요
- Memory Storage(예: React 상태): 가장 안전하지만 새로고침 시 사라짐
- 요청 시 첨부: 클라이언트는 보호된 리소스에 대한 요청 시 토큰을 첨부합니다. (이후 API 요청 시, Authorization 헤더에 JWT를 첨부합니다.) ex) Authorization: Bearer <JWT_TOKEN>
- 토큰 검증: 서버는 요청과 함께 받은 토큰을 서버만 아는 비밀 키를 사용하여 검증합니다. (만약 임의로 만든 토큰이라면, 서버의 비밀 키로 서명했을 때 동일한 서명이 생성될 수 없으므로, 서버는 이 토큰이 위조되었거나 유효하지 않다고 판단하고 접근을 거부합니다.)
⚠️ 보안 유의사항:
JWT는 암호화되지 않은 Base64 인코딩 형식이므로,
토큰이 탈취되면 누구나 해당 권한으로 접근할 수 있습니다.
따라서 HTTPS 통신, Secure 쿠키, 짧은 만료 시간 설정이 필수적입니다.
JWT의 만료와 재발급
보안을 위해 JWT는 보통 짧은 수명의 Access Token과 긴 수명의 Refresh Token을 함께 사용합니다.
- Access Token이 만료되면, 클라이언트는 Refresh Token으로 새로운 Access Token을 요청합니다.
- Refresh Token은 서버 DB나 Redis에 저장되어 관리되며, 탈취된 토큰을 무효화하기 위한 블랙리스트로도 활용됩니다.
이 구조를 통해 서버는 여전히 무상태성을 유지하면서도, 토큰 탈취·만료 등의 상황에 대응할 수 있습니다.
React 및 Next.js에서 인증이 이렇게 쓰이는 이유
React나 Next.js로 만든 앱은 대부분 Single Page Application(SPA) 구조를 가지고 있습니다.
즉, 사용자가 페이지를 이동해도 실제로는 브라우저가 새로운 요청을 보내지 않고 JavaScript가 내부적으로 화면을 갱신합니다.
이때 서버는 모든 페이지 요청을 직접 처리하지 않기 때문에, 세션 기반 인증(Session-based Authentication) 은 잘 맞지 않습니다.
대신 SPA에서는 토큰 기반 인증(Token-based Authentication) 을 사용합니다.
왜냐하면:
- SPA는 페이지 이동 시마다 서버에 요청을 보내지 않습니다.
- 서버는 클라이언트의 상태를 유지하지 않고(stateless) 단순히 요청 단위로만 응답합니다.
- 따라서 클라이언트가 스스로 인증 상태를 유지할 수 있어야 합니다.
JWT는 이러한 SPA 환경에서 완벽히 적합한 구조를 제공합니다. 클라이언트는 JWT를 보관하고, 요청 시마다 헤더에 포함시켜 서버에 전달하면 됩니다.
'NextJS > NextAuth' 카테고리의 다른 글
| [NextAuth] 클라이언트 사이드 페이지 가드 (라우트 보호) 추가하기 - 6 (0) | 2025.11.18 |
|---|---|
| [NextAuth] 클라이언트 컴포넌트 인증 : 로그인, 로그아웃 -5 (0) | 2025.11.17 |
| [NextAuth] 로그인 API 구현 [...nextauth].js - 4 (0) | 2025.11.16 |
| [NextAuth] 회원가입 컴포넌트 및 API 연동 - 3 (0) | 2025.11.12 |
| [NextAuth] 회원가입 API 구현하기 - 2 (1) | 2025.11.10 |