[C#] 코드 스타일 가이드 만들기(코드 가독성, CS 관리, 명명 규칙 등)
by Sugar0810팀에 적합한 스타일 선택
유니티 스타일 가이드의 가이드라인을 팀에 적합한 방식으로 커스터마이즈하는 것이 좋습니다. 규칙이 충돌한다면 유니티의 권장 사항과 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 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 변수에는 카멜 표기법을 사용하도록 권장하고 있습니다.
카멜 표기법(camelCase)
- 첫 문자를 소문자로 표기하고, 그 다음 문자부터는 대문자로 표기 합니다.
이것을 단봉낙타 표기법이라고도 불립니다. (ex. helloWorld)
- Java 프로그래밍에서 권장되는 표기법이라고 합니다.
파스칼 표기법(PascalCase)
- 첫 문자를 대문자로 표기하고, 그 다음 문자도 대문자로 표기하는 표기법입니다.
이것을 쌍봉낙타 표기법이라고도 불립니다. (ex. HelloWorld)
- 함수와 클래스명은 이 표기법을 권장한다고 합니다.
스네이크 표기법(snake_case)
- 한 문자마다 _(underscore)를 붙여 이어나가는 표기법을 말합니다. (ex. hello_world)
명확한 가이드 제작
유니티 가이드 제작에 도움을 준 전문가들은 무엇보다도 가독성이 가장 중요하다고 조언했습니다. 다음은 가독성을 높이는 몇 가지 방법입니다.
인수를 적게 사용: 인수는 메서드의 복잡도를 높일 수 있습니다. 인수의 수를 줄이면 더 쉽게 읽고 테스트할 수 있는 메서드를 만들 수 있습니다.
과도한 오버로드 지양: 메서드 오버로드는 무한대로 만들 수 있습니다. 메서드 호출 방법을 반영하는 몇 가지만 골라서 선택적으로 구현해야 합니다. 메서드를 오버로드하면 각 메서드 서명에 고유한 수의 인수가 있는지 확인하여 혼동을 방지해야 합니다.
부수(부가) 효과 제한: 메서드는 해당 이름이 나타내는 역할만 수행하면 됩니다. 해당 범위를 벗어나는 사항은 수정하지 않아야 합니다. 가능하다면 레퍼런스 대신 값을 인수로 전달하는 것이 좋습니다. 따라서 out이나 ref 키워드를 통해 결과를 다시 전송할 때 그것이 메서드가 수행하도록 정해진 작업이 맞는지 확인해야 합니다. 부수 효과는 특정 작업에서 유용한 경우도 있으나, 의도하지 않은 결과를 초래할 수 있습니다. 예기치 않은 동작을 줄이려면 부수 효과가 없는 메서드를 작성하는 게 좋습니다.
🌙 참고 자료
'⚙️ Programming > C# & Unity' 카테고리의 다른 글
[Unity] TimeScale을 이용한 일시정지 기능 만들기 (0) | 2023.07.05 |
---|---|
[Unity] 개발 기초, 좋은 팁 모음 (0) | 2023.07.05 |
[GUI] EnhancedScroller - 풀링 최적화 무한 스크롤 (0) | 2023.02.15 |
[Unity] Package Manager 커스텀 패키지에 샘플 추가하기 (0) | 2023.02.01 |
[Unity] 서브 모듈화를 통한 라이브러리 관리 - 3 Assembly definition file 코드 외부 참조하여 모듈화하기 (0) | 2023.01.27 |
블로그의 정보
Sugar
Sugar0810