본문 바로가기
TIL/Code States

Code States 25일차 - 객체 지향 JavaScript

by 죠르띠에 2021. 8. 23.

클래스와 인스턴스

객체 지향 프로그래밍

객체 지향 프로그래밍은 하나의 모델이 되는 청사진(blueprint)을 만들고, 그 청사진을 바탕으로 한 객체(object)를 만드는 프로그래밍이다. 사실, 이미 자바스크립트에는 "객체"라는 개념이 객체 지향 프로그램과 무관하게 이미 존재한다. 따라서 여기서는 용어를 잘 구분하는 것이 중요하다. 앞으로, 그냥 객체가 아닌 "청사진"을 바탕으로 한 객체는 인스턴스 객체(instance object), 줄여서 인스턴스(instance)라고 부른다. 청사진은 클래스(class)라고 부른다.

 

객체를 어떤 식으로 만드는지 살펴보면, 그냥 일반적인 함수를 정의하듯 만든다. 이때, 함수를 이용하는 방법이 조금 다르다. 그냥 실행하는 것이 아니고 new 키워드를 써서 만든다. 이는, 새로운 인스턴스를 만드는 방법이다. 클래스를 함수로 정의하면, 일반적인 다른 함수와 구분할 때 암묵적으로 보통 클래스는 대문자, 일반명사로 만든다.

 

클래스를 만드는 방법이 ES6에 도입되었다. 바로 class 키워드이다.

class Car {
  constructor(brand, name, color) {
    // 인스턴스가 만들어질 때 실행되는 코드
  }
}

여기서 보이는 함수는, 객체지향프로그래밍에서 생성자(constructor) 함수라고 한다. 인스턴스가 만들어질 때실행되는 코드이다.

인스턴스를 만들 때에는 new 키워드를 사용한다. 즉시 생성자 함수가 실행되며, 변수에 클래스의 꼭 닮은 새로운 객체, 즉 인스턴스가 할당된다. 각각의 인스턴스는 클래스의 고유한 속성과 메소드를 갖게 된다.

 

클래스 정의

class Car {
  constructor(brand, name, color) {
    this.brand = brand;
    this.name = name;
    this.color = color;
  }
}

this라는 새로운 키워드가 등장한다. 객체지향 프로그래밍에서는 빠지지 않고 등장하므로 간단하게 알아두자.

this는 인스턴스 객체를 의미한다. parameter로 넘어온 브랜드, 이름, 색상 등은 인스턴스 생성시 지정하는 값이며, 위와 같이 this에 할당 하는 것은, 만들어진 인스턴스에 해당 브랜드, 이름, 색상을 부여하겠다는 의미이다.

 

메소드 정의

// ES5
function Car(brand, name, color){ /* 생략 */ }

Car.prototype.refuel = function() {

}

Car.prototype.drive = function() {

}


// ES6
class Car {
  constructor(brand, name, color) { /* 생략 */ }
  
  refuel() {
  
  }
  
  drive() {
  
  }
}

ES5는 prototype이라는 키워드를 사용하야 메소드를 정의할 수 있다. Car 클래스에 메소드를 추가하기 위해서는 'Car.prototype.refuel'과 같이 'prototype'을 이용해야한다.

ES6에서는 생성자 함수와 함께 class 키워드 안쪽에 묶어서 정의한다. 'refuel(){}', 'drive(){}'와 같이 작성된 부분이다.

 

인스턴스의 사용

let avante = new Car('hyundai', 'avante', 'black');
avante.color;
avante.drive();

let mini = new Car('bmw', 'mini', 'white');
mini.brand;
mini.refuel();

 

여기에 있는 용어는 오직 자바스크립트에서만 유용한 용어이고, 생성자 함수 정도만 객체 지향 프로그래밍에서 보편적인 개념이다.

prototype 모델의 청사진을 만들 때 쓰는 원형 객체(original form)
constructor 인스턴스가 초기화될 때 실행하는 생성자 함수
this 함수가 실행될 때, 해당 scope마다 생성되는 고유한 실행 context(execution context) new 키워드로 인스턴스를 생성했을 때에는, 해당 인스턴스가 바로 this의 값이 됨.

객체 지향 프로그래밍

객체 지향 언어

  • "클래스"라고 부르는 데이터 모델의 청사진을 사용해 코드 작성
  • 현대의 언어들은 대부분 객체 지향의 특징을 갖고 있음 (대표적으로 Java, C++, C# 등)
  • JavaScript: 객체 지향으로 작성 가능

객체 지향 프로그래밍이라는 패러다임이 등장하면서, 단순히 별개의 변수와 함수로 순차적으로 작동하는 것을 넘어, 데이터의 접근과, 데이터의 처리 과정에 대한 모형을 만들어 내는 방식을 고안해냈다. 따라서, 데이터와 기능이 별개로 취급되지 않고, 한번에 묶어서 처리할 수 있게 되었다. 이러한 객체 지향의 특징은 빠르게 현대 언어에 적용이 되었다. 자바스크립트는 엄밀히 말해 객체 지향 언어는 아니지만, 객체 지향 패턴으로 작성할 수 있다.

OOP

  • OOP는 프로그램 설계 철학이다.
  • OOP의 모든 것은 "객체"로 그룹화된다.
  • OOP의 4가지 주요 개념을 통해 재사용성을 얻을 수 있다.

OOP는 프로그램 설계 철학 중 하나이고, 객체로 그룹화된다. 이 객체는 한번 만들고 나면, 메모리상에서 반환되기 전까지 객체 내의 모든 것이 유지된다. 객체 내에는 "데이터와 기능이 함께 있다"라는 원칙에 따라 메소드와 속성이 존재한다.

클레스와 인스턴스

  • 클래스는 일종의 원형(orignal form)으로, 객체를 생성하기 위한 아이디어나 청사진이다.
  • 인스턴스는 클래스의 사례(instance object)이다.
  • 클래스는 객체를 만들기 위한 생성자(constructor) 함수를 포함한다.

클래스는, 세부 사항(속성)이 들어가지 않은 청사진이다. 세부 사항만 넣는다면, 객체가 되는 것이다. JavaScript에서 사용하는 용어와 별개로 클래스를 통해 만들어진 객체를 특별히 인스턴스 객체, 줄여서 인스턴스라고 한다. 세부 사항을 넣어주는 역할을 하는 것이 바로 생성자이다. 생성자를 통해 세부 사항(속성)을 넣어준다. 함수에 인자를 넣듯, 속성을 넣을 수 있다.

OOP Basic Concepts

애플리케이션을 만들 때 좋은 설게를 하기 위해서는, 기본적으로 이 객체지향을 이해하고 응용하는 것이 중요하다. 객체 지향 프로그래밍의 주요 컨셉을 소개하면 크게 네가지 기본적인 컨셉이 있다.

  • Encapsulation (캡슐화)
  • Inheritance (상속)
  • Abstraction (추상화)
  • Polymorphim (다형성)

캡슐화 Encapsulation

  • 데이터와 기능을 하나의 단위로 묶는 것
  • 은닉(hiding): 구현은 숨기고, 동작은 노출시킴
  • 느슨한 결합(Loose Coupling)에 유리: 언제든 구현을 수정할 수 있음

캡슐화는 외부에서 데이터(속성)와 속성(메소드)을 따로 정의하는 것이 아닌, 하나의 객체 안에 넣어서 묶는 것이다. 데이터(속성)와 기능(메소드)들이 느슨하게 결합되는 것이다. 느슨한 결합은 코드 실행 순서에 따라 절차적으로 코드를 작성하는 것이 아니라, 코드가 상징하는 실제 모습과 닮게 코드를 모아 결합하는 것을 의미한다.

캡슐화라는 개념에는 "은닉화"의 특징도 포함하고 있는데, 은닉화는 내부 데이터나 내부 구현이 외부로 노출되지 않도록 만드는 것이다. 따라서, 디테일한 구현이나 데이터는 숨기고, 객체 외부에서 필요한 동작(메소드)만 노출시켜야 한다. 은닉화의 특징을 살려서 코드를 작성하면 객체 내 메소드의 구현만 수정하고, 노출된 메소드를 사용하는 코드 흐름은 바뀌지 않도록 만들 수 있다. 반면 절차적 코드의 경우 데이터의 형태가 바뀔 때에 코드의 흐름에 큰 영향을 미치게 되어 유지보수가 어렵다. 그래서 더 엄격한 클래스는 속성의 직접적인 접근을 막고, 설정하는 함수(setter), 불러오는 함수(getter)를 철저하게 나누기도 한다.

추상화 Abstraction

추상화는 내부 구현은 아주 복잡한데, 실제로 노출되는 부분은 단순하게 만든다는 개념이다.

추상화를 통해 인터페이스가 단순해진다. 너무 많은 기능들이 노출되지 않은 덕분에 예기치 못한 사용상의 변화가 일어나지 않도록 만들 수 있다. 추상화는 캡슐화와 비교해서 종종 헷갈려하는 개념 중 하나이다. 캡슐화가 코드나 데이터의 은닉에 포커스를 맞춰져있다면, 추상화는 클래스를 사용하는 사람이 필요하지 않은 메소드 등을 노출시키지 않고 단순한 이름으로 정의하는 것에 포커스가 맞춰져 있다. 클래스 정의 시, 메소드와 속성만 정의한 것을 인터페이스라고 부른다. 이것이 추상화의 본질이다.

상속 Inheritance

상속은 부모 클래스의 특징을 자식 클래스가 물려받는 것이다. 부모/자식으로 이야기하기도 하지만, 보다 그 특징을 자세하게 설명하는 용어는 "기본 클래스(base class)의 특징을 파생 클래스(derive class)가 상속받는다"로 표현하는 것이 적합하다.

예를 들어, 사람(Human)이라는 클래스가 있다고 가정해보자. 사람은 기본적으로 이름과 성별, 나이와 같은 속성, 그리고 먹다, 자다 등과 같은 메소드가 있다고 볼수 있다.
추가적으로 학생(Student)이라는 클래스를 작성한다고 생각해보자. 그런데 이 때 앞서 사람(Human) 클래스의 속성과 메소드를 재구현한다면 비효율적일 것이다. 학생의 본질은 결국 사람이므로, 상속을 이용하여 학생(Student) 클래스는 사람(Human) 클래스를 상속받을 수 있다. 학생은 추가적으로 학습 내용, 공부하다와 같은 속성/메소드를 추가할 뿐이다.

다형성 Polymorphism

Polymorphism이라는 단어의 poly는 "많은", 그리고 morph는 "형태"라는 뜻을 가지고 있다. 즉 "다양한 형태"를 가질 수 있다는 말이 된다.

다형성을 HTML 엘리먼트를 예로 들어 설명해보겠다. 모든 엘리먼트들은 전부 객체이므로, 내부적으로 모양을 그리고 화면에 뿌리는 메소드가 존재할 것이다. 이 메소드가 render라는 이름을 갖고 있다고 가정해보자. 이 경우에는, TextBox, Select, Checkbox의 고통의 부모인 HTML Element라는 클래스에 render라는 메소드를 만들고 상속 받게 만들 수 있다. 그런데 다형성의 핵심은 이 같은 이름의 render 라는 메소드가 조금씩 다르게 작동한다는 데 있다. 이처럼 같은 이름을 가진 메소드라도 조금씩 다르게 작동하는 것이 바로 다형성이다.

Polymorphism이 없다면

만일 언어 자체에서 다형성을 제공하지 않는다면, 기본(부모) 클래스에 종류별로 분기를 시켜서 하나하나 다르게 만들어야 할 것이다. 또는 각각의 자식 클래스의 별도의 각기 다른 render 함수를 만들 수도 있겠지만, 엘리먼트라는 클래스의 본질상 "화면에 뿌린다"(render)는 개념은 부모가 갖고 있는 것이 합리적이다.

 

OOP의 주요 개념에 대한 장점

  • 캡슐화는 코드가 복잡하지 않게 만들고, 재사용성을 높인다.
  • 추상화는 마찬가지로 코드가 복잡하지 않게 만들고, 단순화된 사용으로 인해 변화에 대한 영향을 최소화한다.
  • 상속 역시 불필요한 코드를 줄여 재사용성을 높인다.
  • 다형성으로 인해 동일한 메서드에 대해 if/else if와 같은 조건문 대신 객체의 특성에 맞게 달리 작성하는 것이 가능하다.

'TIL > Code States' 카테고리의 다른 글

Code States 27일차 - StringifyJSON  (0) 2021.08.25
Code States 26일차 - 재귀  (0) 2021.08.24
Code States 20일차 - React State & Props  (0) 2021.08.13
Code States 19일차 - React Router  (0) 2021.08.12
Code States 19일차 - React SPA  (0) 2021.08.12