diff --git a/src/main/java/org/example/tree/domain/member/controller/MemberController.java b/src/main/java/org/example/tree/domain/member/controller/MemberController.java index 005a36a..e1370a1 100644 --- a/src/main/java/org/example/tree/domain/member/controller/MemberController.java +++ b/src/main/java/org/example/tree/domain/member/controller/MemberController.java @@ -28,5 +28,13 @@ public ApiResponse registerMember( return ApiResponse.onSuccess((memberService.register(request))); } + @PostMapping("/reissue") + @Operation(summary = "토큰 재발급", description = "토큰을 재발급합니다.") + public ApiResponse reissue( + @RequestBody final MemberRequestDTO.reissue request + ) { + return ApiResponse.onSuccess(memberService.reissue(request)); + } + } diff --git a/src/main/java/org/example/tree/domain/member/converter/MemberConverter.java b/src/main/java/org/example/tree/domain/member/converter/MemberConverter.java index a5bbdcf..6986bd9 100644 --- a/src/main/java/org/example/tree/domain/member/converter/MemberConverter.java +++ b/src/main/java/org/example/tree/domain/member/converter/MemberConverter.java @@ -28,4 +28,11 @@ public MemberResponseDTO.registerMember toRegister(String accessToken, String re .refreshToken(refreshToken) .build(); } + + public MemberResponseDTO.reissue toReissue(String accessToken, String refreshToken) { + return MemberResponseDTO.reissue.builder() + .accessToken(accessToken) + .refreshToken(refreshToken) + .build(); + } } diff --git a/src/main/java/org/example/tree/domain/member/dto/MemberRequestDTO.java b/src/main/java/org/example/tree/domain/member/dto/MemberRequestDTO.java index 4436f8a..08272fb 100644 --- a/src/main/java/org/example/tree/domain/member/dto/MemberRequestDTO.java +++ b/src/main/java/org/example/tree/domain/member/dto/MemberRequestDTO.java @@ -17,4 +17,9 @@ public static class registerMember { private String phoneNumber; private String userId; } + + @Getter + public static class reissue { + private String refreshToken; + } } diff --git a/src/main/java/org/example/tree/domain/member/dto/MemberResponseDTO.java b/src/main/java/org/example/tree/domain/member/dto/MemberResponseDTO.java index 631e29f..3eeb4ab 100644 --- a/src/main/java/org/example/tree/domain/member/dto/MemberResponseDTO.java +++ b/src/main/java/org/example/tree/domain/member/dto/MemberResponseDTO.java @@ -21,4 +21,13 @@ public static class registerMember { private String accessToken; private String refreshToken; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class reissue { + private String accessToken; + private String refreshToken; + } } diff --git a/src/main/java/org/example/tree/domain/member/service/MemberCommandService.java b/src/main/java/org/example/tree/domain/member/service/MemberCommandService.java index 0750c05..bbfc340 100644 --- a/src/main/java/org/example/tree/domain/member/service/MemberCommandService.java +++ b/src/main/java/org/example/tree/domain/member/service/MemberCommandService.java @@ -5,6 +5,8 @@ import org.example.tree.domain.member.entity.Member; import org.example.tree.domain.member.repository.MemberRepository; import org.example.tree.domain.member.repository.RefreshTokenRepository; +import org.example.tree.global.exception.GeneralException; +import org.example.tree.global.exception.GlobalErrorCode; import org.example.tree.global.security.jwt.RefreshToken; import org.example.tree.global.security.jwt.TokenProvider; import org.example.tree.global.security.jwt.dto.TokenDTO; @@ -39,6 +41,23 @@ public TokenDTO login(Member member) { .build(); } + public TokenDTO reissue(Member member) { + RefreshToken invalidToken = refreshTokenRepository.findByMemberId(member.getId()) + .orElseThrow(() -> new GeneralException(GlobalErrorCode.REFRESH_TOKEN_NOT_FOUND)); + refreshTokenRepository.delete(invalidToken); + String accessToken = tokenProvider.createAccessToken(member.getId()); + String rawToken = tokenProvider.createRefreshToken(member.getId()); + RefreshToken refreshToken = RefreshToken.builder() + .memberId(member.getId()) + .token(rawToken) + .build(); + refreshTokenRepository.save(refreshToken); + return TokenDTO.builder() + .accessToken(accessToken) + .refreshToken(refreshToken.getToken()) + .build(); + } + } diff --git a/src/main/java/org/example/tree/domain/member/service/MemberService.java b/src/main/java/org/example/tree/domain/member/service/MemberService.java index ece8661..66c9b52 100644 --- a/src/main/java/org/example/tree/domain/member/service/MemberService.java +++ b/src/main/java/org/example/tree/domain/member/service/MemberService.java @@ -32,4 +32,11 @@ public MemberResponseDTO.registerMember register(MemberRequestDTO.registerMember return memberConverter.toRegister(savedToken.getAccessToken(), savedToken.getRefreshToken()); } + @Transactional + public MemberResponseDTO.reissue reissue(MemberRequestDTO.reissue request) { + Member member = memberQueryService.findByToken(request.getRefreshToken()); + TokenDTO token = memberCommandService.reissue(member); + return memberConverter.toReissue(token.getAccessToken(), token.getRefreshToken()); + } + } diff --git a/src/main/java/org/example/tree/global/exception/GlobalErrorCode.java b/src/main/java/org/example/tree/global/exception/GlobalErrorCode.java index 6b82187..800c10c 100644 --- a/src/main/java/org/example/tree/global/exception/GlobalErrorCode.java +++ b/src/main/java/org/example/tree/global/exception/GlobalErrorCode.java @@ -21,6 +21,7 @@ public enum GlobalErrorCode { // 404 Not Found - 찾을 수 없음 NEED_AGREE_REQUIRE_TERMS(NOT_FOUND, "필수 약관에 동의해 주세요."), MEMBER_NOT_FOUND(NOT_FOUND, "등록된 사용자 정보가 없습니다."), + REFRESH_TOKEN_NOT_FOUND(NOT_FOUND, "리프레시 토큰이 존재하지 않습니다."), // 409 CONFLICT : Resource 를 찾을 수 없음 DUPLICATE_PHONE_NUMBER(CONFLICT, "중복된 전화번호가 존재합니다."),