[WIL] TDD & 지속 테스트 가능한 구조

2025. 7. 18. 13:11·WIL

기존의 생각 & 지식 & 습관

  • 테스트의 중요성은 어느정도 알고 있었지만, 테스트를 생각하며 프로덕션 코드를 작성하진 않았다.
  • Swagger를 적용하면, Controller에 Swagger 관련 코드가 많아져 코드를 읽기 어렵다.
  • 기능이 많아질수록 DTO 파일이 많아져, 파일 탐색이 어렵다.
  • 퍼사드는 서비스를 조립하는 역할이다.
  • FK를 쓰지 않는 이유는 운영 편의성을 챙기기 위해서다.
  • 로그는 문제가 발생하는 경우에 남긴다.

이번 주의 경험

  • TDD 방식으로 과제를 구현
    • 단위 테스트, 통합 테스트, E2E 테스트
  • API Spec을 인터페이스로 관리함으로써 버저닝
public interface UserV1ApiSpec {
  @Operation(
      summary = "회원가입",
      description = "사용자 정보를 입력하여 회원가입을 수행합니다."
  )
  ApiResponse<UserV1Dto.UserResponse> joinUser(
    @Schema(name = "회원가입 요청", description = "회원가입에 필요한 사용자 정보")
    UserV1Dto.JoinRequest joinRequest
  );
}

  • Inner Class 활용
public class PointCommand {
  public record Get(..) {}
  
  public record Charge() {}
}

public class PointService {
  public Point getPoint(PointCommand.Get command) {
    ..
  }
  
  public Point charge(PointCommand.Charge command) {
    ..
  }
}

  • Facade(application)과 Service(domain)의 정확한 구분
public class UserFacade {
  private final UserService userService;
  private final PointService pointService;

  public UserInfo getUserInfo(String loginId) {
      User user = userService.getUser(new LoginId(loginId));
      if (user == null) {
          throw new CoreException(ErrorType.NOT_FOUND, String.format("%s 사용자를 찾을 수 없습니다.", loginId));
      }
      Point point = pointService.getPoint(user.getId());
      return UserInfo.of(user, point);
  }
}

public class UserService {
  private final UserRepository userRepository;

  @Transactional(readOnly = true)
  public User getUser(LoginId loginId) {
      return userRepository.findByLoginId(loginId).orElse(null);
  }
}

경험을 통해 배운 것

  • TDD 방식으로 코드를 작성함으로써, 단순 검증 도구가 아닌 기능을 설계할 수 있었다.
    • 테스트를 작성하며 내가 어떤 방향으로 구현해갈지 먼저 생각하게 된다.
    • 리팩토링 시에도 테스트를 통해 기능 피드백을 받으며 보다 안전하게 진행할 수 있다.
  • API Spec을 인터페이스로 분리
    • 버전마다 다를 수 있는 API 스펙을 관리할 수 있다.
    • Swagger 관련 어노테이션을 인터페이스에 작성하여 Controller는 시스템 요청/응답에만 집중한다.
  • Inner Class를 통해 DTO 파일이 과하게 많아지는 것을 방지하고, 보다 효과적으로 관리할 수 있다.
  • 각 계층간의 명확한 책임 분리
    • Controller → 비즈니스 맥락 X 그냥 시스템 내/외부 입출력 담당
    • Facade → 비즈니스 유즈케이스. 맥락이랑, 시나리오를 잔뜩
    • Service → 도메인 수준의 기능 제공자 -> 재사용 가능한 CRUD
    • Repository → CRUD를 어디에 어떻게 할건지에 대한 명세를 제공
서비스
퍼사드
개인정보 조회 -> 유저를 조회해봤는데 없으면 사고
회원가입 -> 유저를 조회해봤는데 있으면 사고

서비스는 그냥 조회하기 기능을 주는 것
퍼사드에서 내가 "특정 기능을 위해 맥락을 씌울지 말지를 결정"
  • FK를 쓰지 않는 이유는 운영 편의성도 있지만, 데드락을 방지하기 위함이 크다.
트랜잭션 A가 자식 테이블에 insert하면서 외래키로 인해 부모 테이블을 잠그고,
트랜잭션 B는 부모를 update 후 자식에 insert하려 할 때, 서로 상대의 락을 기다리며 데드락이 발생한다.
  • 로그는 최대한 남기지 않는다.
    • logback을 사용하면 성능적 문제는 크지 않지만, 파일로 저장할 때 디스크 비용이 발생
    • 로그를 남김으로써 해결되는 문제인지 고민 → 로그가 없으면 안된다는 것 자체가 문제
    • 대신 배치 돌기 전 총 몇 건이 도는지 찍는 정도의 로깅은 괜찮다.

'WIL' 카테고리의 다른 글

[WIL] Decoupling with event  (1) 2025.08.31
[WIL] Failure-Ready System  (2) 2025.08.24
[WIL] 인덱스와 캐시  (0) 2025.08.17
[WIL] 동시성 이슈 제어  (4) 2025.08.08
[WIL] 설계 - Software Design  (2) 2025.07.27
'WIL' 카테고리의 다른 글
  • [WIL] Failure-Ready System
  • [WIL] 인덱스와 캐시
  • [WIL] 동시성 이슈 제어
  • [WIL] 설계 - Software Design
g-hwang
g-hwang
g-hwang 님의 블로그 입니다.
  • g-hwang
    g-hwang 님의 블로그
    g-hwang
  • 전체
    오늘
    어제
    • 분류 전체보기 (57)
      • 데브코스 (7)
      • 스프링 (8)
      • 자바 (3)
      • 아키텍처 (3)
      • 트러블 슈팅 (4)
      • 알고리즘 (0)
      • 개발서적 (7)
      • 인프런 워밍업 스터디 (7)
      • 오픈소스 기여 (2)
      • WIL (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    코드래빗
    도메인
    래퍼 클래스
    자바
    인덱스
    다중 컬럼 인덱스
    스프링
    real mysql 8.0
    virtual thread
    인프런 워밍업 스터디
    워밍업 스터디
    jpa 순환참조
    이벤트 스토밍
    JPA
    레이어 아키텍처
    ZSet
    도메인 모델링
    카프카
    스터디3기
    트랜잭션
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
g-hwang
[WIL] TDD & 지속 테스트 가능한 구조
상단으로

티스토리툴바