<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>subb_ny.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 05 Dec 2023 10:48:35 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>subb_ny.log</title>
            <url>https://velog.velcdn.com/images/subb_ny/profile/2b3dfda4-5e74-4a0a-acfe-89eb1370c27c/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. subb_ny.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/subb_ny" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[JavaScript] Q&A renderTable함수 리팩토링 ]]></title>
            <link>https://velog.io/@subb_ny/JavaScript-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</link>
            <guid>https://velog.io/@subb_ny/JavaScript-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</guid>
            <pubDate>Tue, 05 Dec 2023 10:48:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>간단한 코드 설명 
 <code>Q&amp;A를 구현하기 위해 데이터를 받아서 table을 보여주는 코드이다. 
질문과 답변을 데이터 값으로 매칭시켜서 보여주며, 페이지네이션을 구현했으며, 답변 status상태를 색으로 구분했다.</code></p>
</blockquote>
<h4 id="리팩토링-전-코드">리팩토링 전 코드</h4>
<pre><code class="language-javascript">function renderTable(page) {
  const tableBody = document.getElementById(&quot;tableBody&quot;);
  tableBody.innerHTML = &quot;&quot;;

  for (
    let i = (page - 1) * itemsPerPage;
    i &lt; page * itemsPerPage &amp;&amp; i &lt; totalItems;
    i++
  ) {
    const row = filteredQuestions[i];

    const matchingAnswer = filteredAnswers.find(
      (answer) =&gt; answer.ba_id === row.ba_id
    );

    if (itId == row.it_id) {
      const displayTitle =
        row.mb_id === currentUserId || producerAuthority === currentUserId
          ? row.qna_title
          : &quot;비밀글 입니다.&quot;;
      const displayContent =
        row.mb_id === currentUserId || producerAuthority === currentUserId
          ? row.qna_content
          : &quot;&quot;;

      const trTemplate = `
        &lt;tr data-ba_id=&quot;${row.ba_id}&quot;&gt;
            &lt;td class=&quot;table_list_qna font_size &quot;&gt;${row.qna_status}&lt;/td&gt;
            &lt;td class=&quot;table_list_qna&quot;&gt;
                &lt;div class=&quot;qna_title&quot;&gt;Q. ${displayTitle}&lt;/div&gt;
                &lt;div class=&quot;qna_content&quot;&gt;${displayContent}&lt;/div&gt;
            &lt;/td&gt;
            &lt;td class=&quot;table_list_qna nick_date&quot;&gt;
                &lt;div&gt;${row.mb_id}&lt;/div&gt;
                &lt;div&gt;${row.qna_date}&lt;/div&gt;
            &lt;/td&gt;
        &lt;/tr&gt;`;
      tableBody.innerHTML += trTemplate;




  const statusCells = tableBody.querySelectorAll(&quot;.table_list_qna.font_size&quot;);

  statusCells.forEach((cell, index) =&gt; {
    const row = filteredQuestions[(page - 1) * 5 + index];

    if (row.qna_status === &quot;답변대기&quot;) {
      cell.classList.add(&quot;font_color_wait&quot;);
    } else {
      cell.classList.add(&quot;font_color_complete&quot;);
    }
  });
      if (matchingAnswer) {
        let answerDisplayContent =
          row.mb_id === currentUserId || producerAuthority === currentUserId
            ? matchingAnswer.qna_answer_content
            : &quot;비밀글에 대한 답변입니다.&quot;;

        const answerTemplate = `
            &lt;tr class=&#39;table_tr_answer&#39;&gt;
                &lt;td class=&#39;table_list&#39;&gt;&lt;img class=&#39;arrow-img&#39; src=&#39;&lt;?php echo G5_URL ?&gt;/img/right-and-down.png/&gt;&#39; &lt;/td&gt;
                &lt;td class=&#39;table_list&#39;&gt;&lt;div class=&#39;qna_content&#39;&gt;&lt;span class=&#39;qna_strong&#39;&gt;A. &lt;/span&gt; ${answerDisplayContent}&lt;/div&gt;&lt;/td&gt;
                &lt;td class=&#39;table_list nick_date&#39;&gt;&lt;div&gt;${matchingAnswer.mb_id}&lt;/div&gt;&lt;div&gt;${matchingAnswer.qna_answer_date}&lt;/div&gt;&lt;/td&gt;
            &lt;/tr&gt;`; 
        tableBody.innerHTML += answerTemplate;
      }
    }
  }

  // 페이지네이션 로직
  const pagination = document.getElementById(&quot;pagination&quot;);
  pagination.innerHTML = &quot;&quot;;
  if (currentPage &gt; 1) {
    pagination.innerHTML +=
      &#39;&lt;button onclick=&quot;goToPage(currentPage - 1)&quot;&gt;&lt;img class=&quot;arrow&quot; src=&quot;&lt;?php echo G5_URL ?&gt;/img/chevron.png/&gt;&quot;/&gt;&lt;/button&gt;&#39;;
  } // &gt; 생성 
  for (let i = 1; i &lt;= totalPages; i++) {
    pagination.innerHTML += `&lt;button id=&quot;page-${i}&quot; class=&quot;page-button&quot; onclick=&quot;goToPage(${i})&quot;&gt;${i}&lt;/button&gt;`;
  } // 슷지 버튼 생성 
  if (currentPage &lt; totalPages) {
    pagination.innerHTML +=
      &#39;&lt;button onclick=&quot;goToPage(currentPage + 1)&quot;&gt;&lt;img class=&quot;arrow&quot; src=&quot;&lt;?php echo G5_URL ?&gt;/img/arrow.png/&gt;&quot;/&gt;&lt;/button&gt;&#39;;
  }
} // &lt; 생성 </code></pre>
<h3 id="🧩-리팩토링-후-코드">🧩 리팩토링 후 코드</h3>
<p>가장 먼저 생각해야할 것은 함수가 너무 길어 가독성이 떨어진다는 것이었다. 
특히 template 문자열 코드가 함수안에 있으면 상당히 불편했다. 
따라서 createQnaRowTemplate,createAnswerRowTemplate을 만들어 template을 return 해주었다. </p>
<p> 또한 applyStatusColor를 함수로 묶은 이유는 중간에 코드 삽입이 되어 있어서 명시된 이름으로 기능을 한눈에 파악할 수 있으면 좋겠다고 생각이 되어 함수로 따로 빼주었다. </p>
<p>마지막으로 페이지네이션에서 버튼을 생성해주는 코드에서도 중복되는 부분이 있어 다른 부분은 인자로 넘겨주면 되겠다고 생각되어 함수로 만들었다. icon을 생성할 때는 icon인자만 다르게 넘겨주면 됐었는데, 중간에 숫자를 생성하는 부분은 icon이 없고 숫자만 생성되기 때문에 조건문에 따라서 return값을 다르게 넣어줬다. 근데 이 부분도 중복되는 부분이 있기에 최대한 간단화할 수 있는 방법을 고민중이다. </p>
<pre><code class="language-javascript">const  renderTable = (page) =&gt; {
  const tableBody = document.getElementById(&quot;tableBody&quot;);
  tableBody.innerHTML = &quot;&quot;; // 초기화 

  const user = &quot;&lt;?php echo($member[&#39;mb_id&#39;]); ?&gt;&quot;;

  for (
    let i = (page - 1) * itemsPerPage;
    i &lt; page * itemsPerPage &amp;&amp; i &lt; totalItems;
    i++
  ) {
    const row = filteredQuestions[i];
    const matchingAnswer = filteredAnswers.find(
      (answer) =&gt; answer.ba_id === row.ba_id
    );

    if (itId == row.it_id) {
      const displayTitle =
        row.mb_id === currentUserId || producerAuthority === currentUserId
          ? row.qna_title
          : &quot;비밀글 입니다&quot;;
      const displayContent =
        row.mb_id === currentUserId || producerAuthority === currentUserId
          ? row.qna_content
          : &quot;&quot;;

      const trTemplate = createQnaRowTemplate(
        row,
        displayTitle,
        displayContent
      );
      tableBody.innerHTML += trTemplate;

      applyStatusColor(tableBody, filteredQuestions, currentPage);

      if (matchingAnswer) {
        let answerDisplayContent =
          row.mb_id === currentUserId || producerAuthority === currentUserId
            ? matchingAnswer.qna_answer_content
            : &quot;비밀글에 대한 답변입니다&quot;;
        const answerTemplate = createAnswerRowTemplate(
          matchingAnswer,
          answerDisplayContent
        );
        tableBody.innerHTML += answerTemplate;
      }
    }
  }

  // 페이지네이션 로직
  const pagination = document.getElementById(&quot;pagination&quot;);
  pagination.innerHTML = &quot;&quot;;

  if (currentPage &gt; 1) {
    console.log(currentPage);
    pagination.innerHTML += createPaginationButton(currentPage-1 , &quot;chevron&quot;);
  }

  for (let i = 1; i &lt;= totalPages; i++) {
    pagination.innerHTML += createPaginationButton(i);
  }

  if (currentPage &lt; totalPages) {
    pagination.innerHTML += createPaginationButton(currentPage + 1, &quot;arrow&quot;);
  }
}</code></pre>
<p>함수로 분리 </p>
<pre><code class="language-javascript">const createQnaRowTemplate = (row, displayTitle, displayContent) =&gt; {
  return `
        &lt;tr data-ba_id=&quot;${row.ba_id}&quot;&gt;
            &lt;td class=&quot;table_list_qna font_size &quot;&gt;${row.qna_status}&lt;/td&gt;
            &lt;td class=&quot;table_list_qna&quot;&gt;
                &lt;div class=&quot;qna_title&quot;&gt;Q. ${displayTitle}&lt;/div&gt;
                &lt;div class=&quot;qna_content&quot;&gt;${displayContent}&lt;/div&gt;
            &lt;/td&gt;
            &lt;td class=&quot;table_list_qna nick_date&quot;&gt;
                &lt;div&gt;${row.mb_id}&lt;/div&gt;
                &lt;div&gt;${row.qna_date}&lt;/div&gt;
            &lt;/td&gt;
        &lt;/tr&gt;`;
}

const createAnswerRowTemplate = (matchingAnswer, answerDisplayContent) =&gt;  {
  return `
        &lt;tr class=&#39;table_tr_answer&#39;&gt;
            &lt;td class=&#39;table_list&#39;&gt;&lt;img class=&#39;arrow-img&#39; src=&#39;&lt;?php echo G5_URL ?&gt;/img/right-and-down.png/&gt;&#39; &lt;/td&gt;
            &lt;td class=&#39;table_list&#39;&gt;&lt;div class=&#39;qna_content&#39;&gt;&lt;span class=&#39;qna_strong&#39;&gt;A. &lt;/span&gt;${answerDisplayContent}&lt;/div&gt;&lt;/td&gt;
            &lt;td class=&#39;table_list nick_date&#39;&gt;&lt;div&gt;${matchingAnswer.mb_id}&lt;/div&gt;&lt;div&gt;${matchingAnswer.qna_answer_date}&lt;/div&gt;&lt;/td&gt;
        &lt;/tr&gt;`;
}

const applyStatusColor = (tableBody, filteredQuestions, page) =&gt;  {
  const statusCells = tableBody.querySelectorAll(&quot;.table_list_qna.font_size&quot;);

  statusCells.forEach((cell, index) =&gt; {
    const row = filteredQuestions[(page - 1) * 5 + index];

    if (row.qna_status === &quot;답변대기&quot;) {
      cell.classList.add(&quot;font_color_wait&quot;);
    } else {
      cell.classList.add(&quot;font_color_complete&quot;);
    }
  });
}

const createPaginationButton = (page, icon = &quot;&quot;) =&gt;  {
  const iconSrc = icon
    ? `&lt;img class=&quot;arrow&quot; src=&quot;&lt;?php echo G5_URL ?&gt;/img/${icon}.png&quot;/&gt;`
    : &quot;&quot;;
   if (icon === &quot;arrow&quot; ||icon === &quot;chevron&quot; ) {
    return `&lt;button id=&quot;page-${page}&quot; class=&quot;page-button&quot;  onclick=&quot;goToPage(${page})&quot;&gt;${iconSrc}&lt;/button&gt;`;
  } else {
    return `&lt;button id=&quot;page-${page}&quot; class=&quot;page-button&quot;  onclick=&quot;goToPage(${page})&quot;&gt;${iconSrc}${page}&lt;/button&gt;`;
  }
}

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[인턴 경험] sellfarm 도매사이트 리뉴얼 기획 및 개발 ]]></title>
            <link>https://velog.io/@subb_ny/%EC%9D%B8%ED%84%B4-%EA%B2%BD%ED%97%98-sellfarm-%EB%8F%84%EB%A7%A4%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC-%EA%B8%B0%ED%9A%8D-%EB%B0%8F-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@subb_ny/%EC%9D%B8%ED%84%B4-%EA%B2%BD%ED%97%98-sellfarm-%EB%8F%84%EB%A7%A4%EC%82%AC%EC%9D%B4%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC-%EA%B8%B0%ED%9A%8D-%EB%B0%8F-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Sun, 03 Dec 2023 09:27:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>진행 기간 : 2023.09.01 ~ 2023.11.09
 진행 인원: 3명 
사용스킬: PHP,JavaScript, MYSQL
역할: 기획, 디자인, 개발 
운영 사이트: <a href="https://sellfarm.co.kr/">https://sellfarm.co.kr/</a></p>
</blockquote>
<p>NEXT RUNNERS 인턴으로 재직하면서 sellfarm 프로젝트를 맡아서 진행하게 되었다. 
PHP 언어, 그누보드 템플릿으로 제작된 홈페이지였으며 요구 사항을 포함해서 기획, 디자인, 개발까지 요청받았다. </p>
<h2 id="🦠-초기-사이트-문제정의-및-문제-해결-방안-구상-및-추가-요구사항">🦠 초기 사이트 문제정의 및 문제 해결 방안 구상 및 추가 요구사항</h2>
<p><img src="https://velog.velcdn.com/images/subb_ny/post/bfef4bd7-2085-420e-9735-e179fc648c19/image.png" alt=""></p>
<p>먼저 도매사이트 유통에 대한 사전 지식이 없기에, 도매사이트 유통 프로세스에 대해서 이해하고 초기 홈페이지를 분석하였다.직접 사이트를 이용해보고 실제 유저의 의견을 받으며 문제점을 찾아 나갔다. 해결 방법은 벤치마킹 사이트를 참고하고, 팀원들뿐만 아니라 다양한 의견을 받아서 구상해보았다. </p>
<h3 id="✏️-문제점과-해결방안-구상-정리">✏️ 문제점과 해결방안 구상 정리</h3>
<blockquote>
</blockquote>
<ol>
<li>현재 사이트에서는 공급자와 셀러를 구분할 수 없음</li>
</ol>
<p><strong>=&gt; 공급자와 셀러의 회원가입 페이지를 분리하여 최초 회원가입 시, 선택할 수 있도록 구현 **
2. 같은 상품도 무게만 다른 것은 옵션으로 넣어도 되는데 각각 분리되어 있음
**=&gt; 관리자 페이지에서 상품 옵션란을 만들어 옵션 생성 (정규표현식으로 +뒤는 가격, /로 옵션 개수 구분)</strong>
3. 모든 상품 변동 사항, 공급자의 품절/ 재입고 안내를 하나의 게시판에서 관리하는데, 이는 상품이 많아질수록 비효율적이라고 생각됨. 
<strong>=&gt; 상품 상세페이지에 공급자 공지사항 제작(CRUD) 
(해당 상품을 공급하는 공급자가 공지사항 및 Answer작성을 할 수 있도록 관리자페이지에서 권한 부여  )</strong>
4. 셀러와 공급자가 질의응답할 수 있는 기능이 없음 
<strong>=&gt; 상품 상세페이지에 상품 Q&amp;A 제작   **
5. 결제 방식이 무통장 입금만 사용되고 있는데, 매 결제마다 입금 확인하는 것이 번거롭다는 운영자 의견
*<em>=&gt; 예치금 결제 시스템을 도입하여 결제마다 확인하는 번거로움 줄임 *</em>
6. 장바구니에 같은 상품을 반복적으로 담으면 새로 추가되는 것이 아니라 개수가 추가됨 
**=&gt; 이는 같은 상품이면서 옵션이 다른 상황을 고려하지 못한 것이므로 장바구니에 새로운 장바구니 식별 칼럼을 생성하여 식별 칼럼을 기준으로 장바구니 데이터 생성</strong>
7. 상품 이미지 다운로드를 클릭하면 모든 상품 이미지를 다운받을 수 있는 구글 드라이브로 연결되어 있음 
*<em>=&gt; 상품 이미지및 상세페이지의 url을 DB에 넣어, 해당 상품에 대한 이미지만 볼 수 있도록 구현 *</em>
8. 주문 내역을 필터링, 검색 기능이 존재하지 않아서 사용자 불편함 야기
*<em>=&gt; 날짜별, 카테고리별, 상품 상태별 필터링 , 검색 기능 구현 *</em></p>
<blockquote>
<h4 id="추가-기능-요청">추가 기능 요청</h4>
<ol start="9">
<li>주문 내역을 체크박스로 선택한 것을 엑셀로 정보를 다운</li>
<li>엑셀 대량 주문 기능</li>
<li>회원의 등급을 지정하고 지정 등급에 따라 할인률 적용</li>
<li>관리자 페이지에서 엑셀로 송장번호를 등록하면 주문내역에 반영</li>
<li>관리자 페이지에서 엑셀로 대량으로 상품을 등록</li>
</ol>
</blockquote>
<p>이와 같이 정리하여 간단한 피그마 작업을 통해 컨펌을 받아 구현 작업을 시작하였다. 기획부터 디자인, 개발까지 모두 다 전임하여 맡은 상황이라서 고려해야 할 것들이 많았다. </p>
<h2 id="🥇-내가-기여한-구현-기능-정리">🥇 내가 기여한 구현 기능 정리</h2>
<h3 id="1-회원가입-시--셀러와-공급자-분리">1. 회원가입 시 , 셀러와 공급자 분리</h3>
<blockquote>
<h4 id="span-stylebackground-color-rgba24217918805사이트의-문제점-및-해결-방안-구상-span"><span style="background-color: rgba(242,179,188,0.5)">사이트의 문제점 및 해결 방안 구상 </span></h4>
<p>현재 사이트에서는 공급자와 셀러를 구분할 수 없음
*<em>=&gt; 공급자와 셀러의 회원가입 페이지를 분리하여 최초 회원가입 시, 선택할 수 있도록 구현 *</em></p>
</blockquote>
<p> <img src="https://velog.velcdn.com/images/subb_ny/post/450ee660-6941-48cc-b7c6-c9fbaea5a728/image.png" alt=""></p>
<p>회원가입시 셀러와 공급자가 받는 정보가 달랐다. 따라서 회원가입 페이지를 따로 분리하였고, 회원정보 테이블에 mb_qualification라는 coulumn을 생성하여 셀러 or 공급자를 저장하였다. </p>
<h4 id="🛎-span-stylebackground-color-d3e3f5고민했던-부분-span">🛎 <span style="background-color: #d3e3f5">고민했던 부분 </span></h4>
<blockquote>
<p>회원가입 시에 option으로 선택하여 mb_qualification을 받는 게 좋을지 vs 따로 페이지를 나눠서 받는게 좋을지 고민이 되었다. 
유저의 나이대가 셀러는 다양하지만, 공급자는 40대이상-60대까지 비교적 고연령대로 측정되었다.  고령 사용자들은 일단 디지털 환경에 대한 익숙성이 낮을 수 있고, 정보를 이해하고 처리하는 데 시간이 더 걸릴 수 있으므로 간소하고 직관적인 디자인이 중요하다고 생각했다. 따라서 초반 회원가입에 페이지를 나누어 회원가입을 유도하도록 제작했다. 추가적으로 셀러와 공급자 아래에 가벼운 설명글을 적어서 가입시, 셀러와 공급자에 대한 정보가 헷갈리지 않도록 구성했다. </p>
</blockquote>
<br/>
<br/>

<h3 id="2-상품상세페이지에-공급자-공지사항-구현">2. 상품상세페이지에 공급자 공지사항 구현</h3>
<blockquote>
<h4 id="span-stylebackground-color-rgba24217918805사이트의-문제점-및-해결-방안-구상-span-1"><span style="background-color: rgba(242,179,188,0.5)">사이트의 문제점 및 해결 방안 구상 </span></h4>
<p> 모든 상품 변동 사항, 공급자의 품절/ 재입고 안내를 하나의 게시판에서 관리하는데, 이는 상품이 많아질수록 비효율적이라고 생각됨. 
*<em>=&gt; 상품 상세페이지에 공급자 공지사항 제작(CRUD) *</em></p>
</blockquote>
<p>초기 사이트에서는 관리자가 상품 변동사항이나 품절 재입고 안내에 대해 직접 입력하고 하나의 게시판에서 관리하고 있었다. 그러나 이는 상품이 많아질 수록 셀러들이 확인하는게 번거롭고 복잡할 것이라고 예상했다. 따라서 해당 상품에 대한 공지는 해당 상품에 대해서 공급자가 직접 수정 변경하는 방식이 더 효율적이라고 판단했다. </p>
<p>셀팜에서 판매 상품을 올리는 것은 관리자 측에서만 가능하게 하면 좋겠다고 말씀해주셔서, 위와 같이 직접 상품마다 공급자 권한을 부여해야 했다. ,mb_qualification 칼럼으로 공급자로 분류된 회원의 아이디를 가져와서 권한을 부여할 수 있도록 구현했다.  </p>
<h4 id="시연-영상">시연 영상</h4>
<p><img src="https://velog.velcdn.com/images/subb_ny/post/2f423faf-837f-4ee8-9b43-4bc98425106e/image.gif" alt=""></p>
<p>공급자 권한이 선택되면 상품상세페이지에서 공급자 공지사항 수정 버튼이 보이게 되고, 선택된 공급자만이 수정 가능하다. </p>
<h3 id="3-상품상세페이지-qa-기능">3. 상품상세페이지 Q&amp;A 기능</h3>
<blockquote>
<h4 id="span-stylebackground-color-rgba24217918805사이트의-문제점-및-해결-방안-구상-span-2"><span style="background-color: rgba(242,179,188,0.5)">사이트의 문제점 및 해결 방안 구상 </span></h4>
<p>셀러와 공급자가 질의응답할 수 있는 기능이 없음 
*<em>=&gt; 상품 상세페이지에 상품 Q&amp;A 제작   *</em></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/subb_ny/post/d8d0e39c-ff4b-417d-bfd3-5f9b473cc6d1/image.gif" alt=""></p>
<p>공급자 공지와 마찬가지로 Question은 모두가 작성을 할 수 있되, 답변은 해당 상품에 권한을 가진 공급자만 답변할 수 있도록 구현하였다. 
또한 해당 글 작성자와 답변자를 제외한 유저에게는 <strong>&#39;비밀글입니다&#39;</strong> 상태로 보이도록 구혔했다. 
Database: Question table과 Answer table을 만들고 unique key인 Question Id로 사용하여 foreign key로 연결해주었다. </p>
<p>🛎 <span style="background-color: #d3e3f5"> 관련 코드 및 리팩토링 </span> </p>
<blockquote>
<p><a href="https://velog.io/@subb_ny/JavaScript-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81">https://velog.io/@subb_ny/JavaScript-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</a></p>
</blockquote>
<aside>
👉 **인턴 프로젝트 성과 및 배운점**

<p><strong>1. 웹 프로그래밍의 전체적인 흐름 이해</strong></p>
<p>프론트엔드, 백엔드, 데이터베이스를 종합적으로 다룬 경험은 웹 프로그래밍의 흐름을 이해하는데 큰 도움이 되었습니다. PHP 언어는 프론트와 백엔드 분리가 쉽지 않아, 전체 시스템을 하나로 통합하고 관리하는 방법을 습득할 수 있게 되었습니다.</p>
<p><strong>2. 새로운 기술 습득과 성공적인 서비스화</strong></p>
<p>백엔드, 데이터베이스, PHP 등 처음 접한 기술들을 3개월 안에 빠르게 습득하고 성공적으로 서비스화에 기여한 경험이 있습니다. 이로써 기한 안에 세운 목표를 달성하고, 이를 토대로 <strong>인턴 연장 제의를 받았습니다.</strong></p>
<p><strong>3. 레거시 코드 분석과 문제 해결 경험</strong></p>
<p>기존 시스템의 문제를 발견하고 직접 기획하며 해결책을 마련하는 과정에서 실전적인 경험을 쌓을 수 있었습니다. 이는 단순히 요구사항을 구현하는 수동적인 자세가 아닌 <strong>직접 문제를 찾고 기획하여 구현할 수 있</strong>어, 개발자의 필수 요구 사항인 문제 해결 능력에 큰 도움이 되었다고 생각합니다.  </p>
<p><strong>4. 미래 변동성을 고려한 코드 작성의 중요성</strong></p>
<p>서비스화 과정에서 기획과 요구사항의 빈번한 변화를 경험하며, 단순한 구현이 아닌 미래의 변동성을 고려한 유연하고 확장 가능한 코드 작성의 중요성을 깨달았습니다. 미래의 요구사항에 대비하며 코드를 작성함으로써 프로젝트의 장기적인 성공에 기여한 경험이 있습니다.</p>
</aside>]]></description>
        </item>
        <item>
            <title><![CDATA[[프로젝트 비교] 로그인 회원가입 기능 구현  (상태관리)]]></title>
            <link>https://velog.io/@subb_ny/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B9%84%EA%B5%90-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@subb_ny/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B9%84%EA%B5%90-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Thu, 11 May 2023 11:39:20 GMT</pubDate>
            <description><![CDATA[<p>작년 11월에 진행했던 프로젝트와 이번 프로젝트의 로그인 회원가입 기능 구현에서 코드 차이를 비교해보면</p>
<blockquote>
<h4 id="usestate로-상태관리-했던-작년-첫-프로젝트-vs--react-hook-form을-사용했던-이번-프로젝트">useState로 상태관리 했던 작년 첫 프로젝트 VS  react-hook-form을 사용했던 이번 프로젝트</h4>
</blockquote>
<p>이렇게 정리할 수 있다. 
이런 면에서 차이가 확연히 나기 때문에 직접 비교하면서 어떤 코드가 효율적인지 고민해보면 좋을 것 같다. </p>
<hr>
<h2 id="input-상태관리">input 상태관리</h2>
<h3 id="1-작년-프로젝트-구멍마켓">(1) 작년 프로젝트 (구멍마켓)</h3>
<p> <img src="https://velog.velcdn.com/images/subb_ny/post/8bf96320-abd2-4cca-ac90-ef16607e4f8d/image.png" alt=""></p>
<p>위와 같이 필요한 정보를 입력받고 onChange이벤트가 실행될 때마다 각각의 상태변경함수에 e.target.value를 저장하였다. 또한 각각의 입력값마다 유효성 검사 확인여부를 불리언 값으로 관리하는 state도 함께 만들었다. 
이렇게 코드를 작성하면 생기는 단점들을 정리해보았다.</p>
<h3 id="💻-usestate를-통해서만-상태관리를-하는-경우">💻 UseState를 통해서만 상태관리를 하는 경우!</h3>
<blockquote>
<p>1.코드의 가독성이 떨어지고,  컴포넌트의 로직이 복잡해진다.
2. 많은 상태 변수를 업데이트하게 되면, 컴포넌트의 리렌더링이 발생할 가능성이 높아진다. 이는 성능에 영향을 줄 수 있다.
3.useState를 호출하면 각 상태 변수마다 메모리 공간이 할당 되는데, 많은 상태 변수를 가진 컴포넌트의 경우, 메모리 사용량이 증가할 수 있다. 
(실제로 크게 문제가 되진 않는다고 한다) </p>
</blockquote>
<h3 id="❓-그렇다면-해결-방법은">❓ 그렇다면 해결 방법은?</h3>
<p> 관련된 상태 변수들을 하나의 객체로 묶어서 유지하는 것이다.그렇게 관리하면 
 상태 변수의 수가 줄어들고, 상태 간의 관계와 종속성을 관리하기 쉬워진다. 
 <img src="https://velog.velcdn.com/images/subb_ny/post/ba584053-2f57-4730-a743-44b49e7358ca/image.png" alt=""></p>
<p>또한 리액트 hook중에 하나인 useReducer를 사용하여 관리할 수도 있다.
useReducer를 사용하여 리팩토링한 코드는 useReducer에 관한 글을 쓸 때 작성하도록 하겠다. </p>
<h3 id="2-올해-프로젝트-cmgg">(2) 올해 프로젝트 (CMGG)</h3>
<p>타입스크립트를 사용하고, react-hook-form 라이브러리를 사용하여 구현했다. 
<img src="https://velog.velcdn.com/images/real-bird/post/d4c7630b-b52b-4938-bb34-a7f197eddfd9/image.jpg"/>
form형식에서 대부분을 react-hook-form을 사용해서 구현했는데 그 이유를 정리해보겠다. </p>
<blockquote>
<h4 id="1-주요-api기능이-쉽고-직관적이다">1. 주요 API기능이 쉽고 직관적이다.</h4>
<p>예를 들면   handleSubmit 기능은 폼 제출을 처리하기 위해 제출 이벤트가 발생하면 등록된 유효성 검사 규칙에 따라 폼 데이터의 유효성을 검사하고, 유효한 경우에는 지정된 콜백 함수를 호출한다. 
=&gt; 즉 별도의 유효성 검사를 시행하는 코드를 짤 필요가 없다는 것이다. </p>
</blockquote>
<h4 id="2-성능-최적화">2. 성능 최적화</h4>
<p> 리액트의 효율적인 렌더링 방식과 결합하여, 불필요한 리렌더링을 최소화하고 폼 처리의 성능을 향상시킨다.
 이와 관련해서 더 조사해봤는데, 얕은 비교 (Shallow Comparison)를 시행하고, 등록(Registration)과 컨트롤(Control)을 통해서 필요한 폼 데이터만 추적하는 방식을 통해서 성능을 향상시킨다. 
 또한 입력 대기열 (Input Queue)을 사용하는데, 이를 통해 사용자의 입력에 대한 변경 사항을 추적하는데,  입력 이벤트를 버리지 않고 적절한 타이밍에 변경 사항을 처리하고 업데이트하기 때문에 Input 이벤트에 대한 불필요한 리렌더링을 줄일 수 있다. </p>
<h4 id="3-유효성-검사-및-에러-처리가-쉽다">3. 유효성 검사 및 에러 처리가 쉽다!!</h4>
<p>이 부분은 코드를 참고하여 보면 쉽게 알 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/subb_ny/post/fb8cccc4-6625-463e-960b-c16c597529b8/image.png" alt=""></p>
<p>register의 required 속성을 사용하여 submit의 필수 요소인지 아닌지를 간편하게 설정할 수 있고, pattern에 정규표현식을 넣으면 handleSubmit이 실행될때 자동적으로 유효성 검사를 시행해 준다. 
에러처리 또한 react-hook-form에서 정해준 형식대로 작성하면 매우 간단하게 에러처리를 할 수 있다. 위 코드와 같이 각각의 타입별로 에러 메세지를 다르게 띄우는 것도 쉽게 가능하다. </p>
<blockquote>
<p>또한 2번에서 설명했던 성능 최적화도 register함수를 사용해서 input 요소를 등록하고, name 속성에 &quot;password&quot;를 지정하여 해당 요소를 식별하면react-hook-form에 의해 자동으로 추적되며, 입력값은 폼 데이터로 관리되어서 onChange이벤트에 대한 불필요한 렌더링을 최소화할 수 있는 것이다.</p>
</blockquote>
<h4 id="-즉-register함수가-알아서-추적해주기-때문에-각각의-input에-onchange에-상태변경함수를-쓰지-않고도-구현가능하다-👻">=&gt; 즉 register함수가 알아서 추적해주기 때문에 각각의 input에 onChange에 상태변경함수를 쓰지 않고도 구현가능하다 👻</h4>
<h4 id="이와-같은-이유로-react-hook-form으로-구현을-했을-때-usestate만-사용하여-회원가입-기능을-구현했을-때의-단점상태관리-측면을-보완할-수-있었다">이와 같은 이유로 react-hook-form으로 구현을 했을 때, useState만 사용하여 회원가입 기능을 구현했을 때의 단점(상태관리 측면)을 보완할 수 있었다.</h4>
<hr>
<img src="https://www.jjal.today/data/file/gallery/654781973_q2ZR7YLF_81e1944cbb00107c9d2800df9b9f3ea2fad17cd0.jpg"/>

<h3 id="그렇다면--react-hook-form을-사용하는게-더-나은-방법아니야">그렇다면  react-hook-form을 사용하는게 더 나은 방법아니야?</h3>
<p>onChange에 이벤트를 걸어서 input값을 관리하는 방법에<mark style="background-color:#ebf2bd">  <strong>장점</strong></mark>은 없을까? 
 이런 의문점이 들 수 있다.
<img src="https://velog.velcdn.com/images/subb_ny/post/2ec2a498-ead1-474f-99fd-46de25ea4d40/image.gif" alt=""></p>
<p>위와 같이 onchange 이벤트를 통해 실시간으로 유효성 검사를 수행하기 때문에, 사용자는 입력이 유효한지 즉시 피드백을 받을 수 있다. react-hook-form은 사용자가 잘못된 입력을 제출하고 나서야 오류를 알게 되는 데에 반해 위와 같은 방법은 사용자 경험(UX)을 향상시킬 수 있다.</p>
<h2 id="결론">결론</h2>
<p>react-hook-form을 사용하면 개발자 입장에서 기능 구현을 쉽고 편리하게 할 수 있지만, 사용자 ux적인 측면을 고려했을 때는 이벤트를 통해서 사용자에게 즉각적인 유효성 검사의 피드백을 해주는 것이 더 나은 방법이라고 생각되었다. </p>
<p>따라서 어떤 방식으로 구현할 것인지에 대해서는 내가 만들고자 하는 방향성(사용자친화적인 ux가 중요한지, 효율성이 높고 빠른 기능구현이 중요한지)이 어디에 중점을 두고 있는지를 생각하여 구현하면 좋을 것 같다는 생각이다. </p>
<blockquote>
<p>기능 구현을 할 때 어떤 방식이 좋은지에 대해서, 장점과 단점을 항상 염두해두고 어떨 때 쓰는게 가장 효율적일지를 생각하면서 구현하는 &#39;<strong>끊임없이 생각하는 개발자</strong>&#39;가 되자</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] map함수 구현 ]]></title>
            <link>https://velog.io/@subb_ny/JS-map%ED%95%A8%EC%88%98-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@subb_ny/JS-map%ED%95%A8%EC%88%98-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Mon, 20 Feb 2023 12:26:59 GMT</pubDate>
            <description><![CDATA[<p>callback 함수를 이해하기 위해 Array.map함수를 구현해보았다. </p>
<h3 id="순서">순서</h3>
<p>map은 자바스크립트 내장함수라서 .map으로 접근이 가능하지만 
직접 구현한 함수는 arr와 callback함수를 인자로 받는다. </p>
<pre><code class="language-js"> const myMap = (array, callback) </code></pre>
<p>배열을 return해줄 것이기 때문에 빈 배열을 선언하고, </p>
<pre><code class="language-js">const myMap = (array, callback) =&gt; {
        let result = [];}</code></pre>
<p>map 함수는 map의 콜백함수가 배열을 돌면서 value값과 index값을 인자로 받기 때문에 for문을 통해서 배열의 길이 만큼 돌고 callback함수의 인자를 value값, index값으로 지정해 준 후, 
선언해 둔 result에 callback으로 return한 값을 push해준다. </p>
<pre><code class="language-js">const myMap = (array, callback) =&gt; {
        let result = [];
        for (let i = 0; i &lt; array.length; i++) {
          result.push(callback(array[i], i));
        }

      };</code></pre>
<p>push 해준 후 for문 실행이 끝났을 시에 마지막으로 result 배열을 return 해준다. </p>
<h3 id="👩💻-완성">👩‍💻 완성!!</h3>
<blockquote>
<pre><code class="language-js">const myMap = (array, callback) =&gt; {
        let result = [];
        for (let i = 0; i &lt; array.length; i++) {
          result.push(callback(array[i], i));
        }
        return result;
      };</code></pre>
</blockquote>
<pre><code>
###  실제  사용 
&gt;```js
let array1 = [1, 2, 3];
myMap(array1, (value, key) =&gt; value + key));
//결과값 [1,3,5] 



 ``` js
 (value, key) =&gt; { return value + key }</code></pre><p>key와 value를 더해주는 callback함수를 만들어 주었다. </p>
<h3 id="👻-후기">👻 후기</h3>
<blockquote>
<p>map은 callback함수를 항상 사용하여서 구현하는데 map 내부에서는 어떻게 동작되는지 궁금했었다. 또한 평소에 callback함수에 대한 이해도가 부족했다고 생각했기 때문에 이번 기회에 callback 함수를 더 공부할 수 있게 된 것 같다. </p>
</blockquote>
<blockquote>
<p>코드를 구현하면서 배열에 callback함수를 push한다는 것이 생소하였는데 생소한 코드를 자주 접하면서 다양한 코드에 익숙해져야 된다는 생각이 들었다. </p>
</blockquote>
<blockquote>
<p>또한 코드를 짤 때 의식적으로 다양한 걸 시도해보지 않으면 다른 코드를 아무리 본 들, 코드가 항상 내 습관대로 쓰는 제자리 코드가 된다는 걸 느꼈다. 
 항상 시간에 쫒겨 구현에만 급급한 코드가 아니라 효율성과 편리성을 고려한 코드를 지향해야겠다. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[error] type object 'User' has no attribute 'USERNAME_FIELD']]></title>
            <link>https://velog.io/@subb_ny/type-object-User-has-no-attribute-USERNAMEFIELD</link>
            <guid>https://velog.io/@subb_ny/type-object-User-has-no-attribute-USERNAMEFIELD</guid>
            <pubDate>Tue, 10 Jan 2023 08:25:42 GMT</pubDate>
            <description><![CDATA[<h2 id="🤯-error-code">🤯 error code</h2>
<blockquote>
<pre><code class="language-python">from django.db import models
from django.contrib.auth.models import AbstractBaseUser 
class User(AbstractBaseUser):
  pass</code></pre>
</blockquote>
<p> 장고에 있는 기본 base 유저를 상속 받으려는데 </p>
<blockquote>
<pre><code class="language-python">type object &#39;User&#39; has no attribute &#39;USERNAME_FIELD&#39; </code></pre>
</blockquote>
<p>위와 같은 error를 마주했다. 
구글링 해보니 USENAME_FIELD를 넣으면 해결되는 error였지만, 
<mark style='background-color: #ffdce0'> 왜 USENAME_FIELD를 넣어야하는지 이해가 되지 않았다. </mark>
AbstractUser는 USENAME_FIELD 없이도 에러가 생기지 않았기 때문이다. </p>
<h2 id="🤩-solution">🤩 solution</h2>
<p> 그 이유를 장고 공식문서에서 알 수 있었다. </p>
<img src=https://ifh.cc/g/46yh70.jpg width='1000px' height='800px'>

<p>  AbstractBaseUser에는 
USERNAME_FIELD = &#39;고유식별이 될  컬럼&#39; 이 필요하다 
 AbstractUser는  AbstractBaseUser와 다르게 이미 고유식별자가 포함되어 있어서 USERNAME_FIELD를 사용할 필요가 없다<br></p>
<blockquote>
<pre><code class="language-python">from django.db import models
from django.contrib.auth.models import AbstractBaseUser 
class User(AbstractBaseUser):
  email = models.EmailField(unique=True,default=&quot;&quot;)
  USERNAME_FIELD = &#39;email&#39;</code></pre>
</blockquote>
<pre><code>
 위와 같이 이메일이라는 모델의 고유한 컬럼을 넣어준 뒤 
 USERNAME_FIELD를 넣어주면 해결된다. 



 &lt;a src=&quot;https://docs.djangoproject.com/en/4.1/topics/auth/customizing/&quot;&gt;공식문서 참조&lt;/a&gt;  

&lt;img src=&quot;https://cdn.jjalbot.com/2020/03/QPPbAIEl7V/SY9jdwKFd.jpeg&quot;/&gt;

### ❗️ error를 통해 느낀점 
&gt;#### 장고 공식문서 자주활용하기 

장고는 공식문서가 정리가 상당히 잘 되어 있다. 
웬만한 에러는 최대한 공식문서를 활용하면 해결 가능하다.
지금까지 stackoverflow에 의존하였는데 
공식문서 찾아보는 것을 습관화해야겠다. </code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[react] input 작성시간 업데이트 하는 방법 ]]></title>
            <link>https://velog.io/@subb_ny/react-%ED%95%A0%EC%9D%BC%EB%AA%A9%EB%A1%9D-%EC%A4%91-%EC%9E%91%EC%84%B1%EC%8B%9C%EA%B0%84-%EC%B6%94%EA%B0%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@subb_ny/react-%ED%95%A0%EC%9D%BC%EB%AA%A9%EB%A1%9D-%EC%A4%91-%EC%9E%91%EC%84%B1%EC%8B%9C%EA%B0%84-%EC%B6%94%EA%B0%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 27 Dec 2022 09:20:52 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/subb_ny/post/258ae1aa-9312-4b5d-b730-e6cb3153a71e/image.gif" alt=""></p>
<h3 id="⛔️-문제점-발견">⛔️ 문제점 발견</h3>
<h4 id="추가할-때마다-작성된-모든-시간들이-다시-업데이트되는-문제점을-발견했다">추가할 때마다 작성된 모든 시간들이 다시 업데이트되는 문제점을 발견했다.</h4>
<blockquote>
<pre><code class="language-js">const Todo2 = () =&gt; {
  const [todos, setTodos] = useState([]); 
  const [todo, setTodo] = useState(&quot;&quot;);
  const [time, setTime] = useState(&quot;&quot;);
  // 값을 저장해줄 time선언 
  useEffect(() =&gt; {
    setTime(moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;));
  }, [time]);
  const onChange = (e) =&gt; {
    setTodo(e.target.value);
  };  //time값이 변화할때마다 업데이트해주기 
  const onSubmit = (e) =&gt; {
    e.preventDefault();
    setTime(moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;)); //submit 될 때마다 time값 바꿔주기 
    //새로고침 방지
    setTodos((current) =&gt; [todo, ...current]);
    //todos 배열에 값 넣기
    setTodo(&quot;&quot;); //input안의 값초기화
  };
  return (
    &lt;div&gt;
      &lt;form onSubmit={onSubmit}&gt;
        &lt;input
          onChange={onChange}
          placeholder=&quot;할일을 추가하세요&quot;
          value={todo}
&lt;/input&gt;
        &lt;button&gt;add to do&lt;/button&gt;
      &lt;/form&gt;
      {todos.map((todo, num) =&gt; {
        return &lt;TodoItem2 todo={todo} key={num} time={time} /&gt;;
      })}
    &lt;/div&gt;
  );
};
export default Todo2;</code></pre>
</blockquote>
<p><code>&lt;TodoItem2 todo={todo} key={num} time={time} /&gt;</code>
의 컴포넌트는 특별한 코드가 없으므로 생략한다 (props로 받아오기만 했다) </p>
<h2 id="👻-위-코드에서-time-받아오는-방법-첫번째-시도">👻 위 코드에서 Time 받아오는 방법 (첫번째 시도)</h2>
<pre><code class="language-js">  const [time, setTime] = useState(&quot;&quot;); </code></pre>
<p>useState를 사용하여 time값을 저장해줄 변수를 만든다.</p>
<pre><code class="language-js"> const onSubmit = (e) =&gt; {
    e.preventDefault();
    setTime(moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;)); //submit 될 때마다 time값 바꿔주기 
    //새로고침 방지
    setTodos((current) =&gt; [todo, ...current]);
    //todos 배열에 값 넣기
    setTodo(&quot;&quot;); //input안의 값초기화
  };</code></pre>
<pre><code class="language-html">  &lt;form onSubmit={onSubmit}&gt;</code></pre>
<p>onSubmit 함수를 만들어서 onSubmit이벤트가 실행될때 moment라이브러리를 사용해서 현재시간을 받아온 후 setTime 함수에 넣어서 time을 바꿔준다. </p>
<pre><code>   {todos.map((todo, num) =&gt; {
    return &lt;TodoItem2 todo={todo} key={num} time={time} /&gt;;
  })}</code></pre><p>  TodoItem2 (todo, time을 그려주는 컴포넌트)에 time을 넘겨준다. </p>
<blockquote>
<h4 id="😵-위와-같이-코드를-작성하니까-submit이벤트가-실행될-때마다-모든-시간이-계속-업데이트-되었다">😵 위와 같이 코드를 작성하니까 submit이벤트가 실행될 때마다 모든 시간이 계속 업데이트 되었다.</h4>
</blockquote>
<h3 id="😈-해결방법">😈 해결방법</h3>
<p>시간이 계속 업데이트 되는 이유는 submit할 때마다 setTime함수로 time값을 바꿔주는데 어떠한 저장도 하지 않고 바로 props로 time을 넘기니까 계속해서 업데이트 되는 것이다.
time을 두가지 방법으로 저장해보려고 한다. </p>
<h3 id="1첫번째-방법-todos에-객체형태로-넣어주는-방법--성공">1.첫번째 방법 (todos에 객체형태로 넣어주는 방법) =&gt; 성공!</h3>
<pre><code class="language-js">  const [todos, setTodos] = useState([]);</code></pre>
<pre><code class="language-js">  const onSubmit = (e) =&gt; {
    e.preventDefault();
    setTime(moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;));
    setTodos((current) =&gt; [{ todo: todo, time: time }, ...current]);
    setTodo(&quot;&quot;);
  };</code></pre>
<p>전 </p>
<pre><code class="language-js">setTodos((current) =&gt; [todo, ...current]);</code></pre>
<p>후   ⭕️</p>
<pre><code class="language-js">setTodos((current) =&gt; [{ todo: todo, time: time }, ...current]);</code></pre>
<p>todos에 같이 넣어주는 방법이다.
todo를 저장하고 보여주기 위해서 todos라는 배열을 만들어서 저장하고 return할때 map함수로 돌려주었다. 
time도 저장하기 위해서 todos에 객체형태로 todo와 time을 저장한 후 </p>
<pre><code class="language-js">   {todos.map((item, index) =&gt; {
        return &lt;TodoItem key={index} todo={item.todo} time={item.time} /&gt;;
      })}

</code></pre>
<p>item으로 객체를 받아와서 todo와 time을 객체의 key값으로 props로 넘겨주었다. </p>
<h3 id="2-1번째-방법---usestate를-사용하여-time을-todo와-따로-저장하는-방법-실패💧-">2-1번째 방법 - useState를 사용하여 time을 todo와 따로 저장하는 방법 (실패💧 )</h3>
<pre><code class="language-js">  const [time, setTime] = useState(&quot;&quot;);
  const [alltime, setAlltime] = useState([]);
</code></pre>
<p> 처음에는 todo와 같이 시간을 저장해줄 배열을 alltime으로 빼서 따로 저장해주는 방법을 생각했다. </p>
<pre><code class="language-js">  const onSubmit = (event) =&gt; {
    event.preventDefault();
    setTime(() =&gt; moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;));
    //submit할때 setTime으로 time 바꿔주기 
    setTodos((count) =&gt; [todo, ...count]);
    setAlltime((count) =&gt; [time, ...count]);
    //time 저장해주기
    setTodo(&quot;&quot;);
  };</code></pre>
<p>  이후에 todos를 map으로 돌리고 time은 alltime의 배열 인덱스값으로 넘겨주었다. 
  처음에는 todos를 map으로 돌리고 있는 상황에서 alltime도 새로 맵으로 돌려야 하나에 대한 고민을 했었는데 
  이렇게 돌리고 있는 맵안에 다른 배열의 인덱스값으로 넘겨주는 건 방법이 특이하다고 생각했다. 처음보는 방법이었다. </p>
<pre><code class="language-js">   {todos.map((item, index) =&gt; {
        return (
          &lt;TodoItem
            time={alltime[index]}
            todo={item}
            key={index}
            value={todo}
            index={index}
          /&gt;
        );
      })}</code></pre>
<p>  <img src="https://velog.velcdn.com/images/subb_ny/post/023db128-74f5-4503-bdba-91eb69ab30b3/image.gif" alt=""></p>
<p>  그러나 이 코드도 문제점이 있었는데 위와 같이 작성한 시간이 
  하나씩 밀린다는 것이다. (두번째 빨래하기에 들어간 시간은 그 즉시 작성한  시간이 아니라 첫번째 작성한 시간이 들어간다)</p>
<h3 id="📌-이건-또-왜-밀려">📌 이건 또 왜 밀려?????</h3>
  <img src="https://cdn.jjalbot.com/2021/12/s7BNnalArn/s7BNnalArn.jpeg"/>


<p>  alltime을 console에 찍어보니 첫 submit event를 실행할 때에는 초기값인 빈 문자열이 들어왔다.
  그 답을  <a src="https://thrillfighter.tistory.com/670">  참고 블로그1</a>  와<a src="https://junior-datalist.tistory.com/67"> 참고 블로그2</a>
  를 통해서 찾을 수 있었다. </p>
<blockquote>
<h3 id="setstate-가-비동기-방식으로-처리-된다는-것이다">setstate 가 비동기 방식으로 처리 된다는 것이다</h3>
</blockquote>
<p>그래서 </p>
<pre><code class="language-js">setTime(() =&gt; moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;));

setAlltime((count) =&gt; [time, ...count]);</code></pre>
<p>이 두 코드가 같은 함수안에서 작동을 하고 있었는데 둘다 비동기 방식으로 동작해서 함수가 실행됐을 때 setTime애서 time을 현재시간으로 바뀐 뒤, alltime배열에 넣는게 아니라 각각 개별적으로 처리 되기 때문에 alltime(배열)에는 time의 초기값인 &#39;&#39;(빈문자열)이 들어가게 되는 것이다. </p>
<p>결국 time을 useState로 관리해줬기 때문이라는 원인을 찾았다!!</p>
<h3 id="2-2번째-방법-let으로-변수할당을-해서-time을-todo와-따로-저장하는-방법성공🍔">2-2번째 방법 let으로 변수할당을 해서 time을 todo와 따로 저장하는 방법(성공🍔)</h3>
<p>전코드 </p>
<pre><code class="language-js">setTime(() =&gt; moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;));</code></pre>
<p>바꾼 코드 </p>
<blockquote>
</blockquote>
<pre><code class="language-js"> let time = moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;);

useState를 사용하지 않고 time을 변수에 저장해주고 onSubmit함수에 넣어줬더니 제대로 작동했다!!! 

 ## ❗️❗️그렇다면 첫번째 저장 방법 과 두번째 저장 방법 중 어떤 것이 더 효율적일까?? 

useState의 가장 큰 특징은 상태값이 변경 되었을 때, state가 포함된 컴포넌트를 자동으로 재렌더링 해준다는 것이다. 위 코드에서 time은 submit 이벤트가 일어날때만 변경되는 것이기 때문에 굳이 useState를 쓰지 않아도 된다라는 생각이다.
또한 개인적인 생각은 
let을 사용해서 현재시간을 가져오면 ```moment().format(&quot;YYYY-MM-DD HH:mm:ss&quot;)```가  time에 바로 할당이 되지만, useState는 setTime이라는 setter함수를 거쳐서 넣어줘야 함으로 효율성 측면에서는 일반변수를 사용하는 것이 더 효과적이지 않을까 하는 생각이다. 






## 👩‍💻 여러 삽질을 하면서 느낀점 

처음에 이렇게 TodoList에 대해서 깊게 파게 된건 강사님의 코드에 의문점을 가지면서 시작되었다. 여기서 왜 이렇게 쓰셨지 왜 이러면 무한루프가 도는거지? 라는 생각으로 이 코드도 넣어보고 저 코드도 넣어보고 잉??도 많이 하면서 useState나 useEffect에 대한 개념이 좀 더 정리가 된 거 같다는 느낌이 들었다. 
 위 글에서는 다루지 않았지만 고민하는 와중에 useEffect도 사용해봤는데 오잉 이렇게 된다고?? 라고 생각이 드는 코드가 있어서 그 문제에 대해서도 몇주동안 고민했다. 
결국 그에 대한 해답도 찾아서 후속으로 블로그를 쓰려고 한다. 



오랜시간 동안 저 간단한 코드로 이것저것 뜯어보고 넣어보면서 
시간이 지나면 이해가 될 것을 내가 너무 깊게 들어가고 집착을 하나? 라는 생각이 들기도 했다. 
근데 여러 오류들과 마주하고 해결책을 찾는 과정에서 비동기,동기에 대해서도 이해하게 되고, 리액트의 동작원리에 대해 거의 몰랐었는데 이제는 아주 조금은 감이 온 것 같다. ㅎㅎ 내가 어떤 부분이 부족하고 어떤 걸 더 공부하면 좋을 지 알게 된 거 같아서 뿌듯하기도 했다. 

내 공부방식에 더 이상 흔들리지 말아야겠다. 
맞고 틀린건 없으니까 ㅎㅎ </code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[HTML] srcset 속성 사용하는 이유와 작동 프로세스 ]]></title>
            <link>https://velog.io/@subb_ny/HTML-srcset-%EC%86%8D%EC%84%B1-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EC%99%80-%EC%9E%91%EB%8F%99-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4</link>
            <guid>https://velog.io/@subb_ny/HTML-srcset-%EC%86%8D%EC%84%B1-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EC%99%80-%EC%9E%91%EB%8F%99-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4</guid>
            <pubDate>Mon, 12 Dec 2022 09:35:54 GMT</pubDate>
            <description><![CDATA[<p>아직 완벽하게 이해한 건 아니지만 설명이 잘되어 있는 <a href="https://codingcoding.tistory.com/386"> 블로그</a>가 있어서 이를 기반으로 작성하고 추후 계속하여 수정할 예정이다. </p>
<h2 id="1-img-태그의-srcset속성">1. img 태그의 srcset속성</h2>
<h3 id="1-정의">(1) 정의</h3>
<blockquote>
<p>각각 다른 다양한 상황에서 사용될 이미지의 url과 원본 크기를 지정해주는 속성</p>
</blockquote>
<h3 id="2-사용-이유">(2) 사용 이유</h3>
<blockquote>
<ul>
<li>작은 화면에 UI를 나타낼 때 사이즈가 큰 이미지 파일을 보내면 통신 시간이 길어짐   =&gt;❗성능저하 </li>
</ul>
</blockquote>
<ul>
<li>작은 화면에 어울리는 이미지를 큰 화면에서 열면 이미지가 흐릿하게 보임.<h4 id="💡-표시-장치에-따라-이미지를-바꿀-필요가-있다--반응형-이미지-필요-responive-image">💡 표시 장치에 따라 이미지를 바꿀 필요가 있다 =&gt; 반응형 이미지 필요 (Responive Image)</h4>
</li>
</ul>
<h3 id="3-사용-방법">(3) 사용 방법</h3>
<h4 id="💭-기본-구조">💭 기본 구조</h4>
<blockquote>
<pre><code class="language-html">&lt;img src=&quot;flamingo-fallback.jpg&quot;
     srcset=&quot;flamingo-fallback1x.jpg 1000w,
             flamingo-fallback2x.jpg 2000w,
             flamingo-fallback3x.jpg 3000w&quot; /&gt;</code></pre>
</blockquote>
<p>여기에서 w를 정확히 정의하면 실제 이미지의 크기를 의미하지만 srcset의 원리를 이해하기 위해서 픽셀수라고 생각하면 좀 더 쉽게 이해 가능하다. 
즉, 1000w은 1000픽셀 폭의 이미지를, 2000w은 2000픽셀 폭의 이미지라고 생각하면 된다. </p>
<blockquote>
<h3 id="이미지w-🔜--브라우저">이미지(w) 🔜  브라우저</h3>
</blockquote>
<p>이미지의 width(w)를 브라우저에 전달하고, 브라우저는 전달받은 정보를 바탕으로 </p>
<blockquote>
<p>flamingo-fallback1x <span style='background-color: #ffd33d'>1000w</span>.jpg<br>flamingo-fallback2x <span style='background-color: #0366d6'> 2000w</span>.jpg 
     flamingo-fallback3x <span style='background-color: #28a745'>3000w</span>.jpg
       중 어떤 이미지를 보여줄지 선택한다.</p>
</blockquote>
<h4 id="💭-브라우저가-이미지-선택하는-방법">💭 브라우저가 이미지 선택하는 방법</h4>
<p>예를 들어 내가 사용하고 있는 화면이 1800w의 화면이라고 가정할 때,
위의 3개의 이미지 중에 화면 너비를 꽉 채울 수 있는 img를 선택한다. 
그렇다면 어떤 이미지가 화면 너비를 꽉 채울 수 있을까?? </p>
<h3 id="🧠-브라우저의-알고리즘">🧠 브라우저의 알고리즘</h3>
<h4 id="과연-브라우저는-어떤-이미지를-선택할까">과연 브라우저는 어떤 이미지를 선택할까??</h4>
<img src="https://mblogthumb-phinf.pstatic.net/MjAxODExMDZfMTE5/MDAxNTQxNDMwMjg1MjM4.WsfuX8GdOf0hbLKBOQ6xRLem1HhRvH_IS5RbV4VRmPMg.UrRWhoCibZgaA-hNkXovMh_u5eQdQ4HhyriYnU1ine8g.JPEG.cosl922/output_3400057324.jpg?type=w800" width="300px"/>

<p>바로바로바로바로 </p>
<blockquote>
<p>w와 화면 비율이 1 이상인 것들 중 가장 작은 이미지 선택한다는 것이다</p>
</blockquote>
<h4 id="지금부터-브라우저의-알고리즘을-천천히-알아보자">지금부터 브라우저의 알고리즘을 천천히 알아보자</h4>
<h4 id="1-flamingo-fallback1x-span-stylebackground-color-ffd33d1000wspanjpg-❌">1. flamingo-fallback1x <span style='background-color: #ffd33d'>1000w</span>.jpg ❌</h4>
<p> =&gt; 화면은 1800w인데 위 이미지는 1000w이기 때문에 화면 너비를 꽉 채워서 보여 줄 수 <span style='color:red'>없다</span>.</p>
<p>쉽게 말하면 화면은 1800픽셀인데 이미지는 1000픽셀이다. 그렇다면 브라우저는 &quot;픽셀 수가 부족하네?&quot; 라고 느끼고 픽셀을 계산 해본다. </p>
<blockquote>
<p>1800px 화면 폭에 1000w 이미지를 표시하는 것 =&gt; 1000/1800 = <strong>0.555</strong></p>
</blockquote>
<p>결과 값이 1 미만이면 이미지의 픽셀 수가 부족하여 이미지가 깨진다는 것을 의미한다.</p>
<img src="https://mblogthumb-phinf.pstatic.net/20141015_246/smile__0410_1413361444394w9w4Y_JPEG/%C0%AF%BF%EB%C7%D1_%C2%A911.jpg?type=w2" width="400px" height="200px" />

<p>브라우저는 이러한 과정으로 &quot;1번 이미지는 깨지니까 못쓰겠군&quot;이라는 결론에 도달하고 1번 이미지를 보여주지 않는다. </p>
<h4 id="2-flamingo-fallback2x-span-stylebackground-color-0366d6-2000wspanjpg--⭕">2. flamingo-fallback2x <span style='background-color: #0366d6'> 2000w</span>.jpg  ⭕</h4>
<blockquote>
<p>1800px 화면 폭에 2000w 이미지를 표시하는 것 =&gt; 2000/1800 = <strong>1.11</strong>
<img src="" /></p>
</blockquote>
<h4 id="3-flamingo-fallback3x-span-stylebackground-color-28a7453000wspanjpg--❌">3. flamingo-fallback3x <span style='background-color: #28a745'>3000w</span>.jpg  ❌</h4>
<blockquote>
<p>1800px 화면 폭에 3000w 이미지를 표시하는 것 =&gt; 3000/1800 = <strong>1.66</strong></p>
</blockquote>
<p>두 이미지 모두 비율이 1이 넘는다. 앞에서 브라우저는** &quot;w와 화면의 비율이 1 이상인 것들 중 가장 작은 이미지 선택&quot; ** 한다고 했다. 
따라서 최종적으로 이미지 크기가 더 작은 <span style='background-color: #fff5b1'> 2번 이미지 </span>가 선택 되는 것이다. </p>
<h3 id="4-이미지-크기-조정-방법sizes">(4) 이미지 크기 조정 방법(sizes)</h3>
<p>여기서 srcset만 단독으로 사용하게 된다면 
이미지는 화면크기에 가득 차는 상태가 기준이 된다. </p>
<p>항상 이미지가 화면에 가득 차는 걸 보여주고 싶은 건 아니니까 ... 
나온 속성이** sizes**이다 !! </p>
<p>예를 들어 이미지를 화면의 1/10의 크기로 보여주고 싶다면 </p>
<blockquote>
<pre><code class="language-html">&lt;img src=&quot;flamingo-fallback.jpg&quot;
     srcset=&quot;flamingo-fallback1x.jpg 1000w,
             flamingo-fallback2x.jpg 2000w,
             flamingo-fallback3x.jpg 3000w&quot; 
     size=&quot;10vw&quot;/&gt;</code></pre>
</blockquote>
<pre><code>
위와 같이 sizes 속성을 적어주면 된다. 

여기서 size=&quot;10vw&quot;는 &quot;viewport width&quot;의 약자이며 
여기서 100vw가 화면을 가득 채운 비율이다. 

&gt; #### 10vw = 화면폭의 1/10  
( 10vw/ 100vw )
10vw : 안녕? 나는 화면 폭의 1/10의 사진이야 🤲
#### 50vw = 화면폭의 1/2 
(50vw/ 100vw)
50vw: 안녕? 나는 화면 폭의 1/2의 사진이야 🤲







</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2022.10.19 댓글기능구현 첫번째 방법 ]]></title>
            <link>https://velog.io/@subb_ny/2022.10.19-%EB%8C%93%EA%B8%80%EA%B8%B0%EB%8A%A5%EA%B5%AC%ED%98%84-%EC%B2%AB%EB%B2%88%EC%A7%B8-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@subb_ny/2022.10.19-%EB%8C%93%EA%B8%80%EA%B8%B0%EB%8A%A5%EA%B5%AC%ED%98%84-%EC%B2%AB%EB%B2%88%EC%A7%B8-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 19 Oct 2022 12:30:49 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-javascript">import React, { useState } from &quot;react&quot;;
import { FontAwesomeIcon } from &quot;@fortawesome/react-fontawesome&quot;;
import {
  faEllipsis,
  faHeart,
  faArrowUpFromBracket,
} from &quot;@fortawesome/free-solid-svg-icons&quot;;

import { faComment, faBookmark } from &quot;@fortawesome/free-regular-svg-icons&quot;;

export default function Feed() {
  const [comment, setComment] = useState();
  const [id, setId] = useState(&quot;&quot;);
  const [commentArray, setCommentArray] = useState([
    {
      id: 0,
      content: &quot;누가 찍었는지 잘 찍었다&quot;,
    },
  ]);

  const addComment = () =&gt; {
    setId(id + 1);
    const newComment = {
      id: id,
      content: comment,
    };
    setCommentArray([...commentArray, newComment]);
  };

  return (
    &lt;&gt;
      &lt;div className=&quot;feed-body&quot;&gt;
        &lt;div className=&quot;feed-header&quot;&gt;
          &lt;span className=&quot;profile&quot;&gt;
            &lt;img
              className=&quot;profile-img&quot;
              src=&quot;/image/C2DA6EB1-C439-4CE6-8795-DE3AFE708C06-630-000000B1B2E8E8F1.JPG&quot;
            /&gt;
            &lt;span&gt;subb_ny&lt;/span&gt;
          &lt;/span&gt;
          &lt;span className=&quot;etc&quot;&gt;
            &lt;FontAwesomeIcon icon={faEllipsis} className=&quot;ellipsis&quot; /&gt;
          &lt;/span&gt;
        &lt;/div&gt;
        &lt;div className=&quot;feed-picture&quot;&gt;
          &lt;img src=&quot;/image/79F26257-C1CB-4416-BF12-8FDAEF282FD7.JPG&quot; /&gt;
        &lt;/div&gt;
        &lt;div className=&quot;feed-menu&quot;&gt;
          &lt;span&gt;
            &lt;FontAwesomeIcon icon={faHeart} className=&quot;fa-heart&quot; size=&quot;2x&quot; /&gt;
            &lt;FontAwesomeIcon
              icon={faComment}
              className=&quot;fa-comment&quot;
              size=&quot;2x&quot;
            /&gt;
            &lt;FontAwesomeIcon
              icon={faArrowUpFromBracket}
              className=&quot;fa-arrow-up-from-bracket&quot;
              size=&quot;2x&quot;
            /&gt;
          &lt;/span&gt;
          &lt;span&gt;
            &lt;FontAwesomeIcon
              icon={faBookmark}
              className=&quot;fa-bookmark&quot;
              size=&quot;2x&quot;
            /&gt;
          &lt;/span&gt;
        &lt;/div&gt;
        &lt;div className=&quot;feed-comment&quot;&gt;
          &lt;div className=&quot;like&quot;&gt;
            &lt;img src=&quot;/image/1F3451CE-5EC1-459C-82C0-403BA49BE8D7-630-000000B60631D089.JPG&quot; /&gt;
            &lt;span&gt;bbyounghyun&lt;/span&gt;님 &lt;span&gt;외 1013명&lt;/span&gt;이 좋아합니다.
          &lt;/div&gt;
          &lt;span className=&quot;my-comment&quot;&gt;
            &lt;span className=&quot;my-name&quot;&gt;subb_ny&lt;/span&gt;아아아아아아아즈벤야
            키오마아아아아...&lt;span className=&quot;thebogi&quot;&gt;더보기&lt;/span&gt;
          &lt;/span&gt;
          &lt;div className=&quot;your-comment&quot;&gt;
            &lt;div&gt;
              &lt;span className=&quot;your-name&quot;&gt;bbyounghyun&lt;/span&gt;
              나나나나나나나나나나낫
            &lt;/div&gt;
            &lt;span&gt;
              &lt;FontAwesomeIcon icon={faHeart} className=&quot;fa-heart heart2 &quot; /&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;div className=&quot;comment-write&quot;&gt;
            {commentArray.map((comment) =&gt; {
              return &lt;li key={comment.id}&gt;{comment.content}&lt;/li&gt;;
            })}
          &lt;/div&gt;
          &lt;div className=&quot;time&quot;&gt;42분전&lt;/div&gt;
        &lt;/div&gt;
        &lt;div className=&quot;comment-bar&quot;&gt;
          &lt;input
            type=&quot;text&quot;
            placeholder=&quot;댓글달기....&quot;
            onChange={(e) =&gt; {
              setComment(e.target.value);
            }}
          /&gt;
          &lt;button className=&quot;gaesi&quot; onClick={addComment}&gt;
            게시
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[javascript] sort() ]]></title>
            <link>https://velog.io/@subb_ny/javascript-sort</link>
            <guid>https://velog.io/@subb_ny/javascript-sort</guid>
            <pubDate>Wed, 12 Oct 2022 04:30:11 GMT</pubDate>
            <description><![CDATA[<h3 id="sort-기본구조">sort() 기본구조</h3>
<ul>
<li>문자열 정렬 방식 <blockquote>
<pre><code class="language-javascript">let array = [t,e,d,h,q] 
array.sort(); </code></pre>
</blockquote>
</li>
</ul>
<p> ```</p>
<ul>
<li>숫자형 정렬 방식 <blockquote>
<pre><code class="language-javascript">let array = [2,1,4,9,3,123] 
array.sort( function(a, b){
   return a - b
  }); </code></pre>
</blockquote>
</li>
</ul>
<h3 id="❗️작동원리">❗️작동원리</h3>
<ol>
<li>a,b는 array안의 자료이다 </li>
<li>return값(a-b)이 양수면 a를 오른쪽으로 보냄 </li>
<li>return값 (a-b)이 음수면 b를 오른쪽으로 보냄 </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022.10.04 
event.preventDefault()]]></title>
            <link>https://velog.io/@subb_ny/2022.10.04-event.preventDefault</link>
            <guid>https://velog.io/@subb_ny/2022.10.04-event.preventDefault</guid>
            <pubDate>Tue, 04 Oct 2022 13:09:40 GMT</pubDate>
            <description><![CDATA[<h2 id="eventpreventdefault">event.preventDefault()</h2>
<blockquote>
<pre><code class="language-javascript">function 버튼활성화(event) {
  let idValue = document.getElementsByClassName(&quot;login&quot;)[0].value;
  let pwValue = document.getElementsByClassName(&quot;login&quot;)[1].value;
  if (idValue.length &gt; 4 &amp;&amp; pwValue.length &gt; 4) {
    loginButton.style.backgroundColor = &quot;rgb(0 149 246)&quot;;
  } else {
    loginButton.style.backgroundColor = &quot;rgb(197 225 251)&quot;;
    event.preventDefault();
  }
}</code></pre>
</blockquote>
<p>```</p>
<h3 id="📕-error">📕 error</h3>
<p>버튼을 활성화, 비활성화하는 과정에서 다음 단계로 넘어갈 수 없게 하기 위해
event.preventDefault() 를 사용하려고 했으나 작동이 안되었다 </p>
<h3 id="📘-solution">📘 solution</h3>
<p>코드를 자세히 보면 &#39;버튼비활성화&#39;란 함수에 아무런 이벤트가 걸려있지 않다. 
onclick 속성이나 addEventListener를 사용하지 않아서 이벤트 자체가 없었기 때문에 제제할 이벤트 자체가 없었던 것이다. 
그래서 이 코드에서는 event.preventDefault()를 쓰는 것 보다 
disabled 속성을 이용해야 한다.</p>
<blockquote>
<pre><code class="language-javascript">let loginButton = document.getElementsByClassName(&quot;blue-button&quot;)[0];
let id = document.getElementsByClassName(&quot;login&quot;)[0];
let pw = document.getElementsByClassName(&quot;login&quot;)[1];
function activeBtn() {
  let idValue = document.getElementsByClassName(&quot;login&quot;)[0].value;
  let pwValue = document.getElementsByClassName(&quot;login&quot;)[1].value;
  if (idValue.length &gt; 4 &amp;&amp; pwValue.length &gt; 4) {
    loginButton.style.backgroundColor = &quot;rgb(0 149 246)&quot;;
    loginButton.disabled = false;
  } else {
    loginButton.style.backgroundColor = &quot;rgb(197 225 251)&quot;;
    loginButton.disabled = true;
  }
}
id.addEventListener(&quot;input&quot;, activeBtn);
pw.addEventListener(&quot;input&quot;, activeBtn);</code></pre>
</blockquote>
<p>버튼 태그가 boolean 타입이라서 true,false를 통해 활성화,비활성화를 시킬 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[javascript] 캐러셀 만들기 ]]></title>
            <link>https://velog.io/@subb_ny/javascript-%EC%BA%90%EB%9F%AC%EC%85%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@subb_ny/javascript-%EC%BA%90%EB%9F%AC%EC%85%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 21 Sep 2022 05:31:41 GMT</pubDate>
            <description><![CDATA[<h1 id="버튼-누르면-이미지-슬라이드">버튼 누르면 이미지 슬라이드</h1>
<h2 id="1-html">1. HTML</h2>
<blockquote>
<pre><code class="language-html"> &lt;div style=&quot;overflow: hidden&quot;&gt;
      &lt;div class=&quot;slide-container&quot;&gt;
        &lt;div class=&quot;slide-box&quot;&gt;
          &lt;img
            src=&quot;https://codingapple.com/wp-content/uploads/2022/02/car2.png&quot;
            alt=&quot;&quot;
          /&gt;
        &lt;/div&gt;
        &lt;div class=&quot;slide-box&quot;&gt;
          &lt;img
            src=&quot;https://codingapple.com/wp-content/uploads/2022/02/car1.png&quot;
            alt=&quot;&quot;
          /&gt;
        &lt;/div&gt;
        &lt;div class=&quot;slide-box&quot;&gt;
          &lt;img
            src=&quot;https://codingapple.com/wp-content/uploads/2022/02/car3.png&quot;
            alt=&quot;&quot;
          /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;button class=&quot;slide-1&quot;&gt;1&lt;/button&gt;
    &lt;button class=&quot;slide-2&quot;&gt;2&lt;/button&gt;
    &lt;button class=&quot;slide-3&quot;&gt;3&lt;/button&gt;```</code></pre>
</blockquote>
<ul>
<li>부모 div에 overflow:hidden으로 나머지 이미지 숨기기</li>
</ul>
<h2 id="2css">2.CSS</h2>
<blockquote>
<pre><code class="language-css">.slide-container {
  width: 300vw;
  transition: all 1s;
  /*transform: translateX(-100vw); */
  /*margin-left보다 부드러움  */
  /*margin-left: -100vw; 이렇게 표현해서 클래스 추가하는 방법  */
}
.slide-box {
  width: 100vw;
  float: left;
}
.slide-box img {
  width: 100%;
}</code></pre>
</blockquote>
<ul>
<li><p>필수: transition: all ls;</p>
</li>
<li><p>둘 중 하나 </p>
<blockquote>
<ul>
<li>margin-left: -100vw;      </li>
</ul>
</blockquote>
<ul>
<li>transform: translateX(-100vw); </li>
</ul>
<p>2번이 좀 더 부드러움</p>
</li>
</ul>
<h2 id="3-javascript">3. javascript</h2>
<ul>
<li><p>제이쿼리</p>
<blockquote>
</blockquote>
   <script>
    $(".slide-2").on("click", function () {
      $(".slide-container").css("transform", "translateX(-100vw)");
    });
    $(".slide-1").on("click", function () {
      $(".slide-container").css("transform", "translateX(0vw)");
    });
    $(".slide-3").on("click", function () {
      $(".slide-container").css("transform", "translateX(-200vw)");
    });
  </script>

<p>$(&quot;.slide-container&quot;).css(&quot;transform&quot;, &quot;translateX(0vw)&quot;);</p>
</li>
<li><p>바닐라자바스크립트 </p>
<blockquote>
<pre><code></code></pre></blockquote>
<script>
document.querySelector(".slide-container").style.transform =
      ("translateX(-100vw)");
</script></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[javascript] 로그인 형식 검증 ]]></title>
            <link>https://velog.io/@subb_ny/javascript-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%98%95%EC%8B%9D-%EA%B2%80%EC%A6%9D</link>
            <guid>https://velog.io/@subb_ny/javascript-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%98%95%EC%8B%9D-%EA%B2%80%EC%A6%9D</guid>
            <pubDate>Tue, 20 Sep 2022 12:47:27 GMT</pubDate>
            <description><![CDATA[<h3 id="1변수설정">1.변수설정</h3>
<blockquote>
<pre><code class="language-javascript"> $(&quot;form&quot;).on(&quot;submit&quot;,function(){ //&quot;click&quot;도 가능 
         let idType = document.getElementById(&quot;email&quot;).value
         let regExp = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.[a-zA-Z]{2,4}$/;
         let passwordType = document.getElementById(&quot;password&quot;).value
        ```</code></pre>
</blockquote>
<h3 id="2-정규표현식-사용하여-로그인-형식-검증">2. 정규표현식 사용하여 로그인 형식 검증</h3>
<blockquote>
<pre><code class="language-javascript">       if (document.getElementById(&quot;email&quot;).value ==&quot;&quot; || document.getElementById(&quot;password&quot;).value == &quot;&quot;){     
          alert(&quot;다시 입력해주세요&quot;)
          event.preventDefault()
        }else if (passwordType.length &lt; 6) {
          alert(&quot;비밀번호 형식에 맞지 않습니다 &quot;)
          event.preventDefault()   
        }else if (regExp.test(idType)== false){
          alert(&quot;이메일 형식을 지켜주세요 &quot;)
          event.preventDefault()
        }else if ( /[A-Z]/.test(passwordType)== false){
          alert(&quot;비밀번호에 대문자 넣으셔야합니다&quot;)
        event.preventDefault()
        }
      })</code></pre>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022.09.01  ]]></title>
            <link>https://velog.io/@subb_ny/2022.09.01</link>
            <guid>https://velog.io/@subb_ny/2022.09.01</guid>
            <pubDate>Tue, 20 Sep 2022 12:41:34 GMT</pubDate>
            <description><![CDATA[<h2 id="1var-const-let-차이">1.var const let 차이</h2>
<h3 id="var">var</h3>
<p>재선언 됨 
재할당 됨 
범위 function내 </p>
<h3 id="let">let</h3>
<p>재선언 안됨
재할당 됨 
범위 {} (중괄호 안만) </p>
<h3 id="const">const</h3>
<p>재선언 안됨 
재할당 됨 
범위 {} 
재할당이 필요없는 경우, const를 사용해 불필요한 변수의 재사용을 방지하고, 재할당이 필요한 경우 let을 사용하는 것이 좋음.</p>
<h2 id="2에러가-났을-때는-오타-가장-먼저-확인">2.에러가 났을 때는 오타 가장 먼저 확인</h2>
<p>알파벳 하나하나 자세히 확인 </p>
<h2 id="3--html">3  .html</h2>
<p>html 태그 안의 글자 변경하고 싶다면 
get엘리먼트를 통해 해당요소 가져온 후 
.html 사용 </p>
<h2 id="4-addclass-removeclass">4. .addClass .removeClass</h2>
<p>클래스 추가, 제거</p>
<h2 id="5settimeout-setinterval">5.setTimeout setInterval</h2>
<p>  setTimeout(function () {
        $(&quot;.alert&quot;).hide();
      }, 1000); //몇초후에 </p>
<pre><code>  setInterval(function () {
    console.log(&quot;안녕&quot;);
  }, 1000); //초마다!!!</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[2022.09.07 css]]></title>
            <link>https://velog.io/@subb_ny/2022.09.07-css</link>
            <guid>https://velog.io/@subb_ny/2022.09.07-css</guid>
            <pubDate>Tue, 20 Sep 2022 12:41:11 GMT</pubDate>
            <description><![CDATA[<h2 id="css">css</h2>
<h3 id="1-margin-auto">1. margin: auto;</h3>
<blockquote>
<p><code>block</code> 요소일 때 <code>width</code> 값을 주면 더 이상 늘어나지 않게된다.
이 때 <code>margin</code> 에 <code>auto</code> 로 설정하면 요소를 가로 중앙에 오게 할 수 있다.</p>
</blockquote>
<h3 id="2table-요소">2.table 요소</h3>
<blockquote>
<p><code>rowspan</code> 은 행 병합, <code>colspan</code> 은 열을 병합합니다.</p>
</blockquote>
<h3 id="3-버튼-2개를-중앙정렬하는-방법">3. 버튼 2개를 중앙정렬하는 방법</h3>
<p><a href="https://moongom.tistory.com/entry/CSS-%EB%B2%84%ED%8A%BC-%EA%B0%80%EC%9A%B4%EB%8D%B0-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0-button?category=997297">https://moongom.tistory.com/entry/CSS-%EB%B2%84%ED%8A%BC-%EA%B0%80%EC%9A%B4%EB%8D%B0-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0-button?category=997297</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[javascript] 2022.09.20]]></title>
            <link>https://velog.io/@subb_ny/javascript-2022.09.20</link>
            <guid>https://velog.io/@subb_ny/javascript-2022.09.20</guid>
            <pubDate>Tue, 20 Sep 2022 12:40:49 GMT</pubDate>
            <description><![CDATA[<h2 id="1input-change-event">1.input, change event</h2>
<pre><code class="language-javascript"> document.getElementById(&quot;email&quot;).addEventListener(&#39;input&#39;, function(){ };//input이 변할 때 (change랑 비슷 이벤트)
</code></pre>
<p>input 이벤트는 input에 뭔가 작성될 때 생성되는 이벤트 
change는 input에 뭔가 작성되고 다른 곳을 클릭했을 때 생성되는 이벤트 </p>
<h2 id="2-연산자">2. 연산자</h2>
<blockquote>
</blockquote>
<h4 id="-다름">!= 다름</h4>
<h4 id="-엄격한-비교">=== 엄격한 비교</h4>
<h4 id="-느슨한-비교">== 느슨한 비교</h4>
<h4 id="and">&amp;&amp; and</h4>
<h4 id="--or">||  or</h4>
<p>느슨한 비교 : 1 == &#39;1&#39;
엄격한 비교: 1 !== &#39;1&#39; </p>
<p>엄격한 비교는 타입까지 같아야함 </p>
<h2 id="3-변수에-1-하는-법">3. 변수에 +1 하는 법</h2>
<blockquote>
<p>변수++ 
변수+= 1 
변수= 변수+ 1</p>
</blockquote>
<h2 id="4-클래스-추가-방법">4. 클래스 추가 방법</h2>
<ol>
<li>제이쿼리 </li>
</ol>
<blockquote>
<pre><code class="language-javascript">.addClass()</code></pre>
</blockquote>
<p>2.바닐라 자바스크립트 </p>
<blockquote>
<pre><code class="language-javascript">  .classList.add()</code></pre>
</blockquote>
<h2 id="5-settimeout">5. setTimeout</h2>
<p>x초후에 코드 실행하는 내장메서드</p>
<blockquote>
<pre><code class="language-javascript">setTimeout(function(){실행할코드},ms)</code></pre>
</blockquote>
<h2 id="6-setinterval">6. setInterval</h2>
<p>x초마다 코드 실행하는 내장메서드 </p>
<blockquote>
<pre><code class="language-javascript">setInterval(function(){실행할코드},ms)</code></pre>
</blockquote>
<h2 id="7-includes">7. .includes()</h2>
<p>문자검사하는 방법 </p>
<blockquote>
<pre><code class="language-javascript">&#39;abc&#39;.includes(&#39;a&#39;)
// 결과값= true</code></pre>
</blockquote>
<p>정규식이 더 디테일한 문자검사가능 </p>
<h2 id="8-eventpreventdefault">8. event.preventDefault()</h2>
<pre><code class="language-javascript">  $(&quot;form&quot;).on(&quot;submit&quot;,function(){ //&quot;click&quot;도 가능 
        if (document.getElementById(&quot;email&quot;).value ==&quot;&quot; || document.getElementById(&quot;password&quot;).value == &quot;&quot;){

          alert(&quot;다시 입력해주세요&quot;)
          event.preventDefault()

        }else if (document.getElementById(&quot;password&quot;).value.length &lt; 6){
          alert(&quot;비밀번호를 더 길게 입력하세요&quot;)
          event.preventDefault()
        }else if (/\S+@\S+.\S|/.test()){

          alert(&quot;이메일 형식을 지켜주세요 &quot;)
          event.preventDefault()
        }
</code></pre>
<p>Event 인터페이스의 preventDefault() 메서드는 어떤 이벤트를 명시적으로 처리하지 않은 경우, 해당 이벤트에 대한 사용자 에이전트의 기본 동작을 실행하지 않도록 지정합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[javascript]2022.09.17]]></title>
            <link>https://velog.io/@subb_ny/javascript2022.09.17</link>
            <guid>https://velog.io/@subb_ny/javascript2022.09.17</guid>
            <pubDate>Tue, 20 Sep 2022 12:40:35 GMT</pubDate>
            <description><![CDATA[<h2 id="1-input-입력값-불러오기">1. input 입력값 불러오기</h2>
<blockquote>
<pre><code class="language-javascript"> $(&quot;form&quot;).on(&quot;submit&quot;,function(){
        if (document.getElementById(&quot;send&quot;).value == &quot;&quot;){
          alert(&quot;다시 입력해주세요&quot;)
        }</code></pre>
</blockquote>
<p>   .value 메서드를 쓰면 된다 
   오류가 났었는데 제이쿼리 문법을 if문에 쓰려고 하다가 제이쿼리는 .value를 메서드로 사용하지 않아서 제대로 작동하지 않았다. 
   참고로 제이쿼리는 value값을 불러올 때, .val() 메서드를 사용한다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[css] 이중 애니메이션 주기 (실패)]]></title>
            <link>https://velog.io/@subb_ny/css-%EC%9D%B4%EC%A4%91-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EC%A3%BC%EA%B8%B0-%EC%8B%A4%ED%8C%A8</link>
            <guid>https://velog.io/@subb_ny/css-%EC%9D%B4%EC%A4%91-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EC%A3%BC%EA%B8%B0-%EC%8B%A4%ED%8C%A8</guid>
            <pubDate>Sat, 17 Sep 2022 09:53:45 GMT</pubDate>
            <description><![CDATA[<h2 id="html">html</h2>
<blockquote>
<pre><code class="language-html"></code></pre>
</blockquote>
<ul class="list-group ">
    <li class="list-group-item">An item</li>
    <li class="list-group-item">A second item</li>
    <li class="list-group-item">A third item</li>
    <li class="list-group-item">A fourth item</li>
    <li class="list-group-item">And a fifth one</li>
  </ul> 
  <p class="hello">안녕</p>
 <script>
  $(".hello").html("바보"); 
  document.getElementsByClassName("navbar-toggler")[0].addEventListener('click',function(){
    document.getElementsByClassName('list-group')[0].classList.toggle('show');
  })</script>

<h2 id="css">css</h2>
<blockquote>
<pre><code class="language-css">.list-group {
  display: none;
  overflow: hidden;
  animation-duration: 1.5s;
  animation-name: slidein;
}
@keyframes slidein {
  from {
    height: 0;
  }
  to {
    height: 205.5px;
  }
}
@keyframes slideout {
  from {
    height: 205.5px;
  }
</code></pre>
</blockquote>
<p>  to {
    height: 0;
  }
}
.show {
  display: block;
  overflow: hidden;
  animation-duration: 1.5s;
  animation-name: slideout;
}</p>
<p>navbar를 내릴 시에 slidein 애니메이션을 주고 
navbar를 올릴 시에 slideout 애니메이션을 주고싶었는데 
둘 중 하나만 적용이 된다.. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[javascript] 2022.09.16]]></title>
            <link>https://velog.io/@subb_ny/javascript-2022.09.16</link>
            <guid>https://velog.io/@subb_ny/javascript-2022.09.16</guid>
            <pubDate>Fri, 16 Sep 2022 11:40:21 GMT</pubDate>
            <description><![CDATA[<h2 id="1-onclick-속성">1. onclick 속성</h2>
<pre><code class="language-html">&lt;div class=&quot;alert-box&quot; id=&quot;alert&quot;&gt;
      알림창임
      &lt;button onclick=&quot;document.getElementById(&#39;alert&#39;).style.display=&#39;none&#39;;&quot;&gt;
        닫기
      &lt;/button&gt;

</code></pre>
<p>.addEventListener 의 클릭 속성 안쓰고 html의 onclick 속성 써도 가능 </p>
<h2 id="2-콜백함수">2. 콜백함수</h2>
<p>파라미터 자리에 들어가는 함수 </p>
<pre><code class="language-javascript">document
        .getElementById(&quot;closeButton&quot;)
        .addEventListener(&quot;click&quot;, function () {
          document.getElementById(&quot;hello&quot;).style.display = &quot;none&quot;;
        });
</code></pre>
<p>위와 같은 예시에 있는 함수가 콜백함수임 </p>
<h2 id="3-classlist">3 .classlist</h2>
<p>클래스명 추가하는 이벤트 </p>
<pre><code class="language-javascript">document.getElementsByClassName(&#39;list-group&#39;)[0].classList.toggle(&#39;show&#39;);
// .add는 추가하는거 
// .toggle은 있으면 삭제하고 없으면 추가해라 

</code></pre>
<h2 id="4-queryselectorqueryselectorall">4. querySelector(),querySelectorAll()</h2>
<pre><code class="language-javascript">querySelector(#hello)
querySelector(.hello)


querySelectorAll(.hello)[3]</code></pre>
<p>장점: 굳이 class와 id를 나누지 않아도 돼서 편리함 
단점: class는 첫번째 클래스를 찾아줌 
다 찾을려면 <mark style='background-color: #fff5b1'> querySelectorAll</mark>로 찾아야함 =&gt; 인덱싱 필요함 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[css,html] <img> 태그 vs <div> 태그에 background-image 속성 추가 ]]></title>
            <link>https://velog.io/@subb_ny/csshtml-img-%ED%83%9C%EA%B7%B8-vs-div-%ED%83%9C%EA%B7%B8%EC%97%90-background-image-%EC%86%8D%EC%84%B1-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@subb_ny/csshtml-img-%ED%83%9C%EA%B7%B8-vs-div-%ED%83%9C%EA%B7%B8%EC%97%90-background-image-%EC%86%8D%EC%84%B1-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Wed, 07 Sep 2022 11:50:19 GMT</pubDate>
            <description><![CDATA[<h2 id="semantic-web">Semantic Web</h2>
<blockquote>
<p>시맨틱 웹(Semantic Web)은 <mark style='background-color: #fff5b1'> &quot;의미있는 웹&quot;</mark>이라는 뜻으로, 기계가 이해할 수 있는 형태로 제작된 웹을 의미합니다. (물론 사람도 이해할 수 있습니다.)- Wiki 백과
출처: <a href="https://blog.cordelia273.space/21">https://blog.cordelia273.space/21</a> [세상의 모든 지식:티스토리]</p>
</blockquote>
<p>즉,사람을 대신해서 웹 페이지의 정보를 이해하고, 우리에게 필요한 정보만 보여주거나 정보를 가공해서 우리가 필요로 하는 형태로 가공해주는 것을 의미</p>
<h2 id="semantic-tag">Semantic Tag</h2>
<blockquote>
<p>HTML5에 도입된 semantic 태그는 개발자와 브라우저에게 <mark style='background-color: #fff5b1'> 의미있는 태그 </mark>를 제공
semantic 태그에 의해 컴퓨터가 HTML 요소의 의미를 보다 명확히 해석하며 그 데이터를 활용할 수 있는 semantic web이 실현</p>
</blockquote>
<p>즉,</p>
<blockquote>
<img src="https://ifh.cc/g/yFHyWJ.png" width=350px>
</blockquote>
<p>다양한 semantic tag 예시 
<img src="https://velog.velcdn.com/images/subb_ny/post/30d4409d-2a44-4ea6-90d4-048b6814e949/image.png" width=250px></p>
<h2 id="html--img-태그">HTML : img 태그</h2>
<h3 id="📒-사용이유">📒 사용이유</h3>
<blockquote>
<h4 id="1-프린트가-필요한-경우">1. 프린트가 필요한 경우</h4>
<p> 백그라운드 이미지는 출력시 포함되지 않음</p>
</blockquote>
<h4 id="2-이미지가-의미를-가지는-경우">2. 이미지가 의미를 가지는 경우</h4>
<p> 이미지 업로드 실패 시, 깨진 이미지 아이콘과 alt가 함께 노출된다
 alt를 통해 이미지의 원래 의미를 전달할 수 있다.</p>
<h4 id="3-검색-엔진에-노출이-필요한-경우-seo">3. 검색 엔진에 노출이 필요한 경우 (SEO)</h4>
<p>: 검색 엔진 최적화 (SEO)를 할 때 img 태그의 alt 속성을 사용
: alt 속성은 이미지가 렌더링 되지 못할 때 나타날 문자열을 지정하기 위한 값으로,
검색 엔진은 이미지에 대한 정보를 alt 속성을 읽어서 얻는다.
: TTS (Text to Speech)에도 활용된다.</p>
<h3 id="📗-특징">📗 특징</h3>
<blockquote>
<ul>
<li>width와 height를 지정하지 않으면 원본 크기로 보인다.</li>
</ul>
</blockquote>
<ul>
<li>width 또는 height 하나만 지정하면 비율이 맞춰 다른 속성이 자동으로 생성되어 비율이 유지된다.</li>
<li>태그 자체 속성에서 width와 height를 지정할 수 있다.</li>
<li>이미지가 어떠한 이유로 보일 수 없으면 alt에 적힌 내용이 화면상에 나타나고 이미지가 로드되지 않음을 보여준다.</li>
</ul>
<h2 id="div-태그--background-image-속성"><div> 태그 + background-image 속성</h2>
<blockquote>
<h4 id="1-프린트-시-이미지를-제거해야-하는-경우">1. 프린트 시 이미지를 제거해야 하는 경우</h4>
<p> 출력되면 곤란한 이미지일 경우 제거하고 출력 가능</p>
</blockquote>
<h4 id="2-이미지-위에-텍스트가-들어가는-경우">2. 이미지 위에 텍스트가 들어가는 경우</h4>
<p> 배경 이미지를 넣고, div 태그 내부에 텍스트를 넣어 사용할 수 있다.</p>
<h4 id="3-css-sprites를-사용해-이미지-속도를-향상시키는-경우">3. CSS sprites를 사용해 이미지 속도를 향상시키는 경우</h4>
<p> 수 많은 img 태그를 퍼블리싱하는 방법보다, CSS sprites 기법을 사용하면 이미지의 수정, 보관, 관리가 용이하고 로딩 속도가 향상된다.</p>
<h3 id="📗-특징-1">📗 특징</h3>
<blockquote>
<ul>
<li>div 태그이기 때문에 크기를 width와 height를 지정하지 않으면 width와:0; height:0; 이다. 그렇기 때문에 크기를 주지 않으면 아예 보이지않는다.</li>
</ul>
</blockquote>
<ul>
<li>비율을 유지하기 위해서는 background-size: 100%; 라는 속성을 포함해야한다.</li>
<li>height 를 지정해주지 않으면 아예 이미지가 보이지 않는다.</li>
<li>width 를 주지 않으면 background-size: 100%;를 주더라도 비율이 유지 되지 않고 가로는 다보이지만 세로가 짤린 이미지가 보이게 된다.</li>
<li>이미지가 어떠한 이유로 보여줄 수 없는 상태가 되면 화면상에는 아무표시가 나지 않는다.</li>
</ul>
<p>  <a href="https://blog.cordelia273.space/21">https://blog.cordelia273.space/21</a>
  <a href="https://velog.io/@tjdgus0528/img-%ED%83%9C%EA%B7%B8-vs-div-%ED%83%9C%EA%B7%B8%EC%97%90-background-image-%EC%86%8D%EC%84%B1%EC%9D%84-%EC%B6%94%EA%B0%80">https://velog.io/@tjdgus0528/img-%ED%83%9C%EA%B7%B8-vs-div-%ED%83%9C%EA%B7%B8%EC%97%90-background-image-%EC%86%8D%EC%84%B1%EC%9D%84-%EC%B6%94%EA%B0%80</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[javascript] 쉬운 사칙연산 ]]></title>
            <link>https://velog.io/@subb_ny/javascript-%EC%89%AC%EC%9A%B4-%EC%82%AC%EC%B9%99%EC%97%B0%EC%82%B0</link>
            <guid>https://velog.io/@subb_ny/javascript-%EC%89%AC%EC%9A%B4-%EC%82%AC%EC%B9%99%EC%97%B0%EC%82%B0</guid>
            <pubDate>Thu, 01 Sep 2022 07:20:32 GMT</pubDate>
            <description><![CDATA[<p>철수는 은행에 예금을 하러 갔는데 예금 금액에 따라 이율이 달라지는 것을 보고 크게 당황했습니다.</p>
<p>첫 예금액이 5만원 미만이면 이율이 연 15퍼센트,</p>
<p>첫 예금액이 5만원 이상이면 이율이 연 20퍼센트라고 합니다.</p>
<p>그래서 민준이는</p>
<p>(1) 변수에 예금액을 넣으면</p>
<p>(2) 2년 후의 총 예금액을 자동으로 콘솔창에 출력해주는 기능을 자바스크립트로 만들려고하는데</p>
<p>어떻게 코드를 짜면 될까요? </p>
<h3 id="내가-짠-코드">내가 짠 코드</h3>
<blockquote>
<pre><code class="language-javascript">var 예금액 = 10000;
var 미래예금액 = 0;
if (예금액 &gt; 50000) {
  미래예금액 = 예금액 + 예금액 * 0.2 + (예금액 + 예금액 * 0.2) * 0.2;
} else {
  미래예금액 = 예금액 + 예금액 * 0.15 + (예금액 + 예금액 * 0.15) * 0.15;
}
console.log(미래예금액);</code></pre>
</blockquote>
<h2 id="더-간결한-코드">더 간결한 코드</h2>
<blockquote>
<pre><code class="language-javascript">var 예금액 = 60000;
var 미래예금액 = 0;
if ( 예금액 &gt;= 50000 ){
  미래예금액 = 예금액 * 1.2 * 1.2 ;
} else {
  미래예금액 = 예금액 * 1.15 * 1.15 ;
}
console.log(미래예금액) </code></pre>
</blockquote>
<p>예금액+예금액*0.2를 간단하게 1.2로 표현할 수 있다.  </p>
]]></description>
        </item>
    </channel>
</rss>