프로그래머스 풀스택 23
posted on 23 Sep 2024 under category programmers in series programmers
백엔드 심화: 인증과 비동기처리(4)
db-demo.js -> mariadb.js로 이름 변경
기존 query 출력문을 없애고 connection을 모듈화 시켜줌
module.exports = connection
users.js에 가서 mariadb.js의 connection을 불러오고 query문을 출력하기.
connection.query에서 conn.query로 시작해야함!
// express 모듈 셋팅
const express = require('express')
const router = express.Router();
const conn = require('../mariadb') //mariadb.js의 connection을 불러오기
// A simple SELECT query
conn.query(
'SELECT * FROM `users`',
function (err, results, fields) {
var {id, email, name, created_at} = results[0];
console.log(id);
console.log(email);
console.log(name);
console.log(created_at);
}
);
<app.js 실행>
users.js파일의 router.route(‘/users’) 부분!
userId -> 개인정보이므로 url에 안넘기고 body값에 숨김
email을 받기로 수정하기
router.route('/users')
.get((req, res) => {
let {email} = req.body //비구조화
conn.query(
`SELECT * FROM users Where email = ${email}`,
function (err, results, fields) {
if(results.length)
res.status(200).json(results)
else {
res.status(404).json({
message : "죄송합니다. 찾으시는 회원 정보가 없습니다."
})
}
}
);
// const user = db.get(userId) //db에서 객체 꺼내기(map이라서 이제 필요없음)
})
<혼자 수정한 것>
router.route('/users')
.get((req, res) => {
let { email } = req.body; // 비구조화
// SQL 쿼리에서 변수를 직접 넣지 말고 ?로 대체하여 prepared statement 사용
conn.query(
'SELECT * FROM users WHERE email = ?',
[email],
function (err, results, fields) {
if (err) {
// 에러가 발생하면 에러 응답을 보냄
res.status(500).json({ message: '서버 에러가 발생했습니다.', error: err });
} else if (results.length) {
// 조회된 데이터가 있으면 그 결과를 반환
res.status(200).json(results);
} else {
// 조회된 데이터가 없을 경우
res.status(404).json({ message: '죄송합니다. 찾으시는 회원 정보가 없습니다.' });
}
}
)
})
<실행결과>
💫 Prepared Statement
Prepared Statement는 보통 두 단계로 동작 :
쿼리 준비 단계 (Preparation): SQL 쿼리의 구조가 고정된 상태에서 데이터베이스 서버에 전달, 이때 쿼리에서 가변적인 값들은 플레이스홀더(?)로 표현
쿼리 실행 단계 (Execution): 실제 값들은 실행 시점에 제공되며, 이 값들이 플레이스홀더에 대입되어 실행
💫 Prepared Statement의 주요 특징
SQL Injection 방지
SQL Injection은 사용자 입력을 통해 SQL 문을 조작하여 데이터베이스에 악의적인 쿼리를 실행하는 공격 방법임 -> Prepared Statement는 쿼리와 데이터를 분리하여 이러한 공격을 차단
성능 최적화
데이터베이스는 Prepared Statement를 처음 전달받았을 때, 쿼리의 구조를 분석하고 미리 최적화된 실행 계획을 준비 -> 이후 같은 구조의 쿼리가 반복해서 실행될 경우, 쿼리 분석 및 최적화 단계를 생략하여 빠르게 처리
재사용 가능성
Prepared Statement는 동일한 SQL 문을 반복적으로 실행할 때 재사용 가능
[email]은 Prepared Statement에서 사용하는 Placeholders(플레이스홀더)에 값을 바인딩하는 방식.
이를 통해 SQL Injection과 같은 보안 문제를 방지하고, SQL 쿼리에서 동적으로 변수를 안전하게 사용 가능.
동작 방식
?(플레이스홀더): SQL 쿼리에서 ?는 값을 대체할 위치를 나타냄. 즉, 쿼리문 안에서 직접 변수를 넣지 않고, ?를 사용하여 변수를 넣을 자리를 예약하는 역할.
[email]: email은 req.query나 req.body에서 가져온 값. 이 값은 ? 위치에 대입될 값이 됨. 배열 형태로 넘기면 배열의 순서대로 ?에 대응.
<예시>
conn.query(
'SELECT * FROM users WHERE email = ?',
[email], // ? 자리에 email 변수가 들어감
function (err, results, fields) {
// 결과 처리
}
);
💫 오류 해결
Error: read ECONNRESET : 네트워크 연결 오류로, 클라이언트와 서버 간의 TCP 연결이 갑작스럽게 끊겼을 때 발생하는 오류
이 오류는 주로 서버에서 클라이언트의 요청을 처리하던 중 연결이 강제로 리셋되었을 때 발생
ECONNRESET은 Connection Reset by Peer(상대방에 의해 연결이 재설정됨)이라는 의미
아까 사용한 코드는 템플릿 문자열로 인해 SQL 구문 오류로 서버에서 쿼리가 실패했을 가능성이 큼!
💫 select sql 쿼리 형식
나는 저렇게 해결했는데 host: ‘localhost’ -> host: ‘127.0.0.1’로 바꿔주는 것도 해결된다고 함!
💫 회원 API 설계 수정
get과 post의 가장 큰 차이 : body의 유무!!(body 있으면 post!!)
<혼자 수정한 것>
<실행결과>
💫 delete
affetedRows(영향받은 행) : 0 or 1, 2 …
<혼자 수정한 것>
<실행결과>
💫 로그인
<혼자 수정한 것>
<실행결과>
<코드 고도화>
function (err, results, fields) {} : 매개 변수 총 3개. err, result, field
<로그인 부분 고도화>
let sql = 'SELECT * FROM users WHERE email = ?'
이런 구조로 conn.query()를 간편하게 함.
💫 채널 API 설계 수정
채널
<SELECT 채널 개별 조회 혼자 수정한 것>
<실행결과>
<SELECT 회원 채널 전체 조회 혼자 수정한 것>
<실행결과>
단축평가(Short-circuit Evaluation) : 논리 연산자 &&(AND)와 ||(OR)를 사용할 때, 불필요한 연산을 건너뛰는 동작을 의미
논리식의 결과가 이미 확정된 경우 나머지 연산을 수행하지 않고 평가를 “단축”하는 것
<INSERT 채널 개별 등록 혼자 수정한 것>
<실행결과>
Y 일을 통해 명확히 알게 되었거나 이해한 부분(한 일)에 대해 정리 :
DB 모듈화, Prepared Statement, 단축평가
W 배운 점과 시사점 :
DB 모듈화 : 쿼리문을 통해 간편하게 할 수 있음!
Prepared Statement : 쿼리 성능 최적화와 보안 강화를 위해 사용!!
단축평가(Short-circuit Evaluation) : 논리 연산자 &&(AND)와 ||(OR)를 사용할 때, 불필요한 연산을 건너뛰는 동작을 의미.
T 응용하여 배운 것을 어디에 어떻게 적용할지:
쿼리문을 통해 DB를 모듈화가 가능함을 배움! 백엔드에서 DB와 연동하는 걸 더 잘 알게 되었음