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

[3] Backend 개발 - API Server 개발, Node.JS + MyBatis3

carrotweb 2026. 8. 3. 14:36
728x90
반응형

Node.JS에서 MyBatis3 Mapper XML 파일을 이용하기

 

 

1. Mybatis Mapper 설치하기 (터미널에서 실행합니다.)

npm install mybatis-mapper --save

 

2. src\db에 mappers\mariadb\lunchmenu 디렉터리를 생성하고 lunchmenu-sql.xml 파일을 생성합니다.

 

XML은 MyBatis3 구문과 동일합니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="">
</mapper>

<mapper>의 namespace(네임스페이스) 속성은 Mapper XML 파일들을 서로 구분하기 위해서 사용됩니다. 그래서 하나의 Mapper XML 파일만 가리켜야 하기 때문에 중복되지 않아야 합니다.

여기서는 namespace(네임스페이스)를 프로젝트 명과 XML 파일이 있는 경로를 조합하여 사용하겠습니다.

 

프로젝트 명 : apiserver

XML 파일이 있는 경로 : db\mappers\mariadb\lunchmenu
그래서 mapper namespace는 apiserver.db.mappers.mariadb.lunchmenu입니다.

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="apiserver.db.mappers.mariadb.lunchmenu">  
    <select id="">
    </select>

    <insert id="">
    </insert>

    <update id="">
    </update>

    <delete id="">
    </delete>
</mapper>

 

<mapper> 안에서는 <select>(SELECT 쿼리문), <insert>(INSERT 쿼리문), <update>(UPDATE 쿼리문), <delete>(DELEETE 쿼리문)를 설정할 수 있습니다.

 

<select>, <insert>, <update>, <delete>의 id 속성 값은 식별자로 중복되지 않는 이름으로 입력합니다.

id 속성은 mybatisMapper.getStatement() 함수에서 구문을 찾을 때 사용됩니다.

 

id 속성 값에 대한 접두사(Prefix)와 접미사(suffix)를 이용하여 다음과 같은 명명 규칙(NamingRule)을 정합니다.

 

DB : Insert,  Select, Update, Delete

  • SELECT 쿼리문으로 여러 로우를 가져오면 select<모델명>List
  • SELECT 쿼리문으로 로우 수를 가져오면 select<모델명>ListCount
  • SELECT 쿼리문으로 한 로우만 가져오면 select<모델명>Item
  • SELECT 쿼리문으로 하나의 값만 가져오면 select<모델명>Value
  • INSERT 쿼리문은 insert<모델명>Item
  • UPDATE 쿼리문으로 한 로우만 수정하면 update<모델명>Item
  • UPDATE 쿼리문으로 여러 로우를 수정하면 update<모델명>List
  • DELEETE 쿼리문으로 한 로우만 삭제하면 delete<모델명>Item
  • DELEETE 쿼리문으로 여러 로우를 삭제하면 delete<모델명>List

 

CRUD : Create,  Read, Update, Delete (CRUDL : Create,  Read, Update, Delete, List)

  • SELECT 쿼리문으로 여러 로우를 가져오면 read<모델명>List (list<모델명>)
  • SELECT 쿼리문으로 로우 수를 가져오면 read<모델명>ListCount (list<모델명>Count)
  • SELECT 쿼리문으로 한 로우만 가져오면 read<모델명>Item
  • SELECT 쿼리문으로 하나의 값만 가져오면 read<모델명>Value
  • INSERT 쿼리문은 create<모델명>Item
  • UPDATE 쿼리문으로 한 로우만 수정하면 update<모델명>Item
  • UPDATE 쿼리문으로 여러 로우를 수정하면 update<모델명>List
  • DELEETE 쿼리문으로 한 로우만 삭제하면 delete<모델명>Item
  • DELEETE 쿼리문으로 여러 로우를 삭제하면 delete<모델명>List

 

ABCD : Add, Browse, Change, Delete

  • SELECT 쿼리문으로 여러 로우를 가져오면 browse<모델명>List
  • SELECT 쿼리문으로 로우 수를 가져오면 browse<모델명>Count
  • SELECT 쿼리문으로 한 로우만 가져오면 browse<모델명>Item
  • SELECT 쿼리문으로 하나의 값만 가져오면 browse<모델명>Value
  • INSERT 쿼리문은 add<모델명>Item
  • UPDATE 쿼리문으로 한 로우만 수정하면 change<모델명>Item
  • UPDATE 쿼리문으로 여러 로우를 수정하면 change<모델명>List
  • DELEETE 쿼리문으로 한 로우만 삭제하면 delete<모델명>Item
  • DELEETE 쿼리문으로 여러 로우를 삭제하면 delete<모델명>List

 

BREAD : Browse, Read, Edit, Add, Delete

  • SELECT 쿼리문으로 여러 로우를 가져오면 browse<모델명>
  • SELECT 쿼리문으로 로우 수를 가져오면 browse<모델명>Count
  • SELECT 쿼리문으로 한 로우만 가져오면 read<모델명>
  • SELECT 쿼리문으로 하나의 값만 가져오면 read<모델명>Value
  • INSERT 쿼리문은 add<모델명>Object
  • UPDATE 쿼리문으로 한 로우만 수정하면 edit<모델명>Object
  • UPDATE 쿼리문으로 여러 로우를 수정하면 edit<모델명>List
  • DELEETE 쿼리문으로 한 로우만 삭제하면 delete<모델명>Object
  • DELEETE 쿼리문으로 여러 로우를 삭제하면 delete<모델명>List

 

DAVE : Delete, Add, View, Edit

  • SELECT 쿼리문으로 여러 로우를 가져오면 view<모델명>List
  • SELECT 쿼리문으로 로우 수를 가져오면 view<모델명>Count
  • SELECT 쿼리문으로 한 로우만 가져오면 view<모델명>Object
  • SELECT 쿼리문으로 하나의 값만 가져오면 view<모델명>Value
  • INSERT 쿼리문은 add<모델명>Object
  • UPDATE 쿼리문으로 한 로우만 수정하면 edit<모델명>Object
  • UPDATE 쿼리문으로 여러 로우를 수정하면 edit<모델명>List
  • DELEETE 쿼리문으로 한 로우만 삭제하면 delete<모델명>Object
  • DELEETE 쿼리문으로 여러 로우를 삭제하면 delete<모델명>List

 

GRADE : Get, Remove, Add, Delete, Edit

  • SELECT 쿼리문으로 여러 로우를 가져오면 get<모델명>List
  • SELECT 쿼리문으로 로우 수를 가져오면 get<모델명>ListCount
  • SELECT 쿼리문으로 한 로우만 가져오면 get<모델명>Object
  • SELECT 쿼리문으로 하나의 값만 가져오면 get<모델명>Value
  • INSERT 쿼리문은 add<모델명>Object
  • UPDATE 쿼리문으로 한 로우만 수정하면 edit<모델명>Object
  • UPDATE 쿼리문으로 여러 로우를 수정하면 edit<모델명>List
  • UPDATE 쿼리문으로 한 로우만 사용(검색)되지 않게 수정하면 remove<모델명>Object
  • UPDATE 쿼리문으로 여러 로우가 사용(검색)되지 않게 수정하면 remove<모델명>List
  • DELEETE 쿼리문으로 한 로우만 삭제하면 delete<모델명>Object
  • DELEETE 쿼리문으로 여러 로우를 삭제하면 delete<모델명>List

 

DB 명명 규칙으로 사용하겠습니다.

 

3. lunchmenu-sql.xml 파일에 src\api\v1\lunchmenu.js 파일에 있는 query를 <select>, <insert>, <update>, <delete> 구문으로 추가합니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="apiserver.db.mappers.mariadb.lunchmenu">  
    <select id="selectLunchMenuList">
        SELECT *
          FROM tb_lunch_menu
         ORDER BY REG_DTM DESC
    </select>

    <select id="selectLunchMenuItem">
        SELECT *
          FROM tb_lunch_menu
         WHERE seq = ${seq}
    </select>

    <insert id="insertLunchMenuItem">
        INSERT INTO tb_lunch_menu
            (
                menu_name
            )
            VALUES
            (
                #{menuName}
            )
    </insert>

    <update id="updateLunchMenuItem">
        UPDATE tb_lunch_menu
           SET menu_name = #{menuName}
         WHERE seq = ${seq}
    </update>

    <delete id="deleteLunchMenuItem">
        DELETE
          FROM tb_lunch_menu
         WHERE seq = ${seq}
    </delete>
</mapper>

SQL 문에 있는 #{}, ${}는 전달되는 변수가 들어갈 곳입니다.

#{}은 변수에 들어 갈 값이 따옴표로 묶여 변환됩니다. → 문자열에 사용

${}은 변수 그대로 변환됩니다. → 숫자에 사용

 

 

API 수정

1. src\api\v1\lunchmenu.js 파일을 수정합니다.

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

// XML 파일 경로는 상대 경로가 아닌 절대 경로(Root 경로)로 './' 또는 '/' 없이 입력해야 합니다.
mybatisMapper.createMapper(['./src/db/mappers/mariadb/lunchmenu/lunchmenu-sql.xml']);

// SQL 언어 형식과 들여쓰기를 설정
const format = {language: 'mariadb', indent: '  '};

createMapper([XML Files])

  • [XML Files]은 XML 파일 경로들이 있는 배열입니다.
  • XML 파일 경로는 상대 경로가 아닌 절대 경로(Root 경로)로 './' 또는 '/' 없이 입력해야 합니다.
    • Root에 있다면 ./test.xml, test.xml
    • Root의 하위 디렉터리인 src에 있다면 ./src/test.xml, src/test.xml
  • 지정된 xml 파일을 읽고 구문 분석하여 SQL 문을 생성할 준비를 합니다.

 

format 변수의 language 옵션
(https://github.com/sql-formatter-org/sql-formatter/blob/HEAD/docs/language.md#options)

 

// 전체 점심 메뉴 리스트를 리턴합니다.
router.get('/', async function(req, res, next) {
    :

    //const query = "SELECT * FROM tb_lunch_menu ORDER BY REG_DTM DESC";
    const query = mybatisMapper.getStatement('apiserver.db.mappers.mariadb.lunchmenu', 
        'selectLunchMenuList', {}, format);

    :
        data = await conn.query(query);
    :
});

getStatement(namespace: string, sql: string, param?: mybatisMapper.Params, format?: mybatisMapper.Format)

  • namespace은 네임스페이스로 XML 파일에 있는 <mapper>의  namespace 속성 값입니다.
  • sql는 XML 파일에 있는 <select>, <insert>, <update>, <delete>의 id 속성 값입니다.
  • Parameters는 XML 파일에 있는 SQL 문에 있는 #{}, ${} 변수에 들어갈 값입니다.
  • format은 옵션입니다. SQL 언어와 들여쓰기를 설정할 수 있습니다. (https://www.npmjs.com/package/sql-formatter)

 

// 점심 메뉴 번호로 검색하여 점심 메뉴를 리턴합니다.
router.get('/:no', async function(req, res, next) {
    :

    const params = {
        seq : req.params.no
    };

    //const query = "SELECT * FROM tb_lunch_menu WHERE seq = ?";
    const query = mybatisMapper.getStatement('apiserver.db.mappers.mariadb.lunchmenu', 
        'selectLunchMenuItem', params, format);

    :
        data = await conn.query(query);
    :
});

// 점심 메뉴를 등록합니다.
router.post('/', async function(req, res, next) {
    :

    const params = {
        menuName : req.body.menuName
    };

    //const query = "INSERT INTO tb_lunch_menu (menu_name) VALUES(?)";
    const query = mybatisMapper.getStatement('apiserver.db.mappers.mariadb.lunchmenu', 
        'insertLunchMenuItem', params, format);

    :
         data = await conn.query(query);
    :
});

// 점심 메뉴를 수정합니다.
router.put('/:no', async function(req, res, next) {
    :

    const params = {
        menuName : req.body.menuName,
        seq : req.params.no
    };

    //const query = "UPDATE tb_lunch_menu SET menu_name = ? WHERE seq = ?";
    const query = mybatisMapper.getStatement('apiserver.db.mappers.mariadb.lunchmenu', 
        'updateLunchMenuItem', params, format);

    :
        data = await conn.query(query);
    :
});

// 점심 메뉴를 삭제합니다.
router.delete('/:no', async function(req, res, next) {
    :

    const params = {
        seq : req.params.no
    };

    //const query = "DELETE FROM tb_lunch_menu WHERE seq = ?";
    const query = mybatisMapper.getStatement('apiserver.db.mappers.mariadb.lunchmenu', 
        'deleteLunchMenuItem', params, format);

    :
         data = await conn.query(query);
    :
});

 

 

728x90
반응형