서론: for 루프의 종말
자바(Java)를 쓰다가 코틀린(Kotlin)으로 넘어왔을 때 가장 충격적이고 매력적인 기능은 무엇일까요?
저는 단연코 컬렉션 API (Collection Framework)라고 생각합니다.
과거에는 리스트를 다루기 위해 무조건 for 문을 돌리고, 임시 리스트(tempList)를 만들고, if 문으로 거르는 작업을 반복했습니다. 하지만 코틀린에서는 이 모든 과정이 단 한 줄로 끝납니다.
오늘은 코틀린 개발자가 숨 쉬듯이 사용하는 3대장 함수 map, filter, forEach 를 아주 깊게 파헤쳐 보겠습니다.
1. 데이터 변환 공장: map
map은 리스트 안에 있는 요소들을 하나하나 꺼내서 다른 형태로 변환하여 새로운 리스트를 만들어주는 함수입니다.
핵심 개념
- Input: A 타입 리스트 (예:
List<String>) - Output: B 타입 리스트 (예:
List<Car>) - 특징: 리스트의 크기(Size)는 변하지 않습니다.
실전 예제 (자동차 경주)
사용자가 입력한 이름 문자열("pobi", "woni")을 Car 객체로 변환해야 하는 상황입니다.
val names = listOf("pobi", "woni", "jun")
// [Bad] 고전적인 방식
val cars = mutableListOf<Car>()
for (name in names) {
cars.add(Car(name))
}
// [Good] map 사용
val cars = names.map { name ->
Car(name)
}
// [Best] 생성자 참조 (Method Reference)
val cars = names.map(::Car)
map을 쓰면 mutableList를 만들 필요도 없고, add를 호출할 필요도 없습니다. 코드가 선언적으로 바뀝니다. "이 리스트를 Car로 매핑해줘"라고 말이죠.
2. 데이터 거름망: filter
filter는 조건에 맞는 요소만 살려두고, 나머지는 버리는 함수입니다.
핵심 개념
- Input: 리스트
- Condition:
Boolean을 반환하는 람다식 (true면 유지, false면 제거) - Output: 크기가 같거나 줄어든 리스트
실전 예제 (우승자 찾기)
가장 멀리 이동한 자동차(우승자)를 뽑아내야 합니다.
// 1. 가장 멀리 간 위치(maxPosition) 구하기
val maxPosition = cars.maxOf { it.position }
// 2. 그 위치에 있는 차들만 필터링하기
val winners = cars.filter { car ->
car.position == maxPOsition
}
⚠️ 주의할 점
초보자들이 자주 하는 실수 중 하나는 map 안에서 if를 쓰는 것입니다.cars.map { if(it.position == max) ... } 이렇게 쓰면 결과값은 자동차 리스트가 아니라 Unit이나 null이 포함된 이상한 리스트가 됩니다. 골라낼 땐 무조건 filter를 기억하세요.
3. 최종 처리: forEach
forEach는 리스트를 순회하며 작업(Action) 을 수행합니다. 값을 반환하지 않기 때문에(Unit), 보통 로그를 찍거나 UI를 업데이트할 때 사용합니다.
실전 예제 (결과 출력)
// 각 자동차의 이름과 위치를 출력
cars.forEach { car ->
println("${car.name} : ${"-".repeat(car.position)")
}
for (car in cars)와 기능적으로 똑같습니다. 하지만 map, filter와 함께 체이닝(Chaining) 을 할 때 빛을 발합니다.
// 이름이 5글자 이상인 차만 골라서 -> 대문자로 바꾸고 -> 출력하라
names.filter { it.length >= 5 }
.map { it.uppercase() }
.forEach { println(it) }
이런 식의 파이프라인 코딩은 가독성을 극대화해줍니다.
4. 심화 꿀팁: joinToString
리스트의 내용을 콤마(,)로 연결해서 하나의 문자열로 만들고 싶을 때가 많습니다.
(예: "최종 우승자 : pobi, woni")
이때 for문을 돌리며 마지막 콤마를 제거하는 로직을 짜느라 고생하지 마세요.joinToString이 있습니다.
val winners: List<Car> = ...
// 방법 1: map으로 이름을 뽑고 -> join (조금 비효율적)
val result1 = winners.map { it.name }.joinToString(",")
// 방법 2: joinToString 내부에서 변환 (추천 ⭐)
val result2 = winners.joinToString(",") { car ->
car.name
}
println("최종 우승자 : $result2")
결론
코틀린의 컬렉션 함수는 단순한 문법적 설탕(Syntactic Sugar)이 아닙니다. '어떻게(How) 할 것인가' 에서 '무엇(What)을 할 것인가' 로 사고의 전환을 돕는 도구입니다.
- 변환할 땐
map - 걸러낼 땐
filter - 출력할 땐
forEach - 합칠 땐
joinToString
이 네 가지만 자유자재로 다뤄도 여러분의 코드는 훨씬 강력해질 것입니다.
'Dev Log > Kotlin' 카테고리의 다른 글
| [Kotlin] 클래스 초기화의 비밀: init 블록과 실행 순서 완벽 가이드 (0) | 2026.01.04 |
|---|---|
| [Kotlin] 반복문 리팩토링: for 루프 대신 'repeat'를 써야 하는 이유 (0) | 2026.01.03 |
| [Kotlin] 예외 처리 패턴: 사용자 입력을 '우아하게' 다시 받는 법 (ft. 재귀 함수) (0) | 2026.01.02 |
| [Kotlin/Test] 테스트 코드 작성을 위한 JUnit5와 AssertJ 문법 총정리 (TDD 기초) (0) | 2026.01.02 |
| [Kotlin] 안전한 코드를 위한 최소 요구사항 검증! require, check, assert 총정리 (0) | 2026.01.02 |