Go 1.21 defer 성능 개선과 실제 프로젝트 적용기
배경
회사 API 서버를 Go 1.19에서 1.21로 업그레이드했다. 릴리즈 노트에서 defer 성능 개선을 언급했는데, 실제로 체감할 수 있을지 궁금했다.
기존에는 defer의 오버헤드 때문에 hot path에서는 명시적으로 Close()를 호출하는 패턴을 사용했었다.
기존 코드
func processRequest(ctx context.Context, id string) error {
conn, err := db.GetConnection()
if err != nil {
return err
}
result, err := conn.Query(ctx, query, id)
if err != nil {
conn.Close()
return err
}
// ... 처리 로직
conn.Close()
return nil
}
에러 처리마다 Close()를 호출해야 해서 실수하기 쉬웠다.
벤치마크
Go 1.21에서 defer 오버헤드를 측정해봤다.
func BenchmarkDefer(b *testing.B) {
for i := 0; i < b.N; i++ {
func() {
conn := getConn()
defer conn.Close()
// work
}()
}
}
func BenchmarkExplicitClose(b *testing.B) {
for i := 0; i < b.N; i++ {
func() {
conn := getConn()
// work
conn.Close()
}()
}
}
결과는 거의 동일했다. 1.19에서는 defer가 약 15% 느렸는데, 1.21에서는 2% 이내로 차이가 줄었다.
개선 후
func processRequest(ctx context.Context, id string) error {
conn, err := db.GetConnection()
if err != nil {
return err
}
defer conn.Close()
result, err := conn.Query(ctx, query, id)
if err != nil {
return err
}
// ... 처리 로직
return nil
}
코드가 훨씬 깔끔해졌고, 리소스 누수 가능성도 줄었다.
프로덕션 적용
API 서버 전체에 패턴을 적용했다. 약 200개 함수를 수정했고:
- 평균 응답 시간: 변화 없음 (45ms)
- 메모리 사용량: 소폭 감소 (리소스 누수 방지)
- 코드 리뷰 시간: 단순한 로직으로 감소
성능 걱정 없이 defer를 자유롭게 쓸 수 있게 되었다. Go팀의 성능 개선 작업에 감사한다.