6.8. alpine.js를 활용한 채팅폼 enable/focus 상탯값 처리

45분 37초부터 54분 32초까지 보시면 됩니다.

6.8.1. 미리보기

07-alpinejs.gif

새로운 AI 응답을 받으면 자동으로 화면 스크롤이 부드럽게 아래로 이동하고, 응답 대기 중에는 입력 폼 자동 비활성화

6.8.2. alpine.js 소개

웹 프론트엔드에서 UI 동작을 구현하기 위해서는 자바스크립트 구현이 필수적입니다.

alpine.js 라이브러리는 경량 자바스크립트 라이브러리로서 HTML 속성으로 간단하고 직관적으로 상태 관리와 UI 상호 작용을 구현할 수 있도록 도와줍니다. 리액트와 달리 별도의 JSX 문법을 배우지 않아도 되며, HTML 속성을 통해 상태 관리와 이벤트 핸들러를 쉽게 구현할 수 있습니다. 그래서 별도의 빌드도 필요없습니다.

아래는 카운터 컴포넌트 예시 입니다.

  • x-data 속성으로 상탯값 및 관련 메서드를 정의할 수 있습니다.

  • @ 접두사로 이벤트 핸들러를 정의할 수 있습니다.

  • x-text 속성으로 상탯값을 텍스트로 출력할 수 있습니다.

<html>
<head>
   <script src="https://unpkg.com/alpinejs" defer></script>
</head>
<body>
    <div x-data="{ count: 0 }">
        Counter: <span x-text="count"></span>
        <div>
            <button @click="count--">-1</button>
            <button @click="count++">+1</button>
            <button @click="count = 0">Reset</button>
        </div>
    </div>
</body>
</html>
../../_images/07-alpinejs-counter.gif

HTMX 라이브러리와 함께 사용하면 더욱 강력합니다. HTMX 응답으로 alpine.js 컴포넌트 코드를 응답하면 적용된 요소에서 별다른 설정없이 alpine.js 컴포넌트가 동작합니다.

컴포넌트 팩키징이 필요하다면, django-cotton 라이브러리를 통해 장고 템플릿 기반으로 관리하시길 추천드립니다. cotton/counter-html 경로에 카운터 컴포넌트를 정의했다면, 장고 템플릿에서 아래와 같이 컴포넌트를 사용하실 수 있습니다.

<c-counter initial=0 />
<c-counter initial=10 />
<c-counter initial=20 />

6.8.3. alpine.js 라이브러리 추가

alpine.js 설치 공식 문서 : https://alpinejs.dev/essentials/installation

templates/base.html
    <script src="//cdn.tailwindcss.com"></script>
    <script src="//unpkg.com/alpinejs" defer></script>
</head>

6.8.4. 응답 대기 중에는 입력필드 비활성화

채팅 메시지는 그때 그때 id=messages 요소에 추가되므로 상탯값으로 관리하지 않았습니다. 모든 UI 관련 대상들을 상탯값으로 관리할 필요는 없습니다. 필요한 상탯값만 관리하시면 됩니다.

  • x-data="{ enable: true }" 속성으로 enable 상탯값을 정의합니다.

    • 해당 요소 안에서 이 상탯값을 사용할 수 있습니다.

    • 커스텀 이벤트 enable 이벤트 핸들러에서는 enable 상탯값을 true 로 설정합니다.

    • 커스텀 이벤트 disable 이벤트 핸들러에서는 enable 상탯값을 false 로 설정합니다.

    • input[name=message] 요소에 :disabled="!enable" 속성을 추가하여, enable 상탯값이 false 일 때 입력 필드를 비활성화합니다. 그리고 x-effect="if(enable) $el.focus()" 속성을 추가하여, enable 상탯값이 true 로 변경되었을 때 입력 필드로 포커스를 이동시킵니다.

  • hx-on::before-request 이벤트 핸들러는 요청 전송 전에 호출됩니다.

    • disable 이벤트를 발생시켜 enable 상탯값을 false 로 설정합니다.

  • hx-on::after-settle 이벤트 핸들러는 요소가 HTMX를 통한 변경에서 완전히 렌더링된 후에 호출됩니다.

    • 즉 메시지 내용의 변화가 있다면 자동으로 화면 스크롤이 부드럽게 아래로 이동하고, 입력 필드를 다시 활성화시킵니다.

chat/templates/chat/index.html
 1{% extends "base.html" %}
 2
 3{% block main %}
 4
 5    <div class="flex flex-col h-[calc(100vh-4rem)] w-full p-2"
 6         x-data="{ enable: true }"
 7         @enable="enable = true"
 8         @disable="enable = false">
 9
10        <div id="messages"
11             class="flex-1 overflow-y-auto"
12             hx-on::after-settle="
13                 this.scrollTo({ top: this.scrollHeight, behavior: 'smooth' });
14                 htmx.trigger(this, 'enable');
15             ">
16        </div>
17
18        <form id="form"
19              hx-post="{% url 'chat:reply' %}"
20              hx-target="#messages"
21              hx-swap="beforeend"
22              hx-on::before-request="htmx.trigger(this, 'disable');"
23              hx-on::after-request="this.reset();">
24            {% csrf_token %}
25            <input type="text" name="message"
26                   class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:border-blue-500"
27                   autocomplete="off"
28                   autofocus="autofocus"
29                   placeholder="메시지를 입력하세요..."
30                   :disabled="!enable"
31                   x-effect="if(enable) $el.focus()"/>
32        </form>
33    </div>
34
35{% endblock %}