[JS] OOP, 그리고 클래스와 인스턴스

김재훈·2022년 11월 18일
1

오늘은 Javascript에서의 클래스에 대해 학습한 내용을 정리해보겠습니다.

학습목표
1. OOP에 대해 설명할 수 있다.
2. 클래스와 인스턴스가 무엇인지 설명할 수 있다.
3. ES5 문법으로 클래스를 작성하고, prototype이 무엇인지 설명할 수 있다.
4. ES6 문법으로 클래스를 작성하고, new 키워드와 생성자 함수에 대해 설명할 수 있다.

OOP(Object Oriented Programming)

OOP는 우리말로 객체 지향 프로그래밍이라고 합니다.

전통적인 개발 방식에서 프로그램은 데이터(what)와 이를 처리하는 방법(how)를 절차적으로 작성하게끔 개발되어 왔습니다. 이에 다익스트라의 논문(ref.1)에서 프로그래밍의 프로시저라는 개념이 도입되었고, 우리가 흔히 절차형 프로그래밍에서 많이 쓰는 방식인 함수가 이 개념으로부터 탄생되었습니다.

하지만 여전히 데이터와 이를 처리하는 방법은 분리되어 있었습니다. 즉 변수가 너무 많아져 변수 이름을 짓는 것도 힘들어졌으며, 가장 큰 문제는 GUI에서 기존 방식으로 상태를 저장하고, 상태에 따라 동작이 달라지는 경우를 처리하기가 너무 힘들었다는 것입니다.(=== 실행 컨텍스트의 부재)

그래서 데이터를 구조화하여 하나에 담으려는 시도를 하였고, 그것이 객체입니다.
객체는 값(데이터)과 기능(데이터를 처리하는 방법)의 집합이며, 우리가 흔히 부르는 프로퍼티(속성)이 값이고 메서드가 기능인 것입니다.

다시 말해... 객체 지향 프로그래밍은 하나의 작은 기능 단위로 객체를 설계하고, 이들간의 상호작용으로 프로그램을 작성하고자 하는 시도이자 개념이라고 할 수 있습니다.

🤔 point
공부하다 보니 OOP가 좋지 않다는 이야기도 꽤 있었습니다.
이에 대해서는 추후 공부해보겠습니다.

클래스와 인스턴스

클래스는 객체를 생성하기 위해 유사한 부분들을 구조화하여 반복되는 것을 최대한 줄인 일종의 모델입니다.
인스턴스는 클래스를 바탕으로 만든 객체로써 인스턴스 객체라고도 합니다.

[Youtube] 캐리어 만드는 과정 영상을 보면 틀에 맞춰 크게 부풀었다가 줄어들며 하나의 캐리어 가방이 됩니다.
여기서 캐리어 가방의 크기에 맞춘 설계도 또는 공정을 클래스라 할 수 있겠습니다.
그렇게 해서 나온 캐리어 가방 하나하나가 인스턴스가 됩니다.

OOP의 4가지 특성

클래스를 사용하여 얻을 수 있는 객체 지향에서의 특성 4가지를 알아보겠습니다.

1. 캡슐화(Encapsulation)

데이터와 기능을 하나의 단위로 묶어 목적(Hiding, 은닉)을 달성합니다.
결합도를 낮춰(Loose Coupling, 느슨한 결합) 언제든 유연하게 메서드의 내부 동작을 수정할 수 있다는 이점이 있습니다.

대부분의 classical한 OOP(C++, Java 등) 언어에서는 클래스 내부에서만 쓰이는 속성 및 메서드를 구분하기 위한 private 키워드를 제공합니다.
JS에서는 지원하는 브라우저가 적어 TS(TypeScript)를 사용하는 경우도 잦습니다.

아래 코드는 JS와 TS에서 캡슐화를 위해 사용하는 방식 차이입니다.
JS

function Animal(name) {
  let _name = name
  return {
    getName() return _name
  }
}

const cat = new Animal('Cat')
// _name은 private처럼 동작해서 직접 접근할 수 없음
console.log(cat._name)		// -> undefined
console.log(cat.getName())	// -> 함수를 통해서는 접근 가능

TS

class Animal {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}

new Animal('Cat').name; // 사용 불가
// -> Property 'name' is private and only accessible within class 'Animal'.

2. 상속(Inheritance)

부모 클래스(또는 기본 클래스, base class)의 특징을 자식 클래스(또는 파생 클래스, derived class)가 물려받는 것을 말합니다.

상속을 통해 불필요한 코드를 줄여 재사용성을 향상시킬 수 있습니다.

예를 들어 Programmer라는 클래스는 name 속성과 writeCode() 메서드를 가지고 있습니다.
이를 상속하여 속성에 경력기간과 front/back 중 어느 파트인지를 나타내는 속성 및 웹사이트 제작이라는 메서드를 가지는 Web Developer라는 클래스를 생성할 수 있습니다.

3. 추상화(Abstraction)

우리가 물을 마시기 위해 정수기를 사용할 때 수원지가 어디서 오고, 물을 어떻게 정수하고, 어떤 과정을 거쳐 집까지 오는지에 대해 몰라도 정수기의 출수구를 통해 컵에 물을 받듯이 복잡한 부분을 가리고 노출되는 부분을 단순하게 만드는 것이 추상화입니다.

캡슐화가 코드를 가리는 목적이 데이터의 은닉이라면 추상화는 단순화하는 것이 목적입니다.
단순화된 사용으로 의도치 않은 사이드 이펙트를 최소화할 수 있습니다.

클래스 정의 시 인터페이스를 사용한다면 메서드와 속성만 정의하고, 실제 구현은 추후에 가능합니다.

4. 다형성(Polymorphism)

이름이 같은 메서드더라도 여러 형태로 구현할 수 있으며, 자동차를 타는 것을 코드로 표현하면 다음과 같이 설명할 수 있습니다.

// 다형성을 적용하지 않은 방식
function rideAirplane() { ... }
function rideBus() { ... }
function rideCar() { ... }
function ride() {
  if (transportation === 'airplane') {
    rideAirplane()
  }
  else if (transportation === 'bus') {
    rideBus()
  }
  else if (transportation === 'car') {
    rideCar()
  }
}

위처럼 다형성을 적용하지 않으면 ride() 안에서 각 이동수단을 조건으로 분기하여 그에 맞는 함수를 실행시켜줘야 하며, 이동수단이 늘어나는 등 요구사항의 변경이 발생하면 처리해줘야 하는 작업이 늘어납니다.

반면, ride()의 일반적인 부분을 작성한 뒤 다형성을 적용한 경우 각 이동수단에서 추가될 동작들은 상속한 곳에서 그에 맞게 구현하면 되므로 요구사항 변경에 보다 유연하게 대처할 수 있습니다.

문법별 class 작성방법 차이

ES5에서는 클래스 키워드가 없어 클로저 모듈 패턴을 응용해 작성합니다.
ES6부터는 class를 통해 클래스를 작성할 수 있습니다.

let ES5 = function (name) {
  this.name = name
  return {
    getName: function() { return this.name }
  }
}

let ES6 = class {
  constructor(name) { this.name = name }
  getName() { return this.name }
}

let es5ClassVar = new ES5('ES5 Instance')
let es6ClassVar = new ES6('ES6 Instance')

실행 결과
위 코드의 실행 결과

profile
개발하면서 새롭게 배운 내용, 시행착오한 내용들을 잊지 않기 위해 기록합니다.

1개의 댓글

comment-user-thumbnail
2022년 11월 20일

덕분에 객체 지향 프로그래밍 방식의 탄생 배경에 대해 더 잘 이해하고 갑니다~~~

답글 달기