Jelajahi Sumber

utic-incident first commit

HANTE 1 bulan lalu
induk
melakukan
8b0d2a5035

+ 86 - 29
utic-incident-common/src/main/java/com/utic/incident/common/url/RequestUrlData.java

@@ -32,7 +32,28 @@ public final class RequestUrlData {
             String urlString, int connectTimeout, int readTimeout, int maxRetries, int retryDelaySeconds, Class<T> responseType) {
         return executeRequestWithRetries(urlString, connectTimeout, readTimeout, maxRetries, retryDelaySeconds, null, responseType); // ✅ rootElement 추가
     }
-    private static <T> RequestUrlDataResult<T> executeRequestWithRetries(String urlString, int connectTimeout, int readTimeout, int maxRetries, int retryDelaySeconds, TypeReference<T> jsonType, Class<T> xmlType) {
+
+    private static void retryWithDelay(int retryDelaySeconds) {
+        if (retryDelaySeconds > 0) {
+            try {
+                log.info("{}초 동안 대기 후 재시도...", retryDelaySeconds);
+                Thread.sleep(retryDelaySeconds * 1000L);
+            } catch (InterruptedException ex) {
+                Thread.currentThread().interrupt();
+                log.error("재시도 대기 중 인터럽트 발생: {}", ex.getMessage());
+            }
+        }
+    }
+
+    private static <T> RequestUrlDataResult<T> executeRequestWithRetries(
+            final String urlString,
+            final int connectTimeout,
+            final int readTimeout,
+            final int maxRetries,
+            final int retryDelaySeconds,
+            final TypeReference<T> jsonType,
+            final Class<T> xmlType) {
+
         int attempt = 0;
 
         while (attempt < maxRetries) {
@@ -45,40 +66,41 @@ public final class RequestUrlData {
                 String contentType = getContentType(connection);
                 String response = readResponse(connection);
 
-//                log.info("요청 성공 ({}번째 시도): [{}] 상태 코드 {}, 응답 크기 {} bytes", attempt, urlString, responseCode, response.length());
-//                log.info("Content-Type: {}", contentType);
-//                log.info("응답 내용: {}", response);
-
                 if (responseCode == HttpURLConnection.HTTP_OK) {
-                    T data = "application/json".equalsIgnoreCase(contentType) && jsonType != null
-                            ? parseJson(response, jsonType)
-                            : parseXml(response, xmlType);
-
-                    return new RequestUrlDataResult<>(RequestUrlDataError.SUCCESS, data);
+                    if ("application/json".equalsIgnoreCase(contentType) && jsonType != null) {
+                        // JSON 응답 처리
+                        return new RequestUrlDataResult<>(RequestUrlDataError.SUCCESS, "SUCCESS", parseJson(response, jsonType));
+                    }
+                    else if ("application/xml".equalsIgnoreCase(contentType) && xmlType != null) {
+                        // XML 응답 처리
+                        return new RequestUrlDataResult<>(RequestUrlDataError.SUCCESS, "SUCCESS", parseXml(response, xmlType));
+                    }
+                    else {
+                        String errorMessage = String.format("지원되지 않는 Content-Type: %s", contentType);
+                        log.warn("{}.", errorMessage);
+                        return new RequestUrlDataResult<>(RequestUrlDataError.UNSUPPORTED_CONTENT_TYPE, errorMessage, null);
+                    }
                 }
 
-//                log.warn("요청 실패 ({}번째 시도): [{}] 상태 코드 {}, 메시지 {}", attempt, urlString, responseCode, connection.getResponseMessage());
-
-                if (shouldRetry(responseCode) && attempt < maxRetries) {
-                    log.info("재시도 진행 중... ({}/{})", attempt, maxRetries);
-                    Thread.sleep(retryDelaySeconds * 1000L);
-                    continue; // 재시도
+                // 오류 응답 처리
+                if (attempt < maxRetries && shouldRetry(responseCode)) {
+                    int retryDelay = (responseCode == 429) ? getRetryDelay(connection, retryDelaySeconds) : retryDelaySeconds;
+                    log.info("서버에서 오류 발생[RESPONSE: {}]. {}초 후 재시도... ({}/{})", responseCode, retryDelay, attempt, maxRetries);
+                    retryWithDelay(retryDelay);
+                }
+                else {
+                    break;
                 }
-                return new RequestUrlDataResult<>(RequestUrlDataError.HTTP_ERROR, null);
             }
             catch (Exception e) {
                 log.error("요청 오류 발생 ({}/{}): [{}] -> {}", attempt, maxRetries, urlString, e.getMessage());
-                if (attempt < maxRetries) { // 최대 횟수 도달 전이면 재시도
-                    log.info("네트워크 오류 발생. {}초 후 재시도... ({}/{})", retryDelaySeconds, attempt, maxRetries);
-                    try {
-                        Thread.sleep(retryDelaySeconds * 1000L);
-                    } catch (InterruptedException ex) {
-                        log.error("재시도 대기 중 인터럽트 발생: {}", ex.getMessage());
-                        break;
-                    }
-                    continue; // 재시도 로직으로 이동
+                if (attempt < maxRetries) {
+                    log.info("네트워크 요청 오류 발생. {}초 후 재시도... ({}/{})", retryDelaySeconds, attempt, maxRetries);
+                    retryWithDelay(retryDelaySeconds);
+                }
+                else {
+                    return new RequestUrlDataResult<>(RequestUrlDataError.NETWORK_ERROR, e.getMessage(), null);
                 }
-                return new RequestUrlDataResult<>(RequestUrlDataError.NETWORK_ERROR, null);
             }
             finally {
                 if (connection != null) {
@@ -87,11 +109,46 @@ public final class RequestUrlData {
             }
         }
 
-        return new RequestUrlDataResult<>(RequestUrlDataError.RETRY_LIMIT_EXCEEDED, null);
+        log.warn("최대 재시도 횟수({})를 초과했습니다: [{}]", maxRetries, urlString);
+        return new RequestUrlDataResult<>(RequestUrlDataError.RETRY_LIMIT_EXCEEDED, "재시도 횟수 초과", null);
     }
 
     private static boolean shouldRetry(int responseCode) {
-        return responseCode >= 500; // 서버 오류 (5xx) 발생 시 재시도
+        return (responseCode >= 500 && responseCode <= 504) || responseCode == 429;
+    }
+    private static int getRetryDelay(HttpURLConnection connection, int retryDelaySeconds) {
+        String retryAfter = connection.getHeaderField("Retry-After");
+        if (retryAfter != null) {
+            try {
+                int retrySeconds = Integer.parseInt(retryAfter);
+                return Math.min(retrySeconds, retryDelaySeconds); // 최대 제한 적용
+            } catch (NumberFormatException e) {
+                log.warn("Retry-After 값 파싱 오류: {}", e.getMessage());
+            }
+        }
+        return 0; // Retry-After 값이 없거나 오류가 발생한 경우 기본값 반환
+    }
+    private static boolean shouldRetry(int responseCode, HttpURLConnection connection) {
+        if (responseCode >= 500 && responseCode <= 504) {
+            return true; // 서버 오류로 인해 재시도 가능
+        }
+
+        if (responseCode == 429) {
+            String retryAfter = connection.getHeaderField("Retry-After");
+            if (retryAfter != null) {
+                try {
+                    int retrySeconds = Integer.parseInt(retryAfter);
+                    log.info("서버 요청 제한(429). {}초 후 재시도 권장.", retrySeconds);
+                    Thread.sleep(retrySeconds * 1000L);
+                    return true;
+                } catch (NumberFormatException | InterruptedException e) {
+                    log.warn("Retry-After 값을 파싱하는 중 오류 발생: {}", e.getMessage());
+                    Thread.currentThread().interrupt();
+                }
+            }
+            return false; // Retry-After 정보가 없으면 재시도하지 않음
+        }
+        return false; // 기타 오류 코드에 대해서는 재시도하지 않음
     }
 
     private static HttpURLConnection createConnection(String urlString, int connectTimeout, int readTimeout) throws IOException {

+ 2 - 1
utic-incident-common/src/main/java/com/utic/incident/common/url/RequestUrlDataError.java

@@ -4,7 +4,8 @@ public enum RequestUrlDataError {
     SUCCESS(0, "요청 성공"),
     NETWORK_ERROR(1, "네트워크 오류"),
     HTTP_ERROR(2, "HTTP 요청 실패"),
-    RETRY_LIMIT_EXCEEDED(3, "최대 재시도 횟수 초과");
+    RETRY_LIMIT_EXCEEDED(3, "최대 재시도 횟수 초과"),
+    UNSUPPORTED_CONTENT_TYPE(4, "지원되지 않는 Content-Type");
 
     private final int code;
     private final String message;

+ 3 - 1
utic-incident-common/src/main/java/com/utic/incident/common/url/RequestUrlDataResult.java

@@ -2,10 +2,12 @@ package com.utic.incident.common.url;
 
 public class RequestUrlDataResult<T> {
     private final RequestUrlDataError errorCode;
+    private final String errorMessage;
     private final T data;
 
-    public RequestUrlDataResult(RequestUrlDataError errorCode, T data) {
+    public RequestUrlDataResult(RequestUrlDataError errorCode, String errorMessage, T data) {
         this.errorCode = errorCode;
+        this.errorMessage = errorMessage;
         this.data = data;
     }