전체 페이지뷰

2017년 5월 19일 금요일

Compound pattern & MVC pattern, part 2

말씀 드린 바와 같이 MVC 패턴에 대해 알아보고자 합니다. C#에는 윈폼이 존재하여 손쉽게 MVC 패턴을 구현해 볼 수 있습니다. 지금부터 여기에 쓸 글은 코드프로젝트에 나온 것을 기초로 함을 미리 밝혀 둡니다.

MVC 패턴은 Model, View, Controller의 세 가지 파트로 이루어집니다.

Model: MVC 모델을 구현하는 애플리케이션에 필요한 데이터(즉, 비즈니스 로직)를 처리하는 모든 작업을 담당합니다. 이 작업이란 데이터베이스에 데이터 읽기, 쓰기, 네트워크를 통한 원격 기계의 정보 얻기 등을 의미할 수 있습니다. 모델은 또한 백그라운드에서 발생하는 데이터에 대한 변경 사항을 뷰에 알려야합니다.

View: 사용자에게 데이터를 보여주는 역할을 담당합니다. 이 글에서는 WinForms로 사용자에게 뷰 클래스를 보여줄 것입니다.

Controller: 이것은 Model과 View를 묶어주는 MVC 패턴의 중심 구성 요소입니다. 사용자에게 데이터를 표시하는 뷰와 데이터를 다루는 모델은 서로의 존재를 알지 못합니다. 따라서 컨트롤러는 중개자입니다. 예를 들어 컨트롤러는 버튼 클릭과 같은 사용자 입력을 받아  적절한 조치를 취하도록 모델에 알립니다.




프로젝트 생성


새로만들기>프로젝트 템플릿에서 Window Forms 응용 프로그램을 선택하고 솔루션명과 프로젝트명을 정해줍니다( 제 경우 솔루션명을 MVCExample, 프로젝트명을 WinFormMVC로 했습니다).

그리고 나타나는 윈폼 디자이너를 조작하여 창 크기를 줄이고 TextBoxButton 하나씩을 배치했습니다.


버튼을 누르면 숫자가 1 증가하고, 박스에 직접 숫자를 적어 기준값을 바꿀 수도 있는 프로그램을 구상하고 있습니다. 우선 텍스트 박스의 속성 창에서 초기값을 0으로 두기 위해 Text 속성을 0으로 바꿔줍니다.


여기까지 하고 일단 Model을 건드려봅니다.

Model


모델은 컨트롤러가 전달한 숫자를 증가시키는 비즈니스 로직을 처리합니다. 또 View에게 데이터 값 변경(증가)을 알려줍니다.

프로젝트에 추가> 새 항목, 코드 파일을 선택하여 빈 파일을 하나 추가하고 이름을 IModel로 해 줍니다. 그러면 아무 내용도 없는 파일이 하나 추가되는데 거기에 Model을 작성해 보겠습니다.
using System;
 
namespace WinFormMVC
{
    public delegate void ModelHandler<IModel>(IModel sender, ModelEventArgs e);
 
    // EventArgs로부터 파생된 클래스. 
    // 값이 변화될 때 콘트롤러에 넘겨주기 위함
    public class ModelEventArgs : EventArgs
    {
        public int newval;
        public ModelEventArgs(int v)
        {
            newval = v;
        }
    }
 
    // form/view가 구현해야할 인터페이스
    // 모델에서 값이 변화하면 이벤트가 발생됨
    public interface IModelObserver
    {
        void ValueIncremented(IModel model, ModelEventArgs e);
    }
 
    // 값이 변화했을 때 알려줄 수 있는 기능을 붙인 모델 인터페이스
    // 실제 데이터 조작부이며 유저가 텍스트박스를 직접
    // 입력했을 때 값을 설정하는 기능도 들어있음.
    public interface IModel
    {
        void Attach(IModelObserver oberver);
        void Increment();
        void SetValue(int v);
    }
        
    public class IncModel : IModel
    {
        public event ModelHandler<IncModel> changed;
        int value;
 
        public IncModel()
        {
            value = 0;
        }
 
        // 유저가 직접 텍스트 박스에 입력했을 떄 설정하는 메소드
        public void SetValue(int v)
        {
            value = v;
        }
 
        // 값을 변화시키고 새 값으로 이벤트를 발생시킨다.
        public void Increment()
        {
            value++;
            changed.Invoke(thisnew ModelEventArgs(value));
        }
 
        // 값이 변화했을 때 알수 있도록 IModelObserver를 핸들러에 붙임
        public void Attach(IModelObserver imo)
        {
            changed += new ModelHandler<IncModel>(imo.ValueIncremented);
        }
    }
}
cs


Controller


다음으로 컨트롤러를 만들어 봅니다.

컨트롤러는 모델과 뷰를 한데 묶어줍니다. 뷰는 데이터가 어떻게 처리되는가 하는 비즈니스 로직에 대해서는 알지 못합니다. 뷰에 버튼 클릭이나 텍스트박스 입력과 같은 이벤트가 발생되면 뷰는 그것을 컨트롤러에게 알려줍니다. 컨트롤러는 사용자 입력을 기반으로 취해야 할 조치를 결정합니다. 사용자 입력의 유효성을 평가한 후 모델에 데이터 처리를 요청할 수 있습니다.

역시 같은 형식으로 IController라는 파일을 추가하겠습니다.

namespace WinFormMVC
{
    // 컨트롤러는 값을 증가시키는 하나의 기능만을 지원
    public interface IController
    {
        void IncValue();
    }
 
    public class IncController : IController
    {
        IView view;
        IModel model;
 
        // 모델과 뷰를 한데 묶어주고 뷰를 IModelObserver를 통해 모델에 연결해줌
        public IncController(IView v, IModel m)
        {
            view = v;
            model = m;
            view.SetController(this);
            model.Attach((IModelObserver)view);
            view.changed += new ViewHandler<IView>(this.ViewChanged);
        }
 
        // 유저가 값을 변화시켰을 때 부로부터 발생하는 이벤트
        public void ViewChanged(IView  v, ViewEventArgs e)
        {
            model.SetValue(e.value);
        }
 
        // 모델이 수행하는 실제 작업
        public void IncValue()
        {
            model.Increment();
        }
    }
}
cs


View


뷰의 차례입니다.

뷰 클래스는 실제로는 View 인터페이스를 구현할 Form의 일부입니다. 뷰 인터페이스는 그저 뷰가 변경 될 때 발생될 이벤트 핸들러와, 뷰를 제어하는 ​​컨트롤러를 설정하는 기능만을 가집니다.

IView라는 파일을 추가합니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace WinFormMVC
{
    public delegate void ViewHandler<IView>(IView sender, ViewEventArgs e);
 
    // 이벤트가 발생하는 동안 사용될 이벤트 인자 클래스
    public class ViewEventArgs: EventArgs
    {
        public int value;
        public ViewEventArgs(int v)
        {
            value = v;
        }
    }
 
    public interface IView
    {
        event ViewHandler<IView> changed;
        void SetController(IController controller);
    }
}
cs
여기에 들어있는 인터페이스는 Form 내부에서 구현될 겁니다.


Form


다음으로 Form을 수정하겠습니다.

IView 인터페이스에 대해 SetController 메소드를 구현하고 컨트롤러에 대한 참조를 유지하는 역할을 합니다. 모델에서 수행하는 모든 작업에 대해서 폼은 컨트롤러는 사용하여 신호를 보냅니다. 다음 코드에서 버튼을 클릭하면 컨트롤러를 호출하여 값을 증가시키는 것을 볼 수 있습니다. 사용자가 텍스트 상자에 직접 입력하면 컨트롤러가 뷰에서 이 이벤트에 연결된대로 새 값을 컨트롤러를 통해 모델에 설정하는 뷰 변경 이벤트를 발생시킵니다. ValueIncremented는 모델에서 값이 변경 될 때마다 모델에 의해 호출되는 인터페이스 IModelObserver의 구현입니다.

본격적인 코딩 전에 폼 디자이너에 배치했던 텍스트박스와 버튼을 코드에 연결부터 하겠습니다.

디자이너 상에서 버튼을 선택하고 속성 창에서 번개모양 이벤트를 누르면 연결할 이벤트들이 나타납니다. 그 중 Click을 더블클릭하여 줍니다.


텍스트 박스의 경우는 Leave를 선택합니다.

그리고 코드를 다음과 같이 작성합니다.

namespace WinFormMVC
{
    // 폼은 실제로는 뷰의 일부이므로 IView를 구현하여 컨트롤러를 설정하는 기능을 갖게 됨
    // IModelObserver도 구현해서 변화결과를 뷰에 반영
 
    public partial class Form1 : Form, IView, IModelObserver
    {
        IController controller;
 
        public event ViewHandler<IView> changed;
 
        //  뷰와 컨트롤러를 연결함
        public void SetController(IController cont)
        {
            controller = cont;
        }
 
        // 모델에서 이벤트가 일어나면 변화를 텍스트 박스에 표시
        public void ValueIncremented(IModel m, ModelEventArgs e)
        {
            textBox1.Text = "" + e.newval;
        }
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            
        }
 
        // 버튼이 클릭되면 값을 변화하도록 함
        private void button1_Click(object sender, EventArgs e)
        {
            controller.IncValue();
        }
 
 
        // 유저가 텍스트 박스를 입력한 경우 발생
        private void textBox1_Leave(object sender, EventArgs e)
        {
            try
            {
                changed.Invoke(thisnew ViewEventArgs(int.Parse(textBox1.Text)));
            }
            catch
            {
                MessageBox.Show("정수가 아닙니다");
            }
        }
    }
}
cs


Program.cs


다음은 Program.cs 차례입니다.
namespace WinFormMVC
{
    static class Program
    {
        /// <summary>
        /// 해당 응용 프로그램의 주 진입점입니다.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);           
 
            Form1 view = new Form1();
            IModel model = new IncModel();
            IController controller = new IncController(view, model);
            Application.Run(view);
        }
    }
}
cs


이상으로 MVC 모델에 대해 개략적으로 살펴보았습니다. 너무 수박 겉핥기라는 느낌이 들지만 차차 더 알아갈 날이 있을 거라고 생각합니다.

댓글 없음:

댓글 쓰기