Skip to content
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
22574c6
feat: BaseEntity 엔티티 구현 (#1)
sangyunpark99 Mar 31, 2025
af21ba6
feat: User 엔티티 구현 (#1)
sangyunpark99 Mar 31, 2025
0998526
refactor: 사용하지 않는 클래스 삭제 (#1)
sangyunpark99 Mar 31, 2025
c4828de
refactor: User 엔티티 필드명 userId → id로 변경(#1)
sangyunpark99 Mar 31, 2025
2f83a01
refactor: providerId 자료형 String으로 변경(#1)
sangyunpark99 Apr 2, 2025
86cbc52
feat: UserAddress 엔티티 구현 (#1)
sangyunpark99 Apr 2, 2025
9416a2c
chore: .gitignore에 application.yml 파일 추가 (#1)
sangyunpark99 Apr 2, 2025
4a78fb4
refactor: User 엔티티와 UserAddress 엔티티 간의 외래키 제거 (#1)
sangyunpark99 Apr 2, 2025
ea1ac42
feat : 사용자 회원가입 api 엔드포인트 생성 (#1)
sangyunpark99 Apr 2, 2025
abd1120
chore : validation 의존성 추가 (#1)
sangyunpark99 Apr 2, 2025
ed9c25b
feat : 주소 요청 DTO 생성 및 유효성 검증 추가 (#1)
sangyunpark99 Apr 2, 2025
804d08f
feat : 회원 가입 요청 DTO 생성 및 유효성 검증 추가 (#1)
sangyunpark99 Apr 2, 2025
16ec2c2
feat : 회원 가입 요청 DTO 생성 및 유효성 검증 테스트 추가 (#1)
sangyunpark99 Apr 2, 2025
220520b
feat : 주소 요청 DTO 생성 및 유효성 검증 테스트 추가 (#1)
sangyunpark99 Apr 2, 2025
1b03b82
refactor : 유효성 검증 메시지 상수 클래스로 분리 (#1)
sangyunpark99 Apr 2, 2025
b0ae2d2
style : 불필요한 import문 제거 (#1)
sangyunpark99 Apr 2, 2025
7e41f10
chore : 비밀번호 암호화에 필요한 crypto 의존성 추가 (#1)
sangyunpark99 Apr 2, 2025
9c42aaa
feat : User 엔티티에 password 필드 추가 (#1)
sangyunpark99 Apr 2, 2025
c9ba0fd
feat : UserAddress 엔티티에 phoneNumber 필드 삭제 (#1)
sangyunpark99 Apr 2, 2025
50b98e6
feat : UserAddressRequestDto → UserAddress 엔티티 변환 Mapper 구현 (#1)
sangyunpark99 Apr 2, 2025
7802513
feat : UserSignupRequestDto → User 엔티티 변환 Mapper 구현 (#1)
sangyunpark99 Apr 2, 2025
0a919de
test : UserMapper 테스트 (#1)
sangyunpark99 Apr 2, 2025
789331d
test : UserAddressMapper 테스트 (#1)
sangyunpark99 Apr 2, 2025
eac7c9a
feat : 사용자 주소 정보를 담는 UserAddressRequestDto 구현 (#1)
sangyunpark99 Apr 2, 2025
43ba1bb
test : 사용자 주소 정보를 담는 UserAddressRequestDto 테스트 (#1)
sangyunpark99 Apr 2, 2025
f3a9018
feat : 회원 가입시 필요한 UserSignupRequestDto 구현 (#1)
sangyunpark99 Apr 2, 2025
a3347d8
test : 회원 가입시 필요한 UserSignupRequestDto 테스트 (#1)
sangyunpark99 Apr 2, 2025
978f13c
feat : 회원가입 응답용 UserSignupResponseDto 구현 (#1)
sangyunpark99 Apr 2, 2025
d76d899
feat : 회원가입 응답 메시지를 ResponseMessages enum으로 분리 (#1)
sangyunpark99 Apr 2, 2025
8529873
feat : UserController 구현 (#1)
sangyunpark99 Apr 2, 2025
56a96e3
test : UserController 테스트 (#1)
sangyunpark99 Apr 2, 2025
a195785
test : 회원가입 통합 테스트 (#1)
sangyunpark99 Apr 2, 2025
155e062
feat : User 엔티티에 대한 CrudRepository 생성 (#1)
sangyunpark99 Apr 2, 2025
9915ec0
feat : UserService에 회원가입 로직 구현 (#1)
sangyunpark99 Apr 2, 2025
15d61fc
test : UserService에 회원가입 로직 테스트 (#1)
sangyunpark99 Apr 2, 2025
ca16721
feat : 유효성 검증 메시지 상수화 클래스 ValidationMessages 추가 (#1)
sangyunpark99 Apr 2, 2025
75337f9
feat: 중복 회원 예외 메시지 Enum에 추가 (#1)
sangyunpark99 Apr 3, 2025
8e06340
feat: GlobalExceptionHandler에 중복 회원 예외 처리 추가 (#1)
sangyunpark99 Apr 3, 2025
54fbbae
test: UserController에 중복 이메일 및 유효성 검사 테스트 케이스 추가 (#1)
sangyunpark99 Apr 3, 2025
c44d480
feat: UserDuplicateException 예외 클래스 추가 (#1)
sangyunpark99 Apr 3, 2025
7fe1008
test: User 통합 테스트에 회원가입 중복 이메일 실패 케이스 추가 (#1)
sangyunpark99 Apr 3, 2025
b3e6726
feat: UserJpaRepository에 이메일 기반 조회 메서드 추가 (#1)
sangyunpark99 Apr 3, 2025
386c329
feat: 회원가입 시 중복 이메일 검사 로직 추가 (#1)
sangyunpark99 Apr 3, 2025
06b6961
test: UserService 중복 이메일 예외 테스트 추가 (#1)
sangyunpark99 Apr 3, 2025
0eaa0f1
test: UserIntegrationTest에 중복 이메일 실패 케이스 추가 (#1)
sangyunpark99 Apr 3, 2025
a0f2565
refactor: enum 타입 constants 폴더의 enums 폴더로 분류 (#1)
sangyunpark99 Apr 3, 2025
3eaf910
refactor: BaseEntity 클래스 삭제 (#1)
sangyunpark99 Apr 4, 2025
bb9ca16
feat: 비즈니스 로직 예외처리를 위한 BusinessException 구현 (#1)
sangyunpark99 Apr 4, 2025
d4f540c
feat: ErrorCode enum 정의 및 기본 오류 코드 추가 (#1)
sangyunpark99 Apr 4, 2025
ed2b053
feat: 에러 응답시 사용되는 ErrorResponse 구현 (#1)
sangyunpark99 Apr 4, 2025
5ce6acf
chore: 불필요한 클래스 제거 (#1)
sangyunpark99 Apr 4, 2025
ff8762a
refactor: 전역 예외 처리 방식 통합 및 BusinessException 기반 구조로 개선 (#1)
sangyunpark99 Apr 4, 2025
114339e
refactor: BaseEntity 삭제 후, 필드에 createdAt, updatedAt 변수 추가 (#1)
sangyunpark99 Apr 4, 2025
3ab3f83
refactor: User 객체에 대한 LazyLoading 처리 (#1)
sangyunpark99 Apr 4, 2025
119f343
refactor: UserAddressMapper 유틸성 클래스로 변경 및 @Component 제거, 파라미터 변수에 fin…
sangyunpark99 Apr 4, 2025
01f37ab
feat: validation 메시지 삭제 (#1)
sangyunpark99 Apr 4, 2025
62e487e
refactor: ResponseEntity 삭제 (#1)
sangyunpark99 Apr 4, 2025
7177d52
test: 테스트 메시지 검증 삭제 (#1)
sangyunpark99 Apr 4, 2025
3b4de32
test: 테스트 메시지 검증 삭제 (#1)
sangyunpark99 Apr 4, 2025
29f3e5b
refactor: final 키워드 추가 (#1)
sangyunpark99 Apr 4, 2025
ff79ca6
refactor: 회원가입 로직 함수형 스타일로 리팩터링 및 예외 처리 일관화 (#1)
sangyunpark99 Apr 4, 2025
517c1ca
refactor: validation 메시지 제거 (#1)
sangyunpark99 Apr 4, 2025
0d5dc4a
test: 테스트 검증시 메시지 검증 제거 (#1)
sangyunpark99 Apr 4, 2025
1060d9a
test: UserServiceTest에서 UserMapper 제거 및 메서드 호출 방식 리팩터링 (#1)
sangyunpark99 Apr 4, 2025
5698b5b
feat: UserSignupResponseDto message 필드 삭제 (#1)
sangyunpark99 Apr 4, 2025
841bed8
refactor: 파라미터에 final 키워드 추가 (#1)
sangyunpark99 Apr 4, 2025
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
17 changes: 17 additions & 0 deletions .idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions user/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ out/

### VS Code ###
.vscode/

### applicaiton.yml ###
/src/main/resources/application.yml
3 changes: 3 additions & 0 deletions user/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.security:spring-security-crypto:6.4.4'

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
package com.sangyunpark.user.application;

import com.sangyunpark.user.application.mapper.UserMapper;
import com.sangyunpark.user.domain.dto.request.UserSignupRequestDto;
import com.sangyunpark.user.domain.entity.User;
import com.sangyunpark.user.exception.UserDuplicateException;
import com.sangyunpark.user.infrastructure.repository.UserJpaRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.sangyunpark.user.constant.ExceptionMessages.*;

@Service
@RequiredArgsConstructor
public class UserService {

private final UserMapper userMapper;
private final UserJpaRepository userJpaRepository;

@Transactional
public Long signup(UserSignupRequestDto userSignupRequestDto) {
User user = userMapper.toEntity(userSignupRequestDto);
Copy link
Collaborator

Choose a reason for hiding this comment

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

final User user = userMapper.toEntity(userSignupRequestDto);

userJpaRepository.findUserByEmail(userSignupRequestDto.email())
.ifPresent(u -> { throw new UserDuplicateException(EXCEPTION_USER_DUPLICATE.message()); });
Copy link
Collaborator

Choose a reason for hiding this comment

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

return userJpaRepository.findUserByEmail(userSignupRequestDto.email())
.map(s-> throw new UserDuplicateException(EXCEPTION_USER_DUPLICATE.message()) )
.orElseGet(u -> { User savedUser = userJpaRepository.save(user);

    return savedUser.getId();});

User savedUser = userJpaRepository.save(user);
Copy link
Collaborator

Choose a reason for hiding this comment

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

try{
User savedUser = userJpaRepository.save(user);
}catch(Exception ex){
logger.error(e.getMessage(), e)
throw e;
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

try{
String savedUser = json.toString(userJpaRepository.save(user));
return savedUser;
}catch(Exception ex){
logger.info(e.getMessage(), e)
return null;
}


return savedUser.getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.sangyunpark.user.application.mapper;

import com.sangyunpark.user.domain.dto.request.UserAddressRequestDto;
import com.sangyunpark.user.domain.entity.UserAddress;
import org.springframework.stereotype.Component;

@Component
public class UserAddressMapper {

public static UserAddress toEntity(UserAddressRequestDto dto) {
return UserAddress.builder()
.address(dto.address())
.defaultAddress(dto.defaultAddress())
.receiverName(dto.receiverName())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.sangyunpark.user.application.mapper;

import com.sangyunpark.user.domain.dto.request.UserSignupRequestDto;
import com.sangyunpark.user.domain.entity.User;
import com.sangyunpark.user.domain.entity.UserAddress;
import com.sangyunpark.user.domain.vo.UserStatus;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class UserMapper {

private static final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

public User toEntity(UserSignupRequestDto dto) {

UserAddress userAddress = UserAddressMapper.toEntity(dto.shippingInfo());

User user = User.builder()
.email(dto.email())
.username(dto.username())
.password(passwordEncoder.encode(dto.password()))
.registerType(dto.registerType())
.phoneNumber(dto.phoneNumber())
.userType(dto.userType())
.userStatus(UserStatus.ACTIVE)
.build();

user.addUserAddress(userAddress);

return user;
}
}
25 changes: 25 additions & 0 deletions user/src/main/java/com/sangyunpark/user/common/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.sangyunpark.user.common;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

@CreatedDate
@Column(updatable = false, nullable = false)
private LocalDateTime createdAt;

@LastModifiedDate
@Column(nullable = false)
private LocalDateTime updatedAt;
}

This file was deleted.

9 changes: 9 additions & 0 deletions user/src/main/java/com/sangyunpark/user/config/JpaConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.sangyunpark.user.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sangyunpark.user.constant;

public enum ExceptionMessages {

EXCEPTION_USER_DUPLICATE("이미 존재하는 회원입니다.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

not_found_user


private final String message;

ExceptionMessages(String message) {
this.message = message;
}

public String message() {
return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sangyunpark.user.constant;

public enum ResponseMessages {

SUCCESS_SIGNUP("회원가입이 완료되었습니다.");

private final String message;

ResponseMessages(String message) {
this.message = message;
}

public String message() {
return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.sangyunpark.user.constant;

public class ValidationMessages {
public static final String EMAIL_INVALID = "이메일 형식이 올바르지 않습니다.";
public static final String EMAIL_REQUIRED = "이메일은 필수로 입력해야 합니다.";

public static final String USERNAME_REQUIRED = "사용자 이름은 필수로 작성해야 합니다.";
public static final String USERNAME_LENGTH = "이름은 2자 이상 10자 이하로 입력해야 합니다.";

public static final String PASSWORD_REQUIRED = "비밀번호는 필수로 입력해야 합니다.";
public static final String PASSWORD_LENGTH = "비밀번호는 8자 이상 20자 이하여야 합니다.";

public static final String PHONE_REQUIRED = "전화번호는 필수입니다.";
public static final String PHONE_INVALID = "전화번호 형식이 올바르지 않습니다.";
public static final String PHONE_REGEX = "^010-?\\d{4}-?\\d{4}$";

public static final String REGISTER_TYPE_REQUIRED = "가입 유형은 필수입니다.";

public static final String SHIPPING_INFO_REQUIRED = "배송지 정보는 필수입니다.";

public static final String RECEIVER_NAME_REQUIRED = "수령인 이름은 필수입니다.";
public static final String ADDRESS_REQUIRED = "주소 정보는 필수입니다.";

public static final String USERTYPE_REQUIRED = "회원 유형은 필수입니다.";
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sangyunpark.user.domain.dto.request;


import com.sangyunpark.user.constant.ValidationMessages;
import jakarta.validation.constraints.NotBlank;

public record UserAddressRequestDto(
@NotBlank(message = ValidationMessages.RECEIVER_NAME_REQUIRED)
String receiverName,

@NotBlank(message = ValidationMessages.ADDRESS_REQUIRED)
String address,

boolean defaultAddress
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.sangyunpark.user.domain.dto.request;

import com.sangyunpark.user.domain.vo.RegisterType;
import com.sangyunpark.user.domain.vo.UserType;
import jakarta.validation.constraints.*;

import static com.sangyunpark.user.constant.ValidationMessages.*;

public record UserSignupRequestDto(

@Email(message = EMAIL_INVALID)
@NotBlank(message = EMAIL_REQUIRED)
String email,

@NotBlank(message = USERNAME_REQUIRED)
@Size(min = 2, max = 10, message = USERNAME_LENGTH)
String username,

@NotBlank(message = PASSWORD_REQUIRED)
@Size(min = 8, max = 20, message = PASSWORD_LENGTH)
String password,

@NotBlank(message = PHONE_REQUIRED)
@Pattern(regexp = PHONE_REGEX, message = PHONE_INVALID)
String phoneNumber,

@NotNull(message = REGISTER_TYPE_REQUIRED)
RegisterType registerType,

@NotNull(message = USERTYPE_REQUIRED)
UserType userType,

@NotNull(message = SHIPPING_INFO_REQUIRED)
UserAddressRequestDto shippingInfo
) {



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.sangyunpark.user.domain.dto.response;

public record UserSignupResponseDto(
Long userId,
String message
) {
}
62 changes: 61 additions & 1 deletion user/src/main/java/com/sangyunpark/user/domain/entity/User.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,64 @@
package com.sangyunpark.user.domain.entity;

public class User {
import com.sangyunpark.user.common.BaseEntity;
import com.sangyunpark.user.domain.vo.RegisterType;
import com.sangyunpark.user.domain.vo.UserStatus;
import com.sangyunpark.user.domain.vo.UserType;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

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

@Id @GeneratedValue
@Column(name = "user_id")
private Long id;

@Column(unique = true, nullable = false)
private String email;

@Column(nullable = false)
private String username;

@Column(nullable = false)
private String password;

@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserType userType;

@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserStatus userStatus;

@Column
private String providerId;

@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private RegisterType registerType;

@Column(nullable = false)
private String phoneNumber;

@Builder.Default
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserAddress> userAddress = new ArrayList<>();

public void addUserAddress(UserAddress address) {
this.userAddress.add(address);
address.setUser(this);
}

}
Loading
Loading