리액트 개발 체크리스트

리린·2021년 8월 17일
0

MongoDB

목록 보기
2/2

사용하는 경우: DB가 필요할 경우

MongoDB 시작하기

  1. (필수)시작하기
  2. (필수) esm으로 import 모듈 사용하기
    1) 다음 파일 추가
    (jsconfig.json)
    {
    "compilerOptions":{
    "target":"es6",
    "module": "es2015"
    },
    "include":["src/*/"]
    }
    2) src/index.js 다음과 같이 작성
// 이 파일에서만 no-global-assign ESLint 옵션을 비활성화합니다
/* eslint-disable no-global-assign */

require = require('esm')(module /*, options*/);
module.exports = require('./main.js');

3) main.js에 나머지 코드 작성

스키마 및 모델 생성하기

1) mongoose 모듈 가져오기

import mongoose from 'mongoose';

2) mongoose 모듈에서 {Schema} 받아오기

const { Schema } = mongoose;

3) 스키마의 인스턴스인 PostSchema 스키마 작성하기

  • 형식 등을 정의
const PostSchema = new Schema({
  title: String,
  body: String,
  tags: [String],
  publishedDate: {
    type: Date,
    default: Date.now, //현재 날짜를 기본값으로 지정
  },
  user: {
    _id: mongoose.Types.ObjectId,
    username: String,
  },
});

4) PostSchema를 따르는 모델인 Post 작성

const Post = mongoose.model('Post', PostSchema);
export default Post;

(전체)
src/models/post.js

import mongoose from 'mongoose';

const { Schema } = mongoose;

const PostSchema = new Schema({
  title: String,
  body: String,
  tags: [String],
  publishedDate: {
    type: Date,
    default: Date.now, //현재 날짜를 기본값으로 지정
  },
  user: {
    _id: mongoose.Types.ObjectId,
    username: String,
  },
});

const Post = mongoose.model('Post', PostSchema);
export default Post;

db 생성 및 조회 - Controller 함수 추가하기

  1. api에 Controller 함수 이름과 경로 추가하기
    (src/api/posts/index.js)
import Router from 'koa-router';
import * as postsCtrl from './posts.ctrl';

const posts = new Router();

posts.get('/', postsCtrl.list);
posts.post('/', postsCtrl.write);
posts.get('/:id', postsCtrl.read);
posts.delete('/:id', postsCtrl.remove);
posts.patch('/:id', postsCtrl.update);

export default posts;
  1. 생성함수(컨트롤러 함수) 추가
    (src/api/posts/posts.ctrl.js)
    1) ctx.request.body에서 어떤 데이터를 받기를 기대하는지 작성하기
    2) 모델의 인스턴스인 post를 생성하기
    3) post가 save()될 때까지 기다리기
    4) ctx.body에 post를 담기
  • 이때 ctx.body는 response에 해당함
    5) try~ catch로 에러잡기
    (src/api/posts/posts.ctrl.js)
/*
  POST /api/posts
  {
    title: '제목',
    body: '내용',
    tags: ['태그1', '태그2']
  }
*/
export const write = async ctx => {
  const { title, body, tags } = ctx.request.body;
  const post = new Post({
    title,
    body,
    tags,
  });
  try {
    await post.save();
    ctx.body = post;
  } catch (e) {
    ctx.throw(500, e);
  }
};
  1. 조회함수(컨트롤러 함수) 추가

1)async~await로 Post모델을 받아오길 기다린다.
2) .find() 메서드를 사용하여 전체 데이터를 찾는다. (find()안에 조건이 있으면 그 조건에 맞는 데이터를 찾는다)
3) exec() 메서드를 사용하여 조건을 붙인다( 추후 알아볼 것 )

(src/api/posts/posts.ctrl.js)

/*
  GET /api/posts
*/
export const list = async ctx => {
  try {
    const posts = await Post.find().exec();
    ctx.body = posts;
  } catch (e) {
    ctx.throw(500, e);
  }
};
 
  1. 특정 포스트 조회함수 (컨트롤러 함수) 추가

1) ctx.params에서 id를 뽑아온다
2) .findById(id) 메서드를 사용하여 해당하는 아이디의 데이터를 찾는다.
3) exec() 메서드를 사용하여 조건을 붙인다( 추후 알아볼 것 )
4) post가 없으면 ctx.status에 404값을 집어넣는다.
5) 4가 아니라면 ctx.body값에 post를 집어넣는다. 이때 ctx.body는 response 취급이다.
6) try~ catch로 에러를 잡아낸다.

(src/api/posts/posts.ctrl.js)

/*
  GET /api/posts/:id
*/
export const read = async ctx => {
  const { id } = ctx.params;
  try {
    const post = await Post.findById(id).exec();
    if (!post) {
      ctx.status = 404; // Not Found
      return;
    }
    ctx.body = post;
  } catch (e) {
    ctx.throw(500, e);
  }
};
  1. 데이터 삭제함수 (컨트롤러 함수 ) 추가
    1) ctx.params에서 id를 뽑아온다
    2) .findByIdAndRemove(id) 메서드를 사용하여 해당하는 아이디의 데이터를 찾은 뒤 삭제한다.
    3) exec() 메서드를 사용하여 조건을 붙인다( 추후 알아볼 것 )
    4) post가 존재하면 ctx.status에 204값을 집어넣는다.
    5) 4가 아니라면 try~ catch로 에러를 잡아낸다. 이때 ctx.throw에 500값(서버에서 제대로 처리되지 않음)과 에러를 집어넣는다.
    (src/api/posts/posts.ctrl.js)
/*
  DELETE /api/posts/:id
*/
export const remove = async ctx => {
  const { id } = ctx.params;
  try {
    await Post.findByIdAndRemove(id).exec();
    ctx.status = 204; // No Content (성공하기는 했지만 응답할 데이터는 없음)
  } catch (e) {
    ctx.throw(500, e);
  }
};
  1. 데이터 수정함수(컨트롤러 함수) 추가
    1) ctx.params에서 id를 뽑아온다
    2) .findByIdAndUpdate(id, ctx.request.body, {
    new: true, // 이 값을 설정하면 업데이트된 데이터를 반환합니다.
    // false일 때는 업데이트되기 전의 데이터를 반환합니다.
    });
    메서드를 사용하여 해당하는 아이디의 데이터를 넣은 뒤 반환한다. 새 값을 반환한다.
    3) exec() 메서드를 사용하여 조건을 붙인다( 추후 알아볼 것 )
    4) post가 존재하지 않으면 ctx.status에 404값을 집어넣는다.
    5) 4가 아니라면 ctx.body에 post를 넣는다. 이때 ctx.body는 response 취급이다.
    6) try~ catch로 에러를 잡는다.

(src/api/posts/posts.ctrl.js)

/*
  PATCH /api/posts/:id
  {
    title: '수정',
    body: '수정 내용',
    tags: ['수정', '태그']
  }
*/
export const update = async ctx => {
  const { id } = ctx.params;
  try {
    const post = await Post.findByIdAndUpdate(id, ctx.request.body, {
      new: true, // 이 값을 설정하면 업데이트된 데이터를 반환합니다.
      // false일 때는 업데이트되기 전의 데이터를 반환합니다.
    }).exec();
    if (!post) {
      ctx.status = 404;
          return;
    }
    ctx.body = post;
  } catch (e) {
    ctx.throw(500, e);
  }
};
  1. ObjectId 검증하기
    1) ObjectId를 mongoose.Types에서 꺼내오기
    2) id를 ctx.params에서 꺼내오기
    3) ObjectId.isValid(id)를 이용하여 클라이언트가 받아온 id가 ObjectId인지 확인하기
    4) 아닐 경우 400(bad Request)를 쏘기
    5) next()를 리턴하여 미들웨어로 만들기
    (src/api/posts/posts.ctrl.js)
import Post from '../../models/post';
import mongoose from 'mongoose';

const { ObjectId } = mongoose.Types;

export const checkObjectId = (ctx, next) => {
  const { id } = ctx.params;
  if (!ObjectId.isValid(id)) {
    ctx.status = 400; // Bad Request
    return;
  }
  return next();
};
  1. 라우터 경로 설정하여 미들웨어 적용하기
    src/api/posts/index.js
import Router from 'koa-router';
import * as postsCtrl from './posts.ctrl';

const posts = new Router();
posts.get('/', postsCtrl.list);
posts.post('/', postsCtrl.write);
posts.get('/:id', postsCtrl.checkObjectId, postsCtrl.read);
posts.delete('/:id', postsCtrl.checkObjectId, postsCtrl.remove);
posts.patch('/:id', postsCtrl.checkObjectId, postsCtrl.update);

export default posts;
  1. request로 전달받은 요청 내용 검증하기
    1) Joi 모듈 설치하기
import Joi from 'joi';

2) 해당 컨트롤러 함수 안에 비교할 기준 만들기

const schema = Joi.object().keys({
    // 객체가 다음 필드를 가지고 있음을 검증
    title: Joi.string().required(), // required()가 있으면 필수 항목
    body: Joi.string().required(),
    tags: Joi.array()
      .items(Joi.string())
      .required(), // 문자열로 이루어진 배열
  });
  • .Joi.obJect().keys({}) : 객체를 검증한다.
  • .required(): 필수항목이다.
  • .items(Joi.string()): 문자열로 이루어진 배열이다.

3) 검증이 실패할 경우 작성하기
result는 검증의 성공과 실패 여부를 담는다.

const result = schema.validate(ctx.request.body);
  • result.error 객체는 다음과 같이 생겼다.
{
    "isJoi": true,
    "name": "ValidationError",
    "details": [
        {
            "message": "\"tags\" is required",
            "path": [
                "tags"
            ],
            "type": "any.required",
            "context": {
                "key": "tags",
                "label": "tags"
            }
        }
    ],
    "_object": {
        "title": "제목",
        "body": "내용"
    }

(src/api/posts/posts.ctrl.js)

import Post from '../../models/post';
import mongoose from 'mongoose';
import Joi from 'joi';

(...)
export const write = async ctx => {
  const schema = Joi.object().keys({
    // 객체가 다음 필드를 가지고 있음을 검증
    title: Joi.string().required(), // required()가 있으면 필수 항목
    body: Joi.string().required(),
    tags: Joi.array()
      .items(Joi.string())
      .required(), // 문자열로 이루어진 배열
  });

  // 검증하고 나서 검증 실패인 경우 에러 처리
  const result = schema.validate(ctx.request.body);
  if (result.error) {
    ctx.status = 400; // Bad Request
    ctx.body = result.error;
    return;
  }

  const { title, body, tags } = ctx.request.body;
  const post = new Post({
    title,
    body,
    tags,
  });
  try {
    await post.save();
    ctx.body = post;
  } catch (e) {
    ctx.throw(500, e);
  }
};

fake data 생성하기

  1. fake data 생성하기
    (src/createFakeData.js)
import Post from './models/post';

export default function createFakeData() {
  // 0, 1, ... 39로 이루어진 배열을 생성한 후 포스트 데이터로 변환
  const posts = [...Array(40).keys()].map(i => ({
    title: `포스트 #${i}`,
    // https://www.lipsum.com/에서 복사한 200자 이상의 텍스트
    body:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
    tags: ['가짜', '데이터'],
  }));
  Post.insertMany(posts, (err, docs) => {
    console.log(docs);
  });
}
  1. fake data 불러와 데이터 생성하기
    (src/main.js)
require('dotenv').config();
import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import mongoose from 'mongoose';

import api from './api';
import createFakeData from './createFakeData';

// 비구조화 할당을 통해 process.env 내부 값에 대한 레퍼런스 만들기
const { PORT, MONGO_URI } = process.env;

mongoose
.connect(MONGO_URI, { useNewUrlParser: true, useFindAndModify: false })
  .then(() => {
    console.log('Connected to MongoDB');
    createFakeData();
  })
  .catch(e => {
    console.error(e);
  });
(...)

포스터를 다양하게 보여주기

  1. 포스트를 역순으로 불러오기
    1) 포스트를 찾고
    2) .sort({_id: -1})로 역순정렬한다.
    3) exec()으로 수정되었음을 알림
    4) 결과를 ctx.body에 넣어주며, 이때 ctx.body는 response취급이다.
    (src/api/posts/posts.ctrl.js)
export const list = async ctx => {
  try {
    const posts = await Post.find()
      .sort({ _id: -1 })
      .exec();
    ctx.body = posts;
  } catch (e) {
    ctx.throw(500, e);
  }
};
  1. 보이는 개수를 제한.
    1) 포스트를 찾고
    2) .limit(10)으로 10개로 제한함.
    3) exec()으로 수정되었음을 알림
    4) 결과를 ctx.body에 넣어주며, 이때 ctx.body는 response취급이다.

(src/api/posts/posts.ctrl.js)

export const list = async ctx => {
  try {
    const posts = await Post.find()
      .sort({ _id: -1 })
      .limit(10)
      .exec();
    ctx.body = posts;
  } catch (e) {
    ctx.throw(500, e);
  }
};

(~~추가. 페이지 번호 등 .
react 600페이지부터)

profile
개발자지망생

0개의 댓글