이전 글 보기: https://moneytech.kr/53
0. 들어가는 글
애플리케이션과 소프트웨어가 사용자의 선택을 받는 것 중 가장 큰 요소는 "편리함"이다.
앞서 작성한 vdom의 코드를 보면 객체 내에 tag, props, children 이렇게 3개의 속성이 있다.
간단해보이지만 내용이 더 많아지면 훨씬 더 복잡해질 것이다.
샘플 앱 수준도 이런데, 애플리케이션의 UI 구조를 이런 식으로 만들어서 소프트웨어로 제공한다면
그 누구도 사용하지 않을 것이다.
지금 우리가 만들고 있는 것은 react 프레임워크를 흉내내서 만들어보는 것으로,
이전의 app.js에 우리(개발자)가 작성하는 코드가 있다고 가정하는 것이다.
1. 이전 글에서 계속해서 개선하자.
vdom 객체의 구조를 조금 더 편하게 개선해보도록 하자.
vdom 객체가 3개의 속성을 갖고 있는 객체의 단순 반복이라는 것을 눈여겨보자.
3개의 속성(tag, props, children)을 갖고 있는 객체를 계속 생성하는 함수를 하나 만들면
그 함수를 반복 호출하면서 이 구조를 만들어 낼 수 있을 것이다.
이 기능을 react.js가 제공해주어야한다.
기존 작성한 react.js는 아래와 같다.
export function createDOM(node) { // export를 적어서 app.js에서 사용할 수 있도록 함
if (typeof node === 'string') {
return document.createTextNode(node);
}
const element = document.createElement(node.tag);
Object.entries(node.props) // 기존에는 props에 대한 처리가 안됐다. props에 대한 처리를 진행하자. Object.entries() 혹은 Object.keys()를 사용하자.
.forEach(([name, value]) => element.setAttribute(name, value));
node.children
.map(createDOM)
.forEach(element.appendChild.bind(element));
return element;
}
export function render(vdom, container) { // 새로 추가하고, 입력값을 container로 설정
container.appendChild(createDOM(vdom));
}
여기서 중간에 createElement라는 함수를 만든다.
(생략)
export function createElement(tag, props, children) {
return { // 3개의 속성을 가진 객체를 리턴할 것이다.
tag, props, children }; // 변수가 입력된 인수와 같으니까 이름만 작성해도 동작
}
export function render(vdom, container) { // 새로 추가하고, 입력값을 container로 설정
container.appendChild(createDOM(vdom));
}
우리가 만든 react 파일이 수정되었으니 app.js에 있던 코드도 수정해야한다.
(react가 업데이트 되었다고 생각하자.)
이제 다시 app.js에 돌아가서 기존에 객체를 만들던 것을 다른 방식으로 바꾸어보자.
import { createDOM, createElement,render } from './react'; // 작성한 함수를 불러왔다. createElement()
const vdom = {
tag: 'p',
props: {},
children: [
{
tag: 'h1',
props: {},
children: ["React 만들기"],
},
{
tag: 'ul',
props: {},
children: [
{
tag: 'li',
props: {
style: "color:red",
},
children: ["첫 번째 아이템"]
},
{
tag: 'li',
props: {
style: "color:blue",
},
children: ["두 번째 아이템"]
},
{
tag: 'li',
props: {
style: "color:green",
},
children: ["세 번째 아이템"]
},
]
}
],
};
const vdom2 = createElement('p', {}, createElement('h1', {}, "React 만들기") // 여기에 새로 만들어보자.
// 세 번째 인수는 그 자체가 리턴값이므로, 재귀적 방법을 위해서 함수를 다시 작성해서 호출한다.
// 여기서 세 번째 인수의 세 번째 인수("React 만들기")는 배열이 되어야하는데 배열이 아니다.
// 배열로 만들기 위해서 다시 react.js로 넘어가서 마지막 인수를 가변인자(...children)로 바꿔준다.
render(vdom, document.querySelector('#root'));
여기서 vdom2의 주석을 보면, createElement의 마지막 입력값을 배열로 받기 위해서
react.js에 가서 가변인자로 바꿔야한다.
export function createElement(tag, props, ...children) {
return { // 3개의 속성을 가진 객체를 리턴할 것이다.
tag, props, children }; // 변수가 입력된 인수와 같으니까 이름만 작성해도 동작
}
가변인자란?
전개구문 혹은 spread operation이라고 불리는 문법으로 변수 앞에 ...을 붙이면
입력 받을 때 해당 형태를 모두 펼쳐서 삽입해준다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
그래서 최종적으로 vdom은 이런 식으로 작성해본다.
// app.js 최종형태
import { createDOM, createElement, render } from './react';
const vdom = createElement('p', {},
createElement('h1', {}, "React 만들기"),
createElement('ul', {},
createElement('li', { style: "color:red" }, "첫 번째 아이템"),
createElement('li', { style: "color:blue" }, "두 번째 아이템"),
createElement('li', { style: "color:green" }, "세 번째 아이템"),
)
);
console.log(vdom);
render(vdom, document.querySelector('#root'));
이 전에 작성하던 app.js에서 사용자가 훨씬 더 편리한 방식으로 변경되었다.
완전히 편해진 것은 아니지만 코드량이 현격하게 줄었다.
끝!