프로그래밍 입문자에게 화면을 붉게 물들이는 '에러(Error) 메시지'는 두려움의 대상입니다. 보통 코드가 잘못되었거나 프로그램이 망가졌다는 '실패'의 의미로 받아들이기 때문입니다. 하지만 실무적인 관점, 나아가 엔터프라이즈급 소프트웨어 아키텍처의 관점에서 에러는 예상 가능한 상황에서 발생하는 정상적인 흐름 중 하나일 뿐입니다.
세계적인 IT 기업 구글(Google)의 SRE(사이트 신뢰성 엔지니어링) 원칙에는 "희망은 전략이 될 수 없다(Hope is not a strategy)"라는 말이 있습니다. 시스템이 아무런 문제 없이 100% 정상 작동하기만을 바라는 것은 불가능에 가깝습니다. 오늘 포스팅에서는 에러가 왜 제거 대상이 아닌 처리 대상인지, 그리고 올바른 예외 처리(Exception Handling)를 통해 어떻게 견고한 프로그램 안정성 구조를 설계할 수 있는지 깊이 있게 알아보겠습니다.
--------------------------------------------------------------------------------
1. 에러가 발생하는 이유: 현실의 불확실성과 결함
프로그램은 통제된 진공 상태에서 돌아가지 않습니다. 사용자 입력, 네트워크 상태 변화, 파일 시스템, 데이터베이스 상태 등 개발자가 100% 통제할 수 없는 수많은 외부 요인과 끊임없이 상호작용합니다. 이러한 불확실한 외부 환경 속에서 에러는 필연적으로 발생합니다. 비가 오는 날을 생각해 봅시다. 비가 내리는 현실 자체는 문제가 아닙니다. 진짜 문제는 "우산 없이 밖으로 나가는 것"입니다.
이때 우리는 '에러(Error)'와 '결함(Defect/Fault)'을 구분해야 합니다. 사용자가 비밀번호를 짧게 입력했을 때 시스템이 이를 감지하고 재입력을 요청하는 것은 시스템이 자체적으로 회복 가능한 '에러'입니다. 반면, 데이터베이스 연결이 끊어지는 등 외부 요인이나 구조적 문제로 인해 시스템이 자체 복구할 수 없는 고장은 '결함' 또는 '장애(Failure)'로 봅니다. 우리는 회복 가능한 에러를 적절히 핸들링하여 시스템 장애로 이어지는 것을 막아야 합니다.
--------------------------------------------------------------------------------
2. 예외(Exception)의 본질: 흐름을 바꾸는 신호
에러 상황에서 발생하는 '예외(Exception)'는 단순한 오류 메시지가 아닙니다. 예외의 진짜 본질은 프로그램의 실행 흐름을 바꾸는 강력한 제어 장치입니다.
정상적인 흐름을 타던 프로그램에 문제가 발생하면, 예외는 즉각적으로 정상 흐름을 중단시키고 개발자가 미리 준비해 둔 '예외 처리 경로'로 이동하게 만듭니다. 이는 도로 위에서 공사 구간(🚧)을 만났을 때 직진을 멈추고 우회 경로로 돌아가는 것과 완벽히 같은 이치입니다.
결국 예외 처리의 핵심은 프로그램을 강제로 멈추지 않게 하고, 피해의 영향 범위를 제한하는 '응급 처치(🏥)'입니다. 문제를 완전히 없애는 마법이 아니라, 정상 상태로 복구하여 사용자 경험을 유지하기 위한 전략인 것입니다.
--------------------------------------------------------------------------------
3. 에러를 다루는 두 가지 방식과 복원력 패턴
소프트웨어에서 에러를 다루는 방식은 크게 예방과 대응으로 나뉩니다.
- 에러를 막으려는 방식 (예방) : 입력값을 사전에 검증하여 에러 발생 자체를 차단합니다. 이는 사고를 막기 위한 **신호등(🚦)**과 같습니다.
- 에러를 받아들이는 방식 (대응) : 실패를 전제로 시스템을 설계하고 try-catch 구문 등을 사용해 수습합니다. 이는 사고 후 신속히 출동하는 구급차(🚑)의 역할입니다.
현대 마이크로서비스 아키텍처(MSA)에서는 이러한 대응 방식을 극대화하여 서킷 브레이커(Circuit Breaker)나 폴백(Fallback) 같은 복원력 패턴을 사용합니다. 원격 서비스 호출이 실패할 때 연쇄 장애(Cascading Failure)가 전체 시스템으로 번지는 것을 차단하고(서킷 브레이커), 에러 화면 대신 캐시된 데이터나 기본값을 반환하여 사용자 경험을 지키는 것(폴백)이 대표적인 예입니다.
--------------------------------------------------------------------------------
4. 좋은 프로그램은 실패를 예상한다: 안정성 설계의 핵심 원칙
잘 설계된 프로그램이란 에러가 발생할 상황을 미리 고려하고, 이를 어떻게 처리할지 명확히 결정해 둔 프로그램입니다. 이와 관련된 중요한 소프트웨어 설계 개념들이 있습니다.
- Fail-Safe (페일 세이프) & Graceful Degradation (우아한 성능 저하) : 시스템에 장애가 발생하더라도 전체가 무너지지 않고 안전한 모드로 전환되거나(Fail-Safe), 일부 기능이 제한되더라도 핵심 기능은 유지하는 기법(Graceful Degradation)입니다. 이는 비행기(✈) 운항 중 엔진 하나에 문제가 생겨도 즉시 추락하지 않고 보조 시스템으로 착륙하는 것과 같습니다.
- 에러 버젯(Error Budget) : 구글 SRE 팀은 시스템의 가용성 목표를 100%로 잡는 것은 잘못되었다고 말합니다. 100%와 99.999%의 차이를 사용자는 체감하지 못하기 때문입니다. 약간의 다운타임(에러 버젯)을 허용하고, 이를 혁신과 빠른 배포를 위한 기회로 삼아야 합니다.
- 카오스 엔지니어링(Chaos Engineering) : 넷플릭스(Netflix)는 아예 프로덕션 환경에 의도적으로 에러와 장애를 주입하는 '카오스 몽키(Chaos Monkey)'를 도입했습니다. 실패가 불가피하다면, 지속적으로 에러를 경험하고 자동 복구되도록 훈련시켜 시스템의 면역력을 키우는 것입니다. 나아가 에러와 무질서 속에서 오히려 더 강해지는 안티프래질(Antifragile) 아키텍처를 지향하게 됩니다.
--------------------------------------------------------------------------------
5. 초보 개발자의 오해를 깨고 더 나은 설계자로 성장하기
입문자들은 종종 다음과 같은 치명적인 오해를 합니다.
- "에러는 코드에 반드시 없어야만 하는 존재다."
- "try-catch는 에러와 문제를 덮어두고 숨기는 기능이다."
- "프로그램은 어떠한 상황에서도 100% 정상 동작해야 한다."
하지만 에러를 다루는 올바른 철학을 이해하면 개발자로서의 시야가 완전히 달라집니다. 에러를 없애는 데 집착하는 것이 아니라, 버그와 실패를 두려워하지 않고 프로그램을 훨씬 더 안정적으로 설계할 수 있는 능력이 생깁니다. 완벽하고 흠결 없는 시스템을 만들려는 환상에서 벗어나, 충격(에러)이 발생해도 이를 부드럽게 흡수하는 '견딜 수 있는 시스템(Resilient System)'으로 사고를 전환하게 되는 것입니다.
--------------------------------------------------------------------------------
🎯 마지막 요약 정리
"에러는 제거해야 할 실패가 아니라, 프로그램이 예상하고 처리해야 하는 하나의 정상적인 흐름입니다."
여러분의 프로젝트를 되돌아보세요. 내리는 비를 피할 우산을 코드 곳곳에 잘 준비해 두었나요? 발생할 수 있는 에러 상황을 아키텍처 설계의 일부분으로 당당히 받아들일 때, 비로소 견고하고 신뢰받는 안정적인 소프트웨어를 완성할 수 있을 것입니다.

반응형