문제 발생 !

Uncaought TypeError: this.onItemClick is not a function at HTMLElement.onClick

 

무려 나를 3~4시간동안이나 방황하게 만든 오류였다 ...

오류가 났었던 코드
해당 코드로 하니까 문제가 발생한 상황 .... ㅠㅠ

 

 

 

어..? 하다보니까 문제가 해결 됐는데!

근데 도대체 이유가 뭐지 !!

오류가 해결된 상황 (!) 그런데 도대체 왜 ?!

 

 

 

 

답은 javascript <this 바인딩> 문제였다.

 

먼저, this란?

자바스크립트에서 this는 현재 실행되고 있는 코드를 가리킨다. this 를 직역하면 "이것" 이듯이, 그냥 지금 이순간의 코드를 가리키는 것이다. 자기자신을 손가락으로 가리키고 있는 것이라고 생각하면 편하다.

 

그래서 왜 실행이 안됐던 건데?

this.(특정 엘리먼트).addEventListener('click', this.(특정 함수));

// 예시 example
this.gameField.addEventListener('click', this.onClick);

위의 코드와 같이 특정 함수를 함수명 그대로 전달할 때, 자바스크립트에서는 클래스 정보는 같이 전달이 되지 않는다.

즉, 위 코드와 같이 함수를 인자로써 어딘가에 전달할 때, 클래스는 무시된 상태로 함수만 전달이 되어진다.

그러니까 껍데기만 전달이 되고 그 안에 알맹이는 전달이 되지 않는 것이다. (!!!)

 

따라서 이 때 필요한 것이 데이터 바인딩이다 !

 

바인딩 처리하기 첫번째, 직접 바인딩하라고 알려주기

this.(특정 함수) = this.(특정 함수).bind(this);
this.(특정 엘리먼트).addEventListener('click', this.(특정 함수));

// 예시 example
this.onClick = this.onClick.bind(this);
this.field.addEventListener('click', this.onClick);

 

바인딩 처리하기 두번째, 에로우 펑션(Arrow function) 이용하기

this.(특정 엘리먼트).addEventListener('click', (event) => this.(특정 함수)(event));

// 예시 example
this.gameField.addEventListener('click', (event) => this.onClick(event));

 

바인딩 처리하기 세번째, 애초부터 함수를 정의할 때 데이터 바인딩 하기

// 원래 일반적인 함수 정의
onclick(event) {
	// ...
}

// 바인딩 처리한 함수 정의
onclick = event => {
	// ...
};

 

어떻게 보면 keydown 은 keypress 의 상위버전이다.

keydown 은 문자키에 대해서만 반응한다. 즉,  Alt, Shift, Ctrl, Meta

같은 입력에는 반응하지 않는다.그러나 keypress 의 경우에는 모든 입력에 대해서 반응한다.

 

https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event

 

Document: keydown event - Web APIs | MDN

The keydown event is fired when a key is pressed.

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/API/Document/keypress_event

 

Document: keypress event - Web APIs | MDN

The keypress event is fired when a key that produces a character value is pressed down.

developer.mozilla.org

 

<ul></ul>

<input type="text">
<button onclick="onAdd()"><button>
const ul = document.querySelector('ul');
const input = document.querySelector('input');

function onAdd(){

    // 입력한 text를 받아온다
    const input__value = input.value;
    
    // 아무것도 입력되지 않았다면, 리스트에 추가되지 않는다.
   if (input__value == '') {
    	input.focus();
        return;
    }
    
    // 새로운 li 태그를 생성한다.
    const li = document.createElement('li');
    li.innerText = input__value;
    
    // ul 에 생성된 li를 추가해준다.
    ul.appendChild(li);
    
};

javascript 를 파싱해올 때에 순서 또한 고려해야 좋은 코딩을 할 수 있다.

크게 네가지의 경우를 통해서 javascript를 불러와보자.

(자료출처 : dream-coding)

 

첫 번째, head 내부에서 javascript를 불러오기.

만약 js 파일이 엄청나게 커진다면, 웹페이지를 띄우는 데에 상당히 오랜 시간이 걸릴 수 있다.

 

 

 

두 번째, body 태그 뒤에서 javascript 불러오기.

만약, javascript나 애니메이션에 많이 의존적인 웹페이지라면 곤란한 상황이 발생한다.

 

 

 

세 번째, head 내부에서 async 로 불러오기.

파싱과 함께 병렬적으로 페칭이 되고 실행할때 잠시 block 되고 js가 실행되기 때문에 시간이 절약된다.

그러나 js가 html보다 먼저 실행되기 때문에 js가 html 엘리먼트를 사용해야 한다면 불러오지 못하는 오류가 발생한다.

그러나, 파싱을 하다가 중간에 block이 되기때문에 중간에 멈출 수 있다.

 

 

 

네 번째, head 내부에서 defer로 불러오기.

파싱과 페칭을 함께하고 모든 html이 완료되면 그제서야 js를 실행하기 때문에 가장 안전한 방법이다.

https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction

 

Introduction to the DOM - Web APIs | MDN

The Document Object Model (DOM) is the data representation of the objects that comprise the structure and content of a document on the web. In this guide, we'll briefly introduce the DOM.

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API

 

Introduction to the DOM - Web APIs | MDN

The Document Object Model (DOM) is the data representation of the objects that comprise the structure and content of a document on the web. In this guide, we'll briefly introduce the DOM.

developer.mozilla.org

> MDN 사이트에서 DOM 에 관한 설명 참조

 

 

 

HTML 은 DOM tree 구조로 구성되어 있다.

HTML 의 구성요소(Element)들은 모두 Node로 이루어져 있고, 각각의 노드들은 왼쪽과 같은 tree 의 구조로 연결되어 있다. 왼쪽 사진에서 볼 수 있다시피, 모든 노드들은 결국 EventTarget 이기 때문에, 우리가 javascript 코드에서 addEventListener를 사용할 수 있는 이유도 된다.

 

쉽게 말해서, html을 코딩할 때 쓰이는 모든 태그들은 노드인 것이고, 그 노드들은 전부 이벤트타겟이 된다는 것이다.

 

우선, html 이러한 DOM 구조로 이루어져있다는 사실을 알게 되면, 이 구조가 어떤 순서로 웹 화면에 로딩되는지를 이해할 준비가 되었다.

 

 

 

 

 

HTML Rendering 순서

html 렌더링이란, html 이 웹 화면에 보여질 때 어떤 순서대로 업로드 되는지에 관한 이야기이다.

<Constuction 파트>

1. 불러오기

2. 파싱(Parsing) : 웹 엔진이 html 문서를 파싱해서 Dom tree 구조를 만든다.

이 때, CSSDOM 이란, HTML DOM tree 와 비슷하게 CSS 문서의 tree 구조인 것이다.

3. 렌더링 트리 만들기 : 렌더링트리는 dom tree 와 비슷한데, 차이점은 dom tree 는 모든 노드가 포함이 되지만, rendering tree 에는 "실제로 웹 상에 보여지는" 노드들만이 포함된다.

 

<Operation 파트>

4. layout : 위에서 언급했다시피, html 은 노드들로 구성되어 있다. 이러한 노드들의 위치를 정하는 단계이다.

5. paint : 해당 노드들의 위치를 정했다면, 이제 나누어진 노드 별로 해당 부분을 제작하는 것을 말한다.

만약 노드별로 나누어서 작업을 하지 않는다면, html은 특정한 노드를 변경할 때마다 전체 화면을 전부 갱신해야 할 것이다. 그러나 노드별로 쪼개어서 작업을 한다면 html은 해당 부분만 갱신하면 될 것이다.

6. composition : 노드들의 관계만 살짝 변경하는 것이다.

 

위의 과정에서 본다면, 만약 html 웹 상에서 구조를 변경시켜야하는 부분이 있다면, layout을 변경시키는 것보다 paint 선에서, 더 나아가 composition 선에서만 처리할 수 있다면 높은 성능을 보일 것이다.

 

https://csstriggers.com/

 

CSS Triggers

@PROPERTY_DESCRIPTION@ B G W E Change from default B G W E Subsequent updates

csstriggers.com

위의 사이트를 참고하면 어떤 css attribute 가 높은 성능을 보이는지 알 수 있다.

 

 

예를 들면, 노드의 left 혹은 top 속성을 직접적으로 변경한다면 layout의 변경이 일어난다. 그러나 transform 속성을 이용한다면 composition의 변경만으로도 웹 상의 변경을 발생시킬 수 있다. 그러면 더 높은 성능을 보이는 것이다.

 

위의 사진은 CSS triggers 사이트에서 설명하고 있는 css tag 별 성능 분석 자료이다. 보라색은 layout, 연한 초록은 paint, 진한 초록은 composite 를 변경한다는 의미이다. 보면 top 과 left를 직접적으로 변경하면 변경할 때마다 layout 부터 전부 변경하는 것으로 나와 있다. 그러나 transform 속성의 경우에, Chrome의 경우에 composite 파트의 변경만으로 갱신하는 것을 볼 수 있다.

 

이제 정말 성능차이가 나는지에 대해서 살펴보자. 먼저 성능을 알기위해서는 웹을 키고 개발자 도구를 켠 다음에, Performance 에서 녹화를 해주면 알 수 있다.

 

먼저 top과 left 속성에 직접적으로 접근해서 html을 변경하는 경우의 성능.

속도가 16ms 를 넘어가면 성능이 좋지 않음을 의미한다. 그런 의미에서 빨간 줄이 다수 보이는 것을 알 수 있다.

 

다음으로, transform 속성을 변경하는 경우의 성능.

같은 동작을 하는 웹 페이지인데도 속성을 어떤 것을 선택하느냐에 따라서 성능이 이렇게 달라진다. 이번에는 빨간색의 줄들이 나오지 않는 것을 볼 수 있다.

 

 

방금 제시한 예시는 github에 올려두었다.

https://github.com/SMJin/javascript101

 

SMJin/javascript101

Contribute to SMJin/javascript101 development by creating an account on GitHub.

github.com

top 과 left 속성에 직접적으로 접근해서 html을 변경하는 것은 01.wep-api 의 007-practice 이고, transfrom 속성만으로 변경을 시도하는 것은 02.DOM 의 007-practice 이다.

+ Recent posts