|
@@ -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 {
|