Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

Joon's Space

[Express] Restful API 작성 (req/res, router, schema & model) 본문

Web/Express

[Express] Restful API 작성 (req/res, router, schema & model)

Happy Joon 2022. 3. 15. 21:41

RESTful API

REST 란? (Representational State Transfer)의 약자로 자원을 이름으로 구분하여 해당 자원의 상태를 주고받는 모든 것을 의미한다.

 

HTTP 프로토콜을 의도에 맞게 정확히 활용하여 디자인하도록 유도하고 있기 때문에 디자인 기준이 명확해지며, 의미적인 범용성을 지니므로 중간 계층의 컴포넌트들이 서비스를 최적화하는 데 도움이 된다. REST의 기본 원칙을 충실히 지킨 서비스 디자인을 “RESTful”이라고 표현한다. 

 

Restful API 에서는 다음과 같은 규칙을 지킨다.

  • URI는 정보의 자원을 표현해야 한다.
  • 자원에 대한 행위는 HTTP Method(GET, POST, DELETE, PUT, PATCH)로 표현한다.

HTTP Method는 아래과 같이 정리할 수 있다.

HTTP Method CRUD 기능 경로
POST Create 생성 /api
GET Read 조회 /api
PUT/PATCH Update 수정 /api/:id
DELETE Delete 삭제 /api/:id

 

Request

Http요청을 할 때, Request를 req로 줄여서 사용한다. 요청에 대한 query string, parameter, body, header 등을 갖는다. 

 

1. req.params

/api/articles/:id 경로가 있다면 "id"속성을 req.params.id로 사용할 수 있다.

https://api/articles/1234이라면 req.params.id 값은 1234가 된다.

app.get('/api/:id', async (req, res) => {
    console.log(req.params.id)
    res.send('Hello World!')
})

 

2. req.body

http 요청을 한 후 json 값을 담게 될 때 사용한다. (보통 POST로 정보 or 파일 업로드할 때)

이 값은 보통 정의 되어있지 않아서 express.json() 또는 express.urlencoded() 값은 미들웨어 함수를 사용한다.

app.use(express.urlencoded({ extended: false }))

 

Response

res로 줄여서 사용하며, express 어플리케이션이 http 요청을 받을 때 브라우저에게 전송할 때 사용한다.

 

1. res.send([body])

http 응답을 전송, parameter로 전송하는 body는 Buffer 객체, String, 객체, 배열 등. 이때 자동으로 utf-8로 인코딩 된다.

 

app.get('/', (req, res) => {
  res.send('Hello World 1');
  res.send('Hello World 2');
});

위와 같이 2번 사용할 경우 제일 위 res.send()만 적용된다. 

 

2. res.render(view, objects, callback)

템플릿 엔진을 사용하여 view를 렌더링 하여 html 문자열로 클라이언트에 보낸다. objects는 view에서 사용하게 될 변수 objects property이고, callback은 err와 렌더링 된 문자열을 갖는다. 

 

Router

express에 자체 빌트인 되는 app router를 갖는다. app.use()를 사용하거나, router.use()의 인자로 router를 넘겨줄 수 있다.

 

// routes/articles.js

const express = require('express')
const router = express.Router()

router.get('/:id', async (req, res) => {
    res.send(`It is ${req.params.id}'s article.`)
})

module.exports = router

routes 파일을 생성 후 하위에 글에 대한 라우터를 생성해 준다. 마지막엔 다른 파일에서 사용할 수 있게 module.exports = router를 사용.

 

// server.js

const express = require('express')
const articleRouter = require('./routes/articles')
const app = express()

app.use('/articles', articleRouter) // 이와 같이 articleRouter 적용

app.listen(5000);

server root file에 다음과 같이 app.use()를 사용하여 적용시켜준다. 

 

 

Schema

Mongoose의 Schema는 MongoDB에 저장되는 document의 Data 구조 즉 필드 타입에 관한 정보를 json 형태로 정의한 것이다.

 

MongoDB 자체로는 Schema-less 하기 때문에, 고정 Schema가 없어 같은 Collection 내에 있더라도 document level의 다른 Schema를 가질 수 있다. 

 

자유도가 높아 유연하게 사용할 수 있지만 명시적인 구조가 없어서 Mongoose에서 Schema를 사용한다.

 

// models/article.js

const mongoose = require('mongoose')

// 글에 대한 Schema 생성 및 타입 정의
const articleSchema = new mongoose.Schema({
    title: { 
        type: String,
        required: true,
    },
    description: {
        type: String,
    },
    createdAt: {
        type: Date,
        default: Date.now
    },
})

// 다음과 같이 'Article'로 Schema 이름을 정의 해 준다.
module.exports = mongoose.model('Article', articleSchema)

 

Model

위와 같이 model을 생성할 때, model() 메서드에 문자열과 schema를 전달하여 model을 생성한다. model은 보통 대문자로 시작한다.

module.exports = mongoose.model('Article', articleSchema)

 

ArticleRouter에서 새로운 Schema를 생성할 때는 다음과 같은 방법을 사용한다. 

// routes/articles.js

const Article = require('../models/article')

router.get('/new', (req, res) => {
    res.render('articles/new', { article: new Article() })
})

새로운 글을 작성할 때 사용하는 데, articles/new.ejs 경로의 파일을 render 하며, 그때마다 새로운 객체를 생성한다.

 

 

CRUD

// server.js

app.get('/', async (req, res) => {
    const articles = await Article.find().sort({
        createdAt: "desc", // 최신 날짜 순으로 정렬
    }) 
    res.render('articles/index', { articles: articles })
})

 

mongoDB에서 해당 데이터를 return 하거나 저장, 삭제 등을 할 때 findById(), findOne(), save(), findByIdAndDelete() 함수를 사용

// routes/articles.js


// 새로운 글 작성 페이지
router.get('/new', (req, res) => {
    res.render('articles/new', { article: new Article() })
})

// 글 수정 페이지
router.get('/edit/:id', async (req, res) => {
    const article = await Article.findById(req.params.id)
    res.render('articles/edit', { article: article })
})

// 특정 글 조회
router.get('/:slug', async (req, res) => {
    const article = await Article.findOne({ slug: req.params.slug })
    if (article == null) {
        res.redirect('/') // 글이 없으면 전체 글 조회
    }
    res.render('articles/show', { article: article })
})

// 글 생성
router.post('/', async (req, res, next) => {
    req.article = new Article()
    next()
}, saveArticleAndRedirect('new'))

// 글 수정
router.put('/:id', async (req, res, next) => {
    req.article = await Article.findById(req.params.id)
    next()
}, saveArticleAndRedirect('edit'))

// 글 삭제
router.delete('/:slug',async (req, res) => {
    // console.log(req.param.id);
    await Article.findByIdAndDelete(req.params.slug)
    res.redirect('/') // 삭제 후 전체 글 조회
})

// 중복 메소드를 함수로 정리 (글 생성 및 수정 시 글을 업로드 하는 과정이 겹침) 
function saveArticleAndRedirect(path) {
    return async (req, res) => {
        let article = req.article
        article.title = req.body.title
        article.description = req.body.description
        try {
            article = await article.save()
            res.redirect(`/articles/${article.slug}`)
        } catch(e) {
            console.log(e)
            res.render(`articles/${path}`, { article: article })
        }
    }
}

 

위에서 slug 부분은 글을 조회할 때 url이 id로 된다면 못생긴 url이 생성되기 때문에, articleSchema에 slug 필드를 추가하고, 

// models/article.js

const slugify = require('slugify')

articleSchema.pre('validate', function(next) { // 저장하기 전! 
    if(this.title) {
        this.slug = slugify(this.title, { lower: true, strict: true })
    }
    next()
})

articleSchema.pre() 함수를 사용하여 특정 article이 저장되기 전에 slugify를 하여 url을 예쁘게 하기 위함.

title -> 'hello' 일 경우, '/articles/:slug'로 조회하기 때문에 '/articles/hello'의 url을 갖게 된다.

 

반응형

'Web > Express' 카테고리의 다른 글

[Express] Express 초기 설정 및 예제(express, ejs, mongoose)  (0) 2022.03.14