부트캠프/[POSCO x Codingon] 웹개발자 풀스택 과정 12기

[POSCO x Codingon] MVC, MySQL | 웹개발자 풀스택 과정 12기 7주차 회고 2

hyunh404 2024. 4. 11. 10:58
728x90

 

 

7주차 회고

 

 

7주차 두번째 회고로 작성할 내용은 MVC와 MySQL을 연결하는 과정이다. MVC 구조를 이해하는 과정은 어렵지 않았지만 데이터베이스와 연결하면서 복잡한 부분이 생겨 섬세하게 기록해두고 계속해서 복습하려고 한다.
MVC 구조를 생성해보고 MySQL과 연결하는 방법을 연습해볼 수 있었다.

 

 


 

 

1. MVC

 

: Model View Controller

=> Model = 데이터를 처리하는 부분 / View = UI관련 처리(사용자에게 보여짐) / Controller = View와 Model 연결

 

  • 장점 : 패턴을 구분해 개발, 유지보수 용이, 유연성, 확장성 높음, 협업에 용이
  • 단점 : 완벽한 의존성 분리 어려움, 설계단계 복잡 및 시간 오래 걸림, 클래스가 많아짐
  •  

기본적인 MVC 패턴 폴더 구조

 

 

 

대략적으로 폴더의 기능에 대해 설명하자면,

 

기본적인 폴더 구조에서 model 폴더에는 사용할 데이터를 객체나 배열 형태로 저장했다.

다음으로 views폴더에 렌더링할 페이지를 만들어 주었고, 페이지를 렌더링하고 model폴더에 있는 데이터를 받아오고 다시 데이터를 내보내는 기능은 controller 폴더에 정의했다.

 

마지막으로 만들어둔 모든 폴더의 경로를 연결해주는 routes폴더에 경로를 지정한 후 app.js에서 router만 불러와 파일을 실행했다.

 

 

코드 예시
app.js

 

 


 

 

MVC 구조를 연습하고 이전에 실습했었던 axios post 로그인 구조를 MVC 구조로 바꾸는 과제가 주어졌다.

 

기본적으로 MVC 구조는 어렵지 않게 완성할 수 있었으나, axios post 요청에 데이터가 제대로 전달되지 않았다.

이부분은 계속해서 고민해서 수정해봐야할 것 같다.

 

 

꼭 성공해서 과제를 완료하고 싶다...!!!🔥🔥

 

MVC 폴더 구조

 

 

 

일단 기본적인 MVC 폴더 구조는 이렇게 만들어 주었고, model 폴더에 로그인에 사용할 사용자 정보 하나를 저장해주었다.

 

controller 폴더에서 model의 데이터를 받고 다시 데이터를 전달하는 기능과 페이지 렌더링 기능을 정의해줬으며, routes 폴더를 이용해서 폴더와 파일 경로를 연결해주었다.

 

마지막으로 app.js에 router를 불러와 페이지를 실행시켰다.

 

 

이렇게 MVC 구조를 만드는 것은 어렵지 않았으나, views 폴더에 로그인 페이지를 구현하는 과정에서 axios post 요청에 데이터가 정상적으로 전달되지 않는 것을 발견했다.

 

이부분은 오류를 찾아서 다른 방법을 시도해 꼭 해결해보려한다.

 

 

model, controller, routes 폴더

 

 


 

 

2. MVC - MySQL 연결

 

: MVC 구조를 활용해 데이터를 MySQL과 연결해 저장된 데이터를 활용하고, 데이터베이스에 추가 및 삭제가 가능하도록 연결하는 실습을 진행했다.

=> 데이터 베이스를 연결해 방명록을 만드는 실습을 진행했다.

 

 

먼저 mysql을 연결해주기 위해 mysql2라는 모듈을 설치해줬다.

=> npm install mysql2

 

 

model 폴더에 js파일을 생성해 데이터베이스를 연결했다.

const mysql = require('mysql2');
const conn = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: '1234',
    database: 'kdt'
})

 

 

데이터베이스를 연결해준 다음 페이지가 렌더링됐을 때 화면에 저장된 데이터가 보여지고 버튼을 누르면 데이터 베이스에 데이터가 추가 및 삭제, 수정 등이 가능하도록 퀴리를 이용해 함수를 정의했다.

 

정의한 후 다른 파일에서도 이를 사용할 수 있게끔 exports까지 정의해줬다.

exports.allVisitorsList = (cb)=>{
    conn.query('select * from visitor', (err,rows)=>{
        console.log('select *');
        cb(rows)
    })
}

// 데이터 추가
exports.postVisitor = (data, cb)=>{
    const sql = 'insert into visitor (name,comment) values (?,?)'
    const values = [data.name, data.comment]
    conn.query(sql, values, (err,rows)=>{
        cb(rows.insertId)
    })
}

// 1명의 방문자 보기
exports.getShowAvisitor = (id, cb)=>{
     const sql = 'SELECT * FROM kdt.visitor where id=?';
     conn.query(sql, id, (err, rows) => {
        if (err) throw err;
        cb(rows[0]);
    })
}

// 수정 버튼 > 변경
exports.patchContent = (data, cb)=>{
    const sql ="UPDATE visitor SET name =? , comment =? WHERE id = ?";
    const values=[data.name, data.comment, data.id]
    conn.query(sql, values, (err, rows)=>{
       cb(rows)
    })
}

// 삭제 버튼 > 삭제
exports.deletedb = (id, cb)=>{
    const sql ="delete from visitor WHERE id = ?";
    conn.query(sql, id, (err, rows)=>{
       cb(rows)
    })
}

 

 

다음으로 controller에 model의 데이터를 받아서 페이지를 렌더링하고 다시 데이터를 내보내는 함수를 정의했다.

마찬가지로 exports까지 진행해주었다.

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

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

exports.allVisitorsList = (req,res)=>{
    Visitor.allVisitorsList(result=>{
        res.render('visitor', {data:result})
    })
}

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

exports.getShowAvisitor=(req,res)=>{
    Visitor.getShowAvisitor(req.params.id, (result) =>{
      res.send(result)
    })
}

// 수정버튼
exports.patchVisitor =(req,res)=>{
    Visitor.patchContent(req.body, (result)=>{
     res.send(result)
    })
 }

 // 삭제버튼
exports.deleteVisitor=(req,res)=>{
    Visitor.deletedb(req.body.id, (result)=>{
      res.send('삭제성공')
    })
}

 

 

 

이렇게 controller에서 exports한 함수는 routes에서 경로를 설정하는 데 이용할 수 있다.

 

예를들어, '/' 경로가 실행되면 controller에 있는 main함수가 실행되고, '/visitors' 경로가 실행되면 controller의 allVisitorsList 함수가 실행되도록 만들어 주었다.

 

다음으로 정의한 router를 다른 파일에서 사용할 수 있도록 모듈을 exports 했다.

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

// 첫화면
router.get('/', controller.main)

// 첫화면 + 방명록 남기기
router.get('/visitors', controller.allVisitorsList)

// 방명록에 사용자 추가
router.post('/addvisitor', controller.addVisitor)

// 수정버튼
router.get('/visitormodify/:id', controller.getShowAvisitor)

// 수정버튼 > 변경버튼 > 수정
router.patch('/visitoreditDo', controller.patchVisitor)
 
// 삭제버튼
router.delete('/visitordelete', controller.deleteVisitor)

module.exports = router;

 

 

exports한 router는 app.js에서 불러와 페이지가 실행되도록 하는 데 사용된다.

const indexRouter = require('./routes/index')
app.use('/', indexRouter)

 

 

 

이렇게 만든 데이터를 화면에 보여주기 위한 페이지를 ejs파일로 view 폴더에 만들어주었다.

 

데이터는 테이블 형태로 for문을 통해 자동적으로 추가되고 삭제되는 데이터가 보여지도록 정의했다.

 

view폴더 ejs파일

 

 

728x90

 

 

마지막으로 버튼의 동작에 대한 기능을 정의하기 위해 static 폴더에 js파일을 만들어 axios로 동작을 설정했다.

innerHTML, textContent, document.querySelector 등을 이용해 동작을 실행했다.

 

 

먼저, ejs 파일에서 필요한 부분인 tbody와 button-group을 읽어왔다.

 

const tbody = document.querySelector('tbody');
const btnGroup = document.querySelector('#button-group');

 

 

방문자를 추가할 때 form의 유효성 검사를 진행하고 axios로 데이터를 받아 테이블에 추가할 수 있도록 함수를 설정했다.

 

function createVisitor(){
    const form = document.forms['visitor-form'];
    if (form.name.value.length === 0 || form.comment.value.length === 0) {
        alert('이름 또는 방명록 기입해주세요!');
        return;
    }
    if (form.name.value.length > 10) {
        alert('이름은 10글자 미만입니다!');
        return;
    }

    axios({
        method: 'post',
        url: '/addvisitor',
        data: {
            name: form.name.value,
            comment: form.comment.value
        }
    }).then(res=>{
        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" onclick="editVisitor(${data.id})">수정</button></td>
                <td><button type="button" onclick="deleteVisitor(this, ${data.id})">삭제</button></td>
            </tr>
        `;
 
        tbody.insertAdjacentHTML('beforeend', html)
        form.reset();
    })
}

 

 

 

수정버튼이 눌리면 등록버튼이 '변경', '취소' 버튼으로 바뀌도록 설정했으며, 수정하려는 데이터가 form에 보이도록 해주었다.

 

function editVisitor(id){
    alert(`찾는 1명의 id=${id}`);

    axios({
        method: 'get',
        url: `/visitormodify/${id}`
    }).then(res=>{
        const {name, comment} = res.data;
        const form = document.forms['visitor-form'];
        form.name.value = name;
        form.comment.value = comment;
    })

    const html = `
        <button type='button' onclick='editDo(${id})'>변경</button>
        <button type='button' onclick='editCancel()'>취소</button>
    `

    btnGroup.innerHTML = html;
}

 

 

수정 버튼 > 변경, 취소 버튼으로 변경

 

 

 

따라서 내용을 편집한 후 변경버튼을 누르면 데이터가 수정되고, 취소버튼을 누르면 원래대로 돌아가도록 하는 함수도 정의했다.

 

function editDo(id){
    const form = document.forms['visitor-form'];
    axios({
        method:'patch',
        url:'/visitoreditDo',
        data:{
            id:id,
            name : form.name.value,
            comment: form.comment.value
        }
    }).then(res=>{
        const children = document.querySelector(`#tr_${id}`).children;
        children[1].textContent = form.name.value;
        children[2].textContent = form.comment.value;
        editCancel();
    })
}

function editCancel(){
    const form = document.forms['visitor-form'];
    form.reset();
 
    const html = `<button type='button' onclick='createVisitor()'>등록</button>`;
    btnGroup.innerHTML = html;
}

 

 

마지막으로 삭제 버튼을 누르면 데이터가 삭제되도록 하는 함수도 정의해 방명록을 완성할 수 있었다.

 

function deleteVisitor(obj, id){
    if (!confirm('정말 삭제하시겠습니까?')) return;
    axios({
        method:'delete',
        url:'/visitordelete',
        data:{ id:id}
    }).then(res => {
        obj.closest(`#tr_${id}`).remove()
    })
}

 

 

렌더링된 페이지에서 추가되고 변경된 데이터들은 모두 mysql에도 연결되어 저장된다.

 

mysql에 저장된 방명록 변경 데이터

 

 


 

 

MVC와 MySQL을 연결하는 방법과 여러 기능들을 정의하는 방식이 매우 복잡해서 코드 하나하나 이해가 필요했고, 아직도 헷갈리는 부분이 꽤 많아서 지속적으로 연습해보는 것이 필요할 것 같다.

특히, 매개변수로 데이터를 받아오고 다시 내보내는 과정이 이해하는 데 오래걸렸고 데이터가 저장되는 각 파일들의 위치도 정확하게 파악하고 있어야 한다는 필요성을 느꼈다.

기능에 따라 폴더와 파일이 나누어져 있어 오류를 찾거나 에러 발생 시 원인이 되는 위치로 이동하기가 매우 편리해서 좋았다.
팀프로젝트를 진행할 때 유용하게 사용해야 할 개발 구조 방식이라는 생각이 든다.

익숙하게 사용하기 위해서는 지속적으로 연습을 많이 해봐야할 것 같다.
728x90