디자인 시스템을 관리하며 가장 중요하게 여기는 가치는 사용자가 체감하는 안정성과 만족도입니다. 사내 패키지는 일반 서비스와 달리 이탈률이 높진 않지만, 시스템의 불안정함은 곧 제품 개발자들의 업무 효율 저하로 직결되기 때문입니다.
최근 디자인 시스템의 대대적인 리뉴얼을 진행하며 알파 버전의 실험적 기능들을 정식 버전으로 안정화하는 과정을 거쳤습니다. 이 과정에서 기존 기능과의 호환성이 깨지는 브레이킹 체인지(Breaking Change)가 발생했습니다. 처음부터 완벽하게 설계해서 변경할 부분이 없었다면 더 좋았겠지만, 아무래도 실무에서는 다양한 변수가 존재하기 때문에 현실적으로 어려운 일인 것 같아요.
그래서 중요한 것은 변화의 유무가 아니라, 그 변화를 얼마나 안전하게 전달하느냐인 것 같습니다. 이번 포스트에서는 리뉴얼 과정에서 브레이킹 체인지를 보다 안전하게 제공하기 위해 시도한 세 가지 시행착오를 공유합니다.
예측 가능한 버전
기존에는 스프린트 주기에 맞춰 버전을 업데이트했습니다. "새 스프린트가 시작되면 새 버전을 설치한다"는 규칙 덕분에 관리 기준은 명확했고, 사용자들이 최신 버전을 빠르게 설치하도록 유도할 수 있었습니다. 하지만 버전의 숫자는 단순히 스프린트가 진행됨에 따라 무의미하게 증가할 뿐, 그 이상의 의미를 담지 못한다는 문제가 있었습니다.
숫자에 의미가 부재하니 여러모로 혼란스러운 상황이 발생했습니다. 사용자들은 새 버전이 기존 코드와 호환되는지 예측하기 어려웠고, 어떤 변경 사항이 포함되었는지 직관적으로 파악할 수도 없었습니다. 결국 새로운 버전을 설치하는 것에 대한 심리적 부담을 느끼는 경우가 많았고, 오히려 꼭 필요한 경우가 아니면 새로운 버전을 설치하지 않는 상황이 발생하기도 했습니다.


그리고 이는 비단 사용자만의 문제가 아니었습니다. 디자인 시스템을 관리하는 메인테이너들도 숫자만 보고서는 해당 버전의 대략적인 정보조차 파악할 수 없었습니다. 결국 릴리즈 노트를 꼼꼼히 읽지 않는 한 직접 버전을 올려봐야만 변경 사항을 알 수 있었고, 그때는 이미 예기치 못한 브레이킹 체인지를 마주한 후가 될 수도 있는 것입니다. 이렇게 사용자가 직접 문제에 부딪힐 수밖에 없는 환경은 버전 업데이트에 대한 심리적 불안감을 불러일으켰고, 이는 곧 시스템에 대한 신뢰 하락을 의미했습니다.
결국 중요한 것은 사용자가 자신의 행동에 따른 결과를 예측할 수 있게 하는 것입니다. 이를 위해 시멘틱 버저닝(Semantic Versioning)을 도입하여 보다 체계적으로 버전을 관리하기 시작했습니다. 시멘틱 버저닝은 소프트웨어 버전을 관리하는 방법론으로, 변경 범위와 성격에 따라 Major, Minor, Patch 세 가지 숫자로 버전을 구분합니다. 각 숫자는 다음과 같은 의미를 가집니다.
- Major: 호환성이 깨지는 변경 (Breaking Change)
- Minor: 하위 호환성을 유지하는 기능 추가
- Patch: 하위 호환성을 유지하는 버그 수정
이를 통해 사용자는 이제 버전 숫자의 변화만 보고도 기존 코드와의 호환 여부를 직관적으로 인지할 수 있습니다. 브레이킹 체인지의 유무를 미리 인지한 상태에서 업데이트를 진행하기 때문에, 이전보다 심리적 부담을 덜고 훨씬 안정적으로 새로운 버전을 도입할 수 있게 되었습니다.
2. Migration CLI
시멘틱 버저닝을 통해 사용자가 변경 사항을 충분히 예측할 수 있게 되었지만, 그것만으로는 여전히 한계가 있었습니다. 브레이킹 체인지가 있다는 것을 미리 인지하기만 할 뿐, 이를 실제 코드에 적용하려면 결국 릴리즈 노트나 에러 로그를 살피며 직접 부딪쳐야 했기 때문입니다.
이 과정에서 발생하는 불편함을 해소하고자 Next.js나 Chakra UI 같은 오픈소스 라이브러리의 마이그레이션 전략을 벤치마킹했습니다.
# next.js
npx @next/codemod@latest next-image-experimental .
# @chakra-ui/react
npx @chakra-ui/codemod upgrade위와 같이 메이저 버전이 업데이트될 때마다 브레이킹 체인지를 자동으로 코드에 반영해 주는 CLI 도구를 제공하고 있었습니다. 이에 영감을 얻어 jscodeshift 같은 AST(추상 구문 트리) 기반 도구를 활용해, 명령어 한 번으로 모든 변경 사항을 일괄 수정할 수 있는 CLI 도구를 구축했습니다.
이를 통해 변경 사항을 가장 잘 아는 디자인 시스템 관리자가 직접 스크립트를 작성하기 때문에 가장 정확도가 높았습니다. 또 사용자는 마이그레이션에 소요되는 시간을 대폭 줄일 수 있기 때문에 일석이조였습니다. 실제로 CLI를 제공한 이후, 사용자들은 비교적 어려움 없이 변경 사항을 적용하는 것을 확인했습니다.
3. JSDoc
모든 것이 완벽하게 해결되었다고 생각했으나, 예상치 못한 부작용을 마주했습니다. 사용자들이 무리 없이 버전을 업그레이드했음에도 불구하고, 변경 사항 자체에 대한 문의가 생겨나기 시작했다는 것입니다. 이유인 즉슨, CLI가 코드를 대신 수정해 주다 보니, 사용자는 이 변경이 왜 필요한지, 어떤 상황에 적용되는지 등의 맥락을 충분히 이해하지 못했기 때문이었습니다. 결국 마이그레이션 CLI가 당장의 수고는 덜어주었을지언정, 사용자가 새로운 인터페이스를 완전히 숙지하고 지속적으로 사용하게 만든다는 과제는 해결하지 못했습니다.
이를 해결하기 위해서 개발자의 작업 흐름을 방해하지 않으면서도, 정확한 타이밍에 문맥 정보를 제공할 수 있는 방식이 필요했습니다. 그 해답이 바로 JSDoc 문법입니다. JSDoc의 여러 태그를 이용하면 사용자들의 IDE 환경에서 다양한 시각적 피드백을 제공할 수 있고, 마우스 호버 시 해당 요소와 관련한 구체적인 설명을 보여줄 수 있기 때문입니다. 덕분에 사용자는 별도의 문서를 찾아보지 않고도 개발 흐름을 유지하며 자연스럽게 문맥 정보를 인지하게 됩니다.
우리는 새로운 인터페이스와 기존 인터페이스를 일정 기간 공존시키되, @deprecated 태그를 활용해 기존 방식이 향후 제거될 예정임을 명시했습니다. @deprecated 태그는 이름에서 유추할 수 있듯이, 특정 요소가 향후 제거될 예정임을 나타냅니다. 그리고 사용자의 IDE에서는 아래와 같이 삭선을 표시해줍니다. 덕분에 사용자는 평소처럼 코드를 작성하는 과정에서 자연스럽게 새로운 인터페이스로의 전환을 준비할 수 있게 되었습니다.


이 과정을 거치며 사용자는 어떤 코드가 사라지고 무엇으로 대체해야 하는지 충분히 숙지하게 됩니다. 변경 사항에 대한 이해도가 높아진 상태에서 앞서 제공한 CLI 도구를 실행하면, 안전하고 빠르게 전체 코드를 마이그레이션할 수 있습니다. 마이그레이션을 완료한 이후에도 변경점에 대해 충분히 이해하고 있기 때문에 큰 어려움 없이 패키지를 사용할 수 있습니다.
이렇게 세 가지 단계를 거치며 나름대로 안정성과 편의성을 어느정도 확보한 마이그레이션 전략을 완성할 수 있었습니다.
맺음
디자인 시스템을 리뉴얼하며 실험적인 기능을 다양하게 추가하다 보니, 브레이킹 체인지가 발생하는 상황이 꽤 잦았습니다. 처음에는 이를 최대한 피하려고도 했지만, 아직까지는 알파 버전이니까 여러가지를 시도해보자 했던 팀원들의 조언 덕분에 오히려 의미 있는 경험을 한 것 같습니다.
이제 디자인 시스템이 정식 버전으로 출시된 만큼, 가급적 브레이킹 체인지를 배제하며 하위 호환성을 지키는 것이 더 중요할 것입니다. 하지만 언제나 예기치 못한 상황은 발생할 수 있기 때문에, 이번의 경험이 언젠가는 큰 도움이 되는 순간이 올 것이라 생각합니다.
제가 겪은 이 일련의 과정이 라이브러리나 패키지를 관리하며 비슷한 고민을 이어가는 분들에게 작은 도움이 되기를 바랍니다.