본 강좌는 예전 홈페이지에 있던 내용을 다시 편집한 것입니다.

좋은 프로그램이란?
==================


  어떤 프로그램이 좋은 프로그램이고 어떤 것은 좋지 않은 프로그램인지의 절대적인 구분은 없지만 상대적인 구분은 존재한다. 소프트웨어 개발회사에서 일을 하고 싶은 사람이라면 좋은 프로그램을 만드는 방법에 대해 어느정도 이해할 필요가 있다. 나의 그동안의 경험을 통해 좋은 프로그램과 그렇지 못한 프로그램, 그리고 좋은 프로그램에 대한 오해에 대해 몇가지 써보도록 하겠다.



1. 읽기 쉽고 이해하기 쉬워야 한다

  역설적이긴 하지만 읽기 불편한 프로그램은 만들기가 쉽다. 반대로 똑같은 기능을 수행하면서 읽기 좋은 프로그램을 만드는데에는 추가적인 노력이 들어간다. 적어도 전문적인 프로그램을 만드는 사람은 자신이 만드는 프로그램의 소스코드가 다른 사람(동료, 상사, 또는 고객) 에 의해 해독될 것을 전제조건으로 하여야 한다. 또한 모든 프로그램은 계속 바뀔 여지를 안고 있기 때문에, 누군가 반드시 이 코드를 보고 기능을 추가할 것을 예상하여야 한다. 이해하기 쉬운 프로그램을 만드는데에는 다음과 같은 사항들이 중요하다


2. 좋은 코딩 습관

  가장 중요한 것은 일관성이 있어야 한다는 점이다

i). 제일 기본 사항은 변수, 함수 이름들을 일관성 있는 작명과, 기능의 목적을 올바로 설명할 수 있는 의미있는 이름을 만드는 것이 중요하다. 함수의 이름을 지을때에는 그 함수의 내부에서 어떤 일이 일어난다를 설명하기보다는, 무엇을 하기위한 함수인가를 설명하는데 중점을 두어야 한다.

ii). 적절한 주석을 통한 해설이 중요하다. 뻔한 내용까지 일일이 주석을 다는 것은 공간낭비이다. 주석은 코드 자체가 설명해주지 않는 숨은 의미를 서술하는데 쓰이거나, 앞으로 나올 코드의 덩어리가 어떤 취지의 일을 하는지 전반적인 설명을 해주는데 쓰여야 한다

iii) 기능의 적절한 분배가 되어야 한다. 함수 하나가 너무 많은 일을 수행하도록 만드는 것은 여러가지점에서 문제의 소지가 된다. 가능한 한 함수 하나가 화면 1 개를 넘지 않도록 적절한 기능의 분배가 이루어져야 한다.



3. 효율적인 수행을 하여야 한다

  프로그램은 가능한한 최소의 시스템 자원(cpu, memory, io 억세스등) 을 소모하면서 최소한의 시간내에 원하는 작업을 수행할 수 있어야 한다. 그를 위해 다음과 같은 점들이 중요하다

  첫째로, 다룰 플랫폼의 아키텍춰에 대한 확실한 이해가 필요하다. 예를 들어 3d 프로그래밍을 한다면 Direct3D, 네트웍 프로그래밍을 한다면 WinSock, TCP/IP 같은 플랫폼의 아키텍춰에 대해 완벽한 이해를 하여야만 한다. 그렇지 않고서도 돌아가는 프로그램을 만들 수는 있지만 십중팔구 최대한의 효율을 내지 못할 것이 분명하다

  둘째로, 올바른 알고리즘과 데이타구조의 선택을 해야 한다. 자신이 다룰 데이타가 어떤 식으로 사용될 것인지에 대해 역시 심사숙고해보아야 한다. 예를 들어 길찾기 알고리즘을 구현한다고 하면 PriorityQueue 에 대해서 잘 알고 있어야 할 필요가 있을 것이며, 스크립트 인터프리터를 만들거나 파일 리소스 매니저 같은 것을 만든다면 HashTable 을 만들어 사용하는 방법에 대해 잘 알고 있어야 할 것이다. 기본적인 자료구조에 대해 이해가 없이는 효율적인 프로그램 디자인은 불가능하다.

  셋째로, 최적화를 하려면 올바르게 해야 한다. 이르는 말로 "설익은 최적화는 모든 버그의 근원이다" 라고 하기도 한다. 디자인이 확실히 결정되기도 전에 세부적인 부분의 최적화를 행했을 경우에는 디자인 자체를 망치게 된다. 올바른 최적화는 디자인 자체의 최적화와 구현이 완료된 이후의 플랫폼에 맞춰진 최적화를 의미한다



4. 적정선에서의 확장성 있는 설계

  프로그램은 어떠한 요구사항을 충족시키기 위해 만들어지는 것이고, 요구사항은 시간이 지나면 항상 변하기 마련이다. 앞으로 어떤 요구사항이 추가되거나 변경될지 완벽하게 예측하는 것은 불가능에 가깝지만 어느정도의 예상은 필수적이다. 하지만 너무 확장상을 강조하게 되면 무엇을 수행하기 위해 프로그래밍을 하는 것인지가 애매해질 것이므로 균형감각있는 디자인이 필요하다. 때로는 미래를 완전히 잘못 예측해서 이상한 디자인이 나오는 경우도 있다. DirectX 2 의 ExecuteBuffer 가 그 대표적인 예이다. 적정선을 정의하는 것은 불가능하다. 그야말로 중용의 도를 말할 수 밖엔 없을 것이다.



5. 모듈화 (적절한 decoupling)

  프로그램을 만들때에는 지나치게 유기적인 구조로 만들게 되면 모듈성이 저하되고, 모듈성의 저하는 재사용성의 저하로 이어진다. 반대로 지나치게 이산적인 구조로 만들게 되면 사용하기가 불편해진다. 적정선을 만드는 것이 프로그램 디자인상의 중요한 과제중 하나다.
  보통 올바른 디자인이 결여된 상태에서 프로그래밍을 하게 되면 지나치게 유기적인 구조로 가는 성향이 있다. 그 이유는 수행속도와 효율이라는 요소가 개발자에게 1차적으로 와닿기 때문이다. 아무래도 융통성이나 확장성, 가독성등의 요소는 효율성이라는 것만큼 와닿지 않는 것이 보통이다.
  효율을 해치지 않으면서 적절한 모듈화를 하기 위해서 쓰이는 방법에는 여러가지가 있는데 대표적인 방법으로 소프트웨어를 레이어(layer)로 분리하는 방식과 중간자(interrogate) 를 두는 방식이 있다.
레이어방식의 대표적인 사례는 네트워크 프로그래밍의 OSI 7 단계식 구조와 DirectX 의 HEL, HAL 구조가 대표적이다. 다양한 개발자들 (카드제조자, 드라이버 개발자, 운영체제 개발자, 응용프로그램 개발자) 들이 다룰 범위를 분리함으로써 모듈성을 보전하는 것이 레이어방식의 특징이다. 단, 레이어를 남용하게 되면 수행효율에 지장이 올 수 있다
  중간자의 대표적인 사례는 윈도우 프로그래밍의 DC (Device Context) 의 존재이다. 윈도우에 뭔가를 그리기 위해서는, dc 를 확보해 그곳에 그려야만 한다. 대신 개발자는 유저가 사용할 비디오카드의 종류나 해상도, 칼라모드등에 대해서는 신경쓰지 않아도 된다. 이 개념은 더 확장되어 프린터에 뭔가를 그릴때에도 dc 로 통합이 된다. 중간자는 효율적인 모듈화를 위한 도구이다. 중간자가 수행하는 기능은 자기 자신이 처리하는 것일 수도 있고, 자신이 중개할 대상의 기능을 단순히 중개(delegate) 하는 것일 수도 있다. 예를 들어서 DirectX 와 OpenGL 을 동시에 지원하는 그래픽 엔진을 만들기 위해서는 DirectX 와 OpenGL 의 기능의 공통적인 부분을 대행해줄 중간자를 두어야 할 것이다.

imcgames 의 김학규입니다