간단한 Node.js API 서버 직접 만들어보기

Node.js 코딩 맛보기

무언가를 배우는 가장 좋은 방법은 직접 해보는 겁니다. Node.js도 마찬가지겠죠? Node.js를 배우기 위해서는 Node.js를 직접 설치해 보고, 관련된 서비스를 직접 만들어보는 것이 가장 좋은 방법일겁니다.

우선 Node.js에 대해서는 이전의 ‘Node.js 설치 및 NPM 사용 가이드‘란 제목의 포스팅에서 비교적 자세히 설명해 두었습니다. 이번 글에서는 바로 Node.js를 이용한 API 서버를 만들어보도록 하겠습니다.

Node.js API 프로젝트 준비하기

express-generator 설치하기

express-generator는 프로젝트를 사용자가 손쉽게 생성할 수 있게 도와주는 웹 애플리케이션 프레임워크인데, 다음의 명령어로 설치할 수 있습니다.

npm install -g express-generator
express-generator 설치

프로젝트 생성하기

express-generator를 설치했다면 API 서버를 만들기 위한 프로젝트를 생성해줘야 합니다. 새로운 프로젝트는 다음과 같이 express {프로젝트 명}의 명령어로 생성할 수 있습니다.

express node-api
새 프로젝트 생성

프로젝트 모듈 설치하기

프로젝트를 생성한 후에는 프로젝트에 필요한 모듈을 설치해야 합니다. 프로젝트 모듈을 설치하려면 우선 생성된 프로젝트 폴더로 이동한 후 npm install 명령어를 입력해 주면 됩니다.

cd node-api
npm install

만약 모듈을 설치하는 과정에서 ‘npm WARN deprecated’와 같은 경고 메세지가 나온다면 다음의 명령어를 입력해주면 됩니다.

npm audit fix
프로젝트 모듈 설치

서버 구동하기

프로젝트의 모듈을 모두 설치하고 나면 서버를 구동할 수 있는 환경이 구성되는데, npm start 명령어로 서버를 구동하고 테스트 웹페이지를 브라우저에 띄울 수 있습니다.

npm start
서버 구동 완료

서버가 구동된 후에는 http://localhost:3000과 같이 3000번 포트를 통해 웹페이지에 접근할 수 있습니다.

테스트 웹페이지

Express 프로젝트의 구조

express 프로젝트의 구성

express를 통해 프로젝트를 생성하면 위와 같은 구조로 프로젝트의 폴더가 생성되는데, 각각의 폴더와 파일의 역할은 다음과 같습니다.

bin/www
포트 번호 등 웹 서버 구축에 관한 설정 정보가 정의되어 있는 파일
node_modules
Node.js의 모듈이 설치되는 폴더
public
자바스크립트, 이미지, css 등 정적 파일을 위한 폴더
routes
라우팅 리소스 별로 모듈을 만들어 라우팅 로직을 구현할 수 있으며, 클라이언트에서 요청 별로 어떤 로직을 수행할지의 기능을 정해놓은 파일로 자바에서의 controller와 같은 역할을 함
views
request 요청에 대한 로직을 처리한 후 클라이언트에 응답을 보낼 때 html 코드로 변환해서 반환하는 파일을 정의한 폴더
app.js
express의 설정 정보가 담겨있는 파일
package.json
프로그램의 이름, 버전, 모듈 등에 대한 정보를 기술해 둔 파일로 installpackage.json을 참조하여 모듈을 설치

라우터

routes/index.js 파일을 열어보면 다음과 같이 /로 접근하는 경우에 보여주는 화면을 res.render에서 정의하고 있는 것을 확인할 수 있는데, index란 바로 /views/index.jade 파일을 가리킵니다.

routes/index.js
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

라우터 페이지 추가하기

라우터에 새로운 페이지를 추가하는 경우에는 get 방식이나 post 방식으로 설정할 수 있는데, get 방식의 경우에는 router.get 메서드를, post 방식의 경우에는 router.post 메서드를 사용합니다.

두 방식은 모두 status()json() 메서드를 사용하는데, status()의 괄호 안에는 응답하고자 하는 HTTP 상태 코드를 입력하고, json()의 괄호 안에는 응답하고자 하는 json 형식의 데이터를 입력합니다.

GET 방식의 경우
routes/index.js
router.get('/api/get/demo', function(req, res) {
  res.status(200).json({
    "message": "call get api demo"
  })
});
POST 방식의 경우
routes/index.js
router.post('/api/post/demo', function(req, res) {
  res.status(200).json({
    "message": "call post api demo"
  });
});

라우터를 추가하면 구동 중인 서버를 종료하고 재시작을 해야 하는데, 화면단의 파일들은 수정이 바로 적용되어 확인이 가능하지만, 서버단의 소스는 구동 중인 시스템을 재시작해야 추가한 설정 내용이 반영됩니다

서버를 재시작하고 http://localhost:3000/api/get/demo로 접속해 보면 다음과 같은 json 메세지를 확인할 수 있습니다.

/api/get/demo
{"message":"call get api demo"}

post 방식의 경우에는 Postman이라는 서비스를 통해 간단하게 확인해 볼 수 있습니다.

Mysql 연동하기

Node.js은 MySQL과 연동할 수 있는 모듈을 제공해주는데, MySQL 모듈을 사용하기 위해서는 MariaDB를 설치해야 합니다.

MariaDB란 오픈 소스인 관계형 데이터베이스 관리 시스템으로, MySQL과 동일한 소스 코드를 기반으로 하고 있고, GPL v2 라이센스를 따르고 있습니다.

여담이지만 현재 MySQL은 오라클의 소유로 라이센스의 상태가 불확실한 것이 가장 큰 단점으로 꼽히고 있기 때문에, 여기에 반발해 만든 것이 MariaDB이고 기본적으로 MySQL과 소스코드를 공유하고 있는 만큼 사용방법과 구조가 동일합니다.

MariaDB는 공식 홈페이지에 접속하면 다운로드할 수 있는데, 버전과 OS, 시스템 종류, 패키지 등을 설정한 다음 다운로드 받은 msi 파일을 실행하면 자동으로 설치가 되고, MariaDB를 설치한 후에는 다음의 명령어로 mysql 모듈을 설치해 주면 됩니다.

npm install mysql --save
mysql 모듈 설치

mysql 모듈을 설치한 후에는 package.json 파일에 다음과 같이 mysql 모듈이 추가되어 있는 것을 확인할 수 있습니다.

package.json
{
  "name": "node-api",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "jade": "~1.11.0",
    "morgan": "~1.9.1",
    "mysql": "^2.18.1"
  }
}

MySQL 연동하기

우선 Node.js와 MySQL 연동을 위해 /database/란 폴더를 만들고 이 폴더에 db_connect.js란 파일을 생성 한 후 다음과 같이 접속 정보를 추가해 주세요.

/database/db_connect.js
const db = require('mysql');

const conn = db.createConnection({
    host: 'localhost',
    port: 3306,
    user: 'user',
    password: 'password',
    database: 'db_name'
});

module.exports = conn;

다음으로 app.js 파일에 다음과 같이 var app = express(); 코드의 아래에 connect를 위한 코드를 추가해주면 됩니다.

app.js
const db = require('./database/db_connect');
db.connect();

CRUD 테스트

MySQL 연동 작업을 완료했다면 CRUD 테스트를 위해 router.get으로 URL을 맵핑해 주면 되는데, CRUD란 대부분의 컴퓨터 소프트웨어가 가지는 기본적인 데이터 처리 기능인 생성, 읽기, 갱신, 삭제를 일컫는 말입니다.

CRUD 테스트를 위해서는 create, drop, insert, select, update, delete의 총 6개의 URL을 맵핑하는데 각각의 맵핑 URL은 데이터베이스 쿼리를 통해 작업을 수행하고, 작업의 수행 결과를 json 형태의 데이터로 반환해 주는 간단한 API 서버의 역할을 수행 할 수 있습니다.

우선 데이터베이스 쿼리를 처리할 수 있도록 다음과 같이 데이터베이스와 연결을 해주고, 각각의 URL에 대한 DB 쿼리를 만들어 주면 됩니다.

데이터베이스 연결하기
/routes/index.js
const db = require('../database/db_connect');
테이블 생성 및 결과 반환하기
/routes/index.js
// create
router.get('/create', function(req, res) {
  db.query('CREATE TABLE DEPARTMENT (DEPART_CODE INT(11) NOT NULL, NAME VARCHAR(200) NULL DEFAULT NULL COLLATE utf8mb4_general_ci, PRIMARY KEY (DEPART_CODE) USING BTREE)', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});
테이블 삭제 및 결과 반환하기
/routes/index.js
// drop
router.get('/drop', function(req, res) {
  db.query('DROP TABLE DEPARTMENT', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});
데이터 추가 및 결과 반환하기
/routes/index.js
// insert
router.get('/insert', function(req, res) {
  db.query('INSERT INTO DEPARTMENT(DEPART_CODE, NAME) VALUES(5001, "ENGLISH")', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});
전체 데이터 결과 반환하기
/routes/index.js
// select
router.get('/select', function(req, res) {
  db.query('SELECT * FROM DEPARTMENT', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});
데이터 갱신하고 결과 반환하기
/routes/index.js
// update
router.get('/update', function(req, res) {
  db.query('UPDATE DEPARTMENT SET NAME="UPD ENG" WHERE DEPART_CODE=5001', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});
데이터 삭제하고 결과 반환하기
/routes/index.js
// delete
router.get('/delete', function(req, res) {
  db.query('DELETE FROM DEPARTMENT WHERE DEPART_CODE=5001', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});

위와 같이 각각의 URL에 데이터베이스 쿼리를 추가하고 결과를 반환해주는 코드를 입력하면 기본적인 API 서버로 동작시킬 수 있는데, /routes/index.js의 전체 코드는 다음과 같습니다.

전체 코드
/routes/index.js
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

// require db_connect.js
const db = require('../database/db_connect');

// create
router.get('/create', function(req, res) {
  db.query('CREATE TABLE DEPARTMENT (DEPART_CODE INT(11) NOT NULL, NAME VARCHAR(200) NULL DEFAULT NULL COLLATE utf8mb4_general_ci, PRIMARY KEY (DEPART_CODE) USING BTREE)', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});

// drop
router.get('/drop', function(req, res) {
  db.query('DROP TABLE DEPARTMENT', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});

// insert
router.get('/insert', function(req, res) {
  db.query('INSERT INTO DEPARTMENT(DEPART_CODE, NAME) VALUES(5001, "ENGLISH")', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});

// select
router.get('/select', function(req, res) {
  db.query('SELECT * FROM DEPARTMENT', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});

// update
router.get('/update', function(req, res) {
  db.query('UPDATE DEPARTMENT SET NAME="UPD ENG" WHERE DEPART_CODE=5001', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});

// delete
router.get('/delete', function(req, res) {
  db.query('DELETE FROM DEPARTMENT WHERE DEPART_CODE=5001', function(err, rows, fields) {
    if(!err) {
      res.send(rows); // response send rows
    } else {
      console.log('err : ' + err);
      res.send(err); // response send err
    }
  });
});

module.exports = router;

결과 확인하기

URL 맵핑 작업이 끝났다면 서버측 코드를 수정한 만큼, Node.js 서버를 재시작해주어야 새로 맵핑한 주소로 접속할 수 있습니다.

서버 다시 시작하기
npm start

서버를 재시작 한 후에는 다음과 같이 각각의 URL로 접속하여 그 결과를 확인할 수 있습니다.

localhost:3000/create
localhost:3000/insert
localhost:3000/select
localhost:3000/update
localhost:3000/delete
localhost:3000/drop

답글 남기기

Recent Posts

Tailwind CSS를 위한 기본 가이드

Tailwind CSS를 위한 기본 가이드

Tailwind CSS 필수 디렉티브 및 함수 가이드

Tailwind CSS 필수 디렉티브 및 함수 가이드

프런트엔드 개발의 효율성을 높이는 기능 분할 설계 아키텍처

프런트엔드 개발의 효율성을 높이는 기능 분할 설계 아키텍처

스테이블 디퓨전 설치 및 사용 가이드

스테이블 디퓨전 설치 및 사용 가이드

행복만 있는 세상, 정말 우리가 꿈꾸는 이상향일까요?

행복만 있는 세상, 정말 우리가 꿈꾸는 이상향일까요?