Vue.js 3 & NodeJS/사내교육 - 점심 메뉴

[1] Backend 개발 - API Server 개발

carrotweb 2026. 8. 2. 19:16
728x90
반응형

APIServer 프로젝트 생성

1. toyprojects > apiserver 디렉터리 생성 (터미널에서 실행합니다.)

mkdir toyprojects; cd toyprojects; mkdir apiserver; cd apiserver; code .

 

2. 패키지 초기화 (터미널에서 실행합니다.)

npm init -y

 

3. Express.js 설치하기 (터미널에서 실행합니다.)

npm install express --save

 

package.json에서 dependencies 확인

 

4. Entry Point 파일 생성하기 (index.js 파일 생성, express를 가져와 서버를 생성)

const express = require('express');
const app = express();
const port = 9000;

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(port, () => {
    console.log(`APIServer started on port ${port}`);
});

포트를 9000으로 설정하였습니다.

 

Express Middleware

(https://expressjs.com/ko/guide/writing-middleware.html)

 

Express에서 HTTP 요청과 HTTP 응답 사이에서 수행되는 함수입니다.

Middleware는 HTTP 요청이 들어오는 순간부터 순차적으로 실행되고 다음 Middleware가 실행합니다.

(다음 Middleware은 next라는 이름의 변수로 표시됩니다. 현재 처리되는 Middleware에서 next()를 호출하지 않으면 다음 Middleware로 전달되지 않고 중지됩니다.)

 

Application-level middleware (애플리케이션 레벨 미들웨어)

const express = require('express')
const app = express()

const myLogger = function (req, res, next) { <--함수형 미들웨어
  console.log('LOGGED')
  next()
}

app.use(myLogger) <-- 1

app.get('/', (req, res, next) => { <-- 2
  res.send('Hello World!')
})

app.listen(3000)

 

5. npm으로 실행하기 위해 Script 추가 (package.json 파일을 오픈하여 "scripts"에 node 실행문을 추가)

"start": "node index.js"

 

6. npm으로 실행 (터미널에서 실행합니다.)

npm run start

 

7. 웹 브라우저에서 확인 (http://127.0.0.1:9000)

 

 

Mariadb Database, Table 생성

데이터베이스 생성 쿼리 실행

-- lunchdb Database가 있으면 삭제
DROP DATABASE IF EXISTS lunchdb;

-- lunchdb Database가 없으면 생성
CREATE DATABASE IF NOT EXISTS lunchdb;

 

테이블 생성 쿼리 실행

use lunchdb;

-- tb_lunch_menu table이 있으면 삭제
DROP TABLE IF EXISTS tb_lunch_menu;

-- TEST_TB table 생성
CREATE TABLE IF NOT EXISTS tb_lunch_menu (
  seq int NOT NULL AUTO_INCREMENT COMMENT '점심 메뉴 일련번호',
  menu_name varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT '점심 메뉴 명',
  reg_dtm datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '등록일',
  PRIMARY KEY (`seq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='점심 메뉴’;

 

초기 데이터 등록 쿼리 실행

INSERT INTO tb_lunch_menu (menu_name, reg_dtm) VALUES('짜장면', '2025-01-17 13:37:00.000');
INSERT INTO tb_lunch_menu (menu_name, reg_dtm) VALUES('짬봉', '2025-01-17 13:37:14.000');
INSERT INTO tb_lunch_menu (menu_name, reg_dtm) VALUES('볶음밥', '2025-01-17 13:37:26.000');

 

8. DB 연동을 위한 MariaDB Connector 설치 (터미널에서 실행합니다.)

npm install mariadb --save

 

Connection Pools with MariaDB Connector/Node.js (Promise API)
(https://mariadb.com/docs/server/connect/programming-languages/nodejs/promise/connection-pools/)

 

dateStrings를 true로 설정 – 날짜를 문자열로 처리하도록 설정함 → time_zone 문제 해결
(https://mariadb.com/kb/en/node-js-connection-options/)

 

9. DB Connection Pool 생성 (src\db\mariadb 디렉터리 생성, connection.js 파일 생성, DB Connection Pool 생성)

const mariadb = require('mariadb');

const pool = mariadb.createPool({
    host: "127.0.0.1",
    port: 3306,
    user: "root",
    password: "kiki@2000",
    database: "lunchdb",
    connectionLimit: 10,
    dateStrings: true
});

async function getDBConnection() {
    connection = await pool.getConnection();
    return connection;
}

module.exports = getDBConnection;

password는 Mariadb를 설치할 때 입력한 password로 입력하세요.

 

10. 점심 메뉴 API 개발 (src 디렉터리에서 api\v1 디렉터리 생성, lunchmenu.js 파일 생성)

const express  = require('express');
const router = express.Router();
const getDBConnection = require('../../db/mariadb/connection');

// 전체 점심 메뉴 리스트를 리턴합니다.
router.get('/', async function(req, res, next) {
    console.log("REST API Get Method - Read All Lunch Menu.");

    const result = {
        result: 'success',
        code: 200,
        message: '',
        data: []
    }

    const query = "SELECT * FROM tb_lunch_menu ORDER BY REG_DTM DESC";
    
    let conn;
    try {
        conn = await getDBConnection();
        data = await conn.query(query, []);
        if (data.length == 0) {
            result.message = "데이터가 없습니다.";
        } else {
            result.message = "데이터가 조회되었습니다.";
            result.data = data;
        }
    } catch (error) {
        result.result = "fail";
        result.code = 500;
        if (error.code == "ER_NO_SUCH_TABLE") {
            result.message = "테이블이 없습니다.";
        } else {
            result.message = "서버 오류가 발생하였습니다.";
        }
        console.log(error);
    } finally {
        if (conn) {
            conn.release();
        }
    }

    res.status(result.code).json(result);
});

// 점심 메뉴 번호로 검색하여 점심 메뉴를 리턴합니다.
router.get('/:no', async function(req, res, next) {
    console.log(`REST API Get Method - Read Lunch Menu ${req.params.no}.`);

    const result = {
        result: 'success',
        code: 200,
        message: '',
        data: {}
    }

    const query = "SELECT * FROM tb_lunch_menu WHERE seq = ?";

    let conn;
    try {
        conn = await getDBConnection();
        data = await conn.query(query, [req.params.no]);
        if (data.length == 0) {
            result.message = "데이터가 없습니다.";
        } else {
            result.message = "데이터가 조회되었습니다.";
            result.data = data[0];
        }
    } catch (error) {
        result.result = "fail";
        result.code = 500;
        if (error.code == "ER_NO_SUCH_TABLE") {
            result.message = "테이블이 없습니다.";
        } else {
            result.message = "서버 오류가 발생하였습니다.";
        }
        console.log(error);
    } finally {
        if (conn) {
            conn.release();
        }
    }

    res.status(result.code).json(result);
});

// 점심 메뉴를 등록합니다.
router.post('/', async function(req, res, next) {
    console.log("REST API Post Method - Create Lunch Menu.");
    const result = {
        result: 'success',
        code: 200,
        message: '',
        data: {}
    }

    if (req.body.menuName == undefined || req.body.menuName == "") {
        result.result = "fail";
        result.code = 400;
        result.message = "menuName이 없습니다."
        res.status(result.code).json(result);
        return;
    }

    const query = "INSERT INTO tb_lunch_menu (menu_name) VALUES(?)";

    let conn;
    try {
        conn = await getDBConnection();
        data = await conn.query(query, [req.body.menuName]);
        // OkPacket { affectedRows: 1, insertId: 4n, warningStatus: 0 }
        if (data.affectedRows == 1) {
            result.message = "데이터가 등록되었습니다.";
        } else {
            result.message = "데이터가 등록되지 않았습니다.";
        }
    } catch (error) {
        result.result = "fail";
        result.code = 500;
        if (error.code == "ER_NO_SUCH_TABLE") {
            result.message = "테이블이 없습니다.";
        } else {
            result.message = "서버 오류가 발생하였습니다.";
        }
        console.log(error);
    } finally {
        if (conn) {
            conn.release();
        }
    }

    res.status(result.code).json(result);
});

// 점심 메뉴를 수정합니다.
router.put('/:no', async function(req, res, next) {
    console.log(`REST API Put Method - Update Lunch Menu ${req.params.no}.`);

    const result = {
        result: 'success',
        code: 200,
        message: '',
        data: {}
    }

    if (req.body.menuName == undefined || req.body.menuName == "") {
        result.result = "fail";
        result.code = 400;
        result.message = "menuName이 없습니다."
        res.status(result.code).json(result);
        return;
    }
    const query = "UPDATE tb_lunch_menu SET menu_name = ? WHERE seq = ?";
    
    let conn;
    try {
        conn = await getDBConnection();
        data = await conn.query(query, [req.body.menuName, req.params.no]);
        // OkPacket { affectedRows: 1, insertId: 0n, warningStatus: 0 }
        if (data.affectedRows == 1) {
            result.message = "데이터가 수정되었습니다.";
        } else {
            result.message = "데이터가 수정되지 않았습니다.";
        }
    } catch (error) {
        result.result = "fail";
        result.code = 500;
        if (error.code == "ER_NO_SUCH_TABLE") {
            result.message = "테이블이 없습니다.";
        } else {
            result.message = "서버 오류가 발생하였습니다.";
        }
        console.log(error);
    } finally {
        if (conn) {
            conn.release();
        }
    }

    res.status(result.code).json(result);
});

// 점심 메뉴를 삭제제합니다.
router.delete('/:no', async function(req, res, next) {
    console.log(`REST API Delete Method - Delete Lunch Menu ${req.params.no}.`);

    const result = {
        result: 'success',
        code: 200,
        message: '',
        data: {}
    }

    const query = "DELETE FROM tb_lunch_menu WHERE seq = ?";
    
    let conn;
    try {
        conn = await getDBConnection();
        data = await conn.query(query, [req.params.no]);
        // OkPacket { affectedRows: 1, insertId: 0n, warningStatus: 0 }
        if (data.affectedRows == 1) {
            result.message = "데이터가 삭제되었습니다.";
        } else {
            result.message = "데이터가 삭제되지 않았습니다.";
        }
    } catch (error) {
        result.result = "fail";
        result.code = 500;
        if (error.code == "ER_NO_SUCH_TABLE") {
            result.message = "테이블이 없습니다.";
        } else {
            result.message = "서버 오류가 발생하였습니다.";
        }
        console.log(error);
    } finally {
        if (conn) {
            conn.release();
        }
    }

    res.status(result.code).json(result);
});

module.exports = router;

 

11. index.js 파일에 BODY를 JSON 데이터로 변환, URL 인코딩, 요청 리소스 허용 처리

const express = require('express');
const app = express();
const port = 9000;

app.get('/', (req, res) => {
    res.send('Hello World!');
});

// Request의 내용(body)을 JSON 데이터로 변화해서 사용할 수 설정
app.use(express.json());
// express.urlencoded() 메서드를 이용하여 URL를 인코딩 할 수 있게 설정
app.use(express.urlencoded({extended:false}));
// 모든 리소스에 접근 허용 - CORS(Cross-origin resource sharing, 교차 출처 리소스 공유) 문제 해결
// GET, POST, PUT, DELETE 메서드 사용 허용
// content-type 요청 허용
app.use(function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    res.header('Access-Control-Allow-Headers', 'content-type');
    next();
});

// 점심 메뉴 연결
app.use('/api/v1/lunchmenus', require('./src/api/v1/lunchmenu'));

app.listen(port, () => {
    console.log(`APIServer started on port ${port}`);
});

 

12. 웹 브라우저에서 확인 (http://127.0.0.1:9000/api/v1/lunchmenus)

 

점심 메뉴 API 테스트 (POSTMAN에서 확인)

1. 전체 점심 메뉴 조회

 

2. 점심 메뉴 번호로 조회

 

3. 점심 메뉴 등록

 

4. 점심 메뉴 번호로 수정

 

5. 점심 메뉴 번호로 삭제

 

 

 

 

728x90
반응형