<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>soojeong.log</title>
        <link>https://velog.io/</link>
        <description>🍼 newbie frontend developer</description>
        <lastBuildDate>Tue, 09 Nov 2021 17:43:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>soojeong.log</title>
            <url>https://images.velog.io/images/e_soojeong/profile/36ba6014-b1b6-4a0a-b8eb-828a938b57f9/userImage.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. soojeong.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/e_soojeong" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[CSS | perspective를 활용한 tarot card]]></title>
            <link>https://velog.io/@e_soojeong/CSS-perspective%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-tarot-card</link>
            <guid>https://velog.io/@e_soojeong/CSS-perspective%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-tarot-card</guid>
            <pubDate>Tue, 09 Nov 2021 17:43:33 GMT</pubDate>
            <description><![CDATA[<p>3차원의 공간을 위해서는 원근감이 필요하다. 이때 사용할 수 있는 arribute는 perspective이다. 이전에는 사용 빈도수가 얼마나 될까? 라는 생각을 가졌다면, 최근에는 메타버스의 등장으로 더욱 주목받고 있는 arribute인 perspective에 대하여 학습하였다.</p>
<blockquote>
<p>참고 문헌</p>
</blockquote>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/perspective">https://developer.mozilla.org/en-US/docs/Web/CSS/perspective</a></li>
<li><a href="https://imjignesh.com/how-css-perspective-works/">https://imjignesh.com/how-css-perspective-works/</a></li>
<li><a href="https://3dtransforms.desandro.com/card-flip">https://3dtransforms.desandro.com/card-flip</a></li>
<li><a href="https://nykim.work/26">https://nykim.work/26</a></li>
</ul>
<h2 id="1-perspective란">1. perspective란?</h2>
<ul>
<li><p>perspective는 우리가 대상을 보는 거리</p>
</li>
<li><p>값이 적을수록 더 가까이 보게 되므로 효과가 더 극적으로 나타나게 된다. 예를 들어 같은 각도로 기울어진 물체를 멀리서 볼 때와 가까이서 볼 때, 가까이서 보게 되었을 때 그 기울어진 정도가 더 크게 느껴진다.</p>
<p>아래의 코드와 이미지를 통해서 이해를 해보자면, </p>
<pre><code>css
.origin {
         width: 200px;
         height: 200px;
         border: 1px solid black;
         margin: 100px auto;
         /*첫 번째 그림*/
         perspective: 100px;
         /*두 번째 그림*/
         perspective: 400px;
     }

     .rotate-pannel {
         width: 200px;
         height: 200px;
         background: red;
         transform: rotateX(45deg);
     }</code></pre></li>
</ul>
<pre><code>html

&lt;div class=&quot;origin&quot;&gt;
     &lt;div class=&quot;rotate-pannel&quot;&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre><p> <img src="https://images.velog.io/images/e_soojeong/post/2ae2f1ee-123f-4abd-9df3-2f31657118c7/image.png" alt=""> 
 <img src="https://images.velog.io/images/e_soojeong/post/c003675e-9b27-43d6-901c-7fc2934d1108/image.png" alt=""></p>
<p> 같은 각도로 rotateX(45deg) 되었지만, perspective의 값에 따라서 <code>perspective: 100px;</code>일 때와 <code>perspective: 400px;</code> 일 때의 원근감은 크게 나타남을 알 수 있게 되었다.</p>
<h2 id="2-y축의-소실점">2. Y축의 소실점</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/f2bad8ed-8551-4565-b540-e0ade6972501/image.png" alt=""></p>
<p>이 화면은 아래의 코드를 입력하였을 때, 보이는 화면이다. 하나를 기준으로 rotate 할 때에는 몰랐는데, 여려 개의 박스에 적용하니 서로 다른 모습을 보이고 있다.</p>
<pre><code>html
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;perspective&lt;/title&gt;
    &lt;style&gt;
        .origin {
            display: flex;
            justify-content: space-between;
            width: 1000px;
            height: 200px;
            border: 1px solid black;
            margin: 100px auto;
            perspective: 400px;
        }

        .pannel {
            width: 200px;
            height: 200px;
            background: aqua;
            transform: rotateY(45deg);
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div class=&quot;origin&quot;&gt;
        &lt;div class=&quot;pannel&quot;&gt;&lt;/div&gt;
        &lt;div class=&quot;pannel&quot;&gt;&lt;/div&gt;
        &lt;div class=&quot;pannel&quot;&gt;&lt;/div&gt;
        &lt;div class=&quot;pannel&quot;&gt;&lt;/div&gt;
        &lt;div class=&quot;pannel&quot;&gt;&lt;/div&gt;
        &lt;div class=&quot;pannel&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>그 이유는, perspective 속성을 부모 요소에 적용하였기 때문에 pannel들이 서로 다른 투영점(perspective) 즉, 다른 소실점(vanishing point)을 갖고 있기 때문이다.</p>
<h2 id="3-동일한-perspective-설정하기">3. 동일한 perspective 설정하기</h2>
<h3 id="3-1-자식-요소에-transform-속성의-값으로-perspetive를-주고-괄호-안에-수치를-입력">3-1. 자식 요소에 transform 속성의 값으로 perspetive를 주고, 괄호 안에 수치를 입력</h3>
<pre><code>css
    .origin {
            display: flex;
            justify-content: space-between;
            width: 1000px;
            height: 200px;
            border: 1px solid black;
            margin: 100px auto;
        }

        .pannel {
            width: 200px;
            height: 200px;
            background: aqua;
            transform: perspective(400px);
            rotateY(45deg);
        }</code></pre><h3 id="3-2-부모-요소에-perspective-origin-사용">3-2. 부모 요소에 perspective-origin 사용</h3>
<ul>
<li>소실점이 어디있는가를 나타내는 속성</li>
<li>기본 값은 <code>perspective-origin: 50% 50%;</code></li>
</ul>
<pre><code>css 
        .origin {
            display: flex;
            justify-content: space-between;
            width: 1000px;
            height: 200px;
            border: 1px solid black;
            margin: 100px auto;
            perspective-origin: 50% 50%;
        }

        .pannel {
            width: 200px;
            height: 200px;
            background: aqua;
            transform: rotateY(45deg);
        }</code></pre><h2 id="4-perspective와-grid를-활용한-타로카드-뽑기">4. perspective와 grid를 활용한 타로카드 뽑기</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/04aebbf7-9994-40bb-aa1e-6404eb2eb4af/IMB_Vc3zPs.GIF" alt=""></p>
<p>✏️ <strong>설계</strong></p>
<ol>
<li><p>처음엔 perspective를 활용하여 어떻게 앞, 뒤를 나타낼 수 있을까 고민했다. 블로그에서 힌트를 얻어서 앞, 뒤를 나타내는 요소를 만들고 이를 하나의 카드처럼 보일 수 있게 위치 조정을 해주었다.</p>
</li>
<li><p><code>transform-style: preserve-3d</code>는 이 perspective를 부모로부터 받아 자신을 통과해 자식까지 전달한다.</p>
</li>
<li><p><code>backface-visibility: hidden</code>는 3D Transform에서 요소의 뒷면을 숨기는 역할을 한다. hidden 처리하지 않으면 앞면/뒷면이 함께 보이는 문제가 발생한다.</p>
</li>
<li><p><a href="https://imjignesh.com/how-css-perspective-works/">https://imjignesh.com/how-css-perspective-works/</a> 과 <a href="https://nykim.work/26">https://nykim.work/26</a> 를 참고하여 구현하였다. </p>
</li>
<li><p>위치는 grid를 활용하여 한 줄에 5개의 카드가 나타날 수 있도록 하였다.</p>
</li>
</ol>
<p>📒 <strong>나의 생각</strong>
역시 만들고 싶은 것을 만들면 더 열심히 그리고 재미나게 배울 수 있었다. 그리고 모든 것을 알지 못해도, 원하는 것에 대하여 구글링하며 참조하면 구현이 가능하다는 것도 또 다시 깨달았다. 여기서도 추가로 구현하고 싶은 것들이 생각이 났는데, JS를 적용하여 해당하는 카드를 클릭했을 때, 모달 창이 떠서 해당 카드에 대한 설명을 사용자에게 전달해주면 이게 바로..! 온라인 타로가 아닐까? 하는 생각과 함께 이걸 더 디벨롭하면 심리테스트나, mbti 같은 재미난 결과값을 보여주는 프로젝트를 만들 수 있을 것이라고 생각했다. 결론은 레이아웃 구성한 마크업에 JS를 달아보는 일을 꼭 하자는 것..!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS | display:grid; 그런데 mini-project를 곁들인]]></title>
            <link>https://velog.io/@e_soojeong/CSS-displaygrid-%EA%B7%B8%EB%9F%B0%EB%8D%B0-mini-project%EB%A5%BC-%EA%B3%81%EB%93%A4%EC%9D%B8</link>
            <guid>https://velog.io/@e_soojeong/CSS-displaygrid-%EA%B7%B8%EB%9F%B0%EB%8D%B0-mini-project%EB%A5%BC-%EA%B3%81%EB%93%A4%EC%9D%B8</guid>
            <pubDate>Tue, 09 Nov 2021 16:19:54 GMT</pubDate>
            <description><![CDATA[<p>flex를 알면 더 수월하게 공부할 수 있다고 생각했던 grid, 막상 적용하려니 쉽지 않아서 mini-project를 통해서 연습해보았다. </p>
<blockquote>
<p>참고문헌 : <a href="https://studiomeal.com/archives/533">https://studiomeal.com/archives/533</a></p>
</blockquote>
<h2 id="1-flex와-grid의-차이점">1. Flex와 Grid의 차이점</h2>
<ul>
<li>Flex는 한 방향 레이아웃 시스템이고 (1차원)</li>
<li>Grid는 두 방향(가로-세로) 레이아웃 시스템 (2차원)</li>
</ul>
<h2 id="2flex와-grid의-공통점">2.Flex와 Grid의 공통점</h2>
<ul>
<li><p><code>display:grid;</code> : 그리드 컨테이너와 아이템을 설정한다. 
  = <code>display:flex;</code></p>
</li>
<li><p>그리드 컨테이너 : 그리드의 가장 바깥영역
  = <code>display:flex;</code>를 적용한 부모 요소</p>
</li>
<li><p>그리드 아이템 : 그리드 컨테이너의 자식 요소들
  = <code>flex item</code></p>
</li>
</ul>
<h2 id="3-grid의-arrtibute">3. Grid의 arrtibute</h2>
<ul>
<li>그리드 트랙 : 그리드의 행(row) 또는 열(column) mian, cross axis</li>
<li>그리드 셀 : 그리드의 한 칸 (개념적인 정의)</li>
<li>그리드 라인 : 그리드 셀을 구분하는 선</li>
<li>그리드 넘버 : 그리드 라인의 각 번호</li>
<li>그리드 갭 : 그리드 셀 사이의 간격</li>
<li>그리드 에어리어 : 그리드 셀의 집합</li>
</ul>
<h2 id="4-실전-적용">4. 실전 적용</h2>
<ol start="0">
<li><p><strong>그리드의 특징</strong> : 컨테이너를 꽉 채우려고 한다.</p>
</li>
<li><p><code>display: grid;</code> 만 적용한다고 변화하지 않는다.
(item이 block인 경우) flex의 경우엔 부모 요소에 <code>display: flex;</code>만 적어줘도 수평으로 고르게 정렬되었는데, 아무런 변화가 없어서 <strong>그리드가 잘 적용된 건가?</strong> 하였다.</p>
</li>
<li><p><strong>그리드 형태 정의</strong> : <code>grid-template-rows</code>,<code>grid-template-columns</code></p>
<p>컨테이너에 Grid 트랙의 크기들을 지정해주는 속성이다. 이때, grid-template-rows는 행(row)의 배치를 나타내며, grid-template-columns는 열(column)의 배치를 나타낸다. </p>
<pre><code>css

/* 열의 개수 설정 */
/* grid-template-columns: 100px 100px 100px; */
/* grid-template-columns: 1fr 1fr 1fr; */

/* `grid-template-columns` : 열의 넓이 설정 */
/* `grid-template-rows` : 행의 높이를 설정 */</code></pre></li>
<li><p><strong>repeat 함수</strong>
repeat는 반복되는 값을 자동으로 처리할 수 있는 함수이다. </p>
<pre><code>css
/* repeat`( 적용할 트랙의 갯수, 반복할 수치 ) 함수를 이용하면 그리드 트랙 별 수치 반복 설정 */
grid-template-columns: repeat(3,1fr);
grid-template-rows: 100px 100px 100px;</code></pre><p>위 예제 코드의 두번째 속성을 repeat 함수로 나타내어 보면, <code>grid-template-rows: repeat(3, 100px);</code> 로 나타낼 수 있다.</p>
<p>추가로,<code>fr</code>은 fraction의 약어로, 숫자 비율대로 트랙의 크기를 균일하게 나누어 준다.</p>
</li>
<li><p><strong>gap</strong> : <code>row-gap</code>, <code>column-gap</code>, <code>gap</code></p>
</li>
</ol>
<ul>
<li>margin과 유사하다.</li>
<li>단 fr 단위가 아닌 고정단위일때는 gap의 영역을 포함해서 작동하여 스크롤이 생긴다.</li>
<li><code>row-gap</code>, <code>column-gap</code>의 축약형이 <code>gap</code>이다.</li>
</ul>
<pre><code>css
.container {
    row-gap: 10px;
    /* row의 간격을 10px로 */
    column-gap: 20px;
    /* column의 간격을 20px로 */
}

.container {
    gap: 10px 20px;
    /* row-gap: 10px; column-gap: 20px; */
}

.container {
    gap: 20px;
    /* row-gap: 20px; column-gap: 20px; */
}</code></pre><ol start="5">
<li><strong>각 셀의 영역 지정</strong>
Grid 라인 번호를 이용해서 column과 row의 범위를 결정한다. 한글과 컴퓨터의 셀 병합과 유사하다고 생각하면 이해가 쉽다.
<img src="https://images.velog.io/images/e_soojeong/post/04e5250b-fcbe-48ee-a3e9-174527383f4d/image.png" alt=""></li>
</ol>
<ul>
<li><p><code>grid-column-start</code> : column 시작 번호</p>
</li>
<li><p><code>grid-column-end</code> : column 끝 번호</p>
</li>
<li><p><code>grid-column</code> : start와 end 속성을 한번에 쓰는 축약형</p>
</li>
<li><p><code>grid-row-start</code> : row 시작 번호</p>
</li>
<li><p><code>grid-row-end</code> : row 끝 번호</p>
</li>
<li><p><code>grid-row</code> : start와 end 속성을 한번에 쓰는 축약형</p>
</li>
</ul>
<pre><code>css
/* grid-column-start: 1;*/
/*grid-column-end: 3; */

/* 축약형 */
/* grid-column: 1 / 3; */

/* row */
/* grid-row-start: 1;
grid-row-end: 3; */
grid-row: 1/3;

/* span : 칸의 갯수 */
/* span 없다면 기존 그리드 라인과 동일함 */
/* 1번 라인에서 3칸 */
grid-row: 1/ span 3;</code></pre><h2 id="5-grid를-활용한-계산기-만들기">5. Grid를 활용한 계산기 만들기</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/f54323c6-0626-45f1-abe1-9797d993e477/%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-11-10%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.30.35.png" alt=""></p>
<p>✏️ <strong>설계</strong></p>
<ol>
<li>숫자 값이 출력되는 input tag <code>grid-column</code> 적용하기</li>
<li>4 * 5 의 그리드 만들기</li>
<li>숫자 &#39;0&#39;에 <code>grid-column</code> 적용하기</li>
</ol>
<p>💻 <strong>코드</strong></p>
<pre><code>html
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
  &lt;meta charset=&quot;UTF-8&quot;&gt;
  &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
  &lt;title&gt;calculator&lt;/title&gt;
  &lt;link rel=&quot;stylesheet&quot; href=&quot;./../reset.css&quot;&gt;
  &lt;style&gt;
    body {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    .caculator {
      width: 400px;
      height: 600px;
      margin: 0 auto;
      padding: 10px;
      color: #000000;
      background-color: #000000;
    }

    input {
      width: 100%;
      display: grid;
      grid-column: 1 / span 4;
      text-align: right;
      margin-bottom: 5px;
      background: #0000;
      border: none;
      color: #ffffff;
      font-size: 55px; 
      height: 60px;
      font-size: 60px; 
    }

    .first-line, .second-line, .third-line, .forth-line, .fifth-line {
      display: grid;
      height: 100px;
      grid-template-columns: repeat(4, 1fr);
      gap: 5px;
      margin-bottom: 5px;
    }

    .circle {
      border-radius: 50%;
      border: none;
      color: #fff;
      font-size: 28px;
    }

    .circle:hover {
      cursor: pointer;
    }

    .light-gray {
      background-color: #c4c4c4;
    }

    .dark-gray {
      background-color: #444444;
    }

    .orange {
      background-color: #fe9f05;
    }

    .fifth-line .circle:first-child{
      grid-column: 1/3;
      border-radius: 60px;
    }


  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div class=&quot;caculator&quot;&gt;
    &lt;div class=&quot;value&quot;&gt;
      &lt;input type=&quot;text&quot; placeholder=&quot;0&quot;&gt;
    &lt;/div&gt;
    &lt;div class=&quot;first-line&quot;&gt;
      &lt;button class=&quot;circle light-gray&quot;&gt;AC&lt;/button&gt;
      &lt;button class=&quot;circle light-gray&quot;&gt;+/-&lt;/button&gt;
      &lt;button class=&quot;circle light-gray&quot;&gt;%&lt;/button&gt;
      &lt;button class=&quot;circle orange&quot;&gt;+&lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;second-line&quot;&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;7&lt;/button&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;8&lt;/button&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;9&lt;/button&gt;
      &lt;button class=&quot;circle orange&quot;&gt;x&lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;third-line&quot;&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;4&lt;/button&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;5&lt;/button&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;6&lt;/button&gt;
      &lt;button class=&quot;circle orange&quot;&gt;-&lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;forth-line&quot;&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;1&lt;/button&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;2&lt;/button&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;3&lt;/button&gt;
      &lt;button class=&quot;circle orange&quot;&gt;+&lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;fifth-line&quot;&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;0&lt;/button&gt;
      &lt;button class=&quot;circle dark-gray&quot;&gt;.&lt;/button&gt;
      &lt;button class=&quot;circle orange&quot;&gt;=&lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><p>📒 <strong>나의 생각</strong></p>
<p>실제 아이폰 유저인 나로서는 똑같은 UI인 계산기를 바탕으로 재미있게 grid를 실습할 수 있었다. 이후에는 JS를 활용해서 실제로 계산이 가능한 계산기를 만들어서 project를 더 빌드해야겠다는 생각을 하였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS |flex-basis, flex-grow, flex-shrink]]></title>
            <link>https://velog.io/@e_soojeong/CSS-flex-basis-flex-grow-flex-shrink</link>
            <guid>https://velog.io/@e_soojeong/CSS-flex-basis-flex-grow-flex-shrink</guid>
            <pubDate>Mon, 08 Nov 2021 16:02:28 GMT</pubDate>
            <description><![CDATA[<p>flex를 심도 있게 공부하고 싶어서 레이아웃 잡는 것 외에 flex item에 적용하는 속성들에 대하여 학습하기로 하였다.</p>
<h2 id="1-flex-basis">1. flex-basis</h2>
<ul>
<li>유연한 박스의 기본 영역. flex item들의 <strong>기본 점유 크기</strong>를 설정한다.</li>
<li>width, height 와 다른점은 axis 방향에 따라 달라진다는 것이다. (flex-direction이 row일 때는 너비, column일 때는 높이)</li>
<li>기본 값은 <code>auto</code> 이며, 이때에는 width, height 값은 가 적용된다. 반대로, <strong>flex-basis</strong>가 적용되어 있다면 width, height 값은 무시되어 컨텐츠의 크기를 갖게된다.</li>
</ul>
<h2 id="2-flex-grow">2. flex-grow</h2>
<ul>
<li>유연하게 늘리기</li>
<li>flex-grow는 아이템이 <strong>flex-basis의 값보다 커질 수 있는지</strong> 결정하는 속성이다.</li>
<li>flex-grow에는 숫자값이 들어가며, 기본값인 <code>0</code> 보다 큰 값이 들어가면 해당 아이템이 빈 공간을 메우게 된다.</li>
<li>flex-grow에 들어가는 숫자값은 아이템들의 flex-basis를 제외한 여백 부분을 flex-grow에 지정된 숫자의 비율로 나누어 가진다.</li>
</ul>
<pre><code>css
/* 1:2:1의 비율로 세팅할 경우 */
.item:nth-child(1) { flex-grow: 1; }
.item:nth-child(2) { flex-grow: 2; }
.item:nth-child(3) { flex-grow: 1; }</code></pre><p>여백의 비는 정확히 1:2:1이다. 정수 뿐 아니라 소수 비율도 가능하다.</p>
<h2 id="3-flex-shrink">3. flex-shrink</h2>
<ul>
<li>유연하게 줄이기</li>
<li>flex-shrink는 flex-grow와 쌍을 이루는 속성이다.</li>
<li>flex-shrink는 <strong>flex-basis의 값보다 작아질 수 있는지</strong> 결정하는 속성이다.</li>
<li>flex-shrink에는 숫자값이 들어가며, <code>0</code> 보다 큰 값이 들어가면 해당 아이템이 flex-basis보다 작아진다.</li>
<li>기본값은 <code>1</code> 이기 때문에 적용하지 않았어도 아이템이 flex-basis보다 작다.</li>
<li>flex-shrink를 0 으로 적용하면 아이템의 크기가 flex-basis보다 작아지지 않기 때문에 고정폭의 컬럼을 만들 수 있다. 이때, 고정 크기는 width로 설정한다.</li>
</ul>
<pre><code>css
.container {
    display: flex;
}
.item:nth-child(1) {
    flex-shrink: 0;
    width: 100px;
}
.item:nth-child(2) {
    flex-grow: 1;
}</code></pre><h2 id="4-flex-grow-flex-shrink-flex-basis의-축약형-속성">4. flex-grow, flex-shrink, flex-basis의 축약형 속성</h2>
<p>서로 관련된 속성이기 때문에, 축약형을 쓰는 것이 편리하다. 단, 주의할 점은 flex: 1; 과 같이 flex-basis를 생략해서 쓰면 flex-basis의 값은 0 이다.</p>
<pre><code>css

.item {
    flex: 1;
    /* flex-grow: 1; flex-shrink: 1; flex-basis: 0%; */
    flex: 1 1 auto;
    /* flex-grow: 1; flex-shrink: 1; flex-basis: auto; */
    flex: 1 500px;
    /* flex-grow: 1; flex-shrink: 1; flex-basis: 500px; */
}</code></pre><p><strong>참고 자료</strong> <br>
<a href="https://studiomeal.com/archives/197">https://studiomeal.com/archives/197</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | 신규 아이디 추천]]></title>
            <link>https://velog.io/@e_soojeong/al</link>
            <guid>https://velog.io/@e_soojeong/al</guid>
            <pubDate>Thu, 04 Nov 2021 08:33:40 GMT</pubDate>
            <description><![CDATA[<h4 id="문제">문제</h4>
<p>로또의 최고 순위와 최저 순위 문제 확인 👉 <a href="https://programmers.co.kr/learn/courses/30/lessons/72410">https://programmers.co.kr/learn/courses/30/lessons/72410</a></p>
<p><strong>나의 풀이</strong></p>
<pre><code class="language-javascript">function solution(new_id) {
  // 1단계
  new_id = new_id.toLowerCase();
  // 2단계
  new_id = new_id.replace(/[^\w\.\-]/g, &#39;&#39;);
  // 3단계
  new_id = new_id.replace(/[\.]{2,}/g, &#39;.&#39;);
  // 4단계
  new_id = new_id.replace(/^\./, &#39;&#39;);
  // 4단계
  new_id = new_id.replace(/\.$/, &#39;&#39;);
  // 5단계
  if (!new_id) {
    new_id = &#39;a&#39;;
  }
  // 6단계
  if (new_id.length &gt;= 16) {
    new_id = new_id.slice(0, 15);
    new_id = new_id.replace(/\.$/, &#39;&#39;);
  }
  // 단계
  if (new_id.length &lt;= 2) {
    new_id = new_id.padEnd(3, new_id.split(&#39;&#39;).reverse().join(&#39;&#39;));
  }
  return new_id;
}</code></pre>
<p>문제를 보고, 1-7 단계를 하나씩 해결하자고 생각하였다.</p>
<p><strong>1단계</strong> 소문자로 치환 : toLowerCase()<br>
<strong>2단계</strong> 특정 문자 제거 : replace()<br>
<strong>3단계</strong> 특정 문자 치환 : replace()<br>
<strong>4단계</strong> 특정 문자 제거 : replace()<br>
<strong>5단계</strong> &#39;a&#39; 대입 : 직접 대입<br>
<strong>6단계</strong> 조건문, 특정 문자 제거 : slice(), replace()<br>
<strong>7단계</strong> 조건문, 특정 문자 추가(기존 문자열의 맨 끝) : padEnd(), split().reverse().join()<br></p>
<p>메서드들을 적용하기 전, array에 익숙해져 있어 String을 array안에 각각 담아서 적용하려고 했다. 하지만 array에 적용되는 메서드 말고 String에 적용할 수 있는 메서드를 찾아서 적용하였다.</p>
<p>그렇게 새롭게 알게된 javascript 메서드 <code>padEnd()</code>이었다. <code>padEnd()</code>, <code>padStart()</code>는 메서드는 현재 문자열의 시작을 다른 문자열로 채워, 주어진 길이를 만족하는 새로운 문자열을 반환한다. 메서드 이름에서도 추론할 수 있 듯이, <code>padEnd()</code>의 채워넣기는 대상 문자열의 끝(우측)부터 적용됩니다. <code>padStart()</code>의 채워넣기는 대상 문자열의 시작(좌측)부터 적용된다.</p>
<p><i>참고링크</i><br>
<b>padStart()</b><br>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/padStart">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/padStart</a> <br>
<b>padEnd()</b><br>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd</a></p>
<p><strong>다른사람 풀이</strong></p>
<pre><code class="language-javascript">function solution(new_id) {
  const answer = new_id
    .toLowerCase() // 1
    .replace(/[^\w-_.]/g, &#39;&#39;) // 2
    .replace(/\.+/g, &#39;.&#39;) // 3
    .replace(/^\.|\.$/g, &#39;&#39;) // 4
    .replace(/^$/, &#39;a&#39;) // 5
    .slice(0, 15)
    .replace(/\.$/, &#39;&#39;); // 6
  const len = answer.length;
  return len &gt; 2 ? answer : answer + answer.charAt(len - 1).repeat(3 - len);
}</code></pre>
<p>✏️ <strong>내 코드와의 차이점</strong></p>
<ul>
<li>모든 코드가 체이닝 기법으로 적용되었다.</li>
<li>정규 표현식을 사용하여 특정 문자를 제거하고, 치환하고, 대입하였다</li>
<li>나는 맨 마지막 문자열을 찾기 위해서 기존 문자열을 거꾸로 변환하여 반복하였다. 하지만 다른 사람의 코드에서는 맨 마지막, 7단계의 경우는 조건식을 return 하였다. 이때 적용된 메서드는 charAt(), repeat()로 적용하여 마지막 글자를 찾아서 반복되게 적용하였다.</li>
</ul>
<p><i>참고링크</i><br>
<b>charAt()</b><br>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/charAt">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/charAt</a>
<b>repeat()</b><br>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/repeat">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/repeat</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | H-Index]]></title>
            <link>https://velog.io/@e_soojeong/H-Index</link>
            <guid>https://velog.io/@e_soojeong/H-Index</guid>
            <pubDate>Fri, 22 Oct 2021 11:53:30 GMT</pubDate>
            <description><![CDATA[<p><strong>문제</strong></p>
<p>어떤 과학자가 발표한 논문의 인용 횟수를 담은 배열 citations가 매개변수로 주어질 때, 이 과학자의 H-Index를 return 하도록 solution 함수를 작성해주세요.</p>
<p><strong>제한사항</strong>
과학자가 발표한 논문의 수는 1편 이상 1,000편 이하입니다.
논문별 인용 횟수는 0회 이상 10,000회 이하입니다.</p>
<p><strong>입출력 예</strong></p>
<table>
<thead>
<tr>
<th>citations</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[3, 0, 6, 1, 5]</td>
<td>3</td>
</tr>
</tbody></table>
<p><strong>입출력 예 설명</strong>
이 과학자가 발표한 논문의 수는 5편이고, 그중 3편의 논문은 3회 이상 인용되었습니다. 그리고 나머지 2편의 논문은 3회 이하 인용되었기 때문에 이 과학자의 H-Index는 3입니다.</p>
<p><strong>풀이</strong>
문제를 이해 자체가 어려웠다. 아래 링크를 통해서 h-index에 대한 이해를 완료한 뒤 문제를 해결하니 이해할 수 있었다.
<a href="https://www.ibric.org/myboard/read.php?Board=news&amp;id=270333">https://www.ibric.org/myboard/read.php?Board=news&amp;id=270333</a></p>
<p>✏️ <strong>point</strong></p>
<ul>
<li>내림차순 정렬(인용된 횟수가 큰 순서로 정렬)</li>
<li>피인용수(citations(i))가 논문수(i)와 같아지거나 피인용수가 논문수보다 작아지기 시작하는 숫자가 바로 h(=answer)이다! </li>
</ul>
<pre><code class="language-js">function solution(citations) {

    let answers = 0;
    for (let i = 0; i &lt; citations.length; i++) {
        if (i &lt; citations[i]) {
            answers++;
        }
    }

    return answers;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS Attribute Selector(CSS 속성 선택자)]]></title>
            <link>https://velog.io/@e_soojeong/CSS-Attribute-SelectorCSS-%EC%86%8D%EC%84%B1-%EC%84%A0%ED%83%9D%EC%9E%90</link>
            <guid>https://velog.io/@e_soojeong/CSS-Attribute-SelectorCSS-%EC%86%8D%EC%84%B1-%EC%84%A0%ED%83%9D%EC%9E%90</guid>
            <pubDate>Fri, 15 Oct 2021 13:56:36 GMT</pubDate>
            <description><![CDATA[<p>SCSS로 UI를 스타일링하면서 공통의 attribute가 있다면 이를 통하여 스타일 속성을 지정하는 것이 편리할 것이라고 생각을 하였다. class, lang, href 등 다양한 attribute를 적용할 수 있다.</p>
<h2 id="속성-선택자-정리">속성 선택자 정리</h2>
<p>-<code>[attr]</code> : attr이라는 이름의 특성을 가진 요소를 선택합니다.
-<code>[attr=value]</code> : attr이라는 이름의 특성값이 정확히 value인 요소를 선택합니다.
-<code>[attr~=value]</code> : attr이라는 이름의 특성값이 정확히 value인 요소를 선택합니다. attr 특성은 공백으로 구분한 여러 개의 값을 가지고 있을 수 있습니다. 즉, 여러 개의 class가 함께 지정되어 있어도 attr를 특성값으로 가졌다면 선택합니다.
-<code>[attr|=value]</code> : attr이라는 특성값을 가지고 있으며, 그 특성값이 정확히 value이거나 value로 시작하면서 -(U+002D) 문자가 곧바로 뒤에 따라 붙으면 이 요소를 선택합니다. 즉, 시작하는 요소로 선택하되, 하이픈이 붙어있는 요소들도 선택합니다. 보통 언어 서브코드(en-US, ko-KR 등)가 일치하는지 확인할 때 사용합니다.
-<code>[attr^=value]</code> : attr이라는 특성값을 가지고 있으며, 접두사로 value가 값에 포함되어 있으면 이 요소를 선택합니다.
-<code>[attr$=value]</code>
attr이라는 특성값을 가지고 있으며, 접미사로 value가 값에 포함되어 있으면 이 요소를 선택합니다.
-<code>[attr*=value]</code> : attr이라는 특성값을 가지고 있으며, 값 안에 value라는 문자열이 적어도 하나 이상 존재한다면 이 요소를 선택합니다.
-<code>[attr operator value i]</code> : 괄호를 닫기 전에 i 혹은 I를 붙여주면 값의 대소문자를 구분하지 않습니다. (ASCII 범위 내에 존재하는 문자에 한해서 적용됩니다)
-<code>[attr operator value s]</code> : 괄호를 닫기 전에 s 혹은 S를 붙여주면 값의 대소문자를 구분합니다. (ASCII 범위 내에 존재하는 문자에 한해서 적용됩니다)</p>
<h2 id="속성-선택자-예제">속성 선택자 예제</h2>
<p><code>a</code> 태그를 예제로 보면,</p>
<p><strong>CSS</strong> </p>
<pre><code class="language-css">a {
  color: blue;
}

/* Internal links, beginning with &quot;#&quot; */
a[href^=&quot;#&quot;] {
  background-color: gold;
}

/* Links with &quot;example&quot; anywhere in the URL */
a[href*=&quot;example&quot;] {
  background-color: silver;
}

/* Links with &quot;insensitive&quot; anywhere in the URL,
   regardless of capitalization */
a[href*=&quot;insensitive&quot; i] {
  color: cyan;
}

/* Links with &quot;cAsE&quot; anywhere in the URL,
with matching capitalization */
a[href*=&quot;cAsE&quot; s] {
  color: pink;
}

/* Links that end in &quot;.org&quot; */
a[href$=&quot;.org&quot;] {
  color: red;
}

/* Links that start with &quot;https&quot; and end in &quot;.org&quot; */
a[href^=&quot;https&quot;][href$=&quot;.org&quot;] {
  color: green;
}</code></pre>
<p><strong>HTML</strong></p>
<pre><code class="language-html">&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#internal&quot;&gt;Internal link&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://example.com&quot;&gt;Example link&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#InSensitive&quot;&gt;Insensitive internal link&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://example.org&quot;&gt;Example org link&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://example.org&quot;&gt;Example https org link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</code></pre>
<p><strong>결과</strong>
<img src="https://images.velog.io/images/e_soojeong/post/6bbddfe2-b770-4a90-9db8-0432e03e5dbc/image.png" alt=""></p>
<p>참고문헌
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors">https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Core Javascript | 데이터 타입]]></title>
            <link>https://velog.io/@e_soojeong/Core-Javascript-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@e_soojeong/Core-Javascript-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85</guid>
            <pubDate>Fri, 08 Oct 2021 06:53:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>코어 자바스크립트를 보며 공부한 내용입니다.</p>
</blockquote>
<h1 id="1-데이터-타입의-종류">1. 데이터 타입의 종류</h1>
<h2 id="1-1-기본형primitive-type">1-1. 기본형(primitive type)</h2>
<p><strong>불변값을 가진다.</strong></p>
<ul>
<li>Number(숫자)</li>
<li>String(문자열)</li>
<li>Boolean(T,F)</li>
<li>null</li>
<li>undefine</li>
</ul>
<h2 id="1-2-참조형reference-type">1-2. 참조형(reference type)</h2>
<p><strong>가변값을 가진다.</strong></p>
<ul>
<li>Object(객체)<ul>
<li>Array(배열)</li>
<li>Function(함수)</li>
<li>Date(날짜)</li>
<li>RegExp(정규표현식)<h1 id="2-데이터-타입에-관한-배경-지식">2. 데이터 타입에 관한 배경 지식</h1>
모든 데이터는 바이트 단위의 식별자, 즉 <strong>메모리 주솟값</strong>을 통해 서로 구분하고 연결합니다.<h1 id="3-변수-선언과-데이터-할당">3. 변수 선언과 데이터 할당</h1>
<h2 id="3-1-변수">3-1. 변수</h2>
</li>
</ul>
</li>
<li><strong>변수</strong> : 변경가능한 데이터가 담길 수 있는 공간</li>
<li><strong>식별자</strong> : 그 변수의 이름</li>
<li><strong>변수 선언?</strong><ol>
<li>컴퓨터는 메모리의 빈 공간에 식별자 저장</li>
<li>그 공간에 자동으로 undefine 할당</li>
<li>그 변수에 기본형 데이터를 할 당하려고 하면 별도의 공간에 데이터를 저장</li>
<li>그 공간의 주소를 변수의 값 영역에 할당</li>
</ol>
</li>
</ul>
<h1 id="4-기본형-데이터와-참조형-데이터">4. 기본형 데이터와 참조형 데이터</h1>
<p><strong>기본형 데이터</strong></p>
<ul>
<li>모두 불변 값이다.</li>
<li>즉, 한번 만든 값은 변경이 불가능하며 변경은 새로 만드는 동작을 통해서만 이루어진다.</li>
<li>한 번 만들어진 값은 가비지 컬렉팅을 당하지 않는 한 영원히 변하지 않는다.</li>
</ul>
<p><strong>참조형 데이터</strong></p>
<ul>
<li>기본 적인 성질은 가변값인 경우가 많다.
: 객체의 변수(프로퍼티) 영역이 별도로 존재하기 때문에, 참조형 데이터는 불변하지 않다(가변값이다).</li>
<li>하지만, 모두 가변값일 것 같지만 설정에 따라 변경이 불가하거나 아예 불변 값으로 활용하는 방법이 있다.</li>
<li>중첩 객체(nested object): 참조형 데이터의 프로퍼티에 다시 참조형 데이터를 할당하는 경우.
이때, 참조형 데이터를 재할당하는 경우 참조 카운트가 0이 되면 메모리 주소는 가비지 컬렉터의 수거 대상이 된다.
-&gt; <strong>참조 카운트</strong> : 어떤 데이터에 대해 자신의 주소를 참조하는 변수의 개수
-&gt; <strong>가비지 컬렉터</strong> : 런타임 환경에 따라 특정 시점이나 메모리 사용량이 포화 상태에 임박할 때마다 자동으로 수거 대상들을 수거한다. 이때 수거된 메모리는 다시 새로운 값을 할당할 수 있는 빈 공간이 된다.</li>
</ul>
<p><strong>변수 복사 비교</strong></p>
<ul>
<li>변수 복사 과정은 기본형, 참조형 데이터 모두 같은 주소를 바라보게 되는 저에서 동일하다. </li>
<li>복사 과정은 동일하나, 데이터할당 과정에서 이미 차이가 있기 때문에 변수 복사 이후 동작 차이가 있다.</li>
<li><blockquote>
<p>자바스트립트의 모든 데이터 타입은 참조형 데이터이다.</p>
</blockquote>
</li>
<li><blockquote>
<p>기본형은 주솟값을 복사하는 과정이 한 번 만 이뤄지고, 참조형은 한단 계 더 거치게 된다.</p>
</blockquote>
</li>
<li><blockquote>
<p>참조형 데이터가 &#39;가변&#39;이라고 설명 할 때 &#39;가변&#39;은 참조형 데이터 자체를 변경할 경우가 아니라 내부 프로퍼티를 변경할 때만 성립하는 것이다.</p>
</blockquote>
</li>
</ul>
<h1 id="5-불변-객체">5. 불변 객체</h1>
<p>:값으로 전달받은 객체에 변경을 가하더라도 <strong>원본 객체는 변하지 않아야 하는 경우</strong> 필요</p>
<ul>
<li><strong>얕은 복사</strong>
: 바로 아래 단계의 값만 복사하는 방법</li>
<li><strong>깊은 복사</strong>
: 내부의 모든 값들을 하나하나 찾아 전부 복사하는 방법</li>
</ul>
<p>즉, 기본형 데이터의 경우 그대로 복사하면 되지만 <strong>참조형 데이터</strong>는 다시 그 내부의 프로퍼티를 복사하기 때문에 깊은 복사가 필요하다.</p>
<h1 id="6-undefined와-null">6. undefined와 null</h1>
<p>undefined와 null 은 <strong>없음을 나타내는 값</strong></p>
<ul>
<li><strong>undefined</strong> : 어떤 변수에 값이 존재하지 않을 경우
  다만, 본래의 의미에 따라 사용자가 없음을 표현하기 위해 명시적으로 undefined를 대입하는 것은 지양해야함.</li>
<li><strong>null</strong> : 사용자가 명시적으로 &#39;없음&#39;을 표현하기 위해 대입한 값
  주의점 ! type of null은 object이다. 따라서, 비교할 경우 동등연산자(==)이 아닌 일치연산자(===)을 써야 판별할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Core Javascript | 클래스]]></title>
            <link>https://velog.io/@e_soojeong/Core-Javascript-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@e_soojeong/Core-Javascript-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Wed, 06 Oct 2021 05:11:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>코어 자바스크립트를 보며 공부한 내용입니다.</p>
</blockquote>
<h1 id="0-들어가며">0. 들어가며</h1>
<p>** 자바스크립트는 프로토타입 기반 언어이다**
=&gt; &#39;상속&#39;의 개념이 없어 상속에 대한 니즈가 발생하였다.
=&gt; ES6에서 클래스 문법이 추가되었다.</p>
<h1 id="1-클래스와-인스턴스의-개념-이해">1. 클래스와 인스턴스의 개념 이해</h1>
<h2 id="1-1-클래스란">1-1. 클래스란?</h2>
<blockquote>
<p>어떤 사물의 공통 속성을 모아 정의한 추상적인 개념</p>
</blockquote>
<ul>
<li><strong>상위 클래스(superclass)</strong>
같은 클래스에서 상위(superior)의 개념을 갖는 것</li>
<li><strong>하위 클래스(subclass)</strong>
상위 클래스의 조건을 충족하면서 더욱 구체적인 조건이 추가된 것<h2 id="1-2-인스턴스란">1-2. 인스턴스란?</h2>
<blockquote>
<p>클래스의 속성이지니는 구체적인 사례</p>
</blockquote>
</li>
</ul>
<h2 id="1-3-프로그래밍-언어에서의-클래스">1-3. 프로그래밍 언어에서의 클래스</h2>
<ul>
<li><strong>공통 요소를 지니는 집단을 분류하기 위한 개념</strong></li>
<li>클래스가 먼저 정의 되어야 공통적인 요소를 지니는 개체들을 생성할 수 있다.</li>
<li>사용에 따라 추상적 or 구체적 개체가 될 수 있다.</li>
</ul>
<h1 id="2-자바스크립트의-클래스">2. 자바스크립트의 클래스</h1>
<ul>
<li><p><strong>프로토타입 메서드</strong>
인스턴스에서 직접 호출 할 수 있는 메서드</p>
</li>
<li><p><strong>스태틱 메서드</strong>
인스턴스에서 직접 접근할 수 없는 메서드
(생성자 함수를 this로 해야만 호출 가능)</p>
<p>1-3에서 언급한 &quot;사용에 따라 추상적 or 구체적 개체가 될 수 있다.&quot;는 것의 의미는 아래와 같다. </p>
<p>1) Class가 프로토타입 메서드의 역할을 할 때에는 추상적 개체가 되고
2) Class가 스태틱 메서드의 역할을 할 경우, 클래스 자체가 하나의 개체로 취급된다.</p>
</li>
</ul>
<h1 id="3-클래스-상속es6-이전">3. 클래스 상속(ES6 이전)</h1>
<h2 id="클래스-상속을-흉내내기-위한-방법-3">클래스 상속을 흉내내기 위한 방법 3</h2>
<p><strong>전제조건</strong> : constructor 프로퍼티가 원래의 생성자 함수를 바라보도록 조정해야한다.</p>
<ul>
<li>SubClass.prototype에 SuperClass의 인스턴스 할당 =&gt; 다음 프로퍼티를 모두 삭제</li>
<li>빈 함수(Bridge)를 활용하는 방법</li>
<li>Object.create를 이용하는 방법</li>
</ul>
<h1 id="4-클래스-상속es6">4. 클래스 상속(ES6)</h1>
<pre><code class="language-javascript">var Rectangle = class {
    constructor (width, height) {
        this.width = width;
        this.height = height;
    }
    getArea () {
        return this.width * this.height;
    }
};

//ES6 클래스 문법 도입
var Square = class extends Rectangle {
    constructor (width) {
        super(width, width);
    }
    getArea () {
        console.log(&#39;size is :&#39;, super.getArea());
    }
};</code></pre>
<ul>
<li><strong>class</strong> 명령어 뒤에 &#39;<strong>extends</strong>&#39;라는 키워드를 추가하면 상속관계 설정이 끝난다.</li>
<li>constructor 내부에서 <strong>super</strong> 라는 키워드를 함수처럼 사용할 수 있다. 이 함수는 SuperClass의 constructor를 실행한다</li>
<li>constructor 메서드를 제외한 다른 메서드에서는 super 키워드를 객체처럼 활용할 수 있다. 이때, 객체는 SuperClass.prototype을 바라보는데, 호출한 메서드의 this는 &#39;super&#39;가 아닌 원래의 this를 그대로 따른다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Core Javascript | 클로저]]></title>
            <link>https://velog.io/@e_soojeong/Core-Javascript-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@e_soojeong/Core-Javascript-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Sun, 03 Oct 2021 23:03:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>코어 자바스크립트를 보며 공부한 내용입니다.</p>
</blockquote>
<h1 id="1-클로저란">1. 클로저란?</h1>
<p>: 클로저는 함수와 그 함수가 선언될 당시의 Lexical environment의 상호관계에 따른 형상 <strong>(MDN)</strong></p>
<p>: 어떤 함수에서 선언한 변수를 참조하는 내부 함수에서만 발생하는 현상</p>
<p>: 외부 함수의 <u>LexicalEnvironment</u>가 <u>가비지 컬렉팅</u> 되지 않는 현상</p>
<ul>
<li>LexicalEnvironment : 실행 컨텍스트 객체가 활성화 되는 시점에 수집된 정보 중 하나. 함수 실행 도중 변경되는 사항이 즉시 반영됨.</li>
</ul>
<p>=&gt; 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 <strong><u>외부로 전달</u></strong>할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상</p>
<ul>
<li>외부로 전달하는 방법 
  1) return
 2) 콜백 함수로 전달</li>
</ul>
<h1 id="2-클로저와-메모리-관리">2. 클로저와 메모리 관리</h1>
<ul>
<li>메모리 소모는 클로저의 본질적 특성</li>
<li>메모리 누수 : 개발자의 의도와 달리 어떤 값의 참조 카운트가 0이 되지 않아 가비지 컬렉터(Garbage Collector)의 수거가 되지 않는 경우</li>
<li>더 이상 사용하지 않게 된 클로저에 대해서 메모리 소모가 되지 않도록 관리해 줄 필요가 있음.</li>
<li><strong>메모리 관리 방법</strong>
: 식별자에 기본 형 데이터(보통 null, undefined)를 할당</li>
</ul>
<h1 id="3-클로저-활용-사례">3. 클로저 활용 사례</h1>
<p>1) 콜백 함수 내부에서 외부데이터를 이용하고자 할 때</p>
<p>2) <strong>접근 권한 제어(정보 은닉)</strong> : 어떤 모듈의 내부 로직에 대해 외부로의 노출을 최소화해서 모듈간의 결합도를 낮추고 유연성을 높이고자 하는 현대 프로그래밍 언어의 중요한 개념 중 하나. </p>
<p>=&gt; 접근 권한에는 public, private, protected이 있다.</p>
<p>3) <strong>부분 적용 함수</strong> : n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억 시켰다가, 나중에 (n-m)개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있게끔 하는 함수</p>
<p>4) <strong>커링 함수</strong>: 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성한 것</p>
<p>필요한 인자의 개수만큼 함수를 만들어 계속 리턴해주다가 마지막에 조합해서 리턴한다.
다만 인자가 많아질수록 가독성이 떨어진다는 단점이 있다.</p>
<p><strong>ES6에서는 화살표 함수를 써서 간단하게 표기할 수 있다.</strong>
=&gt; ES6에서는 화살표 함수를 써서 같은 내용을 단 한 줄에 표기
=&gt; 이해가 쉽다.
=&gt; 각 단계에서 받은 인자들을 모두 마지막 단계에서 참조. 가비지 컬렉팅 되지 않고 메모리에 쌓였다가, 마지막 호출로 실행 컨텍스트가 종료된 후에야 비로소 한꺼번에 수거
=&gt; <strong>지연 실행</strong>에 유용 : 당장 필요한 정보만 받아서 전달하고, 또 필요한 정보가 들어오면 전달하는 식으로 결국 마지막 인자가 넘어갈 때까지 함수 실행을 미루는 것
=&gt; 활용 방법 : config 파일 작성</p>
<pre><code class="language-js">// 기존 커링 함수
var curry3 = function (func) {
  return function (a) {
    return function (b) {
      return func(a, b);
    };
  };
};

var getMaxWith10 = curry3(Math.max)(10);
console.log(getMaxWith10(8)); // 10
console.log(getMaxWith10(25)); // 25</code></pre>
<pre><code class="language-js">// ES6 화살표 함수
var curry = func =&gt; a =&gt; b =&gt; c =&gt; d =&gt; e =&gt; func(a, b, c, d, e); 
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Algorithm | K번째수]]></title>
            <link>https://velog.io/@e_soojeong/Algorithm-K%EB%B2%88%EC%A7%B8%EC%88%98</link>
            <guid>https://velog.io/@e_soojeong/Algorithm-K%EB%B2%88%EC%A7%B8%EC%88%98</guid>
            <pubDate>Thu, 16 Sep 2021 02:04:33 GMT</pubDate>
            <description><![CDATA[<h4 id="문제-설명">문제 설명</h4>
<p>배열 array의 i번째 숫자부터 j번째 숫자까지 자르고 정렬했을 때, k번째에 있는 수를 구하려 합니다.</p>
<p>예를 들어 array가 [1, 5, 2, 6, 3, 7, 4], i = 2, j = 5, k = 3이라면</p>
<p>array의 2번째부터 5번째까지 자르면 [5, 2, 6, 3]입니다.
1에서 나온 배열을 정렬하면 [2, 3, 5, 6]입니다.
2에서 나온 배열의 3번째 숫자는 5입니다.
배열 array, [i, j, k]를 원소로 가진 2차원 배열 commands가 매개변수로 주어질 때, commands의 모든 원소에 대해 앞서 설명한 연산을 적용했을 때 나온 결과를 배열에 담아 return 하도록 solution 함수를 작성해주세요.</p>
<h4 id="제한사항">제한사항</h4>
<p>array의 길이는 1 이상 100 이하입니다.
array의 각 원소는 1 이상 100 이하입니다.
commands의 길이는 1 이상 50 이하입니다.
commands의 각 원소는 길이가 3입니다.</p>
<hr>

<h4 id="첫번째-풀이--일단-답을-구하자">첫번째 풀이 : 일단 답을 구하자</h4>
<pre><code class="language-js">function solution(array, commands) {

  let result1 = array.slice(commands[0][0]-1, commands[0][1]).sort()[commands[0][2]-1];

  let result2 = array.slice(commands[1][0]-1, commands[1][1]).sort()[commands[1][2]-1];

  let result3 = array.slice(commands[2][0]-1, commands[2][1]).sort()[commands[2][2]-1];

  let answer = [result1, result2, result3];

  return answer;
}</code></pre>
<p>답은 출력되었지만, 정확성 테스트를 모두 통과하지 못했다.</p>
<h4 id="두번째-풀이--반복되는-부분을-줄이자">두번째 풀이 : 반복되는 부분을 줄이자</h4>
<pre><code class="language-js">function solution(array, commands) {
  let answer = commands.map(el =&gt; {
    return array.slice(el[0] - 1, el[1]).sort()[el[2] - 1];
  });

  return answer;
}</code></pre>
<p>첫번째 풀이에서 반복되는 것을 줄이고자 map을 사용하여 식을 도출했다.
하나를 제외하고 정확성 테스트 모두 통과하였다.
배열 매서드를 하나씩 확인하여 정확성을 테스트하던 중, sort메서드에서 문제점을 발견하였다.</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort">Array.prototype.sort()</a> <br>  문자열 대신 숫자를 비교하기 위해 compare 함수는 a에서 b를 뺄 수 있습니다. 다음 함수는 배열을 오름차순으로 정렬합니다 (Infinity 및 NaN이 포함되어 있지 않은 경우)</p>
<pre><code class="language-js">function compareNumbers(a, b) {
  return a - b;
}
</code></pre>
<h4 id="최종-풀이">최종 풀이</h4>
<pre><code class="language-js">function solution(array, commands) {
   let answer = commands.map(el =&gt; {
      return array.slice(el[0] - 1, el[1]).sort((a, b) =&gt; a - b)[el[2] - 1];
  });
  return answer
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Core Javascript | 콜백함수]]></title>
            <link>https://velog.io/@e_soojeong/Core-Javascript-%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@e_soojeong/Core-Javascript-%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98</guid>
            <pubDate>Sun, 12 Sep 2021 09:00:40 GMT</pubDate>
            <description><![CDATA[<p>코어 자바스크립트를 보며 공부한 내용입니다.</p>
<blockquote>
<p><strong>목차</strong></p>
</blockquote>
<ol>
<li>콜백함수란?</li>
<li>제어권</li>
<li>콜백함수는 함수다</li>
<li>콜백 함수 내부의 this에 다른 값 바인딩하기</li>
<li>콜백 지옥과 비동기 제어</li>
</ol>
<h1 id="1-콜백함수란">1. 콜백함수란?</h1>
<h2 id="콜백함수">콜백함수</h2>
<ul>
<li>다른 코드(함수 or 메서드)의 인자로 넘겨주는 함수</li>
<li>제어권도 함께 위임한 함수</li>
</ul>
<h1 id="2-제어권">2. 제어권</h1>
<p><strong>콜백함수의 제어권을 넘겨받은 코드가 갖는 제어권</strong></p>
<ul>
<li>콜백함수를 호출하는 시점을 스스로 판단해서 실행</li>
<li>콜백함수를 호출할 때 인자로 넘길 값과 순서에 대한 제어권</li>
<li>콜백함수의 this에 대한 결정
**     콜백함수의 this**
 1) 무엇을 바라볼지 정해져 있는 경우 ⇒ 해당 객체
 2) 정해지지 않은 경우 ⇒ 전역 객체
 3) 사용자 임의로 변경하고 싶은 경우 ⇒ bind 메서드 활용<h1 id="3-콜백함수는-함수다">3. 콜백함수는 함수다</h1>
*<em>콜백함수로 어떤 객체의 메서드를 전달하더라도, 그 메서드는 메서드가 아닌 함수로서 호출된다. *</em></li>
</ul>
<p>예제를 통해 살펴보면, </p>
<pre><code class="language-js">let obj = {
  vals : [1, 2, 3],
  logValues : function(v, i) {
    console.log(this, v, i)
  }
};
obj.logValues(1,2); //result01
[4,5,6].forEach(obj.logValues); // result02</code></pre>
<p><strong>결과</strong>
<img src="https://images.velog.io/images/e_soojeong/post/d32471d7-4097-4b9c-8d8a-3609f81f3fa1/Untitled.png" alt=""></p>
<p><strong>logValue는</strong></p>
<p>1) result01 ⇒ 메서드 (dot이 있으니 메서드로서 전달 : 함수로 호출됨)</p>
<ul>
<li>this : obj</li>
<li>v : 1</li>
<li>i : 2</li>
</ul>
<p>2) realt02 ⇒ forEach함수의 콜백함수</p>
<ul>
<li>this : 전역객체(Window)
<code>arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])</code>
  ✔️ _전역객체로 출력되는 이유 : obj를 this로 하는 메서드가 아닌 obj.logValues가 가리키는 함수만 전달 ⇒ 별도로 obj 인자를 지정하지 않는다면, obj와 직접적인 연관이 없다.</li>
<li>v : 4, 5, 6 (CurrentValue)</li>
<li>i : 0, 1, 2 (index)</li>
</ul>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">Array.prototype.forEach()</a> : forEach MDN</p>
<h1 id="4-콜백-함수-내부의-this에-다른-값-바인딩하기">4. 콜백 함수 내부의 this에 다른 값 바인딩하기</h1>
<p><strong>콜백 함수 내부의 this가 객체를 바라보게 하는 방법</strong></p>
<ul>
<li>클로저 : 전통적 방법</li>
<li>bind 메서드 이용하는 방법<h1 id="5-콜백-지옥과-비동기-제어">5. 콜백 지옥과 비동기 제어</h1>
<h2 id="동기와-비동기">동기와 비동기</h2>
<h4 id="1-동기와-비동기는-서로-반대의-개념이다">1. 동기와 비동기는 서로 반대의 개념이다.</h4>
</li>
<li>동기 : 즉시 처리가 가능한 대부분의 코드</li>
<li>비동기 : 별도의 요청, 실행 대기, 보류가 필요한 코드 (언제 코드가 실행될지 예측할 수 없는 코드)</li>
</ul>
<h4 id="2-자바스크립트는-동기적이다">2. 자바스크립트는 동기적이다.</h4>
<ul>
<li>호이스팅이 된 이후부터 코드가 작성한 순서(정해진)에 맞춰서 하나씩 동기적으로 실행된다.</li>
<li>호이스팅이란 ? var, function 등의 선언이 자동적으로 코드의 상단으로 옮겨지는 것</li>
</ul>
<h4 id="3-callback은-항상-비동기일까-nope">3. Callback은 항상 비동기일까? Nope!</h4>
<pre><code class="language-js">// 1. 동기 콜백 : 즉각적

//printImmediately  함수가 호이스팅 되면서 맨위에서 실행되고 1,3,hello,2 순으로 콘솔 확인
function printImmediately(print){
  print();
}
printImmediately(()=&gt;console.log(&#39;synchronous callback&#39;));

// 2. 비동기 콜백 : 언제 실행될지 예측할 수 없는 콜백
function printWithDelay(print, timeout){
  setTimeout(print,timeout);
}
printWithDelay(()=&gt;console.log(&#39;async callback&#39;), 2000); // 비동기</code></pre>
<p><a href="https://www.youtube.com/watch?v=s1vpVCrT8f4&amp;feature=youtu.be">드림코딩 by 엘리</a>의 강의를 참고하였습니다.</p>
<h2 id="콜백-지옥">콜백 지옥</h2>
<p>콜백 함수 안의 콜백함수는 콜백 지옥을 초래할 수 있다. 가독성이 떨어지며, 에러 처리의 어려움이 있다.</p>
<p><strong>해결 방법</strong>
<strong>1. 기명함수로 변환</strong> : 일회성 함수를 전부 변수에 할당해야하는 단점</p>
<p><strong>2. Promise(ES6)</strong> : new 연산자(Promise는 클래스이기 때문에 new 키워드 사용)와 함께 Promise 호출</p>
<ul>
<li>Promise : 비동기를 간편하게 처리할 수 있는 자바스크립트 내장 오브젝트</li>
<li>resolve, reject 라는 executor 콜백 함수를 인자로 받음</li>
<li>정해진 시간에 기능을 수행하고나서 정상적으로 기능 수행(resolve)이되면 → 코드 진행 / 그렇지 않을 경우 → Error 오브젝트 반환(reject)</li>
<li>state(오퍼레이션 수행 상태)
1) pending(지정한 오퍼레이션 수행하고 있는 중)
2) fulfilled(오퍼레이션 성공) or rejected(오퍼레이션 실패)</li>
</ul>
<p><strong>3. Generator(ES6)</strong> : * 이 붙은 함수가 Generator 함수</p>
<ul>
<li>Generator 함수 : Generator는 빠져나갔다가 나중에 다시 돌아올 수 있는 함수</li>
<li>Generator 함수 실행 ⇒ Iterator 반환 ⇒ Iterator 내의 next 메서드 호출 ⇒ Generator 함수 내 첫 번째 yield에서 함수 실행을 멈춤
⇒ 다시 next 메서드 호출하여 그 다음 yield에서 함수 실행을 멈춤 : 반복</li>
</ul>
<p>즉, 비동기 작업이 완료되는 시점마다 next 메서드 호출하여 Generator 함수 내부 소스가 위 → 아래로 진행된다.</p>
<p>자세한 셜명은 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/function*">function*</a> MDN 참고</p>
<p><strong>4. Promise + async + await</strong></p>
<ul>
<li>async : 비동기 작업을 수행하고자 하는 함수 앞에 표기</li>
<li>await : 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 표기</li>
</ul>
<p>⇒ Promise 에서 then 체이닝을 계속하다보면 코드가 난잡해지는 것을 방지</p>
<p><a href="https://youtu.be/aoQSOZfz3vQ">드림코딩 by 엘리</a> 의 강의를 참고하였습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Unit Test]]></title>
            <link>https://velog.io/@e_soojeong/Unit-Test</link>
            <guid>https://velog.io/@e_soojeong/Unit-Test</guid>
            <pubDate>Sun, 25 Jul 2021 08:33:43 GMT</pubDate>
            <description><![CDATA[<h2 id="✔️-checklist">✔️ CheckList</h2>
<blockquote>
<p>✔️ 테스트 자동화의 중요성
✔️ 시스템을 테스트하는 3가지 방법(<code>UI test</code>, <code>Integration Test</code>, <code>Unit Test</code>)
✔️ 유닛테스트의 중요성
✔️ 유닛테스트의 장점
✔️ Unit test를 구현할때 지켜야 하는 일반적인 원칙</p>
</blockquote>
<h2 id="테스트-자동화의-중요성">테스트 자동화의 중요성</h2>
<ul>
<li>메뉴얼 테스트 
👉🏻 서비스 오픈 전, 개발자가 아닌 타인이 모든 기능 &amp; 버튼을 누르며 확인하는 것
👉🏻 테스트 실행 속도가 늦고, 인력 소모가 크다
👉🏻 비용, 휴먼 에러 발생</li>
</ul>
<p><strong>따라서  테스트 자동화가 중요하다.</strong></p>
<h2 id="시스템-테스트-방법--testing-pyramid">시스템 테스트 방법  (Testing Pyramid)</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/b7f1b687-4966-4adc-9427-a25ddcb08ca0/image.png" alt="">
전체 테스트 비중을 아래와 같은 수치로 구현하는 것이 권장된다.</p>
<h3 id="1-end-to-end-testingui-testing-10">1. End-TO-End testing(UI testing) 10%</h3>
<p> 👉🏻 프론드, 백엔드 서버가 둘 다 필요하며, 인력 소모가 크다.
 👉🏻 프론트에서 활용함.
 👉🏻 UI test tool : cypress</p>
<h3 id="2-intergrating-testing---20">2. Intergrating testing - 20%</h3>
<p> 👉🏻 백은 백, 프론트는 프론트로 나누어 테스트한다. (포스트맨, httpie 등)
 👉🏻 프론트에서는 mock-up 데이터가 가져와지는 지 테스트하는 것이라고 생각하면 된다.</p>
<h3 id="3-unit-testing---70">3. Unit testing - 70%</h3>
<p> 👉🏻 코드로 코드를 테스트 한다. 가장 작은 단위인 함수(메서드) 단위로 확인한다.
 👉🏻 프론트 :  함수형 컴포넌트 단위로 테스트 코드를 짜서 실행한다. 제스트를 통해서 리액트 컴포넌트 확인을 할 수 있다. jest, enzyme
 👉🏻 백 : pytest, unittest</p>
<h2 id="unit-test-장점">Unit Test 장점</h2>
<ul>
<li><p><strong>유닛 테스트는 UI Test 또는 Integration Test 보다 테스트 비용이 적게든다.</strong>
: UI Test는 백엔드 서버와 프론트를 연동하여 사람이 직접 테스트하지만, 유닛 테스트는 사람이 스크립트로 한꺼번에 자동으로 실행하기 때문이다.</p>
</li>
<li><p><strong>유닛 테스트는 다른 테스트에 비해서 실행 속도가 빠르다.</strong>
: 유닛테스트를 활용하면 하루에도 배포를 여러번 할 수 있어 개발 및 배포 속도에 중요한 영향을 주기 때문에 개발할 때 최대한 활용하는게 좋다.</p>
</li>
<li><p><strong>중장기적으로 유지 보수가 쉽다</strong> 
<code>regression 테스트</code> : 이전에 통과했던 테스트 집합을 가지고 버그를 찾기 위해서 이전에 테스트 되었던 유닛테스트를 반복하는 것이다. regression 테스트도 반복적으로 수행하여 중장기적으로 유지 보수가 쉽다.</p>
</li>
<li><p><strong>버그 방지</strong> 
 유닛테스트를 잘 짜놓으면 유닛테스트가 되었던 코드에서는 버그가 거의 발견되지 않을 것이다.
 그 이후, 버그가 발견되는 경우는 유닛테스트가 없어서 발생하는 경우일것이다.
 따라서 발견된 버그에 대해서도 버그를 수정한 후 유닛테스트를 작성해놓으면 버그를 방지할 수 있다.  </p>
</li>
</ul>
<h2 id="unit-test의-일반-원칙">Unit test의 일반 원칙</h2>
<ul>
<li>테스트 유닛은 각 기능의 가장 작은 단위에 집중한다 : 함수, 메서드 </li>
<li>각 테스트 유닛은 반드시 독립적이어야 한다. </li>
<li>테스트가 빠르게 돌 수 있도록 만들기 위해 노력해야한다.</li>
<li>지금 사용하고 있는 툴이 개별테스트나 테스트 케이스를 어떻게 수행하는지 배워야한다.</li>
<li>그날의 코딩을 시작하기 전에 항상 풀테스트 슈트를 돌려야한다.(기존 테스트가 잘 돌아가는지 먼저 체크한다.)</li>
<li>모두가 공유하는 저장소(ex. 깃헙)에 코드를 집어 넣기 전 자동으로 모든 테스트를 수행하도록 훅을 구현한다.</li>
<li>코드를 디버깅할 때 먼저 새로운 테스트를 작성한다.</li>
<li>테스트 함수에는 길고 서술적인 이름을 사용해야한다.</li>
<li>테스트 코드의 다른 의미는 새로운 개발자를 위한 안내서이다. 누구나 이해하기 쉽게 작성하는 것이 좋다!</li>
</ul>
<blockquote>
<p>✏️ Unit test에 대해서 공부하면서 개인적으로 느낀 점은 작은 단위로 개발을 하고 그 단위 안에 버그가 있는지, 코드가 잘 돌아가는지 테스트를 하면서 작업을 진행하면 어디서 발생한 버그(문제)들인지 캐치할 수 있을 것 같다고 생각했다. <br/> 
✏️ 현재 나는 Unit test를 진행하진 않지만, 작업하는 내용들을 기능단위로 브랜치를 만들거나 컴포넌트화를 하는 것이 좋다고 생각했다. 이렇게 작업을 한다면 효율성도 높아지고, 코드들이 쌓인 이후에 어디서 문제가 발생한건지 알 수도 없는 미연의 상황을 방지할 수 있기 때문이다. (물론 버그잡는 일은 쉽진 않겠지만..🐛)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | Kakao 소셜 로그인]]></title>
            <link>https://velog.io/@e_soojeong/React-Kakao-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8</link>
            <guid>https://velog.io/@e_soojeong/React-Kakao-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8</guid>
            <pubDate>Sat, 24 Jul 2021 13:12:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://developers.kakao.com/docs/latest/ko/getting-started/sdk-js">Kakao Developers</a> 문서를 참고하였습니다.</p>
</blockquote>
<h1 id="시작-전-준비사항">시작 전 준비사항</h1>
<h2 id="1-내-어플리케이션-추가하기">1. 내 어플리케이션 추가하기</h2>
<blockquote>
<p>kakao developers에 들어가서 내 어플리케이션 👉🏻 어플리케이션 추가하기 👉🏻 정보입력</p>
</blockquote>
<p><img src="https://images.velog.io/images/e_soojeong/post/27796446-7834-4e49-b007-e6071262ed15/image.png" alt=""></p>
<p>해당 과정을 거치면 전체 어플리케이션에 내가 추가한 어플리케이션이 보인다.</p>
<h2 id="2-앱-키-확인하기">2. 앱 키 확인하기</h2>
<p>새로 만들어진 어플리케이션을 누르면 여러가지 정보들이 나오는데, 로그인 할 때 필요한 앱키를 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/7063dd54-43b5-4fc5-a680-ff199aea00ec/image.png" alt=""></p>
<h2 id="3-플랫폼-등록하기">3. 플랫폼 등록하기</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/00696d3d-1773-42cc-b9c5-bb40584e5769/image.png" alt=""></p>
<p>설정하고자하는 플랫폼을 선택한다. Web 플랫폼 등록을 선택했다. 그 이후 도메인을 등록해준다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/0183365f-2def-4ca1-ab93-98e7bd2a14e6/image.png" alt="">
❗️ 오류 페이지 ❗️ 오류의 이유는 도메인 주소 뒤에 <code>/</code>를 지우면 된다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/b0c94e27-2f28-4b9e-a897-d3e1aac24512/image.png" alt=""></p>
<p>✔️ 성공 페이지 ✔️ 프론트에서 보기 위해서는 localhost:3000 포트를 이용해야한다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/7fa87363-3942-4273-8e4f-6b9619081e51/image.png" alt=""></p>
<p><strong>등록이 완료되었다.</strong></p>
<h2 id="4-redirect-uri-등록">4. Redirect URI 등록</h2>
<p>여기서, Redirect URI를 등록 관련 시 궁금한 점이 생겼다. </p>
<p>나는 메인으로 설정해주기 위해서 localhost:3000/로 지정해주었다. 현재 백엔드와 연결하지 않고 로그인을 진행해보았을 땐, 일단 메인으로 이동 하지 않는다. </p>
<p>추가로, REST API를 이용할 경우엔 Redirect URI가 필수라고 카카오 개발자 문서에 적혀있다. Javascript로 구현하고 있는 나에게 해당하지 않는 이야기인지 아닌지 구분하기가 어렵다.</p>
<p>현재 내 경우에는 아직 access_token에 대한 Redirect가 없기 때문에 메인화면으로 등록했지만 반영이되지 않고 있다고 생각한다. 완벽한 통신이 진행되지 않아서 그런게 아닐까 생각해본다. ( 백엔드 통신 후 블로그에 다시 업데이트 예정이다!)</p>
<h2 id="5-로그인-활성화-설정">5. 로그인 활성화 설정</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/531ad09f-82be-441d-bd89-4064fb3bb988/image.png" alt=""></p>
<p>활성화를 ON으로 바꾸어 주면 아래와 같이 확인이 가능하다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/8ed91bcc-3fca-4828-ab83-fca1e51b5d31/image.png" alt=""></p>
<h2 id="6-kakao-sdk-for-javascript-설정하기">6. Kakao SDK for JavaScript 설정하기</h2>
<p>👉🏻 먼저, <code>&quot;react-kakao-login</code>을 npm install 한다.</p>
<p>👉🏻 Step 1.웹 페이지에 JavaScript SDK 포함한다.</p>
<pre><code class="language-html">
//index.html

&lt;script src=&quot;https://developers.kakao.com/sdk/js/kakao.js&quot;&gt;&lt;/script&gt;</code></pre>
<p>👉🏻 Step 2.초기화하기</p>
<p>[내 애플리케이션] &gt; [앱 키]에서 JavaScript 키를 확인합니다. 다음과 같이 JAVASCRIPT_KEY에 JavaScript 키를 할당하여 초기화 함수를 호출하고, 이어서 초기화가 잘 되었는지 확인하는 <strong>함수를 호출</strong>합니다.</p>
<p><strong>함수 호출 성공!</strong></p>
<p><img src="https://images.velog.io/images/e_soojeong/post/653c58c1-d714-4278-9493-ea9b863aa2ca/image.png" alt=""></p>
<h2 id="7-init">7. init</h2>
<pre><code> &lt;script&gt;
        // SDK를 초기화 합니다. 사용할 앱의 JavaScript 키를 설정해 주세요.
        Kakao.init(&#39;JAVASCRIPT_KEY&#39;);

        // SDK 초기화 여부를 판단합니다.
        console.log(Kakao.isInitialized());
    &lt;/script&gt;</code></pre><p>카카오 개발자문서에는 위와 같이 init을 해주고 있다.
나는 Login 에서 필요한 것이기 때문에 Login.js에서 적용을 해줄거다.</p>
<pre><code class="language-jsx">//Login.js

 useEffect(() =&gt; {
    window.Kakao.init(&#39;JAVASCRIPT_KEY입력&#39;);
  }, []);</code></pre>
<p>react hooks 를 사용하고 있어서 useEffect에 init설정을 적어주었다.</p>
<p>❗️ 여기서 window라는 위치를 설정해주기 전에는 함수 호출은 잘 되지만 토큰이 확인 안되는 문제를 발견했다...! 혹 동일한 문제를 겪고있다면     <code>window.Kakao.init</code>으로 접근해보자.</p>
<h2 id="8-버튼에-kakao-로그인-연동하기">8. 버튼에 Kakao 로그인 연동하기</h2>
<blockquote>
<p>참고 : <a href="https://developers.kakao.com/tool/demo/login/login?method=dynamic">https://developers.kakao.com/tool/demo/login/login?method=dynamic</a></p>
</blockquote>
<p><img src="https://images.velog.io/images/e_soojeong/post/7ec98516-a25c-4196-a208-b214e5ea9030/IMB_zLqTie.gif" alt=""></p>
<p>내가 선택한 구현 방법에 따른 코드를 확인할 수 있다. 코드를 복사하여 내 버튼과 연동시켜주면 된다 !</p>
<pre><code class="language-jsx">//Login.js
// 로그인 버튼 함수

const loginWithKakao = () =&gt; {
    window.Kakao.Auth.login({
      success: function (authObj) {
        console.log(authObj.access_token);
      },
      fail: function (err) {
        alert(JSON.stringify(err));
      },
    });
  };</code></pre>
<pre><code class="language-jsx">//Login.js
//로그인 버튼
&lt;LoginButton kakao onClick={loginWithKakao}&gt;
            &lt;i className=&quot;fas fa-comment&quot; /&gt;
            카카오톡으로 로그인
          &lt;/LoginButton&gt;</code></pre>
<h2 id="9-작동-완료-">9. 작동 완료 !</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/e9106268-21f1-4dee-825c-b6424870724d/IMB_hq2MSt.gif" alt=""></p>
<p>로그인 토큰도 잘 넘어온다! Redirect URI 부분에 대한 보충 설명, 피드백 받습니다🙏</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git - Rebase]]></title>
            <link>https://velog.io/@e_soojeong/Git-Rebase</link>
            <guid>https://velog.io/@e_soojeong/Git-Rebase</guid>
            <pubDate>Wed, 21 Jul 2021 00:33:18 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>git merge 명령어 대신 git rebase 사용기</p>
</blockquote>
<h2 id="goal">Goal</h2>
<ol>
<li><p>Git flow가 어떤 방식으로 운영 되는지 알고, main, develop, feature, release, hotfix 브랜치를 각각 구분하여 설명할 수 있다.
👉🏻 프로덕션 레벨의 코드도 개발 코드와 분리해서 관리해야하기 때문에 깃 브랜치 관리 전략이 생겨났고 이것은 = <strong>git flow</strong>이다.</p>
<p>👉🏻 메인, delvelop가 큰 줄기이다. feature, release, hotfix는 기능단위 브랜치, 출시를 앞둔 브랜치, 급하게 오류를 잡는 브랜치이다.</p>
</li>
<li><p>branch를 병합하는 두 가지 방식인 rebase와 merge의 차이점에 대해 설명할 수 있다.
👉🏻 rebase : 브랜치 + 브랜치 명령어 =&gt; main의 최신 업데이트 상태의 뒤로가서 붙는다. 
👉🏻 rebase를 사용하면 leaner한 이력관리가 가능해진다. 
👉🏻 불필요한 merge 커밋이 없다. history 이력은 사라지는 점을 참고해야한다.
👉🏻 rebase 명령어를 사용하여 불필요한 커밋을 하나로 squash 할 수 있다.</p>
</li>
</ol>
<h2 id="branch">branch</h2>
<p><strong>branch를 생성하는 이유?</strong>
코드가 서로 섞이지 않게 하기 위해서이다. 메인은 신성하니까...!</p>
<p>위에 짧게 기술하였지만, develop(개발단계)의 코드와 main을 나누어야한다. 왜냐하면 main에서 개발을 하다보면, 실제 사용하는 사용자들이 개발을 하는 단계에서 발생한 문제 때문에 이용을하지 못하기 때문이다.</p>
<h3 id="feature">feature</h3>
<p>큰 기둥 브랜치 2개는 develop, main이다. 기능 단위의 브랜치인 feature는 main에서 합치는게 아닌 develop에서 합친다.</p>
<h3 id="release">Release</h3>
<p>develop단계가 완료되면 Release브랜치로, 그 후 main에서 합친다.</p>
<h3 id="hotfix">hotfix</h3>
<blockquote>
<p>hotfix의 정의 
: 핫픽스(영어: hotfix)는 제품 사용 중에 발생하는 버그의 수정이나 취약점 보완, 또는 성능 향상을 위해 긴급히 배포되는 패치 프로그램</p>
</blockquote>
<p>main에서 바로 따서 급하게 고치는 것. 따라서 나중에 develop 브랜치에 Merge 시켜를 시켜준다.</p>
<h2 id="merge-vs-rebase">Merge VS Rebase</h2>
<h3 id="git---merge">Git - MERGE</h3>
<p>history가 중요한 경우 사용하는 명령어. 
서로 다른 브랜치가 병합될 경우, 커밋 남긴 시점 사이사이에 들어와서 merge가 되고 merge commit이 맨 뒤로 온다.</p>
<h3 id="git---rebase">Git - REBASE</h3>
<p>깔끔한 이력 관리를 위한 명렁어.
베이스 커밋의 위치를 다시 잡는다. 한쪽에 있는 브랜치를 다른 브랜치에 병합하는 것. Rebase는 이력을 깔끔하게 정리할때 사용할 수 있는 장점이 있다.</p>
<p>이 때, commit을 하면서 3-4개가 쌓이면 <strong>squash</strong>를 해서 정리해야한다. 너무 많은 커밋이 쌓이면 충돌이 커밋 개수만큼 일어나기 때문이다..! 방법은 아래와 같다.</p>
<ol>
<li>터미널에 아래와 같은 명령어를 입력한다.</li>
</ol>
<pre><code>git rebase -i main</code></pre><blockquote>
<p>여기서 i는 ?
-i : interactive</p>
</blockquote>
<ol start="2">
<li>명령어를 입력하면 아래와 같은 창이 뜬다.</li>
</ol>
<p><img src="https://images.velog.io/images/e_soojeong/post/f99de0bd-9ae8-4d81-bc32-5e1541894741/image.png" alt=""></p>
<p>커밋 메세지의 구조는 아래와 같다.</p>
<pre><code>pick 커밋해쉬 커밋메세지</code></pre><p>👉🏻 여기서 첫번째 메세지는 그대로 두고
👉🏻 두번째 메세지부터 pick 대신 s를 입력한다 </p>
<p> <strong>TMI</strong> 나는 s대신-s를 적어서... 안됐었다..ㅎㅎ 다시 
 <code>git rebase —-abort</code>를  입력하고 다시 되돌려서 s만 입력한뒤 <code>:wq</code>로 저장하고 커밋 메세지를 제외한 나머지를 저장한다.</p>
<blockquote>
<p><strong>Squash</strong>
: s 커밋을 하나로 합치는 명령어</p>
</blockquote>
<p>추가로, 내가 최종 push한 PR 이후에 수정사항(commit 4)이 생긴다면,
그 이후 main에서 pull 받고 commit 4를 추가해준다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/35f5cc34-ba23-41b9-86c5-d05007fddda8/image.png" alt=""></p>
<p>이후 push를 진행하게되면 main에 있는 브랜치와 다른 commit을 가지고 있기 때문에 push가 거절된다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/cacd77f0-565b-40bf-a2a3-941b35b824e3/image.png" alt=""></p>
<p>이때 push뒤에 -f를 붙여서 force한다. 기본적으로 쓰지 않지만 push 올라가 있는거랑 똑같고, 내가 명확히 commit4만 추가만 된거 아니까 이런 경우에는 -f를 사용한다!</p>
<p>즉, 내가 아는 내용에 한하여 리베이스에서 추가 수정은 -f 하면된다.</p>
<h2 id="git-rebase-충돌">Git rebase 충돌</h2>
<p>충돌 발생 시, 에디터에서 충돌 해결 후 git add . → git rebase —continue 반복하면된다. commit은 필요하지 않다.</p>
<pre><code>git add .</code></pre><pre><code>Git rebase --continue</code></pre><p>아직 merge가 더 익숙하지만 새로운 방법인 rebase를 사용하면서 어떤 부분에서 각자 장단점을 갖는지 체험해보아야겠다 ! 🏔</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS -  Frontend 배포]]></title>
            <link>https://velog.io/@e_soojeong/AWS-Frontend-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@e_soojeong/AWS-Frontend-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Sun, 18 Jul 2021 15:10:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프로젝트를 마무리하고 AWS를 통해 프로젝트 업로드 하기</p>
</blockquote>
<h1 id="1-aws-계정-생성">1. AWS 계정 생성</h1>
<p>먼저 AWS에 가입을 한다. 이때 신용카드 정보가 필요함으로 카드 준비 후, 가입 진행하는 것이 좋다.</p>
<h1 id="2-application-배포">2. Application 배포</h1>
<h2 id="👉🏻-로그인">👉🏻 로그인</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/c8f10627-4f52-40ab-8761-b79f48c45cb1/image.png" alt=""></p>
<p>로그인을 하면 관리 콘솔에서 AWS에 대한 전반적인 정보를 얻을 수 있다.</p>
<h2 id="👉🏻-region">👉🏻 Region</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/548f8ec8-cf04-4a01-9d2e-9ff12de2e7f9/image.png" alt=""></p>
<p>인스턴스를 시작하기 전, Region을 오하이오로 설정한다. 여러 조건들과 비교해보았을 때 오하이오가 가장 나은 옵션이라고 한다.</p>
<h2 id="👉🏻-프리-티어-서버-선택--ubuntu">👉🏻 프리 티어 서버 선택 : ubuntu</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/d9fb4023-6448-4832-9a55-325692beb36a/image.png" alt=""></p>
<p>우분투, 64비트를 선택한다. 명확히는 모르지만 우분투가 다른 OS비해 합리적이라고 이야기를 들었던것 같다.. 아시는 분 계시다면...! 알려주셔요 Why 우분투..?</p>
<h2 id="👉🏻-인스턴스-유형-선택">👉🏻 인스턴스 유형 선택</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/6d69768c-769b-44d9-a2a7-d94284f5befb/image.png" alt=""></p>
<p>CPU, 메모리 등 서버의 규모를 선택해야한다. 프리 티어로 진행할 예정이기 때문에, t2.micro에서 시작한다. EC2에서는 컴퓨터와 데이터 전송에 따른 과금이 되기 때문에 이 부분에 유의하여야 한다.</p>
<h2 id="👉🏻-인스턴스-구성">👉🏻 인스턴스 구성</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/2d3c10fd-e66d-4189-a4e1-e8b9b46e51f1/image.png" alt=""></p>
<ul>
<li>서브넷 : IP 주소 사용</li>
<li>퍼블릭 IP 자동 할당 : 활성화되면 자동으로 동적으로 퍼블릭 IP 자동 할당 (추후 고정 IP를 사용하고싶다면 엘라스틱 IP 설정을 해야한다고 한다)</li>
<li>종료 방식 : 중지</li>
<li>종료기능 방지 활성화 체크</li>
</ul>
<h2 id="👉🏻-스토리지-추가">👉🏻 스토리지 추가</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/6719e103-6b17-4a72-b078-d5808554309e/image.png" alt=""></p>
<ul>
<li>용량을 기본 8GB 보다 높이게되면 요금이 부과되기 때문에 기본으로 설정하여 진행</li>
</ul>
<h2 id="👉🏻-태그-추가">👉🏻 태그 추가</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/f129b996-3bfc-499b-8e38-b19666548bb1/image.png" alt=""></p>
<p>키와 값 형태로 태그를 정의한다. 
보통 키는 계정 이름, 값은 프로젝트 이름으로 한다.</p>
<h2 id="👉🏻-보안-그룹-설정">👉🏻 보안 그룹 설정</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/9be8c0ef-0001-4472-a720-3e031ca9b6e6/image.png" alt=""></p>
<p>EC2에서는 http, https 의 접속을 허용해야한다. 그러기 위해선 규칙 추가를 해야한다. 나는 포트범위 8000로 웹 서버를 돌려야하기 때문에 규칙을 추가해주었다. 이때 포트범위를 <strong>8000</strong>으로 적고, 소스를 <strong>위치 무관</strong>으로 설정해주어야한다.</p>
<h2 id="👉🏻-새로-계정을-만들었다면">👉🏻 새로 계정을 만들었다면</h2>
<p>키페어를 생성해야한다. 이때 키페어는 단 한번만 다운로드 할 수 있기 때문에 이점에 유의하여 안전한 곳에 저장해야한다고 한다!</p>
<h2 id="👉🏻-인스턴스-실행-">👉🏻 인스턴스 실행 !</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/3014d8cf-0772-4be7-b034-c0d6f45887bb/image.png" alt=""></p>
<h1 id="3-application-배포-프론트">3. Application 배포 (프론트)</h1>
<p>우리가 만든 프로젝트를 서버에 올리려면 어떻게 해야할까 ? </p>
<blockquote>
<ol>
<li>소스 코드가 필요하다.<ol start="2">
<li>git</li>
<li>npm</li>
</ol>
</li>
</ol>
</blockquote>
<h2 id="👉🏻-우분투-서버-열기">👉🏻 우분투 서버 열기</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/ab91c6c1-53f2-4ea7-b752-ae2b6caec8be/image.png" alt=""></p>
<p>AWS 폴더를 만들어 키페어를 저장해두고, 우분투 서버를 연다.</p>
<pre><code class="language-bash">ssh -i key이름.pem ubuntu@나의 aws ip주소</code></pre>
<h2 id="👉🏻-git-clone-하기">👉🏻 git clone 하기</h2>
<pre><code class="language-bash">git clone 프로젝트_repo_주소</code></pre>
<h2 id="👉🏻-우분투-서버에-node-npm-다시-설치하기">👉🏻 우분투 서버에 node, npm 다시 설치하기</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/d233862b-4b0e-4fd7-b246-cc93604210aa/image.png" alt=""></p>
<p>원래는 직접 node, npm 페이지에서 다운을 받아서 소스 코드에 적용시켰지만, 터미널에서는 그렇게 진행하기가 어렵기 때문에 명령어를 통해 npm을 설치해야한다. </p>
<pre><code class="language-bash">curl -sL https://deb.nodesource.com/setup_10.x | sudo bash -

sudo apt-get update  // 선택사항
sudo apt-get install nodejs</code></pre>
<p>node가 설치되었으니 npm도 설치 !</p>
<pre><code class="language-bash">cd 프로젝트_repo_주소
npm install</code></pre>
<p><img src="https://images.velog.io/images/e_soojeong/post/c70468ee-892b-49ad-a9c0-dad8a2459a1d/image.png" alt=""></p>
<p>버전을 확인해보니 잘 설치된 것 같으니, 해당 프로젝트에 필요한 dependecies들이 설치 되도록 npm install을 시작한다.</p>
<h2 id="👉🏻-npm-run-build">👉🏻 npm run build</h2>
<p>지금까지 실행한 npm start는 개발모드로, 콘솔로그나 디버깅을 하는 과정 중 브라우저의 스크립트에 모든 소스 코드가 나와있었다. 하지만 배포가 될 때는 소스코드가 다 노출되면 안되며 html, css, javascript 하나로 결과물이 나와야 한다. 즉 프로덕트 모드를 실행해야하는데 이때 npm run build를 해주어야 하는 것이다. </p>
<p><img src="https://images.velog.io/images/e_soojeong/post/cacbbdfa-86f0-421d-a669-c804ca61d736/image.png" alt=""></p>
<p>빌드 한 후, 확인을 해보면 build라는 폴더가 생겼고 이 안에는 하나의 html, css, javascript가 있다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/a79c9749-b09f-410e-bf3a-ac496856bfbb/image.png" alt=""></p>
<h2 id="👉🏻-npm-install-express-설치">👉🏻 npm install express 설치</h2>
<p>웹 서버는 백앤드의 영역이기 때문에, 프론드 앤드 용 웹서버가 필요하다. 따라서 node.js로 웹서버를 띄우려면 외부 라이브러리인 <code>npm install express</code>를 설치해야한다.</p>
<pre><code class="language-bash">npm install express --save</code></pre>
<p>package.json의 <code>dependencies</code>에 남겨야하니까 --save 잊지말기!!</p>
<h2 id="👉🏻-serverjs">👉🏻 server.js</h2>
<p>npm install express 설치만 한다고 되는 것이 아니다!</p>
<pre><code class="language-bash">vi server.js</code></pre>
<p>server 라는 이름은 임의로 설정할 수 있다.</p>
<pre><code class="language-bash">// insert 모드로 아래 코드 복사 붙여넣기

const http = require(&quot;http&quot;);
const express = require(&quot;express&quot;);
const path = require(&quot;path&quot;);

const app = express();

const port = 8000;

app.get(&quot;/ping&quot;, (req, res) =&gt; {
  res.send(&quot;pong&quot;);
});

app.use(express.static(path.join(__dirname, &quot;build&quot;)));

//어떤 경로로 들어오건 간에, build폴더 안의 index.html을 응답해라
app.get(&quot;/*&quot;, (req, res) =&gt; {
  res.set({
    &quot;Cache-Control&quot;: &quot;no-cache, no-store, must-revalidate&quot;,
    Pragma: &quot;no-cache&quot;,
    Date: Date.now()
  });
  res.sendFile(path.join(__dirname, &quot;build&quot;, &quot;index.html&quot;));
});

http.createServer(app).listen(port, () =&gt; {
  console.log(`app listening at ${port}`);
});</code></pre>
<p>⬆️ 백엔드 코드이지만, 자바스크립트로 되어있다. 브라우저에서 돌아가는 것이 아니라, 서버 환경에서 돌아가는 자바스크립트이다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/811163f6-8cb4-4b05-aa7a-86ec51fc0a00/image.png" alt=""></p>
<p>설치 완료 ! <code>cosnt port = 8000</code>은 인스턴스에서 설정한 포트 범위이다. 이렇게 하면 서버 코드가 준비가 되었다.</p>
<h2 id="👉🏻-build-후-run-server">👉🏻 build 후 run server</h2>
<p>node code를 실행하기 위해서는 node + 파일 경로를 입력해주면 된다.</p>
<pre><code class="language-bash">npm run build
node server.js</code></pre>
<p><img src="https://images.velog.io/images/e_soojeong/post/12bbb90f-6989-4323-9fbc-40402c92ac4a/image.png" alt=""></p>
<p>우와 ! 8000 띄우기 성공</p>
<h2 id="👉🏻-node-serverjs">👉🏻 node server.js &amp;</h2>
<p>터미널을 종료하게 되면 서버가 종료되기 때문에, 컴퓨터를 종료하더라도 해당명령하게 돌아갈 수 있게 처리를 해주어야한다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/77e9a520-80cd-449f-9bc6-d72d31648221/image.png" alt=""></p>
<p>현재 서버는 잘 연결되지만, 로그인 하기 전엔 제품 목록을 볼 때 보이지가 않는다. 이 부분에 대해서 리팩토링을 진행해보아야겠다 !! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[First-Project | Team Villy - 기능 구현💊]]></title>
            <link>https://velog.io/@e_soojeong/First-Project-Team-Villy-%EA%B5%AC%ED%98%84-%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@e_soojeong/First-Project-Team-Villy-%EA%B5%AC%ED%98%84-%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Sun, 18 Jul 2021 06:44:06 GMT</pubDate>
            <description><![CDATA[<h1 id="feature-view--code">Feature View &amp; Code</h1>
<blockquote>
<p><strong>Feature</strong></p>
</blockquote>
<ul>
<li>Login</li>
<li>Signup</li>
<li>Main</li>
<li>✅ Recommend</li>
<li>✅ Product</li>
<li>Product Detail</li>
<li>Cart
(나의 담당 페이지 : ✅ )</li>
</ul>
<p>내가 잘 하고 있는지 불안할 땐, 코드 한 줄 한 줄 뜯어보며 설명할 수 있는지 확인해보면 된다고 해주신 멘토님의 조언에 따라 코드 살펴보기 시작 !</p>
<h2 id="💊-1-main">💊 1. Main</h2>
<ul>
<li>Nav바 스크롤 진행 시 background color 변경</li>
<li>Main 하단 부분 슬라이더 구현(라이브러리 없이)</li>
</ul>
<p><img src="https://images.velog.io/images/e_soojeong/post/6e0d622c-7b6a-4c76-b62e-72f0d19b9115/IMB_TZdbkd.gif" alt=""></p>
<p><a href="https://velog.io/@choice/WECODE-Villy">Main의 코드는 여기로 !</a></p>
<h2 id="💊-2-sign-up">💊 2. Sign-up</h2>
<ul>
<li>이름, 전화번호, 이메일 ,비밀번호 총 4개의 Validation 구현</li>
</ul>
<p><img src="https://images.velog.io/images/e_soojeong/post/281cd6ed-cf9c-4ae5-94cb-d520252d12b4/IMB_aQZdU8.gif" alt=""></p>
<h2 id="💊-3-login">💊 3. Login</h2>
<ul>
<li>Validation 조건에 따른 Login 기능 구현</li>
<li>정상적으로 Login 진행 시, access_token 부여
<img src="https://images.velog.io/images/e_soojeong/post/8f30e24d-9ac3-4c8b-992e-11fea78fe148/IMB_znchh0.gif" alt=""></li>
</ul>
<h2 id="✅-4-recommend">✅ 4. Recommend</h2>
<ul>
<li>input value값을 받아 다음 component에 value값 전달</li>
<li>checkbox 값과 동일한 Product category url로 연결
<img src="https://images.velog.io/images/e_soojeong/post/4c454be4-8ffe-4672-898c-0b437459f454/IMB_II73pw.gif" alt=""></li>
</ul>
<p><strong>Recommend 컴포넌트는 아래와 같은 구조를 가지고 있다.</strong>
<img src="https://images.velog.io/images/e_soojeong/post/1f67f45d-b395-44e2-8e34-925dbaa848c2/Untitled%20(Draft)-4.jpg" alt=""></p>
<p>처음엔 SurveyWelcome 컴포넌트를 띄우고, 다음 버튼을 눌렀을 때 Survey01, Survey02, Survey03, Survey04 순으로 컴포넌트 교체가 필요했다. 그래서 먼저 각 컴포넌트를 <code>survey</code>라는 객체로 묶어 준 뒤, 작업을 시작하기로 결정했다.</p>
<p><strong>💻 코드 한줄 한줄 뜯어보기</strong>
<strong>✏️ Recommend.js (부모 컴포넌트) / render() 안 const 선언</strong></p>
<pre><code class="language-jsx">render() {
const survey = {
      1: (
        &lt;SurveyWelcome
          surveyId={this.state.surveyId}
          handleNextSubmmit={this.handleNextSubmmit}
        /&gt;
      ),
      2: (
        &lt;Survey01
          surveyId={this.state.surveyId}
          handleNextSubmmit={this.handleNextSubmmit}
          handlePrevSubmmit={this.handlePrevSubmmit}
          handleInput={this.handleInput}
        /&gt;
      ),
      3: (
        &lt;Survey02
          surveyId={this.state.surveyId}
          handleNextSubmmit={this.handleNextSubmmit}
          handlePrevSubmmit={this.handlePrevSubmmit}
          handleInput={this.handleInput}
          name={this.state.name}
        /&gt;
      ),
      4: (
        &lt;Survey03
          surveyId={this.state.surveyId}
          handleNextSubmmit={this.handleNextSubmmit}
          handlePrevSubmmit={this.handlePrevSubmmit}
          handleInput={this.handleInput}
          name={this.state.name}
        /&gt;
      ),
      5: (
        &lt;Survey04
          surveyId={this.state.surveyId}
          handlePrevSubmmit={this.handlePrevSubmmit}
          handleNextSubmmit={this.handleNextSubmmit}
          handleCheckBox={this.handleCheckBox}
          handleInput={this.handleInput}
          name={this.state.name}
          makeCondition={this.makeCondition}
          history={this.props.history}
        /&gt;
      ),
    };


  ....

}</code></pre>
<p>각 컴포넌트마다 필요한 데이터, 함수들을 전달해주었다. 순서가 뒤바뀌었지만 다음 페이지로 넘어 갈 수 있는 함수는 아래와 같다. ⬇️</p>
<pre><code class="language-jsx">handleNextSubmmit = surveyId =&gt; {
    const newAnswer = [...this.state.answer, { id: surveyId }];
    this.setState({
      answer: newAnswer,
    });
    this.handleNextButton(surveyId);
  };

  handlePrevSubmmit = surveyId =&gt; {
    const previousAnswer = this.state.answer.filter(previous =&gt; {
      return previous.surveyId !== surveyId;
    });
    this.setState({
      answer: previousAnswer,
    });
    this.handlPrevButton(surveyId);
  };

  handlPrevButton = id =&gt; {
    this.setState({ surveyId: id - 1 });
  };

  handleNextButton = id =&gt; {
    this.setState({ surveyId: id + 1 });
  };</code></pre>
<p>지금은 정렬된 듯 보이지만,, 처음에 구상할 때는 정말 머리 아픈 코드였다. 리팩토링 하기 전엔 <code>handleNextSubmmit</code> 함수를 concat으로 구현해서 <code>handlePrevSubmmit</code>가 그 반대라고 생각하니 splice, slice 등과같은 메서드만 생각하게 되었다. 그렇게 생각을 하다가 같은 팀 창원님께 좀 더 효율적인 방법이 없을까하고 여쭤보았는데, filter 매서드를 이야기 해주셨다. filter를 사용한다고 생각하니 <code>handleNextSubmmit</code> 함수도 concat이 아닌 spread 연산자가 생각이 나서 코드를 수정하였다!</p>
<p><strong>✏️ Recommend.js (부모 컴포넌트) / render() 안 return 값</strong>
<code>&lt;div className=&quot;SurveyBox&quot;&gt;{survey[this.state.surveyId]}&lt;/div&gt;</code> 안에서 해당 컴포넌트를 surveyId(1,2,3,4,5)를 그려주면 된다.</p>
<pre><code class="language-jsx">return (
      &lt;section className=&quot;Recommend&quot;&gt;
        &lt;h2 className=&quot;sr-only&quot;&gt;recommend&lt;/h2&gt;
        &lt;div className=&quot;surveyModal&quot;&gt;
          &lt;div className=&quot;surveyBox&quot;&gt;
            &lt;header className=&quot;surveyHeader&quot;&gt;
              &lt;Link to=&quot;/&quot;&gt;
                &lt;button className=&quot;closeButton&quot;&gt;
                  &lt;i class=&quot;fas fa-times&quot; /&gt;
                &lt;/button&gt;
              &lt;/Link&gt;
              &lt;div className=&quot;villyLogo&quot;&gt;Villy&lt;/div&gt;
              &lt;h1&gt;
                빌리!
                &lt;br /&gt;
                &lt;strong&gt;내 건강을 알려줘!&lt;/strong&gt;
              &lt;/h1&gt;
            &lt;/header&gt;

            &lt;div className=&quot;SurveyBox&quot;&gt;{survey[this.state.surveyId]}&lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/section&gt;
    );</code></pre>
<h2 id="✅-5-product">✅ 5. Product</h2>
<ul>
<li>ProductCard list를 통하여 데이터 받아오기</li>
<li>Product Card의 button에 장바구니(Cart) 담기 기능 구현</li>
<li>Product Category 기능 추가 구현
<img src="https://images.velog.io/images/e_soojeong/post/ca74cc43-88a6-4a9c-8544-dbe15947f2b2/IMB_kVbj1k.gif" alt=""></li>
</ul>
<p><strong>Product 컴포넌트는 아래와 같은 구조를 가지고 있다.</strong></p>
<p><img src="https://images.velog.io/images/e_soojeong/post/296a04dd-481f-4e09-b3fe-8f1764417e34/Untitled%20(Draft)-3%202.jpg" alt=""></p>
<p><strong>💻 코드 한줄 한줄 뜯어보기</strong></p>
<p><strong>✏️ Product.js (부모 컴포넌트) / import 부분</strong></p>
<pre><code class="language-jsx">// 컴포넌트 실행 전, import 해주어야 하는 것들에 대한 정리 !

//리액트에서 컴포넌트 요소를 import 할 때
import React, { Component } from &#39;react&#39;;

// API는 config라는 파일로 전체 관리
import { GET_PRODUCTS_API } from &#39;../../config&#39;;

//자식 컴포넌트 요소를 import 할 때
import ProductCard from &#39;./ProductCard/ProductCard&#39;;
import ProductCategory from &#39;./ProductCategory/ProductCategory&#39;;

// 공통으로 사용되는 함수를 분리한 뒤 import 할 때
import { makeCondition } from &#39;../../utils/productUtils&#39;;

// scss를 불러 올 때
import &#39;./Product.scss&#39;;</code></pre>
<p>여기서 가장 중요한 import라고 생각했던 부분은 <code>config.js</code>와 <code>makeCondition</code>의 import이다.</p>
<p><strong>1.<code>config.js</code> **
먼저, config.js는 API 주소를 관리하기 위한 파일이다. config.js이 존재하기 전에는 IP주소가 바뀔 때마다 fetch함수가 적용된 모든 곳에서 바꾸어 주어야 했는데 하나의 파일로 관리하니, 기초가 되는 IP주소만 바꿔 주면 fetch함수를 일일이 변경해줄 필요가 없어서 **효율</strong>이 정말 높아졌다.</p>
<p><strong>아래는 멘토님의 자세한 설명 !</strong></p>
<blockquote>
<p>API는 config.js 파일에서 일괄적으로 관리하면서 import, export 를 통해 사용하는 식으로 관리하는 것이 좋습니다. 그렇지 않을 경우, 백엔드 서버 IP 가 변경되면 fetch 함수를 일일이 찾아서 API 를 직접 수정해 주어야 하기 때문입니다.</p>
</blockquote>
<p>config.js 에서 관리를 하면서 import / export 를 사용하면 아래와 같이 구성이 될 것입니다.</p>
<p>2️⃣ config.js</p>
<pre><code class="language-jsx">// src/config.js
const BASE_URL = &#39;http://10.58.5.151:8000&#39;
export const GET_PRODUCT_API = `${BASE_URL}/products`

// 사용하는 컴포넌트
import { GET_PRODUCT_API } from &#39;../../../config.js&#39;;
...
fetch(`${GET_PRODUCT_API}/5`).then(...).then(...);</code></pre>
<p>위의 경우, 백엔드 IP 가 바뀔 때 config.js 에서 IP 만 바꾸어주면 모든 API 가 새로운 IP 에 따라서 변경됩니다. 그렇기 때문에 일일이 fetch 함수를 찾아 API 를 수정해줄 필요가 없습니다.</p>
<hr/>

<p><strong>2. <code>ProductUtils.js</code></strong>
<code>makeCondition</code>의 함수의 경우, ProductCategory에도 사용이 되지만, 이후 Recommend의 결과 값에도 사용되는 것을 발견하였다. 코드의 중복을 피하기 위해서 멘토님께서 해당 함수를 넣어둔 파일(<code>ProductUtils.js</code>)에서 export하여 필요할 때마다 import하는 방법에 대하여 알려주셨다.</p>
<p>심지어 나는 조건을 만드는 함수에서 바로 fetch를 하고 있었는데 멘토님께서 condition query를 만드는 부분과 fetch요청 및 setState를 하는 기능이 모두 포함되어있는 함수를 구현하는 것보다 fetch요청 및 setState를 하는 부분을 별도 함수로 따로 분리하여 사용하는 것이 좋다고 하셨다. </p>
<p>왜냐하면 함수를 기능별로 나누는 이유가 이렇게 작은 기능들로 다 나누고 각자 조합해서 활용하기 위함이기 때문이다.</p>
<p>1️⃣ Product.js (기존 코드)</p>
<pre><code class="language-jsx">//기존코드

  makeCondition = () =&gt; {
    const filterMatch = {
      bone: 1,
      hair: 2,
      growth: 3,
      skin: 4,
    };

    const filtered = Object.entries(this.state.filterState).reduce(
      (acc, [key, value]) =&gt; {
        if (!acc &amp;&amp; value) {
          return acc + `efficacy=${filterMatch[key]}`;
        }

        if (value) {
          return acc + `&amp;efficacy=${filterMatch[key]}`;
        }
        return acc;
      },
      &#39;&#39;
    );
    return filtered;
  };

  handleCheckBox = event =&gt; {
    const checkBoxName = event.target.name;
    const checkBoxNameState = !this.state.filterState[checkBoxName];
    this.setState(
      {
        filterState: {
          ...this.state.filterState,
          [checkBoxName]: checkBoxNameState,
        },
      },
      this.makeCondition
    );
  };
</code></pre>
<p>*<em>⬆️ 기존엔 이렇게 긴 함수를 컴포넌트와 함께 작성하였다면, *</em></p>
<p>1️⃣ Product.js (리팩토링 코드)</p>
<pre><code class="language-jsx"> // filtering을 위한 함수
  // query라는 변수에 makeCondition 함수(productUtils.js)에서 exprot한 return 값을 저장하여 불러온다.
  fetchFiltering = () =&gt; {
    const query = makeCondition(this.state.filterState);
    fetch(`${GET_PRODUCTS_API}?${query}`)
      .then(res =&gt; res.json())
      .then(data =&gt; {
        this.setState({
          productCard: data.message,
        });
      });
  };

  // checkbox에 event가 발생했을 때, fetchFiltering 함수에 담긴 내용이 진행되게 하는 함수
  // 각 input의 name의 이벤트의 state값을 클릭했을 때 바꾸어 준다.
  handleCheckBox = event =&gt; {
    const checkBoxName = event.target.name;
    const checkBoxNameState = !this.state.filterState[checkBoxName];
    this.setState(
      {
        filterState: {
          ...this.state.filterState,
          [checkBoxName]: checkBoxNameState,
        },
      },
      this.fetchFiltering
    );
  };</code></pre>
<p>*<em>⬆️ 좀 더 간결하게 필요한 값만 가지고 적용할 수 있다. *</em></p>
<p>2️⃣ productUtils.js
Product.js로 전달해줄 return 값을 export 해준다!</p>
<pre><code class="language-jsx">//category-filter-condition
export const makeCondition = filterState =&gt; {
  const filterMatch = {
    bone: 1,
    hair: 2,
    growth: 3,
    skin: 4,
  };

  const filtered = Object.entries(filterState).reduce((acc, [key, value]) =&gt; {
    if (!acc &amp;&amp; value) {
      return acc + `efficacy=${filterMatch[key]}`;
    }

    if (value) {
      return acc + `&amp;efficacy=${filterMatch[key]}`;
    }
    return acc;
  }, &#39;&#39;);

  return filtered;
};
</code></pre>
<p>실제 Recommend Feature에서 사용하려고 분리하였지만,, 마지막에 시간이 부족하여 Recommend에서는 저장하지 못했다. 또 변하지 않는 상수 값이라면 Ref를 활용하여 적용할 수 도 있는 부분이라는 조언을 들었다. 이미 분리된 파일이니까 Recommend 페이지에서 적용될 수 있도록 리팩토링해 볼 것이다!</p>
<p><strong>✏️ Product.js (부모 컴포넌트) / changeProductCard 함수</strong>
Product.js에서 만들어진 ProductCard들은 각각의 버튼을 가지고 있고 해당 버튼은 독립적으로 데이터를 전달해주어야 하는 상황이 발생하였다. 머리가 돌지 않을 땐..해야할 일이 무엇인지 순차적으로 적어 접근해본다. 그리고 무한 콘솔 ...!</p>
<blockquote>
</blockquote>
<ul>
<li>자식요소의 state를 부모에서 관리 해야한다.
왜냐하면, map함수를 통해 각 컴포넌트로 생성된 카드의 관리가 필요하다.</li>
<li>ProductCard 컴포넌트에서 몇 번째 ProductCard가 클릭되었는지 정보 전달이 필요하다.</li>
<li>index 값을 전달해주어 해결한다.</li>
</ul>
<p>1️⃣ Product.js</p>
<pre><code class="language-jsx"> changeProductCard = (productCard, index) =&gt; {
    const newProductCardList = [...this.state.productCard];
    newProductCardList[index] = productCard;
    this.setState({
      productCard: newProductCardList,
    });
  };</code></pre>
<p>어떻게 idnex를 넘겨줄수 있는지 고민해도.. 답이 나오지 않았다. 방법은 알지만 접근하지 못하는 상황에 멘토님께서 <code>newProductCardList[index]</code>로 접근하면 된다는 것을 알려주셨다. 아직도 <code>{}, []</code>를 활용하여 내가 원하는 데이터를 꺼내 쓰는것에 익숙하지 않는 나를 보며.. 참 많은 생각이 들었다. 그렇게 해서 결과를 확인해보면 !</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/88a579dd-b040-4a6a-9958-50d9c1cbac41/image.png" alt=""></p>
<p>해당 index를 가진 productCard의 데이터만 추출해낼 수 있다. <code>changeProductCard</code> 함수를 이제 자식요소인 productCard로 전달해주면 각각의 독립된 productCard에 대한 상태값 관리가 가능해진다.</p>
<p>1️⃣ ProductCard.js</p>
<pre><code class="language-jsx">addCart = event =&gt; {
    const { cart_exist } = this.props.productCard;
    if (cart_exist) {
      return;
    }

    event.preventDefault();
    const newProductCard = { ...this.props.productCard };
    newProductCard.cart_exist = true;
    this.props.changeProductCard(newProductCard, this.props.index);

    fetch(`${CARTLIST_API}`, {
      method: &#39;POST&#39;,
      headers: { Authorization: localStorage.getItem(&#39;access_token&#39;) },
      body: JSON.stringify({
        productID: event.target.name,
      }),
    });
  };</code></pre>
<p>새로 정의된 productCard의 state값을 가공하여 장바구니에 담김이라는 의미를 표현하는 &#39;cart_exist&#39;라는 키값에 따라 T/F에 따른 조건을 부여해주면 장바구니에 담는 기능이 가능해진다.</p>
<p>또한 부모 컴포넌트에서 정의한 <code>changeProductCard</code>함수는 productCard, index를 인자로 받기 때문에 이 부분을 함께 작성해주면 된다.</p>
<h2 id="💊-6-product-detail">💊 6. Product Detail</h2>
<ul>
<li>Product 페이지, 장바구니(Cart)와 데이터 연결
<img src="https://images.velog.io/images/e_soojeong/post/611f8859-55ed-4779-893c-d5d02384a7ac/IMB_a8xE3L.gif" alt=""></li>
</ul>
<p><a href="https://velog.io/@choice/WECODE-Villy">Product Detail의 코드는 여기로 !</a></p>
<h2 id="💊-7-cart">💊 7. Cart</h2>
<ul>
<li>Product에서 담긴 제품 확인</li>
<li>제품 개별 삭제, 전체 삭제 기능 구현</li>
<li>제품 수량 변경 가능
<img src="https://images.velog.io/images/e_soojeong/post/8d995033-5265-4853-8630-b416fb92e123/IMB_883xIz.gif" alt=""></li>
</ul>
<h2 id="💊-8-order">💊 8. Order</h2>
<ul>
<li>결제 완료 후, 해당 주문에 대한 잔여 금액 및 주문 번호 전달
<img src="https://images.velog.io/images/e_soojeong/post/1baf58f1-52f2-45d1-966d-815f5647da68/IMB_vrTtDo.gif" alt=""></li>
</ul>
<p><a href="https://velog.io/@choice/WECODE-Villy">Order의 코드는 여기로 !</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[First-Project  | Team Villy - 회고록 💊]]></title>
            <link>https://velog.io/@e_soojeong/First-Project-Team-Villy-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@e_soojeong/First-Project-Team-Villy-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Sat, 17 Jul 2021 14:05:23 GMT</pubDate>
            <description><![CDATA[<h1 id="team-villy💊">Team Villy💊</h1>
<p>나의 첫 번째 프로젝트가 끝났다. 지난 주, 짧은 코드 리뷰 이후 매일 마주하는 에러와 작동되지 않는 기능들을 마주하며 치열한 시간을 보냈다. 그럼에도 매일 가족처럼 다독이며 함께한 <span style="font-weight: bold">프론트 창원님, 윤경님, 백엔드 태영님, 지우님, 명준님</span>께 감사의 인사를 드리며, 회고 시작!</p>
<h1 id="💊-clone--pilly">💊 Clone : Pilly</h1>
<ul>
<li>영양제 추천, 구독 사이트</li>
<li>사용자의 데이터를 통해 영양제를 추천해주는 서비스 제공 및 구매가 가능한 사이트</li>
</ul>
<h2 id="👉🏻-introduction">👉🏻 Introduction</h2>
<ul>
<li>기간 : 21.07.05 - 21.07.16</li>
<li>구성 : Front-end 3명(<strong>이수정</strong>, 이윤경, 최창원), Back-end 3명(김태영, 설지우, 최명준)</li>
<li><a href="https://github.com/wecode-bootcamp-korea/22-1st-Villy-backend">Villy-backend</a></li>
<li><a href="https://github.com/wecode-bootcamp-korea/22-1st-Villy-frontend">Villy-frontend</a></li>
</ul>
<h2 id="👉🏻-technologies">👉🏻 Technologies</h2>
<h3 id="all">All</h3>
<ul>
<li>Git, Github</li>
</ul>
<h3 id="communication">Communication</h3>
<ul>
<li>Slack</li>
<li>Trello</li>
</ul>
<h3 id="front-end">Front-end</h3>
<ul>
<li>HTML</li>
<li>React</li>
<li>Javascript, JSX </li>
<li>SCSS, CSS</li>
</ul>
<h3 id="back-end">Back-end</h3>
<ul>
<li>Python</li>
<li>Django Web Framework</li>
<li>AWS EC2, RDS, S3</li>
<li>MySQL</li>
</ul>
<h2 id="👉🏻-features">👉🏻 Features</h2>
<h3 id="front-end-1">Front-end</h3>
<ul>
<li>Login</li>
<li>Signup</li>
<li>Main</li>
<li>✅ Recommend</li>
<li>✅ Product</li>
<li>Product Detail</li>
<li>Cart
(나의 담당 페이지 : ✅ )<h2 id="back-end-1">Back-end</h2>
</li>
<li>Users </li>
<li>Products</li>
<li>Carts</li>
<li>Orders</li>
</ul>
<h1 id="프로젝트-진행-과정">프로젝트 진행 과정</h1>
<blockquote>
<p>1차 Sprint-&gt; 1차 Sprint 회고 -&gt; 2차 Sprint -&gt; 발표</p>
</blockquote>
<h2 id="1주차-sprint">1주차 Sprint</h2>
<p>우리는 모든 것을 클론하기보단, 배운 내용에 대해 적용할 수 있는 부분들만 추려서 클론을 하기로 과감하게 결정하였다. 부분에 대해서 프론트 3인의 생각이 동일해서 참 다행이라는 생각이 들었다.
또한 클론할 사이트를 보면서 <strong>우리가 유저라면?</strong> 이라는 고민을 하여 장바구니에 제품을 담고 적용되는 UI, 장바구니 페이지에서 개별 삭제 기능 추가 등 새로운 기획을 하여 좀 더 나은 UX를 제공하고자 하였다.
우리 팀은 <strong>Trello</strong>를 사용하여 스케줄을 관리하였다. 처음엔 어떤 단위로 티켓을 나누어야하는지 몰라서 우리의 방식대로 진행하다가 프로젝트를 진행하면서 기능별로 세세하게 나누고, <code>오늘 할일 / Block 요소 / 내일 할일 / 어제 한 일</code>을 각 티켓 안에 적어두고 list로 관리하였다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/dd3b8683-f789-4904-bbe7-f55ecc95f499/image.png" alt=""></p>
<h3 id="🎯-목표">🎯 목표</h3>
<p><strong>필수 구현</strong></p>
<ul>
<li>로그인, 회원가입</li>
<li>Product 리스트, Product 필터</li>
<li>상세페이지</li>
<li>주문</li>
</ul>
<p><strong>추가 구현</strong></p>
<ul>
<li>결제</li>
<li>추천 제품</li>
</ul>
<h2 id="1주차-sprint-회고">1주차 Sprint 회고</h2>
<p>프로젝트 1주차 금요일에 우리가 진행 중인 내용에 대해서 전반적으로 돌아보는 시간을 가졌다. 
처음엔 이런 시간을 가질 시간에 기능 하나 더 구현하는게 낫지 않을까 생각했지만, 1주차 Sprint 회고를 마치고 나서 나는 <strong>✨ 명확하게 깨달았다. 속도도 중요하지만 방향도 중요하다는 것을 말이다!</strong> 이 부분에 있어서는 실질적 PM이신 창원님께 무한 감사를 드린다. 중간에 되돌아 보는 시간이 없었다면 프로젝트가 하나로 합쳐지는 과정에서 성공적으로 마무리되기 어려웠을 것 같다.</p>
<h3 id="✏️-1주차-스프린트에서-느낀점">✏️ 1주차 스프린트에서 느낀점</h3>
<p>다른 팀원과는 다르게 나는 하나의 페이지 구현에 너무 긴 시간을 지체하고 있다고 판단했다. 브랜치도 너무 오랫동안 열려있어서 최종 Merge를 받는데도 오래 걸렸다. 그래서 내가 내린 결론은 페이지를 구현하면서 <strong>branch 관리</strong>를 잘 해야겠다고 느꼈다.</p>
<ul>
<li>레이아웃 Branch</li>
<li>해당 레이아웃에 필요한 기능별 Branch (여러 개가 될 수 있음)</li>
</ul>
<p>이후 Branch를 구분해서 진행하니 빠르게 Merge가 될 수 있었고 더 명확하게 각 브랜치에서 어떤 기능을 구현하고 있는지 알 수 있었으며, 작업의 효율도 높아졌다.</p>
<h2 id="2차-sprint">2차 Sprint</h2>
<p> 우리 팀은 너무 무리하지 않은 목표 때문이었을까, 2차 스프린트를 시작하는 주에 상대적으로 여유가 있어 추가 구현으로 미뤄둔 사항들을 진행하기로 결정했다.</p>
<p>❗️ 하지만 서로 담당한 티켓이 아니면 전체적인 진행상황을 알 수 없다는 멘토님들의 피드백을 받고 우리 팀은 매일의 회의록을 적어 기록하기로 했다. 이렇게하면 회의록 안에 서로가 담당하는 (백, 프 모두) 내용을 알 수 있어서 좋았다.
<img src="https://images.velog.io/images/e_soojeong/post/7a33b536-4a29-452c-9333-0d37f3a1cf79/image.png" alt=""></p>
<h3 id="✏️-2주차-스프린트에서-느낀점">✏️ 2주차 스프린트에서 느낀점</h3>
<p>매일매일 Daily Stand-Up meeting을 진행한 것이 큰 힘이었을까, 각 프론트도, 백도 포지선 별로 어떻게 상황이 진행되고 있는지 알고 있었기에 추가 구현을 한다고 하여도 서로에게 무리 없는 상황임을 인지하게 되었다. 이 과정에서 <strong>속도를 맞추어서 백은 구현하였지만, 프론트가 구현하지 못한일 혹은 프론트는 구현하였지만 백의 데이터가 부족하다</strong>거나 하는 상황은 오지 않았다. 이 과정에서 정말 소통이 중요하고 공동의 목표를 공유하면서 앞으로 나가야한다는 것을 배웠다.</p>
<h1 id="프로젝트를-마치며">프로젝트를 마치며</h1>
<h2 id="1-협업">1. 협업</h2>
<ul>
<li><p>프로젝트는 처음이라 1차 프로젝트 조와 사이트가 발표될 때 참 많이 떨렸다. 이러한 설렘보다 걱정이 앞섰다. 그 걱정 속에서 내가 선정한 사이트인 <strong>필리</strong>로 우리 팀원들을 만나게되었을 때도 어떻게 이야기를 해 나갈까 고민했었지만, 지금 돌이켜보면 그런 고민은 왜했을까 싶을 정도로 팀원들과 끈끈해져있었다. <br/><br/></p>
</li>
<li><p>지금도 백엔드의 모든 것을 알 수 없지만, 어떻게 데이터를 만들고 전달하는지에 대한 시야가 생겼다. 특히 나는 Product를 담당하면서 정말 여러가지 데이터를 백으로부터 받았어야했다. 내가 만든 목데이터를 가지고 바로 적용될 수 있도록 데이터 type을 잘 맞춰 주셔서 정말 일부 <code>key</code> 값만 변경하면 데이터가 들어올 수 있었고 그때 참 짜릿했었다. 이전에 혼자 작업을 했다면 이 부분이 당연하지..! 라고 생각했을 수 있겠지만, <strong>이젠 안다. 그렇게 진행되기까지는 누군가가 여러 고민과 노력 끝에 만들어준 데이터임을!</strong> <br/><br/></p>
</li>
<li><p>솔직하게 말해서 내가 할 수 있는 역량보다 너무 어려운 페이지를 맡게된 것 같아 매일이 불안하고 자신감이 떨어져있었다. 그때마다 <strong>나를 다독여주셨던 팀원분들과 할 없는건 없다면서..! 되게하면된다고 이야기 주셨던 연욱멘토님</strong> 덕분에 당근과 채찍으로 페이지를 구현할 수 있었다. 이러한 감정들은 솔직하게 말해서 개발자로 다이브하기 전까진 내가 느끼지 못했던 감정이었다. 새로운 것들이 매일 넘쳐나는 개발 일을 하면서, 당장 눈앞의 2차 프로젝트를 하면서 나의 부족함과 한계를 느끼겠지만 그때마다 이번 프로젝트에서 얻어낸 힘을 떠올릴 것이다.</p>
</li>
</ul>
<h2 id="2-개발자다움">2. 개발자다움</h2>
<ul>
<li><p>개발 일을 하기로 마음 먹었다지만 나는 그 무엇 하나에 익숙하지 않았다. 처음 사용하는 터미널 명령어도, github도 익숙해지면 편할 것을 나는 되려 나에게 어색한 친구들이라고 생각하면서 다가가지도 않으려했었다. 하지만 프로젝트를 하면서는 <strong>&quot;효율&quot;</strong> 이라는 것에 대하여 고민을 하게되었다. 조금 더 효율적으로 소통하는 방법, 조금 더 효율적으로 코드를 짜는 방법을 말이다. 사고가 개발자다움으로 변화하고 있는 &#39;나&#39;이다.<br/><br/></p>
</li>
<li><p>누군가와 내가 가진 것을 공유한다는 것에 나는 벽을 느끼고 있었다. 위코드에서 한 달이 지나도 누구의 코드를 본다거나 하는 것은 무언가 훔쳐보는(?) 기분이 들어서 또 보잘 것 없는 내 코드를 누군가에게 공유한다는 건 참으로 큰 용기가 필요했다. 하지만, 이번 프로젝트를 하면서는 내 코드를 팀원, 멘토님과 쉐어를하면서 어떤 부분이 피드백이 필요하고 더 나은 방법인지, 해결할 수 있는 방법은 무엇인지에 대해서 배우게 되었다. 개발자의 <strong>공유 문화</strong>에 어색 하지만 다가가고 있다.</p>
</li>
</ul>
<h2 id="3-개인적으로-잘한-점">3. 개인적으로 잘한 점</h2>
<ul>
<li><p><strong>PR에서 내가 내가 진행한 일을 Commit 메세지와 함께 정리한 점</strong>
프로젝트를 진행하면서 함께 공유해야하는 부분이 많을 것이라고 생각했다. 따라서 git PR 관리가 프로젝트를 할 때 중요할 것이라고 판단하여 최대한 날짜별로 무엇을 했는지 적었다. 이렇게 작성하고 나니 일목 요연하게 무엇을 하였는지 알 수 있어서 편리하였다.
<img src="https://images.velog.io/images/e_soojeong/post/b7cb44d4-d74c-4bc9-90c9-0d30eafdca21/IMB_XKyIdj.gif" alt=""></p>
<p>그리고 연욱멘토님의 정말 꼼꼼한 리뷰를 받고 감사하다는 생각이 마구 들어 최대한 빠르게 리뷰를 반영하려고 노력했다. 그래야 흐름이 끊기지 않고 하나씩 완성해갈 수 있다고 판단하였기 때문이다.</p>
</li>
<li><p><strong>도움청하는 일에 두려워하지 않은 점</strong>
나는 개인적으로 도움을 청하는 일이 어려웟는데, 프로젝트의 특수성은 이러한 나의 어려운 점을 상쇄시켜버렷다. 스스로 처리하기 위해서 오래 잡고 있으면 프로젝트라는 <strong>&#39;기간&#39;</strong> 안에 담당한 업무를 처리할 수 없다는 것을 잘 알 고 있었다. 그래서 정말 처리가 되지 않는 일들, 이해가 되지 않는 것들에 대해서는 적극적으로 물어보고 해결하려고 하였다. 이 부분은 잘한 점이기도 하지만 개인의 발전을 위해서는 시간이 있었더라면 나도 할 수 있었을 텐데.. 하는 아쉬운 생각도 드는 부분이다! </p>
</li>
<li><p><strong>어떤 개발자가 될 것인가?</strong>
<a href="https://velog.io/@e_soojeong/As-a-newbie-front-end-developer">위코드에서의 한달 회고</a>에서는 나는 어떤 개발자가될 것 인가에 대해서 결론 내리지 못한 상태였다. 하지만 이번 프로젝트를 마치고 준식님의 개발자 이야기 세션을 듣고 머릿 속이 정리되는 기분을 느꼈다. 세션의 내용 중, 프론트엔드 개발자는 사용자와 가장 가까운 개발자라고 이야기를해주셨고, 이것이 나에게 개발일의 확신을 주었다.</p>
<p>✏️ <strong>당신의 이야기로부터 동기부여가 되는 프론트엔드 개발자</strong></p>
<p>이런 생각이 머릿 속에 들자 이력서에도 어떻게 나를 소개하면 좋을까 고민했던 부분이 클리어해졌다!</p>
</li>
</ul>
<h2 id="4-개인적으로-아쉬운-점">4. 개인적으로 아쉬운 점</h2>
<ul>
<li>적극적으로 소통하지 못한점
나는 내가 맡은 업무와 배운 점들을 적용하는데 급급하여 백엔드 분들과 개발에 필요한 데이터(?)가 오가는 부분에 대하여 소통을 하지 못해서 아쉬웠다. 무엇이 연결되어야 하는지도 모른채 일단 연결을 시키고 몇몇 키값을 바꿔 두었더니 참 감사하게도 예쁘게 데이터가 들어왔더랬다...
물론 문제 사항 없이 들어오는 것은 참 좋은 일이지만, 원활한 소통이 가져다 주었다기 보단 나에게 맞는 데이터를 만들어주신 것에 대하여 감사함 반, 미안함 반의 감정이 들어서 다음 프로젝트에서는 좀 더 데이터 통신에 적극적으로 소통해볼 것이다! <br/><br/></li>
<li>끝까지 포기하지 못한점
카테고리 부분에서 시간을 많이 소요한 탓에 추천제품(서베이)이라는 설문조사 페이지를 구현하는데 시간이 부족했다. 사실 우리 팀의 계획은 목요일부터는 모두 완료하고, 서로 코드리뷰 하면서 이해하는 시간을 갖기로 결정을 했었다. 하지만 내가 Pilly라는 사이트를 선정한 이유인 <strong>추천제품</strong>을 구현하지 못한다는 아쉬움이 커서 <strong>포기</strong>하지 못하고 계속 develop을 진행했다. 시간이 부족하다면 이 부분을 깔끔하게 포기하고 개인적으로 구현하고 싶은 사항으로 정리하였어도 될 텐데 그렇지 못하고 또 능력은 되지 않아서 기능은 적용되지 않아서 마지막 Merge와 겹치는 상황에서 수많은 감정이 오갔다. 이 부분에 대해서 포기하려고 할 때, 너무 감사하게도 창원님께서 도움을 주셔서 결국 데이터를 받아오는 것에 성공을 했다. 
기능이 된다는 것에 행복했지만, 나의 욕심으로 인해서 팀원들과 계획한 일들을 함께하지 못했다. 또 온전히 나의 힘으로 구현하지 못한 것에 대한 아쉬움도 있었다. 프로젝트를 진행하면서 과감하게 결단을 내려야한다는 점을 배웠던 시간이었다.</li>
</ul>
<h2 id="5-💊-villy-팀원들에게">5. 💊 villy 팀원들에게</h2>
<p>프로젝트 발표를 마치고도 힘들었을텐데 모두 모여 회고 미팅까지 진행한 villy 팀원들은 나에게 참 강한 인상을 남겼다. 이 프로젝트가 끝이 아니고, 우리의 앞으로를 위해서 또 다시 회고를 진행한 우리 팀원들은 참 멋지고 존경하고 싶은 부분들이 넘쳐난다. 첫 프로젝트에서 이렇게 좋은 사람들과 함께할 수 있다는 것과, 서로의 목표를 향해 <strong>속도와 뱡향</strong>을 정해서 달려나간 점, 또 기간 내 구현할 수 있던 것들을 명확히 하고, 추가적인 부분까지 구현한 점들에 대하여 나는 참 성공적인 프로젝트를 해냈다고 생각이 들었다. 2주동안 너무 고생하셨습니다 !</p>
<p><strong>🦋 윤경님</strong>
언제나 늦은 시간까지 작업하시고, 가장 많은 페이지들을 구현해주셔서 감사했습니다. 윤경님을 통해 내가 할 수 있는 것, 그렇지 못한 것을 구분하고 솔직하게 이야기하는 모습에서 많은 것을 배웠습니다. &#39;주니어 개발자&#39;에게 요구하는 것이 무엇인지 명확히 알고 그렇게 나아가려는 모습을 본받고 싶었습니다. 저도 성급하지 않게 저의 페이스대로 성장하는 사람이 될게요 :) </p>
<p><strong>⚓️ 창원님</strong>
우리팀의 실질적 PM 창원님, 매일 미팅을 하면서 <strong>큰 배의 항해사</strong>와 같다고 생각하였습니다. 매일의 주어진 task만 보고 달리던 저에게 프로젝트라는 공동의 목표를 상기시켜주시고 프로젝트는 속도와 방향이 모두 중요하다는 것을 배우게 되었습니다. 창원님의 프로젝트를 넓게 바라보는 시야를 배우게 되었습니다. 감사합니다 !</p>
<p><strong>💻 태영님</strong>
우리의 마스터 태영님, 2주간 엄청난 일들이 있었는데도 정말 큰 일이 아닌 것 처럼 빠른 회복 탄력성으로 프로젝트에 집중하는 모습에 많이 놀랐습니다. 또한 데이터를 다 입력해주시고 이미지 작업도 손수해주셔서 Product 페이지가 제가 원하는 방향대로 구현될 수 있었습니다. 또 마지막 이별선물(?)로 아름다운 터미널을 선사해주셔 감사합니다. 저에게도 귀여운 옥토캣이 터미널에 살게되었습니다 ! </p>
<p><strong>8️⃣ 지우님</strong>
&quot;우리가 구현하고 싶은 서비스가 무엇이다.&quot; 하고 이야기 했을때, 어렵지 않다며 할 수 있다고 늘 긍정적으로 이야기 해주신 지우님. 8층에 갔다오면(?) 새로운 API들이 만들어지고 만들어져서 속도감에 놀랐습니다. 어려운 일임에도 긍정적으로 이야기해주시고 프론트와도 적극적으로 소통해주셔서 감사합니다. </p>
<p><strong>✏️ 명준님</strong>
저의 멘탈이 무너질 때마다 잘하고 있다고 격려해주신 명준님, 감사했습니다. 정말 나는 안되는 걸까 하고 혼잣말을 꺼낼때마다 다정하게 이야기해주셨던 날들 잊지 못할 거에요. 언제나 꾸준하게 노력하는 모습과 그 많은 데이터들을 이해하고 계시는 모습이 대단하다고 생각했습니다. 한편으론 자극도 받기도 했더랍니다. 저의 멘탈을 잘 잡아주셔서 감사합니다.</p>
<h1 id="front-end--feature-view">Front-end : Feature View</h1>
<h2 id="1-main">1. Main</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/6e0d622c-7b6a-4c76-b62e-72f0d19b9115/IMB_TZdbkd.gif" alt=""></p>
<h2 id="2-sign-up">2. Sign-up</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/281cd6ed-cf9c-4ae5-94cb-d520252d12b4/IMB_aQZdU8.gif" alt=""></p>
<h2 id="3-login">3. Login</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/8f30e24d-9ac3-4c8b-992e-11fea78fe148/IMB_znchh0.gif" alt=""></p>
<h2 id="4-recommend">4. Recommend</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/4c454be4-8ffe-4672-898c-0b437459f454/IMB_II73pw.gif" alt=""></p>
<h2 id="5-product">5. Product</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/ca74cc43-88a6-4a9c-8544-dbe15947f2b2/IMB_kVbj1k.gif" alt=""></p>
<h2 id="6-product-detail">6. Product Detail</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/611f8859-55ed-4779-893c-d5d02384a7ac/IMB_a8xE3L.gif" alt=""></p>
<h2 id="7-cart">7. Cart</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/8d995033-5265-4853-8630-b416fb92e123/IMB_883xIz.gif" alt=""></p>
<h2 id="8-order">8. Order</h2>
<p><img src="https://images.velog.io/images/e_soojeong/post/1baf58f1-52f2-45d1-966d-815f5647da68/IMB_vrTtDo.gif" alt=""></p>
<h1 id="back-end--db-modeling">Back-end : DB modeling</h1>
<p><img src="https://images.velog.io/images/e_soojeong/post/1bbe27c7-00ba-407c-a36d-e4b5f5d21d7b/%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-07-16%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.55.19.png" alt=""></p>
<h1 id="우리들의-치열했던-시간">우리들의 치열했던 시간</h1>
<p><img src="https://images.velog.io/images/e_soojeong/post/2d756c58-b626-4bbf-a2c9-4d2887e8c451/IMG_1223.JPG" alt="">!</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/3bc1f344-5e35-4417-90e2-ebbba86198d8/%E1%84%8B%E1%85%B5%E1%84%86%E1%85%B5%E1%84%8C%E1%85%B5%20(1)-1.JPG" alt=""><img src="https://images.velog.io/images/e_soojeong/post/6fa34572-e4a9-42e4-bd5d-14fd990aecee/%E1%84%8B%E1%85%B5%E1%84%86%E1%85%B5%E1%84%8C%E1%85%B5-1.JPG" alt=""></p>
<p>자세한 기술이야기는 <a href="https://velog.io/@e_soojeong/First-Project-Team-Villy-%EA%B5%AC%ED%98%84-%EA%B8%B0%EB%8A%A5">여기로 ! <a/> </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 30 | setState 비동기 특성]]></title>
            <link>https://velog.io/@e_soojeong/TIL-30-setState-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%8A%B9%EC%84%B1</link>
            <guid>https://velog.io/@e_soojeong/TIL-30-setState-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%8A%B9%EC%84%B1</guid>
            <pubDate>Sun, 11 Jul 2021 15:47:31 GMT</pubDate>
            <description><![CDATA[<h1 id="setstate-비동기-특성">setState 비동기 특성</h1>
<ul>
<li>동기기적, 비동기적 특성은 자바스트립트의 특성이다.<ul>
<li>자바스크립트의 이러한 특성 때문에 원하는대로 코드가 동작하지 않는 순간을 마주하게 된다.</li>
<li>혹은 당장은 동작할지 몰라도 추후엔 문제가 발생할 여지가 있다. </li>
</ul>
</li>
</ul>
<p>✏️ 특히 나는 모든 걸(?) CompoenetDidMount안에 두고 state관리를 하는데, 이런 나쁜 습관을 버리고 state의 변화가 필요한 요소들과 아닌 요소들을 나누어 setState를 관리해야한다.</p>
<h2 id="1-비동기-프로그램이란">1. 비동기 프로그램이란?</h2>
<p> 리액트에서 정상적으로 state를 업데이트 하려면 setState 함수를 사용한다. 하지만 setState의 비동기적 특성 때문에 원치 않는 결과를 가져온다. </p>
<p> ✏️ 오늘 내가 구현하려고 했던 checkbox에서도 함수가 실행되었을 때와 이벤트가 실행되었을 때 state 업데이트 내용이 한박자씩 느리게 전달되어 문제가 있었다.</p>
<h2 id="checkbox-처리-시-미리-확인할-내용">Checkbox 처리 시, 미리 확인할 내용</h2>
<ol>
<li><p><code>&lt;input&gt;</code>태그에서는 onChange 속성이 필수이다.</p>
</li>
<li><p><code>&lt;input&gt;</code>태그의 name 프로퍼티를 활용하여 <code>constructor</code>에 값을 정의해준다.</p>
</li>
<li><p>onChange에 따른 이벤트의 값 중에서 boolean을 활용할 수 있는 값을 찾는다.</p>
<p>✏️ 아래와 같이 console.log를 찍으면서 필요한 값이 무엇인가 찾아보았다. <code>console.dir(event)</code>를 활용하니, JavaScript 객체의 모든 속성을 볼 수 있었다.<img src="https://images.velog.io/images/e_soojeong/post/205928c4-9f7e-4ba3-990e-b554bcd9548c/%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-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.33.37.png" alt=""></p>
</li>
</ol>
<p>4.여기서 target이라는 반가운 친구를 만났다. <br/>
5.target을 열어보니 checked라는 속성이 있었다. 원래 최초 코드에는 <code>input</code> 태그마다 checked라는 속성을 부여해서 적용했었는데..! 애초에 그럴 필요가 없었던 것이다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/d6b3bfe2-d20d-4eb0-a438-7a61380eb0f2/%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-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.35.37.png" alt="">
6. checked가 어떤 값을 가지고 있는지 확인하기 위해서  console.log(event.target.checked)를 찍어보았다. 결과값은 checked가 되면 true, 해제가 되면 false값을 갖는다.</p>
<p><img src="https://images.velog.io/images/e_soojeong/post/b1b4f3e6-e4cc-424b-9c27-780a9349894a/%E1%84%92%E1%85%AA%E1%84%86%E1%85%A7%E1%86%AB%20%E1%84%80%E1%85%B5%E1%84%85%E1%85%A9%E1%86%A8%202021-07-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2011.42.12.gif" alt="">
7. <code>input</code> 의 속성은 boolean 타입인 것을 알았으니, 이제 각 name에 붙여준 이름을 최초엔 false라고 지정해주고, value가 <code>onChange</code>되었을 때 그 반대의 값을 갖게해라 ! 라는 조건을 넣은 <code>handleCheckBox</code>라는 함수를 만들어 주었다.</p>
<pre><code class="language-jsx">  handleCheckBox = event =&gt; {
    console.log(event.target.name);
    console.log(event.target.value);
    console.dir(event);
    console.log(event.target.checked);

    this.setState(
      {
        [event.target.name]: !this.state[event.target.name],
      }
    );
  };</code></pre>
<p>✏️ 이때 event.target.name을 그냥 가져오려고 하니 가져와지지가 않아서...검색을 해보았더니 <code>[event.target.name]</code>으로 작성하였더니 값이 잘 전달 되었다.</p>
<pre><code class="language-jsx">handleCheckBox = event =&gt; {
    console.log(event.target.name);
    console.log(event.target.value);
    console.dir(event);
    console.log(event.target.checked);

    this.setState(
      {
        [event.target.name]: !this.state[event.target.name],
      }
    );
  };</code></pre>
<h2 id="setstate-동기처리">setState 동기처리</h2>
<p>이렇게 함수를 만들고 handleCheckBox에서 <code>onChange</code>되었을 때 boolean 값 변화가 있는지 확인을 위해 state값을 찍어보았다....!</p>
<pre><code class="language-jsx">  handleCheckBox = event =&gt; {
    this.setState(
      {
        [event.target.name]: !this.state[event.target.name],
      }
    );
    console.log(this.state);
  };</code></pre>
<p>checked의 T/F 값이 하나씩 밀려서 나오기 시작했다.. 이런 문제에 대하여 동기, 비동기의 문제일 것이라는 이야기를 들었다...! <a href="https://grandj.tistory.com/236">출처</a> 이곳에서 도움을 얻어 handleCheckBox에 arrow 함수를 적어 해결하였다.</p>
<p><strong><em>전체 코드</em></strong>
굴러는 가지만 리팩토링 할게 많다. </p>
<ol>
<li>input태그 정리</li>
<li>fetch함수 주소 정리(4개다 체크 후 해제했을 때 <code>?</code>가 주소에 남는다... 흑..데이터 연결을 해봐야 알겠지만 아마 ? 가 있으면 최초 상태와는 다르게 컴포넌트가 보일거같은 느낌..)</li>
</ol>
<pre><code class="language-jsx"> import React, { Component } from &#39;react&#39;;

// import { GET_PRODUCTS_API } from &#39;../../../src/config.js&#39;;

import ProductCard from &#39;./ProductCard/ProductCard&#39;;

import &#39;./Product.scss&#39;;

export class Product extends Component {
  constructor() {
    super();
    this.state = {
      productCard: [],
      addCart: false,
      bone: false,
      hair: false,
      growth: false,
      skin: false,
    };
  }

  componentDidMount() {
    // fetch(`${GET_PRODUCTS_API}`)
    fetch(&#39;./data/ProductData.json&#39;)
      .then(res =&gt; res.json())
      .then(data =&gt; {
        this.setState({
          productCard: data.message,
        });
      });
  }

  makeCondition = () =&gt; {
    let fillter = [];

    if (this.state.bone) {
      fillter.push(`efficacy=1`);
    }

    if (this.state.hair) {
      fillter.push(`efficacy=2`);
    }

    if (this.state.growth) {
      fillter.push(`efficacy=3`);
    }

    if (this.state.skin) {
      fillter.push(`efficacy=4`);
    }

    console.log(test.join(&#39;&amp;&#39;));
    console.log(`./data/ProductData.json?${fillter.join(&#39;&amp;&#39;)}`);
    // fetch(`${GET_PRODUCTS_API}?`)
    fetch(`./data/ProductData.json?${fillter.join(&#39;&amp;&#39;)}`)
      .then(res =&gt; res.json())
      .then(data =&gt; {
        this.setState({
          productCard: data.message,
        });
      });
  };

  handleCheckBox = event =&gt; {
    console.log(event.target.name);
    console.log(event.target.value);
    console.dir(event);
    console.log(event.target.checked);

    this.setState(
      {
        [event.target.name]: !this.state[event.target.name],
      },
      () =&gt; {
        this.makeCondition();
      }
    );
  };

  render() {
    const { productCard } = this.state;

    return (
      &lt;div className=&quot;Product&quot;&gt;
        &lt;header className=&quot;productHeader&quot;&gt;
          &lt;h1&gt;
            건강한 삶을 위한
            &lt;br /&gt;
            빌리의 연구와 도전은 계속됩니다.
          &lt;/h1&gt;
        &lt;/header&gt;
        &lt;section className=&quot;productBody&quot;&gt;
          &lt;h2 className=&quot;sr-only&quot;&gt;Product Body&lt;/h2&gt;

          &lt;form className=&quot;productCategory&quot;&gt;
            &lt;input type=&quot;checkbox&quot; name=&quot;bone&quot; onChange={this.handleCheckBox} /&gt;
            &lt;label&gt;뼈&lt;/label&gt;
            &lt;input type=&quot;checkbox&quot; name=&quot;hair&quot; onChange={this.handleCheckBox} /&gt;
            모발
            &lt;input
              type=&quot;checkbox&quot;
              name=&quot;growth&quot;
              onChange={this.handleCheckBox}
            /&gt;
            성장
            &lt;input type=&quot;checkbox&quot; name=&quot;skin&quot; onChange={this.handleCheckBox} /&gt;
            피부
          &lt;/form&gt;
        &lt;/section&gt;
      &lt;/div&gt;
    );
  }
}
export default Product;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL 29 | RESTful API]]></title>
            <link>https://velog.io/@e_soojeong/TIL-31-RESTful-API</link>
            <guid>https://velog.io/@e_soojeong/TIL-31-RESTful-API</guid>
            <pubDate>Sat, 10 Jul 2021 15:16:47 GMT</pubDate>
            <description><![CDATA[<h1 id="restful-api">RESTful API</h1>
<blockquote>
<p>🎯 <strong>목표</strong></p>
</blockquote>
<ol>
<li>RESTful API가 무엇인지 설명할 수 있다.</li>
<li>RESTful API의 설계규칙에 따라 엔드포인트를 작성할 수 있다.</li>
<li>Path parameter와 Query parameter의 차이점을 알고, 상황에 맞게 적용할 수 있다.</li>
</ol>
<h2 id="rest하다-">Rest하다 ?</h2>
<ul>
<li><p>REST(REpresentational State Transfer)란 웹에 존재하는 모든 자원(resorce, ex. 이미지, 동영상, 데이터)에 고유한 URI를 부여하여 자원에 대한 주소를 지정하는 방법론, 또는 규칙이다.</p>
</li>
<li><p>Representational State Transfer = REST로
표현되어있는 상태. 즉 ✨<strong>상태를 전달하는 것</strong>에 대한 이야기이다.</p>
</li>
</ul>
<h2 id="restful-api-1">RESTful API?</h2>
<ul>
<li>API 시스템을 구현하기 위한 아키텍처(구조) 중 가장 널리 사용되는 형식이다.</li>
<li>더 이해하기 쉽게는 &quot;프론트엔드에서 백엔드 API를 호출할 url을 어떻게 만들것인가?&quot;에 대한 이야기이다.</li>
<li>리소스(HTTP URI로 정의된)를 어떻게 한다(HTTP Method + Payload)를 구조적으로 깔끔하게 표현하는 것이다.</li>
</ul>
<p><img src="https://images.velog.io/images/e_soojeong/post/682534a6-8757-4c72-8512-f594c724082a/%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-07-08%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.11.48.png" alt=""></p>
<ul>
<li><p>URI &gt; URL
✔️ URI는 URL보다 더 큰 범위의 개념이다.
✔️ URI는 Uniform Resource Identifier의 약어로 해당 사이트의 틀정 자원의 위치를 나타내는 유일한 주소이다.
✔️ 내가 클론 프로젝트를 하고 있는 필리를 예시로 들어 본다면,</p>
<p>메인, 제품보기, 고객후기, 장바구니, 로그인, 스토리, 고객센터로 메뉴가 이동할 때마다 그에 해당하는 주소가 바뀌어 나타난다. 이것은 URI라고 생각한다.</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/e_soojeong/post/8770d46d-b2c2-4a7c-814d-47255da00938/%E1%84%92%E1%85%AA%E1%84%86%E1%85%A7%E1%86%AB%20%E1%84%80%E1%85%B5%E1%84%85%E1%85%A9%E1%86%A8%202021-07-10%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.39.45.gif" alt=""></p>
<p>✏️ URL : URL은 Uniform Resource Locator의 약어로, 내가 위치한 <strong>주소</strong>를 뜻한다. 내가 있는 장소인 것이다. </p>
<ul>
<li><p>HTTP Method
✔️ GET, POST, DELETE 등 HTTP resquest가 의도한는 action을 정의한 것이다.</p>
</li>
<li><p>Payload (짐 : 데이터)
: body에 데이터를 실어서 보내는 것이다. 즉, request에서 server로 보내는 데이터이다.</p>
</li>
</ul>
<h3 id="1-restful-api-장점">1. RESTful API 장점</h3>
<ul>
<li><p>Self-descriptiveness : <strong>자기 표현력이 좋다 !</strong>
RESTful API 자체만으로도 <strong>API의 목적이 쉽게 이해가 된다</strong>는 것이다.</p>
<p>✏️ 내가 지향하는 직관적으로 이해할 수 있는 코드에 부합하는 부분에 있어서 RESTful API는 참 좋은 규칙이라고 생각한다.</p>
</li>
</ul>
<h3 id="2-restful-api-설계-규칙">2. RESTful API 설계 규칙</h3>
<ul>
<li><p>URI 정보를 명확하게 표현해야한다.
: resource는 명사를 사용한다.</p>
</li>
<li><p>resource에 대한 행위를 HTTP Method(GET,POST,PUT,DELETE)로 표현한다.
: URI에 HTTP Method가 포함되서는 안되며, URI에 동사가 포함되서는 안된다.</p>
</li>
<li><p>resource 사이에 연관 관계가 있는 경우
: /resource/고유ID/관계 있는 리소스
<code>ex) GET /users/{user_id}/profile</code>
{user_id}는 변수로, path parameters로 받아오는 것이 좋다.</p>
<ul>
<li><p><code>/</code> 구분자를 사용하여 자원의 계층 관계를 나타내는 데 사용한다.</p>
</li>
<li><p>URI 마지막 문자로 <code>/</code>를 포함하지 않는다.</p>
</li>
<li><p>불가피하게 URI가 길어지는 경우 <code>-</code>을 가독성을 높인다. <code>_</code>은 사용하지 않는다. </p>
</li>
</ul>
<p><img src="https://images.velog.io/images/e_soojeong/post/16eacbc3-1eec-44a7-acb6-e4c223c996e8/image.png" alt=""></p>
<p>필리의 경우, 상품의 이름이 매우 길어서 <code>-</code>으로 나누어 URI를 적어주었다. (물론 Product1과 같은 규칙이 아닌 상품명으로 URI를 구성하였다.</p>
</li>
</ul>
<ul>
<li><p>URI 경로에는 대문자 사용을 피하도록 규정하고 있다.</p>
</li>
<li><p>파일의 경우 payload의 포맷을 나타내기 위한 파일 확장자를 URI에 포함시키면 안된다.</p>
</li>
</ul>
<h2 id="query-parameters-vs-path-parameters">Query Parameters VS Path Parameters</h2>
<h3 id="1-query-parameters-get-parameters">1. Query parameters (GET parameters)</h3>
<ul>
<li><p>쿼리 스트링 : 웹 페이지의 url 주소를 자세히 보면 종종 <code>?</code> 가 포함되어 있는데, 이 물음표는 단순한 문자열이 아니다. 특정한 기능을 수행하고 있다. 물음표 뒤에는 늘 key=value 형식의 문자열이 따오는데 이것을 <strong>Query parameter</strong>라고 한다.</p>
</li>
<li><p>주로 데이터를 조건으로 거르거나(filtering), 특정 방식으로 정렬하거나(sorting), 검색(searching)하고자 하는 경우에 활용한다.</p>
</li>
<li><p><strong>Best Practice</strong> : Filtering, Sorting, Searching의 경우에는 <strong>Query Parameters</strong>이다.</p>
<p>✏️ 레이아웃도, css도 정답은 없지만 Best Practice 것처럼 Filtering, Sorting, Searching는 Query parameters를 사용한다. </p>
</li>
</ul>
<h3 id="2-path-parameters">2. Path parameters</h3>
<ul>
<li>해당 리소스에 더 자세한 정보를 얻기 위해 접근할 때 사용</li>
</ul>
<h1 id="status-code">Status Code</h1>
<p><img src="https://images.velog.io/images/e_soojeong/post/0251c336-7bf5-4bf6-8afb-155e37ceeb0f/IMG_732BA9779C18-1.jpeg" alt=""></p>
<p>Back과 소통할 때, 데이터가 어떻게 전달되는지, 어떤 이유로 전달되지 않는 지에 대하여 다시 한번 확인해보았다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[First-Project  | Team Villy - 코드 리뷰 💊]]></title>
            <link>https://velog.io/@e_soojeong/First-Project-Team-Villy</link>
            <guid>https://velog.io/@e_soojeong/First-Project-Team-Villy</guid>
            <pubDate>Fri, 09 Jul 2021 16:34:30 GMT</pubDate>
            <description><![CDATA[<h1 id="villy-💊">Villy 💊</h1>
<blockquote>
<p>윤경님, 창원님, 지우님, 태영님, 명준님과 함께하는 나의 첫 번째 프로젝트 ✨</p>
</blockquote>
<h2 id="코드-리뷰">코드 리뷰</h2>
<p>프로젝트의 반을 남겨둔 시점에서 내가 새롭게 배우는 것들에 대한 정리 <strong>(feat. 연욱님 리뷰)</strong></p>
<p>✔️ 공백</p>
<pre><code class="language-jsx">import React, { Component } from &#39;react&#39;;

import ProductCard from &#39;./ProductCard/ProductCard&#39;;

import &#39;./Product.scss&#39;;</code></pre>
<p>✏️ import 개수에 따라서, import를 종류별로 공백을 넣어서 구분한다. 단, 이렇게 몇개 없을 경우에는 공백없이 붙여도 무방하다.</p>
<hr/>

<p>✔️ 스타일</p>
<p>✏️ tag selector는 다른 요소들에 영향을 미칠 여지가 너무 많기 때문에 추후 유지보수에 악영향을 미치기 때문에 스타일이 필요한 요소에는 className을 부여하는 것이 좋다. </p>
<p>✏️ 개인적으로는 SASS를 사용하면서 각 컴포넌트마다 최상위에서 스타일을 주기 때문에 nesting된 안쪽의 요소들에는 className을 부여하지 않아도 될 것 같다고 생각했지만 <strong>창원님</strong>과 이야기 해보니, 추후 컴포넌트끼리 합쳐지고 css로 컴파일이 되었을 때 네이밍이 되지 않은 class에 영향을 받을 수 있다는 생각이 들었다.</p>
<hr/>

<p>✏️ addedDec이라는 이름의 className을 사용하고, 피드백을 받았다. 사유는 축약어의 경우 관용적으로 많이 사용하는 event -&gt; e 정도를 제외하고는 네이밍이 다소 길어지더라고 이해가 쉽도록 명확하게 전달해주는 것이 좋다고 한다.</p>
<p>✔️ 너비와 높이</p>
<p>✏️ height를 고정적으로 정해버리면 추후 자식요소들의 height가 변경되었을때 유동적으로 조정되지 않는다. 자식요소들의 height를 수정해야 할 일이 생기면 부모요소의 height도 계산해서 다시 수정해야하는 불상사가 일어난다.</p>
<p>✏️ 이러한 부분을 알고 있으면서도 스타일적인 부분을 클론하다보니 나도 모르게 높이를 주고있었다...하하</p>
<p>✏️ 해결 방법은 자식요소들의 height를 정하고 부모요소의 크기는 거기에 따라 자동적으로 조정되게 고정값을 안주는식으로 레이아웃을 짜는 것이다. (보통 스크롤이 필요한 경우를 제외하고는 모두 bottom-up 방식으로 레이아웃을 작성하는 것이 좋다고 하셨다.)</p>
<hr/>

<p>✔️ 데이터 전달</p>
<p><strong>⌨️ 최초 코드</strong></p>
<pre><code class="language-Jsx">{
    &quot;id&quot;: 1,
    &quot;backgroundColor&quot;: &quot;none&quot;,
}      </code></pre>
<p>✏️ 백엔드에서 주는 데이터형식에 backgroundColor가 있지 않을 것이라는 리뷰를 받았다. </p>
<p>✏️ 우리 프로젝트에서는 제품 카드가 컴포넌트로 반복될때, 카드의 Background 컬러가 9개씩 반복된다. 처음엔 단순히 전달되는 데이터라고만 생각해서 <strong>Back</strong>에서 데이터를 받아올 생각으로 Mock data를 만들었다. 그런데 리뷰를 받고 곰곰히 생각해보니 backgroundColor는 데이터로 저장하는 것 자체가 의미가 없다는 생각이 들었다. 그래서 <strong>Front</strong> 단에서 관리하기로했다.</p>
<p>✏️ 처리 방법은 상수데이터로 저장하여 컴포넌트의 키 값으로 데이터를 전달해주는 것이다. </p>
<p>❗️ 여기서 포인트는 9개의 색상이 반복된다고 해도 제품 카드가 9보다 많아 진다면 ❗️ 이라는 가정을 멘토님께서 해주셨다. 이 부분은 <code>데이터 배열의 index % 상수데이터의 길이</code>를 해주면 이렇게 나머지는 계속해서 0~8을 반복하기 때문에 데이터 배열의 길이와 관계없이 9가지의 색상이 반복된다는 것이다. 나의 사고력으로는 생각하기 어려웠지만...! 방법을 알고나니 이렇게 적용하면 되겠다!! 하는 생각이 들었다 :) </p>
<p><img src="https://images.velog.io/images/e_soojeong/post/63a628f0-9e75-42bf-8667-8dde27f095f3/image.png" alt=""></p>
<p>✏️ 현재는 자식요소에서 props로 받아 이 값들을 아래와 같이 스타일링 하였지만, 인라인 스타일링은 지양해야하는 일이기 때문에 추후엔 class로 부여하여 적용하는 방법을 적용할 것이다. (할 일 태산...)</p>
<pre><code class="language-jsx">&lt;li className=&quot;ProductCard&quot; style={{ backgroundColor }}&gt;</code></pre>
<hr/>
✔️ arrow function

<p><strong>⌨️ 최초 코드</strong></p>
<pre><code class="language-Jsx"> {this.state.productCard.map(product =&gt; {
              return &lt;ProductCard key={product.id} productCard={product} /&gt;;
            })}</code></pre>
<p><strong>🔧 리팩토링</strong></p>
<pre><code class="language-jsx"> {this.state.productCard.map(product =&gt; &lt;ProductCard key={product.id} productCard={product} /&gt;)}</code></pre>
<p>✏️ arrow function 에서 바로 리턴만 할 경우에는 위와 같이 증괄호랑 return 키워드 생략할 수 있다. 아직도 arrow 함수가 무엇을 인자로 전달하는지 헷갈려서 syntax대로 코드를 작성하고 있는데, 어서 익숙해져서 간편하게 사용하는 날이 오길 소망한다 !</p>
<hr/>

<p>✔️ &amp;&amp; 연산자
<strong>⌨️ 최초 코드</strong></p>
<pre><code class="language-jsx">     {description
                ? description.map(descriptionItem =&gt; {
                    return (
                      &lt;ProductDec
                        key={descriptionItem.id}
                        description={descriptionItem.descriptionList}
                      /&gt;
                    );
                  })
                : null}</code></pre>
<p>✏️ map함수를 사용할 때, 배열의 형태가 아닌 경우 error가 발생한다. 이때 조건부 렌더링을 통하여 문제를 해결해야하는데, 나는 삼항연산자로 조건을 부여했는데 이보다 더 유용한 <strong>&amp;&amp;연산자</strong>를 만났다..⚽️ </p>
<p>✏️ <strong>&amp;&amp;연산자</strong>는 true일때만 특정 값을 렌더할 수 있도록 조건을 걸어주는 것이다. 여기선 무조건 true일때만 mapping 해야하니 <strong>&amp;&amp;연산자</strong>를 사용하는 것이 좋다.</p>
<hr>

<p>✔️ state</p>
<p>✏️ 나는 state에 굳이 상태가 변하지 않는 값들에 대하여 state에 저장해버리는 이상한..? 습관이 생겨버렸다. 일단 state에 다 저장해버린다고 해야할까. 데이터 흐름에 대한 이해를 먼저 하고 state를 통해 상태를 변경해주어야하는데 아직 그 부분이 큰 그림으로 그려지지 않는다. 자주 받는 피드백이어서 다음에 코드를 작성할 땐 가장 유의하여 작성하고 싶은 부분이다.</p>
<p>✏️ 또는 하나의 state값을 기준을 변화하는 것들에 대하여는 state로 따로 관리할 필요 없이 reder시에 바로 계산 할 수 있도록 하는 것이 좋다. </p>
<pre><code class="language-jsx">{addCart &amp;&amp; &lt;AiOutlinePlus /&gt;}
{addCart ? &quot;장바구니 추가됨&quot; :  &quot;장바구니 담기&quot;}
</code></pre>
<hr/>

<p>✔️ image 태그</p>
<p>✏️ img tag의 속성을 적을땐 alt가 제일 상단으로 오도록 해야한다. 지금까지 나의 습관은 src를 적고 alt를 적는 것이었는데, alt를 먼저 적어달라고 하셨다. 개인적으로 추측하건데 스크린 리더기에서 읽히게 된다면 먼저 읽혀야하는 것이 이미지의 alt여서 그런게 아닐까 !</p>
<hr/>
✔️ 구조 분해 할당

<p>✏️ 신비한 구조 분해 할당의 세계... 솔직히 state, props로 전달되는 값 정도나 구조 분해 하려고 했지 map으로 전달되는 인자들을 구조 분해 하려는 생각은 한 번도 안해봤다..늘 효율을 생각하면서 코드를 짜는 습관을 들여야겠다.</p>
<p><strong>⌨️ 최초 코드</strong></p>
<pre><code>icon.map(iconItem =&gt; {
                    return (
                      &lt;ProductIcon
                        key={iconItem.id}
                        src={iconItem.src}
                        alt={iconItem.alt}
                      /&gt;
                    );
                  })}</code></pre><p><strong>🔧 리팩토링</strong></p>
<pre><code class="language-jsx">  icon.map(({id,src,alt})=&gt; {
                    return (
                      &lt;ProductIcon
                        key={id}
                        src={src}
                        alt={alt}
                      /&gt;
                    );
                  })}</code></pre>
<hr>

<p>✔️ 셀프클로징 &amp; className</p>
<p>✏️ 잊지말기.. 습관이 무섭다. 특히 icon을 import해오는 경우 종종 셀프클로징을 잊는다. 그리고 className도..class로 적는 매직...! 그래도 className은 경고라도 뜨지만 셀프클로징은 잡아내기 너무 어렵다. 하지만 이런 코드도 잡아내는 연욱님 최고..👍🏻</p>
<h2 id="느낀점">느낀점</h2>
<p>아직 기능을 구현하는 부분에 대하여는 마무리가 되지 않아 정리하지 못했지만, 프로젝트 기간의 반이 지나고 내가 어디서 자주 같은 실수를 반복하는지 어떤 식으로 코드를 짜면 효율적인지에 대하여 돌아보는 시간이었다. 이번 주말은 앞으로 나가기 위한 베이스를 다지는 시간을 가져야지 !  </p>
]]></description>
        </item>
    </channel>
</rss>