From 8fbef441b0e6ec5c34b94030d5521f027639f278 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:32:26 +0900 Subject: [PATCH 01/14] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=A0=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=EC=84=9C=EB=B2=84=20cors=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/nowait/applicationadmin/config/security/CorsConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/config/security/CorsConfig.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/config/security/CorsConfig.java index 4c51b67..da61160 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/config/security/CorsConfig.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/config/security/CorsConfig.java @@ -15,7 +15,7 @@ public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); // 쿠키나 인증헤더 자격증명 허용 - config.setAllowedOrigins(List.of("http://localhost:5173","http://localhost:63342", "https://no-wait-fe-nowait-admin-2y5y.vercel.app", "https://nowait-admin.co.kr")); // 허용할 출처 설정 + config.setAllowedOrigins(List.of("http://localhost:5173","http://localhost:63342", "https://no-wait-fe-nowait-admin-2y5y.vercel.app", "https://nowait-admin.co.kr", "https://www.nowait-admin.com")); // 허용할 출처 설정 config.setAllowedMethods(List.of("GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS")); // 메서드 허용 config.setAllowedHeaders(List.of("*")); //클라이언트가 보낼 수 있는 헤더 config.setExposedHeaders(List.of("Authorization")); //클라이언트(브라우저)가 접근할 수 있는 헤더 지정 From 7f85c2ff1f201a3bd60f13104dc03c96d01c0678 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:33:22 +0900 Subject: [PATCH 02/14] =?UTF-8?q?refactor:=20=ED=9C=B4=EB=8C=80=ED=8F=B0?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EB=A7=88=EC=BC=80=ED=8C=85?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth/oauth2/CustomOAuth2UserService.java | 6 +++ .../domaincorerdb/user/entity/User.java | 45 +++++++++++++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/oauth/oauth2/CustomOAuth2UserService.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/oauth/oauth2/CustomOAuth2UserService.java index a7e1268..048dc43 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/oauth/oauth2/CustomOAuth2UserService.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/oauth/oauth2/CustomOAuth2UserService.java @@ -1,5 +1,6 @@ package com.nowait.applicationuser.oauth.oauth2; +import java.time.LocalDateTime; import java.util.Optional; import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; @@ -50,11 +51,16 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic User user = User.builder() .email(oAuth2Response.getEmail()) + .phoneNumber("") .nickname(oAuth2Response.getNickName()) .profileImage(oAuth2Response.getProfileImage()) .socialType(SocialType.KAKAO) .role(Role.USER) // 일반 유저 설정 .storeId(0L) + .phoneEntered(false) + .isMarketingAgree(false) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) .build(); userRepository.save(user); diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/entity/User.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/entity/User.java index 86d45d9..587f5aa 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/entity/User.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/entity/User.java @@ -1,9 +1,15 @@ package com.nowait.domaincorerdb.user.entity; +import java.time.LocalDate; +import java.time.LocalDateTime; + +import org.springframework.cglib.core.Local; +import org.springframework.data.annotation.LastModifiedDate; import org.springframework.security.crypto.password.PasswordEncoder; import com.nowait.common.enums.Role; import com.nowait.common.enums.SocialType; +import com.nowait.domaincorerdb.base.entity.BaseTimeEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -17,18 +23,23 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Table(name = "users") @Getter -public class User { +@SuperBuilder +public class User extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 생성 private Long id; @Column(nullable = false, unique = true) private String email; // 카카오 이메일 + @Column(nullable = true) + private String phoneNumber; // 사용자 전화번호 + @Column(nullable = false) private String password; // 관리자 패스워드 @@ -44,11 +55,22 @@ public class User { @Enumerated(EnumType.STRING) private Role role; + @Column(nullable = false) + private Boolean isMarketingAgree = false; + + @Column(nullable = false) + private Boolean phoneEntered = false; + private Long storeId; - @Builder - public User(String email,String password, String nickname, String profileImage, SocialType socialType, - Role role, Long storeId) { + @LastModifiedDate + @Column(nullable = false) + private LocalDateTime updatedAt; + + + public User(LocalDateTime createdAt, String email,String password, String nickname, String profileImage, SocialType socialType, + Role role, Long storeId, LocalDateTime updatedAt ) { + super(createdAt); this.email = email; this.password = password; this.nickname = nickname; @@ -56,6 +78,7 @@ public User(String email,String password, String nickname, String profileImage, this.socialType = socialType; this.role = role; this.storeId = storeId; + this.updatedAt = updatedAt; } public static User createUserWithId(Long userId, String email, String nickname, String profileImage, @@ -67,6 +90,8 @@ public static User createUserWithId(Long userId, String email, String nickname, .socialType(socialType) .role(role) .storeId(storeId) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) .build(); user.id = userId; @@ -77,7 +102,19 @@ public static User createUserWithId(Long userId, String email, String nickname, public void updateNickname(String nickname){ this.nickname = nickname; } + public void encodePassword(PasswordEncoder passwordEncoder) { password = passwordEncoder.encode(password); } + + public void setPhoneNumberAndMarkEntered(String phoneNumber, LocalDateTime ts) { + this.phoneNumber = phoneNumber; + this.phoneEntered = true; + this.updatedAt = ts; + } + + public void setIsMarketingAgree(boolean agree, LocalDateTime ts) { + this.isMarketingAgree = agree; + this.updatedAt = ts; + } } From 8296a5c50c6e97cd484c274c85cf1ff083075186 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:33:51 +0900 Subject: [PATCH 03/14] =?UTF-8?q?refactor:=20=ED=9C=B4=EB=8C=80=ED=8F=B0?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EB=A7=88=EC=BC=80=ED=8C=85?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/nowait/applicationuser/security/jwt/JwtUtil.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/security/jwt/JwtUtil.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/security/jwt/JwtUtil.java index 763c320..3df48aa 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/security/jwt/JwtUtil.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/security/jwt/JwtUtil.java @@ -23,11 +23,14 @@ public JwtUtil(@Value("${jwt.secret}") String secret) { ); } - public String createAccessToken(String tokenCategory, Long userId, String role, Long expiredMs) { + public String createAccessToken(String tokenCategory, Long userId, String role, boolean phoneEntered, + boolean marketingAgree, Long expiredMs) { return Jwts.builder() .claim("tokenCategory", tokenCategory) // accessToken .claim("userId", userId) .claim("role", role) + .claim("phoneEntered", phoneEntered) + .claim("marketingAgree", marketingAgree) .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() + expiredMs)) .signWith(secretKey) From 968ae1be740fe4d942cfe640ba2f743e914d34ed Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:34:47 +0900 Subject: [PATCH 04/14] =?UTF-8?q?refactor:=20=ED=9C=B4=EB=8C=80=ED=8F=B0?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EB=A7=88=EC=BC=80=ED=8C=85?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=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 --- .../applicationadmin/user/dto/ManagerSignupRequestDto.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/user/dto/ManagerSignupRequestDto.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/user/dto/ManagerSignupRequestDto.java index 7f807be..0dd4fbc 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/user/dto/ManagerSignupRequestDto.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/user/dto/ManagerSignupRequestDto.java @@ -1,5 +1,7 @@ package com.nowait.applicationadmin.user.dto; +import java.time.LocalDateTime; + import com.nowait.common.enums.Role; import com.nowait.common.enums.SocialType; import com.nowait.domaincorerdb.user.entity.User; @@ -7,7 +9,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.Getter; import lombok.NoArgsConstructor; @@ -39,10 +40,13 @@ public class ManagerSignupRequestDto { public User toEntity() { return User.builder() .email(email) + .phoneNumber("") .password(password) .nickname(nickname) .socialType(SocialType.LOCAL) .role(Role.MANAGER) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) .build(); } From 5b2faf5488b3bbd4ed7a8b4c56e12a8e5841308d Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:34:52 +0900 Subject: [PATCH 05/14] =?UTF-8?q?refactor:=20=ED=9C=B4=EB=8C=80=ED=8F=B0?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EB=A7=88=EC=BC=80=ED=8C=85?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=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 --- .../oauth/oauth2/OAuth2LoginSuccessHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/oauth/oauth2/OAuth2LoginSuccessHandler.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/oauth/oauth2/OAuth2LoginSuccessHandler.java index 4a84368..c6e2dcf 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/oauth/oauth2/OAuth2LoginSuccessHandler.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/oauth/oauth2/OAuth2LoginSuccessHandler.java @@ -38,13 +38,14 @@ public class OAuth2LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHan public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal(); + CustomOAuth2User customUserDetails = (CustomOAuth2User)authentication.getPrincipal(); User user = customUserDetails.getUser(); Long userId = customUserDetails.getUserId(); String role = authentication.getAuthorities().iterator().next().getAuthority(); // JWT 발급 - String accessToken = jwtUtil.createAccessToken("accessToken", userId, role, 30 * 60 * 1000L); // 30분 + String accessToken = jwtUtil.createAccessToken("accessToken", userId, role, + Boolean.TRUE.equals(user.getPhoneEntered()), Boolean.TRUE.equals(user.getIsMarketingAgree()),60 * 60 * 1000L); // 1시간 String refreshToken = jwtUtil.createRefreshToken("refreshToken", userId, 30L * 24 * 60 * 60 * 1000L); // 30일 // 1. refreshToken을 DB에 저장 or update From 008972a680770042d138ffc83802cb12e46b3bfb Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:35:01 +0900 Subject: [PATCH 06/14] =?UTF-8?q?refactor:=20=EA=B8=80=EC=9E=90=EC=88=98?= =?UTF-8?q?=20=EC=A0=9C=ED=95=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/nowait/domaincorerdb/store/entity/Store.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/store/entity/Store.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/store/entity/Store.java index a635dab..109c9c4 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/store/entity/Store.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/store/entity/Store.java @@ -40,13 +40,13 @@ public class Store extends BaseTimeEntity { @Column(nullable = true, length = 200) private String location; - @Column(nullable = true, length = 200) + @Column(nullable = true, length = 250) private String description; @Column(nullable = true, length = 200) private String noticeTitle; - @Column(nullable = true, length = 200) + @Column(nullable = true, length = 500) private String noticeContent; @Column(nullable = true, length = 200) From fa01dfb9aa3c9e563ddfcbae8ab3d3d4d3f9115a Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:35:15 +0900 Subject: [PATCH 07/14] =?UTF-8?q?refactor:=20=ED=9C=B4=EB=8C=80=ED=8F=B0?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EB=A7=88=EC=BC=80=ED=8C=85?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=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 --- .../token/controller/TokenController.java | 82 +++++++++++-------- .../domaincorerdb/user/entity/User.java | 3 - 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/token/controller/TokenController.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/token/controller/TokenController.java index fc6a99e..5c87401 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/token/controller/TokenController.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/token/controller/TokenController.java @@ -11,6 +11,9 @@ import com.nowait.applicationuser.security.jwt.JwtUtil; import com.nowait.applicationuser.token.dto.AuthenticationResponse; import com.nowait.applicationuser.token.service.TokenService; +import com.nowait.domaincorerdb.user.entity.User; +import com.nowait.domaincorerdb.user.exception.UserNotFoundException; +import com.nowait.domaincorerdb.user.repository.UserRepository; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -22,37 +25,50 @@ @RequestMapping("/api/refresh-token") @Slf4j public class TokenController { - private final JwtUtil jwtUtil; - private final TokenService tokenService; - @Value("${jwt.access-token-expiration-ms}") - private long accessTokenExpiration; - @Value("${jwt.refresh-token-expiration-ms}") - private long refreshTokenExpiration; - - @PostMapping - @Operation(summary = "리프레시 토큰", description = "리프레시 토큰을 사용하여 새로운 액세스 토큰과 리프레시 토큰을 발급합니다.") - @ApiResponse(responseCode = "200", description = "새로운 액세스 토큰과 리프레시 토큰 발급 성공") - public ResponseEntity refreshToken( - @CookieValue(value = "refreshToken", required = false) String refreshToken) { - - if (refreshToken == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Refresh token not found in cookies"); - } - - // 리프레시 토큰 검증 - Long userId = jwtUtil.getUserId(refreshToken); - String role = jwtUtil.getRole(refreshToken); - - if (tokenService.validateToken(refreshToken, userId)){ - String newAccessToken = jwtUtil.createAccessToken("accessToken", userId, role, accessTokenExpiration); - String newRefreshToken = jwtUtil.createRefreshToken("refreshToken", userId, refreshTokenExpiration); - - tokenService.updateRefreshToken(userId, refreshToken, newRefreshToken); - - AuthenticationResponse authenticationResponse = new AuthenticationResponse(newAccessToken, newRefreshToken); - return ResponseEntity.ok().body(authenticationResponse); - } - - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid or expired refresh token"); - } + private final JwtUtil jwtUtil; + private final TokenService tokenService; + private final UserRepository userRepository; + @Value("${jwt.access-token-expiration-ms}") + private long accessTokenExpiration; + @Value("${jwt.refresh-token-expiration-ms}") + private long refreshTokenExpiration; + + @PostMapping + @Operation(summary = "리프레시 토큰", description = "리프레시 토큰을 사용하여 새로운 액세스 토큰과 리프레시 토큰을 발급합니다.") + @ApiResponse(responseCode = "200", description = "새로운 액세스 토큰과 리프레시 토큰 발급 성공") + public ResponseEntity refreshToken( + @CookieValue(value = "refreshToken", required = false) String refreshToken) { + + if (refreshToken == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Refresh token not found in cookies"); + } + + // 리프레시 토큰 검증 + Long userId = jwtUtil.getUserId(refreshToken); + String role = jwtUtil.getRole(refreshToken); + User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new); + + if (tokenService.validateToken(refreshToken, userId)) { + String newAccessToken = jwtUtil.createAccessToken( + "accessToken", + userId, + role, + Boolean.TRUE.equals(user.getPhoneEntered()), + Boolean.TRUE.equals(user.getIsMarketingAgree()), + accessTokenExpiration + ); + String newRefreshToken = jwtUtil.createRefreshToken( + "refreshToken", + userId, + refreshTokenExpiration + ); + + tokenService.updateRefreshToken(userId, refreshToken, newRefreshToken); + + AuthenticationResponse authenticationResponse = new AuthenticationResponse(newAccessToken, newRefreshToken); + return ResponseEntity.ok().body(authenticationResponse); + } + + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid or expired refresh token"); + } } diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/entity/User.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/entity/User.java index 587f5aa..bac239a 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/entity/User.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/entity/User.java @@ -1,9 +1,7 @@ package com.nowait.domaincorerdb.user.entity; -import java.time.LocalDate; import java.time.LocalDateTime; -import org.springframework.cglib.core.Local; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.security.crypto.password.PasswordEncoder; @@ -20,7 +18,6 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import lombok.AccessLevel; -import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; From 85aef8aba9fe6970797e89586f7e1ab2c0fb856f Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:35:26 +0900 Subject: [PATCH 08/14] =?UTF-8?q?refactor:=20existsByPhoneNumber=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 --- .../com/nowait/domaincorerdb/user/repository/UserRepository.java | 1 + 1 file changed, 1 insertion(+) diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/repository/UserRepository.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/repository/UserRepository.java index b22b9fc..48a01b4 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/repository/UserRepository.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/repository/UserRepository.java @@ -11,4 +11,5 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); Optional findByNickname(String nickName); + boolean existsByPhoneNumber(String phoneNumber); } From 736c27a7f299675e1d31874e490cfc3e7675616c Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:35:51 +0900 Subject: [PATCH 09/14] =?UTF-8?q?feat:=20=ED=9C=B4=EB=8C=80=ED=8F=B0=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EB=A7=88=EC=BC=80=ED=8C=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B8=B0=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 --- .../user/controller/UserController.java | 42 +++++++++++++++++++ .../user/service/UserService.java | 42 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/controller/UserController.java create mode 100644 nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/service/UserService.java diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/controller/UserController.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/controller/UserController.java new file mode 100644 index 0000000..eddbada --- /dev/null +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/controller/UserController.java @@ -0,0 +1,42 @@ +package com.nowait.applicationuser.user.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +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.RestController; + +import com.nowait.applicationuser.user.dto.UserUpdateRequest; +import com.nowait.applicationuser.user.service.UserService; +import com.nowait.common.api.ApiUtils; +import com.nowait.domainuserrdb.oauth.dto.CustomOAuth2User; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/users") +@RequiredArgsConstructor +public class UserController { + + private final UserService userService; + + @PutMapping("/optional-info") + public ResponseEntity putOptional( + @AuthenticationPrincipal CustomOAuth2User customOAuth2User, + @Valid @RequestBody UserUpdateRequest req) { + + String newAccessToken = userService.putOptional(customOAuth2User.getUserId(), req.phoneNumber(), + req.consent()); + + return ResponseEntity + .status(HttpStatus.OK) + .body( + ApiUtils.success( + newAccessToken + ) + ); + } +} diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/service/UserService.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/service/UserService.java new file mode 100644 index 0000000..f9bc27c --- /dev/null +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/service/UserService.java @@ -0,0 +1,42 @@ +package com.nowait.applicationuser.user.service; + +import java.time.LocalDateTime; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.nowait.applicationuser.security.jwt.JwtUtil; +import com.nowait.domaincorerdb.user.entity.User; +import com.nowait.domaincorerdb.user.exception.UserNotFoundException; +import com.nowait.domaincorerdb.user.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class UserService { + + private final UserRepository userRepository; + private final JwtUtil jwtUtil; + + @Transactional + public String putOptional(Long userId, String phoneNumber, boolean consent) { + + User user = userRepository.findById(userId) + .orElseThrow(UserNotFoundException::new); + + if (userRepository.existsByPhoneNumber(phoneNumber)) { + throw new IllegalArgumentException("Phone number already in use"); + } + + user.setPhoneNumberAndMarkEntered(phoneNumber, LocalDateTime.now()); + user.setIsMarketingAgree(consent, LocalDateTime.now()); + + String role = "ROLE_" + user.getRole().name(); + + return jwtUtil.createAccessToken("accessToken", user.getId(), role, + Boolean.TRUE.equals(user.getPhoneEntered()), + Boolean.TRUE.equals(user.getIsMarketingAgree()), + 60 * 60 * 1000L); + } +} From 1f2032e9309647bc9ab3d0819185127c8cd723dd Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 14:35:57 +0900 Subject: [PATCH 10/14] =?UTF-8?q?feat:=20=ED=9C=B4=EB=8C=80=ED=8F=B0=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B0=8F=20=EB=A7=88=EC=BC=80=ED=8C=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../applicationuser/user/dto/UserUpdateRequest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/dto/UserUpdateRequest.java diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/dto/UserUpdateRequest.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/dto/UserUpdateRequest.java new file mode 100644 index 0000000..68dc73b --- /dev/null +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/dto/UserUpdateRequest.java @@ -0,0 +1,10 @@ +package com.nowait.applicationuser.user.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; + +public record UserUpdateRequest( + @NotBlank + @Pattern(regexp = "^010-\\d{4}-\\d{4}$", message = "휴대폰 번호는 010-0000-0000 형식이어야 합니다.") + String phoneNumber, + Boolean consent) { } From 3a6fb085f3aed3d8e271e7bdcd11dd6905e2b1a7 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 15:01:31 +0900 Subject: [PATCH 11/14] =?UTF-8?q?refactor:=20=EC=9C=A0=ED=9A=A8=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=ED=8C=8C=EC=8B=B1=20=EC=8B=9C?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=EB=AF=B8=EC=B2=98=EB=A6=AC=20=E2=86=92?= =?UTF-8?q?=20500=20=EA=B0=80=EB=8A=A5=EC=84=B1=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../token/controller/TokenController.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/token/controller/TokenController.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/token/controller/TokenController.java index 5c87401..5336874 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/token/controller/TokenController.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/token/controller/TokenController.java @@ -44,8 +44,15 @@ public ResponseEntity refreshToken( } // 리프레시 토큰 검증 - Long userId = jwtUtil.getUserId(refreshToken); - String role = jwtUtil.getRole(refreshToken); + Long userId; + String role; + try { + userId = jwtUtil.getUserId(refreshToken); + role = jwtUtil.getRole(refreshToken); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid or expired refresh token"); + } + User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new); if (tokenService.validateToken(refreshToken, userId)) { From 919c851574cd17637f6d723f55a30bd8de8ac039 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 15:02:48 +0900 Subject: [PATCH 12/14] =?UTF-8?q?refactor:=20Null=20=EA=B0=80=EB=8A=A5=20B?= =?UTF-8?q?oolean=20=EC=96=B8=EB=B0=95=EC=8B=B1=EC=9C=BC=EB=A1=9C=20NPE=20?= =?UTF-8?q?=EC=9C=84=ED=97=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nowait/applicationuser/user/controller/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/controller/UserController.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/controller/UserController.java index eddbada..fcb0580 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/controller/UserController.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/controller/UserController.java @@ -29,7 +29,7 @@ public ResponseEntity putOptional( @Valid @RequestBody UserUpdateRequest req) { String newAccessToken = userService.putOptional(customOAuth2User.getUserId(), req.phoneNumber(), - req.consent()); + Boolean.TRUE.equals(req.consent())); return ResponseEntity .status(HttpStatus.OK) From faabc3146061878370b48ef01f28945ee3da79d9 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 15:03:59 +0900 Subject: [PATCH 13/14] =?UTF-8?q?refactor:=20NPE=20=EB=B0=A9=EC=A7=80?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20primitive=20boolean=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/nowait/applicationuser/user/dto/UserUpdateRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/dto/UserUpdateRequest.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/dto/UserUpdateRequest.java index 68dc73b..f9993f1 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/dto/UserUpdateRequest.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/dto/UserUpdateRequest.java @@ -7,4 +7,4 @@ public record UserUpdateRequest( @NotBlank @Pattern(regexp = "^010-\\d{4}-\\d{4}$", message = "휴대폰 번호는 010-0000-0000 형식이어야 합니다.") String phoneNumber, - Boolean consent) { } + boolean consent) { } From 279bba7e82b78ebb9d8bb612c9909f535f103102 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 5 Sep 2025 15:05:58 +0900 Subject: [PATCH 14/14] =?UTF-8?q?refactor:=20=EC=A0=84=ED=99=94=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=A4=91=EB=B3=B5=20=EA=B2=80=EC=82=AC:=20?= =?UTF-8?q?=EC=9E=90=EA=B8=B0=20=EC=9E=90=EC=8B=A0=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EC=8B=9C=20=EC=98=A4=ED=83=90=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80=20=EB=A9=94=EC=84=9C=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 --- .../com/nowait/applicationuser/user/service/UserService.java | 4 ++-- .../nowait/domaincorerdb/user/repository/UserRepository.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/service/UserService.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/service/UserService.java index f9bc27c..bdd65d5 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/service/UserService.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/user/service/UserService.java @@ -25,8 +25,8 @@ public String putOptional(Long userId, String phoneNumber, boolean consent) { User user = userRepository.findById(userId) .orElseThrow(UserNotFoundException::new); - if (userRepository.existsByPhoneNumber(phoneNumber)) { - throw new IllegalArgumentException("Phone number already in use"); + if (userRepository.existsByPhoneNumberAndIdNot(phoneNumber, userId)) { + throw new IllegalArgumentException("이미 사용 중인 휴대폰 번호입니다."); } user.setPhoneNumberAndMarkEntered(phoneNumber, LocalDateTime.now()); diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/repository/UserRepository.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/repository/UserRepository.java index 48a01b4..5653ee4 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/repository/UserRepository.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/repository/UserRepository.java @@ -11,5 +11,5 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); Optional findByNickname(String nickName); - boolean existsByPhoneNumber(String phoneNumber); + boolean existsByPhoneNumberAndIdNot(String phoneNumber, Long id); }