Menu

자바스크립트 class 완벽 정리: 생성자, 메서드, new 키워드

자바스크립트 클래스가 실제로 어떻게 동작하는지 정리했습니다. class 키워드의 멘탈 모델부터 생성자, 메서드, 인스턴스 필드, getter/setter까지 한 번에 살펴봅니다.

클래스는 객체를 찍어내는 설계도

자바스크립트에서 class는 어떤 종류의 객체가 가질 모양과 동작을 정의하는 설계도라고 보면 됩니다. 설계도를 한 번 작성해 두면, 필요한 만큼 인스턴스를 찍어낼 수 있습니다. 각 인스턴스는 자기만의 데이터를 가지지만, 메서드는 모두 공유합니다.

ES6 클래스 문법의 기본 형태는 다음과 같습니다:

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

class User { ... }는 일종의 설계도를 정의하고, new User(...)는 그 설계도를 바탕으로 인스턴스를 찍어냅니다. 각 인스턴스는 자기만의 nameemail을 갖지만, greet 메서드는 클래스에 하나만 정의되어 있고 모두가 이를 공유합니다.

인스턴스는 두 개, 데이터도 따로따로, 하지만 메서드는 한 벌. 이게 클래스의 핵심입니다.

constructor로 각 인스턴스 초기화하기

constructornew로 인스턴스를 생성할 때 딱 한 번 자동으로 실행되는 특별한 메서드입니다. 갓 만들어진 객체를 초기 상태로 세팅하는 역할을 하는데, 보통은 전달받은 인자를 this에 그대로 담아두는 식으로 쓰입니다.

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

생성자 안의 this는 지금 막 만들어지고 있는 새 인스턴스를 가리킵니다. this.x = x라고 쓰면 바로 그 객체에 x가 심어지죠. new Point(...)를 호출할 때마다 각자 다른 this가 생기기 때문에, 인스턴스마다 고유한 xy를 갖게 됩니다.

생성자를 따로 작성하지 않으면 자바스크립트가 빈 생성자를 알아서 만들어 줍니다. 그러니까 초기화할 게 실제로 있을 때만 직접 써 주면 됩니다.

new 키워드가 있어야 클래스가 동작한다

new 없이 클래스를 호출하면 에러가 납니다:

const p = Point(3, 4);
// TypeError: Class constructor Point cannot be invoked without 'new'

이건 의도된 동작입니다. new 키워드는 순서대로 다음 네 가지 일을 합니다.

  1. 비어 있는 새 객체를 만듭니다.
  2. 이 객체를 클래스의 프로토타입에 연결합니다(메서드가 살고 있는 곳이죠).
  3. this를 새 객체에 바인딩한 상태로 constructor를 실행합니다.
  4. 만들어진 객체를 반환합니다.

new 없이 호출하면 이 과정 중 어느 것도 일어나지 않습니다. 클래스는 이 규칙을 강제하기 때문에 실수로 빠뜨릴 일이 없습니다. class 문법이 나오기 전에는 new를 깜빡하면 조용히 전역 객체를 오염시키곤 했는데, 그에 비하면 확실히 나아진 부분이죠.

메서드는 공유, 필드는 인스턴스별로

클래스 본문에 정의한 메서드는 프로토타입 위에 올라갑니다. 즉, 모든 인스턴스가 하나의 복사본을 함께 씁니다. 반면 인스턴스 필드(즉, this에 대입하는 값)는 인스턴스마다 따로따로, 매번 새로운 복사본이 만들어집니다.

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

인스턴스마다 각자 count를 가지고 있으니 한쪽을 증가시켜도 다른 쪽에는 영향이 없습니다. 그런데 a.incrementb.increment는 사실 완전히 똑같은 함수예요. 프로토타입에 딱 한 번만 저장되고, 인스턴스에서 호출할 때마다 그 함수를 찾아 쓰는 구조죠. 자바스크립트 class가 가볍게 확장될 수 있는 이유가 바로 여기에 있습니다. 인스턴스를 천 개 만들어도 메서드가 천 개로 복사되지는 않거든요.

인스턴스 필드 (Class Fields)

생성자 안이 아니라 클래스 본문 맨 위에서 인스턴스 필드를 바로 선언할 수도 있습니다:

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

필드 선언은 마치 constructor 최상단에 적어둔 것처럼 생성자 본문보다 먼저 실행됩니다. 초깃값이 생성자 인자에 의존하지 않을 때 유용한데, 생성자 안에 this.count = 0; this.step = 1; 같은 코드를 줄줄이 늘어놓는 것보다 훨씬 깔끔하죠.

반대로 값이 인자에 의존한다면, 인자를 쓸 수 있는 생성자 안에 할당 코드를 두는 게 맞습니다.

자바스크립트 getter와 setter

getter와 setter는 겉보기엔 메서드 같지만 동작은 프로퍼티 접근처럼 이뤄집니다. 괄호 없이 값을 읽거나 할당하면, 그 뒤에서 메서드가 알아서 실행되는 방식이죠:

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

호출하는 쪽을 잘 보세요. t.fahrenheit(괄호 없음)는 getter를 읽고, t.fahrenheit = 100은 setter를 실행합니다. 바깥에서 보면 fahrenheit는 평범한 프로퍼티처럼 보이지만, 실제로는 celsius로부터 그때그때 계산되는 값입니다.

getter는 파생값을 다루기에 안성맞춤이고, setter는 값을 할당할 때 검증하거나 정규화하는 용도로 유용합니다. 다만 남용은 금물입니다. 비용이 큰 getter라면 "그냥 프로퍼티 읽는 줄 알았는데 뭔가 무거운 작업을 하네?" 하고 읽는 사람이 당황할 수 있거든요.

메서드 단축 문법, 계산된 이름, 그리고 this

클래스 안에서 메서드를 정의할 땐 단축 문법을 씁니다. function 키워드도, 이름과 본문 사이의 :도 필요 없습니다.

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

add에서 this를 반환하면 메서드 체이닝이 가능해집니다. 각 호출이 같은 인스턴스를 다시 돌려주니까, 바로 이어서 다음 메서드를 호출할 수 있는 거죠.

한 가지 주의할 점이 있습니다. 클래스 메서드는 인스턴스에 자동으로 바인딩되지 않는다는 사실인데요. 메서드를 떼어내서 단독으로 호출하면 this가 사라져 버립니다:

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

g.hello()처럼 호출하면 . 앞의 객체가 this로 자동 바인딩됩니다. 하지만 fn()처럼 따로 떼어내서 호출하면 this가 사라지죠. 이벤트 핸들러처럼 메서드를 분리해서 넘겨야 할 때는, constructor 안에서 bind로 묶어두거나 화살표 함수 형태의 클래스 필드로 선언하는 방법이 있습니다: hello = () => \Hi, ${this.name}`;`.

자바스크립트 class 전체 예제

지금까지 살펴본 필드, constructor, 메서드, getter를 한데 모아 보겠습니다:

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

한눈에 읽힌다는 게 포인트다. BankAccount가 무엇이고 무엇을 할 수 있는지 클래스가 그 자체로 설명해 준다.

자바스크립트 class는 사실 함수다

꼭 기억해 두면 좋은 사실: class는 사실상 문법적 설탕(syntactic sugar)에 가깝다. 내부적으로 보면 클래스는 함수, 그중에서도 생성자 함수이며, 메서드들은 ClassName.prototype에 얹혀 있다:

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

typeof User의 결과는 "function"이고, greet 메서드는 User.prototype에 올라갑니다. 인스턴스는 프로토타입 체인을 따라 greet을 찾아가는데, 이건 자바스크립트가 처음 만들어졌을 때부터 있던 동작 방식 그대로입니다. 클래스 문법은 그저 이 과정을 깔끔하게 써 내려갈 수 있게 해주는 겉옷일 뿐이죠.

이 개념을 머릿속에 잡아두면 나중에 크게 도움이 됩니다. 상속, instanceof 연산자, 프로토타입 체인 디버깅 — 이런 것들이 모두 "클래스는 결국 프로토타입에 메서드가 매달려 있는 함수다"라는 사실을 기억할 때 훨씬 이해하기 쉬워집니다.

다음 주제: 상속

지금까지 살펴본 예제에서는 클래스들이 각자 독립적으로 존재했습니다. 하지만 실제 프로젝트에서는 대부분 클래스 계층 구조를 설계하게 됩니다. 예를 들면 Animal을 상속받는 Dog, User를 상속받는 AdminUser 같은 식이죠. 이런 상속 관계는 extendssuper 키워드로 표현하는데, 바로 다음에 이어서 다뤄보겠습니다.

자주 묻는 질문

자바스크립트에서 클래스는 어떻게 만드나요?

class 키워드 뒤에 클래스 이름을 쓰고, 중괄호 안에 constructor 메서드와 필요한 메서드들을 선언하면 됩니다. 인스턴스는 new 클래스이름(...) 형태로 만듭니다. 예를 들어 class User { constructor(name) { this.name = name; } } 이렇게 정의한 뒤 new User('Ada')로 호출하면 됩니다.

클래스에서 constructor는 무슨 일을 하나요?

constructornew 클래스이름(...)이 호출될 때 딱 한 번 실행됩니다. 주로 전달받은 인자를 this에 붙여서 인스턴스 속성으로 저장하는 역할을 하죠. 직접 작성하지 않으면 자바스크립트가 비어 있는 기본 생성자를 자동으로 넣어 줍니다.

class와 function은 어떻게 다른가요?

사실 내부적으로 보면 클래스도 함수입니다. 정확히 말하면 메서드가 프로토타입에 붙어 있는 생성자 함수죠. class 키워드는 대부분 문법적 설탕(syntactic sugar)에 가깝지만, 반드시 new로 호출하도록 강제하고, extends로 상속을 깔끔하게 처리하며, 메서드를 열거 불가능(non-enumerable)하게 만들어 줍니다. 새로 작성하는 코드라면 직접 생성자 함수를 짜기보다 class를 쓰는 걸 추천합니다.

Coddy로 코딩 배우기

시작하기