[1차 Mybatis-ver 프로젝트 회고] 프로젝트 컨벤션 회고 (DB, Java, MyBatis, SpringBoot, Swagger)

Ogu·2024년 5월 3일
2
post-thumbnail

1차 프로젝트에서 효율적이고 통일성 있는 협업을 위해 여러 컨벤션을 정의하고 적용해 보았다.
이번 포스팅에서는 각 컨벤션들에 대해 KPT 방식으로 회고해보려고 한다.

DB 네이밍 컨벤션

우리 팀에서 사용한 DB 네이밍 컨벤션은 다음과 같다.

  1. 모든 이름은 소문자로 작성한다.
  2. 단어 사이는 underscores(_)로 구분한다. (Snake Case)
  3. 날짜 필드는 '_at' 접미사를 사용한다.
  4. 수량은 '_count' 접미사를 사용한다.
  5. 키에 해당하는 필드는 '_id' 접미사를 사용한다.
  6. 금액은 '_amt' 접미사를 사용한다.
  7. 축약어를 지양하고, full name으로 명시한다.
  8. 그럼에도 전체 단어보다 통용되는 약어는 사용한다. (ex. biz_no, zip_no)

🦕 KEEP

1. 통일된 네이밍으로 가독성 향상
가독성은 개발 효율성을 위해 매우 중요하며 퍼포먼스와도 직접 연결된다. 주석을 쓰는 이유도, 여러 다양한 컨벤션을 정해 사용하는 이유도 그러한 이유 때문이다.
이전에 약어를 많이 사용하여 한눈에 읽히지 않는 어려움이 있었는데, 이후 논의를 통해 7,8번 규칙을 추가하고 난 후 네이밍은 길어졌지만 가독성이 훨씬 좋아졌다.

🔍 Problem

1. 자바의 camelCase 문법과 맞지 않는다.
때문에 Mybatis에서 @NoArgsArgument(기본 생성자) 로 매핑 시 이를 인식하지 못하는 문제가 있었지만, Mybatis에서 이를 자동으로 변환해 주는 설정 방법을 알게 되어 적용하여 해결하였다.

💡Try

이외에 아직 크게 별 다른 문제점을 느끼지 못했다.

Java 컨벤션

마찬가지로 코드의 가독성을 위해 IntelliJ의 기능을 이용해 Java 컨벤션을 적용하였다.
Naver의 캠퍼스 핵데이 Java 코딩 컨벤션 을 InteliJ 네이버 Java 코딩 컨벤션 적용하기 + Checkstyle을 참고하여 적용해 진행하였다.

🦕 KEEP

1. 강제적인 코드 스타일 수정
Java 컨벤션의 경우 매번 크게 의식을 들여 작성하기 어렵다. IntlliJ의 기능을 이용하여 저장시 마다 강제적으로 코드 컨벤션을 자동 적용하여 수정되도록 강제하니 매번 크게 의식을 하지 않아도 되어서 편리했다.

🔍 Problem

1. 원치 않는 컨벤션 자동 적용
위와 같은 편리함에도 불구하고 저장시마다 강제적으로 전부 코드 포맷을 바꿔 버리는 것은 불편했다. 팀원들이 원치 않는 컨벤션이 있었기 때문인데, 다음과 같은 것들이 있었다.

  • 원치 않는 자동 개행
  • 메서드 체이닝의 탭 간격이 default가 4이지만 팀원들은 기존에 8을 사용함(가독성 때문에)

💡Try

1. 자동 적용 기능 -> 단축키 사용시 포맷팅 적용 / 지정된 규칙에 어긋나는 경우 컴파일 시 경고나 에러
그동안은 직접 들여쓰기 간격이나, 개행을 원치 않는 부분에도 자동으로 적용이 되어 불편했다. 따라서 자동 적용 기능은 해제하고, 단축키 상용이나, 경고 표시로 대체하도록 한다.

2. 커스터마이징
항상 적용되길 원하는 들여쓰기 간격 같은 경우 직접 몇가지 항목을 커스터마이징하기로 한다.

MyBatis 매핑 컨벤션


이미지 출처 : [CSBlog] MyBatis Mapper를 활용해 Post 기능 구현하기

Mybatis를 처음 사용하며 프로젝트를 진행중에 몇가지 컨벤션을 정하고 적용했다.
컨벤션은 다음과 같다.

  1. Mapper 인터페이스의 이름은 ~Repository
  2. ${OOOMapper}.xml 파일 네이밍, 경로는 resources > mappers
  3. 결과 매핑 클래스의 변수 네이밍은 camelCase
  4. 기본생성자(@NoArgsConstructor)로 매핑

추가적으로 계속 매핑 시 null 에러가 발생하여 원인을 찾고 3,4번을 정의하기까지의 우여곡절이 있었다.
[Spring + MyBatis - 🛠️TroubleShooting] Mybatis의 생성자 객체 매핑 방법
[MyBatis - 🛠️TroubleShooting] Mybatis에 camelCase 변환 적용하기

🦕 KEEP

  1. Mapper 인터페이스의 명명 규칙 유지
    구글링과 여러 프로젝트를 참고한 결과, Mapper 인터페이스를 ~Mapper 로 명시하는 경우, ~Repository 로 명시하는 경우의 수가 거의 반반이었다.
    하지만 기존에 사용하던 ORM 기술의 네이밍과 혼동을 줄이기 위해 ~Repository 네이밍을 채택했다.

  2. 표준화된 ${OOOMapper}.xml 파일 경로와 네이밍 컨벤션

  3. camelCase 변수 네이밍: Java의 표준 코딩 컨벤션 유지

🔍 Problem

  1. SQL에 의존적, 디버깅의 어려움
    Mybatis에서는 디버깅 시 Repository -> Mapper.sql 간의 에러를 찾는 것이 매우 어려웠다. 이는 Mybatis가 SQL Mapper이기 때문에 어쩔 수 없이 sql에 의존적인 성향을 띄기 때문인데, 그만큼 로직 변경마다 꼼꼼히 보고 신경을 써야 했다.

💡Try

  1. 올바른 디버깅
  2. 복잡한 계층 구조의 생성자 매핑, 조인은 <ResultMap> 사용
    복잡한 계층 구조나 Join 쿼리의 생성자 매핑은 위의 규칙대로만 적용되지 않아 ResulyMap 으로 각 필드와 데이터베이스 칼럼 간의 매핑을 정의하여 사용한다.

SpringBoot(Java) 컨벤션

  1. Message DTO 사용
  2. 생성자 주입으로 의존성 관리: @RequiredArgsConstructor 사용
  3. 모든 메서드는 Get > Post > Put(patch) > Delete
  4. Controller 반환 타입을 ResponseEntity로 통일
    • @GetMapping : ResponseEntity<ResponseDTO>, HttpStatus.OK와 ResponseEntity의 body에 responseDTO 객체 담아 반환
    • @PostMapping 의 경우 HttpStatus.CREATED다음 경로의 URI를 Location에 명시
    • @PutMapping, @PatchMapping : ResponseEntity<MessageDTO>, HttpStatus.OK와 ResponseEntity의 body에 MessageDTO 담아 반환
    • @DeleteMapping 의 경우 ResponseEntity<Void>, HttpStatus.NO_CONTENT 반환
  5. 모든 PostMapping의 RequestDTO 유효성 검사
  6. Oprional로 null 처리
  7. Test코드에서 Mockito의 mock 객체 사용
  8. Layer간 DTO를 사용한 데이터 교환으로 엔티티를 캡슐화

🦕 KEEP

  1. RESTful에 가까운 설계, 상태 전이
    완전히 RESTful한 API는 아니지만, 그에 가까운 설계를 하려고 노력했다.
    HATEOS에 대해 고민해보고, Hypermedia(링크)를 통해서 애플리케이션의 상태 전이가 가능하도록 했다.

    NAVER | 그런 REST API로 괜찮은가 를 보고 나름 고민하여 API 설계를 거듭 수정하였다.

  2. 응답 생성 로직의 일관성
    모든 API 메소드에서 ResponseEntity를 반환하여 응답 생성 형식에 일관성이 유지되었다.

  3. HTTP 상태 코드의 적절한 사용
    각 HTTP 메서드 상황에 맞는 상태코드를 적절히 반환하여 사용자 경험을 개선하고, 클라이언트와 서버 간의 효율적인 데이터 교환을 지원한다.

  4. 일관된 순서와 규칙으로 가독성이 높아지고 코드 리뷰가 편리해짐

  5. Optional -> orElseThrow() 로 Null 처리 명시
    함수형 인터페이스를 통해 가독성이 좋고 일관되고 유연한 코드를 작성할 수 있고, 예외 상황을 명확하게 관리할 수 있었다.

🔍 Problem

1. URI 생성의 중복 코드
RESTful과 HATEOS에 가까운 API를 설계하려 노력했지만, 각 URI를 생성이 필요한 시점에 볼륨이 큰 중복적인 코드의 사용이 있었다.

2. 메서드 순서가 POST가 가장 앞인 프로젝트가 많은 이유?
위와 같이 Get > Post > Put(patch) > Delete 로 메서드 순서를 정했지만, 참고한 대부분의 프로젝트에서 Post > Get ~ 순을 사용하는 이유에 대해 궁금해졌다. (우테코, SAFFY 등)

3. Optional의 이해 부족
여러 장점으로 Optional을 사용하고 있지만, 정확한 사용의 때와 이유를 제대로 이해하고 있지 못한 느낌이 들었다.

4. DTO도 불변 객체로 사용해야 하는가?
DTO는 기본적으로 가변 객체이지만, 불변 객체로 사용해야 한다는 몇몇 블로그의 글을 접했다.
DTO도 모든 상황에서 값이 변동되지 않는, 불변성을 띄어야 하는가? 에 대해서는 아직 잘 모르겠다.

VO와 DTO 는 다르게 부르는 같은 말?
[Java] Request DTO 불변 객체로 만들기 - JSON 역직렬화
DTO(DataTransferObject) vs. VO(Value Object)

💡Try

  1. URI Builder Utility 클래스 사용
    다음 경로의 URI를 Location에 명시 하는 코드에서, URI를 생성하는 로직이 꽤 사이즈가 큼에도 불구하고, 반복해서 사용되고 있었다.
    이러한 기능을 URI Builder 라는 별도의 Utility 로 로직을 분리하고, 필요한 곳에서 호출해서 사용하여 중복 코드를 줄이도록 한다.

  2. Optional 사용에 주의, 공부 더 필요
    Optional이 설계된 목적에 맞게 반환 타입으로만 사용하도록 한다.

    참고
    [Java] 언제 Optional을 사용해야 하는가? 올바른 Optional 사용법 가이드 - (2/2)
    자바 옵셔널(Optional) 완전 정복

3. DTO를 불변 객체로 사용할지에 대해 조금 더 고민해보기

Swagger

API 문서를 자동화를 위해 Swagger를 적용하였다.
API 구조, 응답, 요청 형식, 상태코드, 오류 메시지 등을 설정 파일, 어노테이션만으로 간단하고 편리하게 문서화 할 수 있다는 장점이 있다.

🦕 KEEP

  1. 자동으로 API 엔드포인트를 인식해 도메인별로 분리하여 적용할수 있다.
  2. UX/UI가 좋다.
  3. Swagger에서 Parameter로 실제 호출 및 테스트 가능

🔍 Problem

  1. 비즈니스 로직에서 벗어난 Swagger를 위한 어노테이션의 volumn이 너무 커 지져분해 보인다.

💡Try

  1. Rest Docs + Swagger
    위와 같은 단점을 보완하기 위해 Rest Docs와 Swagger 를 같이 사용하여 각각의 장점을 결합하는 방식이 있다고 한다.

Rest Docs에는 다음과 같은 장점이 있다.

  • API 문서가 테스트코드 통과 후 생성됨 (테스트 코드 강제)
  • 비즈니스 로직에는 API 문서 관련 코드가 전혀 없음

조금 더 방법에 대해 알아보고 적용할지 결정해보도록 한다.

돌아보며

컨벤션과 여러 규칙 및 기술 도입에 있어 왜 그 기술을 도입해야 하는지, 각각의 장단점은 무엇인지 확실히 살펴보는 것이 중요한 것 같다.

또한 프로젝트 중간 스프린트 단위나 주에 한번씩 전체적인 코드 리뷰를 거쳐 제대로 지켜지고 있는지, 의도한 대로 사용되고 있는지 점검해 보는 시간이 필요한 것 같다.

profile
私はゲームと日本が好きなBackend Developer志望生のOguです🐤🐤

0개의 댓글