
AxiosError: Network Error - 안드로이드에서 FormData를 사용한 이미지 업로드 시 네트워크 오류 발생
여러 가지 해결 방법 시도:
서버 및 로직 확인: Swagger/Postman과 iOS 시뮬레이터에서는 동일한 로직으로 업로드에 성공
-> 이를 통해 백엔드 API와 기본적인 FormData 구성 로직 자체에는 문제가 없음을 확인
클라이언트 측 시도: 안드로이드 환경에서 아래와 같은 여러 방법을 시도했으나 모두 동일한 Network Error를 반환했습니다.
이 과정들을 통해, 문제는 코드의 명시적인 버그가 아닌 안드로이드 환경과 axios 라이브러리가 FormData를 처리하는 방식의 차이 때문에 발생한다고 결론
formData.append('file', {
uri: fileUri,
type: 'image/jpeg',
name: fileName,
} as unknown as Blob);
headers: {
'Content-Type': 'multipart/form-data'
}
const fileUri = Platform.OS === 'android'
? uri.replace('file://', '')
: uri;
const formData = new FormData();
formData.append('file', {
uri: image.uri,
type: image.type || 'image/jpeg', // 정확한 MIME 타입 명시
name: image.fileName || 'upload.jpg',
});
// 공통 API 요청 함수 예시
const uploadImageApi = (formData: FormData) => {
return apiClient.post('/file/upload', formData, {
// 1. FormData를 그대로 반환하여 axios의 자동 변환을 막음
transformRequest: (data, headers) => {
return data;
},
// 2. headers에 content-type을 명시하되, boundary는 axios가 자동 생성
headers: {
'Content-Type': 'multipart/form-data',
},
});
};
재사용 가능한 커스텀 훅
→ React Query(@tanstack/react-query)와 결합하여 재사용 가능한 파일 업로드 훅
// useImageUpload.ts
import { useMutation } from '@tanstack/react-query';
import { apiClient } from '@/shared/api'; // 설정된 axios 인스턴스
import { PHOT_BASE_URL } from '@/shared/api/model';
// API 요청 함수
const uploadImageApi = async (formData: FormData): Promise<string> => {
const response = await apiClient.post<string>('/file/upload', formData, {
transformRequest: (data) => data,
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
};
// 파일 업로드를 위한 커스텀 훅
export const useImageUpload = () => {
const mutation = useMutation({
mutationFn: uploadImageApi,
});
const uploadImage = async (uri: string, metadata?: TImageMetadata): Promise<string> => {
// 이미 서버 URL인 경우(수정하지 않은 이미지) 그대로 반환
if (!uri.startsWith('file://') && !uri.startsWith('content://')) {
return uri;
}
const formData = new FormData();
formData.append('file', {
uri,
type: metadata?.type ?? 'image/jpeg',
name: metadata?.fileName ?? 'image.jpg',
});
// React Query의 mutateAsync를 사용해 업로드 실행 및 결과 반환
const resultFromServer = await mutation.mutateAsync(formData);
return PHOT_BASE_URL + resultFromServer;
};
return { uploadImage, isUploading: mutation.isPending };
};