Skip to content

[8 주차] 조항준 / Chapter 8. Spring Security - Security 구조, 폼 로그인 - #39

Open
cho-hj-dev wants to merge 5 commits into
UMC-AYU:mainfrom
cho-hj-dev:Aim-Chapter08
Open

[8 주차] 조항준 / Chapter 8. Spring Security - Security 구조, 폼 로그인#39
cho-hj-dev wants to merge 5 commits into
UMC-AYU:mainfrom
cho-hj-dev:Aim-Chapter08

Conversation

@cho-hj-dev

@cho-hj-dev cho-hj-dev commented May 18, 2026

Copy link
Copy Markdown
Contributor

🔗 Issue Number


📝 개요


🚀 주요 변경 사항


🖼️ 실행 결과 (Screenshots)

image

💬 고민 및 질문

CustomEntryPoint를 구현할 때, 분명 자바 코드 상에서는 ErrorStatus.UNAUTHORIZED라는 객체를 넘겨주도록 설계했습니다. 그런데 실제 Swagger 테스트 환경에서 401 에러를 터뜨려보니 클라이언트가 받는 JSON 창에는 "code": "COMMON401_1"이라는 완전히 다른 문자열이 찍히는 것을 확인했습니다. 찾아보니 자바 코드의 UNAUTHORIZED는 개발자의 가독성을 위한 추상화된 이름(Key)일 뿐이고, 실물 응답으로 나갈 때는 그 내부에 매핑된 실제 비즈니스 코드(Value)로 치환되어 나간다는 구동 원리라는데 제가 이해한게 맞는걸까요??

✅ 실습 체크리스트

  • 이론 학습을 완료 했나요?
  • 미션 요구사항을 이해했나요?
  • 미션을 완료 했나요?

⚙️ 환경 및 컨벤션 체크 (Final Check)

  • 디렉토리 구조 컨벤션을 지켰나요?
  • pr 제목을 컨벤션에 맞게 작성하였나요?
  • pr에 해당되는 이슈를 연결하였나요?
  • Assignees을 본인으로 설정했나요?
  • Reviewers을 설정 했나요?
  • 적절한 라벨을 설정하였나요?

@cho-hj-dev
cho-hj-dev requested review from a team and zldzldzz May 18, 2026 15:05
@cho-hj-dev cho-hj-dev self-assigned this May 18, 2026
@cho-hj-dev
cho-hj-dev requested review from m4ppy and soseongmin03 and removed request for a team May 18, 2026 15:05
@cho-hj-dev cho-hj-dev changed the title [8 주차] 조항 / Chapter 8. Spring Security - Security 구조, 폼 로그인 [8 주차] 조항준 / Chapter 8. Spring Security - Security 구조, 폼 로그인 May 19, 2026
Comment on lines +43 to +46
.exceptionHandling(exception -> exception
.authenticationEntryPoint(new CustomEntryPoint()) //401 에러 핸들러 매핑
.accessDeniedHandler(new CustomAccessDenied()) //403 에러 핸들러 매핑
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

위에서 CustomEntryPoint를 파라미터로 받고 있는 것으로 확인했는데 다시 new 로 새로운 객체를 받는 것은 불필요하다고 생각합니다.
그리고 제 개인적인 생각으로는 따로 객체를 생성하는 것이 아니라 Spring Bean으로 객체 주입을 하는 것이 좋아 보입니다

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

워크북 기반으로 구조를 잡고 예외처리를 하는 과정에서 생명주기, DI흐름을 생각하지 못했던 거 같습니다.
new 키워드를 제거하고, @requiredargsconstructor를 활용해 스프링 컨테이너가 직접 객체를 관리하고 주입하도록 수정하였습니다.
리뷰 감사합니다!

response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

ObjectMapper objectMapper = new ObjectMapper();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

objectMapper객체를 메서드 내에서 생성하면 메서드가 호출될 때마다 객체가 생성하게 됩니다.
이는 자원을 상대적으로 많이 소모하므로 이러한 방식이 아닌 메서드 밖에 private 객체로 생성을 하면 EntryPoint객체가 생성될 때 1회만 생성 되므로 이러한 코드 구성이 더 좋다고 생각합니다

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

리뷰해주신대로 ObjectMapper를 필드로 추출해서 빈으로 재사용하는 구조를 시도해보았는데, 시큐리티 필터 체인 초기화 시점과 ObjectMapper빈의 생성 라이프사이클 순서가 꼬이면서 애플리케이션 컨텍스트 구동 에러가 발생하였습니다. ..
이 부분은 제가 조금 더 살펴보고 수정하도록 하겠습니다..!!

//회원가입 API
@PostMapping("/signup")
public ApiResponse<MemberResponseDTO.joinResultDTO>join(
public ApiResponse<String>join(

@m4ppy m4ppy May 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

다른 API 들 처럼 회원가입도 커스텀 DTO 를 만들어서 결과로 보내주는게 알맞은 방향인 것 같습니다.

@Email(message = "이메일 형식이 올바르지 않습니다.")
String email;

String password;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

다른 필드들처럼 password 도 베리데이션 어노테이션을 붙여주어서 통일시키는 방향이 좋을 것 같습니다.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Status enum 을 구현만하고 사용을 안하고 있습니다.

@@ -0,0 +1,87 @@
package com.aim.umc10th.global.config.config;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

현재 global 패키지 하위에 config 패키지가 두 개로 중복되어 있습니다.

@zldzldzz

zldzldzz commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

요청 데이터를 버리고 하드코딩한 값이 들어가고 있습니다 (MemberService 52~53번 라인)

  .birth(java.time.LocalDate.now())   // request.getBirthDate() 무시하고 오늘 날짜 저장
  .phoneNumber("010-1234-5678")        // 하드코딩

어노테이션 수정 추천

  • 전화 번호의 어노테이션을 @NotBlank가 아닌 패턴(@Pattern)을 사용하는 것도 좋을 것 같아요
 @Pattern(                                                                                                                                                                                                                         
      regexp = "^01[016789]-?\\d{3,4}-?\\d{4}$",                                                                                                                                                                                    
      message = "전화번호 형식이 올바르지 않습니다."                                                                                                                                                                                
  )  
  • 비밀 번호의 어노테이션을 @NotBlank가 아닌 @Size(min = 8, message = "비밀번호는 8자 이상이어야 합니다.")와 같이 최소 길이를 지정해도 좋을 것 같아요.
// 추천 코드
  @NotBlank(message = "전화번호는 필수입니다.")
  @Pattern(regexp = "^01[016789]-?\\d{3,4}-?\\d{4}$", message = "전화번호 형식이 올바르지 않습니다.")
  private String phone;

  @NotBlank(message = "비밀번호는 필수입니다.")
  @Size(min = 8, message = "비밀번호는 8자 이상이어야 합니다.")
  private String password;

이메일 중복 검사가 없습니다

  • email에 unique = true(Member 32번 라인)가 걸려 있고 findByEmail도 만들어 두셨는데, 가입 시 중복 체크를 하지 않습니다.
  • 중복 이메일이면 DB 제약 위반 예외(500)가 그대로 터집니다. findByEmail로 미리 확인 후 의미 있는 예외를 던지는 것이 좋습니다.
 // MemberRepository
  boolean existsByEmail(String email);

  // MemberService.createUser 앞부분
  if (memberRepository.existsByEmail(request.getEmail())) {
      throw new MemberException(ErrorCode.EMAIL_ALREADY_EXISTS); // 코드 추가 필요
  }

MemberConverter의 인스터스화 방지하는 것도 좋을 것 같아요.

private MemberConverter() {
        // 혹시나 클래스 내부나 리플렉션으로 호출하는 것도 막기 위해 예외를 던지기도 합니다.
        throw new IllegalStateException("Utility class");
    }

Member:35의 //(8주차 미션)위에 이메일과 함께 로그인을 위해 추가 같은 작업 메모성 주석도 머지 전에 덜어내면 깔끔할 것 같아요.

MemberRepository의 findById 오버라이드는 제거해도 될 것 같아요

  • JpaRepository<Member, Long>가 이미 동일 시그니처의 findById(Long)를 제공해서, MemberRepository:11의 선언은 중복이라 지워도 동작이 같아요.

CustomAccessDenied의 ObjectMapper import가 다르게 되었습니다 (11번 라인)

  • CustomEntryPoint는 com.fasterxml.jackson.databind.ObjectMapper(Jackson 2.x)를 쓰는데 여기만 다릅니다.
  • CustomEntryPoint와 동일하게 com.fasterxml.jackson.databind.ObjectMapper로 맞추세요.

GeneralErrorCode.NOT_FOUND의 코드 값이 잘못되었습니다
NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON401_1", ...) 401 코드가 404에 붙어있음 UNAUTHORIZED와 코드 문자열이 겹칩니다. COMMON404_1 등으로 수정 필요합니다.

createUser가 "OK" 문자열만 반환합니다. 생성된 memberId나 응답 DTO를 돌려주면 클라이언트가 활용하기 좋습니다.

@Transactional을 jakarta.transaction에서 import했는데, 보통 Spring에서는 org.springframework.transaction.annotation.Transactional을 추천합니다.
롤백 규칙 등 기능이 더 많아요

미구현

  • 선호 음식 정보가 회원가입 과정에 연결하는 과정이 없는 것 같아요

    • MemberRequestDTO.joinDTO(40번 라인)에서 List preferCategory를 받고 @NotEmpty로 검증까지 하지만, MemberService.createUser(35~57번 라인)에서 이 값을 전혀 사용하지 않습니다.
  • 아래 이미지 같이 약관 동의 기능을 추가해도 좋을 것 같아요

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chapter08_Spring Security - Security 구조, 폼 로그인

4 participants