Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ 18팀 윤성용 ] 프레임워크 없이 SPA 만들기 #14

Closed
wants to merge 11 commits into from
5 changes: 3 additions & 2 deletions src/__tests__/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ describe('기본과제 테스트', () => {

loginForm.dispatchEvent(new SubmitEvent('submit', { bubbles: true, cancelable: true }));

expect(localStorage.getItem('user')).toEqual(`{"name":"testuser","email":"","bio":""}`);
expect(localStorage.getItem('user')).toEqual(`{"username":"testuser","email":"","bio":""}`);


const logoutButton = document.getElementById('logout');
logoutButton.click();
Expand Down Expand Up @@ -102,7 +103,7 @@ describe('기본과제 테스트', () => {
bioInput.value = 'Updated bio';
profileForm.dispatchEvent(new SubmitEvent('submit', { bubbles: true, cancelable: true }));

expect(localStorage.getItem('user')).toEqual(`{"name":"testuser","email":"","bio":"Updated bio","username":"testuser"}`);
expect(localStorage.getItem('user')).toEqual(`{"username":"testuser","email":"","bio":"Updated bio"}`);
});
});

Expand Down
9 changes: 9 additions & 0 deletions src/components/Footer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const Footer = () => {
return `
<footer class="bg-gray-200 p-4 text-center">
<p>&copy; 2024 항해플러스. All rights reserved.</p>
</footer>
`
}

export default Footer
26 changes: 26 additions & 0 deletions src/components/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {Store} from "../utils/store.js";

const Header = () => {
const store = new Store()
const {isLogin} = store.getState()
const activeClass = (path) => location.pathname === path ? 'text-blue-600 font-bold' : 'text-gray-600';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

액티브 클래스라는 함수를 만들어서 로그인/비로그인시에 헤더 UI를 구분하는 것을 깔끔하게 구현하신 것 같아요👍🏻👍🏻


return `
<header class="bg-blue-600 text-white p-4 sticky top-0">
<h1 class="text-2xl font-bold">항해플러스</h1>
</header>
<nav id="header-nav" class="bg-white shadow-md p-2 sticky top-14">
<ul class="flex justify-around">
${isLogin ? `
<li><a href="/" class="${activeClass('/')}">홈</a></li>
<li><a href="/profile" class="${activeClass('/profile')}">프로필</a></li>
<li><a href="/login" id="logout" class="text-gray-600" id="logout">로그아웃</a></li>` :
`<li><a href="/" class="${activeClass('/')}">홈</a></li>
<li><a href="/login" class="text-gray-600">로그인</a></li>`}
</ul>
</nav>
`
}

export default Header

23 changes: 23 additions & 0 deletions src/events/addLogoutEvent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {Store} from "../utils/store.js";
import {Logger} from "../utils/logger.js";
import {Storage} from "../utils/storage.js";

export const addLogoutEvent = () => {
const store = new Store()
const logger = new Logger()
const storage = new Storage()
Comment on lines +6 to +8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다 싱글톤으로 작성해놔서 이렇게 가져다 사용하는군요 ㅎㅎ
다르게 생각하면..
불필요한 의존을 많이 가져가는 것 같아요.
그냥 함수를 통해서 필요한 로직만 가져와서 실행해주면 어떨까 싶기도 하고..

const logoutBtn = document.getElementById('logout');

if (logoutBtn) {
logoutBtn.addEventListener('click', (e) => {
storage.removeData('isLogin')
storage.removeData('user')
store.setState({ isLogin: false });
logger.log({
type : 'event',
location : 'addLogoutEvent',
message : 'success logout event'
})
});
}
};
112 changes: 2 additions & 110 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -1,111 +1,3 @@
document.querySelector('#root').innerHTML = `
<div class="bg-gray-100 min-h-screen flex justify-center">
<div class="max-w-md w-full">
<header class="bg-blue-600 text-white p-4 sticky top-0">
<h1 class="text-2xl font-bold">항해플러스</h1>
</header>
import App from "./pages/App.js";

<nav class="bg-white shadow-md p-2 sticky top-14">
<ul class="flex justify-around">
<li><a href="./main.html" class="text-blue-600">홈</a></li>
<li><a href="./profile.html" class="text-gray-600">프로필</a></li>
<li><a href="#" class="text-gray-600">로그아웃</a></li>
</ul>
</nav>

<main class="p-4">
<div class="mb-4 bg-white rounded-lg shadow p-4">
<textarea class="w-full p-2 border rounded" placeholder="무슨 생각을 하고 계신가요?"></textarea>
<button class="mt-2 bg-blue-600 text-white px-4 py-2 rounded">게시</button>
</div>

<div class="space-y-4">

<div class="bg-white rounded-lg shadow p-4">
<div class="flex items-center mb-2">
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2">
<div>
<p class="font-bold">홍길동</p>
<p class="text-sm text-gray-500">5분 전</p>
</div>
</div>
<p>오늘 날씨가 정말 좋네요. 다들 좋은 하루 보내세요!</p>
<div class="mt-2 flex justify-between text-gray-500">
<button>좋아요</button>
<button>댓글</button>
<button>공유</button>
</div>
</div>

<div class="bg-white rounded-lg shadow p-4">
<div class="flex items-center mb-2">
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2">
<div>
<p class="font-bold">김철수</p>
<p class="text-sm text-gray-500">15분 전</p>
</div>
</div>
<p>새로운 프로젝트를 시작했어요. 열심히 코딩 중입니다!</p>
<div class="mt-2 flex justify-between text-gray-500">
<button>좋아요</button>
<button>댓글</button>
<button>공유</button>
</div>
</div>

<div class="bg-white rounded-lg shadow p-4">
<div class="flex items-center mb-2">
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2">
<div>
<p class="font-bold">이영희</p>
<p class="text-sm text-gray-500">30분 전</p>
</div>
</div>
<p>오늘 점심 메뉴 추천 받습니다. 뭐가 좋을까요?</p>
<div class="mt-2 flex justify-between text-gray-500">
<button>좋아요</button>
<button>댓글</button>
<button>공유</button>
</div>
</div>

<div class="bg-white rounded-lg shadow p-4">
<div class="flex items-center mb-2">
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2">
<div>
<p class="font-bold">박민수</p>
<p class="text-sm text-gray-500">1시간 전</p>
</div>
</div>
<p>주말에 등산 가실 분 계신가요? 함께 가요!</p>
<div class="mt-2 flex justify-between text-gray-500">
<button>좋아요</button>
<button>댓글</button>
<button>공유</button>
</div>
</div>

<div class="bg-white rounded-lg shadow p-4">
<div class="flex items-center mb-2">
<img src="https://via.placeholder.com/40" alt="프로필" class="rounded-full mr-2">
<div>
<p class="font-bold">정수연</p>
<p class="text-sm text-gray-500">2시간 전</p>
</div>
</div>
<p>새로 나온 영화 재미있대요. 같이 보러 갈 사람?</p>
<div class="mt-2 flex justify-between text-gray-500">
<button>좋아요</button>
<button>댓글</button>
<button>공유</button>
</div>
</div>
</div>
</main>

<footer class="bg-gray-200 p-4 text-center">
<p>&copy; 2024 항해플러스. All rights reserved.</p>
</footer>
</div>
</div>
`;
App();
42 changes: 42 additions & 0 deletions src/pages/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Router from "../utils/router.js";
import {Store} from "../utils/store.js";
import {$, setTpl} from "../utils/dom.js";
import {Logger} from "../utils/logger.js";

const errorMessageTpl = (errorMessage) => {
return `
<main class="min-h-screen flex justify-center">
<div class="flex flex-col items-center justify-center">
<p class="text-red-600">${errorMessage}</p>
<p>오류 발생!</p>
<p>의도적인 오류입니다.</p>
</div>
</main>
`
}

const App = () => {
const logger = new Logger()

window.addEventListener('error', (event) => {
const {error} = event

try{
const {errorMessage,errorLog} = JSON.parse(error.message)
setTpl(errorMessageTpl(errorMessage))($('#root'));
logger.log(errorLog)
}catch (e) {
setTpl(errorMessageTpl('예기치 못한 에러가 발생했습니다.'))($('#root'));
Comment on lines +25 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실행할 때 마다 querySelector로 root를 가져오게 되네요.
그냥 미리 변수로 담아서 사용하면 어떨까요?

logger.log({
type : 'error',
location : 'addGlobalErrorHandler',
message : 'error logs parse error'
})
}
})

new Router()
new Store()
}

export default App
8 changes: 8 additions & 0 deletions src/pages/dashboard/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {$, setTpl} from "../../utils/dom.js";
import {dashboardTpl} from "./templates.js";

const setDashboardOnDocument = () => {
setTpl(dashboardTpl())($('#root'));
}

export default setDashboardOnDocument
34 changes: 34 additions & 0 deletions src/pages/dashboard/templates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {Logger} from "../../utils/logger.js";

export const dashboardTpl = () => {
const logger = new Logger()
const loggerList = logger.getLogs() || []


return `
<main class="w-full flex justify-center p-6">
<table class="table-auto border-collapse w-full max-w-4xl">
<thead class="bg-gray-100">
<tr>
<th class="border px-4 py-2 text-left">시간</th>
<th class="border px-4 py-2 text-left">이벤트 타입</th>
<th class="border px-4 py-2 text-left">발생 위치</th>
<th class="border px-4 py-2 text-left">내용</th>
</tr>
</thead>
<tbody>
${loggerList.map((items) => {
return `
<tr class="bg-white border-b">
<td class="border px-4 py-2">${items.timestamp}</td>
<td class="border px-4 py-2">${items.type}</td>
<td class="border px-4 py-2">${items.location}</td>
<td class="border px-4 py-2">${items.message}</td>
</tr>
Comment on lines +3 to +27

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

템플릿 대시보드 형태의 함수를 만들어서 UI 구조를 가져가시는게 최고네요,, 성용님의 전용 로거들을 사용하기에 매우 좋은 환경으로 갖춰진 코드같습니다 👍🏻👍🏻

`;
}).join('')}
</tbody>
</table>
</main>
`;
}
16 changes: 16 additions & 0 deletions src/pages/error/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {$, setTpl} from "../../utils/dom.js";
import {errorTpl} from "./templates.js";
import {Logger} from "../../utils/logger.js";

const setErrorOnDocument = () => {
const logger = new Logger()
logger.log({
type : 'error',
location : 'errorPage',
message : 'not find page error'
})

setTpl(errorTpl())($('#root'));
}

export default setErrorOnDocument
17 changes: 17 additions & 0 deletions src/pages/error/templates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const errorTpl = () => {
return `
<main class="bg-gray-100 flex items-center justify-center min-h-screen">
<div class="bg-white p-8 rounded-lg shadow-md w-full text-center" style="max-width: 480px">
<h1 class="text-2xl font-bold text-blue-600 mb-4">항해플러스</h1>
<p class="text-4xl font-bold text-gray-800 mb-4">404</p>
<p class="text-xl text-gray-600 mb-8">페이지를 찾을 수 없습니다</p>
<p class="text-gray-600 mb-8">
요청하신 페이지가 존재하지 않거나 이동되었을 수 있습니다.
</p>
<a href="/" class="bg-blue-600 text-white px-4 py-2 rounded font-bold">
홈으로 돌아가기
</a>
</div>
</main>
`
}
10 changes: 10 additions & 0 deletions src/pages/home/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {$, setTpl} from "../../utils/dom.js";
import {homeTpl} from "./templates.js";
import {addLogoutEvent} from "../../events/addLogoutEvent.js";

const setHomeOnDocument = () => {
setTpl(homeTpl())($('#root'));
addLogoutEvent()
}

export default setHomeOnDocument
Loading
Loading