기존 게시판 프로젝트에 회원가입, 로그인 등 추가하겠습니다.
먼저 server.js 파일에 회원가입 코드를 추가하기위해 필요한 모듈을 설치하겠습니다.
npm install cookieParser
npm install bcrypt
설치 완료 후 server.js에 모듈을 가져옵니다.
const cookieParser = require('cookie-parser');
const bcrypt = require('bcrypt');
모듈을 추가 하였으면 먼저 user 정보를 담을 데이터베이스를 만들어줍니다.
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
);
server.js에 회원가입 처리를 위한 코드를 추가합니다.
// 회원가입 처리를 위한 라우트입니다.
app.post('/register', jsonParser, (req, res) => {
const { username, password } = req.body;
// 사용자명이 이미 사용 중인지 확인합니다.
const checkUsernameQuery = 'SELECT * FROM users WHERE username = ?';
connection.query(checkUsernameQuery, [username], (checkErr, checkResults) => {
if (checkErr) {
console.error('사용자명 가용성 확인 중 오류:', checkErr);
return res.status(500).json({ success: false, message: '서버 오류' });
}
if (checkResults.length > 0) {
return res.status(400).json({ success: false, message: '이미 사용 중인 사용자명입니다.' });
}
// 비밀번호를 해시하여 데이터베이스에 저장합니다.
bcrypt.hash(password, 10, (hashErr, hashedPassword) => {
if (hashErr) {
console.error('비밀번호 해싱 중 오류:', hashErr);
return res.status(500).json({ success: false, message: '서버 오류' });
}
// 사용자 정보를 데이터베이스에 저장합니다.
const insertUserQuery = 'INSERT INTO users (username, password) VALUES (?, ?)';
connection.query(insertUserQuery, [username, hashedPassword], (insertErr, insertResults) => {
if (insertErr) {
console.error('데이터베이스에 사용자 삽입 중 오류:', insertErr);
return res.status(500).json({ success: false, message: '서버 오류' });
}
// 회원가입 성공
res.json({ success: true, message: '회원가입이 완료되었습니다.' });
});
});
});
});
그 다음 로그인기능을 추가하기 위해 로그인 코드도 추가해줍니다.
// 로그인 처리를 위한 라우트입니다.
app.post('/login', jsonParser, (req, res) => {
const { username, password } = req.body;
// 'users' 테이블에서 'username' 및 'password' 열이 있는 것으로 가정합니다.
const query = 'SELECT * FROM users WHERE username = ?';
connection.query(query, [username], (err, results) => {
if (err) {
console.error('데이터베이스 쿼리 중 오류:', err);
return res.status(500).json({ success: false, message: '서버 오류' });
}
if (results.length === 0) {
return res.status(401).json({ success: false, message: '사용자명이나 비밀번호가 올바르지 않습니다.' });
}
const user = results[0];
// 제공된 비밀번호를 데이터베이스에 저장된 해시된 비밀번호와 비교합니다.
bcrypt.compare(password, user.password, (bcryptErr, passwordMatch) => {
if (bcryptErr) {
console.error('비밀번호 비교 중 오류:', bcryptErr);
return res.status(500).json({ success: false, message: '서버 오류' });
}
if (passwordMatch) {
// 비밀번호가 일치하면 쿠키를 설정하고 성공 응답을 보냅니다.
res.cookie('user', username).json({ success: true, message: '로그인 성공' });
// 콘솔에 사용자 정보 기록
console.log('로그인한 사용자:', user);
} else {
// 비밀번호가 일치하지 않으면 실패 응답을 보냅니다.
res.status(401).json({ success: false, message: '사용자명이나 비밀번호가 올바르지 않습니다.' });
}
});
});
});
그 다음 public 디렉터리 에 기존에 구현했던 js 파일은 모두 지워줍니다.
지운 후 public 디렉터리 에 회원가입 페이지와 로그인 페이지를 만들어줍니다.
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>회원가입</title>
</head>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f4f4f4;
}
h1 {
color: #333;
}
form {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 300px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
}
input {
width: 100%;
padding: 8px;
margin-bottom: 16px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background-color: #4caf50;
color: #fff;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
#resultMessage {
margin-top: 16px;
color: #333;
}
</style>
<body>
<h1>회원가입</h1>
<form id="registerForm">
<label for="username">사용자명:</label>
<input type="text" id="username" name="username" required>
<br>
<label for="password">비밀번호:</label>
<input type="password" id="password" name="password" required>
<br>
<button type="submit">가입하기</button>
</form>
<div id="registrationMessage"></div>
</body>
<script>
document.getElementById('registerForm').addEventListener('submit', function (event) {
event.preventDefault();
// 사용자명과 비밀번호 가져오기
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
// 서버에 가입 정보 전송
fetch('/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
})
.then(response => response.json())
.then(data => {
const registrationMessage = document.getElementById('registrationMessage');
if (data.success) {
// 가입 성공 메시지 표시
registrationMessage.innerHTML = '회원가입이 완료되었습니다. <a href="/login">로그인하러 가기</a>';
} else {
// 가입 실패 메시지 표시
registrationMessage.innerHTML = '회원가입 실패: ' + data.message;
}
})
.catch(error => {
console.error('가입 중 오류:', error);
});
});
</script>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>로그인</title>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f4f4f4;
}
h1 {
color: #333;
}
form {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 300px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
}
input {
width: 100%;
padding: 8px;
margin-bottom: 16px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background-color: #4caf50;
color: #fff;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
#resultMessage {
margin-top: 16px;
color: #333;
}
</style>
</head>
<body>
<h1>로그인</h1>
<form id="loginForm">
<label for="username">사용자명:</label>
<input type="text" id="username" name="username" required minlength="3">
<label for="password">비밀번호:</label>
<input type="password" id="password" name="password" required minlength="6">
<button type="button" onclick="submitForm()">로그인</button>
</form>
<div id="resultMessage"></div>
<script>
function submitForm() {
// 입력된 사용자명과 비밀번호 가져오기
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
// 사용자명과 비밀번호를 JSON 객체로 만들기
const data = { username, password };
// 서버에 인증을 위한 AJAX 요청
fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(result => {
// 결과 메시지 표시
document.getElementById('resultMessage').textContent = result.message;
// 로그인이 성공하면 메인 페이지로 리디렉션
if (result.success) {
window.location.href = '/';
}
})
.catch(error => {
console.error('Error:', error);
// 오류 메시지 표시
document.getElementById('resultMessage').textContent = '서버 오류';
});
}
</script>
</body>
</html>
다시 server.js에 가서 로그인 페이지를 제공하는 코드를 추가합니다.
// 로그인 페이지를 제공하는 라우트입니다.
app.get('/login', (req, res) => {
res.sendFile(__dirname + '/public/login.html');
});
로그아웃 코드도 추가해줍니다.
// 로그아웃을 처리하는 라우트를 추가합니다.
app.get('/logout', (req, res) => {
// 사용자가 인증되었는지 확인합니다.
if (req.cookies.user) {
// 사용자 쿠키를 지워 로그아웃합니다.
res.clearCookie('user').json({ success: true, message: '로그아웃 성공' });
} else {
// 사용자가 인증되지 않았으면 오류 응답을 보냅니다.
res.status(401).json({ success: false, message: '인증되지 않음' });
}
});
메인 HTML 파일을 제공하는 코드를 로그인하였을때 쿠키정보를 보내기 위해 코드를 수정해줍니다.
// 메인 HTML 파일을 제공하는 라우트입니다.
app.get('/', (req, res) => {
const { user } = req.cookies;
if (user) {
res.sendFile(__dirname + '/public/index.html');
} else {
res.redirect('/login');
}
});
이제 글쓰기, 글수정, 글삭제 코드를 변경하기 위해서 server.js 에 로그인 하였을때 세션정보를 확인하기 위한 코드를 추가합니다.
// 사용자 세션을 확인하는 라우트를 추가합니다.
app.get('/checkSession', (req, res) => {
// req.cookies가 정의되어 있는지 확인합니다.
if (req.cookies && req.cookies.user) {
// 정의되어 있으면 사용자 정보를 보냅니다.
res.json({ username: req.cookies.user });
} else {
// 정의되어 있지 않거나 사용자가 없으면 null을 보냅니다.
res.json(null);
}
});
MySQL에 가서 board 테이블을 수정합니다.
writer 를 user_id 로 수정해줍니다.
게시글을 작성하는 코드를 변경해줍니다.
// 새로운 게시글을 작성하는 라우트입니다.
app.post('/article', jsonParser, (req, res) => {
// 'board' 테이블에 새 레코드를 삽입하기 위한 SQL 쿼리입니다.
const sql = 'INSERT INTO board (title, user_id, content) VALUES (?,?,?)';
const title = req.body.title;
// 변경된 부분: user_id 정보를 req.body.user_id에서 가져옵니다.
const user_id = req.body.user_id;
const content = req.body.content;
const params = [title, user_id, content];
// 주어진 매개변수로 쿼리를 실행합니다.
connection.query(sql, params, (err, rows, fields) => {
if (err) {
console.error('새 글 삽입 중 오류 발생:', err);
return res.status(500).json({ status: 500, message: '내부 서버 오류' });
}
console.log(rows);
// 쿼리 결과를 응답으로 전송합니다 (클라이언트에게 무언가를 전송하려고 가정합니다).
res.status(201).json({ status: 201, message: '게시글이 성공적으로 작성되었습니다.', data: rows });
});
});
public 디렉터리에 update.html 코드도 수정해줍니다.
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 페이지의 메타 정보를 설정합니다. -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>게시글 수정</title>
<style>
/* 페이지의 스타일을 지정하는 CSS 코드입니다. */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
form {
max-width: 600px;
margin: 20px auto;
padding: 20px;
background-color: #fff;
border: 1px solid #ddd;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"],
textarea {
width: 100%;
padding: 8px;
margin-bottom: 10px;
box-sizing: border-box;
}
button {
background-color: #333;
color: #fff;
padding: 10px;
border: none;
cursor: pointer;
border-radius: 5px;
margin-right: 10px;
}
button:hover {
background-color: #555;
}
h1 a {
text-decoration: none;
color: #333;
}
h1 a:hover {
color: #555;
}
</style>
</head>
<body>
<!-- 페이지 상단에 표시되는 제목입니다. -->
<h1>게시글 수정</h1>
<!-- 게시글 수정 폼입니다. -->
<form id="update-form">
<!-- 게시글 ID를 감추어서 전송합니다. -->
<input hidden id="id" name="id">
<!-- 제목 입력란 -->
<label for="title">제목:</label>
<input type="text" id="title" name="title" required><br>
<!-- 내용 입력란 -->
<label for="content">내용:</label>
<textarea id="content" name="content" required></textarea><br>
<!-- 게시글 수정 버튼 -->
<button onclick="updateArticle()">게시글 수정</button>
<!-- 뒤로가기 버튼 -->
<button onclick="goBack()">뒤로 가기</button>
</form>
<!-- JavaScript 코드입니다. -->
<script>
// URL에서 게시글 ID를 가져옵니다.
const urlParams = new URLSearchParams(window.location.search);
const articleId = urlParams.get('id');
// articleId가 null이 아닌 경우에만 fetch 요청을 보냅니다.
if (articleId !== null) {
// 게시글 상세 정보를 가져와서 폼에 미리 채웁니다.
fetch(`http://localhost:3000/articles/${articleId}`)
.then(response => response.json())
.then(article => {
// 폼 필드에 기존 데이터로 채웁니다.
document.getElementById('id').value = article.idx;
document.getElementById('title').value = article.title;
document.getElementById('content').value = article.content;
})
.catch(error => {
console.error('게시글 정보를 가져오는 중 오류 발생:', error);
});
} else {
console.error('게시글 ID를 찾을 수 없습니다.');
}
// 게시글 수정 버튼을 눌렀을 때 해당 게시글 상세보기 페이지로 이동하는 함수
function updateArticle() {
const title = document.getElementById('title').value;
const content = document.getElementById('content').value;
// PUT 요청을 사용하여 게시글 업데이트
fetch(`http://localhost:3000/article/${articleId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ title, content }),
})
.then(response => response.json())
.then(result => {
console.log('게시글이 성공적으로 업데이트되었습니다.', result);
// 게시글 상세보기 페이지로 이동
const articleDetailsURL = `http://localhost:3000/article-details.html?id=${articleId}`;
alert("게시글이 수정되었습니다!")
window.location.replace(articleDetailsURL);
})
.catch(error => {
console.error('게시글 업데이트 중 오류 발생:', error);
});
}
// 뒤로가기 버튼을 눌렀을 때 이전 페이지로 이동하는 함수
function goBack() {
window.history.back();
}
</script>
</body>
</html>
public 디렉터리에 index.html 에서 비로그인 일때 Login, SignUp 버튼을 보여주고 로그인 하면 로그인 아이디, Logout 버튼이 나오게
수정합니다.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 페이지의 메타 정보를 설정합니다. -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 페이지의 스타일을 지정하는 CSS 코드입니다. -->
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
background-color: #f8f9fa;
}
h1 {
background-color: #343a40;
color: #ffffff;
padding: 20px;
margin: 0;
}
nav ul {
list-style: none;
padding: 0;
margin: 0;
background-color: #343a40;
overflow: hidden;
}
nav ul li {
float: left;
display: inline;
padding: 10px;
}
nav ul li a {
text-decoration: none;
color: #ffffff;
font-weight: bold;
display: inline-block;
padding: 10px 20px;
background-color: #343a40;
transition: background-color 0.3s ease;
}
nav ul li a:hover {
background-color: #495057;
}
#articles-list {
list-style: none;
padding: 0;
margin: 20px;
}
#articles-list li {
background-color: #ffffff;
border: 1px solid #ced4da;
padding: 15px;
margin-bottom: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
#articles-list li a {
text-decoration: none;
color: #343a40;
font-weight: bold;
}
#articles-list li a:hover {
color: #007bff;
}
/* 페이징 컨트롤의 스타일을 설정합니다. */
.pagination {
display: inline-block;
margin-top: 20px;
}
.pagination li {
display: inline;
margin-right: 5px;
}
.pagination a {
color: #007bff;
padding: 5px 10px;
text-decoration: none;
border: 1px solid #007bff;
border-radius: 3px;
}
.pagination .active a {
background-color: #007bff;
color: #fff;
}
.pagination a:hover {
background-color: #0056b3;
color: #fff;
}
.pagination .disabled a {
pointer-events: none;
cursor: not-allowed;
background-color: #ccc;
color: #777;
}
#userInfo {
margin-top: 0px;
padding: 15px; /* Increase the padding to make the background color larger */
background-color: #495057;
color: #fff;
text-align: right;
}
#userInfo a {
color: #fff;
text-decoration: underline;
cursor: pointer;
margin-left: 10px;
}
</style>
</head>
<body>
<!-- 페이지의 제목을 설정합니다. -->
<title>게시글 목록</title>
<!-- 페이지 상단에 표시되는 제목입니다. -->
<h1>게시글 목록</h1>
<div id="userInfo"></div>
<!-- 네비게이션 링크 -->
<nav>
<ul>
<!-- 게시글 작성 페이지로 이동하는 링크 -->
<li><a href="#" onclick="handleCreateClick()">게시글 작성</a></li>
</ul>
</nav>
<!-- 게시글 목록을 표시하는 부분입니다. -->
<ul id="articles-list"></ul>
<!-- Pagination controls -->
<div id="pagination" class="col-8">
<ul class="pagination pagination-sm justify-content-center align-items-center h-100 mb-0" id="pageList"></ul>
</div>
<!-- JavaScript 코드입니다. -->
<script>
// 변수 초기화
const articlesList = document.getElementById('articles-list'); // HTML에서 게시글 목록을 표시할 엘리먼트
const pageListElement = document.getElementById('pageList'); // HTML에서 페이지 목록을 표시할 엘리먼트
let currentPage = 1; // 현재 페이지 변수 초기화
let maxPages = 1; // 최대 페이지 수를 저장하는 변수 초기화
// 함수: 게시글 목록을 가져오는 함수
function fetchArticles(page) {
const pageSize = 5; // 페이지당 표시할 게시글 수 (원하는 값으로 조절)
const startIndex = (page - 1) * pageSize; // 가져올 게시글의 시작 인덱스
// 서버로부터 게시글 데이터 가져오기
fetch(`http://localhost:3000/articles?page=${page}`)
.then(response => response.json())
.then(data => {
const articles = data.rows; // 서버에서 받아온 게시글 데이터
articlesList.innerHTML = ''; // 이전에 표시된 게시글 초기화
// 각 게시글을 리스트에 추가
articles.forEach(article => {
const li = document.createElement('li');
li.innerHTML = `
<strong>Title:</strong> ${article.title}<br>
<strong>Writer:</strong> ${article.user_id}<br>
<strong>Content:</strong> ${article.content}<br>
<strong>Views:</strong> ${article.view_cnt}<br>
<strong>Insert Time:</strong> ${article.insert_time}<br>
<strong>Update Time:</strong> ${article.update_time ? article.update_time : 'N/A'}<br>
<strong>Delete Time:</strong> ${article.delete_time ? article.delete_time : 'N/A'}<br>
<a href="article-details.html?id=${article.idx}">상세 정보 보기</a><br>
<hr>
`;
articlesList.appendChild(li);
});
// 전체 페이지 수 업데이트
maxPages = data.totalPages;
// 페이지 목록 업데이트
renderPaginationControls(data.page_current, data.page_max);
});
}
// 사용자 세션 확인 및 사용자 정보 표시
fetch('/checkSession')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(user => {
const userInfoElement = document.getElementById('userInfo');
if (user) {
userInfoElement.innerHTML = `Logged in as: ${user.username} | <a href="#" onclick="logout()">Logout</a>`;
} else {
userInfoElement.innerHTML = '<a href="/login">Login</a>';
userInfoElement.innerHTML += '<a href="/register.html">Sign Up</a>';
}
})
.catch(error => {
console.error('Error checking user session:', error);
});
// 게시글 작성 버튼 클릭 처리
function handleCreateClick() {
fetch('/checkSession')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(user => {
if (user) {
// 사용자가 로그인한 경우, create.html 페이지로 이동
window.location.href = 'create.html';
} else {
// 사용자가 로그인하지 않은 경우, 경고 메시지 표시 후 로그인 페이지로 이동
alert('로그인이 필요합니다.');
window.location.href = '/login';
}
})
.catch(error => {
console.error('Error checking user session:', error);
});
}
// 함수: 페이지 변경 이벤트 처리
function changePage(page) {
// 페이지가 1보다 작아지지 않도록 보장
currentPage = Math.max(page, 1);
// 페이지가 최대 페이지 수를 초과하지 않도록 보장
currentPage = Math.min(currentPage, maxPages);
// 페이지를 업데이트하고 게시글을 가져옵니다.
fetchArticles(currentPage);
}
// 함수: 페이지 목록 렌더링
function renderPaginationControls(page_current, page_max) {
// 페이지 목록 엘리먼트를 찾지 못한 경우 에러를 방지하기 위해 확인
if (!pageListElement) {
console.error("Error in renderPaginationControls: Unable to find the 'pageList' element on the page.");
return;
}
// 이전 페이지 목록 초기화
pageListElement.innerHTML = '';
const offset = 2;
const previousBtnEnabled = page_current > 1; // 이전 버튼 활성화 여부
const nextBtnEnabled = page_current < page_max; // 다음 버튼 활성화 여부
// "이전" 버튼 추가
pageListElement.innerHTML += `
<li class="page-item ${previousBtnEnabled ? '' : 'disabled'}">
<a class="page-link" href="#" onclick="changePage(${page_current - 1})" ${previousBtnEnabled ? '' : 'tabindex=-1'}>«</a>
</li>
`;
// 페이지 번호 추가
for (let i = 1; i <= page_max; i++) {
if (i == 1 || i == page_max || (i >= page_current - offset && i <= page_current + offset)) {
pageListElement.innerHTML += `
<li class="page-item ${page_current == i ? 'active' : ''}">
<a class="page-link" href="#" onclick="changePage(${i})">${i}</a>
</li>
`;
} else if (i == 2 || i == page_max - 1) {
pageListElement.innerHTML += `
<li><a class="page-link">...</a></li>
`;
}
}
// "다음" 버튼 추가
pageListElement.innerHTML += `
<li class="page-item ${nextBtnEnabled ? '' : 'disabled'}">
<a class="page-link" href="#" onclick="changePage(${page_current + 1})" ${nextBtnEnabled ? '' : 'tabindex=-1'}>»</a>
</li>
`;
}
// 초기 페이지 로딩 시 첫 번째 페이지의 게시글 가져오기
fetchArticles(1);
// 함수: 로그아웃 처리
function logout() {
fetch('/logout')
.then(response => response.json())
.then(result => {
if (result.success) {
// 성공적인 로그아웃 후 홈 페이지로 리다이렉트
window.location.href = '/';
} else {
console.error('Logout failed:', result.message);
}
})
.catch(error => {
console.error('Error during logout:', error);
});
}
</script>
</body>
</html>
public 디렉터리에 article-details.html 에서 로그인한 유저의 게시글이 맞다면 수정,삭제 버튼을 보이게 수정합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 페이지의 메타 정보를 설정합니다. -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 페이지의 제목을 설정합니다. -->
<title>게시글 상세 정보</title>
<!-- 페이지의 스타일을 지정하는 CSS 코드입니다. -->
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
h1 {
background-color: #333;
color: #fff;
padding: 10px;
margin: 0;
}
h1 a {
text-decoration: none;
color: #fff;
transition: color 0.3s ease;
}
h1 a:hover {
color: blue;
}
#article-details {
max-width: 600px;
margin: 20px auto;
padding: 20px;
background-color: #fff;
border: 1px solid #ddd;
}
#article-details strong {
font-weight: bold;
}
#article-details hr {
margin: 10px 0;
border: 0;
border-top: 1px solid #ddd;
}
#article-button {
max-width: 600px;
margin: 20px auto;
padding: 20px;
background-color: #f4f4f4;
}
.action-button {
display: inline-block;
padding: 10px;
margin: 10px 5px 20px;
background-color: #333;
color: #fff;
text-decoration: none;
border: none;
cursor: pointer;
border-radius: 5px;
}
.action-button:hover {
background-color: #555;
}
</style>
</head>
<body>
<!-- 페이지 상단에 위치하는 제목 부분입니다. -->
<h1><a href="http://localhost:3000/">게시글 상세 정보</a></h1>
<!-- 게시글의 상세 정보를 표시하는 부분입니다. -->
<div id="article-details"></div>
<!-- 업데이트 및 삭제 버튼이 있는 부분입니다. -->
<div id="article-button">
<!-- 업데이트 버튼 -->
<button id="update-button" class="action-button">게시글 수정</button>
<!-- 삭제 버튼 -->
<button id="delete-button" class="action-button">게시글 삭제</button>
<!-- 뒤로가기 버튼 -->
<button id="goBack" class="action-button" onclick="goBack()">목록</button>
</div>
<!-- JavaScript 코드입니다. -->
<script>
// URL에서 게시글 ID 가져오기
const urlParams = new URLSearchParams(window.location.search);
const articleId = urlParams.get('id');
// 게시글 상세 정보 가져오기
fetch(`http://localhost:3000/articles/${articleId}`)
.then(response => response.json())
.then(article => {
const articleDetails = document.getElementById('article-details');
if (article) {
// 게시글이 존재하는 경우 상세 정보를 표시합니다.
const update_time = article.update_time ? article.update_time : 'N/A';
articleDetails.innerHTML = `
<strong>Title:</strong> ${article.title}<br>
<strong>Writer:</strong> ${article.user_id}<br>
<strong>Content:</strong> ${article.content}<br>
<strong>Views:</strong> ${article.view_cnt}<br>
<strong>Insert Time:</strong> ${article.insert_time}<br>
<strong>Update Time:</strong> ${update_time}<br>
<hr>
`;
} else {
// 게시글이 존재하지 않는 경우 메시지를 표시합니다.
articleDetails.innerHTML = '<p>게시글을 찾을 수 없습니다.</p>';
}
// 사용자가 작성자인 경우에만 업데이트 및 삭제 버튼을 활성화합니다.
const updateButton = document.getElementById('update-button');
const deleteButton = document.getElementById('delete-button');
fetch('/checkSession')
.then(response => response.json())
.then(user => {
if (user && user.username === article.user_id) {
updateButton.style.display = 'inline-block';
deleteButton.style.display = 'inline-block';
// 업데이트 버튼에 이벤트 리스너 추가
updateButton.addEventListener('click', () => {
// 업데이트 페이지로 리다이렉트
window.location.href = `/update.html?id=${articleId}`;
});
} else {
updateButton.style.display = 'none';
deleteButton.style.display = 'none';
}
// 삭제 버튼에 이벤트 리스너 추가
deleteButton.addEventListener('click', () => {
// 사용자가 정말로 게시글을 삭제하고 싶어 하는지 확인합니다.
const confirmDelete = confirm('정말로 게시글을 삭제하시겠습니까??');
if (confirmDelete) {
// 게시글을 삭제하기 위한 DELETE 요청 수행
fetch(`http://localhost:3000/article/${articleId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
alert("삭제 하였습니다!");
// 선택적으로 게시글 목록 페이지로 리다이렉트할 수 있습니다.
window.location.href = 'http://localhost:3000/';
})
.catch(error => {
console.error('게시글 삭제 오류:', error);
alert('게시글을 삭제하는 동안 오류가 발생했습니다. 다시 시도하세요.');
});
}
});
})
.catch(error => {
console.error('사용자 세션 확인 오류:', error);
});
})
.catch(error => {
console.error(`게시글 가져오기 오류: ${error.message}`);
// ... (기존 코드 유지)
});
// 이전 페이지로 이동하는 함수
function goBack() {
window.location.href = 'http://localhost:3000/';
}
</script>
</body>
</html>
실행 화면
비로그인 시 index.html 화면
로그인 시 index.html 화면
게시글 작성자가 로그인한 유저 라면 article-details.html 화면
게시글 작성자가 로그인한 유저가 아닐때 article-details.html 화면
회원가입 화면
회원가입 했을때 화면
중복 아이디 로 가입 했을때 화면
로그인 화면
전체코드는 깃허브에 올려두겠습니다.
https://github.com/phs1579/NodeBoard 를 참조해주세요.
'Node.js > Node 프로젝트 예제' 카테고리의 다른 글
[Node.js] 게시판[4/4] 댓글 기능, 이미지 업로드 (1) | 2023.12.27 |
---|---|
[Node.js] 게시판(2/4) 게시판 페이칭 처리 (0) | 2023.12.21 |
[Node.js] 게시판(1/4) CRUD 만들기, 게시글 작성/상세/수정/삭제 (1) | 2023.12.20 |
node.js 웹 채팅 (1) | 2023.12.19 |