개발

코드 컨벤션

simba 2020. 10. 8. 10:40

코드 컨벤션이란?

관리하기 쉬운 코드를 작성하기 위한 일종의 코딩 스타일 규약

왜 코딩 규칙(Code Convention)이 필요한가?

  • 소프트웨어를 개발하는 일련의 모든 과정에 들어가는 비용 중 80%가 유지보수에 쓰여진다.
  • 코딩 규칙을 지키면 다른 개발자가 그 소스 코드를 처음 보았을 때, 더 빠른 시간 안에 완벽하게 이해할 수 있도록 도와주기 때문에, 소프트웨어의 가독성이 높아진다.
  • 개발자가 자신의 소스 코드를 제품으로 팔려고 한다면, 자신이 작성한 다른 소스 코드들과 잘 어울리도록 패키지(package)를 적절하게 구성할 필요가 있다.
  1. 자바 소스 파일
  • 시작 주석

모든 소스 파일은 클래스 이름, 버전 정보, 날짜, 저작권 주의를 보여주는 C 스타일의 주석과 함께 시작한다.


/ *
  *클래스 이름
  *
  * 버전 정보
  *
  * 날짜
  *
  *저작권 주의
  */
  • Package 문과 Import 문
  • package명은 단어가 달라지더라도 소문자로 하는것이 좋다
package java.awt;
import java.awt.peer.CanvasPeer;
  • Class와 Interface 선언
  • 소스파일의 인코딩은 UTF-8로 통일한다

2. 들여쓰기

Intellij에서 제공하는 소스 정리

어디서 문장을 내릴까?

문장을 대입 연산자가 아닌 곳에서 잘라야 할 경우 심볼 앞에서 내린다.

대입 연산자에서 잘라야 할 경우 대입 연산자 뒤에서 문장을 내린다.

함수호출의 경우 ‘(‘는 첫 문장에 두고 나머지를 다음 문장으로 내린다.

3. 줄 나누기

하나의 식이 한 줄에 들어가지 않을 때에는, 다음과 같은 원칙에 따라서 두 줄로 나눈다.

  • 콤마 후에 두 줄로 나눈다.
  • 연산자(operator) 앞에서 두 줄로 나눈다.
  • 레벨이 낮은 원칙보다는 레벨이 높은 원칙에 따라 두 줄로 나눈다.
  • 앞줄과 같은 레벨의 식(expression)이 시작되는 새로운 줄은 앞줄과 들여쓰기를 일치시킨다.
  • 만약 위의 원칙들이 코드를 더 복잡하게 하거나 오른쪽 끝을 넘어간다면, 대신에 8개의 빈 칸을 사용해 들여쓴다.
삼항식
alpha = (aLongBooleanExpression) ? beta : gamma;
alpha = (aLongBooleanExpression) ? beta
                                                        : gamma;
alpha = (aLongBooleanExpression)
             ? beta
             : gamma;

 

// 일반적인 들여쓰기
someMethod(int anArg, Object anotherArg, String yetAnotherArg,
                       Object andStillAnother) {
        ...
}

// 너무 멀리 들여쓰는 것을 피하기 위해 8개의 빈 칸으로 들여쓰기
private static synchronized horkingLongMethodName(int anArg,
             Object anotherArg, String yetAnotherArg,
             Object andStillAnother) {
     ...
}

 

연산자
longName1 = longName2 * (longName3 + longName4 - longName5)
                      + 4 * longname6; // 될 수 있으면 이 방법을 사용한다.

longName1 = longName2 * (longName3 + longName4
                                        - longName5) + 4 * longname6; // 될 수 있으면 피한다.

4. 주석

  • 구현 주석

프로그램은 다음과 같은 4가지 형식의 구현 주석을 포함할 수 있다.

블록(Block) 주석

한 줄(Single-Line) 주석

꼬리(Trailing) 주석

줄 끝(End-Of-Line) 주석

4.1 블록(Block) 주석

블록 주석은 파일, 메서드, 자료구조, 알고리즘에 대한 설명을 제공할 때 사용된다. 블록 주석은 각각의 파일이 시작될 때와 메서드 전에 사용된다. 또한 메서드 안에서와 같이 다른 장소에서 사용될 수도 있다. 메서드 안에 존재하는 블록 주석은 주석이 설명하는 코드와 같은 단위로 들여쓰기를 해야한다.

블록 주석은 다른 코드들과 구분하기 위해서 처음 한 줄은 비우고 사용한다.

/*
 * 여기에 블록 주석을 작성한다.
 */

블록 주석의 들여쓰기를 다시 고치지 못하도록 하기 위한 특별한 블록 주석은 /*- 으로 시작할 수 있다.

 

/*-
  * 여기에 들여쓰기를 무시하는 특별한
  * 블록 주석을 작성한다.
  *
  *        one
  *             two
  *                     three
  */

4.2 한 줄(Single-Line) 주석

짧은 주석은 뒤따라 오는 코드와 같은 동일한 들여쓰기를 하는 한 줄로 작성할 수 있다. 만약 주석이 한 줄에 다 들어가지 않는다면, 블록 주석 형식을 따라야 한다. 한 줄 주석은 빈 줄로 시작되어야 한다.

 

if (condition) {

        /* 이 조건을 만족하면 실행된다. */
        ...
}

4. 3 꼬리(Trailing) 주석**

아주 짧은 주석이 필요한 경우 주석이 설명하는 코드와 같은 줄에 작성한다. 하지만 실제 코드와는 구분될 수 있도록 충분히 멀리 떨어뜨려야 한다.

if (a == 2) {
      return TRUE;                     /* 특별한 경우 */
} else {
      return isPrime(a);               /* a 가 홀수인 경우 */
}

4. 4 줄 끝(End-Of-Line) 주석**

주석 기호 // 는 한 줄 모두를 주석 처리하거나 한 줄의 일부분을 주석 처리해야 할 때 사용할 수 있다. 긴 내용을 주석에 포함하기 위해서 연속되는 여러 줄에 이 주석을 사용하는 것은 안되지만, 어떤 코드의 일부분을 주석 처리하기 위해서 여러 줄에 연속해서 사용하는 것은 허락된다.

if (foo > 1) { 
     // double-flip을 실행한다.
     ...
}
else {
      return false;          // 이유를 여기에 설명한다.
}
//if (bar > 1) {
//
//  // double-flip을 실행한다.
// ...
//}
//else {
// return false;
//}
  • 문서화 주석

5. return 문

값을 반환하는 return 문은 특별한 방법으로 더 확실한 return 값을 표현하는 경우를 제외하고는 괄호를 사용하지 않는 것이 좋다.

리턴을 변수로만 할것인지 어느정도까지는 인정을 할 것인지!

6. if, if-else , if else-if else문

if문은 항상 중괄호를 사용한다.

if (condition) // 이렇게 중괄호 {}를 생략해서 사용하지 말자
       statement;

7. White Space

논리적으로 관계가 있는 코드들을 쉽게 구분할 수 있다.

  • 두 줄 띄우는 경우

  • 소스 파일의 섹션들 사이

  • 클래스와 인터페이스의 정의 사이

  • 한 줄 띄우는 경우

  • 메서드들 사이

  • 메서드 안에서 지역변수와 그 메서드의 첫 번째 문장 사이

  • 블록 주석 또는 한 줄 주석 이전

  • 가독성 향상을 위해 메서드 내부의 논리적인 섹션들 사이

 

0. 공통 명명 규칙

대소문자가 구분되며 길이에 제한이 없다

숫자로 시작해서는 안된다

파스칼 표기법 (PascalCase)과 카멜 표기법(camelCase)를 사용한다.

PascalCase : 모든 단어에서 첫 번째 문자는 대문자이며 나머지는 소문자이다.

camelCase : 최초에 사용된 단어를 제외한 첫 번째 문자가 대문자이며 나머지는 소문자이다.

네이밍시 중요한 고려사항

네이밍을 할 때 다음과 같은 질문을 통해 이름을 짓는 것이 필요하다.

  • 왜 존재해야 하는가

  • 무슨 작업을 하는가

  • 어떻게 사용하는가

  • Example

    public List<Piece> findPiecesByColor(Color color){}

  • // 왜 존재해야 하는가 - color에 대해 존재하는 piece들을 알기 위해.

  • // 무슨 작업을 하는가 - color에 맞는 piece들을 가져온다.

  • // 어떻게 사용하는가 - 체스판에서 흑색(or 백색)의 piece들을 가져와서 점수를 계산.

이름만으로도 언제 이 메서드를 호출해야 하는지 의미를 파악할 수 있도록 구체적으로 작성하도록 해야 한다.

1. 패키지(Package) 명명 규칙

Ex) [com].[Company].[Project].[TopPackage].[LowerPackage]

패키지명은 가급적 한단어의 명사를 사용한다.

2. 클래스(Class) 명명 규칙 ( Interfaces도 마찬가지)

  • 클래스 이름은 명사이어야 하며, 복합 단어일 경우 각 단어의 첫 글자는 대문자이어야 한다.
  • 클래스 이름은 간단하고 명시적으로 작성해야 한다.
  • 완전한 단어를 사용하고 두 문자어와 약어는 피하도록 한다(만약, 약어가 URL이나 HTML과 같이 더 많이 ,더 넓게 사용되고 있다면 약어를 사용하는 것도 괜찮다).
  • 클래스명에는 파스칼을 사용한다.

3. 메소드(Method) 명명 규칙

메서드 이름은 동사/전치사로 시작한다

 

메서드 이름은 lowerCamelCase로 작성한다 클래스명에는 파스칼을 사용한다.

Ex) public void SendMessage(String message) {}

 

속성에 접근하는 메소드명의 접두사는 'get','set'을 사용한다.

Ex) public void setDisplayName

Ex) public void getDisplayName

 

데이터를 조회하는 메소드명의 접두사는 find를 사용한다.

Ex) public void findData(String data){}

 

데이터를 입력하는 메소드명의 접두사는 input을 사용한다.

Ex) public void inputData(HashMap data){}

 

데이터를 변경하는 메소드명의 접두사는 modify를 사용한다.

Ex) public void modifyData(HashMap data){}

 

데이터를 삭제하는 메소드명의 접두사는 delete를 사용한다.

Ex) public void deleteData(String data){}

 

데이터를 초기화 하는 메소드명의 접두사는 initialize을 사용한다.

Ex) public void initData(String data){}

 

반환값의 타입이 boolean인 메소드는 접두사로 is를 사용한다.

Ex) public void isData(String Data){}

 

데이터를 불러오는 메소드명의 접두사는 load를 사용한다.

Ex) public void loadData(){}

 

데이터가 있는지 확인하는 메소드명의 접두사는 has를 사용한다.

Ex) public void hasData(){}

 

보다 지능적인 set이 요구될때 사용하는 메소드명의 접두사는 register를 사용한다.

Ex) public void registerAccount(){}

 

새로운 객체를 만든뒤 해당 객체를 리턴해주는 메소드명의 접두사는 create를 사용한다.

Ex) public void createAccount(){}

 

해당 객체를 다른 형태의 객체로 변환해주는 메소드명의 접두사는 to를 사용한다.

Ex) public void toString(){}

 

해당 객체가 복수인지 단일인지 구분하는 메서드명의 접미사는 s를 사용한다.

Ex) public void getMembers(){}

 

B를 기준으로 A를 하겠다는 메소드명의 전치사는 By를 사용한다.

Ex) public void getUserByName(String name){}

 

반환값의 타입이 boolean인 메소드는 접두사로 is를 사용한다.

Ex) public void isData(String Data){}

 

데이터를 불러오는 메소드명의 접두사는 load를 사용한다.

Ex) public void loadData(){}

 

데이터가 있는지 확인하는 메소드명의 접두사는 has를 사용한다.

Ex) public void hasData(){}

 

보다 지능적인 set이 요구될때 사용하는 메소드명의 접두사는 register를 사용한다.

Ex) public void registerAccount(){}

 

새로운 객체를 만든뒤 해당 객체를 리턴해주는 메소드명의 접두사는 create를 사용한다.

Ex) public void createAccount(){}

 

해당 객체를 다른 형태의 객체로 변환해주는 메소드명의 접두사는 to를 사용한다.

Ex) public void toString(){}

 

해당 객체가 복수인지 단일인지 구분하는 메서드명의 접미사는 s를 사용한다.

Ex) public void getMembers(){}

 

B를 기준으로 A를 하겠다는 메소드명의 전치사는 By를 사용한다.

Ex) public void getUserByName(String name){}

4. 변수(Variable) 명명 규칙

변수에 _를 사용할 것인지? 임시변수들의 이름은 어찌 지을것인지?

  • 변수와 메소드의 파라미터에는 카멜표기법을 사용한다.
  • 변수에 약어를 사용하지 않고 모든 의미를 충분히 담는다.
  • 보통의 임시 변수들의 이름은 integer 일 경우에는 i, j, k, m, n을 사용하고, character 일 경우에는 c, d, e를 사용한다.
void getTest(Event event){...} (o)
void getTest(Event valie){...} (x)

협업을 염두해서 짓기

private void validateNumericPosition(String[] expressionAsArray) {
        for (int i = 0; i < expressionAsArray.length; i += 2) {
        ...
        }
}

위 코드에서 2가 의미하는게 뭘까? 다른 사람이 봤을 때 i += 2 를 통해 어떤 처리를 하는지 파악하려면 코드를 분석해야 한다.

private void validateNumericPosition(String[] expressionAsArray) {
         int numberIndex = 2; 
          for (int i = 0; i < expressionAsArray.length; i += numberIndex) {
          ...
         }
}

2에 numberIndex 라는 의미 있는 변수명을 붙여주면 숫자만 걸러내기 위한 식인지 알 수 있다.

변수 이름에 자료형이 들어간다면?

private List<Double> numberList = new ArrayList<>();
private List<String> operatorList = new ArrayList<>();

List 대신 다른 자료형(Set…)을 써야 하는 경우가 오면 어떻게 해야 할까? 기존 변수명이 적절한 의미를 나타내지 못하게 되므로 결국 변수명을 변경해야 하는 번거로움이 생긴다. 변수 이름에 자료형을 쓰지 않아도 타입을 통해 충분히 어떤 변수인지 파악이 가능하다.

private List<Double> numbers = new ArrayList<>();
private List<String> operators = new ArrayList<>();

5. Annotations

애너테이션은 한줄당 하나씩

@Override
@Nullable
public String getNameIfPresent() { ... }