🎯 λ„μ„œ API에 νŽ˜μ΄μ§• 처리λ₯Ό μ μš©μ‹œν‚΅λ‹ˆλ‹€.


πŸ“™ Today I Learned

SQL λ‚ μ§œ 및 μ‹œκ°„ 처리

λ‚ μ§œ 계산

κ³Όκ±° λ‚ μ§œ κ΅¬ν•˜κΈ°

SELECT DATE_SUB(κΈ°μ€€_λ‚ μ§œ, INTERVAL 숫자 λ‹¨μœ„);

미래 λ‚ μ§œ κ΅¬ν•˜κΈ°

SELECT DATE_ADD(κΈ°μ€€_λ‚ μ§œ, INTERVAL 숫자 λ‹¨μœ„);

νŠΉμ • κΈ°κ°„ 쑰회

SELECT * FROM ν…Œμ΄λΈ”λͺ… WHERE 칼럼λͺ… BETWEEN μ‹œμž‘μΌ AND μ’…λ£ŒμΌ;



SQL νŽ˜μ΄μ§• 처리

νŠΉμ • νŽ˜μ΄μ§€ 데이터 κ°€μ Έμ˜€κΈ°

첫번째 방법

SELECT * FROM ν…Œμ΄λΈ”λͺ… LIMIT 개수 OFFSET (νŽ˜μ΄μ§€_번호 - 1) * 개수;

λ‘λ²ˆμ§Έ 방법

SELECT * FROM ν…Œμ΄λΈ”λͺ… LIMIT (νŽ˜μ΄μ§€_번호 - 1) * 개수, 개수;



λ„μ„œ API 섀계

πŸ‘‰ μˆ˜μ •ν•œ 뢀뢄은 주황색 κΈ€μ”¨λ‘œ λ‚˜νƒ€λƒ…λ‹ˆλ‹€.

전체 λ„μ„œ 쑰회

  • Method : GET

  • URL : /books?limit=개수&currentPage=νŽ˜μ΄μ§€λ²ˆν˜Έ

  • HTTP Status Code : 200 Ok

  • Request Body : x

  • Response Body :

[
  {
    "id" : λ„μ„œ id,
    "title": "λ„μ„œ 제λͺ©",
    "img" : 이미지 id,
    "summary" : "μš”μ•½ μ„€λͺ…",
    "author" : "λ„μ„œ μž‘κ°€",
    "price" : 가격,
    "likes" : μ’‹μ•„μš” 수,
    "pub_date" : "μΆœκ°„ λ‚ μ§œ"
  },
  {
    "id" : λ„μ„œ id,
    "title": "λ„μ„œ 제λͺ©",
    "img" : 이미지 id,
    "summary" : "μš”μ•½ μ„€λͺ…",
    "author" : "λ„μ„œ μž‘κ°€",
    "price" : 가격,
    "likes" : μ’‹μ•„μš” 수,
    "pub_date" : "μΆœκ°„ λ‚ μ§œ"
  },
  ...
]

κ°œλ³„ λ„μ„œ 쑰회

  • Method : GET

  • URL : /books/:id

  • HTTP Status Code : 200 Ok

  • Request Body

{
  "email": "μ‚¬μš©μžκ°€ μž…λ ₯ν•œ 이메일",
  "password" : "μ‚¬μš©μžκ°€ μž…λ ₯ν•œ λΉ„λ°€λ²ˆν˜Έ"
}
  • Response Body :
{
  "id" : λ„μ„œ id,
  "title": "λ„μ„œ 제λͺ©",
  "img" : 이미지 id,
  "category" : μΉ΄ν…Œκ³ λ¦¬,
  "format" : "포맷",
  "isbn" : "isbn",  
  "summary" : "μš”μ•½ μ„€λͺ…",
  "author" : "λ„μ„œ μž‘κ°€",
  "pages" : μͺ½ 수,
  "index" : "λͺ©μ°¨",
  "price" : 가격,
  "likes" : μ’‹μ•„μš” 수,
  "liked" : boolean,
  "pub_date" : "μΆœκ°„ λ‚ μ§œ"
}

μΉ΄ν…Œκ³ λ¦¬λ³„ μ‹ κ°„ λ„μ„œ 쑰회

  • Method : GET

  • URL : /books?categoryId=:categoryId&news={boolean}

  • HTTP Status Code : 200 Ok

  • Request Body : x

  • Response Body :

[
  {
    "id" : λ„μ„œ id,
    "title": "λ„μ„œ 제λͺ©",
    "img" : 이미지 id,
    "summary" : "μš”μ•½ μ„€λͺ…",
    "author" : "λ„μ„œ μž‘κ°€",
    "price" : 가격,
    "likes" : μ’‹μ•„μš” 수,
    "pub_date" : "μΆœκ°„ λ‚ μ§œ"
  },
  {
    "id" : λ„μ„œ id,
    "title": "λ„μ„œ 제λͺ©",
    "img" : 이미지 id,
    "summary" : "μš”μ•½ μ„€λͺ…",
    "author" : "λ„μ„œ μž‘κ°€",
    "price" : 가격,
    "likes" : μ’‹μ•„μš” 수,
    "pub_date" : "μΆœκ°„ λ‚ μ§œ"
  },
  ...
]
  • 참고사항 : newsκ°€ true일 λ•Œ μ‹ κ°„ 쑰회 기쀀은 μΆœκ°„μΌλ‘œλΆ€ν„° 1달 이내



BookController.js

const conn = require('../mariadb');
const { StatusCodes } = require('http-status-codes');

const allBooks = (req, res) => {
  const { news, limit, currentPage, categoryId } = req.query;

  const offset = limit * (currentPage - 1);
  let sql = 'SELECT * FROM books';
  let values = [];

  if (categoryId && news) {
    sql +=
      ' WHERE category_id = ? AND pub_date BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()';
    values.push(categoryId);
  } else if (categoryId) {
    sql += ' WHERE category_id = ?';
    values.push(categoryId);
  } else if (news) {
    sql +=
      ' WHERE pub_date BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()';
  }

  sql += ' LIMIT ? OFFSET ?';
  values.push(parseInt(limit), offset);

  conn.query(sql, values, (err, result) => {
    if (err) {
      console.log(err);
      return res.status(StatusCodes.BAD_REQUEST).end();
    }
    if (result.length) {
      return res.status(StatusCodes.OK).json(result);
    } else {
      return res.status(StatusCodes.NOT_FOUND).end();
    }
  });
};

const bookDetail = (req, res) => {
  const id = parseInt(req.params.id);

  const sql =
    'SELECT * FROM books LEFT JOIN categories ON books.category_id = categories.id  WHERE books.id = ?';
  conn.query(sql, id, (err, result) => {
    if (err) {
      console.log(err);
      return res.status(StatusCodes.BAD_REQUEST).end();
    }

    if (result[0]) {
      return res.status(StatusCodes.OK).json(result);
    } else {
      return res.status(StatusCodes.NOT_FOUND).end();
    }
  });
};

module.exports = {
  allBooks,
  bookDetail,
};
  
  • allBooks : λͺ¨λ“  μ±… λͺ©λ‘μ„ μ‘°νšŒν•©λ‹ˆλ‹€.

    • categoryId, news 여뢀에 따라 필터링을 κ±°μΉ©λ‹ˆλ‹€.

    • limit(ν•œ λ²ˆμ— κ°€μ Έμ˜€λŠ” μ±… 개수)와 currentPage(ν˜„μž¬ νŽ˜μ΄μ§€ 번호)둜 νŽ˜μ΄μ§•μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

πŸ€” μ™œ offset은 limit * (currentPage - 1)일까?

offset은 μ‘°νšŒν•  λ°μ΄ν„°μ˜ μ‹œμž‘ μœ„μΉ˜λ₯Ό μ˜λ―Έν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 0λΆ€ν„° μ‹œμž‘ν•˜λ©°, μ§€μ •ν•œ limit만큼 데이터λ₯Ό 뢈러였게 λ©λ‹ˆλ‹€.

currentPageoffset = limit * (currentPage - 1)쑰회 λ²”μœ„ (LIMIT 10 OFFSET X)
1 (첫 νŽ˜μ΄μ§€)10 * (1 - 1) = 01~10번 데이터
2 (두 번째 νŽ˜μ΄μ§€)10 * (2 - 1) = 1011~20번 데이터
3 (μ„Έ 번째 νŽ˜μ΄μ§€)10 * (3 - 1) = 2021~30번 데이터
4 (λ„€ 번째 νŽ˜μ΄μ§€)10 * (4 - 1) = 3031~40번 데이터

⚠️ sql μΆ”κ°€ μ‹œ μ£Όμ˜ν•  점

sql += λ₯Ό ν™œμš©ν•˜μ—¬ SQL 뒀에 쑰건문을 μΆ”κ°€ν•  λ•Œ μ•žμ— 곡백을 μΆ”κ°€ν•΄μ•Ό SQL 문법 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ˜ˆμ‹œ : sql += ' LIMIT ? OFFSET ?'


  • bookDetail : 책을 상세 μ‘°νšŒν•©λ‹ˆλ‹€.

    • books ν…Œμ΄λΈ”κ³Ό categories ν…Œμ΄λΈ”μ„ LEFT JOIN ν•˜μ—¬, 책이 μ†ν•œ μΉ΄ν…Œκ³ λ¦¬ 정보λ₯Ό ν•¨κ»˜ κ°€μ Έμ˜΅λ‹ˆλ‹€.



✏️ ν•œ 쀄 회고

이전에 νŽ˜μ΄μ§€λ„€μ΄μ…˜μ„ 직접 λ§Œλ“€μ—ˆμ„ λ•ŒλŠ” 직접 κ³„μ‚°κΉŒμ§€ vanilla javascript둜 μž‘μ„±μ„ ν–ˆμ—ˆλŠ”λ°, λ°±μ—”λ“œλ₯Ό ν™œμš©ν•˜λ‹ˆ λ”μš± μˆ˜μ›”ν•˜λ‹€λŠ” 것을 λŠκΌˆμŠ΅λ‹ˆλ‹€.

profile
🌱개발 기둝μž₯

0개의 λŒ“κΈ€