초기 프로그래밍 방식은 절차적 프로그래밍 방식이었다. 어떠한 논리를 알맞은 논리 순서대로 써내려가는 것이다. 이러한 경우, 코드의 덩치가 커질수록 복잡해지고 유지보수가 어려워진다.
이후 프로그램을 함수 단위로 나누고 호출을 하는 구조적 프로그래밍 방식이 등장하면서 앞선 문제는 일정 부분 해소되는 듯 했다.
하지만 함수를 통해 데이터의 처리 방법은 구조화했지만 데이터 자체는 구조화하지 못했고, 실행 콘텍스트를 저장할 방법도 없었다. 그래서 프로그래머는 가능한 모든 변수를 다 조사해야 하는 불가능한 어려움에 봉착했다.
이를 극복하기 위한 대안으로 객체 지향 프로그래밍이 등장했다. 작은 문제들의 독립성/신뢰성을 보장해 이들을 조합해 큰 문제를 해결하는 방식인 상향식(BOTTOM-UP) 해결법을 도입했다.
현대에 이르러 객체 지향 프로그래밍도 매우 복잡해지면서 이를 간결하게 정리할 필요성이 생겼고, 디자인 패턴(프로그래밍 형식을 정하는 일종의 약속)이라는 것이 등장했다.
상속화(Encapsulation): 변수와 함수를 하나로 묶는 것을 데이터를 데이터의 번들링이라고 하는데, 객체 지향에서 이 번들링을 클래스를 통해 구현한다. 해당 클래스의 인스턴스 생성을 통해서 클래스 안에 포함된 변수와 메소드에 접근한다.
정보 은닉(Informatino Hiding): 클래스는 외부에서는 노출 가능한 메소드에만 접근 가능하기 때문에 클래스 내부를 알 수 없도록 설계된다. 따라서 정보를 제한적으로 제공하고 클래스끼리의 결합도를 낮춘다. (일반적인 접근 제한: public: 클래스의 외부에서 사용 가능, protected: 상속 받은 자식 클래스에 노출, private: 해당 클래스의 내부에서만 사용 가능, Python은 해당 기능을 사용하지 않는다)
Inheritance: 자식 클래스는 부모 클래스의 특성과 기능을 물려받을 수 있다. 이것을 상속(Ingeritance)라고 한다. 자식 클래스는 상속받은 기능을 수정해서 사용할 수 있다.
다형성(Polymorphism): 하나의 변수, 또는 함수가 상황에 따라 다른 의미로 해석될 수 있는 것을 말한다. 상위 클래스와 그것을 상속받는 다수의 하위 클래스가 있다고 가정할 경우, 하위 클래스에서 다양한 방식으로 변형, 수정 가능시켜 다른 형태로 만들 수 있다.
멤버 변수 | 멤버 함수: 기본적으로 클래스 내부에 있는 어떤 항목들을 가리킬 때, 멤버라는 말을 사용한다. 따라서 멤버 변수란 클래스 내에서 사용하는 변수를 말한다. 그리고 멤버 함수란 이러한 변수를 처리하는 함수들을 가리킨다.
클래스, 객체, 인스턴스: 클래스란 객체를 만들어 내기 위한 설계도, 그리고 연관되어 있는 변수와 메소드의 집합이다. 객체란 클래스대로 생성된 실체, 구현될 대상(OBJECT)를 말한다. 인스턴스랑 실제 메모리에 할당된 객체를 말한다.
자바스크립트는 객체 지향 프로그래밍 언어에서 기본적으로 제공하는 클래스를 지원하지 않는다. 대신 프로토타입이라는 것을 이용해 OOP를 지원한다. ECMA6에는 Class 문법이 추가되어 Class를 사용할 수 있지만, 그렇다고 자바스크립트가 Prototype => Class로 바뀌었다는 것을 의미하지는 않는다.
자바스크립트에서 클래스 대신 function과 new 키워드를 사용해 클래스를 표현할 수 있다. 하지만 이렇게 할 경우, 인스턴스를 생성할 때마다 중복된 멤버 변수가 계속 메모리에 할당될 수 밖에 없다. 이럴 때 Prototype을 사용하면 된다.
자바스크립트의 Prototype에는 Prototype Link와 Prototype Object라는 것이 존재한다. Prototype Object는 함수를 정의하고 생성할 때, 함께 생성된다. 그리고 해당 함수의 prototype이라는 속성을 통해 Prototype Object에 접근할 수 있다.
Prototype Object는 기본 속성으로 constructor
와 __proto__
를 가지고 있다. 여기서 constructor
는 해당 함수를 가리키고, __proto__
는 Prototype Link이다.
__proto__
는 모든 객체가 가지고 있는 속성인데, 이는 부모 함수의 Prototype Object를 가리킨다. 특정 객체의 속성을 조회할 때, 만약 특정 객체가 그 속성을 갖고 있지 않을 경우 해당 속성을 찾기 위해 proto가 가리키고 있는 부모 함수의 Prototype Object를 조회한다. 해당 속성을 찾을 때까지 타고 올라가며 최종적으로 Object의 Prototype Object까지 조회한다. 이와 같은 형태를 Prototype Chain이라고 한다.
Prototype은 여러 객체가 하나의 객체를 공유하고 있기 때문에 메모리를 하나만 사용한다. 따라서 객체를 여러개 생성해야 할 때는 Prototype을 사용하는 것이 효율적이다. 그리고 Prototype의 속성만 수정함으로써 모든 객체를 재설정할 수도 있다.
그러나 Prototype Chain이 깊어질 수록 검색하는 속성 탐색 시간이 늘어난다. 따라서 특정 객체에서 자주 참조해야 하는 속성이라면 Prototype이 아니라 객체 자체의 속성으로 만드는 것이 효율적일 것이다.
클래스 기반에서는 상속을 사용한다. 하지만 프로토타입 기반에서는 객체를 원형으로 하여 복제를 하고, 이를 이용해 객체의 동작 방식에 접근할 수 있다. 이와 같은 행위를 "위임"이라 하며, "위임"을 통해 상속의 과정을 대체한다고 볼 수 있다.
클래스를 사용하게 되면 객체의 만듦새와 기능을 정의하게 된다. 그러면서 클래스에서 생성된 인스턴스는 클래스에서 정의된 타입에 맞추어 주어진 방식대로 동작할 수 밖에 없는 일종의 안정성과 예측성이 보장된다.
프로토타입 기반의 객체는 프로토타입을 참조하는 것이기 때문에 프로토타입과 자식 객체가 메모리 혹은 구조적 유사성을 가질 필요가 없다.
클래스를 상속해서 설계하게 되면 상위 클래스에서 필드 및 공통 메소드를 상속해서 사용할 수 있기 때문에 소스 코드의 양이 줄어들고 기능을 확장하기 용이하다. 그러나 상속 구조가 복잡해지면 상위 클래스에서 어떤 변화가 생겼을 때, 하위 클래스에 어떤 영향을 주는지 예측하기 힘들어질 수도 있고 예상과 다르게 작동할 수도 있다.
좋은 코드에 대한 재미있는 농담이 있다.
The Only Valid Measurement of Code Quality: WTFs / Minute
우스게 소리이지만 한편 일리 있다고도 생각한다. 좋은 코드란 읽기 쉽고, 재사용 가능하며, 리팩토링 가능하도록 작성하는 것이라고 생각한다.
아래는 클린코드 자바스크립트 버전에 대한 간략한 소개이다. 자세한 것은 클린코드 참조
변수
가능하면 의도가 명확하고, 검색 가능하도록 변수명 짓기
함수
가능하면 너무 많은 3개 이상의 인자는 피하기
1개의 함수는 1개의 기능만 하기
함수명은 직관적으로 짓기
중복된 코드 피하기
Object.assign을 사용하기
매개변수로 플래그 사용하지 않기
사이드 이펙트 피하기
명령형 프로그래밍이 아닌, 함수형 프로그래밍 지향하기
조건문 캡슐화 하기
죽은 코드 지우기
클래스
상속보다는 조합을 사용하기
SOLID 원칙 지키기
등등...