-
[Node & MariaDB] 개발자 지망생 스터티 - 17일차스터디/KAKAOCLOUDSCHOOL 2022. 11. 24. 17:52
Node + MariaDB
1. 프로그래밍 언어에서 관계형 데이터베이스를 사용하는 방법
- 데이터베이스 제조업체에서 제공하는 드라이버를 사용하거나 SQL Mapper Framework를 사용하는 방식 - SQL을 이용
- 사용하기가 쉬워서 SI 같은 많은 인력이 진행하는 프로젝트에서 사용 (MyBatis 등..)
- ORM Framework 이용 : SQL을 이용하지 않고 객체 지향 언어의 메서드를 이용해서 SQL을 자동 변환해서 수행하는 방식
- 성능이 우수하기 때문에 적은 인력을 가지고 만드는 솔루션 분야에서 많이 이용
데이터베이스 제공업체에서 제공하는 드라이버를 이용해서 SQL을 실행
- Node에서 MariaDB는 MySQL과 같은 데이터베이스로 취급함
1. MariaDB 연동
1) 필요한 모듈
- mysql
2) 필요한 정보
- 연결할 데이터베이스를 소유하고 있는 컴퓨터의 IP나 도메인과 포트번호
- localhost(127.0.0.1, ::1):3306
- 127.0.0.1 = 루프백이라고 함, localhost에 연결되지 않을 때 사용해볼 수 있음(나가서 다시 들어온다는 개념)
- 3306 = MariaDB 디폴트 포트번호
- localhost(127.0.0.1, ::1):3306
- 사용할 데이터베이스 이름 (sid라고 하기도 함) : "itoriginal"
- 계정 : 아이디와 비밀번호 : "root : qlxkals"
2. 연결
1) 접속 연결 방법
// 모듈 가져오기 const mariadb = require('mysql'); // 접속 정보 가져오기 let connection = mariadb.createConnection({ host:'아이피 나 도메인', port : 포트번호, user : '아이디', password : '비밀번호', database : '데이터베이스 이름' }); // 연결 connection.connect(function(error){ if(error){ // 에러가 발생했을 때 수행할 내용 } }); // 데이터베이스 연결이 되었을 때 수행할 내용
2) 연결 확인
- Node 프로젝트 생성
- mysql 패키지 설치 : npm install mysql
- App.js 파일을 생성 후 데이터베이스 접속 코드를 작성
// 모듈 가져오기 const mysql = require('mysql'); // 접속 정보 생성 let connection = mysql.createConnection({ host:'127.0.0.1', port : 3306, user : 'root', password : 'qlxkals', database : 'itoriginal' }); // 연결 connection.connect((error) => { if(error){ console.log(error); // 에러가 발생했을 때 수행할 내용 }else{ console.log(connection); } });
- 실행 : node App.js
3. SQL 실행
1) SELECT 가 아닌 구문
- 결과가 성공과 실패 또는 영향 받은 개수의 형태
- 연결객체.query(SQL, [파라미터배열]);
- 파라미터 배열은 SQL을 작성할 때 값의 자리에 직접 값을 작성하지 않고 '?' 로 설정한 후 나중에 값을 대입할 수 있음
더보기insert into Table(num, name) values(1, '강감찬')
node에서 SQL을 사용할 때는 문자열로 생성해야 함
"insert into Table(num, name) values(1, '강감찬')"
하지만 계속해서 ""를 사용하는 것은 너무 비효율적이므로 아래와 같이 작성을 함
"insert into Table(num, name) values(?, ?)", [1, '강감찬']
프로그래밍 언어에서 SQL을 작성할 때는 ';' 을 하지 않음
- App.js
// 모듈 가져오기 const mysql = require('mysql'); // 접속 정보 생성 let connection = mysql.createConnection({ host:'127.0.0.1', port : 3306, user : 'root', password : 'qlxkals', database : 'itoriginal' }); // 연결 connection.connect((error) => { if(error){ console.log(error); // 에러가 발생했을 때 수행할 내용 }else{ // console.log(connection); // 테이블 생성 구문 connection.query( 'create table company(id int auto_increment primary key, name varchar(20))'); // 데이터 삽입 구문 connection.query('insert into company(name) values(?)', '네이버'); connection.query('insert into company(name) values(?)', '카카오'); connection.query('insert into company(name) values(?)', '쿠팡'); } });
- 데이터베이스 접속도구 (DBeaver) 에서 확인
SELECT * FROM company;
테이블 생성 결과 확인 2) SELECT 구문
- 조회한 결과(Cursor 또는 하나의 객체 나 배열)
- SELECT 구문은 콜백 함수를 매개변수로 추가하는데 콜백함수의 매개변수가 3개인데 첫번째 에러 객체이고 두번째 검색된 내용인데 javascript 객체 형태로 제공되고 세번째 meta data 로 검색된 결과에 대한 정보임
화면에 출력할 거라면 javascript 객체를 그대로 이용하면 되고 데이터 형태로 제공하고자 하면 JSON 문자열로 변환하면 됨
- SELECT 구문은 콜백 함수를 매개변수로 추가하는데 콜백함수의 매개변수가 3개인데 첫번째 에러 객체이고 두번째 검색된 내용인데 javascript 객체 형태로 제공되고 세번째 meta data 로 검색된 결과에 대한 정보임
- App.js 파일에서 접속에 성공했을 때 부분에 데이터를 읽어오는 코드를 작성
// 연결 connection.connect((error) => { if(error){ console.log(error); // 에러가 발생했을 때 수행할 내용 }else{ // SELECT 구문 connection.query("select * from company", (err, results, fields) => { if(err){ console.log(err); console.log("{result:false}"); }else{ let result = JSON.stringify(results); console.log(result); } }); } });
SELECT 구문 실행 결과 확인 테이블 1개를 SQL을 이용해서 연동 (실습)
0. 기능
- 테이블의 데이터 전체를 가져오기
- 테이블의 데이터 일부분을 가져오기 (페이지 단위로 가져오기) : 더보기
- 데이터 1개를 가져오기
- 데이터 삽입, 삭제, 갱신
- 파일 업로드 와 다운로드
- 가장 최근에 데이터를 수정한 시간을 기록하고 조회
더보기<서버와 클라이언트 사이의 데이터 교환>
- 접속할 때 마다 서버의 데이터를 가져와서 출력
- 서버의 데이터를 클라이언트에 저장하고 접속할 때 마다 서버의 데이터와 클라이언트의 데이터를 비교해서 수정이 발생했을 때만 업데이트 - 서버의 데이터와 클라이언트의 데이터를 비교할 수 있어야 하는데 가장 쉬운 방법은 양쪽에서 수정한 시간을 기록한 후 클라이언트의 수정 시간이 서버의 데이터 수정 시간 보다 이전이면 업데이트를 수행
1. 샘플 데이터 생성
-- 테이블이 존재하는 경우만 수행 DROP TABLE goods; -- 테이블 생성 CREATE TABLE goods( itemid int, itemname VARCHAR(100), price int, description VARCHAR(200), pictureurl VARCHAR(100), updatedate varchar(20), PRIMARY KEY (itemid) )engine=InnoDB DEFAULT CHARSET=utf8; -- 샘플 데이터 입력 insert into goods values(1, '레몬', 500,'비타민 C가 풍부한 쓴귤', 'lemon.jpg', '2020-08-01'); insert into goods values(2, '오렌지', 1500, '비타민 C가 풍부한 당귤', 'orange.jpg', '2020-08-01'); insert into goods values(3, '키위', 2000, '비타민 C가 풍부한 다래', 'kiwi.jpg', '2020-08-01'); insert into goods values(4, '포도', 1000, '항상화 성분과 당분이 높고 무기물이 많은 과일', 'grape.jpg', '2020-08-01'); insert into goods values(5, '딸기', 2000, '수분함량이 높은 과일', 'strawberry.jpg', '2020-08-01'); insert into goods values(6, '무화과', 300, '칼슘, 섬유질 및 항산화 물질을 많이 함유된 식물', 'fig.jpg', '2020-08-01'); insert into goods values(7, '레몬', 500,'비타민 C가 풍부한 쓴귤', 'lemon.jpg', '2020-08-01'); insert into goods values(8, '오렌지', 1500, '비타민 C가 풍부한 당귤', 'orange.jpg', '2020-08-01'); insert into goods values(9, '키위', 2000, '비타민 C가 풍부한 다래', 'kiwi.jpg', '2020-08-01'); insert into goods values(10, '포도', 1000, '항상화 성분과 당분이 높고 무기물이 많은 과일', 'grape.jpg', '2020-08-01'); insert into goods values(11, '딸기', 2000, '수분함량이 높은 과일', 'strawberry.jpg', '2020-08-01'); insert into goods values(12, '무화과', 300, '칼슘, 섬유질 및 항산화 물질을 많이 함유된 식물', 'fig.jpg', '2020-08-01'); insert into goods values(13, '레몬', 500,'비타민 C가 풍부한 쓴귤', 'lemon.jpg', '2020-08-01'); insert into goods values(14, '오렌지', 1500, '비타민 C가 풍부한 당귤', 'orange.jpg', '2020-08-01'); insert into goods values(15, '키위', 2000, '비타민 C가 풍부한 다래', 'kiwi.jpg', '2020-08-01'); insert into goods values(16, '포도', 1000, '항상화 성분과 당분이 높고 무기물이 많은 과일', 'grape.jpg', '2020-08-01'); insert into goods values(17, '딸기', 2000, '수분함량이 높은 과일', 'strawberry.jpg', '2020-08-01'); insert into goods values(18, '무화과', 300, '칼슘, 섬유질 및 항산화 물질을 많이 함유된 식물', 'fig.jpg', '2020-08-01'); insert into goods values(19, '딸기', 2000, '수분함량이 높은 과일', 'strawberry.jpg', '2020-08-01'); insert into goods values(20, '무화과', 300, '칼슘, 섬유질 및 항산화 물질을 많이 함유된 식물', 'fig.jpg', '2020-08-01'); insert into goods values(21, '레몬', 500,'비타민 C가 풍부한 쓴귤', 'lemon.jpg', '2020-08-01'); insert into goods values(22, '오렌지', 1500, '비타민 C가 풍부한 당귤', 'orange.jpg', '2020-08-01'); insert into goods values(23, '키위', 2000, '비타민 C가 풍부한 다래', 'kiwi.jpg', '2020-08-01'); insert into goods values(24, '포도', 1000, '항상화 성분과 당분이 높고 무기물이 많은 과일', 'grape.jpg', '2020-08-01'); insert into goods values(25, '딸기', 2000, '수분함량이 높은 과일', 'strawberry.jpg', '2020-08-01'); insert into goods values(26, '무화과', 300, '칼슘, 섬유질 및 항산화 물질을 많이 함유된 식물', 'fig.jpg', '2020-08-01'); insert into goods values(27, '레몬', 500,'비타민 C가 풍부한 쓴귤', 'lemon.jpg', '2020-08-01'); insert into goods values(28, '오렌지', 1500, '비타민 C가 풍부한 당귤', 'orange.jpg', '2020-08-01'); insert into goods values(29, '키위', 2000, '비타민 C가 풍부한 다래', 'kiwi.jpg', '2020-08-01'); insert into goods values(30, '포도', 1000, '항상화 성분과 당분이 높고 무기물이 많은 과일', 'grape.jpg', '2020-08-01'); commit; select * from goods;
샘플데이터 테이블 결과 확인 2. 기본 설치(필요한 모듈 설치)
1) 프로젝트에 설치
- npm install express morgan multer mysql cookie-parser express-session express-mysql-session dotenv compression file-stream-rotator
더보기express : 웹 서버 모듈
morgan : 로그 기록을 위한 모듈
file-stream-rotator : 로그를 파일에 기록하기 위한 모듈
multer : 파일 업로드 처리를 위한 모듈
mysql : mysql이나 mariadb를 사용하기 위한 모듈
cookie-parse : 쿠키를 사용하기 위한 모듈
express-session : 세션을 사용하기 위한 모듈
express-mysql-session : 세션을 mysql이나 mariadb에 저장하기 위한 모듈
dotenv : .env 파일의 내용을 process.env로 저장해서 사용하기 위한 모듈
compression : 서버가 처리한 결과를 압축해서 클라이언트에게 전송하기 위한 모듈 (트래픽을 줄이기 위한 모듈)
2) 개발 모드로 설치
-
npm install --save-dev nodemon
더보기nodemon : 소스 코드를 수정하면 자동으로 재시작할 수 있도록 해주는 모듈
- package.json 파일의 Script 부분에 "start":"nodemon app", 을 추가 : npm start 명령을 사용하면 nodemon app 이라는 명령이 수행되서 entry point 로 설정한 파일을 실행시켜주는 역할을 함
3) 서버의 데이터 업데이트 시간을 기록한 update.txt 파일을 생성
4) 프로젝트에 .env 파일을 생성하고 필요한 속성을 정의
- app.js 파일에 하드코딩한 접속 정보를 작성
- .env
<.env File> PORT = 9000 COOKIE_SECRET = item HOST = '127.0.0.1' MYSQLPORT = 3306 USERNAME = 'root' PASSWORD = 'qlxkals' DATABASE = 'itoriginal'
5) App.js 파일에 기본 설정 코드를 작성
const express = require('express'); const morgan = require('morgan'); const compression = require('compression'); const path = require('path'); const mysql = require('mysql'); const cookieParser = require('cookie-parser'); const session = require('express-session'); const multer = require('multer'); const dotenv = require('dotenv'); //설정 파일의 내용 가져오기 dotenv.config(); //서버 설정 const app = express(); app.set('port', process.env.PORT || 9000); //로그를 매일 기록하기 위한 설정 let FileStreamRotator = require('file-stream-rotator'); let fs = require('fs'); //로그를 기록할 디렉토리 경로 생성 let logDirectory = path.join(__dirname, 'log'); //디렉토리가 없으면 생성 fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory); //로그 파일 옵션을 설정 let accessLogStream = FileStreamRotator.getStream({ date_format:'YYYYMMDD', filename:path.join(logDirectory, 'access-%DATE%.log'), frequency:'daliy', verbose:false }); //로그 기록 설정 app.use(morgan('combined', {stream:accessLogStream})); //압축해서 전송하는 옵션 설정 app.use(compression()); //POST 방식의 파라미터 읽을 수 있도록 설정 let bodyParser = require('body-parser'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended:true })); //세션을 데이터베이스에 저장하는 작업 //데이터베이스 접속 정보 let options = { host:'127.0.0.1', port:3306, user:'root', password:'wnddkd', database:'adam' }; //세션을 저장하기 위한 MySQL 데이터베이스 저장소 생성 const MariaDBStore = require('express-mysql-session')(session); //세션 설정 app.use(session({ secret:process.env.COOKIE_SECRET, resave:false, saveUninitialized:true, store:new MariaDBStore(options) })); //파일 업로드 설정 const upload = multer({ storage:multer.diskStorage({ destination(req, file, done){ done(null, 'public/img'); }, filename(req, file, done){ const ext = path.extname(file.originalname); done(null, path.basename(file.originalname, ext) + Date.now() + ext); } }), limits:{fileSize: 10*1024*1024} }); //정적 파일의 경로를 설정 app.use('/', express.static('public')); //파일 다운로드를 위한 모듈 let util = require('util'); let mime = require('mime'); //데이터베이스 연결 let connection = mysql.createConnection(options); connection.connect((error) => { if(error){ console.log(error); throw error; } }) //에러 발생시 처리 app.use((err, req, res, next)=>{ console.log(err); res.status(500).send(err.message); }); //서버 구동 app.listen(app.get('port'), () => { console.log(app.get('port'), '번 포트에서 대기 중'); });
더보기정적파일
- 웹에서는 소스 코드를 제외하고 서버의 데이터를 출력하는 용도가 아닌 파일
- html, css, js, 이미지, 사운드, 동영상
6) 서버 구동해서 테스트
- npm start 로 서버 구동 (기본 설정 이후)
더보기git에서 받아온 후 실행
https://github.com/itggangpae/node_mysql.git
- 프로젝트 다운로드
- 프롬프트를 적당한 디렉토리로 이동 : git clone https://github.com/itggangpae/node_mysql.git
- 프로젝트 초기화
- cd node_mysql
- npm istall
- 실행 (현재 우리는 각자 데이터베이스를 이용하기 때문에 데이터베이스 접속 정보는 수정해야 함)
- npm start
7) 프로젝트에 public 디렉토리 생성
8) 샘플 데이터의 이미지를 다운로드 받아서 public 디렉토리에 img 디렉토리로 복사
https://ggangpae1.tistory.com/377
3. 메인 화면 출력 - 서버가 데이터만 리턴하고자 하는 경우에는 이 설정은 필요없음
1) public 디렉토리에 메인 화면으로 사용할 html을 생성하고 작성
- index.html
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Node - MariaDB</title> <link rel="stylesheet" href="/css/common.css"/> </head> <body> <h1>Maria DB</h1> </body> </html>
2) public 디렉토리에 css 디렉토리를 생생하고 common.css 생성하고 작성
h1{ color:red; } a{ text-decoration: none; }
3) App.js 파일에 기본 요청을 처리하는 코드를 추가
... // 기본 요청을 처리 app.get('/', (req, res)=>{ res.sendFile(path.join(__dirname,'index.html')); }); ...
4) 서버를 구동하고 브라우저에 localhost:9000 을 입력해서 확인
- 동일한 와이파이 연결 후 IP주소:PORT 번호 입력시 다른 디바이스로도 확인 가능
5) 데이터베이스에서 아래 SQL을 입력하고 확인
SELECT * FROM sessions;
6) 프로젝트 내의 log 디렉토리 안의 로그 파일 확인
4. 데이터 전체 가져오기
1) URL
- item/all
2) App.js 파일에 추가
... // 데이터 전체 가져오기 처리 app.get('/item/all', (req, res) => { // HTML 출력 : res.sendFile (파일경로) - 서버에 데이터 출력을 못함 (화면만 만들어줄때 쓰는 방식) // 템플릿 엔진 : res.render(파일 경로, 데이터) // 템플릿 엔진에 넘겨주는 데이터는 프로그래밍 언어의 데이터 // JSON 출력 : res.json (데이터) // JSON 문자열의 형태로 데이터를 제공 // Frond End에서 데이터를 수신해서 출력 // 2개 이상의 데이터를 조회할 때는 정렬은 필수 connection.query("select * from goods order by itemid desc", (err, results, fields) => { if(err){ // 에러가 발생한 경우 // 에러가 발생하여도 데이터를 전송하지 않으면 안됨 res.json({'result':false}) }else{ // 정상 응답을 한 경우 res.json({'result':true, 'list':results}) } }); }) ...
3) 서버를 실행하고 브라우저에 localhost:9000/item/all을 입력하고 데이터가 출력되는지 확인
4) index.html 파일에 전체 데이터 가져오기를 위한 코드를 추가
- body 태그에 이벤트를 위한 DOM을 작성
<a href="#" id="getdata">전체 데이터 가져오기</a><br/> <!-- 데이터 출력 영역 --> <div id="content"></div>
- DOM을 클릭했을 때 수행할 스크립트를 작성
window.addEventListener('load', (e) => { //DOM 찾아오기 let allbtn = document.getElementById("allbtn"); //데이터 출력을 위한 영역 let content = document.getElementById("content"); //allbtn 클릭 이벤트 처리 allbtn.addEventListener('click', (e) => { //ajax로 데이터 가져오기 let request = new XMLHttpRequest(); //요청 생성 request.open('GET', '/item/all'); //요청 request.send(''); //데이터가 전송된 경우 처리 request.addEventListener('load', ()=>{ //출력을 하기 위해서는 JSON 문자열을 자바스크립트 객체로 변환 let data = JSON.parse(request.responseText); //데이터 가져오기에 성공한 경우 if(data.result == true){ content.innerHTML = "<div align='center' class='body'>"; content.innerHTML += "<h2>상품 목록</h2>"; content.innerHTML += "<table border='1'>"; content.innerHTML += "<tr class='header'>"; content.innerHTML += "<th align='center' width='80'>ID</th>"; content.innerHTML += "<th align='center' width='320'>이름</th>"; content.innerHTML += "<th align='center' width='100'>가격</th>"; content.innerHTML += "</tr>"; //list 키에 있는 데이터를 가져오기 let ar = data.list; //배열의 데이터 순회 for(let item of ar){ content.innerHTML += "<tr class='record'>"; content.innerHTML += "<td align='center'>" + item.itemid + "</td>"; content.innerHTML += "<td align='left'>" + item.itemname + "</td>"; content.innerHTML += "<td align='right'>" + item.price + "원</td>"; content.innerHTML += "</tr>"; } content.innerHTML += "</table>"; content.innerHTML += "</div>"; }else{ content.innerHTML = "데이터 가져오기 실패"; } }); }); });
5. 데이터 일부분 가져오기
- 데이터의 일부분 가져오기를 처리할 때는 일반적으로 2개의 데이터가 필요
- 하나는 페이지 번호를 시작하는 데이터의 번호 : 시작하는 데이터 번호를 주는 경우는 데이터의 개수가 고정적인 경우가 많음
- 다른 하나는 한 페이지에 출력할 데이터 개수
- URL 확인
- 한겨레 신문사의 경우는 page 에 페이지 번호를 전달
- https://search.hani.co.kr/search/newslist?searchword=%EC%9D%B4%ED%83%9C%EC%9B%90&startdate=1988.01.01&enddate=2022.11.24&page=2&sort=desc
- https://search.hani.co.kr/search/newslist?searchword=%EC%9D%B4%ED%83%9C%EC%9B%90&startdate=1988.01.01&enddate=2022.11.24&page=3&sort=desc
- 동아일보는 p 가 데이터의 시작 번호
- https://www.donga.com/news/search?p=16&query=%EC%9D%B4%ED%83%9C%EC%9B%90&check_news=91&more=1&sorting=1&search_date=1&v1=&v2=
- https://www.donga.com/news/search?p=31&query=%EC%9D%B4%ED%83%9C%EC%9B%90&check_news=91&more=1&sorting=1&search_date=1&v1=&v2=
- 한겨레 신문사의 경우는 page 에 페이지 번호를 전달
- 페이지 번호를 전달하는 것으로 결정하고 데이터의 개수는 5개로 고정
- 페이지 번호를 전달하지 않으면 1PAGE의 데이터를 출력하고 그 이외의 경우는 페이지 번호에 해당하는 데이터를 전달
- get 방식에서의 서버로의 데이터 전달
- 전달하고자 하는 데이터가 1개인 경우
- 파라미터로 전달 : URL?이름=값
- URL에 전달 : URL/값 - 최근 선호하는 방식임
- 전달하고자 하는 데이터가 2개 이상인 경우는 파라미터 형태로 전달
- URL?이름=값&이름=값...
- 전달하고자 하는 데이터가 1개인 경우
- 파라미터 읽기 : req.query.파라미터이름
- URL을 결정 - /item/list
- 구현할 때 더 이상 가져올 데이터가 없는 경우를 위해서 데이터의 전체 개수를 같이 리턴해주어야 함
더보기클라이언트 <->서버 프로그램
- 클라이언트가 요청과 데이터를 서버에게 전송하면 서버는 데이터를 읽어서 작업을 수행하고 그 결과를 전송하는 방식
- 서버는 클라이언트의 데이터를 정확하게 읽어내야 함
테스트를 할 때는 클라이언트가 데이터를 전송한 경우와 그렇지 않은 경우를 나누어서 확인해야 함 - 웹 프로그래밍에서는 요청 방식이 get과 post 2가지가 기본 형식이므로 get 방식 일 때와 post 방식일 때 모두 읽어 낼 수 있어야 함.
MySQL이나 MariaDB에서 데이터의 일부분을 가져오기 (TOP-N)
- SELECT 구문의 마지막에 limit(시작하는 번호 또는 건너뛸 데이터 개수, 데이터 개수)를 추가 해주면 됨
데이터 개수 : 10
1 page 시작 번호 : 0
2 page 시작 번호 : 10
3 page 시작 번호 : 20
4 page 시작 번호 : 30
건너뛸 데이터 개수 = (페이지번호 -1) * 데이터 개수
- 페이지 번호가 전체 데이터 개수보다 커지는 상황이 발생하면 데이터를 하나도 못찾아 옴
클라이언트가
'스터디 > KAKAOCLOUDSCHOOL' 카테고리의 다른 글
[Node + JavaScript] 개발자 지망생 스터디 - 19일차 (0) 2022.11.28 [Node & MariaDB] 개발자 지망생 스터디 - 18일차 (0) 2022.11.25 [DB-MariaDB] 개발자 지망생 스터디 - 16일차 (0) 2022.11.23 [DB-MariaDB] 개발자 지망생 스터디 - 15일차 (0) 2022.11.22 [DB-MariaDB] 개발자 지망생 스터디 - 14일차 (0) 2022.11.21 - 데이터베이스 제조업체에서 제공하는 드라이버를 사용하거나 SQL Mapper Framework를 사용하는 방식 - SQL을 이용