간략하게 프로젝트 하면서 기록해두고 싶었던 것들을
두서 없이 적을 예정입니다...
npm i express, cokie-parser, jsonwebtoken,
+
(몽고db => mongoose, mySQL => mysql2, sequelize => sequelize, sequelize-cli(devDependencies로 설치))
// app.js
const express = require("express");
const app = express();
app.use("/기본주소", [Router1, Router2 ...]);
+ 사용하기 좋은 것들
app.use(express.json());
app.user(express.urlencoded({extended: false}));
express 선언 후 app에 받아주고 app.use로 모든 router들이 거쳐서 지나갈 수 있도록 설정
+json, urlencoded, cokiParser 등 ...
몽고DB의 경우...
Schema의 index.js에서 주소 설정
const mongoose = require("mongoose");
const connect = () => {
mongoose
.connect("mongodb://localhost:27017/주소")
.catch((err) => console.log(err));
};
mongoose.connection.on("error", (err) => {
console.error("몽고디비 연결 에러", err);
});
module.exports = connect;
위의 app.js에서
const connect = require("./schemas/index.js(index.js경로)");
connect();
로 받아줌
이후에 Schema 작성
//postSchema
const mongoose = require("mongoose");
const postsSchema = new mongoose.Schema({
userId: {
type: String,
required: true,
},
nickname: {
type: String,
required: true,
},
});
postsSchema.virtual("postId").get(function () {
return this._id.toHexString();
});
postsSchema.set("toJSON", {
virtuals: true,
});
module.exports = mongoose.model("posts", postsSchema);
postsSchema 안에서 스키마의 형태를 지정해줌
required(필수 값인지?)
unique(유일해야 하는지?)
virtual ~ 부분은 실제 노출되지 않는 부분을 toJSON으로 불러 올때
보이도록 해줌
이를 통해서 post의 _id값을 가진 comment를 만든다거나 할 때 용이함.
아까 작성했던 app.js의 router 부분 작성.
//postsRouter
const express = require("express");
const router = express.Router();
const Posts = require("../schemas/post.js");
이후 get, post, put, delete 등 api 통신 관련 코드 작성
module.exports = router; //으로 내보내줌
++ 추가사항 미들웨어를 통해서 공통적으로 적용시켜야하는 부분에 대해서
편하게 작성이 가능하다.
예를 들어 middleware.js를 만들고
해당 부분에 로그인 인증 기능을 작성하면
const Middleware = require("middleware경로");
router.get("/주소경로", Middleware, async(req, res) => {
const {userId} == middleware에서 export한 값
내용~
}
의 방식으로 미들웨어를 통해 공통적인 인증 사항을 진행 할 수 있음
(완전 편리)
jsonwebtoken
== jwt
cookie에 로그인 정보 등을 바로 부여할 경우, 사용자가 조작하거나 할 수 있는
위험성 존재
따라서 사용자에게는 열쇠만 쥐어주고 필요값은 서버에서 받음
const jwt = require("jsonwebtoken");
//암호화(키생성)
const token = jwt.sign({"이름":"값"}, "비밀 키");
//쿠키로 보냄(authorization의 이름으로 Bearer 타입의 토큰을 보냄)
res.cookie("authorization", `Bearer ${token}`);
으로 사용자에게 보내고
const jwt = require("jsonwebtoken");
module.exports = async (req, res, next) => {
//쿠키 받아옴
const {authorization} = req.cookies;
const [authType, authToken] = (authorization ?? "").split(" ");
if(Bearer토큰인지 확인 등 유효성 검사 진행 ...){
res.status(400).json({})
}
const {"이름"} = jwt.veerify(authToken, "비밀 키")
}
이런식으로 cookie 값을 이용해서 확인 가능
(req.cookies는 cookie-parser를 사용해야만 사용가능)
mySQL sequelize 사용!
npx sequelize init 명령어로 sequelize 기본 구조 작성
config에서 유저 이름, 비밀번호, 데이터베이스 이름, host 값 등을 정해줌
npx sequelize db:create 명령어로 db 생성
몽고 db와는 다르게 migrations과 models 을 사용
예시)
npx sequelize model:generate --name (이름) --attributes (컬럼이름):string,content:string,password:string
아래는 간략한 구조
//migrations
"use strict";
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("Posts", {
postId: {
allowNull: false, // null값 허용 x
autoIncrement: true, // 자동으로 1씩 증가
primaryKey: true, // 기본 키값으로 사용 unique가 자동으로 true
type: Sequelize.INTEGER, // 타입 설정
},
UserId: {
allowNull: false,
type: Sequelize.INTEGER,
references: { // 참조
model: "Users", // 어느 모델에서? "Users"
key: "userId", // 어떤 값을 기준으로? "userId"
},
onDelete: "CASCADE", // "CASCADE"의 경우 Users의 일치하는 userId가 삭제될 경우 같이 삭제된다.
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn("now"), // 현재 시간을 자동으로 저장
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable("Posts");
},
};
//models
"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Posts extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
// 현재 모델(Posts)에서 Users 모델에게 다 대 일 관계 설정
this.belongsTo(models.Users, {
targetKey: "userId", // Users 모델 내부의 userId 컬럼과
foreignKey: "UserId", // Posts 모델의 UserId 컬럼을 연결해줌.
//migrations에서 설정을 해주어야합니다.
});
}
}
Posts.init(
{
postId: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
UserId: {
allowNull: false,
type: DataTypes.INTEGER,
},
createdAt: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
},
{
sequelize,
modelName: "Posts",
}
);
return Posts;
};
다 작성 후
npx sequelize db:migrate 명령어로 migrate
얼추 기존 몽고디비 사용 때와 비슷하지만
결정적으로 find문들의 사용 방법이 다르다!
쿼리를 날리 때 처럼 where절을 사용
예시
await Users.findOne({where: {nickname}});
//생성때도...
await Users.create({nickname});
//join! 아니.. include
await Users.findOne({
attributes: ["보여주고싶은거1", "보여주고싶은거2"],
include:[
{
model: Users,
attributes: ["보여주고싶은거1", "보여주고싶은거2"]
}
],
where: {postId}
});
여기서의 where절은 include에 해당하는 것이 아니라
자기 자신의 findOne에 해당하는 것이고,
include 시의 참조 값은 앞서서
migrations와 models에서 관계 설정을 이미 끝마쳤음!!
즉 전체적인 골자는 비슷하고
다만 몽고디비의 명령어인가 아니면 sequelize의 명령어인가 하는
약간의 차이만 존재했다.