기존의 생각 & 지식 & 습관
- 테스트의 중요성은 어느정도 알고 있었지만, 테스트를 생각하며 프로덕션 코드를 작성하진 않았다.
- Swagger를 적용하면, Controller에 Swagger 관련 코드가 많아져 코드를 읽기 어렵다.
- 기능이 많아질수록 DTO 파일이 많아져, 파일 탐색이 어렵다.
- 퍼사드는 서비스를 조립하는 역할이다.
- FK를 쓰지 않는 이유는 운영 편의성을 챙기기 위해서다.
- 로그는 문제가 발생하는 경우에 남긴다.
이번 주의 경험
- TDD 방식으로 과제를 구현
- API Spec을 인터페이스로 관리함으로써 버저닝
public interface UserV1ApiSpec {
@Operation(
summary = "회원가입",
description = "사용자 정보를 입력하여 회원가입을 수행합니다."
)
ApiResponse<UserV1Dto.UserResponse> joinUser(
@Schema(name = "회원가입 요청", description = "회원가입에 필요한 사용자 정보")
UserV1Dto.JoinRequest joinRequest
);
}
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을 사용하면 성능적 문제는 크지 않지만, 파일로 저장할 때 디스크 비용이 발생
- 로그를 남김으로써 해결되는 문제인지 고민 → 로그가 없으면 안된다는 것 자체가 문제
- 대신 배치 돌기 전 총 몇 건이 도는지 찍는 정도의 로깅은 괜찮다.