드림코딩엘리님의 TypeScript 강의를 토대로 정리했습니다.
객체지향의 원칙 중 하나로 특정 키워드를 사용함으로서 정의한 클래스 외부에서 클래스 내부의 메서드나 멤버변수, 프로퍼티의 접근을 막기 위해 사용한다. 캡슐화라고도 부르며, 클래스 내부의 정보를 은닉하기 보호하기 위해 사용한다. 캡슐화를 위한 키워드는 public
, private
, protected
가 있다.
우리는 이전의 커피기계를 찍어내는 클래스를 만들고 있다. 멤버 변수로는 커피 원 샷을 만들기 위해서 필요한 양을 상수로 지정했고, 생성자 함수에 매개변수로 넣어주기 위해 우리가 가지고 있는 커피 콩의 양을 지정했다. 이 클래스 외부에서 샷을 만들기 위해 필요로 하는 커피콩의 양과 우리가 가지고 있는 커피 콩의 양을 임의로 조절한다면 위험하다. 가령 (6)의 maker.coffeeBeans = -34
같은 값을 넣는다면 말도 안되는 일이 벌어질 수 있는 것이다. 그렇기 때문에 클래스 외부에서는 접근이 불가능 하도록, (1), (2)의 코드를 private
으로 선언한 것이다.
type CoffeeCup = {
shots: number;
hasMilk: boolean;
};
class CoffeeMaker {
private static BEANS_GRAMM_PER_SHOT: number = 7; // (1)
private coffeeBeans: number = 0; // (1)
// contructor를 private로 설정 시 인스턴스를 만들어주는 static한 함수를 따로 만들어 놓아야한다.
private constructor(coffeeBeans: number) { // (2)
this.coffeeBeans = coffeeBeans;
}
static makeMachine(coffeeBeans: number): CoffeeMaker { // (3)
return new CoffeeMaker(coffeeBeans);
}
fillCoffeeBeans(beans: number) { // (4)
// 개발자가 beans를 0보다 작게 설정할 경우 error 메세지를 던져주기.
if (beans < 0) {
throw new Error('value for beans should be greater than 0');
}
this.coffeeBeans += beans;
}
makeCoffee(shots: number): CoffeeCup {
if (this.coffeeBeans < shots * CoffeeMaker.BEANS_GRAMM_PER_SHOT) {
throw new Error('Not enough coffee beans!');
}
this.coffeeBeans -= shots * CoffeeMaker.BEANS_GRAMM_PER_SHOT;
return {
shots,
hasMilk: false,
};
}
}
const maker = new CoffeeMaker(32);
const maker2 = CoffeeMaker.makeMachine(32);
maker.fillCoffeeBeans(10);
// 인스턴스의 coffeeBeans는 음수가 되면 안 된다. 커피 콩의 갯수가 음수는 말 이 안된다.
// 멤버 변수가 private으로 설정됐기 때문에 접근이 불가능하다.
--------------------------------------------------
CoffeeMaker.BEANS_GRAMM_PER_SHOT; // dangerous
maker.coffeeBeans = 3; // valid but dangerous
maker.coffeeBeans = -34; // invalid (6)
--------------------------------------------------
우리는 이제 캡슐화를 통해 클래스 내부에 있는 멤버변수를 외부로부터 안전하게 보호하고 있다. 캡슐화한 멤버 변수는 어떻게 조작을 해야할까? 클래스 내부에서 해당 멤버변수를 조작할 수 있는 메서드를 만들어주어야한다. 사용자는 오직 이 메서드를 통해서만 멤버 변수의 값을 조작할 수 있다. (4)의 코드를 살펴보자. fillCoffeeBeans()
내부에 사용자가 유효하지 않은 값을 넣었을 때 에러를 던져주는 로직을 추가해줄 수도 있다.
우리는 이전 챕터에서 static을 활용하여 클래스 외부에서 생성자 함수를 사용하지 않고 인스턴스를 생성할 수 있다는 것을 알게 됐다. (2), (3)의 코드를 보자. 생성자 함수를 private하게 만들어서 외부에서 접근할 수 없게 만들고, 클래스 내부에 makeMachine
메서드를 통해서 인스턴스를 생성할 수 있게 유도하는 것이다. 이러한 방법을 팩토리 패턴이라고 한다. 팩토리 패턴은 객체를 생성하는 코드를 분리하여 결합도(의존성)을 낮추어 확장성을 증가시키고 코드 수정을 용이하게 하기 위한 패턴이다.
현재로서는 팩토리 패턴에 경험이 부족해 잘 와닿지 않는 내용입니다. 추후에 내용 보충하겠습니다.
class User {
firstName: string;
lastName: string;
fullName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`;
}
}
const user = new User('Steve', 'Jobs');
console.log(user.fullName); // Steve Jobs
// 우리는 firstName을 다른 이름으로 바꾸고 싶다.
user.firstName = 'Hwimin';
// 하지만, 결과는 처음에 constructor에 넣은 'Steve Jobs'가 출력된다.
// constructor에 최초에 지정된 값이 그대로 출력되고 있다.
console.log(user.fullName); // Steve Jobs
class User {
firstName: string;
lastName: string;
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}
const user = new User('Steve', 'Jobs');
console.log(user.fullName); // Steve Jobs
user.firstName = 'Hwimin';
console.log(user.fullName); // Hwimin Jobs
class User {
private firstName: string;
private lastName: string;
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}
class User {
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
constructor(private firstName: string, private lastName: string) {}
}
class User {
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
private internalAge = 4;
get age(): number {
return this.internalAge;
}
set age(num: number) {
if (num < 0) {
throw new Error('age should be greater than 0');
}
this.internalAge = num;
}
constructor(private firstName: string, private lastName: string) {}
}
const user = new User('Steve', 'Jobs');
user.age = 6;
// user.age에 값을 할당할 시 setter 함수에서 포함하고 있는 로직이 실행된다.
// 만약 user.age에 0보다 작은 값을 할당하면 'age should be greater than 0' 이라는 에러가 던져진다.