본문 바로가기

Library/C/C++

섣불리 재사용성을 강조한 코드를 작성하지 말라

개체지향 프로그래밍을 할 때 모두가 강조하는 것은 재사용성을 염두에 두고 코드를 작성하라는 말이다. 물론 맞는 이야기이다. 비슷한 기능을 수행하는 코드를 필요할 때마다 따로 작성하는 것은 불가능하며, 버그의 주범으로 꼽히기도 한다. 그러나, 때에 따라서는 이것이 진실이 아닐 수도 있다. 뭐라고? 구조화 프로그래밍 시절에조차 강조되던 분할 / 모듈화, 그리고 개체지향 패러다임에서의 재사용성 강조로의 발전이 진실이 아니란 말인가?

물론 그것은 아니다. 지금부터 할 이야기는 프로그래밍의 패러다임 변화를 이야기하는 것이 아닌, 과도하거나 막연하게 재사용성을 염두에 두고 코드를 작성하는 것은 헛수고라는 것이다. 이런 경향은 이제 막 병아리 프로그래머를 벗어나 중급 프로그래머로 진입하는 프로그래머의 경우에 많이 나타난다. 다음 사례를 보자.


새로 시작하는 프로젝트에 투입된 Mr. 루키는 재사용성을 염두에 둔 코드의 작성이야말로 유지 보수 비용을 낮게 유지할 수 있으며, 나아가 프로젝트 성공의 결정적인 요소라고 믿고 있다. 고참 개발자는 이 프로젝트에서 표준 라이브러리의 문자열 클래스가 적당치 않다고 생각하고 Mr. 루키에게 쓸만한 문자열 클래스의 개발을 지시하였다. Mr. 루키는 의욕에 가득 차 프로젝트 전체에 걸쳐 빠르고, 훌륭한 재사용성을 가진 멋진 문자열 클래스를 개발하겠다고 다짐하고 설계와 구현에 들어간다.

Mr. 루키는 이 문자열 클래스가 어느 상황에서나 빠르게 동작하기를 원하므로, new 연산자로만 힙을 할당 받는 것은 좋지 않다는 것을 파악한다. 따라서, malloc, new, operator::new() 등과 같은 다양한 방법으로 힙을 할당받는 방법을 선택할 수 있도록 한다. 또, 문자열의 크기에 따라 어느 정도의 힙을 할당 받을지 예측하기 힘들기 때문에, 이것은 사용자의 결정 사항으로 넘기도록 구현한다. Mr. 루키는 문자열에 필요한 힙을 할당받는 전략은 문자열 클래스의 성능을 좌우하는 중요한 문제라는 것을 알고 있기 때문이다.

자, 이미 문자열을 관리하는 힙의 할당, 관리 방법의 다양화로 어느 정도 문자열 클래스는 복잡해진 상태이다. 그러나, Mr. 루키는 이 클래스가 장차 여러 곳에서 쓰이리라 생각하고 이 정도의 복잡성은 지불할만한 가치가 있다고 판단한다. 이제 Mr. 루키는 여기서 더 나아가 이 문자열 클래스가 유니코드 뿐만 아니라 기묘한 파스칼 문자열도 처리할 수 있어야 한다고 생각하고 그에 맞게 적절한 추상화를 한다. 문자열 클래스라면 당연히 문자열을 다루는 몇 가지 편의 함수를 제공해야 하므로, 이제 그는 TrimRight(), TrimLeft()와 같은 함수를 구현하기 시작한다.

Mr. 루키는 한창 즐겁게 훌륭한 재사용성을 가진 문자열 클래스를 만들고 있지만, 이미 프로젝트는 시작한지 꽤 오랜 시간이 흘렀다. 가장 기본적인 문자열 클래스가 아직 구현되지 않았으므로, 프로젝트는 아직 본 궤도에 오르지 못했다. 고참 개발자는 프로젝트에만 필요한 제한된 성격의 문자열 클래스를 만드는데 왜 이렇게 오랜 시간이 걸리는지 마땅치 않은 눈치이다. Mr. 루키는 이제 어느 정도 문자열 클래스가 완성되었다고 생각하고 자랑스럽게 소스 트리에 체크인하였다. 이제 팀원들은 이 문자열 클래스를 사용하며 Mr. 루키가 일을 잘 해냈다고 칭찬하리라. 그런데..

이런! 몇 가지 문자열 편의 함수에서 버그가 발생했다. 팀원들은 Trim() 계열의 함수들이 제대로 동작하지 않는다고 불평을 토해낸다. 문제는 처음의 힙 관리 전략을 다양화하면서 Trim() 계열의 함수에서는 그것을 제대로 반영하지 않은 것이다. Mr. 루키는 곧 버그를 수정한다. 팀원들은 다른 문자열 함수가 필요하다고 Mr. 루키에게 요구하고, Mr, 루키는 그 함수들을 구현하지만 곧 자기가 재사용성의 덫에 빠졌다는 것을 파악한다. 즉, 실제로 팀원들은 Mr. 루키가 만든 문자열 클래스의 특정 전략만을 사용하며, 애초에 이것이 고참 개발자가 새로운 문자열 클래스가 필요하다고 판단한 요소였다. 그러나 Mr. 루키는 문자열 클래스를 '몇 번 사용하지도 않을, 아니면 아예 사용하지도 않을 막연한 재사용성'을 위해서 상당한 코드를 작성해 두었으며, 이제 그 코드를 다시 작성하기에는 시간이 촉박한 상태이다. Mr. 루키는 이제 새로운 함수를 하나 작성할 때마다 그런 '사용하지도 않을 재사용성'을 위해 중복되는 노력을 해야 하며, 그의 프로젝트 수행에 막대한 지장을 준다. 이제 그는 사용되지도 않을, 처음의 거창한 전략들과 재사용성을 강조한 코드들이 오히려 버그의 온상이 되었음을 깨닫는다. Mr. 루키는 시간을 아끼기 위해 특정 전략에서만 문제가 없도록 코딩하지만, 이쯤되면 애초에 목표로 했던 팔방미인 문자열 클래스는 사라져 버렸고, 버그만 득시글거리는 초라한 문자열 클래스만 남아 버렸다.

그리고 맙소사. 팀원 중 하나가 '가장 많이 사용되는 전략' 외에 다른 전략을 호기심으로 사용해봤다. 결과는? Mr. 루키는 팀원들의 싸늘한 시선 속에 자신의 평가가 바닥으로 떨어지는 것을 느낀다..


자, 무엇이 잘못되었을까. Mr. 루키는 고참 개발자가 새로운 문자열 클래스가 필요하게 된 이유를 제대로 분석하지 않았다. 프로젝트에서 필요했던 것은 결코 표준 라이브러리의 문자열 클래스를 대체할 수 있는 문자열 클래스 개발이 아니었다. Mr. 루키는 재사용성이라는 명제에 너무 사로잡힌 나머지 '실제로 사용되지도 않을 재사용성'을 강조한 코드를 만들고, 그것에 눌려버린 것이다. 게다가, 이런 사용되지도 않을 코드들은 대체로 현실적인, 정확한 분석을 도외시한 경우가 많으며, 다시 작성해야  하는 경우가 대부분이다. 그것을 충분히 알려주지 않은 고참 개발자나 팀원들도 잘못이 있다고? 음, 그런 요소들은 글 쓰는 사람이 생략할 수 있는 편한 부분이다.

재사용성의 강조는 아무리 해도 지나치지 않다. 그러나, 명확하게 재사용이 필요한 부분을 잘 알지 못한 상태에서 막연한 재사용성을 염두에 둔 코드 작성은 사상누각이다. 특히, 어디로 튈지 모르는 백지에서라면 더욱 무모한 일이다. 표준 라이브러리의 문자열 클래스와 같은 것은 즉흥적으로 재사용성을 염두에 두고 작성된 것이 아니라, 오랜 분석을 통해 '일반적인' 디자인을 가지게 된 것이다.

재사용성은 코드를 작성해야 할 때 이루어져야 하는 것이 아니며, 작성된 코드의 패턴을 명확히 분석한 뒤에 이루어져야 한다. 막연하게 재사용성을 염두에 둔 코드는 오히려 재사용과 거리가 멀다. 최소한 Mr. 루키가 했던 식의 재사용 가능한 문자열 클래스는 바닥부터 구현을 시작해야 하는 단계에서 시도할 만한 것은 아니었다. 이런 경우, 최초 디자인 단계에서 명세를 통해 중복되는 부분을 찾아 재사용성이 필요한 만큼 구현했다면 충분했을 것이다. 주어진 시간은 무한하지 않다.

섣불리 재사용을 강조한 코드를 작성하는 것은, 자신이 만든 코드를 잘 버리려고 하지 않는 프로그래머들의 특성상 프로젝트를 죽음으로 이끄는 암적인 요소가 될 수도 있다.