함수는 작을수록 읽기 쉽다

함수는 작게 만들수록 의도가 잘 보인다. 짧은 함수는 이름과 본문이 서로를 설명하고, 변경할 때 영향 범위를 파악하기 쉽다. 특히 들여쓰기가 깊어지는 함수는 읽는 사람이 조건과 반복의 맥락을 계속 기억해야 하므로 빠르게 피로해진다.

작다는 말이 무조건 두세 줄이어야 한다는 뜻은 아니다. 중요한 기준은 한 함수가 같은 추상화 수준에서 한 가지 일을 하는지다. 함수 안에 정책 설명, 세부 구현, 예외 처리, 데이터 변환이 한꺼번에 섞이면 읽는 흐름이 끊긴다.

한 가지 일과 한 단계 추상화

좋은 함수는 이름으로 약속한 일을 한다. 더 정확히는 같은 추상화 수준의 문장들로 구성된다. 예를 들어 상위 함수는 “설정 페이지를 포함한다”, “테스트 페이지를 포함한다”, “해제 페이지를 포함한다”처럼 큰 흐름을 보여주고, 각 단계의 세부 구현은 아래 함수로 내려보내는 편이 읽기 쉽다.

이런 방식은 책에서 말하는 내려가기 규칙과 연결된다. 코드를 위에서 아래로 읽을 때 점점 더 구체적인 내용으로 내려가면 독자는 필요한 만큼만 세부 사항을 확인할 수 있다. 반대로 상위 정책과 하위 구현이 한 함수에 섞이면 전체 흐름도, 세부 동작도 모두 흐려진다.

이름과 인수

함수 이름은 길어도 괜찮다. 짧지만 모호한 이름보다 길어도 정확한 이름이 낫다. 함수가 하는 일을 동사와 명사로 자연스럽게 표현하고, 모듈 안에서는 같은 개념에 같은 단어를 쓰는 것이 좋다.

인수는 적을수록 이해하기 쉽다. 인수가 많아질수록 호출하는 쪽에서 순서와 의미를 기억해야 하고, 테스트해야 할 조합도 늘어난다. 가능하다면 인수가 없는 함수가 가장 단순하고, 하나나 둘까지는 비교적 읽을 만하다. 세 개 이상이 필요하다면 객체로 묶을 수 없는지, 함수가 너무 많은 일을 하고 있지는 않은지 의심해볼 만하다.

특히 boolean 플래그 인수는 함수가 두 가지 일을 한다는 신호일 때가 많다. render(true)보다 renderForSuccess()renderForFailure()처럼 의도를 나눈 이름이 읽기 쉽다.

부수 효과를 숨기지 않기

함수는 이름으로 약속한 일을 해야 한다. 값을 조회하는 것처럼 보이는 함수가 내부 상태를 바꾸거나, 검증 함수가 몰래 세션을 초기화한다면 호출자는 코드를 믿기 어렵다. 명령과 조회를 분리하라는 원칙도 여기서 나온다. 함수는 상태를 바꾸거나, 값을 알려주거나, 둘 중 하나에 집중하는 편이 좋다.

오류 처리도 마찬가지다. try/catch가 본문 한가운데 섞이면 정상 흐름과 예외 흐름이 함께 뒤엉킨다. 가능하다면 오류 처리 함수를 분리해서 핵심 로직이 무엇인지 먼저 보이게 만드는 편이 낫다.

중복을 줄이는 함수

함수를 잘 나누는 이유 중 하나는 중복을 줄이기 위해서다. 같은 조건, 같은 계산, 같은 흐름이 반복되면 수정할 지점이 늘어나고 실수할 가능성도 커진다. 중복을 제거하면 코드 양만 줄어드는 것이 아니라 개념이 한 곳에 모인다.

결국 함수는 프로그램을 읽는 단위다. 작고, 이름이 분명하고, 한 가지 일을 하는 함수가 많아질수록 코드는 설명서처럼 읽힌다.

다음장으로 4장