Skip to content

Commit

Permalink
✨ 토스트 메시지 보여주기 (#8)
Browse files Browse the repository at this point in the history
* 🐛 api 응답값이 없는 경우 처리
- default value 설정

* 📦 webpack-dev-server 삭제
- webpack-hot-middleware가 대신함.
- hmr entry point에 accept 추가 -> module 업데이트 수락 (사용하지
  않으면 업데이트가 되지 않는다.)

* 🐛 helmet 설정 변경
- helmet 기본값 COEP로 페이지 보호를 위해 신뢰하는외부소스 콘텐츠만
  허용한다.
- 따라서 import한 css를 불러오지 못함 -> false로 설정

* 📦 connect-history-api-fallback 설치 및 적용
- webpack-dev-middleware 사용시 historyApiFallback 적용을 위해 사용

* 💄 Not Found Page 스타일 적용

* ✨ toast message 추가 및 적용
- display: hidden -> visible로 변경, 3초 뒤 원상태로 설정
  • Loading branch information
padosum committed Jan 10, 2023
1 parent 49e00e1 commit 40b4c1d
Show file tree
Hide file tree
Showing 15 changed files with 167 additions and 857 deletions.
793 changes: 4 additions & 789 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"build": "NODE_ENV=production webpack --config webpack.prod.js",
"start": "NODE_ENV=production npm run build && node ./src/server/bin/www",
"start": "npm run build && NODE_ENV=production node ./src/server/bin/www",
"dev": "NODE_ENV=development nodemon ./src/server/bin/www"
},
"repository": {
Expand All @@ -23,6 +23,7 @@
"@babel/cli": "^7.19.3",
"@babel/core": "^7.20.5",
"@babel/preset-env": "^7.20.2",
"connect-history-api-fallback": "^2.0.0",
"core-js": "^3.26.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
Expand All @@ -43,7 +44,6 @@
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-middleware": "^6.0.1",
"webpack-dev-server": "^4.11.1",
"webpack-hot-middleware": "^2.25.3"
}
}
6 changes: 4 additions & 2 deletions src/client/App.js
@@ -1,5 +1,3 @@
import.meta.webpackHot.accept();

import './styles/reset.css';
import './styles/style.css';
import './styles/global.css';
Expand Down Expand Up @@ -65,3 +63,7 @@ function App($app) {
}

new App(document.querySelector('#app'));

if (import.meta.webpackHot) {
import.meta.webpackHot.accept();
}
3 changes: 2 additions & 1 deletion src/client/components/Sidebar.js
@@ -1,4 +1,5 @@
import { CATEGORY_ICON } from '../constants';
import { showToastMessage } from '../utils';

export default class Siebar {
constructor({ container, initState }) {
Expand Down Expand Up @@ -84,7 +85,7 @@ export default class Siebar {
if (categoryHeader) {
categoryHeader.scrollIntoView({ behavior: 'smooth' });
} else {
alert('해당 카테고리 항목이 없습니다!');
showToastMessage('해당 카테고리 항목이 없습니다!');
}
});
}
Expand Down
30 changes: 15 additions & 15 deletions src/client/components/achievements/Dashboard.js
Expand Up @@ -12,21 +12,21 @@ export default class AchievementsDashboard {

render() {
const {
thisYear,
thisYearTrending,
gapThisYear,
lastYear,
lastYearTrending,
gapLastYear,
yearBeforeLast,
yearBeforeLastTrending,
gapYearBeforeLast,
lastThreeYears,
lastThreeYearsTrending,
gapLastThreeYears,
incompleted,
total,
completed,
thisYear = 0,
thisYearTrending = 0,
gapThisYear = 0,
lastYear = 0,
lastYearTrending = 0,
gapLastYear = 0,
yearBeforeLast = 0,
yearBeforeLastTrending = 0,
gapYearBeforeLast = 0,
lastThreeYears = 0,
lastThreeYearsTrending = 0,
gapLastThreeYears = 0,
incompleted = 0,
total = 0,
completed = 0,
} = this.state;

this.target.innerHTML = `
Expand Down
4 changes: 3 additions & 1 deletion src/client/index.html
Expand Up @@ -9,6 +9,8 @@
<link href="https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css" rel="stylesheet" />
</head>
<body>
<div id="app"></div>
<div id="app">
<div class="toast"></div>
</div>
</body>
</html>
84 changes: 84 additions & 0 deletions src/client/styles/global.css
Expand Up @@ -900,3 +900,87 @@ h2.category-title {
color: var(--decrease-color);
font-weight: 700;
}

.toast {
visibility: hidden;
min-width: 20rem;
position: fixed;
bottom: 3rem;
font-size: 1.6rem;
background-color: var(--bg-color);
box-shadow: 0.6rem 0.6rem 0 var(--primary-color);
border: 1px solid var(--primary-color);
z-index: 6;
justify-self: center;
padding: 1.6rem;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-color);
}

.toast.show {
visibility: visible;
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
}

@-webkit-keyframes fadein {
from {
bottom: 0;
opacity: 0;
}
to {
bottom: 3rem;
opacity: 1;
}
}

@keyframes fadein {
from {
bottom: 0;
opacity: 0;
}
to {
bottom: 3rem;
opacity: 1;
}
}

@-webkit-keyframes fadeout {
from {
bottom: 3rem;
opacity: 1;
}
to {
bottom: 0;
opacity: 0;
}
}

@keyframes fadeout {
from {
bottom: 3rem;
opacity: 1;
}
to {
bottom: 0;
opacity: 0;
}
}

.not-found {
display: flex;
align-items: center;
justify-content: center;
grid-area: content;
font-size: 2rem;
height: calc(var(--header-height) + var(--sub-header-height));
}

.not-found-box {
padding: 1.6rem;
background-color: var(--bg-color);
border: 1px solid var(--primary-color);
box-shadow: 0.6rem 0.6rem 0 var(--primary-color);
}
4 changes: 2 additions & 2 deletions src/client/utils/http.js
Expand Up @@ -18,9 +18,9 @@ const request = async params => {
const parseResponse = async response => {
const { status, ok } = response;

let data;
let data = {};
if (ok) {
data = await response.json();
data = (await response.json()) || {};
}

return {
Expand Down
23 changes: 19 additions & 4 deletions src/client/utils/index.js
@@ -1,10 +1,25 @@
const removeContent = () => {
const container = document.querySelector('#app');
[...container.children].forEach(layoutTag => {
if (layoutTag.tagName !== 'HEADER') {
layoutTag.remove();
[...container.children].forEach(child => {
if (!(child.tagName === 'HEADER' || child.classList.contains('toast'))) {
child.remove();
}
});
};

export { removeContent };
const showToastMessage = message => {
const toast = document.querySelector('.toast');

if (toast.classList.contains('show')) {
return;
}

toast.innerHTML = message;
toast.classList.add('show');

setTimeout(() => {
toast.classList.toggle('show');
}, 3000);
};

export { removeContent, showToastMessage };
6 changes: 3 additions & 3 deletions src/client/views/HomeView.js
Expand Up @@ -14,9 +14,9 @@ export default class HomeView {
}

async getData() {
const { data: categories } = await Api.get(`/api/categories`);
const { data: achievements } = await Api.get(`/api/achievements`);
const { data: trending } = await Api.get(`/api/stats/trending`);
const { data: categories = [] } = await Api.get(`/api/categories`);
const { data: achievements = [] } = await Api.get(`/api/achievements`);
const { data: trending = {} } = await Api.get(`/api/stats/trending`);

this.setData({
categories,
Expand Down
7 changes: 6 additions & 1 deletion src/client/views/NotFoundView.js
Expand Up @@ -5,6 +5,11 @@ export default class NotFoundView {
}

render() {
this.container.innerHTML = '404 Not Found';
this.container.innerHTML = `
<div class="not-found">
<div class="not-found-box">
페이지를 찾을 수 없습니다.
</div>
</div>`;
}
}
50 changes: 21 additions & 29 deletions src/server/app.js
Expand Up @@ -2,54 +2,46 @@ import express from 'express';
import logger from 'morgan';
import helmet from 'helmet';
import errorHandler from './middlewares/errorHandler.js';
import path from 'path';

import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import apiRouter from './routes/api.js';

import webpackConfig from '../../webpack.dev.js';
import history from 'connect-history-api-fallback';

const app = express();
import apiRouter from './routes/api.js';

const invalidPathHandler = (request, response, next) => {
response.status(404);
response.send('invalid path');
};
const app = express();

app.use(helmet());
app.use(
helmet({
crossOriginEmbedderPolicy: false,
}),
);

app.use(logger('common'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.use('/api', apiRouter);

process.env.NODE_ENV = process.env.NODE_ENV || 'development';
if (process.env.NODE_ENV === 'production') {
app.use(express.static('dist'));

if (process.env.NODE_ENV !== 'production') {
webpackConfig.entry.main.unshift('webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000');
const compiler = webpack(webpackConfig);
const __dirname = path.resolve();
app.get('*', function (request, response) {
response.sendFile(path.join(__dirname, '/dist/index.html'));
});
} else {
app.use(history());

app.use(
webpackDevMiddleware(compiler, {
publicPath: webpackConfig.output.publicPath,
}),
);

app.use(
webpackHotMiddleware(compiler, {
log: console.log,
path: '/__webpack_hmr',
heartbeat: 10 * 1000,
}),
);
}
const compiler = webpack(webpackConfig);

if (process.env.NODE_ENV === 'production') {
app.use(express.static('dist'));
app.use(webpackDevMiddleware(compiler, {}));
app.use(webpackHotMiddleware(compiler, {}));
}

app.use(errorHandler);
app.use(invalidPathHandler);

export default app;
3 changes: 0 additions & 3 deletions webpack.config.js
Expand Up @@ -3,9 +3,6 @@ import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';

export default {
entry: {
main: ['./src/client/App.js'],
},
output: {
path: path.resolve('./dist'),
filename: '[name].js',
Expand Down
6 changes: 1 addition & 5 deletions webpack.dev.js
@@ -1,10 +1,9 @@
import { merge } from 'webpack-merge';
import webpackConfig from './webpack.config.js';
import path from 'path';
const __dirname = path.resolve();

export default merge(webpackConfig, {
mode: 'development',
entry: ['webpack-hot-middleware/client?timeout=20000', './src/client/App.js'],
devtool: 'cheap-module-source-map',
devServer: {
proxy: {
Expand All @@ -14,9 +13,6 @@ export default merge(webpackConfig, {
changeOrigin: true,
},
},
static: {
directory: path.join(__dirname, './'),
},
historyApiFallback: true,
},
});
1 change: 1 addition & 0 deletions webpack.prod.js
Expand Up @@ -3,4 +3,5 @@ import webpackConfig from './webpack.config.js';

export default merge(webpackConfig, {
mode: 'production',
entry: ['./src/client/App.js'],
});

0 comments on commit 40b4c1d

Please sign in to comment.