Go가 오류 처리를 단순화하지 않는 이유
Lukas Schneider
DevOps Engineer · Leapcell

서문
“Go의 오류 처리는 작성하기에 너무 장황합니다.” — 이는 거의 모든 Go 프로그래머가 동의하는 감정입니다.
최근 Go 팀은 공식 블로그 게시물을 통해 오류 처리 구문에 대한 새로운 제안을 더 이상 추진하지 않겠다고 공식 발표했습니다. 즉, 앞으로 Go 코드를 작성할 때 익숙한 if err != nil { return err }
줄을 자주 입력하게 될 것입니다.
이는 단순히 구문 설탕 제안의 끝이 아니라 언어 전체의 철학에 대한 재확인입니다. 그렇다면 Go 팀은 왜 이런 결정을 내렸을까요? 그리고 우리는 그들의 끈기를 어떻게 해석해야 할까요?
세 번의 시도, 세 번의 실패: Go의 오류 처리 구문의 여정
지난 7년 동안 Go 팀은 새로운 구문 메커니즘을 도입하여 "반복적인 오류 처리" 문제를 해결하기 위해 세 번 시도했습니다. 이러한 노력 중 어느 것도 채택되지 못했습니다.
첫 번째 시도: 2018 check
/ handle
제안
이 제안은 Go 2 초안에서 비롯되었습니다. 이는 다음을 도입하기 위한 완전한 구문 변경이었습니다.
- 함수 호출에서
error
를 캡처하는check()
- 중앙 집중식 오류 처리를 위한
handle
블록
func printSum(a, b string) error { handle err { return err } // 모든 check 실패는 여기로 이동합니다. x := check(strconv.Atoi(a)) y := check(strconv.Atoi(b)) fmt.Println("result:", x + y) return nil }
- 🔍 장점: 명확한 구조, 통합된 오류 경로 관리.
- ⚠️ 단점: 새로운 구문 블록을 도입하여 학습 곡선을 가파르게 하고, 의미론적 복잡성을 증가시키며, 광범위한 논쟁을 불러일으켰습니다.
결국 Go 팀은 이 메커니즘이 _너무 복잡하다_고 결론지었고 초안 단계를 넘어서지 못했습니다.
두 번째 시도: 2019 try()
제안
첫 번째 시도에서 배운 Go 팀은 더 가벼운 버전인 try()
함수를 제안했습니다.
func printSum(a, b string) error { x := try(strconv.Atoi(a)) y := try(strconv.Atoi(b)) fmt.Println("result:", x + y) return nil }
이는 기본적으로 다음과 같습니다.
x, err := strconv.Atoi(a) if err != nil { return err }
- 🔍 장점: 간단하고 명확하며, 새로운 구문 블록을 도입하지 않고 내장 함수만 도입합니다.
- ⚠️ 단점: 자동
return
은 제어 흐름을 가리고, Go의 오랜 강조점인 "명시적인 가독성"을 위반합니다. 디버깅을 더 어렵게 만들고 인지 부하를 증가시킵니다.
결국 이 제안은 커뮤니티의 강력한 반대로 인해 공식적으로 폐기되었습니다.
세 번째 시도: 2024 ?
연산자 제안
이번에는 Go 팀이 더 현실적인 디자인에 기울었습니다. Rust의 ?
에서 영감을 받아 오류 처리를 위한 후위 구문 설탕을 제안했습니다.
func printSum(a, b string) error { x := strconv.Atoi(a) ? y := strconv.Atoi(b) ? fmt.Println("result:", x + y) return nil }
불행히도 이전 제안과 마찬가지로 이 제안도 다양한 비판과 개인 선호도 기반의 수정으로 인해 빠르게 묻혔습니다.
최종 결정: 더 이상 구문 수준의 변경은 없습니다.
세 번의 노력 모두 광범위한 합의에 도달하지 못했습니다. 2025년 6월 Go 팀은 블로그에서 공식적으로 발표했습니다.
“앞으로 Go 팀은 오류 처리를 위한 구문 언어 변경을 더 이상 추진하지 않을 것입니다. 또한 오류 처리 구문에 주로 관여하는 모든 열려 있는 제안과 들어오는 제안은 더 이상 조사하지 않고 종료할 것입니다.”
이 결정은 기술적인 해결책이 부족해서가 아니라 합의 격차, 비용 편익 분석, 언어 철학의 조합 때문이었습니다.
이는 실용적인 폐쇄일 뿐만 아니라 Go의 설계 원칙에 대한 재확인입니다.
Go 팀이 왜 자신의 신념을 고수했을까요? 두 가지 핵심 이유 설명
Go 팀은 오류 처리가 "작성하기에 반복적"이라는 사실을 모르지 않으며, 더 간결한 구문을 찾을 수 없었던 것도 아닙니다. 지난 7년 동안 그들은 개인적으로 세 가지 제안을 추진했지만 각 제안을 철회했습니다. 이는 기술적인 한계에 관한 것이 아니라 가치 판단에 관한 것입니다.
Go 팀이 궁극적으로 "변경 없음"을 선택한 이유를 두 가지 관점에서 이해할 수 있습니다.
1. "압도적인 합의"에 도달하지 못했습니다.
Go 팀은 반복적으로 강조했습니다. "우리는 단지 작동하는 솔루션이 아니라 널리 받아들여지고 사용되는 솔루션을 찾고 있습니다."
그러나 현실은 모든 제안이 많은 "이것은 내가 원하는 것이 아니다"라는 반응을 불러일으켰습니다.
- 일부는
check/handle
접근 방식이 너무 복잡하다고 생각했습니다. - 다른 사람들은
try()
함수가 너무 자동적이라고 느꼈습니다. - 일부는
?
연산자가 직관적이지만 의미론적으로 충분히 명확하지 않다고 느꼈습니다.
모든 구문 설탕은 스타일 충돌, 철학적 차이, 끝없는 자전거 창고 짓기(무의미한 논쟁)에 직면했습니다. Go 팀은 분명히 지적했습니다.
“지금까지 본 최고의 제안조차 압도적인 지지를 얻지 못했습니다.”
Go의 설계 철학은 항상 매우 실용적이었습니다. 합의가 없으면 변화도 없습니다.
2. 기술적 이점과 비용이 합산되지 않습니다.
모든 제안 뒤에서 Go 팀은 프로토타입 도구 체인(컴파일러, 설명서 및 기타 도구 포함)을 구축했습니다. 그러나 그들은 다음과 같은 사실을 발견했습니다.
- 구문이 더 간결해 보이지만
- 코드는 몇 줄을 절약할 수 있지만
- 읽고 이해하는 데 드는 비용은 비례적으로 감소하지 않았습니다.
예를 들어 다음과 같습니다.
x := strconv.Atoi(a)?
이 줄은 실제로 if
문을 생략하지만 프로그램의 제어 흐름은 덜 명시적이 됩니다. 오류가 어떻게 반환되는지 또는 누가 처리하는지 더 이상 명확하지 않습니다. 이것은 언어 논리의 숨겨진 부분이 됩니다. Go 팀은 걱정합니다.
“언어 수준에서 더 많은 마법을 도입할수록 사용자가 문제를 디버깅하고 읽고 추적하는 데 드는 비용이 높아집니다.”
그들의 관점에서 Go의 장점은 결코 가장 적은 코드를 작성하는 것이 아니라 이해하기 쉽고, 디버깅하기 쉬우며, 안정적으로 실행되는 코드를 작성하는 것입니다.
향후 방향: 구문 변경은 없지만 경험은 개선할 수 있습니다.
Go 팀은 오류 처리에 대한 구문 수준의 변경을 더 이상 추진하지 않겠다고 분명히 밝혔지만, 이는 오류 처리 자체가 "잠겨 있다"는 의미는 아닙니다. 오히려 개발자 경험을 개선할 여지가 여전히 많습니다. 블로그 게시물에서는 향후 작업에 대한 몇 가지 중요한 방향을 언급했습니다.
1. 라이브러리 함수로 중복 코드 줄이기
Go 팀은 오류 처리와 관련된 반복을 줄이기 위해 표준 라이브러리를 개선하는 것을 명시적으로 지원했습니다. 예를 들어 다음과 같습니다.
x, err1 := strconv.Atoi(a) y, err2 := strconv.Atoi(b) if err := cmp.Or(err1, err2); err != nil { return err }
이 예에서는 cmp.Or
을 사용하여 여러 오류 처리를 통합하여 반복적인 검사를 줄입니다. 이 접근 방식은 Go의 구문 일관성을 유지하면서 가독성을 향상시킵니다.
2. IDE 및 도구 체인 지원 강화
블로그에서 제시된 매우 실용적인 방향은 IDE가 "중복을 숨기는" 데 더 똑똑해지도록 하는 것입니다.
최신 IDE(특히 LLM 기반의 지능형 자동 완성 기능과 함께 사용되는 경우)는 if err != nil
과 같은 반복적인 코드 작성을 크게 단순화할 수 있습니다. 향후 가능성은 다음과 같습니다.
- IDE에 "오류 처리 문 숨기기" 토글 추가
- 필요한 경우에만
if err != nil
블록을 확장하여 읽기 흐름 개선 - AI가 더 상황에 맞는 오류 메시지를 생성하도록 허용
이 접근 방식은 언어 자체를 변경하지 않지만 Go 코드 작성 및 읽기 효율성을 실제로 개선할 수 있습니다.
3. 오류 처리 자체에 집중
오류 처리의 핵심으로 돌아가서 오류 반환이 아닌 오류 처리 과정에 집중하면 장황한 코드가 문제가 되지 않습니다. 좋은 오류 처리는 종종 오류에 추가 정보를 추가해야 합니다. 예를 들어 사용자 설문 조사에서 반복적으로 나오는 의견은 오류와 관련된 스택 추적 정보가 부족하다는 것입니다. 이는 다음과 같이 향상된 오류 지원 함수를 생성하고 반환하여 해결할 수 있습니다.
func printSum(a, b string) error { x, err := strconv.Atoi(a) if err != nil { return fmt.Errorf("invalid integer: %q", a) } y, err := strconv.Atoi(b) if err != nil { return fmt.Errorf("invalid integer: %q", b) } fmt.Println("result:", x + y) return nil }
이 접근 방식은 명확성을 더할 뿐만 아니라 개발자가 오류의 원인을 더 잘 이해하도록 도와주어 유지 관리 및 디버깅 가능한 코드를 만들 수 있습니다.
결론
Go 팀은 오류 처리에 대한 구문 수준의 변경을 더 이상 추진하지 않겠다고 분명히 밝혔지만, 이는 오류 처리 최적화가 종료되었다는 의미는 아닙니다. 표준 라이브러리를 개선하고, 도구 체인을 개선하고, 오류 처리 주변의 컨텍스트에 더 집중함으로써 개발자는 언어의 일관성을 유지하면서 Go 코드의 가독성과 효율성을 여전히 개선할 수 있습니다.
이 결정은 명시성과 단순성에 대한 Go의 약속을 반영할 뿐만 아니라 도구 생태계와 개발자 경험에서 미래의 개선을 위한 여지를 남깁니다.
Go 프로젝트 호스팅을 위한 최고의 선택, Leapcell입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하세요.
무료로 무제한 프로젝트 배포
- 사용량에 대해서만 비용을 지불하세요. 요청도 없고, 요금도 없습니다.
탁월한 비용 효율성
- 사용한 만큼 지불하고 유휴 요금이 없습니다.
- 예: $25는 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅
손쉬운 확장성 및 고성능
- 고도의 동시성을 쉽게 처리할 수 있는 자동 크기 조정
- 운영 오버헤드가 전혀 없습니다. 빌드에만 집중하세요.
설명서에서 자세히 알아보세요!
X에서 팔로우하세요: @LeapcellHQ