๋ฐ˜์‘ํ˜•

์ง€๋‚œ ํฌ์ŠคํŒ…์—์„œ MVC ํŒจํ„ด์— ๋Œ€ํ•ด ์†Œ๊ฐœ๋ฅผ ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.
[Design Pattern] MVC(Model-View-Controller) ํŒจํ„ด ์ด์•ผ๊ธฐ #1

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” MVC ํŒจํ„ด์„ ์ ์šฉํ•œ ์œˆํผ(WinForm) ์˜ˆ์ œ๋ฅผ ์†Œ๊ฐœํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. 

MVC ํŒจํ„ด์„ ์ ์šฉํ•œ ์ „ํ™”๋ฒˆํ˜ธ๋ถ€ ํ”„๋กœ๊ทธ๋žจ

์ด์ „ ํฌ์ŠคํŒ…์—์„œ MVC ํŒจํ„ด์— ๋Œ€ํ•œ ์ด๋ก ์ ์ธ ์„ค๋ช…์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ฒ˜์Œ ์ ‘ํ•˜๋Š” ๋ถ„๋“ค์—๊ฒŒ๋Š” ๊ฐœ๋…์ ์œผ๋กœ ์ดํ•ด๊ฐ€ ์ž˜ ๋˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ œ ์„ค๋ช…์ด ๋ถ€์กฑํ•œ ์ ๋„ ์žˆ์ง€๋งŒ, ๋ฌด์—‡๋ณด๋‹ค ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์ดํ•ด๊ฐ€ ๋”์šฑ ์‰ฌ์šธ ๊ฑฐ๋ผ ์ƒ๊ฐํ•˜์—ฌ ์˜ˆ์ œ ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ ์ฝ”๋“œ๋Š” '์ „ํ™”๋ฒˆํ˜ธ๋ถ€'๋ฅผ ์˜ˆ์‹œ๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ , ์กฐํšŒํ•˜๊ณ , ์ œ๊ฑฐํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๋ฐ์ดํ„ฐ์˜ ์ถ”๊ฐ€์™€ ์ œ๊ฑฐ, ๊ฐฑ์‹ ์ด ์ด๋ฃจ์–ด์ง€๋Š”๋ฐ ์ด๊ฒƒ์„ MVC ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ ์—ญํ• ๋ณ„๋กœ ๋‚˜๋ˆ„์—ˆ๋‹ค๊ณ  ์ดํ•ดํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์•„๋ž˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ์˜ˆ์ œ ์ฝ”๋“œ ๋™์ž‘์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘ ์‹œ 4๋ช…์˜ ์ •๋ณด๊ฐ€ ์ž…๋ ฅ๋˜์–ด ์žˆ๊ณ , ๊ธฐ์กด ์ •๋ณด๋ฅผ ์ง€์šฐ๊ฑฐ๋‚˜ ์ƒˆ๋กœ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ ์ฝ”๋“œ ๋™์ž‘

MVC ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ชจ๋ธ์—๋Š” ์‚ฌ๋žŒ์˜ ์ •๋ณด๊ฐ€ ๋ฆฌ์ŠคํŠธ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ๊ณ , ๋ทฐ์—๋Š” ์‚ฌ๋žŒ ๋ชฉ๋ก๊ณผ ์ƒ์„ฑ / ์ œ๊ฑฐ / ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ„ํŠผ๊ณผ ํ…์ŠคํŠธ ๋ฐ•์Šค๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์ค‘๊ฐ„์— ์œ„์น˜ํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. 

MVC ์˜ˆ์ œ ํ”„๋กœ๊ทธ๋žจ ๋‹ค์ด์–ด๊ทธ๋žจ

์ฝ”๋“œ ์„ค๋ช…

์ฝ”๋“œ๋Š” ๋ชจ๋ธ / ์ปจํŠธ๋กค๋Ÿฌ / ๋ทฐ ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์€ ๋ฉ”์ธ ํ”„๋กœ๊ทธ๋žจ ์ˆœ์œผ๋กœ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์˜ˆ์ œ ์ฝ”๋“œ์˜ UML์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์˜ˆ์ œ ํ”„๋กœ๊ทธ๋žจ UML

๋ชจ๋ธ(Model)

Customer ํด๋ž˜์Šค์— ID์™€ Name, Phone, Address ์ •๋ณด๋ฅผ get/set ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

namespace MVCPattern.Model
{
public class Customer
{
public Customer(string id, string name, string phone, string address)
{
_id = id;
_name = name;
_phone = phone;
_address = address;
}
string _id;
public string ID
{
get { return _id; }
set { _id = value; }
}
string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
string _phone;
public string Phone
{
get { return _phone; }
set { _phone = value; }
}
string _address;
public string Address
{
get { return _address; }
set { _address = value; }
}
}
}

์ปจํŠธ๋กค๋Ÿฌ(Controller)

์ปจํŠธ๋กค๋Ÿฌ์—์„œ๋Š” ๋ทฐ์— ์ง์ ‘์ ์œผ๋กœ ์ ‘๊ทผํ•˜์ง€ ์•Š๊ณ , View interface์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก , interface ์—†์ด ์ง์ ‘ ์ ‘๊ทผํ•ด๋„ ์ƒ๊ด€์€ ์—†์ง€๋งŒ ๊ณตํ†ต์ ์ด ๋งŽ์€ ๋ทฐ๋ฅผ ์ง์ ‘ ์ ‘๊ทผํ•˜๊ธฐ ๋ณด๋‹ค๋Š” interface์— ์ ‘๊ทผํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ฝ”๋“œ๊ฐ€ ๋” ๊ฐ„๊ฒฐํ•ด์ง‘๋‹ˆ๋‹ค.
๋ทฐ ์ธํ„ฐํŽ˜์ด์Šค์—๋Š” ํ…์ŠคํŠธ ๋ฐ•์Šค์— ๋ณด์—ฌ์ง€๊ฑฐ๋‚˜ ๋ชฉ๋ก์„ ์ง€์šฐ๊ฑฐ๋‚˜, ๋ฒ„ํŠผ์ด ํด๋ฆญ๋์„ ๋•Œ ์ˆ˜ํ–‰๋˜๋Š” ๋™์ž‘๋“ค์ด ๊ธฐ๋ณธ์œผ๋กœ ์ •์˜๋˜์–ด ์žˆ๊ณ , ๊ทธ ์™ธ ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์ด ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

namespace MVCPattern.Controller
{
public interface IView
{
void setController(Controller controller);
void clearList();
void addCustomer(Customer customer);
void removeCustomer(Customer customer);
void updateListWithCustomer(Customer customer);
string getSelectedCustomerID();
void setSelectedCustomer(Customer customer);
string Name { get; set; }
string Address { get; set; }
string Phone { get; set; }
string ID { get; set; }
bool isModify { set; }
}
}

์ปจํŠธ๋กค๋Ÿฌ์—๋Š” ์œ„์— ์ •์˜ํ•œ ๋ทฐ ์ธํ„ฐํŽ˜์ด์Šค์™€ ๋ชจ๋ธ์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Customer ์ •๋ณด๊ฐ€ View์— ๋„˜๊ธฐ๊ฑฐ๋‚˜ View์˜ ์ •๋ณด๊ฐ€ Customer๋กœ ๋„˜๊ธฐ๊ฑฐ๋‚˜, Customer ๋ชฉ๋ก์„ ์—…๋ฐ์ดํŠธํ•˜๊ฑฐ๋‚˜, Customer๋ฅผ ์ถ”๊ฐ€/์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ๋“ฑ ๋ชจ๋“  ๊ธฐ๋Šฅ์ด ์ปจํŠธ๋กค๋Ÿฌ์— ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

namespace MVCPattern.Controller
{
public class Controller
{
IView _view = null;
IList _customers = null;
Customer _selectedCustomer;
public Controller(IView view, IList customers)
{
_view = view;
_customers = customers;
view.setController(this);
}
private void updateView(Customer customer)
{
_view.ID = customer.ID;
_view.Name = customer.Name;
_view.Phone = customer.Phone;
_view.Address = customer.Address;
}
private void updateCustomer(Customer customer)
{
customer.ID = _view.ID;
customer.Name = _view.Name;
customer.Phone = _view.Phone;
customer.Address = _view.Address;
}
public void LoadView()
{
_view.clearList();
foreach (Customer customer in _customers)
_view.addCustomer(customer);
_view.setSelectedCustomer((Customer)_customers[0]);
}
public void SelectedCustomer(string selectedCustomerId)
{
foreach (Customer customer in this._customers)
{
if (customer.ID == selectedCustomerId)
{
_selectedCustomer = customer;
updateView(customer);
_view.setSelectedCustomer(customer);
this._view.isModify = false;
break;
}
}
}
public void AddNewCustomer()
{
_selectedCustomer = new Customer("", "", "", "");
updateView(_selectedCustomer);
_view.isModify = true;
}
public void RemoveCustomer()
{
string id = this._view.getSelectedCustomerID();
Customer customerToRemove = null;
if (id != "")
{
foreach (Customer customer in _customers)
{
if (customer.ID == id)
{
customerToRemove = customer;
break;
}
}
if (customerToRemove != null)
{
int index = _customers.IndexOf(customerToRemove);
_customers.Remove(customerToRemove);
_view.removeCustomer(customerToRemove);
if (index > -1 && index < _customers.Count)
{
_view.setSelectedCustomer((Customer)_customers[index]);
}
}
}
}
public void Register()
{
updateCustomer(_selectedCustomer);
if (!_customers.Contains(_selectedCustomer))
{
_customers.Add(_selectedCustomer);
_view.addCustomer(_selectedCustomer);
}
else
{
_view.updateListWithCustomer(_selectedCustomer);
}
_view.setSelectedCustomer(_selectedCustomer);
_view.isModify = false;
}
}
}

๋ทฐ(View)

๋ทฐ์—๋Š” View Interface์™€ ๋ฒ„ํŠผ ๋ฐ ๋ฆฌ์ŠคํŠธ ๋ทฐ ์ด๋ฒคํŠธ๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๊ธด ๊ด€๊ณ„๋กœ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ํ•˜๋‹จ์— ์ฒจ๋ถ€๋œ ์˜ˆ์ œ ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

namespace MVCPattern.View
{
public partial class MainForm : Form, IView
{
MVCPattern.Controller.Controller _controller;
public MainForm()
{
InitializeComponent();
}
public void setController(MVCPattern.Controller.Controller controller)
{
_controller = controller;
}
public void clearList() {...}
public void addCustomer(Customer customer) {...}
public void updateListWithCustomer(Customer customer) {...}
public void removeCustomer(Customer customer) {...}
public string getSelectedCustomerID() {...}
public void setSelectedCustomer(Customer customer) {...}
private void buttonRemoveCustomer_Click(object sender, EventArgs e)
{
_controller.RemoveCustomer();
}
private void buttonRegister_Click(object sender, EventArgs e)
{
_controller.Register();
}
private void listViewCustomer_SelectedIndexChanged(object sender, EventArgs e)
{
if (listViewCustomer.SelectedItems.Count > 0)
_controller.SelectedCustomer(listViewCustomer.SelectedItems[0].Text);
}
private void buttonNewCustomer_Click(object sender, EventArgs e)
{
_controller.AddNewCustomer();
}
}
}

ํ”„๋กœ๊ทธ๋žจ ๋ฉ”์ธ

๋ฉ”์ธ์—์„œ ๋ทฐ์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ, ๋ทฐ์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ธ์ž๋กœ ๋„˜๊น๋‹ˆ๋‹ค.

namespace ExamMVC
{
static class Program
{
/// <summary>
/// ํ•ด๋‹น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ฃผ ์ง„์ž…์ ์ž…๋‹ˆ๋‹ค.
/// </summary>
[STAThread]
static void Main()
{
MainForm form = new MainForm();
// Add some dummy data
IList users = new ArrayList();
users.Add(new Customer("1", "๊น€์ฒ ์ˆ˜", "010-1234-5678", "์„œ์šธ์‹œ ๊ฐ•๋™๊ตฌ"));
users.Add(new Customer("2", "์ด์ˆ˜์ง€", "010-0000-1111", "์„œ์šธ์‹œ ๊ฐ•๋‚จ๊ตฌ"));
users.Add(new Customer("3", "์ตœ์ˆ˜์ •", "010-2222-3333", "์„œ์šธ์‹œ ๊ฐ•์„œ๊ตฌ"));
users.Add(new Customer("4", "๋ฐ•์žฌ์ˆ˜", "010-4444-5555", "์„œ์šธ์‹œ ๊ฐ•๋ถ๊ตฌ"));
Controller controller = new Controller(form, users);
controller.LoadView();
form.ShowDialog();
}
}
}

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฒจ๋ถ€๋œ ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

ExamMVC.zip
0.76MB

๋ฐ˜์‘ํ˜•