[노개북] 클린코드 DAY 4~6 - 함수

danbi lee·2025년 7월 8일

개발자북클럽

목록 보기
14/21

TIL 3줄 요약

  • 함수를 만드는 이유는 큰 개념을(함수 이름) 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해서이다.
  • 소프트웨어 개발에서 지금까지 일어난 혁신은 소스 코드에서 중복을 제거하려는 지속적인 노력으로 보인다.
  • 정확하고 깔끔한 언어로 시스템 이야기를 풀어나가는 것이 진짜 목표다.

TIL 날짜

2025.07.07 ~ 07.08

오늘 읽을 범위

  • 함수

책에서 기억하고 싶은 내용

함수는 한가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한가지만을 해야한다.

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

- 추상화 수준: "얼마나 구체적/세부적인지 vs 얼마나 개념적인지"를 나타내는 말
- 높은 추상화: 무엇(what)을 한다에 집중(세부 방법은 안 드러냄)
- 낮은 추상화: 어떻게(how)를 한다를 상세히 기술(구현 세부까지 보임)

지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.

fun renderPageWithSetupsAndTeardowns(page: Page) {
    if (page.isTestPage()) {
        page.includeSetupAndTeardown()
    }
    page.renderAsHtml()
}
  • 테스트 페이지인지 판단
  • 필요하면 설정과 해제 페이지를 추가
  • HTML로 렌더링

이 함수는 사실 3단계 작업을 수행하지만, isTestPage, includeSetupAndTeardown, renderAsHtml 모두 "페이지에 무엇을 한다" 라는 동일한 추상화 수준의 작업 단위이다. 즉, "테스트 페이지를 렌더링하기 위해 필요한 동작 수행"이라는 하나의 역할을 수행한다.

fun renderPageWithSetupsAndTeardowns(page: Page) {
    if (page.isTestPage()) {
        page.includeSetupAndTeardown()
    }
    // 갑자기 아주 낮은 레벨로 내려감
    openFile()
    writeBytes("<html>")
    closeFile()
}
  • 일부는 "페이지에 무엇을 한다" (높은 수준)
  • 일부는 "파일 시스템을 다루는 상세 구현" (낮은 수준)

한 함수 내에 추상화 수준을 섞으면 코드를 읽는 사람이 헷갈린다. 이 함수가 정말 무엇을 책임지는지 혼란을 준다.
근본 개념과 세부사항을 뒤섞기 시작하면, 깨어진 창문처럼 사람들이 함수에 세부사항을 점점 더 추가한다.

위에서 아래로 코드 읽기: 내려가기 규칙
코드는 위에서 아래로 이야기처럼 읽혀야 좋다. 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.

서술적인 이름을 사용하라

코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다.
이름이 길어도 괜찮다. 겁먹을 필요없다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.

함수 인수

이상적인 인수 개수: 0개 > 1개 > 2개 순. 3개는 피하고, 4개 이상은 금지.

단항 함수

  • 질문형: isValid(password) - 인수에 질문을 던짐
  • 변환형: parseInt(text) - 인수를 변환해서 반환
  • 함수명으로 두 용도를 명확히 구분할 것

이항 함수

  • 단항보다 이해하기 어려움
  • 가능하면 단항으로 변경 시도
  • 예외: 좌표처럼 자연스러운 순서쌍 Point(x, y)

삼항 함수

  • 순서, 주춤, 무시로 야기되는 문제가 증가
  • 신중히 고려할 것

출력 인수

  • 객에 지향에서는 불필요. 객체가 자기 상태를 직접 변경하는 방식 선택
// Bad: 출력 인수 - 의미 모호
appendFooter(report)                    // report가 입력? 출력?
fun appendFooter(report: StringBuffer)  // 이름만으론 출력인수인지 모름

// Good: 객체 메서드 - 직관적
report.appendFooter()                   // report가 자신을 수정

명령과 조회 분리

함수는 명령(수행) 또는 조회(답변) 중 하나만 해야 함. 둘 다 하면 혼란 야기.

  • 명령: 객체 상태 변경
  • 조회: 객체 정보 반환
fun set(attribute: String, value: String): Boolean  // 명령 + 조회

if (set("username", "unclebob")) {  // 혼란스러움
    // set이 "설정하다"인지 "설정된"인지 모호
    // 동사인지 형용사인지 불분명
}

// 조회와 명령 완전 분리
if (attributeExists("username")) {           // 조회: 존재 확인
    setAttribute("username", "unclebob")     // 명령: 값 설정
}

함수를 어떻게 짜죠?

점진적으로 단계별 접근!

  1. 초안 작성: 길고 복잡하게 일단 작동하는 코드 작성
  2. 테스트 작성: 서투른 코드로 단위 테스트 케이스 작성
  3. 리팩토링:
    • 코드 다듬기
    • 함수 분리하기
    • 이름 개선하기
    • 중복 제거하기
    • 메서드 순서 조정하기
  4. 테스트 유지: 모든 단위 테스트가 항상 통과하도록 보장

오늘 읽은 소감

정리하고 싶은 내용이 너무 많은데 글이 길어질까봐 짧게 옮겼다. 이 조차도 글이 길어질까봐 겁 먹고있다. 개발을 하다보면 함수를 계속해서 만들어야 하는데, 추상화 수준이 일정해야하고, 함수명에 인수 순서가 반영 되어야하고, 인수의 개수 또한 너무 많으면 안된다는것을 모르고 있었다. 나는 왜 함수를 한번에 완벽하게 짜려고 했는지 모르겠다. 블로그 글도 계속 읽어보며 다듬어가는데, 코드 또한 단계별로 접근해서 개선해 나가야겠다. 프로젝트 상황에 맞추다 보면 오늘 배운 것들을 제대로 지키지 못할거 같다. 그치만 가이드라인이 있으면 마음이 달라진다. 못지키면 찜찜해진다ㅎㅎ

profile
계속해서 보완중

0개의 댓글