본문 바로가기

블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기

[50일차 복습] HTTP protocol 구조 개요와 예제

반응형

이번 포스팅 내용  : 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) 

 

 

 

Common Request msg example

 

 

 

 

*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

반응형