[공부 노트] #11

5 minute read

HTTP / HTTPS

HTTP(HyperText Transfer Protocol)은 클라이언트와 서버 사이에 자원을 주고 받을 때 쓰는 것으로, TCP/IP 프로토콜에 HTTP클라이언트(어플리케이션 계층)이 추가된 프로토콜이다. HTTPS는 HTTP에 SSL(TLS)를 추가한 것으로, 평문을 주고받아 보안 문제가 발생하는 HTTP를 개선하였다. HTTPS 통신을 위한 SSL handshake 과정은 이전 공부노트에 서술해놓았다.

HTTP 1.1

keep-alive
HTTP는 하나의 연결에 하나의 요청이 기본이며, 연결을 매번 끊고 새로 생성하는 구조(connection less)이다. 여러개의 트랜잭션이 필요할 때 매번 새로운 연결을 생성하는 것은 매우 비효율적이므로, 연결된 socket에 마지막으로 접근한 시점으로부터 일정 시간동안 연결을 유지하는 것을 keep-alive 라고 한다. 그런데 이렇게 연결을 유지하는 입장에서는 통신 효율이 증대하는 것이지만, 서버와 연결을 맺을 수 있는 socket은 한정적이므로 연결이 오래 지속되면 다른 클라이언트가 접속하지 못하는 상황이 발생할 수 있고, 이런 이유에서인지 HTTP 2.0에서는 사용되지 않는다.
HTTP 1.1에서는 keep-alive와 조금은 다른 지속 커넥션을 지원하며, 기본으로 활성화 되어있다. 트랜잭션이 끝나고 나면

connection : close

를 헤더에 명시해주어야 한다.

파이프라이닝
HTTP 1.0 에서는 소켓에 요청을 써서 보낸 뒤, 응답을 받고 그 다음 request를 보내는 방식으로 동작한다. HTTP 1.1에서는 각 요청에 대한 응답을 기다리지 않고, 여러개의 HTTP request를 하나의 TCP/IP 패킷으로 연속적으로 패킹하여 보내서 다수의 요청과 응답을 한번에 처리하여 latency를 줄인다. 하지만 응답 처리를 뒤로 미루는 방식이므로 결국 후순위의 응답은 지연될 수 밖에 없고, 이러한 현상을 HOL(Head Of Line) Blocking 이라고 하여 최근 브라우저의 대부분은 이 이유로 파이프라이닝을 하지 못하도록 한다. 대신 요청을 병렬로 처리하기 위해서 여러 커넥션을 이용해 성능을 개선하는 방법을 사용하기도 한다.

HTTP 2.0

멀티플렉싱
한 커넥션으로 동시에 여러 개의 메세지를 주고 받을 있으며, 응답은 순서에 상관없이 stream으로 주고 받는 방식이다. 우선 HTTP 2.0 에서 등장한 용어를 살펴보면

  • 프레임 : 제일 작은 정보의 단위. 헤더나 데이터
  • 메시지 : HTTP 1.1과 마찬가지로 요청과 응답의 단위. 여러개의 프레임으로 이루어짐
  • 스트림 : 클라이언트와 서버 사이의 연결을 통해 주고 받는 메시지들의 흐름

프레임 단위로 보내서 수신측에서 재조립하므로, 하나의 커넥션에 여러 스트림이 병렬적으로 존재할 수 있게 된다.

헤더 압축
헤더 테이블을 사용하여 헤더에 중복을 검사하여 중복된 헤더를 보낼 때는 index값만 전달하고, 중복되지 않는 헤더일 경우에만 인코딩하여 전달한다. 기존에는 헤더가 평문으로 이루어져 있었는데, 허프만 코딩(문자 빈도에 따라 압축하는 방식)을 이용하는 HPACK이라는 헤더 압축 방식을 사용하여 효율을 높였다.

Server Push
HTTP 1.0 은 클라이언트에서 요청한 html 문서를 받은 뒤 그것을 해석하면서 필요한 리소스를 다시 서버에 요청해야하는데, 2.0 에서는 클라이언트에서 요청이 없더라도 필요한 추가적인 리소스를 서버에서 보낸다.

GET / POST

get 방식은 요청하는 데이터가 url에 담겨 전송된다. 따라서 그 크기가 제한적이며, 보안이 요구될 경우 부적절하다. post 방식은 message의 body부분에 담겨서 전송되므로, 크기가 크고 보안면에서 낫다. get은 말그대로 서버에서 데이터를 가져와서 보여주는 용도이고, post는 서버의 값을 변경하기 위해서 사용하는 것이 가장 큰 차이라고 볼 수 있다.

HTTP 프로토콜에서 연결 상태를 유지하기 위한 두가지 방법이다.

쿠키
클라이언트 로컬에 저장되는 key-value 데이터 쌍이다. 보안에 크게 상관없는 장바구니 기능에 쓰인다.

  1. 클라이언트가 서버에 요청
  2. 서버가 쿠키를 만들고 헤더에 포함시켜서 응답
  3. 클라이언트가 이후 요청을 하면 자동으로 쿠키를 헤더에 포함하여 요청

세션
브라우저가 종료되기 전까지 클라이언트의 요청을 유지하게 해주는 기술로, 정보가 서버측에 저장된다.

  1. 클라이언트가 서버에 요청
  2. 서버가 해당 클라이언트에 대해 세션 id를 만들고, 쿠키에 담아서 보내준다
  3. 클라이언트가 이후 요청을 하면 서버는 세션 id의 유효성을 검사한 뒤 세션에 저장한 클라이언트 정보를 이용하여 응답해준다

세션도 쿠키를 사용하는데, 둘의 주요한 차이점은 정보를 클라이언트에 저장하느냐, 서버에 저장하느냐이다. 이 차이로 인해 서버에 정보를 저장하는 세션이 더 보안성이 우수하지만 추가적인 처리가 필요한 세션이 더 느리다. 또한 쿠키는 만료시간 전까지는 브라우저가 종료되어도 유지되는데, 세션은 브라우저가 종료되면 삭제된다. 그리고 세션은 서버에 부하를 줄 수 있다.

토큰(JWT)
JWT(Json Web Token)는 인증에 필요한 정보들을 암호화한 토큰을 말한다. 쿠키/세션과 달리 저장소에 유저의 정보를 담는 것이 아니라 토큰 안에 정보들이 들어있고, 그 구조는 다음과 같다.

  • Header : 암호화할 방식, 타입 등의 정보
  • Payload : 실제 토큰으로 사용하려는 데이터가 담기는 부분
  • Verify Signature : (Header + Payload)를 합쳐 비밀키를 이용하여 해싱 알고리즘으로 인코딩

비밀키를 알지 못한다면 토큰을 조작할 수 없다는 보안성을 갖게 된다.

  1. 클라이언트가 서버에 요청
  2. 서버는 비밀키를 이용해서 토큰을 만들고 유효기간을 정한 뒤, 헤더에 담아서 보내준다.
  3. 클라이언트가 이후 요청을 하면 서버는 토큰의 verify signature를 복호화하여 알맞게 응답해준다.

토큰 자체에 정보가 있다는 점에서 토큰이 길어질수록 네트워크 부하가 있을 수 있고, 헤더와 payload 는 암호화되지 않으므로 중요 정보를 넣지 않아야 한다.

CORS

CORS(Cross Origin Resource Sharing)란 서로 다른 출처의 통신에 제약을 두어 CSRF, XSS와 같은 보안 문제를 방지하고자 브라우저에 구현되어있는 정책이다. 즉 브라우저를 통하지 않는 서버간 통신에서는 이 정책이 적용되지 않고, 정상적으로 요청-응답처리가 된 뒤 브라우저에서 CORS 위반이라고 판단되면 그 응답을 버리는 것이다.
이 때, 서로 다른 출처인지 확인하는 기준은 URL의 구성 요소중 프로토콜/호스트/포트 3가지가 동일한지 확인한다.

웹서버 / WAS

웹서버

  • 웹 서버는 경로를 받아 일치하는 file contents(이미지, html, css 등)을 반환한다.
  • 이는 항상 동일한 페이지를 반환하는 과정이다.
  • ex) apache, nginx

WAS

  • WAS는 DB조회나 로직처리가 필요한 동적인 contents를 반환한다.
  • ex) tomcat, jeus

그렇다면 왜 굳이 둘을 나누어놓은 것일까? 단순히 정적 컨텐츠를 반환하는 일을 WAS에서 같이 처리하는 것이 좋지 않을까?

  1. 기능을 분리하여 서버의 부하를 낮춘다. 단순한 정적 컨텐츠는 빠르게 클라이언트에게 제공하고, WAS에서는 상대적으로 오래걸리는 동적 컨텐츠를 처리하는데만 집중하게한다.
  2. 웹서버에 여러 WAS를 연결하여 장애 대응, 배포 및 유지보수를 더 편하게 할 수 있다.

REST API

  • 자원
  • 행위
  • 표현 방식

REST(Representational State Transfer)란 HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD 동작을 적용하는 것을 의미한다. REST와 RESTful을 굳이 비교하여 말하자면, REST는 아키텍쳐 스타일이고, RESTful은 그 아키텍쳐 스타일을 알맞게 따르는 것이라고 할 수 있다. 그리고 그 스타일은 다음과 같다.

  1. Client-Server
  2. Stateless
  3. Cache
  4. Layered System
  5. Code on Demand(optional)
  6. Uniform Interface

HTTP를 따르는 경우 1-4번 조건은 지킬 수 있으므로 중요한 것은 마지막 Uniform Interface 조건이다.

  • Identification of resources : URI로 자원이 식별되어야 한다
  • Manipulation of resources through representations : 리소스를 다룰 때, http 메시지에 그 표현을 담아야 한다
  • Self-descriptive messages : 메시지는 그 자체로 이해 가능해야한다
  • Hypermedia as the engine of application state(HATEOAS) : 애플리케이션의 상태는 Hyperlink를 이용해 전이되어야 한다

REST라는 개념을 정의한 로이 필딩은

“웹을 망가뜨리지 않고 어떻게 http 기능을 증가시킬 수 있을까?”

라는 고민 끝에 REST를 내놓았다. 즉, 서버와 클라이언트가 서로 독립적으로 진화하기 위한 조건들이 REST라는 것이며, 웹이 이를 아주 잘 지키고 있는 예시이다.

  • HTML 명세가 변경되어도 잘 작동한다
  • HTTP 명세가 변경되어도 잘 작동한다

하지만 로이 필딩은 실제로 우리가 REST API라고 부르는 것들은 Self-descriptive, HATEOAS 조건을 제대로 충족하지 못한다고 말한다. 미디어 타입에서 그 원인을 찾을 수 있는데, REST가 잘 지켜지는 웹과 비교하면 HTML은 IANA의 명세에 모든 태그의 해석 방법이 있으므로 Self-descriptive 하고, a 태그 등을 이용한 링크를 통해 다음 상태로 전이될 수도 있다. 반면 API에서 주로 사용되는 JSON 미디어 타입은 IANA의 명세에 따라 파싱할수는 있지만, 파싱한 내용이 무엇을 의미하는지 알 수 없어서 별도의 API문서를 필요로 하고, 다음 상태로 전이할 링크도 없다. 다음과 같은 방식으로 REST를 따르도록 변경할 수 있다.

  • Self-descriptive messages
    • 미디어 타입을 정의하고 문서를 작성하여 IANA에 등록한 뒤 Content-Type 헤더에 명시해준다
    • 명세를 작성하고 Link 헤더에 profile relation으로 명세를 링크한다
  • HATEOAS
    • data 내부에 다양한 방법으로 하이퍼링크를 표현한다
    • HTTP 헤더(Link, Location)에 표현한다

참고문헌

https://interconnection.tistory.com/74
https://victorydntmd.tistory.com/121
https://gmlwjd9405.github.io/2018/10/27/webserver-vs-was.html
https://velog.io/@kjh03160/%EA%B7%B8%EB%9F%B0-REST-API%EB%A1%9C-%EA%B4%9C%EC%B0%AE%EC%9D%80%EA%B0%80

Leave a comment