본문 바로가기

React

이슈 7 node-sqlite3에서 this.changes / this.lastID 사용하기

반응형

DB update가 성공했는지 확인하기 위해 node-sqlite3의 Database.run에서 제공하는 this.changes를 사용하려 한다.

TL; DR

화살표 함수 대신 함수 선언문을 사용하자.

문제 확인

아래와 같이 코드를 작성하면,

db.run(`
    UPDATE your_table_name
    SET value1=(?), value2=(?)
    WHERE id=(?) AND created=date('now', 'localtime');
  `,
  [value1, value2, id],
  (err) => {
    if (err)
      console.error(err.message);

    handler({
      status: (this.changes === 1) ? "modify_success" : "modify_failed"
    })
  }
);

아래와 같은 에러가 발생한다.

Uncaught TypeError: Cannot read property 'changes' of undefined
    at Statement.eval (webpack-internal:///./src/db/db.js:180:20)

this가 undefined라는 소리인데, 분명 명세서에는 맞게 적혀있는데?? sqlite3의 소스코드에 직접 접근해봤다. (db.run을 cmd + click하면 바로 코드를 확인할 수 있다.) 모듈은 typescript로 작성되어 있다. callback이 RunResult 타입의 this를 변수로 받는 걸 확인할 수 있다.

export class Database extends events.EventEmitter {
    ...

  run(sql: string, callback?: (this: RunResult, err: Error | null) => void): this;
  run(sql: string, params: any, callback?: (this: RunResult, err: Error | null) => void): this;
  run(sql: string, ...params: any[]): this;

    ...
}

맞는데??? 왜 안될까?

문제의 원인 = (err) => { ... }

내 지식이 짧기도 하지만, 라이브러리 설계의 문제이기도 하다. ES6부터 제공되는 화살표 함수는 this를 정의하지 않는다. 대신 코드 바깥의 함수에서 사용하는 this 값을 그대로 사용한다.

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/애로우_펑션

 

화살표 함수

화살표 함수 표현(arrow function expression)은 function 표현에 비해 구문이 짧고  자신의 this, arguments, super 또는 new.target을 바인딩 하지 않습니다. 화살표 함수는 항상 익명입니다. 이  함수 표현은 메소드 함수가 아닌 곳에 가장 적합합니다. 그래서 생성자로서 사용할 수 없습니다.

developer.mozilla.org

정확한 내용은 MDN의 화살표 함수 문서에서 바인딩 되지 않은 this를 참고하면 좋을 듯하다.

  1. webpack이 ES6를 strict mode로 컴파일한다.
  2. 화살표 함수 속 this는 lexical, 정적 변수가 되어버린다.
  3. this 값을 덮어쓸 수 없다.

위 과정을 거쳐 에러가 발생하는 것이다.

문제 해결

문제를 해결하기 위해 화살표 함수를 함수 선언문으로 변경하였다.

이미 Git repository에 해당 문제에 대한 이슈가 등록되어 있지만, 2015년에 올라온 이슈가 아직까지 Open 된 상태라 고쳐질 가능성은 없다고 봐야하는 듯. 대안으로 mozilla에서 만든 promise-sqlite를 제시한 사람이 있다.

이슈: https://github.com/mapbox/node-sqlite3/issues/560

 

TypeError on ECMAScript 6 arrow function as a callback for db#run() · Issue #560 · mapbox/node-sqlite3

I know, this is not a bug ;) the code below: db.run(..., (err) => { ... }); throws error: Uncaught TypeError: Cannot read property 'changes' of undefined at Statement. (...

github.com

대안: https://github.com/mozilla/promise-sqlite

 

mozilla/promise-sqlite

A trivial Promise wrapper for Node's sqlite. Contribute to mozilla/promise-sqlite development by creating an account on GitHub.

github.com

 

도움이 된 링크

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/애로우_펑션

 

화살표 함수

화살표 함수 표현(arrow function expression)은 function 표현에 비해 구문이 짧고  자신의 this, arguments, super 또는 new.target을 바인딩 하지 않습니다. 화살표 함수는 항상 익명입니다. 이  함수 표현은 메소드 함수가 아닌 곳에 가장 적합합니다. 그래서 생성자로서 사용할 수 없습니다.

developer.mozilla.org

https://github.com/mapbox/node-sqlite3/issues/560

 

TypeError on ECMAScript 6 arrow function as a callback for db#run() · Issue #560 · mapbox/node-sqlite3

I know, this is not a bug ;) the code below: db.run(..., (err) => { ... }); throws error: Uncaught TypeError: Cannot read property 'changes' of undefined at Statement. (...

github.com

https://zepeh.tistory.com/394

 

node.js에서 sqlite 사용 중 콜백으로 last insert id를 받아오지 못하는 경우

let db = new sqlite3.Database('C:/Users/user/test.db', sqlite3.OPEN_READWRITE); db.run('INSERT INTO Members(name, email, ip) VALUES(?, ?, ?)', ['1', '2', '3'], (err)=>{ console.log(this.lastID); });..

zepeh.tistory.com

맨 마지막 블로그에서 답을 얻고 더 찾다보니 위 이슈와 MDN 레퍼런스를 찾았다. 위 글을 쓴 이유는 해당 블로그 글에서는 원인을 설명해주지 않기 때문.

반응형