본문 바로가기
프로그래밍/Project

[Project01] MiniBoard(3) - Custom Annotation 생성, 사용자 Model 생성

by 코딩중독 2024. 1. 25.

Custom Annotation 생성

각 클래스의 역할 세분화를 위해 Business, Converter Annotation을 만든다.

요청 로직의 흐름은

Client -> Controller -> Business -> Service -> Repository 순으로 흘러가고, 응답은 역순으로 반환한다.

요청과 응답의 모델이 다르기 때문에 도메인별로 Converter 클래스를 사용할 예정이다.

@Business

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
public @interface Business {

    @AliasFor(annotation = Service.class)
    String value() default "";

}

 

@Converter

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Service
public @interface Converter {

    @AliasFor(annotation = Service.class)
    String value() default "";

}

 

사용자 모델 생성

UserEntity

MySQL user 테이블과 매칭

사용자의 상태값을 Java에서 enum 클래스로 관리하는데 MySQL의 varchar 타입과 맞추기 위해 어노테이션을 추가했다.

실습을 진행할 때 스프링 버전 2.7.9에서는 @Enumerated(EnumType.STRING)으로 사용했는데, 새로운 프로젝트는 3.2.1로 생성을 했더니 DB 연결에서 status를 찾지 못한다고 해서 한참을 헤맸다.

@Entity
@Table(name = "user")
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
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 salt;

    @Column(length = 100, nullable = false)
    private String password;

    //    @Enumerated(EnumType.STRING)  // 'org.springframework.boot' version '2.7.9'
    @Convert(converter = UserStatusConverter.class) // 'org.springframework.boot' version '3.2.1'
    @Column(length = 50, nullable = false)
    private UserStatus status;

    private LocalDateTime registeredAt;

    private LocalDateTime unregisteredAt;

}

 

UserStatusConverter

스프링부트 3.x.x대 버전에서는 컨버터 클래스를 생성하고 달아줘야 한다.

/**
 * 스프링부트 3.x.x 버전에서 java:enum 과 mysql:varchar 맵핑 시키기 위한 컨버터 클래스
 */
public class UserStatusConverter implements AttributeConverter<UserStatus, String> {
    @Override
    public String convertToDatabaseColumn(UserStatus attribute) {
        if (Objects.isNull(attribute)) {
            throw new NullPointerException("Enum Converting String - UserStatus is null");
        }

        return attribute.toString();
    }

    @Override
    public UserStatus convertToEntityAttribute(String dbData) {
        return UserStatus.valueOf(dbData);
    }
}

 

UserRegisterRequest

회원가입을 위해 클라이언트가 입력한 값을 담당할 친구이다.

@Builder ♥ 너무좋다...

@Data 어노테이션은 Getter, Setter, ToString, RequiredArgsConstructor 등의 어노테이션이 모두 포함된 아이인데, 값의 변경이 불가피한 상황에서만 사용하고 Getter 어노테이션만 사용한다.

객체의 일관성을 유지하고 값의 변경이 불가피한 경우에만 사용할 예정이다.

하지만 버릇처럼...

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserRegisterRequest {

    @NotBlank
    private String name;

    @NotBlank
    @Email
    private String email;

    @NotBlank
    private String password;

}

 

UserResponse

클라이언트에서 사용자에 대한 요청을 처리한 후 응답값을 담당할 친구이다.

비밀번호를 제외하고 사용자의 정보를 전달한다.

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserResponse {

    private Long id;

    private String name;

    private String email;

    private UserStatus status;

    private LocalDateTime registeredAt;

    private LocalDateTime unregisteredAt;

}

 

마치며

이제 회원가입을 위한 준비가 얼추 마무리된 상태라 다음에는 회원가입 로직을 진행한다.

개인 프로젝트를 진행하며 기록하는 내용이라 정보를 얻고자 하는 누군가에게는 불친절한 글이 될 수도 있지만 댓글에 질문 주시면 제가 아는 선에서는 답변드리도록 하겠습니다.

잘못된 내용도 지적해 주시면 감사하겠습니다.