객체 지향 프로그래밍은, 절차 지향 프로그래밍과는 다르게 데이터와 기능을 한곳에 묶어서 처리한다. 속성과 메서드가 하나의 "객체"라는 개념에 포함되며, 이는 자바스크립트 내장 타입인 object(이하, object literal)와는 다르게, 클래스(Class)라는 이름으로 부른다.
메서드 호출은 객체.메서드() 과 같이 객체 내에 메서드를 호출하는 방법을 의미합니다.
단순 객체를 사용한 이러한 예제를 흔히 볼 수 있습니다. 다음은 카운터를 구현한 예제입니다. 직접 코드를 쳐서 실습해 봅시다.
⚠️ 메서드 호출 방식을 이용할 때에는 화살표 함수를 쓰지 않습니다. 그 이유는 mdn 화살표 함수 설명에서 찾을 수 있습니다.
바인딩 되지 않은 this
화살표 함수가 나오기 전까지는, 모든 새로운 함수는, 어떻게 그 함수가 호출되는지에 따라 자신의 this 값을 정의했습니다:
이 함수가 생성자인 경우는 새로운 객체
엄격 모드 함수 호출에서는 undefined
함수가 "객체 메서드"로서 호출된 경우 문맥 객체
등등 이는 객체 지향 스타일로 프로그래밍할 때 별로 좋지않습니다.function Person() { // Person() 생성자는 `this`를 자신의 인스턴스로 정의. this.age = 0; setInterval(function growUp() { // 비엄격 모드에서, growUp() 함수는 `this`를 // 전역 객체로 정의하고, 이는 Person() 생성자에 // 정의된 `this`와 다름. this.age++; }, 1000); } var p = new Person();
위의 counter1은 단 하나의 객체만 만들 수 있습니다. 만약 똑같은 기능을 하는 카운터가 여러 개가 필요하다면, 이 코드를 여러 번 만들어야 할까요? 같은 코드를 그대로 복사/붙여넣기 해야 하므로, 재사용성이 떨어집니다.
똑같은 기능을 하는 카운터를 여러 개 만드는 방법 중 하나는, 아래 예제 코드와 같이 클로저 모듈 패턴을 이용할 수 있습니다.
function makeCounter() {
let value = 0;
return {
increase: function() {
value++;
},
decrease: function() {
value--;
},
getValue: function() {
return value;
}
}
}
let counter1 = makeCounter()
counter1.increase()
counter1.getValue() // 1
let counter2 = makeCounter()
counter2.decrease()
counter2.decrease()
counter2.getValue() // -2
똑같은 기능을 하는 ageCounter를 여러 개 만들기 위해서 클로저 모듈 패턴을 이용했습니다. 클로저 모듈 패턴을 이용함으로써 같은 코드를 그대로 복사/붙여넣기 하지 않고 재사용할 수 있게 되었습니다.
객체지향의 개념이 없는 절차적 언어
객체지향 프로그래밍이라는 패러다임이 등장하면서, 단순히 별개의 변수와 함수로 순차적으로 작동하는 것을 넘어, 데이터의 접근과, 데이터의 처리 과정에 대한 모형을 만들어 내는 방식을 고안해냈다.
따라서 데이터와 기능이 별개로 취급되지 않고, 한 번에 묶여서 처리할 수 있게 되었다
OOP는 프로그램 설계 철학이다.
OOP는 프로그램 설계 철학 중 하나이다. OOP는 객체로 그룹화 된다. 이 객체는 한번 만들고 나면, 메모리상에서 반환되기 전까지 객체 내의 모든 것이 유지된다.
객체내에는 "데이터와 기능이 함게 있다"라는 원칙에 따라 메서드와 속성이 존재한다. 예를들어 모든 자동차는 공통적인 기능과 고유의 속성이 있다. 속도를 낸다든지 주유를 한다든지의 기능이 존재한다. 속성으로는 색상, 최고속력 혹은 탑승 인원등과 같은 고유의 데이터가 존재한다.
예로 새로운 객체를 만들 때, "이번에 만들 자동차는 빨간색에 최고 속력은 200km를 내도록 만들어 보자!"와 같이 속성에 고유한 값을 부여할 수 있다.
클래스는, 세부 사항(속성)이 들어가지 않은 청사진이다. 세부 사항만 넣는다면, 객체가 되는 것이다. JavaScript에서 사용하는 용어와 별개로 클래스를 통해 만들어진 객체를 특별히 인스턴스 객체, 줄여서 인스턴스라고 부른다.
그렇다면, 세부 사항은 언제 어떻게 넣어줘야 할까? 이 역할을 하는 것이 바로 생성자이다. 생성자를 통해 세부 사항(속성)을 넣어준다. 함수에 인자를 넣듯, 속성을 넣을 수 있다.
여기 적절한 예시가 있다.
자동차에는 색상, 가격, 속력과 같은 고유의 속성이 있다.
시작, 후진, 전진, 멈춤과 같이 자동차의 기능이 메서드로 존재한다.
다음은 객체지향 프로그래밍의 주요 개념이다.
- Encapsulation(캡슐화)
- Inheritance(상속)
- Abstraction(추상화)
- Polymorphism(다형성)
Encapsulation(캡슐화)
- 데이터와 기능을 하나의 단위로 묶는 것.
- 은닉(hoding): 구현은 숨기고, 동작은 노출(내부데이터나 구현이 외부로 노출되지 않도록 만드는 것)
- 느슨한 결합유라: 언제든 구현을 수정할 수 있음(코드 실행 순서에 따라 절차적으로 코드를 작성하는 것이 아니라, 코드가 상징하는 실제 모습과 닮게 코드를 모아 결합하는 것을 의미)
Inheritance(상속)
- 부모클래스의 특징을 자식 클래스가 물려받는다는 것
- 기본클래스(base class) 의 특징을 파생클래스(derived class)가 상속받는다 로 표현
- 사람이라는 클래스가 있다고 가정하고, 추가적으로 학생 클래스를 작성한다고 생각해보면, 앞서 구현했던 사람 클래스의 속성과 메서드를 다시 구현한다면 비효율적일 것이다. 따라서 학생클래스는 사람 클래스를 상속받을 수 있다.
Abstraction(추상화)
- 내부 구현은 복잡하지만 노출되는 부분은 단순하게 만든다는 개렴
- 캡슐화와 비교하면, 캡슐화는 데이터의 은닉에 포커스가 맞춰져 있다면, 추상화는 클래스를 사용하는 사람이 필요하지 않은 메서드 등을 노출시키지 않고, 단순한 이름으로 정의하는 것에 포커스
Polymorphism(다형성)
- 다형성의 poly는 "많은" , morph는 "형태"라는 뜻을 가지고 있다. 즉, "다양한 형태"를 가질 수 있다는 말이다.
- 객체역시 똑같은 매서드라 하더라도, 다양한 방식으로 구현될 수 있다.
다형성을 HTML 엘리먼트를 예로 들어보면, 앞서 DOM을 배울 때 Textarea,Select,Checkbox 등을 모두 Element라고 부른다.
이 엘리먼트를 직접 구현한다고 생각해보자,
모든 엘리먼트들은 전부 객체이므로, 내부적으로 모양을 그리고 화면에 뿌리는 메서드가 존재할 것이다. 이 메서드가 render라는 이름을 갖고 있다고 가정해보자.
이경우에는 TextBox, Select, Checkbox의 공통 부모인 HTML Element라는 클래스에 render라는 메서드를 만들고 상속을 받게 만들 수 있었다. 이때 다형성의 핵심은 같은 이름의 render 라는 메서드가 조금씩 다르게 작동한다는데 있다.
TextBox는 가로로 긴 네무 상자와 커서가 있는 형태일 것이고, Select 박스는 눌렀을 때 선택지가 나오도록 화면에 그려야 할 것이다. 이처럼 같은 이름을 가진 매서드라도 조금씩 다르게 작동한다. 이것이 다형성이다.
Polymorphism(다형성)이 없다면, 기본(부모)클래스에 종류별로 분기를 시켜서 하나하나 다르게 만들어야 할 것이다.
객체 지향 프로그래밍 패러다임을 따라해 보면, 사람이 세계를 보고 이해하는 방법과 매우 흡사하다고 느끼게 될 것이다.
코드 상에서, 혹은 화면에 보이는 하나의 요소를 객체 단위로 구분시켜서 생각하면, 보다 이해하기 쉬운 코드를 작성할 수 있게 된다.
OOP의 특성을 이해하고 잘 사용하면 좋은 설계를 할 수 있다.
추가
은닉화!! 는 JavaScript에서 가능하지 않지만
Java 에서는 private 키워드를 이용하여.
TypeScript에서는 interface 키워드를 이용하여 구현 가능하다.