서론
기능을 만들 때마다 블로그를 포스트 하지 않고 이전에 포스트 했던 내용들로 구현이 불가한 기능 등을 구현할 때만
블로그를 포스트 했기 때문에, 현재 wargame 사이트는 포스트 한 내용 외에도 몇 가지 기능이 추가로 구현된 상태이다.
문제 업로드 기능 구현 중 파일 업로드 기능 외의 다른 기능은 구현했기 때문에 오늘은 파일업로드 기능을 구현해 보겠다.
파일 업로드, 다운로드 기능
파일 업로드 및 다운로드를 위해서 이 로직이 동작하는 원리를 간략하게 설명해 보자면 다음과 같다.
워게임 사이트 특성상. zip 파일 하나만 업로드가
파일 업로드
- . ejs 파일에서 사용자가 <input type="file">로 입력한 파일 내용을 가져옴
- 컨트롤러 파일에서 업로드 시 필요한 미들웨어 함수를 만들어주고, 이를 라우터 파일에서 적용함
파일 다운로드
- 다운로드용 경로를 app.js에서 설정하고 이를 라우터파일에 설정함
- 업로드 경로/:fileName으로 설정해 해당 경로로 접근하면 파일이 다운되도록 컨트롤러 파일에서 설정함
- . ejs 파일에서 위 경로로 이동하는 버튼을 만들어 버튼 클릭 시 해당 경로로 이동되게끔 설정함
파일 업로드 기능 구현
npm install multer --save
파일 업로드를 위한 Multer 모듈을 다운로드하여 준다. 여기서 Multer 모듈이란 클라이언트가 전송한 파일을 받아서
서버에서 사용할 수 있게 처리하는 미들웨어이다.
이 Multer 모듈을 사용해 파일을 받기 위해선 먼저. ejs 파일에서 요청을 보낼 때 사용하는
form 태그를 수정할 필요가 있다.
// .ejs
<form method="post" enctype="multipart/form-data">
enctype를 multipart/form-data로 설정해 주면 form 내의 필드와 file 필드를 구분지어서 보내는 것이 가능하다.
// upload.controller.js
const upload = multer({
storage: multer.diskStorage({
destination(req, file, done) {
done(null, "../upload");
},
filename(req, file, done) {
const ext = path.extname(file.originalname);
if (ext !== '.zip') {
return res.send(`<script>alert("Only zip files are allowed."); window.location.href = '/';</script>`);
}
done(null, path.basename(file.originalname, ext) + ext);
},
}),
});
컨트롤러 파일에 업로드 함수를 위처럼 설정해 준다. 여기서 multer.diskStorage() 함수로 업로드한 파일에 대해 설정하는 것이 가능하다.
이때 destination과 filename 함수를 통해 각각 최종적으로 파일이 저장되는 디렉터리와, 저장될 이름 등을
설정해 주는 것이 가능하다.
위 코드의 extname은 파일의 확장자만을 추출하는 함수로 이를 통해 특정 확장자만 제한하는 식의 보호가 가능하다.
// upload.controller.js
exports.uploadMiddleware = upload.single('file');
이제 해당 함수를 사용하는 미들웨어 함수를 정의해 준다. 여기서. single은 단일 파일을 업로드한단
의미이다.. array 등을 사용해 여러 파일을 업로드하는 것도 가능하지만 워게임이란 사이트 특성상
하나의 파일만 업로드 가능하게 제한했다.
// upload.js
exports.uploadProblem = (problemData, callback) => {
const upload_query = 'INSERT INTO problems (usr_idx, title, text, category, flag, file) VALUES (?, ?, ?, ?, ?, ?);';
conn.query(upload_query, [problemData.usrIdx.usr_idx ,problemData.title, problemData.text, problemData.category, md5(problemData.flag), problemData.fileName], (err, results) => {
if (err) {
return callback(err);
}
callback(null, results);
});
};
모델 디렉터리의 파일로 업로드할 파일의 이름을 DB에 저장하는 쿼리이다.
이를 통해 다운로드 시 DB를 통한 검색으로 문제에 맞는 파일을 다운로드하는 것이 가능하다.
// routes 파일
router.post("/upload", uploadController.uploadMiddleware, uploadController.handleUpload);
이제 라우터 파일에 앞서 만든 미들웨어 함수를 추가하면 정상적으로 파일 업로드가 설정한 경로로 업로드되는 것을
확인이 가능하다.
파일 다운로드 기능 구현
// app.js
const downloadRouter = require("./routes/download.route.js");
app.use("/download", downloadRouter);
app.js 중 일부 코드이다. download시 사용할 서버의 디렉터리를 설정해 주고, 이를 app.js에서 설정해 준다.
// download.route.js
const express = require("express");
const downloadController = require("../controllers/download.controller.js");
const router = express.Router();
router.get("/:fileName", downloadController.fileDownload);
module.exports = router;
/:fileName 은 뒤의 경로를 변수로써 사용하겠단 의미로 이전에 업로드 시 저장한 이름을 가져오게끔 설정해주어야 한다.
// download.controller.js
const path = require('path');
exports.fileDownload = (req, res) => {
console.log(req.params);
console.log(__dirname);
const filePath = path.join(__dirname, '../../upload', req.params.fileName);
res.download(filePath);
};
filePath 경로에 실제 서버에 저장되어 있는 파일의 경로를 가져와 res.download를 통해 다운로드가 실행된다.
'Dev' 카테고리의 다른 글
비밀번호 초기화 이메일 보내기 구현 (0) | 2025.02.27 |
---|---|
Node.js 무한 대댓글 로직 구현 (0) | 2025.02.27 |
로그인 기능 구현 (0) | 2025.02.27 |
node.js 회원가입 로직 구현 (0) | 2025.02.27 |
Node.js 사진 올리기 (0) | 2025.02.27 |