11package com .backend .domain .repository .service .fetcher ;
22
33import com .backend .domain .repository .dto .response .github .*;
4+ import com .backend .global .exception .BusinessException ;
5+ import com .backend .global .exception .ErrorCode ;
46import com .backend .global .github .GitHubApiClient ;
57import lombok .RequiredArgsConstructor ;
68import lombok .extern .slf4j .Slf4j ;
9+ import org .springframework .retry .annotation .Backoff ;
10+ import org .springframework .retry .annotation .Recover ;
11+ import org .springframework .retry .annotation .Retryable ;
712import org .springframework .stereotype .Component ;
13+ import org .springframework .web .reactive .function .client .WebClientRequestException ;
14+ import org .springframework .web .reactive .function .client .WebClientResponseException ;
815
916import java .time .LocalDateTime ;
1017import java .time .format .DateTimeFormatter ;
@@ -19,26 +26,71 @@ public class GitHubDataFetcher {
1926 private final GitHubApiClient gitHubApiClient ;
2027 private static final int COMMUNITY_ANALYSIS_MONTHS = 6 ;
2128
29+ @ Retryable (
30+ retryFor = {WebClientResponseException .ServiceUnavailable .class ,
31+ WebClientResponseException .InternalServerError .class ,
32+ WebClientRequestException .class }, // 네트워크 타임아웃
33+ noRetryFor = {WebClientResponseException .NotFound .class , // 404, 401 에러는 재시도 X
34+ WebClientResponseException .Unauthorized .class },
35+ maxAttempts = 2 , // 최대 2회 시도 (원본 1회 + 재시도 1회)
36+ backoff = @ Backoff (delay = 1000 ) // 재시도 전 1초 대기
37+ )
2238 public RepoResponse fetchRepositoryInfo (String owner , String repoName ) {
2339 return gitHubApiClient .get ("/repos/{owner}/{repo}" , RepoResponse .class , owner , repoName );
2440 }
2541
42+ @ Retryable (
43+ retryFor = {WebClientResponseException .ServiceUnavailable .class ,
44+ WebClientResponseException .InternalServerError .class ,
45+ WebClientRequestException .class },
46+ noRetryFor = {WebClientResponseException .NotFound .class ,
47+ WebClientResponseException .Unauthorized .class },
48+ maxAttempts = 2 ,
49+ backoff = @ Backoff (delay = 1000 )
50+ )
2651 public String fetchReadmeContent (String owner , String repoName ) {
2752 return gitHubApiClient .getRaw ("/repos/{owner}/{repo}/readme" , owner , repoName );
2853 }
2954
55+ @ Retryable (
56+ retryFor = {WebClientResponseException .ServiceUnavailable .class ,
57+ WebClientResponseException .InternalServerError .class ,
58+ WebClientRequestException .class },
59+ noRetryFor = {WebClientResponseException .NotFound .class ,
60+ WebClientResponseException .Unauthorized .class },
61+ maxAttempts = 2 ,
62+ backoff = @ Backoff (delay = 1000 )
63+ )
3064 public List <CommitResponse > fetchCommitInfo (String owner , String repoName , String since ) {
3165 return gitHubApiClient .getList (
3266 "/repos/{owner}/{repo}/commits?since={since}&per_page=100" , CommitResponse .class , owner , repoName , since
3367 );
3468 }
3569
70+ @ Retryable (
71+ retryFor = {WebClientResponseException .ServiceUnavailable .class ,
72+ WebClientResponseException .InternalServerError .class ,
73+ WebClientRequestException .class },
74+ noRetryFor = {WebClientResponseException .NotFound .class ,
75+ WebClientResponseException .Unauthorized .class },
76+ maxAttempts = 2 ,
77+ backoff = @ Backoff (delay = 1000 )
78+ )
3679 public TreeResponse fetchRepositoryTreeInfo (String owner , String repoName , String defaultBranch ) {
3780 return gitHubApiClient .get (
3881 "/repos/{owner}/{repo}/git/trees/{sha}?recursive=1" , TreeResponse .class , owner , repoName , defaultBranch
3982 );
4083 }
4184
85+ @ Retryable (
86+ retryFor = {WebClientResponseException .ServiceUnavailable .class ,
87+ WebClientResponseException .InternalServerError .class ,
88+ WebClientRequestException .class },
89+ noRetryFor = {WebClientResponseException .NotFound .class ,
90+ WebClientResponseException .Unauthorized .class },
91+ maxAttempts = 2 ,
92+ backoff = @ Backoff (delay = 1000 )
93+ )
4294 public List <IssueResponse > fetchIssueInfo (String owner , String repoName ) {
4395 List <IssueResponse > allIssues = gitHubApiClient .getList (
4496 "/repos/{owner}/{repo}/issues?state=all&per_page=100" , IssueResponse .class , owner , repoName );
@@ -50,6 +102,15 @@ public List<IssueResponse> fetchIssueInfo(String owner, String repoName) {
50102 .collect (Collectors .toList ());
51103 }
52104
105+ @ Retryable (
106+ retryFor = {WebClientResponseException .ServiceUnavailable .class ,
107+ WebClientResponseException .InternalServerError .class ,
108+ WebClientRequestException .class },
109+ noRetryFor = {WebClientResponseException .NotFound .class ,
110+ WebClientResponseException .Unauthorized .class },
111+ maxAttempts = 2 ,
112+ backoff = @ Backoff (delay = 1000 )
113+ )
53114 public List <PullRequestResponse > fetchPullRequestInfo (String owner , String repoName ) {
54115 List <PullRequestResponse > allPullRequests = gitHubApiClient .getList (
55116 "/repos/{owner}/{repo}/pulls?state=all&per_page=100" , PullRequestResponse .class , owner , repoName );
@@ -60,6 +121,15 @@ public List<PullRequestResponse> fetchPullRequestInfo(String owner, String repoN
60121 .collect (Collectors .toList ());
61122 }
62123
124+ @ Retryable (
125+ retryFor = {WebClientResponseException .ServiceUnavailable .class ,
126+ WebClientResponseException .InternalServerError .class ,
127+ WebClientRequestException .class },
128+ noRetryFor = {WebClientResponseException .NotFound .class ,
129+ WebClientResponseException .Unauthorized .class },
130+ maxAttempts = 2 ,
131+ backoff = @ Backoff (delay = 1000 )
132+ )
63133 public Map <String , Integer > fetchLanguages (String owner , String repoName ) {
64134 return gitHubApiClient .get ("/repos/{owner}/{repo}/languages" , Map .class , owner , repoName );
65135 }
@@ -71,4 +141,10 @@ private LocalDateTime getSixMonthsAgo() {
71141 private LocalDateTime parseGitHubDate (String dateString ) {
72142 return LocalDateTime .parse (dateString , DateTimeFormatter .ISO_OFFSET_DATE_TIME );
73143 }
144+
145+ @ Recover // 재시도 실패 시 호출되는 메서드
146+ public RepoResponse recover (WebClientResponseException e , String owner , String repoName ) {
147+ log .error ("GitHub API 재시도 실패: {}/{}" , owner , repoName , e );
148+ throw new BusinessException (ErrorCode .GITHUB_API_FAILED );
149+ }
74150}
0 commit comments