-
Notifications
You must be signed in to change notification settings - Fork 0
[Rule] Code Convention
Bear edited this page Apr 26, 2023
·
2 revisions
몇가지를 제외한 대부분 경우는 sun의 java code convention를 따른다. https://www.oracle.com/technetwork/java/codeconvtoc-136057.html (무려 1999년도에 만들어진 문서)
- CodeStyle.xml 다운로드 (https://dgdeu18iqgapt.cloudfront.net/gleaners.xml)
아래 설명에 대한 intellij 설정은 문서 참조
인코딩
- UTF-8 인텐드
- 4 Space
- Tab 사용 금지 (tab은 몇몇 소스 비교 툴에서 8 스페이스로 번역되어 코드가 엉망이 된다.) 한 줄 최대 길이
- 120 줄이 넘어갈 때 operator는 새로운 줄의 앞에 둔다.
String xxx = "1234567890123456789012345678901234567890 "
+ a1 + " " + "1234567890"
코드 섹션을 주석 수평선을 그어 구분하고 싶을 때, 대 단위 구분은 '=' 문자를 쓰고, 그 외에는 중/소 단위는 '-'를 쓴다. 수평선은 120까지 늘인다.
// =========================================================
// 작업 1 시작
// =========================================================
do something
// -----------------------------------------------------
// 작업 1-1 시작
// -----------------------------------------------------
do something
// -----------------------------------------------------
// 작업 1-2 시작
// -----------------------------------------------------
do something
중괄호 ( 브레이스 )
- 브레이스의 시작 라인은 새로운 라인에 작성하지 않는다.
public void xxx()
{
if () {
처리
} else {
처리
}
while (1) {
처리
}
for (;;) {
처리
}
.
try () {
}
catch (Exception e) {
}
}
- if else 문의 내용이 한줄일 경우는 브레이스 생략 가능하다. (이 경우는 개발자가 하고 싶은대로 하면 된다.)
- 중요! 반복문은 내용이 한줄이여도 강제 브레이스 사용. (한줄일 경우 브레이스 미 사용으로 발생할 수 있는 난독을 최소화 하자는 취지 → 반복문은 실수 하면 피해가 커짐) 네이밍
- camel case를 사용한다.
- boolean 타입 변수 혹은 boolean을 리턴하는 메소드는 "is" 혹은 "need" prefix로 시작한다. 띄어 쓰기
- 연산자는 앞뒤로 무조건 한칸씩 띄운다.
- 캐스팅 연산자의 괄호 뒤는 띄운다. 예: (int) num
- 제어문 뒤와 시작 괄호 사이에 한칸 띄운다. (단 메소드는 메소드 이름과 괄호를 붙여 쓴다.)
- 메소드 파라미터에 콤마 뒤에 한 칸 띄운다.
- not 연산자는 붙여서 사용 (가독성을 위해 intellij에서 operator color 변경 추천 : Editor -> ColorSchema -> Language Defaults -> Operation Sign 색상 변경)
- 한줄 주석 // 뒤에 한칸 띠운다. 연산자 비교 연산
- 비교 연산이 복잡해 지는 경우, 논리 연산자 끼리는 무조건 괄호를 사용하여 단위별로 묶는다. (연산자 우선순위가 있지만, 실수를 줄이고 가독성 향상을 위하여 무조건 묶는다.)
if (((x > 1) || (x < 10)) && ((y > 1) || (y < 10)))
- 할당 연산자에 비교 연산의 결과를 넣는 경우 괄호로 묶는다.
boolean isBoy = (age < 18);
static import 사용금지 (junit 제외)
- 클래스 이름은 나름 일종의 설명인데, 이 설명을 생략한다는 것은 작명 컨벤션을 부정하는 것이다.
- static import가 가독성을 높여 준다는 주장들은 동의할수가 없다. 가독성과 유지보수를 어렵게 만드는 주범중의 하나다.
- oracle도 매우 조심하라고 경고하고 있다.
-
- https://docs.oracle.com/javase/8/docs/technotes/guides/language/static-import.html (Very sparingly! 부분 참조)
- 우리 팀에서는 junit 한해서 사용가능.
- junit에 static import는 거의 통념이 되가고 있기 때문이다. Optional 사용 제한
- Optional은 메소드의 리턴으로만 사용 가능하다.
-
- 메소드 체이닝에 속한 메소드가 null을 반환하여 NPE가 발생하는 경우를 방어할 때만 사용하자. (예: 자바8 stream은 메소드 체이닝 패턴으로 설계 되었기 때문에 Optional을 반환하는 메소드가 많이 있다.)
-
- int, long, double 같은 주요 타입에서 사용할때는 OptionalInt, OptionalLong, OptionalDouble 사용할 것 (Optional에 Integer 넣으면 두번 boxing 하는 개념이기 때문에 더 무겁게 된다.)
- Optional 사용 금지하는 경우
-
- 맴버 변수로 사용 금지 (Optional은 serialize가 안됨)
-
- 단순 null 체크를 위한 사용 금지 (Optional은 무겁다.)
-
- Optional에 빈 컬렉션 넣지 말 것
-
- 컬렉션 원소에 Optional 사용 금지 (map, list, set 모두 포함)
-
- 메소드(생성자 포함) 인자로 Optional 사용 금지
- 참고
-
- 이펙티브 자바(3th) - item 55
- TODO FIXME 두 종류만 사용.
// TODO:
앞으로 해야할 일
반드시 자세한 설명을 넣어야 함
// FIXME:
고쳐져야하는 코드
반드시 자세한 설명을 넣어야 함
메서드 시그니처 및 호출 부 포매팅 관련
- 메서드 시그니처 혹은 호출 부의 파라미터가 길어질 경우 아래와 같이 처리를 한다.
---- 메서드 시그니처 ----
// DO NOT USE THIS
public void foo1(0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057)
{
... logic ...
}
// USE THIS
public void foo1(
0x0051,
0x0052,
0x0053,
0x0054,
0x0055,
0x0056,
0x0057)
{
... logic ...
}
---- 호출 부 -----
// DO NOT USE THIS
foo1(0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057);
// USE THIS
foo1(
0x0051,
0x0052,
0x0053,
0x0054,
0x0055,
0x0056,
0x0057);
메서드 체이닝 사용 시에 한 줄에 점(.) 1개만 허용한다
// DO NOT USE THIS
a().b().c();
// USE THIS
a().b()
.c();
생성자 주입
- 생성자 위에 무조건 명시적으로 @Autowired를 달아준다.
-
- 이유: 스샷에 설명한 것 처럼 실수로 @Service가 제외된 경우 바로 알수 있게된다.
- 롬복의 @RequiredArgsConstructor를 사용할 시에는 무조껀 변수에 private final을 선언하여 처리한다.
-
- 또한, 해당 어노테이션을 POJO, DTO 클래스에서 사용을 절대 금지한다.
- 생성자의 파라미터는 한줄에 한개만 들어갈 수 있게 개행한다.
-
- 이유: 스샷의 빨간 표시처럼 IDE의 도움을 받기 쉽게된다.
- 링크
DB는 약어 유지 새로작성하는 코드의 클래스/메소드/필드명은 관용어(ctlg, cate) 또는 "특별한 약어 필요경우"를 제외하고는 풀네임 사용이 원칙
- WSIN 사용 관용어 (추가가 필요한 경우 아래에 적어주세요)
-
- 카탈로그 - ctlg
-
- 카테고리 - cate 기존 코드는 작업 있을 경우 리팩토링 작명 예
- 상품 이라는 단어를 클래스로 만들경우, DB의 용어 사전이 prod 라도 클래스명은 Prod가 아닌 Product를 사용
- 상품명이라는 단어를 속성으로 만들경우, DB의 용어 사전이 prod_nm이라도 prodName으로 사용 DB 를 제외한 네이밍은 약어 지양, 직관적으로 풀어 정리 Date/Time 관련
- LocalTime: ~Time (단어+Time의 조합이 명사)
- LocalDate: ~Date (단어+Date의 조합이 명사)
- LocalDateTime: (과거분사)~At
- 기간에 대한 변수는 ~From, ~To를 붙인다. ex) collectorFromDate, collectorToDate Serializable 인터페이스를 최대한 구현하지 않는다.
- 이펙티브 자바 규칙74 : Serializable 인터페이스를 구현할 때는 신중하라
- 예외) 라이브러리 자체의 직렬화 / 역직렬화 방식이 존재하지 않을 경우에만 예외적으로 허용
-
- e.g) Ehcache
서버 시간으로 사용할 경우 서버가 여러대일 때, 서버간 시간을 동기화 해주는 이슈가 발생할 수 있음
단위테스트 : 변경사항 발생시, 다른 곳에도 영향이 있는 상태일 경우 단위테스트를 적용
- 모든 컴포넌트에 단위테스트를 진행하는 것이 아닌, 변경사항발생시 영향이 있는 경우에 사용
- 단위테스트시 DB Insert하는 테스트를 Before로 지정하고, 테스트를 생성하면 수정, 삭제, 검색등의 테스트를 처리 가능 통합테스트 : Controller 기준 모든 메서드에 대해서 적용
- 통합테스트는 리팩토링, 구조 변경시 기능이 정상적으로 동작하는 상태임을 보장하는 것이 목적임 테스트의 실행 시간은 400ms(vpn을 사용할경우 1초)를 넘지 않도록 한다 테스트 내부에 log()를 사용하고, print()는 사용하지 않는다.
지역 상수
- 하나의 메서드 내부에서만 사용
- 변할 가능성이 없는 상수 Class 상수
- 두 개 이상의 메서드에서 사용
- 변할 가능성이 있는 상수 (ex. path, size 등) interface static final
- 전반에 걸쳐 공통으로 사용되는 상수
메서드의 파라미터 개수가 4개 이상이면 Model (DTO, VO)를 만들어 전달한다.
https://projectlombok.org/features/experimental/FieldDefaults final 의 경우 makefinal = true 와 @Nonfinal 을 활용 한다.
@Getter
@Setter
public class Pipe implements Serializable
{
private String pipeId;
private int mallId;
private String status;
private String productDisplay;
private String allUrl;
private String summaryUrl;
private int isCrawling;
private String dataProviderType;
private String charset;
private int productType;
private String goodsType;
private String currencyType;
private int scheduleAllHour;
private int scheduleAllMin;
private int scheduleSummaryHour;
private int scheduleSummaryMin;
}
@Getter
@Setter
@FieldDefaults(level= AccessLevel.PRIVATE)
public class Pipe implements Serializable
{
String pipeId;
int mallId;
String status;
String productDisplay;
String allUrl;
String summaryUrl;
int isCrawling;
String dataProviderType;
String charset;
int productType;
String goodsType;
String currencyType;
int scheduleAllHour;
int scheduleAllMin;
int scheduleSummaryHour;
int scheduleSummaryMin;
}
@Getter
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class Test
{
@NotNull
String reviewId;
@NotNull
String pipeId;
@NotNull
String productId;
String buyOption;
@NotNull
Integer mallId;
@NotNull
String mallReviewId;
@NotNull
String mallName;
@NotNull
String storePid;
@NotNull
String storeUserId;
String title;
@NotNull
String content;
@NotNull
String reviewLink;
String displayYn;
@NotNull
Integer productSatisfyLevel;
@NotNull
@JsonProperty("registered_date")
Long registeredAt;
@NotNull
@JsonProperty("updated_date")
Long updatedAt;
@NotNull
String documentHash;
@NonFinal
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
List<Image> images;
}
@FieldDefaults(level = AccessLevel.PRIVATE)
@Getter
@NoArgsConstructor
public class ReviewEP
@Getter
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ReviewEP
Ep → EP ReveiwEp → ReviewEP