3.2 반복문
반복문이란?
반복문은 동일하거나 유사한 작업을 여러 번 실행할 때 사용합니다. JavaScript는 다양한 반복문을 제공합니다.
for 문
가장 기본적인 반복문으로, 초기화·조건·증감을 한 줄에 표현합니다.
// 기본 for 문
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
// 역순
for (let i = 4; i >= 0; i--) {
console.log(i); // 4, 3, 2, 1, 0
}
// 배열 순회
const fruits = ["사과", "바나나", "체리"];
for (let i = 0; i < fruits.length; i++) {
console.log(`${i}: ${fruits[i]}`);
}
// 중첩 for: 2차원 배열
const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
for (let row = 0; row < matrix.length; row++) {
for (let col = 0; col < matrix[row].length; col++) {
process.stdout.write(matrix[row][col] + " ");
}
console.log();
}
// 1 2 3
// 4 5 6
// 7 8 9
while 문
조건이 참인 동안 반복합니다.
// 기본 while
let count = 0;
while (count < 5) {
console.log(count);
count++;
}
// 실전: 데이터를 읽을 때까지 반복
let data = null;
let attempts = 0;
while (!data && attempts < 3) {
data = tryFetchData();
attempts++;
}
// 무한 루프 + break
let input = "";
while (true) {
input = getNextInput();
if (input === "quit") break;
processInput(input);
}
do-while 문
먼저 실행한 후 조건을 확인합니다. 최소 1번은 반드시 실행됩니다.
// do-while: 최소 1회 실행
let result;
do {
result = prompt("숫자를 입력하세요 (1-10):");
} while (isNaN(result) || result < 1 || result > 10);
// while과 비교
let i = 10;
// while: 조건이 처음부터 false면 실행 안됨
while (i < 5) {
console.log("while:", i); // 실행 안됨
i++;
}
// do-while: 최소 1회 실행
do {
console.log("do-while:", i); // "do-while: 10" 출력됨
i++;
} while (i < 5);
for...of: 이터러블 순회
ES6에서 추가된 for...of는 배열, 문자열, Map, Set 등 이터러블 객체를 순회합니다.
// 배열
const numbers = [10, 20, 30, 40, 50];
for (const num of numbers) {
console.log(num);
}
// 인덱스가 필요하면 entries() 사용
for (const [index, value] of numbers.entries()) {
console.log(`[${index}] ${value}`);
}
// 문자열
for (const char of "Hello") {
console.log(char); // H, e, l, l, o
}
// Map
const map = new Map([
["이름", "김철수"],
["나이", 25],
["도시", "서울"],
]);
for (const [key, value] of map) {
console.log(`${key}: ${value}`);
}
// Set
const set = new Set([1, 2, 3, 2, 1]); // 중복 제거
for (const item of set) {
console.log(item); // 1, 2, 3
}
// NodeList (DOM)
for (const li of document.querySelectorAll("li")) {
li.style.color = "blue";
}
for...in: 객체 프로퍼티 열거
const user = { name: "김철수", age: 25, city: "서울" };
for (const key in user) {
console.log(`${key}: ${user[key]}`);
}
// name: 김철수
// age: 25
// city: 서울
for...in 주의사항
// 프로토타입 체인의 프로퍼티도 포함됨!
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() { return "Hi!"; };
const p = new Person("철수");
for (const key in p) {
console.log(key); // name, greet (프로토타입 메서드도!)
}
// hasOwnProperty로 필터링
for (const key in p) {
if (Object.prototype.hasOwnProperty.call(p, key)) {
console.log(key); // name만 출력
}
}
// 더 나은 방법: Object.keys, Object.entries 사용
for (const key of Object.keys(p)) {
console.log(key); // name만 출력 (자체 프로퍼티만)
}
forEach vs for...of
const arr = [1, 2, 3, 4, 5];
// forEach: 콜백 함수 스타일
arr.forEach((item, index) => {
console.log(`${index}: ${item}`);
});
// 단점: break, continue, return(외부) 불가
// for...of: 이터레이션 프로토콜 사용
for (const item of arr) {
if (item === 3) break; // OK!
console.log(item);
}
// 비동기 처리: for...of는 await 사용 가능
async function processItems(items) {
for (const item of items) {
await processItem(item); // OK! 순차 처리
}
}
// forEach는 async/await가 제대로 동작하지 않음
async function badAsync(items) {
items.forEach(async (item) => {
await processItem(item); // 모두 동시에 시작됨!
});
// forEach는 Promise를 기다리지 않음
}
break와 continue
// break: 루프 즉시 종료
const numbers = [1, 5, 3, 8, 2, 9, 4];
let target = -1;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] > 7) {
target = numbers[i];
break; // 찾으면 즉시 종료
}
}
console.log(target); // 8
// continue: 현재 반복 건너뜀
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) continue; // 짝수 건너뜀
console.log(i); // 1, 3, 5, 7, 9
}
// 레이블(label): 중첩 루프에서 외부 루프 제어
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
break outer; // 외부 루프 종료
}
console.log(`${i},${j}`);
}
}
// 0,0 / 0,1 / 0,2 / 1,0 출력 후 종료
성능 최적화 팁
// 큰 배열: length를 변수에 저장
const bigArr = new Array(1000000).fill(1);
// 느린 방법: 매 반복마다 length 접근
for (let i = 0; i < bigArr.length; i++) { }
// 빠른 방법: length 캐싱
for (let i = 0, len = bigArr.length; i < len; i++) { }
// 하지만 현대 JS 엔진은 이를 최적화하므로 for...of가 더 가독성 좋음
for (const item of bigArr) { }
// 성능이 정말 중요할 때: while 역순이 가장 빠름
let i = bigArr.length;
while (i--) {
// i: 999999 → 0
}
고수 팁
반복문 패턴 선택 가이드
// 배열 요소 순회 → for...of 또는 forEach
for (const item of array) { }
array.forEach(item => { });
// 배열 변환 → map
const doubled = array.map(x => x * 2);
// 배열 필터 → filter
const evens = array.filter(x => x % 2 === 0);
// 객체 프로퍼티 → for...of + Object.entries
for (const [key, value] of Object.entries(obj)) { }
// 비동기 순차 처리 → for...of + await
for (const item of items) {
await process(item);
}
// 비동기 병렬 처리 → Promise.all + map
await Promise.all(items.map(item => process(item)));
// 특정 인덱스 접근 필요 → for 또는 entries()
for (let i = 0; i < arr.length; i++) { }
for (const [i, v] of arr.entries()) { }