-
Notifications
You must be signed in to change notification settings - Fork 0
[Perf] 티어표 로딩 속도 #141
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
base: develop
Are you sure you want to change the base?
[Perf] 티어표 로딩 속도 #141
Conversation
📝 WalkthroughWalkthrough애플리케이션 전반에 Micrometer Observations 기반 계측 및 Zipkin 분산 추적을 통합하고 관련 런타임·빌드·인프라 설정과 일부 초기화 흐름을 수정합니다. 주요 변경: GitHub Actions의 pull_request 트리거 활성화, Docker Compose에 Zipkin 서비스 추가, Gradle에 Brave/Zipkin 의존성 추가, 다수의 컨트롤러/서비스/리포지토리 메서드에 Sequence Diagram(s)sequenceDiagram
autonumber
participant Browser as 클라이언트(브라우저)
participant Web as 웹서버 (Controller)
participant Service as 서비스 계층
participant Repo as 리포지토리 / DB
participant Zipkin as Zipkin
rect rgb(250,250,255)
Note over Browser,Web: 페이지 로드 -> JS가 비동기 맵 데이터 요청
Browser->>Web: GET /web/api/tier/map
end
rect rgb(245,255,240)
Note over Web,Service: Controller(`@Observed`) 호출 -> Service(`@Observed`)
Web->>Service: tierMapInfo(ChartCondition)
Service->>Repo: DB 쿼리 (Observed)
Repo-->>Service: 쿼리 결과 (DTO)
Service-->>Web: RestaurantTierMapDTO
end
rect rgb(255,250,240)
Note over Service,Zipkin: Micrometer/Brave가 스팬 생성하여 Zipkin으로 전송
Service->>Zipkin: POST /api/v2/spans (span 데이터)
Zipkin-->>Service: 200 OK
end
rect rgb(250,250,255)
Web-->>Browser: 200 OK (mapData)
Note over Browser: initMap(mapData) 호출로 클라이언트 맵 초기화
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🧪 소형(단위) 테스트 결과106 tests 106 ✅ 2s ⏱️ Results for commit 049a130. ♻️ This comment has been updated with latest results. |
🚀 중형(통합) 테스트 결과9 tests 9 ✅ 0s ⏱️ Results for commit 049a130. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (1)
infra/docker/kustaurant/docker-compose.yml (1)
69-74: Zipkin 서비스 설정 확인 - 버전 고정 권장Zipkin 서비스가 올바르게 설정되었습니다. 포트 9411은 표준이며,
application.yml의 엔드포인트 설정과 일치합니다.프로덕션 환경의 안정성과 재현성을 위해
:latest태그 대신 특정 버전을 고정하는 것을 권장합니다:🔎 제안하는 변경사항
zipkin: - image: openzipkin/zipkin:latest + image: openzipkin/zipkin:3.4 container_name: kustaurant-zipkin ports: - "9411:9411" restart: unless-stopped
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.github/workflows/deploy.ymlinfra/docker/kustaurant/docker-compose.ymlserver/kustaurant/build.gradleserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/chart/controller/RestaurantChartController.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/chart/service/RestaurantChartService.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantChartRepositoryImpl.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantCoreInfoRepository.javaserver/kustaurant/src/main/resources/application.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: 🛠️ 변경된 경우 Kustaurant 빌드 및 푸쉬
- GitHub Check: 🚀 중형 테스트
- GitHub Check: 🧪 소형 테스트
🔇 Additional comments (7)
infra/docker/kustaurant/docker-compose.yml (1)
67-67: 재시작 정책 추가 - 좋습니다
mysqld-exporter서비스에 재시작 정책을 추가한 것은 다른 서비스들과의 일관성을 유지하고 안정성을 향상시킵니다.server/kustaurant/src/main/resources/application.yml (1)
2-2: 애플리케이션 이름 설정 - 좋습니다분산 트레이싱을 위한 필수 설정입니다. Zipkin에서 서비스를 식별하는 데 사용됩니다.
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/chart/controller/RestaurantChartController.java (1)
11-11: 관찰성 애노테이션의 일관된 적용 확인
@Observed애노테이션이 tier() 컨트롤러 메서드에 적절히 추가되었으며, 코드베이스 전반에 걸쳐 네이밍 컨벤션이 일관성 있게 적용되어 있습니다.검증된 패턴:
- 컨트롤러:
tier.controller- 서비스:
tier.service.findBasePage,tier.service.findByConditions,tier.service.getRestaurantTierMapDto- 리포지토리:
tier.repository.getRestaurantIdsWithPage,tier.repository.getRestaurantTiersBasePR 목적(티어표 로딩 속도 개선)에 부합하는 성능 모니터링 구성입니다.
server/kustaurant/build.gradle (1)
83-86: 트레이싱 의존성 추가 확인Micrometer와 Zipkin 트레이싱을 위한 표준 의존성이 올바르게 추가되었습니다. Spring Boot 3.5.6의 의존성 관리를 통해 버전이 자동으로 관리되며, io.micrometer:micrometer-tracing-bridge-brave와 io.zipkin.reporter2:zipkin-reporter-brave는 Spring Boot 공식 문서에서 권장하는 표준 조합입니다.
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantChartRepositoryImpl.java (1)
14-14: 관찰성 계층 추가 승인리포지토리 메서드에
@Observed어노테이션을 추가하여 성능 모니터링을 위한 트레이싱을 활성화한 것은 적절합니다. 네이밍 컨벤션도 일관성 있게 적용되었습니다.Also applies to: 36-36
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantCoreInfoRepository.java (1)
23-23: 리포지토리 관찰성 계층 추가 승인복잡한 조인 쿼리를 수행하는 메서드에
@Observed어노테이션을 추가한 것은 성능 병목 지점을 파악하는 데 도움이 됩니다. 구현이 정확합니다.Also applies to: 39-39
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/chart/service/RestaurantChartService.java (1)
55-55: 서비스 계층 관찰성 추가 승인서비스 메서드들에
@Observed어노테이션을 추가하여 리포지토리 계층과 함께 전체 호출 체인을 추적할 수 있게 되었습니다. 네이밍 컨벤션이 일관되게 적용되었습니다.단, 캐시 비활성화 이슈가 해결되어야 전체 성능 최적화 목표를 달성할 수 있습니다.
Also applies to: 85-85
| pull_request: | ||
| types: [ opened, reopened, synchronize, ready_for_review ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/deploy.ymlRepository: kustaurant/BackEnd-integration
Length of output: 12225
🏁 Script executed:
git log --oneline --all -20 -- .github/workflows/deploy.ymlRepository: kustaurant/BackEnd-integration
Length of output: 536
PR 이벤트로 인한 의도하지 않은 배포 위험 (확인됨)
라인 7-8의 pull_request 트리거는 kustaurant-deploy(라인 128)와 crawler-deploy(라인 215)의 배포 작업을 실행합니다. 이 배포 작업들은 if: needs.detect-changes.outputs.kustaurant == 'true'와 같은 경로 기반 조건만 가지고 있으며, 이벤트 타입 검증(if: github.event_name == 'push')이 없습니다. 따라서 PR에서 관련 파일이 변경되면 프로덕션 배포가 실행됩니다.
추가적으로, detect-changes 작업(라인 24)이 github.event.before를 사용하는데, 이는 PR 이벤트에서 정의되지 않아 경로 감지 로직이 의도대로 작동하지 않을 수 있습니다.
커밋 메시지에서 "임시로"라고 명시되었으므로, 이 PR을 머지하기 전에 반드시 제거되거나 다음과 같이 수정되어야 합니다:
- 배포 작업에
if: github.event_name == 'push'조건 추가 - 또는 PR 환경에서 경로 감지가 올바르게 작동하도록 detect-changes 작업 수정
🤖 Prompt for AI Agents
.github/workflows/deploy.yml lines 7-8: the pull_request trigger combined with
deployment jobs that only check path-based outputs can cause unintended
deployments on PRs and detect-changes uses github.event.before which is
undefined for PRs; update the workflow so deployment jobs include an explicit
event check (e.g., add if: github.event_name == 'push' to kustaurant-deploy and
crawler-deploy) OR change detect-changes to reliably handle PRs (use
github.event.pull_request.base.sha/ head.sha or adjust the action inputs to
compare against the correct refs) so path detection works in PRs, and remove the
temporary "임시로" behavior before merging.
...in/java/com/kustaurant/kustaurant/restaurant/query/chart/service/RestaurantChartService.java
Outdated
Show resolved
Hide resolved
| # tracing | ||
| tracing: | ||
| sampling: | ||
| probability: 1.0 | ||
| zipkin: | ||
| tracing: | ||
| endpoint: http://localhost:9411/api/v2/spans | ||
| observations: | ||
| annotations: | ||
| enabled: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
트레이싱 샘플링 비율 - 프로덕션 환경 고려 필요
트레이싱 설정이 올바르게 구성되었습니다. 하지만 probability: 1.0 (100% 샘플링)은 개발/디버깅 환경에는 적합하지만, 프로덕션 환경에서는 다음과 같은 문제를 야기할 수 있습니다:
- 높은 성능 오버헤드 (모든 요청에 대한 트레이스 수집)
- Zipkin 스토리지 및 네트워크 부하 증가
- 트레이싱 자체가 성능 병목이 될 수 있음
🔎 프로덕션 환경을 위한 권장 설정
프로필별로 샘플링 비율을 다르게 설정하는 것을 권장합니다:
application-prod.yml 파일 생성:
management:
tracing:
sampling:
probability: 0.1 # 프로덕션: 10% 샘플링application-dev.yml 또는 기본 설정 유지:
management:
tracing:
sampling:
probability: 1.0 # 개발: 100% 샘플링또는 환경 변수로 제어:
management:
tracing:
sampling:
probability: ${TRACING_SAMPLING_PROBABILITY:0.1}🤖 Prompt for AI Agents
In server/kustaurant/src/main/resources/application.yml around lines 58 to 67,
the tracing sampling is set to probability: 1.0 which is unsafe for production;
update configuration to use profile-specific or environment-controlled sampling
(e.g., move sampling under management.tracing.sampling and create an
application-prod.yml that sets a lower probability such as 0.1, keep 1.0 for dev
or default, or reference an environment variable like
${TRACING_SAMPLING_PROBABILITY:0.1}) so production traffic is sampled at a lower
rate while development can retain full sampling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
server/kustaurant/src/main/resources/application-local.ymlserver/kustaurant/src/main/resources/application-prod.ymlserver/kustaurant/src/main/resources/application.yml
🚧 Files skipped from review as they are similar to previous changes (1)
- server/kustaurant/src/main/resources/application.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: 🛠️ 변경된 경우 Kustaurant 빌드 및 푸쉬
- GitHub Check: 🚀 중형 테스트
🔇 Additional comments (1)
server/kustaurant/src/main/resources/application-local.yml (1)
28-31: Zipkin 로컬 개발 환경 설정 확인 완료Zipkin 트레이싱 설정이 올바르게 구성되었습니다:
- Docker Compose에 Zipkin 서비스가 9411 포트로 정의됨
application.yml에 샘플링 확률 1.0으로 설정됨application-local.yml에서 올바른 엔드포인트(http://localhost:9411/api/v2/spans) 지정됨로컬 개발 시
docker-compose up으로 Zipkin이 함께 구동되므로 추가 설정 불필요합니다.
| management: | ||
| zipkin: | ||
| tracing: | ||
| endpoint: http://zipkin:9411/api/v2/spans |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Explore repository structure to understand the project layout
echo "=== Repository structure ==="
find . -type f -name "*.yml" -o -name "*.yaml" | grep -E "(docker|compose|kube|manifest|prod)" | head -20Repository: kustaurant/BackEnd-integration
Length of output: 446
🏁 Script executed:
#!/bin/bash
# Check if the application-prod.yml file exists and view context around lines 34-37
if [ -f "server/kustaurant/src/main/resources/application-prod.yml" ]; then
echo "=== application-prod.yml (full file) ==="
cat -n "server/kustaurant/src/main/resources/application-prod.yml"
else
echo "File not found at specified path"
find . -name "application-prod.yml" -type f 2>/dev/null
fiRepository: kustaurant/BackEnd-integration
Length of output: 1140
🏁 Script executed:
#!/bin/bash
# Search for Docker Compose files
echo "=== Docker Compose files ==="
fd -e yml -e yaml | grep -iE "(docker-compose|compose)" | head -10
# Search for Kubernetes manifests
echo -e "\n=== Kubernetes manifest files ==="
fd -e yml -e yaml | grep -iE "(deployment|service|manifest|kube)" | head -10Repository: kustaurant/BackEnd-integration
Length of output: 313
🏁 Script executed:
#!/bin/bash
# Search for tracing and sampling configurations across the codebase
echo "=== Sampling configurations in application files ==="
fd "application" -e yml -e yaml --exec grep -l "sampling\|sampler" {} \;
echo -e "\n=== Zipkin-related properties ==="
fd "application" -e yml -e yaml --exec grep -i "zipkin" {} \; -printRepository: kustaurant/BackEnd-integration
Length of output: 464
🏁 Script executed:
#!/bin/bash
# View the Docker Compose production file for kustaurant to check zipkin service
echo "=== Docker Compose Production File ==="
cat -n "infra/docker/kustaurant/docker-compose.prod.yml"Repository: kustaurant/BackEnd-integration
Length of output: 506
🏁 Script executed:
#!/bin/bash
# View application.yml to check for sampling configuration
echo "=== application.yml (default profile) ==="
cat -n "server/kustaurant/src/main/resources/application.yml"Repository: kustaurant/BackEnd-integration
Length of output: 3086
🏁 Script executed:
#!/bin/bash
# Search for all zipkin-related configurations
echo "=== Searching for zipkin configurations ==="
rg -i "zipkin" --type ymlRepository: kustaurant/BackEnd-integration
Length of output: 162
🏁 Script executed:
#!/bin/bash
# Search for tracing and sampling configurations
echo "=== Searching for sampling/tracing configurations ==="
rg -i "sampling|sampler|tracing" --type ymlRepository: kustaurant/BackEnd-integration
Length of output: 172
🏁 Script executed:
#!/bin/bash
# Search for zipkin configurations without type filter
echo "=== Searching for zipkin configurations ==="
rg -i "zipkin" .
echo -e "\n=== Searching for sampling configurations ==="
rg -i "sampling|sampler" .Repository: kustaurant/BackEnd-integration
Length of output: 50387
🏁 Script executed:
#!/bin/bash
# Check if there are other docker-compose files or if zipkin is defined elsewhere
echo "=== All docker-compose files content ==="
for file in $(find . -name "docker-compose*.yml" -o -name "docker-compose*.yaml"); do
echo "=== File: $file ==="
cat "$file"
echo ""
doneRepository: kustaurant/BackEnd-integration
Length of output: 3654
프로덕션 환경에서 트레이싱 샘플링 비율 조정 필수
Zipkin 엔드포인트 설정은 올바르게 구성되었으며, Docker Compose에서 zipkin 서비스가 올바르게 정의되고 9411 포트가 노출되어 있습니다. 다만 프로덕션 배포 전에 다음을 반드시 처리해야 합니다:
샘플링 설정 문제: application.yml의 management.tracing.sampling.probability: 1.0 (100% 샘플링)이 application-prod.yml에서 오버라이드되지 않아, 프로덕션 환경에서도 모든 트레이스를 수집하게 됩니다. 이는 성능에 심각한 영향을 미칩니다.
필요한 조치:
application-prod.yml에서 샘플링 비율을 프로덕션 수준으로 명시 설정 (예: 0.1~0.2)
management:
tracing:
sampling:
probability: 0.1
zipkin:
tracing:
endpoint: http://zipkin:9411/api/v2/spans🤖 Prompt for AI Agents
In server/kustaurant/src/main/resources/application-prod.yml around lines 34-37,
the production config sets the Zipkin endpoint but does not override sampling,
so the global 100% sampling remains active; update the prod YAML to explicitly
set management.tracing.sampling.probability to a production-appropriate value
(e.g., 0.1–0.2) under the management.tracing.sampling path so sampling is
reduced in production while keeping the existing zipkin endpoint configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/kustaurant/src/main/resources/templates/restaurant/tier.html (1)
373-375: 서버 렌더링된 mapData 제거 필요이 스크립트 블록은 서버에서 mapData를 렌더링하여 전달하고 있지만,
tierMapScript.js는 이제/web/api/tier/map엔드포인트에서 비동기로 데이터를 가져옵니다. 이 코드는 사용되지 않으며 PR 목표("초기 HTML 응답에서 지도 데이터 제거")와 불일치합니다.컨트롤러가 여전히
mapData를 조회하고 있다면, 의도한 DB 비용 절감 효과가 달성되지 않습니다.🔎 제안: 사용되지 않는 스크립트 블록 제거
-<!-- 자바스크립트 데이터 전달용 태그 --> -<script th:inline="javascript"> - const mapData = /*[[${mapData}]]*/ {}; -</script> -
🧹 Nitpick comments (6)
server/kustaurant/src/main/resources/static/css/library/restaurant.css (1)
1-2: LGTM! 렌더 블로킹 최적화에 적합합니다.Google Fonts
@import주석 처리로 CRP(Critical Rendering Path)가 개선됩니다. NanumSquare 폰트가 navbar-new.html에서 CDN으로 로드되고 있으므로 대체 폰트가 정상 동작합니다.향후 유지보수를 위해 주석 처리된 이유를 간단히 기록해두면 좋을 수 있습니다 (예:
/* [Perf] Commented out for CRP optimization - #141 */).server/kustaurant/src/main/resources/static/css/newNavbar-style.css (2)
66-76: 주석 처리된 코드 정리를 고려하세요.여러 주석 처리된 코드 블록(Lines 66-76, 92-102, 132-145)이 남아있습니다. 이전 디자인 레퍼런스로 필요하지 않다면 제거하여 파일 크기를 줄이고 유지보수성을 높일 수 있습니다. 필요시 Git 히스토리에서 복구 가능합니다.
325-325:transparent키워드 사용을 권장합니다.
#0000은 CSS Color Level 4의 4자리 hex 표기법입니다. 호환성과 가독성을 위해transparent키워드 사용을 고려하세요.- background-color: #0000; + background-color: transparent;server/kustaurant/src/main/resources/static/js/restaurant/tierMapScript.js (2)
1-22: 비동기 지도 데이터 로딩 구현 완료CSRF 토큰을 사용한 fetch 요청으로 지도 데이터를 비동기로 가져오는 로직이 올바르게 구현되었습니다. 이는 초기 HTML 응답에서 지도 데이터를 제거하여 서버 측 DB 쿼리 비용을 줄이는 PR 목표를 달성합니다.
🔎 제안: 사용자 피드백을 위한 에러 처리 개선
현재 에러가 발생하면 콘솔에만 로그를 남깁니다. 사용자에게 지도를 로드할 수 없다는 메시지를 표시하는 것을 고려하세요.
} catch (e) { console.error(e); + // 사용자에게 에러 메시지 표시 + const mapArea = document.getElementById('mapArea'); + if (mapArea) { + mapArea.innerHTML = '<div style="padding: 20px; text-align: center;">지도 데이터를 불러올 수 없습니다.</div>'; + } }
97-250: 변수 재선언 개선 권장정적 분석 도구가
var를 사용한 변수 재선언을 감지했습니다 (예:marker,infoWindow,restaurantImgUrl).var는 함수 스코프를 가지므로 런타임 오류는 발생하지 않지만, 코드 가독성과 유지보수성을 위해let또는const를 사용하는 것이 현대적인 JavaScript 모범 사례입니다.🔎 제안: let/const 사용으로 변경
반복문 내에서 선언되는 변수들을
let또는const로 변경하세요:for (const restaurant of favoriteRestaurants) { - let marker = new naver.maps.Marker({ + const marker = new naver.maps.Marker({ // ... }); // ... - var restaurantImgUrl = restaurant.restaurantImgUrl; + const restaurantImgUrl = restaurant.restaurantImgUrl; if (restaurant.mainTier !== -1) { - var infoWindow = new naver.maps.InfoWindow({ + const infoWindow = new naver.maps.InfoWindow({ // ... }); } else { - var infoWindow = new naver.maps.InfoWindow({ + const infoWindow = new naver.maps.InfoWindow({ // ... }); } }다른 반복문에도 동일하게 적용하세요.
server/kustaurant/src/main/resources/db/migration/local/V19__add_rating_index.sql (1)
12-22: 인덱스 설계는 적절하지만 런타임 성능 검증은 코드 리뷰 단계에서 불가능합니다.코드 분석 결과, 인덱스 설계는 쿼리 패턴과 잘 맞습니다:
restaurantActive()메서드가status = "ACTIVE"필터링을 사용하므로 restaurant 테이블 인덱스의 status 컬럼 우선 배치가 타당합니다.RestaurantSituationRelationJpaRepository.findByRestaurantIdAndSituationId()메서드가 존재하여(data_count, situation_id, restaurant_id)인덱스 컬럼 순서가 일반적인 필터 → 커버링 패턴을 따릅니다.restaurantPosition,restaurantCuisine필터를 사용하는 다중 쿼리 저장소들이 확인되어 해당 인덱스들의 필요성이 입증됩니다.다만, EXPLAIN 결과 분석(ALL→ref/range 개선), filesort 제거 확인, 실제 실행 시간 측정은 라이브 데이터베이스에 대한 런타임 테스트를 통해서만 검증 가능합니다. 프로덕션/스테이징 배포 후 성능 지표를 확인하세요.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (21)
common/jpa/src/main/java/com/kustaurant/jpa/rating/entity/RatingEntity.javaserver/crawler/src/main/java/com/kustaurant/crawler/aianalysis/domain/model/AiAnalysisJob.javaserver/crawler/src/main/java/com/kustaurant/crawler/aianalysis/service/AnalyzeReviewTxService.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/common/view/ViewCountService.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/common/view/ViewerKeyProvider.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/chart/controller/RestaurantChartController.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantChartRepositoryImpl.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantCoreInfoRepository.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/controller/RestaurantController.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/infrastructure/repository/RestaurantDetailRepositoryImpl.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/infrastructure/repository/query/RestaurantDetailQuery.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/service/RestaurantService.javaserver/kustaurant/src/main/resources/application-local.ymlserver/kustaurant/src/main/resources/db/migration/local/V19__add_rating_index.sqlserver/kustaurant/src/main/resources/static/css/library/restaurant.cssserver/kustaurant/src/main/resources/static/css/library/style.min.cssserver/kustaurant/src/main/resources/static/css/newNavbar-style.cssserver/kustaurant/src/main/resources/static/css/topSearch-style.cssserver/kustaurant/src/main/resources/static/js/restaurant/tierMapScript.jsserver/kustaurant/src/main/resources/templates/common/navbar-new.htmlserver/kustaurant/src/main/resources/templates/restaurant/tier.html
💤 Files with no reviewable changes (1)
- server/kustaurant/src/main/resources/static/css/topSearch-style.css
🚧 Files skipped from review as they are similar to previous changes (2)
- server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantCoreInfoRepository.java
- server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/chart/controller/RestaurantChartController.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-05T17:31:46.031Z
Learnt from: DingWoonee
Repo: kustaurant/BackEnd-integration PR: 80
File: src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/infrastructure/repository/query/RestaurantDetailQuery.java:57-57
Timestamp: 2025-08-05T17:31:46.031Z
Learning: restaurant_rating 테이블의 score와 tier 컬럼은 NOT NULL 제약조건이 있지만, LEFT JOIN 시 rating 레코드가 없는 식당의 경우 JOIN 결과가 NULL이 될 수 있어 coalesce 처리가 필요함
Applied to files:
server/kustaurant/src/main/resources/db/migration/local/V19__add_rating_index.sqlcommon/jpa/src/main/java/com/kustaurant/jpa/rating/entity/RatingEntity.javaserver/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantChartRepositoryImpl.java
📚 Learning: 2025-08-05T17:31:46.031Z
Learnt from: DingWoonee
Repo: kustaurant/BackEnd-integration PR: 80
File: src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/infrastructure/repository/query/RestaurantDetailQuery.java:57-57
Timestamp: 2025-08-05T17:31:46.031Z
Learning: restaurant_rating 테이블의 score와 tier 컬럼은 NOT NULL 제약조건이 있지만, LEFT JOIN 시 rating 레코드가 없는 식당의 경우 JOIN 결과가 NULL이 될 수 있어 coalesce 처리가 필요함. DB 제약조건과 JOIN 결과는 다른 개념임.
Applied to files:
server/kustaurant/src/main/resources/db/migration/local/V19__add_rating_index.sqlcommon/jpa/src/main/java/com/kustaurant/jpa/rating/entity/RatingEntity.java
🪛 Biome (2.1.2)
server/kustaurant/src/main/resources/static/js/restaurant/tierMapScript.js
[error] 143-143: Shouldn't redeclare 'infoWindow'. Consider to delete it or rename it.
'infoWindow' is defined here:
(lint/suspicious/noRedeclare)
[error] 187-189: Shouldn't redeclare 'restaurantImgUrl'. Consider to delete it or rename it.
'restaurantImgUrl' is defined here:
(lint/suspicious/noRedeclare)
[error] 189-189: Shouldn't redeclare 'infoWindow'. Consider to delete it or rename it.
'infoWindow' is defined here:
(lint/suspicious/noRedeclare)
[error] 221-221: Shouldn't redeclare 'marker'. Consider to delete it or rename it.
'marker' is defined here:
(lint/suspicious/noRedeclare)
[error] 238-238: Shouldn't redeclare 'restaurantImgUrl'. Consider to delete it or rename it.
'restaurantImgUrl' is defined here:
(lint/suspicious/noRedeclare)
[error] 241-241: Shouldn't redeclare 'infoWindow'. Consider to delete it or rename it.
'infoWindow' is defined here:
(lint/suspicious/noRedeclare)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: 🛠️ 변경된 경우 Kustaurant 빌드 및 푸쉬
- GitHub Check: 🛠️ 변경된 경우 Crawler 빌드 및 푸쉬
🔇 Additional comments (21)
server/kustaurant/src/main/resources/templates/common/navbar-new.html (1)
6-11: LGTM! CRP 최적화가 적절하게 적용되었습니다.
- Google Fonts preconnect 주석 처리:
@import비활성화와 일관성 유지- NanumSquare CDN은 유지되어 폰트 대체 동작 보장
defer속성 추가로 스크립트가 렌더 블로킹하지 않음server/kustaurant/src/main/resources/static/css/newNavbar-style.css (1)
58-63: CSS 파일 병합이 잘 수행되었습니다.topSearch-style.css의 내용이 이 파일로 병합되어 HTTP 요청 수가 줄어들었습니다. 섹션 주석(
/* topSearch */)으로 구분된 점도 가독성에 좋습니다.server/crawler/src/main/java/com/kustaurant/crawler/aianalysis/domain/model/AiAnalysisJob.java (1)
35-38: 메서드 시그니처 변경 승인
updateReviewCount메서드가LocalDateTime now파라미터를 받도록 변경되어, 클래스 내 다른 메서드들(startJob,doneJob,failJob,complete)과 일관된 패턴을 따르게 되었습니다. 이는 타임스탬프 관리 책임을 도메인 엔티티에서 분리하여 테스트 가능성과 일관성을 향상시킵니다.변경 사항이 깔끔하게 구현되었습니다.
server/crawler/src/main/java/com/kustaurant/crawler/aianalysis/service/AnalyzeReviewTxService.java (1)
65-73: 타임스탐프 일관성 - 현재 구현은 정상입니다검토 결과, 공유된
now타임스탐프를 사용하는 현재 구현은 올바릅니다. @retryable 데코레이터는 낙관적 잠금 예외 발생 시 메서드 전체를 재실행하므로, 각 재시도마다 line 65에서 새로운 타임스탐프가 생성됩니다. 따라서 최종 성공한 업데이트는 해당 시도의 실제 시간을 정확히 반영합니다.server/kustaurant/src/main/resources/templates/restaurant/tier.html (3)
28-30: CSRF 토큰 설정이 올바르게 추가되었습니다.비동기 fetch 요청에 필요한 CSRF 토큰과 헤더를 메타 태그로 노출하여
tierMapScript.js에서 사용할 수 있도록 했습니다. 보안 모범 사례를 따르고 있습니다.
46-49: 스크립트 defer 속성 추가로 렌더링 성능 개선jQuery, vendors, main.js, Naver Maps 스크립트에
defer속성을 추가하여 렌더링 차단을 방지했습니다. HTML 파싱이 완료된 후 순서대로 실행되므로 초기 페이지 로딩 속도가 개선됩니다.
33-33: Font Awesome 스크립트 제거 완료Font Awesome 스크립트가 올바르게 주석 처리되었으며, 템플릿에서 Font Awesome 아이콘(
fa-*클래스)을 사용하는 부분이 없음을 확인했습니다. Bootstrap Icons CSS가 대체 아이콘 라이브러리로 로드되어 있습니다.server/kustaurant/src/main/resources/static/js/restaurant/tierMapScript.js (1)
24-382: 지도 초기화 로직이 올바르게 리팩터링되었습니다.기존의 전역 변수 기반 초기화에서
initMap(mapData)함수 기반으로 변경하여, 비동기로 받은 데이터를 사용하도록 구조를 개선했습니다. 마커 생성, 이벤트 핸들러, 가시성 제어 로직이 모두 적절하게 캡슐화되었습니다.server/kustaurant/src/main/java/com/kustaurant/kustaurant/common/view/ViewerKeyProvider.java (1)
4-4: LGTM: 관찰성 계측이 적절하게 추가되었습니다.
@Observed애너테이션 추가로 뷰어 키 해석 과정의 성능을 추적할 수 있게 되었습니다. 메서드 시그니처나 로직 변경 없이 순수하게 계측만 추가되었으며, PR의 성능 분석 목표와 잘 부합합니다.Also applies to: 20-20
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantChartRepositoryImpl.java (1)
14-14: LGTM: 리포지토리 레벨 관찰성 추가가 적절합니다.티어 테이블 조회 성능을 측정할 수 있도록
@Observed애너테이션이 추가되었습니다. 명시적인 관찰 이름(tier.repository.getRestaurantIdsWithPage)을 지정하여 메트릭 추적이 용이합니다.Also applies to: 36-36
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/infrastructure/repository/query/RestaurantDetailQuery.java (1)
24-24: LGTM: 레스토랑 상세 조회 성능 추적을 위한 관찰성 추가.복잡한 쿼리를 수행하는
getRestaurantDetails메서드에@Observed애너테이션이 추가되어 상세 페이지 조회 성능을 모니터링할 수 있게 되었습니다. 로직 변경 없이 순수하게 계측만 추가되었습니다.참고: Line 61에서
tier.coalesce(0)처리가 이미 적용되어 있어, LEFT JOIN으로 인한 NULL 값이 적절히 처리되고 있습니다.Based on learnings about LEFT JOIN and NULL handling in rating table.
Also applies to: 39-39
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/controller/RestaurantController.java (1)
11-11: LGTM: 엔드포인트 레벨 관찰성 추가.레스토랑 상세 페이지 엔드포인트에
@Observed애너테이션이 추가되어 전체 요청 처리 시간을 추적할 수 있습니다. 서비스 및 리포지토리 레이어의@Observed와 결합하여 전체 트레이스를 확인할 수 있어 성능 병목 지점 파악에 유용합니다.Also applies to: 34-34
server/kustaurant/src/main/resources/application-local.yml (1)
30-33: Zipkin 로컬 설정이 올바르게 구성되었습니다.docker-compose.yml에 Zipkin 서비스(포트 9411)가 정의되어 있으며, application-local.yml의 엔드포인트 설정이 일치합니다. 필요한 의존성도 build.gradle에 포함되어 있습니다.
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/infrastructure/repository/RestaurantDetailRepositoryImpl.java (1)
10-10: LGTM! 관찰성 계측이 올바르게 적용되었습니다.Spring 관리 빈의 인스턴스 메서드에
@Observed어노테이션이 적절하게 추가되어 AOP를 통한 추적이 정상적으로 작동합니다.Also applies to: 22-23
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/service/RestaurantService.java (1)
7-7: LGTM! 서비스 계층 관찰성 계측이 적절합니다.서비스 계층과 리포지토리 계층 모두에
@Observed가 적용되어 상세한 트레이스 스팬을 제공할 수 있습니다.Also applies to: 22-24
server/kustaurant/src/main/java/com/kustaurant/kustaurant/common/view/ViewCountService.java (1)
26-36: LGTM! 인스턴스 메서드 관찰성 계측이 올바릅니다.
countOncePerHour메서드에@Observed가 적절하게 적용되어 조회수 카운팅 작업을 추적할 수 있습니다.common/jpa/src/main/java/com/kustaurant/jpa/rating/entity/RatingEntity.java (3)
48-50: 헬퍼 메서드 설계는 적절합니다.
tier >= 0로직을 별도 메서드로 분리하여 일관성을 유지하고 가독성을 높였습니다. 생성자와 업데이트 메서드에서 재사용되고 있어 DRY 원칙을 잘 따르고 있습니다.
52-62: 생성자에서hasTier초기화가 올바르게 구현되었습니다.58번 라인에서 헬퍼 메서드를 통해
hasTier를 초기화하여tier값과 일관성을 보장합니다.
64-73: 업데이트 메서드에서hasTier동기화가 올바르게 구현되었습니다.69번 라인에서
tier가 변경될 때마다hasTier도 함께 갱신되어 일관성이 유지됩니다.server/kustaurant/src/main/resources/db/migration/local/V19__add_rating_index.sql (2)
8-10: 복합 인덱스 설계를 재검증하세요.
idx_rr_temp_hastier_order인덱스는 주로 티어표 페이지의getRestaurantIdsWithPage()쿼리를 최적화하기 위해 설계되었습니다. 해당 쿼리는 다음과 같이 실행됩니다:
- WHERE:
is_temp = false AND (tier > 0 또는 tier < 0)- ORDER BY:
has_tier DESC, tier ASC, final_score DESC이 패턴과 인덱스 열 순서가 부분적으로 일치하므로, 티어표 페이지 쿼리는 인덱스를 효과적으로 활용할 수 있습니다.
그러나 다른 쿼리들은 인덱스 활용도가 낮습니다:
RestaurantHomeRepositoryImpl,RestaurantDrawRepositoryImpl:is_temp = true필터 사용 (인덱스 설계와 반대 방향)ORDER BY final_score DESC만 사용하는 쿼리들: 인덱스 열 구조와 불일치또한 WHERE 절에서
tier > 0조건으로has_tier를 간접적으로 필터링하고 있으므로, 인덱스가has_tier DESC를 두 번째 열로 가진 것이 모든 시나리오에서 최적인지 확인이 필요합니다.
1-6: 마이그레이션 코드는 기술적으로 올바르지만, 실제 사용되지 않는 컬럼 추가입니다.
has_tier컬럼의 추가와 UPDATE 로직(tier >= 0)은 스키마 제약조건과 일치합니다.NOT NULL DEFAULT FALSE설정도 안전하고,tier컬럼의 CHECK 제약조건((tier >= 1 AND tier <= 5) OR tier = -1)을 고려할 때 UPDATE 문은 올바르게 동작합니다.그러나
RestaurantDetailQuery.java를 포함한 식별된 Java 쿼리 코드에서hasTier필드가 실제로 사용되지 않습니다. 인덱스(idx_rr_temp_hastier_order)가 생성되었지만 WHERE 절에서 이 컬럼을 필터링하는 쿼리가 없어 인덱스 최적화 효과가 제한적입니다. 이 컬럼이 미래 기능을 위한 것이라면 용도를 명확히 하고, 실제 쿼리 통합 계획을 확인하세요.
| @Builder.Default | ||
| @Column(nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0") | ||
| private boolean hasTier = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
빌더 패턴 사용 시 불일치 가능성을 고려하세요.
@Builder.Default로 hasTier = false를 설정했지만, 클래스 레벨의 @Builder 어노테이션을 통해 빌더를 사용할 경우 .tier(5).build() 형태로 객체를 생성하면 tier >= 0임에도 hasTier가 false로 남아 불일치 상태가 발생할 수 있습니다.
명시적 생성자(52-62번 라인)는 이를 올바르게 처리하지만, 빌더 패턴을 사용하는 코드에서는 일관성이 깨질 수 있습니다.
🔎 일관성 보장을 위한 제안
방안 1: hasTier를 계산된 필드로 변경 (권장)
- @Builder.Default
- @Column(nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0")
- private boolean hasTier = false;
+ @Column(nullable = false, columnDefinition = "TINYINT(1) DEFAULT 0")
+ private boolean hasTier;그리고 빌더 후처리를 위해 커스텀 빌더를 구현:
public static class RatingEntityBuilder {
public RatingEntity build() {
RatingEntity entity = new RatingEntity(restaurantId, selfScore, tier, isTemp, ratedAt, finalScore);
return entity;
}
}방안 2: hasTier를 트랜잭션 필드로 만들고 getter에서 계산
이 경우 DB 저장은 여전히 필요하므로 성능 최적화 목적과 맞지 않을 수 있습니다.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In common/jpa/src/main/java/com/kustaurant/jpa/rating/entity/RatingEntity.java
around lines 33-35 (and note the explicit constructor at lines ~52-62), the
@Builder.Default boolean hasTier = false can become inconsistent when using the
Lombok builder (e.g. .tier(5).build() leaves hasTier false). Fix by making
hasTier a computed value or ensuring the builder sets it: preferred — remove the
mutable default and implement a custom RatingEntityBuilder.build() that
constructs the entity via the existing constructor (so hasTier is derived
consistently from tier), or alternatively make hasTier transient/removed and
compute hasTier in the getter (return tier != null && tier >= 0); update/remove
the @Builder.Default and adjust annotations accordingly so builder-created
instances have consistent hasTier semantics.
| @Observed | ||
| public static String buildDedupKey(ViewResourceType type, long id, String viewerKey) { | ||
| return FORMAT_KEY.formatted(type.key(), type.slot(id), viewerKey); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all calls to buildDedupKey to understand usage pattern
rg -n 'buildDedupKey' --type=java -B2 -A2Repository: kustaurant/BackEnd-integration
Length of output: 1523
🌐 Web query:
Micrometer @Observed annotation static method support Spring
💡 Result:
Short answer: @observed will not be applied to static methods when you rely on Spring AOP (the default/proxy-based approach). You can get @observed on static methods only if you use full AspectJ weaving (compile‑time or load‑time) so the AspectJ aspect can advise static methods. Otherwise instrument static code manually with Observation API.
References:
- Micrometer / Observations: annotation support (uses AspectJ aspect or Spring AOP; you normally register an ObservedAspect). [1]
- Spring AOP is proxy‑based and cannot advise static (or private/final) methods. [2]
- AspectJ (CTW/LTW) can weave and advise static methods; use LTW/CTW to intercept static methods. [3]
If you want, I can show (a) how to configure AspectJ LTW/CTW for Micrometer @observed, or (b) the manual Observation.create(...) pattern for static methods.
정적 메서드에서 @Observed 어노테이션이 작동하지 않습니다.
@Observed 어노테이션은 기본 설정의 Spring AOP 프록시 메커니즘에 의존하는데, Spring AOP는 정적 메서드를 인터셉트할 수 없습니다. 이 어노테이션이 작동하려면 AspectJ 위빙(컴파일 타임 또는 로드 타임)을 설정해야 하므로, 일반적인 Spring 환경에서는 효과가 없습니다.
buildDedupKey는 이미 @Observed가 적용된 countOncePerHour 인스턴스 메서드에서만 호출되므로, 해당 정적 메서드에서 @Observed를 제거하는 것을 권장합니다.
🤖 Prompt for AI Agents
In
server/kustaurant/src/main/java/com/kustaurant/kustaurant/common/view/ViewCountService.java
around lines 21–24: the @Observed annotation on the static method buildDedupKey
does nothing because Spring AOP cannot intercept static methods (AspectJ weaving
would be required); remove the @Observed annotation from this static method (or
alternatively make it an instance method only if you need it intercepted),
leaving the annotation on the instance method countOncePerHour that calls
buildDedupKey, then rebuild and run tests to ensure no behavioral change.
| ratingEntity.hasTier.desc(), | ||
| ratingEntity.tier.asc(), | ||
| ratingEntity.finalScore.desc() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# hasTier 컬럼 추가 마이그레이션 파일 검색
echo "=== Searching for hasTier column migration ==="
rg -n "hasTier|has_tier" --type=sql -C 3
# 관련 인덱스 정의 검색
echo -e "\n=== Searching for indexes on rating table ==="
rg -n "CREATE INDEX|ADD INDEX" --type=sql -C 2 | head -100
# RatingEntity에서 hasTier 필드 정의 확인
echo -e "\n=== Checking hasTier field in RatingEntity ==="
fd -e java | xargs grep -l "RatingEntity" | head -5Repository: kustaurant/BackEnd-integration
Length of output: 5016
🏁 Script executed:
# 더 상세한 검색: 마이그레이션 파일 목록
echo "=== Migration files ==="
fd -e sql | grep -i migr | head -20Repository: kustaurant/BackEnd-integration
Length of output: 1878
🏁 Script executed:
# RestaurantChartRepositoryImpl.java 파일 전체 내용 확인 (크기 확인)
fd -e java | grep -i "RestaurantChartRepositoryImpl"Repository: kustaurant/BackEnd-integration
Length of output: 216
🏁 Script executed:
# Check RestaurantChartRepositoryImpl.java file size and read relevant sections
wc -l server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantChartRepositoryImpl.javaRepository: kustaurant/BackEnd-integration
Length of output: 219
🏁 Script executed:
# Read the entire RestaurantChartRepositoryImpl.java file
cat -n server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantChartRepositoryImpl.javaRepository: kustaurant/BackEnd-integration
Length of output: 5093
🏁 Script executed:
# Check RatingEntity for hasTier field definition
cat -n common/jpa/src/main/java/com/kustaurant/jpa/rating/entity/RatingEntity.javaRepository: kustaurant/BackEnd-integration
Length of output: 2617
LEFT JOIN으로 인한 NULL 값 처리가 누락되었습니다. ORDER BY 절에서 COALESCE를 추가해야 합니다.
마이그레이션과 인덱스 설정은 정상적으로 진행되었습니다:
- ✓ V19__add_rating_index.sql에서 has_tier 컬럼 추가 완료 (NOT NULL DEFAULT FALSE)
- ✓ idx_rr_temp_hastier_order 인덱스 생성 확인
그러나 LEFT JOIN에서 rating이 없는 레스토랑의 경우 NULL 처리가 필수입니다:
데이터베이스 컬럼은 NOT NULL이지만, LEFT JOIN 결과에서는 rating 레코드가 없는 식당의 경우 ratingEntity 전체가 NULL이 되므로 hasTier, tier, finalScore도 모두 NULL입니다. 현재 코드(56-58줄)는 이를 처리하지 않습니다.
-- 필요한 처리:
ratingEntity.hasTier.desc(), -- NULL 처리 필요
ratingEntity.tier.asc(), -- NULL 처리 필요
ratingEntity.finalScore.desc() -- NULL 처리 필요COALESCE를 사용하여 NULL값을 기본값(false, -1, 0 등)으로 변환해야 정렬이 의도대로 작동합니다.
🤖 Prompt for AI Agents
In
server/kustaurant/src/main/java/com/kustaurant/kustaurant/restaurant/query/common/infrastructure/repository/RestaurantChartRepositoryImpl.java
around lines 56-58, the ORDER BY uses ratingEntity.hasTier, ratingEntity.tier,
ratingEntity.finalScore directly which yields NULLs for restaurants without a
LEFT JOINed rating; wrap each ordering expression with COALESCE to provide
non-null defaults (e.g., coalesce(hasTier, false) for boolean, coalesce(tier,
-1) for tier, coalesce(finalScore, 0) for score) so sorting behaves as intended;
implement these COALESCE expressions using the project's QueryDSL/JPQL API
(e.g., coalesce/Expressions.coalesce or the entity path coalesce methods) and
keep the same ASC/DESC directions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
server/kustaurant/src/main/resources/db/migration/prod/V6__20260104.sql (2)
14-24: 다수의 인덱스 추가로 인한 누적 영향 검토이 마이그레이션에서 총 5개의 인덱스가 추가되며,
restaurant테이블에만 3개의 인덱스가 생성됩니다. 특히 세 인덱스 모두status로 시작하는데, 일부 중복이 있을 수 있습니다:
idx_r_status_id: (status, restaurant_id)idx_r_status_cuisine_id: (status, restaurant_cuisine, restaurant_id)idx_r_status_position_id: (status, restaurant_position, restaurant_id)각 인덱스가 서로 다른 쿼리 패턴을 지원하더라도, 전체적인 쓰기 성능 저하와 스토리지 비용 증가를 고려해야 합니다. 특히
restaurant_id가 PK라면 인덱스 끝에 명시적으로 포함할 필요가 없을 수 있습니다(InnoDB는 자동으로 PK를 포함).프로덕션 환경에서 각 인덱스의 실제 사용률을 모니터링하고, 사용되지 않는 인덱스는 제거하는 것을 권장합니다.
1-24: 프로덕션 마이그레이션 실행 전략 수립 권장대용량 테이블의 경우
ALTER TABLE과CREATE INDEX작업이 오래 걸리고 테이블 잠금을 유발할 수 있습니다. 다음 사항들을 고려해주세요:
- 실행 시점: 서비스 트래픽이 적은 시간대 선택
- 잠금 전략: MySQL 5.6+ 또는 MariaDB 10.0+에서는
ALGORITHM=INPLACE, LOCK=NONE옵션 고려- 롤백 계획: 인덱스 제거 스크립트 준비
- 모니터링: 마이그레이션 진행 상황 및 영향 모니터링
참고로,
has_tier컬럼은 derived 값이므로 generated column으로 정의하는 것도 고려해볼 수 있습니다:ADD COLUMN has_tier BOOLEAN GENERATED ALWAYS AS (tier >= 0) STORED이렇게 하면 애플리케이션에서 명시적으로 유지보수할 필요가 없어집니다.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
server/kustaurant/src/main/resources/db/migration/prod/V6__20260104.sqlserver/kustaurant/src/main/resources/templates/restaurant/tier.html
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-05T17:31:46.031Z
Learnt from: DingWoonee
Repo: kustaurant/BackEnd-integration PR: 80
File: src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/infrastructure/repository/query/RestaurantDetailQuery.java:57-57
Timestamp: 2025-08-05T17:31:46.031Z
Learning: restaurant_rating 테이블의 score와 tier 컬럼은 NOT NULL 제약조건이 있지만, LEFT JOIN 시 rating 레코드가 없는 식당의 경우 JOIN 결과가 NULL이 될 수 있어 coalesce 처리가 필요함
Applied to files:
server/kustaurant/src/main/resources/db/migration/prod/V6__20260104.sql
📚 Learning: 2025-08-05T17:31:46.031Z
Learnt from: DingWoonee
Repo: kustaurant/BackEnd-integration PR: 80
File: src/main/java/com/kustaurant/kustaurant/restaurant/restaurant/infrastructure/repository/query/RestaurantDetailQuery.java:57-57
Timestamp: 2025-08-05T17:31:46.031Z
Learning: restaurant_rating 테이블의 score와 tier 컬럼은 NOT NULL 제약조건이 있지만, LEFT JOIN 시 rating 레코드가 없는 식당의 경우 JOIN 결과가 NULL이 될 수 있어 coalesce 처리가 필요함. DB 제약조건과 JOIN 결과는 다른 개념임.
Applied to files:
server/kustaurant/src/main/resources/db/migration/prod/V6__20260104.sql
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: 🛠️ 변경된 경우 Kustaurant 빌드 및 푸쉬
- GitHub Check: 🛠️ 변경된 경우 Crawler 빌드 및 푸쉬
🔇 Additional comments (5)
server/kustaurant/src/main/resources/templates/restaurant/tier.html (3)
28-31: CSRF 토큰이 올바르게 구현되었습니다
tierMapScript.js에서 메타 태그로부터 CSRF 토큰을 정상적으로 받아와/web/api/tier/mapAPI 호출 시 요청 헤더에 포함시키고 있습니다. Spring Security CSRF 보호 패턴을 올바르게 적용하셨습니다.
33-33: Font Awesome 스크립트 안전하게 제거됨렌더링 차단 리소스를 줄이기 위해 Font Awesome 스크립트를 제거하고 Bootstrap Icons로 완전히 마이그레이션했습니다. 티어 페이지, navbar, footer 및 관련 JavaScript 파일에서 Font Awesome 아이콘 사용 흔적이 없으므로 변경사항이 안전합니다.
46-51: 스크립트 defer 속성 추가로 렌더링 성능 개선렌더 블로킹을 방지하기 위해 주요 스크립트에
defer속성을 추가한 것은 훌륭한 CRP 최적화입니다. 스크립트 실행 순서가 유지되므로 의존성 문제도 없습니다.네이버 지도 API와의 비동기 처리도 안전합니다.
tierMapScript.js의 모든 코드가DOMContentLoaded이벤트 콜백 내에서 실행되며, 이 시점에는 네이버 지도 API가 이미 로드되어 있습니다. 추가로 지도init이벤트 리스너를 등록하여 비동기 초기화도 적절히 처리하고 있습니다.server/kustaurant/src/main/resources/db/migration/prod/V6__20260104.sql (2)
4-8:tier >= 0로직과has_tier유지 관리 구현이 올바르게 되어 있습니다.검증 결과:
RatingEntity의tier기본값이 -1이며,hasTier()메서드가tier >= 0으로 정의되어 있어 로직이 정확합니다.- 생성자와
updateRatingData()메서드에서 모두this.hasTier = hasTier(tier)로 티어 값 변경 시has_tier를 올바르게 유지하고 있습니다.- 마이그레이션의
DEFAULT FALSE설정은ALTER TABLE과UPDATE사이의 데이터 일관성을 안전하게 처리하는 표준 패턴입니다.
11-12: 5개 컬럼 복합 인덱스는 실제 쿼리 패턴과 잘 맞춰져 있습니다이 인덱스는 코드에서 실제로 사용되는 쿼리 패턴과 정확하게 맞춰져 있습니다:
is_temp컬럼은 WHERE 조건에서 사용됨 (isTemp.not())has_tier DESC는 ORDER BY에서 정렬 기준으로 사용됨final_score DESC도 ORDER BY에서 사용됨restaurant_id가 마지막에 포함되어 커버링 인덱스 역할마이그레이션 파일에서
has_tier컬럼을 특별히 추가하고 이 인덱스를 함께 생성한 것을 보면 의도된 최적화입니다. 저장 작업도 배치 방식으로 처리되므로 쓰기 오버헤드는 관리 가능한 수준입니다.
이슈(#140)에서 봤듯이 다른 화면에 비해 티어표가 월등히 느려서 원인을 파악하고 개선해봄.
1 티어표 화면 요청/응답 지표 분석
1.1 서버 트레이스 분석
(근데 2번 데이터는 처음 티어표 화면을 보여줄 때 필요한 부분이 아님)
1.2 브라우저 개발자 도구 분석
서버 응답(파란색) 이후에 css나 js 다운로드로 인해(노란색, 보라색) 사용자에게 실제로 화면이 보이기까지 시간이 너무 오래걸림.
2 작업 진행 방향
캐싱 외적인 근본적인 원인을 개선하고 싶었음.
3 상세 설명
3.1 테이블 인덱스 추가
데이터가 많지 않아서 큰 의미는 없지만, 예의상 최소한의 인덱스를 추가함
테스트 시나리오
조건을 넣지 않고 티어표 화면을 100번 호출
DB로 날아가는 대표 쿼리
중식,아시안,혼밥,2~4인조건을 넣고 티어표 화면을 100번 호출DB로 날아가는 대표 쿼리
인덱스 추가 전/후 비교
[performance_schema 기준 실행 시간 전/후]


데이터가 적어서 유의미한 차이는 없는듯함.
다만,
COUNT쿼리의 경우는ORDER BY가 없고 PK만SELECT하기 때문에 index-only-scan으로 데이터가 적은데도 실행 시간이 줄어든게 보이긴 함. (그래도 유의미한 차이는 없는듯)[쿼리 1 EXPLAIN 전/후]


filesort 제거.
기존에
ORDER BY에CASE문을 써서 인덱스를 쓸 수 없었는데,hasTier칼럼을 추가해서 해결함.id만 조회하기 때문에 커버링 인덱스가 됨.
[쿼리 2 EXPLAIN 전/후]


filesort를 제거하고 싶었으나, 성능 개선은 거의 없는데 그로 인한 변경이 너무 복잡해져서 진행 안 했고,
EXPLAIN 기준으로 읽는 rows만 줄임
3.2 지도 데이터를 HTML 응답에서 제거
티어표 화면에서 처음부터 지도 데이터가 필요하지 않음
→ 지도 데이터를 js로 불러오게 함.
→ 지도 데이터를 조회하는 시간이 사라짐
3.3 프론트엔드 CRP 최적화
HTML 응답을 받고도 js(노란색)나 css 파일(보라색) 다운로드 때문에 렌더링이 블록킹 됨(빨간 세모)
→ 실제 화면이 사용자에게 표시되기까지 시간이 더 소비됨
렌더링 지연 주요 원인
수정한 부분
모든 css를 하나로 합치고 외부 css를 없애고 싶었으나 득보다는 실이 많아 보여서 하지 않음.
실제로 한 것들.
<script>태그에defer속성을 추가함아래 이미지는 개선 후

렌더링을 블록킹 하는 것(빨간 세모)이 앞쪽에만 존재
4 작업 전/후 비교
테스트 진행 방식
각각 인스턴스 및 스프링 프로세스 재시작 후 5분 뒤에 티어표 화면을 powershell로 100번 순차 호출하고,
이후에는 20~30회 정도 직접 새로고침을 함.
그리고 그 중 가장 평균적인 수치로 가져옴.
작업 전



평균적인 HTML 응답 시간
LCP 1~1.5초
서버 응답시간 p99, p95
작업 후



평균적인 HTML 응답 시간
LCP 0.5~1초 미만
서버 응답시간 p99, p95
앞으로의 계획
구현 복잡도가 올라가서 조금 타협을 한 부분이 있지만, 일단 목표로 한 것은 다 진행했다고 생각함.
근데 서버에서 실행 시간이 길어지면서 또 느려질 수 있기 때문에 2~3주 정도 뒤에 지표를 다시 봐야할 듯함.