문장 / 제어 흐름과 오류 처리
문장 / 제어 흐름과 오류 처리 (Statements, Control flow and error handling)
JavaScript는 애플리케이션에 많은 상호작용성을 통합하는 데 사용할 수 있는 간결한 문장 집합, 특히 제어 흐름 문장(control flow statements)을 지원한다. 이 장은 이러한 문장들의 개요를 제공한다. JavaScript 참조(reference)는 이 장의 문장들에 대한 철저한 세부 사항을 포함한다. JavaScript 코드에서 세미콜론(;) 문자는 문장을 구분하는 데 사용된다.
어떤 JavaScript 표현식도 곧 문장이다. 표현식에 대한 완전한 정보는 Expressions and operators를 보라.
본문
블록 문장 (Block statement)
가장 기본적인 문장은 **블록 문장(block statement)**으로, 문장들을 그룹화하는 데 사용된다. 블록은 한 쌍의 중괄호로 구분된다.
{
statement1;
statement2;
// …
statementN;
}
예시 (Example)
블록 문장은 일반적으로 제어 흐름 문장(if, for, while)과 함께 사용된다.
while (x < 10) {
x++;
}
여기서 { x++; }이 블록 문장이다.
참고:
var로 선언된 변수는 블록 범위가 아니라 포함하는 함수나 스크립트로 범위가 한정되며, 설정 효과는 블록 너머까지 지속된다. 예:var x = 1; { var x = 2; } console.log(x); // 2블록 안의
var x문장이 블록 앞의var x문장과 같은 범위에 있기 때문에 2를 출력한다. (C나 Java에서는 이에 해당하는 코드가 1을 출력했을 것이다.) 이런 범위 영향은let이나const를 사용하면 완화할 수 있다.
조건문 (Conditional statements)
조건문은 지정된 조건이 참일 때 실행되는 명령 집합이다. JavaScript는 if...else와 switch 두 가지 조건문을 지원한다.
if...else 문장
지정된 논리 조건이 참일 때 문장을 실행하려면 if 문장을 사용하라. 조건이 거짓일 때 문장을 실행하려면 선택적인 else 절을 사용하라.
if 문장은 다음과 같다.
if (condition) {
statement1;
} else {
statement2;
}
여기서 condition은 true 또는 false로 평가되는 어떤 표현식이든 될 수 있다. (true·false로 평가되는 것에 대한 설명은 Boolean을 보라.)
condition이 true로 평가되면 statement1이 실행되고, 그렇지 않으면 statement2가 실행된다. statement1과 statement2는 추가로 중첩된 if 문장을 포함한 어떤 문장이든 될 수 있다.
또한 else if를 사용해 문장을 결합해 여러 조건을 순서대로 검사할 수도 있다.
if (condition1) {
statement1;
} else if (condition2) {
statement2;
} else if (conditionN) {
statementN;
} else {
statementLast;
}
여러 조건의 경우, true로 평가되는 첫 번째 논리 조건만 실행된다. 여러 문장을 실행하려면 블록 문장({ /* … */ })으로 그룹화하라.
모범 사례 (Best practice)
일반적으로 항상 블록 문장을 사용하는 것이 좋은 관행이다 — 특히 if 문장을 중첩할 때.
if (condition) {
// condition이 true일 때의 문장
// …
} else {
// condition이 false일 때의 문장
// …
}
일반적으로 x = y 같은 할당을 조건으로 가진 if...else를 두지 않는 것이 좋은 관행이다.
if (x = y) {
// 여기 문장
}
그러나 드물게 그런 일을 하고 싶다면, while 문서의 "Using an assignment as a condition" 절에 알고 따라야 할 일반적 모범 사례 문법에 대한 안내가 있다.
Falsy 값 (Falsy values)
다음 값들은 false로 평가된다(일명 Falsy 값).
falseundefinednull0NaN- 빈 문자열(
"")
다른 모든 값 — 모든 객체를 포함해 — 은 조건문에 전달되면 true로 평가된다.
참고: 프리미티브 불리언 값
true·false를Boolean객체의true·false값과 혼동하지 말라!const b = new Boolean(false); if (b) { // 이 조건은 true로 평가된다 } if (b == true) { // 이 조건은 false로 평가된다 }
예시 (Example)
다음 예에서 함수 checkData는 Text 객체의 문자가 세 개면 true를 반환하고, 그렇지 않으면 alert를 표시하고 false를 반환한다.
function checkData() {
if (document.form1.threeChar.value.length === 3) {
return true;
}
alert(
`Enter exactly three characters. ${document.form1.threeChar.value} is not valid.`,
);
return false;
}
switch 문장
switch 문장은 프로그램이 표현식을 평가하고 그 표현식의 값을 case 레이블과 일치시키려 시도하게 한다. 일치하는 것을 찾으면 프로그램은 관련 문장을 실행한다.
switch 문장은 다음과 같다.
switch (expression) {
case label1:
statements1;
break;
case label2:
statements2;
break;
// …
default:
statementsDefault;
}
JavaScript는 위 switch 문장을 다음과 같이 평가한다.
- 프로그램은 먼저
expression의 값과 일치하는 레이블을 가진case절을 찾고, 그 절로 제어를 옮겨 관련 문장을 실행한다. - 일치하는 레이블을 찾지 못하면 프로그램은 선택적인
default절을 찾는다:default절을 찾으면 프로그램은 그 절로 제어를 옮겨 관련 문장을 실행한다.default절을 찾지 못하면 프로그램은switch끝 다음 문장에서 실행을 재개한다.
- (관례상
default절은 마지막 절로 쓰이지만, 반드시 그래야 하는 것은 아니다.)
break 문장 (break statements)
각 case 절과 연결된 선택적인 break 문장은 일치하는 문장이 실행되면 프로그램이 switch에서 빠져나와 switch 다음 문장에서 실행을 계속하도록 보장한다. break를 생략하면 프로그램은 switch 문장 안에서 계속 실행된다(다음 case 아래의 문장을 실행하는 식으로).
예시 (Example)
다음 예에서 fruitType이 "Bananas"로 평가되면 프로그램은 값을 case "Bananas"와 일치시키고 관련 문장을 실행한다. break를 만나면 프로그램은 switch를 빠져나와 switch 다음 문장에서 실행을 계속한다. break를 생략했다면 case "Cherries"의 문장도 실행되었을 것이다.
switch (fruitType) {
case "Oranges":
console.log("Oranges are $0.59 a pound.");
break;
case "Apples":
console.log("Apples are $0.32 a pound.");
break;
case "Bananas":
console.log("Bananas are $0.48 a pound.");
break;
case "Cherries":
console.log("Cherries are $3.00 a pound.");
break;
case "Mangoes":
console.log("Mangoes are $0.56 a pound.");
break;
case "Papayas":
console.log("Papayas are $2.79 a pound.");
break;
default:
console.log(`Sorry, we are out of ${fruitType}.`);
}
console.log("Is there anything else you'd like?");
예외 처리 문장 (Exception handling statements)
throw 문장으로 예외를 던지고, try...catch 문장으로 처리할 수 있다.
예외 타입 (Exception types)
JavaScript에서는 거의 모든 객체를 던질 수 있다. 그러나 던져진 객체가 모두 동등하게 만들어진 것은 아니다. 숫자나 문자열을 오류로 던지는 것이 흔하지만, 이 목적을 위해 특별히 만들어진 예외 타입 중 하나를 사용하는 것이 종종 더 효과적이다.
- ECMAScript 예외
DOMException
throw 문장
예외를 던지려면 throw 문장을 사용하라. throw 문장은 던져질 값을 지정한다.
throw expression;
특정 타입의 표현식뿐만 아니라 어떤 표현식도 던질 수 있다. 다음 코드는 여러 종류의 예외를 던진다.
throw "Error2"; // String 타입
throw 42; // Number 타입
throw true; // Boolean 타입
throw {
toString() {
return "I'm an object!";
},
};
try...catch 문장
try...catch 문장은 시도할 문장 블록을 표시하고, 예외가 던져지면 하나 이상의 응답을 지정한다. 예외가 던져지면 try...catch 문장이 그것을 잡는다.
try...catch 문장은 하나 이상의 문장을 포함하는 try 블록과, try 블록에서 예외가 던져지면 무엇을 할지 지정하는 문장을 포함하는 catch 블록으로 구성된다.
즉, try 블록이 성공하기를 원하지만, 그렇지 않으면 제어가 catch 블록으로 넘어가기를 원한다. try 블록 안의 어떤 문장(또는 try 블록 안에서 호출된 함수)이 예외를 던지면 제어는 즉시 catch 블록으로 이동한다. try 블록에서 예외가 던져지지 않으면 catch 블록은 건너뛴다. finally 블록은 try와 catch 블록이 실행된 후, try...catch 문장 다음 문장들이 실행되기 전에 실행된다.
다음 예는 try...catch 문장을 사용한다. 예제는 함수에 전달된 값에 따라 배열에서 월 이름을 가져오는 함수를 호출한다. 값이 월 번호(1–12)에 해당하지 않으면 'Invalid month code' 값으로 예외가 던져지고, catch 블록의 문장들이 monthName 변수를 'unknown'으로 설정한다.
function getMonthName(mo) {
mo--; // 배열 인덱스를 위해 월 번호 조정 (0 = Jan, 11 = Dec)
// prettier-ignore
const months = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
if (!months[mo]) {
throw new Error("Invalid month code"); // 여기서 throw 키워드 사용
}
return months[mo];
}
try {
// 시도할 문장
monthName = getMonthName(myMonth); // 함수가 예외를 던질 수 있음
} catch (e) {
monthName = "unknown";
logMyErrors(e); // 예외 객체를 오류 처리기(즉 자신의 함수)에 전달
}
catch 블록
try 블록에서 생성될 수 있는 모든 예외를 처리하려면 catch 블록을 사용할 수 있다.
catch (exception) {
statements
}
catch 블록은 throw 문장이 지정한 값을 담는 식별자(위 문법에서 exception)를 지정한다. 이 식별자를 사용해 던져진 예외에 대한 정보를 얻을 수 있다.
JavaScript는 catch 블록에 진입할 때 이 식별자를 만든다. 식별자는 catch 블록이 지속되는 동안만 유효하다. catch 블록 실행이 끝나면 식별자는 더 이상 존재하지 않는다.
예를 들어 다음 코드는 예외를 던진다. 예외가 발생하면 제어가 catch 블록으로 이동한다.
try {
throw "myException"; // 예외 생성
} catch (err) {
// 예외를 처리할 문장
logMyErrors(err); // 예외 객체를 오류 처리기에 전달
}
참고:
catch블록 안에서 콘솔에 오류를 기록할 때는 디버깅을 위해console.log()보다console.error()를 사용하는 것이 좋다. 이는 메시지를 오류로 형식화하고 페이지가 생성한 오류 메시지 목록에 추가한다.
finally 블록
finally 블록은 try와 catch 블록이 실행된 후에 실행될 문장을 포함한다. 또한 finally 블록은 try...catch...finally 문장 다음에 오는 코드 전에 실행된다.
finally 블록은 예외가 던져졌는지 여부와 관계없이 실행된다는 점도 중요하다. 단, 예외가 던져지면, 던져진 예외를 처리하는 catch 블록이 없더라도 finally 블록의 문장들은 실행된다.
finally 블록을 사용해 예외가 발생할 때 스크립트가 우아하게 실패하게 만들 수 있다. 예를 들어 스크립트가 묶어둔 리소스를 해제해야 할 수 있다.
다음 예는 파일을 열고 그 파일을 사용하는 문장들을 실행한다. (서버 사이드 JavaScript는 파일에 접근할 수 있게 해준다.) 파일이 열린 동안 예외가 던져지면, 스크립트가 실패하기 전에 finally 블록이 파일을 닫는다. 여기서 finally를 사용하면 오류가 발생해도 파일이 절대 열린 채로 남지 않도록 보장한다.
openMyFile();
try {
writeMyFile(theData); // 이것은 오류를 던질 수 있다
} catch (e) {
handleError(e); // 오류가 발생하면 처리
} finally {
closeMyFile(); // 항상 리소스를 닫는다
}
finally 블록이 값을 반환하면, try와 catch 블록의 어떤 return 문장과도 상관없이 그 값이 전체 try...catch...finally 구성의 반환 값이 된다.
function f() {
try {
console.log(0);
throw "bogus";
} catch (e) {
console.log(1);
// 이 return 문장은 finally 블록이 끝날 때까지 중단된다
return true;
console.log(2); // 도달 불가
} finally {
console.log(3);
return false; // 이전 "return"을 덮어씀
// `f`는 여기서 끝난다
console.log(4); // 도달 불가
}
console.log(5); // 도달 불가
}
console.log(f()); // 0, 1, 3, false
finally 블록이 반환 값을 덮어쓰는 것은 catch 블록 안에서 던지거나 다시 던진 예외에도 적용된다.
function f() {
try {
throw "bogus";
} catch (e) {
console.log('caught inner "bogus"');
// 이 throw 문장은 finally 블록이 끝날 때까지 중단된다
throw e;
} finally {
return false; // 이전 "throw"를 덮어씀
// `f`는 여기서 끝난다
}
}
try {
console.log(f());
} catch (e) {
// 이것은 결코 도달하지 않는다!
// f()를 실행하는 동안 `finally` 블록은 false를 반환하고,
// 이는 위 `catch` 안의 `throw`를 덮어쓴다
console.log('caught outer "bogus"');
}
// 기록:
// caught inner "bogus"
// false
try...catch 문장 중첩 (Nesting)
하나 이상의 try...catch 문장을 중첩할 수 있다.
내부 try 블록에 해당하는 catch 블록이 없으면:
- 반드시
finally블록을 포함해야 하고, - 둘러싸는
try...catch문장의catch블록이 일치 여부를 검사한다.
자세한 내용은 try...catch 참조 페이지의 nested try-blocks를 보라.
Error 객체 활용 (Utilizing Error objects)
오류의 타입에 따라 name과 message 속성을 사용해 더 정제된 메시지를 얻을 수 있을 수 있다.
name 속성은 Error의 일반 클래스(예: DOMException 또는 Error)를 제공하고, message는 일반적으로 오류 객체를 문자열로 변환해 얻는 것보다 더 간결한 메시지를 제공한다.
자신만의 예외를 던질 때 (예를 들어 catch 블록이 자신의 예외와 시스템 예외를 구분하지 않는다면) 이런 속성을 활용하기 위해 Error 생성자를 사용할 수 있다.
예:
function doSomethingErrorProne() {
if (ourCodeMakesAMistake()) {
throw new Error("The message");
}
doSomethingToGetAJavaScriptError();
}
try {
doSomethingErrorProne();
} catch (e) {
// 이제 실제로 `console.error()`를 사용
console.error(e.name); // 'Error'
console.error(e.message); // 'The message' 또는 JavaScript 오류 메시지
}
더 알아보기
- JavaScript 가이드: 함수(Functions)
- JavaScript 가이드: 반복과 순회(Loops and iteration)
- try...catch 참조
- switch 참조
- 키워드: 문장, 제어 흐름, if, else, switch, case, throw, try, catch, finally, 예외 처리, Falsy 값, Error 객체