TIL

TIL - 쇼핑몰 프로젝트(Plain Old) v1 → v2 7일 차. 값, 값 검증을 보자.

김뚜루 2023. 8. 11. 14:16

기분 좋은 1차 면접 합격 소식이 있었다.

면접에 대해 조금 말하자면 상당히 만족스럽고 감동까지 받았던 면접이라고 말할 수 있다.

면접에는 팀장을 포함해 총 3명의 면접관이 있었는데, 모두 내 블로그에 작성된 글을 하나도 빠짐없이 읽었고 심지어 프로젝트 클론까지 떠서 꼼꼼히 검토했다는 말씀을 해주셨기 때문이다.

집에 돌아오는 길에 이런 회사에 합류할 수 있다면 더 이상 소원은 없을 것 같다고 생각하기도 했다.

이제 남은 건 2차 임원 면접.

어떻게 될지 모르지만 최선을 다해보자.

중첩된 data class로 발생했던 오류.

@Entity
data class A(
   @ElementCollection
   val b: List<B>
)

@Embeddable
data class B(
   @Embedded
   val c: C
)

@Embeddable
data class C(
   val dateWithACrush: LocalDate
)

@Entity
data class A(
   @ElementCollection
   val c: List<C> // like a not nested embeddable in Entity
)

코드를 작성하고 빌드를 하려고 하는데 영문 모를 Could not locate setter method for property 에러가 발생했다. 찾아보니 에러가 발생한 원인은 ElementalCollection에 중첩된 embeddable에 있었다.

Hibernate는 기본적으로 setter를 이용해 필드 접근을 하는데 val로 설정되어 있으니 setter가 없어 접근하지 못하는 상황. (https://youtrack.jetbrains.com/issue/KT-26938)

해결 방법으로는 필드를 var로 변경하거나 @Access(AccessType.FIELD)로 설정하면 되는데, data class 불변성을 유지하기 위해 @Access(AccessType.FILED) 어노테이션을 사용해 해결했다.

자바에서는 이런 문제가 발생하지 않았는데, JPA는 여러모로 Kotlin과 사이가 좋지 않은 느낌적인 느낌이다

정규식을 한 곳으로 모아보자.

@Embeddable
data class Email(
    val value: String
) {
    init {
        val emailRegex = Regex("^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")

        if (!value.matches(emailRegex)) {
            throw InvalidEmailException()
        }
    }
}

위 코드는 값 객체가 스스로 유효성 검증을 하는 것을 볼 수 있다.

v1을 진행할 때는 몰랐지만 다시 보니 유효성 검증에 사용될 정규식을 init 블럭에서 선언하고 사용하는 모습이 상당히 불편하게 보였다.

여러 값 객체의 정규식을 수정해야 하는 상황이 발생하면 수정이 필요한 모든 값 객체 파일을 하나씩 수정해야 되어서 현재 방식보단 좀 더 편한 방식이 있을거라 생각했다.

그렇게 조금 고민하다 RegExp 객체를 제공하는 RegExpProvider 클래스를 작성하고, enum으로 작성한 정규식을 통해 Regex 객체를 돌려주는 방식으로 수정해 보았다. 코드는 아래와 같다.

@Embeddable
data class Email(
    val value: String
) {
    init {
        if (!value.matches(RegExpProvider.email())) {
            throw InvalidEmailException()
        }
    }
}

@Component
class RegExpProvider {
    companion object {
        fun email(): Regex {
            return Regex(Regexp.EMAIL.value)
        }
    }
}

enum class RegExp(
    val value: String
) {
    EMAIL("^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"),
    USERNAME("^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"),
    NAME("^[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]{2,}$")
}

그런데 성공적으로 값 객체에서 정규식을 추출해서 한 곳에서 관리하도록 변경한건 좋았지만, 값 객체 하나 추가할 때 RegExpProvider 의 메서드도 추가해야 하고 RegExp에 enum도 추가해야 되고…

작성해야 되는 코드 량이 배로 증가해 과연 잘한 것일까 심히 고민이다.