Skip to content
This page has been auto-translated and may contain errors.View in English

반복문

docs.scrimba.com

1부터 100까지의 숫자를 출력하거나, 단어의 모든 문자를 인사하거나, 정답이 나올 때까지 계속 질문해야 한다고 합시다. 같은 줄을 백 번 쓸 수도 있지만, 입력하기는 지루하고 변경하기는 더 복잡합니다. 반복문은 JavaScript에 같은 코드를 다시 또 다시 실행하도록 명령하는 방법이므로, 한 번만 작성하고 언어가 반복하도록 할 수 있습니다. 이 장에서는 가장 많이 사용하는 세 가지 반복문을 다룹니다: for, while, for...of.

for 반복문

**for 반복문**은 반복 횟수를 알고 있을 때 사용합니다. 자동으로 카운트합니다. 0부터 4까지의 숫자를 출력하는 예입니다:

js
for (let i = 0; i < 5; i++) {
  console.log(i);
}
// 0
// 1
// 2
// 3
// 4

괄호 안의 부분은 세미콜론으로 구분된 세 가지 요소로 이루어져 있습니다:

  • let i = 0시작입니다: i라는 카운터를 만들고 0으로 설정합니다.
  • i < 5조건입니다: 이것이 참이면 반복문이 계속됩니다.
  • i++스텝입니다: 각 반복 후에 i에 1을 더합니다 (i++i = i + 1의 축약형입니다).

따라서 반복문은 i를 0에서 시작하고, i < 5인지 확인하고, 중괄호 안의 코드를 실행한 다음, 1을 더하고 다시 확인합니다. i가 5에 도달하면 조건이 거짓이 되고 반복문이 멈춥니다.

**for 반복문**은 반복 횟수를 미리 알고 있을 때 사용하기 위해 만들어졌습니다. 세 가지 요소를 헤더에 순서대로 포함합니다:

js
for (let i = 0; i < 5; i++) {
  console.log(`pass number ${i}`);
}
// pass number 0
// pass number 1
// pass number 2
// pass number 3
// pass number 4

헤더를 왼쪽에서 오른쪽으로 읽으면: let i = 0초기화로, 다른 무엇보다 먼저 한 번 실행됩니다. i < 5조건으로, 매 반복 전에 확인되며, 조건이 참일 때만 반복문이 실행됩니다. i++스텝으로, 각 반복 후에 실행됩니다. 카운터 i는 일반 변수이므로 반복문 내에서 읽을 수 있으며, 이것이 반복문과 카운팅이 함께 가는 이유입니다.

관례상 카운터는 0에서 시작합니다. 규칙은 아니지만, 0부터 카운팅하는 것은 JavaScript 거의 모든 곳에서 볼 수 있으며, 배열이 항목을 번호 매기는 방식과 일치하므로 처음부터 익숙해지기 좋습니다.

**for 반복문**은 하나의 헤더에 카운트된 반복의 전체 모양을 인코딩합니다: 초기화, 조건, 스텝이 각각 세미콜론으로 구분됩니다.

js
for (let i = 0; i < 5; i++) {
  console.log(`pass number ${i}`);
}
// pass number 0
// pass number 1
// pass number 2
// pass number 3
// pass number 4

순서는 정확하며 기억할 가치가 있습니다: 초기화가 한 번 실행되고, 조건이 테스트되며, 참일 때만 본문이 실행되고, 스텝이 실행된 후 다시 조건으로 돌아갑니다. 이 순서 때문에 조건은 첫 번째 반복 전에 확인되므로, 조건이 처음부터 거짓인 반복문은 본문을 실행하지 않습니다.

카운터에 const가 아닌 let을 사용하세요. 스텝이 매 반복마다 카운터를 다시 할당하기 때문입니다. 그리고 헤더에서 i를 선언하면 반복문에만 범위가 지정되므로 주변 코드로 유출되지 않으며, 이는 반복문을 중첩할 때나 연속으로 여러 개를 쓸 때 카운터 충돌을 방지합니다. 1 대신 0부터 카운팅하는 것이 거의 보편적인 관례이며, 배열이 요소를 인덱싱하는 방식과 직접 매핑되므로 이 습관은 데이터를 반복할 때 즉시 효과를 봅니다.

Junofor 반복문for 반복문은 정해진 횟수만큼 반복하고 자동으로 카운트합니다. 헤더는 세 부분으로 이루어져 있습니다: let i = 0 같은 시작, i < 5 같은 조건으로 반복을 계속 유지, i++ 같은 스텝으로 진행시킵니다. 조건이 거짓이 되면 반복문이 멈춥니다.
Junofor 반복문 횟수를 미리 알고 있을 때 for를 사용하세요. 헤더는 초기화를 한 번 실행하고, 매 반복 전에 조건을 확인하고, 각 반복 후에 스텝을 실행합니다. 0부터 카운팅하는 것이 관례이며, 배열이 항목을 번호 매기는 방식과 일치하므로 지금부터 이 습관을 들이세요.
Junofor 반복문 헤더 순서는 고정입니다: 한 번 초기화, 조건 테스트, 본문 실행, 스텝 실행, 반복. 이것이 처음부터 조건이 거짓이면 본문을 완전히 건너뛰는 이유입니다. 스텝이 카운터를 다시 할당할 수 있도록 let을 사용하고, 헤더에서 선언하여 범위가 반복문에만 유지되도록 하세요.

while 반복문

때로는 반복 횟수를 알 수 없고, 참으로 유지되어야 하는 조건만 알 수 있습니다. 이것이 **while 반복문**의 용도입니다. 조건이 유지되는 동안 계속 실행됩니다:

js
let count = 3;
while (count > 0) {
  console.log(count);
  count = count - 1;
}
console.log("Go");
// 3
// 2
// 1
// Go

반복문은 count > 0를 확인하고, 코드를 실행한 다음 다시 확인합니다. 반복문 내부의 무언가가 count를 변경하므로 조건이 결국 거짓이 되고 반복문이 멈춥니다. 내부의 아무것도 조건을 거짓으로 만들지 않으면, 반복문은 계속 실행됩니다. 이것이 무한 루프이며 프로그램을 멈추게 하므로, 항상 조건이 변할 수 있도록 확인하세요.

**while 반복문**은 조건이 참으로 유지되는 동안 반복하며, 내장된 카운터나 스텝이 없습니다. 이것이 반복 횟수를 미리 알 수 없을 때 올바른 도구이고, 반복 횟수가 정해져 있을 때는 for 반복문이 올바른 도구입니다.

js
let attempts = 0;
let password = "";
while (password !== "letmein") {
  password = "letmein"; // 실제 프롬프트를 대신하는 코드
  attempts = attempts + 1;
}
console.log(`Unlocked after ${attempts} attempt(s)`);
// Unlocked after 1 attempt(s)

조건 (password !== "letmein")은 조건문의 비교 연산자에 의존하며, 거짓이 될 때까지 반복합니다. 문제는 거짓이 되도록 강제하는 것이 없다는 것입니다: 조건이 의존하는 반복문 내부의 무언가를 변경해야 합니다. 조건이 절대 거짓이 되지 않는 while 반복문은 영원히 실행됩니다. 이것이 무한 루프이며 프로그램을 중단시킵니다.

**while 반복문**은 일반적인 형태입니다: 조건이 참인 동안 반복하고, 카운팅이 있다면 그것을 완전히 당신에게 맡깁니다. 반복 횟수가 고정되지 않은 경우, 예를 들어 입력이 유효성을 검사할 때까지 반복하거나 값이 임계값을 초과할 때까지 반복할 때 사용하세요. 횟수가 정해져 있으면, for 반복문이 그 의도를 더 명확하게 나타냅니다.

js
let remaining = 100;
let steps = 0;
while (remaining > 0) {
  remaining = remaining - 15;
  steps = steps + 1;
}
console.log(`Emptied in ${steps} steps`);
// Emptied in 7 steps

존중해야 할 실패 모드는 무한 루프입니다: while은 종료 보장이 없으므로 본문이 조건을 거짓으로 이동해야 합니다. 반복문을 중단시키는 세 가지: 조건이 절대 업데이트되지 않는 경우, 업데이트가 잘못된 방향으로 움직이는 경우, 조건이 충족되지 않고 스킵되는 경우 (위에서 remaining === 0을 사용하면 미스가 됩니다. remaining이 정확히 0이 아닌 음수에 도달하기 때문입니다). 모든 while 반복문은 조건을 거짓으로 이동시키는 줄이 필요합니다. === 0 같은 정확한 조건보다 > 0 같은 범위 테스트를 선호하여 오버슈트한 스텝도 반복문을 종료할 수 있도록 하세요.

Junowhile 반복문while 반복문은 조건이 참인 동안 반복하며, 내장된 카운터가 없습니다. 반복 횟수를 미리 알 수 없을 때 사용하세요. 항상 반복문 내부의 무언가를 변경하여 조건이 거짓이 될 수 있도록 하세요. 그렇지 않으면 프로그램을 멈추게 하는 무한 루프가 됩니다.
Junowhile 반복문 반복 횟수를 알 수 없으면 while을, 횟수가 정해져 있으면 for를 선택하세요. 본문은 조건이 테스트하는 무언가를 변경해야 하고, 그렇지 않으면 거짓이 되지 않습니다. 이것이 무한 루프이며 while을 쓸 때마다 주의해야 할 실수입니다.
Junowhile 반복문while은 종료 보장이 없으므로 본문이 조건을 거짓으로 이동해야 합니다. 중단되는 세 가지 경우: 조건이 절대 업데이트되지 않음, 잘못된 방향으로 이동, 스킵됨. === 0 같은 정확한 조건보다 &gt; 0 같은 범위 테스트를 선호하여 오버슈트한 스텝도 반복문을 종료하도록 하세요.

for...of로 문자열 반복하기

forwhile 반복문은 숫자로 카운트하지만, 종종 무언가의 각 항목을 직접 방문하고 싶습니다. **for...of**가 바로 그 역할입니다. 여기서 단어의 각 문자를 순회합니다:

js
for (const char of "hello") {
  console.log(char);
}
// h
// e
// l
// l
// o

"단어의 각 문자에 대해"로 읽으세요. 각 반복에서 char는 다음 문자를 가지며, 카운터나 조건에 영향을 주지 않습니다. 단순히 각 항목을 순서대로 보고 싶을 때 for 반복문보다 깔끔합니다.

주로 for...of배열에서 사용할 것입니다. 배열은 다음 장들에서 만나는 값의 목록입니다. 문자열은 이미 문자의 시퀀스이고 for...of가 정확히 이렇게 취급하기 때문에 반복하는 좋은 첫 번째 예입니다.

**for...of**는 시퀀스의 항목을 직접 반복하여 카운터나 조건을 관리할 필요 없이 각 값을 차례로 제공합니다:

js
let vowels = 0;
for (const char of "javascript") {
  if (char === "a" || char === "i") {
    vowels = vowels + 1;
  }
}
console.log(`Found ${vowels} of those vowels`);
// Found 3 of those vowels

여기서 const char를 사용하는 것은 의도적입니다: 각 반복이 새로운 char을 만들고 읽기만 하고 다시 할당하지 않으므로 const가 맞고 그 의도를 나타냅니다. for...of 반복문은 카운터 반복문의 수동 관리를 가독성으로 교환하며, 수동으로 인덱싱하지 않으므로 off-by-one 오류의 전체 범주를 제거합니다.

문자열은 반복하기 좋은 첫 번째 시퀀스이지만, 실제 주력은 배열입니다. 배열을 만난 후, for...of는 내용을 깔끔하고 직접적으로 순회하는 방법입니다.

**for...of**는 인덱스를 노출하지 않고 각 값을 이름에 바인딩하여 모든 시퀀스를 요소별로 반복합니다. 문자열에서는 문자를 산출합니다:

js
let shout = "";
for (const char of "loops") {
  shout = shout + char.toUpperCase();
}
console.log(shout);
// LOOPS

const로 반복 변수를 바인딩하세요: for...of는 각 반복마다 새로운 바인딩을 만들므로 다시 할당할 필요가 없으며, const는 본문 내에서 값이 읽기 전용임을 문서화합니다. 카운터 반복문보다의 이점은 절대 인덱스를 쓰지 않는다는 것인데, 이는 구조상 off-by-one 오류를 방지합니다.

이것이 가장 중요한 곳은 배열입니다. 각 요소를 방문할 때, for...of는 카운팅 for 반복문보다 읽기 쉽고 의도를 직접 나타냅니다. 하지만 컬렉션을 새로운 것으로 변환할 때, 배열 반복 메서드가 보통 수동 반복문을 완전히 대체하며, 이는 자체 장에서 다룹니다.

break와 continue

두 키워드는 모든 반복문 내부에서 더 세밀한 제어를 제공합니다. break는 반복문을 즉시 멈추고 그 다음 코드로 이동합니다. continue는 현재 반복의 나머지를 건너뛰고 다음 반복으로 바로 점프합니다.

js
for (let i = 0; i < 10; i++) {
  if (i === 3) {
    continue; // 3을 건너뛰고, 계속 반복
  }
  if (i === 6) {
    break; // 반복문을 완전히 멈춤
  }
  console.log(i);
}
// 0
// 1
// 2
// 4
// 5

break는 필요한 것을 찾으면 일찍 멈추므로 계속 무의미하게 스캔하지 않는 방법입니다. continue는 신경 쓰지 않는 경우를 건너뛰면서 본문의 나머지를 if 내부에 중첩시키지 않는 방법입니다.

Off-by-one 오류와 인덱스 범위

고전적인 반복문 버그는 off-by-one 오류입니다: 한 번 너무 많거나 한 번 너무 적게 실행되는 것입니다. 카운터의 범위에서 비롯됩니다. 길이 5인 문자열은 위치 0, 1, 2, 3, 4에 문자가 있으므로, 마지막 유효한 위치는 length가 아니라 length - 1입니다. 이것이 위치를 카운팅하는 반복문이 i <= text.length가 아니라 i < text.length를 사용하는 이유입니다:

js
const text = "hello";
for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// h
// e
// l
// l
// o

i <= text.length를 쓰면 마지막 반복이 위치 5에 도달하는데, 이는 존재하지 않으므로 undefined가 됩니다. 0에서부터 길이까지 <로 카운팅하는 것이 정확히 모든 위치에 도달하고 그 이상은 가지 않는 패턴입니다. 위치가 필요하지 않고 각 항목만 필요할 때, for...of는 범위 질문을 완전히 피합니다. 이것이 이를 선호하는 이유입니다.

Junofor...of로 문자열 반복하기for...of는 각 항목을 직접 방문하며, 카운터나 조건을 관리할 필요가 없습니다. 문자열에서는 각 반복이 다음 문자를 제공합니다. 나중에 배열에서 가장 많이 사용하겠지만, 문자열은 이미 문자의 시퀀스이기 때문에 반복하기 좋은 첫 번째 것입니다.
Junofor...of로 문자열 반복하기for...of는 각 값을 차례로 제공하므로 카운터와 수동으로 인덱싱하면서 비롯되는 off-by-one 위험을 건너뜁니다. const로 바인딩하세요. 각 반복이 읽기 전용이기 때문입니다. 문자열은 좋은 시작이지만, 배열을 만난 후부터 진정으로 활용하게 됩니다.
Junofor...of로 문자열 반복하기for...of는 각 요소를 새로운 const에 바인딩하고 절대 인덱스를 노출하지 않으므로 구조상 off-by-one 버그를 제거합니다. break는 일찍 멈추고, continue는 반복을 건너뜁니다. 컬렉션을 단순히 순회하는 것이 아니라 변환할 때는, 배열 반복 메서드가 보통 수동 반복문을 대체하며, 이는 자체 장에서 다룹니다.

반복문 이후 배워야 할 것

반복문은 자신을 반복하는 것을 멈추고 언어가 반복하도록 하는 방법입니다. for 반복문은 정해진 횟수를 카운트하고, while은 조건이 유지되는 동안 반복하고, for...of는 시퀀스의 항목을 직접 순회합니다. 이 장에서 가져가야 할 한 가지 습관: 모든 반복문이 끝날 수 있도록 확인하고, 각 항목만 필요할 때 for...of를 선호하세요.

반복문이 중요한 실제 이유는 작업하는 데이터 대부분이 컬렉션으로 오기 때문입니다. 다음 장들은 배열을 소개하며, 이는 계속 반복할 목록이고, 나중에 배열 메서드를 다루면 데이터를 변환할 때 수동으로 작성한 반복문을 종종 대체합니다. 반복문이 의존하는 조건을 다듬고 싶다면, 조건문에서 반복문을 계속 실행할지 멈출지를 결정하는 비교를 다룹니다.