두레 백엔드 컨벤션 정하기

BDD·2024년 1월 21일
0

해당 글은 팜🫧이 작성했습니다.

1월 1일부터 시작된 1주일간의 기획이 종료되었습니다. 기획이 끝난 후 본격적인 프로젝트 개발에 들어가기 전 필요한 기반을 다지기 위해 백엔드 팀에서는 아래와 같은 작업을 선행했습니다.

  1. uri 설계
  2. 백엔드 컨벤션
    • 아키텍처 및 폴더구조
    • 예외처리
    • 깃허브 컨벤션(백엔드)
      • discussion,위키,project 사용 방법
      • pr,issue 템플릿 생성
      • 라벨 생성
    • 코드 컨벤션
      • 코드 스타일
      • 테스트 코드 규칙
  3. 기술스택 정하기
  4. 엔티티 설계(ERD)
  5. 인프라 설계
  6. 메인코드 작성

해당 포스팅에서는 이 중 백엔드 컨벤션에 대해 논의되었던 주제 몇 가지와 그 결론을 정리하려합니다.


폴더구조

도메인형 패키지 구조 vs 계층형 패키지 구조

도메인형 패키지 구조와 계층형 패키지 구조는 서로에 대한 장단점이 뚜렷한, trade off 관계에 있습니다.

  • 도메인형 패키지 구조
    • 도메인별 응집도가 높으나 계층별 응집도가 낮아진다.
    • 큰 규모의 프로젝트에도 계층형 패키지 구조에 비해 가시성이 좋다.

  • 계층형 패키지 구조
    • 도메인별 응집도가 낮으나 api 흐름이나 비즈니스 로직같은 계층내 흐름을 한 패키지 내에서 확인할 수 있어 프로젝트 구조 파악이 쉽다.
    • 규모가 커질 경우 한 패키지 내에 여러 도메인들이 섞여있어 다소 난잡하게 느껴질 수 있다.

두레(DooRe)의 백엔드 팀은 전원이 개발 단계 전 이루어진 기획에 참여했기에 프로젝트 구조 파악이 어려운 도메인형 패키지 구조의 단점이 크게 느껴지지 않았습니다. 무엇보다도 ERD 설계에 예상보다 많은 도메인이 사용되었기 때문에 계층형 패키지 구조를 사용할 경우 한 패키지 내 다수의 클래스가 위치될 것으로 예상되어 도메인형 패키지 구조를 선택했습니다.

DTO 패키지 위치

ㄴ study
|   ㄴ controller
|   ㄴ service
|   |   ㄴ dto
|   ㄴ domain
|   |   ㄴ repository
|   ㄴ exception

패키지 구조를 결정한 이후 dto의 위치에 대한 논의가 진행되었습니다. 이를 위해, dto의 사용범위에 대한 논의가 추가적으로 진행되었습니다.

  • dto를 Controller까지 사용할 경우
    • Controller에서 Service로 넘겨줄 필드가 많을 경우 모두 getter를 사용해 넘기거나 Controller와 Service간 새로운 dto를 생성할수밖에 없다.
    • Controller가 Service로부터 Entity를 반환받아 Domain이 Controller에게 노출된다.

  • dto를 Service까지 사용할 경우
    • Service가 특정 DTO에 종속되어 Service의 모듈화가 어려워진다.
    • DTO가 변경될때 Service 계층이 변경될 가능성이 높아진다.

dto의 사용 범위에 대해서는 정해진 답이 없기에, dto를 Controller까지만 사용할 경우에 대한 단점을 받아들여 dto를 service 계층까지 사용하는 걸로 결정되었습니다.
이때 controller 패키지에 dto를 위치키시는 경우 service가 dto를 사용하기 위해 controller에 의존하게 되므로 Controller → Service → Domain의 계층간 의존성을 지키기 위해 dto는 service 계층에 위치시키는 것으로 결정했습니다.

또한 위와 동일한 논리로 service ↔ repository간 dto를 공유하는 QueryDSL과 같은 프레임워크를 사용하는 경우에 대해선 repository에 dto를 위치시키기로 했습니다.


테스트 코드 작성 규칙

Test Fixture 사용

테스트 픽스쳐
유사한 객체를 생성하는 여러개의 테스트가 있는 경우에 대해 중복코드 발생을 줄이기 위해 사용하는 개념

테스트 픽스처는 하나의 개념으로 적용 방식은 상황에 따라 달라질 수 있습니다. 두레 백엔드팀에서는 테스트 픽스처에 대한 아래 3개의 사용방식이 언급되었습니다.


  1. 테스트 픽스쳐 메서드 사용
    • @BeforeAll: 테스트 메소드 시작 전 1번 실행
    • @AfterAll: 테스트 메서드 종료 후 1번 실행
    • @BeforeEach: 각 테스트 메소드 시작 전 실행
    • @AfterEach: 각 테스트 메소드 종료 후 실행

2. 객체 생성자를 사용한 테스트 픽스처

    public class EventFixture {
      public static Event 인프콘_2023() {
        return new Event("인프콘 2023", "코엑스", LocalDateTime.parse("2023-06-01T12:00:00"),
            LocalDateTime.parse("2023-09-01T12:00:00"), LocalDateTime.parse("2023-05-01T12:00:00"),
            LocalDateTime.parse("2023-06-01T12:00:00"), "https://~~~", EventType.CONFERENCE,
            PaymentType.FREE_PAID, EventMode.ON_OFFLINE, "인프런"
        );
      }
    }

객체 생성자를 사용한 테스트 픽스쳐는 자주 사용될 객체의 케이스를 미리 정의해두고 필요할때 생성하여 사용하는 방식입니다.
이와 같이 텍스트 픽스처를 사용하면 객체 생성시 사용되는 코드가 굉장히 짧고 단순하며, 순수한 객체만을 반환할 수 있다는 장점이 있습니다.
그러나 이러한 방식은 컬럼 수가 많은 테이블의 경우 필드 구분이 어려워진다는 단점이 있습니다.

  1. 빌더 패턴에 적용된 테스트 픽스처

    public MeritType build() {
          return meritTypeRepository.save(MeritType.builder()
              .merit(merit != null ? merit : 3)
              .isMerit(isMerit != null ? isMerit : true)
              .detail(detail != null ? detail : UUID.randomUUID().toString())
              .build());
        }

    텍스트 픽스처에 빌더 패턴에 적용해 사용할 수도 있습니다.이와 같은 테스트 픽스처 사용은 값을 넣지 않은 필드에는 디폴트 값을 반환하고, 변경을 원하는 필드에 대해서는 개발자가 직접 값을 입력할 수 있어 객체 생성이 유연해진다는 장점이 있습니다.
    그러나 이러한 빌더 패턴을 사용하기 위해서는 상당히 긴 코드 작성이 필요하다는 단점이 있습니다.


위 세개의 테스트 픽스처 사용 예시 중 객체 생성자를 사용한 테스트 픽스처빌더 패턴에 적용한 테스트 픽스처는 두레 백엔드 팀원 일부가 서로 다른 프로젝트를 진행하며 사용해본 경험이 있는 테스트 픽스처로, 이 두 방식 중 무엇을 쓸것인지에 대한 논의가 이루어졌습니다.

프로젝트 ‘두레’는 이제 처음 시작하는 프로젝트로 구분이 어려울 정도로 필드가 많은 테이블이 많지 않습니다. 초기 코드 작성 시부터 컬럼 확장을 염두에 둘 필요가 있으나, 컬럼 확장을 염두에 둔 빌더 패턴 사용이 결국 오버엔지니어링으로 이어질지에 대한 점은 알 수 없었기 때문에 긴 시간을 투자해 논의했습니다.


위 두 방법 모두 서로 다른 부분에서 장점을 가지고 있었기에 두레 백엔드 팀은 위 두 패턴의 장점만 취해 사용하기로 결정했습니다.

  1. 빌더 패턴을 사용해 필드 값을 유연하게 변경할 수 있도록 합니다.
  2. DB에 저장하는 과정을 거치지 않고 객체를 반환하도록 합니다.
  3. 대표적으로 많이 사용되는 객체는 객체 생성자를 사용한 테스트 픽스쳐로 미리 생성합니다.

테스트 단위

이후 테스트 단위에 대해 논의했습니다. 테스트에 대해서는 API 문서 테스트, 통합 테스트, 단위 테스트, 그리고 Pojo 테스트의 4가지 테스트를 진행하기로 결정했습니다.

API 문서 테스트
API의 명세와 문서화를 검증하기 위한 테스트로 주로 Spring REST Docs나 Swagger와 같은 도구를 사용하여 API에 대한 문서를 작성하고 자동으로 생성한다.

두레 백엔드 팀에서는 REST Docs를 사용하기로 했습니다. Rest Docs는 Swagger에 비해 설정이 까다로운 것에 비해 Spring MVC와 통합이 뛰어나며 테스트 코드를 통해 API 호출을 테스트하므로 더 높은 정확도를 보여줍니다.

통합 테스트

  • HTTP 수동 테스트는 HTTP 요청과 응답을 수동으로 생성하고 검증하는 방식의 테스트로, 작성이 매우 쉽고 간단하며 API를 직접 실행시키는 것으로 높은 신뢰도를 보여줍니다.
    그러나 자동화된 테스트가 아니기 때문에 순서를 보장하기 위해서 사용자가 직접 순서를 맞춰 실행해야하며, 테스트를 위해 Application을 실행해햐 하는 단점이 있습니다.
  • API 통합 테스트는 수동 테스트에 비해 코드 작성에 시간이 드나 자동화된 테스트가 가능하다는 장점이 있습니다. 또한 HTTP 수동 테스트는 유효성, 권한, 커스텀 어노테이션 테스트에 있어 한계가 있으나 완전히 자동화 된 API 통합 테스트로 해결할 수 있습니다.

위와 같은 장단점을 비교해본 결과, API 통합 테스트를 사용하기로 했습니다. API 통합 테스트는 HTTP 수동 테스트에 비해 까다로우나 백엔드 인원 대부분이 API 통합 테스트 경험이 있기에 학습 비용이 크지 않을 거라 판단했습니다.

profile
부산 개발 동아리

0개의 댓글