Spring 개발하면서 Spring Data JPA를 사용하면 기본적으로 JpaRepository를 상속받아 Repository 인터페이스를 만들게 된다.
이 때, 기본 제공해주는 findById 를 사용하면 Optional 타입을 리턴받는다.
Java 개발을 할 때면 기본 제공 메소드를 매번 구현할 필요가 없어서 굉장히 유용하게 사용하지만 Kotlin 에서도 그럴까?
아래 이어질 내용은 Kotlin에서 이 Optional 타입을 사용하는게 이상적인 형태인지를 고민해본 내용이다.
Null은 kotlin의 친구다!
kotlin 에서 Optional을 쓰면 안되나요?
We use here the CrudRepository.findByIdOrNull Kotlin extension provided by default with Spring Data, which is a nullable variant of the Optional based CrudRepository.findById. Read the great Null is your friend, not a mistake blog post for more details.
가장 먼저, kotlin - spring 튜토리얼을 보면 위와 같은 내용을 볼 수 있다.
사실 이런저런 글들을 뒤져봐도 Kotlin에서 Optional을 쓰면 안된다는 이야기는 없다.
왜 명쾌하게 Optional을 쓰고 안쓰고의 차이에 대한 내용이 없을까 하며 찾아보다보면 모든 Kotlin 개발자들은 기본적으로 Optional 대신 Nullable 타입으로 받아서 Null 처리를 하는걸 기본으로 생각하는걸 볼 수 있다.
위의 예시로 Spring Data 는 2.1.4 (Lovelace SR4) 버전부터 findByIdOrNull 이라는 확장함수를 제공하기 시작했다.
위에서 말한 findByIdOrNull 메소드가 추가된 PullRequest를 봐도 왜 Optional 대신 Nullable한 타입으로 반환을 하는지 이유는 정확히 나와있지 않다.
다만, 이유로 볼 수 있는 한 단어가 있었다.
💡 idiomatic
kotlin에서는 Null일 수도 있고 아닐 수 도 있는 값은 Nullable로 반환하는게 관용적이기 때문이라고 한다.
따라서, 미리 결론을 말하자면 Optional을 쓰면 안되는 이유는 없다!
그런데 kotlin을 사용하는데 굳이 자바에서만 가지고있는 타입인 Optional을 쓸 이유도 없다!
굳이 경우를 따지자면 kotlin은 아래와 같이 Optional 도 Nullable이냐 아니냐를 구분한다.
val optional: Optional<String>? = null
val optional: Optional<String> = Optional.empty()
물론, Spring Data JPA를 사용할 때 반환되는 Optional타입 자체는 NonNull인 값이다.
아래부터는 kotlin과 Null의 관계에 대한 다양한 의견들을 정리한 내용이다.
kotlin은 Null-Safety한 언어인데 Null을 쓰라고?!
넵 Null을 쓰면 됩니다.
개발자들이 Null을 피하는건 사실 NPE를 피하는 것이다.
Null 말고 값이 없음을 표현하는 방법이 있을까?
결국 Null은 필요에 의해서 나왔고 우린 이걸 안전하게 사용하면 된다.
그리고 우리는 Null을 피하는 개발을 하는게 아니라 NPE를 피하는 개발을 하면된다.
어려울 것도 없이 kotlin 문법에 따라서 개발을 하다보면 자연스럽게 대부분의 NPE를 피할 수 있다.
이게 kotlin의 가장 큰 장점이 아닐까,,
그렇게 Null이 좋은거였으면 자바에서는 괜히 Optional을 만들었겠나?!
이 이야기를 하기전에 먼저 생각해볼 것이 있다.
kotlin 1.0 메이저 버전이 세상에 나온건 2016년이다.
그리고, Optional이 추가된 java 8 버전은 2014년에 release가 되었다.
kotlin은 1.0 버전이 release 되기 전 까지 꽤 오랜시간이 걸렸는데 그 오랜시간동안 심지어 자바 기반으로 만들어진 언어가 왜 자바에서 그렇게 자랑하는 Optional을 선택하지 않았을까?
kotlin 진영에서 자바를 바라봤을 때 Null이 문제가 아니라 어떤 타입이든 Null로 초기화 할 수 있는 자바의 타입 시스템이 문제라고 생각했기 때문이다.
그래서 kotlin은 어떤 타입이든 Null로 받고 추가로 Optional로 래핑하는 개념이 아니라 처음부터 Null로 초기화 할 수 있는 타입과 Null로 초기화 할 수 없는 타입을 구분했다.
이러한 방식 덕분에 kotlin을 사용하는 우리는 NPE를 피하려고 노력하는 시간 대부분을 로직 고민에 쏟을 수 있다.
findByIdOrNull 에서도 결국 Optional의 orElse(null)을 사용하는데?
orElse 와 orElseGet은 성능상으로 차이가 있다.
orElse는 Optional이 래핑하고 있는 값이 Null이든 아니든 무조건 실행이 된다.
그에 반해 orElseGet는 Null일 때만 실행이 된다.
그러니 아래처럼 구현되어있는 findByIdOrNull을 사용하면 안된다?
fun <T, ID> CrudRepository<T, ID>.findByIdOrNull(id: ID): T? = findById(id).orElse(null)
orElse와 orElseGet은 각각 위 처럼 구현되어있다.
orElse는 Null 이 아니여도 실행된다는 말의 뜻은 orElse()의 argument가 실행이된다는 뜻이다.
아래 예시와 같은 경우를 말한다.
findById(id).orElse(new User()); // User 인스턴스가 생성된다.
findById(id).orElse(createUser()); // createUser() 메소드가 실행된다.
orElse(null), orElse({primitive type}) 에서는 해당사항 없는 이야기이다.
그렇기 때문에 findById(id).orElse(null)에서 Null 여부에 상관없이 orElse가 무조건 실행된다라는건 문제되지 않는다.
References
https://elizarov.medium.com/null-is-your-friend-not-a-mistake-b63ff1751dd5
'Language > Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린 의 apply, with, let, also, run 은 언제 사용하는가? (0) | 2024.04.05 |
---|---|
[Kotlin] Built-In 함수 활용법 정리(filter, map, let, apply, also) (0) | 2024.04.05 |
[Kotlin] 코틀린 null 처리 - ? ?. ?: !!, let, lateinit, 제너릭, 플랫폼 타입 category (0) | 2024.04.04 |
[Kotlin] 문법 정리 (1) | 2024.04.04 |
코틀린 sqlite 사용해보기 (0) | 2020.07.21 |