IT's Jenna

formidable로 파일 업로드하기 본문

Backend/Backend 기본

formidable로 파일 업로드하기

developer Jenna 2021. 2. 18. 10:10

기본적으로 http 통신 프로토콜에선 req, res에 텍스트를 전달하면서 통신한다. 텍스트 외의 파일을 업로드하기 위해서는 form-data 형식으로 통신을 해야 하고 form-data 형식을 사용하기 위해선 추가 모듈이 필요하다. 이때 사용하는 모듈이 formidable이다.

formidable 모듈은 npm i formidable --save로 설치해서 사용할 수 있다.

 

공부하고 있는 단계에서는 서버와 Database 그리고 파일을 저장하는 storage까지 하나의 PC에서 사용하지만 실무에서는 server 컴퓨터, database 컴퓨터, storage 컴퓨터를 모두 분리해서 사용한다. 이렇게 PC를 분리해서 사용해야 추후에 PC 확장이 용이하다. 

 

파일을 업로드할 때의 절차를 한번 알아보자. 예를 들어 이미지를 업로드한다고 했을 때, 이미지 자체를 db에 저장한다고 생각할 수 있겠지만 실제로는 그렇지 않다. 대용량 정적 파일들은 storage에 따로 저장한다. 실제 파일 업로드 절차는 다음과 같다.

 

  1. 이미지 업로드 API를 통해 이미지를 storage에 업로드
  2. 이미지 업로드 API는 해당 경로를 return
  3. 업로드된 경로를 받고 이 정보를 database에 입력

따라서 파일 업로드를 할 때 필요한 API는 총 2가지다. 이미지를 storage에 업로드하는 api, 이미지 경로를 database에 입력하는 api. 이후에 이미지를 불러올 때는 database의 경로를 통해 불러오고, 수정 시에도 database의 경로를 수정해준다.

 

이미지 파일을 업로드하고 수정하는 예제를 만들어 보았다. 단 지금은 storage를 따로 분리할 수 없기 때문에 public의 images폴더에 파일을 올렸다.

 

  • 우선 user_img 라우팅 경로를 추가로 만들어준다.
  • 위에서 언급한 것 처럼 user_img에는 이미지 업로드와 database 경로 추가 2가지 post method가 필요하다. 하지만 같은 라우팅 경로에서 하나의 method는 하나의 동작을 하기 때문에 이미지 업로드 post method의 라우팅 경로를 달리해주었다.
const formidable = require('formidable')
const path = require('path')
const fs = require('fs')

router.post('/upload', async function(req, res, next) {
    try{
        const query = req.query
        console.log('req : ', req)
        const form = formidable({multiples: true}) //한번에 여러개 파일 입력받아오는 것 허용
        form.parse(req, (err, fields, files) => {
            if (err) {
                next(err)
                return
            }
            console.log('files : ', files)
            const file = files.image
            const dir = `public/images/${query.user_idx}`
            !fs.existsSync(dir) && fs.mkdirSync(dir)
            const newPath = path.join(__dirname,'..', `${dir}/${file.name}`) //__dirname : 현재경로 가져오기
            fs.renameSync(file.path, newPath)
            res.json({result: `images/${query.user_idx}/${file.name}`})
        })

    }catch(err){
        console.log('err : ',err)
        next(err)
    }
})

router.post('/', async function(req, res, next) {
    const body = req.body
    console.log('body : ', body)
    try {
        const connection = await db.beginTransaction()
        const result = await model.insert(connection, body)
        await db.commit(connection)
        res.status(200).json({result})
    } catch (err){
        next(err) // 에러가 있더라도 다른 명령어를 호출해야 함
    }
})
  • form.parse : req로 데이터를 보내주면 이 데이터를 다시 파일 형태로 변환해준다.
  • postman에서 이미지 파일을 보내줄 때 아래 그림과 같이 key를 image로 지정하고 파일을 보내줬기 때문에 files.image로 파일을 가져온다.

<postman으로 이미지 보내기>

  • 파일을 가져왔으면 이제 해당 파일을 저장할 directory를 지정해준다. 이때 사용하는 모듈이 fs 모듈이다. fs는 file system의 약자로 파일을 읽고 쓰는데 사용하는 모듈이다. 
  • !fs.existsSync(dir) && fs.mkdirSync(dir) : fs모듈과 삼항 연산자를 사용해서 일치하는 유저의 directory가 있는지 확인하고 없으면 새로운 directory를 하나 만들어준다 

삼항 연산자 조건문

조건문 || 동작 (조건문이 거짓이면 동작한다)

조건문 && 동작 (조건문이 참이면 동작한다)

const newPath = path.join(__dirname,'..', `${dir}/${file.name}`)
console.log('__dirname : ', __dirname)
console.log('newPath : ', newPath)

//__dirname :  /폴더경로/routes
//newPath :  /폴더경로/public/images/2/스크린샷 2021-01-31 오후 6.25.57.png
  • path모듈파일 directory 설정에 관한 메소드를 제공해준다. 여기서 __dirname은 현재 파일의 경로를 의미한다. 여기서는 routes 폴더가 __dirname으로 설정되어 있는데 이미지는 public 폴더에 저장을 해야 하기 때문에 상위 directory로 한번 갔다가 경로를 다시 설정한다.
  • fs.renameSync(file.path, newPath) : formidable모듈을 사용해서 파일을 가져오면 파일을 우선 가상 폴더에 저장을 해둔다. 해당 파일을 원하는 경로에 저장해주기 위해선 가상 폴더 안의 파일을 복사해서 특정 경로에 넣고 가상 폴더를 삭제를 해줘야 한다. 이러한 전체 과정을 renameSync 메소드가 처리해준다. 매개변수로 가상 폴더와 저장 경로를 받는다.
  • 파일 업로드 API를 돌리면 결과로 img가 저장된 path를 return 한다. 그렇게 받은 path를 다시 post로 user_idx와 함께 database에 올려서 db 관리를 할 수 있다.

 

<참고>

first-class.tistory.com/12

yohanpro.com/posts/js/node-path

'Backend > Backend 기본' 카테고리의 다른 글

서버 API 만들기  (0) 2021.02.18
데이터베이스 모델링  (0) 2021.02.18
Crypto  (0) 2021.02.01
모듈화  (0) 2021.01.27
Database 상태변화  (0) 2021.01.27
Comments