Skip to content

Commit 2be7a2f

Browse files
committed
feat(ranking): 메트릭 집계 공통 로직 추상 클래스 추가
- 메트릭 Reader 공통 추상 클래스인 AbstractMetricsReader 구현 - 월간 및 주간 메트릭 Reader에서 상속하여 코드 중복 제거 - 랭킹 집계 로직을 통합하여 유지보수성 향상
1 parent 6aee58a commit 2be7a2f

3 files changed

Lines changed: 119 additions & 82 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.loopers.batch.job.ranking.reader;
2+
3+
import java.time.LocalDate;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
7+
import org.springframework.batch.item.ItemReader;
8+
9+
import com.loopers.batch.job.ranking.dto.RankingAggregation;
10+
import com.loopers.batch.job.ranking.support.RankingAggregator;
11+
import com.loopers.domain.metrics.ProductMetricsRepository;
12+
13+
import lombok.extern.slf4j.Slf4j;
14+
15+
/**
16+
* 메트릭 Reader 공통 추상 클래스
17+
* - 특정 기간의 데이터를 집계하고 랭킹을 생성하는 공통 로직을 포함
18+
*/
19+
@Slf4j
20+
public abstract class AbstractMetricsReader implements ItemReader<RankingAggregation> {
21+
22+
protected final ProductMetricsRepository productMetricsRepository;
23+
protected final RankingAggregator rankingAggregator;
24+
25+
private Iterator<RankingAggregation> iterator;
26+
27+
protected AbstractMetricsReader(ProductMetricsRepository productMetricsRepository, RankingAggregator rankingAggregator) {
28+
this.productMetricsRepository = productMetricsRepository;
29+
this.rankingAggregator = rankingAggregator;
30+
}
31+
32+
@Override
33+
public RankingAggregation read() throws Exception {
34+
if (iterator == null) {
35+
initializeIterator();
36+
}
37+
38+
return iterator.hasNext() ? iterator.next() : null;
39+
}
40+
41+
private void initializeIterator() {
42+
String logIdentifier = getLogIdentifier();
43+
log.info("{} 랭킹 집계 시작: parameter={}", logIdentifier, getParameterValue());
44+
45+
try {
46+
// 1. 기간 파싱 (추상 메서드 호출)
47+
LocalDate[] dateRange = parseDateRange();
48+
LocalDate startDate = dateRange[0];
49+
LocalDate endDate = dateRange[1];
50+
51+
log.info("집계 기간: {} ~ {}", startDate, endDate);
52+
53+
// 2. DB에서 집계 쿼리 실행
54+
List<Object[]> aggregationResults = productMetricsRepository.aggregateByDateRange(startDate, endDate);
55+
log.info("집계 대상 상품 수: {}", aggregationResults.size());
56+
57+
// 3. 랭킹 처리 (정렬 + TOP 100 + 순위 부여)
58+
List<RankingAggregation> rankings = rankingAggregator.processRankings(aggregationResults);
59+
log.info("생성된 랭킹 수: {}", rankings.size());
60+
61+
iterator = rankings.iterator();
62+
63+
} catch (Exception e) {
64+
log.error("{} 랭킹 집계 중 오류 발생: parameter={}", logIdentifier, getParameterValue(), e);
65+
throw new RuntimeException(logIdentifier + " 랭킹 집계 실패", e);
66+
}
67+
}
68+
69+
/**
70+
* 기간에 해당하는 LocalDate 범위를 반환합니다.
71+
*/
72+
protected abstract LocalDate[] parseDateRange();
73+
74+
/**
75+
* 로그 식별자를 반환합니다. (예: "월간", "주간")
76+
*/
77+
protected abstract String getLogIdentifier();
78+
79+
/**
80+
* 현재 사용 중인 파라미터 값을 반환합니다.
81+
*/
82+
protected abstract String getParameterValue();
83+
}
Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
package com.loopers.batch.job.ranking.reader;
22

33
import java.time.LocalDate;
4-
import java.util.Iterator;
5-
import java.util.List;
64

75
import org.springframework.batch.core.configuration.annotation.StepScope;
8-
import org.springframework.batch.item.ItemReader;
96
import org.springframework.beans.factory.annotation.Value;
107
import org.springframework.stereotype.Component;
118

12-
import com.loopers.batch.job.ranking.dto.RankingAggregation;
139
import com.loopers.batch.job.ranking.support.DateRangeParser;
1410
import com.loopers.batch.job.ranking.support.RankingAggregator;
1511
import com.loopers.domain.metrics.ProductMetricsRepository;
1612

17-
import lombok.RequiredArgsConstructor;
1813
import lombok.extern.slf4j.Slf4j;
1914

2015
/**
@@ -25,51 +20,33 @@
2520
@Slf4j
2621
@StepScope
2722
@Component
28-
@RequiredArgsConstructor
29-
public class MonthlyMetricsReader implements ItemReader<RankingAggregation> {
23+
public class MonthlyMetricsReader extends AbstractMetricsReader {
3024

31-
private final ProductMetricsRepository productMetricsRepository;
3225
private final DateRangeParser dateRangeParser;
33-
private final RankingAggregator rankingAggregator;
34-
35-
private Iterator<RankingAggregation> iterator;
3626

3727
@Value("#{jobParameters['yearMonth']}")
3828
private String yearMonth; // e.g., "2024-12"
3929

40-
@Override
41-
public RankingAggregation read() throws Exception {
42-
if (iterator == null) {
43-
initializeIterator();
44-
}
45-
46-
return iterator.hasNext() ? iterator.next() : null;
30+
public MonthlyMetricsReader(
31+
ProductMetricsRepository productMetricsRepository,
32+
RankingAggregator rankingAggregator,
33+
DateRangeParser dateRangeParser) {
34+
super(productMetricsRepository, rankingAggregator);
35+
this.dateRangeParser = dateRangeParser;
4736
}
4837

49-
private void initializeIterator() {
50-
log.info("월간 랭킹 집계 시작: yearMonth={}", yearMonth);
51-
52-
try {
53-
// 1. 월 → 날짜 범위 변환
54-
LocalDate[] dateRange = dateRangeParser.parseYearMonth(yearMonth);
55-
LocalDate startDate = dateRange[0];
56-
LocalDate endDate = dateRange[1];
57-
58-
log.info("집계 기간: {} ~ {}", startDate, endDate);
59-
60-
// 2. DB에서 집계 쿼리 실행
61-
List<Object[]> aggregationResults = productMetricsRepository.aggregateByDateRange(startDate, endDate);
62-
log.info("집계 대상 상품 수: {}", aggregationResults.size());
63-
64-
// 3. 랭킹 처리 (정렬 + TOP 100 + 순위 부여)
65-
List<RankingAggregation> rankings = rankingAggregator.processRankings(aggregationResults);
66-
log.info("생성된 랭킹 수: {}", rankings.size());
38+
@Override
39+
protected LocalDate[] parseDateRange() {
40+
return dateRangeParser.parseYearMonth(yearMonth);
41+
}
6742

68-
iterator = rankings.iterator();
43+
@Override
44+
protected String getLogIdentifier() {
45+
return "월간";
46+
}
6947

70-
} catch (Exception e) {
71-
log.error("월간 랭킹 집계 중 오류 발생: yearMonth={}", yearMonth, e);
72-
throw new RuntimeException("월간 랭킹 집계 실패", e);
73-
}
48+
@Override
49+
protected String getParameterValue() {
50+
return yearMonth;
7451
}
7552
}
Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
package com.loopers.batch.job.ranking.reader;
22

33
import java.time.LocalDate;
4-
import java.util.Iterator;
5-
import java.util.List;
64

75
import org.springframework.batch.core.configuration.annotation.StepScope;
8-
import org.springframework.batch.item.ItemReader;
96
import org.springframework.beans.factory.annotation.Value;
107
import org.springframework.stereotype.Component;
118

12-
import com.loopers.batch.job.ranking.dto.RankingAggregation;
139
import com.loopers.batch.job.ranking.support.DateRangeParser;
1410
import com.loopers.batch.job.ranking.support.RankingAggregator;
1511
import com.loopers.domain.metrics.ProductMetricsRepository;
1612

17-
import lombok.RequiredArgsConstructor;
1813
import lombok.extern.slf4j.Slf4j;
1914

2015
/**
@@ -25,51 +20,33 @@
2520
@Slf4j
2621
@StepScope
2722
@Component
28-
@RequiredArgsConstructor
29-
public class WeeklyMetricsReader implements ItemReader<RankingAggregation> {
23+
public class WeeklyMetricsReader extends AbstractMetricsReader {
3024

31-
private final ProductMetricsRepository productMetricsRepository;
3225
private final DateRangeParser dateRangeParser;
33-
private final RankingAggregator rankingAggregator;
34-
35-
private Iterator<RankingAggregation> iterator;
3626

3727
@Value("#{jobParameters['yearWeek']}")
3828
private String yearWeek; // e.g., "2024-W52"
3929

40-
@Override
41-
public RankingAggregation read() throws Exception {
42-
if (iterator == null) {
43-
initializeIterator();
44-
}
45-
46-
return iterator.hasNext() ? iterator.next() : null;
30+
public WeeklyMetricsReader(
31+
ProductMetricsRepository productMetricsRepository,
32+
RankingAggregator rankingAggregator,
33+
DateRangeParser dateRangeParser) {
34+
super(productMetricsRepository, rankingAggregator);
35+
this.dateRangeParser = dateRangeParser;
4736
}
4837

49-
private void initializeIterator() {
50-
log.info("주간 랭킹 집계 시작: yearWeek={}", yearWeek);
51-
52-
try {
53-
// 1. 주차 → 날짜 범위 변환
54-
LocalDate[] dateRange = dateRangeParser.parseYearWeek(yearWeek);
55-
LocalDate startDate = dateRange[0];
56-
LocalDate endDate = dateRange[1];
57-
58-
log.info("집계 기간: {} ~ {}", startDate, endDate);
59-
60-
// 2. DB에서 집계 쿼리 실행
61-
List<Object[]> aggregationResults = productMetricsRepository.aggregateByDateRange(startDate, endDate);
62-
log.info("집계 대상 상품 수: {}", aggregationResults.size());
63-
64-
// 3. 랭킹 처리 (정렬 + TOP 100 + 순위 부여)
65-
List<RankingAggregation> rankings = rankingAggregator.processRankings(aggregationResults);
66-
log.info("생성된 랭킹 수: {}", rankings.size());
38+
@Override
39+
protected LocalDate[] parseDateRange() {
40+
return dateRangeParser.parseYearWeek(yearWeek);
41+
}
6742

68-
iterator = rankings.iterator();
43+
@Override
44+
protected String getLogIdentifier() {
45+
return "주간";
46+
}
6947

70-
} catch (Exception e) {
71-
log.error("주간 랭킹 집계 중 오류 발생: yearWeek={}", yearWeek, e);
72-
throw new RuntimeException("주간 랭킹 집계 실패", e);
73-
}
48+
@Override
49+
protected String getParameterValue() {
50+
return yearWeek;
7451
}
7552
}

0 commit comments

Comments
 (0)