본문 바로가기
3. 웹개발/3_1_1 JAVA

[Java] URLConnection & HttpURLConnection

by 갓대희 2018. 11. 18.
반응형

[Java] URLConnection & HttpURLConnection




안녕하세요. 갓대희 입니다. 이번 포스팅은 [ URLConnection & HttpURLConnection 입니다. : ) 


들어가기에 앞서 왜 해당 내용을 공부했고, 어떤 곳에 활용 할 수 있는지 먼저 쓰려고 한다.

EX) HttpURLConnection 을 활용 하여 네이버 실시간 순위를 가져와  보자.


1위 부터 20위 까지 가져와 보도록 하자 ( 30위 까지도 들어가 있더라)



프로그래밍을 통해 네이버 실시간 순위를 들고 온 것을 볼 수 있다.

이 클래스를 활용하여 크롤링도 가능 한 것이다.

간단히 공부하고 내용 정리를 해 보았다.

※ URLConnection 클래스


 - 사용자 인증이나 보안이 설정되어 있지 않은 웹서버에 접속하여 파일 등을 다운로드하는데 많이 사용한다.


 - URLConnection은 리소스에 연결하기 전에 구성 되어야 한다.


 - URLConnection 인스턴스는 재사용 될 수 없다. 각 리소스에 대한 커넥션 마다 다른 인스턴스를 사용해야 한다.


 - 스펙 및 Method 참고 

   URLConnection (Java Platform SE 7)

   http://docs.oracle.com/javase/7/docs/api/java/net/URLConnection.html




※ HttpURLConnection 클래스



 - 스펙 및 Method 참고 

   HttpURLConnection (Java Platform SE 8)

   https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html


 - java.net.HttpURLConnection 클래스는 URLConnection을 구현한 클래스 (java.net 클래스에서 제공하는 URL 요청을 위한 클래스)


 - URLConnection은 웹을 통해 데이터를 주고 받는데 사용된다. (RFC 2616을 따름)


 - 데이터의 타입이나 길이는 거의 제한이 없다.


 - 주로 미리 길이를 알지 못하는 스트리밍 데이터를 주고 받는데 사용된다.


 - http URL을 처리할 때 도움이 되는 몇 가지 추가적인 메서드를 가지고 있다.


 - 요청 방식을 확인 or 설정, redirect 여부 결정, 응답 코드와 메시지를 Read , 프록시 서버가 사용되었는지 여부 확인 메서드 등을 가지고 있다.


 - 다양한 HTTP 응답 코드에 해당하는 상수 값들이 정의되어있다.


 - URLConnection 클래스의 getPermission() 메서드를 오버라이드해놓았다.


 ★ URLConnection 클래스와 마찬가지로 생성자가 protected로 선언되어있기 때문에 기본적으로는 개발자가 직접 HttpURLConnection 객체를 생성할 수 없다.

   하지만 http URL을 사용하는 URL 객체의 openConnection() 메서드가 리턴하는 URLConnection 객체는 HttpURLConnection의 인스턴스가 될 수 있기 때문에 리턴된 URLConnection을 

다음처럼 HttpURLConnection으로 캐스팅해서 사용한다. 


ex ) 

URL u = new URL("http://www.naver.com");

HttpURLConnection http = (HttpURLConnection) u.openConnection();



▶ 요청 방식 설정


 - HttpURLConnection은 기본적으로 GET 메서드를 사용.

 - setRequestMethod() 메서드를 사용해서 메서드 변경 가능.

 - 요청방식은 대문자로 전달해야 한다.

 - 지정된 요청 방식 이외의 파라미터 전달시 java.net.ProtocolException이 발생. 

 - Method 

   public void setRequestMethod(String method)

 - 요청 메서드 종류


1. HEAD

 - 문서의 헤더 정보만 요청한다.


ex)

import java.io.IOException;

import java.net.*;


public class HeaderGetMethod {

public static void main(String[] args) {

String domain = "http://www.naver.com";

try {

URL u = new URL(domain);

HttpURLConnection http = (HttpURLConnection) u.openConnection();

http.setRequestMethod("HEAD");

System.out.println(http.getHeaderFields());

} catch (MalformedURLException e) {

System.out.println(domain+" is not a URL I understand");

} catch (IOException e) {

}

}

}


2. GET

 - 웹 서버로부터 리소스를 가져온다.


3. POST

 - 폼에 입력된 내용을 서버로 전송한다.


4. DELETE

 - 웹 서버의 리소스를 지운다.

 - 대부분의 서버는 기본적으로 DELETE를 허용하지 않거나 인증을 요구한다.

 - 서버는 이 요청을 거절하거나 인증을 요청할 수 있으며, 허용하는 경우에도 응답은 구현에 따라 차이가 있다.

 - 서버설정에 따라 파일을 지우기, 휴지통으로 이동, 파일을 읽을 수 없도록 표시 하는 등의 행위를 하게 된다.


5. put

 - 웹 서버로 리소스를 전송한다.

 - PUT 요청도 파일을 지울 때와 마찬가지로 보통 사용자 인증을 요구하며, PUT 메서드를 지원하도록 설정해줘야 한다.


6. OPTIONS

 - 특정 URL에 대해 지원되는 요청 메서드의 목록을 리턴한다.

 - 요청 URL이 *인 경우 헤당 요청의 대상은 서버에 있는 하나의 특정 URL이 아니라 서버 전체에 적용된다는 것을 의미한다.


7. TRACE

 - 요청을 추적한다.

 - 클라이언트가 보낸 요청이 클라이언트와 서버 사이에 있는 프록시 서버에서 변경되었는지를 확인할 필요가 있는 경우 등에 쓰임



▶ 서버와 연결 해제


 - HTTP 1.1은 단일 TCP 소켓을 사용해서 다수의 요청과 응답을 처리할 수 있는 Keep-Alive(HTTP 연결 재사용)를 지원한다. 

 하지만 Keep-Alive를 사용하면 서버가 클라이언트에게 데이터의 마지막 바이트를 전송한 후에도 연결을 쉽게 닫을 수 없다는 문제가 발생. 

 이때 타임아웃(하단에 상세하게 설명)을 설정하여 특정 시간 동안 활동이 없으면 연결을 종료하는 방법을 사용할 수도 있다.

 위의 방법 보다는 클라이언트가 작업이 끝났을 때 스스로 연결을 종료하는 것이 바람직 하다.


 - HttpURLConnection 클래스는 HTTP Keep-Alive 기능을 명시적으로 끄지 않는 한 기본적으로 지원한다.

   즉 HttpURLConnection 클래스는 서버가 연결을 종료하기 전에 동일한 클라이언트에서 다시 연결할 경우 기존에 연결된 소켓을 재사용하는데,

이때 disconnect() 메서드는 특정 호스트와의 대화가 끝난 시점에 클라이언트가 서버와의 연결을 끊을 수 있도록 한다.

 - 메서드 : public abstract void disconnect()


이 메서드를 호출하면 그 시점에 해당 연결을 아직 열고 있는 스트림이 있을 경우 해당 스트림을 종료시킨다.

그러나 반대로 스트림을 닫는다고 해서 연결을 닫거나 끊지는 않는다.



▶ 타임 아웃 설정 관련 내용 요약

 - timeout (URLConnection은 두가지 타임아웃을 제공, 기본적인 동작은 타임아웃이 안된다.)


1. connect timeout


Method : public void setConnectTimeout(int timeout)

Parameters : timeout - an int that specifies the connect timeout value in milliseconds

 - 밀리 세컨드 단위로 설정한다. 

 - 타임 아웃이 경과하면 java.net.SocketTimeoutException 발생.

 - 타임 아웃이 0이면 무한 타임 아웃으로 해석.


Method : public int getConnectTimeout()

Returns: an int that indicates the connect timeout value in milliseconds

 - 접속 타임 아웃의 설정을 돌려준다.

 - 0을 반환하면 옵션 비활성화됨을 뜻함. (즉, 무한대 시간 초과).

2. read timeout

Method : public void setReadTimeout(int timeout)

Parameters: timeout - an int that specifies the timeout value to be used in milliseconds

 - 밀리 세컨드 단위로 지정된 타임 아웃으로 설정. 

 - 0이 아닌 값은 입력 스트림에서 읽을 때 초과되는 시간을 지정한다.

 - 타임 아웃 발생시 java.net.SocketTimeoutException가 발생.

 - 타임 아웃이 0이면 무한 타임 아웃으로 해석된다.


Method : public int getReadTimeout()

Returns: an int that indicates the read timeout value in milliseconds

 - read 타임 아웃에 대한 설정 반환.

 - 0 반환하면 옵션 비활성화됨을 뜻함. (즉, 무한대 시간 초과).



▶ 서버 응답 처리하기

int getResponseCode()

public String getResponseMessage()


1) 응답 코드와 응답 메시지를 리턴받을 수 있다.


ex) 성공 케이스

String domain = "http://google.co.kr";

try {

URL u = new URL(domain);

HttpURLConnection con = (HttpURLConnection) u.openConnection();

System.out.println("응답코드 : " + con.getResponseCode());

System.out.println("응답메세지 : " + con.getResponseMessage());

} catch (MalformedURLException e) {

System.out.println(domain+" is not a URL I understand");

} catch (IOException e) {

}


응답코드 : 200

응답메세지 : OK


ex) 페이지 없음

String domain = "http://google.co.kr/GGGGGGGGGGGG";

try {

URL u = new URL(domain);

HttpURLConnection con = (HttpURLConnection) u.openConnection();

System.out.println("응답코드 : " + con.getResponseCode());

System.out.println("응답메세지 : " + con.getResponseMessage());

} catch (MalformedURLException e) {

System.out.println(domain+" is not a URL I understand");

} catch (IOException e) {

}


HTTP 1.0은 16가지의 응답 코드를 정의했고, HTTP 1.1은 이를 확장해서 40가지의 응답 코드를 정의한다.

이 응답 코드 중에서 404와 같이 잘 알려진 코드는 숫자만으로도 충분히 의미를 전달할 수 있지만 많은 다른 코드들은 우리에게 친숙하지 않다.

HttpURLConnection 클래스는 일반적인 응답코드 36개를 HttpURLConnection.OL 또는 HttpURLConnection.NOT_FOUND 같은 상수로 제공한다.



2) 에러 발생시 관련 데이터 Read

public InputStream getErrorStream()

 - 에러 발생 시에 리턴되는 데이터는 getErrorStream() 메서드를 통해 얻을 수 있다.

 - 이 메서드는 에러가 발생하지 않았거나 리턴 데이터가 없는 경우에는 null을 리턴한다.

 - getErrorStream() 메서드는 일반적으로 getInputStream()이 실패한 경우의 catch 블록에서 사용한다.


3) 리다이렉트

 - 응답코드 300번대는 모두 일종의 리다이렉트(방향 재지정)를 의미한다.

 - 300번대 응답 코드를 받은 대부분의 브라우저는 해당 리소스의 새로운 위치로부터 자동으로 요청하여 읽지만 

이 방법은 사용자에게 아무런 통보 없이 신뢰할 수 없는 사이트로 이동시킬 가능성이 있기 때문에 보안 상 문제가 될 가능성이 있다.

그래서 기본적으로는 HttpURLConnection은 리다이렉트를 따라갈지언정 그  여부를 결정할 수 있는 static 메서드가 2개 제공된다.


public static boolean getFollowRedirects()

 - 메서드는 리다이렉트가 허용된 경우 true를, 그렇지 않은 경우에는 false를 리턴

public static void setFollowRedirects(boolean follow)

 - 호출 시 true를 파라미터로 전달하면 리다이렉트를 따라가게 만들고, false를 전달하면 따라가지 않게 만든다.

 - 두 메서드는 static이기 때문에 이 메서드가 호출된 이후에 생성된 모든 HttpURLConnection 객체에 영향을 준다. 

   다만 setFollowRedirects() 메서드는 보안 관리자가 변경을 허가하지 않을 경우 SecurityException 예외를 발생시킨다.

   그래서 자바는 개별 인스턴스 단위로 리다이렉트를 설정하는 다음 두 개의 메서드를 제공한다.

   public boolean getInstanceFollowRedirects()

   public void setInstanceFollowRedirects(boolean followRedirects)

인스턴스의 경우에는 setInstanceFollowRedirects() 메서드가 호출되지 않았다면 setFollowRedirects()에 의해 설정된 동작을 따른다.



▶ 스트리밍


 - HTTP 서버로 전송되는 모든 요청에는 HTTP 헤더가 포함된다.


 - 헤더는 요청 본문보다 먼저 전송되기 때문에 헤더를 작성하기 위해 요청 본문의 길이를 먼저 알아야 하는 경우 문제가 발생한다.

자바는 이 상황을 HttpURLConnection에서 얻은 OutputStream에 쓴 모든 데이터를 스트림이 닫힐 때까지 캐시하는 방법으로 해결한다.

즉 스트림이 닫히는 시점에 요청 본문의 크기를 계산해서 이 정보로 Content-length 헤더를 작성하는 것이다.


결국 매우 긴 폼의 경우 정상 동작 하지 않거나 매우 느리다.


이에 대한 해결책.


1. 전송할 데이터의 크기를 알 수 없는 경우(청크 분할 전송 인코딩 모드)


 - 전송할 데이터의 크기를 알 수 없는 경우에는 청크 분할 전송 인코딩(chunked transfer encoding)을 사용할 수 있다.

 - 청크 분할 전송 인코딩에서 요청 본문은 몇 개의 조각으로 분할되어 전송된다.

 - 각 조각은 자신의 컨텐츠 길이를 가지고 있고, 이 방법을 사용하기 위해서는 URL을 연결하기 전에 setChunkedStreamingMode() 메서드를 호출하면서 

원하는 청크의 크기를 파라미터로 전달해야 한다.


public void setChunkedStreamingMode(int chunkLength)


 - 청크 분할 전송 인코딩은 리다이렉션과 인증이 필요한 사이트에서 문제가 된다.

 - 만약 우리가 청크로 분할된 파일을 리다이렉트된 URL이나 패스워드 인증을 요구하는 사이트에 전송할 경우에는 HttpRetryException이 발생.


2. 요청 데이터의 크기를 미리 알 수 있는 경우(고정 길이 스트리밍 모드)


 - 요청 데이터의 크기를 미리 알 수 있는 경우에는 이 정보를 HttpURLConnection 객체에 전달해서 연결을 최적화할 수 있다.

 - 데이터의 크기를 미리 전달할 경우에 자바는 해당 데이터를 네트워크로 즉시 스트리밍할 수 있기 때문이다.

 - 전송할 데이터의 크기를 인자로 setFixedLengthStreamingMode() 메서드를 호출하면 된다.

 - 실제 전송할 데이터의 크기가 int의 최대 크기보다 큰 경우, 자바 7 이후 버전에서는 long 타입을 대신 사용할 수 있다.


public void setFixedLengthStreamingMode(int contentLength)

public void setFixedLengthStreamingMode(long contentLength) // java 7



반응형

댓글2

  • chanchan 2020.12.13 20:30

    각 리소스 커넥션마다 다른 인스턴스를 사용해야 한다는게 어떤 의미인지 알 수 있을까요 ??
    답글

  • Favicon of https://lima1016.tistory.com BlogIcon 햄리뮤 2021.01.10 15:01 신고

    안녕하세요. 블로거님! 블로거님께서 올려주신 글을 참고하여 티스토리에 개인 공부하는 글을 작성하였습니다!
    출처는 당연히 표시했습니다! 좋은 지식 공유해주셔서 감사합니다!

    답글