<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sw-jang12.log</title>
        <link>https://velog.io/</link>
        <description>HiHeLlo!1</description>
        <lastBuildDate>Mon, 15 Jul 2024 05:26:58 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sw-jang12.log</title>
            <url>https://velog.velcdn.com/images/sw-jang12/profile/2efc337d-5141-4790-97a5-d0386c9e3755/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sw-jang12.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sw-jang12" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[tailwind, 슬라이드업, 슬라이드다운 스타일 적용-1]]></title>
            <link>https://velog.io/@sw-jang12/tailwind-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%93%9C%EC%97%85-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%93%9C%EB%8B%A4%EC%9A%B4-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%A0%81%EC%9A%A9-1</link>
            <guid>https://velog.io/@sw-jang12/tailwind-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%93%9C%EC%97%85-%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%93%9C%EB%8B%A4%EC%9A%B4-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%A0%81%EC%9A%A9-1</guid>
            <pubDate>Mon, 15 Jul 2024 05:26:58 GMT</pubDate>
            <description><![CDATA[<p>카카오맵을 이용해 마커를 클릭하면 정보가 나오는 커스텀 오버레이를 작업하고 있었다. 스타일은 tailwind를 사용했다.
<img src="https://velog.velcdn.com/images/sw-jang12/post/6d1abcce-c863-4d84-b679-d838b56294d9/image.gif" alt=""></p>
<p>근데 그냥 껐다 켰다 하는 느낌이라 좀 더 부드러운 효과를 주기 위해서
클릭하면 아래에서 위로, 닫으면 원래 위치에서 아래로 내려가는 부드러운 효과를 주려고 했다.</p>
<p>원래 코드
StoreBox.tsx 컴포넌트</p>
<pre><code class="language-tsx">export default function StoreBox({ store, setStore }: StoreBoxProps) {

  return (
    &lt;div className=&quot;fixed inset-x-0 mx-auto bottom-20 rounded-lg z-10 max-w-sm md:max-w-xl w-full bg-white shadow-xl&quot;&gt;
      {store &amp;&amp; (
        &lt;&gt;
          &lt;div className=&quot;px-8 py-6&quot;&gt;
            &lt;div className=&quot;flex justify-between items-center&quot;&gt;
              &lt;div className=&quot;flex gap-4 items-center&quot;&gt;
                ...
              &lt;/div&gt;
              &lt;button type=&quot;button&quot; onClick={() =&gt; setStore(null)}&gt;
                &lt;AiOutlineClose /&gt;
              &lt;/button&gt;
            &lt;/div&gt;
            ...
          &lt;/div&gt;
          &lt;button&gt;
            상세보기 버튼
          &lt;/button&gt;
        &lt;/&gt;
      )}
    &lt;/div&gt;
  );
}</code></pre>
<p>처음에는 tailwind의 @apply 를 사용해 애니메이션을 적용하려고 했다.</p>
<h1 id="문제상황-1">문제상황 1</h1>
<p>슬라이드업 스타일을 주기 위해, 아래처럼 global.css에 apply 디렉티브를 사용해 slide-up 클래스를 만들어서 애니메이션 스타일을 주려고 했었다.</p>
<pre><code class="language-css">.slide-up {
  @apply animation: slide-up 0.5s ease-out forwards;
}


@keyframes slide-up {
  0% {
    transform: translateY(100%);
  }
  100% {
    transform: translateY(0);
  }
}</code></pre>
<p>여기서 에러가 발생했다.</p>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/de9afab8-337c-4869-88a9-1f7b911d6754/image.png" alt=""></p>
<p>에러메세지는 &#39;animaion:&#39; 클래스가 존재하지 않는다는 내용이다.
그리고 커스텀 클래스라면 @layer 디렉티브로 정의하라고 한다.</p>
<h2 id="문제원인">문제원인</h2>
<p>tailwind에서 유틸리티 클래스를 커스텀하는 방법은 여러 가지가 있다.</p>
<p>config.js 파일에서 설정하는 방법, 
@layer로 특정 계층에서 tailwind의 기본 유틸리티 클래스를 확장하거나 새로운 유틸리티 클래스를 추가하는 방법, 
@apply를 사용해 여러 tailwind 유틸리티 클래스를 조합하여 새로운 클래스를 정의하는 방법, 
아니면 CSS 파일에서 기본 CSS 문법을 사용해 클래스 스타일을 정의해서 바로 사용하는 방법 등등 여러 가지 방법이 있다.</p>
<p>위 방법 중에서 @apply를 사용할 때는 tailwind의 기존 유틸리티 클래스를 조합하여 새로운 유틸리티 클래스를 정의한다. 그래서 처음 봤던 코드처럼 @apply를 사용하려면, tailwind에서 제공하는 기존 유틸리티 클래스를 사용해서 새로운 클래스를 정의해야 한다. 그렇지 않으면 에러가 발생한다.</p>
<p>처음 코드의 경우도 &#39;animation:&#39; 은 tailwind가 제공하는 클래스가 아니기 때문에 클래스가 존재하지 않는다는 에러가 발생한 것이다. </p>
<h3 id="해결방법-선택">해결방법 선택</h3>
<p>위에서 본 것처럼 여러 가지 방법으로 유틸리티 클래스를 커스텀 할 수 있지만, 각각의 방법을 사용하는 목적에는 약간의 차이가 있다.</p>
<h4 id="configjs-파일-설정">config.js 파일 설정</h4>
<ul>
<li>전체 프로젝트에서 일관되게 사용할 커스텀 유틸리티 클래스를 정의한다<pre><code class="language-js">// tailwind.config.js
module.exports = {
theme: {
  extend: {
    colors: {
      brand: {
        light: &#39;#3fbaeb&#39;,
        DEFAULT: &#39;#0fa9e6&#39;,
        dark: &#39;#0c87b8&#39;,
      },
    },
    spacing: {
      &#39;72&#39;: &#39;18rem&#39;,
      &#39;84&#39;: &#39;21rem&#39;,
      &#39;96&#39;: &#39;24rem&#39;,
    },
    fontFamily: {
      sans: [&#39;Graphik&#39;, &#39;sans-serif&#39;],
      serif: [&#39;Merriweather&#39;, &#39;serif&#39;],
    },
  },
},
plugins: [],
};
</code></pre>
</li>
</ul>
<pre><code>

```html
&lt;body class=&quot;font-sans&quot;&gt;

  &lt;div class=&quot;bg-brand p-72 text-white&quot;&gt;
    &lt;h1 class=&quot;text-4xl font-bold&quot;&gt;Hello, Tailwind!&lt;/h1&gt;
    &lt;p class=&quot;mt-4&quot;&gt;This is a paragraph with custom spacing and colors.&lt;/p&gt;
  &lt;/div&gt;

  &lt;div class=&quot;bg-brand-light p-84 text-brand-dark&quot;&gt;
    &lt;h2 class=&quot;text-3xl font-serif&quot;&gt;Custom Font and Colors&lt;/h2&gt;
    &lt;p class=&quot;mt-4&quot;&gt;Using custom theme settings from tailwind.config.js&lt;/p&gt;
  &lt;/div&gt;

&lt;/body&gt;</code></pre><h4 id="apply">@apply</h4>
<ul>
<li>tailwind 유틸리티 클래스들을 조합하여 새로운 유틸리티 클래스를 정의한다. 여러 곳에서 여러 개의 유틸리티 클래스를 반복해서 사용하는 경우, 이를 모아 새로운 클래스로 정의하여 중복을 줄이고 가독성을 높일 수 있다.<pre><code class="language-css">.navbar {
@apply flex justify-between items-center fixed w-full h-[52px] top-0 px-[18px] shadow-sm bg-white;
}</code></pre>
</li>
</ul>
<pre><code class="language-html">&lt;div class=&quot;navbar&quot;&gt;
  navbar
&lt;/div&gt;</code></pre>
<h4 id="layer">@layer</h4>
<ul>
<li>tailwind의 기본 유틸리티 클래스에 사용자 정의 스타일을 추가하거나 기존 스타일을 확장한다. 특정 컴포넌트나 페이지에 한정된 스타일을 작성할 때 유용하다. @apply와 함께 사용할 수도 있고, @layer만 단독으로 사용할 수도 있다.<pre><code class="language-css">/* @layer, @apply 함꼐 사용 */
@layer components {
.btn-primary {
  @apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
}
}
</code></pre>
</li>
</ul>
<p>/* @layer 단독 사용 */
@layer components {
  .btn-primary {
    background-color: #1da1f2;
    color: white;
    font-weight: bold;
    padding: 0.5rem 1rem;
    border-radius: 0.25rem;
  }
}</p>
<pre><code>
#### 기본 css 문법
- css는 html 요소에 적용된 클래스 이름이 tailwind css 클래스든, 사용자 정의 css 클래스든 동일한 방식으로 처리하기 때문에 사용할 수 있다.
- tailwind 유틸리티 클래스를 사용하지 않고 기본 css 문법만으로 작성하는 방법이다. 
- 간편하다는 장점이 있지만 일관성이 떨어져서 유지보수에 어려움이 있을 수 있다.


현재 적용하려고 하는 스타일 즉, 애니메이션은 현재 페이지에서 사용하는 커스텀 오버레이 외에는 사용할 일이 없고, tailwind를 사용하는 프로젝트의 일관성을 유지하기 위해 @layer 방식을 사용하기로 결정했다.


## 코드 적용
우선 아래 코드를 css 파일에 추가했다.
```css
@layer components {
  .slide-down {
    animation: slide-down 0.5s ease-out forwards;
  }
  .slide-up {
    animation: slide-up 0.5s ease-out forwards;
  }
  @keyframes slide-up {
    0% {
      transform: translateY(100%);
      opacity: 0;
    }
    100% {
      transform: translateY(0);
      opacity: 1;
    }
  }
  @keyframes slide-down {
    0% {
      transform: translateY(0);
      opacity: 1;
    }
    100% {
      transform: translateY(100%);
      opacity: 0;
    }
  }
}
</code></pre><p>그리고 동적으로 스타일을 적용하기 위해 백틱으로 className을 정의하는 방식으로 slideup을 구현했다.</p>
<pre><code class="language-tsx">export default function StoreBox({ store, setStore }: StoreBoxProps) {
  const handleClick = () =&gt; {
      setStore(null);
  }

  return (
    &lt;div className={`fixed inset-x-0 mx-auto bottom-20 rounded-lg z-10 max-w-sm md:max-w-xl w-full bg-white shadow-xl ${store ? &quot;slide-up&quot; : null}`}&gt;
      {store &amp;&amp; (
        &lt;&gt;
          &lt;div className=&quot;px-8 py-6&quot;&gt;
            &lt;div className=&quot;flex justify-between items-center&quot;&gt;
              &lt;div className=&quot;flex gap-4 items-center&quot;&gt;
                ...
              &lt;/div&gt;
              &lt;button type=&quot;button&quot; onClick={handleClick}&gt;
                &lt;AiOutlineClose /&gt;
              &lt;/button&gt;
            &lt;/div&gt;
            ...
          &lt;/div&gt;
          &lt;button&gt;
            상세보기 버튼
          &lt;/button&gt;
        &lt;/&gt;
      )}
    &lt;/div&gt;
  );
}</code></pre>
<p>store의 값이 있으면 true로 slide-up 클래스가 붙고 store의 값이 null이 되면 false로 null이 될 수 있게 했다.</p>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/61468074-c069-48ea-8fb8-c598c688d742/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 LV1, 실패율 풀이(JavaScript, 카운팅 배열)]]></title>
            <link>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EC%8B%A4%ED%8C%A8%EC%9C%A8-%ED%92%80%EC%9D%B4JavaScript-%EC%B9%B4%EC%9A%B4%ED%8C%85-%EB%B0%B0%EC%97%B4</link>
            <guid>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EC%8B%A4%ED%8C%A8%EC%9C%A8-%ED%92%80%EC%9D%B4JavaScript-%EC%B9%B4%EC%9A%B4%ED%8C%85-%EB%B0%B0%EC%97%B4</guid>
            <pubDate>Wed, 19 Jun 2024 06:23:05 GMT</pubDate>
            <description><![CDATA[<p>문제 링크
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/42889">https://school.programmers.co.kr/learn/courses/30/lessons/42889</a></p>
<h1 id="프로그래머스-실패율">프로그래머스, 실패율</h1>
<h2 id="문제설명">문제설명</h2>
<p>슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스테이지 차이가 너무 큰 것이 문제였다.</p>
<p>이 문제를 어떻게 할까 고민 한 그녀는 동적으로 게임 시간을 늘려서 난이도를 조절하기로 했다. 역시 슈퍼 개발자라 대부분의 로직은 쉽게 구현했지만, 실패율을 구하는 부분에서 위기에 빠지고 말았다. 오렐리를 위해 실패율을 구하는 코드를 완성하라.</p>
<p>실패율은 다음과 같이 정의한다.
스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수
전체 스테이지의 개수 N, 게임을 이용하는 사용자가 현재 멈춰있는 스테이지의 번호가 담긴 배열 stages가 매개변수로 주어질 때, 실패율이 높은 스테이지부터 내림차순으로 스테이지의 번호가 담겨있는 배열을 return 하도록 solution 함수를 완성하라.</p>
<h2 id="제한사항">제한사항</h2>
<p>스테이지의 개수 N은 1 이상 500 이하의 자연수이다.
stages의 길이는 1 이상 200,000 이하이다.
stages에는 1 이상 N + 1 이하의 자연수가 담겨있다.
각 자연수는 사용자가 현재 도전 중인 스테이지의 번호를 나타낸다.
단, N + 1 은 마지막 스테이지(N 번째 스테이지) 까지 클리어 한 사용자를 나타낸다.
만약 실패율이 같은 스테이지가 있다면 작은 번호의 스테이지가 먼저 오도록 하면 된다.
스테이지에 도달한 유저가 없는 경우 해당 스테이지의 실패율은 0 으로 정의한다.</p>
<h2 id="입출력-예">입출력 예</h2>
<table>
  <tr>
    <th>N</th>
    <th>stages</th>
    <th>result</th>
  </tr>
  <tr>
    <td>5</td>
    <td>[2, 1, 2, 6, 2, 4, 3, 3]</td>
    <td>[3,4,2,1,5]</td>
  </tr>
  <tr>
    <td>4</td>
    <td>[4,4,4,4,4]</td>
    <td>[4,1,2,3]</td>
  </tr>
</table>


<h1 id="처음-코드">처음 코드</h1>
<p>처음에는 단순하게 while 문을 돌면서 filter 메서드를 사용해 &#39;스테이지에 도달한 플레이어 수&#39;와 &#39;클리어하지 못한 플레이어 수&#39;를 구해서 실패율을 계산한 뒤 이 결과를 배열에 넣어서 정렬하는 방식으로 풀었었다.</p>
<pre><code class="language-js">function solution(N, stages) {
  let arr = [];
  let currentStage = 1; // 현재 실패율 계산중인 스테이지 추적

  while(currentStage &lt;= N) {
    let failureRate;

    // 현재 스테이지에 도달한 플레이어 배열
    const stageReachedPlayers = stages.filter(stage =&gt; stage &gt;= currentStage);
    if(stageReachedPlayers.length !== 0) {
      const unclearedPlayers = stageReachedPlayers.filter(stage =&gt; stage === currentStage);
      failureRate = unclearedPlayers.length / stageReachedPlayers.length;
    } else {
      failureRate = 0;
    }

    arr[currnetStage - 1] = failureRate;
    currentStage++;
  }
  let result = arr.map((v, i) =&gt; ({v, i: i + 1})).sort((a,b) =&gt; b.v - a.v).map(obj =&gt; obj.i); 
  return result;
}
</code></pre>
<img src="https://velog.velcdn.com/images/sw-jang12/post/547e3697-981f-4d72-961c-4497caf40136/image.png" width="300" height="200">

<h2 id="코드-개선카운팅-배열-사용">코드 개선(카운팅 배열 사용)</h2>
<p>이제 위 코드를 개선해보자.
우선 처음 코드에서는 currentStage로 현재 실패율을 계산중인 스테이지를 추적하고, 각 스테이지마다 도달한 플레이어 수와 클리어하지 못한 플레이어를 반복해서 필터링했었다. 그리고 arr에 failureRate를 넣은 후 map을 통해 다시 객체로 매핑했다.</p>
<p>시간초과는 일어나지 않았지만 반복적으로 필터링하는 과정에서 시간복잡도가 상승해 연산에 많은 시간이 걸렸고, result 배열을 만드는 과정에서 2번의 매핑을 하고 있어 코드가 복잡해 보였다.</p>
<p>연산 시간을 어떻게 줄일 수 있을까?</p>
<p>카운팅 정렬의 개념을 사용하면 된다. 정확히는 카운팅 정렬을 하기위해 사용하는 카운트용 배열을 사용한다.</p>
<p>우선 문제를 분석해보면, 플레이어가 도달한 스테이지 정보는 모두 정수 형태로 stages 배열에 담긴다. 그리고 실패율을 계산하기 위해서는 &#39;<strong>스테이지에 도달한 플레이어 수</strong>&#39;와 &#39;<strong>스테이지에 도달했지만 클리어하지 못한 플레이어 수</strong>&#39;를 알아야 한다. 이때 &#39;<strong>스테이지에 도달했지만 클리어하지 못한 플레이어 수</strong>&#39;를 구하기 위해 매번 필터링 하지 않고, 카운팅 정렬에 사용되는 <strong>카운팅 배열</strong>을 사용하면 1부터 N번째 스테이지 각각에 대한 플레이어 수를 구할 수 있다. </p>
<pre><code class="language-js">let stageCounts = new Array(N + 2).fill(0);

stages.forEach(stage =&gt; {
  stageCounts[stage]++;
})</code></pre>
<p>이렇게 하면 각 스테이지에 대해 &#39;<strong>스테이지에 도달했지만 클리어하지 못한 플레이어 수</strong>&#39;를 효율적으로 구할 수 있다.</p>
<p>이제 이 배열을 이용하면 더 빠르게 문제를 해결할 수 있다.</p>
<pre><code class="language-js">function solution(N, stages) {
  let answer = [];
  let stageCounts = new Array(N + 2).fill(0);

  stages.forEach(stage =&gt; {
    stageCounts[stage]++;
  })

  let stageReachedPlayers = stages.length;
  for(let i = 1; i &lt;= N; i++) {
    if(stageReachedPlayers &gt; 0) {
      let failureRate = stageCounts[i] / stageReachedPlayers;
      answer.push({stage: i, rate: failureRate});
      stageReachedPlayers -= stageCounts[i];
    } else {
      answer.push({stage: i, rate: 0});
    }
  }
    return answer.sort((a,b) =&gt; b.rate - a.rate).map(obj =&gt; obj.stage);
}</code></pre>
<img src="https://velog.velcdn.com/images/sw-jang12/post/5a74482d-cb71-4b71-9d21-0a5b3025c1b3/image.png" width="300" height="200">













]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript, 메모이제이션(Memoization)]]></title>
            <link>https://velog.io/@sw-jang12/JavaScript-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98Memoization</link>
            <guid>https://velog.io/@sw-jang12/JavaScript-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98Memoization</guid>
            <pubDate>Sun, 14 Jan 2024 12:55:58 GMT</pubDate>
            <description><![CDATA[<p>LeetCode 2623.Memoize 문제를 풀면서 메모이제이션에 대해 공부하게 되어 그 내용을 정리하려고 합니다. </p>
<p><a target="_blank" href="https://leetcode.com/problems/memoize/description/"><strong>문제 링크</strong></a></p>
<p><a target="_blank" href="https://leetcode.com/problems/memoize/solutions/4489787/javascript/?envType=study-plan-v2&envId=30-days-of-javascript"><strong>내 풀이 링크</strong></a></p>
<h1 id="메모이제이션">메모이제이션</h1>
<blockquote>
<p>메모이제이션(memoization)
컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다.
-<a target="_blank" href="https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98">위키백과</a>-</p>
</blockquote>
<p>메모이제이션을 JavaScript를 사용해 여러가지 방식으로 작성해 봤습니다. 여기서는 총 5가지로 나눠 정리했습니다.</p>
<ol>
<li>전역 객체를 이용한 방법</li>
<li>클로저를 이용한 방법</li>
<li>Map 객체를 이용한 방법</li>
<li>모듈 패턴을 이용한 방법</li>
<li>lodash의 memoize함수를 사용한 방법</li>
</ol>
<h2 id="전역-객체를-이용한-방법">전역 객체를 이용한 방법</h2>
<p>JavaScript에서 전역 객체를 선언하여 그 객체를 사용해 결과를 저장하고 접근하는 방식입니다. </p>
<pre><code class="language-js">// 전역 객체
const memo = {};

// 계산을 수행하는 함수
function calculateFunction(input) {
  // 계산 수행
}

// 메모이제이션 함수 
function memoizedFunction(x) {
  if(!Object.hasOwn(memo, x)) {
    // 캐시된 결과가 없다면 memo객체에 계산 결과 저장
    memo[x] = calculateFunction(x);
  }

  return memo[x];
}</code></pre>
<p>이 방식은 전역 변수를 사용합니다. 그래서 전역 네임스페이스를 오염시키기 떄문에 좋은 방법은 아닙니다.</p>
<hr>
<h2 id="클로저를-이용한-방법">클로저를 이용한 방법</h2>
<p>클로저를 이용하는 방법도 위의 코드와 크게 다르지 않습니다.</p>
<pre><code class="language-js">// 계산을 수행하는 함수
function calculateFunction(input) {
  // 계산 수행
  return input + 100000;
}


// 메모이제이션을 적용할 함수를 생성하는 함수
function createMemoizedFunction(fn) {
  const cache = {};

  return function(x) {
    if(!Object.hasOwn(cache, x)) {
      cache[x] = fn(x);
    }
    return cache[x];
  }
}

const memoizedFunction = createMemoizedFunction(calculateFunction);

console.log(memoizedFunction(5)) // 100005
console.log(memoizedFunction(5)) // 100005 캐시된 결과 반환</code></pre>
<p>클로저를 이용하여 메모이제이션을 구현하는 방식은 함수 내부에 캐시를 숨겨서 외부에서 접근할 수 없도록 합니다. 전역 객체를 이용한 방법과 달리 전역 공간을 오염시키지 않고, 캐시가 해당 함수의 스코프 내에 존재하기 때문에, 외부에서 직접적으로 캐시에  접근하거나 이를 변경하는 것을 방지할 수 있습니다.</p>
<hr>
<h2 id="map-객체를-이용한-방법">Map 객체를 이용한 방법</h2>
<p>cache를 일반 객체가 아닌 Map을 사용해도 됩니다.</p>
<pre><code class="language-js">// 계산을 수행하는 함수
function calculateFunction(input) {
  // 계산 수행
  return input + 100000;
}

function createMemoizedFunction(fn) {
  // Map 객체를 캐시로 사용
  const cache = new Map();

  return function(x) {
    if(!cache.has(x)) {
      cache.set(x, fn(x));
    }
    return cache.get(x);
  }
}

const memoizedFunction = createMemoizedFunction(calculateFunction);

console.log(memoizedFunction(5)) // 100005
console.log(memoizedFunction(5)) // 100005 캐시된 결과 반환</code></pre>
<hr>
<h2 id="모듈-패턴을-이용한-방법">모듈 패턴을 이용한 방법</h2>
<pre><code class="language-js">function calculateFunction(input) {
  return input + 10000;
}

const memoizationModule = (function() {
  const cache = {};

  function createMemoizedFuction(fn) {
    return function (x) {
      if(!Object.hasOwn(cache, x)) {
        cache[x] = fn(x);
      }
      return cache[x];
    }
  }

  return createMemoizedFuction: createMemoizedFuction;
})()

const memoizedFunction = memoizationModule.createMemoizedFuction(calculateFunction);

console.log(memoizedFunction(5)); // 10005
console.log(memoizedFunction(5)); // 10005 캐시된 결과 반환</code></pre>
<p>모듈 패턴은 즉시 실행 함수(IIFE)와 클로저를 사용하여 작성할 수 있습니다.</p>
<hr>
<h2 id="lodash의-memoize함수를-사용한-방법">lodash의 memoize함수를 사용한 방법</h2>
<p>lodash 라이브러리의 memoize 함수를 사용하면 더 쉽게 메모이제이션을 적용할 수 있습니다.
lodash의 memoize 함수에 인자로 메모이제이션을 적용할 함수를 건네주기만 하면됩니다.</p>
<p>먼저, lodash 라이브러리를 프로젝트에 추가합니다.</p>
<pre><code class="language-bash">npm i lodash</code></pre>
<p>그리고 lodash라이브러리에서 memoize 함수를 불러와 사용합니다.</p>
<pre><code class="language-js">// lodash 라이브러리에서 
import { memoize } from &#39;lodash&#39;;

// 메모이제이션을 적용할 함수
function calculateFunction(input) {
  return input + 100000;
}

// lodash의 memoize 함수로 메모이제이션 적용
const memoizedFunction = memoize(calculateFunction);

// 함수 호출
console.log(memoizedFunction(5)); // 100005
console.log(memoizedFunction(5)); // 캐시된 결과로 바로 100005 반환</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 페이지 로딩 최적화: script vs script async vs  script defer의 차이점]]></title>
            <link>https://velog.io/@sw-jang12/%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A1%9C%EB%94%A9-%EC%B5%9C%EC%A0%81%ED%99%94-script-vs-script-async-vs-script-defer%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@sw-jang12/%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A1%9C%EB%94%A9-%EC%B5%9C%EC%A0%81%ED%99%94-script-vs-script-async-vs-script-defer%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Tue, 02 Jan 2024 01:29:28 GMT</pubDate>
            <description><![CDATA[<p>웹사이트의 성능을 향상시키는 것은 중요하다. 페이지의 로딩 속도와 콘텐츠 로딩속도가 사용자 경험에 영향을 미치기도 하고, Google의 연구에 따르면 FID(First Input Delay), CLS(Cumulative Layout Shift) 등의 성능을 개선하면 페이지 이탈률이 24% 감소한다고 한다.</p>
<blockquote>
</blockquote>
<p>보고서 링크
<a href="https://blog.chromium.org/2020/05/the-science-behind-web-vitals.html">https://blog.chromium.org/2020/05/the-science-behind-web-vitals.html</a></p>
<p>이러한 성능 지표를 개선하는 한 방법은 JavaScript 로딩 전략을 최적화하는 것이다. <code>&lt;script&gt;, &lt;script async&gt;, &lt;script defer&gt;</code>는 페이지 로딩 속도에 영향을 미치는 스크립트 태그 옵션들이다. 이 세 가지 옵션의 동작 방식과 각기 다른 상황에서 어떤 옵션을 선택해야 하는지 알아보자.</p>
<h2 id="script-만-사용할-경우"><code>&lt;script&gt;</code> 만 사용할 경우</h2>
<ul>
<li><strong>위치</strong>: <code>&lt;head&gt;</code> 또는 <code>&lt;body&gt;</code>안에 있을 수 있다.</li>
<li><strong>동작</strong>: 옵션없이 기본 script 태그만 사용할 경우 브라우저는 script 태그를 만나게 되면 HTML 파싱을 중단하고, script를 다운로드하고 실행한 후에 HTML 파싱을 다시 시작한다. </li>
<li><strong>사용</strong>: 페이지가 script에 의존적인 경우(script가 DOM 요소를 조작하고 해당 요소들이 script 이전에 페이지에 존재하는 경우)</li>
<li><strong>주의할 점</strong> </li>
</ul>
<p>1.이 방법은 페이지의 로딩 시간을 지연시킬 수 있으므로, script가 매우 필요한 경우에만 사용해야 한다.
    2.script의 크기가 크고 많다면 다운로드와 실행에 많은 시간이 걸릴 수 있다. 그래서 이 경우에는<code>&lt;head&gt;</code>보다는 <code>&lt;body&gt;</code> 태그 끝에 위치시키는 게 좋다.</p>
<h2 id="script-async"><code>&lt;script async&gt;</code></h2>
<ul>
<li><strong>위치</strong>: 주로 <code>&lt;head&gt;</code>태그안에 위치한다.</li>
<li><strong>동작</strong>: script는 HTML 파싱되는 동안 비동기적으로 다운로드된다. script는 다운로드가 완료되면 현재 진행 중인 HTML 파싱을 잠시 중단하고 즉시 script를 실행한다. script 실행이 끝난 후에 HTML 파싱이 바로 다시 시작된다.</li>
<li><strong>사용</strong>: 외부 script를 불러올 때 사용한다. 그리고 script 실행 순서가 중요하지 않은 경우, script가 초기 렌더링에 의존적이지 않은 경우에 적합하다.</li>
<li><strong>장점</strong>: 페이지의 로딩 속도에 영향을 주지 않으면서 script를 비동기적으로 로드할 수 있다.</li>
<li><strong>주의할 점</strong><ol>
<li>외부 script를 불러오는 것이 아니라 Inline Script에  async속성을 사용하면 무시된다. Inline Script는 HTML 문서가 파싱될 때 해당 위치에서 즉시 실행되기 때문이다.</li>
<li>그리고 HTML 파싱 중간에 script의 다운로드가 끝나면 script가 바로 실행될 수 있다. 이때 아직 DOM요소가 준비되지 않았을 수 있으므로, DOM 조작이 필요한 script에는 적합하지 않을 수 있다.</li>
<li><code>&lt;script async&gt;</code>은 다운로드가 완료되는 즉시 실행되기 떄문에 script가 여러개인 경우 이들의 실행순서가 보장되지 않는다.따라서 <code>&lt;script async&gt;</code>를 여러 개 사용할 경우 script 간의 의존성을 먼저 확인하고, 가능한 한 각 script가 독립적으로 기능하도록 하여야 한다.</li>
</ol>
</li>
</ul>
<h2 id="script-defer"><code>&lt;script defer&gt;</code></h2>
<ul>
<li><p><strong>위치</strong>: 주로 <code>&lt;head&gt;</code>태그안에 위치한다.</p>
</li>
<li><p><strong>동작</strong>: script의 다운로드와 HTML 파싱이 병렬로 진행된다. async와 달리 HTML 파싱이 전부 끝난 후 DOMContentLoaded 이벤트가 발생하기 바로 전에 실행된다.</p>
</li>
<li><p><strong>사용</strong>: 외부 script를 불러올 때 사용한다. DOM이 준비된 후 실행되기 때문에 DOM 조작이 필요한 script에 적합하다.</p>
</li>
<li><p><strong>주의할 점</strong> </p>
<ol>
<li>외부 script를 불러오는 것이 아니라 Inline Script에  defer속성을 사용하면 무시된다. Inline Script는 HTML 문서가 파싱될 때 해당 위치에서 즉시 실행되기 때문이다.<ol start="2">
<li>브라우저는 <code>&lt;script defer&gt;</code> 태그를 발견한 순서대로 script를 실행하기 때문에, 서로 의존성을 가진 여러 개의 script가 있다면 올바른 순서로 배치하는 것이 중요하다. 이렇게 하면 script들이 의도한 순서대로 실행될 수 있다.</li>
</ol>
</li>
</ol>
<br>


</li>
</ul>
<p> index.html</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;title&gt;Script Loading Demo&lt;/title&gt;
    &lt;script&gt;
      console.log(&quot;1. Without async or defer, in head&quot;);
    &lt;/script&gt;
    &lt;script async src=&quot;async-script.js&quot;&gt;&lt;/script&gt;
    &lt;script defer src=&quot;defer-script.js&quot;&gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;script&gt;
      console.log(&quot;2. Without async or defer, in body&quot;);
    &lt;/script&gt;
    &lt;script&gt;
      // HTML이 완전히 로드되었을 때(DOM트리가 구성되었을 때) 발생
      document.addEventListener(&quot;DOMContentLoaded&quot;, function () {
        console.log(&quot;4. DOMContentLoaded event raised&quot;);
      });
    &lt;/script&gt;
    &lt;script&gt;
      // 모든 요소(이미지, 스타일, 폰트, 스크립트 등)가 로드되었을 때 발생
      window.onload = function () {
        console.log(&quot;5. Load event raised&quot;);
      };
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>async-script.js</p>
<pre><code class="language-js">console.log(&quot;3. Async script loaded&quot;);</code></pre>
<p>defer-script.js</p>
<pre><code class="language-js">console.log(&quot;3. Defer script loaded&quot;);</code></pre>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/feab1e7c-e891-471b-a435-fde1745cda27/image.png" alt=""></p>
<p>만약 부정확한 내용이 있다면 댓글로 알려주세요. 수정하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Lighthouse로 웹페이지의 성능을 올려보자!! - Lighthouse 사용하기]]></title>
            <link>https://velog.io/@sw-jang12/Lighthouse%EB%A1%9C-%EC%9B%B9%ED%8E%98%EC%9D%B4%EC%A7%80%EC%9D%98-%EC%84%B1%EB%8A%A5%EC%9D%84-%EC%98%AC%EB%A0%A4%EB%B3%B4%EC%9E%90-1-Lighthouse-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@sw-jang12/Lighthouse%EB%A1%9C-%EC%9B%B9%ED%8E%98%EC%9D%B4%EC%A7%80%EC%9D%98-%EC%84%B1%EB%8A%A5%EC%9D%84-%EC%98%AC%EB%A0%A4%EB%B3%B4%EC%9E%90-1-Lighthouse-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 20 Dec 2023 04:24:05 GMT</pubDate>
            <description><![CDATA[<p>스터디원들과 프로젝트를 진행 중이었다.
프로젝트를 시작할 때 목표는 반응형 웹페이지를 만드는 것이었는데...
반응형 작업이 어느 정도 마무리되고 나니 욕심이 생겨 &quot;웹페이지의 성능까지 고려하면 더 좋지 않을까?!!&quot; 라는 생각이 들었다.</p>
<h1 id="성능-검사">성능 검사</h1>
<p>성능검사를 할 때 많이 사용하는 구글 Lighthouse, PageSpeed Insights 중에서 URL을 입력하지 않고도 편하게 성능을 측정할 수 있는 구글 Lighthouse를 사용하기로 했다.</p>
<h2 id="lighthouse란">Lighthouse란?</h2>
<image src="https://velog.velcdn.com/images/sw-jang12/post/0db549f4-0d2c-4793-a0c1-660c9d1d56f7/image.jpg"/>


<center style="color: gray; font-size:14px;"><a href="https://pixabay.com/ko//?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=820431">Pixabay</a>로부터 입수된 <a href="https://pixabay.com/ko/users/fancycrave1-1115284/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=820431">fancycrave1</a>님의 이미지 입니다.</center>

<p style="color: gray; font-size:14px;">Lighthouse는 등대라는 뜻인데, 등대처럼 안전하게 웹 성능 최적화의 길을 안내해 주고 위험을 피하도록 돕는 도구라는 의미로 붙이지 않았을까...(추측임)</p>



<blockquote>
<p>구글 Lighthouse는 웹페이지의 품질을 개선할 수 있게 도와주는 오픈 소스 자동화 도구다. 웹페이지의 성능, 접근성, 프로그레시브 웹 앱, SEO 등 다양한 측면에서 성능 평가를 할 수 있다.</p>
</blockquote>
<p>성능 테스트를 하고 싶은 웹페이지에 들어가서 개발자 도구를 열고 Lighthouse를 클릭하면 된다.</p>
<h3 id="lighthouse-사용방법">Lighthouse 사용방법</h3>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/a63469a4-3516-4c42-bdcf-e2db3ef5b179/image.png" alt=""></p>
<p>페이지 로드 분석버튼을 클릭해보자.
<img src="https://velog.velcdn.com/images/sw-jang12/post/d4cec94f-5c91-4e22-afd5-93883f025411/image.png" alt=""></p>
<p>분석이 끝나면 성능, 접근성, 권장 사항, 검색엔진 최적화 4가지 항목의 점수가 나온다.
PWA 항목은 다른 항목들과는 다르게, 점수로 표시되지 않는다. 안정성, 속도, 사용성 등 여러 가지 요소를 검사해서 PWA 기준에 충족되었는지를 판단하고 보여준다.</p>
<p>네이버
<img src="https://velog.velcdn.com/images/sw-jang12/post/fa029a65-189c-429c-9a64-dbc4fda40af5/image.png" alt="">    </p>
<p>X(트위터)
<img src="https://velog.velcdn.com/images/sw-jang12/post/2b856915-c708-46a3-bcd3-41781567f7f9/image.png" alt=""></p>
<p>왼쪽 상단에 + 버튼을 클릭하면 이전 보고서는 그대로 유지한 채로 새로운 Lighthouse 보고서를 생성할 수 있다.
<img src="https://velog.velcdn.com/images/sw-jang12/post/e5faae10-4285-4eb8-bc24-d515b6f72bb1/image.png" alt=""><img src="https://velog.velcdn.com/images/sw-jang12/post/717a02e0-2b9f-4b26-b158-a583b94cc475/image.png" alt=""></p>
<h3 id="개발자도구-lighthous의-단점">개발자도구 Lighthous의 단점</h3>
<p>Lighthouse를 통해 웹페이지를 테스트하면 + 옆에 검사 내역을 볼 수 있는 메뉴를 제공한다.</p>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/a8e90c64-4a42-4c70-97be-9d77eb4a1a45/image.png" alt="">
<img src="https://velog.velcdn.com/images/sw-jang12/post/bf856766-1598-49fb-859a-5d02d5ccb8a5/image.png" alt=""></p>
<p>이렇게 언제, 어느 페이지를 테스트했는지 쉽게 파악할 수 있다.</p>
<p>하지만, 개발자 도구에서 Lighthouse를 사용하면 개발자 도구를 닫을때마다 이전에 테스트했던 내역들이 사라진다.</p>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/52446bce-719e-44f7-b70e-1e19c69363e7/image.gif" alt=""></p>
<h3 id="lighthouse-report-viewer">Lighthouse Report Viewer</h3>
<p>Lighthouse Report Viewer를 사용하면 위에서 언급한 개발자 도구의 단점을 해결할 수 있다.</p>
<h4 id="lighthouse-보고서-파일로-관리하기">Lighthouse 보고서 파일로 관리하기</h4>
<p>Lighthouse Report Viewer를 사용하기 위해서는 보고서를 파일 형태로 가지고 있어야 한다.
오른쪽 상단에 있는 케밥 메뉴 아이콘을 클릭하고 JSON으로 저장을 선택하자. 그러면 보고서가 다운로드 된다.</p>
<p>이제 Lighthouse 보고서를 파일로 관리할 수 있다.
<img src="https://velog.velcdn.com/images/sw-jang12/post/7d0a0ffd-9eb4-489a-bc35-c99fb16154fd/image.png" alt=""></p>
<p><a target='_blank' href="https://googlechrome.github.io/lighthouse/viewer/"><strong>Lighthouse Report Viewer 바로가기</strong></a></p>
<p>위의 링크로 들어가서 다운로드받은 파일을 드래그하면 끝!!
<img src="https://velog.velcdn.com/images/sw-jang12/post/4d2ff7df-c1c1-43ae-9d3e-a7f9a6a9adb0/image.gif" alt=""></p>
<h4 id="lighthouse-report-diff-tool">Lighthouse Report Diff tool</h4>
<p>Lighthouse Report Viewer는 서로 다른 보고서를 비교해주는 기능도 제공한다.
링크로 들어가서 Lighthouse Report Diff tool을 클릭해보자.
<img src="https://velog.velcdn.com/images/sw-jang12/post/31467dc0-72e4-4e2b-8978-5f6b64414f55/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/3a443881-7e02-4093-9cab-f0f9c6115fb6/image.gif" alt=""></p>
<h3 id="주의사항">주의사항</h3>
<p>Lighthouse와 Lighthouse Report Viewer에서 제공하는 보고서를 보면 굉장히 친절하게 문제의 원인뿐만 아니라 개선 방안까지 제시해 준다. 하지만 모든 도구가 그렇듯이 만능은 아니다.</p>
<ol>
<li><p><strong>매번 변하는 점수</strong> : 알기 쉽게 각 항목에 대한 점수를 알려주지만 점수는 테스트할 때마다 변하는 경우가 많다. 다양한 요인들로 인해 테스트 결과는 변동성이 있을 수 있다. 그러니 여러 번 테스트해 보고 그 결과의 평균값을 사용하는 방법도 고려해 보자.</p>
</li>
<li><p><strong>결과 해석</strong> : Lighthouse의 점수가 절대적인 것은 아니다. Lighthouse는 웹 페이지의 성능을 대략 평가해 줄 뿐이고, 보고서에서 제시하는 개선 방안이 웹페이지의 성능을 향상시킬 수 있다는 것을 반드시 보장하지는 않는다는 것을 기억하자.</p>
</li>
<li><p><strong>테스트 환경</strong> : Lighthouse의 성능 점수는 테스트 환경에 영향을 받는다. 따라서 일관된 결과를 얻고 싶다면 가능한 한 동일한 환경에서 테스트하는 것이 좋아 보인다.</p>
</li>
</ol>
<h2 id="다른-페이지-성능-측정-사이트">다른 페이지 성능 측정 사이트</h2>
<p>Lighthouse 말고도 성능 측정 기능을 제공해 주는 좋은 사이트들이 있어 정리해 보았다.
 Lighthouse는 localhost:3000 같은 페이지도 개발자도구를 이용해서 성능을 측정할 수 있다. 아래 사이트들은 URL을 직접 입력해야 하므로 배포 이후에 테스트해 보자.</p>
<ul>
<li><a target="_blank" href="https://pagespeed.web.dev/"><strong>PageSpeed Insights</strong></a> : 구글이 제공하는 서비스다. 문서 정리도 잘 되어있어 있다.<ul>
<li><a target="_blank" href="https://www.webpagetest.org/"><strong>WebPageTest</strong></a> : 테스트 환경 설정이 가능하다.
<img src="https://velog.velcdn.com/images/sw-jang12/post/25387445-e2c6-4a51-bb65-46a0852b22e5/image.png" alt=""></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 LV2, 점프와 순간 이동 풀이(JavaScript)]]></title>
            <link>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV2-%EC%A0%90%ED%94%84%EC%99%80-%EC%88%9C%EA%B0%84-%EC%9D%B4%EB%8F%99-%ED%92%80%EC%9D%B4JavaScript</link>
            <guid>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV2-%EC%A0%90%ED%94%84%EC%99%80-%EC%88%9C%EA%B0%84-%EC%9D%B4%EB%8F%99-%ED%92%80%EC%9D%B4JavaScript</guid>
            <pubDate>Tue, 05 Dec 2023 04:29:34 GMT</pubDate>
            <description><![CDATA[<h1 id="점프와-순간-이동">점프와 순간 이동</h1>
<h2 id="문제-링크">문제 링크</h2>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12980">점프와 순간 이동</a></p>
<h2 id="문제-설명">문제 설명</h2>
<blockquote>
<p>OO 연구소는 한 번에 K 칸을 앞으로 점프하거나, (현재까지 온 거리) x 2 에 해당하는 위치로 순간이동을 할 수 있는 특수한 기능을 가진 아이언 슈트를 개발하여 판매하고 있습니다. 이 아이언 슈트는 건전지로 작동되는데, 순간이동을 하면 건전지 사용량이 줄지 않지만, 앞으로 K 칸을 점프하면 K 만큼의 건전지 사용량이 듭니다. 그러므로 아이언 슈트를 착용하고 이동할 때는 순간 이동을 하는 것이 더 효율적입니다. 아이언 슈트 구매자는 아이언 슈트를 착용하고 거리가 N 만큼 떨어져 있는 장소로 가려고 합니다. 
단, 건전지 사용량을 줄이기 위해 점프로 이동하는 것은 최소로 하려고 합니다. 아이언 슈트 구매자가 이동하려는 거리 N이 주어졌을 때, 사용해야 하는 건전지 사용량의 최솟값을 return하는 solution 함수를 만들어 주세요.</p>
<p>예를 들어 거리가 5만큼 떨어져 있는 장소로 가려고 합니다.
아이언 슈트를 입고 거리가 5만큼 떨어져 있는 장소로 갈 수 있는 경우의 수는 여러 가지입니다.</p>
<ul>
<li>처음 위치 0 에서 5 칸을 앞으로 점프하면 바로 도착하지만, 건전지 사용량이 5 만큼 듭니다.</br></li>
<li>처음 위치 0 에서 2 칸을 앞으로 점프한 다음 순간이동 하면 (현재까지 온 거리 : 2) x 2에 해당하는 위치로 이동할 수 있으므로 위치 4로 이동합니다. 이때 1 칸을 앞으로 점프하면 도착하므로 건전지 사용량이 3 만큼 듭니다.</br></li>
<li>처음 위치 0 에서 1 칸을 앞으로 점프한 다음 순간이동 하면 (현재까지 온 거리 : 1) x 2에 해당하는 위치로 이동할 수 있으므로 위치 2로 이동됩니다. 이때 다시 순간이동 하면 (현재까지 온 거리 : 2) x 2 만큼 이동할 수 있으므로 위치 4로 이동합니다. 이때 1 칸을 앞으로 점프하면 도착하므로 건전지 사용량이 2 만큼 듭니다.</br>
위의 3가지 경우 거리가 5만큼 떨어져 있는 장소로 가기 위해서 3번째 경우가 건전지 사용량이 가장 적으므로 답은 2가 됩니다.
</li>
</ul>
</blockquote>
<h2 id="제한-사항">제한 사항</h2>
<blockquote>
<ul>
<li>숫자 N: 1 이상 10억 이하의 자연수</li>
<li>숫자 K: 1 이상의 자연수</li>
</ul>
</blockquote>
<h2 id="입출력-예시">입출력 예시</h2>
<table>
<thead>
<tr>
<th align="center">N</th>
<th align="center">result</th>
</tr>
</thead>
<tbody><tr>
<td align="center">5</td>
<td align="center">2</td>
</tr>
<tr>
<td align="center">6</td>
<td align="center">2</td>
</tr>
<tr>
<td align="center">5000</td>
<td align="center">5</td>
</tr>
</tbody></table>
<h2 id="내-풀이">내 풀이</h2>
<pre><code class="language-js">function solution(n) {
  let currentPosition = n;
  let batteryUsage = 0;

  while(currentPosition &gt; 0) {
    const isEven = currentPosition % 2 === 0;

    if(isEven) {
      currentPosition /= 2;
    } else {
      currentPosition -= 1;
      batteryUsage += 1;
    }
  }

  return batteryUsage;
}
</code></pre>
<p>배터리 사용량을 최소로 해서 목표지점까지 가려면 순간이동을 최대한 많이 사용해야 한다고 생각했다.
순간이동은 현재 위치 x 2에 해당하는 위치로 이동할 수 있기 때문에, 순간이동 후 현재 위치는 무조건 짝수가 나온다.
즉, 짝수 위치는 순간이동으로 이동한 위치라고 생각할 수 있고, 홀수 위치는 점프로 이동한 위치라고 생각할 수 있다.
따라서 position이 짝수면 2로 나누고, position이 홀수면 -1 한 다음 다시 2로 나누고 베터리 사용량이 1 증가하면 된다.</p>
<pre><code>initial position:5000
currentPosition: 2500
batteryUsage: 0

currentPosition: 1250
batteryUsage: 0

currentPosition: 625
batteryUsage: 0

currentPosition: 624
batteryUsage: 1

currentPosition: 312
batteryUsage: 1

currentPosition: 156
batteryUsage: 1

currentPosition: 78
batteryUsage: 1

currentPosition: 39
batteryUsage: 1

currentPosition: 38
batteryUsage: 2

currentPosition: 19
batteryUsage: 2

currentPosition: 18
batteryUsage: 3

currentPosition: 9
batteryUsage: 3

currentPosition: 8
batteryUsage: 4

currentPosition: 4
batteryUsage: 4

currentPosition: 2
batteryUsage: 4

currentPosition: 1
batteryUsage: 4

currentPosition: 0
batteryUsage: 5</code></pre><h2 id="다른-풀이">다른 풀이</h2>
<pre><code class="language-js">function solution(n) {
  if(n === 1) return 1;
  const nArr = Array.from(n.toString(2));
  return nArr.reduce((a,b) =&gt; (+a) + (+b));
}</code></pre>
<p>어떤 수의 2진수를 구하는 방법을 먼저 살펴보자
n = 5 일 때, </p>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/e1d034da-1eb8-4cb4-9f29-0f10cd47bb98/image.png" alt=""></p>
<p>5 = $101_2$ 가 된다.</p>
<p>이때 2로 나누어지면 0이 되고, 나누어지지 않으면 1이 된다.
즉, 이진수에서 1은 홀수를 2로 나누었을 때 생기므로 
1은 점프, 0은 순간이동으로 생각할 수 있다.</p>
<p>따라서 n을 2진수로 변환하고 1의 개수를 세면 베터리 사용량을 알 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[로그인 모달 포커스 문제]]></title>
            <link>https://velog.io/@sw-jang12/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%AA%A8%EB%8B%AC-%ED%8F%AC%EC%BB%A4%EC%8A%A4-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@sw-jang12/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%AA%A8%EB%8B%AC-%ED%8F%AC%EC%BB%A4%EC%8A%A4-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Tue, 01 Aug 2023 08:31:47 GMT</pubDate>
            <description><![CDATA[<h1 id="tab-focus">tab, focus</h1>
<p>우리는 키보드의 tab 키를 눌러서 웹 서핑을 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/e8d28844-f964-4b77-b191-d4975b872fb5/image.png" width="100%" height="30%">
<img src="https://velog.velcdn.com/images/sw-jang12/post/f89b519c-5052-43cc-b92e-fdbf5d15d8cc/image.png" width="100%" height="30%">
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/0b99fad3-c0d0-46be-90d5-2e0496c71ff8/image.png" width="300px">
<img src="https://velog.velcdn.com/images/sw-jang12/post/73aa02a6-7e32-41c7-b75c-79fd754719d1/image.png" width="300px"></p>
</P>

<h2 id="focus와-접근성">focus와 접근성</h2>
<p>이러한 방식으로 키보드를 활용하여
마우스의 도움 없이도 웹 페이지의 정보에 접근할 수 있다.</p>
<p>스크린리더 사용자, 키보드만으로 웹 페이지에 접근하는 사용자는 포커스의 도움을 받는다.
스크린리더를 사용한다면, 포커스가 가리키는 대상의 텍스트 등을 음성안내를 통해 전달받는다.
그리고 포커스된 요소에 enter를 누르면 어떠한 동작이 발생한다.
그리고 키보드 사용자는 만약 이런 포커스가 제공되지 않는다면 현재 자신이 페이지의 어느 부분을 탐색하고 있는지 알 수 없게 된다.</p>
<p>이렇게 포커스가 접근성 측면에서 중요하다는 것을 알고 나니 현재 만들고 있던 로그인 모달에는 문제가 없는지 궁금해 확인해 보았다.
<img src="https://velog.velcdn.com/images/sw-jang12/post/6c3efa49-c0ac-4fcd-8fb1-f3875be02284/image.png" width="300px"></p>
<p>회원가입 부분 포커스가 | 이 부분까지 들어가는 것을 확인했다.
뭔가 이상하다는 생각이 들어 다른 사이트들은 어떻게 처리하고 있는지 확인해보았다.</p>
<h3 id="네이버">네이버</h3>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/27bc062d-f4ab-475c-b4d3-f9b588fa735e/image.png" width="100%" height="30%">
<img src="https://velog.velcdn.com/images/sw-jang12/post/03207508-3036-4dba-86b7-6e82b9686840/image.png" width="100%" height="20%"></p>


<h3 id="에어비엔비">에어비엔비</h3>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/adab95a1-6d30-4bd9-936e-062f3cfbda37/image.png" alt=""></p>
<h3 id="마이리얼트립">마이리얼트립</h3>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/269a4a6c-1b5c-4e0a-a9ce-036e08bbcfdb/image.png" alt=""></p>
<br>
네이버는 해당하는 텍스트 부분에만 포커스 되고(a태그), 에어비엔비는 button 전체, 마이리얼트립은 보이지 않는다.

<p>문제를 해결하기 위해 같은 방식을 쓰고 있는 네이버를 참고해서 문제를 해결해 보려고 한다.</p>
<p>우선 네이버를 확인해보자.
<img src="https://velog.velcdn.com/images/sw-jang12/post/3ffb6f9a-96d7-4fb1-9322-ef01000e930a/image.png" alt="">
<img src="https://velog.velcdn.com/images/sw-jang12/post/c967a5c1-d05c-44cc-8cc2-4cb2a736e9f5/image.png" alt=""></p>
<p>네이버의 로그인 페이지를 개발자도구를 통해 확인한 결과 li의 가상 요소로 | 모양을 넣고 있다는 것을 알 수 있다.
그리고 이번에는 내 로그인 모달을 확인해봤다.</p>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/37bb2610-548f-48d2-9384-17bb22878400/image.png" alt=""></p>
<p>내 방식과 네이버의 차이는 나는 a 태그의 가상 요소로 | 를 만들었고, 네이버는 li 태그의 가상 요소로 | 를 만들었다는 것을 확인할 수 있었다.</p>
<h2 id="focus를-받는-태그">focus를 받는 태그</h2>
<p>그래서 확인을 해보니 focus 받는 태그가 정해져 있었다.</p>
<blockquote>
<ul>
<li>a 태그</li>
</ul>
</blockquote>
<ul>
<li>button 태그</li>
<li>input 태그</li>
<li>select 태그</li>
<li>textarea 태그</li>
</ul>
<p>내 로그인 모달 같은 경우, a 태그의 가상 요소로 | 가 들어있으므로
포커스가 저렇게 들어갔다고 생각해볼 수 있다.</p>
<p>그래서 저 부분을 ul, li로 처리한 다음 다시 확인했다.</p>
<h2 id="div-a-를-li-ul-a-로-수정">div a 를 li ul a 로 수정</h2>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/1eca8331-3936-4431-b3e4-4a5478779334/image.png" width="300px"><img src="https://velog.velcdn.com/images/sw-jang12/post/1a33cf71-4066-4a46-b583-dc9c878055a8/image.png" alt=""></p>
<p>이제 포커스가 정상적으로 잡힌다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 LV1 완주하지 못한 선수 (자바스크립트)]]></title>
            <link>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Sat, 29 Jul 2023 10:19:23 GMT</pubDate>
            <description><![CDATA[<h1 id="완주하지-못한-선수">완주하지 못한 선수</h1>
<h2 id="문제설명">문제설명</h2>
<blockquote>
<p>수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.</p>
</blockquote>
<h2 id="제한사항">제한사항</h2>
<blockquote>
<ul>
<li>마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.</li>
</ul>
</blockquote>
<ul>
<li>completion의 길이는 participant의 길이보다 1 작습니다.</li>
<li>참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.</li>
<li>참가자 중에는 동명이인이 있을 수 있습니다.</li>
</ul>
<h2 id="입출력-예시">입출력 예시</h2>
<p><img src="https://velog.velcdn.com/images/sw-jang12/post/2674cb20-1618-4d77-bf3b-ea08dbf98e90/image.png" alt=""></p>
<h2 id="문제-링크">문제 링크</h2>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42576">https://school.programmers.co.kr/learn/courses/30/lessons/42576</a></p>
<h2 id="내-풀이">내 풀이</h2>
<pre><code class="language-js">function solution(par, com) {
  let parObj = createObj(par);
  let comObj = createObj(com);

  for(const person in parObj) {
    if(parObj[person] !== comObj[person]) return person;
  }
}

function createObj(arr) {
  return obj = arr.reduce((acc, cur) =&gt; {
    acc[cur] = (acc[cur] ?? 0) + 1;
    return acc;
  }, {});
}

let a = solution([&quot;mislav&quot;, &quot;stanko&quot;, &quot;mislav&quot;, &quot;ana&quot;], [&quot;stanko&quot;, &quot;ana&quot;, &quot;mislav&quot;]);
console.log(a); // mislav</code></pre>
<p>해시 문제이기 때문에 객체를 이용해서 풀었다.
함수로 전달되는 두 배열을 객체로 만든 다음에 만든 두 객체를 서로 비교한다.</p>
<p>마지막 예시를 적용해서 객체로 만든다면
mislav가 parObj 객체에서는 {mislav: 2}, comObj 객체에서는 {mislav: 1}가 된다.</p>
<p>이때 같은 key로 접근해서 value의 값을 확인했을 때
그 값이 같다면 key로 쓰이는 그 사람은 동명이인까지도 전부 완주했다는 의미가 된다.
하지만 서로 value 값이 다르다면 그것은 동명이인 중 한 명이 완주하지 못했다는 의미다.</p>
<p>따라서 value 값이 서로 다를 때 key 값으로 쓰인 값을 반환하면 된다.</p>
<h3 id="-nullish-coalescing-operator-물음표-두-개-연산자">?? (Nullish coalescing operator) 물음표 두 개 연산자</h3>
<p>ES2020에서 새롭게 추가된 연사자다.</p>
<p>left ?? right
left 값이 null 또는 undefined 이면 right 값을 return한다.</p>
<pre><code class="language-js">console.log(null ?? 10) // 10
console.log(undefined ?? 0) // 0
console.log(10 ?? null) // 10
console.log(10 ?? undefined)
console.log(null ?? &#39;a&#39;) // &#39;a&#39;
console.log(null ?? undefined) // undefined</code></pre>
<h2 id="다른-풀이-map-사용">다른 풀이 (map 사용)</h2>
<pre><code class="language-js">function createMap(arr) {
  const map = new Map();
  for(const el of arr) {
    map.set(el, (map.get(el) ?? 0) + 1);
  }
  return map;
}

function solution(par, com) {
  const parMap = createMap(par);
  const comMap = createMap(com);

  for(const [person, cnt] of parMap) {
    if(comMap.get(person) !== cnt) return person;
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 LV1 문자열 내 p와 y의 개수]]></title>
            <link>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%82%B4-p%EC%99%80-y%EC%9D%98-%EA%B0%9C%EC%88%98-m25t8zls</link>
            <guid>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%82%B4-p%EC%99%80-y%EC%9D%98-%EA%B0%9C%EC%88%98-m25t8zls</guid>
            <pubDate>Sat, 15 Jul 2023 11:16:49 GMT</pubDate>
            <description><![CDATA[<p>문제 링크
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/68935">https://school.programmers.co.kr/learn/courses/30/lessons/68935</a></p>
<h1 id="문제-설명">문제 설명</h1>
<blockquote>
<p>자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.</p>
</blockquote>
<h2 id="내-풀이">내 풀이</h2>
<pre><code class="language-js">function solution(n) {
  const arr = []
  const num = 3

  while(n !== 0) {
    arr.push(n % num)
    n = Math.floor(n / num)
  }
  arr.reverse()

  return arr.reduce((acc, cur, idx) = {
    return Math.pow(num, idx) * cur + acc 
  }, 0)
}</code></pre>
<p>10진수를 2진수로 변환하기 위해서는 10진수를 2로 계속 나누면서
나오는 나머지는 역순으로 읽으면 된다.</p>
<p>10진수 45를 2진수로 변환</p>
<blockquote>
<p>45 / 2 = 22, 나머지 1
22 / 2 = 11, 나머지 0
11 / 2 = 5, 나머지 1
5 / 2 = 2, 나머지 1
2 / 2 = 1, 나머지 0
1 / 2 = 0, 나머지 1</p>
</blockquote>
<p>이렇게 하면 45 = 101101(2)가 된다.</p>
<p>그럼 10진수를 3진수로 변환하는 방법도 이와 같다는 것을 알 수 있다.</p>
<blockquote>
<p>45 / 3 = 15, 나머지 0
15 / 3 = 5, 나머지 0
5 / 3 = 1, 나머지 2
1 / 3 = 0, 나머지 1</p>
</blockquote>
<p>45 = 1200(3)이 된다.</p>
<p>이때 arr.push()를 이용하기 때문에 45를 3진수로 변환한 숫자 배열은
[0, 0, 2, 1] 이 된다.</p>
<p>3진수를 10진수로 변환하기 위해서는 
배열을 뒤집어서 [1, 2, 0, 0]으로 만든다음
Math.pow(3, index) * arr[index] 한 값을 다 더하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 LV1 문자열 내 p와 y의 개수]]></title>
            <link>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%82%B4-p%EC%99%80-y%EC%9D%98-%EA%B0%9C%EC%88%98</link>
            <guid>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%82%B4-p%EC%99%80-y%EC%9D%98-%EA%B0%9C%EC%88%98</guid>
            <pubDate>Wed, 12 Jul 2023 13:45:27 GMT</pubDate>
            <description><![CDATA[<p>문제 링크
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/12916">https://school.programmers.co.kr/learn/courses/30/lessons/12916</a></p>
<h1 id="문제-설명">문제 설명</h1>
<p>대문자와 소문자가 섞여있는 문자열 s가 주어집니다. s에 &#39;p&#39;의 개수와 &#39;y&#39;의 개수를 비교해 같으면 True, 다르면 False를 return 하는 solution를 완성하세요. &#39;p&#39;, &#39;y&#39; 모두 하나도 없는 경우는 항상 True를 리턴합니다. 단, 개수를 비교할 때 대문자와 소문자는 구별하지 않습니다.</p>
<ul>
<li><p>정규표현식 사용한 풀이</p>
<pre><code class="language-js">function solution(s) {
  let pArr = s.match(/[p]/gi)
  let yArr = s.match(/[y]/gi)

  if(pArr === null &amp;&amp; yArr === null) return true
  else if(pArr === null || yArr === null) return false
  else if(pArr.length === yArr.length) return true
  else return false
}</code></pre>
<p>처음에는 정규표현식이 생각나서 써 봤는데 match를 썻을 때 s에 p나 y가 없으면 null이 나와서 오히려 더 복잡해 보였다.
그래서 정규표현식을 사용하지 않는 다른 풀이를 작성해봤다.</p>
</li>
<li><p>다른 풀이</p>
<pre><code class="language-js">function solution(s){
  s = s.toLowerCase();
  let arr = s.split(&#39;&#39;).filter((el =&gt; {
      if(el === &#39;p&#39; || el === &#39;y&#39;) {
          return true;
      }
  }))

  let pCount = 0;
  let yCount = 0;

  for(let str of arr) {
      str === &#39;p&#39; ? pCount++ : yCount++;
  }

  return pCount === yCount ? true : false
}</code></pre>
</li>
<li><p>다른 풀이2</p>
<pre><code class="language-js">function solution(s) {
return s.toLowerCase().split(&#39;&#39;).reduce((acc, cur) =&gt; {
  if(cur === &#39;p&#39;) acc + 1
  else if(cur === &#39;y&#39;) acc - 1
  return acc
}, 0) ? false : true
}</code></pre>
<p>acc의 값을 통해서 p의 개수와 y의 개수의 관계를 나타낸다.</p>
</li>
</ul>
<p>최종적으로 반환되는 acc가
양수면 p가 더 많다는 의미이고
0 이면 p와 y의 개수가 같다는 의미이고
음수면 y가 더 많다는 의미이다.</p>
<p>양수, 음수는 truthy, 0은 falsy 이기 때문에
reduce의 결과값이 양수, 음수라면 즉, p와 y의 개수가 서로 다르면 false
reduce의 결과값이 0이라면 즉, p와 y의 개수가 같다면 true 가 반환된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩 도장 LV2, 가장 거리가 짧은 점의 쌍]]></title>
            <link>https://velog.io/@sw-jang12/%EC%BD%94%EB%94%A9-%EB%8F%84%EC%9E%A5-LV2-%EA%B0%80%EC%9E%A5-%EA%B1%B0%EB%A6%AC%EA%B0%80-%EC%A7%A7%EC%9D%80-%EC%A0%90%EC%9D%98-%EC%8C%8D</link>
            <guid>https://velog.io/@sw-jang12/%EC%BD%94%EB%94%A9-%EB%8F%84%EC%9E%A5-LV2-%EA%B0%80%EC%9E%A5-%EA%B1%B0%EB%A6%AC%EA%B0%80-%EC%A7%A7%EC%9D%80-%EC%A0%90%EC%9D%98-%EC%8C%8D</guid>
            <pubDate>Sun, 09 Jul 2023 12:11:33 GMT</pubDate>
            <description><![CDATA[<p>문제 링크
<a href="https://codingdojang.com/scode/408">https://codingdojang.com/scode/408</a></p>
<h1 id="문제-설명">문제 설명</h1>
<p>1차원의 점들이 주어졌을 때, 그 중 가장 거리가 짧은 점의 쌍을 출력하는 함수를 작성하시오(단, 점들의 배열은 모두 정렬되어있다고 가정한다.)
s=[1, 3, 4, 8, 13, 17, 20] 이라면 결과값은 [3,4]가 된다.</p>
<ul>
<li><p>내 풀이</p>
<pre><code class="language-js">function solution(s) {
let ss = []
for(let i = 1; i &lt; s.length; i++) {
  ss.push(s[i] - s[i-1])
}
let minNum = Math.min(...ss)
let minIdx = ss.findIndex(el =&gt; el === minNum)

let answer = [s[minIdx], s[minIdx+1]]
console.log(answer)
}</code></pre>
<p>[1, 3, 4, 8, 13, 17, 20]은 각각 1차원의 위에있는 점들이므로 각 점들 사이의 거리는
3 - 1 = 2
4 - 3 = 1
8 - 4 = 4
13 - 8 = 5
17 - 13 = 4
20 - 17 = 3 이 된다.
그러면 가장 작은 값이 나왔을 때가 두 점사이의 거리가 가장 짧은 점의 쌍인것이다.</p>
</li>
</ul>
<p>ss = [2, 1, 4, 5, 4, 3]인 배열이라고 생각한다면
ss의 0번째 원소는 s의 1번째 원소 - s의 0번째 원소
ss의 1번째 원소는 s의 2번째 원소 - s의 1번째 원소
...
ss의 5번째 원소는 s의 6번째 원소 - s의 5번째 원소이다.</p>
<p>따라서 ss의 1번째 원소가 가장 작다는 것은 
s의 2번째 원소 - s의 1번째 원소가 가장 작다는 것이다
ss의 인덱스가 s의 5번째 원소까지 대응되므로
answer = [s[1], s[2]]</p>
<ul>
<li><p>다른 풀이1</p>
<pre><code class="language-js">function solution(s) {
let idx = 0
let min = Infinity
for(let i = 1; i &lt; s.length; i++) {
  if(s[i] - s[i-1] &lt; min) {
    min = s[i] - s[i-1]
    idx = i
  }
}

let answer = [s[idx-1], s[idx]]
console.log(answer)
}</code></pre>
<p>이 풀이에서</p>
<pre><code class="language-js">let min = Infinity
for(let i = 1; i &lt; s.length; i++) {
if(s[i] - s[i-1] &lt; min) {
  min = s[i] - s[i-1]
}
}</code></pre>
<p>이 부분은 s[i] - s[i-1] &lt; min 가 true 일때 min의 값을 s[i] - s[i-1]로 바꾼다. </p>
</li>
</ul>
<p>이때 s = [1, 3, 4, 8, 13, 17, 20] 이면 
min은 Infinity -&gt; 2 -&gt; 1 이렇게 변한다. 이렇게 min의 값이 변할 때 
idx = i 로 해주면 min의 값이 최소일 때 인덱스를 구할 수 있다.</p>
<ul>
<li><p>다른 풀이2</p>
<pre><code class="language-js">function solution(s) {
let ss = s.slice(1)

let answer = s.map((el, i) =&gt; [el, ss[i]]).sort((a, b) =&gt; {
  return (a[1] - a[0]) - (b[1] - b[0])
})
console.log(answer)
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 LV1 예산]]></title>
            <link>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EC%98%88%EC%82%B0</link>
            <guid>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EC%98%88%EC%82%B0</guid>
            <pubDate>Sat, 08 Jul 2023 10:51:38 GMT</pubDate>
            <description><![CDATA[<p>문제 링크
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/12982">https://school.programmers.co.kr/learn/courses/30/lessons/12982</a></p>
<h1 id="문제설명">문제설명</h1>
<p>부서별 신청금액이 들어있는 배열 d, 전체 예산 budget을 메개변수로 가진다.
전체 예산 내에서 최대 몇 개의 부서에 물품을 지원할 수 있는지 return 하는 함수를 작성하시오.</p>
<ul>
<li><p>내 풀이</p>
<pre><code class="language-js">function solution(d, budget) {
  d.sort((a,b) =&gt; b - a)
  let cnt = 0

  while(true) {
      let money = d.pop()
      budget -= money
      if(budget &gt; 0) cnt++
      else if(budget === 0) {
          cnt++
          break
      } else break
  }
  return cnt
}</code></pre>
</li>
<li><p>프로그래머스에 있는 다른 풀이</p>
<pre><code class="language-js">function solution(d, budget) {
return d.sort((a,b) =&gt; a - b).reduce((count, price) =&gt; {
  return count += ((budget -= price) &gt;= 0)
}, 0)
}</code></pre>
<p>이 풀이에서 count += ((budget -= price) &gt;= 0) 이 부분을 보면
count += true 는 count += 1,
count  += false 는 count += 0 과 같다는 것을 알 수 있다.</p>
</li>
</ul>
<pre><code class="language-js">0 + true // 1
0 + true + true // 2
0 + true * 10  // 10

0 + false // 0</code></pre>
<p>즉 reudce의 count는 budget -= price 가 0보다 크거나 같을 때에만 +1 이 된다. budget이 0보다 작아지면 count + 0 만 반복되서 count 값의 변화가 없어진다.</p>
<p>그렇다면 내가 처음에 썻던 풀이를 while이 아닌 for문을 이용해서 좀 더 간단하게 바꾸는 것도 가능하다.</p>
<pre><code class="language-js">function solution(d, budget) {
  let cnt = 0
  d.sort((a,b) =&gt; a - b)
  for(let i = 0; i &lt; d.length; i++) {
    if((budget -= d[i] &gt;= 0) cnt++
  }
  return cnt
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 LV1 약수의 개수와 덧셈]]></title>
            <link>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EC%95%BD%EC%88%98%EC%9D%98-%EA%B0%9C%EC%88%98%EC%99%80-%EB%8D%A7%EC%85%88</link>
            <guid>https://velog.io/@sw-jang12/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-LV1-%EC%95%BD%EC%88%98%EC%9D%98-%EA%B0%9C%EC%88%98%EC%99%80-%EB%8D%A7%EC%85%88</guid>
            <pubDate>Fri, 07 Jul 2023 10:58:01 GMT</pubDate>
            <description><![CDATA[<p>문제 링크
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/77884">https://school.programmers.co.kr/learn/courses/30/lessons/77884</a></p>
<h1 id="문제-설명">문제 설명</h1>
<p>두 정수 left, right 가 매개변수로 주어진다.
left ~ right 까지 있는 모든 수들 중 약수의 개수가 짝수면 +, 홀수면 - 
한 값을 return 하는 함수를 작성하시오.</p>
<ul>
<li>내 풀이
범위에 해당 하는 수들의 약수의 개수를 구해가면서 짝수면 +, 홀수면 - 한다.<pre><code class="language-js">function solution(left, right) {
let total = 0
for(let i = left; i &lt;= right; i++ {
  let len = createArrayDivisor(i).length
len % 2 === 0 ? total += i : total -= i
}
return total
}
</code></pre>
</li>
</ul>
<p>// n의 약수의 배열을 반환
function createArrayDivisor(n) {
  let arr = []
  for(let i = 1; i &lt;= n; i++ {
    n % i === 0 ? arr.push(i) : &#39;continue&#39;
  }
  return arr
}</p>
<p>const re = solution(13, 17)
console.log(re)
// 43</p>
<pre><code>그리고 약수를 찾는 알고리즘에 대해서 더 알아보았다.

위에서는 약수를 구하기 위해서 1부터 n까지 해당하는 모든 수들을 계속 n에 대해 나누면서 나머지가 0이 되는 수를 찾았다.
이때 시간복잡도는 O(N)의 복잡도는 가진다.

* ** 제곱근을 이용한 약수 구하기**
정수 n의 약수의 개수를 구하는 다른 방법은 제곱근을 이용하는 방법이다.
n의 제곱근을 구한 다음 n을 1부터 제곱근까지 나누어서 0이 되는 수를 먼저 찾는다.
나온 수들을 다시 n에 대해 나누었을 경우 나오는 값들 또한 n의 약수이다.
```js
function divisor(n) {
  // 제곱근 구하기
  let squareRoot = Math.floor(Math.sqrt(n))
  let arr = []

  for(let i = 1; i &lt;= squareRoot; i++) {
    if(n % i === 0) arr.push(i)
  }

  let newArr = arr.map(el =&gt; n / el)
  arr = arr.concat(newArr)

  // 정렬, 중복 제거
  arr.sort((a, b) =&gt; a - b)
  const set = new Set(arr)
  arr = [...set]

  console.log(arr)
}</code></pre><ul>
<li>프로그래머스에 있는 또 다른 풀이
어떤 수가 있을 때 그 수의 제곱근이 정수면 약수의 개수가 홀수인 성질을 이용<pre><code class="language-js">function solution(left, right) {
let total = 0
for(let i = left; i &lt;= right; i++) {
  if(Number.isInteger(Math.sqrt(i))) total -= i  
  else total += i
}
return total
}</code></pre>
</li>
</ul>
<p>Number.isInteger(n) : n의 값이 정수면 true, 정수가 아니면 false 반환
Math.sqrt(n) : n의 제곱근 반환</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩 도장 LV2, 8의 개수 구하기]]></title>
            <link>https://velog.io/@sw-jang12/%EC%BD%94%EB%94%A9-%EB%8F%84%EC%9E%A5-LV2-8%EC%9D%98-%EA%B0%9C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@sw-jang12/%EC%BD%94%EB%94%A9-%EB%8F%84%EC%9E%A5-LV2-8%EC%9D%98-%EA%B0%9C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 05 Jul 2023 13:09:14 GMT</pubDate>
            <description><![CDATA[<p>문제 링크
<a href="https://codingdojang.com/scode/393?answer_mode=hide">https://codingdojang.com/scode/393?answer_mode=hide</a></p>
<p><strong>1부터 10,000까지 8이라는 숫자가 총 몇번 나오는가?</strong>
<strong>8이 포함되어 있는 숫자의 갯수를 카운팅 하는 것이 아니라 8이라는 숫자를 모두 카운팅 해야 한다.
(※ 예를들어 8808은 3, 8888은 4로 카운팅 해야 함)</strong></p>
<ol>
<li><p>정규표현식 이용해서 풀기(내가 푼 방식)</p>
<pre><code>let arr = Array(10000).fill(0).map((el, idx) =&gt; idx + 1)
let cnt = 0
for(let i = 1; i &lt;= arr.length; i++) {
 i += &#39;&#39;
 let eightArr = i.match(/[8]/g)
 if(eightArr === null) continue
 else cnt += eightArr.length
}
console.log(cnt)
// 4000</code></pre></li>
<li><p>filter 이용해서 풀기(다른 사람이 푼 방식)</p>
<pre><code>const res = Array(10000)
             .fill(0)
             .map((el, idx) =&gt; idx + 1)
             .toString()
             .split(&#39;&#39;)
             .filter(el =&gt; el === &#39;8&#39;)
             .length
</code></pre></li>
</ol>
<p>console.log(res)<br>// 4000</p>
<p>```
풀이 링크
<a href="https://www.youtube.com/watch?v=oIuOhhff3Fk&amp;list=PLkfUwwo13dlWZxOdbvMhkzhAowaiEjuGS&amp;index=37">https://www.youtube.com/watch?v=oIuOhhff3Fk&amp;list=PLkfUwwo13dlWZxOdbvMhkzhAowaiEjuGS&amp;index=37</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML 태그, CSS 적용방식 (멋사 FES7 7/5)]]></title>
            <link>https://velog.io/@sw-jang12/HTML-%ED%83%9C%EA%B7%B8-CSS-%EC%A0%81%EC%9A%A9%EB%B0%A9%EC%8B%9D-%EB%A9%8B%EC%82%AC-FES7-75</link>
            <guid>https://velog.io/@sw-jang12/HTML-%ED%83%9C%EA%B7%B8-CSS-%EC%A0%81%EC%9A%A9%EB%B0%A9%EC%8B%9D-%EB%A9%8B%EC%82%AC-FES7-75</guid>
            <pubDate>Wed, 05 Jul 2023 08:52:22 GMT</pubDate>
            <description><![CDATA[<h2 id="html-태그">HTML 태그</h2>
<h3 id="알아야-할-태그">알아야 할 태그</h3>
<blockquote>
<ul>
<li>heading 태그</li>
</ul>
</blockquote>
<ul>
<li>a 태그</li>
<li>P 태그</li>
<li>br 태그</li>
<li>strong 태그</li>
</ul>
<h4 id="1-heading-태그h-태그">1. heading 태그(h 태그)</h4>
<ul>
<li>h1 ~ h6 가 있다.</li>
<li>h 태그는 목차와 같다.</li>
<li>검색엔진은 h 태그에 기반하여 보여주므로 검색엔진 최적화에 사용할 수 있다.</li>
<li>h1 ~ h6는 순차적으로 작성해야 한다.</li>
</ul>
<h4 id="2-a-태그">2. a 태그</h4>
<ul>
<li>링크를 걸어서 다른 페이지로 이동할 수 있다.</li>
<li>전화번호, 메일에 이용할 수도 있다.</li>
</ul>
<p>전화번호 </p>
<pre><code>&lt;a href=&quot;tel:010-1234-1234&quot;&gt;010-1234-1234&lt;/a&gt;</code></pre><p>메일 </p>
<pre><code>&lt;a href=&quot;mailto:12345678@MyMail.com&quot;&gt;2345678@MyMail.com&lt;/a&gt;</code></pre><ul>
<li>target 속성 : 현재 페이지에서 열건지 새 탭에서 열건지 설정할 수 있다.
_self : default, 현재 페이지에서 열기
_blank : 새 탭에서 페이지 열기</li>
<li>download 속성 : 링크를 클릭하면 파일이 다운로드된다.<pre><code>&lt;a href=&quot;test.txt&quot; download&gt;test.txt 다운로드&lt;/a&gt;</code></pre></li>
<li>페이지내에서 이동도 가능하다.<pre><code>&lt;div&gt;hello&lt;/div&gt;
&lt;a href=&quot;#section1&quot; target=&quot;_blank&quot;&gt;section1로 이동합니다.&lt;/a&gt;
&lt;section id=&quot;section1&quot;&gt;
section
&lt;/section&gt;</code></pre></li>
</ul>
<h4 id="3-p-태그">3. p 태그</h4>
<ul>
<li>하나의 문단을 나타낸다.</li>
</ul>
<h4 id="4-br-태그">4. br 태그</h4>
<ul>
<li>html문서 내에서 줄바꿈을 가능하게 해준다.</li>
<li>가능하면 연달아 사용하지 않는 것이 좋다.</li>
</ul>
<h4 id="5-strong-태그">5. strong 태그</h4>
<ul>
<li>단어 또는 문자를 굵게 처리한다.</li>
</ul>
<h3 id="그외-태그">그외 태그</h3>
<blockquote>
<ul>
<li>code 태그</li>
</ul>
</blockquote>
<ul>
<li>pre 태그</li>
<li>q 태그</li>
<li>blockquote</li>
<li>mark 태그</li>
</ul>
<h2 id="css-적용방식">CSS 적용방식</h2>
<p>4가지 방식으로 스타일을 적용할 수 있다.</p>
<h3 id="1인라인-방식">1.인라인 방식</h3>
<ul>
<li>html 태그의 style 속성을 이용해서 스타일을 적용시키는 방식<pre><code>&lt;p style=&quot;color: royalblue&quot;&gt;hello&lt;/p&gt;</code></pre></li>
<li>재사용성이 떨어지고, 가상 요소에 스타일을 줄 수 없기 때문에 잘 사용하지 않는다.<h3 id="2-내부-스타일-적용방식">2. 내부 스타일 적용방식</h3>
</li>
<li>style 태그를 이용해서 스타일을 적용시키는 방식<pre><code>&lt;head&gt;
  &lt;style&gt;
      div {
          background-color: red;
      }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div&gt;hello&lt;/div&gt;
&lt;/body&gt;</code></pre></li>
<li>적용시킬 스타일이 너무 많아지면 html문서가 너무 길어지는 단점이 있다.<h3 id="3-외부-스타일-적용방식">3. 외부 스타일 적용방식</h3>
</li>
<li>html 문서 외부에 있는 style sheet를 불러와서 스타일을 적용시키는 방식</li>
<li>link 태그를 이용해서 불러올 수 있다.</li>
</ul>
<p>주변에 있는 main.css 불러오기</p>
<pre><code>&lt;link rel=&quot;stylesheet&quot; href=&quot;./main.css&quot;&gt;</code></pre><p>주변에 있는 css폴더에 있는 main.css 불러오기</p>
<pre><code>&lt;link rel=&quot;stylesheet&quot; href=&quot;./css/main.css&quot;&gt;</code></pre><p>현재 폴더에서 한 번 나가서 css폴더에 있는 main.css 불러오기</p>
<pre><code>&lt;link rel=&quot;stylesheet&quot; href=&quot;../css/main.css&quot;&gt;</code></pre><h3 id="4-다중-스타일-시트">4. 다중 스타일 시트</h3>
<ul>
<li>스타일을 여러개의 파일로 나누어서 스타일을 적용하는 방법이다.</li>
<li>html 파일 내에서 여러개의 css 파일을 불러올 수도 있고, 한 css 파일에 여러개의 css 파일을 import 해서 불러올 수도 있다.</li>
</ul>
<p>html 파일 내에서 여러개의 css 파일 불러오기</p>
<pre><code>&lt;head&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./style1.css&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./style2.css&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./style3.css&quot;&gt;
&lt;/head&gt;</code></pre><p>한 css 파일에 여러개의 css 파일 import 하기</p>
<pre><code>&lt;!-- index.html --&gt;
&lt;head&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./style.css&quot;&gt;
&lt;/head&gt;</code></pre><pre><code>/* style.css */
@import url(&#39;./style1.css&#39;)
@import url(&#39;./style2.css&#39;)
@import url(&#39;./style3.css&#39;)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[VSC 단축키,  git 명령어 (멋사 FES7 7/4)]]></title>
            <link>https://velog.io/@sw-jang12/VSC-%EB%8B%A8%EC%B6%95%ED%82%A4-git-%EB%AA%85%EB%A0%B9%EC%96%B4-%EB%A9%8B%EC%82%AC-FES7-74</link>
            <guid>https://velog.io/@sw-jang12/VSC-%EB%8B%A8%EC%B6%95%ED%82%A4-git-%EB%AA%85%EB%A0%B9%EC%96%B4-%EB%A9%8B%EC%82%AC-FES7-74</guid>
            <pubDate>Tue, 04 Jul 2023 23:27:29 GMT</pubDate>
            <description><![CDATA[<p>오늘은 첫 수업이 진행되었다.
vscode의 유용한 익스텐션, 단축키, git설치 및 간단한 git 명령어를 배웠다.</p>
<h2 id="vscode-단축키">vscode 단축키</h2>
<ul>
<li>cmd + n : 새 파일 생성</li>
<li>cmd + s : 파일 저장</li>
<li>cmd + , : 설정</li>
<li>cmd + b : 사이드바 열고 닫기</li>
<li>ctrl + shift + ₩ : 터미널 바로열기</li>
<li>option(alt) + 마우스클릭 : 원하는 곳 동시선택</li>
<li>cmd + d : 같은 단어 동시 선택</li>
<li>cmd + alt + 방향키 : 동시 수정</li>
<li>cmd + 좌우 방향키 : 문장 양끝으로 이동</li>
<li>alt + 위아래 방향키 : 순서 바꾸기</li>
<li>ctrl + x : 잘라내기(드래그 x)<h3 id="설치하면-편한-익스텐션">설치하면 편한 익스텐션</h3>
</li>
<li>html tag wrap : option(alt) + w, 특정 문자 감싸서 태그 붙이기(기본은 p태그)</li>
<li>Auto Rename Tag : 여는 태그 이름을 바꾸면 닫는 태그 이름도 바뀐다.</li>
</ul>
<h2 id="git-명령어">git 명령어</h2>
<p>git pull, git add ., git commit -m &#39;메시지&#39;, git push 만 정리한다.</p>
<ul>
<li>git pull : 원격 저장소에서 코드 가져오기 </li>
<li>git add . : 현재 폴더에 있는 수정되거나 추가된 파일 추적</li>
<li>git commit -m &#39;메시지&#39; : 소스코드에 유의미한 메시지 version을 만든다.(메시지는 수정불가이니 제발, 얍 이런 거 x)</li>
<li>git push : 원격 저장소에 소스코드를 올린다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>