들어가기 전

4주간의 프리코스 과정이 끝나고 그동안 내가 작성했던 코드와 다른 분들의 코드를 비교해보는 시간을 가졌다. 프리코스 기간에는 아무래도 시간 관계상 코드 맞리뷰(?)를 통해서 일부의 코드만 보다가 프리코스가 종료된 후 5일간 약 100여 분의 코드를 살펴봤다.

아무래도 다양한 사람이 작성한 코드이다보니 처음보는 문법도 많았고 ‘아~ 맞아 저런게 있었지~‘하는 기능도 찾을 수 있었다. 그 중에서 클래스에 정말 다양한 기능이 있는 것을 확인할 수 있었고, 궁금해졌다.

예전에는 너무 방대한 양이고 내용이 어려워서 건너뛰었던 자바스크립트 Info 사이트에 정리된 내용을 공부해보는 시간을 가졌다.

내가 사용한 클래스의 용도는 무엇이었나

지금까지 나는 클래스를 어떤 용도로 사용했나?

프리코스에 있는 문제를 풀 때, 하나의 공통적인 역할을 하는 클래스를 생성하고 그 아래 관련 기능을 하는 메서드를 포함시켰다. 그리고 컨트롤러에서 관련 메서드를 클래스를 통해서 꺼내는 일이 전부였다.

🖍️ extends

정말 매력적인 키워드이다. 클래스를 상속받아 기존에 존재하는 기능을 사용할 수도 있고, 새로운 기능을 추가해 클래스를 확장할 수 있다.

class VisitDate {
  #date;

  constructor(date) {
    this.#date = Number(date);
  }

  processVisitDate() {
    return this.#date;
  }
}

위 코드는 프리코스 4주차 미션에서 작성한 VisitDate 클래스 일부이다. 실제 코드에서는 Event 클래스에서 VisitDate 클래스의 기능을 일부 사용하였다. 그 과정을 extends 키워드를 접목 시켜 한번 사용해보고자 한다.

class Event extends VisitDate {
  dDayHandler() {
    return this.#date.processVisitDate() * 5;
  }
}

const date = new Event(10);
date.dDayHandler(); // 50
date.processVisitDate(); // 5

클래스 Event 을 사용해 만든 객체는 date.dDayHandler() 같은 Event 에 정의된 메서드에도 접근할 수 있고, date.processVisitDate() 같은 VisitDate 에 정의된 메서드에도 접근할 수 있다.

키워드 extends 는 프로토타입을 기반으로 동작한다. 때문에 Event.prototype 에서 메서드를 찾지 못하면 VisitDate.prototype 에서 메서드를 가져온다.

🖍️ 메서드 오버라이딩

class Event extends VisitDate {
  processVisitDate() {
    return this.#date;
  }
}

자, 이번에는 위와 같은 코드가 있다고 가정을 하자. 만약 Event 클래스에서 자체적으로 processVisitDate() 라는 메서드를 제작한다면 상속받은 메서드가 아닌 자체 메서드가 사용된다. 위에서도 설명했지만 실행 순서는 자체 메서드 -> 상속받은 메서드이기 때문이다.

그렇다면 상속받은 클래스의 메서드를 사용하고 싶다면 어떻게 해야할까? 이럴 때에는 super 키워드를 사용하면 된다.

class Event extends VisitDate {
  dDayHandler() {
    return this.#date.processVisitDate() * 5;
  }

  processVisitDate() {
    super.processVisitDate(); // 부모 클래스에서 값을 받아오고
    this.dDayHandler(); // Event 메서드를 호출한다
  }
}

🖍️ 생성자 오버라이딩

생성자는 보통 부모 constructor 을 호출한다. 이때 부모 constructor 에도 인수를 모두 전달하게 된다.

Event 에 커스텀 생성자를 추가하고, 커스텀 생성자에서 date와 eventList를 지정한다.

class Event extends VisitDate{
  constructor(date, event){
    this.date = date;
    this.event = event;
  }
  ...
}
// Reference Error

하지만 오류가 발생하였다. 무엇이 잘못되었을까?

상속 클래스의 생성자에서는 반드시 super을 호출하여야 한다. super는 this를 사용하기 전에 호출되어야만 한다.

일반 클래스의 생성자 함수와 상속 클래스의 생성자 함수 간 차이는 new와 함께 드러난다.

일반 클래스가 new와 함께 실행되면, 빈 객체가 만들어지고 this에 이 객체를 할당한다. 반면, 상속 클래스의 생성자 함수가 실행되면, 일반 클래스에서 일어난 일이 일어나지 않는다. 상속 클래스의 생성자 함수는 빈 객체를 만들고 this에 이 객체를 할당하는 일을 부모 클래스의 생성자가 처리해주길 기대한다.

이런 차이 때문에 상속 클래스의 생성자에선 super를 호출해 부모 생성자를 실행해 주어야 한다. 그렇지 않으면 this가 될 객체가 만들어지지 않아 에러가 발생한다.

class Event extends VisitDate{
  constructor(date, event){
    super(date);
    this.event = event;
  }
  ...
}

// 정상적으로 작동한다

🖍️ Static… 다들 왜 사용했을까?

프리코스 2주차 미션 코드 피어 리뷰에서 내 기준으로 코드를 잘(?) 작성하였다고 생각한 분들이 거의 Static 을 사용하였다. private 필드만으로 충분히 캡슐화된 코드를 작성할 수 있겠다고 생각하였기 때문에 더욱 궁금했다.

2주차 회고때에는 나의 코드 스타일과 들어맞지 않겠다 생각하여 사용하지 않았지만 이번 기회에 한번 사용해보며 내 코드와 비교해보고 싶었다.

Static을 사용하는 이유

한마디로 요약하자면 Static복제가 필요없는 데이터를 다룰 때 효과적이다.

별도의 인스턴스 생성없이 클래스의 메서드를 바로 실행할 수 있다는 장점이 있다. 내가 기존에 사용했던 방식은 별도로 인스턴스를 생성하기 때문에 아무래도 메모리 낭비도 심하고 코드도 길어지는 단점이 있다.

class Event extends VisitDate {
  constructor(date, event) {
    super(date);
    this.event = event;
  }

  static getEvent() {
    return this.event;
  }
}

Event.getEvent(); // 유효한 값
const event = new Event();
event.getEvent(); // Reference Error

Reference

클래스 상속(Javascript Info) > 정적 메서드와 정적 프로퍼티(Javascript Info)