2020-11-27 TIL
URI와 URL, URN의 관계

- URI
- URL
- URN
한글 코드가 7bit 네트워크 장비를 지날 때의 문제점
- 데이터에 변형이 발생한다.

URL 인코딩 해결 방법

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: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 방식이 적합하다.