프로그래머를 위한 이름 짓는 원리

Gravatar for bakyeono@gmail.com
박연오개발자/개구리 애호가/이름 짓기에 진심인 편
2021. 09. 24.

컴퓨터 프로그래머는 '이름 짓기'를 가장 전문적으로 다루는 직업일지도 모릅니다. 어쩌면 철학관의 작명사보다도 더요. 우리는 좋은 이름을 찾기 위해 많은 시간과 노력을 들입니다. 이름의 좋고 나쁨이 소프트웨어의 유지보수성에 큰 영향을 끼친다는 것을 경험으로 알고 있기 때문입니다.

하지만 정작 컴퓨터 과학 커리큘럼이나 기술 서적에서 이름 짓는 방법을 찾아보기는 쉽지 않습니다. 일이 바쁠 때는 이름 짓는 데 시간을 많이 쓰는 게 잘못처럼 느껴지기도 합니다. 이름은 각자의 취향일 뿐이며 이름의 품질 기준 따위는 없으니 기계적으로 규칙만 정하면 된다고 생각하시는 분도 가끔 만날 수 있습니다.

그렇다면 다음과 같은 의문이 제기됩니다.

  • 이름은 프로그래머들이 생각하는 것만큼 실제로 중요한 것일까요?
  • 어떤 이름이 좋다 나쁘다 판단할 수 있는 기준은 무엇일까요?

이 글에서는 <클로저 기본원리(Elements of Clojure)>(재커리 텔먼 저, Leanpub 출판)에 실린 이름 짓는 원리를 소개하며 이 의문을 풀어보려 합니다.

이름이 중요한 이유

<클로저 기본원리>에서는 이름이 **간접화(indirection)**의 수단이기 때문에 중요하다고 설명합니다. 이 때 간접화란, 무엇(가리키는 방법, 인터페이스)과 어떻게(실제 구현)를 분리하는 것을 뜻합니다. 대부분의 프로그래머에게는 추상화(abstraction)라는 용어가 더 익숙할 것입니다. 하지만 추상화라는 용어에는 그것 말고도 더 많은 뜻이 담겨 있습니다. 한 단어가 갖는 의미가 넓으면 문맥에서 뜻을 파악할 때 더 많은 노력이 듭니다. 그래서 재커리 텔먼은 의도적으로 의미가 더 좁은 용어인 간접화를 사용합니다.

간단한 클로저 함수에서 간접화의 예를 살펴보겠습니다.

1(defn get-태양-목성
2 [m k]
3 (get-in m [:태양 :목성 k]))

이 코드를 자세히 이해하실 필요는 없습니다. 함수에 등장하는 이름을 중심으로 훑어봅시다.

  • 함수 get-태양-목성을 정의했습니다.
  • 매개변수 m: 함수가 넘겨받을 맵 인자에 붙일 이름입니다.
  • 매개변수 k: 함수가 넘겨받을 키워드 인자에 붙일 이름입니다.
  • (get-in m [:태양 :목성 k]): 함수의 본문으로, 중첩 해시맵 m에서 세 키워드 :태양 :목성 k 에 해당하는 값을 차례대로 꺼냅니다.

여기서 mk라는 매개변수 이름은 클로저 생태계에서 관례적으로 사용되는 이름이므로 신경쓰지 말고, 함수 이름 get-태양-목성이 좋은 이름인지 생각해봅시다. 이 이름에는 나쁜 점이 두 가지 있습니다.

  1. 함수의 이름만 보고 하는 일을 알기가 어렵습니다.
  2. 함수 호출을 내부 구현으로 변경하더라도 코드의 이해도가 낮아지지 않습니다. 다시 말해, 함수의 이름이 제공하는 이점이 없습니다.

get-태양-목성이라는 이름은 모호해서 그 이름만 보고는 함수가 무슨 일을 할지 예상하기가 어렵습니다. 그래서 별도의 문서를 보거나 함수의 내부 구현을 봐야 합니다. 그런데 함수의 구현을 보면 간결한 내용이라 오히려 함수 이름을 사용하는 것보다 구현을 인라인으로 사용하는 편이 더 이해하기 쉽습니다.

1(get-태양-목성 m :칼리스토) ; 함수가 무슨 일을 하는지 짐작하기 어렵습니다.
2(get-in m [:태양 :목성 :칼리스토]) ; 구현으로 대체하면 오히려 이해하기 쉽습니다.

그러면 "과연 이 함수가 필요하긴 한 것인가?", "그냥 구현을 풀어 쓰는게 더 나은 게 아닌가?"라는 합리적인 의심을 하게 됩니다. 이런 문제가 생기는 이유는 함수의 이름을 내부 구현을 축약해 붙였기 때문입니다. 함수의 구현이 아니라, 함수의 목적에서 그 이름을 딴다면 이야기는 달라집니다.

1(get-태양-목성 m :칼리스토) ; 함수가 무슨 일을 하는지 짐작하기 어렵습니다.
2(get-목성의-달 m :칼리스토) ; 목성의 위성을 꺼낸다는 걸 짐작할 수 있습니다.

함수의 구현은 그대로 두고 이름을 get-목성의-달으로 바꿨을 뿐입니다. 그런데 다음과 같은 장점이 생깁니다.

  • 함수의 이름이 간접화 계층(indirection layer)을 형성합니다.
  • 함수의 목적(계약)과 수단(구현)이 분리됩니다.
  • 함수의 내부 구현이 인터페이스에서 숨겨지며, 변경 가능하게 됩니다.

여기서 한걸음 더 나아가 매개변수/인자의 이름까지 적절히 변경하면 간접화 수준을 더 높일 수 있습니다.

1(get-목성의-달 은하 :칼리스토) ; 은하 데이터에서 목성의 위성을 꺼내나 봐요.

이제 매개변수/인자에서도 간접화 계층이 형성되어 새로운 이점이 생깁니다.

  • 함수가 어떤 데이터를 대상으로 동작하는지 이해하기 쉬워집니다.
  • 은하계 데이터의 구현이 이름 속에 숨겨집니다. m은 인자가 맵 구조여야 한다는 걸 뜻하지만, 은하는 인자의 목적만을 묘사하고 구현에 대해서는 묘사하지 않기 때문이죠.

이처럼 구현이 아닌 목적을 기준으로 이름을 붙이면 이름은 간접화를 형성하는 수단이 됩니다. 간접화 계층을 한 층 한 층씩 점진적으로 쌓아 올림으로써 프로그래머는 코드 전체를 동시에 이해하지 않고도 작업을 할 수 있게 됩니다. 프로그램을 만드는 일은 간접화 계층을 쌓아올리는 일이고, 간접화 계층은 이름을 붙임으로써 형성할 수 있습니다. 그러므로 프로그램을 만드는 일은 곧 이름 짓는 과정을 반복하는 일입니다.

좋은 이름을 어떻게 찾을 수 있을까?

이름이 그렇게 중요한 것이라면, 어떤 이름이 좋은 이름이라고 할 수 있을까요? 프로그래밍 입문서 <연오의 파이썬>(박연오 저, 인사이트 출판)에서 저는 이 질문에 대해 '문맥에서 충분히 구체적이면서 간결한 이름'이라고 간단히 답했습니다.

의미있는 이름은 프로그램이 다루는 문제에 따라 다르다. 수학 방정식을 푸는 프로그램이라면 x, y, z 처럼 간단한 이름도 충분히 의미 있는 이름이 될 것이다. 반대로 '면적'이라는 꽤 명확해 보이는 이름도 문맥에 따라 의미가 모호해질 수 있다. 다음 이름들을 생각해 보자.

  • 면적
  • 원의 면적
  • 동전의 면적
  • 십원 주화의 면적
  • 1970년대 십원 주화의 면적
  • 내가 가진 1970년대 십원 주화의 면적

동전교환기를 제어하는 프로그램을 만든다면, 그냥 '면적'보다는 더 명확한 이름이 필요할 것이다. 오늘날 발행되는 주화만 취급한다면 '십원 주화의 면적' 정도면 충분한 이름이 될 것이다.

(중략)

이처럼, 그 이름이 사용되는 문맥에서 충분히 구체적이면서 간결한 이름이 좋다.

<연오의 파이썬> '2.2절 변수와 이름' 중

간결한(일반적인) 이름과 자세한(구체적인) 이름 사이에서 배율을 조절하며 알맞은 이름을 찾아야 한다고 설명한 것입니다. 이것은 문학 글쓰기 교재 <개구리를 위한 글쓰기 공작소>(이만교 저, 그린비 출판)를 참고해 쓴 것입니다. 프로그래머와 작가는 '언어'로 무언가(코드와 글)를 '쓰는' 일을 한다는 점에서 공통점이 있어, 글쓰기 교재에서도 프로그래머가 배울 점이 있습니다.

모든 언어는 일정한 의미망을 갖는데, 그 의미망의 크기에 따라서 상위어와 하위어로 나눈다. 상위어일수록 의미가 포괄적이고, 하위어일수록 구체적이다.

(중략)

하위어와 상위어를 적절하게 가름할 필요가 있다. 알맞은 층위에서 알맞게 말하는 것이 가장 알맞다. 가령 구체화가 필요할 때는 하위어를, 일반화가 필요할 때는 상위어를 사용한다.

(중략)

추상의 사다리는 위로 올라갈수록 높은 추상 레벨이 되면서 보다 일반화되고, 내려올수록 낮은 추상 레벨이 되면서 보다 구체화된다. 낮은 레벨의 추상만 사용할 경우 내용은 지나치게 잡다해지고, 높은 추상만 고집할 경우 내용이 허황될 우려가 있다.

<개구리를 위한 글쓰기 공작소> '3장 거칠게 청킹하지 마라' 중

그런데 추상과 구체 사이에서 이름을 찾을 때는 '어떤 특성에 초점을 맞출지'를 함께 고려해야 합니다. 그에 따라 이름이 달라지기 때문입니다. 이 점에 관해서 <클로저 기본원리>에서는 프레게의 의미이론을 소개하며 설명합니다.

프레게의 의미이론

19세기 독일 논리학자 프레게(Gottlob Frege)는 과거 철학자들의 언어철학을 비판하고 문제점을 보완하는 새로운 의미이론을 발표했습니다. 프레게의 의미이론은 이름을 '어떻게' 좁혀야 하는지 판단할 때 참고가 됩니다.

프레게가 비판한 과거의 이론에서는 **'기호(zeichen)'**의 의미는 곧 그 기호가 가리키는 **'지시체(bedeutung)'**를 뜻한다고 생각했습니다. 그런데 이런 생각은 하나의 지시체를 서로 다른 기호로 가리키는 경우에 문제가 됩니다.

프레게는 샛별과 개밥바라기의 예를 듭니다. 샛별과 개밥바라기는 고대 그리스 사람들이 관측했던 두 천체입니다.

  • 샛별(Φώσφορος, 새벽에 동쪽에 보이는 별)
  • 개밥바라기(Ἕσπερος, 저녁에 서쪽에 보이는 별)

그런데 그리스인들은 나중에 샛별과 개밥바라기가 둘 다 같은 천체, 금성이라는 사실을 알게 되었습니다. 기호 '샛별'과 기호 '개밥바라기'는 금성을 동일한 지시체로 공유하는 것입니다. 기호의 의미가 곧 지시체라면, 지시체가 동일한 두 기호를 서로 바꿔 쓰더라도 아무 문제가 없어야 합니다. 실제로 아래의 예에서는 문제가 되지 않습니다.

  1. 샛별은 샛별이다. (참)
  2. 샛별은 개밥바라기다. (참)

샛별과 개밥바라기는 동일한 천체이므로 두 명제는 모두 참입니다. 그런데 두 기호를 서로 바꿔 사용할 수 없는 경우가 있습니다.

  1. 호메로스는 샛별이 샛별이라는 걸 알았다. (참)
  2. 호메로스는 샛별이 개밥바라기라는 걸 알았다. (거짓)

샛별과 개밥바라기가 같은 별이라는 사실이 밝혀진 것은 호메로스가 죽은지 100년도 지난 뒤의 일입니다. 호메로스가 샛별이 개밥바라기라는 걸 알았을 가능성은 거의 0에 가까우므로, 두번째 명제는 거짓으로 간주해야 합니다. 따라서 위 예에서는 샛별과 개밥바라기라는 이름을 서로 바꿔 사용할 수 없습니다.

이 문제가 생기는 이유는 기호에는 지시체만이 아니라 **'의미(sinn)'**가 함께 포함되어 있기 때문입니다. 위 예에서 기호, 의미, 지시체는 각각 다음과 같습니다.

  • 샛별(기호) - 새벽에 동쪽에 보이는 별(의미) - 금성(지시체)
  • 개밥바라기(기호) - 저녁에 서쪽에 보이는 별(의미) - 금성(지시체)

두 기호가 같은 지시체를 가리키더라도, 지시체를 지시하는 방법인 의미가 다르다면 기호의 뜻도 다릅니다. 그러므로 이름을 지을 때는 일반적인 이름과 구체적인 이름 사이에서 알맞은 배율을 찾는 것만이 아니라, 지시체를 어떤 의미로 바라볼 것인지도 함께 고려해야 합니다.

코드에서의 의미이론

프레게의 의미이론에서 살펴본 것과 비슷한 예를 프로그램 코드 버전으로 확인해 봅시다. 동일한 값에 두 개의 이름을 정의합니다.

1(def a 42)
2(def b 42)

ab 는 둘 다 42라는 동일한 값을 가리킵니다. 하지만 다음 두 명제는 동일하지 않습니다.

1(= a a)
2(= a b)

지금은 ab가 둘 다 같은 값을 지시하므로 식을 평가한 결과는 같겠습니다만, 나중에 ab가 지시하는 값이 달라지는 경우를 고려한다면 두 식은 서로 다른 명제입니다.

이 예에서 다음과 같은 생각을 이끌어낼 수 있습니다.

  1. 이름(기호)과 값(지시체)은 서로 다릅니다.
  2. 두 기호가 같은 값을 가리키더라도 의미가 서로 다를 수 있습니다.
  3. 이름은 가리키는 대상(지시체)뿐만이 아니라 그 대상이 무엇이어야 하는지(의미)를, 그리고 나중에 어떤 대상이 될 수 있는지(의미)를 함께 나타냅니다.
  4. 이름은 간접화 계층을 형성하므로, 코드에서 값을 변경해야 하는 경우에 값 자체를 변경하는 대신에 이름이 다른 값을 가리키도록 수정하는 것이 가능합니다.
  5. 동일한 값을 가리키는 여러 개의 이름을 정의한 경우, 이는 비록 지금은 두 이름이 같은 대상을 가리키지만 나중에는 가리키는 대상이 서로 달라질 수도 있다고 프로그래머가 예상했기 때문일 것입니다.

이 설명이 공감되지 않는다면, ab를 좀 더 구체적인 예(number-of-slots, number-of-cards)로 바꿔 생각해보시면 더 쉽게 이해하실 수 있을 것 같습니다.

의미를 기준으로 이름을 붙여야 하는 이유

지시체(구현)를 기준으로 하는 것보다, 의미(목적)를 기준으로 이름을 붙이는 것이 더 좋다는 점은 앞서 get-태양-목성get-태양의-달의 비교로 간단히 언급했습니다. 더 자세한 예를 알아봅시다.

예: 회사에서 내부적으로 사용하는 고유식별자를 id라고 이름 붙이기로 약속했습니다. 고유식별자를 구현하는 방식은 UUID(랜덤 128비트 값)로 정했습니다.

이 사례에서 기호, 의미, 지시체를 찾아봅시다.

  • 기호: 이름의 텍스트 표기. 이 사례에서는 'id'
  • 의미: 대상에 부여한 핵심 속성들. 이 사례에서는 '식별자의 고유성'
  • 지시체: 이름의 참조 대상. 이 사례에서는 'UUID 구현'

다음 두 명제를 고려해 봅시다.

  1. 우리 회사의 고유식별자는 고유하다. (참)
  2. 우리 회사의 고유식별자는 128비트 값이다. (구현에 따라 참일 수도 거짓일 수도 있음)

이름을 id(식별자)라고 붙였는데 식별을 위해서는 값이 고유해야 하므로 이 이름에는 '고유성'이라는 특성이 포함되어 있습니다. 그러므로 첫 명제는 항상 참입니다. 하지만 두번째 명제에서 '128비트 값'이라는 특성은 id라는 이름에 내재된 특성이 아닙니다. 두번째 명제는 항진명제가 아니며, 거짓으로 취급해야 합니다. 만약 두번째 명제를 참으로 간주한다면 '128비트 값'이라는 특성이 회사의 도그마가 되어 장래에 새 설계를 막는 구실을 할 것입니다.

코드에서 낯선 이름을 봤을 때, 코드를 읽는 사람이 알아야 하는 건 그 이름의 의미(목적)뿐입니다. 그 지시체(구현)가 무엇인지는 알 필요가 없으며, 알고 싶지도 않을 것입니다. 이름을 보고도 구현을 참고해야만 한다면 간접화가 제대로 되지 않은 셈입니다.

구현을 묘사하는 이름은 간접화 계층을 올바르게 형성하지 못하며, 구현을 변경하기 위해서는 인터페이스를 함께 변경할 수밖에 없도록 만듭니다. 반면에 의미를 묘사하는 이름은 간접화 계층을 형성하여 이름과 구현을 분리하며, 인터페이스를 유지한 채로 내부 구현을 변경하는 것을 가능하게 합니다.

협의성과 일관성

<클로저 기본원리>에서는 좋은 이름을 판단하는 두 가지 기준으로, '협의성'과 '일관성'을 제시합니다.

  • 협의성(narrowness): 나타내지 않는 것을 제거하여 나타내는 의미를 좁힌 상태
  • 일관성(consistency): 의미의 일관성이 있어 전체적인 문맥, 도메인, 프로그래밍 언어의 생태계 속에서 쉽게 이해되는 상태

협의성(의미가 좁음)

협의성에 대해서는 앞에서도 설명했습니다. 이름이 너무 일반적이지도 않고, 너무 구체적이지도 않게 균형을 갖추었으며, 대상의 핵심 특성에 맞게 의미를 좁힌 것을 말합니다.

  • 일반적인 이름은 대상의 특성 중 일부만을 서술합니다. 대상의 핵심 특성을 모호하게 묘사하여 과도한 변경을 허용합니다.
  • 구체적인 이름은 대상의 특성 중 대부분을 서술합니다. 대상의 내부 구현까지 노출함으로써 변경을 어렵게 만들거나, 사소한 세부 사항까지도 무시하지 못하도록 만듭니다.
  • 의미가 좁은 이름은 일반과 구체 사이에서 균형을 갖추며, 대상에서 나타내고자 하는 특성을 잘 설명하는 이름입니다.

그런데 이름의 협의성을 높이는 수단에는 이름 자체를 자세히 묘사하는 것 외에도 여러 가지가 있습니다.

  • 자세한 기호 (a, area, area-of-coin, area-of-10krw-coin)
  • 기호를 둘러싼 문맥 (coin-exchanger/area-of-coin)
  • 문서 ("이 동전교환기 펌웨어에서는 동전을 면적에 따라 구별하며...")
  • 작업자들의 일상적인 대화 ("그 동전교환기에서 외국 동전도 지원했던가?")

즉, 협의성은 코드 밖에서도 형성되거나 파괴될 수 있습니다. 이렇게 다양한 수단에 의해 협의성이 형성되므로, 긴 이름(unique-arbitrary-string-id) 대신 짧은 이름(id)을 사용하더라도 협의성을 유지할 수 있습니다. 하지만 그렇기 때문에 이름을 둘러싼 전체 문맥을 벗어나면 협의성이 상실될 수도 있습니다.

일관성(의미가 한결같음)

협의성이 전체적인 문맥 속에서 형성된다는 걸 고려하면, 일관성도 좋은 이름의 중요한 특성입니다. 이름에 일관성이 있다는 것은 문맥이 바뀌더라도 항상 같은 의미를 나타내는 것을 말합니다. 일관성 높은 이름은 코드를 읽는 사람이 그 의미를 더 적은 노력으로 파악할 수 있게 해줍니다. 반대로, 이름을 볼 때마다 "이 이름이 저 이름공간에서는 무슨 의미였더라?"하는 생각이 든다면 이름의 일관성이 낮은 것입니다.

예를 들어, 클로저에서는 map이라는 이름이 특정한 시퀀스 함수를 가리킵니다. 그런데 어떤 이름공간에 한정해서 map이라는 이름이 '지리 정보를 담은 데이터 컬렉션'을 가리키도록 재정의했다고 해봅시다. 그러면 다음과 같은 문제가 생깁니다.

  • map이라는 이름이 어떤 의미를 가리키는지에 관해 문서화를 해야 합니다.
  • 코드를 읽는 사람은 map이라는 이름이 등장할 때마다 문맥을 의식적으로 구별해야 합니다.
  • 특정 이름공간에서만이 아니라 전체 코드와 전체 문서에서, 현재 논의하는 map 이 어떤 map을 뜻하는 것인지 명시해야 합니다.

협의성과 일관성이 모두 높은 이름을 찾거나 만들어내는 것은 쉽지 않습니다. 다만, 이 두 특성을 어떤 이름이 좋은지 나쁜지를 판단하는 기준으로 삼으면 이름을 지을 때 도움이 될 수 있겠습니다.

의도적 비일관성

일관성을 의도적으로 깨트리는 경우를 종종 볼 수 있습니다. 예를 들어, 어떤 대학에서 student라는 데이터 유형을 다음과 같이 부서별로 다르게 정의했다고 합시다.

  • 입학부서: student는 입학지원자이다.
  • 회계부서: student는 현 학기에 등록한 자이다.
  • 각 학과부서: student는 학과의 수업에 등록한 자이다.

각 부서별로 서로 다른 소프트웨어를 개발해 사용한다면, student라는 이름을 혼동 없이 사용할 수 있습니다. 기호의 의미는 문맥 속에서 추론되므로, 별도의 문맥을 정의하면 하나의 이름을 서로 다른 용도로 사용할 수 있기 때문입니다.

하지만 이런 방식에는 단점이 있습니다.

  • 실제로는 서로 다른 프로그램을 만드는 것이 아니라, 하나의 프로그램에서 부서별로 이름공간만 다르게 정의하는 사례가 많습니다. 이런 경우, 입학부서 이름공간에 회계부서 이름공간의 student를 가져오는 등 이름과 데이터가 뒤섞일 위험이 생깁니다.
  • 코드를 읽을 때 문맥을 전환하며 이름의 의미를 파악해야 합니다. 코드 읽는 비용이 커지며, 오독의 여지가 생깁니다.

이처럼 여러 개의 문맥을 형성하여 이름에 다양한 의미를 부여하는 의도적 비일관성에는 비용이 따릅니다.

문맥이 여러 갈래로 쪼개지는 문제를 방지하려면 이름의 의미를 가장 일반적인 경우에 맞춰 좁혀야 합니다.

  • student를 입학지원자로 정의하는 경우, 이 이름은 입학부서에서만 일관성이 유지되고 그 밖에서는 일관성이 깨집니다.
  • 모든 부서에서 일관성을 갖추려면 각 부서의 student에 대해 그 실제 의미에 맞는 이름을 서로 다르게 정의해야 합니다. student와 같은 모호한 이름을 사용해서는 안 됩니다.

완전한 일관성을 달성하는 유일한 방법은 기호와 의미 사이에 일대일 관계를 형성하는 것입니다. 각 의미에 맞는 기호를 고안해내야 합니다. 이를 위해서는 자연 이름 대신 인공 이름을 사용하는 것도 검토해 볼 필요가 있습니다.

자연 이름, 인공 이름

자연 이름은 평소 여러 가지 의미로 널리 사용되는 말을 이용한 이름이고, 인공 이름은 특정한 의미가 없는 말이나 새로운 말을 이용한 이름입니다. 이 둘은 서로 상반된 특징을 가집니다.

자연 이름

  • 예: student
  • 명료성: 모호함. 그 이름에 이미 수많은 의미가 담겨 있으며, 따라서 의미가 모호합니다.
  • 이해 수준: 스펙트럼이 넓음. 누구나 그 의미를 유추할 수 있어, 약간의 의미를 이해합니다. 하지만 이해 수준은 사람마다 달라서, 약간만 이해하는 사람도 있고 자세히 이해하는 사람도 있습니다.
  • 참여도: 높음. 학습 없이도 의미를 유추할 수 있습니다. 초보자도 바로 참여할 수 있습니다.

인공 이름

  • 예: monad
  • 명료성: 분명함. 임의의 특정한 의미로 정의할 수 있으므로 모호하지 않습니다.
  • 이해 수준: 이분법적. 누군가가 그 뜻을 알거나, 모르거나 둘 중 하나입니다.
  • 참여도: 낮음. 이름의 의미를 유추할 수 없습니다. 이름의 의미를 모르는 사람은 참여가 불가능하며, 의미를 배워야만 참여할 수 있습니다.

인공 이름은 협의성과 일관성이 높지만, 학습 비용을 높이고 참여도를 낮추며 문서화가 반드시 필요하다는 단점도 있습니다. 제 생각으로는 모든 이름을 인공 이름으로 만들어내는 것은 참여자들의 초기 진입을 어렵게 만들기 때문에 바람직하지 않을 것 같습니다. 일부 전문 영역에서는 인공 이름을 많이 정의해서 의도적으로 진입 장벽을 설정하는 듯한 사례도 볼 수 있습니다. 이렇듯 자연 이름과 인공 이름의 장단점을 따져 알맞은 이름을 골라야 하겠습니다.

그 외의 팁들

<클로저 기본원리>에는 클로저에서 이름을 지을 때 고려해볼 자세한 사항이 더 많이 나와 있습니다. 클로저에 특화된 내용은 빼고, 다른 언어를 사용하는 분들도 참고해 볼만한 팁들을 몇 가지만 더 소개해 보겠습니다.

짧은 이름과 자기명세성이 높은 이름 중에서 고민할 때 (이름의 길이와 자기명세성이 비례한다고 가정)

  • 여러 번 반복해서 사용되는 값에는 짧은 이름을 붙이는 것이 유리합니다.
  • 자기명세성이 약한 이름을 짓는 경우, 그 이름이 가리키는 대상의 구현은 최대한 간결해야 합니다. 이름만으로 이해하지 못하고 코드를 봐야 하니까요.

피할 수 있다면, 피하세요

  • 좋은 이름을 짓기는 어렵습니다. 그러므로 이름이 없어도 되는 경우에 억지로 이름을 붙이지 마세요.
  • 데이터를 일련의 과정에 따라 변환하는 경우 중간 과정의 데이터들 a - b - c - d - e 모두에 이름을 붙이기보다는, 처음과 마지막의 a - e 에만 이름을 붙이는 것이 낫습니다.
  • 어떤 함수의 이름이 그 구현보다 자기명세성이 약하다면, 그 함수에는 이름을 붙이지 마세요. 익명 함수를 사용하면 됩니다. 이는 함수가 비교적 크고 복잡해도 마찬가지입니다. 큰 것이 반드시 나쁜 것을 의미하는 것은 아닙니다.
  • 함수가 너무 커졌는데도 함수를 조각낼 부분 이름들을 떠올리지 못했다면, 그대로 두세요. 나중에 좋은 이름이 생각나면 그 때 분리하면 됩니다. 억지로 떼어내서 붙여 둔 이름이 좋은 간접화 계층을 생성하지 못한다면, 결국 구현체를 봐야 하므로 의미가 없겠지요?

함수를 이름공간으로 묶는 경우

  • 한 이름공간에는 공통된 목적에 한정된 함수들을 담는 것이 좋습니다. 그러면 이름의 의미가 좁혀집니다.
  • 일반적으로 다루는 데이터 유형이나 데이터 스코프가 동일한 함수들을 같은 이름공간으로 묶게 됩니다.
  • 하지만 이름공간이 너무 많아지면 코드를 읽는 비용이 커집니다. 예를 들어 데이터베이스의 테이블 개수만큼 이름공간을 만든다면 비효율적일 가능성이 높습니다.
  • 이름공간은 꼭 필요할 때만 추가하는 것이 좋습니다. "새로운 데이터 유형 / 데이터 스코프를 위한 이름공간을 추가함으로써, 전체 코드가 간결해지는가?" 하고 자문해 보세요.

요약

이름이 중요한 이유

이름은 간접화 계층을 형성합니다. 덕분에 프로그래머는 코드 전체를 동시에 이해하지 않고도 프로그램을 만들 수 있습니다. 이름은 프로그램의 추상성을 높이는 중요한 역할을 합니다.

프레게의 의미이론

두 기호가 같은 지시체를 가리키더라도 의미가 다르다면 기호의 뜻도 다릅니다.

코드에서의 의미이론

이름은 지시체(구현)가 아니라 의미(목적)를 기준으로 지어야 합니다. 그래야 적절한 간접화 계층이 형성됩니다. 의미를 드러내는 이름을 지을 수 없다면, 이름을 붙여야하는 대상이 아닐 수도 있습니다.

협의성

이름이 너무 일반적이면 의미가 모호하며 과도한 변경을 허용하게 됩니다. 이름이 너무 구체적이면 구현이 인터페이스에 노출되며 변경이 어려워집니다. 그 사이에서 강조하고자 하는 대상의 특성에 맞춰 의미를 좁힌 이름이 좋습니다.

일관성

이름의 협의성은 문맥에 의해서도 형성됩니다. 하지만 어떤 이름의 의미가 문맥에 따라 달라진다면 이름의 의미를 이해하거나 커뮤니케이션 하는 데 더 많은 비용이 듭니다. 어떤 문맥에서든 동일한 의미를 갖는 이름이 더 좋습니다.

의도적 비일관성

이름의 의미는 문맥 속에서 파악되므로, 하나의 이름을 다양한 문맥에서 다르게 정의하여 재사용할 수도 있습니다. 하지만 일관성이 낮아짐에 따르는 비용을 감수해야 합니다.

자연 이름과 인공 이름

자연 이름과 인공 이름은 서로 반대의 특성을 갖습니다. 이름의 협의성과 일관성을 높이기 위해 인공 이름을 고려해볼 수 있지만, 자연 이름과 인공 이름의 장단점을 따져서 적절한 이름을 골라야 하겠습니다.

마치며

**'좋은 이름 짓는 방법'**은 프로그래머들의 성배입니다. <클로저 기본원리>에서 명확한 답을 찾을 수 있는 것은 아니고, 이견도 얼마든지 있을 수 있습니다. 이 글에서 소개한 요소들을 모두 고려하더라도 이름을 짓는 일은 여전히 어려울 겁니다. 다만 이름이 왜 중요한지를 설명할 수 있다는 것과, 어떤 이름이 좋은지 나쁜지 헷갈릴 때 참고할 기준을 갖추는 건 도움이 될 것 같습니다. 이 글에서는 클로저에 국한되지 않는 일반적인 작명 기준을 주로 설명했습니다. <클로저 기본원리>에는 클로저에서 이름을 지을 때 참고할 수 있는 내용이 더 많이 실려 있으니 한번 살펴보시기 바랍니다.

참고 자료


추천 콘텐츠