부트캠프에서 프로젝트를 통해 튜터님들께 피드백을 받으면서 개인 프로젝트의 설계에 대해서도 몇가지 잘못된 점과 추가할 테이블이 있어서 수정하기로 했다.
기존 방식
배열을 JSON을 통해 문자열 형태로 변환 후 문자열 형태로 데이터를 저장
변경 후
새로운 테이블을 생성해 A와 B 모델을 N:M 관계 설정
특정 컬럼을 사용하지 않아서 삭제하기로 했다.
예전에는 pk에 대한 이해가 부족해서 pk를 생성하고 다른 unique한 컬럼을 생성해 그것을 pk처럼 다루었다.
기존 방식
A 테이블
PK-id-int
Field-webtoonId-string
변경 후
A 테이블
PK-id-string
웹툰에 사용자가 댓글을 남길수 있도록 댓글 테이블을 추가해야한다. 댓글 테이블은 사용자와 웹툰 테이블과 각각 1:N 관계를 가진다.
현재 DB의 테이블에는 이미 많은 정보가 들어 있으며 그 테이블을 수정하는 작업이기 때문에 잘못하면 데이터의 손실이 발생할 수 있다. 따라서 손실되더라도 바로 복구가 가능하도록 데이터를 미리 백업한다.
RDS는 자동으로 백업 기능을 지원하지만, 정확히 현재 상태를 백업으로 지정해주고 싶기 때문에 수동으로 진행한다.
AWS RDS -> 스냅샷 -> 스냅샷 생성
백업을 하고 싶다면 생성된 스냅샷을 들어가 스냅샷 복원을 선택하면 된다.
만약 스냅샷을 지정하는 것이 아니라 특정 시간대로 돌아가고 싶다고 한다면 자동 백업을 한다.
AWS RDS -> 자동 백업
다른 모델들은 저장되어 있는 데이터가 없어서 sequelize.sync를 사용하면 되고, 현재 마이그레이션을 사용해 변경해야 하는 것은 user와 webtoon 테이블이다.
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
return Promise.all([
// 1. userId -> email로 변경
queryInterface.renameColumn("user", "userId", "email"),
]);
},
// up을 반대로
async down (queryInterface, Sequelize) {
return Promise.all([
queryInterface.renameColumn("user", "email", "userId"),
]);
}
};
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
return Promise.all([
// 기존의 id 컬럼 삭제
await queryInterface.removeColumn("webtoon", "id"),
// webtoonId 컬럼을 pk로 설정
await queryInterface.addConstraint("webtoon", {
fields: ["webtoonId"],
type: "primary key",
name: "webtoon_webtoonId_key",
}),
// webtoonId -> id 이름 변경
await queryInterface.renameColumn("webtoon", "webtoonId", "id"),
// 필요없는 컬럼 삭제
queryInterface.removeColumn("webtoon", "embVectorDescription")
]);
},
// up을 반대로
async down (queryInterface, Sequelize) {
return Promise.all([
await queryInterface.renameColumn("webtoon", "id", "webtoonId"),
await queryInterface.removeConstraint("webtoon", "webtoon_webtoonId_key"),
await queryInterface.addColumn("webtoon", "id", {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
}),
queryInterface.addColumn("webtoon", "embVectorDescription", {
type: Sequelize.TEXT,
allowNull: true,
}),
]);
}
};
$ npx sequelize db:migrate
특정 migration 파일만 실행하고 싶다면 --to 옵션을 사용할수 있습니다.
ex) $ npx sequelize db:migrate --to 20231201-userConfig.js
기존의 장르 목록은 배열을 JSON을 통해 문자열 형태로 변환시켜 다음과 같은 형태로 저장되어 있다.
'["힐링물","짝사랑","소꿉친구","현대로맨스","로맨틱코미디","사차원","친구>연인","삼각관계","친구"]'
이제 genrewebtoon 테이블을 통한 웹툰과 장르의 N:M관계를 설정해 놓았으니 저 데이터를 통해 관계 테이블에 데이터를 저장시켜야 한다.
쿼리로 작성하기에는 내 실력이 모자라 코드를 통해 작성하였다.
async test(): Promise<void> {
try {
const webtoons = await this.webtoonService.getAllWebtoon();
for (let webtoon of webtoons) {
const keywords = JSON.parse(webtoon.genres);
for (let keyword of keywords) {
const genre = await this.getGenre({ keyword, service: "kakao" });
if (!genre) {
console.log(keyword, "장르가 존재하지 않습니다.");
continue;
}
const genreId = genre.id;
const webtoonId = webtoon.id;
const isExist = await this.genreWebtoonModel.findOne({
where: { genreId, webtoonId }
});
if (isExist) {
console.log(`이미 존재합니다.\n[${webtoonId}, ${genreId}]`);
continue;
}
await this.genreWebtoonModel.create({
genreId: genre.id,
webtoonId: webtoon.id,
});
console.log(`\n\n[생성]\n웹툰id: ${webtoon.id}\n제목: ${webtoon.title}\n장르키워드: ${genre.keyword}`);
}
}
} catch(e) {
console.log(e);
}
}