Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix/meal create] 식사 도메인 내 API 수정 #46

Merged
merged 7 commits into from
Oct 22, 2023

Conversation

dldmsql
Copy link
Member

@dldmsql dldmsql commented Oct 8, 2023

작업 내용

식사 도메인 내 API 수정을 하였습니다.

  • 피그마 화면에 맞추어 필요한 데이터 추가 및 DTO 수정 ( 학식 카테고리 )
  • 식사 등록 API 비즈니스 로직 수정
    기존 ) 제공일자 (offeredAt)을 기준으로, 덮어쓰기 방지 검증
    변경 ) 제공일자 & 학식구분( mealType ) 기준으로, 덮어쓰기 방지 검증
  • API로직 수행시간 측정을 위한 AOP 도입
  • 식사 조회 ( 하루/주간 ) 비즈니스 로직 고도화
    기존 ) 등록되어 있는 데이터만 리스트화 해서 반환
    변경 ) 등록되어 있지 않은 데이터 확인 후, 아침/점심/저녁 포멧으로 더미 데이터로 바인딩하여 리스트화 해서 반환
  • Enum 응답 데이터 name -> value 응답으로 변경

관련 이슈

#44

작업 확인 방법

주간단위 식사 조회 API 수행 시, 테스트 응답 데이터

{
  "localDateTime": "2023-10-08T22:10:14.401957",
  "message": "OK",
  "data": [
    {
      "offeredAt": "2023-10-01",
      "dayMealListGetResList": [
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "아침",
          "mealStatus": "운영",
          "offeredAt": "2023-10-01",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "점심",
          "mealStatus": "운영",
          "offeredAt": "2023-10-01",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "저녁",
          "mealStatus": "운영",
          "offeredAt": "2023-10-01",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        }
      ]
    },
    {
      "offeredAt": "2023-10-02",
      "dayMealListGetResList": [
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "아침",
          "mealStatus": "운영",
          "offeredAt": "2023-10-02",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "점심",
          "mealStatus": "운영",
          "offeredAt": "2023-10-02",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "저녁",
          "mealStatus": "운영",
          "offeredAt": "2023-10-02",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        }
      ]
    },
    {
      "offeredAt": "2023-10-03",
      "dayMealListGetResList": [
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "아침",
          "mealStatus": "운영",
          "offeredAt": "2023-10-03",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "점심",
          "mealStatus": "운영",
          "offeredAt": "2023-10-03",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "저녁",
          "mealStatus": "운영",
          "offeredAt": "2023-10-03",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        }
      ]
    },
    {
      "offeredAt": "2023-10-04",
      "dayMealListGetResList": [
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "아침",
          "mealStatus": "운영",
          "offeredAt": "2023-10-04",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "점심",
          "mealStatus": "운영",
          "offeredAt": "2023-10-04",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "저녁",
          "mealStatus": "운영",
          "offeredAt": "2023-10-04",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        }
      ]
    },
    {
      "offeredAt": "2023-10-05",
      "dayMealListGetResList": [
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "아침",
          "mealStatus": "운영",
          "offeredAt": "2023-10-05",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "점심",
          "mealStatus": "운영",
          "offeredAt": "2023-10-05",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "저녁",
          "mealStatus": "운영",
          "offeredAt": "2023-10-05",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        }
      ]
    },
    {
      "offeredAt": "2023-10-06",
      "dayMealListGetResList": [
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "아침",
          "mealStatus": "운영",
          "offeredAt": "2023-10-06",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "점심",
          "mealStatus": "운영",
          "offeredAt": "2023-10-06",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "저녁",
          "mealStatus": "운영",
          "offeredAt": "2023-10-06",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        }
      ]
    },
    {
      "offeredAt": "2023-10-07",
      "dayMealListGetResList": [
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "아침",
          "mealStatus": "운영",
          "offeredAt": "2023-10-07",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "갈비탕, 깍두기, 흰쌀밥",
          "mealType": "점심",
          "mealStatus": "운영",
          "offeredAt": "2023-10-07",
          "price": 0,
          "category": "DEFAULT",
          "restaurantName": "MCC 식당"
        },
        {
          "menu": "등록된 식단이 없습니다.",
          "mealType": "저녁",
          "mealStatus": "운영",
          "offeredAt": "2023-10-07",
          "price": 0,
          "category": "단일메뉴",
          "restaurantName": "MCC 식당"
        }
      ]
    }
  ],
  "success": true
}

추가 정보 (선택 사항)

수행시간 측정 범위가 넓다고 생각되면, Service 혹은 Repository 범위는 LogAspect 파일에서 수정가능합니다.

... 중략
@Around(
            "everymeal.server.global.aop.log.Pointcuts.allQuery() || everymeal.server.global.aop.log.Pointcuts.allService()")
    public Object executingTimeLog(ProceedingJoinPoint joinPoint) throws Throwable {
    ... 중략
}

service와 repository 내부 함수 실행 시점부터 종료시점까지의 소요 시간을 측정해서 콘솔창에 남깁니다.
- 피그마 상에 노출되어야 하는 데이터에 맞추어 DTO를 수정했습니다.
- 등록 시, 동일한 제공일자와 식사구분 데이테를 갖고 있는지 검증 로직으로 변경했습니다.
- 학식 하루/주간 조회 성능 고도화를 위해 비동기 처리/인덱스/Instant 타입 변경을 수행하였습니다.
@dldmsql dldmsql added the Feature 기능 개발 label Oct 8, 2023
@dldmsql dldmsql requested a review from Qbeom0925 October 8, 2023 13:22
@dldmsql dldmsql self-assigned this Oct 8, 2023
@dldmsql
Copy link
Member Author

dldmsql commented Oct 8, 2023

조회 로직 고도화 과정을 기록합니다.

1. '등록되지 않은 식단입니다.' 출력을 위한 7일 간의 아침/점심/저녁 조회 쿼리 각각 수행

학식조회 ( 주간 )

  • 식당 조회
    • 없을 경우, 404 오류
  • 조회하고자 하는 시작 날짜 기준으로 +7일의 List 리스트 생성
  • for(LocalDate ldOfferedAt : list ) 내부에서 아래 3가지 쿼리문을 반복 실행
      1. 아침인 경우, findAllByOfferedAtAndMealType()
      1. 점심인 경우,
      1. 저녁인 경우
    • 등록되지 않은 식단이 존재하는지 확인
      • Res DTO로 등록되지 않은 식단 데이터 생성
  • for문을 통해 수행된 조회 쿼리 내용을 모두 addAll() 함수로 merge

결과
image

2. 제공일자, 학생식당, 식사구분에 인덱스 설정

결과
image

3. 제공일자의 타입을 LocalDate -> Instant로 변경

결과
image

4. 1번의 쿼리 수행을 비동기 방식으로 처리

image

5. 응답 데이터로 변환 과정 코드 간소화

image

@dldmsql
Copy link
Member Author

dldmsql commented Oct 8, 2023

MySQL 에서 Instant 타입 사용 참고.

@dldmsql
Copy link
Member Author

dldmsql commented Oct 8, 2023

@Qbeom0925 이 PR이 merge 되면, 피그마 기반 연관 엔티티와 속성 정의 작업 이어서 하겠습니다~!

@dldmsql
Copy link
Member Author

dldmsql commented Oct 11, 2023

2023.10.11 로컬 환경 테스트 결과 공유

1. 1개의 대학교의 1개의 식당에 101개의 식단 데이터 주간 단위 조회

최대 응답 속도 241ms
최소 응답 속도 50ms

image image

2. 1개의 대학교의 2개의 식당과 각 학교에서 제공하는 식단 데이터의 총합 200개 중 식단 데이터 주간 단위 조회

명지대학교 > MCC 식당 | 풀바셋 식당 > 200개의 식단 데이터 중, 풀바셋 식당의 특정 일자를 기준으로 주단 단위 데이터 조회

최대 응답 속도 239ms
최소 응답 속도 67ms

image image

== Data Set ==

  • 총 200개의 식단 데이터
image
  • 각 식당 별 식단 개수
image

@Qbeom0925
Copy link
Member

최대는 첫번째에 호출한 결과이구 최소는 두번째 호출한 결과일까요?
최대와 최소의 상황이 궁금합니다!

Copy link
Member

@Qbeom0925 Qbeom0925 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

튜닝 완료하였습니다.

첫번째 호출 (JPA 영속성 컨텍스트에 값 미존재 DB 연결)
평균 150ms
스크린샷 2023-10-15 오후 4 20 44

두번째 호출 (JPA 영속성 컨텍스트에 값 존재)
평균 10ms대 유지
스크린샷 2023-10-15 오후 4 21 51

변경점

  • 쿼리 효율화: 새로운 코드에서는 주어진 식당 및 기간 내의 식단 데이터를 한 번의 쿼리로 효율적으로 가져왔습니다. 이것은 여러 비동기 쿼리를 조합하는 대신 데이터베이스에서 필요한 정보를 한 번에 가져옴으로써 성능 향상을 이루었습니다.
  • 성능 향상: 전반적으로 성능이 향상되었으며, 약 100ms 이상의 시간을 절약했습니다.
  • 비동기 쿼리를 개선: 이전 코드에서는 여러 비동기 CompletableFuture를 사용하여 데이터를 검색하고 조합했습니다. 새로운 코드에서는 JPA QueryDSL의 메서드를 사용하여 데이터를 효율적으로 검색했습니다.
  • QueryDSL에서 transform을 사용하여 DTO 기반 조회 및 그룹핑을 진행했습니다

결과 쿼리를 총 2개의 쿼리문으로 줄였습니다.

Copy link
Member

@Qbeom0925 Qbeom0925 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드에서 중요한 부분들 집었습니다!

.orderBy(QMeal.meal.offeredAt.desc(), QMeal.meal.mealType.desc())
.fetch();
return queryResult;
public List<WeekMealListGetResTest> getWeekMealList(Restaurant restaurant, Instant mondayInstant,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가장 핵심입니다!!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

쿼리 확인했습니다!
이렇게 줄어들 수 있다니 신기해요!

@@ -101,13 +102,14 @@ public ApplicationResponse<List<DayMealListGetRes>> getDayMeal(
*/
@GetMapping("/week")
@Operation(summary = "주간 식단 조회")
public ApplicationResponse<List<WeekMealListGetRes>> getWeekMeal(
public ApplicationResponse<List<WeekMealListGetResTest>> getWeekMeal(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단 DTO 구분으로 Test로 만들어 두었습니다.
은비님이 이어서 세부 조건문 추가하여 사용해보세요!!


private String menu;
private MealType mealType;
private MealStatus mealStatus;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단 급한대루 Enum을 그대로 조회하였습니다. 이 부분도 튜닝하면 가능하나, 필요성은 느끼지 못하여 repository 단계에서는 하지 않았습니다.

- 등록되지 않은 데이터 포맷에 맞추어 반환 작업을 서비스 레이어에서 레포지토리 레이어로 변경하였습니다.
…reate

# Conflicts:
#	src/main/java/everymeal/server/meal/service/MealServiceImpl.java
@sonarcloud
Copy link

sonarcloud bot commented Oct 22, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 4 Code Smells

85.4% 85.4% Coverage
0.0% 0.0% Duplication

@dldmsql
Copy link
Member Author

dldmsql commented Oct 22, 2023

@Qbeom0925 규범님, 당일/주간 식단 조회 쿼리 튜닝 작업 진행했습니다!@

가이드로 작성해주신 코드 기반으로 기존 코드 리펙토링 했습니다.

@Qbeom0925 Qbeom0925 merged commit 4c3b545 into develop Oct 22, 2023
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature 기능 개발
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants