MVC 이란?
어플리케이션을 Model, View, Controller 로 나눠서 가독성 / 유지보수 / 확장성을 늘리는 소프트웨어 디자인 패턴
- Model
- 어플리케이션에서 필요한 데이터를 담는 영역
- View
- 사용자에게 보여지는 순수 어플리케이션의 UI
- Controller
- View 와 Model을 연결하는 비즈니스 로직을 담는 영역
Dwitter 서버에 Data층 만들기 ✏️
Router (/router/tweets.js)
import express from 'express'; import 'express-async-errors'; import * as tweetRepository from '../data/tweet.js'; const router = express.Router(); // GET /tweets // GET /tweets?username=:username router.get('/',(req,res,next) => { const username = req.query.username; const data = username ? tweetRepository.getAllByUsername(username) : tweetRepository.getAll(); res.status(200).json(data); }); // GET /tweets/:id router.get('/:id',(req,res,next) => { const id = req.params.id; const tweet = tweetRepository.getById(id); if(tweet){ res.status(200).json(tweet); }else { res.status(404).json({message : `Tweet id(${id}) not found`}); } }); // POST /tweets router.post('/',(req,res,next) => { const {text, name, username} = req.body; const tweet = tweetRepository.create(text, name, username); res.status(201).json(tweet); }); // PUT /tweets/:id router.put('/:id',(req,res,next) => { const id = req.params.id; const text = req.body.text; const tweet = tweetRepository.update(id, text); if(tweet){ res.status(200).json(tweet); }else { res.status(404).json({message : `Tweet id(${id}) not found`}); } }); // DELETE /tweets/:id router.delete('/:id',(req,res,next) => { const id = req.params.id; tweetRepository.remove(id); res.sendStatus(204); }); export default router;
데이터 처리 (/data/tweet.js)
let tweets = [ { id: '1', text: 'Hello world!', createdAt: Date.now().toString(), name: 'Dylan', username: 'dylan', url: 'https://cdn-icons-png.flaticon.com/512/3576/3576887.png' }, { id: '2', text: 'New message :)', createdAt: Date.now().toString(), name: 'Bob', username: 'bob', url: 'https://cdn-icons-png.flaticon.com/512/3576/3576887.png' }, ]; export function getAll(){ return tweets; } export function getAllByUsername(username){ return tweets.filter((tweet)=> tweet.username === username); } export function getById(id){ return tweets.find(tweet=> tweet.id === id); } export function create(text, name, username){ const tweet = { id : Date.now().toString(), text, createdAt : new Date(), name, username, }; tweets = [tweet, ...tweets]; return tweets; } export function update(id, text){ const tweet = tweets.find(tweet => tweet.id === id); if(tweet){ tweet.text= text; } return tweet; } export function remove(id){ tweets = tweets.filter(tweet=> tweet.id !== id); }
Dwitter 서버에 Controller 층 만들기 ✏️
Router(/router/tweets.js)
import express from 'express'; import 'express-async-errors'; import * as tweetController from '../controller/tweet.js'; const router = express.Router(); // GET /tweets // GET /tweets?username=:username router.get('/', tweetController.getTweets); // GET /tweets/:id router.get('/:id',tweetController.getTweet); // POST /tweets router.post('/',tweetController.createTweet); // PUT /tweets/:id router.put('/:id',tweetController.updateTweet); // DELETE /tweets/:id router.delete('/:id',tweetController.deleteTweet); export default router;
Controller (/controller/tweet.js)
import * as tweetRepository from '../data/tweet.js'; export function getTweets(req,res) { const username = req.query.username; const data = username ? tweetRepository.getAllByUsername(username) : tweetRepository.getAll(); res.status(200).json(data); }; export function getTweet(req,res){ const id = req.params.id; const tweet = tweetRepository.getById(id); if(tweet){ res.status(200).json(tweet); } else { res.status(404).json({ message : `Tweet id (${id}) not found`}); } } export function createTweet(req,res) { const {text, name, username} = req.body; const tweet = tweetRepository.create(text, name, username); res.status(201).json(tweet); } export function updateTweet(req,res){ const id = req.params.id; const text = req.body.text; const tweet = tweetRepository.update(id, text); if(tweet){ res.status(200).json(tweet); }else { res.status(404).json({message : `Tweet id(${id}) not found`}); } } export function deleteTweet(req,res){ const id = req.params.id; tweetRepository.remove(id); res.sendStatus(204); }
Async /Await 로 변경
보통 컨트롤러에서는 DB 혹은 파일에서 데이터를 읽어오기때문에 async 로 모두 변경처리해야한다.
데이터 처리 부분 (/data/tweet.js)
let tweets = [ { id: '1', text: 'Hello world!', createdAt: Date.now().toString(), name: 'Dylan', username: 'dylan', url: 'https://cdn-icons-png.flaticon.com/512/3576/3576887.png' }, { id: '2', text: 'New message :)', createdAt: Date.now().toString(), name: 'Bob', username: 'bob', url: 'https://cdn-icons-png.flaticon.com/512/3576/3576887.png' }, ]; export async function getAll(){ return tweets; // async 가 붙으면 모두 promise로 리턴 } export async function getAllByUsername(username){ return tweets.filter((tweet)=> tweet.username === username); // async 가 붙으면 모두 promise로 리턴 } export async function getById(id){ return tweets.find(tweet=> tweet.id === id); // async 가 붙으면 모두 promise로 리턴 } export async function create(text, name, username){ const tweet = { id : Date.now().toString(), text, createdAt : new Date(), name, username, }; tweets = [tweet, ...tweets]; return tweets; // async 가 붙으면 모두 promise로 리턴 } export async function update(id, text){ const tweet = tweets.find(tweet => tweet.id === id); if(tweet){ tweet.text= text; } return tweet; // async 가 붙으면 모두 promise로 리턴 } export async function remove(id){ tweets = tweets.filter(tweet=> tweet.id !== id); }
컨트롤러 부분 (/controller/tweet.js)
import * as tweetRepository from '../data/tweet.js'; export async function getTweets(req,res) { const username = req.query.username; const data = await (username ? tweetRepository.getAllByUsername(username) : tweetRepository.getAll()); res.status(200).json(data); }; export async function getTweet(req,res){ const id = req.params.id; const tweet = await tweetRepository.getById(id); if(tweet){ res.status(200).json(tweet); } else { res.status(404).json({ message : `Tweet id (${id}) not found`}); } } export async function createTweet(req,res) { const {text, name, username} = req.body; const tweet = await tweetRepository.create(text, name, username); res.status(201).json(tweet); } export async function updateTweet(req,res){ const id = req.params.id; const text = req.body.text; const tweet = await tweetRepository.update(id, text); if(tweet){ res.status(200).json(tweet); }else { res.status(404).json({message : `Tweet id(${id}) not found`}); } } export async function deleteTweet(req,res){ const id = req.params.id; await tweetRepository.remove(id); res.sendStatus(204); }
마지막 점검
구조 / 패턴 변경후 RUN 테스트 🙂 잘 동작하는 모습 👏👏