클래스와 모듈
클래스 (constructor, extends, super, static, getter/setter)
클래스는 객체를 만들기 위한 설계도다. ES6 이전에는 함수와 프로토타입으로 객체지향 패턴을 구현했는데, 클래스 문법이 도입되면서 훨씬 직관적으로 쓸 수 있게 됐다. (내부적으로는 여전히 프로토타입 기반으로 동작한다고 한다)
constructor — 클래스의 생성자
new 키워드로 클래스의 인스턴스를 만들 때 자동으로 호출되는 메서드다. 인스턴스의 초기 상태를 설정한다.
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`안녕하세요, ${this.name}입니다`);
}
}
const gori = new User("gori", 30);
gori.greet(); // 안녕하세요, gori입니다
console.log(gori.age); // 30메서드는 프로토타입에 공유된다
클래스 안에서 정의한 메서드는 인스턴스마다 새로 생성되는 게 아니라, 프로토타입에 하나만 만들어지고 모든 인스턴스가 공유해서 사용한다. 그래서 메모리 효율이 좋다.
const gori2 = new User("gori2", 25);
console.log(gori.greet === gori2.greet); // true (같은 메서드를 공유)getter / setter
속성처럼 접근하지만 실제로는 메서드가 실행되도록 만드는 문법이다. 값을 가져오거나(get) 설정할 때(set) 추가 로직을 끼워 넣을 수 있다.
class User {
constructor(name, age) {
this.name = name;
this._age = age; // 외부에서 직접 손대지 않길 바라는 값은 관례상 _를 붙임
}
get age() {
return this._age;
}
set age(value) {
if (value < 0) {
console.log("나이는 음수가 될 수 없습니다");
return;
}
this._age = value;
}
}
const gori = new User("gori", 20);
console.log(gori.age); // 20 (함수 호출이 아니라 속성처럼 접근)
gori.age = -5; // "나이는 음수가 될 수 없습니다"
gori.age = 21;
console.log(gori.age); // 21static — 클래스 자체에 속하는 메서드/속성
인스턴스가 아니라 클래스 자체에서 바로 호출하는 메서드나 속성을 정의할 때 사용한다. 인스턴스를 만들 필요 없는 유틸리티성 기능에 주로 쓰인다.
class MathUtil {
static square(x) {
return x * x;
}
}
console.log(MathUtil.square(4)); // 16
// new MathUtil() 같은 인스턴스 생성 없이 바로 호출 가능extends / super — 상속
extends로 기존 클래스를 상속받아 새로운 클래스를 만들 수 있다. super는 부모 클래스의 constructor나 메서드를 호출할 때 사용한다.
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log(`안녕하세요, ${this.name}입니다`);
}
}
class Admin extends User {
constructor(name, level) {
super(name); // 부모 클래스의 constructor 호출 (this.name = name)
this.level = level;
}
greet() {
super.greet(); // 부모의 greet 메서드도 실행
console.log(`관리자 등급: ${this.level}`);
}
}
const gori = new Admin("gori", "최고관리자");
gori.greet();
// 안녕하세요, gori입니다
// 관리자 등급: 최고관리자super()는 반드시 자식 클래스의 constructor 안에서, this를 사용하기 전에 호출해야 한다.
모듈 시스템 (import / export)
모듈은 코드를 파일 단위로 분리해서 관리할 수 있게 해주는 시스템이다. 기능별로 파일을 나누고, 필요한 부분만 가져다 쓸 수 있어서 코드 관리가 훨씬 수월해진다.
named export / import — 여러 개를 이름 붙여 내보내기
// user.js
export const name = "gori";
export function greet(name) {
console.log(`안녕하세요, ${name}님`);
}
export class User {
constructor(name) {
this.name = name;
}
}// main.js
import { name, greet, User } from "./user.js";
greet(name); // 안녕하세요, gori님
const gori = new User("gori");한 번에 모아서 export하는 방식
// user.js
const name = "gori";
function greet(name) {
console.log(`안녕하세요, ${name}님`);
}
export { name, greet };import 시 이름 바꾸기 (as)
import { greet as greetUser } from "./user.js";
greetUser("gori");default export / import — 하나만 대표로 내보내기
파일당 하나만 지정할 수 있고, import할 때 원하는 이름으로 자유롭게 받을 수 있다.
// user.js
export default class User {
constructor(name) {
this.name = name;
}
}// main.js
import User from "./user.js"; // 중괄호 없이, 이름도 자유롭게 지정 가능
const gori = new User("gori");named export와 default export를 함께 사용
// user.js
export const version = "1.0.0";
export default class User {
constructor(name) {
this.name = name;
}
}// main.js
import User, { version } from "./user.js";전체를 하나의 이름으로 가져오기
import * as userModule from "./user.js";
console.log(userModule.version);
const gori = new userModule.default("gori");모듈의 특징
- 모듈은 기본적으로 strict mode로 동작한다.
- 각 모듈은 자신만의 스코프를 가진다. 모듈 안에서 선언한 변수는 다른 파일에서 자동으로 접근할 수 없고, export한 것만 공유된다.
- 브라우저에서 모듈을 사용하려면
<script type="module">로 불러와야 한다.
<script type="module" src="main.js"></script>named export vs default export, 언제 뭘 쓸까
| 구분 | 개수 제한 | import 시 이름 | 주 사용처 |
|---|---|---|---|
| named export | 여러 개 가능 | 원래 이름 그대로 (또는 as로 변경) | 유틸 함수 모음, 여러 값을 내보낼 때 |
| default export | 파일당 1개 | 자유롭게 지정 가능 | 그 파일을 대표하는 클래스/컴포넌트 하나 |