먼저 ❗️그전에 만들어 놓은
useAuth()
custom hook
를 사용해서token
을 받아 로그인한 작성자를 가지고 온다.
ActivityWrite.js
import { useAuth } from "../../components/hooks/hooks";
const ActivityWritePage = () => {
useAuth();
return (
<DashboardLayout target="활동게시판">
<ActivityCU/>
</DashboardLayout>
);
}
export default ActivityWritePage;
로그인한 사람의
activityCU.js
import { useContext, useEffect, useState } from 'react';
import { UserContext } from '../../App';
import axios from 'axios';
const ActivityCU = () => {
const [userEmail, setUserEmail] = useState(''); //로그인한 사람의 email 정보를 담을 변수
const { accessToken } = useContext(UserContext);
//로그인한 사람의 email 정보 가져오기
useEffect(() => {
const tmp = async () => {
if (!accessToken) return;
let res = await axios.get('/api/loggedInEmail', { headers: { Authorization: `Bearer ${accessToken}` } });
setUserEmail(res.data);
}
tmp();
}, [accessToken]);
return(
<ActivityInputWrap>
<label htmlFor="writerEmail">작성자</label>
<input disabled value={userEmail} id="writerEmail" />
</ActivityInputWrap>
);
- 게시글제목,
- 작성자- 토큰을 주면됨,(대체가능)
- 내용,
- 이미지파일 2개, 올렸으니까 2개
app.js
🔎 1. formData 타입을 보내줘야한다. formData 객체 생성
🔎 2. 이미지 파일 두개를 담아줘야한다.(
img.origin
)
activityCU.js
const handleSubmit = (e) => {
e.preventDefault();
let cpy = JSON.parse(JSON.stringify(formInfo));
cpy.touched.title = true;
cpy.touched.content = true;
setFormInfo(cpy);
//모든 제목하고, 내용이 정상적으로 입력되었다면?
//제목 에러메세지가 '' 이고, 내용 에러메세지가 '' 이라면? 정상적으로 모든 값이 입력됨.
if (formInfo.errors.title === '' && formInfo.errors.content === '') {
// alert('정상적으로 입력되었습니다. 서버로 전송합니다.');
//서버로 전송
//🌟1. formData 생성
let formData = new FormData(); //formData 객체 생성
//🌟2.이미지 파일 두개를 담아줌
imgList.forEach((img) => { //imgList에 있는 모든 이미지를 formData에 추가해준다.
formData.append('image', img.originFile); //image라는 이름으로 img.originFile을 "추가"해준다.
});
axios.post('/api/activities')
}
}
npm i multer
package.json
에서 확인 하면
"multer": "^1.4.5"
를 받은것을 확인할수있다.
그런데, 현재 최신버전에 버그가 있다. 파일을 한글로 적으면 오류가 난다. 그래서 한단계 전버전으로 받겠다.
npm i multer@1.4.4
이렇게 하면 버전이 바뀌는걸 확인할수있다.
이미지는 mysql 에 저장하지않는다.
왜냐하면❓용량이 너무 커서.
컴퓨터 메모리에 저장? 아니면 disk에 저장?
👉👉 disk는 껏다켜도 저장이된다. 메모리는 껏다키면 사라진다.
실무에선 disk도 저장❓ 안한다. 클라우드에 한다.
limit
: 사이즈 설정가능(안줘도 되지만 너무 고화질이면 돈을 많이 내야함)
destination
: 어디 경로에 저장할지
filename
: 파일을 어떤이름으로 저장할지
originalname
: 사용자가 업로드 한 파일 명
destination: (req, file, cb)
매개변수는 3개.
req
= 요청에대한정보(express한테)
file
= 이미지의 file 정보
cb
= cb실질적으로 뭘 실행할건지,cb(null, '../public/images/'); 🌟//업로드할 경로를 써준다.
이미지, 동영상
등을 업로드할 때 사용하는 미들웨어 (그때그때 찾아서 사용하면된다. )
//multer 라이브러리 설정하기
const multer = require('multer');
const upload = multer({
storage: multer.diskStorage({ //diskStorage에 저장.
destination: (req, file, cb) => { //어디 경로에 저장할지
cb(null, '../public/images/'); 🌟//업로드할 경로를 써준다.
},
filename: (req, file, cb) => { //파일을 어떤이름으로 저장할지
cb(null, file.originalname);
}
}),
limit: { fileSize: 5 * 1024 * 1024 } //5MB (파일용량)
});
storage
에서 파일을 저장 할 경로와 이름을 설정 할 수 있는데,file
인수에서 다음 속성으로 파일 정보를 얻을 수 있다.
api/activities
post 요청이 오면, 원래는 보통 req,res함수가 실행되는데.
여기는 두번째함수가 만들어져서, 두번째함수"이미지업로드"
부분이 실행되고 난후,req,res함수가 실행
이것을 바로 미들웨어 라고한다. (보통 중복될때사용)
예를들면, 토큰 받아오기,
이렇게 미리 함수를 만들어두면 사용가능 -
verifyToken
🔎
upload.array('images')
upload를 실행
array는 여러개, single은 하나.
upload.array('images')
('images')
는 activityCU.js에서 images 라는 이름으로 추가했기때문에 그대로 이름을 가져옴
console.log(req.files)
- 2개의 이미지 정보가 들어가있음
console.log(result);
🌟result.insertId
--> 새로 추가된 게시물의 id 값
app.js
app.post('/api/activities', upload.array('images'), async (req, res) => {
// console.log(req.body);
// console.log(req.files);
// console.log(req.headers.authorization)
//이미지 업로드 완료, 그뒤에
//mysql 에 게시글 제목, 내용, 작성자, 작성일자, insert into
const token = req.headers.authorization.replace('Bearer ', '');
const user = jwt.verify(token, process.env.JWT_SECRET);
const { title, content } = req.body; //req.body에 들어있는 title, content
//req.files[0].filename --> 이미지 파일 이름(우리컴퓨터에 저장된 파일 이름)
try {
let sql = `insert into tbl_activities
(title, content, writer_email, activity_view)
values
(?, ?, ?, 0)
`; //조회수는 초기값은 0이니까
let [result] = await pool.query(sql, [
title,
content,
user.email
]);
//🌟result.insertId --> 새로 추가된 게시물의 id 값
// console.log(result);
//이미지 경로도 mysql, tbl_activity_img 테이블에 추가
sql = `
insert into tbl_activity_img
(activity_id, img_url)
values
(?, ?)
`;
for (let i = 0; i < req.files.length; i++) {
await pool.query(sql, [result.insertId, '/images/' + req.files[i].filename]);
} //🌟files안에있는것들 업로드한 이미지들. result.insertId(방금 새로 추가된 게시물 id
//'/images/' 폴더로 추가된 이미지가 들어간다.
//react 에게 응답
res.json({ id: result.insertId }); //게시글인데 방금 추가된 id
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
mysql 에도 저장되었다.
activityCU.js
const handleSubmit = async (e) => {
e.preventDefault();
let cpy = JSON.parse(JSON.stringify(formInfo));
cpy.touched.title = true;
cpy.touched.content = true;
setFormInfo(cpy);
//모든 제목하고, 내용이 정상적으로 입력되었다면?
//제목 에러메세지가 '' 이고, 내용 에러메세지가 '' 이라면? 정상적으로 모든 값이 입력됨.
if (formInfo.errors.title === '' && formInfo.errors.content === '') {
// alert('정상적으로 입력되었습니다. 서버로 전송합니다.');
//서버로 전송
//1. 🌟 formData 생성
let formData = new FormData(); //formData 객체 생성
imgList.forEach((img) => { //imgList에 있는 모든 이미지를 formData에 추가해준다.
formData.append('images', img.originFile); //images라는 이름으로 img.originFile을 "추가"해준다.
});
formData.append('title', formInfo.values.title);
formData.append('content', formInfo.values.content);
//2. 🌟 axios로 전송
let res = await axios.post('/api/activities', formData, {
headers: { Authorization: `Bearer ${accessToken}` },
});
//🌟 방금 새로 생성된 게시글의 id가 들어있다.
navigate(`/activity/${res.data.id}`, { replace: true });
};
}
그런데, 여기서 문제가 생길수도 있다❗️
같은파일 이름을 업로드하면 그전것은 없어지고 최근거로수정이 된다.
그래서 겹칠일없게 여기서 확인할수있게,
uuid(Universally Unique Identifier)
라이브러리를 설치하자 ❗️
npm i uuid
uuid + 시간 (절대로 겹칠일이 없을것이다)
UUID v1
- 현재 시간과 노드 식별자를 기반으로 순차적으로 생성되는 시간 기반 UUID
UUID v4
- 완전히 무작위로 생성되어 임의성이 필요한 경우에 사용되는 가장 보편적인 UUID 버전
UUID v5
-명시적으로 제공된 이름과 네임스페이스 식별자를 사용하여 생성됨
UUID v3
-는 현재 사용되지 않는 UUID v5의 이전 버전으로 MD5 해시 함수를 사용하여 생성됨
v1을 많이 사용하고, 유일성이 보장되지만 보안에 취약함.
그래서 우리는 v4를 사용할것이다.
const multer = require('multer');
const uuid = require('uuid')
const upload = multer({
storage: multer.diskStorage({ // 어디에 저장할지
destination: (req, file, cb) => {
cb(null, '../public/images/'); //어디에 저장할지
}, //어디에 저장할지
filename: (req, file, cb) => { //어떤이름으로 저장할지
let id = uuid.v4();
let now = new Date();
let fileName = `${id}${now.getSeconds()}${file.originalname}`
cb(null, fileName);
}
}),
limit: { fileSize: 5 * 1024 * 1024 } //5MB
});
이렇게 똑같은 파일을 올려도 파일 이름은 다르게 저장됨. 그래서 겹칠일이 없다.