이번에는 작은 서버를 구축하여 HTTP Request를 받고, HTTP Response를 전달하는 과정을 살펴보겠습니다.
클라이언트에서는 fetch() 메서드를 통해 서버에 요청을 보냅니다. 그것은 GET 요청이 될 수도 있고, POST 요청이 될 수도 있습니다. GET 요청이라면 fetch의 인자로 요청을 보낼 URL만 들어갈 것이고, POST 요청이라면, 그 인자로 URL과 method, body, header를 명시한 객체가 인자로 전달될 것입니다.
요청을 보낼 때에는 다음과 같은 요청 메시지가 전달이 됩니다.
요청 메시지는 크게 네 부분으로 나뉠 수 있습니다.
- 시작줄 : 요청 메서드(GET / POST 등), URL, HTTP 버전이 한 줄에 들어갑니다.
- 헤더 : 다양한 종류의 요청 헤더가 있습니다. 개별 속성에 대해서는 다루지 않겠습니다.
- 공백 : 헤더와 바디 사이에 한 줄의 공백을 채워 넣습니다.
- 바디 : GET, HEAD, DELETE, OPTIONS 처럼 리소스를 조회만 하는 요청일 때에는 생략이 가능합니다.
In CORS 에서, OPTIONS 메소드를 통해 프리플라이트 요청 (preflight, 사전 전달), 즉 사전 요청을 보내 서버가 해당 parameters를 포함한 요청을 보내도 되는지에 대한 응답을 줄 수 있게 합니다.
GET : 특정 리소스를 조회할 때 보내는 요청 메서드입니다.
POST : 리소스에 정보를 추가할 때 보내는 요청 메서드입니다.
PUT : 리소스의 모든 것을 수정하기 위한 요청 메서드입니다.
PATCH : 리소스의 일부만을 수정하기 위한 요청 메서드입니다.
DELETE : 리소스를 삭제할 때 보내는 요청 메서드입니다.
OPTIONS : OPTIONS 메서드를 사용해 사전 요청(preflight request)을 보내 서버가 해당 파라미터를 포함한 요청을 보내도 되는 지 응답을 해줍니다.
*PUT과 PATCH
PUT은 보내지지 않은 정보에 대해서는 null값으로 업데이트하지만, PATCH는 보내진 정보에 한해서만 업데이트를 하고 기존의 데이터는 유지합니다.
다음은 특정 URL에 POST 요청을 보내는 예시 코드입니다.
fetch() 메서드에 url과 요청 정보를 담은 객체가 인자로 전달되는 것을 볼 수 있습니다.
fetch(`http://localhost:5000/${path}`, {
method: 'POST',
body: JSON.stringify(body),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => res.json()) // then 메서드를 통해 요청에 대한 응답은 response 객체에 담아서 돌려줍니다.
.then(res => {
this.render(res);
});
클라이언트의 요청을 받은 서버는 다음과 같은 메시지로 클라이언트에게 응답을 보내줍니다.
응답 메시지는 다음과 같이 나뉠 수 있습니다.
- 상태줄 : 프로토콜 버전, 상태 코드(200, 404, 500 등), 상태 텍스트(OK)가 들어갑니다.
- 헤더 : 다양한 종류의 요청 헤더가 있습니다. 개별 속성에 대해서는 다루지 않겠습니다.
- 공백 : 헤더와 바디 사이에 한 줄의 공백을 채워 넣습니다.
- 바디 : 바디는 모든 응답에 들어가지 않습니다. 201, 204과 같은 상태 코드를 가진 응답에는 보통 바디가 없습니다.
HTTP 응답 상태 코드는 꽤나 많이 있지만, 아래의 코드들은 너무나 많이 접할 것이기 때문에 외워두는 것이 좋을 것 같습니다.
다음은 스프린트 때 구현했던 코드의 일부입니다. 서버가 요청을 받아서 응답을 보내주는 코드입니다.
코드의 자세한 흐름을 아직 명확히 알지 못해 자세한 설명은 하지 않겠습니다.
const server = http.createServer((req, res) => {
if (req.method === 'POST') { // POST 요청일 경우
if (req.url === '/lower') {
let data = ''; // data를 선언해줍니다.
req.on('data', chunk => { // ?? on()메서드, 'data', chunk
data = data + chunk;
});
req.on('end', () => { // ?? 'end'
data = data.toLowerCase();
res.writeHead(201, defaultCorsHeader); // res.writeHead() 메서드를 통해 헤더를 명시해줍니다.
res.end(data); // res.end() 메서드를 통해 통신을 종료해줍니다.
});
} else if (req.url === '/upper') {
let data = '';
req.on('data', chunk => {
data = data + chunk;
});
req.on('end', () => {
data = data.toUpperCase();
res.writeHead(201, defaultCorsHeader);
res.end(data);
});
} else {
res.writeHead(404, defaultCorsHeader);
res.end();
}
}
if (req.method === 'OPTIONS') { // preflight 요청일 경우입니다. 해당 url에 접근이 가능한 지 먼저 확인하는 요청입니다. OPTIONS 메서드를 사용합니다.
res.writeHead(200, defaultCorsHeader);
res.end();
}
});