DevBoi

[NestJS] S3이미지 업로드 모듈 제작 본문

Develop/[NestJs]

[NestJS] S3이미지 업로드 모듈 제작

HiSmith 2023. 7. 1. 13:24
반응형

저번 포스팅에서 다뤘던, S3 이미지 업로드 모듈에 대한 마무리를 지으려고한다.

우선, 개발내용은 아래와 같다.

 

1. 음식에 대한 정보를 올릴 수 있음

2.음식에 대한 정보가 생성되면, 음식에 대한 id 값이 생성됨.

3.음식에 대한 id값 기준으로, 해당 음식에 대한 사진정보를 담아서 저장함

4.음식사진에 대한 업로드 모듈을 제작함

 

 

일단 음식사진에 대한 정보를 올리는 모듈이다.

@ApiOperation({ summary: '음식사진 정보 생성' }) 
  @ApiParam({
    description: '음식사진 정보 생성',
    type: [FoodImageReqDto],
    name: 'foodImageInfos'
  })
  @Post(':id')  
  async postFoodImageInfo(@Body('foodImageInfos') foodImageInfos :FoodImageReqDto[], @Param('id') id: number) : Promise<FoodImageResDto[]>{    
    return this.foodImageService.saveAll(foodImageInfos,id);
  }
  async saveAll(foodImageInfos: FoodImageReqDto[],id: number): Promise<FoodImageResDto[]> {
    const food = await this.foodRepository.findOne(id);           
    const saveList :FoodImage[] = await this.foodImageRepository.save(foodImageInfos.map(x => new FoodImage(x,food)));    
    return saveList.map(x =>x.makeResponse());
  }

 

간단하게는 이렇다.

이렇게 정보들을 req list 형태로 올리게 되면, 서비스는 받아서, 저장한다 

그리고, 중요한 포인트는 해당 리턴된 엔티티를 res dto로 변경해서 내려준다.

사실 res로 변경해서 내려주는건 귀찮아서, 하기싫었는데

연관관계로 묶인 엔티티를 다 내리다 보면, 해당 엔티티의 정보들이 너무불필요하게 많이 내려간다.

예를 들어서, 저장후 음식 사진에 대한 값이 다 내려가는 것들이 그 예다.

Response를 변환하는 건 이렇게 사용했다.

export class FoodImageResDto {

  
  id: number; //음식 사진 id
  foodId: number; //음식 정보
  filename: string //음식사진이름
  mastYn: Boolean //대표사진 여부
  priority: number //중요도
  imageUrl: string //부가,설명에 대한 이미지 여부

  constructor(input?: FoodImage){    
    if(input){
      this.id = input.id;
      this.foodId = input.food.id;
      this.mastYn = input.mastYn;  
      this.priority = input.priority;      
      this.imageUrl = "foodimage/"+input.food.id+"/"+input.filename;
      this.filename = input.filename;
    
    }    
    }
}
@Entity()
export class FoodImage {

  @PrimaryGeneratedColumn() 
  id: number; //음식 사진 id
  @JoinColumn() 
  @ManyToOne(() => Food, (food) => food.foodImage)
  food: Food; //음식 정보
  @Column({default: ''})
  filename: string //음식사진이름
  @Column() 
  mastYn: Boolean //대표사진 여부
  @Column({default: 1})
  priority: number //중요도
  @Column({default: ''})
  imageUrl: string //부가,설명에 대한 이미지 여부

  constructor(input?: FoodImageReqDto, food?: Food){    
    if(input){
      this.food = food;
      this.filename = input.filename;
      this.mastYn = input.mastYn;  
      this.priority = input.priority;      
    }    
    }
  makeResponse(): FoodImageResDto{
    return new FoodImageResDto(this);
  }
}

 

이런식으로 해당 음식 엔티티를 응답 엔티티로 변환했다.

@Post('/upload/:id')
  @UseInterceptors(FilesInterceptor('files', 10)) // max file count 10;
  async uploadFile(@UploadedFiles() files,@Param('id') id :number) {
    console.log(id);
    const imgurl: string[] = [];
    await Promise.all(
      files.map(async (file: Express.Multer.File) => {
        const key = await this.uploadsService.uploadFoodImage(file,id);
        imgurl.push(""+key);
      }),
    );  
    return {
      statusCode: 201,
      message: `Success`,
      data: imgurl,
    };
  }
  async uploadFoodImage(file: Express.Multer.File,id :number) {    
    const key = `${file.originalname}`;
    const params = {
      Bucket: 'leedss3/foodImage/'+id,
      ACL: 'public-read',
      Key: key,
      Body: file.buffer,
    };

    return new Promise((resolve, reject) => {
      this.s3.putObject(params, (err, data) => {        
        if (err) reject(err);
        resolve(key);
      });
    });
  }

 

또한 음식에 대한 이미지를 업로드하는 모듈은 이렇게 짰다.

S3버킷에는 food_id를 새로운 경로로 변경하고, 해당 경로에 맞춰서 음식 사진을 저장한다.

업로드 서비스는 별도로 업로드를 위한 S3초기화 인스턴스를 선언해야해서, 해당 전체 소스를 넣어보면 아래와 같다.

@Injectable()
export class UploadsService {
  private readonly s3;

  constructor() {
    
    AWS.config.update({
      //region: process.env.AWS_REGION,
      region: 'ap-northeast-2',
      credentials: {        
        accessKeyId: process.env.AWS_ACCESS_KEY,
       
        secretAccessKey: process.env.AWS_SECRET_KEY,
       
      },
    });
    this.s3 = new AWS.S3();
  }

  async uploadFoodImage(file: Express.Multer.File,id :number) {    
    const key = `${file.originalname}`;
    const params = {
      Bucket: 'leedss3/foodImage/'+id,
      ACL: 'public-read',
      Key: key,
      Body: file.buffer,
    };

    return new Promise((resolve, reject) => {
      this.s3.putObject(params, (err, data) => {        
        if (err) reject(err);
        resolve(key);
      });
    });
  }
}

 

이렇게 되면, 음식 사진에 대한 정보를 저장할 수 있게된다.

 

다음에는 환경 별 다른 환경 변수를 사용 하는 법을 살펴보자.

그리고 조만간 강제적으로 쿼리빌더를 사용할 수밖에 없을 것 같다.

반응형

'Develop > [NestJs]' 카테고리의 다른 글

[NestJs] Swagger관련 설정정리  (0) 2023.07.01
[NestJs] TypeORM entity cascade 설정  (0) 2023.07.01
[NestJs] S3업로드 모듈 생성  (0) 2023.06.26
[NestJs] 다대일 매핑  (0) 2023.06.19
[NestJs] Api 좀 더 Restful 하게 변경  (0) 2023.06.18