유난히 우테코 프리코스 4 주차에서 배우고 나쁜 습관들을 고친 경험이 많은 것 같다. 마지막 주차이기도 하고 지난 주차에 처음으로 피어 리뷰를 받으면서 나는 아직 공부해야 하는 것이 많다는 것을 정말 많이 느꼈다. 지난 3 주차 미션에서는 비슷한 일을 하는 로직과 데이터를 모아 클래스로 만드는 것에 집중했었다. 이번 미션에서는 요구사항들과 지난 미션에서 느낀점을 보완하기 위해 데이터를 관리하는 비즈니스 로직과 사용자와 상호작용하는 User Interface 로직을 분리해보려 한다.
클래스 내의 비즈니스 로직과 UI 로직을 분리하자!
다른 우테코 프리코스 참여자 분들의 코드 리뷰를 하면서 가장 많이 본 용어는 MVC 패턴
이었다. 또한 이번 미션의 요구 사항에는 BridgeGame
클래스 내에서 사용자와 상호작용하는 InputView
OutputView
객체를 사용하지 않을 것으로 명시되어 있다. 이번 미션에서는 적절한 디자인 패턴을 적용해보는 것을 목표로 삼았다.
많은 개발자들은 SW를 설계할 때 비슷한 문제와 고민을 반복적으로 겪는다. 이를 해결하기 위해 설계를 올바르고 빠르게 만들어주는 도구가 디자인 패턴이다. 이 패턴들을 공부할 때 다음 4 가지 요소들을 함께 고려하면 수월할 것이라고 한다.
- 패턴 이름(Pattern Name): 패턴 이름은 솔루션을 담고 있다.
- 문제(Problem): 해결할 문제가 무엇인지 파악한다.
- 해결 방법 & 구현(Solution & Implementation)
- 결과(Consequence): 디자인 패턴을 적용해서 얻는 결과와 장단점을 파악한다.
UI, 데이터 및 논리 제어를 구현하는 SW를 위한 디자인 패턴은 다양하다. 그 중 MVC와 MVP에 대해 알아보고 이번 미션에서 MVP 패턴을 적용하게 된 이유에 대해 설명해보려 한다.
MVC와 MVP는 공통적으로 비즈니스 로직과 화면을 구분하는데 중점을 두어 관심사를 분리한다. 이 개념은 얼마전 토스의 개발자 컨퍼런스 영상에서 보았던 더 나은 프론트엔드 컴포넌트를 만들고 분리하는 방법들과 맞닿아 있다고 생각한다. Headless 기반의 추상화는 디자인에 의존하는 UI와 데이터를 분리하는 방법인데, 이 방법론에서는 headless UI component라는 기능은 있지만 스타일이 없는 컴포넌트를 제시한다.
결국 프론트엔드 개발에서 겪는 반복적인 문제들을 해결하기 위한 다양한 방법론들도 UI 디자인 패턴들에서 유래한다.
MVC는 가장 대중화된 UI 디자인 패턴이며, 코드를 Model-View-Controller로 분리하여 역할을 나눈다. 사실 "이것이 MVC다!"라는 명확한 기준이 있는 것 같지는 않고 사람마다 다르게 해석하여 적용하고 있는 것 같다. 이 글에서는 내가 공부하고 이해한 기준으로 작성할 예정이다.
사용자의 입력을 받아 다리(bridge)를 건너는 게임을 앱으로 만든다고 가정해보자.
앱이 다루는 가변적인 데이터이다. 프론트엔드에서는 화면에 나타내는 데이터, 서버의 API와 DB로부터 전달받는 데이터를 이야기한다.
ex) 사용자의 입력값, 사용자가 건널 다리의 정보 등
앱의 데이터를 사용자에게 보여주는 화면이다. 프론트엔드에서는 HTML, CSS로 만들어져 브라우저에 렌더링되는 코드를 이야기한다.
ex) 사용자와 상호 작용하는 부분
Model의 데이터를 받아 View에 나타내고 View로부터 사용자의 입력을 받아 Model을 변경하는 중간 다리 역할을 한다. 모델을 변경하지 않고도 데이터를 다른 방식으로 나타내고 싶을 때 컨트롤러에서 이를 처리하기도 한다.
MVC 패턴은 각각의 역할을 나누어 코드를 분리하여 유지보수와 개발 효율성을 높이고 싶어서 도입하였으며, 다음과 같은 동작 순서를 가진다.
사용자의 action > Controller가 action을 확인하고 Model을 업데이트 > Controller가 Model의 데이터를 나타낼 View를 선택 > View는 Model을 사용하여 데이터를 나타냄
나는 UI와 데이터 로직을 나누기 위해 MVC 패턴을 도입하려 했으나, 여전히 View와 Model 사이에 의존성이 있는 것을 보고 MVP 패턴에 대해 추가로 공부해보기로 하였다.
Model, View의 개념은 MVC와 동일하다.
View에서 요청한 정보로 Model을 가공하여 View에게 전달한다. Presenter가 View & Model의 인스턴스를 가지고 있어 둘을 연결하는 역할을 한다.
MVC 패턴에서 나타나는 View와 Model의 의존성 문제를 해결하기 위해 Presenter를 통해서만 View에 데이터를 전달한다.
사용자의 action > View가 action을 확인하고 Presenter에 데이터 요청 > Presneter가 Model에 데이터 요청 > Model이 Presenter의 데이터 요청에 응답 > Presneter기 View의 데이터 응답 > View는 Presenter가 제공한 데이터를 화면에 그림
나는 2 가지의 이유로 두 패턴 중 MVP를 선정하였다. 처음부터 패턴을 선정하기보다는 비즈니스-UI 로직을 분리하는 코드를 짜보면서 내가 추구하는 방향성과 MVP 패턴이 더 잘 맞는다고 생각하였다.
미션 요구사항 충족
우테코 4 주차 미션에서는 InputView
OutputView
객체를 사용하고, BridgeGame
클래스에서는 View 관련 코드를 작성하지 말 것이라는 요구사항이 있었다. MVC에서는 View를 제외한 컴포넌트들에서 모두 UI를 업데이트할 수 있으므로 적합하지 않다고 판단하였고, MVP를 사용하여 BridgeGame
클래스를 Model로 사용하는 것으로 결정하였다.
사용자 action이 발생하는 위치 관점
사용자의 action은 MVC에서는 Controller, MVP에서는 View를 통해 들어온다. 나는 이 의미를 inputView
를 Controller로 사용하여 MVC로 사용하거나, InputView
를 View로 사용하여 MVP 패턴을 적용하는 것으로 받아들였다. 나는 후자가 요구사항에 더 적합하다고 생각하였으므로 최종적으로 MVP 패턴을 선택하게 되었다.
사실 개발자마다 디자인 패턴을 조금씩 다르게 이해하고 있으므로 이름만으로 패턴을 나누는 것은 무의미하다고 생각한다. 내가 본 미션에서 적용한 디자인 패턴은 아래와 같으며, 이 링크에서 우테코 4주차 코드의 결과물을 확인할 수 있다.
프론트엔드의 컴포넌트 디자인 패턴을 배우면서 "프레임워크/라이브러리에 종속된 학습법이 아닌가?"라고 고민한 적이 있었다. 프리코스를 통해 보편적인 UI 디자인 패턴을 배우면서, 개발자들이 왜 이러한 개념을 만들어냈는지를 함께 고민해보니 그 부분이 어느정도 해소된 것 같다. 바닐라 자바스크립트를 넘어서 다음 리액트 프로젝트에서는 이러한 패턴을 적용하여 코드를 분리해보는 연습을 할 예정이다.
MDN: MVC 용어사전
프론트엔드에서 MV* 아키텍쳐란 무엇인가요?
디자인 패턴(Design Pattern)이란?
[디자인패턴] MVC, MVP, MVVM 비교
토스ㅣSLASH 22 - Effective Component 지속 가능한 성장과 컴포넌트