[바닐라코딩 프렙] 2주차 후기

Suh, Hyunwook·2021년 8월 1일
0
post-thumbnail

프렙 2주차에는 DOM과 Scope, hoisting에 관하여 학습하였다.
그 중에서도 오늘은 DOM 개념코드리뷰 이후 배운 것들에 대해서 다뤄보고자 한다.

1. DOM(Document Object Model): XML이나 HTML 문서에 접근하기 위한 일종의 인터페이스

DOM을 정의로 학습하면 와닿지 않는다. DOM의 뜻을 한글로 풀어서 말하면 문서객체모델(Document Object Model)이다. 번역투라 그런지 와닿지는 않는다. 외부에서 설명하는 정의를 보면, 페이지를 제어하는 API(Application Programming Interface)이다. 역시 와닿지는 않는다.

DOM은 HTML을 객체처럼 표현한 것이다. HTML을 객체화하였으니, HTML 자체는 아니다. 즉, HTML을 트리의 형태로 만든 것이다. 그리고 그 트리의 나뭇가지들(Branches)을 노드(Node), 노드(Node) 안에 있는 내용(Content)를 요소(Element)라고 부른다.

HTML을 굳이 왜 트리형태로 만들었을까? 이는 HTML을 쉽게 제어하기 위함이다. 즉, id, class 등을 가져오는 getElementById 등과 querySelector 등을 이용하여 각종 이벤트 및 텍스트 수정을 할 수 있다.
좀 더 자세히 말하면 각 노드, 요소들을 제어하여 Javascript를 통해 동적인 효과를 주기 위함이다. 이러한 DOM은 CSSOM(CSS의 DOM 정도로 생각해두자..)과 합쳐져 Render Tree를 형성하게 되고, 이를 통해 최종적인 화면(Viewport)가 결정된다.

2. 시간지연함수(setTimeOut) : 타이머가 만료된 뒤 함수나 지정된 코드를 실행하는 함수

다음으로 다룰 내용은 Tik-tac-to 구현을 하는 과정 무수한 삽질(?)을 하게 만들었던 문제점이다.

뭔가 이상하지 않은가? 위의 클립에서 'XXX'가 보이기 전에 'XX'만 있는데도 'X'가 이겼다는 alert가 먼저 보인 뒤에, X가 표기되는 문제점이 있다.

function writeSign() {
  if (this.textContent !== "") return;

    signCount++;
    if (presentTurn.textContent === `TURN: ${xTurn.textContent}`) {
      this.textContent = `${xTurn.textContent}`;
      index = parseInt(this.id); 
      squares[index] = xTurn.textContent;
      presentTurn.textContent = `TURN: ${oTurn.textContent}`;
      getResult(squares);
      //getResult(squares)함수는 우승 판별 및 alert를 실행해준다.
    } else {
      this.textContent = `${oTurn.textContent}`;
      index = parseInt(this.id);
      squares[index] = oTurn.textContent;
      presentTurn.textContent = `TURN: ${xTurn.textContent}`;
      getResult(squares);
    }
}

문제점을 설명하기 전에, 위 코드의 구조에 대해서 간단히 설명을 하고자 한다. WriteSign()는 3*3보드에 O 또는 X를 표기해주며, getResult(squares)함수는 squares를 parameter로 보내어, 우승 판별 및 alert를 실행해준다.

C언어 계열에서는 순차적으로 실행하기 때문에, 브라우저에 먼저 O,X 표기를 한 뒤에, getResult()함수를 실행하는 저절차식을 따르지만, Javascript에서는 hoisting에 의해 내부 함수부터 실행이 되므로, setTimeout을 통해 의도적으로 실행을 지연시켜줘야 한다.

setTimeout 사용법을 간략히 보면 다음과 같다.
setTimeout : 일정 시간이 지난 후에 원하는 함수를 예약 실행해주는 '호출 스케줄링'이다.
예시를 통해 보면 다음과 같다.

function sayHi() {
  alert('안녕하세요.');
}

setTimeout(sayHi, 1000);

이 함수에서는 sayHi가 1초가 지난 뒤에 실행되도록 한다.
단, 유의해야할 구조는 다음과 같다.

function sayHi() {
  alert('안녕하세요.');
}

setTimeout(sayHi(), 1000);

이 구조에서 sayHi()는 함수실행을 요청하는 것이 아닌, sayHi()의 결과값이고, sayHi는 리턴값이 없으므로 undefined가 되어, 스케줄링 대상을 찾지 못해 코드가 동작하지 않는다. 비동기 이벤트 동작 예제를 통해 이해를 넓혀보자.
(출처: https://doitnow-man.tistory.com/128)

alert('A');
setTimeout(function() {
    alert('B');
},0);
alert('C');       

예상으로는 A,B,C로 이벤트가 호출될 것 같지만, 실제로는 A,C,B 순서대로 이벤트가 호출된다. 어? setTimeout함수에 지연시간이 0인데 왜 그럴까? 이는 javascript 엔진이 먼저 코드를 실행 후 비동기 이벤트인 Web API를 실행하기 때문이다. 상세동작을 통해 보면 다음과 같다.

  1. alert('A')를 call stack에 넣고, alert 실행 후 stack에서 제거
  2. setTimeout을 call stack에 넣고, setTimeout의 Event handler를 Event Queue에 넣은 후 setTimeout을 stack에서 제거
  3. alert('C')를 call stack에 넣고, alert 실행 후 stack에서 제거
  4. call stack이 비어있는 경우, EventQueue에서 Event handler 하나를 call stack에 넣은 뒤, EventQueue에서 해당 Event handler를 제거
  5. call stack에 존재하는 해당 Event handler를 실행 후 call stack에서 제거

자, 이제 문제가 있던 나의 코드를 수정해보도록 하자.

function writeSign() {
  if (this.textContent !== "") return;

    signCount++;
    if (presentTurn.textContent === `TURN: ${xTurn.textContent}`) {
      this.textContent = `${xTurn.textContent}`;
      index = parseInt(this.id); 
      squares[index] = xTurn.textContent;
      presentTurn.textContent = `TURN: ${oTurn.textContent}`;
      setTimeout(() => getResult(squares), 0);
      //getResult(squares)함수는 우승 판별 및 alert를 실행해준다.
    } else {
      this.textContent = `${oTurn.textContent}`;
      index = parseInt(this.id);
      squares[index] = oTurn.textContent;
      presentTurn.textContent = `TURN: ${xTurn.textContent}`;
      setTimeout(() => getResult(squares), 0);
    }
}

위 코드에서 getResult에 setTimeout 함수를 걸고, getResult를 eventQueue에 넣어지도록 수정하였다. 이로서 getResult는 앞의 모든 코드가 실행된 이후 실행되도록 하였다. 시간을 0으로 설정해도 getResult는 eventQueue에 있기 때문에 마지막에 실행된다. 여기서, 주의할 점은 setTimeout(getResult(squares),200)과 같이 짜지 않는 것이 중요하다. 이는 getResult의 결과값을 넣는 것이 getResult의 return 값은 undefined이기 때문에, 의미가 없다.

0개의 댓글