자바스크립트의 객체지향 프로그래밍 - 클래스와 생성자 함수 상속 (extends) 프로토타입과 this 키워드 캡슐화 (클로저 활용)

10. 자바스크립트의 객체지향 프로그래밍 (OOP)

객체지향 프로그래밍(OOP)은 데이터를 객체로 표현하고, 객체 간의 상호작용을 통해 프로그램을 구성하는 프로그래밍 패러다임입니다. 자바스크립트는 객체지향 프로그래밍을 지원하며, 클래스, 상속, 프로토타입, 캡슐화와 같은 OOP 개념을 활용할 수 있습니다. 이 장에서는 자바스크립트에서 객체지향 프로그래밍을 어떻게 구현할 수 있는지에 대해 살펴보겠습니다.

자바스크립트의 객체지향 프로그래밍

10.1 클래스와 생성자 함수

자바스크립트에서 객체를 생성하는 방법에는 생성자 함수클래스 두 가지 방법이 있습니다. ES6(ECMAScript 2015) 이전에는 주로 생성자 함수를 사용했지만, ES6 이후에는 클래스 문법이 도입되어 객체지향 프로그래밍을 더 직관적으로 구현할 수 있게 되었습니다.


10.1.1 클래스

클래스(class)는 객체를 생성하기 위한 템플릿 역할을 합니다. 클래스는 속성(필드)과 메서드를 정의하며, new 키워드를 사용하여 클래스의 인스턴스를 생성할 수 있습니다.

10.1.1.1 클래스 정의

클래스는 class 키워드로 정의하며, 생성자 메서드 constructor를 통해 객체의 초기 상태를 설정할 수 있습니다.

예시: 클래스 정의

javascript
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}.`); } } const person1 = new Person("Alice", 25); person1.greet(); // Hello, my name is Alice.

위 예제에서 Person 클래스는 nameage 속성을 가지며, greet() 메서드를 통해 자기소개를 합니다. new Person("Alice", 25)Person 클래스의 인스턴스를 생성합니다.


10.1.2 생성자 함수

ES6 이전에는 생성자 함수를 사용하여 객체를 생성했습니다. 생성자 함수는 일반 함수이지만, new 키워드와 함께 호출되면 객체를 생성하고 초기화합니다.

10.1.2.1 생성자 함수 정의
javascript
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.greet = function() { console.log(`Hello, my name is ${this.name}.`); }; const person1 = new Person("Bob", 30); person1.greet(); // Hello, my name is Bob.

위 예제에서 Person은 생성자 함수이며, prototype을 사용해 메서드를 정의합니다. new Person("Bob", 30)는 새로운 객체를 생성합니다.


10.1.3 클래스와 생성자 함수의 차이점

  • 문법적 차이: class는 ES6에서 도입된 문법적 설탕(syntax sugar)으로, 본질적으로 생성자 함수를 감싸는 방식입니다.
  • strict mode: 클래스는 자동으로 strict mode로 동작하여, 더 엄격한 규칙을 적용합니다.
  • hoisting: 생성자 함수는 호이스팅(hoisting)이 가능하지만, 클래스는 정의되기 전에 사용할 수 없습니다.



10.2 상속 (extends)

상속(inheritance)은 객체지향 프로그래밍의 중요한 개념 중 하나로, 부모 클래스의 기능을 자식 클래스에서 물려받아 확장할 수 있게 해줍니다. 자바스크립트에서는 extends 키워드를 사용해 상속을 구현할 수 있습니다.


10.2.1 클래스 상속

자식 클래스는 extends 키워드를 사용하여 부모 클래스를 상속받을 수 있습니다. 자식 클래스는 부모 클래스의 모든 속성과 메서드를 상속받으며, 필요에 따라 오버라이딩(재정의) 할 수 있습니다.

예시: 클래스 상속

javascript
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound.`); } } class Dog extends Animal { constructor(name, breed) { super(name); // 부모 클래스의 생성자를 호출 this.breed = breed; } speak() { console.log(`${this.name} barks.`); } } const dog = new Dog("Buddy", "Golden Retriever"); dog.speak(); // Buddy barks.

위 코드에서 Dog 클래스는 Animal 클래스를 상속받아 speak() 메서드를 재정의(오버라이딩)하고, super()를 통해 부모 클래스의 생성자를 호출합니다.


10.2.2 super 키워드

super 키워드는 부모 클래스의 메서드를 호출할 때 사용됩니다. 주로 생성자 함수 내에서 부모 클래스의 생성자를 호출하거나, 부모 클래스의 메서드를 호출하는 데 사용됩니다.

예시: super 키워드

javascript
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound.`); } } class Cat extends Animal { constructor(name, color) { super(name); // 부모 클래스의 생성자 호출 this.color = color; } speak() { super.speak(); // 부모 클래스의 speak 메서드 호출 console.log(`${this.name} meows.`); } } const cat = new Cat("Whiskers", "black"); cat.speak(); // Whiskers makes a sound. // Whiskers meows.

super.speak()는 부모 클래스인 Animalspeak() 메서드를 호출하며, 이후 자식 클래스에서 추가적인 동작을 정의할 수 있습니다.




10.3 프로토타입과 this 키워드

자바스크립트는 프로토타입 기반 언어입니다. 모든 객체는 프로토타입(prototype)이라는 속성을 가지며, 이를 통해 상속과 메서드를 공유할 수 있습니다. 또한, this 키워드는 함수나 메서드가 호출된 객체 자신을 가리킵니다.


10.3.1 프로토타입

모든 자바스크립트 객체는 prototype이라는 숨겨진 링크를 가지고 있으며, 이를 통해 객체 간에 속성과 메서드를 상속받을 수 있습니다. 프로토타입 기반 상속은 자바스크립트의 핵심 개념입니다.

예시: 프로토타입 상속

javascript
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.greet = function() { console.log(`Hello, my name is ${this.name}.`); }; const person1 = new Person("Alice", 25); const person2 = new Person("Bob", 30); person1.greet(); // Hello, my name is Alice. person2.greet(); // Hello, my name is Bob.

위 예제에서 greet() 메서드는 Person의 프로토타입에 정의되어 있으므로, 생성된 모든 Person 객체는 이 메서드를 공유하게 됩니다.


10.3.2 this 키워드

this 키워드는 함수 또는 메서드가 호출된 객체 자신을 가리킵니다. this가 무엇을 참조하는지는 함수가 호출된 방식에 따라 결정됩니다.

예시: this 키워드

javascript
class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}.`); } } const person1 = new Person("Alice", 25); person1.greet(); // Hello, my name is Alice.

위 코드에서 this.nameperson1 객체의 name 속성을 가리킵니다.

10.3.2.1 this의 동적 바인딩

this는 호출 방식에 따라 참조하는 객체가 달라질 수 있습니다. 예를 들어, 메서드를 객체가 아닌 다른 컨텍스트에서 호출하면 this가 예상치 못한 값을 가리킬 수 있습니다.

예시: this의 동적 바인딩

javascript
const obj = { name: "Alice", greet() { console.log(`Hello, my name is ${this.name}.`); } }; const greetFunc = obj.greet; greetFunc(); // this는 undefined를 가리킴, strict mode에서는 오류 발생

이 문제를 해결하기 위해 bind(), call(), apply() 같은 메서드를 사용하여 this를 명시적으로 바인딩할 수 있습니다.




10.4 캡슐화 (클로저 활용)

캡슐화(encapsulation)는 객체지향 프로그래밍의 중요한 원칙으로, 객체의 내부 상태를 외부로부터 숨기고, 객체 내부의 데이터는 오직 해당 객체의 메서드를 통해서만 접근할 수 있도록 하는 것을 의미합니다. 자바스크립트에서는 클로저를 활용하여 캡슐화를 구현할 수 있습니다.


10.4.1 클로저를 통한 캡슐화

클로저는 함수와 함수가 선언될 당시의 렉시컬 환경을 함께 기억하는 구조입니다. 이를 통해 함수 내부에 비공개 변수를 유지할 수 있으며, 외부에서 직접 접근할 수 없게 보호할 수 있습니다.

예시: 클로저를 이용한 캡슐화

javascript
function createCounter() { let count = 0; // 비공개 변수 return { increment() { count++; console.log(count); }, getCount() { return count; } }; } const counter = createCounter(); counter.increment(); // 1 counter.increment(); // 2 console.log(counter.getCount()); // 2 console.log(counter.count); // undefined (외부에서 직접 접근 불가)

위 코드에서 count 변수는 createCounter 함수의 스코프에 캡슐화되어 있으며, 오직 반환된 객체의 메서드를 통해서만 접근할 수 있습니다. 외부에서는 count 변수에 직접 접근할 수 없습니다.


10.4.2 ES6 클래스에서의 캡슐화

ES6 클래스에서는 캡슐화를 완벽히 지원하지는 않지만, WeakMap을 이용하여 객체의 프라이빗(private) 속성을 보호할 수 있습니다. 또한, ES2020에서는 # 기호를 통해 클래스의 프라이빗 속성을 정의할 수 있습니다.

예시: 프라이빗 속성 (#)

javascript
class Person { #name; constructor(name, age) { this.#name = name; this.age = age; } getName() { return this.#name; } } const person = new Person("Alice", 25); console.log(person.getName()); // Alice console.log(person.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class

#name 속성은 프라이빗 속성으로, 클래스 외부에서 직접 접근할 수 없으며, 오직 클래스 내부의 메서드를 통해서만 접근할 수 있습니다.



자바스크립트는 객체지향 프로그래밍(OOP)을 강력하게 지원하며, 클래스, 상속, 프로토타입, this 키워드, 캡슐화 같은 개념을 사용하여 객체 간의 상호작용을 통해 프로그램을 구조화할 수 있습니다. 클래스와 생성자 함수는 객체를 생성하는 기본적인 도구이며, 상속을 통해 코드의 재사용성을 높일 수 있습니다. 또한, 클로저를 사용한 캡슐화를 통해 객체의 상태를 보호하고, 외부에서의 부적절한 접근을 막을 수 있습니다. 객체지향 프로그래밍을 이해하고 이를 활용하면, 더 견고하고 유지보수성이 높은 자바스크립트 애플리케이션을 작성할 수 있습니다.

댓글 쓰기

0 댓글