오늘은 풀스택 개발자라면 반드시 알아야 할 **에러 핸들링(Error Handling)**에 대해 이야기해볼게. 15년차 개발자로 일하면서 수많은 프로젝트를 거쳤지만, 에러 핸들링은 아무리 강조해도 지나치지 않는 부분이거든. 단순히 try-catch 몇 줄 넣는 걸 넘어서, 서비스의 안정성과 사용자 경험을 좌우하는 핵심 요소라고 생각하면 돼.
1. 왜 에러 핸들링이 중요할까?
에러 핸들링은 개발 과정에서 귀찮게 느껴질 수도 있지만, 실제 서비스 운영에서는 생명줄과 같아.
💡 에러 핸들링의 중요성
- 사용자 경험 보호: 예측 불가능한 에러로 서비스가 멈추거나 오작동하면 사용자는 바로 이탈하더라. 에러를 우아하게 처리하면 사용자는 "아, 뭔가 문제가 생겼구나" 하고 이해하고 기다릴 수 있어.
- 문제 진단 및 해결 용이성: 에러가 발생했을 때 충분한 정보가 없으면 원인을 찾느라 밤새는 경우가 허다해. 제대로 된 에러 핸들링은 문제 발생 시점, 원인, 관련 데이터 등을 파악하는 데 결정적인 단서를 제공해 주거든.
- 서비스 안정성 확보: 작은 에러 하나가 전체 시스템에 치명적인 영향을 줄 수도 있어. 에러를 적절히 격리하고 처리함으로써 전체 서비스의 안정성을 유지할 수 있어. 많은 주니어 개발자들이 '일단 돌아가게 만들자!'는 생각으로 에러 핸들링을 후순위로 미루는 경향이 있는데, 나중에 시스템이 복잡해지고 트래픽이 늘어나면 반드시 부메랑처럼 돌아오게 되어 있어.
2. 프론트엔드 에러 핸들링: 사용자 경험 지키기
프론트엔드에서는 사용자가 직접 마주하는 부분이라 에러 핸들링이 더욱 중요해. 백엔드에서 에러가 나도 프론트엔드에서 잘 처리하면 사용자에게는 '잠시 문제가 생겼다' 정도로 보일 수 있거든.
- UI 에러 처리:
- React에서는
Error Boundary를 활용하면 특정 컴포넌트 내부에서 발생하는 렌더링 에러를 잡아낼 수 있어. 덕분에 에러가 발생한 컴포넌트만 망가지고 나머지 UI는 정상적으로 작동하게 만들 수 있지. - Vue나 Angular 같은 다른 프레임워크에서도 유사한 메커니즘이 있거나,
try-catch블록을 활용해서 특정 로직의 에러를 처리해 볼 수 있어. window.onerror나window.onunhandledrejection같은 전역 이벤트 핸들러를 사용해서 예상치 못한 에러를 잡는 것도 좋은 방법이야.
- React에서는
- API 통신 에러:
- 백엔드와의 통신에서 발생하는 에러는 흔하잖아?
fetchAPI를 쓸 때는.catch()체인을 활용하고,axios같은 라이브러리를 쓴다면 **인터셉터(Interceptor)**를 활용하는 게 좋아. - API 요청 전/후, 응답 전/후에 공통적인 에러 처리 로직을 넣어서 **4xx (클라이언트 에러)**나 5xx (서버 에러) 같은 HTTP 상태 코드를 기반으로 사용자에게 적절한 메시지를 보여주거나, 재시도 로직을 구현해 볼 수 있지. 예를 들어, 토큰 만료 시 자동으로 리프레시 토큰을 이용해 재요청하는 로직 같은 것 말이야.
- 백엔드와의 통신에서 발생하는 에러는 흔하잖아?
3. 백엔드 에러 핸들링: 견고한 시스템 만들기
백엔드는 서비스의 핵심 로직과 데이터를 다루는 곳이라, 에러 핸들링이 시스템의 안정성과 데이터 무결성에 직결돼.
- 전역 에러 핸들링 미들웨어/필터:
- Node.js의 Express 프레임워크에서는 에러 핸들링 미들웨어를 사용해서 모든 라우트에서 발생하는 에러를 한곳에서 처리할 수 있어.
app.use((err, req, res, next) => { ... })이런 식으로 말이야. - Spring Boot에서는
@ControllerAdvice와@ExceptionHandler를 활용해서 특정 예외 타입에 대한 응답을 일관되게 처리할 수 있지. - 이렇게 전역으로 에러를 처리하면 코드 중복을 줄이고, 모든 에러를 일관된 형식으로 응답할 수 있어서 클라이언트 개발자도 편해져.
- Node.js의 Express 프레임워크에서는 에러 핸들링 미들웨어를 사용해서 모든 라우트에서 발생하는 에러를 한곳에서 처리할 수 있어.
- 데이터베이스 에러:
- 데이터베이스 작업 중 발생하는 에러는 특히 중요해. 트랜잭션 도중 에러가 발생하면 롤백(Rollback) 처리를 해서 데이터의 일관성을 유지해야 하거든.
- 예를 들어, 계좌 이체 중 한쪽은 돈이 빠져나가고 다른 한쪽은 돈이 들어오지 않는다면 큰일 나잖아? 이런 상황을 방지하기 위해
try-catch와 함께 트랜잭션 관리를 철저히 해야 해.
- 외부 서비스 연동 에러:
- 결제 시스템, 외부 API 등 다른 서비스와 연동할 때 발생하는 에러는 우리가 통제하기 어려운 경우가 많아. 이런 경우를 대비해 재시도(Retry) 로직이나 서킷 브레이커(Circuit Breaker) 패턴을 고려해 볼 수 있어.
- 무작정 재시도하는 것보다는 지수 백오프(Exponential Backoff) 같은 전략을 사용해서 부하를 줄이는 것도 중요해.
4. 에러 로깅과 모니터링: 에러를 알아야 고친다!
에러 핸들링만 잘한다고 끝이 아니야. 에러가 발생했을 때 어떤 에러가 언제, 어디서, 어떤 상황에서 발생했는지 알아야 고칠 수 있거든.
console.error는 개발 단계에서나 유용하지만, 실제 서비스에서는 절대적으로 부족해. 서버가 꺼지거나 로그가 너무 많아지면 놓치기 쉽거든.- 전문 로깅/모니터링 툴을 사용하는 게 현명해. 내가 15년차 개발자로 일하면서 가장 많이 사용하고 추천하는 건 Sentry 같은 에러 트래킹 툴이야. 프론트엔드, 백엔드 가리지 않고 통합적으로 에러를 수집하고, 발생 빈도, 영향도, 관련 사용자 정보까지 한눈에 보여줘서 문제 해결에 엄청난 도움을 주더라.
- 그 외에도 **ELK Stack (Elasticsearch, Logstash, Kibana)**이나 Grafana + Loki 같은 툴을 활용해서 로그를 중앙 집중화하고 시각화해서 모니터링하는 것도 아주 중요해.
- 로그를 남길 때는 단순히 에러 메시지만 남기지 말고, 요청 정보 (IP, 사용자 ID), 파라미터, 스택 트레이스 등 문제 해결에 도움이 될 만한 맥락(Context) 정보를 최대한 많이 포함시키는 습관을 들이는 게 좋아.
💡 핵심 정리
- 에러 핸들링은 사용자 경험 보호, 문제 진단, 서비스 안정성 확보를 위한 필수 과정이야.
- 프론트엔드는
Error Boundary, API 인터셉터 등으로 사용자에게 친화적인 에러 처리를 해봐.- 백엔드는 전역 에러 미들웨어, DB 트랜잭션 롤백, 외부 서비스 연동 시 재시도/서킷 브레이커를 고려해 봐.
Sentry같은 전문 툴로 에러를 로깅하고 모니터링하며, 문제 해결에 필요한 맥락 정보를 충분히 기록하는 것이 중요해. 에러 핸들링은 개발자로서 서비스를 책임지는 자세를 보여주는 중요한 지표이기도 해. 처음부터 완벽하게 하긴 어렵겠지만, 작은 프로젝트부터 꾸준히 연습하고 고민해본다면 훨씬 더 견고하고 신뢰할 수 있는 서비스를 만들어낼 수 있을 거야. 힘내서 멋진 풀스택 개발자로 성장하길 응원할게!