본문 바로가기
TIL

TIL #34) Nest로 S3 이용하기

by 해룸 2024. 3. 25.

Today I Learned

트렐로 프로젝트 중 생성한 카드에 이미지 업로드 하는 기능이 필요해 다음과 같은 코드를 작성했다.

먼저 AWS계정에 S3버켓을 생성하고, 그 버켓에 접근하기 위한 IAM 계정이 필요하다.

다른 글을 참조해 생성했고 대표적으로 권한 문제가 계속 있었는데;; 구글링을 통해 해결했다.

 

AWS S3 버켓에 올리기 위한 핵심적인 로직은 다음과 같다.

@Injectable()
export class AwsService {
  s3Client: S3Client;

  constructor(private configService: ConfigService) {
    // AWS S3 클라이언트 초기화. 환경 설정 정보를 사용하여 AWS 리전, Access Key, Secret Key를 설정.
    this.s3Client = new S3Client({
      region: this.configService.get('AWS_REGION'), // AWS Region
      credentials: {
        accessKeyId: this.configService.get('AWS_S3_ACCESS_KEY'), // Access Key
        secretAccessKey: this.configService.get('AWS_S3_SECRET_ACCESS_KEY'), // Secret Key
      },
    });
  }

  async imageUploadToS3(
    fileName: string, // 업로드될 파일의 이름
    file: Express.Multer.File, // 업로드할 파일
    ext: string, // 파일 확장자
  ) {
    // AWS S3에 이미지 업로드 명령을 생성합니다. 파일 이름, 파일 버퍼, 파일 접근 권한, 파일 타입 등을 설정합니다.
    const command = new PutObjectCommand({
      Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), // S3 버킷 이름
      Key: fileName, // 업로드될 파일의 이름
      Body: file.buffer, // 업로드할 파일
      ACL: 'public-read', // 파일 접근 권한
      ContentType: `image/${ext}`, // 파일 타입
    });

    // 생성된 명령을 S3 클라이언트에 전달하여 이미지 업로드를 수행합니다.
    await this.s3Client.send(command);

    // 업로드된 이미지의 URL을 반환합니다.
    return `https://s3.${process.env.AWS_REGION}.amazonaws.com/${process.env.AWS_S3_BUCKET_NAME}/${fileName}`;
  }

  async DeleteUploadToS3(fileName: string) {
    const params = {
      Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), // S3 버킷 이름
      Key: fileName, // 업로드될 파일의 이름
    };
    const command = new DeleteObjectCommand(params);
    await this.s3Client.send(command);
  }
}

 

 

S3 이미지 저장은 파일 이름으로 구분하기 때문에 같은 이름으로 업로드시 이전 파일위에 덮어씌워진다는 특징이 있다.

그래서 업로드 전 uuid를 이용해 랜덤 문자열을 붙여주는 과정이 필요하다.

@Injectable()
export class UtilsService {
  getUUID(): string {
    return uuidv4();
  }
}

 

네스트를 이용해 이미지를 받아오기 위해서는 FileInterceptor를 통해 멀티미디어 파일을 가져온다.

그리고 Express.Multer.File로 req 받아온다.

//card.controller.ts
@Roles(BoardRole.OWNER, BoardRole.WORKER)
  @UseInterceptors(FileInterceptor('file'))
  @Patch('/:cardId')
  async modifyCard(
    @UserInfo() user: User,
    @Param('cardId', ParseIntPipe) cardId: number,
    @Body() updateCardDto: UpdateCardDto,
    @UploadedFile() file: Express.Multer.File,
  ) {
    const updatedDate = await this.cardService.modifyCard(
      user,
      cardId,
      updateCardDto,
      file,
    );
    return updatedDate;
  }

 

req에서 받아온 파일을 서비스에서 처리하는 방법

//card.service.ts
async modifyCard(
    user: User,
    cardId: number,
    updateCardDto: UpdateCardDto,
    file: Express.Multer.File,
  ) {
    //카드 작성자 확인
    const cardUser = await this.cardRepository.findOne({
      where: { user: user.id },
    });

    if (!cardUser) {
      throw new UnauthorizedException('카드 작업자만 수정할 수 있습니다.');
    }

    const card = await this.cardRepository.findOne({
      where: { id: cardId },
    });

    if (!card) {
      throw new NotFoundException('해당하는 카드가 존재하지 않습니다.');
    }

    //이미 입력된 이미지가 있다면 S3에서 기존 이미지 삭제
    if (card.image_url !== null) {
      await this.awsService.DeleteUploadToS3(card.image_url);
    }

    //S3에 이미지 업로드, url return
    const imageName = this.utilsService.getUUID();
    const ext = file.originalname.split('.').pop();

    const imageUrl = await this.awsService.imageUploadToS3(
      `${imageName}.${ext}`,
      file,
      ext,
    );

    //DB에 저장
    const uploadCard = await this.cardRepository.save({
      id: cardId,
      title: updateCardDto.title,
      description: updateCardDto.description,
      color: updateCardDto.color,
      dead_line: updateCardDto.dead_line,
      image_url: `${imageName}.${ext}`,
      lexo: card.lexo,
    });

    return uploadCard;
  }

S3 버켓에 성공적으로 업로드 된 모습

 

 

도움많이 받은 글

https://medium.com/@fluffy-puppy-lovely/nestjs-aws-s3%EB%A1%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0-05bc258fbca8

 

[NestJS] AWS S3로 이미지 업로드하기

이번 글에서는 NestJS를 사용하여 S3에 이미지 업로드 API를 구현하는 과정에 대해 소개하겠습니다.

medium.com

 

'TIL' 카테고리의 다른 글

힙 정렬  (0) 2024.04.12
이모지 문자 mySql에 저장하기  (0) 2024.04.12
TIL #33) 순서 정렬하기  (0) 2024.03.22
TIL #32) Linked List 자료구조  (0) 2024.03.20
TIL #31) LLM  (0) 2024.03.18