[Node.JS로 웹 개발하기] 1. 웹 서버 만들기

Shy·2023년 9월 15일
0

NodeJS(Express&Next.js)

목록 보기
32/39

웹 서버란?

웹 서버는 텍스트, 이미지, 비디오 및 애플리케이션 데이터와 같은 웹 사이트 콘텐츠를 요청하는 클라이언트에 전달한다. 가장 일반적인 유형의 클라이언트는 사용자가 링크를 클릭하거나 브라우저에 표시된 페이지에서 문서를 다운로드할 때 웹 사이트에서 데이터를 요청하는 웹 브라우저 프로그램이다.

웹 서버는 HTTP(Hypertext Transfer Protocol)를 사용하여 웹 브라우저와 통신한다. 대부분의 웹 페이지 컨텐츠는 HTML(Hypertext Markup Language)로 인코딩된다. 콘텐츠는 정적(예: 텍스트 및 이미지) 또는 동적(예: 계산된 가격 또는 고객이 구매하도록 표시한 항목 목록)일 수 있다. 동적 콘텐츠를 제공하기 위해 대부분의 웹 서버는 비즈니스 논리를 통신으로 인코딩하는 서버 측 스크립팅 언어를 지원한다. 일반적으로 지원되는 언어에는 ASP(Active Server Pages), Javascript, PHP, Python및 Ruby가 있다.

웹 서버는 일반적으로 요청되는 콘텐츠의 전달속도를 높이기 위해 콘텐츠를 캐시할 수도 있다. 이 프로세스를 웹 가속이라 한다.

브라우저가 웹 서버에서 호스팅되는 파일을 필요로 할 때마다 브라우저는 HTTP를 통해 파일을 요청한다. 요청이 올바른 (하드웨어) 웹 서버에 도달하면 (소프트웨어) HTTP 서버는 요청을 수락하고 요청된 문서를 찾아 HTTP를 통해 브라우저로 다시 보낸다. (서버가 요청한 문서를 찾지 못한다면 대신 404 응답을 반환한다.)

자세하게

HTTP Requests

HTTP Method란?

HTTP메서드는 수행할 작업의 종류를 나타내기 위해 서버에 보내는 메시지이다.
이러한 방법을 사용하면 브라우저와 서버 간의 더 풍부한 통신이 가능하다.
가장 일반적으로 사용되는 방법은 GET 및 POST이다.

요청설명
GET어떠한 데이터를 서버로부터 받아(GET)올 떄 사용하는 Method이다.
POST일반적으로 무언가를 생성하기 위해 서버에 데이터 블록을 수락하도록 요청한다. POST/posts
PUT데이터를 업데이트할 때 사용한다. PUT/posts/3
DELETE데이터를 서버에서 삭제할 때 사용한다. DELETE/posts/3

GET Method

GET 메서드는 HTTP 프로토콜에서 사용되는 가장 기본적인 요청 메서드 중 하나이다.

GET 메서드의 특징 및 용도

  1. 정보 검색: GET 메서드는 서버에서 정보를 요청하고 검색하는 데 사용된다. 이것은 읽기 전용 작업으로 간주되며, 서버에 저장된 데이터나 상태를 변경해서는 안 된다.

  2. URL에 포함: GET 요청의 파라미터는 URL에 포함된다. 예를 들어, https://example.com/search?q=query와 같은 URL에서 q=query는 GET 요청의 파라미터이다.

  3. 데이터 길이 제한: URL에 데이터가 포함되기 때문에 GET 요청에는 데이터 길이에 제한이 있다. 대부분의 웹 브라우저와 서버는 URL 길이에 제한을 두고 있기 때문에, 대량의 데이터를 전송하는 데에는 적합하지 않다.

  4. 캐싱: GET 요청은 웹 브라우저나 중간 프록시에 의해 캐싱될 수 있다. 이는 동일한 요청이 여러 번 이루어질 때 서버로의 트래픽을 줄이고 응답 시간을 빠르게 하기 위한 것이다.

  5. 즐겨찾기 및 공유: GET 요청은 URL에 모든 정보가 포함되기 때문에 쉽게 북마크하거나 다른 사람에게 공유할 수 있다.

  6. 안전: GET은 "안전한" 메서드로 간주되며, 서버의 데이터나 상태를 변경하면 안 된다. 그러나 GET 요청이 서버의 상태를 변경하는 사이드 이펙트를 갖도록 설계되어 있으면 안된다.

주의 사항

  • GET 메서드는 민감한 정보(예: 비밀번호)를 전송하기에는 적합하지 않다. 왜냐하면 URL에 포함된 파라미터는 브라우저의 히스토리, 웹 서버 로그, 웹 프록시 로그 등 다양한 곳에 저장될 수 있기 때문이다.
  • GET 요청은 데이터를 생성, 수정, 삭제하는 데 사용되어서는 안된다. 이러한 작업은 POST, PUT, DELETE 등의 다른 HTTP 메서드를 사용해야 한다.

요약하면, GET 메서드는 웹 서버에서 정보를 검색하는 데 사용되는 HTTP 요청 방식이다. 데이터를 전송할 때 URL에 포함되며, 이러한 특성 때문에 다양한 장점과 주의 사항이 있다.

POST Method

POST 메서드는 HTTP 프로토콜에서 사용되는 요청 메서드 중 하나로, 주로 서버에 새로운 데이터를 제출하거나, 데이터를 기반으로 어떤 작업을 실행하도록 서버에 요청할 때 사용된다.

POST 메서드의 특징 및 용도

  1. 데이터 제출: POST는 주로 서버에 새로운 데이터를 제출하거나, 기존 데이터를 수정할 때 사용된다. 예를 들어, 웹 폼을 통해 사용자 정보를 등록하거나, 로그인 정보를 제출할 때 POST 요청이 사용된다.

  2. 데이터 위치: POST 요청의 데이터는 요청 본문(request body)에 포함되어 있다. 이는 GET 메서드와는 대조적으로, URL에 파라미터를 포함하지 않기 때문에 대량의 데이터나 민감한 정보를 전송하기에 적합하다.

  3. 데이터 길이 제한 없음: POST 요청은 요청 본문에 데이터를 포함시키므로, URL의 길이 제한에 영향을 받지 않는다. 따라서 대량의 데이터를 전송할 수 있다.

  4. 서버 상태 변경: POST 요청은 서버의 상태나 저장된 데이터를 변경할 수 있다. 예를 들어, 데이터베이스에 새로운 레코드를 추가하거나, 파일을 업로드하는 작업을 수행할 수 있다.

  5. 캐싱 안됨: 기본적으로 POST 요청은 캐싱되지 않는다. 이는 POST 요청이 서버의 상태를 변경할 수 있기 때문에, 요청의 결과가 항상 동일하게 유지되지 않을 수 있기 때문이다.

  6. 안전하지 않음: POST는 "안전하지 않은" 메서드로 간주된다, 즉 서버의 상태나 데이터를 변경할 수 있다.

주의 사항

  • POST 요청은 의도한 작업을 수행하기 위해 서버가 필요로 하는 모든 데이터를 포함해야 한다. 부족한 데이터로 인해 서버에서 에러가 발생할 수 있다.
  • POST 요청은 민감한 정보(예: 비밀번호, 신용카드 정보)를 전송하는 데에 적합하지만, HTTPS와 같은 보안 프로토콜을 함께 사용하여 데이터를 암호화하는 것이 좋다.

요약하면, POST 메서드는 주로 서버에 데이터를 제출하거나 서버 상태를 변경하는 데 사용되는 HTTP 요청 방식이다. 요청 본문에 데이터를 포함시킬 수 있기 때문에 대량의 데이터 전송이나 민감한 정보 전송에 적합하다.

PUT및 PATCH

PUTPATCH는 둘 다 HTTP 요청 메서드이다. 이들은 주로 서버에 저장된 리소스의 상태를 변경하거나 업데이트하는 데 사용되지만, 사용 방식과 의미에는 중요한 차이점이 있다.

PUT 메서드

  1. 전체 리소스 교체: PUT 요청은 대상 리소스의 현재 상태를 요청 본문의 콘텐츠로 완전히 교체하는 것을 의미한다. 예를 들어, 사용자의 프로필 정보를 업데이트 할 때 해당 사용자의 모든 정보를 제공해야 한다.

  2. 멱등성: PUT은 멱등(idempotent) 요청이다. 이는 동일한 PUT 요청을 한 번 또는 여러 번 수행하더라도 결과가 항상 동일하다는 것을 의미한다.

  3. 리소스 생성: 해당 리소스가 존재하지 않으면 PUT 요청은 새로운 리소스를 생성할 수 있다.

PATCH 메서드

  1. 부분적인 업데이트: PATCH 요청은 리소스의 일부만을 수정하는 데 사용된다. 따라서 전체 리소스 대신 변경하고자 하는 특정 부분만 전송될 수 있다.

  2. 멱등성: PATCH는 일반적으로 멱등이 아니다. 동일한 PATCH 요청을 여러 번 수행하면 결과가 다를 수 있다. 그러나 PATCH의 멱등성은 구현 방식에 따라 달라질 수 있다.

  3. 리소스 생성: PATCH는 리소스의 부분적인 수정만을 목적으로 하므로, 새로운 리소스를 생성하는 데에는 일반적으로 사용되지 않는다.

비교

  • 대상: PUT은 리소스의 전체를 대상으로 하며, PATCH는 리소스의 일부분을 대상으로 한다.

  • 효율성: 변경하고자 하는 데이터가 매우 작을 경우, PATCH는 PUT보다 더 효율적일 수 있다. PATCH는 필요한 변경만을 전송하기 때문이다.

  • 복잡성: PATCH 요청은 변경하려는 데이터의 정확한 부분과 그 변경 방법을 지정해야 하므로, PUT에 비해 복잡할 수 있다.

요약하면, PUT은 리소스의 전체 상태를 업데이트하는 데 사용되며, PATCH는 리소스의 특정 부분만을 업데이트하는 데 사용된다. 어떤 메서드를 사용할지는 상황과 요구 사항에 따라 결정된다.

Stateless Protocol

Stateless가 무엇인지 알기 위한 예시

첫 번째 요청에서 서버에 이미 사용자 123이라고 말해도 그 후 서버에게 다시 물어보면 서버는 내가 누군지 모른다. 왜그럴까?
Because HTTP is stateless!!
stateless가 뭘까?

Stateless Protocol의 주요 특징

  1. 독립적인 요청: 각각의 요청은 다른 요청과 독립적이다. 서버는 이전에 받았던 요청에 관한 정보를 기억하지 않는다.

  2. 정보 저장의 부재: 서버는 클라이언트의 상태에 관한 정보를 저장하지 않는다. 이것은 서버의 메모리 사용을 최소화하고 성능을 향상시키는 데 도움을 준다.

  3. 단순성: 서버와 클라이언트 간의 상호 작용이 단순해진다. 각 요청이 독립적이기 때문에 상호 작용의 복잡성이 줄어든다.

  4. 확장성: 상태 정보를 유지하지 않기 때문에 시스템은 더욱 쉽게 확장될 수 있다. 새로운 서버 인스턴스를 추가하는 것이 간단해진다, 왜냐하면 각 요청이 독립적이고 상태 정보가 저장되지 않기 때문이다.

HTTP와 Stateless Protocol

HTTP (HyperText Transfer Protocol)는 Stateless Protocol의 대표적인 예이다. HTTP는 요청과 응답의 형태로 작동하며, 서버는 이전 요청에 대한 정보를 기억하지 않는다. 그러나, 웹 애플리케이션은 종종 사용자의 세션 정보나 다른 상태 정보를 유지해야 하는 경우가 많기 때문에, 쿠키, 세션, 토큰 기반 인증과 같은 메커니즘이 도입되어 상태 정보를 유지하게 된다.

이러한 메커니즘들은 Stateless Protocol인 HTTP 위에서 상태를 유지하기 위한 솔루션을 제공하지만, 기본적인 HTTP 자체는 상태 정보를 갖지 않는다는 것을 이해하는 것이 중요하다.

HTTP Request, Response

Request 구조

HTTP Request의 구조

  1. Starter line
    요청의 첫 번째 라인으로, 요청 메서드(GET, POST, PUT 등), 요청할 URI, 그리고 HTTP 버전 정보를 포함한다.
    예) GET /index.html HTTP/1.1

  2. Headers
    Key:Value값으로 해당 request에 대한 추가 정보(메타데이터)를 담고 있다.
    예를 들면, User-Agent, Accept, Host, Cookie 등의 헤더가 있다.

  3. Body(optional)
    해당 request가 전송하는 데이터가 담겨있는 부분이다. 전송하려는 데이터가 없다면 비어있게 된다.
    (데이터를 POST 또는 PUT 요청으로 보내는 경우 본문에 데이터가 포함된다. GET 요청의 경우 본문이 없을 수 있다.)

Host요청하려는 서버 호스트 이름, 포트번호
User-agent클라이언트 프로그램 정보
Referer바로 직전에 머물렀던 웹 링크 주소
Accept클라이언트가 처리 가능한 미디어 타입 종류 나열
If-Modified-Since여기에 쓰인 시간 이후로 변경된 리소스 취득, 페이지가 수정되었으면 최신 페이지로 교체
Authorizaition인증 토큰을 서버로 보낼 떄 쓰이는 Header
Origin서버로 Post 여청을 보낼 때 요청이 어느 주소에 시작되었는지 나타내는 값. 이 값으로 요청을 보낸 주소와 받는 주소가 다르면 CORS에러가 발생
Cookie쿠키 값 key-value로 표현된다. Set-Cookie헤더와 함께 서버로부터 이전에 전송됐던 저장된 HTTP쿠키를 포함

예시

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Response 구조

HTTP Response 구조

  1. stauts line
    HTTP version, Status Code, Stauts Text를 나타낸다.
    (응답의 첫 번째 라인으로, HTTP 버전, 상태 코드(예: 200, 404), 그리고 상태 메시지(예: OK, Not Found)를 포함한다.)

  2. headers
    Request의 headers와 동일하다.
    하지만 Response Headers에서만 쓰이는 값도 있다.
    (응답 헤더는 응답에 관한 메타데이터를 포함한다. 예를 들면, Content-Type, Content-Length, Set-Cookie, Cache-Control 등의 헤더가 있다.)

  3. body
    Response의 body와 일반적으로 동일하다.
    (응답의 내용을 포함한다. 예를 들면 HTML, JSON, XML, 이미지 등의 데이터가 될 수 있다.)

Server웹서버의 종류
Agemax-age 시간 내에서 얼마나 흘렀는지 초 단위로 알려주는 값
Referrer-policy서버 referrer 정책을 알려주는 값 ex)origin, no-referrer, unsafe-url
WWW-Authenticate사용자 인증이 필요한 자원을 요구할 시, 서버가 제공하는 인증 방식
Proxy-Authenticate요청한 서버가 프락시 서버인 경우 유저 인증을 위한 값
Set-Cookie서버 측에서 클라이언트에게 세션 쿠기 정보를 설정(RFC 2965에서 규정)

예시

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2021 12:28:53 GMT
Server: Apache/2.4.1 (Unix)
Last-Modified: Mon, 26 Jul 2021 14:05:57 GMT
Content-Type: text/html

<!DOCTYPE html>
<html>
...
</html>

HTTP Status Code

HTTP 상태 코드(HTTP Status Code)는 HTTP 응답의 일부로, 요청이 성공적으로 처리되었는지, 추가 조치가 필요한지, 오류가 발생했는지 등의 정보를 나타낸다. 각 상태 코드는 특정한 의미를 가지며, 3자리 숫자로 구성된다. 상태 코드는 크게 다음과 같은 카테고리로 분류될 수 있다.

코드설명
1xx (Informational)요청이 수신되었고, 프로세스가 계속 진행 중임을 나타낸다.
예: 100 Continue, 101 Switching Protocols
2xx (Successful)요청이 성공적으로 수신되었고 이해되었으며 수락되었음을 나타낸다.
예: 200 OK, 201 Created, 204 No Content
3xx (Redirection)추가 조치가 필요하여 클라이언트가 추가 작업을 수행해야 함을 나타낸다.
예: 301 Moved Permanently, 302 Found, 304 Not Modified
4xx (Client Error)클라이언트 오류로, 요청에 잘못된 문법이 있거나 요청을 처리할 수 없음을 나타낸다.
예: 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found
5xx (Server Error)서버가 유효한 요청을 처리하는 데 실패했음을 나타낸다. 일반적으로 서버에 문제가 발생했을 때 이 범주의 상태 코드가 반환된다.
예: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

이러한 상태 코드를 통해, 개발자나 시스템 관리자는 웹 서버의 응답을 해석하고 적절한 조치를 취할 수 있다. 특히 웹 개발 또는 API 개발을 할 때, 상태 코드는 요청과 응답 사이의 통신 상태를 명확하게 나타내는 중요한 도구로 사용된다.

웹 서버 생성하기

const http = require('http')

const PORT = 3000;

const server = http.createServer((req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/plain'
  });
  res.end('Hello!');
});

server.listen(PORT, () => {
  console.log(`Listening on port ${PORT}...`);
}); // 127.0.0.1 => localhost

코드 분석

주어진 자바스크립트 코드는 간단한 HTTP 서버를 생성하고, 해당 서버를 3000번 포트에서 실행하는 예제이다.

  1. 필요한 모듈을 불러온다.
const http = require('http');

이 줄은 Node.js의 내장 모듈인 http를 불러와서 http 변수에 할당한다. 이 모듈을 사용하여 HTTP 서버를 만들 수 있다.

  1. 사용할 포트 번호를 정의한다.
const PORT = 3000;

서버를 시작할 때 사용할 포트 번호인 3000을 상수 PORT에 할당한다.

  1. HTTP 서버를 생성한다.
const server = http.createServer((req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/plain'
  });
  res.end('Hello!');
});

http.createServer 메서드는 HTTP 서버를 생성한다. 이 메서드에는 요청이 들어올 때마다 호출되는 콜백 함수를 인수로 전달한다.

  • req: 클라이언트로부터의 요청 정보를 담고 있는 객체이다.
  • res: 서버의 응답을 구성하기 위한 객체이다.

res.writeHead는 HTTP 응답의 상태 코드와 응답 헤더를 설정한다. 여기서는 200 상태 코드(성공)와 'text/plain' 콘텐트 타입을 설정했다.

res.end는 응답 본문과 함께 응답을 종료한다. 여기서는 "Hello!"라는 문자열로 응답한다.

  1. 서버를 지정된 포트에서 실행한다.
server.listen(PORT, () => {
  console.log(`Listening on port ${PORT}...`);
});

server.listen 메서드를 사용하여 서버를 시작한다. 첫 번째 인수로는 포트 번호를, 두 번째 인수로는 서버가 시작되면 호출되는 콜백 함수를 전달한다. 여기서는 서버가 시작되면 콘솔에 메시지를 출력하도록 했다.

결과적으로, 이 코드를 실행하면 3000번 포트에서 "Hello!"라는 문자열을 반환하는 HTTP 서버가 시작된다.

CreateServer 메소드

  • http.createServer() 메소드는 server 객체를 생성한다.
  • server 객체는 EventEmitter를 기반으로 만들어졌다. server.on('request', 콜백함수);
  1. EventEmitter: Node.js의 많은 핵심 객체들은 이벤트를 발생시키기 위해 EventEmitter 클래스를 사용한다. http.Server 객체도 그 중 하나이다. 이 말은 http.Server 객체가 다양한 이벤트를 발생시킬 수 있고, 그 이벤트에 대한 리스너를 추가할 수 있다는 것을 의미한다.

  2. Request 이벤트: 가장 흔히 사용되는 이벤트는 'request' 이벤트이다. 이 이벤트는 클라이언트로부터 HTTP 요청이 들어올 때마다 발생한다.
    아래의 http.createServer() 예제에서 전달한 콜백 함수는 내부적으로 'request' 이벤트에 대한 리스너로 등록되는 것과 같다.

server.on('request', (req, res) => {
  // 요청 처리 로직
});
  1. 기타 이벤트: 'request' 외에도 http.Server 객체는 여러 가지 이벤트를 발생시킨다. 몇 가지 예를 들면 다음과 같다.

    • 'close': 서버가 종료될 때 발생한다.
    • 'connection': 새로운 연결이 생성될 때 발생한다.
    • 'error': 서버에서 오류가 발생할 때 발생한다.
  2. 서버 객체의 메서드: 생성된 server 객체에는 이벤트 리스너를 추가하는 것 외에도 다양한 메서드가 있다. 예를 들면 다음과 같다.

    • server.listen(): 서버를 시작하는 메서드이다.
    • server.close(): 서버를 중지하는 메서드이다.
  3. 종료 메서드와 관련된 주의 사항: server.close() 메서드를 호출하면 서버는 새로운 연결을 수락하지 않지만, 현재 처리 중인 연결은 종료되지 않는다. 모든 연결이 종료될 때 'close' 이벤트가 발생한다.

  4. Backlog: server.listen() 메서드의 세 번째 인자로 backlog 값을 지정할 수 있다. 이는 연결 대기열의 최대 길이를 의미하며, 기본값은 OS에 따라 결정된다. 연결 대기열이 가득 차면 추가 요청은 거부된다.

이와 같이 http.createServer() 및 반환되는 server 객체는 다양한 이벤트와 메서드를 제공하므로, Node.js의 공식 문서를 참조하면 더 많은 정보와 세부사항을 얻을 수 있다.

Server 객체

  • server 객체는 컴퓨터의 포트를 수신하고 요청이 만들어질 때마다 requestListener라는 함수를 실행할 수 있다.
  • server.listen() => 서버 실행, server.close() = > 서버 종료
  • server 객체는 EventEmitter를 기반으로 만들어졌다.
  • server.on('request', 콜백 함수), server.on('connection', 콜백 함수) ...(close, upgrade...)
  • HTTP 서버 객체는 컴퓨터의 포트를 수신하고 요청이 만들어질 때마다 requestListener라는 함수를 실행할 수 있다.
  1. server.address(): 이 메서드는 서버가 바인딩된 주소와 포트 정보를 반환한다. 서버가 실행 중일 때 이 정보를 알아낼 수 있다.
const address = server.address();
console.log(`Server listening on ${address.address}:${address.port}`);
  1. server.setTimeout(msecs, callback): 서버의 타임아웃을 msecs 밀리초로 설정한다. 연결이 이 시간 동안 활성화되지 않으면 자동으로 종료된다. 선택적으로 타임아웃 시 실행될 callback 함수도 제공할 수 있다.

  2. server.timeout: 이 속성은 서버의 타임아웃 값을 가져오거나 설정하는 데 사용된다 (밀리초 단위). 기본값은 2분이다.

  3. server.keepAliveTimeout: 클라이언트와의 연결이 몇 밀리초 동안 유휴 상태로 유지될 수 있는지를 결정하는 값을 가져오거나 설정합니다. 기본값은 5초이다.

  4. server.maxHeadersCount: 요청의 최대 헤더 수를 제한하는데 사용될 수 있는 속성이다. 너무 많은 헤더가 전송될 경우, 이를 방지하기 위해 이 값을 설정할 수 있다.

  5. 'error' 이벤트: 이미 언급되었지만, 서버에서 발생하는 예외를 처리하기 위해 이 이벤트를 반드시 처리해야 한다. 그렇지 않으면 프로세스가 종료될 수 있다.

server.on('error', (error) => {
  console.error(`Server error: ${error.message}`);
});
  1. server.listening: Boolean 값으로, 서버가 현재 수신 중인지 여부를 반환한다.

이런 추가 기능들은 서버의 다양한 상황에서 필요에 따라 사용될 수 있다. http.Server 객체의 다양한 메서드와 속성에 대한 자세한 내용은 Node.js 공식 문서에서 확인할 수 있다.

RequestListener 함수

  • requestListener는 서버가 요청을 받을 때마다 호출되는 함수이다.
  • reqeustListener 함수는 사용자의 요처오가 사용자에 대한 응답을 처리한다.

requestListener 함수는 Node.js의 HTTP 서버를 생성할 때 사용되는 콜백 함수이다. 주로 http.createServer() 메서드의 인자로 전달되며, 서버가 클라이언트의 HTTP 요청을 수신할 때마다 호출된다.

requestListener 함수의 형태는 다음과 같다.

function requestListener(req, res) {
  // 요청 처리 로직
}

여기서:

  • req: IncomingMessage 인스턴스로, 클라이언트로부터의 요청 정보와 관련된 다양한 속성과 메서드를 포함하고 있다. 이 객체를 통해 요청 헤더, 요청 URL, HTTP 메서드 등의 정보를 알 수 있다.

  • res: ServerResponse 인스턴스로, 서버 응답과 관련된 다양한 속성과 메서드를 포함하고 있다. 이 객체를 통해 응답 헤더를 설정하거나 HTTP 상태 코드를 지정하거나 응답 본문을 작성할 수 있다.

예제

const http = require('http');

const server = http.createServer((req, res) => {
  // 클라이언트로부터의 요청 URL을 로그로 출력
  console.log(`Received request for ${req.url}`);

  // 응답 설정
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello, World!');
});

server.listen(3000);

위의 예제에서 http.createServer() 메서드에 전달된 함수가 requestListener이다. 클라이언트의 요청을 수신할 때마다 이 함수가 호출되며, 그 안에서 요청에 대한 처리 로직을 작성할 수 있다.

req

  • req(request), res(response) 객체는 노드가 전달해준다.
  • request객체는 IncomingMessage의 인스턴스 이다.
  • IncomingMessage객체는 서버에 대한 요청을 나타낸다.
destroy
headersReturns a key-value pair containing header names and values
httpVersionReturns the HTTP version sent by the client
methodReturns the request method
rawHeadersReturns an array of the request headers
requestTrailersReturns an array of the raw request trailer keys and value
setTimeout()Calls a specified function after a specified number of milleseconds
statusCodeReturns the HTTP response status code
socketReturns the Socket object for the connection
trailersReturns an object containing the trailesr
urlReturns the request URL string
  1. 'data' 이벤트: 요청의 본문 데이터가 수신될 때 발생한다. 주로 POST나 PUT 요청에서 사용되는 데이터를 처리할 때 사용된다.

  2. 'end' 이벤트: 요청 데이터의 수신이 완료되었을 때 발생한다. 이 이벤트가 발생했을 때 요청 처리를 완료하고 응답을 보내는 것이 일반적이다.

  3. 'close' 이벤트: 연결이 끊어졌을 때 발생한다.

  4. 'aborted' 이벤트: 클라이언트가 요청을 중지했을 때 발생한다.

  5. ;error' 이벤트: 요청 처리 중 오류가 발생할 때 발생한다.

res

  • ServerResponse객체는 requestListener함수의 두 번째 매개변수로 전달된다.
  • 클라이언트에 웹 페이지를 제공하기 위해 response객체를 사용한다.
addTrailersAdds HTTP trailing headers
end()Signals that the server should consider that the response is complete
finishedReturns true if the response is complete, otherwise false
getHeader()Returns the value of the specified header
headersSentReturns true if headers were sent, otherwise false
removeHeader()Removes the specified header
sendDateSet to false if the Date header should not be sent in the response. Default true
setHeader()Sets the specified header
setTimeoutSets the timeout value of the socket to the specified number of milliseconds
statusCodeSets the status code that will be sent to the client
statusMessageSets the status message that will be sent to the client
write()Sends text, or a text stream, to the client
wrtieContinue()Sends a HTTP Continue message to the client
writeHead()Sends status and response headers to the client

res 객체는 Node.js의 HTTP 서버에서 클라이언트에게 응답을 보낼 때 사용되는 http.ServerResponse의 인스턴스이다. 이 객체를 통해 응답 헤더를 설정하거나 HTTP 상태 코드를 지정하거나 응답 본문을 작성할 수 있다.

속성

  1. statusCode: 응답에 사용될 HTTP 상태 코드를 가져오거나 설정한다. 예: res.statusCode = 404;
  2. statusMessage: HTTP 상태 메시지를 가져오거나 설정한다. 예: res.statusMessage = "Not Found";
  3. headersSent: Boolean 값으로, 헤더가 클라이언트에게 이미 전송되었는지를 나타낸다.

메서드

  1. writeHead(statusCode, [statusMessage], [headers]): 응답의 상태 코드와 헤더를 설정한다.
res.writeHead(200, {'Content-Type': 'text/plain'});
  1. setHeader(name, value): 개별 응답 헤더를 설정한다.
res.setHeader('Content-Type', 'application/json');
  1. getHeader(name): 설정된 응답 헤더의 값을 가져온다.

  2. removeHeader(name): 특정 응답 헤더를 제거한다.

  3. write(chunk, [encoding], [callback]): 응답 본문에 데이터 청크를 작성한다. 여러 번 호출될 수 있다.

res.write('Hello, ');
res.write('World!');
  1. end([data], [encoding], [callback]): 응답을 종료한다. 선택적으로 데이터 청크를 추가할 수 있다.
res.end('Goodbye!');

7. addTrailers(headers): 응답의 끝 부분에 HTTP 트레일러를 추가한다. HTTP 트레일러는 본문 데이터가 전송된 후에 전송되는 헤더이다.

  1. flushHeaders(): 설정된 헤더와 상태 코드를 클라이언트에 즉시 전송한다.

이 외에도 res 객체는 여러가지 이벤트와 기타 메서드를 포함하고 있다. 원하는 동작을 수행하기 위해 적절한 메서드와 속성을 사용할 수 있다. Node.js의 공식 문서는 http.ServerResponse 객체의 모든 메서드와 속성에 대한 상세한 정보를 제공하므로 참고하면 좋다.

서버에서 클라이언트로 텍스트를 보내는 것이 아닌, javascript object로 보내려면?

Content Type을 변경해 주면 된다.

const http = require('http')
const port = 4000;
const server = http.createServer((req, res) => {
 res.writeHead(200, {
   'Content-Type': 'application/json'
 });
 res.end(JSON.stringify({ a: "a", b: "b");
});

server.listen(port, () => {
   console.log(`Listening on port ${port}`);
 })
  1. const http = require('http'):
    • Node.js 내장 http 모듈을 가져옵니다. 이 모듈은 HTTP 서버 및 클라이언트 기능을 제공한다.
  2. const port = 4000:
    • 서버가 클라이언트의 요청을 수신 대기할 포트 번호 4000을 port 변수에 저장한다.
  3. http.createServer((req, res) => { ... }):
    • http.createServer는 HTTP 서버를 생성하는 함수다.
    • 콜백 함수 (req, res) => { ... }는 클라이언트의 요청이 수신될 때마다 호출된다.
  4. res.writeHead(200, { 'Content-Type': 'application/json' }):
    • 응답의 HTTP 상태 코드를 200 (OK)으로 설정하며, Content-Type 헤더를 application/json으로 설정한다. 이렇게 하면 서버가 JSON 형식의 데이터를 응답할 것임을 나타낸다.
  5. res.end(JSON.stringify({ a: "a", b: "b" })):
    • { a: "a", b: "b" } 객체를 JSON 형식의 문자열로 변환하여 응답 본문으로 전송하고 응답을 종료한다.
  6. server.listen(port, () => { ... }):
    • 서버를 시작하여 4000 포트에서 클라이언트의 연결을 수신 대기한다.
    • 서버가 시작되면 콜백 함수를 호출하여 콘솔에 "Listening on port 4000"라는 메시지를 출력한다.

HTTP Routing

HTTP 라우팅은 웹 애플리케이션에서 중요한 개념이다. 라우팅은 클라이언트의 HTTP 요청을 해당 요청에 대한 적절한 핸들러나 미들웨어로 전달하는 메커니즘이라고 볼 수 있다. 다시 말해, 라우팅은 특정 URL 경로나 HTTP 메서드를 기반으로 요청을 처리하도록 결정한다.

특징과 개념

  1. URL 경로와 HTTP 메서드 기반
    대부분의 웹 프레임워크는 URL의 경로와 HTTP 메서드 (GET, POST, PUT, DELETE 등)를 기반으로 라우팅을 수행한다.
    예를 들어, /users 경로의 GET 요청은 사용자 목록을 가져오는 코드로 라우팅될 수 있고, /users 경로의 POST 요청은 새 사용자를 생성하는 코드로 라우팅될 수 있다.
  2. 동적 라우팅
    많은 웹 프레임워크는 동적 라우팅을 지원한다. 이는 URL의 특정 부분을 변수로 취급하여, 그 값을 후속 처리 과정에서 사용할 수 있게 한다.
    예를 들어, /users/:id 경로는 /users/123 또는 /users/john과 같은 다양한 URL에 일치하며, id 값은 핸들러 내에서 사용될 수 있다.
  3. 미들웨어:
    라우팅 처리 과정 중에 다수의 함수 또는 "미들웨어"를 연결할 수 있다. 각 미들웨어는 요청 및 응답 객체에 대한 작업을 수행하고, 다음 미들웨어로 처리를 전달할 수 있다.
    예를 들어, 인증 미들웨어는 사용자가 인증되었는지 확인하고, 로깅 미들웨어는 요청에 대한 로그를 기록할 수 있다.
  4. 404 또는 오류 처리:
    라우터는 일치하는 경로나 핸들러가 없는 경우의 처리도 관리한다. 일반적으로 404 "Not Found" 응답을 반환하거나 특정 오류 페이지를 표시하는 핸들러로 라우팅된다.
    또한, 핸들러에서 발생하는 오류를 잡아서 사용자에게 적절한 오류 메시지를 반환하는 기능도 있다.

Node.js의 Express.js와 같은 웹 프레임워크는 이러한 라우팅 기능을 제공하며, 강력하고 유연한 라우팅 옵션을 제공한다.

예제

const server = http.createServer((req, res) => {
  
  if (req.url === '/home') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify({
      a: "a",
      b: "b"
    }));
    
  } else if (req.url === '/about') {
    res.setHeader('Content-Type', 'text/html');
    res.write('<html>');
    res.write('<body>');
    res.write('<h1>About Page</h1>');
    res.write('</body>');
    res.write('</html>');
    res.end();
  } else {
    res.statusCode = 404;
    res.end();
  }
});

분석

주어진 코드는 Node.js의 내장 http 모듈을 사용하여 간단한 HTTP 서버의 동작을 구현한 것이다. 이 서버는 요청 URL에 따라 다르게 응답한다.

  1. 서버 생성:
const server = http.createServer((req, res) => {
  ...
});
  • http.createServer 함수를 사용해 서버를 생성한다. 클라이언트의 HTTP 요청이 들어올 때마다 제공된 콜백 함수가 호출된다.
  1. /home 경로 처리
if (req.url === '/home') {
  ...
}
  • 클라이언트의 요청 URL이 '/home'인 경우, 해당 블록 내의 코드가 실행된다:
    • HTTP 응답 상태 코드를 200 (OK)으로 설정한다.
    • 응답 헤더 Content-Type을 application/json으로 설정하여 JSON 데이터를 반환할 것임을 알린다.
    • 응답 본문으로 JSON 형식의 { a: "a", b: "b" } 객체를 전송한다.
  1. /about 경로 처리
else if (req.url === '/about') {
  ...
}
  • 클라이언트의 요청 URL이 '/about'인 경우, 해당 블록 내의 코드가 실행된다:
    • 응답 헤더 Content-Type을 text/html로 설정하여 HTML 문서를 반환할 것임을 알린다.
    • HTML 문서를 작성하여 응답 본문에 추가한다.
    • 응답을 종료한다.
  1. 그 외의 경로 처리
else {
  ...
}
  • 위에서 정의한 /home과 /about 외의 다른 URL로 요청이 들어온 경우, 해당 블록 내의 코드가 실행된다:
    • HTTP 응답 상태 코드를 404 (Not Found)로 설정한다.
    • 별도의 응답 본문 없이 응답을 종료한다.

POST 요청으로 데이터 추가하기

const http = require('http');
const port = 4000;
const targetObject = { a:"a", b:"b" };
const server = http.createServer((req, res) => {
  if(req.method === 'POST' && req.url === '/home') {
    req.on('data', (data) => {
      console.log(data);
      const stringfiedData = data.toString();
      console.log(stringfiedData);
      Object.assign(targetObject, JSON.parse(stringfiedData))
    })
  } else {
    if(req.url === '/home') {
      res.writeHead(200, {
        'Content-Type': 'application/json'
      });
      res.end(JSON.stringify({ a: "a", b: "b" }));
    } else if(req.url === '/about') {
      res.setHeader('Content-Type', 'text/html');
      res.write('<html>');
      res.write('<body>');
      res.write('<h1>About Page</h1>');
      res.write('</body>');
      res.write('</html>');
      res.end();
    } else {
      res.statusCode = 404;
      res.end();
  }
});
fetch('http://localhost:4000/home', { method: 'POST', body: JSON.stringify({ c : "c" }));

서버 코드

  1. 모듈과 상수 선언:
    • 필요한 모듈 http를 require로 불러온다.
    • 서버 포트와 타겟 객체 targetObject를 초기화한다.
  2. 서버 생성
    • http.createServer 메소드를 사용하여 서버를 생성한다.
const server = http.createServer((req, res) => { ... });
  1. POST 요청 처리
    • 요청이 POST 메서드이고 URL이 '/home'일 때:
      • 요청 본문(data)를 받기 위해 'data' 이벤트 리스너를 설정한다.
      • 받은 데이터를 문자열로 변환하고 콘솔에 출력한다.
      • 데이터를 JSON으로 파싱하고 Object.assign을 사용하여 targetObject에 병합한다.
if (req.method === 'POST' && req.url === '/home') { ... }
  1. GET 요청 처리
    • /home 경로: JSON 객체를 응답으로 반환한다.
    • /about 경로: HTML로 이루어진 "About Page"를 응답으로 반환한다.
    • 그 외의 경로: 404 상태 코드를 반환한다.

클라이언트 코드

fetch('http://localhost:4000/home', { method: 'POST', body: JSON.stringify({ c : "c" })});
  • fetch API를 사용하여 http://localhost:4000/home 주소로 POST 요청을 보낸다.
  • 요청 본문(body)로 { c: "c" }라는 JSON 데이터를 전송한다.

이 클라이언트 코드를 실행하면, 서버는 '/home' 경로에서 POST 요청을 받아들이고, 요청 본문에 있는 데이터를 targetObject에 병합한다. 결과적으로 targetObject는 { a: "a", b: "b", c: "c" }가 된다.

파싱?

"파싱(parsing)"은 일련의 문자열 또는 바이트를 의미 있는 데이터 구조나 객체로 변환하는 과정을 의미한다. 반대로, 의미 있는 데이터 구조나 객체를 문자열 또는 바이트로 변환하는 과정을 "직렬화(serialization)"라고 한다.

JSON에 관련하여 파싱과 직렬화는 다음과 같이 이해할 수 있다.

  1. JSON 파싱
    • 문자열을 객체로:
      문자열 형태의 JSON 데이터를 JavaScript 객체나 배열로 변환하는 과정이다. JavaScript에서는 JSON.parse() 메소드를 사용하여 이 작업을 수행한다.
const jsonString = '{"key": "value"}';
const jsonObject = JSON.parse(jsonString);
console.log(jsonObject.key);  // 출력: value
  1. JSON 직렬화
    • 객체를 문자열로:
      JavaScript 객체나 배열을 문자열 형태의 JSON 데이터로 변환하는 과정이다. JavaScript에서는 JSON.stringify() 메소드를 사용하여 이 작업을 수행한다.
const jsonObject = { key: "value" };
const jsonString = JSON.stringify(jsonObject);
console.log(jsonString);  // 출력: {"key":"value"}

간단히 말해, JSON 파싱은 JSON 형태의 문자열을 JavaScript 객체로 변환하는 것이며, JSON 직렬화는 JavaScript 객체를 JSON 형태의 문자열로 변환하는 것이다.

profile
초보개발자. 백엔드 지망. 2024년 9월 취업 예정

0개의 댓글