본문 바로가기

Computer Science

객체지향 프로그래밍에 대해서

# 객체지향 프로그래밍이란?

객체지향 프로그래밍은 프로그램을 명령어 또는 함수의 목록으로 보는 전통적인 명령형 프로그래밍의 절차지향적 관점에서 벗어나, 여러 개의 독립적 단위, 즉 '객체'의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임을 말한다.

 

 

### 프로그래밍 패러다임이란?

여기서 잠깐!

'프로그래밍 패러다임'은 도대체 뭘까?

- 패러다임 : 특정 분야에서의 사고 방식이나 문제 해결을 위한 체계적인 접근 방법

- 프로그래밍 패러다임 : 특정 프로그래밍 언어나 기술의 사용 방법, 코드의 구조화, 문제 해결 방법 등에 대한 전반적인 철학이나 접근 방식.

=> 이해는 가는데 딱 정의하기는 조금 어려워, 내가 나름대로 내린 정의는 '프로그래밍 표현 방법론 또는 구현 방식(스타일)'.

 

 

## 객체지향 프로그래밍의 특징

예시를 통해 알아보자.

 

예시)

삼성스토어에 tv가 여러 대 있다.

일일이 써가며 100대의 tv를 만들고 있었다. (실수가 나오기 마련..)

아니나 다를까, price를 안적는 실수가 나왔다.

let name = "SAMSUNG STORE";

let tv1 = {
  name: 'noona tv',
  price: 200,
  size: '56inch'
}

let tv2 = {
  name: 'ultra tv',
  price: 200,
  size: '27inch'
}

// 이렇게 일일이 쓰면서 tv 100대를 만들고 있었다.

let tv3 = {
  name: 'grand tv', //price 안적는 실수를..
  size: '36inch'
}

 

이렇게 3대만 만들었는데도 벌써 실수가 나온다. 시간 또한, 고작 3대 만들었는데 얼마나 걸렸는가..
똑같은 코드도 계속 쳐야한다. (개발자로서 반복되는 일은 참을 수 없다!)
실수가 생길 수 있는 가능성을 열어두는 것 자체가 용납이 안된다.

 

이렇게 '객체지향'의 개념이 나오기 시작했다.

 

우리 쉽게 만들면서도 실수를 줄일 수 있는 방법이 없을까?

해서 나온 것이 바로 class.

class는 어떤 속성이 있고, 어떤 기능이 있는지 정의해둔 일종의 '작업 지시서'.

누가 와서 만들어도 이 작업지시서만 확인하면 된다.

 

tv에 name, price, size가 들어가있으므로, 아래와 같이 작업지시서를 만들었다.

let name = "SAMSUNG STORE";

class TV {
 name = '';
 price = 0;
 size='';
 contructor(name, price, size) { //생성자함수. class에 있는 속성값을 초기화시켜주는 기능.
   // name = name, //여기 name이 위 name이야, 매개변수 name이야..? 속성 name이야? 헷갈린다.
   // class안에 있는 속성들에는 '여기 있는 name이야!'라고 알려주자, 해서 this를 붙인다.
   this.name = name, // 이 class안에 있는 name
   this.price = price,
   this.size = size,
 } 
}
//신입이 들어오면 '너 이 작업지시대로만 만들어!'

//값을 어떻게 넣느냐
//클래스에 있는 속성들에 값을 초기화 시켜주는 함수가 있다. -> contructor함수

 

위 작업지시서가 말하는건 딱 2가지 이다.

1/ name, price, size와 같은 속성들이 반드시 있어야 한다.

2/ tv를 만들 때는 반드시 name, price, size를 넣어줘야한다.

 

아직 실제 TV를 만들지 않았다. 즉, 생성을 아직 하지않았다.

let tv1 = new TV('noona tv', 200, '56inch');
console.log(tv1); // TV { name: 'noona tv', price: 200, size: '56inch' }

 

너무 간단해졌다. 

5줄로 표현했던 걸 1줄로 깔끔하게 끝냈다.

 

tv2도 만들어보자.

let tv2 = new TV('ultra tv', 200, '27inch');
console.log(tv1); // TV { name: 'ultra tv', price: 200, size: '27inch' }
console.log(tv2.price) // 200

 

위에 길게 쓰던걸 이렇게 한줄로 딱 끝낼 수 있다.

 

tv2안에 있는 price만 가져오고 싶으면 tv2.price를 해주면 된다.

 

 

이렇게 자주쓰는걸 작업지시서로 만든 다음에 찍어내듯이 만들 수 있다.

안에 어떤 요소들이 들어가있는지 한눈에 보기도 쉽다.

 

이렇게 tv 뿐만 아니라 에어컨, laptop도 제품 별로 class를 만들 수 있다.

class TV {
 name = '';
 price = 0;
 size='';
 contructor(name, price, size) { 
   this.name = name,
   this.price = price,
   this.size = size,
 } 
}

class AC {
 name = '';
 price = 0;
 type='';
}

class Laptop {
 name = '';
 price = 0;
 weight=0;
}

 

 

1/ 추상화

위 코드를 보면 제품별로 겹치는 정보가 있다.

→ 바로, name과 price

매우 불편하다...이런 반복 싫다!

 

Product라는 새로운 class를 하나 만들어보자.

그리고 거기에 겹치는 정보인 name, price를 넣어보자.

→ 이렇게 공통되는 특징을 추출하여 class로 정의하는 것을 '추상화'라고 한다.

 

그리고 class TV, AC, Laptop에 있는 name, price를 다 지워주자.

class Product에서 name, price를 한번만 쓸거다.

class Product {
 name = '';
 price = 0;
}

class TV {
 size='';
 contructor(name, price, size) { 
   this.name = name,
   this.price = price,
   this.size = size,
 } 
};

 

이 Product안에 있는 name, price 값을 TV가 어떻게 들고 올 것인가?

 

2/ 상속(Inheritance)

 

class Product { // 공통되는 특징을 추출하여 클래스로 정의하는것: '추상화'
 name = '';
 price = 0;
}

class TV extends Product { // TV는 Product라는 class에 있는 개념을 마치 내것인것처럼 쓴다. 확장팩같은것.
 size='';
 contructor(name, price, size) { 
   this.name = name,
   this.price = price,
   this.size = size,
 } 
}

class AC extends Product { //'상속'이라는 개념.
 type='';
}

class Laptop extends Product {
 weight=0;
}

 

## extends

extends를 이용해 TV는 Product라는 class에 있는 개념을 마치 내 것인것처럼 쓴다. (일종의 확장팩같은?)

 

장점은 name, price를 매번 안써도 된다.

만약 Product의 카테고리가 더 늘어날 경우(tv, 에어컨, 노트북 뿐만 아니라 전자렌지 등..) 그 모든 카테고리에 이제 name, price를 써주지 않아도 된다.

 

또한, Product입장에서는 나를 상속하는 카테고리를 모아서 볼 수 있어, 관리가 쉬워진다.

 

만약 공통된 정보인 color가 필요하면, 매 class에 전부 color를 넣어줄 게 아니라 부모 Product class에 넣어주기만 하면 알아서 추가된다.

 

 

## super

constructor 생성자함수는 그대로 둬도 되나?

class Product {
 name = '';
 price = 0;
 constructor(name, price) {
   this.name = name,
   this.price = price,
 }
}

class TV extends Product { // 그대로 두어도 되나? -> No!
 size='';
 contructor(name, price, size) { 
   this.name = name,
   this.price = price,
   this.size = size,
 } 
}

class TV extends Product {
 size='';
 contructor(name, price, size) { 
   super(name, price); //부모 먼저 초기화
   this.size = size; //자식이 가지고 있는 속성
 } 
}

let tv1 = new TV('noona tv', 200, '56inch');
let tv2 = new TV('grand tv', 100, '27inch');

console.log(tv1); // TV { name: 'noona tv', price: 200, size: '56inch' }
console.log(tv2); // TV { name: 'grand tv', price: 100, size: '27inch' }
console.log(tv1.name) // 'noona tv'

 

생성자함수에서 this가 여기에 있나?

→ 아니다. 부모인 Product에 있다.

그래서 생성자함수에 "this.name = name" 이렇게 해줄 수 없다.

 

이제는 this가 아니라, super를 써야한다.

super는 내 상위 클래스를 부를때 쓴다. 이 super라는 건, 상속한 Product를 부른다.

부모 class에서 constructor를 써줄 수 있다.

 

class TV에는 name, price가 없지만 마치 내꺼인 것 마냥 잘 쓰고 있다.

tv1.name 이렇게 콘솔을 찍어봐도 'noona tv' 이렇게 잘 나온다.

 

 

3/ 캡슐화(Encapsulation)

함수를 정의할 수도 있다.

그럼 함수도 가져다가 쓸 수 있나?

class Product {
 name = '';
 price = 0;
 constructor(name, price) {
   this.name = name,
   this.price = price,
 }
 
 getPrice() { // 함수도 class 안에 정의할 수 있다.
   return this.price + '만원';
 }
 
 setPrice(price) {
   if(price < 0) {
     throw new Error('마이너스값 안됨');
   }
   this.price = price;
 }
}

// 변수들과 변수와 관련된 함수들을 같은 클래스에 넣을 수 있다. -> 캡슐화

class TV extends Product {
 size='';
 contructor(name, price, size) { 
   super(name, price);
   this.size = size;
 } 
}

let tv1 = new TV('noona tv', 200, '56inch');
tv1.setPrice(-1000); // error가 뜬다. Error: 마이너스값 안됨

 

변수(데이터)들과 변수(데이터)와 관련된 함수들을 같이 클래스에 넣어서 패키지처럼 쓸 수 있다.

캡슐화

 

캡슐화를 하게되면 변수에 직접적으로 유저가 접근하는 것을 막을 수 있다.

이를 통해 데이터를 보호하고, 외부에서 직접 접근하지 못하도록 제한함으로써 객체의 상태를 안전하게 유지할 수 있다.

 

위 예시에서 getPrice함수를 호출해서 원하는 형태의 값을 줄 수 있다.

 

tv1.price = -1000 이런 값이 저장되면 안된다.

이런 제약사항들을 걸어줄 수 있다. (위 setPrice함수)

 

 

4/ 다형성(Polymorphism)

: 동일한 메서드나 연산자가 다양한 객체에서 다양하게 동작할 수 있는 능력.

메서드 오버로딩(Overloading)과 메서드 오버라이딩(Overriding)을 통해 구현된다.

- 오버로딩 : 동일한 메서드 이름을 가지지만 매개변수의 개수나 타입을 다르게 정의하여 다양한 형태로 호출할 수 있게 한다.

function greet(entity) {
  return entity.greet();
}

class Dog {
  greet() {
    return 'Woof!';
  }
}

class Cat {
  greet() {
    return 'Meow!';
  }
}

const myDog = new Dog();
const myCat = new Cat();

console.log(greet(myDog)); // 출력: Woof!
console.log(greet(myCat)); // 출력: Meow!

- 오버라이딩 : 부모 클래스에서 정의된 메서드를 자식 클래스에서 동일한 이름으로 재정의 할 수 있다.

class Animal {
  speak() {
    return 'Animal speaks';
  }
}

class Dog extends Animal {
  speak() {
    return 'Woof!';
  }
}

class Cat extends Animal {
  speak() {
    return 'Meow!';
  }
}

const myDog = new Dog();
const myCat = new Cat();

console.log(myDog.speak()); // 출력: Woof!
console.log(myCat.speak()); // 출력: Meow!

 

## 이미 우리가 사용하고 있던 객체지향 개념들

let array = [3,2,1];
array.sort();
array.map();

 

이렇게 array라는 type도 사실은 class라는 객체 형태로 저장이 되어있다.

객체 안에는 sort라는 함수가 저장이 되어있다.

 

그리고 사실 let array = [3, 2, 1]은 다른 표현으로

let array  = new Array(3,2,1)로도 표현이 된다. (Array는 생성자함수)

이것도 Array라는 class인데, 이 Array도 JS에서 제공하는 class.

class Array {
 sort(){},
 map(){},
}

 

객체지향이라는 개념을 한번 이해하기 시작하면 내가 하는 모든 프로그래밍이 말이 된다.

이 sort함수는 어디서 왔어? => Array에서 왔어. Array는 뭐야? Array는 class야.

이 Array는 JS에서 온거야.

 

 

이러한 특징을 지닌 프로그래밍을 하는 방식을 '객체지향 프로그래밍'이라고 한다.

 

 

# 자바스크립트는 객체지향 프로그래밍 언어?

▶ 자바스크립트는 명령형, 함수형, 프로토타입 기반 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어이다.

간혹 C++이나 Java같은 "클래스 기반 객체지향 프로그래밍 언어"의 특징인 클래스, 상속, 캡슐화를 위한 키워드인 public, private, protected등이 없어서 자바스크립트는 객체지향 언어가 아니라고 오해하는 경우가 있다.

 

하지만 자바스크립트는 '클래스 기반 객체지향 프로그래밍 언어'보다 효율적이며 더 강력한 객체지향 프로그래밍 능력을 지니고 있는 '프로토타입 기반의 객체지향 프로그래밍 언어' 이다.

 

 

** ES6에 클래스가 도입되었다. 하지만 ES6의 클래스가 기존의 프로토타입 기반 객체지향 모델을 폐지하고 새로운 객체지향 모델을 제공하는 것은 아니다.

사실 클래스도 함수이며, 기존 프로토타입 기반 패턴의 문법적 설탕이라고 볼 수 있다.

클래스와 생성자 함수는 모두 프로토타입 기반의 인스턴스를 생성하지만, 정확히 동일하게 동작하지는 않는다.

 

 

이렇게 객체지향은 프로그래밍 방법론이기 때문에, 객체지향 언어라 불리는 언어들에서만 객체지향 프로그래밍이 가능한 건 아니다.

다만 이를 구현하기 쉽게 언어가 만들어진 것 뿐이다.

많은 작업들을 객체지향을 이용해서 보다 편리하게 할 수 있지만, 일부 게임 등 특성상 객체지향이 잘 사용되지 않는 분야도 있다.

유용하지만 만능은 아니라는 것!

 

이제는 자바스크립트, 그리고 함수형이라 불리는 여러 언어들도 객체지향적 설계를 포함하기 때문에 객체지향 프로그래밍을 잘 공부해놓으면 사용유무를 떠나서 프로그래밍에 대한 한층 깊은 통찰력을 갖게 된다.

 

 

 

참고

* https://www.youtube.com/watch?v=fECCYukfVok&t=1338s

 

* 책: 모던자바스크립트딥다이브

* 영상 : 얄팍한 코딩사전

https://www.youtube.com/watch?v=vrhIxBWSJ04&t=609s