웹 개발

[포스코X코딩온] 모델 뷰 컨트롤 MVC - MySQL 연결

끊임없이 성장중인 개발자 2023. 12. 1. 22:13
728x90
반응형

포스팅 주제

  • MVC - MySQL 연결

이번 시간에는 MVC에 MySQL을 연결하여 SQL문으로 값을 추가 하는 방법을 살펴 보겠습니다.

 

Ver.1 With Out Connect MySQL

 

1) 우선 기본적인 파일 구조를 잡자!

 

 

controller, model, routes, views 그리고 이번에는 static을 추가로 사용해 보겠습니다.

 

폴더들을 만들었다면 node.js를 설치해 줍시다.

npm init -y
npm install express ejs

 

2) app.js

이제 파일들을 만들어 줍니다.

 

const express = require('express');
const app = express();
const PORT = 8000;

app.set('view engine', 'ejs');
app.set('views', './views');

app.use(express.urlencoded({extended: true}));
app.use(express.json());

// 라우터 분리
const indexRouter = require('./routes/index');
app.use('/', indexRouter);


// 애러 처리
app.get('*', (req,res) => {
    res.render('404');
})

app.listen(PORT, () => {
    console.log(`${PORT} is opening!!`)
})

 

 

3) views 폴더 파일들

  • index.ejs
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>MVC패턴과 MYSQL연결</h1>
    <a href="/visitor">방명록 남기기</a>
</body>
</html>

 

  • 404.ejs
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>404 Error</title>
</head>
<body>
    <h1>⚠️ 404 Error</h1>
    <p>해당 URL에 접근 불가능합니다.</p>
</body>
</html>

 

  • visitor.ejs
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <!-- axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <!-- visitor.js -->
    <script defer src="/static/visitor.js"></script>

</head>
<body>
    <form name="visit">
        <fieldset>
            <legend>방명록 등록</legend>
            
            <input type="text" name="name" id="name" placeholder="사용자 이름"><br>
            <input type="text" name="comment" id="comment" placeholder="방명록"><br>
            
            <button type="button" onclick="insert()">등록</button>
        </fieldset>
    </form>

    <table border="1" cellspacing="0">
        <thead>
            <tr>
                <th>ID</th>
                <th>작성자</th>
                <th>방명록</th>
                <th>수정</th>
                <th>삭제</th>
            </tr>
        </thead>
        <tbody>
            <% for(let i=0; i < data.length; i++) { %>
                <tr id="tr_<%= data[i].id %>">
                    <td><%= data[i].id %></td>
                    <td><%= data[i].name %></td>
                    <td><%= data[i].comment %></td>
                    <td><button type="button">수정</button></td>
                    <td><button type="button">삭제</button></td>
                </tr>
                <% } %>
        </tbody>
    </table>
    </div>
</body>
</html>

 

테이블을 만들어 준다.

 

 

4) model : visitor.js

exports.getVisitors = () =>{
    return [
        {id:1, name:'홍길동', comment: '내가 왔다'},
        {id:2, name:'이찬혁', comment: '으라차차'},
    ];
}

 

5) controller : Cvisitor.js 

const Visitor = require('../model/Visitor')

exports.main = (req,res) =>{
    res.render('index');
};

//이전 버전 [before] MySQL 연결 전
exports.getVisitors = (req, res) => {
    console.log(Visitor.getVisitors());
    res.render('visitor', {data: Visitor.getVisitors()})
}

 

6) route : index.js

const express = require('express');
const router = express.Router();
const controller = require('../controller/Cvisitor');

router.get('/', controller.main);
router.get('/visitor', controller.getVisitors);

module.exports = router;

 

결과

 

지금은 따로 등록, 수정, 삭제가 만들어져 있지 않다.

 

그럼 이제 MySQL과 연결하여 SQL문으로 값을 넘겨주고 실제로 MySQL에서 값을 확인해보자


Ver.2 With Connect MySQL

 

가장 먼저 mysqld을 연결하기 위해서는 npm을 통해서 mysql을 설치해야 한다.

npm install mysql2

 

mysql2

 

1) app.js 미들웨어 추가 (static)

app.use('/static', express.static(__dirname + '/static'));

static 미들웨어를 추가한다.

 

2) static : visitor.js

// const tbody = document.querySelector('tbody');


// 폼의 등록 버튼 클릭시
// -- 테이블에 데이터 추가
function insert(){
    const form = document.forms['visit'];

    if(form.name.value.length === 0 || form.comment.value.length === 0){
        alert('이름 또는 방명록 기입해주세요');
        return;
    }

    // name 10글자 유효성 검사
    if(form.name.value.length > 10){
        alert('이름은 10글자 미만입니다!');
        return;
    }

    axios({
        method : 'post',
        url: '/visitor',
        data: {
            name : form.name.value,
            comment : form.comment.value
        }
        }).then((res) => {
            console.log(res.data);
            const data = res.data;

            // // 새로고침 안해도 보이게 만들기
            // const html = `
            //         <tr id="tr_${data.id}">
            //         <td>${data.id}</td>
            //         <td>${data.name}</td>
            //         <td>${data.comment}</td>
            //         <td><button type="button">수정</button></td>
            //         <td><button type="button">삭제</button></td>
            // `

            // //insertAdjacentHTML: 특정 요소에 html 추가
            // tbody.insertAdjacentHTML('beforeend', html)
        })
}

 

axios 방식으로 표현했으며, method는 post로 받을 것이다.

또한 이름에 예외처리를 해주어서 0 이상 10미만의 글자만 받을 걷이다.

 

3) model : mysql 추가

먼저 이전에 넣었던 값들은 주석 처리해주자!

 

데이터베이스와 연결하기 위해 MySQL에서 테이블을 하나 만들어서 값을 두개 넣어 준다.

<MySQL SQL문>

create table visitor(
	id int not null primary key auto_increment,
    name varchar(10) not null,
    comment mediumtext null
);

desc visitor;

insert into visitor (name, comment) values('홍길동', '내가 왔다!');
insert into visitor (name, comment) values('이찬혁', '으라차차차!!');

 

결과를 확인해 보면 잘 만들어 진것을 확인할 수 있다.

 

< model 수정 >

//=========== mysql 연결 ============

const mysql = require('mysql2');
// DB연결
const conn = mysql.createConnection({
    host: 'localhost', // DB가 설치된 호스트 IP주소
    user: 'user', //DB접속 유저이름
    password: '1234', //DB접속 비밀번호
    database: 'kdt' // DB이름
})

 

우선 MySQL을 연결해 주어야 한다.

위 코드들은 locolhost경로(즉, 내부 경로) 유저 'user'로 비밀번호는 '1234', 데이터베이스 'kdt'에 연결하겠다는 의미다. 

 

**주의

=> user를 root로 설정하지 말자! , bcz MySQL외부 프로그램에서 root로 접근할 수 없기 때문에 연결이 안된다.

 

해당 유저가 있어야 하기 때문에 MySQL에 해당 값을 가지는 유저를 만들어줘야 한다.

<MySQL>

-- 권한을 주려 했지만 현재 root가 localhost로 되어 있어서 grant을 부여하는 기능을 사용할 수 없다.
-- 전체 권한을 가진 계정을 먼저 생성
create user 'user'@'localhost' identified by '1234';

-- user 계정에 DB 권한 부여(모든 DB에 접근가능하도록)
GRANT ALL privileges on *.* to 'user'@'localhost' with GRANT OPTION;

-- mysql 비밀번호 변경
ALTER USER 'user'@'%' IDENTIFIED BY '4321';

 

< model 수정 추가 2>

// cb는 콜백함수이다.
exports.getVisitors = (cb) => {
    conn.query(`SELECT * FROM visitor`, (err, rows) => {
        if(err) throw err;

        console.log('Visitor.js: ', rows);
        cb(rows);
    })
}

 

model visitor.js에 코드를 추가해준다.

**이때 cb는 콜백함수이다.

 

getVisitors라는 이름의 cb콜백함수를 받는 코드로

mysql에 SQL문을 전송해서 출력 결과를 가져와 콜백한다.

'SELECT * FROM visitor' SQL문을 실행 해서 err가 안나면 rows에 값이 저장된다.

rows = >  결과

 

MySQL에서 생성한 값들이 들어가 있는 것을 확인할 수 있다.

 

< model 수정 추가 3>

exports.postVisitor = (data, cb) =>{
    console.log('postVisitor => ', data);
    
    /**
     * Prepared Statements
     * SQL 쿼리에서 사용자 입력을 안전하게 처리하고 SQL 인젝션 공격을 
     * 방지하기 위한 방법
     * 
     * 입력 데이터를 직접 문자열로 쿼리에 삽입하는 대신,
     * 플레이스홀더를 사용하여 쿼리 작성
     * mysql 에서는 ? (물음표) 사용
     * 
     */

    const sql = "INSERT INTO visitor (name, comment) VALUE (?, ?)";

    const values = [data.name, data.comment]; // 하나여도 배열로 감싸서 보내야 한다.
    // 두번째 인자는 항상 배열로 감싸서 보내야 한다.
    conn.query(sql,values, (err, rows) => {
        if(err) throw err;

        console.log('Visitor.js > ' , rows)

        // insertId 값을 넘겨준다.

        cb(rows.insertId);
    })
}

 

postVisitor(data,cb)  --- data는 사용자가 모니터에서 입력하는 값이 들어간다.

postVisitor는 사용자가 입력한 값을 직접 쿼리로 입력하기 위해 만든 것이다.

 

sql에 SQL문 'INSERT INTO visitor (name, comment) VALUE (? , ?)' 쿼리 문을 넣는다.

value가 ?인 이유는 values 배열로 감싸서 보내야 하기 때문이다.

 

**주의 보내는 값이 하나여도 배열로 감싸서 보내야한다.

 

확인해보면 사용자가 보낸 값이 mysql 데이터베이스에 전송된것을 확인할 수 있다.

여기서 rows.insertId를 콘솔로 확인하면 숫자를 확인할 수 있는데 해당 숫자는 값이 들어간 순서를 나타낸다.

이값을 이용해서 id에 넣어야 하기 때문에 cb()에 넣어서 콜백한다.

 

< Controller : Civisitor.js 수정 >

const Visitor = require('../model/Visitor')


exports.main = (req,res) =>{
    res.render('index');
};

// 이전 버전 [before]
// exports.getVisitors = (req, res) => {
//     console.log(Visitor.getVisitors());
//     res.render('visitor', {data: Visitor.getVisitors()})
// }


//[After] , callback 함수를 넘겨준다.
exports.getVisitors = (req, res) =>{
    Visitor.getVisitors((result) => {
        console.log('Cvisitor.js > ', result);
        res.render('visitor', {data: result});
    })
}

// post /visitor
exports.post_visitor = (req, res) =>{
    console.log('req.body >>> : ',req.body);
    const {name, comment} = req.body

    Visitor.postVisitor(req.body, (result) => {
        console.log(result);
        res.send({id: result, name : name, comment: comment});
    })
}

이전에 사용한 값은 주석 처리해줬다.

1) exports.getVisitors에 Visitor.getVisitors에 result가 받는 값은 이전에 콜백한 rows

(즉, select * from visitor로 검색한 값들)이다.

 

이것을 data 변수로 담아서 views폴더에 있는 visitor.ejs에 보내준다.

이 값을 통해 현재 테이블에 존재하는 값들을 화면에 테이블로 보여준다.

 

2) exports.post_visitor에 들어오는 값 req.body는 사용자가 입력한 값이다.

이 값을 가지고 name, comment로 구조분해 한다음, 사용한다.

 

여기서 result값은 rows.insertId값으로 이전에 콜백한 값이다.

이값을 id에 넣어서 다른 값들과 같이 서버로 전송한다.

 

< routes : index.js 수정 >

const express = require('express');
const router = express.Router();

const controller = require('../controller/Cvisitor');

router.get('/', controller.main);
router.get('/visitor', controller.getVisitors);

// post
router.post('/visitor', controller.post_visitor);

module.exports = router;

 

controller에서 받은 값들을 router.get, post로 받아 보내준다.

 

 

 

기본적으로 새로고침을 해야 값이 보이는 것을 확인할 수 있는데, 이것을 고치기 위해서는

static에 visitor.js 주석된 부분을 해체하면 실시간으로 값이 들어가는 것을 확인할 수 있다.

 

이유는 다음시간에 한번 풀어보겠습니다.

 


이번 실습은 조금 헷갈리는 부분이 많은데 다시한번씩 검토 해야할것 같네요🤯

 

 

반응형