Kotlin In Action 1장
in Programming Language on Kotlin
소개
이 책의 대상? 자바를 어느 정도 알고 있는 개발자, 서버나 안드로이드 개발자 또는 JVM에서 실행될 프로젝트를 구축하는 모든 개발자
Kotlin의 주목적은 자바를 대체할 수 있고, 더 간결하고 더 생산적이며 안전한 대체 언어를 제공하는 것
코틀린의 핵심 특징
정적 타입 지정 언어와 동적 타입 지정 언어
자바와 마찬가지로 코틀린 또한 정적 타입 지정 언어이다.
정적 타입 지정 언어란 모든 프로그램 구성 요소의 타입을 컴파일 시점에 알 수 있고, 프로그램 안에서 객체의 필드나 메소드를 사용할 때마다 컴파일러가 타입을 검증해준다는 것을 의미한다.
이는 동적 타입 지정 언어와 상반되는 의미를 갖는데, JVM에서는 그루비나 JRuby가 대표적인 동적 타입 지정 언어이다.
동적 타입 지정 언어란 타입과 관계없이 모든 값을 변수에 넣을 수 있고, 메소드나 필드 접근에 대한 검증이 실행 시점(RunTime)에 일어나며 그에 따라 코드가 더 짧아지고 데이터 구조를 더 유연하게 생성하고 사용할 수 있다.
타입 추론
var x = 1
코틀린에서는 변수를 정의하면서 정수 값으로 초기화를 하는데, 이때 코틀린은 변수의 타입이 Int
임을 자동으로 알아낸다.
컴파일러가 문맥을 고려해 변수 타입을 지정하는 기능을 Type Inference(타입 추론)
이라고 부른다.
코틀린은 왜 정적 타입 지정 언어로 만들었을까?
정적 타입 지정 언어의 장점은 아래와 같다.
성능
실행 시점에 어떤 메소드를 호출할지 알아내는 과정이 필요 없으므로 메소드 호출이 더 빠르다.
신뢰성
컴파일러가 프로그램의 정확성을 검증하기 때문에 실행 시 프로그램이 오류로 중단될 가능성이 적어진다.
유지 보수성
코드에서 다루는 객체가 어떤 타입에 속하는지 알 수 있기 때문에 처음 보는 코드를 다룰 때 더 쉽다.
도구 지원
정적 타입 지정을 활용하면 더 안전하게 리팩토링 할 수 있고, 도구는 더 정확한 코드 완성 기능을 제공할 수 있으며, IDE의 다른 지원 기능도 더 잘 만들 수 있다. 즉 도구의 지원을 받기가 수월하다.
코틀린에서만 제공하는 특별한 타입
코틀린은 널이 될 수 있는 타입 즉, Nullable Type을 지원한다.
널이 될 수 있는 타입을 지원함에 따라 컴파일 시점에 NPE(Null Pointer Exception)이 발생할 수 있는 지 여부를 검사할 수 있어 좀 더 프로그래밍의 신뢰성을 높일 수 있다.
자바 개발자라면 NPE로 고통받아본 경험이 많을 것이다. 코틀린에서는 NPE에 대한 처리를 조금 더 똑똑하게 할 수 있게 Nullable Type을 지원하고 있다.
함수형 프로그래밍에 대한 지원
Kotlin은 객체 지향 프로그래밍(OOP)과 함수 지향 프로그래밍을 모두 지원하는 멀티 패러다임 프로그래밍 언어이다.
Kotlin에서는 어떻게 함수형 프로그래밍을 지원하는지 지금부터 알아보자.
함수형 프로그래밍과 객체지향 프로그래밍
함수형 프로그래밍의 개념은 아래와 같다.
일급 시민 함수
불변성
부수 효과(Side Effect) 없음
함수형 스타일로 프로그램을 작성하는 것에는 어떠한 이득이 있을까?
첫 번째는 간결성
이다.
함수형 코드는 명령형 코드에 비해 더 간결하며 우아하다.
순수 함수를 값처럼 활용할 수 있으면 더 강력한 추상화가 가능하며 코드의 중복을 막을 수 있다.
두 번째는 다중 스레드를 사용해도 안전하다는 것이다.
다중 스레드 환경에서는 적절한 동기화를 하지 않으면 여러 쓰레드가 하나의 데이터에 접근해 많은 문제가 발생한다.
그러나 불변 데이터 구조를 사용하고 순수 함수를 사용한다면 다중 스레드 환경이라도 같은 데이터를 여러 쓰레드가 변경할 수 없다.
즉, 복잡한 동기화 과정이 불필요해지는 것이다.
마지막은 테스트하기가 쉽다는 것이다.
Side Effect가 존재하는 함수는 그 함수를 실행할 때 전체 환경을 구성하는 준비 코드가 필요하지만 순수 함수는 그런 준비 코드 없이 독립적으로 테스트할 수 있다.
하지만 자유로운 Kotlin
그렇다고 해서 코틀린이 함수형 프로그래밍을 강제
하지는 않는다.
명령형 방식이 더 적합한 경우라면 부수 효과를 활용하는 함수를 사용하는 것을 코틀린은 허락한다.
그래서 코틀린은 자유롭게 객체지향과 함수형 접근 방법을 함께 조합하여 문제에 가장 적합한 도구를 사용하도록 개발자에게 자유를 주었다.
코틀린의 철학
주로 코틀린은 자바와의 상호운용성에 초점을 맞춘 실용적이고 간결하며 안전한 언어라고 설명한다.
각각에 대해서 조금 더 자세히 살펴보자.
실용성
코틀린은 연구를 위한 언어가 아니다.
최신 프로그래밍 언어 설계를 앞서 채택하거나 전산학계에서 연구 중인 혁신적인 아이디어를 코틀린을 통해 탐구하려고 하지 않는다.
또한 코틀린은 어느 특정 프로그래밍 스타일이나 패러다임을 사용하는 것을 강제로 요구하지 않는다.
코틀린을 처음 배우는 사람이라면 자바에서 사용해 온 익숙한 프로그래밍 스타일이나 기법을 사용할 수 있다.
추후에 코틀린의 강력한 특성을 발견하면 그 코드를 자신의 코드에 적용하는 방법을 배워서 간결하게 적용할 수 있다.
실용성 만큼이나 코틀린의 강력한 점은 도구를 강조한다는 점이다.
좋은 언어만큼이나 편리한 개발 환경도 생산성 향상에 필수적이다.
코틀린은 Jetbrain에서 개발된 언어이며 Jetbrain은 IDE를 개발하는 회사이다.
때문에 Jetbrain 사의 JVM IDE인 Intellij는 코틀린을 100% 활용할 수 있는 다양한 도구들을 제공해준다.
간결성
개발자는 코드를 새로 작성하는 시간보다 기존 코드를 읽는 시간이 더 길다는 사실은 자명한 사실이다.
때문에 코틀린은 더 간단하고 간결한 코드를 지향하고 있다.
그래서 코틀린에서는 개발자가 작성하는 것이 의미 없다고 판단되는 Getter,Setter, 생성자등을 묵시적으로 지원하면서 개발자가 일일이 이러한 코드를 작성하지 않게 하였다.
또한 코틀린에서는 컬렉션 프레임워크를 다룰 때 더 간결하게 작성하기 위해 람다를 지원하면서 쉽고 간결하게 처리할 수 있게 하였다.
반면 코틀린의 설계 목표에 가능한한 소스코드를 짧게 만들겠다
는 내용은 들어있지 않다.
예를 들어 연산자 오버로딩을 지원하지만 언어가 제공하지 않는 연산자를 프로그래머가 정의하는 것은 허용하지 않는다.
왜냐하면 메소드의 이름을 암호문 처럼 보이는 기호만으로 이루어진 연산자 식별자로 하는 것보다 단어로 이루어진 이름이 훨씬 읽거나 검색하기 쉽기 때문이다.
코드가 간결하면 작성하는데 시간이 덜 걸리는 것은 물론이고 읽는 시간 또한 덜 걸린다.
이것이 코틀린이 간결성을 추구한 이유이다.
안전성
코틀린에서 안전성을 보장하기 위해 NPE를 없애려고 노력했다.
코틀린의 타입 시스템은 null이 될 수 없는 값을 추적하며, 실행 시점에 NPE가 발생할 수 있는 연산을 사용하는 코드를 금지한다.
이는 매우 간단하게 가능한데 아래의 예제를 보자.
val s: String? = null // Nullable
val s: String = "" // Non-Nullable
코틀린에서는 기본타입으로 선언하면 Non-Nullable한 데이터 타입이 만들어지기 때문에 예기치 못한 NPE의 발생을 컴파일 타임에 미리 알 수 있다는 장점이 있다.
또 다른 예외로는 ClassCastException
이 있다.
어떤 객체를 다른 타입으로 캐스팅하기 전에 타입을 미리 검사하지 않으면 ClassCastException
이 발생할 수 있다.
코틀린에서는 타입 검사와 캐스트가 한 연산자에 의해 이루어진다.
코틀린은 이러한 기능등을 제공하며 더욱 안정성있는 프로그래밍이 가능해지게 되었다.
상호 운용성
자바 개발자들이 가장 관심있어 하는 질문은 자바의 기존 라이브러리를 그대로 사용할 수 있는가? 일 것이다.
답은 물론이다.
코틀린의 강력한 특징 중 하나는 기존 자바 코드나 라이브러리와 100% 호환된다는 특징을 갖고 있다.
자바에서 코틀린 코드를 사용하거나 코틀린에서 자바 코드를 사용할 때 그 어떤 노력도 필요없다. 그냥 쓰면 된다.
마찬가지로 자바의 API를 코틀린에서 사용할 때도 별다른 노력없이 사용 가능하다.
그러면 코틀린은 도대체 어떻게 컴파일 되길래 이게 가능한 걸까?
자바와 마찬가지로 코틀린 또한 컴파일 언어이다.
따라서 코틀린 코드를 실행하기 전에 코드를 컴파일 해야한다.
코틀린 컴파일러는 자바 컴파일러와 마찬가지로 코틀린 소스코드를 분석하여 byte code(.class)를 만들어낸다.
코틀린 컴파일러의 이름은 자바와 마찬가지로 kotlinc이며 커맨드라인에서 kotlinc를 사용해 코드를 컴파일하는 것이 가능하다.
kotlinc <소스파일 또는 디렉터리> -include-runtime -d <jar 이름>
java -jar <jar 이름>
코틀린 컴파일러로 컴파일한 코드는 코틀린 런타임 라이브러리에 의존한다.
런타임 라이브러리에는 코틀린 자체 표준 라이브러리 클래스와 코틀린에서 자바 API의 기능을 확장한 내용이 들어있다.
코틀린으로 컴파일한 애플리케이션을 배포할 때는 런타임 라이브러리도 함께 배포해야한다.
실제 프로젝트를 컴파일하기 위해 Maven,Gradle,Ant 등의 빌드 시스템을 사용한다.
Kotlin Runtime Library란 무엇일까?
스터디를 진행하면서 Kotlin Runtime Library는 어디에 존재하는 것인가에 대한 이야기가 나왔다.
이름이 Runtime Library이니 JRE에 포함되어있는건가? 라고 생각했지만, Java JDK를 다운받았지 Kotlin JDK를 다운받은 적은 없었기에 스터디원들 모두 혼란스러웠다.
집에 돌아와서 직접 jar의 내부를 까보았다.
깨알 꿀팁
jar를 zip으로 확장자를 바꾼 후 열어보면 jar의 내부를 볼 수 있다.
보다시피 kotlin-standardlibrary들이 추가되어있는 것을 확인할 수 있었다.
Java의 경우 표준 클래스 라이브러리들이 JRE에 포함되어 있다.
Kotlin 또한 Java의 표준 라이브러리와 별개로 자체 표준 라이브러리가 존재하며 이를 Kotlin에서는 Runtime Library라고 부른다.
그래서 jar 파일로 만들 때 아래와 같은 명령어를 수행하면 RuntimeLibrary를 jar에 포함시키지 않을 수 있다.
kotlinc-jvm hello.kt -d hello.jar
반대로 -include-runtime
옵션을 주면 RuntimeLibrary를 포함할 수 있는데, jar를 배포할 때는 일반적으로 jar에 RuntimeLibrary를 포함시키는 것이 일반적이기 때문에 아래의 명령어를 사용한다.
kotlinc-jvm hello.kt -include-runtime -d hello.jar
결국 우리가 혼란스러웠던 이유는 Kotlin에서 Runtime이라는 표현을 사용했기 때문인데 이는 JRE와 같은 Runtime이라기 보다는 일종의 class library에 더 가깝다고 생각된다.
보통 .jar 파일을 생성할 때 주로 gradle을 사용하다보니 Kotlin이 어떻게 컴파일되고 jar로 패키징되는지까지는 자세하게 몰랐었는데 스터디원들과 이야기 하다보니 의문이 생겨서 찾아보게 되었고 덕분에 좋은 공부가 되었다. (스터디 좋구먼~)
Kotlin에서 jar 파일을 만들 때 runtime을 jar에 포함시키는 이유
Kotlin에서 Lombok을 사용할 수 없는 문제
JVM에서는 코틀린이 먼저 컴파일 된 후 자바가 컴파일 된다.
코틀린이 자바의 롬복으로 생성된 클래스에 접근하려 하나 getter setter가 만들어지지 않아서 접근하지 못한다.
롬복은 컴파일 시간에 동작하는 애노테이션이기 때문이다.
강제로 컴파일러가 자바 먼저 컴파일하게 바꿀 수는 있지만 그러면 자바에서 코틀린 코드를 사용할 수 없다.
출처 : https://sehajyang.github.io/etc/2019/03/07/kotlin-and-lombok.html/
1장 요약
코틀린은 타입 추론을 지원하며 정적 타입 지정 언어이다. 따라서 소스코드의 정확성과 성능을 보장하며 소스코드를 간결하게 유지할 수 있다.
코틀린은 객체지향과 함수형 프로그래밍을 모두 지원한다. 코틀린에서는 일급 시민 함수를 사용해 수준 높은 추상화가 가능하고, 불변 값 지원을 통해 다중 쓰레드 애플리케이션 개발과 테스트를 더 쉽게 할 수 있다.
코틀린은 실용적이며 안전하고 간결하며 상호운용성이 좋다. 이는 코틀린을 설계하며 일반적인 작업에 대해 이미 잘 알려진 해법을 채택하고, NPE와 같이 흔히 발생하는 오류를 방지하며, 읽기 쉽고 간결한 코드를 지원하면서 자바와 아무 제약 없이 통합될 수 있는 언어를 맞추는데 초점을 맞췄다는 뜻이다.
Reference
Kotlin In Action (드미트리 제메로프, 스베트라나 이사코바)