안녕 ES6. (2/4) 클래스와 모듈

클래스

ES5에서 “class”는 예약어이지만 “class”라는 키워드는 아무것도 하지 않았다. 자바스크립트는 여타의 객체지향 언어들과 달리 프로토타입 체인을 이용해 클래스 인스턴스를 만들수 있도록 했다. 이런 기괴한 문법탓에 수많은 class관련 패키지들이 많이 나오기도 했다. 그래서인지, ES6에는 이러한 요구가 받아들여져 다른 OOP들 처럼 자바스크립트에서도 클래스를 생성하고 생성하는것이 매우 쉬워졌다.

confuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'use strict';
class User {
constructor(name = 'Bob') {
this._name = name;
}
say() {
return 'My name is ' + this._name;
}
}
class Admin extends User {
say() {
return 'Your name is ' + super.say();
}
}
var user = new User();
console.log(user.say()); // My name is Bob
var adm = new Admin('Alice');
console.log(adm.say()); // You name is Alice

이것을 ES5로 구현한다면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
'use strict';
function User(name){
this._name = name || 'Bob';
}
User.prototype = Object.create(null, {
constructor: {
value: User
},
say: {
value: function() {
return 'My name is ' + this._name;
}
}
});
function Admin(name) {
User.apply(this, arguments);
}
Admin.prototype = Object.create(User.prototype, {
constructor: {
value: Admin
},
say: {
value: function() {
var superClassPrototype = Object.getPrototypeOf(this.constructor.prototype);
return 'Your name is ' + superClassPrototype.say.call(this);
}
}
});
var user = new User('Alice');
console.log(user.say()); // My name is Alice
var admin = new Admin('Bob');
console.log(admin.say()); // Your name is Bob

이것을 보면 ES6에서 클래스가 얼마나 사용하기 편리해졌는가를 느낄수 있을것이다. 더이상, 함수를 속성과 마챦가지로 “:”구분자로 선언할 필요가 없다.

super와 static

super는 부모클래스를 참조하기 위한 키워드이다. Java에서 super와 같은 기능을 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'use strict'; // must be in strict mode
class myClass {
constructor() {
}
method1() {
}
method2() {
}
static returnPredefinedVal() {
return 42;
}
}
// create a child class
class myChildClass extends myClass {
constructor() {
super.method1();
}
}
myClass.returnPredefinedVal();

myChildClass는 myClass를 상속하고, myClass에서 선언한 method1() 함수를 바로 호출해서 사용할 수 있다. 물론, myChildClass의 인스턴스는 myClass를 상속하므로 myClass의 method1()을 오버라이드할수도 있고, 상속받아 사용할 수도 있다.
그리고, returnPredefinedVal() 처럼 정적으로 선언된 함수는 인스턴스를 생성하지 않고 클래스에 바로 접근할 수도 있다. 물론, static 함수는 인스턴스 속성이나 메소드에 접근할 수 없다.

setter와 getter

인스턴스의 속성값에 접근하는 방법으로 setter와 getter가 추가되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class myClass {
constructor() {
this.value = null;
}
set setValue(value) {
this.value = value + ' from myClass';
}
get getValue() {
return this.value + ' @datetime ...';
}
}
// same like above
var myInstance = new myClass();
myInstance.setValue = 'foo';
myInstance.getValue; // foo @datetime ...

일반적으로, “myClass.value = ‘bar’;” 와 같이 인스턴스 변수에 바로 접근할 수도 있지만 set/get 메소드는 전달하는값 이외의 처리도 함께할 수 있다. 주의할 점은 “myInstance.setValue(value)” 가 아니라 “myInstance.setValue = value” 와 같이 값을 설정하는것 처럼 보여야 하기 때문이다.

모듈

ES5에서는 표준적인 모듈지원이 되지 않았기 때문에 AMD, RequireJS, CommonJS등의 써드파티들이 난무했었다. 일반적으로, NodeJS에서는 CommonJS스타일의 문법이 통용되었기 때문에 브라우저에서도 Browerify가 대세가 되는 추세였다. 역시, 코드를 예로 들어보겠다.

1
2
3
4
5
6
7
// module.js
module.exports = {
port: 3000,
getAccounts: function() {
...
}
}

이경우, ES5에서는 require(‘module’) 을 사용해 의존성을 가져올 수 있었다

1
2
var service = require('module')
console.log(service.port) // 3000

ES6에서는 export와 import를 사용한다.

1
2
3
4
5
// module.js
export var port = 3000
export function getAccounts() {
...
}

ES6에서는 모듈을 import할때 좀 더 편리한 선택지를 제공한다.

1
2
import {port, getAccounts} from 'module'
console.log(port)

위와 같이 필요한 속성만 가져와 모듈 접두어를 생략하고 사용할 수 있다. 또는, 아래와 같이도 사용할 수 있다.

1
2
import * as service from 'module'
console.log(service.port) // 3000

단점이라면, 아직 브라우저들이 ES6 모듈을 지원하지 않는다는 점이다. 그래서, 사실상 브라우저에서 ES6의 모듈기능을 사용하기 위해서는 jspm과 같은 모듈로더를 사용하는편이 좋다.