요약
- 소프트웨어는 에러와 예외가 늘 발생하여 정상적인 사용 흐름이 막히게 됩니다.
- 에러 핸들링과 예외 핸들링
- 에러 핸들링: 컴퓨터가 코드를 실행하는 과정 자체에서 발생하는 에러를 처리하는 과정
ex) 언어의 문법 에러, 통신 장애로 발생하는 에러- 예외 핸들링: 정상적인 서비스 진행을 방해하는 과정으로 코드가 진행될 경우, 이를 막기 위해 개발자가 의도적으로 발생시키는 예외 처리 과정
- Javascript 에서는 try-catch문과 throw를 이용하여 에러를 처리한다.
- 에러 핸들링 미들웨어: 에러를 처리하기 위해 반복적으로 진행되는 코드를 모듈화.
소프트웨어의 확장성, 생산성 향상.
에러 발생 원인: 실행 환경, 사용자의 특성, 개발자의 실수 등
에러 핸들링
개발자가 서비스 중 발생할 수 있는 에러를 사전 예측해, 이용자의 사용 흐름이 막히지 않도록 유도하는 과정
에러 | 예외 | |
---|---|---|
발생 주체(원인) | 컴퓨터 | 개발자 |
의도성 | X | O |
구분 | 에러 핸들링(Error Handling) | 예외 핸들링(Exception Handling) |
예 | 언어의 문법 에러, 통신 장애로 발생하는 에러 | |
(컴퓨터가 코드를 실행하는 과정 자체에서 발생) |
예) Uncaught ReferenceError: consoel is not defined
: 개발자가 실수로 오타를 내서, 컴퓨터가 자바스크립트 문법을 실행하는 과정 자체에서 더이상 진행할 수 없어서 발생한 에러.
‘개발자가 의도적으로 발생시키는 예외상황’
언어가 실행되는 과정 자체에는 큰 문제가 없지만, 개발자와 개발팀이 판단하기에 정상적인 상황이 아닌 과정
예) 아래의 규칙이 있는데 유저가 비밀번호를 5자리만 설정하면?
회원가입의 규칙.
- 회원가입에 사용되는 이메일에는 @ 가 들어가야 한다.
- 비밀번호를 10자 이상 지정하도록 한다.
- 회원가입에 사용되는 계정은 중복될 수 없다.
👉 개발자의 의도와는 다른 방향으로 유저가 서비스를 이용하고자 하므로 예외 상황
👉 개발자는 예상되는 예외 상황들을 나열, 잘못된 방법으로 서비스를 이용하는 것을 막아야 한다!
예외상황은 컴퓨터 언어 입장에서 에러가 아니다.
문법이 틀리지 않았고, 함수를 정상적으로 호출했을 수 있기 때문.
예외 상황은 함수 내부에서 미리 if 문등으로 처리를 해두어야 한다.
잘못된 방식으로 서비스를 이용하기 전에 예외 상황을 미리 일으켜 에러를 발생시키고,
클라이언트(프론트엔드) 개발자에게 에러 상황 에러 코드로 알린다.
프론트엔드 개발자
는 아래 에러를 반환받아, 사용자(고객)에게 적절한 안내 문구를 띄워준다.
const { email, password } = req.body
// 회원가입에 사용되는 이메일에 @ 가 들어가지 않으면 에러 반환
if (!email.includes('@')) {
return new Error('EMAIL_INVALID')
}
// 비밀번호가 10자 미만이라면 에러 반환
if (password.length < 10) {
return new Error('PASSWORD_INVALID')
}
// 이미 가입되어 있는 계정으로 가입 시도를 할 때 에러 반환
...
// -> return은 js에서 일반적인 에러 처리방식이 아니다!!
에러 핸들링
에러 발생 전 사전 처리, 발생한 에러를 잡아내고 처리하는 모든 과정
throw
프로그래밍 언어에서 예외 상황에 에러를 발생시키는 방법
예시)
throw new Error('EMAIL_INVALID')
throw new Error('message 부분')
잠재적인 에러가 발생할 가능성이 있는 부분에서 에러를 처리하기 위한 용도
동작 순서:
1. try { } 블록에서 어떠한 예외 처리를 위해 에러를 throw한다
2. 발생한 에러가 catch { } 블록으로 넘어간다.
어디에 위치시킬까?
catch { } 블록 내부에서 또다시 에러를 throw하면 더 상위 모듈로 에러를 던진다.
try {
const { email, password } = req.body
if (!email.includes('@')) {
const error = new Error('EMAIL_INVALID')
error.statusCode = 400
throw error
}
if (password.length < 10) {
const error = new Error('PASSWORD_INVALID')
error.statusCode = 400
throw error
}
...
} catch (err) {
console.log(err) // 어떠한 형태의 에러가 발생하든, 디버그를 해야하는 상황이 있을 수 있으니 에러를 콘솔에 찍어서 개발자에게 보여준다.
return res.json({}) // 클라이언트에게 정상적으로 수행이 안됐기 때문에 '이 에러는 어떤 에러입니다'라고 내용을 json데이터 형태로 보낸다
}
클라이언트의 요청에 응답하는 과정에서, 중간에서 흘러들어온 코드들을 개발자가 의도한 목적에 맞게 처리하는, 말하자면 거쳐가는 함수
에러 핸들링은 반복 작업이다.
서로 다른 기능이어도 같은 에러를 잡아야 할 수 있다.
예) 회원가입 기능이면서 로그인 기능인 규칙:
- 회원가입에 사용되는 이메일에는 @ 가 들어가야 한다.
- 비밀번호를 10자 이상 지정하도록 한다.
같은 규칙을 서로 다른 두 기능에 중복으로 기입해야 한다면? 코드도 중복으로 계속 기입해야할까? NOooo~ 공통된 작업을 모듈화 👉 미들웨어
Express의 미들웨어: 컨트롤러와 컨트롤러를 이어주는 또 다른 컨트롤러의 한 종류
catch all
: 클라이언트나 사용자가 너무 깊게 알 필요는 없고
그냥 내가 의도한 에러메시지 'A'만 받아도 충분하다고 판단됐을 때 한곳에서 에러메시지를 한 곳에서 모으고 모아서 클라이언트 화면단에 보여주는 역할
const { email, password } = req.body
if (!email.includes('@')) {
throw new Error('EMAIL_INVALID')
}
if (password.length < 10) { // 비밀번호 길이가 10미만일 때
throw new Error('PASSWORD_INVALID')
}
에러들을 한 곳으로 모으는 역할을 미들웨어가 수행하면서 동시에 로그 기능 등을 붙인다면, 에러 핸들링을 하면서 어떤 에러가 발생했었는지 기록도 할 수 있어서 에러 핸들링을 보다 다채롭게 할 수 있습니다.
에러 처리하는 기능을 미들웨어로 모듈화하는 것 또한 특정 기능을 분리하는 것이므로 관심사의 분리 (Seperation Of Concern | SOC)에 해당한다고 할 수 있습니다.