브라우저와 Javascript가 발전하는 과정에서, 브라우저(클라이언트) 단에서 렌더링을 하고 서버에서는 브라우저 렌더링에 필요한 데이터만 제공하는 형태로 기술이 변화했다. 직접적으로 DOM을 다루는 행위가 급격히 감소하고, 상태(State)를 기준으로 DOM을 렌더링 하는 형태로 발전한 것이다. 이러한 과정에서 Client-Side Rendering, 상태 관리라는 개념이 생겼다.
간단한게 setState
를 통해 state
를 기반으로 render
해주는 코드를 만들어보자.
state
가 변경되면render
를 실행한다.state
는setState
로만 변경해야 한다.
위의 규칙을 지켜 코드를 작성하면 브라우저 출력되는 내용은 state
에 종속된다. 즉, DOM을 직접적으로 다룰 필요가 없다.
<div id="app"></div>
<script>
const $app = document.querySelector('#app');
// $변수는 보통 shadow dom host 역할을 하는 요소를 나타낼 때 사용합니다.
/* 초기상태 */
let state = {
items: ['item1', 'item2', 'item3', 'item4']
}
const render = () => {
const { items } = state; // 구조 분해 문법입니다.
$app.innerHTML = `
<ul>
${items.map(item => `<li>${item}</li>`).join('')}
</ul>
<button id="append">추가</button>
`;
/* 클릭 이벤트가 발생하면 setState( ) 실행 */
document.querySelector('#append').addEventListener('click', () => {
setState({ items: [ ...items, `item${items.length + 1}` ] })
})
}
/* setState ( ) 함수가 실행되면 자동으로 render( ) 함수 호출 */
const setState = (newState) => {
state = { ...state, ...newState };
render();
}
/* 초기값 렌더링 */
render();
</script>
✔️ state 값을 직접 변경해도 render( ) 함수는 새로 호출되지 않는다. setState( ) 함수를 호출하여 state 값을 변경하면 자동으로 render( ) 함수를 호출하므로 화면에 변경된 state 값을 새롭게 출력할 수 있다.
✔️ setState( ) 함수의 인자로 함수를 전달하면 이전 state 값을 읽는 과정을 생략할 수 있다.
앞서 작성한 코드를 class 문법으로 추상화시켜보자.
<div id="app"></div>
<script>
class Component {
$target;
$state;
constructor ($target) {
this.$target = $target;
this.setup();
this.render();
}
setup () {};
template () { return ''; }
render () {
this.$target.innerHTML = this.template();
this.setEvent();
}
setEvent () {}
setState (newState) {
this.$state = { ...this.$state, ...newState };
this.render();
}
}
class App extends Component {
setup () {
this.$state = { items: ['item1', 'item2'] };
}
template () {
const { items } = this.$state;
return `
<ul>
${items.map(item => `<li>${item}</li>`).join('')}
</ul>
<button>추가</button>
`
}
setEvent () {
this.$target.querySelector('button').addEventListener('click', () => {
const { items } = this.$state;
this.setState({ items: [ ...items, `item${items.length + 1}` ] });
});
}
}
new App(document.querySelector('#app'));
</script>