[바미] 클린 코드를 읽고..
들어가기전에..
읽은 지 꽤 된 책이지만 내용이 너무 좋아 다시 리마인드 할 겸 의미 있었던 내용들을 적어두려고 합니다.
그렇기 때문에 에서는 책의 모든 내용을 다루지 않습니다.
깨끗한 코드
이 책의 1장에서는 '깨끗한 코드'에 대해 각자 생각했던 내용을 다뤘다. 책을 읽으며 공감이 됐던 부분을 종합해서 내가 생각하는 깨끗한 코드는 아래와 같다.
- 제 3자가 읽기 쉽고 고치기 쉬운 코드
- 한 가지에 집중하는 코드
- 테스트 케이스가 있는 코드
특히 여기에서 가장 중요하게 생각하는 부분은 제 3자가 읽기 쉽고 고치기 쉬운 코드라고 생각하는데, 코드라는 게 결국 한 사람에
귀속된다기보다는 다른 사람이 코드를 이어받아 유지 보수하는 경우가 더 많기 때문에 제 3자가 읽기 쉽고 고치기 쉬운 코드가 가장 중요하다고 생각된다.
그런데 성능이 중요한 경우가 아닌 경우에는 어떨까?
의미 있는 이름
의미 있는 이름을 짓는 것은 사람이 읽기 쉽게 코드를 작성하는 것과 직결되는 문제이기 때문에 더 신경 써야 하는 부분인데, 네이밍은 항상 너무 어려운 일인 것 같다.
- 의도를 분명히 밝히기
- 이름이 존재 이유, 수행하는 기능, 사용 방법을 대변해야한다.
- 네이밍을 설명하는 주석이 필요하면 안 된다.
- 그릇된 정보 피하기
- 널리 쓰이는 의미가 있는 단어를 다른 이유로 사용하면 안 된다.
- 흡사한 이름을 사용하면 안 된다.
- 의미 있게 구분하기
- 이름이 다르면 의도도 달라야 한다.
- 불용어(info, data 같이 애매한 의미의 단어)는 피해야 한다.
- 검색하기 쉬운 단어를 사용하기
- 긴 단어 > 짧은 단어
- 검색하기 쉬운 이름 > 상수
- 이름 길이는 사용되는 범위에 비례한다.
- 클래스 이름은 명사
- 메서드 이름은 동사
- 표준에 따라 get, set, is를 사용한다.
- 생성자를 중복 정의할 때에는 정적 팩토리 메서드를 사용하는 것이 좋다.
함수
읽으면서 개인적으로 해당 장에서 말하고자 하는 내용이 '함수는 하나의 일만 해야 한다'인 것 같다고 느꼈다.
내가 코드를 작성할 때 늘 신경 쓰면서 작성하고자 하는 부분도 이런 부분이다. 쪼갤 수 있을 때까지 쪼개면 과유불급 같고,
그렇다고 내버려두자니 하나의 일만 하는 게 아닌 것 같은..? 이런 부분이 참 애매한 것 같다.
- 작게 만들기
- 함수 안의 줄 수는 적을수록 좋다.
- 들여 쓰기 레벨도 최대 2단까지만 있는 것이 가장 바람직하다.
- 한 가지만 하기
- 한 가지 일을 하는 함수들과 그 함수들을 호출하는 함수 하나로 이루어져 있어야 한다.
- 의미 있는 이름으로 함수를 추출할 수 있다면 한 가지의 일만 하고 있는 것이다.
- 함수 당 추상화 수준은 동일하게 하기
- 코드는 위에서 아래로 읽혀야 한다.
- 함수 인수 최대한 적게 쓰기
- 가장 좋은 경우는 인수가 없는 것, 다음에는 단항 함수(인수가 1개)이다.
- 입력값이 있는 변환 함수는 반환 값이 있는 것이 좋다.
- 플래그 인수(ex. isTrue)는 사용하지 않는 것이 좋다.
- 인수가 많이 필요하다면 독자적인 클래스 생성을 고려해보는 것도 좋은 방법이다.
- 부수 효과 일으키지 말기
- 함수는 정말 그 일만 해야 한다.
- 명령과 조회 분리하기
- 함수는 무언가를 수행하거나 답하거나 둘 중 하나만 해야 한다.
주석
해당 장을 한 줄로 요약하면 '주석은 가능한 적은 게 좋다. 주석이 필요 없는 코드를 만들어야 한다.'인 것 같다.
원래 주석을 꼼꼼하게 다는 습관이 있었는데 이 글을 읽고 주석을 달지 않으려고 했는데 상사에게 원래 중요한 코드이고, 많이 사용하는 코드일 수록 주석을 더욱 꼼꼼히 달아야 한다고 설명을 들은 적이 있다. 그러면서 유명한 오픈소스들은 주석이 더욱 더 자세하게 달려있다 말한다. 여러 사람들이 사용하기 때문에 그 사람에게 설명하기 위해서였다.
회사에서 작성하는 코드도 그러했다. 내 코드를 인수인계 받는 사람을 위해, 몇 달뒤의 나를 위해 주석은 꼼꼼히 달아야 한다 들었다.
책에서는 주석은 나쁜 코드를 보완하지 못한다, 복잡하고 어수선하며 주석이 많이 달린 코드를 만드는 것보단 표현력이 풍부하고 깔끔한 코드를 만드는데 집중해야 한다고 써 있지만 주석을 쓰지 않고 이해가 되는 코드를 만드는 건 쉽지 않을 것이라 생각한다.
좋은 주석
- 코드로 표현하지 못하는 정보를 제공하는 주석
- 의도를 설명하는 주석
- 결과를 경고하는 주석
- 중요성을 강조하는 주석
나쁜 주석
- 코드로 설명할 수 있는데, 코드를 설명하는 주석
- 주석으로 처리한 코드
- 이력을 기록하는 주석
객체와 자료구조
'자바는 객체지향 언어니까 무조건 객체지향적으로 코딩해야지!'라는 생각을 했는데, 이 책에서는 상황에 따라 절차 지향적인 방법을 선택할 수도 있다는 내용을 담고 있었다. 개인적으로 인상 깊어서 예제 코드까지 메모를 해두었다.
- 객체 지향
- 객체지향에서 어려운 변경 코드는 절차 지향 코드에서 쉽다.
- 새로운 자료 타입을 추가하는 유연성이 필요하면 객체가 더 적합하다.
- 절차 지향에서
- 절차 지향에서 어려운 변경 코드는 객체 지향에서 쉽다.
- 새로운 동작을 추가하는 유연성이 필요하면 자료 구조, 절차적인 코드가 더 적합하다.
// 이것보다
public class Square implements Shape {
public double area() {
//
}
}
public class Circle implements Shape {
public double area() {
//
}
}
// 이게 더 나을수도 있다
public class Geometry {
public double area(Object shape) {
if (shape instanceof Square) {
//
} else if (shape instanceof Circle) {
//
}
}
}
- 디미터 법칙을 지키는 게 좋다.
- 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다.
- 기차 충돌은 피하는 게 좋다.
- String outDir = ctxt.getOption().getScratch().getPath();
오류 처리
여기에서 서술하는 내용들은 주워들은 내용들이 나와 다행히(?) 아는 내용들이었다. 그중 외부 라이브러리의 예외처리를 해주는 라이브러리용 클래스를 만드는 것은 꽤 신선하게 다가왔던 것 같다.
- 오류 코드보다 예외를 사용하라
- 미확인 예외를 사용하라
- 확인된 예외는 상위 선언부에서 모두 선언해주어야 하기 때문이다 → OCP 위반
- 예외에 의미를 제공하라
- 호출자를 고려해 예외 클래스를 정의하라
- 외부 라이브러리를 사용할 때에는 라이브러리용 클래스를 고려해보는 것이 좋다.
- null을 반환하지 마라
단위 테스트
테스트는 어떨 보면 실제 코드를 짜는 것보다 더 어려운 것 같다.
테스트 코드만 봐도 실제 코드가 이해 갈 수 있게 작성하라고 하는데, 정말 쉽지 않다.
그리고 여러 상황을 대비하기 위해 여러 상황을 많이 테스트 해보라 말하지만 그 버그를 만드는 것 조차 어려울 때가 많았다.
- TDD로 개발해보자
- 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
- 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다,
- 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
- 테스트는 유연성, 유지 보수성, 재사용성을 제공한다.
- 테스트는 실제 코드보다 더 가독성이 중요하다.
- 테스트 당 개념 하나
- 테스트의 assert를 최소로 하라
- 테스트 함수 하나는 개념 하나만 테스트하라
- F.I.R.S.T
- Fast : 테스트는 빨리 돌아가야 한다.
- Independent : 테스트는 서로 의존하면 안 된다. 한 테스트가 다음 테스트의 실행을 준비해서도 안 된다.
- Repeatable : 테스트는 어떤 환경에서도 반복 가능해야 한다. (네트워크가 없어도 돌아가야 한다.)
- Self-Validating : 결괏값은 boolean 이여야 한다. (성공/실패)
- Timely : 실제 코드 구현 전에 작성해야 한다.
동시성
동시성은 정말 중요하다는 것을 알고 있지만, 정~~~~말 어렵다 … 정말 정말 너무 어렵다. 사실 이 장을 읽으면서도 동시성에 대한 이해가 깊지 않기 때문에 이해가 잘 가지 않는 부분도 있었다. 시간이 될 때 동시성에 대해 깊게 공부해봐야겠다는 생각이 들었다.
동시성을 사용하면 무엇(what)과 언제(when)를 분리할 수 있다.
미신과 오해
- 동시성은 항상 성능을 높여준다.
- 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에나 성능이 높아진다.
- 동시성을 구현해도 설계는 변하지 않는다.
- 무엇과 언제를 분리하면 구조가 크게 달라진다.
타당한 생각들
- 동시성은 다소 부하를 유발한다.
- 동시성은 복잡하다.
- 일반적으로 동시성 버그는 재현하기 어렵다.
- 동시성을 구현하기 위해서는 근본적인 설계 전략을 재고해야 한다.
동시성 방어 원칙
- 공유 객체 하나에는 메서드 하나만 사용하는 것이 좋다.
- 다중 스레드 코드는 버그를 찾기 어렵기 때문에 각별히 깨끗하게 코드를 짜야한다.
- SRP를 준수하는 게 좋다.
- POJO를 사용해 스레드를 아는 코드, 모르는 코드를 분리하는 게 좋다.
- 스레드 코드를 테스트할 때에는 전적으로 스레드만 테스트한다.
- 동시성 오류를 일으키는 잠정적 원인을 철저히 이해한다.
- 사용하는 라이브러리와 기본 알고리즘을 이해한다.
- 보호할 코드 영역을 찾아내는 방법과 특정 코드 영역을 잠그는 방법을 이해한다.
- 어떻게든 문제가 생기기 때문에 테스트는 정말 중요하다.