[Go 공식문서 한국어 정리] ⓪16. Go Modules 호환성 유지하기
[Go 공식문서 한국어 정리] ⓪16. Go Modules 호환성 유지하기
원문 제목: Keeping Your Modules Compatible
작성자: Jean Barkhuysen; Jonathan Amsterdam
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 1. 서론 — 이 문서가 다루는 내용
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Go 모듈을 발전시키면서 새로운 기능을 추가하고 동작을 변경해야 할 때가 있습니다.
하지만 새 메이저 버전을 릴리스하는 것은 사용자에게 큰 부담입니다.
이 문서는 기존 API를 깨뜨리지 않고 변경하는 기법을 설명합니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 2. 핵심 개념
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
① 함수 추가 전략
함수 시그니처는 어떤 변경도 하위 호환성을 깨뜨립니다.
가변 인자를 추가하는 것도 호환성을 해칠 수 있습니다(함수 타입이 달라짐).
새 기능이 필요하면 새 함수를 추가합니다.
예: database/sql의 Query → QueryContext 추가
② 설정 구조체 활용
crypto/tls.Dial은 *Config 구조체를 인자로 받습니다.
nil을 전달하면 기본값을 사용하고, 필드를 설정하면 해당 값만 오버라이드됩니다.
새 설정 파라미터는 Config 구조체에 필드를 추가하는 것만으로 확장 가능합니다.
③ 함수 옵션 패턴
grpc.DialOption처럼 가변 인자로 옵션 함수를 전달하는 패턶도 있습니다.
각 옵션은 상태를 변경하는 함수입니다.
구조체 옵션과 스타일의 차이이며, 둘 다 합리적인 선택입니다.
④ 인터페이스 확장 전략
인터페이스에 메서드를 직접 추가하면 하위 호환성이 깨집니다.
대신 새 인터페이스를 정의하고, 타입 어설션으로 지원 여부를 확인합니다.
archive/tar.Reader는 io.Reader를 받지만, io.Seeker를 지원하면 더 효율적으로 동작합니다.
⑤ 구조체 호환성 유지
exported 구조체에 필드를 추가하는 것은 대부분 하위 호환입니다.
새 필드의 zero value가 기존 동작을 유지해야 합니다.
net.ListenConfig의 KeepAlive 필드가 예시입니다.
⑥ 비교 가능성(comparability)
구조체의 모든 필드가 비교 가능하면 전체 구조첲도 비교 가능합니다.
비교 불가능한 필드(슬라이스, 맵, 함수)를 추가하면 비교 가능성이 깨집니다.
의도적으로 비교를 막으려면 [0]func() 타입의 필드를 추가합니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 3. 주요 내용 상세
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
함수 시그니처 변경은 언제나 하위 호환성을 깨뜨립니다.
func Run(name string)를 func Run(name string, size ...int)로 바꾸면,
var runner func(string) = yourpkg.Run과 같은 코드가 컴파일 오류를 냅니다.
context 패키지 도입 후 database/sql은 QueryContext를 새로 추가했습니다.
기존 Query 메서드는 낶부적으로 QueryContext를 호출합니다.
이렇게 하면 사용자가 자신의 속도로 새 API로 마이그레이션할 수 있습니다.
인터페이스를 설계할 때는 사용자가 구현하지 않도록 unexported 메서드를 추가할 수 있습니다.
testing.TB의 private() 메서드가 예시입니다.
동작 변경도 사용자를 깨뜨릴 수 있습니다.
json.Decoder가 알 수 없는 필드를 무시하다가 에러를 반환하도록 바꾸려면,
DisallowUnknownFields() 메서드로 옵트인 방식을 제공했습니다.
구조체의 zero value가 의미 있는 동작을 해야 합니다.
그래야 기존 코드가 새 필드를 설정하지 않아도 계속 작동합니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 4. 실전 활용 / 예시
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
라이브러리를 설계할 때는 확장성을 고려하여 Config 구조첲이나 Option 패턴을 사용합니다.
인터페이스를 공개할 때는 unexported 메서드를 추가하여 향후 확장의 자유도를 확보합니다.
동작 변경은 옵트인 방식으로 제공하여 기존 사용자에게 영향을 주지 않습니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 5. 정리
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Go 모듈의 호환성을 유지하는 핵심 규칙은 "추가만 하고, 변경하거나 제거하지 마라"입니다.
함수 시그니처는 변경 불가, 인터페이스는 타입 어설션으로 확장, 구조체는 zero value로 호환성 유지.
극적인 API 변경이 필요할 때만 새 메이저 버전을 고려해야 합니다.
📎 출처 링크
https://go.dev/blog/module-compatibility
#Go #Golang #modules #compatibility #공식문서

오뉴노노 님의 최근 댓글
ㅋㅋㅋㅋㅋ 2019 01.14 잘 읽었습니다 2018 12.30 포인트가 없어서 아직 시작을 못하고있는데요! 글은 잘 읽었습니다! 포인트 쌓고 도전할거에요 2018 12.30