개요
사내에서 여러 사람들과 작업을 하다 보면 자기만의 스타일로 코딩하는 경우가 많아집니다.
이 경우 가독성이 떨어진다거나 전혀 다른 코딩 스타일로 서로간의 인상을 구기는 경우가 발생하는데요..
사내 자원인 Source Code 를 표준화하기 위해
Coding Convention(코딩 문법 규칙)과 Programing Practice(프로그래밍 규칙)를 작성해보았습니다.
국내외 여러 사이트와 책들을 참조하여 재작성하였는데.. 출처는 다 까먹었습니다.
아래 내용은 C#으로 프로그램을 작성함에 있어서 일반적으로 주의하여야 할 사항들에 대해 설명하며,
지침일뿐 강제사항은 아닙니다.
Method 이름 길이 제한
되도록 method이름은 25자 이내로 한다.
25자를 넘길 정도의 method라면 너무 많은 기능을 포함하고 있으므로, 여러 개의 method로 나누어 처리할 수 있는지 검토한다.
Method 이름 명명법
method 이름은 동사로 정하되, 동작에 대한 의미가 명백하게 명명한다.
알아보기 쉬운 이름은 별도의 주석이 필요 없을 수도 있다.
좋은표현
void SavePhoneNumber(string phoneNumber)
{
}좋지 않은 표현
// 전화번호를 저장하는 메소드 (Detail을 저장하라는 것만으로는 어떤 값이 저장되는지 명확치 않음)
void SaveDetails(string phoneNumber)
{
}Method는 하나의 동작을 처리함
하나의 method는 하나의 job을 처리함을 기본으로 한다.
좋은표현
SaveAddress(address);
SendMail(address, email);
void SaveAddress(string address)
{
// 주소 저장 작업
}
void SendMail(address, email)
{
// 이메일 전송 작업
}좋지 않은 표현
SaveAddress(address, email);
void SaveAddress(address, email)
{
// 주소 저장 작업
// 이메일 전송 작업
}c#에서 사용하는 variable type 사용
System.namespace 에서 사용하는 type으로 사용하지 않는다.
좋은표현
int age;
string name;
object contactInfo;좋지 않은 표현
Int16 age;
String name;
Object contactInfo;HardCoding 금지
코드상에 숫자를 hardcoding 하지 않는다.
- 숫자가 자주 바뀌는 경우 config, db, resource 등을 이용한다.
- 숫자가 거의 바뀌지 않는 경우 상수를 정의하여 사용하고, 상수가 선언된 줄은 맨 위에 놓는다.
잦은 변경이 예상되는 문자열은 hardcoding 하지 않고, resource를 사용한다.
drive는 항상 “C:”가 아닐 수 있으므로 path 또는 drive name은 hardcodin 하지 않고,
application path를 구하여 참조한다.
(i.e. System.Windows.Forms.Application.StartupPath)
문자열 비교
문자열 비교시 항상 소문자 또는 대문자로 변경하여 비교한다.
if(name.ToLower() == (“kimstar”)
{
}공백값
string의 초기화시 공백값으로 "" 대신 String.Empty를 사용한다. ""는 내부적으로 object를 생성하지만, System.Empty는 object를 생성하지 않는다.
string이 공백값인지 검사할 때 경우에 따라 아래와 같이 사용함을 권장한다.
if(str == ””) {}
if(string.IsNullOrEmpty(str)) {} // null값이 올 수도 있는 경우 사용 권장
if(str.Length == 0) {} // 처리 속도가 빠름멤버변수
각 메소드마다 멤버변수에 접근하여 값을 변경할 경우 tracking이 어려워지므로, 되도록 메소드간의 공유는 지역변수를 사용한다.
멤버변수는 private로 선언한다. 멤버변수에 대한 접근은 public 이나 protected의 property를 통한다.
enum 사용
아래의 예제와 같이 숫자형이나 문자형대신 enum을 사용하여, 오류를 방지한다.
좋은표현
enum MailType
{
Html,
PlainText
}
void SendMail(string message, MailType mailType)
{
switch(mailType)
{
case MailType.Html :
// 처리
break;
case MailType.PlainText :
// 처리
break;
default :
// 처리
break;
}
}좋지 않은 표현
void SendMail(string message, string mailType)
{
switch(mailType)
{
case “Html” :
// 처리
break;
case “PlainText” :
// 처리
break;
default :
// 처리
break;
}
}Event Handler
Event Handler 내부에는 실행에 필요한 코드를 입력하기 보다는, 실행 코드를 정의한 method를 호출함을 권장한다.
Application 기동시 Self Check
- 필요한 파일이 없을 경우 자동으로 생성한다.
- 필요한 레지스트리가 없을 경우 디폴트값으로 생성하고, 사용자에게 공지한다.
- 필요한 DB Connection 자원이 없을 경우, 사용자에게 공지한다.
- 필요한 Network 자원이 없을 경우, 사용자에게 공지한다.
- 환경설정값이 잘못되었을 경우 사용자에게 공지한다.
오류메시지
- 오류메시지는 최대한 사용자가 알아듣기 쉽게 표현한다.
- 오류메시지는 오류 상황과 이에 대한 해결책을 같이 제시한다.
- 로그파일에는 개발자가 상황을 파악할 수 있도록 발생 시간을 포함한 상세한 정보를 기록한다.
좋은표현
데이터베이스를 접속 중 오류가 발생하였습니다.
환경설정 메뉴에서 ID와 Pass를 점검하여 주시기 바랍니다.좋지 않은 표현
DB 접속 오류Parameter
Paramter를 너무 많이 사용하지 않는다. 4~5개가 넘는 parameter를 사용한다면 class 또는 structure를 사용함을 고려한다.
Return Value
Method의 Return value가 Collection일경우 값이 없으면 null로 리턴하지 말고 empty collection을 사용한다. 예를 들어 null로 리턴하면 값을 받는 쪽에서 null 체크를 해야하지만 empty collection으로 리턴 할 경우 반복문 등에서 별도의 null 체크가 필요없다. 또한 값이 없음을 체크하기 위해서는 collection의 count 값을 참조하면 된다.
자원 해제
database, socket, file stream 등의 자원을 open하여 사용한 후에는 항상 자원을 해제하여야 한다. 이때 예기치 못한 exception등의 상황에 대비하여, 항상 finally 블록에서 close를 수행한다.
StringBuilder 사용
- 반복문에서의 문자열 처리작업은 string 대신 StringBuilder를 사용한다.
- string에 다른 값을 넣을 경우 객체를 생성, 복사, 소멸이 반복되므로 효율적이지 못하다.
좋은표현
public string ComposeMessage(string[] lines)
{
StringBuilder message = new StringBuilder();
for(int i = 0; i < lines.Length; i++)
{
message.Append(lines[i]);
}
return message.toString();
}좋지 않은 표현
public string ComposeMessage(string[] lines)
{
string message = String.Empty;
for(int i = 0; i < lines.Length; i++)
{
message += lines[i];
}
return message;
}주석
- 모든 줄에 주석을 남발하지 않는다.
- // 또는 ///를 주로 사용하며, /* */는 되도록 삼가한다.
- 코드가 이해하기 쉬울 경우 주석을 생략한다.
- 초기화 값이 특정값으로 설정될 경우, 이에 대한 설명을 반드시 주석으로 기록한다.
Exception
- try catch문을 사용할때, 아무처리도 하지 않는 catch문은 절대 사용하지 않는다. exception을 숨길 수는 있지만 불안전한 프로그램이 된다.
- Exception에 대해 사용자에게는 친절한 문구로 안내하고, 로그에는 시간을 포함하여 상세한 기록을 남긴다.
- catch 문의 Exception은 명확하게 사용한다.
좋은표현
public void ReadFromFile(string filename)
{
try
{
// 파일을 읽는다.
}
catch(FileIOException fileEx)
{
// 로그기록 및 오류처리
// 다시 exception을 throw 함으로써, 관련곳에 case에 오류가 발생했음을 정확히 알려준다.
throw;
// 아래와 같이 사용하면 원본 call stack을 전달할 수가 없다.
// throw fileEx
}
}좋지 않은 표현
public void ReadFromFile(string filename)
{
try
{
// 파일을 읽는다.
}
catch(Exception Ex)
{
// 오류에 대한 상세한 처리가 힘들고,
// 아래와 같이 리턴할 경우 오류가 발생했는지 알 수 없는 경우도 있다.
return “”;
}
}- try catch는 exception을 방지할 수 없는 경우에만 사용한다.
예를 들어, database에 insert시에 key값의 중복은 미리 select하여 예방할 수 있지만, exception을 통해 중복되었음을 체크할 필요는 없다. - Custom Exception Class를 작성하여 사용할때는 SystemException에서 상속받지 말고, ApplicationException에서 상속받는다.


