Unity 검색

How to create your own C# code style, hero image
How to create your own C# code style, hero image

Unity C# 코드 형식을 지정하는 방법은 다양합니다. 프로젝트에서 일관된 코드 스타일을 사용한다면 더 깔끔하고 알아보기 쉽고, 확장할 수 있는 코드 베이스를 개발할 수 있습니다. 이번 포스팅에서는 자신만의 코드 스타일 가이드를 만들고 유지하는 데 활용할 여러 가이드라인과 예시를 소개합니다.

본 글은 Microsoft에서 제공하는 내용을 바탕으로 작성된 권장 사항입니다. 팀에 가장 잘 맞는 방식이 무엇인지 살펴보고 아이디어를 얻는 기회로 활용하시기 바랍니다.

유니티의 C# 스타일 가이드 활용

Unity 프로젝트는 개발에 참여한 실제 개발자의 수와 상관없이 한 명이 개발한 것처럼 보이는 것이 가장 이상적이라 할 수 있습니다. 스타일 가이드를 사용하면 보다 일관된 코드 베이스를 제작하기 위한 방식을 통합하는 데 도움이 됩니다.

업계 표준을 우선적으로 따르는 것이 좋으며, 자체적인 가이드를 만드는 경우 기존 스타일 가이드를 시작점으로 삼는 것이 좋습니다. 유니티는 내부 및 외부 Unity 전문가와 함께 Microsoft의 포괄적인 C# 스타일을 기반으로 영감을 줄 수 있는 새로운 전자책인 C# 스타일 가이드 만들기: 확장 가능한 깔끔한 코드 작성을 출간했습니다.

Google C# 스타일 가이드도 명명이나 형식, 주석에 관한 규칙을 정의할 때 활용할 수 있는 훌륭한 리소스입니다. 절대적으로 옳거나 틀린 규칙은 없습니다. 다만 유니티 가이드에서는 Microsoft 표준을 따릅니다.

예시 C# 파일이 포함된 유니티의 전자책은 무료로 다운로드할 수 있습니다. 두 리소스 모두 Unity에서 개발하며 접하게 되는 가장 일반적인 코딩 규칙을 중점적으로 다룹니다. 모두 본질적으로 Microsoft 프레임워크 디자인 가이드라인의 일부에 해당되며, 전체 가이드라인에는 훨씬 많은 베스트 프랙티스가 포함되어 있습니다.

팀에 적합한 스타일 선택

유니티 스타일 가이드의 가이드라인을 팀에 적합한 방식으로 커스터마이즈하는 것이 좋습니다. 규칙이 충돌한다면 유니티의 권장 사항과 Microsoft 프레임워크 디자인 가이드라인보다 팀의 선호도를 따릅니다.

스타일 가이드를 개발하려면 초기 투자가 필요하지만, 나중에는 결국 더 많은 비용을 절약할 수 있습니다. 예를 들어 하나의 표준을 세워두면 개발자가 다른 프로젝트로 이동할 때 소요되는 시간을 줄일 수 있습니다.

물론 핵심은 일관성입니다. 이러한 권장 사항을 따르면 나중에 스타일 가이드를 수정해야 할 때도 몇 가지만 찾아서 변경하면 빠르게 코드 베이스를 마이그레이션할 수 있습니다.

C# 코드 스타일 가이드에 포함해야 하는 내용

일상적인 사용 사례의 대부분을 포함하여 수요에 맞는 실용적인 스타일 가이드를 만드는 데 집중하세요. 처음부터 모든 극단적인 사례에 대응하려 하면서 가이드를 너무 복잡하게 만들지는 마세요. 팀이 여러 프로젝트에 반복적으로 적용하는 과정을 통해 가이드는 유기적으로 발전하게 됩니다. 

대부분의 스타일 가이드에는 기본 형식 지정 규칙이 있습니다. 반면 구체적인 명명 규칙이나 네임스페이스 사용 법칙, 클래스 관련 전략 등은 시간이 지남에 따라 점차 개선될 수 있는 조금은 추상적인 영역입니다.

스타일 가이드에 사용할 수 있는 몇 가지 일반적인 형식 및 명명 규칙을 살펴보겠습니다.

형식 지정 규칙

C#에는 두 가지 일반적인 들여쓰기 스타일이 있습니다. 하나는 여는 중괄호를 새 라인에 배치하는 Allman 스타일(BSD Unix의 BSD 스타일이라고도 함)이고 다른 하나는 여는 중괄호를 이전 헤더와 같은 라인에 배치하는 K&R 스타일 또는 'OTB(One True Brace) 스타일'입니다.

가독성을 높이기 위해, 유니티 가이드에서는 Microsoft 프레임워크 디자인 가이드라인에 따라 Allman 스타일을 사용했습니다.

// EXAMPLE: Allman or BSD style puts opening brace on a new line.

void DisplayMouseCursor(bool showMouse) 
{
     if (!showMouse)
     {
          Cursor.lockState = CursorLockMode.Locked;
          Cursor.visible = false;
     }
}

// EXAMPLE: K&R style puts opening brace on the previous line.

void DisplayMouseCursor(bool showMouse){
     if (!showMouse) {
          Cursor.lockState = CursorLockMode.Locked;
          Cursor.visible = false;
     }
}

어떤 스타일을 사용하든 팀의 모든 프로그래머가 해당 스타일을 따라야 합니다.

가이드에는 중첩된 여러 라인의 구문에서 중괄호를 포함할 것인지에 대한 내용도 있어야 합니다. 다음 예에서 중괄호를 없애도 오류가 발생하지는 않지만, 코드를 읽는 중 혼란을 야기할 수 있습니다. 그래서 유니티 가이드는 선택 사항일지라도 더 명확한 표현을 위해 중괄호를 적용하도록 권장합니다.

// EXAMPLE: Keep braces for clarity.

for (int i = 0; i < 10; i++)
{
	for (int j = 0; j < 10; j++)
     {
		ExampleAction();
     }
}
// AVOID: Removing braces from nested multiline statements

for (int i = 0; i < 10; i++)
	for (int j = 0; j < 10; j++)
		ExampleAction();

가독성 개선

띄어쓰기 등의 간단한 요소로도 화면에서 코드의 가독성을 높일 수 있습니다. 선호하는 형식은 다양할 수 있지만, 전반적인 가독성 개선을 위해 스타일 가이드에서는 다음과 같은 방법을 권장합니다.

  • 공백을 추가하여 코드 밀도 감소: 추가 공백을 사용하면 라인의 요소들을 시각적으로 분리하는 느낌을 줄 수 있습니다.
// EXAMPLE: Add spaces to make lines easier to read.
for (int i = 0; i < 100; i++) { DoSomething(i); }

// AVOID: No spaces
for(int i=0;i<100;i++){DoSomething(i);}
  • 함수 인수 사이의 쉼표 다음에 하나의 공백을 사용합니다.
// EXAMPLE: Single space after comma between arguments
CollectItem(myObject, 0, 1);

// AVOID:
CollectItem(myObject,0,1);
  • 괄호와 함수 인수 뒤에는 공백을 추가하지 않습니다.
// EXAMPLE: No space after the parenthesis and function arguments 
DropPowerUp(myPrefab, 0, 1);

// AVOID:
DropPowerUp( myPrefab, 0, 1 );
  • 함수 이름과 괄호 사이에 공백을 사용하지 않습니다.
// EXAMPLE: Omit spaces between a function name and parenthesis.
DoSomething()

// AVOID:
DoSomething ()
  • 대괄호 안에 공백을 사용하지 않습니다.
// EXAMPLE: Omit spaces inside brackets.
x = dataArray[index];

// AVOID:
x = dataArray[ index ];
  • 흐름 제어 조건 앞에 하나의 공백 사용: 비교 연산자와 괄호 사이에 공백을 추가합니다.
// EXAMPLE: Space before condition; separate parentheses with a space
while (x == y)

// AVOID:
while(x==y)
  • 비교 연산자 앞뒤로 하나의 공백을 사용합니다.
// EXAMPLE: Space before condition; separate parentheses with a space
if (x == y)

// AVOID:
if (x==y)

명명 규칙

변수는 일반적으로 상태를 나타내므로 이름에 명확하고 구체적인 명사를 사용하는 것이 좋습니다. 그런 다음 true 또는 false 값을 나타내야 하는 변수에 동사를 부울(bool) 접두사로 사용하면 됩니다. 부울 접두사가 붙은 변수는 플레이어가 달리는 중인지, 또는 게임이 끝났는지 등의 질문에 대한 대답 역할을 수행하는 경우가 많습니다. 의미를 명확하게 전달하려면 동사를 사용하여 접두사를 붙이는 것이 좋습니다. 접두사는 설명이나 조건과 함께 isPlayerDead, isWalking, hasDamageMultiplier 등과 같이 사용되는 경우가 많습니다.

메서드는 작업을 수행하기 때문에 이름을 동사로 시작하고 반환 유형을 기반으로 필요에 따라 컨텍스트를 추가하여 GetDirection, FindTarget 등과 같이 사용하는 것이 가장 좋습니다. 메서드의 반환 유형이 부울이라면 질문 형식으로 표시할 수도 있습니다.

부울 변수와 마찬가지로 true-false 조건을 반환하는 경우 메서드에 동사 접두사를 사용합니다. 그렇게 하면 IsGameOver, HasStartedTurn 등의 질문 형식이 됩니다.

이벤트와 이벤트 핸들의 명명에도 몇 가지 관습적인 규칙이 있습니다. 유니티의 스타일 가이드에서는 이벤트의 이름을 메서드와 비슷하게 동사 구문으로 했습니다. 상태 변경을 정확하게 전달할 수 있는 이름을 사용하는 것이 좋습니다. 현재분사나 과거분사를 사용하여 '이전' 또는 '이후' 이벤트를 나타냅니다. 예를 들어 문을 열기 전의 이벤트를 OpeningDoor라고 하고 문을 연 이후의 이벤트에 DoorOpened라는 이름을 사용합니다.

또한 축약된 이름을 사용하지 않는 편이 좋습니다. 단기적으로는 글자 수를 몇 개 줄여 생산성을 높일 수 있다고 생각할 수 있지만, 지금 본인에게는 명확한 이름이 나중에는 다른 팀원에게 모호하게 보일 수 있습니다. 변수 이름은 분명한 의미를 전달하고 발음이 쉬워야 합니다. 루프와 수식에는 한 글자 변수를 사용해도 괜찮을 수 있지만, 그 외에는 축약어를 사용하지 마시지 바랍니다. 몇 개의 모음을 생략해 시간을 절약하는 것보다는 명확성이 더 중요합니다.

또한 각 행마다 하나의 변수만 선언하세요. 행 수는 늘어나겠지만 오류를 줄이고 가독성을 높일 수 있습니다. 중복된 단어를 여러 번 사용하지 않아야 합니다. 클래스의 이름이 Player라면, 멤버 변수의 이름을 PlayerScore나 PlayerTarget으로 지정할 필요가 없습니다. Score나 Target으로 줄이면 됩니다.

지나치게 많은 접두사나 특수 인코딩도 피하는 것이 좋습니다. 가이드에서 소개한 예시에서 private 멤버 변수는 밑줄(_)을 접두사로 사용하여 로컬 변수와 구분합니다. 일부 스타일 가이드에서는 private 멤버 변수(m_), 상수(k_), 정적 변수(s_) 등의 접두사를 사용하여, 변수에 대한 정보를 더 많이 드러내도록 이름을 지정합니다.

인터페이스 이름에는 대문자 'I'를 접두사로 사용하고 그 뒤에 기능을 설명하는 형용사를 붙이는 것이 좋습니다. 이벤트 발생 메서드(주체)에 'On' 접두사를 사용할 수도 있습니다. 이벤트를 발생시키는 주체는 일반적으로 'On' 접두사가 있는 메서드(예: OnOpeningDoor, OnDoorOpened)에서 이벤트를 호출합니다.

// EXAMPLE: Prefix interface names with a capital “I”
public interface IDamageable<T>
{
    void Damage(T damageTaken);
}


// EXAMPLE: Raising an event if you have subscribers
public void OnDoorOpened()
{
    DoorOpened?.Invoke();
}

대소문자 사용

카멜 표기법과 파스칼 표기법은 스네이크 표기법, 케밥 표기법, 헝가리언 표기법에 비해 일반적으로 사용되는 표준 표기법입니다. 유니티 가이드에서는 일반적인 Unity 사용 사례에 따라 public 필드, 열거형, 클래스, 메서드에 파스칼 표기법을, private 변수에는 카멜 표기법을 사용하도록 권장하고 있습니다.

모든 사항을 형식화하지 마세요

이 글에서 다루는 내용 외에도 고려해야 할 규칙이 많습니다. 예시 가이드와 유니티의 새로운 전자책, C# 스타일 가이드 만들기: 확장 가능한 깔끔한 코드 작성에서 더 나은 구성을 위한 팁을 확인하실 수 있습니다.

정해진 프로덕션 표준을 준수하여 깔끔한 코드를 사용하면 개발의 확장성을 높일 수 있습니다. 스타일 가이드는 개발자가 따라야 할 규칙에 관해 추측하는 일이 없도록 만들어야 합니다. 가이드는 궁극적으로 팀이 코드 베이스에 대해 합의를 이루고 프로젝트를 상업적 규모의 프로덕션으로 성장시키는 데 도움이 됩니다.

스타일 가이드가 얼마나 많은 내용을 포괄적으로 다뤄야 하는지는 상황에 따라 다릅니다. 가이드에 보다 추상적이고 모호할 수 있는 개념에 대한 규칙을 포함할지 여부는 팀의 결정에 달려 있습니다. 네임스페이스 사용, 클래스 세분화, #region 지시문과 같은 지시문 구현 등이 이러한 예에 포함될 수 있습니다. 예를 들어 #region을 사용하면 C# 파일에서 코드 섹션을 축소하고 숨길 수 있으므로 대용량 파일을 더 쉽게 관리할 수 있지만, 많은 개발자는 이를 코드 스멜 또는 안티패턴으로 간주하기도 합니다. 따라서 코드 스타일을 지정할 때 이러한 부분에 지나치게 엄격한 표준을 설정하지 않는 편이 좋을 수 있습니다. 가이드에서 모든 내용을 설명할 필요는 없습니다. 간단히 토론을 통해 팀의 결정으로 정할 수 있는 사항도 있습니다.

명확한 가이드 제작

유니티 가이드 제작에 도움을 준 전문가들은 무엇보다도 가독성이 가장 중요하다고 조언했습니다. 다음은 가독성을 높이는 몇 가지 방법입니다.

  • 인수를 적게 사용: 인수는 메서드의 복잡도를 높일 수 있습니다. 인수의 수를 줄이면 더 쉽게 읽고 테스트할 수 있는 메서드를 만들 수 있습니다.
  • 과도한 오버로드 지양: 메서드 오버로드는 무한대로 만들 수 있습니다. 메서드 호출 방법을 반영하는 몇 가지만 골라서 선택적으로 구현해야 합니다. 메서드를 오버로드하면 각 메서드 서명에 고유한 수의 인수가 있는지 확인하여 혼동을 방지해야 합니다.
  • 부수 효과 방지: 메서드는 해당 이름이 나타내는 역할만 수행하면 됩니다. 해당 범위를 벗어나는 사항은 수정하지 않아야 합니다. 가능하다면 레퍼런스 대신 값을 인수로 전달하는 것이 좋습니다. 따라서 out이나 ref 키워드를 통해 결과를 다시 전송할 때 그것이 메서드가 수행하도록 정해진 작업이 맞는지 확인해야 합니다. 부수 효과는 특정 작업에서 유용한 경우도 있으나, 의도하지 않은 결과를 초래할 수 있습니다. 예기치 않은 동작을 줄이려면 부수 효과가 없는 메서드를 작성하는 게 좋습니다.

유니티 코드 스타일 가이드 및 전자책 다운로드

이번 포스팅이 각자의 스타일 가이드 개발에 도움이 되기를 바랍니다. 예시 C# 파일과 최신 전자책을 살펴보며 유니티가 권장하는 규칙을 검토하고 팀에 맞게 커스터마이즈해 보세요.

개별 규칙의 세부 사항보다 중요한 점은 모든 팀원이 규칙을 일관되게 따르도록 동의하는 것입니다. 확실하지 않은 경우에는 팀에서 자체적으로 개발하는 가이드를 통해 스타일 불일치 요소를 정리하는 것이 좋습니다. 결국은 개인이 아닌 팀 모두의 노력이 필요한 작업입니다.

Create a C# style guide: Write cleaner code that scales e-book
2022년 9월 7일 테크놀로지 | 15 분 소요
관련 게시물