[8기 9회차] 0909 웹 서비스의 이해 - Docker, mariaDB, Node.js에 DB연동
데이터베이스
데이터베이스란?
- 데이터를 통합하여 효율적으로 관리하기 위한 데이터 집합체를 데이터베이스(DB)라고 한다.
- 데이터를 구조화하여 관리함으로써 데이터 중복을 막고, 효율적이고 빠른 데이터 연산을 가능하게 한다.
DBMS
- 데이터베이스를 운영하고 관리하기 위한 DBMS(DataBase Management System)를 통해 데이터베이스 사용
DBMS의 종류
오라클 / MySQL / MariaDB
SQL(Structered Query Language)
- SQL은 데이터베이스에 연산을 요청하기 위해 사용되는 언어로 데이터를 생성, 조회, 수정, 삭제 등과 같은 기능 수행할 수 있다.
- SQL 종류
- 데이터삽입 : INSERT
- 데이터조회: SELECT
- 데이터수정: UPDATE
- 데이터삭제: DELETE
도커 개요

도커(Docker)는 *컨테이너 기반 가상화 플랫폼으로, 응용 프로그램과 그 종속성을 격리된 환경인 컨테이너로 패키징하여 실행하는 기술이다. 이를 통해 응용 프로그램을 서로 다른 환경에서도 일관되게 실행할 수 있고, 개발 환경과 운영 환경 사이의 차이로 인한 문제를 줄일 수 있다. 도커 컨테이너는 가볍고 빠르며 확장성이 좋아서 개발 및 배포 프로세스를 간소화하는 데 사용된다.
*컨테이너란
- 소프트웨어 개발에 필요한 코드나 라이브러리, 버전 같은 것들을 묶어서 컨테이너로 만든다.컨테이너를 통해 다른 컴퓨터(서버)에 운반할 수 있게 된다.- Container는 격리된 공간에서 프로세스가 동작하는 기술
→ 다른 OS에서도 문제없이 개발 가능
- Container를 만들기 위해서는 dockerfile, image라는 게 필요하다.
도커를 이용한 마리아db 설치
- 마리아DB 가져오기
- docker pull mariadb
- 마리아db 설정
- docker run --name mariadb -d -p 3306:3306 --restart=always -e MYSQL_ROOT_PASSWORD=root mariadb
- mariadb가 있는 컨테이너 접속
- docker exec -it mariadb /bin/bash
- mariadb 실행
- mysql -u root -p
- 패스워드 입력
- root
<이슈>
- 문제 : mysql -u root -p 로 mariadb 실행하려고 하면 bash: mysql: command not found 에러가 뜬다.
- 원인 : 찾아보니 MariaDB 10.5 이후부터는 mysql이 아니라 mariadb라는 이름으로 클라이언트를 제공하기 때문이었다.
- 해결방안:
1) 컨테이너 안에서 mariadb -u root -p 실행
2) mysql 명령어로도 쓰고 싶다면(심볼링 링크 만들기)- ln -s /usr/bin/mariadb /usr/bin/mysql 로 심볼릭 링크 만들기
SQL : CREATE
docker로 mariadb 실행
- docker desktop 프로그램 실행
- cmd 실행
- mariadb가 있는 컨테이너 접속 : docker exec -it mariadb /bin/bash
- mariadb 실행 > mysql -u root -p
데이터 저장 방 만들기
- 방 확인 : SHOW DATABASES;
- 방 만들기 : CREATE DATABASE Tennis;
- 방 들어가기 : USE Tennis;
방 안에서 저장소 만들기
- 테이블 형태
CREATE TABLE member
(
id VARCHAR(30),
name VARCHAR(30),
pwd VARCHAR(30)
);
CREATE TABLE PRODUCT
(
id INT,
name VARCHAR(30),
description VARCHAR(100),
price INT );
- 테이블 보기 : SHOW TABLES;
SQL : SELECT, INSERT
테이블 데이터 조회
- SELECT 컬럼명 FROM 테이블명;
- SELECT * FROM 테이블명;
- SELECT * FROM 테이블명 WHERE id = 조건;
테이블 데이터 삽입
- INSERT 컬럼명1, 컬럼명2, INTO 테이블명
VALUES(컬럼1 데이터, 컬럼2 데이터...);INSERT INTO product VALUES(1,'Red Racket','nice red!',300000); INSERT INTO product VALUES(2,'Blue Racket','cool blue!',350000); INSERT INTO product VALUES(3,'Black Racket','chic black!',500000);
SQL : UPDATE, DELETE
테이블 데이터 수정
- UPDATE 테이블명 SET 컬럼명 = 수정할 값 WHERE 조건
- UPDATE member SET pwd = 'zzzzz' WHERE id = 'tennisking';
- 조건 안 걸면 모든 행 수정됨
테이블 데이터 삭제
- DELETE FROM 테이블명 WHERE 조건;
- DELETE FROM member WHERE name='park';
node.js에 db 연동 준비
vscode에 mysql 모듈 설치
- npm install mysql --save
node.js에 db 연결
- 경로 : database\connect\mariadb.js
설정 파일 만들기 SELECT 테스트
해당 경로에 mariadb.js 생성
- \database\connect\mariadb.js
// mariadb.js
// MySQL 연동
const mariadb = require('mysql');
// 연결 통로 만들기
const conn = mariadb.createConnection(
{
host: 'localhost',
port: 3307,
user: 'user',
password: '1234',
database: 'Tennis'
}
);
// 밖에서 이용 가능하도록 export
module.exports = conn;
// index.js
// mariadb 모듈 소환
let mariadb = require('./database/connect/mariadb');
// mariadb 연결
mariadb.connect();
<이슈>
- 문제 : Error: MySQL is requesting the auth_gssapi_client authentication method, which is not supported.
이런 에러가 나서 찾아보니 MariaDB의 인증 방식과 Node.js의 mysql 모듈 간의 호환성 문제라고 한다.
MariaDB가 auth_gssapi_client 인증 방식을 요구하고 있는데, 사용하고 있는 mysql 모듈이 이를 지원하지 않는다고 한다. - 해결시도
1) 그래서 mysql 모듈 대신 mariadb나 mysql2 전용모듈을 써봤으나 되지 않았다.
2) mariadb 사용자 인증 방식을 변경해보기도 했다.
3) mriadb 버전을 낮춰보기도 하다가 mysql8.0 버전을 적용하기도 했다. - 해결 :
- 결국 문제는 포트번호 3306을 다른 곳에서 쓰고 있어서 인것으로 판단된다. db의 포트번호를 3307로 바꿔주니 문제없이 db를 가져왔다
메인페이지 연동
main.html 뿌려주기
1. handle 변수에 '경로 : main 함수' 넣어주기
let handle = {}; // key:value 쌍으로 이루어진 변수(사전)
handle['/'] = main;
2. Node.js의 'fs'모듈을 활용해서 파일 I/O작업을 구현
- fs모듈의 readFileSync 함수를 통해 파일을 불러와서 출력할 수 있는데 해당 모듈과 함수에 대해서는 하단 링크를 참고한다.
2025.09.10 - [개발지식/Node.js] - [Node.js] fs모듈 - readFile(), readFileSync()
[Node.js] fs모듈 - readFile(), readFileSync()
fs 모듈이란?FileSystem의 약자로 fs모듈은 Node.js에서 파일 입출력 처리를 할 때 사용한다.fs 모듈을 사용하기 위해서 require 함수를 활용하거나, ES모듈 사용 시 import/export 방식으로 불러올 수 있다.다
thinktank911.tistory.com
- main 함수의 response.write(body)에서 해당 파일을 뿌려준다.
// requestHandler.js
// FileSync의 약자로 만든 html 가져올 수 있다
const fs = require('fs');
const main_view = fs.readFileSync('./main.html','utf-8');
const orderlist_view = fs.readFileSync('./orderlist.html','utf-8');
// router가 루트를 정해주면 각 루트 페이지에서 하는 기능 설정
function main(response){
// head 적기 : 상태코드, 요청타입
response.writeHead(200, {'Content-Type' : 'text/html'});
// write body. body 적기
response.write(main_view);
// response 종료. 전송바람
response.end();
}
3. main 함수에서 mariadb 모듈 활용해 쿼리를 던진다.
- 이 데이터를 따로 사용하진 않고 콘솔에 출력만 해봤다.
- 추후 프로젝트에서는 이 데이터를 사용해서 ui를 그려줄 예정이다.
// mariadb 모듈 소환
let mariadb = require('./database/connect/mariadb');
// router가 루트를 정해주면 각 루트 페이지에서 하는 기능 설정
function main(response){
console.log('main');
// query 던지기
mariadb.query("SELECT * FROM product", function(err, rows){
console.log(rows);
})
}
5. 이미지 경로도 함수로 빼서 뿌려주기
- 이미지도 경로여서 서버 start할 때 route로 보내주고 handle 변수에 경로를 넣어 함수를 실행시킨다.
🍕readFile()
fs.readFile()메서드는 파일을 읽는데 사용되는 내장 메서드이다. 해당 메서드는 전체 파일을 버퍼로 읽습니다.
fs.readFile(filename,encoding,callback)
- filename: 읽을 파일의 이름이나 다른 위치에 저장된 경우, 전체 경로
- encoding: 파일의 인코딩을 보유. 기본값은 'utf8'이다.
- callback: 파일을 읽은 후 호출되는 콜백함수
- err: 작업에 실패하면 반환되는 오류 (callback함수 첫 번째 인자)
- data: (성공시) 파일의 내용 (callback함수 두 번째 인자)
/* img directory */
handle['/img/redRacket.png'] = redRacket;
handle['/img/blueRacket.png'] = blueRacket;
handle['/img/blackRacket.png'] = blackRacket;
// 서버에 있는 그림도 직접 던져줘야 함
function redRacket(response){
fs.readFile('./img/redRacket.png', function(err, data){
// head 적기 : 상태코드, 요청타입
response.writeHead(200, {'Content-Type' : 'text/html'});
// write body. body 적기
response.write(data);
// response 종료. 전송바람
response.end();
})
}
function blueRacket(response){
fs.readFile('./img/blueRacket.png', function(err, data){
// head 적기 : 상태코드, 요청타입
response.writeHead(200, {'Content-Type' : 'text/html'});
// write body. body 적기
response.write(data);
// response 종료. 전송바람
response.end();
})
}
function blackRacket(response){
fs.readFile('./img/blackRacket.png', function(err, data){
// head 적기 : 상태코드, 요청타입
response.writeHead(200, {'Content-Type' : 'text/html'});
// write body. body 적기
response.write(data);
// response 종료. 전송바람
response.end();
})
}
주문하기
- order 함수 정의
- INSERT 쿼리 던지기
- productId 파라메터 생성
- handle에 넣기
- 오늘 날짜 받아오는 Javascript 함수 : new Date().toLocaleDateString()
function order(response, productId){
// head 적기 : 상태코드, 요청타입
response.writeHead(200, {'Content-Type' : 'text/html'});
// 인서트 쿼리 던지기
mariadb.query("INSERT INTO orderlist VALUES(" + productId + ", '"+ new Date().toLocaleDateString() + "');",
function(err, rows){
console.log(rows);
})
// write body. body 적기
response.write('order page');
// response 종료. 전송바람
response.end();
}
- 주문한 항목id 가져오기
- server.js에서 쿼리데이터 가져와서 route에 넘겨준다
- route.js에서 handle 함수 파라메터로 넘겨준다.
// 최신 버전에서는 new URL() 방식을 쓰는 게 더 안정적이고 표준적임
let pathname = url.parse(request.url).pathname;
// 쿼리 데이터 가져오기
let queryData = url.parse(request.url, true).query;
// route에게 pathname, handle, response, productId 전달
route(pathname, handle, response, queryData.productId);
if(typeof handle[pathname] === 'function'){
handle[pathname](response, productId);
} else{
- main.html에서 order버튼 클릭 시 경로를 쿼리스트링(/order?productId=1) 붙여서 넘겨준다.
<input class="card_button" type="button" value="order"
onclick="location.href='/order?productId=1'">
프로젝트 마무리(+주문 내역 페이지 연동)
- orderlist 함수 정의
- 조회 쿼리 던지기
- orderlist_view html 파일 화면 출력
- 데이터 row를 받아와 반복문 돌려서 테이블에 뿌려주기
function orderlist(response){
// head 적기 : 상태코드, 요청타입
response.writeHead(200, {'Content-Type' : 'text/html'});
mariadb.query("SELECT * FROM orderlist", function(err, rows){
// write body. body 적기
// orderlist_view에 <table>있음
response.write(orderlist_view);
rows.forEach(element => {
response.write("<tr>"
+ "<td>"+element.product_id+"</td>"
+ "<td>"+element.order_date+"</td>"
+ "</tr>");
});
response.write("</table>");
// response 종료. 전송바람
response.end();
})
}
handle['/orderlist'] = orderlist;
- main.html에서 orderlist 경로 변경
- 원래는 orderlist.html 파일 경로를 하드코딩했는데 handle 변수에 넣어준 orderlist 경로를 호출한다.
<a href="/orderlist">order list</a>