웹 프레임워크가 해결하는 문제와 웹 프레임워크 없이 수행하는 방법(1부)

게시 됨: 2022-03-10
빠른 요약 ↬ 이 기사에서 Noam Rosenthal은 프레임워크 전반에 걸쳐 공통적인 몇 가지 기술 기능에 대해 자세히 설명하고 여러 프레임워크에서 이를 구현하는 방법과 비용을 설명합니다.

저는 최근에 프레임워크를 바닐라 JavaScript와 비교하는 데 매우 관심을 갖게 되었습니다. 일부 프리랜스 프로젝트에서 React를 사용하면서 좌절감을 느낀 후 시작되었으며, 최근에 사양 편집기로 웹 표준에 대해 좀 더 친밀하게 알게 되었습니다.

나는 프레임워크 간의 공통점과 차이점이 무엇인지, 웹 플랫폼이 보다 간결한 대안으로 무엇을 제공해야 하는지, 충분한지 여부를 확인하는 데 관심이 있었습니다. 내 목표는 프레임워크를 공격하는 것이 아니라 비용과 이점을 이해하고 대안이 있는지 확인하고 프레임워크를 사용하기로 결정하더라도 이를 통해 배울 수 있는지 확인하는 것입니다.

이 첫 번째 부분에서는 프레임워크 전반에 걸쳐 공통적인 몇 가지 기술 기능과 일부 프레임워크에서 이를 구현하는 방법에 대해 자세히 설명합니다. 또한 이러한 프레임워크를 사용하는 비용도 살펴보겠습니다.

프레임워크

저는 4가지 프레임워크를 선택했습니다. 오늘날 지배적인 React, 그리고 React와 다른 작업을 수행한다고 주장하는 3명의 새로운 경쟁자입니다.

  • 반응
    “React를 사용하면 대화형 UI를 쉽게 만들 수 있습니다. 선언적 보기는 코드를 보다 예측 가능하고 디버그하기 쉽게 만듭니다."
  • 솔리드JS
    "Solid는 React와 동일한 철학을 따릅니다. 그러나 가상 DOM을 사용하지 않는 완전히 다른 구현이 있습니다."
  • 날씬한
    “Svelte는 사용자 인터페이스를 구축하기 위한 근본적이고 새로운 접근 방식입니다. 앱을 구축할 때 발생하는 컴파일 단계입니다. 가상 DOM 비교와 같은 기술을 사용하는 대신 Svelte는 앱 상태가 변경될 때 DOM을 외과적으로 업데이트하는 코드를 작성합니다.”
  • 문학
    "웹 구성 요소 표준 위에 구축된 Lit은 반응성, 선언적 템플릿 및 몇 가지 사려 깊은 기능을 추가합니다."

프레임워크가 차별화 요소에 대해 말하는 내용을 요약하면 다음과 같습니다.

  • React는 선언적 보기를 사용하여 UI를 더 쉽게 구축할 수 있도록 합니다.
  • SolidJS는 React의 철학을 따르지만 다른 기술을 사용합니다.
  • Svelte는 UI에 컴파일 타임 접근 방식을 사용합니다.
  • Lit은 기존 표준을 사용하고 일부 경량 기능이 추가되었습니다.

프레임워크가 해결하는 것

프레임워크 자체는 선언적, 반응성 및 가상 DOM이라는 단어를 언급합니다. 그 의미에 대해 알아보겠습니다.

선언적 프로그래밍

선언적 프로그래밍은 제어 흐름을 지정하지 않고 논리를 정의하는 패러다임입니다. 우리는 어떤 단계가 우리를 거기에 데려갈 것인지보다 결과가 무엇인지 설명합니다.

2010년경 선언적 프레임워크의 초기에는 DOM API가 훨씬 더 단순하고 장황했으며 명령형 JavaScript로 웹 애플리케이션을 작성하려면 많은 상용구 코드가 필요했습니다. 당시 획기적인 Knockout 및 AngularJS 프레임워크와 함께 "모델-뷰-뷰모델"(MVVM) 개념이 널리 보급되어 라이브러리 내부의 복잡성을 처리하는 JavaScript 선언적 계층을 제공했습니다.

MVVM은 오늘날 널리 사용되는 용어가 아니며 "데이터 바인딩"이라는 이전 용어의 변형입니다.

데이터 바인딩

데이터 바인딩은 모델과 사용자 인터페이스 간에 데이터가 동기화되는 방식을 표현하는 선언적 방법입니다.

인기 있는 모든 UI 프레임워크는 데이터 바인딩 형식을 제공하며 해당 자습서는 데이터 바인딩 예제로 시작합니다.

다음은 JSX(SolidJS 및 React)의 데이터 바인딩입니다.

 function HelloWorld() { const name = "Solid or React"; return ( <div>Hello {name}!</div> ) }

Lit의 데이터 바인딩:

 class HelloWorld extends LitElement { @property() name = 'lit'; render() { return html`<p>Hello ${this.name}!</p>`; } }

Svelte의 데이터 바인딩:

 <script> let name = 'world'; </script> <h1>Hello {name}!</h1>

반동

반응성은 변화의 전파를 표현하는 선언적 방법입니다.

데이터 바인딩을 선언적으로 표현하는 방법이 있을 때 프레임워크가 변경 사항을 전파하는 효율적인 방법이 필요합니다.

React 엔진은 렌더링 결과를 이전 결과와 비교하고 그 차이를 DOM 자체에 적용합니다. 변경 전파를 처리하는 이러한 방법을 가상 DOM이라고 합니다.

SolidJS에서 이것은 저장소와 내장 요소를 사용하여 보다 명시적으로 수행됩니다. 예를 들어 Show 요소는 가상 DOM 대신 내부적으로 변경된 사항을 추적합니다.

Svelte에서는 "반응성" 코드가 생성됩니다. Svelte는 어떤 이벤트가 변경을 유발할 수 있는지 알고 있으며 이벤트와 DOM 변경 사이에 선을 그리는 간단한 코드를 생성합니다.

Lit에서 반응성은 기본적으로 HTML 사용자 정의 요소의 기본 제공 반응성에 의존하는 요소 속성을 사용하여 수행됩니다.

점프 후 더! 아래에서 계속 읽기 ↓

논리

프레임워크가 반응성 구현과 함께 데이터 바인딩을 위한 선언적 인터페이스를 제공할 때 전통적으로 명령형으로 작성되는 일부 논리를 표현하는 방법도 제공해야 합니다. 논리의 기본 빌딩 블록은 "if"와 "for"이며 모든 주요 프레임워크는 이러한 빌딩 블록의 일부 표현을 제공합니다.

조건부

숫자 및 문자열과 같은 기본 데이터를 바인딩하는 것 외에도 모든 프레임워크는 "조건부" 프리미티브를 제공합니다. React에서는 다음과 같이 보입니다.

 const [hasError, setHasError] = useState(false); return hasError ? <label>Message</label> : null; … setHasError(true);

SolidJS는 기본 제공 조건부 구성 요소인 Show 를 제공합니다.

 <Show when={state.error}> <label>Message</label> </Show>

Svelte는 #if 지시문을 제공합니다.

 {#if state.error} <label>Message</label> {/if}

Lit에서는 render 함수에서 명시적 삼항 연산을 사용합니다.

 render() { return this.error ? html`<label>Message</label>`: null; }

기울기

다른 일반적인 프레임워크 기본 요소는 목록 처리입니다. 목록은 연락처, 알림 등의 목록과 같은 UI의 핵심 부분이며 효율적으로 작동하려면 하나의 데이터 항목이 변경될 때 전체 목록을 업데이트하지 않고 반응적이어야 합니다.

React에서 목록 처리는 다음과 같습니다.

 contacts.map((contact, index) => <li key={index}> {contact.name} </li>)

React는 특수 key 속성을 사용하여 목록 항목을 구별하고 전체 목록이 모든 렌더링으로 대체되지 않도록 합니다.

SolidJS에서는 forindex 내장 요소가 사용됩니다.

 <For each={state.contacts}> {contact => <DIV>{contact.name}</DIV> } </For>

내부적으로 SolidJS는 항목이 변경될 때 업데이트할 요소를 결정하기 forindex 와 함께 자체 저장소를 사용합니다. React보다 더 명시적이어서 가상 DOM의 복잡성을 피할 수 있습니다.

Svelte는 업데이트 프로그램을 기반으로 변환되는 each 지시문을 사용합니다.

 {#each contacts as contact} <div>{contact.name}</div> {/each}

Lit은 React의 key 기반 목록 매핑과 유사하게 작동하는 repeat 기능을 제공합니다.

 repeat(contacts, contact => contact.id, (contact, index) => html`<div>${contact.name}</div>`

구성 요소 모델

이 기사의 범위를 벗어나는 한 가지는 다양한 프레임워크의 구성 요소 모델과 사용자 정의 HTML 요소를 사용하여 이를 처리하는 방법입니다.

참고 : 이것은 큰 주제이며 너무 길어질 수 있으므로 다음 기사에서 다루기를 바랍니다. :)

비용

프레임워크는 선언적 데이터 바인딩, 제어 흐름 기본 요소(조건 및 목록), 변경 사항을 전파하는 반응 메커니즘을 제공합니다.

그들은 또한 구성 요소를 재사용하는 방법과 같은 다른 주요 사항을 제공하지만 이는 별도의 기사 주제입니다.

프레임워크가 유용한가요? 네. 이 모든 편리한 기능을 제공합니다. 그러나 그것이 올바른 질문입니까? 프레임워크를 사용하려면 비용이 듭니다. 그 비용이 얼마인지 봅시다.

번들 크기

번들 크기를 볼 때 Gzip이 아닌 축소된 크기를 보는 것이 좋습니다. JavaScript 실행의 CPU 비용과 가장 관련이 있는 크기입니다.

  • ReactDOM은 약 120KB입니다.
  • SolidJS는 약 18KB입니다.
  • 약 16KB입니다.
  • Svelte는 약 2KB이지만 생성되는 코드의 크기는 다양합니다.

오늘날의 프레임워크는 번들 크기를 작게 유지하는 데 React보다 더 나은 작업을 수행하는 것 같습니다. 가상 DOM에는 많은 JavaScript가 필요합니다.

빌드

어떻게 든 우리는 웹 앱을 "구축"하는 데 익숙해졌습니다. Node.js와 Webpack과 같은 번들러를 설정하지 않고 프론트 엔드 프로젝트를 시작하는 것은 불가능합니다. Babel-TypeScript 스타터 팩의 일부 최근 구성 변경 사항을 처리하고 그 모든 것을 처리합니다.

프레임워크의 표현이 풍부하고 번들 크기가 작을수록 빌드 도구의 부담과 변환 시간이 커집니다.

Svelte는 가상 DOM이 순수한 오버헤드라고 주장합니다. 동의하지만 아마도 "빌딩"(Svelte 및 SolidJS에서와 같이)과 맞춤형 클라이언트 측 템플릿 엔진(Lit에서와 같이)도 다른 종류의 순수한 오버헤드일 것입니다.

디버깅

구축과 번역에는 다른 종류의 비용이 따릅니다.

웹 앱을 사용하거나 디버그할 때 보는 코드는 우리가 작성한 것과 완전히 다릅니다. 우리는 이제 웹사이트에서 일어나는 일을 리버스 엔지니어링하고 자체 코드의 버그와 연결하기 위해 다양한 품질의 특수 디버깅 도구에 의존합니다.

React에서 호출 스택은 결코 "당신의 것"이 아닙니다. React는 당신을 위해 스케줄링을 처리합니다. 이것은 버그가 없을 때 잘 작동합니다. 그러나 무한 루프 재렌더링의 원인을 식별하려고 시도하면 고통의 세계에 빠지게 될 것입니다.

Svelte에서 라이브러리 자체의 번들 크기는 작지만 앱의 요구에 맞게 사용자 정의된 Svelte의 반응성 구현인 암호화된 생성 코드 전체를 배송하고 디버그하게 됩니다.

Lit을 사용하면 빌드에 관한 것이 아니라 효과적으로 디버그하려면 템플릿 엔진을 이해해야 합니다. 이것이 프레임워크에 대한 내 감정이 회의적인 가장 큰 이유일 수 있습니다.

사용자 정의 선언적 솔루션을 찾을 때 더 고통스러운 명령적 디버깅으로 끝납니다. 이 문서의 예제는 API 사양에 Typescript를 사용하지만 코드 자체에는 변환이 필요하지 않습니다.

업그레이드

이 문서에서는 네 가지 프레임워크를 살펴보았지만 제가 셀 수 있는 것보다 더 많은 프레임워크가 있습니다(AngularJS, Ember.js, Vue.js 등). 프레임워크, 개발자, 마음가짐, 생태계가 발전함에 따라 여러분을 위해 일할 것이라고 믿을 수 있습니까?

자신의 버그를 수정하는 것보다 더 실망스러운 것은 프레임워크 버그에 대한 해결 방법을 찾아야 한다는 것입니다. 그리고 프레임워크 버그보다 더 짜증나는 것은 코드를 수정하지 않고 프레임워크를 새 버전으로 업그레이드할 때 발생하는 버그입니다.

사실, 이 문제는 브라우저에도 존재하지만 발생하면 모든 사람에게 발생하며 대부분의 경우 수정 또는 게시된 해결 방법이 임박합니다. 또한 이 문서에 있는 대부분의 패턴은 성숙한 웹 플랫폼 API를 기반으로 합니다. 항상 최전선에 갈 필요는 없습니다.

요약

데이터 바인딩, 반응성, 조건부 및 목록에 중점을 두고 프레임워크가 해결하려고 하는 핵심 문제와 이러한 문제를 해결하는 방법에 대해 조금 더 깊이 파고 들었습니다. 비용도 살펴봤다.

2부에서는 프레임워크를 전혀 사용하지 않고 이러한 문제를 해결할 수 있는 방법과 이를 통해 배울 수 있는 내용을 살펴보겠습니다. 계속 지켜봐 주세요!

기술 리뷰를 제공해 주신 Yehonatan Daniv, Tom Bigelajzen, Benjamin Greenbaum, Nick Ribal 및 Louis Lazaris에게 특별히 감사드립니다.