kakasoo

[HTTP] 8. 헤더 (Headers) 본문

프로그래밍/HTTP

[HTTP] 8. 헤더 (Headers)

카카수(kakasoo) 2020. 10. 18. 15:10
반응형

3.5 헤더

헤더와 메서드는 클라이언트와 서버가 무엇을 하는지 결정하기 위해서 함께 사용된다. 헤더는 특정 종류의 메시지에만 사용할 수 있는 헤더와, 일반 목적으로 사용하는 헤더, 응답과 요청 양쪽 모두에서 정보를 제공하는 헤더가 있다.

헤더는 크게 다섯 가지로 분류한다.

  • 일반 헤더 (General Headers) : 클라이언트와 서버 양쪽이 모두 사용하는 헤더. 어딘가에 메시지를 보내는 다른 애플리케이션을 위해 다양한 목적으로 사용된다. 예를 들어 Date 헤더가 있다.
  • 요청 헤더 (Request Headers) : 요청에서 사용하는 헤더. 클라이언트가 받고자 하는 데이터 타입이 무엇인지와 같이, 부가 정보를 제공한다.
  • 응답 헤더 (Response Headers) : 클라이언트에게 정보를 제공하기 위한 헤더.
  • 엔터티 헤더 (Entity Headers) : 본문에 대한 헤더를 말한다.
  • 확장 헤더 (Extension Headers) : 비표준 헤더. 애플리케이션 개발자들이 임의로 만든 것이지만, HTTP 프로그램들은 확장 헤더들에 대해 설령 그 의미를 몰라도 전달할 필요가 있다.

3.5.1 일반 헤더

아주 기본적인 정보를 전달하기 위한 헤더로, 메시지의 종류와 상관없이 유용한 정보를 제공한다.

Connection, Date, MIME-Version, Trailer chunked transfer, Transfer-Encoding, Upgrade, Via가 있다.

추가적으로, 일반 캐시 헤더가 있는데, HTTP/1.0은 HTTP 애플리케이션이 매번 데이터를 갖고 오지 않아도 정보를 전달할 수 있도록 로컬 복사본을 캐시할 수 있도록 해주는 헤더를 도입했다.

최신 버전의 HTTP는 매우 풍부한 캐시 매개변수의 집합을 가지고 있다. 추후 자세히 설명한다.

3.5.2 요청 헤더

클라이언트가 요청 전에 요청에 대한 정보, 클라이언트의 선호나 능력에 대해 전달한다. 서버는 클라이언트에게 더 나은 응답을 주기 위해서 이 정보들을 활용한다.

Client-IP, From, Host, Referer, UA-Color, UA-CPU, UA-Disp, UA-OS, UA-Pixels, User-Agent 등이 있다.

추가적으로, 요청 에더에는 4가지의 헤더 범주가 더 있는데, 각각 Accept 관련 헤더, 조건부 요청 헤더, 요청 보안 헤더, 프락시 요청 헤더가 있다.

첫째로 Accept 관련 헤더는 서버에게 자신의 선호와 능력을 알려주는데, 서버는 이를 통해 더 똑똑하게 정보를 제공할 수 있다.

클라이언트는 자신이 원하는 것을 보다 가깝게 얻을 수 있고, 서버는 클라이언트가 사용할 수도 없는 것을 전송하는 데에 시간과 대역폭을 낭비하지 않아도 된다.

둘째로 조건부 요청 헤더 는 클라이언트가 요청에 넣은 제약을 의미한다. 예컨대 클라이언트는 자신이 가지고 있는 리소스가 서버의 리소스와 다를 때에만 새로 전송해주기를 바랄 수도 있다.

조건부 헤더를 사용하면 클라이언트는 서버에게, "요청에 응답하기 전에 먼저 조건이 참이지 확인하게 하는" 제약을 포함시킬 수 있다.

셋째로 요청 보안 헤더가 있다.

HTTP는 자체적으로 간단한 인증요구/응답 체계를 가지고 있는데, 클라이언트가 리소스에 접근하기 전에 자신을 인증하게 함으로써 트랙잭션을 약간이라도 더 안전하게 만들고자 한다.

마지막으로 프락시 요청 헤더는 프락시의 기능을 돕기 위해 만들어진 헤더들이다.

3.5.3 응답 헤더

응답 헤더는 클라이언트에게 부가 정보를 제공한다. 응답을 누가 보내고 있는지, 응답자의 능력은 얼마나 디는지를 알려준다. 또한 응답의 특별한 설명을 제공하기도 한다.

응답헤더는 Age, Public, Retry-after, Server, Title, Warning 등이 있고, 부가적인 범주에는 협상 헤더, 응답 보안 헤더가 잇따.

협상 헤더는 클라이언트와 서버가 어떤 표현을 택할 것인가에 대한 협상을 할 수 있도록 지원하는데, 예컨대 위에서 말한 것과 같이 영어와 프랑스어 중 어떤 정보를 원하는가 등을 협상할 때 사용한다.

응답 보안 헤더는 요청 헤더에서의 요청 보안 헤더와 유사하다.

3.5.4 엔터티 헤더

HTTP 메시지의 엔터티에 대해 설명하는 헤더는 많다. 내용물에 대한, 개체의 타입부터 유효한 메서드 등 광범위한 정보를 제공한다. 일반적으로 엔터티 헤더는 수신자에게 자신이 다루는 것이 무엇인지를 말한다.

콘텐츠 헤더는 엔터티의 콘텐츠에 대한 구체적인 정보를 제공한다. 콘텐츠의 종류, 크기, 기타 콘텐츠를 처리할 때에 유용하게 활용할 수 있다.

엔터티 캐싱 헤더는 언제 어떻게 캐시가 되어야 하는지에 대한 지시자를 제공한다.

헤더들에 대한 자세한 내용은 추후, 해당 챕터에서 이어질 것이다.

부가적인 설명

여기에서는 nodejs와 express를 이용하여 설명하겠다.

  • request의 header를 출력해보았다.
// Get Method
router.get('/', (req, res, next) => {
  console.log(req.headers);
  res.status(200).send('OK');
});

// request headers를 출력한 결과, 기본적으로 담겨있는 정보는 아래와 같다.
{
  host: '127.0.0.1:3000',
  connection: 'keep-alive',
  'upgrade-insecure-requests': '1',
  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
  'sec-fetch-site': 'none',
  'sec-fetch-mode': 'navigate',
  'sec-fetch-user': '?1',
  'sec-fetch-dest': 'document',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'ko,en-US;q=0.9,en;q=0.8',
  'if-none-match': 'W/"aa-z+ebXSEdArbZ+EXlN/WQjf6HV8c"'
}

상당히 많은 header들이 처음부터 들어 있었다, 앞서 설명한 것과 같이 key-value의 쌍으로 이루어져 있다.

왜 key가 중간부터 따옴표 사이에 표현되어 있는가 의문이 든다면, '-' 부호에 대해서 생각해보자. 이것이 포함되어 있는 문자열은 따옴표 사이에 작성하지 않으면 변수끼리의 차로 해석될 것이다.

일반적인 경우 req.headers.host와 같이 작성할 수는 있겠지만, req.headers.user-agent 라고 표현할 수는 없을 것이다. 이런 경우는 req.headers['user-agent']와 같이 찾아 가야 할 것이다.

  • response의 header를 추가해보았다.
app.use((req, res, next) => {
    res.append('Access-Control-Allow-Origin', ['*']);
    res.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.append('Access-Control-Allow-Headers', 'Content-Type');
    next();
});

// Express routes
app.get('/api/examples', (req, res)=> {...});

추가할 때는 위와 같이 append 함수를 사용하면 된다. append 함수는 key와 value를 각각 문자열로 받아서 추가해주는 함수이다. 다른 함수로는 set도 있다.

// 한 번에 추가할 수도 있다.
res.set({
  'Content-Type': 'text/plain',
  'Content-Length': '123',
  'ETag': '12345'
})
// GET으로 전체 정보를 요청할 경우
router.get('/', (req, res, next) => {
  res.set('Writer', 'kakasoo');
  console.log(res.getHeaders().writer);
  res.status(200).send('OK');
});
  • append와 set의 차이
// GET으로 전체 정보를 요청할 경우
router.get('/', (req, res, next) => {
  res.set('Writer', 'kakasoo');            // writer: 'kakasoo'
  res.set('Writer', 'kakaso');             // writer: 'kakaso'
  res.append('Writer', 'kakasu');          // writer: ['kakaso', 'kakasu']
  res.set('Writer', 'kakasi');             // writer: 'kakasi'
  console.log(res.getHeaders());
  res.status(200).send('OK');
});

옆에 작성한 것으로 이해할 수 있을 것이다, set을 사용할 경우 이전에 했던 것을 덮어쓰는 형태로 저장이 이루어진다.

반응형