게시판 만들기 (백엔드)

김주언·2022년 9월 28일
0

MERN

목록 보기
2/4
post-thumbnail

1. 프로젝트 생성

RESTful API 서버 만들거임

HTTP VerbActionPath설명
GETindex/posts모든 서적 리스트 조회
GETretrieve/posts/:id특정 서적 조회
POSTcreate/posts신규 서적 생성
PUTreplace/posts/:id특정 서적 갱신
DELETEdelete/posts/:id특정 서적 삭제

이렇게...

1.1 프로젝트 구조

  • client : 리액트 프로젝트
  • server : 백엔드

server 폴더 의존성 설치

npm init -y
npm install mongoose express cors dotenv moment

server.js - 서버 설정

const express = require("express");
const cors = require("cors");
require("dotenv").config({ path: "./config.env" });
const port = process.env.PORT || 5500;

const app = express();

// middlewares
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(express.json()); // 리퀘스트 바디의 json을 해석

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});

1.2 Mongoose를 사용한 Connection

Mongoose는 MongoDB 기반 ODM(Object Data Mapping) Node.JS 전용 라이브러리

ODM은 데이터베이스와 객체지향 프로그래밍 언어 사이 호환되지 않는 데이터를 변환하는 프로그래밍 기법. 즉 MongoDB 에 있는 데이터를 Application에서 JavaScript 객체로 사용 할 수 있도록 해준다

mongoose에 접속하기 위한 계정과 비밀번호를 환경변수로 관리하기 위해 dotenv를 사용하기로 한다.

.env파일에 아래와 같이 환경변수를 작성한다.

ATLAS_URI=mongodb+srv://jueon-admin:<password>@cluster0.g2eem.mongodb.net/<database>?retryWrites=true&w=majority

server.js 파일에 mongoose 모듈을 require하고 connect 메소드로 MongoDB에 connect한다.

const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
require("dotenv").config({ path: "./config.env" });
const port = process.env.PORT || 5500;
const { ATLAS_URI } = process.env;

const app = express();

// middlewares

// connect to mongoDB server
mongoose.connect(ATLAS_URI).then(() => {
  console.log("Successfully connected to mongodb");
});

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});




2. Schema & Model 생성

2.1 Schema

RDBMS의 Schema는 데이터베이스를 구성하는 레코드의 크기, 키(key)의 정의, 레코드와 레코드의 관계, 검색 방법 등을 정의한 것이다.

Mongoose의 Schema는 MongoDB에 저장되는 document의 Data 구조 즉 필드 타입에 관한 정보를 JSON 형태로 정의한 것으로 RDBMS의 테이블 정의와 유사한 개념이다.

MongoDB는 Schema-less하다. 이는 RDMS처럼 고정 Schema가 존재하지 않는다는 뜻으로 같은 Collection 내에 있더라도 document level의 다른 Schema를 가질 수 있다는 의미이다.

이는 자유도가 높아서 유연한 사용이 가능하다는 장점이 있지만 명시적인 구조가 없기 때문에 어떤 필드가 어떤 데이터 타입인지 알기 어려운 단점이 있다. 이러한 문제를 보완하기 위해서 Mongoose는 Schema를 사용한다.

간단히 말하자면 schema는 document의 구조가 어떻게 생겼는지 알려주는 역할

server 폴더 내부에 models 폴더를 생성하고 Post.js 파일을 생성한 후 코드를 작성한다.

server/models/Post.js

const mongoose = require("mongoose");
const moment = require("moment");

const postSchema = new mongoose.Schema({
  title: { type: String, required: true },
  content: { type: String, required: true },
  createdAt: { type: String, default: moment().format("YYYY-MM-DD hh:mm:ss") },
  updatedAt: { type: Date },
});

const Post = mongoose.model("post", postSchema);
module.exports = Post;

2.2 Model

model() 메소드에 문자열과 schema를 전달하여 model을 생성한다. model은 보통 대문자로 시작한다.

const Post = mongoose.model("post", postSchema);

첫번째 인자는 해당 collection의 단수적 표현을 나타내는 문자열이다. 실제 collection의 이름은 ‘Posts’로 자동 변환되어 사용된다.



3. RESTful API

3.1 Router

router 사용을 위해 server.js 파일을 수정한다

// modules
const app = express();

// middlewares

// route config 추가
app.use("/posts", require("./routes/posts"));

// connect to mongoDB server

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});

require(경로명) 으로부터 모듈을 받아와서, /posts 로 요청이 들어오면 이를 해당 모듈이 처리하게 된다

이 때 모듈은 라우터이고, 라우터는 또 다시 내부적으로 경로를 관리하기 때문에 결론적으로는 /posts 이하 모든 경로를 관리하게 되는것

CRUD 구현

server/routes/posts.js 파일을 생성한다.

1. CREATE ( POST /posts/ )

포스트 생성

const router = require("express").Router();
const Post = require("../models/Post");

router.post("/", (req, res) => {
  const post = new Post();
  post.title = req.body.title;
  post.content = req.body.content;
  // moment 모듈을 사용하여 날짜 포매팅
  post.createdAt = moment().format("YYYY-MM-DD hh:mm:ss");

  post.save((err, result) => {
    if (err) res.json(err);
    res.json(result);
  });
});

모델은 생성자 역할이다. 인스턴스 생성 후 필드를 지정해주고 .save 메소드를 이용하여 데이터를 데이터베이스에 저장한다.

2.1 RETRIEVE ( GET /posts/ )

전체 포스트 출력하기

const router = require("express").Router();
const Post = require("../models/Post");

router.get("/", (req, res) => {
  Post.find({})
    .sort("-createdAt") // 최근 작성된 포스트 순으로 정렬한다.
    .exec((err, posts) => {
      if (err) return res.json(err);
      res.json(posts);
    });
});

.exec 함수 앞에 DB에서 데이터를 어떻게 찾을지, 어떻게 정렬할지 등등을 함수로 표현하고, exec안의 함수에서 해당 data를 받아와서 할일을 정하는 구조

GET과 POST 결과는 위 사진과 같다



2.2 RETRIEVE ( GET /posts/:id )

특정 포스트 출력

router.get("/:post_id", (req, res) => {
  Post.findOne({ _id: req.params.post_id }, (err, result) => {
    if (err) res.json(err);
    res.json(result);
  });
});


3. UPDATE ( PUT /posts/:post_id )

router.put("/:post_id", (req, res) => {
  req.body.updatedAt = moment().format("YYYY-MM-DD hh:mm:ss");
  Post.findOneAndUpdate(
    { _id: req.params.post_id },
    req.body,
    (err, result) => {
      if (err) res.json(result);
      res.json({ message: "post updated" });
    }
  );
});


4. DELETE ( /posts/:post_id )

router.delete("/:post_id", (req, res) => {
  Post.deleteOne({ _id: req.params.post_id }, (err) => {
    if (err) return res.json(err);
    res.status(204).end();
  });
});

삭제된다...

이제 프론트 할거임

profile
학생 점심을 좀 차리시길 바랍니다

0개의 댓글