이제까지 나온 코드 예제들은 아주 단순한 계산과 입출력을 하는 프로그램이었습니다. 하지만 실제로 사용되는 프로그램들은 복잡한 논리 구조를 갖고 있고, 그를 통해 프로그램의 여러 기능이 제대로 동작하는 것입니다. 이 챕터에서는 프로그램의 논리 구조를 표현할 수 있는 조건문과 반복문, 그리고 그 밖에 프로그램의 논리 구조에 영향을 미치는 구문들을 살펴볼 것입니다.
우리가 실제로 사용하는 프로그램들은 수많은 '경우의 수'을 다루고 있습니다.
이렇게 경우에 따라 프로그램의 동작이 달라야 할 때, 우리는 조건문(conditional statement)을 통해 프로그램의 논리 구조를 표현할 수 있습니다.
if...else 구문if...else 구문을 사용하면 조건에 따라 특정 영역의 코드를 실행시키거나 실행시키지 않을 수 있습니다. 아래의 예제를 통해 사용법을 알아보겠습니다.
function roll() {return Math.ceil(Math.random() * 6)}function game() {const result = roll()alert(`결과: ${result}`)// if...else 구문if (result >= 4) {// 괄호 안의 조건을 만족하면, 즉 결과값이 true 이면// 이 영역의 코드가 실행된다.alert('당신이 이겼습니다!')} else {// 위 조건을 만족하지 않으면, 즉 결과값이 false 이면// 대신 이 영역의 코드가 실행된다.alert('당신이 졌습니다.')}}game()
위 코드를 직접 실행해보세요.
else가 필요 없는 경우에는 else를 생략할 수 있습니다.
if (result === 6) {alert('당신은 운이 좋군요!')}
중괄호(curly brace) 내부에 들어있는 구문이 하나라면, 중괄호를 생략해 줄 수도 있습니다.
if (result === 6) alert('당신은 운이 좋군요!')
if...else 구문의 중첩만약 세 개 이상의 경우의 수를 if...else를 통해 표현하려면, if...else를 중첩시키면 됩니다.
주사위를 굴려 3 또는 4가 나오면 비긴 것으로 게임을 고쳐보겠습니다.
function roll() {return Math.ceil(Math.random() * 6)}function game() {const result = roll()alert(`결과: ${result}`)if (result >= 5) {alert('당신이 이겼습니다!')} else {// 중첩된 if...else 구문if (result >= 3) {alert('비겼습니다.')} else {alert('당신이 졌습니다.')}}}game()
위와 같이 if...else 구문이 단순히 중첩된 경우라면 바깥쪽 else의 중괄호(curly brace)를 생략할 수도 있습니다.
if (result >= 5) {alert('당신이 이겼습니다!')} else if (result >= 3) {alert('비겼습니다.')} else {alert('당신이 졌습니다.')}
코드가 이전보다 훨씬 보기 좋아졌습니다. if...else의 중첩에는 제한이 없으므로, 경우의 수가 많은 경우에는 if...else 구문을 아래와 같이 계속 이어나갈 수 있습니다.
function translateColor(english) {if (english === 'red') {return '빨강색'} else if (english === 'blue') {return '파랑색'} else if (english === 'purple' || english === 'violet') {return '보라색'} else {return '일치하는 색깔이 없습니다.'}}
바로 위의 예제와 같이 하나의 변수에 대해 많은 경우의 수가 있는 경우, switch 구문을 사용하면 코드를 조금 더 보기 좋게 만들 수 있습니다.
아래 예제는 바로 위의 코드 예제와 완전히 똑같이 동작합니다.
function translateColor(english) {let resultswitch (english) {case 'red':result = '빨강색'breakcase 'blue':result = '파랑색'breakcase 'purple':result = '보라색'breakcase 'violet':result = '보라색'breakdefault:result = '일치하는 색깔이 없습니다.'}return result}
switch 구문은 case, break, default라는 키워드와 함께 사용됩니다. switch 바로 뒤의 괄호의 값이 '코드 실행 여부를 판별할 기준이 되는 값'이고, 이 기준이 되는 값과 case 바로 뒤에 오는 값이 일치1하면 콜론(:) 뒤의 코드 영역이 실행됩니다. 일치하는 값이 없으면 default 코드 영역이 대신 실행됩니다.
단, 여기서 주의할 점이 있습니다. case 뒤쪽의 코드 영역 마지막에 break를 써주지 않으면, 해당 case가 실행될 때 바로 뒤의 case 코드 영역이 뒤이어 실행되게 됩니다. 예를 들어, 위 코드 예제에서 case 'blue': 부분의 break를 지우고 코드를 실행해보면 이런 결과가 나옵니다.
translateColor('blue') // '보라색'
위와 같이 함수를 호출하면 case 'blue': 뒤쪽의 코드 영역이 실행되는데, 이 안에 break를 써주지 않았기 때문에 다음에 나오는 case 'purple': 뒤쪽의 코드 영역이 같이 실행되었습니다.
이처럼 break를 써 주지 않으면 의도치 않은 동작을 할 수 있으니 주의하세요. 다만 break의 이런 성질을 활용해서 코드를 짧게 쓸 수도 있습니다.
function translateColor(english) {let resultswitch (english) {case 'red':result = '빨강색'breakcase 'blue':result = '파랑색'breakcase 'purple':case 'violet':// 이 코드 영역은 english 변수의 값이 'purple'일 때와 'violet'일 때 모두 실행됩니다.result = '보라색'breakdefault:result = '일치하는 색깔이 없습니다.'}return result}
프로그래밍을 하다보면 유사한 작업을 여러 번 반복해서 해야할 경우가 있습니다.
위와 같은 작업을 하기 위해 JavaScript에서는 반복문(looping statement)을 사용합니다. 반복문은 루프라고 불리기도 합니다.
while 구문while 구문은 특정 조건을 만족하는 한 코드를 반복해서 실행시킵니다.
let i = 0 // 초기값while (i < 5) {// 위 괄호의 값이 `true`인 동안에는// 이 안쪽의 코드를 반복해서 실행시킵니다.console.log(`현재 i의 값: ${i}`)i++ // 갱신}console.log('루프가 종료되었습니다.')
처음에 i의 값이 0이었다가, 코드가 실행됨에 따라 점차 증가하면서 결국 i < 5의 값이 false가 되면, while 구문은 더 이상 내부 코드를 실행시키지 않고 실행 흐름을 다음으로 넘깁니다.
do...while 구문do...while 구문은 while 구문과 사용법은 크게 다르지 않으나, 내부 코드를 무조건 한 번은 실행시킨다는 차이점이 있습니다.
do {console.log('do...while!')} while (false) // 절대 `true`가 될 수 없지만, 루프는 1회 실행됩니다.
for 구문위의 while 구문 예제에서 볼 수 있듯이, 루프에는 초기값과 갱신에 대한 코드가 있는 경우가 많습니다. 이런 경우에는 for 구문을 이용해 코드를 조금 더 짧게 짤 수 있습니다. 아래 예제는 위 while 구문에 대한 예제와 완전히 똑같이 작동합니다.
// for (초기값 정의; 실행 조건; 갱신) { ... }for (let i = 0; i < 5; i++) {console.log(`현재 i의 값: ${i}`)}console.log('루프가 종료되었습니다.')
for 구문으로 정의된 루프는 항상 while 구문으로 바꿔쓸 수 있고, 많은 경우 반대로도 바꿀 수 있습니다. 다만 위와 같이 초기값을 정할 수 있고 갱신을 위한 코드가 짧은 경우에는 for 구문을, 그렇지 않은 경우에는 while 구문을 사용해야 코드가 깔끔해집니다.
ES2015가 나오기 이전까지는 for 구문이 배열을 순회하는 데에도 많이 사용되었습니다.
const arr = [1, 2, 3, 4, 5]for (let i = 0; i < arr.length; i++) {console.log(`배열의 ${i + 1} 번째 요소는 ${arr[i]} 입니다.`)}
하지만 근래에는 배열의 forEach 메소드나 for...of 구문이 더 많이 쓰이는 편입니다.
const arr = [1, 2, 3, 4, 5]arr.forEach((item, index) => {console.log(`배열의 ${index + 1} 번째 요소는 ${item} 입니다.`)})
const arr = [1, 2, 3, 4, 5]for (let item of arr) {console.log(`현재 요소는 ${item} 입니다.`)}
for...of 구문에 대해서는 Iterable 챕터에서 자세히 다룹니다.
break, continue간혹 루프를 도중에 멈추거나, 남은 코드를 건너뛰어버리고 루프의 다음 번 차례로 넘어가야 할 필요가 있습니다. 이 때 사용되는 구문이 break와 continue 입니다.
alert('퀴즈를 시작합니다.')while (true) {const answer = prompt('빨강의 보색은 무엇일까요?')if (answer === '초록') {alert('정답입니다! 🎉')break // 루프를 종료하고 다음 코드로 넘어감} else {alert('틀렸습니다! 다시 시도해보세요.')}}alert('퀴즈가 끝났습니다.')
for (let i = 1; i < 100; i++) {console.log(`현재 숫자는 ${i} 입니다.`)if (i % 7 !== 0) {continue // 루프의 나머지 코드를 건너뜀}console.log(`${i}는 7의 배수입니다.`)}
continue와 break가 루프의 나머지 코드를 건너뛰는 효과를 갖는 것과 유사하게, return과 throw는 함수의 나머지 코드를 건너뛰고 함수를 즉시 종료시키는 결과를 낳습니다.
returnreturn
function translateColor(english) {switch (english) {case 'red':return '빨강색'case 'blue':return '파랑색'case 'purple':case 'violet':return '보라색'default:return '일치하는 색깔이 없습니다.'}}
throwfunction translateColor(english) {switch (english) {case 'red':return '빨강색'case 'blue':return '파랑색'case 'purple':case 'violet':return '보라색'default:throw new Error('일치하는 색깔이 없습니다.')}}
throw 구문은 코드의 실행을 중단시키고 에러를 발생시키는 동작을 합니다. 이에 대해서는 예외 처리 챕터에서 자세히 다룹니다.