📚요약
지난 시간은 JWT를 활용해 API의 수정이 있었습니다. 이번 시간에는 인가(authorization)의 모듈화를 하고 나머지 API도 수정을 해보겠습니다. 추가적으로 API 설계 문서와 실제 구현을 비교해 보면서 부족한 부분과 네이밍 등 어색한 부분도 수정해 보겠습니다.
📖SQL
📄전체 행(튜플, 레코드) 수(Cardinality) 구하기
테이블의 전체 데이터 수를 구하기 위해서는 두 가지 방법이 있습니다.
- count() 활용 : 결과의 행 수를 세는 함수.
- SQL_CALC_FOUND_ROWS & found_rows() 활용 : SQL_CALC_FOUND_ROWS을 통해 테이블의 모든 행 수를 계산하고, found_rows()를 통해 이전 결과의 행 수를 출력.
SELECT count(*) FROM table;
SELECT SQL_CALC_FOUND_ROWS * FROM table;
SELECT found_rows();
두 가지 모두 전체 행 수를 구할 수 있는 방법이지만 MySQL에서는 2번 방법은 deprecated 된다고 하며 1번 방법을 추천하고 있습니다.
MySQL :: WL#12615: Deprecate SQL_CALC_FOUND_ROWS and FOUND_ROWS
WL#12615: Deprecate SQL_CALC_FOUND_ROWS and FOUND_ROWS Affects: Server-8.0 — Status: Complete Description Requirements MySQL has a nonstandard query modifier called SQL_CALC_FOUND_ROWS. When in use on a SELECT with LIMIT, it attempts to calculate h
dev.mysql.com
📖Authorization 모듈화
📄구현
API 중에서 로그인할 때와 안 할 때가 하나의 요청에 구현되는 경우가 있습니다. 이때 구현에는 크게 두 가지 방법이 있습니다.
1. 요청(request)에 authorization을 확인하고, 비어있으면 빈 토큰을 반환하는 방법
// authorization.js
const dotenv = require("dotenv");
const jwt = require("jsonwebtoken");
dotenv.config();
const decodeUser = (req) => {
try {
const token = req.headers.authorization;
if (token) {
const user = jwt.verify(token, process.env.PRIVATE_KEY);
return user;
}
return token;
} catch (err) {
console.log(err.name);
console.log(err.message);
return err;
}
};
module.exports = { decodeUser };
// 모듈 사용 코드
const { decodeUser } = require("../authorization");
const jwt = require("jsonwebtoken");
const userId = decodeUser(req)?.userId;
if (userId instanceof jwt.TokenExpiredError) {
return res.status(StatusCodes.UNAUTHORIZED).json({
message: "로그인 세션 만료됨.",
});
} else if (userId instanceof jwt.JsonWebTokenError) {
return res.status(StatusCodes.BAD_REQUEST).json({
message: "토큰이 이상합니다. 확인해주세요",
});
}
// ... 구현
2. 요청(request)에 authorization을 확인하고, 새로운 에러를 만들어서 반환하는 방법
// authorization.js
const dotenv = require("dotenv");
const jwt = require("jsonwebtoken");
dotenv.config();
const decodeUser = (req) => {
try {
const token = req.headers.authorization;
if (token) {
const user = jwt.verify(token, process.env.PRIVATE_KEY);
return user;
} else {
throw new ReferenceError('jwt must be provided');
}
} catch (err) {
console.log(err.name);
console.log(err.message);
return err;
}
};
module.exports = { decodeUser };
// 모듈 사용 코드
const { decodeUser } = require("../authorization");
const jwt = require("jsonwebtoken");
const userId = decodeUser(req)?.userId;
if (userId instanceof jwt.TokenExpiredError) {
return res.status(StatusCodes.UNAUTHORIZED).json({
message: "로그인 세션 만료됨.",
});
} else if (userId instanceof jwt.JsonWebTokenError) {
return res.status(StatusCodes.BAD_REQUEST).json({
message: "토큰이 이상합니다. 확인해주세요",
});
} else if (userId instanceof ReferenceError) {
// ... 구현
}
저는 1번 방법으로 구현했습니다. 새로운 에러를 반환하면 코드가 복잡해진다고 느꼈고, 코드의 중복이 발생한다고 느꼈습니다.
📖좋아요 & 장바구니 API
📄구현
지난 시간에 구현했던 부분을 모듈화 시켜서 구현했기 때문에 결과는 달라지지 않았습니다.
📖책 API
📄구현


책의 전체 목록 조회는 인가(authorization)가 필요 없고, 개별 책 조회를 했을 때 사용자의 좋아요 여부를 확인하기 위해서 인가가 필요합니다. 이때 로그인을 하지 않아도 책의 상세 정보를 볼 수 있도록 해야 하기 때문에 로그인을 한 경우에만 좋아요 여부(is_like)를 응답(Response)에 담아서 보내줍니다.

전체 책 목록 조회 부분에서 수정된 부분은 단순한 책 목록만 전달하는 것이 아니라 하단의 목록을 출력하고, 현재 페이지를 알려주기 위해서 pagenation 정보를 추가로 보내줍니다.
🍯tip! 여러 SQL을 보내고 싶다면 mysql2 모듈에서 제공하는 함수를 사용해 보낼 수 있다. 자세한 내용은 블로그와 공식 사이트를 참고하면 좋다.
이때 강사님의 코드가 Q4질문을 발생시켰습니다.
📖주문 API
📄구현




authorization 모듈을 적용시켜서 모두 정상 동작하는 것을 확인했습니다.
+a) 마지막으로 API 설계 문서의 통일성을 위해 구현된 부분되 맞추는 동시에 이름을 통일하는 작업을 했습니다. 저는 request는 camelCase로 하고, response는 snake_case로 했는데 둘을 구분하지 않고 통일해야하는지 고민입니다.
자세한 코드는 Github에서 확인할 수 있습니다.
ProgrammersSchool/PROJECT-BOOKSHOP at main · nulzi/ProgrammersSchool
프로그래머스 데브코스에서 학습하는 것들을 모아두는 레포. Contribute to nulzi/ProgrammersSchool development by creating an account on GitHub.
github.com
❔▪❓
Q1. 에러가 발생했을 때 결과에 대한 처리가 동일한 코드가 중복이 되는데 어떻게 모듈화를 해야 할까?
const userId = decodeUser(req).userId;
if (userId instanceof jwt.TokenExpiredError) {
return res.status(StatusCodes.UNAUTHORIZED).json({
message: "로그인 세션 만료됨.",
});
} else if (userId instanceof jwt.JsonWebTokenError) {
return res.status(StatusCodes.BAD_REQUEST).json({
message: "토큰이 이상합니다. 확인해주세요",
});
}
Q2. 모듈화를 시키는 기준이 있을까?
Q3. 좋아요 API를 구현하는 두 함수가 결국 sql과 HTTP Method만 다른데 하나로 합치는 건 안 되는 건가?
Q4. query()함수를 연속으로 실행시켜도 문제가 없는 것인가?
let allBooksRes = {};
conn.query(sql, values,
(err, results) => {
if (err) {
console.log(err);
//return res.status(StatusCodes.BAD_REQUEST).end();
}
console.log(results);
if (results.length) {
allBooksRes.books = results;
} else {
res.status(StatusCodes.NOT_FOUND).end()
}
}
)
conn.query(sql,
(err, results) => {
if (err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
allBooksRes.pagination = pagination;
return res.status(StatusCodes.OK).json(allBooksRes);
}
)
다음 시간에 계속...
출처 & 참고
김송아 강사님의 강의
'개발 > 프로그래머스 데브코스' 카테고리의 다른 글
| 데브코스 스터디(알고리즘, 코테) 13회차 (0) | 2024.06.10 |
|---|---|
| 프로그래머스 데브코스 43일차 with. TS 웹 풀스택 (0) | 2024.06.04 |
| 프로그래머스 데브코스 9주차 회고 with. TS 웹 풀스택 (0) | 2024.06.03 |
| 프로그래머스 데브코스 41일차 with. TS 웹 풀스택 (0) | 2024.06.03 |
| 데브코스 스터디(알고리즘, 코테) 12회차 (1) | 2024.06.03 |