코틀린(Kotlin)이 안드로이드 공식 언어가 된 이유

코틀린 및 안드로이드 개발 일러스트

코틀린이 등장하기 전, 안드로이드 개발은 자바(Java)와 동의어였습니다. 자바는 세계에서 가장 널리 사용되는 언어 중 하나이지만, 안드로이드 에코시스템에는 제약이 많았습니다. 소송 및 호환성 요구 사항으로 인해 안드로이드는 오랫동안 오래된 버전(자바 6 및 7)에 머물러 있었습니다. 이는 장황한 상용구(boilerplate) 코드, 느린 개발 주기, 그리고 악명 높은 ‘10억 달러짜리 실수’인 NullPointerException으로 이어졌습니다.

2017년, 구글은 코틀린을 안드로이드의 최우선(first-class) 지원 언어로 발표하며 개발자 세상을 뒤흔들었습니다. 2019년에는 안드로이드 개발의 방향성을 ‘코틀린 퍼스트(Kotlin-First)‘로 선언하기에 이르렀습니다. 오늘날 상위 1,000개 안드로이드 앱 중 95% 이상이 코틀린으로 작성되었습니다.

코틀린이 자바를 완전히 대체하고 안드로이드 개발의 절대 강자로 자리 잡은 이유는 다음과 같습니다.


1. 제로 코스트의 널 안전성(Null Safety)

자바에서는 모든 객체 참조가 null이 될 수 있습니다. null 참조에서 메서드를 호출하려고 하면 앱이 NullPointerException(NPE)과 함께 작동을 멈춥니다. 이는 안드로이드 앱 크래시의 가장 주요한 원인입니다.

코틀린은 타입 시스템에 널 허용 여부를 직접 내장하여 이 문제를 해결합니다.

  • 널 불가능 타입(Non-Nullable Types): 기본적으로 변수는 null 값을 가질 수 없습니다 (val name: String = "Ghaznix"). 여기에 null을 할당하려고 하면 컴파일 에러가 발생합니다.
  • 널 가능 타입(Nullable Types): 변수가 null이 될 수 있다면 명시적으로 물음표를 붙여 선언해야 합니다 (var name: String? = null).
  • 안전한 호출(Safe Calls): ?. 연산자를 사용하여 속성에 안전하게 접근할 수 있습니다 (예: name?.length). 변수가 null인 경우 앱이 크래시되는 대신 null을 반환합니다.

2. 자바와의 100% 상호 운용성

새로운 프로그래밍 언어를 도입할 때 가장 큰 걸림돌 중 하나는 기존 코드를 다시 작성해야 한다는 점입니다. JetBrains는 자바와의 100% 상호 운용성을 염두에 두고 코틀린을 설계했습니다.

코틀린에서 자바 클래스를 호출하거나 자바에서 코틀린 클래스를 호출하는 작업을 아주 매끄럽게 수행할 수 있습니다. 덕분에 개발자들은 코틀린을 점진적으로 도입할 수 있었습니다. 기존의 레거시 자바 코드는 그대로 두고 새로운 기능만 코틀린으로 작성하여, 두 언어를 한 프로젝트 안에서 컴파일 오류 없이 혼용할 수 있었습니다.


3. 획기적인 상용구 코드 감소

자바는 장황한 코드로 유명합니다. 단순한 데이터 모델을 설정하는 데도 프라이빗 필드, 생성자, getter, setter 및 toString(), equals(), hashCode() 메서드를 작성해야 합니다.

코틀린은 이러한 상용구 코드를 완전히 없앴습니다. 간단한 사용자 데이터 모델을 정의하는 코드를 비교해 보겠습니다.

자바 구현 코드:

public class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(name, user.name) && Objects.equals(email, user.email);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, email);
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', email='" + email + "'}";
    }
}

코틀린 구현 코드:

data class User(var name: String, var email: String)

data 수식어를 사용하는 것만으로 코틀린은 내부적으로 getter, setter, equals(), hashCode(), toString()을 자동으로 생성합니다. 자바의 35줄짜리 클래스가 코틀린에서는 단 한 줄로 줄어듭니다.


4. 비동기 작업을 위한 코루틴(Coroutines)

모바일 앱은 UI가 멈추는 것을 방지하기 위해 백그라운드 스레드에서 네트워크 요청, 데이터베이스 작업, 파일 I/O를 수행해야 합니다.

자바에서 스레드를 관리하려면 RxJava나 지금은 더 이상 사용되지 않는 AsyncTask 클래스 같은 복잡한 라이브러리를 사용해야 했으며, 이는 흔히 ‘콜백 지옥(callback hell)‘을 야기했습니다.

코틀린은 가벼운 동시성 프레임워크인 코루틴을 도입했습니다. 코루틴을 사용하면 비동기식 비차단(non-blocking) 코드를 마치 순차적인 동기식 코드처럼 직관적이고 깔끔하게 작성할 수 있습니다.

// 코틀린 코루틴을 사용한 비동기 네트워크 호출
viewModelScope.launch {
    try {
        val user = apiService.getUserDetails(userId) // 메인 스레드를 차단하지 않고 실행을 일시 중단
        updateUI(user)
    } catch (e: Exception) {
        showError(e)
    }
}

5. 확장 함수(Extension Functions)

자바에서는 클래스의 기능(예: String에 포맷 지정 메서드 추가)을 확장하려 할 때, 해당 클래스를 상속받거나 유틸리티 클래스(StringUtils 등)를 새로 작성해야 했습니다.

코틀린은 소스 코드를 수정하거나 클래스를 상속받지 않고도 기존 클래스에 새로운 메서드를 추가할 수 있는 확장 함수 기능을 제공합니다.

// 이메일 유효성 검사를 위해 String 클래스 확장
fun String.isValidEmail(): Boolean {
    return android.util.Patterns.EMAIL_ADDRESS.matcher(this).matches()
}

// 사용 예시:
val email = "info@ghaznix.com"
if (email.isValidEmail()) {
    // 로그인 절차 진행
}

결론: 개발자 중심의 에코시스템

코틀린의 부상은 단순히 구글의 공식 지원 덕분만은 아닙니다. 그것은 개발자들의 뜨거운 지지와 만족도 덕분이었습니다. Stack Overflow 개발자 설문조사에서 코틀린은 항상 가장 사랑받는 프로그래밍 언어 중 하나로 선정되고 있습니다.

개발자의 편의성을 최우선으로 생각하고, 지루한 반복 코드를 줄이며, 널 안전성 버그를 근절함으로써, 코틀린은 안드로이드 개발 속도를 높였을 뿐만 아니라 전 세계 모바일 앱의 수준을 전반적으로 끌어올렸습니다.


Ghaznix 블로그에서 개발자 인사이트 더 알아보기 →