Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -169,24 +169,38 @@ private boolean bulkInsert(List<NewsEsDocument> docs, String section) throws IOE
}

/**
* ํ‚ค์›Œ๋“œ๋กœ ๋‰ด์Šค ๊ธฐ์‚ฌ ๊ฒ€์ƒ‰
* ์–ด์ œ ๋‚ ์งœ ๊ธฐ์ค€์œผ๋กœ ๋‰ด์Šค ๊ธฐ์‚ฌ ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
*
* <p>{@code combinedTokens} ํ•„๋“œ์— ๋Œ€ํ•ด match ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ ,
* ์ ์ˆ˜(score) ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ๋œ ์ƒ์œ„ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.</p>
* {@code publishedAt} ํ•„๋“œ๊ฐ€ ์–ด์ œ(00:00~24:00)์ธ ๋ฐ์ดํ„ฐ๋งŒ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
* ๊ฒฐ๊ณผ๋Š” ์ ์ˆ˜(score) ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ๋˜์–ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.</p>
*
* @param keyword ๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ
* @param size ์ตœ๋Œ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ˆ˜
* @return ๊ฒ€์ƒ‰๋œ ๋‰ด์Šค ๋„ํ๋จผํŠธ ๋ฆฌ์ŠคํŠธ
* @param size ์ตœ๋Œ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ˆ˜
* @return ์–ด์ œ ๋‚ ์งœ ์ค‘ ํ‚ค์›Œ๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ๋‰ด์Šค ๋„ํ๋จผํŠธ ๋ฆฌ์ŠคํŠธ
* @throws IOException Elasticsearch ๊ฒ€์ƒ‰ ์‹คํŒจ ์‹œ ๋ฐœ์ƒ
*/
public List<NewsEsDocument> searchByKeyword(String keyword, int size) throws IOException {
LocalDate today = LocalDate.now();
LocalDate yesterday = today.minusDays(1);

String from = yesterday.atStartOfDay().toString(); // ์–ด์ œ 00:00:00
String to = today.atStartOfDay().toString(); // ์˜ค๋Š˜ 00:00:00

SearchResponse<NewsEsDocument> response = elasticsearchClient.search(s -> s
.index("news-index-nori")
.size(size)
.query(q -> q
.match(m -> m
.field("combinedTokens")
.query(keyword)
.bool(b -> b
.must(m -> m.match(mm -> mm
.field("combinedTokens")
.query(keyword)
))
.filter(f -> f.range(r -> r
.field("publishedAt")
.gte(JsonData.of(from))
.lt(JsonData.of(to))
))
)
)
.sort(sort -> sort
Expand Down Expand Up @@ -235,4 +249,46 @@ public List<StringTermsBucket> getTopKeywordsForDateRange(LocalDate gte, LocalDa
.buckets()
.array();
}

// ======================= Deprecated =========================

/**
* Hot Fix
*
* Deprecated At 2025-07-24
* By ๊น€์›์ค‘
*
*/
// /**
// * ํ‚ค์›Œ๋“œ๋กœ ๋‰ด์Šค ๊ธฐ์‚ฌ ๊ฒ€์ƒ‰
// *
// * <p>{@code combinedTokens} ํ•„๋“œ์— ๋Œ€ํ•ด match ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ ,
// * ์ ์ˆ˜(score) ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ๋œ ์ƒ์œ„ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.</p>
// *
// * @param keyword ๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ
// * @param size ์ตœ๋Œ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ˆ˜
// * @return ๊ฒ€์ƒ‰๋œ ๋‰ด์Šค ๋„ํ๋จผํŠธ ๋ฆฌ์ŠคํŠธ
// * @throws IOException Elasticsearch ๊ฒ€์ƒ‰ ์‹คํŒจ ์‹œ ๋ฐœ์ƒ
// */
// public List<NewsEsDocument> searchByKeyword(String keyword, int size) throws IOException {
// SearchResponse<NewsEsDocument> response = elasticsearchClient.search(s -> s
// .index("news-index-nori")
// .size(size)
// .query(q -> q
// .match(m -> m
// .field("combinedTokens")
// .query(keyword)
// )
// )
// .sort(sort -> sort
// .score(sc -> sc.order(SortOrder.Desc))
// ),
// NewsEsDocument.class
// );
//
// return response.hits().hits().stream()
// .map(Hit::source)
// .filter(Objects::nonNull)
// .toList();
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ public IntermediateBatchRedisService(@Qualifier("redisSession1Template") RedisTe

private static final String BATCH_KEY_PREFIX = "IntermediateBatch:";

public boolean isBatchAlreadyDone(String key) {
return redisSession1Template.hasKey(key);
}

public void markBatchDone(String key) {
redisSession1Template.opsForValue().set(key, "done", Duration.ofHours(12)); // 12
}

/**
* ์ค‘๊ฐ„ ๋ฐฐ์น˜ ์‹คํ–‰ ํšŸ์ˆ˜๋ฅผ ์กฐํšŒ ๋ฉ”์„œ๋“œ
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,63 +27,132 @@ public class NewsMonitoringService {
private final JobLauncher jobLauncher;
private final Job newsDataSaveJob_Monitoring;

/**
* ๋งค ์ •๊ฐ๋งˆ๋‹ค ์‹คํ–‰ ๋˜๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ์Šค์ผ€์ค„๋Ÿฌ
*
* ๊ฐ ์„น์…˜ ๋ณ„ ํ˜„์žฌ ๋ช‡๊ฐœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ์˜€๋Š”์ง€ ๋ชจ๋‹ˆํ„ฐ๋ง
* 9000๊ฐœ๊ฐ€ ๋„˜๋Š” ์„น์…˜ ๋ฐœ๊ฒฌ ์‹œ, ์ฆ‰์‹œ DB์— Batch ์‹ค์‹œ
*
*/
@Scheduled(cron = "0 0 * * * *") // ๋งค ์‹œ๊ฐ„ ์ •๊ฐ
//@Scheduled(cron = "0 */5 * * * *") // 5๋ถ„
// @Scheduled(cron = "0 */3 * * * *") // 3๋ถ„
//@Scheduled(cron = "0 */3 * * * *") // 3๋ถ„
//@Scheduled(cron = "0 */2 * * * *") // 2๋ถ„
//@Scheduled(cron = "0 * * * * *") // 1๋ถ„
public void monitoring(){
public void monitoring() {

/* ๋‚ ์งœ & ์‹œ๊ฐ„ ํ™•์ธ */
LocalDate day = LocalDate.now(); // ์˜ค๋Š˜์˜ ๋‚ ์งœ
LocalDateTime time = LocalDateTime.now(); // ํ˜„์žฌ ๋‚ ์งœ์™€ ์‹œ๊ฐ„(๋ถ„ ๋‹จ์œ„ ํฌํ•จ)
log.info("// ======================= ์˜ค๋Š˜์˜ ๋‚ ์งœ : {} =========================", day); // ์˜ค๋Š˜์˜ ๋‚ ์งœ ๋กœ๊ทธ ํ™•์ธ
log.info("// ======================= ์˜ค๋Š˜์˜ ๋‚ ์งœ : {} =========================", day);
log.info("ํ˜„์žฌ ์‹œ๊ฐ„ : {}", time);

// ์ž์ •(00์‹œ)์ด๋ฉด ์Šคํ‚ต
if (time.getHour() == 0) {
log.info("[SKIP] ์ž์ • 00์‹œ์—๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ์ž‘์—…์„ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
return;
}

/* ๋‚ ์งœ ํฌ๋งคํŒ… ์ž‘์—… */
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); //์›ํ•˜๋Š” ํฌ๋งท ์ง€์ •
String dateTo = day.format(formatter); //๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
String dateFrom = day.format(formatter); //๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String dateStr = day.format(formatter);

/* ์„น์…˜ ๋ณ„ ๋ˆ„์  ๋ฐ์ดํ„ฐ ์ˆ˜ ๋ชจ๋‹ˆํ„ฐ๋ง ๋กœ์ง */
for (String section : sections) {

// ํ˜„์žฌ ์„น์…˜์˜ ํ˜„ ์‹œ๊ฐ ๋ˆ„์  Total_Item ์ˆ˜ ํ™•์ธ
int total_items = newsMonitoringManager.getTotalItems(section, dateFrom, dateTo);
int total_items = newsMonitoringManager.getTotalItems(section, dateStr, dateStr);
String batchDoneKey = "batch_done:" + dateStr + ":" + section; // ํ•˜๋ฃจ 1ํšŒ ์ฒดํฌ์šฉ
String batchCountKey = "batch_count:" + dateStr + ":" + section; // ํ•˜๋ฃจ ํšŸ์ˆ˜ ์ œํ•œ์šฉ

/* ๋‰ด์Šค ๋ฐ์ดํ„ฐ 9000๊ฐœ ์ด์ƒ ์‹œ ์ค‘๊ฐ„ ๋ฐฐ์น˜ + Redis ๊ธฐ๋ก */
if(total_items >= 9000 && total_items <= 18000){
/* =================== 9000 ์ด์ƒ 18000 ์ดํ•˜์ผ ๊ฒฝ์šฐ =================== */
if (total_items >= 9000 && total_items <= 18000) {

// ํ•ด๋‹น ์„น์…˜์ด ์ด์ „์—๋„ ์‚ฌ์ „ Batch ์ž‘์—…์ด ์‹คํ–‰ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ (Redis์—์„œ ํšŸ์ˆ˜ ์กฐํšŒ)
int n = intermediateBatchRedisService.getBatchCount(section);
log.info("{} ์„น์…˜ ๊ธฐ์กด ์ค‘๊ฐ„ ๋ฐฐ์น˜ ํ˜„ํ™ฉ : ์ด {}ํšŒ ์ค‘๊ฐ„ ๋ฐฐ์น˜ ",section, n);
// ํ•˜๋ฃจ ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰: ์ด๋ฏธ ํ–ˆ๋Š”์ง€ Redis์— ํ™•์ธ
if (intermediateBatchRedisService.isBatchAlreadyDone(batchDoneKey)) {
log.info("[SKIP] {} ์„น์…˜์€ ์ด๋ฏธ {}์— ์ค‘๊ฐ„ ๋ฐฐ์น˜๊ฐ€ ์ˆ˜ํ–‰๋จ", section, dateStr);
continue;
}

// ํ˜„์žฌ total_item์ด ๊ธฐ์ค€(9000๊ฐœ)์„ ๋„˜์œผ๋ฉด Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
runNewsBatch(section);
int prevCount = intermediateBatchRedisService.getBatchCount(batchCountKey);
log.info("{} ์„น์…˜ ๊ธฐ์กด ์ค‘๊ฐ„ ๋ฐฐ์น˜ ํ˜„ํ™ฉ : ์ด {}ํšŒ ์ค‘๊ฐ„ ๋ฐฐ์น˜", section, prevCount);

// ์ค‘๊ฐ„ ๋ฐฐ์น˜๊ฐ€ ์ง„ํ–‰๋œ ์„น์…˜ ๋ฐฐ์น˜ ํ˜„ํ™ฉ ๊ธฐ๋ก (Redis์— ํšŸ์ˆ˜ ์ฆ๊ฐ€)
intermediateBatchRedisService.incrementBatchCount(section);
runNewsBatch(section); // ๋ฐฐ์น˜ ์‹คํ–‰

} /* 2๋ฒˆ์งธ ์ค‘๊ฐ„ ๋ฐฐ์น˜ ๋ถ€ํ„ฐ ์ฃผ์˜ ๋ฉ”์‹œ์ง€ ํ˜ธ์ถœ */
else if(total_items >= 18000){
intermediateBatchRedisService.incrementBatchCount(batchCountKey); // ํšŸ์ˆ˜ ์ฆ๊ฐ€
intermediateBatchRedisService.markBatchDone(batchDoneKey); // ํ•˜๋ฃจ 1ํšŒ ์™„๋ฃŒ ๊ธฐ๋ก

// Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
runNewsBatch(section);
}

// ํ˜„์žฌ total_item์ด ๊ธฐ์ค€(18000๊ฐœ)์„ ๋„˜์œผ๋ฉด Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
log.info("[#์ฃผ์˜#] {}์„น์…˜์˜ total_items์ˆ˜ : {}",section,total_items); // ์ถ”ํ›„, ํ•˜๋ฃจ์— 3๋ฒˆ ์ด์ƒ์˜ ์ค‘๊ฐ„ ํ˜ธ์ถœ์ด ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๋ฐฉ์–ด์ฝ”๋“œ๋„ ์ž‘์„ฑํ•  ๊ฒƒ
/* =================== 18000 ์ดˆ๊ณผ์ผ ๊ฒฝ์šฐ =================== */
else if (total_items > 18000) {

int count = intermediateBatchRedisService.getBatchCount(batchCountKey);

if (count >= 3) {
log.warn("[SKIP] {} ์„น์…˜์€ ์ด๋ฏธ {}์— 3ํšŒ ์ด์ƒ ๋ฐฐ์น˜๋จ", section, dateStr);
continue;
}

runNewsBatch(section); // ๋ฐฐ์น˜ ์‹คํ–‰

intermediateBatchRedisService.incrementBatchCount(batchCountKey); // ํšŸ์ˆ˜ ์ฆ๊ฐ€
log.warn("[#์ฃผ์˜#] {} ์„น์…˜์˜ total_items ์ˆ˜ : {} (๋ˆ„์  {}ํšŒ ๋ฐฐ์น˜๋จ)", section, total_items, count + 1);
}
}
}

// ======================= deprecated =========================

/**
* ๋งค ์ •๊ฐ๋งˆ๋‹ค ์‹คํ–‰ ๋˜๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ์Šค์ผ€์ค„๋Ÿฌ
*
* ๊ฐ ์„น์…˜ ๋ณ„ ํ˜„์žฌ ๋ช‡๊ฐœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ์˜€๋Š”์ง€ ๋ชจ๋‹ˆํ„ฐ๋ง
* 9000๊ฐœ๊ฐ€ ๋„˜๋Š” ์„น์…˜ ๋ฐœ๊ฒฌ ์‹œ, ์ฆ‰์‹œ DB์— Batch ์‹ค์‹œ
*
*/
// @Scheduled(cron = "0 0 * * * *") // ๋งค ์‹œ๊ฐ„ ์ •๊ฐ
// //@Scheduled(cron = "0 */5 * * * *") // 5๋ถ„
// // @Scheduled(cron = "0 */3 * * * *") // 3๋ถ„
// //@Scheduled(cron = "0 */2 * * * *") // 2๋ถ„
// //@Scheduled(cron = "0 * * * * *") // 1๋ถ„
// public void monitoring(){
//
// /* ๋‚ ์งœ & ์‹œ๊ฐ„ ํ™•์ธ */
// LocalDate day = LocalDate.now(); // ์˜ค๋Š˜์˜ ๋‚ ์งœ
// LocalDateTime time = LocalDateTime.now(); // ํ˜„์žฌ ๋‚ ์งœ์™€ ์‹œ๊ฐ„(๋ถ„ ๋‹จ์œ„ ํฌํ•จ)
// log.info("// ======================= ์˜ค๋Š˜์˜ ๋‚ ์งœ : {} =========================", day); // ์˜ค๋Š˜์˜ ๋‚ ์งœ ๋กœ๊ทธ ํ™•์ธ
// log.info("ํ˜„์žฌ ์‹œ๊ฐ„ : {}", time);
//
// /* ๋‚ ์งœ ํฌ๋งคํŒ… ์ž‘์—… */
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); //์›ํ•˜๋Š” ํฌ๋งท ์ง€์ •
// String dateTo = day.format(formatter); //๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
// String dateFrom = day.format(formatter); //๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
//
// /* ์„น์…˜ ๋ณ„ ๋ˆ„์  ๋ฐ์ดํ„ฐ ์ˆ˜ ๋ชจ๋‹ˆํ„ฐ๋ง ๋กœ์ง */
// for (String section : sections) {
//
// // ํ˜„์žฌ ์„น์…˜์˜ ํ˜„ ์‹œ๊ฐ ๋ˆ„์  Total_Item ์ˆ˜ ํ™•์ธ
// int total_items = newsMonitoringManager.getTotalItems(section, dateFrom, dateTo);
//
// /* ๋‰ด์Šค ๋ฐ์ดํ„ฐ 9000๊ฐœ ์ด์ƒ ์‹œ ์ค‘๊ฐ„ ๋ฐฐ์น˜ + Redis ๊ธฐ๋ก */
// if(total_items >= 9000 && total_items <= 18000){
//
// // ํ•ด๋‹น ์„น์…˜์ด ์ด์ „์—๋„ ์‚ฌ์ „ Batch ์ž‘์—…์ด ์‹คํ–‰ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ (Redis์—์„œ ํšŸ์ˆ˜ ์กฐํšŒ)
// int n = intermediateBatchRedisService.getBatchCount(section);
// log.info("{} ์„น์…˜ ๊ธฐ์กด ์ค‘๊ฐ„ ๋ฐฐ์น˜ ํ˜„ํ™ฉ : ์ด {}ํšŒ ์ค‘๊ฐ„ ๋ฐฐ์น˜ ",section, n);
//
// // ํ˜„์žฌ total_item์ด ๊ธฐ์ค€(9000๊ฐœ)์„ ๋„˜์œผ๋ฉด Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
// runNewsBatch(section);
//
// // ์ค‘๊ฐ„ ๋ฐฐ์น˜๊ฐ€ ์ง„ํ–‰๋œ ์„น์…˜ ๋ฐฐ์น˜ ํ˜„ํ™ฉ ๊ธฐ๋ก (Redis์— ํšŸ์ˆ˜ ์ฆ๊ฐ€)
// intermediateBatchRedisService.incrementBatchCount(section);
//
// } /* 2๋ฒˆ์งธ ์ค‘๊ฐ„ ๋ฐฐ์น˜ ๋ถ€ํ„ฐ ์ฃผ์˜ ๋ฉ”์‹œ์ง€ ํ˜ธ์ถœ */
// else if(total_items >= 18000){
//
// // Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
// runNewsBatch(section);
//
// // ํ˜„์žฌ total_item์ด ๊ธฐ์ค€(18000๊ฐœ)์„ ๋„˜์œผ๋ฉด Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
// log.info("[#์ฃผ์˜#] {}์„น์…˜์˜ total_items์ˆ˜ : {}",section,total_items); // ์ถ”ํ›„, ํ•˜๋ฃจ์— 3๋ฒˆ ์ด์ƒ์˜ ์ค‘๊ฐ„ ํ˜ธ์ถœ์ด ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๋ฐฉ์–ด์ฝ”๋“œ๋„ ์ž‘์„ฑํ•  ๊ฒƒ
//
// }
// }
// }

// ======================= Spring Batch Job Launcher (Batch Starter Method) =========================

/**
Expand Down