LINE Corporation이 2023년 10월 1일부로 LY Corporation이 되었습니다. LY Corporation의 새로운 기술 블로그를 소개합니다. LY Corporation Tech Blog

Blog


코드 가독성에 대해 - 2. 명명과 주석

들어가며

안녕하세요. 커뮤니케이션 앱 LINE의 Android 클라이언트 팀 Ishikawa입니다. 이 글은 코드 가독성에 대한 프레젠테이션을 소개하는 비정기 연재 블로그의 두 번째 편입니다(첫 번째 편을 먼저 읽고 오시면 좋습니다). 이번 글에서는 프로그램 내에 작성하는 자연어에 관한 내용인 2장 '명명'과 3장 '주석'에 대해 설명하겠습니다.

2장: 명명 

프로그램을 작성할 때는 클래스나 리소스 등의 다양한 대상에 이름을 붙여야 합니다. 그 이름이 정확하고 명확하며 잘 기술되어 있을수록 코드가 더 잘 읽힙니다. 2장에서는 어떤 이름이 코드 가독성을 높여 주는지에 대해서, 특히 유형(클래스, 인터페이스 등)과 값(변수, 필드, 파라미터 등), 절차(함수, 메서드, 서브루틴 등)에 초점을 맞춰 해설하겠습니다. 단, 여기에서 설명하는 내용은 어디까지나 일반론이니, 사용하는 언어, 플랫폼, 프로젝트에 따라 명명 규칙이 정해져 있을 경우 그 규칙을 우선하기 바랍니다.

2장에서는 이름을 붙일 때 아래의 세 가지를 중요한 사항으로 꼽았습니다.

  1. 이름이 나타내는 내용
  2. 문법
  3. 단어 선택

1. 이름이 나타내는 내용

이름은 명명의 대상(유형, 값, 절차)이 '어떤 것인지, 어떤 일을 하는지'를 나타내야 합니다. 바꿔 말하면 그 대상이 언제, 어디에, 어떻게 사용되는지, 또 누가 사용하는지를 이름에서 나타낼 필요는 없는 셈입니다. 예를 들어, '새로운 메시지 데이터를 받았을 때 그 내용을 표시한다'라는 절차를 작성할 때는 '새로운 메시지를 받았을 때'보다 '내용을 표시한다'에 주목하면 됩니다. 즉, 이 경우에는 onNewMessage보다 showMessageText라고 명명하는 것이 바람직합니다. 

이름이 나타내는 내용이 '어떤 것인지, 어떤 일을 하는지'일 때 얻을 수 있는 이점 중 하나는 그 대상의 책임이 명확해진다는 것입니다. showMessageText라고 명명했을 경우, 인수로 주어진 메시지 데이터를 '표시하기(show)'가 아닌 다른 목적으로 사용해서는 안 된다는 뜻이 됩니다. 하지만 onNewMessage라고 명명하면, 그 메시지 데이터를 다른 목적으로 사용해도 아무도 이상하게 생각하지 않을 겁니다. 책임의 경계선이 모호해지면 중복 조작을 유발하는 버그의 원인이 되거나 의존 관계가 지나치게 복잡해지는 원인이 될 수 있습니다.

하지만 이 원칙에도 예외가 있습니다. 콜백으로 정의된 추상 메서드 같은 경우엔 선언되는 시점에는 어떤 동작을 수행할지 정해지지 않을 수 있습니다. 그럴 때는 onNewMessage처럼 '언제, 어디에서 호출되는지'에 대한 정보를 사용해서 명명합니다. 단, 추상 메서드라도 목적이 확실하다면 '어떤 일을 하는지'로 명명합니다.

2. 문법

이름은 단어를 그냥 나열해서 구성되는 것이 아니라, 영문법에 가까운 규칙에 따라 어순과 어형(語形)이 결정됩니다. 대부분의 경우 값이나 유형에는 명사나 명사구를 사용하고, 절차에는 명령문을 사용합니다. 예를 들어, '수신한 메시지의 텍스트'라는 값은 receivedMessageText라는 명사구로 나타낼 수 있고, '수신한 메시지를 저장한다'라는 절차는 saveReceivedMessage라는 명령문으로 표현할 수 있습니다. 

그 외에도 프로그래밍 언어에 따라 형용사 및 형용사구, 3인칭 시점의 동사 및 조동사 또는 이를 사용한 의문문, 전치사를 동반한 부사구를 사용하는 경우도 있습니다. 예를 들어, 형용사나 형용사구는 성질이나 상태를 표현하는 유형이나 값(예: IterableFINISHED)에, 3인칭 시점의 동사 및 조동사 또는 이를 사용한 의문문은 진리값을 나타내는 값이나 절차(예: containsisTextVisible)에, 전치사를 동반하는 부사구는 유형 변환이나 콜백의 절차(예: toIntonFinished)에 각각 사용합니다.

문법에 맞춰 명명하지 않으면 이름 때문에 오해가 생길 수 있습니다. 클릭 이벤트의 리스너(listener)라면 ClickEventListener라고 명명해야 하는데요. 그렇지 않고 ListnerClickEvent라고 이름을 붙여 버리면 'Listener라는 UI에서 발생한 클릭 이벤트' 혹은 'Listener가 발생시킨 클릭 이벤트'라고 해석될 수 있습니다. 이런 오해를 막기 위해선 명사구의 마지막 단어는 가장 중요한 단어여야 하며, 명령문의 첫 단어는 그 절차를 나타내는 동사여야 합니다.

3. 단어 선택

이름에 사용할 단어를 선택할 때는 '뜻이 모호하지 않은 단어 사용하기', '약어 사용 자제하기', '단위 명시하기', '긍정적인 단어 고르기'가 중요합니다. 여기에서는 '뜻이 모호하지 않은 단어 사용하기'와 '약어 사용 자제하기' 이 두 가지 사항에 대해 해설하겠습니다.

3-A: 뜻이 모호하지 않은 단어 사용하기

이름을 구성하는 단어를 고를 때는 정보량이 더 많은 단어를 선택합니다. 예를 들어, checkMessage라는 이름으로는 메시지의 존재 여부를 알고 싶은 것인지, 형식이 올바른지를 알고 싶은 것인지, 서버에 새로운 메시지가 있는지 문의하고 싶은 것인지, 아니면 특정 조건을 충족한 메시지를 필터링하고 싶은 것인지 알 수 없습니다. 각각 existsMessageisMessageFormatValidqueryNewMessage…takeMessageIf… 등으로 명명해야 합니다. 정보량이 더 많은 단어를 찾을 때는 사전, 특히 유의어 사전을 활용하면 좋습니다.

3-B: 약어 사용 자제하기

이름에 약어를 사용하면 코드를 읽는 부담이 커지는 경우가 많습니다. 약어의 의미를 파악하기 위해선 인지하는 능력보다 기억하는 능력이 필요한 경우가 많고, 무언가를 떠올리는 행위는 사고에 큰 부담을 주기 때문입니다. 즉, im이라는 이름을 보고 무엇을 정의한 것인지 떠올리는 것보다 instanceManager라는 이름을 통해 개요를 이해하는 것이 더 쉽다는 뜻입니다. 물론, 널리 알려진 약어를 사용하는 것은 문제가 되지 않습니다. 예를 들어, TCP나 URL 등은 오히려 원래 이름이 생각나지 않는 사람들이 더 많을 겁니다. 또, string을 str로 줄이는 것과 같이 의미가 명백한 약어는 한정된 범위 내에선 사용해도 무방합니다. 한편, 특정 프로젝트에서만 사용하는 약어를 프로젝트 내에서 정의한 경우, 그 약어를 사용해서 명명해야 하는 경우가 있습니다. 이런 경우엔 새로 프로젝트에 들어온 사람도 그 약어를 이해할 수 있도록 주석으로 해설하거나 별도 용어집을 준비해 놓으면 좋습니다.

3장: 주석

복잡한 코드나 규모가 큰 코드, 직관적으로 이해하기 힘든 코드에는 주석을 달아서 읽는 사람의 이해를 도울 수 있습니다. 또, 주석을 다는 과정에서 코드의 가독성을 더 높이기 위한 아이디어가 떠오를 수도 있습니다. 반대로 말하면, 코딩 규칙에 따라 다르겠지만 충분히 이해하기 쉬운 코드는 주석을 생략해도 무방합니다.

3장에서는 어떤 코드에 어떤 주석을 적어야 하는지 설명하겠습니다. 주석은 특정 도구를 위한 특수한 주석을 제외하면 문서화 주석과 그 외의 주석으로 분류할 수 있습니다. 이번 글에서는 '그 외의 주석'을 '인라인 주석'이라고 부르겠습니다(블록 주석이나 멀티라인 주석을 명시적으로 인라인 주석과 다르게 정의하는 경우도 있지만, 단순화하기 위해 무시하겠습니다).

1. 문서화 주석

문서화 주석은 유형, 값, 절차 등의 선언이나 정의에 다는 주석으로, 일정한 형식에 맞춰 작성한 주석을 말합니다. 문서화 주석을 읽으면 상세한 코드나 참조 대상을 확인할 필요 없이 사양을 이해할 수 있습니다. 문서화 주석은 요약이 필수로 들어가며, 필요하다면 상세 설명문이나 @return과 같은 태그를 부여합니다.

1-A: 요약

문서화 주석의 첫 부분에는 대상이 '어떤 것인지, 어떤 일을 하는지'를 간략하게 설명한 요약을 기입합니다. 코딩 규칙에 따라 요약은 완전한 문장이 아닌 구나 생략된 문장을 사용해서 작성합니다. 예를 들어, 유형이나 값의 요약에는 명사구를, 절차의 요약에는 3인칭 주어를 생략한 문장을 사용하기도 합니다. 만약 요약의 형식이 코딩 규칙에 정의되어 있지 않을 경우, 그 언어의 표준 라이브러리의 형식을 따르면 됩니다. 여담이지만 이 블로그 글 자체도 각 단락의 첫 문장에 핵심 정보가 배치되도록 신경 써서 작성하고 있습니다.

1-B: 상세

요약에서 다 설명하지 못한 사항이 있을 때는 더 자세한 글을 추가로 작성해서 설명합니다. 예를 들어, 사양에 대한 보충 설명이나 전형적인 사용 예, 반환값, 제약이나 예외를 추가하는 경우가 있습니다. 가령 반환값과 부작용을 둘 다 가진 함수가 있다고 해 봅시다. 이럴 때는 함수의 이름과 문서화 주석의 요약에서 부작용 부분에 초점을 맞춰 설명하는 경우가 많습니다. 만약 반환값이 진리값이라면 어떤 경우에 그 값이 참이 되는지 알기 힘듭니다. 이런 경우에는 반환값을 설명하는 문장을 추가해서 코드의 사양을 이해하기 쉽게 만들 수 있습니다.

2. 인라인 주석

인라인 주석은 그 주변 코드를 요약하거나 보충 설명을 하기 위한 주석으로, 코드를 읽을 때 보조하는 역할을 합니다. 문서화 주석과는 달리 요약은 필수가 아닙니다. 이 주석은 규모가 큰 코드를 분할하거나 직관적이지 않은 코드를 설명하는 등의 경우에 작성합니다.

2-A: 규모가 큰 코드 분할하기

절차의 규모가 비교적 큰 경우, 공백 행과 인라인 주석으로 코드 덩어리를 만들면 절차의 흐름을 위에서 아래로 이해하기 쉬워집니다. 이 주석의 내용은 코드 덩어리의 개요, 즉 그 코드 덩어리가 '어떤 것인지, 어떤 일을 하는지'를 표현하는 것이 좋습니다.

2-B: 직관적이지 않은 코드 설명하기

라이브러리의 버그를 회피하는 코드와 같이 언뜻 봤을 때 존재 이유를 알 수 없는 코드가 있는 경우, 그 코드가 필요한 이유를 적어 두면 이해하기 쉬워집니다. 이 주석을 작성하는 기준은 '나중에 누군가가 잘못된 수정이나 리팩터링을 할 가능성이 있는지'로 판단하면 됩니다.

마치며

이번 글은 '명명과 주석' 편으로 프로그램 내에 작성하는 자연어에 대해 소개했습니다. 다음 편은 '상태와 절차' 편으로 4장과 5장의 내용을 소개하겠습니다.