강의

멘토링

로드맵

Inflearn Community Q&A

p's profile image
p

asked

Toby's Clean Spring - Domain Model Pattern and Hexagonal Architecture Part 1

Member Domain Model Extension

안녕하세요 코틀린으로 강의 수강 시 도메인 코드 질문드립니다

Written on

·

39

·

Edited

0

좋은 추석 보내고 계신가요?
현재 코틀린으로 강의를 따라해 보고 있습니다.

 

class Member private constructor(
    val email: String,
    var nickname: String,
    var passwordHash: String,
    var status: MemberStatus,
) {
    fun activate() {
        check(status == MemberStatus.PENDING) {
            "회원이 PENDING 상태가 아닙니다"
        }
        this.status = MemberStatus.ACTIVATE
    }

    fun deactivate() {
        check(status == MemberStatus.ACTIVATE) {
            "회원이 ACTIVE 상태가 아닙니다"
        }
        this.status = MemberStatus.DEACTIVATED
    }

    fun verifyPassword(
        password: String,
        passwordEncoder: PasswordEncoder,
    ): Boolean = passwordEncoder.matches(password, this.passwordHash)

    fun changeNickname(nickname: String) {
        this.nickname = nickname
    }

    fun changePassword(password: String) {
        this.passwordHash = password
    }

    companion object {
        fun create(
            email: String,
            nickname: String,
            password: String,
            passwordEncoder: PasswordEncoder,
        ): Member =
            Member(
                email,
                nickname,
                passwordEncoder.encode(password),
                MemberStatus.PENDING,
            )
    }
}

이러한 식으로 작성하였는데 자바에서는 const로 선언한 객체나 변수가 아닌 이상 기본적으로 가변입니다.

 

그런데 코틀린에서는 val, var 키워드에 따라서 var로 선언해야 가변 타입이 됩니다.

 

Member 도메인 모델 확장 챕터 수강하고 있는데 이 경우는 도메인에서 가변 속성을 미리 정의하고 해당 속성들을 var로 선언하는 것이 맞을지, 혹은 val을 통해 불변성을 확보하고 새 객체를 생성하여 변경을 처리하는 것이 적합할지 궁금합니다.

코틀린에서 도메인 코드를 작성할 때 자바와 다른 문법&개념과 도메인 중심 설계가 종종 난해할 때가 있네요.

javaspringspring-bootjpa리팩터링dddkotlin

Answer 2

0

tobyilee님의 프로필 이미지
tobyilee
Instructor

안녕하세요.

스프링의 JPA의 기술을 코틀린에서 사용할 때 모두 마주치는 고민이네요. 저도 실무에서는 코틀린을 주로 사용합니다. 스프링과 JPA가 워낙 익숙하기도 하고 좋은 기술이고 개발팀원들도 잘 사용하기 때문에 매번 선택하지만, 그래도 코틀린과 JPA는 역시 잘 안 맞아라는 생각은 매번 하게 되죠.

스프링도 그렇지만 JPA는 특히 이름의 기원에서 알 수 있듯이 자바 언어를 위해 설계된 오래된 기술입니다. 그리고 기본 전제는 자바 빈의 setter 유형에서 알 수 있듯이 외부에서 변경 가능한 가변 객체를 전제로 하고 만들어졌습니다. JPA가 필드를 제어하도록 하고 setter를 제거하는 등의 좋은 방식을 도입했지만 근본적으로 가변 객체로 설계된 기술이라는 점은 바뀌지 않습니다.

결론적으로 코틀린에서 JPA를 쓰신다면 JPA 엔티티에 대해서는 불변 객체 스타일이 아니라 가변 객체로 접근해야 하고, 그래서 var를 사용하시는 것이 전제 됩니다. VO처럼 임베딩 하는 객체의 경우는 그 자체로는 불변 객체로 만들 수는 있습니다. 하지만 이 객체가 엔티티의 프로퍼티로 사용될 때는 가변 프로퍼티가 되겠죠.

JPA는 오래전부터 더티 체킹을 이용해서 영속 객체의 상태 변화를 체크하고 업데이트 여부를 판단해왔습니다. 그래서 불변 객체를 매번 새로 만들어 쓰려면 억지로 merge를 매번 해야 하는 지저분한 코드가 될 수 밖에 없습니다. 그리고 대부분의 JPA 엔티티는 영속 상태를 오래 유지하지 않습니다. 하나의 트랜잭션 안에서만 잠시 영속상태를 유지하고 준영속 상태로 전환하거나 제거되죠. 그래서 DB와 매핑되는 엔티티를 불변 객체로 만들었을 때 얻을 수 있는 장점보다는 단점이 더 많아 보이기도 합니다.

아무튼 var를 사용하시고, 코드가 지저분해지지만 setter를 private으로 선언하시고 쓰시면 됩니다. 그러면 적어도 인스턴스를 새로 만들지는 않더라도 객체의 상태를 관리하는 로직의 안정성을 기대할 수 있을 것입니다. 혹은 코드를 깔끔하게 하도록 setter를 private로 만들지는 않고, 코딩 정책으로 이를 사용하지 않게 권장 또는 강제하기도 합니다. 최근엔 이 스타일이 차라리 마음에 드네요. ArchUnit 등에서 setter 호출 여부를 체크해서 테스트를 실패하게 할 수 있습니다.

근본적으로 코틀린에서는 코틀린 언어의 사상을 따라서 만들어진 기술을 쓰는 것이 가장 좋다는 것이 원칙이지만, 그러기에는 나와있는 기술이 너무 빈약합니다. 생태계가 거의 없고 개발도 너무 더뎌서 25년 가까이 발전해온 자바 ORM 기술이 해결하는 많은 문제를 처리하려면 많은 수고를 해야 하겠죠.

저는 써보지는 않긴했는데 하이버네이트에서 val을 사용하는 불변 객체로 JPA를 사용하는 방법이 있다는 걸 들은 적이 있습니다만 확실하지는 않습니다. 한번 찾아보세요.

0

안녕하세요, 인프런 AI 인턴입니다. 정라잇님, 코틀린으로 도메인 모델을 작성하실 때의 어려운 점에 대해 질문 주셨어요. 코틀린의 val과 var는 자바의 final과 다르게 불변성과 가변성을 보다 명확하게 구분해 주므로, 도메인 모델을 설계할 때 중요한 고려 사항이 됩니다.

도메인 모델에서 가변성을 어떻게 다룰 것인지는 도메인의 특성 및 요구사항에 따라 변할 수 있습니다. 코틀린에서 불변성을 유지한다는 것은 도메인 객체가 변하지 않도록 설계하여, 객체 자체의 상태를 보장할 수 있게 해줍니다. 이는 특히 여러 스레드에 의해 객체가 접근될 수 있는 환경에서 유용합니다.

코틀린에서 도메인 코드 작성 시 고려사항:

  1. 불변성을 고려한 설계: 생성 시 값이 절대 변하지 않아야 한다거나 비즈니스 로직에서 상태 변화가 자주 발생하지 않는 경우, val로 선언하여 불변성을 처리하는 것이 좋습니다.

  2. 가변성이 필요한 경우: 사용자가 특정 기능(예: 비밀번호 변경, 상태 변경)을 사용할 때, 도메인 모델의 일부 속성을 변경해야 할 필요가 있습니다. 이런 경우는 var로 설정해도 괜찮습니다. 그러나, 가변성이 현재 디자인에 필요할 때는 그 변화를 적절히 관리할 수 있는 로직, 예를 들어 상태 전환 메소드와 같은 안전한 메커니즘을 준비해야 합니다.

코드상에서는 비밀번호를 변경하는 기능이나 애플리케이션 로직에서 nickname을 변경하는 기능이 있으므로, var로 설정되어 있습니다. 이는 비즈니스 요구사항이 변경되는 경우에도 잘 대응할 수 있는 방식입니다.

도메인 모델의 불변성을 반드시 보장해야 한다면, 변경을 적용할 때마다 새로운 객체를 반환하여 수정하도록 설계할 수도 있습니다. 다만, 그러한 설계는 성능과 메모리 사용에 추가적인 고려가 필요하다는 점을 염두에 두어야 합니다.

강의에서 다루어진 내용과 관련된 기존 게시물을 찾아보면 도움이 될 경우가 있습니다. 관련된 주제를 잘 설명한 게시물을 추천합니다:

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

p's profile image
p

asked

Ask a question