10P by neo 6일전 | favorite | 댓글 5개
  • 저자가 Go 언어를 수년간 사용한 후 Java로 전환하게 되면서 느낀 Go 언어의 한계점과 문제점을 설명하는 글
  • Go가 단순하고 지루한(boring) 언어라는 특징이 장점이 아닌 단점이 될 수 있다는 관점을 제시함
  • Go의 철학: Google의 Go 설계 팀은 단순함과 제한성을 강조했지만, 이는 사용자가 직접 해결해야 하는 반복 작업을 야기함

1. Go는 "재미없다"는 점이 단점일 수 있음

  • Russ Cox의 주장:
    • Go는 "재미없다(boring)"는 점이 장점이라고 강조
    • 루프는 for 하나뿐이며, 필터, 맵, 리듀스 같은 기능은 기본 제공되지 않음
    • 대부분의 다른 언어에서 제공하는 다양한 고급 기능이 없는 것이 단순함의 일부로 간주됨
  • Reddit 사용자 의견:
    • "재미없음"과 "강력함"의 경계가 모호
    • Go의 기본 기능 부족은 언젠가 언어에 추가될 가능성이 높다고 주장
  • 서드파티 패키지 의존:
    • 부족한 기능을 보완하기 위해 자주 사용하는 samber/lo 패키지:
      • 필터, 맵, 검색 등의 필수 기능 포함
      • GitHub에서 18.1k 스타를 받고, 12.6k 이상의 프로젝트에서 사용
    • 일부 기능은 slices 패키지로 추가되었으나 여전히 기능적으로 부족함
  • 작성자의 불만:
    • 반복적인 루프 작성 강요
    • 필터 및 맵과 같은 작업을 간결하게 처리하기 어려움
    • 별도의 리시버 메서드로 추출할 수는 있지만, 코드의 깔끔함을 저해
  • Go의 단순함은 많은 경우 장점이지만, 기본적인 편의 기능 부족은 생산성과 코드 가독성을 떨어뜨리는 단점이 될 수 있음

2. Clean Code 원칙을 방해함

  • 에러 처리 문제:
    • 함수에서 반환 값으로 error를 포함하는 경우가 대부분:
      • if err != nil 패턴을 반복적으로 사용해야 함
      • 코드 정리 과정에서 오히려 코드가 더 복잡해지는 문제 발생
    • HTTP 핸들러 코드가 간단한 프로젝트에서도 20줄 이상으로 증가
      • 원래 목표는 4줄 정도로 유지
    • panic()과 복구 미들웨어를 고려할 정도로 에러 처리 방식에 좌절
  • 짧은 이름 권장:
    • 변수, 메서드, 함수 이름에 짧은 이름 사용 권장:
      • c, a 같은 이름이 무엇을 의미하는지 불명확
      • 예: c는 Command, Controller, Argument, Amendment 중 무엇인가?
      • 긴 이름을 사용하면 더 명확해질 수 있지만, Go의 철학은 짧은 이름을 선호
    • 이로 인해 팀 코드 리뷰 중 테스트 메서드 이름 등에 대한 끝없는 논쟁 유발
  • Go의 철학은 코드의 간결함과 단순함을 강조하지만, 결과적으로 클린 코드 원칙에 반하는 복잡성과 비효율성을 초래할 수 있음

3. 의도적으로 작은 언어 철학과 DIY 문화

  • 기본 기능 부족:
    • 간단한 HTTP 핸들러를 구현하는 것은 쉽지만, 기본 미들웨어(예: 지수 백오프, 교차 사이트 설정 등)가 필요할 경우 여러 패키지를 찾아야 함
    • 이러한 패키지들이 (1) 유지보수되고 있는지, (2) 기대대로 작동하는지 확신하기 어려움
  • 반복 작업 증가:
    • 단순함을 유지하려는 Go의 설계 철학이 오히려 개발자에게 "바퀴를 재발명"하라는 요구로 이어짐
    • 예: 간단한 필터 기능조차 직접 구현해야 하는 상황 발생
  • 미성숙한 패키지 생태계:
    • 많은 GitHub 프로젝트가 방치되거나 소수 버전만 릴리스된 상태
    • 상대적으로 젊은 언어라는 점에서 .NET/Java와 비교가 불공정할 수 있지만, 현실적으로 Go의 패키지 안정성과 성숙도가 부족함
  • ORM의 한계:
    • Go의 주요 ORM 패키지(Gorm)는 Hibernate나 Entity Framework에 비해 기능적으로 뒤처짐
    • 이상한 동작과 문서화 부족 문제 존재
    • Go 커뮤니티의 반응: "Go에선 ORM이 필요 없음, Do It Yourself!"
  • Go의 단순성은 프로젝트와 팀에 따라 장점일 수 있지만, 기본 제공되지 않는 기능의 부족은 생산성과 개발 경험에 부정적인 영향을 미칠 수 있음.

4. Go에서 하나의 방식만 존재하지 않음

  • 일관성과 통일성의 오해:
    • 테이블 테스트(Table Test)
      • stretchr/testify와 같은 테스트 스위트 사용 (557k 프로젝트에서 사용 중)
      • 테이블 테스트 내의 커스텀 서브테스트 작성
    • 이로 인해 "통일된 방식"이라는 Go의 철학과 현실 사이에 괴리가 있음
  • 팀 내 갈등 유발:
    • 팀 간 테스트 스타일 및 구현 방식에 대한 논의가 오히려 증가
    • Go의 철학과 설계 팀 자체도 일관성이 부족:
      • 예: Getter 메서드 네이밍에 대한 불일치
  • 기능 거부와 패키지 의존:
    • Go 팀은 어서션(assertion) 기능 추가를 거부하며, 프로그래머의 부족함을 문제로 삼는 태도 비판받음
    • 결과적으로 필요한 기능을 사용하기 위해 또 다른 패키지를 설치(go get)해야 함
  • Go는 단순성과 통일성을 지향하지만, 실제로는 다양한 구현 방식과 이에 따른 논쟁이 존재하며, 언어 설계 철학의 모호함이 문제를 악화시키고 있음

5. Go의 디버깅은 재미없음

  • 디버깅 중 표현식 평가 불가:
    • 디버깅 세션에서 표현식을 평가하거나 객체의 커스텀 문자열 표현을 확인할 수 없음
    • 런타임에 객체 상태를 명확히 파악하기 어려움
  • 스택트레이스와 로그의 비직관성:
    • 대규모 테스트(예: CI에서 수천 개 테스트 실행) 실패 시 혼란스러운 스택트레이스 및 로그 제공
    • 결과적으로 디버깅이 어려워지고 생산성이 저하됨
  • C 스타일 디버깅 경험:
    • Go의 디버깅 도구 체인이 C 기반으로 동작:
      • C와 유사한 원시적 디버깅 경험 제공
      • 개발자 친화적이지 않음
  • Rust와의 비교:
    • Rust는 Go의 한계를 개선:
      • 명확하고 유용한 에러 정보를 제공
      • 에러 메시지에 정확한 수정 제안 포함
  • Go의 디버깅 경험은 최적화된 바이너리 제공에 중점을 둔 설계 철학에 기반하나, 이는 개발자 경험을 희생시키는 결과를 초래함. 디버깅 효율성을 중시하는 환경에서는 대안 언어를 고려하는 것이 바람직할 수 있음

요약: Go의 적합성과 한계

  • Go 내장 도구의 장점:
    • 패키지 관리, 테스트, 성능 모니터링을 위한 기본 도구 체인 제공
    • 별도 설정 없이 사용 가능하여 초기 개발 환경 설정 간소화
  • 한계:
    • "지루한 코드"와 반복 작업:
      • Go의 도구 체인은 기능적이지만, 코드 작성 시 반복 작업(Plumbing Code)을 강요
      • 예: 단조로운 구문과 제한된 기능이 작업 흥미를 떨어뜨림
    • "import cycle not allowed":
      • 테스트에서 순환 의존(import cycle)을 허용하지 않음
      • 도메인 주도 설계(DDD) 작업 시 구조적 제약으로 인해 복잡도 증가
    • struct 임베딩 기법의 의존성:
      • 이상하고 제한적인 struct 임베딩 메커니즘으로 인한 사용상의 고통
  • 적합한 활용 영역:
    • 인프라스트럭처 개발에 적합:
      • Docker, Drone, Hugo 등 시스템 수준의 툴이 Go로 작성됨
    • 경량 서버 및 CLI 애플리케이션 개발에 유용
  • 부적합한 활용 영역:
    • 복잡한 엔터프라이즈 응용 프로그램(예: ERP 시스템) 개발:
      • 제한된 언어 철학과 도구로 인해 대규모 비즈니스 로직 관리 비효율
  • Go는 특정 작업(특히 인프라 관련)에서는 뛰어난 효율을 제공하지만, 복잡한 비즈니스 도메인 애플리케이션에는 적합하지 않은 도구임. CTO가 구글 기술 스택에 치우친 경우에도 기술 선택에 신중해야 함

if err != nil {} 이거는 솔직히 좀 귀찮긴 합니다. 지적된 단점들도 동의하구요. 그래도 이 언어가 지향하는 바를 명확히 이해하고 어떤 점을 더 활용할지 고민한다면 지적된 단점에도 불구하고 더 잘 활용 할 수 있을 것 같습니다. C랑 비슷한데 GC가 있고, 제한적이지만 제네릭까지 지원되는데다 크로스 컴파일까지 된다! 이렇게 보면 또 혜자스러운 언어가 아닐까요?

Rust의 ?만 있었어도 지금보다는 훨씬 낫지 않았을까요...

Go 를 쓰면서 느낀점은, 그동안 얼마나 암묵적인 에러핸들링을 하고있었나 하고 생각이 들더라구요.
물론 에러핸들링을 한 포인트에서 처리하는 것이 구조적으로 깔끔하게 보일 수 있겠지만, 명시적으로 에러응답이 가능한 동작임을 드러내면서 더 안전한 방식으로 코드를 만들게 되는 것 같다는 생각이 듭니다.

자바에서 go로 넘어가면서 처음엔 비슷한 느낌을 받았던 것 같네요.
지금은 자바에 사용한 시간이 아깝다고 생각할 정도로 go를 즐기고 있습니다. 복잡한 비즈니스 어플리케이션에 적합하지 않다는건 그 어플리케이션이 시스템을 단순화하는데 충분히 고민하지 않았다라는 생각이 들게 만듭니다.

Hacker News 의견
  • Java 개발자가 Go에 Java 스타일을 강요하면 문제가 발생함

    • Go 철학은 단기적으로는 덜 유용하지만, 장기적으로는 큰 차이를 만듦
    • JVM 생태계에 깊이 빠진 사람들은 Go를 즐기기 어려움
  • 많은 개발자들이 너무 이른 추상화를 시도함

    • 간단한 반복이 충분할 때 불필요한 추상화를 만듦
  • Go의 표준 라이브러리는 크지만 모든 것을 할 수 있을 만큼 크지는 않음

    • 프로젝트마다 바퀴를 재발명하는 경향이 있음
    • Go는 서버/CLI 애플리케이션에 이상적임
  • 프로그래밍 언어 선택보다 더 큰 도전 과제가 존재함

    • Go의 메커니즘과 철학에 적응하는 것이 중요함
  • Go를 좋아하는 이유를 이해하기 어려움

    • 언어 자체보다는 도구 체인과 배포의 용이함을 선호함
  • Go의 핵심 팀이 잘못된 결정을 되돌리는 것이 좌절감을 줌

    • 좋은 패키지 시스템과 도구가 존재함
    • 복잡한 ERP 시스템에는 Java가 더 나은 선택일 수 있음
  • Go는 UNIX와 같은 문제를 가짐

    • 복잡성을 사용자에게 떠넘기는 경향이 있음
    • Java의 런타임은 Go에 비해 빠르게 발전하고 있음