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 사용을 줄이는 것만으로도 코드 품질이 올라간 느낌이다.