React Query에서 클래스 메서드를 queryFn으로 사용할 때 주의사항
문제 상황
React Query를 사용하다가 다음과 같은 에러를 만났습니다:
Cannot read properties of undefined (reading 'get')
코드는 다음과 같았습니다:
// queries.ts
export const useUserQuery = () => {
return useQuery({
queryKey: ['user'],
queryFn: api.getUser, // 이 부분이 문제!
});
};
// apis.ts
class ApiClient extends BaseApiClient {
public getUser() {
return this.get<User>("/api/auth/user");
}
}
기본적인 문제죠..? 바로 this, 화살표함수 문제가 딱 떠올라야 하겠죠..?
하지만 저는 오늘도 바로 생각해내지 못했답니다. 헛공부...
원인: JavaScript의 this 바인딩 손실
JavaScript에서 메서드를 다른 곳에 전달할 때, this 컨텍스트가 손실됩니다.
const api = new ApiClient();
// 직접 호출: this가 api 인스턴스를 가리킴
api.getUser(); // ✅ 정상 작동
// 메서드를 변수에 할당: this 컨텍스트 손실
const getUserFn = api.getUser;
getUserFn(); // ❌ this는 undefined
React Query의 queryFn
에 메서드를 전달하면, React Query가 나중에 그 함수를 호출할 때 원래의 인스턴스 컨텍스트 없이 호출하게 됩니다. 따라서 this.get
을 찾을 수 없게 되는 것입니다.
해결 방법 1: 화살표 함수로 변경 (그냥 이걸로 하면 되는 것...)
화살표 함수는 렉시컬 스코프를 사용하여 정의될 당시의 this
를 영구적으로 바인딩합니다.
// apis.ts
class ApiClient extends BaseApiClient {
// 일반 메서드 대신 화살표 함수로 정의
public getUser = () => {
return this.get<User>("/api/auth/user");
};
public logOut = () => {
return this.get("/api/auth/logout");
};
}
const api = new ApiClient();
export default api;
// queries.ts
export const useUserQuery = () => {
return useQuery({
queryKey: ['user'],
queryFn: api.getUser, // ✅ 이제 안전하게 사용 가능!
});
};
해결 방법 2: 래퍼 함수 사용
화살표 함수로 감싸서 명시적으로 컨텍스트를 유지할 수도 있습니다:
export const useUserQuery = () => {
return useQuery({
queryKey: ['user'],
queryFn: () => api.getUser(), // 화살표 함수로 감싸기
});
};
하지만 이 방법은 모든 사용처에서 매번 래핑해야 하므로 번거롭습니다.
그리구.. tanstack 의 client 측 공식 가이드와도 다름
해결 방법 3: bind 사용
생성자에서 메서드를 바인딩할 수도 있습니다:
class ApiClient extends BaseApiClient {
constructor() {
super(config);
this.getUser = this.getUser.bind(this);
this.logOut = this.logOut.bind(this);
}
public getUser() {
return this.get<User>("/api/auth/user");
}
}
하지만 메서드가 많아질수록 관리가 어려워집니다.
결론
React Query의 queryFn
이나 콜백으로 클래스 메서드를 전달할 때는 화살표 함수로 정의하는 것이 가장 안전하고 깔끔한 방법입니다. 화살표 함수는 정의 시점의 this
를 영구적으로 캡처하므로, 어디서 호출되든 항상 올바른 컨텍스트를 유지합니다.
일반 메서드 vs 화살표 함수
class Example {
// ❌ 일반 메서드: this 컨텍스트가 호출 시점에 결정
public method() {
return this.something;
}
// ✅ 화살표 함수: this 컨텍스트가 정의 시점에 고정
public arrowMethod = () => {
return this.something;
}
}
이 패턴은 React Query뿐만 아니라 이벤트 핸들러, setTimeout, Promise 콜백 등 메서드를 전달하는 모든 상황에서 유용하게 사용할 수 있습니다.
'TIL' 카테고리의 다른 글
[251016 TIL] 테스트 환경 구축(vitest, rtl, msw, playwright) (0) | 2025.10.16 |
---|---|
[251015 TIL] 문자인증(알리고, with GQL) (0) | 2025.10.15 |
[251009 TIL] Hasura 트랜잭션 처리 (1) | 2025.10.09 |
[251009 TIL] PostgREST? (supabase vs hasura) (0) | 2025.10.09 |
[240421 WIL 1주차] 웹디자인, github, env (0) | 2024.04.21 |