CS

[HTTP] 개발자로서 갖춰야할 HTTP 지식

SeungbeomKim 2023. 4. 11. 19:15

오늘은 김영한님 모든 개발자를 위한 HTTP 웹 기본 지식 강의를 듣고, 공부했던 것을 복습하기 위해 총정리하는 시간을 가지려고 합니다.

 

강의 내용은 다음과 같습니다. 

 

1. 인터넷 네트워크

  • 인터넷 통신
  • IP(인터넷 프로토콜)
  • TCP, UDP
  • PORT
  • DNS

2. URI와 웹 브라우저 요청 흐름

  • URI
  • 웹 브라우저 요청 흐름

3. HTTP 기본

  • 모든 것이 HTTP
  • 클라이언트 서버 구조
  • Stateful, Stateless
  • 비 연결성(connectionless)
  • HTTP 메세지

4. HTTP 상태코드

  • HTTP 상태코드 소개
  • 2xx(성공), 3xx(리다이렉션), 4xx(Client Error), 5xx(Server Error)

5. HTTP 헤더(일반헤더, 캐시와 조건부 요청)

 

1장은 예전에 포스팅한 적이 있어서 2장부터 총정리하려고 합니다.

 

URI

Uniform : 리소스를 식별하는 통일된 방식

Resource : 자원, URI로 식별할 수 있는 모든 것

Identifier : 다른 항목과 구분하는데 필요한 정보

URL, URN

URL에서 L은 Locater를 의미하고, 리소스가 있는 위치를 지정합니다.

URN에서 N은 Name을 의미하고, 리소스에 이름을 부여합니다.

 

URL 분석

 

scheme://[userinfo@]host[:port][/path][?query][#fragment]
https://www.google.com:443/search?q=hello&hl=ko

프로토콜 : https, 호스트명 : www.google.com,  포트번호 : 443, 패스 : /search, 쿼리 파라미터 : q=hello&hl=ko입니다.

scheme

보통 URL에서 포트번호는 생략합니다. 프로토콜은 어떤 방식으로 자원에 접근하는지에 대한 약속입니다. 

http : 80, https : 443

userinfo

URL에 사용자 정보를 포함해서 인증

host

호스트명, 도메인명 또는 IP주소 직접 지정 가능

port

접속 포트, 일반적으로 생략하는데 default값은 80(http)입니다.

path

리소스 경로, 계층적 구조(ex) /members/100)

query

key=value형태, 쿼리 파라미터 혹은 쿼리 스트링으로 불립니다.(?keyA=valueA&keyB=valueB)

fragment

html 내부 북마크 등에 사용, 서버에 전송하는 정보가 아님

 

웹 브라우저 요청 흐름

 

다음과 같이 클라이언트가 서버로 다음과 같은 URI로 요청을 보내게 되면, 우선적으로 DNS를 조회합니다. DNS에서 해당 도메인에 해당하는 IP주소로 변환시켜 주고 HTTP 요청 메세지를 전송합니다.

 

요청 메세지

GET /search?q=hello&hl=ko HTTP/1.1

Host : www.google.com

 

전송 과정

  1. 웹 브라우저가 HTTP 메세지 생성
  2. Socket 라이브러리를 통해 전달 (TCP/IP 연결 후(3 way handshake) 데이터 전달)
  3. TCP/IP 패킷을 생성(HTTP 메세지 포함)하고 LAN을 거쳐 서버측에 HTTP 메세지 전달
  4. 패킷 정보를 받으면, 서버 측에서는 응답 패킷을 만들어 다시 웹 브라우저로 전달
  5. 웹 브라우저에서는 서버측에서 전달받은 응답 패킷에 있는 HTML 렌더링 과정을 거치게 됨

HTTP 특징

모든 것이 HTTP 

HTTP 메세지에 모든 것을 전송할 수 있습니다. HTML, TEXT, IMG, JSON 등 모든 형태의 데이터를 전송할 수 있기에 서버간에 데이터를 주고 받을때도 대부분 HTTP를 사용합니다. 

HTTP 특징

  1. Client-Server 구조
  2. Stateless, Connectionless
  3. HTTP 메세지
  4. 단순화, 확장 가능

클라이언트 서버 구조이기 때문에 Client가 요청을 보내면, Server측에서 요청에 대한 응답을 만들어서 응답하는 구조입니다.

또한 Stateless이기 때문에, 서버가 클라이언트의 상태를 가지고 있을 수 있기에 확장성이 뛰어나게 됩니다. 하지만, 실무적인 한계도 존재합니다. 

 

실무적인 한계 

예시를 들어, 이벤트 화면이나 로그인이 필요 없는 서비스는 무상태여도 상관없습니다. 하지만, 로그인과 같은 상태 유지가 필요한 영역들은 이들이 처리할 수 없습니다. 그래서 이러한 단점들은 브라우저 쿠키와 서버 세션등을 사용해서 상태를 유지하게 됩니다.

왜 Stateful하게 설계 하지 않냐면 다음과 같은 대안에 생겼냐면, Stateful은 항상 같은 서버를 유지해야 하고, 중간에 서버가 장애가 일어나는 경우가 존재하기 때문입니다. 그래서 최대한 Stateful은 지양하는 게 좋습니다.

 

비연결성(Connectionless)

  • HTTP는 Stateless하다. (연결 유지 x)
  • 초 단위 이하의 빠른 속도로 응답
  • 서버 자원을 효율적으로 사용할 수 있음

비연결성의 한계와 극복

  • 요청과 응답이 끝나면 연결을 끊기 때문에 매번 TCP/IP 연결(3 way handshake)을 새로 맺어야 합니다.
  • 웹 브라우저로 사이트를 요청하면 HTML 뿐만 아니라 JS, CSS, IMG 등 다양한 자원이 함께 다운로드
  • HTTP 지속 연결을 통해 해결

다음과 같이 지속 연결(Persistent Connections)을 통해 매번 TCP/IP 연결을 해야 하는 번거로움을 없앨 수 있고, 데이터를 주고받는 시간도 최적화시킬 수 있게 됩니다.

 

서버 개발자들이 어려워하는 업무 

  • 같은 시간에 딱 맞춰 발생하는 대용량 트래픽
  • 티켓팅, 수강신청 시스템은 정해진 시간에 딱 맞춰 클라이언트가 서버 측으로 HTTP 요청을 보내기 때문에 번거로움이 존재할 수밖에 없습니다. -> 서버를 여러 개 둬서 이를 최소화하려고 노력, 또한 대기열을 관리하는 서버를 따로 구축해서 최대한 Stateless하게 설계하는 것이 바람직함

HTTP 요청 및 응답 메세지와 그에 따른 구조

 

HTTP 응답 메세지와 요청 메세지 구조

HTTP 메세지 구조

  1. start-line(시작 라인) : HTTP 메서드, 절대 경로, HTTP 버전으로 구성
  2. header(헤더, HTTP 전송에 필요한 모든 부가정보, 메세지 바디 내용, 압축, 인증, 클라이언트 정보, 서버 정보, 캐시 정보 등..)
  3. CRLF(공백 라인)
  4. message body(실제 전송할 데이터) : HTML, IMG, JSON 등 바이트로 표현할 수 있는 모든 데이터 전송 가능

 

HTTP API 설계 

API 설계에서 가장 중요한 것이 리소스 식별입니다. 

또한 리소스와 행위를 분리하는 것이 중요합니다. 리소스는 명사이고, 행위는 동사로 구분하면 편합니다. 

회원 등록, 목록 조회, 수정, 삭제에서 리소스는 등록, 조회, 수정이 아닌, 회원이 됩니다. 그리고 행위는 조회, 등록, 삭제, 변경입니다. 그래서 회원 리소스를 URI에 매핑시키는 것이 올바는 API URI 설계 방법입니다. (/members, /members/{id} ..)

 

그러면 행위는 어떻게 구분할까요?

행위는 HTTP 메서드로 구분할 수 있게 됩니다.

HTTP 메서드의 종류

  1. GET : 리소스 조회
  2. POST : 요청 데이터 처리(메세지 바디를 통해 서버로 요청 데이터 전달), 등록에 사용, 대상 리소스가 리소스의 고유한 의미 체계에 따라 요청에 포함된 표현을 처리하도록 요청
  3. PUT : 리소스 전체 대체, 리소스가 없으면 생성
  4. PATCH : 리소스 일부 변경
  5. DELETE : 리소스 삭제 

POST, PUT의 차이점은 POST는 클라이언트가 리소스를 식별하지 않지만, PUT은 클라이언트가 리소스 위치를 알고 URI를 지정해 줄 수 있습니다.  

 

HTTP 속성

  • 안전(Safe) : 호출해도 리소스 변경 x
  • 멱등(Idempotent) : 한번 호출하든 100번 호출하든 결과가 똑같다(Post는 멱등 x), 외부 요인으로 인한 리소스 변경은 고려 x
  • 캐시가능(Cacheable) : 실제로 GET, HEAD 정도만 캐시로 사용, POST, PATCH는 본문까지 캐시 키로 고려해야 하는데, 구현이 어려움

클라이언트에서 서버로 데이터를 전송하는 방식 2가지

  • Query Parameter (조회, 정렬 필터(검색))
  • Message Body(조회를 제외한 부분(POST, PUT, PATCH))

데이터를 전송하는 4가지 상황

  • 정적 데이터 조회(이미지, 정적 텍스트 문서, 쿼리 파라미터 미사용)
  • 동적 데이터 조회(검색, 게시판 목록에서 정렬 필터 사용, 쿼리 파라미터 사용)
  • HTML Form을 통한 데이터 전송(회원가입, 상품 주문, 데이터 변경) -> GET, POST만 지원해서 제약이 있음
  • HTTP API를 통한 데이터 전송(웹 클라이언트(Ajax, 앱 클라이언트, 서버에서 서버끼리의 통신), PUT 기반 등록)

알아두면 좋은 URI 개념

  • 문서(document) : 단일 개념(파일  하나, 객체 인스턴스, 데이터베이스 row)
  • 컬렉션(collection) : 서버가 관리하는 리소스 디렉터리, 서버가 리소스의 URI를 생성 및 관리)
  • 스토어(store) : 클라이언트가 관리하는 자원 저장소, 클라이언트가 리소스의 URI를 알고 관리)
  • 컨트롤 URI : 문서, 컬렉션, 스토어로 해결하기 어려운 추가 프로세스 실행, 동사를 직접 사용(/members/{id}/delete)

 

HTTP 상태코드 정리

  • 1xx(Infomational) : 요청이 수신되어 처리 중
  • 2xx(Successful) : 요청 정상 처리
    • 200 OK (요청 성공)
    • 201 Created(요청 성공하여 새로운 리소스 생성)
    • 202 Accepted(요청을 접수하였으나 처리 x)
    • 204 No Content(요청은 성공하였으나, 응답 페이로드에 본문에 보낼 데이터 x)
  • 3xx(Redirection) : 요청을 완료하려면 추가 행동 필요
    • 301 Moved Permanently : 리다이렉트시 요청 메서드가 GET으로 변경, 본문이 제거될 수 있음(영구 리다이렉션)
    • 308 Permanent Redirect : 301과 기능 동일, 요청 메서드와 본문 유지(영구 리다이렉션)
    • 302 Found : 리다이렉트시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있음(일시적인 리다이렉션)
    • 307 Temporary Redirect : 302와 기능 같고, 리다이렉트 시 요청 메서드와 본문을 변경하면 안 됨(일시적인 리다이렉션)
    • 303 See Oher : 리다이렉트시 요청 메서드 GET으로 변경
    • 304 Not Modified : 캐시를 목적으로 사용, 클라이언트에게 리소스가 변경되지 않았음을 알려준다. 클라이언트는 로컬 PC에 저장된 캐시를 재사용한다. 304 응답은 응답에 메세지 바디 포함 x
  • PRG(Post/Redirect/Get) 사용 근원지
    • POST로 주문 후에 웹 브라우저를 새로고침 하면?
    • 새로고침은 다시 요청하는 것이므로 중복 주문이 될 수 있습니다.
    • POST로 주문 후에 새로 고침으로 인한 중복 주문 방지
    • POST로 주문 후에 주문 결과 화면을  GET 메서드로 리다이렉트
    • 새로고침해도 결과화면을 GET으로 조회
  • 4xx(Client Error) : 클라이언트 오류
    • 400 Bad Request : 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음
    • 404 Not Found : 요청 리소스가 서버에 없음
    • 403 Forbidden : 접근 권한이 불충분한 경우(접근 권한이 불충분한 경우, User가 Admin 리소스에 접근하는 경우)
    • 401 Unauthorized : 인증(Authentication) 되지 않음
      • 인증(Authentication) : 본인이 누구인지 확인(로그인)
      • 인가(Authorization) : 권한부여(Admin 권한처럼 특정 리소스에 접근할 수 있는 권한) 
  • 5xx(Server Error) : 서버 오류
    • 500 Internal Server Error : 서버 내부 문제(애매하면 500 오류)
    • 503 Service Unavailable : 서비스 이용 불가(일시적인 과부하로 인한 요청을 처리할 수 없는 경우)

PRG 사용 전후

 

HTTP 헤더

 

표현

  • Content-Type : 표현 데이터의 형식(text/html, application/json, image/json..)
  • Content-Encoding : 표현 데이터의 압축 방식(gzip, deflate, identity)
  • Content-Language : 표현 데이터의 자연 언어(ko, en, en-US)
  • Content-Length : 표현 데이터의 길이(바이트 단위)

표현 헤더는 전송, 응답시 둘 다 사용합니다.

 

협상

클라이언트가 원하는 표현으로 요청

  • Accept : 클라이언트가 선호하는 미디어 타입 
  • Accept-Charset : 클라이언트가 선호하는 문자 인코딩
  • Accept-Encoding : 클라이언트가 선호하는 압축 인코딩
  • Accept-Language : 클라이언트가 선호하는 자연 언어

협상 헤더는 요청 시에만 사용

 

인증

  • Authorization : 클라이언트 인증 정보를 서버에 전달
  • WWW-Authenticate : 리소스 접근 시 필요한 인증 방법 정의

쿠키

  • Set-Cookie : 서버에서 클라이언트로 쿠기 전달(응답)
  • Cookie : 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청시 서버로 전달

클라이언트가 서버로 로그인 요청을 보낼 때, 유저 정보를 Cookie 헤더를 담아, 웹 브라우저로 전달합니다. 웹 브라우저에 쿠키 저장소(Set-Cookie를 받아 보관)가 있는데, 여기에서 관리한다. 사용자가 모든 요청에 쿠키 메커니즘으로 유저 정보를 기억할 수 있게 됩니다.

 

쿠키를 사용하지 않은 경우와 사용한 경우

 

쿠키란 ? 쿠키는 인터넷 사용자가 웹 사이트를 방문할 때 생성되는 작은 텍스트 파일입니다. 이 파일은 사용자의 컴퓨터나 모바일 장치에 저장되며, 다음에 같은 웹 사이트를 방문할 때 해당 사이트에서 이 파일을 읽어와서 사용자를 인식하게 됩니다.

it 분야에서는 이러한 쿠키를 사용하여 사용자의 로그인 정보나 선호도 등을 기억하고, 이를 기반으로 개인화된 서비스를 제공합니다. 

 

다음과 같이, 쿠키를 사용하지 않은 경우에는 모든 요청에 사용자 정보를 포함해서 서버로 요청을 보내고 있습니다. 하지만, 이러한 방식은 모든 요청에 사용자 정보가 포함되도록 개발해야 되는 번거로움이 있습니다. 그래서 쿠키에 유저 정보를 저장하고 필요할 때마다 유저 정보를 꺼내서 서버에 접근하는 방식이 효율적입니다. 

 

이러한 개인정보는 민감한 정보이기 때문에, 최소한의 정보(세션 id, 인증 토큰) 정보만 저장하고 보안에 민감한 데이터(주민번호, 개인 민감 정보)는 쿠키에 저장하면 안 됩니다.

 

쿠키

 

생명 주기 

expires="날짜", max-age="시간"

 

도메인

명시: 명시한 기준 모데임 + 서브 도메인 포함, 생략 시 현재 문서 도메인만 적용

domain=example.org를 지정해서 쿠키를 생성하면 dev.example.org(하위 도메인)도 쿠키가 접근 가능하지만, 도메인을 지정하지 않으면 하위 도메인은 접근할 수 없습니다.

 

경로

path=/ 루트로 지정 (경로를 포함한 하위 경로 페이지만 쿠키 접근)

 

보안

Secure

 

http, https 구분 x 

Secure적용 시 https인 경우에만 쿠키 전송

 

HttpOnly

Xss 공격 방지

HTTP 전송에만 허용

 

SameSite

XSRF 공격 방지

요청 도메인 == 쿠키 설정 도메인인 경우에만 쿠키 전송

 

캐시와 조건부 요청

 

캐시란? 컴퓨터 과학에서 데이터나 값을 임시로 복사해 놓는 임시 장소

 

만약 캐시가 없다면?

  • 데이터가 변경되지 않아도 계속해서 네트워크를 통해 데이터를 다운로드 받아야 함
  • 인터넷 네트워크는 느리고 비쌈
  • 브라우저 로딩 속도가 느림
  • 느린 사용자 경험

캐시로 인해 불필요한 네트워크 접근 최소화

다음과 같이 데이터가 변경되지 않은 경우, 캐시를 통해 비싼 네트워크 사용량을 줄일 수 있고 속도도 최적화시킬 수 있게 됩니다.

 

캐시 유효 시간이 지나면?

만약 캐시 유효 시간이 초과되면, 다시 네트워크 다운로드를 발생하게 되고, 기존 캐시가 없는 경우와 기능적으로 별 차이가 없게 됩니다.

 

이를 방지하기 위한 방안은 검증헤더와 조건부 요청입니다.

 

캐시 유효 시간이 만료되고, 나올 수 있는 상황 2가지

 

1. 서버에서 데이터를 변경한 경우

2. 서버에서 데이터를 변경하지 않은 경우

 

캐시 시간이 초과되고, 데이터를 변경하지 않은 경우라면, 이전에 캐시에 저장해 두었던 캐시를 재사용할 수 있어야 합니다. 그래서 클라이언트의 데이터와 서버의 데이터가 같다는 사실을 확인할 수 있는 방법이 필요합니다. 이를 위해 필요한 헤더가 검증 헤더입니다.

 

Last-Modified(검증 헤더)를 헤더에 넣어줘서 데이터 최초 변경 시간을 갱신해 줍니다. 그리고 브라우저에서 서버로 요청을 보낼 때는 if-modified-Since(데이터 최조 수정일, 조건부 요청)을 헤더에 넣어줘야 합니다.

 

만약, 서버 측에서 이를 받고, 데이터의 최종 수정일이 같으면, 데이터가 아직 수정되지 않았음을 의미합니다. 수정되지 않았음을 확인하면 서버측에서 브라우저 측으로 HTTP 응답 값 304 Not Modified(바디 x)를 보내줍니다. 그러면 브라우저에서 캐시의 값들을 갱신해주고 캐시에 있는 값들을 다시 사용할 수 있게 됩니다. 

 

검증 헤더와 조건부 요청을 통해 결과적으로는 네트워크 다운로드가 발생하지만, 캐시에 저장된 데이터를 재활용할 수 있고, 용량이 적은 헤더정보만 다운받으면 되기에 불필요한 데이터 접근을 방지하는데 실용적인 해결책이 됩니다. 

 

Last-Modified, If-Modified-Since 단점

  • 1초 미만 단위로 캐시 조정 불가능
  • 날짜 기반의 로직 사용

ETag(Entity Tag)

  • 캐시용 데이터에 임의의 고유한 버전 이름을 달아둠
  • 데이터가 변경되면 이 이름을 바꾸어서 변경함
  • 캐시 제어 로직을 서버에서 관리(클라이언트는 값을 서버에 제공만 하고, 캐시 메커니즘을 모름)

If-None-Match(만약 일치하지 않으면)

-> ETag 일치하면 304 Not Modified 오류 발생

 

캐시 제어 헤더

Cache-Control

  • max-age : 캐시 유효 시간, 초 단위
  • no-cache : 데이터는 캐시해도 되지만, 원 서버에서 검증 및 관리
  • no-store : 민감 정보 저장 x

Pragma(캐시 제어) : HTTP 1.0 하위 호환

Expires(캐시 만료일 지정)

  • 캐시 만료일을 정확한 날짜로 지정
  • HTTP 1.0부터 사용
  • Cache-Control이랑 Expires이 같이 사용되면 Expires는 무시됨

검증 헤더

Last-Modified, ETag

조건부 요청 헤더

If-Match, If-None-Match : ETag값 사용

If-Modified-Since, If-UnModified-Since : Last-Modified 값 사용

 

프록시 캐시

클라이언트와 서버 사이에 대리로 통신을 수행하는 것을 프록시(Proxy)라 하며 중계 기능을 하는 서버를 프록시 서버라고 부릅니다. 원서버와 클라이언트간의 거리가 멀어도 빠른 요청과 응답이 가능한 이유는 프록시 캐시 덕택입니다.

 

캐시 지시어

Cache-Control 

public : 응답이 public 캐시에 저장해도 됨

private : 응답이 해당 사용자만을 위한 것, private 캐시에 저장해야 함

s-maxage : 프록시 캐시에만 적용

 

확실한 캐시 무효화 응답

no-cache : 데이터는 캐시해도 되지만, 원 서버에서 검증 및 사용(원 서버에서 접근 실패 시, 오래된 데이터를 보여주거나 오류 발생)

no-store : 민감 정보가 있으므로 저장 x

must-revalidate : 캐시 만료 후 최초 조회 시 원 서버에 검증해야 함. 원 서버에 접근 실패 시, 반드시 오류 발생

 

정말 많은 내용이 있었지만, 백엔드 개발자가 알아두면 좋을 것 같은 HTTP 지식인 것 같아서 너무 인상 깊게 들었습니다. HTTP에 대한 전반적인 개념, 구조, 흐름, HTTP 통신에 있어서 적용되는 요소들에 대해서도 쉽게 이해할 수 있었습니다. 또한 캐시, 쿠키 등을 사용하는 정확한 이유에 대해서도 알게 되어서 좋았습니다. 가끔씩 강의자분께서 로컬에서 콘솔창을 띄어 HTTP 통신이 어떻게 이뤄지는지에 대해서도 설명했었는데, 이러한 동작과정도 실제로 보니까 직관적으로 다가왔었던 것 같습니다. 이상으로 포스팅 마치겠습니다.

 

<참고 자료>

https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC

 

모든 개발자를 위한 HTTP 웹 기본 지식 - 인프런 | 강의

실무에 꼭 필요한 HTTP 핵심 기능과 올바른 HTTP API 설계 방법을 학습합니다., - 강의 소개 | 인프런

www.inflearn.com