Node.js에서 PostgreSQL 연결

이 글은 아래의 포스트 내용에 대해 DBMS를 이용해 재작성되었습니다.

Node.js의 GET, POST 처리

위 글에 대해서 클라언트의 코드는 모두 동일하고 서버의 코드만 변경되었습니다. 서버에 대한 전체 코드는 다음과 같습니다.

const express = require("express");
const app = express();

const { Client } = require("pg");

const dbClient = new Client({
    user: "postgres",
    host: "localhost",
    database: "postgres",
    password: "****",
    port: 5432
});

dbClient.connect();

app.get("/names", (req, res) => {
    dbClient.query("SELECT name FROM names", (error, result) => {
        if(error) {
            res.sendStatus(500);
        } else {
            res.status(200).json(result.rows);
        }
    });    
});

app.get("/alias", (req, res) => {
    const name = req.query.name;

    dbClient.query(`SELECT alias FROM names WHERE name = '${name}'`, (error, result) => {
        if(error) {
            res.sendStatus(500);
        } else {
            res.status(200).json(result.rows);
        }
    });
});

app.use(express.json());

app.post("/add", (req, res) => {
    const item = req.body;

    if(item.name && item.alias) {
        dbClient.query(`INSERT INTO names (name, alias) VALUES ('${item.name}', '${item.alias}');`, 
            (error, result) => {
                if(result) {
                    console.log(`Changed Row Count ${result.rowCount}`);
                    res.sendStatus(200);
                } else {
                    res.sendStatus(500);           
                }
            }
        );
    } else {
         res.sendStatus(400);
    }    
});

app.use(express.static(__dirname + "/static"));
app.listen(3000);

위의 코드에서 만약 DBMS에 대한 Connection Pool을 도입하고자 한다면 4~12번 코드를 다음처럼 변경하기만 하면 됩니다.

const { Pool } = require("pg");

const dbClient = new Pool({
    user: "postgres",
    host: "localhost",
    database: "postgres",
    password: "3224",
    port: 5432
});

Node.js의 GET, POST 처리

먼저 express를 이용해 다음과 같이 서버를 생성합니다.

const express = require("express");
const app = express();

const table = [
    { name: "Jack", alias: "Monkey" },
    { name: "Shelly", alias: "Cat" },
    { name: "Toms", alias: "Dog" }
];

// 여기에 GET, POST를 처리하는 코드가 추가됨

app.use(express.static(__dirname + "/static"));
app.listen(3000);

4번 코드의 table은 DB 대신 사용하는 데이터 객체입니다. 그리고 10번 코드는 static이라는 디렉토리에 test.html 파일을 넣을 것인데.. 이를 웹브라우저에서 127.0.0.1:3000/test.html 처럼 접근할 수 있도록 하기 위함입니다. 이 test.html 파일이 우리가 만들 서버에 GET, POST 요청을 하는 클라이언트에 해당하는 코드입니다.

가장 먼저 table에 저장된 사용자의 name 목록을 얻어오는 names GET 처리에 대한 코드는 다음과 같습니다. 코드 추가는 10번에 하면 됩니다.

app.get("/names", (req, res) => {
    const names = table.map(item => {
        return { name: item.name };
    });
    res.status(200).json(names);
});

test.html에 입력할 클라이언트 코드는 다음과 같구요.

const url = "http://127.0.0.1:3000/names";
fetch(url, {
    method: "GET"
}).then(response => {
    return response.text();
}).then(text => {
    const data = JSON.parse(text);
    console.log(data);
}).catch(error => {
    console.warn(error);
});

웹브라우저의 콘솔에 표시되는 실행 결과는 다음과 같습니다.

(3) [{…}, {…}, {…}]
0: {name: 'Jack'}
1: {name: 'Shelly'}
2: {name: 'Toms'}
...

이번에는 Query String을 갖는 GET 요청입니다. 이름을 받아 별칭을 그 결과로 전달하는 처리에 대한 서버 코드입니다.

app.get("/alias", (req, res) => {
    const name = req.query.name;
    const result = table.find((item) => {
        return item.name === name;
    });

    if(result) {
        res.status(200).json({ alias: result.alias });
    } else {
        res.status(200).json({});
    }
});

클라이언트 코드는 다음과 같습니다.

const url = "http://127.0.0.1:3000/alias?name=Suzan";
fetch(url, {
    method: "GET",
}).then(response => {
    return response.text();
}).then(text => {
    const data = JSON.parse(text);
    console.log(data);
}).catch(error => {
        console.warn(error);
});

결과는 다음과 같습니다.

{alias: 'Cat'}

이제 POST 호출입니다. 서버에 새로운 사람을 추가하는 서버측 코드입니다.

app.use(express.json());

app.post("/add", (req, res) => {
    const item = req.body;

    if(item.name && item.alias) {
        table.push(item);
        res.sendStatus(200);
    } else {
        res.sendStatus(400);
    }
});

위의 서비스를 이용하는 클라이언트 코드는 다음과 같습니다.

const url = "http://127.0.0.1:3000/add";
fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: "Suzan", alias: "고슴도치" })
}).then(response => {
    console.log(response.ok);
}).catch(error => {
    console.warn(error);
});

Node.js 새로운 프로젝트 생성

npm init -y
npm i express
npm i --save-dev nodemon
npm run devStart

위의 devStart가 의미를 가지려면 package.json 파일의 “scripts”의 값을 다음처럼 변경해야 합니다.

"scripts": {
  "devStart": "nodemon server.js"
},

Node.js에서 Thread 사용

서버 개발 시에 Node.js를 사용할 때 스레드를 이용해 사용자의 요청을 동시에 처리할 수 있습니다.

먼저 서버에 대한 기본 코드입니다.

const express = require("express");
const app = express();

app.get("/", (req, res) => {
    let total = 0;
    for(let i=0; i<10000000000; i++) {
        total++;
    }

    res.status(200).json({total});
});

app.listen(3000);

웹브라우저에서 localhost:3000으로 접속하면 몇초간 응답이 없다가 결과가 표시됩니다. 이 몇초간의 무응답은 서버의 모든 것들이 이 하나의 클라이언트 요청을 처리하는데 전념한다는 점입니다. 이 몇초간 또 다른 클라이언트의 요청이 들어온다면... 서버는 응답은 커녕 듣지도(listen) 못합니다.

이 문제를 개선하기 위한 코드를 살펴보면.. 먼저 위의 코드를 다음처럼 변경합니다.

const express = require("express");
const app = express();
const { Worker } = require("worker_threads");

app.get("/", (req, res) => {
    const worker = new Worker("./worker.js");
    worker.on("message", (data) => {
        res.status(200).json({total: data});
    })
});

app.listen(3000);

worker_threads라는 모듈을 추가했고 클라이언트 요청에 대한 연산을 worker.js로 분리시켰습니다. worker.js가 바로 스레드가 수행하는 코드이고 완료되면 message라는 이벤트가 호출되어 그 결과가 전달됩니다. worker.js의 코드는 다음과 같습니다.

const { parentPort } = require("worker_threads");

let total = 0;
for(let i=0; i<10000000000; i++) {
    total++;
}

parentPort.postMessage(total);

무거운 연산이 완료되면 parentPort.postMessage를 통해 결과를 전달합니다.