Skip to content

Latest commit

 

History

History
140 lines (107 loc) · 7.52 KB

File metadata and controls

140 lines (107 loc) · 7.52 KB

🔐 OAuth2 · JWT · Swagger · Redis · JUnit5 실습

Spring Security 기반 인증/인가와 JUnit5 테스트 학습 레포지토리

Java Spring Boot JUnit5 Redis OpenAPI


📌 레포 목적

  1. 프로젝트 요구사항에 맞춰 서비스 코드를 먼저 작성(TDD 아님)
  2. 서비스를 가장 작은 단위로 나눠 메서드 단위 테스트 작성
  3. 단위 테스트 통과 후 모듈/클래스 간 통합 테스트 수행

→ JUnit5에 익숙해지고 “적절한 테스트 코드”를 작성하는 역량을 목표로 합니다.


✅ 요구사항 & 구현

  • Spring Security로 인증/인가
  • OAuth Client로 Google/Kakao OAuth 로그인
  • 일반 로그인도 지원
  • REST API 설계
  • Swagger(OpenAPI) 자동 문서화
  • RedisRefresh Token 보관
  • 사용자 정보 조회

🧪 단위 테스트

  • CustomOAuth2UserService
    신규 회원 → 회원가입, 기존 회원 → 로그인, 미지원 소셜 예외 처리
  • UserService
    일반 회원가입, 토큰 재발행, 사용자 정보 조회, 로그아웃
  • JwtUtil
    Access/Refresh 토큰 생성, 검증, 사용자 ID 추출

🧪 통합테스트

테스트 환경

  • @SpringBootTest(webEnvironment = RANDOM_PORT)와 TestRestTemplate를 사용하여 실제 서블릿 컨테이너에서 전체 Spring Security 필터 체인을 포함한 HTTP 요청 흐름을 검증
  • H2 인메모리 DB 사용
  • Redis(로컬 6379)를 사용하여 RefreshTokenService를 실제 빈으로 주입해 리프레시 토큰의 저장/검증을 수행
  • 테스트 격리는 @DirtiesContext(BEFORE_EACH_TEST_METHOD)로 보장하고 @Transactional은 사용하지 않아, 롤백 없이 실제 커밋/조회 동작을 검증

이슈 & 해결

  • 현상
    • 중복 회원가입 테스트 실패. 테스트 코드의 userRepository.findAll() 호출 지점에서 UNIQUE(nickname) 제약 위반 발생.
  • 원인
    • Hibernate 기본 FlushMode.AUTO는 SELECT 직전 flush 수행.
    • 테스트가 @Transactional일 때, createTestUser()로 만든 유저는 테스트 트랜잭션 내부(미커밋)에만 존재.
    • 반면 TestRestTemplate로 호출한 POST /api/users는 다른 트랜잭션에서 수행됨.
    • 서비스의 중복 체크(findByUsername, existsByNickname)는 테스트 트랜잭션의 미커밋 데이터를 볼 수 없으므로 INSERT를 시도하고 커밋함.
    • 이후 테스트 코드가 findAll()을 부르는 순간, 테스트 트랜잭션이 SELECT 전에 자동 flush 하며 테스트 측 INSERT가 DB로 밀림
    • 이미 커밋된 동일 nickname과 부딪혀 UNIQUE 충돌이 findAll() 위치에서 표면화.
  • 해결
    • 테스트 메서드의 @Transactional 제거(테스트 트랜잭션과 API 트랜잭션 가시성 이슈 해소)
    • @DirtiesContext(BEFORE_EACH_TEST_METHOD) 로 매 테스트마다 컨텍스트/DB 초기화

검증 대상

  • 회원가입 POST /api/users → 201 Created 응답 및 DB 반영 확인
  • 로그인(JWT 발급) POST /login → 응답 본문에 accessToken, Set-Cookie에 HttpOnly refreshToken 들었는지 검증
  • Redis에 refreshToken 저장 여부 확인
  • 유저 정보 조회 GET /api/users/me → 200 Ok 응답 및 유저 정보 반환
  • 토큰 재발급 POST /api/users/refresh → 새 accessToken 발급 및 RT 회전(새 쿠키)
  • 로그아웃 POST /api/users/logout → refreshToken 쿠키 삭제(Max-Age=0), Redis의 refreshToken 제거
  • 중복 회원가입 차단 → 기존 유저 존재 시 409 Conflict 응답, DB 사용자 수(테스트 유저 1명) 검증

🛠 기술 스택

구분 기술
Backend Java 17, Spring Boot
DB PostgreSQL, JPA, Redis
Auth Spring Security, OAuth-Client, jjwt
OAuth API Google, Kakao
Test JUnit5, Mockito, AssertJ

🚀 빠른 시작

# 실행
./gradlew bootRun

# Swagger UI
open http://localhost:8080/swagger-ui.html

# ubuntu 실행 후 Redis 설치
sudo apt updatge
sudo apt install redis

# Redis 서버 실행
redis-server

# Redis 접속 및 토큰 확인
redis-cli
keys *     # 키 목록 확인
get [키 이름]   # 값 확인인

🔑 인증 플로우 요약

  • 소셜 로그인 진입: GET /oauth2/authorization/{kakao|google}
  • 콜백(백엔드 수신): /login/oauth2/code/{registrationId}
  • 일반 로그인: POST /api/auth/login
  • 로그인 성공 후 access token은 Request Body, refresh token은 HttpOnly 쿠키로 응답
  • 토큰 재발급: POST /api/users/refresh (refresh token 쿠키 필요)
  • 로그아웃: POST /api/users/logout

📸 API 테스트 스냅샷

브라우저 테스트

액세스 토큰 응답
로그인 성공 후 액세스 토큰을 응답으로 수신

리프레시 토큰 쿠키
리프레시 토큰은 HttpOnly 쿠키로 저장

포스트맨 테스트

일반 회원가입
일반 회원가입

일반 로그인
일반 로그인 → AccessToken/RefreshToken 발급

유저 정보 조회
유저 정보 조회

DB 확인

DB 사용자 테이블 조회
DB 사용자 테이블 조회

Redis 리프레시 토큰

로그아웃 시 RT 삭제
로그아웃 시 Redis에서 Refresh Token 삭제

재발급 시 RT 회전
재발급 시 Redis의 기존 Refresh Token 회전(갱신)

스웨거 연동 (API 명세서)

Swagger UI
Swagger UI 화면


📖 스터디 방식

  • 유튜브 강의
  • AI 도구 보조 (Claude AI 활용)
  • 기술 블로그 검색

📝 스터디 하면서 작성한 기술 블로그

단위테스트 테스트 코드 작성
Mockito.Spy() 잘못된 사용으로 인한 문제 발생과 해결
Redis가 무엇일까?