쾌락코딩

express.js에서 class 메서드를 routing할때 bind

|

express.js로 로컬 로그인을 구현 하던 도중 나의 멍청함으로 인한 시간낭비가 발생했다. 이를 해결하기 위한 공부를 하며 아주 조금 성장했지만 일단 적어놓고 똑같은 실수를 반복하지 말자.

일반 적으로 class를 만들고 클래스 메서드를 사용할 때

class A {
  say () {
    console.log('hello');
  }

  func () {
    this.say();
  }
}

const a = new A();
a.func(); // 'hello'

클래스 A의 func 안에 있는 this는 a다(a가 호출했으니).

내가 했던 멍청한 짓은 아래와 같았다.

// auth/AuthCtrl.js
class AuthCtrl {
  isPasswordMatch (plainPassword, hashedPasswod) {
    const result: boolean = compareSync(plainPassword, hashedPasswod);
    return result;
  }
  loginLocalAccount (req, res) {
    //bla bla bla
    const result = this.isPasswordMatch(plainPassword, hashedPasswod);
  }
}
// auth/index.js
...
const auth = express.Router();
const authCtrl = new AuthCtrl();
...
auth.post('/login/local', authCtrl.loginLocalAccount); // 여기가 문제

export default auth;

나는 /login/local 로 접속하면 당연히 정상 실행 될 줄 알았는데, loginLocalAccount의 isPasswordMatch가 전혀 실행되지 않았다. 디버깅을 해보니 this가 undefind로 출력됨을 알 수 있었다. 분명히 const authCtrl = new AuthCtrl(); 이렇게 객체를 생성하고 authCtrl.loginLocalAccount 이렇게 했는데 왜 이럴까?

우선 authCtrl.loginLocalAccount는 함수를 호출한 게 아니다. 만약 authCtrl.loginLocalAccount() 이렇게 했으면 loginLocalAccount의 this가 authCtrl이 되었겠지만 그 상황이 아니라 함수만 넘겨주는 상황이었다. 즉 authCtrl안에 있는 loginLocalAccount함수 자체만 express가 실행하게끔 넘겨준 것이다. (그럼 왜 this가 express 객체가 아니라 undefind인지는 모르겠다.) 따라서 이 라우터가 실행할 loginLocalAccount함수 안의 this는 authCtrl이 아닌 것! 그럼 해결방법은?

바로 Function.prototype.bind함수이다.

auth.post('/login/local', authCtrl.loginLocalAccount.bind(authCtrl));

이렇게 하면 이 라우팅에서 실행되는 loginLocalAccount함수 안의 this는 모두 authCtrl을 가르키게 된다!

Comments