<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>www.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 08 Aug 2021 14:09:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>www.log</title>
            <url>https://images.velog.io/images/devpark_0x1c/profile/00b79d95-b57f-4c1d-aeda-de519287cd13/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. www.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/devpark_0x1c" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[위코드 3개월 시작부터 끝까지]]></title>
            <link>https://velog.io/@devpark_0x1c/%EC%9C%84%EC%BD%94%EB%93%9C-3%EA%B0%9C%EC%9B%94-%EC%8B%9C%EC%9E%91%EB%B6%80%ED%84%B0-%EB%81%9D%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@devpark_0x1c/%EC%9C%84%EC%BD%94%EB%93%9C-3%EA%B0%9C%EC%9B%94-%EC%8B%9C%EC%9E%91%EB%B6%80%ED%84%B0-%EB%81%9D%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Sun, 08 Aug 2021 14:09:54 GMT</pubDate>
            <description><![CDATA[<h1 id="ready">READY</h1>
<p>난 네트워크 엔지니어로 사회 생활을 시작했다. 원래는 전공을 살려서 자바 개발자가 될수 있었지만 모종의 사연으로 인해 취업을 빨리 할 수 있었던 엔지니어의 길로 들어섰다. 엔지니어 생활은 걱정했던 것보다 적성에 맞았지만 항상 같은 루틴인 같은 제품의 설치, 유지보수의 반복으로 인해 회의감이 들었고, 출퇴근 시간이나 집에 있는 시간엔 유튜브로 개발자에 대한 이야기 혹은 서적등을 보면서 내 나름대로 개발자의 길로 들어서는 걸 준비했었던 것 같다.</p>
<h3 id="난-왜-개발자가-되고-싶은가-🤔">난 왜 개발자가 되고 싶은가 🤔</h3>
<p>위의 질문에 난 어떤 대답을 해야할 지 고민이된다. 그리고 조금 뻔한 대답일 수 있지만 역시 <strong>다양한 유형의 사용자를 대상으로 서비스 할 수 있는 점</strong>이 이 길을 선택한 가장 큰 이유이다. 엔지니어 생활을 할 때는 주로 B2B를 주로 했기 때문에 사용자의 폭이 매우 좁았다. 하지만 개발 업체들을 보면 너무나도 다양한 유형의 고객 혹은 회사들을 대상으로 하는 업체들이 많기 때문에 여러가지 상황을 겪을 수 있고 다양한 유형의 서비스를 할 수 있다는 점이 매력적으로 느껴진다. 또 하나의 이유는 IT의 큰 특성 중 하나인 발전의 속도가 굉장히 빠르기 때문에 <strong>다양한 언어들이 계속 등장</strong>한다는 점도 나에겐 매력적으로 다가왔다. 새로운 것을 배우는 것은 누군가에겐 스트레스 받을 수 있는 일이지만 새로운 것을 배워보는 걸 좋아하는 나에겐 너무 매력적인 포인트라고 생각한다. 실제로 위코드에서 프로젝트를 하며 <code>netlify</code>나 <code>firebase</code>를 문서를 찾아보면서 배포도 해보고 내가 만든 웹사이트가 실제로 주소를 가졌을 때의 그 기쁨이 아직도 생생하다.</p>
<h3 id="위코드에-가기까지">위코드에 가기까지</h3>
<p>난 위코드에 21기로 들어갔지만 위코드 존재 자체는 10기쯤부터 알고 있었다. 내 계획은 15기쯤에 지원하는거였는데, 일도 바쁘고 상황도 여의치 않아서 21기에 지원하게 됐다. 위코드에 지원하기까지도 많은 고민을 했던 것 같다. 그 중 제일 큰 고민은 <code>난 정말 하나도 모르는 데, 3개월의 기간동안 내가 얼마나 성장할 수 있을까?</code>였다. 3개월이란 기간이 너무 짧다고 생각했기 때문이다. 하지만 모든 공부가 그러하듯이 내가 얼마나 노력하느냐에 따라 성장 속도가 달라질수 있다는 걸 잘 알고 있기에 사전스터디부터 열심히 하기로 마음을 먹고 본격적으로 개발 공부를 시작했다.</p>
<h1 id="set">SET</h1>
<h3 id="사전스터디">사전스터디</h3>
<p>위코드에서 제일 열심히 했던 기간 중 하나를 꼽으라면 바로 사전스터디 기간이 아닐까 싶다. 사전스터디는 아무래도 자율적으로 해야하는 시간이 너무나도 많았기 때문에 어떻게 효율적으로 시간을 활용하는가가 제일 문제였다. 게다가 나는 사전스터디 둘째 주까지 일을 해야하는 상황이어서 평일엔 퇴근 후 공부를 하고 주말은 거의 하루종일 공부를 했었다. 셋째 주부터는 같은 사전스터디 팀원 중 프로그래밍을 처음 입문하는 사람들과 따로 조를 꾸려서 <strong><a href="https://github.com/AutumnWithJay/JS-Mini-Projects">자바스크립트 프로젝트</a></strong> 를 했었는데, 따로 공부한 이 모임을 통해 굉장히 성장했다고 생각한다.</p>
<h1 id="start-🏃🏻">START 🏃🏻</h1>
<h3 id="위코드에서-배운-것">위코드에서 배운 것</h3>
<p>자바스크립트, 리액트 그리고 프로젝트를 통해 기술들을 배웠지만 가장 많이 배운건 <strong>소통</strong> 이라는 키워드이다. 
처음에 모르는 것을 멘토님에게 물어볼 때 질문하는 법에 대한 방법을 배웠는데 직접적인 소통을 하는 것도 좋지만 내가 물어볼 내용을 정리해서 슬랙으로 물어보는 것도 익혀보라고 했는데, 그 당시에는 직접 대면해서 가르켜주면 될텐데 왜 그럴까 싶었지만 요즘같은 코로나 시대에 비대면으로 일하는 경우가 많기 때문에 이런 것때문에 위에서 비대면으로 소통하는 방법을 가르쳐준 것이구나 하며 깨달았다.
그리고 프로젝트를 통한 팀원간의 소통의 중요성을 배웠다. 아무래도 서로 알던 사이들이 아니기 때문에 프로젝트를 시작했을 때 성격이 조용하신 분들은 좀 소극적인 소통을 하셨는데, 본인의 의견을 내세우는 걸 부담스러워해 손해를 보는 경우가 있었어서 그런 분들을 이끌어주는 역할을 했었다.</p>
<h1 id="finish-🏆">FINISH 🏆</h1>
<h3 id="어떤-개발자가-되고-싶은가">어떤 개발자가 되고 싶은가</h3>
<p>난 누군가에게 도움이 되는 개발자가 되고 싶다. 
위코드 1개월차 당시 사전스터디 때 조금 열심히 했던 결과인지 남들이 어려움을 겪는 순간에 내가 도움을 줄 때가 종종 있었다. 이 때 생각한 건 나처럼 처음 개발에 발을 내민 사람에게 정답은 주지 못해도 정답으로 가는 방향을 제시해 줄수 있는 사람이 되면 좋겠다고.. 그리고 같이 일하고 싶은 개발자가 되었으면 좋겠다. 항상 팀원과 소통하고 모르는 부분이 생기면 항상 그랬던것처럼 문제로부터 도망치지 않고 서적이나 검색 혹은 개발자 커뮤니티를 통해 해결하는 내가 될것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PROJECT] 위코드 2차 프로젝트 회고]]></title>
            <link>https://velog.io/@devpark_0x1c/PROJECT-%EC%9C%84%EC%BD%94%EB%93%9C-2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@devpark_0x1c/PROJECT-%EC%9C%84%EC%BD%94%EB%93%9C-2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 13 Jul 2021 05:12:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://user-images.githubusercontent.com/67620484/125184605-288a0500-e25a-11eb-9d49-0c802de12536.png" width= "500px"/><br></p>
<h1 id="다시-시작-🙆♀️">다시 시작 🙆‍♀️</h1>
<p>1차 프로젝트가 끝나고 바로 다음주부터 2차 프로젝트가 시작되었다. 1차 프로젝트가 끝나고 너무 피곤해서 금, 토요일은 나답지 않게 종일 쉬면서 뒹굴뒹굴거렸다. 직장을 다니고부터 한번도 해보지 않았던 패턴인데 내 나름대로 엄청나게 피곤했긴 했나보다.</p>
<h1 id="2차-프로젝트-구성">2차 프로젝트 구성</h1>
<p>이번 팀 구성이 발표되기 전에 이번엔 또 어떤 사람들과 함께 프로젝트를 진행할지 기대했다😏
오후에 조 발표가 나고 의외의 멤버 한명과 같이 조를 짜게되서 너무 웃겼다.😂 그분은 바로 사전스터디때 처음 만나서 절친이 된 프론트엔드 미영님.!ㅎ_ㅎ 우리가 너무 친하게 지내서 조 짤때 우린 같은 조 될일은 없겠수다~ 했는데 기대도 안한 조합이 구성되서 트러블 날일은 전혀 없겠네~ 하면서 조 발표 되자마자 열심히하자며..서로 다독여줬다
백엔드분은 사전스터디때 같은 조였던 승리님 그리고 얘기해볼 기회가 없던 송준님, 준영님과 함께 다섯명이서 한 조가 구성되었다. 1차 프로젝트때도 그렇지만 이번에도 프론트엔드 인원이 2명이어서 좀 아쉬운점은 있다. 
인원이 적으면 다양하게 해볼순 있지만 하나를 깊게 해볼수 있는 시간이 좀 부족하다는 생각이 들어서 한명이 더 있었음 어땠을까 하는 생각을 했다🧐</p>
<h2 id="drip">DRIP</h2>
<p>프로젝트를 해보고 싶은 사이트를 정할 때 내가 <strong>FRIP</strong>을 제출해서 적었는데, 이게 2차 프로젝트 대상으로 선정될 줄은 몰랐다. 컨텐츠가 생각보다 많은 사이트라서 구현할 수 있을까에 대한 의문이 있었기 때문이다. 우리의 팀명은 <strong>DRIP</strong>인데 미영님이 드립 어떠냐했을때 사람들이 별 의견없이 &quot;좋은데요?&quot;라고 해서 시간들이지 않고 팀명을 금방 정할수 있었다. 역시 드립 잘치는 사람은 팀명도 빨리 짓나보다😏</p>
<h2 id="프로젝트-계획-및-기간">프로젝트 계획 및 기간</h2>
<p>📆 2021.06.21 ~ 07.02</p>
<p>1st Sprint : 개발환경 초기세팅, 전체 레이아웃, 컴포넌트화
2nd Sprint : 컴포넌트 별 기능 구현, 프론트-백 통신, 코드 리팩토링, conflict 수정 작업</p>
<h2 id="사용한-기술">사용한 기술</h2>
<h3 id="front-end">Front-End</h3>
<span>
<img src="https://img.shields.io/badge/HTML5-E34F26?style=for-the-badge&logo=html5&logoColor=white"/><img src="https://img.shields.io/badge/CSS3-1572B6?style=for-the-badge&logo=css3&logoColor=white"/><img src="https://img.shields.io/badge/JavaScript-323330?style=for-the-badge&logo=javascript&logoColor=F7DF1E"/><img src="https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB"/><img src="https://img.shields.io/badge/React_Router-CA4245?style=for-the-badge&logo=react-router&logoColor=white"/><img src="https://img.shields.io/badge/styled--components-DB7093?style=for-the-badge&logo=styled-components&logoColor=white"/>
</span>

<h3 id="back-end">Back-End</h3>
<span>
<img src="https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white"/><img src="https://img.shields.io/badge/Django-092E20?style=for-the-badge&logo=django&logoColor=white"/> <img src="https://img.shields.io/badge/MySQL-00000F?style=for-the-badge&logo=mysql&logoColor=white"/><img src="https://img.shields.io/badge/Amazon_AWS-232F3E?style=for-the-badge&logo=amazon-aws&logoColor=white"/>
</span>  
<br>

<h2 id="drip-유튜브-링크">Drip 유튜브 링크</h2>
<a href="https://youtu.be/cxtBWz6Fbss">
    <img src="https://img.shields.io/badge/YouTube-FF0000?style=for-the-badge&logo=youtube&logoColor=white/"
        style="height : auto; margin-left : 10px; margin-right : 10px;"/>
</a>

<br>

<h2 id="내가-구현한-기능들">내가 구현한 기능들</h2>
<h3 id="소셜-로그인-페이지">소셜 로그인 페이지</h3>
<p><img src="https://user-images.githubusercontent.com/67620484/125184628-5707e000-e25a-11eb-9e35-19c65d68c998.gif" alt="DRIP_로그인"></p>
<ul>
<li>카카오 로그인 API를 활용한 구성</li>
<li>카카오를 통해 Token을 받고 Backend와의 통신이 완료되면 Backend로부터 Token을 받아 Localstorage에 저장</li>
<li>카카오 로그아웃 API를 통해 로그아웃을 실행하고 성공하면 Localstorage에 저장 된 Token 삭제</li>
</ul>
<hr>
<h3 id="제품-상세-페이지">제품 상세 페이지</h3>
<p><img src="https://user-images.githubusercontent.com/67620484/125184669-a3532000-e25a-11eb-8639-ecb6c9629a83.gif" alt="DRIP_상세페이지"></p>
<ul>
<li>백엔드와의 통신을 통해 상품 데이터를 렌더링</li>
<li>카카오 지도 API를 활용하여 수신된 주소 값을 기반으로 지도 출력</li>
<li>상세페이지 내 찜하기 기능 활성화</li>
</ul>
<hr>
<h3 id="찜-페이지">찜 페이지</h3>
<p><img src="https://github.com/AutumnWithJay/21-2nd-Drip-frontend/blob/master/public/Review/Images/DRIP_%EC%B0%9C.gif?raw=true" alt="DRIP_찜">  </p>
<hr>
<h3 id="마이-페이지">마이 페이지</h3>
<p><img src="https://github.com/AutumnWithJay/21-2nd-Drip-frontend/blob/master/public/Review/Images/DRIP_%EB%A7%88%EC%9D%B4%ED%8E%98%EC%9D%B4%EC%A7%80.gif?raw=true" alt="DRIP_마이페이지"></p>
<ul>
<li>주문 페이지를 통해 넘어온 주문 데이터를 렌더링</li>
<li>현재 예약한 체험 내역과 지난 예약 분리하여 노출</li>
<li>지난 예약 내역에서 후기 작성 페이지로 넘어갈 수 있도록 버튼 생성</li>
</ul>
<hr>
<h3 id="후기-페이지">후기 페이지</h3>
<p><img src="https://github.com/AutumnWithJay/21-2nd-Drip-frontend/blob/master/public/Review/Images/DRIP_%ED%9B%84%EA%B8%B0%ED%8E%98%EC%9D%B4%EC%A7%80.gif?raw=true" alt="DRIP_후기"></p>
<ul>
<li>상품 ID를 기반으로 후기 리스트 수신하여 노출</li>
<li>후기 별 도움이 됐어요 버튼을 클릭하면 숫자 카운트가 증가</li>
<li>최신순, 도움순으로 후기 리스트 필터링</li>
<li>폼 데이터를 활용한 사진, 후기 내용, 별점이 포함된 후기 데이터 전송</li>
</ul>
<hr>
<h2 id="기억에-남았던-부분">기억에 남았던 부분</h2>
<h3 id="카카오-api-활용">카카오 API 활용</h3>
<h4 id="로그인">로그인</h4>
<pre><code class="language-js">import { API } from &#39;../../config&#39;;

const KakaoLogin = history =&gt; {
  window.Kakao.Auth.login({
    success: res =&gt; {
      // 로그인 요청이 성공하면 카카오에게 토큰 수신 후 받은 토큰을 변수로 저장
      const authToken = res.access_token;

      window.Kakao.API.request({
        // 로그인한 사용자의 닉네임, 프로필 사진, 이메일 계정을 요청
        url: &#39;/v2/user/me&#39;,
        data: {
          property_keys: [
            &#39;properties.nickname&#39;,
            &#39;properties.profile_image&#39;,
            &#39;kakao_account.email&#39;,
          ],
        },
        success: res =&gt; {
          fetch(`${API}/users/signin`, {
            method: &#39;POST&#39;,
            headers: {
              // 카카오로부터 수신한 토큰을 우리 백엔드에게 전송
              Authorization: authToken,
            },
            // 백엔드에게 회원등록이 가능하게 위에서 받은 사용자 데이터 전송
            body: JSON.stringify({
              name: res.properties.nickname,
              profile_image: res.properties.profile_image,
              email: res.kakao_account.email,
            }),
          })
            .then(res =&gt; res.json())
            .then(res =&gt; {
              // 백엔드로부터 토큰을 수신했다면 해당 토큰을 LocalStorage에 저장
              if (res.token) {
                localStorage.setItem(&#39;Token&#39;, res.token);
                alert(&#39;로그인 되었습니다.&#39;);
                // 로그인 완료창 확인 후 메인으로 이동
                history.push(&#39;/main&#39;);
              } else {
                // 토큰을 수신하지 못했다면 로그인 실패
                alert(&#39;다시 확인해 주세요.&#39;);
              }
            });
        },
      });
    },
    fail: function (err) {
      console.log(&#39;에러&#39;, err);
    },
  });
};

export default KakaoLogin;
</code></pre>
<h4 id="지도">지도</h4>
<pre><code class="language-js">/*global kakao*/

import { useEffect } from &#39;react&#39;;

const Map = props =&gt; {
  useEffect(() =&gt; {
    const container = document.getElementById(&#39;map&#39;);
    const options = {
      center: new window.kakao.maps.LatLng(
        37.365264512305174,
        127.10676860117488
      ),
      level: 3,
    };
    const map = new window.kakao.maps.Map(container, options);
    const geocoder = new window.kakao.maps.services.Geocoder();

    // 주소로 좌표를 검색합니다
    // props로 받은 상품의 주소값을 지도에 표시하기 위해 등록
    geocoder.addressSearch(props.address, function (result, status) {
      // 정상적으로 검색이 완료됐으면
      if (status === window.kakao.maps.services.Status.OK) {
        const coords = new window.kakao.maps.LatLng(result[0].y, result[0].x);

        // 결과값으로 받은 위치를 마커로 표시합니다
        const marker = new window.kakao.maps.Marker({
          map: map,
          position: coords,
        });

        // 지도의 중심을 결과값으로 받은 위치로 이동시킵니다
        map.setCenter(coords);
      }
    });
  }, []);

  return (
    &lt;div&gt;
      &lt;div id=&quot;map&quot; style={{ width: &#39;485px&#39;, height: &#39;300px&#39; }}&gt;&lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Map;</code></pre>
<h1 id="프로젝트를-마무리-짓고">프로젝트를 마무리 짓고..</h1>
<p>이번 프로젝트는 시간이 굉장히 빨리 지나갔음에도 힘든 느낌은 전혀 없었다. 다들 조용조용 자기일만 하셔서 스트레스없이 할수 있었기 때문이다. 물론 중간에 테이블 구성때문에 백엔드분들이 좀 애먹은 부분도 있지만 서로 잘 협력해서 괜찮은 결과물이 나올수 있지 않았나 싶다.
2차 프로젝트를 마지막으로 위코드의 프로젝트 기간은 끝이 났다. 그리고 3개월차에 접어든 나는 기업협업을 선택하지 않고 위코드내에서 개인 공부와 프로젝트를 진행하고 있다.
프로젝트에서 부족했던 부분을 리팩토링을 통해 좀 더 다듬고 많이 활용해볼 예정이다.
프로젝트를 열심히 같이 해준 송준, 박준영, 최승리 그리고 묭님에게 감사하며 두달의 위코드도 끝.!🙆‍♀️😘</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[netlify를 통해 배포를 하며 발생한 에러들]]></title>
            <link>https://velog.io/@devpark_0x1c/netlify%EB%A5%BC-%ED%86%B5%ED%95%B4-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%ED%95%98%EB%A9%B0-%EB%B0%9C%EC%83%9D%ED%95%9C-%EC%97%90%EB%9F%AC%EB%93%A4</link>
            <guid>https://velog.io/@devpark_0x1c/netlify%EB%A5%BC-%ED%86%B5%ED%95%B4-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%ED%95%98%EB%A9%B0-%EB%B0%9C%EC%83%9D%ED%95%9C-%EC%97%90%EB%9F%AC%EB%93%A4</guid>
            <pubDate>Mon, 12 Jul 2021 09:25:06 GMT</pubDate>
            <description><![CDATA[<p>이번 2차 프로젝트 배포가 가능하냐는 팀원 준영님의 말에 netlify를 통해 배포를 진행하고 있는데, 여러가지 에러가 발생하여 상당히 당황스러웠다.</p>
<h1 id="error-list">Error list</h1>
<h2 id="1-mixed-content">1. mixed content</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/04696bb2-320b-4f7a-9127-3ec857ac7e1a/mixed_content.png" alt=""></p>
<p><strong>mixed content</strong>는 암호화된 HTTPS 기반의 사이트에서 암호화되지 않은 HTTP 사이트에 요청을 보내서 Mixed content 에러가 발생한 것이라고 한다.</p>
<p>위 에러를 해결하기 위해서는 
<code>&lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;upgrade-insecure-requests&quot;&gt;</code> 
를 index.html에 추가해주면 에러가 사라진다.</p>
<blockquote>
<p><a href="https://stackoverflow.com/questions/35178135/how-to-fix-insecure-content-was-loaded-over-https-but-requested-an-insecure-re">https://stackoverflow.com/questions/35178135/how-to-fix-insecure-content-was-loaded-over-https-but-requested-an-insecure-re</a></p>
</blockquote>
<hr>
<h2 id="2-treating-warnings-as-errors-because-processenvci--true">2. Treating warnings as errors because process.env.CI = true</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/8efefac6-789c-498a-a61b-00c0b114eb7b/netlify_error.png" alt=""></p>
<p>site settings -&gt; build&amp;deploy -&gt; continuous deployment에 build command를 기존에 <code>npm run build</code>로 되어 있는것을<code>CI= npm run build</code>로 변경해주면 된다.</p>
<hr>
<h2 id="3-netlify에-환경-변수-설정">3. Netlify에 환경 변수 설정</h2>
<blockquote>
<p><a href="https://www.netlify.com/blog/2020/12/10/environment-variables-in-next.js-and-netlify/">https://www.netlify.com/blog/2020/12/10/environment-variables-in-next.js-and-netlify/</a></p>
</blockquote>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/20cec366-22df-4502-884d-e88b9e6a0d9d/image.png" alt=""></p>
<p>현재 프로젝트에서 카카오 API를 사용하고 있어서 .env에 세팅을 했더니 에러가 나는 바람에 해결을 어떻게 할까 하다가 npm에 올라와있는 dotenv_webpack을 사용해도 되지만 netlify에서 가이드라인을 제시해줘서 위의 문서를 보고 해결해줬다.
site setting -&gt; build&amp;deploy -&gt; enviroment에서 내가 사용할 환경변수를 세팅해주면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] Custom Hooks - 3]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-Custom-Hooks-3</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-Custom-Hooks-3</guid>
            <pubDate>Sun, 11 Jul 2021 01:51:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@devpark_0x1c/TIL-Custom-Hooks-2">https://velog.io/@devpark_0x1c/TIL-Custom-Hooks-2</a>  </p>
</blockquote>
<p>에 이어 3번째 hooks 글을 써본다.</p>
<h2 id="usefadein">useFadeIn</h2>
<p>** fadeIn 애니메이션 처리를 할 수 있는 hooks이다** </p>
<pre><code class="language-js">import { useEffect, useRef } from &quot;react&quot;;

const useFadeIn = (duration = 1, delay = 0) =&gt; {
  // duration이 number가 아닐 경우 return
  if (!typeof duration === &quot;number&quot;) {
    return;
  }

  const element = useRef();

  useEffect(() =&gt; {
    // 현재 hooks를 사용하는 태그에서 사용될 css 처리 아래와 같이 한다
    if (element.current) {
      const { current } = element;
      current.style.transition = `opacity ${duration}s ease-in-out ${delay}s`;
      current.style.opacity = 1;
    }
  }, []);

  return { ref: element, style: { opacity: 0 } };
};

const App = () =&gt; {
  // useFadeIn(지속시간, 지연시간)으로 사용된다
  const fadeInH1 = useFadeIn(2, 3);
  const fadeInP = useFadeIn(5, 10);
  return (
    &lt;div&gt;
      &lt;h1 {...fadeInH1}&gt;miyoung&lt;/h1&gt;
      &lt;p {...fadeInP}&gt;ㅎ_ㅎ&lt;/p&gt;
    &lt;/div&gt;
  );
};

export default App;</code></pre>
<h2 id="usescroll">useScroll</h2>
<p>** scroll 위치에 따른 이벤트를 처리하는 hooks**</p>
<pre><code class="language-js">import { useEffect, useState } from &quot;react&quot;;

const useScroll = () =&gt; {
  // scroll의 y축의 초기값을 설정해준다
  const [scroll, setScroll] = useState({
    y: 0
  });

  // scroll이 움직일 때마다 state의 y의 값을 변경한다
  const handleScroll = () =&gt; {
    setScroll({ y: window.scrollY });
  };

  useEffect(() =&gt; {
    window.addEventListener(&quot;scroll&quot;, handleScroll);
    return () =&gt; {
      window.removeEventListener(&quot;scroll&quot;, handleScroll);
    };
  }, []);

  return scroll;
};

const App = () =&gt; {
  const { y } = useScroll();
  return (
    &lt;div style={{ height: &quot;1000vh&quot; }}&gt;
      // 스크롤의 y축이 100 이상일 경우 h1의 글씨 색상이 변경된다
      &lt;h1 style={{ position: &quot;fixed&quot;, color: y &gt; 100 ? &quot;pink&quot; : &quot;black&quot; }}&gt;
        Miyoung
      &lt;/h1&gt;
    &lt;/div&gt;
  );
};

export default App;
</code></pre>
<h2 id="usenotification">useNotification</h2>
<p>** notification을 활용한 hooks이다**</p>
<blockquote>
<p><a href="https://developer.mozilla.org/ko/docs/Web/API/notification">https://developer.mozilla.org/ko/docs/Web/API/notification</a></p>
</blockquote>
<pre><code class="language-js">const useNotification = (title, body) =&gt; {
  // 이 부분은 notification의 mdn에 있는 것을 그대로 사용하였다

  // 브라우저가 Notification을 지원하지 않으면 아래와 같은 경고가 나타난다
  if (!(&quot;Notification&quot; in window)) {
    alert(&quot;This browser does not support desktop notification&quot;);
  }

  const fireNotification = () =&gt; {
    // noti의 권한이 granted일 경우 매개변수로 넘어온 title, body를 사용하여 알림창이 나타난다
    if (Notification.permission === &quot;granted&quot;) {
      new Notification(title, body);
      // notification이 denied일 경우 권한 요청을 하고 granted로 변경되면 알림창이 나타난다
      if (Notification.permission === &quot;denied&quot;) {
        Notification.requestPermission((permission) =&gt; {
          if (permission === &quot;granted&quot;) {
            new Notification(title, body);
          }
        });
      }
    }
  };

  return fireNotification;
};

const App = () =&gt; {
  // 알림창에 대한 내용을 선언한다
  const notif = useNotification(&quot;빵 먹을까?&quot;, {
    body: &quot;미영이 빵 좋아해&quot;
  });

  return (
    &lt;div style={{ height: &quot;1000vh&quot; }}&gt;
      // 버튼을 클릭할 경우 권한을 확인하고 위에서 설정한 알림 내용이 나타난다
      &lt;button onClick={notif}&gt;Miyoung&lt;/button&gt;
    &lt;/div&gt;
  );
};

export default App;
</code></pre>
<h2 id="useaxios">useAxios</h2>
<p>** 프로젝트를 할 때 axios를 사용할 일이 굉장히 많았는데, 중첩되는 내용이 너무 많아서 hooks를 사용했으면 좋지 않았을까 하는 생각이 든다**</p>
<pre><code class="language-js">import defaultAxios from &quot;axios&quot;;
import { useEffect, useState } from &quot;react&quot;;

const useAxios = (opts, axiosInstance = defaultAxios) =&gt; {
  // state에 데이터가 불러오기 전까지 사용될 loading
  // 수신된 데이터를 받아둘 data
  // error 메세지를 받을 error를 선언한다
  const [state, setState] = useState({
    loading: true,
    data: null,
    error: null
  });

  // 다시 fetch할 때 사용할 trigger를 선언한다
  const [trigger, setTrigger] = useState(0);

  // fetch할 대상이 될 url이 없을 경우 return
  if (!opts.url) {
    return;
  }

  // reFetch는 아래에서 새로 받아올 데이터를 유지하고 trigger는 현재 시간을 설정해줌으로 새로 rendering을 한다.
  const reFetch = () =&gt; {
    setState({
      ...state,
      loading: true
    });
    setTrigger(Date.now());
  };

  useEffect(() =&gt; {
    // axios를 실행하고 데이터를 처리한다
    // 다시 데이터를 받아올 trigger를 처리하기 위해 useEffect의 dependency는 trigger를 바라본다 
    axiosInstance(opts.url)
      .then((data) =&gt; {
        setState({
          loading: false,
          error: null,
          data: JSON.stringify(data)
        });
      })
      .catch((err) =&gt; {
        setState({
          loading: false,
          err,
          ...state
        });
      });
  }, [trigger]);

  return { ...state, reFetch };
};

export default useAxios;</code></pre>
<p>이번 hooks 공부를 마무리지으며 2차 프로젝트에 hooks를 적용할 부분이 있으면 refactoring을 하면서 적용해볼 생각이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] Custom Hooks - 2]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-Custom-Hooks-2</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-Custom-Hooks-2</guid>
            <pubDate>Thu, 08 Jul 2021 07:34:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@devpark_0x1c/TID-Custom-hook-1">https://velog.io/@devpark_0x1c/TID-Custom-hook-1</a></p>
</blockquote>
<p>지난번 <strong>custom hooks 1번에 이어</strong> 올리는 글이다.</p>
<h2 id="useclick">useClick</h2>
<pre><code class="language-js">import { useEffect, useRef } from &quot;react&quot;;

const useClick = (onClick) =&gt; {
  // 매개변수 onClick이 함수가 아닐 경우 Early Return
  if (typeof onClick !== &quot;function&quot;) {
    return;
  }

  const element = useRef();

  // ref로 설정된 element에 click 이벤트 설정
  useEffect(() =&gt; {
    if (element.current) {
      element.current.addEventListener(&quot;click&quot;, onClick);
    }
    return () =&gt; {
      element.current.removeEventLister(&quot;click&quot;, onClick);
    };
  }, []);

  return element;
};

const App = () =&gt; {
  const handleClick = () =&gt; console.log(&quot;clicked&quot;);
  // handleClick을 커스텀 훅 useClick에 매개변수로 넣어준다
  const h1Click = useClick(handleClick);

  return (
    &lt;div className=&quot;App&quot;&gt;
      // useClick을 통해 설정된 이벤트로 인하여 h1 태그를 클릭하면 &quot;clicked&quot;가 콘솔에 나타난다.
      &lt;h1 ref={h1Click}&gt;Hi&lt;/h1&gt;
    &lt;/div&gt;
  );
};

export default App;
</code></pre>
<h2 id="useconfirm">useConfirm</h2>
<p><strong>window 객체가 가지고 있는 confirm을 활용한 hooks</strong></p>
<blockquote>
<p><a href="https://developer.mozilla.org/ko/docs/Web/API/Window/confirm">https://developer.mozilla.org/ko/docs/Web/API/Window/confirm</a>  </p>
</blockquote>
<pre><code class="language-js">const useConfirm = (message, resolve, reject) =&gt; {
  // resolve가 없거나 resolve의 타입이 function이 아닐 경우 리턴
  if (!resolve || typeof resolve !== &quot;function&quot;) {
    return;
  }
  // reject 없거나 reject의 타입이 function이 아닐 경우 리턴
  if (!reject || typeof reject !== &quot;function&quot;) {
    return;
  }

  // confirm 버튼을 눌렀을 때 확인을 누르면 매개변수로 넘어온 resolve를 실행
  // 취소를 누르면 매개변수로 넘어온 reject를 실행한다.
  const confirmAction = () =&gt; {
    if (window.confirm(message)) {
      resolve();
    } else {
      reject();
    }
  };

  return confirmAction;
};

const App = () =&gt; {
  const resolve = () =&gt; console.log(&quot;Resolve&quot;);
  const reject = () =&gt; console.log(&quot;Reject&quot;);
  const confirmTest = useConfirm(&quot;Confirm?&quot;, resolve, reject);

  return &lt;button onClick={confirmTest}&gt;yes?&lt;/button&gt;;
};

export default App;
</code></pre>
<h2 id="usepreventleave">usePreventLeave</h2>
<p>** 브라우저에서 탭을 끄거나 새로고침을 할 때 다시 한번 물어보는 이벤트를 활용한 hooks**</p>
<blockquote>
<p><a href="https://developer.mozilla.org/ko/docs/Web/API/Window/beforeunload_event">https://developer.mozilla.org/ko/docs/Web/API/Window/beforeunload_event</a>  </p>
</blockquote>
<pre><code class="language-js">const usePreventLeave = () =&gt; {
  // beforeunload 문서에 따르면 대화상자를 표시하기 위해선 preventDefault를 선언해주어야 한다.
  // 크롬같은 경우는 이벤트의 returnValue 속성에 문자열 할당도 해주어야한다.
  const leave = (event) =&gt; {
    event.preventDefault();
    event.returnValue = &quot;&quot;;
  };

  const addUnloadEvent = () =&gt; {
    window.addEventListener(&quot;beforeunload&quot;, leave);
  };

  const removeUnloadEvent = () =&gt; {
    window.removeEventListener(&quot;beforeunload&quot;, leave);
  };

  return { addUnloadEvent, removeUnloadEvent };
};

const App = () =&gt; {
  const { addUnloadEvent, removeUnloadEvent } = usePreventLeave();
  return (
    &lt;div&gt;
      // addLeaveEvent를 클릭하면 새로고침이나 닫기를 할 경우 물어보는 이벤트가 발생한다
      &lt;button onClick={addUnloadEvent}&gt;addLeaveEvent&lt;/button&gt;
     // removeLeaveEvent 버튼은 위에서 실행되는 이벤트를 제거한다
      &lt;button onClick={removeUnloadEvent}&gt;removeLeaveEvent&lt;/button&gt;
    &lt;/div&gt;
  );
};

export default App;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] Custom hook - 1]]></title>
            <link>https://velog.io/@devpark_0x1c/TID-Custom-hook-1</link>
            <guid>https://velog.io/@devpark_0x1c/TID-Custom-hook-1</guid>
            <pubDate>Tue, 06 Jul 2021 10:01:25 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 마치고 <strong>Hooks</strong>에 대해서 좀 더 공부해보고 싶어서 이런 저런 자료를 찾아보며 공부 중에 있다.
해당 내용에 대한 이해도가 높아진다면 프로젝트에 공통적인 부분을 Hooks로 만들어서 적용해볼 예정이다.
일단 <strong>useState</strong>를 활용한 Hooks를 공부해보았다.</p>
<h3 id="useinput">useInput</h3>
<pre><code class="language-js">const useInput = (initialValue, validation) =&gt; {
  const [value, setValue] = useState(initialValue);

  const onChange = (e) =&gt; {
    const { value } = e.target;

    if (typeof validation === &quot;function&quot;) {
      validation(value);
    }

    if (validation(value)) {
      setValue(value);
    }
  };

  return { value, onChange };
};

const App = () =&gt; {
  const validate = value =&gt; value.length &lt;= 10;
  const {value, onChange} = useInput(&quot;Mr.&quot;, validate);

  return (
    &lt;div&gt;
      &lt;input value={value} onChange={onChange}/&gt;
    &lt;/div&gt;
  );
};</code></pre>
<p>input의 입력값에 대한 검증을 하는 Hooks이다. 
위의 경우는 input의 value값의 길이가 10이 넘으면 <code>if(validation(value))</code>의 값이 <strong>false</strong>가 되어서 더이상 setValue가 동작하지 않는다.
위 처럼 길이에 대한 조건으로 hooks를 만들수도 있지만 본인이 원하는 검증 방식으로도 커스터마이징 해볼수 있다.</p>
<h3 id="usetabs">useTabs</h3>
<pre><code class="language-js">const content = [
  {
    tab: &quot;Section 1&quot;,
    content: &quot;I&#39;m the content of section 1&quot;
  },
  {
    tab: &quot;Section 2&quot;,
    content: &quot;I&#39;m the content of section 2&quot;
  }
];

const useTabs = (initialTab, content) =&gt; {
  const [currentIndex, setCurrentIndex] = useState(initialTab);

  if (!content || !Array.isArray(content)) {
    return;
  }

  const handleOnClick = (index) =&gt; {
    setCurrentIndex(index);
  };

  return { currentIndex, handleOnClick };
};

const App = () =&gt; {
  const { currentIndex, handleOnClick } = useTabs(0, content);
  return (
    &lt;div className=&quot;App&quot;&gt;
      {content.map((data, index) =&gt; {
        return (
          &lt;button key={index} onClick={() =&gt; handleOnClick(index)}&gt;
            {data.tab}
          &lt;/button&gt;
        );
      })}
      {content[currentIndex].content}
    &lt;/div&gt;
  );
};</code></pre>
<p>이 <strong>useTabs</strong>는 프로젝트할 때 적용해보았으면 정말 좋았을 hooks이다.
프로젝트의 상단 메뉴에 탭이 여러개 있는데, useTabs의 default value로 0을 주면 처음 웹페이지에 접속할 때 rendering이 되면서 0번째에 있는 tab이 선택되고 다른 탭을 눌렀을 때 <code>&lt;button key={index} onClick={() =&gt; handleOnClick(index)}&gt;</code>의 <strong>onClick</strong> 이벤트를 통해 내가 클릭한 탭의 <code>index</code>값을 넘겨주면서 탭 전환이 이루어질수 있도록 한다.</p>
<h3 id="usetitle">useTitle</h3>
<pre><code class="language-js">const useTitle = (initialTitle) =&gt; {
  const [title, setTitle] = useState(initialTitle);

  const changeTitle = () =&gt; {
    const titleChange = document.querySelector(&quot;title&quot;);
    titleChange.innerText = title;
  };

  useEffect(changeTitle, [title]);

  return setTitle;
};

const App = () =&gt; {
  const setTitle = useTitle(&quot;Loading&quot;);

  setTimeout(() =&gt; {
    setTitle(&quot;Done&quot;);
  }, 2000);

  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;h1&gt;Hi&lt;/h1&gt;
    &lt;/div&gt;
  );
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PROJECT] 위코드 1차 프로젝트 회고]]></title>
            <link>https://velog.io/@devpark_0x1c/1%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EB%A7%88%EC%B9%98%EA%B3%A0</link>
            <guid>https://velog.io/@devpark_0x1c/1%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%EB%A7%88%EC%B9%98%EA%B3%A0</guid>
            <pubDate>Sun, 20 Jun 2021 07:26:52 GMT</pubDate>
            <description><![CDATA[<h1 id="🤜">🤜</h1>
<p>위코드 생활의 5주차! 드디어 기대했던 프로젝트가 시작되는 주이다.
조 구성은 위코드 측에서 알아서 짜주는거라 어떤 분들이랑 하게될지 무척 궁금했는데, 5명으로 구성된 조에 속하게 되었다.
우리는 쿠캣을 클론하는 조였고, 백엔드 3명, 프론트엔드 2명의 인원으로 시작했다.
백엔드는 성규님, 경철님, 혜지님 프론트는 준희님 그리고 나 인데, 준희님을 제외한 분들은 일대일로 대화해본적이 없는 분들이라 좀 낯설었지만 프로젝트 기간동안 으쌰으쌰하면서 친해질것이 뻔해서 인원 구성에 대해서는 만족한 편이다.</p>
<h1 id="✍️-프로젝트-구성">✍️ 프로젝트 구성</h1>
<p>조가 구성되고 첫 날엔 우리가 &#39;어떤 범위까지 할수 있을까?&#39; 라는 주제를 가지고 엄청나게 긴 토론을 했다. 2주가 지난 지금도 그때의 기억이 생생하다.(당연한건가..?)
프론트쪽에서는 쿠켓의 페이지 전체를 해보는걸로 범위를 정했고 백엔드는 기존 메인페이지를 삭제하고 상품목록이 출력되는 페이지를 메인으로 바꾼뒤에 추후에 시간이 남으면 메인페이지를 해보는 쪽으로 하자는 입장이었는데, 프론트인 우리 입장에선 다 해볼수 있을거 같아서 쉽게 양보하긴 힘든 입장이라 멘토님이 의견 조율을 해준 뒤에 아래와 같은 페이지를 구성해보았다.</p>
<h2 id="파트">파트</h2>
<h3 id="메인페이지">메인페이지</h3>
<ul>
<li>회원가입</li>
<li>로그인</li>
<li>상품 나열된 메인페이지</li>
<li>런칭특가 페이지</li>
<li>상품 상세페이지</li>
<li>관심 상품 페이지</li>
<li>검색</li>
<li>검색 결과 페이지</li>
<li>장바구니(추가 구현 사항)</li>
</ul>
<h3 id="서브페이지">서브페이지</h3>
<ul>
<li>헤더</li>
<li>헤더 네비</li>
<li>푸터</li>
<li>푸터 네비</li>
<li>구매 용 모달 창</li>
</ul>
<h3 id="기타">기타</h3>
<ul>
<li>동적라우팅    </li>
<li>토큰 활용 (Nav바 로그인 상태 감지, 로그아웃, 좋아요 기능)</li>
</ul>
<h2 id="내가-맡은-파트">내가 맡은 파트</h2>
<ul>
<li>회원가입</li>
<li>상품 상세페이지 </li>
<li>검색 </li>
<li>검색결과페이지 </li>
<li>장바구니 </li>
<li>헤더 </li>
<li>푸터 </li>
<li>구매용 모달창</li>
<li>동적라우팅</li>
<li>토큰 활용 (Nav바 로그인 상태 감지, 로그아웃, 좋아요 기능)</li>
</ul>
<h1 id="✍️-팀-기록">✍️ 팀 기록</h1>
<p>우리 조는 <strong>Trello</strong>를 사용해서 일정을 관리했고, 회의록 및 기타 참고사항에 대한 기록용으로 <strong>Notion</strong>을 사용했다.
Notion은 기존에 사용하고 있던 툴이라서 어렵지 않게 사용했고, Trello는 처음 써봄에도 불구하고 UI가 직관적으로 설계되어있어 어려움없이 사용할 수 있었다.
<img src="https://images.velog.io/images/devpark_0x1c/post/9d3cb1a4-bc86-4873-9050-eeb4449c1e12/image.png" alt="">
지금은 다 완료된 상태여서 Done으로 모든 티켓들이 옮겨져있지만, 우리가 진행할 목록들을 back-log에 넣어서 프리뷰를 해보고 해당 주차에 할것들은 this SPRINT에 넣어서 넣어놓은 것은 꼭 다 하는걸 목표로 하였다. 트렐로 툴 자체가 카드 하나를 옮기면 바로 알림 메세지에 누가 어디로 뭘 옮겼다는 게 뜨기 때문에 못해서 다시 back-log로 옮겨놓는 것 자체가 부끄러워서 빠짐없이 했었다ㅎㅎ</p>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/3adf1a3a-f800-4a46-b0b2-ac440d2eb450/image.png" alt="">
노션은 개인 페이지로만 사용해보고 프로젝트에 활용해본 것은 이번이 처음이었는데, 협업에 최적화 된 툴이 아닐까 싶다. 서로 자신의 페이지에 대한 내용을 공유할 때도 그렇고 회의에 대한 내용을 기록해두니 나중에 우리가 무슨말을 했는지 무조건 알수있어서 노션의 유용함을 이번 프로젝트를 통해 다시 한번 느낄수 있었다.</p>
<h1 id="1️⃣주차">1️⃣주차</h1>
<p>기존 쿠캣사이트는 모바일 최적화 사이트라 반응형이 없기 때문에 우리 역시 반응형 레이아웃은 진행하지 않기로 했다.
나는 맡은 페이지들에 대한 레이아웃을 먼저 만들고 그 뒤에 기능을 구현하는 방식으로 프로젝트를 시작했는데, 먼저 공통으로 사용되는 헤더, 푸터를 만들었고 그 뒤에 주요 페이지인 회원가입 -&gt; 상품 상세페이지 -&gt; 구매용 모달창 -&gt; 검색 -&gt; 검색 결과페이지 -&gt; 장바구니 순으로 진행을 했다.
페이지 레이아웃은 생각보다 빠른 3일차 오전에 끝나서 다시 위와 같은 순으로 리액트를 사용하여 기능 구현을 계획했다.
1주차는 회원가입, 상품 상세페이지, 구매용 모달창을 구현했는데, 생각보다 이해 안가는 부분이 있어서 예상 소요시간보다 시간이 초과됐다.</p>
<h2 id="헤더-푸터">헤더, 푸터</h2>
<p>이건 단순한 레이아웃이기 때문에 따로 작성하진 않겠다.</p>
<h2 id="회원가입">회원가입</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/f770da79-c3ae-4e9b-b637-675a3c2ad843/%E1%84%92%E1%85%AC%E1%84%8B%E1%85%AF%E1%86%AB%E1%84%80%E1%85%A1%E1%84%8B%E1%85%B5%E1%86%B8.gif" alt="">
회원가입은 정규표현식으로 모든걸 해결할 수 있어서 크게 어려운 것 없이 마무리 지을수 있었다. 정규표현식은 아래와 같은 코드로 작성하였다.</p>
<pre><code class="language-js">validateEmail = () =&gt; {
    const { email } = this.state;
    const regexEmail = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{3,10})+$/;
    return email.match(regexEmail);
  };

validatePassword = () =&gt; {
  const { password, passwordConfirm } = this.state;
  let validatedResult = &#39;&#39;;

  if (password === passwordConfirm) {
    const regexPassword =
          /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^*+=-]).{7,20}$/;
    passwordConfirm.match(regexPassword)
      ? (validatedResult = &#39;유효한 비밀번호입니다&#39;)
    : (validatedResult = &#39;8~20글자의 비밀번호를 입력해주세요&#39;);
  } else if (password !== passwordConfirm) {
    validatedResult = &#39;비밀번호가 일치하지 않습니다&#39;;
  }

  return validatedResult;
};

validateUserName = () =&gt; {
  const { username } = this.state;
  const regexUsername = /^[가-힣]{2,4}|[a-zA-Z]{2,10}\s[a-zA-Z]{2,10}$/;
  return username.match(regexUsername);
};

validatePhoneNumber = () =&gt; {
  const { phoneNumber } = this.state;
  const regexPhoneNumber = /^\d{3}-\d{4}-\d{4}$/;
  return phoneNumber.match(regexPhoneNumber);
};</code></pre>
<h2 id="상품-상세페이지">상품 상세페이지</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/a5f71aec-bfa6-4627-a68d-3cb3687550f2/%E1%84%89%E1%85%A1%E1%86%BC%E1%84%91%E1%85%AE%E1%86%B7%E1%84%89%E1%85%A1%E1%86%BC%E1%84%89%E1%85%A6%E1%84%91%E1%85%A6%E1%84%8B%E1%85%B5%E1%84%8C%E1%85%B5.gif" alt=""></p>
<p>상세페이지에서 배울수 있던건 <strong>toLocaleString()</strong> 이다.
상품 가격을 노출하는 페이지이다보니 천의자리에서 , 를 넣어야했는데 나는 정규표현식으로 복잡하게 넣었지만 멘토님의 피드백에 맞춰서 toLocalString을 사용해보라는 조언을 얻고 해당 내용을 찾아보니 너무 간단하게 해결할 수가 있어서 허무했다.
그냥 <code>금액관련변수.toLocaleString()</code>이라고 입력하면 위 가격 표시처럼 천의 자리 단위로 알아서 , 가 입력된다.</p>
<blockquote>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/toLocaleString">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/toLocaleString</a></p>
</blockquote>
<h2 id="상품-모달창">상품 모달창</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/76b7bf30-106f-410d-9785-3fa0088bc638/%E1%84%89%E1%85%A1%E1%86%BC%E1%84%91%E1%85%AE%E1%86%B7%20%E1%84%86%E1%85%A9%E1%84%83%E1%85%A1%E1%86%AF%E1%84%8E%E1%85%A1%E1%86%BC.gif" alt=""></p>
<p>모달창에 대한 내용은 이 글을 쓰기전에 한번 남겼기때문에 그 페이지 링크로 대체를 하겠다</p>
<blockquote>
<p><a href="https://velog.io/@devpark_0x1c/TID-Modal%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%8B%A4">https://velog.io/@devpark_0x1c/TID-Modal%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%8B%A4</a>  </p>
</blockquote>
<p>상품 모달창을 구현하면서 너무 멍청한 짓을 해서 지금 생각해도 웃음만 나온다ㅎㅎ</p>
<pre><code class="language-js">constructor() {
  super();
  this.state = {
    totalCount: 0
    totalAmount: 0
  }
}

calculateQuantity = () =&gt; {
  const { selectedItems } = this.state;
  const totalCount = selectedItems.reduce((acc, cur) =&gt; {
    return acc + cur.quantity;
  }, 0);
  this.setState({ totalCount });
};

calculateMoney = () =&gt; {
  const { selectedItems } = this.state;
  const totalMoney = selectedItems.reduce((acc, cur) =&gt; {
    return acc + cur.price * cur.quantity;
  }, 0);
  this.setState({ totalMoney });
};

countQuantity = (idx, kind) =&gt; {
  const { selectedItems } = this.state;
  const sign = kind === &#39;plus&#39; ? +1 : -1;
  const newSelectedItems = selectedItems.map(value =&gt; {
    return value.id === idx
      ? {
      ...value,
      quantity: value.quantity + sign,
    }
    : { ...value };
  });
  this.setState({ selectedItems: newSelectedItems }, () =&gt; {
    this.calculateMoney()
    this.calculateQuantity()
  });
};</code></pre>
<p>위 코드를 보면 내가 왜 그랬는지는 모르겠지만 불필요한 state를 만들어서 수량에 대한 클릭 이벤트가 발생했을 때, 돈과 수량에 대한 업데이트가 일어나도록 만들고 그 함수들을 실행하기 위해 <code>countQuantity</code>가 실행될 때마다 콜백으로 실행되게 하였는데... 전혀 저렇게 구성할 필요 없이 아래와 같은 코드를 통해 수량과, 돈을 리턴받아 JSX로 직접적으로 뿌려주면 되는거라서 위의 코드로 작성했을 때의 나를 보면 쥐구멍으로 숨고 싶다🥲</p>
<pre><code class="language-js">calculateQuantity = () =&gt; {
  const { selectedItems } = this.state;
  const totalCount = selectedItems.reduce((acc, cur) =&gt; {
    return acc + cur.quantity;
  }, 0);
  return totalCount;
};

calculateMoney = () =&gt; {
  const { selectedItems } = this.state;
  const totalMoney = selectedItems.reduce((acc, cur) =&gt; {
    return acc + cur.price * cur.quantity;
  }, 0);
  return totalMoney;
};</code></pre>
<h1 id="2️⃣주차">2️⃣주차</h1>
<p>2주차에는 많은 일들이 있었다.. 백엔드 분들이 작성한 모델링이 좀 꼬여서 전부 갈아엎었고, 새로운 모델링을 하기 위해 꽤 많은 시간이 소요됐다. 정말 테스트를 위해 백엔드와 붙여본건 프로젝트 발표 하루전이었다. 이 때 시간이 너무 부족해서 백엔드팀은 전체페이지에서 장바구니와 검색 페이지를 제외해야 할수도 있다해서 멘붕이 심하게 왔었다.
백엔드 멘토님과 우리 백엔드팀의 회의 결과 전체페이지에서 장바구니만 제외하기로 결정을 했다. 
흙.. 안그래도 해보고 싶었던 페이지가 하나 줄어서 좀 아쉬웠는데, 장바구니가 제외되서 너무 아쉬웠다..ㅠㅠ 그래도 멘붕인 상태에서도 포기하지 않고 진행해준 우리 백엔드팀에게 감사하다👏</p>
<h2 id="검색">검색</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/f5d23870-d124-4041-891f-efe69aea9992/%E1%84%80%E1%85%A5%E1%86%B7%E1%84%89%E1%85%A2%E1%86%A8.gif" alt=""></p>
<pre><code class="language-js">searchByInput = () =&gt; {
    const { productData, inputValue } = this.state;
    const filteredData = productData.result.filter(data =&gt;
      data.name.includes(inputValue)
    );

    this.setState({ searchedData: !inputValue ? [] : filteredData });
  };

searchByKeyword = keyword =&gt; {
  const { productData } = this.state;
  const filteredData 
    = productData.result.filter(data =&gt; data.name.includes(keyword));

  this.setState({ searchedData: filteredData });
};</code></pre>
<p>검색은 백엔드쪽에서 아직 모델링을 하던 중에 만들고 있어서 Mock Data를 활용했는데, 내가 만든 방식은 전체 데이터를 불러와서 필터링 하는 방식을 이용했다. 전체데이터를 기반으로 <code>필터링</code>을 해서 인풋에 값이 입력될 때마다 해당 값에 맞는 데이터가 list로 출력되고, 추천검색어에 있는 검색어도 역시 클릭하면 연관 상품이 출력된다.</p>
<h2 id="토큰-활용nav바-로그인-상태-감지-로그아웃-좋아요-기능">토큰 활용(Nav바 로그인 상태 감지, 로그아웃, 좋아요 기능)</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/65351471-ae80-4fa2-8ef6-59d3dddbfcbe/%E1%84%8C%E1%85%A9%E1%87%82%E1%84%8B%E1%85%A1%E1%84%8B%E1%85%AD.gif" alt=""></p>
<pre><code class="language-js">handleLikeButton = () =&gt; {
  const {
    match: {
      params: { id },
    },
  } = this.props;

  const authToken = localStorage.getItem(&#39;token&#39;);

  fetch(`http://10.58.5.96:8000/wishes`, {
    method: &#39;POST&#39;,
    headers: {
      Authorization: authToken,
    },
    body: JSON.stringify({
      food_id: id,
    }),
  })
    .then(res =&gt; res.json())
    .then(res =&gt; {
    res &amp;&amp; this.state.isLiked
      ? this.setState({ isLiked: false })
    : this.setState({ isLiked: true });
  });
};</code></pre>
<p>로그인을 할 때 서버로부터 토큰을 부여받는 데, 우린 토큰을 <strong>LocalStorage</strong>에 저장을 했다. 
좋아요를 누를 땐 서버에게 <strong>Token</strong>을 넘겨줘야 하기 때문에 localstorage로 부터 토큰을 불러와서 서버에게 넘겨주며 진행을 하였다. 좋아요를 해제하는 버튼은 위의 역순으로 method만 <strong>DELETE</strong>로 변경하여 작성하였다.</p>
<pre><code class="language-js">logout = () =&gt; {
  localStorage.removeItem(&#39;token&#39;);
};</code></pre>
<p>로그아웃은 버튼 자체에게 RemoveItem으로 토큰을 제거하는 방식으로 작성하였다.</p>
<h1 id="🥸-후기">🥸 후기</h1>
<p>2주라는 시간이 되게 길줄 알았는데 생각보다 시간이 너무 빨리 간 프로젝트 기간이었다. 길면 길고 짧으면 짧은 2주였지만 직접 무언가를 만들어봄으로써 내 코드에 대해서 객관적으로 되돌아볼수 있었던 좋은 기회였다. 
프로젝트 2주차에 데이터베이스를 전부 다 날려야하는 불상사가 있었음에도 끝까지 포기하지 않아준 우리 백엔드 조원에게 먼저 감사함을 전하고 싶다.
나와 함께 프론트의 한 축을 맡아준 준희님도 너무 감사하다. 대신 충분히 잘하고 있으니 자신감을 좀 더 가지셨으면 하는 작은 바램이 있다~
다들 너무 고생했습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TID] Modal을 만들다]]></title>
            <link>https://velog.io/@devpark_0x1c/TID-Modal%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%8B%A4</link>
            <guid>https://velog.io/@devpark_0x1c/TID-Modal%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%8B%A4</guid>
            <pubDate>Sun, 06 Jun 2021 08:05:50 GMT</pubDate>
            <description><![CDATA[<p>내일부터 위코드에서 1차 프로젝트가 시작된다. 
조가 어떻게 짜일지 정말 궁금하다. 사람들 성향이 너무나도 달라서 트러블이 일어나는것 아닌지 걱정되기도 한다.
개인적으로 원하는 사람이 있지만 되면 정말 좋고 안되면 내 일을 묵묵히 하면 되는것이기 때문에 크게 받을 필요는 없다고 생각한다. 내 사람들이 스트레스 받지 않기만 바랄뿐..🙁</p>
<p>프로젝트에 들어가기전에 몇가지를 미리 해보는게 좋을것 같아서 그 중 하나인 Modal을 구현해본것을 포스팅해본다.</p>
<p>이번 4주차 실습 항목으로 Monster라는 state, props, component, fetch에 대해서 리뷰해볼수 있는 것을 주었는데, 나는 이 과제에 Modal을 추가해보는 것으로 진행해보았다.</p>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/f2991615-a87d-44ea-b89a-0bd5e831de47/image.png" alt=""></p>
<p>위 화면이 monster 과제를 전부 완료하면 나타나는 창이다.
Modal을 구성할 때 가장 먼저 고려해야할 부분은 Modal 태그를 어디에다가 집어넣는가이다.
위 과제의 Component 구성은 아래와 같이 되어있다.</p>
<pre><code class="language-js">Monster(메인) -&gt; CardList(나열된 카드들) -&gt; Card(카드 안에 내용들)</code></pre>
<p>맨 처음 코드를 작성했을 땐 <code>CardList</code>에 카드가 나열되니까 정보 받은거 구분해서 띄워주면 되겠지? 라고 생각해서 작성했는데 막상 Modal 창을 만들고 띄워보니까 <code>Monster</code>에서 map으로 돌려서 cardList가 생성되니 <strong>몬스터 배열</strong>에서 맨 끝에 있는 몬스터의 데이터만 출력이 되는것이다. 문제의 Modal을 <code>Card</code>로 옮기고 다시 만들어보았다.</p>
<h2 id="구현">구현</h2>
<h3 id="state와-연관-함수">State와 연관 함수</h3>
<pre><code class="language-js">constructor() {
    super();
    this.state = {
      modal: false,
    };
  }

toggleModal = () =&gt; {
  this.setState({
    modal: !this.state.modal,
  });
};
</code></pre>
<p>Modal의 상태는 Boolean 값으로 관리하고 True일 경우 Modal창이 나타난다.
<strong>toggleModal</strong>은 클릭시 현재 Boolean값의 반대로 되게 하는 기능을 하는 함수이다.</p>
<h3 id="modalscss">Modal.scss</h3>
<pre><code class="language-css">body.active-modal {
  overflow-y: hidden;
}

.btn-modal {
  padding: 10px 20px;
  display: block;
  margin: 100px auto 0;
  font-size: 18px;
}

.modal,
.overlay {
  width: 100vw;
  height: 100vh;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  position: fixed;
  z-index: 1;
}

.overlay {
  background: rgba(49, 49, 49, 0.8);
}

.modal-content {
  position: absolute;
  top: 40%;
  left: 50%;
  display: flex;
  justify-content: space-between;
  transform: translate(-50%, -50%);
  line-height: 1.4;
  background: #f1f1f1;
  padding: 14px 28px;
  border-radius: 3px;
  max-width: 700px;
  min-width: 300px;
  z-index: 200;

  &gt; .monster-image {
    width: 50%;
    border: 1px solid black;
    border-radius: 50%;
  }

  &gt; .monster-info {
    display: flex;
    flex-direction: column;
    justify-content: center;

    &gt; .monster-name {
      font-weight: 700;
      text-decoration: underline;
    }
  }
}

.close-modal {
  position: absolute;
  top: 10px;
  right: 10px;
  padding: 5px 7px;
}
</code></pre>
<h3 id="modaljs">Modal.js</h3>
<pre><code class="language-js">import React from &#39;react&#39;;
import &#39;./Modal.scss&#39;;

class Modal extends React.Component {
  render() {
    const { id, name, company, website, toggleModal } = this.props;

    return (
      &lt;&gt;
        &lt;div className=&quot;modal&quot;&gt;
          &lt;div className=&quot;overlay&quot; onClick={() =&gt; toggleModal()}&gt;&lt;/div&gt;
          &lt;div className=&quot;modal-content&quot;&gt;
            &lt;img
              src={`https://robohash.org/${id}?set=set2&amp;size=180x180`}
              alt=&quot;Monster Profile&quot;
              className=&quot;monster-image&quot;
            /&gt;
            &lt;div className=&quot;monster-info&quot;&gt;
              &lt;p className=&quot;monster-name&quot;&gt;{name}&lt;/p&gt;
              &lt;p className=&quot;monster-email&quot;&gt;
                &lt;span&gt;🏢&lt;/span&gt; {company}
              &lt;/p&gt;
              &lt;p className=&quot;monster-website&quot;&gt;
                &lt;span&gt;🌐&lt;/span&gt; {website}
              &lt;/p&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/&gt;
    );
  }
}

export default Modal;</code></pre>
<p><strong>overlay</strong>는 Modal창이 켜졌을 때 주변 배경을 어둡게 하기 위해 Layer를 하나 위에 덮어씌운거라고 생각하면 된다. <strong>overlay</strong>위에 Modal 창이 나타나게 된다.
Modal창이 나타나면 꺼지게 하는걸 어떻게 구현할까라는걸 30분넘게 고민했는데, 고민할 필요가 없었던 사항이었다. 그냥 <strong>overlay</strong> 레이아웃이 바깥에 있으니 거기다가 toggleModal click event를 주면 끝인것이다.</p>
<h2 id="결과">결과</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/36bbca54-0f60-4dd8-b6a9-dc910f2fb4ba/Modal.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LWID] Westagram with react]]></title>
            <link>https://velog.io/@devpark_0x1c/LWID-Westagram-with-react</link>
            <guid>https://velog.io/@devpark_0x1c/LWID-Westagram-with-react</guid>
            <pubDate>Wed, 02 Jun 2021 22:12:17 GMT</pubDate>
            <description><![CDATA[<p>3주차에는 리액트를 배웠고, 2주차 기간에 자바스크립트로 만들었던 westagram을 react로 다시 만들어보는 작업을 진행했다. 
이번 역시 2주차 때 westagram을 만들었을 때 썼던 글처럼 리액트로 바꾸는 동안에 막혔던 부분, 그리고 프로젝트를 통해 리액트를 알수 있었던 것에 대해 정리해 보도록하겠다.</p>
<p><strong>2주차에 썼던 westagram 후기</strong></p>
<blockquote>
<p><a href="https://velog.io/@devpark_0x1c/TID-westagram-%EA%B5%AC%ED%98%84">https://velog.io/@devpark_0x1c/TID-westagram-%EA%B5%AC%ED%98%84</a></p>
</blockquote>
<h2 id="로그인">로그인</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/2d89553b-e1d3-437c-b395-32bde2db0e5e/image.png" alt=""></p>
<h3 id="setstate의-특성으로-인해-발생한-에러">setState의 특성으로 인해 발생한 에러</h3>
<pre><code class="language-js">handleInput = e =&gt; {
   this.setState({
     [e.target.name]: e.target.value,
   });
   this.validLoginButton();   
};

validLoginButton = () =&gt; {
    const { inputId, inputPassword } = this.state;
    inputId.includes(&#39;@&#39;) &amp;&amp; inputPassword.length &gt;= 5
      ? this.setState({ isDisabled: false })
      : this.setState({ isDisabled: true });
  };</code></pre>
<p>이번 필수 과제에 있었던 @가 포함되고 password가 다섯글자 이상일때만 로그인버튼이 활성화되게 하는 것을 하려고 위와 같은 함수를 만들어서 실행했는데, 6글자일때 실행이 되는 것이다. 그래서 나는 console.log로 <code>inputId</code> <code>inputPassword</code>를 확인해봤는데, 내가 입력한것이 한박자씩 늦게 배열로 들어오는 것이다.
알고보니 setState의 특성때문에 위와 같은 현상이 발생하는것이었고, 아래의 글을 작성하며 setState의 콜백을 활용해서 문제를 해결했다.</p>
<blockquote>
<p><a href="https://velog.io/@devpark_0x1c/TIL-setState%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EB%AC%B8">https://velog.io/@devpark_0x1c/TIL-setState%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EB%AC%B8</a></p>
</blockquote>
<h2 id="메인">메인</h2>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/c76658a0-af7e-4b9c-9e7e-cd04b3fc1451/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-06-02%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.48.11.png" alt=""></p>
<h3 id="componentdidmount의-활용">componentDidMount의 활용</h3>
<p>Lifecycle에 대해서 velog에 글을 쓸때만 해도 어떻게 프로젝트에 적용하면 될까? 하는 생각을 했었다. 근데 westagram을 통해 &#39;아~ 이렇게 사용하면 되겠구나&#39;하는 정도는 알게 됐다.
나는 Element 자체에 데이터를 넣지 않고 json파일을 fetch하여 모든 데이터를 가져오는 것을 자바스크립트 때부터 해왔는데, 리액트 역시 그렇게 하는것이 좋을것 같아서 자바스크립트와 같이 fetch 후 데이터를 state에 저장하는 방법으로 진행을 했다.
화면이 처음 실행될 때 data를 가지고 있는 json 파일로부터 데이터를 추출하여 state의 옮기는 flow가 <code>componentDidMount</code>가 해주어야할 큰 임무이다.</p>
<pre><code class="language-js">componentDidMount() {
    this.addData();
  }

addData = () =&gt; {
  fetch(&#39;http://localhost:3000/data/userData.json&#39;)
    .then(res =&gt; res.json())
    .then(data =&gt; {
    this.setState({ userData: data });
  });
};</code></pre>
<p>위 작업처럼 <code>addData</code>라는 함수를 만들어서 <strong>fetch</strong>를 진행하고 <code>componentDidMount</code>는 해당 작업을 수행한다.! <code>componentDidMount</code>는 단 컴포넌트가 실행될 때 단 한번만 실행하기 때문에 위처럼 데이터를 가져올 때 유용하게 사용할수 있는 Lifecycle이라고 생각한다.</p>
<h3 id="component의-활용">component의 활용</h3>
<p>리액트를 배우면서 신기하면서도 오? 이렇게도 구성을? 이라고 생각했던게 component였다. 
HTML 템플릿을 만들어놓으면 그것을 재사용할수 있다는 신박한 개념~
코드도 깔끔해지고 내가 어떻게 구성하냐에 따라 내부를 쪼개는 방식을 다양하게 설정할수 있다는것이 초반 설계만 잘 구상한다면 불필요한 HTML 작성 시간을 줄여줄수 있다는것에 큰 장점이 있다.
이번 westagram에서는 3명의 팀원들과 함께 진행을 했는데, 해당 페이지를 같이 만드는것은 아니고 공통 컴포넌트를 하나 만들어서 공유해서 각자 사용해보는것을 목적으로 진행했다.
인스타그램 최상단에 위치한 <strong>nav</strong>를 공통으로 사용했는데, 우리 조는 팀원 선주님의 것을 사용했다. 
공통 작업을 하면서 들었던 생각은 공통 작업물에 대한 내용은 처음에 구조, 클래스명, css를 팀원들끼리 잘 상의하고 만들어야 한다는 것이다. 이번 같은 경우는 기존에 각자 만들어놓은 nav에서 괜찮아보이는것을 하나 골라서 선정한거기때문에 나랑 선주님의 naming convention이 달랐고 나는 element 클래스명을 <strong>kebab-case</strong>로 했는데, 선주님은 <strong>camelCase</strong>로 작성이 되어있어서 아~ 이런것때문에 나중에 프로젝트할때는 팀원들끼리 잘 정해두고 해야하는거구나~ 하는걸 배웠다.</p>
<h2 id="후기">후기</h2>
<p>기존 자바스크립트로 만든     westagram에서 react로 옮기는 과정은 기능적으로 추가된건 더 없고 리액트로 전환하면서 함수 선언, component 활용, Lifecycle에 대한 이해를 할수 있었고, 다른 부족한 부분들은 1차 프로젝트를 진행해야 다양한 시나리오를 접하면서 지금 배운 class component에 대한 이해를 좀 더 깊게 할수 있을것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] setState에 대한 의문]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-setState%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EB%AC%B8</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-setState%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EB%AC%B8</guid>
            <pubDate>Wed, 02 Jun 2021 11:21:47 GMT</pubDate>
            <description><![CDATA[<h1 id="잉">잉!?</h1>
<p>Westagram을 리액트로 변환하는 과정 중에 처음으로 마주한 setState의 비동기화로 인한 나만의 오류???를 발견했다. 
이건 오류라기보다는 setState의 비동기화로 인해 생긴 현상이라고 보면 좋을것 같다.
먼저 아래와 같이 inputValue에 따른 특정 이벤트를 발생시키는 코드를 보자.</p>
<pre><code class="language-js">import React from &#39;react&#39;;

class SetState extends React.Component {
  constructor() {
    super();
    this.state = {
      inputText: &#39;&#39;,
      color: &#39;&#39;,
    };
  }

  validateInput = () =&gt; {
    const { inputText } = this.state;

    inputText.length &gt;= 5 ? this.setState({ color: &#39;blue&#39; }) : this.setState({ color: &#39;red&#39; });
  };

  handleOnChange = (e) =&gt; {
    this.setState({
      [e.target.name]: e.target.value,
    });
    this.validateInput();
  };

  render() {
    return (
      &lt;div&gt;
        &lt;input type=&quot;text&quot; name=&quot;inputText&quot; onChange={this.handleOnChange} /&gt;
        &lt;p style={{ color: this.state.color }}&gt;{this.state.inputText}&lt;/p&gt;
      &lt;/div&gt;
    );
  }
}

export default SetState;
</code></pre>
<p>내가 위처럼 <code>validateInput</code>이라는 함수에서 <code>inputText</code>의 길이가 5이상일 때, p 태그의 글자가 파란색, 그 이하일때는 빨간색이 되게 설정했다. 하지만 실제로 화면으로 돌려보면 내 맘처럼 되지는 않는다. </p>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/2e1b7e51-92d9-4b17-8eff-34c355cae9cc/setState.gif" alt=""></p>
<p>난 분명 글자를 쳤는데, console.log(inputText)는 한박자씩 늦게 말해주고, 지워주는 것 역시 같다. <code>validateInput</code> 역시 이벤트가 늦게 발생한다.</p>
<p><strong>왜 그런것일까?</strong>
리액트 공식문서는 setState의 특성에 대하여 이렇게 말한다. </p>
<blockquote>
<p>setState()는 컴포넌트를 항상 즉각적으로 갱신하지는 않습니다. 오히려 여러 변경 사항과 함께 일괄적으로 갱신하거나, 나중으로 미룰 수도 있습니다. 이로 인하여 setState()를 호출하자마자 this.state에 접근하는 것이 잠재적인 문제가 될 수 있습니다. 그 대신에 componentDidUpdate 또는 setState의 콜백(setState(updater, callback))을 사용하세요. 둘 다 갱신이 적용된 뒤에 실행되는 것이 보장됩니다</p>
</blockquote>
<p>그렇다. 그냥 <code>setState</code>가 즉각적으로 전달받은 state로 값을 바꾸는 것이 아니라 이전의 리액트 엘리먼트 트리와 전달받은 state가 적용된 엘리먼트 트리를 비교하는 작업을 거치고, 최종적으로 변경되는 부분만 DOM에 적용한다.</p>
<p>이 문제를 해결하기 위해선 공식문서에 언급된것 처럼 componentDidUpdate를 활용하거나 setState의 콜백을 사용해야한다.
나는 setState의 콜백을 이용해보았다. </p>
<pre><code class="language-js">handleOnChange = (e) =&gt; {
    this.setState(
      {
        [e.target.name]: e.target.value,
      },
      () =&gt; {
        this.validateInput();
      },
    );
  };</code></pre>
<p>위처럼 setState의 콜백함수로 <code>validateInput</code>을 실행시키니 아래 화면처럼 내가 의도한대로 진행됨을 볼수있다.
<img src="https://images.velog.io/images/devpark_0x1c/post/7fdfe690-e385-43a6-a86a-28d2ab0e09b1/Callback.gif" alt=""></p>
<p>어렵다.! 리액트! setState는 맨 처음 개념인데도 이렇게 햇갈릴줄이야?</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] React Class Lifecycle의 이해 - 일반적인 유형편]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-React-Class-Lifecycle%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-React-Class-Lifecycle%EC%9D%98-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Sun, 30 May 2021 12:22:52 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/devpark_0x1c/post/ccec673b-b2fa-48e3-a25c-f8d4bc7dd664/image.png" alt=""></p>
<h1 id="lifecycle">Lifecycle</h1>
<p>클래스형 컴포넌트를 공부하면서 가장 중요하다고 생각됐고 실제로 중요한것이 바로 이 <strong>Lifecycle</strong>이 아닌가싶다.
<strong>Lifecycle</strong>은 단어 그대로 생명주기를 나타내는데, 클래스형 컴포넌트에는 Mount, Update, Unmount에 따라 사용되는 <strong>Lifecycle</strong> 메서드가 있다. <strong>Lifecycle</strong> 유형이 아래의 것만 있는게 아닌 덜 일반적인 유형이라는 것도 있는데, 내가 실제로 테스트를 통해 이해한 것들만 정리해보았다.</p>
<h1 id="유형">유형</h1>
<h2 id="mount">Mount</h2>
<h3 id="constuctor">Constuctor</h3>
<pre><code class="language-js">constructor(props) {
  super(props);
  this.state = {
    value: &quot;&quot;
  };
  this.handleClick = this.handleClick.bind(this);
}</code></pre>
<p><strong>constructor</strong>는 <code>this.state</code>에 객체를 할당하여 지역 state를 초기화 하거나 인스턴스에 이번트 처리 메서드를 바인딩 한다.</p>
<h3 id="render">render</h3>
<pre><code class="language-js">render() {
   return (
      &lt;div&gt;안녕하세요&lt;/div&gt;
   )
}</code></pre>
<p><strong>render</strong>는 mount시에만 사용되는 메서드는 아니지만 mount시에도 사용되어서 넣어보았다. 
<strong>render</strong>는 클래스 컴포넌트에서 반드시 선언해야하는 메서드이다. 보통  같은 구조를 하고 있고, 리턴에는 우리가 생성할 구조를 JSX 형태로 return 한다.</p>
<h3 id="componentdidmount">componentDidMount</h3>
<p><strong>componentDidMount</strong>는 컴포넌트가 마운트된 직후 render를 거쳐 바로 호출된다. 가장 적절하게 사용 가능한것은 외부에서 데이터를 불러올때가 아닐까 싶다.</p>
<h2 id="update">Update</h2>
<p><strong>update</strong> 시점의 기준은 새로운 props를 받거나 <code>setState</code>를 통해 state의 값이 변경되면 render -&gt; dom update -&gt; componentDidUpdate의 순으로 이루어진다.</p>
<h3 id="componentdidupdate">componentDidUpdate</h3>
<pre><code class="language-js">componentDidUpdate(prevProps) {
  // 전형적인 사용 사례 (props 비교를 잊지 마세요)
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}</code></pre>
<p><strong>componentDidUpdate</strong>는 update가 되는 시점에 호출이 된다고 써있듯이 <strong>componentDidUpdate</strong>에서 state를 조건문으로 감싸지 않고 호출하면 값이 변경될때마다 계<del>~</del>속 호출이되서 무한 반복이 일어날수도 있다.
여기선 이전과 현재의 <code>props</code>를 비교해서 네트워크 요청을 보내는 작업같은것을 하면 좋다.</p>
<h2 id="unmount">Unmount</h2>
<h3 id="componentwillunmount">componentWillUnmount</h3>
<p><strong>componentWillUnmount</strong>는 컴포넌트가 마운트 해제되어 제거되기 직전에 호출된다. 여기서는 clearTimeout, 네트워크 요청 취소, <strong>componentDidMount</strong> 내에서 생성된 구독 해제 등 필요한 모든 정리 작업을 수행하면 된다.
참고로 마운트가 해제되고 나면 절대로 다시 마운트 되지 않는다</p>
<h1 id="직접-코드로-이해해본-lifecycle">직접 코드로 이해해본 Lifecycle</h1>
<pre><code class="language-js">import React from &#39;react&#39;;

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      inputValue: &#39;default&#39;,
    };
    console.log(`Constructor: ${this.state.inputValue}`);
  }

  componentDidMount() {
    console.log(`componentDidMount: ${this.state.inputValue}`);
  }

  componentDidUpdate() {
    console.log(`componentDidUpdate: ${this.state.inputValue}`);
  }

  onChange = (e) =&gt; {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };

  render() {
    console.log(`render: ${this.state.inputValue}`);
    return (
      &lt;div&gt;
        &lt;input
          type=&quot;text&quot;
          name=&quot;inputValue&quot;
          value={this.state.inputValue}
          onChange={this.onChange}
        /&gt;
      &lt;/div&gt;
    );
  }
}

export default App;</code></pre>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/ba335a3b-1050-4bbe-9f75-2c9f9a01c92b/Lifecycle.gif" alt=""></p>
<p>위 움짤의 순서처럼 처음 새로고침을 하고 Mount가 일어나는 시점에 Constructor, render, componentDidMount순으로 호출이 되고, input의 값을 바꾸는 시점에 state값이 변화하면서 update가 일어나 render, componentDidUpdate가 호출된다.
책과 글로봤을땐 너무 막연했는데, 간단한 코드라도 짜면서 해보니까 이해가 되서 역시 줄줄 읽는거보다는 실습이 최고인것 같다.</p>
<h1 id="출처">출처</h1>
<blockquote>
<p><a href="https://ko.reactjs.org/docs/react-component.html#constructor">https://ko.reactjs.org/docs/react-component.html#constructor</a>
<a href="https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/">https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] React의 시작]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-React%EC%9D%98-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-React%EC%9D%98-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Sat, 29 May 2021 10:03:29 GMT</pubDate>
            <description><![CDATA[<p>3주차부터는 본격적인 프로그래밍을 위해 리액트를 배우기 시작했다.</p>
<h2 id="react">React?</h2>
<p>React는 페이스북에서 개발하고 관리하는 <strong>사용자 인터페이스(UI)를 만들기 위한 JavaScript 라이브러리</strong>다. 리액트와 같은 프론트엔드 라이브러리 혹은 프레임워크를 사용하는 가장 큰 이유 중 하나는 UI를 자동으로 업데이트해 준다는 점이다. 리액트는 <strong><code>가상 돔(Virtual Dom)</code></strong> 을 통해 UI를 빠르게 업데이트한다. 가상 돔은 <strong>이전 UI 상태를 메모리에 유지해서, 변경될 UI의 최소 집합을 계산하는 기술</strong>이다. 이 기술 덕분에 불필요한 UI 업데이트는 줄고, 성능은 좋아진다.</p>
<h2 id="component">Component</h2>
<p>리액트는 컴포넌트라는 것이 있는데, 재사용이 가능한 특정 엘리먼트? 단위라고 보면 될것 같다.
앞서 만든 westagram 같은 경우 nav와 feed 같은 것을 컴포넌트 단위로 나누어 재 사용할수가 있겠다.
컴포넌트를 사용하면 메인페이지 같은 경우 한곳에 모든 엘리먼트를 몰아넣는게 아닌
<img src="https://images.velog.io/images/devpark_0x1c/post/13bd0f98-a725-4c3a-b7bc-084df1d79b77/image.png" alt="">
위처럼 commentList, RecommendList, StoryList같이 세분화시켜 특정 부분만 수정할 일이 있을 때 해당 컴포넌트에 들어가서 필요한 부분만 수정하면 되므로 유지보수에도 용이하다.</p>
<h3 id="class--functional">Class &amp; Functional</h3>
<p>Component는 클래스와 함수형으로 나뉘는 데, 각각의 특징이 있다.</p>
<pre><code class="language-js">import React from &quot;react&quot;;

class Test extends from React.Component {
  render() {
    return &lt;div&gt;&lt;/div&gt;;
  }
}</code></pre>
<p>클래스형 컴포넌트는 위와 같이 render 함수가 꼭 있어야하고 return으로는 보여줄 jsx를 반환한다.</p>
<pre><code class="language-js">import React from &quot;react&quot;;

const test = () =&gt; {
  return (
    &lt;div&gt;HI&lt;/div&gt;
  )
}</code></pre>
<p>함수형 컴포넌트는 자바스크립트에서 일반적으로 함수를 선언할 때처럼 선언을 해서 사용한다.</p>
<h2 id="jsx">JSX</h2>
<p>JSX는 Javascript Syntax Extension이고 말그대로 자바스크립트 확장 문법이다.</p>
<h3 id="장점">장점</h3>
<pre><code class="language-js">class Test extends React.Component {
  render() {
    return &lt;div&gt;&lt;/div&gt;;
  }
}</code></pre>
<p>위에서 볼수 있다시피 jsx는 innerHTML이라던지 textContent라던지 직접 주입시켜주는 함수를 사용하는 것이 아닌 엘리먼트를 기존 html처럼 구성하여 리턴하기 때문에 보기 쉽고 익숙하다.</p>
<h3 id="특징">특징</h3>
<pre><code class="language-js">class Test extends React.Component {
  render() {
    return (
      &lt;div&gt;Test&lt;/div&gt;
      &lt;div&gt;1234&lt;/div&gt;
    )
  }
}</code></pre>
<p>위와 같은 jsx를 리턴하면 리액트는 어떤 반응을 할까?? 바로 에러를 내뿜는다.
그 이유는 jsx 요소를 여러개 리턴할 때는 해당 요소를 감싸는 하나의 부모 엘리먼트가 무조건 하나 있어야 한다. 위와 같은 경우를 수정하기 위해서 아래처럼 해주면 된다</p>
<pre><code class="language-js">class Test extends React.Component {
  render() {
    return (
      &lt;div&gt;
        &lt;div&gt;Test&lt;/div&gt;
        &lt;div&gt;1234&lt;/div&gt;
      &lt;/div&gt;
    )
  }
}</code></pre>
<p>하지만 내가 div로 감싸고 싶지 않다면 어떻게 해야할까? 그럴때 제공해주는 것이 있다</p>
<pre><code class="language-js">class Test extends React.Component {
  render() {
    return (
      &lt;&gt;
        &lt;div&gt;Test&lt;/div&gt;
        &lt;div&gt;1234&lt;/div&gt;
      &lt;/&gt;
    )
  }
}</code></pre>
<p>바로 저렇게 빈 태그로 감싸줘도 전혀 문제가 발생하지 않는다. 저것을 Fragments라고 하는데 DOM에 별도의 노드를 추가하지 않고 하나의 컴포넌트 안에 여러 엘리먼트들을 간단하게 그룹화 할수 있는 기능이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LWID] westagram 구현]]></title>
            <link>https://velog.io/@devpark_0x1c/TID-westagram-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@devpark_0x1c/TID-westagram-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Sat, 29 May 2021 09:38:50 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>지난주에 2주차 세션 및 과제로 Instagram을 클론하는 것을 했다.
이건 TID에는 넣을 수 없어서 <strong>L</strong>ast <strong>W</strong>eek <strong>I</strong> <strong>D</strong>id라고 칭하겠다. 너무 어거지인가??? 😛
미션은 총 9가지로 나뉘었는데 아래와 같다.</p>
<ol>
<li>로그인 페이지 레이아웃</li>
<li>id, pw 입력 시 로그인 버튼 활성화 기능</li>
<li>메인 페이지 레이아웃</li>
<li>댓글 내용 입력 후 Enter나 게시 버튼 누르면 댓글이 추가되는 기능
여기까지가 필수 과제</li>
<li>ID와 암호 검증</li>
<li>댓글 좋아요 / 삭제 기능</li>
<li>아이디 검색 기능</li>
<li>nav 프로필 사진 클릭 시 메뉴 박스 생성</li>
<li>반응형 구현
여기까지가 권장 과제이다.</li>
</ol>
<h2 id="과제-구현-내역">과제 구현 내역</h2>
<p>레이아웃은 html과 CS이기 때문에 넘기고 JS 관련된 것만 정리해보도록 하겠다.</p>
<h3 id="id-pw-입력시-로그인-버튼-활성화">id, pw 입력시 로그인 버튼 활성화</h3>
<p>이 과제는 input에 id, pw가 한글자 이상 입력되면 로그인 버튼 색상이 바뀌는 형식의 과제이다.
밑에 ID / 암호 검증 과제를 하면서 기존 내용이 지워졌는데 아래와 코드와 같이 구현을 했었다.</p>
<pre><code class="language-js">const validateInputLength = (name, e) =&gt; {
  name === &#39;id&#39; ? (idValue = e.target.value) : (passwordValue = e.target.value);
  if (idValue &amp;&amp; passwordValue) {
    loginButton.classList.add(&#39;active&#39;);
    loginButton.removeAttribute(&#39;disabled&#39;);
  } else {
    loginButton.classList.remove(&#39;active&#39;);
    loginButton.setAttribute(&#39;disabled&#39;, &#39;disabled&#39;);
  }
};</code></pre>
<p>하나의 함수로 하기 위해 <code>name</code>으로 위 함수를 쓰는 input이 id인지 password인지를 식별하여 함수를 실행하게 된다.</p>
<h3 id="댓글-내용-입력-후-enter나-게시-버튼-누르면-댓글-추가">댓글 내용 입력 후 enter나 게시 버튼 누르면 댓글 추가</h3>
<p>처음 JS를 공부할 때 innerHTML 사용의 지양하는 글들을 워낙 많이봐서 이걸 구현할 때 innerHTML이 아닌 textContent를 주로 사용했다.</p>
<pre><code class="language-js">const addCommentHandler = (e) =&gt; {
  comment = e.target.value;

  if (e.key === &#39;Enter&#39; &amp;&amp; comment) {
    displayComment();
  }
};</code></pre>
<pre><code class="language-js">const displayComment = () =&gt; {
  if (comment) {
    const commentLiTag = document.createElement(&#39;li&#39;);
    const commentId = document.createElement(&#39;span&#39;);
    const commentText = document.createElement(&#39;span&#39;);
    const commentLike = document.createElement(&#39;button&#39;);
    const commentRemove = document.createElement(&#39;button&#39;);

    commentId.textContent = &#39;xoxoxo_S2&#39;;
    commentId.classList.add(&#39;commentor-id&#39;);
    commentText.textContent = comment;
    commentText.classList.add(&#39;comment-content&#39;);
    commentLike.innerHTML = &#39;&lt;i class=&quot;fas fa-heart&quot;&gt;&lt;/i&gt;&#39;;
    commentLike.classList.add(&#39;comment-like-button&#39;);
    commentLike.addEventListener(&#39;click&#39;, (e) =&gt; changeLikeButtonColor(e));
    commentRemove.textContent = &#39;삭제&#39;;
    commentRemove.classList.add(&#39;comment-remove-button&#39;);
    commentRemove.addEventListener(&#39;click&#39;, () =&gt; removeComment(commentLiTag));

    commentLiTag.appendChild(commentId);
    commentLiTag.appendChild(commentText);
    commentLiTag.appendChild(commentLike);
    commentLiTag.appendChild(commentRemove);
    commentList.appendChild(commentLiTag);

    comment = &#39;&#39;;
    inputComment.value = &#39;&#39;;
  }
};</code></pre>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/2e9eda02-fff7-47c2-af7e-f424559a9cc8/image.png" alt="">
fontawesome을 사용하는 부분만 innerHTML로 처리하고 하나하나 textContent와 appendChild를 이용해서 처리했는데, 내용이 너무나도 길어진게 단점이다.
좋아요 버튼과 삭제버튼은 생성되는 시점에 addEventListener를 주어줘서 이벤트가 바인딩되게 처리했다.</p>
<h3 id="id와-암호-검증">ID와 암호 검증</h3>
<p>여기서부터는 추가 구현과제라 위에서 한 과제에 덮어씌우는 경우가 있었다.
여긴 id에 &#39;@&#39; 포함, pw는 5글자 이상이어야 로그인 버튼이 활성화 되는 조건이 있었다.</p>
<pre><code class="language-js">const validateInputLength = (name, e) =&gt; {
  name === &#39;id&#39; ? (idValue = e.target.value) : (passwordValue = e.target.value);
  if (idValue &amp;&amp; idValue.includes(&#39;@&#39;) &amp;&amp; passwordValue.length &gt;= 5) {
    loginButton.classList.add(&#39;active&#39;);
    loginButton.removeAttribute(&#39;disabled&#39;);
  } else {
    loginButton.classList.remove(&#39;active&#39;);
    loginButton.setAttribute(&#39;disabled&#39;, &#39;disabled&#39;);
  }
};</code></pre>
<p>위에서 이미 조건문을 만들어놨기 때문에 ID는 if문안에 <code>includes</code> 메서드를 활용해 @가 포함되었는지 확인하고 PW는 가장 간단한 <code>length</code>를 이용하여 처리를 해주었다.</p>
<h3 id="댓글-좋아요--삭제-기능">댓글 좋아요 / 삭제 기능</h3>
<pre><code class="language-js">const changeLikeButtonColor = (e) =&gt; {
  !e.target.className.includes(&#39;active&#39;)
    ? e.target.classList.add(&#39;active&#39;)
    : e.target.classList.remove(&#39;active&#39;);
};</code></pre>
<p>좋아요는 DB가 없으면 단순히 색상만 바뀌면 되는것이기 때문에, class를 추가하고 삭제하는 toggle의 방식으로 처리를 하였다.</p>
<pre><code class="language-js">// commentRemove.addEventListener(&#39;click&#39;, () =&gt; removeComment(commentLiTag));

const removeComment = (li) =&gt; {
  li.remove();
};</code></pre>
<p>삭제는 댓글이 만들어질 때 위와 같이 해당 댓글의 부모 요소가 무엇인지 알수있게 처리해주었기 때문에 <code>remove</code>를 이용하여 바로 삭제하였다.</p>
<h3 id="아이디-검색-기능">아이디 검색 기능</h3>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/b9160214-95ed-4bb7-84a0-83b1a7fd131e/image.png" alt=""></p>
<pre><code class="language-js">const searchID = (value) =&gt; {
  const inputValue = searchBar.value;
  return value.id.indexOf(inputValue) !== -1;
};</code></pre>
<pre><code class="language-js">searchBar.addEventListener(&#39;input&#39;, () =&gt; {
  searchResult.innerHTML = &#39;&#39;;
  searchResult.style.display = &#39;none&#39;;
  searchResultTriangle.style.display = &#39;none&#39;;
  if (searchBar.value) {
    const filterdId = personalInfo.filter((value) =&gt; searchID(value));
    if (filterdId) {
      filterdId.forEach((value) =&gt; {
        displaySearchResults(value);
      });
    }
  }
});</code></pre>
<p>이번 과제 중 어떻게 할지 가장 고민을 많이 했던 부분이다.
처음엔 <code>indexOf</code>가 떠올라서 그걸 이용해서 계속 고민해보다가 위코드에서 제시해준 <code>filter</code>를 섞어서 구현을 했다.</p>
<h3 id="nav-프로필-사진-클릭-시-메뉴-박스-생성">nav 프로필 사진 클릭 시 메뉴 박스 생성</h3>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/df545f06-af2f-4fe9-8d6c-0f652a8c0aa4/image.png" alt="">
이건 구현하는 것 자체는 어렵지 않았는데, 여기서 세부적인 미션으로는 저 영역외를 클릭했을 때 메뉴가 사라지는걸 구현했어야했는데, 도무지 생각이 안나는것이다. 처음엔 target의 parentNode를 클릭하면 사라지는 것 까지만 하고 어떻게할까 어떻게할까 하다가 멘토님한테 힌트찬스를 써서 outer click이라는 키워드를 얻고 그에 해당하는 이벤트를 구현하였다.</p>
<pre><code class="language-js">const displayPersonalMenu = (e) =&gt; {
  const personalMenu = document.querySelector(&#39;.personal-menu&#39;);
  const personalMenuTriangle = document.querySelector(&#39;.personal-menu-triangle&#39;);

  if (e.target.className !== &#39;nav-button-profile&#39;) {
    personalMenuTriangle.style.display = &#39;none&#39;;
    personalMenu.style.display = &#39;none&#39;;
  } else {
    personalMenuTriangle.style.display = &#39;block&#39;;
    personalMenu.style.display = &#39;flex&#39;;
  }
};</code></pre>
<pre><code class="language-js">body.addEventListener(&#39;click&#39;, displayPersonalMenu);</code></pre>
<p>위처럼 body에 이벤트를 주어 프로필 버튼을 클릭하면 개인 메뉴가 나타나고 그 외에는 사라지게 구현을 하였다.</p>
<h3 id="반응형-구현">반응형 구현</h3>
<p>반응형은 레이아웃과 마찬가지로 html, css 위주라 따로 작성은 하지 않겠다.
근데 처음에 이것을 구현할 때 viewport 단위를 사용해보고 싶어서 vmin, vmax를 사용했는데, 내가 개념을 잘못이해하고 있어서 브라우저 크기가 커질때 해당 단위를 쓴 엘리먼트들이 너무 커진것이다..
<img src="https://images.velog.io/images/devpark_0x1c/post/68e39cdc-acbb-40f9-a6b0-411586af2ae2/image.png" alt="">
..나의 인스타그램 상태가..?</p>
<h2 id="소감">소감</h2>
<p>그래도 자바스크립트를 어느정도 공부했다고 실전 2주차 과제인데도 불구하고 위코드에서 5일동안 과제 기간을 줬지만 하루만에 다해서 나머지 공부할 시간이 좀 많았던 한주였었다.
3주차부턴 리액트가 들어갔고 그에 대해 또 글을 올려야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[뒤늦은 위코드 사전스터디 후기]]></title>
            <link>https://velog.io/@devpark_0x1c/%EB%92%A4%EB%8A%A6%EC%9D%80-%EC%9C%84%EC%BD%94%EB%93%9C-%EC%82%AC%EC%A0%84%EC%8A%A4%ED%84%B0%EB%94%94-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@devpark_0x1c/%EB%92%A4%EB%8A%A6%EC%9D%80-%EC%9C%84%EC%BD%94%EB%93%9C-%EC%82%AC%EC%A0%84%EC%8A%A4%ED%84%B0%EB%94%94-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sun, 23 May 2021 13:15:38 GMT</pubDate>
            <description><![CDATA[<h2 id="시작">시작</h2>
<p>위코드에 들어가기 정확히 6주전에 사전스터디가 시작됐다. 오프라인이 아닌 온라인에서 난생 처음 보는 사람들과 줌에서의 첫 만남 그리고 30명 넘는 사람들이 한 조가 될순 없어서 6개로 조가 나뉘어지며 공부를 하게 되었다. 우리 조는 6조! 그리고 인원은 7명으로 시작했다.</p>
<h2 id="1주차---html-css">1주차 - HTML, CSS</h2>
<p>1주차는 HTML과 CSS를 배우는것 과정으로 이루어졌다. 생활코딩 혹은 각자의 책으로 공부를 했는데, 나 같은 경우는 강의를 듣기보단 poiemaweb으로 공부를 했다. 생활코딩도 기초를 쌓기엔 좋지만 처음부터 강의를 듣는것보단 웹사이트로 공부를 시작하고 개인적으로 부족하다고 생각되는 부분을 강의로 채우는게 시간적으로 효율적이지 않을까라는 생각에 위와 같은 방법으로 한것이고 실제로 <strong>poiemaweb</strong>으로 부족한 것을 느끼지 못해서 다시 돌아간다해도 위처럼 할 것 같다.</p>
<blockquote>
<p><a href="https://poiemaweb.com/">poiemaweb link</a></p>
</blockquote>
<h3 id="과제">과제</h3>
<p>위코드에선 각 주차마다 간단한 과제를 내줬다. 첫 주차는 HTML과 CSS를 이용하여 자기소개 페이지 만들기를 하는 것.
이 과제를 했을 때 제일 힘들었던 것은 HTML, CSS가 아니라 디자인이었다. 나를 소개하는 것 자체는 문제가 없는 일인데, 웹페이지 구성 때문에 시간을 다 잡아먹었었다. 최종적으로 내가 CSS를 하면서 어려웠던 부분을 써보는 목적으로 만들어보자 라는 결론을 내리며 나의 첫번째 웹사이트를 만들었다. 이 과제는 2주차까지 이어지기 때문에 어떻게 했는지는 2주차 항목에 링크를 올려보겠다.</p>
<h2 id="2주차---javascript">2주차 - Javascript</h2>
<p>2주차는 대망의 JS이다. 자바스크립트는 뭐 난생 처음하는 것이기 때문에 강의를 듣는게 좋다고 생각했는데, 위코드에서는 여러가지 동영상 세션을 제공해줬다. 그게 뭐인지는 여기다가 명시하면 안될거 같아서 따로 적진 않겠지만, 2주차는 Poiemaweb과 동영상 강의를 병행하면서 공부를 이어나갔다.</p>
<h3 id="과제-1">과제</h3>
<p>과제는 자바스크립트에 대한 벨로그 정리 그리고 1주차에 만들었던 자기소개 페이지에 JS를 추가하여 좀 더 업그레이드? 시키는 과제가 주어졌다.</p>
<blockquote>
<p><a href="https://velog.io/@devpark_0x1c/HTML-CSS-%EC%9E%90%EA%B8%B0%EC%86%8C%EA%B0%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-2%E5%AE%8C">https://velog.io/@devpark_0x1c/HTML-CSS-%EC%9E%90%EA%B8%B0%EC%86%8C%EA%B0%9C-%ED%8E%98%EC%9D%B4%EC%A7%80-2%E5%AE%8C</a>  </p>
</blockquote>
<p>나는 위처럼 일단 틀을 만들었고, 거기에 높이에 따른 이미 변경 요소를 추가하는 정도로 자기소개 페이지를 마무리 지었다.  </p>
<h3 id="사전스터디-조모임">사전스터디 조모임</h3>
<p>우리 사전스터디조는 일요일과 수요일에 각자 어떤 공부를 했는지 공유를 했다. 역시나 미리 공부해왔던 분이 두분 계셨고, 한분은 아직 프론트를 할지 백엔드를 할지 미정이어서 공부의 방향을 확실히 잡기전에 분야부터 정하는걸 고민하고 계셨다. 지금은 이름을 다 아는데 굳이 왜 저렇게 지칭했는지 모르겠네ㅎㅎ 여튼 위에 상훈, 진수, 승리님을 제외하고 나머지 4명이 문제였는데, 그 중에 내가 포함되어있다. 
개인적으로 사전 스터디를 좀 알차게 활용하고 싶었던 나였는데, 온라인에서만 만나니까 개인적으로 오프라인에서 얘기좀 하면서 같이 공부하자고 DM 보내기도 좀 민망했는데 마침 우리 조가 3주차 화요일에 오프라인으로 한번 모이기로 정해졌다. </p>
<h2 id="3주차">3주차</h2>
<p>3주차는 프론트엔드와 백엔드로 나뉘는 데, 프론트엔드는 계속 자바스크립트 공부를 하고 백엔드를 지망한 사람은 파이썬을 시작한다. 위에서 본인의 분야를 고민하던 승리님은 결국 백엔드를 지망하며 프론트엔드와 작별하였다. 
3주차 역시 방대한 양의 자바스크립트를 책과 동영상만 보고 이해하기는 무리이고 실습을 통해 이해해야하지 않을까 하는 생각이 내 머릿속에 가득찼다. 이때부터 난 자바스크립트 method를 공부할 때마다 관련된 아이디어를 짜보거나 레딧 혹은 유튜브에 있는 미니 프로젝트들을 찾아보고 스크랩하기 시작했다. </p>
<h3 id="과제-2">과제</h3>
<p>이번주는 무엇을 만들거나 하는 과제는 아니고 velog에 자바스크립트 공부한 내용을 정리하는 것으로 마무리지었다.
나는 Hoisting, Datatype, this, prototype, closure에 대해서 공부했는데, 처음엔 진짜 이게 뭔 소리야 했다. 지금은 저 내용들을 보면 그때보단 이해가 잘되지만 보고 보고 또 봐야하는 개념들이라고 생각한다.</p>
<h3 id="사전스터디-조모임-1">사전스터디 조모임</h3>
<p>우린 드디어 오프라인모임을 했고 우연찮게도 먼저 온 사람이 공부가 안되어있는 문제의 네사람(나, 미영, 유정, 오현)이었다.ㅎㅎㅎ
나머지 사람들도 모였는데 코로나 때문에 따로 앉아야해서 뒤에 온 세명은 다른곳에 앉아서 얘기를 나눴다. 어휴 참 코로나때문에 뭐 모이지도 못하고 정말 힘들다..
나는 각자의 실력이 궁금해서 대충 실력을 떠봤고, 역시나 어느정도 되어있는 사람과는 너무 차이가 나서 우리 네사람끼리 뭔가 해야 조금이라도 따라갈수 있겠다는 생각이 들어서 나는 세사람에게 제안을 했다. 우리끼리 따로 뭔가를 만들면서 공부를 하자고.. 저 얘기를 하기까지 정말 난 엄청난 용기를 가지고 얘기한 것 같다. 왜냐면 이건 다른 사람 입장에서는 &#39;나 혼자 하고싶은데 왜 오바함?&#39; 이라고 생각할수도 있으니까..? 뭐 저런 생각을 가진다고 해도 이해할 수 있다. 여튼 내 의견을 세 분에게 전달했고 세분도 같이 하면 좋을거 같다하여 바로 다음날부터 <strong>조 안의 조</strong> 모임을 시작했다.</p>
<h3 id="조안의-조모임">조안의 조모임</h3>
<p>조안의 조모임(<strong>이하 조안조</strong>)은 사전스터디 조 모임이 끝나고 난 뒤 바로 구글밋을 시작했고, 따로 필요할 경우 추가로 회의를 여는것으로 정했다.
조안조의 과제는 전부 내가 예전에 스크랩 해놨던 자잘한 과제들을 하는걸로 했다.</p>
<h4 id="background-changer">background Changer</h4>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/dddf9105-696b-4ab2-9cac-7f989c732afe/image.png" alt="">
<img src="https://images.velog.io/images/devpark_0x1c/post/284897b5-be87-456c-b1f2-23abdeccbbb4/image.png" alt="">
처음은 다들 많이 하는 버튼을 눌렀을 때 배경화면 색상이 바뀌는 <strong>Background Changer</strong>를 했다.</p>
<pre><code class="language-js">
const body = document.body,
  hexColor = document.querySelector(&#39;.hexColor&#39;),
  clickBtn = document.querySelector(&#39;.clickBtn&#39;);

function codeGenerator() {
  const hexNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, &#39;A&#39;, &#39;B&#39;, &#39;C&#39;, &#39;D&#39;, &#39;E&#39;, &#39;F&#39;];
  let result = &#39;#&#39;;

  for (let i = 0; i &lt; 6; i++) {
    const randomIndex = Math.floor(Math.random() * hexNumbers.length);
    result += hexNumbers[randomIndex];
  }
  hexColor.innerHTML = &#39;&lt;h1&gt;Hex Color : &#39; + result + &#39;&lt;/h1&gt;&#39;;
  body.style.backgroundColor = result;
}

clickBtn.addEventListener(&#39;click&#39;, codeGenerator);

function init() {
  hexColor.innerHTML = &#39;&lt;h1&gt;Hex Color : #ffffff&lt;/h1&gt;&#39;;
}

init();</code></pre>
<ul>
<li>이 과제를 통해 처음으로 배열 순회를 해볼수 있었다.</li>
<li>어떤곳에서 보니 innerHTML을 성능성 좋지 않고 보안상의 이유(XSS Attack)로 인하여 지양하는 게 좋다 했었는데 대체할 만한게 뭐가 있을까? textContent??</li>
</ul>
<h4 id="carousel">Carousel</h4>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/5da7fe82-1bea-49a4-98c9-500ef7befd40/image.png" alt="">
<img src="https://images.velog.io/images/devpark_0x1c/post/1729c461-8046-455f-8b4b-dd6e6c05f3de/image.png" alt=""></p>
<pre><code class="language-js">const previous = document.querySelector(&#39;.prev_page&#39;);
const next = document.querySelector(&#39;.next_page&#39;);
const btn = document.querySelectorAll(&#39;button&#39;);
const image = document.querySelector(&#39;img&#39;);

let num = 1;
const images = [
  &#39;./images/1.jpg&#39;,
  &#39;./images/2.webp&#39;,
  &#39;./images/3.jpeg&#39;,
  &#39;./images/4.jpg&#39;,
  &#39;./images/5.jpg&#39;,
];

previous.addEventListener(&#39;click&#39;, function () {
  if (num &gt; 1 &amp;&amp; num &lt; 6) {
    num -= 1;
  } else if (num == 1) {
    num = 5;
  }
  image.src = images[num - 1];
});

next.addEventListener(&#39;click&#39;, function () {
  if (num &gt; 0 &amp;&amp; num &lt; 5) {
    num += 1;
  } else if (num == 5) {
    num = 1;
  }
  image.src = images[num - 1];
});

for (let i = 0; i &lt; 5; i++) {
  btn[i].addEventListener(&#39;click&#39;, function (e) {
    let value = e.target.value;
    image.src = images[value];
  });
}

function init() {
  image.src = images[0];
}

init();</code></pre>
<ul>
<li>Carousel은 조건문을 다양하게 사용해 볼수 있어서 괜찮았던것 같다.</li>
<li>위 과제를 하면서 button 하나하나에 for문을 이용하여 click 이벤트를 넣어줄수 있다는걸 알게되었다.</li>
</ul>
<h2 id="4주차">4주차</h2>
<p>4주차도 역시 자바스크립트를 계속 공부하는 일정이었다. 범위가 워낙 넓고 모르는 키워드도 많아서 공부할게 아주 산더미였다.</p>
<h3 id="과제-3">과제</h3>
<p>이번에도 자기가 공부한 것에 대해 글을 쓰는걸 과제로 받았다. 4주차에는 ES6에서 추가된 개념과 자료구조에 대해 조금씩 공부를 시작했다.
자료구조는 나에게 또다른 벽으로 다가왔다.</p>
<h3 id="사전스터디-조모임-2">사전스터디 조모임</h3>
<p>조안의 조인 우리를 제외하고 사람들은 각자 자신의 수준에 맞는 공부를 하고 있었다. 승리님은 파이썬을 하느라 정신이 없었고, 상훈님은 자바스크립트를 좀 더 심층적으로 공부하고자 이론쪽으로 좀 더 힘을 쓰고 있었다. 마지막으로 시각화에 관심이 많은 진수님은 canvas를 이용한 미니 프로젝트를 우리에게 보여줬는데, 음...내가 다가가기엔 아직 너무나도 먼 곳처럼 느껴진 멋진 프로젝트였다.ㅎㅎ 좌표값을 이용한 프로젝트였는데.. 일단은 UI를 보는것으로 만족..
위코드의 공식 사전스터디 일정은 이것으로 마무리되었다.</p>
<h3 id="조안의-조모임-1">조안의 조모임</h3>
<p>우리는 4주차에 오프라인으로 모여서 한번 그동안 했던걸 서로 질문도 좀 하고, 4주차에 진행할 과제들을 했다.</p>
<h4 id="baseball">baseball</h4>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/b9d85176-1c38-4006-bb7b-24602c0514e6/image.png" alt=""></p>
<ul>
<li>인터넷에서 꽤나 유명한 숫자야구를 만들어보았다.</li>
<li>숫자야구 규칙이 간단해보이지만 꽤나 까다로워서 구현하는데 애를 먹었던것 같다.</li>
<li>for 문안의 for문은 처음 구현할 때 너무 애먹었는데, 지금 시점에서는 이 때 써봐서 다행이다 싶을정도로 이해가 잘된다.</li>
</ul>
<h4 id="array-practice">Array Practice</h4>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/293d095a-d37b-4267-bf3d-42b6d1bfebfa/image.png" alt="">
<img src="https://images.velog.io/images/devpark_0x1c/post/d4900ae6-b08a-4986-b9d6-f49a04110737/image.png" alt=""></p>
<ul>
<li>Array 관련 method를 연습하기 위해서 시작한 프로젝트여서 아래와 같은 method를 사용해봤다.</li>
<li>push, pop, sort, reduce</li>
</ul>
<h2 id="5주차-번외">5주차 (번외)</h2>
<h3 id="조안의-조모임-2">조안의 조모임</h3>
<p>사전스터디의 일정은 끝났지만 우리는 마지막으로 과제를 하나만 더 해보기로 했다.</p>
<h4 id="expense-tracker">Expense Tracker</h4>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/d0539c45-b2cc-4ed3-8280-61b6c610c023/image.png" alt="">
<img src="https://images.velog.io/images/devpark_0x1c/post/202ac291-07ab-4609-93b6-6652b68af3a4/image.png" alt=""></p>
<ul>
<li>이번 과제는 Localstorage를 한번 활용해보기 위한 과제였다.</li>
<li>데이터베이스를 직접적으로 다룰 수준이 아니기에 Localstorage를 통해 데이터베이스에 어떤 식으로 저장이 될지 간접적으로나마 알수 있었다.</li>
</ul>
<h3 id="개인-과제">개인 과제</h3>
<h4 id="voice-up-and-down-game">Voice Up and Down game</h4>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/f97334eb-ee9e-47f4-8305-a60b722b9651/image.png" alt=""></p>
<h4 id="todo">Todo</h4>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/d8d7fec2-32ad-46bc-bf65-f426b7d7bf65/image.png" alt=""></p>
<h4 id="movie-booking">Movie Booking</h4>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/1248a76b-2472-4010-86d2-acc3469c41a5/image.png" alt=""></p>
<ul>
<li>조안의 조 과제외에도 위코드 들어가기전에 남는 시간이 좀 아까워서 초보자들이 하기 좋은 미니프로젝트들을 해보았다.</li>
</ul>
<h2 id="소감">소감</h2>
<p>위코드에 들어가기 전 사전스터디를 4주정도하고 1주 쉰뒤에 들어간다길래 엄~청 오래 기다리다가 들어가겠구나 했었는데, 시간이 정말 미친듯이 빨리 흘러갔다. 그만큼 난 미친듯이 열심히했고, 전혀 후회가 없는 4월 한달이었다. 하지만 계획없는 사전스터디 기간이었으면 시간이 이처럼 빨리 흘러가진 않았을 것이라고 생각된다.
위코드를 시작하면 잘하는 사람들의 발끝이라도 따라가기 위해 조안의 조모임을 계획한 것인데, 현재 시점(2주차)에 같이 공부한 사람들을 보면 의미없는 기간은 아니었구나라는 생각이 든다.
같이 공부한 사람들에게 너무 부담을 줄까봐 말할때마다 엄청 조심했지만 그것마저 그들에게 부담이 됐을까봐 걱정이 된건 있었다. 그래도 사전스터디를 통해 처음보는 나의 계획에 잘 따라와준 미영, 유정, 오현에게 감사하며 이 글을 마무리 짓는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL 25][JS] ES6 - 5]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-25JS-ES6-5</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-25JS-ES6-5</guid>
            <pubDate>Sun, 02 May 2021 01:06:55 GMT</pubDate>
            <description><![CDATA[<h1 id="symbol">Symbol</h1>
<ul>
<li>symbol은 ES6에서 새롭게 추가된 타입이고 변경 불가능한 primitive 타입이다. </li>
<li>symbol은 주로 이름의 충돌 위험이 없는 unique한 객체의 프로퍼티 키를 만들기 위해 사용한다.</li>
</ul>
<h2 id="특징">특징</h2>
<ul>
<li>Symbol은 값을 생성하여 리턴한다.</li>
<li>Symbol은 리턴된 값을 볼수가 없다.<pre><code class="language-js">const symbol = Symbol();
console.log(sym) // Symbol();
console.log(typeof sym) // symbol</code></pre>
</li>
<li>프로그램 전체의 유일한 값을 제공한다.<pre><code class="language-js">const one = Symbol();
const two = Symbol();
console.log(one === two) // false</code></pre>
</li>
<li>Symbol 값으로 연산 불가<pre><code class="language-js">let sym = Symbol();
try {
const add = sym + 5;
} catch {
console.log(&quot;연산 불가);
}
// 위 경우는 연산 불가 메세지가 노출되는데, 이유는 sym값이 노출될수 있어서 symbol이 sym의 값을 주지 않아 연산이 되지 않기 떄문이다.</code></pre>
</li>
</ul>
<h2 id="사용">사용</h2>
<ul>
<li>Object의 프로퍼티 키로 사용 (symbol-keyed property)<ul>
<li>Symbol 값이 유일하므로 중복되지 않아서<pre><code class="language-js">const sym = Symbol(&quot;설명&quot;);
const obj = {[sym]: 100}
console.log(obj[sym]); // 100
console.log(obj.sym);  // undefined</code></pre>
</li>
</ul>
</li>
<li>Object의 함수 이름으로 사용<pre><code class="language-js">const sym = Symbol(&quot;함수 이름&quot;);
const obj = {
[sym](param){
  return param;
}
};
console.log(obj[sym](200)); // 200</code></pre>
</li>
<li>for in 문에서의 사용<pre><code class="language-js">const obj = {</code></pre>
</li>
</ul>
<p>  two: 200
}
for (let key in obj){
  console.log(key) // two
}
// Symbol은 Enumerable이 false이기 때문에 열거 되지 않아서 two만 출력된다.</p>
<pre><code>- for of 문에서 사용
```js
const list = [Symbol(&quot;100&quot;)];
for (let value of list){
  console.log(value) // Symbol(100)
}</code></pre><ul>
<li>JSON.stringify()에서의 사용<pre><code class="language-js">const sym = Symbol(&quot;JSON&quot;);
const result = JSON.stringify([sym]: &quot;ABC&quot;);
console.log(result); // {}
// Symbol 값은 외부에 노출되지 않기 위해 stringify로도 변환되지 않아서 빈값이 출력된다.</code></pre>
</li>
</ul>
<h2 id="symbol-property">Symbol Property</h2>
<h3 id="symboltostringtag">Symbol.toStringTag</h3>
<ul>
<li>Object.prototype.toString()의 확장(오버라이딩)</li>
<li>기존 Object.toString()은 인스턴스 타입을 명확하게 구분할 수가 없었다<pre><code class="language-js">const Book = function(){};
const obj = new Book();
console.log(obj.toString()); // [object Object]
console.log({}.toString());  // [object Object]
// 위 처럼 결과값의 차이를 구분지을 수 없음</code></pre>
</li>
<li>prototype에 연결하여 작성한다<pre><code class="language-js">const Book = function(){};
const obj = new Book();
console.log(obj.toString(); // [object Object]
Book.prototype[Symbol.toStringTag] = &quot;해리포터&quot;;
console.log(obj.toString(); // [object 해리포터]
// Book의 prototype에 Symbol.toStringTag를 오버라이드하면 tag한 값이 나타난다</code></pre>
<h3 id="symbolisconcatspreadable">Symbol.isConcatSpreadable</h3>
</li>
<li>Array.prototype.concat()은 배열의 엘리먼트를 전개하여 리턴한다.</li>
<li>여기에 Symbol.isConcatSpreadable의 boolean값을 변경시켜줌에 따라 전개를 결정할 수 있다.<pre><code class="language-js">const one = [10, 20], two = [&quot;A&quot;, &quot;B&quot;];
const show = () =&gt; {
console.log(one.concat(two));
};
show(); // [10, 20, A, B]
two[Symbol.isConcatSpreadable] = true;
show(); // [10, 20, A, B]
two[Symbol.isConcatSpreadable] = false;
show(); // [10, 20, [A, B]]</code></pre>
</li>
<li>boolean 값을 true로 하면 기존처럼 전개가 된다.</li>
<li>boolean 값을 false로 하면 배열 자체를 연결한다.</li>
</ul>
<h3 id="symbolspecies">Symbol.species</h3>
<ul>
<li>Symbol.species는 constructor를 리턴한다.(=인스턴스를 리턴한다)</li>
<li>Symbol.species를 오버라이드하면 다른 인스턴스를 리턴할 수 있다.</li>
<li>Symbol.species는 static의 프로퍼티이며 getter는 있지만 setter는 없다</li>
<li>아래처럼 빌트인 오브젝트를 상속받은 class에 Symbol.species를 작성하면 빌트인 오프젝트의 <code>@@species</code>가 오버라이드 된다.<pre><code class="language-js">class Sports extends Array {
static get [Symbol.species](){
  return Array;
}
};
const obj = new Sports(10, 20);</code></pre>
</li>
<li>Symbol.species는 Array, Map, Set, RegExp, Promise, ArrayBuffer, TypedArray만 사용할 수 있다.</li>
</ul>
<h3 id="symboltoprimitive">Symbol.toPrimitive</h3>
<ul>
<li>오브젝트를 대응하는 Primitive 값으로 변환</li>
<li>오브젝트를 문자열에 대응</li>
<li><em>기존의 사용방법*</em><pre><code class="language-js">const book = {
 toString() {
   return &quot;책&quot;;
 }
};
console.log(`${book}) //책</code></pre>
</li>
<li>오브젝트를 숫자에 대응<pre><code class="language-js">const book = {
valueOf() {
  return 30;
}
}
console.log(book * 20) // 600</code></pre>
</li>
<li><em>Symbol.toPrimitive의 사용*</em><pre><code class="language-js">const obj = {
 [Symbol.toPrimitive](hint){
   return hint === &quot;number&quot; ?30;
      hint === &quot;string&quot; ? &quot;책&quot; : &quot;default&quot;;
 }
};
console.log(20 * obj)          // 600
console.log(`${obj}` + 100)    // 책100 (`` 템플릿리터럴로 인해)
console.log(obj + 50)          // default50
console.log(&quot;default&quot; == obj) // true</code></pre>
<h3 id="symboliterator">Symbol.iterator</h3>
<h4 id="array">Array</h4>
</li>
<li>Array 오브젝트에서 Symbol.iterator()를 호출하면 iterator 오브젝트를 반환한다.<pre><code class="language-js">const list = [10, 20];
const obj = list[Symbol.iterator]();
obj.next() // {value: 10, done: false}
obj.next() // {value: 20, done: false}
obj.next() // {value: undefined, done: true}</code></pre>
<h4 id="string">String</h4>
</li>
<li>String 오브젝트 역시 Symbol.iterator()를 호출하면 iterator 오브젝트를 반환한다.<pre><code class="language-js">const list &quot;AB&quot;;
const obj = list[Symbol.iterator]();
obj.next() // {value: A, done: false}
obj.next() // {value: B, done: false}
obj.next() // {value: undefined, done: true}</code></pre>
<h4 id="object">Object</h4>
</li>
<li>빌트인 Object에는 Symbol.iterator가 없어서 Symbol.iterator를 직접 내부에 작성해주면 사용이 가능하다.<pre><code class="language-js">const obj = {
[Symbol.iterator](){
  return {
    count: 0,
    maxCount: this.maxCount,
    next(){
      if (this.count &lt; this.maxCount){
        return {value: this.count++, done: false}
      };
      return {value: undefined, done: true}
    }
  };
}
};
obj.maxCount = 2;
for (const value of obj){
console.log(value); // 0, 1
};</code></pre>
</li>
</ul>
<h2 id="symbol-함수">Symbol 함수</h2>
<h3 id="symbolfor">Symbol.for()</h3>
<pre><code class="language-js">const one = Symbol.for(&quot;sports&quot;)
console.log(one) // Symbol(sports)</code></pre>
<ul>
<li>글로벌 Symbol 레지스트리(Object)에 {key: value} 형태로 Symbol을 저장한다.</li>
<li>파라미터 ()의 문자열이 key가 되고, Symbol()로 생성한 값이 value가 된다.<pre><code class="language-js">const one = Symbol.for(&quot;sports&quot;);
const two = Symbol.for(&quot;sports&quot;); // one이 같은 key를 등록했으므로 two가 등록하지 않음
console.log(one === two); // true</code></pre>
</li>
<li>위처럼 같은 key가 존재하면 기존에 등록된 값을 사용한다.</li>
</ul>
<h3 id="symbolkeyfor">Symbol.keyFor()</h3>
<pre><code class="language-js">const one = Symbol.for(&quot;book&quot;);
const six = Symbol.keyFor(one);
console.log(six) // book</code></pre>
<ul>
<li>글로벌 Symbol 레지스트리에서 Symbol의 key 값을 구해온다.</li>
<li>key 값이 존재하면 key 값을 리턴하고 존재하지 않으면 undefined를 리턴한다.</li>
</ul>
<h3 id="symbolprototypetostring">Symbol.prototype.toString()</h3>
<pre><code class="language-js">console.log(Symbol(&quot;100&quot;).toString(); // Symbol(100)
const sym = Symbol.for(&quot;book&quot;);
console.log(sym.toString());          // Symbol(book)
try {
  console.log(Symbol() + &quot;ABC&quot;);
} catch {
  console.log(&quot;+로 연결 불가&quot;);          // +로 연결 불가
};</code></pre>
<ul>
<li>Symbol을 생성했던 형태를 문자열로 변환하여 리턴(Symbol 값은 리턴하지 않음)</li>
<li>+로 문자열을 연결하면 TypeError가 난다. toString()으로 변환하면 연결은 되지만 Symbol값은 연결되지 않는다.</li>
</ul>
<h3 id="symbolprototypevalueof">Symbol.prototype.valueOf()</h3>
<pre><code class="language-js">console.log(Symbol(&quot;100&quot;).valueOf());     // Symbol(100)
console.log(Symbol.for(&quot;200&quot;).valueOf()); // Symbol(200)</code></pre>
<ul>
<li>기존 valueOf()가 프리미티브 값을 반환하지만 Symbol은 값을 반환하지 않고 Symbol을 생성한 형태를 리턴한다.</li>
<li>Symbol.for()는 for를 제외하고 리턴한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL 24][JS] ES6 - 4]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-24JS-ES6-3</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-24JS-ES6-3</guid>
            <pubDate>Fri, 30 Apr 2021 13:30:02 GMT</pubDate>
            <description><![CDATA[<h1 id="generator">generator</h1>
<ul>
<li>generator는 <code>function*</code> 키워드를 사용한 함수이다.</li>
</ul>
<h1 id="형태">형태</h1>
<h2 id="function-선언문">function* 선언문</h2>
<ul>
<li>function* 다음에 함수 이름 작성<pre><code class="language-js">function* sports(one, two){
yield one + two;
}
console.log(typeof sports); // generator 함수 타입은 function
const obj = sports(1, 2);
console.log(typeof obj); // object
console.log(obj.next()); // iterator 오브젝트여서 next()가 가능</code></pre>
</li>
<li>generator 함수를 호출하면 함수 블록 {} 을 실행하지 않고, generator 오브젝트를 생성하여 반환한다</li>
<li>generator 오브젝트는 iterator 오브젝트이다.</li>
</ul>
<h2 id="function-표현식">function* 표현식</h2>
<ul>
<li>function* 다음에 함수 이름 작성은 선택, 일반적으로 함수 이름을 작성하지는 않는다.</li>
<li>function* 왼쪽에 변수를 선언하며 변수 이름이 함수 이름이 된다.</li>
<li>함수를 선언하는 형태만 다르고 function* 선언문과 같다.<pre><code class="language-js">const sports = function* (one){
yield one;
}
const obj = sports(100);
console.log(obj.next()); {value: 100, done: false}</code></pre>
</li>
</ul>
<h2 id="generatorfunction">generatorFunction</h2>
<ul>
<li>GeneratorFunction.constructor을 사용하여 제네레이터 함수를 생성</li>
<li>파라미터를 문자열로 작성, 마지막 파라미터가 함수 코드가 되고 앞은 파라미터 이름이 된다.<pre><code class="language-js">const fn = new Function(&quot;one&quot;, &quot;return one&quot;);
console.log(fn(100)) // 100
</code></pre>
</li>
</ul>
<p>// new 연산자를 사용하기 위해 constructor 할당
const create = Object.getPrototypeOf(function*(){}).constructor;</p>
<p>// 마지막 파라미터는 함수 코드가 된다
const sports = new create(&quot;one&quot;, &quot;yield one&quot;);
const obj = sports(100);
console.log(obj.next()) // {value: 100, done: false}</p>
<pre><code># 메서드
## return()
- generatorObject.return()
- return()의 파라미터 값을 리턴한다.
- iterator를 종료시킨다.
```js
function* sports(count){
  while(true){
    yield ++count;
  }
}
const obj = sports(10);
console.log(obj.next()) {value: 11, done: false}
console.log(obj.return(70)) // 이터레이터 종료 -&gt; 파라미터 값 70을 리턴
console.log(obj.next(50)) // 이터레이터가 종료되어서 {value: undefined, done: true}</code></pre><ul>
<li>return()의 파라미터 값을 {value: 값, done: true}에서 value 프로퍼티 값으로 설정</li>
</ul>
<h2 id="throw">throw()</h2>
<ul>
<li>generatorObject.throw()</li>
<li>{value: error message, done: true}를 리턴한다.</li>
<li>Error를 의도적으로 발생시킨다.</li>
<li>generator 함수의 catch()문에서 에러를 받는다.<pre><code class="language-js">function* sports(){
try {
  yield 10;
} catch (message){
  yield message;
};
yield 20;
}
const obj = sports();
console.log(obj.next()); // {value: 10, done: false}
console.log(obj.throw(&quot;error&quot;)); // {value: &quot;error&quot;, done: false}
console.log(obj.next()); // {value: 20, done: false}</code></pre>
</li>
<li>error가 발생했지만 iterator는 종료되지 않는다.</li>
</ul>
<hr>
<h1 id="yield">yield</h1>
<pre><code class="language-js">function* sports(one){
  yield one + 10;
  yield;
  const value = yield one + 50;
}
const obj = sports(30);
console.log(obj.next()); // {value: 40, done: false}
console.log(obj.next()); // {value: undefined, done: false}
console.log(obj.next()); // {value: 80, done: false}
console.log(obj.next(200)); // {value: undefined, done: true}</code></pre>
<ul>
<li>yield는 next()로 호출될 때마다 하나씩 실행한다. (위 코드처럼) 전체가 실행되지 않고 next() 하나가 호출될때마다 리턴된다.</li>
<li>yield는 제네레이터 함수 실행을 멈추거나 다시 실행할 때 사용한다.</li>
<li>yield 오른쪽의 표현식을 평가하고 결과를 반환한다.</li>
<li>표현식을 작성하지 않으면 undefined를 반환한다.</li>
<li>{value: <code>값</code>, done: <code>true/false</code>} 반환한다.</li>
</ul>
<h2 id="키워드">키워드</h2>
<h3 id="value">value</h3>
<ul>
<li>yield 표현식의 평가 결과 설정</li>
<li>yield를 실행하지 못하면 undefined</li>
</ul>
<h3 id="done">done</h3>
<ul>
<li>yield를 실행하면 false</li>
<li>yield를 실행하지 못하면 true</li>
</ul>
<h2 id="yield의-사용의-예">yield의 사용의 예</h2>
<h3 id="yield의-반복">yield의 반복</h3>
<pre><code class="language-js">let status = true;
function* sports() {
  let count = 0;
  while(status){
    yield ++count;
  }
}
const obj = sports();
console.log(obj.next()) // {value: 1, done: false}
console.log(obj.next()) // {value: 2, done: false}
status = false;
console.log(obj.next()) // {value: undefined, done: true}

// status에 false를 함으로써 이터레이터 동작 제어</code></pre>
<h3 id="다수의-yield-처리">다수의 yield 처리</h3>
<pre><code class="language-js">function* sports(){
  return yield yield yield;
}
const obj = sports();
console.log(obj.next())  // {value: undefiend, done: falase}
console.log(obj.next(10))// {value: 10, done: false}
console.log(obj.next(20))// {value: 20, done: false}
console.log(obj.next(30))// {value: 30, done: true}

// return 문을 작성하지 않으면 30이 아닌 {value: undefined, done: true} 반환</code></pre>
<hr>
<h1 id="next">next()</h1>
<ul>
<li>next()는 yield 단위로 실행</li>
<li>yield 수만큼 next()를 작성해야 yield 전체가 실행된다.</li>
</ul>
<pre><code class="language-js">function* sports(value){
  value += 20;
  const param = yield ++value;
  value = param + value;
  yield ++value;
}
const obj = sports(10);
console.log(obj.next()) // {value: 31, done:false} 2번째까지 실행됨
console.log(obj.next(20)) // {value: 52, done:false}</code></pre>
<ul>
<li>next()를 호출하면 이전 yield의 다음부터 yield까지 실행한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL 23][Data Structure] Tree - 1]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-23Data-Structure-Tree-1</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-23Data-Structure-Tree-1</guid>
            <pubDate>Thu, 29 Apr 2021 12:46:58 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p><strong>Tree</strong>는 이름처럼 나무가 뿌리를 내리는듯한 구조로 되어있다.
트리 중 가장 최상위에 위치한 Node를 Root Node라고 부른다.
<img src="https://images.velog.io/images/devpark_0x1c/post/d5646652-dd17-4255-ae40-ef38f2a510ae/a_20210427-083612.jpg" alt=""></p>
<pre><code class="language-js">function tree(value){
  this.value = value;
  this.children = [];
}
</code></pre>
<h2 id="1-이진트리">1. 이진트리</h2>
<ul>
<li>이진트리는 자식 노드가 왼쪽, 오른쪽 두개뿐인 트리이다.<pre><code class="language-js">function binaryTreeNode(value) {
this.value = value;
this.left = null;
this.right = null;
}
</code></pre>
</li>
</ul>
<p>function binaryTree(){
  this.root = null;
}</p>
<pre><code>
### 이진트리의 순회 방법
- 순회의 방법 기준은 root node(현재 node)의 기준점에 따라 순회 과정이 진행된다.

#### 선순위 순회 (pre-order)
선순위 순회는 root(현재 node) -&gt; 왼쪽 -&gt; 오른쪽순으로 진행된다.
![](https://images.velog.io/images/devpark_0x1c/post/a1a5cca4-88bf-4afd-b746-0cf2441fec4c/a_20210427-083634.jpg)

##### 과정
1. root인 1번이 출력
1. **왼쪽 node 시작**
1. root의 왼쪽에 위치한 node 2번 출력
1. 2번 node의 왼쪽에 위치한 node 3번 출력
1. 2번 node의 오른쪽에 위치한 node 4번 출력
1. **왼쪽 node 끝**
1. **오른쪽 node 시작**
1. root의 오른쪽에 위치한 node 5번 출력
1. 5번 node 왼쪽에 위치한 node 6번 출력
1. 5번 node 오른쪽에 위차한 node 7번 출력
1. **오른쪽 node 끝**
1. **순회 완료**

##### 코드
```js
function preOrderIteration(){
  // 빈 스택에 루트 추가
  let nodeStack = [];
  nodeStack.push(this.root);

  // 모든 항목 하나씩 출력
  while(nodeStack.length){
    // 스택 최상위 항목 꺼내서 출력
    let node = nodeStack.pop();

    // 꺼낸 노드의 오른쪽, 왼쪽 자식 노드를 스택에 추가
    if(node.right)
      nodeStack.push(node.right)
    if(node.left)
      nodeStack.push(node.left)
  }
}</code></pre><h4 id="중순위-순회-in-order">중순위 순회 (in-order)</h4>
<p>중순위 순회는 왼쪽 -&gt; root(현재 node) -&gt; 오른쪽순으로 진행된다.
<img src="https://images.velog.io/images/devpark_0x1c/post/c07f7b3f-e86d-4e2c-ba1c-f3bf302ca3fe/a_20210427-083650.jpg" alt=""></p>
<h5 id="과정">과정</h5>
<ol>
<li><strong>왼쪽 node 시작</strong></li>
<li>왼쪽인 1번이 출력</li>
<li>1번 node의 root인 node 2번 출력</li>
<li>node 2번의 오른쪽 자식인 node 3번 출력</li>
<li><strong>왼쪽 node 끝</strong></li>
<li>node 2번의 root인 node 4번 출력</li>
<li><strong>오른쪽 node 시작</strong></li>
<li>오른쪽 노드들의 왼쪽 node인 5번 출력</li>
<li>5번 node의 root인 node 6번 출력</li>
<li>root node 5번의 오른쪽 자식 node인 7번 출력</li>
<li><strong>오른쪽 node 끝</strong></li>
<li><strong>순회 완료</strong></li>
</ol>
<h5 id="코드">코드</h5>
<pre><code class="language-js">function inOrderIterate(){
  let current = this.root;
  let s = [];
  let done = false;

  while(!done){
    if (current !== null) {
      s.push(current);
      current = current.left;
    } else {
      if (s.length) {
        current = s.pop();
        current = current.right;
      } else {
        done = true;
      }
    }
  }
}</code></pre>
<h4 id="후순위-순회-post-order">후순위 순회 (post-order)</h4>
<p>후순위 순회는 왼쪽 -&gt; 오른쪽 -&gt; root(현재(node)순으로 진행된다.
<img src="https://images.velog.io/images/devpark_0x1c/post/448bbf5e-1d7e-4269-bcbd-a54f8c096568/a_20210427-083713.jpg" alt=""></p>
<h5 id="과정-1">과정</h5>
<ol>
<li>가장 최상위 node의 자식 node 중 왼쪽부터 시작</li>
<li><strong>왼쪽 node 시작</strong></li>
<li>3번 node의 자식 node 중 왼쪽 node인 1번 출력</li>
<li>3번 node의 자식 node 중 오른쪽 node인 2번 출력</li>
<li>node 1번과 2번의 root인 3번 출력</li>
<li><strong>왼쪽 node 끝</strong></li>
<li><strong>오른쪽 node 시작</strong></li>
<li>6번 node의 자식 node 중 왼쪽 node인 4번 출력</li>
<li>6번 node의 자식 node 중 오른쪽 node인 5번 출력</li>
<li>node 4번과 5번의 root인 6번 출력</li>
<li><strong>오른쪽 node 끝</strong></li>
<li>가장 최상위 node 7번 출력</li>
<li><strong>순회 완료</strong></li>
</ol>
<h5 id="코드-1">코드</h5>
<pre><code class="language-js">function postOrderIterate() {
  let s1 = [], s2 = [];
  s1.push(this.root);

  // 첫번째 스택 비울때까지 계속 실행
  while(s1.length){
    const node = s1.pop();
    s2.push(node);

    // 제거된 항목의 왼쪽과 오른쪽 자식노드를 s1에 추가
    if (node.left)
      s1.push(node.left);
    if (node.right)
      s1.push(node.right);
  }
  // 두번째 스택의 모든 항목 출력
  while(s2.length){
    const node = s2.pop();
  }
}</code></pre>
<h3 id="이진-tree-순회-선택-기준">이진 tree 순회 선택 기준</h3>
<ul>
<li>자식 node가 없는 node에 접근하기 전에 root를 먼저 접근해야 하는 경우 선순위 순회를 선택하면 된다.</li>
<li>부모 node를 선택하기 전에 자식 node가 없는 node를 먼저 접근해야 하는 경우 후순위 순회를 선택하면 된다. 후순위 순회를 선택하면 root를 접근하는 시간 낭비를 하지 않기 때문이다.</li>
<li>tree의 node 자체에 순서가 있어서 tree를 원래 순서대로 순회하고 싶을 때는 중순위 순회를 선택하면 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL 22][JS] Class]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-21JS-Class</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-21JS-Class</guid>
            <pubDate>Mon, 26 Apr 2021 13:53:18 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>기존에 클래스에 대한 글을 썼던것에 보충을 좀 해야지..라고 생각해서 오늘 클래스를 좀 더 공부한 다음 내용 보충을 하려고 했는데, 클래스에 대한 글을 안썼었다. 정말 충격적이다. 그래서 처음부터 클래스에 대한 내용을 복습할 겸 작성해본다.</p>
<h2 id="class">class?</h2>
<p>클래스는 OOP(객체 지향 프로그래밍)에서 특정 객체를 생성하기 위해 변수와 메소드를 선언하는 방법이다. 자바스크립트에 클래스가 나타나기 이전에는 기존에 썼던 글 내용인 prototype에 메소드를 할당해서 사용했었다. prototype에 메소드를 선언하는 방법도 있지만 ES6에서 등장한 class를 활용하면 OOP에 익숙한 개발자들은 좀 더 쉽게 접근이 가능하다.</p>
<h2 id="구조">구조</h2>
<pre><code class="language-js">class Person {
  constructor(name){
    this.name = name;
  }
  sayHi(){
    alert(this.name);
  }
}

let user = new User(&quot;jay&quot;);
user.sayHi() // 경고창과 함께 &quot;jay&quot;가 나타남</code></pre>
<p>위처럼 class는 contructor()와 사용자 정의 메소드를 가진다. 위처럼 <code>new User(&quot;jay&quot;)</code>를 호출하면 객체가 생성되면서 <code>&quot;jay&quot;</code>를 넘겨받은 것과 함께 constructor가 자동으로 실행되면서 <code>this.name</code>에 <code>&quot;jay&quot;</code>가 할당된다.</p>
<h3 id="constructor">constructor</h3>
<p>constructor는 instance를 생성하고 클래스 필드를 초기화하기 위한 메소드이다. 클래스 내에 한 개만 존재하고, 2개 이상이 존재하면 SyntaxError가 발생한다.
constructor는 생략이 가능한데, 만약 생략하게 된다면 <code>constructor(){}</code>를 클래스내에 포함 것과 동일하게 동작한다.</p>
<h2 id="getter-setter">getter, setter</h2>
<h3 id="getter">getter</h3>
<pre><code class="language-js">class Foo {
  constructor(arr = []) {
    this._arr = arr;
  }
  get firstElem() {
    return this._arr.length ? this._arr[0] : null;
  }
}

const foo = new Foo([1, 2]);
// 필드 firstElem에 접근하면 getter가 호출된다.
console.log(foo.firstElem); // 1</code></pre>
<p>getter는 클래스 필드에 접근할 때마다 클래스 필드의 값을 조작하는 경우가 필요할 때 사용한다.
사용은 <code>get</code> 키워드를 이용한다. getter는 뭔가를 얻을 때 사용하므로 다시 뭔가를 반환해야 한다.</p>
<h3 id="setter">setter</h3>
<pre><code class="language-js">class Foo {
  constructor(arr = []) {
    this._arr = arr;
  }
  get firstElem() {
    return this._arr.length ? this._arr[0] : null;
  }
  set firstElem(elem) {
    this._arr = [elem, ...this._arr];
  }
}

const foo = new Foo([1, 2]);
foo.firstElem = 100;
console.log(foo.firstElem); // 100</code></pre>
<p>setter는 클래스 필드에 값을 할당할 때마다 필드의 값을 조작하는 행위가 필요할 때 사용한다.
사용은 <code>set</code> 키워드를 이용한다. setter는 호출하는 것이 아니라 프로퍼티처럼 값을 할당하는 형식으로 사용해서 할당 시에 메소드가 호출된다.</p>
<h2 id="static">static</h2>
<pre><code class="language-js">class Foo {
  constructor(prop) {
    this.prop = prop;
  }
  static staticMethod() {
    return &#39;staticMethod&#39;;
  }
  prototypeMethod() {
    return this.prop;
  }
}

console.log(Foo.staticMethod()); // &#39;staticMethod&#39;

const foo = new Foo(123);
console.log(foo.staticMethod()); // Uncaught TypeError: foo.staticMethod is not a function</code></pre>
<p>클래스의 정적(static) 메소드를 정의할 때 static 키워드를 사용한다. 정적 메소드는 클래스의 인스턴스가 아닌 클래스 이름으로 호출한다. 따라서 클래스의 인스턴스를 생성하지 않아도 호출할 수 있다. 위처럼 인스턴스로 호출을 하면 에러가 난다.!</p>
<h2 id="extends-super">extends, super</h2>
<pre><code class="language-js">class Vehicle {
  constructor() {
    this.passengers = [];
    console.log(&#39;Vehicle Created&#39;);
  }
  addPassenger(p) {
    this.passengers.push(p);
    console.log(&#39;Passenger Added&#39;);
  }
}

class Car extends Vehicle {
  constructor() {
    super(); // Vehicle 클래스의 constructor를 호출 함
    console.log(&#39;Car created&#39;);
  }
  deployAirbags() {
    console.log(&#39;BOOM!&#39;);
  }
}

const car1 = new Car();
car1.addPassenger(&#39;sunny&#39;);
car1.deployAirbags();
console.log(car1);</code></pre>
<p><img src="https://images.velog.io/images/devpark_0x1c/post/5c00e406-6c70-4d51-b44d-13d696334592/image.png" alt="">
extends는 부모 클래스를 상속받는 자식 클래스를 정의할 떄 사용한다.
super는 부모 클래스를 참조할 때나 부모의 constructor를 호출할 때 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL 21][Data Structure] Single Linked List 코드 구현]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-21Data-Structure-Single-Linked-List-%EC%BD%94%EB%93%9C-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-21Data-Structure-Single-Linked-List-%EC%BD%94%EB%93%9C-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Fri, 23 Apr 2021 14:03:14 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/devpark_0x1c/post/8837222b-89ce-4d19-a985-97005612e199/image.png" alt=""></p>
<pre><code class="language-js">function singleLinkedListNode(data) {
  this.data = data;
  this.next = null;
}

class singleLinkedList {
  constructor() {
    this.head = null;
    this.size = 0;
  }
  isEmpty() {
    return this.size === 0;
  }
  insert(value) {
    if (this.head === null) {
      this.head = new singleLinkedListNode(value);
    } else {
      const temp = this.head;
      this.head = new singleLinkedListNode(value);
      this.head.next = temp;
    }
    this.size++;
  }
  remove(value) {
    let currentHead = this.head;
    if (currentHead.data === value) {
      this.head = currentHead.next;
      this.size--;
    } else {
      let prev = currentHead;
      while (currentHead.next) {
        if (currentHead.data === value) {
          prev.next = currentHead.next;
          prev = currentHead;
          currentHead = currentHead.next;
          break;
        }
        prev = currentHead;
        currentHead = currentHead.next;
      }
      if (currentHead.data === value) {
        prev.next = null;
      }
      this.size--;
    }
  }
  deleteHead() {
    const toReturn = null;

    if (this.head !== null) {
      toReturn = this.head.data;
      this.head = this.head.next;
      this.size--;
    }
    return toReturn;
  }
  find(value) {
    const currentHead = this.head;
    while (currentHead.next) {
      if (currentHead.data === value) {
        return true;
      }
      currentHead = currentHead.next;
    }
    return false;
  }
}</code></pre>
<h3 id="insert">insert</h3>
<ul>
<li>head가 null 일경우 head가 신규 node로 설정됨 null이 아닐 경우 예전 head가 temp에 저장되고 새로운 head가 신규로 추가된 node가 된다. 새로운 head의 next는 temp를 가리킴.</li>
</ul>
<h3 id="remove">remove</h3>
<ul>
<li>삭제는 해당 node의 참조를 제거함으로서 구현할 수 있다. 위의 그림처럼 node가 연결 리스트의 중간에 있으면 삭제하고자 하는 node 이전 node의 next가 삭제하고자 하는 node의 다음 node를 가리키도록 한다. node가 단일 연결 리스트의 끝에 위치하면 마지막에서 두 번째 node가 자신의 next를 null로 설정하고 노드의 참조를 끊는다.</li>
</ul>
<h3 id="deletehead">deleteHead</h3>
<ul>
<li>이름 그대로 head를 삭제한다</li>
</ul>
<h3 id="find">find</h3>
<ul>
<li>value가 연결 리스트내에 존재하는지 확인하고 반복문을 통해 next가 가리키는걸 순회한다.</li>
</ul>
<blockquote>
<p>코드를 작성할 때 prototype에다가 넣어볼까 했는데 지금 class를 공부하고 있는중이라 class로 만들어서 해봤는데 이론을 실전에 대입하니까 상당히 괜찮은것 같다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL 20][JS] ES6 - 3]]></title>
            <link>https://velog.io/@devpark_0x1c/TIL-20JS-ES6-3</link>
            <guid>https://velog.io/@devpark_0x1c/TIL-20JS-ES6-3</guid>
            <pubDate>Thu, 22 Apr 2021 13:26:30 GMT</pubDate>
            <description><![CDATA[<h1 id="object">Object</h1>
<h2 id="method">Method</h2>
<h3 id="is">is()</h3>
<ul>
<li>두 개의 파라미터 값과 값 타입을 비교. 같으면 true 아니면 false</li>
<li>Object.is()와 === 비교 차이</li>
</ul>
<ol>
<li>NaN<pre><code class="language-js">NaN === NaN // false
Object.is(NaN, NaN) //true
NaN === 0 / 0 // false
Object.is(NaN, === 0 / 0) //true</code></pre>
</li>
<li>+0, -0<pre><code class="language-js">0 === -0 // true
Object.is(0, -0) // false</code></pre>
</li>
</ol>
<h3 id="assign">assign()</h3>
<ul>
<li>두 번째 파라미터의 오브젝트 프로퍼티를 첫번째 파라미터의 오브젝트에 복사하고 첫번째를 리턴<pre><code class="language-js">const sports = {
kind: &quot;야구&quot;,
}
let baseball = {}
</code></pre>
</li>
</ul>
<p>Object.assign(baseball, sports)
console.log(baseball) // {kind: &quot;야구&quot;}</p>
<pre><code>- 첫번째 파라미터를 작성하지 않거나 null, undefined를 작성하면 TypeError가 난다.
- 다수의 오브젝트를 작성할 수도 있다.
```js
const book = {title: &quot;책&quot;};
const sports = {kind: &quot;야구&quot;};
const obj = Object.assign({}, book, sports);
console.log(obj) {title: 책, kind: 야구}</code></pre><h3 id="entries">entries()</h3>
<ul>
<li>열거 가능한 오브젝트의 {key: value}를 Map 형태로 전환<pre><code class="language-js">const obj = {sports: &quot;야구&quot;, music: &quot;pop&quot;};
const list = Object.entries(obj);
for (let keyValue of list) {
console.log(keyValue); // [sports, 야구], [music, pop]
}</code></pre>
</li>
</ul>
<h3 id="values">values()</h3>
<ul>
<li>열거 가능한 오브젝트의 {key: value}를 [value1, value2] 형태로 변환<pre><code class="language-js">const obj = {sports: &quot;야구&quot;, music: &quot;pop&quot;};
const list = Object.va;ies(obj);
for (let value of list){
console.log(value) // 야구, pop
}</code></pre>
</li>
</ul>
<h3 id="fromentries">fromEntries()</h3>
<ul>
<li>[key, value] 형태를 {key: value} 형태로 변환<pre><code class="language-js">const list = [[&quot;one&quot;, 10], [&quot;two&quot;, 20]]
const obj = Object.fromEntries(list);
console.log(obj) // {one: 10, two: 20}</code></pre>
</li>
<li>프로퍼티 key 값이 같으면 값이 대체된다<pre><code class="language-js">const list = [[&quot;one&quot;, 10], [&quot;one&quot;, 20]]
const obj = Object.fromEntries(list);
console.log(obj) // {one: 20}</code></pre>
</li>
</ul>
<h1 id="array">Array</h1>
<h2 id="method-1">Method</h2>
<h3 id="from">from()</h3>
<ul>
<li>첫번째 파라미터의 오브젝트를 Array 오브젝트로 변환<pre><code class="language-js">const like = {0:&quot;zero&quot;, 1:&quot;one&quot;}
const list = Array.from(like)
console.log(list) // [zero,one]</code></pre>
</li>
</ul>
<h3 id="of">of()</h3>
<ul>
<li>파라미터의 수나 유형에 관계없이 새 Array를 만든다<pre><code class="language-js">Array(7)       // [ , , , , , , ]
Array(1, 2, 3) // [1, 2, 3]</code></pre>
</li>
</ul>
<h3 id="copywithin">copyWithin()</h3>
<ul>
<li>배열의 특정 인덱스 범위의 값을 지정한 위치에 복사한다.<pre><code class="language-js">const list [&#39;a&#39;,&#39;b&#39;,&#39;c&#39;,&#39;d&#39;,&#39;e&#39;];
list.copyWithin(0, 2, 4);
console.log(list) // [c, d, c, d, e];</code></pre>
</li>
</ul>
<h3 id="find">find()</h3>
<ul>
<li>배열에서 특정 값을 찾고, 값을 찾았으면 첫번째 요소의 값을 반환하고 해당 값이 없으면 undefined를 반환한다.<pre><code class="language-js">const arr = [5, 12, 8, 130, 44];
</code></pre>
</li>
</ul>
<p>const found = arr.find(elem =&gt; elem &gt; 10);
console.log(found) // 12;</p>
<pre><code>
### findIndex()
- find() 와 다르게 값이 아닌 인덱스를 반환한다.
```js
const arr = [5, 12, 8, 130, 44];

const found = arr.findIndex(elem =&gt; elem &gt; 10);
console.log(found) // 1</code></pre><h3 id="fill">fill()</h3>
<ul>
<li>배열의 시작 인덱스부터 끝 인덱스까지 정적인 하나의 값으로 채운다.<pre><code class="language-js">const arr = [1, 2, 3, 4]
</code></pre>
</li>
</ul>
<p>console.log(arr.fill(0, 2, 4)) // [1, 2, 0, 0]
console.log(arr.fill(5, 1)) // [1, 5, 5, 5]
console.log(arr.fill(6)) // [6, 6, 6, 6]</p>
<pre><code>### includes()
- 특정 요소를 포함하고 있는지 불리언값으로 반환한다.
```js
const arr = [&quot;야구&quot;, &quot;축구&quot;, &quot;농구&quot;]

console.log(arr.includes(&quot;축구&quot;)) // true</code></pre>]]></description>
        </item>
    </channel>
</rss>