[TIL]React와 MVVM패턴

gun·2021년 3월 9일
6

TIL

목록 보기
14/19

MVVM패턴이란?

MVVM (Model-View-ViewModel) 패턴은 Model View, View, Model의 약자로 프로그램의 비지니스 로직과, 프레젠테이션 로직을 UI로 명확하게 분리하는 패턴입니다. 데이터를 관리하는 로직과 UI로직을 깔끔하게 분리하면, 애플리케이션을 보다 쉽게 테스트, 유지보수 할 수 있습니다. 또한 코드의 재 사용성을 크게 개선하고 개발자와 UI 디자이너가 애플리케이션의 각 부분을 개발 할 때보다 쉽게 협업 할 수 있는 디자인 패턴 입니다.

앞서 말 했듯이 MVVM 패턴은 3가지 구성요소로 구성되어 있습니다.
1. M - (Model) 데이터를 보관하고 있는 부분으로, 서버를 담당하고 있습니다.
2. V - (View) UI로직, 사용자의 눈에 보이는 인터페이스를 담당합니다.
3. VM - (View Model) Model에 데이터를 요청하고, 가공합니다.

위 사진에서 보이듯 View와 ViewModel, ViewModel과 Model이 각각 독립적인 형태를 만들어, 위에서 말한 비지니스 로직과 UI로직(View)을 분리한다 라고 할수 있습니다.

React와 MVVM패턴

React만 사용하여 MVVM 패턴을 적용하는 것은 추천하지 않는 방식입니다. 대부분 개발을 하며, MVVM의 모델(M)에 데이터(상태)를 관리하는 로직을 주로 Redux,context-api 등 을 사용아여 관리하기 때문입니다.

저희는 MVVM의 기초적인 패턴만 연습해본다는 생각으로 React만을 이용하여 TodoList를 만들어 보겠습니다.

$ npx create-react-app react_mvvm
$ cd react_mvvm
$ npm start

리액트를 실행한 뒤 MVVM 패턴에 맞게 디렉터리 구조를 살펴보겠습니다.

./src/model/Model.js
./src/viewModel/ViewModel.js 
./src/view/BookView.js
./src/App.js

Model

  • 어플리케이션의 데이터를 관리 하는부분으로 서버와 같이 생각하면 됩니다.
//src/model/Model.js
class TodoModel {
    constructor () {
      this.todos = [
        {
          id:1,
          todos: "MVVM패턴 공부하기",
          check: true
        }, {
          id:2,
          todos: "오늘 배운거 블로그 쓰기",
          check: false
        }, {
          id:3,
          todos: "프로젝트 완성하기",
          check: false
        }
      ]
    }
  
    add (todo) {
        this.todos.push({todos:todo, check:false, id: this.todos.length+1})
    }
    
    remove (idx) {
      let temp = this.todos[idx]
      
      this.todos[idx] = this.todos[this.todos.length-1]
      this.todos[this.todos.length-1] = temp
  
      return this.todos.pop()
    }
  
    onToggle (idx) {
      const index = this.todos.findIndex(todo => todo.id === idx+1)
      const nextTodos = [...this.todos]
        nextTodos[index] = {
          ...this.todos[index],
          check : !this.todos[index].check
        }
        this.todos = nextTodos
      }
    
  
    getAll () {
      return this.todos
    }
  }
  
  export default TodoModel

View Model

  • View가 사용할 메서드를 구현하는 부분으로, View에 데이터(상태) 변화를 알립니다.
    • View Model은 View 와 1:N 형태를 이루고 있습니다. 예를 들어 2개의 View에서 서로 다른 두 모델의 데이터를 이용해 사용자에게 보여줘야 한다면, View에서 데이터를 가공하는 것이 아닌 View Model에서 가공 해야 한다는 말 입니다.
    • React Hooks의 UseSelector과 Redux의 dispatch라고 생각하시면 될것 같습니다.
//src/viewModel/ViewModel.js
class TodosViewModel {
    constructor (model) {
      this.model = model
    }
    add (todo) {
       return this.model.add(todo)
      }
    getAll () {
      return this.model.getAll()
    }
    onToggle (idx) {
      console.log(idx)
        return this.model.onToggle(idx)
    }
    remove (idx) {
      return this.model.remove(idx)
    }
  }
  
  export default TodosViewModel

View

  • 사용자에게 보여지는 부분으로 UI와 관련된 것을 다루는 부분입니다. 비지니스 로직은 최대한 피해야 하는 부분입니다.
    • remove, onToggle, add 함수는 이벤트시 리 렌더링을 위해 작성 했습니다.
// src/view/View.js
import React,{useState} from 'react';

function TodoView (props) {
    let todos = props.viewModel.getAll()
    const [inputs, setInput] = useState('')
    //리 렌더링을 위해 useState를 이용
    const [toggleState, setToggleState] = useState(true)

    const remove = idx => {
        props.viewModel.remove(idx)
        setToggleState(!toggleState)
    }
    const onToggle = idx => {
        props.viewModel.onToggle(idx)
        setToggleState(!toggleState)
    }
    const add = () => {
        props.viewModel.add(inputs)
        setInput('')
        setToggleState(!toggleState)
    }

    return (
      <>
        <div className="form">
          <input type="text" placeholder="등록" value={inputs} onChange={(e) => setInput(e.target.value)}/>
          <button className="create" onClick={add}>등록</button>
        </div>
        <div className="list">
          <ul>
            {todos.map( (todo, idx) => (
              <li key={idx} >
                <span onClick={() => onToggle(idx)}>
                {todo.check === true ? <span style={{color:'gray'}}>{todo.todos}</span> : <span>{todo.todos}</span>}
                </span>
                <button 
                  onClick={() => remove(idx)}
                  style={{color: 'red'}}
                >
                  삭제
                </button>
              </li>
            ))}
          </ul>
        </div>
      </>    
    );
  
  }

  export default TodoView

View와 Model View, Model View 와 Model을 연결해 줍니다

//src/App.js
import React from 'react';

import TodoViewModel from './viewModel/ViewModel'
import TodoModel from './model/Model'
import TodoView from './view/TodoView'


function App() {
  const bookModel = new TodoModel()
  const bookViewModel = new TodoViewModel(bookModel)

  return (
    <>
      <TodoView 
        viewModel={bookViewModel}
      />
    </>
  )
}

export default App;

마치며...

간단하게 TodoList를 통해 MVVM 패턴을 알아봤습니다. React만 사용했기 때문에 로직이 이상하게 느껴지고 보기 불편하다는 생각이 들지만 상태관리 라이브러리 Redux를 사용해 Model을 구현하고, React-hooks와 dispatch를 통해 Model View를 구현한다면 좀더 완벽하게 MVVM패턴을 구현 할 수 있을거 같습니다.

전체코드

0개의 댓글