진소희
1. 📌 핵심 개념 정리
✅ 요약하기
- 주석
- c1: 부적절한 정보
- 다른 시스템에 (예를 들어, 소스 코드 관리 시스템, 버그 추적 시스템, 이슈 추적 시스템, 기타 기록 관리 시스템에) 저장할 정보는 주석으로 적절하지 못하다.
- 일반적으로 작성자, 최종 수정일, SPR번호 등과 같은 메타 정보만 주석으로 넣는다.
- 주석은 코드와 설계에 기술적인 설명을 부연하는 수단이다.
- c2: 쓸모 없는 주석
- 쓸모 없어질 주석은 아예 달지 않는 편이 가장 좋다.
- 코드와 무관하게 혼자서 따로 놀며 코드를 그릇된 방향으로 이끈다.
- c3: 중복된 주석
- 코드만으로 충분한데 구구절절 설명하는 주석이 중복된 주석이다.
- c4: 성의 없는 주석
- 단어를 신중하게 선택한다. 문법과 구두점을 올바로 사용한다. 주절대지 않는다. 당연한 소리를 반복하지 않는다. 간결하고 명료하게 작성한다.
- c5: 주석 처리된 코드
- 주석으로 처리된 코드를 발견하면 즉각 지워버려라. 소스 코드 관리 시스템이 기억하니까 걱정할 필요 없다.
- c1: 부적절한 정보
- 환경
- E1: 여러 단계로 빌드해야 한다
- 빌드는 간단히 한 단계로 끝나야 한다.
- 소스 코드 관리 시스템에서 이것저것 따로 체크아웃할 필요가 없어야 한다.
- 불가해한 명령이나 스크립트를 잇달아 실행해 각 요소를 따로 빌드할 필요가 없어야 한다.
- 한 명령으로 전체를 체크아웃해서 한 명령으로 빌드할 수 있어야 한다.
- E2: 여러 단계로 테스트해야 한다.
- 모든 단위 테스트는 한 명령으로 돌려야 한다. 모든 테스트를 한 번에 실행하는 능력은 아주 근본적이고 아주 중요하다. 따라서 그 방법이 빠르고, 쉽고, 명백해야 한다.
- E1: 여러 단계로 빌드해야 한다
- 함수
- F1: 너무 많은 인수
- 함수에서 인수 개수는 작을수록 좋다. 아예 없으면 가장 좋다.
- F2: 출력 인수
- 출력 인수는 직관을 정면으로 위배한다.
- 함수에서 뭔가의 상태를 변경해야 한다면(출력 인수를 쓰지 말고) 함수가 속한 객체의 상태를 변경한다.
- F3: 플래그 인수
- 플래그 인수는 혼란을 초래하므로 피해야 마땅하다.
- F4: 죽은 함수
- 아무도 호출하지 않는 함수는 삭제한다. 소스 코드 관리 시스템이 기억하니까 걱정할 필요 없다.
- F1: 너무 많은 인수
- 일반
- G1: 한 소스 파일에 여러 언어를 사용한다.
- 이상적으로는 소스파일 하나에 언어 하나만 사용하는 방식이 가장 좋다.
- 각별한 노력을 기울여 소스 파일에서 언어 수와 범위를 최대한 줄이도록 애써야 한다.
- G2: 당연한 동작을 구현하지 않는다.
- 최소 놀람의 원칙에 의거해 함수나 클래스는 다른 프로그래머가 당연하게 여길 만한 동작과 기능을 제공해야 한다.
- 당연한 동작을 구현하지 않으면 코드를 읽거나 사용하는 사람이 더이상 함수 이름만으로 함수 기능을 직관적으로 예상하기 어렵다.
- G3: 경계를 올바로 처리하지 않는다.
- 스스로의 직관에 의존하지 마라. 모든 경계조건을 찾아내고, 모든 경계 조건을 테스트하는 테스트 케이스를 작성하라.
- G4: 안전 절차 무시
- 실패하는 테스트 케이스를 일단 제껴두고 나중으로 미루는 태도는 신용카드가 공짜 돈이라는 생각만큼 위험하다.
- G5: 중복
- 코드에서 중복을 발견할 때마다 추상화할 기회로 간주하라. 중복된 코드를 하위 루틴이나 다른 클래스로 분리하라. 이렇듯 추상화로 중복을 정리하면 설계의 어휘가 늘어난다.
- G6: 추상화 수준이 올바르지 못하다.
- 추상화는 저차원 상세 개념에서 고차원 일반 개념을 분리한다.
- 모든 저차원 개념은 파생 클래스에 넣고, 모든 고차원 개념은 기초 클래스에 넣는다
- G7: 기초 클래스가 파생 클래스에 의존한다
- 개념을 기초 클래스와 파생 클래스로 나누는 가장 흔한 이유는 고차원 기초 클래스 개념을 저차원 파생 클래스 개념으로부터 분리해 독립성을 보장하기 위해서다.
- 기초 클래스는 파생 클래스를 아예 몰라야 마땅하다.
- G8: 과도한 정보
- 잘 정의된 모듈은 인터페이스가 아주 작다. 부실하게 정의된 모듈은 인터페이스가 구질구질하다.
- 잘 정의된 인터페이스는 많은 함수를 제공하지 않는다. 그래서 결합도가 낮다.
- 자료를 숨겨라. 유틸리티 함수를 숨겨라. 상수와 임시 변수를 숨겨라. 메서드나 인스턴스 변수가 넘쳐나는 클래스는 피하라.
- 인스턴스 를 매우 작게 그리고 매우 깐깐하게 만들어라.
- G9: 죽은 코드
- 죽은 코드란 실행되지 않는 코드를 가리킨다. 죽은 코드를 발견하면 시스템에서 제거하라.
- G10: 수직 분리
- 변수와 함수는 사용되는 위치에 가깝게 정의한다. 지역 변수는 처음으로 사용하기 직전에 선언하며 수직으로 가까운 곳에 위치해야 한다.
- 비공개 함수는 처음으로 호출한 직후에 정의한다. 비공개 함수는 전체 클래스 범위에 속하지만 그래도 정의하는 위치와 호출되는 위치를 가깝게 유지한다.
- G11: 일관성 부족
- 어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현한다.
- G12: 잡동사니
- 아무도 사용하지 않는 변수, 아무도 호출하지 않는 함수, 정보를 제공하지 못하는 주석 등이 좋은 예다. 모두가 코드만 복잡하게 만들 뿐이므로 제거해야 마땅하다.
- G13: 인위적 결합
- 서로 무관한 개념을 인위적으로 결합하지 않는다.
- 일반적으로 인위적인 결합은 직접적인 상호작용이 없는 두 모듈 사이에서 일어난다. 뚜렷한 목적 없이 변수, 상수, 함수를 당장 편한 위치에 넣어버린 결과다.
- G14: 기능 욕심
- 클래스 메서드는 자기 클래스의 변수와 함수에 관심을 가져야지 다른 클래스의 변수와 함수에 관심을 가져서는 안된다.
- 기능 욕심은 한 클래스의 속사정을 다른 클래스에 노출하므로, 별다른 문제가 없다면, 제거하는 편이 좋다.
- G15: 선택자 인수
- 선택자 인수는 목적을 기억하기 어려울 뿐 아니라 각 선택자 인수가 여러 함수를 하나로 조합한다.
- 일반적으로, 인수를 넘겨 동작을 선택하는 대신 새로운 함수를 만드는 편이 좋다.
- G16: 모호한 의도
- 코드를 짤 때는 의도를 최대한 분명히 밝힌다.
- G17: 잘못 지운 책임
- 함수 이름을 살펴보고, 이름을 제대로 지어야 한다.
- G18: 부적절한 static 함수
- 가끔 static으로 정의하면 안 되는 함수를 static으로 정의한다.
HourlyPayCalculator.calculatePay(employee, overtimeRate);
- 함수를 재정의할 가능성이 존재한다. 수당을 계산하는 알고리즘이 여러 개일지도 모른다.
- 일반적으로 static함수보다 인스턴스 함수가 더 좋다.
- static함수로 정의해야겠다면 재정의할 가능성은 없는지 꼼꼼히 따져본다
- G19: 서술적 변수
- 계산을 여러 단계로 나누고 중간 값으로 서술적인 변수 이름을 사용하는 방법이 있다.
- 서술적인 변수 이름은 많이 써도 괜찮다. 일반적으로는 많을수록 더 좋다.
- G20: 이름과 기능이 일치하는 함수
- 이름만으로 분명하지 않기에 구현을 살피거나 문서를 뒤적여야 한다면 더 좋은 이름으로 바꾸거나 아니면 더 좋은 이름을 붙이기 쉽도록 기능을 정리해야 한다.
- G21: 알고리즘을 이해하라
- 구현이 끝났다고 선언하기 전에 함수가 돌아가는 방식을 확실히 이해햐는지 확인하라. 테스트 케이스를 모두 통과한다는 사실만으로 부족하다. 작성자가 알고리즘이 올바르다는 사실을 알아야 한다.
- 알고리즘이 올바르다는 사실을 확인하고 이해하려면 기능이 뻔히 보일 정도로 함수를 깔끔하고 명확하게 재구성하는 방법이 최고다.
- G22: 논리적 의존성은 물리적으로 드러내라
- 한 모듈이 다른 모듈에 의존한다면 물리적인 의존성도 있어야 한다. 논리적인 의존성만으로는 부족하다.
- 의존하는 모든 정보를 명시적으로 요청하는 편이 좋다.
- G23: If/Else 혹은 Switch/Case 문보다 다형성을 사용하라
- 유형보다 함수가 더 쉽게 변하는 경우는 극히 드물다. 그러므로 모든 switch문을 의심해야 한다.
- 이 책의 저자가 따르는 규칙은, 선택 유형 하나에는 switch문을 한번만 사용하고 같은 선택을 수행하는 다른 코드에서는 다형성 객체를 생성해 switch문을 대신한다는 것이다.
- G24: 표준 표기법을 따르라
- 팀은 업계 표준에 기반한 구현 표준을 따라야 한다.
- 구현 표준은 인스턴스 변수 이름을 선언하는 위치, 클래스/메서드/변수 이름을 정하는 방법, 괄호를 넣는 위치 등을 명시해야 한다.
- 표준을 설명하는 문서는 코드 자체로 충분해야 하며 별도 문서를 만들 필요는 없어야 한다.
- G25: 매직 숫자는 명명된 상수로 교체하라
- 일반적으로 코드에서 숫자를 사용하지 말라는 규칙이다. 숫자는 명명된 상수 뒤로 숨기라는 의미다.
- G26: 정확하라
- 코드에서 뭔가를 결정할 때는 정확히 결정한다. 결정을 내리는 이유와 예외를 처리할 방법을 분명히 알아야 한다. 대충 결정해서는 안된다.
- 코드에서 모호성과 부정확은 의견차나 게으름의 결과다. 어느쪽이든 제거해야 마땅하다.
- G27: 관례보다 구조를 사용하라
- 설계 결정을 강제할 때는 규칙보다 관례를 사용한다. 명명 관례도 좋지만 구조 자체로 강제하면 더 좋다.
- G28: 조건을 캡슐화하라
- 조건의 의도를 분명히 밝히는 함수로 표현해라
- G29: 부정 조건은 피하라
- 부정 조건은 긍정 조건보다 이해하기 어렵다. 가능하면 긍정 조건으로 표현한다.
- G30: 함수는 한 가지만 해야 한다.
- G31: 숨겨진 시간적인 결합
- 때로는 시간적인 결합이 필요하다. 하지만 시간적인 결합을 숨겨서는 안 된다.
- 함수를 짤 때는 함수 인수를 적절히 배치해 함수가 호출되는 순서를 명백히 드러낸다.
- G32: 일관성을 유지해라
- 코드 구조를 잡을 때는 이유를 고민하라. 그리고 그 이유를 코드 구조로 명백히 표현하라.
- G33: 경계 조건을 캡슐화하라
- 경계 조건은 빼먹거나 놓치기 십상이다. 경게 조건은 한 곳에서 별도로 처리한다. 코드 여기저기에서 처리하지 않는다.
- G34: 함수는 추상화 수준을 한 단계만 내려가야 한다.
- 함수 내 모든 문장은 추상화 수준이 동일해야 한다. 그리고 그 추상화 수준은 함수 이름이 의미하는 작업보다 한 단계만 낮아야 한다.
- G35: 설정 정보는 최상위 단계에 둬라
- 추상화 최상위 단계에 둬야 할 기본값 상수나 설정 관련 상수를 저차원 함수에 숨겨서는 안된다.
- 대신 고차원 함수에서 저차원 함수를 호출할 때 인수로 넘긴다.
- G36: 추이적 탐색을 피하라
- 일반적으로 한 모듈은 주변 모듈을 모를수록 좋다. 이를 디미터의 법칙이라 부른다.
- 내가 아는 모듈이 연이어 자신이 아는 모듈을 따라가며 시스템 전체를 휘저을 필요가 없다는 의미다.
- G1: 한 소스 파일에 여러 언어를 사용한다.
- 자바
- J1: 긴 import 목록을 피하고 와일드카드를 사용하라
- 패키지에서 클래스를 둘 이상 사용한다면 와일드카드를 사용해 패키지 전체를 가져오라.
- J2: 함수는 상속하지 않는다
- J3: 상수 대 Enum
- public static final이라는 옛날 기교를 더이상 사용할 필요가 없다. enum을 사용하라
- J1: 긴 import 목록을 피하고 와일드카드를 사용하라
- 이름
- N1: 서술적인 이름을 사용하라
- 이름은 성급하게 정하지 않는다. 서술적인 이름을 신중하게 고른다.
- N2: 적절한 추상화 수준에서 이름을 선택하라
- 구현을 드러내는 이름은 피하라. 작업 대상 클래스나 함수가 위치하는 추상화 수준을 반영하는 이름을 선택하라.
- N3: 가능하다면 표준 명명법을 사용하라
- 기존 명명법을 사용하는 이름은 이해하기 더 쉽다.
- 패턴은 한 가지 표준에 불과하다. toString이라는 함수는 자바에서 객체를 문자열로 변환하는 메서드인데 이런 이름은 관례를 따르는 편이 좋다.
- N4: 명확한 이름
- 함수나 변수의 목적을 명확히 밝히는 이름을 선택한다.
- N5: 긴 범위는 긴 이름을 사용하라
- 이름 길이는 범위 길이에 비례해야 한다. 범위가 5줄 안팎이라면 i나 j와 같은 변수 이름도 괜찮다.
- N6: 인코딩을 피하라
- 이름에 유형 정보나 범위 정보를 넣어서는 안된다. 오늘날 개발 환경에서는 이름 앞에 m_이나 f와 같은 접두어가 불필요하다.
- 오늘날 환경은 이름을 조작하지 않고도 모든 정보를 제공한다. 헝가리안 표기법의 오염에서 이름을 보호하라
- N7: 이름으로 부수 효과를 설명하라
- 함수, 변수, 클래스가 하는 일을 모두 기술하는 이름을 사용한다. 이름에 부수 효과를 숨기지 않는다.
- N1: 서술적인 이름을 사용하라
- 테스트
- T1: 불충분한 테스트
- 테스트 케이스는 잠재적으로 깨질만한 부분으 모두 테스트해야 한다.
- 테스트 케이스가 확인하지 않는 조건이나 검증하지 않은 계산이 있다면 그 테스트는 불완전하다.
- T2: 커버리지 도구를 사용하라!
- 커버리지 도구는 테스트가 빠뜨리는 공백을 알려준다. 커버리지 도구를 사용하면 테스트가 불충분한 모듈, 클래스, 함수를 찾기가 쉬워진다.
- T3: 사소한 테스트를 건너뛰지 마라
- 사소한 테스트는 짜기 쉽다. 사소한 테스트가 제공하는 문서적 가치는 구현에 드는 비용을 넘어선다.
- T4: 무시한 테스트는 모호함을 뜻한다
- 불분명한 요구사항은 테스트 케이스를 주석으로 처리하거나 테스트 케이스에 @Ignore를 붙여 표현한다. 선택 기준은 모호함이 존재하는 테스트 케이스가 컴파일이 가능한지 불가능한지에 달려있다.
- T5: 경계 조건을 테스트하라
- 경계 조건은 각별히 신경 써서 테스트한다. 알고리즘의 중앙 조건은 올바로 짜놓고 경계 조건에서 실수하는 경우가 흔하다.
- T6: 버그 주변은 철저히 테스트하라
- 버그는 서로 모이는 경향이 있다. 한 함수에서 버그를 발견했다면 그 함수를 철저히 테스트하는 편이 좋다.
- T7: 실패 패턴을 살펴라
- 때로는 테스트 케이스가 실패하는 패턴으로 문제를 진단할 수 있다. 함리적인 순서로 정렬된 꼼꼼한 테스트 케이스는 실패 패턴을 드러낸다.
- T8: 테스트 커버리지 패턴을 살펴라
- 통과하는 테스트가 실행하거나 실행하지 않는 코드를 살펴보면 실패하는 테스트 케이스의 실패 원인이 드러난다.
- T9: 테스트는 빨라야 한다
- 느린 테스트 케이스는 실행하지 않게 된다.
- T1: 불충분한 테스트
2. 🤔 이해가 어려운 부분
🔍 질문하기
책을 읽으며 이해하기 어려웠던 개념이나 명확하지 않았던 내용을 정리합니다.
- 개념 또는 원칙의 이름
- 어려웠던 부분
해당 개념이 헷갈리거나 명확하지 않았던 점을 구체적으로 설명합니다. - 궁금한 점
해당 개념이 어떤 원리로 동작하는지, 실무에서 어떻게 활용되는지 등을 질문 형태로 정리합니다.
- 어려웠던 부분
3. 📚 참고 사항
📢 논의하기
관련된 자료가 있다면 공유하고, 더 깊이 논의하고 싶은 아이디어나 의견을 정리합니다.
- 관련 자료 공유
- 추가 자료
BNCF FSM
- 추가 자료
- 논의하고 싶은 주제
- 주제
논의하고 싶은 내용을 간략히 정리합니다. - 설명
논의하고 싶은 이유를 작성합니다.
- 주제
No Comments