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 버켓에 성공적으로 업로드 된 모습
도움많이 받은 글
'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 |