실용주의 프로그래머 2. 실용주의 접근법

jiffydev·2021년 8월 1일
0

1. 중복의 해악

소프트웨어는 항상 변한다. 요구사항이 변경될 수도 있고, 규제로 인해 어쩔 수 없이 변경하거나 비즈니스 로직 자체가 바뀔 수도 있다.
이는 개발자가 개발의 대부분을 유지보수에 사용해야 한다는 것을 뜻한다.
그런데 유지보수는 개발이 끝나야 시작하는 것이 아니다. 개발 과정부터 유지보수는 필요하다.

이 때 발생할 수 있는 문제는 개발 중에 지식을 중복해 넣기 쉽다는 것이다.
그래서 소프트웨어를 유지보수하기 쉽게 만드려면 DRY(Don't Repeat Yourself) 원칙을 따라야 한다.

중복이 어떻게 생기는지, 그리고 이를 어떻게 다루어야 하는지는 아래와 같다.

  • 강요된 중복
    개발을 하다보면 환경이 우리에게 중복을 강요하는 것처럼 보일 때가 있다. 프로젝트 표준에서 중복된 정보를 요구할 때도 있고, 멀티 플랫폼인 경우 서로 다른 개발 환경에서 공유된 정의가 중복되거나, 프로그래밍 언어 자체가 구조적으로 중복을 요구하기도 한다.
    하지만 아래 기법들을 통해 DRY 원칙을 따르면서 중복을 제거할 수 있다.

    • 정보의 다양한 표현 양식
      동일한 정보가 다른 형태로 표현되어야 할 때, 필터나 코드 생성기를 작성해 공동의 메타데이터 표현에서 여러 개의 언어에 걸쳐 있는 구조를 만들 수 있다.
    • 코드내의 문서화
      주석이 많다고 좋은 것이 아니다. 오히려 나쁜 코드가 많은 주석을 필요로 한다.
      낮은 차원의 지식은 코드가 알아서 설명하도록 하고, 주석은 높은 차원의 설명을 위해 사용해야 한다.
    • 문서화와 코드
      문서를 작성하고 코드를 작성한다. 그런데 일정이 촉박해지면 문서화는 미루고 코드를 먼저 작성하게 되는 경험은 다들 해 봤을 것이다. 문서화도 코드를 통해 자동화한다.
  • 부주의한 중복
    때로는 설계 실수로 인해 중복이 발생하기도 한다.
    데이터가 정규화되지 않았다면 정규화하자.
    성능상의 이유로 DRY 원칙을 위배해야 한다면 미치는 영향을 최소한으로, 노출되지 않도록 해야 한다.

  • 참을성 없는 중복
    시간의 압박으로 인해 기존 코드를 복사해서 약간만 수정한 채 사용하려는 경우가 있다. 하지만 이 방식으로 당장은 문제를 해결할 수 있어도, 결국 이로 인해 더 큰 문제가 터지게 된다.
    참을성 없는 중복은 발견하기도 쉽고 다루기도 쉽지만 중복을 일으키지 않기 위해 시간을 투자하려는 의지가 있어야 한다.

  • 개발자간의 중복
    한 프로젝트에서 일하는 서로 다른 개발자 사이에서 중복이 발생하면 찾기도 어렵고 해결하기도 어렵다.
    최선책은 개발자간에 적극적이고 빈번한 소통을 장려하는 문화를 안착시키는 것.

2. 직교성

컴퓨터 분야에서 직교성이란 시스템의 한 부분이 변경되어도 다른 부분에 영향을 주지 않는 것을 의미한다. 결합도가 낮은 상태와도 동일한 의미이다.
직교적인 시스템을 통해 변화의 영향력이 최소화됨으로써 개발/테스트 시간이 감소하고, 재사용이 쉬워져 생산성이 향상된다. 또한 문제가 발생하더라도 한 부분에만 국한되고 다른 컴포넌트로 퍼지지 않기 때문에 시스템이 안정적이고 리스크가 감소하게 된다.

이와 같은 직교성의 원칙을 여러 분야에서 적용할 수 있다.


  • 프로젝트 팀 구조를 직교적으로 만들어야 한다. 책임을 잘 정의하고 분배해서 중복이 최소화된 그룹으로 나누어야 한다.

  • 설계
    개발자마다 방식이나 용어는 조금씩 다르지만 직교적인 시스템을 설계할 필요성에 대해서는 누구보다 잘 알고 있다.
    독립적인 모듈, 컴포넌트를 레이어로 구성하고, 각 레이어를 계층화해서 추상화된 층을 만듦으로써 유연성을 높일 수 있다.
    또한 현실 세계와 설계 사이의 결합도에 대해서도 생각해볼 필요가 있다. 자신의 힘으로 제어할 수 없는 속성에 의지해서는 안된다.

  • 툴킷과 라이브러리
    툴킷, 라이브러리를 새로 도입할 때 시스템의 직교성을 해치지는 않는지 주의 깊게 검토해야 한다.
    만약 특별한 방식으로 객체를 생성하고 접근해야 한다면 직교적이지 않은 구조이다.

  • 코딩
    기능 하나에 집중해 코딩하게 되면 큰 맥락을 보지 못하고 의도치 않게 직교성이 떨어지는 코드를 작성하게 될 가능성이 있다.
    코딩을 하면서 직교성을 유지할 수 있는 기법은 다음과 같다.

    • 코드의 결합도 줄이기
      shy code를 작성한다. 필요한 것 외에는 다른 모듈에 보여주지 않고, 다른 모듈의 구현에 의존하지 않는 코드를 작성한다.
    • 전역 데이터 피하기
      전역 데이터를 참조하면 해당 데이터를 공유하는 다른 컴포넌트와 묶이게 된다.
      모듈이 필요로 하는 context를 명시적으로 넘겨주는 방식이 더 바람직하다.
    • 유사한 함수 피하기
      유사해 보이는 함수를 만들어야 하는 경우, 시작과 끝은 공통 코드지만 중간의 알고리즘만 다르다면 디자인 패턴 중 strategy pattern을 사용해 구현할 수 없는지 고민해 본다.
  • 테스트
    직교적인 시스템일수록 테스트하기 쉽다. 단위 테스트를 통해 작성한 시스템이 직교적인지 확인할 수 있다.

3. 가역성

소프트웨어에서 정해진 단 한가지의 방법이란 없다.
그렇기 때문에 여러 방법을 사용할 수 있고, 이 방법들을 적용할 수 있는 유연한 구조가 필요하다.
특정한 벤더에 의존하더라도 잘 추상화해서 코드를 분리하면 변화에 대처할 수 있다.

4. 예광탄

어둠 속에서 총을 쏴 목표를 맞추기 위한 방법은 크게 두 가지가 있을 것이다.
목표물을 맞추기 위해 필요한 요소(환경, 거리 등)를 면밀히 분석한 후에 쏘는 법과 예광탄을 사용해 일단 쏴 본 후 내가 쏜 탄환이 목표에 맞았는지 확인하는 방법이다.

익숙하지 않은 기술을 사용해야 할 때 이런 예광탄같은 코드를 적용할 수 있는데, 완전한 기능을 구현하려고 하는 대신 우선 요구사항을 점진적으로 만족시켜 나가는 방식으로 적용하게 된다.

이런 예광탄 코드는 사용자로 하여금 실제로 돌아가는 것을 일찍 보게 할 수 있고, 개발자는 아무것도 없는 백지에서 시작할 필요가 없어져 시스템의 일관성을 유지하기도 쉽다.

물론 예광탄 코드가 항상 요구사항에 100% 맞춘다는 보장은 없다. 오히려 그런 불확실한 상황이기 때문에 작고 유연한 예광탄 코드가 목표를 달성하도록 수정하기가 쉽다.

이러한 예광탄 코드가 프로토타입과 무슨 차이가 있는지 애매해 보일 수 있다.
하지만 둘의 가장 큰 차이는 프로토타입이 최종 시스템의 일부를 구현한 것이라면 예광탄 코드는 최종 시스템의 전체적인 구조를 구현하고 그 위에 살을 붙여 나가기 위한 프레임워크이다.

5. 프로토타입과 포스트잇

프로토타입은 위험 요소를 분석하고 노출시켜 저렴한 비용으로 바로잡을 수 있는 기회를 주기에 유용하다.
소프트웨어에서도 프로토타입을 만들 수 있는데, 이 때 반드시 코드로 작성할 필요는 없다.
프로토타입은 위험을 수반하는 모든 것이 대상이 될 수 있으며, 프로토타입을 통해 학습하는 것이 목적이다.

프로토타입을 코드로 만들 때는 이 코드가 사용되지 않으리라는 것을 반드시 주지시켜야 하는데, 이를 제대로 하지 못하면 프로토타입을 보완해 배포하자고 주장할 수 있다.

6. 도메인 언어

언어는 문제에 대해 생각하는 방식과 의사소통에 대해 생각하는 방식에 영향을 미친다.
그래서 문제 도메인의 언어, 어휘를 사용하면 시스템이 어떻게 동작해야 하는지 더 잘 알 수 있고, 해결방안을 제시하기도 한다.
문제 도메인에 가까워지면 더 높은 추상화 수준에서 작업함으로써 사소한 구현 세부사항 대신 도메인의 문제에 집중할 수 있다.

각각의 문제 도메인에 맞는 소형 언어를 만들 수도 있는데, 간단하게 구현하는 방식은 파싱할 수 있는 라인 중심 형식으로 만드는 방식이 될 것이다.
또는 BNF를 사용해 문법을 정의하고, 이를 기반으로 좀 더 복잡한 언어를 구현할 수도 있다.

이렇게 구현한 언어는 데이터 언어와 명령형 언어로 쓰일 수 있다.
데이터 언어는 애플리케이션이 사용할 형식의 데이터 구조를 만들고, 환경설정 정보를 표현하기 위해 쓰인다.
명령형 언어는 실제로 실행되고 스크립트처럼 만들어 사용할 수 있다.

7. 추정

추정치를 요구하는 질문에 대답하기 전에, 나의 답변이 어떤 상황에 사용될지를 먼저 파악해야 한다.
또한 추정에서 사용하는 단위가 결과의 해석에 차이를 가져오기도 한다.

추정치를 도출하는 과정은 아래와 같은 단계를 거치게 된다.

  • 무엇을 묻고 있는지 이해하기
    질문에 명시적으로 드러나지는 않지만 무엇을 묻고 있는지 이해함으로써 도메인의 범위에 대해 감을 잡아야 한다.

  • 시스템의 모델 만들어 보기
    요청을 이해했으면 대략적인 모델을 만든다. 이를 통해 시스템 구현에 대한 대략적인 그림이 나오고, 이 과정에서 그동안 드러나지 않았던 이면의 패턴과 프로세스를 발견하게 된다.

  • 모델을 컴포넌트로 나누기
    모델을 컴포넌트로 분해해 어떻게 상호작용하는지에 대해 기술하는 수식을 찾아야 한다. 그래서 각 컴포넌트가 모델에 영향을 미치는 매개변수가 무엇인지를 규명한다.

  • 각 매개 변수에 값을 주기
    매개변수를 찾았다면 결과에 큰 영향을 미치는 매개변수가 무엇인지 규명하고 그 매개변수의 값을 최대한 정확히 산출해야 한다. 보통 큰 영향을 미치는 값은 곱하거나 나누는 값들이다.

  • 답 계산
    시스템이 복잡해지면 추정치는 하나의 답이 나오기 어렵다. 따라서 중요 매개변수들의 값을 변경시켜 가면서 어떤 것이 모델과 잘 맞는지 찾아야 한다.
    만약 이상한 값이 나오더라도 버리지 말고 문제를 잘못 이해한 것은 아닌지, 모델이 잘못되지는 않았는지 확인하는데 사용할 수 있다.

  • 추정치 기록
    추정치가 잘못되었더라도 도망가지 말고 왜 달라졌는지 원인을 파악해야 한다.

프로젝트 일정을 추정하는 것은 프로젝트를 해 봐야 알기 때문에 불가능에 가깝다고 생각할 수 있다.
하지만 점증적 개발을 통해 1. 요구사항 체크, 2. 위험 분석, 3. 설계, 구현, 통합, 4. 사용자와 함께 검증의 과정을 반복하고, 초기 기능의 구현과 테스트를 한 번의 반복으로 삼아 일정을 추정할 수 있다.

profile
잘 & 열심히 살고싶은 개발자

0개의 댓글