코틀린 매우 알은체하기
우아한 형제들에서 온라인으로 진행했던, 박재성님의 세미나 자료중 핵심적인 내용 + @ 를 정리해봅니다.
코틀린
코틀린은 멀티 플랫폼 언어를 지향한다.
- JVM, 자바스크립트, 네이티브 다 동작하게 할수 있는것이 현재 코틀린 재단의 니즈입니다.
코틀린 아이템
아이템1: 코틀린 표준 라이브러리를 익히고 사용하라.
자바의 라이브러리를 import 해서 쓰지말고, 코틀린 표준 라이브러리를 찾아서 공부하고 사용해라.
- 예를들어 Random 기능이 필요한경우, 자바의 경우 Thread Safe 하게 할지를 고민해야하지만, 코틀린에서 제공하는 표준 라이브러리 Random의 경우 기본적으로 Thread Safe 하게 제공합니다.
- 코틀린은 읽기 전용 컬렉션과 변경 가능한 컬렉션을 구별해서 제공합니다.
아이템2: 자바로 디컴파하는 습관을 들여라
익숙한 자바코드로는 어떻게 표현되는지를 확인하라.
- 관련있는 개인 블로그 게시글
shift x 2
이후 kotlin byte code 입력이 더 편합니다.
- java -> kotlin 전환시, 잘못된 코드를 짜지 않게 확인해보면서 코드를 작성해보는 습관을 들이는게 좋습니다.
코틀린과 Java를 같이 쓸수 있는 이유는 같은 JVM 언어이기 때문이다.
- 자바와 코틀린 코드를 동시에 쓰는경우 작성한 코틀린 코드가 코틀린 컴파일러를 통해서
class
파일로 1차로 변환됩니다. - 이후 변환된 class파일과, 자바로 구현되어있는 코드를 같이 또 다시 컴파일합니다. 이때 자바의 에너테이션 프로세싱이 진행됩니다.
아이템3: 롬복 대신 데이터 클래스를 사용하라.
데이터 전달이 주 목적인 클래스를 생성하는 경우, 데이터 클래스를 사용하라.
- 코틀린 컴파일 이후, 에너테이션 프로세싱이 진행되기 때문에 롬복을 통해서 생성된 자바코드는 코틀린에서 사용이 불가능합니다.
-
자바를 먼저 컴파일하게 하면 사용이 가능하나, 그러면 자바코드에서 코틀린 코드를 호출 할 수 없게 됩니다.
- 데이터 클래스를 사용하는것을 권장합니다.
- 많은 코드가 축약됩니다.
- 하나의 코틀린 파일에 여러개의 클래스 파일을 담아도 부담이 없습니다.
copy()
를 적절히 사용하면 데이터 클래스를 불변으로 관리할 수 도 있습니다.- 다만
toString()
도 구현되므로, 순함참조의 가능성이 열려있는 JPA의 Entity 에는 사용하지 않는 것을 권장합니다.
require를 통해서 검증이 가능합니다.
if(조건) throw Exception 과 같은 보일러 플레이트를 줄일 수 있습니다.
1 |
|
- validate 함수를 작성하지 않고
require(조건식)
을 넣어서 생성자 파라미터 검증을 해줄 수 있습니다. - 잘못된 값이 들어올경우
IllegalArgumentException
가 발생합니다. - 이 외에도
check
,assert
(JVM -ea 옵션이 들어간경우에만 동작, 비즈니스 로직에는 사용을 권장하지 않음.) 함수도 있습니다.
Kotlin + Spring Boot
final 키워드가 붙어서 스프링의 기능을 사용하지 못하는 경우를 주의해라.
스프링은 프록시 생성을 위해 상속을 많이 이용합니다.
1 |
|
- 스프링은 기본적으로
@Configuration
클래스에 대한 프록시를 생성합니다. - 하지만 final한 상태에서는 상속이 불가능하므로, open 키워드를 붙여주어야 스프링 기능을 제대로 사용할 수 있게 됩니다.
All-open 플러그인을 사용합니다.
지정한 애너테이션이 있는 클래스와 모든 멤버에 open 변경자를 추가할 수 있습니다.
1 |
|
- 스프링 이니셜라이저로 생성한 프로젝트의
gradle.kotlin
파일을 보면 아마 위와 같은 구문이 적혀 있을것입니다. - 해당 플러그인을 이용하여, 프록시가 필요한
@Component
,@Transactional
과 같은 스프링관련 어노테이션이 붙은 코드에는 open 변경자를 자동으로 붙이게 됩니다.
1 |
|
- 추가적으로 open 키워드가 추가적으로 필요한 경우에는 위처럼
gradle.kotlin
에 추가적으로 넣어주면 됩니다. - proxy 기반의 lazyloading 이 필요한 경우에는 필수적인 옵션입니다.
아이템4: 필드 주입이 필요하면 지연 초기화를 사용하라.
의존성이 주입될 필드를 널이 될수 있는 타입으로 관리하지말라.
1 |
|
lateinit
키워드를 이용해 지연 초기화를 사용해줍니다.- 테스트 코드를 작성하는 경우에 유용합니다.
아이템5. 변경 가능성을 제한하라. (w SpringBoot)
기본적으로 val로 선언하고, 필요할때 var로 변경하라.
students
의 경우에도, 실제로 프로퍼티로 가지고 있는 student는 언더바를 붙여서_student
로 관리하고, 접근이 가능한student
는 immutable 하게 해서 내보낼 수 있습니다.- 기본적으로 코틀린의 경우 프로퍼티의 이름이 중복되고 private한 값들은, cpp나 python 처럼 앞에 언더바를 하나 붙여서 관리하는것이 관례입니다.
1 |
|
- 공식 문서 에서도 해당 방법은 Backing properties 라고 부르고 있습니다.
On the JVM: Access to private properties with default getters and setters is optimized to avoid function call overhead.
- 실제로 어느정도 최적화를 지원해준다고도 합니다.
아이템6. 엔티티에 데이터 클래스 사용을 피하라. (w JPA)
양방향 연관관계의 경우 순환참조가 발생합니다.
toString()
,hashCode
를 호출할때 무한 순환 참조가 발생합니다.
아이템7. 영속화 되지 않는 필드는 사용자 지정 getter를 사용하라. (w JPA)
@Transient
를 사용하지 않아도 됩니다.
1 |
|
- JPA에 의해서 인스턴스화 될때, 초기화 블록이 호출되지 않습니다.
- 이 때문에
@Transient
가 붙고 로직이 있는경우, null이 될수 없는 val 타입임에도 불구하고, null이 삽입 될 수 있습니다. - 그러므로, 사용자 getter 를 이용하여 로직을 타게 하는 방법을 이용하는것을 권장합니다.
- 실제로 다음과 같은값은 getter만 존재하지 실제로 필드변수가 존재하지 않기 때문에, 해당 필드는 영속화 되지 않습니다.
아이템8. 널이 될수 있는 타입은 빠르게 제거하라. (w JPA)
아이디를 0또는 빈 문자열로 초기화하라, 확장함수를 사용해 반복되는 널 검사를 제거할 수 있다.
- Spring Data JPA에서 기본자료형을 사용하고 아이디를 0 으로 잡는경우, new 냐 아니냐를 판단해서 영속화를 하려고 시도합니다.
- 즉 id가 0인 경우, 새로운 엔티티라고 판단하고 영속화를 하려고 시도합니다.
- 해당 내용은 코틀린에 국한된것이 아니라, JPA의
isNew()
체크과정에서 일어납니다. 핵심은 코틀린에서 null이 정말 필요하지 않는경우, 굳이 nullable 하게 하여 다루지 않는것입니다.
1 |
|
- Repository 에서도 확장 함수를 이용해, 서비스에서 하는 중복되는 null check 작업을 레포지토리로 넣을 수 있게 됩니다.
- 다만 터트리고 싶은 예외가 서비스마다 다를수도 있으므로 고민은 해보아야합니다.
Persistence
No-arg 컴파일러 플러그인
JPA 엔티티에 필요한 기본 생성자를 만들어줍니다.
1 |
|
@Entity
,@Embeddable
,@MappedSuperclass
에 기본적으로 적용됩니다.- 이니셜라이저로 생성시 자동으로 추가됩니다.
TIP
잭슨 코틀린 모듈
매개변수가 없는 생성자가 없어도 직렬화, 역직렬화가 가능합니다.
1 |
|
- 스프링 이니셜라이저르 생성된 프로젝트에는 해당 의존성들이 기본적으로 포함되어 있습니다.
- 즉
ObjectMapper
를 주입을 받는다면, 잭슨 코틀린 모듈이 포함되어있습니다. - 중요한점은 스프링에서 제공하는
ObjectMapper
를 사용하지 않고 유닛테스트를 위해서 직접ObjectMapper
를 인스턴스화 사용하는 경우, 잭슨 코틀린 모듈이 포함되어 있지 않습니다. - 이러한 경우,
jackSonObjectMapper()
와 같은 기능을 이용해, 생성해주어야합니다.
- 예시로
Jackson2ObjectMapperBuilder.class
파일의 853 line 을 보면,KotlinDetector
를 통해서 해당 코드가 코틀린을 사용하고 있는지 확인하고 코틀린 모듈을 등록하고 있는것을 확인할 수 있습니다. - 위의 의존성들만 주입을 해준다면, 자동으로 코틀린 관련 jackson 모듈을 찾아서 등록해줍니다.
- 주의할점:
ZoneDateTime
의 경우에는jackSonObjectMapper()
으로 역직렬화가 불가능하므로,ObjectMapper
를 사용해야합니다.
코틀린 애너테이션
애너테이션 생성 방법을 정확히 지정해줄 수 있습니다.
@JsonProperty
의 경우 자바로 디컴파일 하면 필드에 에너테이션이 붙게 되어버립니다.- 만약 생성자 파라미터나, getter 를 통해서 애너테이션을 동작시키고 싶은경우에 문제가 됩니다.
@params:
@get:
을 통해서, 해당 애너테이션이 붙는 위치를 정해줄 수 있습니다.- 사용하는 라이브러리가 생성자 기반인지, getter 기반인지를 확인하고 에너테이션을 어디에 붙일지를 결정하시면 되겠습니다.
Sample Code
gradle.kotlin
큰따음표를 사용합니다.
- 코틀린 DSL을 이용해, 문법상 오류가 있는지 컴파일타임에 확인할수 있습니다.
- 마우스를 올려서 관련 Docs 에 빠르게 접근 할 수 있습니다.
DSL
1 |
|
- 위와같이 시큐리티에 관련된 코드를 스프링 진영해서 Kotlin DSL로 지원해주는것을 확인할 수 있습니다.
1 |
|
mockMvc
를 이용한 테스트 코드의 경우에도 위와같이 코틀린 DSL을 사용하고 있음을 확인할 수 있습니다.
1 |
|
- 만약 void를 반환하게 하고 싶은경우
just Runs
를 해주면 됩니다.
Reference
- https://youtu.be/ewBri47JWII