๐ŸŒณ๋ชฉํ‘œ

์ด์ „ ์‹œ๊ฐ„์— ๊ตฌํ˜„ํ•œ ๋ผ์šฐํ„ฐ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด์„œ API๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์–ด๋–ค API์ฃ ?

๋จผ์ € ์‹ค์Šต์„ ์œ„ํ•œ ๋ธŒ๋žœ์น˜๋กœ ์ด๋™ํ•ด ๋ณผ๊นŒ์š”?

$ git checkout -f route/get-posts-spec

์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๊ณ  ๋ธŒ๋ผ์šฐ์ ธ๋กœ ์ ‘์†ํ•ด๋ณด์„ธ์š”.

โ€œLoadingโ€ฆโ€ ๋ฉ”์„ธ์ง€๊ฐ€ ์‚ฌ๋ผ์ง€์ง€ ์•Š๋„ค์š”. ๋ญ”๊ฐ€๋ฅผ ๋กœ๋”ฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ž˜ ์•ˆ๋˜๋Š”๊ฐ€ ๋ด…๋‹ˆ๋‹ค.

ํฌ๋กฌ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ํ™•์ธํ•˜๋ฉด โ€œGET /api/postsโ€ ์š”์ฒญ์„ ๋ณด๋ƒˆ๋Š”๋ฐ 404 Not Found ์‘๋‹ต์„ ๋ฐ›์•˜๊ตฐ์š”.

ํ”„๋ก ํŠธ์—”๋“œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ์ •ํ™•ํ•œ ์›์ธ์„ ์•Œ ์ˆ˜ ์žˆ์„๊ฒƒ ๊ฐ™๊ตฐ์š”.

const loadTimeline = el => {
  el.innerHTML = 'Loading...'

  api.getPosts()
    .then(data => {
      // ...
    })
    .catch(err => {
      // ...
    })
}

loadTimeline์€ ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋ ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. โ€œLoadingโ€ฆโ€ ๋ฌธ์ž์—ด์„ ๋”์— ์ถœ๋ ฅํ•˜๊ณ  getPosts() ํ˜ธ์ถœํ•˜๊ณ  ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ๋„ค์š”.

const api {
  getPosts() {
    return http('get', '/api/posts')
  }
}

const http = (method, url, data = null) => new Promise((resolve, reject) => {
  const req = new  XMLHttpRequest()
  req.open(method, url, true)
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  req.onreadystatechange = evt => // ...
  req.send(data)
}

getPosts() ๋ฉ”์†Œ๋“œ๋Š” http ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์žˆ๊ตฌ์š”. GET ๋ฉ”์†Œ๋“œ์™€ โ€œ/api/postsโ€ ์ฃผ์†Œ๋กœ AJAX ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„œ๋ฒ„์—์„œ๋Š” ์ด ์—”ํŠธํฌ์ธํŠธ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— 404๋กœ ์‘๋‹ตํ•œ ๊ฒƒ์ด ๋ฌธ์ œ์˜ ์›์ธ์ž…๋‹ˆ๋‹ค.

๐Ÿค์‹ค์Šต - ํฌ์ŠคํŠธ ์กฐํšŒ API๋ฅผ ๊ตฌํ˜„ํ•ด ๋ณด์„ธ์š”.

โ€œGET /api/postsโ€ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.

ํžŒํŠธ: route/api/post.js ํŒŒ์ผ์— ๊ตฌํ˜„, ์•„๋ž˜ ๋ชฉ์—… ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ

const posts = [
  {title: 'post 3', body: 'this is post 3'},
  {title: 'post 2', body: 'this is post 2'},
  {title: 'post 1', body: 'this is post 1'},
]

๐Ÿคํ’€์ด

๊ทธ๋Ÿผ ๊ฐ™์ด ํ’€์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

routes/api ํด๋”์— posts.js ํŒŒ์ผ์„ ๋จผ์ € ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฐ ๋‘ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ ์„œ ์„ค๋ช…ํ• ๊ฒŒ์š”.

const posts = [
  {title: 'post 3', body: 'this is post 3'},
  {title: 'post 2', body: 'this is post 2'},
  {title: 'post 1', body: 'this is post 1'},
]

๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  posts ๋ฐฐ์—ด์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํžŒํŠธ์— ์žˆ๋Š” ๋ชฉ์—… ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

const index = () => (req, res, next) => {
  res.setHeader('Content-Type', 'application/json')
  res.end(JSON.stringify(posts))
}

module.exports = {
  index
}

โ€œ/postsโ€ ์š”์ฒญ์— ๋Œ€ํ•œ ์ปจํŠธ๋กค๋Ÿฌ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜๋กœ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งž์ถ”์—ˆ์Šต๋‹ˆ๋‹ค.

API๋Š” JSON ํ˜•์‹์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— Content-Type ํ—ค๋”๋ฅผ โ€œapplication/jsonโ€์œผ๋กœ ๋งž์ถ”์—ˆ๊ตฌ์š”. ๋ฌธ์ž์—ด์„ ๋ณด๋‚ผ๋•Œ๋Š” JSON.stringfiy() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•ด์•ผํ•˜๋Š”๋ฐ์š” ์–ด๋””์„œ ํ• ๊นŒ์š”? ๋„ค, ๋งž์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ๋„ ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— app.js์—์„œ use() ๋ฉ”์†Œ๋“œ๋กœ ๋“ฑ๋กํ•ด์•ผ๊ฒ ์ง€์š”.

๊ทธ๋ฆฌ๊ณ  ์ด index ํ•จ์ˆ˜๋ฅผ ๋ชจ๋“ˆ๋กœ ๋…ธ์ถœํ•ด ์ค๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์ธ ์กฐํšŒ ๊ธฐ๋Šฅ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜ ์ด๋ฆ„์„ index๋กœ ์ •ํ–ˆ๋‹ต๋‹ˆ๋‹ค.

// ...
const apiPost = require('./routes/api/post');

app.use('/', index.listPosts())
app.use('/api/posts', apiPost.index()) // ๋ผ์šฐํŠธ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
app.use(errors.error404())

โ€/โ€ ๋ผ์šฐํŠธ๋ฅผ ์ถ”๊ฐ€ํ•œ ๋ฐฉ๋ฒ•๊ณผ ๋™์ผํ•˜๊ฒŒ โ€œ/api/postsโ€ ๋ผ์šฐํŠธ๋„ ๊ทธ ์•„๋ž˜ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์„œ๋ฒ„๋ฅผ ์žฌ์‹คํ–‰ํ•˜๊ณ  ํ™•์ธํ•ด ๋ณผ๊นŒ์š”?

API๋กœ ์‘๋‹ต๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ํ™”๋ฉด์— ์ž˜ ๋ฟŒ๋ ค์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์—ˆ๋˜ API ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง ์„น์…˜์œผ๋กœ ์™ผ์ชฝ ์•„๋žซ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ๋ถ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ •๋ฆฌ

  • ํฌ์ŠคํŠธ ์กฐํšŒ API๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • JSON ํ˜•์‹์˜ ์‘๋‹ต์„ ์•Œ์•„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๋ชฉ์ฐจ ๋ฐ”๋กœ๊ฐ€๊ธฐ