자바스크립트 - 캡슐화

안녕하세요, 성장하는 프로그래머 여러분! 오늘 우리는 자바스크립트 캡슐화의 세계로 흥미로운 여정을 떠납니다. 프로그래밍에 처음 도전하는 여러분도 걱정 마세요 - 나는 여러분의 친절한 안내자가 되겠습니다. 이 개념을 단계별로 탐구해 나갈게요. 그럼 가상의 등을 챙기고, 시작해 보겠습니다!

JavaScript - Encapsulation

캡슐화는 무엇인가요?

상상해 보세요. 보물 상자가 있습니다. 누구나 열고 귀중한 보석을 가져가는 것을 원하지 않을 테죠? 프로그래밍에서 캡슐화는 바로 그런 역할을 합니다. 코드를 보호하는 투명한 거품 안에 두고, 외부에서 접근할 수 있는 부분만 허용합니다.

간단히 말하면, 캡슐화는 데이터와 그 데이터를 조작하는 메서드를 단일 유닛이나 객체 내에 통합하는 것입니다. 객체가 어떻게 작동하는 내부 상세를 숨기고, 필요한 것만 노출시키는 방법입니다.

캡슐화의 필요성은 무엇인가요?

"이 캡슐화라는 복잡한东西이 왜 필요한 거야?"라고 궁금해하실 수도 있습니다. 그럼 이 작은 이야기를 들려드릴게요.

옛날 옛적, 스파게티 코드의 땅에 프로그래머 복 named Bob이 살고 있었습니다. Bob의 코드는 누구나 변경할 수 있었고, 곧 다른 프로그래머들이 그의 코드를 건드리기 시작했습니다. 혼란이 빚어졌습니다! 누구도 코드의 어떤 부분이 무엇을 하는지 몰라서, 버그가 토끼처럼 빠르게 증식했습니다.

이때 캡슐화가 구원자로 등장합니다. 캡슐화는 우리를 다음과 같은 것으로 도와줍니다:

  1. 데이터를 우연한 수정으로부터 보호합니다.
  2. 복잡한 구현 상세를 숨깁니다.
  3. 코드를 더 조직적으로 만들어 유지보수를 쉽게 합니다.
  4. 코드의 다른 부분 간의 종속성을 줄입니다.

자바스크립트에서 캡슐화를 달성하는 다양한 방법

자바스크립트에서는 캡슐화를 달성하기 위한 여러 가지 기법이 있습니다. 하나씩 탐구해 보겠습니다:

함수闭包을 사용한 캡슐화

闭包는 자바스크립트에서 환경을 기억하는 마법의 거품과 같습니다. 우리는 그것을 사용하여 사적인 변수와 메서드를 만들 수 있습니다. 예제를 보겠습니다:

function createBankAccount(initialBalance) {
let balance = initialBalance;

return {
deposit: function(amount) {
balance += amount;
console.log(`Deposited ${amount}. New balance is ${balance}`);
},
withdraw: function(amount) {
if (amount <= balance) {
balance -= amount;
console.log(`Withdrawn ${amount}. New balance is ${balance}`);
} else {
console.log("Insufficient funds!");
}
},
getBalance: function() {
return balance;
}
};
}

const myAccount = createBankAccount(1000);
myAccount.deposit(500);  // Deposited 500. New balance is 1500
myAccount.withdraw(200); // Withdrawn 200. New balance is 1300
console.log(myAccount.getBalance()); // 1300
console.log(myAccount.balance); // undefined

이 예제에서 balance는 우리의 사적인 변수입니다. createBankAccount 함수 외부에서 직접 접근할 수 없습니다. 우리는 노출된 메서드인 deposit, withdraw, 그리고 getBalance를 통해 그것과 상호작용할 수 있습니다. 이것이 캡슐화의 실제입니다!

ES6 클래스와 사적인 변수를 사용한 캡슐화

ES6 클래스의 도입으로 우리는 더 익숙한 방법으로 객체를 만들고 캡슐화를 달성할 수 있게 되었습니다. 이렇게 할 수 있습니다:

class BankAccount {
#balance;  // 사적인 필드

constructor(initialBalance) {
this.#balance = initialBalance;
}

deposit(amount) {
this.#balance += amount;
console.log(`Deposited ${amount}. New balance is ${this.#balance}`);
}

withdraw(amount) {
if (amount <= this.#balance) {
this.#balance -= amount;
console.log(`Withdrawn ${amount}. New balance is ${this.#balance}`);
} else {
console.log("Insufficient funds!");
}
}

getBalance() {
return this.#balance;
}
}

const myAccount = new BankAccount(1000);
myAccount.deposit(500);  // Deposited 500. New balance is 1500
myAccount.withdraw(200); // Withdrawn 200. New balance is 1300
console.log(myAccount.getBalance()); // 1300
console.log(myAccount.#balance); // SyntaxError: Private field '#balance' must be declared in an enclosing class

이 예제에서 우리는 # 기호를 사용하여 balance를 사적인 필드로 선언했습니다. 이는 그것이 BankAccount 클래스 내부에서만 접근할 수 있음을 의미합니다.

게터와 셋터를 사용한 캡슐화

게터와 셋터는 객체의 속성에 접근하거나 수정하는 방법을 정의하는 특별한 메서드입니다. 그들은 우리 객체의 속성에 대한 경비원과 같습니다. 그들을 활용해 보겠습니다:

class Circle {
constructor(radius) {
this._radius = radius;
}

get radius() {
return this._radius;
}

set radius(value) {
if (value <= 0) {
throw new Error("Radius must be positive");
}
this._radius = value;
}

get area() {
return Math.PI * this._radius ** 2;
}
}

const myCircle = new Circle(5);
console.log(myCircle.radius); // 5
console.log(myCircle.area);   // 78.53981633974483

myCircle.radius = 10;
console.log(myCircle.radius); // 10

myCircle.radius = -1; // Error: Radius must be positive

이 예제에서 우리는 radius에 대한 게터와 셋터를 사용합니다. 셋터에는 반지의 길이가 항상 양수인지 확인하는 검사가 포함되어 있습니다. 또한 area에 대한 게터를 사용하여 면적을 실시간으로 계산합니다.

자바스크립트에서 캡슐화의 이점

이제 캡슐화를 어떻게 달성할 수 있는지 보았습니다. 그 이점을 요약해 보겠습니다:

  1. 데이터 보호: 우리 객체의 내부에서 비인가 접근을 방지합니다.
  2. 유연성: 내부 구현을 변경할 수 있도록 하여 외부 코드에 영향을 미치지 않습니다.
  3. 모듈성: 자립적인 코드 단위를 만들어 코드를 더 쉽게 관리할 수 있게 합니다.
  4. 디버깅: 데이터가 어디서 수정되는지 제한하여 버그를 추적하기 쉽습니다.
  5. 추상화: 복잡한 구현 상세를 숨기고 객체의 사용을 위한 간단한 인터페이스를 제공합니다.

메서드 표

여기서 우리가 논의한 캡슐화 메서드를 요약한 표를 제공합니다:

메서드 설명 예제
함수闭包 闭包를 사용하여 사적인 변수를 만듭니다 function createObject() { let privateVar = 0; return { getVar: () => privateVar }; }
ES6 클래스와 사적인 필드 #를 사용하여 클래스 내 사적인 필드를 선언합니다 class MyClass { #privateField; constructor() { this.#privateField = 0; } }
게터와 셋터 속성에 대한 접근과 수정을 제어하는 특별한 메서드를 정의합니다 class MyClass { get prop() { return this._prop; } set prop(value) { this._prop = value; } }

그렇게 해서, 여러분은 캡슐화의 세계를 탐험했습니다. 기본 개념에서 다양한 구현 방법까지! 캡슐화는 좋은 비밀 보관자처럼, 무엇을 공유하고 무엇을 숨길지 알고 있습니다. 프로그래밍 여정을 계속하면서, 캡슐화는 견고하고 유지보수 가능한 코드를 만드는 신뢰할 수 있는 동반자가 될 것입니다.

계속 연습하고, 호기심을 가지고, 행복하게 코딩하세요!

Credits: Image by storyset