[TIL] 20210503_Server 예제

18631 단어 TILserverTIL

Server 예제

최근 진행 중인 chatterbox 스프린트에 관련하여 TIL을 작성하려고 합니다.
저에겐 아직 쉽지 않은 과정이기에 천천히 그 과정을 밟아보겠습니다.

먼저 구현된 화면입니다.
서버를 이해하기 위한 스프린트이기 때문에 디자인을 따로 수정하지 않았습니다.
하지만 욕심이 생기면 수정할 계획입니다.

아래의 코드는 값을 입력받아 그 값을 파일에 저장하는 코드 중 일부입니다.

변수에 데이터를 저장하는 경우에는, 서버가 꺼지게 되면 데이터도 초기화됩니다.
하지만 파일에 데이터를 저장하는 경우, 서버가 꺼져도 그 값이 외부 파일에 저장되어 있어서 데이터가 보존됩니다.
외부 파일이 데이터베이스 역할을 하는 것입니다.

주석을 통해 코드를 따라가보도록 하겠습니다.


파일 읽기

파일을 읽는 함수는 따로 정의해두었습니다.
fs 모듈에 해당하는 메서드들은 모두 비동기적으로 처리되기 때문입니다.
파일을 읽고(readFile), 해당 파일을 수정(writeFile)을 해야 할 경우에는 두 메서드가 동기적으로 처리되어야 합니다.
그래서 읽는 함수를 따로 정의를 해두었고, 필요할 때 가져다 사용할 수 있도록 처리했습니다.

const fs = require('fs'); // 파일 조회, 수정 등의 작업을 가능하게 하는 fs 모듈입니다.

// 파일 읽는 함수입니다. Promise로 구현했습니다.
const getDataFromFilePromise = filePath => {
  return new Promise((res, rej) => {
    fs.readFile(filePath, 'utf8', (err, data) => { // readFile의 파라미터로 파일경로, 인코딩(보통 'utf8'), callback함수가 들어갑니다.
      if (err) rej(err);
      else res(data.toString()); // 에러 발생 시, reject함수를 실행시키고, 정상작동 시 response함수에 데이터를 담아 실행시킵니다.
    })
  })
}

요청에 따른 분기

requestHandler 함수를 선언합니다.
이 함수는 request와 response를 인자로 받아 요청에 대한 응답을 처리합니다.
이 함수 안에서 요청에 따른 분기 처리를 합니다.

GET 요청일 시

위에서 따로 구현한 파일 읽기 함수를 호출합니다.

const requestHandler = function (request, response) {
  // OPTIONS 요청일 시에 200 상태코드와 함께 헤더를 응답메시지에 입력합니다.
  if (request.method === 'OPTIONS') {
    response.writeHead(200, defaultCorsHeaders); // cors 정보를 헤더에 추가해주어야 합니다.
    response.end(defaultCorsHeaders.toString());

    // GET 요청일 때입니다.
  } else if (request.method === 'GET' && request.url === '/messages') {
    // Promise Chaining을 통해 읽은 파일의 정보를 가져와 응답객체에 전달해줍니다. (파일은 json형식으로 저장되어 있습니다.)
    getDataFromFilePromise('server/data.json')
      .then((data) => {
        response.writeHead(200, defaultCorsHeaders); // GET 요청일 시에도 cors 정보를 헤더에 추가해주어야 합니다.
        response.end(data); 
    })

POST 요청일 시

개인적으로 POST 요청을 처리할 때가 쉽지 않았습니다.
아직은 낯선 Buffer의 개념과 처음 보는 개념들(on, 'data', 'end')이 등장했기 때문입니다.
Buffer 관련해서는 따로 포스팅을 할 예정입니다.

    // POST 요청일 때입니다.
  } else if (request.method === 'POST' && request.url === '/messages') {
    let body = [];
    request.on('data', chunk => { // 'body'일 시에 
      body.push(chunk);
    }).on('end', () => { // 'end'일 시에 전달받은 Buffer와 body를 더해 문자열로 변환합니다.
      body = Buffer.concat(body).toString();
      
      getDataFromFilePromise('server/data.json') // 위에서 정의한 파일 읽기 함수를 호출하고, Promise chaining을 통해 파일의 내용을 불러옵니다.
      
        .then((data) => { // readFile, writeFile 두 메서드 모두 비동기적으로 작동하기 때문에 무엇이 먼저 완료될 지 모릅니다.
                          // 그렇기 때문에 이렇게 promise chainig으로 읽기와 쓰기 처리를 해준다면, 읽기와 쓰기의 순서가 명확해집니다.
        
        // writeFile의 파라미터로는 파일 경로, 수정할 내용, callback 함수가 들어갑니다.
        fs.writeFile('server/data.json', '{"results":  ['+body+','+data.slice(13), (err, data) => { // 파일은 json 형식으로 저장이 되어 있는데, json을 어떻게 효율적으로 수정해야 할 지 몰라 string을 수정하듯이 수정했습니다.
          if (err) throw err;
          response.writeHead(201, defaultCorsHeaders); // POST 요청일 시에도 cors 정보를 헤더에 추가해주어야 합니다.
          response.end(JSON.stringify(data)); 
        });
      })
    })

    // 예외처리입니다. bad request일 경우에 404 status code를 반환해줍니다.
  } else {
    response.writeHead(404);
    response.end();
  }
};

CORS Header

const defaultCorsHeaders = {
  "access-control-allow-origin": "*", // 접근 가능한 origin 주소를 입력합니다. *(asterisk)는 모든 origin 주소를 허용합니다.
  "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS",
  "access-control-allow-headers": "content-type, accept",
  "access-control-max-age": 10 // CORS 요청의 유효 시간입니다. 요청을 보내고 10초가 지나면 다시 CORS 요청을 보냅니다.
};

module.exports = requestHandler;

저장된 데이터의 모습입니다.
위에서 언급했듯이, 데이터는 파일에 저장이 되고, 서버가 종료되어도 데이터는 외부 파일에 보존되어 있습니다.
편의상 캡쳐를 하지 않고 직접 입력하겠습니다.
data.json파일에는 일렬로 저장되어 있습니다. ex) {"results":[{"username":"server","text":"hell"},...

// data.json
{
  "results": [
    {"username":"server","text":"hell"}, // 추가된 데이터
    {"username":"서반석","text":"my name is bannybanny"}, // 추가된 데이터
    {"username":"banseok", "text":"hello banny"},
    {"username":"a", "text":"hello A"},
    {"username":"b", "text":"hello B"}
  ]
}

짚고 넘어가기

  1. JSON 형식으로 파일이 저장되어 있는데... 파일에 저장되어 있는 JSON을 어떻게 효율적으로 다룰 수 있는지 공부해야겠습니다.
  2. JSON.stringify(), JSON.parse(), a.toJSON(). a.json() 메서드들의 기능을 분명히 알고 있어야겠습니다. 곧 포스팅하도록 하겠습니다.
  3. JSON 자체에 대해서도 공부가 더 필요해 보입니다.

좋은 웹페이지 즐겨찾기