<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>_uchanlee.log</title>
        <link>https://velog.io/</link>
        <description>늦게 시작하는 것을 두려워 하기보단 하다가 중단 하는 것을 두려워 하자!</description>
        <lastBuildDate>Tue, 06 Apr 2021 06:27:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>_uchanlee.log</title>
            <url>https://images.velog.io/images/_uchanlee/profile/af5910b4-068e-44d1-99bd-585756c26c52/profile.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. _uchanlee.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/_uchanlee" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[네 만들어 드렸습니다~]]></title>
            <link>https://velog.io/@_uchanlee/%EB%84%A4-%EB%A7%8C%EB%93%A4%EC%96%B4-%EB%93%9C%EB%A0%B8%EC%8A%B5%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@_uchanlee/%EB%84%A4-%EB%A7%8C%EB%93%A4%EC%96%B4-%EB%93%9C%EB%A0%B8%EC%8A%B5%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Tue, 06 Apr 2021 06:27:47 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 게시글은 간단한 사이트를 만들었는데 친구들 반응이 너무 웃겨서 그 과정을 기록해봅니다. 😆</p>
</blockquote>
<h1 id="tldr">TL;DR</h1>
<ul>
<li>깃허브 README에 폴더 구조를 종종 써놓는데, 손수 노가다 작업이었다.<ul>
<li>웹으로 만듦</li>
<li>친구들 반응이 웃김</li>
</ul>
</li>
<li>사용해보기<ul>
<li><a href="https://woochanleee.github.io/project-tree-generator">woochanleee.github.io/project-tree-generator</a></li>
<li><a href="https://github.com/woochanleee/project-tree-generator">GitHub Repository</a></li>
</ul>
</li>
<li>개발 일기</li>
</ul>
<h2 id="사건의-발단">사건의 발단</h2>
<p>때는 바야흐로 2일전, 그룹 채팅방에 친구 한명이 메시지를 보냈습니다.
<img src="https://images.velog.io/images/_uchanlee/post/47a57531-3d34-441b-b3ba-0025dacf1a59/image.png" alt=""></p>
<blockquote>
<p><strong>&quot;음? 저거 이미 만들어주는 거 있지 않을까? 🤔&quot;</strong></p>
</blockquote>
<p>있다면 댓글로 알려주세요...😢</p>
<h3 id="구글링-👨💻">구글링 👨‍💻</h3>
<p>다음과 같은 키워드로 검색해봤는데 썩 원하는 사이트를 찾지 못했습니다.</p>
<ul>
<li>readme project structure, folder structure generator, folder tree generator 등</li>
</ul>
<p>그 중 대안이 될 만한 내용을 작성한 글 하나를 발견했습니다. </p>
<ul>
<li><a href="https://velog.io/@minsgy/%EB%82%98%EC%9D%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%84%B1%EC%9D%84-%ED%95%9C-%EB%88%88%EC%97%90">velog-@minsgy</a><ul>
<li>대안 -&gt; <a href="https://marketplace.visualstudio.com/items?itemName=Shinotatwu-DS.file-tree-generator">vs-code extension</a></li>
</ul>
</li>
<li><code>$ tree -I “$(shell cat .gitignore | tr -s ‘\n’ ‘|’ )”</code><ul>
<li><a href="https://www.facebook.com/groups/174499879257223/user/100002351296289">박진우님</a></li>
</ul>
</li>
</ul>
<p>무려 좋아요 수가 75개로 많은 관심을 받은 글이네요. <del>나도 좋아요🤪</del></p>
<blockquote>
<p>&quot;잘 찾았으면 그걸 쓰면 되지 왜 만듦?&quot;</p>
</blockquote>
<p>슬슬 이런 의문이 들 수 있는데, 제가 만들게 된 이유는 다음과 같습니다.</p>
<ol>
<li>서비스의 확장성(웹으로 배포하여 누구나 이용 가능)</li>
<li>불필요한 파일 생성 제거(e.g., node_modules, 모든 파일)</li>
</ol>
<p><a href="https://marketplace.visualstudio.com/items?itemName=Shinotatwu-DS.file-tree-generator">vs-code extension</a> 해당 서비스에서 <code>ignore</code> 기능은 없는걸로 판단하여 손수 Project Tree Generator를 개발하게 되었습니다.</p>
<h2 id="전개">전개</h2>
<blockquote>
<p>글쓴이가 디자인과는 거리가 멀고 프론트엔드 개발을 공부하는 학생이여서 UI는 최대한 간결하게 구성하여 개발하였습니다.</p>
</blockquote>
<h3 id="프로젝트-세팅">프로젝트 세팅</h3>
<p>가장 익숙한 기술 스택인 React.js + TypeScript를 사용하기로 결정했고, 보일러 플레이트 코드는 <a href="https://create-react-app.dev/">facebook-CRA</a>로 프로젝트를 세팅했습니다.</p>
<ul>
<li>1년 전 쯤에는 CRA가 무겁고 확장에 복잡성 등 단점이 있어 <a href="https://velog.io/@_uchanlee/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9B%B9%ED%8C%A9%EC%9C%BC%EB%A1%9C-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0without-CRA">webpack-react</a> 이런 글도 썼던 기억이 있다. 그런데 요즘은 CRA도 가볍고 지원되는게 많아지고 회사에서도 종종 쓰인다는 이야기를 들어서 사용하기로 결정했다.</li>
</ul>
<p>+) 스타일은 scss를 사용했다.</p>
<h3 id="react-17">React 17</h3>
<p>CRA 명령어로 생성중인 프로젝트가 생성되고 안 쓰는 파일들을 정리하다 <code>package.json</code>에 들어가 보니 리액트 버전이 17이였다.</p>
<blockquote>
<p><strong>&quot;오? 17에 재미난것들 업데이트 된다고 들었는데&quot;</strong></p>
</blockquote>
<p>얼마 전에 페이스북에서 React 관련 글을 하나 읽었습니다. 그 내용은 다음과 같습니다.</p>
<ul>
<li>Concurrent Mode (Experimental)에서 <code>import React from &#39;react&#39;;</code>를 추가하지 않고도 리액트 컴포넌트를 사용 가능하다는 내용이었다.</li>
</ul>
<p>평소에도 React도 Next.js처럼 해당 구문 없이 사용하고 싶은 욕구가 있어 되게 인상깊게 읽었습니다.</p>
<p>내가 들었던 내용을 토대로 import 구문을 지우고 에러가 나는지 확인해 보았습니다.</p>
<blockquote>
<p><strong>&quot;와우! 이게 되네?&quot;</strong></p>
</blockquote>
<p>16에서는 에러가 나던것이 </p>
<p><img src="https://images.velog.io/images/_uchanlee/post/03ada05a-3c88-48dc-be6e-2a36b8878128/image.png" alt=""></p>
<p>17에서는 &quot;React import 해놓고 안썼다!&quot; 라고 알려주네요.
<img src="https://images.velog.io/images/_uchanlee/post/06125114-a6d9-4ca4-8400-cb849290a85c/image.png" alt=""></p>
<p>공식 문서를 읽지 않고선 믿지 못하는 사람(저)을 위해 링크랑 사진 첨부합니다.</p>
<p>From <a href="https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#whats-a-jsx-transform">React Docs</a>
<img src="https://images.velog.io/images/_uchanlee/post/c8488c01-c774-482c-9061-2cee2235a7b9/image.png" alt=""></p>
<h2 id="위기">위기</h2>
<p>뚝딱뚝딱 🛠 UI 마크업을 끝내고 보니 이제 핵심 기능을 구현해야 할 때가 왔습니다.</p>
<blockquote>
<p><em>근데 이거 어케해..?</em></p>
</blockquote>
<h3 id="로직-어떻게-하지">로직 어떻게 하지...?</h3>
<p><a href="https://woochanleee.github.io/project-tree-generator">Project Tree Generator</a> 해당 사이트의 핵심 기능인 tree의 depth를 구현하는데 긴 시간을 보냈습니다.</p>
<p><img src="https://images.velog.io/images/_uchanlee/post/e9590ac8-7b55-4aa7-864f-a3ad809897e0/image.png" alt=""></p>
<p>처음에는 재귀적으로 해결하려고 했습니다. 그렇게 View를 구현하기 까진 성공 했는데 삭제, 새 파일, depth 증가 감소 등 기능을 구현하려다 보니까 도저히 방법이 생각나지 않았습니다.<em>(댓글로 아이디어 공유해주시면 참고하겠습니다!)</em></p>
<p>그래서 각 라인별로 아이템을 두었고 해당 아이템의 depth(깊이)를 저장해두는 방식으로 구현했습니다. 코드가 궁금하신 분은 <a href="https://github.com/woochanleee/project-tree-generator/blob/main/src/Editor.tsx">GitHub-Editor.tsx</a>에 들어가면 <del>제 더러운 코드들💩</del> 을 확인할 수 있습니다.</p>
<h2 id="절정">절정</h2>
<p>우여곡절 개발을 다 마치고 배포를 해야할 때가 왔습니다. 정적 파일을 쉽게 배포할 수 있는 <a href="https://pages.github.com/">Github Page</a>를 이용하기로 결정했습니다.</p>
<h3 id="npm-gh-page">npm gh-page</h3>
<p>Github Page를 배포하는데 쉽게 도와주는 라이브러리를 갖다 사용했습니다.</p>
<p>바로 <a href="https://www.npmjs.com/package/gh-pages#command-line-utility">gh-page</a> 인데요. 커맨드 단 한 줄으로 자동으로 gh-pages 브랜치를 만들고 그 안에 정적파일을 올려 배포까지 성공하였습니다.</p>
<p>앞으로도 Github Page를 애용하게 될것 같습니다.</p>
<h2 id="결말">결말</h2>
<p>배포를 다 마쳤으니 맨 처음 이 사이트를 필요로 했던, 이 사이트를 만들도록 한 장본인, 그 친구한테 사이트 주소를 전달하러 가봅시다.</p>
<blockquote>
<p><strong>&quot;네 만들어 드렸습니다~&quot;</strong></p>
</blockquote>
<p>실제 대화방의 스크린샷입니다.</p>
<p><img src="https://images.velog.io/images/_uchanlee/post/6544221f-6d1c-4048-b8e7-401143f95fb0/Group%202.svg" alt=""></p>
<h3 id="버그">버그?</h3>
<p>친구3 이 공유하자마자 버그를 제보했네요!(이런) 해당 이슈는 onKeyDown KeyboardEvent에 한글을 입력했을 때 Enter 키를 클릭시 특정 상황에서 이벤트가 2번 발생하는 이슈였습니다. 이전에 한번 겪었던 이슈라 금방 해결했네요.</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode">event.KeyCode</a>를 조건문에 사용하여 해결했는데 deprecated네요.<ul>
<li>혹시 다른 해결방안을 아시는 분 있으면 댓글로 공유해주시면 감사하겠습니다!</li>
</ul>
</li>
</ul>
<h3 id="뜨거운-반응-🕺🕺🕺">뜨거운 반응 🕺🕺🕺</h3>
<p>나는 단순히 친구 한명의 메시지를 보고 &quot;없으면 만들어볼까?&quot; 하는 생각에 하루 동안 뚝딱 만든 사이트였는데, 친구들의 반응이 심상치 않았다. <em><del>(왜 이래 ㅋㅋㅋㅋㅋ)</del></em></p>
<p>그래도 내가 만든 사이트가 아무 관심도 없이 잊혀져 가는 것 보다는 이렇게 관심을 갖고 반응해준 다는건 개발자로써 언제나 뿌듯한 것 같다.</p>
<h2 id="글을-마치며">글을 마치며</h2>
<p>단순히 사이트 하나를 만들고 토이 프로젝트로 끝이날 해프닝이였지만 친구들의 반응이 너무 웃기고 귀여워서 오랜만에 이렇게 글을 쓰네요. (고맙다!)</p>
<p>아무말 대잔치였던 이 글을 여기까지 읽어주신 분이 계실지 모르겠지만 있다면 진심으로 감사 인사 드립니다. 🙇‍♂️</p>
<p>제가 만든 사이트에 버그 제보나 추가 기능 제안 등은 <a href="https://github.com/woochanleee/project-tree-generator/issues">이슈</a>로 남겨주시면 언제든 환영이고, 버그 수정 및 기능 추가 등은 <a href="https://github.com/woochanleee/project-tree-generator/pulls">Pull Request</a>를 날려주시면 확인하도록 하겠습니다!</p>
<p>감사합니다. 😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[문(Statement)과 표현식(Expression)]]></title>
            <link>https://velog.io/@_uchanlee/%EB%AC%B8Statement%EA%B3%BC-%ED%91%9C%ED%98%84%EC%8B%9DExpression</link>
            <guid>https://velog.io/@_uchanlee/%EB%AC%B8Statement%EA%B3%BC-%ED%91%9C%ED%98%84%EC%8B%9DExpression</guid>
            <pubDate>Wed, 28 Oct 2020 13:27:12 GMT</pubDate>
            <description><![CDATA[<h1 id="문statement과-표현식expression">문(Statement)과 표현식(Expression)</h1>
<ul>
<li>문과 표현식을 대충 같은 의미라고 넘겨버리는 개발자가 많다.</li>
<li>그러나 자바스크립트에서 두 용어는 아주 중요한 차이가 있으므로 명확하게 구분해야 한다.</li>
</ul>
<p><img src="https://images.velog.io/images/_uchanlee/post/b935aaa6-02ff-49e4-8086-407d813c1387/statement_expression-diagram.svg" alt=""></p>
<h2 id="문statement">문(Statement)</h2>
<ul>
<li>실행 가능한 최소의 독립적인 코드 조각</li>
<li>statement는 흔히 한 개 이상의 expression이나 프로그래밍 키워드를 포함하는 경우가 많다.</li>
</ul>
<pre><code class="language-js">for (let i = 0; i &lt; 10; i++) {} // for statment

while (true) {} // while statment

if (true) {} // if statment

let a; // declaration statment

function b() {} // function declaration statement</code></pre>
<h2 id="표현식expression">표현식(Expression)</h2>
<ul>
<li>특정한 결괏값으로 계산되는 것</li>
</ul>
<pre><code class="language-js">let a = 3 * 6; // 1.
let b = a; // 2.
b; // 3.</code></pre>
<ol>
<li><ul>
<li>3 * 6은(18이라는 값으로 평가되는) 표현식이다.</li>
<li>let a = 3 * 6; 문은 변수를 선언(그리고 선택적으로 동시에 어떤 값을 할당)하므로 <b>&#39;선언문(Declaration Statement)&#39;</b>이라 한다.</li>
<li>(앞에 let이 빠진) a = 3 * 6;는 <b>&#39;할당 표현식(Assignment Expression)&#39;</b>이라고 한다.</li>
</ul>
</li>
<li><ul>
<li>let b = a; 문도 변수를 선언(그리고 선택적으로 동시에 어떤 값을 할당)하므로 <b>&#39;선언문(Declaration Statement)&#39;</b>이라 한다.</li>
<li>a라는 표현식은 당시 변수들에 저장된 값으로 평가되므로 18이라는 값으로 평가되고 역시 b는 18이 된다.</li>
</ul>
</li>
<li><ul>
<li>b가 표현식의 전부지만 이것 만으로도 완전한 문이다.</li>
<li>일반적으로 이런 문은 <b>&#39;표현식 문(Expression Statement)&#39;</b>이라고 부른다.</li>
</ul>
</li>
</ol>
<h2 id="문의-완료-값">문의 완료 값</h2>
<ul>
<li>모든 문은 (그 값이 undefined라고 해도) 완료 값(Completion Value)을 가진다는 사실을 의외로 모르는 사람들이 많다. <del>나 또한 이번에 처음 알았다.</del><blockquote>
<p>공식 문서중 일부</p>
</blockquote>
</li>
</ul>
<p><img src="https://images.velog.io/images/_uchanlee/post/9cc49210-fe35-4e38-bd57-3611e2e22866/statements_syntax.png" alt=""></p>
<p>📑 <a href="https://www.ecma-international.org/ecma-262/5.1/#sec-12">출처</a></p>
<blockquote>
<h3 id="문의-완료-값을-확인하는-방법은-없을까">문의 완료 값을 확인하는 방법은 없을까?</h3>
</blockquote>
<ul>
<li>가장 확실하고 빠른 방법은 브라우저 개발자 콘솔 창에서 문을 타이핑해보는 것이다.</li>
<li>콘솔 창은 가장 최근에 실행된 문의 완료 값을 기본적으로 출력하게 되어 있다.</li>
</ul>
<blockquote>
<h3 id="let-c--10-같은-문은-완료-값이-뭘까"><code>let c = 10;</code> 같은 문은 완료 값이 뭘까?</h3>
</blockquote>
<ul>
<li>할당 표현식 <code>c = 10;</code>은 할당 이후의 값(여기서는 10)이 완료 값이지만, <code>let</code> 문 자체의 완료 값은 <code>undefined</code>다.</li>
</ul>
<blockquote>
<h3 id="콘솔-창이-반환해준-완료-값은-개발자가-내부-프로그램에서-사용할-수-있는-값은-아니다-그럼-완료-값을-사용할-방법은-없을까">콘솔 창이 반환해준 완료 값은 개발자가 내부 프로그램에서 사용할 수 있는 값은 아니다. 그럼 완료 값을 사용할 방법은 없을까?</h3>
</blockquote>
<ul>
<li>방법은 있지만 꽤 복잡하다.</li>
<li>다른 종류의 문 완료 값을 보자.</li>
<li>예를 들어, 보통의 <code>{ }</code> 블록은 <strong>내부의 가장 마지막 문/표현식의 완료 값</strong>을 자신의 완료 값으로 반환한다.</li>
</ul>
<pre><code class="language-js">let b;

if (true) {
   b = 5 + 10;
}</code></pre>
<ul>
<li>콘솔 창에서 실행하면 15가 나온다.</li>
<li>블록 내의 마지막 문 <code>b = 5 + 10;</code>의 완료 값이 15이므로 if 블록의 완료 값도 15를 반환한 것이다.</li>
<li>즉, 블록의 완료 값은 내부에 있는 <strong>마지막 문의 값</strong>을 암시적으로 반환한 값이다.</li>
</ul>
<blockquote>
<h3 id="하지만-다음과-같은-코드가-작동하지-않는-건-분명히-문제가-있다">하지만 다음과 같은 코드가 작동하지 않는 건 분명히 문제가 있다.</h3>
</blockquote>
<pre><code class="language-js">let a, b;

a = if (true) {
   b = 5 + 10;
}</code></pre>
<ul>
<li>문의 완료 값을 캐치하여 다른 변수에 할당한다는 건 쉬운 구문/문법으로는 불가능하다.</li>
<li>뭔가 방법이 없을까? 🤔</li>
<li>완료 값을 캐치하려면 어쩔 수 없이 <strong>유해함의 대명사</strong> <code>eval()</code>함수를 사용할 수 밖에 없다.</li>
</ul>
<pre><code class="language-js">let a, b;

a = eval(&#39;if (true) { b = 5 + 10; }&#39;);

a; // 15</code></pre>
<ul>
<li>꼴 보기 싫은 코드지만 일단 잘 돌아간다!</li>
<li>콘솔 창 외에 자바스크립트 프로그램에서도 문 완료 값을 확인할 방법이 있음을 알 수 있다.</li>
</ul>
<blockquote>
<h3 id="es7-명세에는-do-표현식이-제안된-상태다">ES7 명세에는 &#39;do 표현식&#39;이 제안된 상태다.</h3>
</blockquote>
<pre><code class="language-js">let a, b;

a = do {
   if (true) {
      b = 5 + 15;
   }
};

a; // 15</code></pre>
<ul>
<li><code>do { }</code> 표현식은 (하나 이상의 문을 포함한) 블록 실행 후 블록 내 마지막 문의 완료 값을 do 표현식 전체의 완료 값으로 반환하며 결국 이 값이 변수 a 에 할당된다.</li>
<li>인라인 함수 표현식 안에 감싸서 명시적으로 반환할 필요 없이 문을 (다른 문 안에 들어갈 수 있는) 표현식처럼 다루자는게 기본적인 아이디어다.</li>
<li>아직까지는 문 완료 값을 대수롭지 않게 여기고 있지만 자바스크립트 언어가 진화할수록 그 중요성은 점점 더 부각될 것 같다.</li>
<li>어서 <code>do { }</code> 표현식이 도입되어 <code>eval()</code> 같은 나쁜 것들을 사용하고픈 욕구를 영원히 잠재울 수 있길 고대한다.</li>
</ul>
<h2 id="표현식의-부수-효과">표현식의 부수 효과</h2>
<ul>
<li>대부분의 표현식에는 부수 효과가 없다. 예를 들면,</li>
</ul>
<pre><code class="language-js">let a = 2;
let b = a + 3;</code></pre>
<ul>
<li>표현식 <code>a + 3</code> 자체는 가령 a 값을 바꾸는 등의 부수 효과가 전혀 없다.</li>
<li>단지 <code>b = a + 3</code> 문에서 결괏값 5가 b에 할당될 뿐이다.</li>
</ul>
<blockquote>
<p>다음의 함수 호출 표현식은 부수 효과를 가진(가졌을지 모를) 표현식의 전형적인 예이다.</p>
</blockquote>
<pre><code class="language-js">let a = 1;

function foo() {
   a += 1;
}

foo(); // 결괏값: &#39;undefined&#39;, 부수 효과: &#39;a&#39;가 변경됨.</code></pre>
<blockquote>
<p>다른 부수 효과를 지닌 표현식을 보자.</p>
</blockquote>
<pre><code class="language-js">let a = 7;
let b = a++;

a; // 8
b; // 7</code></pre>
<ul>
<li>표현식 <code>a++</code>이 하는 일은 두 가지다.</li>
<li>a의 현재 값 7를 반환(그리고 b에 할당하는 것까지)하고 a 값을 1만큼 증가시킨다.</li>
</ul>
<blockquote>
<p><code>++a++</code>은 문법에 맞는 구문일까?</p>
</blockquote>
<ul>
<li>실행하면 <strong>ReferenceError</strong> 에러가 난다.</li>
<li>부수 효과를 유발하는 연산자는 부수 효과를 일으킬 변수 레퍼런스가 꼭 필요하기 때문이다.</li>
<li><code>++a++</code>에서는 <code>a++</code>시 ++ 연산자는 42 같은 원시 값에 직접 부수 효과를 일으킬 수는 없으므로 <strong>ReferenceError</strong>를 던진다.</li>
</ul>
<blockquote>
<p><code>delete</code> 역시 부수 효과를 일으키는 연산자다.</p>
</blockquote>
<ul>
<li><code>delete</code>는 객체의 프로퍼티를 없애거나 배열에서 슬롯을 제거할 때 쓴다.</li>
<li>하지만 단독 문(Standalone Statement)으로 더 많이 쓴다.</li>
</ul>
<pre><code class="language-js">const obj = {
   a: 30,
};

obj.a; // 30
delete obj.a; // true
obj.a; // undefined</code></pre>
<ul>
<li><code>delete</code> 연산자의 결괏값은 유효한/허용된 연산일 경우 <code>true</code>, 그 외에는 <code>false</code>다.</li>
<li>이 연산자의 부수 효과는 바로 프로퍼티(또는 배열 슬롯)를 제거하는 것이다.</li>
<li>&#39;유효한/허용된&#39;이란 무슨 의미일까?<ul>
<li>존재하지 않는 프로퍼티 또는 존재하면서 설정 가능한(Configurable)한 프로퍼티일 경우 <code>delete</code> 연산자는 <code>true</code>를 반환한다. 그 외의 경우는 <code>false</code>를 반환하거나 에러를 낸다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>마지막으로 예시할 부수 효과 유발 연산자는 언뜻 보기에 분명한 것 같으면서도 분명하지 않은 <code>=</code> 할당 연산자다.</p>
</blockquote>
<pre><code class="language-js">let a;

a = 42; // 42
a; // 42</code></pre>
<ul>
<li><code>a = 42</code>에서 <code>=</code> 연산자는 아무리 봐도 부수 효과와는 무관해 보인다.</li>
<li>하지만 <code>a = 42</code> 문의 실행 결과는 이제 막 할당된 값(42)이므로 42를 a에 할당하는 자체가 본질적으로 부수 효과다.</li>
</ul>
<blockquote>
<p>이렇게 할당 표현식문 실행 시 <strong>할당된 값이 완료 값이 되는 작동 원리</strong>는 다음과 같은 연쇄 할당문(Chained Assignment)에서 특히 유용하다.</p>
</blockquote>
<pre><code class="language-js">let a, b, c;

a = b = c = 20;</code></pre>
<ul>
<li><code>c = 20</code> 평가 결과는 (20을 c에 할당하는 부수 효과를 일으키며) 20이 되고, <code>b = 20</code> 평가 결과는 (20을 b에 할당하는 부수 효과를 일으키며) 20이 된다. 결국, <code>a = 20</code>으로 (20을 a에 할당하는 부수 효과를 일으키며) 평가된다.</li>
</ul>
<blockquote>
<p>또 다른 예를 보자.</p>
</blockquote>
<pre><code class="language-js">function vowels(str) {
   let matches;

   if (str) {
      // 모든 모음을 추출한다.
      matches = str.match(/[aeiou]/g);

      if (matches) {
         return matches;
      }
   }
}

vowels(&quot;Hello World&quot;); // [&quot;e&quot;, &quot;o&quot;, &quot;o&quot;]</code></pre>
<ul>
<li>잘 작동하는 코드다.</li>
<li>할당 연산자의 부수 효과를 잘 활용하면 다음과 같이 2개의 <code>if</code> 문을 하나로 간단히 합칠 수 있다.</li>
</ul>
<pre><code class="language-js">function vowels(str) {
   let matches;

   // 모든 모음을 추출한다.
   if (str &amp;&amp; (matches = str.match(/[aeiou]/g))) {
      return matches;
   }
}

vowels(&quot;Hello World&quot;); // [&quot;e&quot;, &quot;o&quot;, &quot;o&quot;]</code></pre>
<ul>
<li><code>matches = str.match</code>를 감싸는 ( )를 빠뜨리면 안 된다.</li>
<li>두 조건이 서로 분명히 연관되어 있음을 잘 보여주기 때문에 나는 후자를 더 선호하는 편이다. 물론 어떤 스타일을 선호할지는 개인 취향 차이다.</li>
</ul>
<h2 id="출처">출처</h2>
<ul>
<li><a href="https://www.hanbit.co.kr/store/books/look.php?p_code=B8227329776">You Don’t Know JS : 타입과 문법, 스코프와 클로저</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 네이티브 react-native start 에러]]></title>
            <link>https://velog.io/@_uchanlee/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-react-native-start-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@_uchanlee/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-react-native-start-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Thu, 08 Oct 2020 10:06:38 GMT</pubDate>
            <description><![CDATA[<p>난 분명히</p>
<blockquote>
<p><code>react-native init test</code>
<code>yarn android</code></p>
</blockquote>
<p>를 순서대로 했지만 iOS는 잘된 반면 안드로이드는 아래와 같은 에러가 발생했다.</p>
<pre><code>iuchan@MacBook-Pro sdf % yarn android
yarn run v1.22.4
$ react-native run-android
info Running jetifier to migrate libraries to AndroidX. You can disable it using &quot;--no-jetifier&quot; flag.
Jetifier found 967 file(s) to forward-jetify. Using 12 workers...
info Starting JS server...
info Installing the app...

FAILURE: Build failed with an exception.

* What went wrong:
Could not initialize class org.codehaus.groovy.runtime.InvokerHelper

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 613ms

error Failed to install the app. Make sure you have the Android development environment set up: https://reactnative.dev/docs/environment-setup. Run CLI with --verbose flag for more details.
Error: Command failed: ./gradlew app:installDebug -PreactNativeDevServerPort=8081

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.</code></pre><p>다행히 구글링을 통해 미리 해결한 사람이 있었다.
이유는 jdk버전이 14로 올라가면서 gradle version이 14를 지원하지 않는 상황에 발생한것이다.
해결 방법은 <code>gradle/wrapper/gradle-wrapper.properties</code>의 gradle 버젼을 올려주는 것이다. 나의 파일은 다음과 같다.</p>
<p><code>gradle-wrapper.properties</code></p>
<pre><code>distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
</code></pre><p><a href="https://coding-start.tistory.com/378">https://coding-start.tistory.com/378</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Github 프로필에 나의 Daliy 코딩 시간을 적용해보자!]]></title>
            <link>https://velog.io/@_uchanlee/Github-%ED%94%84%EB%A1%9C%ED%95%84%EC%97%90-%EB%82%98%EC%9D%98-Daliy-%EC%BD%94%EB%94%A9-%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@_uchanlee/Github-%ED%94%84%EB%A1%9C%ED%95%84%EC%97%90-%EB%82%98%EC%9D%98-Daliy-%EC%BD%94%EB%94%A9-%EC%8B%9C%EA%B0%84%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Fri, 26 Jun 2020 04:10:45 GMT</pubDate>
            <description><![CDATA[<p><a href="https://fernando.kr/develop/2020-05-02-github-gist-posting/?fbclid=IwAR2Xxg1VGXn-4xa1b3pZUbiTIp_nRsmO2ukHfsYffydhe_GYHUhf6A3LbpE">FERNANDO 기술 블로그</a> 를 보고 따라했지만 적용이 안돼서 이를 해결하는 방법을 약간 추가하여 글을 작성해 봅니다.
<img src="https://images.velog.io/images/_uchanlee/post/ac0cef98-abc0-4390-adeb-66fa9c8eee14/image.png" alt=""></p>
<h2 id="github-프로필에-daliy-코딩시간-적용하기">Github 프로필에 Daliy 코딩시간 적용하기</h2>
<p>위의 사진처럼 내가 어느 시간대에 커밋을 했는지 한 눈에 볼수 있는 방법이 있습니다. 이를 적용하는데 어려움을 겪을 분들을 위하여 적용하는 방법을 소개하겠습니다. <del>저 같은 경우 엄청난 삽질을 통해 깨달았습니다.ㅋㅋ</del></p>
<h2 id="적용해보자✅">적용해보자!✅</h2>
<ol>
<li><a href="https://github.com/techinpark/productive-box">techinpark/productive-box</a> <code>레포지토리</code>를 자신의 레포에 포크합니다.<blockquote>
<p>원작자의 <code>Repository</code> 에서 Fork 받아 국내 실정에 맞게 설정을 약간 변경하였다고 합니다.</p>
</blockquote>
</li>
<li>(<a href="https://gist.github.com">https://gist.github.com</a>) 에 들어가서 새로운 Public Gist를 생성합니다. <blockquote>
<p>저는 이렇게 작성해 주었습니다. 
<img src="https://images.velog.io/images/_uchanlee/post/0d2fb89d-3372-43f7-8e0c-51161b942674/image.png" alt=""></p>
</blockquote>
</li>
<li>Github 토큰을 repo와 gist 두 가지를 선택한 채로 생성합니다.
(<a href="https://github.com/settings/tokens/new">https://github.com/settings/tokens/new</a>)<blockquote>
<p>저는 이렇게 생성해주겠습니다. <img src="https://images.velog.io/images/_uchanlee/post/7d7b28f0-fb5d-4e3c-8fda-881aa9f190bc/image.png" alt="">
생성하면 <img src="https://images.velog.io/images/_uchanlee/post/dfba4f1a-b38e-46c4-8e30-fa2b58827593/image.png" alt=""> 이렇게 토큰값을 다시는 보여주지 않는다고 하는데요? <code>b5c762d615b37c24da933969782759b24a59df48</code> 이 값을 복사해서 다시 찾을수 있게 저장해둡시다. <del>저 토큰은 삭제했으니 걱정마세용😆</del></p>
</blockquote>
</li>
<li>아까전에 포크한 자신의 레포지스토리에 가서 <code>Settings</code> -&gt; <code>Secrets</code>를 눌러 New Secret을 눌러 환경변수를 생성해줍시다.<blockquote>
<p>GH_TOKEN 에는 아까 만든 토큰 생성할때 얻은 값을 사용하고<img src="https://images.velog.io/images/_uchanlee/post/a823c545-2dd6-45d4-bfbf-4861a73550e1/image.png" alt="">
GIST_ID 에는 사진과 같이 <a href="https://gist.github.com/woochanleee/%EC%9D%B4%EB%B6%80%EB%B6%84%EC%9D%84%EB%B3%B5%EC%82%AC%ED%95%B4%EC%84%9C%EB%B6%99%EC%9D%B4%EB%A9%B4%EB%90%A9%EB%8B%88%EB%8B%A4">https://gist.github.com/woochanleee/이부분을복사해서붙이면됩니다</a>. <img src="https://images.velog.io/images/_uchanlee/post/333c70d3-e9d5-4472-b4c3-a40a280e4446/image.png" alt=""></p>
</blockquote>
</li>
<li>이제 모든 설정이 끝났습니다. 하지만 포크했던 레포지스터리에서 <code>action</code> 탭에서 <img src="https://images.velog.io/images/_uchanlee/post/17aee433-4eb5-4371-8b75-2da974082b6f/image.png" alt=""> understand my workflows, 를 클릭해서 활성화 시켜줍시다.</li>
<li>이러면 이제 매 정각 업데이트가 될텐데요. 저는 지금 당장 결과가 보이게 하고 싶으니 포크한 프로젝트에 푸쉬를 하나 해서 업데이트를 해주도록 하겠습니다.<blockquote>
<p>저같은 경우엔 REAMD.md 에 공백 하나를 입력하고 update 해줬습니다. 그러면 <img src="https://images.velog.io/images/_uchanlee/post/17c5fe5a-e3df-4f3a-bae1-3bc59876187a/image.png" alt=""> 이렇게 actions 탭에서 workflows가 정상 작동한것을 볼 수 있습니다. 그리고 gist에 들어가보면 <img src="https://images.velog.io/images/_uchanlee/post/1243b8cf-226f-4f36-bfb3-37b8df4388b5/image.png" alt=""> 정상 업데이트 된것을 볼수 있습니다.</p>
</blockquote>
</li>
<li>이제 마지막으로 깃헙 프로필에 pinned를 해서 다른 사용자들이 볼수 있게 설정해보겠습니다.<blockquote>
<p>사진과 같이 정상으로 보입니다. <img src="https://images.velog.io/images/_uchanlee/post/5b2a4c54-1014-4c0f-ad7b-9412c9fb5dcd/image.png" alt=""></p>
</blockquote>
</li>
</ol>
<p>+) Weekly development breakdown은 자신이 wakatime 과 연동해서 무슨 언어를 많이 했는지 파악 할수 있습니다. 이거 같은 경우에도 본 포스트에서 소개한 방법과 마찬가지로 (<a href="https://github.com/maxam2017/waka-box">https://github.com/maxam2017/waka-box</a>)(저는 이걸로 포크해서 사용중입니다.) 또는 (<a href="https://github.com/matchai/waka-box)%EB%A5%BC">https://github.com/matchai/waka-box)를</a> 포크하고 설정해주면 됩니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue.js(ts) + webpack으로 Hello, world! 찍기]]></title>
            <link>https://velog.io/@_uchanlee/vue.js-ts-webpack</link>
            <guid>https://velog.io/@_uchanlee/vue.js-ts-webpack</guid>
            <pubDate>Mon, 01 Jun 2020 11:28:04 GMT</pubDate>
            <description><![CDATA[<p>지금까지는 리액트를 사용하면서 웹사이트를 개발을 했다. 그런데 이번에는 새로운걸 도전해보고 싶어서 vue.js를 ts와 webpack으로 사용해보고 그 과정을 기록해보겠다. 처음 도전하는데 어려움이 있는 경우 이 글을 읽으면 Hello, world를 찍을수 있으니 잘 따라오도록 하자.</p>
<h2 id="코드-작성">코드 작성</h2>
<h3 id="프로젝트-생성">프로젝트 생성</h3>
<pre><code>mkdir vue-webpack &amp;&amp; cd vue-webpack
yarn init -y</code></pre><p><code>package.json</code>은 아래와 같을 것이다.</p>
<pre><code>{
  &quot;name&quot;: &quot;vue-webpack&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;license&quot;: &quot;MIT&quot;,
}</code></pre><p>typescript를 사용할것이므로 <code>tsconfig.json</code>을 설정해준다.</p>
<pre><code>{
    &quot;compilerOptions&quot;: {
        &quot;module&quot;: &quot;CommonJS&quot;,
    },
    &quot;include&quot;: [
        &quot;./src/**/*&quot;,
    ]
}</code></pre><h3 id="라이브러리-설치">라이브러리 설치</h3>
<p>다음으로 이 프로젝트에 필요한 라이브러리를 설치하자.</p>
<pre><code>yarn add vue vue-loader vue-template-compiler vue-styleloader typescript ts-loader css-loader webpack webpack-cli webpack-dev-server</code></pre><blockquote>
</blockquote>
<ul>
<li>vue: vue를 사용하기 위함</li>
<li>vue-loader: vue를 단일 파일 컴포넌트를 지원함</li>
<li>vue-template-compiler: vue의 template을 컴파일하기 위함?(이게 없으니 에러가떠서 추가해줌)</li>
<li>vue-styleloader: .vue안에 있는 style태그를 사용하기 위함</li>
<li>typescript: typescript를 사용하기 위함</li>
<li>ts-loader: ts를 해석하기 위함</li>
<li>css-loader: css파일을 사용하기 위함</li>
<li>webpack: webpack을 사용하기 위함</li>
<li>webpack-cli: webpack 명령어로 build 등을 하기 위함</li>
<li>webpack-dev-server: 코드의 변경사항을 바로 적용하기 위함</li>
</ul>
<h3 id="webpack-설정">webpack 설정</h3>
<p><code>webpack.config.js</code>의 파일은 아래와 같을 것이다.</p>
<pre><code>var path = require(&#39;path&#39;)
var webpack = require(&#39;webpack&#39;)
const VueLoaderPlugin = require(&#39;vue-loader/lib/plugin&#39;);

module.exports = {
    entry: &#39;./src/main.ts&#39;, // 최초 실행될 entry 파일
    output: {
      path: path.resolve(__dirname, &#39;./dist&#39;), build 결과가 저장될 폴더
      publicPath: &#39;/dist/&#39;, // 이걸 지우게 되면 css가 적용이 안되는게 확인됨. 약간 경로 관련해서 문제가 있던걸로 기억하는데 일단 path랑 똑같이 지정해준다.
      filename: &#39;build.js&#39; // build 결과 파일 이름
    },
  module: {
    rules: [ // build 규칙들
      {
        test: /\.vue$/, // .vue파일을
        loader: &#39;vue-loader&#39; // vue-loader로 해석
      },
      {
        test: /\.ts$/, // .ts파일을
        loader: &#39;ts-loader&#39; // ts-loader로 해석
        },
      {
        test: /\.css$/, // .css파일을
        use: [
          &#39;vue-style-loader&#39;, // vue-style-lodaer을 이용해서
          {
            loader: &#39;css-loader&#39;, // css-loader로 해석. 구글링을 찾은 방법
          }
        ]
      }
    ]
  },
  devServer: { // webpack-dev-server 옵션 설정
    host: &quot;localhost&quot;, // localhost 설정
    port: 5500, // 포트 설정
    // noInfo: true, // true로 했더니 실행이 안되는줄알고 몇시간 삽질을 했다. 지워주자.
    open: true, // localhost:5500 으로 새탭 열림
  },
  plugins: [
    new VueLoaderPlugin() // 이것도 오류가나서 내용 보니까 설정해주라 해서 설정해줌.
  ]
}</code></pre><h3 id="maints">main.ts</h3>
<p><code>./src/components/main.ts</code> 코드를 짜보자.</p>
<pre><code>import Vue from &#39;vue&#39;;
import App from &#39;./App.vue&#39;;

new Vue({
    render: h =&gt; h(App),
}).$mount(&#39;#app&#39;);</code></pre><p>위 코드를 해석하자면
<a href="https://goodteacher.tistory.com/85">참고 블로그</a></p>
<pre><code>render: function(createElement) {
  return createElement(App);
}</code></pre><p>일반적으로 h는 hyperscript의 약자로 Virtural DOM에서 관용적으로 사용되는 표현인데 HTML 구조를 생성하는 스크립트라는 의미 라고 에반유는 이야기하고 있다.
또 <code>new Vue({ ... }).$mount(&#39;#app&#39;)</code>이 부분은
<a href="https://kr.vuejs.org/v2/api/index.html#vm-mount">공식문서</a>에 나와 있듯이 id가 app인 태그에 엘리먼트를 삽입하는 부분이다.</p>
<pre><code>var MyComponent = Vue.extend({
  template: &#39;&lt;div&gt;Hello!&lt;/div&gt;&#39;
})

// 생성하고 #app에 마운트 합니다.(#app을 대체합니다)
new MyComponent().$mount(&#39;#app&#39;)

// 위와 같습니다.
new MyComponent({ el: &#39;#app&#39; })

// 또는 문서를 렌더링하고 나중에 추가할 수 있습니다.
var component = new MyComponent().$mount()
document.getElementById(&#39;app&#39;).appendChild(component.$el)</code></pre><h3 id="appvue">App.vue</h3>
<p>vue 관련 코드에 관한 설명은 <a href="https://lannstark.tistory.com/6">블로그</a>여기에서 이해를 해보자.
<code>App.vue</code> 코드를 살펴보자.</p>
<pre><code>
&lt;template&gt; // template는 html template를 짜는 부분인것 같다.
    &lt;div id=&quot;app&quot;&gt;
        &lt;HelloWorld msg=&quot;Welcome to Yout Vue.js App&quot; /&gt; // HelloWorld 컴포넌트에 msg props로 넘겨준 모습으로 예측된다.
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
import HelloWorld from &#39;./components/HelloWorld.vue&#39;; // 다른 컴포넌트를 임포트한다.

export default { // 반드시 객체를 export 해줘야 한다.
    name: &#39;app&#39;,
    components: {
        HelloWorld, // HelloWorld 컴포넌트를 &lt;HelloWorld&gt;로 사용하겠다고 정의, &quot;HelloWorld&quot;: HelloWorld와 같음
    }
}
&lt;/script&gt;

&lt;style&gt;
#app {
    background-color: #000000;
}
&lt;/style&gt;</code></pre><h3 id="helloworldvue">HelloWorld.vue</h3>
<p><code>./src/components/HelloWorld.vue</code>코드는 아래와 같다.</p>
<pre><code>&lt;template&gt;
    &lt;div&gt;
        &lt;p&gt;{{ greeting }} world!&lt;/p&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
    data: () =&gt; ({ greeting: &#39;hello&#39; }) // data가 약간 react state같은 개념 {{ greeting }} 으로 접근 가능한것을 볼 수 있다.
}
&lt;/script&gt;

&lt;style&gt;
p {
    font-size: 40px;
    color: white;
}
&lt;/style&gt;</code></pre><h3 id="타입-지정">타입 지정</h3>
<p>이렇게만 하면 오류가 나게 되는데 아마 App.vue를 import할수 없을 것이다. 이러한 경우엔 커스텀 d.ts 파일을 생성해줘야 하는데 직접하기엔 아직이여서 구글링으로 파일을 찾았다. 아래와 같다.
<code>./src/components/vue-shim.d.ts</code></p>
<pre><code>declare module &quot;*.vue&quot; {
    import Vue from &quot;vue&quot;;
    export default Vue;
}</code></pre><h3 id="indexhtml">index.html</h3>
<p><code>./index.html</code>을 생성해주자.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;&lt;/head&gt;
&lt;body&gt;
    &lt;div id=&quot;app&quot;&gt;
    &lt;/div&gt;
    &lt;script src=&quot;./dist/build.js&quot;&gt;&lt;/script&gt; // webpack build 후 스크립트 삽입
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="scripts-추가">scripts 추가</h3>
<p><code>package.json</code>에 명령어를 추가해주자.</p>
<pre><code>...
 &quot;scripts&quot;: {
    &quot;build&quot;: &quot;webpack&quot;, // webpack build
    &quot;start:dev&quot;: &quot;webpack-dev-server --hot&quot; // 개발시 코드 수정시 자동 반영
  },
...</code></pre><h3 id="hello-world-확인">Hello, world! 확인</h3>
<p>이제 개발 서버를 켜 결과를 확인해보자.</p>
<pre><code>yarn start:dev</code></pre><p>아래와 같이 잘 작동되고 hello world를 다르게 변경시켜도 바로 반영이 되는걸 볼 수 있다.
<img src="https://images.velog.io/images/_uchanlee/post/158e0c35-dbee-4e63-8ace-ea7527449bae/image.png" alt="">
전체 코드는 <a href="https://github.com/woochanleee/vue.js_study/tree/master/vue-webpack">github-uchanlee</a>에서 확인할 수 있다.</p>
<h2 id="마무리">마무리</h2>
<p>이틀간 vue.js를 ts, webpack으로 처음 실행해 봤는데 엄청 삽질을 많이 했다. 확실히 리액트와 다른부분이 있고 비슷한 개념도 있는것 같다. 이걸 개인프로젝트에 적용시킬지는 한번 고민해봐야할 문제인것 같다. vue또한 좋은 라이브러리임은 분명하다. 나중에 기회가 된다면 vue를 자세하게 파볼 생각이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mongoose 2단 SubDocument 사용법(with koa)]]></title>
            <link>https://velog.io/@_uchanlee/mongoose-2%EB%8B%A8-SubDocument-%EC%82%AC%EC%9A%A9%EB%B2%95with-koa</link>
            <guid>https://velog.io/@_uchanlee/mongoose-2%EB%8B%A8-SubDocument-%EC%82%AC%EC%9A%A9%EB%B2%95with-koa</guid>
            <pubDate>Sun, 24 May 2020 14:32:43 GMT</pubDate>
            <description><![CDATA[<p>몽구스로 블로그의 대댓글 스키마를 구현하다가 난관에 빠져 몇시간 삽질을하고 2단subdocument를 사용하는법을 찾아냈다. 내가 <strong><em>&#39;이렇게 하면 되려나?&#39;</em></strong> 하고 찾은 방법이기때문에 이게 정답이라고 말할순 없다. 그냥 단순 참고용으로 확인 바라고 다르게 사용하는 방법이 있다면 댓글로 남겨주면 정말 감사하겠다. 그럼 시작해보도록 하겠다.</p>
<p>나와 같은 문제를 겪거나 해결법이 필요해서 오신 분들을 <a href="#%EB%AA%A8%EB%8D%B8-%EC%83%9D%EC%84%B12">해결 코드</a>로 가서 확인해보고 문제를 해결하기를 추천드린다.</p>
<h2 id="정의">정의</h2>
<blockquote>
<p>📖 subDocument가 뭔데?</p>
</blockquote>
<p>먼저 이것에 대해 알기 전 MongoDB에서 스키마를 디자인 하는 방식에 대해 살펴보고 지나가야 한다.</p>
<h3 id="mongodb-특징-및-서브스키마">MongoDB 특징 및 서브스키마</h3>
<ol>
<li>몽고디비는 기존 RDBMS(관계형데이터베이스)와 스키마를 디자인하는 방식이 완전 다르다.</li>
<li>1 RDBMS에서 데이터베이스를 설계하면 다음과 같을 것이다. <del><em>정확하지 않음</em></del><img src="https://images.velog.io/images/_uchanlee/post/be45e7a8-7d14-4768-9dd7-765367a7a950/image.png" alt="대충"></li>
<li>2 하지만 몽고디비는 NoSQL기반므로 NoSQL에서는 모든 것을 문서 하나에 넣는다. 아래와 같다.<pre><code>  {
      _id: ObjectId,
      title: String,
      body: String,
      username: String,
      createdDate: Date,
      comments: [
          {
              _id: ObjectId,
              text: String,
              createdDate: Date,
           },
      ],
  }</code></pre><ol start="2">
<li>comments와 같이 문서 내부에 또다른 문서가 위치할 수 있는데, 이를 서브다큐먼트(subdocument)라고 한다.</li>
<li>서브다큐먼트 또한 일반 문서를 다루는 것처럼 쿼리할수 있다.</li>
<li>문서 하나에는 최대 16MB의 데이터를 넣을수 있는데, 100자 댓글 데이터가 약 0.24KB를 차지한다. 16MB는 16.384KB이니 문서 하나에 댓글 데이터를 약 68,000개 넣을수 있다. 서브다큐먼트에서 이 용량을 초과할 가능성이 있다면 컬레션을 불리시키는 것이 좋다. 
[참고 - 리액트를 다루는 기술 개정판]</li>
</ol>
</li>
</ol>
<h2 id="1단-subschema-구현">1단 subschema 구현</h2>
<p>먼저, 문서 내에 서브스키마를 하나 넣는 경우를 구현해 보겠다.
하나만 넣을 경우엔 굉장히 간단하다. <a href="https://mongoosejs.com/docs/subdocs.html">몽구스 subdocument 공식문서</a>
공식 문서에 나와있듯이 스키마를 구현할땐 다음과 같이 하면 된다.</p>
<h3 id="스키마-생성">스키마 생성</h3>
<pre><code>import mongoose from &quot;mongoose&quot;;

const { Schema } = mongoose;

const CommentSchema = new Schema({
  text: String,
  publishedDate: {
    type: Date,
    default: Date.now,
  },
});

const PostSchema = new Schema({
  title: String,
  body: String,
  publishedDate: {
    type: Date,
    default: Date.now,
  },
  comments: [CommentSchema] // comments를 CommentSchema타입의 배열로 설정해줌, 여기가 서브 스키마
});

const Post = mongoose.model(&quot;Post&quot;, PostSchema);

export default Post;</code></pre><p>이것처럼 댓글은 여러개가 올수 있으니 CommentSchema타입의 배열로 설정해 준 모습을 볼수 있다.</p>
<h3 id="모델-생성">모델 생성</h3>
<p>스키마를 만들었으니 이제 모델을 만들고 직접 값을 저장한뒤 데이터베이스에 저장해줘야 한다.
그럼 어떻게 하는지 알아보도록 하자. 다음 코드는 코드의 일부분이다.</p>
<pre><code>import Router from &#39;koa-router&#39;;
import Post from &quot;&quot;;
import { Context } from &quot;koa&quot;;

/*
  POST /comments
  {
      text: &#39;우와 좋은 정보 감사합니다!&#39;,    
  }
*/
const router = new Router();
router.post(&#39;/comments&#39;, async (ctx: any) =&gt; {
  const { id } = ctx.params;
  const { text } = ctx.request.body;
  const post: any = new Post(); // 먼저 그냥 model을 하나 생성해준다.

  const commentDoc = post.comments.create({ text }); // ✨핵심✨ comments에서 create함수를 사용해서 CommentSchema 스키마의 모델(?) 아무튼 값을 담을수 있는 문서가 하나 생긴다. 아직 정확히는 모르지만 스키마 타입의 변수라면 create함수가 있는것 같다. 그래서 post.create도 가능할것 같다고 본다.
  const newComments = [...postDoc.comments].concat(commentDoc);
  await Post.findOneAndUpdate(
    { id },
    {
      comments: newComments,
    },
    {
      new: true,
    }
  );
    ctx.body = commentDoc;
  } catch (e) {
    ctx.throw(500, e);
  }
});

</code></pre><p>자 이렇게 서브스키마가 하나 있는 경우에 모델을 생성해 보았다.
<strong>그러면 서브스키마 안에 서브스키마가 있는 경우는?</strong></p>
<h2 id="2단-서브스키마-구현">2단 서브스키마 구현</h2>
<p>서브 스키마내부에 서브스키마 타입의 변수가 또 있는 경우를 해결해 보겠다.</p>
<h3 id="스키마-생성-1">스키마 생성</h3>
<p>먼저 스키마의 구조는 다음과 같이 있다고 하자.</p>
<pre><code>import mongoose from &quot;mongoose&quot;;

const { Schema } = mongoose;

const RecommentSchema = new Schema({
  text: String,
  publishedDate: {
    type: Date,
    default: Date.now,
  },
});

const CommentSchema = new Schema({
  text: String,
  publishedDate: {
    type: Date,
    default: Date.now,
  },
});

const PostSchema = new Schema({
  title: String,
  body: String,
  publishedDate: {
    type: Date,
    default: Date.now,
  },
  comments: {
    type: [CommentSchema],
    default: {
      recomments: [],
    },
  }, // 이부분이 달라졌다. default 값을 선언안해두니 create()부분에서 recomments가 존재하지않다고 생성이 불가하였다.
});

const Post = mongoose.model(&quot;Post&quot;, PostSchema);

export default Post;

</code></pre><p>여기서 주의깊게 봐야할 부분은 PostSchema의 comments부분인데, 처음에 나는 <code>comments: [ComentSchema]</code>
라고만 설정해 주었는데 이렇게 하니까 create부분에서 comments의 값이 []로 빈 배열이여서 recomments를 생성하질 못했다. <em><del>갑자기 글을 쓰다 궁금해졌는데 그냥 schema를 import해와서 schema.create를 하면 문서가 만들어지나? 갑자기 궁금하네 아시는분 댓글좀 달아주세요!</del></em>
그냥 못참아서 직접 해봤는데 사진과 같다고한다. 음.. 왜 안되는지 모르겠다. 스키마 타입의 변수에만 create함수가 있는건가..? 흠....
<img src="https://images.velog.io/images/_uchanlee/post/5cd641d6-629a-4cf4-ae8a-f302b13afe6b/image.png" alt=""></p>
<h3 id="모델-생성2">모델 생성2</h3>
<p>그럼 이제 2단으로 구성되어있는 서브스키마를 어떻게 모델로 생성하는지 알아보도록 하자.</p>
<pre><code>import Post from &quot;../../../../models/post&quot;;
import { Context } from &quot;koa&quot;;
import Router from &#39;koa-router&#39;;

const router = new Router();
/*
  POST comments/:comment_id/recomment
  {
      text: &#39;이게 되네...&#39;,    
  }
*/
router.post(&#39;/comments&#39;, async (ctx: any) =&gt; {
  const { id } = ctx.params;
  const { text } = ctx.request.body;
  const post: any = new Post(); // 아까와 같이 문서(모델?)를 만들어준다.
  try {
    const recommentDoc = post.comments[0].recomments.create({ text }); // 🎇여기 핵심, post는 지금 디폴트로 값이 지정되있는것들만 값이 있는 상태인데 아까 comments의 디폴트 값으로 recomments를 넣어두었기 때문에 comments[0].recomments로 접근해서 create함수를 사용해서 모델(문서?)를 생성해준다.🎇
    await Post.findOneAndUpdate(
      { id },
      {
        recommentDoc
      },
    );
    ctx.body = recommentDoc;
  } catch (e) {
    ctx.throw(500, e);
  }
};
</code></pre><h2 id="마무리">마무리</h2>
<p>오늘 삽질을 하다가 우연히 해결하게 된 몽구스에서 서브스키마 안에 서브스키마를 넣는 방법을 정리해 보았다. 구글링도 엄청 많이 해봤는데 keyword: subdocument.id()함수 - 뭔진 이해 못함, $함수? - 배열 안에 있는 서브스키마를 선택할때 사용한다고 한것 같음, $push - 이것도 관련되서 찾아보다가 발견, 등 등 새로운 개념들을 보았는데 너무 어려웠다. 공식 문서만 보고 개발하기엔 아직 한계가 있는것 같다. (영어 잘하자...)</p>
<p>이렇게 오늘 하루 몽구스 쿼리문에 대해도 많이 습득하고 스키마 짜는 것에대해서도 배워갔다. 백엔드는 너무 어렵다!!! 조금 더 열심히 공부해야겠다는 마음가짐이 생겼다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[koa-body를 이용한 파일(이미지, 동영상) 처리(Using with koa-router)]]></title>
            <link>https://velog.io/@_uchanlee/koa-body%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%8F%99%EC%98%81%EC%83%81-%EC%B2%98%EB%A6%ACUsing-with-koa-router</link>
            <guid>https://velog.io/@_uchanlee/koa-body%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%8F%99%EC%98%81%EC%83%81-%EC%B2%98%EB%A6%ACUsing-with-koa-router</guid>
            <pubDate>Tue, 19 May 2020 14:20:59 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기-전">들어가기 전</h2>
<p>nodejs koa에서 이미지와 동영상 등 파일들을 처리하기위헤 있는 폼데이터(form-data)를 처리하는 방법에 대해 알아보고 정리하였다.</p>
<h3 id="koa-body란">koa-body란?</h3>
<p>먼저 나는 koa-body라는 라이브러리(모듈?)을 사용했다. 사실 이것을 사용하기 전에 multer, formidable, koa-multer, koa-formidable 등 여러 종류를 시도해 보았는데 typescript와 호환이 안되거나 내가 영어 실력이 딸려서 이해하지 못해 formdata를 다루는데 성공하지 못하여서 포기하고 koa-body를 쓰게 됐다.</p>
<h3 id="사용법">사용법</h3>
<p>설명하기 전 <a href="https://www.npmjs.com/package/koa-body">koa-body npm주소</a>에서 자세한 api를 볼수 있으니 참고 바란다. 또 나는 <code>koa-router</code>에 적용하는 방법을 사용했지만 Koa(app)에 적용하는 방법도 많으니 따로 찾아보면 쉽에 찾을수 있을것이다.</p>
<blockquote>
<p>설치</p>
</blockquote>
<pre><code>yarn add koa-body</code></pre><p>를 통해 설치를 먼저 해준다. 신기하게도 ex) types/koa-body 와같이 typescript 개발환경에서 type을 따로 설치 안해줘도 작동이 가능하다. 이유는 잘 모르겠다.</p>
<blockquote>
<p>코드</p>
</blockquote>
<pre><code>import { Context } from &#39;koa&#39;; 
import Router from &#39;koa-router&#39;;
import koaBody from &#39;koa-body&#39;; // koa-body 불러오기

const rotuer = new Router();

router.post(
  &#39;/post&#39;,
  koaBody({
    multipart: true,
  }),), // koaBody를 사용해서 multipart데이터를 허용한다고 설정해준다.
  (ctx: Context) =&gt; {
    const { title, body } = ctx.request.body; // formdata의 기본적인 text필드들은 ctx.request.body에 값이 들어오고
    const file = ctx.request.files.files; // file형식은 ctx.request.files에 들어온다. ctx.request.files.[files] 여기서 마지막 files는 내가 키값으로 설정해준 값이다. ex) new form().append(&#39;files&#39;, ~) 와 같이 키 값을 줬기 때문에 위와같이 접근한것이다.
    let files = [];
    for (let i = 0; i &lt; file.length; i++)
      files.push(fs.readFileSync(file[i].path)); // 나는 file들을 formdata로 읽어와 buffer로 데이터베이스에 저장했다.

    /* 
        여기부터
    */
    const post = new Post({
      title,
      body,
      tags,
      files,
      isPrivate,
    }); 
    try {
      await post.save();
      ctx.body = post;
    } catch (e) {
      ctx.throw(500, e);
  }
  /*
    여기까지는 mongoose, mongodb관련된 코드인데 무시해도 된다.
  */
 }
)</code></pre><p><img src="https://images.velog.io/images/_uchanlee/post/efe1abcc-b3cf-40ba-bfc5-0b2c8fcf5ec4/image.png" alt="">
npm 문서에 보면 multipart와 formidable 옵션이 있는데 둘다 multipart를 사용가능한것 처럼 보이는데 차이를 모르겠다. 나는 multipart가 익숙해서 이걸 옵션에 사용했다.</p>
<blockquote>
<p>성공</p>
</blockquote>
<p><img src="https://images.velog.io/images/_uchanlee/post/f589a327-4190-44a6-baaf-31ef15e761e8/image.png" alt="">
성공적으로 mongodb에 바이너리코드의 버퍼(?)가 저장된것을 볼수 있다.</p>
<h2 id="느낀점">느낀점</h2>
<p>오늘 하루는 formdata를 처리하기위해 5시간을 삽질한것 같다. 이렇게 한번 해결해 냈으니 다음에도 잘 해결할것이라 믿는다.</p>
<p>fomdata를 처리하는 방법을 알아봤는데 file을 다루는 것은 어느 분야에서든 어려운것 같다. 자주 접하면서 익숙해져가는게 답인것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Yarn CLI option 알아보기]]></title>
            <link>https://velog.io/@_uchanlee/Yarn-CLI-option-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@_uchanlee/Yarn-CLI-option-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 28 Apr 2020 07:48:59 GMT</pubDate>
            <description><![CDATA[<p>라이브러리나 프레임워크를 사용하다보면 모듈을 자주 깔게 된다. 예를 들어 리액트를 사용할때면 <code>yarn create react-app &lt;project_name&gt;</code>이런 식으로 사용하곤 한다.</p>
<p> 그런데 가끔 보면 cli 명령문 뒤에 <code>-D</code>와같이 다양한 옵션이 붙는 경우가 있다. 이게 무엇일까? 나는 이에 대해 궁금했고 공부 후 정리해보고자 글을 쓰게 됐다.</p>
<h2 id="목차">목차</h2>
<ol>
<li><a href="#yarn%EC%9D%B4-%EB%AD%90%EC%95%BC">Yarn(얀)</a></li>
<li><a href="#yarnlock">yarn.lock</a></li>
<li><a href="#cli-%EC%98%B5%EC%85%98">CLI 옵션</a></li>
<li><a href="#%EA%B8%80%EC%9D%84-%EB%A7%88%EC%B9%98%EB%A9%B0">글을 마치며</a></li>
</ol>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="yarn이-뭐야">Yarn이 뭐야?</h2>
<p><a href="https://classic.yarnpkg.com">yarn</a> 은 페이스북이 만든 javascirpt 의 package manager 이다.</p>
<h3 id="npm-vs-yarn">npm vs yarn</h3>
<p>npm은 yarn에 비해 성능이 떨어지고 보안 이슈, 의존 패키지의 버저닝 이슈 등이 있어 yarn을 자주 쓴다. </p>
<p>버저닝 이슈는 예를 들면 로컬에서 특정 패키지의 버전이 1.0.0인데 배포를 위한 빌드 서버에서는 시점에 따라 1.0.3으로 업데이트 될 수도 있음을 뜻한다.
<strong><a href="https://github.com/pnpm/benchmarks-of-javascript-package-managers">속도 차이 참고 링크</a></strong></p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="yarnlock">yarn.lock</h2>
<p>yarn으로 프로젝트를 하다보면 루트 디렉토리에 <code>yarn.lock</code>이란 파일이 생기는데(npm인 경우 <code>package-lock.json</code>) 매번 이게 무엇을 하는 파일인지 궁금했다. 그래서 이번 기회에 간단하게 무엇인지 알아봤다.</p>
<p>예를 들어 프로젝트를 개발자 두명이서 진행하는데 서로 react 버전이 다르다면</p>
<blockquote>
<p>A: 16.13.0
B: 16.13.1</p>
</blockquote>
<p>위와 같이 설치 버전이 다를수 있다. 만약 이렇게 된다면 A는 제대로 작동하지만 B는 작동이 안되는 경우가 발생할 수 있다. 이러한 상황은 패키지 매니저에서 패키지 잠금이 지원되지 않던 시절에 문제가 됐으며 이것을 해결하기 위한 패키지 잠금이 나타났다.</p>
<p><code>package-lock.json</code>이나 <code>yarn.lock</code>과 같은 패키지 잠금 파일에는 프로젝트에 패키지에 최초로 추가될 당시에 정확히 어떤 버전이 설치가 되었는지 기록한다.</p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="cli-옵션">CLI 옵션</h2>
<p>앞으로 나올 Yarn Command와 CLI option들은 전체를 다루지 않았으며
내가 지금까지 한번은 봤던 것들로만 이루어져 있다.
<a href="https://classic.yarnpkg.com/en/docs/cli/">Yarn-CLI</a> 공식 문서를 참고했다.</p>
<p><strong>각 command별로 option을 정리하겠다.</strong></p>
<h3 id="기본적인-내용">기본적인 내용</h3>
<h4 id="default-command">Default Command</h4>
<blockquote>
<p><code>yarn</code> 뒤에 다른 명령어 없이 실행하게되면 기본적으로 <code>yarn install</code>이 실행된다.</p>
</blockquote>
<h4 id="user-defined-scripts">User-defined scripts</h4>
<blockquote>
<p><code>yarn &lt;script&gt; [&lt;args&gt;]</code>는 사용자가 정의한 스크립트를 실행한다. 아래 <a href="#yarn-run">yarn run</a>에서 자세히  설명하겠다.</p>
</blockquote>
<h4 id="패지키-버전-표시-방법">패지키 버전 표시 방법</h4>
<blockquote>
<p>틸드(~): 버전의 마지막 자리의 범위에서만 자동으로 업데이트한다.
ex)
~0.0.1:    &gt;=0.0.1 and &lt;0.1.0
~0.1.1:    &gt;=0.1.1 and &lt;0.2.0</p>
</blockquote>
<p>캐럿(^)을 설명하기 전에 먼저 <a href="https://semver.org/">Semantic Versioning</a>(보통 SemVer라고 부른다.)를 설명해야 하는데 Node.js와 npm의 모듈은 모두 SemVer를 따른다. SemVer는 MAJOR.MINOR.PATCH의 버저닝을 따르는데 각 의미는 다음과 같다.</p>
<blockquote>
<ol>
<li>MAJOR version when you make incompatible API changes,</li>
<li>MINOR version when you add functionality in a backwards-compatible manner, and</li>
<li>PATCH version when you make backwards-compatible bug fixes.</li>
</ol>
</blockquote>
<p>즉, MAJOR 버전은 API의 호환성이 깨질만한 변경사항을 의미하고 MINOR 버전은 하위호환성을 지키면서 기능이 추가된 것을 의미하고 PATCH 버전은 하위호환성을 지키는 범위내에서 버그가 수정된 것을 의미한다.</p>
<blockquote>
<p>캐럿(^): Node.js 모듈이 이 SemVer의 규약을 따른다는 것을 신뢰한다는 가정하에서 동작한다. 그래서 MINOR나 PATCH버전은 하위호환성이 보장되어야 하므로 업데이트를 할수 있다.
ex)
^1.0.2    &gt;=1.0.2 and &lt;2.0
^1.0    &gt;=1.0.0 and &lt;2.0
^1    &gt;=1.0.0 and &lt;2.0</p>
</blockquote>
<h3 id="yarn-add">yarn add</h3>
<blockquote>
<p>package를 다운받을때 사용하는 명령어이다.</p>
</blockquote>
<ul>
<li>yarn add &lt;package...&gt;: dependencies에 추가한다.</li>
<li>yarn add &lt;package...&gt; [--dev/-D]: devDependencies에 추가한다.</li>
</ul>
<h3 id="yarn-global">yarn global</h3>
<blockquote>
<p>yarn global &lt;add/bin/list/remove/upgrade&gt; [--prefix]:
패키지를 시스템 전역에서 설치, 업데이트, 삭제한다. 
--prefix옵션이 패지키가 설치되는 경로를 지정하는것 같다.</p>
</blockquote>
<h3 id="yarn-create">yarn create</h3>
<blockquote>
<p>yarn create &lt; starter-kit-package &gt; [&lt; args &gt;]:
<code>create starter kits</code>을 이용해 프로젝트를 설치한다.</p>
</blockquote>
<ul>
<li>뒤에 args는 어떤 옵션이 들어갈 수 있는지 잘 모르겠다.
ex) 추측하건데 <code>yarn create react-app my-app --template typescript</code>와 같이 --template이 옵션인것 같다.<blockquote>
<p>공식문서에서 아래와 같이 CRA를 똑같이 할수 있다고 한다.
$ yarn global add create-react-app
$ create-react-app my-app</p>
</blockquote>
<h3 id="yarn-init">yarn init</h3>
<blockquote>
<p><code>yarn init</code>: <code>package.json</code>을 생성한다.
<code>yarn init --yes/-y</code> 뒤에 옵션은 <code>package.json</code>을 설정하는 질문을 모두 default로 넘어간다.</p>
</blockquote>
</li>
</ul>
<h3 id="yarn-install">yarn install</h3>
<blockquote>
<p><code>yarn install</code>: <code>pcakgae.json</code>의 의존성 모듈을 설치한다.
이때 <code>yarn.lock</code>도 같이 생긴다..</p>
</blockquote>
<h3 id="yarn-run">yarn run</h3>
<p><code>package.json</code></p>
<pre><code>{
  &quot;name&quot;: &quot;my-package&quot;,
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;babel src -d lib&quot;,
    &quot;test&quot;: &quot;jest&quot;
  }
}</code></pre><p>위와 같은 <code>package.json</code>이 있을때</p>
<blockquote>
<p>yarn run [script] [<args>]: scripts에 있는 스크립트를 실행 가능하다.
<code>yarn run test</code>와 같이 가능하다.
<code>yarn run test -o --watch</code>처럼 스크립트 뒤에 옵션을 달아줄 경우 <code>jest -o --watch</code>처럼 스크립트에 대응하는 명령문 뒤에 옵션이 붙는다.
<code>yarn test -o --watch</code>와 같이 run은 생략이 가능하다.
+) --watch 옵션은 jest의 CLI옵션으로 파일이 변경되면 다시 테스트를 진행하는것 같다.
+) -o 옵션또한 jest의 CLI옵션으로 --onlyChanged의 별칭이고 프로젝트에서 변경된 파일을 기준으로 테스트를 하는것 같다. 깃헙이나 Mercurial프로젝트만 가능하다고 한다.</p>
</blockquote>
<h3 id="yarn-upgrade">yarn upgrade</h3>
<blockquote>
<p><code>yarn upgrade [package | package@tag | package@version | --scope @scope]... [--ignore-engines] [--pattern]</code>:
<code>package.json</code>에 명시된 범위내에서 패키지의 버젼을 최신으로업그레이드 해주는 기능이다.
<code>yarn upgrade --latest</code>: --latest옵션은 upgrade처럼 작동하지만 <code>package.json</code>의 범위가 아닌 최신버전으로 설치가 된다.
<code>yarn upgrade left-pad@^1.0.0</code>이와 같이 직접 버젼을 설정해줄수도 있다.</p>
</blockquote>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="글을-마치며">글을 마치며</h2>
<p>오늘은 내가 궁금했던 CLI 옵션들을 공식문서를 읽으면서 정리해봤다. 그런데 내 의도와는 다르게 옵션들보다 yarn의 명령어를 더 정리한것 같다. 그래도 나름대로 옵션들에 대해 알수 있어서 만족한다.</p>
<p>오늘 내가 정리한a것은 yarn의 간의 기별만도 못한것 같다. 공식문서에 나와있는걸 보면 다양한 명령어들과 옵션들이 나와있다.</p>
<p>앞으로 개발을 하면서 더 많은 Command, CLI option들을 마주하게 될것이고 그때 마다 내 머릿속으로 집어 넣을 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 웹팩으로 개발 환경 구축하기(without CRA)]]></title>
            <link>https://velog.io/@_uchanlee/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9B%B9%ED%8C%A9%EC%9C%BC%EB%A1%9C-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0without-CRA</link>
            <guid>https://velog.io/@_uchanlee/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9B%B9%ED%8C%A9%EC%9C%BC%EB%A1%9C-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0without-CRA</guid>
            <pubDate>Mon, 27 Apr 2020 23:48:57 GMT</pubDate>
            <description><![CDATA[<p>지인의 블로그를 보다가 충격적인 사실을 발견했다.</p>
<blockquote>
<p>&quot;내가 회사에 가서도 <a href="https://github.com/facebook/create-react-app">CRA</a>를 통해 개발할까? &quot; 🤔</p>
</blockquote>
<p>에 대한 답변이 X(아니?) 라는 사실이였다. 몇개월 뿐이지만 리액트를 공부한 시점부터 현재까지 리액트로 개발한 프로젝트를 모두 CRA를 통해 진행하였다. 그래서 난 이게 당연하고 정답인줄 알았다. 하지만 이곳의 세계는 너무나도 깊었다. 해답을 찾던중 <a href="https://webpack.js.org/">webpack</a>으로 CRA를 대신할수 있다는 사실을 알았고 이에 대해 공부하고 모르는 개념을 정리하는 시간을 가져보도록 하겠다.</p>
<h2 id="목차">목차</h2>
<ol>
<li><a href="#%EC%9B%B9%ED%8C%A9%EC%9D%B4%EB%9E%80">웹팩이란?</a></li>
<li>1 <a href="#%EB%AA%A8%EB%93%88module">모듈</a></li>
<li>2 <a href="#%EB%B2%88%EB%93%A4%EB%9F%ACbundler">번들러</a></li>
<li>3 <a href="#%EC%9B%B9%ED%8C%A9webpack">웹팩</a></li>
<li><a href="#%EC%8B%A4%EC%8A%B5webpack-react">실습(webpack-react)</a></li>
<li>0 <a href="#0-node-%EC%84%A4%EC%B9%98">node 설치</a></li>
<li>1 <a href="#1-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%8F%B4%EB%8D%94-%EC%83%9D%EC%84%B1-%EB%B0%8F-packagejson-%EC%83%9D%EC%84%B1">프로젝트 폴더 생성 및 package.json 생성</a></li>
<li>2 <a href="#2-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%84%A4%EC%B9%98">라이브러리 설치</a></li>
<li>3 <a href="#3-babel-%EC%84%A4%EC%A0%95">Babel 설정</a></li>
<li>4 <a href="#4-webpack-%EC%84%A4%EC%A0%95">Webpack 설정</a></li>
<li>5 <a href="#5-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%A7%8C%EB%93%A4%EA%B8%B0">리액트 애플리케이션 만들기</a></li>
<li>6 <a href="#6-hot-module-replacement-hmr-%EC%84%A4%EC%A0%95">Hot Module Replacement (HMR) 설정</a></li>
<li>7 <a href="#7-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B9%8C%EB%93%9C">프로젝트 빌드</a></li>
<li><a href="#%EB%8A%90%EB%82%80%EC%A0%90-%EB%B0%8F-%EC%95%9E%EC%9C%BC%EB%A1%9C-%EA%B3%84%ED%9A%8D">느낀점 및 앞으로 계획</a></li>
</ol>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="웹팩이란">웹팩이란?</h2>
<p>먼저 웹팩에 대해 알아보겠다. 구글에 웹팩을 검색하게되면 <strong>&quot;자바스크립트 모듈 번들러&quot;</strong>라고 나온다. 근데 모듈 번들러가 뭘까?</p>
<h3 id="모듈module">모듈(Module)</h3>
<ul>
<li>프로그램을 구성하는 독립적인 요소이다.</li>
<li>데이터와 함수들을 묶어서 모듈을 형성하고 파일 단위로 나눈다.</li>
<li>모듈화 프로그래밍은 기능별로 파일을 나눠서 프로그래밍을 하는 것으로 유지보수가 쉽다는 장점이 있다.<h3 id="번들러bundler">번들러(Bundler)</h3>
</li>
<li>번들러는 여러개의 파일을 하나의 파일로 만들어주는 라이브러리이다.</li>
<li>번들러를 사용하면 소스 코드를 모듈별로 작성할 수 있다.</li>
<li>대표적인 예로 웹팩(wepback), Parcel 등이 있다.</li>
</ul>
<h3 id="웹팩webpack">웹팩(Webpack)</h3>
<ul>
<li>웹팩(Webpack)은 자바스크립트 모듈 번들러이다</li>
<li>웹팩에서 모든 것은 모듈이다. 자바스크립트, CSS, 이미지 등 모든 것을 모듈로 관리한다.</li>
<li>웹팩의 주요 네 가지 개념으로 Entry, Output, Loader, Plugin이 있다.</li>
</ul>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h4 id="131-entry">1.3.1 Entry</h4>
<p><img src="https://images.velog.io/images/_uchanlee/post/ec13826a-115e-4854-bac6-d6ed9ebe101c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-27%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2010.42.48.png" alt=""></p>
<ul>
<li>위 사진처럼 모듈을 많이 불러올경우 의존성이 증가한다.</li>
<li>의존성 그래프의 시작점을 웹팩에서는 엔트리(Entry)라고 한다.</li>
<li>웹팩은 엔트리를 통해서 필요한 모듈을 로딩하고 하나의 파일로 묶는다. </li>
<li>여러개의 엔트리가 존재할 수 있다.<h4 id="132-output">1.3.2. Output</h4>
</li>
<li>엔트리에 설정한 자바스크립트 파일을 시작으로 하나로 묶는다.</li>
<li>번들된 결과물을 처리할 위치를 output에 기록한다.<h4 id="133-loader">1.3.3 Loader</h4>
</li>
<li>웹팩은 JavaScript와 Json만 이해할 수 있다.</li>
<li>로더는 다른 type의 파일(img, font, stylesheet 등)을 웹팩이 이해할 수 있게 변경해준다.<h4 id="134-plugin">1.3.4 Plugin</h4>
</li>
<li>Loader은 모듈을 처리하지만 Plugin은 번들된 파일을 처리한다.</li>
<li>Plugin은 번들된 파일을 난독화 하거나 압축하는데 사용된다.</li>
<li>예를 들어 bundle한 css파일과 js파일을 각각 html 파일에 link 태그와 script태그로 추가해줘야 하는데 HtmlWebpackPlugin은 이것을 자동화해준다.</li>
</ul>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="실습webpack-react">실습(webpack-react)</h2>
<h3 id="0-node-설치">0. node 설치</h3>
<p>실습하기 전 <a href="https://nodejs.org/ko/">node</a>가 준비되어야 한다.</p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h3 id="1-프로젝트-폴더-생성-및-packagejson-생성">1. 프로젝트 폴더 생성 및 package.json 생성</h3>
<p><strong>(의존성 초기화)</strong>
<code>webpack-react-study</code> 폴더를 생성하고 <code>package.json</code> 파일 생성</p>
<pre><code>mkdir webpack-react-study &amp;&amp; cd $_
yarn init -y</code></pre><blockquote>
<p>-y 옵션은 All Yes로 yarn init만 했을시 나타나는 질문에 모두 yes로 넘어가게 해준다. 원하는대로 설정해도 상관 없으나 필자는 -y옵션으로 진행했다.</p>
</blockquote>
<p><code>package.json</code>파일은 아래와 같을 것이다.</p>
<pre><code>{
  &quot;name&quot;: &quot;webpack-react-study&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;license&quot;: &quot;MIT&quot;
}</code></pre><p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h3 id="2-라이브러리-설치">2. 라이브러리 설치</h3>
<p>이번 실습을 진행할때 필요한 초기 라이브러리를 설치하겠다. 초기 프로덕션 의존성(production dependencies) 과 개발 의존성(development dependencies)을 설치한다. 개발 의존성은 개발 단계에서만 사용되는 의존 라이브러리이며, 프로덕션 의존성은 배포 단계에서 사용되는 라이브러리를 말한다.</p>
<pre><code>yarn add react react-dom

yarn add @babel/core babel-loader @babel/preset-env @babel/preset-react css-loader style-loader html-webpack-plugin webpack webpack-dev-server webpack webpack-cli -D</code></pre><p>설치된 라이브러리가 무엇인지 간단하게 알아보자.</p>
<blockquote>
<ul>
<li>react: 리액트</li>
</ul>
</blockquote>
<ul>
<li>react-dom: 브라우저 DOM을 제어한다. UI를 렌더링할때 사용한다.</li>
<li>@babel/core: babel의 핵심 라이브러리로 es6를 es5로 컴파일해준다.</li>
<li>babel-loader: babel과 webpack을 사용해서 자바스크립트 파일을 컴파일한다.</li>
<li>@babel/preset-env: es6, es7 버전을 지정안해도 babel이 자동 탐지한다.</li>
<li>@babel/preset-react: 리액트(JSX)를 js로 인식가능하게 한다.</li>
<li>css-loader: css 파일을 import 또는 require할 수 있게 한다.</li>
<li>style-loader: css파일을 style태그로 만들어 head에 삽입한다.</li>
<li>html-webpack-plugin: 웹팩 번들에 html파일을 제공한다.</li>
<li>webpack: 웹팩을 사용하기위한 주 이다.</li>
<li>webpack-dev-server: 웹팩 개발 서버를 구동할수 있다.</li>
<li>webpack-cli: 웹팩 커맨드라인을 사용할 수 있다.</li>
</ul>
<p>babel이 무엇인가 하면 <a href="https://babeljs.io/">babel</a>은 es6코드를 이전 버전에서 실행 가능하도록 변환해주는 컴파일러이다. babel에 대한 사용법은 나중에 아래에서 살펴보자.</p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h3 id="3-babel-설정">3. Babel 설정</h3>
<p>프로젝트 최상위 <code>webpack-react-study</code>에 바벨 설정 파일을 만든다.</p>
<pre><code>touch .babelrc</code></pre><p><code>.babelrc</code>파일을 열어 아래와 같이 코드를 추가한다.</p>
<pre><code>{
    &quot;presets&quot;: [&quot;@babel/preset-env&quot;, &quot;@babel/preset-react&quot;]
}</code></pre><blockquote>
<ul>
<li>babel 그 자체로는 아무것도 동작하지 않는다. 무언가 하게하려면 plugin을 설치한다.</li>
</ul>
</blockquote>
<ul>
<li>먼저 <strong>.babelrc</strong>라는 파일은 바벨에 대한 플러그인들을 설정해주는 파일이다.</li>
<li>매번 플러그인을 설치하고 등록하기 귀찮아 <strong>&quot;preset&quot;</strong>이라는 기능을 만들었다.</li>
<li>&quot;presets&quot;이 무엇이냐면 플러그인들을 포함한 번들파일인데 preset에 한번만 설정하면 플러그인들이 자동으로 설치된다.</li>
</ul>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h3 id="4-webpack-설정">4. Webpack 설정</h3>
<p>지금부터 웹팩을 설정해보자. <code>webpack.config.js</code>라는 파일을 생성하자.</p>
<pre><code>touch webpack.config.js</code></pre><p>그 다음 아래 코드를 작성하자.</p>
<pre><code>const webpack = require(&#39;webpack&#39;);
const HtmlWebpackPlugin = require(&#39;html-webpack-plugin&#39;);

const port = 3000;

module.exports = {
  // webpack 설정 코드 작성. 작성된 코드는 module.export로 내보냅니다.
};</code></pre><blockquote>
<ul>
<li>기본적으로 webpack과 html-webpack-plugin이 필요하다.</li>
</ul>
</blockquote>
<ul>
<li>4번째 줄에 보통 &quot;const port = process.env.PORT || 3000;&quot; 이렇게 사용하는데 나는 환경변수(process.env)를 어떻게 사용하는지 몰라 지웠다.</li>
</ul>
<h4 id="mode">mode</h4>
<p><code>webpack.config.js</code>파일을 열어 아래 코드를 추가하도록 하자.</p>
<pre><code>...
module.exports = {
  mode: &#39;development&#39;,
};</code></pre><blockquote>
<ul>
<li>mode옵션은 웹팩 설정이 development(개발)모드인지 production(프로덕션) 모드인지 정한다.</li>
</ul>
</blockquote>
<ul>
<li>development 모드는 개발자 경험에 초점이 맞춰진 모드이고 production모드는 배포에 초점이 맞춰진 모드이다.</li>
<li>공부 목적이므로 development를 선택했다.</li>
</ul>
<h4 id="entry-output">entry, output</h4>
<pre><code>module.exports= {
  ...
  entry:&#39;./src/index.js&#39;,
  output:{
      path: __dirname + &#39;/dist&#39;,
      filename: &#39;bundle.[hash].js&#39;
  }
}</code></pre><blockquote>
<ul>
<li>entry 옵션은 앱이 있는 위치와 번들링 프로세스가 시작되는 지점이다.</li>
</ul>
</blockquote>
<ul>
<li>Webpack4(웹팩4)부터는 entry 옵션을 생략할 수 있습니다. 생략할 경우 ./src/index.js를 기본으로 가정한다.</li>
<li>output 옵션은 번들링 프로세스가 끝난 뒤 번들링된 파일을 저장할 장소와 이름을 지정한다.</li>
<li>번들링된 파일 이름을 bundle.[hash].js로 하고 그 파일을 ./dist에 저장하라는 의미이다.</li>
<li>filename의 [hash]는 어플리케이션이 수정되어 다시 컴파일될 때마다 Webpack(웹팩)에서 생성된 해시로 변경해 캐싱에 도움이 된다.</li>
</ul>
<h4 id="module">module</h4>
<pre><code>...
module.exports = {
  ...
  module: {
    rules: [

      // 첫 번째 룰
      {
        test: /\.(js)$/,
        exclude: /node_modules/,
        use: [&#39;babel-loader&#39;]
      },

      // 두 번째 룰
      {
        test: /\.css$/,
        use: [
          {
            loader: &#39;style-loader&#39;
          },
          {
            loader: &#39;css-loader&#39;,
            options: {
              modules: true,
              camelCase: true,
            }
          }
        ]
      }
    ]
  },
};</code></pre><blockquote>
<ul>
<li>module옵션은 번들링과정에서 사용할 규칙을 설정한다.</li>
</ul>
</blockquote>
<ul>
<li>ES6, ES7문법으로 작성된 javascript 파일을 ES5로 바꾸기 위해 작성한 .babelrc를 babel-loader를 이용해 규칙에 적용한다.</li>
<li>node_moudules 폴더를 제외한 모든 .js파일에 babel-loader(.babelrc에 설정한 파일)를 적용한다.</li>
<li>.js파일에서 import 또는 require로 .css파일을 가져올수 있게 해주는 css-loader와 .css파일을 style태그로 만든뒤 head태그 안에 삽입해주는 style-loader를 규칙에 적용한다.<ul>
<li>css-loader의 options는 css-loader에 적용할 옵션이다. </li>
<li>modules - CSS Module 사용한다.</li>
<li>camelCase - CamelCase로 CSS를 사용한다.</li>
</ul>
</li>
</ul>
<h4 id="예시-코드">예시 코드</h4>
<pre><code>/src/App.css
.main_wrapper {
    background-color: blue;
}</code></pre><p>라는 클래스가 있다면 </p>
<pre><code>/src/App.js
import { mainWrapper } from &#39;App.css`;</code></pre><p>와 같이 사용가능하다.</p>
<h4 id="plugin">plugin</h4>
<pre><code>module.exports = {
  ...
  plugins: [
    new HtmlWebpackPlugin({
      template: &#39;public/index.html&#39;,
    })
  ],
};</code></pre><blockquote>
<ul>
<li>plugins옵션은 웹팩 번들과정에 적용할 플러그인을 설정해준다.</li>
</ul>
</blockquote>
<ul>
<li>HtmlWebpackPlugin은 html파일이나 favicon을 번들링과정에 포함한다.</li>
<li>예를들어 번들된 파일 <code>bundle.[hash].js</code>를 index.html에 자동 삽입해준다.</li>
</ul>
<h4 id="devserver">devServer</h4>
<pre><code>module.exports = {
  ...
  devServer: {
    host: &#39;localhost&#39;,
    port: port,
    open: true,
    historyApiFallback: true
  }
};</code></pre><blockquote>
<ul>
<li>devServer 개발서버를 정의하는 옵션이다.</li>
</ul>
</blockquote>
<ul>
<li>host는 로컬호스트로 지정하고 port는 상단에서 정의한 값으로 설정해준다.</li>
<li>open(true)은 서버를 실행했을 때 자동으로 브라우저를 열어주는 옵션이다.</li>
<li>historyApiFallback은 브라우저에서 URL을 변경할 수 있도록 도와주는 옵션이다.</li>
</ul>
<h4 id="완성된-webpackconfigjs코드">완성된 <code>webpack.config.js</code>코드</h4>
<pre><code>const webpack = require(&#39;webpack&#39;);
const HtmlWebpackPlugin = require(&#39;html-webpack-plugin&#39;);

const port = 3000;

module.exports = {
    mode: &#39;development&#39;,
    entry:&#39;./src/index.js&#39;,
    output:{
        path: __dirname + &#39;/dist&#39;,
        filename: &#39;bundle.[hash].js&#39;,
        publicPath: &#39;/&#39;
    },
    module: {
        rules: [
        // 첫 번째 룰
        {
            test: /\.(js)$/,
            exclude: /node_modules/,
            use: [&#39;babel-loader&#39;]
        },
        // 두 번째 룰
        {
            test: /\.css$/,
            use: [
                {
                    loader: &#39;style-loader&#39;
                },
                {
                    loader: &#39;css-loader&#39;,
                    options: {
                        modules: true,
                        localsConvention: &#39;camelCase&#39;,
                    }
                }
            ]
        }
    ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: &#39;public/index.html&#39;,
        })
    ],
    devServer: {
        host: &#39;localhost&#39;,
        port: port,
        open: true,
        historyApiFallback: true,
        hot: true
    }
};</code></pre><p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h3 id="5-리액트-애플리케이션-만들기">5. 리액트 애플리케이션 만들기</h3>
<p>먼저 HtmlWebpackPlugin에 사용되는 <code>index.html</code>을 아래와 같이 생성한다.</p>
<pre><code>mkdir public &amp;&amp; cd $_ &amp;&amp; touch index.html</code></pre><p>public/index.html</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
  &lt;meta charset=&quot;UTF-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
  &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;ie=edge&quot;&gt;
  &lt;title&gt;webpack-react-study&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>루트 디렉토리로 돌아와 <code>index.js</code>를 생성한다.</p>
<pre><code>mkdir src &amp;&amp; cd $_ &amp;&amp; touch index.js</code></pre><p>components/App.js와 App.css를 생성하고 아래와 같이 코드를 작성한다.</p>
<pre><code>mkdir components &amp;&amp; cd $_ &amp;&amp; touch App.js App.css</code></pre><p>src/components/App.js</p>
<pre><code>import React from &#39;react&#39;;
import { mainWrapper } from &#39;./App.css&#39;;
const App = () =&gt; {
    return (
        &lt;div className={mainWrapper}&gt;
            &lt;h1&gt;Hello, Webpack!!! with React&lt;/h1&gt;
        &lt;/div&gt;
    );
};

export default App;</code></pre><p>src/components/App.css</p>
<pre><code>.main_wrapper {
    background-color: blue;
}</code></pre><p>리액트 프로젝트가 거의 다 완성됐다.
<code>package.json</code>에 script를 추가하자.</p>
<pre><code>...
&quot;scripts&quot;: {
    &quot;start&quot;: &quot;webpack-dev-server&quot;
  },
...</code></pre><p><code>package.json</code> 전체 코드는 아래와 같다.</p>
<pre><code>{
  &quot;name&quot;: &quot;webpack-react-study&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;license&quot;: &quot;MIT&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;webpack-dev-server&quot;,
    &quot;build&quot;: &quot;webpack&quot;
  },
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^16.13.1&quot;,
    &quot;react-dom&quot;: &quot;^16.13.1&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@babel/core&quot;: &quot;^7.9.0&quot;,
    &quot;@babel/preset-env&quot;: &quot;^7.9.5&quot;,
    &quot;@babel/preset-react&quot;: &quot;^7.9.4&quot;,
    &quot;babel-loader&quot;: &quot;^8.1.0&quot;,
    &quot;css-loader&quot;: &quot;^3.5.3&quot;,
    &quot;html-webpack-plugin&quot;: &quot;^4.2.0&quot;,
    &quot;webpack&quot;: &quot;^4.43.0&quot;,
    &quot;webpack-cli&quot;: &quot;^3.3.11&quot;,
    &quot;webpack-dev-server&quot;: &quot;^3.10.3&quot;
  }
}
</code></pre><h4 id="리액트-실행">리액트 실행</h4>
<p>이제 터미널에서 <code>yarn start</code>로 개발 서버를 작동한다.
아래 사진과 같이 번들 파일이랑 css가 잘 적용된 모습을 볼수 있다.
<img src="https://images.velog.io/images/_uchanlee/post/eaac087c-5ae7-4332-b3f0-711bb7facf25/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-28%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.12.36.png" alt=""></p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h3 id="6-hot-module-replacement-hmr-설정">6. Hot Module Replacement (HMR) 설정</h3>
<p>먼저 react-hot-loader란 코드가 변경되었을 때 페이지를 새로고침하지 않고 바뀐부분만 빠르게 교체해주는 라이브러리이다.
그럼 터미널에서 <a href="https://github.com/gaearon/react-hot-loader">react-hot-loader</a>를 설치하자.
<code>yarn add react-hot-loader -D</code></p>
<p>그다음 <code>.babelrc</code>파일을 열고 plugin을 추가해주자.</p>
<pre><code>{
    &quot;presets&quot;: [&quot;@babel/preset-env&quot;, &quot;@babel/preset-react&quot;],
    &quot;plugins&quot;: [&quot;react-hot-loader/babel&quot;]
}</code></pre><p><code>webpack.config.js</code>도 수정해주자.</p>
<pre><code>...
module.exports = {
  entry: &#39;./src/index.js&#39;,
  output: {
    ...
    publicPath: &#39;/&#39;
  },
  ...
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    ...
  ],
  devServer: {
    ...
    hot: true
  }
};</code></pre><blockquote>
<ul>
<li><strong>publicPath: &#39;/&#39;</strong> Hot reloading은 중첩된 경로에서 동작하지 않는다고 하여 설정해주지 않을경우 핫리로딩이 작동이 안된다 하는데 나는 자세하겐 모르겠다.</li>
</ul>
</blockquote>
<ul>
<li><strong>webpack.HotModuleReplacementPlugin — HMR</strong> 업데이트시 브라우저 터미널에 표시해 알아보기 쉽게 한다고 하는데 어떻게 표시가 되는진 모르겠다. 또 어떤 글에서 hot옵션을 주고 실행하게되면 <code>webpack.HotModuleReplacementPlugin</code>이 자동으로 추가된다고 했는데 확실하겐 모르겠다. <code>webpack.HotModuleReplacementPlugin</code>이 없고 <code>hot: true</code>만 있을때 핫리로딩이 잘 작동하긴 한다.</li>
<li>*<em>hot: true *</em>  - 서버에 HMR 작동을 허락한다</li>
</ul>
<p><code>App.js</code>에 hot을 적용시키자.</p>
<pre><code>import { hot } from &#39;react-hot-loader&#39;;
...
export default hot(module)(App);</code></pre><h4 id="실행-결과">실행 결과</h4>
<p><code>yarn start</code>로 실행후 <code>App.js</code>를 수정후 저장해보자. 브라우저가 새로고침하지 않고 변경사항이 반영된 모습을 볼수 있다. 또 크롬 개발자 도구에서 Rendering -&gt; Paint를 선택하면 사진과 같이 변겨오딘 부분에 블럭이 생겨 눈에 띈다.
<img src="https://images.velog.io/images/_uchanlee/post/7b5c2630-57f0-45a6-9f4b-bc6ab88a8863/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-28%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.49.25.png" alt="">
<strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h3 id="7-프로젝트-빌드">7. 프로젝트 빌드</h3>
<p>이번엔 프로젝트를 빌드해보도록 하겠다.
<code>yarn build</code>로 빌드를 하자.
아래 사진과 같이 정상적으로 dist폴더 밑에 bundle.[hash].js와 html이 생기는걸 볼수 있다.
<img src="https://images.velog.io/images/_uchanlee/post/40bd153d-5aaf-4e9b-8bd0-5e703b1582b8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-28%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.15.51.png" alt=""></p>
<h2 id="느낀점-및-앞으로-계획">느낀점 및 앞으로 계획</h2>
<p>오늘 처음으로 CRA를 사용하지 않고 webpack으로만 리액트 프로젝트를 구동시켜 봤다. 무작정 예제를 따라하고 에러가 났다. 그래서 공식 문서를 찾고 고쳐가면서 웹팩을 구성하는 부분이 무엇인지 알았고 이해할수 있엇다.</p>
<p>하지만 오늘 내가 정리한게 다가 아니다. webpack의 일부분만 배운것이다. 앞으로 웹팩의 다양한 기능을 공부하고 어느정도 작지않은 프로젝트를 webpack으로 개발해 볼것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Semantic UI(시맨틱 UI) 맛보기 😋]]></title>
            <link>https://velog.io/@_uchanlee/Semantic-UI%EC%8B%9C%EB%A7%A8%ED%8B%B1-UI-%EB%A7%9B%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@_uchanlee/Semantic-UI%EC%8B%9C%EB%A7%A8%ED%8B%B1-UI-%EB%A7%9B%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 26 Apr 2020 05:26:23 GMT</pubDate>
            <description><![CDATA[<p>최근에 공부를하다가 시맨틱 UI라는 프레임워크에 대해 알게 되었다. 가장 유명한 부트스트랩보다 이것에 흥미를 갖게 된것은 나는 색다른걸 좋아하기 때문이다. 그럼 시맨틱 UI를 공부하고 느낀점 그리고 간단한 사용법에 대해 적어보도록 하겠다.</p>
<h2 id="목차">목차</h2>
<ol>
<li><a href="#%EB%93%A4%EC%96%B4%EA%B0%80%EA%B8%B0-%EC%A0%84">들어가기 전</a></li>
<li><a href="#%EC%84%B8%ED%8C%85">세팅</a></li>
<li>1 <a href="#1%EB%8B%A8%EA%B3%84:-%ED%8C%8C%EC%9D%BC-%EB%8B%A4%EC%9A%B4%EB%B0%9B%EA%B8%B0">1단계</a></li>
<li>2 <a href="#%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1">2단계</a></li>
<li>3 <a href="#HTML%EC%95%88%EC%97%90-%EC%82%BD%EC%9E%85">3단계</a></li>
<li>4 <a href="#%EC%9E%98-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94%EC%A7%80-%ED%99%95%EC%9D%B8">4단계</a></li>
<li><a href="#%EC%82%AC%EC%9A%A9%EB%B2%95">사용법</a></li>
<li><a href="#%EB%8A%90%EB%82%80%EC%A0%90-%EB%B0%8F-%EB%82%98%EC%9D%98-%EC%83%9D%EA%B0%81">느낀점 및 나의 생각</a></li>
</ol>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="들어가기-전">들어가기 전</h2>
<p>필자는 <a href="https://opentutorials.org/course/2737">SemanticUI-생활코딩</a>과 <a href="https://semantic-ui.com">공식 문서</a>를 보고 공부했으며 이해한 내용을 바탕으로 정리함을 알린다. 또, 독자가 HTML/CSS/JS를 어느정도 알고 있다 가정하며 이에 대한 설명은 하지 않는다. 마지막으로 필자의 개발 환경은 (mac os catalina 10.15.4)이다. 이점 참고 바란다.</p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="세팅">세팅</h2>
<p>먼저 <a href="https://semantic-ui.com/introduction/getting-started.html">Getting Started</a>에 들어가면 사용방법이 빌드툴을 이용한 방법과 간단히 다운받는 방법이 있는데 
본 글에서는 후자를 채택해 세팅을 진행하도록 하겠다.</p>
<p><strong>다운받아 이용하는 법</strong></p>
<h3 id="1단계-파일-다운받기">1단계: 파일 다운받기</h3>
<p><a href="https://github.com/Semantic-Org/Semantic-UI-CSS/archive/master.zip">Download Zip</a> 클릭후 다운받자.</p>
<h3 id="2단계-프로젝트-생성">2단계: 프로젝트 생성</h3>
<p>프로젝트 생성 후 다운받은 파일을 압축해제 후 폴더 명을Semantic-UI-CSS-master -&gt;  semantic으로 변경하자.</p>
<pre><code>mkdir semantic-UI_study &amp;&amp; cd $_</code></pre><h3 id="3단계-html안에-삽입">3단계: HTML안에 삽입</h3>
<p>다음 실습 파일때 부터 HTML파일 head안에 아래 코드를 삽입해 주면 된다.
아래 코드를 보면 상하단에 .css와 .js로 끝나는 시맨틱 파일들을 불러오고 중간에 jquery CDN을 불러오는 것은 시맨틱 UI가 jquery를 사용해서 만들어 졌기 때문이다. semantic.min.css와 같이 min이 붙어 있는 것은 압축되어있는 것이고 실제 서비르에는 이걸 사용하고 우리는 개발만 할것이기 때문에 min을 없앤 파일을 사용한다.</p>
<pre><code>&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;semantic/semantic.css&quot;&gt;
&lt;script
  src=&quot;https://code.jquery.com/jquery-3.1.1.min.js&quot;
  integrity=&quot;sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=&quot;
  crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;semantic/semantic.js&quot;&gt;&lt;/script&gt;</code></pre><h3 id="4단계-잘-작동하는지-확인">4단계: 잘 작동하는지 확인</h3>
<p><code>index.html</code>파일을 생성하고 잘 작동하는지 확인하자.
크롬에서 html파일을 열고 크롬개발자 도구를 켜 네트워크탭에 css, js파일들이 정상적으로 불러와지는지 확인하자.
index.html</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;link rel=&quot;stylesheet&quot;
        type=&quot;text/csshref=&quot;semantic/semantic.css&quot;&gt;
        &lt;script
          src=&quot;https://code.jquery.com/jquery-3.1.1.min.js&quot;
          integrity=&quot;sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=&quot;
          crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
        &lt;script src=&quot;semantic/semantic.js&quot;&gt;&lt;/script&gt;
    &lt;/head&gt;
&lt;/html&gt;</code></pre><p>아래와 같이 불러와졌다면 성공이다.
<img src="https://images.velog.io/images/_uchanlee/post/9f8373f3-3e69-4e74-a9a1-ea9c11bda169/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-26%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.15.15.png" alt=""></p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="사용법">사용법</h2>
<p><a href="https://semantic-ui.com/">공식 사이트</a>에 들어가서 메뉴를 보면 정말 다양한 것들이 나온다. 여기서 자신이 필요한 엘리먼트 등을 찾아 사용하면 되는것이다. 나는 이것들중 재밌어 보이는 것들 위주로 실습해 보겠다.
아래 사진과 같이 코드 보기를 누르면 예시가 나오는데 복사해 적용하면 된다.
<img src="https://images.velog.io/images/_uchanlee/post/a1982913-fe9b-4515-a0e5-fec3afd1a65f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-26%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.42.31.png" alt=""></p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<blockquote>
<h3 id="form">Form</h3>
</blockquote>
<p><code>form.html</code>파일을 생성하고 아래 코드를 작성하자.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;semantic/semantic.css&quot;&gt;
        &lt;script
          src=&quot;https://code.jquery.com/jquery-3.1.1.min.js&quot;
          integrity=&quot;sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=&quot;
          crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
        &lt;script src=&quot;semantic/semantic.js&quot;&gt;&lt;/script&gt;
        &lt;style&gt;
            body {
                padding: 1rem;
            }
        &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;Form&lt;/h1&gt;
        &lt;form class=&quot;ui form&quot;&gt;
            &lt;div class=&quot;field&quot;&gt;
              &lt;label&gt;First Name&lt;/label&gt;
              &lt;input type=&quot;text&quot; name=&quot;first-name&quot; placeholder=&quot;First Name&quot;&gt;
            &lt;/div&gt;
            &lt;div class=&quot;field&quot;&gt;
              &lt;label&gt;Last Name&lt;/label&gt;
              &lt;input type=&quot;text&quot; name=&quot;last-name&quot; placeholder=&quot;Last Name&quot;&gt;
            &lt;/div&gt;
            &lt;div class=&quot;field&quot;&gt;
              &lt;div class=&quot;ui checkbox&quot;&gt;
                &lt;input type=&quot;checkbox&quot; tabindex=&quot;0&quot; class=&quot;hidden&quot;&gt;
                &lt;label&gt;I agree to the Terms and Conditions&lt;/label&gt;
              &lt;/div&gt;
            &lt;/div&gt;
            &lt;button class=&quot;ui button&quot; type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
          &lt;/form&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre><p>사용할때 ui form 안에 field들로 이루어진걸 확인할수 있다.
form태그에 클래스가 ui와 form이 있는데 나는 왜 두개나 사용했나 궁금해서 클래스 이름을 ui만 남기고, 또 form만 남겨서 결과를 확인했는데 엉망으로 나왔다. 정상적으로 보이기 위해서는 클래스에 ui form을 지정해줘야 하는걸 알수 있다. 지금 이 html파일만 보면 checkBox가 작동하지 않는다. 이를 해결하기 위해선 아래와 같이 자바스크립트 코드를 추가해줘야 한다. 이와 같은 사용법은 페이지 하단쯤에 사진과 같이 있다.
<img src="https://images.velog.io/images/_uchanlee/post/2a630859-deab-421d-b829-7b2e62cf4aea/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-26%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.52.26.png" alt="">
<code>form.html body태그 끝나기 전에 삽입</code></p>
<pre><code>&lt;script&gt;
    $(&#39;.ui.checkbox&#39;).checkbox();
    // document.querySelector(&#39;.ui.checkbox&#39;).checkbox(); 궁금해서 해봤는데 작동하지 않는다. 이로써 semantic UI는 jquery에 의존하는 프레임워크로 예측된다.
&lt;/script&gt;</code></pre><h4 id="결과-사진이다">결과 사진이다.</h4>
<p><img src="https://images.velog.io/images/_uchanlee/post/f31d8de9-afd4-4c43-b49c-b0e5d345ec3e/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-26%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.56.59.png" alt=""></p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<blockquote>
<h3 id="dropdown---multiple-search-selection">DropDown - Multiple Search Selection</h3>
</blockquote>
<p>진짜 너무 신기하고 재밌는게 많은데 다 소개하기엔 너무 루즈해지므로 마지막으로 이것만 소개하고 글을 마치겠다.
<code>dropDown.html</code>파일을 생성하고 다음과 같이 작성하자.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;semantic/semantic.css&quot;&gt;
        &lt;script
          src=&quot;https://code.jquery.com/jquery-3.1.1.min.js&quot;
          integrity=&quot;sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=&quot;
          crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;
        &lt;script src=&quot;semantic/semantic.js&quot;&gt;&lt;/script&gt;
        &lt;style&gt;
            body {
                padding: 1rem;
            }
        &lt;/style&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;dropDown&lt;/h1&gt;
        &lt;select class=&quot;ui fluid search dropdown&quot; multiple=&quot;&quot;&gt;
            &lt;option value=&quot;&quot;&gt;State&lt;/option&gt;
            &lt;option value=&quot;AL&quot;&gt;Alabama&lt;/option&gt;
            &lt;option value=&quot;AK&quot;&gt;Alaska&lt;/option&gt;
            &lt;option value=&quot;AZ&quot;&gt;Arizona&lt;/option&gt;
            &lt;option value=&quot;AR&quot;&gt;Arkansas&lt;/option&gt;
            &lt;option value=&quot;CA&quot;&gt;California&lt;/option&gt;
            &lt;option value=&quot;CO&quot;&gt;Colorado&lt;/option&gt;
            &lt;option value=&quot;CT&quot;&gt;Connecticut&lt;/option&gt;
            &lt;option value=&quot;DE&quot;&gt;Delaware&lt;/option&gt;
            &lt;option value=&quot;DC&quot;&gt;District Of Columbia&lt;/option&gt;
            &lt;option value=&quot;FL&quot;&gt;Florida&lt;/option&gt;
            &lt;option value=&quot;GA&quot;&gt;Georgia&lt;/option&gt;
            &lt;option value=&quot;HI&quot;&gt;Hawaii&lt;/option&gt;
            &lt;option value=&quot;ID&quot;&gt;Idaho&lt;/option&gt;
            &lt;option value=&quot;IL&quot;&gt;Illinois&lt;/option&gt;
            &lt;option value=&quot;IN&quot;&gt;Indiana&lt;/option&gt;
            &lt;option value=&quot;IA&quot;&gt;Iowa&lt;/option&gt;
            &lt;option value=&quot;KS&quot;&gt;Kansas&lt;/option&gt;
            &lt;option value=&quot;KY&quot;&gt;Kentucky&lt;/option&gt;
            &lt;option value=&quot;LA&quot;&gt;Louisiana&lt;/option&gt;
            &lt;option value=&quot;ME&quot;&gt;Maine&lt;/option&gt;
            &lt;option value=&quot;MD&quot;&gt;Maryland&lt;/option&gt;
            &lt;option value=&quot;MA&quot;&gt;Massachusetts&lt;/option&gt;
            &lt;option value=&quot;MI&quot;&gt;Michigan&lt;/option&gt;
            &lt;option value=&quot;MN&quot;&gt;Minnesota&lt;/option&gt;
            &lt;option value=&quot;MS&quot;&gt;Mississippi&lt;/option&gt;
            &lt;option value=&quot;MO&quot;&gt;Missouri&lt;/option&gt;
            &lt;option value=&quot;MT&quot;&gt;Montana&lt;/option&gt;
            &lt;option value=&quot;NE&quot;&gt;Nebraska&lt;/option&gt;
            &lt;option value=&quot;NV&quot;&gt;Nevada&lt;/option&gt;
            &lt;option value=&quot;NH&quot;&gt;New Hampshire&lt;/option&gt;
            &lt;option value=&quot;NJ&quot;&gt;New Jersey&lt;/option&gt;
            &lt;option value=&quot;NM&quot;&gt;New Mexico&lt;/option&gt;
            &lt;option value=&quot;NY&quot;&gt;New York&lt;/option&gt;
            &lt;option value=&quot;NC&quot;&gt;North Carolina&lt;/option&gt;
            &lt;option value=&quot;ND&quot;&gt;North Dakota&lt;/option&gt;
            &lt;option value=&quot;OH&quot;&gt;Ohio&lt;/option&gt;
            &lt;option value=&quot;OK&quot;&gt;Oklahoma&lt;/option&gt;
            &lt;option value=&quot;OR&quot;&gt;Oregon&lt;/option&gt;
            &lt;option value=&quot;PA&quot;&gt;Pennsylvania&lt;/option&gt;
            &lt;option value=&quot;RI&quot;&gt;Rhode Island&lt;/option&gt;
            &lt;option value=&quot;SC&quot;&gt;South Carolina&lt;/option&gt;
            &lt;option value=&quot;SD&quot;&gt;South Dakota&lt;/option&gt;
            &lt;option value=&quot;TN&quot;&gt;Tennessee&lt;/option&gt;
            &lt;option value=&quot;TX&quot;&gt;Texas&lt;/option&gt;
            &lt;option value=&quot;UT&quot;&gt;Utah&lt;/option&gt;
            &lt;option value=&quot;VT&quot;&gt;Vermont&lt;/option&gt;
            &lt;option value=&quot;VA&quot;&gt;Virginia&lt;/option&gt;
            &lt;option value=&quot;WA&quot;&gt;Washington&lt;/option&gt;
            &lt;option value=&quot;WV&quot;&gt;West Virginia&lt;/option&gt;
            &lt;option value=&quot;WI&quot;&gt;Wisconsin&lt;/option&gt;
            &lt;option value=&quot;WY&quot;&gt;Wyoming&lt;/option&gt;
        &lt;/select&gt;
        &lt;script&gt;
          $(&#39;.ui.dropdown&#39;).dropdown(&#39;hidden&#39;);
        &lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre><p>하단에 있는 javascript문을 넣어주지 않으면 아래 사진처럼 UI가 이상해진다. 나는 그래서 처음에 코드가 잘못된줄 알고 해맸다. 결국엔 이 방법을 알아서 적용했다.
<img src="https://images.velog.io/images/_uchanlee/post/20f6944d-cb4a-494e-bd8e-89580261641e/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-26%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.19.46.png" alt=""></p>
<h4 id="결과-사진이다-1">결과 사진이다.</h4>
<p><img src="https://images.velog.io/images/_uchanlee/post/6024dca6-fa9b-4985-9322-3c265c8367be/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202020-04-26%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.19.01.png" alt="">
정상적으로 드롭다운과 검색, 선택, 삭제 모두 가능한걸 볼 수 있다.</p>
<p><strong><a href="#%EB%AA%A9%EC%B0%A8">⬆ 위로가기</a></strong></p>
<h2 id="느낀점-및-나의-생각">느낀점 및 나의 생각</h2>
<p>나는 이번 계기를 통해 처음 css 프레임워크를 사용해 봤다. 예전에 부트스트랩을 공부할까 고민도 했지만 그땐 어려워 보여서 포기했다. 이번에 Semantic UI를 사용하고 정리해 보았는데 정말 간편한 것 같다. 많이 쓰이는 사이드바나 드롭다운에 이렇게 쉽게 구현이 가능하다니 놀라웠다. 또, 사용방법이 자세히 또 쉽게 설명이 되어있어 사용자들이 배우기에 쉬울것 같다. 공식 문서를 읽다보니 React에도 이걸 쓸수 있는것 같다. 오늘 정리한 방식과는 다를것 같긴 하지만 나중에 도전해 볼것이다.</p>
]]></description>
        </item>
    </channel>
</rss>