프로그래밍 공부가 모두 그렇듯, 방법론 개념 자체에 관한 것은 완벽한 정의를 쓰기보다는 이해한 것을 위주로 정리해보려고 한다!
MVC pattern이란 Model, View, Controller로 이뤄지는 기본적인 웹 백엔드 구조이다.
Model은 SQL이나 NoSQL 등의 DB에서 구성되는 스키마 자체를 얘기한다.
View는 사용자에게 보여지는 html, ejs 등의 view format page를 의미한다.
Controller는 View와 model 구성을 이어주는 중간 functionality를 의미한다.
보통 코드를 구성할 때 저 세부분은 따로 폴더를 만들어서 각각의 model이나 기능에 따라 따로 구현을 한다. 아래와 같이..
이건 뭐 이런거다 개념만 알고있으면 되고... 중요한 것은 실제 프로젝트에 적용해보는 것이다.
express에서 routes를 사용한다면 routes폴더의 어떤 model에 관한 router을 다음과 같이 구성할 수 있다. Controller을 따로 뺐기 때문에 가독성 면에서 훨씬 좋다.
const express = require("express");
const router = express.Router();
const campgrounds = require("../controllers/campgrounds");
const catchAsync = require("../utils/catchAsync");
const { isLoggedIn, isAuthor, validateCampground } = require("../middleware");
router
.route("/")
.get(catchAsync(campgrounds.index))
.post(
isLoggedIn,
validateCampground,
catchAsync(campgrounds.createCampground)
);
보면 알겠지만 "/" route에 관한 get request, post request 안의 callback은 모두 다른 곳, 즉 controller에서 require한 function들임을 알 수 있다.
또한 router.get()이 아니라 router.route("/").get().post.()로 같은 route를 가졌지만 method가 다른 애들을 하나로 묶었다는 것도 알 수 있다. 라우팅 파일은 각각의 Routes에 대해 req가 들어갔을 때 어떤 일이 일어나는지 동작을 이해할 수 있게, 깔끔하고 읽기 쉽게 작성하는 것이 좋다고 한다.
campground를 위해 functionlity가 저장된 controllers/campgrounds.js의 코드는 아래와 같다. 필요한 모듈들을 만들고, call back으로 쓰일 function들을 exports하는 구조로 이뤄짐을 알 수 있다.
const Campground = require("../models/campground");
module.exports.index = async (req, res, next) => {
const campgrounds = await Campground.find({});
res.render("campgrounds/index", { campgrounds });
};
module.exports.updateCampground = async (req, res, next) => {
const { id } = req.params;
const campground = await Campground.findByIdAndUpdate(id, {
...req.body.campground,
});
req.flash("success", "Successfully updated campground!");
res.redirect(`/campgrounds/${campground._id}`);
};