돌아온 앱 만들기 시리즈, 그 두 번째!

Hot reload!


링크

① 데스크탑 앱 만들기 #1 - Vite2 + Vue3 + Electron

② 데스크탑 앱 만들기 #2 - Hot reload

③ 데스크탑 앱 만들기 #3 - ipcMain, ipcRenderer(예정)

 

 자, 데크스탑 앱 만들기 두 번째 Hot reload 기능 구현하기입니다. Electron도 Web처럼 프론트와 백엔드로 나누어져 있습니다. 백엔드는 main.js(또는 background.js)에 해당하고, 프론트는 src안에 있는 파일들입니다.

 

 백엔드 코드가 바뀔 때는 앱 자체가 꺼졌다가 다시 실행되고, 프론트 코드가 바뀔때는 바로바로 적용될 수 있게 만들어볼게요.

 


 먼저 백엔드입니다.

 

1. 백엔드 리로드

 백엔드 리로드 기능을 구현하기 위해 라이브러리가 여러 가지 있지만 저는 2021년 11월 11일 현재 2-알파 버전인 electron-reload를 쓰도록 하겠습니다.

npm install --save-dev electron-reload
//or
yarn add -D electron-reload

 설치 이후 백엔드에 해당하는 main.js파일을 엽니다

 

/main.js

const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')

// 이부분을 넣어주세요!!
require('electron-reload')(__dirname, {
  electron: path.join(__dirname, 'node_modules', '.bin', 'electron'),
})
// 여기까지!!

function createWindow() {
  // ... 코드생략

 이제 electron-reload 라이브러리가 electron이 자리 잡고 있는 저 경로를 바라보고 변경이 일어날 때마다 앱을 껐다가 다시 켜줍니다.

 

 백엔드는 끝났습니다. 간단하죠?

 


2. 프론트 핫 리로드

 백엔드는 껐다 켜주는 수준이면 프론트는 확실히 핫하게 리로드해줍니다. vite를 쓰는 이유가 바로 이거지 않겠습니까? 그럼 설정 들어갑니다.

 

 먼저 기본적인 로직은 이렇습니다. 현재는 npm run start(또는 yarn start)를 하면 프론트 코드들이 build 되어서 /dist폴더에 묶어져서 들어갑니다. 그래서 /src에 있는 파일을 수정을 해도 변경이 되었는지 Electron이 알아챌 방법이 없죠.

/main.js

function createWindow() {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 1000,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });

  // and load the index.html of the app.
  // mainWindow.loadFile("./dist/index.html");
  // 여기부터
  if (process.env.NODE_ENV === 'production') {
    mainWindow.loadFile('./dist/index.html')
  } else {
    mainWindow.loadURL('http://localhost:3000')
  }
  // 여기까지 코드 삽입!

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

 여기서 NODE_ENV 환경변수를 이용합니다. 개발환경에서는 Electron이 build되어 있는 파일을 바라보는 게 아니라 vite가 실행되고 있는 URL을 바라보게 해서 수정될 때 적용되게 만드는 겁니다.

 

/vite.config.js

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
const path = require("path");

// https://vitejs.dev/config/
export default defineConfig({
  base: process.env.NODE_ENV === "production" ? path.resolve(__dirname, "dist") : "", // <-- 얘 수정해주세요.
  plugins: [vue()],
});

 여기서는 development 환경에서는 아이들이 asset들이 build 된 파일을 바라보지 않아야하니까!

 

 자, 이제 스크립트를 바꿉니다.

/package.json

// ...생략

"scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview",
    "start": "vite && electron ." // <-- 이부분 vite && 추가
},
  
// ... 생략

 

 그리고 실행해보시면.

vite v2.6.7 dev server running at:

> Local: http://localhost:3000/
> Network: use `--host` to expose

ready in 467ms.

 이렇게 뜨고 일렉트론은 실행조차 되지 않습니다. 그렇다고 start 스크립트에서 "vite"를 빼면 아래 에러가 뿜뿜하죠.

 

스크립트 에러

 에러의 이유는 일렉트론이 실행되면서 localhost:3000를 읽어오지 못하기 때문입니다. localhost:3000이 만들어지기 전에 electron이 먼저 실행되면서 읽어올 수 없다고 찡찡대는 거죠.

 

자, 그럼 방법이 보입니다.

1) vite를 실행시킨다.(개발환경에서만 쓸 거니까 NODE_ENV도 development로 설정해준다.)

2) localhost:3000을 기다린 이후에 일렉트론을 실행한다.

 

 이 두 개의 명령이 따로 실행되면 안 되니 같이 실행될 수 있게 해주는 뭔가가 또 필요하겠죠.

 

 그럼 이제 세 개의 라이브러리가 필요해집니다

cross-env: 윈도우, 맥, 리눅스 환경에 구애받지 않고 환경변수를 설정할 수 있게 해주는 애

wait-on: port가 열릴 때까지 기다릴 수 있게 만들어주는 애

concurrently: 1)과 2)가 동시에 실행될 수 있게 만들어주는 애

 

그럼 설치!

npm install --save-dev concurrently wait-on cross-env
//or
yarn add -D concurrently wait-on cross-env

 설치가 완료되면 스크립트 파일을 아래처럼 만들어주세요./package.json

// ... 위에 생략

"scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview",
    "start": "concurrently -k \"cross-env NODE_ENV=development && vite\" \"wait-on tcp:3000 && electron .\"" // <-- 여기요 여기
},

// ... 아래 생략

 

그리고 npm run start(또는 yarn start)를 실행해봅니다.

제발 실행되었기를 빕니다.

 

 잘 실행이 되었다면 소스코드를 변경해봅니다.

/src/App.vue

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue 3 + Vite!!" /> // <-- 이부분에 느낌표 두개 추가해볼게요.
</template>

편안


 마음이 편안해지는군요. 이제 백엔드와 프론트 둘 다 소스코드가 변경되면 알아서 앱이 껐다 켜지거나, 화면이 리로드 됩니다. 한번 체험해보세요 속도를. 감동입니다.

 

 이제 여러분도 그 감동과 함께 하시기를! 그리고 다음 포스팅은 2021 버전 데스크탑 앱 만들기 마지막입니다. preload.js를 활용해서 ipc 통신하는 방법을 올려보겠습니다. 굿바이!

 


2021.11.12 오류 발생!

NODE_ENV=development 환경에서 localhost:3000이 실행된 이후에 electron이 안뜬다면 start 스크립에서 wait-on tcp:3000을 빼보세요! 그러니 되더군요!

 

 

한 글자 바꿀 때마다 새로고침 하고 싶어.. 너무 좋아..
돌아온 앱 만들기 시리즈

 

바이트가 아니라 빗! 이라고 강조하더군요


링크

① 데스크탑 앱 만들기 #1 - Vite2 + Vue3 + Electron

② 데스크탑 앱 만들기 #2 - Hot reload

③ 데스크탑 앱 만들기 #3 - ipcMain, ipcRenderer(예정)

 

 데스크탑 앱 만들기 시리즈를 작년 9월에 시작하고 벌써 1년이 넘게 지났네요. 그동안 많은 기술이 나왔고 일렉트론도 엄청난 버전업을 했습니다. 2021년 10월 13일 오전 1시 30분 현재 15.1.2 버전이군요. 작년 이맘때가 10.1.0 버전이었는데.(얘네는 왜 이렇게 빨리 버전을 올리는지 모르겠습니다)

 

 그리고 바로 몇 시간 전 11시에 Nuxt3가 또 발표됐습니다. 두근대는 마음으로 만지작 거리다가 오랜만에 블로그를 업데이트해야겠다는 생각이 들어서 바로 시작합니다! 조만간  Nuxt에 대한 내용도 포스팅하고 싶군요 두근두근.

 

 아무튼 그 동안 Vue3도 나오고 Vite도 2 버전으로 업그레이드되었습니다. Vite를 처음 접했을 때의 그 놀라움을 아직도 잊지 못하고 있습니다. 항상 VueCLI로만 작업을 하던 저에게 새로고침 속도의 미학을 알려주었죠. (감동)

에반유. 당신은 정말..

그래서 여러분께도 그 감동을 전해드리고 싶었어요. 자 그럼 각설하고 바로 앱 만들기 시리즈 첫번째. 환경설정에 들어가 보도록 하겠습니다.


1. Vite 설치

npm init vite@latest
// or
yarn create vite

 뒤에 프로젝트 이름을 넣을 수 있는 데 그냥 바로 실행하면 아래 처럼 이름과 옵션을 고를 수 있어요.

~$ yarn create vite

yarn create v1.22.15
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Installed "create-vite@2.6.6" with binaries:
      - create-vite
      - cva
✔ Project name: … vite-electron-test
? Select a framework: › - Use arrow-keys. Return to submit.
    vanilla
❯   vue
    react
    preact
    lit
    svelte

타입스크립트는 vue-ts를 골라주면 되는데 일단 저는 vue 선택해서 Javascript로 진행합니다.

✔ Project name: … vite-electron-test
✔ Select a framework: › vue
? Select a variant: › - Use arrow-keys. Return to submit.
❯   vue
    vue-ts

그러면 완료. 들어가서 종속성을 설치하고 실행합니다.

Done. Now run:

  cd vite-electron-test
  yarn
  yarn dev

✨  Done in 262.96s.

그리고 yarn dev 또는 npm run dev 하면

짜장

라라벨 Watch나 VueCLI serve를 쓰다 보니 속도 하나에도 감동을 느낍니다. 물론 쟤네가 느린 건 아니지만..

localhost:3000만큼 사랑해

완성된 첫페이지입니다.

 

그리고 폴더구조는 아래와 같아요.

아무것도 없지요

아무것도 없어요. 이제부터 하나하나 설치해봅니다.

 

2. Electron 설치

 바로  Dev 디펜던시에 추가해줍니다.

npm install --save-dev electron
//or
yarn add -D electron

설치한 결과  package.json

{
  "name": "vite-electron-test",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview"
  },
  "dependencies": {
    "vue": "^3.2.16"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.9.3",
    "electron": "^15.1.2",
    "vite": "^2.6.4"
  }
}

자 이제 설치했으니 다음 단계로!

 

3. Vite 빌드

 Vite를 빌드하면 dist 폴더에 빌드 파일이 생성됩니다. 그걸 Electron이 읽을 수 있도록 만들어볼 거예요.

npm run build
//or
yarn build

빠른빌드

빌드를 하면 dist폴더에 static파일들이 생성된 것을 확인할 수 있습니다. 이제 Electron이 얘를 읽을 수 있게 경로 설정할 거예요.

 

다음은 Electron 설정을 시작해보죠!

 

4. Electron 파일 설정

Electron 빠른 시작 페이지 맨 밑에 보면 코드가 싹 다 나와있습니다.

링크: https://www.electronjs.org/docs/latest/tutorial/quick-start 

 

네가지 파일 정도를 신경써줘야합니다. 실제로는 3개파일이예요.

- 1. main.js

- 2. preload.js

- 3. index.html(있는거 그대로)

- 4. package.json

 

저 중에 2.preload.js는 세 번째 시리즈(③ 데스크탑 앱 만들기 #3 - ipcMain, ipcRenderer)에서 통신을 위해 필요하니까 파일만 만들면 두시면 됩니다. 그리고 index.html은 그냥 가만히 냅두면 되고 package.json파일은 만들 필요 없으니 main.js파일 경로랑 "start"스크립트만 추가하면 됩니다.

 

package.json

{
  "name": "vite-electron-test",
  "version": "0.0.0",
  "main": "main.js", // <-- 요기 추가
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview",
    "start": "electron ." // <-- 요기 추가
  },
  "dependencies": {
    "vue": "^3.2.16"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.9.3",
    "electron": "^15.1.2",
    "vite": "^2.6.4"
  }
}

main.js 파일명이나 script에 "start" 명령은 좋아하는 걸로 바꿔주세요. 편하신 걸로!

 

main.js 생성

const { app, BrowserWindow } = require('electron')
const path = require('path')

function createWindow () {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('./dist/index.html') // <-- 요기에 './dist/'를 추가!

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()
}

app.whenReady().then(() => {
  createWindow()

  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

 

preload.js 파일 생성

// 냉무

 

자 끝났습니다. 이제 시작해볼까요?

 

5. 실행 그리고 오류 고치기

 설정은 대략 끝났으니 실행해봅니다.

npm run start
//or 
yarn start

짜잔! 빈 화면이 보입니다. 그리고 콘솔 창에 이런 오류가 있을 거예요.

뭐라는 거야 대체.

dist안에 있는 index.html 파일을 열어봅니다.

 

파일들의 경로가

/assets/index.860ec699.js

이런 식으로 지정되어 있을 거예요. Electron은 root폴더에서 실행이 되는데 asset폴더는 root폴더에 있는 게 아니란 말이죠. 그래서 저 경로들을 모조리 dist폴더를 향하게 만들 겁니다.

 

vite.config.js로 가볼게요.

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
const path = require("path"); // <-- 얘 추가하고

// https://vitejs.dev/config/
export default defineConfig({
  base: path.resolve(__dirname, "dist"), // <-- 얘 추가하고
  plugins: [vue()],
});

저 두 줄을 추가하고 다시 빌드합니다.

 

그리고 다시 dist폴더 안에 index.html 파일에 소스 경로를 확인해보면

/Users/my-name/electron/vite-electron-test/dist/assets/index.860ec699.js

 

요로케 뭔가 길어진 것을 확인할 수 있습니다. 그러니 이제 root에 있는 main.js가 읽어 들일 수 있게 변했군요.

 

그럼 다시 "start"해봅시다.

깔끔!

 

6. 마무리

 끝났습니다. 이제 Vite랑 Vue3랑 Electron을 실행할 준비가 됐어요. 근데 개발을 하려면 소스코드가 변경되었을 때 알아서 바뀌게 하고 싶잖아요? 매번 코드 바뀔 때마다 빌드하고 "start"해주는 건 너무 번거롭잖아요? 그래서 다음 포스팅은  Hot reload기능을 구현해보도록 하겠습니다!

 

그럼 다음 시리즈에서!

 


너무 깔끔해.. 젠장, 기분 좋아.
너무 편리해서 놀랍도다!

 

아리땁군요

 


 이미지를 여러 가지로 만지고 놀던 중에 이미지 편집 라이브러리가 필요하게 되었습니다. 직접 만들어 볼 생각은  하지도 않았습니다. 그리고 찾은  게  CropperJS인데요. 너무 간편하고 편리해서 사용법을 공유하고 싶습니다.

 

 문서보고 적용하는 것이 어렵지는 않지만 한 가지 실수를 하는 바람에 2시간 정도를 꼬박 검색하는데 날린 거 같아요. 여러분은 실수하지 마시라고 팁도 넣어드립니다.

 

 적용하는데 있어서 헷갈리지 않도록 기본 코드만 넣어보았어요. 적용이 완료되면 아래처럼 보입니다.

 

저도 아리따운 분을 모셔왔어요!

 바로 코드로 들어갑니다!

 

test.vue

<template>
<div>
  <div class="w-full p-4 text-center bg-green-200 rounded-t-md">
    <div class="flex justify-between pb-2">
        <h3 class="text-lg font-medium text-gray-900">
            CropperJS!
        </h3>
        <!-- "X" image : heroicons -->
        <svg class="h-4 w-4 text-gray-700 cursor-pointer sm:mr-4" 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="M6 18L18 6M6 6l12 12" />
        </svg>
    </div>
    <div class="flex">
        <div class="w-3/4 mr-4">
            <img ref="image" :src="src" class="hidden" alt="image-edit">
        </div>
        <div class="w-1/4">
            <div class="pb-2">Preview</div>
            <div class="flex items-center justify-center h-32 bg-yellow-100">
                <div class="preview overflow-hidden w-full h-32 text-center bg-gray-200"></div>
            </div>
        </div>
    </div>
  </div>
  <div class="bg-teal-100 p-4 text-right rounded-b-md">
    <button type="button" class="w-32 p-2 m-2 border rounded-md shadow-sm bg-red-600">
      Save
    </button>
    <button type="button" class="w-32 p-2 m-2 border rounded-md shadow-sm bg-white">
      Cancel
    </button>
  </div>
</div>
</template>

 먼저 HTML 구성에서 15번째 줄에 img 태그가 있는데 hidden(display:none;)으로 처리해서 보이지 않게 해 주세요. 그리고 20번째 줄에 있는 preview 클래스가 중요합니다. 바로 그 곳에 CropperJS가 미리보기를 넣어줘요. 나머지는 입맛에 맞게 바꾸셔도 됩니다.

<script>
import Cropper from 'cropperjs'
import 'cropperjs/dist/cropper.css'

export default {
  name: 'Home',
  mounted() {
    this.image = this.$refs.image
    this.cropper = new Cropper(this.image, {
        preview: '.preview',
    })
  },
  computed: {
  },
  data: ()=>({
    src: '/images/girl.jpg',
    image: {},
    cropper: {}
  }),
  methods: {
  }
}
</script>

 제가 2시간동안 헤맨이유가 여기에 있습니다. 3번째 줄에 보면 css를 꼭 import 해주세요. 가이드 문서에서도 저렇게 하라고 알려주는데 그걸 놓치고 아까운 시간을 날려버렸습니다. 만약 추가를 안 해주면 편집 컨테이너가 뜨는 게 아니라 이미지만 떠버려서 편집이 불가능해집니다.

 

 그럼 CropperJS에게 이미지의 위치를 Vue의 경우 refs속성(javascript의 경우 getElementById도 가능)을 이용해서 위치를 알려줍니다. 그리고 생성된 인스턴스를 data cropper에 저장하구요. 그리고 option으로 preview의 위치를 preview class 선택자를 이용하여 알려주면 됩니다.

<style scoped>
img {
    display: block;
    max-width: 100%; /* This rule is very important, please do not ignore this! */
}
</style>

 이제 마지막입니다. 위에 script에서 css를 import해주고 나서도 한 가지 해줘야 하는데 css를 추가해줘야 합니다. 이걸 추가 안 하게 되면 컨테이너에 이미지가 자기 사이즈대로 보여서 작게 보이든, 크게 보이든 이쁘게 보이지 않아요.

 

 자, 이제 끝났습니다. 나머지 여러가지 기능들을 추가할 수 있는데 위에 만들어 놓은 cropper 인스턴스를 이용하면 쉽게 메소드를 만들 수 있습니다. 예를 들면 이런 식입니다.

this.cropper.reset() // 원본으로 돌아가기
this.cropper.zoom(0.2) // 확대
this.cropper.zoom(-0.2) // 축소

 

 참 쉽죠? 기능들이 다양하고 많으니 라이브러리에 대한 설명은 아래 링크를 만들어 놓을게요!


라이브러리 링크!

fengyuanchen.github.io/cropperjs/

 

Cropper.js

 

fengyuanchen.github.io


성공하면 쉬워 보이는구나

 

이미지 업로드 마지막 포스팅!

여러개의 이미지를 한번에 등록해보자


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

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

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

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

 

 

 

이미지 다중 업로드 & 삭제

 이번에는 이전 코드를 조금 수정해서 이미지를 여러 개를 한 번에 업로드가 가능하도록 만들고, 등록된 이미지를 삭제하는 것까지 해보겠습니다.

 

 미리 말씀드리지만 이미지 삭제의 경우 FRONT END에서 이미지의 URL만 삭제되게 했습니다. Amazon S3에는 이미지가 저장되어 있는 상태니까 S3에서 삭제 규칙을 생성해주시거나 직접 삭제해 주어야 합니다.

 

자 바로 FRONT END로 코드로 가보겠습니다.

 

▶ FRONTEND

src/views/upload.vue

<template>
<div class="container mx-auto text-sm">
  <div class="grid grid-cols-4 gap-2">
    <div v-for="num in 4" :key="num" class="flex justify-center text-center">
      <div class="w-32 h-32 mb-4 border-2 border-dotted border-blue-500">
        <div v-if="images[num]" class="w-full h-full flex items-center justify-center"
             @mouseenter="showImageMenu(num, true)"
             @mouseleave="showImageMenu(num, false)">
          <img :src="images[num]" :alt="`image${num}`">
          <div v-if="show[num]" class="absolute">
            <div class="h-8 w-8 flex justify-center items-center rounded-full bg-blue-500"
                 @click="removeImage(num)">
              <svg class="w-6 h-6 text-white" 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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
              </svg>
            </div>
          </div>
        </div>
        <div v-else class="w-full h-full flex items-center justify-center cursor-pointer hover:bg-pink-100"
             @click="clickInputTag(num)"
             @drop.prevent="dropInputTag(num, $event)"
             @dragover.prevent>
          <input :ref="`image${num}`"
                 type="file" name="image" accept="image/*" multiple="multiple"
                 class="hidden"
                 @change="uploadImage(num)">
          <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>
          Image {{ num }}
        </div>
      </div>
    </div>
  </div>
</div>
</template>
<script>
import axios from 'axios'

export default {
  data: ()=>({
    images: [],
    show: []
  }),
  methods: {
    removeImage: function(num) {
      this.images.splice(num, 1, null)
    },
    showImageMenu: function(num, bool) {
      this.$set(this.show, num, bool)
    },
    uploadImage: function(num, file) {
      let images = file || Array.from(this.$refs[`image${num}`][0].files, v => v)
      
      for (let i = 0; i < images.length; i++) {
        let form = new FormData()
        let image = images[i]
        
        form.append('image', image)
		
        if (num+i < 5) {
          axios.post('/upload', form, {
              header: { 'Content-Type': 'multipart/form-data' }
          }).then( ({data}) => {
              this.$set(this.images, num+i, data)
          })
          .catch( err => console.log(err))
        }
      }
    },
    dropInputTag: function(num, event) {
      let file = Array.from(event.dataTransfer.files, v => v)
      this.uploadImage(num, file)
    },
    clickInputTag: function(num) {
      this.$refs[`image${num}`][0].click()
    }
  }
}
</script>

 

1. INPUT TAG를 4개로 변경

 먼저 HTML 코드에서 v-for를 이용해서 4개의 INPUT 태그를 만들어줍니다. 그리고 ref에도 num을 붙여서 중복되지 않도록 체크해줍니다.

 

2. clickInputTag 메소드 변경(INPUT 태그 클릭했을 때)

 INPUT 태그를 클릭했을 때 어떤 태그를 클릭했는지 알 수 있도록 num인자를 추가해줍니다(다른 메소드도 동일해요). 아, 그리고 ref의 숫자가 늘어나면서 this.$refs['images${num}']의 값이 배열이니까 [0]을 붙여주어야 제대로 작동합니다.

 

3. dropInputTag 메소드 변경(INPUT 태그에 이미지를 drop 했을 때)

 이미지를 drop했을 경우 uploadImage 메소드의 인자로 들어가는 file 변수의 경우, 1개일 때 Array.from 끝에 [0]을 붙일 때와 다르게 이것만 지워주면 배열의 형태로 전달이 됩니다. 심플하죠?

 

4. uploadImage 메소드 변경(반복문을 통해 여러 번 동작하도록 함)

 이제 여기에서 Array.from 안에 files뒤에 [0]을 지워주면 images 변수에 배열의 형태로 저장이 됩니다. 그럼 for문을 이용해서 반복적으로 이미지를 S3에 저장하고 URL을 받아오면 됩니다.

 이때 저희는 이미지를 업로드할 수 있는 INPUT 태그를 4개를 만들었고, num의 인자가 1부터 4까지 반복되도록 만들었으니, 이미지를 여러 개 업로드하더라도 5가 초과되는 이미지의 경우 upload 되지 않도록 if문을 작성해줍니다.

 

5. 이미지 업로드 성공 이후에 마우스 오버 시 삭제 가능하도록 변경

 @mouseenter, @mouseleave 속성을 이용해서 showImageMenu 메소드가 실행이 되도록 만들어서 svg가 마우스 오버가 되었을 때 보이도록 만들어줍니다. 그리고 이 svg를 클릭했을 때 removeImage 메소드가 실행되도록해서 이미지의 url을 삭제해줍니다.

 이때 중요한 것은 splice의 세 번째 인자를 빈 문자열 값('')을 설정해서 뒤에 있는 이미지가 앞으로 당겨지지 않도록 해야 완벽해지죠.


 드디어 이미지 업로드 시리즈를 끝냈습니다. 뿌듯하군요.

 

혹시 보면서 불편한 부분, 이해가 안 되는 곳 등 의견이 있으면 언제든 알려주세요! 최대한 수정하고 반영하도록 하겠습니다. 그러면서 저도 배울 수 있거든요!

 

그럼 다음에 다른 주제로 뵙겠습니다. 안녕히!


▶ BACKEND 코드는 이미지 업로드하기 #1 같습니다! 


 


배우는 즐거움을 함께 느꼈으면 좋겠어요!

 

레거시 코드 수정하다 만난 복병

 

 


 오늘의 포스팅은 누군가가 애타게 찾아 헤맬지도 몰라 작성합니다.

 

 저는 예전에 만든 웹사이트가 PHPmailer 라이브러리를 사용해서 아이디/비밀번호 찾기를 구현해놓았었습니다. 그런데 그 계정을 카카오 계정으로 통합한 이후에 작동이 안 되어 방법을 찾으면서 시간을 허비했는데, 누군가는 제 글을 읽고 시간을 아꼈으면 합니다.

 

 꼭 PHPmailer를 쓰는 사람이 아니더라도 stmp나 아웃룩, 또는 모바일에서 이메일을 연동해서 사용하는 경우에 도움이 될 것 같습니다.

 

먼저 다음메일을 들어갑니다.

 

 환경설정으로 가면 다음처럼 SMTP 서버명과 포트번호, 아이디 등이 뜨게 되는데 카카오 통합계정의 경우 아이디는 arikong@daum.net처럼 뒤에 골뱅이가 붙습니다. 하지만 아웃룩이나 라이브러리를 사용하는 경우에는 아이디를 arikong으로사용하게 됩니다. 이점 유의하세요.

 

 STMP 서버명도 smtp.kakao.com이 아니라 smtp.daum.net임을 명심해야합니다. 카카오 메일로 카카오 계정을 가진 사람의 경우 smtp.kakao.com 이 되지만 다음 계정을 카카오로 통합시킨 경우에는 smtp.daum.net 이 되니 이점도 헷갈리지 마세요!

 

자, 그럼 이제 카카오계정관리로 들어갑니다.

 계정 보안탭에 보면 2단계 인증이 있는데 여기서 앱 비밀번호를 따로 만들어야 합니다.

 

 그러니까 카카오 통합계정으로 직접 로그인하는 것이 아닌 아웃룩이나 모바일에서 이메일 가져오기, 또는 저처럼 라이브러리를 이용해서 접속을 하고 싶은 사람은 본인의 진짜 비밀번호가 아니라 "앱 비밀번호"라는 것을 만들어서 원래 비밀번호가 유출되는 것을 방지하는 보안 방법인 것이죠.

 그리고 앱 비밀번호로 들어갑니다. 저는 만들어 둔 게 있어서 1로 나오지만 여러분은 없다고 뜰 수도 있어요.

 이제 마지막입니다. 앱 비밀번호에 이름을 쓰고 "생성"버튼을 누르면 임의의 문자가 뜨는데 저 문자를 복사하기 해서 아웃룩이나 라이브러리, 모바일 이메일 가져오기 로그인 시에 "비밀번호"로 사용해주면 됩니다.

 

 해결되셨으면 좋겠네요!


누군가에게는 도움이 되었기를
구현하기 전에 왜 그렇게 겁냈을까.. 몇 줄만 추가하면 되는 것을!

드래그 앤 드랍 구현하기


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

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

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

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

 

 

 이전 이미지 업로드하기 #1 에서는 클릭을 통해 이미지 업로드를 했다면 이번에는 드래그 앤 드랍을 통해 이미지 업로드하는 방법에 대해 포스팅하겠습니다.

끌어서 놓기

  ▶ FRONTEND

src/views/upload.vue

<template>
<div class="container mx-auto text-sm">
  <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()"
          @drop.prevent="dropInputTag($event)"
          @dragover.prevent>
      <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>
</div>
</template>

 먼저 추가된 코드는 input 태그 바로 위에 div 태그에 @drop.prevent와 @dragover.prevent 입니다. 요 두 가지 이벤트만 등록해두면 이미지를 드래그해서 갖다 놓을 수 있게 됩니다. 참 쉽죠? 그리고 drop event가 발생했을 때 dropInputTag 메소드가 실행되면서 인자로 event를 전달해서 업로드할 이미지의 정보를 읽을 수 있게 합니다.

<script>
import axios from 'axios'

export default {
  data: ()=>({
    images: ''
  }),
  methods: {
    uploadImage: function(file) {
      let form = new FormData()
      let image = file || 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))
    },
    dropInputTag: function(event) {
      let file = Array.from(event.dataTransfer.files, v => v)[0]
      this.uploadImage(file)
    },
    clickInputTag: function() {
      this.$refs['image'].click()
    }
  }
}
</script>

 위에서 drop event가 실행되면 dropInputTag 메소드에서 파일에 대한 정보를 읽습니다.

 

 event.dataTransfer.files에 이미지의 정보가 담겨있는데 이 배열이 "유사 배열"입니다. 유사 배열은 배열이지만 배열 같지 않은 이상한 녀석인데요. 이 유사 배열을 얕게 복사해서 배열처럼 사용할 수 있게 해주는 것이 Array.from 메소드입니다. 유사 배열에 대해 나중에 기회가 되면 더 자세히 포스팅하도록 하겠습니다(설명하려면 저도 공부를 더 해야 하기에...).

 

 어쨌든 이미지에 대한 정보를 file 변수에 넣었으니 그냥 원래 있던 uploadImage 메소드에 인자로 넣어서 전달해주겠습니다. 이전 포스팅에서와는 다르게 원래 uploadImage 메소드에 file이라는 인자를 넣어서, 인자가 전달이 되면 전달된 인자를 image변수에 넣어주고, 인자가 전달이 안되면 클릭해서 실행했을 때와 동일하게 image를 읽어오는 방법으로 구현했습니다.

 

 나머지는 똑같아서 따로 설명할 것도 없네요.


▶ BACKEND 코드는 이미지 업로드하기 #1 와 같습니다! 


 포스팅이 시리즈로 나누어져 있는 것이 의아해하시는 분들이 계실 것 같아서 말씀드립니다. 굳이 제가 코드를 나눠서 업로드하는 이유는 제가 배우는 입장이었을 때 이 방법이 가장 좋았기 때문입니다. 너무 길거나 여러 기능을 포함하는 코드는 보고도 어디가 어떻게 작동하는지 이해가 어렵더라구요.

 

 나중에 이미지 업로드 시리즈의 마지막 포스팅에서 제가 구현하려고 한 모든 기능이 들어간 코드를 포스팅할 예정입니다.

 

 그럼 다음에는 여러 개의 이미지 파일을 선택해서 한 번에 올리는 방법과, 올린 이미지를 삭제하는 방법을 포스팅하도록 하겠습니다.


세상에 고수는 많으니 저는 섬세한 컨셉으로
어려워 보였지만 쉬웠다.


① 이미지 업로드하기 #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