Go 1.27 릴리스 노트: 제네릭 메서드와 타입 시스템 향상

Go 1.27은 타입 시스템, 성능 최적화 및 표준 라이브러리 확장에 크게 중점을 둔 업데이트입니다. 주요 기능은 메서드 선언이 수신자와 독립적인 자체 타입 매개변수를 가질 수 있게 하는 제네릭 메서드 도입입니다.

제네릭 메서드

메서드 선언이 이제 자체 타입 매개변수를 선언할 수 있어, 패키지 수준 함수 대신 메서드로 제네릭 연산을 구현할 수 있습니다. 이를 통해 메서드가 제네릭 컨테이너의 요소 타입을 변환할 수 있습니다.

type Box[T any] struct{ v T }

// 메서드가 자체 타입 매개변수 U를 선언합니다 (Go 1.27에서 새로 추가).
func (b Box[T]) Map[U any](f func(T) U) Box[U] {
    return Box[U]{v: f(b.v)}
}

핵심 제한: 인터페이스는 타입 매개변수가 있는 메서드를 선언할 수 없으며, 제네릭 메서드는 인터페이스를 만족시키는 데 사용할 수 없습니다. 인터페이스 안에 제네릭 메서드를 넣으면 컴파일러가 오류를 발생시킵니다.

커뮤니티 관점에서 본 제네릭 메서드

일부 개발자는 유연성 증가를 환영하지만, 다른 이들은 인지 부하와 가독성에 대한 우려를 표명했습니다.

"(b Box[T]) Map[U any](f func(T) U) Box[U]는 Go가 피한 인지적 무게의 유형이다."

이에 대해 Go 유지관리자 @neild는 제네릭 메서드가 언어의 불일치를 해결하고—함수는 제네릭이 될 수 있지만 메서드는 그렇지 못했던 상황—API를 단순화한다고 설명했습니다. 예를 들어 math/rand/v2에서 새로운 (*Rand).N 메서드는 특정 *Rand 소스에서 어떤 정수형이나 기간형이든 제한된 랜덤 값을 번거로운 타입 캐스팅 없이 추출할 수 있게 합니다.

표준 라이브러리 확장

UUID 패키지

Go는 이제 RFC 9562에 따라 UUID를 생성·파싱하는 최상위 uuid 패키지를 포함합니다. 일반 용도의 uuid.New(), 순수 랜덤 UUID를 위한 uuid.NewV4(), 시간 순서 UUID를 위한 uuid.NewV7()를 제공하며, 특히 데이터베이스 키에 유용합니다.

JSON v2 정식화

encoding/json/v2encoding/json/jsontext가 실험 단계에서 벗어나 GOEXPERIMENT=jsonv2 플래그 없이도 사용할 수 있게 되었습니다. 특히 기존 encoding/json(v1)은 이제 v2 구현을 기반으로 합니다. 이 변화는 투명하지만, 성능 이유로 v2는 기본적으로 맵 키를 정렬하지 않으며, 안정적인 출력을 원할 경우 json.Deterministic 옵션을 사용할 수 있습니다.

포스트-양자 암호화

새로운 crypto/mldsa 패키지는 ML-DSA(FIPS 204)를 구현합니다. 이 포스트-양자 디지털 서명 스킴은 crypto/x509crypto/tls(TLS 1.3)에도 통합되었습니다.

성능 및 런타임 개선

더 빠른 메모리 할당

컴파일러가 80바이트 이하의 작은 할당에 대해 크기 특화 메모리 할당 루틴을 사용하게 되어, 해당 할당에 대해 최대 30% 비용을 절감합니다. 일반적으로 할당이 많은 프로그램에서 전체 성능이 약 1% 향상되며, 바이너리 크기가 약 60KB 증가합니다.

포터블 SIMD

실험적인 simd 패키지는 벡터 크기에 구애받지 않는 SIMD 명령을 제공합니다. 하드웨어 벡터 명령이 가능한 경우 이를 사용하고, 그렇지 않으면 순수 Go 에뮬레이션으로 대체합니다. GOEXPERIMENT=simd 로 활성화할 수 있으며, Go 런타임은 이미 Swiss Table 맵 구현에 이를 활용하고 있습니다.

Goroutine 누수 프로파일링

이전에는 실험이었던 goroutine 누수 탐지기가 이제 정규 프로파일이 되었습니다. runtime/pprof는 채널이나 뮤텍스에 영구적으로 블록된 goroutine을 식별하는 goroutineleak 프로파일을 제공합니다.

도구 및 생활 편의성

테스트 향상

  • synctest.Sleep: time.Sleepsynctest.Wait을 결합해 합성 시계를 앞당기고 goroutine이 정착될 때까지 한 번에 기다립니다.
  • 인메모리 테스트 서버: httptest.NewTestServer가 이제 인메모리 가짜 네트워크를 지원해 실제 TCP 포트가 필요 없으며, t.Cleanup과 연동해 자동 리소스 관리를 제공합니다.

언어 인체공학

  • 구조체 리터럴 필드 선택자: 임베디드 구조체에서 승격된 필드를 구조체 리터럴에서 직접 설정할 수 있습니다(예: User{ID: 7} 대신 User{Base: Base{ID: 7}}).
  • 일반화된 함수 타입 추론: 추론이 변환 및 복합 리터럴에도 적용돼, 슬라이스나 맵에서 제네릭 함수를 사용할 때 타입 인자를 수동으로 지정할 필요가 줄어듭니다.
  • strings.CutLastbytes.CutLast: 구분자의 마지막 발생을 기준으로 문자열이나 바이트 슬라이스를 분할하는 새로운 헬퍼 함수입니다.

도구 업데이트

  • go test가 이제 기본적으로 stdversion vet 체크를 실행해 버전 드리프트를 방지합니다.
  • go doc이 버전별 문서(go doc pkg@version)와 실행 가능한 예제를 나열하는 -ex 플래그를 지원합니다.
  • go mod tidy가 흩어진 require 블록을 두 개의 정규 블록(직접 및 간접)으로 병합합니다.

기타 주요 변경 사항

  • HTTP 응답 본문: http.Response.Body가 HTTP/1에서 Close 시 자체적으로 데이터를 소모하도록 바뀌어 연결 재사용이 개선됩니다. 일부 커뮤니티 구성원은 이를 다운로드 중단을 위해 조기에 닫는 경우 위험한 무음 동작 변화라고 지적했습니다.
  • 시간 채널: time.After, time.NewTimer, time.NewTicker가 반환하는 채널은 이제 항상 버퍼가 없습니다.
  • HTTP/2: 내부 구현이 단일 거대한 파일에서 전용 패키지 net/http/internal/http2로 이동했습니다.
  • Unicode 17: 표준 라이브러리가 Unicode 17로 업그레이드되었습니다.
  • math/big: 명시적 반올림 모드(Trunc, Floor, Round, Ceil)를 갖는 Int.Divide가 추가되었습니다.

Sources