위와 같은 흐름을 따라 게시판 기능을 하는 웹페이지를 만들어볼 것이다. 1편에서는 개발환경 세팅과 소스코드를 이해하는 데 초점을 맞췄다. 엄밀히 말하면 프론트엔드를 구현하는 파트라고 볼 수 있다. AWS 콘솔환경에서 다룰 백엔드 파트는 2편에서 다뤄보겠다.
요구사항 분석
우리는 아래와 같은 게시판 페이지를 만들고 싶은 상황이다.
게시물 등록 (article_add.html)
- 사용자가 게시물 제목, 내용, 사진을 입력
- 사진이 S3에 업로드되고, 게시물 정보는 DynamoDB에 저장
- 게시물 작성이 완료되면 게시물 목록 페이지로 리다이렉트
게시물 목록 조회 (article_view.html)
- DynamoDB에서 모든 게시물을 불러와 목록 형태로 화면에 출력
- 게시물 제목을 클릭하면 해당 게시물의 상세 페이지로 이동
게시물 상세 조회 및 삭제 (article_detail.html)
- 특정 게시물을 조회하여 제목, 내용, 첨부된 이미지를 출력
- 사용자가 직접 작성한 게시물은 삭제할 수 있음
소스코드
<!--article_add.html-->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>글 등록 페이지</title>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.283.1.min.js"></script>
<script src="./js/s3_photoExample.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div style="text-align: center;"><h1>글 등록 페이지</h1></div>
<form name="article_add_form" method="POST">
<table>
<tr>
<th>Title</th>
<td><input type="text" id="title" required></td>
</tr>
<tr>
<th>Content</th>
<td><input type="text" id="content" required></td>
</tr>
<tr>
<th>사진</th>
<td><input type="file" id="article_image" name="filename"></td>
</tr>
<tr>
<td colspan="3" style="text-align: center;">
<button type="button" onclick="submitToAPI(event)">등록</button>
<button type="button" onclick="cancelAndGoBack()">취소</button>
</td>
</tr>
</table>
</form>
</div>
<script>
function submitToAPI(e) {
e.preventDefault();
add_article_with_photo('images', function() {
window.location.href = 'article_view.html'; // 등록 후 게시물 목록 페이지로 이동
});
}
function cancelAndGoBack() {
window.location.href = 'article_view.html'; // 게시물 보기 페이지로 이동
}
</script>
</body>
</html>
<!--article_detail.html-->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>게시물 상세 보기</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.283.1.min.js"></script>
<script src="./js/s3_photoExample.js"></script>
</head>
<body onload="getArticleDetail()">
<div class="container">
<h1>게시물 상세 보기</h1>
<div id="article-detail">
<div class="article-box article-title-box">
<h2 id="article-title"></h2>
</div>
<div class="article-box article-content-box">
<img id="article-image" src="" alt="게시물 이미지">
<p id="article-content"></p>
</div>
</div>
<button onclick="goToArticleList()">목록으로</button>
<button id="delete-button" onclick="deleteArticle()">삭제</button>
</div>
<script>
const URL = "https://8uetkgzthk.execute-api.ap-northeast-2.amazonaws.com/2024-08-26/article_resource";
function getArticleDetail() {
const params = new URLSearchParams(window.location.search);
const article_id = params.get('id');
if (article_id) {
fetch(URL + '?article_id=' + article_id, {
method: "GET",
headers: {
'Accept': 'application/json'
}
})
.then(resp => resp.json())
.then(function(data) {
let article = data.Item;
if (!article) {
alert('게시글을 찾을 수 없습니다.');
return;
}
document.getElementById('article-title').textContent = article.title;
document.getElementById('article-content').textContent = article.content;
document.getElementById('article-image').src = article.img_source;
})
.catch(err => console.log(err));
} else {
alert('Invalid article ID');
}
}
function goToArticleList() {
window.location.href = 'article_view.html'; // 게시물 목록 페이지로 이동
}
function deleteArticle() {
const params = new URLSearchParams(window.location.search);
const article_id = params.get('id');
if (!article_id) return alert("게시물 ID가 유효하지 않습니다.");
// 게시물 정보 가져오기
fetch(URL + '?article_id=' + article_id)
.then(resp => resp.json())
.then(function(data) {
const article = data.Item;
if (!article) {
alert('게시글을 찾을 수 없습니다.');
return;
}
// 현재 로그인한 사용자의 이름 가져오기
const currentUser = localStorage.getItem('username');
// 작성자와 현재 사용자 비교
if (article.author === currentUser) {
// DELETE 요청 - body에 article_id를 포함하여 JSON 문자열로 전송
fetch(URL, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
TableName: "simple_board",
Key: { "article_id": article_id }
})
})
.then(resp => {
if (resp.ok) {
alert('게시물이 삭제되었습니다.');
window.location.href = 'article_view.html'; // 목록으로 돌아가기
} else {
resp.json().then(data => alert('삭제 실패: ' + data.message));
}
})
.catch(err => console.log('Error:', err));
} else {
alert('본인의 게시물만 삭제할 수 있습니다.');
}
})
.catch(err => console.log(err));
}
</script>
</body>
</html>
<!--article_view.html-->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>게시물 보기 페이지</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.283.1.min.js"></script>
<script src="./js/s3_photoExample.js"></script>
</head>
<body onload="getArticles()">
<header class="header">
<div class="logo-nav">
<a href="../main_page/index.html" class="logo">Hama Logo</a>
<nav class="nav">
<a href="../bulletin/article_view.html" class="nav-link">community</a>
<a href="#" class="nav-link">news</a>
<a href="#" class="nav-link">guide</a>
</nav>
</div>
<button class="btn" id="userDataButton" onClick="goToUserData()"></button>
</header>
<div class="container">
<h1>게시물 목록</h1>
<ul id="articles"></ul>
<div id="pagination"></div>
<!-- 검색창과 검색 버튼 -->
<div class="search-container">
<input type="text" id="search-input" placeholder="검색어를 입력하세요...">
<button id="search-button" onclick="filterArticles()">검색</button>
</div>
<button id="register-button" onclick="goToArticleAddPage()">등록</button>
</div>
<script>
let article_arr = [];
const URL = "https://8uetkgzthk.execute-api.ap-northeast-2.amazonaws.com/2024-08-26/article_resource";
function getArticles() {
fetch(URL, {
method: "GET",
headers: {
'Accept': 'application/json'
}
})
.then(resp => resp.json())
.then(function(data) {
article_arr = data.Items;
// timestamp 기준으로 최신순으로 정렬
article_arr.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
displayArticles(article_arr);
})
.catch(err => console.log(err));
}
function displayArticles(articles) {
const articlesList = document.getElementById('articles');
articlesList.innerHTML = '';
articles.forEach(function(article) {
let li = document.createElement('li');
li.innerHTML = `
<h3>${article.title}</h3>
<p>작성자: ${article.author}</p>
<p>작성 시간: ${new Date(article.timestamp).toLocaleString()}</p>
`;
li.onclick = function() {
viewArticleDetail(article.article_id);
};
articlesList.appendChild(li);
});
}
function filterArticles() {
const searchInput = document.getElementById('search-input').value.toLowerCase();
const filteredArticles = article_arr.filter(article =>
article.title.toLowerCase().includes(searchInput) ||
article.content.toLowerCase().includes(searchInput)
);
displayArticles(filteredArticles);
}
function goToArticleAddPage() {
window.location.href = 'article_add.html'; // 게시물 등록 페이지로 이동
}
function viewArticleDetail(article_id) {
window.location.href = 'article_detail.html?id=' + article_id; // 게시물 상세 보기 페이지로 이동
}
</script>
</body>
</html>
// s3_photoExample.js
var albumBucketName = "hama-bulletin";
var bucketRegion = "ap-northeast-2";
var IdentityPoolId = "ap-northeast-2:9d74a2fa-0a5b-4206-948d-54e6082933d4";
let currentPage = 1;
const articlesPerPage = 10;
function getArticles() {
fetch(URL, {
method: "GET",
headers: {
'Accept': 'application/json'
}
})
.then(resp => resp.json())
.then(function(data) {
article_arr = data.Items;
// timestamp 기준으로 최신순으로 정렬
article_arr.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
displayArticles(article_arr, currentPage);
})
.catch(err => console.log(err));
}
function displayArticles(articles, page) {
const articlesList = document.getElementById('articles');
articlesList.innerHTML = '';
const startIndex = (page - 1) * articlesPerPage;
const endIndex = startIndex + articlesPerPage;
const paginatedArticles = articles.slice(startIndex, endIndex);
paginatedArticles.forEach(function(article) {
let li = document.createElement('li');
li.innerHTML = `
<h3>${article.title}</h3>
<p>작성자: ${article.author}</p>
<p>작성 시간: ${new Date(article.timestamp).toLocaleString()}</p>
`;
li.onclick = function() {
viewArticleDetail(article.article_id);
};
articlesList.appendChild(li);
});
// 페이지네이션 업데이트
updatePagination(articles.length, page);
}
function updatePagination(totalArticles, currentPage) {
const pagination = document.getElementById('pagination');
pagination.innerHTML = '';
const totalPages = Math.ceil(totalArticles / articlesPerPage);
for (let i = 1; i <= totalPages; i++) {
let button = document.createElement('button');
button.textContent = i;
button.className = i === currentPage ? 'active' : '';
button.onclick = function() {
changePage(i);
};
pagination.appendChild(button);
}
}
function changePage(page) {
currentPage = page;
displayArticles(article_arr, currentPage);
}
function filterArticles() {
const searchInput = document.getElementById('search-input').value.toLowerCase();
const filteredArticles = article_arr.filter(article =>
article.title.toLowerCase().includes(searchInput) ||
article.content.toLowerCase().includes(searchInput)
);
currentPage = 1; // 검색 시 첫 페이지로 이동
displayArticles(filteredArticles, currentPage);
}
AWS.config.update({
region: bucketRegion,
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: IdentityPoolId
})
});
var s3 = new AWS.S3({
apiVersion: "2006-03-01",
params: { Bucket: albumBucketName }
});
// UUID 생성 함수 추가
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// db에 정보 올리는 함수
function upload_to_db(img_location) {
var article_id = uuidv4(); // UUID로 article_id 자동 생성
var article_title = document.querySelector("#title").value;
var article_content = document.querySelector("#content").value;
var username = localStorage.getItem('username');
var Item = {
'article_id': article_id,
'title': article_title,
'content': article_content,
'img_source': img_location,
'timestamp': new Date().toISOString(), // 현재 시간을 ISO 포맷으로 추가
'author': username // 작성자 정보 추가
};
console.log(Item);
const URL = "https://8uetkgzthk.execute-api.ap-northeast-2.amazonaws.com/2024-08-26/article_resource";
fetch(URL, {
method: "POST",
headers: {
'Accept': 'application/json'
},
body: JSON.stringify({
"TableName": "simple_board",
Item
})
}).then(resp => console.log(resp))
.catch(err => console.log(err));
}
function add_article_with_photo(albumName, callback) {
var files = document.getElementById("article_image").files;
if (!files.length) {
return alert("Please choose a file to upload first.");
}
var file = files[0];
var fileName = file.name;
var albumPhotosKey = encodeURIComponent(albumName) + "/";
var photoKey = albumPhotosKey + fileName;
var upload = new AWS.S3.ManagedUpload({
params: {
Bucket: albumBucketName,
Key: photoKey,
Body: file
}
});
var promise = upload.promise();
promise.then(
function(data) {
let img_location = data.Location;
upload_to_db(img_location);
alert("Successfully uploaded photo.");
if (callback) callback(); // 콜백 함수 호출
},
function(err) {
console.log(err);
alert("There was an error uploading your photo: " + err.message);
}
);
}
S3 및 DynamoDB와 연동 (s3_photoExample.js)
var albumBucketName = "hama-bulletin";
var bucketRegion = "ap-northeast-2";
var IdentityPoolId = "ap-northeast-2:*******************************";
let currentPage = 1;
const articlesPerPage = 10;
- albumBucketName: 이미지를 저장할 장소로 사용될 S3 버킷의 이름
- bucketRegion: S3 버킷이 위치한 AWS 리전(region)
- IdentityPoolId: AWS Cognito에서 제공하는 인증 풀 ID로, 이 ID를 통해 익명의 사용자도 AWS 리소스에 접근할 수 있도록 인증을 받음(Amazon Cognito 콘솔탭의 자격 증명 풀에서 확인할 수 있다)
AWS.config.update({
region: bucketRegion,
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: IdentityPoolId
})
});
- AWS.config.update: AWS SDK의 설정을 업데이트하여 Cognito 자격 증명을 사용해 AWS 리소스에 접근할 수 있도록 구성
var s3 = new AWS.S3({
apiVersion: "2006-03-01",
params: { Bucket: albumBucketName }
});
- s3: S3 객체를 생성. 이 객체는 파일 업로드 및 S3 버킷과의 상호작용을 위해 사용된다. 여기서는 API 버전과 버킷 이름을 지정했다.
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
- uuidv4: 고유한 ID를 생성하는 함수. 게시물이나 데이터의 고유 식별자로 사용된다. 각 글마다 고유한 article_id가 부여된다.
function upload_to_db(img_location) {
var article_id = uuidv4(); // UUID로 article_id 자동 생성
var article_title = document.querySelector("#title").value;
var article_content = document.querySelector("#content").value;
var username = localStorage.getItem('username'); // 로컬 스토리지에서 사용자의 이름을 가져옴
- upload_to_db: 이 함수는 사용자로부터 입력받은 게시물의 제목(title), 내용(content), 이미지의 S3 URL을 DynamoDB에 저장하는 역할을 한다.
- article_id: 위에서 정의한 uuidv4 함수를 사용해 고유 ID를 생성한다.
- article_title, article_content: HTML 폼에서 제목과 내용을 가져온다.
- username: 로컬 스토리지에 저장된 사용자 이름을 가져온다.
var Item = {
'article_id': article_id,
'title': article_title,
'content': article_content,
'img_source': img_location,
'timestamp': new Date().toISOString(), // 현재 시간을 ISO 포맷으로 추가
'author': username // 작성자 정보 추가
};
console.log(Item);
- Item: 게시물의 데이터를 객체로 구성. 여기에는 article_id, title, content, 이미지 URL(img_source), 작성 시간(timestamp), 작성자 정보(author)가 포함된다.
const URL = "https://8uetkgzthk.execute-api.ap-northeast-2.amazonaws.com/2024-08-26/article_resource";
fetch(URL, {
method: "POST",
headers: {
'Accept': 'application/json'
},
body: JSON.stringify({
"TableName": "simple_board",
Item
})
}).then(resp => console.log(resp))
.catch(err => console.log(err));
}
- fetch: API Gateway를 통해 DynamoDB에 POST 요청을 보내는 코드. 게시물 정보를 Item 객체에 담아 전송한다. URL은 API Gateway의 엔드포인트.
- Item 데이터가 JSON 형식으로 DynamoDB에 전송된다.
function add_article_with_photo(albumName, callback) {
var files = document.getElementById("article_image").files;
if (!files.length) {
return alert("Please choose a file to upload first.");
}
var file = files[0];
var fileName = file.name;
var albumPhotosKey = encodeURIComponent(albumName) + "/";
var photoKey = albumPhotosKey + fileName;
- add_article_with_photo: 이미지 파일을 S3에 업로드하고, 업로드가 완료되면 콜백을 통해 DynamoDB에 저장하는 함수
- files: 사용자가 업로드한 파일을 가져온다. 파일이 없으면 경고 메시지를 출력.
- albumName: 이미지가 저장될 S3의 폴더(앨범) 이름을 지정
- photoKey: 이미지가 저장될 S3의 경로
var upload = new AWS.S3.ManagedUpload({
params: {
Bucket: albumBucketName,
Key: photoKey,
Body: file
}
});
- upload: AWS.S3.ManagedUpload 객체를 생성해 S3에 이미지를 업로드. Bucket은 S3 버킷 이름, Key는 저장될 파일 경로, Body는 업로드할 파일의 내용을 가리킨다.
var promise = upload.promise();
promise.then(
function(data) {
let img_location = data.Location;
upload_to_db(img_location); // 이미지가 업로드되면 그 위치를 DB에 저장
alert("Successfully uploaded photo.");
if (callback) callback(); // 콜백 함수 호출
},
function(err) {
console.log(err);
alert("There was an error uploading your photo: " + err.message);
}
);
}
- promise: 이미지를 S3에 업로드하는 비동기 작업을 처리. 업로드가 성공하면 data.Location에 이미지의 URL이 담기고, 이를 upload_to_db 함수로 전달하여 DynamoDB에 저장한다.
- 콜백 함수는 성공 시 호출되며, 업로드 완료 후 게시물 등록 등 후속 작업을 처리할 수 있다.
게시물 업로드 구현 흐름 (article_add.html)
게시물을 업로드하는 페이지다. 아래와 같은 페이지가 보일 것이다.
구현과정에 대해 자세히 알아보자.
function submitToAPI(e) {
e.preventDefault();
add_article_with_photo('images', function() {
window.location.href = 'article_view.html'; // 등록 후 게시물 목록 페이지로 이동
});
}
- submitToAPI(e): 게시물 등록 버튼을 누르면 호출된다. add_article_with_photo 함수를 사용해 S3에 이미지를 업로드한 후, 글을 등록하고 article_view.html로 이동한다.
- add_article_with_photo(albumName, callback): 파일 업로드 후 S3에 저장된 이미지 링크를 가져오고, upload_to_db(img_location)를 통해 DynamoDB에 게시물 정보를 저장한다.
게시물 보기 구현 흐름 (article_detail.html)
보고 싶은 게시물을 눌러보면 아래와 같이 게시물 내용을 확인할 수 있다.
이제 구현 흐름에 대해 알아보자.
getArticleDetail 함수는 현재 페이지의 URL에서 게시물 ID를 추출하고, 해당 ID를 이용해 게시물의 상세 정보를 서버에서 가져와 화면에 표시하는 기능을 한다.
function getArticleDetail() {
// 1. URL에서 쿼리 파라미터 추출
const params = new URLSearchParams(window.location.search);
const article_id = params.get('id');
// 2. 게시물 ID가 존재할 경우에만 API 호출
if (article_id) {
// 3. 서버에서 GET 요청으로 게시물 정보 가져오기
fetch(URL + '?article_id=' + article_id, {
method: "GET",
headers: {
'Accept': 'application/json'
}
})
.then(resp => resp.json()) // 서버 응답을 JSON으로 변환
.then(function(data) {
let article = data.Item; // 응답 데이터에서 게시물 정보 추출
if (!article) {
alert('게시글을 찾을 수 없습니다.'); // 게시물 정보가 없을 경우 알림
return;
}
// 4. 가져온 게시물 정보를 HTML 요소에 표시
document.getElementById('article-title').textContent = article.title;
document.getElementById('article-content').textContent = article.content;
document.getElementById('article-image').src = article.img_source;
})
.catch(err => console.log(err)); // 오류 발생 시 콘솔에 로그
} else {
alert('Invalid article ID'); // 게시물 ID가 없는 경우 알림
}
}
- window.location.search는 URL에서 쿼리 문자열을 가져오고, 이를 통해 게시물 ID를 추출
- article_id가 존재하는지 확인한 후, 서버에 GET 요청을 보낸다.
- 서버에서 응답을 받은 후 JSON 형태로 변환한다. 게시물이 존재하지 않으면 사용자에게 알림을 보여준다.
- 정상적으로 데이터를 받아오면, HTML 요소(article-title, article-content, article-image)에 데이터를 출력한다.
deleteArticle 함수는 게시물을 삭제하는 기능을 수행한다. 게시물을 작성한 사용자만 삭제할 수 있으며, 삭제 요청을 서버로 전송한다.
function deleteArticle() {
// 1. URL에서 게시물 ID 추출
const params = new URLSearchParams(window.location.search);
const article_id = params.get('id');
// 2. 게시물 ID가 유효하지 않으면 알림 표시
if (!article_id) return alert("게시물 ID가 유효하지 않습니다.");
// 3. 서버에서 해당 게시물 정보 가져오기
fetch(URL + '?article_id=' + article_id)
.then(resp => resp.json()) // 응답을 JSON으로 변환
.then(function(data) {
const article = data.Item;
if (!article) {
alert('게시글을 찾을 수 없습니다.'); // 게시물이 없을 경우 알림
return;
}
// 4. 로컬 스토리지에서 현재 로그인한 사용자의 이름 가져오기
const currentUser = localStorage.getItem('username');
// 5. 작성자와 현재 사용자가 동일한지 확인
if (article.author === currentUser) {
// 6. DELETE 요청 보내기
fetch(URL, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
TableName: "simple_board",
Key: { "article_id": article_id }
})
})
.then(resp => {
if (resp.ok) {
alert('게시물이 삭제되었습니다.'); // 삭제 성공 알림
window.location.href = 'article_view.html'; // 목록 페이지로 이동
} else {
resp.json().then(data => alert('삭제 실패: ' + data.message)); // 실패 시 메시지 표시
}
})
.catch(err => console.log('Error:', err)); // 오류 처리
} else {
alert('본인의 게시물만 삭제할 수 있습니다.'); // 작성자가 아닌 경우 알림
}
})
.catch(err => console.log(err)); // 오류 처리
}
게시물 목록 구현 흐름 (article_view.html)
게시물 목록에 대한 페이지를 보면 글 제목, 작성자, 작성시간이 보인다.
getArticles()함수는 DynamoDB에서 게시물 목록을 가져다주는 역할을 한다.
function getArticles() {
fetch(URL, {
method: "GET",
headers: {
'Accept': 'application/json'
}
})
.then(resp => resp.json())
.then(function(data) {
article_arr = data.Items;
// 최신 게시물부터 정렬
article_arr.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
displayArticles(article_arr); // 게시물 목록을 화면에 표시
})
.catch(err => console.log(err));
}
- article_arr.sort(): 서버에서 받아온 게시물을 timestamp(게시물이 작성된 시간)를 기준으로 내림차순으로 정렬하여 최신 게시물이 먼저 보이게 한다.
- displayArticles(article_arr): 정렬된 게시물을 화면에 보여주기 위해 displayArticles 함수가 호출된다.
displayArticles 함수는 게시물을 표시해주는 역할을 한다.
function displayArticles(articles) {
const articlesList = document.getElementById('articles'); // 게시물 리스트가 들어갈 영역
articlesList.innerHTML = ''; // 기존 게시물 목록을 초기화
articles.forEach(function(article) {
let li = document.createElement('li'); // <li> 태그 생성
li.innerHTML = `
<h3>${article.title}</h3>
<p>작성자: ${article.author}</p>
<p>작성 시간: ${new Date(article.timestamp).toLocaleString()}</p>
`;
li.onclick = function() {
viewArticleDetail(article.article_id); // 게시물을 클릭하면 상세 페이지로 이동
};
articlesList.appendChild(li); // 리스트에 항목 추가
});
}
- document.createElement('li'): 각 게시물을 리스트 항목(<li>)으로 만든다.
- li.onclick = function(): 리스트 항목을 클릭하면 해당 게시물의 상세 페이지로 이동한다. 이동할 때 article_id를 URL에 포함시켜 게시물의 상세 정보를 보여준다.
function filterArticles() {
const searchInput = document.getElementById('search-input').value.toLowerCase();
const filteredArticles = article_arr.filter(article =>
article.title.toLowerCase().includes(searchInput) ||
article.content.toLowerCase().includes(searchInput)
);
displayArticles(filteredArticles);
}
- 검색창의 입력값을 가져오기: searchInput에 사용자가 입력한 검색어를 소문자로 변환해 저장한다.
- 필터링: article_arr에서 게시물의 제목 또는 내용을 검색어와 비교해서 포함된 항목만 골라낸다. 그 후 displayArticles(filteredArticles)로 필터링된 결과를 화면에 다시 표시한다.
소스코드를 이해하고 코드를 저장했다면, AWS 콘솔 창으로 들어가 백엔드 파트를 구현해주자.
2편 게시글을 참고하길 바란다.
https://vegetableworld.tistory.com/235
'Projects' 카테고리의 다른 글
[Toy Project] Github Actions를 활용한 CI/CD 자동화 배포 파이프라인 구축(EC2) (0) | 2024.10.09 |
---|---|
[AWS Final Project] CloudFront를 사용한 CDN 구축 / ACM, WAF를 활용한 웹서버의 보안 강화 (2) | 2024.09.12 |
[AWS Final Project] S3와 DynamoDB를 활용하여 Serverless 게시판 만들기 - 2편 (Cognito, S3, API Gateway, Lambda, DynamoDB 세팅) (0) | 2024.09.10 |
[AWS Final Project] Amazon Cognito를 활용하여 로그인 기능 구현하기 (2) | 2024.09.08 |
[AWS Final Project] 개요 (0) | 2024.09.06 |