2023. 12. 28. 22:35ㆍETC
참고 및 추천 자료
왜 HTTP를 공부해야 하는가
- 요즘 웹에서는 모든것이 HTTP 프로토콜 위에서 동작한다. 앱과 서버가 통신하거나 서버와 서버가 통신하거나 이미지나 HTML, 영상, 기타파일 등등 모두 HTTP로 주고 받기 때문에 백엔드와 프론트개발자 모두 HTTP에 대한 이해가 필요하다.
- HTTP를 제대로 이해하지 못한 상태에서 그냥 개발 경험을 쌓다보면 여러 오류를 만나게 될 것이다, HTTP를 이해하는것이 나중에 다른 개발 경험을 하는데에도 큰 도움이 된다.
- 특히 백엔드 개발자라면 HTTP에 대한 이해가 있어야 REST API를 설계하는데 중요한 지식이 된다. 웹 개발자라면 계속해서 HTTP 위에서 개발을 해야한다. 집 주변 도서관에서 HTTP 완벽 가이드 를 읽어보길 추천한다, URL 개념부터 차근차근 알려준다.
네트워크에 대해 알아보자
- HTTP 프로토콜을 이해하기 위해서는 기본적으로 네트워크 기본적인 이해가 있어야 한다.
- 멀리 살고있는 누군가에게 내 파일을 전송하고 싶다면 인터넷을 통해 파일을 전송해야 한다. 그런데 인터넷망이 고속도로 처럼 일자로 쭉 뻗어 있는 형태가 아니다, 여러 복잡한 과정을 거치며 파일이 전달된다, 한국에서 미국까지 파일을 전송한다면 중간에 수많은 노드를 거쳐 데이터가 전송되어야 하는데 그 과정을 알아보자
IP 프로토콜
인터넷에 연결된 클라이언트 들은 IP주소를 가지게 된다, 할당받은 IP Address를 통해 데이터를 주고받을 수 있고, 패킷(Packet) 이라는 통신 단위로 데이터를 주고 받는다. 패킷을 주고받을 때에는 출발지와 목적지에 대한 정보, 기타정보를 가지고 IP패킷을 만들어서 인터넷망을 통해 노드들이 서버 패킷정보를 전달하며 최종 목적지로 전달되게 된다. 출발지 IP패킷 생성 (출발지IP주소, 목적지IP주소, 기타정보) -> 인터넷 망 -> 목적지 도착 (다시 응답) -> 인터넷 망 -> 출발지로 Response 위 예시에서 처음 출발지에서 IP패킷을 생성하고 목적지에 도착할 때의 루트와 응답이 출발지로 전달될 때의 루트는 다를수 있다, 워낙 네트워크망이 복잡하기 때문이다.
IP프로토콜의 한계
- 비연결성 - 패킷을 받을 대상(도착지)이 없거나 서비스 불능(오프라인) 상태여도 출발지에서는 알 수 없고 패킷은 그대로 전송 된다.
- 비신뢰성 - 인터넷은 결국 서버들끼리의 통신을 통해 데이터를 주고 받는데 만약 중간에 서버가 손상된다면 패킷이 유실되거나 큰 용량의 패킷을 보내야 하는경우 여러개의 패킷으로 쪼개서 생성해 보내는데 패킷이 순서대로 처리되지 않을 수도 있다. (한 패킷에 약 1500바이트) HELLO, WORLD! 라는 정보를 보낼때 크기가 크면 2번으로 나누어 보내는데 1번 패킷을 보낸이후 2번패킷을 보낼때 다른루트가 더 빠르다고 판단해 다른 루트로 패킷을 보낼수도 있다. 패킷의 순서가 1,2번이 아닌 2,1번으로 도착할 수도 있다.
- 프로세스 구분 - 같은 IP를 사용하는 서버에서 여러개의 애플리케이션이 동작한다면 문제가 된다. (한대의 PC에서 게임도 하고 음악도 듣고 채팅도 한다면 출처를 어떻게 구분할 것인가)
TCP / UDP (IP프토콜의 한계를 극복하자)
인터넷 프로토콜 스택의 4계층 (TCP를 통해 위에 언급 되었던 문제들을 극복할 수 있다.)
* 애플리케이션 계층 -HTTP, FTP
* 전송 계층 - TCP, UDP
* 인터넷 계층 - IP
* 네트워크 인터페이스 계층 (랜카드, 랜드라이버)
프로토콜 계층 순서
- 웹 브라우저에서 Hello, World 라는 메세지 생성 (http가 생성 됨)
- Socket 라이브러리를 통해 전달 (TCP로 감싼다.)
- IP 패킷이 얹어진다. (IP 패킷 생성, TCP 데이터 포함)
- 랜카드를 통해 인터넷 서버로 전송.
위에 순서는 비유가 포함되어 있기 때문에 감싼다등의 표현은 감안해야 한다. 이렇게 여러계층으로 전송하고자 하는 데이터가 생성된다. IP안에는 출발지 IP와 목적지 IP 기타정보가 포함된다 했다, 그리고 TCP에는 출발지 PORT, 목적지 PORT, 전송 제어, 순서, 검증정보 와 같은 정보들이 포함되게 된다.
TCP 특징 - 전송 제어 프로토콜 (Transmission Control Protocol)
- 연결지향 - TCP 3 way handshake 가상연결 (목적지의 네트워크가 유효한지 확인)
- 데이터 전달 보증
- 순서 보장
- 신뢰 할 수 있는 프토콜 이다.
- 대부분의 애플리케이션에서 TCP 사용
TCP 3 way handshake란?
- 클라이언트가 서버에서 SYN(Synchronization) 라는 메세지를 보낸다, 데이터를 보내기전 동기화를 위함
- 서버가 응답으로 ACK(Acknowledgement)라는 응답을 보낸다, 동시에 서버가 다시 클라이언트에게 SYN 메세지를 보낸다.
- 양쪽이 모두 ACK(Acknowledgement) 라는 상태가 된 이후 데이터를 보낸다
- 이것은 가상연결 이기 때문에 실제 연결이 된 것은 아니다, 클라이언트와 서버가 모두 SYN/ACK 를 주고 받았기 때문에 논리적으로 연결 되었다고 판단하는 것이고 실제 데이터를 전송하는 과정에 중간에 수 많은 서버가 있을 것인데 이 중간서버인 노드들은 클라이언트와 서버가 연결되었는지 모른다. (나를 위한 전용 랜선이 보장 되는게 아니다)
순서보장
- 클라이언트가 데이터가 크다면 패킷1, 패킷2, 패킷3 등으로 나누어 보게 된다.
- 만약 서버가 패킷을 받을때 순서가 다르다면 데이터를 버리고 다시 보내라고 한다. (서버에 따라 최적화가 다를 수 있음, 패킷을 모았다가 정렬 하는 등)
- TCP/IP 패킷에 TCP계층에서 전송제어 와 순서에 대한 정보들이 들어있기 때문에 가능하다
UDP는 뭐지? (User Datagram Protocol)
- UDP는 하얀 도화지에 비유된다, 기능이 거의 없다.
- 연결지향 - TCP 3 way handshke 같은 기능 X
- 데이터 전달 보증 X
- 순서 보장 X
- 단순하고 빠르다.
- IP와 거의 같고 PORT+체크섬 정도 기능만 가이고 있다. (하나의 IP에서 PORT를 통해 여러개로 구분하게 한다. )
- 애플리케이션에서 추가 작업이 필요하다. (TCP는 느린데 이걸 더 최적화 하고 싶다면 UDP를 가지고 본인이 직접 튜닝해야 한다.)
- 요즘 UDP가 오히려 각광이라고 한다. HTTP3가 핸드쉐이킹을 줄이고자 UDP를 채택 하였다고 한다. HTTP3가 UDP를 채택한 이유
PORT
포트는 항구라는 뜻이다 하나의 클라이언트 PC에서 여러대의 서버에 연결되어야 한다면, 예를들어 웹브라우저 에서 쇼핑을 하면서 게임도 켜놓고 음악도 듣고 있다면 나의 IP로 온 패킷이 게임에 대한 패킷인지 웹 브라우저의 응답 결과로 온 패킷인지 알 수 없게된다. IP만으로는 이 문제가 해결되지 않고 그래서 TCP/IP 패킷을 주고 받을 때 출발지의 PORT와 도착지의 PORT 정보도 포함되어 있다.
PORT는 같은 IP 내에서 프로세스를 구분하는데 목적이 있다.
- 0 ~ 65535 까지 port를 할당할 수 있다.
- 0 ~ 1023 이미 사용되는 알려진 포트, 사용하지 않는게 좋다.
- FTP - 20, 21
- TELNET - 23
- HTTP - 80
- HTTPS - 443
URI 와 웹 브라우저 요청 흐름
URI는 Uniform Resource Identifier, 리소스를 식별하는 통합된 방법 정도의 의미로 이해할 수 있을 것 같다.
- URI
- URL
- URN
세가지는 다른 개념이다. URN은 거의 처음들어 보았는데 여기에서 1.1.3. URI, URL, and URN이라는 목차를 보면 세 정의를 확인해 볼 수 있다. URI 라는 리소스를 식별하는 가장 큰 개념이 있고 그 안에 URL (Locator)과 URN(Name) 이 포함된다. URL은 그냥 해당 위치에 리소스가 있다 라는 의미이고 URN은 해당 리소스의 이름이다.
URN은 이런 형태의 모습 이다, 대부분 URL을 사용하고 url은 위치에 대한 정보여서 바뀔수도 있다 urn은 이름이기 때문에 바뀌지 않는다. url보다 더 좋을 것 같지만 urn은 책의 ISPN 정보를 구분하는 형태에서 사용되기도 한다고 하나 경험할 일이 잘 없을 듯 하다.
URL schema
- schema: //[userInfo@]host[:port][/path][?query][#fragment]
- https://www.google.com:443/search?q=hello&hl=ko
- 맨위에 언급했던 HTTP 완벽가이드 책에 URI 관련내용이 구체적으로 잘 설명되어 있다.
웹 브라우저 요청 흐름
위 요청이 브라우저로 전달된다면 www.google.com라고하는 정보를 DNS에서 조회해 IP를 알아내고
GET /search?q=hello&hl=ko HTTP/1.1
Host: www.google.com
이런 모습으로 HTTP 요청 메세지가 생성될 것이다, 그리고 IP정보와 포트정보를 알고 있으니 SOCKET 라이브러리를 통해 TCP/IP로 www.google.com호스트에 SYN/ACK 를 통해 연결되었는지 확인할 것이다 (가상연결), HTTP 메세지에 TCP/IP 패킷정보를 담아 네트워크를 통해 서버로 요청이 전달되고 수 많은 인터넷 노드를 통해서 구글로 요청이 전달될 것이다. 요청을 받는 서버는 도착한 HTTP 요청을 파싱해서 HTTP 요청 메세지를 생성하고 TCP/IP 패킷을 만들어서 응답을 해주고 클라이언트로 전달된 응답 HTTP 메세지를 브라우저가 화면에 보여주게 된다.
참고로 계속 언급되는 노드란 Node.js를 말하는게 아니다 컴퓨터 과학에 쓰이는 기초적 단위를 의미하고 HTML에서 가장 작은 단위에 요소를 node 라고 표현 한다.
마찬가지로 여기서 의미하는 노드는 Node.js가 아니라 컴퓨터과학이나 통신망에서 의미하는 노드이다.
HTTP 기본
모든것이 HTTP 이다.
http는 HyperText Transfer Protocol 이다, http는 기본적으로 html 문서간에 링크를 연결할 수 있는 파일을 전송하기 위한 프로토콜 이였다. 그런데 지금은 음성, 영상, 이미지, JSON, XML, text 등등 서버와 서버간에 데이터를 주고받을 때에도 HTTP르 사용한다. 서버간에 TCP프로토콜로 직접 연결해 데이터를 전송하는 경우는 잘 없다고 한다. 최근 모바일 게임에서는 http로 통신을 주고받는 구조로도 많이 개발한다는데 잘 모르겠다.
HTTP의 역사
이미 중요한 핵심 내용들은 HTTP/1.1 버전에서 대부분다 개발 되었다.
- HTTP/0.9 1991년 개발, GET 메서드만 지원, HTTP 헤더가 없다.
- HTTP/1.0 1996년 개발, 메서드 와 헤더가 추가
- HTTP/1.1 1997년 개발, 대부분의 웹이나 어플리케이션에서 사용되는 핵심적인 버전이다.
- RFC2068, 1997년 개정 됨
- RFC2616, 1999년 개정 됨 (이 버전으로 설명되는 책이 많음, 이런 스펙들은 발전이 느리고 보수 적이다.)
- RFC7230~7235 2014년 개정됨
- HTTP/2 2015년 개발, 성능이 개선 됨
- HTTP/3 개발 진행중, TCP 대신 UDP를 채택해 성능개선을 하고 있다고 한다.
StateFul, StateLess
http의 중요한 특징중에는 무상태 프로토콜을 지향한다는 것이다. 서버가 클라이언트의 상태를 보존하지 않는다. 즉, 1분에 서버에 어떤 요청을 보낸후 지금 다시 요청을 보낸다면 1분 전 요청을 보낸 클라이언트와 지금 요청 보내는 클라이언트가 같은 사람인지 모르는다는 것이다.
- 장점으로는 서버의 확장성이 높다. (무상태는 응답 서버를 쉽게 바꿀수 있다 -> 무한한 서버 증설 가능)
- 만약 Stateful 상태라면 중계서버가 서버1에서 하던 작업을 서버2로 위임하기 어려워 진다. (이전 상태를 유지해줘야 하기 때문에)
- 서버가 중간에 장애가 발생해도 다른 서버로 요청을 위임하면 그만이게 된다.
- 단점으로는 서버가 클라이언트의 상태를 모르니 클라이언트가 추가데이터를 보내주어야 한다. (cookie, accessToken)
Stateless 실무 한계
- 모든 것을 무상태로 설계 할 수 있는 경우도 있고 없는 경우도 있다.
- 상태를 유리해야 되는 경우(로그인) 이라면 쿠키/서버세션, JWT 토큰을 사용해야 한다. (꼭 필요한 경우에만 최소한에 상태 유지를 해야 된다.)
- 요청을 위한 데이터를 다 받아야 한다.
비 연결성
장점
- http는 요청과 응답을 할 때만 연결되고 연결이 유지되지 않고 끊어진다.
- 서버는 연결을 유지하지 않기 때문에 최소한의 자원으로 서버를 유지할 수 있다.
- 일반적으로 초 단위 이하의 빠른 속도로 응답한다.
- 수천명이 서비스를 이용해도 실제 서버에서의 동시처리 요청은 수십개 이하로 매우 작다. (어플리케이션에서 연속해서 검색버튼을 누르지는 않는다.)
단점
- TCP/IP 연결을 매번 새로 맺어야 한다, 다음 페이지로 넘어 가기만 해도 연결을 새로 맺어야 하고 3 hands Shake (SYN/ACK)을 다시 해야 된다.
- 웹 브라우저로 사이트를 요청하면 HTML뿐 아니라 js, css, image 같은 다른 리소스를 같이 다운로드 받게 된다.
- 현재는 HTTP 지속 연결(Persistent Connections)로 문제를 해결한다.
- HTTP/2, HTTP/3에서 더 많은 최적화가 진행 되었다.
HTTP 메세지
위 이미지를 보자, HTTP 메세지에는 스타트라인, 헤더필드, 메세지바디 등이 있다.
헤더부분과 http응답 메세지 사이에는 CRLF 라는 공백이 반드시 들어가야 한다.
HTTP 메서드
아래 설명에 리소스라고 표현했는데 최근에는 Representation 라고 표현한다고 한다, 근데 그냥 MDN Web Docs에도 리소스라고 표현되었길래 리소스라고 표현하겠다.
HTTP에는 여러 메서드가 존재한다. 리소스 중심의 리소스를 매칭해서 URI를 설계 해야한다. 리소스와 행위를 분리해야 한다, 가장 중요한 것은 리소스를 식별하는 것이다. HTTP 메서드는 클라이언트가 서버로 요청을 할 때 기대하는 행동이다.
- GET : 리소스 조회
- POST : 요청 데이터 처리 (주로 등록, 꼭 등록을 의미하지 않는다 요청한 데이터를 처리하는 것을 모두 포함한다.)
- PUT : 리소스 대체, 해당 리소스가 없으면 생성을 의미한다. (덮어씌우는 의미도 있다.)
- PATCH : 리소스의 부분 변경
- DELETE: 리소스 삭제
- HEAD : GET과 비슷하지만 메세지 부분을 제외하고, 상태 줄과 헤더만을 반환하다. (startline 과 header만 리턴한다.)
- OPTIONS : 대상 리소스에 대한 통신 가능 옵션을 설명한다. (CORS 체크에 주로 사용 됨)
- CONNECT : 요청한 리소스에 대해 양방향 연결을 시작하는 메소드, 터널을 열기위해 사용된다. 추가설명
- TRACE (en-US) : 대상 리소스의 경로를 따라 메시지 루프백 테스트를 수행하여 디버깅 메커니즘을 제공한다. 추가설명
REST API를 설계할 때는 리소스 기반으로 설계해야 하지만 실제로는 오롯이 리소스 위주로 URI를 설계하고 http 메서드만으로 행위를 구분하기는 어렵다. 그렇기 때문에 POST /orders/{id}/start-delivery와 같은 설계가 필요하기도 한데 이런경우를 컨트롤URI 라고 부른다. (동사를 사용할 것)
HTTP 메서드 속성
- 안전 (Safe Methods)
- 멱등 (Idempotent Methods)
- 캐시가능 (Cacheable Methods)
안전 (Safe Methods)
호출해도 리소스를 변경하지 않는다, 예를들어 GET 메서드는 단순 조회이므로 Safe 하다고 볼 수 있다. 호출했을 때 변경이 발생하지 않는것을 Safe 하다고 한다. GET 과 HEAD는 Safe 하다.
멱등 (Idempotent Methods)
f(f(x)) = f(x)
멱등 참 애매한 표현이다, 한번 호출하든 두번 호출하는 결과는 늘 동일하다. GET 메서드는 멱등 이라고 할 수 있다, PUT 메서드도 멱등 이라고 할 수 있다. 클라이언트가 똑같은 데이터를 PUT 한다면 최종 결과는 똑같다. {name: 'a', age: 10}이라는 데이터를 10번을 PUT하면 최종 결과물은 동일하다 그래서 멱등이다. DELETE 메서드도 멱등이다 마찬가지로 특정 리소스를 DELETE를 10번을 호출해도 최종 결과는 동일하기 때문에 멱등하다. 그런데 POST는 멱등하지 않다, 결제를 2번하거나 게시물을 2번 만들면 최종 결과가 동일하지 않다.
그럼 왜 이런 멱등이라는 개념이 필요할까? 자동 복구 메커니즘과 같은 상황에서 필요하다, 예를들어 DELETE를 요청했는데 응답이 오지않았다면 다시한번 같은 DELETE 요청을 보내는 것이다. 멱등하니까 다시 요청해도 괜찮기 때문이다. 주의할 부분은 멱등은 외부 요인으로 중간에 리소스가 변경되는 것 까지 고려하지는 않는다. (사용자1은 계속 GET을 하는데 사용자2가 중간에 PUT을 하는경우 사용자1은 같은요청에도 결과가 달라졌지만 이런상황까지 고려하지 않는다.)
캐시가능 (Cacheable Methods)
만약 웹브라우저에서 큰 이미지 리소스를 응답받는 경우 응답 결과 리소스를 캐시해서 사용할 수 있는가 하는 여부이다. 스펙상으로는 GET, HEAD, POST, PATCH에 캐시 가능하다고 되어있지만 실제로는 GET, HEAD 정도만 캐시로 사용한다. POST나 PATCH는 본문내용(바디의 내용)까지 캐시 key로 고려를 해야하는데 쉽지 않다.
클라이언트 -> 서버로 데이터 전송
클라이언트가 서버로 부터 데이터를 전송할 때에는 크게 두가지 방식이 있다, 쿼리파라미터 와 메세지 바디를 통한 전송 이다.
- 정적 데이터 조회: 그냥 GET /static/example.jpg와 같이 정적인 파일을 조회할 때는 아무 데이터없이 GET으로 리소스를 조회하기만 하면 된다.
- 동적 데이터 조회:
- 쿼리 파라미터 : GET으로 동적 데이터를 조회해야 하는 경우, 주로 게시판 목록을 필터하거나 검색하는 경우 URI의 마지막 부분에 물음표와 함께 key, value의 모양으로 데이터를 전송한다. google.com/search?q=hello&hl=ko이 때 모든 값은 String타입으로 전달된다.
- HTML Form 전송 : html 문서중 form 태그가 존재한다. form 태그에 method와 action을 통해 어디로 전송할지 정할 수 있고, submit을 하면 form의 데이터를 읽어서 HTTP 메세지로 만들고 form안에 입력했던 내용을 body에 담아 전송하는 것이다. 이 때 Content-Type 은 application/x-www-form-urlencoded이다. JSON으로 보내는 바디데이터와 form 데이터를 구분할 수 있어야 한다. 이 개념을 전혀 모르고 있으면 나중에 삽질을 만나게 된다. Content-Type 종류
파일과 같은 바이너리 데이터를 보내는 경우 multipart/form-data타입으로 보내면 된다, 각 필드들을 위 이미지처럼 나누어준다. 물론 HTTP 메세지는 브라우저가 자동으로 생성해준다. 개인적 경험으로는 프론트에서 필터와 같은 화면을 만들 때 검색한 옵션들을 쿼리스트링으로 만들어 놓으면 다른페이지로 이동한 뒤 뒤로가기를 해서 돌아오는 경우, 또는 다른사람에게 내가 보고있는 화면을 공유하는 경우에 유리하다, 즉 URL에 쿼리스트링으로 필터정보를 남기면 좋다.
HTTP 상태코드
http 상태코드는 클라이언트가 보낸 요청의 처리상태를 응답에서 알려주는 기능이다. 크게 100,200,300,400,500대 로 나뉘어져 구분되어 있다.
- 1xx (Informational): 요청이 수신되어 처리중
- 2xx (Successful): 요청이 정상적으로 처리 됨
- 3xx (Redirection): 요청을 완료하려면 클라이언트 측에서 추가 행동이 필요하다는 의미인데 리다이렉션을 의미하는 경우도 많다. (HTTP Response 메세지에 Header Field에 Location 값이 있으면 리다이렉션 됨)
- 4xx (Client Error): 클라이언트 측에서 오류 발생, 잘못된 문법등으로 서버가 요청을 수행할 수 없음
- 5xx (Server Error): 서버 측에서 오류 발생, 서버가 정상 요청을 처리하지 못함
자주 사용되는 상태코드
더 자세항 설명은 여기를 참고하자
상태코드설명
200 | ok, 정상적으로 처리 되었다. |
201 | created, 새로운 리소스가 생성 되었음을 의미한다, 201인 경우 http 응답헤더에 location 이라는 값으로 생성된 리소스의 uri를 넣어주는 경우가 많다. |
202 | accepted, 요청이 접수되었으나 처리가 완료되지 않았다. 예를들어 batch가 돌아야 실제 처리가 완료되는 경우인데 잘 안쓰인다. |
204 | NoContent요청을 성공적으로 수행했지만 별도로 내려줄 컨텐츠가 없는경우, 예를들어 임시저장 save 버튼을 눌러 임시데이터를 저장했는데 딱히 보여줄 건 없는경우 이다. |
300 | Mutiple Choices, 요청에 대한 응답이 여러개 이다, 사용자 에이전트(브라우저)는 여러개의 응답중 하나를 반드시 선택해야 한다, 선택방식은 표준이 없음. |
301 | Moved Permanently, 기존 리소스의 uri가 영구적으로 변경되었음을 의미한다, 검색엔진 에서도 변경됨을 인지할 수 있음 |
302 | Found, 요청 리소스 URI가 일시적으로 변경 되었음을 의미한다. (요청메서드/메세지바디 대부분 GET 으로 처리 하지만 명확하지 않다.) |
303 | See Other(en-US), 요청 리소스 URI가 일시적으로 변경 되었음을 의미한다. (요청메서드/메세지바디 무조건 GET 으로 처리 한다.) |
304 | Not Modified, 캐시를 목적으로 사용 된다. (리소스가 바뀐게 없으니 캐시를 재사용 하라는 의미) |
307 | Temporary Redirect, 302/303과 같은 기능을 하는데 처음 요청당시 요청메서드/요청바디가 POST라면 유지한채로 리다이렉션 한다. |
308 | Permanent Redirect, 301과 같은 기능이나 리다이렉트 할 때 처음 요청이 POST메서드 이면 메서드와 메세지바디를 유지하며 리다이렉트 시킨다. (실제로는 쓸일이 없다. 리소스가 변했어도 api 요청 스펙도 변하기 때문에) |
400 | Bad Request, 클라이언트 요청이 api 스펙과 안맞아 서버가 처리를 못한다는 의미다. |
401 | Unauthorized, 클라이언트가 해당 리소스에 대한 인증을 해야한다. (Authentication = 인증/로그인이 안됐다, Authorization = 인가/권한이 없다.) |
403 | Forbidden, 접근 권한이 불충분해 서버가 승인을 거부하는 경우 |
404 | Not Found, 해당 리소스가 없는데 서버에 요청을 한 경우 혹은 해당 리소스를 숨기고 싶은경우에도 사용된다. |
500 | Internal Server Error, 서버 내부 오류로 발생한 경우 (애매하면 다 상태코드로 처리 가능) |
503 | Service Unavailable, 서비스 이용불가, 서버가 일시적 과부하 또는 예정된 작업으로 인해 잠시 요청을 처리할 수 없는경우, Retry-After헤더 필드를 통해 언제 복구되는지를 알려줄 수도 있다. |
PRG: Post/Redirect/Get이란?
post로 주문서를 생성한 이후에 웹브라우저에서 실수로 새로고침을 누른다면 의도와 다르게 중복으로 요청이 생성될 것이다. 이런 경우를 막기위해 응답을 GET으로 바꿔 응답해주면 새로고침을 해도 중복되는 오류를 막을 수 있다. (어차피 서버에서 주문서id를 통해 막겠지만 클라이언트 차원에서 방지할 수 있다)
- POST /orders로 주문서 생성을 요청
- 요청 처리이후 DB에 새 주문서 생성
- 응답을 200 ok가 아닌 302 Found로 주고 응답헤더 로케이션에 /orders/order-result/19를 추가한다.
- 브라우저는 리다이렉션된 곳에서 GET /order-result/19를 처리할 것이고 새로고침 해도 생성이 아닌 주문결과를 조회해 전달하게 된다.
HTTP 헤더
http헤더에는 여러가지 값이 들어있는데 http 전송에 필요한 모든 부가정보가 들어있게 된다, 예를들어 메시지 바디의 내용, 메세지 바디의 크기, 압축, 인증, 요청 클라이언트정보 서버 정보, 캐시 관리 등등에 중요한 정보들이 많고 표준 헤더가 많다. 필요시 임의에 헤더를 추가할 수 있지만 클라이언트가 해당 추가필드를 처리하기로 약속되어야 한다.
과거 HTTP 헤더 (RFC2616)
- General 헤더: 메세지 전체에 적용되는 정보 예) Connection: close
- Request 헤더: 요청 정보 예) User-Agent: Mozilla/5.0
- Response 헤더: 응답 정보 예) Server: Apache
- Entity 헤더: 엔티티 바디 정보 예) Content-Type: text/html, Content-Length: 3512
HTTP 표준이 2014년 FRC7230 ~ 72235로 변경되면서 많은 부분이 개선되었다.
- 엔티티(Entity) 대신 표현(Representation) 이라는 개념으로 변경
- Representation = Representation Metadata + Representation Data 로 정의 된다.
- 표현 = 표현 메타데이터 + 표현 데이터
- 메세지 바디를 통해 표현 데이터를 전달한다
- 표현 페더는 표현 데이터(응답으로 전달할 실제 데이터)를 해석할 수 있는 데이터유형, 길이, 압축정보 등을 포함한다.
표현 헤더
- Content-Type: 표현 데이터의 형식
- Content-Encoding: 표현 데이터의 압축 방식
- Content-Language: 표현 데이터의 자연 언어
- Content-Length: 표현 데이터의 길이
- 표현 헤더는 전송, 응답 둘다 사용 가능하다
협상 헤더 (컨텐츠 네고시에이션)
HTTP의 헤더에는 여러가지 헤더가 있다, 그중 위에 써있는 표현헤더도 있고 협상 헤더도 있다. 협상 헤더는 클라이언트가 선호하는 표현으로 달라는 요청이다, 서버가 그 요청대로 응답하지 못할 수도 있다.
- Accept: 클라이언트가 선호하는 미디어 타입 전달 예) text/*, text/plain, text/plain;format=flowed, */*
- Accept-Charset: 클라이언트가 선호하는 문자 인코딩
- Accept-Encoding: 클라이언트가 선호하는 압축 인코딩
- Accept-Language: 클라이언트가 선호하는 자연 언어, Quality Values라고 해서 선호 언어의 우선순위를 정할 수 있다 예) Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
- 협상 헤더는 요청시에만 사용된다.
빨간색 줄쳐진 부분을 보자, 구글 검색에 요청된 Accept-Language예시
기타 헤더
- FROM: 유저 에이전트의 이메일 정보
- Referer: 이전 웹 페이지의 주소 (뒤로가기를 누르면 가는 주소, 유입 경로를 확인하기 위한 수단으로 많이 쓴다.)
- User-Agent: 클라이언트 애플리케이션의 정보이다, 통계정보로 많이 쓰이고 어떤 종류의 브라우저에서 장애가 발생하는지 파악하기 위한 수단으로도 쓰인다.
- Server: 요청을 처리하는 ORIGIN 서버의 소프트웨어 정보 (HTTP 요청을 보내면 중간에 여러 프록시 서버를 거치게 된다, 나의 요청을 처리해준 마지막 서버를 origin 서버라고 한다.)
- Data: 응답에서 메세지가 발생한 날짜와 시간
- Host: 요청한 호스트 정보 (도메인), 필수값이다 하나의 서버에 여러개의 애플리케이션이 여러개가 구동중일 수도 있기 때문에 반드시 포함 되어야 한다.
- Location: 페이지 리다이렉션 정보 (201이면 생성된 리소스, 3xx면 리다이렉션 주소)
- Allow: 허용 가능한 HTTP 메서드 예) Allow: GET, HEAD, PUT
- Retry-After: 503 에러인 경우 언제 다시 서비스를 이용할 수 있는지에 대한 정보
- Authorization: 클라이언트 인증 정보를 서버에 전달한다. 예) Authorization: Basic xxxxx
- WWW-Authenticate: 401에러가 발생하는 경우, 인증방법을 정의해서 보낼때 사용한다 예) WWW-Authenticate: Newauth realm="apps", type=1, title="Login to\"apps\"", Basic realm="simple"
- set-Cookie: 서버에서 클라이언트로 응답하는 쿠키
- Cookie: 클라이언트가 서버에서 받은 쿠키를 저장한 뒤, HTTP 요청시 서버로 전달 (쿠키가 있으면 브라우저는 모든 요청에 쿠키를 자동 포함한다.)
쿠키
- set-cookie: sessionId=abcd1234; expires=Sat, 26-Dec-2021 00:00:00 GMT; path=/; domain=.google.com/; Secure
- 사용자 로그인 세션 관리
- 광고 정보 트래킹
- 네트워크 트래픽 추가 유발
- 최소한의 정보만 사용 (세션 id, 인증 토큰 처럼 최소한의 정보만 사용)
- 서버에 전송하지 않고, 웹 내부 데이터를 사용하고 싶으면 localStorage 등을 사용한다.
- 보안에 민감한 데이터는 저장하면 안된다.
- Set-Cookie: expires=Sat, 26-Dec-2021 06:00:00 GMT만료일이 되면 쿠키 삭제
- Set-Cookie: max-age=3600(3600초)
- 세션쿠키: 만료 날짜를 생략하면 브라우저 종료시 삭제된다.
- 영속 쿠키: 만료 날짜를 입력하면 해당 날짜까지 유지된다.
- domain: domain = example.com(도메인을 명시하면 기준 도메인+ 서브도메인까지 모두 쿠키가 같이 전송된다.), 생략하게 되면 쿠키를 생성한 도메인에서만 접근되고 서브 도메인은 불가능하게 된다.
- path: 패스를 지정하면 해당 경로의 하위 페이지 에서만 쿠키 접근이 가능해진다. 일반적으로 path=/처럼 루트로 지정한다.
- Secure: 쿠키는 원래 http, https를 구분하지 않는데 Secure가 적용되면 https인 경우에만 쿠키를 전송한다.
- HttpOnly: XSS 공격을 방지하기 위함, 자바스크립트 에서 쿠키에 접근하는것을 막는다 (document.cookie 불가)
- SameSite: XSRF 공격을 방지하기 위함, 요청 도메인과 쿠키에 설정된 도메인이 같아야만 쿠키를 전송한다.
HTTP 캐시
- 데이터가 변경되지 않아도 계속 네트워크를 통해 데이터를 다운로드 받아야 한다.
- 인터넷 네트워크는 메모리나 하드디스크에 비해 느리고 코스트가 비싸다
- 브라우저 로딩 속도가 느려지고 안좋은 사용자 경험을 유발한다.
만약 캐싱처리가 안되어 있다면 용량이 큰 리소스(html, video, image 등)를 사용하는 경우 캐시가 없다면 요청될 때 마다 계속해서 큰 용량의 응답을 처리할 것이다. cache-control이라는 헤더 필드로 캐시가 유효한 시간을 지정할 수 있다. 웹 브라우저에는 내부에 캐시를 저장하는 저장소가 있다. 근데 캐시컨트롤에 적혀있는 유효시간을 지나면(만료되면) 당연히 다시 네트워크를 이용해 리소스를 다운로드 받는다.
이런 상황을 해결하기 위한 방법이 검증 헤더와 조건부 요청이다.
캐싱을 위한 방법 01 - 검증헤더
- Last-Modified: 리소스의 마지막 수정일을 표기한다. 예) Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
위에서 처럼 캐시컨트롤을 통해 지정 시간까지 캐싱이 유효하다 라고 정할 수 있지만 시간이 지나면 다시 다운로드 받는다. 이런 해결을 위해 Last-Modified를 사용하면 된다.
- 1번째 요청시 서버는 응답에 Last-Modified를 포함해 응답한다.
- 2번째로 요청할 때 클라이언트가 if-modified-since를 포함한다.
- 2번째 요청에 대한 확인을 할 때 서버는if-modified-since의 값과 리소스 최종 수정일을 비교한다
- 수정한 값이 없다면 304 Not Modified로 응답해 준다. (HTTP Body가 없어야 한다.)
- 304를 응답 받은 클라이언트는 캐싱되어 있던 리소스를 재사용 한다.
- 결과적으로 네트워크 다운로드가 요청/응답이 발생하기는 하지만 바디가 생략되므로 훨씬 적은 비용만 사용하게 된다.
캐싱을 위한 방법 02 - ETag
위에서 확인한 방법은 변경된 데이터가 없는경우 다시 재사용하라는 해결바법 이였다. 그리고 날짜 기반의 로직을 사용하고 있다, 만약 데이터를 수정해 날짜는 다르지만 실질적인 데이터는 동일한 경우, 스페이스나 주석처럼 별 영향없는 변경에 캐시를 유지하고 싶은경우 등등 별도의 캐시 로직을 관리하고 싶은 경우에 필요한 것이 ETag(Entity Tag) 이다. 서버에서 임의에 버전이나 이름을 달아줄 수 있다, 데이터가 변경되면 태그이름을 바꿔서 변경되었음을 인지하는 것이다. if-None-Match헤더필드로 ETag를 전달한다.
- 동일한 파일을 해시로 바꾸면 컨텐츠가 같다면 해시값도 계속 동일하다.
- 각 데이터마다 ETag를 부여하고, 데이터가 변경되면 새 ETag를 붙인다.
- ETag가 바뀌었으면 데이터가 변경 된 것으로 인식 한다.
캐시와 관련된 헤더 필드
- Cache-Control: 캐시 제어를 위한 필드이다.
- Cache-Control: max-age=3600캐시 유효시간을 초단위로 지정한다.
- Cache-Control: no-cache데이터는 캐시해도 되지만, origin 서버(http 요청을 처리하는 실제 서버)에 검증하고 사용한다.
- Cache-Control: no-store데이터에 민감한 정보가 있으므로 저장하지 말라는 의미 (메모리에서 사용하고 최대한 빨리 삭제)
- Pragma: 캐시 제어, http 1.0 이하에서 사용하기 위한 하위호환인데 사용할 일이 없을 것 같다.
- Expires: 캐시 만료일을 지정하기 위함인데 Cache-Control: max-age를 권장한다, 사용할 일이 없을 것 같다.
프록시 캐시
만약 내가 현재 살고있는 경기도에서 미국 아마존 서버로 접속해 쇼핑과 관련된 이미지를 다운로드 하려고 할 때 1초가 걸린다고 하자, 이런 시간을 줄이기 위해 프록시 캐시 서버를 만들어 한국 -> 미국이 아닌 한국 -> 한국 어딘가에 있는 프록시 서버로 이미지를 다운로드 받는다면 훨씬 빠른 속도로 이용이 가능할 것이다. CDN 서비스 라고도 부른다, AWS 에서는 클라우드 프론트가 여기에 해당한다. 몇년전만 해도 아마존을 단순 쇼핑사이트로 생각했던 걸 생각하면 내가 너무 무식한 것 같다.
캐시 무효화
캐시 되면 안되는 경우가 존재할 수 있다, 캐시를 하지 말라고 해도 브라우저 내에서 임시로 캐시하는 경우도 있다고 한다. Cache-Control: no-cache, no-store, must-revalidate이렇게 캐시컨트롤을 추가하고 혹시 모를 HTTP 1.0 이하 버전을 위해 Pragma: no-cache도 추가해주면 된다.
작년에 물류사/관세사 관련 프로젝트를 진행한 적이 있는데 IE에서 캐싱이 되어서 자꾸만 물류사 리스트가 최신이 아닌 문제가 있었다.
axios.defaults.headers.common = {
Pragma: 'no-cache'
}
위 코드로 처리가 되었는데 이 당시에는 이게 무슨의미인지 몰랐는데 지금은 이 글을 보는 분들도 의미를 알것이라 생각한다.
확실하지 않지만 아마도 IE는 http 1.0을 쓰나보다..?
'ETC' 카테고리의 다른 글
AWS 접속 및 RDS 설정하기 (Springboot) (1) | 2024.01.03 |
---|---|
Fish Shell 설치 및 사용법 배우기 (0) | 2023.12.29 |
CSS - FLEX 공부하기 (1) | 2023.12.27 |
H2 Database 공부하기 (3) | 2023.11.12 |
[AWS] RDS 생성 및 설정하기 (0) | 2023.11.10 |