Skip to content

[FEAT] 인증/인가 공통 응답 및 예외처리 적용#48

Merged
Be-HinD merged 21 commits intomainfrom
feat/auth
Dec 9, 2025
Merged

[FEAT] 인증/인가 공통 응답 및 예외처리 적용#48
Be-HinD merged 21 commits intomainfrom
feat/auth

Conversation

@Be-HinD
Copy link
Member

@Be-HinD Be-HinD commented Dec 9, 2025

📝 Pull Request

📌 PR 종류

해당하는 항목에 체크해주세요.

  • 기능 추가 (Feature)
  • 버그 수정 (Fix)
  • 문서 수정 (Docs)
  • 코드 리팩터링 (Refactor)
  • 테스트 추가 (Test)
  • 기타 변경 (Chore)

✨ 변경 내용

기존 인증/인가 로직에서 공통 응답 및 예외 처리 적


🔍 관련 이슈

해당 PR이 해결하는 이슈가 있다면 연결해주세요.
#31


🧪 테스트

변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.

  • 유닛 테스트 추가 / 수정
  • 통합 테스트 검증
  • 수동 테스트 완료
image image

🚨 확인해야 할 사항 (Checklist)

PR을 제출하기 전에 아래 항목들을 확인해주세요.

  • 코드 포매팅 완료
  • 불필요한 파일/코드 제거
  • 로직 검증 완료
  • 프로젝트 빌드 성공
  • 린트/정적 분석 통과 (해당 시)

🙋 기타 참고 사항

  • CustomUserDetails 추가 적용
  • h2관련 실행 오류 조치
  • 공통 응답 status 필드 추가

Summary by CodeRabbit

릴리스 노트

  • 개선사항

    • API 응답 형식 표준화 및 HTTP 상태 코드 명시(작성/생성 시 201 등)
    • 인증 흐름에서 더 구체적이고 일관된 오류 응답 제공(회원 미존재, 중복 가입, 비밀번호 불일치, 만료/유효하지 않은 토큰 등)
    • JWT 검증 및 인증 필터의 오류 처리 강화(구조화된 JSON 에러 응답, 공개 엔드포인트 예외 처리)
    • 로그아웃 시 보안성 향상된 쿠키 속성 적용
  • 테스트

    • 인증 API 테스트 스크립트의 토큰 처리 및 보호 API 테스트 추가/수정

✏️ Tip: You can customize this high-level summary in your review settings.

- JWT 커스텀 필터 추가
- JWT 유틸리티 작성
- 테스트 컨트롤러 추가
- Entity 개발
- API 인가 refresh 분기처리 추가
- accesstoken 재발급 엔드포인트 개발
- Refresh Token JSON -> Cookie 변경 (secure, httponly 설정)
- .http 테스트 코드 작성
- Auth 도메인 전역 예외 처리 및 커스텀 예외 적용
- Security Sevlet 예외 처리 적용
- .http 테스트 코드 추가
- security context에서 userId 가져올 수 있도록 커스텀 클래스 추가
- 테스트 로그 추가
@Be-HinD Be-HinD self-assigned this Dec 9, 2025
@Be-HinD Be-HinD added the ✨enhancement New feature or request label Dec 9, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

인증 흐름과 예외 처리를 도메인 특화 예외로 정리하고 ApiResponse에 HTTP 상태 필드를 추가했습니다. Spring Security용 CustomUserDetails·필터·토큰 공급자 로직이 변경되며 컨트롤러 응답이 모두 ApiResponse(status, ...) 형태로 일관화되었습니다.

Changes

Cohort / File(s) 요약
빌드 구성
\build.gradle``
H2 의존성 스코프를 runtimeOnlydevelopmentOnly로 변경
인증 예외 클래스
\src/main/java/.../auth/exception/*.java``
UserAlreadyExistsException, UserNotFoundException, InvalidPasswordException, DeletedUserException 추가
토큰 예외 클래스
\src/main/java/.../common/security/exception/*.java``
ExpiredTokenException, InvalidTokenException 추가
오류 코드
\src/main/java/.../common/exception/AppErrorCode.java``
USER_NOT_FOUND, ALREADY_EXISTS_USER, INVALID_PASSWORD_VALUE, DELETED_USER, EXPIRED_TOKEN, INVALID_TOKEN 추가
Auth 서비스
\src/main/java/.../auth/application/AuthService.java``
기존 런타임 예외를 도메인 예외로 대체(회원가입/로그인/토큰갱신 경로 예외 변경)
컨트롤러 응답 일원화
\src/main/java/.../auth/presentation/AuthController.java`, `src/main/java/.../user/presentation/UserController.java`, `src/main/java/.../image/presentation/ImageController.java``
모든 엔드포인트가 ResponseEntity<ApiResponse<T>> 반환으로 변경, 성공 호출은 상태 코드(201/200/204) 명시
ApiResponse 리팩토링
\src/main/java/.../common/response/ApiResponse.java``
status 필드 추가 및 모든 factory 메서드가 상태 코드 파라미터를 요구하도록 시그니처 변경
Spring Security 통합 변경
\src/main/java/.../common/security/*.java``
CustomUserDetails 추가, CustomUserDetailsService 반환형 변경(UserNotFoundException 사용), SecurityConfig의 H2 경로 하드코딩
JWT 필터 & 공급자 개선
\src/main/java/.../common/security/JwtAuthenticationFilter.java`, `.../jwt/JwtTokenProvider.java``
공개 엔드포인트 예외 처리(shouldNotFilter), JSON 오류 응답(sendJsonError), ObjectMapper 주입, 토큰 검증이 InvalidToken/ExpiredToken 예외를 던지도록 변경
전역 예외 처리
\src/main/java/.../common/exception/GlobalExceptionHandler.java``
PROBLEM_BASE_URI를 실제 URL에서 about:blank로 변경
설정·테스트 업데이트
\src/main/resources/application.yml`, `src/test/http/auth/auth-api.http``
spring.profiles.active 주석 처리, JPA ddl-auto를 update로 변경, 테스트 스크립트에서 응답 경로를 response.body.data.*로 업데이트

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant JwtFilter as JwtAuthenticationFilter
    participant JwtProvider as JwtTokenProvider
    participant AuthService
    participant DB as UserRepository/DB

    Client->>JwtFilter: 요청(Authorization or cookie)
    alt 공개 엔드포인트
        JwtFilter-->>Client: pass through (no auth)
    else 토큰 있음
        JwtFilter->>JwtProvider: validateToken(token)
        alt 토큰 만료
            JwtProvider-->>JwtFilter: throw ExpiredTokenException
            JwtFilter-->>Client: JSON error (Expired token)
        else 토큰 무효
            JwtProvider-->>JwtFilter: throw InvalidTokenException
            JwtFilter-->>Client: JSON error (Invalid token)
        else 유효
            JwtProvider->>AuthService: get userId / token claims
            AuthService->>DB: load user
            DB-->>AuthService: user
            AuthService-->>JwtFilter: authentication principal
            JwtFilter->>Client: continue filter chain (authenticated)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45분

주의 검사 포인트:

  • AuthService: 각 경로에서 올바른 도메인 예외가 던져지고 상위 핸들러와 매핑되는지 확인
  • ApiResponse: 모든 호출부(컨트롤러, 테스트 등)에서 새 시그니처에 맞게 상태 코드 전달 여부
  • JwtAuthenticationFilter / JwtTokenProvider: 예외 기반 흐름(ExpiredTokenException, InvalidTokenException)과 JSON 오류 직렬화 로직
  • CustomUserDetails / CustomUserDetailsService: UserDetails 구현이 Spring Security와 호환되는지

Possibly related PRs

개요

인증 시스템을 도메인 특화 예외로 리팩토링하고, ApiResponse에 HTTP 상태 코드를 추가하며, Spring Security의 CustomUserDetails를 구현하고, 모든 인증 엔드포인트를 상태 코드가 명시된 래핑된 응답을 반환하도록 업데이트했습니다.

변경 사항

종류 / 파일 요약
빌드 구성
build.gradle
H2 데이터베이스 의존성 스코프를 runtimeOnly에서 developmentOnly로 변경
인증 예외 클래스 추가
src/main/java/team/wego/wegobackend/auth/exception/UserAlreadyExistsException.java, UserNotFoundException.java, InvalidPasswordException.java, DeletedUserException.java
사용자 존재 여부, 비밀번호 검증, 삭제된 계정에 대한 도메인 특화 예외 클래스 추가
토큰 예외 클래스 추가
src/main/java/team/wego/wegobackend/common/security/exception/ExpiredTokenException.java, InvalidTokenException.java
토큰 만료 및 유효성 검사 실패에 대한 예외 클래스 추가
오류 코드 확장
src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java
USER_NOT_FOUND, ALREADY_EXISTS_USER, INVALID_PASSWORD_VALUE, DELETED_USER, EXPIRED_TOKEN, INVALID_TOKEN 오류 코드 추가
인증 서비스 리팩토링
src/main/java/team/wego/wegobackend/auth/application/AuthService.java
일반 런타임 예외를 도메인 특화 예외(UserAlreadyExistsException, UserNotFoundException, InvalidPasswordException, DeletedUserException, ExpiredTokenException)로 대체
인증 컨트롤러 업데이트
src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java
모든 엔드포인트(signup, login, logout, refresh)의 응답을 ResponseEntity<ApiResponse<T>>로 래핑하고 명시적 HTTP 상태 코드(201, 200, 204) 설정
응답 래퍼 리팩토링
src/main/java/team/wego/wegobackend/common/response/ApiResponse.java
status 필드 추가 및 모든 팩토리 메서드를 상태 코드 매개변수 필수로 업데이트
Spring Security 통합
src/main/java/team/wego/wegobackend/common/security/CustomUserDetails.java, CustomUserDetailsService.java
CustomUserDetails 구현 추가 및 예외 처리를 UserNotFoundException으로 통합
JWT 인증 필터 강화
src/main/java/team/wego/wegobackend/common/security/JwtAuthenticationFilter.java
shouldNotFilter 메서드로 공개 엔드포인트 필터링 제외, sendJsonError 메서드로 JSON 오류 응답 추가, 토큰 검증 실패 시 구조화된 JSON 오류 반환
JWT 토큰 공급자 업데이트
src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java
예외 처리를 InvalidTokenException, ExpiredTokenException으로 변경, ObjectMapper 의존성 추가, 검증 로직 개선
보안 구성 수정
src/main/java/team/wego/wegobackend/common/security/SecurityConfig.java
H2 콘솔 경로 매칭을 PathRequest.toH2Console()에서 하드코딩된 "/h2-console/**"로 변경
전역 예외 처리 조정
src/main/java/team/wego/wegobackend/common/exception/GlobalExceptionHandler.java
PROBLEM_BASE_URI를 실제 URL에서 about:blank로 변경
이미지 컨트롤러 업데이트
src/main/java/team/wego/wegobackend/image/presentation/ImageController.java
ApiResponse.success 호출에 상태 코드 201 추가
사용자 컨트롤러 리팩토링
src/main/java/team/wego/wegobackend/user/presentation/UserController.java
test() 메서드 서명을 변경하여 @AuthenticationPrincipal CustomUserDetails 파라미터 추가 및 ResponseEntity<ApiResponse<String>> 반환
설정 및 테스트
src/main/resources/application.yml, src/test/http/auth/auth-api.http
Spring 프로필 활성화 주석 처리, JPA ddl-auto를 none에서 update로 변경, 인증 API 테스트 스크립트 업데이트

예상 코드 리뷰 노력

🎯 4 (복잡함) | ⏱️ ~45분

주의가 필요한 영역:

  • AuthService.java: 모든 인증 흐름(회원가입, 로그인, 토큰 갱신)에서 예외 처리 로직이 변경되었으므로 각 경로별로 올바른 예외가 발생하는지 검증 필요
  • ApiResponse.java: 모든 팩토리 메서드의 시그니처가 변경되었으므로 이를 호출하는 모든 컨트롤러에서 상태 코드가 올바르게 전달되는지 확인 필요
  • JwtAuthenticationFilter.java: 공개 엔드포인트 필터링 제외 로직과 JSON 오류 응답 처리가 추가되었으므로 필터 체인 흐름과 오류 응답 형식 검증 필요
  • JwtTokenProvider.java: 예외 처리 방식이 변경되고 로그 메시지가 수정되었으므로 각 토큰 검증 시나리오 확인 필요

관련된 PR

🐰 새로운 예외들이 춤을 추고,
상태 코드로 응답을 꾸미니,
Security 친구가 와서 토큰을 살피네,
만료와 무효는 JSON으로 알려주고,
모든 요청이 ApiResponse로 반짝이네 ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.89% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 PR의 주요 변경 사항인 인증/인가 로직에 공통 응답 및 예외처리를 적용하는 것을 명확하게 요약하고 있습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 104dd42 and de98af4.

📒 Files selected for processing (3)
  • src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java (3 hunks)
  • src/main/java/team/wego/wegobackend/common/security/JwtAuthenticationFilter.java (4 hunks)
  • src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java (5 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (12)
src/main/java/team/wego/wegobackend/common/security/SecurityConfig.java (1)

31-35: PathRequest.toH2Console() 사용을 권장합니다.

하드코딩된 "/h2-console/**" 대신 PathRequest.toH2Console()을 사용하면 spring.h2.console.path 속성과 자동으로 연동되어 유지보수성이 향상됩니다.

+import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
 ...
    .authorizeHttpRequests((auth) -> auth
-       .requestMatchers("/h2-console/**").permitAll()
+       .requestMatchers(PathRequest.toH2Console()).permitAll()

또한, 프로덕션 환경에서는 H2 콘솔 접근을 비활성화하는 것이 보안상 권장됩니다 (예: @Profile("dev") 조건부 설정).

src/main/java/team/wego/wegobackend/common/exception/GlobalExceptionHandler.java (1)

263-265: 사용되지 않는 파라미터를 제거하세요.

toProblemType 메서드가 이제 상수만 반환하므로 title 파라미터가 사용되지 않습니다. 메서드를 파라미터 없이 단순화하거나 상수를 직접 사용하도록 리팩터링하는 것이 좋습니다.

다음 diff를 적용하여 리팩터링하세요:

-    private static String toProblemType(String title) {
+    private static String toProblemType() {
         return PROBLEM_BASE_URI;
     }

그리고 모든 호출 부분에서 파라미터를 제거하세요:

-        String type = toProblemType(title);
+        String type = toProblemType();
src/main/java/team/wego/wegobackend/auth/application/AuthService.java (2)

17-17: 사용되지 않는 import 제거 권장

AppErrorCode는 이 파일에서 직접 사용되지 않습니다. 예외 클래스들이 내부적으로 사용하므로 이 import는 제거할 수 있습니다.

-import team.wego.wegobackend.common.exception.AppErrorCode;

66-72: 삭제된 사용자 확인 순서 변경 권장

현재 로직에서는 비밀번호 검증 후 삭제된 사용자를 확인합니다. 이 순서는 타이밍 공격(timing attack)에 취약할 수 있으며, 삭제된 계정에 대해서도 비밀번호 검증이 수행됩니다. 삭제된 사용자 확인을 비밀번호 검증 전에 수행하는 것이 보안상 더 좋습니다.

     User user = userRepository.findByEmail(request.getEmail())
         .orElseThrow(UserNotFoundException::new);

+    if (user.getDeleted()) {
+        throw new DeletedUserException();
+    }
+
     if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
         throw new InvalidPasswordException();
     }
-
-    if (user.getDeleted()) {
-        throw new DeletedUserException();
-    }
src/main/java/team/wego/wegobackend/user/presentation/UserController.java (2)

24-24: 로깅 형식 개선 권장

LocalDateTime.now() 문자열 연결은 비효율적이며, 대부분의 로깅 프레임워크가 타임스탬프를 자동으로 추가합니다. SLF4J 플레이스홀더 패턴을 사용하세요.

-log.info(LocalDateTime.now() + "test endpoint call, userId -> {}", userDetails.getId());
+log.info("test endpoint call, userId -> {}", userDetails.getId());

21-32: 테스트 엔드포인트 프로덕션 배포 전 제거 고려

/api/v1/user/test 엔드포인트가 개발/테스트 목적으로 보입니다. 프로덕션 배포 전에 제거하거나, 특정 프로파일에서만 활성화되도록 @Profile("dev") 어노테이션 사용을 고려하세요.

src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java (1)

25-31: 인증/인가 관련 에러 코드 추가 - LGTM!

도메인별 에러 코드가 적절한 HTTP 상태 코드와 함께 추가되었습니다.

선택적 개선 사항: ALREADY_EXISTS_USER의 경우 HttpStatus.CONFLICT (409)가 리소스 충돌을 더 명확하게 표현할 수 있습니다. 현재 BAD_REQUEST도 동작에는 문제없습니다.

src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java (1)

3-21: 사용되지 않는 import 제거 필요

다음 import들이 현재 파일에서 사용되지 않습니다:

  • HttpServletResponse (Line 9)
  • IOException (Line 10)
  • ErrorResponse (Line 19)
 import com.fasterxml.jackson.databind.ObjectMapper;
 import io.jsonwebtoken.*;
 import io.jsonwebtoken.io.Decoders;
 import io.jsonwebtoken.security.Keys;
 import io.jsonwebtoken.security.SignatureException;
 import jakarta.annotation.PostConstruct;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
 import java.time.LocalDateTime;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;

 import javax.crypto.SecretKey;
 import java.util.Date;
-import team.wego.wegobackend.common.response.ErrorResponse;
 import team.wego.wegobackend.common.security.exception.ExpiredTokenException;
 import team.wego.wegobackend.common.security.exception.InvalidTokenException;
src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java (3)

9-9: 사용되지 않는 import.

HttpStatusCode가 import 되었지만 코드에서 사용되지 않습니다.

-import org.springframework.http.HttpStatusCode;

96-98: 도메인 예외 대신 IllegalArgumentException 사용.

PR 목표에 따르면 공통 예외 처리를 적용하는 것인데, 여기서는 IllegalArgumentException을 사용하고 있습니다. 다른 인증 관련 예외들처럼 도메인별 예외(예: InvalidTokenException)를 사용하는 것이 일관성 있습니다.

 if (refreshToken == null) {
-    throw new IllegalArgumentException("Refresh 토큰이 없습니다.");
+    throw new InvalidTokenException("Refresh 토큰이 없습니다.");
 }

102-108: 토큰 재발급에 201 CREATED 사용.

토큰 재발급은 새로운 리소스 생성이 아니라 기존 토큰의 갱신입니다. 200 OK가 더 적절한 상태 코드입니다.

 return ResponseEntity
-    .status(HttpStatus.CREATED)
+    .status(HttpStatus.OK)
     .body(ApiResponse.success(
-        201,
+        200,
         true,
         response
     ));
src/main/java/team/wego/wegobackend/common/security/JwtAuthenticationFilter.java (1)

75-75: 코드 스타일: 연산자 사이 공백 누락.

|UserNotFoundException 앞에 공백이 없습니다.

-} catch (ExpiredTokenException | InvalidTokenException |UserNotFoundException e) {
+} catch (ExpiredTokenException | InvalidTokenException | UserNotFoundException e) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc9a74c and 104dd42.

📒 Files selected for processing (21)
  • build.gradle (1 hunks)
  • src/main/java/team/wego/wegobackend/auth/application/AuthService.java (4 hunks)
  • src/main/java/team/wego/wegobackend/auth/exception/DeletedUserException.java (1 hunks)
  • src/main/java/team/wego/wegobackend/auth/exception/InvalidPasswordException.java (1 hunks)
  • src/main/java/team/wego/wegobackend/auth/exception/UserAlreadyExistsException.java (1 hunks)
  • src/main/java/team/wego/wegobackend/auth/exception/UserNotFoundException.java (1 hunks)
  • src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java (3 hunks)
  • src/main/java/team/wego/wegobackend/common/exception/AppErrorCode.java (1 hunks)
  • src/main/java/team/wego/wegobackend/common/exception/GlobalExceptionHandler.java (2 hunks)
  • src/main/java/team/wego/wegobackend/common/response/ApiResponse.java (1 hunks)
  • src/main/java/team/wego/wegobackend/common/security/CustomUserDetails.java (1 hunks)
  • src/main/java/team/wego/wegobackend/common/security/CustomUserDetailsService.java (2 hunks)
  • src/main/java/team/wego/wegobackend/common/security/JwtAuthenticationFilter.java (4 hunks)
  • src/main/java/team/wego/wegobackend/common/security/SecurityConfig.java (1 hunks)
  • src/main/java/team/wego/wegobackend/common/security/exception/ExpiredTokenException.java (1 hunks)
  • src/main/java/team/wego/wegobackend/common/security/exception/InvalidTokenException.java (1 hunks)
  • src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java (5 hunks)
  • src/main/java/team/wego/wegobackend/image/presentation/ImageController.java (4 hunks)
  • src/main/java/team/wego/wegobackend/user/presentation/UserController.java (2 hunks)
  • src/main/resources/application.yml (2 hunks)
  • src/test/http/auth/auth-api.http (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/team/wego/wegobackend/auth/application/AuthService.java (5)
src/main/java/team/wego/wegobackend/auth/exception/DeletedUserException.java (1)
  • DeletedUserException (6-11)
src/main/java/team/wego/wegobackend/auth/exception/InvalidPasswordException.java (1)
  • InvalidPasswordException (6-11)
src/main/java/team/wego/wegobackend/auth/exception/UserAlreadyExistsException.java (1)
  • UserAlreadyExistsException (6-11)
src/main/java/team/wego/wegobackend/auth/exception/UserNotFoundException.java (1)
  • UserNotFoundException (6-11)
src/main/java/team/wego/wegobackend/common/security/exception/ExpiredTokenException.java (1)
  • ExpiredTokenException (5-10)
src/main/java/team/wego/wegobackend/common/security/CustomUserDetailsService.java (1)
src/main/java/team/wego/wegobackend/auth/exception/UserNotFoundException.java (1)
  • UserNotFoundException (6-11)
🪛 ast-grep (0.40.0)
src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java

[warning] 58-58: The application does not appear to verify inbound requests which can lead to a Cross-site request forgery (CSRF) vulnerability. If the application uses cookie-based authentication, an attacker can trick users into sending authenticated HTTP requests without their knowledge from any arbitrary domain they visit. To prevent this vulnerability start by identifying if the framework or library leveraged has built-in features or offers plugins for CSRF protection. CSRF tokens should be unique and securely random. The Synchronizer Token or Double Submit Cookie patterns with defense-in-depth mechanisms such as the sameSite cookie flag can help prevent CSRF. For more information, see: [Cross-site request forgery prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Req\ uest_Forgery_Prevention_Cheat_Sheet.html).
Context: response.addCookie(createRefreshTokenCookie(loginResponse.getRefreshToken()));
Note: [CWE-352] Cross-Site Request Forgery (CSRF). [REFERENCES]
- https://stackoverflow.com/questions/42717210/samesite-cookie-in-java-application

(cookie-missing-samesite-java)


[warning] 79-79: The application does not appear to verify inbound requests which can lead to a Cross-site request forgery (CSRF) vulnerability. If the application uses cookie-based authentication, an attacker can trick users into sending authenticated HTTP requests without their knowledge from any arbitrary domain they visit. To prevent this vulnerability start by identifying if the framework or library leveraged has built-in features or offers plugins for CSRF protection. CSRF tokens should be unique and securely random. The Synchronizer Token or Double Submit Cookie patterns with defense-in-depth mechanisms such as the sameSite cookie flag can help prevent CSRF. For more information, see: [Cross-site request forgery prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Req\ uest_Forgery_Prevention_Cheat_Sheet.html).
Context: response.addCookie(deleteCookie);
Note: [CWE-352] Cross-Site Request Forgery (CSRF). [REFERENCES]
- https://stackoverflow.com/questions/42717210/samesite-cookie-in-java-application

(cookie-missing-samesite-java)


[warning] 58-58: A cookie was detected without setting the 'HttpOnly' flag. The 'HttpOnly' flag for cookies instructs the browser to forbid client-side scripts from reading the cookie. Set the 'HttpOnly' flag by calling 'cookie.setHttpOnly(true);
Context: response.addCookie(createRefreshTokenCookie(loginResponse.getRefreshToken()));
Note: [CWE-1004] Sensitive Cookie Without 'HttpOnly' Flag. [REFERENCES]
- https://owasp.org/www-community/HttpOnly

(cookie-missing-httponly-java)


[warning] 79-79: A cookie was detected without setting the 'secure' flag. The 'secure' flag for cookies prevents the client from transmitting the cookie over insecure channels such as HTTP. Set the 'secure' flag by calling '.setSecure(true);'.
Context: response.addCookie(deleteCookie);
Note: [CWE-614] Sensitive Cookie in HTTPS Session Without 'Secure' Attribute. [REFERENCES]
- https://owasp.org/www-community/controls/SecureCookieAttribute

(cookie-missing-secure-flag-java)

🔇 Additional comments (15)
src/main/resources/application.yml (1)

5-6: 프로파일 활성화 비활성화의 영향 확인 필요

Spring 프로파일 활성화를 주석 처리하면 더 이상 local 프로파일이 자동으로 활성화되지 않습니다. 이 변경이 의도된 것이라면, 배포 환경에서 spring.profiles.active 환경 변수를 올바르게 설정하여 필요한 프로파일이 활성화되는지 확인하시기 바랍니다. (예: dev, prod 환경에서 해당 프로파일 명시)

build.gradle (1)

44-45: H2가 테스트 환경에서 사용 불가할 수 있습니다.

developmentOnlybootRun 태스크에서만 H2를 포함시키며, 테스트 클래스패스에는 포함되지 않습니다. 통합 테스트에서 H2를 사용한다면 테스트가 실패할 수 있습니다.

테스트에서도 H2가 필요하다면 다음과 같이 수정하세요:

    // H2 Database
-   developmentOnly 'com.h2database:h2'
+   developmentOnly 'com.h2database:h2'
+   testRuntimeOnly 'com.h2database:h2'
src/main/java/team/wego/wegobackend/auth/exception/UserAlreadyExistsException.java (1)

1-11: LGTM!

도메인 예외 패턴을 잘 따르고 있으며, UserNotFoundException 등 다른 예외 클래스와 일관된 구조입니다.

src/main/java/team/wego/wegobackend/common/exception/GlobalExceptionHandler.java (1)

25-25: RFC 7807 표준에 맞춘 변경입니다.

about:blank는 RFC 7807에서 권장하는 기본값으로, 각 오류 타입에 대한 문서 URL이 없을 때 사용하기 적절합니다.

src/test/http/auth/auth-api.http (2)

22-22: 올바른 응답 구조 반영입니다.

새로운 ApiResponse 구조에 맞춰 data.accessToken으로 토큰을 추출하도록 변경되었습니다.


33-50: 유용한 테스트 케이스가 추가되었습니다.

토큰이 없는 경우와 잘못된 토큰을 사용하는 경우에 대한 테스트가 추가되어 인증 오류 시나리오를 포괄적으로 검증할 수 있습니다.

src/main/java/team/wego/wegobackend/auth/exception/DeletedUserException.java (1)

6-10: 올바른 예외 구현입니다.

AppException을 확장하고 AppErrorCode.DELETED_USER를 사용하여 일관된 예외 처리 구조를 따릅니다.

src/main/java/team/wego/wegobackend/auth/exception/UserNotFoundException.java (1)

6-10: 올바른 예외 구현입니다.

AppException을 확장하고 AppErrorCode.USER_NOT_FOUND를 사용하여 일관된 예외 처리 구조를 따릅니다.

src/main/java/team/wego/wegobackend/auth/exception/InvalidPasswordException.java (1)

6-10: 올바른 예외 구현입니다.

AppException을 확장하고 AppErrorCode.INVALID_PASSWORD_VALUE를 사용하여 일관된 예외 처리 구조를 따릅니다.

src/main/java/team/wego/wegobackend/image/presentation/ImageController.java (1)

42-42: 새로운 ApiResponse API를 올바르게 적용했습니다.

모든 업로드 엔드포인트가 명시적인 HTTP 201 상태 코드를 사용하도록 업데이트되었으며, 이는 생성 작업에 적합합니다. 변경 사항이 일관되게 적용되었습니다.

Also applies to: 60-60, 82-82, 113-113

src/main/java/team/wego/wegobackend/auth/application/AuthService.java (1)

42-56: 도메인 예외 적용 - LGTM!

회원가입, 로그인, 토큰 재발급 흐름에서 도메인별 예외(UserAlreadyExistsException, UserNotFoundException, InvalidPasswordException, DeletedUserException, ExpiredTokenException)로 일관성 있게 변경되었습니다. 이를 통해 예외 처리가 명확해지고 공통 응답 형식과 잘 통합됩니다.

Also applies to: 87-110

src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java (1)

105-124: 예외 기반 토큰 검증으로 개선 - LGTM!

토큰 검증 실패 시 false 반환 대신 도메인 예외(ExpiredTokenException, InvalidTokenException)를 던지도록 변경되어, 호출자가 실패 원인을 명확히 알 수 있고 공통 예외 처리와 일관성이 유지됩니다.

src/main/java/team/wego/wegobackend/common/security/JwtAuthenticationFilter.java (3)

37-42: 공개 엔드포인트 필터 스킵 로직 추가.

shouldNotFilter를 통해 공개 패턴에 대해 필터를 건너뛰는 로직이 잘 구현되어 있습니다.


68-70: 인증 성공 시 조기 반환.

인증 성공 후 filterChain.doFilter를 호출하고 즉시 반환하여 에러 처리 로직으로 진입하지 않도록 한 것은 좋은 접근입니다.


44-46: ServletException is missing from the throws clause but the code is valid Java.

The parent class OncePerRequestFilter declares throws ServletException, IOException, but this implementation only declares throws IOException. While Java allows an overriding method to declare fewer checked exceptions than the parent (a subset is permitted), removing ServletException differs from the parent signature. If the try-catch block handles ServletException internally, this is acceptable; however, if this method could propagate ServletException to the caller, it should be declared.

Comment on lines +82 to +87
return ResponseEntity
.status(HttpStatus.NO_CONTENT)
.body(ApiResponse.success(
204,
true
));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

204 NO_CONTENT 응답에 body 포함.

HTTP 204는 응답 본문이 없어야 합니다. ApiResponse.success()로 body를 포함하면 일부 클라이언트에서 문제가 발생할 수 있습니다.

-return ResponseEntity
-    .status(HttpStatus.NO_CONTENT)
-    .body(ApiResponse.success(
-        204,
-        true
-    ));
+return ResponseEntity
+    .status(HttpStatus.NO_CONTENT)
+    .build();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return ResponseEntity
.status(HttpStatus.NO_CONTENT)
.body(ApiResponse.success(
204,
true
));
return ResponseEntity
.status(HttpStatus.NO_CONTENT)
.build();
🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/auth/presentation/AuthController.java
around lines 82-87, the controller is returning a 204 NO_CONTENT while including
a response body via ApiResponse.success(), which violates HTTP semantics and can
break clients; either remove the body and return
ResponseEntity.noContent().build() for a true 204 response, or if you need to
return ApiResponse.success(...) change the status to 200 (OK) and return that
body instead—update the method to follow one of these two options consistently.

Comment on lines +20 to 25
/**
* No Content (ex : 204)
* */
public static <T> ApiResponse<T> success(int status, boolean isSuccess) {
return new ApiResponse<>(status, true, null);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

isSuccess 파라미터가 무시되고 있습니다.

메서드 시그니처에 isSuccess 파라미터가 있지만, 실제로는 항상 true로 하드코딩되어 있습니다. 이는 혼란을 야기할 수 있습니다.

    public static <T> ApiResponse<T> success(int status, boolean isSuccess) {
-       return new ApiResponse<>(status, true, null);
+       return new ApiResponse<>(status, isSuccess, null);
    }

또는 파라미터가 불필요하다면 제거하세요:

-   public static <T> ApiResponse<T> success(int status, boolean isSuccess) {
+   public static <T> ApiResponse<T> successNoContent(int status) {
        return new ApiResponse<>(status, true, null);
    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* No Content (ex : 204)
* */
public static <T> ApiResponse<T> success(int status, boolean isSuccess) {
return new ApiResponse<>(status, true, null);
}
/**
* No Content (ex : 204)
* */
public static <T> ApiResponse<T> success(int status, boolean isSuccess) {
return new ApiResponse<>(status, isSuccess, null);
}
🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/common/response/ApiResponse.java around
lines 20 to 25, the method success(int status, boolean isSuccess) ignores the
isSuccess parameter and always passes true to the ApiResponse constructor;
either use the isSuccess parameter when constructing the ApiResponse (pass
isSuccess instead of true) or remove the isSuccess parameter from the method
signature and any callers, and adjust javadoc accordingly so the method behavior
matches its signature.

Comment on lines +27 to 29
public static <T> ApiResponse<T> error(int status, String message) {
return new ApiResponse<>(status, false, null);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

message 파라미터가 사용되지 않습니다.

error 메서드에서 message 파라미터를 받지만 응답에 포함되지 않습니다. 에러 응답에서 메시지 정보가 손실됩니다.

에러 메시지를 data 필드에 담거나, 별도의 message 필드를 record에 추가하는 것을 고려하세요:

    public static <T> ApiResponse<T> error(int status, String message) {
-       return new ApiResponse<>(status, false, null);
+       return new ApiResponse<>(status, false, (T) message);
    }

또는 ApiResponse record에 message 필드를 추가하세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/common/response/ApiResponse.java around
lines 27-29, the error(int status, String message) method accepts a message but
doesn't include it in the response; update the ApiResponse record to carry a
message field (e.g., add String message as a component), update all
constructors/factory methods to set that field, and change this error(...) to
return a new ApiResponse<>(status, false, null, message) (or equivalent) so the
passed message is included; ensure any callers or JSON serialization mappings
are updated to reflect the new field.

Comment on lines +27 to +40
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}

@Override
public String getUsername() {
return null;
}

@Override
public String getPassword() {
return null;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: UserDetails 메서드가 null을 반환합니다

생성자에서 authoritiesemail을 올바르게 초기화하지만, getAuthorities()getUsername() 메서드가 null을 반환합니다. 이로 인해 Spring Security 인증/인가 과정에서 심각한 오류가 발생합니다:

  1. getAuthorities(): 권한 검사 시 NPE 또는 인가 실패
  2. getUsername(): 인증 주체 식별 실패
 @Override
 public Collection<? extends GrantedAuthority> getAuthorities() {
-    return null;
+    return authorities;
 }

 @Override
 public String getUsername() {
-    return null;
+    return email;
 }

 @Override
 public String getPassword() {
-    return null;
+    return null; // 의도적으로 null 반환 시 주석 추가 권장
 }

getPassword()가 의도적으로 null을 반환하는 경우, 이유를 명시하는 주석을 추가하세요.

🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/common/security/CustomUserDetails.java
around lines 27-40, getAuthorities() and getUsername() currently return null
despite authorities and email being initialized in the constructor; replace
those null returns to return the stored authorities collection and the stored
email respectively so Spring Security can perform authentication/authorization
correctly, and if getPassword() is intentionally returning null, add a clear
comment explaining why (e.g., using token-based auth or credentials not stored
here) otherwise return the stored password field.

Comment on lines +5 to +9
public class InvalidTokenException extends RuntimeException {

public InvalidTokenException() {
super(AppErrorCode.INVALID_TOKEN.getMessageTemplate());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

예외 계층 구조의 일관성을 유지하세요.

이 예외는 RuntimeException을 확장하지만, 같은 PR에서 추가된 다른 인증 관련 예외들(UserNotFoundException, DeletedUserException, InvalidPasswordException)은 모두 AppException을 확장합니다. 일관성을 위해 AppException을 확장하고 AppErrorCode.INVALID_TOKEN을 생성자에 전달해야 합니다.

다음 diff를 적용하세요:

 package team.wego.wegobackend.common.security.exception;
 
 import team.wego.wegobackend.common.exception.AppErrorCode;
+import team.wego.wegobackend.common.exception.AppException;
 
-public class InvalidTokenException extends RuntimeException {
+public class InvalidTokenException extends AppException {
 
     public InvalidTokenException() {
-        super(AppErrorCode.INVALID_TOKEN.getMessageTemplate());
+        super(AppErrorCode.INVALID_TOKEN);
     }
 }
🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/common/security/exception/InvalidTokenException.java
around lines 5-9, the class currently extends RuntimeException but should extend
AppException for consistency with other auth exceptions; change the superclass
to AppException and modify the constructor to call
super(AppErrorCode.INVALID_TOKEN) so the AppErrorCode is passed to the
AppException base class.

Comment on lines +39 to +40
private final ObjectMapper objectMapper;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for ObjectMapper usage in the JwtTokenProvider file and related contexts
rg -n "objectMapper" --type java -A 2 -B 2

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 1719


🏁 Script executed:

cat src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 5157


Remove the unused ObjectMapper field

The objectMapper field is injected via @RequiredArgsConstructor but is never used anywhere in the JwtTokenProvider class. Remove the field declaration and the import statement if it's not needed elsewhere.

🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java
around lines 39-40, the injected private final ObjectMapper objectMapper field
is unused; remove the field declaration and also remove the corresponding import
(com.fasterxml.jackson.databind.ObjectMapper) if it's not used elsewhere in the
file so the generated constructor and imports stay consistent.

public boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token);
log.debug("네 되네요");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

디버그 로그 메시지 정리 필요

"네 되네요"는 개발 중 작성된 비공식적인 디버그 메시지입니다. 프로덕션 코드에서는 제거하거나 의미 있는 메시지로 변경하세요.

-log.debug("네 되네요");
+log.debug("JWT token validation successful");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log.debug("네 되네요");
log.debug("JWT token validation successful");
🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/common/security/jwt/JwtTokenProvider.java
around line 108, the debug log "네 되네요" is an informal development message;
remove it or replace it with a meaningful, contextual message (e.g., describe
the action/state being logged such as "JWT 토큰 생성 성공" or "JWT 검증 성공/실패") and
ensure the appropriate log level (debug/info/error) is used; update the log call
accordingly and keep messages in English or Korean consistently with project
conventions.

defer-datasource-initialization: true
hibernate:
ddl-auto: none
ddl-auto: update
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the application.yml file
find . -name "application.yml" -o -name "application-*.yml" | head -20

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 106


🏁 Script executed:

# Read the application.yml file to see the current configuration
cat -n ./src/main/resources/application.yml

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 2575


🏁 Script executed:

# Check for any deployment configurations or CI/CD files that might handle environment-specific settings
find . -maxdepth 2 -type f \( -name "*.yml" -o -name "*.yaml" -o -name "*.env*" -o -name "Dockerfile*" -o -name ".gitlab-ci.yml" -o -name ".github" \) 2>/dev/null | grep -E "\.(yml|yaml|env)" | head -20

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 109


🏁 Script executed:

# Check docker-compose.yml for any environment variable configuration
cat -n ./docker-compose.yml

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 546


⚠️ 프로덕션 배포 시 위험: ddl-auto: update 설정 검토 필요

Hibernate의 ddl-autoupdate로 설정하면 엔티티 정의에 따라 데이터베이스 스키마를 자동으로 업데이트합니다. 이는 개발 환경에서는 유용하지만, 프로덕션 환경에서는 예상치 못한 스키마 변경으로 인한 데이터 손상이나 서비스 중단을 초래할 수 있습니다.

현재 환경별 설정 파일이 존재하지 않으며, 모든 환경에 동일하게 update 설정이 적용되는 상황입니다. 환경별로 다른 DDL 전략을 적용하시기를 권장합니다:

  • 개발(dev): update 또는 create-drop
  • 프로덕션(prod): validate 또는 none

다음 중 하나의 방식으로 해결하시기 바랍니다:

  • 프로파일별 설정 파일 분리: application-dev.yml, application-prod.yml 생성
  • 환경 변수로 제어: 이미 RDS 설정에서 환경 변수를 활용하고 있으므로, spring.jpa.hibernate.ddl-auto=${DDL_AUTO:validate} 형태로 구성
🤖 Prompt for AI Agents
In src/main/resources/application.yml around line 37, the setting "ddl-auto:
update" is unsafe for production; replace the single global update policy with
profile-specific configuration or an environment-controlled value: create
application-dev.yml (dev: update or create-drop) and application-prod.yml (prod:
validate or none), or change the main config to read ddl-auto from an
environment variable (e.g., DDL_AUTO with a safe default like validate) and
ensure your deployment/CI sets DDL_AUTO appropriately.

@Be-HinD Be-HinD merged commit 729c5df into main Dec 9, 2025
1 check was pending
@Be-HinD Be-HinD deleted the feat/auth branch December 9, 2025 13:49
@github-project-automation github-project-automation bot moved this from Backlog to Done in WeGo-Together Backend Dec 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant