[Joonseok] Week8 미션#82
Conversation
… 선언, form 기반 login-logout 설정 명시
…serDetailsService의 자식으로 선언
…edHandler를 구현한 Custom 객체 선언
…응답을 JSON 템플릿으로 전달하기 위한 AuthenticationEntryPoint의 구현체 CustomEntryPoint 객체 작성
…uthorized 예외 핸들러를 SecurityConfig에 작성
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bdb023fa9a
ℹ️ 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".
| "/swagger-ui/**", | ||
| "/swagger-resources/**", | ||
| "/v3/api-docs/**", | ||
| "/auth/**" // sign-up, login request allow |
There was a problem hiding this comment.
/sign-in 컨트롤러가 기존 로그인 엔드포인트인데 허용 목록에는 /auth/**만 들어가 있어, 새 SecurityFilterChain의 anyRequest().authenticated() 때문에 로그인 요청 자체가 인증을 요구하게 됩니다. Spring Security에서는 인증 진입점은 반드시 permitAll() 대상이어야 하므로, 실제 매핑(/sign-in)을 allowUris에 추가하거나 컨트롤러 경로를 /auth/sign-in으로 맞추는 방식으로 개선하세요. 다음 학습 주제로는 requestMatchers의 경로 매칭과 인증/인가 필터 체인 흐름을 보면 좋습니다.
Useful? React with 👍 / 👎.
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public class CustomUserDetailsService implements UserDetailsService { |
There was a problem hiding this comment.
UserDetailsService를 스프링 빈으로 등록하세요
CustomUserDetailsService가 @Service나 @Bean으로 등록되지 않아 Spring Security가 DB 기반 사용자 조회 로직을 사용할 수 없습니다. 이 상태에서는 작성한 findByEmail/AuthUser 흐름이 인증 과정에 연결되지 않으므로, 클래스를 빈으로 등록하거나 SecurityConfig에서 명시적으로 UserDetailsService/AuthenticationProvider를 구성하세요. 다음 학습 주제로는 Spring Security의 UserDetailsService 자동 탐지와 AuthenticationProvider 구성 방식을 추천합니다.
Useful? React with 👍 / 👎.
kjhh2605
left a comment
There was a problem hiding this comment.
[키워드 조사]
이번 PR diff에는 신규 keyword_summary 문서가 포함되지 않아 문서 기반 개념 피드백은 확인하지 못했습니다. Spring Security 기본 흐름인 SecurityFilterChain, UserDetailsService, PasswordEncoder, CSRF, AuthenticationEntryPoint와 AccessDeniedHandler의 역할을 함께 정리하면 인증/인가 구조 이해가 높아집니다.
[코드 리뷰]
Spring Security 의존성 추가와 인증 실패/인가 실패 응답 래핑까지 시도한 점은 좋습니다. 다만 MVC 계층과 Spring Security가 실제로 연결되는 지점에서 Bean 등록, 권한 매핑, 로그인 성공 경로 같은 세부 설정을 점검해야 합니다. 특히 SecurityContext에 저장될 사용자 객체와 UserDetailsService가 프레임워크에 의해 사용되도록 구성하는 부분을 보완하면 인증 흐름의 완성도가 높아집니다.
|
|
||
| @Getter | ||
| @RequiredArgsConstructor | ||
| public class CustomUserDetailsService implements UserDetailsService { |
There was a problem hiding this comment.
CustomUserDetailsService가 UserDetailsService를 구현하지만 현재 @Service 또는 @Bean으로 등록되지 않아 Spring Security가 해당 구현체를 주입받아 사용하기 어렵습니다. 인증 책임을 담당하는 서비스 계층 객체이므로 @Service를 부여하거나 SecurityConfig에서 명시적으로 Bean으로 등록하는 방식을 권장합니다. 이 부분은 의존성 주입과 Spring Security 인증 흐름을 연결하는 핵심 지점입니다.
|
|
||
| @Override | ||
| public Collection<? extends GrantedAuthority> getAuthorities() { | ||
| return List.of(); |
There was a problem hiding this comment.
현재 권한 목록을 항상 빈 리스트로 반환하면 User.role 정보가 SecurityContext까지 전달되지 않아 역할 기반 인가를 확장하기 어렵습니다. Role을 SimpleGrantedAuthority로 변환해 반환하도록 구성하면 도메인 모델의 역할 책임과 Spring Security의 권한 모델이 자연스럽게 연결됩니다. hasRole, hasAuthority의 차이도 함께 학습하기를 권장합니다.
| .anyRequest().authenticated() | ||
| ) | ||
| .formLogin(form -> form | ||
| .defaultSuccessUrl("/swagger-ui/index-html", true) |
There was a problem hiding this comment.
로그인 성공 후 이동 경로가 /swagger-ui/index-html로 지정되어 있어 실제 Swagger UI 경로인 /swagger-ui/index.html과 맞지 않을 가능성이 높습니다. Security 설정은 인증 성공 후 MVC 라우팅 결과까지 직접 영향을 주므로, 실제 엔드포인트 경로와 일치하는지 확인이 필요합니다.
| private String email; | ||
|
|
||
| @Column(nullable = false) | ||
| private String password; |
There was a problem hiding this comment.
비밀번호 필드를 엔터티에 추가한 점은 인증 구현에 필요하지만, 저장 시점에는 반드시 PasswordEncoder를 거친 해시 값만 들어가도록 서비스 책임을 분리해야 합니다. 엔터티는 상태를 보관하고, 가입/비밀번호 변경 유스케이스에서 암호화 정책을 적용하는 구조가 계층 책임 분리에 더 적절합니다.
|
|
||
| </aside> | ||
|
|
||
| MVC 구조를 사용하여 동기적으로 통신하는 서버와 Webflux를 사용하여 비동기적 반응형 서버 모두 적용 가능하고, 서버를 안전하게 보호하는데에 편리한 기능을 제공합니다. |
There was a problem hiding this comment.
Spring Security와 Java 버전의 관계는 사용 중인 Spring Boot/Security 버전에 따라 달라집니다. 예를 들어 Spring Security 6 계열은 Java 17 기준으로 이해할 수 있지만, 모든 Spring Security가 Java 17 이상에서만 동작한다고 일반화하면 개념이 부정확해질 수 있습니다. 또한 보안 설정 파일이 필요 없다는 표현은 현재 SecurityConfig를 작성한 구조와도 충돌하므로, 자동 설정과 명시적 SecurityFilterChain 설정의 차이를 함께 정리하는 것을 권장합니다.
| </aside> | ||
|
|
||
| ### Authentication | ||
|
|
There was a problem hiding this comment.
인증은 사용자가 누구인지 확인하는 절차이고, 인가는 인증된 사용자가 특정 리소스나 기능에 접근할 권한을 가지는지 판단하는 절차입니다. 현재 설명은 인증을 ‘등록 기록’ 중심으로, 인가를 ‘발급된 리소스 접근 권한 검증’ 중심으로 표현하여 두 개념의 핵심 경계가 흐려질 수 있습니다. Authentication 객체, Principal, GrantedAuthority가 각각 어떤 역할을 맡는지 함께 정리하면 Spring Security 흐름을 이해하기 좋아집니다.
|
|
||
| 인가라는 의미로, 해당 사용자가 서버에서 발급한 리소스 접근 권한이 유효한지 검증하는 행위입니다. | ||
|
|
||
| ### 가장 일반적인 경우… |
There was a problem hiding this comment.
UsernamePasswordAuthenticationFilter는 주로 formLogin 기반 인증 흐름에서 동작하는 필터입니다. ID와 Password를 사용하더라도 JSON 로그인, JWT 발급, 커스텀 필터를 사용하는 구조에서는 다른 필터나 AuthenticationProvider 설계가 필요할 수 있습니다. 필터 체인에서 인증 요청이 AuthenticationManager와 AuthenticationProvider로 위임되는 흐름까지 함께 조사하는 것을 권장합니다.
| @NotBlank(message = "상세 주소 필드는 비어있을 수 없습니다.") | ||
| String detailAddress, | ||
|
|
||
| @NotEmpty(message = "선호 음식 배열은 비어있을 수 없습니다.") |
There was a problem hiding this comment.
foodList를 회원가입 필수 입력으로 받지만 현재 User 엔터티와 saveUser 저장 흐름에는 반영되지 않습니다. 필수 DTO 필드라면 선호 음식 매핑 엔터티까지 저장하거나, 이번 범위에서 저장하지 않을 값이라면 요청 DTO에서 분리하는 것을 권장합니다. DTO와 도메인 저장 책임이 맞아야 계층 간 계약이 명확해집니다.
| String phoneNum | ||
| ) { | ||
|
|
||
| public record Agree( |
There was a problem hiding this comment.
Agree 내부 필드가 모두 nullable Boolean이라 agree 객체만 존재하면 필수 약관 값이 빠져도 검증을 통과할 수 있습니다. 약관 동의는 회원가입 불변식에 가까우므로 각 필드에 @NotNull을 붙이고, 필수 동의 항목은 true 여부까지 서비스나 커스텀 validator에서 검증하는 것을 권장합니다.
| .build()) | ||
| .build(); | ||
|
|
||
| userRepository.save(user); |
There was a problem hiding this comment.
회원가입 저장 전에 이메일 중복 여부를 확인하는 흐름이 필요합니다. findByEmail이 이미 있으므로 중복 이메일이면 도메인 예외를 던지고, 엔터티의 email 컬럼에도 unique 제약을 함께 두는 것을 권장합니다. 서비스 검증과 DB 제약을 같이 두면 비즈니스 규칙과 데이터 무결성이 함께 보장됩니다.
🔗 연관 이슈
🛠 작업 내용
🖼 스크린샷 (선택)
👀 리뷰 요구사항 (선택)
🤖 AI 활용
💬 나의 프롬프트
🧠 AI 응답
✅ 내가 최종 선택한 방법 (이유)
💡 나만의 Tip (선택)