ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • URL과 URI
    프로그래밍/Java 2021. 2. 15. 09:02
    반응형

    호스트는 인터넷이 연결된 컴퓨터이다. 이 호스트는 인터넷 주소(Internet address) 혹은 IP 주소라고 불리는 최소 하나의 고유한 숫자에 의해서 식별된다. 이 호스트에는 HTML 문서나 파일이 있을 수가 있는데 이러한 리소스의 위치를 식별하는데 사용하는 것이 URL(Uniform Resource Locator)이다. URL은 통합 리소스 식별자(URI, Uniform Resource Identifier)의 가장 널리 사용되는 종류 중 하나이다. 

    이제부터 URL 클래스와 URI에 대해서 알아보자.

    URL 클래스만 사용하면 원하는 데이터를 쉽게 얻을 수 있다. 이 URL 클래스는 자바 프로그램이 특정 위치로부터 데이터를 가져오는 가장 쉬운 방법을 제공한다. 이 클래스 내부의 프로토콜이나 서버와 통신하는 방법에 대한 자세한 부분은 신경쓰지 않아도 된다. 

     


    URI

    URI는

    • 문자열
    • 특정 리소스를 가르킴 
      • 서버에 위치한 파일
      • 이메일주소, 책, 사람 이름, 인터넷 호스트, 오라클의 현재 주가 등 
    • 특별한 구문으로 구성

     

    "리소스"라는 것은 서버로부터 받는 모든 것들은 바이트 형태로 존재하는 리소스지만, 이 리소스 자체는 여러가지 의미를 가질 수 있다.

    예를 들면 "김철수"라는 사람 이름도

    • 사람,
    • 누군가의 첫째 아들,
    • 어디 회사에 다니는 개발자 등의 의미를 가질 수 있듯이,

    https://www.un.org/en/documents/udhr/ 이라는 URL은

    • 세계 인권 선언(Universal Declaration of Human Rights)을 식별하는 의미로도 사용되고
    • 다양한 형식, 예를 들면 텍스트, XML, PDF 등을 식별하는 의미가 될 수도 있고
    • 이 선언이 표현된 다양한 언어인 영어, 프랑스어, 아라비어어 등을 식별하는 의미로 사용될 수 있다. 

     

    URI VS URL VS URN?

    https://danielmiessler.com/study/difference-between-uri-url/#:~:text=A%20URI%20is%20an%20identifier,as%20HTTPs%20%2C%20FTP%20%2C%20etc.&text=If%20the%20protocol%20(%20https%20%2C%20ftp,though%20it's%20also%20a%20URI.

     

    URI(Uniform Resource Identifier) URL(Uniform Resource Locator) URN(Uniform Resource Name)
    리소스를 가르킨다.
    특정 문서의 이름이 될 수도 있고
    위치가 될 수도 혹은 둘 다 일 수 있다.
     
    리소스에 어떻게 도달할지에 대한 주소를 가르킨다. 특정 문서에 도달하기 위한 위치를 정확히 나타낸다.  리소스의 이름을 의미한다. URI, URL보다 나중에 나온 개념으로 위치 변동에 관계 없이 특정 문서를 지칭하기 위해 사용한다. 

    이름 : ISBN 0-486-27557-4
    주소 : http://google.com

    주소 : http://google.com 이름 : ISBN 0-486-27557-4

     

    URI vs URL

    웹 상에서는 URL은 서버상의 리소스 위치를 나타내는 웹 주소라고 할 수 있다. 

    URL은 URI의 타입 중 하나라고 볼 수 있다. 

    단, URI는 웹 주소에 국한되지 않고 사람, 장소, 개념 혹은 모든 것을 식별하는데 사용한다.

    예를 들면, XML의 namespace의 URI인 xmlns:android="http://schemas.android.com/apk/res/android” 와 같이 실제 웹사이트는 아니지만 android 라는 prefix(ex. android:layout_width="wrap_content")를 사용하기 위한 식별자로 쓰이기도 한다. 

    따라서 URL = URI이지만 URI  URL이 아니다. 

     

    URI 구문

    URI의 구문은 스킴(scheme)스킴에 따라 달라지는 부분(scheme-specific-part)으로 구성되며 이 둘은 콜론에 의해 구분된다.

    • 스킴:스킴에 따라 달라지는 부분
    • 널리 쓰이는 유명한 스킴 
      • data 링크에 직접 포함된 Base64로 인코딩된 데이터; RFC 2397 참조
      • file 로컬 디스크에 있는 파일
      • ftp FTP 서버
      • http HTTP(Hypertext Transfer Protocol)를 사용하는 WWW(World Wide Web) 서버
      • mailto 메일 주소
      • magnet BitTorrent 같은 P2P(peer-to-peer) 네트워크를 통해 다운로드 가능한 리소스
      • telnet 텔넷 기반 서비스 연결
      • urn 통합 리소스 이름

    모든 URI에서 스킴에 따라 달라지는 부분(scheme-specific-part)에 적용되는 구체적인 구문은 없지만 다음과 같은 계층적인 구조를 많이 사용한다. 

    //콘텐츠 제공자 또는 기관/경로?쿼리

    URI에서 기관(autority) 부분은 URI 주소의 나머지 부분, 즉 경로와 쿼리에 대한 서비스 책임이 있는 기관을 말한다. 예를 들면

    http://www.ietf.org/rfc/rfc3986.txt URI는

    http 스킴
    www.ietf.org  기관
    /rfc/rfc3986.txt 경로 (슬래시포함)
      쿼리부분 미포함 

     

    http://www.powells.com/cgi-bin/biblio?inkey=62-1565928709-0 URI는

    http 스킴
    www.powells.com 기관
    /cgi-bin/biblio  경로
    inkey=62-1565928709-0 쿼리

     

    urn:isbn:156592870 URI는

    urn 스킴
      스킴 외 부분은 일반 형식과 다르다.

     

    현대의 URI은 기관(authority)을 호스트(서버의 이름)으로 사용한다. ex) www.naver.com, yahoo.co.jp  경로는 이 기관이 리소스를 식별하기 위해 사용한다. 여기서 기관이 다르면 같은 경로라도 다른 리소스를 참조하게 된다. 예를 들면, /index.html 경로는 기관이 www.naver.com인 경우와 www.kakao.com 인 경우는 분명 다른 리소스이다. 


    URL

    URL은 URI의 의미인 리소스 식별 외에도 클라이언트가 해당 리소스를 찾을 수 있는 네트워크 상의 위치도 함께 제공하는 URI이다. 일부 URI는 해당 리소스가 무엇인지 말해주기는 하지만 실제로 해당 리소스의 위치나 얻는 방법을 알려주지는 않는다. 

    예를 들면, "해리포터와 죽음의 성물"의 책 제목(URI)과 이 책의 도서관 위치를 나타내는 "312방, 28열, 7선반"(URI, URL)의 차이라고 할 수 있다. 자바 java.net.URI 클래스는 리소스를 식별하는 용도로만 사용되지만 java.net.URL 클래스는 리소스 식별 외에도 리소스를 획득하는 목적으로도 사용된다는 차이가 있다. 

    URL에 표현된 네트워크의 위치는 일반적으로 해당 서버를 접근하는데 사용되는 프로토콜(ex. FTP, HTTP)과 서버의 호스트네임 또는 IP주소 그리고 서버에서 해당 리소스의 경로를 포함한다. 일반적으로 URL은 http://www.ibiblio.org/javafaq/javatutorial.html같이 표현된다. 이 URL은 www.ibilio.org  서버의 javafaq 디렉터리 안에 javatutorial.html으로 이름이 붙은 파일이 존재한다는 것과 이 파일을 HTTP 프로토콜을 사용하여 접근할 수 있음을 나타낸다. 

     

    URL 구문

    URL은 다음과 같은 구문을 사용한다.

    프로토콜://사용자 정보@호스트:포트/경로?쿼리#부위

    여기서 프로토콜은 URI 스킴의 또 다른 이름이다. (스킴은 URI RFC에서 사용된 단어이고 프로토콜은 자바 표준 문서에서 사용된 단어이다.) URL에서 프로토콜 부분은 file, ftp, http, https, magnet, telnet 등이 될 수 있다. 

    Q. URI의 스킴은 항상 프로토콜 문자열이 오나? A. 항상 프로토콜이진 않다! 

    While most URI schemes were originally designed to be used with a particular protocol, and often have the same name, they are semantically different from protocols. For example, the scheme http is generally used for interacting with web resources using HTTP, but the scheme file has no protocol.

    URL에서 호스트 부분은 여러분이 찾는 리소스를 제공하는 서버의 이름이다. 이 호스트는 www.oreilly.com  또는 utopia.poly.edu 같은 호스트네임이거나 204.148.40.9 또는 128.238.3.21 같은 IP 주소일 수 있다. 

    사용자 정보는 해당 서버에 로그인이 필요할 경우 부가적으로 사용되는 로그인 정보이며 사용자 이름 혹은 사용자 이름과 패스워드로 구성된다. 

    포트번호 역시 필요한 경우 부가적으로 제공되며, 기본 포트(HTTP 80)에서 제공될 경우 생략이 가능하다. 

    사용자 정보, 호스트 그리고 포트모두 결합한 문자열을 기관으로 간주한다. 

    경로는 명시된 서버상의 특정 리소스를 가르키며, 종종 /forum/index.php와 같이 파일 시스템상의 경로처럼 보이기도 한다. 그러나 경로는 서버 파일 시스템상의 실제 경로와 연결되는 경우도 있지만 아닌 경우도 있다. URL상의 경로가 파일 시스템상의 경로와 연결되는 경우, 파일 시스템 전체의 루트가 아닌 서비스를 제공하는 웹 서버의 루트로부터 연결되는 경로이다. 

    쿼리 문자열은(query string) 서버에 추가적인 정보를 제공하는데 사용된다. 일반적으로 서버 상에서 실행중인 프로그램에 인자를 자공하기 위한 폼 데이터(form data)로 http URL에서만 사용된다. 

    마지막으로 부위(fragment)리소스의 특정 부분을 참고하는데 사용된다. 예를 들면,

    <h3 id="xtocid1902914">Comments</h3>

    라는 태그가 있다면, 

    http://www.cafeaulait.org/javafaq.html#xtocid1902914 의 URL을 이용하여 해당 위치를 가르킬 수 있다.


    상대 URL

    URL은 웹 브라우저에게 해당 문서에 대한 정보를 많이 알려주는 역할을 한다. 문서를 가져오는데 필요한 프로토콜, 문서가 위치한 호스트, 호스트상에서 문서의 경로. 하나의 문서에서 다른 문서를 참조할 때 이 URL은 동일할 수 있다. 이 때 참조하는 URL은 완전한 형식이 아닌 생략된 형식으로 사용한다. (호스트네임, 프로토콜 등을 부모 문서로부터 상속받아 사용한다고 말함) 

    이때 완전한 형식을 갖춘 URL을 절대적인 URL이라고 부르고 생략된 형식을 갖춘 URL을 상대적인 URL이라고 한다. 

    예를 하나 들어보자. http://www.ibiblio.org/javafaq/javatutorial.html 의 페이지를 방문해서 아래 하이퍼링크를 클릭한다고 해보자.

    <a href="javafaq.html">

    브라우저는 원본 문서의 URL의 끝에서 javatutorial.html 부분을 잘라낸다. 그리고 http://www.ibiblio.org/javafaq/javafaq.html 로 원본 문서 URL의 끝에 연결되어 읽혀진다. 

    반면 상대적 링크가 /로 시작하는 경우, 문서의 최상위 경로의 링크가 된다. 예를 들면

    <a href="/projects/ipv6/">

    위 링크를 클릭할 경우, 브라우저는 http://www.ibiblio.org/projects/ipv6 의 URL 을 만든다. 

    상대적 URL을 사용하면 타이핑 횟수를 줄일 수 있고, 단일 문서로 다양한 프로토콜에서 서비스 제공이 가능하며, 사이트 전체의 이동이나 복사시에 문서 내부 링크를 따로 변경할 필요가 없다는 점이다. 


    URL 클래스

    java.net.URL 클래스는 http://www.locates.com/이나 ftp://ftp.redhat.com/pub/와 같은 URL 을 추상화한 것이다. Object를 상속했고 final 클래스이다. 또한 immutable한 클래스이기 때문에 생성된 후에는 객체의 필드를 변경할 수 없다. 

     


    URL 클래스 만들기

    public URL(String url) throws MalformaedURLException
    public URL(String protocol, String hostname, String file) throws MalformaedURLException
    public URL(String protocol, String host, int port, String file) throws MalformaedURLException
    public URL(URL base, String relative) throws MalformaedURLException

    어떤 생성자를 사용할지는 상황에 따라 달라진다. 이 모든 생성자는 지원되지 않는 프로토콜의 URL을 생성하려할 때, MalformaedURLException을 발생시킨다.

    지원되는 프로토콜은 자바 가상머신에 따라 다르다. 모든 가상머신에서는 http, file을 지원하며 오늘날 자바는 https, jar, ftp도 지원한다. 

     

    문자열을 이용하여 URL 생성하기

    URL 클래스의 제일 간단한 생성자의 경우 단일 인자로 생성한다. 

    try {
       URL u = new URL("http://www.audubon.org/");
    } catch (MalformedURLException ex) {
    }
    

     

    지원되지 않는 프로토콜인지 확인해보기

    import java.net.MalformedURLException;
    import java.net.URL;
    
    class UrlTest {
        public static void main(String[] args) {
            // 하이퍼텍스트 전송 프로토콜
            testProtocol("http://www.adc.org");
    
            // 보안 http
            testProtocol("https://www.amazon.com/exec/obidos/order2/");
    
            // 파일 전송 프로토콜
            testProtocol("ftp://ibilio.org/pub/languages/java/javafaq/");
    
            // 간이 메일 전송 프로토콜
            testProtocol("mailto:elhro@ibiblio.org");
    
            // 텔넷
            testProtocol("telnet://dibner.poly.edu/");
    
            // 로컬 파일 접근
            testProtocol("file:///etc/passwd");
    
            // gopher
            testProtocol("gopher://gopher.anc.org.za/");
    
            // LDAP
            testProtocol("ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress");
    
            // JAR
            testProtocol("jar:http://cafeaulait.org/books/javaio/ioexamples/javaio.jar!" + "/com/macfaq/iio/StreamCopier.class");
    
            // NFS, 네트워크 파일 시스템
            testProtocol("nfs://utopia.poly.edu/usr/tmp/");
    
            // JDBC를 위한 사용자 정의 프로토콜
            testProtocol("jdbc:mysql://luna.ibilio.org:3306/NEWS");
    
            // rmi, 원격 메소드 호출을 위한 사용자 정의 프로토콜
            testProtocol("rmi://ibiblio.org/RenderEngine");
    
            // HotJava를 위한 사용자 정의 프로토콜
            testProtocol("doc:/UserGuide/release.html");
            testProtocol("netdoc:/UserGuide/release.html");
            testProtocol("systemresource://www.adb.org/+/index.html");
            testProtocol("verbtim:http://www.adc.org/");
        }
    
        private static void testProtocol(String url) {
            try {
                URL u = new URL(url);
                System.out.println(u.getProtocol() + " is supported");
            } catch (MalformedURLException ex) {
                String protocol = url.substring(0, url.indexOf(":"));
                System.out.println(protocol + " is not supported");
            }
        }
    }
    

     

    맥 OS에서 Java11으로 실행해보았다. 

    http is supported
    https is supported
    ftp is supported
    mailto is supported
    telnet is not supported
    file is supported
    gopher is not supported
    ldap is not supported
    jar is supported
    nfs is not supported
    jdbc is not supported
    rmi is not supported
    doc is not supported
    netdoc is not supported
    systemresource is not supported
    verbtim is not supported

    책에서는 맥 + Java 7으로 netdoc이 지원된다고 나오는데 직접 실행해보니 지원이 안된다고 나온다. 

    Pixel5에서는 mailto가 지원이 안된다고 나온다. 

     

    URL에 포트를 지정해보기

    try {
        URL u = new URL("http", "fourier.dur.ac.uk", 8000, "/~dma3mjh/jsci/");
    }catch (MalformedURLException ex) {
    }

     

    상대적인 URL 생성하기

    public URL(URL base, String relative) throws MalformaedURLException

    위 생성자를 이용하면 base URL 객체를 알고 있다면, 상대적 주소를 만들 수 있다. 

    try {
        URL u1 = new URL("http://www.adc.org/javafaq/index.html");
        URL u2 = new URL(u, "mailinglists.html");
    }catch (MalformedURLException ex) {
    }

    u1의 경로에 파일이름이 제거되고 "mailinglists.html"이 사용된다. 이 생성자는 가튼 디텍터리 안에 전체 파일 목록에 대한 루프를 처리할 때 유용하다. 

     

    URL 객체를 생성하는 다른 방법들

    java.io.File 클래스는 인자로 전달된 파일과 매칭되는 file URL을 반환하는 toURL() 메소드를 제공한다. 이 메소드에 의해 반환되는 URL의 정확한 형식은 플랫폼에 따라 차이가 있다. 예를 들면 윈도우의 경우 file:/D:/JAVA/JNP4/ToURLTest.java와 같은 형식으로 반환한다. 그러나 리눅스와 유닉스 계역에서는 file:/home/elharo/books/JNP405/ToURLTest.java와 같은 형식으로 반환한다. 실제로도 file URL은 플랫폼과 프로그램에 매우 의존적이다. 종종 웹브라우저나 다른 프래그램의 URL과 교환해서 사용할 수 없다.

    클래스 로더는 클래스 로딩 뿐만 아니라 이미지나 오디오 파일 같은 리소스를 로딩하는데도 사용된다. ClassLoader.getSystemResource(String name) 이라는 메소드를 제공하는데 리소스의 URL을 반환한다. 리소스의 경로는 file:/com/macfaq/sounds/swale.jpg와 같은 슬래시로 된 자바 패키지 이름과 같은 구성이다. 자바 가상 머신은 요청된 리소스를 클래스 경로에서 찾으려고 할 것이다. 


    URL에서 데이터 가져오기

    URL 인스턴스로부터 데이터 가져오는 연습을 해보자.

    public InputStream openStream() throws IOException
    public URLConnection openConnection() throws IOException
    public URLConnection openConnection(Proxy proxy) throws IOException
    public Object getContent() throws IOException
    public Object getContent(Class[] classes) throws IOException
    • openStream() : 데이터를 읽어올 수 있는 InputStream을 반환한다.
    • openConnection() : 그 밖에 데이터 이외에 Response 헤더 등에 접근하려고 할 때 URLConnection을 이용.
    • getContent() : 콘텐츠를 요청할 수 있다. String이나 Image 혹은 InputStream을 반환하기도 한다. 

     

    public final InputStream openStream() throws IOException

    openStream() 메소드는 URL에 의해 참조된 리소스에 연결하고 데이터를 읽을 수 있는 InputStream을 반환한다. 반환된 InpuStream은  URL이 참조하는 HTML 파일 등의 원본 데이터이다. 

    finally 블록 안에 스트림의 안정적인 종료를 호출해줘야 한다. 

    InputStream in = null;
    try {
        URL u = new URL("http://www.lolcats.com");
        in = u.openStream();
        int c;
        while ((c = in.read()) != -1) System.out.write(c);
    } catch (IOException ex) {
    
    } finally {
        try {
            if (in != null) {
                in.close();
            }
        } catch (IOException ex) {
    
        }
    }
    
    // Java7 try-with-resrouces 구문 
    try {
        URL u = new URL("http://www.lolcats.com");
        try (InputStream in = u.openStream()) {
            int c;
            while ((c = in.read()) != -1) System.out.write(c);
        }
    } catch (IOException ex) {
    }

    위 URL 클래스에 넘겨진 URL은 가리키는 대상이 텍스트, GIF, JPEG 이미지 등 뭐든 될 수 있다. 

     

    public URLConnection openConnection() throws IOException

    소켓 통신을 하기 위해서는 클라이언트와 서버 프로그램 모두 소켓이 생성되어 있어야한다. openConnection() 메소드는 지정된 URL에 대한 소켓을 열고 URLConnection 객체를 반환한다. URLConnection은 네트워크 리소스에 대한 열린 연결을 의미한다. 예를 들면, HTTP/S 스킴인 경우 헤더에 접근이 가능하다.

    Map<String, List<String>> fields = uc.getHeaderFields();
    for (String key : fields.keySet()) {
      Sytem.out.println(key + ", " + fields.get(key));
    }

     

    public final Object getContent() throws IOException

    getContent() 메소드는 URL이 참조하는 데이터를 다운로드 받는 세 번째 방법이다. 

    try {
        URL u = new URL("https://ancient-ridge-47919.herokuapp.com/test");
        try {
            Log.d("TAG", "u.getContent();="+u.getContent().toString());
        } catch (IOException ex) {
        }
    } catch (MalformedURLException ex) {
    }

    URL이 텍스트 종류를 참조하는 경우 InputStream을 참조하고 GIF, JPEG 같은 경우는 java.awt.ImageProducer 객체를 반환한다.(책)

    가상머신에 따라 적절한 Content type으로 변환해주는 것 같다. 

    • Mac
      • image : sun.awt.image.URLImageSource
      • video : sun.net.www.protocol.http.HttpURLConnection$HttpInputStream 
    • Pixel5
      • image/video : com.android.okhttp.internal.http.Http1xStream$FixedLengthSource@8ee003b).inputStream() 

    이 것은 getContent() 메소드의 가장 큰 문제점이다. 어떤 종류의 객체가 반환될지 예측하기 어렵다는 것이다. InputStream의 한 종류나 ImageProducer 등이 반환될 수 있다. instanceof를 사용하거나 InputStream을 받을 수 있는 openConnection()을 이용하자.

     


    URI 클래스

    URI는 URL을 일반화(generalization)시킨 것이며 URL뿐만 아니라 URN(Uniform Resource Name)도 함께 포함한다. 실제로 사용되는 대부분의 URI는 URL 이다. 그러나 XML과 같은 대부분의 스펙(specification)과 표준은 URI의 관점에서 정의된다. java.net.URI 클래스는 다음 세가지 관점에서 URL 클래스와 구분된다. 

    • URI 클래스는 순수하게 리소스를 식별하고 URI를 분석하는 기능만 제공된다. URI가 참조하는 리소스를 가져오는 메소드는 제공하지 않는다. 
    • URI 클래스는 URL 클래스보다 스펙 및 표준 사항을 더 잘 따른다.
    • URI 객체는 상대적인 URI를 표현할 수 있다. 반면 URL 클래스는 URI를 저장하기 전에 절대적 URL 형태로 변경한다.

    즉, URL 객체는 네트워크 전송을 위한 애플리케이션 계층 프로토콜을 표현하는 객체인 반면, URI객체는 순수하게 문자열 분석과 조작을 위한 객체이다. URI 클래스는 네트워크 전송을 위한 메소드가 없다. 

    URL이 참조하는 콘텐츠를 주고 받을 경우 URL 클래스를 사용하고 전송보다는 리소스를 식별할 목적으로 사용할 경우 URI 클래스를 사용한다. URI와 URL이 둘 다 필요한 경우에는 URI의 toURL(), URL의 toURI()로 상호변환 가능하다.


    URI 생성하기

    URI 객체는 문자열로부터 만든다. 

    public URI(Sring uri) throws URISyntaxException
    public URI(String scheme, String schemeSpecificPart, String fragment) throws URISyntaxException
    public URI(String scheme, String host, String path, String fragment) throws URISyntaxException
    public URI(String scheme, String authority, String path, String query, String fragment) throws URISyntaxException
    public URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment) throws URISyntaxException

    URL 클래스와 달리 URI 클래스는 내장된 프로토콜 핸들러의 영향을 받지 않는다.

    URI today = new URI("http", "www.ibilio.org", "/javafaq/index.html", "today");
    // 사용 
    public URI(Sring uri) throws URISyntaxException
    
    URI voice = new URI("tel:+1-800-9988-9938");
    URI web = new URI("http://www.xml.com/pub/a/2003/09/17/stax.html#id_hbc");
    URI book = new URI("urn:isbn:1-565-92870-9");

    인자로 사용된 문자열이 URI 구문 규칙을 따르지 않는경우(URI("://abc")로 생성) 생성자는 예외를 발생시킨다, 

    // 사용
    public URI(String scheme, String schemeSpecificPart, String fragment) throws URISyntaxException
    
    URI absolute = new URI("http", "//www.ibilio.org", "today"); // toURL() : http://www.ibilio.org#today
    URI relative = new URI(null, "/javafaq/index.html", "today"); // URL : 정상적인 계층구조가 아님 IllegalArgumentException: URI is not absolute

    스킴을 제외한 인자를 받는 부분은 각 프로토콜에 따라 다르다. http URL, mailto URL, tel URL 등 일 때 각각 다르게 적용될 수 있다.

    // 사용
    public URI(String scheme, String authority, String path, String query, String fragment) throws URISyntaxException
    
    URI today = new URI("http", "www.ibilio.org", "/javafaq/index.html", "referrer=cnet&data=2014-02-23", "today"); // toURL() : http://www.ibilio.org/javafaq/index.html?referrer=cnet&data=2014-02-23#today

    호스트, 경로, 쿼리 문자열, 부위지정자(anchor)을 추가할 수 있다.


    상대 URI 변환하기

    URI 클래스는 상대적인 URI와 절대적인 URI를 서로 변환하기 위한 메소드를 제공한다.

    public URI resolve(URI uri)
    public URI resolve(String uri)
    public URI relativize(URI uri)

    resolve() 메소드로 완전한 경로를 만들기 위해 인자로 URI를 전달한다. 

    URI absolute = new URI("http://www.example.com/");
    URI relative = new URI("images/logo.png");
    URI resolved = absolute.resolve(relative);

    위 코드를 실행하게 되면 "http://www.example.com/images/logo.png" 라는 완전한 URI를 가지게 된다.

    위코드에서 절대적인 URI가 아니더라도 상대적인 URI를 생성할 수 있다.

    URI top = new URI("javafaq/books/");
    URI resolved = top.resolve("jnp3/examples/07/index.html");

     

    "javafaq/books/jnp3/examples/07/index.html" 라는 상대적인 URI를 가지게 된다.

    이 과정을 반대로 실행할 수도 있다. 즉, 절대적인 URI로부터 상대적인 URI를 구해낼 수 있다. 

    URI absolute = new URI("http://www.example.com/images/logo.png");
    URI top = new URI("http://www.example.com");
    URI relative = top.relativize(absolute);

    relative는 relatives() 메소드를 이용해 "/images/logo.png" 라는 상대적인 URI를 갖게 된다. 


    URL Encoding

    웹 URL과 운영체제 사이의 차이점을 처리해야하는 문제가 있었다.

    • 파일 이름에 공백이 허용되거나 되지 않는 시스템들이 있었다. 
    • 웹이 발명되었을 당시에는 유니코드가 널리 사용되지 않았기 때문에 한자어 같은 문자를 처리할 수 없는 시스템이 많았다. 

    따라서 웹 설계자들은 최초의 URL에 아스키코드만 사용되도록 한정했다. 

    따라서 URL을 사용할 땐, 아스키코드(ASCII)의 문자 집합으로 URL을 변환해야한다. 아스키코드는 0~127까지 숫자로 표현 가능한 문자열들이다. 아스키 코드로 변환 가능한 문자 집합은 다음과 같다. 

    • 대문자 A-Z
    • 소문자 a-z
    • 숫자 0-9
    • 문장부호문자 -_.!~*',

    그렇다면 다국어인 한자/한글이나 "/&?@#;$+=%"와 같은 비아스키코드들은 어떻게 작성해야할까? 여기서 유니코드라는 것이 있는데, 유니코드는 아스키코드의 문자 집합보다 더 많은 문자를 표현할 수 있는 문자열 세트이다. 옛날엔 시스템들이 아스키코드나 국가 개별적인 아스키 확장 문자를 사용했지만 현재는 유니코드를 표준으로 사용하고 있다. 

    따라서 비아스키코드 문자들이 사용된 경로나 쿼리 문자열의 일부로 사용될 경우 해당 문자나 문자열 전체를 유니코드 알고리즘으로 인코딩해야한다. 

    URL 문자열 인코딩은 매우 단순하다. 아스키 문자 이외의 모든 문자들은 바이트로 변환되고 변환된 각 바이트는 %기호와 두 개의 아스키코드의 16진수로 표기가 된다.(아스키 문자 허용, 그 외엔 %와 두 개의 16진수, RFC 3986) 퍼센트 인코딩 방식으로 % 사이에 두 개의 16진수를 표기한다. 이때 유니코드 인코딩 알고리즘인 UTF-8을 보편적으로 사용한다. 

    "위키백과"를 UTF-8로 인코딩하면 다음과 같다. 

     

    URLEncoder 클래스

    URLEncoder 클래스를 이용해서 UTF-8 방식으로 URL을 인코딩할 수 있다. 

    try {
        String url = URLEncoder.encode("안녕하세요","UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    "%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94" 로 인코딩 된다. 

    여러 쿼리를 넣어보자. 

    try {
        String query = URLEncoder.encode("https://www.google.com/search?hl=en&as_q=Java&as_epq=I/O", "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    그런데 다음과 같이 인코딩이 된다. "https%3A%2F%2Fwww.google.com%2Fsearch%3Fhl%3Den%26as_q%3DJava%26as_epq%3DI%2FO"

    클래스 내부적으로 쿼리 부분만 따로 파싱이 되지 않기때문에 모두 인코딩이 되어버린다. 

    try {
      String url = "http://www.google.com/search?";
      url += URLEncoder.encode("hl", "UTF-8");
      url+="=";
      url+=URLEncoder.encode("en", "UTF-8");
      url+="&";
      url+=URLEncoder.encode("as_q", "UTF-8");
      url+="=";
      url+=URLEncoder.encode("Java", "UTF-8");
      url+="&";
      url+=URLEncoder.encode("as_epq", "UTF-8");
      url+="=";
      url+=URLEncoder.encode("I/O", "UTF-8");
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }

    이런식으로 실행해야 원하는 결과인 http://www.google.com/search?hl=en&as_q=Java&as_epq=I%2FO을 얻을수 있다.

     

    URLDecoder 클래스

    인코딩된 문자열을 디코딩하는 URLDecoder 클래스가 있다. 

    try {
        String encodedUrl = URLEncoder.encode("안녕하세요","UTF-8");
        String decodedUrl = URLDecoder.decode(url, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    decodedUrl을 출력하면 "안녕하세요"가 출력된다. 

     


     

     

    URI - en.wikipedia.org/wiki/Uniform_Resource_Identifier

    퍼센트 인코딩 - ko.wikipedia.org/wiki/%ED%8D%BC%EC%84%BC%ED%8A%B8_%EC%9D%B8%EC%BD%94%EB%94%A9#:~:text=%ED%8D%BC%EC%84%BC%ED%8A%B8%20%EC%9D%B8%EC%BD%94%EB%94%A9(percent%2Dencoding),%EC%A7%84%EC%88%98%20%EA%B0%92%EC%9C%BC%EB%A1%9C%20%EC%9D%B8%EC%BD%94%EB%94%A9%ED%95%9C%EB%8B%A4.

    유니코드 보편적 사용 - doc.kldp.org/Translations/html/UTF8-Unicode-KLDP/UTF8-Unicode-KLDP-9.html

    UTF-8 인코딩 - norux.me/31

    URI Wikipedia - en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax

     

    반응형

    '프로그래밍 > Java' 카테고리의 다른 글

    보안 소켓  (0) 2021.04.04
    가비지 컬렉션 (Garbage collection)  (0) 2020.10.19
    함수형 프로그래밍 설계 -1  (0) 2017.06.29
    Java Collection  (0) 2015.03.21
    오버로딩(method overloading) vs 오버라이딩(overriding)  (0) 2014.07.30
Designed by Tistory.