[리팩토링, 모듈화] carrot harvest game / object.freeze

holang-i·2021년 5월 22일
0

기존 코드

버튼 클릭시 각각의 이벤트 핸들러를 통해 기능이 동작된다.

  • 게임 시작
  • 게임 멈춤
  • 게임 승, 패에 따라 그 안에서 각각 알림 소리와 알림창을 다르게 설정한다.
  • 게임 시간을 초기화한다.
  • 남은 게임 시간을 보여주는 함수
  • 남은 목표 개수를 보여주는 함수
  • 버튼 숨김 함수
  • 게임 타이머 멈춤 함수
  • 팝업 숨김 함수
    ...

기존 프로젝트 형태 - 하나의 js파일에 많은 기능들이 담겨있다.

위에서 다 적지 않았는데도 이미 함수가 꽤 많다.
위의 기능에서 추가적인 기능이 더 들어갈 경우 하나의 js파일에 너무나도 많은 코드가 복잡하게 작성되어지기 때문에 각각의 코드를 모듈화시키는 작업이 필요 할 것 같다!


우선 게임 시작, 멈춤버튼, 타이머, 게임 상황을 나타내는 영역과 게임 아이템들이 보여지는 부분을 크게 분리시킬 필요가 있을 것 같다.


모듈화

popup창 분리

popup.js 파일을 만들어서 게임의 팝업 창을 클래스로 정의했다.
constructor안에서 html 태그들의 값을 받아와서 생성하고, 팝업창 버튼 이벤트를 통해 콜백함수에 대한 처리를 진행하고, main.js 파일에서 import를 통해 안에서 객체를 생성해서 처리하는 로직으로 모듈을 하나 연결했다.

그리고 연결이 잘 되었는지 확인하기 위해서 live server로 html파일을 열어서 확인해보았는데 한 가지 오류가 발생했다.

🐾  첫 번째 오류 발생


⭐️ module import / export를 이용할 때는 index.html의
script 부분에서 type을 module이라고 지정을 꼭 해줘야 된다.

기존

<script src="src/main.js" defer></script>`

변경

<script type="module" src="src/main.js" defer></script>

🐾  두 번째 오류 발생

constructor() {
  ...
  this.popUp.addEventListener('click', () => {
    this.onClick && this.onClick();
    hide();
  });
}

멤버 변수를 사용할건데 this를 빼먹었다..!
클래스 안에서는 해당 클래스의 멤버변수를 접근할 때 this키워드를 붙여서 접근해야된다.


field 분리

field는 화면의 게임영역이다. 당근과 벌레를 뿌리는 기능을 담당한다.
사용자로부터 당근, 벌레의 개수를 입력받으면 그 개수만큼 각각의 아이템들을 필드영역에 뿌려주는 클래스로 분리시켰다.

private 함수

클래스 내부에서만 사용 가능한 (외부에서는 보이지 않는) 함수를 private 함수라고 한다.
타입스크립트나, 다른 객체지향 프로그래밍 언어에서는 private 키워드로 써줄 수 있는데, 자바스크립트에서는 아직 그런 접근제어자 키워드를 통용적으로 사용하지 않고 있다고 한다.

그래서 보편적으로 함수 또는 변수 이름 앞에 _ 이렇게 붙여 주면, 내부에서만 쓰이는 거구나! 하고 알 수 있기 때문에 랜덤으로 당근, 벌레를 뿌리는 함수에 적용해 놓았다.
--> 좋지 않은 방법이지만 private의 의미를 나타내기위해 함수명 앞에 underscore를 붙여서 작성하였다.

그리고 여기서 field 클래스와는 상관없는 즉, 모든 다른 클래스들에서도 공통적으로 사용되는 함수를 만들었는데 이 함수는 클래스 내부에 포함시키지 않고, static한 함수로 사용하기 위해 클래스의 밖에 정의해놓았다.


새로운 에러!

이번에는 중요한 에러가 발생했다!

이 에러는 this 바인딩에 관련한 에러이기 때문에 해결방법을 정확히 알고있어야 된다.
아래에서 this binding하는 법을 세 가지로 나눠서 정리해 볼 것이다.

this binding

바인딩을 하는 방법에는 3가지 방법이 있다.

첫 번째 방법

constructor( ) {} 안에서 this.~~.bind(this)를 통해 바인드를 해 놓으면 해당 함수는 클래스와 바인딩되어있기 때문에 this로 전달하면 해당 클래스를 알 수 있다.

this.onClick = this.onClick.bind(this);

두 번째 방법

arrow function을 사용하는 방법

해당 이벤트리스너에 이벤트가 전달되면 해당 함수에 이벤트를 넣어서 호출하는 방법이 있다.
하나의 콜백을 감싼다.
==> arrow function은 this가 유지되기 때문!

this.field.addEventListener('click', (e) => this.onClick(e));

세 번째 방법

class 안에 있는 어떤 함수를 다른 콜백으로 전달 할 때는 해당 함수를 변수로 만들고 그 멤버변수는 arrow function을 가리키는 형태로 작성한다.
이 방법을 사용하면 this 바인딩이 자동으로 적용된다.

onClick = (e) => {
...
}

정리

this는 어떤 클래스안에 있는 함수를 다른 콜백으로 전달 할 때는 그 함수가 포함되어져 있는 클래스의 정보가 사라진다!

그렇기때문에 클래스와 해당 함수를 묶을 수 있는, this와 함수를 묶을 수 있는 바인딩 처리를 해줘야 된다.
생성자 함수 안에서 첫 번째 방법과 두 번째 방법을 사용해서 처리하거나 세 번째 방법처럼 arrow function을 사용하면 this, 즉 클래스를 가리키는 정보를 가져가기 때문에 세 번째 방법을 사용할 수 있다.

🤔   나는 세 번째 방법을 사용하는 것이 좋은데 이 방법에 대해 더 자세하게 공부하고 각각 저 방법들을 어느 상황에서 사용해야되는지 더 정확하게 알아보고 사용해야 될 것 같다.


🤗   매우 중요!!!

this: 어떤 클래스 안에 있는 함수를 다른 콜백으로 전달할 때는 그 함수가 포함되어져 있는 클래스 정보가 사라진다. 따라서 클래스와 함수를 묶을 수 있는 바인딩 처리를 해줘야 된다. 또는 arrow function을 사용한다.


Builder Pattern 사용하기

파일 하나에 만들어놨던 함수들을 다시 기능별로, 리액트의 컴포넌트들처럼 분리시키는 작업을 하고있는데 너무 어렵다!
하나의 파일에 여러 기능들을 만들 때 나름 기능별로 함수를 잘 정리했다 생각했는데 그걸 또 나누고 객체를 생성하면서 다른 객체의 메서드를 가져와서 사용하려니까 생각보다 복잡했다.

메인 자바스크립트 파일에서 게임 객체를 생성하면서 게임 진행시간, 당근, 벌레의 개수를 처음 지정해주는데 내가 게임 클래스를 만들어놓고도 new 키워드 안에 값 3개를 넣을 때 첫 번째에 뭐 넣어야 됐었지? 생각했다. 순간 기억이 안 났다.


Builder Pattern이란?

Object를 만들 때 Builder Pattern을 이용해서 오브젝트를 간단, 명료, 가독성 좋게 만들 수 있다.


생성자에 인자가 3개이상 넘어가는 경우를 생각해서 코드 수정하기

기존 코드는 main.js에서 생성자에 값 3개를 넣어서 게임을 만들었는데 외부에서 game에 접근하지 못하도록 설정 할 것이다.

빌더 패턴을 이용해서 외부에서 전달한 값을 받아서 값을 할당하고 그 값을 리턴해서 객체를 생성할 때 해당 값들을 담아 넘기는 형태로 코드를 변경하였다.


Object.freeze()

로직을 처리할 때 (당근, 벌레, 게임의 결과들을) 문자열로 만들어놨기 때문에 오타가 발생하거나 예상치 못했던 문자열이 들어올 수 있기 때문에 Object.freeze( )를 사용해서 객체를 동결화시켜서 코드를 변경하였다.


Object.freeze( ) 메서드는 객체를 말그대로 동결한다.

  • 동결된 객체는 새로운 속성을 추가하거나 존재하는 속성을 제거하는 것을 방지한다.
  • 존재하는 속성의 불변성, 설정 가능성(configurability), 작성 가능한 것, 변경되는 것을 방지한다.
  • 존재하는 속성의 값이 변경되는 것을 방지한다.
  • 동결 객체는 프로토타입이 변경되는 것도 방지한다.
  • freeze( ) 는 전달된 동일한 객체를 반환한다.

위의 개념을 이용해서 각 클래스에서 문자열로 사용되던 값들을 변경해서 코드를 조금 더 안전하게 깔끔하게 수정하였다.


정리 - class로 파일 분리

  • main.js에서는 게임을 만들고 배너와 연결시키는 코드 작성
  • sound.js에서는 application에서 사용되는 모든 소리들을 정리
  • popup.js에는 popup에 관련된 것들
  • game.js에는 게임과 관련된 것들
  • field.js에는 당근, 벌레를 정리
profile
🌿 주니어 프론트엔드 개발자입니다! 부족하거나 잘못된 정보가 있다면 알려주세요:)

0개의 댓글