정말 개발 공부의 끝은 없는 것 같다.
예전에 퍼블리셔로 회사를 다녔을 때, 에러 처리를 어떻게 해결했었나 되돌아보면, 에러가 어디서 발생했는지 알 수 없어 코드의 여기저기를 뒤지며 수정했던 기억이 난다.
때로는 코드를 vs code에 깃렌즈 익스텐션을 깔아가며, 코드를 작성한 사람을 찾아가 직접 물어보기도 했던 경험도 있었다..!
프론트를 공부하면서 400, 403, 404, 500 같은 HTTP 오류 코드가 점점 익숙해지던 중, 커스텀 에러를 활용하면 더욱 체계적으로 다양한 에러를 관리할 수 있다는 사실을 알게 되었다. 그래서 이 글을 한번 써보고자 한다.✍️
프로그래밍에서 커스텀 에러(Custom Error)란 기본 제공되는 Error 객체를 확장하여 특정한 에러 상황에 맞게 설계된 에러 클래스다.
기본 Error 객체는 단순히 메시지와 스택 정보를 제공하지만, 구체적인 상황을 표현하기 어렵다.
커스텀 에러는 에러의 종류를 명확히 구분하고, 상황에 맞는 메시지를 전달하며, 디버깅과 유지보수성을 높여준다.
간단하게 기본 에러 객체와 커스텀 에러를 간단하게 아래와 같이 확인해볼 수 있다.
여기서 에러 메시지는 "에러가 발생했습니다"로 모호하다.
이 에러가 API 키 누락 때문인지, 네트워크 오류 때문인지 명확히 구분할 방법이 없다.
function fetchData(apiKey) {
if (!apiKey) {
throw new Error('에러가 발생했습니다.');
}
}
try {
fetchData(null);
} catch (error) {
console.error(error.message); // "에러가 발생했습니다."
}
name 속성으로 에러의 종류를 명확히 구분할 수 있다.
에러 메시지는 상황에 맞게 작성되어 디버깅과 유지보수가 훨씬 쉬워진다.
class ApiKeyMissingError extends Error {
constructor() {
super('API 키가 설정되지 않았습니다.');
this.name = 'ApiKeyMissingError';
}
}
function fetchData(apiKey) {
if (!apiKey) {
throw new ApiKeyMissingError();
}
}
try {
fetchData(null);
} catch (error) {
console.error(error.name); // "ApiKeyMissingError"
console.error(error.message); // "API 키가 설정되지 않았습니다."
}
커스텀 에러를 사용하면 좋은 이유를 먼저, 더 자세하게 알아보자면 아래와 같이 이야기할 수 있다.
에러 상황의 명확한 표현
단순히 "에러가 발생했습니다" 같은 메시지가 아니라, 어떤 상황에서 에러가 발생했는지 구체적으로 설명할 수 있다.
코드 가독성 및 유지보수성 향상
커스텀 에러를 사용하면 에러가 발생한 맥락과 원인을 한눈에 파악할 수 있다. 이는 디버깅과 유지보수에서 큰 이점을 제공한다.
에러 처리의 일관성 제공
에러를 일관되게 정의하고 처리하면 코드의 통일성과 안정성이 높아진다. 특히, 팀 단위로 작업할 때 커스텀 에러는 통일된 에러 핸들링 방식을 제공한다.
재사용 가능한 에러 정의
커스텀 에러 클래스는 재사용 가능하기 때문에, 프로젝트 전반에서 동일한 유형의 에러를 일관되게 관리할 수 있다.
커스텀 에러는 기본 Error 객체를 상속받아 작성한다. 이렇게 하면 기본 Error 객체의 기능을 활용하면서도 에러의 종류를 구체적으로 정의할 수 있다.
아래는 커스텀 에러 클래스의 기본 구조다.
class CustomError extends Error {
constructor(message: string) {
super(message); // 부모 클래스의 message 설정
this.name = 'CustomError'; // 에러의 이름 설정
}
}
예제를 통해 특정한 상황에 맞는 커스텀 에러를 작성하는 방법을 알아보자!☺️
아래는 내가 실제로 개인 프로젝트를 진행하면서 썼던 코드들이다.
export class ApiKeyMissingError extends Error {
constructor(message = 'API 키가 설정되지 않았습니다.') {
super(message);
this.name = 'ApiKeyMissingError';
}
}
환경 변수나 설정 파일에 API 키가 누락된 경우를 처리하기 위해 사용된다.
외부 API 호출에 필요한 키가 설정되지 않은 경우, 이 에러를 발생시켜 문제를 알릴 수 있다.
export class ApiRequestError extends Error {
constructor(message = 'API 요청에 실패했습니다.') {
super(message);
this.name = 'ApiRequestError';
}
}
외부 API에서 오류가 발생하거나 응답 상태 코드가 200이 아닌 경우 이 에러를 사용한다.
예를 들어, 서버 에러(500)나 잘못된 요청(400)을 처리할 때 유용하다.
export class ApiResponseParsingError extends Error {
constructor(message = 'API 응답을 파싱하는 중 오류가 발생했습니다.') {
super(message);
this.name = 'ApiResponseParsingError';
}
}
API에서 받은 XML 데이터를 JSON으로 변환하는 과정에서 오류가 발생했을 때 사용한다.
예를 들어, API 응답 데이터가 형식에 맞지 않거나 파싱 로직에 문제가 있을 경우 이 에러를 발생시킨다.
에러 메시지를 상수화하면 코드의 재사용성과 가독성이 높아진다. 아래는 에러 메시지를 별도의 상수 파일에서 관리하는 예시다.
export const SEARCH_ERROR_MESSAGES = {
API_KEY_MISSING: 'API 키가 설정되지 않았습니다.',
API_REQUEST_ERROR: 'API 요청에 실패했습니다.',
API_RESPONSE_PARSING_ERROR: 'API 응답을 파싱하는 중 오류가 발생했습니다.',
};
이렇게 하면 에러 메시지를 수정하거나 다국어를 지원할 때 매우 편리하다.
커스텀 에러를 처리할 때는 에러 타입별로 로직을 분리할 수 있다. 아래는 handleError 함수의 예시다. 메세지는 위에 상수화했던 코드를 가져와서 변경해줄 수 있다.
function handleError(error: unknown) {
if (error instanceof ApiKeyMissingError) {
return { message: SEARCH_ERROR_MESSAGES.API_KEY_MISSING, status: 500 };
}
if (error instanceof ApiRequestError) {
return { message: SEARCH_ERROR_MESSAGES.API_REQUEST_ERROR, status: 502 };
}
if (error instanceof ApiResponseParsingError) {
return { message: SEARCH_ERROR_MESSAGES.API_RESPONSE_PARSING_ERROR, status: 500 };
}
// 기타 에러 처리
return { message: '알 수 없는 오류가 발생했습니다.', status: 500 };
}
export const ERROR_MESSAGES = {
API_KEY_MISSING: 'API 키가 설정되지 않았습니다.',
API_REQUEST_ERROR: 'API 요청에 실패했습니다.',
API_RESPONSE_PARSING_ERROR: 'API 응답을 파싱하는 중 오류가 발생했습니다.',
};
class ApiRequestError extends Error {
statusCode: number;
constructor(message: string, statusCode: number) {
super(message); // 스택 추적 활성화
this.name = 'ApiRequestError';
this.statusCode = statusCode; // 추가 정보 포함
}
}
커스텀 에러는 복잡한 애플리케이션의 에러 처리를 체계적이고 명확하게 만들어주는 강력한 도구다. 프로젝트에서 발생할 수 있는 주요 에러 상황을 미리 정의하고, 이를 일관성 있게 처리하면 코드의 품질과 유지보수성이 크게 향상된다.
공부를 하면 할수록 느끼는 것은, 코드의 품질을 높이는 방법뿐만 아니라 팀원들과의 협업을 위해 내 코드가 남들에게도 이해하기 쉽게 작성되는 방법을 배워간다는 점이다. 내가 없어도 동료들이 내 코드를 쉽게 파악하고 유지보수할 수 있는 구조를 만드는 것이 개발자로서 성장의 중요한 부분임을 깨닫는다.
남들에게도 편하지만 내가 휴가를 가더라도 불안하지 않고 당당하게 다녀올 수 있는 계기가 되는 것 같기도 하다.😊