npm init
npm i express
npm i -g nodemon (이미 깔려있다면 생략)
const express = require("express");
const app = express();
app.listen(8080, function () {
console.log("server ready...");
});
nodemon server
bootstrap template download 후 zip 파일을 풀고 작업 폴더 내의 public 폴더를 생성해 모든 리소스를 카피
(https://startbootstrap.com/template/sb-admin)

const express = require("express");
const app = express();
app.listen(8080, function () {
console.log("server ready...");
});
**app.get("/", function (req, res) {
res.sendFile(__dirname + "/public/index.html");
});**
이유는 express내의 라우터가 모든 요청을 인터셉트하기 때문에 static요소들에 직접적인 접근이 불가능하기 때문

express 모듈의 static 메소드를 사용해 static resource를 직접적으로 찾도록 설정, 라우팅 코드가 필요 없기 때문에 주석 처리
const express = require("express");
const app = express();
**app.use(express.static("public"));**
app.listen(8080, function () {
console.log("server ready...");
});
// app.get("/", function (req, res) {
// res.sendFile(__dirname + "/public/index.html");
// });

npm i node-mysql
Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
MySQL 서버가 caching_sha2_password 인증 플러그인을 사용하는 경우 발생
MySQL 클라이언트 라이브러리 업그레이드를 통해 해결
npm install mysql2
**const mysql = require("mysql2");
const conn = mysql.createConnection({
host: "localhost",
user: "test",
password: "a12341234!",
database: "myboard",
});
conn.connect();**
const express = require("express");
const app = express();
app.use(express.static("public"));
app.listen(8080, function () {
console.log("server ready...");
});
**app.get("/list", function (req, res) {
conn.query("select * from post", function (err, rows, fields) {
if (err) throw err;
console.log(rows);
res.send(rows);
});
});**

conn.query 함수는 세 가지 주요 매개변수를 가짐
1. err
err는 쿼리를 실행하는 동안 발생한 오류를 나타내는 매개변수
쿼리 실행 중 오류가 발생하면 err 객체에 오류 정보가 담김
if (err) throw err; 구문을 통해 오류가 발생했을 때 예외를 던져 서버가 적절히 오류를 처리하도록 함
일반적으로 오류 객체에는 오류 메시지, 오류 코드, 스택 트레이스 등의 정보가 포함
2. rows
rows는 쿼리 결과로 반환된 행(row)들을 담고 있는 배열
select * from post 쿼리의 결과로 반환된 데이터베이스 테이블의 각 행이 이 배열의 요소로 포함됨
각 요소는 데이터베이스 테이블의 하나의 행을 나타내며, 객체 형태로 필드 이름과 값을 포함
예를 들어, rows 배열의 첫 번째 요소는 {id: 1, title: 'First Post', content: 'This is the first post.'} 와 같은 형태일 수 있음
3. fields
fields는 쿼리 결과의 각 필드(column)에 대한 메타데이터를 담고 있는 배열
각 필드는 데이터베이스 테이블의 컬럼에 해당하며, 이름, 데이터 유형, 기본값 등의 정보를 포함
이 배열은 일반적으로 쿼리 결과의 구조를 이해하거나 결과를 가공할 때 사용될 수 있음
예를 들어, fields 배열의 첫 번째 요소는 {name: 'id', type: 'INT', ...} 와 같은 형태일 수 있음
지금까지의 순서대로 수행했다면 브라우저 화면에 출력되지 않고 서버 콘솔에 출력될 것
현재는 동기 방식의 요청 처리를 하고 있기 때문에 처리 결과를 주면 이전 화면이 유지되지 않는 문제를 해결하기 위해 XHR 객체 사용
index.html
index.html의 적당한 위치에 다음 두 라인을 추가
```
...
<button id="testBtn">test</button>
...
<script src="js/my.js></script>
```
my.js
testBtn.addEventListener("click", function () {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
// Typical action to be performed when the document is ready:
document.getElementById("demo").innerHTML = xhttp.responseText;
}
};
xhttp.open("GET", "/list", true);
xhttp.send();
});
testBtn.addEventListener("click", async function () {
let resObj = await fetch("/list"); // "/list" 경로로 GET 요청을 보내고 응답을 기다림
let data = await resObj.json(); // 응답 본문을 JSON 형식으로 파싱
let displayData = // 데이터를 HTML 테이블로 변환하여 표시
`<thead>
<td>ID</td>
<td>Title</td>
<td>writer</td>
<td>created</td>
</thead>
<tbody>`;
data.forEach((item,index) => {
displayData +=
`<tr>
<td>${item.id}</td>
<td>${item.title}</td>
<td>${item.profile_id}</td>
<td>${item.created}</td>
</tr>`
});
displayData += `</tbody>`;
document.getElementById("datatablesSimple").innerHTML = displayData;
});
fetch 함수fetch 함수는 네트워크 요청을 생성하고, 원격 서버와의 데이터를 교환하는 데 사용1. 요청 생성
fetch("/list")는 현재 도메인에서 "/list" 경로로 HTTP GET 요청을 생성2. 서버 처리
"/list" 경로에 대한 요청을 처리, 서버 코드에서 "/list"경로에 대한 핸들러가 정의되어 있어야 함app.get("/list", function (req, res) {
conn.query("select * from post", function (err, rows, fields) {
if (err) throw err;
res.send(rows);
});
});
이 핸들러는 데이터베이스에서 post 테이블의 모든 데이터를 조회하고, 이를 클라이언트로 응답으로 보냄
3. 응답 처리
클라이언트 측에서는 fetch("/list") 호출이 완료되면 Promise가 반환
await 키워드를 사용하여 이 Promise가 해결될 때까지 기다릴 수 있음
응답이 성공적으로 도착하면 Response 객체가 반환, 이를 통해 응답 본문을 다양한 형식으로 처리할 수 있음(JSON, 텍스트, ...)
1. 서버 측에서는 "/list" 경로로 들어오는 GET 요청을 처리하여 데이터베이스의 post 테이블에서 모든 데이터를 가져와 응답으로 보냄
2. 클라이언트 측에서는 페이지 로드 시 fetch 함수를 사용하여 서버로부터 데이터를 비동기적으로 가져옴
3. 데이터를 JSON 형식으로 파싱하고, 이를 HTML 테이블 형식으로 변환
4. 변환된 HTML 문자열을 data-table 아이디를 가진 테이블 요소에 삽입
이런 원리로 서버로부터 데이터를 가져와서 동적으로 HTML 테이블에 표시함
서버 실행 중 오류가 났을 때 server.js에서 이 오류를 리턴해보기 위해 일부러 sql에 오타를 넣어봄
const mysql = require("mysql");
const conn = mysql.createConnection({
host: "localhost",
user: "test",
password: "1234",
database: "myboard",
});
conn.connect();
const express = require("express");
const app = express();
app.use(express.static("public"));
app.listen(8080, function () {
console.log("8080 server ready...");
});
app.get("/list", function (req, res) {
conn.query(
`**selectt** post.id, title, content,created, writer,email
from post
left join profile
on post.profile_id=profile.id`,
function (err, rows, fields) {
if (err) {
throw err;
} else {
res.send(rows);
}
}
);
});
const getPostListBtn = document.getElementById("getPostListBtn");
getPostListBtn.addEventListener("click", async function () {
// var xhttp = new XMLHttpRequest();
// xhttp.onreadystatechange = function() {
// if (this.readyState == 4 && this.status == 200) {
// // Typical action to be performed when the document is ready:
// //document.getElementById("demo").innerHTML = xhttp.responseText;
// console.log(xhttp.responseText);
// }
// };
// xhttp.open("GET", "/list", true);
// xhttp.send();
try {
let resObj = await fetch("/list");
let data = await resObj.json();
//console.log(data);
let displayData = `<thead>
<td>ID</td>
<td>Title</td>
<td>writer</td>
<td>created</td>
</thead>
<tbody>`;
data.forEach((item, index) => {
displayData += `<tr>
<td>${item.id}</td>
<td>${item.title}</td>
<td>${item.writer}</td>
<td>${item.created}</td>
</tr>`;
});
displayData += `</tbody>`;
document.getElementById("datatablesSimple").innerHTML = displayData;
** } catch (err) {
alert("게시물 가져오기 실패");
}**
});

<a>태그를 넣어봄<td><a href='#'>${item.title}</a></td>

index.html
button 태그 위에 modal 태그 추가
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close" id="closeBtn">×</span>
<p id="modalText">모달</p>
</div>
</div>
<button id="getPostListBtn">데이터 가져오기</button>
my.js
event 추가
const getPostListBtn = document.getElementById("getPostListBtn");
getPostListBtn.addEventListener("click", async function () {
// var xhttp = new XMLHttpRequest();
// xhttp.onreadystatechange = function() {
// if (this.readyState == 4 && this.status == 200) {
// // Typical action to be performed when the document is ready:
// //document.getElementById("demo").innerHTML = xhttp.responseText;
// console.log(xhttp.responseText);
// }
// };
// xhttp.open("GET", "/list", true);
// xhttp.send();
try {
let resObj = await fetch("/list");
let data = await resObj.json();
//console.log(data);
let displayData = `<thead>
<td>ID</td>
<td>Title</td>
<td>writer</td>
<td>created</td>
</thead>
<tbody>`;
data.forEach((item, index) => {
displayData += `<tr>
<td>${item.id}</td>
<td class="title-cell" data-content="${item.content}"><a href="#">${item.title}</a></td>
<td>${item.writer}</td>
<td>${item.created}</td>
</tr>`;
});
displayData += `</tbody>`;
document.getElementById("datatablesSimple").innerHTML = displayData;
} catch (err) {
alert("게시물 가져오기 실패");
}
// Title cell에 클릭 이벤트 추가, 클릭 시 모달 열기(가시화)
let titleCells = document.querySelectorAll(".title-cell");
titleCells.forEach(cell => {
cell.addEventListener("click", function () {
document.getElementById("modalText").innerText = cell.getAttribute("data-content");
document.getElementById("myModal").style.display = "block";
});
});
});
// 클릭 시 모달 닫기 기능
document.querySelector(".close").addEventListener("click", function () {
document.getElementById("myModal").style.display = "none";
});
// 모달 외부 클릭 시 닫기
window.addEventListener("click", function (event) {
if(event.target == document.getElementById("myModal")) {
document.getElementById("myModal").style.display = "none";
}
});
style.css
css 추가
// display: none 속성으로 버튼 클릭 전엔 모달이 보이지 않음
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
padding-top: 60px;
}
.modal-content {
background-color: #fefefe;
margin: 5% auto;
padding: 15px;
border: 1px solid #888;
width: 30%;
height: 30%;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
유용한정보 감사합니다!