데이터베이스를 통해 데이터를 주고받는 것을 배운다. 배열이나 객체 등을 이용해서 인메모리 형태로 데이터를 저장하는 것과 다른 점을 학습한다.
npm test를 돌리게 되면 자동으로 서버가 실행되고 테스트가 진행된다. 그래서 켜둔 서버를 끄고 테스트를 돌려야한다.
package.json
package.json
에 기록된 npm 모듈 mysql
이 있다. 이 모듈은 Cmarket Server가 MySQL server와 상호작용 할 수 있게 한다고 한다. package.json
을 확인했을 때 dependencies
에 mysql이 있으니 이 모듈은 서버와 데이터베이스서버를 연결해주는 역할을 한다.
config/config.js
비밀번호를 코드에 작성할 수도 있겠지만, 보안상/편의상 이유로 비밀번호는 환경 변수로 분리해놓는 편이 더 좋다.
server/config/config.js
에 mySQL과의 연결을 위한 환경 변수 DATABASE_SPRINT_PASSWORD
를 사용하고 있다. 또 이는, 파일 .env
와 연결되어 있다. .env
파일을 통해 비밀번호를 저장해놓고, config.js
파일에서 비밀번호를 고정시켜준다.
app.js
express로 서버를 만드는 코드가 이미 작성되어있다.
controllers/index.js
orders
에 대한 get
, post
요청과 items
에 대한 get
요청을 처리하는 로직이 이미 구현되어 있다. 이를 참고하여 어떻게 서버에 요청을 보내고 응답을 전송하는지 알 수 있다.
db/index.js
mysql
모듈을 사용해 데이터베이스와 서버를 연결한다.
models/index.js
controller
에서 사용할 orders, items 모델을 정의해야 한다. 기본적인 틀은 짜여져 있고,db/index.js
의 함수를 불러와 SQL을 사용하여 쿼리문을 통해 DB의 정보를 처리한다. 데이터베이스 쿼리는 반드시 비동기 요청인점을 고려해야 한다.
schema.sql
스키마가 미리 작성되어 있다.
seed.sql
에는 데이터를 추가하는 명령어가 작성되어 있다.
-> 미리 파일을 만들어두고 명령어를 실행하는 방식으로 데이터베이스에 테이블을 생성하고 데이터를 추가할 수 있다.
우선 schema.sql
파일을 보고 시각화하기 위해 스키마 디자인을 만들었다.
테스트 케이스를 읽고 1, 2번에 선행되어야 되는 게 3번이라고 생각해서 3번부터 진행하였다.
items: {
get: (callback) => {
// TODO: Cmarket의 모든 상품을 가져오는 함수를 작성하세요
callback(err, result);
}
}
Q. 여러 개의 레코드를 한번의 쿼리로 테이블에 저장하려면 어떻게 해야 할까요?
var sql = "INSERT INTO customers (name, address) VALUES ?";
var params = [
["John", "Highway 71"],
["Peter", "Lowstreet 4"],
["Amy", "Apple st 652"],
["Hannah", "Mountain 21"],
["Michael", "Valley 345"],
["Sandy", "Ocean blvd 2"],
["Betty", "Green Grass 1"],
["Richard", "Sky st 331"],
["Susan", "One way 98"],
["Vicky", "Yellow Garden 2"],
["Ben", "Park Lane 38"],
];
con.query(sql, [params], function (err, result) {
if (err) throw err;
console.log("Number of records inserted: " + result.affectedRows);
});
?
를 쓰면 두번째 인자를 넣어준다는 말이다. 두번째 인자는 무조건 배열로 받는다. ?
를 만나면서 배열이 하나 벗겨지기 때문이다.SQL을 이용해 쿼리문을 작성하고 데이터베이스를 db라는 변수로 받고있기 때문에 db에 쿼리문을 작성해주었다. INSERT가 아닌 단순히 데이터를 불러오고 있으니까 [params] 인자는 적지 않았다.
callback(err, result) 대신 err일 때 err, null 값을 보내주고 err가 아닐 때 null, result 를 보내주었다.
test1 get은 유저의 주문목록을 데이터베이스에서 가져와야 한다. 그래서 데이터베이스에 주문 요청을 저장하는 post가 먼저 구현되어야 한다.
orders: {
get: (userId, callback) => {
// TODO: 해당 유저가 작성한 모든 주문을 가져오는 함수를 작성하세요
callback(err, result);
},
post: (userId, orders, totalPrice, callback) => {
// TODO: 해당 유저의 주문 요청을 데이터베이스에 생성하는 함수를 작성하세요
callback(err, result);
}
}
post: (userId, orders, totalPrice, callback)
를 보면,
유저아이디, 주문들, 총금액을 받아온다. 데이터베이스에 각각 추가해줘야 하는 내용이다.
{
"orders": [
{
"quantity": 1,
"itemId": 2
}
// ...여러 개의 주문 아이템
],
"totalPrice": 16900
}
주문을 할 때 아이템의 아이디와 수량, 그리고 총 주문액까지 같이 합쳐져서 들어가는 걸 볼 수 있다. 이해하기 쉽게 우리가 물건을 살 때 사과 두 개 주세요 하는 거랑 같다. 아까 작성해놓은 스키마 디자인을 보면, order_items
와 orders
테이블에 각각 데이터를 보내야 된다는 걸 알 수 있다. 쿼리문을 두 번 써야한다.
orders
테이블에 userId, totalPrice
를 추가 order_items
테이블에 orders.itemId
와 orders.quantity
를 추가orders
의 id(PK)를 받아와 order_items
의 order_id(FK)
에 넣어줘야 한다. [
{
"id": 1, // orders 테이블의 id
"created_at": "2021-02-19T04:34:11.000Z",
"total_price": 7800,
"name": "칼라 립스틱",
"price": 2900,
"image": "../images/lip.jpg",
"order_quantity": 1,
},
{
"id": 1,
"created_at": "2021-02-19T04:34:11.000Z",
"total_price": 7800,
"name": "뜯어온 보도블럭",
"price": 4900,
"image": "../images/block.jpg",
"order_quantity": 1,
},
// ...여러 개의 주문내역
];
해당 유저가 작성한 모든 주문을 가져올 때 필요한 건 orders 테이블의 id, created_at, total_price, items의 name, price, image, order_quantity
가 있다. 테이블의 컬럼을 확인하고 조인시켜주면 되겠다.
이때 INNER JOIN, LEFT JOIN 둘 다 사용해도 결과값은 같은데 애초에 주문을 하고 그에 맞는 데이터가 담기는 거니까 INNER를 사용해도 값이 빠짐없이 담기고, LEFT를 사용해도 NULL로 담길 값이 없으니(무조건 뭘 시켰는지, 얼마나 시켰는지 있기 때문에) 크게 상관이 없는 거라고 생각된다. 마지막으로 Test3과 같이 callback(err, result) 대신 err일 때 err, null 값을 보내주고 err가 아닐 때 null, result 를 보내주었다.