77import  lombok .RequiredArgsConstructor ;
88import  lombok .extern .slf4j .Slf4j ;
99import  org .springframework .retry .annotation .Backoff ;
10- import  org .springframework .retry .annotation .Recover ;
1110import  org .springframework .retry .annotation .Retryable ;
1211import  org .springframework .stereotype .Component ;
1312import  org .springframework .web .reactive .function .client .WebClientRequestException ;
1413import  org .springframework .web .reactive .function .client .WebClientResponseException ;
1514
1615import  java .time .LocalDateTime ;
1716import  java .time .format .DateTimeFormatter ;
17+ import  java .util .Collections ;
1818import  java .util .List ;
1919import  java .util .Map ;
20+ import  java .util .Optional ;
2021import  java .util .stream .Collectors ;
2122
2223@ Slf4j 
@@ -30,8 +31,6 @@ public class GitHubDataFetcher {
3031            retryFor  = {WebClientResponseException .ServiceUnavailable .class ,
3132                    WebClientResponseException .InternalServerError .class ,
3233                    WebClientRequestException .class },  // 네트워크 타임아웃 
33-             noRetryFor  = {WebClientResponseException .NotFound .class ,  // 404, 401 에러는 재시도 X 
34-                     WebClientResponseException .Unauthorized .class },
3534            maxAttempts  = 2 ,  // 최대 2회 시도 (원본 1회 + 재시도 1회) 
3635            backoff  = @ Backoff (delay  = 1000 )  // 재시도 전 1초 대기 
3736    )
@@ -43,21 +42,25 @@ public RepoResponse fetchRepositoryInfo(String owner, String repoName) {
4342            retryFor  = {WebClientResponseException .ServiceUnavailable .class ,
4443                    WebClientResponseException .InternalServerError .class ,
4544                    WebClientRequestException .class },
46-             noRetryFor  = {WebClientResponseException .NotFound .class ,
47-                     WebClientResponseException .Unauthorized .class },
4845            maxAttempts  = 2 ,
4946            backoff  = @ Backoff (delay  = 1000 )
5047    )
51-     public  String  fetchReadmeContent (String  owner , String  repoName ) {
52-         return  gitHubApiClient .getRaw ("/repos/{owner}/{repo}/readme" , owner , repoName );
48+     public  Optional <String > fetchReadmeContent (String  owner , String  repoName ) {
49+         try  {
50+             String  content  = gitHubApiClient .getRaw ("/repos/{owner}/{repo}/readme" , owner , repoName );
51+             return  Optional .ofNullable (content );
52+         } catch  (BusinessException  e ) {
53+             if  (e .getErrorCode () == ErrorCode .GITHUB_REPO_NOT_FOUND ) {
54+                 return  Optional .empty ();
55+             }
56+             throw  e ;
57+         }
5358    }
5459
5560    @ Retryable (
5661            retryFor  = {WebClientResponseException .ServiceUnavailable .class ,
5762                    WebClientResponseException .InternalServerError .class ,
5863                    WebClientRequestException .class },
59-             noRetryFor  = {WebClientResponseException .NotFound .class ,
60-                     WebClientResponseException .Unauthorized .class },
6164            maxAttempts  = 2 ,
6265            backoff  = @ Backoff (delay  = 1000 )
6366    )
@@ -71,62 +74,83 @@ public List<CommitResponse> fetchCommitInfo(String owner, String repoName, Strin
7174            retryFor  = {WebClientResponseException .ServiceUnavailable .class ,
7275                    WebClientResponseException .InternalServerError .class ,
7376                    WebClientRequestException .class },
74-             noRetryFor  = {WebClientResponseException .NotFound .class ,
75-                     WebClientResponseException .Unauthorized .class },
7677            maxAttempts  = 2 ,
7778            backoff  = @ Backoff (delay  = 1000 )
7879    )
79-     public  TreeResponse  fetchRepositoryTreeInfo (String  owner , String  repoName , String  defaultBranch ) {
80-         return  gitHubApiClient .get (
81-                 "/repos/{owner}/{repo}/git/trees/{sha}?recursive=1" , TreeResponse .class , owner , repoName , defaultBranch 
82-         );
80+     public  Optional <TreeResponse > fetchRepositoryTreeInfo (String  owner , String  repoName , String  defaultBranch ) {
81+         try  {
82+             TreeResponse  tree  = gitHubApiClient .get (
83+                     "/repos/{owner}/{repo}/git/trees/{sha}?recursive=1" ,
84+                     TreeResponse .class , owner , repoName , defaultBranch 
85+             );
86+             return  Optional .ofNullable (tree );
87+         } catch  (BusinessException  e ) {
88+             if  (e .getErrorCode () == ErrorCode .GITHUB_REPO_NOT_FOUND ) {
89+                 return  Optional .empty ();
90+             }
91+             throw  e ;
92+         }
8393    }
8494
8595    @ Retryable (
8696            retryFor  = {WebClientResponseException .ServiceUnavailable .class ,
8797                    WebClientResponseException .InternalServerError .class ,
8898                    WebClientRequestException .class },
89-             noRetryFor  = {WebClientResponseException .NotFound .class ,
90-                     WebClientResponseException .Unauthorized .class },
9199            maxAttempts  = 2 ,
92100            backoff  = @ Backoff (delay  = 1000 )
93101    )
94102    public  List <IssueResponse > fetchIssueInfo (String  owner , String  repoName ) {
95-         List <IssueResponse > allIssues  = gitHubApiClient .getList (
96-                 "/repos/{owner}/{repo}/issues?state=all&per_page=100" , IssueResponse .class , owner , repoName );
97- 
98-         LocalDateTime  sixMonthsAgo  = getSixMonthsAgo ();
99-         return  allIssues .stream ()
100-                 .filter (IssueResponse ::isPureIssue )
101-                 .filter (issue  -> parseGitHubDate (issue .created_at ()).isAfter (sixMonthsAgo ))
102-                 .collect (Collectors .toList ());
103+         try  {
104+             List <IssueResponse > allIssues  = gitHubApiClient .getList (
105+                     "/repos/{owner}/{repo}/issues?state=all&per_page=100" ,
106+                     IssueResponse .class , owner , repoName 
107+             );
108+ 
109+             LocalDateTime  sixMonthsAgo  = getSixMonthsAgo ();
110+             return  allIssues .stream ()
111+                     .filter (IssueResponse ::isPureIssue )
112+                     .filter (issue  -> parseGitHubDate (issue .created_at ()).isAfter (sixMonthsAgo ))
113+                     .collect (Collectors .toList ());
114+ 
115+         } catch  (BusinessException  e ) {
116+             if  (e .getErrorCode () == ErrorCode .GITHUB_REPO_NOT_FOUND ) {
117+                 return  Collections .emptyList ();
118+             }
119+             throw  e ;
120+         }
103121    }
104122
105123    @ Retryable (
106124            retryFor  = {WebClientResponseException .ServiceUnavailable .class ,
107125                    WebClientResponseException .InternalServerError .class ,
108126                    WebClientRequestException .class },
109-             noRetryFor  = {WebClientResponseException .NotFound .class ,
110-                     WebClientResponseException .Unauthorized .class },
111127            maxAttempts  = 2 ,
112128            backoff  = @ Backoff (delay  = 1000 )
113129    )
114130    public  List <PullRequestResponse > fetchPullRequestInfo (String  owner , String  repoName ) {
115-         List <PullRequestResponse > allPullRequests  = gitHubApiClient .getList (
116-                 "/repos/{owner}/{repo}/pulls?state=all&per_page=100" , PullRequestResponse .class , owner , repoName );
131+         try  {
132+             List <PullRequestResponse > allPullRequests  = gitHubApiClient .getList (
133+                     "/repos/{owner}/{repo}/pulls?state=all&per_page=100" ,
134+                     PullRequestResponse .class , owner , repoName 
135+             );
117136
118-         LocalDateTime  sixMonthsAgo  = getSixMonthsAgo ();
119-         return  allPullRequests .stream ()
120-                 .filter (pr  -> parseGitHubDate (pr .created_at ()).isAfter (sixMonthsAgo ))
121-                 .collect (Collectors .toList ());
137+             LocalDateTime  sixMonthsAgo  = getSixMonthsAgo ();
138+             return  allPullRequests .stream ()
139+                     .filter (pr  -> parseGitHubDate (pr .created_at ()).isAfter (sixMonthsAgo ))
140+                     .collect (Collectors .toList ());
141+ 
142+         } catch  (BusinessException  e ) {
143+             if  (e .getErrorCode () == ErrorCode .GITHUB_REPO_NOT_FOUND ) {
144+                 return  Collections .emptyList ();
145+             }
146+             throw  e ;
147+         }
122148    }
123149
124150    @ Retryable (
125151            retryFor  = {WebClientResponseException .ServiceUnavailable .class ,
126152                    WebClientResponseException .InternalServerError .class ,
127153                    WebClientRequestException .class },
128-             noRetryFor  = {WebClientResponseException .NotFound .class ,
129-                     WebClientResponseException .Unauthorized .class },
130154            maxAttempts  = 2 ,
131155            backoff  = @ Backoff (delay  = 1000 )
132156    )
@@ -141,10 +165,4 @@ private LocalDateTime getSixMonthsAgo() {
141165    private  LocalDateTime  parseGitHubDate (String  dateString ) {
142166        return  LocalDateTime .parse (dateString , DateTimeFormatter .ISO_OFFSET_DATE_TIME );
143167    }
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-     }
150168}
0 commit comments