우리가 코드를 작성할 때 어떻게 변수를 명명할 것이냐를 많이 생각한다. 그런데 버전 전략도 상당히 중요하다. 예를 들어 개발자용 버전과 배포용 버전을 별도로 관리하는 것도 필요하다. 프론트엔드 개발자라면 Chrome 버전을 트래킹하면서 새로운 베타 버전인 Chromium이 나온다라고 하면 현재 본인이 작업하고 있는 프로젝트가 호환성이 있는 지도 살펴보아야 할 것이다. DevOps를 맡고 있다면, 도커 이미지의 태그가 중요한 정보일 것이다.
Semver는 Semantic Versioning의 약칭으로, 명세서를 의미한다. 구체적인 명세는 전체 링크를 보아야 하지만, 간단히 말하자면 x.y.z
의 체계를 따르는 버전이라고 할 수 있다. 먼저, x
는 주 버전으로 0으로 시작되면 개발 중임을 의미한다. 현재 개발 중인, 즉 불안정한 (unstable) 버전이므로 사용 중간에라도 사용법이 지속적으로 바뀔 수 있다는 암시다. 만약 a
가 1보다 클 경우 각 버전 내에서 API 명세가 많이 바뀌지 않으며, 바뀌더라도 이전 버전에 대한 호환성이 보장될 것임을 예상할 수 있다. 이 말은 이전 버전에 대한 호환성을 걱정해야 하는 순간이라면 1.x
버전을 넘어섰다는 의미가 된다.
그 다음으로, z
라는 수 버전을 보자. API 명세 자체를 변경하지 않고 버그 수정 등 긴급하거나 사소한 수정이 발생할 경우에 버전을 올린다. 만약 x 또는 y
의 버전이 올라갈 경우 z
는 0으로 초기화된다. 그렇다면 y
란 무엇일까? 직전 버전의 API 명세 자체는 그대로 사용할 수 있더라도, z
버전의 업데이트보다는 큰 변경이 있을 때 올린다. 예를 들어, 새로운 API 기능을 추가하거나 기존 API의 제거를 암시 할 경우에 올리게 된다. 외부 개발자에 노출되는 지점 또는 기존 API가 수정되지 않더라도, 내부적으로는 큰 리팩토링을 거쳤다면 버전을 올릴 수도 있다. 대표적인 예시로 의존 패키지 버전이 변경된 경우를 꼽아볼 수 있다.
하지만 Semver도 분명 문제가 있다. 개발자들이 판단하는 버전업의 경계가 다르다는 것이다. 또한, 버전을 올리는 행위 자체가 수동으로 진행되므로 경우에 따라 Semver를 지키려고 하더라도 그러지 못하는 경우가 허다하다는 것이다. 혹자는 주 버전을 기간에 맞추고 다른 버전은 무시하며 개발하기도 하고, 어떤 분은 버그 수정은 z
에 주고 기능 추가는 y
에 주고 기능 제거는 x
에 준다고도 한다. 무엇이 되었든 표준에 대한 적용이 개발 조직 제각각이라는 것이 문제다. 대표적인 예시는 최근 TypeORM 슬랙에서 일어났던 가벼운 언쟁을 살펴볼 수 있겠다.
앱잼 기간에 TypeORM 학습 및 도입을 선택하면서 어떤 버전을 사용해야 할 지 찾아보다가, 가장 설명 자료가 많은 0.2.30 버전을 택하기로 했다. 조금 더 찾아보니, 올해 3월부터 TypeORM 버전이 0.3.x로 올라갔고 API 변화가 큰 모양이다. 누군가가 TypeORM 슬랙에서 아니 0.2에서 0.3으로 버전업을 했는데 semver compatible 해야하는거 아니냐? 어떻게 backward compatibility가 1.0 이상도 아닌데 보장을 안해줄 수 있느냐? 라고 말했다. 여기에 메인테이너 Pleerock 씨의 대답이 명품이다. 원래 0.2.0 버전도 사람들이 상당히 많이 쓰고 있고, backward compatibility를 고민해야 하는 상황이 온 만큼 원래는 0.2.0을 1.0.0으로 출시했어야 하는데 너무 늦었다는 것이다. 그런데 지금 1.0.0을 배포하기는 싫다고 한다. 왜냐하면 앞으로도 라이브러리의 API 명세 변경이 앞으로 더 잦을 것 같아서란다. 모든 프로젝트가 동일하게 semver 규칙을 적용한다고 하더라도 프로젝트 별로 적용되는 방법이 다른 만큼 실질적 의미가 퇴색되는 것 같다.
LINE에서는 이러한 문제를 방지하고자 headver를 만들어서 주 버전 이외의 버전을 다른 자동화된 정보로 처리했다. 주 버전의 의미는 그대로 같고, yyww의 2자리 년도와 해당 주를 2자리수로 표현한 날짜, 같은 기간내 실행된 증가하는 빌드 번호로 구성했다. 이렇게 하면 주 버전만 수동으로 관리하고, 나머지는 기간과 순서의 의미를 전달함으로써 버전이 일정 기간과 비슷하게 연결되는 점을 활용할 수 있다. 주 버전은 사용자에게 공개되는 단위로 제안해서 사용자 입장에서 버전 변경의 고민을 최소화하도록 만드는 전략도 생각해볼 수 있겠다.
semver의 문제는 3단계의 버전을 각각 가지고 있는데 각 버전을 개발자가 수동 관리한다는 것이 문제다. headver는 수동 관리용 버전을 1개로 줄여서 논쟁을 피했지만, 어떤 방식을 택하더라도 semver의 가장 큰 문제는 실제 코딩에서의 변경에 대한 의미를 판단하여 개발자가 수동
으로 관리해야 한다는 점이다. 따라서 문서화 및 버저닝 작업을 전문적으로 수행하는 테크니컬 라이터나 데브옵스의 직군이 소프트웨어 제품 서빙에 있어 상당히 중요한 역할을 수행한다고 볼 수 있다.
그럼에도 semver의 자동화 작업이 필요하다면 semantic-release-action 를 사용해볼 수 있다. 본 단락의 제목인 semantic-release는 정형화된 커밋 메세지를 활용하여 semver를 자동으로 작성해 줄 수 있다. 예를 들면 커밋 메세지에 BREAKING CHANGE 라는 글자가 있다면 주버전을 올리게 된다. 버그 수정, Hotfix, API 개선 등 실제 작업 내용을 바탕으로 커밋 메시지를 작성하면 된다. 팀 내부에서 커밋하게 될 활동 내역과 카테고리를 사전에 정의하고, 합의된 활동 이름을 각 semver 버전에 연동되도록 작성하면 동작한다. 차후 장기 사이드 프로젝트를 하게 된다면 semver 자동화를 통한 버저닝 전략을 고민하겠다는 교훈을 얻었다.