1. 예외 처리란?
코드 실행 중에 예기치 못한 에러가 발생했을 때, 이로부터 코드의 실행 흐름을 복구할 수 있는데 이를 예외 처리(Exception Handling)라고 한다.
JavaScript는 기본적으로 싱글 스레드이므로 스레드가 멈춘다는 것은 프로세스가 멈추는 것이어서 예외 처리는 필수이다.
2. JavaScript의 예외 처리 방법
& try-catch-finally
try...catch...finally 구문을 사용하여 에러가 나더라도 코드의 실행을 지속할 수 있다.
try {
console.log('에러가 나기 직전까지의 코드는 잘 실행됩니다.');
new Array(-1); // RangeError: Invalid array length
console.log('에러가 난 이후의 코드는 실행되지 않습니다.');
} catch (e) {
console.log('코드의 실행 흐름이 catch 블록으로 옮겨집니다.');
alert(`다음과 같은 에러가 발생했습니다: ${e.name}: ${e.message}`);
}
→ 에러가 났을 때 원상 복구를 시도하려면 try 블록 내부에 작성하면 된다. 에러가 발생했을 때 코드의 실행 흐름이 try 블록에서 catch 블록으로 옮겨 간다. 이때 catch 블록 안에서는 에러에 대한 정보를 담고 있는 객체 e를 사용할 수 있다.
for (let i of [1, 2, 3]) {
try {
if (i === 3) {
break;
}
} finally {
console.log(`현재 i의 값: ${i}`);
}
}
→ finally 블록이 사용되면 try 블록 안에서의 에러 발생 여부와 관계없이 무조건 실행된다.
→ finally 블록은 catch 블록과도 함께 사용된다.
→ 에러가 안 났을 때: try-finally
→ 에러가 났을 때: try - 에러 발생 - catch - finally
const even = parseInt(prompt('짝수를 입력하세요'));
if (even % 2 !== 0) {
throw new Error('짝수가 아닙니다.');
}
→ 에러의 종류를 구분해야 하거나 에러 객체에 기능을 추가해야 할 필요가 있을 때 사용한다.
(1) Promise의 경우
Promise.resolve()
.then(() => {
throw new Error('catch 메소드를 통해 예외 처리를 할 수 있습니다.');
})
.then(() => {
console.log('이 코드는 실행되지 않습니다.');
})
.catch(e => {
return e.message;
})
.then(console.log);
→ 기존 try-catch 블록에 넣을 경우에는 try 블록이 비동기 함수에서 발생한 에러를 처리할 수 없다.
→ 그래서 위와 같이 비동기 콜백의 내부에 try 블록을 작성해 줘야 한다.
→ then 메서드의 연쇄 안에서 에러가 발생하면 try...catch 구문과 유사하게 처음 만나는 에러 처리 콜백으로 코드의 실행 흐름이 건너뛰게 된다.
(2) async-await 사용의 경우
async function func(){
try{
const res = await fetch("https://~~");
}
catch(e){
console.log(e.message);
}
}
func(); // Failed to fetch
→ async-await 형태를 사용하면 위의 체인 형태의 복잡한 구조를 좀 더 단순하게 만들 수 있다.
3. 내 코드에서의 적용
프로젝트를 진행하면서 백엔드에서 MySQL 연동 관련 API를 호출할 때 요청 파라미터가 전달되지 않는 오류를 가장 많이 받았습니다.
그래서 이 부분에 대해서 에러 처리를 해 볼 예정입니다.
MySQL connection 객체로 쿼리를 실행할 때 에러가 발생하면 error를 throw하는 형태이며, SQLdata에 값이 제대로 들어오지 않았을 때 에러가 발생하고 서버가 중단됩니다.
getClubDetailInfo: async (req, res) => {
const selectSQL = `SELECT * FROM CLUB_TABLE WHERE C_IDX =?`;
const SQLdata = [req.query.data];
connection.query(seletSQL, SQLData, (error, result) => {
if (error) throw error;
res.status(200).send(result);
});
}
아래처럼 수정한 후로 SQLdata에 문제가 생기더라도 서버는 중단되지 않으며, 상태 코드로 현재 문제만 전달받을 수 있게 되었습니다.
이 외의 API 코드도 대부분 수정 전 코드와 같이 구성되어 있어서 아래와 같이 수정하였습니다.
getClubDetailInfo: async (req, res) => {
const selectSQL = `SELECT * FROM CLUB_TABLE WHERE C_IDX =?`;
const SQLdata = [req.query.data];
try {
// 기존 SQLdata 문제를 null, undefined 체크를 통해 미리 확인하여,
// 문제 발생 시 TypeError를 던져 주고 아래 실행문은 실행되지 않으며,
// catch문으로 넘어가 상태 코드 405를 클라이언트로 전달해 준다.
if (req.query.data === null || req.query.data === undefined)
throw TypeError("SQLdata Null Error");
connection.query(selectSQL, SQLdata, (error, result) => {
if (error) throw error;
res.status(200).send(result);
});
} catch (error) {
if (error instanceof TypeError) {
res.status(405).send("SQL Error");
} else {
res.status(406).send("Error :" + error);
}
}
}
4. 느낀 점
사이트를 만들면서 try-catch 구문이 예외 처리문이라는 정도는 알고 있었지만, 제대로 된 처리를 하지 않은 상태였습니다.
이번에 적용하면서 가장 많이 받았던 오류를 떠올려 처리해 주었는데, 아직 제가 생각하지 못한 예외 처리 부분들이 많을 것 같습니다.
좀 더 처리해야 할 부분이 생기면 바로바로 업데이트해 나가야겠다는 생각이 들었습니다.
https://helloworldjavascript.net/pages/290-exception.html
https://www.youtube.com/watch?v=EBmIHrLTVdg&ab_channel=코드종
많은 것을 배웠습니다, 감사합니다.