카카오헤어샵 마크업 내재화 이야기
안녕하세요, 카카오헤어샵 플랫폼 개발팀 프론트엔드 개발자 조이입니다!
오늘은 프론트엔드 파트에서 진행한 마크업 내재화에 대한 이야기를 해보려고 합니다.
카카오 헤어샵, 파트너스(점주님 서비스)는 기존에 외부 마크업 개발자(퍼블리셔)분들께 html, css 작업을 요청하고 있었습니다. 내부 개발자 분들이 아니다 보니 저희 기술 스택(next.js)에 맞춘 작업물을 요구하기 어려웠고, 그 결과 마크업은 jsx로 다시 작업을 해야 했고, 모든 스타일은 common.scss 한 파일에서 관리하고 있었습니다.
한 파일로 작성하다 보니 공통 요소가 제대로 관리되지 않아, color, font 등 공통으로 사용하는 부분의 수정이 발생했을 때 모든 화면의 css를 고쳐야 했고, 어떤 화면에서 어떤 css를 사용하고 있는지 추적이 어려워 수정사항이 있으면 계속 새로운 클래스의 css 코드를 추가할 수밖에 없었습니다.
또한, 간단한 스타일 작업도 마크업 개발 업체와 소통을 해야지만 버전을 맞출 수 있어 시간이 오래 걸리게 되었습니다.
특히, 이러한 상황은 여러 작업들이 동시에 진행되거나, 배포 순서가 변경되는 경우에 같은 작업을 여러 번 다시 해야 하는 문제를 발생시켰습니다. 빠른 업무 진행을 위해 외부 마크업 개발자를 사용했던 것이 오히려, 프론트 개발자들의 커뮤니케이션 비용을 증가시키고 반복업무를 발생시키게 되었습니다...
이제는 더 이상 물러날 곳이 없다
고민 끝에, 프론트엔드 개발 파트에서는 외부에서 진행하던 마크업 작업을 내재화하자는 결론을 내리게 되었습니다.
하지만, 서비스가 만들어지던 초기부터 지금까지 외부에서 진행해 온 마크업을 바로 내재화하기에는 어려움이 있었습니다. 여러 스타일 라이브러리, 디자인시스템에 대한 좋은 예시들은 많지만, 계속 외부에서 마크업을 받아서 사용했다 보니 우리에게 맞는 방식이 무엇인지에 대한 고민이 필요했습니다. 마크업 내재화를 하면서 크고 작은 기능 개발도 멈출 수 없기 때문에 한 번에 모든 스타일을 분리해서 적용할 시간 또한 부족했습니다.
그 결과, 처음부터 크게 시스템을 만들려고 하지 말고, 순차적으로 진행하면서 부족한 부분을 채워 디자인 시스템을 만들어 나가자는 방향을 정하게 되었습니다..
가장 먼저 고려해야 했던 부분은 마크업을 내부에서 진행할 때, 당장 나가야 하는 기능 개발에 문제가 없도록 하는 것이었습니다.
이를 위해, 하나의 파일로 되어있는 common 파일을 페이지 단위로 쪼개어 각 페이지의 기능 개발을 할 수 있는 환경을 구축하기로 하였고, 마크업 내재화의 과도기적 성격으로 기존에 next.js에서 기본적으로 제공하는 module.css를 활용하기로 하였습니다. module.css로 common에 있던 스타일을 페이지 단위로 구분하였고, 각 스타일 파일은 다른 화면에 영향을 주지 않도록 분리했습니다. 추후 컴포넌트를 제작할 것이기 때문에 공통 요소들은 지금 분리하는 것이 의미가 없다고 판단하여 여러 화면에서 공통으로 사용되는 스타일만 기존 common에 남겨두었습니다.
페이지 단위의 module.css 분리 작업은 모든 프론트엔드 개발자분들이 화면을 나눠 빠르게 작업을 진행해 주셔서 큰 어려움 없이 분리할 수 있었습니다!
다음으로는 기존 디자인에서 공통 요소들을 추출해야 했습니다.
기존에 디자인 → 마크업 개발자 → 프론트엔드 개발자로 작업이 진행되었어서 디자이너 분들과의 소통이 거의 없는 상태였습니다. 이에 디자인팀과 회의를 통해 최소 단위 디자인 요소(공통 theme)들과 각 요소의 네이밍을 정의하였습니다. 회의를 통해 정리한 디자인 요소들은 vanilla-extract의 theme으로 정의하였습니다.
vanilla-extract는 sass처럼 빌드타임에 ts파일을 css파일로 만들어, 런타임에 js파일이 실행되면서 style을 생성하는 css-in-js보다 성능상의 우위를 가져갈 수 있습니다.
sprinkles을 활용하여 tailwind처럼 Atomic CSS를 구성할 수 있습니다.
tailwind와 비교했을 때, 당장 tailwind가 제공하는 클래스 이름을 외워야 한다는 러닝과 여러 개의 className이 작성되어 jsx 코드가 길어지는 것을 지양하고자 했습니다.
또한, recipe를 활용하여 variant 기반 스타일링을 구성하거나, dynamic을 활용해 동적으로 값을 업데이트할 수도 있습니다.
빌드 시 className이 겹치지 않게 로컬 스코프 클래스를 만들어주어, css-in-js의 장점이었던 다른 클래스와 이름이 동일할 경우에 대한 고민을 해결해 줄 수 있습니다.
타입스크립트 지원하여 type-safe 하게 theme를 다룰 수 있습니다.
이러한 장점을 기반으로 vanilla-extract가 당장 큰 러닝커브 없이 도입할 수 있고, 추후 자체 디자인 시스템 작업에도 적합한 도구라고 판단하였습니다.
이제 공통 디자인 요소를 정의하였고, 이를 활용하여 재사용할 수 있는 공통 컴포넌트를 제작하였습니다.
여러 조합으로 확장성 있게 활용하기 위해서 headless 컴포넌트 라이브러리인 radix-ui를 많이 참고하여, compound component(합성 컴포넌트) 형태로 제작하였습니다.
compound component의 내부 상태 공유는 컨텍스트 api로 진행하였고, 여러 디자인 형태가 있는 컴포넌트는 base가 되는 component를 만든 다음 vanilla extract로 작성한 스타일을 받도록 제작하였습니다.
이번 작업을 통해 프론트엔드 개발 컨벤션도 많이 맞출 수 있었고, 오랫동안 방치되고 있었던 사용하고 있지 않는 코드들도 많이 정리할 수 있었습니다. 공통 디자인 요소가 반영된 컴포넌트가 점점 늘어나면서 작업 시간도 단축되고 매번 새로 작성해서 장황했던 코드들의 가독성도 좋아지고 있습니다.
물론 아직 시스템을 맞춰가는 단계이고, 완벽하지는 않습니다.
하지만 마크업 내재화를 위해서는 꼭 겪어야 하는 과정이었다고 생각합니다. 앞으로도 이런저런 문제가 발생하겠지만, 든든한 팀원들이 있어서 그때마다 같이 논의해서 해결해 나가면 되겠다는 자신감도 생기게 되었습니다!(으쌰으쌰 프론트 화이티잉~)
이후에 vanilla-extract사용한 compound component 제작기도 다뤄보도록 하겠습니다.
지금까지 긴 글 읽어주셔서 감사합니다! :-)
좋은 질문? 지적? 모두 감사합니다~