<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>paulkang.log</title>
        <link>https://velog.io/</link>
        <description>뭐든 기록하면 자산!</description>
        <lastBuildDate>Sun, 30 Mar 2025 04:12:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>paulkang.log</title>
            <url>https://velog.velcdn.com/images/paul_kang/profile/3e83f996-4a43-4f58-bf92-ba80c4c19de7/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. paulkang.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/paul_kang" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[항해플러스 프론트엔드 5기] 1주차 WIL]]></title>
            <link>https://velog.io/@paul_kang/%ED%95%AD%ED%95%B4%ED%94%8C%EB%9F%AC%EC%8A%A4-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-5%EA%B8%B0-1%EC%A3%BC%EC%B0%A8-WIL</link>
            <guid>https://velog.io/@paul_kang/%ED%95%AD%ED%95%B4%ED%94%8C%EB%9F%AC%EC%8A%A4-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-5%EA%B8%B0-1%EC%A3%BC%EC%B0%A8-WIL</guid>
            <pubDate>Sun, 30 Mar 2025 04:12:00 GMT</pubDate>
            <description><![CDATA[<h2 id="weekly-i-learned">Weekly I Learned</h2>
<h3 id="1-문제-과제-프로젝트를-진행하면서-부딪혔던-기술적인-문제">1. 문제 (과제, 프로젝트를 진행하면서 부딪혔던 기술적인 문제)</h3>
<blockquote>
<p>SPA 라우팅을 직접 구현하면서 <code>window.history.pushState()</code>와 <code>location.hash</code>를 사용했으나, 코드가 점차 흩어지고 복잡해지면서 유지보수가 어려워졌다.
<code>main.js</code> 내 이벤트 핸들러가 명확한 기준 없이 나열되어 가독성과 확장성에 어려움을 느꼈다.
과제 초반, 테스트 코드에 대한 이해 없이 기능 개발에만 집중하면서 테스트 실패를 뒤늦게 인지하였다.</p>
</blockquote>
<h3 id="2-시도">2. 시도</h3>
<blockquote>
<p>라우팅 로직을 <code>BaseRouter</code> 클래스로 추상화하고, 이를 기반으로 <code>BrowserRouter</code>, <code>HashRouter</code>를 각각 구현하여 라우팅 구조를 명확하게 정리.
<code>guardRoute</code> 개념을 통해 인증 여부에 따른 라우팅 처리를 공통화.</p>
</blockquote>
<h3 id="3-해결">3. 해결</h3>
<blockquote>
<p>BaseRouter 클래스를 통해 라우팅의 관심사를 분리하고, 재사용 가능한 구조로 리팩토링하여 유지보수성과 확장성을 확보하였다.</p>
</blockquote>
<h3 id="4-알게된-것">4. 알게된 것</h3>
<blockquote>
<p><code>History API</code>, <code>hashchange</code>, <code>popstate</code> 이벤트의 차이점과 클라이언트 라우팅의 내부 동작 원리를 실무적으로 체득했다.
정적 호스팅 환경에서의 SPA 라우팅 설정과 배포시 제약사항을 경험할 수 있는 기회가 되었다.
테스트 흐름 자체가 개발 가이드라인 역할을 할 수 있다는 점을 알게 되었다.</p>
</blockquote>
<h3 id="keep--현재-만족하고-계속-유지할-부분">Keep : 현재 만족하고 계속 유지할 부분</h3>
<p>라우터 추상화를 통해 라우팅 로직을 구조적으로 정리한 점은 매우 만족스러웠고, 이 경험은 앞으로의 프로젝트에서도 많은 도움이 될 것 같다.</p>
<p>과제를 끝까지 완주하며 기술적으로도, 심리적으로도 성장할 수 있었던 점이 좋았다.</p>
<h3 id="problem--개선이-필요하다고-생각하는-문제점">Problem : 개선이 필요하다고 생각하는 문제점</h3>
<p>이벤트 핸들러가 <code>main.js</code>에 흩어져 있어 구조적으로 비효율적이고 추후에 확장하기 어려울 것이라 생각한다.</p>
<p>과제 초반 테스트 코드를 충분히 분석하지 않아, 기능과 테스트가 분리되어 개발 효율이 떨어졌었던 것 같다.</p>
<h3 id="try--문제점을-해결하기-위해-시도해야-할-것">Try : 문제점을 해결하기 위해 시도해야 할 것</h3>
<p>이벤트 핸들러를 역할 단위로 분리하고, 이벤트 위임 및 모듈화를 적용해 코드 가독성과 유지보수성을 높일 예정이다.</p>
<p>다음 프로젝트부터는 개발 초기 단계에서 테스트 코드를 먼저 분석하고, 테스트 주도 개발(<code>TDD</code>)에 더 적극적으로 접근해 볼 계획이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[항해플러스 프론트엔드 5기] 시작하는 마음 ]]></title>
            <link>https://velog.io/@paul_kang/%ED%95%AD%ED%95%B4%ED%94%8C%EB%9F%AC%EC%8A%A4-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-5%EA%B8%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EB%A7%88%EC%9D%8C</link>
            <guid>https://velog.io/@paul_kang/%ED%95%AD%ED%95%B4%ED%94%8C%EB%9F%AC%EC%8A%A4-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-5%EA%B8%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EB%A7%88%EC%9D%8C</guid>
            <pubDate>Sat, 22 Mar 2025 07:53:50 GMT</pubDate>
            <description><![CDATA[<h1 id="시작하는-마음">시작하는 마음</h1>
<h2 id="지금까지의-회고">지금까지의 회고</h2>
<p>지금까진 회사에서의 내 몫만 잘하면 된다는 마음이 컸고, 개발자로서의 삶 보단 직장인으로서의 삶이었던 것 같습니다. 반성하고 성장하고자 항해플러스에 참여하게 되었습니다.</p>
<h2 id="항해-플러스-참여-계기">항해 플러스 참여 계기</h2>
<p>개발자로서 더 성장하고자 용기를 내서 참여하게 되었습니다. 하드스킬 뿐만 아니라 소프트스킬도 더 발전시키고픈 마음이 커서 용기를 내어 참여하였습니다. </p>
<h2 id="향후-5년-뒤-커리어-방향성">향후 5년 뒤 커리어 방향성</h2>
<p>IT 창업을 해서 CTO로서 기술팀을 이끌고 싶습니다.</p>
<h2 id="10주간의-목표">10주간의 목표</h2>
<p>매주 과제를 성실하게 최선을 다해 임하겠습니다.</p>
<h2 id="최종-목표-배지">최종 목표 배지</h2>
<p>색깔은 기억안나지만, 상위 30%가 목표입니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers Level 2] 위장]]></title>
            <link>https://velog.io/@paul_kang/Programmers-Level-2-%EC%9C%84%EC%9E%A5</link>
            <guid>https://velog.io/@paul_kang/Programmers-Level-2-%EC%9C%84%EC%9E%A5</guid>
            <pubDate>Sun, 11 Sep 2022 10:59:43 GMT</pubDate>
            <description><![CDATA[<h1 id="programmers-level-2-위장">Programmers Level 2 위장</h1>
<blockquote>
<ul>
<li>해시</li>
<li>level 2</li>
</ul>
</blockquote>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42578">프로그래머스 레벨2 - &lt;위장&gt; 문제 보기</a></p>
<h2 id="🔍-문제-설명">🔍 문제 설명</h2>
<p>스파이들은 매일 다른 옷을 조합하여 입어 자신을 위장합니다.</p>
<p>예를 들어 스파이가 가진 옷이 아래와 같고 오늘 스파이가 동그란 안경, 긴 코트, 파란색 티셔츠를 입었다면 다음날은 청바지를 추가로 입거나 동그란 안경 대신 검정 선글라스를 착용하거나 해야 합니다.</p>
<table>
<thead>
<tr>
<th>종류</th>
<th>이름</th>
</tr>
</thead>
<tbody><tr>
<td>얼굴</td>
<td>동그란 안경, 검정 선글라스</td>
</tr>
<tr>
<td>상의</td>
<td>파란색 티셔츠</td>
</tr>
<tr>
<td>하의</td>
<td>청바지</td>
</tr>
<tr>
<td>겉옷</td>
<td>긴 코트</td>
</tr>
</tbody></table>
<p>스파이가 가진 의상들이 담긴 2차원 배열 <code>clothes</code>가 주어질 때 서로 다른 옷의 조합의 수를 return 하도록 solution 함수를 작성해주세요.</p>
<h2 id="제한-사항">제한 사항</h2>
<ul>
<li>clothes의 각 행은 [의상의 이름, 의상의 종류]로 이루어져 있습니다.</li>
<li>스파이가 가진 의상의 수는 1개 이상 30개 이하입니다.</li>
<li>같은 이름을 가진 의상은 존재하지 않습니다.</li>
<li>clothes의 모든 원소는 문자열로 이루어져 있습니다.</li>
<li>모든 문자열의 길이는 1 이상 20 이하인 자연수이고 알파벳 소문자 또는 &#39;_&#39; 로만 이루어져 있습니다.</li>
<li>스파이는 하루에 최소 한 개의 의상은 입습니다.</li>
</ul>
<h2 id="입출력-예">입출력 예</h2>
<table>
<thead>
<tr>
<th>clothes</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[[&quot;yellow_hat&quot;, &quot;headgear&quot;], [&quot;blue_sunglasses&quot;, &quot;eyewear&quot;], [&quot;green_turban&quot;, &quot;headgear&quot;]]</td>
<td>5</td>
</tr>
<tr>
<td>[[&quot;crow_mask&quot;, &quot;face&quot;], [&quot;blue_sunglasses&quot;, &quot;face&quot;], [&quot;smoky_makeup&quot;, &quot;face&quot;]]</td>
<td>3</td>
</tr>
</tbody></table>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<h4 id="예제-1">예제 #1</h4>
<p>headgear에 해당하는 의상이 yellow_hat, green_turban이고 eyewear에 해당하는 의상이 blue_sunglasses이므로 아래와 같이 5개의 조합이 가능합니다.</p>
<blockquote>
</blockquote>
<ol>
<li>yellow_hat</li>
<li>blue_sunglasses</li>
<li>green_turban</li>
<li>yellow_hat + blue_sunglasses</li>
<li>green_turban + blue_sunglasses</li>
</ol>
<h4 id="예제-2">예제 #2</h4>
<p>face에 해당하는 의상이 crow_mask, blue_sunglasses, smoky_makeup이므로 아래와 같이 3개의 조합이 가능합니다.</p>
<blockquote>
</blockquote>
<ol>
<li>crow_mask</li>
<li>blue_sunglasses</li>
<li>smoky_makeup</li>
</ol>
<h2 id="💡-풀이">💡 풀이</h2>
<p>문제의 첫번째 예시를 통해 설명해 보겠습니다.</p>
<blockquote>
</blockquote>
<p>[[&quot;yellow_hat&quot;, &quot;headgear&quot;], [&quot;blue_sunglasses&quot;, &quot;eyewear&quot;], [&quot;green_turban&quot;, &quot;headgear&quot;]]</p>
<p>이 문제에서 주어진 <code>headgear</code>의 종류는 2개, <code>eyewear</code>의 종류는 1개 입니다.
이 자료로 <strong>Hash Table</strong>을 만들면 다음과 같습니다.</p>
<pre><code class="language-javascript">let hash = {
      &#39;headgear&#39;: 2,
      &#39;eyewear&#39;: 1
}</code></pre>
<p>각 의상은 종류별로 1개를 입거나 안입을 수 있습니다.</p>
<hr>
<p><code>headgear</code>의 두 종류 중 1개를 선택할 가짓수는 <strong>2C1 = 2</strong> 입니다.
그리고 <code>headgear</code>를 아예 안 입는 가짓수는 <strong>2C0 = 1</strong> 입니다.</p>
<p>=&gt; <code>headgear</code> 종류 중 1개를 입거나 아예 안입을 확률은 <strong>2C1 + 2C0</strong> 이 됩니다.</p>
<hr>
<p>이어서 <code>eyewear</code>의 한 종류중 1개를 선택할 가짓수는 <strong>1C1</strong> 입니다.
그리고 <code>eyewear</code>를 아예 안입는 가짓수 역시 <strong>1C0 = 1</strong> 입니다.
=&gt; <code>eyewear</code> 종류를 1개를 입거나 안입을 확률은 <strong>1C1 + 1C0</strong> 이 됩니다.</p>
<hr>
<p>최종적으로 <code>(2C1 + 2C0) x (1C1 + 1C0)</code> 가 각 의상 종류 중 1개를 입거나 안입을 확률의 조합이 됩니다.</p>
<p><strong>여기서 문제의 조건 중에 의상을 최소 1개는 입는다고 했습니다. 그러면 모든 의상 종류를 하나도 안입는 가짓수 1을 최종값에서 빼주면 됩니다.</strong></p>
<blockquote>
<p>정답은 (2C1 + 2C0) x (1C1 + 1C0) - 1</p>
</blockquote>
<p>이것을 일반화 하면?</p>
<p>hash = {종류1: a, 종류2: b, 종류3: c, 종류4: d} 이렇게 주어졌다면
<code>(aC1 + aC0) x (bC1 + bC0) x (cC1 + cC0) x (dC1 + dC0) - 1</code> 이 됩니다.</p>
<h2 id="💡-나의-소스코드">💡 나의 소스코드</h2>
<pre><code class="language-javascript">function solution(clothes) {
  let answer = 0;

  let obj = new Object();
  for (const cloth of clothes) {
    if (!obj.hasOwnProperty(cloth[1])) obj[cloth[1]] = 1;
    else obj[cloth[1]] = obj[cloth[1]] + 1;
  }

  console.log(obj); // { headgear: 2, eyewear: 1 }

  answer =
    Object.values(obj)
      .map((n) =&gt; comb(n, 0) + comb(n, 1))
      .reduce((a, c) =&gt; a * c) - 1; // -1을 해주는 이유는 적어도 1개의 의상은 입어야 하기 때문입니다.

  return answer;
}

function comb(n, r) {
  if (n === r || r === 0) return 1;
  else {
    return comb(n - 1, r) + comb(n - 1, r - 1);
  }
}
</code></pre>
<h2 id="💡-더-좋은-소스-코드">💡 더 좋은 소스 코드</h2>
<p>좋아요를 가장 많이 받은 다른 풀이 방법입니다. 가장 깔끔한 듯 합니다.</p>
<pre><code class="language-javascript">function solution(clothes) {
  return (
    Object.values(
      clothes.reduce((obj, cloth) =&gt; {
        obj[cloth[1]] = obj[cloth[1]] ? obj[cloth[1]] + 1 : 1;
        return obj;
      }, {})
    ).reduce((a, c) =&gt; a * (c + 1), 1) - 1
  );
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[어쨌든 이벤트 루프는 무엇입니까?]]></title>
            <link>https://velog.io/@paul_kang/%EC%96%B4%EC%A8%8C%EB%93%A0-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C</link>
            <guid>https://velog.io/@paul_kang/%EC%96%B4%EC%A8%8C%EB%93%A0-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C</guid>
            <pubDate>Sat, 03 Sep 2022 14:40:52 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://www.youtube.com/watch?v=8aGhZQkoFbQ">어쨌든 이벤트 루프는 무엇입니까? | Philip Roberts | JSConf EU</a> 강연을 보고 정리한 글입니다.</li>
</ul>
<blockquote>
<p>자바스크립트 &quot;Event Loop&quot;에 대해 정리하고자 합니다.</p>
</blockquote>
<h2 id="javascript-what-are-you">Javascript, What are you?</h2>
<p>대답은? &quot; a single-threaded non-blocking asynchronous concurrent language...&quot;</p>
<p>저는 싱글 스레드 논-블록킹 비동기 동적 언어입니다...😅</p>
<p>&quot; I have a call stack, an event loop, a callback queue, some other apis and stuff... &quot;</p>
<p>저는 콜스택과 이벤트 루프, 콜백 큐 그리고 다른 API나 뭐 그런것들을 가지고 있어요...</p>
<p>(이런 단어들에 집착할 필욘 없을 것 같습니다)</p>
<blockquote>
<p>이 글을 통해 자바스크립트에서 콜백이 왜 중요한지 이해하고, 런타임 환경에 대해 다시 한번 생각하는 기회가 되었으면 합니다.</p>
</blockquote>
<h2 id="자바스크립트-런타임-ex-v8">자바스크립트 런타임 (ex. v8)</h2>
<blockquote>
<p>크롬의 v8 같은 실제 자바스크립트 런타임을 들여다 보겠습니다.</p>
</blockquote>
<ul>
<li>자바스크립트는 싱글 스레드 프로그래밍 언어입니다. (싱글 스레드 런타임을 가지고 있다는 말)</li>
<li>이것은 결국 한번에 하나의 <code>싱글 call stack</code>만을 가지고 있다는 말입니다.</li>
<li>싱글 스레드 = 하나의 프로그램은 동시에 하나의 코드만 실행할 수 있다는 것!</li>
</ul>
<h3 id="call-stack-그림으로-이해하기">Call Stack 그림으로 이해하기</h3>
<p><img src="https://user-images.githubusercontent.com/63832678/188272883-2cfbb750-9f90-46a2-81ed-9d05b6c2e839.png" alt="콜스택"></p>
<ul>
<li>무엇인가를 리턴할 때마다 스택 맨 위에 있는 값을 꺼내게 됩니다. (<code>stack.pop()</code>)<pre><code class="language-javascript"></code></pre>
</li>
</ul>
<p>function foo() {
  return foo();
}</p>
<p>foo();</p>
<pre><code>
![maximum-call-stack](https://user-images.githubusercontent.com/63832678/188273050-7e989312-575b-475b-b971-2fe9ef6fddea.png)

- 이 에러는 개발을 하시다가 한번쯤은 만나 보셨을텐데요.
- 이제야 조금 명확한 이유를 알 것 같습니다.
- 계속되는 foo 함수의 호출로 call stack이 꽉 차버려서 크롬 v8 엔진은 &quot;이 녀석들을 중지 시킬게요 버그를 고쳐주세요!&quot; 라고 말하고 있습니다.

### blocking (What happens when things are slow?)

- 느린 동작이 스택에 남아있는 것을 보통 `블로킹`이라고 말하게 됩니다.
- 브라우저는 실제로 `ajax`요청을 동기적으로 실행시키지 않습니다.
- 브라우저에서 `ajax`요청을 동기적으로 강제시킨다면 브라우저는 모든 리퀘스트가 완료될 때까지 멈출겁니다. (It&#39;s stucked)
- 멈춰있는 동안 콜스택에 쌓여있던 코드들이 후에 실행되겠죠?
- 유려한 UI를 만드려고 한다면, 콜스택을 멈추게 해서는 안됩니다.


### 해결방법은? 비동기 콜백!
- 브라우저 혹은 노드에는 블로킹 함수가 거의 없습니다. 대부분 비동기로 만들어졌습니다.
- 어떤 코드를 실행하면 결국 콜백을 받고 이걸 나중에 실행한다는 말입니다.

```javascript

console.log(&#39;Hi&#39;);

setTimeout(function() {
      console.log(&#39;There&#39;);
}, 5000);

console.log(&#39;!!!&#39;);</code></pre><pre><code class="language-Console">Hi
!!!
There</code></pre>
<ul>
<li>이 순서로 출력합니다.</li>
<li>콜 스택에선 console.log(&#39;Hi&#39;); 가 먼저 쌓이고 동기적으로 실행됩니다.</li>
<li>그다음엔 setTimeout인데 갑자기 어디론가 사라져 버리고 console.log(&#39;!!!&#39;); 가 쌓이고 실행됩니다.</li>
<li>그리고 5초 뒤에 마법처럼 there 가 스택에 나타납니다.</li>
</ul>
<blockquote>
<p>여기에서 이벤트 루프와 동시성이 역할을 하게됩니다!!</p>
</blockquote>
<h3 id="동시성--이벤트루프-concurrency--event-loop">동시성 &amp; 이벤트루프 (Concurrency &amp; Event Loop)</h3>
<blockquote>
<p>한번에 하나씩. 꼭 그렇지는 않다는 것만 빼면.</p>
</blockquote>
<ul>
<li>자바스크립트 런타임은 한번에 하나만 할 수 있습니다.</li>
<li>하지만 브라우저에서의 <code>Web API</code>는 자바스크립트에서 호출할 수 있는 스레드를 효과적으로 지원합니다. 여기에 <code>동시성</code>이 들어오는 것입니다.</li>
</ul>
<blockquote>
<p>아까 사라져버린 setTimeout 콜 스택 상황을 좀더 디깅해봅시다.</p>
</blockquote>
<ul>
<li><code>setTimeout</code>은 브라우저에서 제공하는 Web API입니다. v8 소스코드엔 존재하지 않죠.</li>
</ul>
<p><img src="https://user-images.githubusercontent.com/63832678/188274153-619b5048-e479-4d56-837e-77bf5421af93.png" alt="webapis"></p>
<ul>
<li><p>브라우저가 타이머를 실행시키고 카운트 다운을 시작합니다. 이건 <code>setTimeout</code> 자체는 호출 완료되었고 스택에서 함수를 지울 수 있는 상황입니다.</p>
</li>
<li><p>콜 스택에서 setTimeout 호출문은 사라지고 5초가 흐릅니다.</p>
</li>
<li><p>webAPI는 갑자기 콜스택에 끼여 들 순 없습니다. (이제 테스크큐 또는 콜백큐가 활약할 차례입니다.)</p>
</li>
<li><p>모든 web API는 작동이 완료되면 콜백(cb)을 테스크 큐에 밀어넣습니다.</p>
</li>
</ul>
<p><img src="https://user-images.githubusercontent.com/63832678/188274307-144e2490-4fd9-43ad-9c79-9f6b03b1a0d1.png" alt="task-queue"></p>
<ul>
<li>그리고 드디어 이벤트루프가 할 일이 생겼습니다.</li>
</ul>
<h4 id="이벤트-루프의-역할은-콜-스택과-테스크-큐를-주시하는-것입니다">이벤트 루프의 역할은 콜 스택과 테스크 큐를 주시하는 것입니다.</h4>
<ul>
<li><strong>스택이 비어있으면, 큐의 첫번째 콜백을 스택에 쌓아 효과적으로 실행할 수 있게 해줍니다.</strong></li>
</ul>
<blockquote>
<p>여기까지만으로도 여러분들은 자바스크립트 비동기 함수가 어떻게 작동하는지 알게 되었습니다.</p>
</blockquote>
<h3 id="실제-브라우저-repaint-또는-렌더링-상황에서의-비동기-함수가-어떤-영향을-미치는지">실제 브라우저 repaint 또는 렌더링 상황에서의 비동기 함수가 어떤 영향을 미치는지</h3>
<ul>
<li><p>브라우저는 여러분이 자바스크립트로 하는 무언가로 인해 제약을 받습니다.</p>
</li>
<li><p>브라우저는 기본적으로 화면을 매 16.6ms, 즉 1초에 60프레임을 <code>repaint</code>하는게 이상적입니다. 그게 제일 빠른거죠.</p>
</li>
<li><p>스택에 코드가 있으면, 렌더링을 못합니다. 렌더도 하나의 콜백처럼 행동하니깐요. (스택이 비어질 때까지 기다려야 하는겁니다.)</p>
</li>
<li><p>다른점이라면, 렌더는 여러분의 콜백에 비해 더 높은 우선순위를 갖습니다. 매 16.6ms마다 큐에 들어갑니다 (Render Queue)</p>
</li>
<li><p>렌더가 막히면, 화면의 버튼을 클릭해서 반응을 보는게 불가능합니다.</p>
</li>
<li><p>사람들이 <code>event loop</code>를 막지 말라고 할 때, 바로 이런 현상을 뜻하는 것입니다.</p>
<ul>
<li>스택에 필요없는 느린 코드를 쌓아서 브라우저가 할일을 못하게 만들지 마라. 유동적인 UI를 구현해라. (Ex. 스크롤 이벤트에서의 렌더링 최적화)</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React.memo() 현명하게 써보자]]></title>
            <link>https://velog.io/@paul_kang/React.memo-%ED%98%84%EB%AA%85%ED%95%98%EA%B2%8C-%EC%8D%A8%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@paul_kang/React.memo-%ED%98%84%EB%AA%85%ED%95%98%EA%B2%8C-%EC%8D%A8%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Tue, 16 Nov 2021 09:52:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>사용자들은 빠르고 즉각적인 사용자 경험을 원한다. 100ms 이하의 응답 지연은 사용자로 하여금 즉각적인 반응으로 느끼게 한다. 반면, 100ms~300ms의 응답 지연은 지연이 어느정도 느껴지기 마련이다.</p>
</blockquote>
<blockquote>
<p>사용자 경험의 향상을 위해 리액트는 <code>고차 컴퍼넌트(Higher Order Component, HOC)</code> <code>React.memo()</code>를 제공한다. 컴포넌트를 래핑(wrapping)함으로써 컴포넌트를 메모이징(Memoizing)하고 불필요한 리렌더링을 피할 수 있다.</p>
</blockquote>
<blockquote>
<p>이 글은 언제 <code>React.memo()</code>가 성능을 향상시키고, 언제 불필요한지 설명할 것이다.</p>
</blockquote>
<h2 id="1-reactmemo">1. React.memo()</h2>
<p><strong>DOM</strong> 업데이트를 결정할 때, 리액트는 먼저 컴포넌트를 렌더링한 뒤, 이전 렌더링 결과와 비교한다. 만약 렌더링 결과가 다르다면 그 때 <strong>DOM</strong> 업데이트를 한다.</p>
<p>이전 렌더링 결과와 현재의 렌더링 결과를 비교하는 것은 빠르다. 하지만 어떤 상황에선 이 속도를 더 높일 수 있다.</p>
<p>컴포넌트가 <code>React.memo()</code>로 래핑 되었을 때, 리액트는 컴포넌트를 렌더링하고 결과를 메모이징(Memoizing)한다. 다음 렌더링전에 props가 같다면, 리액트는 메모이징(Memoizing)된 내용을 재사용한다.</p>
<p>예시:</p>
<pre><code class="language-javascript">export function Movie({ title, releaseDate }) {
  return (
    &lt;div&gt;
      &lt;div&gt;Movie title: {title}&lt;/div&gt;
      &lt;div&gt;Release date: {releaseDate}&lt;/div&gt;
    &lt;/div&gt;
  );
}
const MemoizedMovie = React.memo(Movie);
export { MemoizedMovie as Movie };</code></pre>
<p><code>React.memo(Movie)</code>는 메모이징(Memoizing)된 새로운 컴포넌트 <code>MemoizedMovie</code>를 리턴한다.</p>
<p>단 한가지의 차이점을 제외하고, 기존의 <code>Movie</code> 컴포넌트와 같은 렌더링 결과를 낸다.</p>
<p><code>MemoizedMovie</code>의 렌더링 결과는 메모이징 되어있다. 만약 <code>title</code>이나 <code>releaseDate</code> 같은 <code>props</code>가 변경 되지 않는다면 다음 렌더링 때 메모이징 된 결과를 사용한다.</p>
<pre><code class="language-javascript">// First render - MemoizedMovie이 호출됨.
&lt;MemoizedMovie 
  title=&quot;Heat&quot; 
  releaseDate=&quot;December 15, 1995&quot; 
/&gt;
// Second render - MemoizedMovie 호출 x.
&lt;MemoizedMovie
  title=&quot;Heat&quot; 
  releaseDate=&quot;December 15, 1995&quot; 
/&gt;</code></pre>
<p><strong>메모이징 된 결과를 재사용함으로써 성능상의 이점을 얻을 수 있다.</strong></p>
<ul>
<li>리액트는 렌더링을 스킵하고, <code>virtual DOM</code>이 달라진 부분을 체크하지 않는다.</li>
</ul>
<h3 id="11-props-동등-비교-커스터마이징">1.1 props 동등 비교 커스터마이징</h3>
<p>기본적으로 <code>React.memo()</code>는 props 혹은 props 객체를 비교할 때 얕은(shallow) 비교를 한다.</p>
<p>props 비교를 직접 커스터마이징 하고 싶다면 두번째 인수로 비교 함수를 지정해주면 된다.</p>
<pre><code class="language-javascript">React.memo(Component, [areEqual(prevProps, nextProps)]);</code></pre>
<p><code>areEqual(prevProps, nextProps)</code> 함수는 <code>prevProps</code>와 <code>nextProps</code>가 같다면 <code>true</code>를 반환할 것이다.</p>
<pre><code class="language-javascript">function moviePropsAreEqual(prevMovie, nextMovie) {
  return prevMovie.title === nextMovie.title
    &amp;&amp; prevMovie.releaseDate === nextMovie.releaseDate;
}
const MemoizedMovie = React.memo(Movie, moviePropsAreEqual);</code></pre>
<h2 id="2-언제-reactmemo를-사용할까">2. 언제 <code>React.memo()</code>를 사용할까?</h2>
<p><img src="https://images.velog.io/images/paul_kang/post/84bfdb53-c7c5-4049-a026-599d4c3b927c/when-to-use-react-memo-infographic.webp" alt=""></p>
<h3 id="21-같은-props로-렌더링이-자주-일어나는-component">2.1 같은 props로 렌더링이 자주 일어나는 Component</h3>
<p><code>React.memo()</code>를 사용하기 가장 좋은 경우는 함수형 컴퍼넌트가 같은 props로 자주 렌더링 될거라 예상될 때이다.</p>
<p>흔한 경우가 부모 컴포넌트에 의해 자식 컴포넌트가 같은 props로 렌더링 될 때이다.</p>
<p>예시: <code>MovieViewsRealtime</code> 실시간으로 업데이트되는 조회수를 가진 컴포넌트</p>
<pre><code class="language-javascript">function MovieViewsRealtime({ title, releaseDate, views }) {
  return (
    &lt;div&gt;
      &lt;Movie title={title} releaseDate={releaseDate} /&gt;
      Movie views: {views}
    &lt;/div&gt;
  );
}</code></pre>
<p>이 어플리케이션은 매초마다 서버에서 데이터를 폴링(polling)해서 <code>MovieViewsRealtime</code> 컴포넌트의 <code>views</code> prop을 업데이트한다.</p>
<pre><code class="language-javascript">// Initial render
&lt;MovieViewsRealtime 
  views={0} 
  title=&quot;Forrest Gump&quot; 
  releaseDate=&quot;June 23, 1994&quot; 
/&gt;
// After 1 second, views is 10
&lt;MovieViewsRealtime 
  views={10} 
  title=&quot;Forrest Gump&quot; 
  releaseDate=&quot;June 23, 1994&quot; 
/&gt;
// After 2 seconds, views is 25
&lt;MovieViewsRealtime 
  views={25} 
  title=&quot;Forrest Gump&quot; 
  releaseDate=&quot;June 23, 1994&quot; 
/&gt;
// etc</code></pre>
<p><code>views</code> prop이 업데이트 될 때 마다 <code>MoviewViewsRealtime</code> 컴포넌트가 리렌더링 된다. 이때 <code>Movie</code> 컴퍼넌트 또한 <code>title</code>이나 <code>releaseData</code>가 이전과 똑같아도 리렌더링 된다.</p>
<p>바로 이 상황에 <code>React.memo()</code>가 딱이다!</p>
<p>불필요한 리렌더링을 방지하기 위해 <code>React.memo()</code>로 메모이징을 해보자</p>
<pre><code class="language-javascript">function MovieViewsRealtime({ title, releaseDate, views }) {
  return (
    &lt;div&gt;
      &lt;MemoizedMovie title={title} releaseDate={releaseDate} /&gt;
      Movie views: {views}
    &lt;/div&gt;
  )
}</code></pre>
<p><code>title</code> 혹은 <code>releaseDate</code> props가 같다면, 리액트는 <code>MemoizedMovie</code>를 리렌더링 하지 않을 것이다. 이렇게 <code>MovieViewsRealtime</code> 컴포넌트의 성능을 향상시킬 수 있다.</p>
<blockquote>
<p>컴포넌트가 같은 props로 자주 렌더링 되거나, 무겁고 비용이 큰 연산이 있는 컴포넌트의 경우, <code>React.memo()</code>로 래핑할 필요가 있다.</p>
</blockquote>
<blockquote>
<p><strong>profiling</strong>을 통해 <code>React.memo()</code>를 적용했을 때의 이점을 측정하자.</p>
</blockquote>
<h2 id="3-언제-reactmemo를-피해야할까">3. 언제 React.memo()를 피해야할까?</h2>
<blockquote>
<p>만약 컴포넌트가 무겁지 않고 같은 props로 자주 렌더링 되지 않는다면 <code>React.memo()</code>는 필요치 않다.
성능적으로 이점이 없다면 사용하지 않도록 하자.</p>
</blockquote>
<blockquote>
<p>성능 관련 변화가 잘못 적용된다면, 오히려 성능 악화를 불러 일으킬 수 있다. <code>React.memo()</code> 현명하게 사용하자!</p>
</blockquote>
<p>클래스 컴포넌트를 <code>React.memo()</code>로 래핑하는 것은 바람직하지 않다. 만약 필요하다면, <code>PureComponent</code>를 확장해서 사용하거나 <code>shouldComponentUpdate()</code>를 커스터마이징해서 구현하는 것이 옳다.</p>
<h3 id="31-필요없는-props-비교">3.1 필요없는 props 비교</h3>
<p>렌더링될 때 props가 다른 경우가 대부분인 컴포넌트를 생각해 볼 때, 메모이제이션(Memoization)의 이점은 없다.</p>
<p>설령 props가 계속 변하는 컴포넌트를 <code>React.memo()</code>로 래핑할지라도, 리액트는 다음의 2가지 작업을 수행한다.</p>
<ol>
<li>이전 props와 다음 props의 동등 비교를 위한 비교 함수를 호출한다.</li>
<li>props 비교는 거의 항상 false를 리턴할 것이기에, 리액트는 이전 렌더링 결과와 현재의 렌더링 결과의 비교를 수행할 것이다.</li>
</ol>
<blockquote>
<p>** 당신은 성능 이점도 얻지 못한 채, 비교함수만 실행하게 될 것이다!**</p>
</blockquote>
<h2 id="4-reactmemo-와-콜백함수">4. React.memo() 와 콜백함수</h2>
<p>함수 객체는 오직 자신에게만 동일하다. (일반 객체와 같은 원칙을 따른다.)</p>
<p>다음 예를 보자.</p>
<pre><code class="language-javascript">function sumFactory() {
  return (a, b) =&gt; a + b;
}
const sum1 = sumFactory();
const sum2 = sumFactory();
console.log(sum1 === sum2); // =&gt; false
console.log(sum1 === sum1); // =&gt; true
console.log(sum2 === sum2); // =&gt; true</code></pre>
<p>함수 <code>sum1</code>과 <code>sum2</code> 두 함수 모두 두 숫자를 더해주는 함수이다. 그러나 <code>sum1</code>과 <code>sum2</code>는 다른 함수 객체이다.</p>
<blockquote>
<p><strong>부모 컴퍼넌트가 자식 컴퍼넌트의 콜백 함수를 정의할 때, 새 함수 객체 인스턴스가 생성된다. 이것이 어떻게 메모이제이션을 막는지 보고, 고쳐보자.</strong></p>
</blockquote>
<p><code>Logout</code> 컴포넌트는 콜백 prop인 <code>onLogout</code>을 갖는다.</p>
<pre><code class="language-javascript">function Logout({ username, onLogout }) {
  return (
    &lt;div onClick={onLogout}&gt;
      Logout {username}
    &lt;/div&gt;
  );
}
const MemoizedLogout = React.memo(Logout);</code></pre>
<p>콜백함수를 prop으로 받는 컴포넌트는 메모이제이션을 적용할 때 주의를 기울여야 한다. 부모 컴포넌트가 매 렌더링 때마다 다른 콜백 함수의 인스턴스를 넘겨줄 수 있기 때문이다.</p>
<pre><code class="language-javascript">function MyApp({ store, cookies }) {
  return (
    &lt;div className=&quot;main&quot;&gt;
      &lt;header&gt;
        &lt;MemoizedLogout
          username={store.username}
          onLogout={() =&gt; cookies.clear(&#39;session&#39;)}
        /&gt;
      &lt;/header&gt;
      {store.content}
    &lt;/div&gt;
  );
}</code></pre>
<p>동일한 <code>username</code> 이 전달 되더라도, <code>MemoizedLogout</code>은 새로운 <code>onLogout</code> 콜백 때문에 리렌더링을 하게 된다.</p>
<p>Memoization is broken!</p>
<p>이를 해결하기 위해서는, <code>onLogout</code> prop은 매번 같은 콜백 함수 인스턴스를 받아야 한다. <code>useCallback()</code>을 적용해서 콜백 인스턴스를 렌더링 사이에서 지켜보자.</p>
<pre><code class="language-javascript">const MemoizedLogout = React.memo(Logout);
function MyApp({ store, cookies }) {
  const onLogout = useCallback(
    () =&gt; cookies.clear(&#39;session&#39;), 
    [cookies]
  );
  return (
    &lt;div className=&quot;main&quot;&gt;
      &lt;header&gt;
        &lt;MemoizedLogout
          username={store.username}
          onLogout={onLogout}
        /&gt;
      &lt;/header&gt;
      {store.content}
    &lt;/div&gt;
  );
}</code></pre>
<blockquote>
<p><code>useCallback(() =&gt; { cookies.clear() }, [])</code> 는 항상 같은 함수 인스턴스를 반환한다. (cookies 값이 바뀌지 않는다면) <code>MemoizedLogout</code>의 메모이제이션이 정상 작동 될 것이다!</p>
</blockquote>
<h2 id="5-reactmemo는-성능개선의-실마리">5. React.memo()는 성능개선의 실마리</h2>
<p>말그대로, 리액트는 성능개선의 도구로 메모이제이션을 사용한다. 대부분의 상황에서 리액트는 메모이제이션된 컴포넌트의 렌더링을 피하지만, 당신은 그것에 의존해선 안된다.</p>
<h2 id="6-reactmemo와-hooks">6. React.memo()와 hooks</h2>
<p>hooks를 사용하는 컴포넌트는 메모이제이션을 달성하기 위해 자유롭게 <code>React.memo()</code>를 사용할 수 있다. </p>
<p>단, 리액트는 state 값이 바뀐다면 언제든 리렌더링 된다. 설령 컴포넌트가 <code>React.memo()</code>로 래핑되었다 할지라도...</p>
<h2 id="7-결론">7. 결론</h2>
<p><code>React.memo()</code>는 함수형 컴포넌트를 메모이제이션을 하게 해 주는 훌륭한 도구다. 올바르게 적용 된다면 변경되지 않은 동일한 prop에 대해 리렌더링을 하는 것을 막을 수 있다.</p>
<p>다만, 콜백 함수를 prop으로 사용하는 컴포넌트에서 메모이징을 할 때 주의하자. 그리고 확실히 하자. 렌더링 사이에서 이전과 동일한 콜백 함수 인스턴스를 넘기는지.</p>
<p>마지막으로, 메모이제이션의 이점을 확인하기 위해 <strong>profiling</strong>을 사용하는 것을 잊지 말자.</p>
<blockquote>
<p>한줄 요약: <code>React.memo()</code> 컴포넌트의 props가 바뀌지 않았다면, 리렌더링 하지 않도록 설정하여 성능 최적화를 해주는 착한 녀석.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Hook 뽀개기 with TS 👊]]></title>
            <link>https://velog.io/@paul_kang/React-Hook-%EB%BD%80%EA%B0%9C%EA%B8%B0</link>
            <guid>https://velog.io/@paul_kang/React-Hook-%EB%BD%80%EA%B0%9C%EA%B8%B0</guid>
            <pubDate>Sat, 02 Oct 2021 12:40:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/paul_kang/post/d147cf81-f086-49a5-a84c-f3953cac6c44/react.png" alt=""></p>
<h2 id="리액트-hook-알아보자-ts-ver-✍">리액트 Hook 알아보자 (TS ver.) ✍</h2>
<h3 id="1-usestate">1. useState</h3>
<h4 id="기본-꼴">기본 꼴</h4>
<pre><code class="language-typescript">import React, { useState } from &quot;react&quot;

// array destructuring
const [user, setUser] = useState(null);</code></pre>
<blockquote>
<p>인자로 받은 값이 상태의 초기값입니다. <code>user</code> 라는 변수에 초기값 null을 저장하고 <code>setUser</code> 함수를 통해 <code>user</code> 변수에 담긴 상태값을 변경할 수 있습니다.</p>
</blockquote>
<h4 id="여기서-잠깐-리액트의-특성을-알고-넘어갑시다">여기서 잠깐, 리액트의 특성을 알고 넘어갑시다.</h4>
<blockquote>
<p>리액트의 특징 중 하나, 상태값이 변경되면 Virtual DOM이 해당 상태값이 사용된 부분만 다시 렌더링합니다. 
그러나 <code>setUser</code> 와 같은 상태값 변경 함수가 아닌 사용자가 임의로 상태값을 변경하면 리액트는 변경사항을 인식하지 못하고 <strong>리렌더링</strong>을 하지 않습니다. 따라서 상태값을 변경할 때 반드시 상태값 변경 함수(setUser)를 사용합시다.</p>
</blockquote>
<h4 id="예시-ts-ver">예시 (TS ver.)</h4>
<pre><code class="language-typescript">import { useState } from &#39;react&#39;;

type AuthUser = {
  name: string
  email: string
}

export const User = () =&gt; {

  const [user, setUser] = useState&lt;AuthUser&gt;({} as AuthUser); // 1️⃣

  return (
    &lt;div&gt;
      &lt;div&gt;User name is {user.name}&lt;/div&gt;
      &lt;div&gt;User email is {user.email}&lt;/div&gt;
    &lt;/div&gt;
  )
}</code></pre>
<blockquote>
<p>1번 부분을 제외하면 JS 환경에서의 useState 쓰는 방법과 동일합니다.
1번 라인에서 &lt;&gt; 안에 <code>AuthUser</code>라는 Type을 특정해준 것이고 인자값은 초기 상태값인데 <code>{} as AuthUser</code> 에서 as AuthUser 는 <strong>Type Assertion</strong> 이라는 개념입니다. 즉, 초기값은 빈 객체이지만 이 빈 객체의 타입은 <code>AuthUser</code> 라는 것을 개발자가 지정해준 것입니다.</p>
</blockquote>
<blockquote>
<p><strong>Type Assertion</strong> 은 개발자가 타입스크립트 컴파일러보다 이 상태(state)에 대해선 더 잘 알고 있다,  이 상태의 타입은 어떤 특정한 타입만 올 수 있다 같은 이런 상황에서만 사용하도록 합시다.</p>
</blockquote>
<blockquote>
<p>위 예시 코드의 상황은 user값이 항상 존재하는 상황(혹은 user값이 null이 될 수 없는 상황) 에서 가능한 코드입니다.</p>
</blockquote>
<h3 id="2-useeffect">2. useEffect</h3>
<ul>
<li><code>useEffect</code>는 모든 사이드 이펙트를 담당합니다. 예를들면, 이벤트 리스너를 추가하거나, data fetch를 하거나...</li>
<li><code>componentDidUpdate</code>, <code>componentDidMount</code>, <code>componentWillUnmount</code> 
컴포넌트가 처음 마운트 될 때 / 컴포넌트가 업데이트 될 때 / 컴포넌트가 언마운트 될 때 일어나는 동작을 담당합니다.</li>
</ul>
<h4 id="기본-꼴-1">기본 꼴</h4>
<pre><code class="language-typescript">const [name, setName] = useState(&#39;Paul&#39;);
useEffect(() =&gt; {
  document.title = `Hello ${name}&#39;s world!`;
}, [name])</code></pre>
<p>컴포넌트가 마운트 되었을 때 브라우저 title을 &quot;Hello Paul&#39;s world!&quot;로 바꿔줍니다. 그리고 name 상태값이 변경되어 컴포넌트가 업데이트 되었을 때도 호출됩니다.</p>
<h5 id="clean-up-function도-알고-넘어갑시다">clean up function도 알고 넘어갑시다.</h5>
<pre><code class="language-typescript">import React, {useEffect} from &#39;react&#39;

  useEffect(
    (effect function)
    return {
      (cleanup function)
    }
  }, [dependencies]);
</code></pre>
<blockquote>
<p>이펙트 함수 호출 시점은 컴포넌트가 처음 마운트될 때, 의존값으로 주어진 값이 변경될 때라고 말씀드렸습니다.</p>
</blockquote>
<blockquote>
<p>그렇다면 클린업 함수는?
이펙트함수가 호출되기 전, 컴포넌트가 언마운트(unmount)될 때 호출됩니다.
이전 이펙트로 인한 결과를 정리해주는 역할입니다. 말그대로 cleanup!</p>
</blockquote>
<blockquote>
<p>그리고 의존값이 비어있다면 최초 마운트할 때만 이펙트함수를 호출합니다. 또 의존값이 아예 존재하지 않는다면 컴포넌트가 렌더링될 때마다 이펙트를 호출합니다. (조심해야겠죠?)</p>
</blockquote>
<pre><code class="language-typescript">import { useState, useEffect } from &quot;react&quot;;

const Test = () =&gt; {
  const [windowScrollY, setWindowScrollY] = useState&lt;number&gt;(window.scrollY);
  const handleScroll = () =&gt; {
    setWindowScrollY(window.scrollY);
  };
  useEffect(() =&gt; {
    window.addEventListener(&quot;scroll&quot;, handleScroll);
    console.log(&quot;effect&quot;);
    return () =&gt; {
      window.removeEventListener(&quot;resize&quot;, handleScroll);
      console.log(&quot;clean up&quot;);
    };
  }, [windowScrollY]);
  return &lt;div style={{ height: &#39;500vh&#39; }}&gt;{windowScrollY}&lt;/div&gt;;
}

export default Test;

</code></pre>
<p><img src="https://images.velog.io/images/paul_kang/post/58d94dc1-eca2-45c3-a616-2d2e2fadf540/scrollEvent.gif" alt=""></p>
<ul>
<li>스크롤을 내릴때마다 <code>cleanup</code>함수가 먼저 호출되고 정리를 하고 나서 <code>effect</code>함수가 호출되는 것을 확인할 수 있습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Lexical scope and Closure]]></title>
            <link>https://velog.io/@paul_kang/Lexical-scope-and-Closure</link>
            <guid>https://velog.io/@paul_kang/Lexical-scope-and-Closure</guid>
            <pubDate>Sat, 02 Oct 2021 11:27:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/paul_kang/post/9c8bb0c5-f04c-4c01-ad68-d67cf3b9d6b3/JavaScript.png" alt=""></p>
<h3 id="클로저">클로저</h3>
<blockquote>
<p>📚 <strong>클로저</strong>에 대해 알아봅시다.</p>
</blockquote>
<p>은연중에 사용했을 클로저. 제대로 정리해봅시다.</p>
<blockquote>
<p>자바스크립트에서는 내부함수와 외부함수가 존재합니다. 내부함수는 외부함수의 지역변수에 접근할 수 있습니다.
그리고 외부함수가 소멸한 후에도 외부함수의 지역변수에 접근 가능합니다.
이 특성을 이용한 것이 <strong>closure</strong> 입니다.</p>
</blockquote>
<p>말로는 전부 이해하기 힘들죠? 😭</p>
<p>그럼 <strong>예시</strong>를 보겠습니다.</p>
<pre><code class="language-javascript">function outer(outerValue) {
  return function inner(innerValue) {
    console.log(`outer value : ${outerValue}`);
    console.log(`inner value : ${innerValue}`);
  }
}

const inner = outer(&quot;겉은 바삭&quot;);

inner(&quot;속은 촉촉&quot;);

// outer value : 겉은 바삭
// inner value : 속은 촉촉
</code></pre>
<p>결과는 ?</p>
<blockquote>
<p>outer value : 겉은 바삭
inner value : 속은 촉촉</p>
</blockquote>
<p>외부 함수에서 선언된 <code>outerValue</code>라는 지역변수를 inner 함수에서 기억하고 있습니다! </p>
<p>여기서 궁금한 점은, inner 함수는 outerValue 값인 <strong>&quot;겉은 바삭&quot;</strong> 이라는 값을 어떻게 기억하는 걸까요? </p>
<p>이에 대해선 <strong>렉시컬 스코프</strong>에 대해 이해하고 있어야 합니다.</p>
<h3 id="렉시컬-스코프와-유효-범위">렉시컬 스코프와 유효 범위</h3>
<p>위의 상황에서 함수가 실행되면 자바스크립트는 외부 렉시컬 환경과 내부 렉시컬 환경이라는 고유한 유효 범위를 갖습니다. 그리고 이를 메모리에 저장합니다.</p>
<p>외부 렉시컬 환경은 말그대로 함수 바깥의 범위를 참조하고, 내부 렉시컬 환경은 함수 내부에서 사용된 인자나 지역변수를 참조합니다. </p>
<h4 id="여기서-중요한점">여기서 중요한점!</h4>
<p>함수 내부에서 변수를 사용할 때는 일단 내부 렉시컬 환경을 탐색한 후, 찾는 변수가 없다면 외부 렉시컬 환경으로 이동해 변수를 찾습니다. 이것이 <strong>closure</strong>입니다. (앞서 얘기 했듯이)</p>
<p>그럼 어떻게 <strong>&quot;겉은 바삭&quot;</strong>이 출력되었는지 어느 정도 감이 오시나요?</p>
<h3 id="그렇다면-클로저를-통해-얻을-수-있는-가장-큰-이점">그렇다면 클로저를 통해 얻을 수 있는 가장 큰 이점?</h3>
<blockquote>
<p>함수 종료 후에도 사라지지 않는 지역 변수를 만들 수 있습니다!
더 나아가서 객제 지향의 <strong>캡슐화</strong>의 기능을  클로저를 통해 구현할 수 있습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[useLayoutEffect hook을 알아보자]]></title>
            <link>https://velog.io/@paul_kang/useLayoutEffect-hook%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@paul_kang/useLayoutEffect-hook%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Tue, 21 Sep 2021 13:07:05 GMT</pubDate>
            <description><![CDATA[<p><strong>useLayoutEffect</strong>에 대하여 알아봅시다. 
<img src="https://images.velog.io/images/paul_kang/post/891fa74f-2e68-4c18-b062-a74072459d71/react-flow.jpg" alt=""></p>
<ul>
<li>위 사진은 react-hook-flow를 보여줍니다.</li>
</ul>
<p>자세히는 모르겠지만 뭔가 <strong>useEffect</strong>보다 먼저 실행될 것 같은 느낌이 듭니다.</p>
<p>테스트 코드를 작성해보면서 <strong>useEffect</strong> 와 <strong>useLayoutEffect</strong> 가 어떻게 다른지 알아봅시다!</p>
<h3 id="useeffect">useEffect</h3>
<p><img src="https://images.velog.io/images/paul_kang/post/baec0947-c487-49a3-abb5-6a27fb8ab50a/useEffect.png" alt="">
많이 접하는 react hook의 useEffect 상황입니다.</p>
<p>이 때의 실행 순서는 이렇습니다.</p>
<ol>
<li><strong><code>&lt;div&gt;나는 입니다. 지금 이 먹고 싶습니다.&lt;/div&gt;</code></strong> 를 페인트</li>
<li>effect 내부의 setName, setFood를 호출</li>
<li>re-render -&gt; <strong><code>&lt;div&gt;나는 방루이입니다. 지금 초밥이 먹고 싶습니다.&lt;/div&gt;</code></strong></li>
</ol>
<p>이렇게 간단한 DOM 구조라면 체감할 수 없겠지만, 조금만 화면이 복잡해지면 1번에서 3번의 과정에서 <code>나는 입니다. 지금 이 먹고 싶습니다..</code> 라는 텍스트를 오랫동안 볼 수도 있습니다.</p>
<p>하지만 useLayoutEffect라면?</p>
<h3 id="uselayouteffect">useLayoutEffect</h3>
<p><img src="https://images.velog.io/images/paul_kang/post/56493759-8c8b-43f5-9a1f-160e70c94d5d/useLayoutEffect.png" alt=""></p>
<p>거의 똑같은 구조지만 상황은 달라집니다. 실행순서는 이렇습니다.</p>
<ol>
<li>effect 내부의 setName, setFood 호출.</li>
<li><strong><code>&lt;div&gt;나는 방루이입니다. 지금 초밥이 먹고 싶습니다.&lt;/div&gt;</code></strong> 를 페인트</li>
</ol>
<p>아무리 DOM 구조가 복잡해져도 더 이상 잠깐의 깜빡임도 볼 수 없을 것입니다.</p>
<h3 id="정리">정리</h3>
<ul>
<li>useEffect의 이펙트는 DOM이 화면에 그려진 후에 호출됩니다.</li>
<li>useLayoutEffect의 이펙트는 DOM이 화면에 그려지기 전에 호출됩니다.</li>
<li>따라서 이펙트 내에서 초기화 되어야 하는 state는 사용자 경험을 위해 <strong>useLayoutEffect</strong>를 활용해 봅시다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[다크 모드를 구현해보자 (feat. styled-components with Next.js)]]></title>
            <link>https://velog.io/@paul_kang/%EB%8B%A4%ED%81%AC-%EB%AA%A8%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90-feat.-styled-components-with-Next.js</link>
            <guid>https://velog.io/@paul_kang/%EB%8B%A4%ED%81%AC-%EB%AA%A8%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90-feat.-styled-components-with-Next.js</guid>
            <pubDate>Sat, 18 Sep 2021 12:20:17 GMT</pubDate>
            <description><![CDATA[<h2 id="🕶-다크-모드란">🕶 다크 모드란?</h2>
<blockquote>
<p>다크 모드는 어두운 배경에 밝은 텍스트를 표시하는 것을 말합니다. 스크린에서 방출되는 빛을 줄임으로써 컴퓨터나 테블릿, 모바일에서 화면을 조금 더 쉽게 볼 수 있도록 해줍니다.</p>
</blockquote>
<h2 id="💄-스타일-컴포넌트">💄 스타일 컴포넌트?</h2>
<blockquote>
<p>리액트와 가장 호흡이 찰떡이라 생각하는 CSS in JS 라이브러리 입니다.
모던한 프론트엔드 개발자에게 개발자 경험을 획기적으로 개선해줍니다.</p>
</blockquote>
<h2 id="그럼-다크-모드를-구현해-봅시다">그럼 다크 모드를 구현해 봅시다!</h2>
<pre><code>npm i styled-components</code></pre><blockquote>
<p>우선 스타일 컴포넌트를 설치해주고 그 다음 각 프로젝트의 root 디렉토리에 ThemeConfig.js(TS 환경이라면 확장자를 바꿔줍시다) 파일을 만들고 다음과 같이 설정합니다.</p>
</blockquote>
<pre><code>import { createGlobalStyle} from &quot;styled-components&quot;

export const lightTheme = {
  body: &#39;#FFF&#39;,
  text: &#39;#363537&#39;,
  toggleBorder: &#39;#FFF&#39;,
  background: &#39;#363537&#39;,
}

export const darkTheme = {
  body: &#39;#363537&#39;,
  text: &#39;#FAFAFA&#39;,
  toggleBorder: &#39;#6B8096&#39;,
  background: &#39;#999&#39;,
}

export const GlobalStyles = createGlobalStyle`
  body {
    background: ${({ theme }) =&gt; theme.body};
    color: ${({ theme }) =&gt; theme.text};
    font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif;
    transition: all 0.50s linear;
  }
`</code></pre><blockquote>
<p>여기서, <strong>light 테마</strong>와 <strong>dark 테마</strong>, 그리고 <strong>GlobalStyles</strong>를 정의합니다. 나중에 앱전체를 Theming하기 위해서는 이것들이 필요합니다. <strong>createGlobalStyle</strong>는 전역 스타일을 앱전체(body)에 주기 위해 React Component를 생성해줍니다. </p>
</blockquote>
<blockquote>
<p>이렇게 다크모드를 구현하기 위한 테마를 설정했습니다. 그럼 다크모드로 전환하기 위한 토글 버튼을 만들어 봅시다. _app.js 파일에 테마 상태를 만들고 토글을 설정해 봅시다.</p>
</blockquote>
<pre><code>const [theme, setTheme] = useState(&quot;light&quot;) 

const toggleTheme = () =&gt; {
    theme == &#39;light&#39; ? setTheme(&#39;dark&#39;) : setTheme(&#39;light&#39;)
}</code></pre><blockquote>
<p>가장 중요한 것을 까먹었네요. 바로 <strong>ThemeProvider</strong>로 _app.js의 렌더링 부분을 감싸주는 것입니다. </p>
</blockquote>
<pre><code>import { useState } from &quot;react&quot;
import { ThemeProvider } from &quot;styled-components&quot;;

const [theme, setTheme] = useState(&quot;light&quot;) 

const toggleTheme = () =&gt; {
    theme == &#39;light&#39; ? setTheme(&#39;dark&#39;) : setTheme(&#39;light&#39;)
}

return (
  &lt;ThemeProvider&gt;
    &lt;button onClick={toggleTheme}&gt;Switch Theme&lt;/button&gt;
    &lt;Component {...pageProps} /&gt;
  &lt;/ThemeProvider&gt;
) </code></pre><blockquote>
<p>이렇게 설정이 모두 끝났습니다. 마지막으로 _app.js의 전체 코드를 봅시다.</p>
</blockquote>
<pre><code>function App({ Component, pageProps }) {
  const [theme, setTheme] = useState(&quot;light&quot;) 

  const toggleTheme = () =&gt; {
    theme == &#39;light&#39; ? setTheme(&#39;dark&#39;) : setTheme(&#39;light&#39;)
  }

  return (
    &lt;ThemeProvider theme={theme == &#39;light&#39; ? lightTheme : darkTheme}&gt;
      &lt;GlobalStyles /&gt;
      &lt;button onClick={toggleTheme}&gt;Switch Theme&lt;/button&gt;
      &lt;Component {...pageProps} /&gt;
    &lt;/ThemeProvider&amp;gt;
  ) 
}
export default App</code></pre><p>이렇게 성공적으로 Next.js에서의 다크모드를 구현했습니다. 🤩</p>
<blockquote>
<p>TS환경에서의 _app.ts도 올려봅니다.</p>
</blockquote>
<pre><code>import Layout from &#39;../components/Layout&#39;
import { AppProps } from &#39;next/app&#39;
import { ThemeProvider } from &#39;styled-components&#39;
import { useState } from &#39;react&#39;
import { darkTheme, GlobalStyles, lightTheme } from &#39;../my-theme&#39;

function MyApp({ Component, pageProps }: AppProps) {

  const [theme, setTheme] = useState&lt;string&gt;(&#39;light&#39;);
  const toggleTheme = () =&gt; {
    theme == &#39;light&#39; ? setTheme(&#39;dark&#39;) : setTheme(&#39;light&#39;)
  }
  return (
    &lt;ThemeProvider theme={theme === &#39;light&#39; ? lightTheme : darkTheme}&gt;
      &lt;GlobalStyles /&gt;
        &lt;Layout&gt;
          &lt;Component {...pageProps} /&gt;
          &lt;button onClick={toggleTheme}&gt;Switch Theme&lt;/button&gt;
        &lt;/Layout&gt;
    &lt;/ThemeProvider&gt;
    )
}

export default MyApp
</code></pre><blockquote>
<p>이건 ThemeConfig.ts</p>
</blockquote>
<pre><code>import { createGlobalStyle, DefaultTheme } from &#39;styled-components&#39;;

export const lightTheme: DefaultTheme = {
  body: &#39;#FFF&#39;,
  text: &#39;#363537&#39;,
  toggleBorder: &#39;#FFF&#39;,
  background: &#39;#363537&#39;,
}

export const darkTheme: DefaultTheme = {
  body: &#39;#363537&#39;,
  text: &#39;#FAFAFA&#39;,
  toggleBorder: &#39;#6B8096&#39;,
  background: &#39;#999&#39;,
}

export const GlobalStyles = createGlobalStyle`
  body {
    background: ${({ theme }) =&gt; theme.body};
    color: ${({ theme }) =&gt; theme.text};
    font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif;
    transition: all 0.50s linear;
  }
`</code></pre><blockquote>
<p>TS 환경에선 특별히 styled.d.ts 설정을 해줘야 합니다 (root 디렉토리에 만들어줍니다.)</p>
</blockquote>
<pre><code>import &#39;styled-components&#39;;

declare module &#39;styled-components&#39; {
  export interface DefaultTheme {
    body: string;
    text: string;
    toggleBorder?: string;
    background?: string;
  }
}</code></pre><h2 id="더-modern한-다크-모드-구현-방법은-없을까">더 modern한 다크 모드 구현 방법은 없을까?</h2>
<p>이 글을 작성하기 위해 조사를 하던 중 <strong>useDarkMode</strong> Hook이 존재한다는 것을 알게 되었습니다.</p>
<p><strong>useDarkMode</strong> Hook를 사용하면 테마 상태를 설정하거나 토글을 만들 필요가 없습니다. </p>
<p>이런 핫한 기능은 다음에 따로 정리해볼게요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[최소 신장 트리 (MST)]]></title>
            <link>https://velog.io/@paul_kang/%EC%B5%9C%EC%86%8C-%EC%8B%A0%EC%9E%A5-%ED%8A%B8%EB%A6%AC-MST</link>
            <guid>https://velog.io/@paul_kang/%EC%B5%9C%EC%86%8C-%EC%8B%A0%EC%9E%A5-%ED%8A%B8%EB%A6%AC-MST</guid>
            <pubDate>Wed, 15 Sep 2021 10:34:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/paul_kang/post/126babb1-d056-4aa6-8527-c69de19674d8/image.png" alt=""></p>
<blockquote>
<h3 id="💡-목표">💡 목표</h3>
</blockquote>
<ul>
<li>자신없던 최소신장트리와 크루스칼 알고리즘, 프림 알고리즘을 정리하면서 공부하고자 합니다.</li>
</ul>
<h1 id="📌-최소-신장트리">📌 최소 신장트리</h1>
<blockquote>
<h4 id="✅-그래프에서-최소-비용-문제">✅ 그래프에서 최소 비용 문제</h4>
<pre><code>1. 모든 정점을 연결하는 간선들의 가중치의 합이 최소가 되는 트리
2. 두 정점 사이의 최소 비용의 경로 찾기</code></pre></blockquote>
<h4 id="✅-신장트리">✅ 신장트리</h4>
<pre><code>- n개의 정점으로 이루어진 무향 그래프에서 n개의 정점과 n-1개의 간선으로 이루어진 트리</code></pre><h4 id="✅-최소-신장-트리-minimum-spanning-tree">✅ 최소 신장 트리 (Minimum Spanning Tree)</h4>
<pre><code>- 무향 가중치 그래프(undirected weighted graph)에서 신장 트리를 구성하는 간선들의 가중치의 합이 최소인 신장 트리</code></pre><h2 id="kruskal-알고리즘간선-선택">KRUSKAL 알고리즘(간선 선택)</h2>
<blockquote>
<ol>
<li>모든 간선을 가중치에 따라 ((오름차순 정렬))</li>
<li>가중치가 낮은 간선부터 선택하면서 트리를 증가시킴<ul>
<li>사이클이 존재한다면? 다음으로 가중치 낮은 간선 선택</li>
</ul>
</li>
<li><code>n-1</code>개의 간선이 선택될 때까지 2번을 반복</li>
</ol>
</blockquote>
<h3 id="흐름을-봅시다-🎨">흐름을 봅시다 🎨</h3>
<h4 id="1-오름차순으로-간선을-정렬합니다">1. 오름차순으로 간선을 정렬합니다.</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/cfce59c5-3de2-4afa-953d-3db53ea506b6/kruscal1.png" alt=""></p>
<h4 id="2-비용이-제일-작은-35-간선-선택">2. 비용이 제일 작은 3,5 간선 선택</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/f54bdc01-a28b-4c69-9ebd-7d692253fe8d/kruscal2.png" alt=""></p>
<h4 id="3-그다음-12-간선-선택">3. 그다음 1,2 간선 선택</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/fccc819b-f32e-4667-81d9-a352eeba5848/kruscal3.png" alt=""></p>
<h4 id="4-계속-진행">4. 계속 진행</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/f56eac42-ecd9-47b4-85e9-7c43adfde50b/kruscal4.png" alt="">
<img src="https://images.velog.io/images/paul_kang/post/6adc9f5d-0a14-4302-a6d8-cd5a4ed0aabb/kruscal5.png" alt=""></p>
<h4 id="5-사이클이-존재한다면-다음으로-넘어갑니다">5. 사이클이 존재한다면? 다음으로 넘어갑니다.</h4>
<ul>
<li>0, 1 이미 같은 집합이므로 union 불가
<img src="https://images.velog.io/images/paul_kang/post/58b2c2af-4807-4b26-931e-b5d0e49da812/kruscal6.png" alt=""></li>
</ul>
<h4 id="6-n-1개의-간선-선택-완료">6. N-1개의 간선 선택 완료</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/da395409-9b47-4b1a-9005-4fb3854ab809/kruscal7.png" alt=""></p>
<h3 id="java로-구현">Java로 구현</h3>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class MSTKruskalTest {

    // 간선 정보 저장하는 객체 -&gt; 정렬되어야함
    static class Edge implements Comparable&lt;Edge&gt; {
        int start, end, weight;

        public Edge(int start, int end, int weight) {
            this.start = start;
            this.end = end;
            this.weight = weight;
        }

        @Override
        public int compareTo(Edge o) {
            return Integer.compare(weight, o.weight); // 오름차순
        }
    }

    static int[] parents; // 부모 원소를 관리 (트리처럼 사용) // 사이클이 발생하는지 여부 체크
    static int V, E; // Vertex, Edge // 정점, 간선
    static Edge[] edgeList; // 간선들 저장하는 배열 -&gt; 정렬되어야함

    private static void make() {
        parents = new int[V];
        // 모든 원소를 자신을 대표자로 만듦
        for (int i = 0; i &lt; V; i++) {
            parents[i] = i;
        }
    }

    // a가 속한 집합의 대표자 찾기
    private static int find(int a) {
        if (a == parents[a])
            return a; // 자신이 대표자
        return parents[a] = find(parents[a]); // 자신이 속한 집합의 대표자를 자신의 부모로 : path compression
    }

    // 두 원소를 하나의 집합으로 합치기(대표자를 이용해서 합침)
    private static boolean union(int a, int b) {
        int aRoot = find(a);
        int bRoot = find(b);
        if (aRoot == bRoot)
            return false; // 이미 같은 집합으로 합치지 않음

        parents[bRoot] = aRoot;
        return true;
    }

    public static void main(String[] args) throws Exception {
        System.setIn(new FileInputStream(&quot;res/kruskal_input.txt&quot;));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), &quot; &quot;);

        V = Integer.parseInt(st.nextToken());
        E = Integer.parseInt(st.nextToken());

        // 간선 리스트 작성
        edgeList = new Edge[E];

        for (int i = 0; i &lt; E; i++) {
            st = new StringTokenizer(br.readLine(), &quot; &quot;);
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            int weight = Integer.parseInt(st.nextToken());
            edgeList[i] = new Edge(start, end, weight);
        }

        Arrays.sort(edgeList); // 오름차순 정렬

        make(); // 모든 정점을 각각의 집합으로 만들고 출발

        // 간선 하나씩 시도하며 트리 만들어 감.
        int cnt = 0, result = 0; // cnt: 연결 간선 수 result: 최소비용들의 합
        for (Edge e : edgeList) {
            if (union(e.start, e.end)) {  // 시작정점 - 끝정점을 연결 시도
                // 서로 다른 집합이었다가 합쳐지는 게 가능한 경우. start, end는 이제 같은 집합이 됨
                result += e.weight; // 비용누적
                if (++cnt == V - 1) // 연결 간선수가 정점수-1이면 다 연결한 것임
                    break; // 신장트리 완성
            }
        }

        System.out.println(result);
    }

}</code></pre>
<h2 id="prim-알고리즘정점-선택">PRIM 알고리즘(정점 선택)</h2>
<blockquote>
<ol>
<li>임의의 정점 하나 선택해서 시작</li>
<li>선택한 정점과 인접하는 정점들 중의 최소 비용의 간선이 존재하는 정점을 선택</li>
<li><code>N</code>개의 모든 정점이 선택될 때 까지 1번, 2번 과정을 반복</li>
</ol>
</blockquote>
<blockquote>
<h4 id="서로소인-2개의-집합-정보를-유지">서로소인 2개의 집합 정보를 유지</h4>
</blockquote>
<ol>
<li>트리 정점들 - MST를 만들기 위해 선택된 정점들</li>
<li>비트리 정점들 - 선택되지 않은 정점들</li>
</ol>
<h3 id="흐름을-봅시다-🎨-1">흐름을 봅시다 🎨</h3>
<h4 id="1-임의의-정점-0을-선택-0에서-제일-적은-비용인-2와-연결합니다">1. 임의의 정점 0을 선택, 0에서 제일 적은 비용인 2와 연결합니다.</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/66029451-0e44-45ad-ac0c-6fd94592ab44/prim1.png" alt="">
<img src="https://images.velog.io/images/paul_kang/post/2613daea-a8d4-415e-9d6e-f5afee65715e/prim2.png" alt=""></p>
<h4 id="2-0-2번-정점에서-최소비용-간선-선택합니다-제일-작은-21-간선">2. 0, 2번 정점에서 최소비용 간선 선택합니다. (제일 작은 21 간선)</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/e1a5e4d1-ad97-4364-8f59-303b4762e48b/prim3.png" alt=""></p>
<h4 id="3-계속-진행-32-간선-선택-x">3. 계속 진행 (32 간선 선택 x)</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/77b533f4-11ab-4b75-9ad7-578dd8102a6a/prim4.png" alt="">
<img src="https://images.velog.io/images/paul_kang/post/d9c31944-a260-4a6a-beb1-b5767363ba3d/prim5.png" alt=""></p>
<h4 id="4-n개의-정점이-다-선택되었으면-끝">4. N개의 정점이 다 선택되었으면 끝.</h4>
<p><img src="https://images.velog.io/images/paul_kang/post/3906a747-14bc-4b88-b2e6-3b5cb093526e/prim6.png" alt="">
<img src="https://images.velog.io/images/paul_kang/post/1a62f256-6f54-4804-9958-c095836b3412/prim7.png" alt=""></p>
<h3 id="java로-구현-1">Java로 구현</h3>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

// 임의의 정점 (시작점)을 시작으로 최소비용의 간선을 선택하고 -&gt; 최소비용 정점이 결정 됨 -&gt; mst에 포함
public class MSTPrimTest {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());
        int[][] adjMatrix = new int[N][N];
        boolean[] visited = new boolean[N]; // 해당 정점의 방문 여부체크. 해당 정점이 mst에 포함되었는지 여부 체크
        int[] minEdge = new int[N]; // 각 정점별 다른 정점과의 연결 간선 비용 중 최소값.
        // 한 정점과 연결된 다른 간선들의 비용 중 최소값

        StringTokenizer st = null;
        for (int i = 0; i &lt; N; i++) {
            st = new StringTokenizer(br.readLine(), &quot; &quot;);
            for (int j = 0; j &lt; N; j++) {
                // 정점 i -&gt; j로 가는 간선이 존재하고 그 때 가중치가 adjMatrix[i][j]
                adjMatrix[i][j] = Integer.parseInt(st.nextToken());
            }
            minEdge[i] = Integer.MAX_VALUE;
        }

        int result = 0; // 최소신장트리 비용
        minEdge[0] = 0; // 임의의 시작점 0의 간선비용을 0으로 세팅

        for (int i = 0; i &lt; N; i++) { // 모든 정점 수만큼 반복
            // 1. 신장트리에 포함되지 않은 정점 중 최소간선비용의 정점 찾기
            int minCost = Integer.MAX_VALUE;
            int minVertex = -1; // 최소간선비용의 정점번호

            // 1. 현재 모든 정점 중에서 가장 작은 간선 비용을 갖는 정점을 찾고 그 때의 비용은 얼마인지 찾음
            for (int j = 0; j &lt; N; j++) {
                // !visited[j]: 정점 i가 mst에 포함이 안됐고
                if (!visited[j] &amp;&amp; minCost &gt; minEdge[j]) {
                    minCost = minEdge[j]; // 최소 간선 비용
                    minVertex = j; // 최소 간선 비용을 가지는 정점의 번호
                }
            }

            visited[minVertex] = true; // 신장트리에 포함시킴
            result += minCost; // 간선비용 누적

            // 2. 선택된 정점 기준으로 신장트리에 연결되지 않은 타 정점과의 간선 비용 최소로 업데이트
            // 새로 찾은 정점(minVertex)과 연결된 다른 정점들과의 최소 간선 비용을 업데이트
            for (int j = 0; j &lt; N; j++) {

                // adjMatrix[minVertex][j] != 0 : 0이 아니라는 것은 인접해야 한다는 뜻
                if (!visited[j] &amp;&amp; adjMatrix[minVertex][j] != 0 &amp;&amp; minEdge[j] &gt; adjMatrix[minVertex][j]) {
                    minEdge[j] = adjMatrix[minVertex][j];
                }

            }
        }

        System.out.println(result);
    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SWEA 5432] 쇠막대기 자르기]]></title>
            <link>https://velog.io/@paul_kang/SWEA-5432-%EC%87%A0%EB%A7%89%EB%8C%80%EA%B8%B0-%EC%9E%90%EB%A5%B4%EA%B8%B0-qpvywll3</link>
            <guid>https://velog.io/@paul_kang/SWEA-5432-%EC%87%A0%EB%A7%89%EB%8C%80%EA%B8%B0-%EC%9E%90%EB%A5%B4%EA%B8%B0-qpvywll3</guid>
            <pubDate>Sun, 12 Sep 2021 06:52:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/paul_kang/post/a830d3f5-fe5b-41ee-adcd-8a736fe2d77b/%EC%87%A0%EB%A7%89%EB%8C%80%EA%B8%B0-%EC%9E%90%EB%A5%B4%EA%B8%B0.png" alt=""></p>
<h2 id="💡-learned">💡 Learned</h2>
<p><code>count</code>: 조각난 개수
<code>numOfBars</code>: 레이저가 자르는 그 시점의 막대기의 개수 </p>
<ol>
<li><strong>&#39;(&#39;</strong> 만나면 <strong>numOfBars++</strong></li>
<li><strong>&#39;)&#39;</strong> 만나면 막대기의 끝을 의미 =&gt; <strong>count++</strong> and <strong>numOfBars--</strong></li>
<li><strong>&#39;*&#39;</strong> 레이저 만나면 막대기 수 만큼 count 증가 (예를들어 막대기가 3개면 <strong>count+=3</strong>)</li>
</ol>
<ul>
<li>레이저를 <strong>&#39;*&#39;</strong> 로 치환하는 작업이 핵심이 아닐까 싶습니다. <strong>&#39;(&#39;</strong> 와 <strong>&#39;)&#39;</strong>로 이루어졌기 때문에 레이저를 구분한 것이 문제를 분석하기 쉽게 만들어 줍니다.</li>
<li>위의 3가지 특이점을 발견하는 것이 쉬운 일은 아니지만 규칙성을 찾으려고 노력해야 합니다.</li>
</ul>
<h2 id="📑-submitted">📑 Submitted</h2>
<h3 id="java">(JAVA)</h3>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
// 쇠막대기 자르기
public class SWEA5432 {

    public static void main(String[] args) throws Exception {
        System.setIn(new FileInputStream(&quot;res/sample_input2.txt&quot;));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb;
        int T = Integer.parseInt(br.readLine());
        int numOfLines = 0;
        for (int test_case = 1; test_case &lt;= T; test_case++) {
            sb = new StringBuilder();
            int cnt = 0; // 조각난 개수
            String str = br.readLine();
            // &#39;()&#39; 레이저를 &#39;@&#39;으로 치환
            str = str.replace(&quot;()&quot;, &quot;@&quot;);
            char[] temp = str.toCharArray();

            for (char c : temp) {
                switch (c) {
                case &#39;(&#39;:
                    numOfLines++;
                    break;
                case &#39;)&#39;:
                    cnt += 1; // 라인 하나 끝나면 +1
                    numOfLines--;
                    break;
                case &#39;@&#39;:
                    cnt += numOfLines; // 레이저 만나면 라인 수 만큼 증가
                    break;
                }
            }

            sb.append(&quot;#&quot; + test_case);
            sb.append(&quot; &quot;);
            sb.append(cnt);
            System.out.println(sb.toString());
        }

    }

}</code></pre>
<h3 id="js">(JS)</h3>
<pre><code class="language-javascript">    function solution(s) {
      let answer = 0; // 카운트
      let numOfBars = 0; // 막대기 수
      s = s.replaceAll(&#39;()&#39;, &#39;*&#39;); // 레이저 치환

      for (const x of s) {
        switch (x) {
          case &#39;(&#39;:
            numOfBars++;
            break;
          case &#39;)&#39;:
            answer += 1; // 막대기 하나 끝나면 +1
            numOfBars--;
            break;
          case &#39;*&#39;:
            answer += numOfBars; // 레이저 만나면 막대기 수 만큼 증가
            break;
        }
      }
      console.log(answer);
    }
    let str = &quot;(((()(()()))(())()))(()())&quot;;
    solution(str);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[정리] HTML]]></title>
            <link>https://velog.io/@paul_kang/%EC%A0%95%EB%A6%AC-HTML-CSS-JS-ecpknxbw</link>
            <guid>https://velog.io/@paul_kang/%EC%A0%95%EB%A6%AC-HTML-CSS-JS-ecpknxbw</guid>
            <pubDate>Sun, 05 Sep 2021 03:05:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="💡-목표">💡 목표</h3>
</blockquote>
<ul>
<li>그동안 알고 있던 프론트엔드 지식을 총망라해서 정리하고자 합니다.</li>
<li>향후에 계속해서 업데이트하면서 좋은 정리글을 만들고자 합니다.</li>
</ul>
<blockquote>
<h4 id="🤔-동기">🤔 동기</h4>
<p>SSAFY 프론트엔드 시험을 앞두고 정리하면서 공부를 하려고 합니다.</p>
</blockquote>
<hr>
<h1 id="html">HTML</h1>
<p>😊 일단 기본 틀을 봅시다
<img src="https://images.velog.io/images/paul_kang/post/42bd4995-939b-4496-8db5-9a1e7ad71126/html_temp.png" alt=""></p>
<ul>
<li>meta 태그 <strong>검색 최적화</strong>에 매우 중요합니다</li>
<li>페이지마다 신경써서 작성해주면 좋습니다</li>
</ul>
<h2 id="📃-시맨틱-html">📃 시맨틱 HTML</h2>
<blockquote>
<p>시맨틱 태그를 사용해야하는 이유
    1. 검색을 최적화 하기 위해서 시맨틱한 태그를 사용합니다. 
    2. 웹 접근성 때문에 -&gt; 스크린리더나 키보드만을 이용해 웹사이트를 이용하는 경우가 있기 때문입니다.
    3. 개발자를 위해서 -&gt; 유지보수성을 높일 수 있습니다.
<img src="https://images.velog.io/images/paul_kang/post/fbb34578-8039-40ad-8753-9ad7a651b19f/semantic.png" alt=""></p>
</blockquote>
<pre><code class="language-html">&lt;section&gt;&lt;/section&gt;
&lt;aside&gt;&lt;/aside&gt;
&lt;nav&gt;&lt;/nav&gt;
&lt;article&gt;&lt;/article&gt;</code></pre>
<ul>
<li><code>sectioning tag</code>를 잘 사용하여 시맨틱한 HTML을 작성해 봅시다.</li>
<li><code>sectioning tag</code> 즉, 위 태그들을 사용할 때 반드시 그 안에 heading tag를 작성해야 합니다.<pre><code class="language-html">  &lt;section&gt;
            &lt;h1&gt;메뉴&lt;/h1&gt;
            ...
  &lt;/section&gt;</code></pre>
</li>
<li>html에서 단 한개의 main만 사용할 수 있습니다. (main은 sectioning tag가 아닙니다. 고로, 반드시 heading tag를 넣을 필욘 없습니다.)</li>
<li>html에서 논리적인 완결성이 있는 것은 div 보단 section으로 묶습니다. 예를 들면 트위터의 tweet form</li>
<li>컨텐츠로서 완결성이 있는 경우 section 보다는 article 블로그나 뉴스 기사 처럼</li>
<li>aside : section이랑 비슷합니다. 논리적인 완결이 되었지만, 본문이랑은 직접적인 관련성이 없는 경우, 위젯이라던가 배너광고라던가 이런 뉘앙스를 살릴 때 사용합니다.</li>
</ul>
<h2 id="각종-태그들-알아봅시다">각종 태그들 알아봅시다!</h2>
<h3 id="🤔-여기서-잠깐">🤔 여기서 잠깐,</h3>
<h4 id="block과-inline-태그-구분하고-넘어갑시다">block과 inline 태그 구분하고 넘어갑시다</h4>
<p>📌 자주쓰는 것들 위주로 정리했습니다.</p>
<blockquote>
<p>block 태그 &gt; <code>div</code> <code>p</code> <code>form</code> <code>h1~h6</code>, 리스트 태그들(<code>dl</code> <code>ol</code> <code>ul</code> <code>dt</code> <code>dd</code> <code>li</code>)</p>
</blockquote>
<ol>
<li>기본값이 block인 태그들입니다.</li>
<li>width, height값을 통해 크기를 지정할 수 있습니다.</li>
<li>별도의 값을 주지않을 경우 width는 한줄 전체를 사용합니다.</li>
</ol>
<blockquote>
<p>inline 태그 &gt; <code>img</code> <code>a</code> <code>span</code> <code>input</code> <code>button</code> <code>br</code></p>
</blockquote>
<ol>
<li>기본값이 inline인 태그들입니다.</li>
<li>별도의 width, height 값을 설정해도 무시합니다.</li>
<li>태그 내부에 내용만큼, 즉 필요한 최소 길이만큼만 공간을 차지합니다.</li>
<li>inline 요소들은 생성시 줄바꿈없이 이전 요소 바로 뒤에서 가로로 배치됩니다.</li>
</ol>
<h3 id="기본적인-태그들">기본적인 태그들</h3>
<p><img src="https://images.velog.io/images/paul_kang/post/b128efd7-3dc7-464e-8dc0-bc0434078857/basic_tag.png" alt=""></p>
<ul>
<li><code>em</code>태그와 <code>i</code>태그는 화면상으로는 똑같지만 스크린리더로 읽는다면? <code>i</code>태그는 평범하게 읽히겠지만, <code>em</code>태그는 강조되서 읽힙니다.</li>
<li><code>b</code>와 <code>strong</code> 태그도 마찬가지! (<code>b</code>는 시각적으로만, <code>strong</code>은 정말 중요한 강조하는 내용이 있을 때 사용합니다.)</li>
</ul>
<p><img src="https://images.velog.io/images/paul_kang/post/b5feef3a-c7f7-436b-8de4-e7f1f37cf72c/basic_tag2.png" alt=""></p>
<ul>
<li>img 태그에 alt값 쓸말 없어도 &quot;&quot; 빈 문자열로 처리해줍니다.</li>
</ul>
<p><img src="https://images.velog.io/images/paul_kang/post/a91d74ed-acd4-4c27-9817-e0b148b06ec8/basic_tag3.png" alt=""></p>
<p><img src="https://images.velog.io/images/paul_kang/post/74173178-471c-4afd-8eb6-41c29cb663fa/basic_tag4.png" alt=""></p>
<ul>
<li>dl의 자식요소는 오직 <code>div</code>, <code>dt</code>, <code>dd</code>만 가능합니다.</li>
<li><code>dt</code>와 <code>dd</code>는 반드시 <code>dl</code>의 자식요소로 존재해야 합니다.</li>
</ul>
<h3 id="💻-form-태그">💻 form 태그</h3>
<p><img src="https://images.velog.io/images/paul_kang/post/1164b927-9735-4b55-a2ab-2930556a67ff/form.png" alt=""></p>
<pre><code class="language-html">&lt;form action=&quot;API주소&quot; method=&quot;GET | POST&quot;&gt;&lt;/form&gt;</code></pre>
<ul>
<li>label의 for 속성과 input의 id 속성을 매칭 시켜줍니다.</li>
<li>form의 메소드는 두가지 방식이 있습니다.
<em>GET</em> : 눈에 보인다. URL 쿼리스트링에 포함되어 전송됨. 길이제한이 있다.
<em>POST</em> : 눈에 보이지 않는다. 헤더에 포함되어 전송됨. 길이제한이 없다.</li>
</ul>
<h4 id="🔴-radio--checkbox--select-🔴">🔴 Radio &amp; Checkbox &amp; Select 🔴</h4>
<p>📌 <strong>Radio</strong>
<img src="https://images.velog.io/images/paul_kang/post/494ce244-5b4f-4afc-ad47-c839a8d8c623/radio&checkbox.png" alt=""></p>
<p>📌 <strong>Checkbox</strong>
<img src="https://images.velog.io/images/paul_kang/post/06d5ef90-ae8c-48c5-990a-fbcd920e2425/checkbox.png" alt=""></p>
<p>📌 <strong>Select</strong>
<img src="https://images.velog.io/images/paul_kang/post/20879aa6-d7d6-4d84-aeaa-e7e3657b9aec/select.png" alt=""></p>
<h3 id="audio--video--iframe-등-미디어-태그">audio &amp; video &amp; iframe 등 미디어 태그!</h3>
<p><img src="https://images.velog.io/images/paul_kang/post/f6300023-4627-41ff-861f-89f9880a4351/etc.png" alt=""></p>
<h3 id="table-태그">table 태그!</h3>
<p><img src="https://images.velog.io/images/paul_kang/post/e939bcd2-4a4e-4c13-b559-3c93662ceb38/table.png" alt=""></p>
<ul>
<li>최신에는 직접 table 태그를 사용하기보다 flexbox나 grid같은 css 속성을 이용합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ 3109] 빵집]]></title>
            <link>https://velog.io/@paul_kang/BOJ-3109-%EB%B9%B5%EC%A7%91</link>
            <guid>https://velog.io/@paul_kang/BOJ-3109-%EB%B9%B5%EC%A7%91</guid>
            <pubDate>Mon, 30 Aug 2021 02:23:47 GMT</pubDate>
            <description><![CDATA[<h2 id="💡-learned">💡 Learned</h2>
<ul>
<li>파이프라인의 최대 개수를 구하려면 맨 윗 행부터 돌려야 최대값을 구할 수 있습니다.</li>
<li>dfs</li>
</ul>
<h2 id="📑-submitted">📑 Submitted</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    static int R, C;
    static char[][] map;
    static int[] dir = { -1, 0, 1 }; // 우상, 우, 우하 // y값은 기존값에 +1만 해주면 됨.
    static int answer; // 답. 완성된 파이프라인 수

    public static void main(String[] args) throws Exception {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        R = Integer.parseInt(st.nextToken()); // 행
        C = Integer.parseInt(st.nextToken()); // 열

        map = new char[R][C];
        for (int i = 0; i &lt; R; i++) {
            map[i] = br.readLine().toCharArray();
        } // 입력

        // 맨윗줄 첫번째 열에서부터 검사(0,0 -&gt; 1,0 -&gt; 2,0)
        // 중요: 맨 윗 행부터 돌아야 최대값을 구할 수 있음
        for (int i = 0; i &lt; R; i++) {
            connect(i, 0); // 행값, 열값
        }

        System.out.println(answer);
    }

    // (x,y) 위치에서부터 출발해서 3방향을 탐색후 끝열에 도착 가능한지 여부를 알려주는 메소드
    private static boolean connect(int x, int y) {

        // 1. 종료조건: 끝열에 도착한 경우. 파이프라인 한개 완성
        if (y == C - 1) {
            answer++;
            return true;
        }

        // 2. 반복조건: 끝에 아직 도달을 안해서 3방향을 체크 하면서 나가는 작업
        // 3방향 중에서 한방향이라도 성공을 하면 다른 방향은 시도하면 안됨
        for (int d = 0; d &lt; 3; d++) { // 우상 --&gt; 우 --&gt; 우하
            int nx = x + dir[d];
            int ny = y + 1;

            if (nx &gt;= 0 &amp;&amp; ny &gt;= 0 &amp;&amp; nx &lt; R &amp;&amp; ny &lt; C &amp;&amp; map[nx][ny] != &#39;x&#39;) {
                map[nx][ny] = &#39;x&#39;; // 여기 지나갔습니다. 마킹!
                boolean result = connect(nx, ny);
                if (result) {
                    return true;
                }
            }
        } // for문 끝
            // 3방향 모두 끝까지 도달못하면
        return false;

    }

}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ 2493] 탑]]></title>
            <link>https://velog.io/@paul_kang/BOJ-2493-%ED%83%91</link>
            <guid>https://velog.io/@paul_kang/BOJ-2493-%ED%83%91</guid>
            <pubDate>Sun, 29 Aug 2021 14:35:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/paul_kang/post/3ae7baff-5a3e-44fb-b4e3-9658d2d64486/BOJ_Logo.png" alt=""></p>
<h2 id="💡-learned">💡 Learned</h2>
<ul>
<li>왼쪽 옆에 있는 탑이 현재 탑의 높이보다 작으면, 앞으로도 어떠한 신호도 수신할 수 없으므로 스택에서 pop 합니다.</li>
<li>현재 탑의 높이보다 크면 뒤에 오는 탑들의 신호도 수신 가능하므로 peek 합니다. (스택에 계속 남아있습니다.)</li>
</ul>
<h2 id="📑-submitted">📑 Submitted</h2>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws NumberFormatException, IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());

        Stack&lt;int[]&gt; top = new Stack&lt;&gt;();
        StringTokenizer st = new StringTokenizer(br.readLine());

        for (int i = 0; i &lt; N; i++) {
            int height = Integer.parseInt(st.nextToken());

            while (!top.isEmpty()) {
                if (top.peek()[0] &lt; height) // 왼쪽 옆에 있는 탑이 현재 탑의 높이보다 작으면, 어떠한 신호도 수신할 수 없으므로 pop!
                    top.pop();
                else { // 현재 탑의 높이보다 크다면, 해당 탑의 번호를 출력한다.
                    System.out.print(top.peek()[1] + &quot; &quot;);
                    break;
                }
            }

            if (top.empty()) // 탑이 비었다면 0을 출력한다.
                System.out.print(&quot;0 &quot;);
            top.push(new int[] { height, i + 1 }); // 탑의 높이와 탑의 인덱스 순
        }

    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SWEA 1208] Flatten]]></title>
            <link>https://velog.io/@paul_kang/SWEA-1208-Flatten</link>
            <guid>https://velog.io/@paul_kang/SWEA-1208-Flatten</guid>
            <pubDate>Sun, 29 Aug 2021 13:08:15 GMT</pubDate>
            <description><![CDATA[<h2 id="💡-learned">💡 Learned</h2>
<p>평탄화 작업을 할 때 정렬을 먼저 하면 배열의 마지막 요소와 처음 요소를 이용해 쉽게 접근이 가능했습니다.</p>
<h2 id="📑-submitted">📑 Submitted</h2>
<pre><code class="language-java">import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.Scanner;

public class Solution {

    public static void main(String[] args) throws Exception {

        Scanner sc = new Scanner(System.in);

        for (int tc = 1; tc &lt;= 10; tc++) {
            int cnt = sc.nextInt(); // 덤프횟수 1~1000

            int[] nums = new int[100]; // 가로 방향은 100
            for (int i = 0; i &lt; nums.length; i++) {
                nums[i] = sc.nextInt();
            }

            // 평탄화 작업
            while (true) {
                Arrays.sort(nums); // 정렬하기

                int high = nums[99] - nums[0]; // 최고점과 최저점의 차이

                if (high &lt;= 1 || cnt &lt;= 0) { // 평탄화 완료 혹은 덤프횟수를 다 쓴 경우
                    System.out.println(&quot;#&quot; + tc + &quot; &quot; + high);
                    break;
                }
                // 평탄화 완료가 아직이고 or 덤프횟수가 남은 경우
                cnt--;
                nums[99]--;
                nums[0]++;
            }
        }
        sc.close();
    }
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>