<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>AI Platform / Web</title>
    <link>https://han-py.tistory.com/</link>
    <description>데이터분석, NLP, 머신러닝, 하둡, Web 등의 전체 플랫폼을 구축하면서 유용한 정보는 정리하고 있습니다.</description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 07:42:25 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>HAN_PY</managingEditor>
    <item>
      <title>[nodejs] 인코딩과 디코딩</title>
      <link>https://han-py.tistory.com/490</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hanpy-blog.com/language/ko/nodejs/encoder-decoder&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hanpy-blog.com/language/ko/nodejs/encoder-decoder&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 이전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;47&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DxsGx/btrEmajMkol/3xBVsMX1N4K1aFTiviDbJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DxsGx/btrEmajMkol/3xBVsMX1N4K1aFTiviDbJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DxsGx/btrEmajMkol/3xBVsMX1N4K1aFTiviDbJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDxsGx%2FbtrEmajMkol%2F3xBVsMX1N4K1aFTiviDbJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;301&quot; height=&quot;47&quot; data-origin-width=&quot;301&quot; data-origin-height=&quot;47&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/nodejs</category>
      <category>decodeURI()</category>
      <category>decodeURIComponent()</category>
      <category>encodeURI()</category>
      <category>encodeURIComponent()</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/490</guid>
      <comments>https://han-py.tistory.com/490#entry490comment</comments>
      <pubDate>Mon, 6 Jan 2025 10:06:36 +0900</pubDate>
    </item>
    <item>
      <title>블로그 이전.</title>
      <link>https://han-py.tistory.com/603</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-1.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwRiKO/btsx47Y4QSG/ZbKGcEKKInLSdhQwMaWB71/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwRiKO/btsx47Y4QSG/ZbKGcEKKInLSdhQwMaWB71/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwRiKO/btsx47Y4QSG/ZbKGcEKKInLSdhQwMaWB71/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwRiKO%2Fbtsx47Y4QSG%2FZbKGcEKKInLSdhQwMaWB71%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-1.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 한파이입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 가지 이유로 Tistory를 떠나 블로그를 이전하려 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nextjs나 Reactjs로 직접 블로그를 만들어서 진행할 예정이고, 현재 목표는 2023년 전에 블로그를 오픈하고, 1분기 전에 서비스 하나를 오픈하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 전문적인 모습으로 다시 찾아뵙도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(현재 블로그는 정보전달이 아니라, 간단히 자료 저장소로 사용 예정입니다.)&lt;/p&gt;</description>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/603</guid>
      <comments>https://han-py.tistory.com/603#entry603comment</comments>
      <pubDate>Wed, 11 Oct 2023 12:43:19 +0900</pubDate>
    </item>
    <item>
      <title>[React] 데이터 관리를 위한 useState 고찰하기</title>
      <link>https://han-py.tistory.com/568</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;React를 처음 배우면, useState를 상태관리를 위해 사용한다. 이 글을 보고 있는 여러분도 적어도 한번 정도는 사용해 봤을 것이다. useState는 리액트에 제공하는 가장 기본적은 Hook으로 useEffect와 함께 가장 많이 사용된다. 상태관리가 무엇인지에 대해 알아본 후 useState의 본질적인 사용법에 대해 알아보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PJBoG/btshYcPkyZg/yBk4OuLKEdPgSu8D3xGwJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PJBoG/btshYcPkyZg/yBk4OuLKEdPgSu8D3xGwJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PJBoG/btshYcPkyZg/yBk4OuLKEdPgSu8D3xGwJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPJBoG%2FbtshYcPkyZg%2FyBk4OuLKEdPgSu8D3xGwJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;258&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;React에서 State&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;state란 무엇일까? 쉽게 말하면 state는 데이터라고 생각하면 이해가 빠르다. 따라서 상태관리란, &lt;b&gt;데이터 관리&lt;/b&gt;라고 할 수 있다. 공식 문서에 따르면 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;state를 구분하는 방식&lt;/b&gt;&lt;/span&gt;은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;시간에 따라, 변한다면 state가 아니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;parent component에서 Props를 받을 수 있다면, state가 아니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기존의 state나 props로 만들 수 있다면, state가 아니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;state의 기본적인 원칙은 중복되지 않아야 하고, 특정 이벤트에 따른 변화 발생 시 UI가 변화할 수 있어야 한다. 여기서 props와 state의 개념이 나오는데, props와 state를 혼동하면 안 된다. props는 기본적으로 함수로 전달받는 인자의 개념으로 부모 컴포넌트에서 자식 컴포넌트로 데이터를 내려주는 값이라고 할 수 있다.&lt;span style=&quot;color: #009a87;&quot;&gt; state는 컴포넌트의 메모리에 저장되는 값으로, 특정 이벤트에 따른 변화를 발생시킬 수 있는 상태&lt;/span&gt;라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;State의 위치&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;리액트에서는 useState를 통해 상태를 관리한다. 그렇다면, state를 담아두는 useState는 어느 컴포넌트에 넣어야 할까? 가장 먼저 생각해야 할 부분은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;컴포넌트의 책임/역할이라고&lt;/b&gt; &lt;/span&gt; 할 수 있겠다. 기본적으로 리액트는 &lt;b&gt;단방향 데이터 흐름&lt;/b&gt;을 가진다. 따라서 부모 컴포넌트에서 자식 컴포넌트로 데이터를 내려준다. 이러한 흐름은 컴포넌트의 책임이라는 관점에서는 어떤 컴포넌트가 state의 책임을 가지는지 판단하기에 어려울 수 있다. 공식문서에서는 아래의 순서로 데이터에 관한 &lt;b&gt;컴포넌트의 책임을 찾도록 가이드&lt;/b&gt;한다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;state와 관련된 모든 컴포넌트를 확인한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;확인한 컴포넌트의 부모 컴포넌트를 확인한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;일반적으로 부모 컴포넌트에 state를 넣어준다. 만약 마땅한 부모 컴포넌트가 없다면, 컴포넌트를 만들어서 state를 넣어도 된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정리하면 state 공통으로 사용하는 &lt;b&gt;최상단 컴포넌트에 useState&lt;/b&gt;를 넣고, props로 자식 컴포넌트들이 공유하는 형식이라 할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;useState 사용해 보기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기본적으로 html은 변하지 않는 값이다. html을 변화시키기 위해서는 화면을 새로고침해야 한다. 하지만 전체 화면을 모두 새로고침을 하는 것은 비효율적이라 할 수 있다. 따라서 변경되는 부분(Component)만 새로고침을 하기 위해 useState를 사용한다고 할 수 있겠다. 초급자 기준으로 간단히 코드 설명을 해보겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VJkT1/btsiusFtxke/EXuWDBSnxyTejYTRcssLS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VJkT1/btsiusFtxke/EXuWDBSnxyTejYTRcssLS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VJkT1/btsiusFtxke/EXuWDBSnxyTejYTRcssLS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVJkT1%2FbtsiusFtxke%2FEXuWDBSnxyTejYTRcssLS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;746&quot; height=&quot;462&quot; data-origin-width=&quot;746&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;사실 useState는 위의 동그라미 친 3 부분만 추가하면 사용 가능하다. 간단히 코드를 확인해 보자&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;gt; import React, {useState} from 'react';&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 부분은 useState를 사용하기 위해 import 해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&amp;gt; const&amp;nbsp;[hanpy,&amp;nbsp;setHanpy] =&amp;nbsp;useState();&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이 부분은 useState를 사용하는 부분이다. []를 통해서 이름을 짓는다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;[]의 첫 번째 변수 hanpy&lt;/b&gt;&amp;nbsp;:&amp;nbsp;변수를 지정한 부분이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;[]의 두 번째 변수 setHanpy&lt;/b&gt; : 변수 변경은 이것을 사용해서 한다. 통상적으로 첫 번째 변수인 hanpy에서 앞에 set을 붙여 사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;useState()&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;: () 안에는 초기 값을 넣어준다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&amp;gt; {hanpy}&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;html 코드 내부에서 useState 변수를 사용하려면 {} 안에 넣서 사용해 주면 된다. 첫 번째 변수인 hanpy를 넣어서 사용한다. setHanpy는 html내부에서 사용하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;이벤트로 상태값 변화시키기&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;useState에 저장된 값을 변경하는 코드는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuLyaR/btsitZQMpQJ/kSS5pVpyqaw0e9Hew3p1fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuLyaR/btsitZQMpQJ/kSS5pVpyqaw0e9Hew3p1fK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuLyaR/btsitZQMpQJ/kSS5pVpyqaw0e9Hew3p1fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuLyaR%2FbtsitZQMpQJ%2FkSS5pVpyqaw0e9Hew3p1fK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;722&quot; height=&quot;528&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이전 코드에서 2개만 더 추가를 했다. button에 onClick를 걸어서 지정한 함수가 실행되게 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&amp;gt; const handleTest = () =&amp;gt; {}&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;함수 지정 시 위와 같은 방법으로 사용하자. 이는 component에서 class형이 아닌 함수형으로 사용하기로 한 방식과 동일하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&amp;gt; setHanpy(hanpy+1)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;hanpy값을 변경하는 부분이다. useState에 포함된 변수변경은 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;hanpy = hanpy + 1 &lt;/span&gt;&lt;/b&gt;이런식으로 사용하면 안 된다. 그러면 useState를 사용한 이유가 없어진다. 반드시 hanpy의 내부의 값 변경 시 setHanpy를 사용하자.&amp;nbsp; setHanpy로 변경을 하면 UI에 자동으로 반영되어 hanpy 변수가 변경된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 127px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;s&lt;b&gt;etHanpy(hanpy + 1)&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt; 대신 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;setHanpy((pre) =&amp;gt; pre+1)&lt;/span&gt;&lt;/b&gt;를 자주 사용한다. pre란 이전 값을 의미한다.&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위 코드에서 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;setHanpy(hanpy + 1)&lt;/span&gt;&lt;/b&gt; 로 적은 이유는 글을 읽으시는 분들의 직관적 이해도 상승을 위함임을 알자.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;gt; onClick={}&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이벤트 리스너를 react에서 적을 때, 주의할 점은, &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onClick&lt;/span&gt;&lt;/b&gt;대신, &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;onclick&lt;/span&gt;&lt;/b&gt;으로 적으면 작동되지 않는다. 반드시 캐멀케이스인 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;onClick&lt;/b&gt;&lt;/span&gt;으로 적어주자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;useState의 사용법에 대해 간단히 알아보았다. 사실 여기까지는 기초적인 내용이고, 본격적으로 조금 더 깊숙하게 확인해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;useState 심화 ( useState와 외부 API 결합하기 )&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;하나의 state는 하나의 useState를 사용한다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;너무나도 당연한 이야기지만, 코드 작성 시 무의식적으로 useState를 추가하여 사용한다. 기본적으로 API로 데이터를 가지고 와서 화면에 보여주는 순서는 다음과 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터를 가지고 온다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터를 usestate로 저장한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가져온 데이터가 저장을 했으면, 데이터 가공을 통해 추가 데이터를 저장한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이러한 로직은 아래와 같은 코드로 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685854095344&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useGetData } from &quot;./hooks&quot;;
import { computeAnalysisResults } from &quot;./utils&quot;;

const App = () =&amp;gt; {
  const [data, setData] = useState(null);
  const [analysisResults, setAnalysisResults] = useState([]);
  
  useEffect(() =&amp;gt; {
    async function fetch() {
      const response = await useGetData();
      setData(response.data);
    }
    
    fetch();
  }, []);
  
  useEffect(() =&amp;gt; {
    if (data) {
      setAnalysisResults(computeAnalysisResults(data));
    }
  }, [data]);
  
  return &amp;lt;&amp;gt;Dummy UI&amp;lt;/&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;useEffect를 2번 사용하는 로직에는 크게 문제가 없고, 잘 작동할 것이다. 하지만, 위의 코드는 앞으로 문제가 될 수 있는 가능성이 포함되어 있는 코드이다.&amp;nbsp; 위의 코드에서 필자는 analysisResults가 data에만 의존하도록 코드를 작성했다. 그러나 협업하는 개발자가 위의 정확한 로직을 모르고 아래와 같이 getOtherAnalysisResults 함수를 추가하여 버튼을 만들었다고 해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685855215450&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useGetData } from &quot;./hooks&quot;;
import { computeAnalysisResults, getOtherAnalysisResults } from &quot;./utils&quot;;

const App = () =&amp;gt; {
  const [data, setData] = useState(null);
  const [analysisResults, setAnalysisResults] = useState([]);
  
  useEffect(() =&amp;gt; {
    async function fetch() {
      const response = await useGetData();
      setData(response.data);
    }
    
    fetch();
  }, []);
  
  useEffect(() =&amp;gt; {
    if (data) {
      setAnalysisResults(computeAnalysisResults(data));
    }
  }, [data]);
  
  return (
    &amp;lt;&amp;gt;
      Dummy UI
      &amp;lt;button onClick={() =&amp;gt; setAnalysisResults(getOtherAnalysisResults())}&amp;gt;
       GET-OTHER-DATA
      &amp;lt;/Button&amp;gt;
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위 코드를 보면 setAnalysisResults가 두 부분에서 사용된다. data를 fetch 해서 업데이트할 때와 버튼을 누를 때 사용된다. 따라서 &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;AnalysisResult가 data로 가져온 값인지 버튼을 통해서 가져온 값인지 모호해진다. 이 말을 쉽게 이야기하면, 이러한 코드에 계속해서 추가 작업을 진행한다면, 코드가 복잡해지고 에러 추적이 힘들어질 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;기본적으로 useEffect는 외부 데이터와 내부 데이터의 동기화를 통해 UI에 나타낼 때, 사용한다.&lt;/b&gt;&lt;/span&gt; 그러나 위의 사용은 내부의 2개의 state를 동기화하는데, useEffect를 사용한다. 이는 잘못된 방법이라 할 수 있다. 아래와 같이 useEffect를 하나 제외하여 state를 삭제하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이러한 방식은 복잡도를 줄일 수 있고, 다른 개발자가 AnalysisResult를 수정하고 싶을 때 다른 함수를 만드는 것이 아니라 computeAnalysisResult를 우선 확인하여 함수를 추가/수정할 것이다. 동일한 데이터를 2가지로 나누는 것이 아니라, SSOT 원칙을 React 코드에도 반영하도록 하자. 즉, 같은 state는 하나의 상태만 설정하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;컴포넌트에서 useState 초기값이란&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;useState의 초기값에 대해 생각해 보자. useState 선언 시 위 코드에서는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;useState(null), useState([])&lt;/b&gt;&lt;/span&gt; 와 같이 사용을 했다. 여기서 초기값으로 설정한 값은 null과 []를 의미한다. 이때 초기값은 컴포넌트가 mount 될 때만 사용을 한다. 그 후부터 re-render 시, 초기값은 사용되지 않음을 이해하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>API와 useState</category>
      <category>NextJS</category>
      <category>react</category>
      <category>state 관리하기</category>
      <category>useState</category>
      <category>서버 상태와 useState</category>
      <category>컴포넌트 책임에 따른 상태관리</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/568</guid>
      <comments>https://han-py.tistory.com/568#entry568comment</comments>
      <pubDate>Tue, 3 Oct 2023 22:00:07 +0900</pubDate>
    </item>
    <item>
      <title>[정렬 알고리즘] 퀵 정렬(Quick Sort)</title>
      <link>https://han-py.tistory.com/572</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;퀵 정렬(Quick Sort)란, 피벗(pivot, 기준값)을 기준으로 큰 데이터와 작은 데이터를 찾아서 위치를 변경하는 정렬 방식이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;퀵 정렬(Quick sort) 동작 순서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오름차순으로 정렬하는 방법에 대해 알아보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기준점이 될 index를 pivot으로 설정한다.(random value)&lt;/li&gt;
&lt;li&gt;새로운 리스트 2개를 만들고, pivot 값보다 작은 값들과 큰 값들을 모아서 각각 리스트에 담는다.&lt;/li&gt;
&lt;li&gt;pivot값을 기준으로 작은 값이 모든 리스트는 왼쪽, 큰 값들의 리스트는 오른쪽으로 붙인다.&lt;/li&gt;
&lt;li&gt;1-3을 재귀로 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;퀵 정렬(Quick sort) 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퀵 정렬의 구현 방법은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1686032242737&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def quick_sort(collection):
    &quot;&quot;&quot;
    Examples:
    &amp;gt;&amp;gt;&amp;gt; quick_sort([0, 5, 3, 2, 2])
    [0, 2, 2, 3, 5]
    &amp;gt;&amp;gt;&amp;gt; quick_sort([])
    []
    &amp;gt;&amp;gt;&amp;gt; quick_sort([-2, 5, 0, -45])
    [-45, -2, 0, 5]
    &quot;&quot;&quot;
    if len(collection) &amp;lt; 2:
        return collection
    pivot_index = 0  # Use random element as pivot
    pivot = collection[pivot_index]
    greater = []  # All elements greater than pivot
    lesser = []  # All elements less than or equal to pivot

    for element in collection[:pivot_index]:
        (greater if element &amp;gt; pivot else lesser).append(element)

    for element in collection[pivot_index + 1:]:
        (greater if element &amp;gt; pivot else lesser).append(element)

    return [*quick_sort(lesser), pivot, *quick_sort(greater)]


alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
print(quick_sort(alist))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;퀵 정렬(Quick sort)의 시간 복잡도(Time Complexity)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 알아보았던 &lt;a href=&quot;https://han-py.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;선택정렬과 삽입정렬의&lt;/a&gt; 시간 복잡도는 O(N^2)로 느리다. 퀵 정렬의 시간 복잡도는 O(N log N)으로 정렬속도가 빠르다. 하지만, 모든 정렬 알고리즘이 장점만 있는 것은 아니다. 퀵 정렬의 단점은 리스트가 어느 정도 정렬 되어 상태라면 삽입 정렬이 더 빠르다. 정렬 정도에 따른 적절한 정렬 알고리즘을 고르자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;퀵 정렬과 병합 정렬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 퀵 정렬과 병합 정렬의 차이에 대해 알아보자. 위의 코드를 확인해 보면, 퀵 정렬이 주어진 리스트를 분할하고 병합하는 것이 병합정렬과 비슷해 보인다. 그렇다면 차이점은 무엇일까? 병합정렬은 두 부분으로 나눈 후에 각 정렬이 끝난 후 병합하는 후처리 작업이 추가로 필요하다. 하지만 퀵 정렬은 기준값(pivot item)을 중심으로 작은 값은 왼쪽, 큰 값은 오른쪽으로 위치시킨다. 따라서 분할하는 과정에서 pivot들이 자기 자리를 찾아가기 때문에, 분할 이후에 병합하는 과정에서 후처리 작업이 불필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/알고리즘 종류</category>
      <category>Quick Sort</category>
      <category>퀵 정렬</category>
      <category>퀵 정렬 시간복잡도</category>
      <category>퀵정렬과 병합정렬 비교</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/572</guid>
      <comments>https://han-py.tistory.com/572#entry572comment</comments>
      <pubDate>Thu, 28 Sep 2023 22:00:55 +0900</pubDate>
    </item>
    <item>
      <title>[Nextjs] 모달(Modal) 만들기</title>
      <link>https://han-py.tistory.com/598</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모달(Modal)이란&lt;/b&gt;, 원하는 내용을 화면 위에 띄워 표현하는 방식이다. 기본적으로 많은 UI 라이브러리에서 쉽게 제작이 가능하다. 오늘은 Modal을 Component로 만들고 Nextjs/React에 적용하는 법을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모달을 만들기전에 알아야할 개념중 첫번째가 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Portals&lt;/b&gt;&lt;/span&gt;라는 개념이다. 우리는 필요한 위치에 Component로 만들어 모달을 넣어줄 것이다. 그리고 추가로, 다른 Component보다 앞에 표시가 되려면 z-index를 사용하여 표현을 한다. 이때, 중요한 것은 부모의 z-index가 작다면, 자식의 z-index가 아무리 크더라도 부모의 z-index를 따르게 된다. 그렇기 때문에&lt;b&gt; Modal 자체를 종속적으로 Component 내부에 넣으면 다른 컴포넌트보다 앞에 있지 않는 경우가 생기게 된다.&lt;/b&gt; 이를 방지하려면, 최상단의 root에 있는 부분에 엘리먼트를 만들어 Javascript로 접근을 해서 모달에 추가해 주면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;hanpy.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNr5ob/btsvlKe9UAD/lj8flJWCDKUD3lTOQiJ76k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNr5ob/btsvlKe9UAD/lj8flJWCDKUD3lTOQiJ76k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNr5ob/btsvlKe9UAD/lj8flJWCDKUD3lTOQiJ76k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNr5ob%2FbtsvlKe9UAD%2Flj8flJWCDKUD3lTOQiJ76k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-filename=&quot;hanpy.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;React Portals&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모달이 z-index가 먹히지 않는다면 우리는 &lt;b&gt;Portals&lt;/b&gt;라는 개념을 먼저 알아야한다. 기본적으로 React는 부모 컴포넌트의 DOM 내부에서 렌더링이 일어난다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695432218700&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const ParentComponent = ({ children }) =&amp;gt; {
  return &amp;lt;div&amp;gt;{children}&amp;lt;/div&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Portals는 부모 컴포넌트의 내부 DOM이 아니라, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;미리 지정해둔 DOM에서 렌더링 할 수 있는 기능&lt;/b&gt;&lt;/span&gt;이다.&amp;nbsp; 조금 더 알아보면,&lt;b&gt; Protal은 이벤트 버블링&lt;/b&gt;이 Dom내부에서 가능하다. 이벤트 버블링이란, 중첩된 자식 요소에서 이벤트가 발생하면 부모로 이벤트가 전달된다. 이때, 부모 Dom 밖에서 아래와 같이 만들어도, Dom 트리 위치와 상관 없이 Portal은 React 트리 내부에 존재하기 때문에 React의 가상돔에 따른 이벤트 버블링이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695435042615&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;


const Modal = ({ open, onClose, children }) =&amp;gt; {
  if (!open) return null;
  return ReactDOM.createPortal(
    &amp;lt;&amp;gt;
      &amp;lt;div style={overlayStyle} /&amp;gt;
      &amp;lt;div style={modalStyle}&amp;gt;
        &amp;lt;button onClick={onClose}&amp;gt;모달 닫기&amp;lt;/button&amp;gt;
        {children}
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;,
    document.getElementById(&quot;portal&quot;)
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;portal id 값을 최상단에 설정을 했다고 가정해보자. Modal 컴포넌트를 만든 기준으로 부모 컴포넌트가 있다면, DOM 트리와 관련 없이 리엑트 트리에서 상위 컴포넌트라면 이벤트 버블링이 된다. 구조를 살펴보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695435457221&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ReactDOM.createPortal(child, container)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;child는 렌더를 할 수 있는 React 자식요소이고, container는 DOM 엘리먼트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nextjs 모달 적용 / React 모달 적용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Modal Component 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;13버전에 올라오면서 Nextjs 구조가 많이 변경 되었다. Nextjs 12와 13버전의 root 부분이 다르기 때문에 아래를 참고해서 모달을 만들어 보자. 먼저 공통 Modal Compoent 코드는 아래와 같다. 아래의 코드에서는 Scss를 사용하였지만, 기호에 맞게 css는 변경해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695436535615&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Modal.tsx
import React, { ReactNode } from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import styles from &quot;./Modal.module.scss&quot;;

interface ModalProps {
  open: boolean;
  onClose: () =&amp;gt; void;
  children: ReactNode;
}

const Modal = ({ open, onClose, children }: ModalProps) =&amp;gt; {
  if (!open) return null;
  return ReactDOM.createPortal(
    &amp;lt;&amp;gt;
      &amp;lt;div className={styles.overlayStyle} /&amp;gt;
      &amp;lt;div className={styles.modalStyle}&amp;gt;
        &amp;lt;button onClick={onClose}&amp;gt;모달 닫기&amp;lt;/button&amp;gt;
        {children}
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;,
    document.getElementById(&quot;global-modal&quot;) as HTMLElement
  );
};

export default Modal;

// Modal.module.scss

.overlayStyle {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 5;
}

.modalStyle {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 50px;
  background-color: #ffffff;
  z-index: 5;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 상위 html에 우리가 Modal 컴포넌트에서 적은 id값을 넣은 div element를 만들어 주면 된다. 예시코드에서는 id값을 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;global-modal&lt;/b&gt;&lt;/span&gt;로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Root Directory에 Element 만들기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Nextjs 12버전 root Directory&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 root Html부분의 위치는 page/_app.tsx 부분이다. 아래과 같이 dev element를 넣어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695437316911&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Head from &quot;next/head&quot;;
import type { AppProps } from &quot;next/app&quot;;

export default function App({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Deepfake Detection&amp;lt;/title&amp;gt;
      &amp;lt;/Head&amp;gt;
        &amp;lt;Component {...pageProps} /&amp;gt;
        &amp;lt;div id=&quot;global-modal&quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Nextjs 13버전 root Directory&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선 root Html부분의 위치는 app/layout.tsx 부분이다. 아래와 같이 추가해준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695437367806&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import ToastProvider from '@/components/toastProvider/ToastProvider'

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({ children }: {
  children: React.ReactNode
}) {
  return (
    &amp;lt;html lang=&quot;en&quot;&amp;gt;
      &amp;lt;body&amp;gt;
        {children}
        &amp;lt;div id=&quot;global-modal&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;react root Directory&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;리액트의 root Html 부분의 위치는 public/index.html 이다. 아래 처럼 root id 아래에 우리가 만들 모달 id를 넣어주면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695437616999&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;root&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div id=&quot;global-modal&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. JSX 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id값을 설정을 했으면 컴포넌트 내부에서 사용하면 된다. 간단한 컴포넌트 예시는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695437790913&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Modal from &quot;@/components/Modal&quot;;

const TestComponent = () =&amp;gt; {
  const [isModalOpen, setIsModalOpen] = useState(false);
  return (
  &amp;lt;&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setIsModalOpen(true)}&amp;gt;모달 열기&amp;lt;/button&amp;gt;
        &amp;lt;Modal open={isModalOpen} onClose={() =&amp;gt; setIsModalOpen(false)}&amp;gt;
          모달 내용
        &amp;lt;/Modal&amp;gt;
      &amp;lt;/div&amp;gt;

  &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모달을 구현하는 법은 매우 다양하다. 위의 내용은 기초적인 부분만 만들었기 때문에 필요한 프로젝트에 변형해서 사용해 보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>Nextjs 모달</category>
      <category>Portal</category>
      <category>portal Event Bubbling</category>
      <category>react portal</category>
      <category>reactjs 모달</category>
      <category>넥트스 모달</category>
      <category>리액트 모달</category>
      <category>이벤트 버블링</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/598</guid>
      <comments>https://han-py.tistory.com/598#entry598comment</comments>
      <pubDate>Mon, 25 Sep 2023 22:00:47 +0900</pubDate>
    </item>
    <item>
      <title>파일 업로드 type 제한 방법</title>
      <link>https://han-py.tistory.com/593</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;javascript에서 파일 타입을 확인/제한하는 방법, React에서 파일 타입을 확인/제한하는 방법, Nextjs에서 파일 타입을 확인/제한하는 방법에 대해 모두 알아보자. 기본적으로 파일을 업로드하는 로직이 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JavaScript 파일 업로드 type 제한&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 HTML/JavaScript 로직으로 우선 확인을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695083033032&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input type=&quot;file&quot; id=&quot;input&quot; multiple /&amp;gt;
&amp;lt;output id=&quot;output&quot;&amp;gt;상태창&amp;lt;/output&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input element를 통해 파일을 추가할 수 있도록 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;45&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m2lcE/btsuJbKzM94/UvLudnjhCDrLt17x1HBmzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m2lcE/btsuJbKzM94/UvLudnjhCDrLt17x1HBmzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m2lcE/btsuJbKzM94/UvLudnjhCDrLt17x1HBmzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm2lcE%2FbtsuJbKzM94%2FUvLudnjhCDrLt17x1HBmzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;409&quot; height=&quot;45&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;45&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 자바스크립트 로직을 아래와 같이 작성해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695083045228&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Our application only allows GIF, PNG, and JPEG images
const allowedFileTypes = [&quot;image/png&quot;, &quot;image/jpeg&quot;, &quot;image/gif&quot;];

const input = document.getElementById(&quot;input&quot;);
const output = document.getElementById(&quot;output&quot;);

input.addEventListener(&quot;change&quot;, (event) =&amp;gt; {
  const files = event.target.files;

  if (files.length === 0) {
    output.innerText = &quot;Choose image files&amp;hellip;&quot;;
    return;
  }

  const allAllowed = Array.from(files).every((file) =&amp;gt;
    allowedFileTypes.includes(file.type),
  );
  output.innerText = allAllowed
    ? &quot;All files clear!&quot;
    : &quot;Please choose image files only.&quot;;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;allowedFileTypes 변수에 허용할 리스트 타입들을 넣어준다. 그리고 파일이 업로드되면 event.target.files을 통해 파일을 얻는다. 그 후에 allAllowed 변수 부분을 확인해서 file 값이 있는지 없는지 확인을 해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;React 파일 업로드 type 제한&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React/Nextjs에서 사용하는 방식을 알아보자. 테스트 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695087971139&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const TestComponent = () =&amp;gt; {
    const handleImageChange = (e: ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        if (!e.target.files) return;
        const file = e.target.files[0];
        const allowedFileTypes = [&quot;image/png&quot;, &quot;image/jpeg&quot;, &quot;image/gif&quot;];
        const allAllowed = Array.from(files).every((file) =&amp;gt;
            allowedFileTypes.includes(file.type),
        );
        
        // allAllowed에 포함된 파일 유무에 따라 도메인 로직을 작성해 주면된다.

    }
    
    return (
    	&amp;lt;&amp;gt;
            &amp;lt;input
                type='file'
                placeholder='file UPLOAD'
                accept='image/*'
                name=&quot;image&quot;
                onChange={(e) =&amp;gt; handleImageChange(e)}
            /&amp;gt;

        &amp;lt;/&amp;gt;
    )
    
}

export default TestComponent;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면, 파일을 받아서 타입값을 확인하고 체크하는 로직이다. 현재 브라우저에서 파일 type 체크를 하는 방법을 소개했다. 하지만 server에서 체크하는 방법도 있으니 관련 부분이 필요하다면 추가 공부를 해보자.&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/JAVASCRIPT</category>
      <category>웹 파일 업로드</category>
      <category>웹 파일 업로드 제한</category>
      <category>웹 파일 타입 확인</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/593</guid>
      <comments>https://han-py.tistory.com/593#entry593comment</comments>
      <pubDate>Fri, 22 Sep 2023 22:00:57 +0900</pubDate>
    </item>
    <item>
      <title>redux-toolkit 프로젝트 적용 실습</title>
      <link>https://han-py.tistory.com/596</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;redux-toolkit이란 redux를 조금 더 쉽게 사용하기 위해 나온 라이브러리이다. 이 글에서는 개념보다는 쉽게 프로젝트에 redux를 적용할 수 있도록 코드 위주로 설명해 보겠다. 구현 정도는 &lt;b&gt;redux로 전역 변수를 관리하는 부분&lt;/b&gt;까지 진행할 예정이다. 왜냐하면 Redux-Saga나 Redux-thunk 같은 미들웨어는 최근 프런트 진영에서 &lt;b&gt;포일러플레이트 이슈&lt;/b&gt;로 잘 사용하지 않고, 대신&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;react-query, SWR &lt;/b&gt;&lt;/span&gt;같은 라이브러리는 사용하는 추세이기 때문이다. 하지만, &lt;b&gt;react-query나 SWR은 서버와 동기화를 위해 사용되는 것이기 때문에, 완전한 전역변수 관리는 불가능하다.&lt;/b&gt; 따라서 Frontend 자체의 전역변수 관리는 Redux나 Recoil을 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;hanpy.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kATLY/btstSiSei3Q/u8mlnyVTwUMPK8jsCQhKW1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kATLY/btstSiSei3Q/u8mlnyVTwUMPK8jsCQhKW1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kATLY/btstSiSei3Q/u8mlnyVTwUMPK8jsCQhKW1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkATLY%2FbtstSiSei3Q%2Fu8mlnyVTwUMPK8jsCQhKW1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-filename=&quot;hanpy.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;redux-toolkit 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 설치하는 명령어는 아래와 같다. react-redux에 toolkit을 추가 설치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694586787526&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install @reduxjs/toolkit react-redux&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TypeScript&lt;/b&gt;을 사용 중이라면, 아래의 라이브러리를 추가 설치하자. 아래 코드는 TypeScript로 예시가 작성되어 있다. 만약 JavaScript로 코드를 만든다면, 타입 부분만 삭제해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694586775708&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install @types/react-redux&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;redux-toolkit&lt;span&gt;&amp;nbsp;만들기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 root 위치에 redux 폴더를 만들자. 그리고 redux 폴더 내부에 store.ts 파일을 만든다. store.ts 내부의 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694587916588&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// redux/store.ts

import { combineReducers, configureStore } from &quot;@reduxjs/toolkit&quot;;

const rootReducer = combineReducers({});

const store = configureStore({
  reducer: rootReducer,
});

export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;

export default store;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 위의 코드만 설정을 해도 redux store 설정은 완료된다. 하지만 조금 더 추가 설정을 해보자. 먼저&lt;b&gt;&amp;nbsp;non-serializable 타입을 Redux에 저장할&lt;/b&gt; 수 있도록 해보자. 아래와 같이 middleware를 추가하여 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;serializableCheck를 false&lt;/b&gt;&lt;/span&gt;로 설정하면 된다. 설정을 추가한 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694588159012&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { combineReducers, configureStore } from &quot;@reduxjs/toolkit&quot;;

const rootReducer = combineReducers({});

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =&amp;gt;
    getDefaultMiddleware({ serializableCheck: false }),
});

export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;

export default store;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 확인해 보면, middleware 부분이 추가된 것을 알 수 있다. 관련 부분을 조금 더 설명해 보겠다. 기본적으로 JavaScript에서는 객체 타입을 많이 쓴다. 하지만, 우리가 데이터를 브라우저의 localStorage에 저장하려 한다면 객체 자체를 바로 추가하는 것은 불가능하다. 왜냐하면 localStorage는 string 값만 들어갈 수 있기 때문이다. 따라서 JavaScript는 object 타입을 string으로 변환하여 저장을 하는데, 이를 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;serialization(직렬화)&lt;/b&gt;&lt;/span&gt;라고 한다. 반대로 localStorage에서 값을 빼낸 후에 JavaScript에서 string 값을 객체로 변환하는 작업은 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;deserialization(역직렬화)&lt;/b&gt;&lt;/span&gt;하고 한다. 정리하면, serializableCheck 설정이란, Redux의 기본 설정은 action에 직렬화 불가능한 값을 허용한다고 이해하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Chrome extension에서 redux&lt;/b&gt;를 설치하면, 아래와 같이 개발자 도구에서 내가 만든 리덕스를 파악하는 것이 가능하다. 이것은 개발 시 많은 도움을 준다. 하지만, product 배포 환경에서는 아래의 개발 툴은 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT9UGK/btsuAuDpqsb/424U5pg2DYryRHEjyNULK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT9UGK/btsuAuDpqsb/424U5pg2DYryRHEjyNULK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT9UGK/btsuAuDpqsb/424U5pg2DYryRHEjyNULK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT9UGK%2FbtsuAuDpqsb%2F424U5pg2DYryRHEjyNULK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;402&quot; height=&quot;265&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 devTools 설정으로 개발자를 배포 라이브 환경에서 보이지 않게 할 수 있다. store 변수 내용만 아래와 같이 변경해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694991945367&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const store = configureStore({
  reducer: rootReducer,
  devTools: process.env.NODE_ENV !== &quot;production&quot;, // 개발자도구 확인
  middleware: (getDefaultMiddleware) =&amp;gt;
    getDefaultMiddleware({ serializableCheck: false }),
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;devTools 부분이 false라면, 아래의 그림처럼 개발자도구에서 Redux를 내용을 확인할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhE4g0/btst84GXnT2/gTSKxAOYY7coLwiqfMK2uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhE4g0/btst84GXnT2/gTSKxAOYY7coLwiqfMK2uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhE4g0/btst84GXnT2/gTSKxAOYY7coLwiqfMK2uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhE4g0%2Fbtst84GXnT2%2FgTSKxAOYY7coLwiqfMK2uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;404&quot; height=&quot;188&quot; data-origin-width=&quot;404&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리덕스는 페이지를 새로고침을 하면 내용이 없어진다. 리덕스에 로그인 정보를 넣어두었다면, 새로고침 시 정보가 사라져서 계속 로그인을 해야 하는 상황이 일어난다. 이러한 이슈를 해결하기 위해서 redex-persist 설정을 추가해 보자. r&lt;b&gt;edex-persist 설정은 브라우저 저장소 중에 localStorage/SessionStorage에 리덕스 내용을 저장&lt;/b&gt;하여 새로고침 시 데이터 손실을 막는다. 관련 자세한 내용은 여기서는 다루지 않는다. 우선 redex-persist을 설치하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694992921763&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm i redex-persist&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 후에 아래와 같이 설정을 추가해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694993100655&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { combineReducers, configureStore } from &quot;@reduxjs/toolkit&quot;;
import storage from &quot;redux-persist/lib/storage&quot;;

import { persistReducer, persistStore } from &quot;redux-persist&quot;;

const persistConfig = {
  key: &quot;root&quot;,
  storage,
  whitelist: [&quot;&quot;],
};

const rootReducer = combineReducers({});

const persistedReducer = persistReducer(persistConfig, rootReducer);

const store = configureStore({
  reducer: persistedReducer,
  devTools: process.env.NODE_ENV !== &quot;production&quot;, // 개발자도구 확인
  middleware: (getDefaultMiddleware) =&amp;gt;
    getDefaultMiddleware({ serializableCheck: false }),
});

export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;

export default store;
export const persistor = persistStore(store);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;whitelist 부분에 전역으로 넣을 변수를 넣어주는 곳이다. 이제 redux를 사용할 수 있게 컴포넌트로 감싸주는 &lt;b&gt;provider 컴포넌트&lt;/b&gt;를 하나 만들어주자. 아래와 같이 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694993196757&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/redux/provider.tsx

import { ReactNode } from &quot;react&quot;;
import { Provider } from &quot;react-redux&quot;;
import store, { persistor } from &quot;./store&quot;;
import { PersistGate } from &quot;redux-persist/integration/react&quot;;

const Providers = ({ children }: { children: ReactNode }) =&amp;gt; {
  return (
    &amp;lt;Provider store={store}&amp;gt;
      &amp;lt;PersistGate loading={null} persistor={persistor}&amp;gt;
        {children}
      &amp;lt;/PersistGate&amp;gt;
    &amp;lt;/Provider&amp;gt;
  );
};

export default Providers;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설정은 끝났다. 사용하는 부분에 provider 컴포넌트를 넣어준다. 리액트를 사용한다면, root 디렉터리 컴포넌트에 넣어주면 되고 next.js를 사용 중이라면, _app.tsx나 layout.tsx에 넣어주자. 예시는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694993877669&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 적용 전
import Head from &quot;next/head&quot;;
import type { AppProps } from &quot;next/app&quot;;
import &quot;/styles/globals.css&quot;;

export default function App({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;&amp;gt;
        &amp;lt;Component {...pageProps} /&amp;gt;
    &amp;lt;/&amp;gt;
  )
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694993959643&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 적용 후
import Head from &quot;next/head&quot;;
import type { AppProps } from &quot;next/app&quot;;
import &quot;/styles/globals.css&quot;;

export default function App({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Providers&amp;gt;
        &amp;lt;Component {...pageProps} /&amp;gt;
      &amp;lt;/Providers&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Providers 컴포넌트로 설정해 준 부분에서는 Redux를 사용가능하다. 기본 구조는 다 만들었다. 사용로직을 위한 Slice를 작성해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 로그인 관련된 auth Slice를 예제로 작성해 보겠다. authSlice.ts를 만들자. 지금까지 파일 구조는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1694994493188&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/redux
  /slice
    /authSlice.ts
  /provider.tsx
  /store.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;authSlice의 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694994579629&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createSlice } from &quot;@reduxjs/toolkit&quot;;
import { RootState } from &quot;../store&quot;;

export const initialAuthState: AuthState = {
  isLoggedIn: false,
  email: null,
  userName: null,
  userType: null,
};

const authSlice = createSlice({
  name: &quot;auth&quot;,
  initialState: initialAuthState,
  reducers: {
    SET_ACTIVE_USER: (state, action) =&amp;gt; {
      const { email, userName, userType } = action.payload;
      state.isLoggedIn = true;
      state.email = email;
      state.userName = userName;
      state.userType = userType;
    },
    REMOVE_ACTIVE_USER: () =&amp;gt; initialAuthState,
  },
});

export const { SET_ACTIVE_USER, REMOVE_ACTIVE_USER } = authSlice.actions;

export const selectIsLoggedIn = (state: RootState) =&amp;gt; state.auth.isLoggedIn;
export const selectEmail = (state: RootState) =&amp;gt; state.auth.email;
export const selectUserName = (state: RootState) =&amp;gt; state.auth.userName;
export const selectUserType = (state: RootState) =&amp;gt; state.auth.userType;

export default authSlice.reducer;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;initialAuthState&lt;/b&gt;&lt;/span&gt;는 초기값을 설정한 것이다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;authSlice&lt;/b&gt;&lt;/span&gt;에 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;createSlice&lt;/b&gt;&lt;/span&gt;를 통해 이름, 초기값, 리듀서를 설정해 준다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;SET_ACTIVE_USER&lt;/b&gt;&lt;/span&gt;를 실행하면, action으로 받은 값들을 각각의 redux에 등록을 해주는 것이고, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;REMOVE_ACTIVE_USER&lt;/b&gt;&lt;/span&gt;는 로그아웃 시 초기 값으로 상태값들을 변경하는 로직이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 slice는 하나의 그룹으로 생각해도 된다. 만약 추가도 다른 로직이 필요하다면, 추가로 slice를 만들어주면 된다. 이제 만든 slice를 store에 등록하여 컴포넌트에서 사용할 수 있게 하자. store.ts는 아래와 같이 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694995078809&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { combineReducers, configureStore } from &quot;@reduxjs/toolkit&quot;;
import storage from &quot;redux-persist/lib/storage&quot;;

import authReducer from &quot;./slice/authSlice&quot;;
import { persistReducer, persistStore } from &quot;redux-persist&quot;;

const persistConfig = {
  key: &quot;root&quot;,
  storage,
  whitelist: [&quot;auth&quot;],
};

const rootReducer = combineReducers({
  auth: authReducer,
});

const persistedReducer = persistReducer(persistConfig, rootReducer);

const store = configureStore({
  reducer: persistedReducer,
  devTools: process.env.NODE_ENV !== &quot;production&quot;,
  middleware: (getDefaultMiddleware) =&amp;gt;
    getDefaultMiddleware({ serializableCheck: false }),
});

export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;

export default store;
export const persistor = persistStore(store);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 보면 추가된 부분은 combineReducers 부분에 auth: authReducer를 넣어준 것과, localStorage에 저장을 위해 persistConfig내부의 Whitelist에는 우리가 createSlice에 넣었던 이름(auth)을 넣어주면 된다. 여기까지 하면 redux 설정이 완료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Redux를 사용&lt;/b&gt;해 보자. 사용하기 위해 불러오는 부분은 slice에서 export 된 아래의 코드라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694995463973&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const { SET_ACTIVE_USER, REMOVE_ACTIVE_USER } = authSlice.actions;

export const selectIsLoggedIn = (state: RootState) =&amp;gt; state.auth.isLoggedIn;
export const selectEmail = (state: RootState) =&amp;gt; state.auth.email;
export const selectUserName = (state: RootState) =&amp;gt; state.auth.userName;
export const selectUserType = (state: RootState) =&amp;gt; state.auth.userType;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;action부분은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;SET_ACTIVE_USER, REMOVE_ACTIVE_USER&lt;/b&gt;&lt;/span&gt;를 통해 실행한다. 사용법은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694995605197&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useMutation, useQueryClient } from &quot;@tanstack/react-query&quot;;
import { postSignIn, postSignOut } from &quot;./api&quot;;
import { useDispatch } from &quot;react-redux&quot;;
import { REMOVE_ACTIVE_USER, SET_ACTIVE_USER } from &quot;@/redux/slice/authSlice&quot;;

export const useSigninQuery = (redirect: (path: string) =&amp;gt; void) =&amp;gt; {
  const queryClient = useQueryClient();
  const dispatch = useDispatch();

  return useMutation(postSignIn, {
    onSuccess: (response) =&amp;gt; {
      switch (response.result) {
        case &quot;SUCCESS&quot;:
          dispatch(
            SET_ACTIVE_USER({
              isLoggedIn: true,
              email: response.data.id,
              userName: response.data.id,
              userType: response.data.userType,
            })
          );
      }
    },
  });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 확인할 부분은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;useDispatch()를 통해 dispatch 변수로 만들어 준 부분과, dispatch 안에 REMOVE_ACTIVE_USER를 넣어서 사용한 부분&lt;/b&gt;&lt;/span&gt;이 끝이다. 위의 예시에서는 react-query 내부에서 dispatch가 사용되어 어려워 보일 수도 있지만, 간소화해서 다시 표현하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694995994593&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const TestComponent = () =&amp;gt; {
    const dispatch = useDispatch();

    const handleClick = (e: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
        e.preventDefault();
        dispatch(
              SET_ACTIVE_USER({
                  isLoggedIn: true,
                  email: &quot;hanpy@hanpy.com&quot;,
                  userName: &quot;py&quot;,
                  userType: &quot;admin&quot;,
            })
        );
    }
    return (
        &amp;lt;&amp;gt;
            &amp;lt;button onClick={handleClick}&amp;gt;로그인 발생!!&amp;lt;/button&amp;gt;
        &amp;lt;/&amp;gt;
    )


}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;위의 부분이 데이터를 변경하는 action&lt;/span&gt; 부분&lt;/b&gt;이었다면, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;아래는 데이터를 가져올 수 있는 부분&lt;/b&gt;&lt;/span&gt;에 대한 예시이다. 사실 이 부분은 간단하게 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694996228328&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useSelector } from &quot;react-redux&quot;;
import { selectUserName } from &quot;@/redux/slice/authSlice&quot;;


const GNB = () =&amp;gt; {
  const userName = useSelector(selectUserName);
  
  ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useSelector를 우선 Import&lt;/b&gt; 해준다. 그리고 slice부분에서 export해준 데이터 중에 필요한 부분을 가져와서 사용하면 된다. 복잡해 보이지만 사실 redux-tookit을 사용하면 어느 정도 쉽게 전역변수관리가 된다고 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 데이터 변경이 없다면 useSelector를 활용하여 redux의 값들을 가지고 와서 UI에 바로 사용하면 된다. 하지만 데이터 변경이 있다면, redux상태값을 useEffect를 통해 useState 변수에 넣어서 사용하도록 하자.&lt;/p&gt;</description>
      <category>Web/nextjs</category>
      <category>code 실습</category>
      <category>NextJS</category>
      <category>reactjs</category>
      <category>redex-persist</category>
      <category>redux-toolkit</category>
      <category>serializableCheck 설정이란</category>
      <category>useDispatch</category>
      <category>useSelector</category>
      <category>개발자 도구에서 Redux extention이 안 보여요</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/596</guid>
      <comments>https://han-py.tistory.com/596#entry596comment</comments>
      <pubDate>Tue, 19 Sep 2023 23:00:11 +0900</pubDate>
    </item>
    <item>
      <title>[Nextjs] 전역 스타일 적용하기(global css)</title>
      <link>https://han-py.tistory.com/595</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;nextjs 한 번의 설정으로 모든 파일에서 사용할 수 있는 global css 적용하는 방법에 대해 알아보자. Nextjs가 12 버전에서 13 버전으로 업데이트되면서, 디렉터리 구조가 co-location이 강화된 형태로 많이 변경되었다. 따라서 특정 간단히 12 버전에서 전역 스타일 적용 방식을 알아보고, 13 버전으로 넘어가도록 하겠다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;hanpy.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLgh1C/btstRLl9KNO/qwqSNdNzggw7Gkcx6Szjr0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLgh1C/btstRLl9KNO/qwqSNdNzggw7Gkcx6Szjr0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLgh1C/btstRLl9KNO/qwqSNdNzggw7Gkcx6Szjr0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLgh1C%2FbtstRLl9KNO%2FqwqSNdNzggw7Gkcx6Szjr0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-filename=&quot;hanpy.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nextjs 12. 전역 스타일 적용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;13 &lt;span&gt;버전&lt;/span&gt; &lt;span&gt;초반&lt;/span&gt; &lt;span&gt;버전까지도&lt;/span&gt; 12&lt;span&gt;버전과&lt;/span&gt; &lt;span&gt;동일한 디렉토리를 사용하기 때문에, 13 버전이라도 헷갈리는 경우가 있으니 조심하자.&lt;/span&gt;. &lt;span&gt;따라서&lt;/span&gt; &lt;span&gt;우선&lt;/span&gt; 12&lt;span&gt;와&lt;/span&gt; 13 &lt;span&gt;버전의&lt;/span&gt; &lt;span&gt;가장&lt;/span&gt; &lt;span&gt;큰&lt;/span&gt; &lt;span&gt;차이점은&lt;/span&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;_app.tsx/_document.tsx(_app.js/_document.js)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;의&lt;/span&gt; &lt;span&gt;유무라고&lt;/span&gt; &lt;span&gt;할&lt;/span&gt; &lt;span&gt;수&lt;/span&gt; &lt;span&gt;있다&lt;/span&gt;. page 폴더 안에&amp;nbsp;&lt;span&gt;앞의&lt;/span&gt; &lt;span&gt;파일이&lt;/span&gt; &lt;span&gt;있다면&lt;/span&gt;,&lt;span&gt;&amp;nbsp;지금 하는 방식으로 전역 스타일을 적용해 주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;간단하게 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;/styles/globals.css&lt;/b&gt;&lt;/span&gt; 에 전역으로 적용할 css를 넣어주면 된다. 넣어주기만 하면 될까? 아니다. 기본적으로 전역으로 적용하기 위해서는 _app.tsx파일에 아래와 같이 import를 해줘야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694525646308&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import &quot;/styles/globals.css&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;이때 주의할 점은 document.js에 넣으면 에러가 나기 때문에&lt;/span&gt; 아래와 같이 _app.js에 import를 해주면 간단히 해결이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694525790505&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import type { AppProps } from &quot;next/app&quot;;
import &quot;/styles/globals.css&quot;;

export default function App({ Component, pageProps }: AppProps) {
  return (
      &amp;lt;Component {...pageProps} /&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 왜 _document.js에 넣어주면 안 되는 것일까? 기본적으로 _app.js 와 _document.js는 둘 다 진입점이라 생각을 하면 된다. _app.js에서는 React 자체 핸들링을 하기 위한 설정을 넣어준다면, _document.js에서는 HTML 엘리먼트를 핸들링할 때 넣어주면 된다. 따라서 리덕스나 레이아웃 컴포넌트를 넣는 위치는 _app.js가 된다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;_document.js는 서버에서만 렌더링&lt;/b&gt;&lt;/span&gt; 되기 때문에 브라우저에서 적용가능한 것들은 불가능하다. 당연히 이벤트 핸들러도 _document.js에서는 불가능하다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nextjs 13. 전역 스타일 적용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 최신버전에 대해 알아보자. 대부분 아래와 같은 방법으로 Nextjs로 프로젝트를 셋업 했을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694526308852&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx create-next-app@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면, 13버전에는 _app.js와 _document.js가 없다. 기존 12 버전에는 /page 폴더와 /page/api, /public과 같이 각각의 폴더의 특징이 명확했다. 하지만, 13 버전에서는 완전 변경된 방식으로 app 디렉터리 안에 layout.jsx에 아래와 같이 전역 css 스타일을 적용 가능하다. (13 폴더 설명은 생략.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694526549993&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import './globals.css'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({ children }) {
  return (
    &amp;lt;html lang=&quot;en&quot;&amp;gt;
      &amp;lt;body className={inter.className}&amp;gt;{children}&amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RootLayout이란, app 디렉터리의 상위에 존재하는 폴더이다. 그리고 모든 페이지는 RootLayout을 지나기 때문에, &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;RootLayout을 통해&lt;/span&gt; 서버로부터 리턴되는 초기 HTML을 설정할 수 있다.&lt;/b&gt; 이러한 이유로 전역 스타일 설정을 이곳에서 손쉽게 설정을 하면 된다. 정리하면, 기존 12에서 사용되던, _app.js 와 _document.js가 이 RootLayout으로 변경된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;전역 css 내부 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;css 설정 예시를 간단히 보고, 전역 스타일을 활용하는 방법을 알아보자. 아래는 global.css Sample이다. 물론 scss를 사용한다면, 변경해서 적용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694528108598&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;:root {
  --font-family: sans-serif;

  --main-blue: rgb(85, 72, 255);
  --main-white: #ffffff;

  --box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

* {
  scroll-behavior: smooth;
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

html {
  font-size: 14px;
}

body {
  font-family: var(--font-family);
  background-color: var(--background-gray);
}

a {
  text-decoration: none;
  color: inherit;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 간단히 설명해 보면, root 부분에는 CSS 변수를 설정한 부분이라고 할 수 있다. 그리고 설정한 변수는 body 부분처럼 var에 감싸서 사용을 해주면 된다. root 부분의 --main-blue로 색을 지정한 이유는 프로젝트마다 메인 색이 있다. 이러한 메인 색을 변수로 지정하여 관리하기 위함이다. 이러한 방식으로 프로젝트 시작 시 변수를 설정하고 각각의 프로젝트에서 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/nextjs</category>
      <category>global.css</category>
      <category>_app.js와 _document.js 차이</category>
      <category>_app.tsx _document.tsx 차이</category>
      <category>전역 css</category>
      <category>전역 스타일 적용하기</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/595</guid>
      <comments>https://han-py.tistory.com/595#entry595comment</comments>
      <pubDate>Sat, 16 Sep 2023 02:00:51 +0900</pubDate>
    </item>
    <item>
      <title>[영상 편집기 만들기] ffmepg 기초 정리</title>
      <link>https://han-py.tistory.com/589</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ffmepg란 미디어 플레이어나 인코딩 프로그램에 주로 사용되는 라이브러리이다. 우선은 ffmepg 자체에 대해 알아보도록 하자. 그 후에 브라우저에서 영상편집기가 작동할 수 있도록 만들어보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ffmpeg&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 ffmpeg 자체에 대해 알아보자. &lt;a href=&quot;http://www.ffmpeg.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&gt;에 들어가면 아래의 글이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;976&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1JxIq/btsteQ15E48/ZlYr17RUNStTses1kZTmY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1JxIq/btsteQ15E48/ZlYr17RUNStTses1kZTmY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1JxIq/btsteQ15E48/ZlYr17RUNStTses1kZTmY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1JxIq%2FbtsteQ15E48%2FZlYr17RUNStTses1kZTmY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1382&quot; height=&quot;976&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;976&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ffmpeg를 사용하여 비디오 및 오디오 파일을 변환하고, 편집/ 스트림 전송/ 필터링 / 캡퍼 / 재생할 수 있다. 이는 다양한 운영체제에서 사용가능하며, 커맨드 라인 인터페이스를 통해 사용가능하다. ffmpeg 공식 사이트에서 운영체제에 맞는 파일을 받아서 설치해주면 된다. 기본적인 명령어는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 커맨드 문법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693889332233&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ffmpeg [options] -i [입력 파일 경로] [출력 파일 경로]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 자체는 어렵지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 옵션에 -i를 넣어서 가능하다. 간단히 예시를 확인하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693889647649&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// yuv420p를 avi로 변경
$ ffmpeg -i /tmp/test.yuv /tmp/out.avi

// avi를 yuv420p로 변경
$ ffmpeg -i mydivx.avi hugefile.yuv

// MP4를 AVI로 변경
$ ffmpeg -i /tmp/test.mp4 /tmp/out.avi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-i : 입력이 들어갈 파일을 지정하는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 필터링&lt;/h3&gt;
&lt;pre id=&quot;code_1693890117095&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ffmpeg -i vid1.avi -vf noise=alls=0:allf=t vid2.avi
$ ffmpeg -i vid1.avi -vf noise=alls=25:allf=t vid2.avi
$ ffmpeg -i vid1.avi -vf noise=alls=50:allf=t vid2.avi
$ ffmpeg -i vid1.avi -vf noise=alls=100:allf=t vid2.avi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 노이즈 제거를 0, 25, 50, 100 과 같이 필요한 만큼 적용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693890224083&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ffmpeg -i /tmp/test.avi -vf &quot;noise=alls=25, eq=brightness=0.2&quot; /tmp/out.avi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 노이즈 제거와 밝기 조절을 동시에도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-vf : 비디오 필터를 지정하는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 오디오 변환&lt;/h3&gt;
&lt;pre id=&quot;code_1693890307523&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ffmpeg -i /tmp/test.mp4 -vn -acodec copy /tmp/out.mp3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 영상 자체를 오디오로 변환하는 것도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-acodec : 오디오 코덱을 지정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-vcodec : 비디오 코덱을 지정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 녹화&lt;/h3&gt;
&lt;pre id=&quot;code_1693890517178&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ffmpeg -video_size 1024x768 -framerate 30 -f x11grab -i :0.0+100,200 /tmp/out.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-video_size : 녹화되는 크기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-framerate : 녹화 프레임 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-f : 입력이나 출력 파일 형식을 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-f x11grab -i : 화면 캡처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;:0.0+200,300 : 왼쪽 위 기준으로 가로 200/세로 300 픽셀만큼 떨어진 곳에서 1024x768 사이즈 만큼 녹화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 자르기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영상이 큰 파일의 경우 필요구간만 추출하는 것이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695084139157&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ffmpeg -i 원본파일이름 -ss 시작시간 -to 끝시간 출력파일&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-ss 와 -to로 시작과 끝 시간을 정의한다. 시간 포메팅은 h:mm:ss이다. 자르기만 하고, 다른 변경이 필요 없다면 -vcodec/-acodec에 copy로 넣어주면 원본 인코딩을 그대로 사용하여 처리시간이 짧아진다. copy를 넣지 않으면 일부 구간 추출하여 다시 인코딩하므로 처리하는데 시간이 많이 걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1695084657551&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ffmpeg -i input.mp4 -ss 1:12:12 -to 2:13:13 -vcodec copy -acodec copy output.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ffmpeg.wasm&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ffmpegwasm.netlify.app/docs/getting-started/installation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ffmpeg.wasm&lt;/a&gt;는 ffmpeg를 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;javascript&lt;/b&gt;&lt;/span&gt;에서 사용할 수 있는 인터페이스 형식의 패키지로 오픈소스(MIT)이므로 쉽게 사용가능하다. 사실 ffmpeg-fluent도 많이 사용하는데, ffmpeg-fluent는 OS 자체에 추가 설치가 필요하므로, 브라우저 자체동작은 어렵다. ffmpeg.wasm는 javascript에서 사용가능하기 때문에 당연하게도 브라우저에서만 사용가능하다. 우선 바로 리엑트로 구현하기전에, 브라우저에서 ffmpeg을 사용가능한 ffmpeg.wasm에 대해 먼저 알아보자. 그 후에 리액트로 영상 편집기나 영상 플레이어를 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTQalb/btss3Zzqd7l/J9wBaLaRyvHCORkNy0HbMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTQalb/btss3Zzqd7l/J9wBaLaRyvHCORkNy0HbMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTQalb/btss3Zzqd7l/J9wBaLaRyvHCORkNy0HbMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTQalb%2Fbtss3Zzqd7l%2FJ9wBaLaRyvHCORkNy0HbMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;856&quot; height=&quot;194&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 ffmepeg.wasm은 브라우저 안에서 작동하므로 데이터 유출이나 네트워크 지연에 대해 걱정할 필요가 없다. 또한 단일 스레드 및 다중 스레트를 함께 제공하므로 하여, 다양한 데이터 처리를 가능하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp; ffmpeg.wasm 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ffmpeg는 npm 이나 yarn의 패키지 매니저로 설치가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693886644672&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// npm
$ npm install @ffmpeg/ffmpeg @ffmpeg/util

// yarn
$ yarn add @ffmpeg/ffmpeg @ffmpeg/util&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, unpkg 같은 CDN은 제공하지 않기 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. ffmpeg.wasm 아키텍쳐&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7qwzw/btsteSTKDh2/77HcE40AuREZutj0ekz7F0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7qwzw/btsteSTKDh2/77HcE40AuREZutj0ekz7F0/img.png&quot; data-alt=&quot;https://ffmpegwasm.netlify.app/docs/overview/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7qwzw/btsteSTKDh2/77HcE40AuREZutj0ekz7F0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7qwzw%2FbtsteSTKDh2%2F77HcE40AuREZutj0ekz7F0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;831&quot; height=&quot;501&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://ffmpegwasm.netlify.app/docs/overview/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 보면 Main Thread와 Web Worker 두 가지로 크개 나누어 진 것을 볼 수 있다. 기본적으로 Multimedia Transcoding 같은 경우에 작업량이 많기 때문에 main thread에서 실행한다면, 다른 작업들이 큰 영향을 받아서 좋지 않다. ffmpeg.wasm 경우에는 ffmpeg.worker라는 web worker에서 작업을 실행한다. 즉, javascript의 비동기 로직으로 ffmpeg.wasm 함수들이 실행되기 때문에 async/await 문법을 사용해서 실행해 주는 것이 좋다. 위 그림을 보면 multi-thread를 사용하면, ffmpeg.worker 내부의 ffmpeg-core가 작업을 할당해 주는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 ffmpeg을 브라우저에서 사용하는 방법에 대해 간단히 알아보았다. 더 심화된 내용은 구현하는 파트에서 알아보도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>nextjs 영상 편집기</category>
      <category>nextjs 플레이어 만들기</category>
      <category>리액트 영상 편집기</category>
      <category>리액트로 플레이어 만들기</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/589</guid>
      <comments>https://han-py.tistory.com/589#entry589comment</comments>
      <pubDate>Wed, 13 Sep 2023 02:00:29 +0900</pubDate>
    </item>
    <item>
      <title>[React] 첨부한 이미지 보여주기</title>
      <link>https://han-py.tistory.com/590</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 첨부하고, 첨부한 이미지를 브라우저 상에서 보여주는 방식을 알아보자. 브라우저에서 이미지를 업로드하여 보여주는 방식으로는 fileReader를 사용하는 방식과 URL.createObjectURL()을 사용하는 방식이 있다. 오늘 알아볼 방식은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;URL.createObjectURL()이다.&lt;/b&gt;&lt;/span&gt;&amp;nbsp; 우선은 작동 순서를 알아보고, React로 전반적인 파일 업로드와 Image를 브라우저로 표현하는 방식에 대해 알아보자. 그 후에 vanillaJS로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;URL.revokeObjectURL()&lt;/b&gt;&lt;/span&gt; 도 추가로 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/plmkn/btstkWu5X4P/Ymr77llDHcCkhqW0j4YZEK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/plmkn/btstkWu5X4P/Ymr77llDHcCkhqW0j4YZEK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/plmkn/btstkWu5X4P/Ymr77llDHcCkhqW0j4YZEK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fplmkn%2FbtstkWu5X4P%2FYmr77llDHcCkhqW0j4YZEK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 로직 순서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 구현할 로직을 우선 생각해 보면 아래와 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;input 태그&lt;/b&gt;를 통해 image를 받는다.&lt;/li&gt;
&lt;li&gt;받은 image 파일에서 &lt;b&gt;Blob 객체&lt;/b&gt;를 뽑아낸다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;뽑아낸 Blob 객체를 &lt;b&gt;URL.createObjectURL 메서드를&lt;/b&gt; 통해 img 객체의 src에 넣을 수 있는 DOMString으로 변환한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;img 태그를 생성&lt;/b&gt;하여 넣어준다.&lt;/li&gt;
&lt;li&gt;자유롭게 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Blob란 무었일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Blob란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이미지를 Blob 형태로 변경을 한다. Blob는 Binary Large Object의 줄임말로 이미지나 사운드 파일 같은 큰 데이터를 다룰 때 사용한다. 이러한 멀티미디어 객체를 저장하기 위해 주로 사용하는 Blob는 javascript에서도 이미지/비디오/사운드를 핸들링할 때 많이 사용한다고 보면 된다. 추가적인 특징으로는 File 객체는 Blob를 확장하여 만든 것이므로, Blob가 사용가능한 곳에는 File 객체도 사용가능하다. Blob 자체를 DB에 저장하여 사용하기도 하는데, 모든 DBMS에서 사용가능한 것은 아니기 때문에 DB 전략을 만들기 전에 사용가능한지에 대한 확인이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;URL.createObjectURL()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;URL.createObjectURL()&lt;/b&gt;&lt;/span&gt; 메서드는 input으로 들어온 객체를 가리키는 URL을 DOMString으로 변환하는 기능을 한다. 이때 URL 자체는 브라우저 메모리에 올라가기 때문에 창을 닫으면 사라진다. 이때, &lt;u&gt;&lt;b&gt;URL.createObjectURL(object)에서 object로 들어가는 값은 File, Blob, MediaSource 객체들이 가능하다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;component 안에서 react로 구현을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. useState 상태값 정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694011485539&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [image, setImage] = useState();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;image 태그 안에 src에 들어간 부분을 image로 상태를 지정한다. 이 로직에는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;URL.createObjectURL()&lt;/b&gt;&lt;/span&gt;의 출력값이 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 파일 업로드 시 image 상태에 값을 넣을 handle 함수 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694011728062&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const handleImageChange = (e) =&amp;gt; {
    if (!e.target.files) return;
    const file = e.target.files[0];
    if (file) {
      let image = window.URL.createObjectURL(file);
      setImage(image);
    }
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 handleImageChange 함수는 input 태그에 이미지를 첨부했을 때, 첨부된 이미지에서 Blob 값을 빼서 DOMString으로 변환하는 로직까지 포함되어 있다. 이때, e.target.files는 input 태그를 통해 넣은 파일들이 들어간다. 우리는 하나의 image만 첨부했다고 가정을 했기 때문에 e.target.files[0]으로 file을 변수에 담는다. 이때, file이 우리가 실제로 말하는 파일값으로 백엔드로 보내는 로직이 필요하다면, 이 파일 자체를 보내면 된다. e.target.files[0] 을 console을 찍어보면, 아래와 같은 결과를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;42&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VhHqH/btstffWIudv/YWXT2qNnPOo3LL5xUBcUrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VhHqH/btstffWIudv/YWXT2qNnPOo3LL5xUBcUrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VhHqH/btstffWIudv/YWXT2qNnPOo3LL5xUBcUrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVhHqH%2FbtstffWIudv%2FYWXT2qNnPOo3LL5xUBcUrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;42&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;42&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 형식으로 console이 찍히는 것이 바로 File 형식인 것이다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;URL.createObjectURL&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;의&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;b&gt;결과 값&lt;/b&gt;은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;17&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bufZzr/btstfRnGUHx/sUtjXAu33GsA9kuRdno2j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bufZzr/btstfRnGUHx/sUtjXAu33GsA9kuRdno2j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bufZzr/btstfRnGUHx/sUtjXAu33GsA9kuRdno2j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbufZzr%2FbtstfRnGUHx%2FsUtjXAu33GsA9kuRdno2j0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;447&quot; height=&quot;17&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;17&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값을 우리는 image 상태값에 대입하고 &amp;lt;img src={image} /&amp;gt;와 같이 넣어서 이미지를 보이도록 만들면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 컴포넌트 내부 JSX return 부분&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694012558385&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  return (
    &amp;lt;&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;label htmlFor=&quot;testImage&quot;&amp;gt;이미지 추가&amp;lt;/label&amp;gt;
        &amp;lt;input
          id=&quot;testImage&quot;
          type=&quot;file&quot;
          accept=&quot;image/*&quot;
          onChange={handleImageChange}
        /&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div&amp;gt;
        {image &amp;amp;&amp;amp; (
          &amp;lt;Image
            src={image}
            fill
            alt={&quot;추가된 이미지 입니다.&quot;}
          /&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 첫 이미지는 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tIAe9/btstfmH4FE8/yThu06JTK4ZjE1i0bU1vlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tIAe9/btstfmH4FE8/yThu06JTK4ZjE1i0bU1vlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tIAe9/btstfmH4FE8/yThu06JTK4ZjE1i0bU1vlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtIAe9%2FbtstfmH4FE8%2FyThu06JTK4ZjE1i0bU1vlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;286&quot; height=&quot;39&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;39&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;label 태그 부분을 조금 살펴보면, htmlFor 속성값과 input 태그의 id 값을 동일하게 적어야 한다. 이렇게 작성을 해야 이미지 변경 글자를 눌러도 input 태그의 type에 관련된 action들이 실행된다. 그리고 버튼을 눌러 이미지를 추가하면, 추가된 이미지가 브라우저에서 보이게 된다. 사실 익숙하지 않아서 그렇지 그렇게 어려운 내용들은 없다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 브라우저에서 보이고 있는 이미지를 메모리에서 삭제하고 싶다면 아래의 함수를 작성해서 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694050739473&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// img 메모리 제거
const deleteFileImage = () =&amp;gt; {
  URL.revokeObjectURL(image);
  setImage(&quot;&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환한 URL을 저장했던 image 초기화해주는 로직이다. 여기서 URL을 초기화하기 위해 사용되고 있는 &lt;b&gt;URL.revokeObjectURL&lt;/b&gt; 메서드는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;URL.revokeObjectURL()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;URL.revokeObjectURL()&lt;/b&gt;&lt;/span&gt;은 createObjectURL을 통해 생성된 Url을 메모리에서 지울 때 사용한다. 굳이 폐기하는 이유는 javascript 엔진 자체는 생성된 url을 계속 사용하고 있다고 판단을 한다. 따라서 GC로 처리하지 않아 불필요한 메모리 영역이 할당되어 유지되는 것이다. 따라서 URL.revokeObjectURL을 통해서 생성한 Url을 해제하여야 &lt;b&gt;불필요한 메모리 누수&lt;/b&gt;를 막을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Vanilla JS로 이미지 표시하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 화면을 그리는 HTML을 아래와 같이 만들어 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1694048977410&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// upload.html
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot; /&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;input
      type=&quot;file&quot;
      id=&quot;fileElem&quot;
      multiple
      accept=&quot;image/*&quot;
      style=&quot;display: none&quot;
      onchange=&quot;handleFiles(this.files)&quot;
    /&amp;gt;
    &amp;lt;a href=&quot;#&quot; id=&quot;fileSelect&quot;&amp;gt;Select some files&amp;lt;/a&amp;gt;
    &amp;lt;div id=&quot;fileList&quot;&amp;gt;
      &amp;lt;p&amp;gt;No files selected!&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;script src=&quot;./upload.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들고자 하는 로직은, &lt;b&gt;input 태그&lt;/b&gt;의 type을 file로 설정하면 첫 번째에서 위 실습에서 확인한 것처럼 &lt;u&gt;Choose File 버튼&lt;/u&gt;이 생성된다. 그러나 Input 창 말고 다른 UI를 적용하는 방식을 적용하기 위해, input UI는 숨기고&amp;nbsp;&lt;b&gt;a 태그&lt;/b&gt;를 추가로 넣어주었다. 그래서 a 태그를 누르면 input 태그가 실행되는 방식으로 javascript에서 추가를 해줄 것이다. &lt;b&gt;id=&quot;fileList&quot; div 태그&lt;/b&gt; 부분은 추가한 파일의 이미지를 뿌려주는 부분이다. 이때 javascript에서 추가할 로직은 하나를 넣으면 하나의 이미지가 생성되고, 여러 개의 이미지를 한꺼번에 넣으면 여러개의 이미지가 동시에 브라우저에 표출되도록 구현해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML만 보면 아래와 같은 화면이 나올 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cesSt9/btstcQiHQX9/5s3RDT7w9CsqNpbCSlYvUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cesSt9/btstcQiHQX9/5s3RDT7w9CsqNpbCSlYvUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cesSt9/btstcQiHQX9/5s3RDT7w9CsqNpbCSlYvUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcesSt9%2FbtstcQiHQX9%2F5s3RDT7w9CsqNpbCSlYvUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;332&quot; height=&quot;172&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 HTML에 활력을 넣어줄 &lt;b&gt;JavaScript&lt;/b&gt;를 만들어 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694049564086&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.URL = window.URL || window.webkitURL;

const fileSelect = document.getElementById(&quot;fileSelect&quot;);
const fileElem = document.getElementById(&quot;fileElem&quot;);
const fileList = document.getElementById(&quot;fileList&quot;);

fileSelect.addEventListener(
  &quot;click&quot;,
  function (e) {
    if (fileElem) {
      fileElem.click();
    }
    e.preventDefault(); // # 이동 방지
  },
  false
);

function handleFiles(files) {
  if (!files.length) {
    fileList.innerHTML = &quot;&amp;lt;p&amp;gt;No files selected!&amp;lt;/p&amp;gt;&quot;;
  } else {
    fileList.innerHTML = &quot;&quot;;
    const list = document.createElement(&quot;ul&quot;);
    fileList.appendChild(list);
    for (let i = 0; i &amp;lt; files.length; i++) {
      const li = document.createElement(&quot;li&quot;);
      list.appendChild(li);

      const img = document.createElement(&quot;img&quot;);
      img.src = window.URL.createObjectURL(files[i]);
      img.height = 60;
      img.onload = function () {
        window.URL.revokeObjectURL(this.src);
      };
      li.appendChild(img);
      const info = document.createElement(&quot;span&quot;);
      info.innerHTML = files[i].name + &quot;: &quot; + files[i].size + &quot; bytes&quot;;
      li.appendChild(info);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 URL 메서드를 사용하기 위해 크로스 브라우저를 대비한 &lt;span style=&quot;color: #006dd7;&quot;&gt;window.URL와 window.webkitURL로 window.URL&lt;/span&gt; 변수를 만들어 준다. &lt;span style=&quot;color: #006dd7;&quot;&gt;fileSelect 변수&lt;/span&gt;에 지정한 이벤트리스너를 보면, 클릭 시 input 태그를 클릭한 것과 동일하게 작동하도록 로직이 작성되어 있다. 그리고 e.preventDefault() 부분은 기본적으로 a 태그의 작동 방식인 페이지 이동을 방지하기 위한 로직이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;handleFiles()&lt;/span&gt; 함수에서 UI 생성로직과 파일변환로직이 진행된다. core 로직만 확인을 해보면, input의 onchange 로직으로 파일 첨부 시 files가 추가된다. URL.createObjectURL을 통해 URL을 img 태그의 src에 넣어준다. 그리고 Img가 로딩이 완료되면, URL.revokeObjectURL로 URL을 해제하여 메모리 누수를 막는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>react</category>
      <category>URL.createObjectURL()</category>
      <category>URL.revokeObjectURL()</category>
      <category>vanillaJS</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/590</guid>
      <comments>https://han-py.tistory.com/590#entry590comment</comments>
      <pubDate>Thu, 7 Sep 2023 20:00:59 +0900</pubDate>
    </item>
    <item>
      <title>프런트엔드 아키텍처 기초 정리</title>
      <link>https://han-py.tistory.com/584</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;프로젝트를 시작하면, 항상 고민하는 것은 아키텍처에 대한 고민이라고 할 수 있다. 우리는 회사가 가진 리소스에서 가장 효율적으로 프로젝트를 셋업 하기 위해 어떠한 아키텍처를 사용할지 매 순간 고민한다. MVP 개념으로 빠르게 시장 반응을 알기 위해 빠르게 개발이 필요한 경우가 있다. 이러한 경우 개발 빠르게 하기 위하여, vanillaJS를 사용하여 만들 수도 있고, Nextjs를 사용하여 백엔드 없이 하나의 Product으로 개발을 할 수도 있다. 그리고 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;SSR / SSG /CSR 중 어느 것을 사용하는게 프로젝트에 최적화할 수 있을까? 상태관리는 어떻게 할까? 컴포넌트는 어떻게 나누는 것이 좋을까? 와 같은 끊임없는 고민을 한다.&lt;/span&gt;&amp;nbsp; 이러한 고민에는 기본적인 프론트엔드에 대한 포괄적이고 전반적인 지식을 알아야, 상황에 맞는 아키텍처를 선택할 수 있다고 할 수 있다. 이 글을 통해 전반적인 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;프런트엔드 아키텍처에 대한 기초 지식&lt;/span&gt;&lt;/b&gt;을 살펴 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;hanpy.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBUYrG/btsshosOiBl/YKJvCtefeSJ4tIgHYLr3j1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBUYrG/btsshosOiBl/YKJvCtefeSJ4tIgHYLr3j1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBUYrG/btsshosOiBl/YKJvCtefeSJ4tIgHYLr3j1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBUYrG%2FbtsshosOiBl%2FYKJvCtefeSJ4tIgHYLr3j1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-filename=&quot;hanpy.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;UI(User Interface)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;Frontend를 만든다는 말을 UI를 그린다는 말로도 많이 쓰인다. 프런트엔드 아키텍처로 넘어가기 전에 UI에 대해 간단히 알아보자. 모든 프로그래밍은 입력(Input)과 출력(Output)으로 이루어져 있다. Frontend 관점에서 생각해 보면, 사용자가 입력(touch 같은 이벤트)을 주면 프로그래밍은 유의미한 값(화면 변경)을 출력해 주는 것이다. 과거 컴퓨터는 현재 컴퓨터와 다르게 문자열 자체가 UI이었다. 따라서 데이터 자체를 보거나 수정하는 것이 어려웠지 때문에, 사용자가 조금 더 편리하게 프로그래밍을 사용할 수 있도록 변화하여야 했고 사용자 인터페이스(&lt;b&gt;User Interface&lt;/b&gt;)가 생기게 되었다. 사실 최근에는 사용자의 편의보다는 기술에 대한 이해가 중요 시 되고 있긴 하지만, Frontend의 근본은 사용자가 편리하게 제품을 사용할 수 있도록 만들어야 함을 잊으면 안 된다. 정리하면,&amp;nbsp;&lt;b&gt;UI(&lt;b&gt;User Interface&lt;/b&gt;)란 사용자와 컴퓨터 프로그램 사이에서 의사소통 할 수 있도록 만들어진 매개체를 뜻한다.&lt;/b&gt;(위키백과)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt; UI의 관점에서 크게 2가지로 나누어 생각해 볼 수 있다. 바로&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;데이터를 시각화하는 부분(View)&lt;/b&gt;&lt;/span&gt;과&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;데이터를 조작(Bussiness Logic)&lt;/b&gt;&lt;/span&gt;하는 부분이다. 우리(개발자)가 만드는 제품과 사용자와의 상호작용이 User Interface라는 것을 알았다. 그렇다면, UI 관점에서 Frontend를 뜯어보자. 우선 사용자가 브라우저를 통해 화면을 보는 부분이 있고, 그 화면에서 사용자가 터치하거나 입력하여 데이터를 등록하거나 수정하는 부분이 있을 것이다. 전자 부분이 View라고 할 수 있고, 후자 부분이 Bussiness Logic이라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;1. View&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;프런트엔드 기준으로 데이터를 시각화한다는 것은 무엇일까? 데이터를 사용자가 확인할 수 있도록 화면에 보여주는 것이다. frontend를 배우는 입장에서는 이는 HTML, CSS, JavaScript를 활용해 웹을 사용자에게 보이게 하는 부분이라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;2. Bussiness Logic&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;그렇다면 데이터 조작이란 무엇일까? View 단계에서 시간화된 데이터를 사용자는 볼 수 있다. 그리고 사용자는 데이터를 보면서 상호작용(CRUD)을 할 수 있다. 이는 사용자가 특정 이벤트를 발생시키고, 이벤트에 따른 화면 변환이나 데이터 변경을 진행하게 된다. 즉, View를 제외한 모든 부분이 Bussiness Logic이라고 한다. 혹자는 Domain Logic이라고도 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;위에서 알아본 내용을 토대로, 프런트엔드 아키텍처라는 것은 View 로직과 Bussiness Logic을 분리하여 프로젝트를 만드는 것인가 라는 추론을 할 수있다. 반은 맞고 반은 틀리다. 이러한 관점을 이해하기 위해서는 프론트 엔드가 만들어진 역사를 보면서 조금 더 이해를 할 필요가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;프런트엔드 아키텍처&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;프런트엔드 개발자란, View 로직과 Bussiness Logic을 둘 다 커버가능해야 한다. 그렇기 때문에 웹 이 발전하면서, 다양한 관점으로 새로운 아키텍처들이 생겨나기 시작한다. 프로젝트 코드 정리나 재사용성의 관점으로 아래의 글들을 확인해 보자. 간단하게 훑고 넘어가는 것이기 때문에, 디테일한 내용은 구글링을 통해 지식 습득을 하도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;1. Web 1.0&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;Web 1.0은 1990년 초반부터 2000년 초반까지는 초창기 인터넷이라고 할 수 있다. 이러한 Web의 시작은 우리가 현재 보고 있는 화려한 웹페이지는 아니었다. 단순하게 가상공간에&amp;nbsp;&lt;b&gt;HyperText&lt;/b&gt;가 적힌 문서를 링크를 통해 확인할 수 있을 뿐이었다. 이러한 HyperText가 모든 사람이 쉽게 공유할 수 있도록 만든 것이 바로&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt; HTML(Hypertext Markup Language)&lt;/b&gt;&lt;/span&gt;라고 할 수 있다. 추가로 이러한 하이퍼텍스트 공유를 위해 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;HTTP(Hypertext Transfor Protocol)&lt;/b&gt;&lt;/span&gt;를 이용했다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;이때는 Web은 &lt;b&gt;읽기 전용의 웹&lt;/b&gt;이었고, 사람들이 접속해서 정보를 검색하는 것에 초점이 맞춰있었다. 왜냐하면, 초기 HTML은 UI 관점에서 데이터를 변경할 수 없었기 때문이다. 따라서 만약 변경이 필요하다면, 서버의 HTML 파일을 변경하여 저장하는 방식으로 수정이 가능했다. 그 후로 동적으로 서버에서 페이지를 생성가능하게 된다. 쉽게 말하면, 페이지를 이동해야 다른 정보를 가지고 올 수 있게 된 것이다.(같은 주소에서 CSR처럼 화면이 변경되는 것은 아니다.) 이때 PHP가 탄생하게 되고 여러 서버 스크립트 언어가 생겨나기 시작한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;2. JavaScript 등장&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;HTML이라는 글자를 핸들링하기 위해서 스크립트 언어가 필요하다는 고민을 개발자들이 하게 된다. 그래서 서버와의 통신을 통한 UI 변환이나 데이터 검증을 위해 JavaScript가 탄생하게 된다. 이때는 JavaScript는 Nodejs의 JS가 아니다. 브라우저 자체에서 돌아가는 JavaScript라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;3. CSS 등장&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;HTML 자체에서 스타일을 줬었는데, 유지보수가 어려워 콘텐츠와 서식을 분리하기 위해 CSS가 만들어지게 된다. 초기 CSS는 &amp;lt;style&amp;gt; 태그에 넣었다. 그러다 추후 별도의 파일로 css를 저장하여, &amp;lt;link&amp;gt; 태그로 인베드 하는 방식으로 변경되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;4. Internet Exploer 대중화&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;마이크로소프트의 컴퓨터가 세계적으로 유명해지기 시작 한다. 따라서 Windows가 대중화되고 Internet Exploer가 각각의 컴퓨터에 기본으로 설치가 되게 된다. 이러한 수요로 기업에서는 홈페이지를 만드는 것이 명함을 만드는 것과 같게 되기 시작한다. 따라서 기업의 수요로 인해, 웹 자체를 만드는 것이 솔루션화 되기 시작한다. 이러한 현상을 통해 수많은 웹 디자이너와 HTML/CSS를 다루는 웹 개발자라는 직업이 생기기 시작한다. 하지만 이때까지는 웹 자체가 회사의 메인 사업이 아니었고, javascript 자체가 비즈니스 모델과 상관이 없었기 때문에 개발자들 사이에서 웹 개발자는 비주류 개발자로 취급되기도 하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;5. Ajax&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;기본적으로 지금의 JavaScript와 다르게 브라우저 환경자체가 굉장히 열악했다. 따라서 Application 자체를 웹으로 만들 필요 없이 ActionX나 Flash를 많이 활용을 해서 만들었다. 이 시기에 구글에서는 OS를 만드는 것이 아닌 웹 자체를 OS로 만들기 위한 고민을 하였고, 대중화된 Internet Exploer를 능가하는 브라우저를 만들기를 착수한다. 이러한 준비를 통해 구글에서&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Google Maps&lt;/b&gt;&lt;/span&gt;을 javascript 비동기를 활용해 만들어 출시한다. 이 시기 &lt;b&gt;웹 자체로만 다른 도움 없이 동적으로 만든다는 것&lt;/b&gt;은 굉장한 혁신이었고, 이를 통해 javascript가 재평가를 받게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;그렇다면 Google Maps을 탄생시킨 Ajax에 대해 알아보자. &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;Ajax(Asynchronous JavaScript and XML)&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;를 이해하려면,&lt;/span&gt;&lt;/span&gt; 2000년 초반에 등장한 &lt;b&gt;RIA(Rich Internet Application)&lt;/b&gt; 패러다임에 대해 먼저 알 필요가 있다. RIA란 웹은 설치가 필요 없다는 장점을 유지하면서, 데스크톱 application에 비해 늦은 응답속도나 조작(단점)을 개선하는 기술의 통칭이라고 할 수 있다. RIA 패러다임 이전과 이후의 가장 큰 변화는 정적에서 동적으로 변한 것이라고 할 수 있다. 즉, RIA 이전 웹은 화면에 표시된 이후로 변화가 없었다. 하지만, RIA 이후에는 현대 웹 브라우저처럼, &lt;b&gt;화면 렌더 이후에도 사용자의 이벤트에 따라 화면이 변화하는 것&lt;/b&gt;을 의미한다. 동적으로 변환될 때, 페이지 전체가 로딩되지 않는 것이&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Google Map&lt;/b&gt;&lt;/span&gt;의 비동기라고 할 수 있다. 이러한 기술을 구현할 수 있게 도와주는 것이 바로 Ajax이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;6. 웹 표준&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;이전 HTML 1.0에서는 사실상 표준이 없었다. 따라서 현재 사용하는 태그 element도 이름과 기능이 다 달랐다. 따라서 같은 웹 화면을 만들려면, 기업마다 제공하는 브라우저 정책에 맞게 따로 개발을 해야 했다. 하지만, 웹 사용자가 증가함에 따라 W3C(World Wide Web Consortium)에서는 각 기업마다 만드는 웹에서는 HTML/CSS/JAVASCRIPT이 같다면, 같은 화면을 보여주기 위한 표준에 대한 고민을 시작한다. 그래서 HTML 2.0부터는 표준화를 위한 노력을 시작했고, HTML 4.01부터는 CSS와 HTML을 분리하여 HTML에서는 전반적인 구조만 명시하도록 변경되었다. 그 후 W3C에서는 XML과 HTML을 합성하여&amp;nbsp; 만든 XHTML을 웹 표준으로 만들려고 했다. 그러나 당시 대중적인 브라우저를 가진 기업(MS, Apple, Mozilla, Opera)은 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;WHATWG(Web Hypertext Application Technology Working Group)&lt;/b&gt;&lt;/span&gt;를 출범하고, 표준기관의 XHTML을 반대하고 기존의 HTML을 발전시켜 HTML의 표준을 만들려고 했다. 그것이 바로 현재 우리가 사용하고 있는 HTML5의 시초이다. 결국 W3C는 XHTML을 웹 표준으로 하지 않고 WHATWG에서 정의한 표준을 따르기로 한다. 결국엔 HTML5와 CSS3이 표준으로 확정되게 되고 현재 우리가 사용하는 &lt;b&gt;HTML5&lt;/b&gt;가 된 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;7. jQuery 등장&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;위에서 웹 개발도구가 표준화되고 발전하면서, 웹 개발 자체가 개발자들 사이에서 관심을 받게 된다. 또한 Ajax의 탄생으로 서버자체는 HTML을 매 순간 만들 필요 없이, 비동기 데이터 교환만으로 UI 처리가 가능해졌다. 따라서 javascript의 라이브러리들이 발전하고 사용법도 발전하게 된다. 사실 크로스 브라우저에서 JavaScript를 작성하는 것은 쉬운 문제는 아니었다. 그래서 John Resig가 이러한 문제를 해결하기 위해 jQuery 라이브러리를 개발하게 된다. 웹 표준 API와 Ajax까지 해결할 수 있는 jQuery 라이브러리는 현재 가장 유명한 라이브러리 중 하나라고 할 수 있겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;8. 크롬 브라우저 탄생&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;OS는 Window의 대중화로 독보적으로 마이크로소프트가 가져갔다고 할 수 있다. 하지만, 구글에서는 OS를 만드는 것이 아닌 웹 자체를 OS로 만들기 위한 고민을 하였고, Internet Exploer를 능가하는 브라우저를 만들기를 시작한다. 이러한 이유의 초석으로 JavaScript의 성능을 높이는 것이 중요 과제가 된다. 이러한 이유로 &lt;b&gt;V8 JavaScript 엔진을 개발하고 오픈소스로 공유&lt;/b&gt;하게 된다. 그 이후 safari 오픈소스 브라우저 엔진인 webkit을 결합시킨 크롬 브라우저가 탄생하게 된다. 크롬 브라우저의 탄생으로 매우 빠른 속도의 브라우저뿐만 아니라 최적의 디버깅 환경을 개발자들이 사용할 수 있게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;9. Nodejs&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;기본적으로 JavaScript자체는 웹 바라우저에서만 동작을 했다. V8 오픈 소스의 등장 이후에 &lt;b&gt;서버사이드 환경에서 개발할 수 있는 Node&lt;/b&gt;가 탄생한다. 이는 기본적으로 JavaScript는 언어가 아니라는 지배적인 의견이 변하는 계기가 된다. 또한 JavaScript로 서버 개발까지 할 수 있어, 앞으로의 express/nestjs의 토대가 생긴 것이라 할 수 있다. Node는 module 방식을 채택하여 추후 나올 번들러나 es module의 토대가 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;module이 없는 경우에는 한 파일에 대부분의 로직을 다 작성을 해야 했기 때문에, 디버깅이 어렵고 재사용이 어려웠다. module 자체가 발전하면서 module을 패킹징하여 등록하고 관리할 수 있는 npm 이 등장하게 된다. 이때부터 JavaScript 생태계 자체가 폭발적으로 성장하게 된다. 추가로 webpack, babel 등과 같은 Node 기반의 dev-ops도 등장하면서 기업들에서 prod로 만들 수 있을 가능성들이 보이기 시작한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;10. MVC (Model / View / Controller) 패턴&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;HTML / CSS / JavaScript로 관리하면서, jQuery 같은 DOM을 잘 다룰 수 있도록 발전하다 보니 HTML과 JavaScript를 합치려는 시도가 나타나기 시작했다. 즉, &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;데이터 조작과 DOM 조작을 하나로 관리하기 시작&lt;/b&gt;&lt;/span&gt;했다. 따라서 화면 단위가 아니라 컴포넌트 단위로 개발을 진행하기 시작하면서 MVC 컴포넌트가 탄생했다. MVC 패턴이란 각 컴포넌트(Model, Vide, Controller)가 담당하는 역할을 나누어 구현하는 방식이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;간단히 설명해 보면, Model은 데이터와 비즈니스 로직을 담당한다. 따라서 컨트롤러에 의해 원하는 값을 제공하고, 추가 비즈니스 로직을 수행한다. &lt;b&gt;View&lt;/b&gt;는 입출력 인터페이스를 담당한다. &lt;b&gt;Controller&lt;/b&gt;는 출력 정보를 &lt;b&gt;Model&lt;/b&gt;에게 가져와 view와 연결해 주거나 View에게서 받은 데이터를 모델에서 전달하는 역할을 한다. 각각의 컴포넌트 들은 다른 컴포넌트의 정보를 알면 안 되고, 책임 분리가 필요하다. 즉, Controller는 비즈니스 로직과 입출력 인터페이스에 대한 책임이 없어야 하고, Model과 View도 다른 컴포넌트에 대한 정보를 포함하면 안 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;&lt;b&gt;Model 자체의 정의를 조금 더 알아보자.&lt;/b&gt; Model은 상태 / 구조/ 유저의 행동을 나타내는 역할을 한다. 그리고 View는 하나 / 여러 개의 Model로부터 받은 정보를 나타낸다. Model이 변경 시 View에 적절한 UI 변경을 위해 Model에 View를 등록하여 Model이 변경됨을 알려줘야 한다. 정리하면, Model Layer는 View가 화면에 무엇을 그리고 변경해야 하는지 알려주는 Layer라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;아래는 MVC에 대한 대략적인 다이어그램을 그린 것이다. 아래의 그림에서는 View와 Model이 데이터를 주고받지 않는다. 하지만 (Observer 패턴) 정책에 따라 View와 Model이 데이터를 주고받을 수도 있다. 정리하면, MVC 패턴이란 Model View Controller를 활용하여 아키텍처를 만든 총칭이라고 할 수 있다. MVC 패턴 안에서도 어떤 디자인 패턴을&amp;nbsp; 추가로 사용하는지에 따라 변경될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg0MI2/btssbClCVge/XKpRMTbV9NcAyZtzB6Zxq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg0MI2/btssbClCVge/XKpRMTbV9NcAyZtzB6Zxq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg0MI2/btssbClCVge/XKpRMTbV9NcAyZtzB6Zxq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg0MI2%2FbtssbClCVge%2FXKpRMTbV9NcAyZtzB6Zxq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;964&quot; height=&quot;344&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;User의 Event/Input 이 Controller에 들어온다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;Controller는 필요한 데이터를 Model에 요청한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;요청받은 Model은 변경된 데이터를 Controller에 전달한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;전달받은 Controller는 변경이 필요한 View에 요청한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;View는 필요시 Model에게 상태를 요청하고, 화면을 그린다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;11. MVVM(Model / View / ViewModel) 패턴&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;MVVM은 MVC의 변형 패턴으로&amp;nbsp;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;View를 추상화하여 ViewModel을 만드는 것&lt;/b&gt;&lt;/span&gt;이 핵심이라 할 수 있다. View는 자신이 가진 상태를 표현하고 명령을 내릴 수단을 가진다. 따라서 선언적으로 Model을 실행하는 것이 가능하게 된 것이다. 이는&lt;b&gt; 추상화된 ViewModel은 당연히 View자체에 대해 몰라야 하고, 다른 플랫폼에서도 재사용될 수 있다.&lt;/b&gt; 쉽게 말하면, HTML을 class나 id로 접근하는 것이 아니라, 직접적인 HTML에 접근하여 핸들링하는 방식으로 확장되었다고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;여기서 &lt;b&gt;선언적(Declarative) 프로그래밍&lt;/b&gt;이라는 말이 나온다. 선언형 프로그래밍을 보면, 내부적으로는 절차적 프로그래밍으로 되어 있지만, 절차적인 명령형 프로그래밍이 추상화되어 선언형 프로그래밍이 된다고 할 수 있다. 어떤 일을 어떻게 하는지가 절차적(명령형) 프로그래밍이라면, 무엇을 할지가 선언적 프로그래밍이라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;MVVM 패턴에는 데이터 바인딩이라는 개념이 나온다. 추상화된 View와 물리적이 View를 연결하는 것이&lt;b&gt;바로 &lt;span style=&quot;color: #ef6f53;&quot;&gt;데이터 바인딩(data binding)&lt;/span&gt;&lt;/b&gt;이다.&amp;nbsp; 기본적으로 MVC에서는 Controller가 View와 Model을 연결한다. 하지만, MVVM에서는 작업흐름의 제어보다는 View와 ViewModel을 동기화하는데 집중한다. 따라서 MVVM 은 데이터 바인딩에 의존하여 동기화를 진행한다. 데이터 바인딩을 통해 ViewModel의 상태 변경 시 View도 상태가 함께 변경된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vou2O/btssfYBEmrO/XOOQ0I1UC1Ju6YkknUtYkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vou2O/btssfYBEmrO/XOOQ0I1UC1Ju6YkknUtYkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vou2O/btssfYBEmrO/XOOQ0I1UC1Ju6YkknUtYkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVou2O%2FbtssfYBEmrO%2FXOOQ0I1UC1Ju6YkknUtYkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;744&quot; height=&quot;122&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;사용자의 Action이 View를 로 전단된다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;View에서 Action이 들어오면,&amp;nbsp;Command 패턴(선언적)으로 ViewModel에 Action을 전달한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;ViewModel은 Model에게 데이터를 전달한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;Model은 ViewModel에게 요청받은 데이터에 대한 응답을 준다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;ViewModel은 응답받은 데이터를 가공하여 저장한다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;View는 ViewModel과&amp;nbsp;데이터바인딩하여 화면에 표현된다&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;데이터 변경 시 html 변화 없이 템플릿 자체로 변화시키는 방법에 대한 연구가 계속되었다. 이러한 과정 속에서 angaulr.js 같은 MVVM 아키텍처 웹 프레임워크가 만들어진다.&amp;nbsp; angaulr.js를 보면 DOM을 직접적으로 조작하는 코드는 사라지고, 데이터를 angaulr에 전달하면 알아서 그려준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;12. Frontend와 backend의 분리&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;거대 웹서비스들이 등장하면서, 규모가 커지기 시작하면서 유지 보수를 위해 기존과는 다른 방법의 관리가 필요했다. 이러한 이유로 데이처 치리/ 화면 개발을 분리 하여 전문성을 높이는 것이 바로 Frontend와 Backend의 분리라고 할 수 있다. Facebook에서 JSX를 기본으로 하는 React Framework(2013)를 탄생하면서 프런트엔트라는 직업이 자리 잡기 시작한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;13. Container - Presenter 방식&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;사실상 여기서부터의 컴포넌트는 React 컴포넌트라고 할 수 있을 것 같다. &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;UI 컴포넌트를 만드는 이유가 UI의 재사용과 유지보수&lt;/b&gt;&lt;/span&gt;를 위해서라고 할 수 있다. 그러나 프로젝트가 복잡해지면서 데이터가 컴포넌트마다 들어가니 로직이 복잡해지기 시작했다. 이러한 문제를 해결하기 위해 컴포넌트의 책임을 크게 2가지로 분리하여 사용하기로 한다. 데이터를 받아서 보여주기만 하는 Presenter 컴포넌트와 데이터를 조작하는 Container 컴포넌트로 분리한 것이 바로 Container - Presenter 방식이다. Presenter 컴포넌트 자체가 UI만을 나타내기 때문에 재사용에 최적화된 컴포넌트이고, 현재도 많이 사용되고 있는 패턴이라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;14. Props Drill&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;Container - Presenter 방식으로 컴포넌트를 나누어, 데이터를 조작하는 하나의 Container에 포함된 UI 컴포넌트들이 많아지기 시작했다. 따라서 중간 컴포넌트에서는 데이터를 사용하지 않더라도 하위 컴포넌트로 데이터를 보내기 위한 props들이 필요해졌다. 사실상 컴포넌트 독립과 재사용을 위해 컴포넌트를 분리했지만, 중간 컴포넌트로 인해&lt;b&gt;, 상위 컴포넌트와 하위 컴포넌트의 결합도가 올라가는 현상&lt;/b&gt;이 발생한 것이다.&amp;nbsp; 이러한 문제는 또한 하위 컴포넌트의 deps가 깊어질수록 props로 길게 데이터를 보내야 했다. 이러한 문제가 바로 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;Props Drilling 문제&lt;/b&gt;&lt;/span&gt;라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;15 상태 관리(State Management)의 발전&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;위와 같이 Props Drilling 문제가 발생하다 보니, 비즈니스 로직을 컴포넌트 계층구조에 포함시킬 필요를 느끼지 못하게 된다. 따라서 &lt;a href=&quot;https://han-py.tistory.com/487&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;상태 관리(State Management)&lt;/b&gt; &lt;/span&gt;&lt;/a&gt;개념이 나오게 된다. &lt;b&gt;View와 비즈니스 로직을 완전히 분리하여 단방향 데이터 구조를 가지는 FLUX 패턴&lt;/b&gt;이 등장했고, FLUX 패턴을 근본으로 한 Redux의 등장으로 이러한 패턴이 주류가 되었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;하지만, Redux 자체는 많은 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;보일러플레이트&lt;/b&gt;&lt;/span&gt;가 필요했지 때문에, 대규모 프로젝트가 아닌 경우에는 오버엔지니어링이 되었다. 이는 하나의 간단한 로직 추가하는데도 엄청난 많은 코딩을 써야 했기 때문에, Redux를 대체할 상태관리 기술들이 발전하기 시작한다. React 본진에서는 Hooks를 만들어 외부 비즈니스 로직과 쉽게 연동하여, 화면을 변환시킬 수 있도록 되었고, Context를 통해 하위 컴포넌트에서 데이터를 공유할 수 있도록 만들었다. 또한 조금 더 전역 상태 관리를 용이하게 하기 위해 Atom을 활용해 데이터를 저장하고 변경된 데이터를 View로 전달하는 Recoil도 등장하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;16. API 상태관리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;사실 Frontend에서 상태관리를 하고 있지만, 데이터의 CRUD는 서버에서 이루어진다고 할 수 있다. 그렇다면 비즈니스 로직은 벡엔드에서 처리하고 API 자체를 통한 상태관리를 하는 방향으로 변화하게 된다. 이러한 API 상태관리로는 React Query와 SWR이 있다고 할 수 있다. API로 상태를 관리한다는 말이 어색할 수 있다. 쉽게 설명하면, API의 결괏값을 받은 후에 API를 캐싱한다. 이러한 캐싱을 통해 전역 API를 관리하는 것이 가능해진다고 이해하면 된다. 물론 디테일한 부분은 구글링을 해보면 좋을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;17. MVI(Model / View / Intent) 아키텍처&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;MVI의 View는 기본적인 화면을 의미한다. &lt;b&gt;View&lt;/b&gt;에서 사용자가 Event를 발생시키는 것을 사용자의 의도라고 할 수 있다. 이러한 &lt;u&gt;사용자의 의도가 포함된 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;비즈니스 로직&lt;/b&gt;&lt;/span&gt;을 &lt;b&gt;Intent&lt;/b&gt;&lt;/u&gt;라고 한다. 이러한 &lt;u&gt;Intent에 따라서 데이터를 변환시키는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;비즈니스 로직&lt;/b&gt;&lt;/span&gt;을&amp;nbsp; &lt;b&gt;Model&lt;/b&gt;&lt;/u&gt;이라고 한다. 따라서 단반향으로 2개의 비즈니스 모델을 처리한다고 생각하면 된다. 언듯 보면, MVC / MVVM과 비슷해 보이지만, 가장 다른 점은 하나의 컴포넌트의 기준이 아니라 프로젝트 전체에 적용이 된다는 점이다. MVI를 아래의 함수로 많이들 표현을 하는데, 직관적인 이해가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693268375388&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;view(model(intent(user(view(model(intent(user())))))))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;유저가 의도를 가지고 intent를 보내면 model에서 데이터를 변경하거나 필요한 데이터를 View로 보내서 화면이 변경됨을 나타내는 함수라고 할 수 있다. 이러한 cycle이 반복되어 실행되는 게 MVI 아키텍처라고 할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;글을 읽다 보면 느낄 수 있겠지만, 기존에는 재사용이 가능한 컴포넌트를 이용하여 서비스를 구성하는데 집중했다. 최근 방향은 View와 비즈니스 로직을 분리를 우선이 한다. 비즈니스 로직을 순차적으로 정리하면 다음과 같다고 할 수 있다. 사용자의 Action을 Intent로 표현한다. 그리고 Action을 통해 데이터를 변화시키거나, 변화된 데이터에 따른 View로 전파하는 로직으로 표현할 수 있다. 따라서 View 자체끼리는 느슨한 결합이 되고, View는 비즈니스 로직에 의존적이게 되어 변경 로직이나 요구사항에 유연한 대처가 가능하다고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;19. 프런트엔드 아키텍처를 어떻게 만들까?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;정리해 보자. 위에서 우리가 만들어야 할 컴포넌트는 독립성을 유지하고 의존성을 최소화하여 재활용할 수 있어야 한다. 하지만 컴포넌트의 계층구조가 복잡해지고, 데이터를 받는 방식이 복잡해지면서 컴포넌트끼리 데이터를 주고받기 된다. 따라서 컴포넌트 재활용이 어려워지기 시작한다. 이러한 문제를 해결하기 위해 컴포넌트와 데이터를 분리하는 방향으로 단방향 아키텍처가 만들어졌고, 전역 상태 관리가 만들어졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;우리는 습관적으로 useState를 사용하여 상태관리를 한다. 그러나 상태 관리를 잘못하면 '유지보수가 어려운 코드'가 된다. 따라서 처음 우리가 생각해야 할 부분은 View 로직과 비즈니스 로직( = 도메인 로직)의 명확한 구분에서 시작된다.&amp;nbsp;물론, 특정 컴포넌트 영역만 영향을 주는 비즈니스 로직이라면, 특정 컴포넌트 안에서만 만들어줘도 좋다. 작은 컴포넌트 안에서만 책임이 필요하다면, 빠르게 만들 수 있고 구축 비용을 줄일 수 있기 때문에 매우 효율적인 방법이라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;반대로 페이지 수준에서 비즈니스 로직을 다룬다면, module 하나로 분리해서 만드는 것도 좋은 방법 중 하나이다. 하지만, 이러한 로직에 상태가 추가된다면 말이 달라진다. 상태 값이 &lt;b&gt;비즈니스 로직의 상태 값&lt;/b&gt;인지 &lt;b&gt;화면을 변경시키는 상태 값&lt;/b&gt;인지 확인을 해야 한다. 그리고 이에 따라 비즈니스 로직의 상태 값과 화면의 상태 값을 &lt;b&gt;어느 시점에 동기화를 시켜 화면에 표출&lt;/b&gt;하는지도 확인이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;따라서 변경과 초기화 이슈가 있을 수 있기 때문에, 세션이 유지되는 동안만 비즈니스 로직이 유지되고 화면에 변경이 적용되도록 만들기 위해 노력해야 한다. 그렇다면 내가 작성한 코드가 View 로직과 비즈니스 로직이 잘 분리가 되었는지 어떻게 확인할 수 있을까?&amp;nbsp;&lt;b&gt;추가 요구사항이 들어왔을 때 쉽게 수정할 수 있는가 없는가로 확인하면 된다.&lt;/b&gt; SaaS서비스에서 월별 구독 결제에서 중간 취소 시 1/3만 환불을 할 수 있는 비즈니스 로직을 추가한다고 해보자. 이러한 경우 수정할 부분이 많다면 비즈니스 로직과 View 로직이 적절하게 분리되지 않은 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;20. 좋은 아키택처 코드란 무엇일까?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;( 이 부분은 정답이 없기에 관련 부분은 개발을 진행하면서, 계속 추가할 예정입니다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;보통 우리는 Event 함수 자체에 State를 직관적으로 변경한다. 아래의 예를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693119817320&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async const onClick = () =&amp;gt; {
    setModal(false)
    setSampleDate([...sampleDate, {id : &quot;hanpy&quot;}])
    await fetching({id : &quot;hanpy&quot;})
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;위의 로직은 직관적으로 이해하기가 쉽다. modal을 종료하고, Date를 변경한다. 이러한 로직의 단점은 프로젝트가 복잡해졌을 때, 발생한다. 예를 들어 모달이 닫히는 과정에서 에러가 났다면, 데이터 결과를 하나씩 하나씩 추적해야 한다. 모달을 닫는 setModal()이 여러 로직에서 많이 쓰인다면, 각각의 로직을 하나씩 확인하여야 한다. 또한, View 자체에서 데이터를 직접 수정하게 된다면, Model과 View 사이에 의존성이 생기게 된다. 이러한 문제를 방지하기 위해&lt;b&gt; View에서는 Intent(의도)만 전달을 하고, Model에서만 데이터를 변환을 할 수 있도록 만들어야 한다.&lt;/b&gt; 이는 데이터 변환 로직을 Model 모듈에만 있기 때문에 응집도가 높아지고, View와 비즈니스 로직 사이에 느슨한 결합을 구현할 수 있게 된다.&amp;nbsp; 필요하다면 관련 부분을 검색해 보면 좋을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;긴 글을 통해, 다양한 아키텍처들에 대해 알아보았다. 정답이 있다고 말하기는 어렵다. 하지만 좋은 아키텍처를 만드는 이유는, 더 쉽게 코드를 유지보수/재사용하여 product의 에러를 최소화하고 인적 리소스를 절약하는 위함임은 틀림없다고 생각한다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;필자도 오늘도 아키텍처에 대한 고민을 하고 있다. 위의 내용을 파탕으로 주어진 상황에 따라 프로젝트를 성공적으로 이끌 수 있도록 아키텍처를 고민해 보면 좋을 것 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>Ajax</category>
      <category>Container - Presenter</category>
      <category>frontend</category>
      <category>MVC</category>
      <category>MVI</category>
      <category>MVVM</category>
      <category>props drill</category>
      <category>User Interface</category>
      <category>V8</category>
      <category>아키텍처</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/584</guid>
      <comments>https://han-py.tistory.com/584#entry584comment</comments>
      <pubDate>Sun, 3 Sep 2023 21:00:01 +0900</pubDate>
    </item>
    <item>
      <title>input 태그을 focus 하기</title>
      <link>https://han-py.tistory.com/454</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 화면을 들어가 보면 마우스를 클릭할 필요 없이, 바로 input 창에 커서가 올라가 있는 것을 확인 할 수 있다. 이러한 방법을 autofocus라고 한다. 관련 내용을 보면서 구현까지 해보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cC0wpn/btssgJEKTER/ZVrPQqPHy5F2akQq7frdh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cC0wpn/btssgJEKTER/ZVrPQqPHy5F2akQq7frdh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cC0wpn/btssgJEKTER/ZVrPQqPHy5F2akQq7frdh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcC0wpn%2FbtssgJEKTER%2FZVrPQqPHy5F2akQq7frdh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1104&quot; height=&quot;610&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;autofocus&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 로드 시 별도의 클릭없이 지정한 input창에서 바로 작성을 가능하게 할 수 있도록 하는 기능을 만들어 보자. 이러한 기능을 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;autofocus&lt;/span&gt;&lt;/b&gt;라고 한다. 기본적으로 vanilla javascript로 작성을 해보자. 당연히 react에서도 focus를 사용할 수 있다. 아래의 예시를 처음부터 자세히 본다면, 앞으로 autofocus를 마스터 했다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react/nextjs에서 autofocus를 사용하는 방식은 약간 다르다. 관련 내용은 아래의 링크를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/510&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[React] 접속 시 input창에 자동 글쓰기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input tag에 autofocus 추가하기1&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 방법으로는 html의 input 태그에 직접 autofocus를 추가하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647409609253&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input type='text' name='userNickname' placeholder='아이디을 입력하세요.' autofocus/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 input 태그의 마지막 부분에 autofocus를 추가하기만 하면 웹페이지 로드 시 자동으로 autofocus가 되서 바로 키보드를 치는 것이 가능하다. 이렇게만 만들면 사실 어려운 부분은 없다. 그러나 조건에 맞게 핸들링하기는 아쉽다. 특정 조건에서만 focus를 넣게 만들려면, html만으로는 힘들고 javascript를 같이 사용을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;input tag에 autofocus 추가하기2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 방법은 javascript로 핸들링을 하는 방법이다. 아래는 html 태그이다.&lt;/p&gt;
&lt;pre id=&quot;code_1647409738095&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &amp;lt;label
        for=&quot;id&quot;
        &amp;gt;아이디
    &amp;lt;/label&amp;gt;
    &amp;lt;input
        id=&quot;id&quot;
        type=&quot;text&quot;
        placeholder=&quot;아이디를 입력해주세요.&quot;
    /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 위의 input 태그의 id 값으로 element를 받아와서 설정을 진행할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647413228005&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 방법1
const inputId = document.querySelector(&quot;#id&quot;)
inputId.focus()


// 방법2
const inputId = document.getElementById(&quot;id&quot;)
inputId.focus()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 두 가지 방법으로 핸들링이 가능하다. 위의 방법은 #id로 id 값을 #으로 가지고 오는 것이고, 방법2는 getElementById를 활용해서 id 값을 바로 넣어주면 된다. 그리고 focus 매서드를 뒤에 붙여주면 자동으로 autofocus가 된다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;input tag에 autofocus 추가하기3&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방법으로도 충분히 Focus가 가능하다. 하지만 조금 더 디테일하게 focus를 구현해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1693276554109&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const $id = document.getElementBVyId(&quot;id&quot;);

window.addEventListener(&quot;load&quot;, () =&amp;gt; $id.focus());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 로직은 단순한다. id가 id인 값을 불러온다. 그리고 window가 로딩이 완료 됐을 때, 설정한 element에 focus를 주면 된다. window 로딩을 완료를 확인하기 위하여, window.addEventListener(&quot;load&quot;, ) 이벤트를 사용한다. 그리고 $ 기호를 사용하는 이유는 관습적인 이유로 DOM을 가져오는 변수라는 의미로 변수 앞에 $를 붙이는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;심화 focus&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 우리는 focus를 변경할 때, tap을 활용하여 변경을 한다. 그렇다면, Tab 으로 이동시키는 순서도 핸들링할 수 있지 않을까? Tapindex로 관련 핸들링이 가능하다. 추가적으로 더 궁금하다면 &lt;a href=&quot;https://han-py.tistory.com/543&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;여기&lt;/b&gt;&lt;/a&gt;를 눌러 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/JAVASCRIPT</category>
      <category>autofocus</category>
      <category>input tag에 autofocus 추가하기</category>
      <category>nextjs autoFocus</category>
      <category>react</category>
      <category>react autoFocus</category>
      <category>vallina javascript</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/454</guid>
      <comments>https://han-py.tistory.com/454#entry454comment</comments>
      <pubDate>Tue, 29 Aug 2023 23:00:11 +0900</pubDate>
    </item>
    <item>
      <title>[React] 컴포넌트 설계 기초 정리</title>
      <link>https://han-py.tistory.com/583</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #000000;&quot;&gt;Frontend에서 컴포넌트를 만들기 위해 우리가 가장 먼저 접하는 개념은 컴포넌트 주도 개발(Component Driven Development)이라고 할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;컴포넌트 주도 개발(Component Driven Development)&lt;/b&gt;&lt;/span&gt;이란 컴포넌트를 모듈 단위로 개발하여 사용자 인터페이스를 만드는 개발 및 설계 방법론이다. &lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;&lt;b&gt;컴포넌트&lt;/b&gt;란, 상호 교황이 가능하고 표준화된 UI 구성 요소라고 할 수 있다.&lt;/span&gt; 우리는 리액트를 통해 작은 컴포넌트를 만들고, 그 컴포넌트를 활용해 개발을 진행한다. 컴포넌트 주도 개발(CDD)란 재사용 가능한 컴포넌트 만든 후에, 전체 화면(View)을 구성하기 위해 결합해 가는 상향적 구조(bottom-up)라고도 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;UI는 프로젝트가 커질 수록 관리하기가 어렵다. 왜냐하면 크기가 커질수록 로직이 복잡해지기 때문에, 작은 수정에도 UI 자체가 깨지기 쉽고 디버깅이 어렵다. 이러한 문제는 UI 구성요소를 모듈 식으로 세분화하여 유연한 컴포넌트를 만든다면 해결가능하다. CDD의 장점을 정리해 보면 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;독립적으로 컴포넌트를 분리하여, 다양한 시나리오에서 작동하는지 확인 할 수 있다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;컴포넌트 자체 테스트가 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;재사용이 쉬워 UI를 빠르게 작성이 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;컴포넌트를 공유하면 개발 및 디자인이 동시에 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로 CDD를 통해 개발을 진행한다. CDD의 여러 방법 중에 Atomic Design을 알아보고 어떠한 컴포넌트 설계가 우리 개발에 필요한지 고민하는 시간을 가져보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;아토믹 디자인(Atomic design)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;컴포넌트 설계에 정답이 있다고 할 수는 없다. 개발자 각각이 의미있다고 생각되는 기준으로 컴포넌트를 나누기 때문에 매우 주관적이다. 이러한 이유로 재사용이 어려운 컴포넌트가 만들어질 수 있다. 이는 컴포넌트의 관심사가 많아지고 코드가 복잡해지므로 좋은 구조를 가진 컴포넌트를 만들기가 어려워진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTdsxC/btsrEplah4A/4M125SwZNp2dCxXYOLapo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTdsxC/btsrEplah4A/4M125SwZNp2dCxXYOLapo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTdsxC/btsrEplah4A/4M125SwZNp2dCxXYOLapo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTdsxC%2FbtsrEplah4A%2F4M125SwZNp2dCxXYOLapo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;848&quot; height=&quot;250&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이러한 문제는 아토믹 디자인을 통해 어느정도 해결 가능하다. 아토믹 디자인은 이름에서 알 수 있듯이, 모든 것은 atom(원자 컴포넌트)로 구성되어 있고, atom(원자)가 모여 molecule(분자)가 되고, 분자가 모여 organism(유기체)가 되고, 그 후에 원하는 물질이 된다. 이러한 방식으로 각각에 역할을 주고 UI를 구성해 나가는 방식이 Atomic Design이라고 한다. 이를 컴포넌트에 적용해 보면 atom &amp;gt; molecule &amp;gt; organism &amp;gt; template &amp;gt; page로 적용된다고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;atom (원자)&lt;/li&gt;
&lt;li&gt;molcules (분자)&lt;/li&gt;
&lt;li&gt;organism (유기체)&lt;/li&gt;
&lt;li&gt;template (템플릿)&lt;/li&gt;
&lt;li&gt;page (페이지)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;atom&lt;/b&gt;&lt;/span&gt;은 더 이상 분해할 수 없는 기본 컴포넌드라면, input / button 등등 과 같은 기본 HTML element나 글꼴 Layout들이 포함되는 것을 알 수 있다. 폰트나 색상이 아닌 여러 속성들도 더 이상 쪼갤 수 없다면 Atom에 포함된다고 할 수 있다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Molecule&lt;/b&gt;&lt;/span&gt;은 Atom이 여러 개의 Atom이 모여 목적성이 있는 하나의 컴포넌트가 된다. Molecule은 재사용이 많이 되는 구조를 가지며, 단일 책임 원칙을 고려하며 만들어야 한다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;form을 이루는 element들은 molecule라고 할 수 있고, 이러한 molecule이 모여 navbar를 이루면 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;organism&lt;/b&gt;&lt;/span&gt;이라고 할 수 있다. 그렇다면 atom들이 모여 molecule과 organism이 된다는 말인데, 이게 실제 프로젝트를 만들다 보면, 특정한 기준으로 딱 나누어 atom 컴포넌트를 몇 개 모아서 molecule이 된다고 정의하기는 어렵다. 왜냐하면 아토믹 다지인 단위를 나누는 것 자체가 주관적이기 때문이다. 따라서 지금부터의 글은 주관적인 의견이 다분히 반영된다. 참고만 하여 각자의 프로젝트에 맞게 더 좋은 방법을 사용하도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;molecule는 단일 책임 원칙에 따라 하나의 역할을 한다. 그리고 origanism은 여러 개의 atom, molecule, organism으로 구성되어 Layout 기준으로 나누는 영역을 가진다. 이때 기본 원칙은 &lt;b&gt;상태값이 포함되면 origanism으로 판단하고, UI만 존재하여 data를 받아서 작성되는 컴포넌트라면 molecule로 기본 큰 분류를 하면 좋다.&lt;/b&gt; 따라서 naming convension를 세팅할 때, molecules는 UI 관점의 이름(Input, button, Carousel, TextBadge, ProfileImage, ListItem 등등)을 적는 게 좋고, Organism의 경우는 기능적인 이름(Comment, CommentInput, AnnouncementItem, ProfileListItem, CheckBoxListItem, ProfileListItemWithBadge 등등)의 컴포넌트 이름을 넣어준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;br /&gt;&lt;b&gt;단일 책임 원칙(SRP: Single Responsibility Principle)은 다섯 가지 SOLID 애자일 원칙 중 하나로, 하나의 컴포넌트는 하나의 책임을 가져야 한다는 의미이다. 쉽게 말하면, 컴포넌트에 많은 책임이 있다면, 하나의 책임을 변경하면 개발자가 모르는 사이에 다른 컴포넌트에 영향을 줘서 버그를 발생할 가능성이 커진다. 여기서 책임을 모듈이나 기능으로 생각하면 조금 더 쉽게 이해가 될 수 있다.&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면,&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; 단순한 UI를 표시하는 하나의 역할을 가진다면, Molecule로 판단하고, 기능적 요구사항이 포함된다면 Organism이라고 생각하면 좋다.&lt;/b&gt;&lt;/span&gt; 시간을 보여주는 UI는 Molecule로 판단을 하고, 실시간 시간이나 외국 시간을 보여주는 요구사항이 포함된 컴포넌트는 Organism으로 판단한다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Organism과 Template를 판단하는 기준은 재사용으로 판단한다.&lt;/b&gt;&lt;/span&gt; 재사용이 가능하다면 Template(Ex. WorkspackeListTemplate, ContentTemplate 등)으로 판단하고, 재사용이 필요하지 않은 단일 컴포넌트라면 Organism으로 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;organism 제작 후 내부 요소를 추가 수정/추가해야 할 일이 있을 수 있다. 예를 들어 추가 요구사항으로 프로필 컴포넌트에 전화번호를 추가해 달라는 요청이 있었다. 이러한 경우에 새로운 컴포넌트를 만들어야 할까? 기존의 컴포넌트를 사용할 수 없을까? 이럴 때 사용하는 것이 바로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Compound Component&lt;/b&gt;&lt;/span&gt;이다. 관련 내용은 dropdown component에서 추후 추가로 작성을 해 보도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 무엇으로 쪼개어 재사용하여 나누는지 집중하는 것도 중요하지만, 사실 아래와 같이 table의 위치기준으로 나누어 Props를 줄이는 방법도 좋다. 왜냐하면 변수가 많아진다는 것은 예상하지 못하는 결과를 이끌어 내기도 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692576038710&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Table
  top={}
  bottom={}
&amp;gt;
  ...body  
&amp;lt;/Table&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Side Effect 핸들링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Side Effect는 외부의 영향으로 상태가 변경되는지로 판단을 한다. 우리가 주로 사용하는 부분은 네트워크 통신(API)에서 주로 사용한다. 이는 Atom Molecule Oranism Template 내부에 포함시키지 않는다. 이러한 이유는 외부의 요인에 의해 컴포넌트 자체 에러를 발생할 가능성이 있기 때문이다. 따라서 Page 내부에서 Side Effect(API)를 처리하한다. 하지만 이러한 경우에는 Page 자체에 너무 많은 책임을 가지게 된다. 이러한 경우 Wrapped라는 레이어를 하나 더 두고, Side Effect를 포함시켜 주는 것도 좋은 방법이 될 수 있다. 이러한 많은 layer들이 있다면, props로 많은 컴포넌트로 전달이 필요하다. 그러면, Redux 나 Context API를 통해 Wrapped에서 관리하는 방식도 좋은 방식으로 판단된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 사용자 상요작용에 의한 컴포넌트는 Side Effect가 발생하는 지점에서, 넣어주고 의존성이 역전되지 않도록 한다. 쉽게 말하면 model, tooltip 같은 UI는 내부 컴포넌트에서 직접 핸들링하지 않고 상위 컴포넌트에서 아래와 같이 핸들링해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692581716774&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Wrapped = () =&amp;gt; {
  const [visible, setVisible] = useState(false);

  const ModalComponent = ({ visible }) =&amp;gt; (
    &amp;lt;Modal visible={visible}&amp;gt;
      &amp;lt;Content /&amp;gt;
    &amp;lt;/Modal&amp;gt;
  );

  const handleClick = () =&amp;gt; {
    setVisible(!visible);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Component onClick={handleClick} modal={&amp;lt;ModalComponent visible={visible} /&amp;gt;} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 Atomic Design의 원칙을 따라도 되지만, 프로젝트 상황에 따라 적절히 변경하는 것도 좋다. 예를 들면, 필자는 기존의 다른 프로젝트들의 통일성과 복잡한 UI가 없는 프로젝트에서는 아래와 같이 분류하여 컴포넌트를 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Core Component&lt;/b&gt; : Atomic Design의 Atom과 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Wrapper Component&lt;/b&gt; : Core Component를 결합하여 만든 UI Component&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Layout Component&lt;/b&gt; : 화면 Layout을 담당하는 Component(navbar, LNB 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Page Component&lt;/b&gt; : 전체 페이지를 담당하는 Component&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Atomic Design은 항상 옳은가? 기본적으로 소프트웨어는 끊임없이 변화한다. 따라서 변경에 유연하게 대응할 수 있는 컴포넌트를 만들어야 한다. 5단계로 나누어 사용하는 Atomic이 항상 옳다고는 할 수 없다. 그렇다고 상황에 따라 정답이 있는 것도 아니다. 동료들과 고민하면서 프로젝트에 맞게 변경에 유연하게 대응 가능한 컴포넌트 설계를 할 수 있도록 고민해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>Atomic Design</category>
      <category>Component Driven Development</category>
      <category>컴포넌트 주도 개발</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/583</guid>
      <comments>https://han-py.tistory.com/583#entry583comment</comments>
      <pubDate>Tue, 22 Aug 2023 00:24:27 +0900</pubDate>
    </item>
    <item>
      <title>선형탐색알고리즘(linear search algorithm) 과 이진탐색알고리즘(binary search algorithm)</title>
      <link>https://han-py.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;프로그램에서 가장 간단하고 &lt;b&gt;대표적인 알고리즘이 탐색 알고리즘&lt;/b&gt;이다. 기초적인 탐색 알고리즘에는 2가지가 있다. 선형탐색알고리즘이란, 순차적으로 원하는 결괏값을 탐색하는 것이다. 이진탐색알고리즘은 중간지점을 기준으로 반씩 제외하여 원하는 결괏값을 찾는 알고리즘이다. 각각의 알고리즘에 대해 조금 더 알아보고 코드로 적어보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VpWyG/btsrrcffKGC/4MgukfD3px0ThLZ1B0hRi1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VpWyG/btsrrcffKGC/4MgukfD3px0ThLZ1B0hRi1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VpWyG/btsrrcffKGC/4MgukfD3px0ThLZ1B0hRi1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVpWyG%2FbtsrrcffKGC%2F4MgukfD3px0ThLZ1B0hRi1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;584&quot; height=&quot;329&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;1명을 찾기 위해 100만 명을 검사한다고 생각해 보자. 어떻게 찾는 것이 효율적일까? &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;선형탐색의 경우에 순차적으로 원하는 것을 찾는 방식이다. 만약 원하는 사람이 마지막에 있는 경우, 최악의 효율로 100만 번을 검사를 해야 원하는 사람을 찾을 수 있다. 이진탐색의 경우에는 최악의 경우는 20번이다. 생각보다 효율차이가 큰 것을 확인할 수 있다. 각각의 알고리즘에 대해 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;선형 탐색 알고리즘(Linear Search Algorithm)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;왼쪽이나 오른쪽 부터 순차적으로 하나씩 확인하는 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692229793795&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def linear_search(element, some_list):
    for i in range(len(some_list)):
        if some_list[i] == element:
            return i
    return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;linear_search&lt;/b&gt;&lt;/span&gt; 함수를 보면, &lt;b&gt;element&lt;/b&gt;라는 찾고자하는 값과 &lt;b&gt;some_list&lt;/b&gt;라는 조회할 배열을 인자로 받는다. 그래서 For문을 돌리면서 찾고자 하는 값이 나오면, 그 값을 리턴한다. 찾고자 하는 값이 없으면 None을 리턴한다. 사실 간단한 For 문으로 하나씩 조회하는 로직이 선형 탐색 알고리즘이라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이진 탐색 알고리즘(Binary Search Algorithm)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;중간 지점을 기준으로 반씩 제외하여 확인하는 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692229801721&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def binary_search(element, some_list):
    start_index = 0
    end_index = len(some_list) - 1

    while start_index &amp;lt;= end_index:
        midpoint = (start_index + end_index) // 2
        if some_list[midpoint] == element:
            return midpoint
        elif some_list[midpoint] &amp;gt; element:
            end_index = midpoint - 1
        else:
            start_index = midpoint + 1
    return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; binary_search&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 보면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;element&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;라는 찾고자하는 값과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;some_list&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;라는 조회할 배열을 인자로 받는다.&lt;span&gt; 그리고 이진 탐색 알고리즘은 pointer라는 개념이 등장한다. &lt;b&gt;start_index&lt;/b&gt;가 검색하는 범위의 시작 지점이고, &lt;b&gt;end_index&lt;/b&gt;가 검색 범위의 끝 지점이다. start_index와 end_index의 범위를 줄이면서 원하고자 하는 값을 찾는 것이 바로 이진 탐색 알고리즘이라고 할 수 있다. while 로직을 보면, 구하려는 값과 중간 값을 비교하여 포함되지 않는 쪽을 제외하고 다시 start_index와 end_index를 재설정하여 탐색 범위를 줄인다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;탐색 알고리즘 비교&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;선형 탐색과 이진 탐색의 결과물은 같다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt; &lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;만약에 길이가 16인 list를 찾는다고 해보자. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;선형 탐색 알고리즘 (linear search algorithm)&lt;/b&gt;에서는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가장 금방 되는 경우 1번 (처음 검사해서 찾음)이고,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가장 오래 걸리는 경우 16번이다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;이진 탐색 알고리즘(binary search algorithm)&lt;/b&gt;에서는 &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가장 금방 되는 경우 1번 (처음 검사해서 찾음) &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;가장 오래 걸리는 경우 4번 (리스트에 0이 없을 때 0일 찾는 경우 : (인덱스 기준이고 0은 작은 값이므로 외쪽으로 계속 찾게 됨) 16-8-4-2-1이면 첫 번째 자리와 비교했는데도 0이 아니므로 탐색 종료.)이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 98px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: center;&quot;&gt;리스트 길이&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: center;&quot;&gt;선형 탐색&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: center;&quot;&gt;이진 탐색&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;16&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;최악의 경우 16번 탐색 필요&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;최악의 경우 4번 탐색 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;32&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;최악의 경우 32번 탐색 필요&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;최악의 경우 5번 탐색 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;64&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;최악의 경우 64번 탐색 필요&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;최악의 경우 6번 탐색 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;128&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;최악의 경우 128번 탐색 필요&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center; height: 20px;&quot;&gt;최악의 경우 7번 탐색 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;선형 탐색은 리스트의 길이가 길어져지면 탐색 기간도 길어진다. 위의 표를 보면 선형 탐색을 쓰면 안 될 것 같다. 너무 비효율적으로 보인다. 하지만, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;이진 탐색을 사용하려면 리스트가 정렬이 되어 있는 경우에만 사용가능하다.&lt;/b&gt; &lt;/span&gt;즉, 정렬이 되어 있지 않는 리스트는 이진 탐색을 사용할 수 없고 선형 탐색을 사용하거나, 정렬 후에 이진 탐색을 사용해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형탐색알고리즘(linear search algorithm)은 도서관에서 ㄱ부터 시작하는 도서를 하나씩 확인하면서 내가 찾는 책까지 찾는 방법이다. 이진탐색알고리즘(binary search algorithm)은 가운데 값과 목표 값을 비교하면서 탐색범위를 절반씩 줄이는 방법이다. 이진탐색알고리즘은 정렬을 한 후에 적용할 수 있다. 따라서 이진탐색 알고리즘 사용 전에 정렬 알고리즘도 동시에 적용이 필요하기 때문에, 복잡도를 따져본 후에 선형탐색 / 이진탐색 중에 더 적절한 것을 골라 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/알고리즘 종류</category>
      <category>binary search algorithm</category>
      <category>linear search algorithm</category>
      <category>선형탐색알고리즘</category>
      <category>이진탐색알고리즘</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/38</guid>
      <comments>https://han-py.tistory.com/38#entry38comment</comments>
      <pubDate>Thu, 17 Aug 2023 20:00:01 +0900</pubDate>
    </item>
    <item>
      <title>[선형 자료구조 - Python] 연결 리스트(Linked List)</title>
      <link>https://han-py.tistory.com/575</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Python에서 Linked List를 잘 사용하지 않는 것같다. 왜냐하면 구현이 편한, list나 deque로 어느정도 커버가 가능하기 때문이다. 하지만 반드시 필요한 자료구조이기 때문에, 이번 기회에 이해해 보도록 하자. 일반적인 자료구조에서의 리스트란, 순서를 가진 데이터의 집합을 가리키는 추상 자료형(abstract data type)이다. 자료구조의 관점에서 리스트의 종류로는, &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;순차 리스트&lt;/span&gt;&lt;/b&gt;와 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;연결 리스트&lt;/b&gt;&lt;/span&gt;로 나뉜다. 오늘 다루고자 하는 내용은 리스트의 한 종류인,&amp;nbsp;&lt;b&gt;연결 리스트(Linked List)&lt;/b&gt;에 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #666666; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;순차 리스트: 저장소를 배열형태로 만드는 것. 연속적인 메모리 공간에 저장(python에서 list)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;연결 리스트: 저장 할때 마다 메모리를 확보해서 추가시키는 것. 메모리의 동적할당을 기반으로 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결 리스트는 이름에서 알 수 있듯, 각각의 데이터들이 순서대로 연결되어 있는 리스트 이다. 아래의 그림을 보면, A에서 D 까지 순차적으로 화살표를 가지고 연결되어 있는 것을 알 수 있다.&amp;nbsp; 아래 그램을 보면, 삭제 추가 시 끊어진 부분을 다시 연결만 하면 되는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4okRb/btspxqMPe3D/DAJE5gkYeqUHWK0kxZSAOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4okRb/btspxqMPe3D/DAJE5gkYeqUHWK0kxZSAOK/img.png&quot; data-alt=&quot;hanpy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4okRb/btspxqMPe3D/DAJE5gkYeqUHWK0kxZSAOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4okRb%2FbtspxqMPe3D%2FDAJE5gkYeqUHWK0kxZSAOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4116&quot; height=&quot;1667&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J1w18/btspxrdTWRZ/pQZybkuWibIBEvayWgbHx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J1w18/btspxrdTWRZ/pQZybkuWibIBEvayWgbHx1/img.png&quot; data-alt=&quot;hanpy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J1w18/btspxrdTWRZ/pQZybkuWibIBEvayWgbHx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ1w18%2FbtspxrdTWRZ%2FpQZybkuWibIBEvayWgbHx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4116&quot; height=&quot;1667&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCkW96/btspg1Pbw06/RbTk0H0GqMqkLBararHyEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCkW96/btspg1Pbw06/RbTk0H0GqMqkLBararHyEk/img.png&quot; data-alt=&quot;hanpy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCkW96/btspg1Pbw06/RbTk0H0GqMqkLBararHyEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCkW96%2Fbtspg1Pbw06%2FRbTk0H0GqMqkLBararHyEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4116&quot; height=&quot;1667&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbu3W2/btsph9UctDK/b9jmkdUktNHBf0KMaLT9BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbu3W2/btsph9UctDK/b9jmkdUktNHBf0KMaLT9BK/img.png&quot; data-alt=&quot;hanpy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbu3W2/btsph9UctDK/b9jmkdUktNHBf0KMaLT9BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdbu3W2%2Fbtsph9UctDK%2Fb9jmkdUktNHBf0KMaLT9BK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;290&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beMpHb/btspjXeFFMZ/AdODWLlxCIx75HtuyMqEIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beMpHb/btspjXeFFMZ/AdODWLlxCIx75HtuyMqEIK/img.png&quot; data-alt=&quot;hanpy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beMpHb/btspjXeFFMZ/AdODWLlxCIx75HtuyMqEIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeMpHb%2FbtspjXeFFMZ%2FAdODWLlxCIx75HtuyMqEIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4116&quot; height=&quot;1667&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;4116&quot; data-origin-height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 방향으로 연결되어 있기 때문에, 건너뛰거나 화살표 반대방향으로 자료조회는 불가능하다. (물론 양방향 연결리스트는 가능하지만, 여기서는 단방향만 확인하도록 하자.) 즉, 순차리스트와 다르게 원하는 인덱스 위치를 바로 찾아갈 수 없고 첫 노드부터 화살표를 따라 순차적으로 확인을 하면서 찾아야한다. 기본적인 용어 설명은 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각각의 element(A, B, C 등등)를 node라고 한다.&lt;/li&gt;
&lt;li&gt;가장 앞의 노드의 이름이 head node이다.&lt;/li&gt;
&lt;li&gt;가장 뒤의 노드의 이름은 tail node이다.&lt;/li&gt;
&lt;li&gt;각 노드는 데이터와 뒤쪽 노드를 가리키는 pointer를 가진다.&lt;/li&gt;
&lt;li&gt;특정 노드 기준으로 이전 노드를 predecessor node라 하고 뒤쪽 노드를 successor node라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;연결리스트(Linked List) 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결리스트는 인덱스 위치로 바로 찾아갈 수 없고, 개별적으로 위치하고 있는 원소의 주소를 연결하여 하나의 전체적인 자료구조를 이룬다. 따라서 연결리스트는 링크를 통해 원소에 접근하므로, 순차 리스트에서처럼 물리적인 순서를 맞추기 위한 작업이 필요하다고 할 수 있다. 이러한 연결리스트는 자료구조의 크리를 동적으로 조정 가능하여 메모리를&amp;nbsp; 효율적으로 사용이 가능하다(새로운 데이터 추가 시 메모리 할당하 사용한다). 효율적인 메모리 사용은 무슨 말일까? 우선은 메모리에 대해 좀 더 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;순차 리스트와 연결리스트의 메모리 사용 차이점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬의 모든 자료는 객체로 저장되고, 객체의 주소를 저장할 때 연속적인 메모리 공간에 저장이 된다. 아래의 예를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686067553354&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# id() 함수를 사용하면, 식별가능한 유일한 값이 뜬다.
a = 100
b = 100
print(id(a), id(b))

```
1994862384 1994862384
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드의 결과로 알 수 있는 것은, a와 b가 같은 값을 가리키고 있다는 것이다. 그리고 파이썬 자체는 100이라는 값을 저장할 메모리를 하나만 만든 다는 것을 알 수 있다. 아래는 배열에 들어 있는 메모리 주소 값을 print하는 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686067682510&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;arr = [0]*5
for i in range(5):
    print(id(arr[i]))
    
```
1994859184
1994859184
1994859184
1994859184
1994859184
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드로 알 수 있는 것은 id가 다 같다는 말은 arr 안에 있는 인덱스의 데이터 값들이 모두 같은 값을 가리키고 있다는 것을 알 수 있다. 배열의 값 하나를 다른 값을 넣어서 확인을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686067813027&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;arr = [0]*5
arr[3] = 1
for i in range(5):
    print(id(arr[i]))
    
```
1994859184
1994859184
1994859184
1994859216
1994859184
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 값을 통해 파이썬은 연속적인 메모리공간에 객체를 가리키는 주소만 적혀있다는 것을 알 수 있다. 그렇다면 연속적인 메모리공간의 크기는 어떻게 될까? 아래의 코드를 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686067977805&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
arr = []
size = sys.getsizeof(arr)
print(size)    # 실제 가리키는 객체 없다
for i in range(1000):
    arr.append(None)
    new_size = sys.getsizeof(arr)
    if size != new_size:
        print(size, new_size)
        size = new_size
```
56
56 88
88 120
120 184
184 248
248 312
312 376
376 472
472 568
568 664
664 792
792 920
920 1080
1080 1240
...
...
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 내부에 가리키는 값이 없더라도, 크기가 증가함에 따라 메모리가 커지는 것을 알 수 있다. &lt;b&gt;파이썬에서 위와 같은 불필요한 메모리가 사용되는 경우가 있다면, 배열보다는 연결 리스트를 사용하는 것이 현명하다는 것을 알 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;연결 리스트(Linked list) 기본구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 연결 리스트에 데이터를 저장하기 위해, class로 정의된 Node를 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Node란&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;연결 리스트의 구조는 다음과 같다. 하나의 Node에는 data 필드와 link 필드 를 가진다. &lt;b&gt;data 필드&lt;/b&gt;는 실제 데이터를 저장하는 자료구조로, 저장할 원소의 종류나 크기에 따라 구조를 정의하여 사용한다. &lt;b&gt;link 필드&lt;/b&gt;는 다음 Node의 주소를 저장하는 자료구조이다. 이러한 data 필드와 link 필드는 묶어서 class로 Node를 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; Head란&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫번째 노드의 객체에 해당하는 것을 찾아 갈 수 있도록 주소를 저장할&amp;nbsp;&lt;b&gt;Head&lt;/b&gt;가 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; 연결 구조&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;16진수로 나타내는 값이 주소이다.( ex) 0x1018 )&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;각 노드마다 다음 노드의 주소(link 필드)에 의해 다음 노드와 연결되는 구조를 가진다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;head가 가장 앞의 노드를 가리키고, link 필드가 연속적으로 다음 노드를 가리키다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마지막 노드는 다음으로 오는 노드가 없다고 반드시 나타내야한다. 이러한 의미로, 파이썬에서는&amp;nbsp;None을 마지막에 저장해 둔다. 안 하면 오류가 발생한다. (C 언어 에서는 NULL을 적어준다.)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단일(단순) 연결 리스트 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결리스트를 구현하려면 각 Node에 대한 클래스가 따로 필요하다. Node class 코드를 먼저 확인하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Node class 만들기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686036787603&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Node:
    def __init__(self, d=0, n=None):
        self.data = 0
        self.next = None
        print(d, '생성')

    def __del__(self):
        print(self.data, '삭제')

arr = []
for i in range(5):
    arr.append(Node(i))
print(arr)

```
0 생성
1 생성
2 생성
3 생성
4 생성
[&amp;lt;__main__.Node at 0x7f0db06db370&amp;gt;,
 &amp;lt;__main__.Node at 0x7f0db13e1150&amp;gt;,
 &amp;lt;__main__.Node at 0x7f0db06da500&amp;gt;,
 &amp;lt;__main__.Node at 0x7f0db06da650&amp;gt;,
 &amp;lt;__main__.Node at 0x7f0db06db2b0&amp;gt;]
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;__repr__()&lt;/span&gt; &lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;메소드를 정의하지 않았기 때문에 기본적으로 클래스 이름과 객체 주소를 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node 클래스는 2개의 필드(data, next)로 구성이 된다. data는 data에 대한 참조값을 의미하고, next는 뒤쪽 노드에 대한 참조 데이터를 담고 있다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;__init__&lt;/b&gt;&lt;/span&gt;에서는 data를 받아서 data와 next필드를 추가한다. 전달받은 인수가 없다면 None으로 생성한다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;__del__()&lt;/b&gt; &lt;/span&gt;메서드는 소면자 메서드로 생성자와 반대로 인스턴스를 삭제 할 떄 자동으로 호출된다. 삭제 시 필요한 로직이 있다면 이곳에 적어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;LinkedList 만들기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686095548987&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Node:
    def __init__(self, d=0, n=None): # 생성자
        self.data = d        # 정수 값 저장
        self.next = n        # 다음 노드 주소

class LinkedList:
    def __init__(self):
        self.head = None # 첫 번째 노드
        self.size = 0     # 노드의 수

my_list = LinkedList()

def printList(lst): # lst는 LinkedList 객체 주소를 받는거다.
    cur = lst.head
    print('cur.data : ', cur.data)

    while cur is not None:
        print('data : ', cur.data)
        print('next : ', cur.next)
        cur = cur.next

n5 = Node(5);
n4 = Node(4, n5)  # 노드는 4번이고 n5를 가르켜라.
n3 = Node(3, n4)
n2 = Node(2, n3)
n1 = Node(1, n2)
my_list.head = n1    # mylist의 head가 n1이라고 지정
my_list.size = 5

print(my_list) # &amp;lt;__main__.LinkedList object at 0x7f0db06d97e0&amp;gt;
printList(my_list)
```
cur.data :  1
data :  1
next :  &amp;lt;__main__.Node object at 0x7f0db071cd30&amp;gt;
data :  2
next :  &amp;lt;__main__.Node object at 0x7f0db071f0d0&amp;gt;
data :  3
next :  &amp;lt;__main__.Node object at 0x7f0db071cca0&amp;gt;
data :  4
next :  &amp;lt;__main__.Node object at 0x7f0db071eb60&amp;gt;
data :  5
next :  None
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 살펴보면, Node들이 n1 &amp;gt; n2 &amp;gt; n3 &amp;gt; n4 &amp;gt; n5로 연결되어 있는 것을 알 수 있다. 구현을 해보면 확실히 단점은 순차 접근을 해야하기 때문에 마지막 노드까지 가려면, 처음부터 하나씩 접근을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LinkedList CRUD 만들기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686096724072&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Node:
    def __init__(self, d=0, n=None): # 생성자
        self.data = d        # 정수 값 저장
        self.next = n        # 다음 노드 주소

class LinkedList:
    def __init__(self):
        self.head = None # 첫 번째 노드
        self.tail = None # 마지막 노드
        self.size = 0     # 노드의 수를 알 수 있도록 적어둠

mylist = LinkedList()

def printList(lst): # lst는 LinkedList의 출발 객체 주소를 받는거다.
    if lst.head is None:    # 빈리스트인 경우 주의
        return        #빈리스트인지 체크
    cur = lst.head    #첫번째 노드 복사

    while cur is not None:
        print(cur.data)
        cur = cur.next  # 모든 노드를 순서대로 가져올 수 있다

def insertLast(lst, new): # new는 새로 추가할 노드 객체
    # 빈리스트 처리
    if lst.head is None:
        lst.head = lst.tail = new # 비어있으니 처음과 끝이 같다
    else:
        # 빈리스트 아니면 마지막 노드를 찾으면 된다.
        lst.tail.next = new
        lst.tail = new    # 새로 추가되는걸 마지막으로 함.
    lst.size += 1

def deleteLast(lst):
    if lst.head is None:
        return
    pre, cur = None, lst.head
    while cur.next is not None:
        pre = cur    # pre가 cur를 따라간다.
        cur = cur.next
    if pre is None: # 노드가 하나인 경우
        lst.head = lst.tail = None
    else: # 노드가 여러개인 경우
        pre.next = None
        lst.tail = pre
    lst.size -= 1


def insertFirst(lst, new): # new의 next을 head가 가리키는걸로 넣고 head를 new로 가리키게 하면된다.
    if lst.head is None:
        lst.head = lst.tail = new
    else:
        new.next = lst.head
        lst.head = new
    lst.size += 1

def deleteFirst(lst): # 노드가 한개일 경우를 주의하자
    if lst.head is None:
        return
    lst.head = lst.head.next
    if lst.head is None: # 한개인 경우
        lst.tail = None

def insertAt(lst, idx, new):
    # 빈리스트일 경우, idx == 0
    if lst.head is None or idx == 0:
        insertFirst(lst, new)
    # 마지막 추가하는 경우 idx &amp;gt;= lst.size
    elif idx &amp;gt;= lst.size:
        insertLast(lst, new)
    # 중간에 추가하는 경우(head, tail이 바뀔리 없다.)
    else:
        pre, cur = None, lst.head
        for _ in range(idx):
            pre = cur
            cur = cur.next

        new.next = cur
        pre.next = new
        lst.size += 1

#def deleteAt(lst, idx): # 빈리스트 처리해. 그리고 처음, 중간 마지막 나눠서 구분해서 구현하자.        

for i in range(5):
    insertLast(mylist, Node(i))
    printList(mylist)

while mylist.size:    
    # 마지막 노드 삭제 하려면 마지막 앞에 노드를 None으로 바꾸고 tail도 마지막 앞으로 옮겨야한다.
    # 그런데 단일 연결 리스트는 마지막 노드를 안다고 마지막 앞의 노드를 알 수 없다. 그래서 처음부터 다시 찾아야한다. 이런경우는 이중연결리스트 쓰는게 좋다.찾기전에 빈리스트 체크 부터 당연히 해야한다.
    deleteLast(mylist)
    printList(mylist)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 하나씩 보면 크게 어려운 부분은 없다. 주의할 점으로는 마지막 노드 삭제 시, 마지막 앞의 노드를 None로 변경하고 tail도 마지막 앞으로 옮겨준다. 이러한 구조기 때문에 마지막 노드를 알고 있다고 해도, 마지막 앞의 노드를 알 수 없다. 따라서 검색 시 처음부터 다시 찾아야 한다. 이러한 검색이 자주 일어나야 한다면, 이중 연결 리스트를 사용하는 것이 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이중 연결 리스트의 구현법도 사실 간단하다. 우리는 위의 단일 연결리스트에서는 Node에&amp;nbsp; next로 다음 Node값을 넣어준다. 이중 연결 리스트에서는 next에서 추가로 pre 까지 넣어서 이전 Node의 정보도 넣어주기만 하면 된다. 한번 구현해 보면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/알고리즘 종류</category>
      <category>Linked list</category>
      <category>Python</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/575</guid>
      <comments>https://han-py.tistory.com/575#entry575comment</comments>
      <pubDate>Mon, 31 Jul 2023 21:00:30 +0900</pubDate>
    </item>
    <item>
      <title>브라우저 렌더링</title>
      <link>https://han-py.tistory.com/578</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;앞에서 우리는 브라우저에 대한 기본적인 지식을 알아보았다. 이번엔 브라우저가 어떠한 방식으로 렌더링을 하는지 알아보자. 사실 초창기 브라우저에서는 기본적인 마크업 문법을 지키지 않으면, HTML을 파싱 하지 못하였다. 예를 들면, &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;body&lt;/span&gt;&lt;/b&gt; 태그를 안적거나 닫는 태그를 넣지 않으면 파싱자체가 안되었다. 그러나 현재 브라우저(모던 브라우저) 자체의 성능이 좋아지고, 예외처리도 잘 되어 있어서 많은 기초지식이 없더라도 웹 페이지와 application 동작에는 무리가 없이 실행이 된다. 그럼에도 우리는 서비스 사용자가 웹 페이지를 이탈하지 않고 조금 더 좋은 UX를 보여주기 위해서는, &lt;b&gt;브라우저 렌더링에 대한 지식을 정확히 적용&lt;/b&gt;하여 응답 속도를 높이는 과정이 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;응답 속도를 높이는 방법에는 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Infra / backend / frontend&lt;/span&gt;&lt;/b&gt;와 같이 전체 영역에서 작업이 필요하지만, 사실상 가장 큰 영향을 미치는 부분은 사용자와 가장 가까이서 상호작용을 하는 브라우저라고 할 수 있겠다. 브라우저 응답속도를 높이기 위해, 렌더링에 대해 알아보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Critical Rendering Path&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;우선 Critical Rendering Path (CRP) 에 대해 알아보자. Critical Rendering Path이란 &lt;b&gt;웹 브라우저 렌더링 과정&lt;/b&gt;을 의미 한다. 아래의 그림이 CRP의 과정이라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2ED5y/btsnF7Cm0be/scDScHWBbzzjg7KSB7ZZG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2ED5y/btsnF7Cm0be/scDScHWBbzzjg7KSB7ZZG1/img.png&quot; data-alt=&quot;hanpy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2ED5y/btsnF7Cm0be/scDScHWBbzzjg7KSB7ZZG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2ED5y%2FbtsnF7Cm0be%2FscDScHWBbzzjg7KSB7ZZG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;182&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;우리가 기본적으로 HTML, CSS를 서버에서 다운로드하고, &lt;b&gt;DOM과 CSSOM과 같은 Object Model&lt;/b&gt;로 변환한다. 각각의 HTML은 DOM 트리를 빌드하고, CSS는 CSSOM 트리를 빌드한다. 그 후에 &lt;b&gt;DOM&lt;/b&gt;과 &lt;b&gt;CSSOM&lt;/b&gt;를 결합하여 &lt;b&gt;Render Tree&lt;/b&gt;를 형성한다. 그 후 Render Tree에서 &lt;b&gt;Layout&lt;/b&gt;을 실행하여 각 노드의 기하학적 모향을 게산을 한다. 각각의 노드를 화면에 &lt;b&gt;Paint&lt;/b&gt; 하므로 브라우저 렌더가 완료 된다. 우리가 여기서 중점을 둬야 할 부분은 &lt;b&gt;Critical Rendering Path를 최적화&lt;/b&gt;하여 각 단계를 줄인다면, 초기 화면뿐만 아니라, 화면 업데이트 시간도 줄일 수 있다. 이러한 관점을 유지하면서 각각의 프로세스에 대해 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;우선 브라우저를 열어서 주소를 입력한다고 가능을 해보자. 주소 입력 시 DNS 서버로 가서 데이터를 요청 할 주소(ip)를 찾는다. 그리고 ip 주소로 웹 페이지를 표시하기 위한 자료를 요청한다. 브라우저는 웹 사이트에서 받은 정보를 조립하여 유저에게 보여주는 것이다. 서버로 요청(Request)과 응답(Response)을 받는데, 받은 응답정보(Bytes)를 통해 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;DOM&lt;/span&gt;과 &lt;span style=&quot;color: #006dd7;&quot;&gt;CSSOM&lt;/span&gt;&lt;/b&gt;을 그리는 부분이 시작된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;DOM (Document Object Model)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;브라우저는 수신받은 HTML을 통해 &lt;b&gt;Bytes &amp;gt; Characters &amp;gt; Tokens &amp;gt; Nodes &amp;gt; DOM&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #414141; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;과정을 거처 DOM Tree를 만든다. 우선은 브라우저가 원시 Bytes를 네트워크에서 읽어와서 지정된 인코딩(UTF-8)에 따라 개별 문자로 변환을 한다. 그리고 변환된 문자열을 W3C HTML5 포준에 지정된 고유 token(&amp;lt;html&amp;gt;, &amp;lt;body&amp;gt;와 같은 태그들)으로 변환을 한다. 이때 변환 된 token을 Nodes로 변환하는데, 변환된 Node는 각각의 토큰의 속성과 규칙이 포함되어 있는 객체라고 할 수 있다. 마지막으로 각각의 Node들을 DOM으로 변경된다. 이때, 생성된 각체들은 트리 데이터 구조에서 연결되고 태그들은 상하관계를 가지게 된다.(HTML 객체는 body 객체의 상위라는 것은 이 단계에서 적용이 된다고 할 수 있다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BNilY/btsnEWVWwbV/Aj52IXo0aa6UJ6uKzmBaL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BNilY/btsnEWVWwbV/Aj52IXo0aa6UJ6uKzmBaL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BNilY/btsnEWVWwbV/Aj52IXo0aa6UJ6uKzmBaL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBNilY%2FbtsnEWVWwbV%2FAj52IXo0aa6UJ6uKzmBaL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;564&quot; height=&quot;403&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;CSSOM (CSS Object Model)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;CSSOM은 JavaScript에서 CSS를 조작할 수 있는 객체라고 할 수 있다. DOM에서는 HTML을 Node로 변경한다면, CSSOM은 HTML이 아닌 CSS를 Node로 변경해서 동적으로 읽고 수정할 수 있다고 할 수 있다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;CSS를 브라우저에서 이해할 수 있도록 DOM에서 진행한 방식과 동일하게 &lt;b&gt;Bytes &amp;gt; Characters &amp;gt; Tokens &amp;gt; Nodes &amp;gt; CSSOM&lt;/b&gt; 순서로 적용이 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;DOM 트리는 순차적으로 파싱을 한다. 그렇다면, CSSOM도 동일할까? &lt;b&gt;기본적으로 CSS는 HTML의 가장 윗부분에 위치해야한다.&lt;/b&gt; (&amp;lt;head&amp;gt; 아랫부분) 왜냐하면 Render tree를 구성 시 CSSOM 트리는 CSS를 모두 해석이 완료된 후에 CSSOM 트리가 생성되기 때문에, CSSOM이 구성되지 못하면, Render 트리를 만들 수 없고 렌더링이 차단이 된다. 이러한 이유 때문에 CSS는 &lt;b&gt;렌더링 차단 리소스&lt;/b&gt;로 불리기도 한다. DOM 트리 파싱 시 렌더링이 차단되지 않도록, CSS는 상단에 배치하도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;JavaScript&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;DOM과 CSSOM을 공부할 때, 반드시 나오는 개념이 바로 JavaScript이다. DOM과 CSSOM을 동적으로 변화시키는 존재 JavaScript이다. HTML에서 순차적으로 DOM이 생성이 되다가 &amp;lt;script&amp;gt; 태그를 만나면, script을 다운로드하고 실행이 완료될 때까지 DOM 트리 생성은 중단된다. 따라서 JavaScript도 렌더링 차단 리소스라 불린다. 따라서 가능하면 HTML 문서 최하단 &amp;lt;/body&amp;gt; 앞에 두는 것이 좋다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;물론, HTML 파싱 시, script를 만나더라도 파싱을 멈추지 않게 할 수 있다. script 태그에 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;defer나&lt;/span&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;async&lt;/span&gt;&lt;/b&gt;를 추가하면 DOM / CSSOM을 변경하지 않겠다는 의미로 HTML 파싱을 멈추지 않는다. 관련 내용은 &lt;a href=&quot;https://han-py.tistory.com/542&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기를&lt;/a&gt; 참고하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;JavaScript를 간단히 다루고 있지만, 사실 싱글 스레드인 JavaScript의 실행 시간은 렌더링 시간에 직결된다. 또한 JavaScript에 의한 DOM / CSSOM 변경도 렌더링 속도에 관련이 된다고 할 수 있다. 아래의 내용부터는 렌더링 성능 최적화에 관련된 내용도 조금씩 넣어보려 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Render Tree&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;앞에서 우리는 DOM과 CSSOM을 만들었다. 이때, DOM은 콘텐츠를 포함하는 객체이고, CSSOM은 스타일 규칙을 설명하고 있는 독립적인 객체이다. 이러한 객체를 가지고 화면에 pixel을 찍어 보여주기 위해서는 두 객체를 합치는 과정이 필요하다. 이러한 각각의 객체를 합치는 과정으로 DOM과 CSSOM을 합쳐서 Render Tree를 만든다. 세부 과정은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;DOM 트리의 Node을 상위 root를 기준으로 하나씩 변환을 진행한다. 이때, &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;script 태그, meta 태그, display none&lt;/span&gt;&lt;/b&gt;과 같은 속성을 가진 요소들은 rendering 출력에 반영되지 않고, Render Tree에서 생략이 된다. 변환된 Node들에 일치하는 CSSOM을 찾아 적용을 진행하여 Render Tree를 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Render Tree에는 스타일 정보가 포함되어 있고, 실제 화면에 표현되는 노드 들로만 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;기본적으로 display:none 과 visibility: hidden은 다르다고 할 수 있다. 둘다 눈으로 보여지지 않는 것은 맞다. 하지만, display: none은 element 자체를 제거하여, 차지하고 있는 공간 자체를 제거한다. 그러나 visibility: hidden은 공간은 유지한체로 화면에서만 보이지 않는다. 이때, display:none을 사용하면 공간 자체를 지우기 때문에 layout을 다시 그리게 된다. 만약 다시 보여지게 해야한다면, display 보다는 visibility를 사용하는 것이 더 좋다고 할 수 있다.&lt;br /&gt;&lt;br /&gt;정리하면, display: none 은 DOM 조작 및 스타일 변경이 있더라도, 레이아웃과 리페인트가 발생하지 않는다. visibility: hidden은 보이지 않고 공간만 차지하고 있기 때문에, 리페인트는 발생하지 않고 레이아웃만 발생한다. 만약 &lt;b&gt;변경이 많이 일어나는 변경을 한다면, display:none을 한 상태에서 변경 후에 다시 보여지게 한다면, 레이아웃 발생을 최대한 줄이는 것이 가능&lt;/b&gt;하다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Render Tree가 생성되면 Layout 단계를 진행한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Layout (=Reflow)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Render Tree에는 DOM에서 변환된 Node와 CSSOM에서 나온 Node 스타일만 만들어져 있다고 할 수 있다. Layout에서는 실제로 화면에 표시될 노드의 정확한 위치와 크기를 계산한다. 정확한 위치와 크기란 픽셀단위로 표현된다고 이야기할 수 있다. 즉, % 나 vw로 지정했던 모든 값들이 픽셀값으로 나타난다고 할 수 있다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;이때, RenderTree를 탐색하여 각 노드의 Box Type을 확인한다. 그리고 type이 inline / block에 따라 위치를 계삭하여 노드를 위치시킨다.&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Layout 단계를 거치면 각 노드는 정확한 위치를 얻게 되는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;화면에 표시되는 브라우저의 영역과 크기를 &lt;b&gt;Viewport(뷰포트)&lt;/b&gt;라고 한다. 뷰포트는 모바일의 경우 디스플레이의 크기이고, PC의 경우는 브라우저 차으이 크기라고 할 수 있다. 대부분 화면을 그릴 때, 요소의 크기와 위치는 %, vw, vh로 적용되어 있기 때문에, Viewport가 달라진다면, px 계산을 다시 해야 하므로 Layout을 다시 그린다고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Layout은 변경된 위치를 계산하한다. 즉 JavaScript 실행 시 Layout이 계산을 하는 시간을 적게 하는 것이 필요하다. 기본적으로 JavaScript로 DOM의 위치나 스타일을 변경하면, Layout이 변경되고, paint를 다시 진행한다. 이때, Layout을 변경하지 않고 Paint만 다시 할 수 있도록 해서 성능을 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 주의해야 할 점은 Forced Synchronous Layout(강제 동기 레이아웃)을 최소화하거나, 관련 로직을 for문을 돌면서 하나씩 하는 것보다는 변수로 전부 참조한 이후에 한꺼번에 변경하는 것이 더 좋다. 만약 페이지 슬라이더를 만들기 위해 &lt;span style=&quot;color: #006dd7;&quot;&gt;const slideWidth =  tableWrapper.&lt;b&gt;clientWidth&lt;/b&gt;;&lt;/span&gt; 를 사용한다고 해보자. 여기서 사용한 clientWidth / offsetHeight 등등을 사용할 때, DOM 조작이 없는데도 레이아웃을 강제로 발생시킨다. 즉, javascript를 통해 기하학적인 수치를 알아낼 때는 Forced Synchronous Layout(강제 동기 레이아웃)이 발생하므로, 사용할 때는 잠시 고민을 하는 것이 좋다. 이러한 로직을 for 문 안에 넣어서 반복적으로 사용하는 하면, 레이아웃이 반복적으로 발생하여 성능이 안 좋아진다. 이를 어려운 말로 Layout Thrashing이라고 하는데, 사실 클린 코드를 작성하면 크게 문제 될 것 없다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 넓은 범위, 즉 부모 element를 수정을 하면 내부의 element들이 모두 Layout이 발생할 수 있다. 따라서 가능하면 작은 요소들을 변경하는 것이 좋고, 변경이 많이 필요하다면 따로 Layout을 만들면 주변 Layout에 영향을 미치지 않는다. 따로 Layout을 만드는 방법은 absolute나 fixed로 position을 설정해 주면 된다. 또한 Layout 변경을 최소화하기 위해서는, position:absolute;에서 애니메이션을 줄 때 top/bottom/left/right를 변화하는 것보다는 transform을 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Paint(=Resterizing)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Layout 을 통해 화면에 표현하기 위한 계산이 끝나면, UI를 화면에 표현하기 위한 Paint 과정이 이루어진다. Paint는 ㄱRender Tree의 내용을 화면의 픽셀로 변환하는 프로세스로, 텍스트 /color / image/ border 등등이 모든 시간적인 부분을 그린다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정리하면, DOM과 CSSOM 트리를 결합하여 Render tree를 생성한다. 생성된 Render tree는 페이지 렌더에 필요한 node만 가지고 있다. Layout에서는 각각의 정확한 객체의 위치와 사이즈를 계산한다. 그리고 마지막 paint 단계에서는 스크린의 픽셀에 렌더링 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Composite&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Paint 단계가 완료되면, 생성된 레이어를 합성한다. 적절한 위치에 조합하여 스크링에 이미지를 만들어 내는 작업이라고 할 수 있다. 이 단계가 끝나면 웹 페이지를 볼 수 있다. 사실 Composite는 일반적으로 매우 빠르게 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;279b&quot; style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지 기본적인 브라우저 렌더링에 대해 전반적으로 알아 보았다. 다음 글은, 각각의 세부내용도 조금 더 적어보도록 하겠다.&lt;/p&gt;</description>
      <category>Web</category>
      <category>critical rendering path</category>
      <category>cssom</category>
      <category>DOM</category>
      <category>layout</category>
      <category>paint</category>
      <category>Render Tree</category>
      <category>브라우저 렌더링</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/578</guid>
      <comments>https://han-py.tistory.com/578#entry578comment</comments>
      <pubDate>Tue, 25 Jul 2023 21:00:44 +0900</pubDate>
    </item>
    <item>
      <title>브라우저(web browser)의 기본 정리</title>
      <link>https://han-py.tistory.com/577</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 브라우저를 통해 정보전달이나 데이터를 저장한다. 현재 이 글을 쓰고 있는 곳도 브라우저이다. 브라우저에 대한 개념을 찾고 있는 당신 또한 사실상 웹 개발을 하고 있을 가능성이 높다. 브라우저에 대해 잘 알 수록 웹 애플리케이션을 효율적으로 구축하고 UX를 최대화할 수 있다. 인터넷을 사용하거나 개발을 할 때, 브라우저에 대해 고민을 해본 적은 사실 많이 없다고 생각한다. 브라우저에 조금 더 친숙해지는 시간을 가져보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blPxz9/btsmkMT2mRA/9Do6jQ0uYAHhRwK2xgp51k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blPxz9/btsmkMT2mRA/9Do6jQ0uYAHhRwK2xgp51k/img.png&quot; data-origin-width=&quot;155&quot; data-origin-height=&quot;168&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.7005%; margin-right: 10px;&quot; data-widthpercent=&quot;52.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blPxz9/btsmkMT2mRA/9Do6jQ0uYAHhRwK2xgp51k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblPxz9%2FbtsmkMT2mRA%2F9Do6jQ0uYAHhRwK2xgp51k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;155&quot; height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvv76j/btsmkdkjShX/VLoC8D7w0AOXXcACE0kzl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvv76j/btsmkdkjShX/VLoC8D7w0AOXXcACE0kzl0/img.png&quot; data-origin-width=&quot;143&quot; data-origin-height=&quot;170&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.1367%;&quot; data-widthpercent=&quot;47.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvv76j/btsmkdkjShX/VLoC8D7w0AOXXcACE0kzl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcvv76j%2FbtsmkdkjShX%2FVLoC8D7w0AOXXcACE0kzl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;143&quot; height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;브라우저를&amp;nbsp; 공부하는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 HTML 문서를 해석하고, CSS를 적용하며, JavaScript를 실행한다. 각 요소의 동작 원리를 이해하면 웹 페이지의 구조와 스타일링, 상호작용을 제어하는 방법을 알 수 있고 브라우저의 동작 방식을 이해하면 웹 페이지의 오작동을 디버깅하고 문제를 해결하는 데 도움이 된다. 사용자는 보통 chrome이나 Safri를 만이 사용한다. 각각은 브라우저는 다른 동작 방식을 가진다. 하지만 우리가 브라우저에 대한 공부를 통해 각 차이점을 이해하고, 브라우저의 특성에 맞게 웹 애플리케이션 개발이 가능하다. 이러한 각각의 브라우저의 이해를 통해 렌더링 엔진의 동작 방법을 이해하고, DOM 조작이나 css, javascript 파일 로딩을 최적화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;브라우저의 특징&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저의 기능은 여러 가지가 있다. 하지만 주요 특징들은 아래와 같다고 할 수 있다. 개발자의 기준으로 아래의 브라우저의 동작 방식과 기능을 이해하면 웹 페이지의 동작 원리를 파악하고 최적화된 코드를 작성할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DOM(Document Object Model)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM은 HTML 문서의 객체 표현 방식으로 웹 페이지의 구조와 콘텐츠에 대한 인터페이스를 제공한다. 우리 개발자는 DOM을 통해 HTML 요소를 선택하고 조작하면서 동적인 웹 페이지 구축이 가능하다고 할 수 있다. 따라서 브라우저가 HTML 문서를 해석하고 구성되는 방식을 이해하기 위해서는 DOM에 대한 이해가 중요하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CSSOM(CSS Object Model)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSSOM은 CSS 스타일 시트의 객체 표현 방식이다. DOM과 유사하게, CSSOM은 CSS 규칙과 스타일을 조작할 수 있는 인터페이스를 제공한다. 개발자는 CSSOM을 사용하여 애니메이션을 넣거나, 웹페이지의 스타일을 동적으로 조작을 할 수 있다. CSSOM을 통해 상속이나 스타일 규칙의 적용 순서에 대한 이해도를 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Javascript 엔진&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 사용하는 브라우저는 기본적으로 JavaScript 코드를 실행하는 JavaScript 엔진을 내장하고 있다. 대표적인 JavaScript 엔진으로는 &lt;b&gt;Chromium의 V8, Mozilla의 SpiderMonkey, Microsoft의 Chakra&lt;/b&gt; 등이 있다. 개발자는 JavaScript를 사용하여 웹 페이지의 동적인 기능을 구현할 수 있으며, JavaScript 엔진의 동작 방식 이해를 통해, 코드의 실행 성능과 최적화에 대한 이해도를 높일 수 있다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;네트워크 통신&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 브라우저를 연결하기 위해 인터넷을 기본적으로 연결을 한다.&amp;nbsp; 즉, 웹은 서버와의 통신이 필요하다. 그리고 통신을 브라우저가 하기 위해서는 네트워크에 요청을 보내고 응답을 받아오는 기능이 필수적으로 필요하다. 따라서 데이터를 네트워크에서 가져오거나 서버와 상호작용을 위해서는 네트워크 통신에 대해 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가&amp;nbsp; 주소창에 URL나 검색어를 치고, 데이터 Fetching 하는 과정을 간단히 살펴보자. URL을 파싱(domain, params 등)하고 http나 https를 반환한다. NON-ASCII 코드가 필요하다면 URL의 한국어나 특수문자를 변경하여 인코딩한다. 이때 브라우저는 HSTS(HTTP Strict Transport Security) 목록을 체크하고, 검색한 URL이 목록에 있다면 https 프로토콜을 추가하고, 그렇지 않다면 http를 붙인다. 그 후에 DNS(도메인과 연결된 IP 주소를 알려주는 것) 룩업을 시작한다. 브라우저 자체에 DNS 캐시가 있다면, 캐시에서 IP 주소를 찾는다. 없는 경우라면 운영체제 cache에서 찾고, 여기도 없다면 로컬 라우터나 ISP(Internet Service Procider)에 있는 DNS 서버에 요청하여&amp;nbsp; IP 주소를 찾는다. 53 포트를 열어 UDP(User Datagram Protocol) 프로토콜을 통해 DNS 서버와 통신을 시작하게 된다. 통신이 된다면, 서버의 IP 주소로 http(80 포트)/https(443 포트) 요청을 시작하고 TCP 소켓 연결을 요청한다. TCP 해더를 채우기 위해서는 네트워크 계층에 요청을 우선 한다. 그리고 IP 헤더를 채우기 위해 전송 계층에 요청을 하고, 데이터 링크 계층에 요청을 해서 이더넷 프레임 헤더를 채운다. 그리고 네트워크의 데이터 패킷이 디지털에서 전기 신호로 변환되고, 서버와 클라이언트 간의 3way 핸드셰이크 후에 데이터를 클라이언트로 보낸다. 전송 계층에서 마지막으로 핸드 세이크가 일어나면 브라우저는 패킷에서 데이터를 얻게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;보안 및 개인 정보 보호&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자와 상호작용하는 브라우저는 사용자의 개인 정보를 다룰 수밖에 없다. 따라서 개발자는 보안 취약점을 이해하고 웹 애플리케이션에서 사용자의 개인 정보를 안전하게 처리하는 방법을 알아야 한다. 또한, 브라우저가 제공하는 보안 메커니즘을 활용이해하고 활용하여, 사용자를 보호할 수 있는 웹을 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #343a40; text-align: start;&quot;&gt;Browser&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 사용자에게 화면을 보여주는 것이 궁극정인 목적이라고도 할 수 있다. 이러한 과정을 렌더링 과정이라 할 수 있다. 렌더링이란 HTML, CSS, Javascript 등의 웹 콘텐츠를 해석하고 화면에 그리는 것을 말한다. 브라우저는&amp;nbsp; 크게 &lt;span style=&quot;background-color: #ffffff; color: #343a40; text-align: start;&quot;&gt;Mozilla의 Gecko, Apple의 WebKit&lt;/span&gt;&lt;span style=&quot;color: #343a40; text-align: start;&quot;&gt; , Chromium Blink 세 가지로 나뉜다. 이러한 브라우저들을 구성하는 요수 중에 사실상 중요한 것은 엔진이라 할 수 있겠다. 간단한 개념과 엔진들의 종류를 알아보자. 우선은 엔진의 종류로는 브라우저 엔진과 렌더링 엔진으로 나뉜다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #343a40; text-align: start;&quot;&gt;Browser Engine&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #343a40; text-align: start;&quot;&gt;사용자의 이벤트부터(인터페이스), 화면을 그래픽으로 보여주기까지의 과정을 관여한다. 웹이 점점 발전함에 따라 엔진은 고도화가 되었고, 렌더링 엔진과 JavaScript 구문을 분석하는 런타임 JavaScript 엔진이 별도 분리 되었다고 볼 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #343a40; text-align: start;&quot;&gt; Rendering Engine&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #343a40; text-align: start;&quot;&gt;사용자가 요청한 HTML문서와 CSS를 파싱 하여 화면에 그리는 역할을 수행한다. 사실 Rendering Engine은 넓은 의미로 브라우저 엔진으로 불리기도 한다. 왜냐하면 JavaScript 엔진 자체가 Nodejs로 독립적으로 실행할 수 있지만, Rendering Engine은 브라우저에 포함되어 있어서 독립적 실행이 불가능하기 때문이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #343a40; text-align: start;&quot;&gt;Apple의 Webkit&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #343a40; text-align: start;&quot;&gt;WebKit은 Apple이 개발한 오픈소스 웹 렌더링 엔진으로 HTML, CSS, JavaScript를 해석하는 역할을 한다. Apple에서 사용 중인 iOS, macOS, Safari 브라우저와 Apple 제품에서 주로 사용 중이다. 기본적으로 웹 표준을 준수하여 W3C에서 정의 한&amp;nbsp;표준안을 따른다. 또한 오픈 소스로 공개되어 있어 모든 개발자들이 코드를 확인하거나 기여를 할 수 있다. WebKit은 Nitro라는 고성능 JavaScript 엔진을 내장하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Chromium Blink&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google이 개발한 웹 렌더링 엔진으로, Chromium 프로젝트와 Google Chrome 브라우저에서 사용되고 있다. Blink는 웹 페이지의 HTML, CSS, JavaScript 등의 콘텐츠를 해석하고 시각적으로 표현하는 역할을 한다. Blink는 기존에 사용되던 WebKit 렌더링 엔진에서 분기된 것으로, Chromium 프로젝트의 요구사항과 목표에 맞게 개발되었다고 할 수 있다. Blink는 Chromium의 핵심 구성 요소로, 사용자에게 &lt;b&gt;빠른 속도와 탁월한 사용자 경험을 제공하기 위해&lt;/b&gt; 지속적으로 개선되고 최적화 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;Blink &lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;특징과&lt;span&gt; &lt;/span&gt;기능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 브라우저와 마찬가지로 Blink는 웹 표준을 준수하여 개발되었다. W3C(World Wide Web Consortium)에서 정의된 표준 권고안을 따르며, HTML/CSS/JAVASCRIPT 등의 최신 웹 기술을 지원한다. Blink은 Chromium의 다중 프로세스 아키텍처를 기반으로 동작한다. 그래서 각 페이지 탭이나 설치하여 사용되는 확장 프로세스는  별도의 프로세스에서 동작한다. 이를 통해 안정성과 보안 향상뿐만 아니라 여러 작업을 동시에 처리하고 빠른 렌더링 속도를 실형 할 수 있다고 할 수 있다. 렌더링을 조금 더 빠르게 하기 위해 Blink는 증분 렌더링(incremental rendering) 기법을 적용한다. 증분 렌더링이란, 페이지의 일부분이 변경되었을 때, 전체 페이지를 다시 렌더링 하는 것이 아니라, 변경된 부분만 업데이트하는 것을 말한다. 이를 통해 페이지의 로딩 속도와 반응성을 향상할 수 있다. 마지막으로 다양한 플랫폼과 장치에서 동작할 수 있도록 설계되어 PC, 모바일, 태블릿 등 다양한 환경경에서 일관된 사용자 경험을 제공한다. 따라서 개발자는 쉽게 Blink를 기반으로 프로덕트를 다양한 플랫폼에 쉽게 배포할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용에서 우리는 몇가지 키워드 중심으로 브라우저에 대해 살펴보았다. 각각의 키워드별로 살을 붙이는 것을 바로 이 글을 읽는 독자의 몫이다.&lt;/p&gt;</description>
      <category>Web</category>
      <category>브라우저 특징</category>
      <category>브라우조 기초 개념</category>
      <category>사파리</category>
      <category>크롬</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/577</guid>
      <comments>https://han-py.tistory.com/577#entry577comment</comments>
      <pubDate>Wed, 5 Jul 2023 20:00:11 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조 - Python] 큐(Queue) 기초부터 심화까지</title>
      <link>https://han-py.tistory.com/574</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Queue는 컴퓨터 과학에서 중요한 선입선출 자료구조 중 하나이다. 사실 큐의 일상 예시로 설명해 보면, 줄을 서는 개념과 비슷하다. 맛있는 마카롱 집을 가기 위해 줄을 선다고 해보자. 이 경우 먼저 줄을 선 사람이 먼저 마카롱을 사고, 나중에 온 사람이 나중에 마카롱을 사는 것과 같은 원리라고 할 수 있다. 이러한 Quere를 간단한 개념부터 시작해서, 선형 큐(Linear Queue) 원형 큐(Circlar Queue) 등등의 여러 방법으로 구현해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;큐(Queue)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐는 선입선출(FIFO : &lt;span style=&quot;background-color: #f7f7f8; color: #374151; text-align: start;&quot;&gt;First-In-First-Out&lt;/span&gt;) 구조의 자료구조로 먼저 넣은 데이터를 먼저 꺼낸다. 이때, 먼저 데이터를 추가하는 작업을 인큐(enqueue)라고 하고, 데이터를 꺼내는 작업을 디큐(dequeue)라고 한다. 큐는 기본적으로 배열(Array)나 연결 리스트(Linked List)로 구현하는 것이 가능하다. 배열을 이용하여 구현한다면, 간단히 구현이 가능하지만 크기조정이 필요할 수가 있다. 그러나 연결 리스트를 사용하여 구현을 한다면 크기에 제약이 없고 동적으로 크기를 조정할 수 있어 편리하다. 이것은 일반적인 자료구조에서의 이야기이고 파이썬에서는 배열을 통해 Queue를 구현하면 굉장히 비효율적이다. 왜냐하면 배열(List)로 구현한 Queue는 dequeue 시 리스트의 앞쪽을 빼게 된다. 이러한 경우 리스트 내부의 전체 index 값이 한 칸씩 줄어들게 된다. 즉, 전체 메모리 위치가 변경시켜야 되기 때문에 느리다. 물론 Stack의 경우는 리스트의 오른쪽 부분의 데이터를 뺀다 각 데이터의 index값에는 별 문제가 없다. 정리하면, stack의 경우는 리스트로 구현을 해도 성능에 큰 차이가 없지만, 큐의 경우는 리스트로 구현 시 치명적인 속도문제가 발생하여 느려진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 그림은 이해도를 높이기 위해 Queue에 enqueue와 dequeue를 그림으로 표현한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9SmC7/btslaRPQ5en/2P9SGtUB4mDs8CtLw8ogm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9SmC7/btslaRPQ5en/2P9SGtUB4mDs8CtLw8ogm1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2084&quot; data-origin-height=&quot;1250&quot; data-filename=&quot;1.png&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9SmC7/btslaRPQ5en/2P9SGtUB4mDs8CtLw8ogm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9SmC7%2FbtslaRPQ5en%2F2P9SGtUB4mDs8CtLw8ogm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2084&quot; height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQj9eR/btsk9PSEiH4/Grssrdk9YvRaTWGiV8L5ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQj9eR/btsk9PSEiH4/Grssrdk9YvRaTWGiV8L5ZK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2084&quot; data-origin-height=&quot;1250&quot; data-filename=&quot;2.png&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQj9eR/btsk9PSEiH4/Grssrdk9YvRaTWGiV8L5ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQj9eR%2Fbtsk9PSEiH4%2FGrssrdk9YvRaTWGiV8L5ZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2084&quot; height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rEmsQ/btsladFWJgA/ImYtOn0i8AKWkppK1kbYJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rEmsQ/btsladFWJgA/ImYtOn0i8AKWkppK1kbYJ1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2084&quot; data-origin-height=&quot;1250&quot; data-filename=&quot;3.png&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rEmsQ/btsladFWJgA/ImYtOn0i8AKWkppK1kbYJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrEmsQ%2FbtsladFWJgA%2FImYtOn0i8AKWkppK1kbYJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2084&quot; height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;made in hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXj2JH/btslgh0Ut53/fsg9gMKWnZwK0umHID2kOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXj2JH/btslgh0Ut53/fsg9gMKWnZwK0umHID2kOK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2084&quot; data-origin-height=&quot;1250&quot; data-filename=&quot;4.png&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXj2JH/btslgh0Ut53/fsg9gMKWnZwK0umHID2kOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXj2JH%2Fbtslgh0Ut53%2Ffsg9gMKWnZwK0umHID2kOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2084&quot; height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HDYYV/btslbxQYAK7/EagPSuEpTfF6JtZTQFdc30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HDYYV/btslbxQYAK7/EagPSuEpTfF6JtZTQFdc30/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2084&quot; data-origin-height=&quot;1250&quot; data-filename=&quot;5.png&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HDYYV/btslbxQYAK7/EagPSuEpTfF6JtZTQFdc30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHDYYV%2FbtslbxQYAK7%2FEagPSuEpTfF6JtZTQFdc30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2084&quot; height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpnoZh/btslhb7ntbF/FEporggzpfVER1xCf0X5E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpnoZh/btslhb7ntbF/FEporggzpfVER1xCf0X5E1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2084&quot; data-origin-height=&quot;1250&quot; data-filename=&quot;6.png&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpnoZh/btslhb7ntbF/FEporggzpfVER1xCf0X5E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpnoZh%2Fbtslhb7ntbF%2FFEporggzpfVER1xCf0X5E1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2084&quot; height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;made in hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그림을 통해 Queue라는 것은 한쪽 방향으로 데이터가 들어와서 다른 한쪽 출구로 데이터가 나가는 구조라는 것을 직관적으로 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;큐(Queue) 활용 사례&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐는 굉장히 많은 곳에서 다양하게 사용된다. 대표적인 활용 사례는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;작업 스케줄링(Job Scheduling)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;운영 체제에서 프로세스의 실행을 관리할 때 큐를 사용한다. 준비 큐(Ready Queue)에 포함된 프로세스들이 들어온 순차적으로 실행하는 방식으로 작업 스케줄링이 진행된다.&amp;nbsp; 멀티스레드 환경에서는 작업 큐(Job Queue)에 스레드 작업들을 추가한 순서대로 실행할 수 있도록 큐를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;네트워크 패킷 처리(Network Packet Processing)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;네트워크 패킷은 인터넷상에서 데이터를 전송하는 단위이다. 데이터 전송 시 큰 데이터를 작은 조각으로 분할하여 패킷으로 만들고 이를 전송하게 된&lt;span style=&quot;background-color: #ffffff;&quot;&gt;다.&amp;nbsp;이러한 패킷을 받을 때 큐에 저장하고, 순서대로 처리하여 패킷 손실을 방지하고 네트워크 효율을 향상한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #374151; text-align: left;&quot;&gt;멀티스레딩 환경(Multithreading)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;작업 스케줄링과 비슷하지만 조금 더 큰 개념이라고 불 수 있을 것 같다. 시스템에 포함되어 있는 자원들을 여러 프로세스나 스레드가 공유한다고 해보자. 자원에 대한 요청이 있을 시, 이 요청을 큐에 저장하고 순서대로 자원을 할당하는 방식으로 여러 프로세스나 스레스를 공평하게 자원을 사용할 수 있게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;월 서버 요청 처리(Web Server Request Processing)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;웹 서버에서 클라이언트 요청 시, 받은 요청을 큐에 저장호고 순차적으로 처리하여 서버 과부하를 방지하고 응답 시간을 최소화한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #374151; text-align: left;&quot;&gt;캐시 관리(Cache Management)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #374151; text-align: left;&quot;&gt;&amp;nbsp;데이터의 접근 순서에 따라 큐에 저장하여 데이터를 효율적으로 관리하고 필요한 데이터를 빠르게 접근할 수 있도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #374151; text-align: left;&quot;&gt;우선순위 치리(&lt;span style=&quot;background-color: #ffffff; color: #374151; text-align: left;&quot;&gt;Priority&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #374151; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Processing&lt;/span&gt;)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일반적인 큐와는 약간 다르다. 일반 큐의 업데이트 버전이라 하면 된다. 각 작업에 우선순위를 부여하고, 관련 우선순위가 높은 작업을 먼저 처리하는 방식으로 큐를 사용한다. 쉽게 말하면, 큐에 넣는 방식은 동일하다. 그러나 큐에서 빼낼 시 우선순위가 높은 값을 먼저 빼내는 방식이라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;너비 우선 탐색(Breadth-First Search)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일명 BFS라는 너비 우선 탐색 시에도 큐를 사용한다. 탐색할 노드를 큐에 저장하고 순차적으로 방문하여 그래프를 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;버퍼(buffer) 활용하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼란 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 데이터를 보관하는 메모리 영역이라 할 수 있다. 비슷한 의미로 버퍼링이란 버퍼를 활용하는 방식 또는 버퍼를 채우는 동작을 의미한다. 일반적으로 입출력 및 네트워크와 관련된 기능에서 많이 이용이 되고, 순차적으로 입력/출력이 전달되어야 하기 때문에 FIFO 방식의 자료구조인 큐가 활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt; 큐(Queue) 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐의 기본연산은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인큐(Enqueue):&amp;nbsp;큐의&amp;nbsp;뒤에&amp;nbsp;데이터를&amp;nbsp;추가합니다.&lt;/li&gt;
&lt;li&gt;디큐(Dequeue):&amp;nbsp;큐의&amp;nbsp;앞에서&amp;nbsp;데이터를&amp;nbsp;제거하고&amp;nbsp;반환합니다.&lt;/li&gt;
&lt;li&gt;프런트(Front)&amp;nbsp;확인:&amp;nbsp;큐의&amp;nbsp;앞에&amp;nbsp;위치한&amp;nbsp;데이터를&amp;nbsp;반환합니다.&amp;nbsp;제거하지&amp;nbsp;않습니다.&lt;/li&gt;
&lt;li&gt;리어(Rear)&amp;nbsp;확인:&amp;nbsp;큐의&amp;nbsp;뒤에&amp;nbsp;위치한&amp;nbsp;데이터를&amp;nbsp;반환합니다.&amp;nbsp;제거하지&amp;nbsp;않습니다.&lt;/li&gt;
&lt;li&gt;비어&amp;nbsp;있는지&amp;nbsp;확인(Is&amp;nbsp;Empty):&amp;nbsp;큐가&amp;nbsp;비어&amp;nbsp;있는지&amp;nbsp;여부를&amp;nbsp;확인합니다.&lt;/li&gt;
&lt;li&gt;큐의&amp;nbsp;크기&amp;nbsp;확인(Size):&amp;nbsp;큐에&amp;nbsp;저장된&amp;nbsp;데이터의&amp;nbsp;개수를&amp;nbsp;반환합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 queue 기능을 하나씩 만들어보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;큐 생성하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687736293258&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def __init__(self):
        self.items = []&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트로 큐를 만들어서 items에 넣어 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;빈값 확인하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687736337684&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def isEmpty(self):
        if self.items == []:
            return True
        else:
            return False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 내부에 값이 비어있는지 확인하여 Boolean 값을 리턴해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;인큐(enQueue) 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687736379433&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def enqueue(self, item):
        self.items.append(item)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List의 append를 활용하여 인큐를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;디큐(deQueue) 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687736405667&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def dequeue(self):
        if len(self.items) != 0:
            return self.items.pop(0)
        else:
            print(&quot;Queue is empty&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pop(0)을 활용하여 내부의 값을 빼낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;큐 내부 확인하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687736432500&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def display(self):
        return (self.items)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;items를 단순하게 리턴해주면 큐 값을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 결합하여&lt;b&gt; Queue class&lt;/b&gt;를 만들면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686034701166&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Queue:
    def __init__(self):
        self.items = []

    def isEmpty(self):
        if self.items == []:
            return True
        else:
            return False

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        if len(self.items) != 0:
            return self.items.pop(0)
        else:
            print(&quot;Queue is empty&quot;)

    def display(self):
        return (self.items)

if __name__ == &quot;__main__&quot;:
    Q = Queue()
    Q.enqueue(1)
    Q.enqueue(2)
    Q.enqueue(3)
    Q.enqueue(4)
    Q.display()
    Q.dequeue()
    Q.dequeue()
    Q.dequeue()
    Q.display()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;queue에서 pop(0)을 하면 안 쪽 데이터가 한 칸씩 당겨져서 느려진다. 따라서 insert와 pop을 쓰는 것을 지양하자. deque(덱)을 써라.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;덱은 내부적으로 list가 아니라 (Double Ended Queue) 링크드 리스트로 만들어져 있다. 지금 배우는 건 &lt;b&gt;단순열결리스트&lt;/b&gt;고, 덱은 나중에 배울 &lt;b&gt;이중연결 리스트&lt;/b&gt;로 만들어져 있다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;stack은 list의 가운데나 왼쪽을 넣고 뺄 필요 없이, 오른쪽 뒤 끝부분만 수정한다. 따라서 stack은 굳이 덱 쓸 필요 없이 list로 구현하면 된다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Queue는 앞쪽을 빼므로 list로 쓰면 느려진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;collections.deque 라이브러리르&lt;/b&gt; 이용하면, 간단히 stack을 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1686034701169&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections imort deque

class Stack:
    def __init__(self):
        self.items = deque([])

    def isEmpty(self):
        if len(self.items) == 0:
            return True
        else:
            return False

    def enqueue(self, item):
        self.items.append(item)

    def dequeue(self):
        if len(self.items) != 0:
            return self.items.pop()
        else:
            print(&quot;Stack is empty&quot;)

    def peek(self):
        if len(self.items) != 0:
            return self.items[len(self.items) - 1]
        else:
            print(&quot;Stack is empty&quot;)

    def display(self):
        return (self.items)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우선순위 큐&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위 큐는 아래와 같다. 우선순위 큐는 일반적인 큐와 동일하지만, 데이터를 넣는 부분만 다르다. 데이터를 넣을 때, priority(우선순위)를 비교해서 적절한 위치에 들어가게 된다. 아래의 def insert 부분을 보면, for 문을 통해 우선순위를 비교하여 0 인덱스부터 순차적으로 검사하여 추가하는 데이터의 우선순위보다 특정 인덱스의 우선순위가 큰 위치에 값을 넣어준다. 사실상 정렬 알고리즘 방식과 동일하다고도 할 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686034619645&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PriorityQueue:

    def __init__(self):
        self.queue = list()
        # if you want you can set a maximum size for the queue

    # O(n) insertion
    # Inserts new Node into the priority queue in increasing order of priority
    def insert(self, node):
        # if queue is empty
        if self.size() == 0:
            # add the new node
            self.queue.append(node)
        else:
            # traverse the queue to find the right place for new node
            for x in range(0, self.size()):
                # if the priority of new node is greater
                if node.priority &amp;gt;= self.queue[x].priority:
                    # if we have traversed the complete queue
                    if x == (self.size() - 1):
                        # add new node at the end
                        self.queue.insert(x + 1, node)
                    else:
                        continue
                else:
                    self.queue.insert(x, node)
                    return True

    # O(1) deletion
    def delete(self):
        # remove the first node from the queue
        return self.queue.pop(0)

    # Prints each element of the priority queue
    def show(self):
        for x in self.queue:
            print(str(x.info) + &quot; - &quot; + str(x.priority))

    # Returns the size of the priority queue
    def size(self):
        return len(self.queue)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위 큐는 기본적인 큐와 다르게 list에 들어갈 값에 일반적인 정보뿐만 아니라, priority(우선순위정보)도 같이 넣어주는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;큐(Queue) 심화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐에 대한 기초적인 정리는 완료하였다. 큐에 대한 구현을 다른 방법으로 진행해 보자. 구현방식은 front와 rear인 2개의 인덱스를 사용하여 구현해보려 한다. front 변수는 꺼내진 위치를 나타내고, rear 변수는 저장한 위치를 나타낸다. 처음에 자료가 아무것도 없는 상태라면 front와 rear 값이 -1로 초기화가 된다. 기본적으로 pointer를 사용하는 방법으로, 기존에 list를 사용할 때처럼 값을 빼도 list의 내부 값들이 한 칸씩 이동하는 방식은 적용되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 삽입하는 경우를 예를 들어 보겠다. 데이터 A를 넣으면 인덱스 0에 값이 들어가고 rear값은 0으로 변한다. 그리고 다시 데이터 B를 넣으면 인덱스 1 위치에 값이 들어가고 rear값을 1로 변한다. rear은 마지막 저장한 위치를 가리키는 것을 알 수 있다. 정리하면 큐 삽입의 경우는 front는 고정이고 rear 값만 변경되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 삭제하는 경우는 가장 먼저 삽입된 원소가 삭제된다. 위에서 넣었던 데이터 A가 삭제되고 front값은 0이 된다. 이때, 삭제라는 용어를 적었지만, list를 삭제한다기보다는 front 포인터를 옮겨서 삭제 부분을 나타낸다고 이해하면 더 좋을 것 같다. 다시 데이터 C를 넣으면, 1이었던 rear값은 2가 된다. 만약, 세 번의 삽입과 세 번의 삭제가 이루어진다면, Queue는 비어 있고, front와 rear 값은 둘 다 2로 같아진다고 할 수 있다. 즉 front와 rear가 같다면 Queue 비어있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;선형 큐(Linear Queue)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름에서 느껴지듯 선형 큐는 우리가 일반적으로 사용하는, 일자로 구성된 큐라고 할 수 있다. front와 rear 변수를 사용하는 포인터 자료구조를 활용해서 구현해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. front : 저장된 첫 번째 원소의 인덱스로 마지막으로 꺼낸 자리를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. rear : 저장된 마지막 원소의 인덱스로 마지막으로 저장된 자리를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 표현은 초기 상태 / 공백 상태 / 포화 상태로 3가지로 나누어 구현을 할 예정이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #666666; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;초기 상태 : front = rear = -1 (비어있어서 저장된 원소가 없음)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;공백 상태 : front = rear (연산 진행 중의 공백상태)&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;포화 상태 : rear = n-1 (n: 리스트의 크기, n-1: 리스트의 마지막 자리 인덱스)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;rear값이 큐의 마지막 위치 값이 되어있는 경우 큐가 가득 차있음&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;포화 상태가 된 상태에서 데이터를 추가로 넣어야 될 상황이 벌어졌다면... Queue를 작게 만들었을 거다.. 즉, 문제 풀 때 포화 상태가 될 일은 없다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 시 큐의 크기를 초반에 정해 놓고 하는 것이 좋다. 파이썬에서는 크기조정이 자동으로 되지만, 그래도 정해두고 하면 속도가 굉장히 빨라진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;초기 공백 큐 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688020568309&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;q = [0]*3
front = -1
rear = -1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어렵게 class 쓸 필요 없이 단순하게 변수 지정으로 크기가 3인 큐를 list로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;inEmpty(), isFull() 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queue에서는 삽입과 삭제를 위해서 큐가 가능 차있거나 비어있는지 상태를 확인할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688020678927&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def isEmpty():
    return front == rear
def isFull():
    return rear == len(Q)-1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;isEmpty로 공백상태는 front와 rear가 같은 경우를 나타내고, 포화상태(isFull)는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;rear = n-1&lt;/b&gt;&lt;/span&gt; ( n이 리스트의 크기라면 n-1은 리스트의 마지막 인덱스를 가리킨다. )으로 확인가능하다. 공백상태인 경우에는 Ture반환하고, rear 값이 리스트의 마지막 인덱스 값이 되면 포화 상태가 되므로 True를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;enQueue 만들기 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 원소 뒤에 새로운 원소를 삽입하기 위해서는 rear 값을 하나 증가시켜 새로운 원소를 삽입할 지리는 만든다. 그리고 그 자리에 해당하는 리스트 원소 Q [rear] 부분에 item을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688021584302&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def enQueue(item):
    global rear
    if isFull():          
        print(&quot;Queue_Full&quot;) 
    else:
        rear +=1
        Q[rear] = item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;isFull 부분을 확인하는 이유는 큐가 가득 찬 상태에서는 더 이상 원소를 삽입할 수 없기 때문에 큐가 가득 차 있음을 알려주어야 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;deQueue 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 앞에 있는 원소를 삭제하기 위해서는 front 값을 하나 증가 시켜 큐에 남아있는 첫 번째 원소로 이동해야 한다. 새로운 첫 번째 원소를 리턴하여 삭제와 동일한 기능을 한다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688021791769&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def deQueue(item):
    global rear
    if isEmpty():         
        print(&quot;Queue_Empty&quot;) 
    else:
        front +=1
        return Q[front]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐가 비어있는 경우 더 이상 원소를 삭제할 수 없기 때문에 큐가 비어있음을 알려주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Qpeek() 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 앞에 있는 원소를 검색하여 확인한다. 즉, 현재 front의 한자리 뒤(front+1)에 있는 원소, 즉 큐의 첫 번째에 있는 원소를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688021931226&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def Qpeek():
    if isEmpty() : 
        print(&quot;Queue_Empty&quot;)
    else:
        return Q[front+1]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐가 비어있는지를 확인하고, 비어있으면 비어있다고 print 해준다. 여기 중요한 것은 front값은 변경되지 않는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;선형 큐(Linear Queue)의 단점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트의 크기를 동적으로 바꾸지 않고 고정하여 만든다는 것은 사용할 큐의 크기만큼 미리 확보를 해야 한다. 따라서 메모리 낭비가 발생할 수 있다. 선형 큐는 삽입, 삭제의 처리속도가 빠르다는 장점이 있지만, 메모리 낭비가 심하다는 단점도 있다. 삽입, 삭제를 계속할 경우 리스트의 앞부분에 활용할 수 있는 공간이 있음에도 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;rear=n-1&lt;/span&gt; &lt;/b&gt;이 되어 포화 상태로 잘못 인식하여 더 이상의 삽입이 불가능해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형 큐를 해결하는 방법은 여러 가지가 있다. 기본적으로 메모리를 절약을 위해, list에서 데이터를 뺐을 때 앞으로 당기는 것(데이터의 인덱스 값들 변경)은 시간공간 낭비가 너무 심하므로 하면 안 된다. 따라서 큐 라이브러리를 사용해서 구현하거나, 단순 연결 리스트를 활용해서 메모리를 동적으로 확보하는 방식도 좋은 방법이라고 할 수 있다. 여러 방법들 중에 원형 큐(CIrclar Queue)를 만들어 선형 큐의 단점은 보안할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원형 큐(Circular Queue)&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원형 큐는 선형 큐의 문제점을 보안하기 위해 만들어진 자료구조라 고 할 수 있다. rear와 front가 가리키는 위치가 마지막 인덱스까지 가면, 그다음 인덱스는 0으로 갈 수 있도록 만든 것이 바로 원형 큐이다. 기본 리스트를 사용하는 것은 동일하지만, 원형을 반복하는 것과 같이 포이터를 이동시키는 것이라고 할 수 있다. 즉, 1차원 배열을 사용하지만 논리적으로는 배열을 처음과 끝이 연결되어 원형 형태의 큐를 이룬다고 가정하고 사용하는 것이다. (사실 원형 큐의 치명적인 문제는 가득 차면, 못쓴다. 쉽게 말하면, deQueue가 없이 큐가 가득 차게 되면 자료가 덮어쓰게 되어 원형 큐를 못쓰게 된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;원형 큐의 특징&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 공백상태에서 원형 순환을 위해 &lt;b&gt;front = rear = 0으로&lt;/b&gt; 설정되어 있다. 순환을 구현하기 위해서는, front와 rear의 위치가 리스트의 마지막 인덱스인 n-1을 가리킨다면, 논리적 순환을 위해 리스트의 처음 인덱스인 0으로 이동시켜야 한다. 이때 나머지 연산자 %를 사용하여 구현을 한다. front 변수는 공백 상태와 포화 상태 구분을 위해 front가 있는 자리를 사용하지 않고 항상 빈자리로 둔다. 선형 큐와의 차이점을 확인해 보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;&lt;b&gt;테이블 인덱스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;&lt;b&gt;삽입 위치&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;&lt;b&gt;삭제 위치&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;선형 큐&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;rear = rear + 1&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;front = front + 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;원형 큐&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;rear = (rear + 1) % n&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;front = (front + 1) % n&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원형 큐의 n이 넘어가면 n의 나머지를 구하여 위치를 재조정한다. &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;큐 생성 시 시작은 0에서 front와 rear이 시작하고, 인큐는 rear값이 1 증가하고 rear위치에 A를 삽입된다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;즉, 삽입이 0부터 되는 게 아니라 [1]부터 한다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;삭제는 front를 1 증가시키고 프런트위치의 A가 삭제한다.(실제 삭제는 아니고 덮어씌운다고 생각하면 된다.) &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;마지막 삽입은 [0]에도 가능하다. 예를 들면 크기가 4인 원형 큐에서 rear 값이 3이라면, 다음 삽입 위치는 0이 된다고 할 수 있다. 그리고 front위치에는 삽입 불가 하므로 front 빼고 삽입 다 됐으면 Full이라고 할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;isEmpty, isFull 만들기&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공백 상태인 경우는 front = rear인 경우이다. 포화 상태인 경우는 선형큐와 다르게 삽입할 rear의 다음 위치가 현재 front 값일 때이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688025226262&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def isEmpty():
    return front == rear

def isFull():
    return (rear+1) % len(cQ) == front&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;enQueue 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 윈소 뒤에 새로운 원소를 삽입하기 위하여 rear 값을 조정하여 새로운 원소를 삽입할 자리를 마련한다.(&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;rear = (rear + 1) % n&lt;/span&gt;&lt;/b&gt;) 그리고 인덱스에 해당하는 리스트 원소를 저장한다. (&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Q[rear] = item&lt;/span&gt;&lt;/b&gt;)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688025581329&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def enQueue(item):
    global rear
    if isFull:
        print(&quot;Queue_Full&quot;)
    else:
        rear = (rear+1)%len(cQ)
        cQ[rear] = item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enQueue에서 주의할 점은 큐가 가득 차 있지 않을 때만 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;deQueue&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 앞에 있는 원소를 삭제하기 위해 front 값을 조정하여 삭제할 자리를 준비한다. 그리고 새로운 front 원소를 리턴하여 삭제와 동일한 기능을 하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688026625620&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def deQueue():
    global front
    if isEmpty():
        print(&quot;Queue_Empty&quot;)
    else:
        front = (front+1)%len(cQ)
        return cQ[front]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;front 값을 앞으로 이동시킬 때 나머지 연산을 수행하여 큐의 마지막 위치에 있는 경우 큐의 제일 앞으로 front값을 이동시킬 수 있다. 역시 큐가 비어있는 경우 알려주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;연결 큐(Linked Queue) 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 이해를 위해서는 기본적으로 Linked list에 대한 이해가 필요하다. 확인을 하고 오면 아래의 내용을 쉽게 이해할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;큐 생성하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 노드 없이 포인터 변수만 만들자. 아래와 같이 front와 rear는 None으로 초기화하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1688027556499&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;front = None
rear = None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;isEmpty 만들기&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1688027596234&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def isEmpty():
    return front == None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공백상태를 확인하는 것은 간단히 None으로 확인이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;enQueue 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1688027648582&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def enQueue(item):
    global front, rear
    newNode = Node(item) # 새로운 노드 생성
    if front == None: # 큐가 비어있으면
        front = newNode
    else:
        rear.next = newNode
    rear = newNode&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 노드를 생성 후에 데이터 필드에 item을 저장한다. 연결 큐가 공백이 아닌 경우와 공백인 경우에 따라 front, rear 변수를 지정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;deQueue 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1688027765681&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def deQueue():
    global front, rear
    if isEmpty():
        print(&quot;Queue_Empty&quot;)
        return None

    item = front.item
    front = front.next
    if front == None:
        rear = None
    return item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 front를 다음 front를 가리키게 하고, 현재 front의 item을 리턴해주면 간단히 구현이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Linked Queue 테스트하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 코드를 통해 구현하는 것이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1688027982648&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Node:
    def __init__(self, item, n = None):
        self.item = item
        self.next = n
# 노드안에 item과 node을 만듬.


def enQueue(item):
    global front, rear
    newNode = Node(item) # 새로운 노드 생성
    if front == None: # 큐가 비어있으면
        front = newNode
    else:
        rear.next = newNode
    rear = newNode

def isEmpty():
    return front == None

def deQueue():
    global front, rear
    if isEmpty():
        print(&quot;Queue_Empty&quot;)
        return None

    item = front.item
    front = front.next
    if front == None:
        rear = None
    return item

def Qpeek():
    return front.item

def printQ():
    f = front
    s = &quot;&quot;
    while f:
        s += f.item + &quot; &quot;
        f = f.next
    return s

front = None
rear = None

enQueue('A')
enQueue('B')
enQueue('C')
printQ()
print(deQueue())
print(deQueue())
print(deQueue())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Queue는 매우 중요하다. 그리고 위의 정보뿐만 아니라 다른 다양한 큐도 존재한다. 조금 관심이 든다면, 큐에 대해 공부해 보는 것도 좋다는 생각이 든다. 사실 BFS 문제 풀이 부분까지 넣으려 했으나, 글이 너무 길어져서 관련 내용은 다른 글에서 다뤄보도록 하겠다.&lt;/p&gt;</description>
      <category>알고리즘/알고리즘 종류</category>
      <category>Circular Queue</category>
      <category>linear queue</category>
      <category>Queue</category>
      <category>선형 큐</category>
      <category>우선순위 큐</category>
      <category>원형 큐</category>
      <category>자료구죠</category>
      <category>큐</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/574</guid>
      <comments>https://han-py.tistory.com/574#entry574comment</comments>
      <pubDate>Thu, 29 Jun 2023 20:00:41 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조 - Python] 스택(STACK)</title>
      <link>https://han-py.tistory.com/573</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자료구조에서 가장 중요하고 기초라고 할 수 있는 후입선출(LIFO) 구조의 스택에 대해 알아보자. 기본적으로 자료구조에서 기본적으로 자료를 담을 수 있는 방식에는 배열(Array)과 스택(Stack), 큐(Queue)가 있다. 각각의 기본적인 공통점은 자료를 넣어두고 사용하는 것이다. 여기서 우리가 생각할 수 있는 점은 효율적으로 자료를 관리하기 위해, 자료를 담는 방식에 대한 차이를 이해하는 것이 중요하다. 컴퓨터 과학에서 중요한 자료구조 중 하나인 스택(Stack)에 대해 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스택(Stack)이란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택은 데이터를 임시 저장할 떄 사용하는 자료구조이다.&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt; 한 쪽 끝에서만 자료를 넣거나 뺄 수 있는 선형 구조(LIFO - Last In First Out)로 되어 있다. 이러한 자료구조를 직관적으로 풀어보면, 프링글스 과자와 같다고 할 수 있다. &lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;프링글스&lt;/span&gt; 과자를 만들 때, 처음 통에 넣은 과자는 가장 마지막에 먹을 수 있다. 반대로 가장 마지막에 넣은 프링글스 과자는 뚜껑을 열면 가장 상단에 있기 때문에 바로 먹는 것이 가능하다. 이렇게 순차적으로 쌓이기 때문에 나중에 넣은 것을 먼저 꺼낼 수 있는 자료구조가 바로 스택(Stack)이라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택은 다양한 컴퓨터 과학 분야에서 요융하게 사용된다. 다른 블로그에서 소개될 함수 호출이나 재귀 알고리즘에서 스택은 매우 중요한 역할을 한다. 또한, 웹 브라우저의 방문 기록의 뒤로 가기나, 텍스트 편집기에서 실행 취소의 기능도 모두 스택 자료구조를 통해서 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택의 구현 방식을 조금 더 쉽게 그림으로 알아보자. 아래의 그림등 A 데이터와 B 데이터를 순차적으로 PUSH 하여 담아주는 그림이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIBqVp/btskcmozesh/pjWH2MzN98nUSuH8XSLaV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIBqVp/btskcmozesh/pjWH2MzN98nUSuH8XSLaV0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;1667&quot; data-filename=&quot;1.png&quot; width=&quot;300&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIBqVp/btskcmozesh/pjWH2MzN98nUSuH8XSLaV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIBqVp%2Fbtskcmozesh%2FpjWH2MzN98nUSuH8XSLaV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1250&quot; height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yKxu2/btsj8XpcUSK/rRkwmtXIjLHONS6PwQuRc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yKxu2/btsj8XpcUSK/rRkwmtXIjLHONS6PwQuRc0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;1667&quot; data-filename=&quot;2.png&quot; width=&quot;300&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yKxu2/btsj8XpcUSK/rRkwmtXIjLHONS6PwQuRc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyKxu2%2Fbtsj8XpcUSK%2FrRkwmtXIjLHONS6PwQuRc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1250&quot; height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ezAhkB/btsj55aOZgF/TaulQvdFAPAlJlbpzpCgFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ezAhkB/btsj55aOZgF/TaulQvdFAPAlJlbpzpCgFk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;1667&quot; data-filename=&quot;3.png&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ezAhkB/btsj55aOZgF/TaulQvdFAPAlJlbpzpCgFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FezAhkB%2Fbtsj55aOZgF%2FTaulQvdFAPAlJlbpzpCgFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1250&quot; height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;made in hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림에서는 데이터 A와 B를 순차적으로 쌓아 올린것을 확인할 수 있다. 이렇게 쌓아 올린 데이터는 A, B가 쌓여 있는 것을 확인할 수 있다. 이 상태에서 하나의 데이터를 빼면, 아래와 같이 B가 빠지는 것을 확인할 수 있다. 여기서 C 데이터를 넣으면 A위에 C가 쌓이는 것을 확인가능하다. 즉, 데이터가 후입선출(LIFO) 방식으로 나중에 들어간 데이터가 먼저 나온다고 이해를 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d8DRDj/btsj8han8Ak/GtKEs4B5Fft8jtlMTEeFx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d8DRDj/btsj8han8Ak/GtKEs4B5Fft8jtlMTEeFx0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;1667&quot; data-filename=&quot;4.png&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d8DRDj/btsj8han8Ak/GtKEs4B5Fft8jtlMTEeFx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd8DRDj%2Fbtsj8han8Ak%2FGtKEs4B5Fft8jtlMTEeFx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1250&quot; height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wmkNM/btsj3mLcZcD/EwJZPaMCWIOcp4x2maNTrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wmkNM/btsj3mLcZcD/EwJZPaMCWIOcp4x2maNTrK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;1667&quot; data-filename=&quot;5.png&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wmkNM/btsj3mLcZcD/EwJZPaMCWIOcp4x2maNTrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwmkNM%2Fbtsj3mLcZcD%2FEwJZPaMCWIOcp4x2maNTrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1250&quot; height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FRF60/btsj8Bmgpwo/ogF9sWwHnDksNWFLVlLZW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FRF60/btsj8Bmgpwo/ogF9sWwHnDksNWFLVlLZW1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;1667&quot; data-filename=&quot;6.png&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FRF60/btsj8Bmgpwo/ogF9sWwHnDksNWFLVlLZW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFRF60%2Fbtsj8Bmgpwo%2FogF9sWwHnDksNWFLVlLZW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1250&quot; height=&quot;1667&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;made in hanpy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;우리가 위 그림에서 중점적으로 봐야하는 부분은, 같은 부분으로 데이터가 들어오고 나간다는 점이다. 따라서 중간 부분을 핸들링할 수 없고 한 부분만 핸들링이 가능하기 때문에 코딩 부분도 간단하다. 이러한 의미는 다른 자료구조는 그렇지 않다는 점을 의미한다. 우선은 간단히 만들기가 가능한 stack에 대한 코드 구현을 해보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스택(stack) 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택은 배열(Array)나 연결 리스트(Linked List)로 구현이 가능하다. 배열을 사용한 구현 방식은 효율적이지만, 크기가 고정되어 있어 데이터의 추가 삭제 시에 메모리 크기 변경이 필요하다. 하지만 연결 리스트로 구현을 한다면 메모리 제약이 없고 동적으로 크기가 조정할 수 있다. 이것은 일반적인 배열/연결 리스트의 장단점이고 파이썬에서는 배열로 간단하게 구현해서 사용가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택 자료구조에서 일반적으로 포함되는 기본 연산은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;푸시(Push): 스택의 맨 위에 데이터를 추가한다.&lt;/li&gt;
&lt;li&gt;팝(Pop): 스택의 맨 위에 위치한 데이터를 제거하고 반환한다.&lt;/li&gt;
&lt;li&gt;피크(Peek): 스택의 맨 위에 위치한 데이터를 반환한다. 이때, 데이터의 변화는 없다.&lt;/li&gt;
&lt;li&gt;비어 있는지 확인(IsEmpty): 스택이 비어 있는지 여부를 확인한다. 이때, 데이터의 변화는 없다.&lt;/li&gt;
&lt;li&gt;스택의 크기 확인(Size): 스택에 저장된 데이터의 개수를 반환한다. 이때, 데이터의 변화는 없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 간단히 관련 Stack을 만들어 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스택(Stack) 클래스&lt;/h4&gt;
&lt;pre id=&quot;code_1687594268826&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Stack:
    def __init__(self):
        self.items = []&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack이라는 클래스를 만들었다. 객체생성을 위해서는 아래와 같이 간단하게 클래스를 호출하여 가능하다. __init__ 를 통해 우리는 처음 인스턴스 생성 시 self.items로 배열을 넣는 것이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687594301934&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;s = Stack()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 생성 시 items라는 배열이 생성된다. 이 배열이 Stack을 담고 있는 자료구조라고 할 수있고, 이 배열을 가지고 Stack의 만든다고 이해하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스택(Stact) Push&lt;/h4&gt;
&lt;pre id=&quot;code_1687594809795&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def push(self, item):
        self.items.append(item)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 class 내부에 items에 데이터를 넣어줄 수 있는 append를 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스택(Stact) Pop&lt;/h4&gt;
&lt;pre id=&quot;code_1687594847745&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def pop(self):
        if len(self.items) != 0:
            return self.items.pop()
        else:
            print(&quot;Stack is empty&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 것은 pop()를 사용한다는 것이다. pop() 함수는 lsit에 포함된 함수로 리스트 내부의 데이터 중에 가장 마지막에 있는 데이터를 꺼내어 return 해준다. 따라서 Stack의 자료구조라 할 수 있다. 만약 items의 리스트가 빈 배열이라면 에러가 발생할 수 있기 때문에 리스트가 비어있는지 체크를 해주면 된다. 위의 로직에서는 print로 값이 비어 있는 경우 알려주는 방식으로 작성을 했지만, 다른 로직을 추가해 줘도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스택(Stact) Peak&lt;/h4&gt;
&lt;pre id=&quot;code_1687594988244&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def peek(self):
        if len(self.items) != 0:
            return self.items[len(self.items) - 1]
        else:
            print(&quot;Stack is empty&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 위의 데이터를 반환해주는 Peak를 만들어 보자. 리스트의 마지막 값을 리턴해주는 로직으로 간단히 작성이 가능하다. 이 경우도 값이 없는 경우 에러가 날 수 있으니 체크를 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스택(Stact) isEmpty&lt;/h4&gt;
&lt;pre id=&quot;code_1687595495457&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def isEmpty(self):
        if self.items == []:
            return True
        else:
            return False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택이 비었는지 확인해 주는 로직으로는 간단히 위와 같은 로직으로 만들면 된다. 값이 비어있다면 True를 리턴해주고, 값이 비어있지 않다면 False를 리턴해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스택(Stact) Size&lt;/h4&gt;
&lt;pre id=&quot;code_1687595598924&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def size(self):
        return len(self.items)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 리스트의 개수를 리턴해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스택(Stact) 만들기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 각각의 기능별로 만든 코드를 모아서 결합시키면 아래와 같다.&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686033368522&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Stack:
    def __init__(self):
        self.items = []

    def isEmpty(self):
        if self.items == []:
            return True
        else:
            return False

    def push(self, item):
        self.items.append(item)

    def pop(self):
        if len(self.items) != 0:
            return self.items.pop()
        else:
            print(&quot;Stack is empty&quot;)

    def peek(self):
        if len(self.items) != 0:
            return self.items[len(self.items) - 1]
        else:
            print(&quot;Stack is empty&quot;)
 
     def size(self):
        return len(self.items)

    def display(self):
        return (self.items)



# __main__
s = Stack()
c = 0
while c != 5:
    print('\tSTACK OPERATIONS')
    print('1.Push')
    print('2.Pop')
    print('3.Peek')
    print('4.Display Stack')
    print('5.Exit')
    c = int(input('Enter your choice(1-5): '))
    if c == 1:
        x = input(&quot;Enter the item: &quot;)
        s.push(x)
    elif c == 2:
        s.pop()
    elif c == 3:
        s.peek()
    elif c == 4:
        print(s.display())
    elif c != 5:
        print('Wrong Choice! Choose from 1 to 5 only')

print('Bye')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;__main__ 아래 부분은 간단히 while 문을 활용해서 stack를 사용하는 예시를 만들어 보았다. 참고만 하면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;스택(Stact) 만들기 (deque 활용)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;collections.deque 라이브러리르 이용하면, 간단히 stack을 구현할 수 있다. 사실 Stack과 Queue를 만들 때는 메모리/속도 효율성을 증대시키기 위해서 일반 배열(list)이 아니라 collections.deque를 활용하서 만든다. 그 이유는 deque는 이중 열결리스트로 만들어져 있기 때문에 여러 측면에서 일반 배열보다는 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686034344883&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections imort deque

class Stack:
    def __init__(self):
        self.items = deque([])

    def isEmpty(self):
        if len(self.items) == 0:
            return True
        else:
            return False

    def push(self, item):
        self.items.append(item)

    def pop(self):
        if len(self.items) != 0:
            return self.items.pop()
        else:
            print(&quot;Stack is empty&quot;)

    def peek(self):
        if len(self.items) != 0:
            return self.items[len(self.items) - 1]
        else:
            print(&quot;Stack is empty&quot;)

    def display(self):
        return (self.items)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드들을 하나씩 뜯어보면, 사실 기초적인 파이썬 지삭만 있으면 충분히 이해가 될 수 있는 내용으로 구성되어 있다. 위의 내용을 참고하여 자신만의 Stack을 만들어 보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>Stack</category>
      <category>스택</category>
      <category>자료구조</category>
      <category>파이썬</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/573</guid>
      <comments>https://han-py.tistory.com/573#entry573comment</comments>
      <pubDate>Sat, 24 Jun 2023 20:00:03 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조 - Python] 배열</title>
      <link>https://han-py.tistory.com/576</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자료구조의 첫 번째 정리할 부분은 배열이다. 배열은 사실 우리가 사용 중인 파이썬의 list라고도 할 수 있겠다. 그렇다면 파이썬의 list와 자료구조의 배열은 완전히 같은 개념이라고 할 수 있을까? 배열에 대해 간단히 알아보고 코딩으로 체득하도록 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자료구조의 배열이란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인&lt;span&gt; &lt;/span&gt;자료구조에서의&lt;span&gt; &lt;/span&gt;배열이란&lt;span&gt;, &lt;/span&gt;순서를&lt;span&gt; &lt;/span&gt;가진&lt;span&gt; &lt;/span&gt;데이터의&lt;span&gt; &lt;/span&gt;집합을&lt;span&gt; &lt;/span&gt;가리키는&lt;span&gt; &lt;/span&gt;추상&lt;span&gt; &lt;/span&gt;자료형&lt;span&gt;(abstract data type)&lt;/span&gt;이다&lt;span&gt;. 즉, 순서를 가진 데이터 집합을 모두 배열이라고 할 수 있다. 배열을 종류는 크게 2가지로 나뉜다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;순차 리스트&lt;/b&gt;: 저장소를 배열형태로 만드는 것. 연속적인 메모리 공간에 저장(python에서 list)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- &lt;/span&gt;&lt;b&gt;연결&lt;span&gt; &lt;/span&gt;리스트&lt;/b&gt;&lt;span&gt;: &lt;/span&gt;저장&lt;span&gt; &lt;/span&gt;할때&lt;span&gt; &lt;/span&gt;마다&lt;span&gt; &lt;/span&gt;메모리를&lt;span&gt; &lt;/span&gt;확보해서&lt;span&gt; &lt;/span&gt;추가시키는&lt;span&gt; &lt;/span&gt;것&lt;span&gt;. &lt;/span&gt;메모리의&lt;span&gt; &lt;/span&gt;동적할당을&lt;span&gt; &lt;/span&gt;기반으로&lt;span&gt; &lt;/span&gt;구현&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 우리가 배열이라고 사용하는 파이썬의 list는 순차 리스트라고 할 수 있다. 연결 리스트는 다른 페이지로 따로 정리를 할 예정이고, 여기서는 순차 리스트에 대해 조금 더 알아보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;순차 리스트(Sequential List)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순차 리스트는 구현할 자료들을 논리적인  연속적인 메모리에 저장하는 자료구조이다.&amp;nbsp; 따라서 자료의 논리적인 구조와 물리적인 구조가 일치한다고 할 수 있다. 우선 아래의 코드를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686872197121&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;list_test = [3, 7, 12, 45]
print(list_test[1]) # 7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 기본적으로 순차 리스트는 연속적 메모리에 배열이 저장된다고 배웠다. 위의 배열에서 첫 번째 값인 3을 가져오기 위해서는 배열의 시작 주소로 접근을 해서 리스트의 첫번째 데이터를 가지고 올 수 있는 것이다. 만약&lt;span&gt; &lt;/span&gt;메모리&lt;span&gt; &lt;/span&gt;크기가&lt;span&gt; &lt;/span&gt;배열당&lt;span&gt; 4byte&lt;/span&gt;라면&lt;span&gt;, &lt;/span&gt;더하기를&lt;span&gt; &lt;/span&gt;통해&lt;span&gt; &lt;/span&gt;각각&lt;span&gt; &lt;/span&gt;인덱스의&lt;span&gt; &lt;/span&gt;값을&lt;span&gt; &lt;/span&gt;가져오는&lt;span&gt; &lt;/span&gt;것이&lt;span&gt; &lt;/span&gt;가능하다&lt;span&gt;. 즉, &lt;/span&gt;인덱스&lt;span&gt; 0&lt;/span&gt;이면&lt;span&gt; &lt;/span&gt;시작&lt;span&gt; &lt;/span&gt;주소가 되는 것이고&lt;span&gt;, &lt;/span&gt;시작&lt;span&gt; &lt;/span&gt;주소에&lt;span&gt; &lt;/span&gt;바로&lt;span&gt; &lt;/span&gt;첫번째&lt;span&gt; &lt;/span&gt;값이&lt;span&gt; 저장 되어 &lt;/span&gt;있기&lt;span&gt; &lt;/span&gt;때문에&lt;span&gt; &lt;/span&gt;인텍스&lt;span&gt; 0&lt;/span&gt;으로&lt;span&gt; 첫번째 &lt;/span&gt;데이터를&lt;span&gt; &lt;/span&gt;가져온다고&lt;span&gt; &lt;/span&gt;할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;iterable (이터러블)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iterable 객체의 구조는 원소를 하나씩 꺼내는 구조이다. iterable 한 자료형 객체에는 리스트, 튜플, 딕셔너리, 문자열 등이 있다. 배열을 배우는 입장에서 관련된 중요한 개념이 있어 가져왔다. 우선은 아래의 코드 예시를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686872402684&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;iter_x = iter([1,2,3,4,5])
while True:
    try:
        print(next(iter_x))
    except:
        raise&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 내장함수인 iter()에 이터러블 객체를 넣으면, 객체에 대한 iterator를 반환한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이때&lt;span&gt; &lt;/span&gt;&lt;/span&gt;next&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 호출하거나 next() 내장 함수로 원소를 순차적으로 꺼낼 수 있다. 따라서 위의&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;next(iter_x)&lt;/span&gt;는&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;iter_x.&lt;/span&gt;&lt;/span&gt;&lt;b&gt;iter_x.next()와&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;동일하다. &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;또한 만약 꺼낼 원소가 없으면 StopIteration 예외를 발생시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;배열 알고리즘 실습&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 알고리즘/자료구조 공부 시 항상 가장 강조하는 것은 기본적인 코딩 실력이 있어야 한다는 것이다. 따라서 아래의 간단한 실습 정도는 반드시 할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;최대&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;최소&lt;/b&gt;&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;b&gt;구하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;풀이 1&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686872834649&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def max_fuc(data):
    max_val = data[0]
    for i in range(1, len(data)):
        if data[i] &amp;gt; max_val:
            max_val = data[i]
    return max_val&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 풀이는 가장 간단한 최댓값 구하는 문제라고 할 수 있다. 배열을 for문으로 돌면서 max_val 변수보다 큰 변수가 있다면, max_val 변수로 변경을 해주면서 최대값을 구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;풀이 2&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686872851747&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;max([1, 3, 5, 2, 10]) # 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 최대값 최솟값을 구하는 가장 쉬운 방법은&lt;i&gt;&amp;nbsp;&lt;/i&gt;위와&lt;i&gt; &lt;/i&gt;같은&lt;i&gt; &lt;/i&gt;라이브러리를&lt;i&gt; &lt;/i&gt;사용해서&lt;i&gt; max(), min() &lt;/i&gt;값을&lt;i&gt;&amp;nbsp;&lt;/i&gt;구하는 것이다&lt;i&gt;. &lt;/i&gt;하지만&lt;i&gt;, &lt;/i&gt;최대&lt;i&gt;/&lt;/i&gt;최소&lt;i&gt; &lt;/i&gt;구하는&lt;i&gt; &lt;/i&gt;방식은 로직의 가장 기초 부분이라 할 수 있게 때문에&lt;i&gt;&amp;nbsp;&lt;/i&gt;코딩으로도&lt;i&gt; &lt;/i&gt;구현할&lt;i&gt; &lt;/i&gt;수&lt;i&gt; &lt;/i&gt;있어야&lt;i&gt; &lt;/i&gt;한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;enumerate &lt;/b&gt;&lt;b&gt;사용해 보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686873164964&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = ['python', 'java', 'C']

for index, value in enumerate(x):
    print(f`{index}번째 값은 {value}입니다`)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enumerate 내장 함수는 많이 사용하는 함수 중에 하나이다. 특징으로는 기존의 For문은 value값 하나만 준다면, index값과 value값을 동시에 받아서 사용이 가능하다고 할 수 있다. 배열의 원소의 위치를 같이 알 수 있기 때문에 매우 유용하다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;배열을&lt;span&gt; &lt;/span&gt;역순으로&lt;span&gt; &lt;/span&gt;정렬하기&lt;span&gt; 1 (&lt;/span&gt;짝수&lt;span&gt; / &lt;/span&gt;홀수&lt;span&gt; &lt;/span&gt;나누어&lt;span&gt; &lt;/span&gt;생각하기&lt;span&gt;)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;풀이 1&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686873512545&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = [1, 2, 3, 4, 5]
cnt = len(data)
for i in range(cnt // 2):
    data[i], data[cnt - i - 1] = data[cnt - i - 1], data[i]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코딩에서 중점적으로 보아야 하는 부분은 // 부분과 자리 변경 부분이다. 관련 내용은 어렵지 않기 때문에 넘어가도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;배열을&lt;/span&gt; &lt;span&gt;역순으로&lt;/span&gt; &lt;span&gt;정렬하기&lt;/span&gt; 2 (reverse())&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;풀이 1&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686873611345&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = [1, 2, 3, 4, 5]
data.reverse()
print(data)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reverse() 메서드를 활용하는 방법으로 암기해 두면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;배열을&lt;/span&gt; &lt;span&gt;역순으로&lt;/span&gt; &lt;span&gt;정렬하기&lt;/span&gt; 3 (reversed())&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;풀이 1&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686873621536&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = [1, 2, 3, 4, 5]
data = list(reversed(data))
print(data)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reversed() 내장함수를 활용하는 방법으로 암기해 두면 좋다. 위의 reverse()와 reversed()의 차이점을 확인하자. reversed() &lt;span&gt;함수는&lt;/span&gt; x&lt;span&gt;의&lt;/span&gt; &lt;span&gt;원소를&lt;/span&gt; &lt;span&gt;역순으로 정렬해주는&lt;/span&gt; iterable &lt;span&gt;객체를&lt;/span&gt; &lt;span&gt;생성한다&lt;/span&gt;. &lt;span&gt;즉&lt;/span&gt;, data&lt;span&gt;를&lt;/span&gt; &lt;span&gt;역순으로&lt;/span&gt; iterator&lt;span&gt;를 꺼녀서 반환하는&lt;/span&gt; &lt;span&gt;것이다&lt;/span&gt;. &lt;span&gt;따라서&lt;/span&gt; &lt;span&gt;위의 코드 처럼 반환된&lt;/span&gt; iterable &lt;span&gt;객체를&lt;/span&gt; list() &lt;span&gt;함수에&lt;/span&gt; &lt;span&gt;다시&lt;/span&gt; &lt;span&gt;넘겨&lt;/span&gt; &lt;span&gt;새로운&lt;/span&gt; &lt;span&gt;리스트&lt;/span&gt; &lt;span&gt;생성을 해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;효율적인 데이터 관리를 위해 우리는 자료구조에 종류에 대해 배우고 있다. 그중 순차 리스트는 연속적인 메모리로 1차원 배열에 순차적으로 항목들이 저장이 된다. 이러한 자료구조의 가장 기본이자 기초라 할 수 있는 &lt;b&gt;순차 리스트에 대한 코딩 숙련도&lt;/b&gt;는 필수 역량이라 할 수 있다. 배열을 보았으니 이제 다른 자료구조도 한번 같이 알아보자.&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>배열</category>
      <category>순차 리스트</category>
      <category>연결 리스트</category>
      <category>자료구조</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/576</guid>
      <comments>https://han-py.tistory.com/576#entry576comment</comments>
      <pubDate>Fri, 16 Jun 2023 20:00:54 +0900</pubDate>
    </item>
    <item>
      <title>자료구조(Data Structure) 기초 정리</title>
      <link>https://han-py.tistory.com/567</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;전반적인 자료구조/알고리즘에 대한 기초 글을 작성하려 한다.  필요한 내용을 참고해서 확인해 보자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/13&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;알고리즘 확인하기&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd3ALg/btsrhGIuY4I/dF3LPlQY9wcepYOBkzhw90/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd3ALg/btsrhGIuY4I/dF3LPlQY9wcepYOBkzhw90/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd3ALg/btsrhGIuY4I/dF3LPlQY9wcepYOBkzhw90/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd3ALg%2FbtsrhGIuY4I%2FdF3LPlQY9wcepYOBkzhw90%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-2.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Python으로 자료구조 조금 더 알아보기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/567&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[자료구조 기초] Data Structure 기초 시작하기&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[선형 구조 - Python] 배열&amp;nbsp;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/573&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[선형 구조&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&amp;nbsp;&lt;/span&gt;Python] 스택&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/574&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[선형 구조&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&amp;nbsp;&lt;/span&gt;Python]큐&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/575&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[선형 구조&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&amp;nbsp;&lt;/span&gt;Python] 연결 리스트&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;Python으로 알고리즘 조금 더 알아보기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/13&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[알고리즘 기초] Algorithm 기초 시작하기 - python&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[검색 알고리즘] 선형 탐색과 이진탐색&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[검색 알고리즘] 해시&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[재귀 알고리즘] 재귀&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[재귀 알고리즘] DFS&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[재귀 알고리즘] BFS&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/571&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[정렬 알고리즘] 버블 정렬&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[정렬 알고리즘] 선택 정렬&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[정렬 알고리즘] 삽입 정렬&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/572&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[정렬 알고리즘] 퀵 정렬&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[정렬 알고리즘] 병합 정렬&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;[정렬 알고리즘] 카운팅 정렬&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&amp;nbsp;처음 시작하는 단계에서 직관적인 이해를 할 수 있도록 글을 적어보려한다. 따라서 가능한 어려운 용어를 제외하였고, 이 글을 통해 자료구조의 기초적인 이해도를 높일 수 있으면 좋겠다. 본 내용 뿐만 아니라 추가 공부를 통해 스스로 살을 붙여나가길 기대한다. 아래의 모든 예시는 Python과 JavaScript로 만들어 제공할 예정이다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;자료구조(Data Structure)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;우리가 프로그래밍 언어를 배우는 이유는 무엇일까? 프로그래밍을 처음 배우면 보통 변수를 지정하고, 변수에 데이터를 넣는 것으로 시작을 한다. 그리고 변수를 이용해 데이터를 추가/수정/삭제/제거를 하면서 application을 만들기 시작한다. 즉, 이러한 관점에서 우리는 데이터를 다루기 위해 프로그래밍 언어를 배운다고 할 수 있다. 간단한 데이터가 아니라 수천/수만가지의 데이터를 다룬다면, 이러한 데이터를 다루는 방법에도 수만가지가 있을 것이다. 이러한 데이터를 좋은 구조로 조작하기 위해 알아야 하는것이 자료구조라고 할 수있겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;자료구조와 알고리즘&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;잘 선택한 자료구조는 보다 효율적인 알고리즘을 사용할 수 있게 한다. 프로그램 설계 시 가장 먼저 고려해야하는 것은 자료구조이다. 왜냐하면, 시스템의 기능에 따른 적절한 자료구조를 선택하는 것이 최종 성능을 크게 좌우한다. 그리고 자료구조에 따른 알고리즘에 대한 선택은 어느정도 공식화 되어있다고 할 수 있다. 물론 서비스에서 필수적으로 사용되어야할 알고리즘이 있다면, 그 알고리즘에 따른 자료구조의 선택은 필수적이라 할 수 있겠다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; font-family: 'Nanum Gothic';&quot;&gt;정리하면, 자료구조를 구현하기 위해서는 알고리즘이 필요하고, 반대로 알고리즘 문제를 풀기 위해 자료구조를 사용하기 때문에, 자료구조와 알고리즘은 밀접하다고 할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;자료구조의 성능 비교&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;자료구조의 필요성에 대해서는 위에서 알아보았다. 그러면 어떤 자료구조가 좋은지에 대한 기준도 알아보아야한다. 이는 &lt;b&gt;성능 측정 방법&lt;/b&gt;이라고 할 수 있겠다. 당연한 이야기지만, 얼마나 빠르게 목표한 바를 빠르게 도출할 수 있는 프로그램을 만드는지도 중요하다. 추가적으로 좋은 컴퓨터를 이용하면, 계산하는 결과가 빠른 것 처럼 얼마나 돈이 많이 드는지(메모리)도 고려를 해야할 것이다. 물론 돈이 적게 들면서 목표한 결과를 빠르게 도출하는게 가장 좋지만, 이러한 경우도 분명한 trade-off가 있기 때문에 특정 상황에 따른 적절한 자료구조와 알고리즘을 선택하는 것이 개발자의 필수 역량이라고도 할 수 있겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;프로그램을 실행하고 완료하는데 필요로 하는 시간의 양을 &lt;b&gt;시간 복잡도 (Time Complexity)&lt;/b&gt;라고 표현하고, 프로그램을 실행하고 완료하는데 필요한 메모리의 양을 공간 복잡도(Space Complexity)라고 한다. 사실 기초 단계에서는 알고리즘을 평가 시에 메모리 보다는 실행속도에 초점을 둔다. 이러한 성능을 수치로 나타낸 것이 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;빅-오 표기법(Big-Oh Notaion)&lt;/b&gt;&lt;/span&gt;이라고 한다.(기초단계에서는 사실상 시간복잡도라고 생각해도 무방하다) 빅-오 표기법의 대표적인 예는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;Big-O 표기법 예시&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #333333; text-align: start;&quot;&gt;우리의 목표는 정확한 자료구조를 인지하고 있고, 최적의 메모리와 계산을 도출해 내는 것을 목표로 한다. 아래의 표에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;N log N&lt;/span&gt; 까지는 괜찮을 알고리즘이라고 평가한ㄷ.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 110px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 49.6512%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #006dd7;&quot;&gt;&lt;b&gt;O(1)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 20px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;N에 상관 없이 연산횟수가 한번으로 고정이다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 49.6512%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #006dd7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;O(log&amp;nbsp;&lt;/span&gt;&lt;i&gt;N&lt;/i&gt;)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터 수에 비해 연산 횟수 증가률이 낮다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 49.6512%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #006dd7;&quot;&gt;&lt;b&gt;O(&lt;i&gt;N&lt;/i&gt;&lt;span style=&quot;text-align: start;&quot;&gt;)&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터 수에 비례해여 연산횟수가 증가.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 49.6512%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #006dd7;&quot;&gt;&lt;b&gt;O(&lt;i&gt;N&lt;/i&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;log&amp;nbsp;&lt;/span&gt;&lt;i&gt;N&lt;/i&gt;)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터 수가 2개로 늘면, 연산 횟수는 2배보다 조금 더 증가.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 49.6512%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #006dd7;&quot;&gt;&lt;b&gt;O(&lt;i&gt;N&lt;/i&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;^ 2)&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;연산 횟수가 데이터 수의 제곱이다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 49.6512%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #006dd7;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;O(2 ^&amp;nbsp;&lt;/span&gt;&lt;i&gt; N)&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50.3488%; height: 18px;&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;지수적 증가로 사용하기엔 비 요율적이다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;자료구조 종류&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;자료구조의 종류로는 형태에 따라 크게 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;선형 구조(Linear Data Structure)와 비선형 구조(Non-linear Data Structure)&lt;/b&gt;&lt;/span&gt;로 나뉜다. 초급자 기준으로 우리가 작성하고 있는 대부분의 구조는 선형 구조라고 할 수 있다. 쉽게 말하면, for문 하나를 사용하여 작성하는 대부분의 코드들은 선형구조로 연속적인 데이터의 구조라고 할 수 있다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;선형 구조(Linear Data Structure)&lt;/span&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;선형 자료구조는 하나의 데이터 뒤에 다른 데이터가 하나 존재하는 자료구조로, &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터가 일렬로 연속적으로(순차적으로) 연결되어 있다.&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic'; color: #666666;&quot;&gt;&lt;b&gt;예시)&lt;/b&gt; 배열, 연결 리스트(linked list), 스택(stack), 큐, 덱(deque)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;비선형 구조(Non-linear Data Structure)&lt;/span&gt;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;비선형자료구조는 하나의 데이터 뒤에 다른 데이터가 여러개 올수있는 자료구조로&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터가 일직선상으로 연결되어 있지 않아도 된다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;예시)&lt;/b&gt; 트리(tree), 그래프(graph)&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;정리&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;위의 두 가지 종류에 대한 가장 큰 차이는 각각의 자료구조에 대한 기초 블로그에서 찾아보자. 사실 우리는 하나의 데이터에 대해 핸들링 할 수 있는 방법은 다양한다. 하나의 데이터가 아닌 여러 데이터의 경우에 다루는 방법은 더욱 많다고 할 수 있다. 우리가 살고 있는 빅데이터 시대에 데이터를 잘 다루기 위해서는 자료구조에 대한 공부는 필수적이라고 할 수 있겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>Data Structure</category>
      <category>기초 정리</category>
      <category>비선형 구조(Non-linear Data Structure)</category>
      <category>선형 구조(Linear Data Structure)</category>
      <category>자료구조</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/567</guid>
      <comments>https://han-py.tistory.com/567#entry567comment</comments>
      <pubDate>Wed, 7 Jun 2023 21:00:20 +0900</pubDate>
    </item>
    <item>
      <title>알고리즘(Algorithm) 기초 정리 - python</title>
      <link>https://han-py.tistory.com/13</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 알고리즘에 대해서 대부분 학교 수업이나 코딩 테스트를 위해 처음 접하는 경우가 많다. 그렇다면, 알고리즘이란 무엇이고 왜 알고리즘을 배워야 할까? 좋은 알고리즘이란, 간단하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;문제를 해결하는 것&lt;/li&gt;
&lt;li&gt;문제를 더 잘 해결하는 것&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cta3UL/btsj7KwLxiL/43PO7UMsQFWDtzJ5RXIECk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cta3UL/btsj7KwLxiL/43PO7UMsQFWDtzJ5RXIECk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cta3UL/btsj7KwLxiL/43PO7UMsQFWDtzJ5RXIECk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcta3UL%2Fbtsj7KwLxiL%2F43PO7UMsQFWDtzJ5RXIECk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;292&quot; height=&quot;74&quot; data-origin-width=&quot;292&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘을 사용하는 이유는 문제를 더 잘 해결하기 위함이다. 따라서 어떻게 문제를 잘 해결할 수 있을지 같이 고민해볼까 한다. 여기서 제공하는 실습 문제만 암기하여 빠르게 작성할 수 있다면, 알고리즘을 공부하기 위한 기초 코딩은 완성 되었다고 할 수있다. 사실 알고리즘을 공부하고자 하는 우리의 목표는 우리가 만들고자 하는 로직을 작성할 수 있게 함에 있다. 따라서 기초적인 알고리즘을 공부하기 전에 파이썬에 대한 기본적인 숙련도가 필요하다고 생각된다. 기본적으로 for문을 1000번이상은 적어보지 못했다면, 당신은 아직 파이썬을 제대로 공부해보지 못했다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/567&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;자료구조 확인하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;컴퓨터 알고리즘이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;컴퓨터 알고리즘이란, 컴퓨터가 어떤 문제를 해결하기 위해서 컴퓨터가 이해할 수 있는 방식으로 정리되어 있는 해결 방법.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면, 특정 문제를 해결하기 위해 정해둔 일련의 절차라고 할 수 있다. 좋은 알고리즘이란 문제를 잘 해결하는 것이라고 이야기한 적이 있다. 이는 모든 경우에도 잘못된 결과를 도출하면 안 되는 것을 의미한다. 즉, 우리는 모든 경우에 올바른 결괏값을 내는 알고리즘을 만들어야 한다. 정리하면, 우리가 특정 문제를 해결하기 위해 코드를 짠다면, 사실 모든 코드가 알고리즘이라 할 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;알고리즘을 왜 하지?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초입자의 관점이라면 가장 큰 관심사는 취업이라고 생각이 된다. 우리가 좋은 회사에 취업을 한다면, 같이 일할 동료들의 대부분은 대표적이 알고리즘은 당연히 알고 있고, 아래와 같은 대화를 편하게 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;BFS로 접근하자&lt;br /&gt;분할정복을 사용하자.&lt;br /&gt;여기선 O(lg N)으로 해보자&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 내용에 대해서 알고리즘에 대한 지식 없이는 원활한 소통이 어렵다. 또한 초급 단계에서는 알고리즘을 공부하다보면 자연스럽게 기초코딩 능력도 향상된다. 이 글에서는 알고리즘을 공부하기 위한 최소한의 코딩 능력을 향상시켜줄 수 문제들을 준비했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;파이썬 특징&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파이썬은 순차적으로 코드를 실행한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686018284634&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(3)
print(4)
print(1)

```
3
4
1
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파이썬은 들여 쓰기를 사용한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PEP 8(파이썬 스타일 가이드)에 따르면, 공백 4개를 사용하는 것을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개발자들이 작업 시 코드 스타일이 다르면 코드에 일관성이 없어진다. 파이썬에서는 PEP8 문서를 통해 가이드를 제공해 준다. 우리는 함수를 쓸 때 왜 스네이크(snake_case)를 쓸까? 클래스를 쓸 때는 왜 카멜 케이스(CamelCase)를 쓸까? 이는 PEP8에서 권장해 주는 방식을 쓰고 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파이썬은 모두가 객체로 이루어진 언어이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수, 함수, 클래스, 모듈 등등이 모두 객체이기 때문에 변수 자체도 값을 가지지 않는다. 변수는 객체를 참조하는 값을 가지고 있는 것이다. 모든 객체는 메모리를 가지고, 객체를 구분할 수 있는 객체의 고유번호를 가진다. 아래의 예를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686020562325&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = 9
print(id(x)) # 4367483376
print(id(9)) # 4367483376&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id() 함수는 객체의 식별 번호(identity)를 반환해 주는 함수이다. 위의 결과를 보면, 9의 값이 x로 복사하여 들어가는 것이 아니라, 식별 번호만 복사되어 들어간다고 할 수 있다. 파이썬은 함수 시작/종료 시 객체가 생성 소멸되지 않는다는 점 정도는 이해하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;알고리즘 맛보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;input() 함수&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키보드로 문자열을 입력받아 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 문자열을 적은 후에 enter를 누르면 반환된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686017885043&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 예시1
print('문자열을 적으세요 : ')
val = input()



# 예시2
val = input('문자열을 적으세요 : ')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 1과 예시 2는 동일하다. 정리하면 input() 내부에 string을 적으면 print를 생략해서 적는 것이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실습 ) 두 정수의 합 구하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1686017852448&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;first_data = int(input('첫 번째 정수를 입력하시오 : '))
second_data = int(input('두 번째 정수를 입력하시오 : '))
print(f`두 정수의 합은 {first_data + second_data} 입니다`)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;실습 )&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;input 데이터를 변수에 넣기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685888117236&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'''
#input
2 4 5
'''


input_data = map(int, input().split(&quot; &quot;))
print(input_data)
# [2, 4, 5]

[a, b, c] = map(int, input().split(&quot; &quot;))
print(a, b, c)
# 2 4 5&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;조건문 실습으로 이해하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;산술 연산자(operator)와 피연산자(operand)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;산술 연잔사는 +나 -, *, / 같은 기호이고, 연산 대상을 피연산자라고 한다. 피연산자의 개수에 따라 아래와 같이 나늰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단항 연상자(unary operator) -&amp;nbsp; ex) a&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이항 연산자(binary operator) - ex) a &amp;gt; b&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삼항 연산자(ternary operator) - ex) a if b else c&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686016217270&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 이 코드와 아래의 코드는 같습니다.
if (조건문):
    t = a
else:
    t = b
print(t)

# 파이썬의 유일한 삼항 연산자 (if ~ else)
t = a if (조건문) else b
print(t)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 조건문이 참이면 a가 프린트되고, 거짓이면 b가 print 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;조건문 최적화?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;실습 )&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;두 정수를 오름차순으로 변경하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;gt; a, b를 입력받았다. 나는 a보다 b가 더 큰 수가 들어가도록 코드를 변경하고 싶다. 어떻게 해야 할까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단일 대입문 이해하기&lt;/p&gt;
&lt;pre id=&quot;code_1685965824585&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = int(input('정수 a 입력하시오 : '))
b = int(input('정수 b 입력하시오 : '))

if a &amp;gt; b:
    a, b = b, a
    
print(f`a값 : {a} , b값 : {b}`)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬 부분에서 다시 하겠지만, 위의 단일 대입문을 사용하지 않는다면 아래와 같은 방식으로 a와 b의 값을 변경해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685967463619&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = int(input('정수 a 입력하시오 : '))
b = int(input('정수 b 입력하시오 : '))

temp = a # 임시 변수 temp를 사용하여 값 변경
a = b
b = temp&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;반복문 실습으로 이해하기&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;range() 함수&lt;/b&gt; : 이터러블 객체(반복할 수 있는 객체)를 다루는 함수이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;+ 대표적인 이터러블 자료형 - list, str, tuple 이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;range(n) - 0 이상 n 미만인 수를 차례로 나열하는 수열&lt;/li&gt;
&lt;li&gt;range(a, b) - a 이상 b 미만인 수를 차례로 나열하는 수열&lt;/li&gt;
&lt;li&gt;range(a, b, step) - a 이상 b 미만인 수를 step 간격으로 나열하는 수열&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;실습 )&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;1부터 n까지의 정수의 합 구하기(while)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685965127303&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n = int(input('1부터 n까지 정수합을 할 n을 적어주세요 : '))

sum_cnt = 0
i = 1

while i &amp;lt;= n:
    sum_cnt += i
    i += 1

print(f`총합은 {sum_cnt}입니다`)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;실습 )&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;1부터 n까지의 정수의 합 구하기(for)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685965153498&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n = int(input('1부터 n까지 정수합을 할 n을 적어주세요 : '))

sum_cnt = 0

for i in range(1, n + 1):
    sum += i

print(f`총합은 {sum_cnt}입니다`)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;실습 )&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;1부터 n까지의 정수의 합 구하기 (고등 수학 이용하기 - 가우스 덧셈)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685965336956&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n = int(input('1부터 n까지 정수합을 할 n을 적어주세요 : '))

sum_cnt = n * ( n + 1 ) // 2


print(f`총합은 {sum_cnt}입니다`)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;실습 )&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;a부터 b까지의 합을 구하시오&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685966206365&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = int(input('정수 a 입력하시오 : '))
b = int(input('정수 b 입력하시오 : '))

if a &amp;gt; b:
    a, b = b, a

sum_cnt = 0

for i in range(a, b + 1):
    sum += i

print(f`총합은 {sum_cnt}입니다`)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;반복문 조건문 최적화하기&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 모든 반복문 내부의 조건을 통해 하나씩 하는 것은 일반적인 코딩 법이다. 반복되는 것이 있다면, //와 %를 활용하여 줄여주면 효율적인 코딩이 가능하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;진수&lt;/h3&gt;
&lt;pre id=&quot;code_1685888117237&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val = 40
b = format(val, '#b') # 10진수를 2진수로 변환
o = format(val, '#o') # 10진수를 8진수로 변환
h = format(val, '#x') # 10진수를 16진수로 변환

b = format(val, 'b')   # 10진수를 2진수로 변환 (Ob 접두 생략)
o = format(val, 'o')  # 10진수를 8진수로 변환 (Oo 접두 생략)
h = format(val, 'x')  # 10진수를 16진수로 변환 (Ox 접두 생략)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2,4,16진수는 위와 같이 라이브러리를 제공한다. 그러나 그 외는 아래와 같이 직접 구현해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1685888117238&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;number = int(input(&quot;숫자를 입력하세요: &quot;))
n = int(input(&quot;변환할 진수를 입력하세요: &quot;))

answer = &quot;&quot;

while number // n &amp;gt;= 1:
    remain = number % n
    number = number // n
    answer = str(remain) + answer
    if number &amp;lt; n :
        answer = str(number) + answer

print(&quot;변환 값: %s(%s)&quot; % (answer, n))&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;특정 기준에 따라 변형 후 분류하고, 처음 데이터의 값으로 분류하기.&lt;/h3&gt;
&lt;pre id=&quot;code_1685888117238&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;input_data = ['a12dd', 'B43fdASd', 'c35adf', 'b12asd', 'A56asdf', 'a1231asdf']

# 문제에 맞게 데이터 전처리(아래에서는 데문자를 다 소문자로 바꿈)
mid_data = ['a0012dd', 'b0043fdasd', 'c0035adf', 'b0012asd', 'a0056asdf', 'a1231asdf']
res_data = [[a, 0012, dd], [b, 0043, fdasd], [c, 0035, adf]...]

#res_data로 크기 배교후 성립하면 위치를 바꾸는 구조
def chang_data(data):
    for i in range(3):
        for j in range(len(data)):
            for k in range(j+1, len(data)):
                if data[j][i] &amp;gt; data[k][i]:
                    res_data[j], res_data[k] = res_data[k], res_data[j]
                    input_data[j], input_data[k] = input_data[k], input_data[j] # 같이 넣어준다.&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;중복된 list 지우기&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;셋으로 만들면, 중복된 부분이 없어진다. 그 후에 다시 list로 만들어 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1685888117239&quot; class=&quot;lsl&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = [1, 1, 2, 3, 5, 2]
a = list(set(a))&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;Dictionary 자유롭게 다루기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;dicrionary의 key값만 print 하시오&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685888117240&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; x = {'a': 10, 'b': 20, 'c': 30, 'd': 40}
&amp;gt;&amp;gt;&amp;gt; for i in x:
...     print(i, end=' ')
...
a b c d&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그냥 하면 위처럼 key만 출력된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686017210637&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; x = {'a': 10, 'b': 20, 'c': 30, 'd': 40}
&amp;gt;&amp;gt;&amp;gt; for key in x.keys():
...     print(key, end=' ')
...
a b c d&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;.keys()를 활용하면 key만 출력된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;dicrionary의 key, value값을 print 하시오&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1685888117240&quot; class=&quot;yaml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; x = {'a': 10, 'b': 20, 'c': 30, 'd': 40}
&amp;gt;&amp;gt;&amp;gt; for key, value in x.items():
...     print(key, value)
...
a 10
b 20
c 30
d 40&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;.items()를 활용하면 key와 value 값이 나눠서 출력된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685888117242&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; x = {'a': 10, 'b': 20, 'c': 30, 'd': 40}
&amp;gt;&amp;gt;&amp;gt; for value in x.values():
...     print(value, end=' ')
...
10 20 30 40&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;values를 활용하면 value만 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;아스키코드 변환하기&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ord(str) =&amp;gt; int&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자를 인자로 받고, 해당 문자에 해당하는 유니코드를 정수 반환한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;chr(int) =&amp;gt; str&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수를 인자로 받고, 해당 정수에 해당하는 유니코드 문자를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686017511967&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(ord('a')) # 97
print(chr(97)) # 'a'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기초적인 파이썬 문제에 대한 정리를 해보았다. 알고리즘에 대한 개념이해도가 높다는 것은 사실상 코딩을 잘하는 것이다. 위의 문제들을 쉽게 풀 수 있다면, 초급을 넘어섰다고 할 수 있겠다. 이제 다음으로 다른 알고리즘에 대한 개념에 대해 이해하러 가보자.&lt;/p&gt;</description>
      <category>알고리즘/알고리즘 종류</category>
      <category>Algorithm</category>
      <category>Python</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/13</guid>
      <comments>https://han-py.tistory.com/13#entry13comment</comments>
      <pubDate>Tue, 6 Jun 2023 12:17:23 +0900</pubDate>
    </item>
    <item>
      <title>[react] Error 처리하기 기초 정리</title>
      <link>https://han-py.tistory.com/560</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프런트엔드 error를 확인하는 방법은 여러 가지가 있다. 우리는 기본적으로 개발자도구에 console에 발생하는 에러를 통해서 주로 error를 확인한다. 하지만 무작정 error handling을 try catch로 하는 것은 좋은 방식이 아니다. try catch로 해야 하는 부분과 Error boundaries를 활용하는 방법을 알아보고, 리액트라는 선언형 코드에서 사용하는 코드에 대해 알아보자. 이 글을 통해 스스로 내 프로젝트에 error를 넣어주는 방식에 대한 기초를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zLzhb/btr4uKfQiXq/eam45hwJj7bmBKpKDT0ln0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zLzhb/btr4uKfQiXq/eam45hwJj7bmBKpKDT0ln0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zLzhb/btr4uKfQiXq/eam45hwJj7bmBKpKDT0ln0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzLzhb%2Fbtr4uKfQiXq%2Feam45hwJj7bmBKpKDT0ln0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;328&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. try catch&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;javascript를 배운사람은 다 알 수 있는 에러 처리 방법이다. 보통은 nodejs 백엔드에서 api 호출 시 많이 사용한다. 그러면 react에서도 사용하면 될까? 반은 맞고 반은 아니다. react는 선언형(declarative) 프로그램이다. 선언형이기 때문에 세부로직보다는, 어떤 컴포넌트가 렌더링이 될지에 대해 더욱더 생각한다. 더 쉽게 이야기하면, JSX라는 캡슐화된 코드를 사용하기 때문에, JSX의 로직을 모르고 사용하고 싶을 때 선언하여 사용하면 되는 것이다. 기본적으로 try catch를 사용하는 시기는 선언형 보다는 명령형(&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;imperative&lt;/span&gt;) 프로그램에서 많이 사용한다. 선언적 특성을 가진 리액트는 아래에서 소개할 Error boundaries를 사용한다. 만약 setState에 의한 componentDidUpdate 에서 에러가 발생할 경우에 우리는 Error boundaries에서 Error를 확인하는 것이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 try catch를 react에서 사용해야하는 경우도 있다. 바로 &lt;b&gt;Event Handler&lt;/b&gt;의 경우이다. Event Handler가 아닌, 선언형에서는 Error boundaries를 사용하면 된다. 다시 말하면, 선언형이라는 말은 JSX와 같이 렌더링 되는 과정이나, lifecycle method (componentDidUpdate, useEffect 등)에 관련된 코드인 경우에만 해당이 된다고 볼 수 있다. 정리하면,&amp;nbsp;&lt;b&gt;Event Handler&lt;/b&gt;는 렌더링 과정에서 발생하는게 아니라, 렌더링 이후에 특정 행동에 의해서 발생한다. 이는 선언형이 아닌 명령형이라고 할 수 있다. 따라서 Error boundaries가 아닌, try catch로 에러를 핸들링하는 것이 맞다고 할 수 있다. 아래의 예시로 try catch에서 사용되는 Event Handler에 대한 이해를 높여 보자.(공식문서 예시)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679146979691&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    try {
      // Do something that could throw
    } catch (error) {
      this.setState({ error });
    }
  }

  render() {
    if (this.state.error) {
      return &amp;lt;h1&amp;gt;Caught an error.&amp;lt;/h1&amp;gt;
    }
    return &amp;lt;button onClick={this.handleClick}&amp;gt;Click Me&amp;lt;/button&amp;gt;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보면 클릭 handleClick 부분에서 try catch 문이 활용 되었고, error 발생 시 상태를 state에 저장하는 것을 알 수 있다. 사실 이러한 방식은 우리가 자주 사용하는 방식이다. react와 같은 선언형에서는 아래에서 소개할 Error boundaries를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Error boundaries Lifecycle method&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 React에서 제공하는 Error boundaries에 대한 코드를 보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685351423791&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null, errorInfo: null };
  }

  componentDidCatch(error, errorInfo) {
    // Catch errors in any components below and re-render with error message
    this.setState({
      error: error,
      errorInfo: errorInfo,
    });
    // You can also log error messages to an error reporting service here
  }

  render() {
    if (this.state.errorInfo) {
      // Error path
      return (
        &amp;lt;div&amp;gt;
          &amp;lt;h2&amp;gt;Something went wrong.&amp;lt;/h2&amp;gt;
          &amp;lt;details style={{ whiteSpace: &quot;pre-wrap&quot; }}&amp;gt;
            {this.state.error &amp;amp;&amp;amp; this.state.error.toString()}
            &amp;lt;br /&amp;gt;
            {this.state.errorInfo.componentStack}
          &amp;lt;/details&amp;gt;
        &amp;lt;/div&amp;gt;
      );
    }
    // Normally, just render children
    return this.props.children;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보면 사실 단순하다. compojnentDidCatch method에서 error와 errorInfo 받아서 state 상태로 넣어주긴만 하면 된다. Error boundary에서 사용되고 있는 method들은 위의 예시에서 볼 수 있는 componentDidCatch 뿐만 아니라, getDerivedStateFromError 등등이 있다. 관련 method들은 아래에서 하나씩 확인해 보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 현재 frontend의 대세는 class가 아니라 함수형이다. 그런데 왜 Error boundaries는 함수가 아닌 클래스일까? 왜냐하면 아래의 method들이 아직 React 진형에서 Hook을 만들지 않았기 때문이다. 조금 기다려보면, hook으로 나올 것으로 기대된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;componentDidCatch()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 lifecycle method는 하위 컴포넌트에서 에러가 발생 시 아래의 두개의 인자를 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679190010402&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;componentDidCatch(error, info)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;error&lt;/b&gt; : 발생된 error로 try catch의 error와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;info&lt;/b&gt; : error가 발생한 컴포넌트에 대한 정보와 componentStack key가 포함된 객체이다. componentStack이라는 것은 쉽게 말해서 어느 component에서 error가 발생하는지 표현하는 것이다. 예를 들면, react의 경우는 진입점인 index.js부터 app.js 와 같이 발생한 컴포넌트를 추적해서 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면, componentDidCatch의 첫번째 인자는 error의 내용을 나타내고, 두 번째 인자는 error의 위치를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getDerivedStateFromError()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 lifecycle method는 하위 자손 컴포넌트 오류가 발생 했을 때 호출된다. 특히 변화된 state값을 반드시 반환해야 한다. 아래가 예시 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1679287791989&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // state를 갱신하여 다음 렌더링에서 대체 UI를 표시합니다.
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      // 별도로 작성한 대체 UI를 렌더링할 수도 있습니다.
      return &amp;lt;h1&amp;gt;Something went wrong.&amp;lt;/h1&amp;gt;;
    }

    return this.props.children;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getDerivedStateFromError와 componentDidCatch의 차이점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getDerivedStateFromError()는 기본적으로 render 단계에서 호출되기 때문에 side effect를 발생시키면 안된다.&amp;nbsp; side effect와 관련된 호출은 componentDidCatch()를 사용한다. 따라서 error 로그 기록을 호출하고 싶다면, getDerivedStateFromError가 아닌 componentDidCatch에서 사용하여 log를 남기면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Error boundary 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 이 글을 읽는 사람들은 Error boundary 사용법에 대해 이해를 하기위해 이 글을 읽고 있을 것이다. 그리고 에러가 발생하면, 전체 에러발생으로 에러화면이 발생하면 안 된다. 특정 화면만 에러상황에 따라 화면이 내가 의도한 데로 변화하게 도움을 주는 것이 Error boundary라고 할 수 있다. 예를 들어 웹툰을 보고 있다. 특정 웹툰에서만 데이터를 못 들고 왔다면, 특정 웹툰이 표시되는 컴포넌트에서만 재시도 버튼을 누를 수 있게 사용자를 유도하면 된다. 아래의 간단한 사용법을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685353099869&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;hanpy ErrorBoundary TEST&amp;lt;/p&amp;gt;
      &amp;lt;ErrorBoundary&amp;gt;
        &amp;lt;아무컴포넌트1 /&amp;gt;
      &amp;lt;/ErrorBoundary&amp;gt;
      &amp;lt;ErrorBoundary&amp;gt;
        &amp;lt;아무컴포넌트2 /&amp;gt;
      &amp;lt;/ErrorBoundary&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

const root = ReactDOM.createRoot(document.getElementById(&quot;root&quot;));
root.render(&amp;lt;App /&amp;gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;아무컴포넌트1 /&amp;gt; 과 &amp;lt;아무컴포넌트2 /&amp;gt;에서 error가 발생하면, 화면 전체가 error가 발생하는 게 아니라 나의 의도처럼, 에러가 발생한 컴포넌트의 UI만 변경이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 ErrorBoundary를 커스텀 하는 방법은 더욱 많다. react-query 결합하기, API 비동기 (async) 추가, 컴포넌트 주입, error reset, SSR render 제외하기 등등 다양하게 활용하는 방법에 대해서는 추후 작성 될&amp;nbsp;&lt;b&gt;Error boundary 심화&lt;/b&gt; 글에서 상황에 따른, 다양한 예시로 적어보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Error boundary</category>
      <category>frontend error 처리</category>
      <category>NextJS</category>
      <category>react</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/560</guid>
      <comments>https://han-py.tistory.com/560#entry560comment</comments>
      <pubDate>Mon, 29 May 2023 18:52:10 +0900</pubDate>
    </item>
    <item>
      <title>[React] switch문 심화 정리</title>
      <link>https://han-py.tistory.com/566</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt; JavaScript에서 if문과 비슷한 switch 문에 대해 적어보려 한다. 우리는 switch문을 언제 사용하고, 잘 사용하고 있는지에 대해 고민해 보면 좋을 것 같다. 기본적인 문법 설명은 생략한다. eslint관점에서 우선 설명을 풀어본다. 그리고 TypeScript에서 SRP(Single Responsibility Principle) 원칙으로 switch를 어떻게 사용해야 할지에 대한 고민을 함께 해본 후, 추가로&amp;nbsp; React에서 활용해 보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8GTti/btsgEaGzXCV/YBZU9vXWXGhYgotDkgEDkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8GTti/btsgEaGzXCV/YBZU9vXWXGhYgotDkgEDkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8GTti/btsgEaGzXCV/YBZU9vXWXGhYgotDkgEDkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8GTti%2FbtsgEaGzXCV%2FYBZU9vXWXGhYgotDkgEDkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;330&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 우리는 switch 문에서 default값을 꼭 넣어줘야 에러가 발생하지 않는다고 생각한다. 왜냐하면, 개발자도 사람이기 때문에 실수로 모든 조건을 넣지 않아 예외가 발생 할 수 도 있기 때문이다. 또한, eslint 관점에서 default문을 넣어주지 않는, 아래  코드에서는 eslint에서 에러를 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684254392441&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;switch (a) {
    case 1:
        /* code */
        break;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러를 출력 막기위해서는 아래와 같이 default를 넣어주기도 하고, 의도적으로 생략을 명시하기도 한다. eslint에서는 아래의 경우에는 에러 출력을 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684254446161&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// default 추가
switch (a) {
    case 1:
        /* code */
        break;
        
    case 2:
        /* code */
        break;
        
    default:
    // do nothing
}


// 물론 아래와 같이 의도적으로 생략을 명시하는 것도 좋다.
switch (a) {
    case 1:
        /* code */
        break;
        
    case 2:
        /* code */
        break;
        
    // no default
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 eslint를 끈다면, default 값을 적지 않아도 application은 잘 돌아간다. 그렇다면, 왜 default값을 굳이 적어야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단지 개발자의 실수를 막기 위해서 default값을 넣으라고 eslint에서 default-case로 막아주는 것일까? 여기서 조금 더 생각이 필요하다. 개발자의 목표는 우리가 만드는 application이 유지보수가 쉽게 가능하도록 만드는 것에 있다. 그런데 위의 코드는 확장성의 측면에서는 전혀 좋은 코드가 아니다. 그 이유는 바로 코드 자체에서 &lt;b&gt;우리가 예상하는 결과가 아닌 값이 들어올 수도 있기 때문이다.&lt;/b&gt; 만약, switch문에 들어온 a가 1이나 2가 아니라면, 예측 불가능한 코드가 된다. 이는 매우 크리티컬 한 Error를 야기할 수 있다. 그렇다면, 이러한 코드는 좋은 코드라 할 수 있을까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 생각을 해보자. 다른 서버에서 API에서 값을 받아서 switch 로직을 거쳐 비즈니스 로직을 실행한다고 해보자. 갑자기 예상하지 못한 새로운 값이 추가된다면 어떻게 대응하여 코드를 짜야할까? Typescript를 통한 예시를 보고 조금 더 고민해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Typescript에서의 switch 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서는 단순하게 default를 switch에 넣었다.&amp;nbsp; default를 넣는 순간 case에서 정의되지 않는 입력 값은 default로 가게 된다. 이러한 코드는 괜찮아 보일지도 모른다. 하지만, javascript가 아닌 다른 언어에서는 pattern matching이라는 개념이 나온다. 간단히 말하면, pattern matching이란, 다른 인풋값에 따라 matching 되는 다른 아웃풋 값을 연결한다는 개념이다. 이는 switch 문에서 default를 사용하지 않고 모든 경우의 수를 case에 넣어주는 것과 사실 같다. pattern matching 측면에서 switch문을 작성을 한다면, co-lacation를 해치지 않는 선에서 유지보수가 가능한 코드가 된다고 할 수 있다. 이러한 방법은 사실 TypeScript에서&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Union type&lt;/b&gt;&lt;/span&gt;으로 작성이 가능하다. 그렇다면 아래의 코드를 보면서 조금 더 이해도를 높여보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 코드는 typescript 공식문서에서 자주 볼 수 있는 코드이다. 일반적인 shape에 따른 string 값을 리턴하는 함수가 있다. 아래의 코드는 어떤 Error를  TypeScript에서 발생시킬까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684659659852&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Circle = {
  kind: 'circle'
  radius: number
}

type Rectangle = {
  kind: 'rectangle'
  width: number
  height: number
}

type Shape = Circle | Rectangle

const renderShape = (shape: Shape): string =&amp;gt; {
  switch (shape.kind) {
    case 'circle':
      return 'I am a circle'
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 Union type으로 Shape를 지정했기 때문에 아래의 에러가 발생하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MDLJA/btsgEgf6tpe/ctFqZaqjBaplr2kXkzJUK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MDLJA/btsgEgf6tpe/ctFqZaqjBaplr2kXkzJUK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MDLJA/btsgEgf6tpe/ctFqZaqjBaplr2kXkzJUK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMDLJA%2FbtsgEgf6tpe%2FctFqZaqjBaplr2kXkzJUK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;713&quot; height=&quot;57&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;typescript의 모든 경우의 수의 return 값을 테스트한다. 위에서 발생한&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;https://www.typescriptlang.org/tsconfig#noImplicitReturns&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Error&lt;/a&gt;는 Rectangle의 경우에 return값이 없다는 경우에 대한 Error라고 할 수 있겠다. 이러한 TypeScript Error는 우리가 위에서 언급했던, pattern matching관점에서의 개발자의 실수를 방지해 준다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이러한 애러가 발생했을 때, &lt;b&gt;여기서 중요한 것은 default를 switch 문에 추가하면 안 된다는 것이다. Type Script는 인풋 값에 대한 경우한 판단하지, 비즈니스 로직 측면에서, 특정 경우에 대한 case가 빠졌다고 말해주지 않는다.&lt;/b&gt; 즉, pattern matching에 관점에서는 Shape는 Circle과 Rectangle으로 2가지가 가능하기 때문에, default값을 switch문에 추가하는 것이 아니라, case 부분에 rectangle부분을 추가해 줘야 한다는 것이다. case가 많아질수록 이러한 실수는 치명적인 Error를 발생할 수 있다. 또한, Error를 찾는데 더 많은 시간이 걸리 수도 있다. 위의 &lt;span style=&quot;color: #006dd7;&quot;&gt;renderShape&lt;/span&gt; 함수를 수정해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684667784992&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const renderShape = (shape: Shape): string =&amp;gt; {
  switch (shape.kind) {
    case 'circle':
      return 'I am a circle'
    case 'rectangle':
      return 'I am a rectangle'
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 typescript에서는 예측 불가능한 값을 자동으로 타입을 만들어 주 지 않음을 이해하자. 그렇기 때문에 default 값을 가능하면 사용하지 않는 것이 좋다. 더욱 직관적으로 말하면, default를 사용하는 것은, any 쓰는 것과 같다고 할 수 있을 것 같다. 그러면 default를 그냥 사용하면 안 되는 것일까? 그건 아니다. default도 ErrorBoundary와 비슷한 개념으로 예외처리 시 유용하게 사용하기도 한다. 아래 글을 조금 더 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;[심화] React에 적용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 위의 switch가 적용된 함수는 TypeScript에서는 더 이상 수정 할 부분이 없다. 실제 현업에서는 java Script로 작성이 되어 있거나, API를 받아서 사용하는 경우에는, input 값을 typescript로&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;통제할 수 없는 경우가 많다.&lt;/b&gt;&lt;/span&gt; 즉, 여기서는 &lt;b&gt;TypeScript이 런타임 환경은 고려하지 않는다는 관점&lt;/b&gt;에서 글을 풀어보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684668714915&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const App = () =&amp;gt; {
  const [shapes, setShapes] = useState({})

  React.useEffect(() =&amp;gt; {
    getAPIShapes().then(setShapes)
  }, [])

  if (!shapes) {
    return &amp;lt;Loading /&amp;gt;
  }

  return (
    &amp;lt;section&amp;gt;
      {shapes.map((shape) =&amp;gt; (
        &amp;lt;Shape {...shape} /&amp;gt;
      ))}
    &amp;lt;/section&amp;gt;
  )
};

type Circle = {
  kind: 'circle'
  radius: number
};

type Rectangle = {
  kind: 'rectangle'
  width: number
  height: number
};

type Shape = Circle | Rectangle;

const Shape = (props: Shape): JSX.Element =&amp;gt; {
  switch (props.kind) {
    case 'circle':
      return &amp;lt;Circle radius={props.radius} /&amp;gt;
    case 'rectangle':
      return &amp;lt;Rectangle width={props.width} height={props.height} /&amp;gt;
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 위의 코드는 별 문제가 없어 보인다. 그러나, 이는 Circle / Rectangle라는 코드만 받을 때만에 한정해서 문제가 없는 react 코드이다. 만약 backend 팀에서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Circle / Rectangle&lt;span&gt;&amp;nbsp;가 아니라&lt;/span&gt;&lt;/span&gt; 갑자기 새로운 Square을 만들어서 호출한다면 어떻게 될까? 위의 로직에서는 undefined를 return 하게 되어 웹이 터지거나 예상할 수 없는 오류가 발생한다.(물론 react 18부터는 undefined를 return 에도 null로 판단하여 터지진 않는다..) 이러한 경우를 위해서 default가 필요한 것이다. 사실상  예측할 수 없는 부분을 handling 하는 측면에서 default가 필요하다고 볼 수 있는 것이다. default 부분에 Typescript의 never을 이 부분에 활용한다면 예측 불가능한 부분에 대한 handling이 가능하다. 우선은 TypeScript의 never에 대해 간단히 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TypeScript : never type&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;never 타입은 빈 타입으로 발생할 수 없는 타입을 나타내며, 기능 제약을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;허용할 수 없는 매개변수에 제한을 할 수 있다. : never 사용&lt;/li&gt;
&lt;li&gt;아무 값이나 전달하거나 아무 값도 전달하지 않으면 타입 에러가 발생한다. : never 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;never 타입의 사용하는 시기는 여러 가지가 있지만, 여기서는 else와 같은 도달할 수 없는 조건 타입에 사용이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684162513119&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function isCheck(arg: string | number): boolean {
  if (typeof arg === &quot;string&quot;) {
    return true;
  } else if (typeof arg === &quot;number&quot;) {
    return false;
  }
  return fail(&quot;Not Allow!&quot;); //Error
}
function fail(message: string): void { throw new Error(message); }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;isCheck&lt;/span&gt; 함수는 boolean을 리턴하도록 설정되어 있다. 그러나, fail에서 void로 리턴 없음을 설정하면, Error가 발상한다. (void형식에 boolean 형식을 할 당 할 수 없다.) 이러한 경우에 아래와 같이 never을 사용하여 해결 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684162614312&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function isCheck(arg: string | number): boolean {
  if (typeof arg === &quot;string&quot;) {
    return true;
  } else if (typeof arg === &quot;number&quot;) {
    return false;
  }
  return fail(&quot;Not Allow!&quot;);
}
function fail(message: string): never { throw new Error(message); }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;React 예외 API 처리하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 리엑트 코드는 circle과 rectangle 부분의 case만 나눠서 Union type으로 지정되어 있다. 그 외의 경우는 never type을 활용하여 default에 넣어보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684670742681&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const UnknownShape = ({ shape }: { shape: never }) =&amp;gt; (
  &amp;lt;Button&amp;gt;Retray Render&amp;lt;/Button&amp;gt;
)

const Shape = (props: Shape): JSX.Element =&amp;gt; {
  switch (props.kind) {
    case 'circle':
      return &amp;lt;Circle radius={props.radius} /&amp;gt;
    case 'rectangle':
      return &amp;lt;Rectangle width={props.width} height={props.height} /&amp;gt;
    default:
      return &amp;lt;UnknownShape shape={props} /&amp;gt;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드는 기본적으로 서비스 장애를 발생시키지 않는다. 그리고 Frontend 개발자는 logo을 확인하여, 문제을 인지하여 빠르게 대응이 가능하다. 만약 Square부분이 추가된다고 하면 위의 코드에서 case를 추가해 주면 간단히 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 코드를 정리해 보면, 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684672365899&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import * as React from 'react'

type BaseShape = {
  id: number;
};
type Circle = BaseShape &amp;amp; {
  kind: &quot;circle&quot;;
  radius: number;
};
type Rectangle = BaseShape &amp;amp; {
  kind: &quot;rectangle&quot;;
  width: number;
  height: number;
};
type Square = BaseShape &amp;amp; {
    kind: &quot;square&quot;,
    length: 7
}
type Shape = Circle | Rectangle | Square;

const Circle = ({ radius }: Omit&amp;lt;Circle, &quot;kind&quot; | &quot;id&quot;&amp;gt;) =&amp;gt; (
  &amp;lt;div&amp;gt;I'm a circle with radius {radius}&amp;lt;/div&amp;gt;
);

const Rectangle = ({ width, height }: Omit&amp;lt;Rectangle, &quot;kind&quot; | &quot;id&quot;&amp;gt;) =&amp;gt; (
  &amp;lt;div&amp;gt;
    I'm a rectangle with width {width} and height {height}
  &amp;lt;/div&amp;gt;
);

const Square = ({ length }: Omit&amp;lt;Square, &quot;kind&quot; | &quot;id&quot;&amp;gt;) =&amp;gt; (
  &amp;lt;div&amp;gt;
    I'm a rectangle with width {length} and height 
  &amp;lt;/div&amp;gt;
);

const UnknownShape = ({ shape }: { shape: never }) =&amp;gt; &amp;lt;div&amp;gt;Unknown Shape&amp;lt;/div&amp;gt;

const Shape = (props: Shape): JSX.Element =&amp;gt; {
    switch (props.kind) {
        case 'circle':
            return &amp;lt;Circle radius={props.radius} /&amp;gt;
        case 'rectangle':
            return &amp;lt;Rectangle width={props.width} height={props.height} /&amp;gt;
        case 'square':
            return &amp;lt;Square length={props.length} /&amp;gt;
        default:
            return &amp;lt;UnknownShape shape={props} /&amp;gt;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면, 입력이 완전히 제어가 가능하다면, switch에서 default를 제외하는 코드를 작성하면 된다. 그리고 입력을 완전히 제어가 불가능하면, Typescript의 런타임 시 발생할 예외를 default를 추가하여 작성해 주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>JavaScript</category>
      <category>never type</category>
      <category>switch</category>
      <category>typescript</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/566</guid>
      <comments>https://han-py.tistory.com/566#entry566comment</comments>
      <pubDate>Mon, 22 May 2023 00:39:38 +0900</pubDate>
    </item>
    <item>
      <title>HANPY</title>
      <link>https://han-py.tistory.com/563</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 맹목적으로 글을 올리는 것을 잠시 멈추고, 나의 개발을 되돌아보는 시간을 가졌다. 목표한 한 가지가 끝났다. 다시 글을 적을 수 있는 시간이 생겼기에 첫 글을 올리기 전에 글을 남겨본다. 이전과는 다른 방향성을 가지고 글을 적어볼 예정이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-1.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M9rgX/btsgcgtLVYb/swCkKDghTBaaMf1GD1VMG1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M9rgX/btsgcgtLVYb/swCkKDghTBaaMf1GD1VMG1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M9rgX/btsgcgtLVYb/swCkKDghTBaaMf1GD1VMG1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM9rgX%2FbtsgcgtLVYb%2FswCkKDghTBaaMf1GD1VMG1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-filename=&quot;KakaoTalk_Photo_2023-02-26-11-40-27-1.jpeg&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 현 블로그를 삭제하고, 이전 자료를 리빌딩에 대한 고민을 했다.&amp;nbsp;여러 가지 고민 끝에 현 블로그를 유지하기로 마음먹었고, 다시 다음 스탭으로 나아가려 한다. 부족하지만, 제 글을 읽어주시는 많은 분들께 더욱 좋은 정보를 전달하도록 노력하면서 함께 성장하기는 바라본다.&lt;/p&gt;</description>
      <category>hanpy is back</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/563</guid>
      <comments>https://han-py.tistory.com/563#entry563comment</comments>
      <pubDate>Wed, 17 May 2023 01:08:02 +0900</pubDate>
    </item>
    <item>
      <title>[Nodejs/react] 깃헙(github)으로 로그인 /  깃헙(github) API로 데이터 불러오기</title>
      <link>https://han-py.tistory.com/559</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;구글 로그인, 카카오 로그인과 같이 깃헙으로 로그인 하는 방법에 대해 알아보도록 하자. 개념 정리보다는 react를 통해 빠르게 구현할 수 있도록 핵심만 간결하게 적어보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA3Pij%2Fbtr37tFdsNt%2FrAKBZjthamXVhrutwdWwcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;180&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 github이 아닌 &lt;b&gt;카카오&lt;/b&gt; 로그인 붙이는 방법은 아래의 내용을 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/417&quot;&gt;https://han-py.tistory.com/417&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678954158129&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[React/Nodejs] 카카오 로그인 연결하기&quot; data-og-description=&quot;카카오 로그인을 진행해 보자. 기본적인 작동 방식은 여기를 눌러서 확인하자. 기본 셋팅은 할수 있다 판단하고 글을 적어보겠다. 사실 쉽게 사용하기 위해 만들어진 라이브러리도 많다. 그러나&quot; data-og-host=&quot;han-py.tistory.com&quot; data-og-source-url=&quot;https://han-py.tistory.com/417&quot; data-og-url=&quot;https://han-py.tistory.com/417&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tRMzL/hyRWpUBy3T/tPZ2tqTuzU0ZFVa6kqHsak/img.png?width=800&amp;amp;height=541&amp;amp;face=0_0_800_541,https://scrap.kakaocdn.net/dn/k5toP/hyRXu7QDIR/FQeWsJBKmoks8xi4bkDNhK/img.png?width=800&amp;amp;height=541&amp;amp;face=0_0_800_541,https://scrap.kakaocdn.net/dn/OMN92/hyRWqTwj7e/AAjuQwh1wiNpxJungQMl8k/img.png?width=1323&amp;amp;height=811&amp;amp;face=0_0_1323_811&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/417&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://han-py.tistory.com/417&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tRMzL/hyRWpUBy3T/tPZ2tqTuzU0ZFVa6kqHsak/img.png?width=800&amp;amp;height=541&amp;amp;face=0_0_800_541,https://scrap.kakaocdn.net/dn/k5toP/hyRXu7QDIR/FQeWsJBKmoks8xi4bkDNhK/img.png?width=800&amp;amp;height=541&amp;amp;face=0_0_800_541,https://scrap.kakaocdn.net/dn/OMN92/hyRWqTwj7e/AAjuQwh1wiNpxJungQMl8k/img.png?width=1323&amp;amp;height=811&amp;amp;face=0_0_1323_811');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[React/Nodejs] 카카오 로그인 연결하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오 로그인을 진행해 보자. 기본적인 작동 방식은 여기를 눌러서 확인하자. 기본 셋팅은 할수 있다 판단하고 글을 적어보겠다. 사실 쉽게 사용하기 위해 만들어진 라이브러리도 많다. 그러나&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;han-py.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. github OAuth 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연한 말이지만 github에 oauth를 한다고 등록하는 과정이 우선 필요하다. &lt;a href=&quot;https://github.com/settings/developers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/settings/developers&lt;/a&gt; 로 접속을 해서 Apps를 하나 만들자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnuz2e/btr4hQFk4X3/s0klKzYPNYniLIGtfG3aI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnuz2e/btr4hQFk4X3/s0klKzYPNYniLIGtfG3aI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnuz2e/btr4hQFk4X3/s0klKzYPNYniLIGtfG3aI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdnuz2e%2Fbtr4hQFk4X3%2Fs0klKzYPNYniLIGtfG3aI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2558&quot; height=&quot;708&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진에서&lt;b&gt; new OAuth App&lt;/b&gt;을 클릭한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;1436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w3t5Z/btr4gVtzBp1/Ty8Gx9LQGK8ECnvmyxEnDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w3t5Z/btr4gVtzBp1/Ty8Gx9LQGK8ECnvmyxEnDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w3t5Z/btr4gVtzBp1/Ty8Gx9LQGK8ECnvmyxEnDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw3t5Z%2Fbtr4gVtzBp1%2FTy8Gx9LQGK8ECnvmyxEnDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;646&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;1436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 app 등록을 진행한다. 포함되어야할 내용을 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application name : 자유롭게 이름을 넣어주면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Homepage URL : 현재 &lt;b&gt;localhost&lt;/b&gt;로 테스트 할꺼니 localhost로 적어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Authorization callback URL&lt;/b&gt; : ( 중요 ) callback URL을 적어주는 부분이다. github 셋팅을 한 이후에 아래 부분에서 조금 더 자세히 먹어보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2082&quot; data-origin-height=&quot;1182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ePQGAE/btr4fyy389y/SGsmj34KWrURcWqY5uR7Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ePQGAE/btr4fyy389y/SGsmj34KWrURcWqY5uR7Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ePQGAE/btr4fyy389y/SGsmj34KWrURcWqY5uR7Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FePQGAE%2Fbtr4fyy389y%2FSGsmj34KWrURcWqY5uR7Ik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2082&quot; height=&quot;1182&quot; data-origin-width=&quot;2082&quot; data-origin-height=&quot;1182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 application을 생성을 하면, 위와같이 &lt;b&gt;clientID&lt;/b&gt;와 &lt;b&gt;client secrets&lt;/b&gt;를 알 수 있다. 이부분을 저장해 두자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. github OAuth 전체 흐름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 보통 아래의 버튼을 눌러서 내가 만든 페이지의 로그인을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA3Pij%2Fbtr37tFdsNt%2FrAKBZjthamXVhrutwdWwcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;351&quot; height=&quot;120&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 누르면, 아래의 페이지가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;1190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y4YNO/btr38pQc9Rw/z5Xlo9LK7LJC4MLTDw9xE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y4YNO/btr38pQc9Rw/z5Xlo9LK7LJC4MLTDw9xE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y4YNO/btr38pQc9Rw/z5Xlo9LK7LJC4MLTDw9xE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY4YNO%2Fbtr38pQc9Rw%2Fz5Xlo9LK7LJC4MLTDw9xE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;446&quot; height=&quot;771&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;1190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 사진에서 로그인을 완료하면, 내가 적은 &lt;b&gt;Authorization callback URL&lt;/b&gt;로 아래와 같이 code를 query로 담아서 redirect 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678949709381&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;http://localhost:3000/api/auth/oauth/github/callback?code=1ad6213abf955df1b007&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 query에 포함된 code를 가지고 와서 다시 accesstoken을 github에 요청한다. 그리고 받은 access-token을 통해 우리는 상대방의 정보를 가지고 와서 로그인 처리를 할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. code 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 버튼 클릭 시 github 로그인을 사용 할 수 있도록 만들어 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA3Pij/btr37tFdsNt/rAKBZjthamXVhrutwdWwcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA3Pij%2Fbtr37tFdsNt%2FrAKBZjthamXVhrutwdWwcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;351&quot; height=&quot;120&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 아이콘을 만드는 방법은 아래의 코드를 복사해서 사용하도록 하자. 간단히 버튼만 나오도록 css를 구성한것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678950096332&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import styles from &quot;@/styles/Login.module.css&quot;;
import { useRouter } from &quot;next/navigation&quot;;

const Login = () =&amp;gt; {
  const router = useRouter();
  const handleGithubLogin = () =&amp;gt; {
    router.push(
      `${process.env.NEXT_PUBLIC_GITHUB_ORIGIN_URI}/login/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_GITHUB_AUTHORIZE_CLIENT_ID}`
    );
  };
  return (
    &amp;lt;div className={styles.container}&amp;gt;
      &amp;lt;div onClick={handleGithubLogin} className={styles.githubone0}&amp;gt;
        &amp;lt;a className={styles.githubone1}&amp;gt;
          &amp;lt;div className={styles.githubone2}&amp;gt;
            &amp;lt;svg
              height=&quot;18&quot;
              viewBox=&quot;0 0 16 16&quot;
              width=&quot;32px&quot;
              className={styles.githubone3}
            &amp;gt;
              &amp;lt;path
                fillRule=&quot;evenodd&quot;
                d=&quot;M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38
      0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01
      1.08.58 1.23.82.72 1.21 1.87.87
      2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12
      0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08
      2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0
      .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z&quot;
              /&amp;gt;
            &amp;lt;/svg&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div className={styles.githubone4}&amp;gt;Log in with GitHub&amp;lt;/div&amp;gt;
        &amp;lt;/a&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Login;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 css는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678950192778&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.container {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.githubone0 {
  display: flex;
  flex-direction: column;
  width: 180px;
  margin-left: 20px;
}
.githubone1 {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  background-color: #24292e;
  font-family: &quot;Roboto&quot;, sans-serif;
  font-size: 14px;
  color: white;
  border-radius: 10px;
  text-decoration: none;
  cursor: pointer;
}
.githubone2 {
  margin: 1px;
  padding-top: 5px;
  min-height: 30px;
}
.githubone3 {
  fill: white;
  margin-top: 3px;
}
.githubone4 {
  margin-left: 5px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 중요한 것은 사실 버튼이 아니라 아래의 이동 시키는 url이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678950260110&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;`${process.env.NEXT_PUBLIC_GITHUB_ORIGIN_URI}/login/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_GITHUB_AUTHORIZE_CLIENT_ID}`&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;env 파일로 빼 논부분을 풀면, 아래와 같이 사용하면된다. 만약 위에서 저장했던 clientID가 abcde라면 아래와 같이 적어두면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/login/oauth/authorize?client_id=위에서&quot;&gt;https://github.com/login/oauth/authorize?client_id=abcde&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 끝이다. 그렇게 어려운 것은 없다. 위에서는 react(nextjs)를 활용하여 사용하였지만, 조금 더 직관적으로 html으로 표현하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678950913009&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;https://github.com/login/oauth/authorize?client_id=abcde&quot;&amp;gt;
 버튼
&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지만 이동시켜주면 아래와 같이 github이 알아서 로그인 처리를 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;1190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y4YNO/btr38pQc9Rw/z5Xlo9LK7LJC4MLTDw9xE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y4YNO/btr38pQc9Rw/z5Xlo9LK7LJC4MLTDw9xE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y4YNO/btr38pQc9Rw/z5Xlo9LK7LJC4MLTDw9xE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY4YNO%2Fbtr38pQc9Rw%2Fz5Xlo9LK7LJC4MLTDw9xE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;371&quot; height=&quot;642&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;1190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인이 완료가 되면 callback URL로 코드를 보내준다고 했다. 그러면 code 받는 로직을 참고용으로 간단히 적어보면, 아래와 같다. 사실 nextjs의 api 폴더에서 ssr로 만든 것이라 참고용으로 어려운 점은 없을 것이라 본다. 추후 nest 로 백엔드 만들예정이라, 만들면 추가를 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678951956097&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import type { NextApiRequest, NextApiResponse } from &quot;next&quot;;
import { getGithubAccessToken } from &quot;@/lib/auth/github&quot;;

export default async function handle(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === &quot;POST&quot;) res.end();
  if (req.method === &quot;GET&quot;) {
    try {
      const { access_token } = await getGithubAccessToken(req, res);
      res.setHeader(
        &quot;set-cookie&quot;,
        `token=${access_token}; path=/; samesite=strict; httponly; secure;`
      );
      return res.redirect(`/analyze`);
    } catch (error) {
      console.error(&quot;error: &quot;, error);
      // 임시
      res.status(200).json(new Error());
    }
  }
}


async function getGithubAccessToken(req: NextApiRequest, res: NextApiResponse) {
  const { code } = req.query;
  try {
    const { data } = await axios.post(
      `${process.env.GITHUB_ORIGIN_URI}/login/oauth/access_token`,
      {
        client_id: process.env.GITHUB_AUTHORIZE_CLIENT_ID,
        client_secret: process.env.GITHUB_AUTHORIZE_CLIENT_SECRET,
        code: code,
      },
      {
        headers: { Accept: &quot;application/json&quot; },
      }
    );
    return data;
  } catch (err: any) {
    console.log(err.message);
    res.end();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 적으면 위와 같다. code를 받아서 다시 access_token을 요청을 하고, cookie로 담는 로직이다. cookie 담는 부분은 로그인 방식에 따라 정의하면 된다. 정리하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;query에서 code를 뽑는다.&lt;/li&gt;
&lt;li&gt;code와 client_id, client_secret을 담아서 호출하여 access-token을 받는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 로직이 끝이다. access-token 호출 부분을 조금 더 설명하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678952321441&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    const { data } = await axios.post(
      `https://github.com/login/oauth/access_token`,
      {
        client_id: process.env.GITHUB_AUTHORIZE_CLIENT_ID,
        client_secret: process.env.GITHUB_AUTHORIZE_CLIENT_SECRET,
        code: code,
      },
      {
        headers: { Accept: &quot;application/json&quot; },
      }
    );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;client_id, client_secret은 app 등록 시 발급 받은 내용이고, code는 callback url로 받은 것이다. 추가적으로 headers에 application/json을 추가해 줘야 json으로 return 된다. 포함하지 않는다면, return이 string으로 온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 사용자 정보 불러오기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://api.github.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://api.github.com/&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 페이지에 접속해보면, 사용가능 한 API를 확인가능하다. 그리고 관련 자세한 정보에 대한 설명은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.github.com/ko/rest&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.github.com/ko/rest&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678953132352&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;GitHub REST API 설명서 - GitHub Docs&quot; data-og-description=&quot;통합을 만들고, 데이터를 검색하고, 워크플로를 자동화하고, GitHub REST API를 사용하여 빌드합니다.&quot; data-og-host=&quot;docs.github.com&quot; data-og-source-url=&quot;https://docs.github.com/ko/rest&quot; data-og-url=&quot;https://ghdocs-prod.azurewebsites.net/ko/rest&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/boQtir/hyRWqMLpAA/GDdi4uwIfPPEkpHwyKw6C0/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200&quot;&gt;&lt;a href=&quot;https://docs.github.com/ko/rest&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.github.com/ko/rest&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/boQtir/hyRWqMLpAA/GDdi4uwIfPPEkpHwyKw6C0/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub REST API 설명서 - GitHub Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;통합을 만들고, 데이터를 검색하고, 워크플로를 자동화하고, GitHub REST API를 사용하여 빌드합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인을 위한 user정보를 가지고 오려면, 아래의 내용을 참고하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678953532830&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    const { data } = await axios.get(
      `https://api.github.com/user`,
      {
        headers: { 
          Accept: &quot;application/json&quot; 
          Authorization: 'Bearer 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',

        },
      }
    );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git의 repo 정보를 가지고 오려면 아래의 내용을 참고해서 만들면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678953534826&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    const { data } = await axios.get(
      `https://api.github.com/user/repo`,
      {
        headers: { 
          Accept: &quot;application/json&quot; 
          Authorization: 'Bearer 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',

        },
      }
    );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의 할 점은 Bearer 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 부분에 Bearer 뒤에 access_token을 넣어주는 것인데, 너무 당연한 이야기 지만, Bearer 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을 그대로 못쓴다. 만료된 토큰이니, access_token을 발급하여 &lt;span style=&quot;color: #5733b1;&quot;&gt;&lt;b&gt;Bearer 발급한토큰&lt;/b&gt; &lt;/span&gt;을 넣어줘야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다양한 프로젝트에 github OAuth를 적용하고, 다양한 방법으로 github 데이터를 사용해 보자.&lt;/p&gt;</description>
      <category>Web/git</category>
      <category>github accessToken</category>
      <category>github api</category>
      <category>github login</category>
      <category>github oauth</category>
      <category>login</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/559</guid>
      <comments>https://han-py.tistory.com/559#entry559comment</comments>
      <pubDate>Thu, 16 Mar 2023 17:09:56 +0900</pubDate>
    </item>
    <item>
      <title>TypeError: Cannot destructure property 'styles' of 'this.context' as it is null.</title>
      <link>https://han-py.tistory.com/557</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Nextjs에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;TypeError: Cannot destructure property 'styles' of 'this.context' as it is null.&lt;/b&gt;&lt;/span&gt; 에러를 만났다면 해결책은 어렵지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2128&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JLkJv/btr3vNQbK7c/mlrev5XRN3nHAkl2vwp691/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JLkJv/btr3vNQbK7c/mlrev5XRN3nHAkl2vwp691/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JLkJv/btr3vNQbK7c/mlrev5XRN3nHAkl2vwp691/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJLkJv%2Fbtr3vNQbK7c%2Fmlrev5XRN3nHAkl2vwp691%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2128&quot; height=&quot;956&quot; data-origin-width=&quot;2128&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;head /&amp;gt; 셋팅을 잘못해서 그렇다. _document 파일에서는 아래와 같이 셋팅을 하는 것이 맞다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678607874477&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Html, Head, Main, NextScript } from &quot;next/document&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 _document 파일 이외의 장소에서는&amp;nbsp; 위에처럼 쓰면 안된다. 아래와 같이 사용하면된다. _app 파일을 예로 들면 아래와 같이 next/document 가 아닌 next/head로 사용을 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678608015058&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import &quot;@/styles/globals.css&quot;;
import type { AppProps } from &quot;next/app&quot;;
import Head from &quot;next/head&quot;;

export default function App({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Welcome to  hanpy &amp;lt;/title&amp;gt;
        &amp;lt;link rel=&quot;shortcut icon&quot; href=&quot;/favicon.png&quot; /&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;Component {...pageProps} /&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>TypeError: Cannot destructure property 'styles' of 'this.context' as it is null.</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/557</guid>
      <comments>https://han-py.tistory.com/557#entry557comment</comments>
      <pubDate>Sun, 12 Mar 2023 17:00:22 +0900</pubDate>
    </item>
    <item>
      <title>[javascript] 깊은 복사(deep copy)와 얕은 복사(shallow copy) 기초 정리</title>
      <link>https://han-py.tistory.com/553</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;javascript의 변수를 다른 값으로 복사를 하려고 한다. 이때 우리의 생각처럼 복사가 이루어지지 않는다. 왜냐하면 자바스크립트의 복사 방법은 깊은 복사와 얕은 복사로 나누어 지기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-02-23 오후 9.36.18.png&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NpwbT/btr0sAIt12t/fsw8exIhIEYtDBuwRYu2U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NpwbT/btr0sAIt12t/fsw8exIhIEYtDBuwRYu2U0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NpwbT/btr0sAIt12t/fsw8exIhIEYtDBuwRYu2U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNpwbT%2Fbtr0sAIt12t%2Ffsw8exIhIEYtDBuwRYu2U0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;431&quot; height=&quot;429&quot; data-filename=&quot;스크린샷 2023-02-23 오후 9.36.18.png&quot; data-origin-width=&quot;818&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통은 아래와 같이 변수를 만들어서 복사를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677155868303&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = '10';
b = a;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매우 간단한 예이다. 우리는 b에 10이 담겨 있음을 직관적으로 이해할 수 있다. 여기서 변수란, 값을 저장하기 위한 메모리 공간을 식별하기위해 붙인 이름이다. 즉, 변수 사용 시 자바스크립트 엔진은 변수에 메모리 주소를 기억하게 하여, 변수 사용 시 저장된 메모리 주소로 가서 저장된 값을 가져온다. 이러한 방식은 아래의 타입에 따라 약간씩 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Primitive Type (원시 타입)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시 타입에는 number, string, boolean, null, undefined, symbol(ES6) 이 있다. 이러한 값은 데이터가 생성 및 복사 시 새로운 메모리 공간을 확보하여 독립적인 값들을 저장한다. 따라서 데이터 변경 시 우리는 변경한 것 처럼 보이지만, 실제로는 새로운 메모리에 재할당을 한다. 메모리에 재할당을 하기 때문에 값이 변하지 않는 불변성을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Reference Type (참조 타입)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시 타입과 같이 메모리에 직접 접근하지 않고, 간접적인 참조를 통해 메모리에 접근을 한다. 예를 들면, 변수에 array를 할당하면 변수에 메모리 주소를 저장한다. 그리고 각각의 Array에 들어 있는 값은 앞에서 저장한 메모리 값에, 다시 다른 메모리 값에 array에 포함된 요소들을 하나씩 저장한다. 디테일한 부분은 아래에서 좀 더 살펴 보자. 참조타입으로는 원시 타입을 제외한 나머지 전부인, Array, Function, Object(전부 객체)이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 깊은 복사 (deep copy)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원시 타입은 독립적인 메모리에 할당을 한다. 따라서 복사를 하고 수정 시 기존 값은 변하지 않는다. 이를 깊은 복사라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677163278221&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = '1';
let b = a;

console.log(a) // '1'
console.log(b) // '1'

b = '2';

console.log(a) // '1'
console.log(b) // '2'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제는 간단하고 직관적으로 이해할 수 있다. a의 값을 b에 할당했으니, a와 b는 같은 값이다. 그리고 b에 '2'를 할당했으니, b는 '2'가 된다. 그러나, b에 할당을 했을 때, a가 변하는 경우가 있다. 즉, 복사나 수정 시, 기존 값이 변하는 것이 바로 얕은 복사이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 얕은 복사 (shallow copy)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 복사 시 값 자체를 복사하는 것이 아닌, 힙 메모리 주소값을 복사하는 것이다. 간단히 아래의 예를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677164043820&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = [1, 2, 3];
let b = a;

console.log(a); // [1, 2, 3]
console.log(b); // [1, 2, 3]

b[1] = 10000;

console.log(a); // [1, 10000, 3]
console.log(b); // [1, 10000, 3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a를 b에 넣었다. 그런데, b를 변경했는데, a도 동시에 변경이 되었다. 상식적으로 복사한 이유가 사라진다. 같이 변경되면 왜 같이 쓰나!!!! 이러한 해결책은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 참조 타입의 깊은 복사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 방식이 있지만 두 가지 방식을 소개 하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.1. json 객체 메소드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열로 변경한 후에 다시 파싱하여 객체로 만드는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677164517115&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const copyObj = ( obj ) =&amp;gt; JSON.parse(JSON.stringify(obj));

let a = [1, 2, 3];
let b = copyObj(a);

b[1] = 1000;

console.log(a); // [1, 2, 3];
console.log(b); // [1, 1000, 3];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.1. Lodash의 deepclone&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리를 이용하는 방식으로 아래와 같이 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677164587853&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const _ = require(&quot;lodash&quot;);

let a = [1, 2, 3];
let b = _.cloneDeep(a);

b[1] = 1000;

console.log(a); // [1, 2, 3];
console.log(b); // [1, 1000, 3];&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/JAVASCRIPT</category>
      <category>Primitive Type (원시 타입)</category>
      <category>Reference Type (참조 타입)</category>
      <category>깊은 복사 (deep copy)</category>
      <category>얕은 복사 (shallow copy)</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/553</guid>
      <comments>https://han-py.tistory.com/553#entry553comment</comments>
      <pubDate>Fri, 24 Feb 2023 00:03:45 +0900</pubDate>
    </item>
    <item>
      <title>[react] ref 기초 정리(스크롤 관리, 포커싱 관리, 반복 DOM)</title>
      <link>https://han-py.tistory.com/550</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;react로 DOM에 접근하여 스크롤 이벤트, 포커싱 등을 할 수 있는 방법이 바로 ref를 사용하는 방법이다. 기본적인 React에서 부모 자식 컴포넌트의 상호작용은 props로 한다. 그리고 자식을 수정하기 위해서는 props의 값 변경을 통해 수행한다. 수정을 하는 것이 자식 컴포넌트가 아닌 DOM Element나 React Element인 경우도 있을 것이다. 이러한 경우에 ref를 통해 render 되는 html 태그(DOM Node, React Element)에 접근하는 것이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Ref 자체가 단방향 흐름인 React에서 어떠한 오아시스 같은 역할을 하는지도 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m7Ktx/btrZNv7fMmj/jUf5PMP0rrKMbcpGjLVQV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m7Ktx/btrZNv7fMmj/jUf5PMP0rrKMbcpGjLVQV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m7Ktx/btrZNv7fMmj/jUf5PMP0rrKMbcpGjLVQV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm7Ktx%2FbtrZNv7fMmj%2FjUf5PMP0rrKMbcpGjLVQV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;171&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Ref 사용 시기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서는 아래와 같은 시기에 사용을 권장한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;focus 하는 경우&lt;/li&gt;
&lt;li&gt;input 태그 선택&lt;/li&gt;
&lt;li&gt;미디어 재생 관리&lt;/li&gt;
&lt;li&gt;애니메이션을 직접 실행&lt;/li&gt;
&lt;li&gt;서드 파티 DOM 라이브러리를 React와 함께 사용&lt;/li&gt;
&lt;li&gt;timeout IDs 저장&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통은 DOM 조작시 많이 사용 할 것이다. ref 사용 시 주의 할 점은 상태가 변화 할 때 사용하는 것을 지양해야한다. 상태변환은 ref가 아니라 useState를 사용하자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Ref 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변화 시 컴포넌트를 re-render 하지 않는다. useState와 비슷하게, 초기값으로 string, number, object, even function들이 들어가는 것이 가능하다. 주의해야할 점은,&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;rendering 동안에 ref를 read/write하면 안된다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;react는 render 시 ref.current의 변화를 알지 못하기 때문에, 예상치 못한 에러가 발생할 수 있다. 아래의 예는 사용하면 안되는 시기이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676778798341&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function MyComponent() {
  // during rendering
  myRef.current = 123;
  // during rendering
  return &amp;lt;h1&amp;gt;{myOtherRef.current}&amp;lt;/h1&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우는 사용하면 안되고 아래와 같이 event handler 내부나 useEffect 내부에서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676778907884&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function MyComponent() {
  // ...
  useEffect(() =&amp;gt; {
    // read or write refs in effects
    myRef.current = 123;
  });
  
  function handleClick() {
    // read or write refs in event handlers
    doSomething(myOtherRef.current);
  }
  // ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. useState와 의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ref는 일반적인 Javascript의 객체이다. 따라서 current 프로퍼티로 접근하여 읽고 쓰는 것이 가능하다. useState의 상태값의 변화에 따라 re-render가 일어나지만, ref는 re-render가 일어나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.&amp;nbsp; ref 사용법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 간단하다. useRef Hook을 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1676714619429&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef } from 'react';

export default function Counter() {
  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
    alert('You clicked ' + ref.current + ' times!');
  }

  return (
    &amp;lt;button onClick={handleClick}&amp;gt;
      Click me!
    &amp;lt;/button&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useRef(0)에 초기값을 0을 넣은 것을 알 수 있다. 기본적으로 ref는 아래와 같은 return을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676714762258&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ 
  current: 0 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 말을 쉽게 적으면, 우리는 초기값에 접근을 하기위해서는 ref가 아니라 ref.current로 접근을 해야한다는 말이다. 이러한 구조는 의도적으로 mutable하게 만든 것이다. 그래서 값을 읽고 쓰는 것을 가능하게 하고 React에서 감시 할 수 없게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Ref, useState 동시 사용법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급한 것과 같이, 렌더가 필요하면 useState를 사용하고 그렇지 않고 에벤트 핸들러에만 필요하다면 아래와 같이 ref를 사용하는 것이 더 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676717661970&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState, useRef } from 'react';

export default function Stopwatch() {
  const [startTime, setStartTime] = useState(null);
  const [now, setNow] = useState(null);
  const intervalRef = useRef(null);

  function handleStart() {
    setStartTime(Date.now());
    setNow(Date.now());

    clearInterval(intervalRef.current);
    intervalRef.current = setInterval(() =&amp;gt; {
      setNow(Date.now());
    }, 10);
  }

  function handleStop() {
    clearInterval(intervalRef.current);
  }

  let secondsPassed = 0;
  if (startTime != null &amp;amp;&amp;amp; now != null) {
    secondsPassed = (now - startTime) / 1000;
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;h1&amp;gt;Time passed: {secondsPassed.toFixed(3)}&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={handleStart}&amp;gt;
        Start
      &amp;lt;/button&amp;gt;
      &amp;lt;button onClick={handleStop}&amp;gt;
        Stop
      &amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타이머이다. 1ms 마다 시간이 증가 될 수 있도록, setInterval을 사용하여 기본적으로 구현을 했다. 그리고 setInterval을 멈추기 위한 id값을 받을 때 ref를 사용 한 것을 알 수 있다. &lt;b&gt;이때 핵심은, ref는 useState 변경으로 인해 re-render가 일어나도 값변경이 없다.&lt;/b&gt; mount 될 때 ref가 생성되고, unmount 시 ref는 null이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ref를 HTML 태그로 넣는 다면 ref.current.focus() 와 같이 간단하게 DOM 조작이 가능해 진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 심화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 useRef는 useState 상위 개념으로 구현되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676732682761&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function useRef(initialValue) {
  const [ref, unused] = useState({ current: initialValue });
  return ref;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 useRef를 렌더하면, { current : initialValue }를 리턴한다. 그리고 unused가 사용되지 않는다. 왜냐하면, 항상 같은 객체를 return해주기 때문이다. 따라서 ref 자체를 setter가 없는 일반적인 상태 변수라 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. Ref로 DOM 조작하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 리액트는 자동으로 렌더의 결과와 DOM을 일치키기 때문에 DOM 자체를 조작할 필요는 거의 없다. 그러나 아래의 경우는 React 자체적으로 할 수 없기 때문에, ref를 이용해서 핸들링 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;focus&lt;/li&gt;
&lt;li&gt;scroll&lt;/li&gt;
&lt;li&gt;size 측정&lt;/li&gt;
&lt;li&gt;position 측정&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 위와 비슷하다. DOM을 접근하기 위해서는 우선은 아래와 같이&amp;nbsp; useRef를 import 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676734914022&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef } from 'react';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;component 내부에서 ref를 선언한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676734933275&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const myRef = useRef(null);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 DOM node에 ref 속성을 아래와 같이 넣어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676735153274&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div ref={myRef}&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예를 보자. 기본적으로 위에서 설명한 것 처럼 useRef는 current이라는 단일 프로퍼티를 가진 객체이다. 위에서 우리는 myRef.current에 null을 초기 값으로 넣었다. 그리고 React가 DOM을 만들 때, myRef.current는 &amp;lt;div&amp;gt; node를 참조한다. 이때, myRef.current를 이용해서 DOM node에 접근가능하다. myRef.current는 아래와 같이 이벤트 핸들러나 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Element&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;브라우저 API&lt;/a&gt;도 일반 element 처럼 사용가능하다. 쉽게 말해 브라우저 API의 예는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676736488734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;myRef.current.scrollIntoView();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. ref로 focusing 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 클릭하면 input 태그를 포커싱하는 예이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676736854719&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;input ref={inputRef} /&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;
        Focus the input
      &amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;input ref={inputRef}&amp;gt;를 활용하여 기본적으로 input의 DOM node를 react로 핸들링하게 한것이다. 그리고&amp;nbsp;inputRef.current.focus()를 활용하여 포커싱을 하는 예이다. &lt;b&gt;ref는 React가 아닌 외부의 것을 저장하는데 사용되는 것을 확인 할 수 있다. 그리고 rendering이 되어도 상태가 유지됨을 알 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. ref로 Scrolling 구현하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 브라우저 APIs 중에 scrollIntoView()를 활용한 예이다. 사실 위와 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676737493035&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef } from 'react';

export default function CatFriends() {
  const firstCatRef = useRef(null);
  const secondCatRef = useRef(null);
  const thirdCatRef = useRef(null);

  function handleScrollToFirstCat() {
    firstCatRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    });
  }

  function handleScrollToSecondCat() {
    secondCatRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    });
  }

  function handleScrollToThirdCat() {
    thirdCatRef.current.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    });
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;button onClick={handleScrollToFirstCat}&amp;gt;
          Tom
        &amp;lt;/button&amp;gt;
        &amp;lt;button onClick={handleScrollToSecondCat}&amp;gt;
          Maru
        &amp;lt;/button&amp;gt;
        &amp;lt;button onClick={handleScrollToThirdCat}&amp;gt;
          Jellylorum
        &amp;lt;/button&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;ul&amp;gt;
          &amp;lt;li&amp;gt;
            &amp;lt;img
              src=&quot;https://placekitten.com/g/200/200&quot;
              alt=&quot;Tom&quot;
              ref={firstCatRef}
            /&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;
            &amp;lt;img
              src=&quot;https://placekitten.com/g/300/200&quot;
              alt=&quot;Maru&quot;
              ref={secondCatRef}
            /&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li&amp;gt;
            &amp;lt;img
              src=&quot;https://placekitten.com/g/250/200&quot;
              alt=&quot;Jellylorum&quot;
              ref={thirdCatRef}
            /&amp;gt;
          &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 어려운 로직은 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. ref로 여러 목록 참고하기(반복문)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 scroll 같은 경우는 정해진 수의 ref를 만들었다. 그러나 특정 갯수가 정해지지 않는 경우라면, map을 써서 아래와 같이 구현을 할수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676737657847&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  {items.map((item) =&amp;gt; {
    // Doesn't work!
    const ref = useRef(null);
    return &amp;lt;li ref={ref} /&amp;gt;;
  })}
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연하게도 돌아가지 않는다. 왜냐하면, ref 는 component 기준 최상위에서만 호출되어야 한다. 즉, 루프, 조건, map같은 경우에 useRef를 호출 할 수 없다. 해결책은 ref 속성으로 함수(ref callback)를 넣는 것이다. 아래의 예를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676739341796&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef } from 'react';

export default function CatFriends() {
  const itemsRef = useRef(null);

  function scrollToId(itemId) {
    const map = getMap();
    const node = map.get(itemId);
    node.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    });
  }

  function getMap() {
    if (!itemsRef.current) {
      // Initialize the Map on first usage.
      itemsRef.current = new Map();
    }
    return itemsRef.current;
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;nav&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; scrollToId(0)}&amp;gt;
          Tom
        &amp;lt;/button&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; scrollToId(5)}&amp;gt;
          Maru
        &amp;lt;/button&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; scrollToId(9)}&amp;gt;
          Jellylorum
        &amp;lt;/button&amp;gt;
      &amp;lt;/nav&amp;gt;
      &amp;lt;div&amp;gt;
        &amp;lt;ul&amp;gt;
          {catList.map(cat =&amp;gt; (
            &amp;lt;li
              key={cat.id}
              ref={(node) =&amp;gt; {
                const map = getMap();
                if (node) {
                  map.set(cat.id, node);
                } else {
                  map.delete(cat.id);
                }
              }}
            &amp;gt;
              &amp;lt;img
                src={cat.imageUrl}
                alt={'Cat #' + cat.id}
              /&amp;gt;
            &amp;lt;/li&amp;gt;
          ))}
        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

const catList = [];
for (let i = 0; i &amp;lt; 10; i++) {
  catList.push({
    id: i,
    imageUrl: 'https://placekitten.com/250/200?image=' + i
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;간단한 로직이다. &lt;b&gt;itemsRef.current&lt;/b&gt;에 &lt;b&gt;new Map()&lt;/b&gt;을 넣어서 rendering 시에도 상태를 유지하도록 하고, itensRef.current를 가지고 와서 cat.id와 ref callback 함수의 DOM node 인자를 활용한다. ref callback function은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676775436214&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div ref={(node) =&amp;gt; console.log(node)} /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 ref 의 node는 매 render마다 다르다는 것을 알아야 한다. 왜냐하면, component가 re-render 되면, 이전 함수의 인자는 null이 되고 새로운 함수는 DOM node가 된다. 따라서 위 의 예제와 같이 map method로 위와 같이 적어도 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10. 다른 컴포넌트에 ref 붙이기(React.forwardRef())&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새롭게 만든 &amp;lt;MyInput /&amp;gt; 과 같은 컴포넌트에 ref를 붙이고 싶다. 하지만 아래와 같이 구현을 하면, 내가 원하는 DOM node가 아닌 null값이 잡힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676776082949&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef } from 'react';

function MyInput(props) {
  return &amp;lt;input {...props} /&amp;gt;;
}

export default function MyForm() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;MyInput ref={inputRef} /&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;
        Focus the input
      &amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포커싱을 하는 예시이다. 그러나 실행해보면, 아래와 같은 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676776134147&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현하다가 어딘가에서 많이 본 에러 같은 느낌이 들 수도 있다. 기본적으로 React는 각 component는 다른 component의 DOM에 접근하는것을 허용하지 않는다. 물론 props로 자식 component로 DOM 접근도 불가능하다. 그러나 forwardRef API를 사용하면 DOM 노출을 허용할 수 있다. 에러를 자세히 보면 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;React.forwardRef()&lt;/b&gt;&lt;/span&gt;를 사용하라고 추천해 주고 있다. forwardRef는 기본적으로 ref가 props로 내려주는게 안되기 때문에 추가로 사용을 해줘야 한다. 사용법은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676777157042&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const MyInput = forwardRef((props, ref) =&amp;gt; {
  return &amp;lt;input {...props} ref={ref} /&amp;gt;;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;MyInput ref={inputRef} /&amp;gt;&lt;/b&gt;는 기본적으로 React에 해당 컴포넌트의 DOM node를&lt;b&gt; inputRef.current&lt;/b&gt;에 넣도록 한다. &lt;b&gt;forwardRef&lt;/b&gt;의 두번째 인자인 ref로 inputRef를 받는다. 그리고 MyInput은 받은 ref를 &amp;lt;input&amp;gt; 태그 로 넘긴다. 전체코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676777610558&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { forwardRef, useRef } from 'react';

const MyInput = forwardRef((props, ref) =&amp;gt; {
  return &amp;lt;input {...props} ref={ref} /&amp;gt;;
});

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;MyInput ref={inputRef} /&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;
        Focus the input
      &amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 low-level component(button 태그, input 태그 등등)은 ref를 내려주어 DOM node를 노출하는 것이 일반적이다. 그러나 high-level component(form, list, page section 등등)은 예상못한 의존성을 피하기 위해 DOM node를 노출하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;11. 심화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 아래의 코드를 보자. handle 함수 하나만 가지고 왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676778480420&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function TodoList() {
  const listRef = useRef(null);
  const [text, setText] = useState('');
  const [todos, setTodos] = useState(
    initialTodos
  );
  function handleAdd() {
    const newTodo = { id: nextId++, text: text };
    setText('');
    setTodos([ ...todos, newTodo]);
    listRef.current.lastChild.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest'
    });
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;button onClick={handleAdd}&amp;gt;
        Add
      &amp;lt;/button&amp;gt;
      &amp;lt;input
        value={text}
        onChange={e =&amp;gt; setText(e.target.value)}
      /&amp;gt;
      &amp;lt;ul ref={listRef}&amp;gt;
        {todos.map(todo =&amp;gt; (
          &amp;lt;li key={todo.id}&amp;gt;{todo.text}&amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

let nextId = 0;
let initialTodos = [];
for (let i = 0; i &amp;lt; 20; i++) {
  initialTodos.push({
    id: nextId++,
    text: 'Todo #' + (i + 1)
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;handleAdd를 보면, setText반영 후 setTodos로 업데이트가 되고 가장 아래부분에 업데이트가 된 위치로 스크롤이 내려가야한다. 그러나, useState의 set부분이 동기적으로 바로 적용이 안된다. 이러한 경우는 아래와 같이 flushSync를 사용해주면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676778656180&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useState, useRef } from 'react';
import { flushSync } from 'react-dom';

export default function TodoList() {
  const listRef = useRef(null);
  const [text, setText] = useState('');
  const [todos, setTodos] = useState(
    initialTodos
  );

  function handleAdd() {
    const newTodo = { id: nextId++, text: text };
    flushSync(() =&amp;gt; {
      setText('');
      setTodos([ ...todos, newTodo]);      
    });
    listRef.current.lastChild.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest'
    });
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;button onClick={handleAdd}&amp;gt;
        Add
      &amp;lt;/button&amp;gt;
      &amp;lt;input
        value={text}
        onChange={e =&amp;gt; setText(e.target.value)}
      /&amp;gt;
      &amp;lt;ul ref={listRef}&amp;gt;
        {todos.map(todo =&amp;gt; (
          &amp;lt;li key={todo.id}&amp;gt;{todo.text}&amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

let nextId = 0;
let initialTodos = [];
for (let i = 0; i &amp;lt; 20; i++) {
  initialTodos.push({
    id: nextId++,
    text: 'Todo #' + (i + 1)
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>react</category>
      <category>react DOM 관리</category>
      <category>react 스크롤</category>
      <category>react 포커싱</category>
      <category>React.forwardRef()</category>
      <category>REF</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/550</guid>
      <comments>https://han-py.tistory.com/550#entry550comment</comments>
      <pubDate>Mon, 20 Feb 2023 21:19:00 +0900</pubDate>
    </item>
    <item>
      <title>[개발자 도구] TTFB(Time To First Byte)</title>
      <link>https://han-py.tistory.com/548</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TTFB(Time To First Byte)는 서버의 성능을 보여주는 척도로, &lt;b&gt;HTTP를 요청했을 때, 처음으로 정보(Byte)가 브라우저에 도달하는 시간을 의미&lt;/b&gt;한다. 당연한 말이지만, SEO에 직결되고 UX적으로도 사용자가 빠르게 데이터를 확인할 수 있게 하기위해 확인해야하는 정보이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. TTFB&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTFB는 위에서 설명한 것과 같이 웹서버의 응답을 측정하기 위해 사용한다. 좋은 사이트의 경우는 0.8s 즉 800ms 안으로 TTFB가 걸릴 수 있는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. TTFB 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TTFB를 확인하는 부분은 크롬의 개발자 도구를 활용하면 간단하다. 개발자 도구의 네트워크 부분을 들어간다. 그리고 내트워크 내부에서, 아래와 같이 waterfall부분을 찾자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8vTJT/btrZtsipqg1/ZiN8HhMzjKnxOKFeKKc9B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8vTJT/btrZtsipqg1/ZiN8HhMzjKnxOKFeKKc9B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8vTJT/btrZtsipqg1/ZiN8HhMzjKnxOKFeKKc9B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8vTJT%2FbtrZtsipqg1%2FZiN8HhMzjKnxOKFeKKc9B0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1832&quot; height=&quot;612&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색이 있는 이쁜 컬러부분에 마우스를 올려보자. 그러면 아래와 같이 내용이 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;1156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcxGt9/btrZrwy51Et/iXxziSFk0rRPd0yjXLqKd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcxGt9/btrZrwy51Et/iXxziSFk0rRPd0yjXLqKd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcxGt9/btrZrwy51Et/iXxziSFk0rRPd0yjXLqKd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcxGt9%2FbtrZrwy51Et%2FiXxziSFk0rRPd0yjXLqKd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;845&quot; height=&quot;521&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;1156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 사각형 부분의 Waiting for server response 부분이 바로 TTFB 부분이다. 1.32s 가 걸린 것을 알 수 있다. 1.32s가 걸린다면, 무좋은 좋지 않을 웹사이트일까? 그렇지는 않다. 이미지도 용량에 따라서 오래걸리는 부분이 당연히 있다고 할 수 있다. 이러한 경우는 렌더링 페이지가 다 생성 후에 다중에 lazy loading 하는 방식으로 사이트를 만드는 것이 좋다.&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>TTFB(Time To First Byte)</category>
      <category>개발자 도구</category>
      <category>네트워크</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/548</guid>
      <comments>https://han-py.tistory.com/548#entry548comment</comments>
      <pubDate>Fri, 17 Feb 2023 21:32:31 +0900</pubDate>
    </item>
    <item>
      <title>[react] 마우스 클릭 이벤트 타입 지정하기</title>
      <link>https://han-py.tistory.com/545</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;마우스 클릭 시 type 지정하여, event 값을 가져오는 데 어려움을 겪는 사람들을 위해 글을 적어보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 에러가 나는 부분은 event를 가지고와서 핸들링할 때, 주로 발생을 한다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;event.target as HTMLElement&lt;/b&gt; &lt;/span&gt;를 활용해서 사용하는 방식을 사용하면 된다. 사실 간단히 아래와 같이 구현이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676361638562&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type CustomMouseEvent = MouseEvent&amp;lt;HTMLElement&amp;gt;;

const TestComponent = () =&amp;gt; {
  const handleSelect = (event: CustomMouseEvent) =&amp;gt; {
    const eventTarget = event.target as HTMLElement;
    console.log(eventTarget.innerTest)
  };
  return (
  	&amp;lt;button
      onClick={TestComponent}
    &amp;gt; 
    Click Here! 
    &amp;lt;/button&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제를 활용해서 응용을 진행해 보자.&lt;/p&gt;</description>
      <category>Web/typescript</category>
      <category>error</category>
      <category>mouse event</category>
      <category>react</category>
      <category>typescript</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/545</guid>
      <comments>https://han-py.tistory.com/545#entry545comment</comments>
      <pubDate>Tue, 14 Feb 2023 17:05:08 +0900</pubDate>
    </item>
    <item>
      <title>[javascript] event.keyCode 기초 정리</title>
      <link>https://han-py.tistory.com/544</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;보통 우리는 키보드를 javascript로 핸들링 할때, keyCode를 사용해서 적용 한다. 왜냐하면 구글링을 하다보면 KeyCode에 관한 내용들이 많기 때문이다. 하지만 아래의 문서를 보면 Deprecated 되었다고 나와있는 것을 알 수 있다. 정단히 정리하면, 웹 표준에서 제거가 되었다. 호환성 목적으로 사용하기는 하지만, 사용하지 않는것을 추천한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwRxQS/btrYPBndOwg/JTlcsbyU57PFBwrnTDthpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwRxQS/btrYPBndOwg/JTlcsbyU57PFBwrnTDthpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwRxQS/btrYPBndOwg/JTlcsbyU57PFBwrnTDthpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwRxQS%2FbtrYPBndOwg%2FJTlcsbyU57PFBwrnTDthpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;819&quot; height=&quot;285&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. keyCode&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 대부분 사용하지 않더라도 대략적인 사용법은 아래와 같다. 요즘 리액트를 많이 쓰니 리액트 기준으로 관련 로직을 보면 아래와 같다. 아래와 같이 간단하게 사용가능하다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;onKeyDown&lt;/b&gt;&lt;/span&gt; 리스너를 통해서 키보드를 누르는 &lt;b&gt;event&lt;/b&gt;를 받는다. 그리고 받은 이벤트에 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;event.keyCode&lt;/b&gt;&lt;/span&gt;를 보면 숫자가 적혀있는 것을 알 수 있다. 그 숫자가 바로 방향키에 따라 다른 숫자가 나오고, 그 나온 숫자에 따라 로직을 작성해 주면 된다. 아래의 예를 간단히 보면, 위 방향키를 누르면, event.keyCode가 38로 나타난다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676004881027&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const KeyCodeTest = () =&amp;gt; {
  const handleKeyDown = (
    event
  ) =&amp;gt; {
    switch (event.keyCode) {
      case 38:
      	// 위 방향키
        break;
      case 39:
    	// 오른쪽 방향키
        break;
      case 40:
        // 아래 방향키
        break;
      case 37:
        // 왼쪽 방향키
        break;
      }
    };
    return (
        &amp;lt;button onKeyDown={handleKeyDown}&amp;gt;TEST&amp;lt;/button&amp;gt;
    )
}

export default KeyCodeTest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 필요한 내용한 적어서 react로 예시를 만들어 보았다. 관련 내용은 아래의 주소를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#browser_compatibility&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#browser_compatibility&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676005090784&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;KeyboardEvent.keyCode - Web APIs | MDN&quot; data-og-description=&quot;The deprecated KeyboardEvent.keyCode read-only property represents a system and implementation dependent numerical code identifying the unmodified value of the pressed key.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#browser_compatibility&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/D2BFF/hyRzt3I6eH/gzk2KcgCE7W0N6mDZ4xgX0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#browser_compatibility&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#browser_compatibility&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/D2BFF/hyRzt3I6eH/gzk2KcgCE7W0N6mDZ4xgX0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;KeyboardEvent.keyCode - Web APIs | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The deprecated KeyboardEvent.keyCode read-only property represents a system and implementation dependent numerical code identifying the unmodified value of the pressed key.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. event.key&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;event.keyCode를 사용 못하면 무엇을 사용해야하나!!!! 바로 &lt;b&gt;event.key&lt;/b&gt;이다. 관련 javascript 예시는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676005138763&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.addEventListener(&quot;keydown&quot;, (event) =&amp;gt; {
  if (event.defaultPrevented) {
    return; // Do nothing if the event was already processed
  }

  switch (event.key) {
    case &quot;Down&quot;: // IE/Edge specific value
    case &quot;ArrowDown&quot;:
      // Do something for &quot;down arrow&quot; key press.
      break;
    case &quot;Up&quot;: // IE/Edge specific value
    case &quot;ArrowUp&quot;:
      // Do something for &quot;up arrow&quot; key press.
      break;
    case &quot;Left&quot;: // IE/Edge specific value
    case &quot;ArrowLeft&quot;:
      // Do something for &quot;left arrow&quot; key press.
      break;
    case &quot;Right&quot;: // IE/Edge specific value
    case &quot;ArrowRight&quot;:
      // Do something for &quot;right arrow&quot; key press.
      break;
    case &quot;Enter&quot;:
      // Do something for &quot;enter&quot; or &quot;return&quot; key press.
      break;
    case &quot;Esc&quot;: // IE/Edge specific value
    case &quot;Escape&quot;:
      // Do something for &quot;esc&quot; key press.
      break;
    default:
      return; // Quit when this doesn't handle the key event.
  }

  // Cancel the default action to avoid it being handled twice
  event.preventDefault();
}, true);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 event를 불러서 사용하면 된다. 가장 큰 차이는 keyCode는 숫자고, key는 문자이다. 이제 관련 내용을 통해 코딩을 시작을 해보자.&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>e.key</category>
      <category>e.keycode</category>
      <category>react</category>
      <category>방향키</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/544</guid>
      <comments>https://han-py.tistory.com/544#entry544comment</comments>
      <pubDate>Sat, 11 Feb 2023 15:01:05 +0900</pubDate>
    </item>
    <item>
      <title>[UX/UI] web focus 적용하기(tabindex)</title>
      <link>https://han-py.tistory.com/543</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;키보드 포커싱에 대해 알아보자. 기본적으로 마우스 없어도 tab을 이용해서 input 값들을 넣을 수 있다. 그리고 사용자 기준으로 tab을 눌렀을 때 포커싱(마우스 커서)의 위치가 예상 되는 곳에 존재할 수 있도록 만들어 보자. 중요하지 않지만, 매우 중요한 기능이다. 웹 접근성관 관련된 키보드 포커싱에 대해 알아보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 기본 포커싱&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 input창을 누르면 자연스럽게 input창에 커서가 들어간다. 즉, 웹은 사용자가 사용하는 부분에 대해서는 기본적으로 자동으로 포커싱을 시켜준다. 상호작용하는 element로는 &lt;b&gt;a 태그, input 태그, select 태그, button 태그&lt;/b&gt;들이 있다. 그리고 이러한 태그들을 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;tab키&lt;/b&gt;&lt;/span&gt;를 이용해서 키보드로 이동을 할 수 있다. (반대로 이동은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;shift + tab&lt;/b&gt;&lt;/span&gt;으로 실행가능하다.)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 위의 상호작용하는 태그 말고, 다른 태그들은 포커싱이 불가능 할까? 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. tabindex&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tabindex는 기본적으로 element들의 포커스를 설정할 수 있는 기능이라고 할 수 있다. 단순하게 아래와 같이 tag에 넣어서 사용가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675658249080&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 일반 HTML
&amp;lt;div tagindex=&quot;1&quot;&amp;gt;&amp;lt;/div&amp;gt;

// react
&amp;lt;div tagIndex={1}&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tabindex를 이용하여 포커스를 설정할 수 있는 경우의 수는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;포커싱 되지 않는 element를 포커싱 시키기&lt;/li&gt;
&lt;li&gt;포커싱 되는 element를 포커싱 안시키기&lt;/li&gt;
&lt;li&gt;포커싱 순서 변경하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 토대로 하나씩 알아나가 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. tabindex=&quot;0&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 div나 h1 같은 태그는 포커싱이 되지 않는다. 그러나 아래와 같이 tabindex를 0으로 속성값을 추가 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675658852599&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;h1 tabindex=&quot;0&quot;&amp;gt;h1도 포커싱이 가능합니다.&amp;lt;/h1&amp;gt;
&amp;lt;div tabindex=&quot;0&quot;&amp;gt;div도 포커싱이 가능합니다.&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. tabindex=&quot;-1&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 -1을 설정을하면, 기존에 포커싱 잡히던 element들이 포커싱이 잡히지 않게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675658996906&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button tabindex=&quot;0&quot;&amp;gt;button은 포커싱이 안 됩니다.&amp;lt;/button&amp;gt;
&amp;lt;a tabindex=&quot;0&quot;&amp;gt;a도 포커싱이 안 됩니다.&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 순차적인 포커싱&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키보드 포커싱에 대한 기본적인 예제를 알아보았다. 기본적으로 우리는 포커싱을 웹 내부에서 tab으로 확인을 하였다. 그리고 반대로 포커싱을 확인하는 방법으로는 shift + tab 으로 확인을 하였다. 이제는 포커싱을 적용하는 방법 외에도, 포커싱을 하는 순서를 지정해 보자. 아래의 예시를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675749520858&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div tabindex=&quot;4&quot;&amp;gt;4번째로 포커싱이 됩니다.&amp;lt;/div&amp;gt;
&amp;lt;div tabindex=&quot;1&quot;&amp;gt;1번째로 포커싱이 됩니다.&amp;lt;/div&amp;gt;
&amp;lt;div tabindex=&quot;0&quot;&amp;gt;5번째로 포커싱이 됩니다.&amp;lt;/div&amp;gt;
&amp;lt;div tabindex=&quot;2&quot;&amp;gt;2번째로 포커싱이 됩니다.&amp;lt;/div&amp;gt;
&amp;lt;div tabindex=&quot;3&quot;&amp;gt;3번째로 포커싱이 됩니다.&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포커싱의 순서는 생각보다 단순하다. tabindex가 양수 기준으로 1부터 숫서대로 2, 3, 4가 포커싱 된 후에 0이 포커싱 된다. 위의 테스트 내용을 판단해서 직접 테스트 해보면 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 조금 더 편리하게 사용할 수 있도록 개발을 시작해보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>focus 순서</category>
      <category>NextJS</category>
      <category>react</category>
      <category>tabindex</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/543</guid>
      <comments>https://han-py.tistory.com/543#entry543comment</comments>
      <pubDate>Wed, 8 Feb 2023 17:57:37 +0900</pubDate>
    </item>
    <item>
      <title>HTML에서 javascript 동작 순서 기초 정리</title>
      <link>https://han-py.tistory.com/542</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 작성을 하고, js css를 넣어서 웹을 구성을 한다. 그리고 Reactjs 나 nextjs에서도 라이브러리를 이용해서 데이터를 넣는다. 이때 동기적으로 Javascript 파일을 가지고와서 핸들링을 많이한다. 오늘은 HTML 자체에서 js파일을 불러오는 방식에 대해 알아보도록 하자. 기초지만 쉽게 지나치는 부분이라 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. javascript 파일 불러오기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 아래와 같이 javscript를 부르는 코드는 아무 위치에서나 사용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674979340461&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;script src=&quot;./test0.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;html lang=&quot;en&quot; class=&quot;demo1&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;script src=&quot;./test1.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;script src=&quot;./test2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;script src=&quot;./test3.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;div&amp;gt;Hanpy javascript Test&amp;lt;/div&amp;gt;
    &amp;lt;script src=&quot;./test4.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
  &amp;lt;script src=&quot;./test5.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html 밖에서 사용해도 가능하다. javascript 파일을 불러오는 방식은 위와 같이 src를 사용해서 javascript 코드를 가져올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674979444782&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;javascript경로&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니면 javascript 코드를 아래와 같이 직접 적어서 가져오는 방식도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674979585017&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;script&amp;gt;
  console.log('test0');
&amp;lt;/script&amp;gt;
&amp;lt;html lang=&quot;en&quot; class=&quot;demo1&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;script&amp;gt;
      console.log('test1');
    &amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;script&amp;gt;
    console.log('test2');
  &amp;lt;/script&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;script&amp;gt;
      console.log('test3');
    &amp;lt;/script&amp;gt;
    &amp;lt;div&amp;gt;Hanpy javascript Test&amp;lt;/div&amp;gt;
    &amp;lt;script&amp;gt;
      console.log('test4');
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
  &amp;lt;script&amp;gt;
    console.log('test5');
  &amp;lt;/script&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황에 따라 사용을 해주면 된다. 물론 React는 jsx 문법을 사용하기 때문에 위와는 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. HTML에서 javascript 파싱 순서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파싱이란, 프로그램을 실행할 수 있도록 내부 포맷에 맞게 분석하고 변환하는 것을 의미한다. 웹 브라우저의 자바스크립트 엔진이 파싱하는 순서는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;브라우저는 HTML 파일을 열면, 위에서 부터 순차적으로 파싱을 한다.&lt;/li&gt;
&lt;li&gt;파싱 중에 &amp;lt;script&amp;gt; 태그를 만나면 HTML 파싱을 멈추고, script 내부의 자바스크립트 코드를 실행한다.&lt;/li&gt;
&lt;li&gt;자바스크립트 파일 실행이 완료 되면 다시 HTML파싱을 시작한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 시간이 오래 걸리는 자바스크립트 코드를 HTML 페이지 중간에 넣으면 렌더링이 오래 걸리고, SEO에 최악의 결과를 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 일반적인 script 태그 위치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 css를 불러오는 link 는 head 요소의 자식으로 들어가고, javascript를 불러오는 script 는 body 요소가 끝나기 직전에 위치한다. css가 head로 들어가는 이유는 stylesheet를 미리 알아야 빠르게 그리는 것이 가능하기 때문에 head에 위치시켜 body를 그리기 전에 우선적으로 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Javascript를 불러오는 script 태그를 body 끝 부분에 위치 시키는 이유는 html 관련 내용을 모두 보여준 후에 동적 js를 불러와야 html의 파싱 시간을 짧게 가져갈 수 있다. 쉽게 말하면, head 태그나 중간에 들어가면, js를 읽는 시간이 오려결러 웹페이지가 느리게 느낀다. 또한 HTML 파싱 보다 Javascript 파일이 먼저 실행되면 적용이 안되는 경우도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. javascript 받는 시간 변경하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOMContentLoaded 이벤트를 사용하여 DOM 생성을 체크하는 로직을 자바스크립트 파일에 넣으면, DOM 생성을 체크해서 파일을 실행 시킬 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674984578706&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.addEventListener(&quot;DOMContentLoaded&quot;, function() { 
    // DOM생성 이후 콜백 실행
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;async&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 js 파일을 가져올 떄는 async를 추가한다. 비동기로 가져오기 때문에 HTML 파싱이 계속 진행된다. 이떄, 중요한 것은 서로 결합되어있는 자바스크립트 파일을 async로 비동기로 불러온다면, 순서 상관 없이 실행이 될 수 있기 때문에 에러가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674984398272&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;js파일 경로&quot; async&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;defer&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async를 비동기 문제를 해결할 수 있다. 즉, 비동기로 작동하더라도 태그의 위치에 따라 순서대로 실행된다. 그리고 defer 은 DOM이 생성 된 이후에 실행하기 때문에 DOM 생성을 체크 할 필요는 없다. 따라서 아무 위치에서나 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;script&amp;gt;태그를 &amp;lt;/html&amp;gt; 뒤에 넣기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 DOM의 생성이 완료 된 이후에 자바스크립트 코드가 실행된다. 따라서 실행은 가능하긴 하지만, 권장하는 방식은 아니다. 위의 순서를 이해하지 못하는 경우에 일단 적어두고 사용하는 경우가 많다.&lt;/p&gt;</description>
      <category>Web/HTML</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/542</guid>
      <comments>https://han-py.tistory.com/542#entry542comment</comments>
      <pubDate>Sun, 5 Feb 2023 19:31:31 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript Event Loop] JS 작동방식 기초 정리</title>
      <link>https://han-py.tistory.com/540</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;javascript의 작동 방식에 대해 알아보자. 동기적 방식과 비동기적 방식에 대한 근본적인 원리에 대해 심층적으로 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wvElp/btrWYLFiMPP/KmcWK6oWjoYyZhj1K9p3j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wvElp/btrWYLFiMPP/KmcWK6oWjoYyZhj1K9p3j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wvElp/btrWYLFiMPP/KmcWK6oWjoYyZhj1K9p3j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwvElp%2FbtrWYLFiMPP%2FKmcWK6oWjoYyZhj1K9p3j0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;942&quot; height=&quot;956&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Javascript의 동기적 작동방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 코드를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674532230188&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function one() {
  two()
  console.log('one')
}

function two() {
  console.log('two')
}

one()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 어떻게 작동할까?&amp;nbsp; 결과는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674532260268&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;two
one&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;one 함수를 실행한다.&lt;/li&gt;
&lt;li&gt;one 내부의 two 함수가 실행된다.&lt;/li&gt;
&lt;li&gt;console.log('two') 가 실행된다.&lt;/li&gt;
&lt;li&gt;console.log('one')이 실행된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시로 Javascript는 위에서 아래로 실행되고, 왼쪽에서 오른쪽으로 실행되는 것을 확인할 수 있다. 이렇게 순서대로 실행되는 것을 동기적으로 실행된다고 한다. 이러한 방식을 조금 더 자세히 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 호출 스택 ( = 콜 스택 = call stack )&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리란 임시 저장장치로 컴퓨터가 잠깐 기억하고 있는 공간이다. Javascript는 위와 같이 one()&lt;b&gt; 함수를 선언&lt;/b&gt; 시 one() 함수는 &lt;b&gt;메모리에 올라간다&lt;/b&gt;. 그리고 함수 호출 시 메모리에서 함수가 선언 되었는지 찾는다. 이를 호출 스택이라 부르며, 호출된 함수는 STACK 형식으로 아래와 같이 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 51px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;two()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;one()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; text-align: center;&quot;&gt;anonymous&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에서 위로 쌓이고, 위에서 아래로 실행된다. 위와 같은 스택으로 머리속에서 그릴 수 있어야 한다. Anonymous는 가상의 전역 컨텍스트(scope)로 파일 실행 시에 먼저 아래부분에 쌓이게 된다. 그리고 함수가 실행 될 때 마다 anonymous 위로 하나씩 차곡차곡 쌓인다. 그리고 함수의 범위를 나타내는 {}에서 } 부분(함수 끝부분)을 만나면 쌓인 STACK에서 빠지게 된다. 이러한 방식으로 하나씩 실행이 되면, 마지막에는 anonymous만 남게 된다. 마지막으로 Anonymous가 실행되면 파일 실행은 끝이나고 javascript 실행이 완료 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 호출 스택은 Javascript가 어떠한 순서로 작동하는가를 확인할 때 필수적으로 사용된다. 하지만 비동기라면 조금 더 개념을 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. javascript 비동기 작동방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout을 활용하여 비동기 방식의 간단한 예를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674534711800&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function run() {
    console.log('1초 후 실행');
}

console.log('시작')
setTimeout(run, 1000);
console.log('끝')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과 값은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674535898007&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;시작
끝
1초 후 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 위에서 알게 된 &lt;b&gt;호출 스택 방식으로 동기적으로 분석&lt;/b&gt;을 하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파일 실행 시 anonymous(전역 스코프) STACK아래에 생성된다.&lt;/li&gt;
&lt;li&gt;function run 선언 시 메모리에 run 함수 저장된다.&lt;/li&gt;
&lt;li&gt;console.log 호출하면 호출 스택에 쌓인다.&lt;/li&gt;
&lt;li&gt;console.log 실행 후에, 실행이 끝나면 호출 스택에서 바로 빠진다.&lt;/li&gt;
&lt;li&gt;setTimeout도 호출되서 스택 들어왔다 실행 후 빠진다.&lt;/li&gt;
&lt;li&gt;console.log('끝')도 스택에 들어왔다 나간다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;anonymous를 실행하면, javascript 실행이 종료 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위의 방식은 잘못된 방식이다.&lt;/b&gt; 위 코드는 호출 스택만으로 비동기가 설명이 안된다. 호출스택과 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;이벤트 루프&lt;/b&gt;&lt;/span&gt;로 설명이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Javascript 이벤트 루프 ( Task Queue )&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 이벤트 루프를 이해하려면, '호출스택 / 백그라운드 / 메모리 / 태스크 큐 / 콘솔창' 에 대한 기본적인 이해가 있어야한다. 위의 예제를 다시 한번 호출스택에 이벤트 루프를 포함하여 설명해 보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 파일 실행 시 anonymous(전역 스코프) STACK아래에 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. function run 선언 시 메모리에 run 함수 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. console.log 호출하면 호출 스택에 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. console.log 실행 후에, 실행이 끝나면 호출 스택에서 바로 빠진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. setTimeout도 호출되서 스택 들어왔다 실행 후 빠진다. (&lt;b&gt;여기까지는 동일하다.&lt;/b&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5-1. &lt;b&gt;백그라운드에 '타이며(run, 3초)'를 넣어준다. &lt;span style=&quot;color: #006dd7;&quot;&gt;백그라운드&lt;/span&gt;의 장점은 코드가 백그라운드로 가면, 호출스택과 백그라운드가 동시에 실행된다. 따라서 아래의 console.log 실행하면서도 백그라운드에서는 3초를 계속 세고 있다. 추가로 백그라운드가 먼저 끝난다고 하더라도 호출스택이 먼저 처리가 되어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. console.log('끝')도 스택에 들어왔다 나간다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. anonymous를 만나면, 호출 스택이 모두 비워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 3초가 끝나면 run을 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;태스크 큐&lt;/span&gt;로 보내고 백그라운드는 지워진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9.&lt;b&gt; 이벤트 루프의 역할은 호출 스택이 비어있을 떄, 태스크 큐에 있는 함수들 하나하나를 호출스택에&amp;nbsp; 끌어와서 실행해 준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. 호출스택에 run이 담기고 태스크 큐의 run은 지워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11. run 실행되면서 안에 있는 console.log가 run위에 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12. console.log 끝나면, 나가면서 콘솔창에 글 남기고 호출스택 빠짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12. run 함수도 호출스택에서 빠짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;+ 호출스택, 백그라운드, 태스크 큐가 다 지워져 있으면 JS 실행이 완료 된 것이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방식으로 JS 코드 순서 분석이 가능하다. 사실 자세히 공부해보면, 호출스택에 this나 scope 들도 고려를 해야하기 때문에 더 복잡하다. 추가로 백그라운드는 다른 쓰레드를 사용한다. 쉽게 말하면, 작동 방식은 멀티 스레드처럼 동작한다고 보면된다. 그러나 백그라운드에 들어갈 수 있는 함수(setTimeout 등)는 한정적으로 정해져있다. &lt;b&gt;JS는 싱글 스래든인데 백그라운드에서 &lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;b&gt;어떻게 동시 실행될까?&lt;/b&gt; 백그라운드는 JS가 아니라 C++이다. 즉, 호출 스택만 JS이고 백그라운드와 태스크 큐는 다른언어라고 생각하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. promise 와 비동기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 코드를 보자. setTimeout, 비동기, promise가 모두 담겨진 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674539264548&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function endTest(){
    console.log('order :&amp;gt; four')
}
function run() {
    console.log('order :&amp;gt; one');
    setTimeout(()=&amp;gt; {
        console.log('order :&amp;gt; two')
    }, 0)
    new Promise((resolve)=&amp;gt;{
        resolve('order :&amp;gt; three');
    })
    .then(console.log);
    endTest();
}
console.log('start!')
setTimeout(run, 1000)
console.log('end!')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과값은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674539432734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;start!
end!
order :&amp;gt; one
order :&amp;gt; four
order :&amp;gt; three
order :&amp;gt; two&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 부분만 말하면, setTimeout(익명함수, 0)은 실행 후에 호출 스택에서 바로 사라지고 백그라운드로 이동시킨다. new Promise 부분은 처음엔 동기로 작동한다. 호출 스택의 run 함수 위에 new Promise가 쌓인다. 그리고 그 위에 resolve('order :&amp;gt; three')도 쌓인다. 실행 이후에 then을 만나는 순간 백그라운드로 이동시킨다. then 이후에 endTest()가 호출 스택 쌓인 후에 쌓은 호출 스택을 모두 실행한다. 호출 스택을 비운 후에, 백그라운드에 있는 setTimeout의 익명함수와 Promise에서 받은 then 을 처리해야한다. 기본적으로는 먼저 끝나는 부분이 태스크 큐로 이동을 하게 되지만, 여기서는 Promise가 우선 순위로 들어간다. 정리하면, 태스크 큐는 Promise가 우선 순위로 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;promise.then / promise.catch / process.nextTick 등은 태스크 큐에서 타이머 보다 먼저 들어가서 실행이 된다. 사실 설명의 편의상 promise를 태스크 큐에 들어간다고 했지만, 타미어 보다 먼저 실행이 되는 것들은 정확히는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;마이크로 태스크 큐&lt;/b&gt;&lt;/span&gt;에 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Micro Task Queue&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 예제부터 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674546439825&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log('start :&amp;gt; call stack');
setTimeout(() =&amp;gt; console.log('Task Queue'), 0);
Promise.resolve().then(() =&amp;gt; console.log('Micro Task Queue'));
console.log('end :&amp;gt; call stack')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674546467122&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;start :&amp;gt; call stack
end :&amp;gt; call stack
Micro Task Queue
Task Queue&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 정리하면 아래와 같은 순서로 실행되는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;호출 스택&lt;/li&gt;
&lt;li&gt;마이크로 태스크 큐&lt;/li&gt;
&lt;li&gt;태스크 큐&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면, 호출 스택을 모두 실행하면서 비동기 되는 함수들을 큐에 넣는다. 호출 스택이 모두 실행된 이후 이벤트 루트는 큐에서 함수를 가져와 호출 스택에 넣고 실행한다. 이 부분이 콜백 함수의 원리라고 할 수 있다. 콜백함수가 태스크 큐/마이크로 태스크 큐 중 어디에 들어가는지는 아래를 참고하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.1 Task Queue 콜백함수&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%; text-align: center;&quot;&gt;setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI 렌더링&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.2 Micro Task Queue 콜백함수&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;process.nextTick,&amp;nbsp;Promise, Object.observe, MutationObserver&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/JAVASCRIPT</category>
      <category>JavaScript Event Loop</category>
      <category>Micro Task Queue</category>
      <category>Task Queue</category>
      <category>이벤트 루프</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/540</guid>
      <comments>https://han-py.tistory.com/540#entry540comment</comments>
      <pubDate>Thu, 2 Feb 2023 17:55:13 +0900</pubDate>
    </item>
    <item>
      <title>[typescript] assert 로 타입 보장하기</title>
      <link>https://han-py.tistory.com/539</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트를 사용하면 기본적으로 타입 추론과 체크를 진행한다. 하지만 부분적으로 타입 보장이 안되는 경우가 있다. 왜냐하면 느슨하게 타입을 확인하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. nodejs에서 타입 단언 시키기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;assert를 활용해서 type을 단언하여 함수를 만들어 보자. assert() 함수는 런타임 시 불변 조건(invariant)를 검사하는 함수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674472968687&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const assert = require('assert')

function add(x, y) {
  assert(typeof x === 'number')
  assert(typeof y === 'number')

  return x * y // number * number
}

add(1, 1) // 2
add('1', 1)
// AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value: assert(typeof y === 'number')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 경우는 assert 에 넣은 타입이 들어오지 않으면, AssertionError를 던진다. 이러한 방식으로 느슨한 type 체크 부분을 강화 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. assert&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;assert 모듈은 특정 조건이 참인지 거짓인지를 확인하여, 조건이 거짓인 경우 오류를 발생시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674473739838&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = 1;
let b = 2;

assert(a &amp;lt; b); 
console.log(&quot;assert가 통과한다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a&amp;lt; b 라는 값이 참이므로 오류를 발생하지 않는다. 아래의 예를 다시 확인해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674473747921&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = 1;
let b = 2;

assert(a &amp;gt; b); // AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value
console.log(&quot;이 부분의 console은 작동하지 않는다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;assert가 false인 경우는 위와 같이 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value Error&lt;/span&gt;&lt;/b&gt;를 발생 시킨다. 위와 같은 방식으로 null 타입을 아래와 같이 체크 할 수있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674516915622&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import assert from 'assert'

const target = null
assert(target !== null) // error 발생&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/typescript</category>
      <category>AssertionError</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/539</guid>
      <comments>https://han-py.tistory.com/539#entry539comment</comments>
      <pubDate>Mon, 30 Jan 2023 10:14:37 +0900</pubDate>
    </item>
    <item>
      <title>OR 연산자(||)와 병합 연산자(??) 기초 정리</title>
      <link>https://han-py.tistory.com/538</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;nodejs를 통해 백엔드를 만들거나 react로 프런트를 만들 때, null 이나 undefined를 피하기 위한 분기를 조금더 똑똑하게 해보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Nullish coalescing operator (??)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 병합 연산자에 대해 알아보자. 병합 연산자는 왼쪽 피연산자기 null이거나 undefined의 경우는, 오른쪽 피연산자를 반환하고, 그렇지 않은 경우는 왼쪽을 반환한다. 아래의 예를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674467198180&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const foo = null ?? 'default string';
console.log(foo);
// &quot;default string&quot;

const aoo = undefined ?? 'default string';
console.log(aoo);
// &quot;default string&quot;

const boo = false ?? 'default string';
console.log(boo);
// false

const coo = '' ?? 'default string';
console.log(coo);
// &quot;&quot;


const doo = 0 ?? 42;
console.log(doo);
// Expected output: 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시에서 주의 할 점은 OR 연산자와 다르게 boolean이나 0과 같은 기본 if문의 false 값에 상관없이, null과 undefined의 경우만 반응한다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. OR 연산자(Logical OR)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용과 바로 비교를 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1674467473792&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const foo = null || 'default string';
console.log(foo);
// &quot;default string&quot;

const aoo = undefined || 'default string';
console.log(aoo);
// &quot;default string&quot;

const boo = false || 'default string';
console.log(boo);
// &quot;default string&quot;

const coo = '' || 'default string';
console.log(coo);
// &quot;default string&quot;


const doo = 0 ?? 'default string';
console.log(doo);
// &quot;default string&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 부분에서 확인 할 부분은 '' 부분도 오른쪽 피연산자를 반환한다는 것이다. 나머지 부부은 위 예제를 확인하면 이해가 가능 할 것이다. 기본적으로 or 연산자는 왼쪽에서 오른쪽으로 연산을 진행한다. 아래의 예를 보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674468005518&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function A() {
  console.log('called A');
  return false;
}

function B() {
  console.log('called B');
  return true;
}

console.log(B() || A());
// called B
// true

console.log(A() || B());
// called A
// called B
// true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 피연산자의 return값이 true이면 오른쪽 피연산자의 로직을 수행하지 않는 것을 확인 할 수 있다. 정리하면 아래의 값은 false로 리턴한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;null;&lt;/li&gt;
&lt;li&gt;NaN;&lt;/li&gt;
&lt;li&gt;0;&lt;/li&gt;
&lt;li&gt;empty string (&quot;&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;or&lt;span&gt;&amp;nbsp;&lt;/span&gt;''&lt;span&gt;&amp;nbsp;&lt;/span&gt;or&lt;span&gt;&amp;nbsp;&lt;/span&gt;``);&lt;/li&gt;
&lt;li&gt;undefined&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &amp;amp;&amp;amp; 연산자와 || 연산자의 우선순위&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위는 &amp;amp;&amp;amp;가 먼저 우선순위로 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674468164603&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;true || false &amp;amp;&amp;amp; false      // returns true
(true || false) &amp;amp;&amp;amp; false    // returns false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;() 괄호가 포함 된다면 먼저 연산하지만, 그렇지 않은 경우는 ||가 먼저 연산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. coalescing operator example(??)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병합연산자를 ??=를 조금더 활용하는 방법을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674468421653&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function config(options) {
  options.duration ??= 100;
  options.speed ??= 25;
  return options;
}

config({ duration: 125 }); // { duration: 125, speed: 25 }
config({}); // { duration: 100, speed: 25 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;??= 의 경우에 값이 null 이나 undefined의 경우에만 값이 대입하고, 그렇지 않는 경우는 무시하고 넘어간다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;config({ duration: 125 });&lt;/b&gt;&lt;/span&gt; 의 경우는 duration의 값이 있기 때문에 100이 포함되지 않았지만, speed의 경우는 값이 포함되어 있지 않기 때문에 25가 대입되어 return 되는 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AND(&amp;amp;&amp;amp;), OR(||)과 ??를 같이 사용하는 것은 불가능 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674468727451&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;null || undefined ?? &quot;foo&quot;; // raises a SyntaxError
true &amp;amp;&amp;amp; undefined ?? &quot;foo&quot;; // raises a SyntaxError&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 괄호를 사용하여 우선순위를 지정하는 것은 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674468776900&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(null || undefined) ?? &quot;foo&quot;; // returns &quot;foo&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 : &lt;a href=&quot;https://developer.mozilla.org/en-US/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/en-US/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/JAVASCRIPT</category>
      <category>&amp;amp;&amp;amp;</category>
      <category>??</category>
      <category>JavaScript</category>
      <category>Logical AND</category>
      <category>Logical OR</category>
      <category>Nullish coalescing operator (??)</category>
      <category>||</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/538</guid>
      <comments>https://han-py.tistory.com/538#entry538comment</comments>
      <pubDate>Fri, 27 Jan 2023 20:14:32 +0900</pubDate>
    </item>
    <item>
      <title>[React] HTMLElement 기초 정리</title>
      <link>https://han-py.tistory.com/524</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 HTMLElement에 대해 기초단계에서 자세하게 알려주진 않는것 같다. 하지만, 공부를하면, 기초가 중요하다는 것을 알게되는 것 같다. react를 배우면 가상돔을 다루지만, ref과 같은 깊은 지식으로 들어갈 수록 기초의 부재는 빠른 성장을 막는다. 또한 typescript로 적용을 하다보면 상속 시 HTMLElement 에 관련된 이야기들이 많이 나온다. 오늘은 HTMLElement에 대해 이야기를 풀어볼까 한다. 깊게 들어가면 끝도 없다. 따라서 이 글을 통해 맛보기로 기초개념을 이해한 후에, 필요하다면 관련 내용을 조금 더 공부해 보기를 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1640&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJQbhG/btrU132Eb9o/6o2E4DApTnwS8KwSDDVha0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJQbhG/btrU132Eb9o/6o2E4DApTnwS8KwSDDVha0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJQbhG/btrU132Eb9o/6o2E4DApTnwS8KwSDDVha0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJQbhG%2FbtrU132Eb9o%2F6o2E4DApTnwS8KwSDDVha0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1640&quot; height=&quot;560&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1640&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예를 추가하자면, 우리가 javascript에서 사용하는 appendChild는 Node가 제공하는 method이다. appendChild는 Node 에서 상속받아서 사용되는 것이라고 할 수 있다. 간단히 예를 들면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674461352425&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let test = document.createElement('p');
test.innerTest = 'test'
document.body.appendChild(test)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 순수함수와 다르게 &lt;b&gt;react&lt;/b&gt;에서는 아래와 같은 순서로 변환을 한다. 18 버전 기준으로 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674464056038&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import { createRoot } from &quot;react-dom/client&quot;;
import &quot;./index.css&quot;;
import App from &quot;./App&quot;;
import reportWebVitals from &quot;./reportWebVitals&quot;;

const rootElement = document.getElementById(&quot;root&quot;);
const root = createRoot(rootElement);
root.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
);

reportWebVitals();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ReactDom.createRoot는 리액스트 실행하는 root div 태그에 연동을 한다.&lt;/li&gt;
&lt;li&gt;React.createElement는 HTML 요소를 가상 DOM 객체로 구현한다.&lt;/li&gt;
&lt;li&gt;.render 부분은 가상 DOM 객체를 물리 DOM 객체로 변환한다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상 DOM은 기본적으로 메모리를 사용하기 때문에 빠르다. 그리고 변경 전 DOM과 현재 DOM을 비교하여 변경된 부분의 DOM 만 업데이트 한 후에 render된다. 따라서 부분의 요소가 변경 시 전체를 변경해야하는 물리 DOM에 비해 효율적이라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. element 란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;element란 무엇일까? element를 구성하고 있는 tag는 &amp;lt;a&amp;gt;, &amp;lt;p&amp;gt;, &amp;lt;div&amp;gt; 같은 것들을 태그라고 한다. 이러한 태그들은 &lt;span&gt;&amp;lt;a&amp;gt;&amp;lt;/a&amp;gt;, &amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;, &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;span&gt;&amp;nbsp;와 같이&lt;/span&gt;&lt;/span&gt; 시작태그와 종료태그로 나누어 진다. 시작태그는 &amp;lt;input type=&quot;text&quot; /&amp;gt; 과 같이 속성과(attribute) 값(value)을 가질 수 있다. element는 이러한 시작태그와 종료태그가 모두 포함된 것을 말한다. 조금 더 나아가면 html은 이러한 element들로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTMLElement 인터페이스란 무엇일까? 인터페이스 자체는 객체가 제공해야 할 여러 기능을 정의한 규약이라고 할 수 있다. 웹 브라우저는 document.createElement 메서드를 통해서 목적에 맞게 객체를 구현 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. HTMLElement&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dom tree에 대한 간단한 알고 가야할 시기가 왔다. 문서 객체 모델(DOM; Document Object Model)은 말그대로 객체로 element를 보는 것이다. 아래의 간단한 html 코드를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672480213016&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE HTML&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위를 tree 구조로 보면, html 태그 내부에 head, body 태그가 들어가고, body 내부에 div가 포함되는 것을 알 수 있다. 간소하게 말하자면, 이게 바로 DOM트리이다. 기본적은 html은 html 태그 내부에 다 포함되는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. typescript 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typescript를 사용해서 조금 더 관련 이해도를 높여보자. 예를 들어 아래와 같은 Button 컴포넌트를 만들었다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672511895422&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface ButtonProps {
  children: string;
  size: &quot;lg&quot; | &quot;md&quot; | &quot;sm&quot; | &quot;xs&quot;;
  onClick: () =&amp;gt; void;
}

const Button = ({children, size, onClick}: ButtonProps) =&amp;gt; {
  return (
    &amp;lt;button onClinck={onClick}&amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  )
}

export default Button&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 보면, interface로 전달 받을 props를 정해해서 받은 것을 알 수 있다. 위의 컴포넌트는 아래와 같이 사용하기가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672512070393&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Button from &quot;Button&quot;;

const App = () =&amp;gt; {
  return &amp;lt;Button onClick={() =&amp;gt; console.log(&quot;Button event~&quot;)}&amp;gt;Button&amp;lt;/Button&amp;gt;;
};

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 돌아가긴한다. 그러나 고민을 해봐야할 부분은, button 컴포넌트를 만들 때, html button의 기본 규약을 지키면서 만드는 것이 당연히 좋다. 그리고, 기존 button 에 포함된 onClick 같은 것들은 만들지 말고 기존 것을 쓰는 것이 더 좋다. 즉, 여기서는 interface로 새롭게 만드는 것이 아니라, 기존의 HTMLButtonElement를 상속 받아서 버튼 컴포넌트를 만드는 것이 당연히 좋다. 예를들면 원래 버튼이 가지고 있는, disabled authfocus 같은 것들을 원래의 것을 사용하기 위해서는 HTMLButtonElement를 상속 받는 것이 당연히 좋다. 이러한 내용을 반영하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672512327942&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface ButtonProps extends React.ButtonHTMLAttributes&amp;lt;HTMLButtonElement&amp;gt; {}

const Button = (props: ButtonProps) =&amp;gt; {
  return &amp;lt;button {...props} /&amp;gt;;
};

export default Button;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTMLButtonElement는 무엇일까? 위에서 이야기한 HTMLElement의 하위 개념이다. HTMLElement 하위에는 HTMLButtonElement 뿐만 아니라 HTMLDivElement를 포함한 여러 태그 element들이 포함된다. 확장하면, 각각의 커스텀 태그를 만들때, 기본 HTMLElement 내부에 있는 표준으로 상속 받아서 사용한다면, 기존 태그의 속성들을 유지하면서 커스텀 컴포넌트를 만들 수 있게 된다. 정리하면, ButtonHTMLAttributes&lt;span&gt;&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;같은 것들을 ButtonProps를 정의할 수 있음을 알 수 있다. 그리고 처음에 typescript 로 표현한 예시는 size props를 정해진 것만 통과하게 했다. 또한 ButtonHTMLAttributes 정의 없이 모든 props를 만들어도 된다. 각각의 경우에 따라 장단점이 있다. 구현하는 프로젝트에 따라 정책을 정해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 인터페이스들의 이름은 복잡해 보인다. 하지만 &lt;b&gt;HTML요소명Element&lt;/b&gt; 형태의 이름 규칙을 발견 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. HTMLButtonElement&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTMLElement의 직관적인 구조는 아래와 같다. 우리는 button에 대해 조금더 깊게 알아보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;1190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n0BDL/btrU1OxNLmG/PGWkcrDzbu5ZDK0cLoMAn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n0BDL/btrU1OxNLmG/PGWkcrDzbu5ZDK0cLoMAn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n0BDL/btrU1OxNLmG/PGWkcrDzbu5ZDK0cLoMAn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn0BDL%2FbtrU1OxNLmG%2FPGWkcrDzbu5ZDK0cLoMAn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1374&quot; height=&quot;1190&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;1190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTMLElement 에 HTMLButtonElement이 포함된다는 것은 위에서 알아보았다. HTMLElement에 포함되는 개념 중에 대표로 &lt;b&gt;HTMLButtonElement&lt;/b&gt;를 알아보도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ef6cX9/btrU45r6oP1/2NzB7WYKS9XfmRxCs1pBI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ef6cX9/btrU45r6oP1/2NzB7WYKS9XfmRxCs1pBI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ef6cX9/btrU45r6oP1/2NzB7WYKS9XfmRxCs1pBI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fef6cX9%2FbtrU45r6oP1%2F2NzB7WYKS9XfmRxCs1pBI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1576&quot; height=&quot;466&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;button의 type으로는 submit, reset, button으로 세가지가 있다. 세 가지에 따라 다른 역할을 한다. 기본값은 submit 이다. 즉 아래의 두 버튼은 같은 버튼이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672586424281&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;123&amp;lt;/button&amp;gt;
&amp;lt;button type=&quot;submit&quot;&amp;gt;123&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;form 내부에서 쓰면, 자동으로 submit이 되고 새로고침이 된다. 그러나 form 내에서 안쓰면 상관이 없긴하다. 헷갈린다면, 그냥 type을 button으로 고정으로 적고 이벤트에서 로직을 작성해 주는게 좋다. 이러한 type은 HTMLButtonElement.type에 속성값이 들어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하는 도중에 공통된 버튼이 있어 컴포넌트로 버튼을 하나 만들었다. 위의 고려사항들을 모두 고려해서 컴포넌트를 만들 수 있을까? 없다. 왜냐하면, 간단해 보이는 버튼 하나도 아래와 같이 규약이 존재하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2920&quot; data-origin-height=&quot;1256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nFW6G/btrU44fSeNz/WE3OCIBGRqrztDPYruj630/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nFW6G/btrU44fSeNz/WE3OCIBGRqrztDPYruj630/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nFW6G/btrU44fSeNz/WE3OCIBGRqrztDPYruj630/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnFW6G%2FbtrU44fSeNz%2FWE3OCIBGRqrztDPYruj630%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2920&quot; height=&quot;1256&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2920&quot; data-origin-height=&quot;1256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 규약을 고려하지 않은 컴포넌트는 좋지않다. 좋은 컴포넌트를 만들고자하는 시도는 좋지만, 우리는 웹을 이루는 기본에 대한 생각을 하면서 새로운 컴포넌트를 만들어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Frontend를 최근 배운 사람들은 이 글을 읽지 않을 것이다. 아마 typescript를 접하면서 궁금한 부분이 있는 사람들을 타겟으로 기초적인 부분들을 풀어보았다. 넓은 범위를 간략하게 지나갔기 때문에 궁금한 점이 있다면 조금 더 구글링을 해보도록 하자.&lt;/p&gt;</description>
      <category>Web</category>
      <category>button type</category>
      <category>HTMLButtonElement</category>
      <category>HTMLElement</category>
      <category>react 18</category>
      <category>typescript</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/524</guid>
      <comments>https://han-py.tistory.com/524#entry524comment</comments>
      <pubDate>Tue, 24 Jan 2023 18:54:38 +0900</pubDate>
    </item>
    <item>
      <title>[React] Webpack-웹팩 기초 정리</title>
      <link>https://han-py.tistory.com/448</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;웹팩은 모듈 번들러로 프런트엔드 프레임워크에서 가장 많이 사용되고 있다. 쉽게 말해서 웹팩은 웹을 구성하고 있는 HTML, CSS, JAVASCRIPT 등을 모두 각각의 모듈로 보고 이를 조합해서 하나로 만들어주는 도구라고 생각하면 된다. 모듈 번들러라는 단어가 어려울 수 있지만, 쉽게 이야기하면 모듈 변환기라고 생각해도 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mV3pD/btrusOREr8b/hxqYXoeBfH9RGGOCr6gxuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mV3pD/btrusOREr8b/hxqYXoeBfH9RGGOCr6gxuk/img.png&quot; data-alt=&quot;https://webpack.kr/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mV3pD/btrusOREr8b/hxqYXoeBfH9RGGOCr6gxuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmV3pD%2FbtrusOREr8b%2FhxqYXoeBfH9RGGOCr6gxuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1067&quot; height=&quot;466&quot; data-origin-width=&quot;1067&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://webpack.kr/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은 웹팩 공식홈페이지에 있는 그림이다. 그림을 보면, 복잡한 파일들을 간단하게 결합시켜주는 느낌이라는 것을 한눈에 파악할 수 있다. 시작에 앞서 nodejs에 관한 환경 설정은 아래의 url로 대체한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/486&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Web/nodejs] - nodejs 기초 총 정리&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Webpack&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프런트엔드에서 대표적인 모듈 번들러인 웹팩에 대해 알아보자. 웹팩은 복잡한 입력 모듈을 단순한 모듈로 변화해 준다. 이때, 웹팩을 결과물을 &lt;b&gt;번들&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. React 에서의 웹팩&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트(CRA)를 개발하다보면, package.json에서 react-scripts로 프로젝트 시작하는 경험을 해보았을 것이다. 그리고 프로젝트를 시작하는 방식은 개발모드와 배포 시 사용하는, 빌드 모드가 있다는 것을 알 수있다. 아래는 package.json 중 실행과 관련 있는 script 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674278387513&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  ...
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;react-script start&quot;,
    &quot;build&quot;: &quot;react-scripts builds&quot;,
    ...
  },
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scripts의 내용은 단순하다. npm run start를 실행하면, react-script start 가 실행되고, npm run build를 치면 react-scripts builds의 명령어가 실행된다. 이러한 내용을 바탕으로 조금 더 깊게 알아가 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. React 에서의 build mode 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 명령어를 통해 build를 진행하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674276927652&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm run build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드가 완료된 후 내부 파일을 보면, chunk.js나 main.js 같은 이름들의 &lt;b&gt;번들&lt;/b&gt; 파일들을 확인 할 수 있다. 그리고 내부에는 index.html 파일도 확인 할 수 있다. 정리하면, 빌드 시 index.html에 반들어진 번들 파일들이 적용되어 있는 것 이라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드를 완료 했으면, 리액트를 실행하면 된다. 이때 중요한 것은, &lt;b&gt;리액트 하나로는 실행이 안된다&lt;/b&gt;. 웹 서버를 만들고 그 위에 리액트 어플리케이션을 실행해야한다. 아래와 같이 웹 서버를 위한 serve 프로그램을 설치 후 실행을 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674277185139&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install -g serve
$ serve -s build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식으로 build부분을 위의 웹 서버가 아닌, nginx나 apache 웹 서버를 react application을 올리면 배포가 바로 가능 하다. 배포관련은 &lt;a href=&quot;https://han-py.tistory.com/408&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 눌러서 AWS에서 간단히 배포하는 방식을 알아 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. React 에서의 development mode 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 명령어를 통해 개발 명령어를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674278700689&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm start&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드를 시작했을 때와는 먼가 느낌이 다르다. build시에는 서버를 실행하려면 serve라는 프로그램을 통해 실행을 헀다. 그러나 개발모드로 실행 시에는 서버가 바로 동작을 하는 것을 알 수 있다. 개발 모드에서는 webpack이 서버로 동작을 하기 때문이다. react 실행 시 작동 순서는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;react-scripts로 실행 시 파일들을 처음 빌드하여 번들 파일로 만든다.&lt;/li&gt;
&lt;li&gt;번들 파일들이 반영된 index.html을 만든다&lt;/li&gt;
&lt;li&gt;웹 브라우저 실행 후 접속을 한다.(localhost:3000)&lt;/li&gt;
&lt;li&gt;웹팩이 준비한 index.html에서 scripts 태그의 자바스트립트 코드를 실행하여 웹 페이지에 보여준다.&lt;/li&gt;
&lt;li&gt;웹팩 서버는 새로운 파일의 생성 수정 삭제를 감시한다.&lt;/li&gt;
&lt;li&gt;처음 빌드 한 번들 파일과 비교 후 변경된 부분이 있다면, 해당 부분만 빌드한다.&lt;/li&gt;
&lt;li&gt;브라우저의 웹팩과 상호작용하여 변경 된 부분을 실시간으로 반영한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 코딩과 동시에 서버에 반영되는 부분은 hot reloading이라고 한다. 관련 부분은 여기를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. webpack.config.js&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹팩을 초입자들은 지협적인 개념까지 처음부터 공부하는 것은 효율적이지 못하다. 웹팩을 바로 시작할 수 있는 핵심적인 개념 5가지를 먼저 설명한 후에 실습을 진행해 보겠다. 웹팩에 대한 설정은 webpack.config.js을 통해 할 수 있다. 따라서 아래의 내용들을 webpack.config.js에 들어가는 내용이라고 우선 이해를 하고 글을 읽자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. Entry&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Entry 속성은 자바스크립트 파일의 진입점이자 시작점을 지정하는 것이라고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1645964234273&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// webpack.config.js
module.exports = {
  entry: './path/to/my/entry/file.js',
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본값은 ./src/index.js 이지만,&amp;nbsp; 위의 코드처럼 다른 엔트리 포인트를 지정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. Output&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;output 속성은 빌드 시 적용되는 속성에 대한 설정이다. output 내부의 값으로는 path와 filename을 기본값으로 적어주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1645964612862&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// webpack
const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js',
    clean: true,
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;path는 번들(=빌드=변환)된 파일을 생성할 경로를 적어주는 곳이다. 주의할 점은 상대 경로를 넣으면 찾을 수가 없다. 따라서 path라는 모듈을 넣고 resolve 매서드를 사용하여야, 절대 경로로 웹팩이 찾을 수 있다.&lt;/li&gt;
&lt;li&gt;filename은 생성될 파일 이름을 지정해 주는 속성이다.&lt;/li&gt;
&lt;li&gt;clean 부분은 안 적어도 된다. 그러나 true로 설정을 해주면, 새롭게 생성될 경로에 다른 파일들이 있다면, 다 삭제하고 새로운 파일을 생성하는 속성이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3. Loaders&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;webpack은 기본적으로 javascript와 JSON 파일만 이해한다. 따라서 그 외 .css 파일 같은 경우를 webpack이 이해하게 하려면 Loaders 부분에서 설정을 한다. 기본적으로 Loaders webpack 설정에는 &lt;b&gt;test&lt;/b&gt;와 &lt;b&gt;use&lt;/b&gt;로 두 가지 속성을 가진다. 아래의 예를 참고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1645965743078&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js',
  },
  module: {
    rules: [{ 
    	test: /\.txt$/, 
        use: 'raw-loader' 
    }],
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주의 - 기본적으로 webpack에서 위와 같이 rules를 정할 때, module.rules 아래에 정의한다. 최상단의 rules에서 정의하지 않는다.&lt;/li&gt;
&lt;li&gt;위의 내용을 보면 test와 use라는 필수 속성을 가진다.&lt;/li&gt;
&lt;li&gt;test에 있는 내용은 .txt 파일을 선택한다는 말이다.&lt;/li&gt;
&lt;li&gt;use는 test에서 지정한 파일들을 use에 적은 모듈을 활용해서 불러들인다는 말이다.&lt;/li&gt;
&lt;li&gt;위의 코드를 풀이하면, .txt 파일이 확인되면 번들에 추가하기 전에 raw-loader을 사용하여 변환하라는 뜻이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;test: /\.txt$/&lt;/b&gt;&lt;/span&gt; 주의사항 - 따옴표를 사용하지 않았다.&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;'test: /\.txt$/' - .txt로 끝나는 모든 파일에서 일치하도록 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&quot;test: /\.txt$/&quot; - 절대경로에서 .txt로 끝나는 단일 파일과 일치하도록 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4. Plugins&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시를 통해 이해를 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1645966413342&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 내장 plugin에 접근하는 데 사용

module.exports = {
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
  plugins: [
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new HtmlWebpackPlugin({
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title: &quot;keyboard&quot;,
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;template: &quot;./index.html&quot;,
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inject: &quot;body&quot;,
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;favicon: &quot;./favicon.ico&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;})
  ],
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;title은 키보드라 하겠다. html의 head부분의 title을 넣어주는 것과 같다.&lt;/li&gt;
&lt;li&gt;template은 최상위 루트의 상대 경로로 지정해 준다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;inject : 파일을 번들했을 때, js를 바디에 넣어줄지 해더에 넣어줄지를 설정하는 것이다. 이 값 안 넣으면 html이 해더에 들어간다.&lt;/li&gt;
&lt;li&gt;favicon은 파이콘(웹페이지 상단의 title 옆의 아이콘)으로 사용할 위치를 적어두는 거다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.5. Mode&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값은 production으로 none으로 지정하면 webpack에 내장된 환경별 최적화를 활성화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1645966855669&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  mode: 'production',
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;string = 'production': 'none' | 'development' | 'production'&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;development : html, css, javascript 코드의 난독화 제공하지 않는다. 이 말은 번들 후에도 적은 코드 형태를 유지한다.&lt;/li&gt;
&lt;li&gt;production : html, css, javascript 코드의 난독화 제공한다. 이 말은 번들 후에 코드의 사이즈를 최대한 줄여 배포 환경에 최적화시킨다는 말이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를&amp;nbsp; CLI 인수로 전달하려면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1645966960518&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;webpack --mode=development&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 웹팩의 기초 개념이다. 추가적인 내용들은 웹팩을 만들면서 추가하도록 하자.&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>react-scripts</category>
      <category>webpack</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/448</guid>
      <comments>https://han-py.tistory.com/448#entry448comment</comments>
      <pubDate>Sat, 21 Jan 2023 15:33:48 +0900</pubDate>
    </item>
    <item>
      <title>Single Source of Truth 이란,</title>
      <link>https://han-py.tistory.com/531</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Single Source of Truth이란 단일 진실 공급원이라고 불리기도 하고, 신뢰 가능한 단일 소스라고 불리기도 하는 것 같다. 사실 길게 적을 필요도 없다. 간략하기 정의 하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;같은 내용을 중복해서 사용하지 말고, 하나의 내용을 참고 해서 사용한다는 말이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Single Source of Truth를 웹개발로 풀어보면,&amp;nbsp;&lt;b&gt;백엔드&lt;/b&gt; 같은 경우는 DB 핸들링을 하는 장소를 하나의 파일만 만들어서 사용하는 것과 같다고 할 수 있다. &lt;b&gt;프런트엔드&lt;/b&gt;의 경우는 상태관리를 전역으로 한군데 해서 하는 것 과 같다고 할 수 있다. 보통은 제어컴포넌트와 비제어컴포넌트를 이야기 할 때 많이 사용된다.&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/531</guid>
      <comments>https://han-py.tistory.com/531#entry531comment</comments>
      <pubDate>Sat, 14 Jan 2023 15:07:46 +0900</pubDate>
    </item>
    <item>
      <title>error - TypeError: resolver is not a function</title>
      <link>https://han-py.tistory.com/528</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;error&amp;nbsp;-&amp;nbsp;TypeError:&amp;nbsp;resolver&amp;nbsp;is&amp;nbsp;not&amp;nbsp;a&amp;nbsp;function&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;javascript로 코드를 적다보니, 위와 같은 에러(error - TypeError: resolver is not a function)가 발생했다. resolever의 의미는 사전적 의미는 변환기를 의미한다. 코드를 아무리 봐도 에러를 못찾겠다면, 아래의 정답을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;74&quot; data-origin-height=&quot;23&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1tPTc/btrV0Qm9vnm/lExWhlYLRTIUm4rkvCWkc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1tPTc/btrV0Qm9vnm/lExWhlYLRTIUm4rkvCWkc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1tPTc/btrV0Qm9vnm/lExWhlYLRTIUm4rkvCWkc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1tPTc%2FbtrV0Qm9vnm%2FlExWhlYLRTIUm4rkvCWkc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;74&quot; height=&quot;23&quot; data-origin-width=&quot;74&quot; data-origin-height=&quot;23&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export를 적어주자. 함수를 적었지만, export해주지 않으면 호출 시 찾을 수 없어서 나는 에러이다.&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/528</guid>
      <comments>https://han-py.tistory.com/528#entry528comment</comments>
      <pubDate>Wed, 11 Jan 2023 23:07:39 +0900</pubDate>
    </item>
    <item>
      <title>디바운싱(debouncing) 기초 정리</title>
      <link>https://han-py.tistory.com/522</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;디바운싱(debouncing)은 연속으로 호출되는 함수들 중에 마지막 함수만 호출되도록 하는 것이다. 웹 이벤트를 제어하는 방법 중 하나로 쓰로틀링(&lt;span style=&quot;background-color: #ffffff; color: #444444;&quot;&gt;throttling&lt;/span&gt;)과 많이 나오는 개념 중 하나이다. 디바운싱의 예로 가장 많이 나오는 것은 웹 페이지의 스크롤이라고 할 수 있다. 웹 페이지 스크롤을 하면 연속적인 동작이다. 이러한 연속적인 동작에서 마지막에 호출되는 함수만 사용할 수 있도록 만드는 것이 디바운싱이라고 할 수 있다. input과 관련된 디바운싱 예시, 스크롤과 관련된 디바운싱 예시, react에서 hook으로 사용되는 디바운싱 예시도 담았다. 아래의 내용을 통해 디바운싱에 대한 이해도를 높여보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;978&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVXnf8/btrUSzNb9gK/ibo3GuxA9ISi5SsjvcmEKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVXnf8/btrUSzNb9gK/ibo3GuxA9ISi5SsjvcmEKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVXnf8/btrUSzNb9gK/ibo3GuxA9ISi5SsjvcmEKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVXnf8%2FbtrUSzNb9gK%2Fibo3GuxA9ISi5SsjvcmEKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1674&quot; height=&quot;978&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;978&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;디바운싱&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디바운싱은 따로 함수가 존재해서 코드 하나로 사용할 수 있는것은 아니다. 즉, 프로그램 기법으로 이해하면 좋다. 연속으로 발생하는 함수를 묶어서 마지막에 한 번만 실행하는 게 디바운싱이다. 연속으로 발생하는 함수를 무시하기 위해 setTimeout를 사용하여 구현한다. 우선은 브라우저가 제공하는 web API인 &lt;b&gt;setTimeout&lt;/b&gt; 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672146049980&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;setTimeout(callbackFunction, timeout)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;timeout은 함수가 시작되기 전 대기하는 밀리터리 초이다. 1000을 적으면 1초 후에 콜백함수가 실행한다는 이야기이다.&lt;/li&gt;
&lt;li&gt;callbackFunction은 함수가 timeout 이후에 실행되는 함수명이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 우리는 조금 더 알아야하는 web API가 있다. 바로 &lt;b&gt;clearTimeout&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672236514524&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;clearTimeout(timeoutID)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;timeoutID는 식별자로 setTimeout()에서 반환된 값을 timeoutID로 담아서 넣어주는 것이라 생각하면 된다. 자세한 예는 아래의 예에서 확인하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디바운싱 예제&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;input 태그&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input 검책 태그에 글을 작성 시,&amp;nbsp; 0.3 초마다 검색 결과 이벤트를 발생시키는 디바운싱 예제는 아래와 같다. 대부분 블로그에서 아래의 예를 보여준다. 아래의 예에서 디바운싱의 이해를 조금 더 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672237528770&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// html
&amp;lt;input type=&quot;text&quot; id=&quot;debouncing&quot; /&amp;gt;

// javascript
const inputElement = document.querySelector('#debouncing')

const handleInputChange = () =&amp;gt; {
  console.log('검색!');
}

let timerId;

inputElement.addEventListener('input', () =&amp;gt; {
  if (timerId) clearTimeout(timerId);
  timerId = setTimeout(handleInputChange, 300)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글 작성 시 timerId에 값이 있다면, setTimeout의 콜백함수를 진행하기전에 0.3초를 기다리고 있는 상태이다. 따라서 clearTimeout으로 timerId가 들어가서 기다리던 콜백함수가 취소된다. 즉, 0.3 초가 지나기 전에 검색창을 입력하면, 이전에 실행된 setTimeout은 취소되고 앞으로 의 새로운 setTimeout이 실행되는 것이다. 이러한 로직을 통해 0.3초 내에 반복되는 중복 실행을 방지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 위의 내용을 조금 더 업그레이드 해보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;input 태그 적용&lt;/h4&gt;
&lt;pre id=&quot;code_1672242383311&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// html
&amp;lt;input type=&quot;text&quot; id=&quot;debouncing&quot; /&amp;gt;
&amp;lt;div id=&quot;values&quot;&amp;gt;&amp;lt;/div&amp;gt;

// javascript
const inputElement = document.querySelector('#debouncing')
const devElement = document.getElementById('values');


const handleInputChange = () =&amp;gt; {
  devElement.textContent = e.target.value
}

let timerId;

inputElement.addEventListener('input', () =&amp;gt; {
  if (timerId) clearTimeout(timerId);
  timerId = setTimeout(handleInputChange, 300)
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적은 내용들을 0.3초마다 최신화하여 업데이트하는 내용이다. 조금 더 예시를 더 보자. 스크롤 관련된 디바운싱 적용한 예시는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1672242576329&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let timer;

const handleScroll = () =&amp;gt; {
    const scrollValue = document.documentElement.scrollTop;
    console.log(scrollValue);
}

document.addEventListener(&quot;scroll&quot;, () =&amp;gt; {   
    if (timer) clearTimeout(timer)
    timer = setTimeout(() =&amp;gt; handleScroll, 300);
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 예시들이 비슷하다. 이러한 예시들을 통해 앞으로 디바운신이 필요하다면, 적용해서 사용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;React 디바운싱 Hook&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Hook으로 만들어서 필요시 쉽게 불러와서 사용가능하도록 만들어 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1672238792555&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useCallback, useEffect } from 'react';

const useDebounce = (func, delay, deps) =&amp;gt; {
  const callback = useCallback(func, deps);
  useEffect(() =&amp;gt; {
    const timer = setTimeout(() =&amp;gt; callback(), delay);
    return () =&amp;gt; clearTimeout(timer);
  }, [callback, delay]);
};

export default useDebounce;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 유료 API를 사용하는 상황이라면, 디바운싱을 활용하며, 여러 번 호출을 방지하여 API를 막을 수 있다. 서비스 제작 중이라면, 디바운싱을 적용하는 것에 대해 한번 고민을 해보면 좋을 것 같다.&lt;/p&gt;</description>
      <category>Web/JAVASCRIPT</category>
      <category>debouncing</category>
      <category>디바운싱</category>
      <category>디바운싱 react hook</category>
      <category>버튼 디바운싱</category>
      <category>웹 최적화</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/522</guid>
      <comments>https://han-py.tistory.com/522#entry522comment</comments>
      <pubDate>Thu, 29 Dec 2022 00:55:54 +0900</pubDate>
    </item>
    <item>
      <title>[Flask] 플라스크 기초 정리</title>
      <link>https://han-py.tistory.com/339</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; python 으로 서버를 만드는 방법은 여러가지가 있다. 장고보다 쉬운 플라스크를 사용해서 서버를 만들어 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chi8V9/btrTSxb3rIh/VBTIxEEuZcPKSki1KciG11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chi8V9/btrTSxb3rIh/VBTIxEEuZcPKSki1KciG11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chi8V9/btrTSxb3rIh/VBTIxEEuZcPKSki1KciG11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fchi8V9%2FbtrTSxb3rIh%2FVBTIxEEuZcPKSki1KciG11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;731&quot; height=&quot;238&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. Flask 개발환경 구축&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 Flask의 개발 환경은 python 3.7 이상부터 지원가능하다. 기본적으로 파이썬은 설치되었다고 생각하고 진행하겠다. 아래의 명령어로 가상환경을 만들자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671256177698&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 파이썬 가상환경 만들기
$ mkdir myproject
$ cd myproject
$ python -m venv venv

// 가상환경 접속하기
[window] $ source venv/scripts/activate
[mac/ubuntu] $ source venv/bin/activate

// 가상환경 나오기
$ deactivate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Flask 설치하기 / 시작하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상환경 내부에서 아래의 명령어를 치면된다.&lt;/p&gt;
&lt;pre id=&quot;code_1671256741009&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ pip install Flask&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app.py를 만들고 아래를 적자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1612849057094&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask   # flask 클래스를 가져온다.
app = Flask(__name__)     # 플라스크 객체를 생성한다. __name__은 현재 실행 중인 모듈 이름을 전달하는 것이다.

@app.route('/')           # 기본서버 127.0.0.1:5000 뒤에 붙는 주소를 적어준다.
def index():              # 위의 주소를 호출 시 보여 줄 것을 함수로 작성해 준다. 중복되지 않도록만 적어주면된다.
    return 'Hello world'  # 문자열이 출력된다.
    
if __name__ == '__main__':# 다른데서 부르면 실행하지 마라는 뜻이다.
    app.run()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;app.run(debug=True)&amp;nbsp;&amp;nbsp;&amp;nbsp;# debug=True를 하면 고칠 때마다 자동으로 실행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;app.run(port = 80)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 포트 번호를 바꿔준다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;ex_ app.run(debug=True, port=80)&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1612849169174&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from flask import Flask
app = Flask(__name__)

@app.route(&quot;/&quot;)
def hello():
    return &quot;Hello World!&quot;

if __name__ == &quot;__main__&quot;:
    app.run(debug=True, port=80)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드가 플라스크의 기본 구조라고 할 수 있다. python파일을 실행시키면 flask를 실행 가능하다.&amp;nbsp; 이때 주의할 점은 파일이름을 flask.py라고 하면 충돌이 나니 주의해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. FLASK에서 Template, Static파일&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-size=&quot;size20&quot;&gt;template는 html파일을 저장한다.&lt;/li&gt;
&lt;li data-ke-size=&quot;size20&quot;&gt;static 파일은 css, js, Image 파일을 만든다.&amp;nbsp;&lt;/li&gt;
&lt;li data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;html 파일은 render_template() 함수를 이용하셔 연동이 가능하다. 예시는 아래와 같다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1612849525164&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route(&quot;/&quot;)
def hello():
    return render_template('index.html')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 template와 staic의 파일 기본 구조는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- app.py&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- template(폴더)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- static(폴더)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라우팅을 사용법(Variable rules)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&amp;lt;&amp;gt;는&amp;nbsp;데이터를&amp;nbsp;나타낸다.&amp;nbsp;&amp;nbsp;&amp;nbsp;ex&amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&amp;lt;&amp;gt;는&amp;nbsp;데이터&amp;nbsp;타입을&amp;nbsp;정할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int:age&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;함수의&amp;nbsp;인자값으로&amp;nbsp;사용된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1612849633502&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route('/user/&amp;lt;username&amp;gt;')
def show_user_profile(username):  # &amp;lt;&amp;gt;내용은 함수의 인자로 꼭 적어줘야한다.
    return 'user %s' %username

@app.route('/user/&amp;lt;username&amp;gt;/&amp;lt;int:age&amp;gt;')
def show_usr_profile_age(username, age):
    return 'user %s 나이 %d' %(username, age)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;REQUEST 객체&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;데이터는 request 객체 안에 들어가 있고 클라이언트는 request를 서버로 전달한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;request를 사용하려면 import해줘야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;request객체의 속성&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Form&amp;nbsp;-&amp;nbsp;form은&amp;nbsp;매개변수가&amp;nbsp;key고&amp;nbsp;값을&amp;nbsp;value로&amp;nbsp;하는&amp;nbsp;딕셔너리다.&amp;nbsp;&amp;nbsp;&amp;nbsp;:&amp;nbsp;POST&amp;nbsp;방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;args - URL 부분 중 물음표 뒤에 있는 쿼리 문자열(query string)을 parse한다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: GET 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Cookies - 쿠키 이름과 그 값을 가지는 딕셔너리 객체이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;files - 업로드 파일과 관련된 데이터이다.&lt;/li&gt;
&lt;li&gt;method - 현재 request method를 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 예시를 보고 설명을 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1612851078686&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# get, POST는 html의 form에서 GET POST를 method로 지정해 준다.
@app.route('/login', methods=['GET'])
def login():
    return 'get 이름은 %s' % request.args.get('username')

@app.route('/login', methods=['POST'])
def loginp():
    result = request.form
    return render_template('form_result.html', result=result)
    # return 'post 이름은 %s' % request.form['username']     # 딕셔너리에 접근하기 때문에 대괄호이다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Method 사용법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라우팅 주소에 methods 변수에 GET, POST를 설정한다.&lt;/li&gt;
&lt;li&gt;reqeust.method == 'POST'와 같이 request.method는 문자열을 반환 받아 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 보낼 때는 보통은 html의 form tag를 사용한다. form은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1612851179692&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# index.html
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;Practice&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    첫번째 연습 파일.
    &amp;lt;form action=&quot;/login&quot; method=&quot;POST&quot;&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;username&quot;&amp;gt;
        &amp;lt;input type=&quot;submit&quot; value=&quot;확인&quot;&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&amp;lt;!-- # name을 반드시 적어줘야한다. --&amp;gt;
&amp;lt;!-- # name을 안적으면 서버에서 값을 찾을 수가 없다 --&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 form 태그를 사용하여 method부분에 POST나 GET을 적어서 활용해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;request.args&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;get 방식으로 들어온 데이터를 받을 수 있다.&lt;/li&gt;
&lt;li&gt;request.args.get('username') 으로 데이터를 받을 수 가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1612851380186&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route('/login', methods=['GET'])
def login():
    return 'get 이름은 %s' % request.args.get('username')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;request.form&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;post 방식으로 들어온 데이터를 받을 수 있다.&lt;/li&gt;
&lt;li&gt;request.form으로 데이터를 받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1612853894518&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route('/login', methods=['POST'])
def loginp():
    result = request.form
    return render_template('form_result.html', result=result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;request.form으로 데이터를 받아서 result 변수에 넣는다. 그리고 result=result에서 오른쪽 result가 받은 데이터 변수를 넣은 것이고 왼쪽 result가 html에서 사용가능 한 string이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1612854053314&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{% key, value in result.items() %}
  &amp;lt;p&amp;gt;{{ key }}&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;{{ Value }}&amp;lt;/p&amp;gt;
{% endfor %}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 형식으로 사용가능하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;request.files&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;form형식으로 아래와 같이 HTML에서 적는 것이 가능하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1612854178155&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;form action = &quot;/fileUpload&quot; method = &quot;POST&quot; enctype = &quot;multipart/form-data&quot;&amp;gt;
      &amp;lt;input type = &quot;file&quot; name =&quot;file&quot; /&amp;gt;
      &amp;lt;input type = &quot;submit&quot;/&amp;gt;
    &amp;lt;/form&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;form의 actiom은 url을 적는 부분이다.&lt;/li&gt;
&lt;li&gt;부트스트랩을 사용하더라도 form 부분에 action, method, enctype를 추가해 줘야한다.&lt;/li&gt;
&lt;li&gt;input의 type이 file 부분에 name을 추가해 줘야 값을 그 파일을 통해 값을 찾을 수 있다.&lt;/li&gt;
&lt;li&gt;python의 저장 로딕은 아래와 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1612865062498&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.route('/fileupload', methods=['POST'])
def filleupload():
    f = request.files['file']
    dirname = os.path.dirname(__file__) + '/uploads/' + f.filename    # __file__ : 현재파일인 경로를 알려준다.
    f.save(dirname)
    return 'uploads 성공'&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>flask</category>
      <category>flask 설치하기</category>
      <category>flask 파일받기</category>
      <category>python 가상환경 구축</category>
      <category>간단히 구현하는 flask</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/339</guid>
      <comments>https://han-py.tistory.com/339#entry339comment</comments>
      <pubDate>Sat, 17 Dec 2022 15:37:49 +0900</pubDate>
    </item>
    <item>
      <title>error router because the scheme does not have a registered handler.</title>
      <link>https://han-py.tistory.com/517</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;routing이나 redirect를 할 때 error router because the scheme does not have a registered handler. 이러한 애러가 발생했다.&amp;nbsp; 만약 test 환경에서 local 관련 redirect에러가 발생했다면, 해결책은 간단하다. localhost 앞에 http:// 를 넣어주면된다. 끝!&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>error router because the scheme does not have a registered handler.</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/517</guid>
      <comments>https://han-py.tistory.com/517#entry517comment</comments>
      <pubDate>Wed, 14 Dec 2022 03:11:07 +0900</pubDate>
    </item>
    <item>
      <title>[error] File was processed with these loaders: react-refrash</title>
      <link>https://han-py.tistory.com/516</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;다음 애러에 대한 해결책을 제시한다. typescript를 처음 척용하면서 삽질을 많이 했다. 나의경우는 create-react-app을 통해 프로젝트를 생성했다. 그러니 File was processed with these loaders: react-refrash 관련 애러가 발생했다. 추가로 require('/node_modules/react-refresh/runtime.js' 라는 에러도 발생을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에 대한 export가 계속 안되길래 as const 나 enum 등등 구글링해서 나오는 모든 것들을 해도 안됐었다. 약 3인정도 고민을 했었다. react-refresh관련 에러가 나오는 이유는 단순했다. color상수 설정을 해서 아래와 같이 만들었었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1670673771073&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const Colors :{[index:string]: string}  = {
  white: '#fff',
  black: '#282b31',
  gray1: '#12102d',
  gray2: '#222548',
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 export가 계속 안되는것이다. 해결책은 단순하다. CRA로 프로젝트를 생성 시, typescript와 같은 dev 개발환경 설정은 CRA로 생성된 src 폴더 내부에만 적용이 된다. 만약 src 밖에서 설정을 했다면, typescript가 안먹혀서 import를 할 수 가 없다. 나는 &lt;b&gt;src 안으로 위의 colors 객체를 옮기자&lt;/b&gt; 관련 오류가 사라지고 프로젝트가 잘 돌아갔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 src 박에서도 관련 내용이 적용되게 하려면, babel setting을 다시 해주면 된다.&lt;/p&gt;</description>
      <category>Web/React</category>
      <category>File was processed with these loaders: react-refrash</category>
      <category>require('/node_modules/react-refresh/runtime.js</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/516</guid>
      <comments>https://han-py.tistory.com/516#entry516comment</comments>
      <pubDate>Sat, 10 Dec 2022 21:06:56 +0900</pubDate>
    </item>
    <item>
      <title>[react]  CRUD 기초 총 정리</title>
      <link>https://han-py.tistory.com/431</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;REACT CRUD&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 가장 기초이자 기본이 crud이다. 다른 블로그들과의 차이점은 가능한 모든 case를 정리하여 성능비교까지 적어보려한다.(계속 업데이트 예정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 javascript에서 todo list를 만들면서 crud를 많이 구현해 보았을 것이다. &lt;b&gt;일반적인 Javascript와 react CRUD의 차이점은 &lt;span style=&quot;color: #555555;&quot;&gt;Immutability(불변성)&lt;/span&gt;에 있다고 할 수 있다. 기본적으로 리액트는 Mutation 하지 않게 작성을 해야한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 예는 데이터 값을 직접 변경(mutate)하는 예이다. 즉, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;mutation한 수정&lt;/b&gt;&lt;/span&gt;이라고 할 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648038993036&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let player = {score: 1, name: 'Jeff'};
player.score = 2;

// 결과값
// {score: 2, name: 'Jeff'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 예는 &lt;span style=&quot;color: #555555;&quot;&gt;원하는 변과 값이 포함된 새로운 복사본 데이터로 교체하는 것이다. 다른 말로 표현하면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Mutation하지 않은 데이터 수정&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648039022177&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let player = {score: 1, name: 'Jeff'};

let newPlayer = Object.assign({}, player, {score: 2});
// 이제 player는 변하지 않았지만 newPlayer는 {score: 2, name: 'Jeff'}입니다.

// 객체 spread 구문을 사용한다면 이렇게 쓸 수 있습니다.
// var newPlayer = {...player, score: 2};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 쉽게 말해서 객체나 리스트를 직접 수정하는 것이 아니라, 참조형에 대한 깊은 복사를 통해 새로운 객체를 만들어서 react에 넣어주는 방식을 취해야한다. 이렇게 &lt;span style=&quot;color: #555555;&quot;&gt;Immutable한 방식은 아래의 이점을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 복잡한 기능들이 단순해 진다. (Complex&lt;span&gt;&amp;nbsp;&lt;/span&gt;Features Become&lt;span&gt;&amp;nbsp;&lt;/span&gt;Simple)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 변화를 감지할 수 있다. (Detecting Changes)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. React가 다시 랜더할 시기를 결정할 수 있다. (Determining When to Re-Render in React)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 자세한 설명의 아래의 url을 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/459&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://han-py.tistory.com/459&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MOCK 데이터 셋팅&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 리스트 내부에 객체가 포함되어 있는 형태이다. 대부분 아래와 같은 방식으로 JSON 데이터를 받아온다. 따라서 데이터를 아래와 같이 받았다고 생각하고 계속보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 데이터는 GraphQL을 쓰지않는한, 백엔드로 호출해서 가지고 온다. 만약 API를 쓰려면 react 생명주기에 따라 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;useEffect&lt;/b&gt; &lt;/span&gt;내부에서 사용하면 된다.(함수형 컴포넌트) 관련 내용은 아래에서 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/402&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://han-py.tistory.com/402&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1648040400912&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[react web] Hook 기초_useEffect&quot; data-og-description=&quot;0. 들어가면서 &amp;nbsp;사실 이 부분은 쉽게 이해하기 어렵다. 그리고 가장 잘 정리 된 곳은 공식문서이다. https://ko.reactjs.org/docs/hooks-effect.html Using the Effect Hook &amp;ndash; React A JavaScript library for b..&quot; data-og-host=&quot;han-py.tistory.com&quot; data-og-source-url=&quot;https://han-py.tistory.com/402&quot; data-og-url=&quot;https://han-py.tistory.com/402&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fTE1R/hyNNtUeC8E/lJCqG0RsDs4Yedxd9NI7A0/img.png?width=379&amp;amp;height=91&amp;amp;face=0_0_379_91,https://scrap.kakaocdn.net/dn/zzuGN/hyNOxHtXND/FXgdWVrGAPq1QOvfk6OQR1/img.png?width=379&amp;amp;height=91&amp;amp;face=0_0_379_91&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/402&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://han-py.tistory.com/402&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fTE1R/hyNNtUeC8E/lJCqG0RsDs4Yedxd9NI7A0/img.png?width=379&amp;amp;height=91&amp;amp;face=0_0_379_91,https://scrap.kakaocdn.net/dn/zzuGN/hyNOxHtXND/FXgdWVrGAPq1QOvfk6OQR1/img.png?width=379&amp;amp;height=91&amp;amp;face=0_0_379_91');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[react web] Hook 기초_useEffect&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;0. 들어가면서 &amp;nbsp;사실 이 부분은 쉽게 이해하기 어렵다. 그리고 가장 잘 정리 된 곳은 공식문서이다. https://ko.reactjs.org/docs/hooks-effect.html Using the Effect Hook &amp;ndash; React A JavaScript library for b..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;han-py.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 받은 데이터는 useState에 담게된다. useState관련 내용은 아래에서 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/398&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://han-py.tistory.com/398&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1648040450369&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[react web] Hook 기초_useState&quot; data-og-description=&quot;핵심만 압축했습니다. 디테일한 부분은 공식문서를 확인해 주세요! 0. 들어가면서 &amp;nbsp;Hook의 종류는 굉장히 많다. 만약 간단한 프로젝트만 한다면, useState, useEffect 정도만 알아도 충분하고, 복잡한 &quot; data-og-host=&quot;han-py.tistory.com&quot; data-og-source-url=&quot;https://han-py.tistory.com/398&quot; data-og-url=&quot;https://han-py.tistory.com/398&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bSXXV1/hyNOqhhTNx/S5fLdnl3FclQoyZwl8RBy1/img.png?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/398&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://han-py.tistory.com/398&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bSXXV1/hyNOqhhTNx/S5fLdnl3FclQoyZwl8RBy1/img.png?width=800&amp;amp;height=437&amp;amp;face=0_0_800_437');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[react web] Hook 기초_useState&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;핵심만 압축했습니다. 디테일한 부분은 공식문서를 확인해 주세요! 0. 들어가면서 &amp;nbsp;Hook의 종류는 굉장히 많다. 만약 간단한 프로젝트만 한다면, useState, useEffect 정도만 알아도 충분하고, 복잡한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;han-py.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 가상의 데이터를 받았다는 가정하에 받은 데이터를 state로 넣은 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1642033651844&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [datas, setDatas] = useState([
    {
        id: 1,
        name: kang,
    },
    {
        id: 2,
        name: py,
    },
    {
        id: 3,
        name: han,
    }
]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;Create&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648040828210&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  handleClick(i) {
    const history = this.state.history;
    const squares=Array(9).fill(null),
    this.setState({
      history: history.concat([{
        squares: squares,
      }]),
    });
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1642034496281&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const createOrUpdateCard = data =&amp;gt; {
  setDatas(datas =&amp;gt; {
    const updated = { ...datas };
    updated[data.id] = data;
    return updated;
  });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 setDatas 내부에서 화살표 함수를 통해 이전데이터를 가지고 오는 로직이다. 이러한 방식으로 가져오지 않고 그냥 datas를 쓰면, 데이터가 안들어가는 경우가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Read&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 map 함수를 사용해서 넣어주면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. update&lt;/p&gt;
&lt;pre id=&quot;code_1648040803411&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const createOrUpdateCard = data =&amp;gt; {
  setDatas(datas =&amp;gt; {
    const updated = { ...datas };
    updated[data.id] = data;
    return updated;
  });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. delete&lt;/p&gt;
&lt;pre id=&quot;code_1642034623301&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const deleteData = data =&amp;gt; {
  setDatas(datas =&amp;gt; {
    const updated = { ...Datas };
    delete updated[data.id];
    return updated;
  });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data로 인자를 받는다. 그리고 받은 내용이 토함된 id값을 삭제한다. 이때 다른 일반적인 delete 로직과의 차이점은 map을 사용하지 않았다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 위와 같이 사용을 많이한다. 위의 단점은 CRUD 시에, 특정한 타겟 값을 찾을 때 리스트 내부의 객체들을 하나씩 들어가서 id값을 확인해야한다. 따라서 아래와 같이 데이터를 받은 후에 변경을 해주면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648040717002&quot; class=&quot;yaml&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [datas, setDatas] = useState({
    1: {
        id: 1,
        name: kang,
    },
    2: {
        id: 2,
        name: py,
    },
    3: {
        id: 3,
        name: han,
    }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 list를 객체로 변경한다. 그리고 내부의 객체의 id값을 밖으로 빼내어 key로 설정을 한다.&lt;/p&gt;</description>
      <category>Web/React</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/431</guid>
      <comments>https://han-py.tistory.com/431#entry431comment</comments>
      <pubDate>Sat, 10 Dec 2022 15:33:44 +0900</pubDate>
    </item>
    <item>
      <title>[npm] dependencies, devDependencies, peerDependencies</title>
      <link>https://han-py.tistory.com/515</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우리는 nodejs를 사용할 때 습관적으로 npm을 명령어를 사용하여 프로젝트를 생성하고 관리한다. 그리고 생성된 프로젝트에서&amp;nbsp;package.json파일을 참고하여 프로젝트를 진행한다. 그렇다면, 우리가&amp;nbsp;자연스럽게 사용하는 &lt;b&gt;npm&lt;/b&gt;에 대해 조금 더 깊게 알아보는 시간을 가져볼까 한다. 그리고 npm i로 설치를 진행하면, package.json의 &lt;b&gt;dependencies&lt;/b&gt;에 설치한 패키지들이 들어가는 것을 확인할 수 있다. 이러한 dependencies 뿐만 아니라 &lt;b&gt;devDependencies&lt;/b&gt;와 &lt;b&gt;peerDependencies&lt;/b&gt;, &lt;b&gt;peerDependenciesMeta&lt;/b&gt;도 같이 알아보고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zirjw/btrR9GQWQyo/wsc5BBBWNXYrAdB0Do7ws1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zirjw/btrR9GQWQyo/wsc5BBBWNXYrAdB0Do7ws1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zirjw/btrR9GQWQyo/wsc5BBBWNXYrAdB0Do7ws1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzirjw%2FbtrR9GQWQyo%2Fwsc5BBBWNXYrAdB0Do7ws1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1269&quot; height=&quot;321&quot; data-origin-width=&quot;1269&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Build amazing things&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. NPM&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 문구는 npm 공식 문서에 들어가면 나오는 말이다. npm은 node package manager의 줄임말로 nodejs의 패키지를 관리하는 도구라고 할 수 있다. 우리가 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;npm i &amp;lt;패키지명&amp;gt;&lt;/b&gt; &lt;/span&gt;으로 다운 받는 패키지들은 오픈소스를 기반으로 한다. 따라서 우리가 만든 패키지 모듈도 npm에 올려서 다른 사람들이 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NPM은 어떻게 탄생 했을까? NPM의 탄생 이유는 웹 자체의 global 한 특성이 하나의 이유라고 볼 수 있다. 웹 javascript의 경우 &amp;lt;script&amp;gt;를 이용해 bootstrap 같은 외부 javascript 파일을 CDN으로 가져오는 것이 가능하다. 그러나 이러한 파일들은 독립적인 scope를 가지지 않는다. 따라서 global인 전역 변수에 바인딩 되어(호이스팅) 코드가 중복되는 문제가 발생하여 원하는 동작을 하지 않거나, 기존에 돌아가던 기능도 돌아가지 않는 경우가 발생한다. 이러한 문제에 대한 해결을 모듈로써 해결하려는 시도가 있어왔고, 이러한 모듈을 모아서 관리할 수 있는 패키지 관리에 대한 기술발전이 이루어진 것이다. npm(node package manager)은 로그인이 필요 없이 명령어 한 줄로 패키지를 설치하고 바로 사용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. npm 명령어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자이고, nodejs를 사용한다면, 아래의 명령어는 한 번쯤 쳐봤을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669593784603&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install react&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어는 인증이나 회원가입이 필요 없이 react package를 받는 명령어라고 할 수 있다. 좀 더 정리하면 package를 받는 방식은 아래와 같다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. package 설치하기&lt;/h4&gt;
&lt;pre id=&quot;code_1669593875123&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install &amp;lt;package&amp;gt;
$ npm i &amp;lt;package&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두 명령어는 동일한 명령어로 install 대신 i 만 쳐도 가능하다. install 명령어를 실행하면, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;package.json&lt;/b&gt;&lt;/span&gt;에 설치한 목록이 포함되고, Root Directory에 node_modules 폴더가 생기고, 그 내부에 패키지가 설치된다. 이때 설치된 패키지는 현재 만들고 있는 프로젝트 내에서 만 사용된다. 이를 지역 설치라 한다. 그렇다면, 한번 설치된 후에 내 컴퓨터 안의 모든 프로젝트에서 사용할 수 있게 설치하는 것을 전역 설치라고 하는데, 관련 명령어는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1669594166355&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install -g &amp;lt;package&amp;gt;
$ npm i -g &amp;lt;package&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-g 옵션을 추가하면 지역이 아닌 전역으로 패키지가 설치된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 우리가 npm으로 설치하면, package.json에 설치한 목록이 보인다고 했다. 그런데 package.json이 없어서 설치가 안 되는 경우도 있을 것이다. 무슨 말일까? package.json을 만들지 않았다면, 프로젝트 생성을 하지 않은 것이다. 여기서 우리는 프로젝트를 생성하는 방식에 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. 프로젝트 시작하기&lt;/h4&gt;
&lt;pre id=&quot;code_1669594656855&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm init
$ npm init -y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어를 치면 우리는 npm을 시작할 수 있는 package.json파일을 만들 수 있다. 쉽게 말하면, package.json파일을 기준으로 프로젝트를 만든다고 생각하면 된다. 그리고 package.json 파일 내부에 프로젝트와 관련된 정보들이 JSON형식으로 담겨있다. 조금 더 관련 정보를 알고 싶다면 &lt;a href=&quot;https://docs.npmjs.com/cli/v9/configuring-npm/package-json&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&gt;를 참고하자. -y 없이 npm init만 치면 프로젝트에 관련된 정보를 치도록 세팅이 된다. 이런 거 없이 디폴트 값으로 바로 세팅을 하고 싶다면, -y를 추가해서 프로젝트를 생성하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 &lt;a href=&quot;https://create-react-app.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;아래와 같이&lt;/a&gt; 한 줄의 명령어로 바로 프로젝트를 실행할 수 있도록 기본 세팅을 해주는 기능을 제공한다. 이는 react에서 build tool에 최적화된 라이브러리를 자동으로 다 설정하여 같이 설치해준다. 공식문서에서는 이렇게 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;span style=&quot;background-color: #f7f7f7; color: #1c1e21;&quot;&gt;Create React App lets you&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;focus on code, not build tools&lt;/b&gt;&lt;span style=&quot;background-color: #f7f7f7; color: #1c1e21;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ npx create-react-app my-app&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초입자의 기준으로 위의 명령어를 한 번에 쳐서 시작하는 것이 편하고, 위의 명령어를 치면 자연스럽게 package.json이 딸려오기 때문에 package.json에 대한 중요성을 간과하는 경우가 많다. &lt;b&gt;정리해서 결론만 말하면, 프로젝트는 package.json에서 시작된다.&amp;nbsp;&lt;/b&gt;그리고 npm을 사용하는 하나의 프로젝트의 기준은 package.json이 root directory라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3. --save-dev 패키지 설치하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typescript를 사용한다고 해보자. typescript는 개발단계만 필요하지, 배포 단계에서는 필요 없다. 기본적으로 개발에 필요한 패키지를 배포 시에도 같이 설치한다면, 매우 비효율적이다. 따라서 개발에 필요한 패키지와 배포 운영에 필요한 패키지를 분리할 필요가 있어 보인다. 개발단계에서만 필요한 패키지는 아래와 같이 설치하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669630777649&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install --save-dev &amp;lt;package&amp;gt;
$ npm install -D &amp;lt;package&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 devDependencies에 저장이 된다. 이때 --save-dev와 -D는 같은 명령어이다. 패키지 설치 시 버전 관리에 대해 조금 더 알아보는 시간을 가져보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. package.json의 dependencies&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 정리를 해보면, npm init로 프로젝트를 생성하면 package.json파일이 생성된다. 그리고 npm i로 패키지를 다운로드하면 다운로드한 패키지의 이름과 버전이 명시되어 dependencies 내부에 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669631408506&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dependencies {
    &quot;mongodb&quot;: &quot;^4.11.0&quot;,
    &quot;mongoose&quot;: &quot;^6.7.0&quot;,
    &quot;next&quot;: &quot;12.2.5&quot;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dependencies 내부에는 프로젝트 구동 시 필요한 패키지들만 포함이 되고, 개발 시 필요한 패키지들은 아래의 devDependencies에 포함이 된다. 내부를 조금 더 자세히 보면 왼쪽이 패키지의 이름이고, 오른쪽이 숫자가 들어가 있음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mongodb의 버전(&lt;b&gt;&quot;^4.11.0&quot;&lt;/b&gt;)을 보면 ^와 , 4, 11, 0 이 나타나는 것을 확인할 수 있다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;^&lt;/b&gt;&lt;/span&gt;는 캐럿이라 부르고, 그 뒤에 나오는 버전은 순서대로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Major 버전, Minor 버전, Patch 버전이라고&lt;/b&gt; &lt;/span&gt; 부른다. 각각의 버전에 대한 특징은 다음과 같다. 당연하게도 무언가가 업그레이드된다면, 버전이 올라가게 된다. 그리고 업그레이드되는 버전의 정도에 따라 다음과 같이 버전 구분을 할 수 있다. 이전 버전과 호환되지 않는 변경사항이 생기면, Major버전이 올라간다. 그리고 이전 버전과 호환 가능한 버전인 경우는 Minor버전이 증가된다. Patch버전은 기능에 대한 버전이 변경되었을 때 증가한다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;^(캐럿)&lt;/b&gt;&lt;/span&gt;은&lt;span&gt; &lt;/span&gt;패키지&lt;span&gt; 재인스톨 &lt;/span&gt;시&lt;span&gt; &lt;/span&gt;동일한&lt;span&gt; Major &lt;/span&gt;내에서&lt;span&gt; Minor&lt;/span&gt;의&lt;span&gt; &lt;/span&gt;버전이업그레이드된 것이있다면&lt;span&gt;, Minor &lt;/span&gt;버전이&lt;span&gt; &lt;/span&gt;자동으로업그레이드되게 한다&lt;span&gt;. 잘 안쓰이긴&lt;/span&gt;하지만&lt;span&gt;, &lt;/span&gt;캐럿&lt;span&gt; &lt;/span&gt;말고&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; ~(틸트)&lt;/b&gt;&lt;/span&gt;도&lt;span&gt; &lt;/span&gt;가끔&lt;span&gt; &lt;/span&gt;사용한다&lt;span&gt;. ~&lt;/span&gt;를&lt;span&gt; ^ &lt;/span&gt;대신&lt;span&gt; &lt;/span&gt;붙여주면&lt;span&gt;, Minor&lt;/span&gt;버전&lt;span&gt; &lt;/span&gt;내에서&lt;span&gt; Patch&lt;/span&gt;버전이업그레이드된것이&lt;span&gt; &lt;/span&gt;있다면&lt;span&gt;, &lt;/span&gt;재&lt;span&gt; &lt;/span&gt;인스톨&lt;span&gt; &lt;/span&gt;시&lt;span&gt; &lt;/span&gt;업데이트를&lt;span&gt; &lt;/span&gt;시켜주는&lt;span&gt; &lt;/span&gt;것이라&lt;span&gt; &lt;/span&gt;생각하면&lt;span&gt; &lt;/span&gt;된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. devDependencies&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로&lt;span&gt; devdependencies&lt;/span&gt;는&lt;span&gt; &lt;/span&gt;배포에&lt;span&gt; &lt;/span&gt;필요하지&lt;span&gt; &lt;/span&gt;않지만&lt;span&gt;, &lt;/span&gt;개발&lt;span&gt; &lt;/span&gt;시에&lt;span&gt; &lt;/span&gt;필요한&lt;span&gt; &lt;/span&gt;패키지들을&lt;span&gt; &lt;/span&gt;넣어두는&lt;span&gt; &lt;/span&gt;곳이다&lt;span&gt;. &lt;/span&gt;만약&lt;span&gt; &lt;/span&gt;다른&lt;span&gt; &lt;/span&gt;사용자가&lt;span&gt; &lt;/span&gt;내가&lt;span&gt; &lt;/span&gt;만든&lt;span&gt; &lt;/span&gt;패키지를다운로드하여서사용한다고&lt;span&gt; &lt;/span&gt;해보자&lt;span&gt;. &lt;/span&gt;이때&lt;span&gt;, &lt;/span&gt;다른&lt;span&gt; &lt;/span&gt;사용자는&lt;span&gt; &lt;/span&gt;우리가&lt;span&gt; &lt;/span&gt;개발&lt;span&gt; &lt;/span&gt;시&lt;span&gt; &lt;/span&gt;사용했던&lt;span&gt; &lt;/span&gt;패키지나&lt;span&gt; &lt;/span&gt;테스트&lt;span&gt; &lt;/span&gt;도구를&lt;span&gt; &lt;/span&gt;사용하거나&lt;span&gt; &lt;/span&gt;필요하지&lt;span&gt; &lt;/span&gt;않을&lt;span&gt; &lt;/span&gt;것이다&lt;span&gt;. &lt;/span&gt;이러한&lt;span&gt; &lt;/span&gt;패키지를&lt;span&gt; &lt;/span&gt;분리하기&lt;span&gt; &lt;/span&gt;위해&lt;span&gt; &lt;/span&gt;사용하는&lt;span&gt; &lt;/span&gt;것이&lt;span&gt; devDependencies&lt;/span&gt;라고&lt;span&gt; &lt;/span&gt;할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. peerDependencies&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 peerDependency에 관련된 글에 관심을 가진다면, 버전 관리와 종속성에 대한 고민을 해봤을 것이다. 종속성에 관련된 이야기는 다른 글에서 이야기하고, 여기서는 peerDependencies 자체에 집중에서 설명해 보겠다. 아래의 경우를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;HANPY&lt;/b&gt;라는 프로젝트를 구현 중이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;npm i &amp;lt;패키지&amp;gt;&lt;/b&gt;&lt;/span&gt; 명령어로 특정 기능을 구현하기 위해 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;han 4.4.2 패키지&lt;/b&gt;&lt;/span&gt;와 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;py 3.2.7 패키지&lt;/b&gt;&lt;/span&gt;를 다운로드하였다.&lt;/li&gt;
&lt;li&gt;에러가 발생하여 찾아보니, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;han 4.4.2 패키지&lt;/b&gt;&lt;/span&gt;를 작동하기 위해서는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;py 1.3.2 패키지&lt;/b&gt;&lt;/span&gt;가 내부에 설치되어 있었다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;py 3.2.7 패키지&lt;/b&gt;&lt;/span&gt;와 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;py 1.3.2 패키지&lt;/b&gt;&lt;/span&gt;가 충돌하여 오류가 발생한 것을 알았다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 프로젝트 내에서 다른 패키지들을 다운로드하여 사용할 때, 버전이 많이 다른 패키지의 경우에 충돌이 나서 오류가 나는 경우가 있다. 이러한 경우, 패키지를 설치 시 하나의 버전만 다운로드할 수 있도록 해야 한다. (물론, 위의 예의 경우는 버전 차이가 크므로 특정 기능을 구현할 수 있는 다른 패키지를 찾는 게 좋아 보인다....) 이 경우 해결책은 특정 버전이 아닌 경우, 설치를 못하게 막는 기능을 가진 peerDependencies를 사용하여 사전에 에러를 방지하면 된다. 즉, peerDependency의 포인트는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;peerDependencies 내부에 적은 패키지의 버전 외의 다른 패키지를 사용하면 오류가 발생한다는 것이다.&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669632334221&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  &quot;peerDependencies&quot;: {
    &quot;react&quot;: &quot;^16.8 || ^17.0 || ^18.0&quot;,  
   },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 react의 버전이 16.8, 17.0, 18.0에 포함된 버전만 설치를 허용한다는 이야기이다. 당연한 이야기지만, 버전 명시를 유연하기 하지 않으면 설치만 하면 에러가 발생할 것이다. 위의 경우로 ^(캐럿)이 포함되어 유현하게 작성된 것을 확인할 수 있다. 관련된 버전 명시하는 방식은 아래와 같이 다양하게 사용 가능하다. (&lt;a href=&quot;https://docs.npmjs.com/cli/v9/configuring-npm/package-json&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;npm 공식문서 참고&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669637177355&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;dependencies&quot;: {
    &quot;foo&quot;: &quot;1.0.0 - 2.9999.9999&quot;,
    &quot;bar&quot;: &quot;&amp;gt;=1.0.2 &amp;lt;2.1.2&quot;,
    &quot;baz&quot;: &quot;&amp;gt;1.0.2 &amp;lt;=2.3.4&quot;,
    &quot;boo&quot;: &quot;2.0.1&quot;,
    &quot;qux&quot;: &quot;&amp;lt;1.0.0 || &amp;gt;=2.3.1 &amp;lt;2.4.5 || &amp;gt;=2.5.2 &amp;lt;3.0.0&quot;,
    &quot;asd&quot;: &quot;http://asdf.com/asdf.tar.gz&quot;,
    &quot;til&quot;: &quot;~1.2&quot;,
    &quot;elf&quot;: &quot;~1.2.3&quot;,
    &quot;two&quot;: &quot;2.x&quot;,
    &quot;thr&quot;: &quot;3.3.x&quot;,
    &quot;lat&quot;: &quot;latest&quot;,
    &quot;dyl&quot;: &quot;file:../dyl&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 추가적으로 peerDependencies에 포함된 패키지들이 아직 설치가 되어 있지 않더라도 경고 메시지가 발생한다. 그러나 아래의 peerDependenciesMeta을 추가하면, 경고 메시지가 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. peerDependenciesMeta&lt;/h3&gt;
&lt;pre id=&quot;code_1669636992182&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;HANPY&quot;,
  &quot;version&quot;: &quot;3.1.6&quot;,
  &quot;peerDependencies&quot;: {
    &quot;han-kong&quot;: &quot;1.1&quot;
  },
  &quot;peerDependenciesMeta&quot;: {
    &quot;han-kong&quot;: {
      &quot;optional&quot;: true
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언급했던 것과 같이, 만약 han-kong을 설치하지 않으면 프로젝트에서 경고 메시지를 준다. 그러나 peerDependenciesMeta에 optional을 true라고 주면, 설치하지 않아도 아무런 경고 메시지를 주지 않는다. &lt;b&gt;이름 그대로 peerDependenciesMeta는 peerDependencies의 메타 정보를 담고 있다고 보면 된다.&lt;/b&gt; 좀 더 세부 정보는 &lt;a href=&quot;https://docs.npmjs.com/cli/v9/configuring-npm/package-json&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;npm Docs&lt;/a&gt;에서 참고 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. resolutions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json에는 &lt;b&gt;resolutions&lt;/b&gt;라는 값이 있다. peerDependencies와 비슷하지만 다른점이 있어 추가로 작성해본다. 내가 사용하고 있는 하위 버전의 package 버전을 고정하고 싶을 때, 사용한다. 예들들어 나는 han이라는 패키지를 사용하고 있다. 그리고 &lt;b&gt;han&lt;/b&gt;이라는 패키지는 py라는 패키지를 이용해서 만들었다. 최근 &lt;b&gt;py&lt;/b&gt; 1.3.2 패키지가 &lt;b&gt;py&lt;/b&gt; 1.4.0으로 버전 업그레이드를 했는데, 오류가 크게 발생해서 &lt;b&gt;han&lt;/b&gt;이라는 패키지도 돌아가지 않는다면, &lt;b&gt;py&lt;/b&gt;패키지 버전을 &lt;b&gt;py&lt;/b&gt; 1.3.2로 고정을 해야할 필요성이 생긴다. 이떄 아래와 같이 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669716787529&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;resolutions&quot;: {
  &quot;han/py&quot;: &quot;1.3.2&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 말해서, 하위 패키지의 버전을 유지할 이유가 생겼다면 resolutions를 사용해서 하위 버전을 고정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 부분이나 더 궁금한 부분이 있다면, 구글링을 통해 좀 더 공부해보면 좋을 것 같다.&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>dependencies</category>
      <category>devdependencies</category>
      <category>Node Package Manager</category>
      <category>npm</category>
      <category>package.json</category>
      <category>peerDependenciesMeta</category>
      <category>peerDependency</category>
      <category>resolutions</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/515</guid>
      <comments>https://han-py.tistory.com/515#entry515comment</comments>
      <pubDate>Mon, 28 Nov 2022 21:15:04 +0900</pubDate>
    </item>
    <item>
      <title>[nextjs] next.config.js 내부의 Rewrite 적용</title>
      <link>https://han-py.tistory.com/514</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;nextjs 버전이 올라가면서 유용한 기능들이 많이 생겨나고 있다. 여기서 사용해볼 Rewrite에 대해 알아보자. 원리는 간단하다. 나는 &lt;b&gt;/api/hanpy&lt;/b&gt;로 들어오는 요청을 처리하는 API를 만들었다. 그런데, &lt;b&gt;/hanpy&lt;/b&gt;로 들어오는 요청도 &lt;b&gt;/api/hanpy&lt;/b&gt;의 로직을 따르게 만들고 싶은 경우가 있을 것이다. 이때 사용하는 것이 Rewrite이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;356&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwo8tq/btrPS0cglME/6WZTmYYcXCGBxdFx71c6wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwo8tq/btrPS0cglME/6WZTmYYcXCGBxdFx71c6wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwo8tq/btrPS0cglME/6WZTmYYcXCGBxdFx71c6wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwo8tq%2FbtrPS0cglME%2F6WZTmYYcXCGBxdFx71c6wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;356&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 사용 준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npx를 통해 프로젝트 하나를 생성하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667176089994&quot; class=&quot;elixir&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npx create-next-app@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. rewrite 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npx를 통해 프로젝트 하나를 생성하자. 위와 같이 프로젝트를 만들면, 기본 next.config.js는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667176106146&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
}

module.exports = nextConfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 rewrite 시키기&lt;/p&gt;
&lt;pre id=&quot;code_1667176130527&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  async rewrites() {
    return [
      {
        source: '/abc',
        destination: '/'
      }
    ]
  }
}

module.exports = nextConfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에 대한 설명은 &lt;b&gt;/abc로 접근하면, 화면은 /을 보여주라는 이야기 이다.&lt;/b&gt; 아래와 같이 접속을 해도, 보여지는 것은 / 페이지가 보여진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;36&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vzkKv/btrPUnyb7OD/aKetLrO9FAulhmsst9jyl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vzkKv/btrPUnyb7OD/aKetLrO9FAulhmsst9jyl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vzkKv/btrPUnyb7OD/aKetLrO9FAulhmsst9jyl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvzkKv%2FbtrPUnyb7OD%2FaKetLrO9FAulhmsst9jyl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;364&quot; height=&quot;36&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;36&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 궁금한것은 만약 내가 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;/abc라는 페이지가 존재하는 경우에도 rewrite가 될까&lt;/span&gt;&lt;/b&gt;에 대한 궁금증이다. 아래의 예시를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1667176579646&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  async rewrites() {
    return [
      {
        source: '/rewrite/RewriteTest1',
        destination: '/'
      },
      {
        source: '/rewrite/RewriteTest3',
        destination: '/'
      }
    ]
  }
}

module.exports = nextConfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'/'와 '/rewrite/RewriteTest1' 은 존재하는 페이지이고, '/rewrite/RewriteTest3'은 만들지 않은 페이지 이다. 파일구조는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;262&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCViTA/btrPTjQmV6Q/pT0pNMr4HHu4qcbzF5LEI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCViTA/btrPTjQmV6Q/pT0pNMr4HHu4qcbzF5LEI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCViTA/btrPTjQmV6Q/pT0pNMr4HHu4qcbzF5LEI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCViTA%2FbtrPTjQmV6Q%2FpT0pNMr4HHu4qcbzF5LEI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;262&quot; height=&quot;174&quot; data-origin-width=&quot;262&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/rewrite/RewriteTest1 로 접속을 한 경우에는 rewrite가 되지 않고, /rewrite/RewriteTest1 페이지가 보인다. 그러나 /rewrite/RewriteTest3의 경우는 '/'로 rewrite 되는 것을 확인 할 수 있다. 정리하면, 기존에 페이지가 존재한다면, rewrite는 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. :path*&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;:path*&lt;/b&gt;는 뒤에 붙는 모든 것을 다 포함하는 의미이다. 아래의 예시를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1667177555598&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  async rewrites() {
    return [
      {
        source: '/abc/:path*',
        destination: '/'
      },
    ]
  }
}

module.exports = nextConfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 적으면 /abc, /abc/aa, /abc/dskfjwe 등등 모두가 다 '/'로 rewrite 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 심화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용만으로도 간단한 프로젝트는 rewrite가 가능하다. 조금 더 심화 부분은 위의 기본적인 개념을 이해하고 아래의 공식문서를 참고해서 필요한 내용을 찾아보면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/api-reference/next.config.js/rewrites&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/docs/api-reference/next.config.js/rewrites&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669559357017&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;next.config.js: Rewrites | Next.js&quot; data-og-description=&quot;Add rewrites to your Next.js app.&quot; data-og-host=&quot;nextjs.org&quot; data-og-source-url=&quot;https://nextjs.org/docs/api-reference/next.config.js/rewrites&quot; data-og-url=&quot;https://nextjs.org/docs/api-reference/next.config.js/rewrites&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cRkePV/hyQJBfZ5gF/3uAunYl8lIr9yZoqA3U8AK/img.png?width=1686&amp;amp;height=882&amp;amp;face=0_0_1686_882&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/api-reference/next.config.js/rewrites&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nextjs.org/docs/api-reference/next.config.js/rewrites&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cRkePV/hyQJBfZ5gF/3uAunYl8lIr9yZoqA3U8AK/img.png?width=1686&amp;amp;height=882&amp;amp;face=0_0_1686_882');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;next.config.js: Rewrites | Next.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Add rewrites to your Next.js app.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nextjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/nextjs</category>
      <category>api 없애기</category>
      <category>next.config.js</category>
      <category>NextJS</category>
      <category>nextjs redirect</category>
      <category>nextjs rewrite</category>
      <category>주소에서 api 없애기</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/514</guid>
      <comments>https://han-py.tistory.com/514#entry514comment</comments>
      <pubDate>Sun, 27 Nov 2022 23:30:15 +0900</pubDate>
    </item>
    <item>
      <title>도커(Docker) 기초 정리</title>
      <link>https://han-py.tistory.com/494</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;도커란 개발 시 application을 쉽고 빠르게 구축, 공유 및 실행할 수 있는 소프트웨어이다. 만약 프로젝트 시작 시, 환경설정부터 기본 세팅을 해야 한다. 하지만 도커를 사용하면 프로젝트마다 반복되는 세팅을 할 필요가 없다. 이러한 도커에 대한 기초에 대한 총정리를 아래의 글을 통해 풀어볼까 한다. 가능한 전부를 담기위해 노력했고 계속 업데이트 중이다. 이 글을 통해 도커 초급을 졸업하자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pZW9P/btrERmXQclh/R3If6AF9A9wxF3juqp7NGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pZW9P/btrERmXQclh/R3If6AF9A9wxF3juqp7NGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pZW9P/btrERmXQclh/R3If6AF9A9wxF3juqp7NGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpZW9P%2FbtrERmXQclh%2FR3If6AF9A9wxF3juqp7NGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;266&quot; height=&quot;101&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 도커의 탄생&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커란, 컨테이너 기반의 오픈소스 가상화 플랫폼이다. 실행환경을 컨테이너라는 것을 활용하여 추상화하여 동일한 인터페이스를 제공하기 때문에 프로그램의 배포 및 관리를 단순하게 해 준다. 사실 도커 이전의 개발자들은 개발 시 코드를 수정했을 때, 내&amp;nbsp; PC에서는 잘 돌아가지만 다른 PC에서는 에러가 생기는 경우를 많이 경험했을 것이다. 이러한 인프라의 가변성에 대한 문제는 항상 존재한다고 할 수 있다. 이러한 문제는 Docker가 나오면서 해결이 됐다고 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, docker가 다루는 근본적인 문제는 개발을 시작하기 위한 서버 셋팅에서 시작된다고도 할 수 있다. 기본적으로 &lt;b&gt;자체 서버를 운영&lt;/b&gt;하려면 다음과 같이 복잡한 순서를 따라야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;0.1. 자체서버운영&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커가 없는 시기에는 아래와 같은 프로세스를 통해 서버를 설치하고 운영하였다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;용량 산정&lt;/li&gt;
&lt;li&gt;서버 구매&lt;/li&gt;
&lt;li&gt;IDC 입고&lt;/li&gt;
&lt;li&gt;Network 연결&lt;/li&gt;
&lt;li&gt;OS 설치&lt;/li&gt;
&lt;li&gt;SW 설치&lt;/li&gt;
&lt;li&gt;운영 관리&lt;/li&gt;
&lt;li&gt;실시간 감시&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용은 디테일하게 알 필요는 없고, 많은 시간과 리소스가 들어간다. 그리고 상태 관리를 하기 위해서는 하나하나 명령어를 쳐서 수행을 해야 하고, Human Error가 언제든 발생 가능하는 문제점이 있다. 이러한 자체 서버 운영에 대한 어려움 때문에 서버를 안정적이고 용이하게 변경하기 위해 아래와 같이 발전을 하게 된다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%; text-align: center;&quot;&gt;&lt;span&gt;&lt;br /&gt;자체서버운영 -&amp;gt; 설정관리도구 -&amp;gt; 가상머신 -&amp;gt; 클라우드 -&amp;gt; PaaS -&amp;gt; &lt;b&gt;Docker&lt;/b&gt; -&amp;gt; Kubernetes&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체서버운영부터 발전하는 과정을 간단하게 이해해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;0.1.1. 설정 관리 도구&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CHEF, PUPPET 등으로 서버의 상태를 기술한 코드 파일로 서버의 상태를 관리하는 방식이다. 서버의 상태를 모아서 파일로 한 번에 기술하기 때문에 빠르고, 비용 및 리스크가 절감될 뿐만 아니라 서버의 상태를 재현하는 것도 용이해졌다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;IaC(Infrastructure as Code)&lt;/b&gt;&lt;/span&gt;로 코드가 인프라 관리가 가능하다고도 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;0.1.2. 가상 머신&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상 머신은 설정 관리와 같이 나왔다. 쉽게 말하면 물리적 머신을 가상화시켜 가상의 머신을 구성하는 방식이다. 물리적 머신 위에 OS를 설치, OS 위에&amp;nbsp; Hyper-v 설치, 그 위에 여러 다른 OS 설치, 각각의 설치된 OS마다 다양한 실행환경 돌리기 가능하게 구성된다. 따라서 하나의 물리 머신을 여러 대의 가상 머신으로 나누어 다양한 버전의 SW 설치가 가능해진다. 그리고 이때, 가상 머신을 이미지 형태로 저장이 가능하기 때문에 &lt;b&gt;이미지 기반으로 동일한 상태를 가진 서버 구축이 가능&lt;/b&gt;해졌다고 할 수 있다. 이를 변하지 않는 인프라로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Immutable Infrastructure&lt;/b&gt;&lt;/span&gt;라고도 한다. 일회용 종이컵처럼 서버의 상태가 변경 시, 서버를 변경하지 않고 삭제하고 새롭게 서버를 만드는 개념이다. 이러한 개념은 가상 머신에서 처음 등장했고, 앞으로 나올 도커에서 꽃을 피우게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;0.1.3. 클라우드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드가 나오기 전까지는 자체 서버 운영에 적혀있는 용량 산정, 서버 구매부터 실시간 감시에 이르는 모든 과정을 진행했어야 했다. 이러한 비용과 시간이 많이 드는 서버 설치를 안 하기 위해 나온 것이 클라우드라고 생각해도 좋다. 즉, &lt;b&gt;서버 설치와 같은 IT 자원에 대한 관리는 AWS가 전담하고 기업들은 핵심 비즈니스에 집중&lt;/b&gt;할 수 있게 하는 컨셉이 클라우드이다. 기본적으로 amazon, microsoft, google(알파벳 A), naver 등과 같은 기업들이 지원을 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드의 가장 큰 장점은 초기 비용(매몰비용)이 없고 서비스 이용하는 필요한 만큼만 비용이 들어간다. 가상 환경이기 때문에 이미지 기반으로 서버 관리가 가능하다. 이미지만 있으면 동일한 서버를 쉽게 만들기가 가능하다. 추가적으로 유기적 확장이 가능하다. 만약 오늘 선착순 티켓팅이 있어서 트래픽이 몰린다면, 마우스 몇 번만 클릭을 하면 트래픽이 몰리는 시간대에만 서버 확장이 가능하다. 하지만 인프라의 가변성이 존재는 하기 때문에 서버 구축은 쉽게 가능했지만 application은 항상 같은 환경에서 실행은 힘들다. 이러한 문제를 해결하기 위해 나온 개념이 컨테이너다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;0.1.4. 컨테이너&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너는 외부 환경으로부터 격리된 공간에서 프로세스가 동작하는 기술이다. 이는 &lt;b&gt;application을 표준화 되게 패키징하여 동일한 방법으로 배포가 가능하게 한다.&lt;/b&gt; 컨테이너 자체를 가상화 기술 중 하나로 생각할 수도 있다. 하지만 컨테이너는 &lt;span style=&quot;color: #555555;&quot;&gt;호스트에서 다수의 게스트 OS를 구동할 수 있도록 하드웨어를 가상화하는 소프트웨어인 hypervisor를 포함하지 않기 때문에 다양한 장점이 존재한다. 간단히 호스트인 infrastructure(=AWS EC2)를 설치하고 위에 도커만 설치 하면 된다. 그러면 도커 위에 다른 OS 설치 없이 process 만으로 실행이 가능하다. &lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;자세한 내용은 &lt;a href=&quot;https://medium.com/@darkrasid/docker%EC%99%80-vm-d95d60e56fdd&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 눌러 참고하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 도커란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dotCloud라는 회사가 2013년도에 오픈소스를 공개하면서, Docker.Inc로 사명을 변경하면서 도커라는 회사가 생겼다. 도커는 컨테이너 기반의 기술이다. 쉽게 말하면 컨테이너에 기술을 넣으면, 모든 기술들의 실행 방법이 다 같아진다. 이러한 방법을 리눅스에서도 cgroups. navespaces, libcontainer 같은 모듈로 컨테이너 기술을 사용 가능하지만, 너무 어렵다. 그래서 도커를 추천하고 많이 사용한다.&amp;nbsp;따라서 세팅, 운영과 배포 시 사실상 Docker가 필수인 시대가 되어가고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker만 설치되어 있다면, OS와 CPU에 상관없이 컨테이너를 사용 가능하다. 즉, Spring, Nodejs, Django, Nextjs 등등에 관련 없이 컨테이너에만 넣으면 모두 동일하게 실행이 가능하다. 이때 주의해야 할 점은 컨테이너를 삭제하면 내부의 데이터가 모두 삭제되기 때문에 주요한 데이터는 외부 스토리지에 저장이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 욕심을 내보자. 도커와 함께 자주 나오는 용어인 오케스트레이션, Docker Swarm, Kubernetes에 대해 조금 더 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1. 컨테이너 오케스트레이션(Container Orchestration)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 오케스트레이션의 대표적인 예는 kubernetes로 여러 대의 서버와 여러 개의 서비스를 관리하여 자동화하는 기술이다. 소분류로는 스케줄링, 서비스 디스커버리, 모니터링, 로깅 등등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스케줄링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이름과 비슷하게 컨테이너를 스케줄링해서 가상 머신에 배치하는 역할을 한다. 배치 시 컨테이너 스택에 매칭 되는 가상 머신을 자동으로 선택하고, 문제 발생 시 자동으로 다시 배치를 한다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서비스 디스커버리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 수많은 컨테이너들이 생성과 종료를 반복한다. 이때 매번 IP, Port 정보를 알 수가 없기 때문에 이를 자동으로 매칭 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2. Docker Swarm&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Inc에서 만든 컨테이너 오케스트레이션 tool로 기능이 단순하여 쉽게 사용 가능하다. 하지만, 대용량 분산 환경과 같은 복잡한 시스템에서는 적합하지 않아서 Kubernetes(쿠버네티스)를 많이 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.3. Kubernetes&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Inc이 개발한 컨테이너 오케스트레이션 tool로 대규모 분산 환경에 최적화된 기능을 제공한다. 넓은 생태계가 구축이 되어 있기 때문에, 사실상 컨테이너 오케스트레이션에서는 표준이라고 해도 된다. 너무 많이 사용되기 때문에, 관련된 내용은 추가로 정리하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 도커 기초 개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 설명한 것과 같이 기본적으로 도커는 &lt;b&gt;Immutable Infrastructure 패러다임&lt;/b&gt;을 실현하는 기술이다. 따라서 서버 구축 후에는 변경이 불가능하다. 변경이 필요하다면 삭제 후 다시 구축을 한다. 이러한 컨셉은 모든 서버가 항상 동일한 상태임을 보장해 줄 수 있고, 환경 세팅에 어려움을 없애준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커의 큰 cycle을 보면 git과 어느 정도 비슷하다고 할 수 있다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Dockerfile&lt;/b&gt;&lt;/span&gt;을 빌드를 해주면, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;DockerImage&lt;/b&gt;&lt;/span&gt;가 생긴다. 이 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;DockerImage&lt;/b&gt;&lt;/span&gt;를 push 하면 Repository로 들어간다. 그리고 필요한 서버에서 pull 받아서 실행하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 개념을 Image, Container, Dockerfile, Registry 용어를 통해 조금 더 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Image&lt;/b&gt; - image는 프로그램이라고 생각하면 된다. image layer들의 집합으로 image를 만들기도 가능하다. 읽기만 가능(read-only)하고 immutable 하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Container&lt;/b&gt; - container는 프로세스라고 생각하며 된다. 쓰기가 가능하며, 휘발성으로 메모리에 상주한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dockerfile&lt;/b&gt; - instruction set으로 도커 이미지를 만드는 설명서라고 생각하면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Registry&lt;/b&gt; - github과 같은 이미지 저장소이고 대표적인 public registry으로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;DockerHub&lt;/b&gt;&lt;/span&gt;가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 도커 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 도커는 GUI와 CLI 설치가 있다. 둘 다 설치해보자. window는 GUI로 하고, AWS EC2 우분투는 CLI 설치를 진행해 보자. 현재 GUI를 상업적으로 이용하면 유료긴 하지만, 개인 프로젝트 사용 시 별 관계없으므로 아래의 url에서 GUI를 설치하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. 도커 GUI window 설치(Docker Desktop)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/get-docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.docker.com/get-docker/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1655464405841&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Get Docker&quot; data-og-description=&quot; &quot; data-og-host=&quot;docs.docker.com&quot; data-og-source-url=&quot;https://docs.docker.com/get-docker/&quot; data-og-url=&quot;https://docs.docker.com/get-docker/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b73LYX/hyOMNY8pKz/sZanof2nrtVcDVrmo9lGUK/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/02W5w/hyONRr66Gk/1nwbvDJcgZpB6GzOBu7sfk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://docs.docker.com/get-docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.docker.com/get-docker/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b73LYX/hyOMNY8pKz/sZanof2nrtVcDVrmo9lGUK/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/02W5w/hyONRr66Gk/1nwbvDJcgZpB6GzOBu7sfk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Get Docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AaaPO/btrE4JrwqsS/jye7lSEN47dogOV6nIAJa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AaaPO/btrE4JrwqsS/jye7lSEN47dogOV6nIAJa0/img.png&quot; data-alt=&quot;\&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AaaPO/btrE4JrwqsS/jye7lSEN47dogOV6nIAJa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAaaPO%2FbtrE4JrwqsS%2Fjye7lSEN47dogOV6nIAJa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1900&quot; height=&quot;667&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;\&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 운영체제에 맞게 설치를 하면 된다. 만약 2020년 이전에 산 노트북이라면 가상화 설정을 해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JPfiP/btrE3q01AP3/6GhKNe8saL6efHks8HFSo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JPfiP/btrE3q01AP3/6GhKNe8saL6efHks8HFSo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JPfiP/btrE3q01AP3/6GhKNe8saL6efHks8HFSo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJPfiP%2FbtrE3q01AP3%2F6GhKNe8saL6efHks8HFSo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;577&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 가상화 부분이 사용인지 우선적으로 확인을 하자. 그러나 설치하고 실행해보면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uQVIX/btrE3GI1BWF/JwT7UFG3fNs3gENmiQR9ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uQVIX/btrE3GI1BWF/JwT7UFG3fNs3gENmiQR9ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uQVIX/btrE3GI1BWF/JwT7UFG3fNs3gENmiQR9ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuQVIX%2FbtrE3GI1BWF%2FJwT7UFG3fNs3gENmiQR9ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;494&quot; height=&quot;283&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Desktop stopping이라고 뜨면서 안 돌아간다. 그 이유는 WSL을 설치하지 않아서 그렇다. 가상 환경을 위한 WSL 설치를 아래를 참고해서 진행하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/471&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[WSL 2 기초 개념] WSL은 무엇인가&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1655464889036&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[WSL 2 기초 개념] WSL은 무엇인가&quot; data-og-description=&quot;WSL은 Windows Subsystem for Lunux의 줄인말이다. 쉽게 말하면, Window에서 Linux처럼 사용하겠다는 이야기이다. 보통은 Virtual Machine으로 셋팅을 해야 Linux를 사용할 수 있었다. 그러나 우리는 WSL을 통해..&quot; data-og-host=&quot;han-py.tistory.com&quot; data-og-source-url=&quot;https://han-py.tistory.com/471&quot; data-og-url=&quot;https://han-py.tistory.com/471&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ePrHD/hyONSR5IGE/1CzAHXzxf1vN4ymgxGh7kK/img.png?width=800&amp;amp;height=379&amp;amp;face=0_0_800_379,https://scrap.kakaocdn.net/dn/bjWyIw/hyONPuiID3/2kwBpgKcuXKnrsaFkbMzaK/img.png?width=800&amp;amp;height=379&amp;amp;face=0_0_800_379,https://scrap.kakaocdn.net/dn/FubCX/hyONPnwVq2/XkKeeaxRKPjmETFp1dA2k1/img.png?width=927&amp;amp;height=440&amp;amp;face=0_0_927_440&quot;&gt;&lt;a href=&quot;https://han-py.tistory.com/471&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://han-py.tistory.com/471&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ePrHD/hyONSR5IGE/1CzAHXzxf1vN4ymgxGh7kK/img.png?width=800&amp;amp;height=379&amp;amp;face=0_0_800_379,https://scrap.kakaocdn.net/dn/bjWyIw/hyONPuiID3/2kwBpgKcuXKnrsaFkbMzaK/img.png?width=800&amp;amp;height=379&amp;amp;face=0_0_800_379,https://scrap.kakaocdn.net/dn/FubCX/hyONPnwVq2/XkKeeaxRKPjmETFp1dA2k1/img.png?width=927&amp;amp;height=440&amp;amp;face=0_0_927_440');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[WSL 2 기초 개념] WSL은 무엇인가&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;WSL은 Windows Subsystem for Lunux의 줄인말이다. 쉽게 말하면, Window에서 Linux처럼 사용하겠다는 이야기이다. 보통은 Virtual Machine으로 셋팅을 해야 Linux를 사용할 수 있었다. 그러나 우리는 WSL을 통해..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;han-py.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 url을 참고해서 version 2로 세팅을 하고 Ubuntu 20.04 LTS를 추가로 설치해서 실행하면 Ubuntu가 실행된다. 컴퓨터를 재부팅 후에 docker를 실행해보면 설치가 완료될 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655465020960&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run -d -p 80:80 docker/getting-started&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 터미널에서 실행을 하면 도커가 실행될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. 도커 CLI Ubuntu 설치(AWS EC2)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 url을 들어가자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/engine/install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.docker.com/engine/install/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1655465174137&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Install Docker Engine&quot; data-og-description=&quot; &quot; data-og-host=&quot;docs.docker.com&quot; data-og-source-url=&quot;https://docs.docker.com/engine/install/&quot; data-og-url=&quot;https://docs.docker.com/engine/install/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xOz85/hyONOB82nm/6zfh6RJpiyQgT6hWDmKlF0/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/VdWOi/hyONRlmQtG/bS5JFGmpALxWAuei1L9pU0/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://docs.docker.com/engine/install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.docker.com/engine/install/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xOz85/hyONOB82nm/6zfh6RJpiyQgT6hWDmKlF0/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/VdWOi/hyONRlmQtG/bS5JFGmpALxWAuei1L9pU0/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Install Docker Engine&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1895&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4NmOC/btrE4U7rL1i/dlk0XgIRk9E1dk9XOgEl50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4NmOC/btrE4U7rL1i/dlk0XgIRk9E1dk9XOgEl50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4NmOC/btrE4U7rL1i/dlk0XgIRk9E1dk9XOgEl50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4NmOC%2FbtrE4U7rL1i%2Fdlk0XgIRk9E1dk9XOgEl50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1895&quot; height=&quot;848&quot; data-origin-width=&quot;1895&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Ubuntu를 클릭해준다. 다른 환경이라는 본인의 환경에 맞게 클릭을 해주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/engine/install/ubuntu/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.docker.com/engine/install/ubuntu/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1655465265452&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Install Docker Engine on Ubuntu&quot; data-og-description=&quot; &quot; data-og-host=&quot;docs.docker.com&quot; data-og-source-url=&quot;https://docs.docker.com/engine/install/ubuntu/&quot; data-og-url=&quot;https://docs.docker.com/engine/install/ubuntu/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/q5aMl/hyOMEt946C/himPo3IYAQOVKMpmrwhOpK/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/bC4ISm/hyONKM6tYW/gwxbujFEVKFBFvgBqtyKrk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://docs.docker.com/engine/install/ubuntu/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.docker.com/engine/install/ubuntu/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/q5aMl/hyOMEt946C/himPo3IYAQOVKMpmrwhOpK/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/bC4ISm/hyONKM6tYW/gwxbujFEVKFBFvgBqtyKrk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Install Docker Engine on Ubuntu&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 홈페이지에 맞게 쭉 쳐주면 된다. 간단히 정리하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655465493858&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이전 버전 삭제
$ sudo apt-get remove docker docker-engine docker.io containerd runc
$ sudo apt-get update

// 저장소 셋업
$ sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

// official GPG key 세팅
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

// 제장소 세팅
$ echo \
  &quot;deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable&quot; | sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null
  
  // 도커 엔진 설치
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서도 GUI를 설치했지만, 같은 명령어로 가능이 하기 때문에 통합으로 ubuntu로 조금 더 실습을 진행하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3. 도커 Mac 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/desktop/install/mac-install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.docker.com/desktop/install/mac-install/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669125091050&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Install on Mac&quot; data-og-description=&quot; &quot; data-og-host=&quot;docs.docker.com&quot; data-og-source-url=&quot;https://docs.docker.com/desktop/install/mac-install/&quot; data-og-url=&quot;https://docs.docker.com/desktop/install/mac-install/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/YxZey/hyQE4wZx9N/iC44WK5V9yLeKVE3FSphU1/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/yU4Fz/hyQE5WWJiX/ENIQJVAR3vLncWxDTEPsQ1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://docs.docker.com/desktop/install/mac-install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.docker.com/desktop/install/mac-install/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/YxZey/hyQE4wZx9N/iC44WK5V9yLeKVE3FSphU1/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/yU4Fz/hyQE5WWJiX/ENIQJVAR3vLncWxDTEPsQ1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Install on Mac&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서에 들어가서 인스톨을 눌러준다. 나는 M1이니까 아래 그림에서 Mac with Apple sillicon을 눌러준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;987&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HEk3a/btrRQiPYnbd/sxuGckk121mSCXGbTxNk2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HEk3a/btrRQiPYnbd/sxuGckk121mSCXGbTxNk2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HEk3a/btrRQiPYnbd/sxuGckk121mSCXGbTxNk2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHEk3a%2FbtrRQiPYnbd%2FsxuGckk121mSCXGbTxNk2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1404&quot; height=&quot;987&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;987&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치하고 아래 그림처럼 application에 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;337&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EQ4xb/btrRT4vHjd3/9ebLCedZDMeSnXy39zJp10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EQ4xb/btrRT4vHjd3/9ebLCedZDMeSnXy39zJp10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EQ4xb/btrRT4vHjd3/9ebLCedZDMeSnXy39zJp10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEQ4xb%2FbtrRT4vHjd3%2F9ebLCedZDMeSnXy39zJp10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;337&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;337&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 완료 후에 도커를 한번 실행 시킨다. 그 후에 terminal에서 아래와 같은 명령어를 치고 설치가 완료 됐는지를 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669127057885&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 도커 실습&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 도커 명령어를 칠 때마다, sudo를 앞에 넣어줘야 한다.&amp;nbsp; 먼저 sudo를 안 넣어도 되게 세팅을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655466181253&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo usermod -aG docker $USER&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세팅이 끝났다. 도커를 실행해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655466224798&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼가 돌아가면, 설치는 완료된 것이다. 먼저 version 확인을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655466346631&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFjDPS/btrE3XYdBs2/kQP7QSaq5KJ99mFLTKLXik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFjDPS/btrE3XYdBs2/kQP7QSaq5KJ99mFLTKLXik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFjDPS/btrE3XYdBs2/kQP7QSaq5KJ99mFLTKLXik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFjDPS%2FbtrE3XYdBs2%2FkQP7QSaq5KJ99mFLTKLXik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;560&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 확인을 해보면, 위와 같이 Docker Engine이 Client와 Server, 즉 2개로 존재하고 우리는 2개를 다 사용할 수 있다. 좀 더 쉽게 이야기하면, 위의 명령어로 실행을 하면 Client Docker Engine이 실행되는 것이다. 그 후에 client는 server로 requret(요청)을 하고, 다시 server가 client로 response를 돌려 준면, cmd창에 표시된다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1. 도커 컨테이너 실행 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 도커 컨테이너를 실행하는 방법을 확인하고 이번 글을 마쳐볼까 한다. 추가적인 Nginx, redis, spring 등등 과 같은 image 실행은 다른 글에서 추가하겠다. 컨테이너를 실행하는 명령어는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655466715202&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker container run [옵션] [이미지:태그]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지:태그 부분은 예를 들면 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;centos:7과&lt;/b&gt;&lt;/span&gt; 같이 사용할 이미지(centos)와 버전(7)을 명시해 주면 된다. 기본적으로 실행하려는 이미지가 없다면 pull을 local에 다운로드하여서 실행을 한다. 그 후 재실행시에는 설치되어 있기 때문에 pull 없이 바로 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2. 도커 ubuntu 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 예시를 보면 단순히 ubuntu 들어가서 명령어를 치는 것과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655467147481&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker container run ubuntu:latest /bin/echo 'Hello'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어를 정리하면, 최신 우분투를 받은 다음에 명령어를 실행하라는 말이다. 만약 ubuntu이미지를 이전에 받은 것이 있다면, 바로 실행이 될 것이고, 없다면 다운 받은 후에 실행이 될 것이다. &lt;b&gt;/bin/echo 'Hello'&lt;/b&gt; 명령어는 ubuntu 안에서 실행할 명령어이다. bin 안에 있는 echo를 실행한다. ubuntu:latest의 latest는 최신 버전이라 이해하면 된다. 다른 명령어도 아래와 같이 쳐보면서 실습을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yNSgr/btrE4mpVmtn/OKcVUDLSLMmlkfre5ifaVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yNSgr/btrE4mpVmtn/OKcVUDLSLMmlkfre5ifaVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yNSgr/btrE4mpVmtn/OKcVUDLSLMmlkfre5ifaVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyNSgr%2FbtrE4mpVmtn%2FOKcVUDLSLMmlkfre5ifaVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;132&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.3. docker/whalesay 이미지 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 간단한 이미지를 사용하는 실습을 간단히 해보자. image검색은 아래 사진의 사이트인 dockerhub에서 검색이 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kLtZB/btrE3L4OpJP/cF47VKYkHzXsN0TuyQGhF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kLtZB/btrE3L4OpJP/cF47VKYkHzXsN0TuyQGhF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kLtZB/btrE3L4OpJP/cF47VKYkHzXsN0TuyQGhF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkLtZB%2FbtrE3L4OpJP%2FcF47VKYkHzXsN0TuyQGhF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;821&quot; height=&quot;352&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 이미지를 사용해보자. 아래와 같이 명령어를 쳐보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655467485056&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run docker/whalesay cowsay boo
     _____ 
    &amp;lt; boo &amp;gt;
     ----- 
            \
             \
                \     
                                            ##        .            
                                ## ## ##       ==            
                         ## ## ## ##      ===            
                 /&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;___/ ===        
        ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~   
                 \______ o          __/            
                    \    \        __/             
                        \____\______/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 고래가 뜬다면 성공이다! cowsay 뒤에 인자 이름을 적어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEs4uQ/btrE5SgPple/mpETDKVqwHmXGLvV2UkJ2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEs4uQ/btrE5SgPple/mpETDKVqwHmXGLvV2UkJ2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEs4uQ/btrE5SgPple/mpETDKVqwHmXGLvV2UkJ2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEs4uQ%2FbtrE5SgPple%2FmpETDKVqwHmXGLvV2UkJ2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;871&quot; height=&quot;542&quot; data-origin-width=&quot;871&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로딩되는 cmd창을 좀 더 보면 위와 같다. 1번은 local에 image가 있는지를 찾는 것이다. 없기 때문에 2번에서 pull로 다운로드하여 설치를 하는 것이고, 3번에서 실행 결괏값이 나온다고 할 수 있다. 즉, &lt;b&gt;설치한 적이 없어도 알아서 나의 서버로 다운로드해서 실행시켜준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 도커 이미지 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 도커 Image를 받는 실습을 해보았다. 이제 이미지를 만들어서 도커에 올려서 공유하는 실습을 해보자. 명령어부터 보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669127640116&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker build --tag &amp;lt;이미지이름:버전&amp;gt; &amp;lt;dockerfile위치&amp;gt;

or

$ docker build -t &amp;lt;이미지이름:버전&amp;gt; &amp;lt;dockerfile위치&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금더 디테일한 예시를 적어보면, 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669127939075&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 예1
$ docker build -t hanpy:2.3.1 ./

// 예2
$ docker build -t hanpy:dev ./cicd/dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 적어주면, 이미지가 생성 된다. 생성된 이미지를 확인하려면 아래와 같은 명령어를 적어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669128029793&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker images&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 조금 더 봐야할 부분이 dockerfile이다. dockerfile이 있어야 이미지가 말린다. 우선 간단한 Dockerfile 예시부터 보자. 아래는 리액트를 도커에 이미지로 만드는 로직이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669128388813&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM node:16-slim

RUN apt-get update
RUN apt-get install -y build-essential
RUN apt-get install -y curl

WORKDIR /app
COPY ./package.json /app/package.json
WORKDIR /app
RUN npm install -g npm@8.19.2
RUN npm install

COPY . /app
RUN npm run build

EXPOSE 3010

CMD [ &quot;npm&quot;, &quot;run&quot;, &quot;start&quot; ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해석을 하면, node환경에서(FROM node:16-slim) 배포를 위해 명령어를 치는 것을 기술한 것이다. 관련 세부적인 것은 구글링하면 나온다. 여기서는 큰 틀만 이해를 하자. 쉽게 말하면, 배포를 하기위해 치는 명령어를 기술해 두고, image 빌드 시 대신 실행해 주는 것이라 생각을 하면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 이미지 올리기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 하여 이미지를 만들었다. 그리고 이미지를 docker images로 확인을 했다. 그러면 이제 images를 위에서말한 dockerhub에 올려보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hub.docker.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub.docker.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1669129211836&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Docker Hub Container Image Library | App Containerization&quot; data-og-description=&quot;Deliver your business through Docker Hub Package and publish apps and plugins as containers in Docker Hub for easy download and deployment by millions of Docker users worldwide.&quot; data-og-host=&quot;hub.docker.com&quot; data-og-source-url=&quot;https://hub.docker.com/&quot; data-og-url=&quot;https://hub.docker.com/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nCRvj/hyQE3x9yyu/gwS7ka8z0Z06Zk8nYrA3I0/img.png?width=416&amp;amp;height=250&amp;amp;face=0_0_416_250&quot;&gt;&lt;a href=&quot;https://hub.docker.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hub.docker.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nCRvj/hyQE3x9yyu/gwS7ka8z0Z06Zk8nYrA3I0/img.png?width=416&amp;amp;height=250&amp;amp;face=0_0_416_250');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Docker Hub Container Image Library | App Containerization&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Deliver your business through Docker Hub Package and publish apps and plugins as containers in Docker Hub for easy download and deployment by millions of Docker users worldwide.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hub.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 확인 결과 hanpy:dev라는 이미지를 생성했고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669129290210&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker login&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 친후에 dockerhub의 가입한 아이디로 로그인을 하자. 인터넷은 연결되어 있어야한다. 그리고 push를 해주면 올라간다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669129384735&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker push &amp;lt;username&amp;gt;/&amp;lt;저장소이름&amp;gt;:&amp;lt;태크&amp;gt;

//ex
$ docker push twopy/hanpy:dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어를 풀이하면, twopy라는 아이디에 hanpy:dev 이미지를 올린것이다. 이제 dockerhub 홈페이지에 가면, 내가 올린 이미지를 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 docker 기초 전체를 간단히 확인했다. 4시간정도의 강의 분량으로 보인다. 적지 않은 양이지만, 아주 기초적인 부분이고 추후 Redis 나 Jenkins, 쿠버와 연관된 배포까지 심화과정으로 올려보겠다.&lt;/p&gt;</description>
      <category>Web/프로젝트구현</category>
      <category>docker CLI 설치</category>
      <category>Docker Desktop stopping</category>
      <category>docker GUI 설치</category>
      <category>Docker mac 설치</category>
      <category>docker 개념 정리</category>
      <category>docker 기초 개념</category>
      <category>docker 시작하기</category>
      <category>image 빌드하기</category>
      <category>image 올리기</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/494</guid>
      <comments>https://han-py.tistory.com/494#entry494comment</comments>
      <pubDate>Wed, 23 Nov 2022 06:00:26 +0900</pubDate>
    </item>
    <item>
      <title>[react web] 데이터 다른 컴포넌트로 보내기</title>
      <link>https://han-py.tistory.com/409</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;0. 들어가면서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react는 많은 jsx파일이나 js 파일로 컴포넌트가 만들어진다. 그렇다면, 각각의 컴포넌트에 데이터를 전달할 수 있어야한다. 쉽고 간단하게 설명해 보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 부모 컴포넌트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 보낼 컴포넌트다. 이 컴포넌트 안에는 3개의 다른 컴포넌트( DownTest1, DownTest2, DownTest3 )를 만들었다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1636527982944&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/component/UpTest.jsx
import React from 'react';
import DownTest1 from './DownTest1';
import DownTest2 from './DownTest2';
import DownTest3 from './DownTest3';

const UpTest = () =&amp;gt; {
    return (
        &amp;lt;&amp;gt;
            &amp;lt;h3&amp;gt;Parent Component&amp;lt;/h3&amp;gt;
            &amp;lt;DownTest1 a=&quot;a들어감&quot; b=&quot;bb들어감&quot; c=&quot;ccc들어감&quot; title=&quot;title들어감&quot; /&amp;gt;

            &amp;lt;hr /&amp;gt;

            &amp;lt;DownTest2&amp;gt;
                &amp;lt;p&amp;gt;a 이렇게도 들어감&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;b 이렇게도 들어감&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;c 이렇게도 들어감&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;d 이렇게도 들어감&amp;lt;/p&amp;gt;
            &amp;lt;/DownTest2&amp;gt; 

            &amp;lt;hr /&amp;gt;
            
            &amp;lt;DownTest3 buttontag={&amp;lt;button&amp;gt;버튼도 내려간다&amp;lt;/button&amp;gt;} ptag={&amp;lt;p&amp;gt;p태크도 내려간다.&amp;lt;/p&amp;gt;} /&amp;gt;
        &amp;lt;/&amp;gt;
    )
}

export default UpTest;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 좀 더 이해하기 쉽게 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rflaN/btrkoHuSatq/lqbSOhmvKRgODXRtLt2Nn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rflaN/btrkoHuSatq/lqbSOhmvKRgODXRtLt2Nn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rflaN/btrkoHuSatq/lqbSOhmvKRgODXRtLt2Nn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrflaN%2FbtrkoHuSatq%2FlqbSOhmvKRgODXRtLt2Nn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;865&quot; height=&quot;554&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 위와 같이 3가지 방법으로 데이터를 자식 컴포넌트로 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 자식 컴포넌트&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;첫번째 방법&lt;/h4&gt;
&lt;pre id=&quot;code_1636528179397&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; &amp;lt;DownTest1 a=&quot;a들어감&quot; b=&quot;bb들어감&quot; c=&quot;ccc들어감&quot; title=&quot;title들어감&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;직관적으로 간단하다. &lt;b&gt;=&lt;/b&gt;을 기준으로 왼쪽이 DownTest1 컴포넌트에서 사용될 이름이고, 오른쪽이 넣어준 데이터이다. DownTest1의 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UFGZW/btrko7GSP5i/st92FWv7YuFvBnqkedYjd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UFGZW/btrko7GSP5i/st92FWv7YuFvBnqkedYjd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UFGZW/btrko7GSP5i/st92FWv7YuFvBnqkedYjd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUFGZW%2Fbtrko7GSP5i%2Fst92FWv7YuFvBnqkedYjd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;391&quot; height=&quot;313&quot; data-origin-width=&quot;391&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;props뒤에 .a나 .title과 같이, 부모 컴포넌트에서 적은 = 왼쪽부분을 넣어주면 된다. 결과 페이지는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT5hdF/btrkuI6IbRh/N4z1icVVWwwoXc1buwKy8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT5hdF/btrkuI6IbRh/N4z1icVVWwwoXc1buwKy8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT5hdF/btrkuI6IbRh/N4z1icVVWwwoXc1buwKy8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT5hdF%2FbtrkuI6IbRh%2FN4z1icVVWwwoXc1buwKy8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;240&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;두번째 방법&lt;/h4&gt;
&lt;pre id=&quot;code_1636528461510&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;            &amp;lt;DownTest2&amp;gt;
                &amp;lt;p&amp;gt;a 이렇게도 들어감&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;b 이렇게도 들어감&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;c 이렇게도 들어감&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;d 이렇게도 들어감&amp;lt;/p&amp;gt;
            &amp;lt;/DownTest2&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방법은 통으로 넣는 방식이다. &amp;lt;DownTest2&amp;gt;&amp;lt;/DownTest2&amp;gt; 사이에 통으로 데이터를 넣어주는 거다. DownTest2의 코드를 보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;349&quot; data-origin-height=&quot;261&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/butAJz/btrkuFvmVuF/wGgI4h409JVa2tjQZwtm5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/butAJz/btrkuFvmVuF/wGgI4h409JVa2tjQZwtm5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/butAJz/btrkuFvmVuF/wGgI4h409JVa2tjQZwtm5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbutAJz%2FbtrkuFvmVuF%2FwGgI4h409JVa2tjQZwtm5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;349&quot; height=&quot;261&quot; data-origin-width=&quot;349&quot; data-origin-height=&quot;261&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;.children&lt;/span&gt;&lt;/b&gt;을 사용해서 부모데이터를 받는다. 결과는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;353&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R0kEN/btrkuJq1xhA/NAKJAkiRlFhmbMcuFgQBFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R0kEN/btrkuJq1xhA/NAKJAkiRlFhmbMcuFgQBFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R0kEN/btrkuJq1xhA/NAKJAkiRlFhmbMcuFgQBFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR0kEN%2FbtrkuJq1xhA%2FNAKJAkiRlFhmbMcuFgQBFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;353&quot; height=&quot;251&quot; data-origin-width=&quot;353&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;세번째 방법&lt;/h4&gt;
&lt;pre id=&quot;code_1636528788341&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;DownTest3 buttontag={&amp;lt;button&amp;gt;버튼도 내려간다&amp;lt;/button&amp;gt;} ptag={&amp;lt;p&amp;gt;p태크도 내려간다.&amp;lt;/p&amp;gt;} /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방법은 = 왼쪽에는 DownTest3 컴포넌트에서 사용할 변수를 넣어준다. 오른쪽에는 tag를 직접 넣어준다. DownTest3 코드는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caTunX/btrkqHHJQut/MpYL9zcleIreEeqVZFiyZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caTunX/btrkqHHJQut/MpYL9zcleIreEeqVZFiyZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caTunX/btrkqHHJQut/MpYL9zcleIreEeqVZFiyZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaTunX%2FbtrkqHHJQut%2FMpYL9zcleIreEeqVZFiyZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;369&quot; height=&quot;290&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;받는 방법은 첫번째 방법과 비슷하다. 결과 값은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;335&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD6Gyn/btrkupfhCSD/2FukkOHq2kPt1vCALIo6DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD6Gyn/btrkupfhCSD/2FukkOHq2kPt1vCALIo6DK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD6Gyn/btrkupfhCSD/2FukkOHq2kPt1vCALIo6DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD6Gyn%2FbtrkupfhCSD%2F2FukkOHq2kPt1vCALIo6DK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;335&quot; height=&quot;166&quot; data-origin-width=&quot;335&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;사실 첫번째 방식으로 데이터를 많이 보낸다. 그러나 컴포넌트의 재사용성을 위해서는 다른 방식이 좀 더 효율적인 경우가 있다. 따라서 첫번째 방식을 주로 쓰고, 다른 방식들은 있다는 것만 인지하고 있으면 좋을 것 같다.&lt;/p&gt;</description>
      <category>Web/React</category>
      <author>HAN_PY</author>
      <guid isPermaLink="true">https://han-py.tistory.com/409</guid>
      <comments>https://han-py.tistory.com/409#entry409comment</comments>
      <pubDate>Sat, 19 Nov 2022 13:57:47 +0900</pubDate>
    </item>
  </channel>
</rss>