HTMX가 너무 멋져서 직접 만들었다 (2024)
(dbushell.com)- HTMX는 서버 렌더링 HTML을 중심으로 프런트엔드를 점진적으로 향상하며, React식 현대 JavaScript UI가 비대해지기 전의 방식에서 발전한 접근
- 자체 호스팅 podcast web app의 서버 리팩터링에서 SvelteKit을 DinoSsr로 교체하자 페이지 이동이 오디오 재생을 끊는 문제가 생겼고, HTMX로
<main>영역만 갱신해 오디오 컴포넌트를 유지할 수 있었음 - HTMX는 프런트엔드 라이브러리로 배포되지만 구현의 상당 부분이 백엔드 템플릿과 서버 설정에 놓이며, HTML을 반환하는 HTTP 요청이 사실상 HTMX API 역할
hx-*속성, 클릭 가능한<div>예시, 인라인 JavaScript가 많은 고급 예시는 선언적 속성 템플릿의 한계와 문서상의 불만점- 직접 만든 미니 구현은
304캐싱, History API,<title>교체, 포인터 이벤트 기반 프리로드를 주요 요소로 사용했고, 브라우저 JavaScript를 줄여 코드베이스를 더 작고 단순하게 만든 결과
HTMX의 위치
- HTMX는 현대 JavaScript UI를 거부하고 서버 렌더링 HTML을 선호하며, React로 프런트엔드가 비대해지기 전의 방식에서 발전한 접근
- 무한 스크롤과 실시간 검색 결과는 인기 있는 HTMX 예시이며, HTMX는 React 등이 풀려는 모든 문제를 해결하지 않고 제한적으로 보이지만 그 제한 안에서는 가치가 큰 도구
- HTMX는 “magic bullet”이 아니며, HTMX 뒤의 일부 아이디어가 HTMX 자체보다 더 중요하다는 관점이 핵심
실험
- 문서 읽기만으로는 충분하지 않아 실제 사용을 테스트했고, 자체 호스팅 podcast web app 서버 리팩터링이 HTMX 적용 대상
- 이전 구현은 SvelteKit 사용이었고, SvelteKit은 선호하는 도구지만 작은 웹사이트에는 부담스러울 수 있는 프레임워크
- 교체 대상은 직접 만들던 DinoSsr였으며, 같은 프로젝트는 정적 사이트 빌드와 북마크 블로그 제공에도 사용
- DinoSsr는 주로 서버 사이드 기반이며 프런트엔드 “islands”로 컴포넌트를 제공할 수 있지만, 전체 페이지 상호작용은 제공하지 않는 구조
- 오디오 플레이어 컴포넌트는 페이지 이동 중에도 유지되어야 하며, 페이지 이동이 전체 페이지를 다시 로드하면 재생 경험이 빠르게 종료되는 문제
- SvelteKit은 UI 업데이트와 프런트엔드 라우팅을 처리했지만, 새 빌드에서는 오디오 플레이어 컴포넌트만 프런트엔드 JavaScript를 사용하고 DinoSsr는 클라이언트 사이드 라우팅을 의도적으로 시도하지 않는 구조
- 페이지 몇 개는
<main>섹션만 다르므로, HTMX로 링크를 점진적으로 향상하고 이 영역만 갱신하면 페이지 전체를 다시 로드하지 않고 오디오 컴포넌트를 유지 가능 - HTMX 적용은 잘 작동했지만, 곧 HTMX를 제거하고 같은 아이디어로 직접 만든 미니 버전으로 전환
HTMX에 대한 몇 가지 생각
- 이 실험에서 HTMX는 프런트엔드 Svelte의 대체재였지만, React처럼 바로 끼워 넣는 대체재가 아니라 사고방식의 큰 전환
- HTMX는 프런트엔드를 점진적으로 향상하는 JavaScript 라이브러리로 배포되지만, 구현의 대부분은 백엔드에서 이뤄지며 서버 템플릿과 설정은 직접 준비해야 하는 구조
- HTML을 제공하는 HTTP 요청은 사실상 HTMX API 역할이며, 올바른 캐싱을 위해 HTTP 헤더를 설정하는 세부 사항이 중요함
- 표준
data-*속성과dataset이 있는데 비표준hx-*접두 속성을 쓰는 점은 작은 불만 - HTMX 문서에는
<div>예시가 많으며, 아래 예시는 사용자가div를 클릭하면/messages로PUT요청을 보내고 응답을 해당div에 로드하는 구조
<div hx-put="/messages">
Put To Messages
</div>
- 사용자가
<div>요소를 클릭해야 하는 예시는 바람직하지 않다는 비판 - 인라인 JavaScript를 사용하는 일부 고급 HTMX 예시는 다소 지저분해지며, 선언적 속성 템플릿의 한계를 보여주는 사례
- 비판과 별개로 HTMX는 제한적이지만 유용한 기능 집합을 제공하며, 많은 일반적인 웹 디자인 패턴을 향상할 수 있는 도구
직접 만들기
- HTMX를 성공적으로 추가한 직후 제거했고, 같은 아이디어를 사용해 직접 만든 미니 버전 구현
- 캐싱되는 fetch 요청을 허용하기 위해
last-modified,if-modified-sinceHTTP 헤더와304응답을 사용 - 기본 히스토리 통합에는
pushState와popstate를 사용 <title>요소를 추출하고 교체하는 코드를 추가했으며, HTMX preload extension에서 영감을 받아 포인터 이벤트 기반 프리로드를 내장- 포인터 이벤트 기반 프리로드는
click이벤트가 발생하기 전에 fetch 요청을 시작해 작은 성능 향상을 제공 - 실험용 소스 코드는 매우 기본적이지만, 브라우저에 실제로 필요한 JavaScript가 적다는 점을 보여주는 구현
- HTMX 또는 “we have HTMX at home” 방식으로 코드베이스가 훨씬 작고 단순해진 결과
프런트엔드 JavaScript
- 템플릿과 컴포넌트는 아주 작은 웹사이트가 아니라면 코드 구성과 재사용에 사실상 필요하며, PHP, Ruby, Go, JavaScript 등으로 서버 사이드에서도 구현 가능
- 이런 구조를 브라우저에 복제하거나 브라우저에서만 구현할 필요는 없으며, React 등의 인기로 많은 개발자가 이 질문을 멈춘 상태
- 프런트엔드 JavaScript 피로는 실제이며, 오래된 서버 템플릿을 선호하면서도 JS UI를 과도하게 설계해 온 경험과 맞물리는 문제
- HTMX 자체가 아주 훌륭하지 않다고 보더라도, 그 철학은 현대 JavaScript 개발자를 부끄럽게 만들 만큼 강한 가치가 있는 접근
댓글과 토론
Lobste.rs 의견들
-
사소한 트집이지만,
data-*를 써야 할 곳에hx-*접두 HTML 속성을 쓰는 건 별로 마음에 들지 않음
Htmx는 오래전부터data-접두사를 지원함
사용자가<div>요소를 클릭하게 만들면 안 된다는 점에는, htmx의hx-boost를 앵커와 폼 태그에 쓰는 게 가장 좋음. 이 태그들을 올바른 방식으로 자동 강화해 주기 때문에 클릭 가능한 div를 피할 수 있음
브라우저에 실제로 필요한 JavaScript가 얼마나 적은지 보여주는 프로젝트이고, 앞으로 유지보수는 최소화되며 보안 패치도 거의 필요 없을 듯함. 축하할 만함 -
HTMX는 모든 걸 렌더링하니까 서버를 혹사시키지 않나? 예전에 겪던 CGI 문제와 비슷한 느낌임
- 거의 확실히 그렇지 않음. CGI 문제는 주로 짧게 살아 있는 프로세스를 많이 띄우는 비용이었고, FastCGI 같은 것으로 해결됐음
HTML 생성은 딱히 비싼 작업이 아니며, 대안 방식에서 비슷한 양의 JSON을 만드는 것보다 더 비싸다고 보기도 어려움 - “우리”라니 무슨 말인지 모르겠음. 내 블로그는 CGI 기반이고 서버가 HTML을 렌더링하며 JavaScript는 전혀 없음
1999년부터 아무 문제 없었고, 늘 그렇듯 실제 병목이 어디 있는지는 프로파일링으로 확인해야 함 - 보통 서버 사이드 렌더링이라고 부르지만, 실제로는 서버에서 원시 HTML을 만드는 것에 가까움. 실제 렌더링과 DOM 생성은 여전히 브라우저가 하고 있고, 항상 그랬음
PHP까지 CGI 범주에 넣더라도, 이런 방식이 보통이었던 웹 개발의 10년 반 정도를 건너뛰고 있는 셈임
서버에 더 많은 일을 시킨다는 점은 맞지만, 그동안 많은 처리를 클라이언트로 넘긴 주된 이유는 비용 절감이라는 계산된 위험에 가까웠다고 봄. 많은 최종 사용자는 브라우저가 더 많은 일을 한다는 걸 알아채지 못하지만, 오래되었거나 성능이 낮은 기기에서는 지금도, 특히 초기에는 경험이 나빠졌음
다만 HTMX는 완전한 서버 사이드 렌더링과는 다름. API가 JSON으로 데이터를 보내고 클라이언트가 HTML로 렌더링하는 대신, 서버가 같은 데이터를 HTML 조각으로 보내는 방식임. 그래도 서버가 두들겨 맞는다면 REST API보다 본질적으로 자원을 더 먹어서라기보다는 다른 요인 때문일 가능성이 큼 - 벤치마크 없이 단정하긴 어렵지만, 비슷할 것 같음. 두 경우 모두 결국 문자 더미를 내보내는 일이고, 차이는 텍스트 렌더링이 더 비싼지 JSON 생성이 더 비싼지임
경험상 JSON을 쓸 때 더 많은 것을 직렬화하는 경향이 있지만, 제대로 비교할 방법은 잘 모르겠음 - 서버 사이드 애플리케이션에서 실제 HTML 렌더링 단계가 병목인 경우는 거의 없고, 병목은 대체로 그 이전 과정에 있음
- 거의 확실히 그렇지 않음. CGI 문제는 주로 짧게 살아 있는 프로세스를 많이 띄우는 비용이었고, FastCGI 같은 것으로 해결됐음
-
비슷한 이유로 htmx와 매우 유사한 alpine-ajax를 쓰고 있음
alpine-ajax도 정말 마음에 듦. “SSR에서 SPA의 장점을 얻기”를 새로 시작한다면 아마 거기로 갔을 것 같음
어차피 클라이언트 반응형 기능에는 alpine을 쓰고 있기 때문임. 다만 지금은 저장소에 htmx가 이미 많이 들어가 있고, htmx에도 불만은 없음- datastar를 쓰고 있는데, 작업하기 꽤 쾌적했음