처음 웹개발을 배우는 사람들은, 파일 저장을 배울 때 이렇게 배웠을 것이다. '백엔드에 formData를 전달해, 로컬 서버(내 컴퓨터)에 파일을 저장한다' 고 ...(나도 그랬음)
하지만 S3는 내 컴퓨터가 아닌 외부 서버이다. 즉, 남의 컴퓨터에 파일을 저장한다는 뜻이다. 따라서 굳이 백엔드로 전달해 어쩌구 라이브러리를 써서 설정한 다음 내 컴퓨터(로컬)에 저장하고 뭐시기.... 라는 긴 과정을 거칠 필요가 없다.
aws의 aws-sdk 라는 라이브러리를 설치해 사용 방법 대로만 해주면, 프론트엔드에서 바로 S3로 파일을 전송해 저장할 수 있다. S3의 버킷은 우리에게 URL 형태의 파일을 제공해주므로 그를 돌려받아 페이지에 바로 뿌릴 수도 있다. 물론 multer와 aws-sdk, multer-3s를 이용하면 Node.js 환경의 백엔드에서 동작하게 하는 것 또한 당연히 가능한다. 두가지 모두 실습해 볼 것인데, 먼저 프론트에서 처리하는 법을 실습해 볼 것이다.
React에서 aws-sdk 를 이용하여 내 파일 바구니에 이미지를 저장해보자.
벌써 Node.js보다 간단한 것 같다. 실제로 얼마 안 걸린다.
React에서 aws-sdk로 S3 버킷에 이미지 저장하기
사전 준비로는 IdentityPoolId 이 필요하다. 지난 시간에 진행한 것(LINK)을 통하여 IdentityPoolId을 취득하였다. 복사해서 살포시 텍스트 파일에 저장해두자.
프론트엔드를 만들어주었다. (css파일을 따로 만들 필요 없도록 그냥 style 태그를 이용하였다.
function Previews() { 이부분만 } 을 App.js에 복사해 붙여넣기 해서 사용해도 되고, Previews라는 컴포넌트를 만들어 이용해도 된다. 아래와 같이 생긴 간단한 앱이다. 이미지를 업로드하면 미리보기를 제공한다. 제목을 입력하면 Axios를 통해 mongodb에 저장할 것이지만, 이 글에서 해당 부분은 구현하지 않는다.
이 글을 통해 구현할 부분은 [업로드]를 눌렀을 때 S3에 저장하게 하는 코드이다. 따라서 아래 코드에는 해당 부분이 아직 구현되어 있지 않다. S3 저장이 모두 구현된 풀버전 코드는 글 맨 밑에 작성하였다.
import React, { useRef, useState } from "react";
function Previews() {
const uploadIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0klEQVR4nO2UWQ6CQBAF+8cbavjQ4+BJ1VNAUgaXxI1lIJPuwVcJP8yQVE1nMBMiLkDVPVYiwA5ogBY4WKHyT8qJ4Fu+nAj65eNHcL+wn/JNz7uqhJNvgT2w7VmLMYkh+Zc9MSOYIB82ggT5cBHMkA8Twfiv8saP78Zos0dMlZ8ZkDciRX5BQJ6IVPmFAW4X+43UdXcU4I0m4I0m4I0m4I0m4M3qJjCGRQO4MJ2TRQM4JgTUFg1g84g4D4h3a3W319tXCCEyQXBMAc7Y6icgxJ9xBUOuyY7C/8ZJAAAAAElFTkSuQmCC'
const imageInput = useRef();
const[files,setFiles] = useState('')
const [imageSrc, setImageSrc] = useState('');
const [imgname,setImgname] = useState('')
// <=========== Savebutton Click event ===========>
function saveEventhandler() {
// [업로드] 버튼 클릭이벤트
}
const onClickInput = () => {
imageInput.current.click();
}
const onLoadFile = (e) => {
const file = e.target.files;
console.log('onLoadFile',file)
setFiles(file);
console.log('state에 저장 완료 files',files)
let fileBlob = e.target.files[0]
const reader = new FileReader();
reader.readAsDataURL(fileBlob);
return new Promise((resolve) => {
reader.onload = () => {
setImageSrc(reader.result);
resolve();
};
});
}
return(
<div style={{position:'relative'}}>
<div>
<input type="file"
style={{display:'none'}}
onChange={onLoadFile}
ref={imageInput}
/>
<img
style={{
position:'absolute',
top:'20%',
left:'44%',
cursor:'pointer'
}}
src={ imageSrc ? '' : uploadIcon}
onClick={onClickInput}/>
</div>
<img
className="uploadImage"
style={{
minWidth:'150px', minHeight:'150px',maxWidth:'500px', border:'1px solid lightgray',
alignItems:'center', justifyContent:'center', cursor:'pointer'
}}
onClick={onClickInput}
src={imageSrc? imageSrc : ''}/>
<div style={{marginTop:'50px'}} >
<input type="text" placeholder="제목을 입력해주세요."
style={{width:'300px',height:'30px',outline:'none',
fontSize:'25px', color:'white',
borderLeftWidth:0,borderRightWidth:0,borderTopWidth:0,borderBottomWidth:1,
backgroundColor:"transparent"
}}
onChange={(e)=>{setImgname(e.target.value)}}
/><span
style={{marginLeft:'20px', fontSize:'20px', cursor:'pointer' }}
onClick={saveEventhandler}
className="saveButton"
>업로드</span>
</div>
</div>
)
}
export default Previews;
React 프로젝트에서 aws-sdk를 다운로드 해 준다.
npm install aws-sdk
src 폴더에 config 폴더를 만들고, config.js 파일을 만들어 복사해둔 region과 IdentityPoolld를 등록해준다.
export const awsRegion = '나의region을 이곳에 입력하세요 서울은 ap-northeast-2입니다';
export const awsIdentityPoolId = '나의id를 이곳에 입력하세요'
Previews.js 파일로 돌아와 config를 import 해 주고, 아래 코드를 추가한다. AWS.config.update는 AWS를 사용하기 위해 설정해 주어야 하는 기본값이다. 여기에 config에 저장한 지역명과, id를 연결해 주었다.
import * as config from 'config파일 위치'
import AWS from "aws-sdk"
function Previews() {
AWS.config.update({
region: config.awsRegion, // 버킷이 존재하는 리전을 문자열로 입력합니다. (Ex. "ap-northeast-2")
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: config.awsIdentityPoolId, // cognito 인증 풀에서 받아온 키를 문자열로 입력합니다. (Ex. "ap-northeast-2...")
}),
})
...
본격적으로 파일을 저장하기 위한 코드를 짜보자. [업로드] 버튼을 누르면 실행할 코드를 작성한다.
먼저, 사용자가 업로드할 파일의 크기를 제한해준다. 10mb 를 초과하는 파일은 업로드하지 못하게 할 것이다.
function saveEventhandler() {
if (files && files[0].size > (10 * 1024 * 1024)) {
alert("10mb 이하의 파일만 업로드할 수 있습니다.");
} else {
const files에 onLoad를 통해, 업로드된 파일이 임시 저장되어있다. 이 files의 [0]번째 배열에는 파일의 정보가 들어있다. 이를 변수를 선언해 저장해준다.
const uploadFiles = files[0]
이제 S3에 파일을 저장하기 위한 준비를 해보자. ManagedUpload를 통해, 파일을 업로드 할 수 있다. params에는 버킷 이름과 확장자를 포함한 업로드할 파일 이름, 업로드할 파일 그 자체를 넣어주어야 한다.
참고로, files[0].name에는 확장자를 포함한 파일의 풀네임이 들어있다. Key에 uploadFiles.name을 해 주면, 따로 파일 확장자를 추출해 설정하지 않아도 된다.
// Todo S3에 파일을 저장한다.
const upload = new AWS.S3.ManagedUpload({
params: {
Bucket: '버킷이름', // 업로드할 대상 버킷명
Key: '업로드할 파일이름' + '.확장자' , //파일 이름과 확장자
Body: uploadFiles, // 업로드할 파일 객체
},
})
만일 파일 이름과 확장자를 추출하고 싶다면 아래 코드를 사용하면 된다.(나는 사용하지 않았음)
// 파일 이름 추출
const fileName = files[0].name.substring(0,files[0].name.lastIndexOf('.'))
// 파일 확장자 추출
const fileExtension = files[0].name.substring(files[0].name.lastIndexOf('.'),files[0].name.length).toLowerCase()
promise 문법을 사용해, '업로드가 성공했을 경우' 와 '실패했을 경우'를 나누어 처리해주자. async-await을 사용해도 된다. promise가 성공하면, data에 성공한 데이터가 넘겨져온다.
const promise = upload.promise()
promise.then(
function (data) {
alert("이미지 업로드에 성공했습니다.")
console.log('업로드한 데이터',data)
},
function (err) {
return alert("오류가 발생했습니다: ", err.message)
}
)
이를 합치면 아래 코드가 된다.
function saveEventhandler() {
if (files && files[0].size > (10 * 1024 * 1024)) {
alert("10mb 이하의 파일만 업로드할 수 있습니다.");
} else {
const uploadFiles = files[0]
// Todo S3에 파일 저장 후 response로 파일 링크 받아오기
const upload = new AWS.S3.ManagedUpload({
params: {
Bucket: '버킷이름', // 업로드할 대상 버킷명
Key: '확장자를 포함한 파일명', //파일명+확장자
Body: uploadFiles, // 업로드할 파일 객체
},
})
const promise = upload.promise()
promise.then(
function (data) {
alert("이미지 업로드에 성공했습니다.")
},
function (err) {
return alert("오류가 발생했습니다: ", err.message)
}
)
}
}
s3 버킷에 이미지가 저장되는지 테스트 해 보자. 고양이 사진을 넣고 [업로드] 버튼을 클릭한 후에, 버킷을 새로고침 해 주었다.
성공적으로 저장되었다. [키]는 파일 이름, [객체 url] 은 파일을 다운할 수 있게 해주는 url이다.
다른 파일을 다시 올리고, promise가 성공했을 경우 전달받은 data를 콘솔로 출력해 본다. 이를 활용하면 버킷에 있는 데이터를 불러오고, 화면에 로드할 수 있을 것이다.
버킷에 있는 정보를 불러와 조회하는 것은 다음 글에서 해본다.
전체 코드
import React, { useRef, useState } from "react";
import axios from 'axios';
import AWS from "aws-sdk"
import * as config from '../config/config'
function Previews() {
// =========================== set AWS ===========================
AWS.config.update({
region: config.awsRegion, // 버킷이 존재하는 리전 (Ex. "ap-northeast-2")
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: config.awsIdentityPoolId, // cognito 인증 풀에서 받아온 키 (Ex. "ap-northeast-2...")
}),
})
const uploadIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA0klEQVR4nO2UWQ6CQBAF+8cbavjQ4+BJ1VNAUgaXxI1lIJPuwVcJP8yQVE1nMBMiLkDVPVYiwA5ogBY4WKHyT8qJ4Fu+nAj65eNHcL+wn/JNz7uqhJNvgT2w7VmLMYkh+Zc9MSOYIB82ggT5cBHMkA8Twfiv8saP78Zos0dMlZ8ZkDciRX5BQJ6IVPmFAW4X+43UdXcU4I0m4I0m4I0m4I0m4M3qJjCGRQO4MJ2TRQM4JgTUFg1g84g4D4h3a3W319tXCCEyQXBMAc7Y6icgxJ9xBUOuyY7C/8ZJAAAAAElFTkSuQmCC'
const imageInput = useRef();
const[files,setFiles] = useState('')
const [imageSrc, setImageSrc] = useState('');
const [imgname,setImgname] = useState('')
// <=========== Savebutton Click event ===========>
function saveEventhandler() {
if (files && files[0].size > (10 * 1024 * 1024)) {
alert("10mb 이하의 파일만 업로드할 수 있습니다.");
} else {
const uploadFiles = files[0]
// S3에 파일 저장
const upload = new AWS.S3.ManagedUpload({
params: {
Bucket: '버킷이름', // 업로드할 대상 버킷명
Key: `${Date.now()}_${uploadFiles.name}`, //업로드할 파일 이름(확장자 포함)
Body: uploadFiles, // 업로드할 파일 객체
},
})
const promise = upload.promise()
promise.then(
function (data) {
alert("이미지 업로드에 성공했습니다.")
},
function (err) {
return alert("오류가 발생했습니다: ", err.message)
}
)
}
}
const onClickInput = () => {
imageInput.current.click();
}
const onLoadFile = (e) => {
const file = e.target.files;
console.log('onLoadFile',file)
setFiles(file);
let fileBlob = e.target.files[0]
const reader = new FileReader();
reader.readAsDataURL(fileBlob);
return new Promise((resolve) => {
reader.onload = () => {
setImageSrc(reader.result);
resolve();
};
});
}
return(
<div style={{position:'relative'}}>
<div>
<input type="file"
style={{display:'none'}}
onChange={onLoadFile}
ref={imageInput}
/>
<img
style={{
position:'absolute',
top:'20%',
left:'44%',
cursor:'pointer'
}}
src={ imageSrc ? '' : uploadIcon}
onClick={onClickInput}/>
</div>
<img
className="uploadImage"
style={{
minWidth:'150px', minHeight:'150px',maxWidth:'500px', border:'1px solid lightgray',
alignItems:'center', justifyContent:'center', cursor:'pointer'
}}
onClick={onClickInput}
src={imageSrc? imageSrc : ''}/>
<div style={{marginTop:'50px'}} >
<input type="text" placeholder="제목을 입력해주세요."
style={{width:'300px',height:'30px',outline:'none',
fontSize:'25px', color:'white',
borderLeftWidth:0,borderRightWidth:0,borderTopWidth:0,borderBottomWidth:1,
backgroundColor:"transparent"
}}
onChange={(e)=>{setImgname(e.target.value)}}
/><span style={{marginLeft:'20px', fontSize:'20px', cursor:'pointer' }}
onClick={saveEventhandler}
>업로드</span>
</div>
</div>
)
}
export default Previews;
'AWS > S3' 카테고리의 다른 글
[AWS 3S] Node.js 에서 .env 파일을 이용하여 S3 버킷 이용하기 ( 버킷에 파일 저장 + 버킷 객체 목록 읽기 ) (1) | 2023.01.02 |
---|---|
[AWS S3] React 에서 버킷 속 객체 리스트 불러오기 ( ListObjectsV2 ) (0) | 2023.01.02 |
[AWS S3] AWS 버킷 생성과 권한 (1) | 2023.01.01 |
댓글