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. db 모듈화
db 연결이 필요한 곳마다 연결 코드를 다 쓰는 것은 효율적이지 않기 때문에 module.exports 를 통해 모듈화 시킨다.
// mariadb.js
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'root',
database: 'Youtube',
dateStrings: true
});
module.exports = connection
이제 필요한 곳에 require 해서 사용하면 된다.
추가로 모듈화된 db 의 변수명은 connection 에서 따온 conn 이라는 이름을 많이 사용한다.
// user.js
const conn = require('../mariadb')
1.2. sql 쿼리
connection.query() 는 MYSQL 데이터베이스와 연결하여 쿼리를 실행할 때 사용하는 Node.js 의 MYSQL 모듈에서 제공하는 메소드이다. 이 메소드를 사용하면 SQL 쿼리를 실행하고, 그 결과를 콜백함수로 처리할 수 있다.
// connection.query 의 기본 형태
connection.query(
'sql쿼리문',
[values],
function(err, results, fields) { // 쿼리가 실행된 후 호출되는 콜백함수
// 에러처리
// 성공시 결과처리
}
)
만약 특정한 값을 뽑아와야 하는 경우 WHERE 과 ? 를 사용하여 뽑아오면 된다. ? 자리에 들어갈 값을 [values] 자리, 즉 두번째 매개변수로 넣어주면 된다.
// 예시 코드
connection.query(
`SELECT * FROM users WHERE id = ?`,
[id],
function(err, results, fields) {
// 에러처리
// 성공시 결과처리
}
)
connection.query(
`INSERT INTO users (id, name, age) VALUES (?, ?, ?)`,
[id, name, age],
function(err, results, fields) {
// 에러처리
// 성공시 결과처리
}
)
1.3. 회원 API
- SELECT
회원 개별 조회: GET /users
- req: body(email)
- res200: 회원 객체를 통으로 전달
// 회원 개별 조회
router
.route('/users')
.get((req, res) => {
let {email} = req.body
let sql = `SELECT * FROM users WHERE email = ?`
let values = [email]
conn.query(
sql,
values,
function(err, results) {
if (results.length) {
res.status(200).json(...results)
console.log(results)
} else {
res.status(404).json({ message: '회원 정보가 없습니다.'})
}
}
)
})
- INSERT
회원가입: POST /join
- req: body(email, name, password, contact)
- res201: `${name}님 환영합니다`
// 회원 가입
router
.route('/join')
.post((req, res) => {
const {email, name, password, contact } = req.body
let sql = `INSERT INTO users (email, name, password, contact) VALUES (?, ?, ?, ?)`
let values = [email, name, password, contact]
if (email && name && password) {
conn.query(
sql,
values,
function(err, results) {
res.status(201).json({ message: `${name}님 환영합니다. `})
}
)
} else {
res.status(400).json({ message: '빠진 입력값이 있습니다.' })
}
})
- SELECT
로그인: POST /login
- req: body(email, password)
- res200: `${name}님 환영합니다.`
// 로그인
router
.route('/login')
.post((req, res) => {
const {email, password} = req.body
let sql = `SELECT * FROM users WHERE email = ?`
let values = [email]
conn.query(
sql,
values,
function(err, results) {
let loginUser = results[0]
if (loginUser && loginUser.password === password) {
res.status(200).json({ message: `${loginUser.name}님 로그인이 성공하였습니다.` })
} else {
res.status(404).json({ message: '이메일 또는 비밀번호가 틀렸습니다.' })
}
}
)
})
- DELETE
회원 개별 탈퇴: DELETE /users
- req: body(email)
- res200: `${name}님 탈퇴가 완료되었습니다`
// 회원 개별 탈퇴
router
.route('/users')
.delete((req, res) => {
let {email} = req.body
let sql = `DELETE FROM users WHERE email = ?`
let values = [email]
conn.query(
sql,
values,
function(err, results) {
if (results.affectedRows) {
res.status(200).json({ message: '탈퇴가 완료되었습니다.' })
} else {
res.status(404).json({ message: '회원 정보가 없습니다.'})
console.log(results)
}
}
)
})
affectedRows 는 SQL 쿼리가 데이터베이스에서 영향을 미친 행의 수를 나타내는 값으로, INSERT, UPDATE, DELETE 와 같이 데이터 변경 작업을 할 때 반환된다. results 를 출력해보면 확인할 수 있다.
삭제 성공 시 -> effectedRows = 1

삭제 실패 시 -> effectedRows = 0

1.4. 채널 API
- INSERT
채널 생성: POST /channels
- req: body(name, user_id)
- res201: `${name}님 채널을 응원합니다.`
// 채널 개별 생성
router
.route('/')
.post((req, res) => {
let {name, user_id} = req.body
if (name && Number(user_id)) {
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({ message: `${name}님 채널을 응원합니다.` })
}
)
} else {
res.status(400).json({ message: '요청 값을 제대로 보내주세요. '})
}
})
- SELECT
회원의 채널 전체 조회: GET /channels
- req: body(user_id)
- res200: 채널 데이터 list
// 회원 채널 전체 조회
router
.route('/')
.get((req, res) => {
let {user_id} = req.body
let sql = `SELECT * FROM channels WHERE user_id = ?`
// 단축 평가
user_id && conn.query(
sql,
user_id,
function(err, results) {
if (results.length) {
res.status(200).json(...results)
} else {
notFoundChannel(res)
}
}
)
res.status(400).end()
})
처음에는 user_id 가 없는 경우의 예외처리를 위해 if else 문을 사용하지 않고 단축 평가를 통해 코드를 간단하게 만들었다.
user_id 가 존재 + SQL 쿼리가 성공적으로 실행되면 결과를 반환하고,
user_id 가 존재하지 않는다면 밑으로 내려가서 res.status(400).end() 를 만나 상태코드 400 을 뱉도록 했는데 이렇게 하면 user_id 가 존재하는 경우에도 res.status(400).end() 를 만나 상태코드 400 을 뱉고 아래의 에러를 만나게 된다.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
이 에러는 서버에서 이미 클라이언트의 요청에 응답을 보낸 후 같은 요청에 대해 또 보내려고 할 때 발생한다.
즉, user_id 가 존재하는 경우에는 위에서 상태코드 200 을 뱉었는데, 밑으로 내려가서 또 400 을 뱉으려고 하니 덮어씌워져서 발생하는 에러인 것이다. 그래서 if else 를 사용하는 코드로 고쳐주었다.
// 회원 채널 전체 조회
router
.route('/')
.get((req, res) => {
let {user_id} = req.body
let sql = `SELECT * FROM channels WHERE user_id = ?`
if (user_id) {
conn.query(
sql,
user_id,
function(err, results) {
if (results.length) {
res.status(200).json(...results)
} else {
notFoundChannel(res)
}
}
)
} else {
res.status(400).end()
}
})
※ 추가 - 단축 평가 (short-circuit evaluation)
자바스크립트 단축 평가는 논리 연산에서 불필요한 연산을 생략하고, 가능한 빨리 결과를 도출하는 방식으로 &&(AND), ||(OR) 에서 주로 사용된다.
// || (OR)
const a = null || "default";
console.log(a); // "default"
// 좌측이 거짓이므로 우측 값 반환, 우측이 참이면 참이 되고 거짓이면 거짓이 됨, 우측 값에 따라 결정되니 우측 값 반환
const b = "Hello" || "World";
console.log(b); // "Hello"
// 좌측이 참이므로 우측은 평가 X, 둘 중 하나만 참이면 참이기 때문에 우측을 평가할 필요 X
// && (AND)
const a = 1 && 0;
console.log(a); // 0
// 첫 번째 거짓 값인 0이 반환됨, 둘 중 하나만 거짓이어도 거짓이기 때문에 우측 값을 볼 필요 X
const b = true && "Hello";
console.log(b); // "Hello"
// 좌측이 참이므로 우측 값이 반환됨, 우측이 참이면 참 거짓이면 거짓이 됨, 우측 값에 따라 결정되기 때문에 우측 값 반환
- SELECT
채널 개별 조회: GET /channels/:id
- req: URL(id)
- res200: 채널 개별 데이터
// 채널 개별 조회
router
.route('/:id')
.get((req, res) => {
let {id} = req.params
id = parseInt(id)
let sql = `SELECT * FROM channels WHERE id = ?`
conn.query(
sql,
id,
function(err, results) {
if (results.length) {
res.status(200).json(...results)
} else {
notFoundChannel(res)
}
}
)
})
'TIL with Programmers' 카테고리의 다른 글
[TIL] 9/25 next(), 인증, 인가, 로그인, 쿠키, 세션, JWT, 토큰 (0) | 2024.09.26 |
---|---|
[TIL] 9/24 express-validator, 유효성 검사, sql 에러, API 우선순위 (1) | 2024.09.24 |
[회고록] 풀사이클 개발 데브코스 5주차 회고 (1) | 2024.09.14 |
[TIL] 9/13 SQL workbench 사용하기, DB 연동, timezone 셋팅 (1) | 2024.09.13 |
[TIL] 9/12 데이터베이스 실습 - SELECT, ALTER, JOIN, FK, PK, AUTO_INCREMENT, NOT NULL, DEFALUT (0) | 2024.09.12 |