PH pullh
언어 가이드 / Kotlin

LANGUAGE DOSSIER

Kotlin은 모바일 제품팀이 출시 속도와 코드 안정성을 같이 챙길 때 강하다

Android 앱, 백오피스 도구, 경량 API를 한 팀이 함께 관리할 때 Kotlin은 null safety와 간결한 문법으로 커뮤니케이션 비용을 줄여 준다.

Android 제품팀핀테크/커머스 앱 팀사내 운영 도구 팀
Kotlin 커버 이미지
학습 예제 200
카테고리 12
잘 맞는 팀 앱 제품팀

기획 변경이 잦고 앱 릴리즈 주기가 짧은 조직일수록 Kotlin은 “읽는 속도”와 “고치는 속도”를 동시에 챙기기 좋다.

Why It Works

이럴 때 특히 힘을 발합니다

출시 리듬을 지키기 좋다

nullable 처리, data class, extension function이 화면 단위 작업을 짧게 묶어 준다. QA가 막판에 요구사항을 바꿔도 수정 범위를 읽기 쉽다.

도메인 모델링이 깔끔하다

sealed class와 value object를 조합하면 결제 상태, 인증 단계, 주문 생명주기 같은 비즈니스 규칙을 코드에 직접 올려둘 수 있다.

팀 온보딩 비용이 낮다

Java 경험이 있는 인력은 빠르게 적응하고, 신입 개발자는 장황한 보일러플레이트보다 핵심 흐름을 먼저 읽을 수 있다.

Business Scenes

사업 현장에서 자주 보이는 장면

결제 플로우 앱

실패 원인과 재시도 조건을 상태 타입으로 관리해 고객센터와 개발팀이 같은 언어로 장애를 추적할 수 있다.

매장 관리자 태블릿

오프라인 캐시, 스캐너 연동, 직원 권한 분기처럼 현장 로직이 많은 앱에서 가독성이 큰 장점이 된다.

백오피스 승인 도구

Spring 기반 내부 API와 함께 운영하면 앱과 서버 간 모델을 비슷한 감각으로 다뤄 문서화 부담이 줄어든다.

무엇을 줄였고 무엇을 더했나

Kotlin은 백지에서 설계된 언어가 아닙니다. Java가 이미 돌아가고 있는 자리에서, 같은 런타임을 쓰면서 매일 반복되던 불편만 걷어내는 쪽으로 만들어졌습니다. 그래서 배우는 순서도 다릅니다. 문법을 처음부터 익힌다기보다, Java에서 손으로 쓰던 것 중 무엇이 사라졌는지를 확인하는 과정에 가깝습니다.

사라진 쪽은 대체로 반복 작업입니다. 데이터 클래스 하나면 생성자와 접근자, 동등성 비교, 문자열 표현이 함께 생깁니다. 더해진 쪽이 더 중요합니다. 확장 함수는 남의 라이브러리 타입에도 메서드를 붙인 것처럼 쓰게 해 주고, when은 봉인된 타입과 만나면 경우를 빠뜨렸을 때 컴파일러가 지적하게 만듭니다. 위 예제에서 결제 결과를 봉인된 인터페이스로 묶은 이유가 여기 있습니다. 나중에 결과 종류가 하나 늘면, 처리하지 않은 자리가 실행 중이 아니라 빌드 단계에서 드러납니다.

다만 편의 문법에는 대가가 따릅니다. 확장 함수는 정의된 곳이 눈에 보이지 않으므로, 팀 규모가 커지면 “이 메서드가 어디서 왔는가”를 찾는 시간이 늘어납니다. 스코프 함수를 겹쳐 쓰면 줄은 짧아지지만 읽는 사람은 it이 무엇을 가리키는지 매번 되짚어야 합니다. 짧게 쓸 수 있다는 것과 짧게 써야 한다는 것은 다릅니다.

null을 타입으로 처리한다는 것

Kotlin에서 가장 크게 체감하는 변화는 null 처리입니다. 값이 없을 수 있다는 사실이 타입에 붙고, 컴파일러가 그 사실을 무시하지 못하게 막습니다. 실행 중에 방어 코드를 흩뿌리는 대신, 경계에서 한 번 확정하고 안쪽에서는 없는 경우를 신경 쓰지 않는 구조를 만들 수 있습니다.

여기서 흔히 놓치는 구멍이 하나 있습니다. Java 라이브러리에서 넘어오는 값은 널 가능 여부가 선언되어 있지 않은 경우가 많고, 그런 타입은 컴파일러가 판단을 보류합니다. 결과적으로 Kotlin 쪽 코드가 안전해 보여도 Java 경계에서 그대로 터질 수 있습니다. 대응은 단순합니다. 외부에서 들어온 값은 우리 도메인 타입으로 바꾸는 지점을 한 곳으로 모으고, 그 자리에서 널 여부를 확정하는 것입니다. 변수와 값 비교에서 다른 언어들이 같은 문제를 어떻게 다루는지 볼 수 있습니다.

느낌표 두 개를 붙이는 연산자는 “내가 책임진다”는 선언입니다. 코드 리뷰에서 이 연산자가 늘어나기 시작하면 대개 타입 설계가 어긋나 있다는 신호로 보는 편이 맞습니다.

Coroutines

코루틴은 스레드가 아닙니다

중단은 양보입니다

suspend가 붙은 함수는 기다리는 동안 스레드를 붙잡고 있지 않고 내려놓습니다. 그래서 수천 개를 동시에 띄워도 스레드 수천 개를 만드는 것과는 비용이 다릅니다. 대신 CPU를 계속 쓰는 작업을 코루틴으로 감싸는 것만으로는 아무것도 빨라지지 않습니다.

스코프가 수명을 정합니다

구조화된 동시성은 부모가 끝나면 자식도 함께 취소된다는 규칙입니다. 화면이 사라졌는데 요청이 살아남아 결과를 되돌려 주는 사고는 이 규칙을 지키면 대부분 사라집니다. 반대로 스코프 밖에서 코루틴을 띄우면 누수는 그대로 돌아옵니다.

색이 전염됩니다

중단 함수는 중단 함수 안에서만 호출됩니다. 그래서 비동기를 한 군데 도입하면 호출 사슬을 따라 위로 번집니다. 어디까지 중단 함수로 만들고 어디서 경계를 끊을지는 처음에 정해 두는 편이 낫습니다. 코루틴 가이드와 언어별 비동기 비교를 함께 보면 감이 잡힙니다.

Workflow

팀이 움직이는 방식

  1. 기획 변경이 생기면 먼저 상태 모델과 화면 이벤트 타입을 정리한다.
  2. ViewModel과 use case 계층에서 실패 케이스를 빠짐없이 매핑한다.
  3. QA 로그는 크래시 스택보다 “어떤 상태에서 눌렀는지”를 기준으로 정리한다.
  4. 릴리즈 직전에는 네트워크 경계보다 화면별 회귀 포인트를 먼저 잠근다.

실무 감각 코드

sealed interface CheckoutResult {
    data class Approved(val orderId: String) : CheckoutResult
    data class Rejected(val reason: String) : CheckoutResult
    data object Retryable : CheckoutResult
}

fun renderMessage(result: CheckoutResult): String = when (result) {
    is CheckoutResult.Approved -> "주문 ${result.orderId} 결제가 완료됐습니다."
    is CheckoutResult.Rejected -> "승인 실패: ${result.reason}"
    CheckoutResult.Retryable -> "네트워크를 확인한 뒤 다시 시도해 주세요."
}

결제 상태를 타입으로 고정해 두면 QA, 운영, 개발이 같은 용어로 장애를 본다.

Android와 서버, 같은 언어 다른 일

Kotlin을 쓰는 자리는 크게 둘로 갈립니다. Android 앱과 JVM 서버입니다. 언어는 같지만 실제로 붙잡고 있는 문제는 꽤 다릅니다.

Android 쪽에서는 언어보다 플랫폼이 일의 대부분을 차지합니다. 화면 수명 주기, 프로세스가 언제든 정리될 수 있다는 전제, 권한과 백그라운드 실행 제약, 기기마다 다른 동작. 코루틴 스코프를 화면 수명에 맞춰 두는 습관이 중요한 이유도 여기 있습니다. 구성 변경이나 화면 종료 시점에 살아남은 작업이 이미 사라진 화면을 건드리면 크래시나 재현하기 어려운 버그로 돌아옵니다.

서버 쪽에서는 반대로 Java 생태계를 그대로 물려받습니다. 라이브러리, 프레임워크, 배포 방식, 관측 도구가 모두 JVM 세계의 것입니다. Kotlin으로 짜더라도 스택 트레이스는 JVM의 것이고 성능 문제는 JVM 관점에서 풀게 됩니다. 그래서 서버에서 Kotlin을 쓰기로 했다면 Java를 읽을 수 있어야 하는 순간이 반드시 옵니다. Java 가이드를 함께 보시길 권합니다.

도구 체인은 어느 쪽이든 Gradle을 중심으로 돕니다. Kotlin DSL로 빌드 스크립트를 쓰면 IDE의 자동 완성이 붙지만, 빌드가 느려지거나 캐시가 어긋날 때 원인을 찾는 일은 여전히 별도의 기술입니다. 테스트는 JUnit 기반이 기본이고, 표현력이 더 필요하면 Kotlin 전용 테스트 프레임워크를 얹습니다. 코드 스타일과 정적 분석 도구를 CI에 걸어 두면 스코프 함수 남용 같은 논쟁을 규칙으로 넘길 수 있습니다. 코루틴을 디버깅할 때는 중단 지점을 넘나드는 스택이 끊겨 보이므로, 디버거 설정과 로깅에 코루틴 이름을 남기는 습관이 도움이 됩니다.

주제별로 더 파고들 곳은 함수형 스타일, 컬렉션, 테스트 쪽이고, 예제로 익히는 편이 맞다면 Kotlin 학습 라이브러리가 출발점입니다.

이럴 때는 Java로 남으세요

  • Android 네이티브 개발을 할 예정입니까. 이 경우 선택의 문제라기보다 기본값에 가깝습니다.
  • 이미 JVM 위에서 일하고 있고, null 관련 장애나 보일러플레이트가 실제로 팀의 시간을 먹고 있습니까. 그렇다면 도입 근거가 분명합니다.
  • 팀이 점진적으로 옮길 수 있습니까. 같은 프로젝트 안에서 두 언어를 섞을 수 있으므로 전면 전환을 전제로 할 이유는 없습니다.
  • 팀 대부분이 Java에 익숙하고 코드 리뷰 기준이 정착되어 있는데 Kotlin을 쓰는 사람이 혼자뿐이라면, 읽는 사람이 줄어드는 대가가 큽니다. 지금은 아닙니다.
  • JVM과 무관한 영역, 예를 들어 웹 프런트엔드나 데이터 분석이 목표라면 다른 선택지가 먼저입니다. 지금은 아닙니다.

워룸 로그

출시 3주 전, Kotlin이 Android 워룸의 언어가 된 이유

결제 SDK 교체와 앱 크래시가 동시에 들어온 주간. 팀은 화면을 뜯어고치기보다 상태 모델을 다시 세우는 쪽을 택했다.