Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.few.generator.controller

import com.few.generator.usecase.GlobalGenSchedulingUseCase
import com.few.generator.usecase.LocalGenSchedulingUseCase
import com.few.generator.usecase.NasdaqDailyStockCardSchedulingUseCase
import com.few.generator.usecase.RefreshInstagramTokenUseCase
import com.few.generator.usecase.SendCacheMetricsSchedulingUseCase
import com.few.generator.usecase.SendNewsletterSchedulingUseCase
Expand All @@ -16,6 +17,7 @@ class SchedulingController(
private val sendCacheMetricsSchedulingUseCase: SendCacheMetricsSchedulingUseCase,
private val sendNewsletterSchedulingUseCase: SendNewsletterSchedulingUseCase,
private val refreshInstagramTokenUseCase: RefreshInstagramTokenUseCase,
private val nasdaqDailyStockCardSchedulingUseCase: NasdaqDailyStockCardSchedulingUseCase,
) {
private val log = KotlinLogging.logger {}

Expand Down Expand Up @@ -47,4 +49,9 @@ class SchedulingController(
log.error(e) { "Instagram 토큰 갱신 스케줄 실행 중 오류 발생: ${e.message}" }
}
}

@Scheduled(cron = "\${scheduling.cron.nasdaq-daily-stock}", zone = "Asia/Seoul")
fun nasdaqDailyStockScheduling() {
nasdaqDailyStockCardSchedulingUseCase.execute()
}
Comment on lines +53 to +56

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

스케줄러 예외 처리 누락

nasdaqDailyStockScheduling()에 예외 처리가 없습니다. NasdaqDailyStockCardSchedulingUseCase.execute()는 이미지 생성 실패, S3 업로드 실패, Instagram 컨테이너 생성 실패 시 RuntimeException을 발생시킵니다.

refreshInstagramToken()(Line 44-51)과 달리 try-catch가 없어, 예외 발생 시 로깅이 누락되고 스케줄러 동작에 영향을 줄 수 있습니다.

🛡️ 예외 처리 추가 제안
     `@Scheduled`(cron = "\${scheduling.cron.nasdaq-daily-stock}", zone = "Asia/Seoul")
     fun nasdaqDailyStockScheduling() {
-        nasdaqDailyStockCardSchedulingUseCase.execute()
+        try {
+            nasdaqDailyStockCardSchedulingUseCase.execute()
+        } catch (e: Exception) {
+            log.error(e) { "나스닥 주식 카드 스케줄 실행 중 오류 발생: ${e.message}" }
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@domain/generator/src/main/kotlin/com/few/generator/controller/SchedulingController.kt`
around lines 53 - 56, Wrap the body of nasdaqDailyStockScheduling() in a
try-catch similar to refreshInstagramToken(): call
nasdaqDailyStockCardSchedulingUseCase.execute() inside try, catch
RuntimeException (or Exception) and log the error with context (include
exception message/stack) so failures (image generation, S3 upload, Instagram
container creation) are recorded and do not bubble up to break the scheduler;
optionally rethrow only if needed but prefer swallowing after logging to keep
the scheduler running.

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,42 @@ class InstagramUploader(
}
}

// 단일 이미지 컨테이너 생성 (캐러셀 아님)
fun createSingleMediaContainer(
imageUrl: String,
caption: String,
): String? {
val url =
"https://graph.instagram.com/$accountId/media"
.toHttpUrlOrNull()
?.newBuilder()
?.addQueryParameter("access_token", instagramTokenService.getLatestAccessToken())
?.addQueryParameter("image_url", imageUrl)
?.addQueryParameter("caption", caption)
?.build()

val request =
Request
.Builder()
.url(url!!)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

toHttpUrlOrNull() 메서드는 null을 반환할 수 있으므로, url!!을 사용하여 강제로 non-null로 처리하는 것은 KotlinNullPointerException을 발생시킬 수 있습니다. null 가능성을 명시적으로 처리하는 것이 좋습니다.

Suggested change
.url(url!!)
.url(url ?: throw RuntimeException("Invalid Instagram media URL constructed"))

.post(RequestBody.create(null, ""))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

RequestBody.create(null, "")는 OkHttp 4.x에서 더 이상 사용되지 않는(deprecated) 메서드입니다. RequestBody.create("", null) 또는 "".toRequestBody(null)을 사용하는 것이 좋습니다.

Suggested change
.post(RequestBody.create(null, ""))
.post(RequestBody.create("", null))

.build()

instagramOkHttpClient.newCall(request).execute().use { response ->
val responseBody = response.body?.string()
if (!response.isSuccessful) {
val errorResponse = parseErrorResponse(responseBody)
logErrorResponse("[SingleMedia] MediaContainer", response.code, errorResponse)
throw RuntimeException(
"[Instagram][SingleMedia] Creation of MediaContainer Failed: ${errorResponse?.error?.message ?: "Unknown error"}",
)
}
DelayUtil.randomDelay(5, 10)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

DelayUtil.randomDelay(5, 10)에 사용된 5와 10은 매직 넘버입니다. 이 값들이 무엇을 의미하는지 명확히 하거나, 상수로 정의하여 가독성과 유지보수성을 높이는 것이 좋습니다.


return responseBody?.let { parseJsonForId(it).id }
}
}

// 2단계: 캐러셀용 부모 컨테이너 생성
fun createParentMediaContainer(
imageUrls: List<String>,
Expand Down
Loading
Loading