hELLO를 처음 릴리스하고 약 4년의 시간이 지금, 어느덧 깃허브 스타 2K를 달성했다. hELLO는 개발을 시작하고 얼마 안 됐을 무렵, 깃허브에서 다양한 오픈소스를 접하면서 나도 한 번 스타를 받아보고 싶다는 생각과 나만의 개발 블로그를 운영하고 싶다는 마음이 겹쳐 이 기회에 스킨도 만들어볼까 싶어 막연하게 만들어진 티스토리 스킨이다. 당연히 처음에는 네이버 블로그, 워드프레스 등의 고려가 있었지만 낮은 진입 장벽과 디자인 자유도가 있는 티스토리는 내가 시작하기에 적합하고 판단했다. 내가 티스토리를 처음 접했을 때는 지금과는 달리 초대장 시스템이어서 폐쇄형 블로그였는데, 초대장을 달라고 댓글을 적었던 기억이 새록새록 난다.
hELLO의 깃허브 스타가 2K가 된 기념으로 그 시작점이 되었던 티스토리 스킨 프레임워크인 티도리 프레임워크에 대한 얘기를 해보고자 한다. 티도리 프레임워크는 2017년에 시작되었다. 첫 릴리스로부터 벌써 8년이 지나버렸다. (...) 처음 티스토리 스킨을 만들면서 접했던 문제가 있었다면, 단언컨대 최악의 개발 편의성이 가장 큰 걸림돌이었다. hELLO는 2020년에 처음 릴리스 되었지만, 티스토리 스킨을 만들겠다는 시도 자체는 2017년에 처음 했었는데, 그 당시 프론트엔드에서는 제이쿼리(JQuery)는 지고 뷰(Vue)와 리액트(React)와 같은 자바스크립트 프레임워크 및 웹팩(Webpack)으로 대표되는 에셋 번들링이 떠오르고 있던 실정이었다. 티스토리 스킨은 skin.html 파일 하나에 스킨과 관련된 모든 정적 마크업이 있어야 한다는 특수한 조건으로 인해 오픈소스 기술을 사용하려면 CDN(Content Delivery Network)으로 포함하거나 Bower와 구식 패키지 매니저를 사용해야 한다는 점이 마음에 들지 않았어서 스킨보다는 일단 프레임워크를 만들어야겠다는 생각이 먼저 들었었다.
처음 해결해야 할 과제는 관심사에 따라 파일을 분리하는 것이었다. 개발 과정에 사용하는 소스코드임에도 하나의 파일에 모든 마크업이 포함되는 것은 참을 수 없는 문제였기 때문이다. 일단 선결 과제로는 전통적인 티스토리 스킨 개발 방식으로는 에셋 번들러를 사용하여 템플릿을 컴파일 및 번들링을 하지 않았으므로 파일을 분리하기는커녕, .vue, .jsx와 같은 템플릿도 사용하기도 어려웠다는 것인데, 이 문제는 그 당시 가장 진보된 기술이었던 웹팩(Webpack)을 번들러로 사용하고 템플릿을 컴파일하는 것으로 해결했다. 웹팩을 사용하여 컴파일하는 것은 본질적으로 프로젝트 템플릿에는 포함될 필요가 없기 때문에 별도의 CLI 프로젝트로 분리하였다.
처음에는 직접 웹팩 공식문서를 읽으면서 설정을 하나씩 음미하며 .vue, .jsx와 같은 파일을 해석할 수 있도록 했지만, 아주 큰 문제가 있었는데, 자바스크립트, 정확히는 skin.html의 script 태그 내부에 작성되지 않고 별도의 파일로 분리된 자바스크립트에서는 티스토리 치환자를 해석하지 못하는 것이었다! 이는 어찌 보면 당연한 것이었을지도 모른다. 머리를 싸매고 많은 고민을 거듭했지만 결국 이 벽을 넘지 못하고 첫 고배를 마셨다. 실패한 이후, 티스토리 스킨은 자바스크립트 프레임워크라고는 제이쿼리를 사용하는 것이 그나마 최선에 가까운 것이라 여기게 되었고, 제이쿼리와 같은 방식으로 이미 렌더링 된 마크업을 직접 참조하는 것이 적합하다고 판단했다. 실제로 그 당시 티스토리 플랫폼의 내부 코드도 살펴보면 제이쿼리로 작성되었기 때문이다. 물론 이 생각은 지금도 딱히 변하진 않았다. 단지 그 결은 같지만 제이쿼리가 다른 것으로 교체되었을 뿐이다.
하지만 아직 포기하기엔 이르기에, NuxtJs 와 같은 SSG(Static Site Generator)에서 영감을 얻고, .vue, .jsx와 같은 파일을 사용하더라도 자바스크립트로 DOM Tree를 구성하는 것이 아니라 그저 템플릿으로써 취급하고, 컴파일하면 빌드 결과로써 skin.html 에 모든 마크업 요소들이 포함된 완전한 정적(Static) HTML로 빌드되는 것을 시도했었는데, 역시 이 시도 또한 실패하였다. 따지고 보면 뷰나 리액트는 런타임(Run Time)에 동적 바인딩을 기반으로 하는 SPA(Single Page Application)을 전제로 하는데 반면, 티스토리 스킨을 등록할 때 필요로 하는 것은 이미 컴파일 타임(Complie Time)에 빌드된 완전히 정적인 HTML이기 때문에 기술적 특성상 적합하지 않았던 것이다.
두 번째 시도까지 실패한 이후 거의 반쯤 포기상태였는데, 자바스크립트 프레임워크가 아님에도 불구하고 간단하게 관심사에 따라 파일을 분리할 수 있으면서도 낮은 진입장벽과 높은 자유도가 있는 PugJS, EJS와 같은 템플릿 엔진(Template Engine)을 알게 되면서 희망을 찾게 되었다. 특정 자바스크립트 프레임워크를 사용하면 러닝커브가 상승할 뿐만 아니라 프로젝트의 복잡성이 상승하고, 본질적으로 해당 자바스크립트 프레임워크에 대한 큰 의존성을 가지게 되며, 개발자의 기술적 자유도가 일정 부분 희생된다는 단점이 있는데, 템플릿 엔진은 그저 단순하게 HTML을 보다 간편하게 작성하고, 개발 과정에서 파일을 분리할 수 있으며, 컴파일 과정도 아주 쉽기 때문에 부가적인 기능이 거의 없어 알아야 할 내용도 적고, 코드가 읽기 쉽고 직관적이라는 점에 주목했다. 다른 이유보다도 티스토리에 업로드하기 위한 '완전한 정적 HTML 생성'이라는 목적에 완벽하게 부합했다. 이 당시 얻었던 큰 깨달음 중 하나는, 기술은 그저 도구일 뿐이므로 남들이 좋다고 해서 다 좋은 것이 아니라 프로젝트와 환경에 따라 적재적소 하게 써야 한다는 것이었다.
템플릿 엔진을 사용하는 것으로써 관심사에 따라 파일을 분리한다는 이점을 누릴 수 있으면서도, 자바스크립트 프레임워크로 프레임워크를 구성하는 것과는 달리 그 많은 티스토리 치환자와 속성을 임의의 내부 컴포넌트와 상수, 옵션 등으로 만들어낼 필요가 없었으며, 자체적으로 별도의 사용방법을 정의하여 긴 내용으로 문서화할 이유도 없었다. 이로 인해 스킨 개발자의 입장에서는 배워야 할 내용이 거의 없으므로 진입장벽이 낮아지고, 많은 시간을 들여 프레임워크를 익히려고 공들이지 않아도 되었다. 이 얘기는 즉, 약 한두 시간 정도의 조금의 시간을 들여 Pug의 문법을 익혀두기만 하면, 그 이후부터는 그냥 티스토리 스킨 가이드에 있는 대로 고스란히 마크업 하면 대체로 작동한다는 뜻이다. 최근 들어서는 리액트 컴포넌트로 티스토리 스킨 개발 프레임워크를 구성한 사례도 찾아볼 수 있는데, 프레임워크에서 정의한 컴포넌트와 상수, 옵션과 같은 존재들로 인해 사용법에 대한 문서가 제법 긴 것을 볼 수 있다.
그래, 이제 파일을 분리하는 것은 좋은데, Vue의 SFC(Single File Component)처럼 특정 관심사에 해당하는 하나의 파일에 그와 관련된 HTML, CSS, JS 가 모두 포함되어 있으면 더 좋지 않을까 싶은 바램이 있었다. 그래서 웹팩 플러그인을 따로 만들고 템플릿 내부에서 사용된 style, script 태그는 컴파일 과정에서 각각 style.css, script.js로 분리하도록 처리했다. 플러그인의 코드를 보면 알겠지만 정말 코드가 별거 없고 단순하다. 개인적으로 자바스크립트 프레임워크 중에서는 Vue를 선호했었기 때문에 템플릿 엔진을 사용하기 전에 했던 시도에서는 Vue 템플릿으로 어떻게든 해보려고 고민을 많이 했었던 기억이 있다. 지금 생각해 보면 자바스크립트 프레임워크보다는 범용적인 템플릿 엔진을 선택한 것은 잘한 일이었다.
그다음으로 해결해야 하는 과제는 프리뷰 서버였다. 개발 서버를 실행하면 티스토리 치환자가 해석되지 않고 그냥 렌더링 되기 때문에 실질적으로 개발 편의성 향상에 큰 도움이 되진 못한다는 것이 큰 숙제였다. 그래서 내가 내린 결론은, "티스토리 스킨 편집의 프리뷰 과정을 뜯어보자!"였다. 이는 어찌보면 일종의 리버스 엔지니어링(Reverse Engineering)인데, 티스토리 스킨 편집에서 치환자가 포함된 마크업을 티스토리 서버에 전송하면 프리뷰가 응답으로 던져지는 것에 대한 집중적인 탐구였다. 개발자 도구를 열고 네트워크 탭을 뜯어보는 계기가 되었고, 결국에는 티스토리 스킨에 대한 "비"공식 API까지 만들어내기에 이르렀다. 이로써 로컬에서 티스토리 스킨을 "치환자가 해석된 상태"로 프리뷰 하는 것까지 성공했다.
에셋 번들링, 템플릿 엔진, 프리뷰 서버까지 해결했고, 자바스크립트 프레임워크는 표면적으로나마 웹팩으로 Vue, React, Svelte 컴포넌트를 커버하고 Pug에서 그들의 컴포넌트를 렌더링 할 수 있도록 하는 것까지는 처리하였지만, 여전히 컴포넌트 내부에서는 티스토리 치환자를 해석하지 못하는 한계점이 있어서 실질적으로 스킨에 영향을 미치기 위해 사용하는 자바스크립트 프레임워크로는 구닥다리 제이쿼리를 쓴다는 점이 마음에 걸렸는데, 이 문제는 제이쿼리와 결은 비슷하지만, 더 모던한 방식으로 처리할 수 있는 AlpineJS 가 시원하게 해결해 주어서 이제 가려운 곳은 없어졌다. AlpineJS 는 배우기 아주 쉬울 뿐만 아니라 경량급 컴포넌트를 통해 속성 및 데이터 바인딩, 이벤트, 상태관리까지 제공해 주기 때문이다.
마지막으로 빌드 및 배포에서 신경 썼던 것은 엔드 유저를 배려하는 것이었다. 프레임워크 개발자인 내가 있고, 그 프레임워크로 스킨을 만드는 개발자가 있고, 빌드된 스킨을 사용하고 커스텀하는 엔드 유저가 있는데, 이들에 대한 배려도 당연히 해주어야 한다. 그 배려란, 빌드 결과로 나온 파일은 티스토리에 업로드하면 바로 적용이 가능해야 한다는 것과, 프레임워크를 사용하지 않더라도 스킨의 커스터마이징이 가능해야 한다는 것이다. 대체로 자바스크립트 프레임워크에서는 빌드 이후에는 결과물을 건드리지 않는게 보편적이지만, 티스토리 스킨의 경우에는 실제로 많은 사용자들이 skin.html, style.css 등에 바로 커스텀을 진행하기 때문에 벤더 코드를 제외하고는 빌드 결과가 한 줄로 직렬화되거나 읽기 어렵게 난독화되어서는 안 된다. 엔드 유저가 스킨을 커스텀하기 위해 프레임워크 사용을 강제하는 일은 접근성을 저해할 뿐이다. 커스텀을 위한 프레임워크 사용은 필수가 아니라 옵션이 되어야 한다는 것이 내가 그들을 위해 할 수 있는 최대한의 배려였다.
지금은 티도리 프레임워크를 완성 상태라 판단하고 신기능이나 기술 도입은 딱히 하지 않고 유지만 하는 상태로 놔두고 있다. 프레임워크를 사용함으로써 얻을 수 있을 것이라 기대했던 개발 편의성과 요구사항을 모두 충족했기 때문에 티스토리 스킨을 개발하는 데에는 큰 문제가 없기 때문이다. 이는 hELLO를 개발하면서 지속적으로 테스트하고 있다. 만약 코어 부분을 바꾼다고 한다면 에셋 번들러 정도일 듯싶다. 지금에 이르러서는 웹팩은 다소 구시대적이기 때문에 Vite와 같은 번들러로 바꾸는 것도 나쁘진 않아 보인다.