쓸 때는 몰랐지만 많은 기술이 숨어 있었다!
① 이미지 업로드하기 #1 - VueJS + NodeJS
② 이미지 업로드하기 #2 - Amazon S3 setting + upload
③ 이미지 업로드하기 #3 - Drag and Drop
④ 이미지 업로드하기 #4 - Multi image management
토이 프로젝트를 하던 중에 이미지 업로드를 해야 할 일이 생겼습니다. 구현한 모습은 아래와 같아요.
구현 단계는 먼저 한 개의 이미지를 업로드를 시작으로 아마존 저장소인 S3(AWS S3)에 저장하고 URL을 불러와서 썸네일을 보여줄 겁니다. 그리고 이후에 드래그 앤 드롭이 가능하도록 만들고, 여러 개의 이미지를 업로드하고 순서를 바꾸는 등의 잡기술을 구현해 볼 예정이에요.
도움에는 Tailwindcss(css framework), Express(nodejs web framework), 기타 라이브러리가 있습니다.
여러분의 시간은 소중하죠. 바로 시작합니다. 먼저 드래그 앤 드롭은 구현하지 않은 코드입니다.
작동 순서는 이렇습니다.
1. 사각형을 클릭한다.
2. input tag가 열리고 이미지를 선택한다.
3. 선택한 이미지가 업로드된다.
4. 업로드된 이미지의 url을 가져와서 보여준다.
▶ FRONTEND
src/views/upload.vue
<template>
<div class="w-32 h-32 border-2 border-dotted border-blue-500">
<div v-if="images"
class="w-full h-full flex items-center">
<img :src="images" alt="image">
</div>
<div v-else
class="w-full h-full flex items-center justify-center cursor-pointer hover:bg-pink-100"
@click="clickInputTag()">
<input ref="image" id="input"
type="file" name="image" accept="image/*" multiple="multiple"
class="hidden"
@change="uploadImage()">
<svg class="w-8 h-8" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data: ()=>({
images: ''
}),
methods: {
uploadImage: function() {
let form = new FormData()
let image = this.$refs['image'].files[0]
form.append('image', image)
axios.post('/upload', form, {
header: { 'Content-Type': 'multipart/form-data' }
}).then( ({data}) => {
this.images = data
})
.catch( err => console.log(err))
},
clickInputTag: function() {
this.$refs['image'].click()
}
}
}
</script>
설명을 해보자면
1. 그럼 사각형을 클릭해서 clickInputTag 메소드를 실행(click event)합니다.
→ Input tag는 hidden class(display:none과 같음)를 추가해서 안 보이게 만들었기 때문에 vuejs의 refs 속성을 이용해서 DOM에 접근합니다. javascript의 경우 id나 class 선택자를 통해 접근하는 것과 비슷하다고 보면 돼요. Input tag의 ref값이 image이기 때문에 this.$refs['image']로 접근해서 click()을 통해 클릭한 효과를 나타냅니다.
2. Input tag가 열리고 이미지를 선택하면 값이 변화된 것을 감지(change event)해서 uploadImage 메소드를 실행합니다.
→ 이미지 선택창이 열리고 이미지를 선택하면 값이 들어온 것을 감지한 후 바로 업로드하기 위해 change이벤트에 uploadImage를 바로 실행하도록 합니다.
3. uploadImage 메소드를 통해 이미지를 업로드합니다.
→ 메소드가 실행되면 1번과 같이 refs 속성을 통해 값에 접근하고 files값을 new Form 인스턴스에 append 해줘서 form data를 만듭니다. 이후 post방식으로 서버로 보내주면 됩니다. 서버 쪽 코드는 아래 있어요.
4. 업로드된 이미지의 url을 가지고 와서 img tag를 이용하여 나타냅니다.
→ 서버 쪽 코드를 통해 업로드에 성공하면 url을 가져오도록해서 data값인 images에 전달해주고, vue 측에서 images의 값이 변했으니 자동으로 감지해서 이미지를 띄워줍니다.
네, 그렇습니다. 코드가 오히려 간결합니다. 설명이 길죠. 저는 짧은 것도 길게 설명할 수 있는 재주를 가졌어요. (쉽게 설명하는 재주를 가졌다면 강사로 가는 건데...)
다음은 서버 쪽 코드입니다. nodejs의 web framework인 express를 사용하고 있으니 굳이 쓸 필요 없어서 생략합니다. 이번에 upload에 필요한 route는 upload(post) 하나니까 저것만 보고 갈게요.
그리고 파일 업로드하는데 multer를 middleware로 사용했고, amazon S3에 업로드하는 코드는 따로 빼서 index.js가 너무 길어지지 않게 했어요. 사실 한 파일에 넣어도 상관없습니다.
▶ BACKEND
srv/index.js
import express from 'express'
import multer from 'multer'
let upload = multer({ dest: 'public/uploads/' })
import { uploadToStorage } from "./apis/uploadToStorage"
export default (app, http) => {
// ... 생략 ...
app.post('/upload', upload.single('image'), (req, res) => {
uploadToStorage(req.file).then( (response) => {
res.send(response.Location)
}).catch(err=>console.log(err))
})
}
srv/apis/uploadToStorage.js
import AWS from 'aws-sdk'
import fs from 'fs'
AWS.config.region = 'ap-northeast-2'
let s3 = new AWS.S3()
async function uploadToStorage(file) {
let path = file.path
let name = file.originalname
let type = file.mimetype
let image = fs.createReadStream(path)
let parameters = {
Bucket: 'my-bucket-name',
Key: name,
ACL: 'public-read',
Body: image,
ContentType: type,
}
return await s3.upload(parameters, function(err) {
err ? console.log(err) : false
}).promise()
}
export { uploadToStorage }
자, 또 설명을 해보자면
1. FrontEnd에서 upload 요청이 오면, 요청이 온 파일을 local storage에 업로드합니다.
→ axios를 통해 전달된 form data에는 업로드해야 할 파일의 정보가 들어있습니다. upload 라우트에 middleware로 먼저 local storage에 파일을 업로드해주고, 업로드된 파일의 정보(가장 중요한 경로 포함)를 uploadToStorage에 인자로 보냅니다.
2. 업로드된 파일의 정보를 uploadToStorage에 보내서 amazon S3에 업로드합니다.
→ ACL의 경우 public-read를 통해 누구나 접근할 수 있도록 합니다. 그리고 return 값으로 들어가 있는 upload 메소드를 통하면 성공했을 경우 자동적으로 업로드된 이미지(S3에서는 객체라고 부르더군요)에 접근할 수 있는 url을 결과값으로 전달해줍니다.
※ S3를 생성하고 퍼블릭 액세스 차단 설정은 위와 같이 해주세요. 자세한 내용은 다음 포스팅에 하는 걸로! 또 S3를 aws_sdk를 이용해서 제어하기 위해서는 aws_access_key_id, aws_secret_access_key가 필요한데 나중에 따로 포스팅하는 걸로 하고 일단 생활코딩 링크를 남겨둘게요!
(S3에 대한 경험은 나중에 따로 포스팅하겠습니다.)
(그리고 포스팅되었습니다. 링크)
생활코딩 링크 ☞ opentutorials.org/course/2717/11768
3. 업로드에 성공하면 url 정보를 받아서 결과값으로 보내줍니다.
→ 업로드에 성공할 경우 uploadToStorage의 결과값인 url 정보를 express의 res.send를 통해 FrontEnd로 보내줍니다.
뭔가 더 자세하게 설명하고 싶지만 중간에 코드 수정하면서 겪었던 오류들은 이미지 업로드의 주제와는 상관없어서 포스팅에서는 빼도록 하겠습니다. 내용이 이해가 안 되는 것이 있다면 언제든 댓글 남겨주세요!
다음 포스팅은 amazon S3 생성과 설정에 관한 내용입니다. 생활코딩 영상이나 다른 사람들이 포스팅한 내용과 비교해서 큰 내용은 바뀐 게 없지만 UI가 살짝 바뀌어서 업데이트 정도의 의미는 있을 것 같아요. 그럼 2편에서 돌아오겠습니다!
코드를 작성하고 설명할게 더 많을 줄 알았다. 코드만 읽는 게 설명을 읽는 것보다 쉬운 건 비밀.
'기록일지 > VueJS' 카테고리의 다른 글
CropperJS로 이미지 편집하기(feat. vue, tailwind) (1) | 2021.02.24 |
---|---|
이미지 업로드하기 #4 - Multi image management (0) | 2021.02.02 |
이미지 업로드하기 #3 - Drag and Drop (0) | 2021.01.18 |
이미지 업로드하기 #2 - Amazon S3 setting, upload (2) | 2021.01.16 |