diff --git a/src/main/java/everymeal/server/user/controller/UserController.java b/src/main/java/everymeal/server/user/controller/UserController.java index bf9ae2e..ebd967b 100644 --- a/src/main/java/everymeal/server/user/controller/UserController.java +++ b/src/main/java/everymeal/server/user/controller/UserController.java @@ -8,6 +8,7 @@ import everymeal.server.user.controller.dto.request.UserEmailAuthReq; import everymeal.server.user.controller.dto.request.UserEmailLoginReq; import everymeal.server.user.controller.dto.request.UserEmailSingReq; +import everymeal.server.user.controller.dto.request.UserProfileUpdateReq; import everymeal.server.user.controller.dto.response.UserEmailAuthRes; import everymeal.server.user.controller.dto.response.UserLoginRes; import everymeal.server.user.controller.dto.response.UserProfileRes; @@ -26,6 +27,7 @@ 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -145,6 +147,23 @@ public ApplicationResponse getUserProfile( return ApplicationResponse.ok(userService.getUserProfile(authenticatedUser)); } + @Auth(require = true) + @PutMapping("/profile") + @SecurityRequirement(name = "jwt-user-auth") + @Operation(summary = "인증된 사용자의 프로필 정보 수정", description = "인증된 사용자의 프로필 정보를 수정합니다.") + @ApiResponse( + responseCode = "409", + description = """ + (U0005)이미 등록된 닉네임입니다.
+ """, + content = @Content(schema = @Schema())) + public ApplicationResponse updateUserProfile( + @Parameter(hidden = true) @AuthUser AuthenticatedUser authenticatedUser, + @RequestBody UserProfileUpdateReq userProfileUpdateReq) { + return ApplicationResponse.ok( + userService.updateUserProfile(authenticatedUser, userProfileUpdateReq)); + } + private ResponseEntity> setRefreshToken( UserLoginRes response) { ResponseCookie cookie = diff --git a/src/main/java/everymeal/server/user/controller/dto/request/UserProfileUpdateReq.java b/src/main/java/everymeal/server/user/controller/dto/request/UserProfileUpdateReq.java new file mode 100644 index 0000000..8f7d334 --- /dev/null +++ b/src/main/java/everymeal/server/user/controller/dto/request/UserProfileUpdateReq.java @@ -0,0 +1,9 @@ +package everymeal.server.user.controller.dto.request; + + +import io.swagger.v3.oas.annotations.media.Schema; + +public record UserProfileUpdateReq( + @Schema(description = "닉네임", example = "연유크림") String nickName, + @Schema(description = "프로필 이미지 key", example = "user/bc90af33-bc6a-4009-bfc8-2c3efe0b16bd") + String profileImageKey) {} diff --git a/src/main/java/everymeal/server/user/controller/dto/response/UserProfileRes.java b/src/main/java/everymeal/server/user/controller/dto/response/UserProfileRes.java index a300117..f6079bf 100644 --- a/src/main/java/everymeal/server/user/controller/dto/response/UserProfileRes.java +++ b/src/main/java/everymeal/server/user/controller/dto/response/UserProfileRes.java @@ -5,11 +5,11 @@ public record UserProfileRes( Long userId, String nickName, String profileImgUrl, String universityName) { - public static UserProfileRes of(Map user) { + public static UserProfileRes of(Map user, String profileImgUrl) { return new UserProfileRes( (Long) user.get("userId"), (String) user.get("nickName"), - (String) user.get("profileImgUrl"), + profileImgUrl, (String) user.get("universityName")); } } diff --git a/src/main/java/everymeal/server/user/entity/User.java b/src/main/java/everymeal/server/user/entity/User.java index f4f427b..3d46361 100644 --- a/src/main/java/everymeal/server/user/entity/User.java +++ b/src/main/java/everymeal/server/user/entity/User.java @@ -65,4 +65,9 @@ public User(String nickname, String email, String profileImgUrl, University univ public void setEmail(String email) { this.email = email; } + + public void updateProfile(String nickname, String profileImgUrl) { + this.nickname = nickname; + this.profileImgUrl = profileImgUrl; + } } diff --git a/src/main/java/everymeal/server/user/service/UserService.java b/src/main/java/everymeal/server/user/service/UserService.java index 8710953..3e3ab43 100644 --- a/src/main/java/everymeal/server/user/service/UserService.java +++ b/src/main/java/everymeal/server/user/service/UserService.java @@ -5,6 +5,7 @@ import everymeal.server.user.controller.dto.request.UserEmailAuthReq; import everymeal.server.user.controller.dto.request.UserEmailLoginReq; import everymeal.server.user.controller.dto.request.UserEmailSingReq; +import everymeal.server.user.controller.dto.request.UserProfileUpdateReq; import everymeal.server.user.controller.dto.response.UserEmailAuthRes; import everymeal.server.user.controller.dto.response.UserLoginRes; import everymeal.server.user.controller.dto.response.UserProfileRes; @@ -22,4 +23,7 @@ public interface UserService { Boolean checkUser(String email); UserProfileRes getUserProfile(AuthenticatedUser authenticatedUser); + + Boolean updateUserProfile( + AuthenticatedUser authenticatedUser, UserProfileUpdateReq userProfileUpdateReq); } diff --git a/src/main/java/everymeal/server/user/service/UserServiceImpl.java b/src/main/java/everymeal/server/user/service/UserServiceImpl.java index 151cb9e..c23617d 100644 --- a/src/main/java/everymeal/server/user/service/UserServiceImpl.java +++ b/src/main/java/everymeal/server/user/service/UserServiceImpl.java @@ -12,6 +12,7 @@ import everymeal.server.user.controller.dto.request.UserEmailAuthReq; import everymeal.server.user.controller.dto.request.UserEmailLoginReq; import everymeal.server.user.controller.dto.request.UserEmailSingReq; +import everymeal.server.user.controller.dto.request.UserProfileUpdateReq; import everymeal.server.user.controller.dto.response.UserEmailAuthRes; import everymeal.server.user.controller.dto.response.UserLoginRes; import everymeal.server.user.controller.dto.response.UserProfileRes; @@ -21,6 +22,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Map; +import java.util.Optional; import java.util.Random; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -153,6 +155,24 @@ public Boolean checkUser(String email) { @Override public UserProfileRes getUserProfile(AuthenticatedUser authenticatedUser) { Map result = userMapper.getUserProfile(authenticatedUser.getIdx()); - return UserProfileRes.of(result); + String profileImgUrl = s3Util.getImgUrl((String) result.get("profileImgUrl")); + return UserProfileRes.of(result, profileImgUrl); + } + + @Override + @Transactional + public Boolean updateUserProfile( + AuthenticatedUser authenticatedUser, UserProfileUpdateReq request) { + User user = + userRepository + .findById(authenticatedUser.getIdx()) + .orElseThrow(() -> new ApplicationException(ExceptionList.USER_NOT_FOUND)); + // 닉네임 중복 검사 + Optional duplicatedNickName = userRepository.findByNickname(request.nickName()); + if (duplicatedNickName.isPresent() && user != duplicatedNickName.get()) { + throw new ApplicationException(ExceptionList.NICKNAME_ALREADY_EXIST); + } + user.updateProfile(request.nickName(), request.profileImageKey()); + return true; } } diff --git a/src/test/java/everymeal/server/user/controller/UserControllerTest.java b/src/test/java/everymeal/server/user/controller/UserControllerTest.java index bf45a26..c0c9926 100644 --- a/src/test/java/everymeal/server/user/controller/UserControllerTest.java +++ b/src/test/java/everymeal/server/user/controller/UserControllerTest.java @@ -4,6 +4,7 @@ import static org.mockito.BDDMockito.given; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -14,6 +15,7 @@ import everymeal.server.user.controller.dto.request.UserEmailAuthReq; import everymeal.server.user.controller.dto.request.UserEmailLoginReq; import everymeal.server.user.controller.dto.request.UserEmailSingReq; +import everymeal.server.user.controller.dto.request.UserProfileUpdateReq; import everymeal.server.user.controller.dto.response.UserLoginRes; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -134,4 +136,23 @@ void getUserProfile() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("OK")); } + + @DisplayName("인증된 유저의 프로필 정보 수정") + @Test + void updateUserProfile() throws Exception { + // given + UserProfileUpdateReq request = new UserProfileUpdateReq("연유크림", "imageKey"); + + given(userJwtResolver.resolveArgument(any(), any(), any(), any())) + .willReturn(AuthenticatedUser.builder().idx(1L).build()); + + // when-then + mockMvc.perform( + put("/api/v1/users/profile") + .content(objectMapper.writeValueAsString(request)) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("OK")); + } } diff --git a/src/test/java/everymeal/server/user/service/UserServiceImplTest.java b/src/test/java/everymeal/server/user/service/UserServiceImplTest.java index 52e4c39..3146ff3 100644 --- a/src/test/java/everymeal/server/user/service/UserServiceImplTest.java +++ b/src/test/java/everymeal/server/user/service/UserServiceImplTest.java @@ -15,6 +15,7 @@ import everymeal.server.user.controller.dto.request.UserEmailAuthReq; import everymeal.server.user.controller.dto.request.UserEmailLoginReq; import everymeal.server.user.controller.dto.request.UserEmailSingReq; +import everymeal.server.user.controller.dto.request.UserProfileUpdateReq; import everymeal.server.user.controller.dto.response.UserEmailAuthRes; import everymeal.server.user.controller.dto.response.UserLoginRes; import everymeal.server.user.entity.User; @@ -249,6 +250,70 @@ void getUserProfile() { assertEquals(response.nickName(), request.nickname()); } + @DisplayName("인증된 사용자의 프로필 정보 수정") + @Test + void updateUserProfile() { + // given + String token = jwtUtil.generateEmailToken("test@gmail.com", "12345"); + + University university = + universityRepository.save( + University.builder().name("명지대학교").campusName("인문캠퍼스").build()); + UserEmailSingReq request = + new UserEmailSingReq("nickname", token, "12345", university.getIdx(), "imageKey"); + + UserLoginRes userLoginRes = userService.signUp(request); + + AuthenticatedUser user = + jwtUtil.getAuthenticateUserFromAccessToken(userLoginRes.accessToken()); + + UserProfileUpdateReq userProfileUpdateReq = new UserProfileUpdateReq("연유크림", "imageKey2"); + + // when + var response = userService.updateUserProfile(user, userProfileUpdateReq); + + var updatedUser = userService.getUserProfile(user); + + // then + assertTrue(response); + assertEquals(updatedUser.nickName(), userProfileUpdateReq.nickName()); + assertEquals( + updatedUser.profileImgUrl(), + s3Util.getImgUrl(userProfileUpdateReq.profileImageKey())); + } + + @DisplayName("인증된 사용자의 프로필 정보 수정 - 닉네임 중복") + @Test + void updateUserProfile_duplicated() { + // given + String token = jwtUtil.generateEmailToken("test@gmail.com", "12345"); + + University university = + universityRepository.save( + University.builder().name("명지대학교").campusName("인문캠퍼스").build()); + UserEmailSingReq request = + new UserEmailSingReq("nickname", token, "12345", university.getIdx(), "imageKey"); + + UserLoginRes userLoginRes = userService.signUp(request); + + userRepository.saveAndFlush(createUser("hello@world.com", "연유크림")); + + AuthenticatedUser user = + jwtUtil.getAuthenticateUserFromAccessToken(userLoginRes.accessToken()); + + UserProfileUpdateReq userProfileUpdateReq = new UserProfileUpdateReq("연유크림", "imageKey2"); + + // when then + ApplicationException applicationException = + assertThrows( + ApplicationException.class, + () -> userService.updateUserProfile(user, userProfileUpdateReq)); + + assertEquals( + applicationException.getErrorCode(), + ExceptionList.NICKNAME_ALREADY_EXIST.getCODE()); + } + private User createUser(String email, String nickname) { return User.builder().email(email).nickname(nickname).build(); }