본문 바로가기
Back-end/nest.js

Nest.js : AWS S3 Presigned url 사용하기 - 2

by hsloth 2023. 12. 20.

 

지난 포스팅에서 Presigned url을 발급 받아보았다.

 

https://suloth.tistory.com/188

 

Nest.js : AWS S3 Presigned url 사용하기 - 1

Nest.js에서 AWS S3의 presigned url을 발급받아 사용해보려고 한다. Presigned url 이란? 말 그대로 미리 서명된 url이다. 서버에서 AWS S3에 요청하여 presigned url을 발급받을 수 있다. 이렇게 발급받은 presigned

suloth.tistory.com

 

이번 포스팅에서는 발급받은 presigned url에 대한 후처리 (DB에 profileUrl 업로드) 작업에 대해 포스팅하려고 한다.

 

왜 presigned url을 이용해서 이미지를 S3에 PUT 했는데, 또 처리가 필요하냐고 묻는다면

다음과 같은 이유가 있다.

1. DB를 업데이트 해줘야 한다.

  • S3에 업로드만 하면 뭐하나. DB의 profileUrl을 업데이트 해줘야 나중에 해당 url로 이미지를 가져올 수 있다.

2. 아니 그러면, presigned url 발급 받을 때 DB 업데이트 하면 되지않나?

  • 말도 안되는 소리다. url 발급 받을 때 DB를 업데이트 해버리면, 모종의 이유로 presigned url을 사용해서 이미지를 업로드할 때 에러가 발생하거나, 발급받은 presigned url을 사용하지 않는 경우에는 해당 파일 url은 아무것도 없는 url이 되어버린다.

위 두 가지 이유 때문에 S3에 이미지 업로드 후, 클라이언트에서 서버측으로 업로드 완료 요청을 보내는 것이다.

 


Presigned url을 이용한 업로드 완료 요청


 

먼저, presigned url을 발급받으면 중간에 저장될 파일명을 찾을 수 있다.

https://버킷명.s3.리전.amazonaws.com/1702984544757abc.jpeg
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD
&X-Amz-Credential=임의의값
&X-Amz-Date=20231219T111544Z
&X-Amz-Expires=180
&X-Amz-Signature=임의의값
&X-Amz-SignedHeaders=host
&x-id=PutObject

 

위 url에서 https://버킷명.s3.리전.amazonws.com/1702984544757abc.jpeg

가 해당 파일을 얻을 수 있는 url이다.

 

따라서, 클라이언트 측에서는 presigned url의 해당 부분을 가지고 서버에게 요청을 보내면 된다.

 

먼저, body로 받을 Dto를 정의하자.

profileImageUrl이 필요하다.

// update-user-profile-image.dto.ts

// class-validator를 사용하는 경우
export class UpdateUserProfileImageDto extends PickType(UserEntity, [
  'profileImgUrl',
] as const) {}

// 혹은
interface UpdateUserProfileImageDto {
  profileImgUrl: string;
}

 

 

그리고 Controller와 Service를 작성하자.

이미지 프로필에 대한 수정 요청이므로 Put메소드를 사용한다.

여기서는 JWT를 통한 로그인을 고려하고 작성한다.

  // user.controller.ts
  @UseGuards(AccessTokenAuthGuard)
  @Put('profile-image')
  async updateUserProfileImage(
    @Body() body: UpdateUserProfileImageDto,
  ): Promise<boolean> {
    const isUpdated = await this.userService.updateUserProfileImage(
      body,
      user.id,
    );

    return isUpdated;
  }
  
  // user.service.ts
    async updateUserProfileImage(
    updateUserProfileImageDto: UpdateUserProfileImageDto,
    userId: number,
  ): Promise<boolean> {
    const isUpdated = await this.userRepository.updateUser(
      {
        profileImgUrl: updateUserProfileImageDto.profileImgUrl,
      },
      {
        id: userId,
      },
    );

    return isUpdated;
  }

 

이런식으로 작성하면 된다.

나의 경우에는 update가 되면, true를 리턴하도록 Repository에 updateUser함수를 만들어서 짜놨다.

userId에 해당하는 user의 profileImgUrl을 수정하도록 작성했다.

 

어차피 로그인을 해야 업데이트할 수 있게 해놨으므로 따로 이상한 값으로 요청을 보낼 수 없다고 생각했으므로 url 검증은 하지 않았다.

하지만, 만약에 url 검증이 필요하다면 redis같은 cache db를 두거나, url암호화를 통한 검증을 하면 될 것 같다.

 

혹시, 이 방법보다 좋은 방법을 알고 있으시다면 댓글 부탁드립니다.