[Chanyeol] Week9 미션#101
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dc566d1435
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
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".
| filterChain.doFilter(request, response); | ||
| } catch (Exception e) { | ||
| BaseErrorCode code = GeneralErrorCode.UNAUTHORIZED; |
There was a problem hiding this comment.
filterChain.doFilter를 try-catch(Exception)로 감싸서 처리하면, JWT 검증과 무관한 컨트롤러/서비스 예외까지 모두 UNAUTHORIZED(401)로 바뀝니다. 이렇게 되면 원래의 비즈니스 에러 코드와 전역 예외 처리 흐름이 깨져 디버깅과 API 계약 학습이 어려워집니다. JWT 파싱/인증 단계에서 발생한 예외만 별도로 처리하고, 그 외 예외는 다시 던져서 @RestControllerAdvice가 처리하도록 분리해 주세요. 다음으로는 서블릿 필터 예외 전파와 Spring Security 예외 처리 체인(ExceptionTranslationFilter) 을 함께 학습하는 것을 권장합니다.
Useful? React with 👍 / 👎.
| String socialUid; | ||
| try { | ||
| providerId = SocialType.valueOf(userRequest.getClientRegistration().getRegistrationId().toUpperCase()); | ||
| socialUid = String.valueOf(oAuthMember.getAttribute("id")); |
There was a problem hiding this comment.
OAuth 식별자 누락을 문자열 "null"로 저장하지 마세요
소셜 응답에 id가 없을 때 String.valueOf(oAuthMember.getAttribute("id"))는 예외가 아니라 문자열 "null"을 반환하므로, 잘못된 계정이 생성되거나 여러 사용자가 같은 socialUid로 매칭될 수 있습니다. 소셜 로그인에서 socialUid는 계정 식별의 핵심이므로, 값이 비어 있거나 null이면 즉시 예외를 던지도록 검증 로직을 분리해 주세요. 다음으로는 OAuth 사용자 식별자 검증(필수 claim 검증)과 계정 매핑 규칙 을 학습하면 좋습니다.
Useful? React with 👍 / 👎.
kjhh2605
left a comment
There was a problem hiding this comment.
[키워드 조사]
세션과 토큰, Access Token과 Refresh Token, OAuth 1.0/2.0의 차이를 장단점 중심으로 정리한 점이 좋습니다. 특히 토큰 탈취, Refresh Token 관리, Scale-out 관점까지 함께 적어 인증 방식 선택 기준을 이해하려는 흐름이 보입니다. 다만 OAuth 2.0은 인증 자체보다 인가 프레임워크라는 관점, Authorization Code 흐름의 참여자 역할, 현재 코드에서 Refresh Token을 아직 발급하지 않는 범위를 함께 표시하면 정리의 정확성이 더 높아집니다.
[코드 리뷰]
세션 기반 로그인 핸들러를 제거하고 JWT 필터, OAuth2 사용자 서비스, 성공 핸들러, /me API를 연결한 방향은 Week9 학습 목표와 잘 맞습니다. Controller에서 @AuthenticationPrincipal로 현재 사용자를 받도록 바꾼 점도 이전의 static SecurityUtil 의존을 줄이는 좋은 개선입니다. 다음 단계에서는 JWT에 담는 권한 claim과 UserDetails 권한 모델을 일치시키고, OAuth 가입/연동 정책을 명확히 분리하며, 토큰 기반 인증 흐름에서 실제 DB 조회 여부와 stateless 개념을 코드 기준으로 설명할 필요가 있습니다.
|
|
||
| 대표적으로 **JWT(JSON Web Token)**가 많이 사용되며, 이 토큰 안에 사용자 ID, 권한, 만료시간 등의 정보를 직접 포함하고 있습니다. 클라이언트는 이 토큰을 안전한 곳(보통 localStorage 또는 HttpOnly 쿠키)에 저장했다가, 이후 API 요청 시 Authorization: Bearer <token> 헤더에 담아 서버로 보냅니다. | ||
|
|
||
| 서버는 토큰의 **서명을 검증**하기만 하면 되며, 별도로 사용자 정보를 DB에서 조회할 필요가 없습니다. 즉, **서버가 클라이언트의 상태를 유지하지 않는 Stateless(무상태)** 방식입니다. |
There was a problem hiding this comment.
JWT는 서명 검증만으로 토큰 자체의 무결성과 만료 여부를 확인할 수 있지만, 현재 구현처럼 JwtAuthFilter에서 이메일로 CustomUserDetailsService를 다시 호출하면 매 요청마다 DB 조회가 발생할 수 있습니다. 학습 정리에는 "JWT 방식은 서버 세션 저장소가 필요하지 않지만, 구현에 따라 사용자 상태 확인을 위해 DB 조회를 병행할 수 있습니다"처럼 구분해 두는 것을 권장합니다.
| Instant now = Instant.now(); | ||
|
|
||
| // 인가 정보 | ||
| String authorities = member.getAuthorities().stream() |
There was a problem hiding this comment.
AuthMember#getAuthorities()가 현재 빈 리스트를 반환하므로 role claim도 빈 문자열로 발급됩니다. 이후 hasRole, hasAuthority 같은 인가 규칙을 사용할 계획이라면 Member의 역할 값을 GrantedAuthority로 변환하는 위치를 정하고, JWT claim과 SecurityContext의 권한이 같은 기준을 사용하도록 맞추는 것을 권장합니다.
|
|
||
| // DB 저장: 있다면 그 데이터 가져오고 없으면 새로 저장 | ||
| Member member = memberRepository.findBySocialTypeAndSocialUidAndDeletedAtIsNull(providerId, socialUid) | ||
| .or(() -> memberRepository.findActiveByEmail(dto.email())) |
There was a problem hiding this comment.
소셜 고유 ID로 먼저 찾고 이메일로도 찾는 흐름은 기존 계정과 소셜 계정을 연결하려는 의도가 보입니다. 다만 이메일로 찾은 기존 회원에 socialType/socialUid를 저장하지 않으면 계정 연동 정책이 코드에 명확히 남지 않습니다. 일반 회원과 소셜 회원을 자동 연결할지, 별도 동의 후 연결할지 정책을 정하고 Service에서 그 책임을 분리하는 것을 권장합니다.
| Long currentMemberId = SecurityUtil.getCurrentMemberId(); | ||
| Long currentMemberId = authMember.getMember().getId(); | ||
|
|
||
| Long userMissionId = 1L; // 임시 값 |
There was a problem hiding this comment.
인증된 사용자 ID를 @AuthenticationPrincipal로 가져온 점은 적절합니다. 다만 userMissionId가 임시 상수로 남아 있으면 요청한 store/mission과 실제 리뷰 작성 대상의 관계가 Service 계층에서 검증되지 않습니다. 리뷰 생성 API의 입력 DTO나 경로에서 사용자 미션 식별자를 명확히 받고, 해당 미션이 현재 회원의 미션인지 검증하는 흐름으로 옮기는 것을 권장합니다.
🔗 연관 이슈
#100
🛠 작업 내용
JWT 토큰 구현 - 회원가입, 로그인 구현하고 마이페이지도 워크북과 같은 형식으로 개선
OAuth 관련 구현 - 카카오 OAuth 구현
🤖 AI 활용
💬 나의 프롬프트
🧠 AI 응답
✅ 내가 최종 선택한 방법 (이유)