웜블러드에서는 ReactVueJS 같은 단일 페이지 구동(SPA) 기반 웹프론트엔드 기술을 사용하지 않습니다. 그 이야기를 좀 해보죠.

요즘 웹개발의 트렌드가 과연 최선일까?

요즘의 웹개발은 대개, SPA 형태의 프론트엔드와 API 서버 역할의 백엔드로 구분되어 진행됩니다. 저는 지난 10여년간 여러 개발팀들을 이끌어오며 여러 문제들을 겪으면서, 이런 구조가 과연 효과적인가 하는 고민을 해왔습니다.

  • 백엔드에서는 데이터를 JSON으로 직렬화하고, 프론트엔드에서는 JSON을 역직렬화합니다. 단순한 JSON 타입을 그대로 쓰지 않고 클래스로 맵핑하려면, 백엔드, 프론트엔드 각각이 직렬화/역직렬화 장치를 마련해야 합니다. 각각 중복된 중간과정이 존재합니다.
  • 백엔드는 API를 만들고 swagger로 그것을 문서화하고, 프론트엔드는 API를 가져다가 화면을 만들기 위해서 그 문서를 사용합니다. API를 잘 사용하기 위해서는 문서를 풍부하게 만들어야 하는데, 그러다보면 개발이 느려질 수 있습니다. API 변경이 발생하면 문서도 함께 업데이트해줘야 합니다. 백엔드가 만들어둔 API가 프론트엔드에서 사용하기 적절하지 않거나 미진한 경우도 있고, 벡엔드가 만들어둔 용도와 다르게 사용하는 경우도 종종 발생합니다.
  • 백엔드와 프론트엔드가 서로 속도도 다르고 업무하는 시기도 다릅니다. 프론트엔드는 이미 동작하는 API가 나와있어야 개발을 완료할 수 있습니다. 개발 시기가 달라도 개발을 할 수 있도록 API 명세를 먼저 만들어두고 그 규약에 맞춰서 개발하기도 합니다. 하지만 실제로 화면을 동작시키다보면 명세에서 부족한 것들이 나오게 마련입니다. 상대 파트에 수정 요청을 하면, 지금 하는 작업을 어느 정도 마무리하고 해주겠다고 합니다.

frontend and backend

여러분도 이런 상황을 겪어보셨을겁니다.

제가 웜블러드에 합류하고 회사에서 유일한 웹개발자로 혼자서 웹사이트를 만들려니, 프론트엔드 따로 백엔드 따로 만들면서 속도를 내기는 어려워보였습니다.

가만히 생각해보면, 딜매치 같은 서비스는 굳이 SPA로 개발할 필요가 없습니다. Google Docs나 Notion처럼 사용자 상호작용이 긴밀한 웹애플리케이션과는 달리, 딜매치는 웹페이지의 성격이 강합니다. 과장을 좀 보태서 말하자면, 초기 버전의 딜매치는 워드프레스로 만들 수도 있었죠.

그래서, 그 옛날 PHP와 같은 방식의, 멋있게 말하자면 Web 1.0 방식으로 웹개발을 시작했습니다. 옛날에는 다 그렇게 개발했었죠.

하지만 그 방식에는 몇 가지 치명적인 약점이 있었는데, 처음으로는, 페이지를 이동할 때마다 HTML 문서 전체를 새로 로딩하기 때문에 화면이 깜빡거리는 것 같은 현상이 발생한다는겁니다. 그리고 POST가 필요한 동작들은 별도의 페이지로 구성하게 된다는 문제도 있었습니다. 물론 javascript나 Ajax를 써서 한 화면에도 모달창 등을 통해 동작하도록 구현할 수는 있겠지만요.

오래된 미래, htmx

기존의 HTML에는 몇 가지 생각해볼 점이 있습니다.

  • HTML 표준에서 상호작용을 담당하는 요소는 a, form 이렇게 단 두 개입니다. 소위 Web1.0 시절부터 지금까지요. HTML에 풍부한 요소들이 도입되는 동안, 상호작용 부분에 있어서는 큰 발전이 없었습니다.
  • 그리고 a, form 요청은 응답으로 항상 HTML 문서 전체를 받아오는 것을 전제로 합니다.

이것을 뒤집어서 생각해보면, 이렇습니다.

  • a, formGET이나 POST 요청을 보낼 수 있을까요? 다른 태그들에서도 GET이나 POST 요청을 보낼 수 있다면 어떨까요?
  • 요청의 결과로 문서 전체가 아니라 일부만 받아오고, 기존 문서의 일부만을 갱신할 수 있다면 어떨까요?

htmx1는 간단한 자바스크립트 라이브러리로, HTML의 속성에 몇 가지를 추가하여 a, form 외의 일반적인 태그에서도 GET이나 POST 요청을 받아올 수 있게 하고, 요청의 결과 일부를 DOM의 특정 부분에 치환할 수 있게 합니다. 이렇게 간단한 방식으로 HTML을 확장하는 것으로도 많은 것들을 할 수 있습니다.

htmx를 사용한 아래와 같은 코드를 봅시다. Medium에서처럼, 박수 아이콘을 클릭하면 박수 숫자가 증가하는 내용입니다.

<div id="post-card-1">
  <form>
    <input type="hidden" name="post_id" value="1"/>
    <button hx-post="#" hx-target="#post-card-1">Clap 3 times</button>
  </form>
</div>
<div id="post-card-2">
  <form>
    <input type="hidden" name="post_id" value="2"/>
    <button hx-post="#" hx-target="#post-card-2">Clap 10 times</button>
  </form>
</div>

button을 클릭하면 POST 요청이 발생합니다. 서버에서 카운트를 올리고 <div id="post-card-x">...</div>에 해당하는 HTML 코드 조각을 응답값으로 내려주면, htmx는 그 코드를 hx-target="..."에 지정된 대상에 치환합니다. 대략 코드의 느낌만 보시죠.

def post_list(request):
    if not request.htmx:
        for post in post:
            html.append(render_post(post))
        return ''.join(html)
    else:
        post_id = request.POST.get('post_id')
        post = Post.objects.get(id=post_id)
        post.clap()
        return render_post(post)


def render_post(post):
    return f'<div id="post-card-{post.id}">
  <form>
    <input type="hidden" name="post_id" value="{post.id}"/>
    <button hx-post="#" hx-target="#post-card-{post.id}">Clap {post.clap_count} times</button>
  </form>
</div>

post_list 함수에서는, htmx 요청이 아니라 일반적인 요청일 때는 딜 목록 전체에 대한 HTML을 응답합니다. 하지만 htmx 요청일 때는, 클릭된 특정 게시글에 대한 부분적인 HTML만을 응답합니다. 그렇게 응답된 HTML 조각은 증가된 박수 갯수를 담고 있고, 기존의 HTML 일부분을 대체합니다.

화면의 상호작용이 구현되었습니다. javascript도, React도, webpack 빌드 과정도 전혀 없이, 단순하고 우아한 HTML, 그리고 그 HTML 문자열을 만들어내는 Python 코드만 가지고서요.

이렇게 HTML 조각을 응답으로 내려주는 방법은 사실 새로운 방법이 아닙니다. Ajax가 나오고 나서, 그리고 JSON이 대중화되기 이전에는 이렇게 HTML 조각을 응답하는 방법이 종종 사용되었습니다. htmx는 이러한 사용 패턴을, HTML의 속성이라는 형태로 구조화한 것입니다.

웜블러드의 모험

웜블러드에서는 Django를 이용하여 단순하고 전통적인 웹서비스 개발 방식을 사용하면서, 매끄러운 상호작용이 필요한 부분에는 htmx를 사용하고 있습니다. 물론 개중에는 클라이언트에서 구동하는 것이 더 바람직한 동작도 있고, 그런 경우에는 단순한 vanilla javascript를 사용합니다.

단순하고 평이하지만 제 몫을 하는 기술을 사용하기 때문에, 한 사람의 개발자가 DB 설계, 비즈니스 로직 구현, HTML/CSS를 통한 화면 구현, 상호작용 구현 등을 모두 개발할 수 있습니다. 때문에 각 사람이 하나의 기능을 백엔드 끝에서부터 프론트엔드 끝까지 맡아 개발합니다. 인터페이스를 맞출 필요도, API를 의도대로 사용하지 않는 문제도 존재하지 않습니다. RESTful API를 개발해두지 않아서 앱은 어떻게 개발할까 고민하다가, Progressive Web App을 사용하여 앱 경험을 제공하고 있습니다. 이렇게, 단순하고 기본적인 Python, HTML/CSS, vanilla javascript만 알면 딜매치 서비스의 이쪽 끝에서 저쪽 끝까지의 모든 코드를 어려움 없이 이해하고 즉시 개발할 수 있습니다.

웜블러드는 이렇게 단순화된 기술스택을 사용해서, 유의미한 작은 기능들을 2~5일 정도 규모로 끊임없이 작고 민첩하게 개발해가고 있습니다. 여러 개발조직을 경험해왔던 한 비개발 직군 임원분은, ‘개발을 이렇게 빠르게 하는 개발조직은 처음 봤다’라고 감탄하시더군요.

아직 htmx의 역사가 오래되지 않고 사용자층이 많지 않아서 베스트 프랙티스가 많이 쌓여있지 않습니다. 웜블러드는 딜매치의 시범 서비스를 시작한 2023년 초부터 htmx를 실험적으로 도입해왔고, 앞으로도 웜블러드 안에 계속 쌓여갈 경험들을 발굴하여 소개해가고자 합니다. 그리하여 한국에도 좀 더 대안적이고 실용적인 웹 개발 흐름이 더욱 더 생겨나기를 기대합니다.


  1. htmx가 만들어진 배경, 철학을 더 깊이 알아보시려면, htmx 개발자들이 직접 쓴 hypermedia.systems라는 책을 읽어보세요.