728x90

Click! 👉 https://dasolpark.github.io/Dasol_JS_Calculator/

 

Calculator

 

dasolpark.github.io

Github 👉 https://github.com/DasolPark/Dasol_JS_Calculator

 

DasolPark/Dasol_JS_Calculator

Calculator with Vanilla JavaScript. Contribute to DasolPark/Dasol_JS_Calculator development by creating an account on GitHub.

github.com

 Vanilla JavaScript로 개발한 🧮'계산기' 입니다.

eval을 이용하여 효율적으로 개발할 수 있었으며, 여러가지 변수는 고려하지 않고 단순하게 만들어 보았습니다😊

Main 화면
실행 화면
결과 화면

728x90

Click! 👉https://dasolpark.github.io/Dasol_ReactJS_Movie_App/#/

 

Movie App

 

dasolpark.github.io

Github 👉https://github.com/DasolPark/Dasol_ReactJS_Movie_App

 

DasolPark/Dasol_ReactJS_Movie_App

ReactJS with Movie App. Contribute to DasolPark/Dasol_ReactJS_Movie_App development by creating an account on GitHub.

github.com

🎞 가장 높은 평점을 받은 영화와 최근 개봉한 영화 리스트를 알려주는 Movie App입니다.

✨ state 관리, event 관리, API를 이용해 데이터 받아오기는 물론이고,
react-router-dom을 이용하여 HashRouter, Route, Link를 연습하기에 좋은 프로젝트입니다.

가장 상단에 위치한 컴포넌트 영화 제목, 장르, 개봉 연도, 평점, 러닝타임, 요약 등 자세한 영화 정보를 안내합니다.
그리고 이 컴포넌트를 클릭하면 해당 영화 데이터와 함께 Detail 페이지로 이동하여 더욱 더 자세한 영화 정보를 안내합니다.

TOP RATED 페이지에서는 평점 순위대로 영화 목록이 출력되며,
LATEST 페이지에서는 가장 최근에 개봉한 영화를 출력해줍니다.

웹과 모바일에서 모두 이용 가능하도록 디자인하였습니다.

Top Rated Page
Latest Page
Detail Page
Full Page

728x90

Click! 👉 https://dasolpark.github.io/Dasol_React_Video_Search_With_YouTube_API/

 

Video Search With YouTube API

 

dasolpark.github.io

Github 👉 https://github.com/DasolPark/Dasol_React_Video_Search_With_YouTube_API

 

DasolPark/Dasol_React_Video_Search_With_YouTube_API

ReactJS with YouTube API. Contribute to DasolPark/Dasol_React_Video_Search_With_YouTube_API development by creating an account on GitHub.

github.com

🔍 YouTube API를 이용해 YouTube 영상을 검색하는 간단한 웹 앱 사이드 프로젝트입니다.

✨ Functional? or Class? Component, Communicating Child to Parent,  Deeply Nested Callbacks, Event,
axios를 이용하여 API 다루기 등 개념을 익히기에 좋습니다.
앞서 만들었던 Unsplash 프로젝트와 중복되는 개념이 많아 React 기초를 확고히 다지기 좋은 프로젝트입니다.

영상 리스트는 최대 30개까지 출력되며,
grid를 활용하여 모바일에서도 문제 없이 사용 가능하도록 디자인하였습니다.

랜딩 페이지
영상 리스트
전체 페이지

 

728x90

Click! 👉 https://dasolpark.github.io/Dasol_React_Free_Image_Search_With_Unsplash/

 

Free Image Search with Unsplash

 

dasolpark.github.io

Github 👉https://github.com/DasolPark/Dasol_React_Free_Image_Search_With_Unsplash

 

DasolPark/Dasol_React_Free_Image_Search_With_Unsplash

ReactJS with Unsplash API. Contribute to DasolPark/Dasol_React_Free_Image_Search_With_Unsplash development by creating an account on GitHub.

github.com

🔍Unsplash API를 이용해 무료 이미지를 검색하는 간단한 웹 앱 사이드 프로젝트입니다.

✨ components 구성하기, state 다루기, 부모와 자식 component 간 데이터를 주고 받기, event 다루기, 
React.createRef()를 이용하여 DOM 접근하기, axios를 이용하여 API 다루기 같은 개념을 익히기에 좋습니다.

이미지 결과는 최대 30개까지 출력하며, 각 이미지 크기에 맞게 grid 크기를 적용하였습니다.

 

랜딩 페이지
검색 후 출력된 이미지 일부 페이지
검색 후 출력된 이미지 전체 페이지

728x90
728x90

Click! 👉 https://dasolpark.github.io/Dasol_React_Current_Weather_With_Open_Weather_Map_API/

 

Current Weather With Open Weather Map API

 

dasolpark.github.io

Github 👉https://github.com/DasolPark/Dasol_React_Current_Weather_With_Open_Weather_Map_API

 

DasolPark/Dasol_React_Current_Weather_With_Open_Weather_Map_API

ReactJS with Open Weather Map API. Contribute to DasolPark/Dasol_React_Current_Weather_With_Open_Weather_Map_API development by creating an account on GitHub.

github.com

Navigator를 이용해 latitude, longitude를 받고,
해당 위치를 기반으로 Open Weather Map API를 이용해 날씨 정보를 받는 간단한 사이드 프로젝트입니다.

봄, 여름, 가을, 겨울 모두 다른 디자인을 적용하였고,
현재위치, 현재 날씨, 체감 온도, 최저 기온, 최고 기온, 현재 습도 정보를 제공합니다.

위치를 제공을 요청하는 페이지
봄 페이지
여름 페이지
가을 페이지
겨울 페이지

728x90

✨ DB를 이용해 게시판을 만들기 전에, 간단히 연습할 수 있는 방법입니다.

Chrome Browser - F12(or Inspect) - Application - Storage - Local Storage
위의 경로를 찾아 들어가면, 내 브라우저에만 저장되는 공간이 있다. 이걸 이용해 간단하게 게시판을 구현할 수 있다.

📢 Local Storage 사용법

1. 데이터 저장하기
localStorage.setItem( 명칭, 저장할 내용 );

2. 데이터 불러오기
localStorage.getItem( 명칭 )

글은 여러 개이며 하나의 글에 필요한 데이터번호, 제목, 내용, 작성자, 작성일, 조회이기 때문에
하나의 글을 저장할 때는 배열로 저장하고, 하나의 글에 들어간 데이터는 Object로 저장해주면 간단히 저장할 수 있다.
예를 들어, objArr = [ { num: 1, title: 'title1'... }, { num: 2, title: 'title2'... }, ... ] 형식으로 저장하면 된다.

📢 게시판을 만들어가는 작업 순서

1. input과 form tag를 이용해 글을 작성하는 부분을 만들어주고, 이 내용을 받아 localStorage에 저장해준다.
2. localStorage에서 데이터를 받아와, DOM을 이용해 목록을 출력해준다.
3. 글 제목을 누르면, 글 내용이 출력되도록 해준다.
4. 한 화면에는 최대 5개의 글만 보여주며, 그 이상의 글은 인덱스 처리를 해주고 눌렀을 때 해당 목록을 보여준다.

😃 보기 쉽게 하기 위해서, 싱글 페이지글 목록, 글 내용, 작성 부분을 한 번에 보여주도록 구성하였다.
이 과정에서 글 번호가 곧 데이터 index이므로 꼬이지 않도록 Sort해주는 작업이 필요하며,
각 글 목록마다 id를 다르게 부여하여, event를 받아줄 수 있도록 해주는 작업이 필요하다.

아래는 예제가 실제로 실행되는 모습이다.

페이지 실행 첫 번째 화면
글 제목을 눌렀을 때, 내용을 보여주는 화면
2번째 글 목록 화면
3번째 글 목록 화면
글 작성하는 부분
위에서 작성한 글 내용 보기

이렇게 연습을 하고, DB와 함께 진행할 때는 어떻게 적용하고 개발을 진행해 나가야 할지 고민하면 될 듯 하다.

궁금한 점이 있으시면 언제든 댓글 달아주세요!

Full Code (https://github.com/DasolPark/Play-Ground_JavaScript) board.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Board with Local Storage</title>
<link
href="https://fonts.googleapis.com/css?family=Jua&display=swap"
rel="stylesheet"
/>
<style>
* {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-family: 'Jua', sans-serif;
}
body {
padding: 0px;
margin: 0px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 95vh;
background-color: #f6f9fc;
}
 
.main__container {
height: 700px;
padding-top: 10px;
padding-bottom: 10px;
}
 
.board__container,
.contents__container,
.editor__container {
width: 700px;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
background-color: #ffffff;
}
 
.board__container {
margin: 10px 0px 0px 0px;
height: 220px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.1);
position: relative;
}
 
.contents__container {
margin: 10px 0px;
height: 220px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.1);
}
 
.editor__container {
margin: 10px 0px;
height: 220px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.1);
}
 
/* Board Lists */
.board {
width: 600px;
text-align: center;
margin-bottom: 15px;
}
 
.board__index-container {
position: absolute;
bottom: 10px;
}
 
.board__index-link {
all: unset;
margin-right: 5px;
}
 
.board__index-link:hover {
cursor: pointer;
color: blue;
}
 
/* Contents */
.contents__titles {
width: 600px;
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10px;
border-bottom: 2px solid rgba(0, 0, 0, 0.08);
margin-bottom: 20px;
}
 
.contents__column {
width: 200px;
display: flex;
justify-content: space-around;
align-items: center;
}
 
.contents__content {
width: 600px;
display: flex;
justify-content: start;
align-items: center;
}
 
/* Editor */
.editor__form {
width: 600px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
 
input {
all: unset;
border-radius: 5px;
border: 1px solid rgba(0, 0, 0, 0.5);
}
 
.editor__title-input,
.editor__content-input {
width: 100%;
padding: 5px 10px;
margin-bottom: 10px;
}
 
.editor__content-input {
height: 100px;
}
 
.editor__btn {
all: unset;
border: 1px solid rgba(0, 0, 0, 0.5);
border-radius: 5px;
padding: 5px 10px;
}
 
button:active {
transform: scale(0.93);
}
</style>
</head>
<body>
<main class="main__container">
<div class="board__container">
<table class="board">
<thead class="board__table-head">
<tr class="board__titles">
<th class="board__column">번호</th>
<th class="board__column">제목</th>
<th class="board__column">작성자</th>
<th class="board__column">작성일</th>
<th class="board__column">조회</th>
</tr>
</thead>
<tbody class="board__contents" id="board-body"></tbody>
</table>
<div class="board__index-container" id="index-container"></div>
</div>
<div class="contents__container"></div>
<div class="editor__container">
<form action="#" class="editor__form" id="editor-form">
<input
type="text"
class="editor__title-input"
id="editor-title-input"
placeholder="제목"
/>
<input
type="text"
class="editor__content-input"
id="editor-content-input"
placeholder="내용"
/>
<button class="editor__btn" id="editor-submit-btn">작성 완료</button>
</form>
</div>
</main>
<script>
const boardTableBody = document.querySelector('#board-body');
 
const contentsContainer = document.querySelector('.contents__container');
 
const editorForm = document.querySelector('#editor-form');
const titleInput = document.querySelector('#editor-title-input');
const contentInput = document.querySelector('#editor-content-input');
 
const BOARDLIST_LS = 'boardLists';
const boardListsObj = [];
let nums = 0;
let author = 'David';
let date = new Date();
let views = Math.floor(Math.random() * 99) + 1;
 
function onTitleClick(e) {
contentsContainer.textContent = '';
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
const index = e.target.parentNode.id.replace(/[a-z|-]/gi, '');
 
const contentsTitles = document.createElement('div');
contentsTitles.classList.add('contents__titles');
 
const contentsColumnFirst = document.createElement('div');
contentsColumnFirst.classList.add('contents__column');
 
const contentsTitle = document.createElement('div');
contentsTitle.classList.add('contents__title');
contentsTitle.textContent = lists[index].title;
 
// contents__titles > column >author, date, views
const contentsColumnSecond = document.createElement('div');
contentsColumnSecond.classList.add('contents__column');
 
const contentsAuthor = document.createElement('div');
contentsAuthor.classList.add('contents__author');
contentsAuthor.textContent = lists[index].author;
 
const contentsDate = document.createElement('div');
contentsDate.classList.add('contents__date');
contentsDate.textContent = lists[index].date;
 
const contentsViews = document.createElement('div');
contentsViews.classList.add('contents__views');
contentsViews.textContent = lists[index].views;
 
const contentsContent = document.createElement('div');
contentsContent.classList.add('contents__content');
contentsContent.textContent = lists[index].content;
 
contentsColumnFirst.appendChild(contentsTitle);
 
contentsColumnSecond.appendChild(contentsAuthor);
contentsColumnSecond.appendChild(contentsDate);
contentsColumnSecond.appendChild(contentsViews);
 
contentsTitles.appendChild(contentsColumnFirst);
contentsTitles.appendChild(contentsColumnSecond);
 
contentsContainer.appendChild(contentsTitles);
contentsContainer.appendChild(contentsContent);
}
 
function assignIndex() {
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
if (!lists) {
nums = 0;
} else {
nums = parseInt(lists[lists.length - 1].num) + 1;
}
}
 
function onEditorFormSubmit(e) {
e.preventDefault();
 
const title = titleInput.value;
const content = contentInput.value;
 
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
if (!lists) {
const objArr = [];
objArr.push({
num: `${nums++}`,
title: `${title}`,
author: `${author}`,
date: `${date.getFullYear()}.${date.getMonth() +
1}.${date.getDate()}.`,
views: `${views++}`,
content: `${content}`
});
 
localStorage.setItem(BOARDLIST_LS, JSON.stringify(objArr));
} else {
lists.push({
num: `${nums++}`,
title: `${title}`,
author: `${author}`,
date: `${date.getFullYear()}.${date.getMonth() +
1}.${date.getDate()}.`,
views: `${views++}`,
content: `${content}`
});
 
localStorage.setItem(BOARDLIST_LS, JSON.stringify(lists));
}
 
titleInput.value = '';
contentInput.value = '';
titleInput.focus();
window.location.reload();
}
 
function showBoardLists() {
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
 
lists.forEach((list, index) => {
if (index < 5) {
const tr = document.createElement('tr');
tr.classList.add('board__content');
 
const tdNum = document.createElement('td');
tdNum.classList.add('board__content-column');
tdNum.textContent = list.num;
 
const aTitle = document.createElement('a');
aTitle.href = '#';
aTitle.id = `link-to-content${index}`;
 
const tdTitle = document.createElement('td');
tdTitle.classList.add('board__content-column');
tdTitle.textContent = list.title;
 
aTitle.appendChild(tdTitle);
 
const tdAuthor = document.createElement('td');
tdAuthor.classList.add('board__content-column');
tdAuthor.textContent = list.author;
 
const tdDate = document.createElement('td');
tdDate.classList.add('board__content-column');
tdDate.textContent = list.date;
 
const tdViews = document.createElement('td');
tdViews.classList.add('board__content-column');
tdViews.textContent = list.views;
 
tr.appendChild(tdNum);
tr.appendChild(aTitle);
tr.appendChild(tdAuthor);
tr.appendChild(tdDate);
tr.appendChild(tdViews);
 
boardTableBody.appendChild(tr);
const linkToContent = document.querySelector(
`#link-to-content${index}`
);
linkToContent.addEventListener('click', onTitleClick);
} else if (index === 5) {
const boardIndexMax = Math.ceil(lists.length / 5);
for (let i = 0; i < boardIndexMax; i++) {
const indexContainer = document.querySelector('#index-container');
 
const aIndex = document.createElement('a');
aIndex.classList.add('board__index-link');
 
const spanIndexText = document.createElement('span');
spanIndexText.classList.add('board__index');
spanIndexText.textContent = i + 1;
 
aIndex.appendChild(spanIndexText);
indexContainer.appendChild(aIndex);
 
aIndex.addEventListener('click', () => {
showBoardListsNewPage(i);
});
}
}
});
}
 
function showBoardListsNewPage(pageIndex) {
boardTableBody.textContent = '';
const lists = JSON.parse(localStorage.getItem(BOARDLIST_LS));
const limitPage = pageIndex * 5;
 
lists.forEach((list, index) => {
if (index >= limitPage && index < limitPage + 5) {
const tr = document.createElement('tr');
tr.classList.add('board__content');
 
const tdNum = document.createElement('td');
tdNum.classList.add('board__content-column');
tdNum.textContent = list.num;
 
const aTitle = document.createElement('a');
aTitle.href = '#';
aTitle.id = `link-to-content${index}`;
 
const tdTitle = document.createElement('td');
tdTitle.classList.add('board__content-column');
tdTitle.textContent = list.title;
 
aTitle.appendChild(tdTitle);
 
const tdAuthor = document.createElement('td');
tdAuthor.classList.add('board__content-column');
tdAuthor.textContent = list.author;
 
const tdDate = document.createElement('td');
tdDate.classList.add('board__content-column');
tdDate.textContent = list.date;
 
const tdViews = document.createElement('td');
tdViews.classList.add('board__content-column');
tdViews.textContent = list.views;
 
tr.appendChild(tdNum);
tr.appendChild(aTitle);
tr.appendChild(tdAuthor);
tr.appendChild(tdDate);
tr.appendChild(tdViews);
 
boardTableBody.appendChild(tr);
const linkToContent = document.querySelector(
`#link-to-content${index}`
);
linkToContent.addEventListener('click', onTitleClick);
}
});
}
 
editorForm.addEventListener('submit', onEditorFormSubmit);
 
if (boardTableBody) {
assignIndex();
showBoardLists();
}
</script>
</body>
</html>
728x90

Click! 👉 https://www.youtube.com/watch?v=wSSwgFFH5v8&feature=youtu.be

Github: https://github.com/DasolPark/AriTeam_FullStack_Chatbot_Aribot

대학 생활 편의를 위한 챗봇웹앱졸업 작품으로 개발하였습니다.

👓사용한 기술
Front-End: JavaScript, HTML/CSS, Bootstrap
Back-End: Node.js
Database: MySQL, MongoDB
Cloud Server: AWS EC2
Chatbot Platform: danbee.Ai

📳챗봇: 학사 규칙, 빈 강의실 조회/예약, 부서 위치/전화번호 안내, 학교 식당 메뉴 안내, 편의 시설 위치 안내 등
💻웹앱: 빈 강의실 조회/예약, 개설 강좌 안내, 챗봇 소개, 데이터 관리자 페이지 등

클라우드와_인공지능을_이용한_대학교_학사행정지원_챗봇과_웹앱.pdf
1.83MB

728x90

Click! 👉 https://dasolpark.github.io/Dasol_JS_Momentum_Clone/

 

Welcome to Momentum Clone

 

dasolpark.github.io

Github 👉 https://github.com/DasolPark/Dasol_JS_Momentum_Clone

 

DasolPark/Dasol_JS_Momentum_Clone

Let's make momentum clone with Vanilla JS. Contribute to DasolPark/Dasol_JS_Momentum_Clone development by creating an account on GitHub.

github.com

항상 이용하고 있는 Chrome Extension Momentum
HTML/CSS/JavaScript
를 사용하여 나만의 스타일로 재구성 해봤습니다.

🌳배경 이미지는 무작위로 생성되며, 😃본인의 이름을 저장할 수 있고, ⛅간단한 날씨 정보도 얻을 수 있습니다.
또한, 📑해야 할 일 목록을 작성하여 체크하거나 삭제하며 사용할 수 있습니다.

날씨 APIopenweathermap, 배경 이미지 APIunsplash를 이용하였으며
이름과 해야 할 일 데이터Local Storage에 저장하였습니다.

브라우저 앱이기 때문에, 노트북이나 데스크톱 브라우저에 최적화되어 있습니다.

첫 번째 화면,이름을 묻는 페이지

 

두 번째 화면, 사용자를 반겨주며 오늘 가장 중요한 일을 작성할 수 있습니다.
세 번째 화면, 사용자를 반겨주며 작성된 오늘 가장 중요한 일을 확인할 수 있습니다.
네 번째 화면, 오늘 가장 중요한 일을 완료 표시 할 수 있습니다.
다섯 번째 화면, 해야 할 일 목록을 작성할 수 있습니다.
여섯 번째 화면, 해야 할 일 목록을 완료 표시 또는 삭제할 수 있습니다.

728x90

📳

Click! 👉 https://dasolpark.github.io/Dasol_CSS_WhatsApp_Clone/

 

Chats

Archived Chats 0 Broadcast Lists New Group

dasolpark.github.io

Github👉https://github.com/DasolPark/Dasol_CSS_WhatsApp_Clone

 

DasolPark/Dasol_CSS_WhatsApp_Clone

Let's make WhatsApp Design Clone with Responsive CSS - DasolPark/Dasol_CSS_WhatsApp_Clone

github.com

해외에서 많이 사용하는 메신저앱 WhatsApp의 디자인 패턴HTML CSS를 이용해 복제해 보았습니다.

📳모바일이나 💻데스크톱 등 어떤 브라우저에서도 디자인이 무너지지 않도록, 반응형 CSS를 적용하였으며,
JavaScript를 이용해 현재 시간 띄우기, 전화 목록 지우기, 채팅 메세지 등록을 간단히 구현해 보았습니다.

해당 프로젝트는 width:375, height:638 크기에 최적화되어 있습니다.

좌측 Status, 우측 Calls Page
좌측 Calls - Edit 기능, 우측 Calls - New Call Page
좌측 Chats, 우측 Chats - Chat Page
Settings Page

+ Recent posts