<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>choi-ju12g</title>
        <link>https://velog.io/</link>
        <description>누구나 할 수 있지만 아무나 못하는 일을 하자</description>
        <lastBuildDate>Sat, 12 Nov 2022 04:56:02 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. choi-ju12g. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/choi-ju12g" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[남도학숙 식단 어플] 3. 크롤링 방식 변경(puppeteer 사용 X -> api 요청 방식)]]></title>
            <link>https://velog.io/@choi-ju12g/%EB%82%A8%EB%8F%84%ED%95%99%EC%88%99-%EC%8B%9D%EB%8B%A8-%EC%96%B4%ED%94%8C-3.-%ED%81%AC%EB%A1%A4%EB%A7%81-%EB%B0%A9%EC%8B%9D-%EB%B3%80%EA%B2%BDpuppeteer-%EC%82%AC%EC%9A%A9-X-api-%EC%9A%94%EC%B2%AD-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@choi-ju12g/%EB%82%A8%EB%8F%84%ED%95%99%EC%88%99-%EC%8B%9D%EB%8B%A8-%EC%96%B4%ED%94%8C-3.-%ED%81%AC%EB%A1%A4%EB%A7%81-%EB%B0%A9%EC%8B%9D-%EB%B3%80%EA%B2%BDpuppeteer-%EC%82%AC%EC%9A%A9-X-api-%EC%9A%94%EC%B2%AD-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Sat, 12 Nov 2022 04:56:02 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-red1-문제상황span"><span style="color: red">1. 문제상황</span></h2>
<p><a href="https://velog.io/@choi-ju12g/%EB%82%A8%EB%8F%84%ED%95%99%EC%88%99-%EC%8B%9D%EB%8B%A8-%EC%96%B4%ED%94%8C-2.-Node.js%EB%A5%BC-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%81%AC%EB%A1%A4%EB%A7%81-%ED%95%98%EA%B8%B0">지난글</a>에서 나는 cheerio와 puppeteer를 이용해서 식단표를 크롤링하는데 성공했다 
<img src="https://velog.velcdn.com/images/choi-ju12g/post/4dfd0add-ae24-49ad-9302-6c8e8f70f090/image.png" alt=""></p>
<p>그런데, 마음에 들지 않는 부분이 있었다. 그것은 바로 <span style="color: red">속도</span>였다. 
저 상태는 단순히 값을 저장만하고 콘솔에 찍는 작업만 했는데, 3초가량 시간이 소요됐다.
그래서 직접 chromium을 열어서 크롤링에 도움을 주는 puppeteer를 사용하지 않고 하는 방식으로 변경해보려고 한다. </p>
<hr>
<h2 id="span-stylecolor-skyblue2-해결-방안span"><span style="color: skyblue">2. 해결 방안</span></h2>
<p>나는 Postman이라는 개발한 API를 테스트하고, 테스트 결과를 공유하여 API 개발의 생산성을 높여주는 플랫폼을 이용하여 어떤 api 요청을 보내면 식단표가 포함된 데이터를 받아올 수 있는지 체크하고 그것을 코드로 구현하여 속도를 개선해보고자 한다. 사실 이게 더 빠를 것이란 보장은 없지만, 적어도 내 생각에 POST요청 1번, GET요청 1번정도 가 이루어지는데 이렇게 오래 걸리진 않을 것 이란 판단이었다. </p>
<h4 id="1-남도학숙-페이지로-이동-하여-로그인-할때-어떤-요청이-이루어지는지-파악">1) 남도학숙 페이지로 이동 하여 로그인 할때 어떤 요청이 이루어지는지 파악</h4>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/2552f5b7-f9f5-4531-ae3e-da0faa50f768/image.gif" alt=""></p>
<p>로그인 버튼을 눌렀을 때 이루어진 작업들을 크롬 개발자도구의 Network 탭을 통해 확인할 수 있었다. 
<img src="https://velog.velcdn.com/images/choi-ju12g/post/e26879be-73c3-48e6-8870-bd4181a1e8ef/image.png" alt=""></p>
<p>-&gt; 여기서 1가지 사실을 알았다. 로그인을 할 때 <code>POST 요청</code>을 보내며 request URL은 <code>http://portal.ndhs.or.kr/login</code> 으로 보내면 된다. 그렇게 나는 무작정 Postman으로 post 요청만 보냈는데, 아무리 해도 로그인 페이지의 html만을 받아올 뿐이었다. 
<img src="https://velog.velcdn.com/images/choi-ju12g/post/a5c41be7-2bfa-4a24-bc1d-b7b17f595487/image.png" alt=""></p>
<h4 id="2-post요청엔-requestbody영역에-데이터를-전달해야-하잖아">2) post요청엔 requestBody영역에 데이터를 전달해야 하잖아?</h4>
<p>그렇다. 나는 로그인이라는 것을 할 거 면서 가장 기본적인 아이디 비밀번호 정보를 보내지도 않고 로그인이 되지 않는다고 헤매고 있었다. 
<img src="https://velog.velcdn.com/images/choi-ju12g/post/ec863701-a13a-426b-b195-d05e85d115f9/image.png" alt="">
다시 개발자 도구에서 Headers 옆에 Payload 창을 보면 보내는 데이터 값들이 나타난다. 로그인 창에서 입력한 아이디는 userId의 value로, 비밀번호는 password로 들어갔다. 
<img src="https://velog.velcdn.com/images/choi-ju12g/post/5de3d2e9-e3ce-4604-bf72-d864a4799ada/image.png" alt="">
Postman의 Body 부분에 데이터를 그대로 넣어주고 다시 요청을 보내보니 이번엔 로그인 이후의 페이지가 정상적으로 나타났다. </p>
<h4 id="3-식단표-탭으로-들어갈때-보내는-요청-파악">3) 식단표 탭으로 들어갈때 보내는 요청 파악</h4>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/3363e907-d333-4906-842e-ed74eb43ac07/image.gif" alt="">
이제 로그인 이후 페이지에서 보낼 요청을 보면, <code>GET 요청</code>을 통해 html을 받아온다. 나는 이제 거기서 식단표 영역을 적절히 파싱해서 화면에 보여주기만 하면 내가 계획했던 식단표 보여주는 어플 버전 1 완성이다. </p>
<p>Postman으로 확인했을 때, 역시 식단표가 잘 나왔다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/fb48c5d2-00b0-4114-a589-094733aa944a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Photorage] 4-1. React + Vite + Storybook VS Code 절대경로 설정]]></title>
            <link>https://velog.io/@choi-ju12g/Photorage-4-2.-React-Vite-Storybook-VS-Code-%EC%A0%88%EB%8C%80%EA%B2%BD%EB%A1%9C-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@choi-ju12g/Photorage-4-2.-React-Vite-Storybook-VS-Code-%EC%A0%88%EB%8C%80%EA%B2%BD%EB%A1%9C-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Fri, 29 Jul 2022 05:25:22 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolorpurple-1-절대-경로와-상대-경로"><span style="color:purple"> 1. 절대 경로와 상대 경로</h2>
<p>  VS Code를 이용하면 각 파일을 import 해줄 때 우리는 종종 아래와 같은 형식을 사용한다.
  <code>ex) import Button from &#39;../../../src/components/Button&#39;</code></p>
<p>  이는 상대경로로 현재 import 하는 파일의 디렉토리 기준으로 원하는 파일의 경로를 찾아가게 된다.</p>
<p>  상대 경로를 사용하게 되면 절대경로를 사용할때와는 달리 상위 디렉토리의 이름이 바뀌는 경우 등에 있어 문제없이 작동한다. 하지만 이런 상대경로를 계속해서 이용하는 것은 현재 프로젝트 구조에서 많은 불편함을 유발한다. 그래서 나는 import를 자주할 파일 즉, 컴포넌트 들의 특정 디렉토리를 절대경로로 지정하여 쉽게 import 할 수 있도록 설정하려고 한다.</p>
<p>  <code>ex) import Button from &#39;@components/Button&#39;</code></p>
<h2 id="span-stylecolorskyblue-2절대-경로-설정"><span style="color:skyblue"> 2.절대 경로 설정</h2>
<h3 id="span-stylecolorskyblue-1-tsconfigjson-수정"><span style="color:skyblue"> 1) tsconfig.json 수정</h3>
<p>우선 프로젝트 루트 디렉토리에 있을 tsconfig.json 파일을 수정해야 한다.
compilerOptions에 baseUrl과 paths 설정을 추가하자(의미는 주석으로 설명)</p>
<pre><code class="language-json">  {
    &quot;compilerOptions&quot;: {
      &quot;baseUrl&quot;: &quot;.&quot;, // 현재 디렉토리를 기준으로 한다.
      &quot;paths&quot;: {
        &quot;@src/*&quot;: [   //@src로 시작하면 아래 줄의 디렉토리를 의미한다.
          &quot;src/*&quot;        //baseUrl을 기준으로 src/ 하위 디렉토리를 @src로 표현
        ],
        &quot;@components/*&quot;: [
          &quot;src/components/*&quot;
        ],
        &quot;@styles/*&quot;: [
          &quot;src/styles/*&quot;
        ]
      }
    },
  }</code></pre>
<p>  아마 JavaScript를 사용하고 있다면 위의 코드만 넣으면 될 것이다. 하지만 TypeScript를 위의 코드로 절대경로를 설정하기 위해서는 <code>@types/node</code> 라는 패키지가 필요하다.
  <code>npm i -g @types/node</code>를 통해 추가하도록 하자.
  <a href="https://www.npmjs.com/package/@types/node">@types/node 공식 페이지</a>에서 확인하도록 하자.</p>
<h3 id="span-stylecolorskyblue-2-viteconfigts-수정"><span style="color:skyblue"> 2) vite.config.ts 수정</h3>
<p>  나는 빌드 도구로 Webpack이 아닌 Vite를 사용했기 때문에 vite.config.ts 파일을 수정해줘야 한다. 만약 본인이 React 프로젝트 시작을 CRA로 했다면 Webpack.config 파일을 수정해야 할 것 이다.</p>
<pre><code class="language-typescript">import { defineConfig } from &#39;vite&#39;
import react from &#39;@vitejs/plugin-react&#39;
import { resolve } from &#39;path&#39;

// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: [
      { find: &#39;@src&#39;, replacement: resolve(__dirname, &#39;src&#39;)},
      { find: &#39;@components&#39;, replacement: resolve(__dirname, &#39;src/components&#39;)},
      { find: &#39;@styles&#39;, replacement: resolve(__dirname, &#39;src/styles&#39;)}
    ]
  }
})
</code></pre>
<h3 id="span-stylecolorskyblue-3-storybookmainjs-수정"><span style="color:skyblue"> 3) .storybook/main.js 수정</h3>
<p>마찬가지로 나는 Vite를 썻고, 또 디자인 도구로 Storybook을 사용했기 때문에 .storybook/main.js 파일도 수정해야한다.</p>
<pre><code class="language-javascript">  const path = require(&#39;path&#39;);

module.exports = {

    viteFinal: async (config, { configType }) =&gt; {
      config.resolve.alias = {
        ...config.resolve.alias,
        &#39;@src&#39;: path.resolve(__dirname, &quot;../src&quot;),
        &#39;@components&#39;: path.resolve(__dirname, &#39;../src/components&#39;),
        &#39;@styles&#39;: path.resolve(__dirname, &#39;../src/styles&#39;)
      };

      return config;
    }
}
</code></pre>
<h3 id="span-stylecolorskyblue-4-jestconfigts-수정"><span style="color:skyblue"> 4) jest.config.ts 수정</h3>
<p>  위와 같이 설정을 한 뒤 일반 코드에 작성한 것처럼 Jest에도 절대경로를 사용할 수 있다고 생각을 하겠지만 테스트를 할 경우 에러가 발생됩니다.</p>
<pre><code class="language-typescript">  module.exports = {
    moduleNameMapper: {
        &#39;^@/(.*)$&#39;: &#39;&lt;rootDir&gt;/src/$1&#39; // @/로 시작하는 경로를 src/ 경로로 설정
    }
};</code></pre>
<p>  [참고 링크]</p>
<ul>
<li><p><a href="https://carpediem9911.tistory.com/44">https://carpediem9911.tistory.com/44</a> </p>
</li>
<li><p><a href="https://velog.io/@dev2820/%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-webpack%EC%97%90%EC%84%9C-vite%EB%A1%9C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98%ED%95%98%EA%B8%B0">https://velog.io/@dev2820/%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-webpack%EC%97%90%EC%84%9C-vite%EB%A1%9C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98%ED%95%98%EA%B8%B0</a></p>
<ul>
<li><a href="https://jforj.tistory.com/253">https://jforj.tistory.com/253</a></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 당신의 React 코드를 2분만에 리팩토링 하는 매우 쉬운 방법]]></title>
            <link>https://velog.io/@choi-ju12g/React-%EB%8B%B9%EC%8B%A0%EC%9D%98-React-%EC%BD%94%EB%93%9C%EB%A5%BC-2%EB%B6%84%EB%A7%8C%EC%97%90-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%ED%95%98%EB%8A%94-%EB%A7%A4%EC%9A%B0-%EC%89%AC%EC%9A%B4-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@choi-ju12g/React-%EB%8B%B9%EC%8B%A0%EC%9D%98-React-%EC%BD%94%EB%93%9C%EB%A5%BC-2%EB%B6%84%EB%A7%8C%EC%97%90-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%ED%95%98%EB%8A%94-%EB%A7%A4%EC%9A%B0-%EC%89%AC%EC%9A%B4-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 08 Jun 2022 03:51:32 GMT</pubDate>
            <description><![CDATA[<p>원문 링크 - <a href="https://medium.com/@chrlschn/is-this-the-easiest-2-min-refactoring-to-improve-your-react-code-5a5d9e0547f0">https://medium.com/@chrlschn/is-this-the-easiest-2-min-refactoring-to-improve-your-react-code-5a5d9e0547f0</a>
이 글은 위의 원문을 번역한 글입니다. </p>
<hr>
<p>혹시 리액트 코드를 작성하면서, 삼항 연산자를 엄청나게 사용되는 코드를 작성한 적이 있나. 삼항 연산자는 분명 유용 하지만 남발하게 되면 가독성이 매우 떨어지게 된다. 나의 경우로 말을 하면 사실 삼항 연산자가 2개 이상 중첩되면 바로 의미 이해가 어렵다. 하지만 분명 의미적으로 삼항 연산자가 여러개여야 하는 경우, 즉 분기가 조건의 조건의 조건 등을 타고 가야하는 경우는 무조건 있기 때문에 그러한 상황에서 어떻게 코드를 작성하면 유지보수에 용이한 코드를 작성할 수 있는지 설명하겠다. 
<br><br></p>
<p><strong>예제 코드</strong>
<img src="https://velog.velcdn.com/images/choi-ju12g/post/fd58900a-196b-4068-a641-5fba1c9bc6b1/image.png" alt=""></p>
<p>우선 위에 코드가 어떤 플로우를 가지고 있는지 생각해보자.</p>
<blockquote>
<ol>
<li>signUpState가 default 이면 null을 아니면 A를 반환한다.</li>
<li>A는 signUpState가 warning이면 <WarningMessage>를 아니면 B를 반환한다.</li>
<li>B는 signUpState가 info이면 <InfoMessage>를 아니면 C를 반환한다.</li>
<li>C는 signUpState가 success이면 D를 아니면 <ErrorMessage>를 반환한다.</li>
<li>D는 user.Internal이 참이면 <InternalUserForm>을 아니면 <ExternalUserForm>을 반환한다.</li>
</ol>
</blockquote>
<p>이걸 좀 풀어서 생각하면 결국, signUpStater를 판단해서 그에 맞는 컴포넌트를 반환하고, 특별하게 signUpState가 success인 경우에 내부폼인지 외부폼인지를 판단하겠다는 뜻이다.</p>
<p>자 이걸 바꿔보자.</p>
<h2 id="1-컴포넌트-안으로-로직을-옮겨라">1. 컴포넌트 안으로 로직을 옮겨라</h2>
<p>이 방식은 간단하다. 삼항 연산자로 판단하던 조건을 컴포넌트 안에서 판단하도록 구현하는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/6508a9b1-2bf7-4356-9356-d290a36c83d5/image.png" alt=""></p>
<p>위의 설명에서 5번 로직인 user.Internal 조건을 가지고 null을 리턴할지, 컴포넌트를 리턴할 지를 컴포넌트 내부에서 결정하도록 구조를 바꾸었다. 놀랍게도 이것이 OOP의 캡슐화 개념이다. 물론 위의 컴포넌트 역시 2개의 컴포넌트로 분리하는 것도 가능하지만 현재 상황에서 그렇게 작업하는 것은 더 지저분하게만 할 뿐이다. 현재 방식이 모듈화에 더 가까운 코드 형태이다.
<br>
이제 전체 코드는 이렇게 바뀌었다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/f768e75e-3306-40ee-939f-971d06c49d1e/image.png" alt=""></p>
<p>이러한 방식의 장점은 컴포넌트의 구성을 볼때 어떤 조건들이 있고 각 컴포넌트에 영향을 미치는지 파악하기 어렵다는 문제를 해결해준다. 그리고 위에 코드는 아직 signUpState 라는 조건에 대한 로직을 옮기진 않았다. 다시한번 코드 구조를 바꿔볼 수 있겠다. 과정은 생략하고 결과 코드를 아래 첨부하겠다.</p>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/638657f6-e681-4852-8865-6a53a49a5737/image.png" alt=""></p>
<p>처음보다는 낫지만, <code>signUpState</code> 를 컴포넌트 안으로 넣지 않는 한 마지막 남아있는 삼항연산자 한 개는 제거할 수 없다. 또 만약 <code>signUpState</code>를 컴포넌트 안으로 넣게 된다면 의존성이 바인딩 된다. 우리는 <InternalUserForm>과 <ExternalUserForm>이 <code>signUpState</code> 상태를 알아야 하는지에 대한 확신이 없는한 의존성을 분리시키는 것이 더 깔끔하다. 그렇다면 어떻게 해야할까</p>
<h2 id="2-로직은-레이아웃-안에-유지시켜라">2. 로직은 레이아웃 안에 유지시켜라</h2>
<p>무슨 말일까? 기껏 컴포넌트 안으로 조건에 대한 로직을 옮겨서 삼항연산자를 제거해놓고서는 다시 레이아웃에 로직을 나두라니? 당연히 이전 상태로 돌리라는 뜻은 아니다. 위에 방식으로 바꾼 구조의 결과를 보면 코드가 어떤 로직을 원하는지 이해하기 어렵진 않다. 하지만 어떤 동작을 하는지 정확하게 파악하기 위해서는 각각의 컴포넌트를 하나하나 확인해야 한다.  signUpState가 어떤 String을 가질때 해당 컴포넌트가 렌더링 되는지 등을 파악하기 위해선 말이다. 그래서 조건을 기준으로 분기하는 로직은 컴포넌트 안으로 넣 돼, 어떤 조건인지는 최종코드에서 볼 수 있도록 바꾸라는 말이다. </p>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/5ba928d2-0c60-47eb-b96a-e0530ac64527/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/ec8476dc-a8eb-4f0f-b8fc-1f2c3d69c2b2/image.png" alt=""></p>
<p>이렇게 코드 구조를 바꾸면 엄청나게 코드를 유지보수하기 쉬워진다.</p>
<ul>
<li>새로운 메세지를 넣기를 원하는가? 몇 뎁스의 삼항연산자의 흐름을 알필요가 없다.</li>
<li>로직을 바꾸기를 원하는가? 전체 코드 흐름에 상관없이 바꾸고 싶은 컴포넌트의 로직을 바꿔도 사이드 이펙트가 발생하지 않는다</li>
<li>논리적으로 코드를 이해하기 어려운가? 단순히 boolean 함수 하나를 반환할 뿐이다. 해당 컴포넌트를 렌더링 할지 말지에 대해서</li>
<li>특정 하나의 컴포넌트가 조건이 더 필요할 때와 같은 경우 우리는 너무도 쉽게 개별적 요소를 수정할 수 있다.</li>
</ul>
<p>이러한 방식을 이용하면 Generic한 컴포넌트를 아래와 같이 구성할 수도 있다.</p>
<pre><code class="language-jsx">&lt;Conditional
    when=&quot;() =&gt; {...}&quot;
&gt;
    &lt;CardContent&gt;...&lt;/CardContent&gt;
&lt;/Conditional&gt;</code></pre>
<p>이렇게 하면 코드가 복사되는 양을 줄일 수 있다는 장점은 있지만, 레이아웃에 복잡도는 조금 더 올라가긴 한다. 상황에 맞게 잘 사용하자.</p>
<p>또 만약 <code>useEffect</code> 를 props로 넘겨야하는 상황에는 지금까지 방식은 작동하지 않는다. 그런 상황엔 아래 방식을 활용하자.</p>
<pre><code class="language-jsx">{condition &amp;&amp; &lt;CardContent /&gt;}</code></pre>
<hr>
<p>여담으로 위의 코드 방식이 뭔가 익숙하다고 느끼는 사람이 있을 수 있다. 아마 Vue.js를 공부하고 사용해본 사람이면 비슷하다고 느낄 수 있다. 아래는 Vue.js 방식을 보여주겠다.</p>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/a7fc6a75-172c-4d51-9f33-2c93d1a494bd/image.png" alt=""></p>
<p>Vue.js는 기본적으로 무조건 이렇게 하도록 되어있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Photorage] 3.  React + Spring 을 AWS EC2 Nginx로 배포하기]]></title>
            <link>https://velog.io/@choi-ju12g/Photorage-3.-React-Spring-%EC%9D%84-AWS-EC2-Nginx%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B01</link>
            <guid>https://velog.io/@choi-ju12g/Photorage-3.-React-Spring-%EC%9D%84-AWS-EC2-Nginx%EB%A1%9C-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B01</guid>
            <pubDate>Mon, 30 May 2022 09:00:10 GMT</pubDate>
            <description><![CDATA[<p>지난 포스팅에서 프로젝트를 Vite로 만들고 스토리북까지 추가해봤다. 이번에는 리액트로 만들 웹을 AWS의 EC2 제품을 이용하여 배포하는 인프라를 구축 해보겠다.</p>
<p>글 제목이 React + Spring이라 헷갈릴 수도 있을 것 같다. 정확히 우리 프로젝트는 MSA로 설계를 진행하고 있고, 나는 Photorage-web이라는 이름으로 프론트 서버를 현재 띄울 것이다. 그럼 굳이 제목에 Spring을 왜 붙여놨냐하면, 내가 AWS에 배포해보기 위해 관련 글을 찾아볼 때 프론트와 백을 분리한 프로젝트인지 아닌지 알기 어려웠던 기억이 있어서 우리 프로젝트는 프론트와 백이 분리가 되어 있다는 것을 명확히 하기 위해 이렇게 글 제목을 정했다.</p>
<p>그 전에 AWS가 뭔지, EC2가 뭔지 전혀 모르겠다면 [AWS 간단 정리]를 보고와도 좋을 것 같다.</p>
<hr>
<blockquote>
<p>해당 글은 <a href="https://velog.io/@jjhstoday/AWS-EC2%EC%97%90-React-Node.js-%EC%95%B1-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-1-AWS-EC2-instance-%EC%83%9D%EC%84%B1">이 글</a> 을 보고 진행한 EC2 생성 과정에서 내가 느꼈던 점에 대해 기록한 글이다.</p>
</blockquote>
<h2 id="span-stylecolorpurple-1-왜-ec2를-쓰나"><span style="color:purple"> 1. 왜 EC2를 쓰나?</h2>
<p>  사실 구글에 React AWS 배포 라고 검색을 해보면, EC2를 사용하는 예제보다는 S3 혹은 Amplify를 이용한 서버리스 프로젝트에 소개가 대다수이다. 하지만 나는 현재 이 프로젝트에 백엔드 개발 팀이 분명하게 있고, 또 주제가 사진을 다루는 만큼 단순히 S3를 이용한 웹서버를 띄우는 것에는 무리가 있다고 판단했다. 또 추후 기회가 된다면 SSR로 바꾸는 것도 고려하고 있기 때문에 직접 EC2에 Nginx를 설치하여 웹 서버를 띄워보려고 한다. </p>
<h2 id="span-stylecolorskyblue-2-ec2-인스턴스-생성"><span style="color:skyblue"> 2. EC2 인스턴스 생성</h2>
<h3 id="span-stylecolorskyblue-1-aws-ec2-서비스-페이지-접속"><span style="color:skyblue"> 1) AWS EC2 서비스 페이지 접속</h3>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/fa88cfa7-e0d9-4927-afeb-16a53774f2e3/image.png" alt="">
  AWS에 가입을 하고 루트 사용자로 로그인을 하게 되면 다음과 같은 페이지가 보일텐데, AWS 서비스에서 EC2가 보인다면 선택을 하고, 안보인다면 모든 서비스를 펼쳐서 EC2에 접속을 하자.</p>
<h3 id="span-stylecolorskyblue-2-인스턴스-생성"><span style="color:skyblue"> 2) 인스턴스 생성</h3>
<p>  <img src="https://velog.velcdn.com/images/choi-ju12g/post/dea0a03f-6380-4bdf-b397-86e0cd44af71/image.png" alt=""></p>
<ol>
<li><p>왼쪽 메뉴의 인스턴스를 누른다(현재 위에 사진은 인스턴스를 눌렀을 때 화면이다.)</p>
<ol start="2">
<li>우측 상단에 인스턴스 시작 버튼을 누른다.</li>
</ol>
<h3 id="span-stylecolorskyblue-3-ami-선택1단계"><span style="color:skyblue"> 3) AMI 선택(1단계)</h3>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/6f968b86-1268-4c30-a9b3-06edca3c4424/image.png" alt=""></p>
</li>
</ol>
<p>해당 사진 우측 빨간 네모안에 보면 총 7단계에 거쳐서 인스턴스를 생성할 수 있게 되어있다. 아마 똑같이 인스턴스 생성을 통해 들어갔는데 위에 사진처럼 뷰가 안보이는 분들도 있을 것이다. 현재 AWS가 웹페이지의 뷰를 새로 만들고 있는 것 같더라. 그럴때는 우측 상단에 기존 탭으로 보기 등의 버튼이 뜰텐데 그것을 누르면 현재와 같은 페이지를 볼 수 있다.</p>
<p>  나는 AWS의 체험판 느낌인 프리티어로 생성할 것이기에 왼쪽에 프리티어만을 체크해서 고를 수 있는 운영체제를 살펴보았고, Amazon Linux 2 AMI 64비트를 선택하게 되었다(사실 이쪽 분야는 아는게 너무 없어서 AWS에서 제공하는 설명 보고 최적화 되어있다길래 선택했다).</p>
<h3 id="span-stylecolorskyblue-4-인스턴스-유형-선택2단계"><span style="color:skyblue"> 4) 인스턴스 유형 선택(2단계)</h3>
<p>  <img src="https://velog.velcdn.com/images/choi-ju12g/post/0fdd8db4-d2b7-4dd7-8ef7-6d5c0ff4bfe1/image.png" alt=""></p>
<p>  2단계 인스턴스 선택에서 역시 기본으로 선택되어 있는 t2.micro(프리티어)로 선택하였다. 
  이후 3~5단계에서 세부 설정을 할 수 있지만 참고한 포스팅 글들에서 대부분 넘어가도 괜찮다고 해서 추후 사용하다 뭔가 설정을 추가로 하는일이 있으면 그때 수정하든 새로운 포스팅을 하든 하겠다.</p>
<h3 id="span-stylecolorskyblue-5-보안-그룹-구성6단계"><span style="color:skyblue"> 5) 보안 그룹 구성(6단계)</h3>
<p>  <img src="https://velog.velcdn.com/images/choi-ju12g/post/16ec64d8-6a90-41ce-a9af-65b85b095f6a/image.png" alt=""></p>
<p> 좌측 하단의 규칙 추가 버튼을 위해 http와 사용자 지정 TCP를 추가해주었다. 
  http는 Nginx로 React를 배포할 것이기에 추가하였고, 사용자 지정 TCP는 추후 node.js 등으로 SSR을 할 것을 대비하여 8000포트를 이용해 추가해주었다. 
  소스 항목을 통해 내 instance(EC2 서버)에 접근할 수 있는 방법을 제한 할 수 있다. 그런데 아직 이런것을 제한했다 어떤 불상사가 생길지 몰라서 나는 Anywhere 에서 접근할 수 있다는 의미인 0.0.0.0/0 으로 두었다(사실 그냥 추가하고 안만지면 저런 상태로 있다)</p>
<h3 id="span-stylecolorskyblue-6-새로운-키-페어-생성"><span style="color:skyblue"> 6) 새로운 키 페어 생성</h3>
<p>  <img src="https://velog.velcdn.com/images/choi-ju12g/post/8a3c8430-4a0b-4db1-b335-65dc640aed7f/image.png" alt=""></p>
<p>  7단계 검토에서 지정해준 것들을 확인하고 나면 키페어를 생성하는 창이 나온다.
  현재 내 사진에는 생성시에 만들어둔 키페어가 있어서 저렇게 나오는데 새 키페어를 생성해서 <code>이름을 작성하고 다운로드 받아둔 뒤</code> 인스턴스를 시작하자.</p>
<ul>
<li>이후 다운받은 프리이빗 키(.pem)을 보안된 위치인 .ssh 하위로 이동 + 프라이빗 키 권한을 설정해주자<pre><code class="language-bash">$ mv ~/Downloads/[키페어 이름].pem ~/.ssh/[키페어 이름].pem
$ chmod 400 ~/.ssh/[키페어 이름].pem</code></pre>
본 프로젝트는 Mac으로 진행했다. 혹시 윈도우에서 하시는분들은 다른 포스팅을 이 부분 이 조금 다를 것 같은데 ec2 window 프라이빗 키 권한설정 등으로 검색해서 진행하자.</li>
</ul>
<h2 id="span-stylecolorpink-3-인스턴스-연결"><span style="color:pink"> 3. 인스턴스 연결</h2>
<p>  <img src="https://velog.velcdn.com/images/choi-ju12g/post/1a0bac2f-c523-4de5-9347-730cad70f66c/image.png" alt="">위 사진의 숫자 순서대로 클릭하여 연결 페이지로 들어간다. 
  그런 다음 설치 되어 있는 내장 터미널이든 따로 사용하는 터미널이 있다면 터미널을 켜주자.</p>
<p>  <img src="https://velog.velcdn.com/images/choi-ju12g/post/51b1773a-6640-427e-a3dc-dd8f55994f5a/image.png" alt=""></p>
<p>  그리고 터미널에 &#39;이 부분 복사&#39; 라고 되어있는 곳을 참고하여 입력하면 되는데, 아마 그대로 입력하면 안되고 IP라던가 현재 인스턴스 이름 등은 자기가 세팅한대로 값을 넣어줘야 한다. 그대로 붙여넣기 하고 왜 안되냐고 투덜거리지 말자(나처럼).
  형식은 아래와 같다.</p>
<pre><code class="language-bash">  $ ssh -i [프라이빗 키(.pem) 경로] [AMI의 사용자 이름]@[인스턴스의 퍼블릭 DNS]</code></pre>
<p>  <img src="https://velog.velcdn.com/images/choi-ju12g/post/2fc45ed6-6e43-4925-ab30-3b6aea37a200/image.png" alt="">
이런 화면이 자신의 터미널에 떴다면 연결에 성공한 것이다. </p>
<h2 id="span-stylecolororange-4-사용할-패키지-설치"><span style="color:orange"> 4. 사용할 패키지 설치</h2>
<p> <a href="https://docs.aws.amazon.com/ko_kr/sdk-for-javascript/v2/developer-guide/setting-up-node-on-ec2-instance.html">AWS 공식문서</a>를 참고하여 설치를 진행했다.</p>
<h3 id="span-stylecolororange1-nvm-설치-후-node-설치"><span style="color:orange">1) nvm 설치 후 node 설치</h3>
<p>  <img src="https://velog.velcdn.com/images/choi-ju12g/post/0df13c4f-63c1-4da9-aad5-52b00da13cc3/image.png" alt=""></p>
<p>  다음과 같은 코드 흐름으로 진행했는데 nvm으로 분명 노드를 설치를 했음에도 마지막 5단계에 현재 노드 버전을 콘솔로 찍어보려고 하니 <code>command node not found</code> 뭐 이런 느낌의 에러로 노드가 없다고 나왔다. 그래서 nvm --list  명령어를 통해 노드 버전을  확인해보니 분명 있었는데 안되더라. </p>
<p>  <strong>이런 문제가 발생하신 분은 아래처럼 해보길 바란다</strong>
  (참고 링크 - <a href="https://tttap.tistory.com/120">https://tttap.tistory.com/120</a>)</p>
<p>  1) <code>nvm install node</code> 대신 <code>nvm install [버전 명(숫자) or lts/이름]</code>으로 설치</p>
<ul>
<li>프로젝트 생성을 할 때, 별 생각없이 Vite로 생성을 하고 나서 생각하니까 인턴 하고 있는 회사에서 사용중인 node 14.17.5 버전으로 생성했다. 그래서 EC2 인스턴스의 노드도 메이저 버전 14를 맞추기로 했고, 그 중 LTS를 설치했다.
ex) <code>$ nvm install lts/fermium</code></li>
<li>lts 이름이 확인하고 싶다면 <code>nvm list</code> 명령어를 통해 확인하자.</li>
</ul>
<p>2) 1번을 하면 노드가 설치 되었을 것이다. 그러먼 <code>nvm use v14(버전명)</code> 또는 <code>nvm use lts/fermium</code> 등과 같은 명령어를 사용하면 nvm이 현재 사용할 노드 버전을 해당 버전으로 바꾼다.</p>
<p>  3) 디폴트 노드 버전을 설정한다.</p>
<ul>
<li><code>nvm alias default 버전명</code>  을 실행하면 된다
ex) <code>$ nvm alias default lts/fermium</code></li>
</ul>
<hr>
<h3 id="span-stylecolororange2-git-설치-및-프로젝트-클론"><span style="color:orange">2) git 설치 및 프로젝트 클론</h3>
<pre><code class="language-bash">  $ sudo yum update // 초기에 package 들을 update 해준다.
$ sudo yum install git // 깃 설치
  $ git clone [url] // 프로젝트 코드 클론 ex) git clone https://github.com/[닉네임]/[레포지토리네임]</code></pre>
<h2 id="span-stylecolorred-5-nginx-설치-및-설정"><span style="color:red"> 5. Nginx 설치 및 설정</h2>
<p>  사실 여기까지도 상당히 낯설고 어려웠다. 조금 만 더 힘내서 해보자.</p>
<h3 id="span-stylecolorred-1-nginx-설치"><span style="color:red"> 1) Nginx 설치</h3>
<pre><code class="language-bash">  $ sudo yum install nginx //Amazon Linux 1 환경
  $ sudo amazon-linux-extras install nginx1.12 //Amazon Linux 2 환경(내 경우)</code></pre>
<p>   나는 Linux2 환경을 선택해서 아래 코드를 이용하여 설치하였다. </p>
<h3 id="span-stylecolorred--2-react-앱-빌드"><span style="color:red">  2) React 앱 빌드</h3>
<p>  지난번에 git clone을 이용하여 프로젝트를 가져왔었다. 해당 프로젝트의 루트 디렉토리로 이동한 후 build를 해주자. 내 글을 그대로 따라 왔다면 해당 프로젝트의 루트 디렉토리는 아래와 같을 것이다.
  <code>$ home/ec2-user/[프로젝트 명]</code> 해당 파일로 이동하는 명령어는 cd 이다
  ex) $ cd home/ec2-user/photorage-web</p>
<p>   이동을 완료했다면, </p>
<pre><code class="language-bash">  $ yarn install //필요한 package 설치(미설치 된게 있다면)
  $ yarn build //프로젝트 사전 build 명령어</code></pre>
<p>  명령어를 통해 build를 하면 되는데, 나는 React를 Vite라는 툴체인으로 프로젝트를 생성하여서 미리 설정되어있는 <code>yarn build</code> 라는 명령어를 통해 손쉽게 빌드 파일을 만들 수 있다(주의할 점, CRA는 빌드된 결과물이 build 라는 이름의 디렉토리에 담기는데, Vite는 dist라는 이름의 디렉토리에 담긴다.)</p>
<h3 id="span-stylecolorred-3-nginx-설정"><span style="color:red"> 3) Nginx 설정</h3>
<p>  여기서부터는 거의 모르는 내용이라 거의 따라했어서 원문의 내용을 그대로 가져오고 내가 겪었던? 첨언할 부분을 추가하겠다.</p>
<blockquote>
<p>3-1. nginx.conf 열기
우선 아래 명령어를 통해 해당 파일을 열면 기본적으로 여러 내용이 이미 설정되어 있는 것을 확인할 수 있다.
<code>$ sudo vi /etc/nginx/nginx.conf</code> <br>
nginx.conf 내에서 React 프로젝트의 build로 바로 이어지게 설정할 수도 있지만, 설정들을 깔끔하게 관리하기 위해서 일반적으로 선호되는 방식은 /etc/nginx 내에 sites-enabled 디렉토리를 생성하여 여기에 각 서비스의 설정을 넣고 nginx.conf가 이들을 확인하도록 하는 것이다.<br>
하나의 인스턴스 내부인데도 ‘각 서비스’라고 표현한 것은, 가상 호스트(서버) 개념을 사용할 수 있기 때문이다. 이를 통해 하나의 컴퓨터, 즉 하나의 IP 내에서 여러 웹을 배포할 수 있다.(Nginx의 장점이라 할 수 있겠다. 🤗)</p>
</blockquote>
<h2 id="그대로-따라하면-etcnginx-디렉토리의-nginxconf-라는-파일이-vi-라는-편집-툴을-이용하여-열리게-될-것이다-vi-사용이-익숙치-않아서-당황할-수-있으나-일단-따라하자">  그대로 따라하면 /etc/nginx/ 디렉토리의 nginx.conf 라는 파일이 vi 라는 편집 툴을 이용하여 열리게 될 것이다. vi 사용이 익숙치 않아서 당황할 수 있으나 일단 따라하자.</h2>
<blockquote>
<p>3-2. nginx.conf 수정
지금은 앞서 말한 대로 sites-enabled 디렉토리에 따로 설정을 만들어줄 것이므로, server 블럭 행들은 모두 주석 처리(행 앞에 #) 하도록 한다. 그리고 server 블럭 바로 위에 아래와 같이 include /etc/nginx/sites-enabled/*.conf;를 추가함으로써 sites-enabled 하위의 설정 파일들을 포함하도록 한다.</p>
</blockquote>
<pre><code>... 
include /etc/nginx/conf.d/*.conf;
(✨ 코드 추가) include /etc/nginx/sites-enabled/*.conf;
# server {
#    listen       80 default_server;
#    listen       [::]:80 default_server;
#    server_name  _;
#    root         /usr/share/nginx/html;
    # Load configuration files for the default server block.
#    include /etc/nginx/default.d/*.conf;
#    location / {
#    }
#    error_page 404 /404.html;
#        location = /40x.html {
#    }
#    error_page 500 502 503 504 /50x.html;
#        location = /50x.html {
#    }
# }
...</code></pre><p>  위에서도 말했듯 vi를 이용하는게 익숙치 않을 수 있다(나도 그랬다). 저 부분을 못찾을 수도 있는데 우선 처음 터미널에 바로 수정이 되지않을 것이다. 그럴 때 <code>키보드 i를 누르면 해당 파일을 수정하겠다</code> 라는 뜻이 된다. 그 이후 키보드 방향키를 이용하여 아래로 내리다 보면 위에 나오는 설명이 나오는데 차분히 include 구문을 추가해주고 주석처리 진행해주자. 
  여기까지 했다면 <code>키보드 esc 누르기 &gt; :wq 입력</code>을 해주자. :wq(저장하고 나가기) 라는 뜻이다.</p>
<hr>
<blockquote>
<p>3-3. sites-available, sites-enabled 설정
일반적인 방식은 sites-available 디렉토리에 필요한 파일들을 작성한 후 이들과 연결되는 symbolic link(symlink)를 sites-enabled에 추가하는 것이다. 때문에 /etc/nginx 내부에 두 디렉토리를 모두 생성해 준다. 그리고 sites-available 내에 원하는 이름으로 설정 파일을 생성해 열도록 한다.</p>
</blockquote>
<pre><code class="language-$">$ sudo mkdir /etc/nginx/sites-enabled
$ sudo vi /etc/nginx/sites-available/#######.conf</code></pre>
<p>지금은 도메인 등록도 되어있지 않고, HTTPS를 위한 준비도 되어있지 않으므로, HTTP에 해당하는 port 80에 대해서 아주 기본 설정만 포함한다.</p>
<pre><code>server {
  listen 80;
  location / {
    root /home/ec2-user/#######/#######/build;(본문 내용 -&gt; 삭제)
      root /home/ec2-user/#######/dist;(필자 코드 -&gt; 적용) 
    index index.html index.htm(본문 내용인데 필요 없어 보여서 삭제함);
    try_files $uri $uri/ /index.html;
  }
}</code></pre><p>location 뒤의 /는 directive라고 하는 부분인데, IP 주소나 도메인의 뒷부분인 URI에 대응된다. /만 쓰면 ‘/’로 시작하는 모든 URI에 해당한다는 것으로 현재 설정은 이 인스턴스의 IP 주소로 port 80을 통해 들어오는 모든 URL을 연결시켜주는 것이다.
root 행에는 아까 Git을 통해 가져온 프로젝트 내부의 build 디렉토리 경로를 입력한다.</p>
<p>  자 우선 위에서 <code>#####.conf =&gt; [자신의 프로젝트 명].conf</code> 로 이해하고 입력하자. 또 <code>/etc/nginx/sites-available/#####.conf</code> 파일에 입력할 때 위에 적어둔대로 자기의 상황에 맞게 입력하자.</p>
<hr>
<blockquote>
<p>3-4. sites-enabled &gt; symlink 생성 및 Nginx 설정 테스트</p>
</blockquote>
<pre><code>  $ sudo ln -s /etc/nginx/sites-available/#######.conf /etc/nginx/sites-enabled/#######.conf
  $ sudo nginx -t</code></pre><pre><code>nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful</code></pre><p>  위와 같은 내용이 출력되면 설정의 문법이 정상적이라는 것이다.</p>
<p>  위와 같이 안뜬다면 지금까지 흐름에서 주석을 달 때, } 를 몇개 빠뜨렸다거나 프로젝트 루트 설정이 잘못되었다거나 그럴 가능성이 높다. 꼼꼼하게 작성해보자. 추가로 아래엔 기본적이지만 도움이 될 리눅스 명령어를 조금만 적어두겠다.</p>
<pre><code class="language-bash">  $ cd ~~ //~~경로로 이동
  $ ls  //현재 경로에 존재하는 모든 파일 및 디렉토리 표시
  $ pwd //현재 경로를 출력
  $ mkdir ~~ //현재 경로에 ~~이름을 가진 디렉토리(폴더) 만들기
  $ touch ~~ //현재 경로에 ~~이름을 가진 파일 만들기
  $ rm ~~ //현재 경로에 ~~이름을 가진 파일 삭제(디렉토리를 삭제하고 싶으면 $ rm -rf ~~)</code></pre>
<hr>
<blockquote>
<p>3-5. Nginx 동작
이제 Nginx를 동작시키고, 웹 브라우저에서 AWS EC2 인스턴스 IP 주소를 입력하면 build한 React 앱으로 연결되는 것을 확인할 수 있다.
(AWS EC2 인스턴스 IP 주소로 바로 연결할 때 https로 들어가지는데, 현재는 http만 설정했기 때문에, http로 수정하면 들어가질 것이다.)</p>
</blockquote>
<pre><code>$ sudo systemctl start nginx</code></pre><p>  만약, 500 Internal Server Error가 발생한다면, build까지의 경로로 접근할 때 거치는 디렉토리들에 대해 외부에서의 실행 권한이 없어서 발생하는 문제일 가능성이 높으므로, 홈 디렉토리인 /home/ec2-user의 권한을 others의 실행 권한을 포함한 711로 설정한다.<br>
<code>$ chmod 711 /home/ec2-user</code></p>
<p>  위에서 이상이 없었다면 정상적으로 본인의 프로젝트의 빌드 파일이 출력될 것이다. 그게 안된다면 프로젝트 빌드 루트를 적었던 <code>etc/nginx/sites-available/#####.conf</code>파일에 오타나 잘못된 내용은 없어는지 살펴보자.</p>
<hr>
<p>  현재 내 빌드 된 웹사이트 - 해당 주소를 통해 누구나 접속 할 수 있지만, 비용절감을 위해 EC2를 사용하지 않을 때는 <code>중지</code> 상태로 두는데 이걸 다시 켜면 IP주소가 바뀌더라. 나중에 고정 도메인을 구해서 그걸 등록해보겠다.
  <img src="https://velog.velcdn.com/images/choi-ju12g/post/8901e76b-0d26-48c3-a645-6d446700c8f3/image.png" alt=""></p>
<p>  [참고 링크]</p>
<ul>
<li><a href="https://velog.io/@tunakim/AWS-EC2-Nginx-%EC%9D%B4%EC%9A%A9%ED%95%B4-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC">https://velog.io/@tunakim/AWS-EC2-Nginx-%EC%9D%B4%EC%9A%A9%ED%95%B4-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC</a></li>
<li><a href="https://developerbee.tistory.com/205">https://developerbee.tistory.com/205</a></li>
<li><a href="https://blog.leehov.in/37">https://blog.leehov.in/37</a></li>
<li><a href="https://medium.com/@bdv111/aws-ec2%EC%97%90%EC%84%9C-nginx%EB%A1%9C-react-%EC%95%B1-%EC%A7%81%EC%A0%91-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-c1e09639171e">https://medium.com/@bdv111/aws-ec2%EC%97%90%EC%84%9C-nginx%EB%A1%9C-react-%EC%95%B1-%EC%A7%81%EC%A0%91-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-c1e09639171e</a></li>
<li><a href="https://ppost.tistory.com/entry/ReactNginxAWS-AWS-EC2%EC%97%90-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95">https://ppost.tistory.com/entry/ReactNginxAWS-AWS-EC2%EC%97%90-React-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</a></li>
<li><a href="https://tttap.tistory.com/120">https://tttap.tistory.com/120</a></li>
<li><a href="https://ppost.tistory.com/entry/AWS-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1-%ED%9B%84-%EC%A3%BC%EC%9D%98%ED%95%A0%EA%B2%83">https://ppost.tistory.com/entry/AWS-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1-%ED%9B%84-%EC%A3%BC%EC%9D%98%ED%95%A0%EA%B2%83</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Photorage] 2. React + TypeScript를 Vite로 시작하기(스토리북 추가)]]></title>
            <link>https://velog.io/@choi-ju12g/Photorage-2-1.-React-TypeScript%EB%A5%BC-Vite%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@choi-ju12g/Photorage-2-1.-React-TypeScript%EB%A5%BC-Vite%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Thu, 26 May 2022 05:01:14 GMT</pubDate>
            <description><![CDATA[<p>이번엔 Vite를 이용하여 프로젝트를 생성 하고 사용하고자 하는 추가 개발 도구들(스토리북 등)을 설치 해보자. 그 전에 앞서  npm 이나 yarn 이 설치가 되어있지 않은 분들은 그것을 먼저 설치 하도록 하자. 
참고 링크 - <a href="https://heropy.blog/2017/11/25/yarn/">https://heropy.blog/2017/11/25/yarn/</a></p>
<h2 id="1-프로젝트-생성">1. 프로젝트 생성</h2>
<p>나는 yarn을 이용해서 프로젝트 생성을 해줬다. npm 명령어도 설명을 위해서 같이 적어두겠다. </p>
<p><code>yarn create vite [프로젝트 명] --template react-ts</code> 
or
<code>npm create vite@latest [프로젝트 명] --template react-ts</code></p>
<blockquote>
<p>실제 사용 <code>$ yarn create vite photorage-web --template react-ts</code>
참고) 여기서 프로젝트 명, 템플릿은 빼고 명령어를 실행해도 터미널 상에서 설정할 수 있는 로직이 있으니 그렇게 설정해도 된다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/c38be7e1-a3f9-42a1-b25e-02eeb30dfd4e/image.png" alt=""></p>
</blockquote>
<h2 id="2-프로젝트-실행">2. 프로젝트 실행</h2>
<p>CRA를 할때와 다르게 진짜 순식간에 프로젝트 생성이 끝났다. 위 사진에서 보이는대로 2.57초가 걸렸으니 말이다. 
다음으로 아래 명령어를 통해 기본으로 제공되는 화면을 띄워보았다.</p>
<pre><code class="language-bash">$ cd [프로젝트 명]
$ yarn (필요 패키지 다운)
$ yarn dev or yarn vite(개발 서버 실행)</code></pre>
<p>위 코드를 실행하게 되면 localhost:3000 에 아래와 같은 창이 띄워지면 프로젝트 생성에 성공한 것이다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/c305672e-0e7e-4d6a-b21b-73b876540797/image.png" alt=""></p>
<ul>
<li>[비교] CRA를 이용한 프로젝트 생성 시간 : 약 70초
<img src="https://velog.velcdn.com/images/choi-ju12g/post/700f6560-6a9e-4230-b24d-372291271ed2/image.png" alt=""></li>
</ul>
<h2 id="3-storybook-추가">3. Storybook 추가</h2>
<p>UI 관리 툴로 Storybook을 사용할 것이기 때문에 추가 설치를 해줬다.</p>
<pre><code class="language-bash">$ npx sb init --builder @storybook/builder-vite
$ npm run storybook</code></pre>
<p>위 명령어를 실행하면 프로젝트 루트 디렉토리에 <code>.stories</code> 디렉토리가 생기고 src폴더에 관련 컴포넌트들이 위치하게 된다. 또 localhost:6006에 CRA때와 똑같이 띄워지더라. </p>
<p>추가로, 나는 컴포넌트는 src 폴더에서 구조화해서 가지고 있고 그것을 스토리북에 등록하는 stories 파일들은 따로 관리가 하고 싶었다(기본 설정은 stories와 tsx 파일이 같이 있음)</p>
<p>그래서 .stories 폴더의 main.js의 아랫 부분을 수정하고 stories 파일들을 따로 빼주었다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/b869ab65-a13a-4cc9-906f-f621dbef02a9/image.png" alt=""></p>
<h2 id="4-현재까지-느낀-cra와-차이점들">4. 현재까지 느낀 CRA와 차이점들</h2>
<p>1) git이 자동으로 생성이 안된다(CRA는 실행시 자동으로 git이 생성 됨)
2) build 파일 이름이 build가 아닌 dist로 설정된다.
3) 프로젝트 생성 시간이 빠르고, 빌드도 진짜 빠르다
4) 스토리북 관련해서는 아직 CRA가 더 빠르다고 한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Photorage] 1-3. CRA 대안 선택(Vite React) ]]></title>
            <link>https://velog.io/@choi-ju12g/Photorage-2.-React-TypeScript-%EB%A5%BC-Vite%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@choi-ju12g/Photorage-2.-React-TypeScript-%EB%A5%BC-Vite%EB%A1%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 26 May 2022 04:22:44 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@choi-ju12g/Photorage-1-2.-CRA-%EC%97%86%EC%9D%B4-React-TypeScript-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0">[Photorage] 1-2. CRA없이 React+TypeScript 개발환경 구축하기</a>에서 이어지는 글이다.</p>
<h2 id="1-cra-대안-결정">1. CRA 대안 결정</h2>
<p>CRA 없이 프로젝트 기본 설정을 해봤다. 정말 힘들긴 힘들더라. 아직 모르는게 너무 많기 때문이었다. 또 내가 생각했던 webpack 등을 안쓰고 내 입맛대로 커스텀 하면서 프로젝트 세팅을 하기에는 무리가 있다고 판단이 되었다(능력적으로도 시간적으로도).
그러던 중 Toast UI 의 <a href="https://ui.toast.com/weekly-pick/ko_20220127">차세대 빌드 도구 비교</a> 라는 글을 보게 되었다. 자세한 설명은 해당 링크를 가서 보면 좋을 것 같다. 이 글에서는 마지막 부분에 있는 비교 표를 기반으로 왜 내가 Vite를 선택했는지 기록해두려 한다. </p>
<ul>
<li><h3 id="사유-1-사용-사례">사유 1. 사용 사례</h3>
</li>
</ul>
<p>가장 빠른 빌드 속도를 자랑하는 esbuild를 사실 처음에는 마음에 두고 있었는데, 아직 프로덕션에 사용하기에 준비가 되지 않았다고 한다. 일단 사용 사례가 많지는 않고, css 관련 부분에 오류가 많이 존재한다고 한다. 그래서 일단 esbuild는 사용하지 않기로 했다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/0d5ef816-f0fd-4483-93b7-534a5039ad9a/image.png" alt=""></p>
<hr>
<ul>
<li><h3 id="사유-2-설치-및-개발-서버">사유 2. 설치 및 개발 서버</h3>
기본 설치 용량이 가장 가벼운 wmr은 프론트엔드 프레임워크를 위한 템플릿이 준비되어 있지 않았다. 사용하기 위해서는 Preact 라는 것을 사용해야 하는 것 같아 사용하지 않기로 했다.(Preact가 별로다 보다는  팀 프로젝트고 개발자가 나혼자는 아니기 때문에 팀원들의 공통 기술 스택이 React로 결정되어서 React 위주로 생각하고 있다)
또, 개발 서버 관련 해서 처음에 원했던게, HMR이라고 하는 수정사항이 있을때 바로바로 자동으로 새로고침을 해주는 서비스였는데 이건 Snowpack과 Vite 둘다 적용되더라.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/e9d2e43f-8764-4135-b2a8-1b9c31243563/image.png" alt=""></li>
</ul>
<hr>
<ul>
<li><h3 id="사유-3-프로덕션-빌드-및-ssr서버사이드-렌더링-지원">사유 3. 프로덕션 빌드 및 SSR(서버사이드 렌더링 지원)</h3>
마지막으로 고려했던 부분은 어찌됐든 우리는 실제 서비스를 배포하는 것까지 목표로 하고 있었기에, 사전에 구성된 프로덕션 빌드가 있으면 좋겠다는 생각을 했다. 내가 이것을 직접 구성하는 것은 여러 포스팅을 읽어보니 무지성 esbuild 등을 이용한 프로덕션 배포는 오히려 별로라는 식의 글이 있었기 때문이다.
사실 다른기능의 표에 있는 것들은 내가 생각을 많이 해본 분야는 아니긴 했는데 서버 사이드 렌더링은 기회가 된다면 붙여보고 싶은 기능이었고, CSS 전처리기는 당연히 쓸 생각이었는데 이걸 따로 고려를 해야한다는 것을 이때 알게 되었다. 
<img src="https://velog.velcdn.com/images/choi-ju12g/post/eb399b29-02bf-43a5-a1f8-3fcf89e34e27/image.png" alt=""></li>
</ul>
<hr>
<h2 id="2-vite-사용-결정">2. Vite 사용 결정</h2>
<p>이렇게 Vite를 사용 결정을 하고 가장 걱정되었던 것은 관련 자료가 너무 없지는 않을지였다. 그래서 <a href="!%5B%5D(https://velog.velcdn.com/images/choi-ju12g/post/1ca26817-06e3-4a58-8637-7eca86278775/image.png)">Vite 공식 홈페이지</a> 및 여러 포스팅을 둘러봤는데 생각보다 CRA에서 Vite로 넘어오려고 하는 분들이 꽤 있는 것 같아서 믿고 Vite를 써보기로 결정했다. 다행히 React와 TypeScript 모두 지원하더라(그 외에도 Vanila, Vue 등 많은 프레임워크를 손쉽게 시작할 수 있게 구성되어 있어서 맘에 든다)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Photorage] 1-2. CRA 없이 React + TypeScript 개발환경 구축하기]]></title>
            <link>https://velog.io/@choi-ju12g/Photorage-1-2.-CRA-%EC%97%86%EC%9D%B4-React-TypeScript-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@choi-ju12g/Photorage-1-2.-CRA-%EC%97%86%EC%9D%B4-React-TypeScript-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 26 May 2022 03:52:27 GMT</pubDate>
            <description><![CDATA[<p>지난 글 <a href="">1-1. React CRA가 최선이고 최고일까?</a>에 이어지는 글입니다.</p>
<p>나는 CRA를 안쓰고 싶었으며, 빠른 빌드 도구인 esbuild 존재를 알았기 때문에 CRA 없이 React + TypeScript의 개발환경을 세팅하는 방법을 우선 알아보았다. </p>
<h3 id="1-패키지-매니저는-npm을-사용">1. 패키지 매니저는 npm을 사용</h3>
<p>yarn과 npm의 차이는 <a href="https://joshua1988.github.io/vue-camp/package-manager/npm-vs-yarn.html#yarn">https://joshua1988.github.io/vue-camp/package-manager/npm-vs-yarn.html#yarn</a> 참고하면 좋을 것 같다.</p>
<h3 id="2-packagejson-파일을-생성-및-설정">2. package.json 파일을 생성 및 설정</h3>
<pre><code class="language-bash">&gt; npm init</code></pre>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;webpack-practice&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.tsx&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;
  },
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;
}
// npm init 명령어는 package.json에 정보를 기록하고 프로젝트를 초기화한다</code></pre>
<h3 id="3-typescript를-설치선택">3. TypeScript를 설치(선택)</h3>
<pre><code class="language-bash">npm i typescript</code></pre>
<h3 id="4-react-dom을-설치">4. react-dom을 설치</h3>
<pre><code class="language-bash">npm i react react-dom
npm i -D @types/react @types/react-dom</code></pre>
<ul>
<li>React와 React-dom은 js로 만들어져있고 타이핑을 제공하지 않는다.
따라서 React와 React-dom의 타입은 알지 못하기 때문에 외부 패키지를 사용한다.<ul>
<li>TS로 만들어진 라이브러리는 따로 @types/____와 같은 타입을 받을 필요가 없다.</li>
</ul>
</li>
</ul>
<h3 id="5-webpack을-설치">5. webpack을 설치</h3>
<pre><code class="language-bash">npm i webpack webpack-cli webpack-dev-server -D</code></pre>
<ul>
<li>-D는 개발용(webpack은 개발단계에서만 사용되기 때문에)</li>
<li>devServer: <strong>실시간</strong> 개발 모드로 개발하는 중 변경사항이 프로젝트에 반영</li>
<li>TypeScript 코드를 원하는 JavaScript 코드로 컴파일하려면 ts-config.json이 필요하다.</li>
<li>ts-loader를 이용해 Typescript를 Javascript로 변환시켜 줘야한다.</li>
</ul>
<h3 id="6-바벨크로스-브라우징-이슈-해결을-설치-및-설정">6. 바벨(크로스 브라우징 이슈 해결)을 설치 및 설정</h3>
<pre><code class="language-bash">npm i -D babel-loader @babel/core 
npm i -D @babel/preset-env @babel/preset-react @babel/preset-typescript</code></pre>
<pre><code class="language-jsx">module.exports = {
  presets: [
    &quot;@babel/preset-react&quot;,
    &quot;@babel/preset-env&quot;,
    &quot;@babel/preset-typescript&quot;,
  ],
};

/*
목적에 맞게 여러가지 플러그인(코드 변환을 처리하는 모듈)을 세트로 모아놓은 것을 “프리셋”이라고 한다.
babel-loader : JSX 및 ES6+ 문법을 트랜스파일링
@babel/core : 바벨의 코어
@babel/preset-react : 리액트의 JSX를 트랜스파일링
@babel/preset-env : ES6+ 코드를 ES5로 트랜스파일링하고 브라우저 폴리필을 자동화
*/</code></pre>
<h3 id="7-tsconfigjson-설정하기">7. tsconfig.json 설정하기</h3>
<pre><code class="language-bash">tsc --init</code></pre>
<ul>
<li>타입스크립트 컴파일러 옵션은 해당 문서를 참고하자</li>
</ul>
<pre><code>[TS DOC | Intro to the TSConfig Reference](https://www.typescriptlang.org/tsconfig)</code></pre><h3 id="8-필요한-플러그인-설치">8. 필요한 플러그인 설치</h3>
<pre><code class="language-bash">npm i -D html-webpack-plugin clean-webpack-plugin</code></pre>
<ul>
<li>플러그인이란<ul>
<li>로더가 파일 단위로 처리하는 반면 플러그인은 번들된 결과물을 처리한다.</li>
<li>번들된 자바스크립트를 난독화 한다거나 특정 텍스트를 추출하는 용도로 사용한다.</li>
</ul>
</li>
<li><strong>CleanWebpackPlugIn</strong>: 번들링을 할 때마다 이전 번들링 결과를 제거</li>
<li><strong>HtmlWebpackPlugin</strong>: HTML 파일에 번들링 된 자바스크립트 파일을 삽입해주고 이 플러그인으로 빌드하면 HTML 파일로 아웃풋에 생성된다.</li>
</ul>
<h3 id="9-webpackconfigjs-설정">9. webpack.config.js 설정</h3>
<pre><code class="language-jsx">const path = require(&#39;path&#39;);
const webpack = require(&#39;webpack&#39;);

module.exports = {
  mode: &#39;development&#39;, // 배포용: production
  devtool: &#39;eval&#39;, // 배포용: hidden-source-map   *그냥 source-map을 쓰면 개발자 도구에 소스코드가 노출이 된다.
  resolve: {
    extensions: [&#39;.jsx&#39;, &#39;.js&#39;, &#39;.tsx&#39;, &#39;.ts&#39;], // 웹팩에서 처리해주는 확장자들
  },

  entry: {
    app: &#39;./app.tsx&#39;,
  },
  // modules, plugins에 적힌 처리과정을 client.tsx에 적용을 해서 최종적으로 app.js를 뽑아낸다(output).
  module: {
    rules: [
      // babel대신에 tsc 설정.
      {
        test: /\.tsx?$/, // tsx나 ts파일을 발견하면
        loader: &#39;ts-loader&#39;, // 해당 loader를 통해서 버전을 변환하겠다 는 뜻.
      },
    ],
  },
  plugins: [], // 현재는 필요없어진 옵션이라고 한다.
  output: {
    filename: &#39;[name].js&#39;, // or &#39;[name].js&#39;
    path: path.join(__dirname, &#39;dist&#39;), // npm webpack을 실행하면 client.tsx를 통해서 webpack처리 후 dist폴더가 생기고 그 안에 app.js가 들어있다.
  },
};</code></pre>
<h3 id="10-indextsxentry와-apptsx를-만들어줍시다">10. index.tsx(entry)와 App.tsx를 만들어줍시다</h3>
<pre><code class="language-tsx">//index.tsx
import ReactDOM from &quot;react-dom&quot;;
import App from &quot;./App&quot;;

ReactDOM.render(&lt;App /&gt;, document.getElementById(&quot;root&quot;));

//App.tsx
const App: React.FC = () =&gt; {
    return (
        &lt;div&gt;hello world&lt;/div&gt;
    )
};

export default App;</code></pre>
<h3 id="마지막으로-프로젝트를-시작하기-위해-스크립트를-수정해줍시다">마지막으로 프로젝트를 시작하기 위해 스크립트를 수정해줍시다</h3>
<pre><code class="language-tsx">&quot;scripts&quot;: {
    &quot;dev&quot;: &quot;webpack-dev-server --mode=development --open --hot --progress&quot;,
    &quot;build&quot;: &quot;webpack --mode=production  --progress&quot;,
    &quot;start&quot;: &quot;webpack --mode=development  --progress&quot;
},</code></pre>
<p>** 참고 링크 **</p>
<ul>
<li><a href="https://velog.io/@ginameee/typescript-%EB%B2%88%EB%93%A4%EB%A7%81">https://velog.io/@ginameee/typescript-번들링</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Photorage] 1-1. React CRA가 최선이고 최고일까?]]></title>
            <link>https://velog.io/@choi-ju12g/Photorage-1-1.-React-CRA%EA%B0%80-%EC%B5%9C%EC%84%A0%EC%9D%B4%EA%B3%A0-%EC%B5%9C%EA%B3%A0%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@choi-ju12g/Photorage-1-1.-React-CRA%EA%B0%80-%EC%B5%9C%EC%84%A0%EC%9D%B4%EA%B3%A0-%EC%B5%9C%EA%B3%A0%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Tue, 17 May 2022 14:43:52 GMT</pubDate>
            <description><![CDATA[<h2 id="공부-배경">공부 배경</h2>
<p>내가 ICT 인턴을 진행하는 기업의 서비스는 90%가 Vanila TypeScript로 이루어져있고, 요새 새로 추가하는 신규 기능 같은 경우는 React를 통해 개발을 진행하고 있다. 나는 입사 당시만해도 React를 전혀 몰랐기 때문에 아무런 감흥도 없었는데, 인턴 동기들은 상당히 당황? 했던 것이 CRA(create-react-app) 이라는 툴체인을 사용하지 않은 부분이었다. </p>
<hr>
<h2 id="cra가-뭔데">CRA가 뭔데?</h2>
<p>CRA가 뭔지 React 공식 홈페이지의 말을 인용해보겠다. </p>
<blockquote>
</blockquote>
<p>React를 배우고 있거나 아니면 새로운 싱글 페이지 앱을 만들고 싶다면 <span style="color:red"><code>Create React App</code>.</p>
<ul>
<li><p>많은 파일과 컴포넌트 스케일링</p>
</li>
<li><p>서드 파티 npm 라이브러리 사용</p>
</li>
<li><p>일반적인 실수를 조기에 발견</p>
</li>
<li><p>CSS와 JS를 실시간으로 편집</p>
</li>
<li><p>프로덕션 코드 최적화</p>
</li>
<li><p>별도의 환경설정 없음</p>
<p>즉 한마디로, <a href="https://velog.io/@choi-ju12g/JavaScript-%EB%B2%88%EB%93%A4%EB%9F%ACBundler-%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C">번들러</a>(웹팩) 설정을 해주고 다양한 라이브러리 등을 사용하기 쉽게 해주는 도구인 것이다. 보통 React를 처음 공부할 때 CRA를 많이 이용하여 SPA 및 Component, props, state 등의 개념을 익히기 시작한다.
<code>npx create-react-app [프로젝트 명]</code></p>
<p>하지만, 분명 단점도 존재한다. 그것은 이미 세팅된 설정들을 바꾸기 어렵고, eject 등의 명령어를 통해 바꿀 수 있지만 그렇게 되면 CRA의 라이브러리 버전 관리를 통한 의존성 문제 제거 등의 다양한 이점을 누리지 못하게 될 수도 있다.(eject 가 뭔지, 대체 방안은 없는지는 다른 글에서 기회가 되면 포스팅 해보겠다)</p>
<h3 id="요약">요약</h3>
<ul>
<li><p>CRA란?</p>
<ul>
<li>리액트 프로젝트를 시작하는데 필요한 개발 환경을 세팅해주는 툴</li>
</ul>
</li>
</ul>
</li>
<li><p>CRA의 장점</p>
<ul>
<li><p>webpack, babel과 같은 모듈 번들러, 트랜스파일러를 자동으로 설정해줘서 초보자가 접근하기 쉽습니다.</p>
</li>
<li><p>ES6+문법, CSS 후처리와 같은 개발환경을 자동으로 설정해줍니다.</p>
</li>
</ul>
</li>
<li><p>CRA의 단점</p>
<ul>
<li>웹팩, 바벨 설정을 변경하기 어렵습니다.(eject 사용해서 수정해줘야함)</li>
<li>작은 프로젝트를 진행할 때 너무 파일이 무겁습니다.</li>
</ul>
<hr>
</li>
</ul>
<h2 id="cra가-쓰기-싫었던-이유">CRA가 쓰기 싫었던 이유</h2>
<p>  나는 분명 웹개발 초보이다. 웹팩 등의 바벨 설정 문제가 생기면 해결도 잘 못할 뿐더러 어디서 문제가 발생했는지 파악하기도 어려울 수도 있다. 그런 입장에서 CRA는 최고의 선택일 수 있다. 그럼에도 내가 CRA에 의구심을 갖고 다른 것을 쓰려고 한 이유를 정리해두려 한다. </p>
<h3 id="1-빌드가-느리다">1. 빌드가 느리다.</h3>
<p>  빌드가 뭔지 또 그게 어떤 문제로 이어질 지 바로 떠오르지 않았다면, <a href="https://velog.io/@choi-ju12g/%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EB%B9%8C%EB%93%9C-%EB%B0%B0%ED%8F%AC-%EA%B0%9C%EB%85%90-%EB%B0%8F-CICD-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC">컴파일, 빌드, 배포 개념 및 CI/CD 개념 정리</a> 를 읽어보는 것을 추천한다. </p>
<p>ICT인턴십을 진행하는 회사의 웹사이트를 빌드하는데 10분정도의 시간이 걸린다. 사실 엄청 긴 시간은 아닐 수 있다. 나도 그렇게 생각했다. 하지만 회사 사수분은 이것이 너무 느리다며 개선을 원했고 그 때 나는 어느 토스 프론트 개발자분의 <a href="https://velog.io/@lingodingo/%EB%B9%8C%EB%93%9C-%EC%86%8D%EB%8F%84%EB%A5%BC-%EC%98%AC%EB%A0%A4%EB%B3%B4%EC%9E%90feat.-esbuild">이글</a>을 보고 CRA가 최선은 아님을 알게 되었다.
그래서 실제 이 토이 프로젝트를 배포하게 된다면, 나는 AWS의 클라우드 서비스를 이용해야 하고 이것은 빌드 시간을 기준으로 요금을 책정한다. 
  그 말은 즉슨, <span style="color:red">빌드 시간이 짧으면 짧을수록 비용을 절감 할 수 있다.<span/></p>
<h3 id="2-번들링-도구웹팩-등을-공부해보고-싶어졌다">2. 번들링 도구(웹팩 등)을 공부해보고 싶어졌다.</h3>
<p>  위에서도 잠깐 언급했지만 나는 웹팩이 뭔지 전혀 몰랐는데, 회사에서 일을 해보며 자연스럽게 <code>package.json</code> 을 보며 관심을 갖게 되고 더 나아가 <code>webpack.config</code> 등의 파일을 보는데 두려움이 적어졌다. 
  <a href="https://velog.io/@choi-ju12g/JavaScript-%EB%B2%88%EB%93%A4%EB%9F%ACBundler-%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C">번들러</a>, <a href="">package.json의 이해</a>, <a href="">npm vs yarn</a> 등의 글 포스팅이 그 관심의 증거이다.
  그래서, 이러한 초기 설정 및 프로젝트 관리를 직접 해보고자 하는 마음이  생겨 CRA가 아닌 다른 것들을 찾아보게 되었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[배경 지식] 번들러(Bundler) 에 대해서]]></title>
            <link>https://velog.io/@choi-ju12g/JavaScript-%EB%B2%88%EB%93%A4%EB%9F%ACBundler-%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</link>
            <guid>https://velog.io/@choi-ju12g/JavaScript-%EB%B2%88%EB%93%A4%EB%9F%ACBundler-%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</guid>
            <pubDate>Tue, 17 May 2022 13:46:26 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-blue-포스팅-배경"><span style="color: blue"/> 포스팅 배경</h2>
<p>아래는 ICT 인턴십을 준비하며 기업 면접에서 받았던 질문과 답변 내용이다.  </p>
<blockquote>
</blockquote>
<p>Q. <code>FrontEnd 개발자</code> 로서 현재 프로젝트가 느리다고 생각될 때, 어떠한 조치를 취할 수 있겠습니까?<br>
A. 시간이 걸리는 동안 로딩되는 표시를 띄워 사용자로 하여금 현재 뭔가 작동중이라는 것을 명시적으로 표현 해주겠다(UI/UX 문제로 해석) <br>
Q. 실제로 프로젝트의 성능을 높일 수 있는 방법은 없습니까?<br>
A. (도저히 생각나지 않아서) Garbage Collection 등 더이상 쓰지 않는 메모리를 줄이겠습니다. </p>
<p>이를 끝나고 곰곰히 생각해봤지만 그때의 나는 정답을 몰랐다. 근데 ICT인턴을 진행하다 보니 아마 지금 설명해보고자 하는 번들러가 정답이지 않았을까 싶다. </p>
<h2 id="span-stylecolor-green-번들러-탄생-이유"><span style="color: green"/> 번들러 탄생 이유</h2>
<p>인터넷 등장 직후 웹 사이트가 하는 일은 많지 않았다. HTML과 CSS 그리고 JavaScript 몇 개 파일을 주고 받았을 뿐이다. 하지만 오늘날 인터넷의 발전에 따라 웹 사이트를 이루고 있는 프로젝트의 규모는 너무나도 커졌다. 이로 인해 아래의 문제점들이 나타나기 시작했다. </p>
<p><strong>1. 중복된 변수 명으로 인한 에러</strong></p>
<ul>
<li><code>A.js</code> 의 const temp와 <code>B.js</code> 의 const temp를 구별하지 못함.</li>
</ul>
<p><strong>2. 파일로 인한 네트워크 지연 문제</strong></p>
<ul>
<li>1개 파일 전송에 1초가 걸린다고 할 때, 파일 개수가 늘어남에 따라 전송 소요 시간이 너무 긺</li>
<li>하나의 파일에 모든 코드를 넣게 되면, 개발자 입장에서 유지 보수를 진행하기가 매우 어렵고 힘듦.</li>
</ul>
<p>이러한 문제들을 해결하기 위해 여러개의 파일을 하나의 파일로 묶어주는 <code>번들러(Bundler)</code>가 등장하게 되었다. 굉장히 여러종류가 있지만 React의 툴체인 중 가장 보편적인 CRA(Create-React-App) 를 이루고 있는 Webpack은 아래의 그림처럼 설명한다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/8abbcf2a-9009-4c82-9205-432a81d9925b/image.png" alt=""></p>
<h2 id="span-stylecolor-blue-번들러-장점"><span style="color: blue"/> 번들러 장점</h2>
<p><strong>- 네트워크 병목 현상 해결</strong>
위에서 설명한 <code>A.js</code> 와 <code>B.js</code>파일을 번들러를 사용할 경우 하나의 파일로 묶어서 보낼 수 있다. 또 여러개의 파일을 번들링 할 때 종속성을 알아서 확인하여 사용하지 않는 파일은 포함하지 않고 번들링하여 파일의 개수 및 크기를 줄여 페이지 로딩을 빠르게 해줄 수 있다.</p>
<p><strong>- 높은 수준의 언어 사용 가능(ES6 이상의 문법, CSS Preprocessor, TypeScript 등)</strong>
Webpack은 <code>Babel</code> 이라는 트랜스파일러(컴파일러)를 이용하여 ES6 이상의 문법을 ES5 버전의 JavaScript 코드로 변환시켜 주는데, 이로써 브라우져에서 지원하지 않는 문법을 쓸 수 있게 된다.
또, CSS 전처리기인(Less, SCSS 등)과 정적 타입 언어인 TypeScript 등을 사용할 수 있게 된다. </p>
<p><strong>- 어플리케이션 프로덕션 환경 최적화</strong>
코드를 작성할 때, <code>ESLint</code>나 <code>Prettier</code>를 사용해본 경험이 있을 것이다. 이것들은 코드를 사람(개발자)가 보기 편하도록 규칙(포맷)을 지정하여 코드들을 재정렬한다. 이로써 브라우져 입장에서는 필요없는 공백들이 상당히 생기게 되는데 번들러는 이를 제거해준다.</p>
<h2 id="span-stylecolor-orange-번들러-종류-및-비교"><span style="color: orange"/> 번들러 종류 및 비교</h2>
<p>비교를 직접 할 까 하다가 너무 좋은 글을 발견하여 링크를 남기는것으로 대체하겠다. 
<a href="https://wormwlrm.github.io/2020/08/12/History-of-JavaScript-Modules-and-Bundlers.html">https://wormwlrm.github.io/2020/08/12/History-of-JavaScript-Modules-and-Bundlers.html</a></p>
<p>** 참고 자료 **
<a href="https://velog.io/@eastshine94/Bundler-%EC%99%9C-Bundler%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94%EA%B0%80">https://velog.io/@eastshine94/Bundler-%EC%99%9C-Bundler%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94%EA%B0%80</a></p>
<p><a href="https://developer-alle.tistory.com/297">https://developer-alle.tistory.com/297</a></p>
<p><a href="https://heropy.blog/2018/01/31/sass/">https://heropy.blog/2018/01/31/sass/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Photorage] 0. 토이 팀 프로젝트 시작 ]]></title>
            <link>https://velog.io/@choi-ju12g/Photorage-0.-%ED%86%A0%EC%9D%B4-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@choi-ju12g/Photorage-0.-%ED%86%A0%EC%9D%B4-%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Tue, 17 May 2022 13:46:16 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-요약">프로젝트 요약</h2>
<ul>
<li>사진 기반의 SNS가 넘쳐나는데, 다른 SNS 서비스들과 다르게 &quot;앨범&quot; 이라는 개념을 조금 더 강하게 도입하고자 함.</li>
<li>기존 SNS의 복잡한 기능들 대신 일상/여행의 앨범이라는 개념만으로 구현.</li>
<li>친구들끼리의 추억, 가족들끼리의 추억을 담은 앨범으로 시작. 그 순간을 앨범이라는 틀에 넣어서, 이름을 짓고 싶을 수 있음</li>
<li>인스타는 그런 느낌의 기능이 부족하고, 네이버 MYBOX는 너무 클라우드 드라이브 느낌에 약간의 사진 앨범 자동 생성 등을 엮은 느낌</li>
<li>적당한 SNS + 확장성 + 앨범(사진)의 기능 강화</li>
</ul>
<hr>
<h2 id="팀-소개">팀 소개</h2>
<ul>
<li>UI/UX Designer 겸 Front-end 개발자 1명</li>
<li>Front-end 개발자 1명(본인)</li>
<li>Back-end 개발자 2명</li>
</ul>
<p>총 4명으로 이루어진 팀으로 각자의 목표를 갖고 그것을 성취할 수 있는 프로젝트를 진행하기로 했다. 이번엔 정말 출시 후 서비스 까지를 바라보고 진행 할 예정이다. 혼자 하는 것이 아니니까 좀 더 할 수 있지 않을까 싶다.</p>
<p>내 목표는 아래와 같다.</p>
<blockquote>
<ul>
<li>내가 사용하는 언어, 툴에 대해 이해하고 쓰고 왜 그것을 쓰는지 나름의 이유를 갖을 수 있기</li>
</ul>
</blockquote>
<ul>
<li>웹 사이트를 온전히 처음(프로젝트 생성)부터 끝(배포 후 서비스) 까지 개발해보기</li>
<li>Front-end 개발자로서 내 포트폴리오의 메인 프로젝트를 경험해보기</li>
</ul>
<p>그래서 해당 목표를 이루기 위해, 어쩌면 개발 시간보다 개념을 찾아보고 그것을 블로그에 포스팅하며 정리하는 시간이 초반엔 훨씬 많을지도 모르겠다. 그렇더라도 이번 Photorage 프로젝트 만큼은 선택에 있어서 많은 고민을 하고 단순히 이걸 많이 쓰니까  이걸 해야지가 아닌 우리 프로젝트에는 이게 더 좋겠네 싶은 것들을 골라서 쓰고 기록해보려고 한다.</p>
<hr>
<h2 id="기술-스택">기술 스택</h2>
<ul>
<li>Front-end<ul>
<li>React(Vite 툴체인, TypeScript 적용)</li>
<li><a href="https://velog.io/@choi-ju12g/0.-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-%EB%93%B1%EB%A1%9D">Storybook</a> 도입 </li>
<li>Styled-Component(CSS in JS) 도입</li>
<li>상태 관리 툴(Recoil or MobX 등 미정)</li>
<li>웹서버 : AWS의 EC2, S3, CloudFront 사용 예정</li>
</ul>
</li>
<li>Back-end<ul>
<li>Spring Boot + Node.js</li>
<li>MongoDB(NOSQL)</li>
<li>API 서버 : AWS 의 EC2 사용 예정(MSA 구조로 여러 서버로 분리)   </li>
</ul>
</li>
<li>통신<ul>
<li>Ajax 기반 통신</li>
</ul>
</li>
<li>디자인<ul>
<li>Figma : Storybook 연동</li>
</ul>
</li>
<li>협업 및 코드 관리 툴<ul>
<li>Notion, Slack, GitHub</li>
</ul>
</li>
</ul>
<hr>
<p>** 여담 및 각오 **
그동안 블로그의 몇개 개인 프로젝트를 하는 듯 포스팅을 하다 거의 올리지 못하고 있다. 이유는 단순히 시간을 들이지 못했기 때문이다. 그 이유는 ICT 인턴을 진행하며 퇴근 이후, 개인 프로젝트를 진행할 여유가 없었기 때문이다. 실제 서비스에 모든 영역에 참여하는 것은 당연히 아니지만 그래도 모르는 것이 너무나 많았기에 프로젝트 외적인 공부를 진행하다보니 프로젝트에 시간을 쏟지 못했다. 이제 3달차가 되어가는 입장에서 그동안 공부했던 내용들을 함축해서 적용해 볼 수 있도록 팀 토이프로젝트를 시작해보려 한다(하면서 내가 그동안 놀고만 있지는 않았다는 것을 블로그 포스팅의 깊이와 양에서 보여주고 싶다)</p>
<p>** 참고 링크 **</p>
<ul>
<li><p><a href="https://storybook.js.org/tutorials/intro-to-storybook/react/ko/get-started/">https://storybook.js.org/tutorials/intro-to-storybook/react/ko/get-started/</a></p>
</li>
<li><p><a href="https://vitejs-kr.github.io/guide/why.html">https://vitejs-kr.github.io/guide/why.html</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[배경 지식] 컴파일, 빌드, 배포 개념 및 CI/CD 개념 정리 ]]></title>
            <link>https://velog.io/@choi-ju12g/%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EB%B9%8C%EB%93%9C-%EB%B0%B0%ED%8F%AC-%EA%B0%9C%EB%85%90-%EB%B0%8F-CICD-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@choi-ju12g/%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EB%B9%8C%EB%93%9C-%EB%B0%B0%ED%8F%AC-%EA%B0%9C%EB%85%90-%EB%B0%8F-CICD-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 16 May 2022 12:56:53 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-orange-빌드-컴파일-배포build-compile-deploy란"><span style="color: orange"/> 빌드, 컴파일, 배포(Build, Compile, Deploy)란?</h2>
<ul>
<li><h3 id="span-stylecolor-orange-컴파일"><span style="color: orange"/> 컴파일</h3>
</li>
<li><em>컴퓨터가 이해할 수 있는 언어로 바꿔주는 과정*</em>
보통 Java, C#, Swift와 같은 고급 언어로 작성된 코드를 기계어로 변환하는 과정을 말한다.
자바스크립트에서의 컴파일러는 Babel과 Typescript 등이 있다. Babel을 통해 최신 자바스크립트 스펙을 모든 브라우저에서 사용 가능한 스크립트로 컴파일
Typescript를 통해 TS -&gt; JS 컴파일
자바스크립트? 인터프리터 언어임 .. 하지만 컴파일과정 거친다. (컴파일이 필요한 경우엔 컴파일)</li>
</ul>
<ul>
<li><h3 id="span-stylecolor-orange-빌드"><span style="color: orange"/> 빌드</h3>
</li>
<li><ul>
<li>컴파일 된 코드를 실행할 수 있는 상태로 만드는 일** 
실제로 JavaScript로 된 프로젝트의 빌드 파일을 보면 아래와 같이 하나의 파일이 직렬화 되어 알아 볼 수 없는 형태로 되어있는 것을 볼 수 있다. 
빌드 툴로는 빌드 툴로는 Ant, Maven, Gradle 등이 있습니다. </li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/c6b8680b-46ba-4b68-87b2-f1fb2a4ae224/image.png" alt=""></p>
<ul>
<li><h3 id="span-stylecolor-orange-배포"><span style="color: orange"/> 배포</h3>
</li>
<li><ul>
<li>빌드가 완성된 실행 가능한 파일ㅇ르 사용자가 접근할 수 있는 환경에 배치시키는 일**</li>
</ul>
</li>
</ul>
<h2 id="span-stylecolor-blue-ci-cd란"><span style="color: blue"/> CI CD란?</h2>
<ul>
<li><h3 id="span-stylecolor-blue-ci란"><span style="color: blue"/> CI란?</h3>
<p><code>빌드/테스트 자동화 과정</code>을 의미하는 용어로 개발자를 위한 자동화 프로세스이며, 지속적인 통합(Continuous Integration)을 의미한다.
쉽게 GitHub에 특정 브랜치(master)에 새로운 커밋이 될 때 마다, 해당 코드를 바탕으로 빌드하고 사용자가 미리 만들어둔 테스트 코드를 실행하여 문제가 있는지 없는지를 체크하는 과정을 자동화 한것을 의미한다. </p>
</li>
<li><h3 id="span-stylecolor-blue-cd란"><span style="color: blue"/> CD란?</h3>
<p><code>배포 자동화 과정</code>을 의미하는 용어로 <code>지속적 서비스 제공(Continuous Delivery)</code> 또는 <code>지속적 배포(Continuous Deployment)</code>를 의미한다. 기존에는 빌드 후 문제가 없다고 판단되면 실제 서버든, 클라우드 환경의 서버 환경에 합쳐진 코드(빌드 된 상태의)를 올리는 과정을 하며 이를 배포한다 라고 한다. 그런데 위에서 설명한 CI의 과정이 되어 있다면, 우리는 배포 마저도 CI가 완료되는 시점에 자동으로 실행하면 된다. 이를 CD라고 한다. 
<img src="https://velog.velcdn.com/images/choi-ju12g/post/dae0c75c-2aa8-46c6-b12d-338071e0a43f/image.png" alt=""></p>
</li>
</ul>
<p>CI/CD 를 도와주는 각종 오픈 소스 및 플랫폼이 궁금하다면 <a href="https://velog.io/@skyni/CICD-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EA%B0%81%EC%A2%85-%ED%94%8C%EB%9E%AB%ED%8F%BC-%EC%A0%95%EB%A6%AC">CI/CD 개념 및 각종 플랫폼 정리</a>글을 통해 확인하자.</p>
<ul>
<li><h3 id="span-stylecolor-blue-cicd-적용-전-후-시나리오-비교"><span style="color: blue"/> CI/CD 적용 전 후 시나리오 비교</h3>
</li>
<li><p>CI/CD 적용 전</p>
<blockquote>
<p>1) 코드를 수정/추가 등을 하며 개발을 진행한다.
2) 각자의 코딩 컨벤션에 따라 브랜치에 push 한다.(여기서 에러가 발생했는지 아닌지 판단할 수 없음)
3) 해당 브랜치 코드에 문제가 없다고 판단되면 main branch에 병합한다.
4) 에러가 발생했다면 1)~3) 과정으로 에러를 고치고, 에러가 발생하지 않았다면  직접 배포를 진행한다.</p>
</blockquote>
</li>
<li><p>CI/CD 적용 후</p>
<blockquote>
<p>1) CI/CD 적용 전과 동일
2) push된 코드를 CI가 알아서 Build, Test, Lint(포맷팅)을 실행하고 결과를 알려준다.
3) 개발자들은 결과를 보고 에러가 난 부분을 수정한 후 main branch에 병합한다.
4) main branch를 감지하고 있던 CD 과정이 알아서 배포를 수행한다. </p>
</blockquote>
</li>
</ul>
<h3 id="참고-자료"><strong>참고 자료</strong></h3>
<p><a href="https://seosh817.tistory.com/104">https://seosh817.tistory.com/104</a></p>
<p><a href="https://velog.io/@skyni/CICD-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EA%B0%81%EC%A2%85-%ED%94%8C%EB%9E%AB%ED%8F%BC-%EC%A0%95%EB%A6%AC">https://velog.io/@skyni/CICD-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EA%B0%81%EC%A2%85-%ED%94%8C%EB%9E%AB%ED%8F%BC-%EC%A0%95%EB%A6%AC</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[남도학숙 식단 어플] 2. Node.js를 로그인이 필요한 페이지 크롤링 하기]]></title>
            <link>https://velog.io/@choi-ju12g/%EB%82%A8%EB%8F%84%ED%95%99%EC%88%99-%EC%8B%9D%EB%8B%A8-%EC%96%B4%ED%94%8C-2.-Node.js%EB%A5%BC-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%81%AC%EB%A1%A4%EB%A7%81-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@choi-ju12g/%EB%82%A8%EB%8F%84%ED%95%99%EC%88%99-%EC%8B%9D%EB%8B%A8-%EC%96%B4%ED%94%8C-2.-Node.js%EB%A5%BC-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-%ED%81%AC%EB%A1%A4%EB%A7%81-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 15 May 2022 06:34:02 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-orange🙏-크롤링이란span"><span style="color: orange">🙏 크롤링이란?</span></h2>
<p>크롤링(crawling)이란 데이터를 수집하고 분류하는 것을 의미한다. 주로 인터넷상의 웹페이지를 수집해서 분류하고 저장하는 것을 뜻하며 데이터가 어디에 저장되어 있는지 위치에 대한 분류 작업이 크롤링의 주요 목적이다. 크롤링의 주요 대상은 다양한 형태로 존재하는 데이터로, 데이터 생성 스타일에 따라, 정형, 반정형 그리고 비정형 데이터로 구분되기도 하지만, 데이터를 생산하는 주체에 따라 기업과 사용자가 생성하는 데이터로 분류할 수 있다</p>
<h3 id="span-stylecolor-orange정적-크롤링"><span style="color: orange">정적 크롤링</h3>
<p>정적 크롤링은 정적인 데이터를 수집하는 방법을 말한다. 정적인 데이터란 변하지 않는 데이터를 의미한다. 한 폐이지 안에서 원하는 정보가 모드 드러날 때 정적 데이터라고 할 수 있다. 즉 정적 크롤링은 주소를 통해 단발적으로 접근하여 한 페이지 내에서 모든 작업이 이루어지기 때문에 속도가 매우 빠르다는 장점이 있지만, 수집 데이터의 한계가 존재한다는 단점이 있다.</p>
<h3 id="span-stylecolor-orange동적-크롤링"><span style="color: orange">동적 크롤링</h3>
<p>동적 크롤링은 동적인 데이터를 수집하는 방법을 말한다. 동적인 데이터란 입력, 클릭, 로그인 등과 같이 페이지 이동이 있어야 보이는 데이터를 의미한다. 예를 들어서, 메일함에 있는 메일 제목 데이터를 수집할 때 로그인 과정을 거친 후 메일함에 들어가야 하는 동적인 과정이 필요하다. 이런 경우가 동적인 크롤링이다. 동적인 크롤링은 브라우저를 사용하여 연속적으로 접근하여 수집 데이터의 한계가 없지만, 속도가 느리다는 단점이 있다.[5]</p>
<h2 id="span-stylecolor-blue🙏-크롤링-툴"><span style="color: blue">🙏 크롤링 툴</h2>
<h3 id="span-stylecolor-blue-cheerio"><span style="color: blue"> cheerio</h3>
<p>cheerio 는 nodejs 의 라이브러리로 http 통신을 통해 데이터를 가져오는 기능을 <strong>포함하고 있지 않은</strong> 라이브러리이다.
그럼 왜쓰냐?</p>
<p>HTML문서를 문자열로 cheerio에 집어 넣어주면 jQuery 및 쿼리 셀렉터의 선택자 형식으로 데이터를 추출할 수 있기 때문이다.
Cheerio와 같은 HTML 문서 파싱 라이브러리를 사용하지 않는다면 정규식으로 매번 필요한 값을 문서에서 찾아야 하기 때문에 생산성 향상에 여러모로 불리 할 수 있다.</p>
<p>만약  HTTP 통신으로 데이터를 가져오고 싶다면 자바스크립트의 기본 라이브러리인 XMR(XMLHttpRequest)을 이용하거나 node js 라이브러리인 request 또는 request-promise 라이브러리를 이용하여 HTTP 통신의 Response 결과를 받아 온 다음 response 결과 중에서 HTML 문서를 cheerio에 로딩시켜 줘야 한다.</p>
<h3 id="span-stylecolor-blue-puppeteer"><span style="color: blue"> puppeteer</h3>
<p>Puppeteer는 Chrome 팀이 개발한 Node 라이브러리 입니다.</p>
<p>Headless 혹은 온전한 크롬도 컨트롤 할 수 있는 고차원 API를 제공합니다. 쉽게 말해 Broswer에서 여러분이 수동으로 하는 대부분의 일들을 Puppeteer를 통해 할 수 있습니다.</p>
<blockquote>
<p>예시</p>
</blockquote>
<ul>
<li><p>페이지의 스크린샷 혹은 PDF 파일 생성</p>
</li>
<li><p>싱글 페이지 어플리케이션 크롤링 및 미리 렌더링 된 컨텐츠 생성</p>
</li>
<li><p>폼 제출, UI 테스트, 키보드 입력등 자동화</p>
</li>
<li><p>테스트를 작성 하고, 최신의 크롬에서 최신 자바스크립트와 브라우저 기능들을 돌려 볼 수 있습니다.</p>
<h2 id="span-stylecolor-f09999👏-계획-및-구현"><span style="color: #f09999">👏 계획 및 구현</h2>
<h3 id="span-stylecolor-f09999계획"><span style="color: #f09999">계획</h3>
<p><strong>puppeteer</strong> 라이브러리를 이용하여 내가 직접 남도학숙 홈페이지에서 식단표를 확인하는 로직을 구현한다. 그 과정에서 필요한 정보를 cheerio를 이용하여 쉽게 가져온다. </p>
<h3 id="span-stylecolor-f09999-구현"><span style="color: #f09999"> 구현</h3>
<p><strong>1. cheerio, puppeteer 설치</strong></p>
<pre><code class="language-bash">npm install cheerio puppeteer</code></pre>
<p>위의 명령어를 프로젝트의 루트 디렉토리(MEAL_PLANNER_ALARM_NDHS)에서 실행한다.</p>
</li>
</ul>
<blockquote>
<p>[프로젝트 구조]
📂 MEAL_PLANNER_ALARM_NDHS
  | -- 📂 client(React를 사용한 프론트 디렉토리)
  | -- 📄 index.js(Express 서버 - 추후 설명 예정)
  | -- 📄 crawl.js(크롤링 관련 코드)</p>
</blockquote>
<p>  <strong>2. crawl.js 코드</strong></p>
<pre><code class="language-javascript">  const puppeteer = require(&#39;puppeteer&#39;);
const cheerio = require(&#39;cheerio&#39;);

let MealList = {
    &quot;date&quot;: &quot;&quot;,
    &quot;breakfast&quot;: &quot;&quot;,
    &quot;lunch&quot;: &quot;&quot;,
    &quot;dinner&quot;: &quot;&quot;, 
 };

async function crawl(){
    // 가상 브라우져를 실행, headless: false를 주면 벌어지는 일을 새로운 창을 열어 보여준다(default: true)
    const browser = await puppeteer.launch({headless: true});
    const page = await browser.newPage();
    const ndhs_id = &#39;&#39;; // 추후 로그인 폼에서 각자의 아이디 비밀번호를 입력받게 할 예정
    const ndhs_pw = &#39;&#39;;

    // headless: false일때 브라우져 크기 지정해주는 코드
    // await page.setViewport({
    //     width: 1366,
    //     height: 768
    // });

    //페이지로 가라
    await page.goto(&#39;http://portal.ndhs.or.kr/index&#39;);

    //해당 페이지에 특정 html 태그를 클릭해라
    await page.click(&#39;body &gt; div &gt; div &gt; div &gt; div &gt; div &gt; div.row &gt; div &gt; div.login-body &gt; div &gt; div.col-xs-12.col-sm-5.login-con.pt20 &gt; div &gt; form &gt; ul &gt; li:nth-child(2)&#39;);

    //아이디랑 비밀번호 란에 값을 넣어라
    await page.evaluate((id, pw) =&gt; {
    document.querySelector(&#39;#stuUserId&#39;).value = id;
    document.querySelector(&#39;#stuPassword&#39;).value = pw;
    }, ndhs_id, ndhs_pw);

    //로그인 버튼을 클릭해라
    await page.click(&#39;#student &gt; div &gt; div:nth-child(2) &gt; button&#39;);

    //로그인 화면이 전환될 때까지 기다려라, headless: false 일때는 필요 반대로 headless: true일때는 없어야 되는 코드
    //await page.waitForNavigation()

    //로그인 성공 시(화면 전환 성공 시)
    if(page.url() === &#39;http://portal.ndhs.or.kr/dashboard/dashboard&#39;){
        //학사 페이지로 가서
        await page.goto(&#39;http://portal.ndhs.or.kr/studentLifeSupport/carte/list&#39;);

        // 현재 페이지의 html정보를 로드
        const content = await page.content();
        const $ = cheerio.load(content);
        const lists = $(&quot;body &gt; div.container-fluid &gt; div:nth-child(6) &gt; div &gt; table &gt; tbody &gt; tr&quot;);
        lists.each((index, list) =&gt; {
            MealList[index] = {
                date: $(list).find(&quot;th&quot;).text().replace(&#39;\n\t\t\t\t\t\t\t\t&#39;,&quot;&quot;),
                breakfast:$(list).find(&quot;td:nth-of-type(1)&quot;).text(),
                lunch:$(list).find(&quot;td:nth-of-type(2)&quot;).text(),
                dinner:$(list).find(&quot;td:nth-of-type(3)&quot;).text()
            }
            console.log(MealList[index]); 

        })
    }
    //로그인 실패시
    else{
        console.log(&#39;실패&#39;);
        ndhs_id = &#39;nope&#39;;
        ndhs_pw = &#39;nope&#39;;
    }

    //브라우저 꺼라
    await browser.close();     
};


crawl();
</code></pre>
<p>  ** 3. 크롤링 결과 **
  프로젝트 루트 디렉토리에서 <code>node crawl.js</code> 실행 결과
  <img src="https://velog.velcdn.com/images/choi-ju12g/post/4dfd0add-ae24-49ad-9302-6c8e8f70f090/image.png" alt=""></p>
<h3 id="참고-링크">참고 링크</h3>
<blockquote>
</blockquote>
<ul>
<li><a href="https://no-free-lunch.tistory.com/10">https://no-free-lunch.tistory.com/10</a></li>
<li><a href="http://wiki.hash.kr/index.php/%ED%81%AC%EB%A1%A4%EB%A7%81">http://wiki.hash.kr/index.php/%ED%81%AC%EB%A1%A4%EB%A7%81</a></li>
<li><a href="https://velog.io/@recordboy/Express-Puppeteer-React-Express%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%B4-%ED%81%AC%EB%A1%A4%EB%9F%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EB%B0%8F-Heroku%EC%97%90-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0">https://velog.io/@recordboy/Express-Puppeteer-React-Express%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%B4-%ED%81%AC%EB%A1%A4%EB%9F%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EB%B0%8F-Heroku%EC%97%90-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[vscode] 디렉토리 폴더 구조 겹침 없애기]]></title>
            <link>https://velog.io/@choi-ju12g/vscode-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%ED%8F%B4%EB%8D%94-%EA%B5%AC%EC%A1%B0-%EA%B2%B9%EC%B9%A8-%EC%97%86%EC%95%A0%EA%B8%B0</link>
            <guid>https://velog.io/@choi-ju12g/vscode-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%ED%8F%B4%EB%8D%94-%EA%B5%AC%EC%A1%B0-%EA%B2%B9%EC%B9%A8-%EC%97%86%EC%95%A0%EA%B8%B0</guid>
            <pubDate>Tue, 10 May 2022 07:55:24 GMT</pubDate>
            <description><![CDATA[<h2 id="1-상황-설명">1. 상황 설명</h2>
<p>vscode를 사용하다보면 한번씩 좌측에 있는 탐색기에 하위 폴더가 / 로 보이는 경험을 겪은 적 있을겁니다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/bd234a8a-8985-45a3-9e5a-e3940d528223/image.png" alt=""></p>
<p>하위 폴더면 하위 폴더 답게 계층구조를 지켜서 화면에 보였으면 한 것이 한 두번이 아닙니다.
해당 문제를 해결하는 것은 매우 간단합니다.</p>
<h2 id="2-해결-방법">2. 해결 방법</h2>
<p><code>1) vscode 설정창 들어가기</code></p>
<pre><code>macOS : ⌘ + ,
Windows : Ctrl + ,
Linux : Ctrl + ,
위의 키를 동시에 누르면 설정화면이 나타나고 원하는 항목을 검색하여 설정할 수 있다.  </code></pre><p><img src="https://velog.velcdn.com/images/choi-ju12g/post/3142dbb2-a5de-4b3f-aede-a41667c93b3f/image.png" alt="">
다음과 같은 화면이 뜨면 설정창을  연 것입니다.</p>
<p> <code>2) explorer.compactFolders 검색 후 Explorer: Compact Folders 체크 해제</code>
 <img src="https://velog.velcdn.com/images/choi-ju12g/post/d1caf3d8-c839-47d1-925b-bfa7cbb43d65/image.png" alt=""></p>
<h2 id="3-결과">3. 결과</h2>
<p>원하던 대로 하위 폴더임이 여실히 드러나는 계층 구조를 그대로 보여주게 됩니다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/0725b9cb-60f5-46d3-b3c2-30d0594a0ccd/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2. CRA(JS) -> CRA(TS) 변환 실습]]></title>
            <link>https://velog.io/@choi-ju12g/cra</link>
            <guid>https://velog.io/@choi-ju12g/cra</guid>
            <pubDate>Fri, 29 Apr 2022 04:18:22 GMT</pubDate>
            <description><![CDATA[<h2 id="1-프로젝트-환경-세팅">1. 프로젝트 환경 세팅</h2>
<ol>
<li>CRA<ul>
<li>JS : npx create-react-app test</li>
<li>TS: npx create-react-app testts —template typescript</li>
</ul>
</li>
</ol>
<pre><code>[프로젝트 구조]![](https://velog.velcdn.com/images/choi-ju12g/post/88b45cd7-2c4f-460a-b2f1-a4974f5ead1b/image.png)

[package.json] - 빨간색 줄들이 TS 프로젝트에 추가되어 있음.
![](https://velog.velcdn.com/images/choi-ju12g/post/0bf49c13-81b5-4f89-9cd6-425098b9eb9d/image.png)

[tsconfig.json]

```javascript
{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es5&quot;,
    &quot;lib&quot;: [
      &quot;dom&quot;,
      &quot;dom.iterable&quot;,
      &quot;esnext&quot;
    ],
    &quot;allowJs&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;allowSyntheticDefaultImports&quot;: true,
    &quot;strict&quot;: true,
    &quot;forceConsistentCasingInFileNames&quot;: true,
    &quot;noFallthroughCasesInSwitch&quot;: true,
    &quot;module&quot;: &quot;esnext&quot;,
    &quot;moduleResolution&quot;: &quot;node&quot;,
    &quot;resolveJsonModule&quot;: true,
    &quot;isolatedModules&quot;: true,
    &quot;noEmit&quot;: true,
    &quot;jsx&quot;: &quot;react-jsx&quot;
  },
  &quot;include&quot;: [
    &quot;src&quot;
  ]
}
```</code></pre><hr>
<ol start="2">
<li>Storybook 추가</li>
</ol>
<ul>
<li>JS : npx -p @storybook/cli sb init</li>
<li>TS : npx -p @storybook/cli sb init --type react_scripts</li>
</ul>
<p>[프로젝트 구조]
<img src="https://velog.velcdn.com/images/choi-ju12g/post/5881e758-4793-4ec0-be26-0e4abd7ec1b6/image.png" alt=""></p>
<hr>
<p>[스토리북 실제 화면] 왼쪽 : JS, 오른쪽 : TS
<img src="https://velog.velcdn.com/images/choi-ju12g/post/b301de01-35c9-436d-9994-b36373444a12/image.png" alt=""></p>
<hr>
<h2 id="2js프로젝트-변환">2.JS프로젝트 변환</h2>
<ol>
<li>타입스크립트 추가 → package.json dependencies 추가</li>
</ol>
<pre><code class="language-bash"># package.json
npm install --save typescript @types/node @types/react @types/react-dom @types/jest

# or

yarn add typescript @types/node @types/react @types/react-dom @types/jest</code></pre>
<ol start="2">
<li>tsconfig.json 추가</li>
</ol>
<pre><code class="language-jsx">//tsconfig.json
{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;src/&quot;,
    &quot;target&quot;: &quot;es5&quot;,
    &quot;lib&quot;: [
      &quot;dom&quot;,
      &quot;dom.iterable&quot;,
      &quot;esnext&quot;
    ],
    &quot;allowJs&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;allowSyntheticDefaultImports&quot;: true,
    &quot;strict&quot;: true,
    &quot;forceConsistentCasingInFileNames&quot;: true,
    &quot;module&quot;: &quot;esnext&quot;,
    &quot;moduleResolution&quot;: &quot;node&quot;,
    &quot;resolveJsonModule&quot;: true,
    &quot;isolatedModules&quot;: true,
    &quot;noEmit&quot;: true,
    &quot;jsx&quot;: &quot;react&quot;,   //
    &quot;esModuleInterop&quot;: true
  },
  &quot;include&quot;: [
    &quot;src&quot;
  ],
  &quot;exclude&quot;: [
    &quot;**/*.test.ts&quot;,
    &quot;**/*.test.tsx&quot;
  ]
}</code></pre>
<blockquote>
<p>관련 에러 해결 </p>
</blockquote>
<ul>
<li>[디버깅] Cannot use JSX unless the &#39;--jsx&#39; flag is provided.ts(17004) 해결****
참고 링크 : <a href="https://steadily-worked.tistory.com/632">https://steadily-worked.tistory.com/632</a>
해결 법 : tsconfig.json 의 “jsx” : “react-jsx” 를 “react” 로 변경 <ul>
<li>(React) Argument type &#39;HTMLElement or null&#39; not assignable to parameter type &#39;Element or DocumentFragment&#39; 에러
해결법 1 : tsconfig.json 의 “strict” : “true” 를 “false” 로 변경 (스트릭트 모드 사용 X 방법)
해결법 2 : index.tsx 에서 </li>
<li>const* root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;)) 를
const root = ReactDOM.createRoot(
document.getElementById(&#39;root&#39;) as HTMLElement
);
로 타입을 지정</li>
</ul>
</li>
</ul>
<ol start="3">
<li><p>스토리북 컴포넌트 jsx → tsx 변환 (스토리북 빌드시 제공된 예시 Button 가지고 비교)</p>
<p> 1) Button.jsx 의 확장자를 tsx로 바꾸고, propTypes로 정의된 Prop를 Interface로 타입을 지정해준다.</p>
</li>
</ol>
<pre><code class="language-jsx">// JSX 방식
import PropTypes from &#39;prop-types&#39;;

Button.propTypes = {
  primary: PropTypes.bool,
  backgroundColor: PropTypes.string,
  size: PropTypes.oneOf([&#39;small&#39;, &#39;medium&#39;, &#39;large&#39;]),
  label: PropTypes.string.isRequired,
  onClick: PropTypes.func,
};</code></pre>
<pre><code class="language-tsx">// TSX 방식
interface ButtonProps {
  primary?: boolean;
  backgroundColor?: string;
  size?: &#39;small&#39; | &#39;medium&#39; | &#39;large&#39;;
  label: string;
  onClick?: () =&gt; void;
}</code></pre>
<ol start="4">
<li><p>jsx에서 any타입에 지정해두지 않은 속성을 받았다면, prop 설정에서 props를 옵셔널(?)를 붙여주도록 하자.( 위 타입스크립트 프롭스의 primary, size, onClick) </p>
</li>
<li><p>jsx의 defaultProps 값은 실제 컴포넌트의 프롭스에서 값을 지정해준다</p>
</li>
</ol>
<pre><code class="language-jsx">// JSX
Button.defaultProps = {
  backgroundColor: null,
  primary: false,
  size: &#39;medium&#39;,
  label: &#39;&#39;,
  onClick: undefined,
};

export const Button = (
{ primary, 
backgroundColor, 
size, 
label, 
...props }) =&gt; {
  const mode = primary ? &#39;storybook-button--primary&#39; : &#39;storybook-button--secondary&#39;;
  return (
    &lt;button
      type=&quot;button&quot;
      className={[&#39;storybook-button&#39;, `storybook-button--${size}`, mode].join(&#39; &#39;)}
      style={backgroundColor &amp;&amp; { backgroundColor }}
      {...props}
    &gt;
      {label}
    &lt;/button&gt;
  );
};</code></pre>
<pre><code class="language-tsx">// TSX
export const Button = ({
  primary = false,
  size = &#39;medium&#39;,
  backgroundColor,
  label,
  ...props
}: ButtonProps) =&gt; {
  const mode = primary ? &#39;storybook-button--primary&#39; : &#39;storybook-button--secondary&#39;;
  return (
    &lt;button
      type=&quot;button&quot;
      className={[&#39;storybook-button&#39;, `storybook-button--${size}`, mode].join(&#39; &#39;)}
      style={{ backgroundColor }}
      {...props}
    &gt;
      {label}
    &lt;/button&gt;
  );
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[1. 배경 및 변환 전략]]></title>
            <link>https://velog.io/@choi-ju12g/CRA%EB%A1%9C-%EC%83%9D%EC%84%B1%ED%95%9C-JS-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-TS%EB%A1%9C-%EB%B0%94%EA%BE%B8%EA%B8%B0</link>
            <guid>https://velog.io/@choi-ju12g/CRA%EB%A1%9C-%EC%83%9D%EC%84%B1%ED%95%9C-JS-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-TS%EB%A1%9C-%EB%B0%94%EA%BE%B8%EA%B8%B0</guid>
            <pubDate>Thu, 28 Apr 2022 13:35:26 GMT</pubDate>
            <description><![CDATA[<h2 id="1-배경-설명">1. 배경 설명</h2>
<p>내 지난 포스팅 <a href="https://velog.io/@choi-ju12g/0.-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-%EB%93%B1%EB%A1%9D">React-Storybook 0. 프로젝트 생성 및 스토리북 등록</a> 에서 나는 앞으로 내가 만들게 될 리액트 컴포넌트 들을 최대한 재사용성을 높여서 등록하고, 그것들을 모아두겠다고 말했었다. 그렇게 CRA를 이용하여 프로젝트를 만들어 봤었다. </p>
<p>그런데 여기서 미쳐 생각하지 못했던 문제가 생겼다. 바로 CRA를 할때 TS, 즉 타입스크립트를 이용하도록 생성하지 않았던 것이다.</p>
<p>그렇게 우선, 예시로 주어진 컴포넌트의 확장자를 <code>.jsx</code> 에서 <code>.tsx</code> 로 바꾸어 보았지만 역시 어림도 없었나보다.빨간줄의 에러를 상당히 많이 볼 수 있었다.</p>
<p>물론 그 당시에는 프로젝트를 생성한지 얼마 안되었기 때문에, 사실 프로젝트를 지우고 다시 TS 프로젝트로 만들면 됐다. 하지만 만약 이미 JS로 프로젝트를 만들었는데 그 이후 타입스크립트를 도입하고 싶을 떄, 할 수 있어야 하기에 방법을 찾아 한번 직접 바꿔보려고 한다. </p>
<h2 id="2-영문-포스팅-글-번역">2. 영문 포스팅 글 번역</h2>
<p>원본 링크 : <a href="https://medium.com/opensanca/migrating-from-js-to-ts-cra-b5f679086c5a">https://medium.com/opensanca/migrating-from-js-to-ts-cra-b5f679086c5a</a></p>
<p>참고 링크 : <a href="https://create-react-app.dev/docs/adding-typescript/">https://create-react-app.dev/docs/adding-typescript/</a></p>
<h2 id="javascript를-typescript로-바꾸기cra">Javascript를 Typescript로 바꾸기(CRA)</h2>
<p>자바스크립트로 짜여진 CRA 프로젝트를 타입스크립트로 바꾸는 방법에 대해서 설명하겠다. </p>
<p><a href="https://create-react-app.dev/docs/adding-typescript/">**CRA 공식 문서</a>** 를 참고 해도 되지만, 이 글을 읽으면 더 깊은 학습을 할 수 있을 것을 약속한다.</p>
<p>당신의 타입스크립트로의 모든 코드베이스를 바꾸는 훌륭한 전략을 소개할 것인데, </p>
<p>그것을 위해 3가지 스텝으로 분류했다.</p>
<ol>
<li>어떻게 스스로 타입스크립트를 구성하고, linter/prettier 등을 고치고 jest를 통합시킬 것인가?</li>
<li>간단한 리팩토링을 통해 최대한 머리아프지 않게 현재 당신의 코드를 타입스크립트로 바꾸는 전략</li>
<li>내가 지금까지 공부했고, 나를 도왔고, 내가 범했던 다양한 실수들 그리고 거기서 나의 문제를 해결해줬던  config들 소개</li>
</ol>
<h3 id="configs구성">Configs(구성)</h3>
<p>첫째, 어플리케이션 작동에 문제가 없게 하는데 초점을 맞출 것이다. </p>
<p>둘째, 코딩 생활을 더 쉽게 만들어주는 linter랑 prettier를 고칠 것이다.</p>
<p>셋째, JS와 TS에서 모두 작동하는 테스트 코드를 구성할 것이다. </p>
<h3 id="app">App</h3>
<p>이 설정은 가장 쉽다. CRA 구성은 JS,TS를 동시에 모두 지원하기에 너는 단지 <code>타입스크립트 컴파일러</code>를 구성하면 된다. 여기서 생각해봐야할 점은, 이전에 구성한 적 없는 <code>tsconfig.json</code> 파일을 어디서 가져오는 지에 대한 것이다. </p>
<p>그래서 타입스크립트 템플릿으로 CRA를 사용하여 새로운 프로젝트를 만든 후 생성되는 <code>tsconfig.json</code>을 이용하고 몇 가지 설정을 추가할 것이다. (ex <code>baseUrl</code> (<code>NODE_PATH</code>  바꾸기 위함)).</p>
<p>타입스크립트 CRA 프로젝트를 너의 어플에 적용하기 위해서는 <code>TS Dependencies</code>를 설치하고 너의 <code>app&#39;s root</code> 에 <code>tsconfig.json</code> 을 추가해라. </p>
<pre><code class="language-bash">yarn add typescript @types/react @types/react-dom @types/node</code></pre>
<pre><code class="language-json">//tsconfig.json
{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;src/&quot;,
    &quot;target&quot;: &quot;es5&quot;,
    &quot;lib&quot;: [
      &quot;dom&quot;,
      &quot;dom.iterable&quot;,
      &quot;esnext&quot;
    ],
    &quot;allowJs&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;allowSyntheticDefaultImports&quot;: true,
    &quot;strict&quot;: true,
    &quot;forceConsistentCasingInFileNames&quot;: true,
    &quot;module&quot;: &quot;esnext&quot;,
    &quot;moduleResolution&quot;: &quot;node&quot;,
    &quot;resolveJsonModule&quot;: true,
    &quot;isolatedModules&quot;: true,
    &quot;noEmit&quot;: true,
    &quot;jsx&quot;: &quot;react&quot;,
    &quot;esModuleInterop&quot;: true
  },
  &quot;include&quot;: [
    &quot;src&quot;
  ],
  &quot;exclude&quot;: [
    &quot;**/*.test.ts&quot;,
    &quot;**/*.test.tsx&quot;
  ]
}</code></pre>
<h3 id="linterprettier">Linter/Prettier</h3>
<p>linter와 prettier 에 대해 말하자면 입아프자. 나는 타입스크립트에서 작동하도록 내  eslint 설정을 업데이트 했다. 만약 아직 linter나 prettier를 사용하지 않는다면, 아무리 작은 사이드 프로젝트라 할 지 라도 당장 너가 하던 것을 멈추고 추가할 것을 권한다. </p>
<p>아래의  dependencies를 eslint나 prettier 에 추가하고,  <code>.eslintrc.js</code> (.ts 파일 확장자를 인식하기 위한 파일) 을 업데이트하고 타입스크립트 플러그인을 사용해라. </p>
<pre><code class="language-bash">yarn @typescript-eslint/eslint-plugin @typescript-eslint/parser</code></pre>
<pre><code class="language-jsx">// .eslintrc.js
module.exports = {
  plugins: [&#39;prettier&#39;, &#39;react&#39;, &#39;@typescript-eslint&#39;],
  extends: [
    &#39;./node_modules/kcd-scripts/eslint.js&#39;,
    &#39;plugin:@typescript-eslint/eslint-recommended&#39;,
    &#39;plugin:@typescript-eslint/recommended&#39;,
    &#39;prettier/@typescript-eslint&#39;,
  ],
  rules: {
    &#39;prettier/prettier&#39;: &#39;error&#39;,
    &#39;react/jsx-filename-extension&#39;: [
      1,
      { extensions: [&#39;.js&#39;, &#39;.jsx&#39;, &#39;.tsx&#39;] },
    ],
  },
  settings: {
    &#39;import/resolver&#39;: {
      node: {
        extensions: [&#39;.js&#39;, &#39;.jsx&#39;, &#39;.ts&#39;, &#39;.tsx&#39;],
        paths: [&#39;src&#39;],
      },
    },
  },
}</code></pre>
<pre><code class="language-jsx">//prettier.config.js
const prettierConfig = require(&#39;kcd-scripts/prettier&#39;);

module.exports = Object.assign(prettierConfig, {
  bracketSpacing: true,
  endOfLine: &#39;lf&#39;,
});</code></pre>
<h3 id="tests">Tests</h3>
<p>너의 모든 테스트와 컴포넌트들은 JS로 작성되어 있다.  jest는 컴포넌트가 JS로 작성되었든 TS로 작성되었든 신경쓰지 않고 적용된다. 아무것도 할 필요가 없다. </p>
<p>그런데 TS 컴포넌트를 추가하고 나면, TS 테스트 코드를 작성하고 싶을 수 있다. 이 경우에는 TS로 작성된 테스트를 변환하도록 <code>jest.config.js</code> 를 손봐야 합니다. jest에 dependencies를 추가하고 업데이트 해라.</p>
<pre><code class="language-bash">yarn add ts-jest @types/jest</code></pre>
<pre><code class="language-jsx">//jest.config.js
const { jest: jestConfig } = require(&#39;kcd-scripts/config&#39;)

module.exports = Object.assign(jestConfig, {
  globalSetup: &#39;&lt;rootDir&gt;/src/setupJest&#39;,
  modulePaths: [&#39;&lt;rootDir&gt;/src/&#39;],

  transform: {
    ...jestConfig.transform,
    &#39;\\.tsx?$&#39;: &#39;ts-jest&#39;,
    &#39;^.+\\.jsx?$&#39;: &#39;babel-jest&#39;,
  },

  globals: {
    &#39;ts-jest&#39;: {
      tsConfig: &#39;tsconfig.json&#39;,
    },
  },
})</code></pre>
<h3 id="변환-전략">변환 전략</h3>
<p>TS와 JS 둘다 작동가능하게 하는 하이브리드 앱을 구성하는 것은 쉽다. 전체 어플리케이션을 바꾸는 것은 엄천난 도전이 될 것이다. 나는 내 어플리케이션을 변환하는데 도움을 준 4가지 전략을 소개한다. </p>
<p>첫째, 작은 것(leaves) 부터 변환을 시작해라. </p>
<p>너의 어플리케이션은 마치 나무처럼 동작 할 것이다 .나무 처럼 동작한다면 가능한 다른 내부적인 의존성이 없는 마지막 컴포넌트 부터 재작성 하기 시작해라. 그렇게 하면 충돌이 적어질 것이고 모든 leaf가 변환이 되었다면 그 모든 부모 컴포넌트 들을 변환해서 루트까지 변환해 가라. </p>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/cf24d6d7-b123-48e9-bab5-fc42f6420fef/image.png" alt=""></p>
<p>둘째, 너의 테스트 코드 부터 재작성해라. 
너의 코드 베이스의 각각의 테스트 코드 확장자를 .js 에서 .ts로 바꾸고 생기는 문제들을 먼저 고쳐라. 그렇게 고쳐진 테스트 코드들은 타입스크립트로 재작성 될 너의 코드들의 수많은 비준수 사항, 오류 등을 잡아 낼 수 있을 것이다. </p>
<p>셋째, 새로운 기능부터 시작해라.</p>
<p>오직 새로운 기능만을 TS로 작성하면 새로운 기능을 테스트하기 위한 새로운 코드와 새로운 루틴을 작성하면 사용이 더 쉬워진다. 새로운 기능은 당신이 예측하기 쉽고, 컨트롤 가능한 영역의 영향을 노출한다.</p>
<p>넷째, 필요할 때 마다 변환해라.</p>
<p>사실 이 것은 가장 어려울 수 있지만 가장 현실적이다. 손 볼 필요가 있는 특정한 파일이 있다면 그 파일만 우선적으로 변환해라. 만약 너가 그 파일에 대한 테스트 코드가 있다면( 사실 이것이 반드시 있기를 바란다.), 그 테스트 코드를 먼저 수정하고 그 후에 너의 컴포넌트를 변환하기를 추천한다. 그렇게 새로운 제품 요청사항이나 디자인 변화 등의 변경사항에 너의 컴포넌트를 수정 해나가라.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[배경 지식] Storybook 시작하기(feat. React + TypeScript)]]></title>
            <link>https://velog.io/@choi-ju12g/0.-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-%EB%93%B1%EB%A1%9D</link>
            <guid>https://velog.io/@choi-ju12g/0.-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B6%81-%EB%93%B1%EB%A1%9D</guid>
            <pubDate>Thu, 28 Apr 2022 12:33:40 GMT</pubDate>
            <description><![CDATA[<p>ICT 인턴에서 Front-end 개발자로 일을 하면서 여러가지 공부를 하고 있는데, 여기서 알게 된 것이 스토리북 이라는 것이다.</p>
<h2 id="스토리북-소개">스토리북 소개</h2>
<blockquote>
<p>이 가이드는 전문 개발자들이 디자인 시스템을 배울 수 있는 방법을 다루고 있습니다. 자바스크립트(JavaScript), 깃(Git), 그리고 지속적인 통합 환경(Continuous Integration, CI)에 대해 어느 정도 경험을 하고 읽기를 권해드립니다. 또한 스토리 작성이나 설정 파일 수정 등의 스토리북의 기본 사용법을 알고 있어야 합니다(스토리북 소개를 참고하세요).
링크 : <a href="https://storybook.js.org/tutorials/design-systems-for-developers/react/ko/introduction/">https://storybook.js.org/tutorials/design-systems-for-developers/react/ko/introduction/</a></p>
</blockquote>
<p>쉽게 이해하자면 우리는 리액트로 특히 함수형 컴포넌트로 뷰를 구성할 때, 재사용성을 고려하면서 만들게 된다. 함수형 컴포넌트 자체가 새로운 뷰를 만들 때, 이미 있는 컴포넌트들의 재조립을 해서 뷰를 구성하고 없는 부분을 추가로 구성하는 방식을 지향하고 있기 때문이다. 이런 상황에서 쉽게 컴포넌트를 관리할 툴이 필요했던 것이다. 그렇게 스토리북은 큰 노력을 들이지 않고 우리의 <code>React Components</code>를 등록하고 확인할 수 있도록 도아줄 것이다. </p>
<p>아래 사진은 스토리북 공식 홈페이지에서 말하는 사용하는 도구들이다.
<img src="https://velog.velcdn.com/images/choi-ju12g/post/dcc7219a-7cf1-4a07-84bc-4235d17a5718/image.png" alt=""></p>
<h2 id="프로젝트-생성-및-스토리북-등록">프로젝트 생성 및 스토리북 등록</h2>
<p>참고 링크 : <a href="https://storybook.js.org/tutorials/intro-to-storybook/react/ko/get-started/">https://storybook.js.org/tutorials/intro-to-storybook/react/ko/get-started/</a></p>
<ol>
<li>React 프로젝트 생성(CRA)
우선 앞으로 만들 컴포넌트 들을 모아둘 프로젝트를 CRA를 이용하여 생성하겠다</li>
</ol>
<pre><code class="language-bash">$ npm create-react-app &lt;프로젝트 디렉토리 명&gt;
// npm create-react-app react-storybook-component

$ cd &lt;프로젝트 디렉토리 명&gt;
// cd react-storybook-component</code></pre>
<ol start="2">
<li>스토리북 설치
다음은 스토리북 CLI를 사용해서 프로젝트에 스토리북을 설치한다.</li>
</ol>
<pre><code class="language-bash">$ npx -p @storybook/cli sb init</code></pre>
<p>참고로 npx는 의존성 등의 문제를 잡아주는 노트 패키지를 설치시켜주는 노드 패키지라고 한다. 만약 깔려있지 않아 npx를 사용할 수 없다면 아래의 명령어를 통해 npx를 우선 설치해주자.</p>
<pre><code class="language-bash">npm install npx -g</code></pre>
<p><code>package.json</code> 파일에 스크립트와 dveDependencies에 스토리북 이름이 붙은 코드들이 추가된 것을 확인할 수 있다면 정상적으로 설치가 된것이다. 
<img src="https://velog.velcdn.com/images/choi-ju12g/post/ea3f3645-2b40-41f7-91f2-b5f8e7e5bf16/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/choi-ju12g/post/7dd01d59-419f-4cf2-b14a-6c4e4477082e/image.png" alt=""></p>
<ol start="3">
<li>스토리북 구동
이렇게 설치한 스토리북을 구동시켜보면 스토리북에서 기본으로 제공하는 컴포넌트들을 확인할 수 있다. <pre><code class="language-bash">$ npm run storybook</code></pre>
<img src="https://velog.velcdn.com/images/choi-ju12g/post/25368187-b868-49a8-a6c5-41e854e8df43/image.png" alt=""></li>
</ol>
<p>이렇게 한번 컴포넌트를 등록을 해주고 나면(물론 처음 컴포넌트를 만들 때는 별도의 .stories.jsx 라는 파일을 만들어주어야 하긴 한다) 컴포넌트의 프롭스 등을 바꿔보며 손쉽게 수정, 생성할 수 있다. </p>
<p>그래서 앞으로 사이드 프로젝트나 일을 하면서 내가 개발하게  된 컴포넌트들을 여기다 모아놓고 다른 프로젝트를 할 때 가능한 재상용이 용이하게 하여 이용해보려고 한다.</p>
<blockquote>
<p><a href="https://github.com/Choi-Ju12g/react-storybook-component">https://github.com/Choi-Ju12g/react-storybook-component</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 참조형 및 원시형 타입]]></title>
            <link>https://velog.io/@choi-ju12g/JavaScript-%EC%B0%B8%EC%A1%B0%ED%98%95-%EB%B0%8F-%EC%9B%90%EC%8B%9C%ED%98%95-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@choi-ju12g/JavaScript-%EC%B0%B8%EC%A1%B0%ED%98%95-%EB%B0%8F-%EC%9B%90%EC%8B%9C%ED%98%95-%ED%83%80%EC%9E%85</guid>
            <pubDate>Sun, 27 Mar 2022 02:47:30 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolorblue🫵-데이터-타입span"><span style="color:blue">🫵 데이터 타입</span></h2>
<p>JavaScript는 두 가지 <code>원시 타입(Primitive Type)</code>과 <code>참조타입(Reference Type)</code>이 있다.</p>
<p>원시 타입은 쉽게 말해, 정수, 실수, 문자, 논리 리터럴등의 실제 데이터 값을 저장하는 타입이고,</p>
<p>참조 타입은 객체(Object)의 번지를 참조(주소를 저장)하는 타입으로 메모리 번지 값을 통해 객체를 참조하는 타입이다.</p>
<h2 id="span-stylecolororange👋🏻-원시형-타입span"><span style="color:orange">👋🏻 원시형 타입</span></h2>
<p>원시형 타입은 복사를 할 때 값 자체를 복사한다.</p>
<pre><code class="language-javascript">const number = 1;
const num2 = number;

console.log(num2);
&gt;&gt;&gt; 1</code></pre>
<p>위와 같은 코드에서 num2 는 1이라는 값 자체를 받아온 하나의 정수 데이터 타입이다. 또 값 자체를 받아왔기 때문에 아래와 같이 할당 이후에 <code>number</code>의 값을 바꿔도 <code>num2</code>의 값은 바뀌지 않는다. </p>
<pre><code class="language-javascript">let number = 1;
const num2 = number;
number = 2;

console.log(num2);
&gt;&gt;&gt; 1</code></pre>
<h2 id="span-stylecolororange✍🏼-참조형-타입-span"><span style="color:orange">✍🏼 참조형 타입 </span></h2>
<p>객체와 배열은 참조형 타입이다.</p>
<pre><code class="language-javascript">const Person = {
  name: &quot;max&quot;
};

const person2 = Person;

console.log(person2);
&gt;&gt;&gt; [object Object] {
          name:&quot;max&quot;
    }</code></pre>
<p>여기서 Person 매모리에 객체가 저장되고, person2는 메모리에 포인터가 저장된다. 그래서 <code>const person2 = Person;</code> 구문 이후에 Person의 이름을 바꾸고 똑같이 콘솔에 값을 찍어보면 참조형 타입에 대한 이해 없이 바라봤을 때와 다른 결과값을 볼 수 있다.</p>
<pre><code class="language-javascript">const Person = {
  name: &quot;max&quot;
};

const person2 = Person;
Person.name = &quot;min&quot;;

console.log(person2);
&gt;&gt;&gt; [object Object] {
          name:&quot;min&quot;
    }</code></pre>
<p>분명 값을 넘겨준 이후에 값을 바꾸었지만 값은 똑같이 변했다. 이것이 참조했다고 하는 이유이다. 단지 포인터를 복사해서 새로운 객체가 가르키도록 했기 때문이다. 이를 이해하는 것은 중요하다. 다른 곳에서 같은 객체를 참조하고 있다가 데이터 변경이 일어났을 때 예상치 못한 효과가 발생할 수 있기 때문이다. 
그렇다면 이것을 완전히 새로운 객체로 받으려면 어떻게 해야할까?</p>
<p>바로 객체를 만들고 다른 인스턴스의 속성을 전개하면 된다. </p>
<pre><code class="language-javascript">const Person = {
  name: &quot;max&quot;
};

const Person2 = {
  ...Person
}

Person.name = &quot;min&quot;;

console.log(Person2);
&gt;&gt;&gt; [object Object] {
          name:&quot;max&quot;
    }</code></pre>
<p>다음과 같이 하면 진짜로 우리가 원하는 객체의 복사가 일어난다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[남도학숙 식단 어플] 1. 등장 배경 및 기능 정의]]></title>
            <link>https://velog.io/@choi-ju12g/%EB%82%A8%EB%8F%84%ED%95%99%EC%88%99-%EC%8B%9D%EB%8B%A8-%EC%96%B4%ED%94%8C-1.-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD-%EB%B0%8F-%EA%B8%B0%EB%8A%A5-%EC%A0%95%EC%9D%98</link>
            <guid>https://velog.io/@choi-ju12g/%EB%82%A8%EB%8F%84%ED%95%99%EC%88%99-%EC%8B%9D%EB%8B%A8-%EC%96%B4%ED%94%8C-1.-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD-%EB%B0%8F-%EA%B8%B0%EB%8A%A5-%EC%A0%95%EC%9D%98</guid>
            <pubDate>Thu, 24 Mar 2022 13:22:39 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-orange🙏-등장-배경span"><span style="color: orange">🙏 등장 배경</span></h2>
<p>나는 현재 남도 학숙(지역 학사)에 살고 있다. 그렇게 아침과 저녁을 보통 학숙에서 나오는 밥을 먹는데, 매번 식사 메뉴를 확인하기 위해서는 아래와 같은 과정을 거쳐야 했다.</p>
<ol>
<li>핸드폰 크롬에 남도학숙 검색.</li>
<li>남도학숙 사이트에서 남도학숙 학사관리 시스템 입장.</li>
<li>로그인</li>
<li>학생 생활 지원 -&gt; 식단표 -&gt; 식단 확인</li>
</ol>
<p>이 일련의 과정이 우선 귀찮았다 . 그리고 학사는 아무래도 많은 학생들의 밥을 해야해서 그런지 정말 맛있어보이는 메뉴인데 입에 안맞고, 기대도 안했는데 너무 맛있었던 경험이 꽤 있었다. 그래서 남도학숙 식단 어플을 만들어서 쉽게 식단 메뉴를 확인하고 나만의 별점을 매겨서 내가 맛있게 먹었던 메뉴가 나오는 날에는 별도의 알림을 보내 줄 수 도 있는 어플을 만들어 보려한다. 참고로 어느정도 만들고 나면 남도학숙에 무료로 뿌려서 피드백도 받아보고 가능하면 서비스를 해보려고 한다.</p>
<h2 id="span-stylecolor-blue🙏-기능-정의"><span style="color: blue">🙏 기능 정의</h2>
</span> 

<ul>
<li><p>어떤 어플?</p>
<ul>
<li>React와 Node.js를 이용한 웹 뷰 어플리케이션</li>
</ul>
</li>
<li><p>기능</p>
<ul>
<li>로그인(남도학숙 아이디 비번)</li>
<li>로그인 유지 혹은 자동 로그인(쿠키/세션스토리지 사용)</li>
<li>React의 SPA 화면</li>
<li>남도학숙 식단표 크롤링</li>
</ul>
</li>
<li><p>배포</p>
<ul>
<li>Github Pages 같은 무료 호스팅 사이트를 이용 예정.</li>
</ul>
</li>
<li><p>기술 스택</p>
<ul>
<li>React(함수형 컴포넌트 방식)</li>
<li>css in js 방식으로 styled-components 생각</li>
<li>node.js</li>
</ul>
</li>
</ul>
<h2 id="span-stylecolor-f09999👏-계획span"><span style="color: #f09999">👏 계획</span></h2>
<p>이 프로젝트는 오래 걸릴 것이다. 기능이 많아서도 내가 투자할 시간이 없어서도 아니다. 내가 사용할 기술 하나하나에 이유와 목적을 가지고 시작할 것이고, 그것을 다 기술할 것이며 해결해 나가보려고 한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[무작정 포트폴리오] 4. 깃헙 페이지에서 동작하는 SPA(Vanilla JS) - 잠정 중단]]></title>
            <link>https://velog.io/@choi-ju12g/%EB%AC%B4%EC%9E%91%EC%A0%95-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-4.-%EA%B9%83%ED%97%99-%ED%8E%98%EC%9D%B4%EC%A7%80%EC%97%90%EC%84%9C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-SPAVanilla-JS</link>
            <guid>https://velog.io/@choi-ju12g/%EB%AC%B4%EC%9E%91%EC%A0%95-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-4.-%EA%B9%83%ED%97%99-%ED%8E%98%EC%9D%B4%EC%A7%80%EC%97%90%EC%84%9C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-SPAVanilla-JS</guid>
            <pubDate>Tue, 15 Mar 2022 03:57:50 GMT</pubDate>
            <description><![CDATA[<h2 id="미리보기">[미리보기]</h2>
<p><img src="https://images.velog.io/images/choi-ju12g/post/5c208b69-a311-4ece-a14f-1ab2a5574776/SPA-animation-in-GH_Pages.gif" alt=""></p>
<p>참고자료 :  <a href="https://github.com/MichaelCurrin/single-page-app-vanilla-js">SPA without server(깃헙 페이지 동작 가능) 하게 해준 github repo</a></p>
<hr>
<h2 id="span-stylecolororange문제점span"><span style="color:orange">문제점</span></h2>
<p>지난번 <a href="https://velog.io/@choi-ju12g/%EB%AC%B4%EC%9E%91%EC%A0%95-%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-3.-SPA-%EA%B5%AC%ED%98%84Vanilla-JS">[무작정 포트폴리오] 3. SPA 구현(Vanilla JS)</a>에서 SPA 구현에 성공한 듯 보였다. </p>
<p>하지만 이를 위해서 예제로 <code>express</code>라는 서버를 사용하였고, 서버의 역할은 새로운 url의 요청이 들어올 때, 사용자가 지정한 형식으로  url을 변경하여 <code>&lt;a&gt;</code>의 href 속성값을 맞춰주며 SPA를 구현했기에 깃헙 페이지나 평소에 사용하던 vscode의 extension인 &#39;live server&#39;를 이용하게 되면 원하는 동작이 실행되지 않았다. </p>
<h2 id="span-stylecolorblue해결법span"><span style="color:blue">해결법</span></h2>
<p>해결법은 지난 포스트의 server.js(express)가 해주었던 url 변경(라우팅 조작)을 Github Pages의 동작에 맞춰주면 된다.
(ex.<code>~/post</code>  -&gt; <code>~/&lt;repo 이름&gt;/post</code> 와 같은 형식으로 변경해야한다.)</p>
<blockquote>
<p> 📂portfolio(프로젝트 폴더)
  |
  |---- 📂static
  |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|---- 📂 css
  |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|---- 📂 js
  |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| ---- 📂 components
  |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| ---- <span style="color:red">index.js(수정)</span>
   |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| ---- <span style="color:red">lib.js(생성)</span>
  |---- index.html
  |---- <span style="color:red">server.js(삭제)</span></p>
</blockquote>
<h3 id="🎈-libjs">🎈 lib.js</h3>
<pre><code class="language-javascript">const PROJECT_TITLE = &#39;&lt;repo 이름&gt;&#39;;

export function u(url) {
  return `/${PROJECT_TITLE}${url}`;
}</code></pre>
<p>lib.js는 간단하다. Github Pages의 기본 설정을 보면 레포지토리 이름이 마지막에 위치하게 된다. <code>~/&lt;repo 이름&gt;</code>의 형식으로 말이다. 그래서 레포 이름을 상수값으로 설정해두고, 인자를 그 뒤에 붙인 문자열을 반환하는 함수를 작성한다. </p>
<h3 id="👑-indexjs">👑 index.js</h3>
<p>이는 정규 표현식(Regrex)에 대한 이해가 필요한 부분이긴 하나 목적은 확실하다. 내가 버튼에 연결해둔 링크 뒤의(href=&quot;&lt;이름&gt;&quot;) &lt;이름&gt; 부분을 주소(url) 끝부분에     <code>~/&lt;이름&gt;</code> 으로 만드는 것이 목적이다. 그리고 문자열 매치 함수 역시 커스텀으로 손봐 이용 할 수 있게 되었다.</p>
<h2 id="span-stylecolorred한계span"><span style="color:red">한계</span></h2>
<p>당연히 수동으로 url의 결과값을 예상하여 수정하는 방식이었기에, 한계가 존재한다.
    1. 페이지 새로고침
    2. 유효한 경로값 입력
  이를 해결하기 위해서는 매번 첫 페이지로 돌아가야 한다. 이에 대한 해결은 local static server(in SPA mode)나 Netlify config로 할 수 있다곤 하는데 당장은 고려하지 않을 예정이다.
  이유는 내가 만들 포트폴리오 사이트는 정적 사이트이며 사용자와 인터랙션 하는 일이 거의 예상 가능한 범위 내이기 때문에 당장은 신경 쓰지 않으려고 한다.</p>
<h2 id="잠정-중단">잠정 중단</h2>
<p> 3월부터 ICT인턴십을 하게 되면서 회사에서는 바닐라 타입스크립트와 리액트를 사용하게 되었다. 물론 그것이 내가 자바스크립트를 이용한 포트폴리오 제작에 추가적인 공부를 할 시간이 없다는 이유는 아니다. 단지 현재 회사에서 더 많은 것을 얻고 싶다. 프론트 개발자로서는 정말 많은 일과 기능을 구현해 볼 수 있는 회사에 인턴을 4달동안 할 수 있기에 더욱 집중해보려고 한다. 그래서 잠시 포트폴리오 사이트 제작은 멈추고(대신 포트폴리오 정리는 노션을 이용해서 내용을 미리 채워 두겠다), 리액트를 이용한 개인 프로젝트를 진행 해보려 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ICT 인턴십] 3. 탈락 후 추가 합격!!]]></title>
            <link>https://velog.io/@choi-ju12g/ICT-%EC%9D%B8%ED%84%B4%EC%8B%AD-3.-%ED%83%88%EB%9D%BD-%ED%9B%84-%EC%B6%94%EA%B0%80-%ED%95%A9%EA%B2%A9</link>
            <guid>https://velog.io/@choi-ju12g/ICT-%EC%9D%B8%ED%84%B4%EC%8B%AD-3.-%ED%83%88%EB%9D%BD-%ED%9B%84-%EC%B6%94%EA%B0%80-%ED%95%A9%EA%B2%A9</guid>
            <pubDate>Tue, 22 Feb 2022 14:08:57 GMT</pubDate>
            <description><![CDATA[<p>지난 <a href="https://velog.io/@choi-ju12g/ICT-%EC%9D%B8%ED%84%B4%EC%8B%AD-2.-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B0%8F-%EB%A9%B4%EC%A0%91">[ICT 인턴십] 2. 코딩테스트 및 면접</a>에서 최종 탈락을 하면 글을 마무리 하겠다고 했다. 그러나 제목에서 이미 알 수 있듯이 추가합격으로 ICT 인턴십에 합류하게 되었다. </p>
<h2 id="span-stylecolororange📢-첫-결과-화면span"><span style="color:orange">📢 첫 결과 화면</span></h2>
<p><img src="https://images.velog.io/images/choi-ju12g/post/6461d097-85a1-4eab-9d74-5bbd149e7f6d/ict%EC%9D%B8%ED%84%B4%EC%8B%AD%20%EA%B2%B0%EA%B3%BC_%EB%A7%88%ED%82%B9.png" alt="">
2.19 ~ 2.21 까지가 합격한 학생이 인턴 기업을 확정하는 기간이었다. 
나는 안타깝게도 1기업 서류 탈락, 2기업 면접 탈락이라는 결과를 맞이해야 했다. 내가 많이 부족한 것은 당연히 알고 있었고, 사실 붙는게 매우 어려울 것이라 생각했어서 그렇게 아쉽진 않았다. 정말 진심으로 말이다. </p>
<p>그렇게 복학을 준비하며 공부를 이어나가고 있었다. 그런데 2.21 오후 4시 경 C기업에서 전화를 한통 받게 된다. </p>
<p>첫 마디가 &quot;예비 순위가 높으셔서, 몇 가지 더 확인하고 싶어서 연락드렸습니다&quot; 였다. 질문은 졸업까지 남은 학기와 원하는 개발 파트 재확인이었다. 그렇게 전화를 받고나니 공부가 손에 안잡혔다. 괜히 붙을 것만 같고 그랬다. 추가 합격 기간은 2.22 부터였기에 내일즈음에나 결과가 나오겠거니 하고 있었는데, 전화 통화 2시간 뒤인 오후 6시경 추가합격 공지를 받았다. </p>
<h2 id="span-stylecolorred-🎁-최종-결과"><span style="color:red"> 🎁 최종 결과</h2>
<p>  <img src="https://images.velog.io/images/choi-ju12g/post/36415b8d-23b0-4611-8bae-9f216b06862a/image.png" alt="">
  너무도 감사하게 인턴의 기회를 얻었다. 그리고 붙은 기업은 3개 중 내가 가장 가고 싶어 했던 기업이다. 인턴으로 하게 될 업무가 굉장히 명확하게 나와있었고, 웹 프론트엔드 개발자를 뽑는 기업이었으며, React라는 프레임워크를 사용한다. 
또 살게 될 학사에서 편도 40분 정도의 거리였으며, 점심 식사비 지원을 시작으로 복지는 유연근무제(1<del>3시간), 재택근무(주 1</del>3회), 개발 장비 제공, 복장 완전 자율, 외부 교육 및 기술 세미나 적극 지원, 도서 구입 지원 등 처음 회사에서 일해보게 될 나한테는 하나하나 너무 매력적이고 많았다.</p>
<p>  이번 기회를 정말 잘 살려서 어떻게든 개발자로서의 능력을 키우고 성장할 것이다. 저번 글에서 말했던 것처럼 이제는 진짜 인턴 일지 같은 형식으로 내가 만들고 공부한 것 등을 정리해서 올릴 수 있도록 하겠다.</p>
]]></description>
        </item>
    </channel>
</rss>