[Suhhee] week9 미션#108
Conversation
💡 Codex Review
카카오 계정에서
ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
kjhh2605
left a comment
There was a problem hiding this comment.
[키워드 조사]
이번 PR에는 Week9 keyword_summary 신규 파일이 포함되어 있지 않습니다. 코드 변경 범위가 JWT, OAuth2 로그인, stateless 세션 정책과 직접 연결되어 있으므로 Access Token/Refresh Token의 역할 분리, OAuth2 authorization code 흐름, JWT claim 설계, Spring Security 필터 체인과 ExceptionTranslationFilter의 경계를 추가로 정리하는 것을 권장합니다.
[코드 리뷰]
일반 로그인과 OAuth 로그인 모두 JWT 발급 흐름으로 연결하고, 인증된 사용자를 @AuthenticationPrincipal 기반으로 조회하려는 방향이 좋습니다. PasswordEncoder, CustomUserDetailsService, OAuthSuccessHandler처럼 책임을 나누어 구성한 점도 적절합니다. 다만 stateless 설정과 OAuth2 로그인 흐름의 세션 의존성, 로그인 DTO 검증, JWT 필터의 예외 처리 범위를 더 명확히 해야 인증/인가 흐름을 안정적으로 설명할 수 있습니다.
| .authorizeHttpRequests(requests -> requests | ||
| .requestMatchers(allowUris).permitAll() // Public API는 허용 | ||
| .anyRequest().authenticated() // 그 외의 Private API는 인증 필요 | ||
| .sessionManagement(session -> session |
There was a problem hiding this comment.
SessionCreationPolicy.STATELESS를 사용하면서 oauth2Login을 함께 활성화하면 OAuth2 authorization request 저장 방식이 함께 정리되어야 합니다. 기본 구현은 인가 요청 상태를 세션에 저장하는 흐름을 사용할 수 있으므로, 완전한 stateless JWT 구조를 의도했다면 쿠키 기반 AuthorizationRequestRepository를 두거나 OAuth 로그인 구간만 세션을 허용하는 정책을 명확히 분리하는 것을 권장합니다.
| @Operation(summary = "로그인 API", description = "이메일과 비밀번호로 로그인하여 JWT 토큰을 발급받습니다.") | ||
| @PostMapping("/auth/login") | ||
| public ApiResponse<UserResponseDto.LoginResultDto> login( | ||
| @RequestBody UserRequestDto.LoginDto requestDto |
There was a problem hiding this comment.
로그인 요청 DTO를 그대로 받으면 이메일 형식, 비밀번호 공백 여부 같은 입력 계약이 컨트롤러 경계에서 검증되지 않습니다. LoginDto에 Bean Validation을 추가하고 컨트롤러에서 @Valid를 적용하면 인증 서비스는 검증된 값으로 비밀번호 비교와 토큰 발급 책임에 집중할 수 있습니다.
| // 다음 필터로 이동 | ||
| filterChain.doFilter(request, response); | ||
|
|
||
| } catch (Exception e) { |
There was a problem hiding this comment.
현재 filterChain.doFilter까지 try 범위에 포함되어 있어 JWT 검증 실패가 아닌 하위 필터·컨트롤러 예외도 모두 401 응답으로 변환될 수 있습니다. JWT 파싱/인증 객체 생성 과정의 예외만 좁게 처리하고, 나머지 예외는 Spring의 예외 처리 흐름으로 전파되도록 분리하는 것을 권장합니다.
| > 1. 클라이언트가 로그인에 성공하면 서버는 사용자 식별 정보와 권한 등을 포함한 데이터(Payload)에 디지털 서명을 하여 토큰을 생성함 | ||
| > 2. 서버는 이 토큰을 클라이언트에게 반환하고 서버는 이 토큰을 저장하지 않음 | ||
| > 3. 클라이언트는 토큰을 로컬 스토리지나 쿠키에 저장하고 이후 요청마다 HTTP 헤더(`Authorization`)에 토큰을 담아 전송한다 | ||
| > 4. 서버는 전달받은 토큰의 서명을 비밀키로 복호화하여 위변조 여부를 검증한 뒤 서명이 유효하면 토큰 내부의 데이터를 신뢰하고 요청을 처리해준다 |
There was a problem hiding this comment.
JWT 서명 검증은 비밀키로 ‘복호화’하는 과정이라기보다, Header/Payload와 비밀키로 서명을 다시 계산해 Signature와 비교하는 과정으로 이해하는 것이 정확합니다. JWT 자체는 기본적으로 암호화가 아니라 Base64URL 인코딩과 서명으로 구성되므로, Payload에 민감 정보를 넣지 않는 이유까지 함께 정리하는 것을 권장합니다.
| > 4. 서버는 전달받은 토큰의 서명을 비밀키로 복호화하여 위변조 여부를 검증한 뒤 서명이 유효하면 토큰 내부의 데이터를 신뢰하고 요청을 처리해준다 | ||
| > * **특징:** | ||
| > * **Stateless:** 서버는 클라이언트의 상태를 저장하지 않고 토큰 자체에 인증 정보를 포함한다 | ||
| > * **확장성 우수:** 서버가 상태를 저장하지 않으므로 서버를 무한정 늘려도 인증 처리에 문제가 없다는 이점이 있다 |
There was a problem hiding this comment.
토큰 방식이 서버 확장에 유리한 점은 맞지만, ‘무한정 늘려도 문제가 없음’은 운영 조건을 지나치게 단순화한 표현입니다. 서명 키 관리, 토큰 만료/재발급 정책, 블랙리스트 저장소, Refresh Token 저장 전략이 함께 맞아야 확장성이 안정적으로 확보됩니다. stateless 인증의 장점과 이를 보완하기 위해 필요한 상태 저장 요소를 구분해 정리하는 것을 권장합니다.
| "/login/**", | ||
| "/passkey/**", | ||
| "/auth/**", | ||
| "/webauthn/**" |
There was a problem hiding this comment.
WebAuthn 등록/인증 엔드포인트를 모두 permitAll로 열면 패스키 등록이 기존 사용자 인증 상태나 가입 흐름과 분리되어 실행될 수 있습니다. Passkey는 credential을 특정 사용자 계정에 안전하게 연결해야 하므로, 등록 옵션/검증 API는 인증된 사용자 또는 명확한 회원가입 세션에만 허용하고 로그인 검증 API와 권한 경계를 분리하는 것을 권장합니다. 다음 학습으로 WebAuthn registration ceremony와 authentication ceremony의 책임 차이를 함께 정리하면 좋습니다.
|
|
||
| @GetMapping("/test") | ||
| public String test() { | ||
| return "/auth/index.html"; |
There was a problem hiding this comment.
@Controller에서 정적 파일 경로를 문자열로 반환하면 ViewResolver 설정에 따라 템플릿 뷰 이름으로 해석될 수 있습니다. 정적 리소스 페이지로 명확히 이동하려면 redirect:/auth/index.html 또는 forward:/auth/index.html처럼 의도를 드러내는 반환값을 사용하는 것을 권장합니다. MVC에서 컨트롤러가 뷰 이름을 반환하는 경우와 정적 리소스를 직접 제공하는 경우의 차이를 함께 확인하면 좋습니다.
🔗 연관 이슈
🛠 작업 내용
🖼 스크린샷 (선택)
👀 리뷰 요구사항 (선택)
🤖 AI 활용
💬 나의 프롬프트
내가 작성한 코드에서 잘못 작성된 부분, 로직이 잘못된 부분이 있으면 해당 부분에 대해서 알려줘
🧠 AI 응답
잘못 작성된 로직에 대한 수정사항 + 오타 수정
✅ 내가 최종 선택한 방법 (이유)
💡 나만의 Tip (선택)