/////
Search
Duplicate
5️⃣

값 검증

: 값 검증은 표현 영역과 응용 서비스 두 곳에서 모두 수행할 수 있다.
: 원칙적으로 모든 값에 대한 검증은 응용 서비스에서 처리한다.
public class joinService { @Transactional publilc void join(JoinRequest joinReq) { checkEmpty(joinReq.getId(), "id"); checkEmpty(joinReq.getName(), "name"); checkEmpty(joinReq.getPassword(), "password"); if (joinReq.getPassword().equals(joinReq.getConfirmPassword())) throw new InvalidPropertyException("confirmPassword"); checkDuplicated(joinReq.getId()); } private void checkEmpty() { ... } private void checkDuplicated() { ... } }
Java
복사
: 그런데 표현 영역은 잘못된 값이 존재하면 이를 사용자에게 알려주고 다시 입력받아야 한다. 스프링 MVC는 폼에 입력한 값이 잘못된 경우 에러메시지를 보여주기 위한 용도로 Erros나 BindingResult를 사용하는데, 컨트롤러에서 위와 같은 응용 서비스를 사용하면 폼에 에러 메시지를 보여주기 위해 다음과 같이 다소 번잡한 코드를 작성해야한다.
@Controller public class Controller { @PostMapping("/member/join") public String join(JoinRequest joinRequest, Erros eorrs) { try { joinService.join(joinRequest); } catch(EmptyPropertyException ex) { erros.rejectValue(ex.getPropertyName(), "empty"); return fromView; } catch(EmptyPropertyException ex) { erros.rejectValue(ex.getPropertyName(), "invalid"); return fromView; } catch(EmptyPropertyException ex) { erros.rejectValue(ex.getPropertyName(), "duplicate"); return fromView; } } }
Java
복사
: 응용 서비스에서 각 값이 유효한지 확인할 목적으로 익셉션을 사용할 때의 문제점은 좋지않은 UX를 제공한다는 것, 사용자는 폼에 값을 입력하고 전송했는데 입력한 값이 잘못 되어 다시 폼에 입력해야 할 때 한 개 항목이 아닌 입력한 모든 항목에 대해 잘못된 값이 존재하는지 알고 싶을 것이다. 그래야 한 번에 제대로 입력할 수 있기 때문이다.
: 근데 응용 서비스에서 값을 검사하는 시점에 첫 번째 값이 올바르지 않아 익셉션을 발생시키면 나머지 항목에 대해 유효성을 체크하지 않기 때문에 사용자는 첫 번째 값에 대한 에러 메시지만 보게 되고 나머지 항목에 대해서는 값이 올바른지 알 수 없게 된다. 이는 사용자가 같은 폼에 값을 여러 번 입력하게 된다.
: 이런 사용자 불편을 해소하기 위해 응용 서비스에서 에러 코드를 모아 하나의 익셉션으로 발생시키는 방법도 있다.
@Transactional public OrderNo placeOrder(OrderRequest orderRequest) { List<ValidateionError> erros = new ArrayLists<>(); if (orderRequest == null) { erros.add(ValidationError.of("empty"); } else { if (orderRequest.getOrdererMemberId() == null) erros.add(ValidationError.of("ordererMemberId", "empty")); if (orderRequest.getOrderProducts() == null) erros.add(ValidationError.of("orderProducts", "empty")); if (orderRequest.getOrderProducts().empty()) erros.add(ValidationError.of("orderProducts", "empty")); } // 응용 서비스가 입력 오류를 하나의 익셉션으로 모아서 발생 if (!errors.isEmpty()) throw new ValidationErrorException(erros); ... }
Java
복사
: 표현 영역은 응용 서비스가 ValidationErrorException을 발생시키면다음 코드처럼 익셉션에서 에러 목록을 가져와 표현 영역에서 사용할 형태로 변환 처리한다.
@PostMapping("/orders/order") public String order(@ModelAttribute("orderReq") OrderRequest orderRequest, BindingResult bindingResult, ModelMap modelMap) { User user = (User) SecurityContextHolder.getContext() .getAuthenticatoin().getPrincipal(); orderRequest.setOrdererMemberId(MemberId.of(user.getUsername())); try { OrderNo orderNo = placeOrderService.placeOrder(orderRequest); modeMap.addAttribute("orderNo", orderNo.getNumber()); } catch (ValidataionErrorException e) { e.getErros().forEach(err -> { if (err.hasName()) { bindingResult.rejectValue(err.getName(), err.getCode()); } else { bindingResult.reject(err.getCode()); } }); poplucateProductsModel(orderReqeust, modelMap); return "order/confirm"; } }
Java
복사
: 물론 모든 값의 형식을 검증한 뒤, 에러가 존재하면 다시 폼을 보여주는 방식을 채택할 수도 있다.
: 스프링과 같은 프레임워크는 값 검증을 위한 Validator 인터페이스를 별도로 제공하므로 이 인터페이스를 구현한 검증기를 따로 구현하면 위 코드를 다음과 같이 간결하게 줄일 수 있다.
@Controller public class Controller { @PostMapping("/member/join") public String join(JoinRequest joinRequest, Erros eorrs) { new JoinRequestValidator().validate(joinRequest. erros); if (erros.hasErros()) return fromView; try { joinService.join(joinRequest); return successView; } catch(DuplicateIdException ex) { erros.rejectValue(ex.getPropertyName(), "duplicate"); return fromView; } } }
Java
복사
: 이렇게 표현 영역에서 필수 값과 값의 형식을 검사하면 실질적으로 응용 서비스는 ID 중복 여부와 같은 논리적 오류만 검사하면 된다.
: 즉 표현 영역과 응용 서비스가 값 검사를 나눠서 수행하는 것이다. 응용 서비스를 사용하는 표현 영역 코드가 한 곳이면 구현의 편리함을 위해 다음과 같이 역할을 나누어 검증을 수행할 수도 있다.
표현 영역: 필수 값, 값의 형식, 범위 등을 검증한다.
응용 서비스: 데이터 존재 유뮤와 같은 논리적 오류를 검증한다.
: 응용 서비스에서 얼마나 엄격하게 값을 검증해야 하는지에 대해서는 의견이 갈릴 수 있다. 작성자도 예전에는 표현 영역에서 필수 값을 검증하고 응용 서비스에서는 논리적 오류 검증을 해도 괜찮다고 생각했는데, 요즘은 가능하면 응용 서비스에서 필수 값 검증과 논리적인 검증을 모두 하는 편이다. 응용 서비스에서 필요한 값 검증을 모두 처리하면 프레임워크가 제공하는 검증 기능을 사용할 때보다 작성할 코드가 늘어나는 불편함이 있지만 반대로 응용 서비스의 완성도가 높아지는 장점이 있다.
⇒ 아마 요즘 추세는 서버 사이드 렌더링 등 표현 영역이 대부분 Json만 내려주기 때문이라 UX를 그렇게 신경쓰지 않아도 되서 그런 것 같다!