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는 undefinedReact 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 |