# JavaScript 뷰를 어려운 방식으로 구축하기 - UI 작성 패턴

> Clean Markdown view of GeekNews topic #20482. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=20482](https://news.hada.io/topic?id=20482)
- GeekNews Markdown: [https://news.hada.io/topic/20482.md](https://news.hada.io/topic/20482.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2025-04-23T09:18:59+09:00
- Updated: 2025-04-23T09:18:59+09:00
- Original source: [github.com/matthewp](https://github.com/matthewp/views-the-hard-way)
- Points: 10
- Comments: 3

## Summary

프레임워크 없이 **순수 자바스크립트**로 뷰를 구축하는 방법을 설명하며, **명령형 접근 방식**을 통해 성능과 유지보수성을 확보합니다. 상태 업데이트와 DOM 업데이트를 명확히 구분하고, **엄격한 명명 규칙과 구조적 패턴**을 따릅니다. 이 방식은 **디버깅이 쉬우며**, 모든 브라우저와의 호환성을 보장하고, **0 dependencies**라는 장점이 있습니다. 초보자에게는 어려울 수 있지만, 학습을 통해 **실제 시스템 작동 방식에 대한 깊은 이해**를 제공합니다.

## Topic Body

- **Writing JavaScript Views the Hard Way** : 프레임워크 없이 순수 자바스크립트로 뷰를 구축하는 방법을 설명하는 글  
- **직접적인 명령형 접근 방식**을 통해 성능, 유지보수성, 이식성을 확보함  
- 상태 업데이트와 DOM 업데이트를 **명확히 구분**하고, 각 역할에 따라 **엄격한 명명 규칙과 구조적 패턴**을 따름  
- 이 방식은 **디버깅이 쉬우며**, **모든 브라우저 호환성**을 보장하고, **0 dependencies**라는 큰 장점이 있음  
- 초보자에게는 어려울 수 있지만, 학습 시 **실제 시스템 작동 방식에 대한 깊은 이해**를 제공함  
  
---  
  
### 자바스크립트 뷰를 'Hard Way'로 작성하기  
  
#### 이것이 무엇인가?  
  
- 이 방식은 **React, Vue, lit-html**과 같은 프레임워크 없이 **자바스크립트만으로 뷰를 구성하는 패턴**임  
- 특정 라이브러리나 도구가 아닌 **코딩 패턴 자체**로, 스파게티 코드 문제를 방지함  
- **직접적인 명령형 방식**을 사용함으로써, 추상화를 줄이고 직관성을 높임  
  
#### 프레임워크 대비 장점  
  
- **성능**: 명령형 코드로 인해 불필요한 연산 없이 동작하며, 핫패스와 콜드패스 모두에 적합함  
- **0 dependencies**: 라이브러리 업그레이드나 호환성 문제로부터 자유로움  
- **이식성**: 작성한 코드가 어느 프레임워크에도 이식 가능함  
- **유지보수성**: 명확한 섹션 구조와 명명 규칙으로 코드 위치 파악이 쉬움  
- **브라우저 지원**: IE9 이상 대부분 브라우저와 호환되며, IE6까지도 일부 수정으로 지원 가능함  
- **디버깅 용이성**: 중간 레이어 없이 **얕은 스택 트레이스**를 제공  
- **함수형 구조**: 불변성은 아니지만, 모든 구성 요소가 **함수 기반**으로 구성됨  
  
### 구조 설명  
  
#### 전체 구조  
  
- `template` → `clone()` → `init()` 함수로 구성됨  
- `init()` 함수는 **상태 변수, DOM 참조, 업데이트 함수, 이벤트 리스너 등**을 포함한 하나의 뷰 인스턴스를 생성  
  
##### 예시 코드 구조 (Hello World)  
  
```js  
const template = document.createElement('template');  
template.innerHTML = `&lt;div&gt;Hello &lt;span id="name"&gt;world&lt;/span&gt;!&lt;/div&gt;`;  
  
function clone() {  
  return document.importNode(template.content, true);  
}  
  
function init() {  
  let frag = clone();  
  let nameNode = frag.querySelector('#name');  
  let name;  
  
  function setNameNode(value) {  
    nameNode.textContent = value;  
  }  
  
  function setName(value) {  
    if(name !== value) {  
      name = value;  
      setNameNode(value);  
    }  
  }  
  
  function update(data = {}) {  
    if(data.name) setName(data.name);  
    return frag;  
  }  
  
  return update;  
}  
```  
  
### `init()` 함수 내부 구성  
  
#### 1. DOM 변수  
  
- `frag`는 `clone()`으로부터 생성된 템플릿 조각  
- 내부 요소는 `querySelector()`로 참조하고, 변수명은 `fooNode` 형태 사용  
  
#### 2. DOM 뷰  
  
- 다른 뷰를 포함하는 부분 (재사용 가능한 서브 뷰)  
- 예시:  
  
```js  
let updateChildView = childView();  
```  
  
- 뷰 업데이트 함수는 `updateFoo` 형태로 명명  
  
#### 3. 상태 변수  
  
- 뷰 내에서 변경될 수 있는 데이터 값  
- DOM 업데이트를 효율적으로 하기 위해 현재 값과 비교하여 필요할 때만 DOM 변경  
  
#### 4. DOM 업데이트 함수  
  
- DOM 요소의 상태를 변경할 때 사용  
- 예시:  
  
```js  
function setNameNode(value) {  
  nameNode.textContent = value;  
}  
```  
  
- DOM 조작은 반드시 이 함수 안에서만 수행  
  
#### 5. 상태 업데이트 함수  
  
- 상태 변경 로직과 그에 따른 DOM 반영 포함  
- 변경되지 않은 값은 무시하여 **불필요한 DOM 변경 방지**  
- 예시:  
  
```js  
function setName(value) {  
  if(name !== value) {  
    name = value;  
    setNameNode(value);  
  }  
}  
```  
  
### `template`과 `clone()` 함수  
  
#### template  
  
- `&lt;template&gt;` 요소로 정적 HTML 구조 생성  
- DOM에 직접 삽입되지 않으며, clone을 통해 복사본 생성  
  
#### clone()  
  
- `document.importNode(template.content, true)`로 복제  
- 필요시 `.firstElementChild`를 사용하여 루트 요소 반환 가능  
  
### 상호작용 방식  
  
#### 부모 → 자식 데이터 흐름  
  
- 부모는 자식의 `init()`을 호출하여 **업데이트 함수**를 획득하고, `update({ name: 'foo' })` 형식으로 호출  
  
#### 이벤트 기반 데이터 전파  
  
- 기본적으로 **props down, events up** 모델 따름  
- 하위 뷰는 이벤트를 상위로 디스패치하여 통신  
  
### React와 비교  
  
- **`constructor()` (React)** → **`init()` (Hard Way)**  
  - 컴포넌트 초기 설정을 담당함  
- **`render()` (React)** → **`update(data)` (Hard Way)**  
  - 화면 갱신 및 UI 업데이트 역할 수행  
- **`this.setState()` (React)** → **`setX(value)` (Hard Way)**  
  - 상태값을 직접 설정하는 방식으로 대체됨  
- **`props` (React)** → **`update(data)`로 전달된 값 (Hard Way)**  
  - 부모 컴포넌트로부터 전달된 데이터 처리 방식  
- **JSX / Virtual DOM (React)** → **HTML 템플릿 + DOM API (Hard Way)**  
  - 선언형 UI 대신 수동적인 DOM 조작 및 템플릿 사용  
  
### 결론  
  
- 이 방식은 익숙한 프레임워크에 비해 **초기 진입 장벽은 높지만**, 다음과 같은 강점을 가짐:  
  - **성능 최적화**  
  - **완전한 제어권**  
  - **학습을 통한 깊은 이해**  
- 각 역할별 함수 분리 및 명명 규칙을 통해, 프레임워크 없이도 **유지보수 가능한 UI 구성 가능**  
  
### 호환성  
  
- 최신 예시는 **현대 브라우저용 API**를 사용하지만, IE9 이하까지도 **함수 기반 대체**를 통해 지원 가능  
- 이벤트 대신 **props로 함수 전달**을 사용하는 방식으로 IE6까지도 확장 가능

## Comments



### Comment 37768

- Author: wfedev
- Created: 2025-04-24T18:58:02+09:00
- Points: 1

결국은 웹 컴포넌트로..

### Comment 37671

- Author: ahwjdekf
- Created: 2025-04-23T19:07:24+09:00
- Points: 2

축하합니다.  또다른 js프레임워크가 탄생되었습니다

### Comment 37555

- Author: neo
- Created: 2025-04-23T09:19:03+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=43733636) 
* 많은 JS 개발자들에게는 이단일 수 있지만, 'state' 변수는 안티 패턴이라고 생각함
  - 웹 컴포넌트를 사용하며 '평면' 변수 타입에 대해 state 변수를 추가하는 대신 DOM 요소의 value/textContent/checked 등을 유일한 진실의 원천으로 사용함
  - 필요한 경우 setter와 getter를 추가함
  - 코드의 양이 적어도 자연스럽게 많은 것이 올바르게 작동함
  - WebComponents를 사용하면 객체와 인접한 HTML 템플릿의 분리가 이루어져서 스파게티 코드가 아닌 퓨실리나 마카로니 같은 세분화가 이루어짐

* 이 접근 방식이 매우 유지보수 가능하다고 설명서에 나와 있지만, 동의하지 않음
  - 디자인 패턴이 오직 관례에 기반하고 있음
  - 복잡한 앱에서 여러 개발자가 동시에 작업할 때, 적어도 한 명은 관례에서 벗어날 가능성이 높음
  - iOS의 UIKit 같은 클래스 기반 UI 프레임워크는 모든 개발자가 표준 API 세트를 사용하도록 강제하여 코드가 예측 가능하고 유지보수가 쉬움

* 최근에 vite와 함께 순수 '바닐라' TypeScript로 애플리케이션을 작성하고 있으며, 프론트엔드 '최고' 관행에 대해 점점 더 의문을 가짐
  - 확장성은 결론지을 수 없지만, 성능 면에서 큰 이점이 있음
  - 재미있고, 많은 것을 배우며, 디버깅이 간단하고, 아키텍처를 이해하기 쉬움
  - 템플레이팅이 가장 그리움

* 이 접근 방식은 오래된 backbone js 라이브러리를 떠올리게 함
  - 웹 플랫폼에 적응된 MVC 패턴의 예시가 있는 GitHub 저장소도 있음

* 최근에 비슷한 것을 생각해냈지만, 템플릿 요소를 사용하지 않음
  - 함수와 템플릿 리터럴을 사용하여 문자열을 반환하고, 기존 요소의 innerHTML에 넣거나 새로운 div 요소를 생성하여 넣음
  - 함수가 중첩되어 합리적인 방식으로 구성하기 어려움

* 이 코드는 반응형 뷰 라이브러리가 대체하려는 수동 업데이트 코드와 정확히 같아 보임

* 약 20년 동안 프로그래밍을 해왔지만 프론트엔드 프레임워크에 익숙해지지 않음
  - 백엔드에 더 강해서 보안 관련 상호작용은 서버를 통해야 한다고 생각함
  - JS는 견고한 HTML 및 CSS 기반에 클라이언트 측 기능을 추가하는 것으로 봄

* React.createElement와 유사한 헬퍼를 사용함
  - 모의 서버 대시보드의 작동 예시가 있음

* HTML 기반 도구를 위한 JS 툴킷을 구축하려는 시도로 deja-vu.junglecoder.com에서 작업 중임
  - 적절한 반응형/양방향 데이터 바인딩은 아직 해결하지 못했지만, grab/patch가 꽤 괜찮음
  - 템플릿을 사용하는 방식이 템플릿의 일부를 이동하기 매우 쉬움

* 대학 졸업 후 첫 공식 직장에서 Delphi 소프트웨어의 웹 버전을 만드는 작업을 했음
  - 팀은 이미 프론트엔드를 세 번째로 다시 작성 중이었고, 프레임워크를 변경해야 했음
  - 자체 프레임워크를 작성해야 한다고 주장했지만, 팀은 내 제안을 좋아하지 않았음
  - 이후 다른 회사에서 더 나은 제안을 받아 떠남
  - 이후 tiny.js라는 또 다른 프레임워크를 시도했으며, 개인 프로젝트에 사용 중임
