이번 스프린트에서는 쇼핑몰 애플리케이션 Cmarket의 데이터베이스를 구축합니다. 모든 요청은 반드시 데이터베이스를 이용해야 합니다. 구현한 Cmarket이 영속적인 데이터를 가질 수 있도록, 더 이상 배열이나 객체 등을 이용해서 인메모리 형태로 데이터를 저장하지 마세요.
먼저 Sprint를 받으면 npm모듈을 설치하고, 내부에 있는 파일들을 하나씩 살펴보자.
여기서 제일 먼저 해야할 것은 환경변수를 설정해주는 일이다. .env.example파일을 .env으로 바꿔주고, .env파일 내부에 있는 MySQL 비밀번호에 대한 환경변수 설정을 해준다.
const dotenv = require('dotenv');
dotenv.config();
const config = {
development: {
host: 'localhost',
user: 'root',
password: process.env.DATABASE_SPRINT_PASSWORD,
database: 'cmarket'
},
test: {
host: 'localhost',
user: 'root',
password: process.env.DATABASE_SPRINT_PASSWORD,
database: 'cmarket_test'
}
};
module.exports = config;
이번 스프린트를 해결하기 위해서는 파일을 하나하나 뜯어보는 것이 중요하다. config/config.js에서는 dotenv을 이용한 환경변수 모듈이 require되어 있는 것을 볼 수 있다. 해당 파일을 MySQL에 들어가기위한 정보를 참조하기 위한 파일이라고 보면 될 것이다.
const mysql = require('mysql');
const dotenv = require('dotenv');
const config = require('../config/config');
dotenv.config();
const con = mysql.createConnection(
config[process.env.NODE_ENV || 'development']
);
con.connect((err) => {
if (err) throw err;
});
module.exports = con;
db/index.js에서는 config/config.js 파일을 가져오고 있고, MySQL, dotenv의 모듈도 사용하고 있다. npm의 공식문서에 보면, 소개와 함께, 아래와 같은 코드로MySQL을 사용한다고 나와있다.
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'me',
password : 'secret',
database : 'my_db'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
connection.end();
공식문서에 나와있는 간단한 코드를 살펴보면 config/config.js의 파일과 db/index.js파일이 합쳐졌다고 볼 수 있겠다.
참조
mysql
const express = require('express');
const indexRouter = require('./routes');
const cors = require('cors');
const morgan = require('morgan');
const app = express();
const port = 4000;
app.use(
morgan(' :method :url :status :res[content-length] - :response-time ms')
);
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/', indexRouter);
module.exports = app.listen(port, () => {
console.log(` 🚀 Server is starting on ${port}`);
});
다음 app.js의 파일을 살펴보면 여러 모듈들이 참조되어 있는 것을 볼 수 있다. 이전에 배웠던 cors, express가 require되고 있으며 express의 .use를 이용해서 미들웨어를 활용하는 것을 볼 수 있다. localhost로의 접속을 해주기 위한 port = 4000 있음을 볼 수 있고, routes의 폴더도 미들웨어로 활용되고 있음을 볼 수 있다.
const express = require('express');
const router = express.Router();
const itemsRouter = require('./items');
const usersRouter = require('./users');
// TODO: Endpoint에 따라 적절한 Router로 연결해야 합니다.
router.use('/items', itemsRouter);
router.use('/users', usersRouter);
module.exports = router;
위의 코드를 무작정 답만 찾기 보다는 해당하는 파일의 역할이 무엇인지 아는것이 가장 중요하다. routes 폴더는 routing역할을 하는 폴더이다. 그 중심엔 index.js라는 파일이 있고, 여기서 EndPoint에 따른 분기가 일어나는 것이다.
app.js에서 routes폴더를 참고 하고 있고 해당 폴더를 미들웨어로써 실행시키고 있다. 클라이언트의 요청에 따라서 데이터를 분류할 때, 해당 데이터의 요청이 어디로 갈지 길을 알려주는 곳이라고 생각하면 되겠다.
.env를 설정한 이후 sprint에서는 user.js라는 파일을 만들으라고 한다. 이는 routes/index.js에서 분기된 요청이 user.js에 흘러 들어가야 한다는 것이다. 그렇기 때문에, require을 사용하여 만들었던 ./user 파일을 usersRouter로 설정하여 app.js에서 사용하였던 미들웨어처럼 자동적으로 분기 될 수 있도록 하는 것이다.
const usersRouter = require('./users');
router.use('/users', usersRouter);
위의 두 줄을 추가하여 users.js 파일로의 분기가 될 수 있도록 한다.
const router = require('express').Router();
const controller = require('./../controllers');
// GET /items Router와 Controller를 연결합니다.
router.get('/', controller.items.get);
module.exports = router;
routes/index.js에서 분기된 첫 번째 파일이다. items.js로의 요청이 들어온다면 해당 파일에서 해당 controller로의 분기가 될 수 있도록 한다. 위의 코드에서 분기가 되는 부분인 아래의 코드를 살펴보자.
router.get('/', controller.items.get);
express의 공식문서에 따르면, get method를 사용할 때는 첫 번째 인자로는 해당 route의 인자를 사용하고 두 번째로는 콜백함수가 들어가는 것을 볼 수 있다.
참조
미들웨어 사용
const router = require('express').Router();
const controller = require('./../controllers');
// GET /items Router와 Controller를 연결합니다.
router.get('/:userId/orders', controller.orders.get);
router.post('/:userId/orders', controller.orders.post);
module.exports = router;
해당 파일도 items.js와 같이 route 설정을 해주면 되겠다. 하지만 여기서 요구하는 조건은 items.js와는 다르다. get요청과 post요청 또한 실행해야 하는 것이다.
하지만 위와 같은 답을 적기 전에, 이 파일이 무엇을 하는 파일인지를 생각해보자. items.js라는 파일은 아이템의 목록을 가져오도록 하는 get요청을 수행하기 위한 route라고 봐도 무방하다. 하지만, user.js는 어떤 역할을 할까?
sprint의 client를 npm start해보면, 해당 페이지에 주문내역이 존재한다. user.js는 주문내역에 관한 route인 것이다. 주문내역에는 “누가 주문했는가?”로 route가 될 수 있겠다. user 각각의 고유 ID로 분기되기 때문에, params 요청으로 /:userId/orders 와 같은 분기가 필요한 것이다.
하여, 아래와 같은 route를 사용한 것이다.
router.get('/:userId/orders', controller.orders.get);
router.post('/:userId/orders', controller.orders.post);
sprint가 요구하는 get과 post를 수행하기 위한 router를 아래와 같이 연결해주고,
const router = require('express').Router();
const controller = require('./../controllers');
controller로의 분기를 위해 controller의 폴더 또한 참조해준다.
items.js에서 봤던 궁금증이 또 발생하게 된다. 첫 번째 인자로 route경로를 설정해 주었고 콜백함수가 들어가야하는데 여기서도 controller.orders.get과 controller.orders.post가 사용되고 있다. 이러한 궁금증을 해결하기 위해 controller/index.js를 살펴보자.