@Data
@SuperBuilder
@MappedSuperclass
@AllArgsConstructor
@NoArgsConstructor
public class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
@Entity
@Table(name = "user")
@Getter
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class UserEntity extends BaseEntity {
@Column(length = 50, nullable = false)
private String name;
@Column(length = 100, nullable = false)
private String email;
@Column(length = 100, nullable = false)
private String password;
@Column(length = 50, nullable = false)
@Enumerated(EnumType.STRING)
private UserStatus status;
@Column(length = 150,nullable = false)
private String address;
private LocalDateTime registeredAt;
private LocalDateTime unregisteredAt;
private LocalDateTime lastLoginAt;
}
@Repository
public interface UserRepository extends JpaRepository<UserEntity,Long> {
// select * from user where id =? and status =? order by id DESC limit 1;
Optional<UserEntity> findFirstByIdAndStatusOrderByIdDesc(Long userId, UserStatus userStatus);
// select * from user where email =? and password =? and status =? order bt id desc limit;
Optional<UserEntity> findFirstByEmailAndPasswordAndStatusOrderByIdDesc(String email, String password, UserStatus userStatus);
}
findFirstByIdAndStatusOrderByIdDesc라고 userid, userStatus를 이용해 옵셔널을 반환한다findFirstByEmailAndPasswordAndStatusOrderByIdDesc라고 email, userid,userStatus를 이용해서 옵셔널을 반환한다어노테이션을 이용해서, service로 컴포넌트 등록도 하고, 명시적으로 분리해줄 것이다
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
public @interface Business {
@AliasFor(annotation = Service.class)
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
public @interface Converter {
@AliasFor(annotation = Service.class)
String value() default "";
}


public void statusAndRegisteredAt(UserStatus status, LocalDateTime registeredAt){
this.status=status;
this.registeredAt=registeredAt;
}
statusAndRegisterdAt 메서드를 추가해주었다/**
* User 도메인 로직을 처리하는 서비스
*/
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
@Transactional
public UserEntity register(UserEntity userEntity) {
return Optional.ofNullable(userEntity)
.map(it -> {
it.statusAndRegisteredAt(UserStatus.REGISTERED, LocalDateTime.now());
return userRepository.save(it);
})
.orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT, "User Entity Null"));
}
public UserEntity login(String email, String password){
UserEntity userWithThrow = getUserWithThrow(email, password);
return userWithThrow;
}
public UserEntity getUserWithThrow(String email, String password){
return userRepository.findFirstByEmailAndPasswordAndStatusOrderByIdDesc(
email, password, UserStatus.REGISTERED
).orElseThrow(() -> new ApiException(UserErrorCode.USER_NOT_FOUND));
}
public UserEntity getUserWithThrow(Long userId){
return userRepository.findFirstByIdAndStatusOrderByIdDesc(
userId, UserStatus.REGISTERED
).orElseThrow(() -> new ApiException(UserErrorCode.USER_NOT_FOUND));
}
}
register기능이 있다findFirstByEmailAndPasswordAndStatusOrderByIdDesc 메서드를 이용해 UserEntity를 반환한다항상 말했듯이, Api스펙으로 entity를 보내주면 위험하다
- 그렇기에, 로그인할 때 필요한 데이터를 받는 UserLoginRequest와
- 회원가입할 때 필요한 데이터를 받는 UserRegisterRequset와
- User엔티티에 db가 만들어주는 pk를 포함해서 응답을 보내는 UserRespone즉
- DTO 계층을 만들어준다
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserLoginRequest {
@NotBlank
private String email;
@NotBlank
private String password;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserRegisterRequest {
@NotBlank
private String name;
@NotBlank
@Email
private String email;
@NotBlank
private String address;
@NotBlank
private String password;
}
@Data
@RequiredArgsConstructor
@AllArgsConstructor
@Builder
public class UserResponse {
private Long id;
private String name;
private String email;
private UserStatus status;
private String address;
private LocalDateTime registeredAt;
private LocalDateTime unregisteredAt;
private LocalDateTime lastLoginAt;
}
@Converter
@RequiredArgsConstructor
public class UserConverter {
public UserEntity toEntity(UserRegisterRequest request){
return Optional.ofNullable(request)
.map(it -> {
// to Entity
return UserEntity.builder()
.name(request.getName())
.email(request.getEmail())
.password(request.getPassword())
.address(request.getAddress())
.build();
})
.orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT, "UserRegisterRequest Null"));
}
public UserResponse toResponse(UserEntity userEntity) {
return Optional.ofNullable(userEntity)
.map(it->{
// to response
return UserResponse.builder()
.id(userEntity.getId())
.name(userEntity.getName())
.status(userEntity.getStatus())
.email(userEntity.getEmail())
.address(userEntity.getAddress())
.registeredAt(userEntity.getRegisteredAt())
.unregisteredAt(userEntity.getUnregisteredAt())
.lastLoginAt(userEntity.getLastLoginAt())
.build();
})
.orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT, "UserEntity Null"));
}
}
toEntity 매서드를 만든다toResponse도 만들어준다@Business
@RequiredArgsConstructor
public class UserBusiness {
private final UserService userService;
private final UserConverter userConverter;
/**
* 사용자에 대한 가입처리 로직
* 1. request -> entity
* 2. entity -> save
* 3. save Entity -> response
* 4. response return
*/
public UserResponse register(UserRegisterRequest request) {
/*UserEntity entity = userConverter.toEntity(request);
UserEntity newEntity = userService.register(entity);
UserResponse response = userConverter.toResponse(newEntity);
return response;*/
return Optional.ofNullable(request)
.map(it->userConverter.toEntity(it))
.map(it->userService.register(it))
.map(it->userConverter.toResponse(it))
.orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT, "request null"));
}
}
@RestController
@RequiredArgsConstructor
@RequestMapping("/open-api/user")
public class UserOpenApiController {
private final UserBusiness userBusiness;
// 사용자 가입 처리
@PostMapping("/register")
public Api<UserResponse> register(@Valid @RequestBody Api<UserRegisterRequest> request){
UserResponse response = userBusiness.register(request.getBody());
return Api.OK(response);
}
// 로그인
@PostMapping("/login")
public Api<TokenResponse> login(@Valid @RequestBody Api<UserLoginRequest> request){
...
// Todo : token검증 기능 business계층에 추가하기
return Api.OK(response);
}
}
register메서드를 통해서 (변환,저장,변환)을 한 후, UserResponse형태로 반환한다