Skip to main content

김주엽

1. 📌 핵심 개념 정리

✅ 요약하기

  1. 주석
    • 부적절한 정보
      • 변경 이력과 같은 주석은 적절하지 못하다.
      • 일반적으로 작성자, 최종 수정일, SPR(Software Problem Repost) 번호 등과 같은 메타 정보를 주석으로 남긴다.
      • 주석은 코드와 설계에 기술적인 설명을 부연하는 수단이다.
    • 쓸모 없는 주석
      • 오래된 주석, 엉뚱한 주석, 잘못된 주석은 쓸모가 없다.
      • 쓸모 없어질 주석을 작성하지 않는 것이 가장 좋지만 작성했다면 재빨리 삭제하는 것이 좋다.
      • 쓸모 없는 주석은 코드를 그릇된 방향으로 이끈다.
    • 중복된 주석
      • 코드만으로 충분한데 구구절절 설명하는 주석을 중복된 주석이라고 한다.
        • 예시
          i++; // i 증가
          
      • 함수 서명만 달랑 기술하는 Javadoc 주석
        • 예시
          /**
           * @param sellRequest
           * @return
           * @throws ManagedComponentException
           * **/
          public SellResponse beginSellItem(SellRequest sellRequest) 
              throws ManagedComponentException {
          }
          
    • 주석 처리된 코드
      • 코드를 읽다가 주석으로 처리된 코드가 나오면 아주 거슬린다.
      • 오래된 코드인지 중요한 코드인지 구분할 수 없기에 아무도 삭제하지 않는다.
        • 누군가에게 필요하거나 다른 사람이 사용할 코드라 판단하기 때문이다.
      • 주석으로 처리된 코드는 읽는 사람을 헷갈리게 만든다.
      • 이전 코드는 코드 관리 시스템이 기억하기에 걱정말고 바로 지우도록 하자.

  1. 환경
    • 여러 단계로 빌드해야 한다.
      • 빌드는 간단히 한 단계로 끝나야 한다.
      • 불가해한 명령이나 스크립트를 실행해 각 요소를 따로 빌드할 필요가 없어야 한다
      • 시스템에 필요한 파일을 찾느라 여기저기 찾으러 다닐 필요가 없어야 한다.
      • 한 명령으로 전체를 체크아웃해서 빌드할 수 있어야 한다.
    • 여러 단계로 테스트해야 한다.
      • 모든 단위 테스트는 한 명령으로 돌려야 한다.
        • IDE에서 버튼 하나로 테스트가 가능한 것이 가장 이상적이다.
      • 아무리 열악한 환경이라도 셸에서 명령 하나로 가능해야 한다.

  1. 함수
    • 너무 많은 인수
      • 함수에서 인수는 작을수록 좋고 아예 없으면 가장 좋다.
      • 넷 이상은 그 가치가 아주 의심스럽기에 최대한 피한다.
    • 출력 인수
      • 일반적으로 독자는 인수를 입력으로 간주한다.
      • 함수에서 뭔가의 상태를 변경해야 한다면 함수가 속한 객체의 상태를 변경한다.
    • 플래그 인수
      • boolean 인수는 함수가 여러 기능을 수행한다는 명백한 증거다.
      • 플래그 인수는 혼란을 초래하므로 피하라.
    • 죽은 함수
      • 아무도 호출하지 않는 함수는 과감하게 삭제한다.
      • 소스 코드 관리 시스템이 모두 기억하기에 걱정할 필요 없다.

  1. 일반
    • 한 소스 파일에 여러 언어를 사용한다.
      • 오늘날 프로그래밍 환경은 한 소스 파일 내에서 다양한 언어를 지원한다.
        • 자바 소스 파일에서 XML, HTML, YAML, Javadoc 등을 포함한다.
        • JSP 파일에서 HTML, 자바, 태그 라이브러리 구문, 영어 주석, JavaScript 등을 포함한다.
      • 이는 좋게 말하면 혼란스럽고 나쁘게 말하면 조잡하다.
      • 한 소스 파일에 언어 하나만 사용하는 방식이 가장 좋다.
      • 현실적으로 어렵다면 소스 파일에서 언어 수와 범위를 최대한 줄이도록 한다.
    • 당연한 동작을 구현하지 않는다.
      • 최소 놀람의 원칙(The Principle of Least Surprise)에 의거해 함수나 클래스는 당연한 동작과 기능을 제공한다.
      • 당연한 동작을 구현하지 않으면 코드를 읽거나 사용하는 사람이 함수 기능을 예상하기 어렵다.
      • 저자를 신뢰하기 어려워 코드를 모두 살펴보게 된다.
    • 경계를 올바로 처리하지 않는다.
      • 대부분의 개발자가 머릿속에서 코드를 돌려보고 끝낸다.
      • 자신의 직관에 의존할 뿐 모든 경계와 구석진 곳에서 코드를 증명하려 애쓰지 않는다.
      • 모든 경계 조건을 찾아내고 모든 경계 조건을 테스트하는 테스트 케이스를 작성하라.
    • 중복
      • 이 책에서 나오는 가장 중요한 규칙 중 하나로 DRY(Don't Repeat Yourself) 원칙이라 부른다.
      • 익스트림 프로그래밍의 핵심 규칙 중 하나로 론 제프리스는 모든 테스트를 통과한다는 규칙 다음으로 중요하게 꼽았다.
      • 코드에서 중복을 발견할 때마다 추상화할 기회로 간주하라.
      • 중복된 코드를 하위 루틴이나 다른 클래스로 분리하라.
      • 가장 흔한 유형은 똑같은 코드가 여러 차례 나오는 중복이다.
        • 개발자가 마우스로 긁어다 여기저기로 복사한 듯 보이는 코드를 말한다.
        • 이런 중복은 간단한 함수로 교체한다.
      • 좀 더 미묘한 유형은 여러 모듈에서 일련의 switchif문으로 같은 조건을 확인하는 중복이다.
        • 다형성을 이용해서 대체하라.
      • 마지막 유형으로는 알고리즘은 유사하나 코드가 서로 다른 중복이다.
        • Template Method 혹은 Strategy 패턴으로 중복을 제거한다.
      • 최근 15년 동안 나온 디자인 패턴은 대다수가 중복을 제거하는 패턴이다.
    • 추상화 수준이 올바르지 못한다.
      • 추상화는 저차원 상세 개념에서 고차원 일반 개념을 분리한다.
      • 모든 저차원 개념은 파생 클래스에 넣고 모든 고차원 개념은 기초 클래스에 넣는다.
      • 세부 구현과 관련된 상수, 변수, 유틸리티 함수는 기초 클래스에 넣으면 안 된다.
        • 기초 클래스는 구현 정보에 대해 알 수 없어야 한다.
      • 고차원 개념과 저차원 개념을 섞는 경우를 피하라.
        • 예시
          public interface Stack {
            Object pop() throws EmptyException;
            void push(Object o) throws FullException;
            double percentFull();
            class EmptyException extends Exception {}
            class FullException extends Exception {}
          }
          
          percentFull 함수는 추상화 수준이 올바르지 못하다. Stack을 구현하는 방법은 다양하기에 BoundedStack과 같은 파생 인터페이스에 넣어야 마땅하다.
      • 잘못된 추상화는 임시변통으로 고치기는 불가능하다.
    • 기초 클래스가 파생 클래스에 의존한다.
      • 기초 클래스가 파생 클래스를 사용한다면 문제가 있다는 말이다.
      • 일반적으로 기초 클래스는 파생 클래스를 아예 몰라야 마땅하다.
      • 간혹 파생 클래스 개수가 확실히 고정되었다면 기초 클래스에 파생 클래스를 선택하는 코드가 들어간다.
        • FSM(Finite State Machine) 구현에서 많이 볼 수 있는 사례다.
        • 그러나 FSM은 기초 클래스와 파생 클래스가 굉장히 밀접하고 언제나 같은 JAR 파일로 배포한다.
    • 과도한 정보
      • 잘 정의된 모듈은 인터페이스가 아주 작다.
      • 잘 정의된 인터페이스는 많은 함수를 제공하지 않아 결합도가 낮다.
      • 우수한 개발자라면 클래스나 모듈 인터페이스에 노출할 함수를 제한할 줄 알아야 한다.
      • 클래스가 제공하는 메서드 수는 작을수록 좋다.
      • 함수가 아는 변수, 클래스의 인스턴스 변수 수도 작을수록 좋다.
      • 자료, 유틸리티 함수, 상수와 임시 변수를 숨겨라.
      • 하위 클래스에서 필요하다는 이유로 protected 변수, 함수를 마구 생성하지 마라.
      • 인터페이스를 매우 작고 깐깐하게 만들어서 결합도를 낮춰라.
    • 일관성 부족
      • 어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현한다.
      • 예를 들어 한 함수에서 response라는 변수에 HttpServletResponse를 저장했다면 다른 함수에서도 일관성 있게 동일한 변수 이름 혹은 유사한 이름을 사용하라.
    • 기능 욕심
      • 클래스 메서드는 자기 클래스의 변수와 메서드에 관심을 가져야지 다른 클래스의 변수, 함수를 참조해서는 안 된다.
      • 메서드가 다른 객체의 참조자와 변경자를 사용해 그 객체를 조작하는 것을 말한다.
    • 서술적 변수
      • 프로그램 가독성을 높이는 가장 효과적인 방법 중 하나가 서술적인 변수 이름을 사용하는 방법이다.
      • 서술적인 변수 이름은 많을수록 더 좋다.
    • If/Else 혹은 Switch/Case 문보다 다형성을 사용하라
      • 대다수 개발자가 switch문을 사용하는 이유는 손쉬운 선택이기 때문이다.
        • switch문을 사용하기 전에 다형성을 먼저 고려하라.
      • 유형보다 함수가 더 쉽게 변하는 경우는 극히 드물다.
        • 선택 유형 하나에는 switch문을 한 번만 사용하고 같은 선택을 수행하는 다른 코드에는 다형성 객체를 사용하라.
    • 조건을 캡슐화하라
      • bool 논리는 이해하기 어렵다.
      • 조건의 의도를 분명히 밝히는 함수로 표현하라
      • 예시
        if (shouldBeDeleted(timer))
        
        라는 코드는 다음 코드보다 좋다.
        if (timer.hasExpired() && !timer.isRecurrent())
        
    • 부정 조건은 피하라
      • 부정 조건은 긍정 조건보다 이해하기 어렵다.
      • 가능하면 긍정 조건으로 표현한다.
    • 함수는 한 가지만 해야 한다.
      • 한 가지만 수행하는 좀 더 작은 함수 여럿으로 나누는 것이 마땅하다.
      • 예시
        public void pay() {
          for (Employee e : employees) {
            if (e.isPayday()) {
              Money pay = e.calculatePay();
              e.deliveryPay(pay);
            }
          }
        }
        
        위의 함수는 다음과 같이 함수 셋으로 나누는 편이 좋다.
        public void pay() {
          for (Employee e : employees) {
            payIfNeessary(e);
          }
        }
        
        public void payIfNecessary(Employee e) {
          if (e.isPayday()) {
            cacluateAndDeliverPay(e);
          }
        }
        
        private void calculateAndDeliveryPay(Employee e) {
          Money pay = e.calculatePay();
          e.deliverPay(pay);
        }
        
        위에서 각 함수는 한 가지 일만 수행한다.
    • 숨겨진 시간적인 결합
      • 때로는 시간적인 결합이 필요하지만 이를 숨겨서는 안 된다.
      • 함수를 작성할 때 함수 인수를 적절히 배치해 함수가 호출되는 순서를 노출시켜라.
      • 예시
        public class MoogDriver {
          public void dive(String reason) {
            Gradient gradient = staturateGradient();
            List<Spline> splines = reticulateSplines(gradient);
            diveForMoog(splines, reason);
          }
        }
        
    • 추이적 탐색을 피하라
      • 일반적으로 한 모듈은 주변 모듈을 모를수록 좋다.
      • A가 B를 사용하고 B가 C를 사용하더라도 A가 C를 알 필요는 없다는 뜻이다.
      • 이를 디미터의 법칙이라 부르는데 가장 중요한 것은 자신이 직접 사용하는 모듈만 알아야 한다는 뜻이다.

  1. 자바
    • 긴 import 목록을 피하고 와일드카드를 사용하라
      • 패키지에서 클래스를 둘 이상 사용한다면 와일드카드(*)를 사용해 패키지 전체를 가져온다.
        • 예시
          import package.*;
          
      • 긴 import 목록은 읽기에 부담스럽다.
      • 사용하는 패키지를 간단히 명시하면 충분하다.
    • 상수 대 Enum
      • 자바 5부터 enum을 지원하기에 public static final int라는 옛날 관습을 더 이상 사용할 필요가 없다.
      • 메서드와 필드도 사용할 수 있기에 유연하고 서술적인 강력한 도구다.
      • 좋은 예시
        public enum HourlyPayGrade {
          APPRENTICE {
            public double rate() {
              return 1.0;
            }
          },
          LIEUTENANT_JOURNEYMAN {
            public double rate() {
              return 1.2;
            }
          },
          JOURNEYMAN {
            public double rate() {
              return 1.5;
            }
          },
          MASTER {
            public double rate() {
              return 2.0;
            }
          };
        
          public abstract double rate();
        }
        

  1. 테스트
  • 불충분한 테스트

2. 🤔 이해가 어려운 부분

🔍 질문하기

책을 읽으며 이해하기 어려웠던 개념이나 명확하지 않았던 내용을 정리합니다.

  1. 개념 또는 원칙의 이름
    • 어려웠던 부분
      해당 개념이 헷갈리거나 명확하지 않았던 점을 구체적으로 설명합니다.
    • 궁금한 점
      해당 개념이 어떤 원리로 동작하는지, 실무에서 어떻게 활용되는지 등을 질문 형태로 정리합니다.

  1. 개념 또는 원칙의 이름
    • 어려웠던 부분
      .
    • 궁금한 점
      .

  1. 개념 또는 원칙의 이름
    • 어려웠던 부분
      .
    • 궁금한 점
      .

3. 📚 참고 사항

📢 논의하기

관련된 자료가 있다면 공유하고, 더 깊이 논의하고 싶은 아이디어나 의견을 정리합니다.

  1. 관련 자료 공유
    • 추가 자료
      관련 블로그 글이나 공식 문서 링크를 제공합니다.

  1. 논의하고 싶은 주제
    • 주제
      논의하고 싶은 내용을 간략히 정리합니다.
    • 설명
      논의하고 싶은 이유를 작성합니다.