<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>minchan_0.log</title>
        <link>https://velog.io/</link>
        <description>프론트엔드 개발자는 안하기로 했어요</description>
        <lastBuildDate>Sat, 16 Jul 2022 05:57:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. minchan_0.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/minchan_0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[웹툰 추천 서비스 제작 과정]]></title>
            <link>https://velog.io/@minchan_0/%EC%9B%B9%ED%88%B0-%EC%B6%94%EC%B2%9C-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A0%9C%EC%9E%91-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@minchan_0/%EC%9B%B9%ED%88%B0-%EC%B6%94%EC%B2%9C-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A0%9C%EC%9E%91-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Sat, 16 Jul 2022 05:57:33 GMT</pubDate>
            <description><![CDATA[<p>깃허브 주소 : <a href="https://github.com/whara123/mbti-webtoon-test">https://github.com/whara123/mbti-webtoon-test</a></p>
<p>크롤링을 사용해보고 싶어서 mbti 테스트를 기반으로 레진 코믹스 웹툰 추천 서비스를 만들었다.</p>
<p>같이 스터디하는 분이 흥미가 있으시다고 해서 2인 팀으로 진행하게 됐다 ! </p>
<p>메인/mbti테스트 페이지를 팀원분이 맡고 나는 원래 목적인 크롤링이 사용되는 결과페이지를 맡게 되었다.</p>
<p>페이지 컨셉을 정하다가, 레진 코믹스를 검색하면 나오는 &quot;재미라는 것이 폭발한다&quot;를 보고 폭발에 꽂혀서 폭발로 컨셉을 정했다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/670c7d82-a2f8-4338-9c88-64e604fa0af8/image.png" alt="">그렇게 지어진 이름..</p>
<h2 id="크롤링">크롤링</h2>
<p>처음에는 axios와 cheerio를 이용해서 크롤링하면 쉽게 될 줄 알고 시도했는데, 레진은 정적인 페이지가 아니고 js를 통해 동적으로 내용이 생성되기 때문에 axios로는 빈칸만 가져왔다.</p>
<p>동적 페이지 크롤링을 위해 puppeteer를 사용했다.</p>
<blockquote>
</blockquote>
<p>Chrome 또는 Chromium을 제어하는 고급 API를 제공하는 노드 라이브러리
<a href="https://pptr.dev/">https://pptr.dev/</a></p>
<p>puppeteer는 가상 브라우저를 열어서 그 안에서 다양한 작업을 명령할 수 있다.</p>
<pre><code class="language-js">const puppeteer = require(&#39;puppeteer&#39;);
const cheerio = require(&#39;cheerio&#39;);</code></pre>
<p>cheerio는 여전히 요소를 찾을 때 쉽게 사용할 수 있어서 같이 사용했다.</p>
<pre><code class="language-js">const browser = await puppeteer.launch({headless: true})
const page = await browser.newPage();

//설정한 url로 이동
await page.goto(url);

//페이지 html정보를 가져옴
const content = await page.content();
const $ = cheerio.load(content);

//가져올 태그 선택
const list = $(&#39;레진페이지에서 태그 위치 확인&#39;).children(&#39;li:nth-child(-n+10)&#39;);</code></pre>
<p>goto(주소)로 이동 후, 원하는 영역의 seleter를 찾아 그 자식요소 중 li를 상위 10개까지만 가져왔다. 그 뒤 10개의 정보를 each로 돌리면서 배열에 넣어준다.</p>
<pre><code class="language-js">list.each((idx, node) =&gt; {
      recommendData.push({
        title: $(node).find(&#39;....&#39;).text(),
        artist: $(node).find(&#39;....&#39;).text(),
        link: $(node).find(&#39;a&#39;).attr(&#39;href&#39;),
        img: $(node).find(&#39;... &gt; img&#39;).attr(&#39;src&#39;),
      });
    });</code></pre>
<p>제목과 작가 정보는 text()로 태그에 있는 텍스트를 가져왔고, 링크나 썸네일 이미지 주소는 attr을 이용해 가져온다.</p>
<p>그런데 문제가 있었다. 동적 페이지인 레진은 아직 드래그되지 않은 영역의 요소들은 로딩이 바로 안되고 어트리뷰트가 src가 아니라 data-src로 적용되어 있어 10개를 제대로 가져오지 못한다.</p>
<pre><code class="language-js">await page.evaluate(`window.scrollTo(0, document.body.scrollHeight/5)`);</code></pre>
<p>그래서 evaluate을 이용해 페이지 스크롤 위치를 조금 변경했다. 그럼 진입 후 document.body.scrollHeight/5 만큼 살짝 스크롤을 이동하고 레진 페이지가 동작하면서 제대로 된 주소를 가져올 수 있게 된다.</p>
<p>비슷한 동작으로 실시간 랭킹페이지로도 이동해서 상위권 리스트도 가져왔다!</p>
<pre><code class="language-js">await browser.close();
return { recommendRandomData, rankData };</code></pre>
<p>크롤링이 끝나면 브라우저를 꼭 닫아주자.</p>
<p>크롤링 데이터를 이제 react 페이지로 옮기고 싶은데 나는 node에 상당히 무지한 상태였다.</p>
<p>React에서 export하고 import하면 될줄 알았는데 그런게 아니었다.</p>
<p>이틀을 뻘겋게 뜨는 오류들과 싸우다가 알게되었는데,</p>
<p><strong>브라우저는 puppeteer를 작동시킬 수 없었다</strong></p>
<blockquote>
</blockquote>
<p><img src="https://golden-goblin.com/content-thief/wp-content/uploads/sites/5/kboard_attached/1/202006/5ed8e0d37033f5013918.gif" alt="">(생각해보니까 당연한거 아녀 .. ?)</p>
<p>express로 구축한 웹서버에서 puppeteer를 사용할 수 있다. 부랴부랴 서버 폴더를 생성해서 express 설치.</p>
<p>서버에서 크롤링을 사용할 수 있게 </p>
<pre><code class="language-js">module.exports.crawler = crawler;</code></pre>
<p>exports해준다.</p>
<p>서버 index.js에서 </p>
<pre><code class="language-js">const express = require(&#39;express&#39;);
const router = express.Router();

const app = express();
const port = process.env.PORT || 5000;
app.listen(port);

let getCrawler = require(&#39;../crawler&#39;);</code></pre>
<p>크롤러를 가져오고, </p>
<pre><code class="language-js">app.use(&#39;/getData&#39;, async function (req, res) {
  let result;

  if (req.query.name) {
    result = await getCrawler.crawler(req.query.name);
  } else {
    result = await getCrawler.crawler(&#39;all&#39;);
  }
  console.log(JSON.stringify(result));
  res.send(result);
});</code></pre>
<p>/getData 로 접속해서 크롤러를 사용해준다.</p>
<p>리액트에서 3000번, 서버에서 5000번을 사용해야 해서 port를 5000번으로 넣어줬다. </p>
<p>ProxyMiddleware를 사용해서 cors 이슈를 방지해준다.</p>
<pre><code class="language-js">const { createProxyMiddleware } = require(&#39;http-proxy-middleware&#39;);

module.exports = function (app) {
  app.use(
    createProxyMiddleware(&#39;/getData&#39;, {
      target: &#39;http://localhost:5000&#39;,
      changeOrigin: true,
    }),
  );
};</code></pre>
<p>/getData로 api요청 시 <a href="http://localhost:5000/getData%EB%A1%9C">http://localhost:5000/getData로</a> 호출하게한다.</p>
<p>서버에서 package.json에 서버와 리액트를 동시에 틀기위해 설정을 바꾼다.
concurrently가 있어야 동시에 실행이 가능하다 !</p>
<pre><code class="language-js">    &quot;start&quot;: &quot;nodemon app.js&quot;,
    &quot;dev&quot;: &quot;concurrently \&quot;yarn run dev:server\&quot; \&quot;yarn run dev:client\&quot;&quot;,
    &quot;dev:server&quot;: &quot;yarn start&quot;,
    &quot;dev:client&quot;: &quot;cd .. &amp;&amp; yarn start&quot;</code></pre>
<p>그 뒤에 결과 페이지에서 mbti결과를 받아서 어떤 링크로 이동시킬 지 설정해준다.</p>
<pre><code class="language-js">const resultData = (result) =&gt; {
    fetch(`/getData?name=${result}`)
      .then((res) =&gt; {
        return res.json();
      })
      .then((data) =&gt; {
        dispatch(
          updateRecommned(data.recommendRandomData, data.rankData, true),
        );
      });
  };</code></pre>
<p>받아온 결과데이터는 redux를 사용하여 관리해준다.</p>
<h2 id="redux">redux</h2>
<p>스토어에 resultdata 등록</p>
<pre><code class="language-js">import { createStore, combineReducers } from &#39;redux&#39;;
import mbtiqna from &#39;./modules/mbtiqna&#39;;
import mbti from &#39;./modules/mbti&#39;
import resultdata from &#39;./modules/resultdata&#39;;

const rootReducer = combineReducers({ mbtiqna, mbti, resultdata });
const store = createStore(rootReducer);

export default store;</code></pre>
<p>resultdata.js에서 action 생성</p>
<pre><code class="language-js">const UPDATE = &#39;resultdata/UPDATE&#39;;

export function updateRecommned(rcData, rankData, isLoading) {
  return { type: UPDATE, rcData, rankData, isLoading };
}

export default function reducer(state = {}, action = {}) {
  switch (action.type) {
    case &#39;resultdata/UPDATE&#39;: {
      return {
        rcData: action.rcData,
        rankData: action.rankData,
        isLoading: action.isLoading,
      };
    }
    default:
      return state;
  }
}</code></pre>
<p>이런식으로 추천할 데이터와 실시간 랭킹 데이터 크롤링 완료 여부를 판단하는 객체 상태를 리덕스로 관리한다.</p>
<pre><code class="language-js">import { useSelector, useDispatch } from &#39;react-redux&#39;;
const dispatch = useDispatch();
.
.
.
dispatch(updateRecommned(data.recommendRandomData, data.rankData, true)
.
.
. </code></pre>
<p>프로젝트 팀원분이 redux를 익혀두셔서 나도 배우면서 쉽게 사용할 수 있었다 !</p>
<p>받아온 데이터를 통해 react 화면구성에 사용해주었다. 추천 웹툰과 하단에는 실시간 데이터를 넣어준다.</p>
<p><img src="https://user-images.githubusercontent.com/64121533/178947837-b4bfbf85-b14f-4c88-a021-66a866dd2783.gif" alt=""></p>
<p>그 뒤에 express는 heroku를 통해, 페이지는 netlify를 이용해서 배포해주었다.</p>
<blockquote>
<p>배포 페이지
<a href="https://mbtiwebtoontest.netlify.app">https://mbtiwebtoontest.netlify.app</a></p>
</blockquote>
<p>처음에는 크롤링만 되면 금방 할 수 있겠지 싶었는데 예상치 못하게 express도 경험해보고 프론트엔드도 서버를 알아야 한다고 하는 이유를 체감하게 됐다.</p>
<p>여기 서술한거보다 오류가 엄청 많았고 하나하나 쉬운게 없었다. </p>
<p>그래도 잘 작동하는 걸 보면 뿌듯하고 재밌는 프로젝트 였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 Deep Dive 정리  프로퍼티 어트리뷰트(16.1~데이터프로퍼티까지) 부분]]></title>
            <link>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%96%B4%ED%8A%B8%EB%A6%AC%EB%B7%B0%ED%8A%B816.1%EB%8D%B0%EC%9D%B4%ED%84%B0%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EA%B9%8C%EC%A7%80-%EB%B6%80%EB%B6%84</link>
            <guid>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%96%B4%ED%8A%B8%EB%A6%AC%EB%B7%B0%ED%8A%B816.1%EB%8D%B0%EC%9D%B4%ED%84%B0%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EA%B9%8C%EC%A7%80-%EB%B6%80%EB%B6%84</guid>
            <pubDate>Mon, 20 Jun 2022 15:51:30 GMT</pubDate>
            <description><![CDATA[<h2 id="내부-슬롯과-내부-메서드">내부 슬롯과 내부 메서드</h2>
<p>프로퍼티 어트리뷰트를 이해하기 위해 먼저 내부 슬롯과 내부 메서드의 개념을 알아보자.</p>
<p>자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티와 의사 메서드를 말한다.(의사👨‍⚕️?... pseudo를 찾아보니, 가짜라는 뜻이다.)</p>
<p>근데 내부 슬롯과 내부 메서드가 아직 뭔지 알기 어려웠다. 그래서 내용을 더 찾아봤는데
<img src="https://velog.velcdn.com/images/minchan_0/post/f06b29d4-cc90-40ed-8751-2b8b3391bad6/image.png" alt="">
(더 알기 어렵다)</p>
<p>...아무튼! ECMAScript 사양에 등장하는 이중 대괄호[[]]로 감싼 이름들이 바로 내부 슬롯과 내부 메서드이다.</p>
<p>그 다음 내용은 저 위의 내용을 함축적으로 서술하고 있는데, 내부 슬롯과 내부 메서드는</p>
<ol>
<li><p>ECMAScript 사양에 의해 구현되어 있고 자바 스크립트 엔진에서 동작한다.</p>
</li>
<li><p>엔진의 내부 로직이므로 개발자가 직접 접근하거나 호출하도록 공개된 프로퍼티가 아니다.</p>
</li>
<li><p>단, 일부에 한해서 간접적으로 접근할 수는 있다.</p>
</li>
</ol>
<p>EX) [[prototype]]이라는 내부 슬롯은 <strong>proto</strong>를 통해 간접적으로 접근 할 수 있다.</p>
<h3 id="프로퍼티-어트리뷰트와-프로퍼티-디스크립터-객체">프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체</h3>
<p>자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 <strong>상태를 나타내는 프로퍼티 어트리뷰트</strong>를 기본값으로 자동 정의한다. 프로퍼티 어트리뷰트는 자바 스크립트 엔진이 관리하는 내부 상태 값인 내부 슬롯 Value, Writable, Enumerable, Configurable이다.</p>
<p>위 네 가지는 직접 접근할 수 없지만 Object.getOwnPropertyDescriptor 메서드를 사용하여 간접적으로 확인할 수는 있다.</p>
<pre><code class="language-js">const person = {
name : &#39;lee&#39;
};

person.age = 20;

console.log(Object.getOwnPropertyDescriptor(person, &#39;name&#39;));
//프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체를 반환

console.log(Object.getOwnPropertyDescriptors(person));
//모든 프로퍼티의 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체들을 반환한다.</code></pre>
<h3 id="데이터-프로퍼티와-접근자-프로퍼티">데이터 프로퍼티와 접근자 프로퍼티</h3>
<h3 id="데이터-프로퍼티">데이터 프로퍼티</h3>
<p>프로퍼티는 데이터/접근자로 구분할 수 있다.
<img src="https://velog.velcdn.com/images/minchan_0/post/3b6a4d34-1104-4ea6-aec7-c131055cc4ab/image.png" alt=""></p>
<p>데이터 프로퍼티는 위에서도 말했듯 프로퍼티를 생성할 때 기본값으로 자동 정의된다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/f2d4be59-2bd2-4967-a67d-380e143182eb/image.png" alt=""></p>
<p>프로퍼티가 생성될 때 [[Value]]의 값은 프로퍼티 값으로 초기화되고, 나머지는 true로 초기화된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 Deep Dive 정리 함수 부분]]></title>
            <link>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-%ED%95%A8%EC%88%98-%EB%B6%80%EB%B6%84</link>
            <guid>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-%ED%95%A8%EC%88%98-%EB%B6%80%EB%B6%84</guid>
            <pubDate>Mon, 20 Jun 2022 15:45:08 GMT</pubDate>
            <description><![CDATA[<h2 id="함수">함수</h2>
<h3 id="참조에-의한-전달과-외부-상태의-변경">참조에 의한 전달과 외부 상태의 변경</h3>
<p>11장에서 알아본 원시값과 객체의 비교에서 보았듯 원시값은 값에 의한 전달, 객체는 참조에 의한 전달 방식으로 동작한다. 이는 매개변수 또한 마찬가지다. 매개변수도 함수 몸체 내부에서 변수와 동일하게 취급되므로 값에 의한 전달과 참조에 의한 전달 방식을 그대로 따른다.</p>
<p>함수를 호출하면서 매개변수에 값을 전달하는 방식을 값에 의한 호출, 참조에 의한 호출로 구별해 부르는 경우도 있으나, 동작 방식은 값에 의한 전달, 참조에 의한 전달과 동일하다.</p>
<p>어떤 함수를 통해 매개변수로 전달 받은 원시타입 인수와 객체 타입 인수를 함수 몸체에서 변경할 때, 원시 타입 인수를 받은 매개변수의 경우 <strong>원시 값은 변경 불가능한 값</strong>이므로 직접 변경 할 수 없기 때문에 재할당을 통해 할당된 원시 값을 새로운 원시값으로 교체했다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/ce1d97d2-8d22-43ae-9a24-419d1affeb16/image.png" alt=""></p>
<p>하지만 객체 타입 인수를 받은 매개변수의 경우 <strong>객체는 변경이 가능</strong>하기 때문에 재할당없이 직접 할당된 객체를 변경했다. 객체 타입 인수는 참조 값이 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본이 훼손된다. 다시 말해 외부 상태, 즉 함수 외부에서 함수 몸체 내부로 전달한 참조 값에 의해 원본 객체가 변경되는 부수 효과가 발생한다.
<img src="https://velog.velcdn.com/images/minchan_0/post/997abc4c-caa1-4703-9c0a-f85a7a9428ff/image.png" alt="">
<img src="https://velog.velcdn.com/images/minchan_0/post/d48d0115-f3bb-455f-b37e-63602329b705/image.png" alt="">
(알이 후라이가 되지 않도록 하자)</p>
<p>이처럼 함수가 외부 상태를 변경하면 상태 변화를 추적하기 어렵다. 코드의 복잡성을 증가시키고 가독성을 해치게 된다. 복잡한 코드에서 의도치 않은 객체의 변경을 추적하는 것은 어려운 일이다. 객체의 변경을 추적하려면 옵저버 패턴 등을 통해 객체의 참조를 공유하는 모든 이들에게 변경 사실을 통지하고 이에 대처하는 추가 대응이 필요하다.</p>
<blockquote>
<ul>
<li>옵저버 패턴?
객체의 상태 변화를 관찰하는 관찰자 즉 옵저버들의 목록을 객체 등록하여 상태 변화가 있을 때마다 매서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>디자인 패턴?
소프트웨어 공학의 디자인에서 특정 문맥에서 공통적으로 발생하는 문제에 대해 재사용 가능한 해결책이다. &#39;GoF의 디자인 패턴&#39;에서는 객체지향적 디자인 패턴의 카테고리를 생성패턴, 구조패턴, 행동패턴 3가지로 구분하고 있다.</li>
</ul>
</blockquote>
<p>이러한 문제의 해결 방법 중 하나는 객체를 불변 객체로 만들어 사용 것이다. 객체의 복사본을 새롭게 생성하는 비용은 들지만 마치 원시값처럼 변경 불가능한 값으로 동작하게 하는 것이다.</p>
<p>객체의 상태 변경을 원천 봉쇄하고, 상태 변경이 필요한 경우에는 깊은 복사를 통해 새로운 객체를 생성하고 재할당을 통해 교체한다.</p>
<p>외부 상태를 변경하지 않고 외부 상태에 의존하지도 않는 함수를 순수 함수라고 한다. 이를 통해 부수효과를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이려는 프로그래밍 패러다임을 <strong>함수형 프로그래밍</strong>이라 한다.</p>
<h3 id="다양한-함수의-형태">다양한 함수의 형태</h3>
<h3 id="즉시-실행-함수">즉시 실행 함수</h3>
<p>함수 정의와 동시에 즉시 호출되는 함수를 즉시 실행 함수라고 한다. 즉시 실행 함수는 단 한번만 호출되며, <strong>다시 호출 할 수 없다.</strong></p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/bf5146e4-50ba-408f-8a61-f002311a3bc9/image.png" alt="">
(실행되고 다시 호출할 수 없다.)
<img src="https://velog.velcdn.com/images/minchan_0/post/b7843828-8ad2-48f9-b49a-57177f69020c/image.png" alt=""></p>
<p>즉시 실행 함수는 함수 이름이 없는 익명 함수를 사용하는 것이 일반적이다. 함수이름이 있는 기명 즉시 실행 함수도 사용할 수는 있다. 그렇지만 다시 호출하는 것은 여전히 불가능하다.</p>
<p>즉시 실행함수는 반드시 그룹 연산자로 감싸야 한다. 그렇지 않으면 syntaxError가 발생한다.</p>
<ol>
<li><p>익명 함수인 경우</p>
<ul>
<li>그룹 연산자로 감싸지 않은 경우 선언문으로 판단된다. 함수 선언문은 이름을 생략할 수 없다.</li>
</ul>
</li>
</ol>
<ol start="2">
<li><p>기명 함수인 경우</p>
<ul>
<li>그룹 연산자로 감싸지 않은 경우 코드 블록의 닫는 중괄호 뒤에 세미콜론이 암묵적으로 추가되기 때문에 그 위에 ()는 함수 호출이 아닌 그룹 연산자로 해석, 피연산자가 없기 때문에 에러 발생</li>
</ul>
</li>
</ol>
<p>즉 그룹 연산자로 함수를 묶은 이유는 먼저 함수 리터럴을 평가해서 함수 객체를 생성하기 위해서다.
<img src="https://velog.velcdn.com/images/minchan_0/post/65c0d434-26d8-4e63-8d82-41559292c5c0/image.png" alt="">
즉시 실행 함수도 일반 함수처럼 값을 반환할 수 있고 인수를 전달할 수도 있다.</p>
<h3 id="재귀함수">재귀함수</h3>
<p>함수가 자기 자신을 호출하는 것을 재귀 호출이라 한다. 재귀 함수는 자기 자신을 호출하는 행위, 즉 재귀 호출을 수행하는 함수를 말한다.</p>
<p>함수 이름은 함수 몸체 내부에서만 유효하다. 따라서 함수 내부에서는 함수 이름을 사용해 자기 자신을 호출할 수 있다. 함수 표현식으로 정의한 함수 내부에서는 함수 이름은 물론 함수를 가리키는 식별자로도 자기 자신을 재귀 호출할 수 있다. 단 함수 외부에서 함수를 호출할 때는 반드시 함수를 가리키는 식별자로 해야한다.</p>
<p>재귀 함수는 자신을 무한 재귀 호출한다. 따라서 탈출 조건을 반드시 만들어야 한다.</p>
<p>재귀 함수는 반복되는 처리를 반복문 없이 구현할 수 있다는 장점이 있지만, 무한 반복에 빠질 위험이 있고, 이로 인해 스택 오버플로 에러를 발생시킬 수 있으므로 주의해서 사용해야 한다. 따라서 반복문보다 재귀함수가 더 직관적으로 이해하기 쉬울 때만 한정적으로 사용하는 것이 바람직하다.</p>
<h3 id="중첩함수">중첩함수</h3>
<p>함수 내부에 정의된 함수를 중첩함수 또는 내부함수라 한다. 그리고 내부함수를 포함한 함수는 외부함수라 부른다. 일반적으로 중첩함수는 자신을 포함하는 외부 함수를 돕는 헬퍼 함수의 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/60ccd047-95b4-417e-8257-4fdd30504c6d/image.png" alt=""></p>
<p>중첩함수는 스코프와 클로저에 깊은 관련이 있다..</p>
<hr>
<p>참고
모던 자바스크립트 DeepDive</p>
<p>중첩함수 예시</p>
<p><a href="https://velog.io/@sungjun-jin/JavaScript-Functions">https://velog.io/@sungjun-jin/JavaScript-Functions</a></p>
<p>기타</p>
<p><a href="https://ko.wikipedia.org/wiki/%EC%98%B5%EC%84%9C%EB%B2%84_%ED%8C%A8%ED%84%B4">https://ko.wikipedia.org/wiki/%EC%98%B5%EC%84%9C%EB%B2%84_%ED%8C%A8%ED%84%B4</a></p>
<p><a href="https://readystory.tistory.com/114">https://readystory.tistory.com/114</a></p>
<p><a href="https://ko.wikipedia.org/wiki/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%94%94%EC%9E%90%EC%">https://ko.wikipedia.org/wiki/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%94%94%EC%9E%90%EC%</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 Deep Dive 정리 객체 부분]]></title>
            <link>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-4-%EC%97%B0%EC%82%B0%EC%9E%90</link>
            <guid>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-4-%EC%97%B0%EC%82%B0%EC%9E%90</guid>
            <pubDate>Mon, 20 Jun 2022 15:39:07 GMT</pubDate>
            <description><![CDATA[<h2 id="객체">객체</h2>
<p>객체라는 단어 자체가 무슨 느낌인지 잘 감이 안온다. 객체는 메소드와 데이터의 묶음이라고 할 수 있다. 객체를 한자로 뜯어보면, 손 객자에 몸 체 인데 결국 어떤 물건 사물을 말하는 것이라 이해할 수 있다.</p>
<p>객체는 프로퍼티의 개수가 정해져 있지 않고 동적으로 추가/삭제할 수 있다. 프로퍼티의 값에도 제약이 없다. 객체는 복합적인 자료 구조이므로, 관리하는 방식이 원시값과 다르게 복잡하고 구현방식도 브라우저마다 다를 수 있다.</p>
<blockquote>
<ul>
<li>자료구조?</li>
<li><em>효율적인*</em> 접근 및 수정을 가능하게 하는 <strong>자료의 조직, 관리, 저장</strong>을 의미한다. 자바 스크립트 객체는 key와 value로 데이터를 저장하는 자료구조인 해시테이블로 생각할 수 있다. 하지만 클래스 없이 생성하고, 동적으로 프로퍼티와 메서드를 추가할 수 있다는 점에서 차이가 있다. 이는 편리하지만, 성능면에서는 생성과 프로퍼티 접근에 비용이 많이 드는 비효율적인 방식이다.</li>
</ul>
</blockquote>
<p>V8 자바스크립트 엔진에서는 성능을 보장하기 위해 <strong>히든 클래스</strong>라는 방식을 사용하고 있다.</p>
<blockquote>
<ul>
<li>히든 클래스?
객체를 만들 때 히든클래스라는 객체를 생성하여 오프셋 정보를 저장하는 것</li>
</ul>
</blockquote>
<p><del>* 오프셋..?</del></p>
<p><del>세그먼트에 대한 상대 주소</del></p>
<p><del>* 세그먼트..?!???</del></p>
<p><del>메모리 구분을 위해 사용되는 주소</del>
.
.
.
(무슨 말인지 모르겠다. 뒤돌아 나간다.)</p>
<p>객체를 생성하고 프로퍼티에 접근하는 것도 원시 값과 비교할 때 비용이 많이 드는 일이다. 따라서 객체는 원시 값과는 다른 방식으로 동작하도록 설계되어 있다.</p>
<h3 id="변경-가능한-값">변경 가능한 값</h3>
<p><strong>원시값을 할당한 변수</strong>가 기억하는 메모리 주소를 통해 공간에 접근하면, 원시 값에 접근할 수 있다. 객체는 메모리 공간에 접근하면 <strong>참조 값(객체가 저장된 메모리 공간의 주소)</strong>에 접근된다.</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/43d856d8-0fc5-49b6-92b7-e68eedd2df8b/image.png" alt=""><img src="https://velog.velcdn.com/images/minchan_0/post/c5672d8d-999f-4427-b670-fc042c2cd9c0/image.png" alt="">(값에 또 주소가 있었다.)<img src="https://velog.velcdn.com/images/minchan_0/post/6dfeb7a6-83c0-4ec9-8cce-460aa6bd60de/image.png" alt="">(객체의 할당)</p>
</blockquote>
<p>원시 값을 할당한 변수를 참조하면 메모리에 저장되어 있는 원시값에 접근한다. 하지만 객체를 할당한 변수를 참조하면 메모리에 저장되어 있는 참조 값을 통해 실제 객체에 접근한다.</p>
<p>그래서 객체를 할당한 변수는 객체를 참조한다, 객체를 가리키고 있다라고 표현한다. 앞장에서 살펴본 것 처럼 원시 값은 변경 불가능한 값이라 값을 변경하기 위해서는 재할당해줘야 한다.</p>
<p>객체는 변경 가능한 값이다. 재할당없이 동적으로 추가하거나, 프로퍼티 값을 갱신/삭제할 수 있다.
<img src="https://velog.velcdn.com/images/minchan_0/post/b3a74ccf-8426-4970-94c0-797557aa05c1/image.png" alt="">
원시 값이었다면 변경을 위해 재할당을 통해서 메모리에 원시 값을 새롭게 생성한다. 객체는 재할당을 하지 않고 변경이 가능하니, 변수의 참조값은 변경되지 않는다.</p>
<p>객체는 크기가 일정하지 않고, 프로퍼티 값이 객체인 경우 복사 생성 비용이 많이 든다. 효율적인 소비가 어렵고, 성능이 나빠진다. 이는 효율적인 접근/수정을 가능하게 하는 자료구조라고 하기 어려울 수 있다.</p>
<p>따라서 자바 스크립트 객체는 메모리를 효율적으로 사용하기 위해서, 새로 생성하는 비용을 절약해서 성능을 향상시키기 위해 <strong>변경 가능한 값</strong>으로 설계되어 있다.</p>
<p>이는 효율성과 성능을 위해 어느정도 구조적 단점을 감안한 설계이다. 이런 구조는 부작용이 있는데, 여러개의 식별자가 하나의 객체를 공유할 수 있다는 것이다.</p>
<h3 id="참조에-의한-전달">참조에 의한 전달</h3>
<p>객체를 가리키는 변수(원본)을 다른 변수(사본)에 할당하면, <strong>원본의 참조 값이 복사</strong>되어 전달된다. 그러면 사본에는 원본이 가지고 있던 참조값을 갖게 되고, 결과적으로 동일한 객체를 가리키게 된다.</p>
<p>이 것은 두 개의 식별자가 하나의 객체를 공유한다는 것을 의미한다. 따라서, 원본 또는 사본 중 어느 한쪽에서 객체를 변경하면, 서로 영향을 받는다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/312654df-2327-4463-b86e-5fe99601a800/image.png" alt=""></p>
<p>결국 값에 의한 전달과 참조에 의한 전달은 식별자가 기억하는 <strong>메모리 공간에 저장되어 있는 값을 복사해서 전달</strong>하는 면에서 동일하다.</p>
<p>다만 식별자가 기억하는 메모리 공간, 즉 변수에 저장되어 있는 값이 원시 값이냐, 참조 값이냐의 차이만 있을 뿐이다. 따라서 자바스크립트에는 참조에 의한 전달은 존재하지 않고, <strong>값에 의한 전달만이 존재</strong>한다고 말할 수 있다.
<img src="https://velog.velcdn.com/images/minchan_0/post/1f096c2a-b91d-4c7d-bef0-33024bbbfea9/image.png" alt=""></p>
<p>자바스크립트의 이 동작을 설명하는 정확한 용어는 존재하지 않는다. <strong>공유에 의한 전달</strong>이라 표현하는 경우도 있지만 공식적인 용어가 아니고, 정확히 설명하지 못한다.</p>
<p>다만 여기서는 전달되는 값의 종류가 원시 값인지, 참조 값인지 구별하기 위해 값에 의한 전달과 참조에 의한 전달로 구분하기로 한다.</p>
<p>자바스크립트는 포인터가 존재하지 않기 때문에 다른 언어에서의 참조에 의한 전달과 의미가 정확하게 일치하지는 않는다.</p>
<ul>
<li>포인터?</li>
</ul>
<p>메모리의 주소값을 저장하는 변수를 포인터라고 한다.</p>
<hr>
<p>참고</p>
<p>모던자바스크립트 DeepDive</p>
<p><a href="https://ko.wikipedia.org/wiki/%EC%9E%90%EB%A3%8C_%EA%B5%AC%EC%A1%B0">https://ko.wikipedia.org/wiki/%EC%9E%90%EB%A3%8C_%EA%B5%AC%EC%A1%B0</a></p>
<p><a href="https://darozzang.tistory.com/entry/%EC%A0%88%EB%8C%80%EC%A3%BC%EC%86%8C-%EC%84%B8%EA%B7%B8%EB%A8%BC%ED%8A%B8-%EC%98%A4%ED%94%84%EC%85%8B">https://darozzang.tistory.com/entry/%EC%A0%88%EB%8C%80%EC%A3%BC%EC%86%8C-%EC%84%B8%EA%B7%B8%EB%A8%BC%ED%8A%B8-%EC%98%A4%ED%94%84%EC%85%8B</a></p>
<p><a href="https://engineering.linecorp.com/ko/blog/v8-hidden-class/">https://engineering.linecorp.com/ko/blog/v8-hidden-class/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Project - XY게임]]></title>
            <link>https://velog.io/@minchan_0/React-Project-XY%EA%B2%8C%EC%9E%84</link>
            <guid>https://velog.io/@minchan_0/React-Project-XY%EA%B2%8C%EC%9E%84</guid>
            <pubDate>Sun, 19 Jun 2022 07:17:10 GMT</pubDate>
            <description><![CDATA[<p><code>죄수의 딜레마</code> 개념을 활용한 XY 게임을 웹 콘텐츠로 개발했다. 4팀이 10라운드를 거쳐 최대의 이익을 달성하는 멀티 게임이며 기업 교육(한국사회복지공제회, 경기문화재단 등)용으로 <strong>서비스 중</strong>이다.</p>
<p>실제로 서비스되는 웹 콘텐츠 개발에 관심이 있어서 팀에 참여하게 되었다. 프로젝트에서 어떤 것을 했는지 기록해보려 한다.</p>
<p><a href="https://github.com/X-y-game/x-y-game">https://github.com/X-y-game/x-y-game</a></p>
<h2 id="게임-화면-진입-전까지">게임 화면 진입 전까지</h2>
<h3 id="채널-룸-선택-화면">채널, 룸 선택 화면</h3>
<p>api로 채널리스트와 룸 정보를 가진 객체를 받고 <code>map</code>을 통해서 list 컴포넌트를 생성해준다.</p>
<blockquote>
<p>api를 작업한 동료분이 편리하게 api 함수를 만들어줘서 편하게 사용할 수 있었다.</p>
</blockquote>
<pre><code class="language-js">export const getChannelsAPI = () =&gt; {
  const options = {
    method: &quot;GET&quot;,
  };
.
  return fetch(CHANNELS, options);
};</code></pre>
<pre><code class="language-js">import { getRoomAPI } from &quot;../../api/api&quot;;
.
.
const [rooms, setRooms] = useState([]);
useEffect(() =&gt; {
    async function getRoomList() {
      const response = await (await getRoomAPI(channelId)).json();
      setRooms(response.roomLists);
    }
    getRoomList();
  }, []);
.
.
.
return (
    &lt;Body&gt;
.
.
.  
        &lt;WrapChannelUL&gt;
          {rooms?.map(({ _id, title }, index) =&gt; (
            &lt;RoomList
              key={_id}
              id={_id}
              channelIndex={indexId}
              text={title}
              roomNum={index + 1}
              channelId={channelId}
            /&gt;
          ))}
        &lt;/WrapChannelUL&gt;
.
.
    &lt;/Body&gt;
  );</code></pre>
<p>작업 중에 제대로 데이터가 넘어오지 않는 문제가 있었고, <code>옵셔널 체이닝</code>의 존재를 알게 되었다. <code>옵셔널 체이닝</code>은 대상의 undefined나 null이면 뒤의 코드를 진행하지 않고 바로 undefined를 반환하는 연산자이다. 프로퍼티가 없는 중첩 객체를 에러 없이 안전하게 접근할 수 있다. <code>?.</code>를 사용하면 된다.
<img src="https://velog.velcdn.com/images/minchan_0/post/78b656a6-e12b-4bbd-8afe-27a13aab2761/image.png" alt="">
백엔드 작업자분이 만들어 둔 데이터를 사용해서 채널 리스트를 생성해준다.
<img src="https://velog.velcdn.com/images/minchan_0/post/9d20f53b-edc3-478a-8a39-f51341dc2a21/image.png" alt=""></p>
<h3 id="비밀번호-체크하기">비밀번호 체크하기</h3>
<p>채널에는 비밀번호가 존재하는데, 사실 비밀번호 보다는 입장 코드에 가깝다. 최소한의 보안을 위해 이를 확인하는 코드를 작성했다.</p>
<p>먼저 비밀번호를 확인하는 컴포넌트를 만들어 둔다음, 채널에서 그 컴포넌트를 껐다 키는 함수를 작성한다. 비밀번호 컴포넌트에 해당 함수를 보내서 state를 관리해준다.</p>
<blockquote>
<p>openPwForm이 true일 때 컴포넌트가 보이도록 작성</p>
</blockquote>
<pre><code class="language-js">const [openPwForm, setOpenPwForm] = useState(false);
  const handleClick = () =&gt; {
    setOpenPwForm(!openPwForm);
  };
return (
    &lt;&gt;
      {openPwForm &amp;&amp; (
        &lt;CheckPw passWord={pw} 
                   index={index} 
                 channelId={channelId} 
                 title={text} 
                 handleClick={handleClick} 
        /&gt;)}
      &lt;li onClick={handleClick} onKeyDown={handleClick} aria-hidden=&quot;true&quot;&gt;
        {text}
      &lt;/li&gt;
    &lt;/&gt;
  );</code></pre>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/dbe4a281-7aaa-4b99-bab4-de18118cdf7f/image.png" alt=""></p>
<p>비밀번호 컴포넌트에서 input에 들어온 문자열과 props로 받은 password를 비교해 맞는 경우에만 다음 페이지로 이동하도록 했다. <code>useState</code>와 input 태그의 <code>onChange</code>, <code>value</code>를 통해 입력 값을 데이터로 사용할 수 있도록 한다.</p>
<blockquote>
<pre><code class="language-js">const [wrongPw, setWrongPw] = useState(true);
const [inputText, setInputText] = useState(&quot;&quot;);
  const onChange = (event) =&gt; {
    setInputText(event.target.value);
  };
  .
  .
  .
return (
    &lt;Wrap&gt;
      &lt;CheckPassWard&gt;
        &lt;p&gt;비밀번호를 입력하세요 👀&lt;/p&gt;
        &lt;input type=&quot;text&quot; onChange={onChange} value={inputText} /&gt;
        &lt;Message check={wrongPw}&gt;비밀번호가 틀렸습니다.&lt;/Message&gt;
        &lt;EnterButton type=&quot;submit&quot; onClick={checkPw}&gt;
          입력
        &lt;/EnterButton&gt;
      &lt;/CheckPassWard&gt;
      &lt;Dimmed onClick={handleClick}&gt;dimmed&lt;/Dimmed&gt;
    &lt;/Wrap&gt;
  );
  .
  .
  .</code></pre>
</blockquote>
<pre><code>
비밀번호가 틀린 경우에는 경고 문구를 띄워주었다. 그리고 dimd처리를 해줘서 비밀번호 컴포넌트 이 외의 공간을 누르면 `handleClick`이 작동되어 `OpenPwForm`의 상태가 변하고, 컴포넌트가 사라진다.

### 딤드처리 하기

모달이나 팝업을 강조해주기 위해 뒷 화면에 생기는 음영효과를 뜻하는 딤드처리를 해주는 작업을 진행했다. 모달을 닫을 때 굳이 X버튼이 없어도 모달을 제외한 다른 화면을 누르면 딤드가 눌리면서 다시 꺼지게 한다던지 간편하게 활용할 수 있다.

```js
&lt;Dimmed onClick={handleClick}&gt;dimmed&lt;/Dimmed&gt;
.
.
.
const Dimmed = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  font-size: 0;
  background-color: rgba(0, 0, 0, 0.7);
`;</code></pre><p>비밀번호가 맞으면 history.push로 다음 페이지로 이동한다.
이때 채널이름 props를 다음페이지에 보내서 어떤 채널에 들어왔는지 표시하기 위해 코드를 작성했다.</p>
<blockquote>
<pre><code class="language-js">.
.
.//비밀번호 맞는 경우 ...
 history.push({
        pathname: `/channel/${index}`,
        state: { channel: channelId, title: `${title}` },
      });</code></pre>
</blockquote>
<pre><code>
state를 통해 channel, title을 다음페이지에서 활용할 수 있다.
![](https://velog.velcdn.com/images/minchan_0/post/164b8a5d-90f1-46d2-b911-6f5928d1dbde/image.gif)

### 헤더 컴포넌트

채널, 룸, 팀 선택 페이지에는 상단 부분이 중복되어서 이를 헤더 컴포넌트로 분리 했다.
```js
//channels
 &lt;Header title=&quot;채널을 선택하세요&quot; channel=&quot;&quot; roomId=&quot;&quot; /&gt;
//room
&lt;Header title=&quot;룸을 선택하세요&quot; channel={channelTitle} roomId=&quot;&quot; /&gt;
//team
&lt;Header title=&quot;팀 선택&quot; channel={channelIndex} roomId={roomTitle} /&gt;</code></pre><p>헤더에서는 <code>삼항연산자</code>를 통해 주소에 channel/이 포함된 경우를 따져 각 페이지에 정보를 표시했다.</p>
<pre><code class="language-js">export default function Header({ title, channel, roomId }) {
  const location = useLocation();

  return (
    &lt;Title&gt;
      &lt;GoBackButton /&gt;
      {location.pathname.includes(&quot;channel/&quot;) ? (
        &lt;Infodata&gt;{channel}&lt;/Infodata&gt;
      ) : (
        &lt;Infodata&gt;
          &lt;Infodata&gt;{roomId}&lt;/Infodata&gt;
        &lt;/Infodata&gt;
      )}
      {title}
    &lt;/Title&gt;
  );
}</code></pre>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/cc36d96b-11a6-4352-9fb5-a96887f41c4a/image.png" alt=""> 테스트 채널로 들어온 경우 룸 선택 때 채널명이 위에 나온다. 팀 선택인 경우는 들어온 룸 이름이 나온다.<img src="https://velog.velcdn.com/images/minchan_0/post/5972c671-917f-4fda-b12b-31f3903f8481/image.png" alt=""><img src="https://velog.velcdn.com/images/minchan_0/post/46028725-2d5a-4db4-b23d-a0adc19cc4f7/image.png" alt=""></p>
<h2 id="게임-화면">게임 화면</h2>
<h3 id="게임-정보-모달">게임 정보 모달</h3>
<p>게임이 시작된 후 게임에 필요한 모달은 세 가지이다.</p>
<ul>
<li>라운드 결과 모달</li>
<li>중간 결과 모달</li>
<li>게임 최종 결과</li>
</ul>
<p>하나의 모달에서 조건에 맞는 정보를 보여주기 위해 조건부 렌더링을 작업했다.
<img src="https://velog.velcdn.com/images/minchan_0/post/4a03b231-be2b-495f-842a-c51166c08e08/image.png" alt=""></p>
<h3 id="라운드-결과-모달">라운드 결과 모달</h3>
<p>라운드가 끝나는 시점에 모달을 보여줘서 각 팀의 선택카드와 이익, 손실을 표시해준다. </p>
<p>모달컴포넌트에 선택한 카드와 그 라운드의 점수가 props로 전달된다. 그 정보를 가지고 라운드 결과를 배열에 넣어주었다.</p>
<pre><code class="language-js">const roundResultData = () =&gt; {
    const pushData = [];
    for (let i = 0; i &lt; 4; i += 1) {
      pushData.push({
        team: i + 1,
        cardXY: selectCard[i],
        point: roundScore[i],
      });
    }
    return pushData;
  };</code></pre>
<p><code>삼항연산자</code>를 통해 현재 상황이 중간결과인지 조건에 맞게 출력한다. 아까 만들어둔 정보를 <code>map</code>으로 순회하며 출력 해주었다.</p>
<pre><code class="language-js">//isCurrentResult &lt;- 중간 결과(현황 모달에 대한 boolean값)
{isCurrentResult ? (
                  .
                  .
                  .

            ) : (
              //중간 결과가 아닌 경우 라운드 결과 모달이 나온다.
              &lt;WrapResult&gt;
                {roundResultData().map((data) =&gt; (
                  &lt;TeamResult
                    key={`modal_${data.team}`}
                    team={data.team}
                    cardXY={data.cardXY}
                    point={data.point}
                    round={round}
                    handleFinishedModal={handleFinishedModal}
                  /&gt;
                ))}
              &lt;/WrapResult&gt;
            )}</code></pre>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/f99311c0-a9b0-4b00-9652-ec61fd4f9aac/image.gif" alt=""></p>
<h3 id="중간-결과현황-모달">중간 결과(현황) 모달</h3>
<p>아까 <code>삼항연산자</code>를 통해 isCurrentResult가 true일때는 중간 결과 모달을 보여주도록 한다.</p>
<pre><code class="language-js">{isCurrentResult ? (
              &lt;CurrentResult
                scoreData={scoreBoard}
                selectData={selectBoard}
                round={round}
                isFinishResult={isFinishResult}
              /&gt;
            ) : (
              .
              .
              .
            )}</code></pre>
<p>중간 결과 모달에는 각 라운드 별 점수와 선택한 카드, 라운드 정보, 최종결과여부가 props로 전달된다.</p>
<p>전달된 데이터를 활용해서 중간 결과 모달에 맞게 빈 배열에 정보를 넣어주었다. 팀 별 선택카드와 라운드 점수를 넣어주었다. styled-components 로 이익인 경우는 파란색, 손해인 경우는 붉게 적용해서 이익/손해를 파악하기 쉽게 해주었다.</p>
<pre><code class="language-js">const roundData = () =&gt; {
    const pushData = [];
    if (!isFinishResult) {
      for (let i = 0; i &lt; round - 1; i += 1) {
        pushData.push({
          id: i + 1,
          roundNum: i + 1,
          teamOneCardXY: selectData[i][0],
          teamTwoCardXY: selectData[i][1],
          teamThreeCardXY: selectData[i][2],
          teamFourCardXY: selectData[i][3],
          .
          .
          .
        });
      }</code></pre>
<p>당시에는 빨리 기능을 마무리하고싶어서 이런식으로 데이터를 정제했는데 더 좋은 방법이 있었을 것 같다.</p>
<p><strong>발생한 문제</strong></p>
<p>원래는 5라운드라면 4라운드까지만 중간결과를 보여주게 되어 있었지만 데이터 상의 문제로 for문 조건이 바뀌면서 5라운드의 점수까지 보여주게 되었다. 그런데 아직 선택을 하지 않은 팀이 중간 결과 모달을 열어서 다른 팀이 선택한 카드를 볼 수 있는 문제가 있었다. </p>
<p>모든 팀이 선택하지 않으면 점수가 계산되지 않는 점을 활용해서 삼항 연산자로 점수가 계산되지 않은 경우(0점)이면 물음표를 표시하게 해주었다.</p>
<blockquote>
<pre><code class="language-js">.
.
{teamOneScore === 0 ? &quot;?&quot; : teamOneScore}</code></pre>
</blockquote>
<pre><code>![](https://velog.velcdn.com/images/minchan_0/post/2183f240-9036-475f-bb20-566fef056a99/image.gif)


### 최종 결과 모달

모달에서 라운드결과인지 중간결과인지를 판단하기 전에, 최종결과를 보여줄지 말지를 결정해주는 것을 먼저 진행한다. 이 역시 `삼항연산자`를 활용했다.

최종 결과는 각 팀의 점수 합계를 보여주었는데 나중에 피드백으로 수정되어 중간 결과의 형태에서 각 합계를 보여주는 것으로 바뀌었다.

최종 결과를 보여줄 때는 데이터 정제 시 계산을 더 해줘서 배열에 저장했다.
```js
for (let i = 0; i &lt; round; i += 1) {
        one += scoreData[i][0];
        two += scoreData[i][1];
        three += scoreData[i][2];
        four += scoreData[i][3];
        pushData.push({
          id: i + 1,
          roundNum: i + 1,
          teamOneCardXY: selectData[i][0],
          teamTwoCardXY: selectData[i][1],
          teamThreeCardXY: selectData[i][2],
          teamFourCardXY: selectData[i][3],
        .
        .
        .
         .
         .
         .
         .
          total: one + two + three + four,
        });
      }
    }
    return pushData;
  };</code></pre><p><img src="https://velog.velcdn.com/images/minchan_0/post/93404ce7-dc3b-4f30-a672-a18465c033cd/image.png" alt=""></p>
<h2 id="게임-화면-스타일-개선">게임 화면 스타일 개선</h2>
<p>게임 로직은 뛰어난 팀원분이 잘 구현해주셨지만 문제가 있었다. 모두가 느끼고 있었던 것인데, <strong>페이지가 너무 이쁘지 않다는 것</strong>이다.</p>
<p>보기 좋게 하기 위해 대대적인 공사에 들어갔다. 폰트를 적용하고 컨텐츠 별 화면을 개선시켰다.</p>
<ol>
<li>카드 선택</li>
</ol>
<p>선택 시 위에 보이던 팀 별 ? 표시를 없애고 선택한 카드를 내는 것 처럼 애니메이션 작업을 해주었다. <code>keyframes</code>로 애니메이션을 설정해주고 styled-components에서 <code>animation</code>으로 실행시켜 준다.</p>
<pre><code class="language-js">const cardAnim = keyframes`
  0%{
    transform: translate(-50%, 50%);
  }
  100%{
    transform: translate(-50%, 0%);
  }
`;

const SelectCard = styled.div`
  display: flex;
    .
    .
    .
  animation: ${cardAnim} 1s forwards;
`;</code></pre>
<blockquote>
<p>개선 전<img src="https://velog.velcdn.com/images/minchan_0/post/bf616ef7-0de9-4f97-9fca-961c5fe28bc3/image.png" alt="">개선 후<img src="https://velog.velcdn.com/images/minchan_0/post/2ac5711a-d8e9-4326-8fb7-46cebee53fd0/image.gif" alt=""></p>
</blockquote>
<ol start="2">
<li>결과 모달
라운드 결과가 원래는 바로 보였는데 카드의 뒷면을 먼저 보여주고 카드가 돌아가면서 선택한 카드가 보이게 바꾸었다. 뒷면과 앞면을 class로 구분해서 겹쳐두고 각각 애니메이션을 주었다.</li>
</ol>
<pre><code class="language-js">&lt;RotateContainer&gt;
          &lt;BackCard className=&quot;back card&quot;&gt;
            &lt;CardBackGround src={CardBackground} alt=&quot;card-back&quot; /&gt;
          &lt;/BackCard&gt;
          &lt;SelectCard name={cardXY} className=&quot;front card&quot;&gt;
            {cardXY}
          &lt;/SelectCard&gt;
&lt;/RotateContainer&gt;
.
.
const RotateAnimFront = keyframes`
 0%{
  transform: rotateY(-180deg);
 }
 50%{
  transform: rotateY(-180deg);
 }
 100%{
  transform: rotateY(0);
 }
`;

const RotateAnimBack = keyframes`
 0%{
  transform: rotateY(0);
 }
 50%{
  transform: rotateY(0);
 }
 100%{
  transform: rotateY(180deg);
 }
`;
.
.const RotateContainer = styled.div`
  position: relative;

  .card {
    -webkit-backface-visibility: hidden;
    -webkit-transform: translate3d(0, 0, 0);
    -webkit-perspective: 0;
    -webkit-transition: 1s;
    backface-visibility: hidden;
    visibility: visible;
  }

  .front {
    transform: rotateY(-180deg);
    animation: ${RotateAnimFront} 2s forwards;
  }
  .back {
    transform: rotateY(0);
    animation: ${RotateAnimBack} 2s forwards;
  }
`;</code></pre>
<blockquote>
<p>개선 전<img src="https://velog.velcdn.com/images/minchan_0/post/078f5f65-043d-4cc8-a032-67ad86fee1ec/image.gif" alt="">개선 후<img src="https://velog.velcdn.com/images/minchan_0/post/38260403-a70b-43b7-8219-e3a1a2e74b0a/image.gif" alt=""></p>
</blockquote>
<p>또 최종 결과가 나오는 방식을 바꾸었는데 기존에는 마지막 라운드가 종료되면 결과를 보여주지 않고 바로 최종 결과가 나왔다. 그래서 마지막 라운드 결과를 먼저 보여주고 최종결과 모달을 볼 수 있는 버튼을 만들었다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/8e82199a-1835-40bc-b453-bd22a1e1d313/image.gif" alt=""></p>
<ol start="3">
<li>규칙 모달
정확히 알아보기 힘들었던 게임 규칙 모달을 좀 더 직관적으로 바꾸었다.
<img src="https://velog.velcdn.com/images/minchan_0/post/bbe35f21-c6f1-4e3c-90d7-f2182a4b7a0e/image.png" alt=""></li>
</ol>
<h2 id="그-외">그 외</h2>
<h3 id="styled-components">styled-components</h3>
<p>이 프로젝트를 하기 전에는 왜 styled-components써야 하는지 체감하기 어려웠는데 프로젝트 이후 styled-components를 활용하면서 편리함을 느꼈다.</p>
<p>예를 들어 props를 정해둔 뒤 styled-components에서 props에 맞게 스타일을 적용해줄 수 있어서 편리했다.</p>
<pre><code class="language-js">//승패에 따라 배경 색상을 다르게 주기
&lt;CheckCard name={teamOneScore &lt; 0 ? &quot;패&quot; : &quot;승&quot;}&gt;
.
.
.
  background-color: ${(props) =&gt; (props.name === &quot;승&quot; ? &quot;#c3e8fb&quot; : &quot;#ffb7b7&quot;)};</code></pre>
<h3 id="propstype오류-해결">propsType오류 해결</h3>
<p>리액트에서 props 데이터를 넘길 때 type을 명시하라고 eslint가 잔소리를 하는데 익숙하지 않아서 이를 해결하기 위해 꽤나 고생했다.</p>
<p>넘어온 props data에 타입을 명시해주면 된다.</p>
<pre><code class="language-js">CalenderContorol.propTypes = {
  year: PropTypes.string.isRequired,
  month: PropTypes.string.isRequired,
  handlePrevMonth: PropTypes.func.isRequired,
  handleNextMonth: PropTypes.func.isRequired,
};
특정 타입의 행렬이라면(ex. [1,2,3,4,5])
DayofTheWeek: PropTypes.arrayOf(PropTypes.number).isRequired,</code></pre>
<p>데이터가 하나가 아닌 경우 오류 메세지가 떴고 이를 해결하기 위해 <code>oneOfType</code>을 활용했다. 여러 종류중 하나의 종류가 될 수 있는 객체에 활용한다고 한다.(<a href="https://ko.reactjs.org/docs/typechecking-with-proptypes.html">https://ko.reactjs.org/docs/typechecking-with-proptypes.html</a>)</p>
<pre><code class="language-js">channels: PropTypes.oneOfType([PropTypes.objectOf(PropTypes.array), PropTypes.arrayOf(PropTypes.array)]).isRequired,</code></pre>
<h3 id="historypush로-props-데이터-보낼-때-문제-해결">history.push로 props 데이터 보낼 때 문제 해결</h3>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/5269ad77-b5c4-4a18-ab6c-c1266df0ec59/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/242490c9-425e-463d-99e1-b15a243d9955/image.png" alt=""></p>
<p>이런식으로 보낸 데이터를 <code>uselocation</code>으로 받아서 사용했는데, 가끔 여러개의 props를 보내려고 하면 eslint에서 오류를 발생시켰다.(같은 방식인데도 어떤데서는 안되는 문제가 있었다.)</p>
<p>리터널 문법을 활용해서 이를 해결했다.
<img src="https://velog.velcdn.com/images/minchan_0/post/2e6cd21b-9996-47a3-9e37-ce7782e36906/image.png" alt="">
이런식으로 하나의 props에 여러 데이터를 묶어 보낸 뒤</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/c703c763-eb0a-4d2a-9654-04a5e0d3839d/image.png" alt="">
<code>split</code>으로 나눠서 데이터를 받아 활용할 수 있었다. 원인을 찾으면 좋았겠지만 시간이 부족하고 원인도 찾기 힘들어서 일단은 임시방편으로 처리 해두었다.</p>
<h3 id="느낀점">느낀점</h3>
<p>이 프로젝트에 처음 참여할 때만 해도 객체와 <code>map</code>을 활용하지 못해서 그냥 수작업으로 작업할 정도로 react에 익숙하지 못했다. 점차 react에 익숙해지면서 컴포넌트 분리나 state관리 등을 활용해서 작업을 할 수 있었고 react를 왜 쓰는지 체감으로 느낄 수 있는 프로젝트 였다.</p>
<p>같이 작업한 분들의 실력이 좋아서 데이터도 쓰기 편하게 넘겨주시고 여러모로 많이 배울 수 있었다.</p>
<p>또 이 프로젝트는 <code>socket</code>을 활용한 프로젝트였는데 이 부분에서는 1도 도움이 되지 못했다.. 프론트쪽 작업이라도 열심히 하고자 했는데 <code>socket</code>을 좀 더 공부해서 문제해결에 도움이 되었으면 더 좋았을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[오픈소스 프로젝트 [Flex & Grid]]]></title>
            <link>https://velog.io/@minchan_0/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Flex-Grid</link>
            <guid>https://velog.io/@minchan_0/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Flex-Grid</guid>
            <pubDate>Wed, 15 Jun 2022 19:33:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>웹페이지 주소
<a href="https://flexngrid.com/">https://flexngrid.com/</a></p>
</blockquote>
<p>오픈소스 프로젝트인 flexngrid 페이지에서 담당한 영역에 대해 기록해보려 한다.</p>
<p>제주코딩베이스캠프 이호준 대표님이 모집한 프로젝트로, flex와 grid에 대한 내용과 실제로 실습이 가능한 오픈소스 페이지이다. 교육적으로 사용할 수 있는 페이지에 관심이 있었고 재밌을 것 같아 프로젝트에 참여하게 되었다.</p>
<p>이전부터 검색 기능을 해보고 싶었는데 마침 자리가 비어있어서 검색을 담당했다.</p>
<h2 id="검색-모달">검색 모달</h2>
<h3 id="검색한-내역-배열에-저장하기">검색한 내역 배열에 저장하기</h3>
<p>먼저 모달에서 검색한 내역을 저장해주기 위해서 빈 배열을 생성하고, 검색할때마다 input에 들어간 텍스트를 배열에 저장해줬다.</p>
<p>최근 검색어가 위에 올라와야 하므로, 배열 뒤에 저장하는 <code>push</code>가 아닌 앞 순서로 저장하는 <code>unshift</code>로 배열에 저장해주었다.<img src="https://velog.velcdn.com/images/minchan_0/post/3b1ded71-7de2-4022-b12d-5c6caffca9f3/image.png" alt=""></p>
<p>생각한 것과 다르게 신경쓸 것이 꽤 있었는데, 검색 내용 없이 그냥 검색을 했을 경우와 같은 내용을 검색한 경우에도 배열에 추가되어서 조건문으로 비어있거나 이미 있는 내용이 아닐때만 배열에 추가하도록 했다.</p>
<blockquote>
<pre><code class="language-js">if (!searchHistory.includes(searchInputs.value) &amp;&amp; searchInputs.value) {
      if (searchHistory.length &gt; 4) {
        searchHistory.pop();
        searchHistory.unshift(searchInputs.value);
      } else {
        searchHistory.unshift(searchInputs.value);
      }</code></pre>
</blockquote>
<pre><code>
`includes`는 배열이 특정 요소를 포함하는지 판별한다. 그리고 검색어는 5개까지만 저장되기 때문에 5가 넘어갈때 `pop`으로 뒤에 있는 배열요소를 제거했다.

**발생한 문제**
이미 검색한 내용으로 다시 검색한 경우에는 배열에 추가되지 않아서 최근 검색어로 올라오지 않고 같은 자리에 있는 문제가 있었다.

![](https://velog.velcdn.com/images/minchan_0/post/6255ae0b-967e-408a-8d56-f4f5a23ed07b/image.png)

검색어가 동일한 경우에는 `filter`를 사용해서 검색어를 빼고 저장한 뒤 다시 
`unshift`로 저장해줘서 중복된 검색어라면 제일 앞으로 가도록 바꿔서 문제를 해결했다.
&gt;```js
let searchHistory=[];
/*if (!searchHistory.includes(searchInputs.value) &amp;&amp; searchInputs.value) {
      if (searchHistory.length &gt; 4) {
        searchHistory.pop();
        searchHistory.unshift(searchInputs.value);
      } else {
        searchHistory.unshift(searchInputs.value);
      }
    }*/ else if (searchHistory.includes(searchInputs.value)) {
      searchHistory = searchHistory.filter((text) =&gt; {
        return text !== searchInputs.value;
      });
      searchHistory.unshift(searchInputs.value);
    }</code></pre><h3 id="localstorage로-데이터-저장">localstorage로 데이터 저장</h3>
<p>내가 검색한 내역은 다른 페이지에서도 보여야하고, 페이지를 종료했다 다시 들어왔을 때도 저장이 되어 있어야 해서 <code>localstorage</code>를 활용해서 배열리스트를 받아오게 작업했다.</p>
<blockquote>
<pre><code class="language-js">localStorage.setItem(&#39;searchHistoryData&#39;, JSON.stringify(searchHistory));</code></pre>
</blockquote>
<pre><code>
`localStorage`는 무조건 string 형태로 데이터를 저장하기 때문에 `JSON.stringify`로 배열 형태로 저장했다. 받아올때는`JSON.parse`로 받아오면 된다.

&gt;```js
const searchData = localStorage.getItem(&#39;searchHistoryData&#39;);
if (searchData !== null) {
  searchHistory = JSON.parse(searchData);
}</code></pre><p>검색 배열이 달라질때마다 <code>localStorage</code>에 저장해주었다. </p>
<h3 id="검색-리스트-삭제">검색 리스트 삭제</h3>
<p>X버튼을 눌렀을 때 해당 검색 리스트가 삭제되는 기능을 만들기 위해 <code>previousSibling</code>과 <code>filter</code>를 이용했다.</p>
<p>마크업 상 X버튼 위에 텍스트가 저장되기 때문에 요소의 바로 이전 형제를 찾는 <code>previousSibling</code>을 사용해 텍스트를 저장한 뒤 <code>filter</code>로 배열을 다시 반환시킨다.</p>
<blockquote>
<pre><code class="language-js">if (e.target.classList.value == &#39;btn-del&#39;) {
    e.preventDefault();
    const inText = e.target.previousSibling.innerText;
    searchHistory = searchHistory.filter((text) =&gt; {
      return text !== inText;
    });
    localStorage.setItem(&#39;searchHistoryData&#39;, JSON.stringify(searchHistory));
}</code></pre>
</blockquote>
<pre><code>
전체 삭제는 배열을 빈 배열로 바꿔주기만 하면 된다.

### 리스트 동적 생성

모달이 켜질때 배열의 정보로 리스트를 생성해주기 위해 함수를 작성했다. 마크업은 따로 담당자가 있어서 담당자가 만든 마크업대로 생성해주었다. forEach로 배열을 순회해서 순서대로 동적으로 생성해준다. `createElement`, `setAttribute`, `appendChild`, `createTextNode`을 이용했다. 이전에도 js 프로젝트에서 해당 기능들로 생성했었기 때문에 쉽게 만들 수 있었다.

**발생한 문제**

모달이 열릴 때 마다 동적생성이 이뤄지기 때문에 계속해서 리스트가 추가되는 문제가 있었다. 생성하기 전에 한번 비워주는 과정이 필요했다.
&gt;```js
const removeChildAll = (ele) =&gt; {
  while (ele.hasChildNodes()) {
    ele.removeChild(ele.firstChild);
  }
};</code></pre><p>생성하는 함수에 해당 코드로 자식 요소를 비워주게 해서 같은 리스트가 계속 쌓이는 문제를 해결했다.</p>
<blockquote>
<p>완성된 모습
<img src="https://velog.velcdn.com/images/minchan_0/post/fcfd6d86-290d-4e30-9a87-5c096e67b27c/image.gif" alt=""></p>
</blockquote>
<h3 id="검색-페이지로-이동하기">검색 페이지로 이동하기</h3>
<p>검색 버튼을 누르거나 검색한 리스트를 누르면 <code>location.href</code>을 통해 검색페이지로 이동 시킨다. 처음에는 <code>localStorage</code>로 검색어를 저장해서 사용했는데 같이 기능을 담당한 동료가 <strong>엔터입력</strong>으로 검색이 가능하게 바꾸고, url에 표시된 검색어를 사용할 수 있게 코드를 개선시켜 주었다.</p>
<p><code>form</code>태그를 이용해 get방식으로 엔터 입력 시 URL에 파라미터의 형태로 전송해준뒤, 검색 페이지에서 <code>URLSearchParams</code>를 이용해 검색어를 가져온다.</p>
<pre><code class="language-js">//검색 페이지
const URLSearch = new URLSearchParams(location.search);
const searchQuery = URLSearch.get(&quot;q&quot;);</code></pre>
<p>이때만 해도 페이지 이동 = <code>localStorage</code>사용에 매몰되어 있어서 별다른 생각을 하지 않고 작업했는데 좋은 의견을 주어 더 편리하게 검색을 이용할 수 있었다!🙇‍♂️ </p>
<p>검색 모달 관련한 기능은 아주 어려운 부분 없이 금방 끝낼 수 있었다.</p>
<h2 id="검색-페이지">검색 페이지</h2>
<p>검색페이지에서는 검색어에 관련된 리스트를 보여주고, 클릭 시 해당 내용의 페이지로 이동하는 페이지이다.</p>
<blockquote>
<p>피그마 디자인<img src="https://velog.velcdn.com/images/minchan_0/post/a41c6207-dcd8-4476-87ae-6b9720a31947/image.png" alt=""></p>
</blockquote>
<h3 id="검색리스트-동적-생성">검색리스트 동적 생성</h3>
<p>검색어까지 검색페이지에 넘겨주었으니 이제 남은 것은 검색어를 가지고 해당하는 리스트를 생성하는 일만 남았다.</p>
<p>어떻게 해야할지 고민이 많이 됐다. 설명 페이지의 내용을 가져오는 것은 둘째치고, 내용으로 검색을 하게 되면 중복되는 경우가 분명 생길텐데 어떻게 해야할지 감이 잘 잡히지 않았다.</p>
<p>내용페이지의 마크다운을 fetch로 받아와서 string 형태의 데이터를 정제해서 사용할 수는 있었는데 제목은 #으로 잡혀있어서 <code>filter</code>를 통해 데이터를 정제할 수 있었지만 설명부분은 따로 가져오기가 난감했다. 게다가 제목이 아닌 내용에 #이 포함되어 있는 경우에도 <code>filter</code>에 걸려 같이 넘어와 사용하기 힘들었다.</p>
<blockquote>
<p>마크다운을 fetch로 받아온 string<img src="https://velog.velcdn.com/images/minchan_0/post/9c1d1a62-3e4d-42fb-a10c-0ed7d5364567/image.png" alt=""></p>
</blockquote>
<p>그러던 중 에디터를 맡은 동료가 설명페이지를 마크다운으로 파싱해서 다시 마크업으로 전환하는 기능을 만들었고 제목에는 <code>&lt;h2&gt; ~ &lt;h4&gt;</code>, 문장에는 <code>&lt;p&gt;</code>가 붙은 string 데이터를 얻을 수 있게 되었다.</p>
<h3 id="단계별로-개발해보자">단계별로 개발해보자!</h3>
<p>기간이 충분하지 않으므로 일단 <strong>할 수 있는 것</strong>부터 개발을 진행하기 시작했다. 내용을 제외하고 제목 기준으로만 리스트를 생성하도록 처음 목표를 설정했다. 최종 목표는 내용기준으로도 리스트를 만드는 것이다.</p>
<h3 id="1단계--제목으로만-찾기">1단계 : 제목으로만 찾기</h3>
<p>빈 배열에 flex와 grid내용을 각각 <code>filter</code>를 통해 제목을 가져오고, map으로 내용을 모두 소문자로 바꾼다. 그렇게 나온 배열 중에 검색어에 포함된 내용을 push해준다.</p>
<blockquote>
<pre><code class="language-js">const contents = [];
.
//fetch로 마크다운 파싱하는 코드 
.
.
contents.push(
      flexhtml.filter(
        (v) =&gt; v.includes(`h2`) || v.includes(`h3`) || v.includes(`h4`)).map((v) =&gt; v.toLocaleLowerCase())
        .filter((v) =&gt; v.includes(searchQuery.toLowerCase())),,
      gridhtml.filter(
        (v) =&gt; v.includes(`h2`) || v.includes(`h3`) || v.includes(`h4`)).map((v) =&gt; v.toLocaleLowerCase())
        .filter((v) =&gt; v.includes(searchQuery.toLowerCase())),
);</code></pre>
</blockquote>
<pre><code>
contents 배열을 forEach로 순회해서 flex와 grid에서 받아온 제목을 리스트 생성 함수에 넣어줬다.

&gt;```js
contents.forEach((v, i) =&gt; {
    v.forEach((value) =&gt; {
     .
     .
     //리스트를 생성하는 코드
     .
     .
    });</code></pre><p>id값이 제목으로 정해져있기 때문에 받아온 value의 태그를 정규식으로 삭제 후 리스트에서 a 태그 href에 value를 넣어서 해당 위치로 이동하게 해줬다. </p>
<pre><code class="language-js">value = value.replace(/&lt;\/?[^&gt;]+(&gt;|$)/g, &quot;&quot;);
const searchListItemLink = document.createElement(&quot;a&quot;);
      searchListItemLink.setAttribute(
        &quot;href&quot;,
        `${i == 0 ? `/flex/#${value}` : `/grid/#${value}`}`
      );
value = value.replace(/[0-9.]/g, &quot;&quot;);
</code></pre>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/c42c1237-fb9f-4907-b845-e6515b7288c9/image.png" alt=""></p>
<p><strong>발생한 문제</strong>
정규식에서 숫자와 .을 제거했는데 제목의 뒤쪽에 있는 숫자들도 삭제되서 해당 부분을 수정했다.
<img src="https://velog.velcdn.com/images/minchan_0/post/e5c71aff-8cff-4c09-ab25-7b70c82a5929/image.png" alt="">수정 후<img src="https://velog.velcdn.com/images/minchan_0/post/5202c03c-cac9-4d6a-b60d-ca961be58d94/image.png" alt=""></p>
<h3 id="2단계--제목에서-상위-항목을-포함하기">2단계 : 제목에서 상위 항목을 포함하기</h3>
<p>이제 제목으로 리스트를 생성했으니까 그 제목의 상위 항목을 찾게 개선시켜보기로 한다.
<img src="https://velog.velcdn.com/images/minchan_0/post/c308af0f-6369-479b-9c7d-524a3e2ebd4a/image.png" alt=""></p>
<p>설명 페이지의 구조는 큰 제목<code>h2</code>와 그 아래에 있는 항목 <code>h3</code>, <code>h4</code>으로 이뤄져 있다.</p>
<blockquote>
<pre><code class="language-js">gridhtml.filter((v) =&gt; v.includes(`h2`) || v.includes(`h3`) ||
v.includes(`h4`)).map((v) =&gt; v.toLocaleLowerCase())</code></pre>
</blockquote>
<pre><code>
일단 받아오는 방식을 제목 모두를 가져오게 바꾸고, 아이템을 생성할 때 검색어를 포함하는지 확인하게 변경했다.

그리고 value가 `h3`나 `h4`라면 while문으로 `h2`가 나올때까지 index를 줄여서 상위 제목을 찾는다
&gt;```js
.
.
contents.forEach((v, i) =&gt; {
v.forEach((value, j) =&gt; {
      if (value.includes(searchQuery.toLowerCase())) {
        if (value.includes(&quot;h3&quot;) || value.includes(&quot;h4&quot;)) {
          while (!contents[i][j].includes(&quot;h2&quot;)) {
            j--;
            if (j &lt; 0) {
              break;
            }
          }
          highTag = contents[i][j];
        } else if (value.includes(&quot;h2&quot;)) {
          highTag = value;
        }
        .
        .
        .
        .
        const searchRoute = document.createElement(&quot;span&quot;);
        searchRoute.setAttribute(&quot;class&quot;, &quot;route-search&quot;);
        searchRoute.appendChild(
          document.createTextNode(
            i == 0 ? `flex &gt; ${highTag}` : `grid &gt; ${highTag}`
          )
        );
        const searchTitle = document.createElement(&quot;strong&quot;);
        searchTitle.setAttribute(&quot;class&quot;, &quot;tit-search&quot;);
        searchTitle.appendChild(document.createTextNode(value));
      .
      .
      .
      .
      }</code></pre><p>변수에 해당 내용을 넣어서 사용한다. 처음부터 <code>h2</code>였다면 value자체를 상위 제목으로 사용했다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/55116bd6-69ac-44ed-b32e-81f71b7dab5f/image.png" alt=""></p>
<p>1단계에서는 하나의 value값으로 현재 제목과 상위 제목을 전부 사용했기 때문에 같은 제목인 경우 내용이 달라도 어디 내용인지 알 수 없었지만 이제 상위 제목으로 어떤 내용에서 온 리스트인지 알게 되었다.</p>
<h3 id="3단계--내용으로-찾기">3단계 : 내용으로 찾기</h3>
<p>이제 최종 목표인 내용으로도 검색해서 리스트를 생성하는 일만 남았다. <code>filter</code>로 가져올 때 p태그를 포함해서 가져오도록 한다.</p>
<blockquote>
<pre><code class="language-js">gridhtml.filter(
        (v) =&gt;
          v.includes(`h2`) ||
          v.includes(`h3`) ||
          v.includes(`h4`) ||
          v.includes(`&lt;p&gt;`)
      )</code></pre>
</blockquote>
<pre><code>
![](https://velog.velcdn.com/images/minchan_0/post/aa5ff0a4-3dc1-4986-81b4-18ad6f2c4476/image.png)

`h2`를 찾을때와 마찬가지로 `&lt;p&gt;`도 찾아준다. 먼저 제목이 나오니까 그에 해당하는 내용은 제목보다 아래 index에 있다. index를 증가시켜서 `&lt;p&gt;`를 찾아 사용했다.
&gt;```js
.
.
.
 if (value.includes(&quot;h3&quot;) || value.includes(&quot;h4&quot;)) {
          currentTitle = value;
          while (!contents[i][j].includes(&quot;&lt;p&gt;&quot;)) {
            j++;
            if (contents[i][j].includes(&quot;&lt;p&gt;&quot;)) {
              currentDesc = contents[i][j];
            }
          }
   .
   .
   .
   .</code></pre><p>그런데 이렇게 내용을 가져오면 문제가 있다. <code>1.1. 들어가기에 앞서</code>의 내용은 <code>p</code>태그 4개로 이뤄진 문장인데 이 경우 첫 번째 문장만 사용하게 된다. 또 <code>p</code>를 기준으로 찾는 경우에도 해당 내용의 제목을 찾을 때 중복이나 올바른 동작을 하지 않을 가능성이 크다.</p>
<p>그래서 아이템을 생성하기 전에, 배열 데이터를 다시 정제했다. <code>p</code>태그가 나오게 되면 그 index를 저장하고 그 <code>p</code>태그를 빈 배열에 더해준다. 그리고 <code>splice</code>로 그 태그를 삭제한다. 그 뒤에 다시 확인해서 <code>p</code>태그가 아닐때 까지 while문을 사용했다.</p>
<p>while문을 빠져나오게 되면 처음에 저장한 index에 <code>splice</code>로 추가해준다.</p>
<blockquote>
<pre><code class="language-js"> contents.forEach((check, i) =&gt; {
    for (let j = 0; j &lt; contents[i].length - 1; j++) {
      if (check[j].includes(&quot;&lt;p&gt;&quot;)) {
        firstDesc = j;
        while (contents[i][j].includes(&quot;&lt;p&gt;&quot;)) {
          desc += contents[i][j];
          contents[i].splice(j, 1);
          if (j &lt; contents[i].length - 1) {
          } else {
            break;
          }
          if (contents[i][j].includes(&quot;&lt;p&gt;&quot;)) {
            continue;
          } else {
            break;
          }
        }
        contents[i].splice(firstDesc, 0, desc);
        desc = [];
      }
    }
  });</code></pre>
</blockquote>
<pre><code>

이를 통해 제목 밑에 있는 설명 내용이 하나의 p태그에 담기게 된다.

![](https://velog.velcdn.com/images/minchan_0/post/e82fc6b5-5479-4a0b-bc7d-938745ea88b3/image.png)이렇게 되있던 p 태그들이 2번에 모두 합쳐짐
![](https://velog.velcdn.com/images/minchan_0/post/d5a02663-750d-4bb8-82f8-c970363b2a17/image.png)

&gt;1. `h2`를 찾은 경우
상위 제목과 현재 제목을 value로 넣어준다. while문으로 `p`태그를 찾아서 있는 경우에 변수에 넣어서 사용하고, `h3`, `h4`가 나오는 경우에는(큰 제목 밑에 내용이 없는 경우) 빈칸으로 두었다.
&gt;```js
.
.
.
else if (value.includes(&quot;h2&quot;)) {
          highTag = value;
          currentTitle = value;
          while (!contents[i][j].includes(&quot;&lt;p&gt;&quot;)) {
            j++;
            if (contents[i][j].includes(&quot;&lt;p&gt;&quot;)) {
              currentDesc = contents[i][j];
            } else if (
              contents[i][j].includes(&quot;h3&quot;) ||
              contents[i][j].includes(&quot;h4&quot;)
            ) {
              currentDesc = &quot;&quot;;
              break;
            }
          }
        }</code></pre><blockquote>
<ol start="2">
<li><code>h3</code>, <code>h4</code>를 찾은 경우 
현재 제목을 value로 넣어주고, 역시 while문으로 <code>p</code>태그를 찾아서 변수에 넣어주고, 그 뒤에 <code>h2</code>를 찾아 상위 제목도 찾아 사용한다.<pre><code class="language-js">if (value.includes(&quot;h3&quot;) || value.includes(&quot;h4&quot;)) {
       currentTitle = value;
       while (!contents[i][j].includes(&quot;&lt;p&gt;&quot;)) {
         j++;
         if (contents[i][j].includes(&quot;&lt;p&gt;&quot;)) {
           currentDesc = contents[i][j];
         }
       }
       while (!contents[i][j].includes(&quot;h2&quot;)) {
         j--;
         if (contents[i][j].includes(&quot;h2&quot;)) {
           highTag = contents[i][j];
           break;
         }
         if (j &lt; 0) {
           break;
         }
       }
     }</code></pre>
</li>
</ol>
</blockquote>
<pre><code>
&gt;3. `p`를 찾은 경우 
내용 변수에 value를 넣어주고, while문으로 `h2 ~ h4`를 찾아서 현재 제목을 설정하고, `h2`를 찾아서 상위 제목에 넣어준다.
&gt;```js
.
.
.
else if (value.includes(&quot;&lt;p&gt;&quot;)) {
          currentDesc = value;
          while (!contents[i][j].includes(&quot;h2&quot;)) {
            j--;
            if (
              contents[i][j].includes(&quot;h2&quot;) ||
              contents[i][j].includes(&quot;h3&quot;) ||
              contents[i][j].includes(&quot;h4&quot;)
            ) {
              currentTitle = contents[i][j];
              if (contents[i][j].includes(&quot;h2&quot;)) {
                highTag = contents[i][j];
                break;
              }
              while (!contents[i][j].includes(&quot;h2&quot;)) {
                j--;
                if (contents[i][j].includes(&quot;h2&quot;)) {
                  highTag = contents[i][j];
                  break;
                }
              }
              break;
            }
            if (j &lt; 0) {
              break;
            }
          }
        }
.
.
.</code></pre><p><strong>발생한 문제들</strong></p>
<ol>
<li>검색어가 제목과 내용 둘다 포함된 경우 중복되서 리스트가 생성되는 문제가 있었다.
어차피 링크 이동은 제목 기준으로만 이동하기 때문에 prev변수를 만들어서 이전에 이미 이 제목으로 된 리스트가 있지 않은 경우에만 생성해서 중복을 방지했다. </li>
</ol>
<blockquote>
<pre><code class="language-js">.
.
.
 if (prev != currentTitle) {
   //리스트 생성 부분
          prev = currentTitle;
          const searchListItem = document.createElement(&quot;li&quot;);
   .
   .
   .</code></pre>
</blockquote>
<pre><code>
2. 정규식에 문제가 있어서 제목이 또 이상하게 걸러지는 문제가 있었다. 공백과 .을 기준으로 다시 정규식을 변경했다.

&gt;제목 규칙이 `1.1. 제목` 이런식으로 숫자뒤에 마침표가 찍히고 공백이 한칸 있다. 그걸 기준으로 정규식을 변경했다.
```js
currentTitle = currentTitle.replace(/[^s]+[.]/gi, &quot;&quot;);</code></pre><ol start="3">
<li>내용을 모두 소문자로 변경해서 검색했었는데, 제목에 대문자가 포함된 경우가 있었다. 이 경우 id값이 달라서 링크 이동이 제대로 되지 않았기 때문에 같이 공부하는 동료의 의견을 받아 대소문자 상관없이 검색되도록 정규식을 사용했다.</li>
</ol>
<blockquote>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/b74af43c-df2e-4429-a8dd-5682727bc8e3/image.png" alt=""></p>
</blockquote>
<pre><code class="language-js">if (value.includes(value.match(new RegExp(searchQuery, &quot;i&quot;))))</code></pre>
<p>정규식 i플래그를 사용하면 대소문자를 구분하지 않고 비교해준다. <code>tolowercase</code>로 소문자로 바꿨던 내용을 없애고 정규식을 통해 확인해 주니 굳이 변환할 필요도 없고 대소문자 관련한 다양한 문제가 해결되었다.<img src="https://velog.velcdn.com/images/minchan_0/post/338c8ee8-b5b9-42f0-889f-b1e71c447b1d/image.png" alt=""></p>
<p>결과는..</p>
<blockquote>
<p>내용으로도 검색되는게 이렇게 신날 줄이야!<img src="https://velog.velcdn.com/images/minchan_0/post/01700871-9085-4c40-bc58-687bc328b95f/image.png" alt=""></p>
</blockquote>
<h2 id="그-외-작업">그 외 작업</h2>
<h3 id="다크모드-유지하기">다크모드 유지하기</h3>
<p>다크모드로 css 스타일이 바뀐 후 설정을 유지하기 위해 <code>localStorage</code>를 사용했다. 다크모드를 누를 때 마다 boolean 변수를 사용하여 true false로 localStorage에 저장해서 사용했다.</p>
<pre><code class="language-js">const handleDarkMode = () =&gt; {
.
.
.
isDark = !isDark;
localStorage.setItem(&quot;darkmode&quot;, isDark);
}</code></pre>
<p><code>getItem</code>으로 true인지 false인지 확인 해서 다크모드를 실행할지 아닐지를 정해줬다. string으로 저장되서 그냥 문자열로 확인해주었다.</p>
<pre><code class="language-js">if (currentMode == &quot;true&quot;) {
isDark=true;
//다크모드 실행
} esle {
isDark=false;
//다크모드 취소
          }</code></pre>
<h3 id="사이드-메뉴-자동-스크롤">사이드 메뉴 자동 스크롤</h3>
<p>설명 페이지의 사이드 메뉴의 스크롤이 페이지 스크롤에 반응하지 않는 점이 아쉬워서, 담당자분께 허락을 받고 사이드 메뉴도 스크롤 되도록 변경했다.</p>
<blockquote>
<p>수정 전
<img src="https://velog.velcdn.com/images/minchan_0/post/c4caf1f5-2719-4a01-9593-33fbc6d9ad10/image.gif" alt="">수정 후<img src="https://velog.velcdn.com/images/minchan_0/post/226fdc0a-9517-44a9-b05b-8df23bb69224/image.gif" alt=""></p>
</blockquote>
<p>이미 사이드메뉴에 스크롤스파이가 적용되어 있어서 bold처리된 메뉴의 <code>offsetTop</code>을 이용해 <code>scrollTo</code>로 사이드 메뉴의 스크롤을 동작시켰다. 자연스러운 위치 이동을 위해서 사이드 메뉴의 height값의 절반만큼 빼줬다.</p>
<pre><code class="language-js">.
.
//스크롤스파이 적용...
drawerMenu.scrollTo(
0, drawerTit[i].offsetTop - drawerMenu.clientHeight / 2);</code></pre>
<p><strong>아쉬운 점</strong>
기능 구현에 중점을 둬서 깔끔한 느낌의 코드가 아니라서 아쉬웠다. 스파게티 그 자체인 느낌이었고 알고리즘에 익숙하다면 더 깔끔하게 만들 수 있지 않을까? 여러모로 아쉽다.</p>
<p><strong>개선할 부분</strong>
내용으로 검색된 아이템에 검색한 텍스트가 표시되면 좋을 것 같다.</p>
<p><strong>느낀점</strong>
작은 목표부터 차근차근 밟아 나가며 이번 기능을 완성했는데 단계별로 완성할 때마다 성취감이 있었다. 아쉬운 부분이많지만 어떻게든 구현했다는게 중요하다고 생각해서, 일단은 작은 만족을 느꼈다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[팀 프로젝트 LionTime]]></title>
            <link>https://velog.io/@minchan_0/%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-LionTime</link>
            <guid>https://velog.io/@minchan_0/%ED%8C%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-LionTime</guid>
            <pubDate>Wed, 15 Jun 2022 05:56:11 GMT</pubDate>
            <description><![CDATA[<p>멋쟁이 사자처럼 프론트엔드스쿨 1기에서 진행한 팀 프로젝트인 SNS 마켓 프로젝트에서 담당한 영역을 어떻게 작업했는지 기록을 남겨보려고 한다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/f3eb09eb-bd09-49f5-97f2-89c9909e2160/image.png" alt=""></p>
<p>이 프로젝트의 목표는 <strong>바닐라 자바스크립트를 이용해 SNS 서비스의 프론트엔드 구현과 API를 이용한 서버 연결</strong>이다.</p>
<p>나는 채팅화면 구성, 하단 탭 메뉴, 모달 버튼 영역을 맡게 되었는데, 작업하다보니 API를 사용하는 영역이 없었다.(어라?)</p>
<p>대신 작업을 빨리 끝내고 API영역을 도와주면 되겠다 싶어서 서둘러서 작업을 진행했다. 채팅화면은 기능이 지원되지 않아서 말그대로 html/css작업만 했고, 하단 탭 메뉴도 금방 끝났다.</p>
<h2 id="모달기능">모달기능</h2>
<p>페이지에는 다양한 버튼이 있는데, 그 버튼 마다 나오는 모달 안에 메뉴가 다르다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/d75c6ba8-0a90-4971-ae49-97af4ec9c0ee/image.png" alt=""></p>
<p>각각 태그를 만들어서 보여주는 것보단 하나의 모달에 메뉴를 동적으로 생성하는게 재사용성도 좋고, html가 훨씬 줄어들 것 같아 js 작업을 시작했다.</p>
<h3 id="메뉴-동적-생성하기">메뉴 동적 생성하기</h3>
<h4 id="createelement">createElement</h4>
<p>createElement는 요소를 생성하는 함수이다.</p>
<pre><code class="language-js">document.createElement(&#39;div&#39;)</code></pre>
<p>이런식으로 div 태그를 생성할 수 있다.</p>
<h4 id="setattribute">setAttribute</h4>
<p>setAttribute는 요소에 어트리뷰트를 추가하는 함수이다.</p>
<pre><code class="language-js">Element.setAttribute(&quot;class&quot;,&quot;seletBtn&quot;)</code></pre>
<h4 id="appendchild-createtextnode">appendChild, createTextNode</h4>
<p>appendChild는 지정 요소의 자식요소를 추가하는 함수이다.
createTextNode는 요소에 텍스트를 추가하는 함수이다.</p>
<pre><code class="language-js">Element.appendChild(document.createTextNode(&#39;채팅방 나가기&#39;))</code></pre>
<p>이 네 가지 함수들로 메뉴를 동적으로 생성해 줄 수 있었다.</p>
<p>먼저 addEventListener로 내가 누른 target의 class명으로 어떤 버튼인지 파악해준 뒤, 그 버튼에 맞는 요소를 생성하도록 함수를 작성했다.</p>
<pre><code class="language-js">if (e.target.classList.value === &#39;btn-menu&#39;) {
        const menulist = createEle(&#39;li&#39;, &#39;class&#39;, &#39;list-modal-menu&#39;);
        const menuBtn = createEle(&#39;button&#39;, &#39;type&#39;, &#39;button&#39;);
        addAttr(menuBtn, &#39;class&#39;, &#39;btn-list close-chat-room&#39;);
        menuBtn.appendChild(document.createTextNode(&#39;채팅방 나가기&#39;));
        menulist.appendChild(menuBtn);
        modalContainer.appendChild(menulist);
    }

function createEle(eleName, attr, attrName) {
    const createEle = document.createElement(eleName);
    createEle.setAttribute(attr, attrName);
    return createEle;
}

function addAttr(ele, attr, attName) {
    ele = ele.setAttribute(attr, attName);
    return ele;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/dac31c7e-b4f0-463b-af3b-1e69c1bdc520/image.png" alt=""></p>
<p>모달은 <code>display: none</code>에서 버튼을 누르면 classList.add로 class를 추가해서, css에서 <code>display:block</code>으로 바꿔줘서 나타나게 해줬는데, 나는 모바일에서 나오는 모달처럼 스르륵 올라오길 바랬다.</p>
<p>그래서 기본 모달크기와 그 안의 메뉴의 크기를 계산해서 위로 올라오도록 추가 작업을 시작했다.</p>
<h4 id="childelementcount">childElementCount</h4>
<p>childElementCount는 요소의 자식의 갯수를 반환한다.</p>
<p>요소의 갯수만큼 아이템의 height값을 곱해서 밑으로 내려줬다.</p>
<pre><code class="language-js">bottomValue = modalContainer.childElementCount * 46 + 46;
modal.style.bottom = `-${bottomValue}px`;</code></pre>
<p>요소의 높이가 46으로 고정되있어서 급한김에 46 그대로 썼는데, 요소의 height 값을 구해서 계산해주면 나중에 변경점이 있을때를 대비할 수 있을 것같다.</p>
<p>이제 버튼을 누르면 메뉴가 생성되고 아이템 갯수에 따라 화면 밑으로 모달이 위치하게 된다. 그 뒤 모달의 bottom값이 0으로 위치값을 바꾼다. 모달 css에 <code>transition</code>을 줘서 내가 원하는 스르륵 올라오는 모달이 완성된다.</p>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/faac7804-4a75-4fa8-85ea-9c6099c3c5b6/image.gif" alt=""></p>
<p>그 외 메뉴를 눌렀을 때 각각 기능을 추가해서 내가 맡은 영역을 마무리했다. 생각보다는 시간이 더 걸렸지만 다른 사람들의 API작업도 도와주면서 프로젝트를 잘 마무리 할 수 있었다.</p>
<p>나중에 팀 리더이신분이 프로젝트 이름에 맞게 이미지를 교체해주시고 다양한 기능을 추가해서 보기 좋은 프로젝트가 된 것 같다.</p>
<h3 id="아쉬운-점">아쉬운 점</h3>
<p>API를 나중에 도와주면서 만지기는 했지만 담당영역에 API작업이 없어서 아쉬웠다. 대신 여러 사람들이 가져온 API로 작업을 해서 거의 모든 컨텐츠의 API를 확인할 수는 있었다.</p>
<p>또 처음으로 js에서 동적으로 요소를 생성했는데, 더 깔끔하게 할 수 있는 방법이 있을 것 같다. 코드 재사용성이 좋지 못했다. js 공부한지 얼마되지 않은 시점이어서 더 아쉬웠다.</p>
<p>지금이라면 배열과 반복문을 통해서 더 적은 코드로 재사용하기 편하게 만들 수 있을 것 같아 나중에 기회가 되면 리펙토링을 진행해보고 싶다.</p>
<h3 id="마무리하며">마무리하며</h3>
<p>팀 프로젝트에서 중요한 것은 진행 중인 영역에 대한 공유와 커뮤니케이션인 것 같다. 이번에 만들어진 팀은 갑자기 만들어진 팀이라 서로 좀 서먹했고 소통이 살짝 부족했던 것 같다. 다들 착해서 분위기는 좋았지만 좀 더 친해질 시간이 있었으면 더 좋았겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 Deep Dive 정리 3 데이터 타입]]></title>
            <link>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-3-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-3-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</guid>
            <pubDate>Thu, 28 Apr 2022 20:17:32 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-타입">데이터 타입</h2>
<p>자바스크립트에는 객체타입을 제외하고 7개의 원시 데이터 타입이 있다.
** - number, string, boolean, undefined, null, symbol**</p>
<h3 id="숫자타입">숫자타입</h3>
<p>숫자타입은 정수와 실수를 구분하지 않고 하나의 숫자타입만 존재한다고 명시되어 있는데 이는 C언어와 같은 정적언어와는 다른 부분이다. </p>
<p>C언어의 경우 정수와 실수를 구분하는 데이터 타입(int, float 등)이 있어서 상황에 맞게 선언하여 사용하는데, 자바스크립트는 모든 수를 실수형 데이터 타입으로 사용한다.</p>
<p>이 숫자타입의 값은 배정밀도 64비트 부동소수점 형식을 따른다. 컴퓨터는 2진법을 사용하고 몇몇 소수에서 10진법-&gt;2진법으로 변환할 때 무한소수가 발생한다. 컴퓨터는 무한소수를 모두 담아낼 수 없기 때문에 유한한 숫자로 바꾼다. 그러다 보면 연산이 우리가 생각한 값과 다르게 작동되는 것이다.</p>
<blockquote>
<p>이런식으로 난리가 난다(?)
<img src="https://velog.velcdn.com/images/minchan_0/post/e1a0195f-95a0-4fbc-8ba0-3278e71e7903/image.png" alt=""><img src="https://velog.velcdn.com/images/minchan_0/post/2e5655be-c8a3-4a35-92e3-40e6fba51775/image.png" alt=""></p>
</blockquote>
<ul>
<li>소수점 계산 문제를 어떻게 대응할까?</li>
</ul>
<ol>
<li>toFixed()
입력받은 숫자를 매개변수 만큼 반올림해서 string으로 변환하는 함수 toFixed를 사용하여 대응.
toFixed(0)이면 정수를 반환한다.
<img src="https://velog.velcdn.com/images/minchan_0/post/5b7270e3-979e-491c-be2b-ad074c8c6bc6/image.png" alt=""></li>
<li>Math.round()
매개변수로 들어온 값을 반올림하여 가까운 정수로 반환하는 함수를 사용하여 대응.
<img src="https://velog.velcdn.com/images/minchan_0/post/40680c7e-99d3-4c54-80dd-46e37ddf937c/image.png" alt=""></li>
</ol>
<h3 id="문자열타입">문자열타입</h3>
<p>문자열 타입의 설명이 문자열이다(...)
문자열은 문자가 나열된 형태라고 생각하면 쉽다. 연속된 문자들의 집합인 문자열은 배열의 형태를 가지고 있다.</p>
<blockquote>
<p>배열처럼 사용할 수 있다.
<img src="https://velog.velcdn.com/images/minchan_0/post/b7229993-c5d5-45e4-a4d8-b007c8d1863f/image.png" alt=""></p>
</blockquote>
<p>C언어 같은 경우 char라고 하는 한 문자만 받는 자료형이 있지만 자바스크립트는 문자열을 받는 형태만 가지고 있다. 일반적으로 <strong>작은따옴표</strong>로 텍스트를 감싸서 표현한다.</p>
<p>그 외에는 <strong>큰따옴표, 백틱(`)</strong> 으로 감싸 사용한다. 감싸지 않으면 텍스트로 인식하지 않기 때문에 꼭 감싸줘야 한다.</p>
<p>일반 문자열에서는 개행(줄바꿈)이 허용되지 않는다. 그래서 개행이 필요할 때 이스케이프 시퀀스를 사용하게 된다.</p>
<blockquote>
</blockquote>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/22832c02-007c-4718-88e7-6914284e076d/image.png" alt=""></p>
<p>그런데 <code>템플릿 리터널</code>이라고 하는 문자열 표기법은 있는 그대로를 출력하기 때문에 이스케이프를 사용할 필요가 없다. 공백이나 줄바꿈이 모두 허용한다. 대신 백틱(`)을 이용하여 감싸야한다. 있는 그대로를 출력하기 때문에 변수를 사용해도 변수명을 그대로 출력한다.</p>
<p>표현식을 리터널 안에서 사용하려면, ${}안에 표현식을 넣어 사용할 수 있다.</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/c580d1af-0012-48dc-ad8d-20c864b90625/image.png" alt="">템플릿 리터널이 아닌 일반적인 문자열에서는 + 연산자를 통해 사용하면 된다.<img src="https://velog.velcdn.com/images/minchan_0/post/4ad41e1a-5b61-49bc-a7dd-e3d14ff2305a/image.png" alt=""></p>
</blockquote>
<h3 id="불리언타입">불리언타입</h3>
<p>true flase로만 이뤄진 타입이다. 보통 조건문과 함께 사용된다.</p>
<h3 id="undefined타입">undefined타입</h3>
<p>정의되지 않음을 나타내기 위한 타입이다. 자바스크립트가 처음 할당이 이뤄지기 전까지 빈상태로 두지 않기 위해 초기화하는 값이다. 그래서 변수를 참조했을 때 undefined가 반환된다면 초기화가 되지 않았다는 의미이다.</p>
<p>이 타입은 자바스크립트가 변수를 초기화할 때 사용하는 값으로, 굳이 개발자가 null을 놔두고 undefined로 할당하는 일은 없어야 겠다.</p>
<p>let 변수는 <strong>선언 후 초기 값을 지정하지 않으면</strong> undefined로 초기화 된다.
var 변수는 <strong>선언 후 암묵적으로</strong> undefined로 초기화 한다.
(더 알아보고 싶다면 <code>시간상 사각지대</code>내용을 추가로 확인해보자)</p>
<h3 id="null타입">null타입</h3>
<p>null타입은 null 타입이 유일하다. 프로그래밍 언어에서 null은 변수에 값이 없다는 것을 의도적으로 명시할때 사용한다.</p>
<p>null을 변수에 할당하는 것은 변수가 이전에 참조하던 값을 더 이상 참조하지 않겠다는 의미이다. 자바스크립트 엔진은 누구도 참조하지 않는 메모리 공간에 대해 <code>가비지 콜렉션</code>을 수행할 것이다. 또는 함수가 유요한 값을 변환할 수 없는 경우 명시적으로 null을 반환하기도 한다.</p>
<h3 id="symbol타입">symbol타입</h3>
<p>symbol은 es6에서 추가된 타입으로 변경이 불가능한 원시 타입의 값이다. 다른 값과 중복되지 않는 유일무이한 값이기 때문에 주로 이름이 충돌할 위험이 없는 객체의 프로퍼티 키를 만들기 위해 사용한다.</p>
<blockquote>
<p>심벌은 함수를 호출해 생성한다.</p>
</blockquote>
<pre><code class="language-js">var key = Symbol(&#39;key&#39;);</code></pre>
<h3 id="객체타입">객체타입</h3>
<p>자바스크립트 타입은 크게 원시타입과 객체타입으로 나뉜다. 두 타입이 근본적으로 다르다는 의미이다. 현재는, <strong>자바스크립트를 이루고 있는 거의 모든 것이 객체</strong>라는 것만 알아두자.</p>
<h3 id="데이터-타입의-필요성">데이터 타입의 필요성</h3>
<h4 id="타입에-의한-메모리-공간의-확보와-참조">타입에 의한 메모리 공간의 확보와 참조</h4>
<p>값은 메모리에 저장하고 참조할 수 있어야 한다. 값을 저장하려면 먼저 메모리 공간에 크기를 결정해야 한다. 자바스크립트 엔진은 변수에 할당되는 값의 데이터 타입에 따라 확보해야 할 메모리 공간의 크기가 결정된다.</p>
<p>예를 들어, score라는 변수에 100을 저장한다면 자바스크립트는 score에 숫자타입 값이 할당되어 있으므로 score를 숫자타입으로 인식한다. 숫자타입은 8바이트 단위로 저장되므로 score를 참조하면 8바이트 단위로 메모리 공간에 저장된 값을 읽는 것이다.</p>
<h4 id="데이터-타입에-의한-값의-해석">데이터 타입에 의한 값의 해석</h4>
<p>메모리에 읽어 들인 2진수를 어떻게 해석하느냐를 판단해야 한다. 숫자 65는 문자열로 해석하면 A가 된다. score가 숫자타입이라는 것을 인식해야 의도에 맞는 사용을 할 수 있을 것이다.</p>
<blockquote>
<p>데이터 타입은 값의 종류를 말한다. 자바스크립트의 모든 값은 데이터 타입을 갖는다.</p>
</blockquote>
<ul>
<li>값을 저장할 때 확보해야 하는 메모리 공간의 크기를 결정하기 위해</li>
<li>값을 참조할 때 한 번에 읽어 들여야 할 메모리 공간의 크기를 결정하기 위해</li>
<li>메모리에서 읽어 들인 2진수를 어떻게 해석할지 결정하기 위해</li>
</ul>
<h3 id="동적타이핑">동적타이핑</h3>
<h4 id="동적-타입언어와-정적타입-언어">동적 타입언어와 정적타입 언어</h4>
<p>C언어와 같은 정적 타입 언어는 변수를 선언할 때 변수에 할당할 수 있는 값의 종류, 즉 데이터 타입을 사전에 선언해야 한다. 이를 명시적 타입선언이라고 한다. 정적 타입 언어는 컴파일 시점에 타입체크(선언한 데이터 타입에 맞는 값을 할당했는지 검사)를 수행하고 만약 체크를 통과하지 못하면 에러를 발생시키고 프로그램 실행을 막는다.</p>
<p>그러나 자바스크립트는 var, let, const로 어떤 데이터 타입의 값이라도 자유롭게 할당할 수 있다. 자바스크립트에서는 값을 할당하는 시점에 변수의 타입이 동적으로 결정되고 변수의 타입을 언제든지 변경 할 수 있다.</p>
<p>즉 자바스크립트에서 변수는 선언이 아닌 할당에 의해 타입이 결정된다. 이를 <strong>동적타이핑</strong>이라 한다.</p>
<h4 id="동적-타입-언어와-변수">동적 타입 언어와 변수</h4>
<p>동적타입언어는 편리하지만 구조적 단점도 분명 있다.</p>
<ul>
<li>복잡한 프로그램에서는 변화하는 변수 값을 추적하기 어려울 수 있다.</li>
<li>개발자의 의도와 상관없이 자바스크립트 엔진에 의해 암묵적으로 타입이 변환되기도 한다.</li>
<li>안정적인 프로그램을 위해 변수 데이터 타입을 체크해야 하는 경우 코드의 양이 증가하고 번거롭다.</li>
</ul>
<p>그러니 우리는 변수를 제한적으로 사용해야 한다.</p>
<ul>
<li>변수의 유효범위(스코프)를 최대한 좁게 만들어 변수의 부작용을 방지</li>
<li>전역적으로 사용하는 변수를 최대한 쓰지 않는다.</li>
<li>변수보다 상수를 사용하여 값의 변경을 억제한다.</li>
<li>식별자의 이름을 의미 파악이 쉽도록 작성한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 Deep Dive 정리 2 표현식과 문]]></title>
            <link>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-2-%ED%91%9C%ED%98%84%EC%8B%9D%EA%B3%BC-%EB%AC%B8</link>
            <guid>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-2-%ED%91%9C%ED%98%84%EC%8B%9D%EA%B3%BC-%EB%AC%B8</guid>
            <pubDate>Wed, 27 Apr 2022 01:34:35 GMT</pubDate>
            <description><![CDATA[<h4 id="값">값</h4>
<p>표현식이 평가되어 생성된 결과를 값이라고 한다.</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/minchan_0/post/ffc667d2-70a0-4ee6-a1bb-2462255a7dc9/image.png" alt=""></p>
</blockquote>
<p>10+20은 평가되어 30이라는 값을 생성한다. 모든 값은 타입을 가지고있고, 2진수로 저장된다.</p>
<p>변수 sum에는 10+20이 할당되는 것이 아니라, 10+20의 식이 평가되어 나온 결과인 30이 값으로 할당된다. 즉 10+20은 할당 이전에 평가되어 값을 생성해야 한다.</p>
<p>값은 다양한 방법으로 생성하는데, 기본적으로 <code>리터널</code>을 사용한다.</p>
<h4 id="리터널">리터널</h4>
<p>리터널이란 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법이다.</p>
<p>숫자, 알파벳, 한글, 또는 기호 &#39;&#39;,&quot;&quot;, [], {}...로 표기한 코드를 리터널이라고 한다. 이를 통해 다양한 종류의 값을 생성할 수 있다.</p>
<blockquote>
</blockquote>
<p>ex) 1,2,3..(정수 리터널) 10.1, 123.3...(부동소수점 리터널) true, false(불리언 리터널) 등.. </p>
<h4 id="표현식">표현식</h4>
<p>값으로 평가될 수 있는 문을 표현식이라 한다. 표현식이 평가되면 새로운 값을 생성하거나 기존 값을 참조한다. 리터널 역시 값으로 평가되기 때문에, 리터널도 표현식이다. </p>
<pre><code class="language-js">var score = 50+50;</code></pre>
<p>50+50은 리터널과 연산자로 이뤄져 있다. 해당 식이 평가되어 값을 생성하므로 표현식이라 할 수 있다.</p>
<p>이 score변수를 참조하면 생성하지는 않지만, 값으로 평가되기 때문에 이 역시 표현식으로 본다. 이처럼 값으로 평가될 수 있는 문은 모두 표현식이라 할 수 있다.</p>
<h4 id="문">문</h4>
<p>프로그램을 구성하는 기본 단위이자 최소 실행 단위를 문이라고 한다.</p>
<p>문의 집합으로 이뤄진 것이 바로 프로그램이며, 문을 작성하고 순서에 맞게 나열하는 것이 프로그래밍이다. 문은 여러 토큰으로 구성되는데, 토큰은 문법적으로 더 이상 나눌 수 없는 코드의 기본 요소를 의미한다.</p>
<blockquote>
<p>ex)키워드, 연산자, 식별자, 리터널, 세미콜론...</p>
</blockquote>
<p>문은 컴퓨터에게 내리는 명령이다. 선언문, 할당문, 조건문 등으로 구분할 수 있다.</p>
<h4 id="세미콜론과-세미콜론-자동-삽입-기능">세미콜론과 세미콜론 자동 삽입 기능</h4>
<p>다른 언어를 경험하고서 자바스크립트를 하면 좀 어색한 부분이 있다. 변수 사용이나 호이스팅같은 것이 그렇다. 그리고 세미콜론 자동 삽입기능도 마찬가지다. 자바스크립트가 문의 끝이라고 예측되는 지점에 세미콜론을 자동으로 붙이기 때문이다.</p>
<p>그렇지만 개발자가 예상한 부분과 자바스크립트가 예측한 부분이 다를 수 있으므로, 세미콜론을 붙이는 쪽을 선호하려 한다.</p>
<h4 id="표현식인-문과-표현식이-아닌-문">표현식인 문과 표현식이 아닌 문</h4>
<p>위에서 값으로 평가될 수 있는 문은 표현식이라 한다는 것을 명시했다. 표현식이 아닌 문은 무엇일까? 쉽게 생각하면, 값으로 평가될 수 없는 문은 표현식이 아닌 문이다. 예를 들어 변수 선언문을 보자</p>
<pre><code class="language-js">var a;</code></pre>
<p>a는 어떤 값으로 평가될 수 없기 때문에 표현식으로 볼 수 없다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 Deep Dive 정리 1 변수]]></title>
            <link>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-1-%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@minchan_0/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-%EC%A0%95%EB%A6%AC-1-%EB%B3%80%EC%88%98</guid>
            <pubDate>Wed, 20 Apr 2022 03:20:24 GMT</pubDate>
            <description><![CDATA[<h3 id="컴퓨터-메모리">컴퓨터 메모리</h3>
<p>사람은 두뇌로 계산과 기억 모두 처리하지만, 컴퓨터는 <code>cpu</code>를 사용해서 연산하고 메모리를 사용해서 <code>데이터</code>를 기억한다.</p>
<p><code>메모리</code>는 <code>데이터</code>를 저장할 수 있는 메모리 셀의 집합체이다. 메모리 셀은 하나당 1바이트의 크기를 가지고 있다. 컴퓨터는 메모리 셀의 크기 단위로 데이터를 <code>저장</code>하거나 <code>읽어</code>들인다.</p>
<p>컴퓨터는 모든 데이터를 2진수로 처리하는데, 10 + 20을 수행한다고 생각해보면 숫자 10과 20은 <code>메모리 공간</code>에 기억되어야 한다. 그 뒤 <code>cpu</code>가 그 것을 연산하고 결과 값인 30을 임의의 메모리 공간에 저장한다.</p>
<blockquote>
<p>메모리가 호텔이라면...
<img src="https://velog.velcdn.com/images/minchan_0/post/a1a2f472-ca01-4777-b9aa-9b0940a24d3b/image.png" alt=""></p>
</blockquote>
<p>연산 결과가 메모리에 저장되긴 했지만 이 숫자를 재사용할 수 없다. 재사용을 위해서는 결과값이 저장된 메모리 공간에 직접 접근해야 한다. 하지만 주소를 통해 직접 접근하는 것은 매우 위험하다.</p>
<p>의도와 다른 값이 변경되거나 시스템이 멈출 수 있고, 직접적으로 메모리를 제어하더라도 값이 저장될 메모리 주소는 코드가 실행될 때 상황에 따라 임의로 결정되기 때문에 개발자가 사용하기 좋지 않고 리스크만 클 뿐이다. 그리고 알려주지도 않는다.</p>
<blockquote>
<p>데이터의 프라이버시를 지키자..
<img src="https://velog.velcdn.com/images/minchan_0/post/be862d2c-44c5-4cc3-b056-ffac4bcc606f/image.png" alt=""></p>
</blockquote>
<p>그래서 프로그래밍 언어는 기억하고 싶은 값을 메모리에 <code>저장</code>하고 그 값을 <code>읽어</code> 재사용하기 위해 <code>변수</code>라는 매커니즘을 제공한다.</p>
<h3 id="변수">변수</h3>
<p><code>변수</code>는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 메모리 공간을 식별하기 위해 붙인 이름을 말한다.</p>
<p><strong>값을 가리키는 상징적</strong>인 이름이다. 변수는 값이 저장된 메모리 공간의 주소로 치환되어 실행된다. 따라서 개발자는 안전하게 <code>변수</code>를 통해 값에 접근할 수 있다.</p>
<blockquote>
<p><code>변수</code>는 하나의 값을 저장하기 위한 수단이지만, 객체나 배열같은 자료구조를 사용하면 여러 개의 값을 하나로 <strong>그룹화</strong>해서 하나의 값처럼 사용할 수 있다.
<img src="https://velog.velcdn.com/images/minchan_0/post/dc4045e2-8edb-43bc-952d-efeb7d7c8685/image.png" alt="">
다시 처음에 수행했던 10 + 20을 <code>변수</code>에 넣어서 실행해보자.
<img src="https://velog.velcdn.com/images/minchan_0/post/728fd7ac-5f20-4e62-b202-899446ac1a09/image.png" alt="">
이제 변수 result를 통해 저장된 30이라는 값을 재사용할 수 있게 되었다!</p>
</blockquote>
<h3 id="식별자">식별자</h3>
<ul>
<li>메모리 공간에 저장된 값을 식별할 수 있는 이름을 <strong>변수명(식별자)</strong></li>
<li>변수에 저장된 값은 <strong>변수 값</strong></li>
<li>변수에 값을 저장하는 것은 <strong>할당</strong></li>
<li>변수에 값을 읽어들이는 것은 <strong>참조</strong></li>
<li>식별자는 값이 아닌, <code>메모리 주소</code>를 기억하고 있다.</li>
</ul>
<p>식별자는 꼭 변수 이름에만 국한하지 않고 함수나 클래스 등의 이름은 모두 식별자라 한다. 이런 이름들은 네이밍 규칙을 준수해야 하고, <code>선언</code>에 의해 자바 스크립트 엔진에 알린다.</p>
<h3 id="변수-선언">변수 선언</h3>
<p>변수를 <code>생성</code>하는 것을 말한다. 변수를 사용하기 위해서는 반드시 선언이 필요하다. 선언에는 var, let, const 키워드를 사용한다. (이 세가지 키워드는 다음에 더 깊게 알아볼 수 있다.)</p>
<p>변수 선언 이후 값을 할당하지 않으면 값이 비어 있을 것으로 생각할 수 있지만, 자바스크립트는 undefined라는 값으로 <code>암묵적 할당</code>을 한다.</p>
<p>var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 동시에 진행된다. 즉 </p>
<pre><code class="language-js">var score;</code></pre>
<p>이렇게 <strong>선언</strong>되면 score를 등록한 뒤 <strong>초기화 단계</strong>를 통해 undefiend로 할당해서 초기화 한다.</p>
<blockquote>
</blockquote>
<p>변수 이름을 비롯한 식별자는 <code>실행 컨텍스트</code>에 등록된다. 자바스크립트 엔진이 소스코드를 평가하고 실행하기 위해 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역이다.</p>
<p>만약 초기화 단계를 거치지 않으면 확보된 메모리 공간에는 이전에 다른 애플리케이션이 사용했던 값이 남아 있을 수 있다. 이러한 값을 쓰레기 값이라 하는데, var 키워드는 암묵적으로 초기화함으로 이를 방지한다.</p>
<h3 id="변수-선언의-실행-시점과-호이스팅">변수 선언의 실행 시점과 호이스팅</h3>
<p>c언어에서 변수 선언을 참조 코드보다 밑에 하면 오류가 일어난다.
<img src="https://velog.velcdn.com/images/minchan_0/post/50ff1ba9-f84d-4b12-9b42-aca8ebf10c80/image.png" alt=""></p>
<p>코드는 한 줄 씩 위에서 아래로 순차적으로 실행되므로, 참조 에러가 발생하는 것은 당연해 보이지만 자바스크립트는 이런 경우 undefined를 출력한다.
<img src="https://velog.velcdn.com/images/minchan_0/post/bd0423e6-0043-4c34-b14e-d3d55a2112f0/image.png" alt="">
변수 선언이 런타임 보다 그 이전 단계에서 먼저 실행되기 때문이다.</p>
<p>자바 스크립트는 순차적인 실행에 앞서서 소스 코드의 평가 과정을 거치고, 그 과정에서 모든 선언문을 먼저 찾아서 실행한다. 즉 변수 선언이 어디 있던지 다른 코드보다 먼저 실행된다.</p>
<p>이처럼 변수 선언문이 코드의 선두로 <strong>끌어 올려진 것처럼</strong> 동작하는 자바스크립트 고유의 특징을 변수 <strong>호이스팅</strong>이라고 한다. var, let, const, function, class 키워드를 사용해서 선언하는 모든 식별자는 호이스팅 된다.</p>
<h3 id="값의-할당">값의 할당</h3>
<p>변수에 값을 할당할 때는 할당 연산자(=)를 사용한다. 이 연산자는 우변의 값을 좌변의 변수에 할당한다.</p>
<pre><code class="language-js">var score= 80;</code></pre>
<p>이런식으로 <code>선언</code>과 <code>할당</code>을 하나의 문으로 단축 표현 할 수 있다. 자바스크립트는 이런 단축 표현을 하더라도 <code>선언</code>과 <code>할당</code>을 나누어서 실행한다.</p>
<p><code>선언</code>은 런타임(소스코드가 순차적으로 실행되는 시점)보다 먼저 실행되지만,
<code>할당</code>은 런타임에 실행된다.
<img src="https://velog.velcdn.com/images/minchan_0/post/a6358581-0572-4f2a-91c4-c00ff607040c/image.png" alt=""></p>
<h3 id="값의-재할당">값의 재할당</h3>
<p>이미 값이 할당되어 있는 변수에 새로운 값을 다시 할당하는 것을 재할당이라고 한다. 만약 값을 재할당할 수 없어서 변수에 저장된 값을 변경할 수 없다면 <code>상수</code>라고 한다. const 키워드로 선언한 변수는 재할당이 금지된다. 따라서 상수를 표현할 수 있다.</p>
<p>score라는 변수에 80을 할당했다가, 90으로 재할당했다고 가정하면, 이전에 할당되었던 undefined와 80은 어떤 변수도 값으로 가지지 않은 상태가 된다. 이것은 undefined와 80이 필요하지 않다는 것이고, 이런 불필요한 값은 <code>가비지 콜렉터</code>에 의해 자동 해제된다.</p>
<p><code>가비지 콜렉터</code>는 메모리 공간을 주기적으로 검사해서 더 이상 사용하지 않는 메모리를 해체하는 기능을 말한다. C언어같은 경우 개발자가 <strong>직접 메모리 관리</strong>를 통해 성능 향상을 노려볼 수 있지만 치명적인 오류를 발생시킬 가능성도 높다. 자바스크립트는 언매니지드 언어로, 개발자가 메모리 해체 시점을 알수는 없지만 역량에 의존하는 부분이 적어지니 일정한 생산성을 확보할 수 있다.</p>
<h3 id="식별자-네이밍-규칙">식별자 네이밍 규칙</h3>
<ul>
<li>특수문자를 제외한 문자, 숫자, 언더스코어, 달러기호를 포함할 수 있다.</li>
<li>숫자로 시작하지 않는다.</li>
<li><code>예약어</code>로 네이밍 하지 않는다.(예약어는 프로그래밍 언어에서 사용되고 있거나 예정인 단어를 말한다. ex) await, break, case 등등..)</li>
<li>네이밍은 그 의미를 알 수 있도록 작성한다.</li>
</ul>
<blockquote>
<p>컨벤션은 하나 이상의 영어 단어로 구성된 식별자를 만들 때 가독성을 위해 규정한 명명 규칙이다.</p>
</blockquote>
<pre><code class="language-js">//카멜케이스
var camelCase;
//스네이크 케이스
var snake_case;
//파스칼 케이스
var PascalCase;
//헝가리안 케이스(타입+식별자)
var strFirstName;</code></pre>
<p>자바스크립트에서는 일반적으로 변수나 함수의 이름에는 <strong>카멜 케이스</strong>, 클래스의 이름에는 <strong>파스칼 케이스</strong>를 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS Project 플러피 버드 제작 과정]]></title>
            <link>https://velog.io/@minchan_0/JS-Project-%ED%94%8C%EB%9F%AC%ED%94%BC-%EB%B2%84%EB%93%9C-%EC%A0%9C%EC%9E%91-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@minchan_0/JS-Project-%ED%94%8C%EB%9F%AC%ED%94%BC-%EB%B2%84%EB%93%9C-%EC%A0%9C%EC%9E%91-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Fri, 08 Apr 2022 05:59:14 GMT</pubDate>
            <description><![CDATA[<p>javaScript로 간단한 게임을 만들면서 로직 구현도 해보고 js에 더 익숙해질 겸 플러피 버드를 만들기로 했다.</p>
<p>플러피 버드는 클릭만으로 플레이하는 간단한 게임으로 오래 전 굉장히 유행했었다.</p>
<p><img src="https://media.vlpt.us/images/minchan_0/post/bb774983-077c-493d-87e5-50f929e14bf9/image.png" alt=""></p>
<p>게임 제작에 입문하거나 가볍게 만드는 작품으로 많이 이용되서 리소스도 많은 편이나, 직접 만드는 게 더 재밌을 거 같아서 직접 그렸다.</p>
<p>닭 캐릭터와 배경, 위/아래 장애물만 그리면 되서 그림은 금방 그렸다. html로 게임화면으로 사용할 태그를 넣어주고, 그 안에 캐릭터와 장애물을 배치했다.</p>
<pre><code class="language-html">&lt;div class=&quot;background&quot;&gt;
      &lt;div class=&quot;chiken&quot;&gt;&lt;/div&gt;

      &lt;div class=&quot;down-wall&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;down-wall&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;down-wall&quot;&gt;&lt;/div&gt;

      &lt;div class=&quot;top-wall&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;top-wall&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;top-wall&quot;&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre>
<p>css로 각각, background에 배경 이미지, chiken에 캐릭터 이미지, wall에 각각 위 아래 이미지를 적용시켰다.</p>
<p><img src="https://media.vlpt.us/images/minchan_0/post/604f6b6d-4a6d-4ceb-96e6-b611504a2143/image.png" alt=""></p>
<h4 id="중력-적용">중력 적용</h4>
<blockquote>
<p>닭 캐릭터가 중력을 받아 밑으로 떨어지도록 하는 코드를 작성, 자연스럽게 떨어지는 것을 구현하기 위해 닭의 bottom에 계속 점프력을 담은 변수를 더해준다. 그리고 점프력에 중력값을 빼서 중력을 받는 것처럼 보일 수 있게 코드를 작성했다.</p>
</blockquote>
<pre><code class="language-javascript">//이런식으로 작성
let jumpPower = 3;
let gravity = 0.3;
chikenBottom += jumpPower;
jumpPower -= gravity;
chiken.style.bottom = `${chikenBottom}px`;</code></pre>
<h4 id="점프-구현">점프 구현</h4>
<blockquote>
<p>클릭 이벤트를 받아서 클릭 시 점프력에 다시 값을 재할당하면 캐릭터가 위로 점프하듯이 위치가 올라간다.</p>
</blockquote>
<pre><code class="language-js">  function jump() {
    jumpPower = 6.5;
  }
  document.addEventListener(&quot;click&quot;, () =&gt; {
      jump();
  });</code></pre>
<p>장애물이 움직이고, 캐릭터가 그 사이를 넘어가면 점수를 획득 할 수 있게 html을 추가로 작성했다.</p>
<pre><code class="language-html">      &lt;div class=&quot;chiken&quot;&gt;&lt;/div&gt;

      &lt;div class=&quot;down-wall anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;down-wall anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;down-wall anim&quot;&gt;&lt;/div&gt;

      &lt;div class=&quot;top-wall check anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;top-wall check anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;top-wall check anim&quot;&gt;&lt;/div&gt;
      &lt;span class=&quot;score&quot;&gt;Score : &lt;/span&gt;
      &lt;div class=&quot;start&quot;&gt;Touch!&lt;/div&gt;</code></pre>
<h4 id="장애물-애니메이션-구현">장애물 애니메이션 구현</h4>
<blockquote>
<p>장애물은 css 애니메이션으로 움직이게 하고, js를 통해 위치를 랜덤하게 바꿔줬다.</p>
</blockquote>
<pre><code class="language-css">.top-wall.anim,
.down-wall.anim {
  animation: wallmove 3s infinite linear;
}
@keyframes wallmove {
  0% {
  }
  100% {
    left: -300px;
  }
}</code></pre>
<p><code>animationiteration</code>속성을 이용해 애니메이션이 끝날때, 랜덤하게 위치를 바꾸도록 한다.</p>
<pre><code class="language-js"> topWalls.forEach((topWall, idx) =&gt; {
    topWall.addEventListener(&quot;animationiteration&quot;, () =&gt; {
      randomPos = Math.floor(Math.random() * 160 - 40) + 40;
      topWall.style.top = `-${randomPos}px`;
      downWalls[idx].style.top = `${-randomPos + 530}px`;
      if (topWall.classList.value === &quot;top-wall anim&quot;) {
        topWall.classList.add(&quot;check&quot;);
      }
    });
  });</code></pre>
<p>위에 달린 장애물을 기준으로 애니메이션이 끝나면 랜덤한 위치로 바꿔준다. 그 뒤, 매칭되는 밑 장애물의 위치를 특정 길이만큼 움직인다. 아래 check는 점수를 위한 클래스이다.</p>
<h4 id="게임-오버와-점수-획득-구현">게임 오버와 점수 획득 구현</h4>
<blockquote>
<p>요소의 위치값을 얻기 위해 <strong>offset속성</strong>을 사용할 수 있다. 장애물에는 check라는 클래스를 줘서 점수 획득 후, check를 remove로 없애주어 중복 점수를 방지했다. 그리고 <strong>애니메이션이 끝나면 다시 check속성을 add시켜준다. (위 코드 참고)</strong></p>
</blockquote>
<pre><code class="language-js"> topWalls.forEach((topWall) =&gt; {
        if (
          chiken.offsetLeft + chiken.offsetWidth &gt;
          topWall.offsetLeft + topWall.offsetWidth
        ) {
          if (topWall.classList.contains(&quot;check&quot;)) {
            Score++;
            scoreBoard.innerText = `Score : ${Score}`;
            topWall.classList.remove(&quot;check&quot;);
          }
        }
      });
    }</code></pre>
<p>   캐릭터의 right값과 장애물의 right값을 구해서, 캐릭터의 right값이 더 커지면(즉 캐릭터가 장애물을 지나갔다면) Score를 증가 시킨다.<img src="https://velog.velcdn.com/cloudflare/minchan_0/1a0d480c-ad8d-4a2c-b17f-97d56b188119/image.png" alt=""><img src="https://velog.velcdn.com/cloudflare/minchan_0/69f8104e-71ee-48e2-84e3-d7b98f1af71d/image.png" alt=""></p>
<h4 id="게임-오버">게임 오버</h4>
<blockquote>
<p>offset속성으로 각각의 위치를 체크할 수 있으니 장애물과 닿았을 때 게임오버가 되도록 구현해줄 수 있다.</p>
</blockquote>
<pre><code class="language-js">ex) 캐릭터의 top이 위 장애물의 bottom보다 작으면서 
캐릭터의 right가 위 장애물의 left보다 크다면 충돌했음을 알 수 있다.</code></pre>
<pre><code class="language-js">topWalls.forEach((topwall) =&gt; {
        if (
          chiken.offsetLeft &lt;= topwall.offsetLeft + topwall.offsetWidth &amp;&amp;
          chiken.offsetLeft + chiken.offsetWidth &gt;= topwall.offsetLeft &amp;&amp;
          chiken.offsetTop &lt;= topwall.offsetTop + topwall.offsetHeight &amp;&amp;
          chiken.offsetTop + chiken.offsetHeight &gt;= topwall.offsetTop
        ) {
          gameOver();
        }
      });</code></pre>
<p>추락하거나, 화면 밖으로 나가게 되면 게임오버</p>
<pre><code class="language-js">    if (chiken.offsetTop + chiken.offsetHeight &gt; 600 || chiken.offsetTop &lt; 0) {
      gameOver();
    }</code></pre>
<h4 id="게임결과">게임결과</h4>
<blockquote>
<p>게임 종료 시 점수와 재시작을 할 수 있는 팝업을 만들어 게임 로직을 완성</p>
</blockquote>
<pre><code class="language-html">  &lt;body&gt;
    &lt;div class=&quot;background&quot;&gt;
      &lt;div class=&quot;chiken&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;down-wall anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;down-wall anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;down-wall anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;top-wall check anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;top-wall check anim&quot;&gt;&lt;/div&gt;
      &lt;div class=&quot;top-wall check anim&quot;&gt;&lt;/div&gt;
      &lt;span class=&quot;score&quot;&gt;Score : &lt;/span&gt;
      &lt;div class=&quot;start&quot;&gt;Touch!&lt;/div&gt;
      &lt;div class=&quot;dimd&quot;&gt;
        &lt;div class=&quot;text-game-over&quot;&gt;
          &lt;p&gt;게임종료!&lt;/p&gt;
        &lt;/div&gt;
        &lt;div class=&quot;score-board&quot;&gt;
          &lt;p class=&quot;best-score&quot;&gt;&lt;/p&gt;
          &lt;p class=&quot;text-score&quot;&gt;점수 :&lt;/p&gt;
          &lt;div class=&quot;wrap-btn&quot;&gt;
            &lt;button type=&quot;button&quot; class=&quot;btn-restart&quot;&gt;재시작&lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/body&gt;</code></pre>
<p>게임 종료 시 최고 점수 갱신</p>
<pre><code class="language-js">  if (bestScore &lt; Score) {
      bestScore = Score;
    }</code></pre>
<p><code>animationPlayState</code>로 애니메이션 멈춤</p>
<pre><code class="language-js">topWalls.forEach((topwall) =&gt; {
      topwall.style.animationPlayState = &quot;paused&quot;;
    });
downWalls.forEach((downwall) =&gt; {
      downwall.style.animationPlayState = &quot;paused&quot;;
    });</code></pre>
<p>종료 팝업켜기</p>
<pre><code class="language-js">document.querySelector(&quot;.dimd&quot;).classList.add(&quot;on&quot;);
document.querySelector(&quot;.text-score&quot;).innerText = `SCORE : ${Score}`;
document.querySelector(&quot;.best-score&quot;).innerText = `BEST : ${bestScore}`;</code></pre>
<p>그 외 종료 팝업에서 sns 공유를 추가하고 프로젝트를 마무리했다. 다 만들고나서 과정을 쓰려니 쓰기 애매한 것들도 많고 그 때 어떻게 했는지 기억이 안나서 고생했다..</p>
<p>전체적인 코드를 적지는 않았지만 만약 플러피버드를 만들고 싶다면 참고할 부분이 있을 것 같다.</p>
<p>간단한 게임이지만 신경쓸게 많고 생각보다 쉽지 않았다. 일단은 프로젝트를 종료했지만 추가적으로 랭크를 구현하는 것을 최종 목표로 생각 중이다.</p>
<p><img src="https://velog.velcdn.com/cloudflare/minchan_0/97d54032-558e-447b-a03e-be65104405b1/ezgif.com-gif-maker.gif" alt=""></p>
<p>게임하러 가기
<a href="https://flappychiken.netlify.app/">https://flappychiken.netlify.app/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 12 - 리액트로 생각하기]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-12-%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A1%9C-%EC%83%9D%EA%B0%81%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-12-%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A1%9C-%EC%83%9D%EA%B0%81%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 10 Mar 2022 15:23:07 GMT</pubDate>
            <description><![CDATA[<p>오늘 알아볼 것</p>
<ul>
<li>리액트로 어떻게 생각하는지 알아보자</li>
</ul>
<ol>
<li><p>구조 나누기</p>
<ul>
<li>단일책임원칙(하나의 컴포넌트는 한가지 일을 한다)에 따라 컴포넌트를 나눈다.</li>
</ul>
</li>
<li><p>state를 사용하지 않고, 정적인 페이지를 만든다.</p>
<ul>
<li>페이지를 만들다보면 재사용성이 필요한 부분이 눈에 보인다!(헤더 등)</li>
<li>보통 하향식으로 상위 컴포넌트부터 작성하는 방식을 사용한다.</li>
<li>그러나 프로젝트가 커지면 상향식으로 만드는게 개발하기 더 쉽다.</li>
</ul>
</li>
<li><p>state로 관리할 데이터를 찾아본다.
<img src="https://images.velog.io/images/minchan_0/post/5ad18fa4-aedb-4252-baf7-a709aab77f6d/image.png" alt="">위의 질문이 맞으면 state가 아니다.</p>
</li>
<li><p>state가 어디에 있어야 할지찾는다.</p>
<ul>
<li>보통 상위에 있는 컴포넌트에서 state를 관리해야 한다.
<img src="https://images.velog.io/images/minchan_0/post/2f66560d-5915-4708-b45e-3b7defbef1c6/image.png" alt=""></li>
</ul>
</li>
</ol>
<ol start="5">
<li>역방향 데이터 흐름으로 데이터를 관리한다.(onChange로 input 데이터를 관리 등)</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 11 - 합성]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-11-%ED%95%A9%EC%84%B1</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-11-%ED%95%A9%EC%84%B1</guid>
            <pubDate>Thu, 10 Mar 2022 15:17:23 GMT</pubDate>
            <description><![CDATA[<p>오늘 알아볼 것</p>
<ul>
<li>합성</li>
</ul>
<p>리액트는 합성 모델을 가지고 있어 상속대신 합성을 사용하여 컴포넌트 간 코드를 재 사용할 수 있다.
컴포넌트는 <strong>자신의 출력에 다른 컴포넌트를 참조</strong> 할 수 있다.</p>
<p>예시</p>
<pre><code class="language-js">const MyHeader = ({headText, leftChild, rightChild})=&gt;{
  return(
    &lt;header&gt;
      &lt;div className=&quot;head_btn_left&quot;&gt;{leftChild}&lt;/div&gt;
      &lt;div className=&quot;head_text&quot;&gt;{headText}&lt;/div&gt;
      &lt;div className=&quot;head_btn_right&quot;&gt;{rightChild}&lt;/div&gt;
    &lt;/header&gt;
  )
}

&lt;div className=&quot;DiaryPage&quot;&gt;
        &lt;MyHeader 
          headText={`${getStringDate(new Date(data.date))} 기록`}
          leftChild={&lt;MyButton text={&quot;&lt; 뒤로가기&quot;} onClick={()=&gt;navigate(-1)}/&gt;}
          rightChild={&lt;MyButton text={&quot;수정하기&quot;} onClick={()=&gt;navigate(`/edit/${data.id}`)}/&gt;}
        /&gt;</code></pre>
<p><img src="https://images.velog.io/images/minchan_0/post/d3b3a171-a9b3-4547-8f6d-7463485ec348/image.png" alt=""></p>
<pre><code class="language-js">const headText= `${curDate.getFullYear()}년 ${curDate.getMonth()+1}월`

&lt;div&gt;
      &lt;MyHeader headText={headText}
                leftChild={&lt;MyButton text={&quot;&lt;&quot;} onClick={decreaseMonth}/&gt;}
                rightChild={&lt;MyButton text={&quot;&gt;&quot;} onClick={increaseMonth}/&gt;}
      /&gt;</code></pre>
<p><img src="https://images.velog.io/images/minchan_0/post/97761fde-335e-405a-b885-2374fde0db23/image.png" alt=""></p>
<p>예시 출처는 유데미 강의 <a href="https://www.udemy.com/course/winterlood-react-basic/">한입 크기로 잘라 먹는 리액트</a> 중 일부</p>
<p>상속
쓰지말라고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 10 - State 끌어올리기]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-10-State-%EB%81%8C%EC%96%B4%EC%98%AC%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-10-State-%EB%81%8C%EC%96%B4%EC%98%AC%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Thu, 10 Mar 2022 15:12:51 GMT</pubDate>
            <description><![CDATA[<p>요지는 동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영할때, state를 끌어올리라는 말인데. .</p>
<p>이해한 바로는, props로 하위 컴포넌트에 상위컴포넌트의 상태를 변경시키는 함수를 전달하고, 하위 컴포넌트에서 이를 실행시키라는 말 같다.</p>
<p>아래는 프로젝트 하면서 구현한 비밀번호 입력 모달창에 대한 코드이다. 하위 컴포넌트에서 props로 받은 함수를 실행시키며, 상위 컴포넌트의 상태를 변경한다.</p>
<p>ex)</p>
<p><strong>상위 컴포넌트</strong></p>
<pre><code class="language-js">export default function Channel({ text, index, channelId, pw }) {
    const [openPwForm, setOpenPwForm] = useState(false);

    const handleClick = () =&gt; {
        setOpenPwForm(!openPwForm);
    };

return (
    &lt;&gt;
        {openPwForm &amp;&amp; (
            &lt;CheckPw passWord={pw} 
                       index={index} 
                     channelId={channelId} 
                     title={text} 
                     handleClick={handleClick} /&gt;
        )}
            &lt;li onClick={handleClick} onKeyDown={handleClick} aria-hidden=&quot;true&quot;&gt;
                {text}
            &lt;/li&gt;
    &lt;/&gt;
);
}</code></pre>
<p><strong>하위 컴포넌트</strong></p>
<pre><code class="language-js">export default function CheckPw({ passWord, index, channelId, title, handleClick }) {
..
..
..
return (
    &lt;Wrap&gt;
        &lt;CheckPassWard&gt;
            &lt;p&gt;비밀번호를 입력하세요 👀&lt;/p&gt;
            &lt;input type=&quot;text&quot; onChange={onChange} value={inputText} /&gt;
            &lt;Message check={wrongPw}&gt;비밀번호가 틀렸습니다.&lt;/Message&gt;
            &lt;EnterButton type=&quot;submit&quot; onClick={checkPw}&gt;
                입력
            &lt;/EnterButton&gt;
        &lt;/CheckPassWard&gt;
        &lt;Dimmed onClick={handleClick}&gt;dimmed&lt;/Dimmed&gt;
&lt;/Wrap&gt;
);
}</code></pre>
<p><img src="https://images.velog.io/images/minchan_0/post/635409b8-857c-4d6e-87e2-aa81c7350caf/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 9 - 폼]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-9-%ED%8F%BC</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-9-%ED%8F%BC</guid>
            <pubDate>Thu, 10 Mar 2022 15:08:21 GMT</pubDate>
            <description><![CDATA[<p>오늘 알아볼 것</p>
<ul>
<li>폼</li>
</ul>
<p>form은 사용자로부터 데이터를 입력받기 위해 필요한 기능이다. 사용자가 form에 입력한 데이터에 접근하도록 하는 것을 제어 컴포넌트라 한다.</p>
<p><strong>제어 컴포넌트</strong>
input, textarea, select와 같은 폼 엘리먼트는 일반적으로 state로 관리하고 업데이트 한다.
React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어한다.</p>
<p>ex)</p>
<pre><code class="language-js">const FormEditor = () =&gt;{

    const[name, setName]= useState(&quot;&quot;); 
    const[content, setContent]= useState(&quot;&quot;); 
    const [alpa, setAlpa] = useState(&quot;A&quot;);


    const handleSubmit = (e) =&gt;{
        alert(name + &quot;이 말했다. &quot; + content + alpa + &quot;라고..&quot;);
        e.preventDefault();
    }
    return (
        &lt;&gt;
            &lt;form onSubmit={handleSubmit}&gt;
                &lt;label&gt;이름 :
                    &lt;input type=&quot;text&quot; value={name} onChange={(e)=&gt;setName(e.target.value)}/&gt;
                &lt;/label&gt;
            &lt;br/&gt;
                &lt;textarea value={content} onChange={(e)=&gt;setContent(e.target.value)}/&gt;
            &lt;br/&gt;
                &lt;select value={alpa} onChange={(e) =&gt; {setAlpa(e.target.value);}} &gt;
                    &lt;option value=&quot;A&quot;&gt;A&lt;/option&gt;
                    &lt;option value=&quot;B&quot;&gt;B&lt;/option&gt;
                    &lt;option value=&quot;C&quot;&gt;C&lt;/option&gt;
                &lt;/select&gt;

                &lt;button&gt;입력&lt;/button&gt;
            &lt;/form&gt;
    &lt;/&gt;
    );

}</code></pre>
<p><strong>다중 입력 제어하기</strong>
각 엘리먼트에 name 어트리뷰트를 추가하고, 이를 통해 어떤 작업을 할지 선택할 수 있게 한다.</p>
<pre><code class="language-js">const [state, setState] = useState({
    name:&quot;&quot;,
    content:&quot;&quot;,
    alpa:&quot;A&quot;,
})


const handleSubmit = (e) =&gt;{
    alert(name + &quot;이 말했다. &quot; + content + alpa + &quot;라고..&quot;);
    e.preventDefault();
}

const handleChangeState = (e)=&gt;{
    setState({
        …state,
        [e.target.name]: e.target.value,
    });
}

return (
    &lt;&gt;
        &lt;form onSubmit={handleSubmit}&gt;
            &lt;label&gt;이름 :
                &lt;input name=&quot;name&quot; type=&quot;text&quot; value={state.name} onChange={handleChangeState}/&gt;
            &lt;/label&gt;
        &lt;br/&gt;
            &lt;textarea name=&quot;content&quot;  value={state.content} onChange={handleChangeState}/&gt;
        &lt;br/&gt;
            &lt;select name=&quot;alpa&quot; value={state.alpa} onChange={handleChangeState} &gt;
                &lt;option value=&quot;A&quot;&gt;A&lt;/option&gt;
                &lt;option value=&quot;B&quot;&gt;B&lt;/option&gt;
                &lt;option value=&quot;C&quot;&gt;C&lt;/option&gt;
            &lt;/select&gt;

            &lt;button&gt;입력&lt;/button&gt;
        &lt;/form&gt;
&lt;/&gt;
);</code></pre>
<p><strong>제어 컴포넌트와 비제어 컴포넌트</strong></p>
<p>제어 컴포넌트는 사용자가 입력을 할때마다 렌더링하지만 비제어 컴포넌트는 사용자가 제출 등의 액션을 하기 전에는 리렌더링이나 동기화를 시키지 않는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 8 - 리스트와 Key]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-8-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%99%80-Key</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-8-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%99%80-Key</guid>
            <pubDate>Thu, 10 Mar 2022 15:03:56 GMT</pubDate>
            <description><![CDATA[<p>오늘 알아볼 것</p>
<ul>
<li>여러개의 컴포넌트를 렌더링
<img src="https://images.velog.io/images/minchan_0/post/c8f9f055-6e97-464e-84e3-81bcb53869aa/image.png" alt="">*이런 모양은 바람직하지 못하다.</li>
</ul>
<p>이런 경우 배열을 만들어 map을 통해 중복코드를 줄일 수 있다.</p>
<pre><code class="language-js">const arr = Array(30).fill(1).map( ( v, i ) =&gt; v + i );
// 1~30까지의 숫자가 담긴 배열 생성
&lt;WarpUL&gt;
    { arr.map(( data ) =&gt; (
        &lt;RoomList key = {`room_${data}`} id ={data} text={`${data}룸`} /&gt;
    )}
&lt;/WarpUL&gt;</code></pre>
<p>데이터를 받아서 생성하는 경우</p>
<pre><code class="language-js">//데이터 예시
const data = [
    { 
        id: &quot;12312dsfjoewifjweidss&quot;,
        name:&quot;홍길동&quot;,
        age: 18,
        목표: &quot;~&quot;,
    },
    {
        id: &quot;123jsfioj123oi9df&quot;,
        name:&quot;아무개&quot;,
        age:20,
        목표:&quot;~~&quot;,
    }
]

&lt;WarpUL&gt;
    { data.map(( {id, name, age} ) =&gt; (
        &lt;List key = {`person_${id}`} name ={name} age={`${data.age}살`} /&gt;
    )}
&lt;/WarpUL&gt;</code></pre>
<blockquote>
<p><strong>Key</strong></p>
</blockquote>
<ul>
<li>어떤 항목을 변경/추가/삭제할지 식별하는 것을 돕는다.</li>
<li>엘리먼트에 안정적인 고유성을 부여하기 위해 사용한다.</li>
<li>배열 내부의 엘리먼트에 지정해야 한다.</li>
</ul>
<p><strong>Key가 왜 필요할까?</strong></p>
<pre><code>&lt;ul&gt;
    &lt;li&gt;1번&lt;/li&gt;
    &lt;li&gt;2번&lt;/li&gt;
&lt;/ul&gt;

--------3번을 추가했다------------

&lt;ul&gt;
    &lt;li&gt;1번&lt;/li&gt;
    &lt;li&gt;2번&lt;/li&gt;
    &lt;li&gt;3번&lt;/li&gt;
&lt;/ul&gt;</code></pre><p>이 경우 리액트는 1,2,3이 다 달라졌다고 판단해서 전부 다시 렌더링하게 된다.
그러나 key값이 있는 경우에는 3번 리스트만 추가하는 방식으로 일부분만 변경한다.</p>
<p>보통 데이터에 고유의 ID문자열을 사용하지만, ID가 없는 경우 최후의 수단으로 index를 사용할수도 있다. 그렇지만 위의 문제가 비슷하게 발생하기 때문에 사용을 지양해야 한다.</p>
<p>nanoid를 사용하여 ID가 없는 경우에 대비할 수 있도록 하는게 좋겠다.
<a href="https://github.com/ai/nanoid/">https://github.com/ai/nanoid/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 7 - 조건부 렌더링]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-7-%EC%A1%B0%EA%B1%B4%EB%B6%80-%EB%A0%8C%EB%8D%94%EB%A7%81</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-7-%EC%A1%B0%EA%B1%B4%EB%B6%80-%EB%A0%8C%EB%8D%94%EB%A7%81</guid>
            <pubDate>Thu, 10 Mar 2022 14:53:59 GMT</pubDate>
            <description><![CDATA[<p>오늘 알아볼 것</p>
<ul>
<li>조건부 렌더링</li>
</ul>
<p>조건부 렌더링을 통해서 원하는 컴포넌트만 렌더링하여 재사용성을 높힐 수 있다.</p>
<p><img src="https://images.velog.io/images/minchan_0/post/57a26e81-2690-46b4-b080-1eea1703707b/%EC%BA%A1%EC%B2%98.PNG" alt="">
<code>하나의 모달 컴포넌트에서 조건에 따라 다른 컨텐츠 내용을 출력</code></p>
<h4 id="삼항연산자를-이용한-렌더링">삼항연산자를 이용한 렌더링</h4>
<pre><code class="language-js">return (
    &lt;&gt;
      {!마지막라운드인지 ? (
        &lt;&gt;
          &lt;DIV&gt;
            &lt;p&gt;{버튼을누른거 ? &quot;중간 결과&quot; : &quot;라운드 결과&quot;}&lt;/p&gt;
            {버튼을누른거 ? (
              &lt;중간결과 컴포넌트 /&gt;
            ) : (
              &lt;라운드결과 컴포넌트&gt;
                {~~내용~~}
              &lt;/라운드결과 컴포넌트&gt;
            )}
          &lt;/DIV&gt;
          &lt;Content&gt; hi~ &lt;/Content&gt;
        &lt;/&gt;
      ) : (
        &lt;DIV&gt;
          &lt;p&gt;최종결과&lt;/p&gt;
          &lt;최종결과 컴포넌트 /&gt;
        &lt;/DIV&gt;
      )}
      &lt;Content&gt;{마지막라운드인지 ? &quot;게임이 종료 되었습니다!&quot; : &quot;&quot;}&lt;/Content&gt;
    &lt;/&gt;
  );</code></pre>
<h4 id="스타일에-조건으로-렌더링하기">스타일에 조건으로 렌더링하기</h4>
<pre><code class="language-js">&lt;span style = {{display : 조건 ? &quot;display&quot;:&quot;none&quot; }}&gt; Content &lt;/span&gt;</code></pre>
<h4 id="중괄호-안에-표현식을-사용하여-논리연산자를-통해-렌더링">중괄호 안에 표현식을 사용하여, 논리연산자를 통해 렌더링</h4>
<pre><code class="language-js"> if (!조건) {
    return null;
  }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 6 - 이벤트 처리하기]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-6-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-6-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 10 Mar 2022 13:50:31 GMT</pubDate>
            <description><![CDATA[<p>오늘 알아볼 것</p>
<ul>
<li>이벤트</li>
</ul>
<p><strong>리액트에서 이벤트를 처리하는 방식</strong></p>
<ul>
<li><p>리액트의 이벤트는 소문자 대신 카멜케이스를 사용한다.</p>
</li>
<li><p>JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달한다.</p>
<pre><code class="language-js">&lt;button onClick={activateLasers}&gt;
Activate Lasers
&lt;/button&gt;</code></pre>
</li>
<li><p>flase를 반환해도 기본 동작을 방지 할 수 없다.(preventDefalut를 명시적으로 호출해야 함)</p>
<pre><code class="language-js">function Form() {
function handleSubmit(e) {
  e.preventDefault();
  console.log(&#39;You clicked submit.&#39;);
}

return (
  &lt;form onSubmit={handleSubmit}&gt;
    &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
  &lt;/form&gt;
);
}</code></pre>
</li>
<li><p>addEventListener를 호출할 필요가 없고, 엘리먼트가 렌더링될때 리스너를 제공한다.</p>
<pre><code class="language-js">render() {
  return (
    &lt;button onClick={this.handleClick}&gt;
      {this.state.isToggleOn ? &#39;ON&#39; : &#39;OFF&#39;}
    &lt;/button&gt;
  );
}</code></pre>
</li>
</ul>
<h4 id="jsx-콜백-안에서-this">JSX 콜백 안에서 this</h4>
<p>javaScript에서 클래스 메서드는 기본적으로 <span style="background-color:#fff5b1">바인딩</span>되어 있지 않다. <code>this.handleClick</code>을 바인딩 하지 않고 <code>onClick</code>에 전달하였다면, 함수가 실제 호출될 때 this는 undefined가 된다.</p>
<blockquote>
<p><strong>바인딩</strong>
프로그램에 사용된 구성요소의 실제 값 또는 프로퍼티를 결정짓는 행위
어떤 기본 단위가 가질 수 있는 구성요소의 구체적인 값, 성격을 확정짓는 것</p>
</blockquote>
<p>일반적으로 onClick={this.handleClick}과 같이 뒤에 ()를 사용하지 않고 메서드를 참조할 경우, 해당 메서드를 바인딩 해야 한다.</p>
<pre><code class="language-js">class LoggingButton extends React.Component {
  // 이 문법은 `this`가 handleClick 내에서 바인딩되도록 합니다.
  // 주의: 이 문법은 *실험적인* 문법입니다.
  handleClick = () =&gt; {
    console.log(&#39;this is:&#39;, this);
  }

  render() {
    return (
      &lt;button onClick={this.handleClick}&gt;
        Click me
      &lt;/button&gt;
    );
  }
}</code></pre>
<p>OR 콜백에 화살표 함수를 사용하는 방법도 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 5 - State와 생명주기]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-5-State%EC%99%80-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-5-State%EC%99%80-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Tue, 08 Mar 2022 07:24:51 GMT</pubDate>
            <description><![CDATA[<p>오늘 알아볼 것</p>
<ul>
<li>State</li>
<li>생명주기</li>
</ul>
<h3 id="state">State</h3>
<p>Prop와 유사하지만 비공개이며, 컴포넌트에 의해 완전히 제어된다.</p>
<h4 id="함수에서-클래스로-변환">함수에서 클래스로 변환</h4>
<pre><code class="language-js">function Clock(props) {
  return (
    &lt;div&gt;
      &lt;h1&gt;Hello, world!&lt;/h1&gt;
      &lt;h2&gt;It is {props.date.toLocaleTimeString()}.&lt;/h2&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>클래스로 변환시키면,</p>
<pre><code class="language-js">class Clock extends React.Component {
  render() {
    return (
      &lt;div&gt;
        &lt;h1&gt;Hello, world!&lt;/h1&gt;
        &lt;h2&gt;It is {this.props.date.toLocaleTimeString()}.&lt;/h2&gt;
      &lt;/div&gt;
    );
  }
}</code></pre>
<p>여기서 render 메서드는 업데이트가 발생할 때마다 호출되지만, 같은 DOM 노드로 Clock 컴포넌트를 렌더링하는 경우 Clock 클래스의 단일 인스턴스만 사용된다.</p>
<h4 id="클래스에-로컬-state-추가하기">클래스에 로컬 State 추가하기</h4>
<pre><code class="language-js">class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      &lt;div&gt;
        &lt;h1&gt;Hello, world!&lt;/h1&gt;
        &lt;h2&gt;It is {this.state.date.toLocaleTimeString()}.&lt;/h2&gt;
      &lt;/div&gt;
    );
  }
}

ReactDOM.render(
  &lt;Clock /&gt;,
  document.getElementById(&#39;root&#39;)
);</code></pre>
<h4 id="생명주기-메서드를-클래스에-추가하기">생명주기 메서드를 클래스에 추가하기</h4>
<blockquote>
<p><strong>마운팅</strong>
컴포넌트가 처음 DOM에 렌더링 될 때마다 타이머를 설정한다.</p>
</blockquote>
<blockquote>
<p><strong>언마운팅</strong>
컴포넌트에 의해 생성된 DOM이 삭제될 때마다 타이머를 해제한다.</p>
</blockquote>
<pre><code class="language-js">class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
  }

  componentWillUnmount() {
  }

  render() {
    return (
      &lt;div&gt;
        &lt;h1&gt;Hello, world!&lt;/h1&gt;
        &lt;h2&gt;It is {this.state.date.toLocaleTimeString()}.&lt;/h2&gt;
      &lt;/div&gt;
    );
  }
}</code></pre>
<p>componentDidMount()는 컴포넌트 출력물이 DOM에 렌더링 된 후 실행된다.
여기서 타이머를 설정하기 좋다.</p>
<pre><code class="language-js">  componentDidMount() {
    this.timerID = setInterval(
      () =&gt; this.tick(),
      1000
    );
  }</code></pre>
<p>componentWillUnmount()에서는 타이머를 분해한다.</p>
<pre><code class="language-js"> componentWillUnmount() {
    clearInterval(this.timerID);
  }</code></pre>
<p>setState를 사용하여 date를 업데이트 하는 메서드를 구현한다.</p>
<pre><code class="language-js">  tick() {
    this.setState({
      date: new Date()
    });
  }</code></pre>
<p>생명주기에 대한 자세한 글은 <a href="https://react.vlpt.us/basic/25-lifecycle.html">여기로</a></p>
<h4 id="state의-올바른-사용">State의 올바른 사용</h4>
<ul>
<li><p>State를 직접 수정하지 말 것.
  대신 setState를 사용하자.</p>
</li>
<li><p>State 업데이트는 비동적일 수 있다.
  React는 성능을 위해 setState 호출을 한꺼번에 처리할 수 있다.
  this.state가 비동기적으로 업데이트 될 수 있기 때문에 다음 state를 계산할 때
  해당 값에 의존해서는 안된다.</p>
</li>
<li><p>State 업데이트는 병합된다.
  setState를 호출할 때 React는 제공한 객체를 현재 state로 병합합니다.</p>
</li>
</ul>
<h4 id="데이터는-아래로-흐른다">데이터는 아래로 흐른다.</h4>
<p>컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달 할 수 있다.
일반적으로 이를 하향식, 단방향식 데이터 흐름이라고 한다.</p>
<p>모든 state는 항상 특정한 컴포넌트가 소유하고 있다.
그 state로 부터 파생된 UI또는 데이터는 오직 트리구조에서 자신의 아래에 있는 컴포넌트에만 영향을 미친다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 4 - Component와 Props]]></title>
            <link>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-4-Component%EC%99%80-Props</link>
            <guid>https://velog.io/@minchan_0/%EB%A6%AC%EC%95%A1%ED%8A%B8-4-Component%EC%99%80-Props</guid>
            <pubDate>Mon, 07 Mar 2022 07:26:07 GMT</pubDate>
            <description><![CDATA[<p>오늘 알아볼 것</p>
<ul>
<li>Component</li>
<li>Props</li>
</ul>
<blockquote>
<p><strong>Component</strong>
재사용이 가능한 각각의 독립된 모듈을 뜻한다.</p>
</blockquote>
<blockquote>
<p><strong>Props</strong>
property의 약자로 부모에게 받아온 데이터이다. 사전적으로 성질, 속성의 의미를 가지고 있다.</p>
</blockquote>
<h3 id="함수-컴포넌트와-클래스-컴포넌트">함수 컴포넌트와 클래스 컴포넌트</h3>
<ul>
<li>컴포넌트를 정의하는 가장 간단한 방법은 JavaScript 함수를 작성하는 것이다.<pre><code class="language-js">function Welcome(props) {
return &lt;h1&gt;Hello, {props.name}&lt;/h1&gt;;
}</code></pre>
함수 컴포넌트</li>
</ul>
<pre><code class="language-js">class Welcome extends React.Component {
  render() {
    return &lt;h1&gt;Hello, {this.props.name}&lt;/h1&gt;;
  }
}</code></pre>
<p>클래스 컴포넌트</p>
<p>React가 볼 때 위 두가지 유형의 컴포넌트는 동일하다.</p>
<h3 id="컴포넌트-렌더링">컴포넌트 렌더링</h3>
<p>React 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있다.</p>
<pre><code class="language-js">function Welcome(props) {
  return &lt;h1&gt;Hello, {props.name}&lt;/h1&gt;;
}

const element = &lt;Welcome name=&quot;Sara&quot; /&gt;;
ReactDOM.render(
element,
document.getElementById(&#39;root&#39;));</code></pre>
<blockquote>
<p><strong>컴포넌트의 이름은 항상 대문자로 시작한다.</strong>
Element가 소문자로 시작하는 경우에는 div태그와 같이 내장 컴포넌트라는 것을 뜻하기 때문에 컴포넌트는 대문자로 사용한다.</p>
</blockquote>
<h3 id="컴포넌트-합성">컴포넌트 합성</h3>
<p>컴포넌트는 자신의 출력에 <strong>다른 컴포넌트를 참조</strong>할 수 있다.
이는 모든 세부 단계에서 동일한 추상 컴포넌트를 사용할 수 있다는 것을 의미한다.</p>
<pre><code class="language-js">function Welcome(props) {
  return &lt;h1&gt;Hello, {props.name}&lt;/h1&gt;;
}

function App() {
  return (
    &lt;div&gt;
      &lt;Welcome name=&quot;Sara&quot; /&gt;
      &lt;Welcome name=&quot;Cahal&quot; /&gt;
      &lt;Welcome name=&quot;Edite&quot; /&gt;
    &lt;/div&gt;
  );
}

ReactDOM.render(
  &lt;App /&gt;,
  document.getElementById(&#39;root&#39;)
);</code></pre>
<h3 id="컴포넌트-추출">컴포넌트 추출</h3>
<p>컴포넌트를 작게 나눠서 가독성과 재사용성을 높힐 수 있다.</p>
<pre><code class="language-js">function Comment(props) {
  return (
    &lt;div className=&quot;Comment&quot;&gt;
      &lt;div className=&quot;UserInfo&quot;&gt;
        &lt;img className=&quot;Avatar&quot;
          src={props.author.avatarUrl}
          alt={props.author.name}
        /&gt;
        &lt;div className=&quot;UserInfo-name&quot;&gt;
          {props.author.name}
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div className=&quot;Comment-text&quot;&gt;
        {props.text}
      &lt;/div&gt;
      &lt;div className=&quot;Comment-date&quot;&gt;
        {formatDate(props.date)}
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>Component를 잘게 쪼개서 작성한다면,</p>
<pre><code class="language-js">function Avatar(props) {
  return (
    &lt;img className=&quot;Avatar&quot;
      src={props.user.avatarUrl}
      alt={props.user.name}
    /&gt;
  );
}

function UserInfo(props) {
  return (
    &lt;div className=&quot;UserInfo&quot;&gt;
      &lt;Avatar user={props.user} /&gt;
      &lt;div className=&quot;UserInfo-name&quot;&gt;
        {props.user.name}
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

function Comment(props) {
  return (
    &lt;div className=&quot;Comment&quot;&gt;
      &lt;UserInfo user={props.author} /&gt;
      &lt;div className=&quot;Comment-text&quot;&gt;
        {props.text}
      &lt;/div&gt;
      &lt;div className=&quot;Comment-date&quot;&gt;
        {formatDate(props.date)}
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h3 id="props는-읽기-전용">Props는 읽기 전용</h3>
<p>컴포넌트의 자체 props를 수정해서는 안된다.</p>
<p>**모든 React컴포넌트는 자신의 props를 다룰 때 반드시 <span style="background-color:#fff5b1">순수 함수</span>처럼 동작해야 한다.</p>
<blockquote>
<p><strong>순수함수</strong>
어떤 함수에 동일한 인자를 주었을 때 항상 같은 값을 리턴하는 함수</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>