logo

DB 연동

데이터베이스 연동하기

46 조회

0 추천

1,301 단어

7분 예상

2025. 02. 08. 게시

2025. 02. 10. 수정

luasenvy 작성

CC BY-NC-SA 4.0

HTML 구현

DB와 연동하기 위해서 남아있는 create.html, update.html, detail.html 페이지를 구현한다.

create.html

<!DOCTYPE html>
<html>

<head>
  <title>연락처 등록</title>
  <meta charset="utf-8">

  <link rel="stylesheet" href="./style.css">
  <script src="./create.js"></script>
</head>

<body>
  <main>
    <h1>연락처 등록</h1>

    <form method="post" action="http://localhost:8000/api/contact">
      <input type="text" name="username" placeholder="이름" required>
      <input type="email" name="email" placeholder="이메일" required>

      <button type="submit">등록</button>
    </form>
  </main>
</body>

</html>

index.js

/** else...*/

case "POST": {
  let data = Buffer.from("");

  req.on("data", (chunk) => {
    data = Buffer.concat([data, chunk])
  });

  req.on("end", () => {
    const urlEncoded = new URLSearchParams(data.toString())
    const body = Object.fromEntries(urlEncoded.entries());

    const { username, email } = body;

    res.writeHead(301, { "Location": "/" });
    return res.end();
  });

  return;
}

/** else...*/

html에서 post로 설정하였기 때문에 서버측에서 라우트를 설정할 때 req.method === "POST" 일 때를 구현하면 된다. <form />태그를 통해 고전적인 방식으로 통신을 하게 되면 application/x-www-form-urlencoded 방식으로 바디를 전송하게 된다.

서버측에서 클라이언트로부터 전송되는 바디를 송신하기 위해서 req.on()을 통해 이벤트 리스너를 활용하면 된다. 바디는 크기에 따라 chunk1로 나누어 전송되는데 서버는 이 chunk를 하나로 연결하여 온전한 바디 데이터로 활용할 수 있는 것이다. 나뉘어진 chunk별로 data 이벤트를 통해 수신하며 모든 수신이 완료되면 end 이벤트가 발생한다.

URL 방식으로 인코딩된 데이터를 파싱하려면 우리가 흔히 알고 있는 querystring 방식을 떠올리면 된다. nodejs에서는 내장객체 URLSearchParams와 내장함수 Object.fromEntries()를 활용하면 조금 더 간편하게 해석할 수 있다. BODY를 해석하고 DB에 저장한 후에 다시 목록을 볼 수 있도록 301 redirect를 활용하였다.

postgresql

CREATE TABLE public.contact (
  username varchar(25) NOT NULL,
  email varchar(50) NULL,
  bio text NULL,
  created int8 NULL,
  CONSTRAINT contact_pk PRIMARY KEY (username)
);

데이터베이스에 테이블을 생성하는 SQL이다. postgresql은 sudo apt install postgresql 명령으로 아주 쉽게 설치할 수 있다. DB에 접속하여 작업할 수 있는 클라이언트 프로그램으로는 dbeaver, adminer 등 다양한 프로그램들이 있으니 잘 활용하여 데이터베이스 작업을 진행하면 된다.

pg 패키지

postgresql에 연결하여 데이터베이스를 사용하려면 직접 소켓 프로그래밍을 작성하는 것도 방법이겠지만 이미 누군가가 안정적으로 널리 쓰이는 클라이언트를 구현해두었으니 우리는 사용법만 잘 숙지하고 적절하게 사용하면 된다.

npm i -S pg

npm 생태계에서 postgresql 클라이언트라면 pg 패키지를 사용하면 아주 쉽게 데이터베이스를 연동할 수 있다.

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

const pool = new Pool({
  host: "localhost",
  port: 5432,
  user: YOUR_USERNAME,
  password: YOUR_PASSWORD,
  database: YOUR_DBNAME,
});

/** else...*/

req.on("end", async () => {
  const urlEncoded = new URLSearchParams(data.toString())
  const body = Object.fromEntries(urlEncoded.entries());

  const { username, email } = body;

  const client = pool.connect();
  try{
    await client.query(`
      INSERT INTO contact (username, email) VALUES ($1, $2)
    `, [username, email]);

    res.writeHead(301, { "Location": "/" });
    return res.end();
  } catch(err) {
    res.writeHead(500);
    return res.end(err.message);
  } finally {
    client.release();
  }
});

/** else...*/

DB를 준비하였으면 POST기능을 실제로 DB와 연동하여 기능을 구현한다. 클라이언트 패키지를 활용하여 postgresql과 연결하고 pool 객체로부터 connection 객체를 얻어 DB에 엑세스 할 수 있다. 일반적으로 커넥션 객체라고 함은 직접 데이터베이스와 연결되어 명령과 결과를 주고 받는 객체를 일컫는다. 이러한 커넥션 객체는 효율을 위하여 싱글톤 패턴등을 활용하여 프로그램내에 하나의 객체만 유지한다.

통신을 위해서 소켓을 사용하는데 사용할 때에만 소켓을 열고, 그렇지 않을 때는 닫아주는 것이 좋다. 소켓 연결이 점점 늘어나다보면 시스템에서 지원하는 최대소켓 개수를 넘어가 오류가 발생하여 서버가 종료되어버린다. 이러한 일들이 빈번하다보니 커넥션 객체를 관리하는 별도의 객체로 pool을 활용하는 패턴이다. pool에 있는 커넥션들 중 유휴 커넥션 객체를 전달받아 작업하고 다시 놓아주는release 방식이다. 여러 작업이 동시에 들어와도 안정성을 보장할 수 있다.

UPDATE contact
   SET email = $2
 WHERE username = $1
DELETE
  FROM contact
 WHERE username = $1

원리를 이해했다면 나머지 PUT, DELETE 의 기능도 구현해보자. 직접 클라이언트를 통하여 SQL을 생성하고 실행하는 것도 기능을 구현할 수 있는 방법이지만 위 처럼 pg 패키지를 활용하면 SQL Injection 취약점처럼 SQL 프로그래밍에서의 보안적인 부분을 포함하여 다양한 기능을 활용할 수 있다는 장점도 있다.

Footnotes

  1. 설정한 크기만큼 원본 데이터를 여러개로 잘랐을 때 하나의 요소를 칭함