외부반복(for)
외부반복이란 Iterator와 같이 사용자가 직접 별도의 객체를 생성하여 명시적으로 컬렉션의 각 요소를 가져와 처리하는 방식을 지칭한다. 일반적으로 우리가 알고 있는 반복은 외부반복이며 전통적인 반복 방식으로 사용자가 직접 컬렉션의 각 요소를 반복, 순회하며 처리하고 반복문의 시작과 끝을 명시적으로 지정할 수 있다.
이 방식에서 반복의 제어는 사용자의 코드에 의해 명시적으로 관리된다. 반복 로직과 실제 비즈니스 로직이 혼재되어 코드의 가독성과 유지보수성이 감소할 수 있다. 구문의 간결성 측면에서 반복 제어 로직이 사용자의 코드에 명시적으로 포함되어 있어 명시적으로 콜백 함수를 호출해야 하기 때문에 비교적 구문이 더 길어지고 복잡하다.
병렬처리 수행 시에도 동기화에 대한 관리를 직접 해주어야 하기 때문에 오류가 발생하기 쉽다.
부정적인 예를 많이 들긴 했지만 짝수번 index만 처리하는 것과 같은 간결한 조건부 반복 시 유용하게 사용할 수 있다.
dart에서 for문은 외부반복을 수행하는 반복구문이다. 반복문 실행 시 변수 i를 정의한 뒤 i의 값을 하나씩 증가시키면서 list의 i번째 항목을 조회하는 방식으로 진행한다.
void main() {
List<String> colors = ['red', 'orange', 'yellow', 'green', 'blue'];
// for loop
for(int i=0; i<colors.length; i++){
print(colors[i]);
}
}
물론 dart에선 이렇게도 사용할 수는 있다.
void main() {
List<String> colors = ['red', 'orange', 'yellow', 'green', 'blue'];
// for loop
for(String item in colors){
print(item);
}
}
내부반복(forEach)
내부반복은 개발자가 직접 반복을 제어하지 않고 컬렉션 내부적으로 반복을 처리할 수 있다. 일반적으로 내부반복은 반복을 추상화하고 반복되는 요소의 처리를 라이브러리에 위임한다. 그러므로 사용자는 각 요소에 대한 처리 로직(비즈니스 로직)에만 집중할 수 있으며 내부 함수를 호출하는 형태로 사용하므로 반복문의 구문이 비교적 간결해져 코드의 가독성과 유지보수성을 개선할 수 있다. filter, map, reduce와 같은 함수형 프로그래밍에 자주 사용되는 함수들도 내부반복에 해당한다.
forEach문은 list의 내부 메소드로써 실행 시 list의 각 요소(elements)들을 순차적으로 접근하여 하나씩 반환받는다. 그러므로 forEach는 내부반복이라 볼 수 있다.
void main() {
List<String> colors = ['red', 'orange', 'yellow', 'green', 'blue'];
print("forEach loop");
// forEach loop
colors.forEach((e) {
print(e);
});
}
결론
실제로 내부반복과 외부반복의 성능 차이는 어떤 언어나 환경에서도 일관되게 보장되지는 않는다. 그러므로 내부반복이나 외부반복 중 현재 개발하는 부분에 가독성과 목적에 맞게 선택하는 것이 중요하다. 다만 코드 가독성을 높이고 루프 내에서 반복변수를 수정해야 하는 경우에는 forEach loop를 사용하는 것이 조금 더 적절하며, 배열의 각 요소에 대해 단순한 반복 작업을 수행하며 루프 내에서 반복변수를 수정하지 않아도 되는 경우에 for loop를 사용하는 것이 좋다.