The Island Architecture

요약: 아일랜드 아키텍쳐를 사용하면 서버 렌더링되는 웹 앱 내에서 작은 단위의 인터렉트에 집중한 분리된 코드 조각을 만들게 된다. 아일랜드 아키텍쳐는 점전직으로 향상되는 HTML을 만들어낸다. 단일 앱이 전체 페이지 렌더링을 컨트롤하는 대신, 다수의 엔트리 포인트가 존재한다. 이 “아일랜드”의 상호작용을 위한 스크립트들은 독립적으로 클라이언트에 전송되고 hydrate되며 그 외 나머지 페이지들은 정적 HTML로 렌더링하게 된다.

자바스크립트의 로딩과 처리가 과하면 성능에 악영향을 미친다. 그러나 정적 웹 사이트에서도 어느정도의 상호작용과 자바스크립트를 필요한 경우가 많다. 지금까지 아래 두 항목들 사이에 적절한 균형잡힌 앱을 만들기 위한 정적 렌더링의 다양한 방법을 알아보았다.

  1. CSR 사이트와 유사한 수준의 상호작용
  2. SSR 사이트의 SEO 최적화

SSR의 핵심은 서버에서 렌더링한 HTML과 클라이언트에서 rehydrate를 할 때 필요한 자바스크립트와 함께 클라이언트에 전송하는 것이다. Rehydration은 서버 렌더링 후 클라이언트 측에서 UI컴포넌트들의 상태를 재 생성하는 과정이다. 이 rehydration도 비용이 있기 때문에. 각 SSR 바리에이션들은 이 과정을 최적화하고 있다. 이는 부분적인 hydration과 성능에 문제가 생길만한 컴포넌트들의 렌더링에 대한 스트리밍 처리로 해결하고 있지만, 이런 기법들도 자바스크립트를 클라이언트에 전송하는것은 마찬가지이다.

“아일랜드 아키텍쳐”라는 용어는 Katie Sylor-Miller와 Jason Miller에 의해 대중화되었다. 이 아키텍쳐는 상호작용이 가능한 요소들을 “아일랜드” 란 개념으로 클라이언트에 독립적으로 전송하며 그 외에는 정적 HTML을 사용하는 방식을 사용해 클라이언트에 전송해야 하는 자바스크립트를 최소화하는 것을 목표로 하고 있다. 아일랜드는 컴포넌트 기반의 아키텍쳐로 정적/동적 아일랜드가 있는 구획화된 뷰를 만드는 것을 제안하고 있다. 페이지 내에 정적 구역은 상호작용이 필요없는 HTML로써 hydration을 할 필요가 없고, 동적 구역은 HTML과 렌더링 후 이를 hydration하기 위한 스크립트의 조합으로 이루어져 있다.

이어서 아일랜드 아키텍쳐를 구현하는데 사용할 수 있는 다양한 옵션과 함께 조금 더 자세히 살펴보자.

동적 컴포넌트의 아일랜드

대부분의 페이지는 정적 컨텐츠와 동적 컨텐츠의 조합이다. 일반적으로 페이지는 분리될 수 있는 상호작용 가능 영역을 포함함 정적 컨텐츠로 구성된다. 아래는 이에 대한 예시이다.

  1. 블로그 포스트, 뉴스 기사, 어떤 조직의 홈페이지는 텍스트와 이미지는 물론 소셜 미디어 임베드나 채팅 같은 상호작용 가능한 컴포넌트들을 가지고 있다.
  2. 커머스의 상품상세의 경우 정적인 상품 상세설명과 앱 내의 다른 페이지에 대한 링크를 가지고 있다. 페이지 내의 다른 부분에서는 이미지 캐러셀이나 검색과 같은 상호작용 가능한 컴포넌트를 렌더링하고 있다.
  3. 일반적인 은행 웹 사이트의 경우 정적인 거래내역을 상호작용 가능한 필터 기능과 함께 제공한다.

정적 컨텐츠들은 상태가 없으며 이벤트를 발생하지도 않기 때문에 렌더링 후 rehydration할 필요가 없다. 렌더링 후 버튼, 필터, 검색과 같은 동적 컨텐츠들은 각각의 이벤트 핸들러를 연결되어야 한다. VirtualDOM은 클라이언트 측에서 재 생성되며 이런 재 생성, rehydration, 이벤트 핸들링 함수들은 클라이언트에 보내야 하는 자바스크립트의 크기를 키운다.

아일랜드 아키텍쳐는 페이지 별로 서버 사이드 렌더링을 하게 되며 각 페이지는 각각의 정적 컨텐츠를 포함하고 있다. 하지만 이 때 렌더링된 HTML들은 동적 컨텐츠에 대해서는 placeholder로 렌더링하게 된다. 각각의 동적 컨텐츠 placeholder는 서버 렌더링 결과와 이를 클라이언트에서 hydrate하기 위한 스크립트를 포함한 앱과 같이 구성되어 있다.

점진적 hydration 패턴에서는 hydration 구조가 페이지의 위에서부터 아래로 진행된다. 페이지 자체에서 각 컴포넌트의 hydration이 스케쥴링된다. 아일랜드 아키텍쳐에서 각 컴포넌트들은 각자의 hydration 스크립트를 가지고 있으며 이는 페이지 내의 다른 스크립트들과 별개로 비동기로 실행된다. 한 컴포넌트의 성능 이슈가 다른 컴포넌트에게 영향을 주지 않는다.

아일랜드 아키텍쳐 구현하기

아일랜드 아키텍쳐는 여러 기술들의 일부 컨셉들을 최적화된 형태로 조합하여 사용하고 있다. Jekyll 또는 Hugo는 템플릿 기반 정적 사이트 생성 도구로 정적 컴포넌트를 페이지에 렌더링하는 기능을 제공한다. 대부분의 모던 자바스크립트 프레임웍은 동형 렌더링을 지원하여 동일한 코드를 서버에서도, 클라이언트에서도 렌더링 할 수 있다.

Jason의 블로그 포스트에서는 컴포넌트의 hydrating 스케쥴러를 구현하기 위해 requestIdleCallback() 을 사용하는것을 권장하고 있다. 아일랜드 아키텍쳐를 위해 정적 동형 렌더링과 컴포넌트 레벨의 부분 hydration의 스케쥴링이 필요한데 따라서 프레임웍은

  1. 클라이언트 측에 추가 자바스크립트 전송 없이 서버에서 정적 컨텐츠 렌더링하는것을 지원해야 한다.
  2. 정적 컨텐츠를 만들 때 placeholder를 통한 독립적인 동적 컴포넌트를 임베드할 수 있어야 한다. 각 동적 컴포넌트는 자기 자신을 hydrate하는 스크립트를 포함하고 있어야 하며, 이 스크립트들은 requestIdleCallback()을 활용해 메인 스레드가 유휴 상태일 때 실행되어야 한다.
  3. 컴포넌트의 동형 렌더링을 지원하여 서버에서 렌더링 되고 클라이언트에서 hydration될 수 있어야 한다.

아래 이야기할 옵션들을 사용하여 아일랜드 아키텍쳐를 구현할 수 있다.

프레임워크

요즘은 아일랜드 아키텍쳐를 지원하는 프레임웍이 많은데 그 중 주목할만한 것을 뽑자면

  1. Marko: Marko는 서버렌더링 성능을 위해 eBay에서 개발하고 관리하는 오픈소스 프레임웍이다. 이 프레임웍은 스트리밍 렌더링과 자동 부분적 hydration을 조합하여 아일랜드 아키텍쳐를 지원한다. HTML과 기타 정적 에셋들은 준비되자마자 클라이언트 측에 즉시 스트리밍 된다. 자동 부분적 hydration은 상호작용 가능한 컴포넌트들이 스스로 hydrate되도록 한다. Hydration코드는 상호작용 가능한 컴포넌트에만 포함되어 전달되며 브라우저에서 상태를 변경시킬 수 있다. 동형 렌더링을 지원하여 Marko컴파일러는 클라이언트 또는 서버어디든 최적화된 코드를 생성한다.
  2. Astro: Astro는 React, Preact, Svelte, Vue 등의 프레임웍의 UI컴포넌트들에 대해 가벼운 정적 HTML페이지를 생성해낼 수 있는 정적 사이트 빌더이다. 부분적 hydration을 내장하고 있어 클라이언트 측 자바스크립트가 필요한 컴포넌트들은 의존 모듈과 함께 개별적으로 로드된다. 또한 Astro는 컴포넌트가 보여질 때 지연로딩되도록 할 수 있다. 아래에 구현 예제를 참고하자.
  3. Eleventy + Preact: Markus Overlehner는 정적 사이트 빌더 Eleventry와 동형 렌더링을 지원하는 Preact의 부분적 hydration을 함께 활용하여 아일렌드 아키텍쳐의 데모를 시연했다. 데모는 hydration을 지연시키는것도 구현하고 있다. 상호작용 가능한 컴포넌트는 WithHydration 래퍼를 사용하여 클라이언트 측에서 hydrate되고 있다.

Marko와 Eleventy는 Jason에 의해 아일랜드 아키텍쳐가 알려지기 전보다 일찍 나왔지만 그것을 구현하기 위한 기능을 모두 지원한다, 반면 Astro는 해당 아키텍쳐를 기반으로 만들어졌다. 이어진 섹션에서 Astro로 구현한 블로그 예제를 확인해 보자.

구현 예제

아래 예제는 Astro로 구현한 블로그이다. SamplePost는 SocialButtons라는 상호작용 가능한 컴포넌트를 import하고 있다. 이 컴포넌트는 HTML템플릿에서 마크업 내 필요한 위치에 포함되어 있다.

Astro page (SamplePost.astro)

---
// Component Imports
import { SocialButtons } from '../../components/SocialButtons.tsx';
---

<html lang="en">
 <head>
   <link rel="stylesheet" href="/blog.css" />
 </head>

 <body>
   <div class="layout">
     <article class="content">
       <section class="intro">
         <h1 class="title">Post title (static)</h1>
         <br/>
         <p>Post sub-title (static)</p>
       </section>
       <section class="intro">
           <p>This is the  post content with images that is rendered by the server.</p>
           <img src="https://source.unsplash.com/user/c_v_r/200x200" />
           <p>The next section contains the interactive social buttons component which includes its script.</p>
       </section>
       <section class="social">
           <div>
           <SocialButtons client:visible></SocialButtons>
           </div>
       </section>
     </article>
   </div>
 </body>
</html>

SocialButtons 컴포넌트는 각 이벤트 핸들러를 구현한 Preact 컴포넌트이다.

SocialButtons component (SocialButtons.tsx)

import { useState } from 'preact/hooks'

/** a counter written in Preact */
export function SocialButtons() {
  const [count, setCount] = useState(0)
  const add = () => setCount(i => i + 1)
  const subtract = () => setCount(i => i - 1)

  return (
    <>
      <div>{count} people liked this post</div>
      <div align="right">
        <img src="/like.png" width="32" height="32" onclick={add}></img>
        <img src="/unlike.png" width="32" height="32" onclick={subtract}></img>
      </div>
    </>
  )
}

SocialButtons 컴포넌트는 런타임에 페이지에 포함되고 클릭 이벤트가 필요에 따라 동작할 수 있게 클라이언트 에서 hydrate된다.

Astro는 HTML, CSS, Script를 깔끔하게 분리할 수 있도록 되어있어 컴포넌트 기반 설계를 하기 좋다. 설치하고 웹 사이트를 만드는것 또한 쉽다.

장점과 단점

아일랜드 아키텍쳐는 서버사이드 렌더링, 정적 사이트 생성, 부분적 hydration의 일부 기능들을 조합하여 사용한다. 아일랜드 아키텍쳐를 도입할때의 장점은 아래와 같다,

  1. 성능: 클라이언트에 보내는 자바스크립트의 크기가 줄어든다. 자바스크립트는 오직 상호작용이 가능한 컴포넌트를 위해서만 포함되는데, 이는 전체 페이지에 대해 VirtualDOM을 생성하고 rehydrate하기 위한 코드량보다 훨씬 적다. 줄어든 스크립트 크기만금 TTI가 빨라진다.

    가이드 문서 사이트를 Astro로 개발하면 Next.js 혹은 Nuxt.js로 만드는 것에 비해 자바스크립트를 83%가량 줄일 수 있었다. 또 다른 사용자는 성능 향상을 리포트했다.

    출처: https://divriots.com/blog/our-experience-with-astro/
    출처: https://divriots.com/blog/our-experience-with-astro/
  2. SEO: 모든 정적 컨텐츠가 서버에서 만들어지기 때문에 검색엔진에 최적화되어 있다.

  3. 중요 컨텐츠 우선 처리 가능: 블로그, 뉴스 기사, 상품페이지의 중요 컨텐츠들은 거의 즉시 사용자게에 보여진다. 상호작용이 가능한 컨텐츠는 이런 주요 컨텐츠가 렌더링 된 후 점차적으로 사용가능하게 된다.

  4. 접근성: 앱 내 다른 페이지에 접근하기 위한 일반적인 정적 HTML 링크는 페이지의 접근성을 향상시킨다.

  5. 컴포넌트 기반: 컴포넌트 기반 설계의 장점을 그대로 가져갈 수 있다. 재사용이 용이하며 유지보수성 또한 좋다.

이런 장점에도 불구하고 아직 컨셉이 초기 단계라 제한되는 단점들이 존재한다.

  1. 아일랜드 아키텍쳐를 사용하기 위해서 선택할 수 있는 프레임웍이 제한적이다. 기존애 존재하는 앱을 마이그레이션 하기 위해서는 많은 노력이 필요할 것이다.
  2. Jason 본인의 최초 블로그 포스트 외에 이 아이디어에 대한 논의가 그리 많이 되지 않고 있다.
  3. 아일랜드 아키텍쳐를 지원한다고 가이드 하는 새로운 프레임웍이나 라이브러리들이 많이 나오고 있어 제대로 된 것을 찾기가 어렵다.
  4. 상호작용이 많이 이뤄지는 소셜 미디어 앱의 경우 이 아키텍쳐가 적합하지 않을 수 있다. 이 경우 정말 많은 아일랜드를 구현해야 할 것이다.

아일랜드 아키텍쳐는 개념 자체가 나온지 얼마 되지는 않았지만 성능상의 이점으로 인해 앱의 속도를 크게 향상시킬 수 있다. 페이지 성능에 미치는 영향을 최소화하며 동시에 동적 컴포넌트를 통해 상호작용을 지원하며 정적 컨텐츠를 렌더링하기 위한 SSR 사용을 하게 된다. 아일랜드 아키텍쳐가 앞으로 커뮤니티가 발전하여 사용가능한 구현에 선택지가 많아지길 바란다.

읽을만한 내용들