diff --git a/src/main/java/side/onetime/auth/handler/OAuthLoginSuccessHandler.java b/src/main/java/side/onetime/auth/handler/OAuthLoginSuccessHandler.java index 2602c80..4fc8eb1 100644 --- a/src/main/java/side/onetime/auth/handler/OAuthLoginSuccessHandler.java +++ b/src/main/java/side/onetime/auth/handler/OAuthLoginSuccessHandler.java @@ -81,7 +81,7 @@ private void handleAuthentication(HttpServletRequest request, HttpServletRespons if (existUser == null) { // 신규 유저 처리 - handleNewUser(request, response, provider, providerId, name); + handleNewUser(request, response, provider, providerId, name, email); } else { // 기존 유저 처리 handleExistingUser(request, response, existUser); @@ -94,9 +94,9 @@ private void handleAuthentication(HttpServletRequest request, HttpServletRespons } // 신규 유저 처리 - private void handleNewUser(HttpServletRequest request, HttpServletResponse response, String provider, String providerId, String name) throws IOException { + private void handleNewUser(HttpServletRequest request, HttpServletResponse response, String provider, String providerId, String name, String email) throws IOException { log.info("신규 유저입니다."); - String registerToken = jwtUtil.generateRegisterToken(provider, providerId, name, ACCESS_TOKEN_EXPIRATION_TIME); + String registerToken = jwtUtil.generateRegisterToken(provider, providerId, name, email, ACCESS_TOKEN_EXPIRATION_TIME); String redirectUri = String.format(REGISTER_TOKEN_REDIRECT_URI, registerToken, URLEncoder.encode(name, StandardCharsets.UTF_8)); getRedirectStrategy().sendRedirect(request, response, redirectUri); } diff --git a/src/main/java/side/onetime/controller/UserController.java b/src/main/java/side/onetime/controller/UserController.java index 69c216c..37c2d98 100644 --- a/src/main/java/side/onetime/controller/UserController.java +++ b/src/main/java/side/onetime/controller/UserController.java @@ -2,10 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import side.onetime.dto.UserDto; import side.onetime.global.common.ApiResponse; import side.onetime.global.common.constant.SuccessStatus; @@ -25,4 +22,23 @@ public ResponseEntity> onboardUser( UserDto.OnboardUserResponse onboardUserResponse = userService.onboardUser(onboardUserRequest); return ApiResponse.onSuccess(SuccessStatus._ONBOARD_USER, onboardUserResponse); } + + // 유저 정보 조회 API + @GetMapping("/profile") + public ResponseEntity> getUserProfile( + @RequestHeader("Authorization") String authorizationHeader) { + + UserDto.GetUserProfileResponse getUserProfileResponse = userService.getUserProfile(authorizationHeader); + return ApiResponse.onSuccess(SuccessStatus._GET_USER_PROFILE, getUserProfileResponse); + } + + // 유저 정보 수정 API + @PatchMapping("/profile/action-update") + public ResponseEntity> updateUserProfile( + @RequestHeader("Authorization") String authorizationHeader, + @RequestBody UserDto.UpdateUserProfileRequest updateUserProfileRequest) { + + userService.updateUserProfile(authorizationHeader, updateUserProfileRequest); + return ApiResponse.onSuccess(SuccessStatus._UPDATE_USER_PROFILE); + } } \ No newline at end of file diff --git a/src/main/java/side/onetime/domain/User.java b/src/main/java/side/onetime/domain/User.java index c745c43..f47ad1d 100644 --- a/src/main/java/side/onetime/domain/User.java +++ b/src/main/java/side/onetime/domain/User.java @@ -20,6 +20,9 @@ public class User extends BaseEntity { @Column(name = "name", nullable = false, length = 50) private String name; + @Column(name = "email", nullable = false, length = 50) + private String email; + @Column(name = "nickname", nullable = false, length = 10) private String nickname; @@ -30,10 +33,15 @@ public class User extends BaseEntity { private String providerId; @Builder - public User(String name, String nickname, String provider, String providerId) { + public User(String name, String email, String nickname, String provider, String providerId) { this.name = name; + this.email = email; this.nickname = nickname; this.provider = provider; this.providerId = providerId; } + + public void updateNickName(String nickname) { + this.nickname = nickname; + } } \ No newline at end of file diff --git a/src/main/java/side/onetime/dto/UserDto.java b/src/main/java/side/onetime/dto/UserDto.java index 2039903..cdad045 100644 --- a/src/main/java/side/onetime/dto/UserDto.java +++ b/src/main/java/side/onetime/dto/UserDto.java @@ -7,6 +7,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import side.onetime.domain.User; public class UserDto { @Builder @@ -37,4 +38,32 @@ public static OnboardUserResponse of(String accessToken, String refreshToken) { .build(); } } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + @JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class) + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class GetUserProfileResponse { + private String nickname; + private String email; + + public static GetUserProfileResponse of(User user) { + return GetUserProfileResponse.builder() + .nickname(user.getNickname()) + .email(user.getEmail()) + .build(); + } + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + @JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class) + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class UpdateUserProfileRequest { + private String nickname; + } } \ No newline at end of file diff --git a/src/main/java/side/onetime/exception/UserErrorResult.java b/src/main/java/side/onetime/exception/UserErrorResult.java index 7cbb342..c29dc0f 100644 --- a/src/main/java/side/onetime/exception/UserErrorResult.java +++ b/src/main/java/side/onetime/exception/UserErrorResult.java @@ -10,7 +10,8 @@ @RequiredArgsConstructor public enum UserErrorResult implements BaseErrorCode { _NOT_FOUND_USER(HttpStatus.NOT_FOUND, "404", "유저를 찾을 수 없습니다."), - _NICKNAME_TOO_LONG(HttpStatus.BAD_REQUEST, "400", "닉네임 길이 제한을 초과했습니다.") + _NICKNAME_TOO_LONG(HttpStatus.BAD_REQUEST, "400", "닉네임 길이 제한을 초과했습니다."), + _NOT_FOUND_REQUEST_NICKNAME(HttpStatus.BAD_REQUEST, "400", "닉네임 요청 값을 찾을 수 없습니다.") ; private final HttpStatus httpStatus; diff --git a/src/main/java/side/onetime/global/common/constant/SuccessStatus.java b/src/main/java/side/onetime/global/common/constant/SuccessStatus.java index 6f03298..eaa3f4c 100644 --- a/src/main/java/side/onetime/global/common/constant/SuccessStatus.java +++ b/src/main/java/side/onetime/global/common/constant/SuccessStatus.java @@ -37,6 +37,8 @@ public enum SuccessStatus implements BaseCode { _REISSUE_TOKENS(HttpStatus.CREATED, "201", "토큰 재발행에 성공했습니다."), // User _ONBOARD_USER(HttpStatus.CREATED, "201", "유저 온보딩에 성공했습니다."), + _GET_USER_PROFILE(HttpStatus.OK, "200", "유저 정보 조회에 성공했습니다."), + _UPDATE_USER_PROFILE(HttpStatus.OK, "200", "유저 정보 수정에 성공했습니다."), ; private final HttpStatus httpStatus; diff --git a/src/main/java/side/onetime/service/UserService.java b/src/main/java/side/onetime/service/UserService.java index 6cca11a..c13a207 100644 --- a/src/main/java/side/onetime/service/UserService.java +++ b/src/main/java/side/onetime/service/UserService.java @@ -37,6 +37,7 @@ public UserDto.OnboardUserResponse onboardUser(UserDto.OnboardUserRequest onboar String provider = jwtUtil.getProviderFromToken(registerToken); String providerId = jwtUtil.getProviderIdFromToken(registerToken); String name = jwtUtil.getNameFromToken(registerToken); + String email = jwtUtil.getEmailFromToken(registerToken); if (onboardUserRequest.getNickname().length() > NICKNAME_LENGTH_LIMIT) { throw new UserException(UserErrorResult._NICKNAME_TOO_LONG); @@ -44,6 +45,7 @@ public UserDto.OnboardUserResponse onboardUser(UserDto.OnboardUserRequest onboar User user = User.builder() .name(name) + .email(email) .nickname(onboardUserRequest.getNickname()) .provider(provider) .providerId(providerId) @@ -62,4 +64,27 @@ public UserDto.OnboardUserResponse onboardUser(UserDto.OnboardUserRequest onboar // 액세스 토큰 반환 return UserDto.OnboardUserResponse.of(accessToken, refreshToken); } -} \ No newline at end of file + + // 유저 정보 조회 메서드 + public UserDto.GetUserProfileResponse getUserProfile(String authorizationHeader) { + User user = jwtUtil.getUserFromHeader(authorizationHeader); + + return UserDto.GetUserProfileResponse.of(user); + } + + // 유저 정보 수정 메서드 + @Transactional + public void updateUserProfile(String authorizationHeader, UserDto.UpdateUserProfileRequest updateUserProfileRequest) { + User user = jwtUtil.getUserFromHeader(authorizationHeader); + String nickname = updateUserProfileRequest.getNickname(); + + if (nickname == null) { + throw new UserException(UserErrorResult._NOT_FOUND_REQUEST_NICKNAME); + } + if (nickname.length() > NICKNAME_LENGTH_LIMIT) { + throw new UserException(UserErrorResult._NICKNAME_TOO_LONG); + } + user.updateNickName(nickname); + userRepository.save(user); + } +} diff --git a/src/main/java/side/onetime/util/JwtUtil.java b/src/main/java/side/onetime/util/JwtUtil.java index 982d7f4..303608f 100644 --- a/src/main/java/side/onetime/util/JwtUtil.java +++ b/src/main/java/side/onetime/util/JwtUtil.java @@ -44,13 +44,14 @@ public String generateAccessToken(Long userId, long expirationMillis) { } // 레지스터 토큰을 발급하는 메서드 - public String generateRegisterToken(String provider, String providerId, String name, long expirationMillis) { + public String generateRegisterToken(String provider, String providerId, String name, String email, long expirationMillis) { log.info("레지스터 토큰이 발행되었습니다."); return Jwts.builder() .claim("provider", provider) // 클레임에 provider 추가 .claim("providerId", providerId) // 클레임에 providerId 추가 .claim("name", name) // 클레임에 name 추가 + .claim("email", email) // 클레임에 email 추가 .issuedAt(new Date()) .expiration(new Date(System.currentTimeMillis() + expirationMillis)) .signWith(this.getSigningKey()) @@ -92,7 +93,7 @@ public Long getUserIdFromToken(String token) { } } - // 토큰에서 유저를 반환하는 메서드 + // 헤더에서 유저를 반환하는 메서드 public User getUserFromHeader(String authorizationHeader) { String token = getTokenFromHeader(authorizationHeader); @@ -121,14 +122,14 @@ public String getProviderFromToken(String token) { // 토큰에서 providerId를 반환하는 메서드 public String getProviderIdFromToken(String token) { try { - String userId = Jwts.parser() + String providerId = Jwts.parser() .verifyWith(this.getSigningKey()) .build() .parseSignedClaims(token) .getPayload() .get("providerId", String.class); log.info("providerId를 반환합니다."); - return userId; + return providerId; } catch (JwtException | IllegalArgumentException e) { // 토큰이 유효하지 않은 경우 log.warn("토큰에서 providerId를 반환하는 도중 에러가 발생했습니다."); @@ -139,14 +140,14 @@ public String getProviderIdFromToken(String token) { // 토큰에서 name을 반환하는 메서드 public String getNameFromToken(String token) { try { - String userId = Jwts.parser() + String name = Jwts.parser() .verifyWith(this.getSigningKey()) .build() .parseSignedClaims(token) .getPayload() .get("name", String.class); log.info("name을 반환합니다."); - return userId; + return name; } catch (JwtException | IllegalArgumentException e) { // 토큰이 유효하지 않은 경우 log.warn("토큰에서 이름을 반환하는 도중 에러가 발생했습니다."); @@ -154,6 +155,24 @@ public String getNameFromToken(String token) { } } + // 토큰에서 name을 반환하는 메서드 + public String getEmailFromToken(String token) { + try { + String email = Jwts.parser() + .verifyWith(this.getSigningKey()) + .build() + .parseSignedClaims(token) + .getPayload() + .get("email", String.class); + log.info("email을 반환합니다."); + return email; + } catch (JwtException | IllegalArgumentException e) { + // 토큰이 유효하지 않은 경우 + log.warn("토큰에서 이메일을 반환하는 도중 에러가 발생했습니다."); + throw new TokenException(TokenErrorResult._INVALID_TOKEN); + } + } + // Jwt 토큰의 유효기간을 확인하는 메서드 public boolean isTokenExpired(String token) { try {