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

[14] Backend 개발 - 사용자 API 개발, 라우트 별 인증 처리, 인가 처리

carrotweb 2026. 8. 5. 16:27
728x90
반응형

라우트 별로 인증 처리

1. index.js 파일 수정

const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
const port = 9000;

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

:

// Cookie 설정
app.use(cookieParser());

// 인증 설정
//app.use('/api/v1/lunchmenus', require('./src/api/v1/authMiddleware'));
// 점심 메뉴 연결
app.use('/api/v1/lunchmenus', require('./src/api/v1/lunchmenu'));
// 사용자 연결
app.use('/api/v1/member', require('./src/api/v1/member'));

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

전체 lunchmenu에 대한 인증 설정을 주석 처리하거나 삭제합니다.

 

2. src\api\v1\lunchmenu.js 파일 수정 - 점심 메뉴 등록, 수정, 삭제일 때만 인증/인가가 처리되게 하겠습니다.

:
const authMiddleware = require('./authMiddleware');

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

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

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

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

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

 

사용자 API 테스트 (POSTMAN에서 확인)
1. 사용자 로그인

 

2. 전체 점심 메뉴 등록 - 인증 확인

 

 

인증 처리를 통한 등록 (등록자 컬럼 추가)

 

테이블 수정 쿼리 실행, 데이터 업데이트 쿼리 실행

use lunchdb;

-- tb_lunch_menu table에 mbr_id 컬럼 추가
ALTER TABLE tb_lunch_menu ADD mbr_id varchar(50) NOT NULL COMMENT '사용자 ID' AFTER menu_name;

-- tb_lunch_menu table에 모든 mbr_id의 값을 'system'으로 등록
UPDATE tb_lunch_menu SET mbr_id='system';

 

사용자 API 테스트 (POSTMAN에서 확인)

전체 점심 메뉴 조회

 

1. src\db\mappers\mariadb\lunchmenu\lunchmenu-sql.xml 파일을 수정합니다.

    :

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

    :

 

2. src\api\v1\lunchmenu.js 파일 수정 - 사용자 ID를 토큰에 있는 sub로 처리

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

    const params = {
        menuName : req.body.menuName,
        memberID : req.tokenData.sub
    };

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

    let conn;
    try {
        conn = await getDBConnection();
        //data = await conn.query(query, [req.body.menuName, req.tokenData.sub]);
        data = await conn.query(query);
        // OkPacket { affectedRows: 1, insertId: 4n, warningStatus: 0 }
        if (data.affectedRows == 1) {
            result.message = "데이터가 등록되었습니다.";
        } else {
            result.message = "데이터가 등록되지 않았습니다.";
        }
    } catch (error) {
        :

 

사용자 API 테스트 (POSTMAN에서 확인)

전체 점심 메뉴 등록

 

전체 점심 메뉴 조회

 

 

인증 처리를 통한 수정/삭제 (등록자 비교)

1. src\api\v1\lunchmenu.js 파일 수정

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

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

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

    let conn;
    try {
        conn = await getDBConnection();
        //data = await conn.query(query, [req.params.no]);
        data = await conn.query(query);
        if (data.length == 0) {
            result.message = "데이터가 없습니다.";
        } else {
            if (data[0].mbr_id == req.tokenData.sub) {
                // data = await conn.query(query, [req.body.menuName, req.params.no]);
                data = await conn.query(querySelect);
                // OkPacket { affectedRows: 1, insertId: 0n, warningStatus: 0 }
                if (data.affectedRows == 1) {
                    result.message = "데이터가 수정되었습니다.";
                } else {
                    result.message = "데이터가 수정되지 않았습니다.";
                }
            } else {
                result.result = "fail";
                result.code = 403;
                result.message = "id가 동일하지 않습니다.";
            }
        }
    }
    :

 

사용자 API 테스트 (POSTMAN에서 확인)

전체 점심 메뉴 수정 - 사용자 ID 비교

이전에 등록된 모든 점심 메뉴의 mbr_id를 'system'으로 등록하였기 때문에 id가 일치하지 않습니다.

 

 

2. src\api\v1\lunchmenu.js 파일 수정

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

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

    let conn;
    try {
        conn = await getDBConnection();
        //data = await conn.query(query, [req.params.no]);
        data = await conn.query(querySelect);
        if (data.length == 0) {
            result.message = "데이터가 없습니다.";
        } else {
            if (data[0].mbr_id == req.tokenData.sub) {
                // data = await conn.query(query, [req.params.no]);
                data = await conn.query(queryDelete);
                // OkPacket { affectedRows: 1, insertId: 0n, warningStatus: 0 }
                if (data.affectedRows == 1) {
                    result.message = "데이터가 삭제되었습니다.";
                } else {
                    result.message = "데이터가 삭제되지 않았습니다.";
                }
            } else {
                result.result = "fail";
                result.code = 403;
                result.message = "id가 동일하지 않습니다.";
            }
        }
    }
    :

 

사용자 API 테스트 (POSTMAN에서 확인)

전체 점심 메뉴 삭제 - 사용자 ID 비교

이전에 등록된 모든 점심 메뉴의 mbr_id를 'system'으로 등록하였기 때문에 id가 일치하지 않습니다.

 

 

 

Scope를 통한 인가(접근 권한 확인) 처리

1. src\api\v1\lunchmenu.js 파일 수정

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

    const searchInfo = search(req);

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

    // Scope로 접근 권한 확인
    const scopes = req.tokenData.scope.split(" ");
    if (scopes.findIndex((element) => element === "lunchmenu.all" || element === "lunchmenu.read") < 0) {        
        result.result = "fail";
        result.code = 403;
        result.message = "접근 권한이 없습니다."
        res.status(result.code).json(result);
        return;
    }

    :
});
:
// 점심 메뉴 번호로 검색하여 점심 메뉴를 리턴합니다.
router.get('/:no', authMiddleware, 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: {}
    };

    // Scope로 접근 권한 확인
    const scopes = req.tokenData.scope.split(" ");
    if (scopes.findIndex((element) => element === "lunchmenu.all" || element === "lunchmenu.read") < 0) {        
        result.result = "fail";
        result.code = 403;
        result.message = "접근 권한이 없습니다."
        res.status(result.code).json(result);
        return;
    }

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

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

    // Scope로 접근 권한 확인
    const scopes = req.tokenData.scope.split(" ");
    if (scopes.findIndex((element) => element === "lunchmenu.all" || element === "lunchmenu.write") < 0) {        
        result.result = "fail";
        result.code = 403;
        result.message = "접근 권한이 없습니다."
        res.status(result.code).json(result);
        return;
    }

    :
});
:
// 점심 메뉴를 수정합니다.
router.put('/:no', authMiddleware, 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: {}
    }

    // Scope로 접근 권한 확인
    const scopes = req.tokenData.scope.split(" ");
    if (scopes.findIndex((element) => element === "lunchmenu.all" || element === "lunchmenu.write") < 0) {        
        result.result = "fail";
        result.code = 403;
        result.message = "접근 권한이 없습니다."
        res.status(result.code).json(result);
        return;
    }

    :
});
:
// 점심 메뉴를 삭제합니다.
router.delete('/:no', authMiddleware, 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: {}
    }

    // Scope로 접근 권한 확인
    const scopes = req.tokenData.scope.split(" ");
    if (scopes.findIndex((element) => element === "lunchmenu.all" || element === "lunchmenu.delete") < 0) {        
        result.result = "fail";
        result.code = 403;
        result.message = "접근 권한이 없습니다."
        res.status(result.code).json(result);
        return;
    }

    :
});

 

사용자 API 테스트 (POSTMAN에서 확인)

전체 점심 메뉴 등록 - 사용자 Scope 비교

로그인된 사용자의 scope가 profile lunchmenu.read이기 때문에 읽기만 됩니다.

사용자의 scope를 lunchmenu.all 이나 lunchmenu.write로 수정하면 등록이 됩니다.

728x90
반응형