시작하며
안녕하세요, 오랜만에 포스팅 입니다!
이번에 가져온 주제는 "Java Code Convention"입니다 :)
드디어 우아한테크코스 6기 선발을 위한 프리코스가 시작되었습니다 !
이번 과제를 시작하기에 앞서 자바 코드 컨벤션을 준수해야 한다고 적혀있었는데요.
처음에는 "일단 구현 먼저 하면서 생각해야지!"하고 마음 먹었다가, 이건 정말 위험한 생각인걸 인지하고 먼저 공부를 해보려고 마음 먹었습니다😅
그래서 우테코에서 제시한 Google Java Style Guide를 참고하여 본문 내용을 읽어보며 정리를 시작해 보았습니다😊
혹시라도 잘못된 내용이나, 더 알아야 할 내용이 있다면 알려주시면 감사하겠습니다.
01 소개
이 문서는 Java 프로그래밍 언어의 소스 코드에 대한 Goole 코딩 표준의 완전한 정의로 사용됩니다.
자바 소스 파일은 해당 규칙을 준수하는 경우에만 Google 스타일로 설명이 됩니다.
1) 용어 참고 사항
달리 명시하지 않는 한 이 문서는 다음을 수행합니다.
- class라는 용어는 “일반” 클래스, 열거형 클래스, 인터페이스 또는 주석 유형(@interface)을 의미하기 위해 포괄적으로 사용됩니다.
- 클래스의 멤버라는 용어는 중첩된 클래스, 필드, 메서드 또는 생성자를 의미하기 위해 포괄적으로 사용됩니다.
- 주석이라는 용어는 항상 구현 주석을 의미합니다. 우리는 “documentation comments”이라는 문구를 사용하지 않고 “Javadoc”이라는 일반적인 용어를 사용합니다.
02 소스파일 기본 사항
1) 파일 이름
- 소스 파일 이름은 포함된 최상위 클래스의 대소문자 구분하여 작성하고 .java 확장자로 구성
- etc. Person.java
2) 파일 인코딩 : UTF-8
- 소스 파일은 UTF-8로 인코딩
3) 특수 문자
3-1) 공백 문자
- 줄 바꿈 문자(\n)를 제외하고, ASCII horizontal space character(0x20)(=스페이스)가 유일한 공백 문자
위 내용은 다음과 같다.
1. 문자열 및 문자들의 다른 모든 공백 문자들은 이스케이프 처리 된다.
2. Tab 문자는 들여 쓰기에 사용 되지 않는다.
-> 즉, 들여쓰기는 스페이스 키로 한다.
-> IDE에서 탭을 누르면 자동으롤 스페이스바 n개로 바꿔준다.
3-2) 특수 문자
- 특수문자는 (\b, \t, \n, \f, \r, \” , \’, \)을 사용
- 8진수, 유니코드 대신 이스케이프 문자 사용
이스케이프 문자(escape sequence)
- 이스케이프 문자는 제어문자들과 출력되지 않는 문자들을 가리킨다.
- 키보드로 표현할 수 없는 문자들이다.
- 보통 역슬래시(\)와 문자와 결합하여 사용한다.
3-3) Non-ASCII 문자
- Non-ASCII 문자의 경우 실제 유니코드 문자 또는 유니코드 이스케이프가 사용된다.
- 프로그램이 ASCII 문자를 처리하지 못할 수 있다는 생각에 코드 가독성을 낮추지 마라.
- etc
Example Discussion String unitAbbrev = "μs"; 최적: 주석없이도 완벽하다. String unitAbbrev = "\u03bcs"; // "μs" 허용, 하지만 이럴 이유는 없음. String unitAbbrev = "\u03bcs"; // Greek letter mu, "s" 허용, 하지만 이상하고 실수할 수 있음. String unitAbbrev = "\u03bcs"; 나쁨: 읽는 사람이 알 수가 없음. return '\ufeff' + content; // byte order mark 좋음: 프린트할 수 없는 문자들에 대해 이스케이프를 썼으며 주석이 필요하다면 만든다.
03 소스파일 구조
- 소스 파일은 다음과 같은 순서로 구성
- 라이센스
- Package 구문
- Import 구문
- Class 선언
1) 라이센스
- 만약, 라이센스 또는 저작권 정보가 있다면 기입
2) Package 구문
- 아무리 길어도 줄바꿈 하지 않는다
3) Import 구문
- 아무리 길어도 줄바꿈 하지 않는다
- 와일드카드(e.g.*)를 사용하지 않는다
- static import → non-static import 순서대로 기재한다
- 만약 한 섹션 내에 static과 non-static import가 모두 있는 경우 섹션을 나눈다
- 정적 중첩 클래스는 static import로 가져오지 않는다
4) Class 선언
- 최상위 클래스만 선언
- 클래스의 멤버와 Initializer의 순서는 따로 없지만, 논리적인 순서를 따라야한다
- 새 메서드를 끝에 추가하는 것은 시간순이지 논리적인 순서가 아니다
- 동일한 메서드명(생성자, 오버라이딩 된 메서드)은 한 곳에 모음
04 포맷팅
1) 중괄호
- K&R 스타일을 따른다
- 구현부가 없거나 한 줄의 구문을 포함해도 중괄호 사용
- 예시
return () -> {
while(condition()) {
method(); //한 줄이어도 중괄호를 사용한다.
}
}
return () -> {} //빈 블록은 줄바꿈하지 않아도 된다.
- 여는 중괄호 뒤에 닫는 중괄호 앞쪽을 줄바꿈 한다.
- 닫는 중괄호 뒤쪽은 구문이 끝나거나 메서드 생성자 이름이 있는 클래스의 내용이 끝났을 때만 줄바꿈
틀린 예시
if (a > b)
return a;
else if (a == b)
return 1;
else
return b;
옳은 예시
if (a > b) {
return a;
} else if (a == b) {
return 1;
} else { // 닫는 줄바꿈 뒤쪽은 구문이 끝나지 않아 줄바꿈 하지 않음
return b;
}
- 빈 블록은 줄바꿈 하지 않고 {}로 사용
- 그러나 multi-block은 할 수 없다
- etc. if, else, try-catch-finally
코드 예시
// 허용되지 않음: 멀티 블럭 구문에서는 간결한 빈 블럭을 사용할 수 없음
try {
doSomething();
} catch (Exception e) {}
// 허용되지 않음: 멀티 블럭 구문에서는 간결한 빈 블럭을 사용할 수 없음
try {
doSomething();
} catch (Exception e) {}
2) 들여쓰기
- 들여쓰기는 스페이스 4칸씩 적용
- 새 블록 또는 블록과 유사한 구조(block-like construct)가 열릴 때마다 들여쓰기가 네 칸씩 증가
- 블록이 끝나면 들여쓰기는 이전 들여쓰기 단계로 돌아간다
- 들여쓰기 단계는 블록 전체의 코드와 주석 모두에 적용
3) 한 문장에 하나의 statement
- 한 줄에는 하나의 명령문만 사용하고 다른 명령문은 줄바꿈한다
4) 열 제한
- 한 줄에 100자가 넘어가면 안됨
- 우테코에서는 120자로 제한
- 그보다 길 경우, 줄바꿈한다
- "문자"는 유니코드 코드 포인트를 의미
5) Line-wrapping
- Line-wrapping : 합법적으로 한 줄로 작성 가능하지만, 여러 줄로 나누어서 표현하는 경우
- 모든 상황에서 줄 바꿈을 하라는 절대적인 공식은 없으며, 상황에 따라 적절하게 사용하면 됨
- 메서드나 지역변수를 추출하면 줄 바꿈을 하지 않고도 해결 가능
5-1) 줄바꿈을 해야하는 경우
- 줄 바꿈의 주요 지침은 더 높은 문법 수준 에서 중단하는 것
- 비 연산자에서 줄이 끊어지면 기호 앞에서 줄바꿈 해야함
- 점 구분 기호 ( .)
- 메서드 참조의 두 콜론 ( ::)
- Type 바운드의 앰퍼샌드 (<T extends Foo & Bar>)
- catch 블록의 파이프 (catch (FooException | BarException e))
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
this.someString = new StringBuffer()
.append("Humans ")
.append("are ")
.append("intelligent ")
.append("apes.");
this.someString = "Humans "
+ "are "
+ "intelligent "
+ "Apes.";
- 대입 연산자는 일반적으로 기호 뒤에서 줄바꿈하지만 어느 쪽이든 허용
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
- 메서드 또는 생성자의 이름은 뒤에 오는 여는 괄호 (
(
)와 붙여 사용 → 줄바꿈 x - 쉼표 (,)는 그 앞에있는 토큰과 붙여 사용한다 → 줄바꿈 x
- lambda의 본문이 중괄호가없는 단일 식으로 구성된 경우 화살표 바로 뒤에 줄 바꿈이있을 수 있다. 이를 제외하고는 lambda의 화살표 옆에서 줄이 끊어지지 않는다
MyLambda<String, Long, Object> lambda =
(String label, Long value, Object obj) -> {
...
};
Predicate<String> predicate = str ->
longExpressionInvolving(str);
5-2) 들여쓰기 지속은 최소 2번 이상의 들여쓰기
- 줄 바꿈 시 그 다음 줄은 원래 줄에서 2번 이상 들여쓰기
- 우테코에서는 +8 스페이스 이상(4번 들여쓰기)적용
6) 공백
6-1) 줄바꿈
- 하나의 빈 줄이 나타나는 경우는 다음과 같다
- 클래스 멤버들을 구분하는 데 사용 : 메서드, 생성자, 멤버변수
- 멤버 변수의 경우, 사이에 코드가 없다면 굳이 공백라인을 넣지 않아도 된다.
- 메서드 내부에서 논리적으로 그룹핑 되는 부분
- 클래스 멤버들을 구분하는 데 사용 : 메서드, 생성자, 멤버변수
6-2) 띄어쓰기
- 해당 줄에서 뒤에 오는 여는 괄호에서 if, for 또는 catch 이후 나오는 여는 괄호에서 사용
- else 또는 catch 이후 나오는 닫는 중괄호에서 분리
Boolean test = true;
if (test) {
//...
} else {
//...
}
int testNum = 2 + 1 * (4 / 2) - 25;
for (int i = 0; i < 3; i++) {
//
}
- 여는 중괄호 앞에서의 두가지 예외
@SomeAnnotation({a, b}) (공백이 사용되지 않음)
String[][] x = {{"foo"}};( {{아래 8 번 항목 사이에 공백이 필요하지 않음 )
7) 그룹화 괄호 : 권장
- 선택적 그룹화 괄호는 작성자와 검토자가 코드가 없으면 코드가 잘못 해석될 가능성이 없으며 코드를 읽기 쉽게 만들지 않았다는 동의하에 생략 됨
- 모든 독자가 자바 연산자 우선 순위 테이블이 있다고 가정하는 것은 옳지 않음
- 우선순위가 명확해도 괄호로 감쌀 것!
8) 특정 구조
8-1) Enum 클래스
- enum 상수 뒤에 오는 각 쉼표 뒤에 줄바꿈은 선택 사항
- 추가적으로 줄바꿈 가능(일반적으로 한번)
private enum Answer {
YES {
@Override public String toString() {
return "yes";
}
},
NO,
MAYBE
}
- method와 주석이 없는 enum class는 배열 초기화와 같은 포맷으로 정의 될 수 있음
private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }
8-2) 변수 선언
- 선언 당 하나의 변수
- 모든 변수 선언 (필드 또는 로컬)은 하나의 변수만 선언
// 잘못된 코드
int a, b;
int a; int b;
// 옳은 코드
int a;
int b;
- 필요할 때 선언
- 무조건적으로 시작부분에 시작하는 것은 아님
- 지역 변수는 범위를 최소화하기 위해 처음 사용되는 지점에 가깝게 선언됨
- 지역 변수 선언에는 일반적으로 이니셜 라이저가 있거나 선언 직후에 초기화 됨
- 무조건적으로 시작부분에 시작하는 것은 아님
8-3) 배열
- 배열 선언은 "block-like"하게 하면 된다
- 모두 허용
new int[] { new int[] {
0, 1, 2, 3 0,
} 1,
2,
new int[] { 3,
0, 1, }
2, 3
} new int[]
{0, 1, 2, 3}
- C type 배열 선언 없음
- 대괄호는 변수가 아닌 type의 일부를 형성
String[] args // 허용
String args[] // 허용 x
8-4) switch 문
- 들여쓰기
- +2로 들여쓰기
- Fall-through : 주석
- Switch 블록 내에서 각 문 그룹 중 하나 (break, continue, return또는 발생한 예외)가 돌연 종료되거나, 다음 구문으로 넘어가게 적을 수 있다. 여기에는 주석을 달 수 있다.
switch (input) {
case 1:
case 2:
prepareOneOrTwo();
// fall through
case 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
}
- default 코드가 없더라도 default 블록을 추가
8-5) 어노테이션
- 클래스, 메서드 또는 생성자에 적용되는 어노테이션은 문서 블록 바로 뒤에 나타나며, 각 어노테이션은 자체 줄에 나열됨
- 즉, 한 줄에 하나의 주석
@Override
@Nullable
public String getNameIfPresent() { ... }
-
- 파라미터가 없는 단일 어노테이션은 첫번째 줄 맨 처음에 쓸 수 있음
@Override public int hashCode() { ... }
- 필드에 적용되는 어노테이션의 경우 여러 주석 (매개 변수화 가능)이 같은 줄에 나열 될 수 있음
@Partial @Mock DataLoader loader;
8-6) 주석
/*
* This is // And so /* Or you can
* okay. // is this. * even do this. */
*/
8-7) Modifier 순서
- public
- protected
- private
- abstract
- default
- static
- final
- transient
- volatile
- synchronized
- native
- strictfp
8-8) 숫자 리터럴
- long값을 갖는 정수 리터럴 L은 소문자가 아닌 대문자를 사용
1000L(o)
1000l(x)
09 Naming
1) 모든 식별자에 공통적인 규칙
- 모든 식별자들은 ASCII와 숫자 값만 사용해야 함
- 식별자에 prefix나 suffixes 는 사용하지 않음
name_
mName
s_name
kName
2) 식별자 유형별 규칙
2-1) Package명
- 모두 소문자
- 밑줄 없음(_)
2-2) Class 명
- UpperCamelCase로 작성
- 일반적으로 명사 또는 명사구
- Character, ImmutableList
- 테스트일 경우 끝에 Test 붙여주기
- etc. HashTest
2-3) 메서드 명
- lowerCamelCase로 작성
- 일반적으로 동사 또는 동사구
- etc. sendMessage, stop
- 테스트 명은 규칙이 없음
2-4) 상수 명
- CONSTANT_CASE를 사용
- 모두 대문자 사용
- 밑줄(_)로 각 단어 구분
// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final ImmutableMap<String, Integer> AGES = ImmutableMap.of("Ed", 35, "Ann", 32);
static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }
// Not constants
static String nonFinal = "non-final";
final String nonStatic = "non-static";
static final Set<String> mutableCollection = new HashSet<String>();
static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);
static final ImmutableMap<String, SomeMutableType> mutableValues =
ImmutableMap.of("Ed", mutableInstance, "Ann", mutableInstance2);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};
- 일반적으로 명사 또는 명사구
인스턴스의 상태가 변경 될 수있는 경우 상수가 아님
단순히 객체 변형을 막는 것이 목적이 아님
2-5) 상수가 아닌 필드 명
- lowerCamelCase로 작성
- 일반적으로 명사 또는 명사구
2-6) 파라미터 명/지역변수 명
- lowerCamelCase로 작성
- 공용 메서드에서 한 문자 파라미터 이름은 피해야 함
3) 프로그래밍 규칙
1) @Override: 항상 사용
- 오버라이딩이 된 모든 경우에 @Override 필수 사용
- 부모 클래스와 메서드를 재정의하거나 인터페이스를 구현했을 때도 마찬가지
- 다만 부모 쪽에서 @Deprecated를 선언했을 경우에는 자식 쪽에서 @Override 생략 가능
2) 모든 예외 처리
- 모든 예외는 무시하지 말고 처리
- 만약 예외를 처리하지 않을 경우, 이유 명시
- 테스트 코드에서는 필요할 경우 무시 가능
3) Static 멤버 접근
- 클래스 명으로 접근
Foo aFoo = ...;
Foo.aStaticMethod(); // good
aFoo.aStaticMethod(); // bad
somethingThatYieldsAFoo().aStaticMethod(); // very bad
07 Javadoc
/**
* 여러 줄의 Javadoc 텍스트가 여기에 작성됩니다.
* 일반적으로 래핑됩니다 ...
*/
public int method ( String p1 ) { ... }
/ ** 특히 짧은 Javadoc입니다. * /
- /** 다음은 공백
- 문단과 문단 사이 공백
- @이 시작하기 전에 공백라인
- @param, @return, @throws, @deprecated 순으로 사용
- 설명은 무조건 기술해야 하며, 한 문장을 넘어가면 4개 이상의 스페이스로 들여쓰기
마치며
우와.. 찾아보니까 내용이 엄청 많아서 너무 놀랐습니다..
그래도 자바 컨벤션 규칙에 대해서 알아보기 전에는 단순히 코드를 깔끔하게 구현하기 위해서라고 생각하고 별로 중요하게 생각 안해왔던 부분이였는데, 이런 부분들이 나중에 협업을 진행하는 과정에서 정말 중요할 거 같더라구요!
오늘을 기회 삼아 또 한가지 새로움을 느낄 수 있어 너무 뿌듯하네요.. 후후
이번 포스팅을 시작으로 앞으로 한 달간 우테코 프리코스에 더더욱 몰입해보고 싶어졌어요 !
이렇게, 오늘의 길고 긴 포스팅을 마치도록 하겠습니다.
다음 포스팅에서 만나요 :)
📜 참고 문헌
'Java' 카테고리의 다른 글
Refactoring은 극단적으로 해볼수록 더욱 성장한다 ! (feat. TDD) (0) | 2024.05.14 |
---|---|
[Java] OOP - 메세징(메세지 전송)이란? (1) | 2023.11.03 |