유저 아바타 변경 기능
multer를 사용해보자
이미지 업로드 버튼 클릭 시 파일을 첨부할 수 있도록 input태그를 사용했다.
<Upload htmlFor='avatar'>
이미지 업로드
<input type='file' id='avatar' name='avatar' onChange={onUploadHandler} />
</Upload>
onUploadHandler 함수 실행 시 유저가 input창에서 선택했던 파일을 FormData를 사용해 avatar라는 키의 값으로 넣어준다.
그리고 post통신으로 서버에 데이터를 보낸다. body에는 formData가 들어간다.
const onUploadHandler = useCallback(
(e) => {
e.preventDefault();
const formData = new FormData();
formData.append('avatar', e.target.files[0]);
fetch(`${process.env.API_SERVER}/api/users/${userId}/avatar/upload`, {
method: 'POST',
body: formData,
})
.then((res) => res.json())
.then(() => refetch())
.catch((error) => console.error(error));
},
[userId, refetch],
);
모든 통신에 axios를 사용했는데 왜 여기는 fetch를 사용했는가?
axios로 파일 통신하면 버전에 따라 문제가 발생할 수 있다고 한다. 그래서 여기서만 fetch를 사용했다.
또, formData에 append를 사용해서 파일을 넣었는데 console.log(formData)를 찍어보면 값을 볼 수 없었다.
한참을 헤메다가 위 블로그에서 원인을 찾았고, for in문과 for of문을 사용하면 확인이 가능하다는 것을 알았다.
자 이제 그럼 서버로 파일을 넘겼으니 express환경에서 처리해보자.
multer라이브러리를 사용하면 프론트에서 보낸 파일을 서버에서 처리할 수 있다.
npm i multer
import fs from 'fs';
import multer from 'multer';
(function () {
const dir = 'src/assets/avatars';
if (!fs.existsSync(dir)) {
console.log('src 폴더가 없습니다. 폴더를 생성합니다.');
const subDirs = dir.split('/');
let currentDir = '';
for (const subDir of subDirs) {
currentDir += subDir + '/';
if (!fs.existsSync(currentDir)) {
fs.mkdirSync(currentDir);
}
}
}
})();
const storage = multer.diskStorage({
limits: { fileSize: 50 * 1024 }, // 파일크기 500kb 제한
// 파일 저장 경로
destination: (req, file, cb) => {
cb(null, 'src/assets/avatars/');
},
// 파일 이름 변경
filename: (req, file, cb) => {
cb(null, req.params.id + file.originalname);
},
});
const upload = multer({ storage });
multer의 사용법은 인터넷을 찾아보면 다양하게 나와있다.
일단 나는 서버의 하드에 저장하기 위해 diskStorage를 사용하고 아래와 같은 옵션을 지정했다.
- limits : fileSize 50 * 1024 (500kb)
- destination : src/assets/avatars/
- filename : req.params.id + file.originalname (유저id + 파일명)
서버에 src/assets/avatars/ 경로가 없으면 생성하도록 맨 위에는 즉시실행함수를 작성해서 경로를 생성할 수 있도록 했다.
이제 비로소 multer는 미들웨어로 프론트에서 파일을 받으면 서버의 폴더에 저장한다.
저장한 파일 유저의 아바타로 설정하기
router.post('/users/:id/avatar', upload.single('avatar'), async (req, res) => {
const url = `https://www.sentenceu.co.kr/src/assets/avatars/${req.params.id}${req.file.originalname}`;
await User.updateOne({ _id: req.params.id }, { $set: { userAvatar: url } })
.then((user) => {
if (!user) return res.status(403).json({ message: 'User not found.' });
return res.status(200).json({ message: 'Upload Success' });
})
.catch((error) => res.status(500).json({ message: error.message }));
});
post API를 실행하고 multer미들웨어를 통과하면 이제 유저의 아바타를 업로드한 파일로 설정해줘야한다.
서버에 저장한 파일의 경로를 url변수로 저장하고 params로 받은 id로 유저를 검색해 해당 유저의 아바타 이미지 경로를 url로 변경한다.
개발환경에서는 잘 설정 되던게 배포를 하고나면 이미지의 경로를 찾지 못하는 문제가 발생했다.
해결방법은 express의 정적파일 서빙에 있었다.
홈페이지 접속 시 build/index.html 파일을 서빙해준 것처럼 유저의 이미지에도 접근 할 수 있도록 src폴더를 설정해주면 된다.
app.use('/src', express.static(path.join(__dirname, 'src')));
클라우드타입 배포 자동화
클라우드타입에 배포되어있는 내 서버는 깃허브 레포와 연동되어 Github push 후 서버를 재배포해야하는 번거로움이 있었다.
클라우드타입은 Github Actions로 배포 자동화 기능을 제공하고있다.
위의 링크에서 알려준 것처럼 설정하면 서버 레포지토리에 push할 때마다 클라우드타입 서버배포가 자동화로 이루어진다.