From b2f38213da4629f1a60ec477fb6af84338069eb9 Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 21:50:44 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=20=EC=95=BD=EA=B4=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/controller/AuthController.java | 10 +++++++++- .../domain/dto/request/AuthRequest.java | 10 ++++++++++ .../service/controller/AuthControllerTest.java | 18 ++++++++++++++---- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/controller/AuthController.java b/auth-server/src/main/java/com/truve/platform/auth/service/controller/AuthController.java index e2134d5f..96409fbb 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/controller/AuthController.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/controller/AuthController.java @@ -38,7 +38,15 @@ public class AuthController { public ApiResult signUp( @RequestBody @Valid AuthRequest.SignUp request ) { - authService.signUp(request.getEmail(), request.getPassword()); + authService.signUp( + request.getEmail(), + request.getPassword(), + request.isServiceTermsAgreed(), + request.isElectronicFinanceTermsAgreed(), + request.isPrivacyCollectionAgreed(), + request.isMarketingInfoAgreed(), + request.isOver14Agreed() + ); return ApiResult.ok(); } diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/domain/dto/request/AuthRequest.java b/auth-server/src/main/java/com/truve/platform/auth/service/domain/dto/request/AuthRequest.java index ea59704c..6cb45532 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/domain/dto/request/AuthRequest.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/domain/dto/request/AuthRequest.java @@ -18,6 +18,16 @@ public static class SignUp { @NotBlank private String password; + + private boolean serviceTermsAgreed; + + private boolean electronicFinanceTermsAgreed; + + private boolean privacyCollectionAgreed; + + private boolean marketingInfoAgreed; + + private boolean over14Agreed; } @Getter diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java index 29ef38ed..915e0136 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java @@ -45,7 +45,12 @@ class AuthControllerTest { String body = """ { "email": "new@test.com", - "password": "password123" + "password": "password123", + "serviceTermsAgreed": true, + "electronicFinanceTermsAgreed": true, + "privacyCollectionAgreed": true, + "marketingInfoAgreed": false, + "over14Agreed": true } """; @@ -57,7 +62,7 @@ class AuthControllerTest { // then resultActions.andExpect(status().isOk()) .andExpect(jsonPath("$.code").value("ok")); - verify(authService).signUp("new@test.com", "password123"); + verify(authService).signUp("new@test.com", "password123", true, true, true, false, true); } @Test @@ -67,11 +72,16 @@ class AuthControllerTest { String body = """ { "email": "dup@test.com", - "password": "password123" + "password": "password123", + "serviceTermsAgreed": true, + "electronicFinanceTermsAgreed": true, + "privacyCollectionAgreed": true, + "marketingInfoAgreed": false, + "over14Agreed": true } """; willThrow(new CustomException(ErrorCode.ALREADY_EXISTS_EMAIL)) - .given(authService).signUp(anyString(), anyString()); + .given(authService).signUp(anyString(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean()); // when ResultActions resultActions = mockMvc.perform(post("/api/auth/sign-up") From ba41d3cc3ceafbe54916850b9ea20a13b3c5d2c3 Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 21:50:56 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EB=82=B4=20=ED=95=84=EC=9A=94=20=EC=95=BD?= =?UTF-8?q?=EA=B4=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/auth/service/service/AuthService.java | 14 +++++++++++++- .../auth/service/service/AuthServiceTest.java | 12 +++++++++--- .../truve/platform/common/exception/ErrorCode.java | 1 + 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java b/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java index 1ef4ad9c..95cc19ea 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java @@ -99,7 +99,15 @@ public void logout(String accessToken) { } @Transactional - public void signUp(String email, String password) { + public void signUp( + String email, + String password, + boolean serviceTermsAgreed, + boolean electronicFinanceTermsAgreed, + boolean privacyCollectionAgreed, + boolean marketingInfoAgreed, + boolean over14Agreed + ) { String verifiedAt = emailVerificationRepository.isVerifiedEmail(email); Preconditions.validate(!(verifiedAt == null || verifiedAt.isBlank()), ErrorCode.NOT_VERIFIED_EMAIL); @@ -108,6 +116,10 @@ public void signUp(String email, String password) { !userRepository.existsByEmail(email), ErrorCode.ALREADY_EXISTS_EMAIL ); + Preconditions.validate( + serviceTermsAgreed && electronicFinanceTermsAgreed && privacyCollectionAgreed && over14Agreed, + ErrorCode.REQUIRED_TERMS_NOT_AGREED + ); String encodedPassword = passwordEncoder.encode(password); diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java index 7cc7b382..48aa6f31 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java @@ -246,7 +246,7 @@ class SignUpTest { }); // when - authService.signUp(email, password); + authService.signUp(email, password, true, true, true, false, true); // then ArgumentCaptor savedUserCaptor = ArgumentCaptor.forClass(User.class); @@ -275,7 +275,10 @@ class SignUpTest { given(emailVerificationRepository.isVerifiedEmail(email)).willReturn(""); // when - CustomException exception = assertThrows(CustomException.class, () -> authService.signUp(email, password)); + CustomException exception = assertThrows( + CustomException.class, + () -> authService.signUp(email, password, true, true, true, false, true) + ); // then assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.NOT_VERIFIED_EMAIL); @@ -294,7 +297,10 @@ class SignUpTest { given(userRepository.existsByEmail(email)).willReturn(true); // when - CustomException exception = assertThrows(CustomException.class, () -> authService.signUp(email, password)); + CustomException exception = assertThrows( + CustomException.class, + () -> authService.signUp(email, password, true, true, true, false, true) + ); // then assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.ALREADY_EXISTS_EMAIL); diff --git a/common/src/main/java/com/truve/platform/common/exception/ErrorCode.java b/common/src/main/java/com/truve/platform/common/exception/ErrorCode.java index 5f15f74b..0d4f1b96 100644 --- a/common/src/main/java/com/truve/platform/common/exception/ErrorCode.java +++ b/common/src/main/java/com/truve/platform/common/exception/ErrorCode.java @@ -18,6 +18,7 @@ public enum ErrorCode { ALREADY_VERIFIED_EMAIL(HttpStatus.BAD_REQUEST, "이미 인증된 이메일입니다.", "A07"), INVALID_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, "리프레시 토큰이 올바르지 않습니다.", "A08"), KAKAO_USERINFO_FAILED(HttpStatus.BAD_REQUEST, "카카오 사용자 정보를 가져오지 못했습니다.", "A09"), + REQUIRED_TERMS_NOT_AGREED(HttpStatus.BAD_REQUEST, "필수 약관 동의가 필요합니다.", "A10"), NOT_FOUND_PAYMENT(HttpStatus.BAD_REQUEST, "존재하지 않는 결제입니다.", "P01"), INVALID_PAYMENT_AMOUNT(HttpStatus.BAD_REQUEST, "유효하지 않은 결제 금액입니다.", "P02"), From 5952750a71bea883a4929048510e79d9e687c888 Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 21:54:20 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20User=20entity=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=BC=20=EC=95=BD=EA=B4=80=20=EB=8F=99=EC=9D=98=20=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/domain/entity/User.java | 57 ++++++++++++++++++- .../auth/service/domain/entity/UserTest.java | 12 +++- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/domain/entity/User.java b/auth-server/src/main/java/com/truve/platform/auth/service/domain/entity/User.java index 1ec4192b..80e0a390 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/domain/entity/User.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/domain/entity/User.java @@ -47,10 +47,38 @@ public class User extends BaseEntity { private String oAuthRefreshToken; + @Column(nullable = false) + private boolean serviceTermsAgreed; + + @Column(nullable = false) + private boolean electronicFinanceTermsAgreed; + + @Column(nullable = false) + private boolean privacyCollectionAgreed; + + @Column(nullable = false) + private boolean marketingInfoAgreed; + + @Column(nullable = false) + private boolean over14Agreed; + @Builder - private User(UUID publicId, String email, String password, AuthProvider provider, - UserRole role, String oAuthUserId, String oAuthAccessToken, String oAuthRefreshToken) { + private User( + UUID publicId, + String email, + String password, + AuthProvider provider, + UserRole role, + String oAuthUserId, + String oAuthAccessToken, + String oAuthRefreshToken, + boolean serviceTermsAgreed, + boolean electronicFinanceTermsAgreed, + boolean privacyCollectionAgreed, + boolean marketingInfoAgreed, + boolean over14Agreed + ) { this.publicId = publicId; this.email = email; this.password = password; @@ -59,15 +87,33 @@ private User(UUID publicId, String email, String password, AuthProvider provider this.oAuthUserId = oAuthUserId; this.oAuthAccessToken = oAuthAccessToken; this.oAuthRefreshToken = oAuthRefreshToken; + this.serviceTermsAgreed = serviceTermsAgreed; + this.electronicFinanceTermsAgreed = electronicFinanceTermsAgreed; + this.privacyCollectionAgreed = privacyCollectionAgreed; + this.marketingInfoAgreed = marketingInfoAgreed; + this.over14Agreed = over14Agreed; } - public static User createLocalUser(String email, String password) { + public static User createLocalUser( + String email, + String password, + boolean serviceTermsAgreed, + boolean electronicFinanceTermsAgreed, + boolean privacyCollectionAgreed, + boolean marketingInfoAgreed, + boolean over14Agreed + ) { return User.builder() .publicId(UUID.randomUUID()) .email(email) .password(password) .provider(AuthProvider.LOCAL) .role(UserRole.MEMBER) + .serviceTermsAgreed(serviceTermsAgreed) + .electronicFinanceTermsAgreed(electronicFinanceTermsAgreed) + .privacyCollectionAgreed(privacyCollectionAgreed) + .marketingInfoAgreed(marketingInfoAgreed) + .over14Agreed(over14Agreed) .build(); } @@ -86,6 +132,11 @@ public static User createOAuthUser( .oAuthUserId(oAuthUserId) .oAuthAccessToken(oAuthAccessToken) .oAuthRefreshToken(oAuthRefreshToken) + .serviceTermsAgreed(false) + .electronicFinanceTermsAgreed(false) + .privacyCollectionAgreed(false) + .marketingInfoAgreed(false) + .over14Agreed(false) .build(); } } diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/domain/entity/UserTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/domain/entity/UserTest.java index 10d3056c..27b9f2b7 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/domain/entity/UserTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/domain/entity/UserTest.java @@ -28,7 +28,7 @@ class CreateLocalUserTest { @DisplayName("createLocalUser 호출 시 LOCAL/MEMBER 권한으로 사용자를 생성한다.") void createLocalUser_success() { // when - User user = User.createLocalUser(EMAIL, PASSWORD); + User user = User.createLocalUser(EMAIL, PASSWORD, true, true, true, false, true); // then assertAll( @@ -37,6 +37,11 @@ void createLocalUser_success() { () -> assertThat(user.getPassword()).isEqualTo(PASSWORD), () -> assertThat(user.getProvider()).isEqualTo(AuthProvider.LOCAL), () -> assertThat(user.getRole()).isEqualTo(UserRole.MEMBER), + () -> assertThat(user.isServiceTermsAgreed()).isTrue(), + () -> assertThat(user.isElectronicFinanceTermsAgreed()).isTrue(), + () -> assertThat(user.isPrivacyCollectionAgreed()).isTrue(), + () -> assertThat(user.isMarketingInfoAgreed()).isFalse(), + () -> assertThat(user.isOver14Agreed()).isTrue(), () -> assertThat(user.getOAuthUserId()).isNull(), () -> assertThat(user.getOAuthAccessToken()).isNull(), () -> assertThat(user.getOAuthRefreshToken()).isNull() @@ -67,6 +72,11 @@ void createOAuthUser_success() { () -> assertThat(user.getPassword()).isNull(), () -> assertThat(user.getProvider()).isEqualTo(AuthProvider.KAKAO), () -> assertThat(user.getRole()).isEqualTo(UserRole.MEMBER), + () -> assertThat(user.isServiceTermsAgreed()).isFalse(), + () -> assertThat(user.isElectronicFinanceTermsAgreed()).isFalse(), + () -> assertThat(user.isPrivacyCollectionAgreed()).isFalse(), + () -> assertThat(user.isMarketingInfoAgreed()).isFalse(), + () -> assertThat(user.isOver14Agreed()).isFalse(), () -> assertThat(user.getOAuthUserId()).isEqualTo(OAUTH_USER_ID), () -> assertThat(user.getOAuthAccessToken()).isEqualTo(OAUTH_ACCESS_TOKEN), () -> assertThat(user.getOAuthRefreshToken()).isEqualTo(OAUTH_REFRESH_TOKEN) From e3d5b2d1f86e0f64e0f092ae13f69078048fdc41 Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 21:54:53 +0900 Subject: [PATCH 04/10] feat: User create agreed --- .../platform/auth/service/service/AuthService.java | 10 +++++++++- .../platform/auth/service/service/AuthServiceTest.java | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java b/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java index 95cc19ea..5240a6f9 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java @@ -123,7 +123,15 @@ public void signUp( String encodedPassword = passwordEncoder.encode(password); - User user = User.createLocalUser(email, encodedPassword); + User user = User.createLocalUser( + email, + encodedPassword, + serviceTermsAgreed, + electronicFinanceTermsAgreed, + privacyCollectionAgreed, + marketingInfoAgreed, + over14Agreed + ); userRepository.save(user); emailVerificationRepository.deleteVerifiedEmail(email); diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java index 48aa6f31..39ebe205 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java @@ -53,7 +53,7 @@ class AuthServiceTest { private AuthService authService; private User createUser(Long id, String email, String encodedPassword) { - User user = User.createLocalUser(email, encodedPassword); + User user = User.createLocalUser(email, encodedPassword, true, true, true, false, true); ReflectionTestUtils.setField(user, "id", id); return user; } @@ -260,6 +260,11 @@ class SignUpTest { assertThat(savedUserCaptor.getValue().getEmail()).isEqualTo(email); assertThat(savedUserCaptor.getValue().getPassword()).isEqualTo("encoded"); assertThat(savedUserCaptor.getValue().getRole()).isEqualTo(UserRole.MEMBER); + assertThat(savedUserCaptor.getValue().isServiceTermsAgreed()).isTrue(); + assertThat(savedUserCaptor.getValue().isElectronicFinanceTermsAgreed()).isTrue(); + assertThat(savedUserCaptor.getValue().isPrivacyCollectionAgreed()).isTrue(); + assertThat(savedUserCaptor.getValue().isMarketingInfoAgreed()).isFalse(); + assertThat(savedUserCaptor.getValue().isOver14Agreed()).isTrue(); assertThat(savedUserCaptor.getValue().getPublicId()).isInstanceOf(UUID.class); assertThat(keyCaptor.getValue()).isEqualTo(savedUserCaptor.getValue().getPublicId().toString()); assertThat(eventCaptor.getValue()).isInstanceOf(UserSignedUpEvent.class); From d909c7e13f74b8577fd2ace04cc26c63d81bbc08 Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 22:00:48 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20=EC=95=BD=EA=B4=80=20=EB=8F=99?= =?UTF-8?q?=EC=9D=98=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 --- .../controller/AuthControllerTest.java | 29 +++++++++++++++++++ .../auth/service/service/AuthServiceTest.java | 24 +++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java index 915e0136..494299b6 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java @@ -94,6 +94,35 @@ class AuthControllerTest { .andExpect(jsonPath("$.code").value(ErrorCode.ALREADY_EXISTS_EMAIL.getCode())); } + @Test + @DisplayName("필수 약관 미동의로 회원가입 요청하면 400을 반환한다.") + void 회원가입_실패_필수_약관_미동의() throws Exception { + // given + String body = """ + { + "email": "new@test.com", + "password": "password123", + "serviceTermsAgreed": true, + "electronicFinanceTermsAgreed": false, + "privacyCollectionAgreed": true, + "marketingInfoAgreed": false, + "over14Agreed": true + } + """; + willThrow(new CustomException(ErrorCode.REQUIRED_TERMS_NOT_AGREED)) + .given(authService).signUp(anyString(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean()); + + // when + ResultActions resultActions = mockMvc.perform(post("/api/auth/sign-up") + .contentType(MediaType.APPLICATION_JSON) + .content(body)); + + // then + resultActions.andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorType").value("CLIENT_ERROR")) + .andExpect(jsonPath("$.code").value(ErrorCode.REQUIRED_TERMS_NOT_AGREED.getCode())); + } + @Test @DisplayName("로그인에 성공하면 accessToken을 반환하고 refreshToken 쿠키를 설정한다.") void 로그인_성공() throws Exception { diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java index 39ebe205..d6351d1a 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java @@ -313,5 +313,29 @@ class SignUpTest { verify(emailVerificationRepository, never()).deleteVerifiedEmail(anyString()); verify(userSignedUpEventPublisher, never()).publish(anyString(), any()); } + + @Test + @DisplayName("필수 약관에 동의하지 않으면 예외가 발생한다.") + void 회원가입_실패_필수_약관_미동의() { + // given + String email = "new@test.com"; + String password = "plain"; + + given(emailVerificationRepository.isVerifiedEmail(email)).willReturn("1700000000000"); + given(userRepository.existsByEmail(email)).willReturn(false); + + // when + CustomException exception = assertThrows( + CustomException.class, + () -> authService.signUp(email, password, true, false, true, false, true) + ); + + // then + assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.REQUIRED_TERMS_NOT_AGREED); + verify(passwordEncoder, never()).encode(anyString()); + verify(userRepository, never()).save(any(User.class)); + verify(emailVerificationRepository, never()).deleteVerifiedEmail(anyString()); + verify(userSignedUpEventPublisher, never()).publish(anyString(), any()); + } } } From c24fabbc1b6e4f9717281606d9f9b09df148dcef Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 22:08:04 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../truve/platform/auth/service/domain/entity/User.java | 8 ++++++++ .../platform/auth/service/domain/entity/UserTest.java | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/domain/entity/User.java b/auth-server/src/main/java/com/truve/platform/auth/service/domain/entity/User.java index 80e0a390..4bf6cd19 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/domain/entity/User.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/domain/entity/User.java @@ -30,6 +30,9 @@ public class User extends BaseEntity { @Column(nullable = false, unique = true) private String email; + @Column(unique = true) + private String nickname; + // TODO: 기획 논의 이후 비밀번호 정책 정규식 설정 private String password; @@ -67,6 +70,7 @@ public class User extends BaseEntity { private User( UUID publicId, String email, + String nickname, String password, AuthProvider provider, UserRole role, @@ -81,6 +85,7 @@ private User( ) { this.publicId = publicId; this.email = email; + this.nickname = nickname; this.password = password; this.provider = provider; this.role = role; @@ -96,6 +101,7 @@ private User( public static User createLocalUser( String email, + String nickname, String password, boolean serviceTermsAgreed, boolean electronicFinanceTermsAgreed, @@ -106,6 +112,7 @@ public static User createLocalUser( return User.builder() .publicId(UUID.randomUUID()) .email(email) + .nickname(nickname) .password(password) .provider(AuthProvider.LOCAL) .role(UserRole.MEMBER) @@ -127,6 +134,7 @@ public static User createOAuthUser( return User.builder() .publicId(UUID.randomUUID()) .email(email) + .nickname(null) .provider(provider) .role(UserRole.MEMBER) .oAuthUserId(oAuthUserId) diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/domain/entity/UserTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/domain/entity/UserTest.java index 27b9f2b7..de8b68e2 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/domain/entity/UserTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/domain/entity/UserTest.java @@ -15,6 +15,7 @@ class UserTest { private static final String EMAIL = "test@truve.com"; + private static final String NICKNAME = "tester"; private static final String PASSWORD = "encoded-password"; private static final String OAUTH_USER_ID = "oauth-user-id"; private static final String OAUTH_ACCESS_TOKEN = "oauth-access-token"; @@ -28,12 +29,13 @@ class CreateLocalUserTest { @DisplayName("createLocalUser 호출 시 LOCAL/MEMBER 권한으로 사용자를 생성한다.") void createLocalUser_success() { // when - User user = User.createLocalUser(EMAIL, PASSWORD, true, true, true, false, true); + User user = User.createLocalUser(EMAIL, NICKNAME, PASSWORD, true, true, true, false, true); // then assertAll( () -> assertThat(user.getPublicId()).isInstanceOf(UUID.class), () -> assertThat(user.getEmail()).isEqualTo(EMAIL), + () -> assertThat(user.getNickname()).isEqualTo(NICKNAME), () -> assertThat(user.getPassword()).isEqualTo(PASSWORD), () -> assertThat(user.getProvider()).isEqualTo(AuthProvider.LOCAL), () -> assertThat(user.getRole()).isEqualTo(UserRole.MEMBER), @@ -69,6 +71,7 @@ void createOAuthUser_success() { assertAll( () -> assertThat(user.getPublicId()).isInstanceOf(UUID.class), () -> assertThat(user.getEmail()).isEqualTo(EMAIL), + () -> assertThat(user.getNickname()).isNull(), () -> assertThat(user.getPassword()).isNull(), () -> assertThat(user.getProvider()).isEqualTo(AuthProvider.KAKAO), () -> assertThat(user.getRole()).isEqualTo(UserRole.MEMBER), From 0b76c2bd11de3d76bf5e35da98438556bb0e2c36 Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 22:08:16 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/event/UserSignedUpEvent.java | 3 +-- .../platform/auth/service/service/AuthService.java | 2 ++ .../auth/service/service/AuthServiceTest.java | 13 ++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/event/UserSignedUpEvent.java b/auth-server/src/main/java/com/truve/platform/auth/service/event/UserSignedUpEvent.java index e1405325..e5a79825 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/event/UserSignedUpEvent.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/event/UserSignedUpEvent.java @@ -27,8 +27,7 @@ public static UserSignedUpEvent from (User user) { EVENT_TYPE, LocalDateTime.now(), user.getPublicId(), - // TODO: 닉네임으로 변경 예정 - user.getEmail() + user.getNickname() ); } } diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java b/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java index 5240a6f9..8fa872dd 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java @@ -101,6 +101,7 @@ public void logout(String accessToken) { @Transactional public void signUp( String email, + String nickname, String password, boolean serviceTermsAgreed, boolean electronicFinanceTermsAgreed, @@ -125,6 +126,7 @@ public void signUp( User user = User.createLocalUser( email, + nickname, encodedPassword, serviceTermsAgreed, electronicFinanceTermsAgreed, diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java index d6351d1a..17bfb8f8 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java @@ -52,8 +52,10 @@ class AuthServiceTest { @InjectMocks private AuthService authService; + private static final String NICKNAME = "tester"; + private User createUser(Long id, String email, String encodedPassword) { - User user = User.createLocalUser(email, encodedPassword, true, true, true, false, true); + User user = User.createLocalUser(email, NICKNAME, encodedPassword, true, true, true, false, true); ReflectionTestUtils.setField(user, "id", id); return user; } @@ -246,7 +248,7 @@ class SignUpTest { }); // when - authService.signUp(email, password, true, true, true, false, true); + authService.signUp(email, NICKNAME, password, true, true, true, false, true); // then ArgumentCaptor savedUserCaptor = ArgumentCaptor.forClass(User.class); @@ -258,6 +260,7 @@ class SignUpTest { verify(userSignedUpEventPublisher).publish(keyCaptor.capture(), eventCaptor.capture()); assertThat(savedUserCaptor.getValue().getEmail()).isEqualTo(email); + assertThat(savedUserCaptor.getValue().getNickname()).isEqualTo(NICKNAME); assertThat(savedUserCaptor.getValue().getPassword()).isEqualTo("encoded"); assertThat(savedUserCaptor.getValue().getRole()).isEqualTo(UserRole.MEMBER); assertThat(savedUserCaptor.getValue().isServiceTermsAgreed()).isTrue(); @@ -282,7 +285,7 @@ class SignUpTest { // when CustomException exception = assertThrows( CustomException.class, - () -> authService.signUp(email, password, true, true, true, false, true) + () -> authService.signUp(email, NICKNAME, password, true, true, true, false, true) ); // then @@ -304,7 +307,7 @@ class SignUpTest { // when CustomException exception = assertThrows( CustomException.class, - () -> authService.signUp(email, password, true, true, true, false, true) + () -> authService.signUp(email, NICKNAME, password, true, true, true, false, true) ); // then @@ -327,7 +330,7 @@ class SignUpTest { // when CustomException exception = assertThrows( CustomException.class, - () -> authService.signUp(email, password, true, false, true, false, true) + () -> authService.signUp(email, NICKNAME, password, true, false, true, false, true) ); // then From d39219a67563a5bfa879219af5f1e85fa04cff7a Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 22:08:27 +0900 Subject: [PATCH 08/10] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/auth/service/controller/AuthController.java | 1 + .../auth/service/domain/dto/request/AuthRequest.java | 3 +++ .../auth/service/controller/AuthControllerTest.java | 9 ++++++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/controller/AuthController.java b/auth-server/src/main/java/com/truve/platform/auth/service/controller/AuthController.java index 96409fbb..2df9a37a 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/controller/AuthController.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/controller/AuthController.java @@ -40,6 +40,7 @@ public ApiResult signUp( ) { authService.signUp( request.getEmail(), + request.getNickname(), request.getPassword(), request.isServiceTermsAgreed(), request.isElectronicFinanceTermsAgreed(), diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/domain/dto/request/AuthRequest.java b/auth-server/src/main/java/com/truve/platform/auth/service/domain/dto/request/AuthRequest.java index 6cb45532..ee8dd2cb 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/domain/dto/request/AuthRequest.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/domain/dto/request/AuthRequest.java @@ -19,6 +19,9 @@ public static class SignUp { @NotBlank private String password; + @NotBlank + private String nickname; + private boolean serviceTermsAgreed; private boolean electronicFinanceTermsAgreed; diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java index 494299b6..6c8f246b 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java @@ -46,6 +46,7 @@ class AuthControllerTest { { "email": "new@test.com", "password": "password123", + "nickname": "tester", "serviceTermsAgreed": true, "electronicFinanceTermsAgreed": true, "privacyCollectionAgreed": true, @@ -62,7 +63,7 @@ class AuthControllerTest { // then resultActions.andExpect(status().isOk()) .andExpect(jsonPath("$.code").value("ok")); - verify(authService).signUp("new@test.com", "password123", true, true, true, false, true); + verify(authService).signUp("new@test.com", "tester", "password123", true, true, true, false, true); } @Test @@ -73,6 +74,7 @@ class AuthControllerTest { { "email": "dup@test.com", "password": "password123", + "nickname": "tester", "serviceTermsAgreed": true, "electronicFinanceTermsAgreed": true, "privacyCollectionAgreed": true, @@ -81,7 +83,7 @@ class AuthControllerTest { } """; willThrow(new CustomException(ErrorCode.ALREADY_EXISTS_EMAIL)) - .given(authService).signUp(anyString(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean()); + .given(authService).signUp(anyString(), anyString(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean()); // when ResultActions resultActions = mockMvc.perform(post("/api/auth/sign-up") @@ -102,6 +104,7 @@ class AuthControllerTest { { "email": "new@test.com", "password": "password123", + "nickname": "tester", "serviceTermsAgreed": true, "electronicFinanceTermsAgreed": false, "privacyCollectionAgreed": true, @@ -110,7 +113,7 @@ class AuthControllerTest { } """; willThrow(new CustomException(ErrorCode.REQUIRED_TERMS_NOT_AGREED)) - .given(authService).signUp(anyString(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean()); + .given(authService).signUp(anyString(), anyString(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean()); // when ResultActions resultActions = mockMvc.perform(post("/api/auth/sign-up") From cf2faa7e614cbfb9899878de8948f886a84bc08f Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 22:10:40 +0900 Subject: [PATCH 09/10] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/repository/UserRepository.java | 2 + .../auth/service/service/AuthService.java | 10 +++++ .../controller/AuthControllerTest.java | 30 +++++++++++++ .../auth/service/service/AuthServiceTest.java | 45 +++++++++++++++++++ .../platform/common/exception/ErrorCode.java | 2 + 5 files changed, 89 insertions(+) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/repository/UserRepository.java b/auth-server/src/main/java/com/truve/platform/auth/service/repository/UserRepository.java index fba66d15..fff6eca9 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/repository/UserRepository.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/repository/UserRepository.java @@ -17,6 +17,8 @@ public interface UserRepository extends JpaRepository { boolean existsByEmail(String email); + boolean existsByNickname(String nickname); + default User findByEmailOrThrow(String email) { return findByEmail(email).orElseThrow( () -> new CustomException(ErrorCode.NOT_FOUND_EMAIL) diff --git a/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java b/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java index 8fa872dd..9597fd25 100644 --- a/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java +++ b/auth-server/src/main/java/com/truve/platform/auth/service/service/AuthService.java @@ -1,6 +1,7 @@ package com.truve.platform.auth.service.service; import java.util.UUID; +import java.util.regex.Pattern; import org.springframework.data.util.Pair; import org.springframework.security.crypto.password.PasswordEncoder; @@ -23,6 +24,7 @@ @Service @RequiredArgsConstructor public class AuthService { + private static final Pattern NICKNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9가-힣]{2,10}$"); private final UserRepository userRepository; private final EmailVerificationRepository emailVerificationRepository; @@ -117,6 +119,14 @@ public void signUp( !userRepository.existsByEmail(email), ErrorCode.ALREADY_EXISTS_EMAIL ); + Preconditions.validate( + nickname != null && NICKNAME_PATTERN.matcher(nickname).matches(), + ErrorCode.INVALID_NICKNAME + ); + Preconditions.validate( + !userRepository.existsByNickname(nickname), + ErrorCode.ALREADY_EXISTS_NICKNAME + ); Preconditions.validate( serviceTermsAgreed && electronicFinanceTermsAgreed && privacyCollectionAgreed && over14Agreed, ErrorCode.REQUIRED_TERMS_NOT_AGREED diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java index 6c8f246b..cd2fd04a 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/controller/AuthControllerTest.java @@ -126,6 +126,36 @@ class AuthControllerTest { .andExpect(jsonPath("$.code").value(ErrorCode.REQUIRED_TERMS_NOT_AGREED.getCode())); } + @Test + @DisplayName("닉네임 형식 오류로 회원가입 요청하면 400을 반환한다.") + void 회원가입_실패_닉네임_형식_오류() throws Exception { + // given + String body = """ + { + "email": "new@test.com", + "password": "password123", + "nickname": "a b", + "serviceTermsAgreed": true, + "electronicFinanceTermsAgreed": true, + "privacyCollectionAgreed": true, + "marketingInfoAgreed": false, + "over14Agreed": true + } + """; + willThrow(new CustomException(ErrorCode.INVALID_NICKNAME)) + .given(authService).signUp(anyString(), anyString(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean()); + + // when + ResultActions resultActions = mockMvc.perform(post("/api/auth/sign-up") + .contentType(MediaType.APPLICATION_JSON) + .content(body)); + + // then + resultActions.andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorType").value("CLIENT_ERROR")) + .andExpect(jsonPath("$.code").value(ErrorCode.INVALID_NICKNAME.getCode())); + } + @Test @DisplayName("로그인에 성공하면 accessToken을 반환하고 refreshToken 쿠키를 설정한다.") void 로그인_성공() throws Exception { diff --git a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java index 17bfb8f8..07fb49ef 100644 --- a/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java +++ b/auth-server/src/test/java/com/truve/platform/auth/service/service/AuthServiceTest.java @@ -240,6 +240,7 @@ class SignUpTest { given(emailVerificationRepository.isVerifiedEmail(email)).willReturn(verifiedAt); given(userRepository.existsByEmail(email)).willReturn(false); + given(userRepository.existsByNickname(NICKNAME)).willReturn(false); given(passwordEncoder.encode(password)).willReturn("encoded"); given(userRepository.save(any(User.class))).willAnswer(invocation -> { User savedUser = invocation.getArgument(0); @@ -326,6 +327,7 @@ class SignUpTest { given(emailVerificationRepository.isVerifiedEmail(email)).willReturn("1700000000000"); given(userRepository.existsByEmail(email)).willReturn(false); + given(userRepository.existsByNickname(NICKNAME)).willReturn(false); // when CustomException exception = assertThrows( @@ -340,5 +342,48 @@ class SignUpTest { verify(emailVerificationRepository, never()).deleteVerifiedEmail(anyString()); verify(userSignedUpEventPublisher, never()).publish(anyString(), any()); } + + @Test + @DisplayName("닉네임 형식이 유효하지 않으면 예외가 발생한다.") + void 회원가입_실패_닉네임_형식_오류() { + // given + String email = "new@test.com"; + String password = "plain"; + + given(emailVerificationRepository.isVerifiedEmail(email)).willReturn("1700000000000"); + given(userRepository.existsByEmail(email)).willReturn(false); + + // when + CustomException exception = assertThrows( + CustomException.class, + () -> authService.signUp(email, "a b", password, true, true, true, false, true) + ); + + // then + assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.INVALID_NICKNAME); + verify(userRepository, never()).save(any(User.class)); + } + + @Test + @DisplayName("이미 사용 중인 닉네임이면 예외가 발생한다.") + void 회원가입_실패_중복_닉네임() { + // given + String email = "new@test.com"; + String password = "plain"; + + given(emailVerificationRepository.isVerifiedEmail(email)).willReturn("1700000000000"); + given(userRepository.existsByEmail(email)).willReturn(false); + given(userRepository.existsByNickname(NICKNAME)).willReturn(true); + + // when + CustomException exception = assertThrows( + CustomException.class, + () -> authService.signUp(email, NICKNAME, password, true, true, true, false, true) + ); + + // then + assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.ALREADY_EXISTS_NICKNAME); + verify(userRepository, never()).save(any(User.class)); + } } } diff --git a/common/src/main/java/com/truve/platform/common/exception/ErrorCode.java b/common/src/main/java/com/truve/platform/common/exception/ErrorCode.java index 0d4f1b96..a42ae7d7 100644 --- a/common/src/main/java/com/truve/platform/common/exception/ErrorCode.java +++ b/common/src/main/java/com/truve/platform/common/exception/ErrorCode.java @@ -19,6 +19,8 @@ public enum ErrorCode { INVALID_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, "리프레시 토큰이 올바르지 않습니다.", "A08"), KAKAO_USERINFO_FAILED(HttpStatus.BAD_REQUEST, "카카오 사용자 정보를 가져오지 못했습니다.", "A09"), REQUIRED_TERMS_NOT_AGREED(HttpStatus.BAD_REQUEST, "필수 약관 동의가 필요합니다.", "A10"), + INVALID_NICKNAME(HttpStatus.BAD_REQUEST, "유효하지 않은 닉네임입니다.", "A11"), + ALREADY_EXISTS_NICKNAME(HttpStatus.BAD_REQUEST, "중복된 닉네임입니다.", "A12"), NOT_FOUND_PAYMENT(HttpStatus.BAD_REQUEST, "존재하지 않는 결제입니다.", "P01"), INVALID_PAYMENT_AMOUNT(HttpStatus.BAD_REQUEST, "유효하지 않은 결제 금액입니다.", "P02"), From 67510b3d03f3be01d130683d3a18011e322e2406 Mon Sep 17 00:00:00 2001 From: l-lyun Date: Sat, 21 Mar 2026 22:15:07 +0900 Subject: [PATCH 10/10] =?UTF-8?q?feat:=20entity=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20ddl-auto=20u?= =?UTF-8?q?pdate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- auth-server/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth-server/src/main/resources/application.yml b/auth-server/src/main/resources/application.yml index 421af4e1..fff46089 100644 --- a/auth-server/src/main/resources/application.yml +++ b/auth-server/src/main/resources/application.yml @@ -13,7 +13,7 @@ spring: jpa: hibernate: - ddl-auto: validate + ddl-auto: update show-sql: false kafka: