-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
124 lines (76 loc) · 232 KB
/
index.html
File metadata and controls
124 lines (76 loc) · 232 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<!DOCTYPE html><html><head><meta charSet="utf-8" class="next-head"/><title class="next-head">Dev.log</title><meta name="description" content="salgum1114 Dev.log" class="next-head"/><meta name="keywords" content="salgum1114,blog,react,antd,webpack,css,javascript" class="next-head"/><meta property="og:title" content="Dev.log" class="next-head"/><meta property="og:description" content="salgum1114 Dev.log" class="next-head"/><meta property="og:type" content="website" class="next-head"/><meta property="og:site_name" content="Dev.log" class="next-head"/><meta property="og:locale" content="ko_KR" class="next-head"/><meta property="og:url" content="https://salgum1114.github.io" class="next-head"/><meta property="og:image" content="https://salgum1114.github.io/static/images/authors/salgum1114.png" class="next-head"/><meta name="twitter:title" content="Dev.log" class="next-head"/><meta name="twitter:image" content="https://salgum1114.github.io/static/images/authors/salgum1114.png" class="next-head"/><link rel="preload" href="/_next/static/WBzuTPjOfilMhrb159LWu/pages/index.js" as="script"/><link rel="preload" href="/_next/static/WBzuTPjOfilMhrb159LWu/pages/_app.js" as="script"/><link rel="preload" href="/_next/static/WBzuTPjOfilMhrb159LWu/pages/_error.js" as="script"/><link rel="preload" href="/_next/static/runtime/webpack-484a8928da4ec3492fcd.js" as="script"/><link rel="preload" href="/_next/static/chunks/commons.14799f0a166b7233d4ca.js" as="script"/><link rel="preload" href="/_next/static/chunks/styles.a8a2d4bb615b8dc9d9ef.js" as="script"/><link rel="preload" href="/_next/static/runtime/main-0463b6c82337fbcda1e9.js" as="script"/><link rel="stylesheet" href="/_next/static/css/commons.8ba03f37.chunk.css"/><link rel="stylesheet" href="/_next/static/css/styles.2d00b941.chunk.css"/><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no"/><meta name="google-site-verification" content="YCCU8qpDKf7ka8WDDPA6rt1y0m9egFSi7zeHgmayb6Y"/><meta name="description" content="salgum1114 Dev.log"/><meta name="keywords" content="salgum1114,blog,react,antd,webpack,css,javascript"/><meta property="og:title" content="Dev.log"/><meta property="og:description" content="salgum1114 Dev.log"/><meta property="og:type" content="website"/><meta property="og:site_name" content="Dev.log"/><meta property="og:locale" content="ko_KR"/><meta property="og:url" content="https://salgum1114.github.io"/><meta property="og:image" content="https://salgum1114.github.io/static/images/authors/salgum1114.png"/><meta name="twitter:title" content="Dev.log"/><meta name="twitter:image" content="https://salgum1114.github.io/static/images/authors/salgum1114.png"/><link rel="manifest" href="/static/manifest.json"/><link rel="icon" type="image/png" sizes="192x192" href="/static/images/favicon/icon-192x192.png"/><link rel="icon" type="image/png" sizes="32x32" href="/static/images/favicon/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/static/images/favicon/favicon-96x96.png"/><link rel="shortcut icon" href="/static/favicon.ico"/><link rel="stylesheet" href="https://fonts.googleapis.com/earlyaccess/notosanskr.css"/><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.min.css"/><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/styles/railscasts.min.css"/><script async="" src="https://www.googletagmanager.com/gtag/js?id=UA-97485289-2"></script><script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-97485289-2');
</script><script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script></head><body><div id="__next"><section class="ant-layout" style="height:100%"><header class="ant-layout-header" style="background-color:#fff;padding:0;box-shadow:0px 0px 10px -2px rgba(0, 0, 0, 0.75);z-index:1000;position:fixed;width:100%"><div style="display:flex;align-items:center;height:100%"><div style="display:flex;justify-content:flex-start;margin-left:16px;cursor:pointer"><i aria-label="icon: bars" style="font-size:1.25rem" tabindex="-1" class="anticon anticon-bars"><svg viewBox="0 0 1024 1024" class="" data-icon="bars" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false"><path d="M912 192H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM104 228a56 56 0 1 0 112 0 56 56 0 1 0-112 0zm0 284a56 56 0 1 0 112 0 56 56 0 1 0-112 0zm0 284a56 56 0 1 0 112 0 56 56 0 1 0-112 0z"></path></svg></i></div><div style="display:flex;justify-content:center;flex:1;color:#000;font-weight:500;overflow:hidden"><h2 class="container-title"></h2><a href="https://github.com/salgum1114" target="_blank"><i aria-label="icon: github" style="font-size:1.25rem" class="anticon anticon-github"><svg viewBox="64 64 896 896" class="" data-icon="github" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false"><path d="M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9a127.5 127.5 0 0 1 38.1 91v112.5c.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z"></path></svg></i></a></div><div style="display:flex;justify-content:flex-end;align-items:center;margin-right:16px"><i aria-label="icon: search" style="font-size:1.25rem" tabindex="-1" class="anticon anticon-search"><svg viewBox="64 64 896 896" class="" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false"><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></div></div></header><main class="ant-layout-content" style="margin-top:64px;overflow:auto"><div class="container"><div class=""><div class="blog-masonry-sizer"></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/reactjs/2019-11-28-react-class-equivalents/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/react.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">React Class vs React Hooks Lifecycle 비교 정리</h2></div><div class="ant-card-meta-description">4시간 후</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">React Class에서 사용하는 Lifecycle과 React Hooks에서 사용하는 Lifecycle의 차이점을 비교 정리한다.
COMPONENTDIDMOUNT
// Class
class Example extends React.Component {
componentDidMount() {
...
}
}
// Hook
con...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/etc/2019-11-14-letsencrypt-public-ca/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/letsencrypt.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">Letsencrypt로 공인 CA 적용하기</h2></div><div class="ant-card-meta-description">14일 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">1. LETSENCRYPT 클라이언트 다운받기
다운로드 받고자하는 경로에서 다음 명령어를 실행한다.
git clone https://github.com/letsencrypt/letsencrypt
2. 의존성 다운로드 받기
다운로드 받은 후 letsencrypt 디렉토리에서 다음 명령어를 실행한다.
./letsencrypt-auto --help
3...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/reactjs/2019-10-30-react-hot-loader-patch-warning/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/react.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">React Hot Loader Patch Warning 해결 방법</h2></div><div class="ant-card-meta-description">한 달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">최근 React 버전을 최신으로 올리면서 브라우저 개발자 콘솔에서 불필요한 React Hot Loader Warning 로그가 찍힌다.
나와 같은 문제를 가지고 다양한 사람들이 React Hot Loader 이슈에 올렸고, 나는 그 이슈에 답변들을 보고 해결에 나섰다.
React-Hot-Loader: react-🔥-dom patch is not d...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/etc/2019-10-22-react-design-editor-1/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/react-design-editor.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">[React Design Editor] 차트 추가</h2></div><div class="ant-card-meta-description">한 달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">이번에 React Design Editor 프로젝트에 차트를 추가할 수 있도록 업데이트 했다.
차트는 Canvas로 개발된 Baidu의 Echarts를 사용하였다.
최초는 Chart.js를 사용하여 Canvas의 Context로 차트를 Fabric.js 내로 직접 렌더링 하려 했으나 Chart.js의
레이아웃 기능 제약에 막혀 Echarts를 선택하게 ...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/nextjs/2019-05-28-nextjs-static-website-5/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/nextjs.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">Next.js로 정적 웹사이트 만들기 - 5. Material UI 적용하기</h2></div><div class="ant-card-meta-description">6달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">이번에는 프로젝트에 Material UI를 적용하여 레이아웃을 만들어 보고 몇가지 컴포넌트를 사용하여 레이아웃을 만들어보려고 한다.
먼저 Metarial UI를 사용하기 위해 npm package를 받는다.
npm install --save @material-ui/core @material-ui/icons @material-ui/styles
Next...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/nextjs/2019-05-24-nextjs-static-website-4/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/nextjs.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">Next.js로 정적 웹사이트 만들기 - 4. Routing 사용하기</h2></div><div class="ant-card-meta-description">6달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">지난 글에서 index.js와 post.js를 생성했었고 Routing을 사용하지 않고 URL로 직접 접근해서 화면을 확인했었다. 이번 글에서는
Next.js의 Routing을 사용하여 페이지 전환을 해보려고한다.
> Next.js github에 Routing에 대한 설명이 잘 나와있다.
Next.js에서 Route는 pages디렉토리 경로에 작성한...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/nextjs/2019-05-21-nextjs-static-website-3/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/nextjs.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">Next.js로 정적 웹사이트 만들기 - 3. 레이아웃</h2></div><div class="ant-card-meta-description">6달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">이전 글에서 프로젝트 구성을 하여 간단한 화면을 확인했었다.
이번에는 Next.js에 내장되어 있는 _document.js, _app.js, _error.js를 커스터마이징하여 레이아웃을 새롭게 구성하는
방법을 설명하려고 한다.
> 각각에 대한 설명은 Next.js 구조에서 확인할 수 있다.
_DOCUMENT.JS 파일 생성
_document.js...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/nextjs/2019-05-20-nextjs-static-website-2/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/nextjs.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">Next.js로 정적 웹사이트 만들기 - 2. 프로젝트 구성</h2></div><div class="ant-card-meta-description">6달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">사실 Next.js는 설명할게 별로 없다. 예제들이 너무 잘되어 있기 때문에.. 그렇지만 내 머릿속에서 사라져가기 때문에 기록이라도 남기려고
작성한다.
1. NPM 프로젝트 생성
프로젝트를 구성하기 위해 static-website 디렉토리를 하나 만들고 cd static-website로 가서 npm init으로 npm
프로젝트를 만들자.
> 참고로 n...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/etc/2019-05-20-using-openssl-in-windows/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/openssl.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">Windows에서 openssl로 인증서 생성하고 MQTT로 테스트하기</h2></div><div class="ant-card-meta-description">6달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">회사에서 MQTT를 SSL/TLS로 테스트해야 하는 경우가 생겼는데, 기존에 나와있는 Eclipse Mosquitto로 하려다가 이왕 하는 김에
Javascrpt로 MQTT Broker랑 Client (Pub, Sub)를 직접 구현해보기로 했다.
전체적으로 MQTT Broker와 Client를 구현하기 위한 라이브러리들은 다음과 같다.
* Mosca ...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/reactjs/2019-05-11-using-typescript-without-tsx/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/react_typescript.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">React에서 .tsx 없이 Typescript 사용하기</h2></div><div class="ant-card-meta-description">7달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">이번 글에서는 기존에 .js, .jsx 확장자로 작성된 React 컴포넌트에서 Typescript를 적용하여 컴포넌트 개발이나 사용 시에 타입을
확인할 수 있는 방법에 대해서 설명하려고 한다.
Button.js 컴포넌트로 간단한 예를 들어본다.
src/
components/
button/
index.js
...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/nextjs/2019-05-06-nextjs-static-website-1/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/nextjs.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">Next.js로 정적 웹사이트 만들기 - 1. Next.js 구조</h2></div><div class="ant-card-meta-description">7달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">Next.js는 현재 가장 유명한 React용 서버 사이드 렌더링 프레임워크이다.
프레임워크로써 이해하기 쉬운 구조와 수많은 예제들, 그리고 다양한 플러그인들을 지원하고 있어 많은 사람들에게 사랑받고 있다.
또한, Next.js는 next export라는 명령어로 routing 경로로 하여금 정적 웹사이트를 만들 수 있는 기능도 제공하고 있다.
> ...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/reactjs/2019-05-03-medium-zoom/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/medium-zoom.gif"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">medium-zoom 적용하기</h2></div><div class="ant-card-meta-description">7달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">평소 Medium에서 개발 포스팅을 찾아보는 편인데, 글에서 이미지를 클릭했을 때, 줌이 되는 기능이 이뻐보여서 내 개발 블로그에도 적용하고
싶어 비슷한 라이브러리들이 있나 찾아보게 되었다.
우선 이 블로그가 React로 개발했으니 React에서 사용할 수 있는 라이브러리를 찾아보니 react-medium-image-zoom이
있었다. 하지만 React로...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/css/2019-04-30-overscroll-behavior-contain/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/css.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">overscroll-behavior: contain 속성 사용하기</h2></div><div class="ant-card-meta-description">7달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">웹에서 스크롤을 다루다보면 항상 마주치는 문제 중 하나가 스크롤 체이닝이다.
다음 예제를 보자.
See the Pen overscroll-behavior: cotain before by Sung Gyun Oh (@salgum1114) on
CodePen.위 예제에서 Box-1의 스크롤이 끝에 도달하게 되면 상위 요소인 App의 스크롤이 시작이된다.
...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/css/2019-04-28-scroll-behavior-smooth/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/css.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">scroll-behavior: smooth 속성 사용하기</h2></div><div class="ant-card-meta-description">7달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">블로그에 부드러운 스크롤링을 위해서 CSS의 scroll-behavior를 활용해본다.
우선, scroll-behavior이 뭔지 알아보자.
> w3school: CSS scroll-behavior Property에 아주 잘 나와있다.
사용 방법은 아주 간단하다. 아래 코드를 확인해보자.
SCROLL-BEHAVIOR: SMOOTH 적용 전
See...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/reactjs/2019-04-25-back-top-component/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/react.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">BackTop 컴포넌트 만들기</h2></div><div class="ant-card-meta-description">7달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">블로그 개발 중 스크롤이 되었을 때, 상위로 이동시키는 BackTop을 적용하기 위해 Ant.Design의 BackTop 컴포넌트를 사용하려고
했다.
하지만 Ant.Design의 BackTop 컴포넌트를 적용해보니 잘 안된다. 따라서 직접 BackTop 컴포넌트를 구현하기로 결정했다.
> Ant.Design 홈페이지에선 잘 동작한다.
구현 코드는 ...</div></div></div></div><div class="container-col blog-masonry-item col-3"><div class="ant-card ant-card-bordered ant-card-hoverable"><div class="ant-card-cover"><a style="width:100%;padding-top:56.25%;position:relative;display:block" href="/webpack/2019-04-22-uglifyjs-to-terser/"><img style="-o-object-fit:cover;object-fit:cover;display:block;border-top-left-radius:4px;border-top-right-radius:4px;position:absolute;top:0;left:0;width:100%;height:100%" alt="Post cover" src="/static/images/covers/webpack.png"/></a></div><div class="ant-card-body" style="height:12rem"><div class="ant-card-meta"><div class="ant-card-meta-avatar"><span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="/static/images/authors/salgum1114.png"/></span></div><div class="ant-card-meta-detail"><div class="ant-card-meta-title"><h2 class="ant-card-meta-title">UglifyJsPlugin const 오류 해결 방법</h2></div><div class="ant-card-meta-description">7달 전</div></div></div><div style="overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;word-wrap:break-word;margin-top:1.5rem">최근 React Design Editor에 빌드 실패 관련 이슈가 올라왔다.
내용인 즉슨, production으로 빌드시 UglifyJsPlugin에서 Unexpected token: keyword «const»라는
오류가 발생하여 계속해서 빌드에 실패한다는 것이다.
[Bug] Not able to create build for the applicati...</div></div></div></div></div><div style="margin:16px"><div style="max-height:250px;max-width:1200px;margin:auto"><ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-8569372752842198" data-ad-slot="5204006558" data-ad-format="auto" data-full-width-responsive="true"></ins></div></div><button type="button" class="ant-btn blog-backtop ant-btn-primary ant-btn-circle"><i aria-label="icon: to-top" style="font-size:1.25rem" class="anticon anticon-to-top"><svg viewBox="64 64 896 896" class="" data-icon="to-top" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false"><path d="M885 780H165c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h720c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zM400 325.7h73.9V664c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V325.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 171a8 8 0 0 0-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13z"></path></svg></i></button></div></main><div><div class="blog-dialog blog-dialog-right blog-dialog-hide" style="width:0"><div class="blog-dialog-title" style="background-color:#fff;width:100%"><div class="blog-dialog-title-extra"></div><div class="blog-dialog-title-close"><i aria-label="icon: close" tabindex="-1" class="anticon anticon-close"><svg viewBox="64 64 896 896" class="" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg></i></div></div><div class="blog-dialog-content" style="padding:0"><div class="blog-search" style="display:flex;flex-direction:column;height:100%"><div style="margin:16px"><span class="ant-input-search ant-input-affix-wrapper"><input type="text" value="" class="ant-input"/><span class="ant-input-suffix"><i aria-label="icon: search" tabindex="-1" class="anticon anticon-search ant-input-search-icon"><svg viewBox="64 64 896 896" class="" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false"><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></span></span></div><div style="margin:16px"><h4>최근 검색</h4><div style="display:flex;justify-content:center;padding:8px;color:#999999">검색 기록이 없습니다</div></div><div style="margin:16px"><h4>최근 기록</h4><div style="display:flex;justify-content:center;padding:8px;color:#999999">읽은 기록이 없습니다</div></div><div style="margin:16px;flex:1"><h4>전체 태그</h4><div style="margin:8px;cursor:pointer" class="ant-tag">react (11)</div><div style="margin:8px;cursor:pointer" class="ant-tag">react-hooks (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">letsencrypt (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">openssl (2)</div><div style="margin:8px;cursor:pointer" class="ant-tag">webpack (2)</div><div style="margin:8px;cursor:pointer" class="ant-tag">react-hot-loader (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">antd (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">fabricjs (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">react-design-editor (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">nextjs (5)</div><div style="margin:8px;cursor:pointer" class="ant-tag">website (5)</div><div style="margin:8px;cursor:pointer" class="ant-tag">typescript (2)</div><div style="margin:8px;cursor:pointer" class="ant-tag">mqtt (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">medium-zoom (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">css (2)</div><div style="margin:8px;cursor:pointer" class="ant-tag">overscroll-behavior (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">scroll-behavior (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">component (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">backtop (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">uglifyjs (1)</div><div style="margin:8px;cursor:pointer" class="ant-tag">terser-webpack-plugin (1)</div></div></div></div><div class="blog-dialog-footer" style="justify-content:center"><a href="https://github.com/salgum1114" target="_blank"><i aria-label="icon: github" style="font-size:1.25rem" class="anticon anticon-github"><svg viewBox="64 64 896 896" class="" data-icon="github" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false"><path d="M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9a127.5 127.5 0 0 1 38.1 91v112.5c.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z"></path></svg></i></a></div></div></div></section></div><script>__NEXT_DATA__ = {"props":{"pageProps":{"posts":{"/reactjs/2019-11-28-react-class-equivalents":{"path":"/reactjs/2019-11-28-react-class-equivalents","content":"\u003cp\u003eReact Class에서 사용하는 Lifecycle과 React Hooks에서 사용하는 Lifecycle의 차이점을 비교 정리한다.\u003c/p\u003e\n\u003ch2 id=\"componentdidmount\"\u003eComponentDidMount\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs jsx language-jsx\"\u003e\u003cspan class=\"hljs-comment\"\u003e// Class\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eExample\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eReact\u003c/span\u003e.\u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n componentDidMount() {\n ...\n }\n}\n\n\u003cspan class=\"hljs-comment\"\u003e// Hook\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e Example = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n useEffect(\u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n ...\n }, []);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"componentwillunmount\"\u003eComponentWillUnmount\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs jsx language-jsx\"\u003e\u003cspan class=\"hljs-comment\"\u003e// Class\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eExample\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eReact\u003c/span\u003e.\u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n componentWillUnmount() {\n ...\n }\n}\n\n\u003cspan class=\"hljs-comment\"\u003e// Hook\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e Example = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n useEffect(\u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n ...\n };\n }, []);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"componentwillreceiveprops\"\u003eComponentWillReceiveProps\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs jsx language-jsx\"\u003e\u003cspan class=\"hljs-comment\"\u003e// Class\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eExample\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eReact\u003c/span\u003e.\u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n componentWillReceiveProps(nextProps) {\n ...\n }\n or\n UNSAFE_componentWillReceiveProps(nextProps) {\n ...\n }\n}\n\n\u003cspan class=\"hljs-comment\"\u003e// Hook\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e Example = \u003cspan class=\"hljs-function\"\u003e(\u003cspan class=\"hljs-params\"\u003eprops\u003c/span\u003e) =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { value } = props;\n useEffect(\u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n ...\n }, [value]);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"shouldcomponentupdate\"\u003eShouldComponentUpdate\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs jsx language-jsx\"\u003e\u003cspan class=\"hljs-comment\"\u003e// Class\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eExample\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eReact\u003c/span\u003e.\u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n shouldComponentUpdate(nextProps) {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e nextProps.value !== \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props.value;\n }\n}\n\n\u003cspan class=\"hljs-comment\"\u003e// Hook\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e Example = React.memo(\u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n ...\n}, (prevProps, nextProps) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e nextProps.value === prevProps.value;\n})\n\u003c/code\u003e\u003c/pre\u003e\n\u003cblockquote\u003e\n \u003cp\u003e\u003cstrong\u003e주의\u003c/strong\u003e\u003c/p\u003e\n \u003cp\u003eclass 컴포넌트의 shouldComponentUpdate() 메서드와 달리, areEqual 함수는 props들이 서로 같으면 true를 반환하고, props들이 서로 다르면 false를 반환합니다. 이것은 shouldComponentUpdate와 정반대의 동작입니다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"componentdidupdate\"\u003eComponentDidUpdate\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs jsx language-jsx\"\u003e\u003cspan class=\"hljs-comment\"\u003e// Class\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eExample\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eReact\u003c/span\u003e.\u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n componentDidUpdate(prevProps, prevState) {\n ...\n }\n}\n\n\u003cspan class=\"hljs-comment\"\u003e// Hook\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e Example = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n useEffect(\u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n ...\n });\n}\n\u003c/code\u003e\u003c/pre\u003e","preview":"React Class에서 사용하는 Lifecycle과 React Hooks에서 사용하는 Lifecycle의 차이점을 비교 정리한다.\n\nCOMPONENTDIDMOUNT\n// Class\nclass Example extends React.Component {\n componentDidMount() {\n ...\n }\n}\n\n// Hook\ncon...","title":"React Class vs React Hooks Lifecycle 비교 정리","author":"salgum1114","date":"2019-11-28 20:30","tags":"react, react-hooks","cover":"/static/images/covers/react.png","next":"/etc/2019-11-14-letsencrypt-public-ca","prev":null},"/etc/2019-11-14-letsencrypt-public-ca":{"path":"/etc/2019-11-14-letsencrypt-public-ca","content":"\u003ch2 id=\"1letsencrypt\"\u003e1. Letsencrypt 클라이언트 다운받기\u003c/h2\u003e\n\u003cp\u003e다운로드 받고자하는 경로에서 다음 명령어를 실행한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003egit \u003cspan class=\"hljs-keyword\"\u003eclone\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003ehttps\u003c/span\u003e://github.com/letsencrypt/letsencrypt\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"2\"\u003e2. 의존성 다운로드 받기\u003c/h2\u003e\n\u003cp\u003e다운로드 받은 후 \u003ccode\u003eletsencrypt\u003c/code\u003e 디렉토리에서 다음 명령어를 실행한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003e\u003cspan class=\"hljs-string\"\u003e./letsencrypt-auto\u003c/span\u003e \u003cspan class=\"hljs-params\"\u003e--help\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"3\"\u003e3. 인증서 발급받기\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eletsencrypt\u003c/code\u003e 디렉토리에서 다음 명령어를 실행한다.\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e주의: 반드시 80포트가 비어있는 상태에서 해야한다.\u003c/em\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003e./letsencrypt-auto certonly \u003cspan class=\"hljs-comment\"\u003e--standalone -n -m \u0026lt;your email\u0026gt; --agree-tos -d \u0026lt;your domain: www.example.com\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003e\u0026lt;your email\u0026gt;\u003c/code\u003e에 담당자 메일 주소를 작성한다.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003e\u0026lt;your domain\u0026gt;\u003c/code\u003e에 구매한 도메인 주소를 작성한다.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/letsencrypt_create_ca.png\" alt=\"letsencrypt_create_ca\" /\u003e\u003c/p\u003e\n\u003ch2 id=\"4pempkcs12\"\u003e4. pem -\u0026gt; pkcs12 변환\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eroot 계정으로 접근 \u003ccode\u003esudu su\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e그 다음 발급된 인증서 경로로 이동한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003ecd \u003cspan class=\"hljs-meta-keyword\"\u003e/etc/\u003c/span\u003eletsencrypt\u003cspan class=\"hljs-meta-keyword\"\u003e/live/\u003c/span\u003e\u003cspan class=\"hljs-params\"\u003e\u0026lt;your domain\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e그런 다음 아래 openssl 명령어를 실행한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003eopenssl pkcs12 -export -\u003cspan class=\"hljs-keyword\"\u003ein\u003c/span\u003e fullchain\u003cspan class=\"hljs-selector-class\"\u003e.pem\u003c/span\u003e -inkey privkey\u003cspan class=\"hljs-selector-class\"\u003e.pem\u003c/span\u003e -out cert_and_key\u003cspan class=\"hljs-selector-class\"\u003e.p12\u003c/span\u003e -name \u0026lt;project name\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003e\u0026lt;project name\u0026gt;\u003c/code\u003e에 프로젝트 이름을 작성한다.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e그럼 비밀번호를 물어보는데 비밀번호를 입력한다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/letsencrypt_create_cert_and_key.png\" alt=\"letsencrypt_create_cert_and_key\" /\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e비밀번호는 기억하고 있어야한다.\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"5pkcs12jks\"\u003e5. pkcs12 -\u0026gt; jks로 변환\u003c/h2\u003e\n\u003cp\u003ekeytool을 이용해 jks를 생성한다. 다음 명령어를 실행한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003ekeytool -importkeystore -srcstorepass \u0026lt;password\u0026gt; -destkeystore \u0026lt;project name\u0026gt;\u003cspan class=\"hljs-selector-class\"\u003e.jks\u003c/span\u003e -srckeystore cert_and_key\u003cspan class=\"hljs-selector-class\"\u003e.p12\u003c/span\u003e -srcstoretype PKCS12 -storepass \u0026lt;password\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003e\u0026lt;project name\u0026gt;\u003c/code\u003e에 프로젝트 이름을 작성한다.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ccode\u003e\u0026lt;password\u0026gt;\u003c/code\u003e 영역에 pem -\u003e pkcs12 변환했을 때, 입력한 비밀번호로 바꾼다.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/letsencrypt_create_jks.png\" alt=\"letsencrypt_create_jks\" /\u003e\u003c/p\u003e\n\u003cp\u003e여기까지 실행되었고 다음과 같이 목록이 보인다면 정상적으로 인증서 발급이 끝났다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/letsencrypt-list.png\" alt=\"letsencrypt-list\" /\u003e\u003c/p\u003e\n\u003ch2 id=\"6jks\"\u003e6. JKS 생성 경로\u003c/h2\u003e\n\u003cp\u003e생성된 JKS는 다음과 같은 경로에 생성되어 있다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003e\u003cspan class=\"hljs-meta-keyword\"\u003e/etc/\u003c/span\u003eletsencrypt\u003cspan class=\"hljs-meta-keyword\"\u003e/live/\u003c/span\u003e\u003cspan class=\"hljs-params\"\u003e\u0026lt;your domain\u0026gt;\u003c/span\u003e/\u003cspan class=\"hljs-params\"\u003e\u0026lt;project name\u0026gt;\u003c/span\u003e.jks\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e이 생성된 JKS를 가지고 Java Keystore에 등록하여 사용하면 된다.\u003c/p\u003e\n\u003ch2 id=\"7\"\u003e7. 프로젝트 재기동\u003c/h2\u003e\n\u003cp\u003e생성된 JKS를 Java Keystore에 등록한 후 프로젝트를 재기동하여 브라우저 URL 입력 왼쪽 부분을 확인하여 CA가 적용되었는지 확인한다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e정상\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/letsencrypt-success.png\" alt=\"letsencrypt-success\" /\u003e\u003c/p\u003e","preview":"1. LETSENCRYPT 클라이언트 다운받기\n다운로드 받고자하는 경로에서 다음 명령어를 실행한다.\n\ngit clone https://github.com/letsencrypt/letsencrypt\n\n\n2. 의존성 다운로드 받기\n다운로드 받은 후 letsencrypt 디렉토리에서 다음 명령어를 실행한다.\n\n./letsencrypt-auto --help\n\n\n3...","title":"Letsencrypt로 공인 CA 적용하기","author":"salgum1114","date":"2019-11-14 10:00","tags":"letsencrypt, openssl","cover":"/static/images/covers/letsencrypt.png","next":"/reactjs/2019-10-30-react-hot-loader-patch-warning","prev":"/reactjs/2019-11-28-react-class-equivalents"},"/reactjs/2019-10-30-react-hot-loader-patch-warning":{"path":"/reactjs/2019-10-30-react-hot-loader-patch-warning","content":"\u003cp\u003e최근 React 버전을 최신으로 올리면서 브라우저 개발자 콘솔에서 불필요한 React Hot Loader Warning 로그가 찍힌다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/reactjs/react-hot-loader-patch-warning.png\" alt=\"react-hot-loader-patch-warning\" /\u003e\u003c/p\u003e\n\u003cp\u003e나와 같은 문제를 가지고 다양한 사람들이 React Hot Loader 이슈에 올렸고, 나는 그 이슈에 답변들을 보고 해결에 나섰다.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/gatsbyjs/gatsby/issues/11934\"\u003eReact-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work. #11934\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003ewarning이 큰 문제는 아니고 React 버전에 따른 React Hot Loader 마이그레이션이라던지 Webpack 설정을 조금 바꿔 주면 되는 것이었다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e우선 새롭게 설치해야되는 Dependency로 \u003ccode\u003e@hot-loader/react-dom\u003c/code\u003e을 \u003ccode\u003enpm install --save-dev @hot-loader/react-dom\u003c/code\u003e로 설치해준다.\u003c/p\u003e\n\u003cp\u003e그런 다음 Webpack Rules 옵션에서 다음과 같은 Rule을 추가해준다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e{\n \u003cspan class=\"hljs-attr\"\u003etest\u003c/span\u003e: \u003cspan class=\"hljs-regexp\"\u003e/\\.(js|jsx|tsx|ts)?$/\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003einclude\u003c/span\u003e: \u003cspan class=\"hljs-regexp\"\u003e/node_modules/\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003euse\u003c/span\u003e: [\u003cspan class=\"hljs-string\"\u003e'react-hot-loader/webpack'\u003c/span\u003e],\n},\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e그런 다음 재기동 후 브라우저 개발자 콘솔을 확인해보면 더 이상 Warning 로그가 찍히지 않는다.\u003c/p\u003e","preview":"최근 React 버전을 최신으로 올리면서 브라우저 개발자 콘솔에서 불필요한 React Hot Loader Warning 로그가 찍힌다.\n\n\n\n나와 같은 문제를 가지고 다양한 사람들이 React Hot Loader 이슈에 올렸고, 나는 그 이슈에 답변들을 보고 해결에 나섰다.\n\nReact-Hot-Loader: react-🔥-dom patch is not d...","title":"React Hot Loader Patch Warning 해결 방법","author":"salgum1114","date":"2019-10-30 20:30","tags":"react, webpack, react-hot-loader","cover":"/static/images/covers/react.png","next":"/etc/2019-10-22-react-design-editor-1","prev":"/etc/2019-11-14-letsencrypt-public-ca"},"/etc/2019-10-22-react-design-editor-1":{"path":"/etc/2019-10-22-react-design-editor-1","content":"\u003cp\u003e이번에 React Design Editor 프로젝트에 차트를 추가할 수 있도록 업데이트 했다.\u003c/p\u003e\n\u003cp\u003e차트는 Canvas로 개발된 Baidu의 Echarts를 사용하였다.\u003c/p\u003e\n\u003cp\u003e최초는 Chart.js를 사용하여 Canvas의 Context로 차트를 Fabric.js 내로 직접 렌더링 하려 했으나 Chart.js의 레이아웃 기능 제약에 막혀 Echarts를 선택하게 됐다.\u003c/p\u003e\n\u003cp\u003eChart.js를 사용하려고 했던 배경은 다음과 같다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003e\u003cspan class=\"hljs-number\"\u003e1.\u003c/span\u003e Canvas의 Context로 차트를 렌더링한다.\n\u003cspan class=\"hljs-number\"\u003e2.\u003c/span\u003e \u003cspan class=\"hljs-module-access\"\u003e\u003cspan class=\"hljs-module\"\u003e\u003cspan class=\"hljs-identifier\"\u003eFabric\u003c/span\u003e.\u003c/span\u003e\u003c/span\u003ejs의 Custom Object Class에서는 `_render`함수에서 Canvas의 Context를 다룰 수 있다.\n\u003cspan class=\"hljs-number\"\u003e3.\u003c/span\u003e \u003cspan class=\"hljs-module-access\"\u003e\u003cspan class=\"hljs-module\"\u003e\u003cspan class=\"hljs-identifier\"\u003eChart\u003c/span\u003e.\u003c/span\u003e\u003c/span\u003ejs의 옵션이 \u003cspan class=\"hljs-module-access\"\u003e\u003cspan class=\"hljs-module\"\u003e\u003cspan class=\"hljs-identifier\"\u003eEchart\u003c/span\u003e.\u003c/span\u003e\u003c/span\u003ejs의 사용법보다 쉽다.\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e처음 개발 단계에서 순조롭게 Chart.js의 인스턴스를 생성할 떄, Fabric.js의 context를 활용하여 차트를 렌더링할 수 있었지만, Chart.js의 레이아웃 옵션에는 width, height를 따로 지정할 수 있는 방법이 없었다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003eChart.js의 레이아웃 옵션에는 \u003ccode\u003eresponsive\u003c/code\u003e 반응형 옵션만 제공된다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e결국 Chart.js는 포기하고 다양한 차트를 지원하는 Echarts를 React Design Editor 내에 구현된 Element Handler를 통해 구현하게 되었다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003eEcharts의 경우 Canvas의 Context로 직접 다룰 수 있는지에 대한 여부는 확인되지 않았지만, 향후에 발견하게 되다면 변경할 예정이다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e현재 차트 옵션을 통으로 설정하게 되어 있고, 복사 및 붙여 넣기, 크기 조절, 브라우저 반응형을 지원한다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/react-design-editor-chart.png\" alt=\"react-design-editor-chart\" /\u003e\u003c/p\u003e","preview":"이번에 React Design Editor 프로젝트에 차트를 추가할 수 있도록 업데이트 했다.\n\n차트는 Canvas로 개발된 Baidu의 Echarts를 사용하였다.\n\n최초는 Chart.js를 사용하여 Canvas의 Context로 차트를 Fabric.js 내로 직접 렌더링 하려 했으나 Chart.js의\n레이아웃 기능 제약에 막혀 Echarts를 선택하게 ...","title":"[React Design Editor] 차트 추가","author":"salgum1114","date":"2019-10-20 10:09","tags":"react, antd, fabricjs, react-design-editor","cover":"/static/images/covers/react-design-editor.png","next":"/nextjs/2019-05-28-nextjs-static-website-5","prev":"/reactjs/2019-10-30-react-hot-loader-patch-warning"},"/nextjs/2019-05-28-nextjs-static-website-5":{"path":"/nextjs/2019-05-28-nextjs-static-website-5","content":"\u003cp\u003e이번에는 프로젝트에 \u003ca href=\"https://material-ui.com/getting-started/installation/\"\u003eMaterial UI\u003c/a\u003e를 적용하여 레이아웃을 만들어 보고 몇가지 컴포넌트를 사용하여 레이아웃을 만들어보려고 한다.\u003c/p\u003e\n\u003cp\u003e먼저 \u003ccode\u003eMetarial UI\u003c/code\u003e를 사용하기 위해 \u003ccode\u003enpm package\u003c/code\u003e를 받는다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascrpt language-javascrpt\"\u003e\u003cspan class=\"hljs-built_in\"\u003enpm\u003c/span\u003e install --save @material-ui/core @material-ui/icons @material-ui/styles\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003eNext.js\u003c/code\u003e는 기본적으로 Server Side Rendering 이기 때문에, 단순히 \u003ccode\u003eMaterial UI\u003c/code\u003e 컴포넌트를 사용만할 경우 스타일이 정상적으로 먹히지 않는다.\n그렇기 때문에 몇가지 작업을 해줘야 하는데, 일반적인 \u003cstrong\u003eServer Rendering\u003c/strong\u003e인 경우에는 \u003ca href=\"https://material-ui.com/guides/server-rendering/\"\u003eMaterial Server Rendering\u003c/a\u003e을 참고하면 되고 우리는 \u003ccode\u003eNext.js\u003c/code\u003e를 사용하기 때문에 \u003ca href=\"https://github.com/mui-org/material-ui/tree/master/examples/nextjs\"\u003eMaterial with Next.js\u003c/a\u003e를 참고하면 된다.\u003c/p\u003e\n\u003cp\u003e기존에 \u003ccode\u003e_app.js\u003c/code\u003e에 작성된 레이아웃을 바꿔보기 위해 \u003ccode\u003eLayout\u003c/code\u003e 컴포넌트를 새로 만든다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ecomponents\u003c/code\u003e 디렉토리에 \u003ccode\u003eLayout.js\u003c/code\u003e 파일을 생성하고 다음 코드를 작성한다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLayout.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React, { Component } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Link \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/link'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e {\n AppBar,\n IconButton,\n Toolbar,\n Typography,\n Drawer,\n Divider,\n List,\n ListItem,\n ListItemIcon,\n ListItemText,\n Container,\n} \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'@material-ui/core'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { Menu, ChevronLeft, Home, Inbox, Mail } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'@material-ui/icons'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e drawerWidth = \u003cspan class=\"hljs-number\"\u003e240\u003c/span\u003e;\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eLayout\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n state = {\n \u003cspan class=\"hljs-attr\"\u003eopen\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e,\n }\n\n handleDrawerOpen = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.setState({\n \u003cspan class=\"hljs-attr\"\u003eopen\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n });\n }\n\n handleDrawerClose = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.setState({\n \u003cspan class=\"hljs-attr\"\u003eopen\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e,\n });\n }\n\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { children } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { open } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.state;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u0026lt;section\u0026gt;\n \u0026lt;AppBar\n position=\"static\"\n style={{\n width: `calc(100% - ${open ? drawerWidth : 0}px)`,\n marginLeft: open ? drawerWidth : 0,\n transition: 'all 0.2s',\n }}\n \u0026gt;\n \u0026lt;Toolbar\u0026gt;\n \u0026lt;IconButton edge=\"start\" color=\"inherit\" aria-label=\"Menu\" onClick={this.handleDrawerOpen}\u0026gt;\n \u0026lt;Menu /\u0026gt;\n \u0026lt;/IconButton\u0026gt;\n \u0026lt;Typography variant=\"h6\" style={{ flex: 1 }}\u0026gt;\n Static Website\n \u0026lt;/Typography\u0026gt;\n \u0026lt;/Toolbar\u0026gt;\n \u0026lt;/AppBar\u0026gt;\n \u0026lt;Drawer variant=\"persistent\" open={open}\u0026gt;\n \u0026lt;div style={{ width: drawerWidth }}\u0026gt;\n \u0026lt;div style={{ display: 'flex', justifyContent: 'flex-end' }}\u0026gt;\n \u0026lt;IconButton onClick={this.handleDrawerClose}\u0026gt;\n \u0026lt;ChevronLeft /\u0026gt;\n \u0026lt;/IconButton\u0026gt;\n \u0026lt;/div\u0026gt;\n \u0026lt;Divider /\u0026gt;\n \u0026lt;List\u0026gt;\n \u0026lt;Link href=\"/\"\u0026gt;\n \u0026lt;a\u0026gt;\n \u0026lt;ListItem button\u0026gt;\n \u0026lt;ListItemIcon\u0026gt;\u0026lt;Home /\u0026gt;\u0026lt;/ListItemIcon\u0026gt;\n \u0026lt;ListItemText primary=\"Home\"/\u0026gt;\n \u0026lt;/ListItem\u0026gt;\n \u0026lt;/a\u0026gt;\n \u0026lt;/Link\u0026gt;\n \u0026lt;Link href=\"/post\"\u0026gt;\n \u0026lt;a\u0026gt;\n \u0026lt;ListItem button\u0026gt;\n \u0026lt;ListItemIcon\u0026gt;\u0026lt;Inbox /\u0026gt;\u0026lt;/ListItemIcon\u0026gt;\n \u0026lt;ListItemText primary=\"Post\"/\u0026gt;\n \u0026lt;/ListItem\u0026gt;\n \u0026lt;/a\u0026gt;\n \u0026lt;/Link\u0026gt;\n \u0026lt;Link href=\"/about\"\u0026gt;\n \u0026lt;a\u0026gt;\n \u0026lt;ListItem button\u0026gt;\n \u0026lt;ListItemIcon\u0026gt;\u0026lt;Mail /\u0026gt;\u0026lt;/ListItemIcon\u0026gt;\n \u0026lt;ListItemText primary=\"About\"/\u0026gt;\n \u0026lt;/ListItem\u0026gt;\n \u0026lt;/a\u0026gt;\n \u0026lt;/Link\u0026gt;\n \u0026lt;/List\u0026gt;\n \u0026lt;/div\u0026gt;\n \u0026lt;/Drawer\u0026gt;\n \u0026lt;Container\u0026gt;\n \u0026lt;article style={{\n width: `calc(100% - ${open ? drawerWidth : 0}px)`,\n marginLeft: open ? drawerWidth : 0,\n transition: 'all 0.2s',\n margin: '16px 0',\n }}\u0026gt;\n {children}\n \u0026lt;/article\u0026gt;\n \u0026lt;/Container\u0026gt;\n \u0026lt;/section\u0026gt;\n );\n }\n}\n\nexport default Layout;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e그리고나서 \u003ccode\u003eapp.js\u003c/code\u003e에 작성된 레이아웃 코드를 다음과 같이 변경한다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eapp.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e App, { Container } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/app'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Head \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/head'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Layout \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'../components/material/Layout'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootApp\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eApp\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { Component, ...other } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eContainer\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eHead\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003etitle\u003c/span\u003e\u0026gt;\u003c/span\u003eStatic Website\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003etitle\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eHead\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLayout\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eComponent\u003c/span\u003e {\u003cspan class=\"hljs-attr\"\u003e...other\u003c/span\u003e} /\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLayout\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eContainer\u003c/span\u003e\u0026gt;\u003c/span\u003e\n );\n }\n}\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e여기까지 작성 후에 화면을 확인해본다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-material-ui.png\" alt=\"first-material-ui\" /\u003e\u003c/p\u003e\n\u003cp\u003e하지만 화면을 새로고침해서 보게되면 처음에 설명했다시피 스타일이 적용되지 않았다가 렌더링이 모두 끝난 후 스타일이 적용되는 것을 볼 수 있다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/material-client-rendering.gif\" alt=\"material-client-rendering\" /\u003e\u003c/p\u003e\n\u003cp\u003e이 문제를 해결하기 위해 \u003ccode\u003e_document.js\u003c/code\u003e와 \u003ccode\u003e_app.js\u003c/code\u003e를 수정한다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e_document.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Document, { Head, Main, NextScript } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/document'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { ServerStyleSheets } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'@material-ui/styles'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e flush \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'styled-jsx/server'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootDocument\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDocument\u003c/span\u003e \u003c/span\u003e{\n \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003easync\u003c/span\u003e getInitialProps(ctx) {\n \u003cspan class=\"hljs-comment\"\u003e// Render app and page and get the context of the page with collected side effects.\u003c/span\u003e\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e sheets = \u003cspan class=\"hljs-keyword\"\u003enew\u003c/span\u003e ServerStyleSheets();\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e originalRenderPage = ctx.renderPage;\n\n ctx.renderPage = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e\n originalRenderPage({\n \u003cspan class=\"hljs-attr\"\u003eenhanceApp\u003c/span\u003e: \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003eApp\u003c/span\u003e =\u0026gt;\u003c/span\u003e props =\u0026gt; sheets.collect(\u0026lt;App {...props} /\u0026gt;),\n });\n\n const initialProps = await Document.getInitialProps(ctx);\n\n return {\n ...initialProps,\n // Styles fragment is rendered after the app and page rendering finish.\n styles: (\n \u0026lt;React.Fragment\u0026gt;\n {sheets.getStyleElement()}\n {flush() || null}\n \u0026lt;/React.Fragment\u0026gt;\n ),\n };\n }\n\n render() {\n return (\n \u0026lt;html\u0026gt;\n \u0026lt;Head\u0026gt;\n \u0026lt;meta charSet=\"utf-8\" /\u0026gt;\n \u0026lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no\" /\u0026gt;\n \u0026lt;meta name=\"description\" content=\"My First Static Website\"/\u0026gt;\n \u0026lt;meta name=\"keywords\" content=\"nextjs,static,website\" /\u0026gt;\n \u0026lt;/Head\u0026gt;\n \u0026lt;body\u0026gt;\n \u0026lt;Main /\u0026gt;\n \u0026lt;NextScript /\u0026gt;\n \u0026lt;/body\u0026gt;\n \u0026lt;/html\u0026gt;\n );\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cstrong\u003e_app.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e App, { Container } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/app'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Head \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/head'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e CssBaseline \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'@material-ui/core/CssBaseline'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Layout \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'../components/material/Layout'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootApp\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eApp\u003c/span\u003e \u003c/span\u003e{\n componentDidMount() {\n \u003cspan class=\"hljs-comment\"\u003e// Remove the server-side injected CSS.\u003c/span\u003e\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e jssStyles = \u003cspan class=\"hljs-built_in\"\u003edocument\u003c/span\u003e.querySelector(\u003cspan class=\"hljs-string\"\u003e'#jss-server-side'\u003c/span\u003e);\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (jssStyles) {\n jssStyles.parentNode.removeChild(jssStyles);\n }\n }\n\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { Component, ...other } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eContainer\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eHead\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003etitle\u003c/span\u003e\u0026gt;\u003c/span\u003eStatic Website\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003etitle\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eHead\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eCssBaseline\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLayout\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eComponent\u003c/span\u003e {\u003cspan class=\"hljs-attr\"\u003e...other\u003c/span\u003e} /\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLayout\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eContainer\u003c/span\u003e\u0026gt;\u003c/span\u003e\n );\n }\n}\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e다시 화면을 확인해본다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/material-server-rendering.gif\" alt=\"material-server-rendering\" /\u003e\u003c/p\u003e\n\u003cp\u003e새로고침을 해도 스타일이 정상적으로 적용되는 것을 확인할 수 있다.\u003c/p\u003e\n\u003cp\u003e이렇게해서 \u003ccode\u003eMaterial UI\u003c/code\u003e 컴포넌트를 적용해봤고 컴포넌트들을 사용해서 화면을 구성해 나가면 된다.\u003c/p\u003e\n\u003cp\u003e다음 글에서는 \u003ccode\u003eAnt.Design\u003c/code\u003e을 적용하여 Layout을 작성해본다.\u003c/p\u003e\n\u003ch3 id=\"nextjs\"\u003eNext.js로 정적 웹사이트 만들기 목차\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-06-nextjs-static-website-1/\"\u003eNext.js로 정적 웹사이트 만들기 - 1. Next.js 구조\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-20-nextjs-static-website-2/\"\u003eNext.js로 정적 웹사이트 만들기 - 2. 프로젝트 구성\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-21-nextjs-static-website-3/\"\u003eNext.js로 정적 웹사이트 만들기 - 3. 레이아웃\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-24-nextjs-static-website-4/\"\u003eNext.js로 정적 웹사이트 만들기 - 4. Routing 사용하기\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-28-nextjs-static-website-5/\"\u003eNext.js로 정적 웹사이트 만들기 - 5. Material UI 적용하기\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","preview":"이번에는 프로젝트에 Material UI를 적용하여 레이아웃을 만들어 보고 몇가지 컴포넌트를 사용하여 레이아웃을 만들어보려고 한다.\n\n먼저 Metarial UI를 사용하기 위해 npm package를 받는다.\n\nnpm install --save @material-ui/core @material-ui/icons @material-ui/styles\n\n\nNext...","title":"Next.js로 정적 웹사이트 만들기 - 5. Material UI 적용하기","author":"salgum1114","date":"2019-05-28 15:00","tags":"react, nextjs, website","cover":"/static/images/covers/nextjs.png","next":"/nextjs/2019-05-24-nextjs-static-website-4","prev":"/etc/2019-10-22-react-design-editor-1"},"/nextjs/2019-05-24-nextjs-static-website-4":{"path":"/nextjs/2019-05-24-nextjs-static-website-4","content":"\u003cp\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-21-nextjs-static-website-3/\"\u003e지난 글\u003c/a\u003e에서 \u003ccode\u003eindex.js\u003c/code\u003e와 \u003ccode\u003epost.js\u003c/code\u003e를 생성했었고 \u003ccode\u003eRouting\u003c/code\u003e을 사용하지 않고 URL로 직접 접근해서 화면을 확인했었다. 이번 글에서는 \u003ccode\u003eNext.js\u003c/code\u003e의 \u003ccode\u003eRouting\u003c/code\u003e을 사용하여 페이지 전환을 해보려고한다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e\u003ca href=\"https://github.com/zeit/next.js/#routing\"\u003eNext.js github\u003c/a\u003e에 \u003ccode\u003eRouting\u003c/code\u003e에 대한 설명이 잘 나와있다.\u003c/p\u003e\n \u003cp\u003e\u003ccode\u003eNext.js\u003c/code\u003e에서 \u003ccode\u003eRoute\u003c/code\u003e는 \u003ccode\u003epages\u003c/code\u003e디렉토리 경로에 작성한 파일명으로 생성된다. 그렇기 때문에, 파일명도 신경써서 잘 만들어야한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"aboutjs\"\u003eabout.js 파일 생성\u003c/h2\u003e\n\u003cp\u003eRouting 경로를 더 추가하기 위해 \u003ccode\u003epages\u003c/code\u003e디렉토리에 \u003ccode\u003eabout.js\u003c/code\u003e파일을 추가하고 다음과 같이 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003eAbout\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003eabout.js\u003c/code\u003e까지 생성하였다면 총 3개의 Routing이 생기게 되었다.\u003c/p\u003e\n\u003ch2 id=\"_appjslink\"\u003e_app.js에 Link 컴포넌트 추가\u003c/h2\u003e\n\u003cp\u003e그런 다음, 공통의 레이아웃이 작성된 \u003ccode\u003e_app.js\u003c/code\u003e에서 \u003ccode\u003e\u0026lt;header\u0026gt;\u003c/code\u003e엘리먼트 안에 \u003ccode\u003e\u0026lt;Link\u0026gt;\u003c/code\u003e 컴포넌트를 추가하여 생성한 \u003ccode\u003eRouting\u003c/code\u003e 연결해본다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e App, { Container } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/app'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Head \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/head'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Link \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/link'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e styles = {\n \u003cspan class=\"hljs-attr\"\u003elayout\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003edisplay\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'flex'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003ewidth\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'100%'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eheight\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'100%'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eflexDirection\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'column'\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003eheader\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003eheight\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e60\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003emain\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003eflex\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e1\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003efooter\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003eheight\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e60\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003edivider\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003emargin\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'0 8px'\u003c/span\u003e,\n },\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootApp\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eApp\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { Component, ...other } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eContainer\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eHead\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003etitle\u003c/span\u003e\u0026gt;\u003c/span\u003eStatic Website\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003etitle\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eHead\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.layout}\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eheader\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.header}\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLink\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ehref\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"/\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ea\u003c/span\u003e\u0026gt;\u003c/span\u003eHome\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ea\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLink\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003espan\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.divider}\u003c/span\u003e\u0026gt;\u003c/span\u003e|\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003espan\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLink\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ehref\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"/post\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ea\u003c/span\u003e\u0026gt;\u003c/span\u003ePost\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ea\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLink\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003espan\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.divider}\u003c/span\u003e\u0026gt;\u003c/span\u003e|\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003espan\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eLink\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003ehref\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"/about\"\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ea\u003c/span\u003e\u0026gt;\u003c/span\u003eAbout\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ea\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eLink\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eheader\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003emain\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.main}\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eComponent\u003c/span\u003e {\u003cspan class=\"hljs-attr\"\u003e...other\u003c/span\u003e} /\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003emain\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003efooter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.footer}\u003c/span\u003e\u0026gt;\u003c/span\u003eFooter\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003efooter\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eContainer\u003c/span\u003e\u0026gt;\u003c/span\u003e\n );\n }\n}\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003eimport Link from 'next/link'\u003c/code\u003e로 모듈을 import 해주고 \u003ccode\u003e\u0026lt;Link\u0026gt;\u003c/code\u003e 컴포넌트에 \u003ccode\u003ehref\u003c/code\u003e Props 값으로 \u003ccode\u003epages\u003c/code\u003e디렉토리에서 생성한 파일명을 작성해준다. 마지막으로 \u003ccode\u003e\u0026lt;Link\u0026gt;\u003c/code\u003e안에는 \u003ccode\u003e\u0026lt;a\u0026gt;\u003c/code\u003e를 추가해준다.\u003c/p\u003e\n\u003cp\u003e여기까지 작성되었다면 다음과 같은 화면이 보일 것이다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-routing.png\" alt=\"first-routing\" /\u003e\u003c/p\u003e\n\u003cp\u003e한 번씩 눌러본다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-routing-click.gif\" alt=\"first-routing-click\" /\u003e\u003c/p\u003e\n\u003ch2 id=\"postjsrouter\"\u003epost.js에 Router 추가\u003c/h2\u003e\n\u003cp\u003e다음으로, \u003ccode\u003e\u0026lt;button\u0026gt;\u003c/code\u003e엘리먼트 \u003ccode\u003eonClick\u003c/code\u003e을 이용해서 \u003ccode\u003eRouting\u003c/code\u003e을 해보는데, 이때 \u003ccode\u003eNext.js\u003c/code\u003e의 \u003ccode\u003eRouter\u003c/code\u003e를 사용해본다.\u003c/p\u003e\n\u003cp\u003e먼저, \u003ccode\u003e\u0026lt;Link\u0026gt;\u003c/code\u003e와 \u003ccode\u003eRouter\u003c/code\u003e의 큰 차이점은 클라이언트 사이드로 렌더링 하냐 안하냐에 있다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e\u003ccode\u003eRouter.push\u003c/code\u003e를 사용하면 클라이언트 사이드로 렌더링을 한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Router \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/router'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n First post page\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eonClick\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{()\u003c/span\u003e =\u0026gt;\u003c/span\u003e Router.push('/')}\u0026gt;Home\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e \n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003epost.js\u003c/code\u003e에서 \u003ccode\u003eimport Router from 'next/router'\u003c/code\u003e로 모듈을 import 해주고 \u003ccode\u003e\u0026lt;button\u0026gt;\u003c/code\u003e을 추가한 다음 \u003ccode\u003eonClick\u003c/code\u003e 속성에 \u003ccode\u003eRouter.push('/')\u003c/code\u003e를 추가한다.\u003c/p\u003e\n\u003cp\u003e여기까지 작성되었다면 다음처럼 동작한다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-router-click.gif\" alt=\"first-router-click\" /\u003e\u003c/p\u003e\n\u003cp\u003e마찬가지로 \u003ccode\u003eabout.js\u003c/code\u003e에도 추가해준다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Router \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/router'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n About\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eonClick\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{()\u003c/span\u003e =\u0026gt;\u003c/span\u003e Router.push('/')}\u0026gt;Home\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"urlparamsquerystring\"\u003eURL Params(Query String) 전달\u003c/h2\u003e\n\u003cp\u003e다음으로 \u003ccode\u003eRouting\u003c/code\u003e을 할 때, URL로 Parameter를 전달하여 각 페이지에서 전달받은 Parameter를 화면에 처리할 수 있도록 해본다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e\u003ccode\u003eHome\u003c/code\u003e 버튼을 클릭 했을 때, 어떤 페이지에서 \u003ccode\u003eHome\u003c/code\u003e으로 돌아왔는지 확인해보는 예제를 작성한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ccode\u003epost.js\u003c/code\u003e에서 했던 것처럼 \u003ccode\u003eabout.js\u003c/code\u003e에도 \u003ccode\u003e\u0026lt;button\u0026gt;\u003c/code\u003e엘리먼트를 추가하고 \u003ccode\u003eonClick\u003c/code\u003e속성에 \u003ccode\u003eRouter.push\u003c/code\u003e를 추가한다.\u003c/p\u003e\n\u003cp\u003e그러고 나서 \u003ccode\u003epost.js\u003c/code\u003e와 \u003ccode\u003eabout.js\u003c/code\u003e에 작성된 \u003ccode\u003eRouter.push('/')\u003c/code\u003e를 각각 \u003ccode\u003eRouter.push('/?history=post')\u003c/code\u003e와 \u003ccode\u003eRouter.push('/?history=about')\u003c/code\u003e로 변경해준다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003epost.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Router \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/router'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n First post page\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eonClick\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{()\u003c/span\u003e =\u0026gt;\u003c/span\u003e Router.push('/?history=post')}\u0026gt;Home\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e \n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cstrong\u003eabout.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Router \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/router'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n About\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eonClick\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{()\u003c/span\u003e =\u0026gt;\u003c/span\u003e Router.push('/?history=about')}\u0026gt;Home\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e이렇게 작성된 페이지에서 \u003ccode\u003eHome\u003c/code\u003e 버튼을 클릭하여 \u003ccode\u003eHome\u003c/code\u003e경로로 가게 됐을 경우, URL에는 Query String이 붙게 되었고, \u003ccode\u003eindex.js\u003c/code\u003e에 전달되는 props에 \u003ccode\u003econsole.log(props)\u003c/code\u003e로 로그를 확인해보면 \u003ccode\u003eDevelop Console\u003c/code\u003e에 다음처럼 보이게 된다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-query-string.png\" alt=\"first-query-string\" /\u003e\u003c/p\u003e\n\u003cp\u003eprops에 \u003ccode\u003erouter\u003c/code\u003e 오브젝트가 전달되는데, \u003ccode\u003erouter\u003c/code\u003e 오브젝트 안에서 전달된 \u003ccode\u003equery\u003c/code\u003e를 확인할 수 있다.\u003c/p\u003e\n\u003cp\u003e이것을 활용해서 \u003ccode\u003eindex.js\u003c/code\u003e에서 어떤 페이지로부터 넘어왔는지 확인해본다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eindex.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e styles = {\n \u003cspan class=\"hljs-attr\"\u003efrom\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003ecolor\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'red'\u003c/span\u003e,\n },\n};\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { router } = props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n Home\n {\n router.query.history ? (\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.from}\u003c/span\u003e\u0026gt;\u003c/span\u003e\n From the {router.query.history}\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n ) : null\n }\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e화면을 확인해본다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-query-string-from.png\" alt=\"first-query-string-from\" /\u003e\u003c/p\u003e\n\u003ch2 id=\"asurlparams\"\u003e\u003cstrong\u003eas\u003c/strong\u003e 속성으로 URL Params 숨기기\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eURL Params\u003c/code\u003e가 URL에 보이는 것이 맘이 편하지 않다면 \u003ccode\u003eNext.js\u003c/code\u003e에서는 숨길 수 있는 방법이 있다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eas\u003c/code\u003e속성을 이용하는 것인데, \u003ccode\u003e\u0026lt;Link href=\"/?history=post\" as=\"/\"\u0026gt;\u003c/code\u003e로 사용하거나 \u003ccode\u003eRouter.push(\"/?history=post\", '/')\u003c/code\u003e로 사용할 수 있다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003epost.js\u003c/code\u003e와 \u003ccode\u003eabout.js\u003c/code\u003e에 가서 다음과 같이 바꿔준다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003epost.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Router \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/router'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n First post page\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eonClick\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{()\u003c/span\u003e =\u0026gt;\u003c/span\u003e Router.push('/?history=post', '/')}\u0026gt;Home\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e \n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cstrong\u003eabout.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Router \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/router'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n About\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eonClick\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{()\u003c/span\u003e =\u0026gt;\u003c/span\u003e Router.push('/?history=about', '/')}\u0026gt;Home\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e다시 화면을 확인해본다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-routing-as.gif\" alt=\"first-routing-as\" /\u003e\u003c/p\u003e\n\u003cp\u003e아까와는 다르게 URL에 \u003ccode\u003eQuery String\u003c/code\u003e이 붙지 않고 \u003ccode\u003ehistory\u003c/code\u003e가 전달된 것을 확인할 수 있다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e하지만 이 방법에서 문제점을 찾으라고 한다면, 새로고침을 했을 때 전달받은 \u003ccode\u003erouter\u003c/code\u003e 오브젝트에 \u003ccode\u003equery\u003c/code\u003e가 비어있으므로 \u003ccode\u003eFrom the\u003c/code\u003e문구가 보이지 않게 된다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"withrouter\"\u003e\u003cstrong\u003ewithRouter\u003c/strong\u003e 사용\u003c/h2\u003e\n\u003cp\u003e다음으로 \u003ccode\u003ewithRouter\u003c/code\u003e를 사용해볼 것인데, 이 녀석은 \u003ccode\u003eRouter\u003c/code\u003e의 \u003ccode\u003eHOC\u003c/code\u003e로써 컴포넌트에서 \u003ccode\u003erouter\u003c/code\u003e props를 전달받을 수 있도록 해준다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eHistory\u003c/code\u003e 예제를 위해서 root 경로에 \u003ccode\u003ecomponents\u003c/code\u003e 디렉토리를 만들고 하위에 \u003ccode\u003eHistory.js\u003c/code\u003e 파일을 생성한다.\u003c/p\u003e\n\u003cp\u003e그런 다음, \u003ccode\u003eHistory.js\u003c/code\u003e에는 다음과 같이 작성한다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHistory.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React, { Component } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { withRouter } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/router'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e styles = {\n \u003cspan class=\"hljs-attr\"\u003ehistory\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003ecolor\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'red'\u003c/span\u003e,\n },\n};\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eHistory\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { router } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e router.query.history ? (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.history}\u003c/span\u003e\u0026gt;\u003c/span\u003e\n {router.query.history}\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n ) : \u003cspan class=\"hljs-literal\"\u003enull\u003c/span\u003e;\n }\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e withRouter(History);\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003eimport { withRouter } from 'next/router'\u003c/code\u003e로 모듈을 import 시키고 \u003ccode\u003ewithRouter(History)\u003c/code\u003e로 Component를 감싸게 되면 props로 router를 전달받을 수 있게 된다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eindex.js\u003c/code\u003e는 다음과 같이 변경한다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eindex.js\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e History \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'../components/History'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n Home\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eHistory\u003c/span\u003e /\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e작성한 후에 화면을 확인한다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-router-history.png\" alt=\"first-router-history\" /\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eindex.js\u003c/code\u003e에서 \u003ccode\u003e\u0026lt;History\u0026gt;\u003c/code\u003e로 \u003ccode\u003erouter\u003c/code\u003e를 props으로 전달하지 않지만 \u003ccode\u003e\u0026lt;History\u0026gt;\u003c/code\u003e컴포넌트에서 \u003ccode\u003eHOC\u003c/code\u003e로 \u003ccode\u003ewithRotuer\u003c/code\u003e를 사용했기 때문에 props로 \u003ccode\u003erouter\u003c/code\u003e오브젝트를 전달받은 것을 볼 수 있다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-router-history-log.png\" alt=\"first-router-history-log\" /\u003e\u003c/p\u003e\n\u003ch3 id=\"nextjs\"\u003eNext.js로 정적 웹사이트 만들기 목차\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-06-nextjs-static-website-1/\"\u003eNext.js로 정적 웹사이트 만들기 - 1. Next.js 구조\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-20-nextjs-static-website-2/\"\u003eNext.js로 정적 웹사이트 만들기 - 2. 프로젝트 구성\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-21-nextjs-static-website-3/\"\u003eNext.js로 정적 웹사이트 만들기 - 3. 레이아웃\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-24-nextjs-static-website-4/\"\u003eNext.js로 정적 웹사이트 만들기 - 4. Routing 사용하기\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-28-nextjs-static-website-5/\"\u003eNext.js로 정적 웹사이트 만들기 - 5. Material UI 적용하기\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","preview":"지난 글에서 index.js와 post.js를 생성했었고 Routing을 사용하지 않고 URL로 직접 접근해서 화면을 확인했었다. 이번 글에서는 \nNext.js의 Routing을 사용하여 페이지 전환을 해보려고한다.\n\n\u003e Next.js github에 Routing에 대한 설명이 잘 나와있다.\n\nNext.js에서 Route는 pages디렉토리 경로에 작성한...","title":"Next.js로 정적 웹사이트 만들기 - 4. Routing 사용하기","author":"salgum1114","date":"2019-05-24 08:27","tags":"react, nextjs, website","cover":"/static/images/covers/nextjs.png","next":"/nextjs/2019-05-21-nextjs-static-website-3","prev":"/nextjs/2019-05-28-nextjs-static-website-5"},"/nextjs/2019-05-21-nextjs-static-website-3":{"path":"/nextjs/2019-05-21-nextjs-static-website-3","content":"\u003cp\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-20-nextjs-static-website-2/\"\u003e이전 글\u003c/a\u003e에서 프로젝트 구성을 하여 간단한 화면을 확인했었다.\u003c/p\u003e\n\u003cp\u003e이번에는 Next.js에 내장되어 있는 \u003ccode\u003e_document.js\u003c/code\u003e, \u003ccode\u003e_app.js\u003c/code\u003e, \u003ccode\u003e_error.js\u003c/code\u003e를 커스터마이징하여 레이아웃을 새롭게 구성하는 방법을 설명하려고 한다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e각각에 대한 설명은 \u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-06-nextjs-static-website-1/\"\u003eNext.js 구조\u003c/a\u003e에서 확인할 수 있다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"_documentjs\"\u003e_document.js 파일 생성\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e_document.js\u003c/code\u003e는 Next.js에서 서버 사이드 렌더링이 시작이되는 \u003ccode\u003eDocument\u003c/code\u003e 파일이다.\u003c/p\u003e\n\u003cp\u003e즉, \u003ccode\u003e\u0026lt;html\u0026gt;\u003c/code\u003e, \u003ccode\u003e\u0026lt;head\u0026gt;\u003c/code\u003e, \u003ccode\u003e\u0026lt;body\u0026gt;\u003c/code\u003e를 포함한다고 보면된다.\u003c/p\u003e\n\u003cp\u003e그럼 \u003ccode\u003epages\u003c/code\u003e디렉토리에 \u003ccode\u003e_document.js\u003c/code\u003e파일을 생성하고, 다음과 같이 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Document, { Head, Main, NextScript } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/document'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootDocument\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDocument\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u0026lt;html\u0026gt;\n \u0026lt;Head\u0026gt;\n \u0026lt;meta charSet=\"utf-8\" /\u0026gt;\n \u0026lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no\" /\u0026gt;\n \u0026lt;meta name=\"description\" content=\"My First Static Website\"/\u0026gt;\n \u0026lt;meta name=\"keywords\" content=\"nextjs,static,website\" /\u0026gt;\n \u0026lt;style global jsx\u0026gt;\n {`\n html, body, #__next {\n height: 100%;\n width: 100%;\n overflow: hidden;\n }\n `}\n \u0026lt;/style\u0026gt;\n \u0026lt;/Head\u0026gt;\n \u0026lt;body\u0026gt;\n \u0026lt;Main /\u0026gt;\n \u0026lt;NextScript /\u0026gt;\n \u0026lt;/body\u0026gt;\n \u0026lt;/html\u0026gt;\n );\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003e\u0026lt;Head\u0026gt;\u003c/code\u003e안에 \u003ccode\u003e\u0026lt;script\u0026gt;\u003c/code\u003e, \u003ccode\u003e\u0026lt;meta\u0026gt;\u003c/code\u003e, \u003ccode\u003e\u0026lt;link\u0026gt;\u003c/code\u003e 등 리소스 및 Metadata들을 작성해줄 수 있다.\u003c/p\u003e\n\u003ch2 id=\"_appjs\"\u003e_app.js 파일 생성\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e_app.js\u003c/code\u003e는 어플리케이션이 시작되는 초기 페이지라고 볼 수 있다. 이 페이지에서는 라우팅을 통해 컴포넌트를 \u003ccode\u003eprops\u003c/code\u003e으로 전달받기 때문에 공통의 레이아웃을 작성하여 모든 화면에서 같은 레이아웃이 나올 수 있도록 작업하거나, \u003ccode\u003eComponentDidCatch\u003c/code\u003e와 같은 라이프 사이클을 통해 \u003ccode\u003eError Handling\u003c/code\u003e을 할 수 있다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003epages\u003c/code\u003e디렉토리에 \u003ccode\u003e_app.js\u003c/code\u003e파일을 생성하고, 다음과 같이 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e App, { Container } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/app'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Head \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/head'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e styles = {\n \u003cspan class=\"hljs-attr\"\u003elayout\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003edisplay\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'flex'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003ewidth\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'100%'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eheight\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'100%'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eflexDirection\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'column'\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003eheader\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003eheight\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e60\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003emain\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003eflex\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e1\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003efooter\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003eheight\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e60\u003c/span\u003e,\n },\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootApp\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eApp\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { Component, ...other } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eContainer\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eHead\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003etitle\u003c/span\u003e\u0026gt;\u003c/span\u003eStatic Website\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003etitle\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eHead\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.layout}\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eheader\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.header}\u003c/span\u003e\u0026gt;\u003c/span\u003eHeader\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eheader\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003emain\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.main}\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eComponent\u003c/span\u003e {\u003cspan class=\"hljs-attr\"\u003e...other\u003c/span\u003e} /\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003emain\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003efooter\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{styles.footer}\u003c/span\u003e\u0026gt;\u003c/span\u003eFooter\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003efooter\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eContainer\u003c/span\u003e\u0026gt;\u003c/span\u003e\n );\n }\n}\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003e\u0026lt;header\u0026gt;\u003c/code\u003e, \u003ccode\u003e\u0026lt;main\u0026gt;\u003c/code\u003e, \u003ccode\u003e\u0026lt;footer\u0026gt;\u003c/code\u003e로 레이아웃을 만들고 \u003ccode\u003eprops\u003c/code\u003e로 전달 받은 \u003ccode\u003eComponent\u003c/code\u003e를 렌더링한다.\u003c/p\u003e\n\u003cp\u003e여기까지 작성한 다음 화면을 확인해본다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-layout.png\" alt=\"first-layout\" /\u003e\u003c/p\u003e\n\u003cp\u003eHeader, Static Website, Footer순으로 화면에 보인다.\u003c/p\u003e\n\u003cp\u003e이렇게 해서 \u003ccode\u003epages\u003c/code\u003e에 페이지들을 추가하게 되면 Next.js에서는 URL에서 페이지 경로로 접근했을 경우 페이지에 작성된 \u003ccode\u003eComponent\u003c/code\u003e를 전달해주게 되어 공통의 레아이웃으로 화면은 구현할 수 있게 된다.\u003c/p\u003e\n\u003ch2 id=\"_errorjs\"\u003e_error.js 파일 생성\u003c/h2\u003e\n\u003cp\u003eNext.js에서는 \u003ccode\u003epages\u003c/code\u003e에 없는 경로로 접근했을 경우, 내장되어 있는 Error 페이지를 보여주게 되어있다.\u003c/p\u003e\n\u003cp\u003e마찬가지로 Error 페이지도 새롭게 작성할 수 있다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003epages\u003c/code\u003e디렉토리에 \u003ccode\u003e_error.js\u003c/code\u003e파일을 생성하고 다음과 같이 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React, { Component } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootError\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'Error!'\u003c/span\u003e;\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e작성한 후에 현재 경로에 \u003ccode\u003e/post\u003c/code\u003e를 붙여 URL에 접근해보면 다음과 같은 에러 화면을 확인할 수 있다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-error-page.png\" alt=\"first-error-page\" /\u003e\u003c/p\u003e\n\u003cp\u003e여기까지 레이아웃을 구성하였다면 \u003ccode\u003epages\u003c/code\u003e에 새로운 페이지를 만들어서 확인해본다.\u003c/p\u003e\n\u003ch2 id=\"postjs\"\u003epost.js 파일 생성\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003epages\u003c/code\u003e디렉토리에 \u003ccode\u003epost.js\u003c/code\u003e파일을 생성하고 다음과 같이 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n First post page\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e \n );\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003epost.js\u003c/code\u003e를 만들기 전에 \u003ccode\u003e/post\u003c/code\u003e경로로 접근했을 때는 에러가 났었지만 이제는 \u003ccode\u003eFirst post page\u003c/code\u003e라는 문구로 화면에 에러없이 나오는 것을 확인할 수 있다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-post-page.png\" alt=\"first-post-page\" /\u003e\u003c/p\u003e\n\u003ch3 id=\"nextjs\"\u003eNext.js로 정적 웹사이트 만들기 목차\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-06-nextjs-static-website-1/\"\u003eNext.js로 정적 웹사이트 만들기 - 1. Next.js 구조\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-20-nextjs-static-website-2/\"\u003eNext.js로 정적 웹사이트 만들기 - 2. 프로젝트 구성\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-21-nextjs-static-website-3/\"\u003eNext.js로 정적 웹사이트 만들기 - 3. 레이아웃\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-24-nextjs-static-website-4/\"\u003eNext.js로 정적 웹사이트 만들기 - 4. Routing 사용하기\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-28-nextjs-static-website-5/\"\u003eNext.js로 정적 웹사이트 만들기 - 5. Material UI 적용하기\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","preview":"이전 글에서 프로젝트 구성을 하여 간단한 화면을 확인했었다.\n\n이번에는 Next.js에 내장되어 있는 _document.js, _app.js, _error.js를 커스터마이징하여 레이아웃을 새롭게 구성하는\n방법을 설명하려고 한다.\n\n\u003e 각각에 대한 설명은 Next.js 구조에서 확인할 수 있다.\n\n\n_DOCUMENT.JS 파일 생성\n_document.js...","title":"Next.js로 정적 웹사이트 만들기 - 3. 레이아웃","author":"salgum1114","date":"2019-05-21 10:00","tags":"react, nextjs, website","cover":"/static/images/covers/nextjs.png","next":"/nextjs/2019-05-20-nextjs-static-website-2","prev":"/nextjs/2019-05-24-nextjs-static-website-4"},"/nextjs/2019-05-20-nextjs-static-website-2":{"path":"/nextjs/2019-05-20-nextjs-static-website-2","content":"\u003cp\u003e사실 Next.js는 설명할게 별로 없다. \u003ca href=\"https://github.com/zeit/next.js/tree/canary/examples\"\u003e예제들\u003c/a\u003e이 너무 잘되어 있기 때문에.. 그렇지만 내 머릿속에서 사라져가기 때문에 기록이라도 남기려고 작성한다.\u003c/p\u003e\n\u003ch2 id=\"1npm\"\u003e1. npm 프로젝트 생성\u003c/h2\u003e\n\u003cp\u003e프로젝트를 구성하기 위해 \u003ccode\u003estatic-website\u003c/code\u003e 디렉토리를 하나 만들고 \u003ccode\u003ecd static-website\u003c/code\u003e로 가서 \u003ccode\u003enpm init\u003c/code\u003e으로 npm 프로젝트를 만들자.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e참고로 \u003ccode\u003enpm init\u003c/code\u003e을 실행하게 되면 여러 입력을 받게 되는데, 나중에 변경할 수 있으니 다 엔터를 치고 넘어간다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003ccode\u003enpm init\u003c/code\u003e이 완료되었으면, \u003ccode\u003estatic-website\u003c/code\u003e 폴더에 \u003ccode\u003epackage.json\u003c/code\u003e 파일이 하나 생성된걸 볼 수 있는데, npm 프로젝트는 이 파일로 시작해서 끝난다고 보면 된다.\u003c/p\u003e\n\u003ch2 id=\"2dependencies\"\u003e2. dependencies 설치\u003c/h2\u003e\n\u003cp\u003e기본적으로 Next.js로 프로젝트를 구성하여 웹 페이지까지 띄우기 위해 필요한 종속성으로는 다음이 필요하다.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e@babel/node // node 파일에서 es6 문법을 사용하기 위해서\u003c/li\u003e\n\u003cli\u003e@babel/preset-env // es6 문법을 사용하기 위해서\u003c/li\u003e\n\u003cli\u003enext@7.0.2 // \u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-06-nextjs-static-website-1/\"\u003eNext.js구조\u003c/a\u003e에서 확인 가능\u003c/li\u003e\n\u003cli\u003ereact\u003c/li\u003e\n\u003cli\u003ereact-dom\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e이 종속성들을 설치해보자.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003estatic-website\u003c/code\u003e 경로에서 각 명령어들을 실행한다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003edepenencies\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003enpm install --save react react-dom next@7.0.2 express\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003edevDependecies\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003enpm install --save-dev @babel/node @babel/preset-env\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e이렇게 \u003ccode\u003enpm install\u003c/code\u003e 명령어로 종속성들을 설치하고나면 다음과 같이 \u003ccode\u003epackage.json\u003c/code\u003e이 구성된다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e-dev가 붙은 것과 아닌 것의 차이는 runtime 중에 종속성을 사용하냐 안하냐로 대부분 구분 짓는다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e{\n \u003cspan class=\"hljs-string\"\u003e\"name\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"static-website\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"version\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"1.0.0\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"description\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"main\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"index.js\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"scripts\"\u003c/span\u003e: {\n \u003cspan class=\"hljs-string\"\u003e\"test\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"echo \\\"Error: no test specified\\\" \u0026amp;\u0026amp; exit 1\"\u003c/span\u003e\n },\n \u003cspan class=\"hljs-string\"\u003e\"author\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"license\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"ISC\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"dependencies\"\u003c/span\u003e: {\n \u003cspan class=\"hljs-string\"\u003e\"next\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^7.0.2\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"react\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^16.8.6\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"react-dom\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^16.8.6\"\u003c/span\u003e\n },\n \u003cspan class=\"hljs-string\"\u003e\"devDependencies\"\u003c/span\u003e: {\n \u003cspan class=\"hljs-string\"\u003e\"@babel/node\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^7.2.2\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"@babel/preset-env\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^7.4.4\"\u003c/span\u003e\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e여기까지 되었으면 향후에 이 프로젝트가 \u003ccode\u003egit\u003c/code\u003e에 연결될 수도 있기 때문에,\u003ccode\u003estatic-website\u003c/code\u003e 경로에 \u003ccode\u003e.gitignore\u003c/code\u003e를 만들어 \u003ccode\u003enode_modules\u003c/code\u003e와 같은 것들을 제외 시켜준다.\u003c/p\u003e\n\u003cp\u003e현재는 \u003ccode\u003enode_modules\u003c/code\u003e와 \u003ccode\u003epackage-lock.json\u003c/code\u003e만 있으니 두개를 제외시킨다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003enode_modules/\npacakge-\u003cspan class=\"hljs-keyword\"\u003elock\u003c/span\u003e.json\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"nextconfigjs\"\u003enext.config.js 생성\u003c/h2\u003e\n\u003cp\u003e기본적으로 Next.js는 프로젝트의 루트 경로에 있는 next.config.js를 읽어들이게 된다.\u003c/p\u003e\n\u003cp\u003e간단하게 \u003ccode\u003enext.config.js\u003c/code\u003e를 생성한다. 파일 내용은 다음과 같이 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-built_in\"\u003emodule\u003c/span\u003e.exports = {};\n\u003c/code\u003e\u003c/pre\u003e\n\u003cblockquote\u003e\n \u003cp\u003e향후에 플러그인들을 설정에 추가한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"packagejson\"\u003epackage.json 스크립트 작성\u003c/h2\u003e\n\u003cp\u003eNext.js 패키지를 설치하게 되면 다음과 같은 cli를 제공한다.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003enext // 내장되어 있는 webpack-dev-server로 기동\u003c/li\u003e\n\u003cli\u003enext build // 프로젝트를 빌드하여 .next 폴더를 생성한다.\u003c/li\u003e\n\u003cli\u003enext start // 빌드된 .next 폴더를 기준으로 웹 서버를 기동해준다.\u003c/li\u003e\n\u003cli\u003enext export // html 파일들로 내보내기를 해준다.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n \u003cp\u003eNext.js에는 webpack 설정이 내장되어 있기 때문에, hot-loader 같은 것들이 기본적으로 동작한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e다음과 같이 \u003ccode\u003epackage.json\u003c/code\u003e의 \u003ccode\u003escripts\u003c/code\u003e에 추가해준다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e{\n \u003cspan class=\"hljs-string\"\u003e\"name\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"static-website\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"version\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"1.0.0\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"description\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"main\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"index.js\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"scripts\"\u003c/span\u003e: {\n \u003cspan class=\"hljs-string\"\u003e\"test\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"echo \\\"Error: no test specified\\\" \u0026amp;\u0026amp; exit 1\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"dev\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"next\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"build\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"next build\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"start\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"next start\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"export\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"next export\"\u003c/span\u003e\n },\n \u003cspan class=\"hljs-string\"\u003e\"author\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"license\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"ISC\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"dependencies\"\u003c/span\u003e: {\n \u003cspan class=\"hljs-string\"\u003e\"express\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^4.17.0\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"next\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^7.0.2\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"react\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^16.8.6\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"react-dom\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^16.8.6\"\u003c/span\u003e\n },\n \u003cspan class=\"hljs-string\"\u003e\"devDependencies\"\u003c/span\u003e: {\n \u003cspan class=\"hljs-string\"\u003e\"@babel/node\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^7.2.2\"\u003c/span\u003e,\n \u003cspan class=\"hljs-string\"\u003e\"@babel/preset-env\"\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"^7.4.4\"\u003c/span\u003e\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e이 상태로 npm run dev로 기동해보면 실패한다. 이유는 \u003ccode\u003epages\u003c/code\u003e 폴더가 없어서 그렇다.\u003c/p\u003e\n\u003ch2 id=\"pagesindexjs\"\u003epages 폴더 및 index.js 파일 생성\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003estatic-website\u003c/code\u003e 경로에 \u003ccode\u003epages\u003c/code\u003e 폴더를 생성하고 그 하위에 \u003ccode\u003eindex.js\u003c/code\u003e 파일을 생성한다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eindex.js\u003c/code\u003e 에는 다음과 같이 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e (props) =\u0026gt; {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\n static webiste\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ediv\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n )\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e그리고 나서 \u003ccode\u003enpm run dev\u003c/code\u003e 를 다시 실행해본다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003enext의 기본 포트는 3000이고 변경하고자 하면 next -p \u003c포트 번호\u003e 를 붙이면 된다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003elocalhost:3000으로 접속하여 화면을 확인하면 static website를 출력하는 화면이 나온다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/first-page.png\" alt=\"first-page\" /\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003epages\u003c/code\u003e에 \u003ccode\u003eindex.js\u003c/code\u003e를 작성하게 되면 \u003ccode\u003eindex.js\u003c/code\u003e는 루트 컨텍스트로 인식하게 된다. 따라서 나중에 Home과 같은 화면은 \u003ccode\u003eindex.js\u003c/code\u003e에서 컴포넌트를 구현하면 된다.\u003c/p\u003e\n\u003ch3 id=\"nextjs\"\u003eNext.js로 정적 웹사이트 만들기 목차\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-06-nextjs-static-website-1/\"\u003eNext.js로 정적 웹사이트 만들기 - 1. Next.js 구조\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-20-nextjs-static-website-2/\"\u003eNext.js로 정적 웹사이트 만들기 - 2. 프로젝트 구성\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-21-nextjs-static-website-3/\"\u003eNext.js로 정적 웹사이트 만들기 - 3. 레이아웃\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-24-nextjs-static-website-4/\"\u003eNext.js로 정적 웹사이트 만들기 - 4. Routing 사용하기\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-28-nextjs-static-website-5/\"\u003eNext.js로 정적 웹사이트 만들기 - 5. Material UI 적용하기\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","preview":"사실 Next.js는 설명할게 별로 없다. 예제들이 너무 잘되어 있기 때문에.. 그렇지만 내 머릿속에서 사라져가기 때문에 기록이라도 남기려고\n작성한다.\n\n1. NPM 프로젝트 생성\n프로젝트를 구성하기 위해 static-website 디렉토리를 하나 만들고 cd static-website로 가서 npm init으로 npm\n프로젝트를 만들자.\n\n\u003e 참고로 n...","title":"Next.js로 정적 웹사이트 만들기 - 2. 프로젝트 구성","author":"salgum1114","date":"2019-05-20 12:43","tags":"react, nextjs, website","cover":"/static/images/covers/nextjs.png","next":"/etc/2019-05-20-using-openssl-in-windows","prev":"/nextjs/2019-05-21-nextjs-static-website-3"},"/etc/2019-05-20-using-openssl-in-windows":{"path":"/etc/2019-05-20-using-openssl-in-windows","content":"\u003cp\u003e회사에서 MQTT를 SSL/TLS로 테스트해야 하는 경우가 생겼는데, 기존에 나와있는 \u003ca href=\"https://mosquitto.org/\"\u003eEclipse Mosquitto\u003c/a\u003e로 하려다가 이왕 하는 김에 Javascrpt로 MQTT Broker랑 Client (Pub, Sub)를 직접 구현해보기로 했다.\u003c/p\u003e\n\u003cp\u003e전체적으로 MQTT Broker와 Client를 구현하기 위한 라이브러리들은 다음과 같다.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/mcollina/mosca\"\u003eMosca\u003c/a\u003e (MQTT Broker) \u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/mqttjs/MQTT.js\"\u003eMQTT.js\u003c/a\u003e (MQTT Client)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e두개의 라이브러리를 받고 예제를 각 라이브러리들의 github에서 사용법을 찾아보면 된다.\u003c/p\u003e\n\u003ch2 id=\"moscaserverts\"\u003eMosca 예제 (server.ts)\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e mosca \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'mosca'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { moscaSettings } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'./options'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e server = \u003cspan class=\"hljs-keyword\"\u003enew\u003c/span\u003e mosca.Server(moscaSettings, () =\u0026gt; {\n \u003cspan class=\"hljs-built_in\"\u003econsole\u003c/span\u003e.log(\u003cspan class=\"hljs-string\"\u003e'Mosca server is up and running'\u003c/span\u003e);\n});\n\nserver.on(\u003cspan class=\"hljs-string\"\u003e'clientConnected'\u003c/span\u003e, (client: mosca.Client) =\u0026gt; {\n \u003cspan class=\"hljs-built_in\"\u003econsole\u003c/span\u003e.log(\u003cspan class=\"hljs-string\"\u003e'Client connected'\u003c/span\u003e, client.id, \u003cspan class=\"hljs-keyword\"\u003enew\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eDate\u003c/span\u003e());\n});\n\nserver.on(\u003cspan class=\"hljs-string\"\u003e'clientDisconnected'\u003c/span\u003e, (client: mosca.Client) =\u0026gt; {\n \u003cspan class=\"hljs-built_in\"\u003econsole\u003c/span\u003e.log(\u003cspan class=\"hljs-string\"\u003e'Client Disconnected:'\u003c/span\u003e, client.id, \u003cspan class=\"hljs-keyword\"\u003enew\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eDate\u003c/span\u003e());\n});\n\nserver.on(\u003cspan class=\"hljs-string\"\u003e'published'\u003c/span\u003e, (packet: mosca.Packet, client?: mosca.Client, callback?: \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e) =\u0026gt; {\n \u003cspan class=\"hljs-built_in\"\u003econsole\u003c/span\u003e.log(\u003cspan class=\"hljs-string\"\u003e'Published'\u003c/span\u003e, packet.payload, client \u0026amp;\u0026amp; client.id, callback);\n});\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"mqttjspublisherts\"\u003eMQTT.js 예제 (publisher.ts)\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e mqtt \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'mqtt'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { mqttClientOptions, mqttsClientOptions } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'./options'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e client = mqtt.connect(mqttsClientOptions);\n\nclient.on(\u003cspan class=\"hljs-string\"\u003e'connect'\u003c/span\u003e, () =\u0026gt; {\n client.subscribe(\u003cspan class=\"hljs-string\"\u003e'presence'\u003c/span\u003e);\n client.publish(\u003cspan class=\"hljs-string\"\u003e'presence'\u003c/span\u003e, \u003cspan class=\"hljs-string\"\u003e'Hello mqtt'\u003c/span\u003e);\n client.end();\n});\n\nclient.on(\u003cspan class=\"hljs-string\"\u003e'error'\u003c/span\u003e, (error) =\u0026gt; {\n \u003cspan class=\"hljs-built_in\"\u003econsole\u003c/span\u003e.log(error);\n});\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"mqttjssubsriberts\"\u003eMQTT.js 예제 (subsriber.ts)\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e mqtt \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'mqtt'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { mqttClientOptions, mqttsClientOptions } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'./options'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e client = mqtt.connect(mqttsClientOptions);\n\nclient.on(\u003cspan class=\"hljs-string\"\u003e'connect'\u003c/span\u003e, () =\u0026gt; {\n client.subscribe(\u003cspan class=\"hljs-string\"\u003e'presence'\u003c/span\u003e);\n});\n\nclient.on(\u003cspan class=\"hljs-string\"\u003e'message'\u003c/span\u003e, (topic, message) =\u0026gt; {\n \u003cspan class=\"hljs-built_in\"\u003econsole\u003c/span\u003e.log(topic, message);\n client.end();\n});\n\nclient.on(\u003cspan class=\"hljs-string\"\u003e'error'\u003c/span\u003e, (error) =\u0026gt; {\n \u003cspan class=\"hljs-built_in\"\u003econsole\u003c/span\u003e.log(error);\n});\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"optionsts\"\u003e옵션 (options.ts)\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { IClientOptions } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'mqtt'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e fs \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'fs'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e interface ClientOptions extends IClientOptions {\n passphrase?: string;\n secureProtocol?: string;\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e interface MoscaLogger {\n name?: string;\n level?: number | string;\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e interface MoscaSecure {\n port?: number;\n keyPath?: string;\n certPath?: string;\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e interface MoscaSettings {\n \u003cspan class=\"hljs-attr\"\u003eport\u003c/span\u003e: number;\n logger?: MoscaLogger;\n secure?: MoscaSecure;\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e mqttClientOptions: ClientOptions = {\n \u003cspan class=\"hljs-attr\"\u003ehost\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'localhost'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eport\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e1883\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eprotocol\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'mqtt'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003econnectTimeout\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e10\u003c/span\u003e * \u003cspan class=\"hljs-number\"\u003e1000\u003c/span\u003e,\n};\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e mqttsClientOptions: ClientOptions = {\n \u003cspan class=\"hljs-attr\"\u003ehost\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'localhost'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eport\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e8443\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eprotocol\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'mqtts'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eprotocolId\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'MQIsdp'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eprotocolVersion\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e3\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003esecureProtocol\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'TLSv1_method'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003ereconnectPeriod\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e5\u003c/span\u003e * \u003cspan class=\"hljs-number\"\u003e1000\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003erejectUnauthorized\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003econnectTimeout\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e10\u003c/span\u003e * \u003cspan class=\"hljs-number\"\u003e1000\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eca\u003c/span\u003e: [fs.readFileSync(\u003cspan class=\"hljs-string\"\u003e'keystore/ca/ca.crt'\u003c/span\u003e)],\n \u003cspan class=\"hljs-attr\"\u003ekey\u003c/span\u003e: fs.readFileSync(\u003cspan class=\"hljs-string\"\u003e'keystore/client/client.key'\u003c/span\u003e),\n \u003cspan class=\"hljs-attr\"\u003ecert\u003c/span\u003e: fs.readFileSync(\u003cspan class=\"hljs-string\"\u003e'keystore/client/client.crt'\u003c/span\u003e),\n};\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e moscaSettings: MoscaSettings = {\n \u003cspan class=\"hljs-attr\"\u003eport\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e1883\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003elogger\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003ename\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e\"secure\"\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003elevel\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e40\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003esecure\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003eport\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e8443\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003ekeyPath\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'keystore/server/server.key'\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003ecertPath\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'keystore/server/server.crt'\u003c/span\u003e,\n },\n};\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e이제 문제는 Openssl 인데, Windows에서 Openssl을 사용하기 위해 2가지 방법이 있다.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e바이너리로 묵인 것을 다운로드 받기.\u003c/li\u003e\n\u003cli\u003e소스를 받아서 직접 빌드하기.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e2번은 귀찮기 때문에, 1번을 찾아보고 다운로드 받았다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e\u003ca href=\"https://code.google.com/archive/p/openssl-for-windows/downloads\"\u003ehttps://code.google.com/archive/p/openssl-for-windows/downloads\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e다운로드 받고나면 압축을 푼 경로로 들어가 \u003ccode\u003ebin\u003c/code\u003e에서 \u003ccode\u003eWindows Command Line\u003c/code\u003e으로 직접 \u003ccode\u003eopenssl\u003c/code\u003e명령어를 치면 되지만, 매번 할 수 없기 때문에 이것도 javascript로 \u003ccode\u003ekeygen\u003c/code\u003e을 만들었다.\u003c/p\u003e\n\u003cp\u003e프로젝트 경로로 맞춰 명령어를 실행하기 위해 openssl을 압축을 풀어 프로젝트 내부로 옮기고, 마찬가지로 openssl로 생성된 키와 인증서를 프로젝트 내부에 두기 위해 프로젝트 내에 \u003ccode\u003ekeystore\u003c/code\u003e 폴더를 만든다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003eOpenssl로 생성해야되는 키와 인증서는 자체 서명된 CA, Server, Client가 모두 필요하기 때문에 keystore 하위 폴더로 ca, server, client를 만든다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e위에 작성된 MQTT Broker, Client, Options를 포함하여 이렇게 생긴 폴더 구조가 생길 것이다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/openssl-project-directory.png\" alt=\"project-directory\" /\u003e\u003c/p\u003e\n\u003cp\u003e그 다음 openssl 명령어를 실행하기 위한 Keygen을 작성한다.\u003c/p\u003e\n\u003ch2 id=\"keygenkeygents\"\u003eKeygen (keygen.ts)\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { execSync, ExecSyncOptions } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'child_process'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e path \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'path'\u003c/span\u003e;\n\ntype Status = \u003cspan class=\"hljs-string\"\u003e'all'\u003c/span\u003e | \u003cspan class=\"hljs-string\"\u003e'ca'\u003c/span\u003e | \u003cspan class=\"hljs-string\"\u003e'server'\u003c/span\u003e | \u003cspan class=\"hljs-string\"\u003e'client'\u003c/span\u003e | \u003cspan class=\"hljs-string\"\u003e'verify'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e argsLength = process.argv.length;\n\u003cspan class=\"hljs-keyword\"\u003elet\u003c/span\u003e status: Status = \u003cspan class=\"hljs-string\"\u003e'all'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (argsLength \u0026gt; \u003cspan class=\"hljs-number\"\u003e2\u003c/span\u003e) {\n status = process.argv.slice(\u003cspan class=\"hljs-number\"\u003e2\u003c/span\u003e)[\u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e] \u003cspan class=\"hljs-keyword\"\u003eas\u003c/span\u003e Status;\n}\n\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e opensslPath = \u003cspan class=\"hljs-string\"\u003e'openssl/bin/openssl'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e opensslConfPath = \u003cspan class=\"hljs-string\"\u003e`openssl/openssl.cnf`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e binPath = path.resolve(__dirname, opensslPath)\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e confPath = path.resolve(__dirname, opensslConfPath);\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e execSyncOption: ExecSyncOptions = {\n \u003cspan class=\"hljs-attr\"\u003estdio\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'inherit'\u003c/span\u003e,\n}\n\n\u003cspan class=\"hljs-comment\"\u003e/**\n * Create CA\n */\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e caKeystorePath = \u003cspan class=\"hljs-string\"\u003e'keystore/ca'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e caKeyPath = \u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${caKeystorePath}\u003c/span\u003e/ca.key`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e caCrtPath = \u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${caKeystorePath}\u003c/span\u003e/ca.crt`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (status === \u003cspan class=\"hljs-string\"\u003e'all'\u003c/span\u003e || status === \u003cspan class=\"hljs-string\"\u003e'ca'\u003c/span\u003e) {\n execSync(\u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${binPath}\u003c/span\u003e req -new -x509 -days 1024 -extensions v3_ca -keyout \u003cspan class=\"hljs-subst\"\u003e${caKeyPath}\u003c/span\u003e -out \u003cspan class=\"hljs-subst\"\u003e${caCrtPath}\u003c/span\u003e -config \u003cspan class=\"hljs-subst\"\u003e${confPath}\u003c/span\u003e`\u003c/span\u003e, execSyncOption);\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (status === \u003cspan class=\"hljs-string\"\u003e'verify'\u003c/span\u003e) {\n execSync(\u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${binPath}\u003c/span\u003e x509 -text -in \u003cspan class=\"hljs-subst\"\u003e${caCrtPath}\u003c/span\u003e`\u003c/span\u003e, execSyncOption) ;\n}\n\n\u003cspan class=\"hljs-comment\"\u003e/**\n * Create server key\n */\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e serverKeystorePath = \u003cspan class=\"hljs-string\"\u003e'keystore/server'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e serverKeyPath = \u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${serverKeystorePath}\u003c/span\u003e/server.key`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e serverCsrPath = \u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${serverKeystorePath}\u003c/span\u003e/server.csr`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e serverCrtPath = \u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${serverKeystorePath}\u003c/span\u003e/server.crt`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (status === \u003cspan class=\"hljs-string\"\u003e'all'\u003c/span\u003e || status === \u003cspan class=\"hljs-string\"\u003e'server'\u003c/span\u003e) {\n \u003cspan class=\"hljs-comment\"\u003e// execSync(`${binPath} genrsa -des3 -out ${serverKeyPath} 2048`, execSyncOption);\u003c/span\u003e\n execSync(\u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${binPath}\u003c/span\u003e genrsa -out \u003cspan class=\"hljs-subst\"\u003e${serverKeyPath}\u003c/span\u003e 2048`\u003c/span\u003e, execSyncOption);\n execSync(\u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${binPath}\u003c/span\u003e req -out \u003cspan class=\"hljs-subst\"\u003e${serverCsrPath}\u003c/span\u003e -key \u003cspan class=\"hljs-subst\"\u003e${serverKeyPath}\u003c/span\u003e -new -config \u003cspan class=\"hljs-subst\"\u003e${confPath}\u003c/span\u003e`\u003c/span\u003e, execSyncOption);\n execSync(\u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${binPath}\u003c/span\u003e x509 -req -in \u003cspan class=\"hljs-subst\"\u003e${serverCsrPath}\u003c/span\u003e -CA \u003cspan class=\"hljs-subst\"\u003e${caCrtPath}\u003c/span\u003e -CAkey \u003cspan class=\"hljs-subst\"\u003e${caKeyPath}\u003c/span\u003e -CAcreateserial -out \u003cspan class=\"hljs-subst\"\u003e${serverCrtPath}\u003c/span\u003e -days 1024`\u003c/span\u003e, execSyncOption);\n}\n\n\u003cspan class=\"hljs-comment\"\u003e/**\n * Create client key\n */\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e clientKeystorePath = \u003cspan class=\"hljs-string\"\u003e'keystore/client'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e clientKeyPath = \u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${clientKeystorePath}\u003c/span\u003e/client.key`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e clientCsrPath = \u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${clientKeystorePath}\u003c/span\u003e/client.csr`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e clientCrtPath = \u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${clientKeystorePath}\u003c/span\u003e/client.crt`\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (status === \u003cspan class=\"hljs-string\"\u003e'all'\u003c/span\u003e || status === \u003cspan class=\"hljs-string\"\u003e'client'\u003c/span\u003e) {\n \u003cspan class=\"hljs-comment\"\u003e// execSync(`${binPath} genrsa -des3 -out ${clientKeyPath} 2048`, execSyncOption);\u003c/span\u003e\n execSync(\u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${binPath}\u003c/span\u003e genrsa -out \u003cspan class=\"hljs-subst\"\u003e${clientKeyPath}\u003c/span\u003e 2048`\u003c/span\u003e, execSyncOption);\n execSync(\u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${binPath}\u003c/span\u003e req -out \u003cspan class=\"hljs-subst\"\u003e${clientCsrPath}\u003c/span\u003e -key \u003cspan class=\"hljs-subst\"\u003e${clientKeyPath}\u003c/span\u003e -new -config \u003cspan class=\"hljs-subst\"\u003e${confPath}\u003c/span\u003e`\u003c/span\u003e, execSyncOption);\n execSync(\u003cspan class=\"hljs-string\"\u003e`\u003cspan class=\"hljs-subst\"\u003e${binPath}\u003c/span\u003e x509 -req -in \u003cspan class=\"hljs-subst\"\u003e${clientCsrPath}\u003c/span\u003e -CA \u003cspan class=\"hljs-subst\"\u003e${caCrtPath}\u003c/span\u003e -CAkey \u003cspan class=\"hljs-subst\"\u003e${caKeyPath}\u003c/span\u003e -CAcreateserial -out \u003cspan class=\"hljs-subst\"\u003e${clientCrtPath}\u003c/span\u003e -days 1024`\u003c/span\u003e, execSyncOption);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e최종 적으로 npm script를 작성한다.\u003c/p\u003e\n\u003ch2 id=\"packagejsonscript\"\u003epackage.json script\u003c/h2\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/openssl-npm-script.png\" alt=\"npm-script\" /\u003e\u003c/p\u003e\n\u003ch2 id=\"tldr\"\u003eTL;DR\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003enpm run keygen\u003c/code\u003e을 실행하면 CA, Server, Client 순으로 키와 인증서를 생성하기 위한 입력들이 나오고 질문에 맞춰 입력하면 되고, 최종적으로 keystore 폴더 하위에 ca, server, client에 키와 인증서들이 생성된다.\u003c/p\u003e\n\u003cp\u003e그런 다음, \u003ccode\u003enpm start\u003c/code\u003e, \u003ccode\u003enpm run sub\u003c/code\u003e, \u003ccode\u003enpm run pub\u003c/code\u003e을 실행하여 메시지를 주고 받는 것을 확인해보면 된다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/etc/openssl-mqtt-result.png\" alt=\"mqtt-result\" /\u003e\u003c/p\u003e\n\u003cp\u003e아래 완성본을 받아서 테스트 해보면 된다.\u003c/p\u003e\n\u003ch2 id=\"\"\u003e완성된 예제\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/salgum1114/mqtt-ssl\"\u003ehttps://github.com/salgum1114/mqtt-ssl\u003c/a\u003e\u003c/p\u003e","preview":"회사에서 MQTT를 SSL/TLS로 테스트해야 하는 경우가 생겼는데, 기존에 나와있는 Eclipse Mosquitto로 하려다가 이왕 하는 김에\nJavascrpt로 MQTT Broker랑 Client (Pub, Sub)를 직접 구현해보기로 했다.\n\n전체적으로 MQTT Broker와 Client를 구현하기 위한 라이브러리들은 다음과 같다.\n\n * Mosca ...","title":"Windows에서 openssl로 인증서 생성하고 MQTT로 테스트하기","author":"salgum1114","date":"2019-05-20 10:09","tags":"openssl, typescript, mqtt","cover":"/static/images/covers/openssl.png","next":"/reactjs/2019-05-11-using-typescript-without-tsx","prev":"/nextjs/2019-05-20-nextjs-static-website-2"},"/reactjs/2019-05-11-using-typescript-without-tsx":{"path":"/reactjs/2019-05-11-using-typescript-without-tsx","content":"\u003cp\u003e이번 글에서는 기존에 .js, .jsx 확장자로 작성된 React 컴포넌트에서 Typescript를 적용하여 컴포넌트 개발이나 사용 시에 타입을 확인할 수 있는 방법에 대해서 설명하려고 한다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eButton.js\u003c/code\u003e 컴포넌트로 간단한 예를 들어본다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs\"\u003esrc/\n components/\n button/\n index\u003cspan class=\"hljs-selector-class\"\u003e.js\u003c/span\u003e\n Button\u003cspan class=\"hljs-selector-class\"\u003e.js\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e구조로 컴포넌트가 작성되어 있다고한다면. 동일한 파일 경로에 \u003ccode\u003eindex.d.ts\u003c/code\u003e를 만들자.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs diff language-diff\"\u003esrc/\n components/\n button/\n index.js\n + index.d.ts\n Button.js\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003eindex.d.ts\u003c/code\u003e에는 \u003ccode\u003ebutton\u003c/code\u003e에 필요한 \u003ccode\u003eProps\u003c/code\u003e들을 Typescript로 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e interface ButtonProps extends React.HTMLAttributes\u0026lt;HTMLButtonElement\u0026gt; {}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eButton\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eReact\u003c/span\u003e.\u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eButtonProps\u003c/span\u003e\u0026gt; \u003c/span\u003e{}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cblockquote\u003e\n \u003cp\u003e이 예제에서는 별다른 Props가 필요없이 Button 컴포넌트의 Typescript를 작성하는데 중점을 둔다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e그럼 이제 \u003ccode\u003eButton.js\u003c/code\u003e를 작성한다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React, { Component } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-comment\"\u003e/**\n * @augments {Component\u0026lt;import('.').ButtonProps\u0026gt;}\n */\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eButton\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { children, ...other } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e {\u003cspan class=\"hljs-attr\"\u003e...other\u003c/span\u003e}\u0026gt;\u003c/span\u003e{children}\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e;\n }\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e Button;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e이렇게 작성된 \u003ccode\u003eButton.js\u003c/code\u003e 컴포넌트의 \u003ccode\u003ethis.props\u003c/code\u003e의 deconstructing 부분에서 \u003ccode\u003ectrl + space\u003c/code\u003e로 힌트를 쳐보면 다음과 같이 제공할 수 있는 props들이 힌트로 나오게된다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/reactjs/define-typescript-hint.png\" alt=\"define-typescript-hint\" /\u003e\u003c/p\u003e\n\u003cp\u003e이제 이 \u003ccode\u003eButton.js\u003c/code\u003e 컴포넌트를 가지고 다른 컴포넌트에서 사용해본다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/reactjs/using-typescript-hint.png\" alt=\"using-typescript-hint\" /\u003e\u003c/p\u003e\n\u003cp\u003e추가적으로 \u003ccode\u003efunctional component\u003c/code\u003e에서는 다음처럼 작성할 수 있다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React, { Component } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-comment\"\u003e/**\n * @description\n * @param {import('.').ButtonProps} props\n * @returns {React.ReactNode}\n */\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e Button = \u003cspan class=\"hljs-function\"\u003e(\u003cspan class=\"hljs-params\"\u003eprops\u003c/span\u003e) =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { children, ...other } = props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e {\u003cspan class=\"hljs-attr\"\u003e...other\u003c/span\u003e}\u0026gt;\u003c/span\u003e{children}\u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003ebutton\u003c/span\u003e\u0026gt;\u003c/span\u003e\u003c/span\u003e\n )\n}\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e Button;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e힌트도 똑같이 사용할 수 있다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/reactjs/functional-using-typescript-hint.png\" alt=\"functional-using-typescript-hint\" /\u003e\u003c/p\u003e","preview":"이번 글에서는 기존에 .js, .jsx 확장자로 작성된 React 컴포넌트에서 Typescript를 적용하여 컴포넌트 개발이나 사용 시에 타입을\n확인할 수 있는 방법에 대해서 설명하려고 한다.\n\nButton.js 컴포넌트로 간단한 예를 들어본다.\n\nsrc/\n components/\n button/\n index.js\n ...","title":"React에서 .tsx 없이 Typescript 사용하기","author":"salgum1114","date":"2019-05-11 19:50","tags":"react, typescript","cover":"/static/images/covers/react_typescript.png","next":"/nextjs/2019-05-06-nextjs-static-website-1","prev":"/etc/2019-05-20-using-openssl-in-windows"},"/nextjs/2019-05-06-nextjs-static-website-1":{"path":"/nextjs/2019-05-06-nextjs-static-website-1","content":"\u003cp\u003e\u003ca href=\"https://nextjs.org/\"\u003eNext.js\u003c/a\u003e는 현재 가장 유명한 \u003ca href=\"https://reactjs.org/\"\u003eReact\u003c/a\u003e용 서버 사이드 렌더링 프레임워크이다.\u003c/p\u003e\n\u003cp\u003e프레임워크로써 이해하기 쉬운 구조와 \u003ca href=\"https://github.com/zeit/next.js/tree/canary/examples\"\u003e수많은 예제\u003c/a\u003e들, 그리고 다양한 플러그인들을 지원하고 있어 많은 사람들에게 사랑받고 있다.\u003c/p\u003e\n\u003cp\u003e또한, Next.js는 \u003ccode\u003enext export\u003c/code\u003e라는 명령어로 \u003ccode\u003erouting 경로\u003c/code\u003e로 하여금 정적 웹사이트를 만들 수 있는 기능도 제공하고 있다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003eNext.js의 최신 버전은 8.x.x인데 최신 버전에서는 export 기능이 제대로 동작하지 않으며, 현재 \u003ccode\u003enext export\u003c/code\u003e 기능은 7.x.x 버전에서 에러없이 잘 동작한다. \u003ca href=\"https://github.com/zeit/next.js/issues/6251\"\u003e[Static export failed when upgraded to v8.0.0 #6251]\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e이 글은 Next.js의 export 기능을 사용하여 정적 웹사이트를 만드는 것에 대해서 설명하려고 한다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e이 글은 서버 사이드 렌더링이 아닌 정적 웹사이트를 만드는 것에 중점으로 설명한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"nextjs\"\u003eNext.js 기본 구조\u003c/h2\u003e\n\u003cp\u003eNext.js의 기본 구조는 다음처럼 구성된다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003epages/ \u003cspan class=\"hljs-comment\"\u003e// HTML Document, Application Container, 각종 페이지 등을 작성한다.\u003c/span\u003e\n _document.js \u003cspan class=\"hljs-comment\"\u003e// HTML Document.\u003c/span\u003e\n _app.js \u003cspan class=\"hljs-comment\"\u003e// Application Container. 공통의 레이아웃을 작성한다.\u003c/span\u003e\n _error.js \u003cspan class=\"hljs-comment\"\u003e// Error Page.\u003c/span\u003e\n index.js \u003cspan class=\"hljs-comment\"\u003e// Root Page /로 시작되는 경로를 말한다.\u003c/span\u003e\n hello.js \u003cspan class=\"hljs-comment\"\u003e// Hello Page /hello로 시작되는 경로를 말한다.\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e/ \u003cspan class=\"hljs-comment\"\u003e// 정적 파일 (이미지, 파일 등)을 업로드 한다.\u003c/span\u003e\nnext.config.js \u003cspan class=\"hljs-comment\"\u003e// Next.js의 환경 설정 파일이다. 라우팅 설정, typescript, less 등의 webpack 플러그인을 설정한다.\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"_documentjs\"\u003e_document.js\u003c/h2\u003e\n\u003cp\u003e_document.js는 SPA에서 시작점이 되는 index.html이라고 생각하면 된다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003eCustom Document를 만들때만 작성이 필요하며, 생략된 경우 Next.js가 기본 값을 사용한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e작성 예시는 다음과 같다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Document, { Head, Main, NextScript } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/document'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootDocument\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDocument\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u0026lt;html\u0026gt;\n \u0026lt;Head\u0026gt;\n \u0026lt;meta charSet=\"utf-8\" /\u0026gt;\n \u0026lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no\" /\u0026gt;\n \u0026lt;meta name=\"description\" content=\"Dev.log\"/\u0026gt;\n \u0026lt;meta name=\"keywords\" content=\"blog,react,antd,webpack,css,javascript\" /\u0026gt;\n \u0026lt;link rel=\"manifest\" href=\"/static/manifest.json\" /\u0026gt;\n \u0026lt;link rel=\"shortcut icon\" href=\"/static/favicon.ico\" /\u0026gt;\n \u0026lt;link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/earlyaccess/notosanskr.css\" /\u0026gt;\n \u0026lt;link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.min.css\" /\u0026gt;\n \u0026lt;link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/styles/railscasts.min.css\" /\u0026gt;\n \u0026lt;/Head\u0026gt;\n \u0026lt;body\u0026gt;\n \u0026lt;Main /\u0026gt;\n \u0026lt;NextScript /\u0026gt;\n \u0026lt;/body\u0026gt;\n \u0026lt;/html\u0026gt;\n );\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e_document.js에서 React와 같은 동작을 수행할 수 있을 것처럼 보이지만 \u003ccode\u003eReact Lifecycle\u003c/code\u003e과 \u003ccode\u003eData Fetching\u003c/code\u003e이 불가능하다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/nextjs/document-app-differences.png\" alt=\"document-app-differences\" /\u003e\u003c/p\u003e\n\u003cp\u003eReact에서는 \u003ccode\u003e\u0026lt;div id=\"root\" /\u0026gt;\u003c/code\u003e처럼 React 컴포넌트가 마운트 되는 최초 요소를 작성하게 된다. 하지만 Next.js에서는 따로 작성할 필요 없이, \u003ccode\u003eMain\u003c/code\u003e과 \u003ccode\u003eNextScript\u003c/code\u003e 클래스에 내장 되어 있다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eMain\u003c/code\u003e과 \u003ccode\u003eNextScript\u003c/code\u003e의 자세한 설명은 이 글을 참조한다. \u003ca href=\"https://stackoverflow.com/questions/52083848/nextjs-main-and-nextscript\"\u003eNextJS: Main and Nextscript\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"_appjs\"\u003e_app.js\u003c/h2\u003e\n\u003cp\u003eReact에서 대부분 App.js이라는 이름으로 공통의 레이아웃을 작성하듯이 Next.js에서는 Application의 공통 레이아웃을 _app.js에서 작성할 수 있다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e생략된 경우 Next.js가 기본 값을 사용한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e작성 예시는 다음과 같다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e App, { Container } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/app'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e NProgress \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'nprogress'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Router \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'next/router'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Helmet \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react-helmet'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e moment \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'moment'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e Layout \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'../components/Layout'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'../styles/index.less'\u003c/span\u003e;\n\nmoment.locale(\u003cspan class=\"hljs-string\"\u003e'ko'\u003c/span\u003e);\n\nRouter.events.on(\u003cspan class=\"hljs-string\"\u003e'routeChangeStart'\u003c/span\u003e, (url) =\u0026gt; NProgress.start());\nRouter.events.on(\u003cspan class=\"hljs-string\"\u003e'routeChangeComplete'\u003c/span\u003e, () =\u0026gt; NProgress.done());\nRouter.events.on(\u003cspan class=\"hljs-string\"\u003e'routeChangeError'\u003c/span\u003e, () =\u0026gt; NProgress.done());\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootApp\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eApp\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { Component, ...other } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u0026lt;Container\u0026gt;\n \u0026lt;Helmet title=\"Dev.log\" /\u0026gt;\n \u0026lt;Layout {...other} {...this.state}\u0026gt;\n \u0026lt;Component {...other} {...this.state} /\u0026gt;\n \u0026lt;/Layout\u0026gt;\n \u0026lt;/Container\u0026gt;\n );\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e이곳에서 ComponentDidCatch로 Error Handling을 한다던지, Routing이 되었을 때의 Progressing 처리, 스타일 시트 Import 등을 작성한다.\u003c/p\u003e\n\u003ch2 id=\"_errorjs\"\u003e_error.js\u003c/h2\u003e\n\u003cp\u003eError 처리를 공통으로 하고자 할 때, 공통적으로 사용할 수 있는 Error Page를 작성할 수 있다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e생략된 경우 Next.js가 기본 값을 사용한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e작성 예시는 다음과 같다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React, { Component } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e ErrorPage \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'../components/ErrorPage'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eexport\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003edefault\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRootError\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n render() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { statusCode } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eErrorPage\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estatusCode\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{statusCode}\u003c/span\u003e /\u0026gt;\u003c/span\u003e;\n }\n}\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"nextconfigjs\"\u003enext.config.js\u003c/h2\u003e\n\u003cp\u003ewebpack plugin들과 Next.js의 라우팅 설정을 작성한다.\u003c/p\u003e\n\u003cp\u003e작성 예시는 다음과 같다.\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e withLess = \u003cspan class=\"hljs-built_in\"\u003erequire\u003c/span\u003e(\u003cspan class=\"hljs-string\"\u003e'@zeit/next-less'\u003c/span\u003e);\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e withTypescript = \u003cspan class=\"hljs-built_in\"\u003erequire\u003c/span\u003e(\u003cspan class=\"hljs-string\"\u003e'@zeit/next-typescript'\u003c/span\u003e);\n\n\u003cspan class=\"hljs-comment\"\u003e// fix: prevents error when .less files are required by node\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (\u003cspan class=\"hljs-keyword\"\u003etypeof\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003erequire\u003c/span\u003e !== \u003cspan class=\"hljs-string\"\u003e'undefined'\u003c/span\u003e) {\n \u003cspan class=\"hljs-built_in\"\u003erequire\u003c/span\u003e.extensions[\u003cspan class=\"hljs-string\"\u003e'.less'\u003c/span\u003e] = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003efile\u003c/span\u003e =\u0026gt;\u003c/span\u003e {}\n}\n\n\u003cspan class=\"hljs-built_in\"\u003emodule\u003c/span\u003e.exports = withTypescript(withLess({\n \u003cspan class=\"hljs-attr\"\u003elessLoaderOptions\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003ejavascriptEnabled\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003eexportPathMap\u003c/span\u003e: \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e {\n \u003cspan class=\"hljs-string\"\u003e'/'\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003epage\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'/'\u003c/span\u003e },\n \u003cspan class=\"hljs-string\"\u003e'/about'\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003epage\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'/about'\u003c/span\u003e },\n \u003cspan class=\"hljs-string\"\u003e'/readme.md'\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003epage\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'/readme'\u003c/span\u003e },\n \u003cspan class=\"hljs-string\"\u003e'/p/hello-nextjs'\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003epage\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'/post'\u003c/span\u003e, \u003cspan class=\"hljs-attr\"\u003equery\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003etitle\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'hello-nextjs'\u003c/span\u003e } },\n \u003cspan class=\"hljs-string\"\u003e'/p/learn-nextjs'\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003epage\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'/post'\u003c/span\u003e, \u003cspan class=\"hljs-attr\"\u003equery\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003etitle\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'learn-nextjs'\u003c/span\u003e } },\n \u003cspan class=\"hljs-string\"\u003e'/p/deploy-nextjs'\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003epage\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'/post'\u003c/span\u003e, \u003cspan class=\"hljs-attr\"\u003equery\u003c/span\u003e: { \u003cspan class=\"hljs-attr\"\u003etitle\u003c/span\u003e: \u003cspan class=\"hljs-string\"\u003e'deploy-nextjs'\u003c/span\u003e } }\n },\n}));\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNext.js와 관련된 webpack plugin들은 \u003ccode\u003e@zeit/next\u003c/code\u003e Prefix로 패키지를 제공하고 있으며, 사용할 때는 \u003ccode\u003ewith\u003c/code\u003e prefix로 선언해서 사용한다.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eexportPathMap\u003c/code\u003e이 Next.js의 라우팅 경로이다.\u003c/p\u003e\n\u003ch3 id=\"nextjs-1\"\u003eNext.js로 정적 웹사이트 만들기 목차\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-06-nextjs-static-website-1/\"\u003eNext.js로 정적 웹사이트 만들기 - 1. Next.js 구조\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-20-nextjs-static-website-2/\"\u003eNext.js로 정적 웹사이트 만들기 - 2. 프로젝트 구성\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-21-nextjs-static-website-3/\"\u003eNext.js로 정적 웹사이트 만들기 - 3. 레이아웃\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-24-nextjs-static-website-4/\"\u003eNext.js로 정적 웹사이트 만들기 - 4. Routing 사용하기\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://salgum1114.github.io/nextjs/2019-05-28-nextjs-static-website-5/\"\u003eNext.js로 정적 웹사이트 만들기 - 5. Material UI 적용하기\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","preview":"Next.js는 현재 가장 유명한 React용 서버 사이드 렌더링 프레임워크이다.\n\n프레임워크로써 이해하기 쉬운 구조와 수많은 예제들, 그리고 다양한 플러그인들을 지원하고 있어 많은 사람들에게 사랑받고 있다.\n\n또한, Next.js는 next export라는 명령어로 routing 경로로 하여금 정적 웹사이트를 만들 수 있는 기능도 제공하고 있다.\n\n\u003e ...","title":"Next.js로 정적 웹사이트 만들기 - 1. Next.js 구조","author":"salgum1114","date":"2019-05-06 19:50","tags":"react, nextjs, website","cover":"/static/images/covers/nextjs.png","next":"/reactjs/2019-05-03-medium-zoom","prev":"/reactjs/2019-05-11-using-typescript-without-tsx"},"/reactjs/2019-05-03-medium-zoom":{"path":"/reactjs/2019-05-03-medium-zoom","content":"\u003cp\u003e평소 \u003ca href=\"https://medium.com/\"\u003eMedium\u003c/a\u003e에서 개발 포스팅을 찾아보는 편인데, 글에서 이미지를 클릭했을 때, 줌이 되는 기능이 이뻐보여서 내 개발 블로그에도 적용하고 싶어 비슷한 라이브러리들이 있나 찾아보게 되었다.\u003c/p\u003e\n\u003cp\u003e우선 이 블로그가 React로 개발했으니 React에서 사용할 수 있는 라이브러리를 찾아보니 \u003ca href=\"https://github.com/rpearce/react-medium-image-zoom\"\u003ereact-medium-image-zoom\u003c/a\u003e이 있었다. 하지만 React로 작성해야 하기 때문에, 마크다운이 HTML로 번역된 상태에서는 사용이 불가능해 보인다.\u003c/p\u003e\n\u003cp\u003e이와는 다르게 querySelector로 \u003ccode\u003e\u0026lt;img\u0026gt;\u003c/code\u003e요소들을 파라미터로 전달하기만 하면 사용할 수 있는 \u003ca href=\"https://github.com/francoischalifour/medium-zoom\"\u003emedium-zoom\u003c/a\u003e 라이브러리가 있었다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e\u003ca href=\"https://github.com/francoischalifour/medium-zoom\"\u003emedium-zoom\u003c/a\u003e에 대한 사용 방법은 github에 잘 나와있고 playground로 데모를 확인할 수 있다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"mediumzoom\"\u003emedium-zoom 사용 방법\u003c/h4\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-comment\"\u003e// CSS selector\u003c/span\u003e\nmediumZoom(\u003cspan class=\"hljs-string\"\u003e'[data-zoomable]'\u003c/span\u003e);\n\n\u003cspan class=\"hljs-comment\"\u003e// HTMLElement\u003c/span\u003e\nmediumZoom(\u003cspan class=\"hljs-built_in\"\u003edocument\u003c/span\u003e.querySelector(\u003cspan class=\"hljs-string\"\u003e'#cover'\u003c/span\u003e));\n\n\u003cspan class=\"hljs-comment\"\u003e// NodeList\u003c/span\u003e\nmediumZoom(\u003cspan class=\"hljs-built_in\"\u003edocument\u003c/span\u003e.querySelectorAll(\u003cspan class=\"hljs-string\"\u003e'[data-zoomable]'\u003c/span\u003e));\n\n\u003cspan class=\"hljs-comment\"\u003e// Array\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e images = [\n \u003cspan class=\"hljs-built_in\"\u003edocument\u003c/span\u003e.querySelector(\u003cspan class=\"hljs-string\"\u003e'#cover'\u003c/span\u003e),\n ...document.querySelectorAll(\u003cspan class=\"hljs-string\"\u003e'[data-zoomable]'\u003c/span\u003e),\n];\n\nmediumZoom(images);\n\u003c/code\u003e\u003c/pre\u003e\n\u003cblockquote\u003e\n \u003cp\u003e추가적인 옵션이 있는데 \u003ca href=\"https://github.com/francoischalifour/medium-zoom\"\u003emedium-zoom github\u003c/a\u003e에서 확인하자.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eReact에서 사용할 때는 다음 처럼 사용하면 된다.\u003c/p\u003e\n\u003ch4 id=\"reactmediumzoom\"\u003eReact에서 medium-zoom 사용 방법\u003c/h4\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e mediumZoom \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'medium-zoom'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eMediumZoomExample\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eReact\u003c/span\u003e.\u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e \u003c/span\u003e{\n componentDidMount() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e images = \u003cspan class=\"hljs-built_in\"\u003edocument\u003c/span\u003e.querySelectorAll(\u003cspan class=\"hljs-string\"\u003e'.example-container img'\u003c/span\u003e);\n mediumZoom(images);\n }\n\n render() {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u0026lt;div className=\"example-container\"\u0026gt;\n \u0026lt;img src=\"/test.png\" /\u0026gt;\n \u0026lt;img src=\"/test2.png\" /\u0026gt;\n \u0026lt;/div\u0026gt;\n );\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e블로그에 적용된 결과.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/reactjs/medium-zoom.gif\" alt=\"medium-zoom-apply\" /\u003e\u003c/p\u003e","preview":"평소 Medium에서 개발 포스팅을 찾아보는 편인데, 글에서 이미지를 클릭했을 때, 줌이 되는 기능이 이뻐보여서 내 개발 블로그에도 적용하고\n싶어 비슷한 라이브러리들이 있나 찾아보게 되었다.\n\n우선 이 블로그가 React로 개발했으니 React에서 사용할 수 있는 라이브러리를 찾아보니 react-medium-image-zoom이\n있었다. 하지만 React로...","title":"medium-zoom 적용하기","author":"salgum1114","date":"2019-05-03 12:46","tags":"react, medium-zoom","cover":"/static/images/covers/medium-zoom.gif","next":"/css/2019-04-30-overscroll-behavior-contain","prev":"/nextjs/2019-05-06-nextjs-static-website-1"},"/css/2019-04-30-overscroll-behavior-contain":{"path":"/css/2019-04-30-overscroll-behavior-contain","content":"\u003cp\u003e웹에서 스크롤을 다루다보면 항상 마주치는 문제 중 하나가 \u003cstrong\u003e스크롤 체이닝\u003c/strong\u003e이다.\u003c/p\u003e\n\u003cp\u003e다음 예제를 보자.\u003c/p\u003e\n\u003ciframe height=\"465\" style=\"width: 100%;\" scrolling=\"no\" title=\"overscroll-behavior: cotain before\" src=\"//codepen.io/salgum1114/embed/MRMaWX/?height=265\u0026theme-id=0\u0026default-tab=css,result\" frameborder=\"no\" allowtransparency=\"true\" allowfullscreen=\"true\"\u003e\n See the Pen \u003ca href='https://codepen.io/salgum1114/pen/MRMaWX/'\u003eoverscroll-behavior: cotain before\u003c/a\u003e by Sung Gyun Oh\n (\u003ca href='https://codepen.io/salgum1114'\u003e@salgum1114\u003c/a\u003e) on \u003ca href='https://codepen.io'\u003eCodePen\u003c/a\u003e.\n\u003c/iframe\u003e\n\u003cp\u003e위 예제에서 Box-1의 스크롤이 끝에 도달하게 되면 상위 요소인 App의 스크롤이 시작이된다.\u003c/p\u003e\n\u003cp\u003e심지어 App의 스크롤이 끝에 도달하게 되면 이 포스팅의 본문까지 스크롤이 시작되게 된다.\u003c/p\u003e\n\u003cp\u003e이러한 현상을 \u003cstrong\u003e스크롤 체이닝\u003c/strong\u003e이라고 불리며, 구글 개발자 사이트의 \u003ca href=\"https://developers.google.com/web/updates/2017/11/overscroll-behavior\"\u003eTake control of your scroll: customizing pull-to-refresh and overflow effects\u003c/a\u003e에 설명이 잘 되어있다.\u003c/p\u003e\n\u003cp\u003e여기서 설명하길 CSS의 \u003cstrong\u003eoverscroll-behavior\u003c/strong\u003e속성을 사용하여 스크롤로 일어날 수 있는 다양한 현상을 조절할 수 있다고 한다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e단, 이 속성은 본문의 첫번째 단락에 주의로 크롬 63버전 이상부터 지원하며, 다른 브라우저는 고려 중이라고 작성되어있다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch4 id=\"\"\u003e브라우저 호환성 참고\u003c/h4\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/css/overscroll-behavior-support.png\" alt=\"overscroll-behavior-support\" /\u003e\u003c/p\u003e\n\u003cp\u003e이제 예제에 \u003cstrong\u003eoverscroll-behavior\u003c/strong\u003e 속성을 적용하여 스크롤 체이닝 현상을 피해보자.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003eoverscroll-behavior 속성은 x, y도 따로 다룰 수 있다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ciframe height=\"465\" style=\"width: 100%;\" scrolling=\"no\" title=\"overscroll-behavior: cotain after\" src=\"//codepen.io/salgum1114/embed/EJBVvq/?height=265\u0026theme-id=0\u0026default-tab=css,result\" frameborder=\"no\" allowtransparency=\"true\" allowfullscreen=\"true\"\u003e\n See the Pen \u003ca href='https://codepen.io/salgum1114/pen/EJBVvq/'\u003eoverscroll-behavior: cotain after\u003c/a\u003e by Sung Gyun Oh\n (\u003ca href='https://codepen.io/salgum1114'\u003e@salgum1114\u003c/a\u003e) on \u003ca href='https://codepen.io'\u003eCodePen\u003c/a\u003e.\n\u003c/iframe\u003e\n\u003cp\u003eBox-1에서 스크롤을 끝까지 도달시켜보면 이전과 다르게 상위 요소인 App의 스크롤이 시작되지 않으면서 스크롤 체이닝을 피하게 된다.\u003c/p\u003e\n\u003ch4 id=\"-1\"\u003e참고 사이트\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior\"\u003eMDN: overscroll-behavior\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://developers.google.com/web/updates/2017/11/overscroll-behavior\"\u003eGoogle Developers - Take control of your scroll: customizing pull-to-refresh and overflow effects\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","preview":"웹에서 스크롤을 다루다보면 항상 마주치는 문제 중 하나가 스크롤 체이닝이다.\n\n다음 예제를 보자.\n\nSee the Pen overscroll-behavior: cotain before by Sung Gyun Oh (@salgum1114) on \nCodePen.위 예제에서 Box-1의 스크롤이 끝에 도달하게 되면 상위 요소인 App의 스크롤이 시작이된다.\n\n...","title":"overscroll-behavior: contain 속성 사용하기","author":"salgum1114","date":"2019-04-30 19:28","tags":"css, overscroll-behavior","cover":"/static/images/covers/css.png","next":"/css/2019-04-28-scroll-behavior-smooth","prev":"/reactjs/2019-05-03-medium-zoom"},"/css/2019-04-28-scroll-behavior-smooth":{"path":"/css/2019-04-28-scroll-behavior-smooth","content":"\u003cp\u003e블로그에 부드러운 스크롤링을 위해서 CSS의 \u003ccode\u003escroll-behavior\u003c/code\u003e를 활용해본다.\u003c/p\u003e\n\u003cp\u003e우선, \u003ccode\u003escroll-behavior\u003c/code\u003e이 뭔지 알아보자.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e\u003ca href=\"https://www.w3schools.com/cssref/pr_scroll-behavior.asp\"\u003ew3school: CSS scroll-behavior Property\u003c/a\u003e에 아주 잘 나와있다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e사용 방법은 아주 간단하다. 아래 코드를 확인해보자.\u003c/p\u003e\n\u003ch4 id=\"scrollbehaviorsmooth\"\u003escroll-behavior: smooth 적용 전\u003c/h4\u003e\n\u003ciframe height=\"465\" style=\"width: 100%;\" scrolling=\"no\" title=\"scroll-behavior: smooth before\" src=\"//codepen.io/salgum1114/embed/ZZPBjZ/?height=265\u0026theme-id=0\u0026default-tab=html,result\" frameborder=\"no\" allowtransparency=\"true\" allowfullscreen=\"true\"\u003e\n See the Pen \u003ca href='https://codepen.io/salgum1114/pen/ZZPBjZ/'\u003escroll-behavior: smooth before\u003c/a\u003e by Sung Gyun Oh\n (\u003ca href='https://codepen.io/salgum1114'\u003e@salgum1114\u003c/a\u003e) on \u003ca href='https://codepen.io'\u003eCodePen\u003c/a\u003e.\n\u003c/iframe\u003e\n\u003ch4 id=\"scrollbehaviorsmooth-1\"\u003escroll-behavior: smooth 적용 후\u003c/h4\u003e\n\u003ciframe height=\"465\" style=\"width: 100%;\" scrolling=\"no\" title=\"scroll-behavior: smooth\" src=\"//codepen.io/salgum1114/embed/axMmrP/?height=265\u0026theme-id=0\u0026default-tab=html,result\" frameborder=\"no\" allowtransparency=\"true\" allowfullscreen=\"true\"\u003e\n See the Pen \u003ca href='https://codepen.io/salgum1114/pen/axMmrP/'\u003escroll-behavior: smooth\u003c/a\u003e by Sung Gyun Oh\n (\u003ca href='https://codepen.io/salgum1114'\u003e@salgum1114\u003c/a\u003e) on \u003ca href='https://codepen.io'\u003eCodePen\u003c/a\u003e.\n\u003c/iframe\u003e\n\u003cblockquote\u003e\n \u003cp\u003espeed를 조절할 수 있는지는 모르겠다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e이제 블로그에 적용된 것을 확인해보자.\u003c/p\u003e\n\u003ch4 id=\"bottom\"\u003eBottom\u003c/h4\u003e\n\u003cp\u003e\u003ca href=\"#top\"\u003e아래로 이동\u003c/a\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\u003c/p\u003e\n\u003ch4 id=\"top\"\u003eTop\u003c/h4\u003e\n\u003cp\u003e\u003ca href=\"#bottom\"\u003e위로 이동\u003c/a\u003e\u003c/p\u003e","preview":"블로그에 부드러운 스크롤링을 위해서 CSS의 scroll-behavior를 활용해본다.\n\n우선, scroll-behavior이 뭔지 알아보자.\n\n\u003e w3school: CSS scroll-behavior Property에 아주 잘 나와있다.\n\n\n사용 방법은 아주 간단하다. 아래 코드를 확인해보자.\n\nSCROLL-BEHAVIOR: SMOOTH 적용 전\nSee...","title":"scroll-behavior: smooth 속성 사용하기","author":"salgum1114","date":"2019-04-28 16:28","tags":"css, scroll-behavior","cover":"/static/images/covers/css.png","next":"/reactjs/2019-04-25-back-top-component","prev":"/css/2019-04-30-overscroll-behavior-contain"},"/reactjs/2019-04-25-back-top-component":{"path":"/reactjs/2019-04-25-back-top-component","content":"\u003cp\u003e블로그 개발 중 스크롤이 되었을 때, 상위로 이동시키는 \u003ccode\u003eBackTop\u003c/code\u003e을 적용하기 위해 \u003ca href=\"https://ant.design/components/back-top/\"\u003eAnt.Design의 BackTop 컴포넌트\u003c/a\u003e를 사용하려고 했다.\u003c/p\u003e\n\u003cp\u003e하지만 \u003ca href=\"https://ant.design/components/back-top/\"\u003eAnt.Design의 BackTop 컴포넌트\u003c/a\u003e를 적용해보니 잘 안된다. 따라서 직접 \u003ccode\u003eBackTop\u003c/code\u003e 컴포넌트를 구현하기로 결정했다.\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003eAnt.Design 홈페이지에선 잘 동작한다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e구현 코드는 다음과 같다.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBackTop.tsx\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e React, { Component } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'react'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e { Icon, Button } \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'antd'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e throttle \u003cspan class=\"hljs-keyword\"\u003efrom\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'lodash/throttle'\u003c/span\u003e;\n\ninterface IProps {\n scrollStep?: number;\n delayMs: number;\n target: string;\n}\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBackTop\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eComponent\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eIProps\u003c/span\u003e\u0026gt; \u003c/span\u003e{\n private content: Element;\n private timeoutId: NodeJS.Timeout;\n private intervalId: NodeJS.Timeout;\n\n \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e defaultProps = {\n \u003cspan class=\"hljs-attr\"\u003escrollStep\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e50\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003edelayMs\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e16\u003c/span\u003e,\n }\n\n componentDidMount() {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e { target } = \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props;\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (target) {\n \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.content = \u003cspan class=\"hljs-built_in\"\u003edocument\u003c/span\u003e.querySelector(target);\n \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.content.addEventListener(\u003cspan class=\"hljs-string\"\u003e'scroll'\u003c/span\u003e, \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.onScroll);\n }\n }\n\n onScroll = throttle(\u003cspan class=\"hljs-function\"\u003e(\u003cspan class=\"hljs-params\"\u003ee\u003c/span\u003e) =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (e.target.scrollTop \u0026gt;= \u003cspan class=\"hljs-number\"\u003e64\u003c/span\u003e) {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e backtop = \u003cspan class=\"hljs-built_in\"\u003edocument\u003c/span\u003e.querySelector(\u003cspan class=\"hljs-string\"\u003e'.backtop'\u003c/span\u003e);\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (backtop) {\n content.classList.add(\u003cspan class=\"hljs-string\"\u003e'visible'\u003c/span\u003e);\n }\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.timeoutId) {\n clearTimeout(\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.timeoutId);\n }\n \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.timeoutId = setTimeout(\u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (backtop) {\n backtop.classList.remove(\u003cspan class=\"hljs-string\"\u003e'visible'\u003c/span\u003e);\n }\n }, \u003cspan class=\"hljs-number\"\u003e1500\u003c/span\u003e);\n } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e backtop = \u003cspan class=\"hljs-built_in\"\u003edocument\u003c/span\u003e.querySelector(\u003cspan class=\"hljs-string\"\u003e'.backtop'\u003c/span\u003e);\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (backtop) {\n backtop.classList.remove(\u003cspan class=\"hljs-string\"\u003e'visible'\u003c/span\u003e);\n }\n }\n }, \u003cspan class=\"hljs-number\"\u003e200\u003c/span\u003e);\n\n scrollStep = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.content.scrollTop === \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e) {\n clearInterval(\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.intervalId);\n }\n \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.content.scrollTo(\u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e, \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.content.scrollTop - \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props.scrollStep);\n }\n\n scrollToTop = \u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.content \u0026amp;\u0026amp; \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props.target) {\n \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.intervalId = setInterval(\u003cspan class=\"hljs-function\"\u003e\u003cspan class=\"hljs-params\"\u003e()\u003c/span\u003e =\u0026gt;\u003c/span\u003e {\n \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.scrollStep();\n }, \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.props.delayMs);\n }\n }\n\n render() {\n \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e (\n \u003cspan class=\"xml\"\u003e\u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eButton\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eclassName\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"backtop\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003etype\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"primary\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eshape\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"circle\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003eonClick\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{this.scrollToTop}\u003c/span\u003e\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;\u003cspan class=\"hljs-name\"\u003eIcon\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003etype\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e\"to-top\"\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003estyle\u003c/span\u003e=\u003cspan class=\"hljs-string\"\u003e{{\u003c/span\u003e \u003cspan class=\"hljs-attr\"\u003efontSize:\u003c/span\u003e '\u003cspan class=\"hljs-attr\"\u003e1.25rem\u003c/span\u003e' }} /\u0026gt;\u003c/span\u003e\n \u003cspan class=\"hljs-tag\"\u003e\u0026lt;/\u003cspan class=\"hljs-name\"\u003eButton\u003c/span\u003e\u0026gt;\u003c/span\u003e\n );\n }\n}\n\nexport default BackTop;\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cstrong\u003ebacktop.less\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs less language-less\"\u003e\u003cspan class=\"hljs-selector-class\"\u003e.backtop\u003c/span\u003e {\n \u003cspan class=\"hljs-attribute\"\u003ewidth\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e40px\u003c/span\u003e;\n \u003cspan class=\"hljs-attribute\"\u003eheight\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e40px\u003c/span\u003e;\n \u003cspan class=\"hljs-attribute\"\u003eposition\u003c/span\u003e: fixed;\n \u003cspan class=\"hljs-attribute\"\u003ebottom\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e16px\u003c/span\u003e;\n \u003cspan class=\"hljs-attribute\"\u003eright\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e16px\u003c/span\u003e;\n \u003cspan class=\"hljs-attribute\"\u003etransition\u003c/span\u003e: all \u003cspan class=\"hljs-number\"\u003e0.25s\u003c/span\u003e ease-in;\n \u003cspan class=\"hljs-attribute\"\u003etransform\u003c/span\u003e: translateY(\u003cspan class=\"hljs-number\"\u003e10px\u003c/span\u003e);\n \u003cspan class=\"hljs-attribute\"\u003eopacity\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e;\n \u003cspan class=\"hljs-selector-tag\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"hljs-selector-class\"\u003e.visible\u003c/span\u003e {\n \u003cspan class=\"hljs-attribute\"\u003eopacity\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e1\u003c/span\u003e;\n \u003cspan class=\"hljs-attribute\"\u003etransform\u003c/span\u003e: translateY(\u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e);\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003cstrong\u003eBackTop이 적용된 화면이다.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/css/blog-backtop-apply.gif\" alt=\"blog-backtop-apply\" /\u003e\u003c/p\u003e","preview":"블로그 개발 중 스크롤이 되었을 때, 상위로 이동시키는 BackTop을 적용하기 위해 Ant.Design의 BackTop 컴포넌트를 사용하려고\n했다.\n\n하지만 Ant.Design의 BackTop 컴포넌트를 적용해보니 잘 안된다. 따라서 직접 BackTop 컴포넌트를 구현하기로 결정했다.\n\n\u003e Ant.Design 홈페이지에선 잘 동작한다.\n\n\n구현 코드는 ...","title":"BackTop 컴포넌트 만들기","author":"salgum1114","date":"2019-04-25 18:23","tags":"react, component, backtop","cover":"/static/images/covers/react.png","next":"/webpack/2019-04-22-uglifyjs-to-terser","prev":"/css/2019-04-28-scroll-behavior-smooth"},"/webpack/2019-04-22-uglifyjs-to-terser":{"path":"/webpack/2019-04-22-uglifyjs-to-terser","content":"\u003cp\u003e최근 \u003ca href=\"https://github.com/salgum1114/react-design-editor\"\u003eReact Design Editor\u003c/a\u003e에 빌드 실패 관련 이슈가 올라왔다.\u003c/p\u003e\n\u003cp\u003e내용인 즉슨, \u003ccode\u003eproduction\u003c/code\u003e으로 빌드시 UglifyJsPlugin에서 \u003ccode\u003eUnexpected token: keyword «const»\u003c/code\u003e라는 오류가 발생하여 계속해서 빌드에 실패한다는 것이다.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/salgum1114/react-design-editor/issues/101\"\u003e[Bug] Not able to create build for the application #101\u003c/a\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"hljs shell language-shell\"\u003e\u003cspan class=\"hljs-meta\"\u003ereact-design-editor\u0026gt;\u003c/span\u003e\u003cspan class=\"bash\"\u003e npm run build\u003c/span\u003e\n\nreact-design-editor@0.0.1 build D:\\Projects\\VirtualTutor\\vt-bkp\\react-design-editor\nwebpack -p --config webpack.prod.js\n\nclean-webpack-plugin: D:\\Projects\\VirtualTutor\\vt-bkp\\react-design-editor\\public\\js has been removed.\nHash: 6f635a8eff2d04c3d630\nVersion: webpack 4.30.0\nTime: 28583ms\nBuilt at: 04/19/2019 4:12:58 PM\n129 assets\nEntrypoint app = js/1.784bfc366d4d89f7.js js/app.359b7dab68a97eb6.js\n[17] (webpack)/buildin/harmony-module.js 573 bytes {1} [built]\n[45] ./node_modules/rc-animate/es/Animate.js + 3 modules 17.1 KiB {1} [built]\n| 4 modules\n[65] (webpack)/buildin/global.js 472 bytes {1} [built]\n[193] (webpack)/buildin/module.js 497 bytes {1} [built]\n[557] jsdom (ignored) 15 bytes {0} {1} [built]\n[558] jsdom/lib/jsdom/living/generated/utils (ignored) 15 bytes {0} {1} [built]\n[559] jsdom/lib/jsdom/utils (ignored) 15 bytes {0} {1} [built]\n[560] xmldom (ignored) 15 bytes {0} {1} [built]\n[765] ./src/containers/App.js 3.46 KiB {0} [built]\n[845] ./src/registerServiceWorker.js 6.72 KiB {0} [built]\n[846] ./src/i18n/index.js 53 bytes {0} [built]\n[1134] multi @babel/polyfill ./src/index.js 40 bytes {0} [built]\n[1310] ./src/index.js 1.72 KiB {0} [built]\n[1705] ./node_modules/antd/es/index.js + 303 modules 1.55 MiB {1} [built]\n| 304 modules\n[1708] multi react react-dom lodash fabric antd 76 bytes {1} [built]\n+ 1694 hidden modules\n\nERROR in js/1.784bfc366d4d89f7.js from UglifyJs\nUnexpected token: keyword «const» [./node_modules/@babel/polyfill/lib/index.js:6,0][js/1.784bfc366d4d89f7.js:185555,2]\nChild html-webpack-plugin for \"index.html\":\n1 asset\nEntrypoint undefined = index.html\n[2] (webpack)/buildin/global.js 472 bytes {0} [built]\n[3] (webpack)/buildin/module.js 497 bytes {0} [built]\n+ 2 hidden modules\nnpm ERR! code ELIFECYCLE\nnpm ERR! errno 2\nnpm ERR! react-design-editor@0.0.1 build: webpack -p --config webpack.prod.js\nnpm ERR! Exit status 2\nnpm ERR!\nnpm ERR! Failed at the react-design-editor@0.0.1 build script.\nnpm ERR! This is probably not a problem with npm. There is likely additional logging output above.\n\nnpm ERR! A complete log of this run can be found in:\nnpm ERR! C:\\Users\\manish.kumar.GATEWAYGROUP\\AppData\\Roaming\\npm-cache_logs\\2019-04-19T10_42_59_421Z-debug.log\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003ebabel polyfill\u003c/code\u003e시 뭔가 충돌나는 것 같아 나와 비슷한 사례를 찾기 위해 구글링을 해본다.\u003c/p\u003e\n\u003cp\u003e최근 이슈는 아니지만, 비슷한 사례가 많이 나온다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/webpack/uglifyjs-const-issue-search.png\" alt=\"uglify-js-issue\" /\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/webpack-contrib/uglifyjs-webpack-plugin/issues/362\"\u003e첫번째 이슈\u003c/a\u003e 사이트에서 내용을 읽어보니, UglifyJsPlugin 버전 2부터 uglify-es가 버려졌으니 es6를 사용하고 싶으면 terser-webpack-plugin을 사용하라는 내용인것 같다.\u003c/p\u003e\n\u003cp\u003e실제로 확인해보니 \u003ca href=\"https://github.com/webpack-contrib/uglifyjs-webpack-plugin/releases/tag/v2.0.0\"\u003euglifyjs-webpack-plugin 릴리즈 태그 v2.0 Change Log\u003c/a\u003e에 내용이 있었다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/webpack/uglifyjs-v2-change-log.png\" alt=\"uglify-js-2.0\" /\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n \u003cp\u003e근데 terser가 무슨 뜻이지? naver 사전에서는 간결한 이라고 나온다.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e아무튼 \u003ca href=\"https://github.com/salgum1114/react-design-editor/issues/101\"\u003e이슈\u003c/a\u003e 해결을 위해 \u003ccode\u003euglifyjs-webpack-plugin\u003c/code\u003e에서 \u003ccode\u003eterser-webpack-plugin\u003c/code\u003e으로 변경을 시도해본다.\u003c/p\u003e\n\u003cp\u003e우선 변경을 위해 \u003ca href=\"https://github.com/webpack-contrib/terser-webpack-plugin\"\u003eterser-webpack-plugin\u003c/a\u003e github에서 사용방법을 찾아봤다.\u003c/p\u003e\n\u003cp\u003e찾아보니 terser-webpack-plugin과 uglifyjs-webpack-plugin의 사용법이 크게 다르지 않았다.\u003c/p\u003e\n\u003ch3 id=\"uglifyjswebpackplugin\"\u003e기존 uglifyjs-webpack-plugin 옵션\u003c/h3\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003eoptimization: {\n \u003cspan class=\"hljs-attr\"\u003eminimizer\u003c/span\u003e: [\n \u003cspan class=\"hljs-keyword\"\u003enew\u003c/span\u003e UglifyJsPlugin({\n \u003cspan class=\"hljs-attr\"\u003ecache\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eparallel\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003euglifyOptions\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003ewarnings\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003ecompress\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003ewarnings\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eunused\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003eecma\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e6\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003emangle\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eunused\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003esourceMap\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n }),\n ],\n},\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"terserwebpackplugin\"\u003e변경할 terser-webpack-plugin 옵션\u003c/h3\u003e\n\u003cpre\u003e\u003ccode class=\"hljs javascript language-javascript\"\u003eoptimization: {\n \u003cspan class=\"hljs-attr\"\u003eminimizer\u003c/span\u003e: [\n \u003cspan class=\"hljs-keyword\"\u003enew\u003c/span\u003e TerserPlugin({\n \u003cspan class=\"hljs-attr\"\u003ecache\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eparallel\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eterserOptions\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003ewarnings\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003ecompress\u003c/span\u003e: {\n \u003cspan class=\"hljs-attr\"\u003ewarnings\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003efalse\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eunused\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003eecma\u003c/span\u003e: \u003cspan class=\"hljs-number\"\u003e6\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003emangle\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n \u003cspan class=\"hljs-attr\"\u003eunused\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n },\n \u003cspan class=\"hljs-attr\"\u003esourceMap\u003c/span\u003e: \u003cspan class=\"hljs-literal\"\u003etrue\u003c/span\u003e,\n }),\n ],\n},\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e기본적인 옵션은 같고, \u003ccode\u003euglifyOptions\u003c/code\u003e에서 \u003ccode\u003eterserOptions\u003c/code\u003e으로 변경하면 되는 것 같다.\u003c/p\u003e\n\u003cp\u003e이제 변경된 옵션으로 \u003ca href=\"https://github.com/salgum1114/react-design-editor\"\u003eReact Design Editor\u003c/a\u003e를 빌드해본다.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"/static/images/webpack/terser-webpack-plugin-build.png\" alt=\"terser-webpack-plugin-build\" /\u003e\u003c/p\u003e\n\u003ch4 id=\"\"\u003e성공한것 같다.\u003c/h4\u003e\n\u003cp\u003e\u003cem\u003e마지막으로 요청자에게 이슈를 해결했다는 코멘트를 달아주는것으로 마무리한다.\u003c/em\u003e\u003c/p\u003e","preview":"최근 React Design Editor에 빌드 실패 관련 이슈가 올라왔다.\n\n내용인 즉슨, production으로 빌드시 UglifyJsPlugin에서 Unexpected token: keyword «const»라는\n오류가 발생하여 계속해서 빌드에 실패한다는 것이다.\n\n[Bug] Not able to create build for the applicati...","title":"UglifyJsPlugin const 오류 해결 방법","author":"salgum1114","date":"2019-04-22 21:05","tags":"webpack, uglifyjs, terser-webpack-plugin","cover":"/static/images/covers/webpack.png","next":null,"prev":"/reactjs/2019-04-25-back-top-component"}}},"authors":{"salgum1114":{"name":"salgum1114","title":"Sung Gyun Oh","avatar":"/static/images/authors/salgum1114.png","bio":"Hello world!"}},"tags":{"react":{"total":11,"paths":["/reactjs/2019-11-28-react-class-equivalents","/reactjs/2019-10-30-react-hot-loader-patch-warning","/etc/2019-10-22-react-design-editor-1","/nextjs/2019-05-28-nextjs-static-website-5","/nextjs/2019-05-24-nextjs-static-website-4","/nextjs/2019-05-21-nextjs-static-website-3","/nextjs/2019-05-20-nextjs-static-website-2","/reactjs/2019-05-11-using-typescript-without-tsx","/nextjs/2019-05-06-nextjs-static-website-1","/reactjs/2019-05-03-medium-zoom","/reactjs/2019-04-25-back-top-component"]},"react-hooks":{"total":1,"paths":["/reactjs/2019-11-28-react-class-equivalents"]},"letsencrypt":{"total":1,"paths":["/etc/2019-11-14-letsencrypt-public-ca"]},"openssl":{"total":2,"paths":["/etc/2019-11-14-letsencrypt-public-ca","/etc/2019-05-20-using-openssl-in-windows"]},"webpack":{"total":2,"paths":["/reactjs/2019-10-30-react-hot-loader-patch-warning","/webpack/2019-04-22-uglifyjs-to-terser"]},"react-hot-loader":{"total":1,"paths":["/reactjs/2019-10-30-react-hot-loader-patch-warning"]},"antd":{"total":1,"paths":["/etc/2019-10-22-react-design-editor-1"]},"fabricjs":{"total":1,"paths":["/etc/2019-10-22-react-design-editor-1"]},"react-design-editor":{"total":1,"paths":["/etc/2019-10-22-react-design-editor-1"]},"nextjs":{"total":5,"paths":["/nextjs/2019-05-28-nextjs-static-website-5","/nextjs/2019-05-24-nextjs-static-website-4","/nextjs/2019-05-21-nextjs-static-website-3","/nextjs/2019-05-20-nextjs-static-website-2","/nextjs/2019-05-06-nextjs-static-website-1"]},"website":{"total":5,"paths":["/nextjs/2019-05-28-nextjs-static-website-5","/nextjs/2019-05-24-nextjs-static-website-4","/nextjs/2019-05-21-nextjs-static-website-3","/nextjs/2019-05-20-nextjs-static-website-2","/nextjs/2019-05-06-nextjs-static-website-1"]},"typescript":{"total":2,"paths":["/etc/2019-05-20-using-openssl-in-windows","/reactjs/2019-05-11-using-typescript-without-tsx"]},"mqtt":{"total":1,"paths":["/etc/2019-05-20-using-openssl-in-windows"]},"medium-zoom":{"total":1,"paths":["/reactjs/2019-05-03-medium-zoom"]},"css":{"total":2,"paths":["/css/2019-04-30-overscroll-behavior-contain","/css/2019-04-28-scroll-behavior-smooth"]},"overscroll-behavior":{"total":1,"paths":["/css/2019-04-30-overscroll-behavior-contain"]},"scroll-behavior":{"total":1,"paths":["/css/2019-04-28-scroll-behavior-smooth"]},"component":{"total":1,"paths":["/reactjs/2019-04-25-back-top-component"]},"backtop":{"total":1,"paths":["/reactjs/2019-04-25-back-top-component"]},"uglifyjs":{"total":1,"paths":["/webpack/2019-04-22-uglifyjs-to-terser"]},"terser-webpack-plugin":{"total":1,"paths":["/webpack/2019-04-22-uglifyjs-to-terser"]}}},"page":"/","query":{},"buildId":"WBzuTPjOfilMhrb159LWu","nextExport":true};__NEXT_LOADED_PAGES__=[];__NEXT_REGISTER_PAGE=function(r,f){__NEXT_LOADED_PAGES__.push([r, f])}</script><script async="" id="__NEXT_PAGE__/" src="/_next/static/WBzuTPjOfilMhrb159LWu/pages/index.js"></script><script async="" id="__NEXT_PAGE__/_app" src="/_next/static/WBzuTPjOfilMhrb159LWu/pages/_app.js"></script><script async="" id="__NEXT_PAGE__/_error" src="/_next/static/WBzuTPjOfilMhrb159LWu/pages/_error.js"></script><script src="/_next/static/runtime/webpack-484a8928da4ec3492fcd.js" async=""></script><script src="/_next/static/chunks/commons.14799f0a166b7233d4ca.js" async=""></script><script src="/_next/static/chunks/styles.a8a2d4bb615b8dc9d9ef.js" async=""></script><script src="/_next/static/runtime/main-0463b6c82337fbcda1e9.js" async=""></script></body></html>