Skip to main content
soso01 blog
  1. Posts/

클린코드 3장 함수 요약

·4 mins

작게 만들어라. #

  • 함수를 만드는 첫째 규칙은 ‘작게’이고, 둘째도 ‘작게’다.
  • 켄트 백의 프로그램 Sparkle은 모든 함수가 2~4줄 정도였고, 각 함수가 너무도 명백했다.

블록과 들여쓰기 #

  • if / else문, while문 등에 들어가는 블록은 한줄이어야 한다.
  • 대게 거기서 함수를 호출한다.
  • 이 말은 즉, 중첩 구조가 생길만큼 함수가 커져서는 안된다는 의미이다.

한 가지만 해라! #

  • 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야한다.
  • ‘한 가지’란? 추상적인 수준의 하나의 작업을 의미한다.
    • 예를 들어 ‘물마시기’는 컵을 들고 → 입에 가져다 대고 → 마신다 라는 과정을 거치지만 ‘물을 마신다’라는 추상적 개념으로 하나의 작업으로 볼 수 있음.
  • 우리가 함수를 만드는 이유는 큰 개념을 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해서이다.
  • 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.

함수당 추상화 수준은 하나로! #

  • 함수가 확실히 ‘한 가지’ 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
  • 한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다.
    • 특정 표현이 근본 개념인지 아니면 세부사항인지 구분하기 어렵기 때문이다.
    • 근본 개념과 세부사항을 뒤섞기 시작하면, 깨어진 창문처럼 사람들이 함수에 세부사항을 점점 더 추가한다.
  • 내려가기 규칙
    • 코드는 위에서 아래로 이야기처럼 읽혀야 좋다.
    • 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
    • 즉, 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.

switch 문 #

  • 본질적으로 switch 문은 N가지를 처리한다.
  • 각 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법이 있다. 다형성을 이용하자.
  • switch문을 추상 팩토리에 숨기고, 파생 클래스가 함수를 실행하도록 하자.

서술적인 이름을 사용하라! #

  • 워드가 말했던 클린 코드의 원칙, “코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 클린 코드라 불러도 된다.”
  • 한 가지만 하는 작은 함수에 좋은 이름을 붙인다면 이 원칙을 달성함에 있어 이미 절반은 성공한다.
  • 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
  • 이름이 길어도 괜찮다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
  • 서술적인 이름을 사용하면 개발자 머릿속에서도 설계가 뚜렷해지므로 코드를 개선하기 쉬워진다.

함수 인수 #

  • 이상적인 인수는 0개다. 다음은 1개이고, 3개 이상은 가능한 피하자.
  • 테스트 관점에서 보면 인수는 어렵다.
    • 갖가지 인수 조합으로 함수를 검증하는 테스트 케이스를 작성한다고 상상해보자.
    • 인수가 0개이면 간단하다. 3개이상이면 상당히 부담스럽다.
  • 출력 인수는 입력 인수보다 이해하기 어렵다.
    • 흔히 우리는 함수에다 인수로 입력을 넘기고 반환값으로 출력을 받는다는 개념에 익숙하다.
    • 대개 함수에서 인수로 결과를 받으리라 기대하지 않는다.
  • 단항 형식
    • 함수에 인수를 1개 넘기는 이유로 가장 흔한 경우 두가지이다.
      • 인수에 질문을 던지는 경우
      • 인수를 뭔가로 변환해 결과를 반환하는 경우
    • 드물지만 단한 함수 형식 이벤트도 사용함.
  • 플래그 인수
    • 플래그 인수는 끔찍하다.
    • 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 공표하는 셈이나 마찬가지이다.
  • 이항 함수
    • 인수가 2개이면 1개인 경우보다 이해하기 어렵다.
    • new Point(x, y)와 같은 경우는 적절하다. x, y는 자연적인 순서를 가진다.
    • 하지만 다른 경우 인수간에 자연적 순서가 없이, 인위적인 순서를 기억해야 하는 불편함을 가진다.
    • 이항 함수는 프로그램을 짜다보면 불가피한 경우도 생기지만, 가능하다면 단항함수로 바꾸도록 노력하자.
  • 인수 객체
    • makeCircle(x, y, radius)를 인수 객체를 사용해 makeCircle(point, radius)로 고칠 수 있다.

사이드 이펙트를 일으키지마라 #

  • 사이드 이펙트는 거짓말이다. 함수에서 한 가지를 하겠다고 약속하고선 남몰래 다른 짓을 하니까.
  • 사이드 이펙트는 ‘시간적인 결합’이나 ‘순서 종속성’을 초래할 수 있다.

명령과 조회를 분리하라! #

  • 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.
  • 둘 다 하면 혼란을 초래한다.

오류 코드보다 예외를 사용하자 #

if (deletePage(page)) === E_OK)
  • 명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 미묘하게 위반한다.
    • 자칫하면 if문에서 명령을 표현식으로 사용하기 쉬운 탓이다. ??
  • 위 코드는 여러 단계로 중첩되는 코드를 야기한다.
  • 오류 코드를 반환하면 호출자는 오류 코드를 곧바로 처리해야 한다는 문제에 부딪힌다.
  • try / catch 블록은 추하다.
    • 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다.
    • 그러므로 try / catch 블록을 별도 함수로 뽑아내는 편이 좋다.
  • 오류 처리도 한 가지 작업이다.
    • 함수는 ‘한 가지’ 작업만 해야 한다. 오류처리도 작업에 속한다.
    • 오류를 처리하는 함수는 오류만 처리해야 마땅하다.

반복하지 마라! #

  • 중복은 문제다.
  • 코드 길이가 늘어날 뿐 아니라 알고리즘이 변하면 네 곳이나 손봐야 한다.

함수를 어떻게 짜죠? #

  • 소프트웨어를 짜는 행위는 여느 글짓기와 비슷하다.
  • 논문이나 기사를 작성할 때는 먼저 생각을 기록한 후 읽기 좋게 다듬는다.
  • 함수도 마찬가지로 처음에는 길고 복잡하다. 들여쓰기 단계도 많고 중복된 루프도 많다. 인수 목록도 아주 길다. 이름은 즉흥적이고 코드는 중복된다. 그 코드를 테스트하는 단위테스트 코드를 만들고 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거해야 한다.
  • 최종적으로 이 장에서 설명한 규칙을 따르는 함수가 얻어진다. 처음부터 짜내는건 어렵다.

결론 #

  • 함수는 언어에서 동사며, 클래스는 명사다.
  • 마스터 프로그래머는 시스템을 구현할 프로그램이 아니라 풀어갈 이야기로 여긴다.
  • 이 장에서 함수를 잘 만드는 기교를 소개했지만, 진짜 목표는 시스템이라는 이야기를 잘 풀어나가는 데 있다는 사실을 명심하자.