본문 바로가기
BackEnd

Error Handing에 대해서

by SoriKim 2023. 11. 19.
반응형

1. Error Handing의 목적 & 의의 

1️⃣ Error Handing 목적

소프트웨어는 의도에 맞게 잘 흘러가는 것이 좋겠지만, 항상 에러와 예외가 늘 발생하여 정상적인 사용이 막히게 됩니다. 

에러가 발생하는 이유는 실행 환경, 사용자의 특성, 개발자의 실수 등 여러 가지 원인이 있습니다. 

개발자는 사용자가 서비스를 이용하는 동안 발생할 수 있는 에러를 미리 사전에 예측하여, 이용자의 사용 흐름이 막히지 않도록 유도해야 합니다. 이런 과정을 Error Handing이라 합니다. 

 

그러나 에러 발생율이 0%인 서비스는 존재하지 않습니다. 따라서, 예외 상황 및 에러가 발생할 경우 소프트웨어를 갑자기 종료하는 것이 아닌 에러가 발생했음을 알리고, 곧 서비스가 정상적으로 구동될 것을 안내하는 것이 좋습니다. 

 

이런 에러 핸들링은 사용자로 하여금 서비스가 다시 안정화될 것이라는 믿음을 주고, 서비스 이용자의 이탈을 방지하기 위한 수단으로도 사용됩니다. 

 

2️⃣ Error Handing 의의 

에러 핸들링은 엄밀히 말해, Error Handing(에러 핸들링)과 Exception Handing(예외 핸들링) 두 가지 종료로 구분됩니다. 에러와 예외 상황은 발생 상황과 발생 주체가 다릅니다. 에러는 컴퓨터가 내지만, 예외는 개발자가 의도적으로 발생시키는 것입니다. 

 

컴퓨터가 내는 에러는 언어의 문법 에러, 통신 장애로 발생하는 에러 등 컴퓨터가 코드를 실행하는 과정 자체에서 발생하는 에러입니다.

 

consleo.log("Hello") // 함수 이름 오류

 

위와 같이 함수 이름에 대한 오류를 낼 경우 Uncaught ReferenceError: consleo is not defined라는 에러가 발생합니다. 이것은 개발자가 의도적으로 만든 에러가 아닌 실수로 오타를 내어 컴퓨터가 자바스크립트 문법을 실행하는 과정 자체에서 더 이상 진행할 수 없어 발생한 에러입니다. 

 

그럼 개발자가 의도적으로 발생시키는 예외상황은 언어가 실행되는 과정 자체에는 큰 문제가 없지만, 개발자와 개발팀이 판단할 때 정상적인 상황이 아닌 과정을 말합니다. 회원가입 상황에 아래와 같은 규칙이 있다고 가정하겠습니다.

1) 회원가입에 사용되는 이메일에는 @가 들어가야 한다. 

2) 비밀번호를 10자 이상 지정해야 한다. 

3) 회원가입에 사용되는 계정은 중복될 수 없다. 

 

이런 경우 유저가 비밀번호를 5자리만 설정하면 개발자의 의도와는 다른 방향으로 유저가 서비스를 이용하고자 하므로 예외상황에 해당합니다. 따라서, 개발자는 예상되는 예외 상황들을 나열해 잘못된 방법으로 서비스를 이용하는 것을 막아야 합니다. 위의 회원가입 규칙을 따라 아래와 같은 예외처리가 필요합니다. 

1) 회원가입 시 이메일에 @가 들어가지 않으면 에러 반환

2) 비밀번호를 10자 미만 입력 시 에러 반환

3) 이미 가입된 계정으로 가입 시도 시 에러 반환

 

이와 같이 예외 상황은 엄밀히 말해 컴퓨터 언어 입장에서는 에러가 아닙니다. 문법이 틀리지 않았고, 함수를 정상적으로 호출했기 때문입니다. 그러나 이런 예외상황은 함수 내부에 미리 if 문등으로 처리해 잘못된 방식으로 서비스를 이용하기 전 예외 상황을 미리 일으켜 에러를 발생시켜 클라이언트(프론트 엔드) 개발자에게 에러 상황 에러 코드로 알립니다. 프론트 엔드 개발자는 이런 에러를 반환받아, 사용자(고객)에게 적절한 안내 문구를 띄워줍니다. 

const { email, password } = req.body

if (!email.includes('@')) {
  return new Error('EMAIL_INVALID')
}

if (password.length < 10) {
  return new Error('PASSWORD_INVALID')
}
...

에러 발생 전 사전 처리, 발생한 에러를 잡아내고 처리하는 모든 과정을 에러 핸들링이라 일컫습니다. 

 

 

2.  throw와 try - catch

1️⃣ throw 

위 예시 코드에서 특정 예외 상황에 에러를 return 했는데, 이 방법은 에러를 발생시키는 일반적인 방법은 아닙니다. Javascript 뿐만 아니라 여러 프로그래밍 언어에서 예외 상황에 에러를 발생시키는 방법은 throw입니다. 아래와 같이 사용되는데, 어떤 예외 상황인지, 어떤 에러인지를 설명하기 위해 '메시지'부분이 필수로 들어가게 됩니다. 

throw new Error('EMAIL_INVALID')

throw new Error('message 부분')

 

예외 상황이 발생하면 진행하던 작업을 중단시키고 위 에러를 호출한 상위 모듈로 제어를 넘기게 됩니다. 즉, throw는 여기 이 지점에 특정 에러가 발생함을 알리고, 이 에러가 발생하면, 상위 모듈은 그에 맞는 액션을(코드) 취하게 됩니다. 

 

상위 모듈은 하위에 발생해 넘어오는 에러에 대해 대비할 수 있습니다. A라는 에러가 발생할 경우 B라는 처리를 해주고, C라는 에러가 발생할 경우 D라는 방법으로 처리한다는 형태입니다. 

해당 역할은 try-catch에서 수행하도록 설계되어 있습니다. 

 

2️⃣ try-catch

try-catch문은 잠재적 에러가 발생할 가능성이 있는 부분에 에러를 처리하기 위한 용도로 사용합니다. try 블록에는 에러가 발생할 가능성이 있는 코드를 넣고, catch 블록에는 해당 에러가 발생할 경우를 대비한 행동을 넣어둡니다. 

 

try 블록에 어떤 예외 처리를 위해 에러를 throw하면 발생 에러가 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.status(err.statusCode || 500).json({message: err.message})
}

 

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

1️⃣ 미들웨어란?

에러 핸들링은 반복적인 작업으로 서로 다른 기능이라도 같은 에러를 잡아야 할 수 있습니다. 

앞서 언급되었던 회원가입 시 두 가지 규칙은  

1) 회원가입에 사용되는 이메일에는 @가 들어가야 한다. 

2) 비밀번호를 10자 이상 지정해야 한다. 

회원가입 기능에 포함된 동시, 로그인 기능에도 포함되는 규칙입니다. 같은 규칙을 서로 다른 두 기능에 중복으로 기입하는 경우 코드도 중복으로 계속 기입해야 합니다. 이런 공통된 작업은 모듈화 하는 용도로 사용되는 개념 가운데 하나가 미들웨어입니다. 

 

Express의 미들웨어는 컨트롤러(controller)와 컨트롤러를 이어주는 또 다른 컨트롤러의 한 종류입니다. 즉, 컨트롤러 사이에 위치해 컨트롤러 진입 전 공통적으로 해야하는 작업들을 처리하는 기능을 합니다. 중복된 기능이라 해서 전부 미들웨어로 생성하는 것은 아닙니다. 대표적으로 사용하는 미들웨어는 주로 CORS 정책을 설정하는 미들웨어나 서버에서 일어나는 상황을 기록(로그, log)하는 logging 미들웨어입니다. 에러 핸들링 또한 미들웨어로 사용하는 것을 추천하며 사용 시 여러 경우에 발생하는 에러 핸들링을 하나로 모을 수 있는 장점이 있습니다. 

 

2️⃣ 에러 핸들링에서 미들웨어의 중요성 

각 계층에서 각자 에러를 처리하게 되면 에러 처리를 하는 계층이 너무 많아지게 되어 유지보수가 어려워집니다. 

따라서, 에러는 한 군데 모아 처리하는 것이 좋습니다. 예외 상황이 발생 시 Error를 바로 response로 반환하지 않고 throw합니다. "에러를 던진다(throw)"라고 표현합니다." 던진 에러는 에러 처리 미들웨어 한 군데에서 "잡아서(catch)" 처리합니다. 

const { email, password } = req.body

if (!email.includes('@')) {
  throw new Error('EMAIL_INVALID')
}

if (password.length < 10) {
  throw new Error('PASSWORD_INVALID')
}

 

에러를 한 곳으로 모으는 역할을 미들웨어가 수행하면서 동시 로그 기능등을 붙이게 된다면, 에러 핸들링을 하면서 어떤 에러가 발생했었는지 기록도 할 수 있어 에러 핸들링 보다 다채롭게 할 수 있습니다. 에러 처리하는 기능을 미들웨어로 모듈화 하는 것 또한 특정 기능을 분리하는 것이므로 관심사의 분리(Seperation Of Concern | SOC)에 해당한다고 할 수 있습니다. 

 

반응형

'BackEnd' 카테고리의 다른 글

각 Layer 별 Error Handling에 대해  (0) 2023.11.23
Error Handing 적용하기  (0) 2023.11.19
[API] Middleware에 대해서  (0) 2023.11.15
[API] Path Parameter & Query Parameter에 대해서  (0) 2023.11.15
[API] RESTful API란?  (0) 2023.11.15

댓글