토이 프로젝트 - 음식 메뉴 API

개요

음식 주문 애플리케이션을 사용하면 음식점의 메뉴를 확인할 수 있습니다.
평소에 이러한 메뉴 API는 어떻게 제공 되고 있을까 궁금했습니다.
이번 프로젝트를 기회로 시중에 있는 배달 앱을 분석해보면서 API를 구현해보도록 하겠습니다.

사용 기술

스프링 부트와 JPA를 기본적으로 사용하며 스프링 데이터 JPAQerydsl을 부분적으로 사용합니다.
언어는 java 8입니다.

분석

가장 쉽게 접할 수 있는 배달의 민족앱을 기준으로 분석을 해보겠습니다. (제가 좋아하는 피자샵 매장을 예시로 들겠습니다.)
먼저 앱 내에서 음식점을 클릭하면 메뉴 리스트를 볼 수 있습니다.

정리 해보면 다음과 같습니다.

추가로 세부사항을 더 분석하겠습니다.

요구사항

먼저 메뉴 API 사용 대상을 생각해보도록 하겠습니다.
첫번째로 음식점 관리 애플리케이션 입니다.
음식점 관리 앱에서는 음식점 사장님이 직접 메뉴를 등록, 수정, 삭제, 조회할 수 있어야 합니다.

두번쨰로는 음식 주문 애플리케이션 입니다.
음식 주문 앱에서는 음식점의 메뉴 조회가 주가 됩니다.
음식점의 전체 메뉴를 조회하고 메뉴를 클릭했을때 보여줄 메뉴 상세 정보를 조회할 수 있어야합니다.

정리 해보면 다음과 같습니다.

설계

도메인 모델

메뉴 도메인 모델

테이블 모델

구현

저장

@PostMapping("/api/v1/store/{storeId}/menu-group")
public void saveMenuGroup(@PathVariable("storeId") Long storeId, @RequestBody MenuGroupSaveDto menuGroupSaveDto) {
    menuService.saveMenuGroup(storeId, menuGroupSaveDto);
}
@Transactional
public void saveMenuGroup(Long storeId, MenuGroupSaveDto menuGroupSaveDto) {
    Store store = storeRepository.findById(storeId)
            .orElseThrow(EntityNotFoundException::new);
    menuGroupRepository.save(menuGroupSaveDto.toEntity(store));
}
@Getter
public class MenuGroupSaveDto {

    private String name;

    private Integer priority;

    public MenuGroup toEntity(final Store store) {

        MenuGroup menuGroup = MenuGroup.builder()
                .name(name)
                .priority(priority)
                .store(store)
                .build();
        return menuGroup;
    }
}

리팩토링

연쇄 저장

영속성 전이기능을 사용하면 부모 엔티티가 영속 상태로 변할때 자식 엔티티도 같이 영속 상태로 변합니다.
이를 이용해 저장 API 구현시 dto에 하위 엔티티 정보를 입력했을때 같이 저장되게 하면 어떻게 될까 생각했고 구현해보기로 했습니다.

@Getter
public class MenuGroupSaveDto {

    private String name;

    private Integer priority;

    private List<MenuSaveDto> menuSaveList;

    public MenuGroup toEntity(final Store store) {

        MenuGroup menuGroup = MenuGroup.builder()
                .name(name)
                .priority(priority)
                .store(store)
                .build();

        if (menuSaveList != null && menuSaveList.size() > 0) {
            menuSaveList.forEach(msd -> msd.toEntity(menuGroup));
        }
        
        return menuGroup;
    }
}