Zig로 타입 안전한 HTML 템플릿 만들기

Type-safe HTML templating in Zig

lalinsky.com ▲ 238 댓글 85 lukaslalinsky

요약

Go의 Templ을 본떠 만든 Zig용 템플릿 엔진 zt는 템플릿 실수를 컴파일 오류로 잡아 냅니다.

2000년 PHP 3.0 시절부터 웹을 만들어 온 개발자 루카시 랄린스키의 글입니다. 2년 전 Zig로 API 백엔드 라이브러리를 만들기 시작한 그는 HTML 화면까지 Zig로 만들려다 마땅한 템플릿 도구가 없어 직접 만들었습니다.

왜 중요한가

  • 템플릿 속 오타나 잘못된 타입이 실행 중 오류가 아니라 빌드 단계에서 드러납니다.
  • 아레나 할당기와 htmx 덕분에 Zig 웹 개발이 가능해진 상황에서, 마지막으로 빠져 있던 템플릿을 채웠습니다.

핵심 내용

  • 템플릿은 zig build 때 Zig 코드로 바뀌고, 매개변수에 타입이 붙어 컴파일러가 검사합니다.
  • {u.nmae} 같은 오타는 필드가 없다는 컴파일 오류로 나오고, 템플릿의 원래 줄과 열도 알려 줍니다.
  • 동적인 값은 기본으로 이스케이프(특수문자를 안전한 문자로 바꾸는 처리)되고, 그대로 내려면 {!value}로 따로 표시해야 합니다.
  • 생성된 코드는 메모리 할당 없이 std.Io.Writer에 바로 써서 소켓이나 버퍼로 곧장 내보냅니다.
  • 줄 첫머리의 if를 코드로 읽어 일반 문장이 오류를 내는 문제가 있어, @if 표기를 도입할지 의견을 받고 있습니다.

HN 반응

  • 저수준 언어 웹 개발은 1990년대 C·C++ 시절로 돌아간 것이라는 지적에, 작성자는 자신에게 웹은 늘 스크립트 언어의 몫이었다고 답했습니다.
  • Kotlin의 kotlinx.html, Java의 jte 같은 비슷한 도구 소개가 이어졌고, 별도 템플릿 파일을 코드로 바꾸면 IDE 지원을 잃는다는 아쉬움도 나왔습니다.

댓글

30개 표시 · 전체 85개
  1. pjmlp HN

    2년 전에 Zig를 접했는데, 이렇게 저수준 언어로 웹 개발을 고려하게 될 줄은 상상도 못 했습니다.

    글쓴이가 2000년에 PHP 3.0으로 웹 개발을 시작한 사람이라는 점을 생각하면 좀 웃긴 말입니다.

    저는 1997년에 시작했는데, 분산 시스템 수업 과제가 Perl과 C로 CGI를 짜는 것이었습니다.

    1999년부터 2001년 사이 제가 다니던 회사의 웹 개발은 대부분 Tcl과 C였고, ASP는 당연하다는 듯 VB와 C++ COM 컴포넌트가 섞여 있었습니다. 게다가 mod_something 식의 Apache 확장과 IIS ISAPI 모듈을 짜는 재미도 있었는데, 물론 이것도 C와 C++로 했습니다.

    아, 마이크로소프트는 ATLServer도 내놓았습니다. 웹 개발에 ATL을 쓰고 싶지 않은 사람이 어디 있겠습니까. 볼랜드/인프라이즈도 Delphi와 C++ Builder용으로 비슷한 걸 내놓고 있었습니다.

    PHP 3.0만 해도 내장 기능이 거의 없던 시절이라 C로 확장 모듈을 짜는 게 아주 흔했습니다.

    그러니 2010년대에 느려터진 스크립트 언어가 판을 먹기 전의 방식으로 돌아간다는 걸 아무도 생각해 보지 못했다는 글을 읽으니 좀 아이러니합니다.

  2. hermitdev HN

    저는 2000년부터 포춘 500대 기업에서 인턴을 했습니다.

    주로 사내 서버, 앱, 데이터베이스를 추적하는 인트라넷 페이지를 만들었습니다. 당시로서는 꽤 멋졌던 기능 하나가 대시보드에 표시된 서버들을 서버 쪽에서 렌더링하면서 핑을 보내는 것이었습니다. 한 번에 보통 수백 대 규모였습니다. 사이트는 IIS와 NT 4에서 돌았고 VBS와 ASP로 작성했습니다. (제 첫 서버는 사실 듀얼 Alpha 칩이었던 것 같습니다. x86도 아니었고, x64보다 한참 전이었던 건 확실합니다.)

    핑 기능(네, 그냥 평범한 ICMP 핑입니다)을 구현하려고 C++로 COM 서버를 만들어 VBS 쪽에서 호출했습니다. 그러다 .Net이 나왔고 .Net Framework 1.1이 출시되자 상사를 설득해 사이트 전체를 ASP.Net으로 다시 짰습니다. C++ COM 객체를 버리고 C#에서 raw 소켓으로 구현할 수 있었습니다(적절한 권한을 설정하는 게 좀 골치 아팠는데, 구글이나 스택오버플로가 도와주기 전이었으니까요). 오히려 C++ COM 객체를 동작시키는 쪽이 더 쉬웠습니다. 적어도 MSDN 라이브러리(CD와 DVD 한 무더기에 담긴 것)에 문서가 풍부했으니까요. .Net 쪽은 처음엔 좋았지만, 그 시점의 C++ 쪽이 이미 갖춘 깊이는 없었습니다.

  3. lukaslalinsky HN

    저한테 웹은 늘 이런 스크립트 언어의 영역이었습니다. CGI 시절에도 간단한 웹 폼을 만들 정도로만 Perl을 익혔습니다. 이 분야는 임시 문자열과 딕셔너리를 많이 다루는 일이라 GC 언어와 묶어서 생각하게 됐습니다. 실제로 제가 웹 프로젝트에 처음 쓴 컴파일 언어는 Go였는데, 그마저도 가비지 컬렉터가 있어서였습니다.

  4. pjmlp HN

    그렇군요, 살아온 경험이 다르니 그럴 만합니다.

  5. kccqzy HN

    구글은 아마 지금도 웹 개발의 상당 부분이 C++로 이뤄질 겁니다. Java보다는 적겠지만 그래도 코드가 많습니다.

  6. flossly HN

    저는 Kotlin의 kotlinx.html이 꽤 마음에 듭니다. 실제 Kotlin으로 HTML을 쓰는 eDSL이라서 템플릿 코드에 브레이크포인트를 걸 수 있거든요. 문자열에 값을 끼워 넣는 방식의 템플릿 라이브러리보다 아주 조금 느리지만, 감수할 만한 수준입니다. 컴파일 시간에 주는 영향도 문자열 보간 방식 템플릿 라이브러리보다는 더 큽니다.

    타입 안전성은 Kotlin이 이미 제공하는 것 그대로이고, 거기에 더해 HTML 명세에 맞게 특정 요소 안에 특정 요소를 중첩할 수 없도록 막아 주는 이점이 조금 있습니다. 직접 요소와 속성을 만들 수도 있지만, HTML 명세에 있는 것들은 대개 kotlinx.html에도 들어 있어서 자동 완성도 됩니다.

    이 라이브러리는 상당 부분이 실제 HTML 명세에서 생성됩니다.

    Claude Code는 이걸 쓰는 데 문제가 없어 보입니다(요즘은 저도 템플릿을 거의 직접 쓰지 않습니다).

    https://github.com/kotlin/kotlinx.html

    https://dzone.com/articles/modern-type-safe-template-engines-part-2

  7. giancarlostoro HN

    젯브레인스가 자기들이 계속 끼워 넣으려는 AI 쓰레기보다 Kotlin에 더 신경 써 줬으면 좋겠어요. 이 추세면 AI 분야에서는 질 게 뻔한데, Kotlin은 잠재력이 훨씬 큰데도요. 나온 지 거의 10년 됐나? 아, 15년이네요, 이런!

  8. solatic HN

    이 설계가 마음에 듭니다. 특히 Zig 빌드 체인을 활용한다는 점이 좋네요. 재빌드가 충분히 빨라서 개발 중에 사실상 JS 스타일 핫 리로딩을 쓸 수 있을 것 같습니다. 이제 Zig용 https://github.com/air-verse/air 만 있으면 되겠습니다(Ziex는 CLI에 내장돼 있지만, Ziex 전용이죠)...

    벤치마크가 나오길 기대하겠습니다!

  9. fergie HN

    W3C: <includes src="./header.html"> 같은 걸 그냥 넣으면 끝나는데 왜 안 만드는 겁니까?

  10. tannhaeuser HN

    HTML의 바탕이 되는 마크업 메타 언어인 SGML(ISO 8879)에는 그게 있고, 타입 검사를 하는 템플릿 같은 다른 기능도 많이 있습니다.

    그래서 SGML의 기능을 활용하는 어휘일 뿐인 HTML은 자체적으로 임시방편 메커니즘을 정의할 필요가 없었습니다.

    참고로 <?php ...> 구문도 SGML에서 유래했습니다. SGML 용어로는 "처리 명령(processing instruction)"이고, 내용을 포함시키는 기본 수단은 "엔티티 참조(entity reference)" 쪽입니다.

  11. pwdisswordfishq HN
  12. pautasso HN

    좋은 생각이네요!

    https://web.dev/articles/imports

    이미 해 봤고 겪어 봤죠.

    2014년 도입(Chrome 36) 2018년 폐기 예고(Chrome 70) 2019년 퇴출(Chrome 73)

  13. stefs HN

    저도 직접 설계한 "타입 안전" 템플릿 엔진을 (Kotlin용 Gradle 플러그인으로) 쓰고 있는데, 접근이 반대입니다. 모델 클래스를 템플릿에 끼워 넣는 대신, 모든 값을 매개변수로 받는 템플릿 클래스를 생성합니다.

    예를 들어 MyTemplate.tpl.html이 이렇다면

        {if $foo}hello world, {$myname}!{else}goodbye, world.{/if}
    

    이런 식으로 쓸 수 있는 Kotlin 클래스가 만들어집니다.

        MyTemplate(
          foo = true,
          myname = "bar"
        ).toString()
    

    저한테 가장 큰 장점은 IDE 지원을 온전히 공짜로 받는다는 점입니다. 매핑이 1:1이 아니어도 별도의 뷰 모델을 쓸 필요가 없고, 기본 JDK 기능을 빼면 런타임에 파싱·컴파일·해석하는 과정이 없어서 성능도 꽤 좋습니다(더 빠른 엔진도 있지만요).

    https://codeberg.org/h34tnet/tempolin

    경고: 아직은 저 혼자만 쓰고 있으니 프로덕션에서는 쓰지 마세요. 그리고 지금 개선 작업을 하고 있는데 아직 푸시하지 않았습니다.

  14. EddieRingle HN

    아주 깔끔하네요. Android의 데이터 바인딩 시절(XML 뷰에 대한 타입 안전한 생성 인터페이스)이 거의 떠오릅니다. Android 개발은 곧 Compose UI로 넘어갔지만요. Kotlin이 모든 걸 다룰 만큼 표현력이 충분하니까 그랬겠죠. 그 뒤로 저는 다른 생태계들이 템플릿에 너무 많이 의존하는 게 점점 짜증스러워졌습니다. (사실 젯브레인스가 Amper/Kotlin Toolchain에 YAML을 고른 건 두 배로 짜증납니다. 어차피 특수 템플릿 기능을 넣어야 했는데, 왜 Kotlin이나 그 일부를 쓰지 않았을까요?)

  15. kitd HN

    잘 만드셨네요. 제 생각엔 좋은 접근이고(확인이 필요하시다면), Go의 Templ도 같은 방식을 택했습니다. 이를 위한 'generate' 명령이 있죠.

  16. fitsumbelay HN

    글쓴이가 겪었다는 Ch-ch-changes 정신으로 말하자면, 에이전트 코딩이 아니었다면 저는 이런 글을 읽고 있지도 않았을 겁니다.

    물론 100년에 한 번쯤 C로 'Sup World를 짜 보긴 합니다. 얼추 뭔가 해냈다는 그 느낌을 맛보려고요. 하지만 저수준 언어로 만든 실행 파일을 제가 만들고 유지하고 전문성과 감각을 쌓아 가는 제품으로 생각해 보라고요?

    저수준 언어로 하는 웹 개발에 대해 말하자면, "웹"과 "개발" 사이에 괄호 속 "서비스"가 있다는 걸 깨닫는 순간, 속도와 안정성 등을 제공하는 언어를 찾게 되는 건 필연입니다.

  17. codethief HN

    그러다 Ziex를 보고 감탄했습니다. JSX 방식을 JavaScript 밖에서도 할 수 있다는 건 생각해 본 적이 없었습니다.

    저도 생각 못 했어요! 정말 멋지네요! 어떻게 동작하는 건가요?

    수정: 아, 알겠네요. Zig 코드로 컴파일되는 커스텀 파일 형식이군요. 그래도 꽤 멋지지만 IDE 지원을 전부 잃는다는 게 아쉽습니다.

  18. nicoburns HN

    Rust에는 일반 cargo build로 동작하면서 IDE 지원도 유지되는 rsx 매크로가 있습니다.

  19. tancop HN

    Zig는 Rust나 C 스타일의 매크로를 아예 원하지 않기 때문입니다. 그래서 이런 건 대부분의 IDE가 실행하지 않는 빌드 스크립트에 들어가야 하죠. 언젠가 빠른 컴파일러를 활용하는 Zig IDE가 나올지도 모르겠네요.

    파서를 건너뛰고 토큰화만 해서 comptime 함수에 넘기는 '{ } 같은 특수 블록 타입으로 문법 확장을 깔끔하게 할 방법이 있을 것 같습니다. 하지만 Zig의 주요 장점 하나가 숨겨진 제어 흐름이 없다는 것이라서, 그 점을 그대로 지키고 싶어 하겠죠.

  20. christophilus HN

    LLM이 LSP를 꽤 잘 만드니까, 이게 인기를 끌면 문제 될 일은 아닐 겁니다.

  21. infogulch HN

    zt는 또 다른 인기 Zig 템플릿 엔진인 zmpl과 비교하면 어떻습니까?

    https://github.com/jetzig-framework/zmpl

  22. lukaslalinsky HN

    Zmpl은 JSON 같은 데이터 트리를 만들게 하고, 템플릿은 그 값들을 사용해 동작합니다. 안타깝게도 그러면 다시 런타임 오류를 다뤄야 합니다. 사소한 점이지만 템플릿을 std.Io.Writer에 바로 쓰지 않고 할당된 문자열로 렌더링하려고 하기도 합니다. 그리고 문법도 있는데, 저는 Go의 Templ 방식이 더 좋습니다.

  23. weitzj HN

    글 감사합니다. 10년쯤 전에 Google Soy 템플릿을 썼던 기억이 납니다. 예를 들어 Helm 템플릿이나 Go 템플릿을 만들 때 쓰는 것과 비슷하다고 생각합니다. HTML 이스케이프, JavaScript 이스케이프 등이 내장돼 있었죠.

    Google Soy용 Zig 래퍼가 혹시 있을까요?

  24. lukaslalinsky HN

    재미있는 사실인데, 저도 Google Closure Templates를 포크해서 Python 지원을 추가한 적이 있습니다. 잠깐 썼고 완전히 잊고 있었는데, 이제 기억나네요. 그때 쓰는 게 즐거웠습니다. https://pythonhosted.org/Flask-Soy/

  25. Humphrey HN

    아이디어가 마음에 듭니다! 다른 언어에도 비슷한 게 만들어질 수 있겠네요. 가령 Django와 JS React 프런트엔드에서 같은 템플릿을 재사용하면서, tsx가 제공하는 타입 안전성도 다 갖추는 식으로요.

  26. lucasdicioccio HN

    저는 몇 년간 쌓인 불만을 해소하려고 최근에 템플릿 언어(정확히는 문서를 위한 중간 언어)를 하나 만들었습니다. 이미 제 프로젝트 네댓 개에 넣었고요(HTML, 이메일, 대시보드, 챗봇 메시지, 일부 설정 템플릿용).

    "결정적이고, 호스팅 가능하며, 템플릿에서 훅을 걸 수 있을 만큼은 갖춘" 설계 지점이 이걸 특별하게 만듭니다. https://lucasdicioccio.github.io/tramaj/ .

  27. marcle HN

    저는 Ur/Web의 타입 안전한 XML/HTML이 늘 우아하다고 생각했습니다.

    예시는 http://www.impredicative.com/ur/demo/ 에서 볼 수 있습니다.

  28. riffraff HN

    한동안은 너도나도 mustache[0]를 템플릿 시스템으로 구현하던 시기가 있었는데, 금방 시들었습니다. 결국 언어 간 템플릿은 그리 쓸모가 없기 때문입니다. 호스트 언어를 바꾸면서 템플릿은 그대로 쓸 일이 드물거든요.

    0: https://en.wikipedia.org/wiki/Mustache_(template_system)

  29. bvrmn HN

    그 열풍은 mustache가 표현 전용 템플릿 엔진이라는 "순수성" 때문이었던 것 같습니다. 쓸모 있으려면 어느 정도의 로직은 필요하다는 게 드러났죠.

  30. _kidlike HN

    Java용 jte.gg와 아주 비슷한 것 같네요.

Hacker News에서 보기 ↗