From 83a4aa49d835e0016e3a9e3ab9f52f32a574e055 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Sun, 25 Jan 2026 03:53:02 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat(user):=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=ED=85=8C=EC=9D=B4=EB=B8=94=EA=B3=BC=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=20=EA=B0=80=EC=9E=85=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/core/entity/SignupSourceType.java | 9 +++++++++ .../apu/saerok_server/domain/user/core/entity/User.java | 5 +++++ .../db/migration/V86__add_signup_source_to_users.sql | 3 +++ 3 files changed, 17 insertions(+) create mode 100644 src/main/java/org/devkor/apu/saerok_server/domain/user/core/entity/SignupSourceType.java create mode 100644 src/main/resources/db/migration/V86__add_signup_source_to_users.sql diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/core/entity/SignupSourceType.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/core/entity/SignupSourceType.java new file mode 100644 index 00000000..6696cf81 --- /dev/null +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/core/entity/SignupSourceType.java @@ -0,0 +1,9 @@ +package org.devkor.apu.saerok_server.domain.user.core.entity; + +public enum SignupSourceType { + INSTAGRAM, + OTHER_SNS, + FRIEND, + COMMUNITY, + ETC +} diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/core/entity/User.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/core/entity/User.java index 59e9382d..a7f2778d 100644 --- a/src/main/java/org/devkor/apu/saerok_server/domain/user/core/entity/User.java +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/core/entity/User.java @@ -40,6 +40,11 @@ public class User extends SoftDeletableAuditable { @Setter private SignupStatusType signupStatus; + @Setter + @Enumerated(EnumType.STRING) + @Column(name = "signup_source", nullable = true) + private SignupSourceType signupSource; + @Column(name = "joined_at") private OffsetDateTime joinedAt; diff --git a/src/main/resources/db/migration/V86__add_signup_source_to_users.sql b/src/main/resources/db/migration/V86__add_signup_source_to_users.sql new file mode 100644 index 00000000..e705f266 --- /dev/null +++ b/src/main/resources/db/migration/V86__add_signup_source_to_users.sql @@ -0,0 +1,3 @@ +-- 회원가입 경로 추적을 위한 컬럼 추가 +ALTER TABLE users + ADD COLUMN signup_source VARCHAR(30); From 103d1e5155377b795677a7ad8868155d1c344439 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Sun, 25 Jan 2026 05:05:02 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat(user):=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=99=84=EB=A3=8C=20API=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EC=9A=94=EC=B2=AD=20=EB=B0=94=EB=94=94=EC=99=80=20?= =?UTF-8?q?=EC=BB=A4=EB=A7=A8=EB=93=9C=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/api/dto/request/SignupCompleteRequest.java | 13 +++++++++++++ .../user/application/dto/SignupCompleteCommand.java | 10 ++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/main/java/org/devkor/apu/saerok_server/domain/user/api/dto/request/SignupCompleteRequest.java create mode 100644 src/main/java/org/devkor/apu/saerok_server/domain/user/application/dto/SignupCompleteCommand.java diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/api/dto/request/SignupCompleteRequest.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/api/dto/request/SignupCompleteRequest.java new file mode 100644 index 00000000..585853e5 --- /dev/null +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/api/dto/request/SignupCompleteRequest.java @@ -0,0 +1,13 @@ +package org.devkor.apu.saerok_server.domain.user.api.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "회원가입 완료 요청 DTO") +public record SignupCompleteRequest( + @Schema(description = "사용자 닉네임", example = "새록이") + String nickname, + + @Schema(description = "회원가입 경로", example = "INSTAGRAM", allowableValues = {"INSTAGRAM", "OTHER_SNS", "FRIEND", "COMMUNITY", "ETC"}) + String signupSource +) { +} diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/application/dto/SignupCompleteCommand.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/application/dto/SignupCompleteCommand.java new file mode 100644 index 00000000..7273a211 --- /dev/null +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/application/dto/SignupCompleteCommand.java @@ -0,0 +1,10 @@ +package org.devkor.apu.saerok_server.domain.user.application.dto; + +import org.devkor.apu.saerok_server.domain.user.core.entity.SignupSourceType; + +public record SignupCompleteCommand( + Long userId, + String nickname, + SignupSourceType signupSource +) { +} From b8714ceae77d727c7e0e726ff2e0b8c03af37f42 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Sun, 25 Jan 2026 05:05:28 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat(user):=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=99=84=EB=A3=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/api/UserController.java | 33 +++++++++++++++++++ .../user/application/UserCommandService.java | 30 +++++++++++++++-- .../domain/user/mapper/UserWebMapper.java | 4 +++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/api/UserController.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/api/UserController.java index e4d259f5..29815d7c 100644 --- a/src/main/java/org/devkor/apu/saerok_server/domain/user/api/UserController.java +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/api/UserController.java @@ -9,6 +9,7 @@ import jakarta.annotation.security.PermitAll; import lombok.RequiredArgsConstructor; import org.devkor.apu.saerok_server.domain.user.api.dto.request.ProfileImagePresignRequest; +import org.devkor.apu.saerok_server.domain.user.api.dto.request.SignupCompleteRequest; import org.devkor.apu.saerok_server.domain.user.api.dto.request.UpdateUserProfileRequest; import org.devkor.apu.saerok_server.domain.user.api.dto.response.ProfileImagePresignResponse; import org.devkor.apu.saerok_server.domain.user.api.dto.response.UpdateUserProfileResponse; @@ -109,6 +110,38 @@ public UpdateUserProfileResponse updateUserProfile( ); } + @PostMapping("/signup-complete") + @PreAuthorize("isAuthenticated()") + @ResponseStatus(HttpStatus.NO_CONTENT) + @Operation( + summary = "회원가입 완료", + security = @SecurityRequirement(name = "bearerAuth"), + description = """ + 회원가입을 완료하고 회원가입 경로를 기록합니다. + + 처리 내용: + - 닉네임 설정 및 유효성 검증 + - 회원가입 경로 기록 (INSTAGRAM, OTHER_SNS, FRIEND, COMMUNITY, ETC) + - 회원가입 상태를 COMPLETED로 변경 + + 주의사항: + - 회원가입이 이미 완료된 사용자는 호출할 수 없습니다 + """, + responses = { + @ApiResponse(responseCode = "204", description = "회원가입 완료 성공", content = @Content), + @ApiResponse(responseCode = "400", description = "회원가입 완료 실패 - 닉네임 정책 위반, 중복 완료 등", content = @Content), + @ApiResponse(responseCode = "401", description = "사용자 인증 실패", content = @Content), + } + ) + public void signupComplete( + @AuthenticationPrincipal UserPrincipal userPrincipal, + @RequestBody SignupCompleteRequest request + ) { + userCommandService.signupComplete( + userWebMapper.toSignupCompleteCommand(request, userPrincipal.getId()) + ); + } + @PostMapping("/me/profile-image/presign") @PreAuthorize("isAuthenticated()") @Operation( diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandService.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandService.java index b071999a..18040dc1 100644 --- a/src/main/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandService.java +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandService.java @@ -5,8 +5,10 @@ import org.devkor.apu.saerok_server.domain.user.api.dto.response.ProfileImagePresignResponse; import org.devkor.apu.saerok_server.domain.auth.core.repository.SocialAuthRepository; import org.devkor.apu.saerok_server.domain.user.api.dto.response.UpdateUserProfileResponse; +import org.devkor.apu.saerok_server.domain.user.application.dto.SignupCompleteCommand; import org.devkor.apu.saerok_server.domain.user.application.dto.UpdateUserProfileCommand; import org.devkor.apu.saerok_server.domain.user.application.helper.UserHardDeleteHelper; +import org.devkor.apu.saerok_server.domain.user.core.entity.SignupStatusType; import org.devkor.apu.saerok_server.domain.user.core.entity.User; import org.devkor.apu.saerok_server.domain.user.core.repository.UserRepository; import org.devkor.apu.saerok_server.domain.user.core.service.UserProfileImageUrlService; @@ -53,8 +55,6 @@ public UpdateUserProfileResponse updateUserProfile(UpdateUserProfileCommand comm throw new BadRequestException("프로필 사진 변경 시, profileImageContentType과 profileImageObjectKey 둘 다 있어야 합니다"); } - userSignupStatusService.tryCompleteSignup(user); - return new UpdateUserProfileResponse( user.getNickname(), user.getEmail(), @@ -63,6 +63,32 @@ public UpdateUserProfileResponse updateUserProfile(UpdateUserProfileCommand comm ); } + public void signupComplete(SignupCompleteCommand command) { + User user = userRepository.findById(command.userId()) + .orElseThrow(() -> new NotFoundException("존재하지 않는 사용자 id예요")); + + // 1) 회원가입 상태 검증 (중복 완료 방지) + if (user.getSignupStatus() == SignupStatusType.COMPLETED) { + throw new BadRequestException("이미 회원가입이 완료된 사용자입니다"); + } + + // 2) 닉네임 설정 + try { + userProfileUpdateService.changeNickname(user, command.nickname()); + } catch (IllegalArgumentException e) { + throw new BadRequestException("닉네임 정책을 만족하지 않습니다: " + e.getMessage()); + } + + // 3) 회원가입 경로 설정 + if (command.signupSource() == null) { + throw new BadRequestException("회원가입 경로는 필수입니다"); + } + user.setSignupSource(command.signupSource()); + + // 4) 회원가입 완료 상태로 변경 + userSignupStatusService.tryCompleteSignup(user); + } + public ProfileImagePresignResponse generateProfileImagePresignUrl(Long userId, String contentType) { userRepository.findById(userId).orElseThrow(() -> new NotFoundException("해당 사용자가 존재하지 않습니다.")); if (contentType == null || contentType.isEmpty()) { diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/mapper/UserWebMapper.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/mapper/UserWebMapper.java index c26f28e4..82d61eea 100644 --- a/src/main/java/org/devkor/apu/saerok_server/domain/user/mapper/UserWebMapper.java +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/mapper/UserWebMapper.java @@ -1,6 +1,8 @@ package org.devkor.apu.saerok_server.domain.user.mapper; +import org.devkor.apu.saerok_server.domain.user.api.dto.request.SignupCompleteRequest; import org.devkor.apu.saerok_server.domain.user.api.dto.request.UpdateUserProfileRequest; +import org.devkor.apu.saerok_server.domain.user.application.dto.SignupCompleteCommand; import org.devkor.apu.saerok_server.domain.user.application.dto.UpdateUserProfileCommand; import org.mapstruct.Mapper; import org.mapstruct.MappingConstants; @@ -11,4 +13,6 @@ public interface UserWebMapper { UpdateUserProfileCommand toUpdateUserProfileCommand(UpdateUserProfileRequest request, Long userId); + + SignupCompleteCommand toSignupCompleteCommand(SignupCompleteRequest request, Long userId); } From cee468c779a55bb8444f18772a619b9e0eb67918 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Sun, 25 Jan 2026 05:16:52 +0900 Subject: [PATCH 4/5] =?UTF-8?q?test(user):=20=EA=B4=80=EB=A0=A8=ED=95=B4?= =?UTF-8?q?=EC=84=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/UserCommandServiceTest.java | 111 +++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandServiceTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandServiceTest.java index d25ddead..ae8795da 100644 --- a/src/test/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandServiceTest.java +++ b/src/test/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandServiceTest.java @@ -6,8 +6,10 @@ import org.devkor.apu.saerok_server.domain.auth.infra.SocialRevoker; import org.devkor.apu.saerok_server.domain.user.api.dto.response.ProfileImagePresignResponse; import org.devkor.apu.saerok_server.domain.user.api.dto.response.UpdateUserProfileResponse; +import org.devkor.apu.saerok_server.domain.user.application.dto.SignupCompleteCommand; import org.devkor.apu.saerok_server.domain.user.application.dto.UpdateUserProfileCommand; import org.devkor.apu.saerok_server.domain.user.application.helper.UserHardDeleteHelper; +import org.devkor.apu.saerok_server.domain.user.core.entity.SignupSourceType; import org.devkor.apu.saerok_server.domain.user.core.entity.SignupStatusType; import org.devkor.apu.saerok_server.domain.user.core.entity.User; import org.devkor.apu.saerok_server.domain.user.core.repository.UserRepository; @@ -101,7 +103,6 @@ void nicknameOnly() { verify(userRepository).findById(42L); verify(userProfileUpdateService).changeNickname(user, "newNick"); - verify(userSignupStatusService).tryCompleteSignup(user); verify(userProfileImageUrlService).getProfileImageUrlFor(user); verify(userProfileImageUrlService).getProfileThumbnailImageUrlFor(user); verifyNoMoreInteractions(userProfileUpdateService); @@ -296,4 +297,112 @@ void userNotFound() { verifyNoInteractions(userHardDeleteHelper, kakaoRevoker, appleRevoker); } } + + @Nested + class SignupComplete { + + @Test + @DisplayName("정상 플로우: 닉네임 설정, 회원가입 경로 설정, 상태 COMPLETED로 변경") + void ok() { + long userId = 42L; + User user = new User(); + ReflectionTestUtils.setField(user, "id", userId); + user.setSignupStatus(SignupStatusType.PROFILE_REQUIRED); + ReflectionTestUtils.setField(user, "email", "test@example.com"); + + given(userRepository.findById(userId)).willReturn(Optional.of(user)); + doAnswer(invocation -> { + User u = invocation.getArgument(0); + String newNick = invocation.getArgument(1); + u.setNickname(newNick); + return null; + }).when(userProfileUpdateService).changeNickname(same(user), eq("새록이")); + + SignupCompleteCommand cmd = new SignupCompleteCommand(userId, "새록이", SignupSourceType.INSTAGRAM); + + sut.signupComplete(cmd); + + verify(userRepository).findById(userId); + verify(userProfileUpdateService).changeNickname(user, "새록이"); + verify(userSignupStatusService).tryCompleteSignup(user); + assertThat(user.getNickname()).isEqualTo("새록이"); + assertThat(user.getSignupSource()).isEqualTo(SignupSourceType.INSTAGRAM); + } + + @Test + @DisplayName("이미 회원가입 완료된 사용자는 BadRequestException") + void alreadyCompleted_throwsBadRequest() { + long userId = 42L; + User user = new User(); + ReflectionTestUtils.setField(user, "id", userId); + user.setSignupStatus(SignupStatusType.COMPLETED); + + given(userRepository.findById(userId)).willReturn(Optional.of(user)); + + SignupCompleteCommand cmd = new SignupCompleteCommand(userId, "새록이", SignupSourceType.INSTAGRAM); + + assertThatThrownBy(() -> sut.signupComplete(cmd)) + .isInstanceOf(BadRequestException.class) + .hasMessageContaining("이미 회원가입이 완료된 사용자입니다"); + + verifyNoInteractions(userProfileUpdateService, userSignupStatusService); + } + + @Test + @DisplayName("닉네임 정책 위반 시 BadRequestException") + void invalidNickname_throwsBadRequest() { + long userId = 42L; + User user = new User(); + ReflectionTestUtils.setField(user, "id", userId); + user.setSignupStatus(SignupStatusType.PROFILE_REQUIRED); + + given(userRepository.findById(userId)).willReturn(Optional.of(user)); + doThrow(new IllegalArgumentException("닉네임 정책 위반")) + .when(userProfileUpdateService).changeNickname(user, "badnick"); + + SignupCompleteCommand cmd = new SignupCompleteCommand(userId, "badnick", SignupSourceType.INSTAGRAM); + + assertThatThrownBy(() -> sut.signupComplete(cmd)) + .isInstanceOf(BadRequestException.class) + .hasMessageContaining("닉네임 정책을 만족하지 않습니다"); + } + + @Test + @DisplayName("회원가입 경로 null이면 BadRequestException") + void nullSignupSource_throwsBadRequest() { + long userId = 42L; + User user = new User(); + ReflectionTestUtils.setField(user, "id", userId); + user.setSignupStatus(SignupStatusType.PROFILE_REQUIRED); + + given(userRepository.findById(userId)).willReturn(Optional.of(user)); + doAnswer(invocation -> { + User u = invocation.getArgument(0); + String newNick = invocation.getArgument(1); + u.setNickname(newNick); + return null; + }).when(userProfileUpdateService).changeNickname(same(user), eq("새록이")); + + SignupCompleteCommand cmd = new SignupCompleteCommand(userId, "새록이", null); + + assertThatThrownBy(() -> sut.signupComplete(cmd)) + .isInstanceOf(BadRequestException.class) + .hasMessageContaining("회원가입 경로는 필수입니다"); + + verifyNoInteractions(userSignupStatusService); + } + + @Test + @DisplayName("사용자 없음 시 404") + void userNotFound() { + given(userRepository.findById(999L)).willReturn(Optional.empty()); + + SignupCompleteCommand cmd = new SignupCompleteCommand(999L, "새록이", SignupSourceType.INSTAGRAM); + + assertThatThrownBy(() -> sut.signupComplete(cmd)) + .isInstanceOf(NotFoundException.class); + + verifyNoInteractions(userProfileUpdateService, userSignupStatusService); + } + } } From 29f6616a50054528dd0c01d050586f45e747b323 Mon Sep 17 00:00:00 2001 From: pizzazoa Date: Sun, 25 Jan 2026 16:41:36 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refactor(user):=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EC=9D=84=20=EB=AA=85=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/api/UserController.java | 9 +++++---- .../dto/response/SignupCompleteResponse.java | 17 +++++++++++++++++ .../user/application/UserCommandService.java | 5 ++++- .../application/UserCommandServiceTest.java | 11 ++++++++++- 4 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/devkor/apu/saerok_server/domain/user/api/dto/response/SignupCompleteResponse.java diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/api/UserController.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/api/UserController.java index 29815d7c..df931a60 100644 --- a/src/main/java/org/devkor/apu/saerok_server/domain/user/api/UserController.java +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/api/UserController.java @@ -12,6 +12,7 @@ import org.devkor.apu.saerok_server.domain.user.api.dto.request.SignupCompleteRequest; import org.devkor.apu.saerok_server.domain.user.api.dto.request.UpdateUserProfileRequest; import org.devkor.apu.saerok_server.domain.user.api.dto.response.ProfileImagePresignResponse; +import org.devkor.apu.saerok_server.domain.user.api.dto.response.SignupCompleteResponse; import org.devkor.apu.saerok_server.domain.user.api.dto.response.UpdateUserProfileResponse; import org.devkor.apu.saerok_server.domain.user.api.response.CheckNicknameResponse; import org.devkor.apu.saerok_server.domain.user.api.response.UserInfoResponse; @@ -112,7 +113,6 @@ public UpdateUserProfileResponse updateUserProfile( @PostMapping("/signup-complete") @PreAuthorize("isAuthenticated()") - @ResponseStatus(HttpStatus.NO_CONTENT) @Operation( summary = "회원가입 완료", security = @SecurityRequirement(name = "bearerAuth"), @@ -128,16 +128,17 @@ public UpdateUserProfileResponse updateUserProfile( - 회원가입이 이미 완료된 사용자는 호출할 수 없습니다 """, responses = { - @ApiResponse(responseCode = "204", description = "회원가입 완료 성공", content = @Content), + @ApiResponse(responseCode = "200", description = "회원가입 완료 성공", + content = @Content(schema = @Schema(implementation = SignupCompleteResponse.class))), @ApiResponse(responseCode = "400", description = "회원가입 완료 실패 - 닉네임 정책 위반, 중복 완료 등", content = @Content), @ApiResponse(responseCode = "401", description = "사용자 인증 실패", content = @Content), } ) - public void signupComplete( + public SignupCompleteResponse signupComplete( @AuthenticationPrincipal UserPrincipal userPrincipal, @RequestBody SignupCompleteRequest request ) { - userCommandService.signupComplete( + return userCommandService.signupComplete( userWebMapper.toSignupCompleteCommand(request, userPrincipal.getId()) ); } diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/api/dto/response/SignupCompleteResponse.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/api/dto/response/SignupCompleteResponse.java new file mode 100644 index 00000000..16bb4118 --- /dev/null +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/api/dto/response/SignupCompleteResponse.java @@ -0,0 +1,17 @@ +package org.devkor.apu.saerok_server.domain.user.api.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import org.devkor.apu.saerok_server.domain.user.core.entity.SignupStatusType; + +@Schema(description = "회원가입 완료 응답 DTO") +public record SignupCompleteResponse( + @Schema(description = "사용자 ID", example = "42") + Long userId, + + @Schema(description = "회원가입 상태", example = "COMPLETED") + SignupStatusType signupStatus, + + @Schema(description = "성공 여부", example = "true") + boolean success +) { +} diff --git a/src/main/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandService.java b/src/main/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandService.java index 18040dc1..46d83a38 100644 --- a/src/main/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandService.java +++ b/src/main/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandService.java @@ -4,6 +4,7 @@ import org.devkor.apu.saerok_server.domain.auth.infra.SocialRevoker; import org.devkor.apu.saerok_server.domain.user.api.dto.response.ProfileImagePresignResponse; import org.devkor.apu.saerok_server.domain.auth.core.repository.SocialAuthRepository; +import org.devkor.apu.saerok_server.domain.user.api.dto.response.SignupCompleteResponse; import org.devkor.apu.saerok_server.domain.user.api.dto.response.UpdateUserProfileResponse; import org.devkor.apu.saerok_server.domain.user.application.dto.SignupCompleteCommand; import org.devkor.apu.saerok_server.domain.user.application.dto.UpdateUserProfileCommand; @@ -63,7 +64,7 @@ public UpdateUserProfileResponse updateUserProfile(UpdateUserProfileCommand comm ); } - public void signupComplete(SignupCompleteCommand command) { + public SignupCompleteResponse signupComplete(SignupCompleteCommand command) { User user = userRepository.findById(command.userId()) .orElseThrow(() -> new NotFoundException("존재하지 않는 사용자 id예요")); @@ -87,6 +88,8 @@ public void signupComplete(SignupCompleteCommand command) { // 4) 회원가입 완료 상태로 변경 userSignupStatusService.tryCompleteSignup(user); + + return new SignupCompleteResponse(user.getId(), user.getSignupStatus(), true); } public ProfileImagePresignResponse generateProfileImagePresignUrl(Long userId, String contentType) { diff --git a/src/test/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandServiceTest.java b/src/test/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandServiceTest.java index ae8795da..86d19d6d 100644 --- a/src/test/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandServiceTest.java +++ b/src/test/java/org/devkor/apu/saerok_server/domain/user/application/UserCommandServiceTest.java @@ -5,6 +5,7 @@ import org.devkor.apu.saerok_server.domain.auth.core.repository.SocialAuthRepository; import org.devkor.apu.saerok_server.domain.auth.infra.SocialRevoker; import org.devkor.apu.saerok_server.domain.user.api.dto.response.ProfileImagePresignResponse; +import org.devkor.apu.saerok_server.domain.user.api.dto.response.SignupCompleteResponse; import org.devkor.apu.saerok_server.domain.user.api.dto.response.UpdateUserProfileResponse; import org.devkor.apu.saerok_server.domain.user.application.dto.SignupCompleteCommand; import org.devkor.apu.saerok_server.domain.user.application.dto.UpdateUserProfileCommand; @@ -317,11 +318,19 @@ void ok() { u.setNickname(newNick); return null; }).when(userProfileUpdateService).changeNickname(same(user), eq("새록이")); + doAnswer(invocation -> { + User u = invocation.getArgument(0); + u.setSignupStatus(SignupStatusType.COMPLETED); + return null; + }).when(userSignupStatusService).tryCompleteSignup(same(user)); SignupCompleteCommand cmd = new SignupCompleteCommand(userId, "새록이", SignupSourceType.INSTAGRAM); - sut.signupComplete(cmd); + SignupCompleteResponse response = sut.signupComplete(cmd); + assertThat(response.success()).isTrue(); + assertThat(response.userId()).isEqualTo(userId); + assertThat(response.signupStatus()).isEqualTo(SignupStatusType.COMPLETED); verify(userRepository).findById(userId); verify(userProfileUpdateService).changeNickname(user, "새록이"); verify(userSignupStatusService).tryCompleteSignup(user);