Go 1.20 슬라이스를 배열 포인터로 변환하기
문제 상황
레거시 C 라이브러리와 연동하는 코드를 유지보수하던 중, 슬라이스를 고정 크기 배열로 변환하는 부분에서 매번 unsafe 패키지를 사용해야 했다. Go 1.20으로 업그레이드하면서 새로 추가된 변환 기능을 적용해봤다.
기존 방식
func processData(data []byte) error {
if len(data) < 32 {
return errors.New("insufficient data")
}
// unsafe를 사용한 변환
arr := (*[32]byte)(unsafe.Pointer(&data[0]))
return cLib.Process(arr)
}
unsafe 패키지 사용은 항상 조심스러웠고, 코드 리뷰 때마다 길이 체크가 제대로 됐는지 확인해야 했다.
Go 1.20 개선
func processData(data []byte) error {
if len(data) < 32 {
return errors.New("insufficient data")
}
// 타입 안전한 변환
arr := (*[32]byte)(data)
return cLib.Process(arr)
}
런타임에 길이가 부족하면 패닉이 발생하므로, 명시적인 길이 체크는 여전히 필요하다. 하지만 unsafe를 제거할 수 있어서 코드가 더 명확해졌다.
실전 적용 사례
SHA-256 해시 처리 부분에서 특히 유용했다.
func parseHash(hashSlice []byte) ([32]byte, error) {
if len(hashSlice) != 32 {
return [32]byte{}, errors.New("invalid hash length")
}
return *(*[32]byte)(hashSlice), nil
}
마이그레이션
프로젝트 전체에서 grep -r "unsafe.Pointer" | grep "\[\d\+\]byte"로 대상을 찾아 하나씩 교체했다. 테스트 커버리지가 있는 부분부터 진행했고, 약 15군데 정도를 수정했다.
컴파일러가 타입 체크를 해주니 실수할 여지가 줄어들었다. unsafe 사용을 줄이는 것만으로도 코드 품질이 올라간 느낌이다.