[JS] 객체 지향
JavaScript & TypeScript

[JS] 객체 지향

반응형

1. 객체지향 프로그래밍란?

객체 지향 프로그래밍이라는 패러다임이 등장하기 전, 절차 언어가 있었습니다. 우리는 앞서 모든 것을 절차로 생각해왔습니다. 기껏해야 함수로 이동하는 것이 전부였죠. 초기의 C, 포트란같은 언어들은 객체 지향의 개념이 없는 절차 언어(순차적인 명령의 조합)였습니다.

그러나, 객체 지향 프로그래밍이라는 패러다임이 등장하면서, 단순히 별개의 변수와 함수로 순차적으로 작동하는 것을 넘어, 데이터의 접근과, 데이터의 처리 과정에 대한 모형을 만들어 내는 방식을 고안해냈습니다. 따라서, 데이터와 기능이 별개로 취급되지 않고, 한번에 묶어서 처리할 수 있게 되었습니다. 속성과 메소드가 하나의 "객체"라는 개념에 포함되며, 이는 자바스크립트 내장 타입인 object(이하, object literal)와는 다르게, 클래스(Class)라는 이름으로 부릅니다. 

이러한 객체 지향의 특징은 빠르게 현대 언어에 적용이 되었습니다. 자바스크립트는 엄밀히 말해 객체 지향 언어는 아니지만, 객체 지향 패턴으로 작성할 수 있습니다.

 

OOP는 프로그램 설계 철학 중 하나입니다. (객체지향은 어떤 컴퓨터 언어를 사용하던지 적용할 수 있는 일종의 "철학", "패러다임" 이다.) OOP는 객체로 그룹화됩니다. 이 객체는 한번 만들고 나면, 메모리상에서 반환되기 전까지 객체 내의 모든 것이 유지됩니다. 객체 내에는 "데이터와 기능이 함께 있다"는 원칙에 따라 메소드와 속성이 존재합니다.

예를 한번 들어보겠습니다. 모든 자동차는 공통적인 기능과 고유의 속성이 있습니다. 속도를 낸다던지, 주유를 한다던지 등의 기능이 존재하며, 속성으로는 색상, 최고 속력 혹은 탑승인원 등과 같은 고유의 데이터가 존재하죠. 새로운 객체를 만들 때, "이번에 만들 자동차는, 빨간색의 최고 속력은 200km/h 를 내도록 만들어보자!"와 같이, 속성에 고유한 값을 부여할 수 있습니다.

 

 

 

2. OOP Basic Concepts

객체 지향 프로그래밍의 주요 네가지 기본적인 컨셉이 있습니다.

 

1. 캡슐화 Encapsulation

 

캡슐화는 외부에서 앞서 말했던 데이터(속성)와 기능(메소드)을 따로 정의하는 것이 아닌, 하나의 객체 안에 넣어서 묶는 것입니다. 데이터(속성)과 기능(메소드)들이 느슨하게 결합되는 것이죠.

 

느슨한 결합은 코드 실행 순서에 따라 절차적으로 코드를 작성하는 것이 아니라, 코드가 상징하는 실제 모습과 닮게 코드를 모아 결합하는 것을 의미합니다. 마우스 구동을 위한 코드 작성을 예로 들겠습니다. 스위치가 눌리고, 전기 신호가 생겨서, 전선을 타고 흐르고.. 와 같은 전 과정을 이곳 저곳에 나누어 작성하는 것이 아니라, 마우스의 상태를 속성(property)로 정하고 클릭, 이동을 메소드(method)로 정해서 코드만 보고도 인스턴스 객체의 기능을 상상할 수 있게 작성하는 것이 느슨한 결합을 추구하는 코드 작성법입니다.

 

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

 

 

2. 추상화 Abstraction

추상화는 내부 구현은 아주 복잡한데, 실제로 노출되는 부분은 단순하게 만든다는 개념입니다. 예를 들어 전화라는 객체가 있다면, 그 안에는 스피커와 마이크가 존재하고, 서킷 보드 등이 존재하는 등 내부 구현이 되어 있을 것입니다. 그러나 실제로 우리가 사용할 때에는, 이러한 존재에 대해서는 생각하지 않고 단순히 수화기를 들고 버튼을 눌러서 해결하는 것으로 인터페이스(interface)를 단순화할 수 있습니다.

 

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

 

 

3. 상속 Inheritance 

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

 

예를 들어, 사람(Human)이라는 클래스가 있다고 가정해봅시다. 사람은 기본적으로 이름과 성별, 나이와 같은 속성, 그리고 먹다, 자다 등과 같은 메소드가 있다고 볼 수 있습니다.

추가적으로 학생(Student)이라는 클래스를 작성한고 생각해봅시다. 그런데 이 때 앞서 사람(Human) 클래스의 속성과 메소드를 재구현한다면 비효율적일 것입니다. 학생의 본질은 결국 사람이므로, 상속을 이용하여 학생(Student) 클래스는 사람(Human)클래스를 상속받을 수 있습니다. 학생은 추가적으로 학습 내용, 공부하다와 같은 속성/메소드만 추가할 뿐인거죠.

 

 

4. 다형성 Polymorphism

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

"말하다"라는 동작의 본질은 "입으로 소리를 내다"를 의미합니다. 그러나, 각기 다른 동물들이 "말할 때" 제각각의 소리를 내는 것처럼, 객체 역시 똑같은 메소드라 하더라도, 다른 방식으로 구현될 수 있습니다.

 

객체지향 각 컨셉의 장점

캡슐화는 코드가 복잡하지 않게 만들고, 재사용성을 높입니다.

추상화는 마찬가지로 코드가 복잡하지 않게 만들고, 단순화된 사용으로 인해 변화에 대한 영향을 최소화합니다

상속 역시 불필요한 코드를 줄여 재사용성을 높입니다.

다형성으로 인해 동일한 메소드에 대해 if/else if와 같은 조건문 대신 객체의 특성에 맞게 달리 작성하는 것이 가능해집니다.

 

 

 

3. 객체 지향 구조

ES6 내용만 다루도록 하겠습니다.

 

객체 지향 프로그래밍은 하나의 모델이 되는 청사진(blueprint)을 만들고, 그 청사진을 바탕으로 한 객체(Object)를 만드는 프로그래밍 패턴입니다.

class는 틀

틀안에 무엇을 넣을껀지를 생각해보면, 속성은 재료, 메소드는 물건의 기능을 결정.

object는 틀에서 생성된 물체 (new)

1. class 선언

class 를 그냥 함수로 정의하면, 일반적인 다른 함수와 구분은, 클래스를 만드는 암묵적인 규칙으로 보통 클래스는 대문자, 그리고 일반명사로 만듭니다. 따라서 일반적인 함수를 만들 때에는, 적절한 동사를 포함하고, 소문자로 시작하도록 만들면 좋습니다.

 

2. class의 속성과 메소드

클래스에 속성과 메소드를 정의하고, 인스턴스에서 이용 합니다.

속성은, 브랜드, 차 이름, 색상, 현재 연료 상태, 최고 속력 등이 있을 수 있습니다.

메소드는 쉽게 말해 "객체에 딸린 함수"입니다. 연료 주입, 속력 설정, 운전 등이 메소드입니다.

 

 1) 속성 정의(부여)

this라는 새로운 키워드가 등장합니다. this는 인스턴스 객체를 의미합니다.

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

 

 2) 메소드 정의(부여)

생성자 함수와 함께 class 키워드 안쪽에 묶어서 정의합니다. `refuel() {}`, drive() {}`와 같이 작성되어 있는 부분입니다.

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

	refuel() {
	}
	drive() {
    return this name + '운전 합니다'
	}
}

let avante = new Car('hyundai', 'avante', 'black');
avante.color;  // 'black'
avante.drive(); // 'avante 운전 합니다'

3. new 키워드

 

객체를 어떤 식으로 만드는지 살펴보면, 그냥 일반적인 함수를 정의하듯 만듭니다. 이때, 함수를 이용하는 방법이 조금 다릅니다. 그냥 실행하는 것이 아니고 new 키워드를 써서 만듭니다. 이는, 새로운 인스턴스를 만드는 방법입니다.

클로저 함수를 연상시키는 방법 입니다. 객체 그 자체로 사용하지 않고 변수에 할당을 하여 그 변수를 사용 합니다.

 

4. extend

extends 키워드는 클래스를 다른 클래스의 자식으로 만들기 위해 class 선언 또는 class 식에 사용됩니다.

5. super

super 키워드는 부모 오브젝트의 함수를 호출할 때 사용됩니다.

생성자에서는 super 키워드 하나만 사용되거나 this 키워드가 사용되기 전에 호출되어야 합니다. 또한 super 키워드는 부모 객체의 함수를 호출하는데 사용될 수 있습니다.

 

class Polygon {
  constructor(height, width) {
    this.name = 'Polygon';
    this.height = height;
    this.width = width;
  }
  sayName() {
    console.log('Hi, I am a ', this.name + '.');
  }
}

class Square extends Polygon { // extends 사용
  constructor(length) {
    // 여기서, 부모클래스의 생성자함수를 호출하여 높이값을 넘겨줍니다.
    // Polygon의 길이와 높이를 넘겨줍니다.
    super(length, length);

    // 참고: 파생 클래스에서 super() 함수가 먼저 호출되어야
    // 'this' 키워드를 사용할 수 있습니다. 그렇지 않을 경우 참조오류가 발생합니다.
	this.height;
    this.name = 'Square';
  }

  get area() {
    return this.height * this.width;
  }

  set area(value) {
    this.area = value;
  }
}

 

그 외 자료들

JavaScript는 프로토타입 기반 언어 입니다. 여기서 프로토타입(Prototype)은 원형 객체를 의미합니다.

https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/Object_prototypes

__proto__를 이용하면 부모 클래스의 프로토타입, 혹은 '부모의 부모 클래스'의 프로토타입을 탐색할 수 있습니다.

 

 

객체지향 관련 블로그

https://jeong-pro.tistory.com/95

 

객체 지향 프로그래밍이 뭔가요? (꼬리에 꼬리를 무는 질문 1순위, 그놈의 OOP)

객체 지향 프로그래밍(Object Oriented Programming) 여러 SW기업 신입사원 기술면접에서 워밍업느낌으로 면접자들 긴장을 풀어줄 겸 처음으로 자주 나오는 질문이다. "객체 지향 프로그래밍에 대해 설

jeong-pro.tistory.com

 

 

 

30기 동기분 중에 잘하시는 분의 발언 내용 중..

 

안타깝지만 현대 프로그래밍에서 OOP 즉 객체지향 프로그래밍이 기존의 구조적, 절차적 프로그래밍보다 더 나은, 더 뛰어난 캡슐화를 제공하지는 못했습니다. 하지만 상속이라는 개념만큼은 확실히 더 나은 방향성을 제시한 것은 맞습니다.

객체지향 프로그래밍언어들이 public, private, protected의 키워드들을 도입하면서 어느정도 본인들 언어의 불완전한 캡슐화를 보완한 것은 맞지만 객체지향프로그래밍 언어 자체의 캡슐화 개념은 프로그래머가 올바른 마인드를 가지고 있고 데이터를 우회해서 사용하지 않을거라는 믿음을 기반으로 이루어지는 캡슐화기 때문에 오히려 C언어에서와 같은 절차적 언어에서 누렸던 완벽한 캡슐화를 오히려 약화시킨것에 가깝습니다. 즉 캡슐화는 객체지향만의 특출난점이라거나 장점은 아닙니다. 좀 더 쉽다는거 하나는 장점일 수 있겠네요

자바와 C#에서 헤더와 구현체를 구분하는 방식을 버리고 클래스의 선언과 정의를 구분하는게 불가능해지면서

오히려 현대의 OOP 언어들이 캡슐화의 정의를 훼손한거에 더 가까울 수도 있겠네요 면접에서 OOP가 캡슐화에 의존한다던가, 캡슐화가 특출난점, 장점이라고하면 전 오히려 틀린 답변에 가깝다고 봅니다

 

 

 

 

 

 

반응형

'JavaScript & TypeScript' 카테고리의 다른 글

[JS] method 정리  (0) 2021.07.25
[JS] 비동기, Node.js 모듈, fetch API  (0) 2021.06.24
[JS] 고차함수  (1) 2021.06.06
[JS] DOM  (6) 2021.06.06
[JS] 복습(과제) 정리  (0) 2021.06.01