Menu

자바스크립트 상속: extends, super, 메서드 오버라이드

자바스크립트 클래스에서 상속이 어떻게 동작하는지 알아봅니다. extends와 super, 메서드 오버라이드, 그리고 상속 대신 컴포지션을 써야 할 때까지 정리했습니다.

기존 클래스를 확장해 새 클래스 만들기

자바스크립트 상속은 이미 만들어 둔 클래스의 설계도를 그대로 물려받아 새로운 클래스로 확장하는 기능이다. 부모 클래스가 가진 모든 필드와 메서드를 그대로 가져다 쓸 수 있고, 필요한 부분만 추가하거나 바꿔 쓰면 된다:

index.js
Output
Click Run to see the output here.

Dog extends Animal은 "Dog는 Animal이고, 거기에 뭔가 조금 더 있다"는 뜻입니다. rex에는 자체적으로 speak 메서드가 없지만, 조회가 Animal까지 타고 올라가서 거기 있는 메서드를 찾아냅니다. 이렇게 타고 올라가는 동작이 상속의 핵심이에요. 결국 프로토타입 체인에 좀 더 예쁜 문법을 씌운 것뿐입니다.

생성자에서 super 사용법

자식 클래스에 자체 생성자를 두면 반드시 지켜야 할 규칙이 하나 있습니다. this를 건드리기 전에 먼저 super(...)를 호출해야 한다는 점이에요. super는 부모의 생성자를 실행하고, 바로 그 시점에 객체가 만들어지고 초기화됩니다:

index.js
Output
Click Run to see the output here.

super(name) 줄을 빼먹으면 this를 읽거나 쓰려는 순간 바로 ReferenceError가 터집니다. 부모 생성자가 먼저 실행되기 전까지는 엔진이 this를 내주지 않거든요.

자식 클래스에서 따로 생성자를 선언하지 않으면, JavaScript가 모든 인자를 super로 그대로 넘기는 기본 생성자를 자동으로 만들어 줍니다. 그래서 필드를 추가하거나 초기화 로직을 덧붙일 때만 생성자를 직접 작성하면 됩니다.

자바스크립트 메서드 오버라이드

자식 클래스는 상속받은 어떤 메서드든 새로 정의할 수 있습니다. 이때 프로토타입 체인에서 가장 가까운 메서드가 우선권을 가져요:

index.js
Output
Click Run to see the output here.

마법 같은 건 없습니다. Dog 인스턴스에서 speak()를 호출하면, 엔진은 먼저 인스턴스 자체에서 speak를 찾고, 그다음 Dog.prototype을 뒤져서 메서드를 찾자마자 멈춥니다. Animal.prototype까지는 가지도 않죠.

덮어쓰지 말고 확장하기: super.method()

부모의 메서드를 완전히 교체하고 싶지 않을 때도 있습니다. 오히려 기존 동작에 뭔가를 덧붙이고 싶을 때가 많죠. 이럴 때 오버라이드한 메서드 안에서 super.method(...)를 호출하면 부모 버전을 그대로 실행할 수 있습니다.

index.js
Output
Click Run to see the output here.

여기서부터 상속이 진가를 발휘합니다. 서브클래스는 부모의 로직을 복사하지 않고 그대로 재사용할 수 있죠. 나중에 Animal.describe가 바뀌면 Dog.describe도 자동으로 그 변경사항을 물려받습니다.

super는 생성자뿐 아니라 어떤 메서드 안에서도 쓸 수 있습니다. 항상 호출 대상의 부모 클래스 버전을 가리키죠.

instanceof와 프로토타입 체인

instanceof는 객체의 프로토타입 체인에 특정 클래스가 포함되어 있는지를 확인합니다. 모든 서브클래스 인스턴스는 부모 클래스의 인스턴스이기도 합니다:

index.js
Output
Click Run to see the output here.

네 개 모두 true를 반환합니다. 상속 체인이 Puppy -> Dog -> Animal -> Object 순으로 이어지고, instanceof는 이 체인을 따라 올라가면서 확인하기 때문이죠. 타입 체크에 유용하긴 하지만, 실무에서는 생각보다 자주 쓰지 않습니다. 대부분의 코드는 그냥 메서드를 호출하고 나머지는 다형성에 맡기거든요.

조금 더 실전적인 예제

자주 보게 되는 패턴이 있습니다. 공통 로직을 담은 부모 클래스를 두고, 이를 특화시킨 여러 자식 클래스를 만드는 방식이죠.

index.js
Output
Click Run to see the output here.

describe 메서드는 Shape에만 정의되어 있고 서브클래스에서 다시 작성할 필요가 없습니다. 내부에서 this.area()를 호출하면 런타임에 실제 객체에 맞는 서브클래스 메서드로 연결되기 때문이죠. 이게 바로 다형성(polymorphism)입니다. 호출하는 코드는 같지만, 실제 객체가 무엇이냐에 따라 동작이 달라지는 것.

상속 vs 컴포지션, 언제 뭘 써야 할까

자바스크립트 상속은 한 줄만 써도 부모의 메서드를 통째로 가져올 수 있어서 처음엔 꽤 생산적으로 느껴집니다. 하지만 계층이 깊어질수록 점점 관리가 어려워져요.

기준은 이렇게 잡으면 됩니다. "X는 Y다"라는 관계가 명확하고, 자식 클래스가 부모의 동작 대부분을 실제로 공유한다면 extends를 쓰세요. 반대로 헬퍼 메서드 한두 개를 공유하려고 상속을 꺼내들고 있다면, 컴포지션이 더 낫습니다. 즉, 헬퍼 객체를 필드로 들고 있게 만드는 방식이죠:

index.js
Output
Click Run to see the output here.

상속 트리가 Animal -> Mammal -> Dog -> WorkingDog -> PoliceDog처럼 깊어지면 다이어그램에서는 깔끔해 보여도 실제 코드에서는 골치가 아파집니다. 루트 근처에서 뭔가 하나만 바꿔도 그 변경이 모든 하위 클래스로 예측 불가능하게 번져가거든요. 건강한 코드베이스는 대부분 상속을 한두 단계 정도로 얕게 유지하고, 나머지는 컴포지션으로 해결합니다.

다음 주제: 정적 멤버(static)

지금까지 이 문서에서 다룬 건 전부 인스턴스에 속한 것들입니다. new Thing().something() 형태로 호출하는 메서드들이죠. 그런데 특정 인스턴스가 아니라 클래스 자체에 속한 메서드나 데이터가 필요할 때가 있습니다. 이럴 때 쓰는 게 바로 static인데, 다음 장에서 바로 다뤄보겠습니다.

자주 묻는 질문

자바스크립트에서 상속은 어떻게 동작하나요?

extends 키워드로 한 클래스가 다른 클래스를 상속할 수 있습니다. 자식 클래스는 부모의 모든 메서드와 필드를 물려받고, 거기에 새로운 걸 추가하거나 기존 동작을 오버라이드할 수 있죠. 내부적으로는 자바스크립트가 자식 클래스의 prototype을 부모의 prototype에 연결해 두기 때문에, 메서드를 찾을 때 프로토타입 체인을 따라 자연스럽게 부모까지 올라가면서 탐색이 이뤄집니다.

super는 어떤 역할을 하나요?

super(...)는 부모 클래스의 생성자를 호출합니다. 자식 클래스 생성자 안에서 this를 쓰기 전에 반드시 먼저 호출해야 합니다. 한편 super.method(...) 형태는 부모 쪽 메서드를 호출하는 방식인데, 자식에서 기존 동작을 완전히 갈아엎지 않고 확장하고 싶을 때 이렇게 씁니다.

상속과 컴포지션 중 뭘 써야 할까요?

진짜로 '~은 의 일종이다(is-a)' 관계가 성립하고, 자식이 부모 동작 대부분을 그대로 쓰는 경우라면 상속이 맞습니다. 그냥 기능만 재사용하고 싶은 거라면 컴포지션, 즉 객체가 다른 객체를 들고 있는 구조를 쓰는 게 낫습니다. 깊은 클래스 계층은 시간이 지날수록 유지보수가 어려워지는 편이라, 실제 현업 코드베이스에서도 상속은 보통 12단계 정도에서 끝납니다.

Coddy로 코딩 배우기

시작하기