영원한사랑

객체지향 기법과 코드의 재사용

브래드 피트가 아킬레스를 연기한 영화 ‘트로이(Troy)’는 스케일로 관중을 압도하는, 말하자면 전형적인 헐리우드 영화이다. 어렸을 때 ‘호머 이야기’라는 제목을 달고 나온 아동용 ‘일리아드’를 읽었던 필자에게 어린 시절의 향수를 불러일으키기에 충분했는데, ‘글래디에이터(Gladiator)’ 정도의 재미를 기대한 관객들은 자칫 지루할 수도 있을 것처럼 보였다. 어쨌든 필자에게 있어서 ‘트로이’는(브래드 피트의 과도한 영웅주의가 부담스럽긴 했지만) 어렸을 때 읽었던 영웅들의 이야기를 어른이 되어서 어른의 눈으로 확인하는 기회를 제공해 준 ‘고마운’ 영화였다.

엑스트라 대신 동원된 엄청난 수의 객체
인류사에서 손꼽히는 트로이 전쟁을 묘사한 영화에서 엄청난 수의 ‘엑스트라’가 동원된 것은 놀라운 일이 아니다. 특히 트로이의 왕자 파리스와 스파르타의 왕 메날라우스가 일대일로 결투를 벌이는 장면에서 펼쳐지는 그리스 연합군의 위용은 어마어마하다. 하지만 그 많은 사람을 실제 엑스트라로 동원했을 리는 없다. 화면을 꽉 채우는 엄청난 수의 사람을 묘사하기 위해서 컴퓨터 그래픽스를 이용했을 텐데, 그렇다고 한다면 트로이 전쟁을 재현하기 위해서 ‘엑스트라’ 대신 엄청난 수의 ‘객체(object)’가 동원된 셈이다.
프로그래밍 세계에서 객체라는 말이 사용되기 시작한 것은 80년대 중반 무렵이었다. 따라서 객체라는 존재는 어느덧 나이 스물이 다 된 ‘청년’이 되었다. 요즘 프로그래밍에 입문하는 사람들에게는 객체가 별로 새롭거나 어려운 개념이 아닐지 몰라도 C언어를 중심으로 한 ‘절차적(procedural)’ 프로그래밍을 구사하던 사람들에게는 객체가 쉽게 친해질 수 있는 개념이 아니었다. 객체는 근본적인 패러다임의 변화를 요구했기 때문에 사람들은 흔히 C++나 자바처럼 객체를 지원하는 프로그래밍 언어를 ‘사용’하는 것과 객체라는 개념을 ‘이해’하는 것을 동일한 것으로 착각하기도 했다(둘은 전혀 별개의 문제이다).
예를 들어서 트로이와 그리스 군대의 병사들을 컴퓨터 프로그램으로 제어하라고 했을 때, 객체의 개념을 정확하게 습득하고 있는 프로그래머와 그렇지 않은 프로그래머가 작성하는 알고리즘은(사용하는 프로그래밍 언어에 상관없이) 다를 것이다. 여러 명의 병사를 절차적 기법으로 일일이 제어하려면 많은 수의 복잡한 함수를 ‘if’나 ‘switch’ 같은 조건을 통해서 호출해야 하기 때문에 논리가 복잡해지는 것을 피할 수 없다. 이러한 알고리즘은 병사의 수가 적당한 수준일 때는 상관없지만 그 수가 수천이나 수만에 이르게 되면 현실적으로 넘을 수 없는 한계에 봉착한다. ‘객체지향(object oriented)’ 기법은 바로 이러한 한계를 극복할 수 있는 방법론을 제공하기 위해서 고안되었다.
객체지향에 익숙한 프로그래머라면 다양한 ‘조건’을 이용하는 절차적 프로그래밍의 초식과 달리 ‘칼로 찌르기’, ‘방패로 막기’, ‘달리기’, ‘고함지르기’, 혹은 ‘죽기’처럼 병사가 취할 만한 여러 가지 ‘동작(action)’을 ‘메쏘드(method)’로 구현하고, 키, 체중, 나이와 같이 병사 개인의 모습을 묘사하는 데이터는 ‘속성(attribute)’에 담아서 ‘Soldier’라는 이름의 클래스(class)를 정의할 것이다. 그리고 전쟁 장면을 연출할 때는 Soldier 클래스의 ‘인스턴스(instance)’를 무수하게 생성시킴으로써 웅장한 모습을 나타낼 것이다.

객체, 객체지향이란 무엇인가
프로그래밍의 세계에서 객체지향이라는 말은 흔히 사용되기 때문에 누구나 쉽게 이야기하지만 정작 그 말이 구체적으로 의미하는 바는 모호할 때가 종종 있다. 회사에서 프로그래머를 채용할 때 ‘객체지향’이라는 요구 사항은 항상 들어가는 양념처럼 강조되지만, 그 양념을 뿌려야 하는 이유에 대해서는 누구도 말하지 않는다. 심지어 C++나 자바를 오랫동안 이용한 프로그래머에게 “객체지향이란 무엇인가”라는 질문을 하면 답이 돌아오지 않는 경우가 드물지 않다. 객체를 이용하는 ‘방법’엔 정통한 사람이 객체를 이용하는 ‘이유’에 대해서는 쉽게 답하지 못하는 기이한 상황이 벌어지는 것이다.
객체지향 기법이 갖는 여러 가지 특징 중에서 ‘상속(inheritance)’과 ‘다형성(polymorphism)’은 특별히 자주 언급되는 내용이다. 간단하게 살펴보자면 상속은 이미 정의된 클래스를 다른 클래스로 확장(extend)함으로써 미리 정의된 메쏘드와 속성을 고스란히 간직하고 있는 새로운 클래스를 정의하는 기법이다. 예를 들어서 먹기, 잠자기, 뛰기, 말하기, 죽기 같은 것처럼 사람이라면 누구나 수행하는 기본적인 동작을 정의하는 메쏘드를 담은 Human이라는 클래스가 있다고 하자. 그렇다면 Human 클래스를 확장한 Soldier 클래스는 Human 클래스 안에 이미 정의되어 있는 메쏘드를 다시 정의할 필요 없이 찌르기, 막기, 활쏘기와 같이 군인에게만 해당되는 메쏘드를 정의하면 된다. 다른 기본적인 메쏘드들은 Human 클래스로부터 상속받기 때문이다.

상속과 재사용의 관계
1988년에 『객체지향 프로그래밍 저널(Journal of Object-Oriented Programming)』에 실렸던 랄프 존슨(Ralph E. Johnson)과 브라이언 푸트(Brian Foote)의 논문 「재사용이 가능한 클래스의 설계(Designing Reusable Classes)」를 보면 ‘상속’의 이점이 다음과 같이 설명되어 있다.

“클래스의 상속은 여러 가지 이점을 가지고 있다. 그 하나는 코드의 재사용이 가능하다는 것이다. 여러 개의 클래스에서 공통으로 사용되는 코드는 그들의 슈퍼클래스(superclass) 안에 놓일 수 있기 때문에 각각의 새로운 클래스는 굳이 동일한 코드를 다시 정의할 필요가 없다”

한편 상속과 함께 객체지향 방법론의 또 다른 한 축을 구성하는 ‘다형성’은 동일한 이름을 갖는 메쏘드나 연산자가 다양한 문맥(context)에서 겉모습은 그대로 유지하되 속모습은 달리하면서 사용될 수 있도록 하는 강력한 기법이다. 예를 들자면 자바 언어에서 1+1이라는 명령문 안에 있는 ‘+’는 두 개의 정수를 더하는 덧셈 연산자이지만 “abc”+“def”라는 명령문 안에 있는 ‘+’는 “abc”와 “def”라는 두 개의 문자열을 서로 연결(concatenation)하라는 문자열 연산자로 파악된다. ‘+’라는 기호의 겉모습은 유지되고 있지만, 그 연산자가 실제로 수행하는 연산의 내용은 완전히 다르다.
존슨과 푸트는 같은 논문에서 다형성의 이점을 설명하기 위해서 ‘case’ 구문을 사용하는 프로그래밍을 다음과 같이 비판했다.

“하지만 이런 식의 프로그래밍은(case 구문을 사용하는 절차적 프로그래밍은 - 인용자) 위험하다. 왜냐하면 특정한 경우(case)를 깜빡 잊어버리기가 쉽기 때문이다. 그런 방법을 이용하면 프로그램의 어느 한 곳에 가해지는 사소한 수정이 추가적인 경우(cases)를 유발할 수 있기 때문에 결국 재사용이 어려운 코드를 양산하게 된다”

다형성이 지원되지 않는 조건에서 ‘+’ 기호를 앞에서와 마찬가지로 여러 가지 목적에 사용한다고 가정하자. 이러한 경우에는 함수에 전달된 인수(parameter)의 데이터형을 확인해서 그것이 ‘정수’면 ‘더하기’를 수행하는 함수를 호출하고, ‘문자열’이면 ‘문자열 연결’을 수행하는 함수를 호출하는 조건문을 프로그래머 스스로 작성해야 한다. 존슨과 푸트가 지적한 것은 프로그램의 규모가 커지면 그러한 조건문이 등장하는 장소가 늘어날 뿐만 아니라 필요한 조건을 프로그래머가 깜빡 잊고 추가하지 않는 경우가 발생할 수도 있기 때문에 궁극적으로 프로그램의 재사용성이 떨어진다는 점이다(if-else가 빈번하게 출몰하는 프로그램은 일단 좋은 프로그램인지 여부를 의심해 볼 필요성이 있다).
자바 언어를 사용해서 프로그래밍을 할 때 메쏘드의 이름은 동일하게 유지하면서 입력되는 인수의 수나 데이터 형만 살짝 바꿔주는(보통 오버로드(overload)라고 불리는) 경우가 있다. 이러한 ‘초식’이 가능한 이유는 바로 자바 언어가 다형성을 지원해 주는 ‘객체지향’ 언어이기 때문이다.

상속, 다형성, 그리고 프로토콜
존슨과 푸트는 ‘상속’과 ‘다형성’ 이외에 ‘프로토콜(protocol)’이라는 특징도 설명했다. 그들이 설명한 프로토콜은 현재의 기준으로 보았을 때, 흔히 말하는 API라는 개념과 별로 다르지 않다. 객체와 객체, 혹은 컴포넌트와 컴포넌트 사이에 구체적인 내용이 생략된 추상적인 API를 정의함으로써 동시다발적인 코딩을 가능하게 하여 소프트웨어의 개발 속도를 향상시키고, 객체 상호간의 의존성을 최소화하고, 궁극적으로 ‘코드의 재사용’ 가능성을 극대화하는 기법을 프로토콜이라는 용어로 설명했다. 요즘에는 프로토콜이라는 단어가 주로 네트워크 분야에서 사용된다는 점을 생각해 보면 그들이 논문을 작성한 1988년과 2004년 사이에 존재하는 시간의 간격을 느낄 수 있다.
정리해 보자면 상속과 다형성과 프로토콜은 ‘객체지향’ 기법이 갖는 세 가지 주요한 특징에 속한다. 이 세 가지 개념은 객체를 적어도 ‘이용’할 줄 아는 프로그래머라면 쉽게 설명할 수 있는 개념이다. 하지만 아직 우리는 가장 본질적인 질문을 검토하지 않았다. “왜 객체지향인가? 도대체 객체가 무엇이기에 우리는 ‘객체’를 ‘지향’해야 하는가?”라는 질문이 바로 그것이다.

재사용되는 코드의 량은 얼마인가
앞에서 세 가지 특징을 설명할 때 한 번도 빠지지 않고 등장한 개념이 있었다. 그것은 바로 ‘코드의 재사용’이다. 필자가 경험이 부족한 주니어 프로그래머들과 함께 일할 때 가장 힘주어 강조하는 것도 바로 이 코드의 재사용이다. 코드의 재사용은 새로운 소프트웨어의 개발 속도를 향상시켜줄 뿐만 아니라 디버깅이나 수정을 쉽게 만들어 관리(maintenance)의 효율을 증가시켜 준다. 코드의 재사용이 객체지향 기법이 갖는 목적의 전부라고 말할 수는 없지만, 객체지향의 가장 중요한 목적이 코드의 재사용이라고 말할 수는 있다. 그래서 자기가 작성한 코드 중에서 ‘재사용’되는 코드의 량이 얼마나 되는가 하는 질문에 대한 답은 프로그래밍 ‘실력’을 드러내는 리트머스 시험지가 된다. 그렇다면 여러분이 작성한 코드 중에서 재사용되는 코드의 량은 얼마인가.


저자 : 임백준

 


원문 : http://www.imaso.co.kr/whatsnew/coulum/article.html?id=4831&forum=0