2021-10-27-TIL
검증코드는 어디에 있어야 하는가?
백엔드에서 검증코드는 계층형 아키텍처에서 어느 계층에 존재해야 하는가? 컨트롤러에서만 검증하는 것이 기본적인 생각인 것 같다. 하지만 동료의 이야기에 따르면 금융권에서는 검증요소의 중복과 상관없이 모든 계층에서 검증을 수행하는 편이라고 한다. 철저히 검증을 하려면 모든 계층에서 검증을 수행하는 것도 좋은 것 같다. 단, 그것이 성능에 무리가 가지 않는다면 말이다.
검증의 종류에 따라 위치가 달라진다
검증을 한 덩어리로 보지 않고 무엇을 검증하는지로 나누면 위치를 정하기 쉬워진다.
- 형식 검증: 필수 값이 있는지, 길이와 형식이 맞는지. 요청이 들어오는 입구인 컨트롤러에서 거른다. Spring MVC에서는
@RequestBody가 붙은 인자에@Valid를 함께 붙이면 Bean Validation 제약(@NotBlank,@Size등)이 자동으로 검사된다. - 업무 규칙 검증: 잔액이 충분한지, 이미 처리된 주문인지. 데이터 상태를 알아야 판단할 수 있으므로 서비스나 도메인 객체 안에 둔다.
- 데이터 무결성: 중복 금지, 참조 관계. 마지막 방어선으로 DB 제약 조건이 지킨다.
컨트롤러를 거치지 않고 서비스가 호출되는 경로(배치, 메시지 소비자, 다른 서비스)가 있다면 컨트롤러 검증만으로는 부족하다. 이럴 때 서비스 계층에도 같은 제약을 걸 수 있다. Spring은 MethodValidationPostProcessor를 등록하고 클래스에 @Validated를 붙이면, 서비스 메서드의 파라미터 제약을 AOP 프록시로 검사해 준다.
1
2
3
4
5
6
7
@Validated
@Service
public class TransferService {
public void transfer(@NotNull Long fromId, @Positive long amount) {
// 호출 경로와 관계없이 파라미터 제약이 검사된다
}
}
이 방식도 프록시를 거친 호출에만 적용되므로, 같은 클래스 안에서 자기 메서드를 호출하면 검사되지 않는다.
@Transactional(readOnly = true)
JPA에서 프록시 객체를 찾는 비용을 줄이고자 해당 설정을 한다.
- https://stackoverflow.com/questions/44984781/what-are-advantages-of-using-transactionalreadonly-true
문서를 다시 확인해 보니 위 메모는 정확하지 않았다. Spring Javadoc에서 readOnly는 실제 트랜잭션 하위 시스템에 주는 힌트이고, 읽기 전용임을 알려서 런타임 최적화를 허용하는 플래그다. 쓰기를 반드시 막아 주는 것은 아니고, 힌트를 해석하지 못하는 트랜잭션 매니저는 조용히 무시한다.
JPA 구현체가 Hibernate일 때의 효과는 Spring Data JPA 문서에 나온다. 읽기 전용 트랜잭션에서는 flush 모드가 MANUAL로 바뀌고, 그래서 Hibernate가 변경 감지(dirty checking)를 건너뛴다. 변경 감지는 영속성 컨텍스트에 올라온 엔티티마다 처음 상태를 기억해 두었다가 flush 시점에 비교하는 작업이다. 조회한 엔티티가 많을수록 이 비교 비용이 커지므로, 조회만 하는 서비스 메서드에 readOnly = true를 붙이면 그 비용을 줄일 수 있다. 줄어드는 것은 프록시를 찾는 비용이 아니라 이 변경 감지 비용이다.
댓글
아직 댓글이 없습니다