데브코스 웹풀스택 과정/TIL

[8기 9회차] 0909 웹 서비스의 이해 - Docker, mariaDB, Node.js에 DB연동

thinktank911 2025. 9. 9. 21:19

데이터베이스

데이터베이스란?

  • 데이터를 통합하여 효율적으로 관리하기 위한 데이터 집합체를 데이터베이스(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 설치

  1. 마리아DB 가져오기
    • docker pull mariadb
  2. 마리아db 설정
    • docker run --name mariadb -d -p 3306:3306 --restart=always -e MYSQL_ROOT_PASSWORD=root mariadb
  3. mariadb가 있는 컨테이너 접속
    • docker exec -it mariadb /bin/bash
  4. mariadb 실행
    • mysql -u root -p
  5. 패스워드 입력
    • 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 실행

  1. docker desktop 프로그램 실행
  2. cmd 실행
  3. mariadb가 있는 컨테이너 접속 : docker exec -it mariadb /bin/bash
  4. 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>