프로젝트에서 유저와 프로젝트에 사용할 이미지를 저장하기 위한 이미지 서버를 팀 동료가 구축했다.
이미지 서버에서 Post요청과 Delete요청으로 이미지 파일을 관리할 수 있게 구축했기에
이제는 FE에서 이미지 파일을 관리할 수 있는 API를 개발하면 된다.
파일 자체를 서버로 보내는것은 개발 공부를 하면서 처음 해보는 것이기에 조사를 해봤다.
- 이미지 파일을 자바스크립트 단에서 변형없이 비동기로 제출하려면 form-data 객체를 이용해야한다.
- form-data 객체는 간단히 문자열화 할 수 없기에 console.log와 같은 방법으로는 확인이 어렵다.
불가능은 아니다. get이나 getAll을 이용하거나 객체를 순회하면 값을 읽을 수 있다. - 이미지 파일을 전송할 때 이미지는 base64, buffer, 2진 data 형식으로 전송가능하다.
이번에는 base64인코딩 형식으로 전송했다.
이렇게 조사를 한 뒤 API 개발을 시작하고 시험을 했는데,
........
역시 한방에 되는 법은 없나보다.
문제가 발생했다.
처음에는 단순 cors에러인줄 알았지만
서버에서 cors 허용을 한뒤에도 다른 에러가 발생했다.
이 문제는 이미지 파일을 resizing 하는 과정에서 발생했다.
export const resizingImg = async (imgFile: File, maxSizeMB: number, maxWidthOrHeight: number) => {
const options = {
maxSizeMB: maxSizeMB,
maxWidthOrHeight: maxWidthOrHeight
}
try {
if (!(imgFile instanceof Blob) || !(imgFile instanceof File)) {
return 'instance error';
}
if (!imgFile.type.startsWith('image/')) {
return 'fileType error';
}
const compressedFile = await imageCompression(imgFile, options);
const promise = imageCompression.getDataUrlFromFile(compressedFile);
return await promise;
}
catch(e) {
console.log(e);
}
}
위의 코드는 이미지 파일을 받아 지정한 크기와 너비/높이로 이미지를 resizing해주는 코드이다.
문제는 try의 마지막에 있었다.
지금 보면 return 값이 promise라는 값인데, 이 값은 resizing된 이미지 파일의 파일명을 의미한다.
즉, 파일 그 자체를 보내야되지만 나는 파일명만 form-data객체에 넣고 이미지 서버에 보낸 것이다.
서버에서 이미지 파일을 받아 동작하기에 당연히 파일명만을 보내 에러가 발생한 것이였다.
위의 코드를 다시 손본 결과 아래의 코드로 바뀌게 되었다.
export const resizingImg = async (imgFile: File, maxSizeMB: number, maxWidthOrHeight: number) => {
const options = {
maxSizeMB: maxSizeMB,
maxWidthOrHeight: maxWidthOrHeight
}
if (!(imgFile instanceof Blob) || !(imgFile instanceof File)) {
return 'instance error';
}
if (!imgFile.type.startsWith('image/')) {
return 'fileType error';
}
const compressedFile = await imageCompression(imgFile, options);
const promise = await imageCompression.getDataUrlFromFile(compressedFile);
return {
'file': compressedFile,
'fileName': promise
}
}
이렇게 바뀐 함수는 file과 fileName을 결과값을 반환해줬다.
위와같이 나누어 반환한 이유는 file은 form-data객체로 감싸 서버에 보낼 용도,
fileName은 이미지를 컴포넌트에서 미리보기 기능을 위해 이미지 파일명만 보냈다.
이제 이미지 서버와 통신이 원활하게 동작하는 것을 확인했다.
이미지 파일을 업로드하면 반환 값으로 state: 성공 여부, imageUrl: 이미지 주소 를 받게 되는데
받은 이미지 주소를 DB에 있는 유저의 imageUrl에 넣어주면 된다.
이때는 특정 칼럼만 수정하는 것이기에 HTTP 메서드중 Patch를 이용해 BE에 요청을 보냈다.
/**
* 계정 관리 페이지에서 유저가 이미지를 변경할 때 유저 이미지 변경 요청을 보내는API
** PATCH요청
* @param accessToken : string
* @param userImgUrl : string
* @returns db정보 변경 성공 여부
*/
export const updateUserImgApi = async (
accessToken: string,
userImgUrl: string,
) => {
const res = await fetch(`${process.env.REACT_APP_API_URL}/account_manage/userimage`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
"accessToken": accessToken,
"userImgUrl": userImgUrl
})
})
return res.json();
};
(최근에 RESTFul 한 API 작성을 목표로 삼았기에 많은 것을 알아보고있다.)
이렇게 작성된 API는 최종적으로 DB를 다루는 코드를 통해 데이터를 수정하게 된다.
async updateUserImage(userId: number, userImgUrl: string) {
const query = `UPDATE User SET profileImage="${userImgUrl}" WHERE userId="${userId}"`
const ret = this.sendQuery(query);
return ret;
}
이렇게 해서 이미지 서버에 이미지 업로드 API와 부가적인 API를 다뤄보았다.
이 작업을 통해 배운점은
- 항상 서버측에 API 요청을 보낼때는 서버에서 데이터를 받는 방식을 확인해야한다.
- form-data를 이용해 파일을 보내면 파일의 변조가 어려워 보안적으로 좋고,
서버에서 쉽게 파싱이 가능해 사용에 용이하며,
큰 용량과 다양한 타입을 지원하기에 범용성이 높기에 사용하는 것 - RESTFUL API를 작성하는 것은 앞으로 협업을 하는 개발자 커리어를 시작함에 있어 중요하다.
아래는 내가 RESTFUL을 공부하며 찾은 좋은 블로그 글들이다.
시간이 된다면 나도 정리해 글을 작성하도록 해야겠다.
'프로젝트 > 가말다 - 마일스톤' 카테고리의 다른 글
오류) 도메인 변경에 따른 이미지 서버 관련 오류 발견 (0) | 2024.03.23 |
---|---|
로그인 페이지 리펙토링 (0) | 2023.12.29 |
Store과 DB를 혼동해 사용하지 말자. (0) | 2023.05.16 |
로그아웃 기능 중 쿠키 전달을 위한 fetch의 credentials(내용증명) 옵션 사용 (0) | 2023.04.19 |
BE - DB연결 방식 변경 (Prisma -> SQL쿼리) (0) | 2023.04.06 |