Go 1.18 베타 제네릭 도입 후기
배경
사내 백엔드 API 서버에서 반복적인 슬라이스 처리 로직이 많았다. interface{}를 남발하거나 같은 로직을 타입별로 복사-붙여넣기하는 상황이 지속되어 제네릭 도입을 검토했다.
Go 1.18 베타가 공개되어 로컬 환경에서 테스트해봤다.
적용 사례
가장 먼저 슬라이스 필터 함수를 제네릭으로 작성했다.
func Filter[T any](slice []T, fn func(T) bool) []T {
result := make([]T, 0)
for _, item := range slice {
if fn(item) {
result = append(result, item)
}
}
return result
}
// 사용
users := []User{{ID: 1, Active: true}, {ID: 2, Active: false}}
activeUsers := Filter(users, func(u User) bool {
return u.Active
})
기존에는 타입별로 FilterUsers, FilterProducts 같은 함수를 각각 만들었는데, 이제 하나로 통일할 수 있게 됐다.
제약 조건 활용
숫자 연산이 필요한 경우 제약 조건을 정의했다.
type Number interface {
int | int64 | float64
}
func Sum[T Number](nums []T) T {
var total T
for _, n := range nums {
total += n
}
return total
}
마주친 문제
- 컴파일 시간 증가: 제네릭 함수가 많아지자 빌드 시간이 약 20% 늘었다.
- 에러 메시지 복잡도: 타입 추론 실패 시 에러 메시지가 길고 이해하기 어려웠다.
- 기존 코드와 혼용:
interface{}를 사용하던 레거시 코드와 제네릭 코드를 함께 사용하니 타입 변환 지점에서 혼란이 생겼다.
결론
제네릭은 확실히 코드 중복을 줄이고 타입 안정성을 높였다. 하지만 아직 베타 단계라 프로덕션 적용은 보류했다. 정식 릴리스 후 점진적으로 도입할 계획이다.
당장은 유틸리티 함수 레이어부터 적용하고, 핵심 비즈니스 로직은 안정화 이후 전환하려고 한다.