🚀 들어가면서
지금껏 개발하면서 함수(메서드)를 어떻게 만들어야할지 많은 고민을 했었다. SRP(단일책임원칙)을 지켜야하므로 함수는 한 가지 책임만 가지게 만들어 유지보수성을 높여야한다고 하지만 생각과는 다르게 개발이 되었던 경우가 많았다. 예전에 읽었던 책을 빠르게 읽어보면서 다시끔 클린코드를 생각해보자.
어떤 프로그램이든 가장 기본적인 단위가 함수이다. 아래 질문을 가지고 이번 장을 살펴보자.
- 함수가 읽기 쉽고 이해하기 쉬운 이유는 무엇일까?
- 의도를 분명히 표현하는 함수를 어떻게 구현할 수 있을까?
- 함수에 어떤 속성을 부여해야 처음 읽는 사람이 프로그램 내부를 직관적으로 파악할 수 있을까?
✏️ #1, 작게 만들어라!
- 함수를 더욱 작게 만들수록 좋다
- 각 함수가 이야기 하나를 표현하여 명확하게 파악할 수 있다
함수가 작을수록 더 좋다는 증거는 없지만, 100줄 넘는 함수보다 10줄의 함수가 더욱 파악하기 쉽다
함수를 작게 만들라는 규칙이 가장 중요하다고 생각된다. 작게 만들기위해서는 각 함수가 SRP를 지키며 명확하게 작성해야하기 때문이다. 그래서 이야기를 풀듯이 개발할 수 있다.
📝 #1-1, 블록과 들여쓰기
- if문, else문, while문 등에 들어가는 블록은 한 줄이어야 한다
- 블록 안에서 호출하는 함수 이름을 적절히 짓는다면, 코드를 이해하기 쉬움
- 중첩 구조가 생길만큼 함수가 커져서는 안된다
- 들여쓰기 수준은 1~2단을 넘어서면 안된다
위 규칙을 지킨다면 함수는 읽고 이해하기 쉬워진다.
들여쓰기는 가독성에서도 중요한 부분이다. if문 3중 중첩을 본적이 있는가? 정말 힘들다. 처음 개발할 당시에도 이런식으로 작성했지만 추후 유지보수 때 파악하기 힘들어 주석을 남겼었는데 지금 생각해보면 주석을 써야할만큼 복잡했다는 의미가 된다. 우아한 테크코스에서도 들여쓰기는 2단을 넘기지 말아야 한다고 했다.
✏️ #2, 한 가지만 해라!
- 함수는 한 가지를 해야 한다
- 그 한 가지를 잘 해야 한다
- 그 한 가지만을 해야 한다
함수 한 개에 여러 작업이 있다면, 함수명을 적절히 지으면서도 함수를 작게 만들어야한다. 벌써부터 힘들지 않은가? 차라리 각 작업을 함수로 추출해서 사용하는 것이 함수명을 적절히 지으면서도 함수를 작게 만들 수 있는 방법이다.
✏️ #3, 함수 당 추상화 수준은 하나로!
- 함수가 '한 가지' 작업만 하려면 함수 내 모든 문장이 추상화 수준이 동일해야 한다
- 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다
- 특정 표현이 근본 개념인지 아니면 세부사항인지 구분하기 어렵기 때문이다
`getHtml()` 은 추상화가 가장 높고, `PathParser.render(pagepath);`는 중간이고, `.append("\n")`는 가장 낮음
📝 #3-1, 위에서 아래로 코드 읽기
- 코드는 위에서 아래로 이야기처럼 읽혀야 좋음
- 핵심은 짧으면서도 '한 가지'만하는 함수
- 위에서 아래로 읽어내려 가듯이 코드를 구현하면 추상화 수준을 일관되게 유지하기 쉬워짐
✏️ #4, 서술적인 이름을 사용하라!
- 함수가 작고 단순할수록 서술적인 이름을 고르기 쉬워짐
- "코드를 읽으면서 짐작했던 기능을 그대로 수행한다면 깨끗한 코드"
- 길고 서술적인 이름이 짧고 어려운 이름보다 좋음
- 길고 서술적인 이름이 길고 서술적인 주석보다 좋음
- 이름을 붙일 때는 일관성이 있어야 함
✏️ #5, 함수 인수
- 함수에서 이상적인 인수 개수는 0개이며, 다음은 1개(단항), 다음은 2개(이항)이다
- 3개(삼항) 이상은 가능한 피하자
- 인수가 적을수록 이해하기 더 쉽다
- 입력 인수를 변환하는 함수라면 변환 결과는 반환값으로 돌려주자
- 함수에 플래그 인수(boolean)을 넘기는 것은 좋지 않음
- 이항 함수는 위험이 따른다는 것을 이해하고 가능하면 단항 함수로 변경하는 것이 좋음
- 삼항 함수는 이항 함수보다 순서, 무시 등 문제가 2배 늘어나는 것을 생각하자
- 인수가 2~3개 이상 필요하다면 독자적인 클래스를 만들어보자
- e.g. `makeCircle(double x, double y, double radius)` -> `makeCircle(Point center, double radius)`
- 함수의 의도나 인수의 순서와 의도를 제대로 표현하려면 좋은 함수 이름이 필수
- `write(name)` : 이름이 무엇이든 쓴다
- `writeField(name)` : 이름이 필드 이름이라는 것을 알 수 있음
- 함수 이름에 키워드를 추가해보자
- `assertEquals(expected, actual)`보다 `assertExpectedEqualsActual(expected, actual)`를 작성하면 인수의 순서를 기억할 필요가 없음
✏️ #6, 부수 효과를 일으키지 마라!
- 함수는 한 가지 일만 해야하는데 여러 작업을 하게되면 부수 효과가 일어남
- 부수 효과로 숨겨진 경우에는 혼란이 매우 커짐
- 예를 들면, 비밀번호 검증 함수인데 실패하면 세션 정보를 지워버리는 부수효과를 만들면 예상치 못한 혼란을 초래할 수 있음
✏️ #7, 오류 코드보다 예외를 사용하라!
- 오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되어 깔끔해짐
- try/catch 블록은 좋지않으며, 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞어서 좋지않으므로 블록을 별도 함수로 뽑아내자
- 오류 처리도 '한 가지' 작업에 속하므로 오류를 처리하는 함수는 오류만 처리하자
✏️ #8, 반복하지 마라!
- 중복은 문제
- 코드 길이가 늘며, 알고리즘 변경 시 여러 곳을 손봐야하며, 한 곳을 빠뜨리면 오류 발생
- 중복을 없애면 가독성 크게 높아짐
- 많은 원칙과 기법은 중복을 제거하거나 제어할 목적으로 나왔다는 사실을 명심
✏️ #9, 함수를 어떻게 짜죠?
- 소프트웨어를 개발하는 것은 글짓기와 비슷함
- 먼저 생각을 기록하고 읽기 좋게 다듬는다
- 초안은 서투르고 어수선하므로 원하는 대로 읽힐 때까지 다듬고 고치고 정리함
- 함수를 작성할 때도 위와 마찬가지이며, 처음부터 완벽하게 작성할 순 없다
👋 마치면서
프로그래밍의 기술은 언제나 언어 설계의 기술이다.
이 규칙을 잘 따른다면 길이가 짧고, 이름 좋고, 체계 잡힌 함수가 나올 것이다.
진짜 목표는 "시스템이라는 이야기를 풀어가는 것"을 명심하자.
작성하는 함수가 분명하고 정확한 언어로 깔끔하게 맞아떨어지면 이야기를 풀기 쉬워진다.
Clean Code / 로버트 C.마틴 지음 / 박재호, 이해영 옮김 / 인사이트
'도서' 카테고리의 다른 글
[토비의 스프링] 1장 오브젝트와 의존관계 (0) | 2022.09.25 |
---|---|
[Clean Code] 2장 의미 있는 이름 (0) | 2022.02.13 |
[Clean Code] 1장 깨끗한 코드 (0) | 2022.02.13 |
[Clean Code] 시작 (0) | 2022.02.12 |