지킬에서 하킬까지 장대한 야크 털 깎기 여정

코드 하이라이트가 안 될 때, 으레 야크 털을 깎으러 가기 마련이다

이 모든 것은 블로그 글에 삽입한 엘름 코드에 코드 하이라이트가 안 된다는 것을 알아차렸을 때 시작되었습니다. 문제의 근원은 간단히 확인할 수 있었습니다. 기존 블로그는 지킬 및 보조 라이브러리를 모아둔 Github Pages 젬을 사용해서 만들었는데, 이 중 코드 하이라이트를 담당하는 rouge 라이브러리가 엘름 문법을 지원하기 이전인 2.2.1 버전으로 고정되어 있었습니다. rouge 버전을 강제로 올려서 간단히 해결할 수도 있었겠지만, 이 참에 아예 Github Pages 젬을 빼버리고 모든 의존성을 최신 버전으로 올려버릴까? 라는 생각이 들었습니다. 그렇게 야크 털 깎기가 시작되었습니다.

그런데 이왕 그럴거면 어설프게 짠 CSS도 고치는게 어떨까? 그런데 이왕 그럴거면 다른 정적 사이트 생성기를 써보는 건 어떨까? 그런데 이왕 그럴거면 최근에 새롭게 배운 언어를 써보면 어떨까? 다행히도 하스켈로 만들어진 정적 사이트 생성기인 하킬은 이미 안정화 단계에 들어섰고, 제가 필요로 하는 기능도 대부분 제공하고 있었습니다. 그리하여 블로그를 하킬로 개편하기로 결정했습니다. 이 시점에서 장대한 야크 털 깎기 여정이 시작될 것을 알았지만 에라 모르겠다하고 첫 발을 내딛었습니다.

하스켈에서는 시행착오를 통해서 프로그램 동작방식을 배울 수 없다

하킬은 제가 처음으로 제대로 사용해본 하스켈 라이브러리인데, 익숙해지는데 생각보다 많이 어려웠습니다. 다른 언어에서 새로운 라이브러리를 살펴볼 때는 먼저 문서를 읽어서 전체적인 흐름을 파악하고, 코드를 조금씩 실행해보면서 제가 이해한 대로 라이브러리가 동작하는지 확인했습니다. 이건 무난하고 일반적인 방식이라고 생각합니다.

문제는 하스켈에서는 이 방법을 사용할 수 없었다는 것입니다. 하킬 문서를 읽은 후 코드를 실행해보려 했을 때, 컴파일러는 제가 작성한 코드 내의 타입이 일치하지 않는다는 에러 메시지만 주었습니다. 정작 제가 작성한 코드가 올바른지 아닌지에 대해서는 아무 것도 알려주지 않는 피드백이었기에 그다지 쓸모가 없었죠. 물론 컴파일러가 제가 무엇인가 잘못했다는 것을 알려주고 있다는 것 정도는 알 수 있었습니다. 하지만 에러 메시지는 제 실수를 하킬 타입을 사용해서 설명하고 있었고, 애초에 그 코드를 작성한 이유가 하킬의 코드와 타입이 어떻게 동작하는지 이해하기 위한 것이었다는 것을 생각해보면 딱히 도움이 되는 피드백은 아니었습니다. 에러 메시지에 나온 하킬 타입이 무엇인지 이해하기 위해서는 그저 문서와 소스 코드를 읽는 것 외에는 다른 방법이 없었습니다. 이건 마치 새로운 수학적 개념을 관련 정리와 증명을 읽어보기만 하면서 이해하려는 것과도 비슷한 느낌이었습니다.

또한 라이브러리를 조금이라도 사용해보려면 라이브러리의 상당 부분을 이해해야만 한다는 문제가 있었습니다. 하킬에서는 IO와 통신하는 인터페이스로 hakyll :: Rules a -> IO () 함수를 제공하는데, 타입에서 보면 알 수 있듯이 이를 사용하려면 Rules a 타입을 이해해야만 합니다. 문서에 따르면 이 타입은 “[하킬 자체의 문서 생성용] 컴파일러를 실행할 때 사용되는 규칙”에 상응합니다. 그리고 Rules a 타입의 데이터는 match :: Pattern -> Rules () -> Rules (), route :: Routes -> Rules (), 및 compile :: (Binary a, Typeable a, Writable a) => Compiler (Item a) -> Rules () 같은 함수를 통해서 생성할 수 있는데, 이걸 사용하려면 또다시 Pattern, Routes, Comipler a, Item a 타입을 이해해야만 하는 등 알아둬야할 내용이 고구마 줄기처럼 연이어서 나옵니다. 물론 이렇게 연결된 개념을 계속 익혀야하는 것은 소프트웨어 분야에서 일반적인 상황이지만, 하스켈의 경우 시행착오를 통해서 조금씩 이해할 수도 없이 한 번에 이 모든 개념을 관념적으로 이해해야 했습니다.

다행히 하킬의 저자는 다양한 예제 및 예시를 제공했고, 이는 라이브러리를 이해하는데 정말 유용했습니다. 만약 그런 자료가 없었다면 라이브러리를 사용할 수 있을 때까지 도대체 얼마나 더 걸렸을 지 알 수 없을 정도로요. 만약 하스켈 라이브러리를 작성할 계획이라면 예시 코드를 최대한 많이 제공하도록 부탁드립니다. 하스켈 라이브러리를 시험해보는 것은 하스켈 특유의 엄격한 컴파일러때문에 어려운데, 이는 특히 초심자에게 더욱 부담으로 다가오므로 그런 어려움을 해소해줄 수 있도록 최대한 많은 도움이 필요합니다.

국제화는 언제나 어렵다

기존 블로그와 마찬가지로 이번에도 구현하는데 가장 어려운 것은 국제화였습니다. 하킬에서 이를 기본 제공하지 않기 때문에 직접 구현해야만 했는데, 구글해서 찾은 기존 구현체는 그다지 마음에 들지 않았습니다. 해당 구현체에서는 원본 마크다운 파일 안에 모든 언어의 텍스트가 들어있었고, 각 텍스트 앞에는 그 언어명이 표기되어 있었습니다. 그리고 사용하지 않는 언어의 텍스트는 언어명을 이용해 확인한 후 유닉스 sed 명령어를 통해서 제거하고 있었습니다. 대충 아래와 같은 구성이었죠.

일단 여러 언어의 텍스트를 섞어놓는 것이 마음에 들지 않았습니다. 난잡해 보일 뿐 아니라 제가 글을 쓰는 방식하고도 맞지 않았습니다. 그래서 새로운 구조를 생각해냈는데, 각 글의 제목에 따른 폴더를 만들고 그 아래에 언어별로 파일을 만드는 방식이었습니다. 예를 들어 이 글의 마크다운 파일은 posts/2019-03-15-from-jekyll-to-hakyll/en.mdposts/2019-03-15-from-jekyll-to-hakyll/ko.md라는 두 파일로 구성되어 있습니다. 일단 해당 파일을 찾아서 적절한 HTML 파일로 변경하도록하는 것까지는 수월했습니다.

하지만 각 글마다 언어 변경을 위한 링크가 필요하다는 문제가 발생했죠. 링크를 만들기 위해서는 하킬 프로그램에서 변환 중인 마크다운 파일이 들어있는 폴더 내에 어떤 파일들이 존재하는지를 알아야 했는데, 이는 하스켈 상에서 IO 타입 내에 표현되어야하는 IO 작업이었습니다. 따라서 main :: IO () 함수 내에서 모든 글의 폴더와 파일의 목록을 작성했고, 마크다운 파일을 변환할 때마다 해당 목록을 활용해서 언어 변경 링크를 생성해야 했습니다.

또한 템플릿 내에 실제로 링크를 만드는 부분에서도 문제가 발생했습니다. 하킬 템플릿에 언어별 링크 텍스트와 URL을 담은 튜플이나 레코드 타입 데이터의 목록을 전달할 수 있었으면 편했을텐데, 하킬 템플릿이 받는 데이터 타입은 Item a 뿐이었는데 이는 특정한 내용과 해당 내용을 가리키는 식별자를 나타내는 타입이었습니다. 따라서 “en” 혹은 “ko”를 식별자로 가지되 내용은 비어있는 Item String 타입의 목록을 만들고, 이를 템플릿에 전달하여 링크를 만들도록 했습니다. Item a 타입을 잘못 사용한 것은 아니지만 링크를 만드는 방법이 불필요하게 번잡하다는 느낌을 지울 수는 없었습니다.

버티컬 리듬과 Rem 기반 레이아웃

버티컬 리듬이란 개념은 기존 블로그를 만드는 과정에서 다른 사람의 을 보고 알게 되었는데, 그 사람의 블로그 글의 자간 및 행간이 너무나 마음에 들어서 허락을 받고 관련 CSS를 복사해왔었습니다. 당시에는 CSS를 잘 몰라서 있는 그대로 통째로 가져왔죠. 하지만 지금은 CSS에도 훨씬 익숙해졌기 때문에 버티컬 리듬을 제대로 이해하고 새 블로그에 직접 적용해보기로 했습니다. 그 과정에서 큰 도움이 된 에 나온 조언에 따라서 기본적인 버티컬 리듬을 적용했는데, 원래 사용하던 CSS의 저자인 Sylvain이 작성한 버티컬 리듬보다는 훨씬 단순하지만 제가 이해하고 관리할 수 있는 정도로 만들었습니다. 또한 레이아웃도 마음에 들었던 다른 블로그에서 허락을 받고 따와서 Rem 기반 레이아웃을 만들었습니다.

깃헙 페이지에서 넷라이피로

호스팅도 깃헙 페이지에서 넷라이피(Netlify)로 옮겼는데, 가장 큰 이유는 넷라이피에서 리다이렉션 룰을 제공하기 때문입니다. 이를 통해 사용자의 HTTP 헤더를 확인하고 /ko/index.html 또는 /en/index.html로 적절히 보내도록 할 수 있게 되었습니다. 깃헙 페이지에서는 해당 기능이 없어서 영어는 index.html에서, 한국어는 index_ko.html에서 제공했는데 처음부터 굉장히 마음에 안 들었었습니다. 게다가 넷라이피는 리포지토리에 새로운 소스 코드를 푸시하면 자동적으로 빌드 및 배포하는 기능을 비롯해 깃헙 페이지의 강점은 모두 가지고 있는데 더해 추가적인 기능도 지원하기 때문에 깃헙 페이지보다 모든 면에서 나은 선택이라고 생각합니다.

엘름 코드 하이라이트는 여전히 안된다는 반전

블로그를 다시 만든 후에야 알아차렸는데 엘름 코드 하이라이트가 여전히 안 되고 있었습니다. 하킬은 마크다운을 HTML으로 변환할 때 Pandoc 이라는 하스켈 라이브러리를 사용하는데, Pandoc은 코드 하이라이트 기능을 위해 Skylighting이라는 라이브러리를 사용합니다. 그리고 이 Skylighting 라이브러리에는 엘름을 다루는 문법 분석기가 없었습니다. 기껏 열심히 하스켈 타입들을 공부하고 사용하고 새로운 CSS 기법도 적용해서 새 블로그를 만들어놨더니 다시 원점이라는 말이었죠. 솔직히 많이 허탈하긴 했는데, 이왕 여기까지 온거 엘름 문법 분석기까지 만들었고, 지금은 Skylighting 라이브러리와 KDE의 코드 하이라이트 라이브러리에서 리뷰를 받고 있는 중입니다.

이렇게까지 해야만 했는가?

결국 블로그 개편은 생각보다 훨씬 오래걸렸는데, 역시나 초보자가 쓸만한 정적 사이트로 하킬을 추천하지는 않을 것 같습니다. 하스켈 특유의 진입장벽때문에 정적 사이트 생성기로 쓰기는 적절하지 않은 것 같고, 차라리 고나 자바스크립트로 만들었다면 더 빨리 만들었을 것 같습니다. 하지만 실제로 하스켈 프로그램을 작성해보기에는 정말 좋은 연습용 프로젝트라고 생각합니다. 그러니 정적 사이트 생성기를 사용하는게 이미 익숙한데 하스켈을 연습해보고 싶다면 하킬을 써보는 것을 추천합니다. 추상화도 직관적이고 예제나 예시도 많이 있으니까요.