웹 프론트엔드 개발자들의 손가락을 보호해 주는 일등 공신은 역시 에밋(Emmet)이다.
올드 개발자들에게는 젠 코딩(Zen Coding)이라는 과거의 명칭이 더 친숙할 수도 있다.

image 젠 코딩!!!

CSS의 축약 명세(예: div>ul>li*3)만 입력하고 단축키를 누르면 거대한 HTML 블록을 뿜어내던 혁신적인 엔진이다.
이후 ‘Emmet’으로 이름을 바꾸며 스펙을 확장했고, 지금은 VS Code 등 현대적인 IDE에 기본 탑재되어 있다.

요즘은 당연한 공기 같은 기능이지만, 경량 에디터 입장에서는 이 엔진을 네이티브로 이식하는 게 늘 숙제다.

우리의 Notepad4에도 무려 14년 전인 2012년 무렵, JavaScript 코어를 C++로 직접 포팅했던 레거시 엔진이 탑재되어 있다.
그동안 제 역할을 해왔지만 본진의 사양 업데이트를 따라가지 못하고 예전 자리에 그대로 머물러 있었다.

최근 이 14년 된 코어 엔진을 바닥부터 완전히 리뉴얼하는 대수술을 집도했다.
과거의 낡은 구조적 한계를 Modern C++ 기반의 TinyEmmet으로 완전히 새로 짠 기록이다.


1. 14년 전 레거시 아키텍처의 한계: “C++의 탈을 쓴 JavaScript”

당시 포팅을 진행할 때의 C++ 환경은 지금과 완전히 달랐다.
동적 타이핑 언어인 JavaScript의 흐름을 C++로 옮기는 과정은 그야말로 서커스에 가까웠다.

  • 수동 메모리 관리와 댕글링 포인터의 위험:
    vector<CTreeNode*> children 처럼 자식 노드들을 생 포인터(Raw Pointer)의 배열로 관리했다.
    노드가 압축되거나 구조가 바뀔 때마다 루프를 돌며 일일이 delete를 호출해야 했다.
  • 정규식을 흉내 낸 수동 스캔 루프 (iRegExp00 ~ iRegExp13):
    native C++ 환경에서 정규식을 고속으로 처리하기 까다롭던 시절이었다.
    결국 스크립트 엔진의 정규식을 모사하기 위해 iRegExp00 부터 iRegExp13까지 수동 스캔 함수들을 엮어야 했다.
    물론, 지금이라고 std::regex가 나아진 건 아니라는 게 함정
  • 불필요한 컨테이너 오버헤드:
    표준 라이브러리(std::vector)가 성숙하기 전이라 CWstrsStack, CIntStack 은 커스텀 클래스들을 별도로 선언해 썼다.
    문자열 데이터를 수시로 중복 복사하며 메모리와 성능을 낭비한 건 덤이다.

2. TinyEmmet의 탄생: Modern C++ 전면 도입

새롭게 리뉴얼한 TinyEmmet 코어는 모던 C++의 안전성과 고속 성능을 최대한 활용했다.

  • std::unique_ptr를 통한 객체 소유권 독점:
    트리의 부모-자식 관계와 노드 생명 주기를 엄격한 스마트 포인터 계층 구조로 전환했다.
    수동 delete가 사라지면서 구현 파일에 껍데기만 남아있던 빈 소멸자들까지 정리했다.
  • std::wstring_view 기반의 제로-카피(Zero-Copy) 토큰화:
    문자열을 자르고 분리할 때마다 새로운 메모리를 할당하여 복사본을 만들던 과거의 악습을 드디어 버렸다.
    원본 문자열의 주소만 안전하게 참조하는 std::wstring_view를 통해 파싱 속도를 최대한 향상시켰다.
  • 화이트리스트 기반의 정석 문맥 분석기 전환:
    과거에는 “내가 아는 특수기호가 아니면 전부 단어다”라는 식의 위험한 블랙리스트 방식을 썼었다.
    새 엔진은 에밋 명세상 허용된 문자열 규칙만 명확하게 정의하는 화이트리스트 방식으로 파서의 구현 방향을 수정했다.

3. 리팩토링 과정에서 해결한 기존 코드의 구조 결함

단순히 코드만 보기 좋게 고친 것에 그치지 않았다.
30개가 넘는 테스트 세트를 구동하며 숨어 있는 명세 오류들을 정밀하게 사냥했다.

① 중괄호{} 문법의 정상화와 다국어(한글) 무결성 확보

기존 코드는 중괄호{} 자체를 제대로 인식하지 못해 텍스트 컨텐츠를 마크업 내부에 삽입하는 에밋의 기초 문법마저 겉돌았다.
설령 억지로 구동하더라도 link{https://teus.me}처럼 내부 특수기호(:, /)를 태그 경계선으로 오인해 문맥이 파괴됐다.
새 엔진은 중괄호 본문 영역을 안전 구역으로 사전 가드하고, 한글 유니코드까지 온전하게 수용하도록 파이프라인을 재작성했다.

② CSS 축약어 파이프라인 구축

기존 버전은 CSS 단축어 전개 능력이 사실상 전무했다.
포맷터 내부에 @f 하나만 임시 땜질 코드로 심어두고 연명하던 수준이었다.
새 엔진은 콜론 분기 문맥과 w100p, m10-20 같은 수치 결합형 문맥을 독립 분석하는 전용 CSS 드라이버를 구성했다.
더불어 @font-face, @media 등 에밋 사양의 모든 특수 At-Rules 예약어 인터셉트 레이어까지 완비했다.

③ XML 프로파일의 엄격한 빈 태그(Void) 격리 보호

기존 엔진은 HTML5 사양의 공태그 목록(link, img 등)을 XML 환경에서도 구별 없이 공유하는 문제가 있었다.
이 때문에 XML 모드에서 자식과 본문이 있는 일반 태그 모드임에도 이름만 보고 빈 태그로 오인해 <link />로 닫아버렸다.
포맷터 내부의 프로파일별 분기를 완전히 격리하여 XML 고유의 유효성 사양을 만족시켰다.

④ 최신 웹 표준 마크업 데이터 보강

현대 웹 표준 명세인 <source src="" type="" /> 규격 등 과거 유산 시스템에서 누락되었던 데이터 테이블 라인을 확충했다.
이로써 변환 전개 시 내부 핵심 속성들이 실종되던 누락 현상을 차단했다.


4. 결론: 전통의 아키텍처 위에 정돈된 4개의 블록

이번 리팩토링은 “A 부위를 고치면 멀쩡하던 B 부위가 깨지는” 회귀 버그와의 치열한 싸움이었다.
리팩토링을 통해 코드는 훨씬 가벼워졌고, 메모리는 안전해졌으며, 에밋 표준 명세와 다국어 처리는 견고해졌다.

정리된 코어 엔진을 확보했으니, 이제 이 베이스 위에서 사용자 인터페이스를 튜닝할 차례다.

당연히도 다음 단계는 Notepad4와 더불어 Notepad++에 적용하는 것.

image TinyEmmet!!!

카테고리:

업데이트: