250x250
Notice
Recent Posts
Recent Comments
Link
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

devlog_owen

MySQL 배열로 데이터 저장하기 (+nest.js, swagger) 본문

TIL

MySQL 배열로 데이터 저장하기 (+nest.js, swagger)

developer_owen 2024. 3. 5. 17:42
728x90

 

 

문제상황

 

Gallery 엔티티에서 photo 컬럼을 다중파일 업로드를 해야한다.

하지만 MySQL과 같은 관계형 데이터베이스(RDBMS)는 일반적으로 배열을 데이터로 받을 수 없다.

그 이유는 정규화(Normalization) 때문이다.

RDBMS는 데이터를 정규화하여 중복을 최소화하려는 경향이 있다. 각 열은 단일 값을 포함하며 배열과 같은 중첩된 구조는 데이터 정규화의 원칙에 어긋난다.

 


해결방안

 

1. Photo 엔티티 추가

 

테이블을 분리(정규화)하는 것이 가장 정석적인 방법이라고 한다. DB에서 배열을 그대로 하나의 레코드에 저장하는 것은 제 1정규형에 위반되므로 기본적으로 RDBMS에서 제한한다.

따로 Gallery 이미지를 저장하는 테이블을 맞들고 relation 걸고 갤러리 조회할 때 조인을 해서 가져오는것이 정석이다.

 

2. 배열 자료형을 문자열로 변환해 저장

 

1번의 방법대로 하기에는 단순히 갤러리의 이미지는 갤러리와만 연결되어 있기때문에 굳이 테이블을 추가해서 DB의 성능을 떨어뜨리는 것보다 역정규화를 통해 단순히 배열 그대로 테이블에 집어 넣는 방식을 선택했다. 

 


구현방식

  //갤러리 등록
  //gallery.controller.ts
  
  @ApiBearerAuth('accessToken')
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    description: 'Upload gallery with image.',
    type: 'multipart/form-data',
    schema: {
      type: 'object',
      properties: {
        files: {
          type: 'array',
          items: {
            type: 'string',
            format: 'binary',
            description: 'The image files to upload.',
          },
        },
        content: {
          type: 'string',
          description: 'The content of the gallery.',
        },
        date: {
          type: 'date',
          description: 'date of the gallery.',
        },
      },
    },
  })
  @Post()
  @UseInterceptors(FilesInterceptor('files', 5))
  @UseGuards(accessTokenGuard)
  async addgallery(
    @Body() createGalleryDto: CreateGalleryDto,
    @UserId() userId: number,
    @UploadedFiles() files: Express.Multer.File[],
  ) {
    const urls = await Promise.all(
      files.map(async (file) => await this.galleryService.imageUpload(file)),
    );
    return await this.galleryService.addgallery(createGalleryDto, userId, urls);
  }

 

@UseInterceptors(FilesInterceptor('files', 5)) :

클라이언트로부터 전송된 파일들을 처리하기위한 인터셉터. 여기서 files는 배열이고 5는 최대파일의 갯수.

@UploadedFiles() files: Express.Multer.File[],:

FilesInterceptor를 사용하기 위해 적용됨.

 

 

  //갤러리 등록
  //gallery.service.ts
  
  async addgallery(
    createGalleryDto: CreateGalleryDto,
    userId: number,
    urls: string[],
  ) {
    const user = await this.userService.findUserById(userId);
    if (user.role !== 1) {
      throw new BadRequestException('관리자만 등록이 가능합니다.');
    }

    const gallery = await this.galleryRepository.save({
      ...createGalleryDto,
      photos: JSON.stringify(urls),
      user: user,
    });
    return gallery;
  }

 

 

    const gallery = await this.galleryRepository.save({
      ...createGalleryDto,
      photos: JSON.stringify(urls),
      user: user,
    });
    return gallery;

 

여기서 JSON.stringify를 통해 문자열로 변환시켜 저장한다.


 

결과

 

 

배열로 잘 들어간다!!


 

728x90