Skip to content

Conversation

@sunwoo1256
Copy link
Collaborator

Spring Security 설정과 JWT Token을 발급해주는 로그인 기능을 구현했습니다.
email, password, name을 받아 database에 member를 추가하는 회원가입 기능을 구현했습니다.

이후 Redis를 이용한 refresh token 관리 기능을 추가 예정입니다.
member table의 채워지지 않은 값들과 member_preference table에서 회원의 정보들을 받아 저장해야 하므로 이 값들을 받는 회원 프로필 설정 기능을 추가할 예정입니다.

@sunwoo1256 sunwoo1256 linked an issue May 13, 2025 that may be closed by this pull request
@SeongHo5356 SeongHo5356 requested a review from runasy-koonta May 14, 2025 18:59
Copy link
Member

@SeongHo5356 SeongHo5356 left a comment

Choose a reason for hiding this comment

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

우선 구현하느라 고생하셨습니다. 스프링 시큐리티는 다시봐도 구현할 게 많네요. 컨벤션도 잘 지키고, dto도 다 record타입으로 만들어서 사용하시고 노력한 부분이 많이 보여서 좋았습니다~

함수 네이밍에 좀 더 신경쓰고, @Valid를 활용하면 좀 더 가독성 좋고 안정적인 코드가 될 수 있을 것 같습니다.

Comment on lines +52 to +55
// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
Copy link
Member

Choose a reason for hiding this comment

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

추후에 목적에 따라 gradle을 나눠볼 수도 있을 것 같아요!
아래 참고블로그 남겨놓을게요
gradl 분리

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

새로운 정보를 알게 되었습니다. 감사합니다!

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return memberRepository.findByEmail(username)
.map(this::createUserDetails)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
.orElseThrow(() -> new CoreException(GlobalErrorType.MEMBER_NOT_FOUND)

기존에 정의해놓은 exception 클래스를 활용할 수도 있을 것 같아요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

한번 활용해 보겠습니다!!

import static org.aspectj.weaver.tools.cache.SimpleCacheFactory.path;

@RequiredArgsConstructor
public class JwtAuthenticationFilter extends GenericFilterBean {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public class JwtAuthenticationFilter extends GenericFilterBean {
public class JwtAuthenticationFilter extends OncePerRequestFilter {

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

GenericFilterBean: 한 번의 클라이언트 요청에 대해 여러번 요청하게 되어 자원 낭비가 발생할 수 있다.
OncePerRequestFilter: doFilterInternal 메서드를 구현하여 사용자의 요청 당 한 번만 실행되는 필터를 만들 수 있다.

감사합니다!!

Comment on lines 42 to 48
String token = resolveToken((HttpServletRequest) servletRequest); // header에서 jwt 토큰을 추출
if (token != null && jwtTokenProvider.validateToken(token)) { // 토큰이 존재하고 유효할 경우
Authentication authentication = jwtTokenProvider.getAuthentication(token); // 토큰에서 Authentication 객체를 얻어
SecurityContextHolder.getContext().setAuthentication(authentication); // Spring SecurityContext에 저장
}
filterChain.doFilter(servletRequest, servletResponse); // 다음 필터로 요청을 전달(이걸 호출하지 않으면 요청이 중간에서 끊겨버려서 다음 단계 진행 불가)
}
Copy link
Member

Choose a reason for hiding this comment

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

이렇게 하면 유효하지 않아도 다음 핉터로 전달되는거 아닌가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

그런 문제가 발생할 것 같습니다. 유효하지 않을 경우 오류를 return하는 부분을 추가해야 할 것 같습니다!

System.out.println(path);
// 인증이 필요 없는 경로는 필터 패스
if (NO_AUTH_PATHS.contains(path)) {
System.out.println("No auth needed for path: " + path);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
System.out.println("No auth needed for path: " + path);
log.debug("No auth needed for {}", path);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

대량의 로그 발생시 서버에 악영향을 주는 Sysout을 지양해야 한다는 사실을 알게됐습니다. 감사합니다!!

Comment on lines 15 to 19
@Entity

@NoArgsConstructor

public class Meeting extends BaseEntity {
Copy link
Member

Choose a reason for hiding this comment

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

이런 데는 혹시 왜 줄바꿈이 있나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

수정 해 놓겠습니다!

private final MemberService memberService;

@PostMapping("/login")
public ApiResponse<?> login(@RequestBody LoginRequest loginRequest){
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public ApiResponse<?> login(@RequestBody LoginRequest loginRequest){
public ApiResponse<?> login(@Valid @RequestBody LoginRequest loginRequest){

요청 바디의 유효성 검사를 먼저 할 수도 있을 것 같네요
@Valid 참고자료

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

감사합니다! dto와 연관지어 사용해보겠습니다!

}

@PostMapping("/signup")
public ApiResponse<?> signUp(@RequestBody SignUpRequest signUpRequest){
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public ApiResponse<?> signUp(@RequestBody SignUpRequest signUpRequest){
public ApiResponse<?> signUp(@Valid @RequestBody SignUpRequest signUpRequest){

Comment on lines 6 to 10
@Schema(description = "이메일", example = "abc1234@khu.ac.kr")
String email,

@Schema(description = "비밀번호", example = "1234")
String password
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
@Schema(description = "이메일", example = "abc1234@khu.ac.kr")
String email,
@Schema(description = "비밀번호", example = "1234")
String password
@Null
@Schema(description = "이메일", example = "abc1234@khu.ac.kr")
String email,
@Null
@Schema(description = "비밀번호", example = "1234")
String password

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

혹시 로그인 기능이니 @notblank를 한번 사용해봐도 괜찮겠죠..?!

Copy link
Member

Choose a reason for hiding this comment

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

넵~ 공부해보시고 쓰고싶으신 거 사용하시면 될 것 같아요

@Schema(description = "이름", example = "홍길동")
String name
) {
public Member toEntity(String encodedPassword) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
public Member toEntity(String encodedPassword) {
public Member toMember(String encodedPassword) {

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

toMember로 하면 훨씬 명확해질 것 같습니다. 감사합니다!

@sunwoo1256 sunwoo1256 requested a review from SeongHo5356 May 17, 2025 18:41
private long refreshTokenExpirationTime;

//application.yml에 저장한 secret 값을 가져와서 key에 저장하기
@PostConstruct // 스프링 컨테이너가 빈을 생성하고, 모든 의존성 주입이 끝난 뒤 자동으로 호출되는 메서드, 주입된 값을 안전하게 사용 가능
Copy link
Collaborator

Choose a reason for hiding this comment

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

@PostConstruct로 하면 Value 값이 잘 들어온후 key값 사용하는거라 key에서 null이 안들어오겠지만
public JWTUtil(MemberRepository memberRepository,
@value("${spring.jwtsecretkey}") String secretKey) {
this.memberRepository = memberRepository;
if (secretKey == null || secretKey.isEmpty()) {
throw new IllegalArgumentException("spring.jwtsecretkey 이 설정되어야 합니다");
}
this.jwtParser = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes()))
.build();
} 생성자 주입할 때 잘 적어줘도 오류가 안나긴 하더라고요 빈 객체 생성 시점에서 생기는거라 생성자 주입에 key 쓰는거 다 넣어주면 괜찮을 거 같아요

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.

FEAT: 로그인 기능 구현

4 participants