[Node.js] HTTP Transaction 공식문서 정리 & SPRINT-Node.js(mini) 💯️

jungeundelilahLEE·2020년 12월 26일
0

Node.js

목록 보기
12/27

goal

SPRINT

  • http 서버 만들기
  • 각 요청 routing하기
  • routing에 따른 API제공하기
  • 서버를 활용하기 위한 API 문서 작성하기

HTTP Transaction

  • 요청 핸들러 함수로 HTTP 서버의 인스턴스를 생성하고 특정 포트로 서버를 열 수 있습니다.
  • request 객체에서 헤더, URL, 메서드, 바디 데이터를 가져올 수 있습니다.
  • URL이나 request 객체의 데이터에 기반을 둬서 라우팅을 할 수 있습니다.
  • response 객체로 헤더, HTTP 상태 코드, 바디 데이터를 보낼 수 있습니다.
  • request 객체에서 response 객체로 데이터를 파이프로 연결할 수 있습니다.
  • request와 response 스트림 모두에서 스트림 오류를 처리할 수 있습니다.

basic 💯️

  • http서버란?
    : http protocol을 통해 통신해서 API를 제공하는 주체

  • routing
    : 조건에 따라 분기한다

👉️ GET/POST 요청에서 url에따라 즉, 클라이언트의 요청에 따라 routing하고, 그 요청에 따라 서버는 필요한 데이터를 만든다


  • 데이터는 저장, 불러오기가 가능해야한다. => (일단은) JS의 object나 array에 저장할 수 있다. (=> 이 경우, 서버를 끄면 사라진다. => node.js의 filesystem을 통해서 서버를 꺼도 보존할 수 있다.(db를 통해서)(later))

  • API 문서 작성하기

    1. API 사용법 (method, router, etc..)
    2. 기대되는 return date형식 및 예제

미니 서버 만들기 - SPRINT 💯️

const http = require('http'); // http module

const PORT = 5000;

const ip = 'localhost';

const server = http.createServer((req, res) => {
  
  if (req.method === 'POST') {
    
    if (req.url === '/lower') {
      let data = '';
      req.on('data', chunk => {
        data = data + chunk;
      });
      req.on('end', () => {
        data = data.toLowerCase();
        res.writeHead(201, defaultCorsHeader);
        res.end(data);
      });
    } 
    
    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') {
    res.writeHead(200, defaultCorsHeader);
    res.end();
  }
  
});

server.listen(PORT, ip, () => {
  console.log(`http server listen on ${ip}:${PORT}`);
});

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
};



- Node.js 공식문서는 👉️ 여기를 참고
- HTTP header의 종류 및 항목에 대한 블로그는 👉️ 여기를 참고

HTTP Transaction 공식문서 💯️

서버생성

  • 모든 node 웹 서버 애플리케이션은 웹 서버 객체를 생성해야 한다.
  • createServer로 서버 생성- 이 서버로 오는 HTTP 요청마다 createServer에 전달된 함수가 한 번씩 호출된다.
  • HTTP 요청이 서버에 오면, node가 requestresponse 객체를 전달하며 요청 핸들러 함수를 호출한다. (이 요청을 실제로 처리하기 위해서는 listen메서드가 server객체에서 호출되어야 한다.)

method, URL, headers

  • 요청을 처리할 때, METHOD와 URL을 확인한 후, (node가 이미 유용한 프로퍼티를 넣어둔) request객체를 통해 적절한 작업을 실행한다.
    • request객체는 ImcomingMessage의 인스턴스다.
  • method : 일반적인 http메서드(http verbs)
  • url : 전체 url에서 서버, 프로토콜, 포트를 제외한 것
  • headers : request객체에 headers라는 전용 객체가 있다.

요청바디

  • 핸들러에 전달된 request 객체는 ReadableStream인터페이스를 구현한다. > 이 스트림에 이벤트리스너를 등록하거나, 다른 스트림에 파이프로 연결할 수 있다.
let data = '';
      req.on('data', chunk => {
        data = data + chunk;
      });
      req.on('end', () => {
        data = data.toLowerCase();
        res.writeHead(201, headers);
        res.end(data);
      });
  • (req를 일종의 스트림으로 보자. 이 스트림에 on 메소드로 이벤트를 등록했다.)
  • 스트림의 dataend 이벤트에 이벤트 리스너를 등록해서 데이터를 받을 수 있다.
  • 이벤트 관련 메서드는 👉️ 여기를 참고
  • data이벤트에서 발생시킨 청크는 Buffer이다.
  • (이 청크가 문자열 데이터라는 것을 알고 있다면 이 데이터를 배열에 수집한 다음 end 이벤트에서 이어 붙인 다음 문자열로 만드는 것이 가장 좋다.)
  • npm에 **concat-streambody** 같은 모듈로 이 로직을 감출 수 있다.

=> 이벤트 종류 찾아보기

오류에 대한 간단한 설명

  • request 객체가 ReadableStream이므로 EventEmitter이기도 하고 오류가 발생했을 때 EventEmitter처럼 동작한다.
  • request 스트림의 오류가 발생하면 스트림에서 error 이벤트가 발생하면서 오류를 전달한다.
  • (이벤트에 리스너가 등록되어 있지 않다면 Node.js 프로그램을 종료시킬 수도 있는 오류를 던질 것. 따라서 단순히 오류를 로깅만 하더라도 요청 스트림에 error 리스너를 추가해야 한다.)
    (하지만 HTTP 오류 응답을 보내는 것이 좋을 것.)
request.on('error', (err) => {
  // 여기서 `stderr`에 오류 메시지와 스택 트레이스를 출력합니다.
  console.error(err.stack);
});

request 요청 관련 종합

const http = require('http');

http.createServer((request, response) => {
  const { headers, method, url } = request;
  let body = [];
  request.on('error', (err) => {
    console.error(err);
  }).on('data', (chunk) => {
    body.push(chunk);
  }).on('end', () => {
    body = Buffer.concat(body).toString();
    // 여기서 헤더, 메서드, url, 바디를 가지게 되었고
    // 이 요청에 응답하는 데 필요한 어떤 일이라도 할 수 있게 되었습니다.
  });
}).listen(8080); // 이 서버를 활성화하고 8080 포트로 받습니다.

👉️ 위의 예제는 요청(request)만 받을 수 있고, 응답(response)하지는 않는다.

response 객체

  • response 객체는 ServerResponse의 인스턴스 && WritableStream이다.
  • 이 객체에는 client에게 데이터를 응답해주기 위한 여러가지 유용한 메서드가 있다.

HTTP 상태코드

  • 따로 설정하지 않으면 응답의 HTTP상태코드는 디폴트로 200이다.
  • 상태 코드를 변경하려면 statusCode프로퍼티를 설정해야 한다.
response.statusCode = 404; // 클라이언트에게 리소스를 찾을 수 없다고 알려줍니다.

응답 헤더 설정

  • setHeader메서드로 헤더를 설정한다.
  • 헤더이름의 대/소문자는 중요치 않으며, 헤더를 여러 번 설정한다면, 마지막에 설정한 값을 보낸다.
response.setHeader('Content-Type', 'application/json');

명시적인 헤더 데이터 전송

  • statusCode, setHeader는 바디 데이터를 보내기 전 적절한 순간에 헤더를 보내는 일을 노드에 의존하고 있다.
  • 따라서, writeHead메서드를 통해 명시적으로 응답 스트림에 헤더를 작성할 수 있다.
  • writeHead메서드는 스트림에 상태코드와 헤더를 작성한다.
response.writeHead(200, {
  'Content-Type': 'application/json',
  'X-Powered-By': 'bacon'
});
  • 이렇게 헤더를 설정하고 나면, 응답 데이터를 전송할 준비를 마친 것이다!

응답 바디 전송

  • response 객체는 WritableStream이므로 클라이언트로 보내는 응답 바디는 일반적인 스트림 메서드를 사용해서 작성한다.
  • 스트림 메서드는 👉️ 여기를 참고
  • 스트림의 end함수에 스트림에 보낼 데이터의 마지막 비트를 선택적으로 전달할 수 있다.
response.end('<html><body><h1>Hello, World!</h1></body></html>');
  • 바디에 데이터 청크를 작성하기 전에 상태 코드와 헤더를 설정해야 한다.

오류에 대한 간단한 설명

  • request와 같다 (위의 내용 참고)

response 응답 관련 종합

// 클라이언트가 서버에 보낸 모든 데이터를 다시 보내는 서버
// JSON.stringify 를 이용하여 데이터를 JSON방식으로 formating한다.

http.createServer((request, response) => {
  const { headers, method, url } = request;
  let body = [];
  request.on('error', (err) => {
    console.error(err);
  }).on('data', (chunk) => {
    body.push(chunk);
  }).on('end', () => {
    body = Buffer.concat(body).toString();
   
    // from here
    response.on('error', (err) => {
      console.error(err);
    });

    response.statusCode = 200;
    response.setHeader('Content-Type', 'application/json');
    // 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다.
    // response.writeHead(200, {'Content-Type': 'application/json'})

    const responseBody = { headers, method, url, body };

    response.write(JSON.stringify(responseBody));
    response.end();
    // 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다.
    // response.end(JSON.stringify(responseBody))

    // 새로운 부분이 끝났습니다.
  });
}).listen(8080);

에코 서버 예제

  • 에코서버는 요청받은 데이터를 그대로 응답으로 돌려보내는 서버이다.
  • 요청 스트림에서 데이터를 가져와 응답 스트림에 쓰기만 하면 된다.
  • 예제) 아래의 조건에서 에코 응답 보내기
    • 요청 메서드가 POST인 경우
    • URL이 /echo인 경우
const http = require('http');

http.createServer((request, response) => {
  if (request.method === 'POST' && request.url === '/echo') {
    let body = [];
    request.on('data', (chunk) => {
      body.push(chunk);
    }).on('end', () => {
      body = Buffer.concat(body).toString();
      response.end(body);
    });
  } else {
    response.statusCode = 404;
    response.end();
  }
}).listen(8080);

👉️ 위의 예제에서 url을 검사하며, "라우팅(routing)"을 하고 있다. (switch문으로도 할 수 있으며, express와 같은 프레임워크로도 할 수 있다.)
👉️ request 객체는 ReadableStream이고 response 객체는 WritableStream이다. (=== 데이터를 한 스트림에서 다른 스트림으로 직접 연결하는 pipe를 사용할 수 있음을 의미)

const http = require('http');

http.createServer((request, response) => {
  if (request.method === 'POST' && request.url === '/echo') {
    request.pipe(response);
  } else {
    response.statusCode = 404;
    response.end();
  }
}).listen(8080);

👉️ THIS IS "Stream(스트림)!"


출처 : https://gmlwjd9405.github.io/2019/01/28/http-header-types.html 💚️

profile
delilah's journey

0개의 댓글