프론트에서 이미지를 서버에 저장한다음, 그 경로를 데이터베이스에 저장하고, 프론트가 데이터베이스에서 서버에 저장된 이미지의 주소를 받아와서 이미지 태그의 주소에 넣어주는 방법을 알아보자.
import logo from './logo.svg';
import './App.css';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import FilesUpload from './components/FilesUpload';
function App() {
return (
<div className="App">
<FilesUpload/>
</div>
);
}
export default App;
import React, { useEffect, useState } from "react";
import axios from "axios";
function FilesUpload() {
const [file, setFile] = useState();
const [list, setList] = useState();
const onFileChange = (e) => {
setFile({ profileImg: e.target.files[0] });
};
const onSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("profileImg", file.profileImg);
//보통 form 태그에서 데이터를 전송할때는 json 형식을 이용하지만,
//이미지나 파일등을 업로드 할때에는 formData 객체를 이용한다.
//append() 메서드를 통해 빈 FormData 객체에 key-value 쌍을 추가해준다.
console.log(formData);
axios
.post("http://localhost:4000/api/user-profile", formData, {})
// form-data, x-www-form-urlencoded 등의 파일을 보낼 때에는 헤더를 추가해줘야 한다.
// 위의 형식은 { headers: { 'Content-Type': 'multipart/form-data' } } 이다.
// 하지만 headers 의 기본 Content-Type 이 multipart/form-data 이므로 생략 가능하다.
.then((res) => {
console.log(res);
});
};
useEffect(() => {
async function fetchData() {
// You can await here
const response = await axios.get("http://localhost:4000/api");
// db 에 저장된 (서버에 저장되어 있는) 이미지의 경로를 가져온다.
setList(response.data.users);
// ...
}
fetchData();
}, []);
return (
<div className="container">
<div className="row">
<form onSubmit={onSubmit}>
<div className="form-group">
<input type="file" onChange={onFileChange} />
</div>
<div className="form-group">
<button className="btn btn-primary" type="submit">
Upload
</button>
</div>
</form>
</div>
{list &&
list.map((image, index) => (
<img key={index} src={`${image.profileImg}`} />
))}
</div>
);
}
export default FilesUpload;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
_id: mongoose.Schema.Types.ObjectId,
profileImg: {
type: String
}
}, {
collection: 'users'
})
module.exports = mongoose.model('User', userSchema)
let express = require('express'),
mongoose = require('mongoose'),
cors = require('cors'),
bodyParser = require('body-parser');
const api = require('../backend/routes/user.routes')
// MongoDB Configuration
mongoose
.connect('...')
.then((x) => {
console.log(`Connected to Mongo! Database name: "${x.connections[0].name}"`)
})
.catch((err) => {
console.error('Error connecting to mongo', err.reason)
})
const app = express();
app.use(bodyParser.json());
// req.body 는 body-parser 를 사용하지 전까지 default 값으로 undefined 가 설정되어 있다.
// body-parser 를 이용하여 req.body 데이터를 사용자가 원하는 형태로 parsing 하여 이용할 수 있다
app.use(bodyParser.urlencoded({
extended: false
}));
// true 를 주면 qs 모듈을 사용하는 것으로, 따로 npm install 을 해야한다.
// 따라서 node.js 에 기본적으로 내장된 query-string 모듈을 사용하려면 false 를 줘야한다.
app.use(cors());
app.use('/public', express.static('public'));
// 정적(static) 파일을 손쉽게 제공하기 위해 사용한다. express.static 을 사용하지 않으면,
// 정적 파일이 존재하는 path 로 접근하기 위한 코드가 번거롭고 복잡하게 된다.
// static 의 인자로 디렉토리명을 전달하며, 해당 디렉토리 경로의 데이터들은
// 웹브라우저의 요청에 따라 서비스를 제공할 수 있다.
// 해당 디렉토리에 접근할때에도 해당 경로를 static 경로로 지정해야 한다.
app.use('/api', api)
// "/api" 에 접근하는 모든 것들을 user.routes 에게 위임한다
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
console.log('Connected to port ' + port)
})
app.use((req, res, next) => {
// Error goes via `next()` method
setImmediate(() => {
next(new Error('Something went wrong'));
});
});
app.use(function (err, req, res, next) {
console.error(err.message);
if (!err.statusCode) err.statusCode = 500;
res.status(err.statusCode).send(err.message);
});
let express = require('express'),
multer = require('multer'),
mongoose = require('mongoose'),
uuidv4 = require('uuid/v4'),
//네트워크 상에서 고유성이 보장되는 id 를 만들기 위한 표준 규약, 버전 4 는 랜덤 생성.
router = express.Router();
const DIR = './public/';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR)
},//file 을 받아와서 DIR 경로에 저장한다.
filename: (req, file, cb) => {// 저장할 파일의 이름을 설정한다.
const fileName = file.originalname.toLowerCase().split(' ').join('-');
cb(null, uuidv4() + '-' + fileName)
// (uuidv4 O) 7c7c98c7-1d46-4305-ba3c-f2dc305e16b0-통지서
// (uuidv4 X) 통지서
}
});
var upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {// 말 그대로 fileFilter
if(file.mimetype == "image/png"
|| file.mimetype == "image/jpg"
|| file.mimetype == "image/jpeg"){
cb(null, true);
} else {
cb(null, false);
return cb(new Error('Only .png .jpg and .jpeg format allowed!'));
}
}
});
let User = require('../models/User');
router.post('/user-profile', upload.single('profileImg'), (req, res, next) => {
// upload.single('profileImg') 에서 profileImg 는 formData 의 key 를 말한다.
// 따라서 "profileImg" key 의 value 값을 서버의 지정된 폴더에 저장한다.
const url = req.protocol + '://' + req.get('host')
// req.protocol => http or https
// req.get('host') => (현재) localhost:4000
const user = new User({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
profileImg: url + '/public/' + req.file.filename
});
user.save().then(result => {
res.status(201).json({
message: 'User registerd successfuly!',
userCreated: {
_id: result._id,
profileImg: result.profileImg
}
})
}).catch(err => {
console.log(err);
res.status(500).json({
error: err
})
})
})
router.get('/', (req, res, next) => {
User.find().then(data => {
res.status(200).json({
message: "User list retrieved successfully!",
users: data
})
})
});
module.exports = router;
테스트 결과물은 아래와 같다.
[참고]
react-file-upload-tutorial
https://www.positronx.io/react-file-upload-tutorial-with-node-express-and-multer/
static file service
https://askforyou.tistory.com/6