출처 : 남궁성의 자바(java) 초보스터디 게시판


소프트웨어를 효율적으로 개발한다는 것은 바꿔 말하면 소프트웨어 전문가가 되어야 한다는 것을 의미한다. 그러기 위해서는 소신 있게 행동할 수 있는 올바른 마음가짐과 현재 수준의 정확한 인식, 충분한 지식과 경험이 필요하다. 1부에서는 이처럼 방대한 주제들 중에 몇 가지 핵심적인 항목들에 대해 간단히 알아본다.


김익환 | ikkim@ahnlab.com

서울공대를 졸업하고 스탠포드 대학에서 석사를 받은 필자는 현재 안철수연구소 CTO겸 부사장을 맡고 있다. 미국 실리콘밸리 IT회사에서 쌓은 개발경험을 토대로 "대한민국에는 소프트웨어가 없다"는 책을 썼다. 지식정보산업의 중요성이 부각되고 있는 세계화 시대에 글로벌 경쟁력을 갖춘 소프트웨어기업이 나오기를 고대하고 있다.



자기가 아는 지식이라 해도 사용할 수 없는 경우가 많다. 책에 적혀 있는 글을 읽었다고 자기 자식이 되지는 않는 것과 마찬가지다. 소위 알파벳의 모음인 백과사전식의 지식이 이 같은 경우다. 알파벳의 모음이 지식의 시작일 뿐이라는 것을 마크 트웨인은 다음과 같이 풍자하기도 했다.


마크 트웨인의 친구 중 훌륭한 목사 한 사람이 있었다. 그는 자신의 설교에 어렵게 마크 트웨인을 초대했다. 목사는 어느 때보다 훌륭한 설교를 준비했다. 훌륭한 설교에 청중은 감명 깊게 설교를 듣고 있었다. 그러나 마크 트웨인은 아주 지루하게 앉아 있을 뿐이었다.


-----------------------------------------------

설교가 끝나고 목사가 물었다. “설교가 자네 마음에 들던가?”

마크 트웨인은 대답하였다. 

“요즈음 마침 책을 읽고 있는데 당신이 한 말 모두가 다 그 안에 적혀 있더군.”

목사는 그의 말을 믿을 수가 없었다. 

“아마도 여기저기에 비슷한 문장쯤이야 몇 개 있었겠지만 설마 전부를 다 빌릴 수가...”

마크 트웨인은 대답한다. 

“단어 하나하나를 모두 복사했더군. 그것은 완전히 도둑질이지.”

목사는 어이가 없어 자기도 그 책을 한번 보고 싶다고 했다. 다음날 마크 트웨인은 그 책을 그에게 보냈다. 

그것은 사전이었다. 물론 그 사전 안에는 그가 한 말 모두가 다 들어 있었다.

-----------------------------------------------


사전은 누구나 다 가질 수 있지만 사전을 가지고 있다고 훌륭한 설교를 할 수는 없다. 마찬가지로 소프트웨어 개발이나 기술에 관한 책과 문서는 너무 많고 주제도 방대하다. 그러한 알파벳적인 지식 중에서도 극히 일부분 외에는 접할 수 없다. 설령 그러한 지식을 많이 안다고 해도 훌륭한 소프트웨어 개발을 하려면 마음가짐과 경험, 지혜 등이 추가로 요구된다.


필요한  마음가짐 

자기의 지식을 자유자재로 사용하기 위해서는 머릿속의 지식을 습관적으로 행동할 수 있는 흔들리지 않는 확고한 소신이 있어야 한다. 확고한 소신이 없이는 옳은 방향대로 실천하기에 수많은 유혹과 장애물이 기다리고 있는 게 현실이다. 


효율적인 개발을 논하기에 앞서 필자가 현장에서 느낀 몇 가지 핵심적인 마음가짐을 나열해 보기로 한다. 이런 마음가짐이 확고하지 않고는 아무리 좋은 기법을 알고 있어도 실천하기는 쉽지 않다.


다른 사람의 입장을 생각하자

이 세상의 모든 사람들은 서로 편하고 행복하게 살려고 노력한다. 내가 편하기 위해서 다른 사람은 불편해도 된다고 생각하는 것은 소프트웨어를 포함한 모든 인간 생활에서 불행과 충돌의 시작이다. One-man-company가 아닌 이상 소프트웨어는 팀으로 개발이 되는데 내가 하는 행동이 남에게 어떻게 영향을 미칠까를 생각하며 행동해야 문제가 없고 서로 시너지 효과를 낼 수 있는 것이다.


많은 경우에 다른 사람이 고생하는 것을 알면서도 자기의 편의를 위해 귀찮은 일을 다른 사람에게 전가하는 경우가 있다. 예를 들어 요구사항을 분명히 명시하지 않고 구현을 부탁한다든지, 설계를 제대로 안하고 프로그래머에게 코딩을 부탁하는 등의 경우가 그것이다. 역지사지라고 서로 반대 입장에서 생각해 보면 내가 어떻게 하는 것이 서로에게 효율적일지 분명히 알게 된다. 소프트웨어는 사람의 팀워크가 특히 중요한 업종이기에 협동정신이 무엇보다 중요하다.


소프트웨어 개발은 구현만이 전부가 아니다

‘개발’이라는 용어는 저마다의 수준에 따라 다르게 해석된다. 가장 낮은 수준에서는 코딩 혹은 프로그래밍을 생각할 것이고 높은 수준에서는 요구사항 분석서(Software Requirement Specification)부터 설계, 구현, 테스트, 출시, 유지보수까지를 포함하는 소프트웨어 라이프 사이클의 전 공정을 포함할 수 있다. 그래서 개발하는 데 얼마나 걸릴까 하는 똑같은 질문에 엄청나게 다른 대답이 나올 수 있는 것이다. 개발은 구현만이 아니고 소프트웨어 라이프 사이클 동안의 모든 행동을 포함한다. 


심지어는 개발을 하기 위한 형상관리, 버그 관리 시스템, 테스트 환경, 프로젝트 관리 등 모든 주위 환경의 준비까지도 포함된다. 그런 것들이 다 개발 일정이나 품질에 영향을 미치게 된다. 이제부터는 대화중에 개발이라는 단어가 나오면 이런 모든 단계를 의미하는 것으로 생각한다. 코딩은 전체 개발과정의 20~30% 정도에 불과하다는 사실을 잊지 말자.

 

설계를 코딩보다 좋아해야 한다

설계는 소프트웨어 개발과정중의 요구사항 분석과 코딩의 사이에 위치한 핵심적인 단계이다.  이 핵심적인 단계가 제대로 되어 있으면 거의 모든 문제가 미연에 발견되고 방지된다. 앞쪽으로는 요구사항 분석에서 벌어진 무리한 기능이나 일정이 발견되는 기회가 되고 뒤쪽으로는 코딩과정에서 생길 수 있는 구조적인 문제를 모두 발견할 수 있다. 아이러니하게도 이러한 핵심적인 단계를 주로 시간이 없다는 핑계로 게을리 한다. 설계는 생략할 수 있지만 코딩은 생략할 수 없다는 생각인데 사실은 많은 개발자가 생각하는 코딩의 일부가 설계의 일부분이라는 것을 간과하고 있다.


그래서 모든 소프트웨어공학이나 개발방법론에서 코딩이나 설계에 소비하는 시간이 전체 개발기간의 20~30% 정도씩 비슷하게 할당되도록 하는 것이 설계를 중요시 하는 이유다. 진정한 코딩영역은 단순작업에 가까운 편이고 설계는 지식, 경험과 창의력이 요구되는 어려운 작업이다.


설계는 컴포넌트(Component)와 인터페이스(interface)를 제공하는 것이 핵심이다. 그 나머지는 모두 추가 정보일 뿐이다. 컴포넌트와 인터페이스가 제대로 설명되어 있지 않은 설계는 설계가 아니다. 설계 툴로서 많이 사용하는 UML은 인터페이스를 객체지향언어의 함수 레벨에서 가장 적절히 표현할 수 있으나 일반적인 인터페이스는 표현하기 어렵다는 단점이 있다. 그러므로 클래스(Class)와 함수(Function)를 주요 정보로서 나타내는 설계의 최하위 단에서는 UML이 적절할 수도 있으나 모든 설계정보를 설명하기에는 충분하지 않다.


설계에 대한 이해를 돕기 위해 소프트웨어 개발환경 중에서 가장 익숙한 예를 들어보자. 모든 프로그래머가 이런저런 SDK(Software Development Kit)를 이용해서 프로그래밍을 했을 것이다. 컴포넌트는 SDK처럼 함수의 Set으로 표현될 수 있다. SDK는 그 내부의 구현방식을 들여다보지 않아도 어떠한 기능을 가지고 있고 외부에서 어떻게 사용하는지를 알 수 있다. 그 것이 컴포넌트와 인터페이스의 기본이다. 


인터페이스는 물론 이러한 함수로만 표현될 수 없는 것이 많기 때문에 컴포넌트를 표현하기 위한 다른 방법이 다양하게 요구된다. 함수로 표현되는 인터페이스는 인터페이스 중에서 가장 간단한 것이라고 할 수 있다. 국제적으로 보안 소프트웨어제품의 안정성을 승인해 주는 국제보안인증제도인 CC인증도 바로 이 컴포넌트와 인터페이스를 기술하는 것이다.


전문가가 되기 위해서는 항상 배워야 한다

소프트웨어의 진정한 전문가가 되는 데에는 세 가지 어려운 문제가 있다. 좋은 개발환경을 접하기가 어렵고, 좋은 스승을 만나기가 어렵고, 마지막으로 자기 자신을 깨닫기가 어렵다. 


누구나 취미활동을 하더라도 혼자만의 경험이나 책으로 배워서는 전문가가 되기 어렵다는 사실을 깨달았을 것이다. 바둑에 큰 뜻을 둔 두 명의 젊은이가 평생을 산에 가서 연구하고 실력을 길러 가지고 왔는데 초보수준밖에 안되었다는 옛 이야기가 있다. 스승을 찾을 수 있는 상황이 안 되면 동료에게서라도 배워야 한다. 그래서 피어 리뷰(Peer Review)라고 하는 것이 중요하다. 서로 동료의 장점과 실수를 검토하고 의논함으로써 많은 것을 배울 수 있는 것도 사실이다. 하지만 선배나 스승의 존재는 훨씬 더 빨리 배우고 시행착오를 줄일 수 있기 때문에 중요하다. 또, 수준이 높아졌다고 해도 알면 알수록 배울 것은 더 많아진다는 것이 또 현실이다. 그래서 전문가가 되기 위한 배움은 끝이 없다.


소프트웨어 개발의 의식 레벨

소프트웨어회사와 개발자들의 수준을 표현하기 위해 의식 레벨을 소프트웨어에 적용한 것이 있다. 세 가지 레벨로 나누어 놓았는데 현재 속한 회사나 자신의 수준을 측정해 보고 미래의 목표를 세우는 데 가이드가 되었으면 한다.


의식레벨 I

- 프로그래밍 영웅이 존경의 대상이다

- 자신이 최고이고 다른 것을 받아들이지 않는 태도를 가지고 있다


이 레벨에서는 잘 되어야 프로그래밍 영웅 혹은 카우보이 프로그래머로 지칭되는 독불장군 개발자가 될 수 있다. 이 레벨에서는 대부분 다른 사람의 코드는 인정하지 않고 자신만이 최고라는 생각에 산다. 이 레벨의 특징은 균형과 견제가 없이 개발에 관한 모든 권한을 가진 팀에서 개인역량위주의 극단적인 현상이 많이 발생한다. 이 레벨에서는 단기적으로는 성공할 것처럼 보일 수는 있으나 장기적으로는 성공하기 어렵고 잘 되는 경우라고 해도 결국은 더 많은 비용을 지불하고 실패한다.


이 레벨에서 존중 받지만 바람직하지 않은 프로그래밍영웅은 아래와 같이 묘사된다. 프로그래밍 영웅은 항상 자기 능력을 시험해 볼만한 일을 선택하고 어마어마한 코드를 써 나간다. 그런 사람은 야근을 밥 먹듯 하고 곧 프로젝트에서 절대로 빼 놓을 수 없는 사람이 된다. 성공은 그들의 어깨에 달려 있는 것같이 보인다. 그들이 없는 프로젝트 수행은 생각할 수도 없다.


이런 사람들은 대부분의 설계정보와 소스코드를 자기만 볼 수 있도록 숨겨놓고 테크니컬 리뷰에 참여하는 것을 거부한다.


또 팀이 정해놓은 표준을 따르지 않는다. 이러한 행동들은 결국 다른 팀원들이프로젝트에 가치 있는 공헌을 할 수 있는 기회를 박탈당하게 된다. 많은 프로그래밍 영웅들은 전혀 영웅이 아니다. 그들은 단지 프로그래밍 독불장군일 뿐이다.


혼란스러운 회사는 혼란스러운 소프트웨어만 만들어 낸다. 영웅개발자를 고용하고 그들에게 전권을 주며 기적을 만들어 내기 위해 영웅들을 자유롭게 놔두는 회사는 결국 기발할지는 모르지만 에러도 무지무지하게 많은 상품을 만들어 낸다.


의식레벨 II

- 규칙중시, 팀 중시의 문화

- 회사에서 정한 방법론을 잘 따른다

- 같은 종류의 프로젝트에는 적합하다


이 레벨에서는 전사적인 개발방법론을 정해 놓고 모두 예외 없이 따라서 한다. 이러한 전사적인 방법론을 경험 없고 규칙을 싫어하는 사람들이 잘못 이해하면 비효율적인 프로세스라고 생각할 수도 있으나 적어도 비슷한 유형의 프로젝트에는 항상 최적화된 방법론을 유지해야 함은 기본이다.


이 레벨에서 단점은 경우는 너무 잘 하기 위해 산출물의 문서를 너무 많이, 자세히 규정하면 할수록 효율적으로 적용될 수 있는 유형의 프로젝트가 제한적일 수도 있다는 점이다. 하지만 현실적으로는 문서 개수가 너무 많아서 문제가 되기보다는 가장 핵심이 되는 소수의 문서조차도 작성하려고 하지 않는 엔지니어의 거부감이 우선인 경우가 대부분이다.


이 레벨은 다음 레벨에 오르기 위한 필수적인 단계이므로 절대 게을리 할 수 없는 단계다. 하지만 정해놓은 규칙도 여러 가지 이유로 그대로 따라서 실행하기는 쉽지 않기 때문에 처음에 기대했던 대로는 잘 되지 않는다. 알려진 좋은 규칙은 많지만 그 회사의 수준에서 실행가능하고 적절한 규칙을 정하기는 쉽지 않다. 


이 레벨에서 다양한 프로젝트를 경험하면서 시행착오도 겪고 그러면서 숙련된다. 이러한 우여곡절을 겪으면서 다음 레벨로 올라가기 위해서 많은 시간을 보내야 한다. 개인의 역량에 따라 수년 혹은 십년 이상이 걸릴 수 도 있고 사람에 따라서는 안타깝게도 표면적으로만 겉돌면서 영원히 받아들이지 못하는 경우도 있다.


의식레벨 III

- 모든 프로젝트는 다르다

- 규칙보다는 원칙에 우선 한다

- 판단과 창의성이 뛰어나다

- 규칙을 지키면서 적절한 변형을 사용 한다


이 레벨의 시작은 레벨 II에서 다양한 프로젝트를 거치면서 장단점을 파악하고 그 동안의 경험을 적절히 조합하여 사용할 수 있는 능력이 있어야 한다. 그래도 현실에서 판단을 어렵게 만드는 것은 새로운 프로젝트가 기존경험의 조합만으로 결정될 수 없고 항상 창의성을 요구하는 새로운 유형의 프로젝트가 나타난다는 것이다. 그래서 이 세상에 똑같은 프로젝트는 없다고 한다. 이 레벨에서는 주어진 규칙은 다 알고 응용하는 것이기 때문에 실제 프로젝트에 접하면서 그 다양하고 오묘함을 느끼게 되는 단계이다.


많은 개발자들이 대부분 레벨 I에 머물면서 레벨 II의 혜택도 받아보지 못하고 경력을 마감할 지도 모른다. 벤처회사는 보통 레벨 I에서 시작한다는 생각을 할 수도 있으나 레벨 I로 회사를 성공적으로 지속할 수 있는 기간이나 확률은 얼마 되지 않는다. 지속적으로 성장하기 위해서는 언젠가는 레벨 II를 거쳐 레벨 III로 넘어가야 한다. 


‘선무당이 사람 잡는다’ 는 것이 바로 레벨 II를 거치지 않은 개발자들이 의식레벨 III를 흉내 낼 때 벌어지는 현상이다. 이는 레벨 I 과 다름이 없다. 늘어놓는 변명들은 비슷하다. ‘인터넷 프로그래밍은 전통적인 방법론에 부적당하다’거나 ‘빠른 시간에 수정을 요구하는 환경은 즉각적인 코딩을 해야 한다’는 등이다. 하지만 이런 변명들은 소프트웨어산업이 50년의 세월을 거치는 동안에 새로운 기술이 나올 때마다 계속 제기되었던 이슈들이다. 


어떻게 하면 프로그래밍을 쉽게 할 수 있을까 하는 열망에서 계속 객체지향 프로그래밍, 3G, 4G, 5G, Case Tool등 새로운 기법들이 유행처럼 생겨났었는데 그래도 소프트웨어의 개발 핵심은 변하지 않았다. 단지 표면적인 기법들은 조금씩 추가되고 변형된 정도이다. 우리가 계속해서 밥과 김치를 먹듯이 음식이 아무리 변해도 핵심적인 음식은 변하지 않는다. 결과적으로 보면 유행처럼 생겨났던 것들은 대부분 장기적으로는 사라지곤 했다.


기본적으로 꼭  갖추어야  하는  프로그래밍의  예

다음에 나오는 프로그래밍 예제들은 보통 간과되지만 좋은 프로그래밍을 위해서는 절대 양보할 수 없는 몇 가지 예를 들은 것이다. 이것을 이해하고 비슷한 개념을 다른 환경에서도 응용할 수 있기를 바란다. 효율적인 프로그래밍을 위한 것이라기보다는 프로그래밍의 필수적인 요소라고 할 수 있다. 필자의 저서인 『대한민국에는 소프트웨어가 없다』에 몇 가지 추가적인 예와 자세한 설명이 나와 있으니 관심 있는 독자들은 참고하기 바란다.


Public Interface는 꼭 필요한 곳에만 사용해라

Object Oriented Programming(OOP, 객체지향 프로그래밍)이라는 말이 많이 사용되는데 요새 주로 사용하는 프로그래밍언어인 자바나 C++이 이 분류에 속하는 언어다. 물론 이 언어를 사용한다고 OOP가 구현되는 것은 절대 아니다. 흉내는 내지만 제대로 OOP를 이해하는 사람은 별로 본적이 없다. 


흔히 스파게티 코드라고 부르는 용어가 있다. 프로그램이 스파게티의 면처럼 꼬이고 엉켜있어서 최초 개발자만 이해 가능하고 다른 사람은 이해하기 힘든 코드이다. C++이나 자바로도 얼마든지 스파게티코드를 만들어낼 수 있다. 반면에 능력 있는 개발자들은 C와 같은 언어로도 static을 이용하여 OOP적인 프로그래밍을 하고 있었다. OOP의 개념은 C와 같은 OOP이전 언어 때도 이미 있었고 또 사용되고 있었다. 그것이 좀 더 세련되고 모양을 갖춘 것이 OOP 라는 이름을 얻은 것뿐이?


모든 Class는 Public Interface로서 이해되고 사용된다. 어떤 Class를 이해하려면 그 안의 코드를 볼 필요가 없다. 봐서도 안 된다. 이유 없이 Private Interface로 해야 되는 것을 Public Interface로 만들어 놓는 다는 것은 OOP의 핵심을 위반하는 것이다. <리스트 1>은 Java의 예인데 C++를 아는 사람도 이해하는 데는 문제없을 것이다.


<리스트 1>-----------------------------------------------------------

 class LogManager {


  public static void writeLog (String log) {

   String tempStr = format (log);

   …….

  }


  public static String format (String log) {

   String timeStr = ……

   String newLog = timeStr + log ;

   return newLog;

  }

  ….

 }

--------------------------------------------------------------------


애초의 의향은 다른 프로그램에서 로그를 기록하기 위해 LogManager.writeLog(…)를 호출하기 위해 쓴 것이다. 그 목적으로 이러한 코딩을 했다면 OOP의 기초도 안 되어 있는 것이다. 이런 코드를 보면 나는 기절초풍한다. 이렇게 해도 구동하는 데는 전혀 문제가 없는데 과연 뭐가 그렇게 뭐가 문제인지 궁금한 독자는 다음 글을 읽기 전에 그 해답을 생각해 보자.


이 클래스에는 두 개의 메소드가 있다. writeLog()는 다른 프로그램에서 호출해서 사용하기 위한 목적이고 format()은 이 클래스 안에서 내부적으로 사용하기 위한 것이다. 여기에서 format()은 밖으로 알려지지 않는 Private 인터페이스이어야 한다. 즉 다음과 같이 public대신 private으로 정의되어야 한다.


private static String format (String log) {


아직도 뭐가 그렇게 큰 잘못이냐고 할지도 모른다. 이런 문제를 알아내고 문제를 수정하지 않는다면 당신은 프로그래머로서의 능력이 없거나 자질이 없다고 할 수 있다. private이 될 것이 public으로 되어있다는 것은 OOP의 기초가 안 되어 있다는 얘기다.


사실 이 코드를 실행시키는 데에는 아무런 문제가 없다. 문제는 이 프로그램을 수정할 때 발생한다. 새 버전을 만들거나 버그를 고칠 때가 문제이다. public으로 된 것은 이미 외부에서 사용하고 있다고 가정해야 하기 때문에 수정 할 수가 없다. 수정을 하게 되면 이미 사용하고 있는 곳에서는 망가지기 때문이다. OOP의 정의가 Object는 외부와 Method에 의해서 통신하는 것인데 이 통신방법을 임의로 변경하는 것이기 때문에 이전 버전과의 호환성이 개진다. 한마디로 public이라고 정의해 놓은method를 수정하려면 그 method를 사용하는 모든 프로그램들을 다 찾아서 고쳐야 한다.


라이브러리를 이용하는 현명한 프로그래머가 되어라

초보 프로그래머부터 경험 많은 프로그래머들을 고루 접하다 보면 경험의 중요성을 새삼 실감하게 된다. 직관적이고 본능적인 판단이 필요한 때가 많은데 그것은 경험에서 나오기 때문이다. 그런데 직접 경험이 없어도 간접 경험이라는 게 있지 않은가? 또 경험이 없어도 현명한 프로그래머는 어디서 답을 얻을 지 안다.


예전에 내가 운영하던 회사에서 있었던 일을 예로 들어보자. 언제나 열심히 일하고 또 능력도 있는 한 프로그래머가 있었다. 하루는 그가 프로그램이 느린 문제를 해결하기 위해 애를 쓰고 있었다. 그 프로그램은 자신이 직접 수만 줄의 데이터를 읽어 정렬하도록 만든 것이었다. 가만 보니까 MFC(Microsoft Foundation Class)의 라이브러리에 있는 Hash를 쓰면 되는 것인데 어렵게 구현을 한 것이었다. 


제대로 구현을 했는지는 확인하지 않았지만 근본적으로 잘 구현된 라이브러리에 있는 기능을 사용하면 되는 것이었다. 지금까지 써 놓은 코드를 다 지우고 라이브러리에 있는 코드 몇 줄로 바꾸어 놓았다. 그런데 MFC의 Hash를 사용했는데도 썩 좋은 결과가 나오지 않았다. 또 막혔다. 다시 Hash의 API를 조사해 보니 버킷 사이즈를 조절할 수 있는 방법이 있었다. 그걸 조정해서 다시 해보니 속도도 빨라지고 문제가 해결되었다. 내가 Hash에 대해 모르고 지나쳤다면 한 며칠은 고생하면서 이상한 방법으로 해결했을 것이다. 혼자 고생하는 것도 능력부족이다. 다른 사람에게 물어봐서 하는 것도 능력이다.


그럼 수만 개나 되는 라이브러??있는 것을 어떻게 알고 사용할 것인가. 직감적으로 내가 원하는 기능의 존재유무를 판단하고 어디에 있을지 추측한 후 검색할 것이다. 자바에도 사용할 수 있는 클래스가 5,000개를 넘어선지 오래다. 그 중에서 자주 사용하는 몇 십 개를 제외하고는 뭐가 있는지 잘 모른다.


현대의 프로그래밍은 20년 전과는 달리 많은 부분이 라이브러리를 사용하게 된다. 즉 라이브러리를 얼마나 적절히 잘 사용하느냐가 프로그래밍의 중요한 부분이 되었다. 마치 라이브러리에 있는 기능을 조합하는 것이 프로그래밍의 중요한 일과가 되어 버린 것이다. 앞에서 말한 직감 외에 프로그래밍 도우미사이트에서의 검색 또한 도움이 될 수 있다. 하지만 가장 중요한 것은 동료나 선배들로부터 배우는 것이다. 모르면 물어보면 된다. 자존심 상할 것 하나 없다. 열등감이 있는 사람들은 그래서 같이 일하기 어렵다. 자존심 상하니까 혼자서 해결하려고 든다. 소스코드 검토 시에 이런 문제는 꼭 지적이 되어야 한다. “어 그거 라이브러리에 있는데 개발하셨어요?” 이런 소리가 자주 들리면 문제가 많은 개발그룹이다. 


그러나 라이브러리에 있는데 자체 구현했다는 것조차 모른다면 그 개발그룹은 아예 희망이 없다. 그런 그룹이 개발한 코드는 문제투성이고 끝없이 고생만 낳을 것이다. 그나마 프로그램이 안 팔려면 다행이지만 어쩌다가 영업을 잘해서 한번 팔리기라도 하면 빼도 밖도 못하는 손해 보면서도 그만두지 못하고 괴로운 상황이 될 것은 불 보듯 훤한 일이다.

프로그래밍을 하다 보면 누구나 스트링을 숫자로 변환시켜야 하는 경우가 자주 발생한다. 이것을 라이브러리에서 있는 것을 아는 사람이면 10초면 코드를 적을 수 있다. 


라이브러리를 모르는 사람은 어떤가. 혼자서 구현을 하든지 라이브러리를 검색해야 할 것이다. 어떻게 하던 시간은 족히 10분은 지나간다. 한두 시간이 걸릴 수도 있다. 10초와 한 시간의 차이는 엄청나다. 이는 거짓말 같은 사실이다. 좋은 프로그래머와 나쁜 프로그래머의 차이가 이럴 수 있다. 밤 새워서 열심히 일 해봐야 일하는 방법을 모르면 좋은 개발자가 될 수 없다.

 

모든 Exception을 처리해라

프로그램을 제대로 개발하려면 세세히 신경 쓸 일이 너무 많다. 사정이 이렇다 보니 어지간한 것은 대충 가정하여 처리하게 된다. 좋은 예로 변수를 당연히 null이 아니라고 가정하고 사용하는 것을 들 수 있다. 이런 잘못은 누구의 코드를 봐도 쉽게 발견할 수 있다. <리스트 2>를 살펴보자.


<리스트 2>-----------------------------------------------------------.

int[] priceArray = getPriceList();

int sum = 0;

for (int i=0; i<priceArray.length. i++) {

 sum = sum + priceArray[i]; 

}

-------------------------------------------------------------------


이 얼마나 간단한 코드인가. 이 코드는 일반적인 경우 문제없이 구동 될 것이다. 운이 좋으면(?) 평생 뭐가 잘못되었는지조차 발견하지 못할 수도 있다. 문제는 priceArray가 null 포인터가 될지도 모른다는 것이다. Null 포인터가 되면 priceArray.length에서 NullPointException이 발생하고 프로그램의 작동이 중지될 것이다. 왜 이것이 컴파일은 문제없이 되고 구동 시에만 문제가 되는지 모르는 사람도 많다. 보통은 Exception이 발생하는 경우에 처리하는 코드가 없으면 컴파일러가 처리하는 코드를 추가하라고. 친절하게 가르쳐 주지 않던가? 


이것은 소위 RuntimeException이라고 부르는데 컴파일 할 당시에는 오류를 발견할 수 없는 경우다. 실제 구동 할 때만 발생할 수 있는 경우다. 아직도 Exception을 사용해 본적이 없거나 RuntimeException이 무엇인지 몰랐다면 지금 당장 책을 보고 배우자. 당신은 지금까지 엉터리 프로그램을 하고 있었던 것이다. NullPointException 이외에 ArrayIndexOutOfBoundsException 같은 것도 흔히 보는 RuntimeException이다.


여러분이 프로그램을 구동하다가 NullPointException이나ArrayIndexOutOfBoundsException이 한번이라도 발생했다면 믿을 수 없는 코드이다. 당장 가서 코드를 고쳐야 한다. 발생한 곳만 고치는 것이 아니라 전체 소스코드를 처음부터 끝까지 한 줄 한 줄 확인하면서 비슷한 오류를 범했는지 확인해야 한다.


그러면 어떻게 해야 옳은 프로그램인가? 여러 가지 방법이 있는데 <리스트 3>을 통해 두 가지 방법을 확인할 수 있다.


<리스트 3>----------------------------------------------------------

(첫째 방법)


int[] priceArray = getPriceList();

int sum = 0;

if (priceArray != null) {

for (int i=0; i<priceArray.length. i++) {

  sum = sum + priceArray[i]; 

}

}


(둘째 방법)


int[] priceArray = getPriceList();

int sum = 0;

try {

for (int i=0; i<priceArray.length. i++) {

  sum = sum + priceArray[i]; 

}

} catch (NullPointException e) {

 e.printStackTrace();

 // 어떻게 처리할지 코드를 여기에 삽입한다.

--------------------------------------------------------------------


여기서도 역시 되는 기능보다는 항상 잘못될 경우를 처리하는데 더 신경을 곤두세우고 있다는 것을 명심하기 바란다. 되게 만드는 것보다는 안 되는 경우를 처리하는데 몇 배나 많은 시간이 걸린다.

 

코드는 아무리 양이 적더라도 복사하지 말라

프로그램 코딩을 하다 보면 가장 유혹에 빠지기 쉬운 것이 코드의 일부분을 복사해서 사용하는 것이다. 즉 비슷한 코드가 두 군데 이상 있다는 것을 의미한다. 나는 ‘적은 양의코드라도 복사해서 사용하지 말라’는 규칙을 정하고, 이 규칙을 고의로 어기는 사람은 해고대상자라고 선언한다. 무슨 큰 잘못 같지도 않은데 살벌하게 해고대상자까지 되느냐고 물을지 모르지만 내 신념은 확고하다. 미래에 큰 문제는 모두 이렇게 사소한 데에서 발생해서 눈덩이처럼 커진다. 병의 인자를 포함하고 태어나는 것이다. 


프로그램에도 수명이 있다. 태어나서 죽을 때까지 자라고 병들고 고치고 한다. 태어날 때부터 기초체력이 튼튼하면 병이 잘 걸리지도 않거니와 병이 걸려도 치료가 쉽다. 다른 병과의 합병증이 없이 한 가지 병만 고치면 되기 때문이다. 그럼 어떻게 코드가 태어나서 자라는지를 살펴보자.


<리스트 4>와 같이 간단한 코드가 있다. 생명의 시작이다. 이것이 심각하게 잘못된 코드라는 것을 인식하지 못한다면 당신은 병약한 프로그램을 만들지도 모르는 개발자다.


<리스트 4>-----------------------------------------------------------

(1 단계) 시작

if (x>y) {

  price = getPrice (x);

 } else {

  price = getPrice (y);

 }


(2 단계) 변수에 item이 하나 더해졌다.

if (x>y) {

  price = getPrice (x, item);

 } else {

  price = getPrice (y, item);

 }


(3 단계) getPrice() 할 때마다 로그를 할 필요성이 생겼다.

if (x>y) {

  price = getPrice (x, item);

  writeLog (x, item, price);

 } else {

  price = getPrice (y, item);

  writeLog (y, item, price);

 }


(4 단계) getPrice()가 오류(자바에서의 Exception)을 발생시킨다. 자바의 예를 들겠다.


if (x>y) {

 try {

   price = getPrice (x, item);

  } catch (Exception ex) {

   ex.printStackTrace();

} finally {

        ……..

  writeLog (x, item, price);

 } else {

 try {

   price = getPrice (y, item);

  } catch (Exception ex) {

   ex.printStackTrace();

} finally {

       ……….

  writeLog (y, item, price);

 }

--------------------------------------------------------------------


<리스트 4>는 뒤로 가면 갈수록 중복된 코드가 많아지고 고치기도 힘들어 진다. 남의 코드면 더욱 더 그렇다. 다른 사람 코드를 수정하면서 전체코드를 다 이해하고 이런 것을 고치려 시도할 수 있다면 정의심에 불타는 강심장의 사나이다. 무모하다. 지금 예는 가장 간단한 경우를 보여준 것이고 실제 코드는 더 복잡하다. 실제 현실에서는 잘못되었어도 원래 프로그래머를 비난하고 핑계 대는 것이 안전하지 괜히 손댔다가 잘못되면 욕만 바가지로 먹는다. 그래서 한 번 나쁘게 적힌 코드는 평생 살아있게 마련이다. ‘세 살 버릇이 여든 간다’는 말이 딱 맞는다. 


애초에 뭐가 잘못되었을까? <리스트 5>처럼 올바르게 시작한 경우를 보면 1 단계에서는 약간의 추가코드가 들어간다. 미래를 위한 준비 작업인 셈이다. 대신 4 단계에서는 훨씬 더 간단해 진 것을 확인할 수 있을 것이다. 이 코드는 가면 갈수록 처음부터 잘 쓰인 코드의 혜택을 받으며 계속 자라 날 것이다.


<리스트 5>-----------------------------------------------------------

(올바른 1 단계) 시작

 int  z;

if (x>y) {

  z = x;

 } else {

  z = y;

}

 price = getPrice (z);


 원본보다 2 줄 많아졌다. 

 

(올바른 4 단계) 

 int  z;

if (x>y) {

  z = x;

 } else {

  z = y;

 }


try {

  price = getPrice (z, item);

 } catch (Exception ex) {

  ex.printStackTrace();

} finally {

 ……..

 writeLog (z, item, price);

--------------------------------------------------------------------


여기에서 살펴본 예는 가장 최하단에서의 중복을 보여 준 것에 불과하다. 위로 올라가면 갈수록 문제도 많아지고 문제점을 발견하는 것도 발견하기 어려워진다. 메소드의 중복, 라이브러리, 파일의 중복 등 수도 없이 많다. 이런 중복만이라도 없앨 수 있는 개발체계가 갖춰져 있다면 제대로 개발하고 있는 회사라고 단언할 수 있다. 중복코드는 잘못된 개발시스템과 무능력한 프로그래머의 합작품이다.


효율적인 소프트웨어 개발을 한다는 것은 전문가가 되어야한다는 것을 의미한다. 백과사전식의 지식을 줄줄 외우는 것과 효율적으로 소프트웨어를 개발하는 것은 별개의 문제다. 그 조각난 지식들의 중요성을 파악하고 적절히 잘 조합해서 사용하기 위해서는 지식이외의 경험과 지혜가 필요하다. 하지만 그런 지식, 경험과 지혜는 단기간에 비법으로 배울 수 있는 것이 아니다. 


어떤 일에서든 단기간에 전문가로 길러지는 것은 불가능한 일이다. 다만 1부와 뒤에 나올 내용들에서 제시되는 개발 생산성을 높일 수 있는 중요한 개념과 필수적인 활용법들을 잘 익혀둔다면 전문가의 영역에 한 발짝 다가선 것임에는 틀림없을 것이다. 필자는 독자들의 생각이나 잘못된 시각을 고치는데 조금이라도 일조할 수 있었다면 그것만으로 감사할 뿐이다.