티스토리 뷰

반응형

 

엘리님의 브라우저 101이라는 강의를 듣고 있다. 

사실 직접해보는 시간을 가지고, 또 강의를 듣는 과정을 반복하니 어려웠던 함수를 선언하고 호출하는 것도 

'아 이거구나!' 하는 순간이 왔다. 

 

하지만 class를 이용해 리펙토링하는 과정에서 정말 너무 부족함을 깨달았다.

그 전과는 다른 어려움이랄까..  항상 하루에 드는 깨달음이 있으면 글을 적는 편인데,

이번에는 그런게 없었고, 계속 '왜 그런 거지?'라는 의문이 들었다. 

 

유튜브에 있는 class와 콜백 함수와 관련해 있는 강의는 다 보았다.

봐도 좀 어려웠지만, 반복해서 보니 이해가 좀 되는 것 같다 싶어 다시 강의를 보면 '왜?'라는 생각이 들었다. 

그리고 다시 유투브강의들을 보고, 문법책도 보았다. 

 

주말에 약속이있어 약속 장소를 가는 버스에서도, 자기 전에도 봤던 기본 정의도 계속 보며 잠들고는 했다. 

그리고 또 엘리님강의를 보고 하나하나 쳐보고 해도 이해가 잘 안 되어 다시 반복.. 

 

그래도 확실히 처음 강의를 들었을 때보다는 익숙해지고 흐름을 파악하고 있는 것 같아 희망을 가지고 이 루틴을 반복했다. 

 

앞서 공부했던 것들을 바탕 + 정말 원초적인 대입법으로 마침내 이해하는데 성공했다.

물론 새롭게 하면 새로울지 모르겠지만, 적어도 이 코드를 이해할 수 있다는 게 얼마나 행복한 일인지 모른다. 

 

 

 

그 과정에서 처음에 이해를 함에 도움이 되었던 몇가지를 적어보겠다. 

 

1. 인스턴스(instance)는 현재 생성된 바로 그 '객체'라고 생각하자. (instant의 느낌) 여기서 인스턴스를 생성하는데 
사용하는 함수를 생성자(cosntructor)라고 한다. 
2. 따라서  초기화된 인스턴스를 생성하려면 constructor 내부에 this를 이용하여 인스턴스 프로퍼티를 추가한다. 
3. 여기서 class 내부의 this는 메서드를 소유한 객체가 아닌 새로 기술할 객체 즉 메서드를 호출한 객체에 바인딩된다. 

4. 함수는 값으로 전달할 수 있다. 

 

이해가 안되었던 코드만 가져왔다. 

 

// popup.js

"use strict";

export default class PopUp {
  constructor() {
    this.popUprefreshBtn = document.querySelector(".pop-up__refresh");
    this.popUprefreshBtn.addEventListener('click', () => {
        this.onClick && this.onClick();
    })
  }


setClickListener(onClick) {
  this.onClick = onClick;
}

}
//main.js

import PopUp from './popup.js';

const gameFinishBanner = new PopUp();

gameFinishBanner.setClickListener(() => {
  startGame();
})

 

PopUp class 안에서 onClick 함수를 호출하는 동작원리가 이해가 가지 않았다.

처음에는 클래스를 계속 이해하려고 했는데, 알고 나니 콜백 함수 개념의 이해가 부족했음을 깨달았다. 

 

이제 이 코드들을 순서대로 뜯어보겠다. 

 


01. 코드 작성 순서대로 보기  

 

 

1. class 내에 함수를 선언한다. : setClickListener();

 

setClickListener(onClick) {
  this.onClick = onClcik;
}

PopUp 클래스 안에서 setClickListener()

이라는 메서드를 선언했다. 그리고 그 안에 onClick이라는 매개변수(Parameter)를 넣어  setClickListener(onClick)가 되었고, 

그 함수 안에서는 this.onClick이라는 멤버 변수를 만들어 매개변수(Parameter)인 onClick을 할당했다. 

 

 

 

2. popUprefreshBtn 에 click 이벤트를 넣는다. 

 

 this.popUprefreshBtn.addEventListener('click', () => {
        this.onClcik && this.onClcik();
    })

this.onClick이라는 변수에 어떤 값이 지정되어 있다면 this.onClick()을 실행하라.

&&연산자를 이용해 this.onClick이 false면 뒤의 this.onClick();을 실행하지 않게 해 준다. 

 

 

3. main.js에서 PopUp class 를 불러온 후 새로운 변수에 할당한다.

import PopUp from './popup.js';

// class를 새로운 변수에 할당한다. 
const gameFinishBanner = new PopUp();

// setClickListener를 등록한다. 
gameFinishBanner.setClickListener(() => {
  startGame();
})


// class 등록전에 기존의 코드. 
popUprefreshBtn.addEventListener("click", () => {
  startGame();
});

그래서 main.js에 PopUp을 가져오고, 새로운 변수(gameFinishBanner)에 할당했다. 

내가 혼동했던 부분이 그 다음의 콜백 함수를 등록하는 과정에서이다. 

 

 

'여기서 startGame();을 넣었는데, 어떻게 class PopUP 에 등록한 함수에서 onClick으로 동작하는 걸까?'

 

 


코드 동작 순서 (참고)

꼭 이해하고 싶은 마음에 동작 순서도 적어보았다. 

 

-class에 의해 object가 만들어질 때, 

    const gameFinishBanner = new PopUp();

 

1. Class 내부에 있던 메서드 (setClickListener)는 호이 스팅으로 가장 먼저 선언

 

2. constructor 내부에 있는 코드 실행

    - this.popUpRefresh

    - this.popUpRefresh.addEventListener

 


02. 코드 동작 흐름대로 파악하기 

 

 

다시 천천히 봐보자. 

 

const gameFinishBanner = new PopUp();

gameFinishBanner.setClickListener(() => {
  startGame();
})

 

1.  setClickListener이라는 함수에 함수 startGame()를 인자로 전달한다.  


popUprefreshBtn.addEventListener("click", () => {startGame();});

 

 

이 부분이 헷갈린 이유는 불필요한 함수가 안에 있기 때문이었다. 불필요한 함수를 제거하니 훨씬 가독성이 좋았다. 

popUprefreshBtn.addEventListener("click", startGame);

위의 기존의 코드를 새로 만든 class 변수(gameFinishBanner)에  새로 할당한 것뿐이다. 

>>

1. gameFinishBanner.setClickListener(() => {startGame();}))

2. gameFinishBanner.setClickListener(startGame);  // 불필요한 함수 제거

참고로 click 이벤트는 넣지 않는다.

click이벤트는 class PopUp에 이미 등록해놓았기 때문에 필요한 부분만 가져오면 된다. 


 

여기서 불필요한 함수를 제거함으로써 기존에 호출하던 방식의 startGame(); 형태와 혼동이 와서 찾아보았다.

startGame()으로 쓰게 되면 매개변수(Parameter)로 전달하게 되는 것이 아니라 함수가 실행이 되기 때문에 꼭 ()를 빼야 한다. 

==> 이런 콜백 함수는 바로 실행하는 함수가 아니라, 함수의 참조값(정의)만 전달함으로써, 후에 필요할 때 호출할 수 있게 된다.

 

관련 링크 (http://daplus.net/javascript-javascript-%ED%95%A8%EC%88%98%EB%A5%BC-%EB%A7%A4%EA%B0%9C-%EB%B3%80%EC%88%98%EB%A1%9C-%EC%A0%84%EB%8B%AC/)

 

따라서 setClickListener이라는 함수에 startGame이라는 함수를 인자로 전달하게 되었다. 

 

 

 

 


 

 

 

  setClickListener(onClick) {
    this.onClick = onClick;
  }

 

2. setClickListener함수 안에서는 어떤 일이 일어날까?

 

인자(argument)로 전달받은 함수 startGame()를 PopUp오브젝트의 this.onClick에 할당해 놓는다. 

 

setClickListener(startGame) {
this.onClick = startGame ;
}
 
 
그래서 언제든지 onClick()을 작성하면 우리가 할당한 함수를 호출하게 된다. 즉, startGame(); 함수가 호출되며 게임이 시작된다.

 

 

 


 

 

 

export default class PopUp {
  constructor() {
    this.popUprefreshBtn = document.querySelector(".pop-up__refresh");
    this.popUprefreshBtn.addEventListener("click", () => {
      this.onClick && this.onClick();
    });
  }

3. PopUprefreshBtn이 클릭되면 콜백 함수로 인해 startGame()이 호출된다.

 

 

따라서 후에 PopUprefreshBtn이 클릭이 되면

this.onClick이라는 변수에 어떤 값이 지정이 되어있다면 (= setClickListener라는 함수를 통해서 등록된 함수가 있다면)

this.onclick() 즉,  startGame()을 수행하게 된다. 

(앞서 얘기한 바와 같이 매개변수로 전달받은 함수를 호출하려면 ()를 붙인다. => onClick();)

 

 

 

*참고 

 this.onClick && this.onClick();

this.onClick은 옵셔널 한 함수다. 그러니까  popup창을 호출하지 않을 수 있는 경우가 있을 수 있다.

이렇게 되면 gameFinishBanner.setClickListener를 호출하지 않게 되고, this.onClick은 undefined이 되는데, undefined으로 호출할 수는 없으니 값이 있을 때만 호출할 수 있도록 해주는 것이 좋다. 

 

단, 생성자에서 onClick을 항상 전달받아 와서 값이 계속 있는 경우라면 생략 가능하다. 

(글에는 생략되었지만 아마 this.hide()라는 함수가 있어서 더더욱 써줘야 할 수도 있겠다는 생각이 든다. )

 

 

 


 

 

 

아 머릿속의 복잡함을 글로 풀어내니 나는 확실히 정리가 되었는데,

읽으시는 분이 계시다면 이해가 안 되실 것 같다.. 

그래도 내가 이해하려고 올린 것이니 만족한다. 

 

어려웠던 부분이었는데 이 정도까지 이해할 수 있음에 너무 감사하다. 

처음 접했을 때는 정말 벌써 막히는 건가 라는 생각도 들어 힘이 쭉쭉 빠졌다.

그래도 몇 번 반복하면 되겠지 싶어 반복하고, 공부했음에도 이해가 되지 않았다.

이러니 심적으로 불안하고 힘이 들어 공부가 하기 싫었으며 더 나아가 '그냥 이번 파트는 지나칠까?'라는 생각도 들었다.

 

하지만 그럴 때마다 '한 번 더 해보자'라는 생각이 뒤따라 들면서

계속 들여다보고 바라보면 이해할 수 있는 시발점이 있을 거라는 생각에 지나치지 않고 붙들고 있었다. 

 

그리고는 마침내 '유레카!' 하는 순간이 왔다. 

 

지금 정말 뿌듯하고 기분은 좋지만,

완전히 완벽히 이해했다고는 못하겠다. 다른 곳에서 응용할 수 있을 때 완벽히 이해한 것일 테니..

하지만 적어도 내가 어느 부분이 많이 부족하고, 이해가 덜 되었는지 알았고, 그 부분을 많이 채웠던 시간들인 것 같다. 

 

내일도 파이팅하자..

 

 

 

반응형