Unknownpgr

블로그 개발을 시작하다👨‍💻[7] - Refactoring

2020-09-25 15:56:00 | Korean

얼마 전 블로그에 글을 적으려다가 큰 문제점을 하나 발견했습니다. 바로 수식이었는데요. 수학이나 물리학 관련 포스팅을 하려면 수식 입력이 필수적인데, 제 블로그 방식에서는 수식을 렌더링하기가 상당히 어려운 문제가 있었습니다. 이번에는 그 문제를 어떻게 해결했는지를 포스팅해보고자 합니다. Refactoring: clean your code

기존 방식의 문제점

기존에는 다음과 같은 방법으로 포스트를 렌더링했었습니다.

  1. 마크다운파일을 작성
  2. 마크다운파일을 html로 변환
  3. 변환된 html을 JSX로 변환
  4. JSX를 동적으로 import하여 렌더링

블로그를 처음 개발할 때에는 React 기능을 최대한 이용해보고 싶었습니다. 또 React에서 html을 삽입하려면 dangerouslySetInnerHTML라는 속성을 이용해야 하는데, 누가 봐도 이용하지 마시오라는 느낌을 풍기고 있어 이 기능을 사용하지 않고 구현했었습니다. 그래서 이런 복잡한 구현 방법을 사용하게 되었는데요.

이 방법의 문제점은 여러 추가 기능을 넣기가 참 힘들다는 점이었습니다. 일단 수학 식을 백엔드에서 렌더링하려고 하니, class를 포함한 각종 속성들을 설정해줘야 하는데 모든 속성들이 .jsx타입으로 잘 변환될지 장담할 수 없었습니다. 이전에 포스팅했었던 이모지 문제도 같은 맥락에서 발생하는데, html을 JSX로 변환했더니 이모지를 제대로 처리하지 못해 warning이 발생했었습니다. 이미지 처리도 상당히 번거로워서 정규표현식을 비롯한 각종 트릭을 사용해서 겨우겨우 처리했었습니다.

그리고 컴파일 시간이 생각보다 길다는 문제도 있었습니다. 처음에는 .jsx.js로 컴파일하는 것이 금방 될 것이라고 생각했었는데, 의외로 시간이 30초~1분 이상 걸려 상당히 오랫동안 기다려야만 했었습니다. 특히 포스트 하나를 컴파일하려면 모든 파일들을 다 새로 컴파일해야 한다는 것이 치명적인 단점이었습니다.

새로운 방식

그냥 간단하게 가기로 했습니다.

  1. 마크다운을 작성
  2. html파일로 저장
  3. JSX에서 동적으로 로드한 html파일을 dangerouslySetInnerHTML속성을 이용하여 삽입 (단 동적 로드는 import가 아니라 fetch를 사용함. 즉, 재컴파일이 필요 없음.)

새로운 방식의 장점

새로운 방식을 적용하면서 사용하던 라이브러리showdown에서 markdown-it으로 바꾸었는데, 이렇게 하니 많은 장점이 생겼습니다. markdown-it은 두 가지 단계로 동작하는데, 하나는 markdown string을 파싱하여 token들로 나누는 단계이고, 그 다음은 이 토큰들을 다시 html로 렌더링하는 단계입니다. 이때 이 두 단계 사이에서 여러 작업을 한다면 많은 것들이 수월해집니다.

파싱

예를 들어 이전에는 Table of Content를 만들거나 이미지, 이모지 처리를 위해 완성된 html파일을 정규표현식으로 파싱했었습니다. 이 방법은 일반적으로 잘 작동했지만, 100% 작동하기란 원리적으로 불가능했습니다. html 문법은 문맥 자유 문법인데 비해, 정규표현식은 정규 문법만을 판단할 수 있기 때문입니다. 예를 들어 attribute 내부에 >기호가 포함되었을 경우, 기존의 처리 방법은 이것을 제대로 된 태그로 인식하지 못합니다. 그런데 markdown-it은 제대로 된 파서를 사용하기 때문에 이런 것들을 완벽하게 구분할 수 있습니다. 따라서 특정 태그를 가져오거나 인식하기 위해서는 Token tree를 DFS하면 됩니다. 그래서 원래는 블로그 관리자 코드가 기본 200줄에 100줄정도 되는 다른 모듈을 3개정도 포함해야 했지만, 현재는 150줄 정도 되는 파일 하나로 줄어들었습니다.

Syntax Highlighter

두 번째로 Syntax highlighter를 이용가능하게 되었습니다. 기존에는 코드를 입력할 때 Syntax highlight가 되지 않았는데, markdown-ithighlight.js를 사용할 수 있는 깔끔한 방법을 제공합니다. 그래서 이제 코드 입력 시 Syntax highlight가 됩니다.

// 코드 하이라이팅 예제
#include<stdio.h>

int main(){
    printf("Hello world!\n");
    return 0;
}

수식 렌더링

세 번째로 markdown-it-katex라는 LaTeX\LaTeX수식 렌더링 플러그인도 있어서 수식도 렌더링이 됩니다. KaTeX\KaTeX라는 웹에서 레이텍을 렌더링하기 위한 라이브러리가 있는데, 이것을 markdown-it에서 사용할 수 있도록 래핑해놓은 플러그인입니다. (그대로 사용하기에는 몇 가지 문제가 있어 직접 수정하여 사용했습니다. 맨 아래 TMI부분을 참조해주세요.)

Let f(x)=f(x)αThen f(x)=exα\text{Let }f(x) =\frac{f'(x)}{\alpha}\\ \text{Then } f(x)=e^{\frac{x}{\alpha}}

이런 간단한 수식 뿐만 아니라, LaTeX\LaTeX에서 지원하는 갖은 복잡한 수식들이 다 지원됩니다.

f(x)=ξ^,e2πiξxdξf(x) = \int_{-\infty}^\infty\hat\xi,e^{2 \pi i \xi x}\,d\xi

이모지

네 번째로 도대체 왜인지 모르겠지만, 이모지를 막 넣어도 오류가 안 납니다.;; 이전에는 <span>으로 둘러주고...별 이상한 과정이 다 필요했었는데.

👩‍💻👩‍💻🤘❤

속도

마지막으로 중복 컴파일이 필요없어졌습니다. 원래는 블로그 구조를 바꾸는 등 큰 작업 뿐만 아니라, 단순히 포스트 하나를 업로드하기 위해서도 블로그의 재 컴파일이 필요했었습니다. 그러나 이제는 포스트를 렌더링할 때 import가 아니라 fetch를 사용하므로 블로그 전체 구조를 바꿀 때에만 재 컴파일이 필요하고 그 외에는 컴파일 없이 블로그 포스트 작성이 가능합니다.

여러가지로 얻는 게 많은 리팩토링이었습니다.

추가로 배운 것 - 폰트 적용법

저는 NotoSans폰트를 좋아해서 아래와 같이 CSS를 설정하여 블로그 전체에 폰트를 적용해두었습니다.

* {
  font-family: "Noto Sans KR", sans-serif;
}

그런데 이렇게 하니 수식의 숫자들이 전부 NotoSans가 되어 매우매우 보기가 싫어졌습니다. 거기다 <code>태그 역시 Consolas등 적절한 폰트 대신 NotoSans가 되어버렸습니다. 한참을 씨름하다가, 웹페이지 전체의 기본 폰트를 설정하기 위해서는 아래와 같이 해야한다는 것을 깨달았습니다.

html,
body,
#root {
  //	...other attrs
  font-family: "Noto Sans KR", sans-serif;
}

<math>태그나 <code>태그는 기본 폰트를 가지고 있습니다. 위의 방법을 사용하면 기본 폰트들을 전부 덮어써버리는 데 비해, 아래 방법을 사용하면 기본 폰트가 있는 태그는 건드리지 않고 다른 태그들의 폰트만을 바꿀 수 있습니다.

TMI

markdown-it-katex 버전

저는 수식 렌더링에 markdown-it-katex라이브러리를 이용했었습니다. 근데 이 라이브러리가 제대로 동작하지 않았습니다. 유니코드(한글)이 들어가면 동작을 안 하고, 줄바꿈(//)도 작동하지 않았습니다.

그런데 katex의 데모 사이트를 확인해보면 한글과 줄바꿈 모두 잘 동작합니다. 그래서 문제를 해결하기 위해 라이브러리를 직접 뜯어봤습니다. 확인해보니 생각보다 간단한 문제였는데, 현재 0.12.0까지 나온 katex라이브러리를 0.6.0버전을 쓰고 있었습니다.(...4년 전 버전입니다...)

라이브러리 깃헙을 확인해보니 이미 katex 버전 업데이트에 대한 PR이 이루어졌고, PR 커밋도 이뤄졌습니다. 그런데 왠진 모르겠지만 npm에서 모듈을 다운받으면 적용이 안 돼있었습니다.

결국 당장에는 해결할 방법이 없어 보이기에 그냥 라이브러리를 통째로 복사해다가 수정해서 사용했습니다.

KaTeX 분수 가로선

그리고 KaTeX\KaTeX에서 작은 분수를 쓰면 가로선이 렌더링되지 않는 문제가 있었습니다. 확인해보니 가로선 굵기가 0.045em이었는데, 웃기게도 크롬에서 0.04em~0.05em사이 굵기의 선이 렌더링되지 않는 에러가 있었습니다. 다행히도 이럴 때를 대비해서인지 KaTeX에 가로선 최소 굵기 옵션이 있었고, 이를 사용하며 문제를 해결했습니다.


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -