Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c9e1af3
feat : 회원가입 url 호출 테스트 및 프로덕션 코드 작성
Feb 2, 2026
40037d8
feat : 회원가입 request dto 생성
Feb 2, 2026
0768937
feat : 회원가입 api 응답객체 타입 변경
Feb 2, 2026
fcfe9df
test : 테스트 코드를 mockMvc 방식으로 변경
Feb 2, 2026
7b5ee41
feat : 요청 dto의 이메일 검증 코드 작성
Feb 2, 2026
01dfaf4
test : 요청 dto의 이메일 형식 성공, 실패 테스트 작성
Feb 2, 2026
1d431d4
test : @NotBlank 테스트 추가
Feb 2, 2026
4534d90
feat : 생년월일 검증 테스트 코드 및 로직 작성
Feb 2, 2026
bb55305
feat : 이름 검증 테스트 코드 및 로직 작성
Feb 2, 2026
d5f5679
feat : @Valid 테스트를 위해 MethodArgumentNotValidException 핸들러 추가
Feb 2, 2026
afce54c
test : 회원가입 API 실패 테스트 작성
Feb 2, 2026
ddb16e2
test : 테스트 코드내에서 dto 중복 제거
Feb 3, 2026
28f3ab8
feat : 비밀번호 검증 테스트 코드 및 로직 작성
Feb 3, 2026
4bcad0d
refactor : 로그인 id + 비밀번호를 헤더로 분리
Feb 3, 2026
f33afac
fix : exception 핸들러 추가해서 테스트 깨지는 현상 수정
Feb 3, 2026
02792ab
feat : 스프링 시큐리티 crypto 의존성 추가 및 사용자 빈 등록
Feb 3, 2026
1380b79
feat : UsersService 계층 생성 및 비밀번호 검증 코드 추가
Feb 3, 2026
fe97a43
test : UsersService slice 테스트 코드 추가
Feb 3, 2026
a94a2dd
refactor : 테스트 메서드 네이밍 형식 변경
Feb 3, 2026
4a1e035
feat : User 엔티티 & 레포지토리 코드 추가
Feb 3, 2026
00efb46
test : slice 테스트 클래스명 변경
Feb 3, 2026
f31a7bb
fix : 순환참조 오류 수정
Feb 4, 2026
402838e
refactor : SignCommand 객체 추가
Feb 4, 2026
b8d02c6
test : user 생성 테스트 및 검증 코드 추가
Feb 4, 2026
8609eaf
test : users http 추가
Feb 4, 2026
4320bc1
feat : user 저장 로직 추가
Feb 4, 2026
5b45d32
refactor : 기존 service 코드를 facade로 이동
Feb 5, 2026
1679ffe
docs : 주석 추가
Feb 5, 2026
4b0b620
test : 이미 가입되어있는 경우 회원가입 불가한 테스트코드 추가
Feb 5, 2026
bc89541
feat : 환자 존재 여부 확인하는 서비스 코드 추가
Feb 5, 2026
f6f3e3a
fix : 아이디는 숫자+비밀번호만 가능하도록 변경(특수문자 x)
Feb 5, 2026
84b19be
refactor : api 응답객체 + application 계층 dto 추가
Feb 5, 2026
3aa3349
refactor : 회원가입 API 헤더 부분 리팩토링 (ArgumentResolver 활용)
Feb 5, 2026
c122240
feat : 내 정보조회 mock API 추가
Feb 5, 2026
cd56a8a
feat : 내 정보 조회를 위한 ArgumentResolver와 service 계층내 사용자 인증 코드 추가
Feb 5, 2026
bd6908b
feat : 정보 조회 API 내부 로직 추가
Feb 5, 2026
61c0372
test : http에 내정보조회 API 추가
Feb 5, 2026
d9f704c
feat : 내 정보 조회시 이름 마지막 부분을 마스킹 처리
Feb 5, 2026
3048849
feat : 비밀번호 변경 요청 객체 및 테스트 코드 작성
Feb 5, 2026
1c89ed2
feat : 비밀번호 변경 요청 서비스 로직 추가 및 테스트 코드 작성
Feb 5, 2026
3a52ab6
test : 비밀번호 변경 API에 대한 controller 슬라이스 테스트 작성
Feb 5, 2026
39dc6b8
fix : authenticate()이 null을 응답하는 버그 수정
Feb 5, 2026
0067927
refactor : interfaces 패키지 정리
Feb 5, 2026
f84a492
refactor : application 패키지 정리
Feb 5, 2026
079a149
refactor : domain 패키지 정리
Feb 5, 2026
7e379b6
refactor : 비밀번호 검증 코드를 facade -> service 로 이동
Feb 7, 2026
5b50c60
refactor : 중복 email 검증 로직 & 테스트 추가
Feb 7, 2026
89930d5
test : 회원가입, 내 정보 조회 E2E 테스트 추가
Feb 7, 2026
fbb0b85
test : 비밀번호 변경 E2E 테스트 추가
Feb 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/commerce-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies {

// web
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.security:spring-security-crypto")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:${project.properties["springDocOpenApiVersion"]}")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.loopers.application.user;

import com.loopers.domain.user.UserModel;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
* 인증된 사용자의 식별 정보. 컨트롤러에서 "현재 사용자"를 식별하는 용도로 사용한다.
*/
@Getter
@AllArgsConstructor
public class AuthUserPrincipal {

private final Long id;
private final String loginId;

public static AuthUserPrincipal from(UserModel user) {
return new AuthUserPrincipal(
user.getId(),
user.getLoginId()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.loopers.application.user;

import java.time.LocalDate;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class SignUpCommand {

private final String loginId;
private final String loginPw;
private final LocalDate birthDate;
private final String name;
private final String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.loopers.application.user;

import com.loopers.domain.user.UserModel;
import com.loopers.domain.user.UserService;
import com.loopers.interfaces.user.ChangePasswordRequest;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@AllArgsConstructor
public class UserFacade {

private final UserService userService;

public UserInfo signUp(SignUpCommand command) {
UserModel userModel = userService.createUser(
command.getLoginId(),
command.getLoginPw(),
command.getBirthDate(),
command.getName(),
command.getEmail()
);
Comment on lines 15 to 22
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# 이메일 unique 제약 확인
find . -type f -name "*.java" | head -20 | xargs grep -l "User" | head -5

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 470


🏁 Script executed:

# UserFacade가 있는 디렉토리 구조 파악
find . -type f -name "UserFacade.java" -o -name "UserService.java" -o -name "User.java" | head -10

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 238


🏁 Script executed:

# unique 제약 및 마이그레이션 파일 검색
fd -e java -e sql -e xml | head -30

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 2529


🏁 Script executed:

# 이메일 유니크 제약/인덱스 및 마이그레이션 확인
rg "UniqueConstraint|@Column.*unique|unique\s*=\s*true" --type java -A 2 -B 2

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 67


🏁 Script executed:

# SQL 마이그레이션 파일에서 unique 확인
rg "create table|alter table|unique" --type sql -i

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 233


🏁 Script executed:

cat -n ./apps/commerce-api/src/main/java/com/loopers/domain/user/UserModel.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 3051


🏁 Script executed:

cat -n ./apps/commerce-api/src/main/java/com/loopers/infrastructure/UserJpaRepository.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 513


🏁 Script executed:

cat -n ./apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 3136


🏁 Script executed:

cat -n ./apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 2318


🏁 Script executed:

# DB 마이그레이션 파일 검색
find . -type f \( -name "*.sql" -o -name "V*.sql" -o -name "*.xml" \) | grep -i migrat

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 67


이메일 중복 확인만으로는 레이스 컨디션을 막기 어렵다

동시 가입 요청이 existsByEmail을 통과한 후 createUser가 호출되면 중복 사용자가 생성되거나 DB 무결성 위반으로 500 에러가 발생한다. 현재 UserModel의 email 필드는 nullable 제약만 있고 unique 제약이 없으며(line 43), UserService.createUser에서 중복키 예외를 처리하지 않는다(line 19-22).

수정안: DB 레벨에 email 컬럼에 unique 제약을 추가하고, createUser에서 DataIntegrityViolationException을 CoreException(BAD_REQUEST)으로 변환하여 처리하거나 트랜잭션 격리 수준을 상향 조정하라. 추가로 동일 이메일 동시 요청 시 하나만 성공하고 나머지는 적절한 예외로 반환되는지 동시성 테스트로 검증하라.

참고로 password 검증 정책(생년월일 포함 여부)은 signUp과 changePassword에서 일관되게 적용되고 있다(UserService line 55, UserFacade line 43-53).

🤖 Prompt for AI Agents
In `@apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java`
around lines 18 - 33, Add a DB-level unique constraint on the UserModel.email
column and handle unique-key violations in UserService.createUser: keep the
existsByEmail check but catch DataIntegrityViolationException (or the underlying
constraint violation) in createUser and translate it to throw new
CoreException(ErrorType.BAD_REQUEST, "...email already registered...") so
concurrent sign-ups return a controlled 400 instead of a 500; alternatively
document/implement a higher isolation/locking strategy if preferred and add a
concurrency test to assert only one of simultaneous signUp calls (via
UserFacade.signUp -> userService.createUser) succeeds while others receive the
mapped CoreException.


return UserInfo.from(userModel);
}

public UserInfo getMyInfo(Long userId) {
UserModel user = userService.findById(userId);
return UserInfo.from(user);
}

public void changePassword(Long userId, ChangePasswordRequest request) {
userService.changePassword(userId, request.currentPassword(), request.newPassword());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.loopers.application.user;

import com.loopers.domain.user.UserModel;
import java.time.LocalDate;

public record UserInfo(Long id, String loginId, String name, String email, LocalDate birthDate) {

public static UserInfo from(UserModel userModel) {
return new UserInfo(
userModel.getId(),
userModel.getLoginId(),
userModel.getName(),
userModel.getEmail(),
userModel.getBirthDate()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.loopers.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class PasswordEncoderConfig {

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.loopers.config;

import com.loopers.interfaces.api.AuthUserArgumentResolver;
import com.loopers.interfaces.api.CredentialsHeadersArgumentResolver;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {

private final CredentialsHeadersArgumentResolver credentialsHeadersArgumentResolver;
private final AuthUserArgumentResolver authUserArgumentResolver;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(credentialsHeadersArgumentResolver);
resolvers.add(authUserArgumentResolver);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.loopers.domain.user;

import com.loopers.domain.BaseEntity;
import com.loopers.support.error.CoreException;
import com.loopers.support.error.ErrorType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDate;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Comment;

@Entity
@Table(name = "users")
@NoArgsConstructor
@Getter
public class UserModel extends BaseEntity {

@Id
@GeneratedValue
private Long id;

@Comment("아이디")
@Column(name = "login_id", nullable = false)
private String loginId;

@Comment("비밀번호")
@Column(name = "password", nullable = false)
private String password;

@Comment("생년월일")
@Column(name = "birth", nullable = false)
private LocalDate birthDate;

@Comment("이름")
@Column(name = "name", nullable = false)
private String name;

@Comment("이메일")
@Column(name = "email", nullable = false)
private String email;
Comment on lines +26 to +44
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find apps/commerce-api -name "UserModel.java" -type f

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 155


🏁 Script executed:

fd "UserModel" --type f apps/commerce-api

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 230


🏁 Script executed:

git ls-files "apps/commerce-api" | grep -i user | head -20

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 1672


🏁 Script executed:

cat -n apps/commerce-api/src/main/java/com/loopers/domain/user/UserModel.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 3051


🏁 Script executed:

cat -n apps/commerce-api/src/test/java/com/loopers/domain/user/UserModelTest.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 3097


🏁 Script executed:

cat -n apps/commerce-api/src/main/java/com/loopers/domain/user/UserRepository.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 464


🏁 Script executed:

cat -n apps/commerce-api/src/main/java/com/loopers/infrastructure/UserRepositoryImpl.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 1209


🏁 Script executed:

find apps/commerce-api -name "*.sql" -o -name "*schema*" -o -name "*migration*" | head -20

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 67


🏁 Script executed:

cat -n apps/commerce-api/src/main/java/com/loopers/infrastructure/UserJpaRepository.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 513


🏁 Script executed:

cat -n apps/commerce-api/src/main/java/com/loopers/domain/user/UserService.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 3136


🏁 Script executed:

cat -n apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 2318


🏁 Script executed:

cat -n apps/commerce-api/src/test/java/com/loopers/domain/user/UserServiceTest.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 6343


🏁 Script executed:

find apps/commerce-api -path "*/resources/*" -name "*.sql" -o -path "*/resources/*" -name "*.xml" | head -20

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 67


🏁 Script executed:

cat -n apps/commerce-api/src/test/java/com/loopers/application/UserFacadeTest.java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 3545


🏁 Script executed:

find apps/commerce-api/src/test -name "*Integration*" -o -name "*Repository*Test*"

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 178


🏁 Script executed:

cat -n apps/commerce-api/src/test/java/com/loopers/domain/example/ExampleServiceIntegrationTest.java | head -50

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 2006


🏁 Script executed:

rg "DataIntegrityViolation|ConstraintViolation|duplicate" apps/commerce-api/src/test --type java

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 5775


🏁 Script executed:

rg "existsByLoginId|findByLoginId" apps/commerce-api/src --type java -A 2 -B 2

Repository: Loopers-dev-lab/loop-pack-be-l2-vol3-java

Length of output: 2362


login_id/email에 데이터베이스 유니크 제약이 필수다

  • 현재 상태: UserFacade.signUp()에서 email만 existsByEmail()로 중복 확인하고, login_id는 중복 체크 없음. 두 필드 모두 데이터베이스 레벨 제약 부재.
  • 운영 관점: 동시 회원가입 시 서비스 레벨 중복 체크만으로는 race condition에 의해 중복 레코드가 생성될 수 있으며, 특히 login_id는 현재 중복 방지 로직이 전혀 없어 인증 및 조회가 모호해질 수 있다.
  • 수정안: users 테이블에 @UniqueConstraint를 추가하여 login_id와 email 모두에 데이터베이스 레벨 유니크 제약을 적용한다.
  • 추가 테스트: 동일 login_id/email 저장 시 DataIntegrityViolationException이 발생하는지 저장소 통합 테스트로 검증한다.
🔧 수정 예시
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.Id;
 import jakarta.persistence.Table;
+import jakarta.persistence.UniqueConstraint;
@@
-@Table(name = "users")
+@Table(
+    name = "users",
+    uniqueConstraints = {
+        `@UniqueConstraint`(name = "uk_users_login_id", columnNames = "login_id"),
+        `@UniqueConstraint`(name = "uk_users_email", columnNames = "email")
+    }
+)
🤖 Prompt for AI Agents
In `@apps/commerce-api/src/main/java/com/loopers/domain/user/UserModel.java`
around lines 26 - 44, Add database-level unique constraints for both loginId and
email in the UserModel entity: modify the class-level mapping (UserModel) to
declare unique constraints for the login_id and email columns (e.g., via
`@Table`(uniqueConstraints=...)) or mark each field as unique via `@Column`(unique =
true) on the loginId and email fields, and ensure column names match "login_id"
and "email". After adding constraints, add/adjust an integration test to attempt
saving duplicate login_id and duplicate email and assert that a
DataIntegrityViolationException (or the JPA persistence exception your stack
throws) is raised when violating each constraint. Ensure UserFacade.signUp
behavior remains compatible (it can keep existsByEmail() but rely on DB
constraint for safety).


private UserModel(String loginId, String password, LocalDate birthDate, String name, String email) {
this.loginId = loginId;
this.password = password;
this.birthDate = birthDate;
this.name = name;
this.email = email;
}

@Override
protected void guard() {
if (loginId == null || loginId.isBlank()) {
throw new CoreException(ErrorType.BAD_REQUEST, "로그인 ID는 필수입니다.");
}
if (password == null || password.isBlank()) {
throw new CoreException(ErrorType.BAD_REQUEST, "비밀번호는 필수입니다.");
}
if (birthDate == null) {
throw new CoreException(ErrorType.BAD_REQUEST, "생년월일은 필수입니다.");
}
if (name == null || name.isBlank()) {
throw new CoreException(ErrorType.BAD_REQUEST, "이름은 필수입니다.");
}
if (email == null || email.isBlank()) {
throw new CoreException(ErrorType.BAD_REQUEST, "이메일은 필수입니다.");
}
}

public static UserModel create(String loginId, String encodedPassword, LocalDate birthDate, String name, String email) {
return new UserModel(loginId, encodedPassword, birthDate, name, email);
}

public void changePassword(String newEncodedPassword) {
this.password = newEncodedPassword;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.loopers.domain.user;

import java.util.Optional;

public interface UserRepository {

UserModel save(UserModel userModel);

Optional<UserModel> findById(Long id);

Optional<UserModel> findByLoginId(String loginId);

Boolean existsByEmail(String email);

Boolean existsByLoginId(String loginId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.loopers.domain.user;

import com.loopers.support.error.CoreException;
import com.loopers.support.error.ErrorType;
import jakarta.transaction.Transactional;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
public class UserService {

private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;

@Transactional
public UserModel createUser(String loginId, String rawPassword, LocalDate birthDate, String name, String email) {
if (userRepository.existsByLoginId(loginId)) {
throw new CoreException(ErrorType.BAD_REQUEST, "이미 사용 중인 아이디입니다.");
}
if (userRepository.existsByEmail(email)) {
throw new CoreException(ErrorType.BAD_REQUEST, "이미 가입된 이메일입니다.");
}
validatePasswordNotContainsBirthDate(rawPassword, birthDate);

String encodedPassword = passwordEncoder.encode(rawPassword);
UserModel user = UserModel.create(loginId, encodedPassword, birthDate, name, email);
return userRepository.save(user);
}

public UserModel authenticate(String loginId, String rawPassword) {
UserModel user = userRepository.findByLoginId(loginId)
.orElseThrow(() -> new CoreException(ErrorType.UNAUTHORIZED, "로그인 정보가 올바르지 않습니다."));
if (!passwordEncoder.matches(rawPassword, user.getPassword())) {
throw new CoreException(ErrorType.UNAUTHORIZED, "로그인 정보가 올바르지 않습니다.");
}
return user;
}

public Boolean existsByEmail(String email) {
return userRepository.existsByEmail(email);
}

public UserModel findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "사용자를 찾을 수 없습니다."));
}

@Transactional
public void changePassword(Long userId, String currentPassword, String newPassword) {
UserModel user = findById(userId);

if (!passwordEncoder.matches(currentPassword, user.getPassword())) {
throw new CoreException(ErrorType.BAD_REQUEST, "기존 비밀번호가 일치하지 않습니다.");
}

if (passwordEncoder.matches(newPassword, user.getPassword())) {
throw new CoreException(ErrorType.BAD_REQUEST, "새 비밀번호는 기존 비밀번호와 달라야 합니다.");
}

validatePasswordNotContainsBirthDate(newPassword, user.getBirthDate());

String newEncodedPassword = passwordEncoder.encode(newPassword);
user.changePassword(newEncodedPassword);
}

private void validatePasswordNotContainsBirthDate(String password, LocalDate birthDate) {
String birthStr = birthDate.toString().replace("-", "");
if (password.contains(birthStr)) {
throw new CoreException(ErrorType.BAD_REQUEST, "비밀번호에 생년월일을 포함할 수 없습니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.loopers.infrastructure;

import com.loopers.domain.user.UserModel;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserJpaRepository extends JpaRepository<UserModel, Long> {

Boolean existsByEmail(String email);

Boolean existsByLoginId(String loginId);

Optional<UserModel> findByLoginId(String loginId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.loopers.infrastructure;

import com.loopers.domain.user.UserModel;
import com.loopers.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class UserRepositoryImpl implements UserRepository {

private final UserJpaRepository userJpaRepository;

@Override
public UserModel save(UserModel userModel) {
return userJpaRepository.save(userModel);
}

@Override
public java.util.Optional<UserModel> findById(Long id) {
return userJpaRepository.findById(id);
}

@Override
public java.util.Optional<UserModel> findByLoginId(String loginId) {
return userJpaRepository.findByLoginId(loginId);
}

@Override
public Boolean existsByEmail(String email) {
return userJpaRepository.existsByEmail(email);
}

@Override
public Boolean existsByLoginId(String loginId) {
return userJpaRepository.existsByLoginId(loginId);
}
}
Loading