Go 1.19 제네릭 도입 후 공통 유틸 리팩토링
배경
사내 API 서버는 Go 1.17로 운영 중이었다. 슬라이스 필터링, 맵 변환 같은 유틸 함수가 타입별로 중복 구현되어 있었고, 인터페이스 기반 추상화는 타입 안정성이 떨어졌다. Go 1.19로 업그레이드하면서 제네릭을 적용해봤다.
기존 코드의 문제
// 각 타입마다 별도 함수 필요
func FilterStrings(items []string, fn func(string) bool) []string {
result := []string{}
for _, item := range items {
if fn(item) {
result = append(result, item)
}
}
return result
}
func FilterInts(items []int, fn func(int) bool) []int {
// 동일한 로직 반복...
}
제네릭 적용
func Filter[T any](items []T, fn func(T) bool) []T {
result := make([]T, 0, len(items))
for _, item := range items {
if fn(item) {
result = append(result, item)
}
}
return result
}
// 사용
activeUsers := Filter(users, func(u User) bool {
return u.Active
})
제약 조건 활용
숫자 타입 처리가 필요한 경우 커스텀 제약을 만들었다.
type Number interface {
~int | ~int64 | ~float64
}
func Sum[T Number](items []T) T {
var sum T
for _, v := range items {
sum += v
}
return sum
}
실제 적용 결과
- 유틸 패키지 코드 라인 수 40% 감소
- 타입 안정성 확보 (컴파일 타임 체크)
- 러닝 커브는 있었지만 팀 내 빠르게 적응
주의사항
제네릭이 만능은 아니다. 성능 크리티컬한 부분은 벤치마크 필수다. 초기 컴파일 시간이 약간 늘었고, 일부 IDE 지원이 완벽하지 않았다. 하지만 코드 중복 제거와 유지보수성 향상 효과가 더 컸다.
내년에는 표준 라이브러리에 제네릭 기반 슬라이스, 맵 유틸이 추가될 예정이라고 하니 기대된다.