JDK에 기본으로 포함된 HttpURLConnection 클래스만으로도 충분히 HTTP와 HTTPS 연결을 구현할 수 있다는 사실을 알고 계셨나요? 복잡한 외부 의존성 없이 순수 자바로 HTTP/HTTPS 연결을 구현하는 방법을 알아보겠습니다.
목차
1. [HttpURLConnection vs 외부 라이브러리](#httpurlconnection-vs-외부-라이브러리)
2. [HTTP GET 요청 구현 (파라미터 포함)](#http-get-요청-구현)
3. [HTTP POST 요청 구현 (JSON 데이터 전송)](#http-post-요청-구현)
4. [HTTPS 연결 시 자주 발생하는 문제와 해결법](#https-연결-시-자주-발생하는-문제와-해결법)
5. [타임아웃 설정과 성능 최적화](#타임아웃-설정과-성능-최적화)
6. [자주 묻는 질문(FAQ)](#자주-묻는-질문)
HttpURLConnection vs 외부 라이브러리
"왜 HttpURLConnection을 사용해야 할까요?" 많은 개발자들이 가장 먼저 던지는 질문입니다.
HttpURLConnection의 장점
- 별도의 의존성 추가 필요 없음 - JDK에 기본 내장
- 가벼운 메모리 사용량 - 외부 라이브러리보다 리소스 사용이 적음
- 단순한 HTTP 요청에 적합 - 간단한 API 호출에 불필요한 오버헤드 제거
- 레거시 시스템 호환성 - 오래된 자바 버전에서도 사용 가능
외부 라이브러리(OkHttp, Apache HttpClient 등)가 더 나은 경우
- 복잡한 HTTP 요청이 많은 경우
- 동시에 많은 연결을 처리해야 하는 경우
- 고급 기능(캐싱, 쿠키 관리 등)이 필요한 경우
HTTP GET 요청 구현
실무에서 가장 많이 사용되는 GET 요청 구현 방법을 살펴보겠습니다. 특히 URL 파라미터를 포함한 요청 방법에 초점을 맞추겠습니다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class HttpGetExample {
public static void main(String[] args) {
try {
// 파라미터 준비
Map<String, String> parameters = new HashMap<>();
parameters.put("name", "홍길동");
parameters.put("age", "30");
parameters.put("query", "자바 프로그래밍");
// 파라미터가 포함된 URL 생성
String baseUrl = "http://example.com/api/search";
StringBuilder urlBuilder = new StringBuilder(baseUrl);
urlBuilder.append("?");
boolean first = true;
for (Map.Entry<String, String> entry : parameters.entrySet()) {
if (!first) {
urlBuilder.append("&");
}
urlBuilder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.toString()));
urlBuilder.append("=");
urlBuilder.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.toString()));
first = false;
}
// URL 객체 생성
URL url = new URL(urlBuilder.toString());
// HttpURLConnection 객체 생성
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 요청 메소드 설정
connection.setRequestMethod("GET");
// 타임아웃 설정 - 실무에서 중요!
connection.setConnectTimeout(5000); // 연결 타임아웃 5초
connection.setReadTimeout(5000); // 읽기 타임아웃 5초
// 요청 헤더 설정
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "Mozilla/5.0"); // 일부 서버는 User-Agent를 요구함
// 응답 코드 확인
int responseCode = connection.getResponseCode();
System.out.println("응답 코드: " + responseCode);
// 응답 읽기
try (BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
String line;
StringBuilder response = new StringBuilder();
while ((line = in.readLine()) != null) {
response.append(line);
}
System.out.println("응답 내용: " + response.toString());
}
// 연결 종료
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
실무 팁: GET 요청 시 주의사항
- 항상 파라미터를 URL 인코딩하세요 - 특수문자나 한글이 포함된 경우 반드시 필요
- User-Agent 헤더 설정 - 일부 서버는 봇 요청을 차단하므로 브라우저처럼 보이게 설정
- try-with-resources 사용 - 자원 누수를 방지하기 위해 자동 닫기 활용
- 응답 인코딩 확인 - 서버 응답의 문자 인코딩이 UTF-8인지 확인하고 적절히 처리
HTTP POST 요청 구현
REST API 연동 시 가장 많이 사용되는 JSON 데이터 전송을 위한 POST 요청 구현 방법입니다.
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class HttpPostExample {
public static void main(String[] args) {
try {
// URL 객체 생성
URL url = new URL("http://example.com/api/users");
// HttpURLConnection 객체 생성
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 요청 메소드 설정
connection.setRequestMethod("POST");
// 출력 스트림 활성화 (POST 데이터 전송을 위해 필요)
connection.setDoOutput(true);
// 타임아웃 설정
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
// 요청 헤더 설정
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Accept", "application/json");
// JSON 데이터 준비
String jsonInputString = "{"
+ "\"name\": \"홍길동\","
+ "\"email\": \"hong@example.com\","
+ "\"age\": 30,"
+ "\"address\": {"
+ " \"city\": \"서울\","
+ " \"zipcode\": \"12345\""
+ "}"
+ "}";
// POST 데이터 전송
try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
wr.write(input, 0, input.length);
}
// 응답 코드 확인
int responseCode = connection.getResponseCode();
System.out.println("응답 코드: " + responseCode);
// 정상 응답과 에러 응답을 구분하여 처리 - 실무에서 매우 중요!
if (responseCode >= 200 && responseCode < 300) { // 성공 응답
try (BufferedReader br = new BufferedReader(
new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
String line;
StringBuilder response = new StringBuilder();
while ((line = br.readLine()) != null) {
response.append(line);
}
System.out.println("성공 응답: " + response.toString());
}
} else { // 에러 응답
try (BufferedReader br = new BufferedReader(
new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8))) {
String line;
StringBuilder response = new StringBuilder();
while ((line = br.readLine()) != null) {
response.append(line);
}
System.out.println("에러 응답: " + response.toString());
}
}
// 연결 종료
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
실무 팁: POST 요청 시 주의사항
- 반드시 setDoOutput(true) 설정 - POST 데이터 전송에 필수
- 정확한 Content-Type 헤더 설정 - 특히 JSON 데이터 전송 시 application/json; charset=UTF-8 필수
- 에러 응답 처리 - 상태 코드에 따라 getInputStream() 또는 getErrorStream()을 사용
- try-with-resources 사용 - 모든 스트림을 안전하게 닫기
HTTPS 연결 시 자주 발생하는 문제와 해결법
HTTPS 연결 시 개발자들이 자주 겪는 인증서 관련 문제와 해결 방법을 알아보겠습니다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyStore;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class HttpsExample {
public static void main(String[] args) {
try {
// 1. 기본 HTTPS 요청 (대부분의 공개 API에 적합)
URL url = new URL("https://api.github.com/users/octocat");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
int responseCode = connection.getResponseCode();
System.out.println("응답 코드: " + responseCode);
// 응답 읽기
if (responseCode == HttpsURLConnection.HTTP_OK) {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()))) {
String line;
StringBuilder response = new StringBuilder();
while ((line = in.readLine()) != null) {
response.append(line);
}
System.out.println("응답 내용: " + response.toString());
}
}
// 연결 종료
connection.disconnect();
} catch (Exception e) {
System.out.println("HTTPS 연결 오류: " + e.getMessage());
e.printStackTrace();
// 오류 해결 가이드 제공
if (e.getMessage().contains("PKIX path building failed") ||
e.getMessage().contains("unable to find valid certification path")) {
System.out.println("\n[인증서 오류 해결 방법]");
System.out.println("1. 서버의 인증서를 Java 키스토어에 추가하세요:");
System.out.println(" $ keytool -import -alias example -keystore $JAVA_HOME/lib/security/cacerts -file server.crt");
System.out.println("2. 또는 개발/테스트용으로만 인증서 검증을 비활성화하는 코드를 사용하세요 (프로덕션 환경에서는 권장하지 않음)");
}
}
}
// 개발 환경에서만 사용할 인증서 검증 비활성화 메소드 (보안 위험이 있으므로 프로덕션에서는 사용하지 마세요!)
private static void disableCertificateValidation() throws Exception {
// 모든 인증서를 신뢰하는 TrustManager 생성
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
};
// SSL 컨텍스트 설정
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// 호스트명 검증 비활성화
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
}
// 프로덕션 환경에서 사용할 사용자 정의 키스토어 설정 메소드
private static void setupCustomTrustStore(String trustStorePath, String trustStorePassword) throws Exception {
System.setProperty("javax.net.ssl.trustStore", trustStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
}
}
HTTPS 연결 문제 해결 가이드
1. "PKIX path building failed" 오류 해결
이 오류는 서버의 SSL 인증서를 Java가 신뢰하지 않을 때 발생합니다.
해결 방법:
- 프로덕션 환경: 서버 인증서를 Java 키스토어에 추가
keytool -import -alias example -keystore $JAVA_HOME/lib/security/cacerts -file server.crt
- 개발/테스트 환경: Java 시스템 속성 설정
System.setProperty("javax.net.ssl.trustStore", "path/to/custom/truststore");
System.setProperty("javax.net.ssl.trustStorePassword", "password");
2. "Hostname verification failed" 오류 해결
이 오류는 인증서의 도메인명과 실제 접속 URL의 도메인명이 일치하지 않을 때 발생합니다.
해결 방법:
- 올바른 도메인명으로 접속하거나
- 개발/테스트 환경에서는 호스트명 검증 비활성화 (프로덕션에서는 권장하지 않음)
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
타임아웃 설정과 성능 최적화
실무에서 HTTP 요청의 성능과 안정성을 높이기 위한 타임아웃 설정 방법과 성능 최적화 팁을 알아보겠습니다.
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpOptimizationExample {
public static void main(String[] args) {
HttpURLConnection connection = null;
try {
URL url = new URL("http://example.com/api/resource");
connection = (HttpURLConnection) url.openConnection();
// 1. 기본 설정
connection.setRequestMethod("GET");
// 2. 타임아웃 설정 - 실무에서 매우 중요
connection.setConnectTimeout(3000); // 연결 타임아웃: 3초
connection.setReadTimeout(10000); // 읽기 타임아웃: 10초
// 3. 캐싱 비활성화 (항상 최신 데이터 필요 시)
connection.setUseCaches(false);
// 4. 리다이렉션 자동 처리 (필요 시)
connection.setInstanceFollowRedirects(true);
// 5. Keep-Alive 설정 (연결 재사용)
connection.setRequestProperty("Connection", "keep-alive");
// 6. Gzip 압축 지원 (대용량 데이터 처리 시 효율적)
connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
// 7. 응답 코드 확인
int responseCode = connection.getResponseCode();
System.out.println("응답 코드: " + responseCode);
// 응답 처리 (생략)...
} catch (Exception e) {
e.printStackTrace();
} finally {
// 항상 연결 종료
if (connection != null) {
connection.disconnect();
}
}
}
}
성능 최적화 팁
- 적절한 타임아웃 설정
- 연결 타임아웃(connectTimeout): 일반적으로 3-5초
- 읽기 타임아웃(readTimeout): 응답 크기에 따라 10-30초
- 연결 재사용(Connection Pooling)
- HttpURLConnection은 기본적으로 연결 풀링을 지원하지 않음
- 대량의 요청이 필요한 경우 Apache HttpClient나 OkHttp 같은 라이브러리 고려
- 압축 활용
- Accept-Encoding: gzip, deflate 헤더 추가
- 응답 처리 시 GZIPInputStream을 통해 압축 해제
- 비동기 요청 처리
- 다수의 요청을 병렬로 처리하려면 ExecutorService 활용
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Future<String>> futures = new ArrayList<>();
for (String apiUrl : urls) {
futures.add(executor.submit(() -> sendHttpRequest(apiUrl)));
}
for (Future<String> future : futures) {
String response = future.get(); // 응답 처리
}
자주 묻는 질문
Q1: HttpURLConnection과 HttpsURLConnection의 차이점은 무엇인가요?
A: HttpsURLConnection은 HttpURLConnection의 서브클래스로, SSL/TLS를 통한 보안 연결을 지원합니다. 기본 사용법은 동일하지만, HttpsURLConnection은 인증서 검증과 같은 추가 보안 기능을 제공합니다.
Q2: HttpURLConnection으로 파일을 업로드하는 방법은?
A: 멀티파트 폼 데이터(multipart/form-data)를 사용하여 파일을 업로드할 수 있습니다.
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
try (OutputStream output = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8), true)) {
// 텍스트 파라미터 추가
writer.append("--" + boundary).append("\r\n");
writer.append("Content-Disposition: form-data; name=\"param\"").append("\r\n");
writer.append("\r\n");
writer.append("value").append("\r\n");
// 파일 파라미터 추가
writer.append("--" + boundary).append("\r\n");
writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"file.txt\"").append("\r\n");
writer.append("Content-Type: text/plain").append("\r\n");
writer.append("\r\n");
writer.flush();
// 파일 데이터 복사
Files.copy(Paths.get("path/to/file.txt"), output);
output.flush();
// 경계 종료
writer.append("\r\n");
writer.append("--" + boundary + "--").append("\r\n");
}
Q3: 어떤 경우에 OkHttp나 Apache HttpClient 같은 외부 라이브러리를 사용하는 것이 좋을까요?
A: 다음과 같은 경우 외부 라이브러리 사용을 고려하세요:
- 자동 연결 풀링이 필요한 경우
- 복잡한 인증 메커니즘(OAuth 등)이 필요한 경우
- 재시도 로직, 인터셉터 등 고급 기능이 필요한 경우
- 비동기/동시 요청이 많은 경우
Q4: HTTP 상태 코드를 어떻게 효과적으로 처리해야 할까요?
A: 상태 코드 범위별로 처리하는 것이 좋습니다:
- 2xx (성공): 정상 응답 처리
- 3xx (리다이렉션): 필요시 새 위치로 요청 재시도
- 4xx (클라이언트 오류): 요청 수정 필요 (잘못된 인증, 권한 등)
- 5xx (서버 오류): 일정 시간 후 재시도 로직 구현
Q5: HttpURLConnection의 성능을 개선하는 방법은?
A:
- 요청 간 Connection: keep-alive 헤더를 사용하여 연결 재사용
- 큰 응답을 처리할 때는 버퍼 크기 조정 (기본 8KB)
- 압축(gzip) 지원 활성화
- 응답 처리 시 BufferedReader/BufferedInputStream 사용
- 가능하면 ExecutorService를 사용하여 병렬 요청 처리
결론
자바의 HttpURLConnection은 외부 라이브러리 없이도 충분히 강력한 HTTP/HTTPS 통신을 구현할 수 있는 클래스입니다. 이 글에서 다룬 샘플 코드와 팁을 활용하면 대부분의 일반적인 API 통신을 구현할 수 있습니다.
복잡한 요구사항이나 대규모 애플리케이션에서는 OkHttp, Apache HttpClient 같은 전문 라이브러리를 고려해볼 수도 있지만, 간단한 HTTP 요청이나 외부 의존성을 최소화하고 싶은 경우 HttpURLConnection은 여전히 훌륭한 선택입니다.
'프로그램 > Java' 카테고리의 다른 글
| [Java] 소수점 자르기(반올림, 내림, 버림) 방법 (0) | 2025.03.14 |
|---|---|
| [Java] Date 객체와 날짜 처리 방법 (0) | 2025.03.13 |
| [Java] Rest API 연결 가이드 샘플 (0) | 2025.02.26 |
| 예쁜꼬마선충의 신경 연결 지도를 활용하여 로봇에 인공지능(AI) 프로그램 (0) | 2025.02.25 |
| 예쁜꼬마선충 뇌정보를 이해하고 이용한 Java 프로그램 (0) | 2025.02.25 |