영주의 개발노트
코틀린스럽다고? 너 누군데? 🤷♀️ 본문
최근 코드 리뷰에서 ‘OOO인 게 좀 더 코틀린스러운 코드입니다.’라는 피드백을 받은 적이 있다. 코틀린에 대해 공부해 본 적이 없는 난 이 리뷰를 받고 아래와 같은 생각이 들었다.
코틀린스럽다는 게 뭐지? 🤔

코틀린이 지향하는 바를 알아야 '코틀린스럽다'는 의미를 명확하게 파악할 수 있을 것이라고 생각했다. 그러기에 이에 대해 공부해보기로 했다.
코틀린스럽다?
코틀린은 기존 자바보다 더 간결하고 안전하며 직관적인 코드 작성을 목표로 설계된 언어이다. 그렇기에 '코틀린스럽다'라는 개념은 코틀린의 지향점과 주요 기능을 적극 활용하여 코드의 가독성과 효율성을 극대화하는 것을 의미한다. 코틀린스러운 코드로 나아가기 위해 코틀린의 핵심 원칙을 자바 언어와의 차이와 함께 정리해보고자 한다.
원칙 (1): 간결함과 가독성
코틀린의 핵심 목표 중 하나는 불필요한 코드를 줄이고, 의미를 명확하게 표현하는 것이다.
list가 있고 각 원소에 특정 연산을 한 값을 반환하는 새로운 리스트를 만들고자 한다. 불필요한 루프 대신 stream()을 사용하지 않고도 map, filter 등의 컬렉션 API를 활용하여 간결하고 가독성 높은 코드를 작성할 수 있다.
🚫 자바 스타일의 비효율적인 코드
val list = listOf(1, 2, 3)
val newList = mutableListOf<Int>()
for (i in list) {
val doubled = i * 2
if (doubled > 3) {
newList.add(doubled)
}
}
✅ 코틀린스러운 코드
val result = listOf(1, 2, 3).map { it * 2 }.filter { it > 3 }
원칙 (2): null safety
코틀린은 NPE를 방지하기 위해 타입 시스템에서 null을 엄격하게 관리한다. safe call(?.)과 evis 연산(?:)을 활용하여 null을 좀 더 쉽고 안전하게 처리할 수 있다.
🚫 자바 스타일의 비효율적인 코드
fun getLength(str: String?): Int {
return if (str != null) str.length else 0
}
✅ 코틀린스러운 코드
fun getLength(str: String?): Int = str?.length ?: 0
원칙 (3): 표현식 중심 코드
코틀린에서는 if, when 등을 statement가 아닌 expression으로 활용하는 것이 권장된다. 표현식으로 활용하여 간결하고 직관적인 코드를 작성할 수 있다. 불필요하게 return 이 여러 번 들어가는 걸 방지할 수 있고, when 문을 작성하게 된다면 더더욱 간결하고 직관적으로 표현이 가능하다.
🚫 자바 스타일의 비효율적인 코드
fun getGrade(score: Int): String {
if (score >= 90) {
return "A"
} else if(score >= 80) {
return "B"
} else if(score >= 70) {
return "C"
} else if(score >= 60) {
return "D"
} else {
return "F"
}
}
✅ 코틀린스러운 코드
fun getGrade(score: Int): String {
return if (score >= 90) {
"A"
} else if (score >= 80) {
"B"
} else if (score >= 70) {
"C"
} else if (score >= 60) {
"D"
} else {
"F"
}
}
fun getGrade(score: Int): String {
return when (score / 10) {
9 -> "A"
8 -> "B"
7 -> "C"
6 -> "D"
else -> "F"
}
}
원칙 (4): 컬렉션 적극 활용
List, Map 등의 컬렉션을 더 직관적이고 유용하게 사용할 수 있도록 코틀린 문법을 확장했다. 순서 혹은 키가 있는 컬렉션 요소에 접근할 때 배열 인덱스로 접근하는 것처럼 바로 접근 가능하다.
🚫 자바 스타일의 비효율적인 코드
val list = listOf(1, 2, 3)
println(list.get(0))
val map = mutableMapOf("a" to 1, "b" to 2)
map.put("c", 3)
✅ 코틀린스러운 코드
val list = listOf(1, 2, 3)
println(list[0]) // .get() 대신 [] 사용
val map = mutableMapOf("a" to 1, "b" to 2)
map["c"] = 3 // put 대신 사용 가능
원칙 (5): 중위(Infix) 호출 활용
코틀린은 연산자를 보다 자연스럽게 사용하기 위해 중위 호출 문법을 제공한다. equals() 대신 == 로 문자열 동일성 비교, mapOf에 key-value를 넣을 때 to로 key-value를 묶어주는 등의 직관적이고 읽기 쉬운 코드를 작성할 수 있도록 해준다.
🚫 자바 스타일의 비효율적인 코드
if (1 <= a && a <= b) {
println("a는 1과 b 사이에 있음")
}
✅ 코틀린스러운 코드
if (a in 1..b) {
println("a는 1과 b 사이에 있음")
}
다시 한번, 코틀린스럽다?
코틀린스럽다는 건 단순히 코틀린 문법을 사용하는 것이 아니라, 코틀린이 추구하는 간결성, 안정성, 표현 중심, 직관성을 코드에 잘 녹여내는 것이라고 생각한다.
여기서 드는 의문이 하나 있다. "코틀린스러운 게 마냥 좋기만 할까?"
내 대답은 아니오다. 뭐든 과도하게 사용하면 독이 된다. apply, let, run 등의 scope 함수를 과도하게 사용한다면 가독성이 떨어질뿐더러 코드의 흐름이 복잡해진다. 또한, 람다를 지나치게 사용하게 되면 JVM 최적화에 어려움을 겪을 수도 있다.
균형 ⚖️

그러므로, 균형을 잡는 것이 중요하다. 코틀린스러운 코드를 쓴다고 해서 항상 좋기만 한 것은 아니다. 가독성과 유지보수성을 해치면서까지 코틀린스러움을 추구하는 것은 오히려 독이 될 수도 있다.
언제, 어디서, 어떻게 사용하는 것이 적절한지 고민하는 것이 더 중요하다. 팀원들이 쉽게 이해할 수 있는 코드인지, 나중에 수정할 때 불필요한 복잡성을 유발하지는 않는지, 성능상 문제가 발생할 여지는 없는지 따져봐야 한다.
코틀린스러움을 살리되 무조건적으로 적용하기보다는 상황에 맞게 적절하게 활용하는 것이 바람직하다.
'STUDY 📖 > Kotlin' 카테고리의 다른 글
코루틴 뽀개기 - (1) (0) | 2025.03.12 |
---|