Go 1.18 베타 제네릭 도입 후기
배경
사내 백엔드 프로젝트에서 슬라이스 조작 함수들이 타입별로 중복되어 있었다. FilterInt, FilterString, MapInt 같은 식으로 5~6개 타입마다 동일한 로직이 반복되고 있었다.
Go 1.18 베타가 12월 초 공개되면서 제네릭이 추가되었다는 소식에 로컬 환경에 설치해서 테스트해봤다.
기존 코드
func FilterInt(slice []int, fn func(int) bool) []int {
result := []int{}
for _, v := range slice {
if fn(v) {
result = append(result, v)
}
}
return result
}
func FilterString(slice []string, fn func(string) bool) []string {
// 동일한 로직 반복...
}
제네릭 적용
func Filter[T any](slice []T, fn func(T) bool) []T {
result := []T{}
for _, v := range slice {
if fn(v) {
result = append(result, v)
}
}
return result
}
// 사용
filtered := Filter([]int{1, 2, 3, 4}, func(n int) bool {
return n > 2
})
타입 파라미터 문법이 대괄호 [T any]를 사용한다. 처음엔 낯설었지만 금방 익숙해졌다.
제약 조건
숫자 타입만 받는 함수가 필요한 경우 constraints 패키지를 사용할 수 있다.
import "golang.org/x/exp/constraints"
func Sum[T constraints.Ordered](slice []T) T {
var sum T
for _, v := range slice {
sum += v
}
return sum
}
느낀 점
- 코드 중복이 확실히 줄어들었다
- 컴파일 타임에 타입 안정성 확보
- 문법이 생각보다 직관적이다
- 아직 베타라 프로덕션 적용은 내년 정식 릴리즈 이후에 고려 예정
기존 Go 철학과 맞지 않는다는 의견도 있지만, 실용적인 측면에서 환영할만한 변화였다.