이번 포스팅 내용 : http protocol 의 요청 Request / 응답 Response이 어떤 구조로, 어떻게 주고 받아지는지, 이론 공부, 그리고 직접 server 만들어서 요청, 응답을 분석해보기
HTTP
[ Hypertext Transfer Protocol ]
인터넷에서 가장 많이 사용되는 프로토콜 (통신 규약)
- Http 는 http Client 가 http server로 요청을 보내고 요청을 받은 server는 client에게 응답을 보내는 형태로 이루어져 있다.
http 통신 흐름
1. User가 브라우저에서 URL을 issues.
2. 브라우저가 Request 요청 메세지를 서버에게 보낸다.
3. 서버가 받은 URL을 document directory로 파일 or 프로그램으로 만든다(그린다).
4. 서버가 Response 응답 메세지를 브라우저에게 보낸다.
5. 브라우저가 서버로부터 받은 응답을 formats 하여 display 한다.
Http Request Message ( 요청 메세지 ) 이론
-> Request line *1) request method *2) URI 값 / http 버전
-> *3) Header
-> Header 와 Body 구분 Line ! (must)
-> Body (Optional)
*1 ) Request methods or Verbs 종류
HTTP 1.0 & HTTP 1.1 | |
GET (most common) | Client 가 server에서 웹 리소스를 받아올 때 사용 |
POST (most common) | Web server에 데이터를 보낼 때 사용 |
HEAD | HEAD를 사용하면 GET 을 통해 받아오는 Response의 Header를 받아올 수 있음 |
HTTP 1.1 | |
PUT | server에 데이터 저장을 요청할 때 사용 |
DELETE | server에 데이터 삭제를 요청할 때 사용 |
TRACE | 수행 작업의 diagnostic trace를 요청할 때 사용 |
OPTIONS | 지원하는 request 목록을 받아올 때 사용 |
CONNECT | 프록시에게 다른 호스트에게 연결하고 콘텐츠를 분석하거나 캐시 없이 응답하도록 지시하는데 사용 |
* 2 ) Request URL 분석
* 3) HTTP - headers
하나의 header 는 여러개의 values 를 가질 수 있고 이들은 ' , ' 컴마로 구분된다. 수 많은 headers 들이 있고 이들과 친숙해지는건 크게 중요한건 아니다. headers는 대부분 web server와 client에게 맞는 default값으로 되어 있다. 예를 들면 connection header는 보내질필요가 없는게 server에 의해 keep-alive를 default behaviour로 생각하기 때문이다.
원래의 http protocol은 non persistent connections ex) 요청하고 -> 응답받고 -> 연결 끊기.
-> 시간 자원낭비의 이유로 새로 생긴 것이 connection:keep-alive header
in HTTP v1.1 에서는 dfault mode : persistnet connections
이제 Client 쪽에서 server에게 '연결 끊을게~' by using the header conneciton:close 원할 때 연결 끊기 가능
Keep-alive 탄생 이유, 사용법
https://blog.stackpath.com/glossary-keep-alive/
Keep-alive 적정 시간
A value between 7 to 10 seconds is usually ideal. With higher traffic this value can go extremely higher to make sure there is no frequent TCP connection re-initiated. If this value goes down too much, Keep-Alive loses it’s purpose!
Keep-alive 장점
1. Reduced CPU Usage ↓
2. Web page speed ↑
3. HTTPS: HTTPS connections (content over Secure Socket Layer) are very resource intensive. So it is highly recommended to enable Keep-Alive for HTTPS connections and maybe spice it a bit with HTTP/2.
Request Header 의미 알아보기
Request Header | |
Host | 도메인 이름 |
Accept | Client가 다룰 수 있는 MINE 타입 열거 |
Accept-Language | Client가 다룰 수 있고 선호하는 언어 타입 열거 |
Accept-Charset | Client가 다룰 수 있고 선호하는 문자 집합 열거 ex) utf8, BIG5, ISO-8859-2.. |
Accept-Encoding | Client가 지원하는 Encoding 타입 열거 ex) gzip, deflate .. |
Connection | Request 후 연결을 닫을 것인지 유지할 것인지 여부 ex) Close, Keep-Alive |
User-Agent | Request가 만들어진 브라우저 타입 |
Content-Length | POST Request를 사용할 때 Request Body data 길이 |
Content-Type | POST Request를 사용할 때 Request Body data format 을 MIME 타입으로 나타냄 |
Http Response Message ( 응답 메세지 ) 이론
<- * 1) Status code, status msg
<- * 2) Response Header
<- Header와 Body 사이의 line (must)
<- Body
- 첫번째 줄 : 필수 ( http version, response code(200), description (OK) )
- 그 이외에 줄들 : optional
* 1) Reponse Status codes
- 1xx - 100번 대 - informational 서버의 상태 정보
- 2xx - 200번 대 - Successful 🎉🎉 성공 !
- 3xx - 300번 대 - Multiple Choice Request를 처리 위해 다른 액션이 필요함
- 4xx - 400번 대 - Client Error ☹ Request에 문법 오류가 있어나 이해를 못함
- 5xx - 500번 대 - Server Error ☹ Server에서 Request를 처리 못함.
* 2) Response Header
Headers 는 첫번째 줄 제외 모두 optional 이다.
Response , Request Header 모두 Client와 Web server 사이의 추가적인 정보를 전달한다.
Header는 Web server(Response headers) or browser (Request headers) 에 의해 자동적으로 생긴다. 그리고 숨겨져 있기 때문에 보통 개발자들이 신경쓰지 않는다. 그러나 web developers and IOT developers 는 header에 대해 이해도가 있어야하며 수동으로 설정할 줄 알아야 한다. 이를 위한 가장 많이 쓰이는 tool은 'curl utility. Google Vhrome, Firefox의 extensions도 requrest headers설정을 할 수 있게 한다.
Response Header 의미 알아보기
Response Header | |
Location | Request 완료 또는 새 리소스 식별을 위해 요청된 URI가 아닌 위치로 Redirect 위해 사용 |
Server | Request 처리를 위한 server의 소프트웨어 정보 |
WWW-Authenticate | Request-URI에 적용될 수 있는 인증체계와 매개변수 정보 |
Allow | Request-URI에서 지원하는 Method 열거 |
Content-Encoding | Body에 포함된 컨텐츠의 인코딩 형태를 알려줌 ex) Content-Encoding:gzip |
Content-Type | Content의 미디어 타입 알려줌 ex) Content-Type:text/html; charset=ISO-8859-4 |
Expires | Content가 더이상 유효하지 않을 수 있는 date/time 제공 ex) Expires:Fri, 16 Oct 2020 04:33 |
Last-Modified | 리소스가 마지막으로 업데이트된 date/time ex) Last-Modified: Thu, 16 Oct 2020... |
Accept-Ranges | Server가 리소스 요청에 대한 수락 범위를 나타내는데 사용 Ex) Accept-Rnages:bytes |
ETag | 요청된 변형에 대한 엔티티 태그의 현재값을 나타냄 |
Content-Language | Content에서 사용된 언어 열거 |
Content-MD5 | Body content의 ND5코드 제공하는데 사용 |
Transfer-Encoding | 송수신자 간 안전하게 전송위해 본문에 적용된 변환 유형 나타냄 ex)Transfer-Encoding:chunked |
HTTP protocol 실습 ⌨
실습 준비
1. 먼저 server.js 파일과 views 폴더, index.html 만든다.
2. 아래 터미널 명령 실행
$npm init
$npm install express nunjucks body-parser
3. server.js 코드 작성
const express = require('express');
const nunjucks = require('nunjucks');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({extended:false}));
nunjucks.configure('views',{
express:app,
})
app.set('view engine', 'html');
app.get('/',(req,res)=>{ // method=get & url= 도메인 +/ 일 경우 실행
res.render('index.html');
})
// GET //
app.get('/login', (req,res)=>{ // method=get & url = /login일 경우 실행
let {userid,userpw}=req.query;
console.log(userid,userpw);
res.send(`GET OK ${userid}/${userpw}`);
});
// POST //
app.post('/login', (req,res)=>{ // method=post & url = /login일 경우 실행
let {userid,userpw}=req.body;
console.log(userid,userpw);
res.send(`POST OK ${userid}/${userpw}`);
});
app.listen(3000,()=>{
console.log('server start port : 3000')
})
views - index.html 코드 작성
<h2>GET 요청 </h2>
<form action="/login" method="get">
<input type="text" name="userid">
<input type="password" name ="userpw">
<input type="submit" value="GET">
</form>
<h2>POST 요청 </h2>
<form action="/login" method="post">
<input type="text" name="userid">
<input type="password" name ="userpw">
<input type="submit" value="POST">
</form>
4. server 실행
$node server.js
여기서부터 http protocol request, response 흐름 따라가기
1. Client --------REQ by GET -----> SERVER & SERVER <-------RES------- Client
브라우저 : http://localhost:3000 입력하면 아래 코드 get ('/')에 의해 'index.html' 을 render 보내주게 됨
app.get('/',(req,res)=>{ // method=get & url= 도메인 +/ 일 경우 실행
res.render('index.html');
})
index.html의 내용대로 아래처럼 render됨
* render의 역할 : html page 를 nunjucks package를 사용해서 html을 읽고 구문이 오류없이 다 맞고 해석완료하면 res 응답에 담아 보여주겠다. nunjucks는 무조건 {} 대괄호로 시작. 읽다가 오류가 있으면 그 아래 내용은 읽을 수가 없어 전체 html을 읽을 수 없음. -> 오류 뜸.
* 브라우저의 역할 : text를 읽어서 그림으로 그려줌
* 요청 문서는 항상 String만 가능 !
<h2>GET 요청 </h2>
<form action="/login" method="get">
<input type="text" name="userid">
<input type="password" name ="userpw">
<input type="submit" value="GET">
</form>
이 때 상태에서 브라우저 - f12 - 네트워크 - 상태 200 GET 클릭 -> 헤더 -> 요청헤더 -> 원시 활성화 하면
위에서 이론으로 배운 REQUEST MSG HEADER 부분이 보인다.
GET / HTTP/1.1 // method / version
Host: localhost:3000 // 도메인
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate //encoding 지원 형태
Connection: keep-alive //연결 keep-alive하겠다.
Upgrade-Insecure-Requests: 1
위에서 하나씩 의미를 분석해본 단어들이 나왔다. body에 담은 내용이 없어서 body는 생략됨
이에 대한 Response 응답 헤더
HTTP/1.1 200 OK //상태 굳
X-Powered-By: Express //express 사용함
Content-Type: text/html; charset=utf-8
Content-Length: 1052
ETag: W/"41c-LS5ETxGxc/6UKwtPYOzqJ5csjUI"
Date: Tue, 25 May 2021 16:00:35 GMT
Connection: keep-alive
Keep-Alive: timeout=5 //5초 유지 keep-alive 설정
응답도 이론에서 본 것처럼 나왔다.
-> GET요청 text에 아무거나 입력후 GET 버튼 클릭-> html input type='submit' -> action='/login' url을 가지고 요청을 보내게 됨. - > GET으로 보냈으니 아래의 server.js 코드가 실행됨
// GET //
app.get('/login', (req,res)=>{
let {userid,userpw}=req.query;
console.log(userid,userpw);
res.send(`GET OK ${userid}/${userpw}`);
});
server에서 요청받은 걸 처리해주고 rend(`GET OK ${userid}/${userpw}` 응답을 보냄. ( 위 왼쪽 그림)
여기 요청과 응답의 헤더 (body 없음)
Response Header
GET /login?userid=asdf&userpw=asdf HTTP/1.1 //방식은 GET, 저 URL로 부탁해 !
Host: localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://localhost:3000/
Upgrade-Insecure-Requests: 1
Request Header
HTTP/1.1 200 OK // OK알겟어
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 16
ETag: W/"10-c+E1gpeehIBRaW85eUlOoSDHcG4"
Date: Tue, 25 May 2021 16:14:50 GMT
Connection: keep-alive
Keep-Alive: timeout=5
1. Client --------REQ by POST -----> SERVER & SERVER <-------RES------- Client
다시 http:localhost:3000 입력 -> 이번에는 post 요청에 아무거나 입력 -> POST 버튼 클릭 -> 요청,응답 쳌
Request Header + body (post는 body에 보통 내용이 있다. 위에 '요청'에 값이 있음. - 아래 요청헤더 + body 합친 것)
POST /login HTTP/1.1 //method:post고 도메인+ /login 요청 한다.
Host: localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded //post 일 경우 중요한 값
Content-Length: 23
Origin: http://localhost:3000
Connection: keep-alive
Referer: http://localhost:3000/
Upgrade-Insecure-Requests: 1
//header 와 body의 구분 line
userid=asdf&userpw=asdf
위 요청을 받고 응답을 아래처럼 줌
// POST //
app.post('/login', (req,res)=>{
let {userid,userpw}=req.body;
console.log(userid,userpw);
res.send(`POST OK ${userid}/${userpw}`);
});
응답 메세지 : Response msg (header + body)
HTTP/1.1 200 OK // 상태 오게이~
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 17
ETag: W/"11-NU6fUghY2ovO8ynkYApNPZnRnRw"
Date: Tue, 25 May 2021 16:22:52 GMT
Connection: keep-alive
Keep-Alive: timeout=5
// header & body 구분 line
POST OK asdf/asdf //응답으로 돌려준 값 (네트워크 '응답' 부분에 담겨져 있음 )
이제 fetch를 사용한 비동기 통신으로 get과 post 실습해보기
동기 vs 비동기
동기 : 순차적 (차례대로)
한번의 클릭으로 모든html 전체를 줌
내가 요청한게 돌아올 때까지 가만 ~ 히 있음
http protoca l규칙 : 한 번의 요청에 한 번의 응답. (두 번의 응답 x)
한 번 요청하면 응답이 올 때까지 다음 요청을 할 수 없음.
ex. 인터넷 느린 상황에서 로딩 중 다른거 눌렀을 때 응답 없음, 화면 안바뀜 -> 동기의 대표적 사례
비동기 : 병렬처리 (되는대로)
원하는 내용만 (요청한 내용만) 보여줌
요청 보내고 응답이 안와도 다른 요청을 보낼 수 있음
ex. 웹툰 보다가 위에꺼 아직 로딩 안되었을 떄 스크롤 내려서 아래쪽에는 나오는 경우
인스타, 페북 등 보다가 인터넷 끊겨도 로딩이 되어진 윗 부분이 보이는 경우
views - index.html에 비동기 코드 추가
<h2>비동기 활용하기 GET</h2>
<button id = "btn" > 통신하기 </button>
<div id = "getroot"></div>
<script type="text/javascript">
const btn = document.querySelector('#btn');
btn.addEventListener('click',btnFn);
function btnFn(){
console.log('req click');
let options = {
method:'GET',
}
fetch('http://localhost:3000/login?userid=asdf&userpw=asdf', options)
.then(data=>{
console.log(data)
return data.text();
})
.then(text=>{
const root = document.querySelector('#getroot');
root.innerHTML += text + '<br />';
})
}
</script>
fetch('~') 첫번째 인자값 - 요청 url / 두번째 인자값 - 설정
해당 url 로 요청을 보낸다. options이라는 것과 options는 method:GET이다.
보내는 url을 보면 ? 뒤에 userid와 userpw 를 담아 보낸다.
통신하기 버튼을 클릭하면 get 값 으로 보낸 내용이 나온다.
data ↓ 여기서 필요한 부분 body 를 text()함수를 이용해 뽑음.
Response { type: "basic", url: "http://localhost:3000/login?userid=asdf&userpw=asdf",
redirected: false, status: 200, ok: true, statusText: "OK",
headers: Headers, body: ReadableStream, bodyUsed: false }
받은 내용이 성공적 resolve 라면 (fetch의 결과값은 Promise 객체) .then(data = > ~ ) 그 결과를 data에 담고 retrn data.text(); data 중 필요한 부분만 뽑는 (.text())함수를 이용하여 return 반환해라
그 반환한 값을 또 (text=>~) text에 넣고 root div 아래에 계속 이어 붙여라
반환 값 : asdf/asdf
위 실행 시 Request header (body 없음)
GET /login?userid=asdf&userpw=asdf HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: */*
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:3000/
Connection: keep-alive
If-None-Match: W/"10-c+E1gpeehIBRaW85eUlOoSDHcG4"
Response header
HTTP/1.1 304 Not Modified // 300번대 -> 요청 처리를 위해 다른 액션이 필요
X-Powered-By: Express
ETag: W/"10-c+E1gpeehIBRaW85eUlOoSDHcG4"
Date: Tue, 25 May 2021 16:45:52 GMT
Connection: keep-alive
Keep-Alive: timeout=5
//header & body 구분 line
GET OK asdf/asdf //res
index.html 에 아래 코드 추가
<h2>비동기 활용하기 POST </h2>
<button id="btn2">통신하기</button>
<div id = "postroot"></div>
<script type="text/javascript">
const btn2 = document.querySelector('#btn2');
btn2.addEventListener('click',btnFn2);
function btnFn2(){
console.log('req click');
let options = {
method:'POST',
header:{
'content-type':'application/json'
},
//body:{'userid':'aaaa','userpw':'bbbb'},
body:JSON.stringify({userid:'aaaa',userpw:'aaaa'}),
}
fetch('http://localhost:3000/login', options)
.then(data=>{
return data.text();
})
.then(text =>{
const root = document.querySelector('#postroot');
root.innerHTML += text+'<br/>';
})
}
</script>
*applicaion json을 쓸 때는 body부분도 json으로 바꿔야함.
body:JSON.stringify({userid:'aaaa',userpw:'aaaa'}),
node.js 에서 변환할 때 qs 객체로 string으로 보내는 걸 추천
여기서는 외부 라이브러리 없이 JSON method 사용
요청 문서는 항상 String만 가능하다.
server.js 에 bodyParser.json() method 추가
const express = require('express');
const nunjucks = require('nunjucks');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json()); //추가
app.use(bodyParser.urlencoded({extended:false}));
이하 생략
참고 블로그 : 감사합니다.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview
http://www.steves-internet-guide.com/http-basics/
https://ryanclaire.blogspot.com/2020/10/HTTP-Overview.html
https://www3.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html
'블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기' 카테고리의 다른 글
[51일차 복습] 카카오 API / 주소, 우편번호 요청 및 가져오기 (0) | 2021.05.26 |
---|---|
[51일차]20210526 API 카카오 주소 가져오기 (0) | 2021.05.26 |
[50일차] 20210525 HTTP post, get 공부 (0) | 2021.05.26 |
[49일차 복습] JavaScript & Oauth 기능으로 kakao login 연결하기 (0) | 2021.05.25 |
[49일차] 20210524 javaScript 카카오 oauth 로그인 kakao login (1) | 2021.05.25 |