2020-11-27 TIL

6 minute read

URI와 URL, URN의 관계

image-20201130105106148

  • URI
    • URL
    • URN

한글 코드가 7bit 네트워크 장비를 지날 때의 문제점

  • 데이터에 변형이 발생한다.

image-20201130105219950

URL 인코딩 해결 방법

image-20201130105308426

GET 요청 데이터 읽기

  • 웹브라우저에서 웹서버의 자원을 요청하는 방법

    • 1) 서블릿 클래스를 실행하고 싶을 때
    • => 서블릿 클래스의 실제 위치:
      • 톰캣배치폴더/wtpwebapps/eomcs-java-web/WEB-INF/classes/com/eomcs/web/ex04/Servlet01.class
      • => 요청:
      • 해당 서블릿을 서버에 등록할 때 사용한 URL을 지정해야 한다.
      • http://localhost:9999/eomcs-java-web/ex04/s1
    • 2) HTML, CSS, JavaScript, JPEG 등 정적 파일을 받고 싶을 때
    • => 정적 파일의 실제 위치:
      • 톰캣배치폴더/wtpwebapps/eomcs-java-web/ex04/test01.html
      • => 요청:
      • http://localhost:9999/eomcs-java-web/ex04/test01.html
    • 3) /WEB-INF/ 폴더에 있는 정적 파일을 받고 싶을 때
    • =정적 파일의 실제 위치:
    • 톰캣배치폴더/wtpwebapps/eomcs-java-web/WEB-INF/ex04/test01.html
      • => 요청:
      • /WEB-INF 폴더 아래에 있는 파일은 클라이언트에서 요청할 수 없다!
      • 웹 애플리케이션의 정보를 두는 폴더이기 때문이다.
  • HTTP 요청 형식
    • method sp request-URI sp http_version CRLF
    • *(general header request header entity header) CRLF
    • CRLF
    • message-body
  • GET 요청 HTTP 프로토콜 예)
    • => GET 요청은 데이터를 request-URI에 붙여서 보낸다.
    • => request-URI 예)
    • /java-web/ex04/s1?name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20
    • 서블릿 URL : /java-web/ex04/s1
    • 데이터(Query String) : name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20
    • => 데이터 형식
    • 이름=값&이름=값&이름=값

// => URL 인코딩

// - 데이터를 보낼 때 7bit 네트워크 장비를 거치면 8비트 데이터가 깨진다.

// - 이를 방지하고자 보내는 데이터를 7비트로 변환한다.

// - 어떻게? 원래 코드 값을 아스키(ASCII) 문자 코드로 변환한다.

// - ASCII 코드는 7비트이기 때문에 데이터를 주고 받을 때 깨지지 않을 것이다.

// - URL 인코딩이란? 문자 코드의 값을 ASCII 코드화시키는 것이다.

// - 예) “ABC가각”을 전송한다고 가정하자

// “ABC가각”의 문자 코드(UTF-8) 값 : 414243EAB080EAB081

// 7비트 장비를 통과:

// 41 => 0100 0001 => [7비트 장비] => 100 0001 => [8비트로 복원] => 0100 0001

// 42 => 0100 0010 => [7비트 장비] => 100 0001 => [8비트로 복원] => 0100 0010

// 43 => 0100 0011 => [7비트 장비] => 100 0011 => [8비트로 복원] => 0100 0011

// EA => 1110 1010 => [7비트 장비] => 110 1010 => [8비트로 복원] => 0110 1010

// ASCII 문자코드로 변환 :

// => 코드 값이 이미 ASCII 라면 그대로

// 41 ==> 41

// 42 ==> 42

// => 코드 값이 ASCII 가 아니라면 각 4비트 값을 아스키 문자라 간주하고 코드로 변환한다.

// E ==> ‘E’ ==> 45

// A ==> ‘A’ ==> 41

// 이렇게 변경한 후, URL 인코딩 값임을 표시하기 위해 앞에 ‘%’ 코드를 붙인다.

// EA ==> 25 45 41 ==> 사람이 보는 문자로 표현하면 ==> %EA ==>

// %EA 문자를 받은 쪽에서는 원래의 값을 변환(URL 디코딩)한다.

// %EA(3바이트) ==> 1110 1010(1바이트)

//

//

//

// => 예)

/*

* GET /java-web/ex04/s1?name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=20 HTTP/1.1 Host: localhost:8080

* Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1

* User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like

* Gecko) Chrome/73.0.3683.86 Safari/537.36 Accept:

* text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng, Accept-Encoding:

* gzip, deflate, br Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6 빈 줄

*/

// HTTP 응답 프로토콜

// => 형식

// status-line(HTTP프로토콜 상태코드 간단한문구) CRLF

// *(general header response header entity header) CRLF

// CRLF

// message-body

//

// => 예:

/*

* HTTP/1.1 200 OK Content-Type: text/plain;charset=UTF-8 Content-Length: 27 Date: Thu, 28 Mar 2019

* 05:46:08 GMT

CRLF

이름=홍길동

나이=20

*/

// URI (Uniform Resource Identifier)

// => 웹 자원의 위치를 가리키는 식별자

// => 종류

// URL(Uniform Resource Locator)

// scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

// 예) http://localhost:8080/ex04/s1?name=홍길동&age=20

//

// URN(Uniform Resource Name)

// ::= "urn:" ":"

// 예) urn:lex:eu:council:directive:2010-03-09;2010-19-UE

POST 요청 데이터 읽기

// POST 요청

// - 웹 페이지의 폼(method=’POST’ 일 때)에서 전송 버튼을 클릭하면 POST 요청을 보낸다.

//

// 테스트

// - http://localhost:8080/java-web/ex04/test02.html 실행

//

// 웹 브라우저가 보낸 데이터 읽기

// => 데이터를 읽을 때는 GET 요청과 POST 요청이 같다.

// ServletRequest.getParameter(“파라미터이름”)

// => POST 요청으로 보낸 데이터는 기본으로 영어(ISO-8859-1)라고 간주한다.

// 그래서 영어 코드를 UCS2(UTF-16) 문자 코드로 변환한다.

// 예) “ABC가각”을 보낸다고 가정하자.

// 실제 웹 브라우저가 “ABC가각” 문자열을 보낼 때 다음과 같이 UTF-8 코드를 보낸다.

// 41 42 43 EA B0 80 EA B0 81

// 그런데 서블릿에서는 이 코드 값을 ISO-8859-1 코드라고 간주한다.

// 그래서 getParameter()를 호출하여 값을 꺼내면

// 위의 코드를 UTF-16으로 바꿔서 리턴한다.

// 즉 각 바이트에 그냥 00을 붙여 문자열을 만든 후 리턴한다.

// 왜? 영어(ISO-8859-1)를 2바이트 유니코드로 바꿀 때 그냥 앞에 00 1바이트를 붙이면 되기 때문이다.

// 위의 코드를 UTF-16으로 바꾸면 다음과 같다.

// 0041 0042 0043 00EA 00B0 0080 00EA 00B0 0081

// 이렇게 변환된 코드를 화면에 출력하면 다음 폰트로 보인다.

// ABC가각

//

// 클라이언트가 보낸 한글을 읽을 때 깨지는 문제 해결?

// => 다음 코드의 주석을 풀고 테스트 해보라!

// 정상적으로 잘 출력될 것이다.

// => 원리

// getParameter()를 최초로 호출하기 전에 먼저

// 클라이언트 보낸 데이터의 인코딩 형식이 어떤 문자표로 되어 있는지 알려줘야 한다.

// => 주의!

// 반드시 getParamter()를 최초로 호출하기 전이어야 한다.

// 한 번 getParameter()를 호출한 후에는 소용없다.

req.setCharacterEncoding(“UTF-8”);

String age = req.getParameter(“age”);

String name = req.getParameter(“name”);

res.setContentType(“text/plain;charset=UTF-8”);

PrintWriter out = res.getWriter();

out.printf(“이름=%s\n”, name);

out.printf(“나이=%s\n”, age);

out.println(“——————-“);

char[] chars = name.toCharArray();

for (char c : chars) {

out.printf(“%x\n”, (int) c);

}

}

}

// HTTP 요청 형식

// method sp request-URI sp http_version CRLF

// *(general header request header entity header) CRLF

// CRLF

// message-body

//

// POST 요청 HTTP 프로토콜 예)

// => POST 요청은 데이터를 message-body에 붙여서 보낸다.

// => 데이터 형식과 URL 인코딩은 GET 요청과 같다.

// => 예)

/*

* POST /java-web/ex04/s2 HTTP/1.1 Host: localhost:8080 Connection: keep-alive Content-Length: 33

* Pragma: no-cache Cache-Control: no-cache Origin: http://localhost:8080 Upgrade-Insecure-Requests:

* 1 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Macintosh; Intel Mac

* OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36 Accept:

* text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng, Referer:

* http://localhost:8080/java-web/ex04/test02.html Accept-Encoding: gzip, deflate, br

* Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6 빈 줄

* name=ABC%EA%B0%80%EA%B0%81&age=20

*/

// GET 요청 vs POST 요청

// 1) 전송 데이터 용량

// => GET

// - 대부분의 웹서버가 request-line과 헤더의 크기를 8KB로 제한하고 있다.

// - 따라서 긴 게시글과 같은 큰 용량의 데이터를 GET 방식으로 전송할 수 없다.

// => POST

// - HTTP 요청 헤더 다음에 message-body 부분에 데이터를 두기 때문에

// 용량의 제한 없이 웹 서버에 전송할 수 있다.

// - 즉 웹 서버가 제한하지 않는 한 전송 데이터의 크기에 제한이 없다.

// - 웹 서버가 제한한다?

// 특정 사이트에서는 게시글의 크기나 첨부파일의 크기에 제한을 둔다.

// => 용도

// 게시글 조회나 검색어 입력 같은 간단한 데이터 전송에는 GET 요청으로 보내고

// 게시글 등록이나 첨부파일 같은 큰 데이터 전송에는 POST 요청으로 보낸다.

//

// 2) 바이너리 데이터 전송

// => GET

// - request-URI가 텍스트로 되어 있다.

// 따라서 바이너리 데이터를 request-URI에 붙여서 전송할 수 없다.

// - 그럼에도 꼭 GET 요청으로 바이너리 데이터를 보내고자 한다면?

// 바이너리 데이터를 텍스트로 변환하면 된다.

// 예를 들어 바이너리 데이터를 Base64로 인코딩하여 텍스트를 만든 후에

// GET 요청 방식대로 이름=값 으로 보내면 된다.

// - 그래도 결국 용량 제한 때문에 바이너리 데이터를 GET 요청으로 전송하는 것은 바람직하지 않다.

// => POST

// - 이 방식에서도 이름=값 형태로는 바이너리 값을 전송할 수 없다.

// - multipart 형식을 사용하면 바이너리 데이터를 보낼 수 있다.

// - 보통 파일 업로드를 구현할 때 이 multipart 전송 방식으로 사용한다.

//

// 3) 보안

// => GET

// - URL에 전송 데이터가 포함되어 있기 때문에

// 사용자 아이디나 암호 같은 데이터를 GET 방식으로 전송하는 것은 위험하다.

// - 웹 브라우저는 주소 창에 입력한 값을 내부 캐시에 보관해 두기 때문이다.

// - 그러나 게시물 번호 같은 데이터는 URL에 포함되어야 한다.

// 그래야 다른 사람에게 URL과 함께 데이터를 보낼 수 있다.

// => POST

// - mesage-body 부분에 데이터가 있기 때문에

// 웹 브라우저는 캐시에 보관하지 않는다.

// - 또한 주소 창에도 보이지 않는다.

// - 사용자 암호 같은 데이터를 전송할 때는 특히 이 방식으로 보내는 것이 바람직 하다.

// 즉 보내는 데이터를 웹 브라우저의 캐시 메모리에 남기고 싶지 않을 때는 POST 방식을 사용한다.

// - 꺼꾸로 특정 페이지를 조회하는 URL일 경우 POST 방식을 사용하면

// URL에 조회하려는 정보의 번호나 키를 포함할 수 없기 때문에

// 이런 상황에서는 POST 방식이 적절하지 않다.

// 오히려 GET 방식이 적합하다.

Categories:

Updated: