Dev/SW Engineering

31. Databases - Client-Server-Database

HJChung 2020. 8. 31. 23:12

이번 sprint에서는 대표적인 관계형 데이터베이스관리 시스템(RDBMS)인 MySQL을 이용하여 Schema를 설계하고 SQL을 사용하여 데이터를 영속성있게(persisstently)저장하는 방법을 배우는 것이 목표였다. 

 

그래서 앞에서는 데이터베이스 시스템 구조 및 기본 개념, 쿼리문 작성 등에 대해서 정리해보았다. 

여기서는 본격적으로 데이터베이스를 서버와 연결하고 영속적인 데이터를 요청하고 받아오는 방법에 대해서 배운 것을 정리해보고자 한다.

 

1. 클라이언트, 서버, 데이터베이스의 관계

2. 필요한 스키마를 디자인하고 작성하기

Client의 Browser의 화면은 

이런식이고, 

기존의 in-memory방식으로 데이터를 저장했을 때의 방식은 

{id:1, username:'grace', text:'hello world!', roomname:'tech_talk'} 이런 방식이었다. 

 

이와 같은 기능을 하도록 스키마를 디자인해보면, 

이렇게 된다.

  • schema.sql
CREATE TABLE user(
    id int NOT NULL AUTO_INCREMENT, 
    username varchar(255) NOT NULL,
    PRIMARY KEY(id)
);

CREATE TABLE messages(
    id int NOT NULL AUTO_INCREMENT, 
    userId varchar(255) NOT NULL,
    roomname varchar(255) NOT NULL,
    text text(1024),
    PRIMARY KEY(id)
);

그리고 

 mysql -u root -p< [schema.sql파일 경로]

을 통해 내 MySQL 서버에 위에서 만든 스키마가 생성될 수 있게 한다.

확인해보면 잘 들어간 것을 확인할 수 있다. 

3. 서버와 데이터베이스의 상호작용

1) 먼저 npm을 이용해 mysql npm 모듈을 설치해서 해당 애플리케이션의 Server가 MySQL server와 상호작용할 수 있게 만든다. 

더보기

By the way, what is the difference between mysql and mysql2 at npm?
check this out

아직 공부하지 않았다.. 공부하고 업뎃할 예정..

2) 그리고 서버를 작성하고 실행하기 전에 MySQL 비밀번호를 보안상 환경 변수로 작성해서 분리해 놓는다.  

$ export DATABASE_SPRINT_PASSWORD=[자신의 비밀번호]

이렇게 사용하면 실제 db를 연결하는 index.js에서는 아래와 같이 사용할 수 있다. 

const password = process.env.DATABASE_SPRINT_PASSWORD;

하지만 이러한 방식은 지역 환경 변수(local environment variable)이기 때문에  현재 로그인 중인 세션에서만 동작(접속 종료하면 사라짐)해서 해당 터미널창을 닫으면 다시 매번 작성해줘야한다는 귀찮음이 있다. 

 

3) mysql과 node.js 연결 - uses the mysql npm module to connect to the database server running on your computer

그러기 위해서 데이터베이스 연결을 만들고 연결 객체를 export한다. 

  • server/db/index.js 
const mysql = require("mysql");
const password = process.env.DATABASE_SPRINT_PASSWORD;
const host = 'localhost';

//reference: https://www.w3schools.com/nodejs/nodejs_mysql.asp
const dbConnection = mysql.createConnection({
  host: host,
  user: 'root',
  password: password,
  database: 'chat',
})

dbConnection.connect(function (err) {
  if (err) throw err
  console.log('Connected!')
})

module.exports = dbConnection

Connected!로 잘 나온다 :))

 

4. 웹애플리케이션 서버 작성

1) model 에 해당하는 부분 작성

model은 애플리케이션의 정보, 데이터를 나타낸다. 데이타베이스, 처음의 정의하는 상수, 초기화값, 변수 등을 뜻한다. 또한 이러한 DATA, 정보들의 가공을 책임지는 컴포넌트를 말한다.

그래서 여기서 쿼리문을 작성해서 연결된 db에서 데이터를 insert하거나 받아올(select)해올 수 있다. 

  • server/models/index.js: defines the messages and users models that your application will use. 
  • post는 데이터베이스에 insert하는 기능이다. insert into [column1, colum2, ..] value [value1, value2, ...] 에서는 특정 columns가 있고, 이를 parameter로 준다. 그리고 value에 해당하는 부분은 ? 로 한다.  (이 외에도 value가 들어가는 쿼리문의 경우 '?'을 사용한다.(예를 들어, 'SELECT * FROM `books` WHERE `author` = ?', ['David'],))
  • github.com/mysqljs/mysql#performing-queries
 

mysqljs/mysql

A pure node.js JavaScript Client implementing the MySQL protocol. - mysqljs/mysql

github.com

여기에 query문을 작성하는 여러 방법이 나와있다. 

대표적으로 

var userId = 'some user provided value';
var sql    = 'SELECT * FROM users WHERE id = ' + connection.escape(userId);
connection.query(sql, function (error, results, fields) {
  if (error) throw error;
  // ...
});

이런식으로 작성할 수 있다. 

그러면, 

이런 구조의 테이블에서 

1.messages의 정보와 그 message를 작성한 user 정보(username) get 해오는 model의 get 함수를 구현하려면?

//server/models/index.js
var db = require('../db')
module.export = {
	messages: {
         get: function(callback){
            var sql = 'messages.id, messages.text, messages.roomname, users.username FROM messages RIGHT JOIN users ON (user.username=messages.userId);',
            db.query(sql, function(err, results) {callback(err, results);}) //이 callback부분은 controller에서 담당한다. 
       }
         	

2. message가 작성되면 그걸 messages 테이블에 추가하는 model의 post 함수를 구현하려면?

☆ 다시한번 강조!! '?' 을 이용해서 parameter를 준다.

아 그리고 limit이 사용된다. limit 출력되는 레코드의 개수를 제한할 때 사용된다. 

blog.naver.com/hateatom/100135786033 여기 mysql limit 사용법에 대해 잘 나와있다. :))

//server/models/index.js
var db = require('../db')

module.exports = {
	messages:{
          post: function (messsageData, callback) {
                var sql = `INSERT INTO messages(text, userId, roomname) VALUES (?, (select id from users where username=? limit 1),?;)`,
                db.query(
                  sql, 
                  messsageData,
                  function(err, results) {
                        callback(err, results);
                    } // 이 callback부분은 controller가 담당한다. 
                )
              },
      }
},

2) controller 에 해당하는 부분 작성

위에서 model에서 db관련해서 쿼리문을 통해 데이터를 다룬 이후, callback은 controller에서 작성된 것이 실행된다고 하였다. 

왜냐하면 controller가 사용자가  model을 통해서 데이터를 가져오고 그 정보를 바탕으로 시각적인 표현을 담당하는 View를 제어해서 사용자에게 전달하는 역할을 담당하기 때문이다. (사실 이렇게 적으면서도 아직 MVC 디자인 패턴을 많이 다뤄보지 못해서 자신이 없다..)

그래서,

1. model의 get 함수에서 messages의 정보와 그 message를 작성한 user 정보(username) get해왔다면 controller에서는?

잘 받아온 결과를 json화 해서 view에게 전달한다. 

//server/controllers/index.js
var models = require('../models')

module.exports = {
  messages: {
    get: function(req, res) {
      models.messages.get(function(error, results) {
        if (error) {
          res.send(error);
        }
        res.json(results);
      });
    },

2. message가 작성되면 그걸  model의 post메소드의 인자로 넘겨주는 controller는?  그리고 model의 post 메서드에서 쿼리문을 통해 messages 테이블에 추가해왔다면  controller에서는?

//server/controllers/index.js
var models = require('../models')

module.export = {
	messages: {
    	post: function(req, res){
        	var params = [req.body.text, req.body.username, req.body.roomname]
        	models.messages.post(params, function(error, results){
            	if(error){
                	throw error
                }
                res.sendStatus(201)
            })
        }
   }
}
      

 

지금까지 관계형 데이터베이스관리 시스템(RDBMS)인 MySQL을 이용하여 Schema를 설계하고 서버와 연동하여 SQL을 사용하여 데이터를 영속성있게(persisstently)저장하고 불러오는 방법을 정리해보았다. 

 

reference

github.com/mysqljs/mysql

 

mysqljs/mysql

A pure node.js JavaScript Client implementing the MySQL protocol. - mysqljs/mysql

github.com