MVC Pattern에서의 유효성 검사

pengooseDev·2023년 7월 23일
2
post-thumbnail

최신순 정렬

해당 글은 위쪽의 내용들이 최신 내용!


'23.08.04 생각이 바뀌었다.

각 Layer와 객체엔 각자의 책임이 있다. 검증 또한 그러하다.

각 도메인과 Model, View는 최소한의 책임이 존재한다. 물론, View에서 Car에 대한 검증을 진행해야 한다는 것은 아니다. 각자의 책임이 너무 오지랖이 되는 것은 아닌지 경계해야 한다는 뜻이다.

Validation의 위치에 대해 토론한 StackOverflow의 글이다.
해당 글에서도 많은 의견이 오가고 있다.

필자는 많은 고민을 하였고, 각 객체는 DI받는 객체를 제외하고는 그 자체로 무결성을 지녀야 한다는 결론에 도달하였다.

물론, 추후에 다시 관점이 변할 수 있지만!
우선은 각 Layer의 책임과 역할을 아래와 같이 정리해보았다.


Controller의 책임

  • View와 Domain 또는 Model들을 합치고 조작하는 Service Layer의 역할을 담당한다.
  • 코드의 흐름을 담당한다.
  • 하위의 View나 Domain, Model에서 발생하는 Error를 catch하여 에러를 처리한다.

View의 책임

  • User에게 보이는 Interface의 역할을 한다. (OutputView)
  • User로부터 값을 입력받는다. (InputView)
  • 입력받는 값에 최소한의 유효성 검증을 진행한다.

여기서 최소한의 유효성 검증에 대해 고민을 해보아야한다.
"어디까지 검증할 것인가?"

  • "자동차의 이름은 5글자를 넘지 않는다"
    위와 같은 Validation을 InputView에 추가해둘 경우, 해당 Domain 또는 Model에 높은 결합도가 발생해 다른 Domain 및 Model에서 사용이 불가능하다.

즉, 어디서든 사용이 가능한 View를 구현하기 위해선, 모든 입력값들에서 공통적으로 시행하는 유효성 검사만의 책임을 지워야 한다는 것이다.

예를들어, "빈 문자열에 대한 검증"을 진행한다는 것이 그러한 예이다.


Model과 Domain

  • Model과 Domain은 자체적인 Validation을 통해 자체 무결성을 유지해야한다.
  • 어디까지 추상화 할 것인가? 바뀔 수 있는 요소들은 DI받아 사용하고, 그 이외의 것들은 무결성을 유지하도록 추상화하자.

RacingGame에서 사용하는 Vehicle이 Car가 아니라 Bike가 될 수 있다는 것을 고려해보면, 확장성과 더 깊이 사고할 수 있다.


'23.07.24 - 첫 결론은 Controller

Contoller? View? Car? RacingGame?

Validation에 대한 책임을, 누구에게 이양할까 고민하다 "fail-fast"와 관련된 좋은 글을 찾게 되었다.

Controller나 View 등. 각각의 객체가 담당하고 있는 역할도 고민해야겠지만, 문제가 있는 데이터를 너무 오래 들고있는 것도 좋지는 않다는 뜻이다.

아 물론, 정해진 답은 없다.
다만, 내가 작성하는 코드의 로직에서 적절한 Validation의 위치에 대해 고찰한 글이다?


View?

input 값이니, 당연히 View에게 책임이 있지 않을까? 고민을 하다가 다른 Layer로 책임을 넘기기로 했다. 이유는 아래와 같다.

도메인(관심사)에 맞지 않음

도메인(관심사) 별로 나눈 것을 생각해 봤을 때, View는 User에게 interface를 제공하는 것이 본질이지 값에 대한 검증에 책임이 있다고 보기는 어려웠다. 그저 인터페이스일 뿐이다.

재사용성 낮아짐

UI에 validation을 달아두면, 결합도가 높아져 재사용성에 제한이 생긴다. ReactJS에서 재사용 할 컴포넌트를 구현하는 과정에서, 순수함수 형태로 작성할 때 가장 재사용성이 높았다는 것을 기억하자.

TC 짜기가 어려워짐

테스트 코드를 짜기가 어렵다. validation이 비동기적으로 시행되는 Input에 달려있다?
결국 이를 mock데이터를 만들고 넘겨주어야 한다. 그럼 Validation과정이 pass 되어버린다.


Domain?

해당 Layer는 경주에 관련된 비즈니스 로직을 처리하는 책임을 가지고 있는 친구라서 조금 고민하다가 빠르게 넘겼다.


Car(Model)?!

Car가 가진 name field에 대한 유효성을 검사하는 것이기 때문에 Car도 나쁘지 않은 선택지라고 생각했다. 객체가 생성될 때, 검사를 진행하는 것이 가장 깔끔하다고 생각했기 때문이다. 다만, 하나의 문제가 생각났다. Error 처리가 그것이다.

Validation Error에 대한 처리를 Car에게 이양하는 것이 과연 옳은가?

자동차는 부릉부릉 운전만 잘 하면 되지, 에러 처리 책임이 있다고 보기엔 어렵다고 판단했다. 물론, 위의 선택지보단 Car가 괜찮아보이긴 했다.


Controller!

Controller는 코드의 흐름을 전반적으로 관리하며, 모든 객체들이 모여 하나의 유기체가 될 수 있도록 하는 친구라고 생각한다. 에러가 발생한다면 결국 "프로그램이 종료"된다는 것을 생각해봤을 때, 코드의 흐름이 종료 된다는 것이고, 코드의 흐름을 관리하는 Controller에서 에러 처리를 담당하는 것이 바람직하다고 결론을 내렸다.

이는 "fail-fast"의 원칙과도 맞닿아있다.

fail-fast 원칙에 따르면 View에서 유효성 검증을 진행하는 것이 옳아보이지만, 위에 작성한 이유 때문에 return 대비 cost가 더 크다고 판단했다.

따라서, 이에 대한 값을 바로 받아오는 Controller가 유효성 검사에 대한 책임을 갖는 것으로 결론을 내렸다. 땅땅!

0개의 댓글