𝗣𝗮𝗰𝗸𝗮𝗴𝗲.𝗷𝘀𝗼𝗻 𝘃𝘀 𝗚𝗼.𝗺𝗼𝗱: 𝗪𝗵𝗲𝗿𝗲 𝗗𝗶𝗱 𝘁𝗵𝗲 𝗩𝗲𝗿𝘀𝗶𝗼𝗻 𝗙𝗶𝗲𝗹𝗱 𝗚𝗼?
JavaScript에서 Go로 넘어간다면, 한 가지 놀라운 사실을 발견하게 될 것입니다.
package.json 파일에서는 버전이 맨 위에 바로 보입니다. 읽을 수도 있고, 풀 리퀘스트(pull request)에서 수정할 수도 있으며, 검색할 수도 있습니다. 즉, 코드 내부에 존재하는 명확한 사실입니다.
이제 go.mod 파일을 열어보세요. 버전 정보가 없습니다.
이것은 실수가 아니라 선택입니다.
Go는 자체 모듈을 위해 별도의 버전 필드를 사용하지 않습니다. 대신, Go는 git tag를 사용합니다.
Go에서 버전을 설정하려면 다음과 같이 합니다:
- git tag v1.2.3
- git push origin v1.2.3
git tag가 단일 진실 공급원(single source of truth)이 됩니다. 누군가 go get을 실행하면, Go는 적절한 커밋을 찾기 위해 저장소의 태그를 확인합니다.
이 설계에는 강력한 장점이 있습니다. 버전이 잘못된 코드를 가리키는 일이 절대 발생하지 않는다는 점입니다. npm에서는 배포된 코드와 태그가 지정된 소스 코드가 서로 어긋날 수 있습니다. 하지만 Go에서는 버전이 커밋을 가리키는 포인터이기 때문에 두 가지가 동일합니다.
하지만 이 방식은 워크플로우를 몇 가지 측면에서 변화시킵니다:
- 버전을 확인하려면 명령어가 필요합니다. 파일을 보는 대신
git describe --tags를 실행해야 합니다. - 버전 업데이트(version bump)가 코드 리뷰에 나타나지 않습니다. 태그 푸시(tag push)는 코드 변경이 아니므로 풀 리퀘스트에 표시되지 않습니다.
- 로컬 빌드 시 의사 버전(pseudo-versions)이 사용됩니다. 커밋에 태그를 달기 전까지는 v1.2.3와 같은 깔끔한 버전 대신 긴 숫자와 해시 문자열을 보게 됩니다.
- 메이저 버전이 바뀌면 임포트 경로(import path)가 변경됩니다. npm에서는 버전 번호만 바꾸고 패키지 이름은 유지하지만, Go에서는 v2 이상부터
/v2와 같이 경로를 변경해야 합니다. 이를 통해 프로그램이 충돌 없이 동일한 라이브러리의 v1과 v2를 동시에 사용할 수 있습니다.
대부분의 다른 생태계는 파일에 버전을 기록합니다:
- Node: package.json
- Rust: Cargo.toml
- Python: pyproject.toml
- Java: pom.xml
- .NET: .csproj
Go는 다릅니다. 오직 git tag에만 의존합니다.
Go 바이너리에 깔끔한 버전을 표시하고 싶다면, 빌드 과정에서 ldflags를 사용하여 버전을 주입할 수 있습니다. 많은 개발자가 태그 및 릴리스 프로세스를 자동화하기 위해 goreleaser와 같은 도구를 사용하기도 합니다.
두 모델은 서로 다른 우선순위를 나타냅니다:
- 파일 기반 버전은 읽고 리뷰하기 쉽습니다. 하지만 파일이 실제 코드와 일치하지 않을 위험이 있습니다.
- 태그 기반 버전은 조작이 불가능합니다. 대신 확인하고 관리하기가 더 어렵다는 단점이 있습니다.
Go는 매니페스트(manifest)가 항상 코드와 일치하도록 보장하기 위해 태그 모델을 선택했습니다.
Go 코드를 배포하는 분들께: 일상적인 업무에서 태그 전용(tag-only) 모델은 어떻게 느껴지시나요? 만약 바꿀 수 있다면 바꾸시겠습니까?
출처: https://dev.to/dalirnet/packagejson-vs-gomod-where-did-the-version-field-go-3301