Home JavaScript의 while과 for 반복문
Post
Cancel

JavaScript의 while과 for 반복문

while과 for 반복문

개발을 하다 보면 여러 동작을 반복해야 하는 경우가 종종 생깁니다.

상품 목록에서 상품을 차례대로 출력하거나 숫자를 1부터 10까지 하나씩 증가시키면서 동일한 코드를 반복 실행해야 하는 경우같이 말이죠.

반복문(loop) 을 사용하면 동일한 코드를 여러 번 반복할 수 있습니다.

while 반복문

while 반복문의 문법은 다음과 같습니다.

1
2
3
4
while (condition) {
  // 코드
  // '반복문 본문(body)'이라 불림
}

condition(조건)이 truthy 이면 반복문 본문의 코드가 실행됩니다.

아래 반복문은 조건 i < 3을 만족할 동안 i를 출력해줍니다.

1
2
3
4
5
let i = 0;
while (i < 3) { // 0, 1, 2가 출력됩니다.
  console.log( i );
  i++;
}

반복문 본문이 한 번 실행되는 것을 반복(iteration, 이터레이션) 이라고 부릅니다. 위 예시에선 반복문이 세 번의 이터레이션을 만듭니다.

i++가 없었다면 이론적으로 반복문이 영원히 반복되었을 겁니다. 그런데 브라우저는 이런 무한 반복을 멈추게 해주는 실질적인 수단을 제공합니다. 서버 사이드 자바스크립트도 이런 수단을 제공해 주므로 무한으로 반복되는 프로세스를 죽일 수 있습니다.

반복문 조건엔 비교뿐만 아니라 모든 종류의 표현식, 변수가 올 수 있습니다. 조건은 while에 의해 평가되고, 평가 후엔 불린값으로 변경됩니다.

아래 예시에선 while (i != 0)을 짧게 줄여 while (i)로 만들어보았습니다.

1
2
3
4
5
let i = 3;
while (i) { // i가 0이 되면 조건이 falsy가 되므로 반복문이 멈춥니다.
  console.log( i );
  i--;
}

본문이 한 줄이면 대괄호를 사용하지 않아도 됩니다. 이는 if제어문과 마찬가지로 반복문 본문이 한 줄짜리 문이라면 대괄호 {...}를 생략할 수 있습니다.

1
2
let i = 3;
while (i) console.log(i--);

do {…} while 반복문

do {…} while 문법을 사용하면 condition을 반복문 본문 아래로 옮길 수 있습니다.

1
2
3
do {
  // 반복문 본문
} while (condition);

이때 본문이 먼저 실행되고, 조건을 확인한 후 조건이 truthy인 동안엔 본문이 계속 실행됩니다.

1
2
3
4
5
let i = 0;
do {
  console.log( i );
  i++;
} while (i < 3);

do{…}while 문법은 조건이 truthy 인지 아닌지에 상관없이, 본문을 최소한 한번이라도 실행하고 싶을 때만 사용해야 합니다. 대다수의 상황에선 do {…}while보다 while(…) {…}이 적합합니다.

for 반복문

for 반복문은 while 반복문보다는 복잡하지만 가장 널리 쓰이는 반복문입니다.

문법은 다음과 같습니다.

1
2
3
for (begin; condition; step) {
  // ... 반복문 본문 ...
}

for문을 구성하는 각 요소가 무엇을 의미하는지 알아봅시다. 아래 반복문을 실행하면 i0부터 3이 될 때까지(단, 3은 포함하지 않음) console.log(i)가 호출됩니다.

1
2
3
for (let i = 0; i < 3; i++) { // 0, 1, 2가 출력됩니다.
  console.log(i);
}

for문의 구성 요소는 다음과 같습니다.

구성 요소  
begini = 0반복문에 진입할 때 단 한 번 실행됩니다.
conditioni < 3반복마다 해당 조건이 확인됩니다. false이면 반복문을 멈춥니다.
bodyconsole.log(i)condition이 truthy일 동안 계속해서 실행됩니다.
stepi++각 반복의 body가 실행된 이후에 실행됩니다.

일반적인 반복문 알고리즘은 다음과 같습니다.

1
2
3
4
5
begin을 실행함
→ (condition이 truthy이면 → body를 실행한 후, step을 실행함)
→ (condition이 truthy이면 → body를 실행한 후, step을 실행함)
→ (condition이 truthy이면 → body를 실행한 후, step을 실행함)
→ ...

begin이 한 차례 실행된 이후에, condition 확인과 body, step이 계속해서 반복 실행됩니다.

반복문을 처음 배우신다면, 위 예시를 실행했을 때 어떤 과정을 거쳐 콘솔 창에 출력되는지 종이에 적어가며 공부해보세요. 이렇게 하면 반복문을 쉽게 이해할 수 있습니다.

정확히 어떤 과정을 거치는지는 아래 예시에서 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
// for (let i = 0; i < 3; i++) console.log(i)

// begin을 실행함
let i = 0
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { console.log(i); i++ }
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { console.log(i); i++ }
// condition이 truthy이면 → body를 실행한 후, step을 실행함
if (i < 3) { console.log(i); i++ }
// i == 3이므로 반복문 종료

인라인 변수 선언 지금까진 ‘카운터’ 변수 i를 반복문 안에서 선언하였습니다. 이런 방식을 ‘인라인’ 변수 선언이라고 부릅니다. 이렇게 선언한 변수는 반복문 안에서만 접근할 수 있습니다.

1
2
3
4
for (let i = 0; i < 3; i++) {
 console.log(i); // 0, 1, 2
}
console.log(i); // Error: i is not defined

인라인 변수 대신, 정의되어있는 변수를 사용할 수도 있습니다.

1
2
3
4
5
6
7
let i = 0;

for (i = 0; i < 3; i++) { // 기존에 정의된 변수 사용
  console.log(i); // 0, 1, 2
}

console.log(i); // 3, 반복문 밖에서 선언한 변수이므로 사용할 수 있음

구성 요소 생략하기

for문의 구성 요소를 생략하는 것도 가능합니다.

반복문이 시작될 때 아무것도 할 필요가 없으면 begin을 생략하는 것이 가능합니다.

1
2
3
4
5
let i = 0; // i를 선언하고 값도 할당하였습니다.

for (; i < 3; i++) { // 'begin'이 필요하지 않기 때문에 생략하였습니다.
  console.log( i ); // 0, 1, 2
}

step 역시 생략할 수 있습니다.

1
2
3
4
5
let i = 0;

for (; i < 3;) {
  console.log( i++ );
}

위와 같이 for문을 구성하면 while (i < 3)과 동일해집니다.

모든 구성 요소를 생략할 수도 있는데, 이렇게 되면 무한 반복문이 만들어집니다.

1
2
3
for (;;) {
  // 끊임 없이 본문이 실행됩니다.
}

for문의 구성요소를 생략할 때 주의할 점은 두 개의 ; 세미콜론을 꼭 넣어주어야 한다는 점입니다. 하나라도 없으면 문법 에러가 발생합니다.

반복문 빠져나오기

대개는 반복문의 조건이 falsy가 되면 반복문이 종료됩니다.

그런데 특별한 지시자인 break를 사용하면 언제든 원하는 때에 반복문을 빠져나올 수 있습니다.

아래 예시의 반복문은 value에 값이 할당되면 실행되고, 아무런 값도 할당되지 않으면 반복문을 ‘종료’합니다.

1
2
3
4
5
6
7
8
9
10
11
12
let sum = 0;

while (true) {

  let value = 1000;

  if (!value) break; // (*)

  sum += value;

}
console.log( '합계: ' + sum );

다음 반복으로 넘어가기

continue 지시자는 break의 ‘가벼운 버전’입니다. continue는 전체 반복문을 멈추지 않습니다. 대신에 현재 실행 중인 이터레이션을 멈추고 반복문이 다음 이터레이션을 강제로 실행시키도록 합니다(조건을 통과할 때).

continue는 현재 반복을 종료시키고 다음 반복으로 넘어가고 싶을 때 사용할 수 있습니다.

아래 반복문은 continue를 사용해 홀수만 출력합니다.

1
2
3
4
5
6
7
for (let i = 0; i < 10; i++) {

  // 조건이 참이라면 남아있는 본문은 실행되지 않습니다.
  if (i % 2 == 0) continue;

  console.log(i); // 1, 3, 5, 7, 9가 차례대로 출력됨
}

i가 짝수이면 continue가 본문 실행을 중단시키고 다음 이터레이션이 실행되게 합니다(i가 하나 증가하고, 다음 반복이 실행됨). 따라서 console.log 함수는 인수가 홀수일 때만 호출됩니다.

continue는 중첩을 줄이는 데 도움이 됩니다.
홀수를 출력해주는 예제는 다음과 같이 생길 수도 있습니다.

1
2
3
4
5
6
for (let i = 0; i < 10; i++) {

 if (i % 2) {
   console.log( i );
    }
}

기술적인 관점에서 봤을 때, 이 예시는 위쪽에 있는 예시와 동일합니다. continue를 사용하는 대신 코드를 if 블록으로 감싼 점만 다릅니다.

그런데 이렇게 코드를 작성하면 부작용으로 중첩 레벨(대괄호 안의 console.log 호출)이 하나 더 늘어납니다. if 안의 코드가 길어진다면 전체 가독성이 떨어질 수 있습니다.

’?’ 오른쪽엔 breakcontinue가 올 수 없습니다.
표현식이 아닌 문법 구조(syntax construct)는 삼항 연산자 ?에 사용할 수 없다는 점을 항상 유의하시기 바랍니다. 특히 breakcontinue 같은 지시자는 삼항 연산자에 사용하면 안 됩니다.

1
2
3
4
5
if (i > 5) {
console.log(i);
} else {
continue;
}

물음표를 사용해서 위 조건문을 아래와 같이 바꾸려는 시도를 할 수 있을겁니다.

1
(i > 5) ? console.log(i) : continue; // 여기에 continue를 사용하면 안 됩니다.

이런 코드는 문법 에러를 발생시킵니다. 이는 물음표 연산자 ?if문 대용으로 쓰지 말아야 하는 이유 중 하나입니다.

breack/continue와 레이블

여러 개의 중첩 반복문을 한 번에 빠져나와야 하는 경우가 종종 생기곤 합니다.

ij를 반복하면서 프롬프트 창에 (0,0)부터 (2,2)까지를 구성하는 좌표 (i, j)를 입력하게 해주는 예제를 살펴봅시다.

1
2
3
4
5
6
7
8
9
10
11
for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 여기서 멈춰서 아래쪽의 `완료!`가 출력되게 하려면 어떻게 해야 할까요?
  }
}

console.log('완료!');

사용자가 Cancel 버튼을 눌렀을 때 반복문을 중단시킬 방법이 필요합니다.

input 아래에 평범한 break 지시자를 사용하면 안쪽에 있는 반복문만 빠져나올 수 있습니다. 이것만으론 충분하지 않습니다(중첩 반복문을 포함한 반복문 두 개 모두를 빠져나와야 하기 때문이죠). 이럴 때 레이블을 사용할 수 있습니다.

레이블(label) 은 반복문 앞에 콜론과 함께 쓰이는 식별자입니다.

1
2
3
labelName: for (...) {
  ...
}

반복문 안에서 break <labelName>문을 사용하면 레이블에 해당하는 반복문을 빠져나올 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
outer: for (let i = 0; i < 3; i++) {

  for (let j = 0; j < 3; j++) {

    let input = prompt(`(${i},${j})의 값`, '');

    // 사용자가 아무것도 입력하지 않거나 Cancel 버튼을 누르면 두 반복문 모두를 빠져나옵니다.
    if (!input) break outer; // (*)

    // 입력받은 값을 가지고 무언가를 함
  }
}
console.log('완료!');

위 예시에서 break outerouter라는 레이블이 붙은 반복문을 찾고, 해당 반복문을 빠져나오게 해줍니다.

따라서 제어 흐름이 (*)에서 console.log('완료!')로 바로 바뀝니다.

레이블을 별도의 줄에 써주는 것도 가능합니다.

1
2
outer:
for (let i = 0; i < 3; i++) { ... }

continue 지시자를 레이블과 함께 사용하는 것도 가능합니다. 두 가지를 같이 사용하면 레이블이 붙은 반복문의 다음 이터레이션이 실행됩니다.

레이블은 마음대로 ‘점프’할 수 있게 해주지 않습니다. 레이블을 사용한다고 해서 원하는 곳으로 마음대로 점프할 수 있는 것은 아닙니다.

아래 예시처럼 레이블을 사용하는 것은 불가능합니다.

1
2
3
break label; // 아래 for 문으로 점프할 수 없습니다.

label: for (...)

breakcontinue는 반복문 안에서만 사용할 수 있고, 레이블은 반드시 break이나 continue 지시자 위에 있어야 합니다.

요약

지금까지 세 종류의 반복문에 대해 살펴보았습니다.

  • while - 각 반복문이 시작하기 전에 조건을 확인합니다.
  • do {..} while - 각 반복이 끝난 후에 조건을 확인합니다.
  • for (;;) - 각 반복이 시작하기 전에 조건을 확인합니다. 추가 세팅을 할 수 있습니다.

“무한” 반복문은 보통 while(true)를 써서 만듭니다. 무한 반복문은 여타 반복문과 마찬가지로 break 지시자를 사용해 멈출 수 있습니다.

현재 실행 중인 반복에서 더는 무언가를 하지 않고 다음 반복으로 넘어가고 싶다면 continue 지시자를 사용할 수 있습니다.

반복문 앞에 레이블을 붙이고, break/continue에 이 레이블을 함께 사용할 수 있습니다. 레이블은 중첩 반복문을 빠져나와 바깥의 반복문으로 갈 수 있게 해주는 유일한 방법입니다.

This post is licensed under CC BY 4.0 by the author.

JavaScript의 null 병합 연산자

JavaScript의 switch문