인프런 커뮤니티 질문&답변

Like me black님의 프로필 이미지

작성한 질문수

[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지

시퀄라이즈 모델 생성 질문드립니다

해결된 질문

22.05.15 14:38 작성

·

937

0

강사님 안녕하세요
항상 좋은 강의 들려주셔서 감사합니다

강의를 수강하며 코드를 따라해보니 실행도 잘되고 테이블도 잘 생성되었습니다

 

 

그런데 제가, 코드를 변형하고 싶었습니다
user.js에 기본키 역할을 하는 email 컬럼(필드)을 넣고 싶었습니다

기본키로 id가 자동 생성된다고 말씀해주셨는데,

저는 email 컬럼(필드)를 만들어서 기본키 역할을 하게 만들고 싶었습니다


그래서 추가했더니 에러가 생겨서 혹시 어디가 문제인지 봐주시면 정말 감사하겠습니다
(스택오버플로우를 찾아봐도 아직 실력이 부족해서 원하는 해답을 찾기 어려웠습니다)

 

1단계. 워크 벤치에서 테이블을 다시 삭제했습니다

 

2단계는 user.js를 아래처럼 고쳤습니다

 

3단계는 comment.js를 고쳤습니다

 

4단계 app.js를 실행했습니다

그러자 에러가 생겼습니다

 

콘솔창 메시지를 그대로 복붙했습니다

C:\Program Files\nodejs\node.exe .\app.js

3001 번 포트에서 대기 중

Executing (default): CREATE TABLE IF NOT EXISTS `users` (`email` VARCHAR(30) , `name` VARCHAR(20) NOT NULL UNIQUE, `age` INTEGER UNSIGNED NOT NULL, `married` TINYINT(1) NOT NULL, `comment` TEXT, `created_at` DATETIME NOT NULL, PRIMARY KEY (`email`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci;

Executing (default): SHOW INDEX FROM `users` FROM `nodejs`

Executing (default): CREATE TABLE IF NOT EXISTS `comments` (`id` INTEGER NOT NULL auto_increment , `comment` VARCHAR(100) NOT NULL, `created_at` DATETIME, `commenter` VARCHAR(30), PRIMARY KEY (`id`), FOREIGN KEY (`commenter`) REFERENCES `users` (`email`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;

Error

    at Query.run (c:\실험\learn_sequelize\node_modules\sequelize\lib\dialects\mysql\query.js:52:25)

    at c:\실험\learn_sequelize\node_modules\sequelize\lib\sequelize.js:313:28

    at processTicksAndRejections (node:internal/process/task_queues:96:5)

    at async MySQLQueryInterface.createTable (c:\실험\learn_sequelize\node_modules\sequelize\lib\dialects\abstract\query-interface.js:94:12)

    at async Function.sync (c:\실험\learn_sequelize\node_modules\sequelize\lib\model.js:937:5)

    at async Sequelize.sync (c:\실험\learn_sequelize\node_modules\sequelize\lib\sequelize.js:377:9) {name: 'SequelizeDatabaseError', parent: Error: Referencing column 'commenter' and refe…lumn 'email' in foreign key constraint 'comm…, original: Error: Referencing column 'commenter' and ref…umn 'email' in foreign key constraint 'comm…, sql: 'CREATE TABLE IF NOT EXISTS `comments` (`id` IN…LT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;', parameters: {…}, …}


아래는 전체 소스코드 입니다

app.js 소스코드입니다

const express = require('express');
const path = require('path')
const morgan = require('morgan')
const nunjucks = require('nunjucks');

const { sequelize } = require('./models')

const app = express();
app.set('port', process.env.PORT || 3001);
app.set('view engine', 'html');
nunjucks.configure('views', {
    express: app,
    watch: true,
});
sequelize.sync({force: false})
    .then(()=>{
        console.log('데이터베이스 연결 성공');
    })
    .catch((err)=>{
        console.error(err);
    });

app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
    
// app.use('/', indexRouter);
// app.use('/users', usersRouter);
// app.use('/comments', commentsRouter);
    
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');
});// 
    
app.listen(app.get('port'), () => {
    console.log(app.get('port'), '번 포트에서 대기 중');
}); //3001번 포트 대기 중


user.js 소스코드입니다

/**
 * ./models/user.js
 */

 const Sequelize = require('sequelize');

 module.exports = class User extends Sequelize.Model {
   static init(sequelize) {
     return super.init({
      email: {
        type: Sequelize.STRING(30),
        primaryKey: true,
       },
       name: {
         type: Sequelize.STRING(20),
         allowNull: false,
         unique: true,
       },
       age: {
         type: Sequelize.INTEGER.UNSIGNED,
         allowNull: false,
       },
       married: {
         type: Sequelize.BOOLEAN,
         allowNull: false,
       },
       comment: {
         type: Sequelize.TEXT,
         allowNull: true,
       },
       created_at: {
         type: Sequelize.DATE,
         allowNull: false,
         defaultValue: Sequelize.NOW,
       },
     }, {
       sequelize,
       timestamps: false,
       underscored: false,
       modelName: 'User',
       tableName: 'users',
       paranoid: false,
       charset: 'utf8',
       collate: 'utf8_general_ci',
     });
   }
 
   static associate(db) {
     db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'email' });
   }
 };
 

comment.js 소스코드입니다

/**
 * ./models/comment.js
 */

 const Sequelize = require('sequelize');

 module.exports = class Comment extends Sequelize.Model {
   static init(sequelize) {
     return super.init({
       comment: {
         type: Sequelize.STRING(100),
         allowNull: false,
       },
       created_at: {
         type: Sequelize.DATE,
         allowNull: true,
         defaultValue: Sequelize.NOW,
       },
     }, {
       sequelize,
       timestamps: false,
       modelName: 'Comment',
       tableName: 'comments',
       paranoid: false,
       charset: 'utf8mb4',
       collate: 'utf8mb4_general_ci',
     });
   }
 
   static associate(db) {
     db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'email' });
   }
 };
 
 

index.js 소스코드입니다

const Sequelize = require('sequelize');
const User = require('./user');
const Comment = require('./comment');

const env = process.env.NODE_ENV || 'development'; //환경변수 NODE_ENV 또는 'development' 로 하겠다

const config = require(__dirname + '/../config/config.json')[env];
const db = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

db.sequelize = sequelize;
db.Sequelize = Sequelize;

db.User = User;
db.Comment = Comment;

User.init(sequelize);
Comment.init(sequelize);

User.associate(db);
Comment.associate(db);

module.exports = db;

 

구글링을 해봐도 원인을 잘 모르겠어서 가르쳐주시면 정말 감사하겠습니다

읽어주셔서 감사합니다

답변 1

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 15:35

에러메시지가 짤려서 안 보이는데요. 저기 나오는 SQL을 순서대로 워크벤치에 입력한 후에 에러메시지 전체를 확인해봐야할 것 같습니다.

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 16:00

답변해주셔서 감사합니다
워크벤치를 확인해보니, users 테이블은 생성이 되었습니다
comments 테이블은 생성되지 않았습니다

제 생각에는
부모 테이블을 만든 다음 외래키를 갖는 자식 테이블을 생성하는 순서니까
부모테이블인 users 테이블부터 먼저 만들어졌으니 순서는 맞는 것 같았습니다

에러메시지를 봤습니다


여기서 파란색 표시된 에러 메시지를 스택오버플로우에 검색해봤습니다
컬럼이 호환되지 않는다고 합니다
mysql - 오류 번호 : 3780 외래 키 제약 조건 '%s'에서 열 '%s'와 참조 열 '%s'를 참조하는 것은 호환되지 않습니다 - 스택 오버플로 (stackoverflow.com)

그런데, 저는 이 원인이 좀 의아한 게 아래 사진을 봐주시면

comments.js의 commenter 컬럼을

user.js의 email 컬럼을 참고하는 외래키로 만들어주긴 했지만,
commenter의 type을 따로 세팅해주지도 않았는데, 왜 호환성 문제가 생긴건지 모르겠습니다


혹시 제가 놓친 부분을 가르쳐주시면 정말 감사하겠습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 16:19

아.. 해결했습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 16:20

commenter도 직접 데이터타입을 지정해줬습니다

그러자, 잘 해결되었습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 16:23

혹시 이렇게 이해해도 되는지 질문드리고 싶습니다
위의 사진처럼 commenter 컬럼 따로 이렇게 만들어주지 않고

 static associate(db) {
     db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'email' });

 } 

이렇게 belongsTo 문 안에만 commenter 외래키를 만들어주면
commenter 컬럼의 데이터타입의 디폴트값이,
시퀄라이즈가 자동으로 만들어주는 id 컬럼과 같은 int 타입이 되기 때문에
호환성 문제가 생긴 것이라는 결론을 내렸습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 16:25

이렇게 접근하지 말라고 하셔서, 안그럴려고 해도 에러를 해결하려고 계속 파다보니 이런 식으로 접근을 하게 되었습니다..

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 16:27

공식문서를 찾아보는 연습을 하셔야합니다. 그래야지만 추론을 안 할 수 있습니다. 검색 시에도 공식문서도 검색하세요.

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 16:32

네 알겠습니다..
먼저 익스프레스 공식문서부터 찾아봤었는데 시퀄라이즈가 안나와서요


시퀄라이즈 공식문서도 찾아봤는데, 여기는 검색창이 안보여서.. 어떤 목록을 찾아야 하는지 모르겠어서
Sequelize v6 | Sequelize

결국 스택오버플로우에서 다른 분들 예시보고 추론을 하게 된 것 같습니다
그래도 공식문서 위주로 찾는 연습을 해볼게요

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 16:33

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 16:34

외래키가 정수가 아닐때 하는 방법입니다. 요즘은 검색이 곧 실력이라 검색 능력도 많이 키우셔야 합니다.

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 16:39

소중한 시간 내어 도와주셔서 감사합니다
학생일 때도 중요하지만, 나중에 현업자가 되면, 스스로 공식문서를 찾는 실력이 더 중요해질텐데, 좋은 조언을 해주신다고 생각하고 계속 연습하겠습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 17:28

선생님 저도 제가 말도안된다고 생각하는데요
데이터 타입 맞췄고, 해결했는데, 재부팅 후 다시 실행해보니 똑같은 에러가 생겨서요
그런 문제가 있으신 적이 없으셨죠..?

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 17:29

30분째 고민 중인데 이게 말도 안된다고 생각해서, associate 문만 지워보니, 이러면 두 테이블 다 잘 생겨서요.. 아무리 생각해도 같은 문제 같은데, 타입을 맞춰서요

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 17:57

도대체 이게 어떻게..

공식문서 찾아보겠습니다..

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:14

여기에 답이 있나요..? 도저히 모르겠습니다



외래키가 문자열 타입이라서


이 부분을 변형하면 되는 건지 질문드리고 싶습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:17

공식문서처럼 이렇게 바꿨더니



이번엔 아예 다른 오류가 생깁니다 ㅠㅠ

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 18:24

제가 공식문서서 보여드린건 foreignKey type입니다. 포린키 타입을 스트링으로 강제지정할 수 있습니다.

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 18:25

그리고 이미 기존에 인덱스가 정수로 생성되어버린 경우는 테이블을 수정하셔야 합니다.

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:27

테이블 아예 전부 다 지우고 시도했습니다 선생님 ㅠㅠ

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:27

user.js 입니다

/**
 * ./models/user.js
 */

 const Sequelize = require('sequelize');

 module.exports = class User extends Sequelize.Model {
   static init(sequelize) {
     return super.init({
      email: {
        type: Sequelize.STRING(30),
        primaryKey: true,
       },
       name: {
         type: Sequelize.STRING(20),
         allowNull: false,
         unique: true,
       },
       age: {
         type: Sequelize.INTEGER.UNSIGNED,
         allowNull: false,
       },
       married: {
         type: Sequelize.BOOLEAN,
         allowNull: false,
       },
       comment: {
         type: Sequelize.TEXT,
         allowNull: true,
       },
       created_at: {
         type: Sequelize.DATE,
         allowNull: false,
         defaultValue: Sequelize.NOW,
       },
     }, {
       sequelize,
       timestamps: false,
       underscored: false,
       modelName: 'User',
       tableName: 'users',
       paranoid: false,
       charset: 'utf8',
       collate: 'utf8_general_ci',
     });
   }
 
    static associate(db) {
      db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'email' });
    }
 };
Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:27

comment.js 입니다 ㅠㅠ

/**
 * ./models/comment.js
 */

 const Sequelize = require('sequelize');

 module.exports = class Comment extends Sequelize.Model {
   static init(sequelize) {
     return super.init({
      commenter: {
        type: Sequelize.STRING(30),
       },
       comment: {
         type: Sequelize.STRING(100),
         allowNull: false,
       },
       created_at: {
         type: Sequelize.DATE,
         allowNull: true,
         defaultValue: Sequelize.NOW,
       },
     }, {
       sequelize,
       timestamps: false,
       modelName: 'Comment',
       tableName: 'comments',
       paranoid: false,
       charset: 'utf8mb4',
       collate: 'utf8mb4_general_ci',
     });
   }
 
  static associate(db) {
      db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'email' });
    }
 };
Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:28

테이블 전부 다 지운 상태에서 index.js comment.js user.js 모두 associate문만 주석처리하면
실행이 아주 잘됩니다 2개의 테이블이 잘 생성되었습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:29

다시 테이블을 전부 지우고 

주석처리했던 associate문을 주석해제하고 실행하면
동일한 에러가 생깁니다..

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:31

아무리 생각해도 associate 소스코드가 문제인 것 같은데, 도저히 모르겠습니다
주말인데, 이렇게 힘들게 해드려서 죄송합니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:34

마지막으로 index.js 소스코드입니다

const Sequelize = require('sequelize');
const User = require('./user');
const Comment = require('./comment');

const env = process.env.NODE_ENV || 'development'; //환경변수 NODE_ENV 또는 'development' 로 하겠다
console.log(process.env.NODE_ENV)
console.log(env)
const config = require(__dirname + '/../config/config.json')[env];
const db = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

db.sequelize = sequelize;
db.Sequelize = Sequelize;

db.User = User;
db.Comment = Comment;

User.init(sequelize);
Comment.init(sequelize);

User.associate(db);
Comment.associate(db);

module.exports = db;
제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 18:40

제가 공식문서랑 뭐가 문제인지 다 알려드렸는데 수정이 안 되시나요??

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 18:40

const { DataTypes } = require("Sequelize");

 

Foo.hasOne(Bar, {

  foreignKey: {

    // name: 'myFooId'

    type: DataTypes.UUID

  }

});

Bar.belongsTo(Foo);

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 18:49

제가 그걸 보고이렇게 바꿨는데, 잘못바꿨나봐요

생각해보니 이렇게 변경한 것은 모듈을 안가져온 것부터 잘못 바꾼 거네요

다시 여러가지로 해석보면서 바꿔볼게요
이것도 좀 스스로 해봐야 그 다음에 공식문서 코드 보고 제 코드에 적용시킬 때 실력이 늘 것 같네요
정말 감사합니다
도저히 모르겠으면 다시 질문드릴게요

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 18:53

아뇨.. type이 핵심입니다... 제가 코드에 type을 적어드렸는데 왜 자꾸 name만 적으시나요. foreignKey: { name: 'commenter', type: Sequelize.STRING(30) }

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 18:58

아, 그리고 지금 charset, collation이 테이블 둘이 다릅니다. 문자열에서는 이 둘이 중요해서 두 테이블이 같아야할 것 같습니다. 테이블 지우고 다시 만들어보세요.

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 19:00

이렇게 수정하는 것도 아닌 것 같아서 다른 방법을 시도하겠습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 19:03

이번에는

DataTypes.UUID

DataTypes.Sequelize.STRING(30)

로 변경해봤는데 실패했습니다

다른 방법을 시도하겠습니다

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 19:04

앗 댓글 2개나 달아주신 걸 지금 봤습니다 ;;
지금 다시 말씀해주신 대로 바꿔볼게요!

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2022. 05. 15. 19:09

참고로 Sequelize랑 DataTypes랑 호환됩니다. DataTypes.UUID, Sequelize.UUID

DataTypes.STRING(30), Sequelize.STRING(30)

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 19:12

선생님 이렇게 주말에도 저를 도와주셔서 정말 감사드립니다
해결했습니다
정말 고맙습니다..

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 19:14

댓글 달아주신 걸 지금 봤네요 가르쳐주셔서 고맙습니다 선생님

Like me black님의 프로필 이미지
Like me black
질문자

2022. 05. 15. 19:22

그리고 오늘 같은 문서, 같은 해설, 같은 코드를 봐도 해석하고 적용하는 게 이렇게 이해하는 깊이의 차이가 다를 수 있다는 걸 많이 느꼈습니다..
선생님과 저는 하늘과 땅 차이니까 당연한 것이지만,
한편으로는 정답을 보여주셨는데도 적용을 못해서 저의 부족함을 많이 느꼈네요

그리고 또 한편으로는 이런 코드를 저 스스로 해석하고 적용하는 연습을 할 수 있도록 훈련시켜주셔서 고맙습니다 정말 고생 많으셨습니다