2000년에 PHP 3.0으로 웹 개발을 시작했다면서 저수준 언어인 Zig로 웹 개발을 하게 될 줄 몰랐다는 건 묘하게 재미있음. 나는 1997년 분산 시스템 수업에서 Perl과 C로 CGI를 작성했고, 1999~2001년 직장에서도 웹 개발에 Tcl과 C를 주로 사용했음.
당시 ASP는 VB와 C++ COM 구성요소를 섞어 썼고, Apache 확장이나 IIS ISAPI 모듈도 C와 C++로 작성함. Microsoft는 ATLServer를, Borland/Inprise는 Delphi와 C++ Builder용 비슷한 도구를 제공했으며, 내장 기능이 적었던 PHP 3.0에서도 C 확장을 만드는 일이 흔했음.
2010년대 느린 스크립트 언어가 주도권을 잡기 전의 저수준 언어 기반 웹 개발로 돌아가는 일을 아무도 생각하지 못한 발상처럼 받아들이니 역설적으로 느껴짐.
내게 웹은 늘 스크립트 언어의 영역이었음. CGI 시절에도 간단한 웹 폼을 만들 만큼만 Perl을 배웠고, 임시 문자열과 사전을 많이 다루는 작업이라 자연스럽게 가비지 컬렉션이 있는 언어와 연결해서 생각함. 웹 프로젝트에 처음 사용한 컴파일 언어도 Go였고, 그마저도 가비지 컬렉터가 있어서 선택했음.
직접 만든 Kotlin용 Gradle 플러그인 형태의 타입 안전 템플릿 엔진을 쓰고 있는데, 모델 클래스를 템플릿에 넣는 대신 모든 값을 매개변수로 받는 템플릿 클래스를 생성함.
예를 들어 MyTemplate.tpl.html에 {if $foo}hello world, {$myname}!{else}goodbye, world.{/if}를 쓰면, MyTemplate(foo = true, myname = "bar").toString()으로 사용할 수 있는 Kotlin 클래스가 만들어짐.
별도 작업 없이 IDE 지원을 온전히 활용하고, 데이터가 일대일로 대응하지 않아도 별도의 뷰 모델을 만들 필요가 없다는 게 가장 큰 장점임. 기본 JDK 동작 외에는 실행 시 파싱·컴파일·해석이 없어 성능도 꽤 좋지만, 더 빠른 엔진도 있음. https://codeberg.org/h34tnet/tempolin
아직 사용자가 나뿐이므로 운영 환경에서는 사용하지 말아야 함. 현재 작업 중인 개선 사항도 아직 저장소에 올리지 않은 상태임.
좋은 접근이라고 봄. Go의 Templ도 같은 방식을 채택하며, 이를 위한 generate 명령을 제공함.
W3C 차원에서 이런 기능을 제공해서 해결하면 안 될까?
HTML의 기반인 마크업 메타언어 SGML(ISO 8879) 에는 해당 기능뿐 아니라 타입 검사를 하는 템플릿 같은 기능도 있음. HTML은 SGML의 기능을 활용하는 어휘 집합이므로 별도의 임시방편을 정의할 필요가 없었음.
그 구문도 SGML에서 유래했으며, SGML 용어로는 콘텐츠 삽입의 기본 수단인 엔티티 참조가 아니라 처리 지시문에 해당함.
다른 언어에도 비슷한 도구가 있으면 좋겠음. tsx 수준의 타입 안전성을 유지하면서 Django와 JavaScript React 프런트엔드에서 같은 템플릿을 재사용하는 방식도 상상해 볼 수 있음.
여러 해 쌓인 불편을 해결하려고 최근 템플릿 언어, 정확히는 문서용 중간 언어를 만들었음. 이미 프로젝트 네다섯 개에서 HTML, 이메일, 대시보드, 챗봇 메시지, 설정 파일을 만드는 데 사용 중임. 결정적 동작, 호스트 프로그램에 내장 가능, 템플릿에서 훅을 연결하기에 충분한 기능을 설계 기준으로 삼은 게 특징임. https://lucasdicioccio.github.io/tramaj/
그렇게 설명하는 건 정확하지 않음. Io 인터페이스의 취소 처리는 오히려 최고 수준에 속함. defer/errdefer와 결합하면 비동기 drop 같은 동작도 가능해서 비동기 Rust보다 안전한 취소 보장을 얻을 수 있음. 문제가 되는 부분은 오류 처리에서 Reader/Writer와 Io 인터페이스의 상호작용뿐임.
Andrew가 그렇게 답한 데에는 핵심 개발팀과 lalinsky 사이의 과거 관계가 작용함.
Zig 사용자는 아니지만, 연결된 글에는 인터페이스에 취소 기능을 추가하자는 제안이 왜 나쁜지 보여주는 반례가 있음. 취소를 원하지 않거나 제공할 수조차 없는 인터페이스에 취소 의미론을 강제하게 됨. 라이브러리 설계가 좋지 않다는 이유로 새 메서드를 강요하는 건 적절하지 않다고 봄.
불편은 이해하지만 Scott Redig의 답변은 충분히 타당함. 라이브러리가 cancelled()를 삼키는 것과 취소를 지원한다고 거짓으로 표시하는 것 중 무엇이 더 나쁜지도 모르겠음.
Java의 jte.gg와 매우 비슷해 보임.
거부감만 참을 수 있다면 M4로도 원하는 기능 일부를 구현할 수 있음. sinclude(...)는 파일을 포함하며, 실패해도 오류를 내지 않음. PHP나 Perl을 사용할 수 없어서 사용자 로그인 이력을 이런 방식으로 만든 적이 있음. index.m4에서 byuser.m4, bydate.m4, footer.m4를 포함하고, m4 index.m4 > index.htm으로 HTML을 생성함. 사용자명별 파일에는 A-L과 M-Z, 마지막 로그인 연도별 파일에는 2025와 2026 링크를 넣었음. 바닥글에서는 syscmd로 date | tr -d "\n"을 실행해 마지막 갱신 시각을 표시함.
Rust 매크로의 좋은 점은 별도 파일 형식이나 새 문법을 만들지 않고도 이런 기능을 구현할 수 있다는 것임. #[component] 함수 안의 rsx!에서 Layout을 구성하고, if let Some(u) = user로 로그인 여부에 따라 환영 문구와 프로필·로그인 링크를 렌더링할 수 있음.
하지만 그건 HTML이 아님. Rust 파서 규칙을 따라야 하는 새로운 DSL을 배워야 하니 같은 방식이라고 할 수 없음.