본문 바로가기
Spring 7기 프로젝트/뉴스피드 팀 프로젝트

Spring Security + JWT 인증 시스템 이해하기

by JuNo_12 2025. 5. 29.

🤔 왜 인증이 필요할까?

웹 애플리케이션에서 "이 사람이 누구인지"를 확인하고, "이 사람이 이 기능을 사용할 권한이 있는지"를 판단해야 합니다.

예시: 인스타그램

  • 로그인 안 한 상태: 게시물 보기만 가능
  • 로그인 한 상태: 게시물 작성, 좋아요, 댓글 가능
  • 내 게시물: 수정/삭제 가능
  • 남의 게시물: 수정/삭제 불가능

🔐 전통적인 세션 방식의 문제점

세션 방식이란?

1. 사용자 로그인 → 서버가 "세션ID" 생성 → 쿠키로 브라우저에 저장
2. 다음 요청 시 → 쿠키의 세션ID 확인 → 서버 메모리에서 사용자 정보 조회

문제점

  • 서버 메모리 사용: 사용자가 많으면 메모리 부족
  • 확장성 문제: 서버가 여러 대면 세션 공유 복잡
  • 모바일 앱 호환성: 쿠키 사용이 어려움

🎫 JWT(JSON Web Token)란?

간단히 말하면

"신분증" 같은 토큰입니다. 사용자 정보가 암호화되어 담겨있어요.

JWT의 구조

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyQGVtYWlsLmNvbSJ9.signature
    ↑ Header        ↑ Payload              ↑ Signature
  • Header: 어떤 암호화 방식 사용했는지
  • Payload: 사용자 정보 (이메일, 만료시간 등)
  • Signature: 위조 방지용 서명

JWT의 장점

  • 서버 메모리 절약: 토큰에 정보가 다 들어있음
  • 확장성 좋음: 여러 서버가 같은 비밀키로 검증 가능
  • 모바일 친화적: HTTP 헤더로 전송

🛡️ Spring Security란?

Spring에서 제공하는 보안 프레임워크입니다. 개발자들에게 편의성을 제공합니다.

주요 역할

  1. 인증(Authentication): "누구인가?"
  2. 인가(Authorization): "권한이 있는가?"
  3. 보안 설정: 어떤 URL은 로그인 필요, 어떤 URL은 공개 등

핵심 개념

  • Filter Chain: 요청이 들어오면 여러 필터를 거쳐서 검증
  • Authentication: 인증된 사용자 정보를 담는 객체
  • SecurityContext: 현재 로그인한 사용자 정보를 저장하는 곳

🔄 우리 프로젝트의 인증 흐름

1. 회원가입

클라이언트 → AuthController → AuthService → 비밀번호 암호화 → DB 저장

2. 로그인

1. 클라이언트가 이메일/비밀번호 전송
2. AuthService에서 DB의 사용자 정보와 비교
3. 인증 성공 시 JWT 토큰 생성
4. 클라이언트에게 토큰 반환

3. 인증이 필요한 API 호출

1. 클라이언트가 Authorization 헤더에 토큰 포함해서 요청
2. JwtAuthenticationFilter가 토큰 검증
3. 유효한 토큰이면 SecurityContext에 사용자 정보 저장
4. Controller에서 @AuthenticationPrincipal로 사용자 정보 사용

4. 로그아웃

1. 클라이언트가 로그아웃 요청 (토큰 포함)
2. 해당 토큰을 블랙리스트에 추가
3. 이후 그 토큰으로 요청하면 거부

📋 주요 컴포넌트 설명

🔧 JwtTokenProvider

역할: JWT 토큰 생성, 검증, 정보 추출

// 토큰 생성
String token = jwtTokenProvider.createToken("user@email.com");

// 토큰에서 이메일 추출
String email = jwtTokenProvider.getSubject(token);

// 토큰 유효성 검증  
boolean isValid = jwtTokenProvider.validateToken(token);

🚪 JwtAuthenticationFilter

역할: 모든 요청을 가로채서 토큰 검증

요청 → 토큰 추출 → 유효성 검사 → 블랙리스트 확인 → 인증 정보 설정 → 다음 단계

🚨 JwtAuthenticationEntryPoint

역할: 인증 실패 시 에러 응답 생성

// 토큰이 없거나 잘못된 경우
{
  "error": "로그인이 필요합니다."
}

🛑 TokenBlacklist

역할: 로그아웃된 토큰 관리

  • 로그아웃하면 토큰을 블랙리스트에 추가
  • 블랙리스트의 토큰으로 요청하면 거부
  • 스케줄러가 주기적으로 만료된 토큰 정리

🔍 실제 동작 예시

로그인 성공 시

POST /api/auth/login
{
  "email": "user@email.com",
  "password": "password123"
}

응답:
{
  "token": "eyJhbGciOiJIUzI1NiJ9..."
}

게시글 작성 시

POST /api/posts
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
{
  "title": "제목",
  "content": "내용"
}

인증 실패 시

POST /api/posts
(토큰 없음)

응답:
{
  "error": "로그인이 필요합니다."
}

정리

우리가 구현한 시스템의 특징

  1. JWT 기반: 서버 메모리 절약, 확장성 좋음
  2. 블랙리스트: 로그아웃 처리로 보안 강화
  3. 필터 체인: Spring Security로 체계적인 보안 처리
  4. 예외 처리: 사용자 친화적인 에러 메시지
  5. 스케줄링: 자동으로 만료된 토큰 정리

장점

  • 성능: 세션보다 빠름
  • 보안: 토큰 위조 불가, 블랙리스트로 안전한 로그아웃
  • 확장성: 모바일 앱에서도 사용 가능
  • 유지보수: Spring Security로 체계적 관리