<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sweet_pumpkin.log</title>
        <link>https://velog.io/</link>
        <description>단오해서 단호박!</description>
        <lastBuildDate>Sun, 26 Feb 2023 04:20:26 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sweet_pumpkin.log</title>
            <url>https://velog.velcdn.com/images/sweet_pumpkin/profile/8dbd56d2-ca14-4de9-ba9c-82d8093450f9/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sweet_pumpkin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sweet_pumpkin" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Typescript 환경설정]]></title>
            <link>https://velog.io/@sweet_pumpkin/Typescript-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@sweet_pumpkin/Typescript-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sun, 26 Feb 2023 04:20:26 GMT</pubDate>
            <description><![CDATA[<h4 id="span-stylecolor-5fc69a1-nodejs-환경설정span"><span style="color: #5fc69a">1. node.js 환경설정</span></h4>
<pre><code>$ npm init -y</code></pre><h4 id="span-stylecolor-5fc69a2-typescript-설치span"><span style="color: #5fc69a">2. typescript 설치</span></h4>
<pre><code>npm i -D typescript</code></pre><h4 id="span-stylecolor-5fc69a3-tsconfigjson-설정span"><span style="color: #5fc69a">3. tsconfig.json 설정</span></h4>
<ul>
<li><p>terminal</p>
<pre><code>touch tsconfig.json</code></pre></li>
<li><p>tsconfig.json</p>
<pre><code>{
&quot;include&quot; : [&quot;src&quot;] // src 폴더의 모든 파일을 확인한다는 뜻.
&quot;compilerOptions&quot;: {
  &quot;outDir&quot;: &quot;build&quot;, // 자바스트립트 파일이 생성될 디렉터리를 지정.
  &quot;rootDir&quot;: &quot;./&quot;, // 루트경로 바꾸기 (js 파일 아웃풋 경로에 영향줌)
  &quot;target&quot;: &quot;ES6&quot;, // 어떤 버전의 자바스크립트로 컴파일 할 것인지.
  &quot;lib&quot;: [&quot;ES6&quot;, &quot;DOM&quot;], // 합쳐진 라이브러리에서 정의된 파일을 특정 =&gt; 자바스크립트 코드가 어디에 동작할 것인지 작성.
  &quot;strict&quot;: true, // 엄격하게 검사.
  &quot;noImplicitAny&quot;: true, // any 타입 금지
  &quot;noImplicitThis&quot;: true, // any 타입인 this 키워드 금지
  &quot;allowJs&quot; : true, // js 파일들 ts에서 import해서 쓸 수 있는지 
  &quot;checkJs&quot;: true, // js 파일에서도 에러체크 할 것인지
  &quot;esModuleInterop&quot;: true, // ES6 모듈 사양을 준수하여 CommonJS 모듈을 가져올 수 있다.
  &quot;module&quot;: &quot;CommonJS&quot;, //무슨 import 문법 쓸건지 &#39;commonjs&#39;, &#39;amd&#39;, &#39;es2015&#39;, &#39;esnext&#39;
  &quot;jsx&quot;: &quot;preserve&quot;, // tsx 파일을 jsx로 어떻게 컴파일할 것인지 &#39;preserve&#39;, &#39;react-native&#39;, &#39;react&#39;
  &quot;declaration&quot;: true, // 컴파일시 .d.ts 파일도 자동으로 함께생성 (현재쓰는 모든 타입이 정의된 파일)
  &quot;outFile&quot;: &quot;./&quot;, // 모든 ts파일을 js파일 하나로 컴파일해줌 (module이 none, amd, system일 때만 가능)
  &quot;removeComments&quot;: true, // 컴파일시 주석제거 
  &quot;noUnusedLocals&quot;: true, // 지역변수 전부 사용해야 함
  &quot;noUnusedParameters&quot;: true, // 파라미터 전부 사용해야 함
  &quot;noImplicitReturns&quot;: true, // 모든 함수에 return이 포함되어야 함 
}
}</code></pre></li>
<li><p>package.json</p>
<pre><code>&quot;scripts&quot;: {
  &quot;build&quot; : &quot;tsc&quot;,
  &quot;start&quot;: &quot;node build/index.js&quot;
}</code></pre><h4 id="span-stylecolor-5fc69a4-ts-node--nodemon-설치span"><span style="color: #5fc69a">4. ts-node &amp; nodemon 설치</span></h4>
<blockquote>
<p>ts-node : 빌드 없이 타입스크립트를 실행
nodemon : 서버를 재실행하지 않고, 자동으로 커멘드를 실행</p>
</blockquote>
</li>
<li><p>terminal</p>
<pre><code>$ npm i -D ts-node
$ npm i nodemon</code></pre></li>
<li><p>package.json</p>
<pre><code>&quot;scripts&quot;: {
  &quot;build&quot;: &quot;tsc&quot;,
  &quot;dev&quot;: &quot;ts-node src/index.js&quot;,
  &quot;start&quot;: &quot;node build/index.js&quot;
}</code></pre></li>
</ul>
<h4 id="span-stylecolor-5fc69a5-타입-정의span"><span style="color: #5fc69a">5. 타입 정의</span></h4>
<pre><code>$ npm i -D @types/styled-components
$ npm i -D @types/react-router-dom</code></pre><hr>
<h4 id="span-stylecolor-5fc69a참고-링크span"><span style="color: #5fc69a">참고 링크</span></h4>
<p><a href="https://www.typescriptlang.org/tsconfig">https://www.typescriptlang.org/tsconfig</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[webpack template 만들기]]></title>
            <link>https://velog.io/@sweet_pumpkin/webpack-template-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/webpack-template-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sat, 21 Jan 2023 23:50:01 GMT</pubDate>
            <description><![CDATA[<h3 id="1-초기-설정">1. 초기 설정</h3>
<h4 id="terminal">terminal</h4>
<pre><code>npm init -y</code></pre><pre><code>npm i -D webpack webpack-cli webpack-dev-server@next</code></pre><h5 id="용어설명">용어설명</h5>
<ul>
<li><code>webpack-cli</code>: command line interface를 지원해주는 패키지. 해당 패키지로 webpack 명령을 사용할 수 있게 됨.</li>
<li><code>webpack-dev-server</code>: 코드 수정을 즉각적으로 확인 할 수 있는 개발 서버를 열어주는 패키지<h4 id="packagejson">package.json</h4>
<pre><code>&quot;scripts&quot;: {
 &quot;dev&quot;: &quot;webpack-dev-server --mode development&quot;,
 &quot;build&quot;: &quot;webpack --mode production&quot;
},</code></pre></li>
</ul>
<h3 id="2-플러그인-설치">2. 플러그인 설치</h3>
<h4 id="terminal-1">terminal</h4>
<pre><code>npm i -D html-webpack-plugin copy-webpack-plugin</code></pre><h5 id="용어설명-1">용어설명</h5>
<ul>
<li><code>html-webpack-plugin</code>: webpack 번들을 제공하는 HTML 파일 생성을 단순화한다.</li>
<li>&#39;copy-webpack-plugin&#39;: 지정한 폴더 안의 내용을 build된 폴더 안으로 들어 갈 수 있게 설정하는 플러그인.</li>
</ul>
<h3 id="3-module">3. module</h3>
<h4 id="terminal-2">terminal</h4>
<pre><code>npm i -D css-loader style-loader</code></pre><h5 id="용어설명-2">용어설명</h5>
<p><code>css-loader</code>: js에서 css를 해석하는데 도움을 줌.
<code>style-loader</code>: 해석된 css파일을 html파일에 삽입시키는 역할.</p>
<h4 id="mainjs">main.js</h4>
<pre><code>import &#39;../css/style.css</code></pre><h3 id="3-autoprefixer">3. autoprefixer</h3>
<h4 id="terminal-3">terminal</h4>
<pre><code>npm i -D postcss autoprefixer postcss-loader</code></pre><h5 id="용어설명-3">용어설명</h5>
<ul>
<li><code>postcss</code>: 스타일의 후처리를 도와준다.</li>
<li><code>autoprefixer</code>: 공급업체 접두사를 자동으로 붙여준다.</li>
<li><code>postcss-loader</code>: webpack에서 postcss를 동작시킨다.</li>
</ul>
<h4 id="packagejson-1">package.json</h4>
<pre><code>  // 현재 프로젝트가 어떤 브라우저를 지원하는 지 명시
  &quot;browserslist&quot;: [
    &quot;&gt; 1%&quot;,
    &quot;last 2 versions&quot;
  ]</code></pre><h4 id="postcssrcjs">.postcssrc.js</h4>
<pre><code>module.exports = {
    plugins: [
      require(&#39;autoprefixer&#39;)
    ]
};</code></pre><h3 id="4-babel">4. babel</h3>
<h4 id="terminal-4">terminal</h4>
<pre><code>npm i -D @babel/core @bable/preset-env @bable/plugin-transform-runtime babel-loader</code></pre><h5 id="용어설명-4">용어설명</h5>
<ul>
<li><code>@babel/core</code>: babel을 사용하기 위한 필수 패키지</li>
<li><code>@babel/preset-env</code>: js 기능들을 별다른 명시없이 자동으로 사용할 수 있게 하는 기능.</li>
<li><code>@babel/plugin-transform-runtime</code>: 비동기처리를 위한 플러그인</li>
<li><code>babel-loader</code>: webpack에서 babel 기능을 사용할 수 있게 한다.</li>
</ul>
<h4 id="babelrcjs">.babelrc.js</h4>
<pre><code>module.exports = {
    presets: [&#39;@babel/preset-env&#39;],
    plugins: [
      [&#39;@babel/plugin-transform-runtime&#39;]
    ]
};</code></pre><h3 id="5-webpackconfigjs">5. webpack.config.js</h3>
<pre><code>// import
const path = require(&#39;path&#39;);
const HtmlPlugin = require(&#39;html-webpack-plugin&#39;);
const CopyPlugin = require(&#39;copy-webpack-plugin&#39;);

// export
module.exports = {
  // 파일을 읽어들이기 시작하는 진입점 설정
  entry: &#39;./js/main.js&#39;,

  // 결과물(번들)을 반환하는 설정
  output: {
      // path에 어떠한 결과물을 어디에 내어줄 것인지 설정.
      // path는 절대 경로를 필요로 함.
    // resolve는 첫 번째 인수와 두 번째 인수를 합쳐 준다.
    // __dirname은 현재 파일이 있는 경로를 지칭
    path: path.resolve(__dirname, &#39;dist&#39;),
    // 어떤 파일 명으로 내보낼 것인지 설정
    filename: &#39;main.js&#39;,
    // 기존에 내보낸 파일을 제거.
    clean: true,
  },

  module: {
    rules: [
      {
        test: /\.css$/,
        // 순서가 중요. 맨밑에서 부터 읽힘.
        use: [
          &#39;style-loader&#39;,
          &#39;css-loader&#39;,
          &#39;postcss-loader&#39;,
        ]
      },
      {
        test: /\.js$/,
        use: &#39;babel-loader&#39;
      }
    ]
  },

  // 번들링 후 결과물의 처리 방식 등 다양한 플러그인들을 설정.
  plugins: [
    new HtmlPlugin({
      template: &#39;./index.html&#39;
    }),
    new CopyPlugin({
      patterns: [
        { from: &#39;static&#39; }
      ]
    })
  ],

  // 서버 주소 설정
  devServer: {
    host: &#39;localhost&#39;
  },
};</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[MacOS에서는 CLOCKS_PER_SEC가 1,000,000으로 나눠집니다!!]]></title>
            <link>https://velog.io/@sweet_pumpkin/Mac-OS%EC%97%90%EC%84%9C%EB%8A%94-CLOCKSPERSEC%EA%B0%80-1000000%EC%9C%BC%EB%A1%9C-%EB%82%98%EB%88%A0%EC%A7%91%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@sweet_pumpkin/Mac-OS%EC%97%90%EC%84%9C%EB%8A%94-CLOCKSPERSEC%EA%B0%80-1000000%EC%9C%BC%EB%A1%9C-%EB%82%98%EB%88%A0%EC%A7%91%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Wed, 10 Aug 2022 01:11:02 GMT</pubDate>
            <description><![CDATA[<p>어쩌다보니 C언어도 공부하기 시작한 나... </p>
<p>C언어를 독학하는데 있어서 가장 큰 문제는 C언어 강의나 서적들이 Window 운영체제를 기반으로 설명해준다는 것이다. 나는 맥북쓰는데... MacOS에서 알려주는 그런 강의없습니까... 서로 다른 운영체제를 쓰다보니, Window에서 사용가능한 코드가 MacOS에서는 작동하지 않는 경우가 종종 있다. 오늘이 그러했다...</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/383cb2b8-2bd5-47e7-ae05-9f2561c98ea9/image.png" alt="">
<del>왜 MacOS론 햄보칼수업써!!</del></p>
<h2 id="span-stylecolor-5fc69a경과시간-구하기span"><span style="color: #5fc69a">경과시간 구하기</span></h2>
<p>문제는 C언어로 경과시간을 구하면서부터 시작되었다. 특정 동작이 일어난 후 시작 시간에서부터 경과시간을 구하는 코드는 대충 아래와 같다.</p>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;time.h&gt;

int main(void) {
    long startTime = 0;
    long elapsedTime = 0;

    startTime = clock();

    /* ~~~ 특정 동작 실행 중 ~~~ */

    elapsedTime = (clock() - startTime) / CLOCKS_PER_SEC;

    printf(&quot;경과 시간 : %ld초\n&quot;, elapsedTime);

    return 0;
}</code></pre><p>먼저 <code>clock()</code>을 사용해서 처음 시작 시간 값과 동작 종료 후 시간 값을 각각 구한다. 이후, 동작 종료 후 시간에 처음 시작 시간을 뺀 뒤 <code>CLOCKS_PER_SEC</code>로 나눠주면 초 단위로 값이 나오게 된다. 문제는 <code>CLOCKS_PER_SEC</code>이 Window에서는 1,000을 의미하지만, MacOS에서는 1,000,000을 의미한다. 즉 같은 코드지만, MacOS에서는 Window에서 출력된 경과시간과 같은 값을 얻기 위해 훨씬 더 긴 영겁의 시간(?)을 견뎌야 한다는 것. </p>
<h2 id="span-stylecolor-5fc69agettimeofday로-경과시간-구하기span"><span style="color: #5fc69a">gettimeofday()로 경과시간 구하기</span></h2>
<p>구글링 끝에 <code>gettimeofday()</code>로 경과시간을 구하는 방법을 습득했다. 코드는 아래와 같다.</p>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;sys/time.h&gt;

int main(void) {
    struct timeval startTime;
    struct timeval endTime;
    int elapsedTime;

    gettimeofday(&amp;startTime, NULL);

    /* ~~~ 특정 동작 실행 중 ~~~ */

    gettimeofday(&amp;endTime, NULL);
    elapsedTime = endTime.tv_sec - startTime.tv_sec;

    printf(&quot;경과 시간 : %d&quot;, elapsedTime);

    return 0;
}</code></pre><p><code>clock()</code>을 사용했을때와 비슷한 구조다. <code>gettimeofday()</code>는 아래와 같이 두개의 인자를 갖는다. 첫번째 인자 <code>tv</code>는 현재 시스템 시간을 저장하기 위한 구조체, 두번째 인자 <code>tz</code>은 타임존을 설정하기 위해서 사용된다. 현재 <code>tz</code>는 사용하지 않기 때문에 <code>NULL</code>을 사용하면 된다.</p>
<pre><code>#include &lt;sys/time.h&gt;

int gettimeofday(struct timeval *tv, struct timezone *tz);

// tv
struct timeval
{
    long tv_sec;       // 초
    long tv_usec;      // 마이크로초
}

// tz
struct timezone
{
    int tz_minuteswest:  // 그리니치 서측분차  
    int tz_dsttime       // DST 보정타입(일광 절약시간)
}</code></pre><p>끝.</p>
<hr>
<p>참고 : <a href="https://www.joinc.co.kr/w/man/2/gettimeofday">https://www.joinc.co.kr/w/man/2/gettimeofday</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React로 그림판 만들기2 : 리셋&저장]]></title>
            <link>https://velog.io/@sweet_pumpkin/React-%EA%B7%B8%EB%A6%BC%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B02-%EB%A6%AC%EC%85%8B%EC%A0%80%EC%9E%A5</link>
            <guid>https://velog.io/@sweet_pumpkin/React-%EA%B7%B8%EB%A6%BC%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B02-%EB%A6%AC%EC%85%8B%EC%A0%80%EC%9E%A5</guid>
            <pubDate>Fri, 29 Jul 2022 08:13:39 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@sweet_pumpkin/React%EB%A1%9C-%EA%B7%B8%EB%A6%BC%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B01">이전 글</a>에 이어서 작업물 리셋&amp;저장 기능을 구현해보자. 해당 기능은 매우 간단한 코드 작성으로 구현 가능하다. 노마드 코더 <a href="https://nomadcoders.co/javascript-for-beginners-2">바닐라JS로 그림 앱 만들기</a> 강의와 딱히 다를 것 없을지도? </p>
<h2 id="span-stylecolor-5fc69aprops-이용해-값-전달하기span"><span style="color: #5fc69a">props 이용해 값 전달하기</span></h2>
<p>그림을 그릴 수 있는 기능은 App.jsx에, 다른 기능들은 MenuBar.jsx에 코드를 작성했다. 따라서 App.jsx에 <code>canvas</code> 정보들을 MenuBar.jsx에 props를 사용해 전달했다.</p>
<ul>
<li>App.jsx<pre><code>// ~~~ 생략 ~~~
import MenuBar from &quot;./components/MenuBar&quot;;
</code></pre></li>
</ul>
<p>return (
    {/* <del>~ 생략 ~</del> */}
    <MenuBar 
        getCtx={getCtx}
        getCanvas={getCanvas}
    />
);</p>
<pre><code>
- MenuBar.jsx</code></pre><p>export default function MenuBar({ getCtx, getCanvas }) {
    return ();
}</p>
<pre><code>
## &lt;span style=&quot;color: #5fc69a&quot;&gt;리셋 기능 구현하기&lt;/span&gt;
리셋 기능은 `clearRect()` 메서드를 사용하면 쉽게 작업물을 초기화할 수 있다. `&lt;RestartAltIcon /&gt;`은 mui 아이콘이니 신경쓰지말자.
</code></pre><p>export default function MenuBar({ getCtx, getCanvas }) {</p>
<pre><code>const onReset = () =&gt; {
    getCtx.clearRect(0, 0, getCanvas.width, getCanvas.height);
  }

 return (
    &lt;li onClick={onReset}&gt;
        &lt;RestartAltIcon className=&quot;icons&quot; /&gt;
      &lt;/li&gt;
  );</code></pre><p>}</p>
<pre><code>
## &lt;span style=&quot;color: #5fc69a&quot;&gt;다운로드 기능 구현하기&lt;/span&gt;

이미지 다운로드 기능은 노마드 코더 [바닐라JS로 그림 앱 만들기](https://nomadcoders.co/javascript-for-beginners-2) 강의와 다를 것이 없다. 뭔가 React스러운(?) 코드를 원했으나, 아직 괜찮은 코드를 찾지 못했다ㅠ. &quot;으... React 저렇게 하는 거 아닌데...&quot; 하시는 분은 제발 댓글로 알려줘잉...
</code></pre><p>export default function MenuBar({ getCtx, getCanvas }) {</p>
<pre><code>const onSave = () =&gt; {
  const imageURL = getCanvas.toDataURL();
  const downloadImage = document.createElement(&quot;a&quot;);
  downloadImage.href = imageURL;
  downloadImage.download = &quot;paint_image&quot;;
  downloadImage.click();
}

 return (
    &lt;li onClick={onSave}&gt;
        &lt;SaveIcon className=&quot;icons&quot; /&gt;
      &lt;/li&gt;
  );</code></pre><p>}</p>
<p>```</p>
<p>아직 디자인을 어떻게 할 지 몰라서 대충 배치했는데, 나에게는 아직 디자인이 제일 어려운 것 같다. 그쪽으로 감각이 없나? ㅠ. 다음은 색상선택, 지우개 기능을 하려고 하는데 노마드 코더 <a href="https://nomadcoders.co/javascript-for-beginners-2">바닐라JS로 그림 앱 만들기</a>에 없던 지우개, 이미지&amp;텍스트 추가 기능이 새로 업데이트 되었다.(노마드로부터 돈 안 받았다.) 해당 강의를 다시 보면서 React로 바꿔보는 작업을 해봐야겠다.</p>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React로 그림판 만들기1]]></title>
            <link>https://velog.io/@sweet_pumpkin/React%EB%A1%9C-%EA%B7%B8%EB%A6%BC%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B01</link>
            <guid>https://velog.io/@sweet_pumpkin/React%EB%A1%9C-%EA%B7%B8%EB%A6%BC%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B01</guid>
            <pubDate>Tue, 19 Jul 2022 03:50:32 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-5fc69a아-react가-미숙했던-나의-옛날3달-전이여span"><span style="color: #5fc69a">아! React가 미숙했던 나의 옛날(3달 전)이여!</span></h2>
<p>노마드 코더 <a href="https://nomadcoders.co/javascript-for-beginners-2">바닐라JS로 그림 앱 만들기</a> 강의를 보며 진행했던 프로젝트를 React로 바꿔보고싶었다. 예전에 한 번 시도했었다가 막힌 이후로 다시 한 번 시도해보는 것이다.</p>
<p>국비 과정을 통해 다양한 React 프로젝트를 경험하다보니, 예전에 왜 실패했었는지 단번에 이해가 되었다. 옛날 실패했던 프로젝트 코드를 열어보니, 그때는 JavaScript에만 익숙했기 때문에 React App 위에 JavaScript를 얹혀놓은 괴상한 모습을 하고 있었다.</p>
<h2 id="span-stylecolor-5fc69areact-hook-사용하기span"><span style="color: #5fc69a">React Hook 사용하기</span></h2>
<p>바닐라JS와는 다르게, React에서는 <code>canvas</code>와 <code>getContext()</code>를 사용하기 위해서는 먼저 <code>useRef()</code>와 <code>useEffect()</code>, <code>useState()</code>의 도움을 받아야 한다.</p>
<ul>
<li>Vanilla JS<pre><code>&lt;body&gt;
  &lt;canvas id=&quot;canvas&quot;&gt;&lt;/canvas&gt;
&lt;/body&gt;
&lt;script&gt;
  const canvas = document.getElementById(&quot;canvas&quot;);
  const ctx = canvas.getContext(&quot;2d&quot;);
&lt;/script&gt;</code></pre></li>
<li>React<pre><code>import React { useEffect, useRef, useState } from &quot;react&quot;;
</code></pre></li>
</ul>
<p>export default function App() {
    const canvasRef = useRef(null);
    const [getCtx, setGetCtx] = useState(null);</p>
<pre><code>useEffect(() =&gt; {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext(&quot;2d&quot;);
    setGetctx(ctx);
}, [])

return (
    &lt;canvas ref={canvasRef}&gt;&lt;/canvas&gt;
);</code></pre><p> }</p>
<pre><code>
## &lt;span style=&quot;color: #5fc69a&quot;&gt;addEventListener말고 Mouse Event 사용하기&lt;/span&gt;

`canvas`에 대한 설정을 해준 다음에는, `canvas` 범위 내에서 현재 마우스의 위치 정보를 받아와야 한다. 그래야 그 위치에 그림을 그릴 수 있기 때문이다. 바닐라JS에서는 `addEventListener()`를 사용했지만, React에서는 `canvas` 태그에 Mouse Event를 사용하자.

- Vanilla JS</code></pre><script>
  canvas.addEventListener("mousemove", onMouseMove);
  canvas.addEventListener("mousedown", startPainting);
  canvas.addEventListener("mouseup", stopPainting);
  canvas.addEventListener("mouseleave", stopPainting);
</script>
<pre><code>
- React</code></pre><p>return(
    &lt;canvas
        ref={canvasRef}
        onMouseDown={() =&gt; setPainting(true)}
        onMouseUp={() =&gt; setPainting(false)}
        onMouseMove={e =&gt; drawFn(e)}
        onMouseLeave={() =&gt; setPainting(false)}
    &gt;
    </canvas>
);</p>
<pre><code>
## &lt;span style=&quot;color: #5fc69a&quot;&gt;마우스 현재 위치 정보 가져와서 그리기&lt;/span&gt;

위의 이벤트를 설명하자면, 마우스를 눌렀을 경우 그림을 그릴 수 있고, 마우스를 뗐을 경우나 `canvas` 범위 내에서 벗어날 때에는 그림을 그릴 수 없다. 그리고 마우스를 `canvas` 범위 안에서 움직일 때 움직인 위치 정보를 가져다주게 된다.

바닐라 JS는 `offsetX`와 `offsetY`로 위치 정보를 가져올 수 있는데, React에서는 `nativeEvent`로 한 번 거쳐서 `offsetX`와 `offsetY`를 가져와야 한다. `clientX`나, `pageX` 등은 `canvas` 범위를 기준으로 하지 않기 때문에 내가 그림을 그리려는 위치와 실제 그림이 그려지는 위치가 차이가 발생한다. 따라서 React에서는 `event.nativeEvent.offsetX`, `event.nativeEvent.offsetY`와 같이 사용해야 한다.

- Vanilla JS</code></pre><script>
    let painting = false;

    function stopPainting() {
          painting = false;
    }

    function startPainting() {
          painting = true;
    }

    function onMouseMove(event) {
          const x = event.offsetX;
          const y = event.offsetY;
          if(!painting) {
            ctx.beginPath();
            ctx.moveTo(x, y);
          } else {
            ctx.lineTo(x, y);
            ctx.stroke();
          }
    }
</script>
<pre><code>
- React</code></pre><p>const [painting, setPainting] = useState(false);</p>
<p>const drawFn = e =&gt; {
    const mouseX = e.nativeEvent.offsetX;
    const mouseY = e.nativeEvent.offsetY;
    if (!painting) {
      getCtx.beginPath();
      getCtx.moveTo(mouseX, mouseY);
    } else {
      getCtx.lineTo(mouseX, mouseY);
      getCtx.stroke();
    }
}</p>
<pre><code>
## &lt;span style=&quot;color: #5fc69a&quot;&gt;전체 코드&lt;/span&gt;
- App.jsx</code></pre><p>// react
import React, { useRef, useEffect, useState } from &quot;react&quot;;
// style
import { CanvasStyle } from &quot;./styles/cavas&quot;;</p>
<p>export default function App() {
  // useRef
  const canvasRef = useRef(null);
  // getCtx
  const [getCtx, setGetCtx] = useState(null);
  // painting state
  const [painting, setPainting] = useState(false);</p>
<p>  useEffect(() =&gt; {
    // canvas useRef
    const canvas = canvasRef.current;
    canvas.width = 650;
    canvas.height = 540;
    const ctx = canvas.getContext(&quot;2d&quot;);
    ctx.lineJoin = &quot;round&quot;;
    ctx.lineWidth = 2.5;
    ctx.strokeStyle = &quot;#000000&quot;;
    setGetCtx(ctx);
  }, []);</p>
<p>  const drawFn = e =&gt; {
    // mouse position
    const mouseX = e.nativeEvent.offsetX;
    const mouseY = e.nativeEvent.offsetY;
    // drawing
    if (!painting) {
      getCtx.beginPath();
      getCtx.moveTo(mouseX, mouseY);
    } else {
      getCtx.lineTo(mouseX, mouseY);
      getCtx.stroke();
    }
  }</p>
<p>  return (
    <CanvasStyle>
      <div className="view">
        <div className="canvasWrap">
          &lt;canvas 
            className=&quot;canvas&quot;
            ref={canvasRef}
            onMouseDown={() =&gt; setPainting(true)}
            onMouseUp={() =&gt; setPainting(false)}
            onMouseMove={e =&gt; drawFn(e)}
            onMouseLeave={() =&gt; setPainting(false)}
          &gt;
          </canvas>
        </div>
      </div>
    </CanvasStyle>
  )
}</p>
<p>```</p>
<h2 id="span-stylecolor-5fc69a결과물span"><span style="color: #5fc69a">결과물</span></h2>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/c99ce162-afe4-4eab-98b4-aa11b93ce4a2/image.gif" alt=""></p>
<p>일단 검은 선만 그을 수 있는 상태다. 다음번에는 노마드 코더 <a href="https://nomadcoders.co/javascript-for-beginners-2">바닐라JS로 그림 앱 만들기</a> 강의 내용처럼 색상선택이나 페인트 기능, 저장 기능 등을 구현해야하는데...(귀찮)</p>
<p>시간 날 때마다 계속 작업해보도록 하겠다.</p>
<p>끝.</p>
<hr>
<p>다음 글 : <a href="https://velog.io/@sweet_pumpkin/React-%EA%B7%B8%EB%A6%BC%ED%8C%90-%EB%A7%8C%EB%93%A4%EA%B8%B02-%EB%A6%AC%EC%85%8B%EC%A0%80%EC%9E%A5">React로 그림판 만들기2 : 리셋&amp;저장</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[단 몇 줄로 구현하는  무한 스크롤 : NYT API로 뉴스 기사 검색 웹 페이지 만들기 2]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EB%8B%A8-%EB%AA%87-%EC%A4%84%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-NYT-API%EB%A1%9C-%EB%89%B4%EC%8A%A4-%EA%B8%B0%EC%82%AC-%EA%B2%80%EC%83%89-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-2</link>
            <guid>https://velog.io/@sweet_pumpkin/%EB%8B%A8-%EB%AA%87-%EC%A4%84%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-NYT-API%EB%A1%9C-%EB%89%B4%EC%8A%A4-%EA%B8%B0%EC%82%AC-%EA%B2%80%EC%83%89-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-2</guid>
            <pubDate>Thu, 16 Jun 2022 13:11:39 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylecolor-5fc69a이전-글span"><span style="color: #5fc69a">이전 글</span></h3>
<ul>
<li><a href="https://velog.io/@sweet_pumpkin/%ED%95%9C%EA%B8%80%EB%A1%9C-%EA%B0%9C%EB%B0%9C%EC%9D%84-%EC%B6%94%EA%B5%AC%ED%95%98%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94%EA%B1%B8%EA%B9%8C-NYT-API%EB%A1%9C-%EB%89%B4%EC%8A%A4-%EA%B8%B0%EC%82%AC-%EA%B2%80%EC%83%89-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-1">NYT API로 뉴스 기사 검색 웹 페이지 만들기 1</a></li>
</ul>
<hr>
<h2 id="span-stylecolor-5fc69aintersection-observer-apispan"><span style="color: #5fc69a">Intersection Observer API</span></h2>
<p>이전 글에서 NYT API를 사용해 뉴스 기사 정보들을 불러왔다. 그런데 문제는 한번 API를 호출할 때마다 뉴스 기사 정보를 페이지당 10개씩 가져온다는 것.</p>
<p>여러가지 방법이 있겠지만, 무한 스크롤을 통해 다음 페이지를 호출해보자.</p>
<h3 id="span-stylecolor-5fc69areact-intersection-observer-설치span"><span style="color: #5fc69a">React Intersection Observer 설치</span></h3>
<p>React 환경에서 무한 스크롤은 <code>react-intersection-observer</code>을 사용하면 매우 간단하게 구현 가능하다.</p>
<pre><code>$ npm install react-intersection-observer</code></pre><p>위의 예시처럼 명령어를 입력해 설치를 끝냈으면 무한 스크롤을 구현하고 싶은 컴포넌트에 설치한 Intersection Observer API를 불러와주자.</p>
<h3 id="span-stylecolor-5fc69a무한-스크롤-만들기span"><span style="color: #5fc69a">무한 스크롤 만들기</span></h3>
<p>무한 스크롤은 아래와 같이 단 몇 줄로 구현이 가능하다.</p>
<h4 id="appjs">App.js</h4>
<pre><code>import { useInView } from &quot;react-intersection-observer&quot;;

export default function App() {
    const [ref, inView] = useInView();

    // ~~~

    useEffect(() =&gt; {
        if (inView) {
              setPage(prev =&gt; prev + 1)
        }
      }, [inView])

    return (
        {/* ~~~ */}
        &lt;div ref={ref} /&gt;
    );
}</code></pre><p><code>useInView()</code>의 초기 값은 false다. 아래 ref를 걸어놓은 div 태그가 화면에 보이면 <code>inView</code>는 true를 반환할 것이고, 아니라면 false를 다시 반환 할 것이다.</p>
<p><code>useEffect()</code>로 <code>inView</code>가 true일때마다 API의 페이지를 1씩 증가시켜주면 무한 스크롤을 구현할 수 있다.</p>
<p>참고로 <code>ref</code>와 <code>inView</code>는 마음대로 바꿔서 쓰면 된다. 근데 다 이거 쓰는 것 같음...</p>
<p>아래는 코드 적용 화면</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/18730d45-0dfa-4fd4-b31b-25430c19b079/image.gif" alt=""></p>
<h3 id="span-stylecolor-5fc69aoptionsspan"><span style="color: #5fc69a">Options</span></h3>
<p><code>useInView()</code> 안에는 아래와 같이 여러가지 옵션을 지정할 수 있다.</p>
<ul>
<li>root : 관찰 대상의 경계를 지정한다. 기본 값은 뷰포트다.</li>
<li>rootMargin : root에 margin을 설정한다.</li>
<li>threshold : root와 관찰 대상의 교차영역을 비율로 지정한다. 0에서 1까지 값을 지정할 수 있다.</li>
</ul>
<p>이외에도 다양한 옵션이 있는데, 자세한 내용은 <a href="https://www.npmjs.com/package/react-intersection-observer">https://www.npmjs.com/package/react-intersection-observer</a> 에서 확인 가능하다.</p>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[한글로 개발을 추구하면 안되는걸까 : NYT API로 뉴스 기사 검색 웹 페이지 만들기 1]]></title>
            <link>https://velog.io/@sweet_pumpkin/%ED%95%9C%EA%B8%80%EB%A1%9C-%EA%B0%9C%EB%B0%9C%EC%9D%84-%EC%B6%94%EA%B5%AC%ED%95%98%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94%EA%B1%B8%EA%B9%8C-NYT-API%EB%A1%9C-%EB%89%B4%EC%8A%A4-%EA%B8%B0%EC%82%AC-%EA%B2%80%EC%83%89-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-1</link>
            <guid>https://velog.io/@sweet_pumpkin/%ED%95%9C%EA%B8%80%EB%A1%9C-%EA%B0%9C%EB%B0%9C%EC%9D%84-%EC%B6%94%EA%B5%AC%ED%95%98%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94%EA%B1%B8%EA%B9%8C-NYT-API%EB%A1%9C-%EB%89%B4%EC%8A%A4-%EA%B8%B0%EC%82%AC-%EA%B2%80%EC%83%89-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-1</guid>
            <pubDate>Thu, 16 Jun 2022 08:18:45 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-5fc69anyt-api-사용방법span"><span style="color: #5fc69a">NYT API 사용방법</span></h2>
<p>영포자 경력 수십년차(전생에 위정척사파였던 듯) 간단한 영문 API 문서도 이해하고 활용하기가 쉽지 않다.
이번 프로젝트에 사용한 뉴욕타임즈 API도 쉬운 편에 속한다던데 이거 제대로 쓰는 데에도 한참 걸렸다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/bf7acde3-4d66-46a3-b5f7-f9bb5dc81a6b/image.png" alt=""></p>
<p>근데 구글링 열심히해봤는데 왜 아무도 안알려주냐.. 
진짜 안 찾아봐도 될 정도로 그렇게 쉬웠나ㅠ</p>
<h3 id="span-stylecolor-5fc69a나는-쓴다-nyt-article-search-apispan"><span style="color: #5fc69a">나는 쓴다. NYT Article Search API.</span></h3>
<p>뉴욕타임즈 뉴스 기사 검색 api는 아래 웹 페이지에서 회원가입을 해 받아올 수 있다.</p>
<p><a href="https://developer.nytimes.com/">https://developer.nytimes.com/</a> </p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/366e93a3-d945-48a8-a3b3-498152e0b1e4/image.png" alt=""></p>
<p>가입하면 위 사진처럼 자신의 계정 정보에 접근해 Apps 페이지로 들어가보자. My Apps 페이지에서 New App 버튼을 클릭하면 아래와 같은 화면이 나올 것이다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/c778ed84-ae48-4424-9ad5-53c84a5bf10e/image.png" alt=""></p>
<p>Overview 항목에서는 App의 이름을 지정해주고, 따로 해당 App에 대한 설명이 필요하다면 적어주도록 한다.</p>
<p>APIs에서는 사용할 Api를 지정해주면 되는데, 기사 검색 기능만 필요하니 Article Search API만 Enable로 등록하면 된다. 저장하면 사용가능한 API key가 뜰 것이다. 해당 key를 가지고 기사 검색 기능을 구현하면 된다.
.
.
.
.
.
.
...까지만 적혀있더라...</p>
<p>사실 프로젝트를 완성한 입장에서 보면 별 것 아니었다. 공식문서에 다 적혀있고 차근차근 찾아보면 되는 부분인데...
<img src="https://velog.velcdn.com/images/sweet_pumpkin/post/76d6c585-0edd-47f4-b5a9-44156ce667d5/image.jpeg" alt=""></p>
<p><em>아ㅎ 영어 안 읽음 ㅈㅅㅎㅎ 구글링하면 나오겠지ㅎㅎ</em></p>
<p>이딴 태도로 몇시간을 버렸던 것 같다. <del>영어 너무 싫엉...</del></p>
<h3 id="span-stylecolor-5fc69a뉴스-기사-검색-api-활용하기span"><span style="color: #5fc69a">뉴스 기사 검색 API 활용하기</span></h3>
<p>NYT devolper 사이트의 APIs 탭에서 기사 검색 api 파트로 들어가면, 아래의 주소와 방금 만든 api key를 활용해 api를 사용할 수 있다.</p>
<pre><code>https://api.nytimes.com/svc/search/v2/articlesearch.json?q=election&amp;api-key=yourkey</code></pre><p>해당 메뉴에서, PATHS 탭에 들어가면 검색 뿐만 아니라 여러 기능들을 쓸 수 있게 &quot;영어&quot;로 친절하게 안내해준다.</p>
<p>그 중에서 내가 쓴 기능은 뉴스 기사 검색(q), 페이지 정보(page), 정렬 방법(sort), 총 3개다. 이것을 api 주소에 적절하게 넣어주면 된다.</p>
<p>만약 한국을 검색하고, 페이지 수는 3페이지, 정렬방법을 최신순으로 지정해 출력하려면 아래와 같은 주소로 불러오면 된다.</p>
<pre><code>https://api.nytimes.com/svc/search/v2/articlesearch.json?q=korea&amp;page=3&amp;sort=newest&amp;api-key=yourkey</code></pre><p>이렇게하면 최대 10개의 뉴스 기사들을 제공해준다. 하지만 내가 원하는 것은 10개 이상의 뉴스 기사들이 출력되는 것을 원한다. 그것은 다음 글에서 설명하겠다.</p>
<hr>
<h3 id="span-stylecolor-5fc69a다음-글span"><span style="color: #5fc69a">다음 글</span></h3>
<ul>
<li><a href="https://velog.io/@sweet_pumpkin/%EB%8B%A8-%EB%AA%87-%EC%A4%84%EB%A1%9C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-NYT-API%EB%A1%9C-%EB%89%B4%EC%8A%A4-%EA%B8%B0%EC%82%AC-%EA%B2%80%EC%83%89-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-2">NYT API로 뉴스 기사 검색 웹 페이지 만들기 2</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Firebase GoogleAuthProvider 외않됨;;]]></title>
            <link>https://velog.io/@sweet_pumpkin/Error-Firebase-GoogleAuthProvider-%EC%99%B8%EC%95%8A%EB%90%A8</link>
            <guid>https://velog.io/@sweet_pumpkin/Error-Firebase-GoogleAuthProvider-%EC%99%B8%EC%95%8A%EB%90%A8</guid>
            <pubDate>Sun, 05 Jun 2022 11:55:22 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-5fc69a문제-발생span"><span style="color: #5fc69a">문제 발생</span></h2>
<p>React 환경에서 Firebase <code>GoolgeAuthProvider()</code>를 사용해 구글 아이디로 회원가입 코드를 작성하던 중 아래와 같은 에러 메세지가 발생했다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/b7450a4e-9074-471e-8c48-81c6e95b7ea2/image.png" alt=""></p>
<h2 id="span-stylecolor-5fc69a문제-해결span"><span style="color: #5fc69a">문제 해결</span></h2>
<p>환경변수 설정 문제로, <code>.env</code>와 그것을 받는 파일에서 오타가 있어서 발생한 오류. React 환경에서는 환경변수를 사용할때 꼭 변수명 앞에 <code>REACT_APP_</code>을 붙여야 한다. 코드 작성시 오타에 유의하자.</p>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[5월 4주차 코딩테스트 문제 풀이 리뷰]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-5%EC%9B%94-4%EC%A3%BC%EC%B0%A8-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%A6%AC%EB%B7%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-5%EC%9B%94-4%EC%A3%BC%EC%B0%A8-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%A6%AC%EB%B7%B0</guid>
            <pubDate>Sun, 29 May 2022 11:00:44 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-행렬의-덧셈span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [행렬의 덧셈]</span></h1>
<h3 id="문제설명">문제설명</h3>
<p>행렬의 덧셈은 행과 열의 크기가 같은 두 행렬의 같은 행, 같은 열의 값을 서로 더한 결과가 됩니다. 2개의 행렬 arr1과 arr2를 입력받아, 행렬 덧셈의 결과를 반환하는 함수, solution을 완성해주세요.</p>
<h4 id="제한-조건">제한 조건</h4>
<ul>
<li>행렬 arr1, arr2의 행과 열의 길이는 500을 넘지 않습니다.</li>
</ul>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th align="center">arr1</th>
<th align="center">arr2</th>
<th align="center">return</th>
</tr>
</thead>
<tbody><tr>
<td align="center">[[1,2],[2,3]]</td>
<td align="center">[[3,4],[5,6]]</td>
<td align="center">[[4,6],[7,9]]</td>
</tr>
<tr>
<td align="center">[[1],[2]]</td>
<td align="center">[[3],[4]]</td>
<td align="center">[[4],[6]]</td>
</tr>
</tbody></table>
<h3 id="제출답안">제출답안</h3>
<pre><code>function solution(arr1, arr2) {
    var answer = [];

    for(let i =0; i&lt;arr1.length; i++){
        let res = []
        for(let j =0; j&lt;arr1[i].length; j++){
            res.push(arr1[i][j] + arr2[i][j])
        }
        answer.push(res)
    }
    return answer
}
// =&gt; 테스트 통과!</code></pre><h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-x만큼-간격이-있는-n개의-숫자span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [x만큼 간격이 있는 n개의 숫자]</span></h1>
<h3 id="문제-설명">문제 설명</h3>
<p>함수 solution은 정수 x와 자연수 n을 입력 받아, x부터 시작해 x씩 증가하는 숫자를 n개 지니는 리스트를 리턴해야 합니다. 다음 제한 조건을 보고, 조건을 만족하는 함수, solution을 완성해주세요.</p>
<h3 id="제한조건">제한조건</h3>
<ul>
<li>x는 -10000000 이상, 10000000 이하인 정수입니다.</li>
<li>n은 1000 이하인 자연수입니다.</li>
</ul>
<h3 id="입출력-예-1">입출력 예</h3>
<table>
<thead>
<tr>
<th align="center">x</th>
<th align="center">n</th>
<th align="center">answer</th>
</tr>
</thead>
<tbody><tr>
<td align="center">2</td>
<td align="center">5</td>
<td align="center">[2,4,6,8,10]</td>
</tr>
<tr>
<td align="center">4</td>
<td align="center">3</td>
<td align="center">[4,8,12]</td>
</tr>
<tr>
<td align="center">-4</td>
<td align="center">2</td>
<td align="center">[-4,-8]</td>
</tr>
</tbody></table>
<h3 id="제출답안-1">제출답안</h3>
<pre><code>function solution(x, n) {
    var answer = [];
    for (let i = 0; i &lt; n; i ++) {
        answer.push(x * (i + 1));
    }
    return answer;
}
// =&gt; 테스트 통과</code></pre><h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-수박수박수박수박수박수span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [수박수박수박수박수박수?]</span></h1>
<h3 id="문제-설명-1">문제 설명</h3>
<p>길이가 n이고, &quot;수박수박수박수....&quot;와 같은 패턴을 유지하는 문자열을 리턴하는 함수, solution을 완성하세요. 예를들어 n이 4이면 &quot;수박수박&quot;을 리턴하고 3이라면 &quot;수박수&quot;를 리턴하면 됩니다.</p>
<h3 id="제한-조건-1">제한 조건</h3>
<ul>
<li>n은 길이 10,000이하인 자연수입니다.</li>
</ul>
<h3 id="입출력-예-2">입출력 예</h3>
<table>
<thead>
<tr>
<th align="center">n</th>
<th align="center">return</th>
</tr>
</thead>
<tbody><tr>
<td align="center">3</td>
<td align="center">&quot;수박수&quot;</td>
</tr>
<tr>
<td align="center">4</td>
<td align="center">&quot;수박수박&quot;</td>
</tr>
</tbody></table>
<h3 id="제출답안-2">제출답안</h3>
<pre><code>function solution(n) {
    var answer = &#39;&#39;;
    for (let i = 1; i &lt;= n; i ++) {
        if (i % 2 === 0) {
            answer += &quot;박&quot;;
        } else {
            answer += &quot;수&quot;;
        }
    }
    return answer;
}
// =&gt; 테스트 통과</code></pre><p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 콜라츠 추측 ]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-JavaScript-%EC%BD%9C%EB%9D%BC%EC%B8%A0-%EC%B6%94%EC%B8%A1</link>
            <guid>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-JavaScript-%EC%BD%9C%EB%9D%BC%EC%B8%A0-%EC%B6%94%EC%B8%A1</guid>
            <pubDate>Sat, 28 May 2022 03:46:27 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-콜라츠-추측span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [콜라츠 추측]</span></h1>
<h3 id="문제설명">문제설명</h3>
<p>1937년 Collatz란 사람에 의해 제기된 이 추측은, 주어진 수가 1이 될때까지 다음 작업을 반복하면, 모든 수를 1로 만들 수 있다는 추측입니다. 작업은 다음과 같습니다.</p>
<pre><code>1-1. 입력된 수가 짝수라면 2로 나눕니다. 
1-2. 입력된 수가 홀수라면 3을 곱하고 1을 더합니다.
2. 결과로 나온 수에 같은 작업을 1이 될 때까지 반복합니다.</code></pre><h4 id="제한-사항">제한 사항</h4>
<ul>
<li>입력된 수, <code>num</code>은 1 이상 8000000 미만인 정수입니다.</li>
</ul>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th align="center">n</th>
<th align="center">result</th>
</tr>
</thead>
<tbody><tr>
<td align="center">6</td>
<td align="center">8</td>
</tr>
<tr>
<td align="center">16</td>
<td align="center">4</td>
</tr>
<tr>
<td align="center">626331</td>
<td align="center">-1</td>
</tr>
</tbody></table>
<h4 id="입출력-예-설명">입출력 예 설명</h4>
<p>입출력 예 #1
문제의 설명과 같습니다.</p>
<p>입출력 예 #2
16 -&gt; 8 -&gt; 4 -&gt; 2 -&gt; 1 이되어 총 4번만에 1이 됩니다.</p>
<p>입출력 예 #3
626331은 500번을 시도해도 1이 되지 못하므로 -1을 리턴해야합니다.</p>
<h3 id="제출답안">제출답안</h3>
<pre><code>function solution(num) {
    let answer = 0;

    while (num !== 1) {
        if (num % 2 === 0){
            num /= 2;
        } else {
            num = (num * 3) + 1;
        }
        answer += 1;
    }

    if (answer &gt;= 500) {
        return -1;
    } else {
        return answer;
    }
}

// =&gt; 테스트 통과!!</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[React & Redux로 로그인 웹페이지 만들기]]></title>
            <link>https://velog.io/@sweet_pumpkin/Megabyte-School-React-Redux%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/Megabyte-School-React-Redux%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sat, 28 May 2022 01:54:27 GMT</pubDate>
            <description><![CDATA[<p>Megabyte School React 과정에서 API를 통해 가져온 정보로 로그인 페이지를 만들어보는 실습 과제를 수행했다.</p>
<p>API를 사용하는 방법은 독학이나 이번 과정에서 몇 번 해봤지만 아직까지 마냥 막막하고 손에 익지 않은 것 같다. 이때문에 이번 과제를 수행하는데 많이 해맸던 것 같다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/4c7dd8fd-9bcd-4a5f-82e6-52a2514b3deb/image.jpeg" alt=""></p>
<blockquote>
<p><em>API... Reat... Redux 다 배운건데 어떻게 하더라...</em></p>
</blockquote>
<p>알듯 말듯 될 듯 말듯 안되는 상황 속에 고전하고 있던 차에 수강생 한 분 께서 전체적으로 정리를 해주셨다. 덕분에 무사히 어떤 부분에서 이해가 부족했는지 알 수 있었고, 성공적으로 과제를 마칠 수 있었다.</p>
<h1 id="span-stylecolor-5fc69a프로젝트-준비하기span"><span style="color: #5fc69a">프로젝트 준비하기</span></h1>
<p>실습 과제는 아래와 같은 구조로 되어 있는 프로젝트다. <code>LoginComponent.jsx</code>는 아이디와 비밀번호를 입력할 수 있는 로그인 화면,<code>MyPage.jsx</code>는 로그인이 성공했을 경우 
나타나는 페이지다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/b42c1e31-fc5e-4711-8b29-7bd04320cd94/image.png" alt=""></p>
<h4 id="실습-목표는-다음과-같다">실습 목표는 다음과 같다.</h4>
<ul>
<li>아이디와 비밀번호를 <em>state</em>, _input_으로 관리할 것.</li>
<li>로그인 버튼 클릭 시 빈 값이 하나라도 있다면, <code>alert()</code>로 _submit event_를 종료시킬 것.</li>
<li>_axios_를 통신하는 동안 버튼이 클릭되지 않도록 할 것.</li>
<li><em>axios</em> 통신이 끝난 후 1.5초까지 &#39;Loading...&#39;을 출력할 것. </li>
</ul>
<h4 id="유저-정보는-다음과-같다">유저 정보는 다음과 같다.</h4>
<ul>
<li>ID : fastcampus</li>
<li>PW : 1234</li>
</ul>
<h4 id="api-명세서는-다음과-같다">API 명세서는 다음과 같다.</h4>
<ul>
<li><p>반드시 body(obj)를 같이 보내줄 것.</p>
<ul>
<li>body.id : user.id</li>
<li>body.pw : user.pw</li>
</ul>
</li>
<li><p>로그인 실패시</p>
<ul>
<li>code : 400 = body 값이 비어 있을 때</li>
<li>code : 401 = 존재하지않는 id일 때</li>
<li>code : 402 = 비밀번호가 틀렸을 때</li>
</ul>
</li>
<li><p>로그인 성공 시</p>
<ul>
<li>code : 200</li>
<li>useInfo = object, user의 데이터가 넘어옴.</li>
</ul>
</li>
</ul>
<h1 id="span-stylecolor-5fc69amainjsxspan"><span style="color: #5fc69a">main.jsx</span></h1>
<p>main.jsx에 Provider와 store를 아래와 같이 설정해준다.</p>
<pre><code>import React from &#39;react&#39;
import ReactDOM from &#39;react-dom/client&#39;
import App from &#39;./App&#39;

import { Provider } from &quot;react-redux&quot;;
import store from &quot;./reducer/store.js&quot;;

ReactDOM.createRoot(document.getElementById(&#39;root&#39;)).render(
    &lt;Provider store={store}&gt;
      &lt;App /&gt;
    &lt;/Provider&gt;
)</code></pre><h1 id="span-stylecolor-5fc69astorejsspan"><span style="color: #5fc69a">store.js</span></h1>
<p><code>configureStore()</code>로 스토어를 만들어 준다. 리듀서는 userSlice.js에서 가져와주자. </p>
<pre><code>import { configureStore } from &quot;@reduxjs/toolkit&quot;;
import userSlice from &quot;./userSlice&quot;;

export default configureStore({
  reducer: {
    user: userSlice,
  },
  middleware: (getDefaultMiddleware) =&gt;
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});</code></pre><h1 id="span-stylecolor-5fc69alogincomponentjsxspan"><span style="color: #5fc69a">LoginComponent.jsx</span></h1>
<p>먼저 로그인 컴포넌트를 완성해주자. 제일 먼저 아이디 input과 비밀번호 input, 그리고 로그인 버튼을 만든다. 그리고 input에 입력한 값은 <code>useState()</code>로 관리한다. 그 다음 버튼을 클릭했을때, axios를 이용해 서버와 통신하여 입력한 input 값이 정확한지 확인하도록 해야한다.</p>
<pre><code>// API
let body = {
    id,
    password
}
axios.post(LOGIN_API_KEY, body).then(res =&gt;{***})</code></pre><p>서버와 통신하기 위해서 위와 같은 코드를 작성한다. body는 id와 password 값을 받아서 서버로 전달해준다. 현재 아이디 input의 값과 비밀번호 input의 값은 각각 id와 password이란 이름으로 지정했으므로, body가 필요한 값과 input 값의 이름이 같다. 따라서 <code>id = id</code>, <code>password = password</code>이므로 위와 같이 하나씩만 작성하는 것이 가능하다.</p>
<p>body에 담긴 값은 <code>axios.post()</code>를 통해 <code>LOGIN_API_KEY</code>로 전달된다. 그리고 그 정보는 <code>then()</code>을 통해서 res로 받을 수 있다. <code>console.log(res)</code>를 해본다면 아래와 같은 정보가 뜰 것이다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/ba2a8f51-2615-4cf6-8be7-4618cb91899a/image.png" alt=""></p>
<p>우리가 필요한 것은 data 부분인데, 현재 input에는 아무것도 입력하지 않았기 때문에 code : 400이 뜬 것을 볼 수 있다. 이것을 가지고 다음 화면으로 넘어갈지, submit 이벤트를 종료할지 선택할 수 있다. 코드가 400대라면 <code>alert()</code>를 제공하고 submit 이벤트를 종료 시킬 것이고, 코드가 200이라면 로그인 성공 화면으로 넘어가게 할 것이다.</p>
<p>코드가 200이 나오기 위해서는 위의 예시처럼 id 값을 fastcampus, password 값을 1234로 입력시켜줘야한다. </p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/83e46827-0302-4c54-a5e9-f246ef33f5fe/image.png" alt=""></p>
<p><code>console.log(res.data)</code>를 입력했을때 코드가 200일 경우 userInfo를 통해 추가로 정보가 제공된다. 이것을 가지고 로그인 성공화면에서 텍스트로 &quot;Welcome 패스트캠퍼스!&quot;라는 안내 문구를 제공할 것이다.</p>
<p>조건문을 통해 코드가 400대일 경우 <code>alert()</code> 후 이벤트 종료, 200일 경우 해당 정보를 <code>userDispatch()</code>를 통해 <code>userSlice.js</code>의 <code>loginUser()</code> 리듀서로 넘겨주자. axios 통신이 끝나기 전 해당 조건문이 실행되면 제대로 동작하지 않기 때문에 <code>then()</code> 안에서 작성해야 한다.</p>
<pre><code>.then(res =&gt; {
  const code = res.data.code;
  if (code === 400) {
      alert(&quot;비어있는 내용입니다.&quot;);
  } else if (code === 401) {
      alert(&quot;존재하지 않는 id입니다.&quot;);
  } else if (code === 402) {
      alert(&quot;비밀번호가 일치하지 않습니다.&quot;);
  } else {
      dispatch(loginUser(res.data.userInfo));
  }
});</code></pre><p>무사히 userInfo 값을 리듀서에 전달 한 상황이다. 이제 버튼을 클릭했을때 &quot;Loading...&quot; 문구가 출력됐다가 이벤트까 끝나면 1.5초 뒤 사라져야하며, 그동안 버튼이 클릭되지 않도록 해야 한다.</p>
<p>로딩 메세지는 <code>useEffect()</code>를 사용해도 되지만, submit 이벤트가 발생할때 출력되도록 코드를 작성했다. 이 경우 <code>then()</code> 안에 setTimeout()을 통해 로딩 메세지가 axious 통신이 끝난 이후 1.5초 뒤 사라지게 하자.</p>
<p>그리고 버튼은 <code>disabled={}</code>에 들어갈 값을 <code>useState()</code>를 통해 관리해준다. 초기 값은 <em>false</em>, submit 이벤트가 발생하면 <em>true</em>, axious 통신이 끝나면 다시 <em>false</em> 값을 지정해주자.</p>
<p>위의 설명을 통해 아래와 같이 코드를 작성해보자.</p>
<pre><code>import React, { useState } from &quot;react&quot;
import { useDispatch } from &quot;react-redux&quot;;
import { loginUser } from &quot;../reducer/userSlice.js&quot;;
import axios from &quot;axios&quot;;
import &quot;./Login.css&quot;

function LoginComponent() {
    const dispatch = useDispatch();

    const [id, setId] = useState(&quot;&quot;);
    const [password, setPassword] = useState(&quot;&quot;);

    const [loading, setLoading] = useState(false);
    const [msg, setMsg] = useState(&quot;&quot;);

    const LoginFunc = (e) =&gt; {
        e.preventDefault();
        // Loading... 메세지 출력
        setMsg(&quot;Loading...&quot;);

        // API
        let body = {
            id,
            password
        }
        axios.post(LOGIN_API_KEY, body)
        .then(res =&gt; {
            // 2순위 통신이 끝나야 작동. 통신 이후 클릭이 되도록.
            setLoading(false);
            // Loading... 메세지가 통신이 끝난 후 1.5초 이후 없어짐.
            setTimeout(() =&gt; setMsg(&quot;&quot;), 1500);
            // code = 데이터 상태
            const code = res.data.code;
            if (code === 400) {
                // 비어있는
                alert(&quot;비어있는 내용입니다.&quot;)
            } else if (code === 401) {
                // 존재하지 않는 id
                alert(&quot;존재하지 않는 id입니다.&quot;)
            } else if (code === 402) {
                // 비밀번호가 틀렸을때
                alert(&quot;비밀번호가 일치하지 않습니다.&quot;)
            } else {
                dispatch(loginUser(res.data.userInfo));
            }
        })
        // 1순위 로그인 버튼을 누르면 클릭이 안되도록.
        setLoading(true);
    }

    return (
        &lt;&gt;
            &lt;h1&gt;LoginComponent&lt;/h1&gt;
            &lt;form 
                onSubmit={LoginFunc}
                className=&quot;login-wrap&quot;
            &gt;
                &lt;input
                        type=&quot;text&quot; 
                        placeholder=&#39;아이디&#39; 
                        className=&#39;id&#39;
                        onChange={e =&gt; setId(e.target.value)}
                    /&gt;
                    &lt;br /&gt;
                    &lt;input 
                        type=&quot;password&quot; 
                        placeholder=&#39;비밀번호&#39; 
                        className=&#39;pw&#39;
                        onChange={e =&gt; setPassword(e.target.value)} 
                    /&gt;
                &lt;br /&gt;
                &lt;button
                    disabled={loading} 
                    type=&quot;submit&quot;
                    className=&#39;btn&#39;
                &gt;
                    로그인
                &lt;/button&gt;
                &lt;div
                    className=&#39;msg&#39;
                &gt;
                    {msg}
                &lt;/div&gt;
            &lt;/form&gt;
        &lt;/&gt;
    )
}

export default LoginComponent</code></pre><p>위 코드를 작성하면 아래와 같은 예제를 만들 수 있다.</p>
<h4 id="span-stylecolor-5fc69a아이디와-비밀번호를-모두-입력하지-않았을-경우span"><span style="color: #5fc69a">아이디와 비밀번호를 모두 입력하지 않았을 경우</span></h4>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/f0331532-a515-45dd-8437-5d35c6ee14dc/image.gif" alt=""></p>
<h4 id="span-stylecolor-5fc69a아이디는-정확하지만-비밀번호가-틀렸을-경우span"><span style="color: #5fc69a">아이디는 정확하지만 비밀번호가 틀렸을 경우</span></h4>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/4b08c81f-1663-4046-a7d7-bd68384b1664/image.gif" alt=""></p>
<h4 id="span-stylecolor-5fc69a비밀번호는-정확하지만-아이디가-틀렸을-경우span"><span style="color: #5fc69a">비밀번호는 정확하지만 아이디가 틀렸을 경우</span></h4>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/e8315d61-c7f7-4d0d-b084-ca23c1e03dfa/image.gif" alt=""></p>
<h1 id="span-stylecolor-5fc69auserslicejsspan"><span style="color: #5fc69a">userSlice.js</span></h1>
<p>userSlice.js에서는 <code>loginUser()</code>를 통해 초기 상태 값에 LoginComponent.jsx에서 받아온 userInfo 값을 집어 넣어 줄 것이고, <code>clearUser()</code>를 통해 받아온 값을 다시 비워주게 할 것이다.</p>
<p>먼저 <code>createSlice()</code>로 리듀서를 만들고 초기값을 다음과 같이 작성한다.</p>
<pre><code>initialState: {
  name: &quot;&quot;,
  id: &quot;&quot;,
  isLoading: false, // optional
  isLogin: null,
 },</code></pre><p>LoginComponent.jsx에서 받아온 userInfo의 값은 액션의 payload에 담겨져있다. 이것을 
아래와 같이 <code>loginUser()</code>에서 initialState의 name과 id에 각각 넣어주자, 그리고 <code>clearUser</code>에는 넣어준 값을 다시 빈 값으로 만든다.</p>
<pre><code>import { createSlice } from &quot;@reduxjs/toolkit&quot;;

export const userSlice = createSlice({
    name: &quot;user&quot;,
    initialState: {
        name: &quot;&quot;,
        id: &quot;&quot;,
        isLoading: false, // optional
        isLogin: null,
    },
    reducers: {
        // login 성공 시
        loginUser: (state, action) =&gt; {
            // name, id에 API 값 받아오기
            state.name = action.payload.name;
            state.id = action.payload.id;
            // state 변화를 알림
            return state;
        },
        // login 실패 시
        clearUser: (state) =&gt; {
            // name, id 값을 비워줌.
            state.name = &quot;&quot;;
            state.id = &quot;&quot;;
            // state 변화를 알림
            return state;
        },
    },
});

export const { loginUser, clearUser } = userSlice.actions;
export default userSlice.reducer;</code></pre><h1 id="span-stylecolor-5fc69aappjsxspan"><span style="color: #5fc69a">App.jsx</span></h1>
<p>LoginComponent.jsx와 Mypage.jsx 모두 App.jsx를 통해 화면에 출력되도록 구조를 설정했다. 그 이유는 App.jsx에서 특정 조건이 일어날 경우 한 컴포넌트만을 출력할 수 있도록 설정하기 위해서다. 아까 userSlice.js에서는 <code>loginUser()</code>가 실행될 경우 userInfo의 값이 state에 적용될 것이고, <code>clearUser()</code>가 실행된다면 state가 초기 값과 같은 빈 값으로 설정될 것이다. 이것을 아래와 같이 삼항연산자로 App.jsx에 작성하면 원하는 조건에 원하는 컴포넌트가 출력된다. 현재 state에서 변동이 일어나는 것은 id와 name이므로, 둘 중 하나를 선택해 작성한다.</p>
<pre><code>import React from &#39;react&#39;;
import { useSelector } from &quot;react-redux&quot;;
// import { BrowserRouter, Routes, Route } from &#39;react-router-dom&#39;;

import LoginComponent from &#39;./component/LoginComponent&#39;;
import MyPage from &#39;./component/MyPage&#39;;

function App() {
  const user = useSelector(state =&gt; state.user);

  return (
    &lt;&gt;
    {user.id !== &quot;&quot; ? &lt;MyPage /&gt; : &lt;LoginComponent /&gt;}
    &lt;/&gt;
  )
}

export default App
</code></pre><h1 id="span-stylecolor-5fc69amypagejsxspan"><span style="color: #5fc69a">MyPage.jsx</span></h1>
<p>Mypage.jsx에서는 <code>useSelector()</code>를 사용해서 userSlice.js에 있는 name 값을 가져와서 출력하고, <code>useDispatch()</code>를 통해서 버튼을 클릭하면 <code>clearUser()</code>를 실행시켜 값을 비워줘야 한다.</p>
<pre><code>import React from &#39;react&#39;
import { useSelector, useDispatch } from &quot;react-redux&quot;;
import { clearUser } from &quot;../reducer/userSlice.js&quot;;
import &quot;./MyPage.css&quot;

function MyPage() {
    const user = useSelector((state) =&gt; state.user);
    const dispatch = useDispatch();

    const LogoutFunc = () =&gt; {
        dispatch(clearUser(user));
    }
    return (
        &lt;&gt;
            &lt;h1&gt;MyPage&lt;/h1&gt;
            &lt;p&gt;{`${user.name}(${user.id})`}님, 안녕하세요!&lt;/p&gt;
            &lt;button onClick={() =&gt; LogoutFunc()}&gt;로그아웃&lt;/button&gt;
        &lt;/&gt;
    )
}

export default MyPage</code></pre><p>위의 코드를 작성했을 경우 아래와 같은 예제가 출력된다.
<img src="https://velog.velcdn.com/images/sweet_pumpkin/post/4347d0fc-49e1-48f3-b9ab-c161d920ca39/image.gif" alt=""></p>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[최고 리덕스야 고맙다! Redux & Redux Toolkit 알아보기]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EB%AC%B4%EC%9E%91%EC%A0%95%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EC%B5%9C%EA%B3%A0-%EB%A6%AC%EB%8D%95%EC%8A%A4%EC%95%BC-%EA%B3%A0%EB%A7%99%EB%8B%A4-Redux-Redux-Toolkit-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/%EB%AC%B4%EC%9E%91%EC%A0%95%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EC%B5%9C%EA%B3%A0-%EB%A6%AC%EB%8D%95%EC%8A%A4%EC%95%BC-%EA%B3%A0%EB%A7%99%EB%8B%A4-Redux-Redux-Toolkit-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 25 May 2022 06:07:38 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolor-5fc69awhat-is-reduxspan"><span style="color: #5fc69a">What is Redux?</span></h1>
<p>리덕스는 상태 관리 라이브러리 중 하나로, 현재까지 가장 많이 쓰이고 있다. </p>
<p>상태 관리란 UI와 UX에 맞게 데이터를 관리하거나, 서버와 주고 받는 데이터를 관리하는 것을 말한다.</p>
<p>간단한 프로젝트라면 괜찮겠지만, 복잡하고 크기가 큰 대형 프로젝트라면 상태 관리의 난도는 크게 올라간다. 리액트의 state 끌어올리기를 생각해보자. 한 컴포넌트의 함수를 props 형태로 다른 컴포넌트로 전달하고 또 그것을 props 형태로 또 다른 컴포넌트로 전달하고... 이런 복잡한 구조는 불필요한 props 전달로 유지보수 또는 props 추적을 힘들게하는 <span style="color: #5fc69a">Props drilling</span>을 야기한다.</p>
<p>상태 관리 라이브러리는 이러한 문제들을 해결하기 위해 고안되었다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/4b69bb9b-26ce-4c95-b04c-b79dd3bf1b07/image.gif" alt=""></p>
<p>위의 그림은 기존 상태 관리 방식을 시각화한 것이다. 위 그림처럼 상태 변경이 한 번 일어났을 뿐인데 해당 데이터를 변경하기 위해 또 다른 상태 변경이 여러 번 일어나고 있다. 이러한 구조는 오류를 야기할 수 있으며, 발생한 오류를 잡아내기도 쉽지 않다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/ed1d68d9-3b90-4c95-8b54-ff3e5c6e7d47/image.gif" alt=""></p>
<p>그러나 리덕스는 Reducer와 Store를 통해 상태 변경 과정을 간소화하여 기존의 문제를 해결했다. 이것은 복잡한 상태 관리를 매우 효율적이고 간편하게 변경하여 오류 발생률을 낮추게 한다.</p>
<h1 id="span-stylecolor-5fc69aredux-기본-용어span"><span style="color: #5fc69a">Redux 기본 용어</span></h1>
<p>리덕스의 기본 용어에 대해서 알아보자.</p>
<h3 id="span-stylecolor-5fc69astorespan"><span style="color: #5fc69a">Store</span></h3>
<p>스토어는 컴포넌트의 상태를 관리하는 저장소다. 하나의 프로젝트는 하나의 스토어만 가질 수 있다.</p>
<h3 id="span-stylecolor-5fc69aactionspan"><span style="color: #5fc69a">Action</span></h3>
<p>스토어의 상태를 변경하기 위해서는, 액션을 생성해야한다. 액션은 객체이며, 반드시 type을 가져야 한다. 액션 객체는 액션생성함수에 의해서 만들어진다.</p>
<h3 id="span-stylecolor-5fc69areducerspan"><span style="color: #5fc69a">Reducer</span></h3>
<p>리듀서는 현재 상태와 액션 객체를 받아 새로운 상태를 리턴하는 함수다. </p>
<h3 id="span-stylecolor-5fc69adispatchspan"><span style="color: #5fc69a">Dispatch</span></h3>
<p>디스패치는 스토어의 내장 함수 중 하나이며, 액션 객체를 넘겨줘 상태를 업데이트 시켜주는 역할을 한다.</p>
<h3 id="span-stylecolor-5fc69asubscribespan"><span style="color: #5fc69a">Subscribe</span></h3>
<p>스토어의 내장 함수 중 하나로, 리듀서가 호출될 때 서브스크라이브된 함수 및 객체를 호출한다.</p>
<p>아래의 그림으로 위의 용어들을 이해해보자.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/53df187d-3402-4ec1-be28-1fd13dcbba57/image.gif" alt=""></p>
<ol>
<li><p>UI가 처음 렌더링될 때, UI 컴포넌트는 리덕스 스토어의 상태에 접근하여 해당 상태를 렌더링한다. </p>
</li>
<li><p>이후 UI에서 상태가 변경되면, 앱은 디스패치를 실행해 액션을 일으킨다. </p>
</li>
<li><p>새로운 액션을 받은 스토어는 리듀서를 실행하고 리듀서를 통해 나온 값을 새로운 상태로 저장한다.</p>
</li>
<li><p>서브스크라이브된 UI은 상태 업데이트로 변경된 데이터를 새롭게 렌더링한다.</p>
</li>
</ol>
<h1 id="span-stylecolor-5fc69aredux-사용하기span"><span style="color: #5fc69a">Redux 사용하기</span></h1>
<p>리덕스를 사용하기 위해서는 먼저 리덕스를 설치해야 한다.</p>
<pre><code>$ npm install redux</code></pre><p>리액트 환경이라면 추가로 <code>react-redux</code>를 설치해야한다.</p>
<pre><code>$ npm install redux react-redux</code></pre><h3 id="span-stylecolor-5fc69astore-만들기span"><span style="color: #5fc69a">Store 만들기</span></h3>
<p>리덕스 설치가 완료되었다면, 스토어를 만들어 주자. 스토어는 <code>createStore()</code>를 통해 만들 수 있다.</p>
<pre><code>import { createStore } from &#39;redux&#39;;</code></pre><p>그러나 리덕스 4버전 이상일 경우 아래와 같은 경고 메세지가 나타난다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/33583da6-56e2-4d0d-8473-1b109ba86a70/image.png" alt=""></p>
<p>경고 메세지는 스토어를 만들때 리덕스의 <code>createStore()</code>를 사용하기보단, 리덕스 툴킷의 <code>configureStore()</code>를 사용할 것을 요구한다.</p>
<p>리덕스 툴킷을 이용해 기존 리덕스 문법보다 훨씬 쉬운 방법으로 상태 관리를 할 수 있는데, 리덕스 개발사 쪽에서 리덕스 툴킷을 쓰라고 하는 것 같다.</p>
<h1 id="span-stylecolor-5fc69aredux-toolkit-사용하기span"><span style="color: #5fc69a">Redux Toolkit 사용하기</span></h1>
<p>리액트 환경에서 아래의 카운트업 코드를 리덕스 툴킷을 통해 만들어 보자.</p>
<pre><code>// App.jsx

import React, { useState } from &#39;react&#39;

export default function App() {
  const [counter, setCounter] = useState(0);

  const minus = () =&gt; {
    setCounter(prev =&gt; prev - 1);
  }
  const plus = () =&gt; {
    setCounter(prev =&gt; prev + 1);
  }

  return (
    &lt;div&gt;
      &lt;button onClick={minus}&gt;-&lt;/button&gt;
      Value: { counter } 
      &lt;button onClick={plus}&gt;+&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre><p>리덕스 툴킷 역시 설치가 필요하다.</p>
<pre><code>npm install @reduxjs/toolkit</code></pre><p>리액트 환경이라면 <code>react-redux</code> 설치가 필요하다.</p>
<h3 id="span-stylecolor-5fc69astore-연동하기span"><span style="color: #5fc69a">Store 연동하기</span></h3>
<p><code>Provider</code>는 <code>react-redux</code>에서 리액트 앱에 스토어를 연동할 수 있게 해주는 컴포넌트다. 아래와 같이 <code>Provider</code> 컴포넌트를 불러와 연동할 컴포넌트를 감싸준 뒤, <code>Provider</code>의 props로 사용할 스토어를 지정해주면 된다.</p>
<pre><code>import React from &#39;react&#39;
import ReactDOM from &#39;react-dom/client&#39;
import App from &#39;./App&#39;

import { Provider } from &quot;react-redux&quot;;
import store from &quot;./store/store.js&quot;;

ReactDOM.createRoot(document.getElementById(&#39;root&#39;)).render(
    &lt;Provider store={store}&gt;
      &lt;App /&gt;
    &lt;/Provider&gt;
)
</code></pre><h3 id="span-stylecolor-5fc69astore-만들기span-1"><span style="color: #5fc69a">Store 만들기</span></h3>
<p>먼저 store.js에 <code>configureStore()</code>로 스토어를 만들어주자.</p>
<pre><code>// store.js

import { configureStore } from &#39;@reduxjs/toolkit&#39;;

export const store = configureStore({
    reducer: counterSlice,
    middleware: [...middlewares]
})</code></pre><p>기존 리덕스에서는 스토어 생성 후 미들웨어가 한 개 이상이라면, 여러 메서드를 통해 긴 코드를 작성해야 했다.</p>
<p><code>configureStore()</code>는 별도의 메서드 없이 바로 미들웨어를 추가할 수 있다는 장점이 있다.</p>
<h3 id="span-stylecolor-5fc69acreateslice-생성span"><span style="color: #5fc69a">createSlice 생성</span></h3>
<p>기존 리덕스에서는 액션을 디스패치하기 위한 별도의 함수가 필요했고, 액션의 객체를 리듀서를 통해 리턴하는 구조였다.</p>
<p><code>createSlice()</code>는 액션에 대한 함수 설정과 리듀서를 따로 생성하지 않아도 된다.</p>
<p>아래는 <code>createSlice()</code>를 사용한 카운트업 코드와 그에 대한 설명이다. </p>
<pre><code>// countSlice.jsx

import { createSlice } from &#39;@reduxjs/toolkit&#39;
export const counterSlice = createSlice({
  name: &#39;counter&#39;,
  initialState: { value: 0 },
  reducers: {
    plus: state =&gt; {
      state.value += 1
    },
    minus: state =&gt; {
      state.value -= 1
    },
  },
})

export const { plus, minus } = counterSlice.actions; 
export default counterSlice.reducer;</code></pre><ul>
<li>initialState를 통해 state의 처음 상태를 정의한다.</li>
<li>reducers에서 액션을 설정한다.</li>
<li>_plus_와 _mius_를 export해서 _App.jsx_에 import한다.</li>
<li>slice는 <code>slice.reducer</code>로 내보낸다. _store.js_는 위 파일을 전부 리듀서로 받는다.</li>
</ul>
<h3 id="span-stylecolor-5fc69auseselector-usedispatch로-상태-접근하기span"><span style="color: #5fc69a">useSelector, useDispatch로 상태 접근하기</span></h3>
<p><code>useSelector()</code>는 기존 리덕스의 <code>connect()</code>를 이용하지 않고 리덕스의 상태를 조회할 수 있다. </p>
<p><code>useDispatch()</code>는 생성한 액션을 발생시키며, 액션생성 함수를 가져온다. 위 설명의 디스패치와 같다고 보면 된다.</p>
<p>아래는 <code>useSelector()</code>와 <code>useDispatch()</code>에 대한 설명이다.</p>
<pre><code>// App.jsx

import React from &#39;react&#39;
import { useDispatch, useSelector } from &#39;react-redux&#39;;
import { plus, minus } from &#39;./counter/countSlice01&#39;;

export default function App() {
  const count = useSelector(state =&gt; state.counter.value);
  const dispatch = useDispatch();

  return (
    &lt;div&gt;
      &lt;button onClick={() =&gt; dispatch(minus())}&gt;-&lt;/button&gt;
      Value: { count } 
      &lt;button onClick={() =&gt; dispatch(plus())}&gt;+&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre><ul>
<li><code>useSelector()</code>로 스토어에서 현재 상태 값을 가져온다.</li>
<li><code>useDispatch()</code>를 통해 변경되는 값을 스토어로 전달한다.</li>
</ul>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[훅. 후훅. 리. 리액트 훅. 후훅. 훅. 리. 리액트 훅 알아보기]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EB%AC%B4%EC%9E%91%EC%A0%95-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%ED%9B%85.-%ED%9B%84%ED%9B%85.-%EB%A6%AC.-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85.-%ED%9B%84%ED%9B%85.-%ED%9B%85.-%EB%A6%AC.-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/%EB%AC%B4%EC%9E%91%EC%A0%95-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%ED%9B%85.-%ED%9B%84%ED%9B%85.-%EB%A6%AC.-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85.-%ED%9B%84%ED%9B%85.-%ED%9B%85.-%EB%A6%AC.-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Tue, 24 May 2022 14:13:37 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolor-5fc69awhat-is-react-hookspan"><span style="color: #5fc69a">What is React Hook?</span></h1>
<p>React 16.8버전부터 새롭게 추가된 Hook은 기존 class를 사용하지 않고, React state와 lifecycle features를 연동할 수 있게 해주는 함수다.</p>
<p>기존 class를 사용한 React는 코드의 재사용성이 낮고, 작성하기 까다로우며, reloading이 신뢰하기 어렵다.</p>
<p>이러한 class가 가진 문제들을 함수형 컴포넌트 기능을 사용해 해결할 수 있는데, 함수형 컴포넌트는 state나 lifecycle을 직접 다룰 수 없다. </p>
<p>Hook은 함수형 컴포넌트가 class 컴포넌트의 역할을 할 수 있게 해준다.</p>
<h1 id="span-stylecolor-5fc69ausestatespan"><span style="color: #5fc69a">useState</span></h1>
<pre><code>import React, { useState } frome &#39;react&#39;;
const [value, setValue] = useState(initialValue);</code></pre><p><code>useState()</code>는 함수형 컴포넌트에서도 가변적인 state를 지닐 수 있게 하는 Hook이다. <code>useState()</code>는 길이가 2인 배열을 반환하는데, 첫번째는 상태 값, 두 번째는 상태를 업데이트하는 함수이다. </p>
<p>_initalValue_는 초깃값으로 최초 렌더링 시에 한 번 사용된다. 해당 함수에 파라미터를 넣어 호출하면, 전달받은 바라미터 값이 바뀌고 컴포넌트는 정상적으로 리렌더링된다.</p>
<p>아래는 <code>useState()</code>를 사용해 만든 카운트업 예제</p>
<pre><code>import React, { useState } from &#39;react&#39;;

export default function App() {
  const [count, setCount] = useState(0);
  return (
    &lt;div&gt;
      {`count: ${count}`}
      &lt;button onClick={() =&gt; setCount(prev =&gt; prev + 1)}&gt;+&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre><p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/b28c1dfb-be28-4e3f-9017-8b0250ddb057/image.gif" alt=""></p>
<h1 id="span-stylecolor-5fc69auserefspan"><span style="color: #5fc69a">useRef</span></h1>
<pre><code>import React, { useRef } from &#39;react&#39;;
const valueRef = useRef(initalValue);</code></pre><p>자바스크립트의 <code>querySelector()</code> 또는 <code>getElememtById()</code> 같이 DOM을 선택할때 사용하는 Hook이다.</p>
<p>React 컴포넌트에서 state는 상태를 바꾸는 함수를 호출한 이후 업데이트 된 상태를 조회할 수 있으나, <code>useRef()</code>를 사용할 경우 바로 조회가 가능하다.</p>
<p>아래는 <code>useState()</code>와 <code>useRef()</code>로 input에 입력한 value를 리셋하고 input 창을 포커싱하는 예제.</p>
<pre><code>import React, { useState, useRef } from &#39;react&#39;

export default function Practice09() {
  const input = useRef(null);
  const [text, setText] = useState(&quot;&quot;);

  const handleClick = () =&gt; {
    setText(&quot;&quot;);
    input.current.focus();
  }

  return (
    &lt;div&gt;
      &lt;span&gt;현재 value는 { text }입니다.&lt;/span&gt;&lt;br /&gt;
      &lt;input type=&quot;text&quot; ref={ input } value={ text } onChange={(e) =&gt; setText(e.target.value)} /&gt;
      &lt;button onClick={handleClick}&gt;RESET&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre><p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/ac27fceb-67e0-48f9-ad16-27eda6574a3a/image.gif" alt=""></p>
<h1 id="span-stylecolor-5fc69auseeffectspan"><span style="color: #5fc69a">useEffect</span></h1>
<pre><code>import React, { useEffect } from &#39;react&#39;;
useEffect(() =&gt; {}, []);</code></pre><p>컴포넌트가 렌더링 될 때마다 특정 작업을 실행하게 하는 Hook.</p>
<p><code>useEffect()</code>의 매개변수는 익명함수와 빈배열 두 가지 요소를 가진다. 배열은 빈 배열일때 처음 렌더링에만 실행되며, 값이 있으면 특정 상태 값이 업데이트 될 때만 실행된다. 마지막으로, 아예 배열이 생략된다면 리렌더링 될때마다 실행된다.</p>
<p>컴포넌트가 언마운트 되기전 또는 업데이트 직전 어떤 작업을 수행하고 싶다면, clean-up 함수를 반환해야한다. </p>
<p>clean-up 함수는 컴포넌트가 마운트 되기 전과 업데이트 전에 실행되며, 업데이트 전에 실행될 경우 업데이트 직전 state 값에 접근이 가능하다.</p>
<p>아래는 <code>useState()</code>와 <code>useEffect()</code>를 사용해 input 값이 3초 뒤 나오게 하는 예제로, <code>useEffect()</code>에 clean-up 함수를 넣어 value 값이 마지막 값으로만 나오게 설정했다.</p>
<pre><code>import React, { useState, useEffect } from &#39;react&#39;

export default function App() {
  const [value, setValue] = useState(&#39;&#39;);
  const [num, setNum] = useState(&quot;값을 입력하세요.&quot;);
  const [res, setRes] = useState(&quot;&quot;);
  useEffect(() =&gt; {
    if (value === &quot;&quot;) {
      return setNum(&quot;값을 입력하세요.&quot;)
    } else {
      const a = setTimeout(() =&gt; setNum(&quot;3초 뒤 실행됩니다.&quot;), 0);
      const b = setTimeout(() =&gt; setNum(&quot;2초 뒤 실행됩니다.&quot;), 1000);
      const c = setTimeout(() =&gt; setNum(&quot;1초 뒤 실행됩니다.&quot;), 2000);
      const d = setTimeout(() =&gt; setNum(&quot;실행 완료&quot;), 3000);
      const e = setTimeout(() =&gt; setRes(value), 3000);
      return () =&gt; {
        clearTimeout(a);
        clearTimeout(b);
        clearTimeout(c);
        clearTimeout(d);
        clearTimeout(e);
      }
    }
  }, [value]);
  return (
    &lt;&gt;
      &lt;input 
        type=&quot;number&quot;
        onChange={(e) =&gt; {setValue(e.target.value)}}
      /&gt;
      &lt;br /&gt;
      { num }
      &lt;br/&gt;
      {`예상 값: ${ value }`}
      &lt;br /&gt;
      {`출력 값: ${ res }`}
    &lt;/&gt;
  )
}
</code></pre><p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/f9da9714-28e7-4a78-8bf8-41434b7bde80/image.gif" alt=""></p>
<h1 id="span-stylecolor-5fc69ausememospan"><span style="color: #5fc69a">useMemo</span></h1>
<pre><code>import React, { useMemo } from &#39;react&#39;;
const memoizationValue = useMemo(() =&gt; 함수, 배열);</code></pre><p>React에서 컴포넌트의 렌더링은 계속해서 일어나고 있다. 간단한 구조라면 무리없이 재렌더링이 가능할 것이다. 그러나 복잡한 수식을 계산하는 등 리턴 값이 수초 이상 걸리는 구조라면 재렌더링할때마다 해당 수식이 다시 계산되는 일이 발생하며 UI에 지속적으로 지연이 발생된다. </p>
<p>이같은 상황은 memoization 기법을 활용하면 해결할 수 있다.</p>
<p>memoization은 기존에 수행한 연산의 결과값을 어딘가에 저장해두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법을 말한다. 중복 연산을 피할 수 있기 때문에 성능을 최적화할 수 있다는 장점이 있다.</p>
<p><code>useMemo()</code>는 두 개의 인자를 받는다. 첫 번째는 결과값을 생성해주는 팩토리 함수이며, 두 번재는 기존 결과값을 재활용하는 입력값 배열이다.</p>
<h1 id="span-stylecolor-5fc69ausecallbackspan"><span style="color: #5fc69a">useCallback</span></h1>
<pre><code>import React, { useCallback } from &#39;react&#39;;
const callBackValue = useCallback(() =&gt; 함수, 배열)</code></pre><p><code>useCallback()</code>은 함수를 memoization된 값을 반환하여 계속해서 동일한 값을 보내주는 역할을 한다. </p>
<p><code>useCallback()</code>은 두 개의 인자를 가지며, 첫 번째는 인라인 콜백, 두 번째는 의존성 값의 배열을 받게 된다. 배열에 변경을 감지해야할 값을 넣어주면, 등록한 함수가 변경될 때마다 새로운 콜백함수를 생성하게 된다.</p>
<p>이것은 최적화된 자식 컴포넌트에 props로 콜백 함수를 내려줄 때 유용하다.</p>
<p><code>useMemo()</code>는 memoization된 값을 반환하여 동일 계산 반복수행을 최소화시켜주는 반면, <code>useCallback()</code>은 memoization된 콜백을 반환하여 새로운 함수가 생성되는 것을 줄이는 역할을 한다는 차이점이 있다.</p>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 자릿수 더하기]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-Javascript-%EC%9E%90%EB%A6%BF%EC%88%98-%EB%8D%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-Javascript-%EC%9E%90%EB%A6%BF%EC%88%98-%EB%8D%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 13 May 2022 23:33:24 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-자릿수-더하기span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [자릿수 더하기]</span></h1>
<h3 id="문제설명">문제설명</h3>
<p>자연수 N이 주어지면, N의 각 자릿수의 합을 구해서 return 하는 solution 함수를 만들어 주세요.
예를들어 N = 123이면 1 + 2 + 3 = 6을 return 하면 됩니다.</p>
<h4 id="제한사항">제한사항</h4>
<ul>
<li>N의 범위 : 100,000,000 이하의 자연수</li>
</ul>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th>N</th>
<th>answer</th>
</tr>
</thead>
<tbody><tr>
<td>123</td>
<td>6</td>
</tr>
<tr>
<td>987</td>
<td>24</td>
</tr>
</tbody></table>
<h4 id="입출력-예-설명">입출력 예 설명</h4>
<p>입출력 예 #1
문제의 예시와 같습니다.</p>
<p>입출력 예 #2
9 + 8 + 7 = 24이므로 24를 return 하면 됩니다.</p>
<h3 id="제출답안오답">제출답안(오답)</h3>
<pre><code>function solution(n) {
    return String(n).split(&quot;&quot;).reduce((a, b) =&gt; Number(a) + Number(b));
}</code></pre><h3 id="풀이">풀이</h3>
<pre><code>function solution(n) {
    return String(n).split(&quot;&quot;).reduce((a, b) =&gt; Number(a) + Number(b), 0);
}</code></pre><p><code>reduce()</code> 메서드의 initialValue를 입력하지 않아 테스트를 통과하지 못했다. 꼭 써주도록하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[5월 2주차 코딩 테스트 문제 풀이 리뷰]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-5%EC%9B%94-2%EC%A3%BC%EC%B0%A8-%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%A6%AC%EB%B7%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-5%EC%9B%94-2%EC%A3%BC%EC%B0%A8-%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4-%EB%A6%AC%EB%B7%B0</guid>
            <pubDate>Fri, 13 May 2022 23:19:01 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-이상한-문자-만들기span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [이상한 문자 만들기]</span></h1>
<h3 id="문제설명">문제설명</h3>
<p>문자열 s는 한 개 이상의 단어로 구성되어 있습니다. 각 단어는 하나 이상의 공백문자로 구분되어 있습니다. 각 단어의 짝수번째 알파벳은 대문자로, 홀수번째 알파벳은 소문자로 바꾼 문자열을 리턴하는 함수, solution을 완성하세요.</p>
<h4 id="제한사항">제한사항</h4>
<ul>
<li>문자열 전체의 짝/홀수 인덱스가 아니라, 단어(공백을 기준)별로 짝/홀수 인덱스를 판단해야합니다.</li>
<li>첫 번째 글자는 0번째 인덱스로 보아 짝수번째 알파벳으로 처리해야 합니다.</li>
</ul>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th align="center">s</th>
<th align="center">return</th>
</tr>
</thead>
<tbody><tr>
<td align="center">&quot;try hello world&quot;</td>
<td align="center">&quot;TrY HeLlO WoRlD&quot;</td>
</tr>
</tbody></table>
<h4 id="입출력-예-설명">입출력 예 설명</h4>
<p>&quot;try hello world&quot;는 세 단어 &quot;try&quot;, &quot;hello&quot;, &quot;world&quot;로 구성되어 있습니다. 각 단어의 짝수번째 문자를 대문자로, 홀수번째 문자를 소문자로 바꾸면 &quot;TrY&quot;, &quot;HeLlO&quot;, &quot;WoRlD&quot;입니다. 따라서 &quot;TrY HeLlO WoRlD&quot; 를 리턴합니다.</p>
<h3 id="제출답안">제출답안</h3>
<pre><code>function solution(s) {
    return s.split(&quot; &quot;).map(a =&gt; a.split(&quot;&quot;).map((b, idx) =&gt; idx % 2 === 0 ? b.toUpperCase() : b.toLowerCase()).join(&#39;&#39;)).join(&#39; &#39;);
}</code></pre><h3 id="풀이">풀이</h3>
<p>&quot;try hello world&quot;를 예로 들어보자. 공백을 기준으로 짝수 홀수가 나누어진다. 따라서 <code>split()</code>로 문장을 단어로 한 번 나누고, 단어를 글자로 한 번 더 나눠야한다. 이것을 다시 이어 붙이려면 <code>join()</code> 역시 두 번 필요하다. 또한 그 내부의 <code>map()</code> 또한 두 개 필요하다. 마지막 <code>map()</code> 메서드에서 짝수와 홀수를 나눠 각각 대문자와 소문자로 구분해주면 답안을 완성할 수 있다.</p>
<hr>
<h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-하샤드-수span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [하샤드 수]</span></h1>
<h3 id="문제설명-1">문제설명</h3>
<p>양의 정수 x가 하샤드 수이려면 x의 자릿수의 합으로 x가 나누어져야 합니다. 예를 들어 18의 자릿수 합은 1+8=9이고, 18은 9로 나누어 떨어지므로 18은 하샤드 수입니다. 자연수 x를 입력받아 x가 하샤드 수인지 아닌지 검사하는 함수, solution을 완성해주세요.</p>
<h4 id="제한-조건">제한 조건</h4>
<ul>
<li>x는 1 이상, 10000 이하인 정수입니다.</li>
</ul>
<h4 id="입출력-예-1">입출력 예</h4>
<table>
<thead>
<tr>
<th align="center">arr</th>
<th align="center">return</th>
</tr>
</thead>
<tbody><tr>
<td align="center">10</td>
<td align="center">true</td>
</tr>
<tr>
<td align="center">12</td>
<td align="center">true</td>
</tr>
<tr>
<td align="center">11</td>
<td align="center">false</td>
</tr>
<tr>
<td align="center">13</td>
<td align="center">false</td>
</tr>
</tbody></table>
<h4 id="입출력-예-설명-1">입출력 예 설명</h4>
<p>입출력 예 #1
10의 모든 자릿수의 합은 1입니다. 10은 1로 나누어 떨어지므로 10은 하샤드 수입니다.</p>
<p>입출력 예 #2
12의 모든 자릿수의 합은 3입니다. 12는 3으로 나누어 떨어지므로 12는 하샤드 수입니다.</p>
<p>입출력 예 #3
11의 모든 자릿수의 합은 2입니다. 11은 2로 나누어 떨어지지 않으므로 11는 하샤드 수가 아닙니다.</p>
<p>입출력 예 #4
13의 모든 자릿수의 합은 4입니다. 13은 4로 나누어 떨어지지 않으므로 13은 하샤드 수가 아닙니다.</p>
<h3 id="제출답안-1">제출답안</h3>
<pre><code>function solution(x) {
    var answer = true;
    var arr = String(x).split(&quot;&quot;).reduce((a, b) =&gt; Number(a) + Number(b));
    answer = x % arr === 0 ? true : false;
    return answer;
}</code></pre><h3 id="풀이-1">풀이</h3>
<p><code>split()</code> 메서드는 <em>string</em> 값이어야 사용할 수 있다. x를 <em>string</em> 값으로 만들고 그것을 배열로 나눈 뒤 <code>reduce()</code>를 통해 이것을 더해야한다. 그러나 x의 값은 현재 <em>string</em> 값이기 때문에 <em>number</em> 값으로 변환해야한다.</p>
<hr>
<h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-시저-암호span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [시저 암호]</span></h1>
<h3 id="문제설명-2">문제설명</h3>
<p>어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 &quot;AB&quot;는 1만큼 밀면 &quot;BC&quot;가 되고, 3만큼 밀면 &quot;DE&quot;가 됩니다. &quot;z&quot;는 1만큼 밀면 &quot;a&quot;가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.</p>
<h4 id="제한조건">제한조건</h4>
<ul>
<li>공백은 아무리 밀어도 공백입니다.</li>
<li>s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.</li>
<li>s의 길이는 8000이하입니다.</li>
<li>n은 1 이상, 25이하인 자연수입니다.<h4 id="입출력-예-2">입출력 예</h4>
<table>
<thead>
<tr>
<th>s</th>
<th>n</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;AB&quot;</td>
<td>1</td>
<td>&quot;BC&quot;</td>
</tr>
<tr>
<td>&quot;z&quot;</td>
<td>1</td>
<td>&quot;a&quot;</td>
</tr>
<tr>
<td>&quot;a B z&quot;</td>
<td>4</td>
<td>&quot;e F d&quot;</td>
</tr>
</tbody></table>
</li>
</ul>
<h3 id="제출답안-2">제출답안</h3>
<pre><code>function solution(s, n) {
    return s.split(&quot;&quot;).map((e)=&gt;{
        if (e == &quot; &quot;){
            return e;
        }
        const a = e.charCodeAt()
        return e.toUpperCase().charCodeAt()+n &gt; 90 
            ? String.fromCharCode(a+n-26) 
            : String.fromCharCode(a+n)
    }).join(&#39;&#39;);
}</code></pre><h3 id="풀이-2">풀이</h3>
<p>아스키코드를 사용해서 문제를 풀어야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 직사각형 별찍기]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-Javascript-%EC%A7%81%EC%82%AC%EA%B0%81%ED%98%95-%EB%B3%84%EC%B0%8D%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-Javascript-%EC%A7%81%EC%82%AC%EA%B0%81%ED%98%95-%EB%B3%84%EC%B0%8D%EA%B8%B0</guid>
            <pubDate>Wed, 11 May 2022 19:11:10 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-직사각형-별찍기span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [직사각형 별찍기]</span></h1>
<h3 id="문제설명">문제설명</h3>
<p>이 문제에는 표준 입력으로 두 개의 정수 n과 m이 주어집니다.
별(*) 문자를 이용해 가로의 길이가 n, 세로의 길이가 m인 직사각형 형태를 출력해보세요.</p>
<h4 id="제한사항">제한사항</h4>
<ul>
<li>n과 m은 각각 1000 이하인 자연수입니다.</li>
</ul>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th align="center">입력</th>
<th align="center">출력</th>
</tr>
</thead>
<tbody><tr>
<td align="center">5&nbsp;&nbsp;3</td>
<td align="center">*****<br />*****<br />*****</td>
</tr>
</tbody></table>
<pre><code>process.stdin.setEncoding(&#39;utf8&#39;);
process.stdin.on(&#39;data&#39;, data =&gt; {
    const n = data.split(&quot; &quot;);
    const a = Number(n[0]), b = Number(n[1]);
    console.log(a);
    console.log(b);
});</code></pre><h3 id="제출답안">제출답안</h3>
<pre><code>process.stdin.setEncoding(&#39;utf8&#39;);
process.stdin.on(&#39;data&#39;, data =&gt; {
    const n = data.split(&quot; &quot;);
    const a = Number(n[0]), b = Number(n[1]);
    const star = &quot;*&quot;.repeat(a);
    for (let i = 0; i &lt; b; i++) {
        console.log(star);
    }
});

// =&gt; 테스트 통과!</code></pre><p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSON 파일 데이터 가져와서 리스트 만들기 ]]></title>
            <link>https://velog.io/@sweet_pumpkin/Megabyte-School-JSON-%ED%8C%8C%EC%9D%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%80%EC%A0%B8%EC%99%80%EC%84%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/Megabyte-School-JSON-%ED%8C%8C%EC%9D%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%80%EC%A0%B8%EC%99%80%EC%84%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 11 May 2022 18:48:22 GMT</pubDate>
            <description><![CDATA[<p>패스트캠퍼스 메가바이트스쿨 프론트엔드 개발자 과정의 첫 토이 프로젝트는 금융앱 만들기였다.</p>
<p>대충 아래와 같은...
<img src="https://velog.velcdn.com/images/sweet_pumpkin/post/558f5650-abcb-4d65-8d8d-cd68c18389e5/image.gif" alt=""></p>
<p>대충 자바스크립트와 CSS로 슬라이드와 슬라이드 그리고 슬라이드를 만드는...</p>
<p>그런데 JSON 파일 데이터를 추출하는 건 배운 적이 없는데?
<img src="https://velog.velcdn.com/images/sweet_pumpkin/post/8615de92-027e-4203-8910-b1a044e77d52/image.png" alt=""></p>
<p>...</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/b8d8fcbb-0a00-4242-a58d-ef3690c42e55/image.png" alt=""></p>
<p>도와줘잉</p>
<h2 id="span-stylecolor-5fc69ajson-파일-가져오기span"><span style="color: #5fc69a">JSON 파일 가져오기</span></h2>
<p>아래의 <em>list.json</em> 파일을 <em>js</em> 파일로 가져와 보자. <em>list.json</em> 파일은 다는 아니고 몇개만 밑에 적어보겠다.(너무 길어)</p>
<pre><code>[
  {
    &quot;date&quot;: &quot;2022.05.01&quot;,
    &quot;inOut&quot;: &quot;in&quot;,
    &quot;type&quot;: &quot;profit&quot;,
    &quot;item&quot;: &quot;용돈&quot;,
    &quot;price&quot;: 300000
  },
  {
    &quot;date&quot;: &quot;2022.05.02&quot;,
    &quot;inOut&quot;: &quot;out&quot;,
    &quot;type&quot;: &quot;taxi&quot;,
    &quot;item&quot;: &quot;택시&quot;,
    &quot;price&quot;: 8900
  },
  {
    &quot;date&quot;: &quot;2022.05.03&quot;,
    &quot;inOut&quot;: &quot;out&quot;,
    &quot;type&quot;: &quot;edu&quot;,
    &quot;item&quot;: &quot;교보문고&quot;,
    &quot;price&quot;: 23000
  },
  {
    &quot;date&quot;: &quot;2022.05.04&quot;,
    &quot;inOut&quot;: &quot;out&quot;,
    &quot;type&quot;: &quot;eatout&quot;,
    &quot;item&quot;: &quot;스타벅스신사역점&quot;,
    &quot;price&quot;: 18000
  },
  {
    &quot;date&quot;: &quot;2022.05.05&quot;,
    &quot;inOut&quot;: &quot;out&quot;,
    &quot;type&quot;: &quot;eatout&quot;,
    &quot;item&quot;: &quot;씨유화곡미라클점&quot;,
    &quot;price&quot;: 3000
  },
  {
    &quot;date&quot;: &quot;2022.05.06&quot;,
    &quot;inOut&quot;: &quot;out&quot;,
    &quot;type&quot;: &quot;daily&quot;,
    &quot;item&quot;: &quot;다이소&quot;,
    &quot;price&quot;: 12000
  },
  {
    &quot;date&quot;: &quot;2022.05.07&quot;,
    &quot;inOut&quot;: &quot;out&quot;,
    &quot;type&quot;: &quot;eatout&quot;,
    &quot;item&quot;: &quot;비트박스(강남점)&quot;,
    &quot;price&quot;: 15000
  },
  {
    &quot;date&quot;: &quot;2022.05.08&quot;,
    &quot;inOut&quot;: &quot;out&quot;,
    &quot;type&quot;: &quot;eatout&quot;,
    &quot;item&quot;: &quot;매드클라운포갈릭(강남점)&quot;,
    &quot;price&quot;: 230000
  }
]</code></pre><p>위의 파일을 <em>js</em> 파일로 가져와보자.</p>
<pre><code>fetch(&quot;./json/list.json&quot;)
.then((res) =&gt; {
  return res.json()
})
.then((obj) =&gt; {
  List(obj);
})</code></pre><p>먼저 _fetch_에 url로 요청을 보내 바로 뒤에오는 응답에 대해 json()을 해줘야 한다.</p>
<p>fetch 를 실행하면, 원격 저장소의 최신 이력을 확인할 수 있다. 사실 경로를 보면 fetch 필요없다. import 쓰면 됨. 그러나 이번 토이 프로젝트에서 제공된 json 파일은 원격 저장소 주소로 요청을 보내는 것이었다.</p>
<p>이제 url로 요청을 한 뒤 <code>.then</code>을 이용해 바로 뒤 응답에 대해 json()을 해서 정보를 가져올 수 있지만, 이대론 가져온 정보를 바로 쓸 순 없다. 한번 더 <code>.then</code>을 사용해야 제대로된 정보를 가져올 수 있다.</p>
<p>받아온 정보를 이제 우리가 만들 <code>List()</code> 함수에 담아주면 json 정보를 사용할 수 있다.</p>
<h2 id="span-stylecolor-5fc69ahtml--css-구조-파악하기span"><span style="color: #5fc69a">HTML &amp; CSS 구조 파악하기</span></h2>
<p>우리가 만들어야할 리스트의 구조는 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/c5187a94-e775-42b5-944c-bf260156ec92/image.png" alt=""></p>
<p>HTML에서 id 값을 &quot;receipt&quot;로 가지고 있는 ul 태그에 div 태그와 li 태그가 있다. 그리고 해당 태그 안에는 각각 span 태그 두 개가 위치하고 있다. HTML 코드로 표현하면 아래와 같다.</p>
<pre><code>&lt;ul id=&quot;receipt&quot;&gt;
  &lt;div&gt;
    &lt;span&gt;2022.05.08&lt;/span&gt;
    &lt;span&gt;238,000원 지출&lt;/span&gt;
  &lt;/div&gt;
  &lt;li&gt;
    &lt;span&gt;하늘섬플레이스(강남점)&lt;/span&gt;
    &lt;span&gt;8,000원&lt;/span&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;span&gt;매드클라운포갈릭(강남점)&lt;/span&gt;
    &lt;span&gt;23,000원&lt;/span&gt;
  &lt;/li&gt;
&lt;/ul&gt;</code></pre><p>ul 태그 내부에 span 태그들을 div로 감싼 형태로 한 것은 CSS 상에서 편의를 위한 것이다. 이렇게 표현했지만 ul 태그를 제외하고 전부 javascript에서 만들기 때문에 실제로 HTML 상 코드는 아래와 같다.</p>
<pre><code>&lt;ul id=&quot;receipt&quot;&gt;&lt;/ul&gt;</code></pre><h2 id="span-stylecolor-5fc69a날짜-출력하기span"><span style="color: #5fc69a">날짜 출력하기</span></h2>
<pre><code>funciton List(obj) {
  // ...
}</code></pre><p>위와 같은 <code>List()</code> 함수가 실행되면, 먼저 예시처럼 &quot;2022.05.08&quot;과 같은 날짜 데이터를 출력한다. 그리고 해당 날짜 데이터를 기준으로 결내 내역 중 지출 가격을 모두 더해 따로 출력한다. 또 날짜 데이터를 기준으로 각각 결제 장소와 결제 가격을 출력하는데, 소득이 발생했을 경우 지출과 다른 색으로 출력할 수 있도록 할 것이다.</p>
<p>먼저 날짜 데이터를 추출해보자</p>
<pre><code>const date = obj.map(v =&gt; v.date);
    const arr = new Array(date);
    const arrZero = arr[0];
    const arrUni = arrZero.filter((val, idx) =&gt; {
      return arrZero.indexOf(val) === idx;
    })
    const arrReverse = arrUni.reverse();
    for (let i = 0; i &lt; arrReverse.length; i++) {
      const D = arrReverse[i];

      const ulEl = document.querySelector(&#39;#receipt&#39;);
      const dataWrap = document.createElement(&#39;div&#39;);
      const dateName = document.createElement(&#39;span&#39;);

      dateName.className = &quot;dateName&quot;;
      dateName.textContent = D;

      ulEl.appendChild(dataWrap);
      dataWrap.appendChild(dateName);
}</code></pre><p>먼저 <code>map()</code>을 통해 list.json에서 날짜(date)만 추출해 그것을 <code>new Array(date)</code>를 통해 배열로 만들어주자. 이렇게 하면 <code>arr</code>은 우리가 원하는 날짜 배열을 한번 더 감싼 형태로 값을 내보내준다. 즉 우리가 원하는 값은 <code>arr[0]</code>에 있다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/675d7742-fa2e-48d8-89dc-daf668a78ad5/image.png" alt=""></p>
<p>이것을 <code>filter()</code>와 <code>indexOf()</code> 메서드를 이용해 중복된 날짜 데이터들을 정리해주자.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/ccc4e72d-e11a-49fa-b5bd-657f1a49a686/image.png" alt=""></p>
<p>위와 같이 출력된 날짜 데이터를 for 구문으로 돌리면 되겠지만, 문제는 우리가 표현할 리스트가 최신 날짜 순으로 되어있다. <code>reverse()</code> 메서드를 이용해 배열을 거꾸로 바꿔주자. 다음 for 구문을 통해 이것을 배열의 길이 값까지 반복시켜주자.</p>
<p>이 다음 div 태그와 span 태그를 만들고, span 태그에 <code>textContent</code> 내용을 for 구문을 반복시켜 추출한 값으로 지정해주자.</p>
<p>마지막으로 <code>appendChild</code>로 HTMl의 ul 태그 내부에 이때까지 만든 코드들을 붙여주자. 이렇게 하면 json 파일에 들어있는 날짜 5월 1일부터 8일까지가 하나 하나 리스트에 출력될 것이다.</p>
<h2 id="span-stylecolor-5fc69a일별-지출-금액-출력하기span"><span style="color: #5fc69a">일별 지출 금액 출력하기</span></h2>
<pre><code>const ulEl = document.querySelector(&#39;#receipt&#39;);
const dataWrap = document.createElement(&#39;div&#39;);
const plusPrice = document.createElement(&#39;span&#39;);
plusPrice.className =&quot;plusPrice&quot;;

const DPrice = obj.map(
        v =&gt; v.date === D &amp;&amp; v.inOut === &#39;out&#39; ? v.price : null)
        .reduce((a, b) =&gt; {return a + b}, 0);

const changeD1Price = DPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, &#39;,&#39;);

plusPrice.textContent = `${changeD1Price}원 지출`;

ulEl.appendChild(dataWrap);
dataWrap.appendChild(plusPrice);
</code></pre><p>먼저 map을 통해 json 파일 중 가격(price) 값을 가져올 것인데, 삼항연산자를 이용해서 날짜(date) 값이 똑같은 경우와 지출여부(inOut)가 &#39;out&#39;일 경우만 가져오도록 한다. 그리고 그것을 <code>reduce()</code> 메서드를 통해 더해주면 지출 값이 나온다.</p>
<p>이것을 아래와 같은 코드에 붙여 작성하면 값의 세자리마다 콤마를 찍어 출력할 수 있다.</p>
<pre><code>.toString().replace(/\B(?=(\d{3})+(?!\d))/g, &#39;,&#39;);</code></pre><p>그리고 그것을 백틱을 이용해 &#39;원 지출&#39;이라는 텍스트를 붙여 출력해주자.</p>
<h2 id="span-stylecolor-5fc69a결제-내역-출력하기span"><span style="color: #5fc69a">결제 내역 출력하기</span></h2>
<pre><code>for (let i = 0; i &lt; obj.length; i++) {
  const date = obj[i].date;
  const inOut = obj[i].inOut;
  const item = obj[i].item;
  const price = obj[i].price;
  const changePrice = price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, &#39;,&#39;);

  if (date === D) {
    const liItem = document.createElement(&#39;span&#39;);
    const liPrice = document.createElement(&#39;span&#39;);
    const listWrap = document.createElement(&#39;li&#39;);
    liItem.textContent = `${item}`;

    if (inOut === &#39;out&#39;) {
      liPrice.classList.add(&#39;out&#39;);
      liPrice.textContent = `${changePrice}원`; 
    } else {
      liPrice.classList.add(&#39;in&#39;);
      liPrice.textContent = `${changePrice}원`; 
    }

    ulEl.appendChild(listWrap);
    listWrap.appendChild(liItem);
    listWrap.appendChild(liPrice);
  }
}</code></pre><p>for 구문을 이용해서 json 파일 배열의 길이만큼 반복해주자. 반복한 값 중 위의 for 구문을 통해 반복되고 있는 날짜(D)와 일치 할 때만 결제 장소(item)를 출력하게 하자. 그리고 그 중에서 지출여부(inOut)이 &#39;out&#39;일 경우 해당 span에 클래스 이름에 &#39;out&#39;을 더해주고, &#39;in&#39;일 경우 &#39;in&#39;을 더해주면서 각각 결제금액을 출력하게 하자. 여기서 추가한 &#39;in&#39;과 &#39;out&#39;은 CSS 상에서 색상을 다르게 나타게 할 것이다.</p>
<p>위의 코드를 모두 합치면 다음과 같다.</p>
<pre><code>fetch(&quot;./json/list-01.json&quot;)
.then((res) =&gt; {
  return res.json()
})
.then((obj) =&gt; {
  List(obj);
})

  function List(obj) {

    // 날짜 배열 추출
    const date = obj.map(v =&gt; v.date);
    const arr = new Array(date);
    const arrZero = arr[0];
    const arrUni = arrZero.filter((val, idx) =&gt; {
      return arrZero.indexOf(val) === idx;
    })
    const arrReverse = arrUni.reverse();
    for (let i = 0; i &lt; arrSorts.length; i++) {
      const D = arrReverse[i]; // 날짜 추출

      // 지출 합산 &amp; 날짜 표시 추출
      const ulEl = document.querySelector(&#39;#receipt&#39;);
      const dataWrap = document.createElement(&#39;div&#39;);
      const dateName = document.createElement(&#39;span&#39;);

      // 날짜 표시
      dateName.className = &quot;dateName&quot;;
      dateName.textContent = D;

      // 지출 합산
      const plusPrice = document.createElement(&#39;span&#39;);
      plusPrice.className =&quot;plusPrice&quot;;

      const DPrice = obj.map(
        v =&gt; v.date === D &amp;&amp; v.inOut === &#39;out&#39; ? v.price : null)
        .reduce((a, b) =&gt; {return a + b}, 0
      );

      const changeD1Price = DPrice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, &#39;,&#39;);

      plusPrice.textContent = `${changeD1Price}원 지출`;

      ulEl.appendChild(dataWrap);
      dataWrap.appendChild(dateName);
      dataWrap.appendChild(plusPrice);

      // 결제 내역 추출
      for (let i = 0; i &lt; obj.length; i++) {
        const date = obj[i].date;
        const inOut = obj[i].inOut;
        const item = obj[i].item;
        const price = obj[i].price;
        const changePrice = price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, &#39;,&#39;);

        if (date === D) {
          const liItem = document.createElement(&#39;div&#39;);
          const liPrice = document.createElement(&#39;div&#39;);
          const listWrap = document.createElement(&#39;li&#39;);

          liItem.textContent = `${item}`;

          if (inOut === &#39;out&#39;) {
            liPrice.classList.add(&#39;out&#39;);

            liPrice.textContent = `${changePrice}원`; 
          } else {
            liPrice.classList.add(&#39;in&#39;);
            liPrice.textContent = `${changePrice}원`; 
          }

          ulEl.appendChild(listWrap);
          listWrap.appendChild(liItem);
          listWrap.appendChild(liPrice);
        }
      }
    }
  }</code></pre><h2 id="span-stylecolor-5fc69a출력-결과span"><span style="color: #5fc69a">출력 결과</span></h2>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/4b4e1d2a-d2cd-4019-8afc-140607c28bd4/image.gif" alt=""></p>
<p><a href="https://gilded-hotteok-27194b.netlify.app/">Netlify 배포 링크</a></p>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[너의 할 일은。React로 To Do List 만들기]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EB%AC%B4%EC%9E%91%EC%A0%95-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EB%84%88%EC%9D%98-%ED%95%A0-%EC%9D%BC%EC%9D%80%E3%80%82React%EB%A1%9C-To-Do-List-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/%EB%AC%B4%EC%9E%91%EC%A0%95-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EB%84%88%EC%9D%98-%ED%95%A0-%EC%9D%BC%EC%9D%80%E3%80%82React%EB%A1%9C-To-Do-List-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 02 May 2022 13:59:01 GMT</pubDate>
            <description><![CDATA[<h2 id="span-stylecolor-5fc69a목표-설정span"><span style="color: #5fc69a">목표 설정</span></h2>
<ul>
<li>React로 To Do List 만들기</li>
<li>input 입력 값 15자로 제한하기</li>
<li>To Do List 삭제하기</li>
<li>To Do List 10개로 제한하기</li>
<li>To Do List LocalStorage에 저장하기</li>
</ul>
<h2 id="span-stylecolor-5fc69a코드-살펴보기span"><span style="color: #5fc69a">코드 살펴보기</span></h2>
<pre><code>import &#39;./App.css&#39;;
import { useState, useEffect } from &quot;react&quot;;

function App() {
  const [toDo, setToDo] = useState(&quot;&quot;);
  const [toDos, setToDos] = useState([]);
  const onChange = (event) =&gt; setToDo(event.target.value);
  const onSubmit = (event) =&gt; {
    event.preventDefault();
    if (toDo === &quot;&quot;) {
      return;
    }
    if (toDos.length &lt; 10) {
      setToDos((currentArray) =&gt; [toDo, ...currentArray]);
    } else {
      return alert(&quot;등록된 리스트가 너무 많습니다.&quot;);
    }
    setToDo(&quot;&quot;);
  };
  const onClick = (idx) =&gt; {
    setToDos(toDos.filter((_, toDoIdx) =&gt; idx !== toDoIdx));
  }
  useEffect(() =&gt; {
    const data = localStorage.getItem(&#39;toDoList&#39;);
    if (data) {
      setToDos(JSON.parse(data));
    }
  }, []);
  useEffect(() =&gt; {
    localStorage.setItem(&#39;toDoList&#39;, JSON.stringify(toDos));
  }, [toDos]);

  return (
    &lt;div className=&quot;box&quot;&gt;
      &lt;div className=&quot;todo&quot;&gt;
        &lt;h1&gt;너의 할 일은。&lt;/h1&gt;
        &lt;span className=&quot;sub&quot;&gt;&quot;아직 한 적 없는 일을, 찾고 있어&quot;&lt;/span&gt;
        &lt;form
          className=&quot;list&quot; 
          onSubmit={onSubmit}&gt;
          &lt;input 
            onChange={onChange} 
            maxLength={15}
            value={toDo} 
            type=&quot;text&quot; 
            placeholder=&quot;Write your to do...&quot; 
          /&gt;
          &lt;button&gt;
          &lt;span className=&quot;material-symbols-outlined&quot;&gt;add&lt;/span&gt;
          &lt;/button&gt;
        &lt;/form&gt;
        &lt;ul&gt;
          {toDos.map((item, idx) =&gt; (
            &lt;li 
              key={idx}
            &gt;
              {item}
              &lt;button onClick={() =&gt; onClick(idx)}&gt;
              &lt;span className=&quot;material-symbols-outlined&quot;&gt;delete_forever&lt;/span&gt;
              &lt;/button&gt;
            &lt;/li&gt;
          ))}
        &lt;/ul&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default App;
</code></pre><h2 id="span-stylecolor-5fc69ato-do-list-만들기span"><span style="color: #5fc69a">To Do List 만들기</span></h2>
<p>input창에서 <code>onChange</code>를 사용해 입력된 값을 <code>toDo</code>와 <code>setToDo</code>를 이용해 받는다. </p>
<p><em>form</em> 태그에 쌓여진 <em>button</em> 태그가 하나일때, <em>button</em> 태그를 활성화한다면 <em>form</em> 태그 전체를 활성화시킬 수 있다. 이것을 이용해 _button_를 누르면, <code>onSubmit</code>이 실행되도록 하자. <code>onSubmit</code>으로 인해 제출된 <code>toDo</code>는 값이 비었을 경우 아무것도 실행되지 않고, 값이 있을 경우 아래와 같은 코드로 실행된다.</p>
<pre><code>setToDos((currentArray) =&gt; [toDo, ...currentArray]);</code></pre><p><code>...</code>은 기존의 배열 값을 의미한다. 아래와 같은 예시를 들어보자</p>
<pre><code>const str1 = [1, 2, 3, 4];
const str2 = [5, str1];
const str3 = [5, ...str1];

console.log(str2); // =&gt; [5, Array(4)]
console.log(str3); // =&gt; [5, 1, 2, 3, 4]
</code></pre><p>위의 예시대로 <code>...</code>을 사용하면 기존의 배열에 새로운 값을 더할 수 있다. 즉 기존의 <code>toDo</code>에 새로운 값을 계속 더할 수 있다는 의미다.</p>
<p><code>toDo</code>가 <code>onSubmit</code>됐다면, <em>input</em> 창을 비워주기 위해 <code>setToDo(&quot;&quot;);</code>를 실행해준다. <code>onSubmit</code>으로 등록된 <code>toDo</code> 값은 하단의 _ul_태그에서 <code>map()</code>을 통해 출력된다.</p>
<h3 id="span-stylecolor-5fc69amap-메서드-알아보기span"><span style="color: #5fc69a">map() 메서드 알아보기</span></h3>
<p><code>map()</code>은 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환한다. 사용방법은 아래와 같다.</p>
<pre><code>arr.map(callback(currentValue, index, array), thisArg)</code></pre><ul>
<li><code>currentValue</code> : 현재 처리할 요소.</li>
<li><code>index</code> : 처리할 현재 요소의 인덱스.</li>
<li><code>array</code> : <code>map()</code>을 호출한 배열.</li>
<li><code>thisArg</code> : <code>callback</code>을 실행할 때 <code>this</code>로 사용되는 값.</li>
</ul>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map">map() 메서드 자세히 알아보기</a></p>
<p>여기서는 item 값만 줘도 되나, index 값을 따로 주지 않으면 콘솔 창에서 key 값을 줘야한다고 경고 메세지가 뜬다. </p>
<p>여기까지 코드를 작성했으면, 아래와 같이 <em>input</em> 창에 To Do List를 작성하고 To Do List를 <em>ul</em> 태그에 출력할 수 있을 것이다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/fa0c921c-8b06-42c4-87f8-a3aa948c62ff/image.gif" alt=""></p>
<h2 id="span-stylecolor-5fc69ainput-입력-값-15자로-제한하기span"><span style="color: #5fc69a">input 입력 값 15자로 제한하기</span></h2>
<p>너무 많은 글자를 작성하지 못 하도록 <em>input</em> 태그에 <code>maxLength</code>를 사용하여 아래와 같이 글자 수를 제한할 수 있다.</p>
<pre><code>&lt;input maxLength={15} /&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/aa8caf7d-b2c3-4dad-bf06-f63ec84a10db/image.gif" alt=""></p>
<h2 id="span-stylecolor-5fc69ato-do-list-삭제하기span"><span style="color: #5fc69a">To Do List 삭제하기</span></h2>
<p>To DO List가 추가된 <em>ul</em> 태그의 <em>button</em> 태그에 <code>onClick</code> 기능을 추가해 <code>filter()</code>를 활용한 함수를 실행하게 하자. </p>
<h3 id="span-stylecolor-5fc69afilter-메서드-알아보기span"><span style="color: #5fc69a">filter() 메서드 알아보기</span></h3>
<p><code>filter()</code>는 배열의 요소를 순차적으로 순회하면서 조건에 일치하는 요소를 모아 새로운 배열을 반환한다. 사용 방법은 아래와 같다.</p>
<pre><code>arr.filter(callback(element, index, array), thisArg)</code></pre><p><code>element</code> : 처리할 현재 요소.
<code>index</code> : 처리할 현재 요소의 인덱스.
<code>array</code> : <code>filter</code>를 호출한 배열.
<code>thisArg</code> : <code>callback</code>을 실행할 때 <code>this</code>로 사용하는 값.</p>
<p><code>filter()</code>를 테스트를 통과한 요소들로 이루어진 새로운 배열이 반환되며, 어떠한 요소도 통과하지 못하면 빈 배열이 반환된다. 예를들어, 아래와 같은 배열에서 5의 배수인 요소만 추출하고 싶을때 <code>filter()</code> 메서드를 사용하면 된다.</p>
<pre><code>const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const answer = arr.filter(arr =&gt; arr &gt; 5)
console.log(answer); // =&gt; [6, 7, 8, 9]
</code></pre><p><code>onClick(idx)</code>을 통해 버튼을 클릭했을 때 삭제 기능을 넣은 함수를 실행하게 한 뒤 <code>filter()</code> 메서드를 사용해 코드를 아래와 같이 작성해보자. <code>idx</code>는 <code>toDos</code>의 배열 번호라고 생각하면 된다.</p>
<pre><code>const onClick = (idx) =&gt; {
    setToDos(toDos.filter((_, toDoIdx) =&gt; idx !== toDoIdx));
  }</code></pre><p><code>toDos</code> 배열 안에서 <code>filter()</code> 메서드를 사용해서 배열 번호를 <code>toDoidx</code>로 선언해주자. 앞의 element요소는 삭제 기능에 필요없는 부분이므로 언더스코어(_)를 써준다. 작성을 하지않으면 삭제 기능이 이루어지지 않으니 꼭 작성해주자. 그리고 익명함수를 통해 우리가 클릭한 배열 번호(삭제할 To Do)가 현재 <code>toDos</code>가 가지고 있는 배열 번호 중 일치하지 않는 것만 출력해주도록 작성해주면 된다.</p>
<p>이렇게 코드를 작성하면 _li_에 속한 To Do List가 아래와 같이 삭제된다. </p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/663899d6-87e5-4fbb-8488-dc55dc94038f/image.gif" alt=""></p>
<h2 id="span-stylecolor-5fc69ato-do-list-10개로-제한하기span"><span style="color: #5fc69a">To Do List 10개로 제한하기</span></h2>
<p>위의 To Do List 배열을 더해주는 코드에 if문을 씌워 10개 이하일때 동작하게하고, 10개 초과일시 <code>alert()</code>를 실행해 알림 메세지를 띄워주자.</p>
<pre><code>if (toDos.length &lt; 10) {
      setToDos((currentArray) =&gt; [toDo, ...currentArray]);
    } else {
      return alert(&quot;등록된 리스트가 너무 많습니다.&quot;);
    }</code></pre><p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/23f31106-92e7-4ad8-9f39-4855b600fac7/image.gif" alt=""></p>
<h2 id="span-stylecolor-5fc69ato-do-list-localstorage에-저장하기span"><span style="color: #5fc69a">To Do List LocalStorage에 저장하기</span></h2>
<p>To Do List를 LocalStorage에 저장하기 위해서는 <code>useEffect()</code>를 사용해야 한다. <code>useEffect()</code>는 React 컴포넌트가 렌더링될 때마다 특정 작업을 실행할 수 있도록하는 역할을 한다. <code>useEffect()</code>는 컴포넌트가 처음 렌더링될때 한 번 실행되고, 배열로 지정된 <code>useState()</code>의 값이 변경되면 다시 실행된다. 이러한 기능으로 하나의 값이 변동될때마다 전체 컴포넌트를 렌더링하지 않아 부담을 줄일 수 있다. 사용 방법은 아래와 같다.</p>
<pre><code>const [name, setName] = useState();
useEffect(() =&gt; {
  console.log(&quot;Hello!&quot;);  
}, [name]);</code></pre><p>첫 번째 렌더링이 될 때 <code>console.log(&quot;Hello!&quot;)</code>를 한 번, <code>name</code>이 변할 때마다 다시 <code>console.log(&quot;Hello!&quot;)</code>를 한 번 더 출력하는 코드다. 위의 예시를 이용해 To Do List를 LocalStorage에 저장해보자.</p>
<pre><code>useEffect(() =&gt; {
    localStorage.setItem(&#39;toDoList&#39;, JSON.stringify(toDos));
  }, [toDos]);</code></pre><p>위와 같이 &#39;toDoList&#39;라는 이름으로 새로 저장되는 배열 <code>toDos</code>를 LocalStorage에 저장할 수 있다.
이때 LocalStorage는 object를 저장할 수 없기에 <code>JSON.stringify()</code>를 이용해 string으로 바꿔 저장할 수 있다.</p>
<pre><code>useEffect(() =&gt; {
    const data = localStorage.getItem(&#39;toDoList&#39;);
    if (data) {
      setToDos(JSON.parse(data));
    }, []);</code></pre><p>저장된 값은 위의 <code>getItem()</code>을 이용해 불러올 수 있는데, 이때 string으로 변환된 값을 <code>JSON.parse()</code>을 이용해 object 값으로 다시 바꿔줘야한다. 또한 LocalStorage가 비어있을 경우 아무 것도 출력되지 않도록 if문을 추가한다.</p>
<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/7f59568d-d47f-4b5e-b0c8-e8b325ef6d2c/image.gif" alt=""></p>
<p><a href="https://brilliant-cranachan-4b5cc5.netlify.app/">배포 링크</a>
<a href="https://github.com/Sweet-Pumpkin/react-to-do-list">코드 보기</a></p>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 핸드폰 번호 가리기]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-Javascript-%ED%95%B8%EB%93%9C%ED%8F%B0-%EB%B2%88%ED%98%B8-%EA%B0%80%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@sweet_pumpkin/%EC%BD%94%ED%85%8C%EB%AC%B8%ED%92%80-Javascript-%ED%95%B8%EB%93%9C%ED%8F%B0-%EB%B2%88%ED%98%B8-%EA%B0%80%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Sun, 01 May 2022 13:20:52 GMT</pubDate>
            <description><![CDATA[<h1 id="span-stylecolor-5fc69aprogrammers--javascript-lv1-핸드폰-번호-가리기span"><span style="color: #5fc69a">Programmers  JavaScript Lv.1 [핸드폰 번호 가리기]</span></h1>
<h3 id="문제설명">문제설명</h3>
<p>프로그래머스 모바일은 개인정보 보호를 위해 고지서를 보낼 때 고객들의 전화번호의 일부를 가립니다.
전화번호가 문자열 phone_number로 주어졌을 때, 전화번호의 뒷 4자리를 제외한 나머지 숫자를 전부 <code>*</code>으로 가린 문자열을 리턴하는 함수, solution을 완성해주세요.</p>
<h4 id="제한사항">제한사항</h4>
<ul>
<li>phone_number는 길이 4 이상, 20이하인 문자열입니다.</li>
</ul>
<h4 id="입출력-예">입출력 예</h4>
<table>
<thead>
<tr>
<th align="center">phone_number</th>
<th align="center">return</th>
</tr>
</thead>
<tbody><tr>
<td align="center">&quot;01033334444&quot;</td>
<td align="center">&quot;*******4444&quot;</td>
</tr>
<tr>
<td align="center">&quot;027778888&quot;</td>
<td align="center">&quot;*****8888&quot;</td>
</tr>
</tbody></table>
<h3 id="제출답안">제출답안</h3>
<pre><code>&lt;script&gt;
  function solution(phone_number) {
    const asterlisk = &quot;*&quot;.repeat(phone_number.length -4);
    const number = phone_number.substr(-4);
    const answer = asterlisk + number;
    return answer;
  }
  // =&gt; 테스트 통과!
&lt;/script&gt;</code></pre><h1 id="span-stylecolor-5fc69a정답해설span"><span style="color: #5fc69a">정답해설</span></h1>
<h2 id="span-stylecolor-5fc69arepeat-알아보기span"><span style="color: #5fc69a">repeat 알아보기</span></h2>
<p><code>repeat()</code>는 문자열을 주어진 횟수만큼 반복해 붙인 새로운 문자열을 반환한다. 사용 방법은 아래와 같다. </p>
<pre><code>&lt;script&gt;
  str.repeat(count);
&lt;/script&gt;</code></pre><p><code>count</code>는 문자열을 반복할 횟수이고, 0과 양의 무한대 사이의 정수여야 한다.</p>
<h2 id="span-stylecolor-5fc69asubstr-slice-substring-알아보기span"><span style="color: #5fc69a">substr, slice, substring 알아보기</span></h2>
<h3 id="substr">substr()</h3>
<p><code>substr()</code> 문자열에서 특정 위치에서 시작하여 특정 문자 수 만큼의 문자들을 반환한다. 사용 방법은 아래와 같다. </p>
<pre><code>&lt;script&gt;
  str.substr(start, length);
&lt;/script&gt;</code></pre><p><code>start</code>는 추출하고자 하는 문자열의 시작 위치이며, <code>length</code>는 추출하고자 하는 문자열의 총 합이다. <code>length</code> 값을 지정하지 않으면 <code>start</code>부터 문자열 전체를 값으로 한다. </p>
<p><code>start</code>가 양수일 경우, 문자의 길이 보다 작으면 빈문자열이 리턴된다.
<code>start</code>가 음수일 경우, <code>start</code>의 _index_는 문자열의 뒤에서부터 시작된다.</p>
<br />

<p>**  그.런.데.  **</p>
<br />

<p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/48efb25d-13fb-4afa-b1fa-8aa57145badf/image.png" alt=""></p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/substr">MDN</a>에서 위와 같은 경고 메세지가 있었다. 앞으론 <code>slice()</code> 써야겠지...</p>
<h3 id="slice">slice()</h3>
<p><code>slice()</code>는 어떤 배열의 시작부터 끝까지에 대한 얕은 복사본을 새로운 배열 객체로 반환한다. 원본 배열은 바뀌지 않는다. 사용 방법은 아래와 같다. </p>
<pre><code>&lt;script&gt;
  str.slice(start, end);
&lt;/script&gt;</code></pre><p><code>start</code>는 검색구간의 시작지점이며, <code>end</code>는 검색구간의 종료지점이다. <code>end</code> 값을 지정하지 않으면 <code>str.length-1</code>을 입력한 것과 같다. <code>start</code>가 음수일 경우, <code>start</code>의 _index_는 문자열의 뒤에서부터 시작된다.</p>
<h3 id="substring">substring()</h3>
<p><code>substring</code>은 <code>string</code> 객체의 시작 인덱스로 부터 종료 인덱스 전 까지 문자열의 부분 문자열을 반환한다. 사용 방법은 아래와 같다.</p>
<pre><code>&lt;script&gt;
  str.substring(start, end);
&lt;/script&gt;</code></pre><p><code>start</code>는 반환문자열의 시작 인덱스, <code>end</code>는 반환문자열의 마지막 인덱스이다. <code>end</code>가 생략된 경우, 문자의 끝까지 모든 문자를 추출한다. <code>start</code>와 <code>end</code>가 같을 경우 빈 문자열을 추출한다. <code>start</code> 값이 <code>end</code> 값보다 클 경우 두 개의 인자를 바꾼 듯 작동하게 된다. 아래의 예제를 보자.</p>
<pre><code>&lt;script&gt;
  const str = &quot;Hello&quot;;
  str.substring(1,3) // =&gt; &quot;el&quot;
  str.substring(3,1) // =&gt; &quot;el&quot;
&lt;/script&gt;</code></pre><h1 id="span-stylecolor-5fc69a정규표현식으로-표현하기span"><span style="color: #5fc69a">정규표현식으로 표현하기</span></h1>
<p>위의 문제는 정규표현식으로 출력할 수 있다. </p>
<pre><code>&lt;script&gt;
  function solution(phone_number) {
    return phone_number.replace(/\d(?=\d{4})/g, &quot;*&quot;);
  }
  // =&gt; 테스트 통과!
&lt;/script&gt;</code></pre><br />
끝.]]></description>
        </item>
        <item>
            <title><![CDATA[왜 콘솔로그가 두 번 출력되지? 왜 콘솔로그가 두 번 출력되지? : React StrictMode]]></title>
            <link>https://velog.io/@sweet_pumpkin/%EB%AC%B4%EC%9E%91%EC%A0%95-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EC%95%84%EB%8B%88-%EC%99%9C-%EC%BD%94%EB%93%9C%EA%B0%80-%EB%91%90-%EB%B2%88-%EC%B6%9C%EB%A0%A5%EB%90%98%EB%8A%94-%EA%B1%B4%EB%8D%B0-React-StrictMode</link>
            <guid>https://velog.io/@sweet_pumpkin/%EB%AC%B4%EC%9E%91%EC%A0%95-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EC%95%84%EB%8B%88-%EC%99%9C-%EC%BD%94%EB%93%9C%EA%B0%80-%EB%91%90-%EB%B2%88-%EC%B6%9C%EB%A0%A5%EB%90%98%EB%8A%94-%EA%B1%B4%EB%8D%B0-React-StrictMode</guid>
            <pubDate>Fri, 29 Apr 2022 07:51:21 GMT</pubDate>
            <description><![CDATA[<p>평화로운 React 공부시간.</p>
<p>React 앱 환경에서 분명 아래와 같이 <code>console.log()</code>를 한 번 작성했는데 개발서버 화면에서는 두 번 출력되고 있었다.</p>
<pre><code>&lt;script&gt;
  console.log(&#39;뭐지 콩신의 저주인가?&#39;)
  function() {
    return(
      &lt;div&gt;Hello World!&lt;div&gt;
    );
  }
  export default Hello;
&lt;/script&gt;</code></pre><p><img src="https://velog.velcdn.com/images/sweet_pumpkin/post/922a662a-b52d-455c-955c-055e09f2e4fb/image.png" alt=""></p>
<p>이런 현상은 <code>StrictMode</code> 때문인데, React 환경에서 작성한 코드를 출력하기 위해 만든 <code>index.js</code> 파일에 가면 볼 수 있다.</p>
<pre><code>&lt;script&gt;
  import { StrictMode } from &quot;react&quot;;
  import { createRoot } from &quot;react-dom/client&quot;;
  import Hello from &quot;./hello&quot;;

  const rootElement = document.getElementById(&quot;root&quot;);
  const root = createRoot(rootElement);

  root.render(
    &lt;StrictMode&gt;
      &lt;Hello /&gt;
    &lt;/StrictMode&gt;
  );
&lt;/script&gt;</code></pre><p><code>StrictMode</code>는 앱에서 잠재적인 문제를 알아내기 위한 도구로, Fragment와 같이 UI를 렌더링하지 않고 자손들에 대한 부가적인 검사와 경고를 활성화한다. <code>StrictMode</code>는 다음과 같은 부분에서 도움을 줄 수 있다.</p>
<ul>
<li>안전하지 않은 생명주기를 사용하는 컴포넌트 발견</li>
<li>레거시 문자열 ref 사용에 대한 경고</li>
<li>권장되지 않는 findDOMNode 사용에 대한 경고</li>
<li>예상치 못한 부작용 검사</li>
<li>레거시 context API 검사</li>
</ul>
<p><code>StrictMode</code>는 개발 모드에서만 활성화되기 때문에, 프로덕션 빌드에는 영향을 끼치지 않는다. 즉 <code>console.log()</code>가 두 번 뜨는 것은 <code>StrictMode</code>로 인한 정상적인 상황으로, 만약 이것이 거슬린다면 <code>index.js</code>에서 출력할 파일을 감싸고 있는 <code>&lt;StrictMode&gt;&lt;/StrictMode&gt;</code>를 제거하면 된다.</p>
<p><a href="https://ko.reactjs.org/docs/strict-mode.html">StrictMode 자세히 알아보기</a></p>
]]></description>
        </item>
    </channel>
</rss>