오늘은 Express로 CRUD를 구현해보았따!! 데이터베이스는 MySQL을 사용했고, ORM은 typeORM을 사용했다. sequelize가 아닌 typeORM을 사용한 이유는, typeORM은 SQL 문법을 사용하기 때문에 SQL 작성 연습을 위해 typeORM을 선택했다. sequelize는 자바스크립트로 코드를 작성하면 알아서 SQL 구문으로 변경해준다. 나중에 SQL 작성이 익숙해지면 그땐 sequelize를 써보고 싶다.
기본 설정
const express = require("express");
const app = express();
const cors = require("cors");
const dotenv = require("dotenv");
dotenv.config();
const { DataSource } = require("typeorm");
const db = new DataSource({
type: process.env.DB_CONNECTION,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
});
db.initialize().then(() => {
console.log("DB 초기화");
});
app.use(cors());
app.use(express.json());
지금도 require이 많은데 앞으로 사용할 프레임워크가 많아지면 require 이 더 많아지겠지? 20줄 정도 차지해도 상관 없는건지 궁금하다!
.env
DB_CONNECTION = mysql
DB_HOST = 127.0.0.1
DB_USERNAME = root
DB_PASSWORD = 나의비밀번호
DB_DATABASE = Product
DB_PORT = 3306
DB_LOGGING =TRUE
나는 다른 포트번호를 쓰고 싶었는데 MySQL의 기본 포트가 3306이라고 한다. 딴 걸로 바꿨다가 작동 안해서 당황했다 큐큐
데이터베이스 이름은 내가 사용할 데이터베이스로 적어야 한다. 나는 상품 관련 데이터베이스를 만들어서 CRUD 연습을 해볼거라서 Product로 지었당
스키마
▶️ clothes 테이블
▶️ customer 테이블
간단하게 CRUD 연습을 할 거였기 때문에 shopping_list는 없어도 됐당...ㅋㅋ
clothes 테이블과 customer 테이블은 다대다 관계이다. 하나의 상품을 구입한 고객이 여러 명일 수 있고, 하나의 고객이 여러 상품을 구매할 수 있다! 그래서 고객이 물건을 살 때마다 구입한 상품을 기재할 수 있도록 clothes 테이블과 customer 테이블을 맵핑해 줄 clothes_customer 테이블을 만들었다.
맵핑 테이블이 있으면 좋은 점은 구입 시에만 customer_id와 clothes_id가 저장이 되기 때문에 데이터의 무결성을 확보할 수 있다. 또 조인을 하는 대신에 이 테이블을 이용해서 특정 고객이 구매한 옷을 보거나 아니면 특정 의류를 구매한 고객을 조회할 수 있다. 필요 시에는 이 테이블에 부가적인 정보를 추가해서 관리할 수도 있다! 다대다 관계에서 맵핑 테이블은 데이터의 일관성을 유지하고 조인보다 효율적으로 데이터를 조회할 수 있다는 장점이 있다.
API 명세서
설명 | 매서드 | url |
전체 clothes 조회 | GET | / |
구입 이력이 있는 customer와 판매 이력이 있는 clothes 조회 |
GET | /clothes |
customer 추가하기 | POST | /customer |
clothes 수정하기 | PUT | /clothes |
clothes 삭제하기 | DELETE | /clothes:productId |
조회하기(app.get)
node.js에서는 이벤트 핸들러로 직접 매서드(get,post 등)별로 실행할 동작을 분기처리해줘야 했다. 하지만 express는 다르다. app.HTTP매서드('/url', callback)로 각 url에 따라 실행 시킬 콜백함수를 작성하면 된다! 코드의 가독성도 높아지고 매우매우 편리하다.
node.js로 CRUD를 구현할 때보다 코드의 양도 확연히 줄어서 오늘은 코드까지 포함해서 포스팅하려고 한다ㅎㅎCRUD 연습하기 전에 각 테이블에 데이터 넣어놓기 잊지 말자~~~데이터도 안 넣어놓고 '왜 결과가 안나오지?'하기 없기!!
▶️ 전체 조회하기
app.get("/", async (req, res) => {
await db.query(`SELECT * FROM clothes`, (err, results) => {
res.status(200).json(results);
});
});
// HTTPie
http GET localhost:3000/
▶️ 구입 이력 있는 clothes와 판매 이력 있는 customer 조회
app.get("/clothes", async (req, res) => {
await db.query(
`SELECT clothes.productId, clothes.type, clothes.name, clothes.img,
customer.id, customer.email, customer.name, customer.shopping_list
FROM clothes_customer map
INNER JOIN customer ON map.customer_id = customer.id
INNER JOIN clothes ON map.clothes_id = clothes.productId`,
(err, results) => {
res.status(200).json(results);
}
);
});
// HTTPie
http GET localhost:3000/clothes
지난 번엔 이런 형태의 테이블을 조회할 때 FROM 내부에 이너조인 쓰고 바깥에도 이너조인을 써서 조회했었는데 이렇게 INNER JOIN을 동시에 두 개 써도 되는거였따ㅏㅏㅏ
데이터 추가하기(app.post)
app.post("/customer", async (req, res) => {
const { email, name, password, shopping_list } = req.body;
await db.query(
`INSERT INTO customer(
email, name, password, shopping_list
) VALUES (?,?,?,?) `,
[email, name, password, shopping_list]
);
res
.status(200)
.json({ message: "customer 테이블에 데이터가 추가되었습니다." });
console.log("customer 테이블 데이터 추가 완료!!");
});
// 또는
app.post("/customer", async (req, res) => {
const { email, name, password, shopping_list } = req.body;
await db.query(
`INSERT INTO customer(
email, name, password, shopping_list
) VALUES ([email : ?,name : ?,password : ?,shoppin_list : ?]) `
);
res
.status(200)
.json({ message: "customer 테이블에 데이터가 추가되었습니다." });
console.log("customer 테이블 데이터 추가 완료!!");
});
// HTTPie
http POST localhost:3000/customer email="coco@email.com" name="말랭이" password="비밀번호486"
추가하기는 고객 정보 추가하는 게 재밌어서 예시를 customer로 가져왔다. 당근 clothes 테이블을 가지고도 수정하기 기능을 만들어두긴 했다! 추가할 때 NOT NULL 제약조건이 없는 컬럼은 요청 시 데이터를 보내지 않아도 된다. shopping_list가 그렇다.
나는 여태까지 요청을 보낼 때 email , name, 이렇게 콤마로 구분을 지어야 하는 줄 알고 콤마를 꼬박꼬박 써왔다.
그러다 MySQL에 저장된 꼬라지를 보고 그동안 잘못했음을 깨달았따ㅠㅠ아 콤마 왜 같이 저장되는데...쒹쒹..데이터 구분지어준거였다고...
데이터 수정하기(app.put)
app.put("/clothes", async (req, res) => {
const { type, name, img, productId } = req.body;
await db.query(
`UPDATE clothes
SET
{type=?,
name=?,
img=?}
WHERE {productId=?};`
);
res
.status(200)
.json({ message: "clothes 데이터가 성공적으로 업데이트되었습니다." });
});
// 또는
app.put("/clothes", async (req, res) => {
const { type, name, img, productId } = req.body;
await db.query(
`UPDATE clothes
SET
type=?,
name=?,
img=?
WHERE productId=?;`,
[type, name, img, productId]
);
res
.status(200)
.json({ message: "clothes 데이터가 성공적으로 업데이트되었습니다." });
});
// HTTPie
http PUT localhost:3000/clothes productId=2 type="상의" name="하늘색 블라우스" img="/img2"
clothes 테이블에도 콤마가 같이 저장이 되었길래 put으로 두 개만 수정해주었당ㅎㅎ
데이터 삭제하기(app.delete)
app.delete("/clothes/:clothesId", async (req, res) => {
const { clothesId } = req.params;
await db.query(
`DELETE FROM clothes
WHERE clothes.productId = ${clothesId}`
);
res
.status(200)
.json({ message: `${clothesId}번 의류가 성공적으로 삭제되었습니다.` });
});
// HTTPie
http DELETE localhost:3000/clothes/6
clothesId를 파라미터로 전달해서 특정 clothesId가 삭제되도록 경로를 지정해주었다.
중간중간 내가 쿼리문을 제대로 맞게 썼는지, 그리고 정말로 데이터베이스에 변경된 내용이 저장이 됐는지 확인하기 위해서 터미널 두 개를 켜놓고 작업을 했다.
ㅎㅎ터미널 명령어로만 컴퓨터와 소통하는 거 정말 재밌다. 지금이야 쉽지만 점점 데이터가 많아지고 구조가 복잡해지면 골아프겠찡...🥺계속 달려보자 홧팅~~
댓글