Skip to content

[그리디] 김지우 Spring Core (배포) 7,8,9 단계 제출합니다. #178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: ji-woo-kim
Choose a base branch
from

Conversation

Ji-Woo-Kim
Copy link

안녕하세요, 태연님! 리뷰로는 처음 뵙네요, 잘부탁드립니다 😄

미션 진행 과정

✅ 1. JWT 로직 분리와 @configuration 적용 (7단계)

기존에 구현해둔 JWT 관련 로직을 roomescape 외부 패키지로 이동시키고, 요구사항에 맞춰 @Component가 아닌 @Configuration@Bean으로 JwtUtils를 등록시켰습니다.
또, 인증 로직을 컨트롤러에서 더 편리하게 사용하기 위해 @Login 커스텀 어노테이션을 만들었습니다. 컨트롤러 메서드의 파라미터에 @Login을 붙이면, ArgumentResolver가 HTTP 요청의 토큰을 자동으로 파싱하여 LoginMember 객체를 만들어 주입해주기 때문에 컨트롤러에서의 복잡한 파싱로직을 없앨 수 있었습니다.

✅ Profile을 이용한 데이터 로딩 분리 (8단계)

사용하던 data.sql을 제거하고 데이터베이스를 초기화 해주는 클래스를 구현하였습니다. @Profile을 이용해 운영 환경의 DataLoader와 테스트 환경의 TestDataLoader의 역할을 분리하여 사용하도록 했습니다.
또 다른 요구사항으로 JWT 토큰 생성에 사용되는 비밀키를 application.properties 파일로 분리하고 @Value 어노테이션으로 주입받아 사용하는 부분은 이미 이전 미션 진행하면서 구현해두었습니다.

이 외에 중복 로직을 제거하는 등의 리팩토링 과정을 진행하며 미션 마무리했습니다.


roomescape와 같은 레벨의 계층인 auth 패키지와 roomescape 내부 auth 패키지의 역할 분리가 잘 되었는지와 더불어
전체적인 코드를 보시고 어색하거나 개선할 부분이 있다면 편하게 의견 남겨주시면 감사하겠습니다! 🙇

@Ji-Woo-Kim Ji-Woo-Kim changed the base branch from main to ji-woo-kim July 16, 2025 12:23
Copy link

@TaeyeonRoyce TaeyeonRoyce left a comment

Choose a reason for hiding this comment

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

안녕하세요, 지우님! 리뷰어로는 처음이네요. 반갑습니다🙌
그동안의 스터디는 어떠셨는지 궁금하네요!

@Login 어노테이션을 통해 공통 인증 로직을 잘 처리하신 것 같습니다!
다만, auth패키지의 분리와 jwtUtils의 분리에 대한 의문이 좀 남아있어요. 지우님의 의도와 의견 남겨주시면 좋을 것 같습니다.
추가적으로 코멘트 몇몇 남겨 두었으니 확인해주세요!

import java.util.Arrays;
import java.util.Date;

public class JwtUtils {

Choose a reason for hiding this comment

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

JwtUtils에 Component를 제거한 이유가 있나요?
auth라는 roomspace와 동등한 레벨의 패키지로 분리한 이유도 궁금합니다!

Copy link
Author

@Ji-Woo-Kim Ji-Woo-Kim Jul 20, 2025

Choose a reason for hiding this comment

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

이런 방식으로 구현한 이유는 우선 미션 요구사항과 주어진 테스트를 통과시키기 위해서입니다.

@Test
void 칠단계() {
    Component componentAnnotation = JwtUtils.class.getAnnotation(Component.class);
    assertThat(componentAnnotation).isNull();
}

요구사항 해결 방향성
JWT 관련 로직으로는 토큰 생성과 토큰 값 조회 기능이 있습니다.
해당 로직을 하나의 클래스로 이동 시킨 후 roomescape 외부 패키지로 이동하세요.
이 때 @Configuraion과 @bean이 사용됩니다.

주어진 테스트코드에서 JwtUtils 클래스가 @Component 어노테이션을 가지고 있지 않아야 통과하도록 짜여져 있었습니다. 또한, 미션 요구사항에 따라 roomescape와 동등한 레벨의 패키지를 생성하였고@Configuration@Bean 사용에 대한 해결 방향성을 참고하여 AuthConfig를 통해 secretKey를 주입받아 JwtUtils Bean을 생성하는 방식으로 구현했습니다.

단순히 미션의 요구사항만을 충족하고 넘어가기엔 아쉬운 것 같아
왜 이렇게 구현을 해야하는지, 이 미션을 통해 무엇을 학습해봐야 하는지 생각해보았는데요!
자동으로 빈을 등록하는 방식(@component 사용)과 수동으로 등록하는 방식의 차이를 경험해보고
서비스 로직과 인증 로직을 분리하는 설계를 학습해보라는 의도가 있는 것이 아닌가 싶습니다.

e.printStackTrace();
return ResponseEntity.badRequest().build();
public ResponseEntity<String> handleRuntimeException(Exception e) {
log.error("예상치 못한 예외가 발생했습니다.", e);

Choose a reason for hiding this comment

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

더 좋은 방식의 예외 로그 출력이에요👍👍
아래 다른 ExceptionHandler에도 발생한 예외에 대해서 로그를 남기면 좋을 것 같아요

Copy link
Author

Choose a reason for hiding this comment

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

오류를 잡기 위해서 리팩토링 했던 부분이라 미처 다른 부분은 신경을 못 썼네요 🥲
다른 부분도 로그 출력하도록 수정해두었습니다!

Comment on lines 44 to 51
try {
Claims claims = jwtUtils.getClaims(token);
Long id = Long.parseLong(claims.getSubject());
String name = claims.get("name", String.class);
String email = claims.get("email", String.class);
String role = claims.get("role", String.class);

return new LoginMember(id, name, email, role);

Choose a reason for hiding this comment

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

jwt토큰에 다양한 정보들이 담겨있네요. 유저를 식별하는데 이렇게 많은 정보가 필요할까요?
jwt를 데이터를 담는 토큰보단 인증정보만 담은 토큰으로 사용 하시는게 좋아보여요.

발급한 토큰이 만료 되기 전에id = 1인 회원의 이름이 수정 되거나 이메일을 변경하는 상황이 발생하면 서버에서는 추가적인 처리를 해야합니다.
고유하고 불변한 id값을 통해 서명하고, 이를 통해 회원을 조회하면 데이터는 무결하게 유지할 수 있을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

토큰을 이용해서 모든 정보를 직접 처리하는 방식이 말씀해주신 데이터 무결성 문제까지도 발생할 수 있는 등 적절하지 않은 설계였던 것 같습니다.
따라서 인증 정보만 담도록 수정했고, 고유 식별자인 id만 토큰에 포함시키는 방식으로 변경했습니다!
Id는 바뀌지 않는 정보이므로 예시로 설명해주신 데이터 무결성 문제 또한 발생하지 않을 것이라 생각됩니다.

Copy link
Author

@Ji-Woo-Kim Ji-Woo-Kim Jul 20, 2025

Choose a reason for hiding this comment

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

Id만 담도록 수정한 뒤, 요구사항에서 '불필요한 DB 접근 최소화'라는 힌트에 따라 역할(role) 정보는 토큰에 담겨있도록 했습니다. 역할 정보의 변동은 거의 없기도 하고 토큰에 역할 정보가 있음으로 관리자 권한을 확인하는 AdminInterceptor에서 DB 접근 없이 처리할 수 있다는 장점이 확실히 있는 것 같습니다.!

import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import roomescape.auth.exception.UnauthenticatedException;

Choose a reason for hiding this comment

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

java.auth -> java.roomspace 를 결국 다시 의존하네요.
인증/인가 영역, jwt 처리 영역이 정말 roomspace 라는 application과 분리 되려면 roomspace의 회원에 대한 정보, 인증 정책과 독립적이어야 해요.

- 토큰에 고유 식별자(id), 역할(role) 정보만 포함
- LoginMember, LoginUserArgumentResolver roomescape 내부로 이동
@Ji-Woo-Kim
Copy link
Author

태연님, 제가 고민하는 부분들 위주로 꼼꼼히 봐주셔서 감사합니다! 🥹

리뷰 주신 부분을 다시 한 번 살펴보면서 Jwt 로직과 애플리케이션 로직 분리와 구조에 대해 좀 더 고민해보았습니다.
코멘트에 답변을 간략히 드렸지만 변경된 부분에 대해 다시 한 번 정리해드리겠습니다.

1️⃣ Jwt 토큰 수정

토큰에 모든 정보가 담겨있고 이로 인해 데이터 무결성 문제가 발생할 수 있다는 점을 말씀해주셔서 고유 식별자인 Id와 DB 접근 최소화를 위한 역할 정보만을 담고 있도록 수정하였습니다.

  • AdminInterceptor는 토큰에 포함된 역할을 이용하여 DB접근 없이 관리자 권한을 확인할 수 있고,
  • ArgumentResolver에선 토큰에서 ID를 추출한 뒤 MemberService를 통해 DB에 접근하여 정보를 조회하도록 로직을 수정하였습니다. ArgumentResolver는 컨트롤러에 최신 상태의 LoginMember 객체를 전달해야 하기 때문에 DB 접근이 불가피하다고 생각하였습니다.

추가로, 캡슐화를 위해 getClaims() 메서드를 private로 바꾸고 외부에서 필요한 정보를 제공해주는 메서드들을 추가해두었습니다.

2️⃣ 구조 개선

순환 의존성 문제를 해결하기 위해 구조를 어떻게 분리해야 할지 고민한 끝에 책임 분리를 명확히 하고자 구조를 수정해보았습니다.

  • JwtUtils@Login 어노테이션처럼 어떤 애플리케이션에서도 쓸 수 있다고 판단되는 도구 역할의 클래스는 roomescape와 동일한 레벨인 auth 패키지에 남겨두고
  • 애플리케이션의 구현체 역할을 하는 클래스는 애플리케이션(roomescape) 안에 속하도록 이동시켰습니다.

이전보다 좀 더 책임분리가 잘 된 것 같은데 태연님의 의견은 어떠신지 궁금합니다!

이외에 혹시 더 개선할 점이 있는지 다시 한번 가볍게 봐주시면 감사하겠습니다. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants