본 글은 Claude 4 Sonnet으로 작성했습니다.
HTMX의 장점
1. JavaScript 없이도 동적 웹 구현: HTMX는 HTML 속성만으로 AJAX 요청, DOM 조작, WebSocket 통신이 가능하다. 복잡한 JavaScript 프레임워크 없이도 현대적인 웹 애플리케이션을 만들 수 있다.
<!-- 버튼 클릭 시 서버에서 데이터를 가져와 div에 삽입 -->
<button hx-get="/api/users" hx-target="#user-list">
사용자 목록 불러오기
</button>
<div id="user-list"></div>
2. 서버 중심 아키텍처 복귀: React나 Vue와 달리 서버에서 HTML을 렌더링하고 클라이언트는 단순히 받아서 표시한다. 이는 SEO에 유리하고 초기 로딩 속도를 향상시킨다.
3. 점진적 향상(Progressive Enhancement): 기존 HTML에 HTMX 속성을 추가하는 방식으로 점진적으로 기능을 확장할 수 있다. 레거시 코드와의 호환성이 우수하다.
4. 작은 번들 사이즈: HTMX는 약 14KB 크기로 React(42KB) + React-DOM(130KB)보다 훨씬 가볍다. 모바일 환경에서 빠른 로딩이 가능하다.
HTMX 핵심 문법 구조
기본 HTTP 요청 속성
<!-- GET 요청: 데이터 조회 -->
<div hx-get="/api/posts" hx-trigger="load">
<!-- 페이지 로드 시 자동으로 게시글 목록을 가져옴 -->
</div>
<!-- POST 요청: 데이터 전송 -->
<form hx-post="/api/posts" hx-target="#result">
<input name="title" placeholder="제목" />
<input name="content" placeholder="내용" />
<!-- 폼 제출 시 POST 요청을 보내고 결과를 #result에 표시 -->
<button type="submit">게시글 작성</button>
</form>
<!-- PUT 요청: 데이터 수정 -->
<button hx-put="/api/posts/123" hx-include="#edit-form">
수정하기
</button>
<!-- DELETE 요청: 데이터 삭제 -->
<button hx-delete="/api/posts/123"
hx-confirm="정말 삭제하시겠습니까?">
삭제하기
</button>
조건부 렌더링
HTMX는 서버 응답에 따라 조건부로 콘텐츠를 표시한다.
<!-- 서버에서 빈 응답이 오면 해당 요소를 숨김 -->
<div hx-get="/api/notifications"
hx-trigger="every 30s"
hx-swap="innerHTML">
<!-- 30초마다 알림을 확인하고 업데이트 -->
</div>
서버 측 조건부 응답 예시:
# Flask 예시
@app.route('/api/notifications')
def get_notifications():
notifications = get_user_notifications()
if not notifications:
return "" # 빈 응답으로 요소 숨김
return render_template('notifications.html',
notifications=notifications)
반복 요소 처리
목록 데이터는 서버에서 반복 렌더링하여 전달한다.
<!-- 사용자 목록 컨테이너 -->
<div id="user-list"
hx-get="/api/users"
hx-trigger="load">
<!-- 서버에서 렌더링된 사용자 목록이 여기에 삽입됨 -->
</div>
<!-- 무한 스크롤 구현 -->
<div hx-get="/api/posts?page=1"
hx-trigger="load"
hx-swap="afterend">
<!-- 첫 페이지 로드 -->
</div>
<div hx-get="/api/posts?page=2"
hx-trigger="revealed"
hx-swap="afterend">
<!-- 스크롤하여 요소가 보일 때 다음 페이지 로드 -->
</div>
서버 측 반복 처리:
@app.route('/api/users')
def get_users():
users = User.query.all()
# 서버에서 HTML로 렌더링하여 반환
return render_template('user_list.html', users=users)
<!-- user_list.html 템플릿 -->
{% for user in users %}
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<!-- 각 사용자별 삭제 버튼 -->
<button hx-delete="/api/users/{{ user.id }}"
hx-target="closest .user-card"
hx-swap="outerHTML">
삭제
</button>
</div>
{% endfor %}
이벤트 트리거와 타겟 지정
<!-- 다양한 트리거 이벤트 -->
<input hx-get="/api/search"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
name="query"
placeholder="검색어 입력">
<!-- 키업 이벤트 발생 후 500ms 지연하여 검색 실행 -->
<!-- 여러 타겟에 동시 업데이트 -->
<button hx-post="/api/cart/add"
hx-target="#cart-items"
hx-swap="beforeend">
<!-- 장바구니에 아이템 추가 후 목록 끝에 추가 -->
장바구니 추가
</button>
<!-- 조건부 CSS 클래스 토글 -->
<button hx-post="/api/like"
hx-target="#like-count"
hx-swap="innerHTML"
onclick="this.classList.toggle('liked')">
<!-- 좋아요 버튼 클릭 시 카운트 업데이트 및 스타일 변경 -->
♥ <span id="like-count">0</span>
</button>
폼 검증과 에러 처리
<form hx-post="/api/register" hx-target="#form-result">
<input name="email" type="email" required>
<input name="password" type="password" required>
<!-- 제출 중 표시될 인디케이터 -->
<button type="submit">
가입하기
<span class="htmx-indicator">처리 중...</span>
</button>
<!-- 서버 응답이 표시될 영역 -->
<div id="form-result"></div>
</form>
서버 측 검증 처리:
@app.route('/api/register', methods=['POST'])
def register():
email = request.form.get('email')
password = request.form.get('password')
# 검증 실패 시
if User.query.filter_by(email=email).first():
return '<div class="error">이미 존재하는 이메일입니다.</div>', 400
# 성공 시
create_user(email, password)
return '<div class="success">가입이 완료되었습니다!</div>'
WebSocket 통신
<!-- WebSocket 연결로 실시간 채팅 -->
<div hx-ws="connect:/ws/chat">
<div id="messages" hx-ws="swap"></div>
<!-- WebSocket 메시지가 여기에 자동 삽입 -->
<form hx-ws="send:submit">
<input name="message" placeholder="메시지 입력">
<button type="submit">전송</button>
</form>
</div>
실무 활용 팁
1. 로딩 상태 표시
<button hx-get="/api/data" hx-indicator="#spinner">
데이터 로드
</button>
<div id="spinner" class="htmx-indicator">로딩 중...</div>
2. 캐시 제어
<!-- 매번 새로운 데이터 요청 -->
<div hx-get="/api/time"
hx-trigger="every 1s"
hx-headers='{"Cache-Control": "no-cache"}'>
</div>
3. 에러 처리
<div hx-get="/api/data"
hx-on="htmx:responseError: alert('서버 오류가 발생했습니다.')">
</div>
HTMX는 간단한 HTML 속성만으로도 강력한 인터랙티브 웹 애플리케이션을 구축할 수 있게 해주는 도구다. 복잡한 프론트엔드 프레임워크에 지친 개발자들에게는 새로운 대안이 될 수 있다.