자바 코딩 컨벤션(Java Code Conventions)

Code Conventions(코딩 규칙)을 왜 알아야 할 까 ?

개발하는 과정 속에서 구현하는 비용보다 유지보수의 비용이 더 크게 들어가는데 

프로젝트나 소프트웨어 개발을 하면서 작성한 코드를 나만 보는 것이 아니라 참여한 모든 개발자들이 보게 됩니다.

하지만 나만의 방식으로 코드를 짰다면 다른 사람들이 봤을 때 이해하기가 힘들겠죠.

예를 들자면 나만의 독특한 글씨체로 글을 쓴 편지를 친구에게 보냈는데 그 친구가 그 편지를 읽는데에 해독이 필요한 것처럼요.

그래서 Code Conventions이 필요합니다.

정해진 Code Conventions에 맞춰 코드를 작성하게 된다면 나 외의 다른 사람들도 코드를 해석하는데 빠르게 이해할 수 있어 지게 되므로 가독성이 좋아지게 됩니다.

 

이 포스팅에서 설명할 컨벤션 규칙은 NAVER CAMPUS HACKDAY의 규칙을 참고하여 작성하였습니다.

 

인텔리제이에 Java Code Conventions을 적용할 수 있는데 여기를 참고하시는 것을 추천 드립니다.


파일 공통 요건

파일 인코딩은 UTF-8

  • 인텔리제이 Encoding 설정

인텔리제이의 경우 Help -> Edit Custom VM Options을 클릭한 후, -Dfile.encoding=UTF-8 를 추가해준 후 저장해줍니다.

  • 인텔리제이 파일 인코딩 설정

File -> Settings -> Editor -> File Encodings -> Global, Project Encoding, Properties Files 설정을 UTF-8로 변경

 

 

새줄 문자는 LF

파일의 마지막에 개행 추가 (newline)

File -> Settings -> Editor -> General


이름 (Naming)

식별자에는 영문/숫자/언더스코어만 허용한다.

변수명, 클래스명, 메서드명 등에는 영어와 숫자만을 사용한다.

상수에는 단어 사이의 구분을 위하여 언더스코어(_)를 사용한다. 

 

식별자의 이름을 한국어 발음대로 영어로 옮겨 표기하지 않는다.

  • 나쁜 예 : moohyungJasan (무형자산)
  • 좋은 예 : intangibleAssets (무형자산)

 

패키지 이름은 소문자로 구성한다.

단어별 구문을 위해 언더스코어(_)나 대문자를 섞지 않는다.

- 나쁜 예
package com.navercorp.apiGateway
package com.navercorp.api_gateway

- 좋은 예
package com.navercorp.apigateway

 

클래스/인터페이스 이름에 대문자 카멜표기법 적용한다.

클래스 이름은 단어의 첫 글자를 대문자로 시작하는 대문자 카멜표기법(Upper camel case)을 사용한다.

- 나쁜 예
public class reservation
public class Accesstoken

- 좋은 예
public class Reservation
public class AccessToken

 

클래스 이름과 에 인터페이스 이름에 명사를 사용한다.

클래스 이름은 동사가 아닌 명사나 명사절로 짖는다.

인터페이스(interface)의 이름은 클래스 이름은 명사/명사절로 혹은 형용사/형용사절로 짓는다.

 

테스트 클래스는 "TEST"로 끝난다.

JUnit 등으로 작성한 테스트 코드를 담은 클래스의 이름 마지막에 "Test"을 붙인다

public class ValidatorTest {

 

메서드 이름과 변수에는 소문자 카멜표기법을 적용한다.

첫 번째 단어를 소문자로 작성하고, 이어지는 단어의 첫 글자를 대문자로 작성하는 소문자 카멜표기법(Lower camel case)를 사용한다. 

테스트 클래스의 메서드 이름에서는 언더스코어를 허용한다.

- 메서드
getSize();
printBridgeResult();

- 변수
private boolean authorized;
private int accessToken;

 

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

메서드명은 기본적으로는 동사로 시작한다.

다른 타입으로 전환하는 메서드나 빌더 패턴을 구현한 클래스의 메서드에는 전치사를 쓸 수 있다.

- 동사 사용 : renderHtml()
- 전환 메서드의 전치사 : toString()
- Builder 패턴 적용한 클래스의 메서드의 전치사 : withUserId(String id)

 

상수는 대문자와 언더스코어로 구성한다.

"static final"로 선언되어 있는 필드일 때를 상수로 간주한다.

상수 이름은 대문자로 작성하며, 복합어는 언더스코어 ' _ ' 를 사용하여 단어를 구분한다.

public final int LOTTO_MIN_RANGE = 1;
public final int LOTTO_MAX_RANGE = 45;
public final String BUY_LOTTO_INPUT = “구입할 로또 개수를 입력해주세요”;

 

임시 변수 외에는 1 글자 이름 사용 금지한다.

변수의 이름에는 1글자로 된 이름을 쓰지 않는다.

반복문의 인덱스나 람다 표현식의 파라미터 등 짧은 범위의 임시 변수에는 관례적으로 1글자 변수명을 사용할 수 있다.

HtmlParser p = new HtmlParser(); // X
HtmlParser parser = new HtmlParser(); // O

선언

클래스, 필드, 메서드, 변수값, import문 등의 소스 구성요소를 선언할 때 고려해야할 규칙이다.

 

소스파일당 1개의 탑레벨 클래스를 담는다.

탑레벨 클래스(Top level class)는 소스 파일에 1개만 존재해야 한다.

- 나쁜 예
public class LogParser {
}

class LogType {
}

- 좋은 예
public class LogParser {
    // 굳이 한 파일안에 선언해야 한다면 내부 클래스로 선언한다.
    class LogType {
    }
}

 

static import문에만 와일드 카드를 허용한다.

클래스를 import할때는 와일드카드(*) 없이 모든 클래스명을 다 쓴다.

static import에서는 와일드카드를 허용한다.

또한, static import와 import 그룹핑을 해서 아래 순서에 맞춰 작성한다. 다른 그룹간에는 공백라인을 한 줄 추가.

  • static import
  • import
import static baseball.Computer.*;
import static baseball.Message.*;

import java.util.ArrayList;
import java.util.HashSet;

 

제한자의 선언 순서

클래스/메서드/멤버변수의 제한자는 Java Language Specification에서 명시한 아래의 순서로 쓴다.

public 
protected 
private 
abstract 
static 
final 
transient 
volatile 
synchronized 
native 
strictfp

 

메서드는 접근자 기준으로 작성하지 않고 기능 및 역할별로 분류한다.

접근자 기준으로 작성하지 않고, 기능 및 역할별로 분류하여 기능을 구현하는 그룹별로 작성이 이루어지도록 해야한다. 

기능 및 역할별로 분류되어 public 사이에 private 메소드가 존재할 수 있다

 

 

클래스는 상수, 멤버 변수, 생성자, 메서드 순으로 작성한다.

class A {
    상수(static final) 또는 클래스 변수

    인스턴스 변수

    생성자

    메서드
}

 

한 줄에 여러 문장을 쓰지 않고, 하나의 선언문에는 하나의 변수만을 다룬다.

문장이 끝나는 ; 뒤에는 새줄을 삽입한다.

변수 선언문은 한 문장에서 하나의 변수만을 다룬다.

- 나쁜 예
int base, weight;

- 좋은 예
int base;
int weight;

- 나쁜 예
int base = 0; int weight = 2;

- 좋은 예
int base = 0;
int weight = 2;

들여쓰기

  • 4개의 빈 칸(space)를 들여쓰기 단위로 사용한다.

1개의 탭의 크기는 스페이스 4개와 같도록 에디터에서 설정한다.

File - Settings - Editor - Code Style - HTML

 

  • 클래스, 메서드, 제어문 등의 코드 블럭이 생길 때마다 1단계를 더 들여쓴다.

중괄호

중괄호({,}) 는 클래스, 메서드, 제어문의 블럭을 구분한다.

 

중괄호 선언은 K&R 스타일(Kernighan and Ritchie style)을 따른다.

줄의 마지막에서 시작 중괄호`{`를 쓰고 열고 새줄을 삽입한다. 블럭을 마친후에는 새줄 삽입 후 중괄호를 닫는다.

public class SearchConditionParser {
    public boolean isValidExpression(String exp) {

        if (exp == null) {
            return false;
        }

        return true;
    }
}

 

닫는 중괄호와 같은 줄에 else, catch, finally, while을 선언한다.

- if문
if (line.startWith(WARNING_PREFIX)) {
    return LogPattern.WARN;
} else if (line.startWith(DANGER_PREFIX)) {
    return LogPattern.NORMAL;
} else {
    return LogPattern.NORMAL;
}

- try catch문
try {
    writeLog();
} catch (IOException ioe) {
    reportFailure(ioe);
} finally {
    writeFooter();
}

- while문
do {
    write(line);
    line = readLine();
} while (line != null);

 

조건/반복문에 중괄호를 필수로 사용한다.

조건, 반복문이 한 줄로 끝더라도 중괄호를 활용한다.

- 나쁜 예
if (exp == null) return false;

for (char ch : exp.toCharArray()) if (ch == 0) return false;

- 좋은 예
if (exp == null) {
    return false;
}

for (char ch : exp.toCharArray()) {
    if (ch == 0) {
        return false;
    }
}

줄바꿈

줄바꿈은 작성한 명령어가 줄 너비를 초과했을 경우 코드 가독성을 위해서 강제로 줄을 바꾸는 것을 말한다.

최대 줄 사용 너비는 120자까지 가능하다.

package,import 선언문 중간에서는 줄을 바꾸지 않는다. 최대 줄수를 초과하더라도 한 줄로 쓴다.

 

줄바꿈 후 추가 들여쓰기

줄바꿈 이후 이어지는 줄에서는 최초 시작한 줄에서보다 적어도 1단계의 들여쓰기를 더 추가한다.

IDE의 자동정렬 기능 ( 인텔리제이의 경우 ctrl + alt + l )을 활용한다.

AbstractAggregateRootTest.AggregateRoot proxyAggregateRoot =
        em.getReference(AbstractAggregateRootTest.AggregateRoot.class, aggregateRoot.getId());

 

줄바꿈 허용 위치

가독성을 위해 줄을 바꾸는 위치는 다음 중의 하나로 한다.

extends 선언 후  
implements 선언 후
throws 선언 후
시작 소괄호(() 선언 후
콤마(,) 후
. 
연산자 전
  • +, -, *, /, %
  • ==, !=, >=, >,⇐, <, &&, ||
  • &, |, ^, >>>, >>, <<, ?
  • instanceof
  •  
public boolen isAbnormalAccess (
    User user, AccessLog log) {
    String message = user.getId() + "|" | log.getPrefix()
        + "|" + SUFFIX;
}

빈 줄

빈 줄은 명령문 그룹의 영역을 표시하기 위하여 사용한다.

 

package 선언 후 빈 줄을 삽입한다.

package baseball;

import java.util.List;

 

import 선언의 순서와 빈 줄 삽입

import 구절은 아래와 같은 순서로 그룹을 묶어서 선언한다.

static imports
java.
javax.
org.
net.
8~10을 제외한 com.*
1~6, 8~10을 제외한 패키지에 있는 클래스
com.nhncorp.
com.navercorp.
com.naver.

 

각 그룹 사이에는 빈줄을 삽입한다.

같은 그룹 내에서는 알파벳 순으로 정렬한다.

import java.util.Date;
import java.util.List;

import javax.naming.NamingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;

import com.google.common.base.Function;

import com.naver.lucy.util.AnnotationUtils;

 

 

메서드의 선언이 끝난 후 다음 메서드 선언이 시작되기 전에 빈줄을 삽입한다.

public void setId(int id) {
    this.id = id;
}

public void setName(String name) {
    this.name = name;
}

공백

빈줄을 포함하여 모든 줄은 탭이나 공백으로 끝내지 않는다.

 

대괄호 뒤에 공백 삽입

닫는 대괄호(]) 뒤에 `;`으로 문장이 끝나지 않고 다른 선언이 올 경우 공백을 삽입한다.

- 나쁜 예
int[]masks = new int[]{0, 1, 1};

- 좋은 예
int[] masks = new int[] {0, 1, 1};

 

중괄호 시작 전과 종료 후에 공백 삽입

여는 중괄호({) 앞에는 공백을 삽입한다.

닫는 중괄호(}) 뒤 else ,catch 등의 키워드가 있을 경우 중괄호와 키워드 사이에 공백을 삽입한다.

public void printWarnMessage(String line) {
    if (line.startsWith(WARN_PREFIX)) {
        ...
    } else {
        ...
    }
}

 

제어문 키워드와 여는 소괄호 사이에 공백 삽입

if, for, while, catch, synchronized, switch와 같은 제어문 키워드의 뒤에 소괄호((,)`)를 선언하는 경우, 시작 소괄호 앞에 공백을 삽입한다.

if (maxLine > LIMITED) {
    return false;
}

 

식별자와 여는 소괄호 사이에는 공백을 삽입하지 않는다.

식별자와 여는 소괄호(() 사이에는 공백을 삽입하지 않는다.

생성자와 메서드의 선언, 호출, 애너테이션 선언 뒤에 쓰이는 소괄호가 그에 해당한다.

public StringProcessor() {} // 생성자

@Cached("local")
public String removeEndingDot(String original) {
    assertNotNull(original);
    ...
}

 

타입 캐스팅(casting)에 쓰이는 소괄호 내부에 공백을 삽입하지 않는다.

- 나쁜 예
String message = ( String ) rawLine;

- 좋은 예
String message = (String)rawLine;

 

콤마/구분자 세미콜론의 뒤에 공백을 삽입한다.

콤마(,)와 반복문(while, for)의 구분자로 쓰이는 세미콜론(;)에는 뒤에만 공백을 삽입한다.

- 나쁜 예
for (int i = 0;i < length;i++) {
    display(level,message,i)
}

- 좋은 예
for (int i = 0; i < length; i++) {
    display(level, message, i)
}

 

주석문 기호 전후의 공백 삽입

주석의 전후에는 아래와 같이 공백을 삽입한다

  • 명령문과 같은 줄에 주석을 붙일 때 // 
  • 주석 시작 기호 // 
  • 주석 시작 기호 /* 
  • 블록 주석을 한 줄로 작성시 종료 기호 */ 
/*
 * 공백 후 주석내용 시작
 */

System.out.print(true); // 주석 기호 앞 뒤로 공백

/* 주석내용 앞에 공백, 뒤에도 공백 */