파일 데이터를 저장해야할 때 가장 단순한 방법은 다른 타입의 데이터들과 마찬가지로 파일도 데이터를 그대로 DB에 저장하는 방법일 것이다.
이럴 때에는 blob
을 사용할 수 있다.
blob
(Binary Large Object, 블랍)은 이미지, 사운드, 비디오와 같은 멀티미디어 데이터를 다룰 때 사용할 수 있는 JavaScript 타입으로 이름에서 알 수 있듯이 바이너리 데이터를 저장할 수 있다.
blob
에 대한 자세한 내용은 아래의 글을 참고하면 좋을 것 같다.
Blob(블랍) 이해하기
나는 blob
타입으로 직접 저장 후 프론트에 전달하는 것보다는 클라우드 스토리지에 이미지를 올리고 프론트에서 직접 접근할 수 있는 url을 전달해주는 것이 더 편할 것이라고 판단하여 클라우드 스토리지를 사용하였다.
(클라우드 스토리지 설정 다 하고 알게된 사실은 blob도 직접 데이터를 저장하는 것이 아니라 위치 포인터값을 저장한다고 한다...!)
클라우드 스토리지를 사용하는 방법은 파일을 클라우드 스토리지에 저장하고 파일 접근이 가능한 url을 DB에 저장하는 것이다.
이번 프로젝트에서는 AWS S3
를 사용하여 파일을 저장하였다. NestJS
로 구성한 서버에서 AWS S3
를 어떻게 사용할 수 있는지 알아보자.
aws s3 홈페이지
위의 링크로 이동하여 로그인을 하자.
로그인 후에는 파일을 저장할 Bucket
을 생성해야 한다.
해당 Bucket
안에 전송한 파일들이 모여서 저장된다.
원하는 버킷 이름을 지정해준다.
외부에서 url로 접근할 예정이기 때문에 모든 퍼블릭 액세스 차단을 해제해준다.
다른 설정은 그대로 유지한 채 버킷 만들기 버튼을 누르면 버킷이 만들어진다.
위에서 퍼블릭 액세스 차단을 해제해줬지만 기본적인 AWS의 정책은 Denied
이기 때문에 따로 정책을 정의해줘야 url을 통한 직접 접근이 가능하다.
아까 만들어준 버킷에 들어가 권한 메뉴의 버킷 정책을 아래와 같이 정의해주자. (버킷에 저장된 object에 대한 외부 접근을 Allow 로 설정해주는 정책이다.)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::[버킷 이름]/*"
}
]
}
이제 NestJs 서버에서 앞서 만들어준 버킷에 파일을 업로드하고 삭제하는 코드를 작성해보자.
우선 aws-sdk
를 사용할 것이기 때문에 npm을 통해 설치해준다.
npm i aws-sdk
설치가 완료되면 import 후 자신의 AWS accessKey
와 secretAccessKey
를 이용하여 사용할 객체를 생성할 수 있다.
import * as AWS from 'aws-sdk';
const s3 = new AWS.S3({
accessKeyId: 'your_aws_access_key',
secretAccessKey: 'your_aws_secret_key',
region: 'your_region',
});
생성한 객체를 사용하여 원하는 파일을 버킷에 업로드하고 삭제할 수 있다.
const key = `${Date.now() + '파일 데이터 이름'}`;
const params = { Bucket: 'your_bucket_name', Key: key };
await s3
.putObject(
{
Key: key,
Body: '파일 데이터',
Bucket: 'your_bucket_name',
},
(err) => {
if (err) {
throw err;
}
},
).promise();
putObject
함수를 사용하여 원하는 버킷에 파일을 업로드할 수 있다.
업로드하고 싶은 파일을 Body
부분에 넣어주면 된다.
key
값은 오브젝트마다 고유한 값을 가져야하기 때문에 현재 시간 데이터를 앞에 넣어주었다. (나중에 url 받아오거나 삭제할 때 key 값을 사용해서 오브젝트에 접근한다.)
업로드한 파일은 각자 고유의 url이 생성된다.
const params = { Bucket: 'your_bucket_name', Key: key };
const imageUrl: string = await new Promise((r) =>
s3.getSignedUrl('getObject', params, async (err, url) => {
if (err) {
throw err;
}
r(url.split('?')[0]); // return object url
}),
);
getSignedUrl
함수를 사용하면 버킷에 있는 오브젝트의 url을 받아올 수 있다.
이제 이 url을 DB에 저장하고 프론트에게 그대로 전달하면 프론트에서 url로 직접 접근하여 파일을 가져올 수 있다.
const deleteParams = {
Bucket: 'your_bucket_name',
Key: key,
};
await s3.deleteObject(deleteParams).promise();
deleteObject
함수를 사용하면 앞서 지정해줬던 key
값을 통해 오브젝트에 접근하여 해당 오브젝트를 버킷에서 삭제한다.
참고 자료
https://docs.aws.amazon.com/AmazonS3/latest/API/API_Operations_Amazon_Simple_Storage_Service.html
https://sangjuntech.tistory.com/20
https://ko.wikipedia.org/wiki/%EB%B0%94%EC%9D%B4%EB%84%88%EB%A6%AC_%EB%9D%BC%EC%A7%80_%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8
https://heropy.blog/2019/02/28/blob/