Error Handling

HS K·2022년 11월 20일
0

요약

  • 소프트웨어는 에러와 예외가 늘 발생하여 정상적인 사용 흐름이 막히게 된다.
  • 에러 핸들링과 예외 핸들링
    • 에러 핸들링 : 컴퓨터가 코드를 실행하는 과정 자체에서 발생하는 에러를 처리하는 과정
      ex) 언어의 문법 에러, 통신 장애로 발생하는 에러
    • 예외 핸들링: 정상적인 서비스 진행을 방해하는 과정으로 코드가 진행될 경우, 이를 막기 위해 개발자가 의도적으로 발생시키는 예외 처리 과정
  • Javascript 에서는 try-catch문과 throw를 이용하여 에러를 처리한다.
  • 에러 핸들링 미들웨어 : 에러를 처리하기 위해 반복적으로 진행되는 코드를 모듈화.
    소프트웨어의 확장성, 생산성 향상.

0. 학습목표

  1. 에러 핸들링의 목적과 의의를 설명할 수 있다.
  2. 발생 가능한 에러에 대해 설명 할 수 있다.
  3. 사용가능한 여러 에러 처리법을 적용할 수 있다.

1. 에러 핸들링의 목적과 의의

1-1. 에러 핸들링 목적

에러 발생 원인: 실행 환경, 사용자의 특성, 개발자의 실수 등

에러 핸들링
개발자가 서비스 중 발생할 수 있는 에러를 사전 예측해, 이용자의 사용 흐름이 막히지 않도록 유도하는 과정

  • 에러 발생율이 0%인 서비스는 존재하지 않는다.
  • 예외 상황이나 에러가 발생 시,
    👉 (X) 소프트웨어 갑자기 종료하는 것
    👉 (O) 에러가 발생했음을 알리고, 곧 서비스가 정상적으로 구동될 것임을 안내하는 것.
  • 효과: 사용자 신뢰성(서비스가 다시 안정화될 것이다), 서비스 이용자 이탈 방지

1-2. 에러 핸들링 목적

에러예외
발생 주체컴퓨터개발자
의도성XO
구분에러 핸들링예외 핸들링
언어의 문법 에러, 통신 장애로 발생하는 에러
(컴퓨터가 코드를 실행하는 과정 자체에서 발생)

에러

ex) Uncaught ReferenceError: console 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에서 일반적인 에러 처리방식이 아니다!!

에러 핸들링

에러 발생 전 사전 처리, 발생한 에러를 잡아내고 처리하는 모든 과정

2. throw와 try-catch

2-1. throw

throw
프로그래밍 언어에서 예외 상황에 에러를 발생시키는 방법
cf) 특정 예외 상황에서 에러를 return 하는 방법은 에러를 발생시키는 일반적인 방법은 아니다. 자바스크립트 뿐만 아니라 여러 프로그래밍 언어에서 예외 상황에 에러를 발생시키는 방법은 throw이다.

throw new Error('EMAIL_INVALID')

throw new Error('message 부분')
  • 예외 상황 발생 시, 진행하던 작업 중단 및 에러를 호출한 상위 모듈로 제어를 넘긴다. 해당 지점에 특정 에러가 발생했음을 알리고, 이 에러가 발생하면 상위 모듈은 그에 맞는 액션을 (코드) 취한다.

    • 상위 모듈 : 하위에서 발생하여 넘어오는 에러에 대해 대비.
      ex) 에러A 발생 시 B라는 처리, 에러C 발생 시 D라는 방법으로 처리
  • try-catch에서 수행하도록 설계되어 있다.

  • 메세지 부분 : 어떤 예외 상황인지, 어떤 에러인지 설명하기 위해서 메시지 부분이 필수적으로 들어간다.

2-2. try-catch

잠재적인 에러가 발생할 가능성이 있는 부분에서 에러를 처리하기 위한 용도

  • try { } 블록: 에러가 발생할 가능성이 있는 코드
  • catch { } 블록: 에러가 발생했을 때를 대비한 코드

작동원리
1. try { } 블록에서 어떠한 예외 처리를 위해 에러를 throw한다
2. 발생한 에러가 catch { } 블록으로 넘어간다.

따라서 try 블록 내부에 throw 코드가 있거나, throw 코드를 내장하고 있는 다른 함수를 호출하는 부분이 있다.

try {
  const a = someFunc(); // <-- 함수 실행이 잘못될 확률이 있으므로 try 내부에 존재.
  let aDouble = a + a; // <-- 변수 할당이 잘못되어 있을 확률이 있으므로 try 내부에 존재.
  let nothing = 1 + 4; // <-- try에 있을 필요가 없음.
} 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데이터 형태로 보낸다
}

3. 에러 핸들링에서의 미들웨어

3-1. 미들웨어란 무엇일까?

클라이언트의 요청에 응답하는 과정에서, 중간에서 흘러들어온 코드들을 개발자가 의도한 목적에 맞게 처리하는, 말하자면 거쳐가는 함수

에러 핸들링은 반복 작업이다.
서로 다른 기능이어도 같은 에러를 잡아야 할 수 있다.

ex) 회원가입 기능이면서 로그인 기능인 규칙

- 회원가입에 사용되는 이메일에는 @ 가 들어가야 한다.
- 비밀번호를 10자 이상 지정하도록 한다.

같은 규칙을 서로 다른 두 기능에 중복으로 기입해야 한다면? 코드도 중복으로 계속 기입해야할까? NO! 공통된 작업을 모듈화 👉 미들웨어


Express의 미들웨어 : 컨트롤러와 컨트롤러를 이어주는 또 다른 컨트롤러의 한 종류
  • 컨트롤러 사이에 위치하며, 컨트롤러 진입 전 공통적으로 해야하는 작업들을 처리하는 기능을 한다.
  • 다른 모듈에서 반복적으로 사용되어야 하는 중복 기능을 보통 미들웨어로 구현하는 편(항상은 아님!)
  • 에러 핸들링을 미들웨어로 사용하는 것의 장점: 여러 경우에 발생하는 에러 핸들링을 하나로 모을 수 있다.

미들웨어 예

  • CORS 정책 설정 미들웨어
  • logging 미들웨어 : 서버에서 일어나는 상황을 기록(log)
  • Application level middleware - app.use : request 객체로 들어온 클라이언트의 의도한 요청이 꼭 훑고 지나갔으면 하는 모든 내용이 담겨있기 때문에 가장 많이 사용된 미들웨어
  • Router level middleware - router.use : 라우터를 사용한 분기
  • Built-in middleware - express.static
  • Built-in middleware - express.json : 새롭게 들어온 request 안의 body의 데이터들이 잘 parsing하고 정리할 수 있게 도와주는 도구
  • Error handlng middleware - app.use(err, req, res, next) : 예) Controller와 Controller 간 에러 이음새 역할, 손쉽게 처리, 다른 미들웨어들에 비해 매개변수의 수가 늘어날 수 있다.

cf) 해당 예시는 위코드 세션에 나온 자료보다 더 많은 예시가 기재되어있다. 철진님한테 물어보기. 위에 기재된 내용이 어떤 미들웨어인지 하나씩 살펴볼 것

//handles unauthorized erros 
app.use((err, req, res, next)) => {
if(err.httpStatusCode === 401){
res.status(401).render('Unauthorized');
}
next(err);
})
// handles not found errors 
app.use((err, req, res, next)) => {
if(err.httpStatusCode === 404) {
res.status(404).render('Not Found');
}
next(err);
});
//catch all
app.use((err, req, next)) => {
console.log(err);
if(!res.headersSent) { 
res.status(err.httpStatusCode || 500).render('UnknownError');
}
});

catch all
: 클라이언트나 사용자가 너무 깊게 알 필요는 없고
그냥 내가 의도한 에러메시지 'A'만 받아도 충분하다고 판단됐을 때 한곳에서 에러메시지를 한 곳에서 모으고 모아서 클라이언트 화면단에 보여주는 역할

3-2. 에러 핸들링에서 미들웨어의 중요성

  • 에러핸들링에서 미들웨어를 사용하지 않을 때
    👉 각 계층에서 각자의 에러를 처리하므로 유지보수가 어려워진다.
  • “에러를 던진다(throw)” : 예외 상황 발생 시, Error를 바로 response로 반환하지 않고 throw 한다.
  • “에러를 잡는다(catch)” : 던진 에러를 에러 처리 미들웨어 한군데에서 처리한다.
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)에 해당한다고 할 수 있다.

profile
주의사항 : 최대한 정확하게 작성하려고 하지만, 틀릴내용이 있을 수도 있으니 유의!

0개의 댓글