HTTP 관련 내용은 사실 읽어도 읽어도 잘 머리에 안남는 것 같다. 사실 전반적인 흐름은 아는데, 뭔가 단어들이 기억에 잘 남는다는 느낌은 안들었었다. 다시한번 정리하면서 반복학습을 할 목적으로 포스팅을 남겨본다.
HTTP 란?
- HyperText Transfer Protocol
- 응용 레벨의 프로토콜 (OSI 7)
- HTTP는 신뢰할만한 전송 또는 세션 레이어의 연결(Connection)을 통해 메시지를 주고 받는 상태가 없는 (Stateless) 요청/응답 프로토콜이다.
- HTTP 클라이언트
- 서버와 연결을 맺고, 하나 이상의 HTTP 메시지를 보내는 프로그램
- HTTP 서버
- 클라이언트의 연결을 수락하고 (연결을 맺고)
- HTTP 요청을 처리하여 응답을 보내주는 프로그램
역사
- HTTP v0.9 (1996)
- HTTP v1.0 (1996)
- RFC2616
- URL과 URI의 차이
- URL (Uniform REsource Locator)
- URI의 한 종류
- scheme이 http인 경우의 URI를 URL이라 함
- URI (Uniform Resource Indicator)
- 날짜 포맷
- 3가지의 날짜 포맷을 지원
- RFC 1123: 가장 많이 사용
- Sun, 06 Nov 1994 08:49:37 GMT
- RFC 1036
- Sunday, 06-Nov-94 08:49:37 GMT
- asctime
- HTTP v1.1 (1999)
- working group
- wg (working group)
- 예외 케이스를 개정하는 작업을 했던 곳
- 문서를 개정해서 만듦
- 상세하게 보고 싶으면 해당 링크에서 확인
브라우저에 입력 후 창이 보이기 까지 과정
- PC (Client: User Agent)
- User agent
- HTTP 프로토콜 헤더에 있음
- Mozilla/5.0 ~ 으로 시작하는 녀석
- 이 정보를 왜 줄까?
- 호환성 때문
- 옛날에 서버 차원에서 들어온 값에 따라 다르게 보내주는 경우가 있었음
- 즉, 이 정보 바탕으로 호환되는 결과를 응답으로 줘! 라는 얘기
- DNS Server
- Domain Name Server
- 그러면 어떻게 DNS서버를 들렀다 오는걸까?
- 그러면 매번 입력할 때마다 DNS를 들렀다 오는걸까?
- ROOT DNS
- 전세계에 13개의 Root DNS 서버가 있다.
- 가장 확실한 내용의 DNS 정보를 가지고 있는 서버
- 나머지 DNS 서버는 이걸 복제해서 가지고 있음
- 물어본다면, 가장 가까운 DNS 서버에 물어보게 됨
- CDN
- 서버까지 와서 보내주기 싫으니 유저 근간에 만들어두는 것
- 분산해서 서버를 복사해두고(캐시) 가장 근거리에 있는 서버에서 응답해주는 것
- 응답 시간이 빨라지고, 안정성도 보장한다.
- 어떻게 보면 프록시 서버와도 유사한 기능을 한다고 볼 수 있다.
- Server (Origin)
- Application Server
- 네트워크를 통해 Endpoint와 통신을 할 수 있는 서버를 말한다.
- Web Server
- 주로 HTTP 프로토콜을 처리하는 서버
- Apache
- 정적인 처리에 특화된 웹서버
- 정적이란?
- 미리 서버에 저장된 파일을 제공
- 어떤 요청이냐에 상관없이 같은 정보를 제공
- Tomcat
- Apache와 세트로 다님
- 동적처리에 주로 사용됨 (정적 처리도 가능)
- Servelet Container
- Servelet을 관리하는 역할을 수행
- 웹서버와 소켓을 만들어 Servet이 통신하는 환경을 제공
- Servelet이란?
- 웹에서 자바 프로그래밍 구현을 위해 탄생
- 클라이언트의 요청에 따라 처리 후 결과를 클라이언트에 보내는 클래스
- Tomcat을 사용하는 경우 보톤 Web Application Server라 불린다.
- 흐름
- Client -> Apache -> Tomcat -> Servelet -> Tomcat -> Apache -> Client
- Database Server
- File Server
- Proxy Server
HTTP는 어떻게 동작할까?
- HTTP/0.9
- 메소드 GET 밖에 없음
- 문서 포맷 HTML만 가능
- 헤더 없음
GET /test.html
- HTTP/1.0
HTTP 메시지의 구조
- Start Line
- Header
- 헤더 중 X가 붙은 경우, 커스텀 헤더임
Pragma
: 옛날 캐시 정책 Deprecated
Cache-Control
: 캐시 정책인데, 기능이 추가된 것들
Connection
: close, keep-alive 두 개의 옵션이 있음
- HTTP는 상태가 없는 프로토콜
- 즉, 이전 Request에 대해서 고려하지 않는다는 것
- 터미널 같은 경우, 이전 요청에 종속적인 결과를 줌
- TCP 통신을 하게되면 연결과정에 오버헤드가 생김
- 그런데 어차피 계속 요청, 응답을 할거라면 요청 보낼때마다 이를 연결하고 끊는 것은 비효율적
- 그래서 해당 헤더가 있다.
- 그런데 이게 문제가 있을 수도 있다.
- 대용량 서비스 같은 경우에 해당 옵션을 킨다는 것은 소켓하나를 점유하고 있다는 것이기 때문
- 만약 특정 이벤트를 진행해서 순간적으로 사람들이 엄청 몰린다고 해보자. 이 상황이면 켜는 것이 좋을까, 끄는 것이 좋을까?
- 끄는 경우
- 키는 경우 (요즘)
- 근데 끄게 되었을 때, 문제가 생기는데 페이지는 떴지만, 이미지는 뜨지 않는 상황이 발생했다.
- 다시 재요청을 하는데, 그럴 경우 해당 요청이 뒤로 밀리게 되기 때문
- 즉, 이렇게 되면 한사람이 느끼는 서비스 품질이 상당히 떨어지게 된다.
- 보통 들어와서 사이트 전체를 보고, 로그인을 하든 어떠한 행동을 취하는 것까지가 하나의 액션이기 때문이다.
- 그렇기 때문에 요즘에는 keep alive를 키되, 해당 연결 지속 시간을 줄이는 것으로 변화하는 추세이다.
- Blank Line
- Body
Request-Line OR Status-Line
[Header CRLF]
CRLF
[ Body ]
- CR (Carriage return)
- LF (Line Feed)
- 타자기에서 발생한 어원
- 이전에는 종이가 움직이고 아이핑하는 것은 타자기의 중앙에 위치했음
- 이 때, 종이를 움직이게 만드는 것을
carriage
라 불렀음
- 한줄을 다 치게되면, 왼쪽으로 옮겨졌던 종이를 다시 오른쪽으로 옮겨야 함
- 이 과정에서 나온 것이 Carriage return
- 또 다음줄을 쳐야했기 때문에 종이를 위로 올려야 함
- 현재
- 이제는 Carriage Return을 할 필요가 없음
- Line Feed만 사용하면 의미를 충분히 전달할 수가 있음
- 하지만..
- Window: CRLF 사용
\r\n
- Unix 계열: LF만 사용
\n
- 에디터에서는 이를 똑같이 인식해주나, 이전 HTTP 메시지를 직접 작성한 경우,
\r\n
이 없어서 곤역을 치렀던 경험이 있음
HTTP 메소드
- 종류
- GET
- 요청이 캐시된다.
- 브라우저, 로봇이 임의로 요청이 가능하다.
- Conditional GET
- 클라이언트는 이전에 한번 요청해서 돌려받은 리소스에 대해 다시한번 요청을 할 때, 불필요한 트래픽을 줄이기 위해, 해당 리소스가 변경된 경우에만 다시 보내달라고 요청할 수 있다.
- 이러한 방법때문에 실제로 웹브라우저를 사용할 때, 모든 요청이 서버로 들어가지 않는 경우가 더 많다.
- 언제 사용할까?
- 클라이언트가 어떤 웹 페이지 접근 후, 다시 같은 페이지 접근했을 때, 서버가 그 페이지의 모든 내용을 다시 전송하는 것은 낭비이지 않나?
- 웹 페이지의 내용 변경이 없다면, 페이지 내용 전송은 필요없지 않을까?
- 이런 것을 가능하게 한다면 무엇을 기억해두어야 할까?
- 언제 마지막으로 해당 리소스를 요청해서 받았는지 (Timestamp)
- 해당 페이지의 유효시간
- 어떤 리소스에 활용하면 좋을까?
- 이미지, js, css
- HTML (X)
- 조금만 변경되도 다른 자원을 받도록 되는 경우가 많기 때문
- User 정보에 따라 달라지는 경우
- 문법
Last-Modified
: HTTP-Date
- 응답 헤더
- Last-Modified는 Date보다 이후여서는 안된다(Must Not)
expires
age
cache-control
If-Modified-Since
: HTTP-Date
- 요청 헤더
- 만약 해당 Date보다 Modified가 이후라면 (해당 요청시점이후에 수정되었다면)
- 그렇지 않다면
Etag
- 자원의 수정이 가해졌는지 확인할 수 있는 문자열
- Weak eTag (W/"syzzy")
- 리소스에 의미있는 수준의 변화가 없다면 바뀌지 않을 수 있음
- String eTag ("xyzzy")
- 문법
- POST
- 생성
- 생성작업을 GET으로 보낼 경우, 사용자 데이터와 같은 중요한 정보가 캐시되고 (보안적으로 나쁨), 로봇의 임의 요청이 가능하기 때문에 좋지 않다.
- PUT
- POST와의 차이
- 생성하거나, 대체(전체) 할 때 사용
- 전체 업데이트
- PATCH
- 리소스의 위치를 알고 있을 때 사용
- 부분적인 업데이트
- DELETE
- OPTIONS
- 어떠한 리소스를 대상으로 어떤 동작을 할 수 있는지 던져볼 때 사용
- Allow라는 header로 OPTIONS, GET, HEAD 이런식으로 알려줌
- HEAD
- TRACE
- 해당 리소스를 가기 위해 어떤 프록시를 타고 갔는지 보여줌
- CONNECT
- SSL 터널링 등 특수 용도에 사용하는 메소드
- 속성
- 멱등성
- 여러번 요청해도 효과는 한번 요청한 것과 같음
- 안전성
- 여러번 요청 걸어도 결과가 같기 때문
- GET, HEAD와 같은 메서드는 리소스에 영향을 주지 않기 때문에 안전
- 그렇지 않은 메서드 같은 경우 Non Safe Method
Method | 안전성 | 멱급성 | 특징 |
---|
GET | O | O | |
POST | X | X | 생성이기 때문에 멱급성을 만족하기 못함 |
PUT | X | O | 정보를 바꾸는 것 가능, 그렇지만 전체를 보내야 함, 전체를 항상 업데이트 하기 때문에 멱급성 만족 |
PATCH | X | X | 서버 리소스를 수정, 리소스의 내용이 어떠냐에 따라 부분적 결과만 바뀌기 때문에 항상 같은 결과를 기대할 수 없음 |
HEAD | | | GET 요청시 어떤 헤더들을 받는지 알려줌 (GET과 같으나 본문 없음) |
TRACE | | | 요청이 서버에 들렀다가 응답 반환까지 거치는 프록시를 알려줌 |
OPTIONS | | | 특정 URL에 대한 옵션들을 알려줌, 사용가능한 메소드 반환 |
DELETE | X | O | 서버의 리소스를 삭제, 같은 리소스가 있을 수 없기 때문에 멱급성 만족 |
HTTP 상태 코드
Code | Infomation | Range | Description |
---|
1xx | Informational | 100, 101 | 100: Continue 101: Switching Protocols |
2xx | Successful | 200~206 | 201: Created 202: Accepted (대기열 긴 경우, 요청은 받았어! ^^ 이 용도..) |
3xx | Redirection | 300~307 | 301: Moved Permanently 302: Found (이전 Moved Temporarily) 307: Temporary Redirect (Recommaned) 308: Permanent Redirect (Recommaned) |
4xx | Client Error | 400~417 | 400: Bad Request (구문 인식 불가) 403: Forbidden (금지됨) 404: Not found |
5xx | Server Error | 500~505 | 500: 서버 오류 |
HTTP 트랜잭션 과정
www.example.org/index.html
을 GET 요청 한 상황을 가정해보자.
- 서버는 80번 포트를 열고 요청을 대기한다.
- 클라이언트는 웹 브라우저 주소창에 URL을 입력한다.
- 웹 브라우저는 DNS에 물어보고 해당 URL Host의 아이피를 알아낸다.
- 알아낸 IP와 포트번호 80(HTTP 기본)으로 TCP Connection을 연다.
- 웹 브라우저는 열린 TCP Connection에
GET /index.html HTTP/1.1
요청을 보낸다.
- 서버는 TCP Connection을 통해 들어온
GET /index.html HTTP/1.1
을 읽고 index.html을 요청함을 확인한다.
- 서버는
/index.html
의 내용을 본문으로 하는 HTTP 응답 메시지를 만들어 이를 클라이언트에게 보내주기 위해 TCP Connection에 쓴다.
- 클라이언트는 HTTP 응답 메시지의 본문을
Content-Length
만큼 읽고, Content-Type
의 값(text/html
)을 읽어 본문을 HTML로 렌더링한다.
URL Encoding
Cache
- 언제 사용할까?
- Conditional GET을 이용하여 트래픽은 줄일 수 있으나, Round Trip은 줄일 수 없다.
- Round Trip
- 패킷이 보내지고 응답을 받을 때까지 걸리는 시간
- 즉, 리소스의 변경 사실을 확인하기 위해서라면 원 서버에 요청을 보내고 응답을 기다려야 한다.
- 원 서버가 멀리 있다면 이 지연이 수백 ms일 수 있다.
- 이 지연까지 줄이고 싶어 탄생하게 된 개념
- 용어
- Fresh (신선한): freshness_lifetime > current_age
- Stale (상한): freshness_lifetime <= current_age
- 브라우저 캐시
- 캐시된 응답이 아직 fresh한 경우
- 브라우저 내에 저장된 값 반환 (Read from disk)
- 캐시된 응답이 stale한 경우
- 서버로 요청을 보냄 (Validation)
- 리소스 변경이 없는 경우
- 리소스 변경이 있는 경우
- Cache-control
- 예전에 Pragma로 사용
- HTTP 1.1 Spec
- 해당 캐시 동작을 변경하고 싶다면 서버에서 해당 헤더를 변경하면 된다.
- Expire의 경우 절대 시간 표현이나 해당 헤더는 상대시간 표현 가능
State Management
- HTTP에서 상태가 필요한 이유
- 로그인이 되게 하고 싶다.
- 쇼핑몰 로그인이 아니어도 카트 목록을 기억하게 하고 싶다.
- 해결 방법
- Fat URL
- URL에 상태 정보를 모두 집어 넣기
- URL을 파싱해서, 사용자의 상태를 확인한다.
- 하지만 사용하지 않음
- IP 추적
- Authentication
- HTTP 인증 메커니즘 사용 (Basic Authentication)
- Cookie
- 서버가 쿠키를 생성하라는 명령을 내리고, 클라이언트가 만들어서 클라이언트(브라우저)에 저장하는 값
- User Agent가 알아서 저장한다.
- 생성 후에 서버는 세션을 생성하고, 이 쿠키를 통해 어떤 세션과 연결되어 있는지 판단한다.
- 방식
- Set-Cookie 응답 헤더를 통해 서버에서 클라이언트에 보냄
- 클라이언트는 쿠키를 보관하고 있다가 필요할 때 Cookie 헤더를 통해 서버에 보냄
- 응답 헤더
Set-Cookie: Name=Value; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
- 요청 헤더
Cookie: NAME1=OPAQUE_STRING1; NAME2=OPAQUE_STRING2 ...
- 종류
- Session Cookie
- Persistent Cookie
- Domain 지정 여부에 따라 동작이 다름
- 보통 사용하는 쿠키
- 어떤 값을 담으면 좋을까?
- 정말 기본적인 값
- 보안적으로 문제가 생김
- 카트 정보, 물품 정보들을 이전에 담았었으나, 이제는 그것도 개인정보라 판단하여 넣지 않음
HTTPS
- TLS 위에서 동작하는 HTTP
- HTTP 메시지를 암호화하여 주고 받음
- 보안이란?
- 서버 인증
- 클라이언트는 자신이 진짜 서버임을 알 수 있어야 함
- 클라이언트 인증
- 서버는 자신이 진짜 사용자임을 알 수 있어야 함
- 무결성
- 클라이언트와 서버는 그들의 데이터가 위조되는 것으로 부터 안전해야 함
- 암호화
- 클라이언트와 서버는 도청 걱정 없이 대화가 가능해야 함
- 동작 방식
- 서버가 클라이언트에게 인증서를 보내어 자신을 증명한다.
- 인증서의 내용
- 내용 이상 없음
- 위조되지 않음
- 서명 기관이 믿을 만함
- 클라이언트는 해당 인증서를 검증하고, 이상이 없다면 HTTPS 커넥션을 시작한다.
- 클라이언트가 무작위 수(대칭키, 세션키)를 생성한 뒤, 인증서에 함께 온 공개키로 암호화하여 서버에 전달
- 서버는 개인키로 암호화된 값을 해독하여 대칭키를 얻음
- 이 후, 해당 대칭키를 가지고 서로 통신함
- 왜 비대칭키를 쭉 수용하지 않고 대칭키만을 사용할까?
- 비용 때문에
- 비대칭 암호화 방식은 안전하나, 키를 두개 운용해야 하고 복잡하다는 단점이 있음
HTTP/2
- HTTP/1.1 보다 빠름
- 기능
- Header Compression
- Muliplexed Streams
- 순차적 요청을 통해 커넥션 연결 후 데이터를 받았으나 Stream 기능을 통해 커넥션 하나를 통해 여러 데이터를 주고 받음
- HTTP/1.1
- TCP 커넥션 1개를 연다.
- HTML 문서 1개응 요청해서 받는다.
- TCP 커넥션 7개를 더 연다.
- PNG 파일 8개를 받는다.
- PNG 파일 3개를 받는다.
- PNG 파일 3개를 받는다.
- 사실 이렇게 한 이유는, 브라우저 당 Connection 개수에 제한이 있기 때문
- 클라이언트 입장에서 Connection 수를 정해놓지 않으면, 지나치게 리소스를 많이 사용할 수 있기 때문에
- 서버 입장에서는 무리하게 요청이 들어올 것 같기 때문에
- HTTP/2
- TCP 커넥션 1개를 연다.
- HTML 문서 1개를 요청해서 받는다.
- PNG 파일 13개를 요청해서 받는다.
- Server Push
- 안할 경우
- TCP 커넥션 1개를 연다.
- HTML 문서 1개를 요청해서 받는다.
- 그림 파일 2개를 요청해서 받는다.
- 할 경우
- TCP 커넥션 1개를 연다.
- HTML 문서 1개를 요청해서 그림 파일 2개와 함께 받는다.
- Stream Priority
- 응답을 줄 때, 우선순위를 지정해서 줄 수 있음
- 의존성 지정 안하면
- TCP 커넥션 1개 연다.
- HTML 문서 1개 요청하여 받는다.
- CSS 문서 1개와 그림파일 2개를 요청해서 받는다.
- CSS 문서가 늦게와서 렌더링이 늦어진다.
- 의존성 지정하면
- TCP 커넥션 1개 연다.
- HTML 문서 1개 요청하여 받는다.
- CSS 문서 1개와 그림 파일 2개를 요청해서 받는다.
- 이 떄, 그림파일이 CSS에 의존성이 있다고 설정한다.
- CSS 문서가 가장 먼저 도착하고, 순조로운 렌더링이 가능하다.