back-end/🐾 삼냥이즈 트러블슈팅

[삼냥이즈 트러블슈팅] Typeorm Findone()

삼냥이즈 기술 블로그 2025. 4. 24. 01:27

이슈

현재 클라이언트는 서버로부터 access 토큰 및 refresh 토큰을 발급 받고, 시간이 지나 토큰이 만료되면 서버에 저장된 토큰 데이터를 이용해 재발급 받는 방식이다. 개발을 하던 와중 창준이 계정의 토큰이 만료된 후 다시 접속하면 돈이나 경험치, 장비 등의 개수 등이 서버에 있는 값과 다르게 전달되고, 결제 등을 하면 창준이 계정의 인벤토리가 아닌 내 계정의 인벤토리 정보가 수정되는 것을 발견하였다. 테스트 결과 access token이 만료되어 재발급 하는 과정에서 refresh token 을 비워서 보내면 뜬금없이 내 계정에 대한 JWT 토큰이 발급되는 것을 확인하였다.

 

원인

  async findByRefreshToken(refreshToken: string): Promise<RefreshToken | null> {
    return await this.findOne({ where: { refresh_token: refreshToken } });
  }

클라이언트가 refresh token을 전송하면 그 정보를 바탕으로 위 메서드를 이용해 해당 토큰이 어떤 유저의 토큰이고 언제 만료되어야 하는지에 대한 로우를 가져온다. 이때 만일 refreshTokenundefined 값이 들어가면 해당 조건 절이 무시되어 테이블의 가장 첫번째 로우를 반환한다. 공교롭게도 해당 로우는 내 계정이었고, 이 때문에 서버를 개발하면서 내 계정으로 서버 api가 잘 작동하는지 테스트 했을 때는 문제를 찾을 수 없었다.

 

해결 과정

위 경우를 포함해 모든 findOne() 메서드에 대해 조건절이 undefined가 넘어갈 수 없도록 검사하도록 수정했다.


새로 알게 된 것

FineOne의 문제점

await this.userRepository.findOne(
    { where:
        { email: 'example@sam-meows.com' }
    }
);

findOne() 메서드는 인자로 where 구문을 이용해 검색 조건을 설정할 수 있다.
이때 검색 조건 뒤에 오는 값이 undefined인 경우 해당 값이 null인 값 등을 찾는 것이 아닌 구문 자체가 무시된다.
즉 where절 자체가 무시되고, 단순히 테이블의 첫 번째 로우를 가져오는 함수로 바뀌게 된다.

따라서 조건이 undefined이 아닌지 체크 후 사용하거나 find() 메서드에 take: 1 인자를 주어 이용하여야 한다.