인프런 커뮤니티 질문&답변

Summer Kim님의 프로필 이미지
Summer Kim

작성한 질문수

자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)

10강. 코틀린에서 상속을 다루는 방법

protected, private 필드들의 getter...

작성

·

360

·

수정됨

2

선생님, 안녕하세요

 

10강 코틀린 상속 편 보다가 질문 드립니다.

예제로 아래 JavaAnimail 클래스를 주셨고,

public abstract class JavaAnimal {

  protected final String species;
  protected final int legCount;

  public JavaAnimal(String species, int legCount) {
    this.species = species;
    this.legCount = legCount;
  }

  abstract public void move();

  public String getSpecies() {
    return species;
  }

  public int getLegCount() {
    return legCount;
  }

}

이와 동일한 코드로 아래 코틀린으로 변환했는데요,

package com.lannstark.lec10;

abstract class Animal(
    protected val species: String,
    protected val legCount: Int,
) {
    abstract fun move()
}

 

위의 자바 코드는 getter들이 public 으로 열려있어서 외부에서 인스턴스 생성 시 cat.getSpecies(), cat.getLegCount()등이 호출되나, 아래 코틀린에서 자동생성되는 getter의 경우 protected로 cat.species, cat.legCount 에 대해 가시성이 없습니다.

 

그러면...

  1. 클래스의 필드를 public으로 다 열고

     

     

     

a. val 는 더 이상 해줄 수 있는 것이 없음(어차피 생성자 초기화만 가능)

class Animal2(val species: String, val legCount: Int)

b. var는 private setter을 둔다.

class Animal3(species: String, legCount: Int) {
    var species = species
        private set
    var legCount = legCount
        private set
}

 

  1. 필드를 protected, private 으로 두고 별도의 public getter를 내부에 다시 만든다..
    (getter만 열 수도 없고, public으로 override도 안 되는 것 같습니다..!)

class Animal4(species: String, legCount: Int) {
    private val species = species
        public get // ❌Getter visibilty 는 필드와 일치해야 함
        override public get // ❌'override' is not applicable to 'getter'
    private val legCount = legCount

    fun getSpecies() = this.species;
    fun getLegCount() = this.legCount;
}

 

😔...흠 어떻게 하는게 맞을까요.

jpa 엔티티에서의 사용까지 생각하면 1-b가 가장 나을까요? 엔티티 필드들은 거의 var이기도 할테고요...

자바에서 사용하는 스타일로 마음이 가장 안심(?)인 건 2번인데, 이렇게 하면 코틀린의 코드 간결성과 편리함을 엔티티 내부에서는 거의 사용하지 못하는 것 같아서 그나마 코틀린스럽게 코드를 사용할 수 있는 옵션이 1-b 인 것 같다고 생각했는데요...

더 좋은 방법이 있는지... 어떻게 하는 것이 best practice인지, 또 선생님은 실무에서 어떻게 하시는지 궁금합니다!!

답변 1

0

최태현님의 프로필 이미지
최태현
지식공유자

안녕하세요, Summer Kim님! 정말 좋은 포인트 감사드립니다! 😊 👍

말씀해주신 것처럼 JavaAnimal 을 따라한 코틀린 Animal 에서 protected 필드를 갖게 되면, getter에 대한 public 권한이 없기 때문에 가시성 부분에 차이가 있었네요!! 🥺

때문에 정말 동일하게 하려면 다음과 같이 open val 을 사용해야 할 것 같습니다!
open val인 이유는, 코틀린에서 필드는 기본적으로 override가 불가능하기 때문에 하위 클래스에서 override를 열어주는 open 키워드가 필요하기 때문입니다! (강의에서는 첫 번째 하위 클래스를 만들어 보며 open 에 대해 설명드리고 있습니다! 🙂)

abstract class Animal(
    open val species: String,
    open val legCount: Int,
) {
    abstract fun move()
}

 

추가적으로, 질문 주신 가장 핵심적인 부분은 "jpa 엔티티에서의 사용까지 생각하면 어떻게 Entity 클래스를 구성해야 하는가"로 느껴져서 이 부분에 대해 답변 드려보도록 하겠습니다!

 

JPA 엔티티가 반드시(?) 지켜야 할 두 가지 규칙은 다음과 같습니다.

  • 리플렉션 활용을 위해 매개변수가 없는 기본 생성자 (NoArgumentConstructor)가 있어야 할 것.

  • 프록시 활용을 위해 연관관계 필드는 하위 클래스에서 override 할 수 있어야 할 것.

 

또한, 일반적으로 프로그래밍을 할 때 지키면 좋은 규칙은 다음과 같죠

  • 캡슐화를 위해 getter와 setter는 가급적 열지 말 것, 즉 필드의 외부 노출을 최소로 할 것

 

여기서 우선 JPA가 반드시 지켜야할 두 가지 규칙을 생각해보면 코틀린과 상당히 대조된다는 것을 느낄 수 있습니다. 코틀린은 주 생성자가 반드시 존재하기 때문에 매개변수가 없는 기본 생성자를 언어 차원에서 만들고 싶어도, 만들기 어렵습니다. 또한, 하위 클래스에서 override를 하려면 open 키워드를 붙여 주어야 하는데 모든 필드에 open 키워드를 붙이는 것 역시 까다롭죠

때문에 두 가지 플러그인을 JPA + Kotlin 조합에서 사용하게 됩니다.

  1. kotlin-jpa 플러그인 (내부에 no-args 플러그인이 JPA 어노테이션을 대상으로 적용)

  2. all open 플러그인 (직접 JPA 어노테이션 대상으로 설정이 필요)

자세한 내용은 다른 강의인 <실전! 코틀린과 스프링부트로 도서관리 애플리케이션 개발하기> 코프링과 플러그인 부분에서 확인해보실 수 있습니다! (꼭 결제하지 않으셔도 보실 수 있게 해당 영상은 잠시 무료로 얼여두었습니다! 😊)

image

다음으로는 '좋은 규칙'을 고민해 보아야 합니다. 코틀린에서는 var 필드를 그냥 사용하는 순간 setter가 자연스럽게 외부에 노출되기 때문에 대부분 경우 필요한 getter는 public으로 열어두고 어떻게 setter를 숨길 것일지 고민하게 되죠!

여기서 선택지는 두 가지가 있습니다.

 

  1. setter를 어떻게든 숨겨보자!

  2. 그냥 setter도 열어두고 setter를 쓰지 말자!

 

setter를 숨기는 방법은 말씀해주신 private set 을 활용할 수도 있고, backing property 라는 개념을 쓸 수도 있습니다! backing property 라는 개념은 아래와 같이 코드로 작성해볼 수 있습니다.

class A(
  private var _name: String, // private var _필드이름을 사용한 후
) {
  val name: String // getter를 명시적으로 public 하게!
    get() = this._name
}

 

이 두 가지 선택중에 저는 "매우 개인적으로" 두 번째 방법을 선호하는 편입니다. 다만 함께 프로젝트를 진행하는 팀원 전체가 setter의 위험성에 알고, setter가 열려 있어도 습관적으로 사용하지 않아야 한다는 무시무시한 조건이 선행되어야 해요 😭 때문에 코틀린 스프링을 함께 사용하시는 또 다른 개발자 분들께서는 setter를 어떻게든 숨기는 분들도 많이 계셨습니다. 결국 덜 좋은 방법과 조금 더 덜 좋은 방법 중에 선택해야 하는 느낌이에요...!

 

답변이 도움이 되었으면 좋겠습니다!! 또 궁금한 부분 있으시면~ 언제든 편하게 질문 남겨주세요! 😊

감사합니다!! 🙇

 

Summer Kim님의 프로필 이미지
Summer Kim
질문자

선생님~~ 친절한 답변 감사드려요~~

안그래도 자바에서는 별도 라이브러리지만 롬복 어노테이션 몇 개만 붙이면 간결하게 가져갈 수 있는데, 코틀린에서 왜 굳이 코드를 더 써야할까? 라고 생각했는데 코틀린도 자바처럼 플러그인을 활용하는군요..

좀 더 공부하고 연습하면서 고민해 보겠습니다💗

(이미 선생님의 코틀린 강의 기본편-고급편- 실전 프로젝트 연습편 까지 모두 구매해 두었답니다~😆😆😆)

최태현님의 프로필 이미지
최태현
지식공유자

앗! 벌써 모두 구매하셨군요 ㅎㅎㅎ 감사합니다! 😊 공부하시고 연습하시면서 어려운 점 편하게 물어봐주세요~~~ 🙇🙇

Summer Kim님의 프로필 이미지
Summer Kim

작성한 질문수

질문하기