무거움의 원인은 마크다운이 아니라 웹엔진이었다
마크다운 에디터를 열어 두기만 했는데 팬이 도는 건 마크다운이 비싸서가 아니에요. 같은 문서를 같은 조건에서 재보면 비용은 렌더링 엔진과 시계에 묶인 루프에서 나와요.

노트북에서 마크다운 에디터를 열어 뒀어요. 아무것도 입력하지 않았는데 팬이 돌기 시작했죠.
처음엔 마크다운 때문이라고 생각했어요. 문서를 계속 다시 그리느라 느린 거라고요.
정말 그럴까요?
같은 조건에서 비교했어요
화면 크기, 렌더링 방식, 문서를 모두 같게 맞췄어요. 7.5KB, 113줄짜리 문서를 열고 25초 동안 아무것도 하지 않았어요. 타이핑할 때도 같은 글자를 같은 속도로 입력했어요.
웹엔진으로 미리보기를 그리는 apostrophe의 유휴 CPU 사용률은 34.6%, 메모리는 378MB였어요. 프로세스는 5개였고요.
웹엔진 없이 같은 문서를 인라인으로 그린 프로토타입은 CPU 0.7%, 메모리 64MB를 썼어요. 프로세스는 하나였어요.
둘 다 마크다운을 그리고 있었지만 결과는 크게 달랐어요.
apostrophe에서 미리보기만 끄자 CPU는 7.0%, 메모리는 133MB로 줄었어요. 앱도, 툴킷도 그대로였어요. 달라진 건 미리보기를 그리는 방식뿐이었죠.
마크다운을 전혀 그리지 않는 텍스트 에디터도 재봤어요. CPU 2.2%, 메모리 79MB였어요.
마크다운을 그리는 프로토타입이 오히려 더 가벼웠어요.
그제야 분명해졌어요. 문제는 마크다운이 아니었어요.
아무것도 안 해도 초당 60번 일하고 있었어요
원인은 미리보기 스크롤을 본문과 맞추는 코드에 있었어요.
이 코드는 스크롤할 때만 실행되지 않았어요. 16ms마다 타이머가 깨어나 WebKit 프로세스에서 자바스크립트를 실행했어요. 응답이 오면 다시 타이머를 걸었고요.
아무도 창을 건드리지 않아도 초당 약 60번씩 일한 거예요.
웹엔진을 쓰면 문서를 편집하는 영역과 미리보기를 그리는 영역이 서로 다른 세계가 돼요. 두 상태를 맞추는 가장 쉬운 방법은 계속 확인하는 거예요. 유휴 CPU를 따로 측정하지 않으면 이 비용은 잘 보이지 않아요.
타이핑할 때 차이는 더 커졌어요. apostrophe는 CPU 180%, 프로토타입은 41%를 썼어요.
한쪽은 키를 누를 때마다 브라우저 프로세스까지 오갔어요. 다른 쪽은 바뀐 줄에만 태그를 다시 붙였고요.
측정 결과를 설계 원칙으로 만들었어요
프로토타입에는 특별한 최적화가 없었어요.
스타일은 텍스트 위에 태그로 입혔어요. 여러 편집은 스스로 사라지는 일회성 idle 하나로 모았어요. 다시 태그를 붙이는 범위는 바뀐 줄로 한정했어요. 프로세스도 하나만 썼어요.
무언가를 더한 게 아니라, 필요 없는 것을 두지 않은 거예요.
이후 두 가지를 설계 원칙으로 정했어요.
첫째, 웹엔진을 의존성에 넣지 않아요.
둘째, 문서가 바뀌지 않고 애니메이션도 없다면 프레임, 타이머, 틱을 예약하지 않아요.
이 원칙은 나중에 붙이기 어려워요. 웹엔진은 단순한 라이브러리가 아니라 앱의 구조를 바꿔요. 한 번 넣으면 걷어내는 일이 곧 다시 만드는 일이 돼요. 타이머도 규칙이 없으면 기능마다 하나씩 늘어나고요.
대신 쉬운 길을 포기해야 했어요. 파일 변경을 폴링으로 확인할 수 없었고, 수식을 웹뷰에 맡길 수도 없었어요. 필요한 레이아웃은 직접 만들어야 했죠.
원칙이 흐려지지 않도록 릴리스마다 실제 기기에서 다시 측정해요.

숫자를 볼 때 주의할 점도 있어요
실험은 소프트웨어 렌더링으로 진행했어요. 실제 GPU 환경보다 CPU 사용률이 높게 나와요. 그래서 절대값보다 비율을 봐야 해요.
프로토타입의 유휴 CPU 0.7%도 대부분 커서가 깜빡일 때 쓴 값이에요. 깜빡임을 끄면 0과 구별하기 어려웠어요.
모든 웹엔진 기반 앱이 같은 문제를 겪는다는 뜻은 아니에요. 이번 결과는 apostrophe에 한정돼요. Chromium 계열처럼 유휴 상태에서 작업을 줄이는 엔진도 있어요.
프로토타입의 기능도 많지 않았어요. 표, 수식, 내보내기, 찾기 기능이 없었죠. 비교할 수 있는 건 렌더링 비용까지예요.
그래도 한 가지는 분명했어요.
비용을 만드는 건 마크다운이 아니었어요. 렌더링 엔진과 멈추지 않는 루프였어요.
원인을 정확히 찾으면 가벼움은 새로운 기능이 아니라 뺄셈이 돼요.