TIL 01 - 이미지와 AWS S3

양진영·2023년 1월 16일
0

어떠한 일을 맡아서 진행하든 해보지 않았던 일들이 대부분이라 대게 처음에는 무척 어렵게 느껴진다. 높은 확률로 하다보면 어찌 어찌 진행해 나가는 내 자신을 찾아볼수있다. 하지만 반대의 경우도 있다. 구글링으로 찾아봐도 이게 무슨 소리인지 도대체 이기능이 여기서 어떤 목적으로 쓰이는건지 모를때가 많다. 그렇게 되면 참 곤란하다. 왜냐하면 그저 구글링으로 코드 카피했을때 이게 안됄때는 왜 안돼는지 아예 감도 안잡히기 때문이다. 나에게 있어 외부서비스와의 통신으로 원하는 기능을 개발하는 것들이 어려웠고 지금도 어렵다. 외부서비스... 사실 앞으로 정말 많이 맞닥드릴것이다. AWS, GCP를 비롯한 클라우드 서비스 및 결제할때 사용할수있는 toss나 여타 다른 결제서비스 연동등 이 있을것이다.

하지만 어렵다고 아몰랑 하고있을수는 없기에 오늘처럼 외부 서비스를 이용하거나 했을때는 기록을 남기려 한다. 사실 오늘 했던일은 동료인 ㄴㅇ없이는 해낼수없었을것이다. 정말 오늘 하고자 하는 기능에 대한 이해가 1도 없는 상황에서 천천히 이해 시켜줬던 ㄴㅇ에게 커다란 감사를 표하고자 한다. 그럼 지금부터 본격적으로 오늘의 시행착오를 써보려 한다.

그래서 오늘 한게 뭔데?

오늘 한일은 AWS S3에 이미지를 업로드 한 업무를 하였다. 뭐 고수들이 봤을때는 저게 어려워...? 이딴게 서버개발자...? 라고 할수도있겠지만 내 딴에는 정말 어려웠다. 사실 그냥 이미지를 업로드 하는것이라면 이미 완성했었다. 하지만 나는 이미지를 multipart/form-data를 사용하여 바로 s3와 통신하여 올리는게 아니라 일단 이미지를 로컬 서버로 가져와 업로드 하는 로직을 함수로 빼서 재사용성을 높히고자 하였기에 시행착오를 겪게 되었다. 그럼 이제부터 기능을 만들며 몰랐던 부분에 대해 정리하여 올리고자 한다.

시행착오1 - multer가 하는 역할이 뭐지?

multer는 middleware이다. 어떠한 역할을 수행하는 middleware냐면 클라이언트로 부터 파일을 multipart/form-data형태로 입력받았을때 file(다중이라면 아마 files라는 이름으로 했을것이다.)안에 있는 컨텐츠를 req안에 넣어주는 역할을 한다. 그러니까 즉 클라이언트가 요청했을때 요청정보가 바로 로직을 수행하는 컨트롤러로 가는것이 아니라 multer를 거쳐 multer가 클라이언트로 부터 받은 파일정보를 req 안에 넣어줘서 컨트롤러에서 파일정보를 받아쓰는 역할이라고 보면 편할것이다.

client -> multer -> 컨트롤러 순으로 보면되고, multer없이는 multipart/form-data로 전달한 파일은 컨트롤러에서 확인했을때 undefined로 뜰것이다.

시행착오2 - multer랑 multerS3랑 무슨차이?

사실 나는 같은건줄 알았다... 참으로 부끄럽지만 나와 같은 것을 고민하고 있을 사람들에게 간단하게 설명하자면 multer는 mutipart/form-data를 가져와 로컬서버에서 파일을 인식하게 해주는 미들웨어라면 multerS3는 aws와 통신하여 multipart/form-data로 요청한 파일을 로컬로 가져오는게 아니라 바로 aws s3로 쏴주는 역할로 보면 편할것이다. 참고로 multerS3는 multer-s3라는 라이브러리를 인스톨 받아 사용할수있다.

시행착오3 - 실제로 s3에 전송은 어떻게 하나?

위에서 살짝 언급했던것이지만 나는 이번에 multerS3를 사용하여 s3로 다이렉트로 보내는것이 아닌 multer를 이용하여 일단 서버로 가져와서 그것을 재활용할수있는 형태의 로직을 만들고 싶었다. multer로 file을 request객체 안에 담아 우리 컨트롤러 안에서 파일을 인식할수있게 했다. 그렇게 우리 컨트롤러 내에게 인식한 파일을 가지고 로직을 수행시켰다면 이제 그 결과물을 s3로 보내주어야 했다. 나는 이때 PutObjectCommandInput을 이용하였다. aws-sdk/client-s3라이브러리 에서 import해서 사용할수 있는 기능으로 params에 타입으로 PutObjectCommandInput타입을 입혀 사용하였다.

import { PutObjectCommandInput } from '@aws-sdk/client-s3';
import { s3Client } from './aws';
// s3Client의 역할은 직관적으로 나와있지만 s3와 통신할수 있는 S3Client객체이다.
// s3Client를 import해온 ./aws 내용...
// AWSSDK.config.update({
//  credentials: {
//    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
//    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
//  },
// });
//
// export const s3Client = new AWS.S3({
//  region: process.env.AWS_REGION as string,
// });

const params: PutObjectCommandInput = {
	Bucket: aws s3에 만들어둔 bucket명,
    Key: 파일이 저장될 경로  ex.) abc/hahaha.jpg 라고하면 만들어둔 bucket안에 abc폴더안에 hahaha.jpg라는 파일이 업로도 될것임.
    Body: file.buffer  파일의 내용으로는 buffer형태를 넣어준다.
}

const result = await s3Client.putObject(params)
  if (result) return key;
  else throw new Error('파일이 없습니다.');

if해서 경로(key)만 넘긴 이유는 key만 넘겼다가 리사이징 될 url host부분만 클라이언트에서 적어주고 나머지 부분은 key로 입력받을수 있게 하려함

  • 그냥 s3호스트 주소와 리사이징해서 저장되는 주소의 경로는 다를수있기 때문에 키만 남겨주고 리사이징된 주소는 나중에 클라이언트가 줄수있게 하려하는데 쉽게 이해가 갔을지 모르겠다...
    ex ) s3.ap-northeast-2/image1.jpg 를 그냥 업로드 해서 저장될 이미지라고 한다면 s3.ap-northeast-2/resize/image1.jpg 요런식으로 경로가 바뀔수있어서 내가 전달해주는건 image1.jpg이고 나중에 클라이언트가 s3.ap-northeast-2/resize 까지만 적어주면 그뒤에 파일 경로는 내가 리턴해준 값을 넣어주면 된다.

사실 이거 외에도 부분부분 모르는 것들은 많았다. 하지만 그것들은 일단은 파일업로드 주제와는 조금 벗어난 것들일수 있기에 일단 내가 공부해 두었다가 나중에 주제가 맞다고 생각했을때 올리도록 하겠다. 다시한번 ㄴㅇ에게 감사함을 표하며 오늘의 블로그는 여기서 마치도록 하겠다.

profile
왜? 라는 질문을 중요시하는 서버 개발자입니다

0개의 댓글