저번주 미니프로젝트 때부터 있어왔던 트러블 슈팅을 순서대로 정리해본다.
//user를 참조하는 테이블의 모델 파일
LikeAndDislike.associate = function (models) {
LikeAndDislike.belongsTo(models.User, {
foreignKey: "userId",
targetKey: "userId",
onUpdate: "cascade",
onDelete: "cascade",
constraints: false,
});
LikeAndDislike.belongsTo(models.User, {
foreignKey: "targetUserId",
targetKey: "userId",
onUpdate: "cascade",
onDelete: "cascade",
constraints: false,
});
};
User.associate = function (models) {
User.hasMany(models.LikeAndDislike, {
foreignKey: "userId",
sourceKey: "userId",
onUpdate: "cascade",
onDelete: "cascade",
constraints: false,
});
User.hasMany(models.LikeAndDislike, {
foreignKey: "targetUserId",
sourceKey: "userId",
onUpdate: "cascade",
onDelete: "cascade",
constraints: false,
});
};
이런 식으로 1 대 다 관계인 두 모델에 hasMay, belongsTo를 적고
그 안에 foreingKey, sourceKey, targetKey, onUdate, onDelete를 적어주면 모든게 다 완벽히 테이블에 적용되어 생성됬었다.
models/index.js파일은 건들지 않아도 됐고, migration 파일에도 딱히 관계에 관해서는 작성하지 않았었다.
나는 내가 작성한게 잘 먹혔는지를 워크벤치에 자동으로 외래키나 cascade가 적용됐는지 여부로 확인하였다.
성공했을 때 그 방법을 잘 정리해뒀고 그대로 따라했건만 제대로 되지 않았다. 그래서 하루 종일 찾아서 외래키를 적용시키는데 성공하는 두 가지 방법을 찾아내었다.
npx sequelize-cli migration:generate --name user-like-association
npx sequelize-cli migration:generate --name user-dislike-association
이와 같이 새 마이그레이션 파일을 만들고 다음과 같은 것을 작성하여 직접 외래키를 추가한다.
"use strict";
module.exports = {
async up(queryInterface, Sequelize) {
queryInterface.addConstraint("Likes", {
fields: ["userId"],
type: "foreign key",
name: "Like_People_asociation",
references: {
table: "Users",
field: "userId",
},
});
},
async down(queryInterface, Sequelize) {
queryInterface.removeConstraint("Likes", {
fields: ["userId"],
type: "foreign key",
name: "Like_People_asociation",
references: {
table: "Users",
field: "userId",
},
});
},
};
이 방법으로 외래키가 생성된 것을 확인하였지만 이것도 뭔가 완벽하게 되지는 않아서 두번째 방법을 했던 것으로 기억한다.
//user를 참조하는 테이블을 생성하는 마이그레이션 파일
userId: {
type: Sequelize.INTEGER,
allowNull: false,
onUpdate: "cascade",
onDelete: "cascade",
constraints: false,
references: {
model: "Users",
key: "userid",
},
},
마이그레이션에 references를 이와 같이 적으니 외래키가 생성된 것을 워크벤치로 확인 가능하였다.
근데 이 방법도 문제가 있었는데 테이블 생성 직후 워크벤치로 확인하였을 때는 외래키 이름이 정상적으로 되있었는데
한번 쿼리를 실행하고 나니까,
UserUserid 이런식으로 시퀄라이즈가 자동생성한 컬럼이름으로 바뀌는 현상이 생겼다.
그래서 또 방법을 찾아보다가, 저번주에 model파일에 작성한 방법과 이 방법을 혼용해 보기로 했다.
그랬더니, 외래키가 처음 생성된 것에서 변하지 않고 그대로 잘 유지되었다.
그러나 한가지 문제가 더 남았었는데, cascade는 적용되지 않고, RESTRICT로 외래키가 생성되는 것이었다.
시간이 없으니 쿼리만 잘 작동된다면 그냥 두는 게 맞지만, 쿼리가 안되도록 막고 있는 것으로 생각되었다.
sequelize cannot add or update a child row a foreign key constraint fails
이런 식의 에러였는데, 나는 이것을 외래키 관계는 잘 되었지만 그것에 cascade가 적용이 되지 않아서 생기는 에러라고 생각되었다.
그래서 cascade는 또 어떻게 다시 적용하는지 찾아봤다.
userId: {
type: Sequelize.INTEGER,
allowNull: false,
onUpdate: "cascade",
onDelete: "cascade",
constraints: false,
references: {
model: "Users",
key: "userid",
},
},
성공한 방법은 아까 그 두번째 방법에도 써놨는데, 나는 여기서 전에는
onDelete와 onUpdate를 references 안에다가 써놨었다.
그런데 이렇게 바깥에다 쓰는게 맞았던 것이다.
이 references 자체는 시퀄라이즈 공식문서에도 나와있지만, onUpdate를 어디다 명시해야하는지는 공식문서에서 찾지 못했다.
시퀄라이즈 사이트는 검색 기능도 없어서 찾기도 매우 불편했다.
아무튼 다른 사이트를 보다가 발견하게 되었다.
cascade까지 적용하고서 한동안 문제 없이 쿼리가 잘됐었는데 갑자기 또 이 에러가 발생하였다. 그래서 에러를 자세히 다시 읽어보니
이 constraint라는 것이 문제일 수 있겠단 생각이 들어 이것을 false로 지정하는 것들을 찾아서 적용했다.
대충 이게 무슨 기능이냐면, 참조되는 테이블보다 참조하는 테이블이 먼저 생성될 수 없도록 제한하고
또, 참조되는 테이블에 없는 값이 참조하는 테이블의 외래키의 값으로 먼저 들어가는 것도 제한하는 기능인 것 같았다.
어쨌든, 위에 작성한 코드들에는 이미 constrait 관련된게 작성된 버전들이지만 models파일에는 저기에 작성하는 건 맞는 것 같고.
migraition에서는 references 안에다 적는게 맞을지, 밖에다 적는게 맞을지, 아니면 migration에서는 적어도 소용없는 것인지는 확인을 못했다.
결국에 이번에 저 에러가 떴던 이유는 내가 외래키 하나를 값을 입력안하고 create 하려고해서 난 에러였던 거로 확인되어서 일단 넘어갔다.
npx sequelize db:drop
npx sequelize db:create
npx sequelize db:migrate
npx sequelize db:migrate:undo
여기서 undo는 빼고 저 위 세개를 순서대로 치면서 테이블을 리셋하였다.
migrate:undo는 순서가 꼬이면 테이블 삭제가 안될 가능성이 존재하는데 db자체를 drop하는게 더 확실한 방법이다.
그리고 나서 더미 데이트들은 workbench에서 insert 구문을 쫘르륵 적어놓은 sql파일을 실행하여 바로바로 추가하였다.
이번에 하는 주제가 틴더 클론 코딩이라서 사용자 데이터가 바로바로 많이 필요했기 때문이다.
나에게는 다행히 이문제는 발생하지 않았지만 다른 팀 친한 사람이 engine문제로 고통 받고 있었다.
migration으로 테이블을 생성하고 워크벤치로 확인해보면,
engine이 이와같이 InnoDB여야 외래키가 정상적으로 적용되는데
MyISAM engine으로 테이블이 생성되어서 잘 적용이 안되는 문제였다.
이 문제를 나도 찾아보았는데, sequelize 관련해서는 engine이라는 키워드로 나오는 설정 방법이 없어보였고.
이 문제는 sequelize가 아닌 mysql 상의 문제인 것으로 생각되었다.
실제로 나는 다음과 같이 확인해 봤을 때
show engines;
InnoDB가 DEFAULT로 잘 적용되어있는데
그 친구는 null이라고 되어있었다.
그래서
set global storage_engine=innoDB;
이 명령어를 쳐보도록 조언해주었는데, 일단 해결됐는지는 모르겠다.
constraints는 제약조건이란 뜻이라 여럭지를 포괄하는 개념이구나
https://dev-coco.tistory.com/55