왜 필요한가?
팀원들과, 특히 백엔드끼리 협업을 하면서 설계를 끝내고 구현으로 들어가면서
가장 먼저 맞닥들인 문제는 “컨벤션” 이었습니다.
같은 프로젝트의 코드를 건드리는 팀원의 입장에서 다른 사람이 쓴 코드를 이해하는 데에 시간이 걸리거나
하다못해 DTO 의 클래스 이름부터가 뭐하는 DTO 인지 알기 어렵다면,
같이 코드를 작성하는 데에 불편함이 있습니다.
그래서 프로젝트를 같이 하는 팀원과 컨벤션을 정하고 코드 작성을 시작하기로 했습니다.
코드 스타일에 표준이라는 것은 없지만,
팀 내부에서 같은 코드 스타일을 유지하는 것이 작업 효율을 높일 것이라고 생각했습니다.
유지보수의 관점에서도 그렇고요 !
제가 코드를 계속 작성하고 고쳐나갈 수 있다면 좋겠지만,
누군가 새로운 사람이 투입됐을 때 어떤 코드 규칙이 있다면
그 엔지니어가 코드를 이해하는 데에 도움이 될 것이라고 생각했습니다.
코딩 컨벤션이란
프로그래밍 언어는 말 그대로 언어. 한국어, 영어와 비슷하다고 생각합니다.
지역마다 사투리가 있듯 프로그래밍에서도 스타일이라는 것이 존재합니다.
스타일이 다르다는 것은 틀린 것은 아니지만, 통일된 기준으로 코드를 작성해야 소통이 원활합니다.
어떤 코딩 컨벤션을 선택한다는 것은 “우리는 경상도식 사투리로 가자” 는 것과 유사한 것 같습니다.
1. IDE 를 활용해 코딩 컨벤션 적용하기
저희 팀은 IntelliJ + mac OS 환경이었기 때문에 그 기준으로 작성해보았습니다.
여기서는 Naver Campus Hackday 컨벤션을 사용했습니다.
아래 링크에서 xml 파일을 다운로드하거나 xml 파일을 만들고 내용을 복붙하면 됩니다.
IntelliJ 에서 preferences
-> Editor
-> code style
로 들어가서 적용.
이제 코드를 작성하고 command
+ option
+ L
을 누르면 자동으로 코드가 포맷에 맞게 수정됩니다.
+ 추가
IntelliJ 에 있는 CheckStyle 이라는 플러그인을 이용하면 룰에 어긋나는 코드에 대해 알림 메시지를 띄워줍니다.
투머치라고 생각되면 스킵 ㅎ.
install 하고 나면 preferences
-> Tools
→ CheckStyle
→ +
버튼을 선택해서
아래 링크에 있는 파일을 저장 후 새로운 CheckStyle 을 생성합시다.
https://github.com/naver/hackday-conventions-java/blob/master/rule-config/naver-checkstyle-rules.xml
아마 Property 로 suppressionFile 을 설정하라고 뜰텐데,
검사 대상에서 제외할 파일을 선택하는 설정입니다.
제외 대상이 없이 그대로 진행하면 에러가 뜨니, 저는 이 설정을 삭제하고 진행했습니다.
삭제하는 방법은 naver-checkstyle-rules.xml 파일을 열고 아래 부분을 삭제하면 됩니다.
이제 생성한 CheckStyle 을 선택하고 Apply
.
CheckStyle 이 적용되면 자동으로 룰에 어긋나는 부분을 보여줍니다.
하단 CheckStyle 탭을 보면 아래에 메시지가 뜹니다.
너무 보기 싫다면 설정에서 Active 를 꺼두면 됩니다.
위에서 초록색 재생 버튼을 클릭하거나 파일을 열고 우클릭해서 Check Current File
을 클릭하면 검사를 실시합니다.
2. 네이밍 결정하기
- 변수는 CamelCase 를 기본으로 함.
- userEmail
- URL, 파일명은 kebab-case 를 사용함.
- /user-email-page
- 패키지명은 단어가 달라지더라도 무조건 소문자를 사용함.
- frontend, useremail
- ENUM 이나 상수는 대문자를 사용함.
- NORMAL_STATUS
- 함수명은 소문자로 시작하고 동사로 네이밍함.
- getUserId(), isNormal()
- 클래스명은 명사로 네이밍하고 UpperCamelCase 를 사용함.
- UserEmail
- 객체 이름을 함수 이름에 중복해서 넣지 않음.
- line.getLength()
O
/ line.getLineLength()X
- line.getLength()
- 컬렉션은 복수형을 사용하거나 컬렉션을 명시해줌.
- List ids, Map<User, Integer> userToIdMap
- 이중적인 의미를 가지는 단어는 지양.
- event, design
- 의도가 드러난다면 되도록 짧은 이름을 선택.
- retreiveUser()
X
/ getUser()O
- retreiveUser()
- 함수의 부수효과를 설명.
- 위 함수의 경우 단순히 order 만 가져오는 것이 아니라, 없으면 생성해서 리턴
- 따라서 getOrder()
X
/ getOrCreateOrder()O
public Order getOrder() {
if (order == null) {
order = Order()
}
return order;
}
- LocalDateTime → xxxAt, LocalDate → Dt 로 네이밍
- 객체를 조회하는 함수는 JPA Repository 에서 findXXX 형식의 네이밍 쿼리메소드를 사용하므라
개발자가 작성하는 Service 단에서는 되도록 getXXX 를 사용.
저희 팀은 위 항목들을 기본적으로 지키기로 약속했습니다.
구체적으로 Controller 와 Service 에서 사용할 메소드의 네이밍은 아래 규칙을 따르기로 했습니다.
Controller
orderList()
: 목록 조회orderDetails()
: 단건 상세 조회orderSave()
: 등록/수정/삭제가 동시에 일어나는 메소드orderAdd()
: 등록orderModify()
: 수정orderRemove()
: 삭제
Service
getOrders()
: 목록 조회getOrder()
: 단건 조회addOrder()
: 등록modifyOrder()
: 수정removeOrder()
: 삭제saveOrder()
: 등록/수정/삭제가 동시에 일어나는 메소드
DTO
도메인별로 상위 DTO 를 하나 만들어서 Inner Class 들로 관리.
객체지향 관점에서는 중첩해서 만드는 것이 안티 패턴일 순 있지만 Req/Res 에 사용하는 DTO 이기 때문에,
저희팀은 예외적으로 허용하기로 했습니다.
User 도메인에서 이를 관리하는 경우 아래와 같은 방식
public class User {
@Getter
@AllArgsConstructor
@Builder
public static class Info {
private int id;
private String name;
private int age;
}
@Getter
public static class Request {
private String name;
private int age;
}
@Getter
@AllArgsConstructor
public static class Response {
private Info info;
private int returnCode;
private String returnMessage;
}
}
이미 프로젝트에서 CommonResponse
객체를 만들어서 사용하고 있다면,
도메인 DTO InnerClass 에 Request
, Response
등의 네이밍은 지양합니다.
참고글 ⬇️
https://google.github.io/styleguide/javaguide.html
https://naver.github.io/hackday-conventions-java/