__________________________________________________
1-20 로그인/회원가입 기능을 Sequelize 로 구현하기 (1)
=> < 2.4 MySQL → Sequelize 04>
폴더 : kimminsoo -> sparta -> node_js -> learning -> second_step -> shop_project_sequelize
Node.js 입문주차에서는 mongoose를 이용해서 로그인 및 회원가입 기능을 구현했었지.
그러나 우리가 구현하는 기능들은 데이터베이스의 유형에 구애받지 않는다는것을 직접 체험해보기 위해
사용자 모델을 sequelize로 다시 구현하고 기능이 잘 동작하는지 확인해볼거야.
=> 쇼핑몰 프로젝트.
_________________
— sequelize 를 통한 res-mysql 사용을 위해서 테이블 모델 구성하기 —
1.
Models -> user.js 를 삭제
=> mongoose 로 구현한 모델을 지우고, 해당 모델을 sequelize 로 다시 구현해 볼거야.
2.
터미널에 아래의 명령어를 입력.
=> 이를 통해서 user 모델과, 이 모델을 적용시키기 위한 테이블 생성 코드도 동시에 만들거야.
——
npx sequelize model:generate --name User --attributes email:string,nickname:string,password:string
// 실행 결과 - 터미널
Sequelize CLI [Node: 18.12.0, CLI: 6.5.2, ORM: 6.27.0]
New model was created at /Users/kimminsoo/sparta/node_js/learning/second_step/shop_project_sequelize/models/user.js .
New migration was created at /Users/kimminsoo/sparta/node_js/learning/second_step/shop_project_sequelize/migrations/20221219033042-create-user.js .
——
=> 정상적으로 models 라는 폴더 안에 user.js 가 생성 되었어. User 모델이 만들어진 것.
그리고 migrations -> 20221219033042-crete-user.js 라는 파일도 생겼어.
=> user 테이블을 구성하는 정보를 만들기 위해서 migrations 는 테이블을 만들기 위한 정보들을 정리해서 해당 js 파일로 명시한 거야.
models/user.js 의 역할:
데이터베이스에 읽고 쓰고, 변경하는 데이터를 모델이라는 형태로 구현해서 정해둔 형식대로 다루게 해주는 역할을 맡아.
이 파일을 직접 만들어보고 싶다면 공식 문서의 모델 페이지를 참고해.
migrations 안에 있는 파일의 역할:
데이터베이스에 테이블을 생성하고 제거할 때 여기에 있는 파일을 사용.
Seqeulize cli는 models(src/db/models)을 생성하여 데이터베이스를 생성했어.
업데이트는 migration files(src/db/migrations)에 저장될 것.
=> 관련 블로그 :
3.
잠시 집고 넘어가자. Models 안의 user.js 와 migrations 안의 20221219033042-crete-user.js 의 차이에 대해서.
Migrations 폴더 안에 있는 js 파일들은, 실제 데이터베이스 안에 테이블이 만들어 지기 위해서 필요한 일들이라고 할 수 있어.
20221219033042-crete-user.js 안을 보면 다음과 같이 명시된 구문이 있어.
——
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Users', …..
——
=> 즉, Users 라는 테이블을 실제로 만들기 위해서 사용하는 migrations 모듈이라는 뜻이야.
마치 create table Users (id auto-increment primary-key, email….) 이런 식으로 sql 문으로 table 을 만들 듯!
그렇다면, models 폴더 안의 user.js 는 어떤 역할인가?
=> 실제로 express 내에서 sequelize 를 통해 DB 안에 있는 정보를 사용하거나 조회하거나 하려는 작업을 할 때에는,
실제 DB 안에 있는 정보가 아니라, models 안에 있는 각각의 모델을 기준으로 사용을 하게 될거야.
——
User.init({
email: DataTypes.STRING,
nickname: DataTypes.STRING,
password: DataTypes.STRING
},
——
=> User 모델은 DB 안에 email, nickname, password 와 같이 3개의 칼럼이 존재하는 것으로 내부적으로 인지, 인식을 하고 있어.
이 모델을 가지고 우리가 express 내부에서도 sequelize 를 통해서 mysql 의 DB 를 사용할 수 있게 되는 것이지.
즉, 데이터베이스에 읽고 쓰고, 변경하는 데이터를 모델이라는 형태로 구현해서 정해둔 형식대로 다루게 해주는 역할을 하는거지.
이 파일을 직접 만들어보고 싶다면 공식 문서의 모델 페이지를 참고해봐!
정리.
Migrations 는 실제 DB에 있는 테이블을 만들기 위해서 사용.
Model 는 실제로 express 내부에서 해당하는 DB 에 있는 테이블을 사용하기 위해서 사용.
4.
models 와 migration 을 수정해야 해.
=> 우리는 명령어 한줄로 간단하게 사용자 모델을 만들었지만, 두 파일 모두 한가지씩 수정할 거야.
Sequelize는 데이터의 고유 ID를 기본적으로 "id" 로 저장하도록 하는데, 스파르타가 준비한 프론트엔드 코드는
이 고유 ID의 이름이 "id"가 아닌 "userId"여야 제대로 동작하도록 짜여 있어.
그렇기 때문에 id 대신 userId라는 이름으로 ID를 다루도록 수정해야 해.
5.
migration 의 user 테이블 생성 코드와 models 의 user 테이블 모델을 만들었으니, 실재로 테이블을 생성해 보자
——
npx sequelize db:migrate
// 터미널 결과
Sequelize CLI [Node: 18.12.0, CLI: 6.5.2, ORM: 6.27.0]
Loaded configuration file "config/config.json".
Using environment "development".
== 20221219033042-create-user: migrating =======
== 20221219033042-create-user: migrated (0.076s)
——
=> config/config.json 파일에 있는 development 키의 옵션들을 참고하여 테이블을 만들었다고.
그리고 테이블의 스키마는 migrate 기능을 사용해서 구성했다. 해당 기능은 migrations 폴더 내부의 테이블 생성 코드들을 참조한다.
참고로, Users 테이블은 생성될 때 migrations 폴더의 “ 20221219033042-create-user.js “ 파일의
테이블 생성용 코드를 기틀로해서 만들어진 것인데, Users 라는 이름은 어디서 온 것이냐면
——
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {….
——
여기서 테이블 이름을 ‘Users’ 라 하겠다 라고 명시를 했었네.
근데 ‘Users’ 는 어디서 온 건데….?
——
npx sequelize model:generate --name User --attributes email:string,nickname:string,password:string
——
=> 모델이랑 migration 만들 때 입력했던 명령어인데…
설마 모델명 “User” 을 보고 테이블 이름을 “Users” 라고 정한 건가…?
그럼 s 붙이기 싫을 때에는 migration 에 가서 내가 직접 테이블 이름을 바꿔주면 되겠네
__________________________________________________
1-21 로그인/회원가입 기능을 Sequelize 로 구현하기 (2)
=> < 2.4 MySQL → Sequelize 05>
마지막 장에서는,
sequelize 를 통해서 테이블도 만들어 봤으니 실재로 코드들을 수정해보자.
이전에는 mongoose 라이브러리를 사용했었지? 이제는 mysql 모듈을 사용하는 것으로 바꿀거야.
이번에 구현한 사용자 모델을 이용해서 MongoDB가 아닌 MySQL에 사용자 데이터를 저장하도록 바꿔보자.
1.
사용자 모델 불러오기 예시
——
const { User } = require("./models");
——
=> 뭐꼬 이건?
models/index.js 파일이 데이터베이스 연결 설정과 함께
Sequelize 모델 파일을 불러와서 다시 내보내주기 때문에 위의 코드처럼 사용자 모델을 사용해야 함.
뭔소리냐.
require("./models") 는 자기가 알아서 models 안에 있는 index.js 를 불러 오는데,
*** const connect = require("./schemas") // index.js 파일은 폴더를 불러오기만 해도 사용할 수 있어. << 이것 처럼
index.js 파일은 데이터베이스 연결을 설정함과 동시에 sequelize 모델들을 전부 다 함께 불러와서는 다시 모듈로 내보내 줘.
즉, 원래 같았으면 models/user.js. 이렇게 불러와야 할 것을
*** const Goods = require('../schemas/goods.js') <<< 이것 처럼
models 로 index.js 를 자동으로 가져오게 함으로써 index.js 가 함께 불러오는 테이블 모델들도 함께 사용할 수 있게 되는 거야.
가져오는 형태가 객체 분해 할당인 이유는, index.js 가 swquelize 모델들을 전부 다 가지고 오는데 객체(키, 키값) 형태로 가져오기 때문.
2.
회원가입 API 수정
——
// 경로 : localhost:8080/api/users 회원가입 API
router.post("/users", async (req, res) => {
const { email, nickname, password, confirmPassword } = req.body;
if (password !== confirmPassword) {
res.status(400).send({
errorMessage: "패스워드가 패스워드 확인란과 다릅니다.",
});
return;
}
// email or nickname이 동일한게 이미 있는지 확인하기 위해 가져온다.
const existsUsers = await User.findAll({
where: {
[Op.or]: [{ email }, { nickname }],
},
});
if (existsUsers.length) {
res.status(400).send({
errorMessage: "이메일 또는 닉네임이 이미 사용중입니다.",
});
return;
}
await User.create({ email, nickname, password });
res.status(201).send({});
});
——
=> email 과 nickname이 동일한 게 이미 있는지 확인하는 메소드인 findAll(),
마지막에 insert하는 create().
기존에 mongoose 를 사용했을 때와는 달라진 부분들.
3.
— 사용자 모델을 이용해 로그인 기능 수정하기 ——
=> 여기서는 사용자 정보를 데이터베이스에서 가져오는 부분만 수정하면 문제 없을거야.
——
// 경로 : localhost:8080/api/auth 로그인 API
router.post("/auth", async (req, res) => {
const {email, password} = req.body;
const user = await User.findOne({
where: {
email,
},
});
if (!user || password !== user.password) {
// 입력받은 이메일의 user가 없다면, 즉 null 이거나 해당 user의 비밀번호와 입력받은 비밀번호가 다른 경우.
return res.status(400).json({errorMessage: "아이디 혹은 비밀번호가 다릅니다...."})
}
const token = jwt.sign({userId: user.userId}, "sparta-secret-key") // find 로 가져올 때 virtual 덕분에 userId 라는 필드가 생김.
res.status(200).json({
"token": token
})
})
——
=> 로그인 시 DB에서 사용자 정보를 가져오는 부분만 수정되었어.
findOne() 메소드를 사용했네.
4.
사용자 모델을 이용해 로그인 확인 미들웨어 수정하기
=> userId를 가지고 데이터베이스에서 사용자 정보를 불러오도록 해야 해.
공식 문서를 참고해보니 findByPk를 사용하면 적당해 보여.
——
// schema 모델
const { User } = require("../models");
// jwt
const jwt = require("jsonwebtoken");
const { json } = require("express"); // 이거 왜 있는건데? 뭐지?
module.exports = async (req, res, next) => {
const {authorization} = req.headers;
/*
Bearer eyff89fsdf9... 막 이렇게 생긴토큰.
localhost:8080/api/auth (로그인 API) 에 접속했을 때 로그인 하면서 jwt 토큰을 받아갔었어.
프론트에서는 그 jwt 토큰을 가지고 있다가 req 를 보낼 때 HTTP header 에
Authorization: Bearer 타입으로 담아서 보낸거야.
*/
const [authType, authToken] = authorization.split(" ");
/*
Bearer 과 뒷 부분의 토큰을 분리해서 배열로.
그리고 나서 [0]의 Bearer 라는 타입과 [1] 의 jwt 토큰 내용을 배열 구조분해 할당.
*/
// 헤더의 요소 중 하나인 authorization 에 담긴 토큰의 타입이 Bearer 가 아니거나, 토큰이 비어있을 경우.
if (authType !== "Bearer" || !authToken) {
return res.status(400).json({errorMessage: "로그인 후 사용이 가능한 API 입니다."})
}
// 위의 if 를 넘겼다면, 무사히 유저의 req.header 에 jwt 토큰이 담겨져 왔다는 뜻. 그러니 복호화 및 검증을 해야.
try {
const {userId} = jwt.verify(authToken"sparta-secret-key") // 무사히 검증이 됐다면 payload를 반환.
User.findByPk(userId).then((user) => {
res.locals.user = user;
next();
});
} catch (error) {
return res.status(400).json({errorMessage: "잘못된 토큰입니다. 로그인을 해주세요."})
}
}
——
=> const user = await User.findById(userId) 가
===> User.findByPk(userId).then() 으로 변했어.
사용하는 DB 를 mongoDB 에서 mysql 로 바꾸었는데, 코드 적으로는 크게 바뀐 부분이 없어.
즉, 어떤 DB를 쓸까 처럼 ‘도구’ 에 매몰되는 것 보다는, API 기능을 구현하기 위한 로직에 집중하는 게 더 올바른 방향성.
'내일배움캠프_개발일지 > Node.js 기초' 카테고리의 다른 글
Node.js 심화-1 (0) | 2022.12.26 |
---|---|
Node.js 숙련 -8 (0) | 2022.12.23 |
Node.js 숙련 - 6 (0) | 2022.12.21 |
Node.js 숙련 - 5 (1) | 2022.12.20 |
Node.js 숙련 - 4 (0) | 2022.12.20 |