본문 바로가기
개발 기초 다지기

티켓예매 사이트 공연 날짜/시간 body에서 배열형식으로 받기

by 너의고래 2024. 7. 8.

열심히 티켓예매 사이트 개인 과제를 진행중이다. 공연 등록에서 고려해야할 점은 하나의 공연이 여러날짜에, 그리고 같은 날짜여도 여러 시간에 나눠서 진행된다는 것이다. 그렇기에 공연을 등록할 때, 배열을 통해 한번에 여러 날짜와 시간을 입력해야한다.

 

body를 통해 어떻게 배열을 받을 수 있지?

과제 발제 문서를 통해 이 부분을 배열로 받으면 된다는 힌트를 얻었다. 그렇다면 배열은 어떠한 방식으로 받을 수 있을까?

우선은 body를 통해 배열이 들어온다고 생각했을 때, 제일 먼저 만나게 되는 곳이 어디인지 생각해보았다. 바로 내가 만든 createPerformanceDto였다. 

 

createPerformanceDto

공연 스케줄에 대한 데이터 DTO를 먼저 만들어주었다.

class PerformanceScheduleDto {
    @IsDate()
    @Type(() => Date)
    @IsNotEmpty({ message: '공연 날짜를 입력해주세요.' })
    performanceDate: Date;

    @IsString()
    @IsNotEmpty({ message: '공연 시간을 입력해주세요.' })
    performanceTime: string;

    @IsNumber()
    @IsNotEmpty({ message: '전체 좌석수를 넣어주세요.' })
    totalSeats:number;

    @IsNumber()
    remainingSeats?: number;

}

 

배열형식으로 받을 정보인 날짜, 시간 그리고 좌석수를 입력할 수 있도록 구성을 짜주었다.

 

그리고 공연 등록시 필요한 전체 데이터를 포함한 DTO를 만들어주었다.

export class CreatePerformanceDto {
  @IsString()
  @IsNotEmpty({ message: '공연명을 입력해주세요.' })
  title: string;

  @IsString()
  @IsNotEmpty({ message: '공연 설명을 입력해주세요.' })
  description: string;

  @IsEnum(Category, {message: '유효한 카테고리를 입력해주세요.'})
  @IsNotEmpty({ message: '카테고리를 입력해주세요.'})
  category: Category;

  @IsString()
  @IsNotEmpty({ message: '공연 장소를 입력해주세요.' })
  location: string;

  @IsNumber()
  @IsNotEmpty({ message: '공연 가격을 입력해주세요.' })
  price: number;

  @IsString()
  @IsNotEmpty({ message: '이미지를 넣어주세요.' })
  img: string;

  @IsArray()
  @Type(() => PerformanceScheduleDto)
  schedules: PerformanceScheduleDto[]

}

 

공연 등록시 필요한 정보를 구성해주었고, 아래쪽에 여러 공연 스케줄을 함께 입력받기 위한 속성을 추가해주었다.

    • @IsArray() : schedule이 배열형태인지 유효성 검사 수행
    • @Type(()=> PerformanceScheduleDto) : schedules 배열의 요소를 위에 작성한, PerformanceScheduleDto 타입으로 변환. 이를 통해 NestJS 입력된 JSON 데이터를 자동으로 PerformanceScheduleDto 객체로 변환.
    • 'schedules' : 여러 개의 공연 일정을 포함하는 배열을 나타내기 위해 사용된 속성 이름
    • PerformanceScheduleDto[] : 여러 개의 공연 일정을 포함

 

controller 파일

Dto를 설정해줬으니 다음으로 연결되는 controller를 만져주었다.

  //공연 등록
  @Roles(Role.Admin)
  @UseGuards(RolesGuard)
  @Post()
  async create(@Body() createPerformanceDto: CreatePerformanceDto) {
    const performance = await this.performanceService.create(createPerformanceDto);
    return {
        status: 201,
        message: '공연 등록에 성공했습니다.',
        data: performance,
    }
  }

 

controller 파일은 아무래도 클라이언트의 요청 응답을 다루다보니 배열을 위해 따로 만져주지는 않았다.

열심히 만든 Dto파일을 body에서 받아와서 service 파일로 잘 넘겨주었다.

 

 

service 파일

 //공연 등록
  async create(createPerformanceDto: CreatePerformanceDto): Promise<Performance> {
    const { title, description, category, location, price, img, schedules } = createPerformanceDto;
    
    const performance = this.performanceRepository.create({
        title, description, category, location, price, img });

    const savedPerformance = await this.performanceRepository.save(performance);

    const performanceSchedule = schedules.map(schedule =>
        this.performanceScheduleRepository.create({
            ...schedule,
            remainingSeats: schedule.totalSeats,
            performance: savedPerformance,
        })
        )
       await this.performanceScheduleRepository.save(performanceSchedule);

    return savedPerformance;
    }

 

Dto파일에서 각 데이터를 가져와 정의 할 때 날짜, 시간 부분은 createPerformanceDto에 정의내려준 'schedules'로 가져왔다. Dto를 지정해준 덕분에 배열을 객체형태로 간단하게 가져올 수 있었다.

performances와 performanceSchedules 테이블을 따로 만들 것이기 때문에 각자 엔티티를 생성한 후 데이터베이스에 저장하도록 만들었다.

performanceSchedule 엔티티 생성 부분

  • 배열형태인 schedules map 메서드를 사용하여 순회하도록 만들어 주었다.
  • 각 각 순회하며, performanceScheduleRepository.create를 통해 엔티티를 생성해준 후, 데이터베이스에 저장.
  • ...schedule : schedule 객체의 모든 속성을 포함시키기 위해, 전개하여 새로운 객체에 복사해준다.
  • 새롭게 생겨야하는 칼럼인 remainigSeats 그리고 데이터 일관성을 위해 생성된 PerformanceSchedule Performance와의 관계를 갖도록 savedPerformance 설정
  • 이 값을 데이터베이스에 저장

 

데이터 조회

 

 

이런식으로 날짜, 시간 데이터가 배열 형식으로 출력되는것을 확인할 수 있다.

 

일반적인 구조에서 한발짝 더 들어가 배열을 다룬다는 것이 쉽지는 않았지만, Dto만 잘 구성해주면 생각보다 쉽게 구현할 수 있다는 것을 알게되었다. Dto 그리고 엔티티의 관계성과 조금 더 가까워진 시간이었다.

댓글