📂 목차
- Node.js MVC : 로그인 기능 구현
- Node.js MVC with MySQL (실제 데이터베이스에 연결하기): 방명록 만들기
- POSTMAN 사용하기 (UI 없이 post, delete, patch 테스트하기)
MVC는 소프트웨어 설계에서 자주 사용되는 디자인 패턴 중 하나로, 데이터를 처리하는 Model, 화면을 담당하는 View, 그리고 Model과 View를 연결하는 Controller의 앞글자를 따서 만들어진 이름이다.
🚀 로그인 기능
13.mvc-practice
로그인 페이지 (동적Form MVC 패턴 적용) ver1, 2
`checkValidity()`
if (!form.userId.checkValidity() || !form.userPw.checkValidity()) {
result.textContent = '아이디와 비밀번호는 필수 값입니다!';
return;
}
로그인 유효성 검사 중 checkValidity() 사용하면 , required 속성이 있는 항목은 True, 없으면 false를 반환한다.
🚀 방명록 만들기
14.mvc with MySQL + 99.env
드디어 하드 코딩 졸업!!! MySQL에서 생성한 실제 DB와 연결해서 방명록을 만들었다.
1. 개발환경 세팅
npm init -y // 프로젝트 초기화
npm i express ejs mysql2 // 패키지 설치
# app.js 파일 생성
# Mvc패턴에 따른 폴더 및 파일 생성
2. MVC 패턴 폴더 구조
14.mvc-mysql_guestbook/
│
├── controller/
│ └── Cvisitor.js
│
├── model/
│ └── Visitor.js
│
├── node_modules/
│
├── routes/
│ └── index.js
│
├── static/
│ └── visitor.js
│
├── views/
│ ├── 404.ejs
│ ├── index.ejs
│ └── visitors.ejs
│
├── app.js
├── package-lock.json
└── package.json
3. DB 연결
(1) 로컬 mySQL 서버(localhost) 접속
// CLI 시작하기: MySQL root 사용자로 접속
mysql -u root -p
// 특정 사용자와 호스트로 접속하기
// mysql -u [사용자이름] -p -h [호스트이름]
mysql -u suyeon -p -h localhost
(2) mysql 데이터베이스 테이블 생성
이전에 하드코딩한 임시 데이터를 실제 visitor 테이블을 생성해서 데이터 추가하기
(3) 프로젝트와 mysql 연결하기
4. 방명록 전체 조회 기능 구현을 통해 MVC 흐름 이해하기 (feat. 콜백함수)
모델을 함수화하여 방명록 전체를 조회해보자.
Controller/ Cvisitor.js
const Visitor = require('../model/Visitor');
// GET / => localhost:PORT/
exports.main = (req, res) => {
res.render('index');
};
// GET /visitors => localhost:PORT/visitors
exports.getVisitors = (req, res) => {
/* [DB 연결 전] */
// res.render('visitors', { data: Visitor.getVisitors() });
/* [DB 연결 후] */
// Model.Visitor의 getVisitors 호출
// -- model.Visitor의 결과물 cb(rows) = 인자 result(배열 형태)
Visitor.getVisitors((result) => {
// 전체목록 Cvisitor.js : [{id: , name: , comment: }, ...]
console.log('전체목록 Cvisitor.js', result);
// 콜백함수: 데이터를 visitors라는 Ejs템플릿에 전달해서 렌더링
res.render('visitors', { data: result });
});
};
Model/ Visitor.js
const mysql = require('mysql2');
const conn = mysql.createConnection({
host: 'localhost',
user: 'suyeon',
password: 'my password',
database: 'sesac',
});
exports.getVisitors = (cb) => {
// 연결 테스트
conn.connect((err) => {
if (err) {
console.error('데이터베이스 연결 실패:', err);
return;
}
console.log('데이터베이스에 성공적으로 연결되었습니다.');
});
// conn.query 비동기로 DB에서 데이터 조회
conn.query('SELECT * FROM visitor', (err, rows) => {
// 쿼리 실행 중 에러 발생하면 중단
if (err) throw err;
// 쿼리 결과 Visitor.js : //[{id: , name: , comment: }, {}, ..]
console.log('쿼리 결과 Visitor.js:', rows);
cb(rows); //결과 데이터를 Cvisitor.js 컨트롤러로 전달
});
};
`conn` 객체 안의 query 메서드 사용하여 비동기로 데이터베이스에서 데이터를 조회한 결과를 컨트롤러에 전달한다.
콜백함수
콜백함수는 어떤 작업이 완료된 후 실행될 함수이다. 여기서는 컨트롤러의 `Visitor.getVisitors`에 전달한 함수 `(result) => {...}`가 콜백함수이다.
컨트롤러는 데이터를 받아야 하는 입장이고, 모델은 데이터를 가져오는 역할을 한다. 모델은 데이터를 가져온 후 콜백함수로 데이터를 컨트롤러에 전달한다.
콜백함수 동작 순서
(1) `Visitor.getVisitors((result) => { ... })`
// controller/ Cvisitor.js
exports.getVisitors = (req, res) => {
Visitor.getVisitors((result) => {
// 이 부분이 콜백 함수
// result는 Visitor.getVisitors의 cb(rows)에서 전달된 데이터
res.render('visitor', { data: result });
});
};
컨트롤러에서 모델 Visitor.getVisitors에 콜백함수 `(result) => { ... }`를 전달한다. 콜백함수는 DB 조회가 끝난 후 실행될 함수이다.
즉, 컨트롤러는 모델에게 데이터를 가져오라고 요청하면서, 데이터를 가져오면 이 함수를 실행하라는 뜻으로 함수의 인자에 함수(콜백함수)를 전달하고 있다.
(2) 모델`Visitor.js`에서 쿼리를 실행해서 DB 작업이 완료되면:
// model/ Visitor.js
// 1. 전체 목록 조회
exports.getVisitors = (cb) => {
conn.query(`select * from visitor`, (err, rows) => {
if (err) {
throw err;
}
console.log('모델', rows);
/*모델 [
{ id: 1, name: '산타', comment: '호호호호' },
{ id: 2, name: '루돌프', comment: '메리 크리스마스!!!' }
]*/
cb(rows);
// 컨트롤러가 전달한 함수(콜백함수) 호출, 결과 데이터(rows)를 컨트롤러에 전달
});
};
- `cb(rows)`를 호출한다.
- 이때, `cb`로 전달된 함수는 컨트롤러에서 넘긴 `(result) => { ... }`이다.
(3) 컨트롤러로 돌아가서:
`result`는 `rows`로 채워진 데이터를 담고, 컨트롤러의 콜백함수가 실행된다.
// controller/ Cvisitor.js
exports.getVisitors = (req, res) => {
Visitor.getVisitors((result) => {
// 이 부분이 콜백 함수
// result는 Visitor.getVisitors의 cb(rows)에서 전달된 데이터
res.render('visitor', { data: result });
});
};
routes/ index.js
const express = require('express');
const router = express.Router();
const controller = require('../controller/Cvisitor');
// GET /
router.get('/', controller.main);
// GET /visitors
router.get('/visitors', controller.getVisitors);
작성한 컨트롤러를 라우터와 연결하여 특정 URL 경로(http://localhost:8080/visitors)에서 컨틀롤러의 함수가 실행될 수 있도록 설정한다. 컨트롤러에는 라우터가 실행할 실제 비즈니스 로직(DB 작업 등)이 작성되어 있다.
라우터 파일은 위와 같이 모델과 컨트롤러가 작성된 후 작성한다. 라우터는 컨트롤러를 호출하고, 컨트롤러는 모델을 사용하여 데이터를 처리하기 때문이다. 마지막으로 `app.js`와 연결하여, 이 라우터를 불러와야 경로 설정이 유효해진다.
app.js
const express = require('express');
const app = express();
const PORT = 8080;
// middleware 설정
app.set('view engine', 'ejs');
app.use('/views', express.static(__dirname + '/views'));
app.use('/static', express.static(__dirname + '/staitc'));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// 라우터 연결
const indexRouter = require('./routes');
app.use('/', indexRouter); // localhost:PORT/ 경로를 기본으로 ./routes/index.js 파일에 선언한 대로 동작
// 404 Err
app.get('*', (req, res) => {
res.render('404');
});
// 서버 시작
app.listen(PORT, () => {
console.log(`http://localhost:${PORT}`);
});
정리하면, 모델 → 컨트롤러 → 라우터 와 같이 흐름을 이해할 수 있다.
- 모델: 데이터베이스 처리
- 컨트롤러: 비즈니스 로직 구현(DB 작업 수행 등)
- 라우터: 경로와 컨트롤러 연결
5. 방명록 신규 데이터 등록 (feat. POSTMAN 테스트)
post, delete, patch는 UI가 없을 때 POSTMAN에서 테스트를 해볼 수 있다.
Civisitor.js
const Visitor = require('../model/Visitor');
exports.main = (req, res) => {
res.render('index');
};
// 3. POST/ visitor
exports.postVisitor = (req, res) => {
console.log(req.body);
Visitor.postVisitor(req.body, (result) => {
console.log('Cvisitor.js', result);
res.send('response');
});
};
Model/ Visitor.js
// DB 연결 후
const mysql = require('mysql2');
const conn = mysql.createConnection({
host: 'localhost',
user: 'suyeon',
password: '1125',
database: 'selfstudy',
});
// 3. 데이터 등록
// visitor 테이블에 데이터 삽입
// data는 클라이언트에서 받은 req.body 정보 (commnet, name 포함한 객체 형태)
exports.postVisitor = (data, cb) => {
conn.query(
`insert into visitor value(null, "${data.name}", "${data.comment}")`,
(err, rows) => {
if (err) throw err;
console.log('모델의 post', rows);
cb(rows.insertId);
}
);
};
routes/ index.js
const express = require('express');
const router = express.Router();
const controller = require('../controller/Cvisitor');
// GET /
router.get('/', controller.main);
// GET /visitors
router.get('/visitors', controller.getVisitors);
// GET /visitor/:id
router.get('/visitor/:id', controller.getVisitor);
// POST /visitor
router.post('/visitor', controller.postVisitor);
module.exports = router;
6. 프론트엔드와 백엔드를 연결하는 `static/ visitor.js` 작성하기
모델, 컨트롤러, 라우터 작성 후 프론트엔드와 백엔드를 연결하여 클라이언트 UI와 서버 간 데이터 흐름을 처리하는 스크립트를 작성해야 한다.
주요 기능:
- 방명록 등록, 삭제, 수정 요청 처리: 사용자가 입력한 데이터를 서버에 전송/ 서버에서 데이터를 받아 화면에 업데이트
- UI 업데이트와 데이터 연동: 서버 응답에 따라 테이블 데이터 동적 업데이트/ 사용자 경험 향상
작성한 스크립트를 뷰(views/visitor.ejs)와 연동하여 실제로 동작하는지 테스트해보자.
✍🏻 회고
어려웠던 내용
1. 클라이언트와 서버 간 데이터 흐름
특히 함수나 조건문 안에서 콘솔 로그를 자주 확인하지만, 이 데이터가 클라이언트 측인지, 서버 측인지 헷갈릴 때가 많다. 또 MVC패턴이 낯설기도 하고, 이전에는 프론트엔트의 views 디렉토리만 사용했었는데 이제 Model, views, controller, routes로 분리해서 사용해야 하니 익숙해지는 데 시간이 필요한 것 같다. 지금으로서는 한땀 한땀 주석 작성하면서 이해 중이다.
2. 실제 데이터베이스에 연결하기
임시DB 처리도 어려웠는데, 실제 DB는 더 어려웠다.
참고
- 새싹x코딩온 웹개발자 풀스택 과정
'Today I Learned > SeSAC 웹 2기' 카테고리의 다른 글
React useRef, Lifecycle(useEffect) | 12주차 (1) (0) | 2025.01.14 |
---|---|
React Event Handling | 11주차 (3) (0) | 2025.01.14 |
MySQL 입문 | 6주차(1) (1) | 2024.11.27 |
Node.js Form 전송(get, post 요청) 과제 회고 | 5주차(1) (1) | 2024.11.27 |
Node.js 입문, Express 모듈 서버 만들기 | 4주차(2) (0) | 2024.11.21 |