왜 알아보았는가
RestTemplate Class를 이용하여 외부 API 호출을 시도했고 코드에는 전혀 이상이 없어보였다. 하지만 실패했고 다음과 같은 Exception이 발생했다.
💡
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "{도메인}”: Illegal given domain name: {도메인}; nested exception is javax.net.ssl.SSLHandshakeException: Illegal given domain name: {도메인}
원인
해당 프로젝트는 jdk8 구버전 이하에서 발생한다. 호출하는 API의 도메인에 언더바 (_)가 포함되어 있는 경우 jvm이 인식하지 못하고 ResourceAccessException을 throw 한다.
해결법
편리한 RestTemplate은 이용하지 못하고 HttpURLConnection을 통해 API 호출을 해주어야 한다. 특정 옵션을 주는데 구체적인 사항은 코드로 살펴보자
private JsonNode API호출ByHttpsURLConnection(String strURL){
JsonNode commands = null;
try {
URL url = new URL(strURL);
// jdk 구버전 (corretto-1.8.0_312)에서 언더바(_) 포함된 domain은 호출하지 못하는 에러 처리를 위한 옵션
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
// set property
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "Mozilla/5.0");
conn.setRequestProperty("Accept-Language", "ko-kr");
conn.setRequestProperty("Access-Control-Allow-Origin", "*");
conn.setRequestProperty("Content-Type", "application/json");
conn.setConnectTimeout(10000);
conn.setReadTimeout(5000);
conn.setDoOutput(true); // 항상 갱신된 내용 가져오기
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
wr.flush();
wr.close();
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
Charset charset = Charset.forName("UTF-8");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
StringBuilder sb = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) {
sb.append(inputLine);
}
in.close();
ObjectMapper objectMapper = new ObjectMapper();
commands = objectMapper.readTree(sb.toString()).get("header"); // response에 header에 해당하는 key 값의 value를 받아오기 위함
}
} catch (IOException e) {
e.printStackTrace();
throw new OliveException("API 호출 에러");
}
return commands;
}
즉 HttpURLConnection을 통해 API를 호출하면서 openConnection() 하기 전에 다음과 같은 옵션을 주면 된다!
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
스택 오버플로우 참고