Clean Code 주석 달기 - 2부

Seoyong Lee·2023년 4월 29일
0

개발 관련 생각들

목록 보기
7/9

지난 글에 이어서 클린코드를 읽어 보고 마음에 든 부분을 발췌하였다.

5장 형식 맞추기

코드 형식은 의사소통의 일환이다. 의사소통은 전문 개발자의 일차적인 의무다. (96p)

  • 개발자의 의사소통이 중요하다는 말은 많이 들어보았지만 사실 그 의사소통의 범위에는 코드 컨벤션도 포함되어 있었다!

오늘 구현한 기능이 다음 버전에서 바뀔 확률은 아주 높다. 그런데 오늘 구현한 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다. (96p)

  • 코드의 형식을 맞추는 부분은 간단히 협업 편의를 의한 것으로 생각했다. 그러나 여기서 나아가 앞으로 작성할 코드의 방향을 결정할 수 있다는 생각은 조금 신선했다.
  • 책에서 FitNesse의 사례를 들며 언급했던 '평균 200줄 정도의 파일로도 대규모 시스템을 구축할 수 있다'는 말도 중요한 포인트였다. 경험상 무식한 방법일 수 있지만 한 파일에 강제로 몇 줄 이상 넘지 못하도록 제한을 두기만 해도 많은 부분이 자연스럽게 개선되는 것을 느꼈다.

신문 기사처럼 작성하라 (98p)

  • 파일 안에서도 신문과 같은 체계가 필요하다. 상단에는 고차원 개념과 알고리즘을 설명한다. 아래로 내려갈수록 자세하게 세부 내용을 묘사한다.

개념적 유사성. ...친화도가 높을수록 코드를 가까이 배치한다. (106p)

  • 한 함수가 다른 함수를 호출하거나 다른 변수를 사용한다면 서로 가까이 두는 것이 가독성을 위해 좋을 것이다.
  • 함수 내부에서 사용되는 지역변수는 함수가 작다는 가정하에 각 함수의 시작 부분에 선언한다. 만약 함수가 점점 커지면서 상단에 선언된 변수와의 거리가 멀어져 불편해진다면 이 또한 함수 분리의 신호로 볼 수 있겠다.

탐은 한 가지 규칙에 합의해야 한다. 그리고 모든 팀원은 그 규칙을 따라야 한다. 그래야 소프트웨어가 일관적인 스타일을 보인다. 개개인이 따로국밥처럼 맘대로 짜대는 코드는 피해야 한다. (113p)

  • 5장에서 가장 중요한 글이라고 생각한다. 팀은 기본적으로 하나의 규칙에 합의하고 커밋해야 한다. 그러나 항상 시간이 지나면 조금씩 흐트러지는 경우가 발생한다. 이를 방지하고 일관성 있게 규칙을 유지하려면 팀원의 코드를 확인하는 리뷰에 필수적으로 참여해야 한다.

6장 객체와 자료 구조

객체는 동작을 공개하고 자료를 숨긴다. ...자료 구조는 별다른 동작 없이 자료를 노출한다. ...시스템을 구현할 때, 새로운 자료 타입을 추가하는 유연성이 필요하면 객체가 더 적합하다. 다른 경우로 새로운 동작을 추가하는 유연성이 필요하면 자료 구조와 절차적인 코드가 더 적합하다. (127-128p)

  • 6장의 내용은 클래스와 관련된 복잡한 내용인 것 같지만 실은 윗글로 전부 요약해볼 수 있었다. 자료 타입의 잦은 변경이 예상되면 일반적인 객체를 사용하라. 동작(함수)이 자주 추가되거나 수정될 것으로 예상되면 공개 변수만 있고 함수가 없는 클래스인 자료 전달 객체(DTO: Data transfer Object)와 절차적인 코드를 사용하라.

디미터 법칙(Law of Demeter)은 잘 알려진 휴리스틱으로, 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다.

  • 객체는 자료를 숨기고 함수를 공개한다. 즉 조회 함수로 내부 구조를 공개하면 안 된다.

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath()
흔히 위와 같은 코드를 기차 충돌(train wreck)이라 부른다. 여러 객차가 한 줄로 이어진 기차처럼 보이기 때문이다.

  • 책에 의하면 위 코드의 개선 방법은 다음과 같다.
    	Options opts = ctxt.getOptions();
    	File sctatchDir = opts.getScratchDir();
    	final String outputDir = sctatchDir.getAbsolutePath();
  • 실제 코드를 작성하면서 위 같이 사용했던 경우가 많았는데 좋은 수정 근거를 찾을 수 있었다.

7장 오류 처리

오류 코드보다 예외를 사용하라. (130p)

  • 이 부분은 코드를 보면 명확하게 이해할 수 있다.

    if () { 
    	// 동작
      if () { 
        // 동작
      } else {
        // 에러 처리
      }
    } else {
      // 에러 처리
    }
  • 위 같은 방식은 끔찍하다. 예외를 지원하지 않는 프로그래밍 언어가 많았던 시절에 쓰던 방법이었지만 이제 다음과 같이 예외를 던지는 방법을 사용할 수 있다.

    try {
    	동작함수()
    } catch (e) {
    	// 에러 처리
    }
    
    동작함수 () {
      동작();
      throw new Error("에러 발생");
    }
  • 위 코드는 결과적으로 동작 알고리즘과 에러 처리 알고리즘을 분리하여 코드 품질도 나아졌다.

    try-catch-finally 문부터 작성하라. (132p)

  • 예외가 발생할 코드를 짤 경우 try-catch-finally 구조로 먼저 시작해 보자. try-catch 구조로 범위를 정의한 이후 TDD로 나머지 논리를 추가하면 코드 작성을 수월하게 할 수 있다고 한다.

    null을 반환하지 마라 (138p)
    null을 전달하지 마라 (140p)

  • 실제로 코드를 작성하다 데이터 요청의 예외처리를 하는 경우 이상하게 null을 반환하도록 짜고 싶어진다. 그러나 이는 추후 null을 확인하는 코드를 모든 곳에 붙이도록 만드는 악습이다.
  • 만약 null을 반환하도록 작성한 이후에 누군가가 실수로 null 확인을 빼먹으면 서비스는 고장 나게 된다. 이를 방지하기 위해서는 그냥 예외를 던지거나 특수 사례 객체를 반환하도록 수정해야 한다.

오류 처리는 조금 더 나아가 특수 상황을 직접 처리할 필요 자체를 없애는 것이 좋다고 한다. 특수 사례를 처리하는 클래스나 객체를 만들어 사용하면 클라이언트 코드가 예외 상황을 직접 처리할 필요가 없어진다.

8장 경계

곧바로 우리쪽 코드를 작성해 외부 코드를 호출하는 대신 먼저 간단한 테스트 케이스를 작성해 외부 코드를 익히면 어떨까? 짐 뉴커크(Jim Newkirk)는 이를 학습 테스트라 부른다. (147p)

  • 보통 프론트엔드 개발을 진행하다보면 수많은 외부 라이브러리를 사용한다. 이를 안전하게 사용하려면 학습 테스트를 진행해야 한다고 한다.
  • 솔직히 학습 테스트가 존재한다는 점은 처음 알았다. 그러나 사용 중인 외부 라이브러리의 버전 변경으로 서비스에 문제가 일어나는 경험은 매우 흔하다. 이를 방지하기 위해 간단한 테스트를 작성하는 것은 충분히 가치 있다고 생각된다.

소프트웨어 설계가 우수하다면 변경하는데 많은 투자와 재작업이 필요하지 않다. ...통제하지 못하는 코드를 사용할 때는 너무 많은 투자를 하거나 향후 변경 비용이 지나치게 커지지 않도록 각별히 주의해야 한다.(151p)

  • 이는 외부 패키지를 사용할 때 명심해야 할 원칙으로 가능한 의존도를 낮추는 것이 중요하다고 한다.
  • 외부 패키지 호출 코드 자체를 줄여 경계를 관리해야 한다는 말에도 100% 동의한다. 남의 작업물에 나의 코드와 서비스를 맡길 수는 없다. 불필요하게 외부 코드를 사용하더라도 경계 관리가 반드시 함께 적용되어야 한다.

다음 장에서 이어집니다.

0개의 댓글