forked from Loopers-dev-lab/loop-pack-be-l2-vol2-java
-
Notifications
You must be signed in to change notification settings - Fork 0
배치 서비스 구현 #47
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
Merged
Merged
배치 서비스 구현 #47
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
회원 가입시 User 저장이 수행된다. ( spy 검증 ) 이미 가입된 ID 로 회원가입 시도 시, 실패한다.
회원 가입이 성공할 경우, 생성된 유저 정보를 응답으로 반환한다.
회원 가입 시에 성별이 없을 경우, `400 Bad Request` 응답을 반환한다.
해당 ID 의 회원이 존재할 경우, 회원 정보가 반환된다. 해당 ID 의 회원이 존재하지 않을 경우, null 이 반환된다. 내 정보 조회에 성공할 경우, 해당하는 유저 정보를 응답으로 반환한다. 존재하지 않는 ID 로 조회할 경우, `404 Not Found` 응답을 반환한다.
해당 ID 의 회원이 존재할 경우, 보유 포인트가 반환된다. 해당 ID 의 회원이 존재하지 않을 경우, null 이 반환된다. 포인트 조회에 성공할 경우, 보유 포인트를 응답으로 반환한다. `X-USER-ID` 헤더가 없을 경우, `400 Bad Request` 응답을 반환한다.
0 이하의 정수로 포인트를 충전 시 실패한다. 존재하지 않는 유저 ID 로 충전을 시도한 경우, 실패한다. 존재하는 유저가 1000원을 충전할 경우, 충전된 보유 총량을 응답으로 반환한다. 존재하지 않는 유저로 요청할 경우, `404 Not Found` 응답을 반환한다.
[volume-3] 도메인 모델링 및 구현
This reverts commit 0074ea9.
…9-revert-86-3round Revert "Revert "[volume-3] 도메인 모델링 및 구현""
…-3round Revert "[volume-3] 도메인 모델링 및 구현"
Round3: Product, Brand, Like, Order
…-round3 Revert "Round3: Product, Brand, Like, Order"
- 상품별 일간 메트릭을 저장하는 ProductMetricsEntity 추가 - 복합키를 사용하여 메트릭을 구분하는 ProductMetricsId 추가 - 상품 ID와 날짜로 메트릭을 조회하는 메서드 구현 - 메트릭 저장 및 조회 기능을 제공하는 ProductMetricsRepository 추가 - 메트릭 관련 비즈니스 로직을 처리하는 ProductMetricsService 추가 - 메트릭 엔티티에 대한 단위 테스트 추가
- 상품별 일간 메트릭을 저장하는 ProductMetricsEntity 추가 - 복합키를 사용하여 메트릭을 구분하는 ProductMetricsId 추가 - 상품 ID와 날짜로 메트릭을 조회하는 메서드 구현 - 메트릭 저장 및 조회 기능을 제공하는 ProductMetricsRepository 추가 - 메트릭 관련 비즈니스 로직을 처리하는 ProductMetricsService 추가 - 메트릭 엔티티에 대한 단위 테스트 추가
- 주간 및 월간 랭킹 집계 Job 설정 - 메트릭 데이터 처리기 및 점수 계산기 구현 - 랭킹 집계 및 저장 로직 추가 - 날짜 범위 파싱 유틸리티 구현 - 관련 테스트 코드 추가
- 월간 및 주간 랭킹 엔티티(MonthlyRankEntity, WeeklyRankEntity) 생성 - 각 엔티티에 대한 JPA Repository 및 서비스 구현 - 랭킹 데이터 저장 및 조회 기능 추가 - 관련 테스트 코드 작성
- rankPosition 필드 타입을 int에서 long으로 변경 - createdAt 필드 타입을 ZonedDateTime에서 LocalDateTime으로 변경 - 불필요한 메서드 주석 제거
- 일간, 주간, 월간 랭킹 조회 API 명세 및 구현 - 랭킹 데이터 처리 로직 추가 - 기존 코드 리팩토링 및 불필요한 메서드 제거
- rank_position을 base_rank_position으로 변경 - year_month를 base_year_month로 변경 - 로그 메시지 개선 및 예외 처리 강화
- 코드 가독성을 높이기 위해 불필요한 임포트 문을 제거 - 주석 및 공백 정리로 코드 일관성 향상
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
📌 Summary
Spring Batch를 활용한 주간/월간 랭킹 집계 시스템을 구현하고, API 구조를 개선했습니다.
주요 구현 내용:
RankingV1Controller분리 및 Java 8 Date API 활용구현된 배치 프로세스:
product_metrics7일치 →mv_product_rank_weeklyTOP 100product_metrics30일치 →mv_product_rank_monthlyTOP 100viewCount×1 + likeCount×3 + salesCount×5 + orderCount×2💬 Review Points
1. Spring Batch Job 설계 (Chunk-Oriented Processing)
배경 및 문제 상황:
대량의 메트릭 데이터를 집계하여 주간/월간 랭킹을 생성해야 하는데, 메모리 효율성과 트랜잭션 안정성을 고려했습니다.
해결 방안:
Chunk-Oriented Processing 패턴을 적용하여 Reader → Processor → Writer 구조로 설계했습니다.
구현 세부사항:
1) Job Configuration - 파라미터 기반 동작:
2) ItemReader - DB 집계 쿼리 + TOP 100 필터링:
3) ItemWriter - 멱등성 보장:
고민한 점:
2. 핵심 비즈니스 로직 모듈화 및 단위 테스트
배경 및 문제 상황: 배치 Job의 복잡한 로직(점수 계산, 날짜 파싱, 집계 처리)을 통합 테스트로만 검증하면 실패 시 원인 파악이 어렵고, 테스트 실행 시간이 오래 걸릴것으로 예상했습니다.
해결 방안: 핵심 비즈니스 로직을 별도 클래스로 분리하고 단위 테스트로 검증한 후, Job에 조립하는 방식을 채택했습니다.
구현 세부사항:
1) ScoreCalculator - 점수 계산 로직:
2) DateRangeParser - 날짜 범위 파싱:
3) RankingAggregator - 집계 및 순위 부여:
고민한 점:
단위 테스트 우선 접근: 복잡한 배치 로직을 작은 단위로 분해하여 각각 단위 테스트로 검증한 후 조립하는 방식으로, 문제 발생 시 빠른 원인 파악이 가능하도록 했습니다.
점수 계산 공식: Redis ZSET의 가중치와 동일하게 유지하여 일간/주간/월간 랭킹 간 일관성을 보장했습니다.
ISO Week 표준: WeekFields.ISO를 사용하여 국제 표준 주차 계산을 적용했습니다.
3. Materialized View 설계 (@EmbeddedId 복합키)
배경 및 문제 상황: 주간/월간 랭킹 데이터를 효율적으로 저장하고 조회하기 위한 테이블 구조가 필요했습니다. 특히 (product_id, year_week) 또는 (product_id, year_month) 복합키로 유니크 제약을 보장해야 했습니다.
해결 방안: Hibernate 6.x 권장 방식인 @EmbeddedId를 사용하여 복합키를 객체로 캡슐화했습니다.
구현 세부사항:
1) 복합키 클래스 (@embeddable):
고민한 점:
@EmbeddedId vs @IdClass: @EmbeddedId는 복합키를 객체로 캡슐화하여 타입 안전성을 제공하고, Hibernate 6.x에서 권장하는 방식을 이용해 구현하도록 했습니다.
인덱스 설계: (year_week, rank_position) 복합 인덱스로 주차별 순위 조회를 최적화했습니다.
순위 검증: Entity 생성 시 1~100 범위 검증으로 데이터 무결성을 보장했습니다.
4. Controller 리팩토링 - 관심사 분리
배경 및 문제 상황: 기존 ProductV1Controller에 랭킹 관련 메서드가 포함되어 있어 관심사가 혼재되어 있었습니다. 또한 Java 8 Date API를 활용한 파라미터 처리가 필요했습니다.
해결 방안: 랭킹 전용 RankingV1Controller를 분리하고, Java 8 Date API(WeekFields, YearMonth)를 활용하여 파라미터를 자동 처리하도록 구현했습니다.
구현 세부사항:
1) RankingV1Controller - 전용 컨트롤러:
2) ProductV1Controller - 랭킹 메서드 제거:
고민한 점:
5. E2E 테스트
구현 세부사항:
테스트 커버리지:
=> 총 11개 테스트 케이스 모두 통과
✅ Checklist
🧱 Spring Batch (3/3)
apps/commerce-batch/src/main/java/com/loopers/batch/job/ranking/WeeklyRankingJobConfig.javaapps/commerce-batch/src/main/java/com/loopers/batch/job/ranking/MonthlyRankingJobConfig.javayearWeek,yearMonthWeeklyMetricsReader.java,MonthlyMetricsReader.javaRankingProcessor.java(공통)WeeklyRankWriter.java,MonthlyRankWriter.javamodules/jpa/src/main/java/com/loopers/domain/ranking/WeeklyRankEntity.javamodules/jpa/src/main/java/com/loopers/domain/ranking/MonthlyRankEntity.java@EmbeddedId) + TOP 100 + 순위 정보🧩 Ranking API (1/1)
GET /api/v1/rankings(Redis ZSET)GET /api/v1/rankings/period(MV 테이블)apps/commerce-api/src/main/java/com/loopers/interfaces/api/ranking/RankingV1Controller.java🧪 핵심 로직 단위 테스트 (4/4)
ScoreCalculator) 검증apps/commerce-batch/src/test/java/com/loopers/batch/job/ranking/support/ScoreCalculatorUnitTest.javaDateRangeParser) 검증apps/commerce-batch/src/test/java/com/loopers/batch/job/ranking/support/DateRangeParserUnitTest.javaRankingAggregator) 검증apps/commerce-batch/src/test/java/com/loopers/batch/job/ranking/support/RankingAggregatorUnitTest.javaRankingAggregation) 테스트apps/commerce-batch/src/test/java/com/loopers/batch/job/ranking/dto/RankingAggregationUnitTest.java🔧 코드 품질
🧪 E2E 테스트
apps/commerce-api/src/test/java/com/loopers/interfaces/api/ranking/RankingApiE2ETest.javaapps/commerce-batch/src/test/java/com/loopers/batch/job/ranking/RankingBatchE2ETest.javaapps/commerce-batch/src/test/java/com/loopers/batch/job/ranking/ManualBatchJobTest.java📎 References
주요 구현 파일
배치 시스템:
apps/commerce-batch/src/main/java/com/loopers/batch/job/ranking/WeeklyRankingJobConfig.java,MonthlyRankingJobConfig.javaWeeklyMetricsReader.java,MonthlyMetricsReader.javaWeeklyRankWriter.java,MonthlyRankWriter.javaScoreCalculator.java,DateRangeParser.java,RankingAggregator.javaAPI 시스템:
apps/commerce-api/src/main/java/com/loopers/interfaces/api/ranking/RankingV1Controller.javaapps/commerce-api/src/main/java/com/loopers/interfaces/api/ranking/RankingV1ApiSpec.javaapps/commerce-api/src/main/java/com/loopers/interfaces/api/product/ProductV1Controller.java(리팩토링)도메인 모델:
modules/jpa/src/main/java/com/loopers/domain/ranking/WeeklyRankEntity.java,WeeklyRankId.javaMonthlyRankEntity.java,MonthlyRankId.java테스트:
apps/commerce-batch/src/test/java/com/loopers/batch/job/ranking/support/apps/commerce-api/src/test/java/com/loopers/interfaces/api/ranking/RankingApiE2ETest.java기술 참고 자료
지금 이 pr 을 깔끔하게 code - block 을 정리해서 재 작성해줘