🚨 Trouble Shooting
프로젝트에서 매장의 메뉴를 추가하기 위해 서버측 코드를 수정하던 중 아래와 같은 에러가 발생했습니다.
Multer는 Node.JS에서 파일을 관리하는 모듈인데, 저는 이미지를 저장하기 위해 사용했습니다.
1차적으로 확인한건 multer를 사용하면서 선언한 변수들을 확인했습니다.
이미지를 저장하기 위한 NodeJs 서버의 로컬 저장소의 경로를 지정합니다.
// 디스크에 이미지 저장을 위한 multer 설정
const storeMainImageStorage = multer.diskStorage({
destination: '/store_images_volume/main',
filename: (req, file, cb) => {
const fileName = 'main_img' + Date.now() +'.'+ file.originalname.split('.').pop();
cb(null, fileName);
},
fileFilter : fileFilter,
//최대 30MB
limits: { fileSize: 30 * 1024 * 1024 }
});
Multer 인스턴스를 생성하고 위에서 생성 경로를 저장소로 지정합니다.
const uploadStoreMainImage = multer({ storage: storeMainImageStorage });
POST 요청을 받기 위한 로직을 만들어주었습니다.
// 새로운 매장 정보 저장
app.post("/db/store-registration", uploadStoreMainImage.single('storeMainImage'), (req, res) => {
console.log("post 요청이 수신 되었습니다.");
try {
const file = req.file;
const storeName = req.body.storeName;
const ceoName = req.body.ceoName;
const CRN = req.body.crn;
const contact = req.body.contact;
const address = req.body.address;
const latitude = req.body.latitude;
const longitude = req.body.longitude;
const kind = req.body.kind;
console.log("storeName : " + storeName);
if (file) {
const filePath = file.path;
connection.query(
'INSERT INTO STORE_INFO (CRN, STORE_NAME, IMAGE_PATH, CEO_NAME, CONTACT, ADDRESS, LATITUDE, LONGITUDE, KIND) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
[CRN, storeName, filePath, ceoName, contact, address, latitude, longitude, kind],
(err, results, fields) => {
if (!err) {
console.log("이미지가 성공적으로 저장되었습니다.");
res.status(200).send("이미지가 성공적으로 저장되었습니다.");
} else {
console.error("이미지 저장 실패: ", err);
res.status(200).send("이미지 저장 실패");
}
}
);
}
} catch (err) {
console.error(err);
res.status(500).send("오류 발생");
}
});
uploadStoreMainImage.single('storeMainImage')
- Multer 미들웨어에서 파일 업로드를 처리하는 방법을 지정하는 부분입니다.
- 하나의 파일만 업로드되고, 이 파일의 필드 이름이 'storeMainImage'임을 나타냅니다.
- 클라이언트가 서버에 이미지를 업로드할 때, 해당 이미지가 'storeMainImage'라는 필드 이름으로 전송되어야 합니다.
안드로이드의 서버 요청을 위한 Retrofit 인터페이스 입니다. 중요한 점은 서버의 필드 이름과 동일해야 합니다.
interface StoreInfoApi {
/**
* Multipart : 이미지, 오디오, 비디오 등과 같은 여러 종류의 데이터를 서버에 업로드하거나 전송할 때 사용
* 업로드할 데이터를 @Part 어노테이션을 사용해 파라미터로 지정
* */
@Multipart
@POST("/db/store-registration") // 서버 엔드포인트 URL, HTTP POST 요청
suspend fun storeRegistration(
@Part storeMainImage: MultipartBody.Part?, // 이미지 데이터를 나타내는 파라미터
@Part("storeName") storeName: RequestBody, // 매장명
@Part("ceoName") ceoName: RequestBody, // 점주명
@Part("crn") crn: RequestBody, // 사업자 등록 번호
@Part("contact") contact: RequestBody, // 전화번호
@Part("address") address: RequestBody, // 주소
@Part("latitude") latitude: RequestBody, // 위도
@Part("longitude") longitude: RequestBody, // 경도
@Part("kind") kind: RequestBody // 타입
): ResponseBody
// 서버로부터 받은 응답을 처리하기 위한 Retrofit Call 객체를 반환
// @Part로 나눠진 파라미터를 하나의 객체로 만들어 전송
위와 같이 코드를 작성하고 서버에서 지정한 이미지를 식별하기 위한 이름도 지정했는데 문제가 해결되지 않았습니다.
분명 문제는 서버측의 필드 이름과 일치하지 않아서 발생하는 문젠데 분명 현재 코드에는 문제가 없어 보였습니다.
httpLoggingInterceptor를 사용해 로그를 분석하다 이상한걸 발견했습니다. 서버에 전송될 때 필드 네임이 storeMainImage이 아닌 storeImage로 전송되는 것이었습니다!!
데이터를 레트로핏 인터페이스에 전달하기까지의 과정에서 storeImage로 전달받은 원인을 찾았습니다
이미지를 서버에 전송하기 위해 MultipartBody.Part 타입으로 변환하는 과정에서 필드 네임을 지정해주게 되는데
이 때 지정해 주는 필드 네임을 기준으로 서버에서 데이터를 받게 됩니다.
필드 네임을 서버에서 지정한 storeMainImage로 변경해 주었고 서버에 저장하는데 성공했습니다!
정말 생각치도 못한곳에서 에러가 발생했습니다. 뭔가 에러를 수정하는 과정에서 시야가 너무 좁았다는 생각이 들어서 앞으로는 좀 더 넓게 볼 수 있게 노력해야겠습니당~
'PROJECT' 카테고리의 다른 글
안드로이드 클린 아키텍처 도메인 레이어 설계 (0) | 2024.07.13 |
---|---|
[PROJECT] Fused Location Provider (0) | 2024.01.28 |
[PROJECT] 프로젝트에 DataBinding & @BindingAdapter 사용해보기 (3) | 2024.01.24 |
[PROJECT]프로젝트 리팩토링 (0) | 2024.01.16 |