웹 애플리케이션 아키텍처의 미래를 살펴봅시다.
원문입니다 : https://www.epicweb.dev/the-webs-next-transition
웹은 25년 전에 시작된 기술로 구성됩니다.
HTTP, HTML, CSS, JS는 모두 90년대 중반(제가 8살 때)에 처음으로 표준화되었습니다.
그 이후로 웹은 유비쿼터스 애플리케이션 플랫폼으로 진화했습니다.
웹이 발전함에 따라 웹 애플리케이션 개발을 위한 아키텍처도 발전했습니다.
오늘날 웹 애플리케이션을 구축하기 위한 핵심 아키텍처가 많이 있습니다.
현재 가장 인기 있는 아키텍처는 SPA(단일 페이지 앱)이지만
우리는 매일 웹 애플리케이션 구축을 위해 새롭고 향상된 아키텍처로 전환하고 있습니다.
<a> 및 <form> 요소는 웹 역사의 처음부터 존재했습니다.
브라우저가 서버에서 무언가를 가져오기 위한 링크와
브라우저가 서버로 무언가를 보내고 그 대가로 무언가를 받기 위한 form입니다.
이 양방향 통신은 처음부터 사양의 일부였으며,
덕택에 지금도 웹에서 강력한 응용 프로그램을 만드는 것이 가능합니다.
주요 아키텍처는 다음과 같습니다(인기 있는 순서대로).
- Multi-Page Apps(MPAs)
- Progressively Enhanced Multi-Page Apps (PEMPAs, 일명 “자바스크립트를 끼얹은”)
- Single Page Apps (SPAs)
- The next transition(다음 단계)
MPA, SPA 같은 단어는 그냥 번역하지 않기로 했습니다.
사실 이 정도의 글을 찾아오실 분이면 당연히 아실 것이라 생각합니다.
각 아키텍처에는 장단이 있습니다.
문제점(pain point)은 고유한 절충안과 함께
다음 아키텍처로의 전환에 동기를 부여합니다.
애플리케이션을 어떻게 구축하든 거의 항상 서버에서 실행되는 코드가 필요합니다.
(로컬 스토리지에 게임 상태를 저장하는 Wordle과 같은 게임은 예외입니다).
이러한 아키텍처를 구별하는 것 중 하나는 코드가 있는 위치입니다.
각각을 차례로 살펴보고 시간이 지남에 따라 코드의 위치가 어떻게 변했는지 살펴보겠습니다.
각 아키텍처를 다루면서 다음 코드 사용 사례를 구체적으로 고려할 것입니다.
- Persistence - 데이터베이스에서 데이터 저장하고 읽기
- Routing - URL 기반 트래픽 전달
- Data fetching - 영속성에서 데이터 가져오기
- Data mutation - 영속성에서 데이터 변경하기
- Rendering logic - 사용자에게 데이터 보여주기
- UI Feedback - 사용자 상호작용에 대한 응답
당연히 웹 애플리케이션에는 이 비트보다 더 많은 부분이 있지만,
이것들은 웹 개발자로서 우리가 대부분의 시간을 보내는 곳이자 가장 많이 움직이는 부분입니다.
프로젝트 규모와 팀 구조에 따라 우리는 위 범주 전체를 개발할 수도 있고,
일부만 작업할 수도 있습니다.
Multi-Page Apps (MPAs)
웹 역사 초기 당시 웹 브라우저의 기능을 기반으로 웹에서 동작하는 유일한 아키텍처였습니다
MPA을 사용하면 우리가 작성하는 모든 코드가 서버에 있습니다.
UI 피드백 코드는 클라이언트 사용자 브라우저에서 처리됩니다.
MPA 아키텍처의 동작
문서 요청 :
사용자가 주소 표시줄에 URL을 입력하면 브라우저는 우리 서버에 요청을 보냅니다.
서버의 라우팅 로직은 데이터를 검색하기 위해 영속성 코드와 통신하여 영속성의 데이터를 가져오는 함수를 호출합니다.
영속성에서 가져온 데이터는 렌더링 로직에서 클라이언트로의 응답으로 보낼 HTML을 결정하는 데 사용됩니다.
그 동안 브라우저는 일종의 보류 상태(pending state)로 사용자 피드백(일반적으로 파비콘 위치의 스피너)을 제공합니다.
리디렉션 & 변경(mutation) 요청 :
사용자가 양식(form)을 제출(submit)하면 브라우저는 양식을 서버로 보내는 요청으로 직렬화합니다.
서버의 라우팅 로직은 데이터베이스 업데이트를 위해 영속성 코드와 통신하여 영속성의 데이터를 변경하는 함수를 호출합니다.
그런 다음 브라우저가 새로운 UI를 가져오기 위해 GET 요청을 트리거하도록 리디렉션으로 응답합니다.(사용자가 URL을 처음 입력했을 때 발생한 것과 동일한 작업을 트리거함).
다시, 브라우저는 보류 중 UI로 사용자 피드백을 제공합니다.
참고: 성공적인 변경은 새 HTML이 아닌 리디렉션 응답을 전송하는 것이 중요합니다.
그렇지 않으면 히스토리 스택에 POST 요청이 쌓이게 되어
back 버튼을 누르면 POST 요청이 다시 트리거됩니다.
MPA의 장단점
MPA의 멘탈 모델은 간단합니다.
우리는 그 당시 그것에 대해 감사해하지 않았습니다.
요청에서 주로 쿠키에 의해 처리되는 일부 상태와 복잡한 흐름이 있었지만,
대부분의 경우 모든 것이 요청/응답 주기 시간 내에 발생했습니다.
이 아키텍처의 단점은 다음과 같았습니다.
- 전체 페이지 새로 고침
- 어떤 것은 어렵게 만들고(포커싱 관리)
- 다른 것은 비실용적이며(트윗에 좋아요를 표시할 때마다 전체 페이지를 새로 고친다고 상상해 보세요...)
- 일부는 불가능합니다(애니메이션 페이지 전환).
- 사용자 피드백 컨트롤
- 파비콘이 스피너로 바뀌는 것도 좋지만 종종 더 나은 UX는 사용자가 상호작용한 UI에 시각적으로 더 가까운 피드백입니다.
- 그리고 그것은 확실히 디자이너들이 브랜딩 목적으로 커스터마이징하기를 좋아하는 것입니다.
- 낙관적(optimistic) UI는 어떻습니까?
MPA를 지속적으로 개선하여 더 많은 사용 사례에 대해 보다 실용적인 옵션으로 만들고 있다는 점은 주목할 만합니다.
그러나 대부분의 웹 애플리케이션에서는 여전히 충분하지 않습니다.
어쨌든 당시에는 이 문제가 표준 위원회의 생각과는 거리가 멀었고 사용자들은 지금 더 많은 것을 원했습니다!
Progressively Enhanced Multi-Page Apps (PEMPAs)
Progressive Enhancement는 웹 애플리케이션이
모든 웹 브라우저에서 동작하고 사용 가능해야 하며
브라우저가 경험을 향상시키기 위해 가지고 있는 추가 기능을 활용해야 한다는 아이디어입니다.
브라우저 기능 활용의 필요성에 대해서만 말하자면 말입니다...
XMLHttpRequest는 1998년 Microsoft의 Outlook Web Access 팀에서 처음 개발했지만 2016년까지 표준화(standardized)되지 않았습니다. 믿어지시나요?
물론 이것이 브라우저 벤더와 웹 개발자를 막지는 못했습니다.
AJAX는 2005년 용어로 대중화되었고 많은 사람들이 브라우저에서 HTTP 요청을 하기 시작했습니다.
비즈니스는 UI를 원래 위치에서 업데이트하기 위한 적은 양의 데이터를 위해 서버로 돌아갈 필요가 없다는 아이디어를 기반으로 구축되었습니다.
이를 통해 PEMPA를 구축할 수 있습니다.
PEMPA 아키텍처
이제 우리는 브라우저의 UI 피드백을 담당할 뿐만 아니라
라우팅, 데이터 가져오기, 데이터 변형, 서버에서 담당하는것 외를 담당하는
클라이언트 렌더링 로직도 담당하게 되었습니다.
이것을 통해 우리는 무엇을 얻었나요?
점진적 개선의 이면에 있는 아이디어는 우리의 목표가 기능적인 앱이어야 한다는 것입니다.
특히 2000년대 초반에는 사용자가 멋진 새 AJAX 함수를 실행할 수 있는 브라우저를 사용하거나
앱과 상호 작용하기 전에 JavaScript를 다운로드할 수 있을 만큼 충분히 빠른 네트워크에 있다고 보장할 수 없었습니다.
따라서 기존 MPA 아키텍처를 유지하고
JavaScript만 사용하여 사용자 경험을 향상해야 했습니다.
즉, 우리가 원하는 사용자 경험 향상 수준에 따라 거의 모든 범주에서 코드를 작성해야 할 수도 있지만,
영속성은 예외입니다.
(정말 깔끔한 오프라인 모드 지원을 원하지 않는 한 업계 표준 관행이 아니므로 차트에 포함되어 있지 않습니다.)
또한 클라이언트가 생성할 AJAX 요청을 지원하기 위해 백엔드에 더 많은 코드를 추가해야 했습니다.
따라서 네트워크의 양쪽에 뭔가가 더 많이 생겼습니다.
이 때는 jQuery, MooTools 등의 시대입니다.
PEMPA 아키텍처 동작
문서 요청 :
사용자가 처음으로 문서를 요청할 때 MPA 예제에서와 동일한 일이 여기에서도 발생합니다.
그러나 PEMPA는 브라우저에 의해 향상된 기능을 위한 <script> 태그를 포함하여
클라이언트에 JavaScript를 로드합니다.
클라이언트 측 탐색 :
사용자가 앱 내에 있는 href가 있는 anchor 요소를 클릭하면
클라이언트 측 데이터 가져오기 코드가 기본 전체 페이지 새로 고침 동작을 방지하고
JavaScript를 사용하여 URL을 업데이트합니다.
그런 다음 클라이언트 라우팅 로직은 UI에 발생해야 하는 업데이트를 결정하고
데이터 가져오기 라이브러리가 서버 끝점에 네트워크 요청을 하는 동안
보류 상태(UI 피드백) 표시를 포함한 업데이트를 수동으로 수행합니다.
서버 라우팅 로직은 데이터 가져오기 코드를 호출하여 지속성 코드에서 데이터를 검색하고
이를 응답으로 보냅니다(XML 또는 JSON).
클라이언트는 렌더링 로직에서 해당 데이터를 최종 UI 업데이트를 수행하는 데 사용합니다.
리디렉션 & 변경(mutation) 요청 :
사용자가 양식을 제출하면 클라이언트 측 데이터 변경 로직이 기본 전체 페이지 새로 고침 및 게시 동작을 방지하고
JavaScript를 사용하여 양식을 직렬화하고 데이터를 서버 끝점으로 보냅니다.
그런 다음 서버 라우팅 로직은 데이터 변형 함수를 호출합니다.
이 함수는 지속성 코드와 상호 작용하여 변형을 수행하고 업데이트된 데이터로 클라이언트에 응답합니다.
클라이언트 렌더링 로직은 업데이트된 데이터를 사용하여 UI를 업데이트하며,
어떤 경우에는 클라이언트 측 라우팅 로직이 클라이언트 측 탐색 흐름과 유사한 흐름을 트리거하는 다른 위치로 사용자를 보냅니다.
즉, 리디렉션을 클라이언트에서 수행
PEMPA의 장단점
클라이언트 측 코드를 이용해, UI 피드백 책임을 클라이언트로 가져옴으로써 MPA의 문제를 확실히 해결했습니다.
이제 훨씬 더 많은 제어 권한이 있으며 사용자에게 더 맞춤화된 앱과 같은 느낌을 줄 수 있습니다.
불행히도 사용자가 원하는 최상의 경험을 제공하기 위해 라우팅, 데이터 가져오기, 변형 및 렌더링 로직을 담당해야 합니다.
여기에는 몇 가지 문제가 있습니다.
1. prevent default
브라우저가 라우팅 및 양식 제출을 수행하는 것만큼 좋은 작업을 수행하지 않습니다.
페이지의 데이터를 최신 상태로 유지하는 것은 이전에는 전혀 문제가 되지 않았지만
이제는 클라이언트 측 코드의 절반 이상이 되었습니다.
또한 경쟁 조건, 양식 재제출 및 오류 처리는 버그가 숨어들기 딱 좋은 장소입니다.
2. 관리할 코드
이전에 작성할 필요가 없었던 관리할 코드가 훨씬 더 많습니다.
상관관계가 인과관계를 의미하지 않는다는 것을 알고 있지만
일반적으로 코드가 많을수록 버그가 더 많다는 것을 알았습니다 🤷♂️
3. 코드 중복
렌더링 로직과 관련하여 많은 코드 중복이 있습니다.
클라이언트 코드는 백엔드 코드와 동일한 방식으로 UI를 업데이트해야 합니다.
따라서 백엔드와 동일한 UI를 프론트엔드에서도 사용할 수 있어야 합니다.
그리고 대부분 백엔드와 프론트엔드는 완전히 다른 언어로 되어 있어 코드 공유가 시작되지 않습니다.
템플릿뿐만 아니라 로직도 마찬가지입니다.
문제는
"클라이언트 측 인터랙션을 만든 다음
클라이언트 코드에 의해 업데이트된 UI가 전체 페이지 새로 고침이었다면 발생했을 것과 동일한지 확인하기"
입니다.
이것은 놀랍게도 수행하기 어렵습니다.
(개발자가 일상적으로 사용하는 PEMPA 아키텍처를 활용하는 site가 있으며 매우 자주 버그가 발생합니다.)
4. 코드 정돈
PEMPA에서는 이것이 매우 어렵습니다.
데이터를 저장하거나 UI를 렌더링하는 중앙 집중화된 장소가 없었기 때문에
사람들은 거의 모든 곳에서 수동으로 DOM을 업데이트으며,
코드를 따라가기가 매우 어려웠고 개발 속도가 느려졌습니다.
5. 클라이언트 / 서버 간접 참조
API 경로와 이를 사용하는 클라이언트 측 데이터 가져오기 및 데이터 변형 코드 사이에 간접 참조가 있습니다.
네트워크 한쪽의 변경은 다른 쪽의 변경을 필요로 하며,
그 간접적인 참조 방식의 무결성은 클라이언트 쪽 코드와 서버 쪽 코드를 다 읽어봐야 정상인지 알수 있게 함으로,
우리가 아무 것도 손상시키지 않았다는 것을 알기 어렵게 했습니다.
이는 여우가 사냥개의 추적을 피하기 위해 강을 건너는 것처럼 디버깅의 체인이 끊기게 합니다.
네트워크는 이러한 간접화를 유발하는 장벽이 되었습니다.
이 때가 제가 웹 개발 세계에 입문한 즈음입니다.
이 때를 회상하면 그리움과 오싹함이 동시에 느껴집니다🍝.
Single Page Apps (SPAs)
백엔드에서 UI 코드를 삭제하기만 하면 중복 문제를 제거할 수 있다는 것을 깨닫는 데 오래 걸리지 않았습니다..
이 그래픽이 PEMPA 그래픽과 거의 동일하다는 것을 알 수 있습니다.
유일한 차이점은 서버의 렌더링 로직이 사라졌다는 것입니다.
UI에 대한 경로가 더 이상 필요하지 않기 때문에 일부 라우팅 코드도 사라졌습니다.
우리에게 남은 것은 API 경로뿐입니다.
Backbone, Knockout, Angular, Ember, React, Vue, Svelte 등의 시대입니다.
오늘날 대부분의 업계에서 사용하는 전략입니다.
SPA 아키텍처 동작
문서 요청 :
백엔드에 더 이상 렌더링 로직이 없기 때문에
모든 문서 요청(사용자가 URL을 입력할 때 수행하는 첫 번째 요청)은
정적 파일 서버(일반적으로 CDN)에서 제공합니다.
SPA의 초기 CDN에서 제공하는 정적 파일의 일종인 HTML 문서는
애플리케이션을 "마운트"하는 데 사용되는
<body>에 <div id="root"></div>가 있는
거의 항상 사실상 비어 있는 HTML 파일이었습니다.
그러나 요즘에는 프레임워크를 통해 "정적 사이트 생성"(SSG)이라는 기술을 사용하여
빌드 시 알고 있는 만큼의 페이지를 미리 렌더링할 수 있습니다.
리디렉션 & 변경(mutation) 요청 :
SPA의 다른 동작은 PEMPA와 동일합니다.
이제 우리는 주로 XMLHttpRequest 대신 fetch를 사용합니다.
SPA의 장단점
흥미로운 점은 위의 아키텍처 동작에서 PEMPA와의 유일한 차이점은,
문서 요청이 더 나쁘다는 것입니다!
그럼 이걸 왜쓰나요?
가장 큰 이점은 개발자 경험입니다. 이것이 PEMPA에서 SPA로의 전환을 주도한 최초의 원동력이었습니다.
코드 중복이 없다는 것은 엄청난 이점이었습니다.
우리는 다양한 수단을 통해 이 변경을 정당화했습니다(DX는 결국 UX를 위한 입력입니다).
불행히도 DX를 개선하는 것은 SPA가 실제로 우리를 위해 한 모든 것입니다.
나는 개인적으로 CDN이 서버가 생성할 수 있는 것보다 더 빠르게 HTML 문서로 응답할 수 있기 때문에,
SPA 아키텍처가 인지된 성능에 도움이 된다고 확신했던 것을 기억합니다.
그러나 현실의 시나리오에선 별 차이를 만들지 않는 것처럼 보입니다.(현대 인프라 덕택에 이는 더욱 사실이 아닙니다).
슬픈 현실은 SPA가 더 몇가지 케이스 처리를 쉽게 만드는 최신 기술들을 탑재하고도,
PEMPA와 동일한 문제들을 갖고 있다는 것입니다.
설상가상으로 SPA는 몇 가지 새로운 문제도 도입했습니다.
1. 번들 크기
2. 워터폴
데이터를 가져오기 위한 모든 코드가 이제 JavaScript 번들 내에 있기 때문에
데이터를 가져오기 전에 JS가 다운로드될 때까지 기다려야 합니다.
이에 더하여 이러한 JS 번들의 코드 분할 및 지연 로딩을 활용해야 하며
이제 document → app.js → page.js → component.js → data.json → image.png와 같은
렌더링 의존성 워터폴 현상이 발생합니다.
이것은 좋지 않으며 궁극적으로 훨씬 더 나쁜 사용자 경험을 초래합니다.
정적 콘텐츠의 경우는 이것의 대부분을 피할 수 있지만,
다양한 문제의 원인이 존재하며, SSG 솔루션 개발자들은
그들 솔루션 만의 방법으로 해당 문제를 해결하여 솔루션을 장사할 생각에 신나있습니다.
3. 런타임 성능
우리의 강력한 서버에서 실행되던 것이 이제는 사람들의 손에 있는 미니 컴퓨터에서 실행될 것입니다.
덕택에 우리가 더 적은 힘으로 사람들을 달에 보낼 수 있었다는 것을 알고 있지만 여전히 이것은 문제입니다.
4. 상태관리
이것은 큰 문제가 되었습니다.
이에 대한 증거로 이 문제를 해결하는 데 사용할 수 있는 라이브러리 수를 제시합니다😩.
이전에는 MPA가 DOM에서 상태를 렌더링하고 이를 참조/변경했습니다.
이제 우리는 서버에서 JSON을 받고,
데이터가 업데이트되었을 때 백엔드에 알려야 할 뿐만 아니라
해당 상태의 메모리 내 표현을 최신 상태로 유지해야 합니다.
이것은 소프트웨어에서 가장 어려운 문제 중 하나인 캐싱의 모든 문제를 나타냅니다.
일반적인 SPA에서 상태 관리는 사람들이 작업하는 코드의 30-50%를 차지합니다.
(이 통계는 인용이 필요하지만 당신은 이미 이 말이 사실임을 알고 있습니다)
이러한 문제를 해결하고 영향을 줄이는 데 도움이 되도록 라이브러리가 만들어졌습니다.
이것은 매우 도움이 되었지만, 몇몇은 이를 자바스크립트 개발을 더욱 피로(fatiguing)하게 한다 호소합니다.
SPA는 2010년대 중반부터 웹 앱을 구축하는 사실상의 표준이 되었습니다.
하지만 우리는 2020년대에 접어들었고 몇 가지 새로운 아이디어가 떠오르고 있습니다.
Progressively Enhanced Single Page Apps (PESPAs)
MPA는 단순한 멘탈 모델을 가지고 있습니다.
SPA에는 더 강력한 기능이 있습니다.
MPA 단계를 거쳐 SPA에서 작업하는 사람들은
지난 10년 동안 우리가 잃어버린 단순성(simplicity)을 진정으로 그리워합니다.
이는 SPA 아키텍처 이면의 동기가
PEMPA보다 개발자 경험을 개선하기 위한 것이었다는 사실을 고려하면 특히 흥미로울 것입니다.
SPA와 MPA를 단일 아키텍처로 병합하여 둘 다의 장점을 최대한 활용할 수 있다면
더 간단하고 유능한 것을 갖게 될 것입니다.
이것이 바로 PESPA입니다.
Progressive Enhancement의 목표도,
즉, 클라이언트 측 JavaScript가 없어도 충분한 기능을 갖춘 앱(functional app)임을 잊으면 안됩니다.
우리의 프레임워크가 Progressive Enhancement를 핵심 원칙으로 활성화하고 장려한다고 하면,
우리 앱의 기반(foundation)는 MPA의 단순한 멘탈 모델입니다.
특히, 무언가를 요청/응답 주기의 맥락에서 생각하는 멘탈 모델입니다.
이를 통해 SPA의 문제를 크게 제거할 수 있습니다.
Progressive Enhancement의 주요 이점은 "앱이 JavaScript 없이 작동한다"는 것이 아니라(좋은 부수적 이점이지만)
멘탈 모델이 훨씬 더 간단하다는 것입니다.
이를 위해 PESPA는 기본값을 방지할 때의 "브라우저를 에뮬레이트"해야 합니다.
따라서 서버 측 코드는 브라우저가 요청을 하든 자바스크립트 기반 fetch 요청을 하든 상관없이 동일한 방식으로 작동합니다.
따라서 우리는 여전히 데이터 가져오기 코드를 소유하고 있지만 간단한 멘탈 모델을 유지할 수 있습니다.
PESPA의 브라우저 에뮬레이션 동작 중 중요한 부분은,
페이지의 데이터를 최신 상태로 유지하기 위해,
변형이 발생할 때 페이지의 데이터를 재검증하는 한다는 것입니다.
MPA를 사용하면 전체 페이지를 새로고침했습니다.
PESPA를 사용하면 이 재검증이 가져오기 요청과 함께 발생합니다.
PEMPA에도 한 가지 중요한 문제가 있었음을 기억하십시오. 바로 코드 중복입니다.
PESPA는 백엔드 UI 코드와 프론트엔드 UI 코드를 정확히 동일하게 만들어 이 문제를 해결합니다.
서버에서 클라이언트와 동일하게 렌더링 할 수 있고,
클라이언트에서 UI 업데이트와 상호작용을 가능하게 하는 라이브러리를 사용하면
코드 중복 문제가 없습니다.
클라이언트에 데이터 가져오기, 변형 및 렌더링을 위한 작은 상자가 있음을 알 수 있습니다.
이 비트는 progressive을 위한 것입니다.
예를 들어, 보류 상태, 낙관적 UI 등은 실제로 서버에 위치할 자리가 없으므로
클라이언트에서만 실행되는 일부 코드를 갖게 됩니다.
하지만 최신 UI 라이브러리를 사용하면 이것마저도 서버 쪽에 위치시킬 수 있습니다.
(주 : 뭔지는 원문에도 안써있고 아직 저도 안찾아봤습니다. 한번 봐야겠네요)
PESPA 아키텍처의 동작
문서 요청 :
PESPA를 사용한 문서 요청은 PEMPA와 사실상 동일합니다.
앱에 필요한 초기 HTML은 서버에서 직접 전송되며
JavaScript도 서버에서 직접 로드되어 사용자 상호 작용 경험을 향상시킵니다.
클라이언트 측 네비게이션 :
사용자가 링크를 클릭하면 기본 동작이 방지됩니다.
라우터는 새 경로에 필요한 데이터와 UI를 결정하고
다음 경로에 필요한 데이터에 대해 데이터 가져오기를 트리거하고
해당 경로에 대해 렌더링되는 UI를 렌더링합니다.
리디렉션 & 변경(mutation) 요청 :
두 차트가 동일하다는 사실을 눈치채셨나요?
PESPA를 사용한 변형은 양식 제출(submit)을 통해 수행됩니다. onClick + fetch 넌센스는 더 이상 필요하지 않습니다.
(그러나 사용자의 세션 시간이 초과될 때 로그인 화면으로 리디렉션하는 것과 같은 PE(progressive enhancement)에는
명령형 돌연변이가 좋습니다).
사용자가 양식을 제출하면 브라우저 기본 동작을 방지합니다.
변형 코드는 양식을 직렬화하고 양식의 액션과 연결된 경로(기본값은 현재 URL)에 양식 데이터를 요청으로 보냅니다.
백엔드의 라우팅 로직은 업데이트를 수행하기 위해 영속성 코드와 통신하는 액션 코드를 호출하고,
액션 코드는 성공적인 응답(예: 트윗 좋아요) 또는 리디렉션(예: 새 GitHub 리포지토리 생성)을 리턴합니다.
리디렉션인 경우 라우터는 해당 경로에 대한 코드/데이터/asset을 병렬로 로드한 다음 렌더링 로직을 트리거합니다.
리디렉션이 아닌 경우 라우터는 현재 UI에 대한 데이터의 유효성을 다시 검사하고 렌더링 논리를 트리거하여 UI를 업데이트합니다.
흥미롭게도 인라인 변형 / 리디렉션 상관없이
라우터가 관여하는 두 가지 타입의 돌연변이에 대해 동일한 멘탈 모델을 제공합니다.
PESPA의 장단점
PESPA는 이전 아키텍처의 수많은 문제를 제거합니다.
하나씩 살펴보겠습니다.
MPA의 문제점
1. 전체 페이지 새로 고침
PESPA는 기본 동작을 방지하고
클라이언트 측 JS를 사용하는 대신 서버에서 브라우저를 에뮬레이트합니다.
우리가 작성하는 코드의 관점에서 이것은 MPA와 별반 다르지 않지만
사용자의 관점에서는 훨씬 개선된 경험입니다.
2. UI 피드백 컨트롤
PESPA를 사용하면 기본값을 방지하고 가져오기 요청을 하기 때문에
네트워크 요청을 완전히 제어할 수 있으므로
각 UI에 가장 적합한 방식으로 사용자에게 피드백을 제공할 수 있습니다.
PEMPA의 문제점
1. 기본값 방지
PESPA는 기본 동작을 방지하고
클라이언트 측 JS를 사용하는 대신 서버에서 브라우저를 에뮬레이트합니다.
우리가 작성하는 코드의 관점에서 이것은 MPA와 별반 다르지 않지만
사용자의 관점에서는 훨씬 개선된 경험입니다.
2. 관리할 코드
클라이언트와 서버 간에 코드를 공유하고
브라우저 동작을 에뮬레이트하는 올바른 추상화를 사용하면
결국 직접 작성해야 하는 코드의 양이 크게 줄어듭니다.
3. 코드 중복
PESPA의 아이디어 중 일부는 서버와 클라이언트가 렌더링을 위해 정확히 동일한 코드를 사용한다는 것입니다.
클라이언트가 업데이트한 UI가 페이지를 새로 고칠 때 얻는 것과 동일한지 확인하는것을 잊지 마세요.
PESPA를 사용하면 항상 우리 개발자의 노력이나 고려 없이 해당 검사를 통과해야 합니다.
4. 코드 정돈
PESPA의 브라우저 에뮬레이션이 제공하는 멘탈 모델 덕택에 애플리케이션 상태 관리는 고려 대상이 아닙니다.
그리고 렌더링 로직은 네트워크의 양쪽에서 동일하게 처리되므로 우연한 DOM 변형도 없습니다.
5. 서버 / 클라이언트 간접 참조
브라우저를 에뮬레이트하는 PESPA는
프론트엔드용 코드와 백엔드용 코드가 함께 배치되어
간접 참조를 제거하고 훨씬 더 생산적임을 의미합니다.
SPA의 문제점
1. 번들 크기
PESPA로 이동하려면 서버가 필요합니다.
우리는 엄청난 양의 코드를 백엔드로 이동할 수 있습니다.
UI에 필요한 모든 것은 서버와 클라이언트 모두에서 실행할 수 있는 작은 UI 라이브러리,
UI 상호 작용 및 피드백을 처리하기 위한 일부 코드,
컴포넌트용 코드입니다.
그리고 URL(경로 기반) 코드 분할 덕택에 마침내 수백 KB의 JS가 있는 웹 페이지와 작별할 수 있습니다.
게다가 PE로 인해 대부분의 앱은 JS 로드가 완료되기 전에 동작해야 합니다.
여기에 더해 현재 JS 프레임워크 별로 클라이언트에 필요한 JS의 양을 더 줄이려는 노력이 있습니다.
2. 워터폴
PESPA의 중요한 부분은 코드를 실행하지 않고도
주어진 URL에 대한 코드, 데이터 및 에셋 요구 사항을 인식할 수 있다는 것입니다.
즉, 코드 분할 외에도 PESPA는 코드, 데이터 및 에셋에 대한 가져오기를 연속적으로 한 번에 하나씩 기다리는 대신
한 번에 모두 트리거할 수 있습니다.
이것은 또한 PESPA가 사용자가 탐색을 시작하기 전에 이러한 항목을 미리 가져올 수 있음을 의미하므로
필요할 때 브라우저가 돌아서 즉시 반환하여 앱 사용의 전체 경험을 즉각적으로 느낄 수 있습니다.
3. 런타임 성능
PESPA는 성능을 위해 두 가지 작업을 수행합니다.
1) 많은 코드를 서버로 이동하므로 처음에 장치에서 실행할 코드가 적습니다.
2) Progressive Enhancement 덕분에 UI를 JS 로드 및 실행 완료되기 전에 사용할 수 있습니다.
4. 상태 관리
MPA 멘탈 모델을 제공하는 브라우저 에뮬레이션 덕택에
애플리케이션 상태 관리는 PESPA 컨텍스트에서 문제가 되지 않습니다.
이에 대한 증거는 앱의 대부분의 기능이 JavaScript 없이도 작동해야 한다는 사실입니다.
PESPA는 돌연변이가 완료되면 페이지의 데이터를 자동으로 재검증합니다.
(MPA는 전체 페이지 새로고침 덕분에 무료로 얻을 수 있음).
PESPA는 클라이언트 측 JavaScript가 있을때와 없을 때
정확히 동일하게는 동작하지 않는다는 점을 강조하는 것이 중요합니다.
어쨌든 이것이 점진적 향상의 목표는 결코 아닙니다.
대부분의 앱은 JavaScript 없이 동작해야 합니다.
우리가 sans-JavaScript 사용자 경험에 관심을 갖기 때문만은 아닙니다.
점진적 향상을 목표로 하여 UI 코드를 대폭 단순화하기 때문입니다.
우리가 JS 없이 얼마나 멀리 갈 수 있는지 놀라실 것입니다.
그러나 일부 애플리케이션의 경우 클라이언트 측 JavaScript 없이 모든 것이 작동하도록 하는 것이 필요하거나 실용적이지 않습니다.
그러나 일부 UI 요소가 동작하는 데 일부 JavaScript가 필요하더라도,
PESPA의 주요 이점을 얻을 수 있습니다.
PESPA를 정의하는 것
- 기능적 기준
- JS는 기능을 활성화 하는데 사용되는게 아니라, 기능과 사용자 경험을 향상시키는 데 사용됩니다.
- 지연 로딩 + 지능적인 프리페치(JS 코드 이상을 가져오는)
- 코드를 서버로 이동
- UI 코드의 서버 / 클라이언트 중복 없음(PEMPA에서와 같이)
- 투명한(transparent) 브라우저 에뮬레이션(#useThePlatform)
단점은 아직 발견 중입니다만,
여기 몇 가지 생각과 초기 반응이 있습니다.
SPA와 SSG에 익숙한 많은 사람들은 이제 앱을 실행하는 서버 측 코드가 있다는 사실에 불만일 것입니다.
하지만 사실 어떤 앱도 서버 측 코드를 피할 수 없습니다.
전체 사이트를 한 번 빌드하고 CDN에 고정할 수 있는 사용 사례가 분명히 있지만
비지니스를 위해 빌드되는 대부분의 앱은 이 범주에 맞지 않습니다.
사람들은 서버 비용에 대해 우려하고 있습니다.
SSG를 사용하여 앱을 한 번 빌드한 다음 CDN을 통해 매우 저렴한 비용으로
거의 무한한 수의 사용자에게 제공할 수 있다는 것입니다.
이 비판에는 두 가지 문제가 있습니다.
1) 아마도 우리 앱에서 API를 사용하고 있을 것이므로 이러한 사용자는 어쨌든 방문 시 가장 비싼 서버 측 코드를 많이 트리거할 것입니다.
2) CDN은 HTTP 캐싱 메커니즘을 지원하므로, 우리 앱을 SSG처럼 사용할 수 있다면,
캐싱 매커니즘을 사용하여 빠른 응답을 제공하고 렌더링 서버가 처리하는 작업량을 제한할 수 있습니다.
사람들이 SPA를 떠날 때 겪는 또 다른 일반적인 문제는
이제 서버에서 렌더링하는 문제를 해결해야 한다는 것입니다.
이것은 클라이언트에서만 코드를 실행하는 데 익숙한 사람들에게는 확실히 다른 모델이지만,
서버 사이드 렌더링을 고려한 도구를 사용하는 경우에는 어렵지 않습니다.
(그렇지 않은 경우 확실히 어려울 수 있지만)
마이그레이션하는 동안 특정 코드가 클라이언트 측에서만 실행되도록 하는 합리적인 해결 방법이 있습니다.
내가 말했듯이, 우리는 PESPA 앱의 단점을 여전히 발견하고 있지만
지금까지 우리가 인식할 수 있는 이점은 절충할 가치가 있다고 생각합니다.
사실 우리는 기존 도구(ex, nextjs)로 꽤 오랫동안 PESPA 아키텍처의 기능을 가지고 있었지만,
서버와 렌더링 로직 코드를 공유하면서 점진적 향상에 초점을 맞춘 것은 새로운 것입니다.
이 게시물은 플랫폼의 기능뿐만 아니라 사실상의 표준 아키텍처를 시연하는 데 주로 관심이 있습니다.
PESPA 구현체: 리믹스
PESPA에 대한 책임을 주도하는 것은 웹 기초와 현대적인 사용자 경험에 중점을 둔 웹 프레임워크인 Remix입니다.
Remix는 PESPA 제품에 대해 설명한 모든 것을 즉시 제공하는 최초의 웹 프레임워크입니다.
다른 프레임워크는 PESPA에 대한 Remix의 리드를 따를 수 있고 리믹스의 철학을 적용할 수 있습니다.
나는 더 많은 것이 뒤따를 것이라고 상상합니다.
(다시 말하지만, 메타 프레임워크(프레임워크를 위한 프레임워크)는 꽤 오랫동안 PESPA 아키텍처를 사용할 수 있었지만
Remix는 이 아키텍처를 최전선에 두고
다른 제품들은 따라오고 있습니다.)
주 : SolidJs 개발자가 Remix가 바퀴를 만들고 있음을 언급했던걸로 기억합니다.
그 뒤를 뒤따르는 프레임워크들은 각자 방식대류 바퀴를 재발명하고 있는거죠.
PESPA를 위한 웹 프레임워크가 있을 때 상황은 다음과 같습니다.
이 경우 Remix는 네트워크의 브리지 역할을 합니다.
Remix가 없으면 완전한 PESPA를 갖기 위해 이것을 직접 구현해야 합니다.
Remix는 또한 규칙 기반 및 구성 기반 라우팅의 조합을 통해 우리 앱의 라우팅을 처리합니다.
또한 Remix는 데이터 가져오기 및 변형(예: twitter like 버튼)과
보류 상태 및 낙관적 UI를 표시하기 위한 UI 피드백 기능(컴포넌트) 등을 제공하는 프레임워크입니다.
Remix에 내장된 중첩 라우팅 덕분에 코드 구성도 개선되었습니다(Next.js에서도 추구하는 것).
특히 PESPA 아키텍처에는 중첩 라우팅이 필요하지 않지만
경로 기반 코드 분할은 중요한 부분입니다.
또한 중첩 라우팅을 통해 훨씬 더 세분화된 코드 분할을 얻는 것도 꽤 중요한 측면입니다.
Remix는 PESPA 아키텍처로 더 나은 경험을 더 빠르게 구축할 수 있음을 보여줍니다.
그리고 우리는 이런 성과를 얻습니다.
한번에 완벽한 라이트하우스 점수를 얻을 수 있다고요?
결론
개인적으로 저는 이 전환에 대해 매우 낙관적입니다.
더 나은 UX와 DX를 동시에 얻는 것은 확실한 승리입니다.
나는 그것이 중요한 것이라고 생각하고 미래가 우리에게 어떤 영향을 미칠지 기대됩니다.
이 블로그 게시물을 다 읽어준 것에 대한 보상으로 TodoMVC 앱을 사용하여
시대에 따라 움직이는 이 모든 코드를 보여주는 레포지토리를 공유하겠습니다!
일부 아이디어를 보다 구체적으로 만드는 데 도움이 되길 바랍니다.
참고
The new wave of Javascript web frameworks
Progressively enhance for a more resilient web