자바스크립트 - 클래스 되짚어보기
들어가기 전
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