본문 바로가기

Book

실용주의 프로그래머(3~4장)

3. 기본도구

3장에서는 프로그래밍을 할 때 필요한 도구들과 관련된 숙련법들을 정리했다

Topic 16 일반 텍스트의 힘

프로그래머로서 기본 재료는 지식인데, 이 지식은 전부 텍스트로 이루어진다. 여기서는 텍스트 그 자체의 특징과 장점을 말해준다. 예를 들어 이진 포맷 같은 특정 형태는 분명 이점이 존재하지만 데이터를 이해하는데 필요한 문맥이 데이터 자체와 별도로 분리되어 있다는 문제가 있다. 즉, 인위적으로 데이터와 그 데이터의 의미가 분리된 것이다. 가능하다면 텍스트로 기록해 놓는다면 시간이 지나도 쉽게 사용이 가능하다.

Topic 17 셸 가지고 놀기

여기선 셸 명령어가 얼마나 유용하고 생산성을 증가시켜주는지 말해준다. 각각 독립적으로 동작하는 명령어를 잘 조합해서 사용하면 자동화에 큰 도움이 될 거 같다.

Topic 18 파워 에디팅

에디터의 중요성과 숙련이 필요함을 강조한다. 여기 보면서 다시 vim을 써야 겠다고 생각이 들었다.

Topic 19 버전 관리

버전 관리는 지금은 그냥 기본적으로 쓰고 있어서 중요한건 알고 있었지만 여기서는 그 이유들을 정리해 줬다. 특이한 내용이 있었는데, 공유 디렉터리와 버전 관리 시스템의 차이를 설명하는 부분이었는데, 여기서 버전관리를 사용하면서 클라우드 같은 공유 디렉터리를 사용하면 둘의 장점을 사용해서 좋을 거 같다고 하지만 오히려 위험할 수 있다고 했다. 클라우드의 자동 동기화와 버전 관리 시스템이 충돌할 경우를 말하는 거 같다.

Topic 20 디버깅

버그가 발생했을때의 심리와 대응하는 방식들에 대한 이야기가 있다. 뒤에 대응 전략들은 의식적으로 적용하려고 노력해야겠다.

  • 버그 재현하기
    • 코드를 고치기전에 테스트부터 해야 한다
  • 오류 메시지 제발 제대로 읽기
    • 앞에도 나오지만 당황하지 말고 차분하게
  • 이진 분할을 생각해 보자(몇 가지 시나리오)
    • 방대한 스택 트레이스
      • 이진 분할로 체크하면서 정상인 부분 찾기
    • 입력값에 따라 결과가 바뀜
      • 특정 데이터 셋에서 이진 분할 탐색하면서 최소한의 데이터 셋을 찾아라
    • 릴리즈 사이에 발생한 문제
      • 과거 버전들을 이진 분할로 탐색하면서 어디서부터 문제인지 확인
  • 로깅과 트레이싱(가장 자주 사용하는 방법 같다)
  • 고무오리
    • 누군가에게 가르친다 생각하고 문제를 처음부터 정리해 보기
  • 소거법
    • 변화가 생긴 코드부터 의심하는 게 맞음 일단 외부 제품들, 환경은 나중에 의심해 볼 대상

Topic 21 텍스트 처리

범용 텍스트 처리 도구가 있으면 좋다고 함. 저자들이 만든 텍스트 처리도구 예시가 나와있음. 처음 강조한 일반적인 텍스트를 처리하는 게 이점이 많음을 강조함.

파이썬이 워낙 강력한 스크립트 언어이니 파싱 관련해서 공부를 더 해봐야겠다.

Topic 22 엔지니어링 일지

이건 지금도 필요성을 많이 느낀다. 애초에 무슨 일을 하든 일지를 써야겠다고 느꼈다. 저자는 손으로 직접 쓰는 걸 추천하는데 안 하는 것보단 나으니 핸드폰으로라도 의식적으로 생각난 걸 기록하는 연습을 해야겠다.

4. 실용주의 편집증

4장은 현실적으로 완벽한 소프트웨어는 존재할 수 없기 때문에 실수에 대한 방어책을 어떤 식으로 세워야 하는지 알려준다.

Topic 23 계약에 의한 설계

이 토픽에서는 DBC(Design by Contract) 개념을 소개한다. 간단히 정리하면 프로그램의 정확성을 보장하기 위해 소프트웨어 모듈의 권리와 책임을 문서화하고 합의하는 데에 초점을 맞췄다고 한다. 크게 3가지로 구성될 수 있다.

  • 선행 조건
    • 루틴 호출 전 참이어야 하는 조건이다. 루틴의 선행 조건이 위반된다면 루틴이 호출되어선 안됨. 제대로 된 데이터를 전달하는 것은 호출하는 쪽의 책임
  • 후행 조건
    • 루틴이 완료되었때의 시스템의 상태. 루틴에 후행조건이 있다는 것은 종국에 종료될 것을 의미
  • 클래스 불변식
    • 호출자의 입장에서 볼 때는 이 조건이 언제나 참인 것을 클래스가 보장한다. 내부 처리 중에는 불변식이 참이 아닐 수 있지만, 루틴이 종료되고 호출자에게 제어권이 넘어가면 참이 되어야 함
    • 여기서는 클래스 불변식이라 했지만 함수형 언어에도 해당된다. 함수형 언어에서도 불변성 특징이 강조되는데, 여기서 클래스라 정리된걸 상태로 생각하면 될 거 같다.

또한 여기서 테스트 접근 방법을 기준으로 TDD와 DBC를 비교한 내용이 있다.

  • DBC는 테스트 환경 구성이나 목(mock)이 필요 없다.
  • DBC는 모든 입력값에 대해 성공과 실패를 정의한다. 반면에 테스트는 하나가 한 가지 경우만 다룬다.
  • TDD와 다른 테스트는 빌드 과정 중 ‘테스트’할 때만 수행된다. 하지만 DBC와 단정문은 영원하다. 설계, 개발, 배포, 유지 보수 전체에 걸쳐 사용된다.
  • TDD는 테스트 중인 코드 내의 내부 불변식을 확인하는 것에 초점을 두지 않는다. 그보다는 공개 인터페이스를 확인하는 블랙박스 방식에 더 가깝다.
  • DBC는 방어적 프로그래밍보다 더 효율적이고 더 DRY 하다. 방어적 프로그래밍에서는 아무도 데이터를 검증하지 않는 상황에 대비하기 위해 모든 사람이 데이터를 검증한다.

code에서 DBC를 지원하지 않는 경우에는 해당하는 앞에서 정리한 3가지를 기반으로 주석이나 테스트를 작성해 볼 수 있다. 몇몇 언어에서는 단정문을 사용해서 부분적으로나마 흉내 낼 수 있다. 완벽할 수는 없는 게 상속 같은 경우에는 전부 자동화해서 처리하기가 힘들다.

또한 이 DBC는 ‘일찍 작동을 멈춰라’라는 개념과 잘 어울린다고 한다. 선행조건과 후행조건, 불변식을 검증하면 더 일찍 멈추고, 문제에 대한 보다 정확한 정보를 알려줄 수 있을 것이라 한다.

Topic 24 죽은 프로그램은 거짓말을 하지 않는다

여기서는 위에서 잠깐 언급한 작동을 멈추라는 개념에 대해 좀 더 자세하게 이야기를 한다. 예외처리도 애플리케이션 코드와 함께 지저분하게 작성되는 거보다 차라리 그냥 에러 던지고 작동 빨리 멈추고 수정하는 게 낫다고 함. 물론 상황에 따라서 다르게 구현되어야 한다. 로깅이 필수적인 상황이라던지 여러 상황을 봐가면서 적용해야 한다.

Topic 25 단정적 프로그램

단정문을 사용하여 작성하는 코드의 예시와 상황을 설명한다. DBC 이야기하면서 나왔지만 조건에 맞게 단정문을 사용하면서 코드를 작성하는 게 좋다. 단정문은 불가능한 상황을 체크하기 위해서 사용되는 것이다. 특정 로직에 직접적으로 쓰이는 게 아니다. 또한 이터레이션처럼 동작마다 값이 달라지는 기능에서는 쓰이면 안 된다.

또한 누군가 성능에 영향을 주기 때문에 단정문을 꺼야 한다고 주장하는 사람이 있는데, 정말 치명적인 상황 아니면 끌필요가 없다.

Topic 26 리소스 사용의 균형

여기서는 리소스를 할당하고 해제되지 않은 상황들에 대한 이야기를 한다. 기본적인 원칙은 다음과 같다

‘자신이 시작한 것은 자신이 끝내라’. 함수나 객체에서도 기본적으로 할당했으면 해제까지 책임을 지도록 코드를 작성해야 한다.

중첩할당하는 부분에서 몇 가지 팁을 제안해 준다.

  • 리소스를 할당한 순서의 역순으로 해제하라. 이렇게 해야 한 리소스가 다른 리소스를 참조하는 경우에도 참조를 망가트리지 않는다.
  • 코드의 여러 곳에서 동일한 구성의 리소스들을 할당하는 경우에는 언제나 같은 순서로 할당해야 교착 가능성을 줄일 수 있다. 프로세스 A가 resource1을 이미 확보하고서 resource2를 획득하려고 하고 있는데 프로세스 B는 resource2를 확보한 상태로 resource1을 막 요청하려고 하면, 두 프로세스 모두 영원히 기다리게 될 것이다.

위의 패턴은 리소스의 종류에 상관없이 적용할 수 있다. 또한 객체지향언어를 사용한다면 리소스를 클래스 안에 캡슐화에서 사용하는 게 좋다. 다음으로는 동적자료 구조 사용하는 경우에 자원 해제하는 3가지 방식을 제안한다.

  • 최상위 구조가 자기 안에 들어 있는 하위 구조들을 해제할 책임을 진다. 하위 구조들은 또다시 재귀적으로 자기 안에 들어 있는 자료들을 해제할 책임을 지고, 이런 식으로 반복된다.
  • 최상위 구조가 그냥 할당 해제된다. 최상위 구조가 참조하던 하위 구조들은 연결이 끊어져서 다른 곳에서 참조하지 않는다면 외톨이가 된다.
  • 최상위 구조가 하나라도 하위 구조를 가지고 있으면 자신의 할당 해제를 거부한다.

마지막으로 프로그래머는 자신을 포함해서 아무도 믿지 않기 때문에 실제로 점검하는 코드를 작성하는 것을 좋아한다. 대부분의 애플리케이션에서는 리소스의 종류별로 Wrapper을 만들어 모든 할당과 해제 기록을 보관하는 것을 뜻한다.

Topic 27 헤드라이트를 앞서 가지 마라

여기서는 너무 먼 미래를 무리해서 내다보면서 개발하지 말라는 얘기를 한다. 분명 미래를 계획하고 준비하는 건 필요하지만, 너무 미래의 일에 맞춰져 있다면 틀릴 가능성이 훨씬 높아진다고 한다. 불확실한 미래를 대비한다고 시간 쓸 바에는 현재 코드를 ETC를 지향하면서 짜려고 노력해야 한다. 마지막으로 내가 좋아하는 나심 탈렙의 ‘블랙스완’ 이야기가 나오는데 소프트웨어 개발에서도 당연히 해당되는 이야기다. 미래는 모른다.

 

참고 및 출처: <실용주의 프로그래머(20주년 기념판)> (데이비드 토머스, 앤드류 헌트 지음, 정지용 옮김, 김창준 감수 인사이트 , 2022)