OOP

곽태민·2023년 7월 12일
0

TIL

목록 보기
29/65
post-custom-banner

개요

Nest.JS로 개발하면서 OOP로 구조가 짜여 있었다. 이렇게 프레임워크에서 만들어주지 않고, Express.JSOOP를 개발하던 와중에 너무 어렵기도하고, 이해가 안되는 부분이 한 두개가 아니라서 정리를 해보려고한다.

우선 구글링을 통해서 알았던 OOP의 장/단점 중 장점인 캡슐화와 단점인 생산성에 대해서 몸소 느끼고 있다.. 정리를 하기 전에 우선 객체 지향에 대해서 정리를 해보려고 한다.


객체 지향 이전의 프로그래밍

객체 지향이 나타나기 전에는 JavaScript에서 Object를 사용하지 않고, string, boolean, number, if, for 등으로만 개발을 했다.

순차적 프로그래밍과 goto

초창기 프로그래밍에서는 순차적으로 실행이 되었다. 현 시점에서도 그 전에 만들어 둔 것과 같은 반복적인 동작이 필요하다는 것을 느끼게 된다.

for이나 while과 같은 반복문이 존재했지만 일부 반복의 범위가 코드를 다시 실행해야 할 경우가 발생하고 함수라는 개념이 없던 때에는 특정 위치로 실행 순서를 강제로 옮기는 goto문을 만들어냈다.

이렇게 강제로 순서를 바꾸면서 코드가 커져가고 이 코드의 흐름을 제어하기 힘들어졌다.

var hp = 100
var mp = 100

gameloop:
...

if(key == 'A') {
	goto magic
}

...
goto gameloop

magic:
mp -= 10

...
goto gameloop

절차적(구조적) 프로그래밍

goto문과 같이 실행 순서를 강제로 바꾸는게 아니라 일정하게 반복되는 코드를 만들고, 해당 코드를 호출하고서 다시 원래 실행 순서로 돌아오는 방식의 Procedural를 통해 개발하는 절차적 프로그래밍이 생기면서 함수 개념이 생겼다.

즉, 절차적 프로그래밍데이터와 데이터를 처리하는 동작을 함수 단위로 코드를 분리하고 재사용하는 형태의 프로그래밍 방식이다.

(함수라는 표현을 쓰지만 함수형 프로그래밍하고는 다름.)

절차적 프로그래밍은 아주 익숙한 방식으로 현재도 간단한 코드들을 작성할 떄 이러한 방식으로 프로그래밍을 한다.

패러다임의 한계

패러다임의 한계는 프로그램의 덩치가 커지면서 알 수 있다. 이러한 방식으로 코드가 커지면 아래와 같은 문제가 생긴다.

절차적 프로그래밍을 기본적으로 사용하면서 전역 변수의 형태로 변수를 만들었지만 그러다 보면 프로그램의 덩치가 커지면 커질수록 변수에 같은 이름을 쓸 수 없게된다.

그러다보니 변수명 관리가 어려워지게 되는데 이름 앞에 prefix가 늘어나면서 매번 이렇게 prefix를 붙이지 않고서 하나의 파일단위 or 모듈단위로 prefix를 부여해서 관리하는 namespace라는 방식이 등장한다.

구조체의 등장

namespace만으로 비슷한 형태의 데이터를 다룬다는 것은 쉽지 않다. 예를 들어 게임을 만들 때 하나의 캐릭터에 속해있는 이름, hp, mp, item 등 구조 형태를 가지게 되는 변수를 만들기 위해서 prefix를 붙여 만들어야 했다.

let character1_name = 'ian.kwak';
let character1_hp = 100;
let character1_mp = 100;

function character1_useSkill() {
	...
	character1_mp = 80; // 변수를 직접 수정하게 된다.
}

character1_do_somthing();

위 코드와 같이 프로그래밍을 하게되면 캐릭터가 2개, 3개, 4개 … 100개 늘어나면 만들어야 할 코드와 중복되는 코드가 눈에 보이게 되는데, 이렇게 서로 연관된 데이터들을 하나로 묶어서 namespace처럼 관리해서 해당 변수에 접근할 수 있는 구조체가 등장한다.

//Struct
let character = {
	name: 'ian.kwak',
	hp: 100,
	mp: 500,
};

function useSkill(character) {
	...
	character.mp -= 10; //변수를 직접 수정하게 됨.
}

do_somthing(character);

위와 같이 단위로 변수들을 하나로 묶어서 변수명의 중복을 줄이고 함수나 배열 등에서 하나의 변수처럼 활용할 수 있게된다. 이렇게 코드의 덩치가 커져도 일관성을 유지하며 코드를 짤 수 있게 됐다.


객체 지향 프로그래밍 등장

구조체가 생기면서 의미를 갖는 데이터들을 구조화 시켜서 프로그래밍을 하니 동작보다는 데이터를 중심으로 코딩하면서 코드의 덩치가 커지더라도 일관성을 유지하기 좋다는 것을 알게된다. 하지만 코드를 한 곳에 모아놓으면 비슷한 패턴이 자주 생기는 것을 느끼게 된다.

const character = {
	name: 'ian.kwak',
	hp: 100,
	mp: 100,
};

function character_attack(charachter) {}
function character_useSkill(character) {}
function character_moveTo(character, toX, toY) {}

위 코드와 같이 특정 구조체만 가지고 동작을 하는 함수들이 만들어진다는 것을 알게 되면서 함수 역시 전역 namespace를 쓰고 있다 보니 character_ 와 같은 prefix를 달아야 한다는 것을 알게 되었다.

구조체에 매번 쓰이는 함수를 하나로 쓰는 방법?

구조체와 항상 쓰이는 함수들을 하나로 묶어서 구조체와 함께 함수까지 포함하는 개념을 만들게 되면서 class가 등장하게 된다.

struct + function(struct, …) = class
구조체 + 구조체를 인자로 가지는 함수 = class

class Character {
	name = 'ian.kwak';
	hp = 1000;
	mp = 1000;

	attack() {}
	useSkill() {}
}

const character = new Character();
character.attack();
character.useSkill();

위와 같이 데이터와 처리 방식을 분리하던 절차식 프로그래밍과 다르게 데이터와 처리 방식이 하나의 모듈로 관리가 되면서

작은 프로그램들이 독립적으로 돌아가는 형태를 띄우고, 프로그램이 덩치가 커져도 작게 프로그램을 만들고 조립하는 방식으로 개발할 수 있다는 것을 느끼게 된다.

이렇게 만드는 설계도를 만들어서 양산하듯이 프로그램을 만들고, 이것들을 조립해서 사용하는 개념으로 classobject가 등장한다.

  • class: 구조체와 함수를 합쳐서 선언하는 것.
  • object: class를 통해서 만들어진 결과물을 값과 동작을 함께 가지고 있는 것.
  • Bottom-up: 작은 문제를 해결하는 것을 모아서 하나의 문제를 해결하는 프로그램을 개발하는 방식

이런 개발방식은 개념이 확장되는데, 프로그램은 모두 객체로 만들어져 있고 객체들 간의 메시지를 주고받는 상호작용으로 이루어진다.

Object-Oriented Programming (OOP) 객체 지향 프로그래밍
⇒ 프로그램을 객체로 바라보는 관점으로 프로그래밍을 하는 것.

재사용이 가능한 객체들을 많이 만들어 놓는 것이 중요하다는 것을 알게되면서 객체의 재사용을 높이기 위해 OOP는 여러 개념들이 추가됐다.

  • 캡슐화
    • 객체를 사용하면서 객체의 모든 데이터에 접근을 할 필요가 없다는 걸 알게 됨.
    • 내부 데이터는 내부에서만, 외부 데이터는 필요한 내용만 만들어 두는 편이 프로그램 안정성 및 사용성 측면에서 효율적임.
    • 외부로 노출해야하는 값과 내부에서만 사용하는 값을 구분하는 기능을 추가
    • 데이터 보호해주는 캡슐로 보고 내부 데이터 접근을 막고, 외부로 보낼 method만 열어두는 특성을 캡슐화라 한다.
    • 이를 통해 객체 안정성을 높이고, 필요 method만 열어두면서 객체 재사용성을 높임
  • 상속
    • 객체가 중심이 되어서 재사용성은 좋지만 객체에는 여러 변수와 함수가 섞여 있어 일부는 재사용을 하고 일부는 달라져야 하는 경우가 발생한다.

    • 상속은 기존 상위 클래스에 기능을 가져와 재사용할 수 있으면서 동시에 새롭게 만든 클래스에 새로운 기능을 추가할 수 있다.

      JavaJavaScript는 단일 상속만 지원, C++ 은 다중 상속 지원된다. Java다중 상속이 OOP 관점에서 크게 유효하지 않다고 생각하지만, 다중 상속이 필요할 수 있다고 인정하면서 대비책으로 Interface를 다중 상속 구현 할 수 있게 해서 임시적으로 다중 상속을 가능하게 해놓았다. 하지만 Interface의 존재가 다중 상속을 위함이라면 다시 생각해봐야 한다.

    • 상속이 필요한 이유 ⇒ 코드 중복 없애기 위해서

      • 코드 중복이 많으면 개발도 힘들지만, 유지보수도 힘듦.
  • 추상화
    • 공통적인 부분을 모아서 상위 개념으로 새롭게 이름을 붙이는 것을 추상화라함.

    • 공통적인 요소나 특징을 추출하는 과정이 추상화라고 생각하면 된다.

      Class Car {
      	name = '';
      
      	move() {}
      	closeDoor() {}	
      }
      
      class Hyundai extends Car {
      	name = 'Genesis';
      	
      	move() {}
      }
      
      class benz extends Car {
      	name = 'CLS';
      
      	move() {}
      }
  • 다형성
    • 상속을 통한 기능을 확장하거나 변경하는 것을 가능하게 해준다.
    • 다형성은 형태가 같은데 다른 기능을 하는 것을 의미한다.
      • 같은 동작이지만 다른 결과물이 나올 때 다형이라고 생각하면 됨.
    • 코드의 재사용, 코드 길이 감소로 유지보수가 용이.
profile
Node.js 백엔드 개발자입니다!
post-custom-banner

0개의 댓글