diff --git a/.gitignore b/.gitignore index 7b79b7ca..894ecfdb 100644 --- a/.gitignore +++ b/.gitignore @@ -171,7 +171,6 @@ application-local.yml application-prod.yml ### Test ### -/src/test/ data.sql # End of https://www.gitignore.io/api/java,macos,gradle,intellij diff --git a/src/HMH-Server.iml b/src/HMH-Server.iml new file mode 100644 index 00000000..abfb34a9 --- /dev/null +++ b/src/HMH-Server.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/user/controller/UserApi.java b/src/main/java/sopt/org/hmh/domain/auth/controller/AuthApi.java similarity index 64% rename from src/main/java/sopt/org/hmh/domain/user/controller/UserApi.java rename to src/main/java/sopt/org/hmh/domain/auth/controller/AuthApi.java index 0177c935..77878853 100644 --- a/src/main/java/sopt/org/hmh/domain/user/controller/UserApi.java +++ b/src/main/java/sopt/org/hmh/domain/auth/controller/AuthApi.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.controller; +package sopt.org.hmh.domain.auth.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -7,15 +7,15 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; -import sopt.org.hmh.domain.user.dto.request.SocialPlatformRequest; -import sopt.org.hmh.domain.user.dto.request.SocialSignUpRequest; +import sopt.org.hmh.domain.auth.dto.request.SocialPlatformRequest; +import sopt.org.hmh.domain.auth.dto.request.SocialSignUpRequest; import sopt.org.hmh.global.auth.UserId; import sopt.org.hmh.global.auth.jwt.JwtConstants; import sopt.org.hmh.global.common.response.BaseResponse; @Tag(name = "회원 관련 API") @SecurityRequirement(name = JwtConstants.AUTHORIZATION) -public interface UserApi { +public interface AuthApi { @Operation(summary = "소셜 로그인") ResponseEntity> orderLogin( @@ -34,17 +34,4 @@ ResponseEntity> orderSignup( ResponseEntity> orderReissue( @Parameter(hidden = true) @RequestHeader(JwtConstants.AUTHORIZATION) final String refreshToken ); - - @Operation(summary = "로그아웃") - ResponseEntity> orderLogout(@UserId @Parameter(hidden = true) final Long userId); - - @Operation(summary = "유저 정보 불러오기") - ResponseEntity> orderGetUserInfo(@UserId @Parameter(hidden = true) final Long userId); - - @Operation(summary = "회원 탈퇴") - public ResponseEntity> orderGetUserPoint(@UserId final Long userId); - - @Operation( - summary = "회원 탈퇴") - ResponseEntity> orderWithdraw(@UserId @Parameter(hidden = true) final Long userId); } diff --git a/src/main/java/sopt/org/hmh/domain/auth/controller/AuthController.java b/src/main/java/sopt/org/hmh/domain/auth/controller/AuthController.java new file mode 100644 index 00000000..f12e88a5 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/auth/controller/AuthController.java @@ -0,0 +1,67 @@ +package sopt.org.hmh.domain.auth.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import sopt.org.hmh.domain.auth.exception.AuthSuccess; +import sopt.org.hmh.domain.auth.dto.request.SocialPlatformRequest; +import sopt.org.hmh.domain.auth.dto.request.SocialSignUpRequest; +import sopt.org.hmh.domain.auth.service.AuthService; +import sopt.org.hmh.global.auth.social.SocialAccessTokenResponse; +import sopt.org.hmh.global.common.response.BaseResponse; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/user") +public class AuthController implements AuthApi { + + private final AuthService authService; + + @PostMapping("/login") + @Override + public ResponseEntity> orderLogin( + @RequestHeader("Authorization") final String socialAccessToken, + @RequestBody final SocialPlatformRequest request + ) { + return ResponseEntity + .status(AuthSuccess.LOGIN_SUCCESS.getHttpStatus()) + .body(BaseResponse.success(AuthSuccess.LOGIN_SUCCESS, authService.login(socialAccessToken, request))); + } + + @PostMapping("/signup") + @Override + public ResponseEntity> orderSignup( + @RequestHeader("Authorization") final String socialAccessToken, + @RequestHeader("OS") final String os, + @RequestBody final SocialSignUpRequest request + ) { + return ResponseEntity + .status(AuthSuccess.SIGNUP_SUCCESS.getHttpStatus()) + .body(BaseResponse.success(AuthSuccess.SIGNUP_SUCCESS, authService.signup(socialAccessToken, request, os))); + } + + @PostMapping("/reissue") + @Override + public ResponseEntity> orderReissue( + @RequestHeader("Authorization") final String refreshToken + ) { + return ResponseEntity + .status(AuthSuccess.REISSUE_SUCCESS.getHttpStatus()) + .body(BaseResponse.success(AuthSuccess.REISSUE_SUCCESS, authService.reissueToken(refreshToken))); + } + + @GetMapping("/social/token/kakao") + public ResponseEntity> orderGetKakaoAccessToken( + @RequestParam("code") final String code + ) { + return ResponseEntity + .status(AuthSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS.getHttpStatus()) + .body(BaseResponse.success(AuthSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS, authService.getSocialAccessTokenByAuthorizationCode(code))); + } +} \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/user/dto/request/OnboardingRequest.java b/src/main/java/sopt/org/hmh/domain/auth/dto/request/OnboardingRequest.java similarity index 82% rename from src/main/java/sopt/org/hmh/domain/user/dto/request/OnboardingRequest.java rename to src/main/java/sopt/org/hmh/domain/auth/dto/request/OnboardingRequest.java index 3c0fa3ba..e9a94fa0 100644 --- a/src/main/java/sopt/org/hmh/domain/user/dto/request/OnboardingRequest.java +++ b/src/main/java/sopt/org/hmh/domain/auth/dto/request/OnboardingRequest.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.dto.request; +package sopt.org.hmh.domain.auth.dto.request; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; diff --git a/src/main/java/sopt/org/hmh/domain/user/dto/request/SocialPlatformRequest.java b/src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialPlatformRequest.java similarity index 74% rename from src/main/java/sopt/org/hmh/domain/user/dto/request/SocialPlatformRequest.java rename to src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialPlatformRequest.java index 02bf7840..cfec598a 100644 --- a/src/main/java/sopt/org/hmh/domain/user/dto/request/SocialPlatformRequest.java +++ b/src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialPlatformRequest.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.dto.request; +package sopt.org.hmh.domain.auth.dto.request; import sopt.org.hmh.global.auth.social.SocialPlatform; diff --git a/src/main/java/sopt/org/hmh/domain/user/dto/request/SocialSignUpRequest.java b/src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialSignUpRequest.java similarity index 91% rename from src/main/java/sopt/org/hmh/domain/user/dto/request/SocialSignUpRequest.java rename to src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialSignUpRequest.java index 84cb8cbc..9861dc6f 100644 --- a/src/main/java/sopt/org/hmh/domain/user/dto/request/SocialSignUpRequest.java +++ b/src/main/java/sopt/org/hmh/domain/auth/dto/request/SocialSignUpRequest.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.dto.request; +package sopt.org.hmh.domain.auth.dto.request; import com.fasterxml.jackson.annotation.JsonProperty; import sopt.org.hmh.domain.challenge.dto.request.ChallengeSignUpRequest; diff --git a/src/main/java/sopt/org/hmh/domain/user/dto/response/LoginResponse.java b/src/main/java/sopt/org/hmh/domain/auth/dto/response/LoginResponse.java similarity index 82% rename from src/main/java/sopt/org/hmh/domain/user/dto/response/LoginResponse.java rename to src/main/java/sopt/org/hmh/domain/auth/dto/response/LoginResponse.java index 114f4af2..cf9f4d78 100644 --- a/src/main/java/sopt/org/hmh/domain/user/dto/response/LoginResponse.java +++ b/src/main/java/sopt/org/hmh/domain/auth/dto/response/LoginResponse.java @@ -1,7 +1,7 @@ -package sopt.org.hmh.domain.user.dto.response; +package sopt.org.hmh.domain.auth.dto.response; import com.fasterxml.jackson.annotation.JsonProperty; -import sopt.org.hmh.domain.user.domain.User; +import sopt.org.hmh.domain.users.domain.User; import sopt.org.hmh.global.auth.jwt.TokenResponse; public record LoginResponse( diff --git a/src/main/java/sopt/org/hmh/domain/user/dto/response/ReissueResponse.java b/src/main/java/sopt/org/hmh/domain/auth/dto/response/ReissueResponse.java similarity index 88% rename from src/main/java/sopt/org/hmh/domain/user/dto/response/ReissueResponse.java rename to src/main/java/sopt/org/hmh/domain/auth/dto/response/ReissueResponse.java index 0ceec7c0..119600c9 100644 --- a/src/main/java/sopt/org/hmh/domain/user/dto/response/ReissueResponse.java +++ b/src/main/java/sopt/org/hmh/domain/auth/dto/response/ReissueResponse.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.dto.response; +package sopt.org.hmh.domain.auth.dto.response; import com.fasterxml.jackson.annotation.JsonProperty; import sopt.org.hmh.global.auth.jwt.TokenResponse; diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/exception/UserError.java b/src/main/java/sopt/org/hmh/domain/auth/exception/AuthError.java similarity index 81% rename from src/main/java/sopt/org/hmh/domain/user/domain/exception/UserError.java rename to src/main/java/sopt/org/hmh/domain/auth/exception/AuthError.java index 7d2e336d..2b80ff6b 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/exception/UserError.java +++ b/src/main/java/sopt/org/hmh/domain/auth/exception/AuthError.java @@ -1,11 +1,11 @@ -package sopt.org.hmh.domain.user.domain.exception; +package sopt.org.hmh.domain.auth.exception; import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import sopt.org.hmh.global.common.exception.base.ErrorBase; @AllArgsConstructor -public enum UserError implements ErrorBase { +public enum AuthError implements ErrorBase { // 400 BAD REQUEST INVALID_USER(HttpStatus.BAD_REQUEST, "Principle 객체가 없습니다."), @@ -13,9 +13,7 @@ public enum UserError implements ErrorBase { // 403 FORBIDDEN NOT_SIGNUP_USER(HttpStatus.FORBIDDEN, "회원가입된 유저가 아닙니다. 회원가입을 진행해주세요."), - - // 404 NOT FOUND - NOT_FOUND_USER(HttpStatus.NOT_FOUND, "유저를 찾을 수 없습니다."); + ; private final HttpStatus status; private final String errorMessage; diff --git a/src/main/java/sopt/org/hmh/domain/auth/exception/AuthException.java b/src/main/java/sopt/org/hmh/domain/auth/exception/AuthException.java new file mode 100644 index 00000000..d68559b5 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/auth/exception/AuthException.java @@ -0,0 +1,10 @@ +package sopt.org.hmh.domain.auth.exception; + +import sopt.org.hmh.global.common.exception.base.ExceptionBase; + +public class AuthException extends ExceptionBase { + + public AuthException(AuthError errorBase) { + super(errorBase); + } +} \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/auth/exception/AuthSuccess.java b/src/main/java/sopt/org/hmh/domain/auth/exception/AuthSuccess.java new file mode 100644 index 00000000..e382ef95 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/auth/exception/AuthSuccess.java @@ -0,0 +1,36 @@ +package sopt.org.hmh.domain.auth.exception; + +import lombok.AllArgsConstructor; +import org.springframework.http.HttpStatus; +import sopt.org.hmh.global.common.exception.base.SuccessBase; + +@AllArgsConstructor +public enum AuthSuccess implements SuccessBase { + + // 200 OK + LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), + REISSUE_SUCCESS(HttpStatus.OK, "토큰 재발급에 성공했습니다."), + GET_SOCIAL_ACCESS_TOKEN_SUCCESS(HttpStatus.OK, "소셜 액세스 토큰 발급을 성공했습니다."), + + // 201 CREATED + SIGNUP_SUCCESS(HttpStatus.CREATED, "회원 가입에 성공했습니다."), + ; + + private final HttpStatus status; + private final String successMessage; + + @Override + public int getHttpStatusCode() { + return this.status.value(); + } + + @Override + public HttpStatus getHttpStatus() { + return this.status; + } + + @Override + public String getSuccessMessage() { + return this.successMessage; + } +} \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/user/repository/OnboardingInfoRepository.java b/src/main/java/sopt/org/hmh/domain/auth/repository/OnboardingInfoRepository.java similarity index 60% rename from src/main/java/sopt/org/hmh/domain/user/repository/OnboardingInfoRepository.java rename to src/main/java/sopt/org/hmh/domain/auth/repository/OnboardingInfoRepository.java index 5d28859a..3fdb1d65 100644 --- a/src/main/java/sopt/org/hmh/domain/user/repository/OnboardingInfoRepository.java +++ b/src/main/java/sopt/org/hmh/domain/auth/repository/OnboardingInfoRepository.java @@ -1,7 +1,7 @@ -package sopt.org.hmh.domain.user.repository; +package sopt.org.hmh.domain.auth.repository; import org.springframework.data.jpa.repository.JpaRepository; -import sopt.org.hmh.domain.user.domain.OnboardingInfo; +import sopt.org.hmh.domain.users.domain.OnboardingInfo; public interface OnboardingInfoRepository extends JpaRepository { } diff --git a/src/main/java/sopt/org/hmh/domain/user/repository/ProblemRepository.java b/src/main/java/sopt/org/hmh/domain/auth/repository/ProblemRepository.java similarity index 59% rename from src/main/java/sopt/org/hmh/domain/user/repository/ProblemRepository.java rename to src/main/java/sopt/org/hmh/domain/auth/repository/ProblemRepository.java index 396bd0c0..5e60683e 100644 --- a/src/main/java/sopt/org/hmh/domain/user/repository/ProblemRepository.java +++ b/src/main/java/sopt/org/hmh/domain/auth/repository/ProblemRepository.java @@ -1,7 +1,7 @@ -package sopt.org.hmh.domain.user.repository; +package sopt.org.hmh.domain.auth.repository; import org.springframework.data.jpa.repository.JpaRepository; -import sopt.org.hmh.domain.user.domain.OnboardingProblem; +import sopt.org.hmh.domain.users.domain.OnboardingProblem; public interface ProblemRepository extends JpaRepository { } diff --git a/src/main/java/sopt/org/hmh/domain/auth/service/AuthService.java b/src/main/java/sopt/org/hmh/domain/auth/service/AuthService.java new file mode 100644 index 00000000..e37ff19d --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/auth/service/AuthService.java @@ -0,0 +1,89 @@ +package sopt.org.hmh.domain.auth.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import sopt.org.hmh.domain.auth.dto.response.ReissueResponse; +import sopt.org.hmh.domain.challenge.service.ChallengeService; +import sopt.org.hmh.domain.users.domain.User; +import sopt.org.hmh.domain.auth.dto.request.SocialPlatformRequest; +import sopt.org.hmh.domain.auth.dto.request.SocialSignUpRequest; +import sopt.org.hmh.domain.auth.dto.response.LoginResponse; +import sopt.org.hmh.domain.users.service.UserService; +import sopt.org.hmh.global.auth.jwt.TokenService; +import sopt.org.hmh.global.auth.jwt.exception.JwtError; +import sopt.org.hmh.global.auth.jwt.exception.JwtException; +import sopt.org.hmh.global.auth.social.SocialPlatform; +import sopt.org.hmh.global.auth.social.SocialAccessTokenResponse; +import sopt.org.hmh.global.auth.social.apple.fegin.AppleOAuthProvider; +import sopt.org.hmh.global.auth.social.kakao.fegin.KakaoLoginService; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class AuthService { + + private final KakaoLoginService kakaoLoginService; + private final AppleOAuthProvider appleOAuthProvider; + + private final ChallengeService challengeService; + private final TokenService tokenService; + private final UserService userService; + + @Transactional + public LoginResponse login(String socialAccessToken, SocialPlatformRequest request) { + + SocialPlatform socialPlatform = request.socialPlatform(); + String socialId = getSocialIdBySocialAccessToken(socialPlatform, socialAccessToken); + + User loginUser = userService.getUserBySocialPlatformAndSocialId(socialPlatform, socialId); + + return performLogin(socialAccessToken, socialPlatform, loginUser); + } + + @Transactional + public LoginResponse signup(String socialAccessToken, SocialSignUpRequest request, String os) { + + SocialPlatform socialPlatform = request.socialPlatform(); + String socialId = getSocialIdBySocialAccessToken(socialPlatform, socialAccessToken); + + userService.validateDuplicateUser(socialId, socialPlatform); + + User user = userService.addUser(socialPlatform, socialId, request.name()); + + challengeService.updateChallengeForPeriodWithInfo( + challengeService.addChallenge(user.getId(), + request.challengeSignUpRequest().period(), + request.challengeSignUpRequest().goalTime()), + request.challengeSignUpRequest().apps(), + os); + userService.registerOnboardingInfo(request); + + return performLogin(socialAccessToken, socialPlatform, user); + } + + private String getSocialIdBySocialAccessToken(SocialPlatform socialPlatform, String socialAccessToken) { + return switch (socialPlatform.toString()) { + case "KAKAO" -> kakaoLoginService.getSocialIdByKakao(socialAccessToken); + case "APPLE" -> appleOAuthProvider.getApplePlatformId(socialAccessToken); + default -> throw new JwtException(JwtError.INVALID_SOCIAL_ACCESS_TOKEN); + }; + } + + public ReissueResponse reissueToken(String refreshToken) { + return tokenService.reissueToken(refreshToken); + } + + + private LoginResponse performLogin(String socialAccessToken, SocialPlatform socialPlatform, User loginUser) { + if (socialPlatform == SocialPlatform.KAKAO) { + kakaoLoginService.updateUserInfoByKakao(loginUser, socialAccessToken); + } + return LoginResponse.of(loginUser, tokenService.issueToken(loginUser.getId())); + } + + public SocialAccessTokenResponse getSocialAccessTokenByAuthorizationCode(String code) { + return kakaoLoginService.getKakaoAccessToken(code); + } + +} \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/user/controller/UserController.java b/src/main/java/sopt/org/hmh/domain/user/controller/UserController.java deleted file mode 100644 index e7359a7b..00000000 --- a/src/main/java/sopt/org/hmh/domain/user/controller/UserController.java +++ /dev/null @@ -1,104 +0,0 @@ -package sopt.org.hmh.domain.user.controller; - -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import sopt.org.hmh.domain.user.domain.exception.UserSuccess; -import sopt.org.hmh.domain.user.dto.request.SocialPlatformRequest; -import sopt.org.hmh.domain.user.dto.request.SocialSignUpRequest; -import sopt.org.hmh.domain.user.service.UserService; -import sopt.org.hmh.global.auth.UserId; -import sopt.org.hmh.global.auth.social.SocialAccessTokenResponse; -import sopt.org.hmh.global.common.response.BaseResponse; -import sopt.org.hmh.global.common.response.EmptyJsonResponse; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/v1/user") -public class UserController implements UserApi{ - - private final UserService userService; - - @PostMapping("/login") - @Override - public ResponseEntity> orderLogin( - @RequestHeader("Authorization") final String socialAccessToken, - @RequestBody final SocialPlatformRequest request - ) { - return ResponseEntity - .status(UserSuccess.LOGIN_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(UserSuccess.LOGIN_SUCCESS, userService.login(socialAccessToken, request))); - } - - @PostMapping("/signup") - @Override - public ResponseEntity> orderSignup( - @RequestHeader("Authorization") final String socialAccessToken, - @RequestHeader("OS") final String os, - @RequestBody final SocialSignUpRequest request - ) { - return ResponseEntity - .status(UserSuccess.SIGNUP_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(UserSuccess.SIGNUP_SUCCESS, userService.signup(socialAccessToken, request, os))); - } - - @PostMapping("/reissue") - @Override - public ResponseEntity> orderReissue( - @RequestHeader("Authorization") final String refreshToken - ) { - return ResponseEntity - .status(UserSuccess.REISSUE_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(UserSuccess.REISSUE_SUCCESS, userService.reissueToken(refreshToken))); - } - - @PostMapping("/logout") - @Override - public ResponseEntity> orderLogout(@UserId final Long userId) { - userService.logout(userId); - return ResponseEntity - .status(UserSuccess.LOGOUT_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(UserSuccess.LOGOUT_SUCCESS, new EmptyJsonResponse())); - } - - @GetMapping - @Override - public ResponseEntity> orderGetUserInfo(@UserId final Long userId) { - return ResponseEntity - .status(UserSuccess.GET_USER_INFO_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(UserSuccess.GET_USER_INFO_SUCCESS, userService.getUserInfo(userId))); - } - - @GetMapping("/point") - @Override - public ResponseEntity> orderGetUserPoint(@UserId final Long userId) { - return ResponseEntity - .status(UserSuccess.GET_USER_POINT_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(UserSuccess.GET_USER_POINT_SUCCESS, userService.getUserPoint(userId))); - } - - @DeleteMapping - @Override - public ResponseEntity> orderWithdraw(@UserId final Long userId) { - userService.withdraw(userId); - return ResponseEntity - .status(UserSuccess.WITHDRAW_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(UserSuccess.WITHDRAW_SUCCESS, new EmptyJsonResponse())); - } - - @GetMapping("/social/token/kakao") - public ResponseEntity> orderGetKakaoAccessToken( - @RequestParam("code") final String code - ) { - return ResponseEntity - .status(UserSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS.getHttpStatus()) - .body(BaseResponse.success(UserSuccess.GET_SOCIAL_ACCESS_TOKEN_SUCCESS, userService.getSocialAccessTokenByAuthorizationCode(code))); - } -} \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/user/dto/response/UserPointResponse.java b/src/main/java/sopt/org/hmh/domain/user/dto/response/UserPointResponse.java deleted file mode 100644 index 082bc9ae..00000000 --- a/src/main/java/sopt/org/hmh/domain/user/dto/response/UserPointResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package sopt.org.hmh.domain.user.dto.response; - -import sopt.org.hmh.domain.user.domain.User; - -public record UserPointResponse( - Integer point -) { - public static UserPointResponse of(User user) { - return new UserPointResponse( - user.getPoint() - ); - } -} \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/user/service/ExpiredUserDeleteScheduler.java b/src/main/java/sopt/org/hmh/domain/user/service/ExpiredUserDeleteScheduler.java deleted file mode 100644 index 0c2ea49a..00000000 --- a/src/main/java/sopt/org/hmh/domain/user/service/ExpiredUserDeleteScheduler.java +++ /dev/null @@ -1,20 +0,0 @@ -package sopt.org.hmh.domain.user.service; - -import java.time.LocalDateTime; -import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -@Component -@RequiredArgsConstructor -@Transactional -public class ExpiredUserDeleteScheduler { - - private final UserService userService; - - @Scheduled(cron = "0 0 4 * * ?") - public void deleteExpiredUser() { - userService.deleteExpiredUser(LocalDateTime.now()); - } -} diff --git a/src/main/java/sopt/org/hmh/domain/users/controller/UserApi.java b/src/main/java/sopt/org/hmh/domain/users/controller/UserApi.java new file mode 100644 index 00000000..f03019d8 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/users/controller/UserApi.java @@ -0,0 +1,24 @@ +package sopt.org.hmh.domain.users.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.http.ResponseEntity; +import sopt.org.hmh.global.auth.UserId; +import sopt.org.hmh.global.common.response.BaseResponse; + +public interface UserApi { + + @Operation(summary = "로그아웃") + ResponseEntity> orderLogout(@UserId @Parameter(hidden = true) final Long userId); + + @Operation(summary = "유저 정보 불러오기") + ResponseEntity> orderGetUserInfo(@UserId @Parameter(hidden = true) final Long userId); + + @Operation(summary = "유저 포인트 정보 불러오기") + public ResponseEntity> orderGetUserPoint(@UserId final Long userId); + + @Operation( + summary = "회원 탈퇴") + ResponseEntity> orderWithdraw(@UserId @Parameter(hidden = true) final Long userId); + +} diff --git a/src/main/java/sopt/org/hmh/domain/users/controller/UserController.java b/src/main/java/sopt/org/hmh/domain/users/controller/UserController.java new file mode 100644 index 00000000..478f267c --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/users/controller/UserController.java @@ -0,0 +1,56 @@ +package sopt.org.hmh.domain.users.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import sopt.org.hmh.domain.users.domain.exception.UserSuccess; +import sopt.org.hmh.domain.users.service.UserService; +import sopt.org.hmh.global.auth.UserId; +import sopt.org.hmh.global.common.response.BaseResponse; +import sopt.org.hmh.global.common.response.EmptyJsonResponse; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/user") +public class UserController implements UserApi{ + + private final UserService userService; + + @PostMapping("/logout") + @Override + public ResponseEntity> orderLogout(@UserId final Long userId) { + userService.logout(userId); + return ResponseEntity + .status(UserSuccess.LOGOUT_SUCCESS.getHttpStatus()) + .body(BaseResponse.success(UserSuccess.LOGOUT_SUCCESS, new EmptyJsonResponse())); + } + + @GetMapping + @Override + public ResponseEntity> orderGetUserInfo(@UserId final Long userId) { + return ResponseEntity + .status(UserSuccess.GET_USER_INFO_SUCCESS.getHttpStatus()) + .body(BaseResponse.success(UserSuccess.GET_USER_INFO_SUCCESS, userService.getUserInfo(userId))); + } + + @GetMapping("/point") + @Override + public ResponseEntity> orderGetUserPoint(@UserId final Long userId) { + return ResponseEntity + .status(UserSuccess.GET_USER_POINT_SUCCESS.getHttpStatus()) + .body(BaseResponse.success(UserSuccess.GET_USER_POINT_SUCCESS, userService.getUserInfo(userId).point())); + } + + @DeleteMapping + public ResponseEntity> orderWithdraw(@UserId final Long userId) { + userService.withdraw(userId); + return ResponseEntity + .status(UserSuccess.WITHDRAW_SUCCESS.getHttpStatus()) + .body(BaseResponse.success(UserSuccess.WITHDRAW_SUCCESS, new EmptyJsonResponse())); + } + +} diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingInfo.java b/src/main/java/sopt/org/hmh/domain/users/domain/OnboardingInfo.java similarity index 94% rename from src/main/java/sopt/org/hmh/domain/user/domain/OnboardingInfo.java rename to src/main/java/sopt/org/hmh/domain/users/domain/OnboardingInfo.java index 08db01bb..04f92495 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingInfo.java +++ b/src/main/java/sopt/org/hmh/domain/users/domain/OnboardingInfo.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.domain; +package sopt.org.hmh.domain.users.domain; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingProblem.java b/src/main/java/sopt/org/hmh/domain/users/domain/OnboardingProblem.java similarity index 94% rename from src/main/java/sopt/org/hmh/domain/user/domain/OnboardingProblem.java rename to src/main/java/sopt/org/hmh/domain/users/domain/OnboardingProblem.java index 5220bd5b..f2d37faf 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/OnboardingProblem.java +++ b/src/main/java/sopt/org/hmh/domain/users/domain/OnboardingProblem.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.domain; +package sopt.org.hmh.domain.users.domain; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/User.java b/src/main/java/sopt/org/hmh/domain/users/domain/User.java similarity index 97% rename from src/main/java/sopt/org/hmh/domain/user/domain/User.java rename to src/main/java/sopt/org/hmh/domain/users/domain/User.java index fd1c2df0..6d5058b9 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/User.java +++ b/src/main/java/sopt/org/hmh/domain/users/domain/User.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.domain; +package sopt.org.hmh.domain.users.domain; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/UserConstants.java b/src/main/java/sopt/org/hmh/domain/users/domain/UserConstants.java similarity index 86% rename from src/main/java/sopt/org/hmh/domain/user/domain/UserConstants.java rename to src/main/java/sopt/org/hmh/domain/users/domain/UserConstants.java index 7a96a680..02c59884 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/UserConstants.java +++ b/src/main/java/sopt/org/hmh/domain/users/domain/UserConstants.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.domain; +package sopt.org.hmh.domain.users.domain; import lombok.AccessLevel; import lombok.NoArgsConstructor; diff --git a/src/main/java/sopt/org/hmh/domain/users/domain/exception/UserError.java b/src/main/java/sopt/org/hmh/domain/users/domain/exception/UserError.java new file mode 100644 index 00000000..5978b116 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/users/domain/exception/UserError.java @@ -0,0 +1,31 @@ +package sopt.org.hmh.domain.users.domain.exception; + +import lombok.AllArgsConstructor; +import org.springframework.http.HttpStatus; +import sopt.org.hmh.global.common.exception.base.ErrorBase; + +@AllArgsConstructor +public enum UserError implements ErrorBase { + + // 404 NOT FOUND + NOT_FOUND_USER(HttpStatus.NOT_FOUND, "유저를 찾을 수 없습니다."), + ; + + private final HttpStatus status; + private final String errorMessage; + + @Override + public int getHttpStatusCode() { + return this.status.value(); + } + + @Override + public HttpStatus getHttpStatus() { + return this.status; + } + + @Override + public String getErrorMessage() { + return this.errorMessage; + } +} \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/exception/UserException.java b/src/main/java/sopt/org/hmh/domain/users/domain/exception/UserException.java similarity index 78% rename from src/main/java/sopt/org/hmh/domain/user/domain/exception/UserException.java rename to src/main/java/sopt/org/hmh/domain/users/domain/exception/UserException.java index 81c000a2..176f0832 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/exception/UserException.java +++ b/src/main/java/sopt/org/hmh/domain/users/domain/exception/UserException.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.domain.exception; +package sopt.org.hmh.domain.users.domain.exception; import sopt.org.hmh.global.common.exception.base.ExceptionBase; @@ -7,4 +7,4 @@ public class UserException extends ExceptionBase { public UserException(UserError errorBase) { super(errorBase); } -} \ No newline at end of file +} diff --git a/src/main/java/sopt/org/hmh/domain/user/domain/exception/UserSuccess.java b/src/main/java/sopt/org/hmh/domain/users/domain/exception/UserSuccess.java similarity index 70% rename from src/main/java/sopt/org/hmh/domain/user/domain/exception/UserSuccess.java rename to src/main/java/sopt/org/hmh/domain/users/domain/exception/UserSuccess.java index cae47ddb..a8cf03bb 100644 --- a/src/main/java/sopt/org/hmh/domain/user/domain/exception/UserSuccess.java +++ b/src/main/java/sopt/org/hmh/domain/users/domain/exception/UserSuccess.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.domain.exception; +package sopt.org.hmh.domain.users.domain.exception; import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; @@ -8,16 +8,10 @@ public enum UserSuccess implements SuccessBase { // 200 OK - LOGIN_SUCCESS(HttpStatus.OK, "로그인에 성공했습니다."), - REISSUE_SUCCESS(HttpStatus.OK, "토큰 재발급에 성공했습니다."), - LOGOUT_SUCCESS(HttpStatus.OK, "로그아웃에 성공했습니다."), GET_USER_INFO_SUCCESS(HttpStatus.OK, "유저의 정보를 불러오는데에 성공했습니다."), GET_USER_POINT_SUCCESS(HttpStatus.OK, "유저의 포인트 정보를 불러오는데에 성공했습니다."), + LOGOUT_SUCCESS(HttpStatus.OK, "로그아웃에 성공했습니다."), WITHDRAW_SUCCESS(HttpStatus.OK, "회원 탈퇴를 성공하였습니다."), - GET_SOCIAL_ACCESS_TOKEN_SUCCESS(HttpStatus.OK, "소셜 액세스 토큰 발급을 성공했습니다."), - - // 201 CREATED - SIGNUP_SUCCESS(HttpStatus.CREATED, "회원 가입에 성공했습니다."), ; private final HttpStatus status; @@ -37,4 +31,4 @@ public HttpStatus getHttpStatus() { public String getSuccessMessage() { return this.successMessage; } -} \ No newline at end of file +} diff --git a/src/main/java/sopt/org/hmh/domain/user/dto/response/UserInfoResponse.java b/src/main/java/sopt/org/hmh/domain/users/dto/response/UserInfoResponse.java similarity index 72% rename from src/main/java/sopt/org/hmh/domain/user/dto/response/UserInfoResponse.java rename to src/main/java/sopt/org/hmh/domain/users/dto/response/UserInfoResponse.java index 0cf207b8..006a435a 100644 --- a/src/main/java/sopt/org/hmh/domain/user/dto/response/UserInfoResponse.java +++ b/src/main/java/sopt/org/hmh/domain/users/dto/response/UserInfoResponse.java @@ -1,6 +1,6 @@ -package sopt.org.hmh.domain.user.dto.response; +package sopt.org.hmh.domain.users.dto.response; -import sopt.org.hmh.domain.user.domain.User; +import sopt.org.hmh.domain.users.domain.User; public record UserInfoResponse( String name, diff --git a/src/main/java/sopt/org/hmh/domain/user/repository/UserRepository.java b/src/main/java/sopt/org/hmh/domain/users/repository/UserRepository.java similarity index 72% rename from src/main/java/sopt/org/hmh/domain/user/repository/UserRepository.java rename to src/main/java/sopt/org/hmh/domain/users/repository/UserRepository.java index f37f1ebf..64db18da 100644 --- a/src/main/java/sopt/org/hmh/domain/user/repository/UserRepository.java +++ b/src/main/java/sopt/org/hmh/domain/users/repository/UserRepository.java @@ -1,4 +1,4 @@ -package sopt.org.hmh.domain.user.repository; +package sopt.org.hmh.domain.users.repository; import java.time.LocalDateTime; import java.util.List; @@ -6,16 +6,18 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import sopt.org.hmh.domain.user.domain.User; -import sopt.org.hmh.domain.user.domain.exception.UserError; -import sopt.org.hmh.domain.user.domain.exception.UserException; +import sopt.org.hmh.domain.users.domain.User; +import sopt.org.hmh.domain.auth.exception.AuthError; +import sopt.org.hmh.domain.auth.exception.AuthException; +import sopt.org.hmh.domain.users.domain.exception.UserError; +import sopt.org.hmh.domain.users.domain.exception.UserException; import sopt.org.hmh.global.auth.social.SocialPlatform; public interface UserRepository extends JpaRepository { default User findBySocialPlatformAndSocialIdOrThrowException(SocialPlatform socialPlatform, String socialId) { - return findBySocialPlatformAndSocialId(socialPlatform, socialId).orElseThrow(() -> new UserException( - UserError.NOT_SIGNUP_USER)); + return findBySocialPlatformAndSocialId(socialPlatform, socialId).orElseThrow(() -> new AuthException( + AuthError.NOT_SIGNUP_USER)); } default User findByIdOrThrowException(Long userId) { diff --git a/src/main/java/sopt/org/hmh/domain/users/service/ExpiredUserDeleteScheduler.java b/src/main/java/sopt/org/hmh/domain/users/service/ExpiredUserDeleteScheduler.java new file mode 100644 index 00000000..15546b27 --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/users/service/ExpiredUserDeleteScheduler.java @@ -0,0 +1,30 @@ +package sopt.org.hmh.domain.users.service; + +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import sopt.org.hmh.domain.challenge.service.ChallengeService; +import sopt.org.hmh.domain.users.repository.UserRepository; + +@Component +@RequiredArgsConstructor +@Transactional +public class ExpiredUserDeleteScheduler { + + private final UserRepository userRepository; + private final ChallengeService challengeService; + + @Scheduled(cron = "0 0 4 * * ?") + public void deleteExpiredUser() { + deleteExpiredUser(LocalDateTime.now()); + } + + public void deleteExpiredUser(LocalDateTime currentDate) { + List expiredUserList = userRepository.findIdByDeletedAtBeforeAndIsDeletedIsTrue(currentDate); + userRepository.deleteAllById(expiredUserList); + challengeService.deleteChallengeRelatedByUserId(expiredUserList); + } +} diff --git a/src/main/java/sopt/org/hmh/domain/users/service/UserService.java b/src/main/java/sopt/org/hmh/domain/users/service/UserService.java new file mode 100644 index 00000000..33eb784c --- /dev/null +++ b/src/main/java/sopt/org/hmh/domain/users/service/UserService.java @@ -0,0 +1,92 @@ +package sopt.org.hmh.domain.users.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; +import sopt.org.hmh.domain.auth.dto.request.SocialSignUpRequest; +import sopt.org.hmh.domain.auth.exception.AuthError; +import sopt.org.hmh.domain.auth.exception.AuthException; +import sopt.org.hmh.domain.auth.repository.OnboardingInfoRepository; +import sopt.org.hmh.domain.auth.repository.ProblemRepository; +import sopt.org.hmh.domain.users.domain.OnboardingInfo; +import sopt.org.hmh.domain.users.domain.OnboardingProblem; +import sopt.org.hmh.domain.users.domain.User; +import sopt.org.hmh.domain.users.domain.UserConstants; +import sopt.org.hmh.domain.users.dto.response.UserInfoResponse; +import sopt.org.hmh.domain.users.repository.UserRepository; +import sopt.org.hmh.global.auth.redis.RedisManagerService; +import sopt.org.hmh.global.auth.social.SocialPlatform; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class UserService { + + private final RedisManagerService redisManagerService; + private final UserRepository userRepository; + private final OnboardingInfoRepository onboardingInfoRepository; + private final ProblemRepository problemRepository; + + + @Transactional + public void withdraw(Long userId) { + redisManagerService.deleteRefreshToken(userId); + userRepository.findByIdOrThrowException(userId).softDelete(); + } + + public void logout(Long userId) { + redisManagerService.deleteRefreshToken(userId); + } + + public UserInfoResponse getUserInfo(Long userId) { + return UserInfoResponse.of(userRepository.findByIdOrThrowException(userId)); + } + + public User getUserBySocialPlatformAndSocialId(SocialPlatform socialPlatform, String socialId) { + User user = userRepository.findBySocialPlatformAndSocialIdOrThrowException(socialPlatform, socialId); + if (user.isDeleted()) { + user.recover(); + } + return user; + } + + public void validateDuplicateUser(String socialId, SocialPlatform socialPlatform) { + if (userRepository.existsBySocialPlatformAndSocialId(socialPlatform, socialId)) { + throw new AuthException(AuthError.DUPLICATE_USER); + } + } + + public User addUser(SocialPlatform socialPlatform, String socialId, String name) { + return userRepository.save( + User.builder() + .socialPlatform(socialPlatform) + .socialId(socialId) + .name(validateName(name)) + .build() + ); + } + + private String validateName(String name) { + if (!StringUtils.hasText(name)) { + return UserConstants.DEFAULT_USER_NAME; + } + return name; + } + + public void registerOnboardingInfo(SocialSignUpRequest request) { + OnboardingInfo onboardingInfo = OnboardingInfo.builder() + .averageUseTime(request.onboardingRequest().averageUseTime()) + .build(); + Long onboardingInfoId = onboardingInfoRepository.save(onboardingInfo).getId(); + + List problemList = request.onboardingRequest().problemList().stream() + .map(problem -> OnboardingProblem.builder() + .onboardingInfoId(onboardingInfoId) + .problem(problem).build()) + .toList(); + problemRepository.saveAll(problemList); + } + +} diff --git a/src/main/java/sopt/org/hmh/global/auth/jwt/JwtGenerator.java b/src/main/java/sopt/org/hmh/global/auth/jwt/JwtGenerator.java index 6a5c8e24..185280db 100644 --- a/src/main/java/sopt/org/hmh/global/auth/jwt/JwtGenerator.java +++ b/src/main/java/sopt/org/hmh/global/auth/jwt/JwtGenerator.java @@ -24,6 +24,7 @@ public class JwtGenerator { private Long ACCESS_TOKEN_EXPIRATION_TIME; @Value("${jwt.refresh-token-expiration-time}") private Long REFRESH_TOKEN_EXPIRATION_TIME; + private final TokenRepository tokenRepository; public String generateToken(Long userId, boolean isRefreshToken) { @@ -49,6 +50,12 @@ public String generateToken(Long userId, boolean isRefreshToken) { return token; } + public JwtParser getJwtParser() { + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build(); + } + private Date generateNowDate() { return new Date(); } @@ -57,7 +64,7 @@ private Date generateExpirationDate(boolean isRefreshToken, Date now) { return new Date(now.getTime() + calculateExpirationTime(isRefreshToken)); } - public SecretKey getSigningKey() { + private SecretKey getSigningKey() { return Keys.hmacShaKeyFor(encodeSecretKey().getBytes()); } @@ -73,9 +80,4 @@ private String encodeSecretKey() { .encodeToString(JWT_SECRET.getBytes(StandardCharsets.UTF_8)); } - public JwtParser getJwtParser() { - return Jwts.parserBuilder() - .setSigningKey(getSigningKey()) - .build(); - } } \ No newline at end of file diff --git a/src/main/java/sopt/org/hmh/global/auth/jwt/JwtProvider.java b/src/main/java/sopt/org/hmh/global/auth/jwt/JwtProvider.java index 91424c4e..dceb8aba 100644 --- a/src/main/java/sopt/org/hmh/global/auth/jwt/JwtProvider.java +++ b/src/main/java/sopt/org/hmh/global/auth/jwt/JwtProvider.java @@ -11,8 +11,8 @@ public class JwtProvider { private final JwtGenerator jwtGenerator; public TokenResponse issueToken(Long userId) { - return TokenResponse.of(jwtGenerator.generateToken(userId, true), - jwtGenerator.generateToken(userId, false)); + return TokenResponse.of(jwtGenerator.generateToken(userId, false), + jwtGenerator.generateToken(userId, true)); } public Long getSubject(String token) { diff --git a/src/main/java/sopt/org/hmh/global/auth/jwt/TokenService.java b/src/main/java/sopt/org/hmh/global/auth/jwt/TokenService.java new file mode 100644 index 00000000..66292c1e --- /dev/null +++ b/src/main/java/sopt/org/hmh/global/auth/jwt/TokenService.java @@ -0,0 +1,40 @@ +package sopt.org.hmh.global.auth.jwt; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import sopt.org.hmh.domain.auth.dto.response.ReissueResponse; +import sopt.org.hmh.global.auth.jwt.exception.JwtError; +import sopt.org.hmh.global.auth.jwt.exception.JwtException; +import sopt.org.hmh.global.auth.redis.RedisManagerService; + +@Service +@RequiredArgsConstructor +public class TokenService { + + private final JwtProvider jwtProvider; + private final JwtValidator jwtValidator; + private final RedisManagerService redisManagerService; + + @Transactional + public ReissueResponse reissueToken(String refreshToken) { + String parsedRefreshToken = parseTokenString(refreshToken); + Long userId = jwtProvider.getSubject(parsedRefreshToken); + jwtValidator.validateRefreshToken(parsedRefreshToken); + redisManagerService.deleteRefreshToken(userId); + return ReissueResponse.of(jwtProvider.issueToken(userId)); + } + + private String parseTokenString(String tokenString) { + String[] parsedTokens = tokenString.split(" "); + if (parsedTokens.length != 2) { + throw new JwtException(JwtError.INVALID_TOKEN_HEADER); + } + return parsedTokens[1]; + } + + public TokenResponse issueToken(Long userId) { + return jwtProvider.issueToken(userId); + } + +} diff --git a/src/main/java/sopt/org/hmh/global/auth/redis/TokenService.java b/src/main/java/sopt/org/hmh/global/auth/redis/RedisManagerService.java similarity index 88% rename from src/main/java/sopt/org/hmh/global/auth/redis/TokenService.java rename to src/main/java/sopt/org/hmh/global/auth/redis/RedisManagerService.java index 9357150a..812a5681 100644 --- a/src/main/java/sopt/org/hmh/global/auth/redis/TokenService.java +++ b/src/main/java/sopt/org/hmh/global/auth/redis/RedisManagerService.java @@ -7,13 +7,10 @@ @Service @RequiredArgsConstructor -public class TokenService { +public class RedisManagerService { private final TokenRepository tokenRepository; - /** - * Refresh 토큰 삭제 - */ public void deleteRefreshToken(Long userId) { if (tokenRepository.existsById(userId)) { tokenRepository.deleteById(userId); diff --git a/src/main/java/sopt/org/hmh/global/auth/social/kakao/fegin/KakaoLoginService.java b/src/main/java/sopt/org/hmh/global/auth/social/kakao/fegin/KakaoLoginService.java index c1aff229..04616e5d 100644 --- a/src/main/java/sopt/org/hmh/global/auth/social/kakao/fegin/KakaoLoginService.java +++ b/src/main/java/sopt/org/hmh/global/auth/social/kakao/fegin/KakaoLoginService.java @@ -6,7 +6,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import sopt.org.hmh.domain.user.domain.User; +import sopt.org.hmh.domain.users.domain.User; import sopt.org.hmh.global.auth.jwt.exception.JwtError; import sopt.org.hmh.global.auth.jwt.exception.JwtException; import sopt.org.hmh.global.auth.social.SocialAccessTokenResponse; diff --git a/src/test/java/sopt/org/hmh/HmhApplicationTests.java b/src/test/java/sopt/org/hmh/HmhApplicationTests.java new file mode 100644 index 00000000..a35f1001 --- /dev/null +++ b/src/test/java/sopt/org/hmh/HmhApplicationTests.java @@ -0,0 +1,13 @@ +package sopt.org.hmh; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class HmhApplicationTests { + + @Test + void contextLoads() { + } + +}