Search
6️⃣

왜 ES6부터 클래스 개념이 추가된걸까?

Created
2023/09/07
Tags
JavaScript
Category
Knowledge
Parent item
Sub-item
2 more properties

 질문의 배경

자바스크립트 ECMAScript의 히스토리와 클래스 개념에 대해 공부하다가 문득, ES5에서도 생성자 함수와 프로토타입을 통해 충분히 객체지향 언어의 상속을 구현할 수 있는데 왜 클래스 개념을 추가했을까 궁금증이 생겼다.
궁금증에 대한 해답을 [문제 정의 - 해결]의 틀로 살펴보자.

 문제 정의: 추가되기 전의 상황

자바스크립트의 클래스 개념이 ES6(2015)에서 추가된 이유는 아래와 같다.

쓰는 사람이 어렵다는데 어떡해

ES5까지는 클래스 없이도 생성자 함수와 프로토타입을 통해 객체지향 언어의 상속을 구현할 수는 있었으나, 기존 자바나 C+과 같이 클래스 기반의 객체지향 프로그래밍에 익숙한 프로그래머들에게는 혼란을 주었다. 이는 자바스크립트를 어렵게 느끼게 하는 장벽과도 같았다.

코드 쓰기 귀찮고 유지보수 어려워

기존 객체를 상속하고 확장하기 위한 메커니즘이 비교적 복잡했기에, 코드를 작성하고 유지보수하는 것이 어려웠다.

상속도 어려운데 오류도 난다고

자바스크립트에서 클래스 개념이 등장하기 전에는 객체 생성을 위해 생성자 함수와 프로토타입을 사용해야 했다. 이는 오류를 발생시킬 가능성이 높고, 상속을 제대로 다루기 어려웠다.
이에 ES6에서는 클래스 기반 객체지향 프로그래밍 언어와 매우 흡사한 새로운 객체 생성 메커니즘을 제시했다.

문법적 설탕 (syntactic sugar)?

그렇다고 해서 클래스가 혜성처럼 등장해 기존의 프로토타입 기반의 객체지향 모델을 저리 가라! 하며 새롭게 클래스 기반 객체지향 모델을 제공한 것은 아니다. 사실 클래스는 일종의 함수다.
기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용할 수 있도록 하는, 일종의 '문법적 설탕' 같은 조미료 역할인 셈이다. 하지만 이 설명은 여러 의견이 있는데, 혹자는 클래스를 단순한 문법적 설탕이라기보다 '새로운 객체 생성의 메커니즘'이라고 말한다. 그냥 참고 정도만 해두자.

기존 생성자 함수랑 다른 점

클래스와 생성자 함수 모두 프로토타입 기반의 인스턴스를 생성하지만, (당연히) 정확히 동일하게 동작하지는 않는다. 몇 가지 차이가 있는데, 요약하자면 클래스가 생성자 함수보다 엄격하고, 생성자 함수에서는 제공하지 않는 추가 기능도 제공한다.
차이점은 아래와 같다.

new 연산자

생성자 함수: new 연산자 없이 호출하면 일반 함수로서 호출된다.
클래스: new 연산자 없이 호출하면 에러가 발생한다. (나쁜 게 아님)

extends, super

생성자 함수: 상속을 지원하는 extends와 super 키워드를 지원하지 않는다.
클래스: 상속을 지원하는 extends와 super 키워드를 지원한다.

호이스팅

생성자 함수: 선언문으로 정의된 생성자 함수는 함수 호이스팅이, 표현식으로 정의된 생성자 함수는 변수 호이스팅이 발생한다.
클래스: 호이스팅이 발생하지 않는 것처럼 동작한다.

strict mode

생성자 함수: 암묵적으로 strict mode가 지원되지 않는다.
클래스: 클래스 내의 모든 코드는 암묵적으로 strict mode가 지정 및 실행되며, 해제할 수 없다.
생성자 함수와 클래스는 프로토타입 기반의 객체지향을 구현했다는 점에서는 매우 유사하지만, 클래스는 생성자 함수 기반의 객체 생성 방식보다 견고하고 명료한 편이다.
특히 3. extends와 super 키워드는 기존에 어려웠던 상속 관계 구현을 더욱 간결하고도 명료하게 해 준다.

 해결: ES6에서 클래스의 추가와 효과

ES6에서 클래스를 도입하여 자바스크립트의 객체 지향 프로그래밍을 더욱 간편하게 만들었다고 하는데, 아래의 코드로 먼저 그 효과를 체감해 보자.

클래스 사용 전

// ES5 생성자 함수 function Person(name) { this.name = name; } // 프로토타입 메소드 Person.prototype.sayHello = function() { console.log(`Hello, my name is ${this.name}`); }; // 인스턴스 생성 const person = new Person('Ella'); // 프로토타입 메소드 호출 person.sayHello(); // "Hello, my name is Ella"
JavaScript
복사

클래스 사용 후

// ES6 클래스 선언문 class Person { // 생성자 constructor(name) { // 인스턴스 생성 및 초기화 this.name = name; } // 프로토타입 메소드 sayHello() { console.log(`Hello, my name is ${this.name}`); } } // 인스턴스 생성 const person = new Person('Ella'); // 프로토타입 메소드 호출 person.sayHello(); // "Hello, my name is Ella"
JavaScript
복사
책 <모던 자바스크립트 Deep Dive>에서도 차이점을 한눈에 보기 쉽게 설명해 두어 가져왔다.
출처: 책 <모던 자바스크립트 Deep Dive>

 요약하자면, 이런 게 좋아졌다

클래스 사용으로 인한 효과를 다시 한번 글로 요약해 보자면, 아래와 같다.

가독성 개선

클래스를 사용해 코드가 더 읽기 쉽고 이해하기 쉬워졌다. 딱 봐도 객체 생성과 메소드 정의가 더 간결해졌다.

상속 지원

상속을 더 쉽게 다룰 수 있게 되었다. 'extends'를 사용해 다른 클래스를 확장할 수 있고, 'super'를 사용해 부모 클래스의 메소드를 상속받고 호출할 수 있다.

정적 메소드

클래스 내부에 'static'을 사용해 정적 메소드를 정의할 수 있으며, 이는 클래스 레벨에서 호출할 수 있는 유용한 기능을 제공한다. 참고로 정적 메서드(Static methods)는 클래스의 인스턴스가 아닌 클래스 자체에 속하는 메서드이다. 따라서 클래스의 인스턴스를 따로 생성하지 않으면서 호출이 가능하고, 클래스의 상태를 조작하거나 조회하면서 클래스 내부의 데이터를 공유하는 데에 활용될 수 있다.

참고 자료

책 <모던 자바스크립트 Deep Dive>