배열로 저장된 데이터를 리스트(List)나 세트(Set)로 변경했을 때, 데이터를 출력하는 모든 for 루프 코드를 수정해야 했던 경험이 있으신가요? 데이터 저장 방식이 바뀌더라도 출력하는 코드는 그대로 유지할 수 있다면 얼마나 편할까요?
오늘은 컬렉션의 구현 방법을 노출하지 않으면서도 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 이터레이터 패턴(Iterator Pattern)에 대해 알아보겠습니다.

1. 이터레이터 패턴이란?
이터레이터 패턴은 컬렉션(집합체)의 내부 구조를 노출하지 않고 요소들을 순차적으로 탐색할 수 있는 인터페이스를 제공하는 디자인 패턴입니다.
이 패턴을 사용하면 내부가 배열로 되어 있든, 트리 구조로 되어 있든 상관없이 클라이언트는 오직 hasNext()와 next()라는 동일한 방법으로 요소에 접근할 수 있습니다.
2. 왜 이터레이터 패턴을 써야 할까? (장점)
- 집합체와 알고리즘의 분리: 집합체의 내부 구조(배열, 리스트 등)가 변경되어도 순회하는 코드는 영향을 받지 않습니다.
- 단일 책임 원칙(SRP): 집합체 클래스는 데이터를 관리하는 일에만 집중하고, 순회 로직은 이터레이터가 담당합니다.
- 일관된 인터페이스: 어떤 컬렉션이라도 동일한 인터페이스로 순회할 수 있어 코드의 가독성이 높아집니다.
3. 이터레이터 패턴의 구조
패턴을 구성하는 4가지 핵심 요소는 다음과 같습니다.
- Iterator (인터페이스): 요소를 탐색하기 위한 인터페이스 (hasNext, next)
- ConcreteIterator: 인터페이스를 구현하며 탐색 중인 현재 위치를 추적합니다.
- Aggregate (인터페이스): 이터레이터 객체를 생성하는 메서드를 정의합니다.
- ConcreteAggregate: 실제 데이터 리스트를 보유하며, 그에 맞는 이터레이터를 생성하여 반환합니다.
4. Java 실무 예제: 커스텀 컬렉션 순회하기
자바의 표준 Iterator 인터페이스를 활용하여 간단한 도서 목록 시스템을 구현해 보겠습니다.
Step 1. 데이터 객체와 집합체 정의
import java.util.Iterator;
import java.util.NoSuchElementException;
// 데이터 객체
class Book {
private String title;
public Book(String title) { this.title = title; }
public String getTitle() { return title; }
}
// ConcreteAggregate: 도서 목록 컬렉션
class BookShelf implements Iterable<Book> {
private Book[] books;
private int last = 0;
public BookShelf(int size) { this.books = new Book[size]; }
public void addBook(Book book) {
if (last < books.length) { books[last++] = book; }
}
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator();
}
// ConcreteIterator: 내부 클래스로 구현하여 컬렉션 데이터에 접근
private class BookShelfIterator implements Iterator<Book> {
private int index = 0;
@Override
public boolean hasNext() {
return index < last;
}
@Override
public Book next() {
if (!hasNext()) throw new NoSuchElementException();
return books[index++];
}
}
}
Step 2. 실행 및 테스트
public class IteratorDemo {
public static void main(String[] args) {
BookShelf shelf = new BookShelf(4);
shelf.addBook(new Book("Clean Code"));
shelf.addBook(new Book("Design Patterns"));
shelf.addBook(new Book("Java Optimization"));
// 이터레이터를 통한 일관된 순회
Iterator<Book> it = shelf.iterator();
while (it.hasNext()) {
System.out.println("도서명: " + it.next().getTitle());
}
// Iterable을 구현했으므로 for-each 구문도 사용 가능!
System.out.println("\n-- for-each 구문 사용 --");
for (Book book : shelf) {
System.out.println(book.getTitle());
}
}
}
5. 실생활 비유: TV 채널 돌리기
우리가 TV 내부 회로가 어떻게 구성되어 있는지 몰라도 리모컨의 '다음' 버튼(next)만 누르면 채널을 순차적으로 넘길 수 있는 것과 같습니다. 리모컨이 바로 TV 채널이라는 집합체에 대한 이터레이터 역할을 하는 셈이죠.
결론
이터레이터 패턴은 "컬렉션의 구현을 숨기면서 요소를 꺼내는 방법을 표준화"하는 강력한 도구입니다. 자바 개발자라면 Iterable 인터페이스와 함께 이 패턴을 이해함으로써 더 유연하고 객체지향적인 코드를 작성할 수 있습니다.
도움이 되셨다면 공감과 구독 부탁드립니다! 디자인 패턴에 대해 더 궁금한 점이 있다면 댓글로 남겨주세요.
'Mobile & App Stack > Java Software Architecture & Patterns' 카테고리의 다른 글
| 고전 GoF를 넘어선 최신 디자인 패턴 트렌드: MSA부터 리액티브까지 (0) | 2025.01.08 |
|---|---|
| 실무에서 바로 써먹는 디자인 패턴 활용 사례 및 오남용 방지 가이드 (0) | 2025.01.07 |
| 비지터 패턴(Visitor): 기존 코드 수정 없이 기능을 무한 확장하는 법 (0) | 2025.01.05 |
| 상태 패턴(State Pattern): if-else 조건문 지옥에서 탈출하는 법 (0) | 2025.01.04 |
| 템플릿 메서드 패턴: 중복 코드를 줄이는 상속의 기술 (Java 예제) (0) | 2025.01.03 |