
Iterator 패턴은 디자인 패턴 책에서 보면 꽤 전통적인 주제처럼 느껴집니다. 그런데 코틀린에서는 이 패턴이 낡은 이야기가 아니라, Iterable, iterator(), for 문, Sequence를 이해하는 데 바로 연결되는 살아 있는 감각입니다.
이번 글에서는 Iterator 패턴을 정의 암기로 설명하지 않고, 컬렉션 내부 구조를 감춘 채 같은 방식으로 순회하고 싶은 문제에서 출발해 코틀린 표준 라이브러리와 Sequence까지 연결해보겠습니다.

공식 기준은 Kotlin iterators 문서와 Kotlin sequences 문서를 참고했습니다. 이번 글은 그 내용을 패턴 관점과 코틀린 실전 감각으로 다시 풀어 설명합니다.
Iterator 패턴은 왜 필요할까
컬렉션을 순회할 때 우리가 정말 원하는 것은 내부 자료구조의 구현 세부가 아닙니다. List가 배열 기반인지, 연결 구조인지, 트리인지보다 중요한 것은 원소를 하나씩 꺼내 같은 방식으로 다룰 수 있는가입니다.
Iterator 패턴은 이 지점을 추상화합니다. 사용자는 순회 대상의 내부 구조를 몰라도 되고, 공통된 순회 인터페이스만 알면 됩니다. 즉 패턴의 핵심은 “어떻게 저장돼 있는가”보다 “어떻게 방문할 수 있는가”를 분리하는 데 있습니다.
코틀린에서는 이 패턴이 어디에 보일까
코틀린에서 가장 익숙한 장면은 Iterable과 iterator()입니다. 표면적으로는 그냥 for 문을 쓰는 것 같지만, 그 뒤에는 순회자를 꺼내는 약속이 숨어 있습니다.
val numbers = listOf(1, 2, 3)
val iterator = numbers.iterator()
while (iterator.hasNext()) {
println(iterator.next())
}이 코드는 전형적인 Iterator 패턴의 장면입니다. numbers가 내부에서 배열을 쓰든 다른 구조를 쓰든, 사용하는 쪽은 iterator.hasNext()와 iterator.next()만 알면 됩니다.

for 문은 왜 Iterator 패턴과 연결될까
코틀린 공식 문서에도 나오듯이, Iterable 컬렉션에 대해 for 문을 쓰면 iterator를 암묵적으로 얻습니다. 즉 우리는 편한 문법을 쓰고 있지만, 실제 개념은 여전히 순회자 기반입니다.
val numbers = listOf("one", "two", "three")
for (item in numbers) {
println(item)
}이 문법이 중요한 이유는, Iterator 패턴이 책 속의 낡은 추상화가 아니라 코틀린의 기본 순회 문법에 이미 녹아 있다는 점을 보여주기 때문입니다. 즉 Iterator 패턴을 이해하면 for 문의 배경도 더 잘 보입니다.
Sequence는 왜 여기서 같이 나올까
여기서 많은 독자가 헷갈립니다. Iterator 패턴과 Sequence는 같은 것일까요? 완전히 같지는 않습니다. Iterator 패턴은 순회 인터페이스를 통일하는 문제에 더 가깝고, Sequence는 그 순회를 lazy 처리와 연결하는 방식에 더 가깝습니다.
즉 Iterator 패턴이 “원소를 하나씩 방문한다”는 공통 통로를 만든다면, Sequence는 “그 방문을 언제 실제로 계산할 것인가”를 더 세밀하게 다루는 쪽입니다.

Iterable과 Sequence는 무엇이 다를까
여기서 중요한 것은 eager와 lazy를 성능 신화처럼 외우지 않는 것입니다. eager는 각 단계를 바로 수행하면서 중간 결과를 만들어 가는 쪽이고, lazy는 정말 필요해질 때까지 계산을 미루는 쪽입니다. 둘 중 무엇이 더 좋은지는 데이터 크기와 처리 체인 길이에 따라 달라집니다.


Kotlin 공식 문서 기준으로 일반 Iterable 체인은 보통 eager하게 처리됩니다. 즉 map, filter 같은 단계가 순서대로 실행되면서 중간 결과 컬렉션이 생길 수 있습니다. 반면 Sequence는 가능한 한 lazy하게 흘러갑니다.
val result = listOf(1, 2, 3, 4, 5)
.asSequence()
.filter { it % 2 == 1 }
.map { it * 2 }
.toList()이 코드는 결과가 실제로 필요해질 때까지 계산을 미루고, 원소 하나씩 filter와 map을 통과시키는 흐름으로 볼 수 있습니다. 그래서 긴 처리 체인이나 큰 데이터에서는 유리할 수 있습니다.

다만 여기서 Sequence가 항상 더 빠르다고 단정하면 안 됩니다. 공식 문서도 lazy 처리에는 오버헤드가 있을 수 있다고 말합니다. 작은 컬렉션이나 단순 연산에서는 일반 컬렉션 체인이 더 단순하고 충분할 수 있습니다.
ListIterator와 MutableIterator를 보면 더 분명해진다
코틀린 공식 문서 기준으로 리스트에는 ListIterator가 있고, mutable 컬렉션에는 MutableIterator가 있습니다. 이 점은 Iterator 패턴이 단순히 한 종류의 순회자만 있다는 뜻이 아니라, 기본 순회 인터페이스 위에 더 구체적인 책임이 확장될 수 있음을 보여줍니다.
ListIterator는 양방향 순회처럼 리스트에 특화된 기능을 보여주고, MutableIterator는 순회 중 변경이라는 더 조심스러운 책임을 드러냅니다. 즉 Iterator 패턴은 단순한 next 호출을 넘어서, 순회 책임을 어떤 수준까지 열어둘지 설계하는 문제와도 닿아 있습니다.

실전에서는 어떻게 판단하면 좋을까
- 순회 추상화 자체를 이해하고 싶다면 Iterator 패턴부터 본다
- for 문, iterator(), Iterable 관계를 이해하면 코틀린 기본 순회가 훨씬 선명해진다
- 여러 연산을 이어 붙일 때 중간 컬렉션 생성을 줄이고 싶다면 Sequence를 검토한다
- 하지만 작은 데이터나 단순 처리에서는 Sequence를 과하게 쓰지 않아도 된다
예를 들어 단순한 리스트 한 번 순회는 Sequence까지 갈 필요가 없는 경우가 많습니다. 반면 필터, 매핑, take 같은 연산이 길게 이어지고 데이터가 커질수록 Sequence의 장점이 더 잘 보일 수 있습니다.
코틀린다운 포인트는 무엇일까
전통적인 디자인 패턴 책에서는 Iterator를 별도 참여 객체와 UML로 설명하는 경우가 많습니다. 하지만 코틀린에서는 패턴의 핵심 아이디어가 언어와 표준 라이브러리 안에 자연스럽게 녹아 있어, 훨씬 덜 무겁게 만날 수 있습니다.
이 점이 바로 시리즈 전체에서 반복해서 보게 되는 코틀린다운 포인트입니다. 패턴이 사라진 것이 아니라, 더 언어 친화적인 형태로 흡수된 것입니다. Iterator도 그 대표적인 예입니다.
이런 관점은 앞선 Command 패턴 글에서 코틀린이 실행 단위 구조를 어떻게 더 가볍게 표현하는지 본 흐름과도 이어집니다. 또 직전 15편 Interpreter 패턴 글처럼, 언어와 라이브러리가 전통 패턴의 무게를 어떻게 줄여 주는지 비교해 보면 시리즈 흐름이 더 잘 보입니다.
마무리
Iterator 패턴은 컬렉션 내부 구조를 숨긴 채 공통된 방식으로 순회하고 싶다는 오래된 문제를 해결합니다. 코틀린에서는 이 아이디어가 Iterable, iterator(), for 문 안에 이미 자연스럽게 스며들어 있습니다.
그리고 Sequence는 그 순회 감각을 lazy 처리까지 확장해서, 여러 연산을 더 유연하게 이어 가게 해 줍니다. 즉 Iterator 패턴은 순회 추상화의 뼈대이고, Sequence는 그 뼈대 위에 올라간 코틀린다운 처리 전략으로 이해하면 훨씬 덜 헷갈립니다.
코틀린 디자인 패턴 시리즈
코틀린 디자인 패턴 시리즈(0) – 왜 아직도 디자인 패턴을 배워야 할까
코틀린 디자인 패턴(1) – Singleton 패턴은 언제 쓰고 object는 어떻게 다를까
코틀린 디자인 패턴(2) – Factory Method 패턴으로 생성 책임 나누기
코틀린 디자인 패턴(3) – Abstract Factory 패턴은 언제 필요할까
코틀린 디자인 패턴(4) – Builder 패턴과 named argument는 어떻게 다를까
코틀린 디자인 패턴(5) – Prototype 패턴과 data class copy 정리
코틀린 디자인 패턴(6) – Adapter 패턴으로 기존 코드를 새 인터페이스에 맞추기
코틀린 디자인 패턴(7) – Bridge 패턴은 상속 폭발을 어떻게 줄일까
코틀린 디자인 패턴(8) – Composite 패턴으로 트리 구조 다루기
코틀린 디자인 패턴(9) – Decorator 패턴은 상속 대신 어떻게 확장할까
코틀린 디자인 패턴(10) – Facade 패턴으로 복잡한 시스템 감추기
코틀린 디자인 패턴(11) – Flyweight 패턴은 메모리를 어떻게 아낄까
코틀린 디자인 패턴(12) – Proxy 패턴은 Decorator와 무엇이 다를까
코틀린 디자인 패턴(13) – Chain of Responsibility 패턴으로 조건 분기 줄이기