JavaScript 12분 읽기

제어문 - 조건문, 반복문

제어문의 사용을 줄이기 위해서 함수형 프로그래밍에서는 map, filter, reduce와 같은 방식을 제시하기도 합니다.

제어문

프로그래밍 코드는 위에서 아래로 실행되게 됩니다. 하지만 제어문을 사용하게 되면 이 흐름이 바뀔 수 있죠. 조건에 따라 특정 코드가 실행되고, 특정 코드를 계속 반복하는 식으로 흐름이 변하게 됩니다. 이렇게 프로그래밍 코드의 흐름을 제어하는 문이 제어문입니다.

제어문의 사용을 줄이기 위해서 함수형 프로그래밍에서는 map, filter, reduce와 같은 방식을 제시하기도 합니다.


블록문

블록문은 여러 코드를 { }를 이용해 묶은 것이라고 생각하면 됩니다. 코드 블록이라고도 합니다.

조건문

조건문은 특정 조건에 특정 코드 블록을 실행하게 하는 제어문입니다.

if

if를 사용해서 if문이라고도 합니다.

형태는 어렵지 않습니다. 조건식에는 불리언 값으로 평가될 수 있는 표현식이 들어가야 합니다. 불리언 값으로 평가될 수 있는 것들은 무엇이 있을까요?

  • 비교 연산자를 사용한 표현식( === !== > < >= <= … )
  • 논리 연산자를 사용한 표현식( || && ! )
  • truthy, falsy로 평가되는 표현식 ( 0 '' "" undefined null … )
  • true, false
// if ... else ...
if(조건식){
  // 조건식이 참인 경우
  
}else{ // else는 꼭 필요한 것이 아니기 때문에 if만 단독으로 사용할 수 있습니다.
 // 조건식이 거짓인 경우 
  
}

// if ... else if ... else
if(조건식1){
  // 조건식1이 참인 경우
  
}else if(조건식2){
  // 조건식1이 거짓이고 조건식2가 참인 경우
  
}else{
  // 조건식1과 조건식2가 모두 거짓인 경우
  
}

위와 같은 형태로 조건문을 작성합니다. ifelse는 하나의 조건에 하나씩 사용할 수 있지만 else if는 여러 개 사용이 가능합니다. 조건이 많다면 else if를 많이 사용하면 되겠죠?

예시를 보겠습니다.

var count = 10;

if(count > 10){
  console.log('count가 10보다 큽니다.');
}else{
  console.log('count가 10보다 크지 않습니다(작거나 같습니다.)');
} 

// 'count가 10보다 크지 않습니다(작거나 같습니다.)'

조건식이 불리언 값으로 평가되지 않을 경우 암묵적으로 형변환한 이후 평가하게 됩니다.

if(0){
  console.log(true); // 0이 참이면 true 출력
}else{
  console.log(false); // 0이 거짓이면 false 출력
}
// false

위와 같이 0은 숫자타입이지 불리언 타입이 아니지만, falsy값이면서 불리언으로 평가되면 false라고 할 수 있습니다. 그래서 false가 출력이 되는 것이죠.

이렇게 조건식이 falsy값이 아닌 경우는 true로 평가되고, falsy값인 경우는 false로 평가되게 됩니다. 굳이 비교 연산자를 사용하지 않고도 조건식으로 평가될 수 있다는 것을 기억하세요.

조건문은 삼항 조건 연산자로도 바꿀 수 있습니다. 가독성을 고려해서 사용하세요!

// 위의 예시를 삼항 조건 연산자로 바꿉니다.
0 ? console.log(true) : console.log(false); // false

// 조건을 중첩한 삼항 조건 연산자
'중첩' ? ('중첩됨' ? true : false) : false ; // true

문자열 '중첩'은 falsy값이 아니기 때문에 불리언 값으로 평가되면 true가 됩니다. 문자열의 falsy값은 '', "", ``죠.

조건문이 복잡하다면 삼항 조건 연산자를 사용하는 것은 가독성이 좋지 않을 수 있습니다. 하지만, 복잡하지 않다면 충분히 사용해도 되겠죠.

switch

switch문도 조건문입니다. 다만 사용되는 키워드가 많고, 불리언으로 평가되는 조건식을 받기보다 표현식으로 비교하는 느낌이 강합니다. 예시를 보고 확인해 보죠.

형태는 아래와 같습니다.

// switch문 형태
switch(표현식A){
  case 표현식1:
    // 표현식A === 표현식1일 때 실행되는 코드
  	break; // switch문에서 탈출하는 용도
  case 표현식2:
    // 표현식A === 표현식2일 때 실행되는 코드
   	break;
  default:
    // 모든 case와 다른 경우 실행
}

break의 경우 case에 맞는 경우에 switch문을 탈출하는 용도인데, 만약 탈출하지 않는다면 해당 case와 그 밑에 있는 case의 모든 코드를 실행하게 됩니다. 이것을 폴스루라고 합니다. 원하는 표현식과 다른데도 코드가 실행될 수 있기 때문에 이를 방지하기 위해서 break를 사용합니다.

하지만 꼭 모든 케이스에 break를 넣어줘야 하는 것은 아닙니다. 특정 케이스부터 여러 케이스의 코드를 사용할 때는 폴스루를 이용할 수 있겠죠. 하지만 자주 사용할 지는 모르겠네요.

반복문

반복문은 특정 조건을 만족시킨다면 특정 코드블록을 계속 반복하는 제어문입니다. 즉, 조건을 계속 만족시킨다면 무한정으로 코드블록을 실행하게 되므로 무한반복 문제에 빠지게 되므로 주의해야 됩니다.

for문

for를 이용해서 반복문을 만들 때는 초기 변수, 조건식, 증감식이 필요합니다.

  • 초기 변수: 변수를 선언하거나 할당합니다.
  • 조건식: 반복문을 계속 실행할지 판단하는 조건식입니다.
  • 증감식: 초기 변수로 선언한 변수를 증가 혹은 감소시킵니다. 보통 증감 연산자를 사용합니다.

형태는 아래와 같습니다.

for(초기 변수 ; 조건식 ; 증감식){
  // 반복할 코드
}

// 예시
for(var i =0; i<10; i++){
  console.log(i);
}
// 0 1 2 3 4 5 6 7 8 9 이 차례대로 출력됩니다.

// 무한 반복하는 법
for(;;){
  console.log('무한반복'); // 함부로 실행시키지 마세요. 프로세스가 멈출 수 있습니다.
}

위 예시는 초기 변수 i 에 0을 할당하고, i가 10보다 작은 경우 코드 블록을 실행합니다. 이후 i를 1 증가 시킵니다. 조건이 거짓이 될 때인 i가 10이 되는 순간 반복문은 끝이 나게 됩니다.

while문

while을 이용해서 반복문을 만들 때는 조건식만 필요합니다. 조건식이 참인 경우 코드블록을 반복해서 실행합니다.

형태는 아래와 같습니다.

while(조건식){
  // 반복할 코드
}

// 예시
var i = 0;
while(i<10){
  console.log(i);
  i++;
}

// 무한 반복하는 법
while(true){
  console.log('무한 반복');
}

위 예시는 for문을 사용했을 때와 같은 결과를 출력합니다. 반복되는 코드 안에서 반드시 조건을 탈출하게 만들어야 합니다. 그렇지 않은 경우 무한 반복을 하게 됩니다.

do…while문

do...while문은 while문과 비슷하게 동작하지만 do의 코드블록을 먼저 실행하고 조건식을 while문의 조건식을 평가한다는 것이 다릅니다. 즉, 한 번은 코드를 실행한다는 것이죠.

때에 따라서 while문이 아니라 do...while문을 이용하는 것이 좋은 경우도 있습니다.

형태는 아래와 같습니다.

do{
  // 반복할 코드 (먼저 실행합니다.)
}while(조건식)

반복문의 중첩

반복문은 중첩이 가능합니다. 보통 for문을 중첩시키는 경우가 많습니다. 예시로 구구단을 출력하는 것을 보여드릴게요.

for (let i = 2; i < 10; i++) {
  for (let j = 1; j < 10; j++) {
    console.log(`${i} * ${j} = ${i * j}`);
  }
}

for문 안에 for문이 또 들어갔습니다. 이 경우 외부 반복문의 변수가 2일 때 내부 반복문이 조건식을 만족하지 못할 때까지 반복하고 외부 반복문의 증감식이 수행됩니다. 그 후 다시 외부 반복문의 조건식을 평가해서 만족하면 내부 반복문을 수행하는 방식을 계속 반복합니다.

구구단의 예시를 보면 i가 2인 경우 j가 1부터 9까지의 구구단을 출력하고 i가 증가하는 것을 볼 수 있습니다. 이 순서만 기억한다면 중첩 반복문을 사용하는 것은 어렵지 않을 것입니다.

중첩의 경우 이중만 할 수 있는 것은 아닙니다. 하지만 너무 많은 중첩은 가독성이 좋지 않는 것뿐만 아니라 프로그램의 성능을 저하시킬 수 있는 요인이 된다는 것을 잊지마세요.

for문과 while문은 명확히 어떤 것을 사용해야 하는지 구분하기 어렵습니다. 서로 바꿀 수 있기 때문이죠. 하지만 자주 사용되는 방식이 있습니다.

for문을 사용

  • 반복 횟수가 비교적 명확할 때
  • 배열, 문자열 내부를 순환할 때
  • 반복문의 중첩이 필요할 때

while문을 사용

  • 반복 횟수가 명확하지 않을 때

break

break는 코드블록을 탈출시키도록 해줍니다. 단, 레이블문, 반복문 혹은 switch문에서만 사용 가능합니다.

if(true){
    break; // SyntaxError: Illegal break statement
}

다른 코드블록에서 break를 사용하면 문법 에러가 발생하죠.

사용하는 방식은 아래와 같은 경우가 있겠네요.

// 무한 반복에서 탈출하기
var i = 0;
while(true){
 if(i === 3){
   break;
 }else{
   i++;
 }
}

꼭 무한 반복이 아니더라도 특정 조건이 되면 탈출하는 방식으로 사용될 수 있겠죠. 중첩 반복문의 경우 내부 반복문에서 break를 하면 외부 반복문으로 탈출합니다. 즉, break만 사용하면 중첩 반복문을 한 번에 탈출할 수는 없습니다.

이때는 레이블문을 사용합니다. 이외에도 레이블문을 사용할 수 있지만, 프로그램 흐름이 복잡해져 오류 발생이 쉬우니 사용하지 말도록 하죠.

// 중첩 반복문 탈출

for(var i=0; i<2;i++){
  for(var j = 0; j < 10; j++){
    if(j === 3) break;
    console.log(i, j);
  }
  console.log(i);
}
/*
0 0 (내부 반복문)
0 1 (내부 반복문)
0 2 (내부 반복문)
0 (외부 반복문)
1 0 (내부 반복문)
1 1 (내부 반복문)
1 2 (내부 반복문)
1(외부 반복문)
*/

// 레이블문 사용

outer: for(var i=0; i<2;i++){
  for(var j = 0; j < 10; j++){
    if(j === 3) break outer;
    console.log(i, j);
  }
  console.log(i);
}
/* 
0 0 
0 1
0 2
*/

outer 레이블로 탈출하게 되는 것이죠.

continue

계속하다를 의미하는 continue는 코드 흐름을 중단하고 증감식으로 이동시킵니다. 즉, continue이후의 코드를 무시하고 증감식을 수행하고 다시 반복문을 실행하는 것이죠.

for(var i = 0; i < 10; i++){
  if(i === 2) continue;
  console.log(i);
}

위의 예시를 실행하면 2가 출력되지 않습니다.continue를 만나는 순간 증감식으로 이동했기 때문이죠.