초기 computer programming의 역사

ENIAC 을 기억하는가? computer의 역사를 보면 항상 등장하는 computer이며 그만큼 친숙한 기계이기도 하다. 진공관으로 만들어져 있으며 Bug 라는 용어가 만들어지게 한 장본인 이기도 하다. 이 컴퓨터는 제 2차대전 때 암호문을 해독하는데 사용되었다는 얘기로 유명하며 크기 역시 유명하다. 이렇게 사용된 진공관은 이후 트랜지스터와 IC로 발전되어 소형화되었고, 보다 고성능을 가지게 되었다. 지금 우리가 사용하는 computer의 원형이 되는 셈이다. 이렇게 초기의 진공관 컴퓨터들이 현재 사용하는 computer의 원형이 되었다면 분명 사용을 위해 programming을 했을텐데, 어떤 방법으로 프로그래밍을 했을까?

천공카드 – 또는 punch Card- 라는 조금 굵은 용지에 구멍을 뚫어서 그걸 ENIAC에 입력하는 방식을 사용했다.

그 당시는 지금처럼 I/O device (키보드나 모니터) 가 발달하지 않았던 시점이기 때문에 그나마 생각할 수 있었던 방식이 천공카드였다. 구멍을 실수로 잘못 뚫는다면 당연히 프로그래밍은 처음부터 다시 진행되어야만 했다. 물론 프로그램 뿐만이 아니라 프로그램에서 처리하게 될 data도 천공카드로 입력했다. 당연히 지금의 방식에 비해 비효율적이던 방법이지만 역사의 초창기는 늘 이렇기 때문이니 그 사람들을 탓하지는 마시기를.

이후 TR(트랜지스터)과 IC(집적회로)의 발달에 힘입어 전자제품업계는 눈부시게 발전했고 그것은 computer업계에도 영향을 미치기 시작했다. 키보드와 모니터가 만들어졌고 이는 computer에서 중요한 장치가 되었다. 그리고 대부분의 분야가 그렇듯이 초기의 컴퓨터는 대학에서 연구용으로 도입되었다.지금처럼 PC의 형태가 아닌 Host-Torminal 의 형태였다. 중앙 전산실에서 Host 서버가 관리되며 접근이 가능한 곳에 Serial 또는 interface를 사용한 Terminal 단말기들이 설치되는 형태였다. 지금은 없어진 “Digital” 이라는 회사의 “PDP”시리즈는 단연 중심이 있었다.

처음의 Programing은 어셈블러였다. 기계어와 거의 1:1의 대응이 되는 어셈블러는 제대로 된 프로그래밍의 초기격이라 할 수 있었다. 이후 Dennis MacAlistair Ritchie 와 Ken Thompson 에 의해 C언어가 만들어졌다. 해커들은 열광했다. C 언어는 강력하며 기계어보다 사용하기에 훨씬 쉬웠다. Berkely 에서는 UNIX를 C를 이용해서 만들기 시작했다. Generc OS 의 시대가 오기 시작했고 C언어의 시대가 오기 시작했다. 효율성을 보다 중시한 프로그래밍이 요구되었으며 당시 컴퓨터의 성능을 생각하면 이는 당연한 상황이었다. 구동에 시간이 오래 걸리는 프로그램을 좋아하는 사람은 없었기 때문이다.

이렇게 Enterprise 환경이 발달하는 동안…(진행중)

Design Pattern이 객체지향에서 필요한 이유

객체지향은 Software Modeling 의 방향을 인간에서 찾는다. 인간의 인식과 물리를 넘는 논리적 개념으로 현상을 분석하고 설명하며 구현한다. 또한 논리적 사고의 영역에서 설계적 효율성과 구현의 문제를 고민하는 분야이기도 하다.

Computer Science 에서 Design Pattern은 단기간에 등장한 것이 아니다.

고수들의 수많은 코딩과 경험에서 공통점을 찾고, 그것을 다듬어 Pattern화를 시키고, 분류 기준을 만들어서 정리한것이 Design Pattern 이다. 물론 우리가 알 지 못하는 더 많은 Pattern 이 존재하며 그런것들은 지금도 연구되어 새로이 논문이 되어 나타난다. 이렇게 효율성에 대해 정제된 결과가 바로 Design Pattern이다.

객체지향은 문명이 발전되어 온 것 처럼 지금도 발전되고 있다. 꾸준히 효율성과 편의성을 찾으며 멈추는 일 또한 없다. 이런 과정에서 Design Pattern이 중요해지는 것은 어찌보면 당연한 일이다. 발전을 위해서는 과거의 복기가 필요하며 과거의 기록에서 Pattern을 찾아내서 정리하고 다듬는 Design Pattern 은 객체지향 프로그램을 좀 더 좋은것으로 유지하기위한 필요요소 일 수 밖에 없다고 생각한다. 정리된 Pattern은 새로운 기능이나 library(Linked List의 구현체등)형태로 나타나는 경우가 있다. 이 때 관련된 이론을 잘 알아둔다면 이러한 것들을 사용하는 데에도 더 도움이 될 것이 아닐까?

Class 와 Obejct(instance). 그리고 Factory

객체지향에서 Class 의 존재는 매우 중요하다. inheritance(상속성), encapsulate(캡슐화), polymorphism(다형성) 의 세가지로 대표되는 객체지향의 특성은 대부분의 객체지향 언어에서 제공하는 Class 에 포함되어 있다. 사실 살펴보고 싶은것은 너무나 잘 알려진 앞의 3가지 특성이 아니라 Class 와 instance 의 관계에 대한 것이다.

당신은 Class 를 무엇이라고 생각하는가? 우리는 Class 를 무엇이라고 정의할 수 있을까? Prototype?(물론 패턴이름을 말하는것은 아니다). 또는 어떠한것의 원형(Orign) 이라고 생각할 수도 있을지 모른다. 또는 과자등을 찍어내기 위한 형틀에 가까운 무언가일지도 모른다.

약간 다른 얘기가 되겠지만 조금 옆길로 새보도록 하자. 컵을 공산품으로 만들기 위한 과정을 고민해보자. 일단 컵의 원형부터 먼저 만들어야 할 것이다. 그리고 시행착오를 거쳐 대량생산이 가능한 형태로 다듬어야 공산품으로서는 쓸만할 것이다. 이렇게 하나의 컵이 만들어지게 되면 생산을 위한 준비과정으로 들어가야 한다. 만들어진 시험작을 이용해서 형틀을 만들것이다. 그리고 만들어진 형틀을 이용해서 생산설비를 만들것이다. 만들어진 설비를 이용해서 생산을 시작하면 된다. 물론 필요한 수량만큼 생산을 해야 낭비가 없을것이다.

다시 본론으로 돌아와서 Computer Software 의 범용적 특징을 되새겨보도록 하자(물론 특별한 조건등은 그다지 고민하지 않기로 한다). File(또는 software)는 일반적인 경우 원래의 위치에서 다른위치(같은 물리적 disk내의 다른위치일수도 있다)로 복사(copy) 작업을 진행하는 경우 원본의 내부 또는 외부가 전혀 손상되지 않는다. 원본과 그대로 같은 내용의(inode 또는 생성일등은 틀려질 수 있겠지만) 복제품이 만들어 지는 것이다. 이렇게 만들어진 복제품은 완전히 원본과 동일한 동작을 하게된다. 이런 복사작업은 저장 미디어 뿐만이 아니라 로딩된 물리메모리에서도 동일한 개념으로 동작하게 된다.

Class 는 어떠한 객체를 생성하는 형틀의 역할을 한다. 그 자체로도 필요한 기능을 가지고 사용될 수도 있지만 Class 와 꼭 닮은 객체를 만드는데로 사용될 수 있다. 자루까지 쇠로 만들어진 튼튼한 망치정도면 적당한 비유가 될까? 망치로서의 용도도 충분히 가지고 있지만 그 자체로 다른 망치를 만들어내는 원형(prototype)의 역할도 할 수 있다. 물론 Computer 의 세계는 실제 세계보다는 좀 더 논리적인 개념하에서 동작하지만…

이렇게 원형이 되는 경우를 Class 라고 하며 이를 이용해서 생성된 결과물의 존재를 객체(Obejct) 또는 인스턴스(instance) 라고 한다. 이렇게 instance 를 찍어내는 Class 는 그 스스로 무언가를 생성하는것과 비슷한 동작을 하게 된다. 이런 일련의 과정을 짚어보면 Class 를 공장(Factory)라고 볼 수도 있을 것이다. 그럼 이런 과정들을 위해 만들어진 Class 들을 뜯어서 다시 손보고 개량하는 작업은 어떻게 불러야 할까? 그렇다. 답은 명료하다. 바로 “refactoring” 이다. 필자는 이러한 내용들이 refactoring 을 설명하는 대에 대한 가장 정확한 답이라고 확신한다.

instance 를 취급하는 방법은 각 환경에 따라 다르다. compiler 는 class 를 통해서 instance 를 생성할때 real memory(protected) 에 생성되는 포인터(pointer) 로서 이것을 취급한다. 물론 compiler 는 interperter 가 아니기 때문에, 이러한 동작들은 binary 가 실행되는 시점(runtime) 에 진행되게 된다.

Virtual Machine 의 대표격인 smalltalk 에서는 instance 의 생성작업은 좀 더 복잡한 동작을 가지게 된다. 근래에 가장 유명한 Smalltalk 구현체인 Pharo(CogVM) 을 예로 들어보자. CogVM 을 실행하면 주어진 인자(argument)에 따라 Smalltalk image 를 로딩한다. 이때 Smalltalk image 안의 모든 class 를 로딩해서 unique instance 로 메모리에 생성한다(물론 image 의 동작에는 이런 부분이 snapshot 형태로 포함되어 있기도 하지만.. 이런 부분은 논외로 한다). smalltalk 의 Class Browser(nautilus)에서 보이는 모든 class 는 사실 unique instance 를 이미 가지고 있다고 보는것이 맞다. class side 의 메서드(Method)를 사용하는 경우라면, 내부에서는 Class 에 대응되는 unique instance 를 통해서 해당 메서드를 실행하게 된다. 물론 Class 를 이용해서 instance 를 생성하게 되면 unique instance 와는 전혀다른 새로운 instance 를 생성하게 되는데, 이때 바탕이 되는 class 의 생성자(creator)를 이용하기 위해서는 Class 에 대응되는 unique instance 의 생성자 메서드를 이용하게 된다. 대부분의 Smalltalk Systam 은 재념적으로 비슷한 동작을 하도록 구성되어 있다.

이번 내용은 조금 긴 내용이 되었지만, 요점은 다음과 같다.

* Class == Factory
* sintance == Class 의 전사체
* class 로 만들어진 library 를 정리하는 작업 == refactoring
* Virtual Machine 환경과 Compiler 언어의 instance 취급방법에는 차이가 있다.

이렇듯 class 와 instance 의 개념을 좀 더 숙지하고 나면 이후 객체지향을 이해하는데 좀 더 도움이 될 것이다.