1P by GN⁺ 3일전 | ★ favorite | 댓글 1개
  • Go 언어에서 오류 처리의 장황함이 오랫동안 사용자 불만의 상위에 해당함
  • 다양한 구문 개선 제안(예: check/handle, try, ? 연산자 등) 이 논의 및 실험되었으나, 커뮤니티의 충분한 합의 없이 모두 기각됨
  • 언어 변화가 미치는 코드, 도구, 문서 등 광범위한 영향과 Go 특유의 단순함 유지 원칙이 주요 고려사항임
  • 현행 방식의 명확함, 디버깅 편의 및 일부 사용자 선호로 인해, 굳이 구문 변화를 도입할 명분이 약함
  • 가시적 미래에는 오류 처리 구문 변화 계획이 없으며, 관련 제안들은 모두 추가 조사 없이 종료될 예정임

Go의 오류 처리 장황함 문제 제기

  • Go의 오래된 불만 중 하나는 오류 처리 구문이 지나치게 장황함
  • 대표적으로 if err != nil과 같은 패턴이 코드에서 반복적으로 나타남
  • 여러 API 호출이 필요한 프로그램일수록 이 구문이 두드러지며, 실제 로직보다 오류 처리 코드가 더 많아지는 현상 발생함
  • 연간 사용자 설문에서 해당 불만이 상위에 지속적으로 언급됨

커뮤니티와의 협의 및 초기 제안

  • Go 팀은 커뮤니티 피드백을 중시하여 오류 처리 개선안 연구를 계속해 옴
  • 2018년 Go 2 프로젝트 논의에서 Russ Cox가 오류 처리 문제 핵심을 공식적으로 정리함
    • Marcel van Lohuizen이 제안한 checkhandle 메커니즘 안 등장
    • 유사 언어들과 비교 분석 및 다양한 대안 검토 포함
  • 이 방식은 실제로 코드를 간결하게 해주긴 하지만, 복잡성 증가로 인해 채택되지 않음

try 제안과 그 이후

  • 2019년, 훨씬 간소화된 try 내장 함수 제안이 이루어짐
    • check 기능만 코드로, handle 생략
    • 해당 제안은 제어 흐름을 감추는 문제로 비판받고 커뮤니티 반발 속에서 폐기됨
  • 이 경험을 통해 충분한 피드백 없는 완성형 제안의 위험성을 깨닫게 됨
    • 대규모 변경 제안은 초기 설계 단계에서 더 폭넓은 의견 수렴이 중요함을 확인함

추가 시도 및 다양한 제안들

  • 수많은 변형과 대안적 오류 처리 방식 제안이 꾸준히 커뮤니티에서 등장함
    • Ian Lance Taylor의 umbrella issue로 현황 정리, Go Wiki 및 블로그 등에서 사례 지속 수집
  • 2024년에는 Rust에서 차용한 ? 연산자 적용 제안이 나옴
    • 소규모 사용성 테스트에서 직관적이라는 피드백 있었으나, 역시 다양한 의견 속에 합의에 이르지 못함

논의의 교착과 결론

  • 공식, 비공식 제안이 3건 이상, 커뮤니티 제안은 수백 건에 달하지만 충분한 공감대/합의 부족으로 모두 기각됨
  • Go 내부 아키텍트 그룹조차 방향성에 대한 의견 일치가 없음
  • 상황 변화나 특별한 공감대 형성 전까지는 오류 처리 구문 변화 시도 자체를 중단하기로 결정함

현행 방식 유지를 옹호하는 주요 논거

  • 초기 언어 설계 시 구문적 설탕을 넣었으면 논란이 없었겠지만, 현재는 15년간 사용된 방식에 익숙한 생태계가 읶음
  • 새로운 구문을 도입하면 필연적으로 기존/신규 사용자 간 코드 스타일 간극 및 일관성 붕괴 우려 존재함
  • Go의 설계 철학(같은 것을 여러 방식으로 하지 않음)과 간결성/일관성 중시 원칙과도 부합함
    • 짧은 변수 선언(:=)의 재선언 허용도 오류 처리로 인해 생긴 부차적 변화임
  • 명확한 오류 처리 구문(if를 통한)은 코드 읽기, 디버깅, 브레이크포인트 설정에 직관적인 강점이 있음
  • 언어 변화는 실제 변경의 범위(코드, 문서, 도구 등)와 비용 측면에서도 큰 부담임

대안적 개선 및 미래 방향

  • 표준 라이브러리의 기능 강화(예: cmp.Or 도입)로 일부 반복 코드 줄이기는 가능함
  • IDE·개발 도구의 코드 접기, 자동완성, LLM 활용 등으로 장황함을 실무에서 어느 정도 극복 가능
  • 주요 Go 사용자 그룹(예: Google Cloud Next 행사 참석자)에서는 언어 변화 필요성에 부정적인 견해 우세함
    • Go 사용이 늘수록 장황함 문제는 실제로 체감이 줄어듦

구문 개선 필요성을 지지하는 논거

  • 사용자 피드백 기반으로 여전히 오류 처리 구문 개선 요구 존재
  • 문자 수만 줄이는 게 아닌, 명확성을 높이는 오류 처리 구문이 코드 품질/안전성 개선에 기여할 수도 있음
  • 단순 오류 확인이 아닌, 실제 역할을 하는 오류 처리에 대해 더 정밀한 연구가 필요함

최종 결론 및 향후 정책

  • 현재까지 별다른 합의나 실질적 변화 없는 상황을 인정하며, 가시적 미래에는 오류 처리를 위한 구문적 언어 변화 논의·제안을 모두 중단함을 선언함
  • 기존 논의와 연구 과정은 Go 생태계와 프로세스 개선에 간접적으로 기여함
  • 향후 혹시 더 명확한 문제 정의와 합의가 생길 경우 논의가 재개될 수 있음
  • 당분간은 새로운 시도보다는 Go 자체의 견고함과 단순함을 유지하는 데 주력할 방침임
Hacker News 의견
  • 만약 Go 팀이 다른 대안을 할 수 있었다고 쉽게 제안하고 싶다면, Go2ErrorHandlingFeedback 위키GitHub 이슈 검색 링크를 꼭 확인해주길 바라는 마음임. 제안한 거의 모든 아이디어가 이미 진지하게 논의되었고, Go 팀의 투명한 접근 방식에 감사함을 느끼는 사용자로서 매일 Go를 사용하는 즐거움이 큼

    • 초안 설계 문서는 C++, Rust, Swift에 대해 언급하지만, 내가 찾는 Haskell/Scala/OCaml 같은 함수형 언어의 do-notation/for-comprehensions/monadic-let은 찾아보기 어려움. Go 팀이 마치 언어 설계의 마스터처럼 보이지만, 정작 Java처럼 파라메트릭 다형성이 없는 정적 타입의 한계에 부딪혀 오류 처리 문제에서 해답을 못 내는 모습임. 이건 언어 근본 설계에서 온 문제라고 봄

    • 똑똑하고 숙련된 사람들이 작성한 문서임에도 불구하고, Haskell의 Maybe/Either 모나드와 bind 연산자(do-notation) 같은 해결책이 어디에도 언급되지 않는 것이 매우 신기함. 실제로는 어렵거나 현학적이지 않고, 오류를 안전하게 전달하는데 아주 우아하고 검증된 방법임에도 Go 커뮤니티에서 이걸 접목하지 않은 이유를 모르겠음. 이 페이지가 존재한다는 건 고맙지만, 이렇게 유명한 솔루션을 넘긴다는 건 이해하기 힘듦

    • 거의 모든 언어가 다양한 더 나은 접근을 제공하는데, Go에서만 왜 이렇게 문제가 크게 부각되는지 궁금증이 듦. 단순히 합의가 안 되는 건지, 아니면 Go 언어만의 어떤 특징 때문에 다른 언어의 해결책이 맞지 않는 건지 궁금함

    • Go 비판에서 자주 보이는 현상은 비교적 비전문가들이 Go 개발자들이 자기들보다 언어를 더 모를 거라고 전제하는 경향임. 사실 Go 개발자들은 대부분의 경우 오히려 훨씬 더 경험 많고 훨씬 더 많이 알고 있음. 비전문가는 여러 특징을 가진 언어가 무조건 더 좋다고 생각하지만, 실제로는 전체적인 균형을 잘 맞추는 게 중요하다는 점을 간과함

  • 새로운 언어 기능 추가에 신중을 기하는 Go의 보수성 덕분에 사용자가 혜택을 본다고 생각함. Swift의 경우 기능 변화가 너무 많아 학습도 어렵고, 최신 맥에서도 종종 간단한 프로젝트 하나조차 빌드가 안 되는 경험이 있음. 키워드가 계속 늘어나고 바뀌는 탓에 Swift는 사용 지속성이 떨어지고, 그에 비해 Go는 꾸준함이 강점임

  • 한번은 Go 함수가 내부 함수에서 오류 발생을 기대하는 예외적 상황이 있었고, 내부 함수가 오류를 내지 않으면 오히려 함수를 오류로 처리해야 했던 적이 있었음. 흔하지 않은 구조에서 if err == nil 로 분기해야 했고, 습관적으로 if err != nil 을 써버려서, 평소 쓰던 패턴에 너무 익숙해져 실수를 찾는 데 오래 걸렸던 경험임. 자주 쓰는 if err != nil 과 드물게 쓰는 if err == nil 의 문법적 구분을 언어 차원에서 지원했다면 실수를 줄일 수 있었겠다는 생각을 함

    • "if err == nil"을 쓸 때마다 // inverted 라는 주석을 달아 패턴을 강조함. 언어에서 자동으로 다뤄주면 좋겠지만, 현재는 이런 방식으로라도 구분을 더 뚜렷하게 할 수 있음
    • 사실 이건 문법 변화에 대한 반대 입장임. 자주 쓰는 if err == nil { return ... } 패턴이 코드에 오히려 더 어색하게 보일 수 있음. 현재의 Go 에러 처리 방식이 명확하고 읽기 쉬워서 많은 사람들이 선호한다는 의견임
    • if fruit != "Apple" 같은 패턴에서도 동일한 혼란이 생길 수 있기 때문에, 본질적으로 에러 처리만의 문제가 아닌 전반적인 상태 분기 문제로 봐야 한다는 주장임. 에러도 결국 다른 상태값처럼 다뤄지는 것임
    • IDE나 글꼴 설정에서 "if err != nil"을 특수 기호처럼 렌더링해서 배경에 자연스럽게 묻히게(덜 눈에 띄게) 하고, 다르게 쓰인 "if err == nil"만 도드라지게 해 에디터 차원에서 실수 방지 가능성 있음
    • 에디터에서 "if err … {" 같이 패턴을 축약해 보여주는 방식으로 가독성을 개선할 수도 있을 거라는 제안임
  • Go의 명시적 에러 처리방식이 마음에 듦. 함수가 항상 성공(minimal error)하거나 실패할 수 있는 구조로 단순히 이해함. 실패 가능성 있는 함수는 꼭 처리해줘야 다음 단계로 진행할 수 있음. 여러 언어가 예외 처리를 통해 오류가 발생하면 catch될 때까지 스택을 타고 오류를 던지다 보니, 에러가 어디서 발생했는지만 알려줄 뿐 실질적 힌트가 부족하다는 불만이 있음. Go에서는 다음과 같은 옵션을 명확히 가질 수 있음: 1) 에러 무시 2) 에러 발생 시 곧바로 반환 3) 에러 래핑해서 유용한 정보 추가 4) 특정 에러 해석해서 분기 처리(예, 404로 변환). Go2에서는 Result<Value, Failure> 타입이나 더 구체적이고 나열 가능한 에러 타입을 추가해보고 싶음. Go 1과의 호환성을 위해 Go 2에서 도입하는 게 더 적합하다고 생각함

    • 에러 처리 정책은 반드시 호출자가 결정해야 하며, 하위 스택에서 처리하는 것은 바람직하지 않다고 경험함. 결국 에러는 래핑해서 상위로 전달하는 단순 반복 작업이 되기 쉬움
    • "Go의 에러 처리"는 사실 자바스크립트, 파이썬이 아닌 함수형 언어나 Rust, Java 등 대다수 언어들이 이미 제공함. 결국 제네릭만 있으면 Go 방식의 에러 처리를 어느 언어에서든 구현할 수 있다는 주장임. 비교 대상이 JS나 Python에 머문다면 흔한 패턴에 불과함
    • "함수가 실패하면 반드시 처리해야 한다"는 점이 바로 Go의 실패 포인트라고 지적함. Go에서는 사실상 에러를 완전히 무시할 수 있어, 실제로 robust한 소프트웨어를 만들고 싶으면 오히려 Go의 방식이 약점이 될 수 있음
    • Go2는 결국은 "절대 출시되지 않을 실험실"에 머물 거라는 씁쓸한 의견임
  • Go의 오류 처리 방식은 처음엔 별로였지만, errors-are-values 블로그 포스트를 읽고, panic(err)을 적재적소에 활용하기 시작하면서 오히려 큰 만족을 느끼게 됨. 부모 코드가 직접 처리해선 안 되는 비정상적인 상태에는 panic을 활용해 코드의 잡다한 에러 분기를 대폭 줄일 수 있었음. 이러한 오류 관리 방식은 실제 업무에서 큰 도움이 되고 있음

    • 이런 논리는 오히려 Go의 빈약한 오류 처리를 방어할 수 없고, 개선해도 장점이 사라지지 않는다는 반론이 있음
    • PHP도 레벨별 에러 처리나 @ 연산자로 call site 에러 억제가 가능하고, bash도 -e와 같은 에러 관리 기법이 있다는 언급
    • C#에서 try/catch/finally 흐름을 처음 봤을 땐 참신했지만, 지금은 오히려 Go처럼 단순한 로직을 선호하게 됨. 높은 코드 분량(Loc)도 실제로 코드 흐름이 명확한 게 장점이라 생각함
    • 러스트의 sum type 기반 에러도 ‘errors are values’ 패러다임에 속함을 언급함
  • 실제 에러를 처리하면 장황함은 금방 가려진다는 주장에 manual stack trace 생성이 진짜 ‘처리’인지 의문이 생김. Go의 정의에 따르면 예외(exception)도 처리가 되는 것 아닌지? 라는 유쾌한 반론이 있음

    • 수십 줄의 stack trace가 정말로 명확한 정보인지 의문이 듦. 개인적으로는 단 한 줄 wrap error가 훨씬 효율적이라 생각하고, 로그를 정리하는 데에도 도움이 됨. 10년 넘게 Go를 사용해오면서 런타임 함수까지 포함된 장황한 스택 정보를 필요로 한 적 없음
  • 이 글에서 Go 오류 처리의 문제를 단순히 "문법이 장황하다"고 다루는 게 마음에 들지 않음. 진짜 문제로는 1) 오류가 조용히 누락되거나 실수로 무시되기 쉬움 2) 함수 결과를 값처럼 쉽게 넘기거나 저장 불가 3) errors.Is 같은 중첩 오류가 타입 시스템과 어색하게 맞물림 4) 에러 스위칭이 어려움 5) sentinel value 활용이 표준 라이브러리에서 많음 6) 제네릭과의 궁합이 안 좋아 패키지 필요성이 생김 등 다양한 문제가 있다고 생각함

    • Go의 전문 프로그래머 90%는 각 에러 반환 분기별로 테스트 케이스를 작성해 커버리지를 맞추는데, 예외처리 언어에선 불필요한 작업임
    • 이 글에서 It’s too verbose(장황하다)가 주 문제라고 했다는 주장은 사실과 다르다고 생각함. 구문을 바꿔도 본질적 개선점이 크지 않음
    • Go의 변화 속도가 매우 느리다는 것(제네릭도 오랜 시간 걸림)이 오히려 장점이라고 보는 관점도 있음
    • 구글러로서 Go 팀의 결정에 또다시 실망하게 되었음
  • Elixir(및 Erlang)에서는 함수가 일반적으로 {:ok, result} 또는 {:error, description} 튜플을 반환함. Elixir의 with 문법 덕분에 에러 처리를 블록 하단에서 묶어 가독성이 훨씬 좋아짐. Go에도 with 문과 비슷한 걸 도입하면, 에러가 nil일 때만 연속 실행하고, 최하단에 에러 핸들러 블록을 두는 식으로 더 읽기 좋게 개선 가능함

    • Go는 커뮤니티 합의 문제 때문에 가장 기본적인 sum type, 에러 핸들링, 패키지 매니지먼트 같은 가치 있는 기능도 도입이 아주 느림. 제네릭 13년, 에러 핸들링 16년, 패키지 관리 9년이 걸릴 정도로 느린 변화. 신중함도 중요하지만, 완벽만 쫓다보니 결정이 늘 미뤄진다는 아쉬움이 큼
    • Go의 다중 반환 패턴은 관점에 따라 비정상적으로 여겨지기도 함. 여러 타입을 반환하는 함수로 가능한 게 오직 변수 할당 뿐이라는 비판임
  • Rust 스타일을 바로 따라가지 않는 이유를 모르겠다는 입장임. 특히 제네릭이 생긴 지금은 금방 비슷한 구현도 가능함. Rust의 ? 연산자가 편하긴 해도 오류를 무시하는 걸 조장한다고 비판하는 논리는 공감이 가지 않음. 실제로 Go는 오류 반환값을 무시해도 컴파일 오류 없이 넘어가는 경우가 숱하게 나옴. Rust 스타일처럼 아예 Result 타입 반환을 강제해야 실수 방지가 가능함. 편의성 명목으로 논란이 된다면 panic도 금지해야 맞지 않냐는 강한 주장임

    • Go가 Result를 도입하지 못하는 이유는 sum type이 없고, 모든 타입에 zero value가 필요하다는 특이한 설계 때문이라는 의견임
    • “? 연산자” 같은 편의성 기능이 “래핑된 에러를 더 이상 안 쓸 거다”는 주장에 대해서는, 도리어 그런 기능에 wrapping을 장려하는 설계가 가능하다는 반론임
    • 편의성 강조 기능(Rust 스타일)에 대한 단점으로, 분기 흐름이 한 줄에 숨겨지고, 디버깅 브레이크포인트 걸기도 어렵다는 점, enrich/handling보다는 bubbling에만 극도로 집중된다는 이유로 Go가 버린 문법(e.g. 3항 연산)임을 설명함
    • Rust 스타일을 그대로 비교 적용한다 해도, 실제로 Go에서 뭐가 equivalent인지가 명확하지 않다는 기술적인 의문 제기임
    • 제네릭 도입 후 Rust 스타일로 뭘 구현했다는 것인지 코드 예시가 궁금하다는 피드백임
  • Rust처럼 체크박스 체크하며 기능 채택을 논의하는 구조가 아니라, 언어는 전체적인 일관성 안에서 설계해야 한다고 생각함. 기능 리스트를 모두 체크했다고 바로 도입하는 것이 실제로 언어의 본질에 맞지 않을 수도 있음

    • Rust가 디자인 바이 커미티(committee) 방식으로 가면서 문법이 읽기 어렵고 일관성이 떨어진다는 이미지가 생김
    • “완벽한 해결책” 같은 건 없다는 의견임
    • 설문 결과 Go의 단일 치명적 문제점이 에러 처리라고 하기엔 13%만이 그렇게 응답했으며, 현 상태를 선호하는 유저도 적지 않음. 설문 결과 참고