Go 1.21 제네릭 적용 후기: API 응답 래퍼 리팩토링

배경

사내 마이크로서비스 API 게이트웨이를 Go로 운영 중인데, 각 서비스마다 비슷한 응답 래핑 코드가 반복되고 있었다. Go 1.18에서 제네릭이 도입됐지만 프로덕션 적용은 미뤄왔는데, 1.21이 안정화되면서 본격적으로 도입해봤다.

기존 코드

type UserResponse struct {
    Data    User   `json:"data"`
    Message string `json:"message"`
}

type ProductResponse struct {
    Data    Product `json:"data"`
    Message string `json:"message"`
}

func handleUserAPI(w http.ResponseWriter, r *http.Request) {
    user := getUserFromDB()
    resp := UserResponse{Data: user, Message: "success"}
    json.NewEncoder(w).Encode(resp)
}

각 엔드포인트마다 Response 구조체를 만들고 있었다.

제네릭 적용

type APIResponse[T any] struct {
    Data    T      `json:"data"`
    Message string `json:"message"`
    Error   string `json:"error,omitempty"`
}

func respondJSON[T any](w http.ResponseWriter, data T, message string) {
    resp := APIResponse[T]{
        Data:    data,
        Message: message,
    }
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(resp)
}

func handleUserAPI(w http.ResponseWriter, r *http.Request) {
    user := getUserFromDB()
    respondJSON(w, user, "success")
}

결과

  • 30개 넘는 Response 구조체 제거
  • 타입 안정성 유지하면서 보일러플레이트 코드 80% 감소
  • 에러 핸들링도 제네릭 함수로 통일

컴파일 타임은 약간 늘었지만 실행 성능은 차이 없었다. 팀 내에서도 코드 가독성이 개선됐다는 반응이 좋았다. 다음에는 repository 레이어도 제네릭으로 리팩토링할 계획이다.