코틀린(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 개발자 설문조사에서 코틀린은 항상 가장 사랑받는 프로그래밍 언어 중 하나로 선정되고 있습니다.
개발자의 편의성을 최우선으로 생각하고, 지루한 반복 코드를 줄이며, 널 안전성 버그를 근절함으로써, 코틀린은 안드로이드 개발 속도를 높였을 뿐만 아니라 전 세계 모바일 앱의 수준을 전반적으로 끌어올렸습니다.