1. 미니미니 프로젝트 (실습)
이전 내용
[TIL] 9/6 핸들러, 예외 처리, HTTP 상태코드
1. 핸들러 (Handler)HTTP request 가 날아오면 자동으로 호출되는 메소드 노드에서는 콜백함수로, 콜백함수를 핸들러라고 생각하면 된다. (cf. 스프링에서는 컨트롤러라고 불린다)즉, HTTPMETHOD 를 사용
everydayc0ding.tistory.com
[TIL] 9/9 API 설계 - 회원 API, 채널 API
1. 미니미니 프로젝트 (실습) 지난번에 했던 미니미니 프로젝트를 이어서 해보려고 한다. 지난번에는 회원 API 의 회원가입 / 회원 개별 조회 / 회원 개별 탈퇴까지 했으니, 로그인 API 를 만들어보
everydayc0ding.tistory.com
[TIL] 9/23 node.js, db 연결, db 모듈화, SQL 쿼리문, affectedRows, 단축평가(short-circuit evaluation)
1. 미니미니 프로젝트 (실습) 1.1. db 모듈화 db 연결이 필요한 곳마다 연결 코드를 다 쓰는 것은 효율적이지 않기 때문에 module.exports 를 통해 모듈화 시킨다.// mariadb.jsconst mysql = require('mysql2');cons
everydayc0ding.tistory.com
1.1. 유효성(Validation) 검사
사용자가 입력한 값의 유효성(= 타당성)을 확인하는 것
express-validator 라는 외부 모듈을 사용해 유효성을 검사할 예정이므로 먼저 설치
npm install express-validation
여기서 body는 req.body 에 들어있는 값의 유효성을 검증하기 위해 사용되는 메서드이고, validationResult 는 express-validator 모듈에만 존재하는 변수로 오류가 났을 때 오류를 받아주는 메서드이다.
(body 외에도 param(), query(), header(), cookies() 등의 메서드가 존재한다)
// express-validator 모듈 불러옴
const { body, validationResult } = require('express-validator');
body 의 메개변수로 유효성 검증할 필드명을 넣고 .isEmail(), .isString(), isEmpty(), isLength() 등의 다양한 메서드들을 붙여서 유효성을 검사할 수 있다. 필드 여러 개의 유효성을 검사해야 할 경우, 아래와 같이 배열로 묶어서 사용하면 된다.
만약 유효성 검사를 통과하지 못한 경우, validationResult 에 에러 객체에 값이 담기게 되고 errors.isEmpty() 에 걸려 if 문 안에서 처리된다. if 문 안에서는 return 문을 통해 코드가 더 밑으로 내려가지 못하게 처리한다.
app.post('/user', [
body('email').isEmail(),
body('password').isLength({ min: 5 })
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
res.send('User data is valid');
});
1.2. sql 에러
유효성 검사 코드를 추가한 개별 채널 생성 코드이고, channels 테이블의 user_id 필드는 users 테이블의 id 필드를 참조하는 FK 이다.
채널을 생성할 때 users 테이블에 존재하지 않는 id 값을 넣고 INSERT 했을 때 실제 데이터베이스 테이블에 데이터가 삽입되지 않지만 에러가 발생하지 않고 성공적으로 처리되었다는 201 Created 상태코드를 뱉는 문제가 발생했다.
-> 이는 sql 에러 처리를 해주지 않았기 때문에 생기는 문제로 sql 에러 처리가 필요하다.
// 채널 개별 생성
router
.route('/')
.post(
[body('user_id').notEmpty().isInt().withMessage('숫자 입력 필요'),
body('name').notEmpty().isString().withMessage('문자 입력 필요')]
, (req, res) => {
const err = validationResult(req) // 유효성 검사했는데 에러가 났을 때
if (!err.isEmpty()) {
return res.status(400).json(err.array())
}
const {name, user_id} = req.body
let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`
let values = [name, user_id]
conn.query(sql,values,
function(err, results) {
res.status(201).json(results)
}
)
})
- sql 에러처리
sql 쿼리문의 콜백함수의 첫번째 매개변수는 err 로 sql 에러 발생시 에러를 담게 된다.
sql 에러가 발생한 경우 sql 쿼리문 콜백함수 안에서 if 문을 통해 에러처리를 해주고, 에러가 발생하지 않은 경우 밑으로 내려가 에러처리를 해주면 된다.
router
.route('/')
.post(
[body('user_id').notEmpty().isInt().withMessage('숫자 입력 필요'),
body('name').notEmpty().isString().withMessage('문자 입력 필요')]
, (req, res) => {
const err = validationResult(req) // 유효성 검사했는데 에러가 났을 때
if (!err.isEmpty()) {
return res.status(400).json(err.array())
}
const {name, user_id} = req.body
let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`
let values = [name, user_id]
conn.query(sql,values,
function(err, results) {
if (err) { // sql 에러 처리
console.log(err)
return res.status(400).end()
}
res.status(201).json(results)
}
)
}) // 채널 개별 생성
1.3. API 의 우선순위
- 채널 전체 조회: GET /channels
- 채널 개별 조회: GET /channels/:id
채널 개별 조회 API 는 url 로 id 값을 받아야 하는데 실수로 id 값을 입력하지 않았다면 포스트맨은 채널 전체 조회 API 와 채널 개별 조회 API 를 어떻게 구별할까?
-> 사실 포스트맨은 구별할 수 있는 방법이 없다. 대신 API 에는 우선순위가 존재하기 때문에 포스트맨은 우선순위가 높은 API 로 인식할 것이다. API 의 우선순위는 코드가 적힌 순서에 따라 결정이 된다. 그래서 채널 전체 조회 API 가 채널 개별 조회 API 보다 위쪽에 적혀있다면 포스트맨은 채널 전체 조회 API 로 인식할 것이고, 채널 개별 조회 API 가 채널 전체 조회 API 보다 위쪽에 적여있다면 포스트맨은 채널 개별 조회 API 로 인식할 것이다.
1.4. 채널 API - UPDATE
- 채널 개별 수정: PUT /channels/:id
UPDATE 쿼리문은 results 로 json 형태의 객체를 반환하기 때문에 존재하지 않는 id 값을 url 로 줬을 때 에러가 나는 것이 아니라 affectedRows 값이 0 인 객체가 반환이 된다. 그래서 수정 사항이 제대로 반영이 되었는지 처리를 하기 위해서는 results.affectedRows 값에 따라 분기하도록 만들어 처리해줘야 한다.

// 채널 개별 수정
router
.route('/:id')
.put(
[param('id').notEmpty().withMessage('채널 id 필요'),
body('name').notEmpty().isString().withMessage('채널명 오류')]
,(req, res) => {
const err = validationResult(req)
if (!err.isEmpty()) {
return res.status(400).json(err.array())
}
let {id} = req.params
id = parseInt(id)
let {name} = req.body
let sql = `UPDATE channels SET name = ? WHERE id = ?`
let values = [name, id]
conn.query(sql, values,
function(err, results) {
if (err) {
console.log(err)
res.status(400).end()
}
if (results.affectedRows) {
res.status(200).json({ message: '수정이 완료되었습니다.' })
} else {
res.status(400).end()
}
}
)
})
1.4. 채널 API - DELETE
- 채널 개별 삭제: DELETE /channels/:id
DELETE 쿼리문도 UPDATE 쿼리문과 같이 results 값으로 json 형태의 객체를 반환한다.
그래서 url 로 존재하지 않는 id 값을 보내도 에러가 나지 않고 results 객체에 affectedRows 값이 0인 객체가 반환되기 때문에 UPDATE 쿼리문처럼 results.affectedRows 값에 따라 분기하도록 만들어 처리해줘야 한다.

router
.route('/:id')
.delete(
param('id').notEmpty().withMessage('채널 id 필요')
,(req, res) => {
const err = validationResult(req)
if (!err.isEmpty()) {
return res.status(400).json(err.array())
}
let {id} = req.params
id = parseInt(id)
const sql = `DELETE FROM channels WHERE id = ?`
const channel = db.get(id)
conn.query(sql, id,
function(err, results) {
if (err) {
return res.status(400).end()
}
if (results.affectedRows) {
res.status(200).json({ message: '삭제되었습니다.' })
} else {
res.status(404).end()
}
}
)
}) // 채널 개별 삭제
'TIL with Programmers' 카테고리의 다른 글
[TIL] 9/26 와이어프레임 보고 API 설계해보기 (0) | 2024.09.26 |
---|---|
[TIL] 9/25 next(), 인증, 인가, 로그인, 쿠키, 세션, JWT, 토큰 (0) | 2024.09.26 |
[TIL] 9/23 node.js, db 연결, db 모듈화, SQL 쿼리문, affectedRows, 단축평가(short-circuit evaluation) (0) | 2024.09.23 |
[회고록] 풀사이클 개발 데브코스 5주차 회고 (1) | 2024.09.14 |
[TIL] 9/13 SQL workbench 사용하기, DB 연동, timezone 셋팅 (1) | 2024.09.13 |