본문으로 건너뛰기

02 실용주의 접근법

중복의 해악직교성은 긴밀한 관계가 있다. 전자는 시스템을 통틀어 어떤 지식을 중복하지 말라고 경고하며, 후자는 하나의 지식을 여러 개의 시스템 컴포넌트에 걸쳐 쪼개 놓지 말라고 조언한다.

변화의 속도가 빨라짐에 따라, 애플리케이션을 적절하게 유지하는 것은 점점 더 어려워진다. 가역성에서는 변화하는 환경에서 프로젝트를 분리하는 몇 가지 기법을 볼 것이다.

예광탄에서는 요구사항을 모으고, 설계를 테스트하고, 코드를 구현하는 것을 동시에 가능케 하는 개발 스타일에 대해 이야기할 것이다. 프로토타입과 포스트잇에서 아키텍처, 알고리즘, 인터페이스, 아이디어 등을 테스트하기 위해 프로토타입을 어떻게 사용할지 보게 될 것이다. 도메인 언어에서 여러분 스스로 구현할 수 있는 좀 더 현실적인 제안을 보게 될 것이다. 마지막으로 우리는 시간과 자원이 제한된 세상에서 일한다. 어떤 일들이 얼마나 걸릴지를 아는 데 능숙해진다면 이런 희소성을 더 잘 극복할 수 있다. 추정에서는 이 것을 다루게 된다.

개발 중에 이런 기본적인 원리들을 유념한다면 더 좋고, 더 빠르고, 더 강력한 코드를 작성할 수 있을 것이다. 게다가 그것이 쉬운 것처럼 보이게도 할 수 있다.

7. 중복의 해악

프로그래머들은 늘 유지보수 모드에 있다. 우리가 이해하고 있는 것은 날마다 바뀐다. 유지보수는 별개의 활동이 아니며, 전체 개발 과정의 일상적인 부분이다. 유지보수를 하려면, 애플리케이션에 들어 있는 지식의 캡슐들을 찾아내고, 또 바꿔야 한다. 문제는 명세와 프로세스 그리고 프로그램을 개발하는 중에 지식을 중복해 넣기 쉽다는 것이다.

소프트웨어를 신뢰성 높게 개발하고, 개발을 이해하고 유지보수하기 쉽게 만드는 유일한 길은 우리가 DRY 원칙이라고 부르는 것을 따르는 것뿐이라 생각한다.

DRY 원칙

모든 지식은 시스템 내에서 단일하고, 애매하지 않고, 정말로 믿을만한 표현 양식을 가져야 한다.

TIP 11

DRY - 반복하지 마라 Don't Repeat Yourself

DRY를 따르지 않는다면, 남은 방법은 똑같은 것이 두 군데 이상에 표현되게 하는 것이다. 만약 하나를 바꾼다면 나머지 것들도 바꿔야 함을 기억해야 한다. 이것은 기억하느냐 마느냐의 문제가 아니다. 단지 언제 잊어버릴 것인가 하는 문제다.

어떻게 중복이 생기는가?

  • 강요된 중복 개발자들은 다른 선택이 없다고 느낀다. 환경이 중복을 요구하는 것처럼 보인다.
  • 부주의한 중복 개발자들은 자신들이 정보를 중복하고 있다는 것을 깨닫지 못한다.
  • 참을성 없는 중복 중복이 쉬워 보이기 때문에 개발자들이 게을러져서 중복을 하게 된다.
  • 개발자간의 중복 한 팀에 있는 여러 사람들이 동일한 중복을 중복한다.

강요된 중복

우리는 모두 중복을 피할 수 없을 듯한 상황에서 일해 보았다. 하지만 많은 경우, 각각의 지식을 한 곳에 두면서 DRY 원칙을 따르고, 동시에 우리의 삶을 더 편하게 만드는 길이 존재한다. 여기에 그 기법들이 있다.

정보의 다양한 표현양식

코딩 수준에서 종종 동일한 정보가 다른 형태로 표현되어야 할 필요성을 느낄 때가 있다. 클라이언트-서버 어플리케이션을 작성하는 데 클라이언트 쪽과 서버 쪽에서 서로 다른 언어를 사용하지만 공통된 구조를 양쪽 모두에서 표현해야 할 떄가 있다. 데이터베이스 테이블의 스키마를 그대로 반영하는 속성들을 가진 클래스가 필요할 수도 있다.

보통은 약간의 창의력만 있으면 중복의 필요를 제거할 수 있다. 간단한 필터나 코드 생성기를 작성하는 것이 해답이다. 소프트웨어가 빌드될 때 마다 간단한 코드 생성기를 사용해 공동의 메타데이터 표현에서 여러 개의 언어에 걸쳐 있는 구조를 만들어낼 수 있다.

코드내의 문서화

나쁜 코드는 많은 주석을 필요로 한다. DRY 원칙은 낮은 차원의 지식은 그것이 속하는 코드에 놔두고, 주석은 다른 높은 차원의 설명을 위해 아껴두라고 말한다. 그렇지 않으면 지식을 중복하게 되며, 변경할 때마다 매번 코드와 주석 모두를 바꾸어야 한다. 주석은 필연적으로 낡게 될 것이다.

문서화와 코드

문서와 코드 모두는 동일 지식에 대한 표현이다. 우리 모두는 마감일이 다가오기 시작하면 문서의 갱신을 뒤로 미루기 시작한다.

언어에 관한 문제

많은 언어가 소스코드에 상당한 양의 중복을 강요한다. 이것은 언어가 모듈의 인터페이스와 그 구현을 분리하는 경우에 자주 생긴다.

참을성 없는 중복

모든 프로젝트는 시간의 압박을 받는다. 우리가 할 수 있는 최선이란 결국 지름길을 택하는 것이라고 내모는 그런 압박 말이다. 이런 유혹을 느낀다면 '돌아가는 길이 지름길이다'라는 진부한 격언을 기억하라. 지금 당장 몇 초를 절약할 수 있을지라도, 나중에는 몇 시간을 잃게 될런지 모른다.

참을성 없는 중복은 발견하기도 쉽고 다루기도 쉬운 형태지만, 나중의 고통을 피하기 위해서는 훈련이 필요하고, 미연에 시간을 투자할 의지가 있어야 한다.

개발자간의 중복

발견하거나 다루기 가장 어려운 유형의 중복은 한 프로젝트에서 일하는 서로 다른 개발자 사이에서 발생한다. 전체 기능 집합이 부주의하게 중복될 수 있고, 그런 중복이 발견되지 않을 수 있다.

높은 차원의 해법으로, 깨끗한 설계와 강력하고 기술적인 프로젝트 리더, 그리고 그 설계 내에서 책임의 분배가 제대로 이해되도록 하는 것, 이런 것들로 개발자 간의 중복 문제를 다루어라. 그렇지만 모듈 차원이라면 그 문제를 알아채기란 더욱 어렵다.

우리가 느끼기에 최선책은 개발자간에 적극적이고 빈번한 소통을 장려하는 것이다. 공통의 문제를 다루기 위한 토론장을 만들어라.

Tip 12

재사용하기 쉽게 만들어라.

여러분이 조성해야 할 환경이란 뭔가를 직접 만드는 것보다 기존의 것을 찾아내고, 또 재사용하기 쉬운 환경이다. 만약 재사용에 실패한다면 지식 중복의 위험을 각오해야 한다.

8. 직교성

설계, 빌드, 테스트 그리고 확장하기에 쉬운 시스템을 만드는 데에 있어 직교성은 매우 중요한 개념이다.

직교성이란

컴퓨팅에서 이 용어는 일종의 독립성이나, 결합도 줄이기(decoupling)을 의미한다. 하나가 바뀌어도 나머지에 어떤 영향도 주지 않으면 서로 직교한다고 할 수 있다. 잘 설계된 시스템에서는 데이터베이스 코드가 사용자 인터페이스에 대해 직교할 것이다. 데이터베이스에 영향을 주지 않으면서 인터페이스를 바꿀 수 있고, 또한 인터페이스를 바꾸지 않으면서 데이터베이스를 교환할 수 있다.

직교성의 장점

비직교적인 시스템은 본질적으로 변화와 조정을 하기가 복잡하다. 시스템의 컴포넌트들이 고도로 상호의존적인 경우, 특정 국지정 부분만 수정하는 방법이 없다.

Tip 13

관련 없는 것들 간에 서로 영향이 없도록 하라.

우리는 self-contained인 컴포넌트를 설계하기 원한다. 독립적이며 단일하고 잘 정의된 목적을 가진 컴포넌트(응집). 컴포넌트들이 각기 격리되어 있으면 어느 하나를 바꿀 때 나머지 것들을 걱정하지 않아도 된다.

직교적인 시스템을 작성하면 두 가지 큰 장점이 있다. 생산성 향상과 리스크 감소.

생산성 향상

  • 변화가 국소화(localize)되서 개발 시간과 테스트 시간이 줄어든다. 상대적으로 작고, 자족적인 컴포넌트를 작성하는 것이 하나의 커다란 코드 덩어리를 만드는 것보다 더 쉽다. 새로운 코드를 추가할 때 마다 기존의 코드를 계속 바꾸어야할 필요가 없다.
  • 직교적인 접근법은 또한 재사용을 촉진한다. 컴포넌트들에 명확하고 잘 정의된 책임이 할당되어 있다면 애초의 구현자들이 미처 생각하지 못했던 방식으로 새로운 컴포넌트와 결합할 수 있다.
  • 직교적인 컴포넌트들을 결합하는 경우 꽤 미묘한 생산성 향상이 있다. M가지 일을 하는 컴포넌트와 N가지 일을 하는 컴포넌트가 있다고 가정하자. 만약 그것들이 직교적이라면 결합했을 때 결과물은 M x N 가지 일을 한다. 직교적인 컴포넌트들을 결합함으로써 단위 노력당 더 많은 기능을 얻을 수 있다.

리스크 감소

  • 감염된 코드는 격리된다. 어떤 모듈이 병에 걸렸다 해도 시스템의 나머지 부분으로 증상이 전파될 확률이 낮다. 그 부분만 도려내고 새롭고 건강한 놈으로 이식해 넣기도 쉽다.
  • 시스템이 잘 깨어지지 않는다. 어떤 부분을 골라서 약간 바꾸고 수리해도 거기서 생기는 문제점들은 그 부분에만 한정될 것이다.
  • 직교적인 시스템은 해당 컴포넌트들에 대해 테스트를 설계하고 실행하기 훨씬 쉽기 때문에, 더 많은 테스트를 하게 된다.
  • 써드파티 컴포넌트로 연결되는 인터페이스들이 전체 개발의 작은 부분에 한정되기 때문에 특정 벤더나 제품, 플랫폼에 덜 종속될 것이다.

프로젝트팀

팀 내 업무가 겹치는 영역이 많다면 구성원들은 책임 영역에 대해 혼동하게 된다. 뭘 하나 바꾸려고 해도 그들 중 누구라도 영향을 받을 수 있기 때문에 전체 팀원이 모여야 한다. 어떻게 팀을 조직해서 책임이 잘 정의되어 있고 중복이 최소화된 그룹으로 만들 것인가?

프로젝트 팀의 구조가 얼마나 직교성을 갖는지 간단히 측정해 볼 수 있는 방법이 있다. 요청된 개별 변화에 대한 토론에 참여할 필요가 있는 사람이 몇 명인가를 보라.

설계

개발자 대다수는 직교적인 시스템을 설계할 필요를 잘 안다. 시스템은 협력하는 모듈들의 집합으로 구성되어야 하고, 각 모듈은 다른 부분과 독립적인 기능을 구현해야 한다. 컴포넌트들이 레이어로 조직되기도 하는데, 각 레이어는 하나의 추상화 층을 이루게 된다. 이 레이어식 접근은 직교적 시스템을 설계하는 강력한 방법이다. 각 레이어는 자기 밑에 있는 레이어들이 제공하는 추상화만을 사용하기 때문에, 코드에 영향을 끼치지 않으면서 아래에 있는 다른 구현들을 바꾸는 높은 유연성을 얻을 수 있다.

코딩

  • 코드의 결합도를 줄여라. 부끄럼타는 코드(shy code)를 작성하라. 즉 불필요한 어떤 것도 다른 모듈에 보여주지 않으며, 다른 모듈의 구현에 의존하지 않는 코드를 작성하라.
  • 전역 데이터를 피하라. 코드가 전역 데이터를 참조할 때마다, 코드는 해당 데이터를 공유하는 다른 컴포넌트와 묶이게 된다. 디자인 패턴에서 소개하는 singleton pattern은 특정 클래스의 객체가 단 하나의 인스턴스만을 갖도록 보장해준다. 하지만 많은 개발자들이 싱글튼 객체를 전역 데이터의 일종으로 남용한다.
  • 유사한 함수를 피하라. 종종 유사해 보이는 함수의 집합을 구현해야 할 떄가 있다. 중복 코드는 구조적 문제의 징후다. 디자인 패턴에서 소개한 스트래티지 패턴(strategy pattern)을 사용하여 더 나은 구현을 할 수는 없는지 고려해보기 바란다.

자신이 작성하는 코드를 항상 비판적으로 검토해 보는 습관을 기르기 바란다. 기회가 있을 때마다 코드의 구조와 직교성을 향상시키기 위해 노력하라. 이러한 프로세스를 리팩터링이라 부른다.

테스트

직교적으로 설계, 구현한 시스템은 테스트하기 더 쉽다. 시스템 컴포넌트 간의 상호작용이 형식화되고 제한되었기 때문에 시스템 테스트의 더 많은 부분을 각각의 모듈 수준에서 수행할 수 있기 때문이다. 이는 모듈 수준의 테스트가 통합 테스트보다 테스트케이스를 만들고 수행하기 훨씬 쉽다는 점에서 좋은 소식이라 할 수 있다.

직교적으로 살아가기

DRY 원리는 시스템 내부의 중복을 최소화시키고, 직교성은 시스템 컴포넌트 간의 상호의존도를 줄인다. 당연한 말이겠지만 DRY 원리로 무장하고 직교성 원리를 충실히 사용한다면 개발하고 있는 시스템이 더 유연하고, 이해하기 쉽고 또한 디버그, 테스트, 유지도 쉬워질 것이다.

9. 가역성

당신이 가진 생각이 딱 하나밖에 없다면, 그것만큼 위험한 것은 없다.

이 책의 많은 주제들은 유연하고, 적응 가능한 소프트웨어를 만드는 방법에 대해 설명한다. 이런 추천안들을 따른다면 중요하면서도 되돌릴 수 없는 결정을 가능한 한 줄일 수 있게 될 것이다. 이는 우리가 프로젝트 초기에 항상 최선의 결정을 내리는 것은 아니라는 점에서 매우 유용하다.

Tip 14

최종 결정이란 없다.

유연한 아키텍처

많은 사람들이 코드를 유연하게 유지하려고 노력한다. 하지만 아키텍처, 배포, 벤더 통합 영역의 유연성에 대해서도 관심을 기울일 필요가 있다.

보통 특정 벤더 제품에 대한 의존도 등은 잘 정의하고 추상화한 인터페이스를 통해 감출 수 있다. 만약 벤더 의존적인 코드를 깨꿋하게 분리하지 않으면 어떻게 될까? 벤더 의존적인 수행문들이 코드 전반에 흩어져 있을 것이고, 이는 유지보수성, 유연성을 극도로 떨어뜨리게 된다.

누구도 미래에 대해서는 알 수 없으며, 우리라고 예외는 아니다.

10. 예광탄

어둠 속에서 빛을 내는 코드

예광탄이 효과가 있는 까닭은 일반 탄환과 동일한 환경과 제약 조건에서 발사되고 날아가기 때문이다. 탄환이 목표물에 도달하는 시간이 짧기 때문에, 기관총 사수는 즉각적인 반응을 얻을 수 있다. 그리고 실용적인 관점에서 봐도 예광탄은 상대적으로 비용이 적게 드는 방법이다.

코딩에서도 동일한 효과를 얻으려면, 우리는 요구사항으로부터 최종 시스템의 일부 측면에까지 빨리, 눈에 보이게, 반복적으로 도달하게 해줄 무언가를 찾아야 한다.

Tip 15

목표물을 찾기 위해 예광탄을 써라.

예광탄 코드는 나중에 버리려고 만드는 것이 아니다. 그것은 계속 사용할 코드다. 예광탄 코드에도 상용 코드와 마찬가지로 모든 에러 검사, 구조화, 문서화, 자기 검사가 포함된다. 단지 예광탄 코드에는 아직 완전한 기능이 들어있지 않을 뿐이다. 하지만 시스템을 구성하는 요소를 모두 연결해 놓은 후라면 목표물에 얼마나 가까이 다가섰는지 확인할 수 있으며, 필요하다면 조정도 할 수 있다. 그렇게 해서 일단 목표물을 맞춘다면 기능을 추가하는 일은 쉽다. 예광탄 개발 방법은 점진적인 접근 방법이다.

예광탄 코드 접근 방법에는 여러 장점이 있다.

  • 사용자들은 뭔가 작동되는 것을 일찍부터 보게 된다. 사용자들은 지금 보는 것이 아직 완성되지 않은 것임을 이해할 것이다. 이들은 기능이 없다고 실망하지 않을 것이며, 오히려 자기가 쓸 시스템에 눈에 보이는 진전이 있음을 보게 되어 매우 기뻐할 것이다. 바로 이 사용자들이 아마도 각 반복 주기마다 여러분이 얼마나 목표물에 가까이 갔는지 알려주 ㄹ사람들이 된다.

  • 개발자들은 들어가서 일할 수 있는 구조를 얻는다. 여러분의 팀은 더 이상 무에서 많은 것을 만들어 낼 필요가 없어진다. 그러면 모든 사람의 생산성이 더 좋아지고, 일관성도 촉진된다.

  • 통합 작업을 수행할 기반이 생긴다. 시스템의 요소들이 모두 연결된 다음에는, 코드를 추가할 수 있는 어떤 환경이 생긴다. 새로 도입된 변화가 어떤 영향을 주는지 더욱 명확하게 보이며, 상호작용들은 더 제한적이다. 그러므로 디버깅과 테스팅은 속도도 빨라지고 더 정확해질 것이다.

  • 보여줄 것이 생긴다

  • 진전 상황에 대해 더 정확하게 감을 잡을 수 있다. 예광탄 코드 개발 방법에서 개발자들은 유스 케이스를 한 번에 하나씩 다룬다. 하나가 끝나면, 다음으로 넘어간다. 이러면 수행을 평가하기도 쉽고 사용자들에게 얼마나 진전되었는지 보여주기도 쉽다.

예광탄이 언제나 목표물을 맞추는 것은 아니다

예광탄 코드 기법은 일이 어떻게 될지 100% 확신할 수 없는 상황에서 사용된다. 그러므로 처음 몇 번 시도 때 목표에 맞지 않더라도 놀랄 필요가 없다. 코드의 크기가 작으면 관성 역시 약하므로 빠르고 쉽게 바꿀 수 있다. 다른 어떤 방법보다 더 적은 비용과 더 빠른 속도로 애플리케이션에 대한 반응들을 모아서 새롭고 더 정확한 버전을 만들 수 있을 것이다.

예광탄 코드 대 프로토타이핑

프로토타입은 최종 시스템의 어떤 특정한 측면을 탐사해 보는 것이 목표다. 어떤 개념을 구현해 보려고 시도할 때 대충 끼워 맞춘 것들을 모두 버린 다음, 실험 과정에서 얻은 교훈을 바탕으로 다시 코드를 만들게 된다.

프로토타입은 나중에 버릴 수 있는 코드를 만든다. 예광탄 코드는 기능은 별로 없지만 완결된 코드이며, 최종 시스템 골격의 일부를 이룬다. 프로토타입을 예광탄이 하나라도 발사되기 전에 먼저 일어나는 정찰과 정보 수집으로 생각하면 되겠다.