Skip to content

[Feat/#199] 마트 공휴일 휴무 지원 및 영업중 여부 판별에 공휴일 반영 - #201

Merged
tnals0924 merged 3 commits into
mainfrom
feat/#199-market-holiday-closed
Jul 16, 2026
Merged

[Feat/#199] 마트 공휴일 휴무 지원 및 영업중 여부 판별에 공휴일 반영#201
tnals0924 merged 3 commits into
mainfrom
feat/#199-market-holiday-closed

Conversation

@tnals0924

Copy link
Copy Markdown
Contributor

#️⃣연관된 이슈

📝작업 내용

1. 마트 영업시간에 공휴일 휴무 여부(isHolidayClosed) 추가

  • BusinessHours JSONB에 slots와 같은 위계로 isHolidayClosed: Boolean 추가 (기본값 false → 기존 데이터 마이그레이션 불필요)
  • 마트 등록/수정 요청에서 생략 가능(생략 시 false), owner·user 상세 및 전단 프리뷰 응답에 노출

2. 공휴일 API 연동 및 isOpenNow 반영

  • core: HolidayServiceHolidayReaderHolidayCache/HolidayClient 인터페이스 (레이어 규칙 준수)
  • infrastructure:client: 한국천문연구원 특일정보 API(getRestDeInfo) 클라이언트 — 전용 holidayRestClient 빈, serviceKey는 URI 변수 확장으로 엄격 인코딩(+/= 이중 인코딩 함정 회피)
  • infrastructure:redis: 연 단위 캐싱(holiday:{year}, TTL 1일) — API 월 10,000회 제한 대응, 다중 인스턴스 공유. API 장애 시 짧은 TTL(10분) 빈 값 캐싱으로 재시도 폭주 방지
  • 장애 대응: 캐시·API 모두 실패 시 공휴일 아님으로 간주(경고 로그만) — isOpenNow가 조회 전체를 깨지 않음
  • isOpenAt(now, holidays): 공휴일 휴무 마트는 공휴일에 휴무 처리, 전날이 공휴일이면 자정 넘김 꼬리 영업도 휴무 처리
  • 호출부 4곳(user 마트 상세/주변 목록, owner 전단 프리뷰 2곳) 요청당 1회 공휴일 조회 후 전달

💬리뷰 요구사항(선택)

  • 배포 환경에 HOLIDAY_API_SERVICE_KEY(data.go.kr Decoding 키) 등록이 필요합니다. 키가 없어도 앱은 뜨고 요일 기준으로만 판별합니다.
  • 공휴일 캐시 TTL 1일(임시공휴일 반영 목적)이 적절한지 의견 부탁드립니다.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tnals0924, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bd82a9db-83e1-43ad-bd71-69b13fcbafa4

📥 Commits

Reviewing files that changed from the base of the PR and between 91882f8 and 52349df.

📒 Files selected for processing (3)
  • api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/flyer/FlyerPreviewQueryFacade.kt
  • api/user-api/src/main/kotlin/kr/dongchimi/api/user/market/MarketDetailQueryFacade.kt
  • api/user-api/src/main/kotlin/kr/dongchimi/api/user/market/NearbyMarketQueryFacade.kt
📝 Walkthrough

Walkthrough

공휴일 API 조회·Redis 캐시를 추가하고, 공휴일 휴무 설정에 따라 영업 여부를 판정하도록 변경했습니다. 시장 등록·수정 요청과 시장·전단 조회 응답에도 공휴일 휴무 정보가 반영됩니다.

Changes

공휴일 영업시간 지원

Layer / File(s) Summary
공휴일 조회 도메인 계약과 오케스트레이션
core/src/main/kotlin/kr/dongchimi/core/holiday/*
연도별 공휴일 캐시·외부 클라이언트 계약을 정의하고, 기준일과 전날의 공휴일을 조회하며 API 실패 시 빈 결과와 폴백 캐시를 사용합니다.
공휴일 API 연동과 Redis 저장
infrastructure/client/..., infrastructure/redis/..., .env.example
한국천문연구원 API 응답 파싱, 요청 타임아웃, Redis 날짜 직렬화, 정상·폴백 TTL 및 환경 설정을 추가합니다.
공휴일 휴무 설정과 영업 판정
core/src/main/kotlin/kr/dongchimi/core/market/BusinessHours.kt, api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/market/request/*
BusinessHours가 공휴일 휴무 플래그와 당일·전날 공휴일을 반영한 isOpenAt 판정을 사용하도록 변경되고, 등록·수정 요청이 해당 값을 전달합니다.
시장 조회와 전단 응답 반영
api/user-api/src/main/kotlin/kr/dongchimi/api/user/market/*, api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/flyer/*, api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/market/response/*
조회 퍼사드가 공휴일을 가져와 영업 여부 계산에 전달하며, 시장·주변 시장·전단 응답에 isHolidayClosed를 추가합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MarketQueryFacade
  participant HolidayService
  participant HolidayReader
  participant RedisHolidayCache
  participant DataGoKrHolidayClient
  participant MarketResponse
  MarketQueryFacade->>HolidayService: getHolidays(baseDate)
  HolidayService->>HolidayReader: getHolidays(baseDate)
  HolidayReader->>RedisHolidayCache: get(year)
  RedisHolidayCache-->>HolidayReader: cached holidays or miss
  HolidayReader->>DataGoKrHolidayClient: fetchHolidays(year)
  DataGoKrHolidayClient-->>HolidayReader: holiday dates
  HolidayReader->>RedisHolidayCache: put(year, holidays)
  HolidayReader-->>HolidayService: merged holiday dates
  MarketQueryFacade->>MarketResponse: isOpenAt(now, holidays)
Loading

Possibly related PRs

Suggested labels: ✨ FEAT

Suggested reviewers: imddoy

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 [Feat/#199] 형식을 따르며 공휴일 휴무 지원과 공휴일 반영 변경을 정확히 요약합니다.
Description check ✅ Passed 템플릿의 필수 섹션인 연관된 이슈, 작업 내용, 리뷰 요구사항을 모두 포함합니다.
Linked Issues check ✅ Passed 이슈 #199의 핵심 요구사항인 공휴일 휴무 필드, API 연동, Redis 캐시, isOpenAt 반영, 4개 호출부 적용이 모두 반영되었습니다.
Out of Scope Changes check ✅ Passed 요구사항 외의 눈에 띄는 기능 추가나 무관한 코드 변경은 보이지 않습니다.
마이그레이션-엔티티 쌍 확인 ✅ Passed 변경된 파일에 JPA 엔티티나 SQL 마이그레이션이 없어서 이 체크는 적용 대상이 아닙니다.
레이어 의존성 방향 확인 ✅ Passed PR 범위의 Service 클래스는 Repository/JpaRepository를 직접 주입하지 않고, HolidayService는 HolidayReader만 주입해 위임합니다.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/flyer/FlyerPreviewQueryFacade.kt`:
- Around line 33-35: Refactor getPeriodicPreview and getDailyPreview so
holidayService.getHolidays(today) executes outside the `@Transactional`(readOnly =
true) scope, avoiding database connection retention during external API calls.
Preserve the existing preview response data and transactional behavior for the
database-backed operations, using separate transaction boundaries where needed.

In
`@infrastructure/redis/src/main/kotlin/kr/dongchimi/infrastructure/redis/RedisHolidayCache.kt`:
- Around line 14-34: Update the runCatching blocks in RedisHolidayCache.get,
put, and putFallback to attach onFailure handlers that log the Redis exception
using the project’s existing logger implementation, while preserving the current
fail-open behavior and return values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 43ca2e15-bb2f-43b9-bab5-47c7b0bcc991

📥 Commits

Reviewing files that changed from the base of the PR and between c76bbde and 91882f8.

📒 Files selected for processing (25)
  • .env.example
  • api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/flyer/FlyerPreviewQueryFacade.kt
  • api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/flyer/response/FlyerDailyPreviewResponse.kt
  • api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/flyer/response/FlyerPreviewResponse.kt
  • api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/market/request/MarketRegisterRequest.kt
  • api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/market/request/MarketRequestSupport.kt
  • api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/market/request/MarketUpdateRequest.kt
  • api/owner-api/src/main/kotlin/kr/dongchimi/api/owner/market/response/OwnerMarketDetailResponse.kt
  • api/user-api/src/main/kotlin/kr/dongchimi/api/user/market/MarketDetailQueryFacade.kt
  • api/user-api/src/main/kotlin/kr/dongchimi/api/user/market/NearbyMarketQueryFacade.kt
  • api/user-api/src/main/kotlin/kr/dongchimi/api/user/market/response/MarketDetailResponse.kt
  • api/user-api/src/main/kotlin/kr/dongchimi/api/user/market/response/NearbyMarketResponse.kt
  • core/src/main/kotlin/kr/dongchimi/core/holiday/HolidayCache.kt
  • core/src/main/kotlin/kr/dongchimi/core/holiday/HolidayClient.kt
  • core/src/main/kotlin/kr/dongchimi/core/holiday/HolidayReader.kt
  • core/src/main/kotlin/kr/dongchimi/core/holiday/HolidayService.kt
  • core/src/main/kotlin/kr/dongchimi/core/market/BusinessHours.kt
  • infrastructure/client/src/main/kotlin/kr/dongchimi/client/holiday/DataGoKrHolidayClient.kt
  • infrastructure/client/src/main/kotlin/kr/dongchimi/client/holiday/HolidayApiProperties.kt
  • infrastructure/client/src/main/kotlin/kr/dongchimi/client/holiday/HolidayRestClientConfig.kt
  • infrastructure/client/src/main/resources/application-infrastructure-client.yml
  • infrastructure/redis/src/main/kotlin/kr/dongchimi/infrastructure/redis/HolidayCacheProperties.kt
  • infrastructure/redis/src/main/kotlin/kr/dongchimi/infrastructure/redis/HolidayRedisKeys.kt
  • infrastructure/redis/src/main/kotlin/kr/dongchimi/infrastructure/redis/RedisHolidayCache.kt
  • infrastructure/redis/src/main/resources/application-infrastructure-redis.yml

Comment on lines +14 to +34
// Redis 장애 시 miss로 간주 → HolidayReader가 외부 API 조회로 fallback한다(fail-open).
override fun get(year: Int): Set<LocalDate>? =
runCatching {
stringRedisTemplate.opsForValue().get(HolidayRedisKeys.holidays(year))?.toHolidays()
}.getOrNull()

override fun put(
year: Int,
holidays: Set<LocalDate>,
) {
runCatching {
stringRedisTemplate.opsForValue().set(HolidayRedisKeys.holidays(year), holidays.toValue(), properties.ttl)
}
}

override fun putFallback(year: Int) {
runCatching {
stringRedisTemplate.opsForValue().set(HolidayRedisKeys.holidays(year), EMPTY_VALUE, properties.fallbackTtl)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Redis 예외를 삼키지 말고 에러 로그를 남길 것을 권장합니다.

Redis 장애 시 runCatching을 통해 예외를 삼키고 외부 API로 폴백(fail-open)하는 전략은 서비스 안정성 측면에서 올바릅니다. 하지만 예외를 완전히 삼키면 인프라 구성 오류나 연결 장애를 조기에 인지하기 어렵습니다.
운영 환경에서의 가시성을 확보하기 위해 onFailure를 활용하여 에러 로그를 남기는 것을 권장합니다.

💡 에러 로깅 추가 예시

프로젝트에서 사용하는 로거 구현체(예: KotlinLogging 등)에 맞게 주석 부분을 수정해 적용해 주세요.

     // Redis 장애 시 miss로 간주 → HolidayReader가 외부 API 조회로 fallback한다(fail-open).
     override fun get(year: Int): Set<LocalDate>? =
         runCatching {
             stringRedisTemplate.opsForValue().get(HolidayRedisKeys.holidays(year))?.toHolidays()
-        }.getOrNull()
+        }.onFailure {
+            // logger.warn(it) { "Redis 공휴일 캐시 조회 실패: year=$year" }
+        }.getOrNull()
 
     override fun put(
         year: Int,
         holidays: Set<LocalDate>,
     ) {
         runCatching {
             stringRedisTemplate.opsForValue().set(HolidayRedisKeys.holidays(year), holidays.toValue(), properties.ttl)
+        }.onFailure {
+            // logger.warn(it) { "Redis 공휴일 캐시 저장 실패: year=$year" }
         }
     }
 
     override fun putFallback(year: Int) {
         runCatching {
             stringRedisTemplate.opsForValue().set(HolidayRedisKeys.holidays(year), EMPTY_VALUE, properties.fallbackTtl)
+        }.onFailure {
+            // logger.warn(it) { "Redis 공휴일 캐시 Fallback 저장 실패: year=$year" }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@infrastructure/redis/src/main/kotlin/kr/dongchimi/infrastructure/redis/RedisHolidayCache.kt`
around lines 14 - 34, Update the runCatching blocks in RedisHolidayCache.get,
put, and putFallback to attach onFailure handlers that log the Redis exception
using the project’s existing logger implementation, while preserving the current
fail-open behavior and return values.

@imddoy imddoy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

주기적으로 가져오도로고 해야 하나 했는데, 이렇게 Redis에 만료시간을 설정해서 미스가 발생하면 가져오게 할 수 있군요!! 수고하셨습니다!

@tnals0924
tnals0924 merged commit c0a8ad3 into main Jul 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

마트 공휴일 휴무 지원 및 영업중 여부 판별에 공휴일 반영

2 participants