새로운 블로그로 이전하였습니다!
article thumbnail image
Published 2023. 1. 10. 23:59

원래 회사에서 React Framework + TypeScript 기반의 프로젝트가 진행중이였었는데 회사에서 React를 도입한게 처음이라서 F/E에 추가인원투입을 위해선 React에 대한 전반적인 교육이 필요하였는데 React 교육이 하루 이틀로 끝날 것도 아니고.. 전반적으로 유지보수에서 어려움이 있어서 결국 React 프로젝트를 엎고 대신 그보다 좀 더 깊이가 얕은 lit-element 라이브러리를 사용하는 것으로 변경되었습니다. 그래서 이번에 lit element 를 사용하면서 중요한 부분들에 대해 정리하여 기록해두고자 합니다.

Lit-Element 와 React

Lit-Element 를 사용한 이유는 컴포넌트화를 통한 재활용 및 관리를 편하게 하기 위함이였습니다.

React처럼 Custom Element를 쉽게 만들 수 있고 또한 LifeCycle을 제공해서 특정 컴포넌트만 ReRendering 하는 기능을 쉽게 구현할 수 있도록 해줌과 동시에 공식 문서양이 많지 않아서 금방 배울 수 있다는 점에서 Lit-Element를 채용하게 되었습니다.

대신 React처럼 페이지 라우팅, 메인 App 을 렌더링 전 필수 데이터 로딩 등 상태관리는 제공되지 않아서 그런 점은 따로 구현이 필요합니다.

또한 글로벌 단에 자동으로 Import 되는 것이 아니므로 꼭 js 파일 export/Import가 필요합니다.

해당 본문에서는 Lit-Element의 사용법 및 Life-Cycle에 대해 간단히 정리하려고 합니다.

 

Lit-Element Getting Started

https://lit.dev/docs/getting-started/

 

Getting Started – Lit

Simple. Fast. Web Components.

lit.dev

CSR에 특화된 라이브러리로 ESM 형식의 모듈을 제공합니다.

VSCode에서 구현을 추천합니다.
VSCode 의 lit-plugin 을 다운로드 받으면 html 구문에 대해 하이라이트 표시 및 자동완성을 제해줘서 좀더 편하게 개발이 가능합니다. ( Emmet 은 안됨 ㅠ )

Lit-Element 컴포넌트

기본 템플릿

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {

  this.className = "";

  render(){
    
    return html`
      <div>
        <p>A paragraph</p>
      </div>
    `;
  }
}
customElements.define('my-element', MyElement);

render() 함수의 return html`` 부분을 화면에 출력합니다.

class 구문 밖 customElements.define 부분은 필수로 입력하여야 합니다.

위처럼 등록 시 아래 예시처럼 new 생성자 혹은 HTML 태그를 이용하여 호출이 가능합니다.

new MyElement()

--------OR--------

<body>
	<my-element></my-element>
</body>

 

Property

리액트와 동일하게 프로퍼티 요소가 변경되면 LifeCycle에 따라 리렌더링을 발생시킵니다.

아래 소스코드와 같이 구현 시 properties 로 등록되어 구현이 가능합니다.

또한 constructor 에서 동일한 이름으로 this 변수 생성 시 해당 변수에 입력됩니다.

Property 생성

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
  
  constructor({
  	string: ""
    boolean: true
    obejct: {}
    array: []
    event: null
  }) {
  	super();
    
    this.string = "기본값";
    this.boolean = true;
    this.object = {};
    this.array = [];
    this.event = null;
  }

  static get properties() {
  	return {
      string: {type: String}
      boolean: {type: Boolean}
      object: {type: Object}
      array: {type: Array}
      event: {type: MouseEvent}
    }
  }
  render(){
    
    return html`
      <div>
        <p>A paragraph</p>
      </div>
    `;
  }
}
customElements.define('my-element', MyElement);

 

Property 바인딩

위에서 생성하였던 MyElement 를 사용할 경우 예시 입니다.

import { LitElement, html } from 'lit-element';
import "./my-element.js";

class MyComponent extends LitElement {

	handlerClick() {
      console.log("click!!");
    }
    
	render(){
    	const myElement = new MyElement({
    		string: "String",
        	boolean: true,
        	object: {},
        	array: [],
    	});
    
    	return html`
        	<div>
                <my-element
                    string=""
                    ?boolean=true
                    .object=${object}
                    .array=${array}
                    @event=${this.halderClick}
                ></my-element> // HTML 방식 호출
        		${myElement} // 생성자 방식 호출
      		</div>
    	`;
	}
}
customElements.define('my-element', MyElement);

HTML 방식 호출 시 리렌더링을 웹 엔진이 관리해서 요소 변경이 없을 시 렌더링하지 않지만

생성자 방식 호출 시 render() 호출 시 무조건 컴포넌트를 새로 그림

 

Event 바인딩

Lit 에서 HTML을 그릴 때 Event를 주고 싶은 경우 아래 두가지 방식으로 바인딩 시킬 수 있습니다.

아래는  `click` 이벤트를 바인딩하는 예시 입니다.

DOM에 추가된 후 생성

_handleClick()
{
	console.log("click");
}

render() {
	return html`<button @click="${this._handleClick}">`;
}

DOM에 추가되기 전 생성

constructor() {
	super();
	this.addEventListener('click', this._handleClick);
}

 

LifeCycle

HTML 요소를 렌더링 하는 과정에 있어서 특정 타이밍에 맞춰서 필요한 로직을 실행시킬 수 있습니다.

예를 들어 1. 의 리렌더링 요소가 발생될 경우 아래 2 ~ 9 까지 순서대로 동작하게 됩니다.

 

1. Property

static get properties() {
	return {
		string: {type: String}
	}
}

proeprties 안에 선언된 값이 변경될 경우 리렌더링 됩니다.

컴포넌트의 text나 value가  가변적일 경우 사용하면 좋습니다.

 

2. this.requestUpdate()

func() {
	this.requestUpdate();
}

this.requestUpdate 함수가 실행될 경우 리렌더링이 발생합니다.

특정 함수에서 로직 발생 후 컴포넌트를 렌더링 시키고 싶을 때 유용하게 사용할 수 있습니다.

 

3. performUpdate()

async performUpdate()
{
	super.performUpdate();
}

super.performUpdate 실행 시 다음 렌더링 과정으로 넘어가게 됩니다.

보통 비동기 호출이 필요할 경우 사용하여서 대개 async / await 을 주로 붙여서 사용하게 됩니다.

 

4. shouldUpdate( changeProperties )

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {
  static get properties() {
    return {
      prop1: { type: Number },
      prop2: { type: Number }
    };
  }
  constructor() {
    super();
    this.prop1 = 0;
    this.prop2 = 0;
  }

  render() {
    return html`
      <p>prop1: ${this.prop1}</p>
      <p>prop2: ${this.prop2}</p>
      <button @click="${() => this.prop1=this.change()}">Change prop1</button>
      <button @click="${() => this.prop2=this.change()}">Change prop2</button>
    `;
  }

  /**
   * Only update element if prop1 changed.
   */
  shouldUpdate(changedProperties) {
    changedProperties.forEach((oldValue, propName) => {
      console.log(`${propName} changed. oldValue: ${oldValue}`);
    });
    return changedProperties.has('prop1');
  }

  change() {
    return Math.floor(Math.random()*10);
  }
}
customElements.define('my-element', MyElement);

기존 value와 변경된 value가 무엇인지 확인이 가능합니다.

기본적으로 아무거나 Propery가 변경되었을 때 리렌더링 되지만
특정 Property가 변경되었을 때에만 리런데링 시키고 싶을 때 사용하면 됩니다.

 

5, update()

뭐에 쓰는지 모르겠습니다..

공식 문서에 따르면 메소드를 override 할 필요가 없으나 만약 했다면 super.update( changeProperties ) 를 호출해야 렌더링이 진행 된다고 합니다.

Reflects property values to attributes and calls render to render DOM via lit-html. Provided here for reference. You don't need to override or call this method. But if you override it, make sure to call super.update(changedProperties) or render will never be called.

 

6. render()

render()
{
	return html``;
}

 

return 된 html 요소를 DOM에 바인딩 시킵니다.

 

7. firstUpdated()

firstUpdated()
{
	console.log("firstUpdate");
}

HTML 요소가 DOM에 처음 바인딩 될 때 최초 1회만 실행됩니다.

DOM 내에 context 요소가 필요하며 일회성 작업이 필요할 때 사용하기 용이합니다.

 

8. updated()

updated()
{
	console.log("updated");
}

HTML 요소가 DOM에 바인딩 및 렌더링 된 후 마다 실행됩니다.

DOM 내에 context 요소가 필요할 때 사용하기 용이합니다.

 

9. await this.updateComplete

await this.updateComplete;

모든 렌더링이 완료 됐을 때 Promise<Boolean> 값을 리턴해줍니다.

 

Exmaple

import { LitElement, html } from 'lit-element';

class MyElement extends LitElement {

  this.className = "";

  render(){
    
    return html`
      <div>
        <p>삼항연산자</p>
        ${this.boolean ? 
        	html`<p>트루임</p>`
            : html`<p>거짓임</p>`}

        <p>반복문</p>
        ${this.array.map((item) => 
        	html`<p>${item}</p>` }
      </div>
    `;
  }
}
customElements.define('my-element', MyElement);

 

참조

https://lit.dev/docs/v1/

https://lit.dev/docs/

'Front-End' 카테고리의 다른 글

검색엔진최적화(SEO)  (2) 2023.10.22
복사했습니다!