HTMX: 프런트엔드 재고 - React는 잊어라
Olivia Novak
Dev Intern · Leapcell

Front-End Development Completely Held Captive by JavaScript
옛날 옛적에, 저는 Django 템플릿을 사용하여 웹 UI를 손쉽게 구축할 수 있었습니다. 페이지의 다양한 크기의 반복적인 부분에 대해 템플릿이나 조각을 활용하여 추상화하거나 캡슐화했습니다. 물론, 필요한 경우 페이지에 상호 작용성을 추가하기 위해 JavaScript를 작성하는 것을 주저하지 않았습니다.
그러나 이런 방식으로 구축된 UI에는 명백한 결함이 있습니다. 사용자가 페이지와 상호 작용할 때마다 백엔드는 전체 HTML 페이지를 다시 보내야 합니다. 이는 대역폭을 낭비할 뿐만 아니라 상호 작용을 서투르고 매끄럽지 않게 만듭니다. 상호 작용 기능을 향상시키기 위해 일부 AJAX 라이브러리가 등장했습니다. 시간이 지남에 따라 JavaScript는 점점 더 많은 작업을 수행했으며, 심지어 서버 측에서 HTML 템플릿을 렌더링하는 작업까지 점차 클라이언트 측으로 이동했습니다. 결국 React로 대표되는 반응형 컴포넌트 기반 UI가 개발의 봄을 맞이했습니다.
React는 가상 DOM, 단방향 데이터 흐름, JSX 및 컴포넌트 기반 사고와 같은 많은 혁신적인 개념을 웹 개발에 가져왔습니다. 프런트 엔드를 HTML 중심 클라이언트에서 JavaScript 중심 클라이언트로 완전히 전환했습니다. 동시에 백엔드는 프런트 엔드 렌더링 단계에서 물러나 HTML 생성을 프런트 엔드에 넘겨주고 데이터 API 제공에 집중했습니다. 그러나 JavaScript가 프런트 엔드 개발을 완전히 장악하고 HTML이 부차적인 역할을 맡기 시작하면서 상황이 약간 통제 불능이 되었습니다. 완전히 정적 HTML로 구성된 푸터 콘텐츠조차도 JavaScript (JSX)를 통해 구현해야 합니다. 원래 콘텐츠가 풍부했던 HTML 페이지는 매우 간단해졌으며, body에는 마운트를 위한 루트 요소만 남아 있는 경우가 많습니다.
React가 프런트 엔드 세계에서 인기를 얻으면서 결함이 점차 드러났습니다. 이러한 문제를 해결하기 위해 새로운 아이디어, 프레임워크 및 생태 도구가 계속 등장하고 있습니다. 열정적인 프런트 엔드 개발자는 산을 뚫고 강 위에 다리를 놓는 것처럼 새롭게 발생하는 문제에 대한 해결책을 계속 찾고 있습니다. 적합한 상태 관리 도구가 없을 때는 Redux가 생성되었고, 상태 관리가 너무 복잡하다고 생각될 때는 Hooks가 도입되었으며, JavaScript 클라이언트가 SEO에 친숙하지 않을 때는 SSR과 같은 기술이 채택되었습니다. 다양한 솔루션이 계속 쌓이면서 원래 간단했던 솔루션이 점점 더 복잡해져서 결국 거대하고 엉망진창인 "프랑켄슈타인"을 형성했습니다. 요즘 저는 프런트 엔드 개발에 대한 두려움을 느낍니다. 원래 간단했던 작업이 이제는 다양한 번거로운 세부 사항으로 가득 차 있습니다. 프런트 엔드 코드를 작성할 때 과도한 복잡성에 압도되는 경우가 많습니다.
더욱 심각한 것은 JavaScript가 프런트 엔드 개발을 완전히 장악했기 때문에 프런트 엔드 프로젝트의 규모가 계속 증가하여 TypeScript가 최선의 방법이 되었습니다. TypeScript를 폄하하려는 것은 아닙니다. 실제로 TypeScript는 대규모 프런트 엔드 프로젝트에서 JavaScript가 가진 많은 문제를 매우 잘 해결하는 잘 설계된 언어입니다. 그러나 정말로 모든 곳에서 "대규모" 프런트 엔드 프로젝트가 필요할까요? 프런트 엔드 프로젝트를 그토록 번거롭고 부풀어 오르게 만드는 근본 원인은 무엇일까요? 처음에는 이러한 프레임워크의 주요 목적이 프런트 엔드를 더 반응적으로 만들고, 재사용하기 쉽고, 더 표현력이 풍부하게 만드는 것이 아니었나요? 그러나 지금은 React와 Vue, SolidJS, Svelte 등과 같이 잇달아 등장한 많은 프런트 엔드 프레임워크의 복잡성으로 인해 프런트 엔드 개발자, 특히 저와 같은 "유사 프런트 엔드 개발자"는 작은 프로젝트를 큰 프로젝트로, 간단한 프로젝트를 복잡한 프로젝트로 바꾸게되었습니다. 따라서 복잡한 프로젝트를 처리하기 위해 TypeScript를 선택하는 것은 불가피한 것 같습니다.
HTMX: HTML의 원래 염원으로 돌아가기
HTMX라는 이름이 어디에서 유래했는지 확실하지는 않지만, 비전에 따르면 HTML eXtension을 의미할 수 있다고 추측합니다. HTMX는 HTML을 향상시키고 개발하는 데 집중해야 한다고 믿습니다. HTML의 많은 결함은 JavaScript로 HTML을 직접 대체하는 대신 태그 속성을 разумного तरीके으로 사용하는 것과 같이 의미론화를 통해 해결할 수 있습니다. 다음은 htmx.org에 대한 직접적인 소개입니다. "htmx는 속성을 사용하여 HTML에서 직접 AJAX, CSS 전환, 웹 소켓 및 서버 전송 이벤트에 액세스할 수 있도록 해주므로 하이퍼텍스트의 단순성과 강력함으로 최신 사용자 인터페이스를 구축할 수 있습니다."
HTMX의 핵심 비전과 특징은 다음과 같습니다.
- 점진적 향상: HTMX는 점진적 향상 원칙에 따라 설계되었으며, 이는 JavaScript 없이도 페이지가 제대로 작동하는지 먼저 확인한 다음 점차 더 많은 기능을 추가한다는 의미입니다.
- 낮은 침투성: HTMX는 기존 코드에 대한 침투성을 최소화하기 위해 노력합니다. 전체 애플리케이션을 다시 작성할 필요가 없습니다. 필요한 곳에 몇 가지 속성만 추가하여 HTMX를 사용하기 시작할 수 있습니다.
- 순수 HTML: HTMX를 사용하면 JavaScript를 작성하지 않고도 많은 복잡한 프런트 엔드 기능을 구현할 수 있습니다. 이를 통해 코드를 더 쉽게 이해하고 유지 관리할 수 있으며, 특히 HTML 및 서버 측 프로그래밍에 더 익숙한 개발자에게 적합합니다.
- 기존 기술과의 호환성: HTMX는 기존 프레임워크 및 라이브러리와 원활하게 협업할 수 있습니다. Flask, Django, Rails 또는 기타 백엔드 프레임워크를 사용하든 HTMX를 쉽게 포함할 수 있습니다.
- 작고 빠름: 많은 최신 프런트 엔드 프레임워크와 비교하여 HTMX는 매우 가벼우므로 로드 속도가 더 빠르고 사용자 경험을 향상시킬 수 있습니다.
이러한 기능을 통해 HTMX의 목표는 프런트 엔드 개발을 단순화하여 개발자가 많은 양의 JavaScript 또는 복잡한 프런트 엔드 프레임워크를 사용하지 않고도 상호 작용성이 높고 응답성이 뛰어난 웹 페이지를 빠르고 효율적으로 만들 수 있도록 하는 것임을 알 수 있습니다.
복잡한 프런트 엔드 프레임워크와 많은 양의 JavaScript 개발이 필요하지 않은 경우 현재 프런트 엔드가 직면한 많은 문제가 쉽게 해결된다는 것을 알게 될 것입니다. 예를 들어 전체 페이지를 완전히 JavaScript화할 필요가 없고, 페이지의 JavaScript화로 인해 발생하는 SEO 문제에 대해 걱정할 필요가 없고, 복잡한 상태를 관리할 필요가 없고, 엔지니어링 문제를 해결하기 위해 TypeScript를 도입할 필요가 없습니다.
샘플 코드 비교
HTMX에서 일반적인 프런트 엔드 기능인 자동 완성을 구현하는 방법을 먼저 살펴보겠습니다.
<input type="text" hx-trigger="keyup delay:500ms, custom-event" hx-get="/search" hx-target="#search-results" hx-swap="innerHTML"> <div id="search-results"></div>
너무 놀라지 마세요. 이 작은 코드가 HTMX 버전의 자동 완성을 위한 모든 코드입니다. HTMX가 일반 HTML 태그에 몇 가지 중요한 속성을 추가했음을 알 수 있습니다.
- hx-trigger: AJAX 호출과 같은 htmx 액션을 트리거할 시기와 방법을 지정하는 데 사용됩니다. 이 속성을 통해 개발자는 특정 이벤트 (예: 클릭, 입력 또는 포커스 등)가 발생할 때 서버와의 상호 작용을 시작하는 방법을 제어할 수 있습니다. 이 예에서는 두 개의 트리거 이벤트가 설정됩니다. a) 입력에서 keyup 이벤트가 발생하고 500ms 지연 후 트리거합니다. b) custom-event라는 이벤트가 수신될 때 트리거합니다.
- hx-get: htmx 액션이 트리거될 때 실행할 호출입니다. hx-get은 GET 요청을 나타냅니다. 마찬가지로 hx-post, hx-put, hx-delete, hx-patch 등도 서버 호출에 사용할 수 있습니다. hx-get과 같은 속성을 통해 HTMX는 서버와 상호 작용할 수 있는 권한을 각 태그에 분산시켜 전통적으로 <a/>와 <form/>만 서버와 상호 작용할 수 있다는 제한을 변경합니다.
- hx-target: 서버의 응답이 반환될 때 응답이 채워질 위치를 지정합니다. hx-target은 모든 CSS 표현식일 수 있습니다. 여기서는 ID가 search-results인 노드를 가리킵니다. 기본적으로 현재 노드입니다. hx-get과 같은 속성이 페이지에서 서버와 상호 작용할 수 있는 기능을 제공하는 경우 hx-target은 페이지에서 동적으로 업데이트할 수 있는 기능을 제공합니다.
- hx-swap: 서버의 응답이 반환될 때 콘텐츠를 교체하거나 대체하는 방법을 지정합니다. 기본적으로 innerHTML이며, 이는 #search-results 내부의 HTML이 서버에서 반환된 데이터로 대체된다는 의미입니다. hx-swap에는 outerHTML, beforeend, afterend와 같은 다른 동작이 있으며 교체 방법을 위한 애니메이션 효과를 추가할 수도 있습니다. 자세한 내용은 설명서를 확인하십시오.
이러한 속성은 초보자에게 매우 유용합니다. 이러한 속성을 마스터하면 페이지 내 상호 작용의 대부분을 처리할 수 있습니다. HTMX는 다른 많은 hx-* 속성도 제공하며 여기서는 하나씩 소개하지 않습니다. 이러한 속성을 사용하면 검색 상자의 동작을 쉽게 제어하고 원래 많은 JavaScript가 필요했던 효과를 얻을 수 있습니다.
더 복잡한 예를 살펴보겠습니다.
애플리케이션이 여러 개의 노트북을 표시한다고 가정합니다. 각 노트북에는 여러 개의 메모가 있고 각 메모에는 자세한 정보가 있습니다. 세 개의 열 레이아웃으로 표시합니다. 사용자가 왼쪽 열에서 book1을 클릭하면 book1 아래의 메모가 두 번째 열에 페이지 매김된 형태로 표시되고 두 번째 열의 첫 번째 메모의 세부 정보가 세 번째 열에 표시됩니다.
다음은 HTMX를 사용하여 구현된 코드입니다.
- 왼쪽 열의 코드:
<ul> {% for book in books %} <li> <a hx-get="/books/{{book.id}}" hx-target="#note-list">{{book.name}}</a> </li> {% endfor %} </ul>
- 중간 열의 코드:
<div id="note-list"> {% for note in current_book.notes %} <div onclick="htmx.trigger('#note-detail', 'loadNote', {id: '{{note.id}}'})"> <h2>{{note.title}}</h2> <p>{{note.summary}}</p> </div> {% endfor %} </div>
- 오른쪽 열의 코드:
<div id="note-detail" _="on loadNote(data) from body htmx.ajax('GET', `/notes/${data.id}`, '#note-detail')" > <h3>{{title}}</h3> <p>{{detail}}</p> </div>
이런 식으로 몇 가지 간단한 템플릿과 HTMX 속성을 보완하여 첫 번째 페이지 렌더링이 완료될 뿐만 아니라 사용자의 클릭에 따라 페이지를 업데이트할 수도 있습니다. 예를 들어 사용자가 book2를 클릭하면 /books/2에 액세스하기 위해 GET 요청이 트리거되고 다음 응답이 반환됩니다 (즉, 중간 열의 템플릿에서 생성된 콘텐츠).
200 OK
HX-Trigger: {"loadNote": {"id": "book2id1"}}
Content-Type: text/html
<div onclick="htmx.trigger('#note-detail', 'loadNote', {id: 'book2id1'})">
<h2>Hello 1</h2>
<p>World 1</p>
</div>
<div onclick="htmx.trigger('#note-detail', 'loadNote', {id: 'book2id2'})">
<h2>Hello 2</h2>
<p>World 2</p>
</div>
...
이 결과는 HTMX에 의해 #note-list로 렌더링되어 중간 열의 업데이트를 수행합니다. 동시에 반환된 데이터의 HX-Trigger 헤더에 loadNote 이벤트가 포함되어 있으므로 이 이벤트는 #node-detail에 의해 캡처되어 /notes/book2id1에 GET 요청을 보내고 해당 응답이 오른쪽 열로 렌더링됩니다.
위의 기능을 React를 사용하여 구현하면 코드 양이 크게 늘어나고 더 많은 상태 관리 및 컴포넌트 상호 작용 로직을 처리해야 합니다. 다음은 간단한 React 구현 예제입니다 (간소화된 버전, 핵심 로직만 표시).
import React, { useState, useEffect } from'react'; const BookList = ({ books }) => { const [currentBook, setCurrentBook] = useState(null); const [currentNote, setCurrentNote] = useState(null); useEffect(() => { if (books.length > 0) { setCurrentBook(books[0]); if (books[0].notes.length > 0) { setCurrentNote(books[0].notes[0]); } } }, [books]); const handleBookClick = (book) => { setCurrentBook(book); if (book.notes.length > 0) { setCurrentNote(book.notes[0]); } }; const handleNoteClick = (note) => { setCurrentNote(note); }; return ( <div className="flex"> <div className="w-1/3"> <ul> {books.map((book) => ( <li key={book.id} onClick={() => handleBookClick(book)}> {book.name} </li> ))} </ul> </div> <div className="w-1/3"> {currentBook && ( <div> {currentBook.notes.map((note) => ( <div key={note.id} onClick={() => handleNoteClick(note)}> <h2>{note.title}</h2> <p>{note.summary}</p> </div> ))} </div> )} </div> <div className="w-1/3"> {currentNote && ( <div> <h3>{currentNote.title}</h3> <p>{currentNote.detail}</p> </div> )} </div> </div> ); }; export default BookList;
React를 사용하여 동일한 기능을 구현하려면 페이지 상태를 관리하기 위해 여러 상태 변수 (예: currentBook 및 currentNote)를 정의해야 하며, 사용자 상호 작용을 처리하기 위해 컴포넌트에 더 많은 이벤트 처리 함수 (예: handleBookClick 및 handleNoteClick)를 작성해야 합니다. 대조적으로 HTMX의 구현 방법은 더 간결하고, 로직이 더 명확하며, 전통적인 서버 렌더링 아이디어에 더 가깝습니다.
결론
HTMX는 프런트 엔드 엔지니어가 아닌 사람들을 위해 프런트 엔드 개발의 문을 다시 열었습니다. 스프레드시트 또는 Google Maps와 같이 상호 작용성이 매우 높은 애플리케이션을 개발하지 않는 경우 기본적으로 HTMX를 사용하여 기존 프런트 엔드 개발 프레임워크를 대체하고 HTML 중심의 가벼운 프런트 엔드 개발 모드로 돌아갈 수 있습니다. HTMX를 사용하면 클라이언트를 SPA로 구현할지 MPA로 구현할지 고민할 필요가 없습니다. 라우팅에 가장 적합한 방법을 선택하고, 데이터를 가장 자연스러운 방식으로 표시하고, 사용자가 데이터를 생성, 읽기, 업데이트, 삭제 또는 기타 작업)과 상호 작용하도록 할 수 있습니다.
현재 HTMX 생태계는 아직 초기 단계에 있습니다. 주류 백엔드 프레임워크가 이에 대한 심층적인 지원 또는 통합을 제공하기를 정말로 기대하고 있습니다. HTMX의 가치가 지속적으로 발견됨에 따라 프런트 엔드 개발자가 아닌 사람들도 자신감을 회복하고 웹 프런트 엔드를 포함하는 완전한 제품을 쉽게 개발할 수 있을 것이라고 믿습니다.
Leapcell: 최고의 서버리스 웹 호스팅
마지막으로 웹 서비스를 배포하는 데 가장 적합한 플랫폼인 **Leapcell**을 추천합니다.
🚀 좋아하는 언어로 빌드
JavaScript, Python, Go 또는 Rust로 손쉽게 개발하십시오.
🌍 무료로 무제한 프로젝트 배포
사용한 만큼만 지불하십시오. 요청도, 요금도 없습니다.
⚡ 사용한 만큼만 지불, 숨겨진 비용 없음
유휴 요금이 없으며 원활한 확장성만 제공됩니다.
🔹 Twitter에서 팔로우하세요: @LeapcellHQ