이름은 명확성을 유지하는 선에서 최대한 짧아야 한다
원문은 Ben Hoyt님이 에 게재했습니다. 이 블로그 구독하기
이 짧은 글은 Python API 디자인에 대해 제가 했던 강연의 일부를 바탕으로 했지만, 핵심 내용은 프로그래밍 언어 전반에 걸쳐 통한다고 생각한다.
넷스케이프에서 일했던 프로그래머 필 칼튼(Phil Karlton)은 유명하게도 “컴퓨터 과학에서 어려운 일은 두 가지뿐이다. 캐시 무효화와 이름 짓기다”라고 말했다.
사실 이름을 짓는 것 자체는 쉽다. 어려운 건 이름을 잘 짓는 것이다. 이름을 짓는 일이 더 어려웠더라면 좋은 이름을 고르는 데 더 오래 고민했을지도 모른다.
일부 개발자는 너무 짧은 이름을 쓰기도 한다. 하지만 더 흔한 실수는 지나치게 긴 이름을 쓰는 것이라고 생각한다.
이름이 길면 중요한 정보를 파악하기 어려워진다. 예를 들어, 갓 대학을 졸업한 밥(Bob Just-Out-Of-Uni)이 Requests 라이브러리를 설계하고 있는데, 교수 중 한 명이 “함수 이름은 그 함수가 하는 일을 설명하도록 지어라”고 말했다고 상상해 보자.
그 자체로 나쁜 조언은 아니지만, 그는 “함수 이름은 그 함수가 하는 모든 것을 설명하도록 지어라”고 잘못 알아듣고 이렇게 작성했다:
# requests.py
def send_get_request_and_receive_response(url):
...이 함수를 사용하면 다음과 같이 된다:
import requests
response = requests.send_get_request_and_receive_response(url)무엇이 문제일까? 불필요한 단어들은 아무것도 더해주지 않는다. 오히려 명확성을 떨어뜨린다. requests.get()는 보고 읽기에 쉽지만, 긴 이름에서는 중요한 부분인 ‘get’ 메서드가 이름 한가운데 묻혀 버린다.
“request”라는 단어는 모듈 이름에 한 번, 함수 이름에 또 한 번, 두 번 등장한다. 그리고 “send”와 “receive response”도 있다. 하지만 이 라이브러리의 존재 이유 자체가 요청을 보내고 응답을 받는 것이므로, 이 역시 불필요하다.
결국 남는 것은 간결한 requests.get()이다 — “GET 요청을 수행하라”는 의미를 명확하고 간결하게 전달한다. 이보다 더 짧게 만들 수는 없다. 물론 requests.g()처럼 만들 수도 있겠지만, 그건 그냥 우스꽝스럽다.
나는 모듈 이름과 함께 점으로 이어 쓰는 형태로 모듈이 사용되도록 설계하는 것을 강력히 지지한다. Requests가 바로 그 예다. get()만 따로 보면 무슨 의미인지 잘 와닿지 않지만, requests.get()은 깔끔하고 명확하다. 모듈 이름이 바로 옆에 있어 의미를 분명히 해 주므로 이름 충돌도 일어나지 않는다.
다시 말해, 이름은 그 대상이 하는 모든 일을 설명하도록 짓지 말고, 문맥 안에서 의미가 명확해지도록 지어야 한다.
간결한 이름을 쓰는 것은 라이브러리를 설계할 때뿐만 아니라 일반적인 코드를 작성할 때도 중요하다. 지역 변수 이름은 대개 짧아도 된다. 주변 코드, 즉 그 변수가 속한 함수나 스크립트가 문맥을 제공하기 때문이다.
예를 들어 보자. 최근 repository_statistics.py 스크립트에서 이런 코드를 봤다:
repository_index_retriever = RepositoryIndexRetriever(args.architecture)
repository_statistics_processor = RepositoryStatisticsProcessor()
for repository_object in repository_index_retriever.retrieve_repositories():
for repository_file in repository_object.repository_files:
repository_statistics_processor.increment_repository_file_count(repository_file.name)
for repository_file_name, count in repository_statistics_processor.get_top_n_repositories(10):
print(repository_file_name, count)거창한 단어가 너무 많다! 코드의 단순한 논리가 장황한 표현에 묻혀 버렸다.
내가 다시 쓴 코드는 이렇다(그리고 RepositoryStatisticsProcessor는 그저 collections.Counter를 거창하게 포장한 것에 불과했으므로, 그냥 그걸 쓰자):
fetcher = repolib.IndexFetcher(args.arch)
counts = collections.Counter()
for repo in fetcher.get_repos():
for file in repo.files:
counts[file.name] += 1
for repo, count in counts.most_common(10):
print(repo, count)구조는 같지만 논리를 파악하기가 훨씬 쉬워졌다.
그리고 파이썬에서는 종종 한 줄짜리 리스트 컴프리헨션이나 제너레이터 표현식을 사용한다. 그런 코드를 작성할 때는 한 글자 변수 이름을 써도 된다 — 두 번의 사용이 한 줄에 함께 있으면, f가 file이나 file_object만큼이나 명확할 수 있다.
예를 들어, Counter.update와 제너레이터 표현식, 그리고 한 글자 변수 이름을 써서 안쪽 루프를 이렇게 바꿀 수 있다:
counts.update(f.name for f in repo.files)마지막으로 Go 프로젝트의 테크 리드인 러스 콕스(Russ Cox)가 자신의 변수 네이밍 철학을 이렇게 말한 인용으로 글을 맺겠다:
이름의 길이는 그 이름이 담고 있는 정보량을 넘어서는 안 된다. … 전역 이름은 더 다양한 문맥에서 등장하므로 상대적으로 더 많은 정보를 담아야 한다. 그럼에도 불구하고 짧고 정확한 이름이 장황한 이름보다 더 많은 것을 말해 줄 수 있다.
acquire와take_ownership을 비교해 보라. 모든 이름이 제 역할을 하게 하라.
마지막 문장은 그 자체로 시다. 하지만 나는 내 나름의 좀 더 평범한 ‘변수 네이밍 철학’을 다시 한번 강조하며 글을 마무리하겠다:
이름은 명확성을 유지하는 선에서 최대한 짧아야 한다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기