Kotlin을 사용하는 이유

홍석규·2022년 4월 19일
1

많은 개발자들이 개발 언어로 Kotlin을 사용하고 있습니다. 본 글에서 Kotlin 이란 어떤 언어이며, 어떤 장점이 있어 많은 개발자들이 사용하는지 알아보겠습니다.

Kotlin 이란

Kotlin은 JetBrains 사에서 2011년 처음 공개된 후 2016년 정식 출시된 오픈소스 프로그래밍 언어입니다. JVM 기반의 언어이며, Java와 유사하지만 훨씬 더 간결한 문법과 다양한 기능을 제공합니다. Java와 100% 호환이 가능한데 이는 Java 기반 프로그램이 모두 Kotlin으로 개발이 가능하다는 의미가 됩니다. 어떻게 100% 호환이 가능한 걸까요? 이를 알아보기 위해 JVM에서 Kotlin 언어가 어떻게 빌드 되는지 보겠습니다.

Kotlin이 빌드 되는 과정

Kotlin은 JVM 상에서 동작하기 때문에 빌드 되는 과정이 Java와 유사합니다.

  1. *.kt 확장자의 코틀린 코드 파일 작성
  2. Kotlin 컴파일러인 Kotlinc에서 *.kt 파일을 바이트 코드로 변환
  3. JVM에서 Kotlinc가 변환 한 바이트 코드를 기계어로 변환

이를 그림으로 표현하면 다음과 같습니다.

Kotlin 컴파일러와 Java 컴파일러 모두 Java 바이트코드인 class 파일로 변환하게 됩니다. 이는 Kotlin이 컴파일 되어 생성된 바이트 코드를 디컴파일 해 Java 코드를 확인할 수도 있다는 의미가 되는데요. 실제로 안드로이드 스튜디오와 같은 IDE에선 Kotlin으로 작성된 코드를 디컴파일 해 Java 코드로 보여주는 기능을 제공합니다.

Kotlin은 빌드 되면서 Kotlin 컴파일러를 거쳐 Java 바이트코드를 생성하고, JVM을 통해 기계어로 변환 되는 것을 알 수 있었습니다. Kotlin 컴파일러가 Java 바이트 코드를 생성하기 때문에 다시 Java로 디컴파일이 가능하며, Java와 100% 호환되는 것을 알 수 있습니다.

Kotlin 언어의 특징과 장점

Kotlin 공식 홈페이지에서 코틀린을 다음과 같이 이야기하고 있습니다.

  • A modern programming language that makes developers happier.

왜 이렇게 이야기하는 것일까요? Java는 처음 발표된 지 무려 20년이 지난 오래된 언어입니다. 처음 공개된 이후부터 지금까지 사용하면서 많은 문제점들이 발견되었고, 이를 수정하고 개선된 신버전들이 계속 출시되어 어느새 17 버전까지 나오게 되었습니다. Kotlin은 Java 언어가 처음 설계되었을 때부터 가진 많은 문제점들을 고려해서 언어를 설계했고 개발자가 더 쉽고 편리하게 작성할 수 있게 설계되었습니다. 그럼 Kotlin이 어떤 특징과 장점이 있는지 한 번 살펴보겠습니다.

  • 정적 타입의 언어

    • Kotlin은 컴파일 시점에 객체의 타입이 결정됩니다. 이를 정적 타입의 언어라고 표현하는데요. 이와 반대로 런타임 시점에 객체의 타입이 결정되는 동적 타입의 언어로 Python, JavaScript 등이 있습니다. 정적 타입의 언어는 컴파일 시점에 타입이 결정 됨으로써 런타임 시점에 문제가 발생할 확률이 훨씬 더 적어 상대적으로 안전한 언어라고 할 수 있습니다.
  • 타입 추론

    • Kotlin은 기본적으로 변수가 선언될 때 변수의 타입을 명시하지 않더라도 할당된 값을 보고 어떤 자료형을 가지는지 추론해 줍니다. 타입 추론 덕분에 반복적인 코드량을 줄일 수 있게 됩니다.
  • 간결한 언어

    • 코틀린은 개발자가 더 쉽고 편리하게 코드를 작성할 수 있게 설계되었다고 했습니다. 기존 Java 언어로 개발을 할 때 불필요하게 작성했던 boilerplate code를 줄일 수 있게 되었는데요. 가장 대표적인 내용이 data class입니다. 간단한 클래스를 예시로 data class를 사용할 경우 코드량이 얼마나 줄어드는지 Java 코드와 비교해 보겠습니다.
      //Kotlin
      data class Person(val name: String, val age: Int)
      
      //Java
      public class Person {
      	private String name;
      	private int age;
      
      	public Person(String name, int age) {
      		this.name = name;
      		this.age = age;
      	}
      	
      	@Override 
      	public int hashCode() {
      		return super.hashCode();
      	}
      
      	@Override
      	public boolean equals(Object obj) {
      		return super.equals(obj);
      	}
      
      	@Override
      	public String toString() {
      		return "Person" + "name = " + name + "age = " + age;
      	}
      }
      Java를 이용할 경우 불필요하게 추가되던 boilerplate code들이 Kotlin의 data class를 사용하니 훨씬 간결하고 편리하게 작성되는 것을 볼 수 있습니다. 이처럼 Kotlin은 동일한 코드도 더 간결하게 작성할 수 있게 도와줍니다.
  • Null safe 언어

    • Java 기반의 애플리케이션을 개발하다 보면 많은 NullPointer Exception을 경험한 적 있을 것입니다. Java 언어가 만들어질 때 Nullable에 대한 처리를 해주지 않았고, 예상하지 못한 많은 Null로 인한 문제가 발생하게 됩니다. 그나마 Java 8 버전 이후에 Optional이라는 Wrapper 클래스를 이용해 Null 체크를 지원해 줬는데요. Kotlin은 개발 단계에서부터 Null safe 한 언어로 개발되었습니다. Kotlin은 Nullable과 non-null 타입을 구분해둠으로써 Null이 가능한 타입을 별도로 명시해두었습니다. 예상치 못한 위치에 Null이 포함될 경우를 대비해 안전하게 Null을 처리하는 기능들을 지원해 Null로 인해 발생할 수 있는 문제점들을 최소화할 수 있게 되었습니다.
  • 코루틴

    • Kotlin은 비동기 처리와 효과적인 동시성 작업을 위해 코루틴이라는 아주 강력한 기능을 제공합니다. api 호출, db connect와 같은 I/O 작업은 비동기로 수행되는데요. 기존에는 비동기 처리를 위해 Callback을 사용하거나 RxJava와 같은 외부 플러그인을 이용해 비동기 로직을 처리했습니다.

      Callback을 사용할 경우 콜백 지옥이라고 할 정도로 코드의 depth가 깊어지고 RxJava를 사용할 경우 직관적이지 않은 코드와 불필요한 Stream 작업이 발생됩니다.

      코루틴을 사용할 경우 순차적인 코드 작성이 가능해 가독성을 높일 뿐만 아니라, 스레드를 더 효율적으로 사용해 성능도 좋아질 수 있습니다. 간단하게 코루틴으로 코드를 작성할 경우 Callback과 비교해 코드가 얼마나 가독성이 좋아지는지 보겠습니다.

      callback을 사용하는 경우 api 호출 성공과 실패에 대한 callback 메서드를 전달해야 합니다. 그리고 api 응답을 성공적으로 수신 한 경우 다른 api를 호출하면서 다시 성공과 실패에 대한 callback을 전달하게 되는데요. 이 과정에서 코드 depth가 깊어지면서 가독성이 상당히 안 좋아지는 것을 볼 수 있습니다.

fun upload() { //callback
    loading.postValue(true)
    api.prepareUpload().enqueue(object : Callback<PrepareUploadResponse>{
        override fun onResponse(call: Call<PrepareUploadResponse>, response: Response<PrepareUploadResponse>) {
            if (response.isSuccessful) {
                val path = response.body()
                api.upload(path).enqueue(
                    object : Callback<UploadResponse>{
                        override fun onResponse(call: Call<UploadResponse>, response: Response<UploadResponse>) {
                            if (response.isSuccessful) {
                                loading.postValue(false)
                                successToast.postValue(true)
                            }
                        }
                        override fun onFailure(call: Call<PrepareUploadResponse>, t: Throwable) {
                            loading.postValue(false)
                            Log.d("")
                        }
                    }
                )
            }
        }
        override fun onFailure(call: Call<PrepareUploadResponse>, t: Throwable) {
            loading.postValue(false)
            Log.d("")
        }
    })
}

fun upload() { //coroutines
    try {
        val path = api.prepareUpload()
        val response = api.upload(path)
        loding.postValue(false)
        successToast.postValue(true)
    } catch (e: Exception) {
        loading.postValue(false)
        Log.d("")
    }
}

이에 반해 코루틴으로 작성된 코드를 살펴보면 순차적인 코드 작성이 가능해 훨씬 가독성이 좋은 것을 알 수 있습니다.(물론 위 코드 그대로 동작하진 않습니다. 순차적인 코드 작성이 가능한 점을 나타내기 위한 예시일 뿐입니다.) 단순히 가독성을 높여줄 뿐 아니라 코루틴은 suspend 함수를 이용해 thread를 더 효율적으로 사용할 수 있습니다. 코루틴의 동작에 대해 더 알아보고 싶다면 아래 링크를 확인해 보세요.

추가로 Kotlin 공식 홈페이지에 가보면 함수형 프로그래밍 지원, 객체 지향 프로그래밍 지원, 멀티 플랫폼 지원 등 본 글에서 설명하지 않은 Kotlin이 제공하는 다양한 기능들을 확인해 볼 수 있습니다.

Kotlin을 사용하는 이유에 대해서 알아보기 위해 Kotlin이 어떻게 빌드되고 특징과 장점은 어떤 것들이 있는지 알아보았습니다. Java언어와의 100% 상호운용성, 간결한 언어, Null Safe 등 Kotlin을 사용하게 될 경우 얻을 수 있는 많은 이점들이 있는것을 알 수 있었는데요. 이러한 장점들 덕분에 60% 이상의 안드로이드 개발자들이 Kotlin 언어를 사용하고 있습니다. 또한 안드로이드 코드 스니펫도 Kotlin으로 먼저 작성되고 안드로이드 스튜디오 내부에서 Java to Kotlin 변환을 지원해 주고 있습니다. 아직 Kotlin을 접해본 적 없다면, Kotlin에 대해서 한 번 공부해 보는 게 어떨까요?

profile
학습한 내용을 공유하고 기록합니다.

0개의 댓글