어려워 보였지만 쉬웠다.


① 이미지 업로드하기 #1 - VueJS + NodeJS

② 이미지 업로드하기 #2 - Amazon S3 setting, upload

③ 이미지 업로드하기 #3 - Drag and Drop

④ 이미지 업로드하기 #4 - Multi image management

 

 이번에는 2021년 1월 2일 기준 Amazon S3를 생성하는 방법과 설정하는 방법에 관해서 포스팅하겠습니다.

 

 목차

1. S3 bucket 만들기

2. IAM 유저 등록

3. 로컬에 aws-sdk 사용에 필요한 credential 만들기


1. S3 bucket 만들기

 아마존 서비스 목록에서 S3를 선택해서 들어가면 다음과 같은 화면이 나옵니다.

 저는 이미 하나 생성해 둔 상태라서 버킷 이름만 좀 가려놓겠습니다. 버킷 만들기를 클릭하면 여러 가지 옵션을 설정해야 하는데 하나씩 보여드릴게요!

 일반 구성에는 버킷 이름리전이 있는데 버킷 이름은 중복이 되지 않도록 소문자로 작성을 해주시면 됩니다. 우리는 서울에 사니까 리전은 서울로 자동선택되어 있을 거예요. 원하시는 곳을 지정하면 됩니다.

 

 이 부분이 조금 헷갈리는 부분인데요. 아마존 공식 문서를 읽어보면 이해가 안되는 부분이 있어서 테스트를 해봤어요. 그래서 제 방식대로 설명을 좀 해보자면

 

1) 새 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단

→ 이 부분은 공식문서에서 영문으로 BlockPublicAcls라고 합니다. 모든 퍼블릭 접근을 차단한다는 뜻입니다. 이미지 업로드 #1 글에서 나왔던 코드를 보면 S3 upload 옵션(parametes) 중에 ACL을 public-read로 표현한 부분이 있는데

ACL을 public-read로 설정했다는 내용

 ACL을 public으로 설정하려고 하면 업로드 자체를 Access Denied 오류를 내뿜으면서 거절한다. ACL을 삭제하고 업로드해봤는데 업로드가 가능합니다. 그리고 퍼블릭 설정을 하지 않았기 때문에 당연히 URL로 접근해서 이미지를 불러오는 것이 불가능해지구요.

 

2) 임의의 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단

→ 이 부분은 IgnorePublicAcls라고 합니다. 차단까지는 아닌데 무시한다는 거죠. 만약 ACL을 퍼블릭으로 설정해서 업로드를 시도하면 업로드는 가능합니다. 1번처럼 Access Denied 오류를 뿜지는 않는데 URL을 통해서 이미지에 접근하려고 하면 접근이 되지 않습니다. 애써 작성한 코드를 무시해버리는 엄청난 놈이에요.

 

3) 새 퍼블릭 버킷 또는 액세스 지점 정책을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단

→ BlockPublicPolicy는 퍼블릭 설정을 함부로 바꾸지 못하게 하는 옵션이에요. 다른 누군가가 제가 해놓은 설정을 바꾸면 안 되니까요.

 

4) 임의의 퍼블릭 버킷 또는 액세스 지점 정책을 통해 부여된 버킷 및 객체에 대한 퍼블릭 및 교차 계정 액세스 차단

RestrictPublicBuckets라고 부르는 이 옵션은 권한이 부여된 사용자에게만 파일(객체)이나 S3(버킷)에 퍼블릭 설정을 할 수 있도록 하는 옵션입니다.

 

그래서 3, 4번 옵션만 켜놓고 옵션을 설정해놓고 사용하면 이미지를 업로드하고 URL로 접근은 허용하면서 보안을 챙길 수 있을 것 같습니다. 그리고 테스트해보면서 알게 된 사실인데 3, 4번을 켜놓고 업로드해 놓은 이미지 파일(퍼블릭으로 접근 가능한)은 나중에 1,2번 옵션을 켜 놓아도 여전히 접근이 가능하더라구요. 신기하죠?

 

그리고 아래는 한글로 설명된 아마존 공식 문서입니다. 저 같은 비전공자는 읽어도 무슨 말인지 잘 이해가 되지 않아요. 왜 공식문서는 하나같이 이해가 어려운가요? 불만입니다.

 

아마존 공식 문서 링크 ☞ docs.aws.amazon.com/ko_kr/AmazonS3/latest/dev/access-control-block-public-access.html

 

그 외에 버킷 버전 관리는 비활성화, 태그는 무시하고 기본 암호화는 비활성화, 고급설정은 건들지 않고 진행하겠습니다.

 

그리고 "버킷 만들기"를 클릭하면 깨끗한 S3가 만들어집니다.

 

백지

 이제 끝입니다.

 

※ 나중에 저기 "관리"탭에 들어있는 "수명 주기 규칙"을 추가할 수 있는데 너무 길어지니까 마지막 포스팅에서 팁으로 넣어둘게요!


2. IAM 유저 등록

 IAM은 아마존에 있는 리소스에 접근할 수 있는 권한을 부여하는 서비스입니다. 일단은 유저를 먼저 생성해봅니다. 서비스→보안, 자격 증명 및 보안 준수→IAM으로 접근할 수 있습니다.

 

 접근하면 기본적으로 대시보드가 보이는데 왼쪽 메뉴의 "사용자"를 클릭합니다.

 

사용자 → 사용자 추가

 이제 사용자를 추가할 수 있습니다. 한번 추가해봅니다.

 

  사용자 이름은 본인이 사용할 사용자 이름입니다. 원하는 이름을 만들고 액세스 유형은 "프로그래밍 방식"을 선택합니다.

 

 이제 권한을 설정합니다. 그룹을 하나 만들어서 거기에 유저를 추가해도 좋고, 아니면 기존 정책에 직접 연결을 사용해서 바로 적용해도 됩니다. 앞으로 유저가 많아지면 그룹으로 관리하는 게 좋겠지만 여기서는 "기존 정책에 직접 연결"을 해보겠습니다.

 

 s3로 검색을 해보면 두 번째에 AmazonS3FullAccess가 있습니다. S3에 읽기, 쓰기 등 모든 접근 권한을 주는 거예요. 그리고 다음을 누르면 태그를 만들 수 있는 단계인데 만들어도 좋고, 안 만들어도 상관없습니다. 저는 패스!

 

 그럼 아래처럼 유저가 하나 생성이 됩니다.

 IAM 유저를 생성이 끝났습니다. 저 액세스 키 ID비밀 액세스 키가 바로 aws-sdk를 이용해서 Amazon S3에 접근할 수 있는 아이디와 비밀번호 같은 역할을 하게 됩니다.

 


3. 로컬에 aws-sdk 사용에 필요한 credential 만들기

 다음으로 본인의 로컬에 credential 파일을 만들어 줍니다. 그러면 aws-sdk가 자동적으로 credential을 읽어서 S3를 제어할 수 있게 되는 방식인 거죠.

 

※ Mac/Linux : ~/.aws/credentials

Windows : C:\Users\USERNAME\.aws\credentials

[default]
aws_access_key_id = your_access_key
aws_secret_access_key = your_secret_key

 위의 경로에 credentials라는 파일을 만들어서 your_access_key에 액세스 키 ID를, your_secret_key에 비밀 액세스 키를 넣어서 저장해줍니다.

 

그리고 프로젝트에 aws-sdk를 설치해주면 됩니다.

npm install aws-sdk
// or
yarn add aws-sdk

끝!


 이전 포스팅에서 S3에 업로드하는 코드와 설명을 다 해놓았기 때문에 꼭 참고해주세요. 다음에는 드래그 앤 드롭을 통해 파일을 업로드하는 코드를 이전 코드에 덧붙여서 포스팅하겠습니다.

 

 코로나가 심해지는 요즘, 모두 조심하세요!


오늘도 하나 해냈습니다. 뿌듯하군요!
쓸 때는 몰랐지만 많은 기술이 숨어 있었다!


이미지 업로드하기 #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편에서 돌아오겠습니다!


코드를 작성하고 설명할게 더 많을 줄 알았다. 코드만 읽는 게 설명을 읽는 것보다 쉬운 건 비밀.

+ Recent posts