Refactoring traditional web application with MVC pattern (Part 1)

Introduction

어떤 practice가 보편화되어있고 그것을 보조하는 툴이나 생각의 장치들이 잘 발달되어 있다면, 오히려 그 practice의 장점이 무엇인지 파악하는 데에는 방해가 되기도 한다. 어떻게 보면, 생산성을 높히고자 하는 우리의 노력이 때로는, 각 개인의 능력을 키우는 데에는 방해가 될 수도 있는 것이다.

예를 들어, 잘 만들어진 소프트웨어 프로세스 환경에만 익숙한 개발자는, 그러한 프로세스가필요없다고 주장하는 사람에게, 그것이 왜 필요한지 제대로 설명하지 못할 것이다.현대의 특정 프로그래밍 언어에만 익숙한 사람은, 자신이 활용하고 있는 수많은 언어적 장치들의 이점을 제대로 이해하고 있지 못할 가능성이 높다.

마찬가지로, 잘 만들어진 Model-View-Controller(이하 MVC) 프레임워크에만 익숙한 사람이라면, 그 패턴이 가지고 있는 유용성이나 의미를 발견하지 못할 가능성이 높다. 대부분의 웹 어플리케이션 개발자들은, 프리젠테이션과 로직이 뒤섞인 기존의 웹 어플리케이션에만 익숙하거나, MVC 프레임워크 웹 어플리케이션에만 익숙하다. 기존의 웹 어플리케이션이 MVC를 사용함으로써 어떤 점에서 좋아질 수 있는지는 전자나 후자의 프로그래머들 모두 알지못할 가능성이 높다.

이 글의 목적은 기존의 웹 어플리케이션에 MVC 패턴을 적용하면서 리팩토링하는 과정을 보임으로써, MVC 패턴의 유용성을 재발견하는데 있다.

Galaxy

Galaxy는 개인적으로 개발중인, RSS를 주기적으로 받아와서 사용자에게 보여주는 웹 어플리케이션이다. 이 글에서 예로 들 부분은 Galaxy의 admin 모듈이다. 이 모듈의 기능은 다음과 같다.

  • 사용자는 자신이 구독할 RSS Subscription을 등록, 삭제하거나 조회, 수정할 수 있으며,
  • 각각의 Subscription에는 Category를 할당할 수 있다.
  • 이 Category 역시 등록, 삭제, 조회, 수정될 수 있다.
  • 그리고, 사용자는 전체 Subscription을 수동으로 업데이트할 수 있다.

Galaxy in traditional-web-application style

일단 Galaxy의 첫번째 버전에서는 Subscription을 등록하는 기능만 제공하기로 결정했다고 가정해보자. 자, 먼저 Subscription을 등록하는 폼을 보여주고 사용자가 정보를 입력하고서 폼을 submit하면 Subcription 정보를 DB에 저장하는 로직을 처리하는 어플리케이션을 생각해보자.

자신을 Perl CGI나 PHP, ASP 등을 사용하는 웹 어플리케이션이 막 퍼지기 시작하던 시절의 웹 프로그래머라고 가정해보자. 필요한 기능은 그다지 많지 않고 로직도 단순하다. 그 시절에 방명록 따위를 심심풀이로 짜본 경험이 있다면, 금방 감이 올 것이다. 파일 이름은 admin.rb라고 하자. (Galaxy는 Ruby 어플리케이션이므로, 예제 역시 Ruby 언어를 사용한다. 간단한 템플리팅(templating)을 위해 eruby를 사용한다고 가정하자.) 웹어플리케이션이라는 신기술을 다룰 줄 아는 우리가 생각해낸 코드는 아마 다음과 같을 것이다.

Traditional-web-application-style Galaxy

즉, ‘mode’ 리퀘스트 파라미터가 없을 때는 Subscription 등록을 위한 양식(form)을 보여주고, 이 양식이 submit되면 ‘mode’ 리퀘스트 파라미터가 설정되어 Subscription 등록을 처리하는 방식이다.

이 코드에 어떤 문제가 있는가? 그렇지 않다. 문제는 없어 보인다. Subscription 등록이라는 단순한 기능을 수행하는 목적의 어플리케이션으로서 이 이상의 무언가를 필요로 한다면, 그게 더 이상한 것이다.

이제 다시 질문을 하겠다. 이 코드에 어떤 문제가 있는가? 그렇다. 이 어플리케이션은 현재는 Subscription 등록이라는 단순한 기능밖에 없지만, 앞으로 위에서 언급했던 여러 기능들을 모두 넣어야하는 미래를 가지고 있다. 자 여기서 약간의 상상력이 필요하다. 이 기능들을 하나씩 추가할 때마다, 리퀘스트 파라미터에 따라 분기하는 if-else-end 구문은 엄청나게 길고 복잡해져갈 것이다.

그리고, 이 어플리케이션은 웹 어플리케이션임을 기억해야한다. 위의 예에서는 html이 별로 사용되지 않았지만, 실제 서비스에 적용하기 위해 웹 디자이너가 던져준 화려한 페이지의 html 마크업은 훨씬 복잡할 것이다. 기존의 웹 어플리케이션은 프리젠테이션을 위한 html 마크업과 도메인 로직을 위한 코드가 복잡하게 뒤섞이는 경향이 있다.

웹 어플리케이션의 또 한가지 중요한 특징은, 로직의 변경 빈도와 프리젠테이션의 변경 빈도가 현저히 차이난다는 것이다. 커다란 리뉴얼부터 자잘한 수정까지 html 마크업은 코드에 비해 자주 변경된다. 문제는 html 마크업과 코드가 뒤섞여있음으로 인해, 잦은 html 마크업의 변경은 필연적으로 코드, 그 중에서도 가장 중요한 도메인 로직의 버그 가능성을 낳게 된다.

결국, 우리에게는 프리젠테이션과 도메인 로직의 분리(decomposition)가 필요하다.

Extracting domain objects

위의 예에서 도메인 로직은 어떤 부분인가? 바로 "Subscription을 등록하는 것"이다. 도메인 로직을 프리젠테이션으로부터 분리해내는 방법에는 여러가지가 있겠지만, 우리는 객체지향언어를 사용하고 있으므로, 도메인 로직을 도메인 객체(domain object)의 형태로 분리해내는 방법을 채택하자. "Subscription을 등록하는 것"에서 도메인 객체는 무엇일까? 바로 Subscription이다.

Subscription 객체를, 우리가 사용하고 있는 Ruby로 표현하기 위해서는 클래스를 사용하면 되겠다.

Subscription class as domain object

그리고, 기존 코드는 Subscription 객체를 사용하도록 바뀔 것이다.

Using Subscription object

이로써 원래 코드에 있던 도메인 로직 부분을 몽땅 하나의 클래스로 분리해낼 수 있게 되었다. 변경된 admin.rb에는 클래스 하나와 훨씬 간단해진 나머지 코드가 남게되었다. 또한, 프리젠테이션을 자주 변경해도 DB에 도메인 정보를 저장하는 걸 빠뜨리거나 할 가능성은 훨씬 줄어들게 되었다.

MVC 패턴에서는 도메인 정보를 나타내거나 그것을 처리하는 역할을 하는 도메인 객체를 Model이라고 부른다.

댓글 달기

이메일 주소는 공개되지 않습니다.

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.