
ExpressJS MySQL CRUD


회원가입
import express from "express";
import {
isAuthenticated,
isNotAuthenticated,
} from "../middleware/middleware.js";
import {
authJoin,
authLogin,
} from "../controllers/authController.js";
const router = express.Router();
router.post("/join", isNotAuthenticated, authJoin);
router.post("/login", isNotAuthenticated, authLogin);
export default router;
routes > auth.js
import passport from "passport";
import bcrypt from "bcrypt";
import {
db,
userQuery,
authQuery,
} from "../database/index.js";
const authJoin = async (req, res, next) => {
const { name, password, email, gender } = req.body;
try {
await db.query("START TRANSACTION");
const searchName = await userQuery.searchName(name);
if (searchName.length > 0) return res.redirect("/?error=이미 가입된 회원입니다.");
const hash = await bcrypt.hash(password, 12);
const joinUser = await authQuery.joinUser(name, hash);
const userId = joinUser.insertId;
const user = await authQuery.infoUser(userId, email, gender);
await db.query("COMMIT");
res.json(user);
} catch (err) {
await db.query("ROLLBACK");
console.error(err);
next(err);
}
}
export {
authJoin,
authLogin,
}
controllers > authController.js
import mysql from "mysql2";
import dotenv from "dotenv";
dotenv.config();
const pool = mysql.createPool({
host: "localhost",
user: process.env.DB_ID,
database: process.env.DB,
password: process.env.DB_PW,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
pool.getConnection(async (err, conn) => {
try {
conn.release();
}
catch (err) {}
err ? console.error(err) : console.log("db connect");
});
const db = pool.promise();
const authQuery = {
joinUser: async (name, password) => {
const [ sql ] = await db.query(`
INSERT INTO users
(name, password)
VALUES
("${name}", "${password}")
`);
return sql;
},
infoUser: async (fk, email, gender) => {
const [ sql ] = await db.query(`
INSERT INTO infos
(fk_user_id, email, gender)
VALUES
(${fk}, "${email}", "${gender}")
`);
},
}
export {
db,
userQuery,
authQuery,
postQuery,
}
database > index.js
- post /auth/join 요청이 들어오면 authJoin 함수가 실행된다.
- 먼저 기존 유저인지 검사 후 아니면 회원가입을 진행한다.
- 유저와 세부 정보 테이블이 따로 분리돼있으므로 먼저 유저 테이블에 이름과 패스워드를 넣는다.
- 그 결과를 바탕으로 세부 정보 테이블에 외래키와 나머지 정보들을 넣는다.
- 유저 테이블에 데이터가 들어가야 세부 정보 테이블에도 넣을 수 있으므로 트랜잭션을 사용했다.
로그인
import passport from "passport";
import bcrypt from "bcrypt";
import {
db,
userQuery,
authQuery,
} from "../database/index.js";
const authLogin = async (req, res, next) => {
passport.authenticate("local", (authError, user, info) => {
if (authError) {
console.error(authError);
next(authError);
}
if (!user) return res.redirect(`/?error=${ info.message }`);
return req.login(user, (loginError) => {
if (loginError) {
console.error(loginError);
next(loginError);
}
return res.redirect("/main");
});
})(req, res, next);
}
export {
authJoin,
authLogin,
}
controllers > authController.js
import mysql from "mysql2";
import dotenv from "dotenv";
dotenv.config();
const pool = mysql.createPool({
host: "localhost",
user: process.env.DB_ID,
database: process.env.DB,
password: process.env.DB_PW,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
pool.getConnection(async (err, conn) => {
try {
conn.release();
}
catch (err) {}
err ? console.error(err) : console.log("db connect");
});
const db = pool.promise();
const userQuery = {
usersPosts: async () => {
const [ sql ] = await db.query(`
SELECT * FROM users U
JOIN infos I
ON U.user_id = I.fk_user_id
JOIN posts P
ON U.user_id = P.fk_user_id
`);
return sql;
},
searchId: async (id) => {
const [ sql ] = await db.query(`
SELECT * FROM users U
JOIN infos I
ON U.user_id = I.fk_user_id
WHERE U.user_id = ${id}
`);
return sql;
},
searchName: async (name) => {
const [ sql ] = await db.query(`
SELECT * FROM users
WHERE name = "${name}"
`);
return sql;
},
}
export {
db,
userQuery,
authQuery,
postQuery,
}
database > index.js
import passport from "passport";
import passportLocal from "passport-local";
import bcrypt from "bcrypt";
import { db } from "../database/index.js";
const LocalStrategy = passportLocal.Strategy;
export default () => {
passport.use(new LocalStrategy({
usernameField: "name",
passwordField: "password",
}, async (name, password, done) => {
try {
const sql = `SELECT * FROM users WHERE name = "${name}"`;
const [ exist ] = await db.query(sql);
const user = exist[0];
if (user) {
const compare = await bcrypt.compare(password, user.password);
if (compare) {
done(null, user);
} else {
done(null, false, { message: "비밀번호 불일치" });
}
} else {
done(null, false, { message: "가입되지 않은 회원" });
}
} catch (err) {
console.error(err);
done(err);
}
}));
};
passport > localStrategy.js
import passport from "passport";
import local from "./localStrategy.js";
import { db } from "../database/index.js";
export default () => {
passport.serializeUser((user, done) => {
done(null, user.user_id);
});
passport.deserializeUser((id, done) => {
const user = `SELECT * FROM users WHERE user_id = ${id}`;
db.query(user)
.then(res => done(null, res[0]))
.catch(err => done(err));
});
local();
}
passport > index.js
import express from "express";
import morgan from "morgan";
import nunjucks from "nunjucks";
import path from "path";
import dotenv from "dotenv";
import cookieParser from "cookie-parser";
import session from "express-session";
import passport from "passport";
import indexRouter from "./routes/index.js";
import authRouter from "./routes/auth.js";
import postRouter from "./routes/post.js";
import passportConfig from "./passport/index.js";
dotenv.config();
const app = express();
const __dirname = path.resolve();
passportConfig();
app.set("port", process.env.NODE_ENV || 1000);
app.set("view engine", "html");
nunjucks.configure("views", {
express: app,
watch: true,
});
app.use(morgan("dev"));
app.use("/image", express.static(path.join(__dirname, "images")));
app.use(express.static(path.join(__dirname, "public")));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
}));
app.use(passport.initialize());
app.use(passport.session());
app.use("/", indexRouter);
app.use("/auth", authRouter);
app.use("/post", postRouter);
app.use((req, res, next) => {
const error = new Error(`$${req.method} ${req.url} 존재하지 않습니다.`);
error.status = 404;
next(error);
});
app.use((err, req, res, next) => {
res.locals.message = err.message;
res.locals.error = process.env.NODE_ENV !== "production" ? err : {};
res.status(err.status || 500);
res.render("error");
});
const server = app.listen(app.get("port"), () => {
console.log("1000");
});
app.js
- port /auth/login 요청이 들어오면 authLogin 함수가 실행된다.
- 그 후 패스포트 전략을 사용하여 검증이 끝나면 /main 페이지로 리다이렉트시킨다.
- 패스포트는 세션에 의존하므로 반드시 세션 미들웨어 아래 라우터 미들웨어 위에 initialize/session을 호출한다.
게시글 작성
import express from "express";
import {
isAuthenticated,
isNotAuthenticated,
} from "../middleware/middleware.js";
import {
postWrite,
postUpdate,
postDelete,
multerNone,
multerImage,
} from "../controllers/postController.js";
const router = express.Router();
router.post("/write", isAuthenticated, multerImage.fields([
{ name: "image1" },
{ name: "image2" },
{ name: "image3" },
]), postWrite);
export default router;
routes > post.js
import multer from "multer";
import path from "path";
import {
userQuery,
postQuery,
} from "../database/index.js";
const multerNone = multer();
const multerImage = multer({
storage: multer.diskStorage({
destination(req, file, done) {
done(null, "images/")
},
filename(req, file, done) {
const ext = path.extname(file.originalname);
done(null, path.basename(file.originalname, ext) + Date.now() + ext);
},
}),
limits: { fileSize: 5 * 1024 * 1024 },
});
const postWrite = async (req, res, next) => {
try {
await postQuery.writePost(
req.user[0].user_id,
req.body.title,
req.body.content,
req.files["image1"]?.[0].filename ?? null,
req.files["image2"]?.[0].filename ?? null,
req.files["image3"]?.[0].filename ?? null
);
res.redirect("/notice");
} catch (err) {
console.error(err);
next(err);
}
}
export {
postWrite,
postUpdate,
postDelete,
multerNone,
multerImage,
}
controllers > postController.js
import mysql from "mysql2";
import dotenv from "dotenv";
dotenv.config();
const pool = mysql.createPool({
host: "localhost",
user: process.env.DB_ID,
database: process.env.DB,
password: process.env.DB_PW,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
pool.getConnection(async (err, conn) => {
try {
conn.release();
}
catch (err) {}
err ? console.error(err) : console.log("db connect");
});
const db = pool.promise();
const postQuery = {
searchId: async (id) => {
const [ sql ] = await db.query(`
SELECT * FROM users U
JOIN posts P
ON U.user_id = P.fk_user_id
WHERE P.post_id = ${id}
`);
return sql;
},
writePost: async (fk, title, content, image1, image2, image3) => {
const[ sql ] = await db.query(`
INSERT INTO posts
(fk_user_id, title, content, image1, image2, image3)
VALUES (${fk}, "${title}", "${content}", "${image1}", "${image2}", "${image3}")
`);
return sql;
},
deletePost: async (id) => {
const [ sql ] = await db.query(`
DELETE FROM posts
WHERE post_id = ${id}
`);
return sql;
},
updatePost: async (id, title, content) => {
const [ sql ] = await db.query(`
UPDATE posts
SET
title = "${title}",
content = "${content}"
WHERE
post_id = ${id}
`);
return sql;
},
}
export {
db,
userQuery,
authQuery,
postQuery,
}
database > index.js
- post /post/write 요청이 들어오면 postWrite 함수가 실행된다.
- 게시글 정보(req.body)와 유저 정보(req.user)를 넣는다.
- multer로 여러 개의 이미지를 fields 메서드(하나면 single)로 받는다.
- 게시글의 이미지는 NULL 값이 될 수 있으므로 존재하면 이미지의 파일 이름을 넣고 없으면 NULL 값으로 넣는다.
게시글 업데이트
import express from "express";
import {
isAuthenticated,
isNotAuthenticated,
} from "../middleware/middleware.js";
import {
postWrite,
postUpdate,
postDelete,
multerNone,
multerImage,
} from "../controllers/postController.js";
const router = express.Router();
router.put("/update/:id", isAuthenticated, postUpdate);
export default router;
routes > post.js
import multer from "multer";
import path from "path";
import {
userQuery,
postQuery,
} from "../database/index.js";
const postUpdate = async (req, res, next) => {
try {
await postQuery.updatePost(
req.params.id,
req.body.title,
req.body.content
);
res.redirect("/notice");
} catch (err) {
console.error(err);
next(err);
}
}
export {
postWrite,
postUpdate,
postDelete,
multerNone,
multerImage,
}
controllers > postController.js
import mysql from "mysql2";
import dotenv from "dotenv";
dotenv.config();
const pool = mysql.createPool({
host: "localhost",
user: process.env.DB_ID,
database: process.env.DB,
password: process.env.DB_PW,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
pool.getConnection(async (err, conn) => {
try {
conn.release();
}
catch (err) {}
err ? console.error(err) : console.log("db connect");
});
const db = pool.promise();
const postQuery = {
updatePost: async (id, title, content) => {
const [ sql ] = await db.query(`
UPDATE posts
SET
title = "${title}",
content = "${content}"
WHERE
post_id = ${id}
`);
return sql;
},
}
export {
db,
userQuery,
authQuery,
postQuery,
}
database > index.js
- put /post/update/:id 요청이 들어오면 postUpdate 함수가 실행된다.
- 게시글 번호(req.params.id)와 업데이트 정보(req.body)를 받아 업데이트한다.
게시글 삭제
import express from "express";
import {
isAuthenticated,
isNotAuthenticated,
} from "../middleware/middleware.js";
import {
postWrite,
postUpdate,
postDelete,
multerNone,
multerImage,
} from "../controllers/postController.js";
const router = express.Router();
router.delete("/delete/:id", isAuthenticated, postDelete);
export default router;
routes / post.js
import multer from "multer";
import path from "path";
import {
userQuery,
postQuery,
} from "../database/index.js";
const postDelete = async (req, res, next) => {
try {
await postQuery.deletePost(req.params.id);
res.redirect("/notice");
} catch (err) {
console.error(err);
next(err);
}
}
export {
postWrite,
postUpdate,
postDelete,
multerNone,
multerImage,
}
controllers > postController.js
import mysql from "mysql2";
import dotenv from "dotenv";
dotenv.config();
const pool = mysql.createPool({
host: "localhost",
user: process.env.DB_ID,
database: process.env.DB,
password: process.env.DB_PW,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
pool.getConnection(async (err, conn) => {
try {
conn.release();
}
catch (err) {}
err ? console.error(err) : console.log("db connect");
});
const db = pool.promise();
const postQuery = {
deletePost: async (id) => {
const [ sql ] = await db.query(`
DELETE FROM posts
WHERE post_id = ${id}
`);
return sql;
},
}
export {
db,
userQuery,
authQuery,
postQuery,
}
database > index.js
- delete /post/delete/:id 요청이 들어오면 postDelete 함수가 실행된다.
- 게시글 번호(req.params.id)를 받아 해당하는 게시글을 삭제한다.
'서버 > Express' 카테고리의 다른 글
ExpressJS CSRF (0) | 2022.06.19 |
---|---|
ExpressJS Sequelize transaction (0) | 2022.06.17 |
ExpressJS CORS(Cross-Origin-Resource-Sharing) (0) | 2022.03.28 |
ExpressJS JWT (0) | 2022.03.26 |
ExpressJS method-override (0) | 2022.03.22 |