본문 바로가기

유니티 개발 정보/개념

유니티 코딩 규칙

해당 글은 http://unitygems.com/coding-conventions/을 번역한 글입니다.

잘못 번역된 부분이 있을 겁니다. 이부분은 댓글에 적어주시면 보는대로 바로 수정하겠습니다. 감사합니다.

---------------------------------------------------------------------------------------------------------------------------------

How you write code can make it legible and easy to share
or impenetrable and a great alternative to cross word puzzles
if you like a challenge and need to debug your code 3 months
after writing it. Nothing's perfect, here's one interpretation of best practices

어떻게 코드를 작성하는 것이 가독성이 좋고, 손쉽게 공유하거나 이해하기 쉽고, 3개월 후에 자신의 코드를 
디버깅하는데 도움이 되는가..
완벽한 것은 없다. 이 글에는 모범 사례 중 하나를 설명한다.

Motivation
동기

You should read this article if you are new to programming or new to the C#, Unity Script 
and Unity environments and want to know about coding 
structure and variable naming conventions.  You will learn (/hear me rant about)

만약 프로그래밍 또는 , C#, Script, Unity 환경에 처음이거나, 코딩 구조와 변수 명명 규칙에 대해 알고 
싶어한다면 이 글을 반드시 읽어야 한다. 당신은 앞으로
  • Why we have variable naming conventions and why they matter
    왜 우리는 다양한 변수 명명 규칙을 가지는지와 왜 그것들이 문제가 되는지에 대해
  • How and where to declare variables
    어떻게 그리고 어디에 변수를 선언하는지에 대해
  • Keeping your code DRY
    코드 중복을 방지하는 것에 대해서
배우게 될 것이다.

There's really just one lesson here - write code that does what it says so that you and 
everyone else can just understand it by reading it.
하나의 교훈이 존재한다. - 코드가 의미하는 것을 작동하는, 그리고 읽을 수 있는 코드를 작성하라.

Warning: this is like discussing religion or politics
경고 : 이것은 종교나 정치에 대해서 논쟁하는 것과 같다.

There are very different opinions on naming conventions.  This article represents the .NET standard 
(with Unity modifications and a few personal opinions).  About the only wrong thing in naming conventions is not to have one, to have one that's half baked, or to have one that is opposite to a commonly practiced convention.

명명 규칙에 대한 많은 의견이 존재한다. 이 글은 .NET 표준(유니티에 맞는 수정부분과 몇몇의 의견을 포함한)을 나타낸다. 명명 규칙에 대한 잘못된 생각중 한가지는 1가지 규칙을 고집하는 것이 아니라, 
섣부르거나 불 충분한 의견을 가지거나, 일반적인 관례 규칙에 대한 반대의 의견을 가지는 것이다.


NamingConventions
명명 규칙


It is very tempting when you start to program to just call your classes, your variables, your properties and your methods by whatever name you like and using any combination of capital letters, underscores and other paraphernalia that comes to mind.  However naming conventions exist for a reason - and it isn't us pedants just wanting to dictate how things are done.  Naming conventions do this for you:

프로그램을 작성할 때 클래스, 변수, 속성 그리고 메소드를 자신의 취향에 맞는 이름과 대문자의 조합, underbar(_), 그밖에 떠오르는 것들로 표현하고 싶은 유혹이 매우 클 것이다.
그러나 명명 규칙이 존재하는 이유가 있다. 이들은 단순히 규칙을 좋아하는 사람이 코드를 작성하기 위해서 만든 것이 아니다. 다음과 같은 이유로 명명 규칙이 존재한다.


  • Reduce the number of coding errors you make
    에러를 줄이기 위해서

  • Make your code more readable to someone else who looks at it
    당신의 코드를 보는 사람들이 좀 더 읽기 쉽게 하기 위해서

  • Make your code make more sense to you when you come back to it after a month or two
    작성 후 한 두달후에 다시 자신의 코드를 보았을 때 좀 더 이해가 잘 되기 위해서

  • Reduce the need to create masses of documentation by self documenting your code
    자기 코드를 설명하는 문서의 작성을 줄이기 위해서


Identifier Names
식별자 명


Make your identifier names meaningful, even if it makes them long.  If you use a decent editor like Visual Studio or MonoDevelop you will hardly ever type them all anyway - so do yourself a favour and don't write code like this:

변수 명이 좀 길어질지라도, 의미 있게 이름을 지어라.  만약 당신이 Visual Studio나 MonoDevelop같은 최신 에디터를 쓴다면 아마 변수 명의 전부를 입력하지 않아도 될 것이다. (인텔리전스 기능을 의미)
자신을 생각해서 다음과 같은 코드를 작성하지 마라.

1
2
3
4
5
var p = transform.position;
for(var i = 0; i < ar.Length; i++)
{
    p += ar[i];
}

Does this make more sense to you?
이 코드가 더 이해가 쉽지 않은가?

1
2
3
4
5
var endPosition = transform.position;
for(var step = 0; step < pathStepVectors.Length; step++)
{
       endPosition += pathStepVectors[step];
}


When you declare a boolean variable give it an auxilliary verb so that it has more meaning.  For example alive is less useful than isAlive which clearly shows that your are sampling something that has a present value relevant now; activated could imply that something was ever active or that it was active right now while that question would not arise if you called your variable isActivatedor hasActivated.

bool변수를 선언할때는 조동사를 사용하면 더 의미있게 만들 수 있다.
예를 들어 alive보다 isAlive가 더 유용하다. isAlive는 명확하게 어떤 것에 대한 현재의 상태 값을 의미한다는 것을 알 수 있다. Activated는 그것이 과거에 active했었는지, 질문을 했을 때에 active했는지 모호하지만 isActivated 또는 hasActivated로 표현하면 이러한 의문은 필요없을 것이다.




Capitals & Why They Matter
문자들 & 왜 그것이 문제인가


Ok so first here's the rules:
우선 여기에 규칙들이 있다.

  • Class names always start with a Capital letter
    클래스 이름은 반드시 대문자로 시작해야 한다.

  • Therefore, because classes and scripts are closely related in Unity, script names start with a Capital letter
     유니티에서는 클래스와 스크립트는 밀접한 관계이기때문에 스크립트또한 대문자로 시작해야 한다.

  • Public and protected fields of a class start with a lower case letter*
    클래스의 Public 그리고 Protected 변수는 소문자로 시작한다.* (밑에 *부분 참조)

  • Public and protected properties of a class start with a Capital letter, but Unity regularly uses lower case itself, so you can just adopt the rule:Properties and fields start with a lower case letter
    클래스의 Public 그리고 Protected 프로퍼티(속성)은 대문자로 시작한다. 하지만 유니티는 일반적으로 소문자를 사용한다. 그래서 다음과 같은 방식을 선택해야 한다. : 속성과 필드는 소문자로 시작한다.

  • Methods or functions always start with a capital letter
    메소드 or 함수는 항상 대문자로 시작한다.

  • Private properties and fields of a class start with an _
    클래스의 Private 속성과 필드는 _(underbar)로 시작한다.

  • Parameter names start with a lower case letter
    매개변수의 이름은 소문자로 시작한다.

  • All identifiers are written in camelCase - where the first letter of every new word after the first one is a Capital letter.
    모든 변수는 카멜방식(낙타모양방식)으로 작성한다. 첫번째 이후의 단어는 대문자로 시작한다.

* .NET conventions say that any publicly accessible member of a class should start with a Capital letter - be it member, property or field.  Therefore you will often encounter strange things in Unity when integrating with .NET libraries as the convention is different.  I've finally opted for using Unity conventions in my code, but there will sadly be some inconsistency no matter which approach you take.
* .Net 규칙은 클래스에서 pulicly하게 접근 가능한 변수들은 대문자로 시작하라고 말한다.
그것은 멤버, 속성, 필드가 될 수 있다. 그러므로 유니티에서, 규칙이 다른 .NET 라이브러리와 통합하려고 할때 종종 문제가 발생할 것이다. 최종적으로 나는 코드에서 유니티 규칙을 사용하는 것을 선택 했다. 그러나 슬프게도 어떤 것을 선택할지라도 약간의 불일치는 발생할 것이다.

Camel case is great as itReallyMakesReadingIdentifiersFarMoreEasy - thanifyouwrotethemlikethis - 
and it is far_less_cumbersome_than_using_underscores.
Right so straight away I know that:
카멜방식은 보는 것처럼 thanifyouwrotethemlikethis 보다 itReallyMakesReadingIdentifiersFarMoreEasy  더 읽기 쉬우며 _(underbar)를 사용하는 것보다 덜 길고 덜 복잡하다.


  • thisVariable = 5 // does not call any code that has any effect, because it starts with a lower case letter.
    어떤 기능을 가지는 코드를 호출하지 않는다. 그래서 소문자로 시작한다.

  • ThisIdentifier.thisValue // is probably a static variable (though it could be the value of something returned by a property - heh nothing's perfect) - I should be able to recognise ThisIdentifier as one of my classes
    ThisIdentifier.thisValue는  아마 정적 변수일 것이다.
    (물론 프로퍼티에 의해 리턴되는 값일 수도 있지만) 
    그리고 ThisIdentifier가 나의 클래스중 하나인 것을 알 수 있다.

When I write a function that takes parameters I often want to store them in variables in my class - it's good to be able to have private variables start with a _ because then there isn't a naming conflict (and of course, I know that they're private!)
매개변수를 가지는 함수를 작성했을 때 나는 종종 매개변수들을 나의 클래스에 있는 변수에 저장하기를 원한다. _(underbar)로 시작하는 private 변수를 가지면 쉽게 가능하다. 왜냐하면 이름 충돌이 일어나지 않기 때문이다.(물론 그 변수가 private인지도 알 수 있다.)
1
2
3
4
function SetTarget(targetPosition : Vector3)
{
     _targetPosition = targetPosition;
}

Is clearer and more obvious than prefixing the variable targetPosition with this. every time we need to make a distinction - likely to lead to problems that.  In fact it's also usually a good idea to make parameter names more obvious too:

이는 this 접두사를 가지는 변수보다 더 명확하다.  뚜렷한 차이를 만들 때 마다 그것은 문제로 이끌기 쉽다. 사실 보통은 매개변수의 이름을 더 명확하게 만드는 것이 유용하긴 하다.


1
2
3
4
function SetTargetPosition(newTargetPosition : Vector3)
{
     _targetPosition = newTargetPosition;
}


Hungarian Notation
헝가리안 표기법

The standard way to name variables in C++ was always to use Hungarian notation so you can work out what type the variable actually refers to - it's kind of more important in C++, but not much and itlpszMakes szYour m_lpstrCode somewhat hard to read as you have to skip past a whole usBunch of characters to get to the meaning, and in any case do you know if a lpszVariable is actually compatible with an lpstrVariable?  It's not obvious that it is.  That and half the time you can't remember whether bByte is the right notation or is that bBoolean - hang on that's sFloatingPoint for a single isn't it and fBoolean for flag? Don't even get me started on arrays and other higher level collection structures.
C++에서 변수의 이름을 짓는 표준적인 방법은 헝가리안 표기법을 사용하는 것이다. 그래서 변수가 어떤 타입인지 쉽게 알 수 있다. 그것은 C++에서는 좀 중요하다. 하지만 pszMakes, szYour, m_lpstrCode 처럼 약간 읽기 어렵고, 의미를 얻기 위해서는 이런 타입을 가리키는 문자들을 뛰어 넘어야 한다.


It is not recommended to use hungarian notation, nor is the distracting m_inFrontOfAllMembers very clear is it?  Don't use it.  A variable is a variable - plain and simple, a private variable is far more legible as _variable and readability is actually more important than variable types to the meaning of your code.
헝가리안 표기법을 사용하는 것을 추천하지 않는다. 또한 집중을 흐트리는 m_inForntOfAllMember는 매우 명확하지 않다. 그것을 사용하지마라. 변수는 변수이다. 분명하고 간단해야 한다. private 변수는 _(underbar)를 사용하는 것이 훨씬 더 읽기 쉽다. 사실 가독성은 코드에서의 변수타입의 의미보다 더 중요하다.


Where and How To Define Variables?
어디에 그리고 어떻게 변수를 선언해야 하나?

Best practices currently dictate that variables in a function should be defined as close as possible to their usage, being defined as they are initialised is the best choice if that is possible.
가장 좋은 사례는 함수 안에서 가능한 그들이 사용되는데 가장 가까이 정의되어야 하고, 가능한 그들이 정의될 때 초기화 되는 것이 최고의 선택이다.


Also remember that variables in a function have been allocated on the stack and are not initialized with their default value so you should always ensure that they are initialised before they are used or allocate the default value yourself.
함수 안에 변수는 스택에 할당되며, 그들은 기본 값으로 초기화 되지 않기때문에, 변수가 사용되기 전에 
항상 초기화 되어있는지 확인해야 한다.


In C# there is a lot of debate around the use of var to define a variable in a function.  var imputes the type of object from the thing that it is set = to.  So
C#에서는 변수를 정의하는 var의 사용에 대한 많은 논쟁이 있다.
var은 정의 되어있는 값으로 부터 객체의 타입을 결정한다. 

  • var x = 0; //x is an int
  • var x = 0f; //x is a float
  • var x = new Dictionary<string, float>(); // x is a dictionary of string to float
It's this last example that is so compelling to me.  I would of course never call a dictionary x but I think this is far more legible:
마지막 예제는 매우 매력적이다. 물론 나는 이렇게 사용하지 않지만
이렇게 사용하는 것이 좀 더 읽기 쉽다고 생각한다.
1
var lookupWeight = new Dictionary<string, float>();

Than this alternative:

1
Dictionary<string, float> lookupWeight = new Dictionary<string, float>();

It takes me a moment to even find the variable name in there, especially if it's buried in a bunch of other code and definitions.
만약 이게 다른 코드들에 묻혀 있다면, 변수 이름을 찾는데 시간이 걸릴 것이다.

One school of thought is that var should only be used for prototyping and that you aren't a proper programmer if you use it otherwise.
한쪽에서는 var은 오로지 프로토타입에서만 쓰여야 하고, 그렇지 않고 만약 그것을 사용한다면 당신은 올바른 프로그래머가 아니라는 인식이 있다.

The other school, of which I am an evangelist (did you guess?), says that actually being able to read your code quickly is the most important part of the creative process and debugging process.  Any accidental errors, like initialising something you wanted to be a float with an integer, are immediately caught in the compiler anyhow.
나는 반대편의 입장에서, 사실 당신의 코드를 빠르게 읽게하는 것은 창의적인 프로세스와 디버깅 프로세스의 매우 중요한 한부분이다. float로 초기화해야되는 것을 int로 초기화하는 이같은 돌발적인 에러는 즉시 컴파일러에서 잡아준다.

In fact when I'm calling a function to return a variable value then I really don't need to remember what the actual type is that's coming back from it is - so long as I (and intellisense) know what properties, methods or fields it has and whether I can access it with array semantics!
사실 나는 특정 값을 리턴하는 함수를 호출할 때 내가 그 값이 프로퍼티인지 메소드인지 필드인지, 그리고 그것이 배열의 의미로 접근할 수있는지 알고 있다면
나는 그 함수가 어떤 타입의 값을 리턴하는지 기억하는 것을 원하지 않는다. 

1
2
3
var newEnemy = CreateEnemy("Bob", EnemyType.pedanticProgrammer);
newEnemy.EvolveNow();  //Intellisense knew what newEnemy was
                          //I don't need to

This becomes even more important when using Linq or anonymous classes because it can actually be very hard to work out what the return value is, even though you (and again, intellisense) know exactly how to use it.
이것은 Linq나 익명 클래스를 사용할 때 더 중요하다. 당신이 그것을 어떻게 사용하는지 정확하게 알고 있다고 할지라도, 그것은 실제적으로 반환 값이 무엇인지 산출되는 것은 매우 어렵기 때문이다.

(*간단하게 요약하면 var을 사용하는데 찬성하는 입장임, 왜냐하면 타입 불일치의 오류는 컴파일러가 다 잡아 주기 때문에 가독성을 높이는 것이 더 중요하다는 생각을 가지고 있다.)


Where do you put variables in a class?  Well I put the public ones at the top and the private ones close to where they are first defined and used.  I don't do scrolling when I'm concentrating and I like to see the public variable interface of the class closely tied together.  I will also put properties first, just after fields.
당신은 클래스의 어디에 변수를 선언하는가? 나는 public 변수를 맨 위에 두고, private 변수는 그들이 정의되고 사용되는 가장 가까운 곳에 선언한다. 나는 집중할 때 스크롤을 움직이지 않는다. 그리고 클래스의 public 변수가 함께 묶여 있는 것을 좋다. 또 필드값 뒤에 속성을 선언한다

Subclasses and Enums
하위 클래스와 열거형


So you can define helper classes inside other classes or in global scope - if this class is ONLY going to be use in reference to another class or its functions then define it inside - Unity 3.x doesn't support namespaces so anything else will just end up with clutter.  If you define subclasses then they should come at the bottom of your outer class definition.  Remember to use #regions to simplify your file structure.
만약 해당 클래스가 오로지 다른 클래스의 참조로만 사용되어야 하거나, 내부에 정의된 함수에 의해서만 사용이 되어야 한다면, 
당신은 다른 클래스의 내부에 하위클래스를 정의 하거나, 또는 전역 범위에서 클래스를 정의할 수 있다. 

Unity 3.x에서는 네임스페이스를 지원하지 않는다. 그래서 그 밖의 다른 클래스들은 결국에 잡동사니가 될 것이다. 만약 당신이 하위 클래스를 정의한다면 그들은 반드시 당신의 외부 클래스 정의의 밑에 와야 한다.

#region을 사용하면 당신의 소스 구조를 간단하게 만들 수 있다는 것을 기억하라.

1
2
3
4
5
#region Is your friend for logical groups of code
 
//Logical block of code
 
#endregion

The argument for Enums is slightly less clear - simply because you will probably end up typing the name of them when you pass them as function parameters or set field or property values.
Enum 인자는 규칙이 약간 덜 명확하다. 만약 당신이 enum인자를 함수의 매개변수 혹은 필드나 프로퍼티(속성)값을 정의하는데 쓰인다면 당신은 아마 결국에 그들의 이름을 타이핑할 것이기 때문이다.

1
var easedPosition = Easing.EaseInOut(currentTime, Easing.EasingFunction.LinearEasing);
Because of our captial letter naming convention it is obvious that Easing is a class and EaseInOut must therefore be a static function defined by that class
대문자 명명 규칙때문에 Easing이 클래스인것은 명백하고, EaseInOut은 그 클래스에 선언된 정적 함수임이 틀림없다.

The answer in my case is to define enums in global space if they are used outside of a class often and if they are defined in the class, I think about how the final code will read and try to do something like this:
만약 enum이 종종 클래스의 외부에서 사용되고, 클래스 안에 정의 된다면 나의 경우에는 enum을 글로벌로 선언한다.
나는 어떻게 최종 코드가 읽힐지 생각하고, 밑에 처럼 작성 하기위해 노력한다.

1
2
3
4
5
6
7
//Either:
 
var easedPosition = Easing.EaseInOut(currentTime, EasingFunction.Linear);
 
//Or:
 
var easedPosition = Easing.EaseInOut(currentTime, Easing.Function.Linear);

In the latter, Function makes sense because externally it will be preceded by Easing and internally it's fairly obvious that this is an easing class.
후자의 경우 Function이라는 이름은 이해하기가 쉽다. 표면적으로 Easing앞에 와있고 그것은 내부적으로 Easing클래스 안에 있다는 것이 명확하기 때문이다.

Don't Be Wet Be DRY
(Wet: Write Everything Twice definition, DRY표현의 반대 의미를 익살스럽게 나타내는 표현이다.
결국 Don't Be Wet나 Be Dry나 같은 말이다. 똑같은 것을 반복하지마라)

DRY stands for Don't Repeat Yourself and it is a mantra that should be playing in your head all the time you are writing code.  Cut & Paste can be the enemy of productivity.  You write some code,  you cut and paste is half a dozen times and everything is dandy - right up to the point where you decide to refactor or fix a bug and then you have to find all of the places you pasted that code - in the end you will forget one and the bug will go unnoticed for ages.
DRY는 자기자신을 반복하지마라의 의미로, 당신이 코드를 작성할 때 머리속에서 계속적으로 실행되어야 하는 주문과 같은 것이다. 붙여넣기는 생산성의 적이 될 수 있다. 코드를 작성할 때, 여러 차례의 붙여넣기는 멋져 보인다. 요점은 당신이 리펙토링을 결정해야하는 곳이나 버그를 고쳐야 할 때 당신은 붙여넣기한 모든 곳을 알아야 한다. 결국에는 이것을 잊어버릴 것이고, 버그는 오랫동안 갑자기 예고없이 나타날 것입니다.


Bite the bullet and write a function if you are going to do exactly the same thing in multiple places.  Try to organise where these functions live - create a global static class and put general utility functions in there.  The effort of thinking about a function can be useful too:
만약 여러 곳에 같은 일을 처리하는 소스를 작성해야 한다면, 이를 악물고 함수를 작성하라. 이러한 함수가 어디에 위치해야하는지 준비하라. 전역 정적 클래스를 만들고, 일반적인 유틸리티 함수를 그곳에 두어라.
함수에 대해 생각하는 노력(고민)은 언제나 유용하다.


  • Pretty much all coding involves holding only a part of the whole structure in your head at a time. When you are writing code and just type in the name of a function in line and move on,  then fill out that function afterwards.
    거의 대부분 당신의 머리에서는 한번에 오직 전체구조의 일부분 만을 가지고 있다.
    당신이 코드를 작성할 때, 일단 함수의 이름을 작성하고 그리고 이동시킨 다음, 그 다음에 함수를 작성해라
    (간단하게 함수의 이름과 위치를 먼저 결정하고 그 다음 함수를 작성하라는 의미)


  • Black box in other words - don't even worry yourself about how it will work until you fill it in. Sure you may have to add some parameters, but pretty quickly you will find you are building useful function prototypes as you code. Some refactoring tools will actually just build these functions out for you - working out all of the parameter types and names by inferring them from the variables you passed to the non-existent function.
    당신이 함수 안에 내용을 채우기 전까지 그들이 어떻게 작동할 것인지에 대한 고민은 하지마라.
    물론 당신은 약간의 매개변수를 추가해야 하지만, 당신은 꽤 빠르게 당신의 함수 프로토타입을 잘 작성하고 있다는 것을 알게 될 것이다. 약간의 리팩토링 툴은 사실 당신을 위해서 이런 함수를 만든다. 당신의 변수를 존재하지 않는 함수의 매개변수로 보내는 것을 추론하여 모든 매개변수의 타입과 이름을 작성한다.


  • Thinking about functions and the parameters helps you spot the theoretical mistakes in your architecture - you will write better code.  
    함수와 매개변수에 대한 고민은 당신이 구조에서 이론적 실수를 발견하는데 도움을 준다.
    그리고 더 나은 코드를 작성할 수 있을 것이다.


  • Always try to write a function that is efficient but has the maximum flexibility - the whole of your code is effectively the software equivalent of Frankenstein's monster - these functions are going to be the beautiful elegant bits!
    항상 최대의 융통성을 가지는 효율적인 함수를 작성하기 위해 노력해라. 당신의 코드 전체는 실질적으로 프랑케슈탄인의 몬스터와 동일한 소프트웨어이다.(프랑케슈타인의 몬스터 : 자기가 만들어낸 저주의 씨) 이들의 함수는 아름답고 우아한 비트가 될 것이다.

Conclusion
결론


The most important thing to do is be consistent, concise but readable in your identifier definitions and always indent your code.  If you've indented more than 4 times, consider refactoring to multiple functions.  Your code should be as DRY as possible.
가장 중요한 것은 변수 정의에 있어서 가독성, 일관성, 정확성, 그리고 항상 들여쓰기를 하는 것이다.
만약 4번이상의 들여쓰기를 했다면, 함수를 작성함으로 리펙토링을 고려하라. 그리고 반드시 DRY(Don't repeat Yourself) 규칙을 지켜라.


The conventions presented here represent my view on the current recommendations from .NET and Unity - feel free to argue, rant, rave and call me names in the comments section.
여기에 나타난 규칙은 현재 .NET과 유니티의 권장사항에 대한 나의 견해다.


'유니티 개발 정보 > 개념' 카테고리의 다른 글

코루틴(Coroutine)++  (3) 2013.10.08
유니티 메모리 관리 - 4(마지막)  (0) 2013.10.03
유니티 메모리 관리 - 3  (1) 2013.08.13
유니티 메모리 관리 - 2  (0) 2013.08.09
유니티 메모리 관리 - 1  (0) 2013.08.07