favicon

Jayden { do: smite }

230424(월)

🎄 성장일지 4.0

행복한 이기주의자(웨인 다이어)의 내용에 자극받아 시작하는 소박한 성장기록

살아있는 꽃과 죽은 꽃은 어떻게 구별하는가?<br/> 성장하고 있는 것이 살아 있는 것이다.<br/> 생명의 유일한 증거는 성장이다!

🌳 (1.0)키워드<br/> 최대한 간단하게 정리, 추후에 보면서 스스로 설명<br/> 🍉 (2.0)경험 위주로<br/> 단순 정보를 전달하기보다 무엇을 배웠고 어떻게 해결했는지 짧고 간단하게 작성<br/> ❄️ (3.0)정해진 템플릿에 맞춰서<br/> 키워드, 경험 모두 좋다. 다만 매일 작성하기로 마음 먹은만큼 핵심만 간결하게 정리할 수 있게 템플릿을 작성<br/> (3.1)230102부터 시작되는 학습에 관한 내용 추가<br/> (3.2)230313부터 좀더 경험, 감정 위주의 내용도 담기!<br/> 🌾 (4.0)학습 키워드에서 최대한 간단한 정보 제공, 고민에서 내 경험을 자세히 적자!<br/>

🧐 고민 사항

🔑 오늘의 학습 키워드

  • 리팩토링

🥳 학습 내용

리팩토링 성공?!

드디어... 몇일동안 혼자 끙끙거리던 리팩토링을 성공했다!!! 아주 간단한 Header만 기존과 비교해보자면 Header는 Model, Component, View 이 3가지로 구성되어있다.

Model은 큰 변화가 없기에 View와 Component만 살펴보려 한다.

리팩토링 전 코드

View

import { State } from '@custom-types/types'; import { AbstractView } from '@custom-types/abstracts.js'; export class HeaderView extends AbstractView { constructor() { super(); } protected setWrapper() { this._wrapperElement.innerHTML = `<header class="h-1/6 bg-green-100 border border-green-500 text-3xl text-gray-500 grid place-content-center">Header</header>`; } render(state: State) { return; } }

Component

import { Props, State } from '@custom-types/types'; import { Component } from '@custom-types/interfaces'; import { HeaderModel } from '@components/header/HeaderModel.js'; import { HeaderView } from '@components/header/HeaderView.js'; export class HeaderComponent implements Component { private _model: HeaderModel; private _view: HeaderView; constructor(props?: Props) { this._model = new HeaderModel(); this._view = new HeaderView(); } get element() { return this._view.element; } get state() { return this._model.state; } private setState(state: State) { this._model.setState(state); this._view.render(this._model.state); } attachTo(component: Component, position: InsertPosition = 'beforeend') { component.element.insertAdjacentElement(position, this.element); } }

위에서 문제는 View의 render 메서드가 단순히 이미 만들어진 템플릿에 이벤트를 붙이거나 dom api를 통한 직접 접근으로 바뀐 state를 전달하고 ui의 모습이 변한다는 점이다. 또, Component에서 구현된 attachTo라는 메서드는 가독성은 좋지만 부모에 한번 element 형태로 붙여버리면 이후에 이 element에 innerHTML 형태로 새로운 state를 넘긴 template literal을 전달할 수 없게 된다.(부모에 붙인 순간 이미 정적인 element가 붙어버리고 그 뒤에 값은 바꾼다고 부모에 붙은 element가 변하지 않기 때문이다.) 그리고 가장 큰 문제는 처음 내가 설계했던 컴포넌트마다 Model, View를 갖고 있는 리액트스러운 느낌이 아니라는 것이다. setState가 일어날 때마다 render 메서드가 실행되면서 새로운 state가 반영된 ui로 변경되어야 하는데 위 코드는 앞서 말한 문제들로 되지 않는다.

그럼 이제 새로운 코드를 살펴보자..!

리팩토링 후 코드

View

import { State } from '@custom-types/types'; import { TempAbstractView } from '@custom-types/abstracts.js'; export class HeaderView extends TempAbstractView { constructor($target: HTMLElement) { super($target); } template(state: State) { return `<header class="h-full bg-green-100 border border-green-500 text-3xl text-gray-500 grid place-content-center">Header</header>`; } render(state: State) { this.$target.innerHTML = this.template(state); this.addChildren(state); this.setEvents(state); } setEvents(state: State) { return; } addChildren(state: State) { return; } }

Component

import { Props, State } from '@custom-types/types'; import { TempComponent } from '@custom-types/interfaces'; import { HeaderModel } from '@components/header/HeaderModel.js'; import { HeaderView } from '@components/header/HeaderView.js'; export class HeaderComponent implements TempComponent { private _model: HeaderModel; private _view: HeaderView; $target: HTMLElement; constructor(targetElement?: HTMLElement, props?: Props) { this.$target = targetElement as HTMLElement; this._model = new HeaderModel(); this._view = new HeaderView(this.$target); } get state() { return this._model.state; } private setState(state: State) { this._model.setState(state); this._view.render(this._model.state); } }

위의 Header 컴포넌트는 따로 자식 컴포넌트가 없어서 props를 전달하거나 자식을 붙일 일이 없다. 위에서 핵심은 부모(target)을 전달하여 자식에서 부모의 element에 접근이 가능하게 함으로써 innerHTML로 새로운 state에 따른 ui를 변경할 수 있게 한 점이다.

위의 내용들은 추후에 더 자세히 길게 다뤄볼 예정이다.(리팩토링 정말 힘들었다...ㅠ 물론 이제서야 웃으면서 할 수 있게 되었지만..!)

undefined

Copyright 2023. all rights reserved by Jayden