<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Orbit</title>
        <link>https://velog.io/</link>
        <description>For me better than yesterday</description>
        <lastBuildDate>Mon, 05 Feb 2024 14:20:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Orbit</title>
            <url>https://velog.velcdn.com/images/fervor_dev/profile/7499689d-f0e1-4e06-8798-5ed40f1dda0d/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Orbit. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/fervor_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Next.js] i18n 적용하기 with App routing]]></title>
            <link>https://velog.io/@fervor_dev/Next.js-i18n-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-with-App-routing</link>
            <guid>https://velog.io/@fervor_dev/Next.js-i18n-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-with-App-routing</guid>
            <pubDate>Mon, 05 Feb 2024 14:20:10 GMT</pubDate>
            <description><![CDATA[<h3 id="👋-intro">👋 Intro</h3>
<p>취업하고 바쁘게 지내던 중 이제 조금 적응하고 시간을 효율적으로 사용할 수 있을 것 같아 간만에 글을 작성한다.</p>
<p>회사는 <code>Vue</code>를 사용하는데, 왜 갑자기 <code>Next.js</code>일까?
사내 테스트로 <code>AI Generator Tools</code>를 사용해 보면서 대부분의 새로운 서비스는 <code>React</code>를 기반하여 제공된다는 것을 깨달았다.</p>
<p>점유율이 가장 높은 서비스를 다룰 줄 알아야 기존 서비스를 넓은 시야로 바라볼 수 있을 것 같다.</p>
<h3 id="📌-check-point">📌 Check Point</h3>
<h4 id="rendering">Rendering</h4>
<p>기존에는 CSR 서비스를 제공했기 때문에 클라이언트 측에서 브라우저의 Web API를 사용하여 <code>locale</code>을 가져왔다.
-&gt; <code>navigator.language</code></p>
<p>하지만 <code>Next.js</code>는 SSR이기 때문에 클라이언트에서 정보를 받기 전에 미리 보여주어야 한다.
렌더링 이후 <code>locale</code>을 설정하는 것은 UX를 고려하였을 때 좋지 않다.</p>
<p>따라서 클라이언트가 서버에 페이지를 요청할 때, 서버는 header의 <code>Accept-Language</code>를 확인하고 해당 <code>locale</code>로 설정하려고 한다.</p>
<h4 id="app-routing">App routing</h4>
<p><code>Next.js</code>가 App routing을 제공하면서 기존 i18n 라이브러리인 <code>next-i18next</code>이 적합하지 않다.</p>
<p><em>관련 문서</em></p>
<ul>
<li><a href="https://locize.com/blog/next-i18next/">All side optimized Next.js translations</a></li>
<li><a href="https://locize.com/blog/next-i18n-static/">Static HTML Export with i18n compatibility</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/dd200b5b-520f-4030-9272-43ebdb65d220/image.png" alt=""></p>
<p>따라서 <a href="https://locize.com/blog/next-app-dir-i18n/">Article - next-app-dir-i18n</a>  를 참고하여 진행해보자.</p>
<h3 id="⚡-locale-strategies">⚡ Locale strategies</h3>
<p><code>Next.js</code>에서는 locale의 두 가지 방식을 장려한다.</p>
<ul>
<li>Sub-path Routing</li>
<li>Domain Routing </li>
</ul>
<p><strong>Sub-path Routing</strong>은 <code>example.com/ko</code>나 <code>example.com/en</code>과 같은 구조이고,
<strong>Domain Routing</strong>은 <code>example.ko</code>나 <code>example.en</code>과 같은 구조이다.</p>
<p>필자는 Sub-path Routing 방식으로 진행하고 글을 작성하고자 한다.</p>
<h3 id="🚀-get-started">🚀 Get started</h3>
<h4 id="installation">Installation</h4>
<pre><code class="language-bash">npm install i18next react-i18next i18next-resources-to-backend accept-language

or

yarn add i18next react-i18next i18next-resources-to-backend accept-language</code></pre>
<h4 id="folder-structure">Folder structure</h4>
<pre><code>.
└── app
    ├── [lng]
    |   └── [home]
    |       ├── head.tsx
    |       └── pages.tsx
    ├── layout.ts
    └── page.ts</code></pre><h4 id="try-it">Try it!</h4>
<ol>
<li>i18n 설정</li>
</ol>
<ul>
<li>필자는 <code>ko</code>를 기본 언어로 설정하고 진행하였다.<blockquote>
<p>만약, 하나의 <code>json</code> 파일에 모든 번역을 적용할 것이라면 <code>namespace</code>를 고정해주면 된다.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-ts">export const fallbackLng = &#39;ko&#39;;
export const languages = [fallbackLng, &#39;en&#39;];
export const defaultNS = &#39;home&#39;;
export const cookieName = &#39;i18next&#39;;

export function getOptions(
  lng = fallbackLng,
  ns: string | string[] = defaultNS
) {
  return {
    supportedLngs: languages,
    fallbackLng,
    lng,
    fallbackNS: defaultNS,
    defaultNS,
    ns,
  };
}

// i18n/settings.ts</code></pre>
<ul>
<li>i18n을 <code>init</code>하는 <code>index.ts</code> 파일을 생성한다. 번역을 호출할 때마다 새로운 인스턴스를 만들어 준다.</li>
</ul>
<blockquote>
<p>이는 컴파일하는 동안 모든 것이 <strong>병렬</strong>처럼 실행되는 것처럼 보인다. 따라서 번역이 일관되게 유지된다.</p>
</blockquote>
<pre><code class="language-ts">import { createInstance, Namespace, FlatNamespace, KeyPrefix } from &#39;i18next&#39;;
import resourcesToBackend from &#39;i18next-resources-to-backend&#39;;
import { initReactI18next } from &#39;react-i18next/initReactI18next&#39;;
import { FallbackNs } from &#39;react-i18next&#39;;

import { getOptions } from &#39;@/i18n/settings&#39;;

const initI18next = async (lng: string, ns: string | string[]) =&gt; {
  const i18nInstance = createInstance();
  await i18nInstance
    .use(initReactI18next)
    .use(
      resourcesToBackend(
        (language: string, namespace: string) =&gt;
          import(`./locales/${language}/${namespace}.json`)
      )
    )
    .init(getOptions(lng, ns));
  return i18nInstance;
};

export async function useTranslation&lt;
  Ns extends FlatNamespace,
  KPrefix extends KeyPrefix&lt;FallbackNs&lt;Ns&gt;&gt; = undefined,
&gt;(lng: string, ns?: Ns, options: { keyPrefix?: KPrefix } = {}) {
  const i18nextInstance = await initI18next(
    lng,
    Array.isArray(ns) ? (ns as string[]) : (ns as string)
  );
  return {
    t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix),
    i18n: i18nextInstance,
  };
}

// i18n/index.ts</code></pre>
<blockquote>
<p>client에서 사용할 <code>useTranslation</code> hook을 생성한다.</p>
</blockquote>
<pre><code class="language-ts">&#39;use client&#39;;

import { useEffect, useState } from &#39;react&#39;;
import i18next, { FlatNamespace, KeyPrefix } from &#39;i18next&#39;;
import {
  initReactI18next,
  useTranslation as useTranslationOrg,
  UseTranslationOptions,
  UseTranslationResponse,
  FallbackNs,
} from &#39;react-i18next&#39;;
import { useCookies } from &#39;react-cookie&#39;;
import resourcesToBackend from &#39;i18next-resources-to-backend&#39;;
import LanguageDetector from &#39;i18next-browser-languagedetector&#39;;
import { getOptions, languages, cookieName } from &#39;@/i18n/settings&#39;;

const runsOnServerSide = typeof window === &#39;undefined&#39;;

i18next
  .use(initReactI18next)
  .use(LanguageDetector)
  .use(
    resourcesToBackend(
      (language: string, namespace: string) =&gt;
        import(`./locales/${language}/${namespace}.json`)
    )
  )
  .init({
    ...getOptions(),
    lng: &#39;ko&#39;,
    detection: {
      order: [&#39;path&#39;, &#39;htmlTag&#39;, &#39;cookie&#39;, &#39;navigator&#39;],
    },
    preload: runsOnServerSide ? languages : [],
  });

export function useTranslation&lt;
  Ns extends FlatNamespace,
  KPrefix extends KeyPrefix&lt;FallbackNs&lt;Ns&gt;&gt; = undefined,
&gt;(
  lng: string,
  ns?: Ns,
  options?: UseTranslationOptions&lt;KPrefix&gt;
): UseTranslationResponse&lt;FallbackNs&lt;Ns&gt;, KPrefix&gt; {
  1;
  const [cookies, setCookie] = useCookies([cookieName]);
  const ret = useTranslationOrg(ns, options);
  const { i18n } = ret;
  if (runsOnServerSide &amp;&amp; lng &amp;&amp; i18n.resolvedLanguage !== lng) {
    i18n.changeLanguage(lng);
  } else {
    const [activeLng, setActiveLng] = useState(i18n.resolvedLanguage);
    useEffect(() =&gt; {
      if (activeLng === i18n.resolvedLanguage) return;
      setActiveLng(i18n.resolvedLanguage);
    }, [activeLng, i18n.resolvedLanguage]);
    useEffect(() =&gt; {
      if (!lng || i18n.resolvedLanguage === lng) return;
      i18n.changeLanguage(lng);
    }, [lng, i18n]);
    useEffect(() =&gt; {
      if (cookies.i18next === lng) return;
      setCookie(cookieName, lng, { path: &#39;/home&#39; });
    }, [lng, cookies.i18next]);
  }
  return ret;
}


// i18n/client.ts</code></pre>
<ol start="2">
<li><code>middleware</code> 설정</li>
</ol>
<blockquote>
<p>cookie가 있다면 해당 언어로 적용하고, 없다면 headers의 <code>Accept-Language</code>를 가져온다.</p>
</blockquote>
<pre><code class="language-ts">import { NextResponse } from &#39;next/server&#39;
import acceptLanguage from &#39;accept-language&#39;
import { fallbackLng, languages, cookieName } from &#39;.@/i18n/settings&#39;

acceptLanguage.languages(languages)

export const config = {
  // matcher: &#39;/:lng*&#39;
  matcher: [&#39;/((?!api|_next/static|_next/image|assets|favicon.ico|sw.js).*)&#39;]
}

export function middleware(req) {
  let lng
  if (req.cookies.has(cookieName)) lng = acceptLanguage.get(req.cookies.get(cookieName).value)
  if (!lng) lng = acceptLanguage.get(req.headers.get(&#39;Accept-Language&#39;))
  if (!lng) lng = fallbackLng

  // Redirect if lng in path is not supported
  if (
    !languages.some(loc =&gt; req.nextUrl.pathname.startsWith(`/${loc}`)) &amp;&amp;
    !req.nextUrl.pathname.startsWith(&#39;/_next&#39;)
  ) {
    return NextResponse.redirect(new URL(`/${lng}${req.nextUrl.pathname}`, req.url))
  }

  if (req.headers.has(&#39;referer&#39;)) {
    const refererUrl = new URL(req.headers.get(&#39;referer&#39;))
    const lngInReferer = languages.find((l) =&gt; refererUrl.pathname.startsWith(`/${l}`))
    const response = NextResponse.next()
    if (lngInReferer) response.cookies.set(cookieName, lngInReferer)
    return response
  }

  return NextResponse.next()
}

// middleware.ts</code></pre>
<ol start="3">
<li><code>locale</code> 파일 생성</li>
</ol>
<pre><code class="language-json">{
  &quot;title&quot;: &quot;Next.js i18n 적용하기&quot;,
  &quot;desc&quot;: &quot;with App Routing&quot;,
  &quot;link&quot;: &quot;{{link}}로 이동&quot;
}

// i18n/locales/ko/home.json</code></pre>
<pre><code class="language-json">{
  &quot;title&quot;: &quot;Implementing Next.js i18n&quot;,
  &quot;desc&quot;: &quot;with App Routing&quot;,
  &quot;link&quot;: &quot;Go to {{link}}&quot;
}

// i18n/locales/en/home.json</code></pre>
<ol start="4">
<li><code>i18n</code> 적용</li>
</ol>
<blockquote>
<p><code>RootLayout</code>에 <code>lng</code> params를 추가한다.</p>
</blockquote>
<pre><code class="language-tsx">import { languages } from &#39;@/i18n/settings&#39;;

export const generateStaticParams = async () =&gt; {
  return languages.map((lng) =&gt; ({ lng }));
};

const RootLayout = ({
  children,
  params: { lng },
}: RootLayoutProps &amp; PropsLanguage) =&gt; {
  return (
    &lt;html lang={lng} dir={dir(lng)}&gt;
      &lt;body&gt;
        &lt;Providers&gt;
          &lt;Layout&gt;{children}&lt;/Layout&gt;
        &lt;/Providers&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
};

export default RootLayout;

// app/layout.tsx</code></pre>
<blockquote>
<p>만약 <code>lng</code> params가 없을 경우 default language 경로로 redirection한다.
필자는 기본 경로를 <code>/home</code>으로 설정했다.</p>
</blockquote>
<pre><code class="language-tsx">import { redirect } from &#39;next/navigation&#39;;

import { fallbackLng, languages } from &#39;@/i18n/settings&#39;;

export default async function Page({ params: { lng } }: { params: { lng: string }) {
  if (languages.indexOf(lng) &lt; 0) lng = fallbackLng;
  redirect(`/${lng}/home`);
}

// app/page.tsx</code></pre>
<blockquote>
<p>index와 client에서 각각 가져와 <code>locale</code>과 <code>namespace</code>를 지정 후 테스트한다.</p>
</blockquote>
<pre><code class="language-tsx">import { useTranslation } from &#39;@/i18n&#39;;

export default async function Head({
  params: { lng },
}: {
  params: {
    lng: string;
  };
}) {
  const { t } = await useTranslation(lng, &#39;home&#39;);

  return (
    &lt;&gt;
      &lt;title&gt;{t(&#39;title&#39;)}&lt;/title&gt;
      &lt;meta name=&quot;description&quot; content={t(&#39;desc&#39;)} /&gt;
    &lt;/&gt;
  );
}

// app/[lng]/[home]/head.tsx</code></pre>
<pre><code class="language-tsx">import { useTranslation } from &#39;@/i18n/client&#39;

export default function Page({ params: { lng } }: {
  params: {
    lng: string;
  };
}) {
  const { t } = useTranslation(lng, &#39;home&#39;)
  return (
    &lt;&gt;
      &lt;main&gt;
          &lt;div&gt;{t(&#39;title&#39;)}&lt;/div&gt;
          &lt;div&gt;{t(&#39;desc&#39;)}&lt;/div&gt;
        {languages.filter((l) =&gt; lng !== l).map((l, index) =&gt; {
          return (
            &lt;span key={l}&gt;
              {index &gt; 0 &amp;&amp; (&#39; or &#39;)}
              &lt;Link href={`/${l}/home`}&gt;{t(&#39;link&#39;, { link: l })}&lt;/Link&gt;
            &lt;/span&gt;
          )
        })}
      &lt;/main&gt;
    &lt;/&gt;
  )
}

// app/[lng]/[home]/page.tsx</code></pre>
<h4 id="preview">Preview</h4>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/80d317d7-94ab-4ab6-8b1c-290f523240bd/image.gif" alt=""></p>
<h3 id="📕-reference">📕 Reference</h3>
<ul>
<li><a href="https://github.com/i18next/next-i18next">Github - next-i18next</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 거리두기 확인하기 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B1%B0%EB%A6%AC%EB%91%90%EA%B8%B0-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B1%B0%EB%A6%AC%EB%91%90%EA%B8%B0-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0-JS</guid>
            <pubDate>Tue, 27 Jun 2023 12:12:45 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>개발자를 희망하는 죠르디가 카카오에 면접을 보러 왔습니다.</p>
<p>코로나 바이러스 감염 예방을 위해 응시자들은 거리를 둬서 대기를 해야하는데 개발 직군 면접인 만큼
아래와 같은 규칙으로 대기실에 거리를 두고 앉도록 안내하고 있습니다.</p>
<p>대기실은 5개이며, 각 대기실은 5x5 크기입니다.
거리두기를 위하여 응시자들 끼리는 맨해튼 거리1가 2 이하로 앉지 말아 주세요.
단 응시자가 앉아있는 자리 사이가 파티션으로 막혀 있을 경우에는 허용합니다.
예를 들어,
<img src="https://velog.velcdn.com/images/fervor_dev/post/ed67a47c-91b5-4fba-8d7d-9a71df9e7829/image.png" alt=""></p>
<p>5개의 대기실을 본 죠르디는 각 대기실에서 응시자들이 거리두기를 잘 기키고 있는지 알고 싶어졌습니다. 자리에 앉아있는 응시자들의 정보와 대기실 구조를 대기실별로 담은 2차원 문자열 배열 <code>places</code>가 매개변수로 주어집니다. 각 대기실별로 거리두기를 지키고 있으면 1을, 한 명이라도 지키지 않고 있으면 0을 배열에 담아 return 하도록 solution 함수를 완성해 주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li><p><code>places</code>의 행 길이(대기실 개수) = 5</p>
<ul>
<li><code>places</code>의 각 행은 하나의 대기실 구조를 나타냅니다.</li>
</ul>
</li>
<li><p><code>places</code>의 열 길이(대기실 세로 길이) = 5</p>
</li>
<li><p><code>places</code>의 원소는 <code>P</code>,<code>O</code>,<code>X</code>로 이루어진 문자열입니다.</p>
<ul>
<li><code>places</code> 원소의 길이(대기실 가로 길이) = 5</li>
<li><code>P</code>는 응시자가 앉아있는 자리를 의미합니다.</li>
<li><code>O</code>는 빈 테이블을 의미합니다.</li>
<li><code>X</code>는 파티션을 의미합니다.</li>
</ul>
</li>
<li><p>입력으로 주어지는 5개 대기실의 크기는 모두 5x5 입니다.</p>
</li>
<li><p>return 값 형식</p>
<ul>
<li>1차원 정수 배열에 5개의 원소를 담아서 return 합니다.</li>
<li><code>places</code>에 담겨 있는 5개 대기실의 순서대로, 거리두기 준수 여부를 차례대로 배열에 담습니다.</li>
<li>각 대기실 별로 모든 응시자가 거리두기를 지키고 있으면 1을, 한 명이라도 지키지 않고 있으면 0을 담습니다.</li>
</ul>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>places</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[[&quot;POOOP&quot;, &quot;OXXOX&quot;, &quot;OPXPX&quot;, &quot;OOXOX&quot;, &quot;POXXP&quot;], [&quot;POOPX&quot;, &quot;OXPXP&quot;, &quot;PXXXO&quot;, &quot;OXXXO&quot;, &quot;OOOPP&quot;], [&quot;PXOPX&quot;, &quot;OXOXP&quot;, &quot;OXPOX&quot;, &quot;OXXOP&quot;, &quot;PXPOX&quot;], [&quot;OOOXX&quot;, &quot;XOOOX&quot;, &quot;OOOXX&quot;, &quot;OXOOX&quot;, &quot;OOOOO&quot;], [&quot;PXPXP&quot;, &quot;XPXPX&quot;, &quot;PXPXP&quot;, &quot;XPXPX&quot;, &quot;PXPXP&quot;]]</td>
<td>[1, 0, 1, 1, 1]</td>
</tr>
<tr>
<td>### 🔑풀이</td>
<td></td>
</tr>
</tbody></table>
<pre><code class="language-js">function solution(places) {
  const result = [];
  for (const place of places) {
    let isSafePlace = 1;

    // 대기실을 순회하며 응시자의 자리에 대해 거리두기를 확인
    for (let i = 0; i &lt; 5; i++) {
      for (let j = 0; j &lt; 5; j++) {
        // 응시자가 앉아있는데
        if (place[i][j] === &quot;P&quot;) {
          // 거리두기를 지키지 않으면
          if (!isSafe(place, i, j)) {
            isSafePlace = 0;
            break;
          }
        }
      }

      if (!isSafePlace) {
        break;
      }
    }

    result.push(isSafePlace);
  }

  return result;
}

function isSafe(place, x, y) {
  // 상하좌우 방향을 나타내는 배열
  const dx = [0, 0, 1, -1];
  const dy = [1, -1, 0, 0];

  for (let i = 0; i &lt; 4; i++) {
    // 인접한 위치 좌표 계산
    const nx = x + dx[i];
    const ny = y + dy[i];

    // 대기실 범위 이내 여부 확인
    if (nx &gt;= 0 &amp;&amp; nx &lt; 5 &amp;&amp; ny &gt;= 0 &amp;&amp; ny &lt; 5) {
      // 맨해튼 거리가 1인 경우
      if (place[nx][ny] === &quot;P&quot;) {
        return false;
        // 인접한 위치에 빈 테이블이 있는 경우
      } else if (place[nx][ny] === &quot;O&quot;) {
        for (let j = 0; j &lt; 4; j++) {
          // 빈 테이블을 기준으로 다른 방향으로 인접한 위치 좌표 계산
          const nnx = nx + dx[j];
          const nny = ny + dy[j];

          if (nnx &gt;= 0 &amp;&amp; nnx &lt; 5 &amp;&amp; nny &gt;= 0 &amp;&amp; nny &lt; 5) {
            // 현재 응시자의 위치가 아닌 경우
            if (nnx !== x || nny !== y) {
              // 맨해튼 거리가 2이고 파티션 없이 응시자가 있는 경우
              if (place[nnx][nny] === &quot;P&quot;) {
                return false;
              }
            }
          }
        }
      }
    }
  }

  // 거리두기를 모두 지키면 true
  return true;
}
</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/81302">프로그래머스 - 거리두기 확인하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[원티드] 프리온보딩 프론트엔드 챌린지
 7월 - 사전과제]]></title>
            <link>https://velog.io/@fervor_dev/%EC%9B%90%ED%8B%B0%EB%93%9C-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%B1%8C%EB%A6%B0%EC%A7%80-7%EC%9B%94</link>
            <guid>https://velog.io/@fervor_dev/%EC%9B%90%ED%8B%B0%EB%93%9C-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EC%B1%8C%EB%A6%B0%EC%A7%80-7%EC%9B%94</guid>
            <pubDate>Fri, 23 Jun 2023 08:38:27 GMT</pubDate>
            <description><![CDATA[<p>올해 1월부터 지금까지,
수 많은 이력서를 제출하고 뜨거운 합격을 맞이하면서 어느정도 멘토링의 필요성을 느끼는 지금,
운이 좋게도 원티드에서 프리온보딩 프론트엔드 챌린지를 진행하는 것이 아닌가?</p>
<p>취업 코칭 및 이력서 특강까지,
삽질하고 있는 내게 좋은 기회가 될 것 같아 바로 지원하였다.</p>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/e9f38cb2-8dad-4039-ad79-26a4f9aabd47/image.png" alt=""></p>
<p>프론트엔드 뿐만 아니라 다양한 분야를 지원해준다. 비용이 따로 없어 손해볼 것 없으니 일단 신청 버튼 누르자.</p>
<h2 id="사전과제">사전과제</h2>
<hr>
<p>사전과제는 챌린지 참여를 다짐하고 학습을 준비하기 위한 용도이며 학습 커리큘럼이 사전 미션을 기반으로 내용이 구성되어 있다고 한다. 내용은 아래와 같다.</p>
<blockquote>
</blockquote>
<ol>
<li>CSR(Client-side Rendering)이란 무엇이며, 그것의 장단점에 대하여 설명해주세요.</li>
<li>SPA(Single Page Application)로 구성된 웹 앱에서 SSR(Server-side Rendering)이 필요한 이유에 대하여 설명해주세요.</li>
<li>Next.js 프로젝트에서 yarn start(or npm run start) 스크립트를 실행했을 때 실행되는 코드를 Next.js Github 레포지토리에서 찾은 뒤, 해당 파일에 대한 간단한 설명을 첨부해주세요.<ul>
<li><a href="https://nextjs.org/docs/getting-started">https://nextjs.org/docs/getting-started</a> (Next.js 세팅 가이드)</li>
<li><a href="https://github.com/vercel/next.js/">https://github.com/vercel/next.js/</a> (Next.js Github 레포지토리)    </li>
<li><code>_document.js</code>, <code>_app.js</code>, <code>getServerSideProps</code> 같은 요소들에 대해 설명을 요구하는 과제가 아닙니다. 오히려 Next.js 코드 베이스 내부를 살펴보라는 의미입니다.</li>
</ul>
</li>
</ol>
<p>1번, 2번처럼 CSR의 정의와 장단점, SPA의 SSR 필요성 등은 익숙히 보았으나,
3번처럼 프레임워크의 코드 작동원리와 구조를 직접 뜯어보는 경험은 정말 좋았다.</p>
<ul>
<li><a href="https://github.com/vercel/next.js">Next.js Github 레포지토리</a></li>
</ul>
<h3 id="csrclient-side-rendering">CSR(Client-side Rendering)</h3>
<hr>
<blockquote>
<p>CSR (Client-side Rendering)은 웹 애플리케이션의 렌더링 방식 중 하나로, 브라우저에서 JavaScript를 이용하여 동적으로 콘텐츠를 생성하는 방식이다. CSR에서는 초기에 빈 페이지가 로드되고, 클라이언트 측 JavaScript 코드가 실행되면서 필요한 데이터를 서버로부터 비동기적으로 가져와서 렌더링한다.</p>
</blockquote>
<h4 id="장점">장점</h4>
<ol>
<li><p><strong>사용자 경험(UX)이 향상될 수 있다.</strong></p>
<p> 사용자가 페이지를 빠르게 로드하고 상호작용할 수 있으며, 필요한 데이터만 가져와서 업데이트할 수 있다.</p>
</li>
<li><p><strong>서버 부하가 감소한다.</strong></p>
<p> 서버는 초기 렌더링 후 클라이언트 측에서 대부분의 작업을 처리하므로, 서버의 자원 사용량이 감소한다.</p>
</li>
</ol>
<h4 id="단점">단점</h4>
<ol>
<li><p><strong>초기 로딩 속도가 느릴 수 있다.</strong></p>
<p> 페이지가 비어 있는 상태로 시작하고, 필요한 데이터와 JavaScript를 다운로드해야 하므로 초기 로딩 시간이 필요하다.</p>
</li>
<li><p><strong>검색 엔진 최적화(SEO)에 어려움을 겪을 수 있다.</strong></p>
<p> 검색 엔진은 CSR 방식으로 렌더링된 페이지의 콘텐츠를 인식하기 어렵다.</p>
</li>
</ol>
<h3 id="ssrserver-side-rendering">SSR(Server-Side Rendering)</h3>
<hr>
<blockquote>
<p>SSR (Server-side Rendering)은 웹 애플리케이션의 렌더링 방식 중 하나로, 서버에서 초기 페이지 렌더링을 처리하여 클라이언트에 완전한 HTML 페이지를 전달하는 방식입니다. 클라이언트의 요청에 따라 서버에서 필요한 데이터를 가져와 페이지를 동적으로 생성한 후, 완성된 HTML 페이지를 클라이언트에 제공합니다.</p>
</blockquote>
<h4 id="장점-1">장점</h4>
<ol>
<li><p><strong>초기 로딩 속도가 빠르다.</strong></p>
<p> 서버에서 완전한 HTML 페이지를 생성하여 전달하기 때문에, 사용자는 초기에 빠르게 콘텐츠를 볼 수 있다.</p>
</li>
<li><p><strong>검색 엔진 최적화(SEO)가 용이하다.</strong></p>
<p> 검색 엔진은 HTML 페이지를 쉽게 읽고 인덱싱할 수 있으므로, 검색 결과에 페이지가 포함되기 쉽다.</p>
</li>
<li><p><strong>모든 사용자에게 동일한 콘텐츠를 제공한다.</strong></p>
<p> 서버에서 페이지를 완전히 렌더링하기 때문에, 서버에서 처리되는 로직이 클라이언트의 환경에 의해 영향을 받지 않는다.</p>
</li>
</ol>
<h4 id="단점-1">단점</h4>
<ol>
<li><p><strong>서버의 부하가 증가할 수 있다.</strong></p>
<p> 모든 요청마다 서버에서 페이지를 렌더링하므로, 서버의 자원 사용량이 증가할 수 있다.</p>
</li>
<li><p><strong>복잡한 상호작용이 필요한 경우 추가적인 클라이언트 측 JavaScript 코드가 필요할 수 있다.</strong></p>
<p> SSR은 초기 렌더링만 처리하므로, 클라이언트 측에서 추가적인 상호작용을 위한 JavaScript 코드를 작성해야 할 수 있다.</p>
</li>
</ol>
<h3 id="spasingle-page-application로-구성된-웹-앱은-왜-ssr이-필요할까">SPA(Single Page Application)로 구성된 웹 앱은 왜 SSR이 필요할까?</h3>
<hr>
<p>SPA(Single Page Application)로 구성된 웹 앱에서 SSR(Server-side Rendering)이 필요한 이유는</p>
<blockquote>
<p><strong>검색 엔진 최적화(SEO)</strong>와 <strong>초기 로딩 속도 개선</strong> 때문이다.</p>
</blockquote>
<p>이는 SPA로 구성된 웹 앱들은 기본적으로 CSR이다.
SSR은 CSR의 단점을 극복하는 장점을 가졌기 때문에 이를 확인하기 위해 앞선 개념을 다진 것이다.</p>
<h4 id="검색-엔진-최적화seo의-중요성">검색 엔진 최적화(SEO)의 중요성</h4>
<ol>
<li><p><strong>검색 엔진에서 노출</strong></p>
<p> 대부분의 웹 사용자는 검색 엔진을 통해 정보를 찾는다. 웹 사이트가 검색 엔진에서 상위에 노출되면 더 많은 유저들이 해당 사이트를 방문할 가능성이 높아진다. 나조차도 최상단의 링크를 먼저 누르기 때문에 SEO가 그만큼 중요하다고 할 수 있다.</p>
</li>
<li><p><strong>유기적인 트래픽 증가</strong></p>
<p> 검색 엔진에서 유기적인 트래픽을 유입받을 수 있다. 검색 엔진은 사용자가 원하는 정보를 제공하기 위해 웹 사이트의 콘텐츠를 인덱싱하고 순위를 매긴다. 웹 사이트가 관련성 높은 키워드에 대해 상위에 노출되면 사용자가 해당 키워드로 검색할 때 웹 사이트로 유입되는 트래픽이 증가하며 이는 매출과 연관이 생기게 된다.</p>
</li>
<li><p><strong>브랜드 가시성 강화</strong></p>
<p> 높은 검색 엔진 순위는 웹 사이트의 브랜드 가시성을 강화할 수 있다. 상위에 노출되는 웹 사이트는 사용자에게 신뢰와 신뢰성을 전달할 수 있으며, 브랜드 인지도를 향상시킨다. 사용자들은 일반적으로 상위 순위에 있는 사이트를 신뢰하고 방문하는 경향이 있다.</p>
</li>
<li><p><strong>경쟁 우위 확보</strong></p>
<p> SEO를 통해 경쟁사보다 더 나은 검색 엔진 순위를 얻을 수 있다. 경쟁이 치열한 온라인 시장에서 상위 순위에 있는 웹 사이트는 경쟁사들보다 더 많은 유저들의 관심을 받을 수 있고, 비즈니스 성과를 향상시킬 수 있다.</p>
</li>
<li><p><strong>사용자 경험 개선</strong></p>
<p> SEO는 사용자 경험을 개선하는 데에도 도움을 준다. 검색 엔진은 웹 사이트의 속도, 모바일 친화성, 콘텐츠 품질 등을 고려하여 순위를 결정한다. 따라서 SEO를 통해 웹 사이트를 최적화하면 사용자들에게 더 나은 경험을 제공할 수 있다.</p>
</li>
</ol>
<h4 id="초기-로딩-속도의-중요성">초기 로딩 속도의 중요성</h4>
<img src="https://velog.velcdn.com/images/fervor_dev/post/bef598e7-9773-41f1-b821-44c17da2f9ce/image.png" width="70%" align="left">

<img width="100%">

<p>구글 리서치 2017에 따르면 로딩 속도가 3초를 넘어가면서부터 이탈률이 높게 나타나는 것으로 조사되었다.</p>
<p>SPA(Single Page Application)는 기본적으로 CSR(Client-side Rendering)이기 때문에 JavaScript를 로드하면서 사용자가 이탈할 수 있다.</p>
<p>이로 인해 초기 로딩 속도는 웹 서비스에서 굉장히 중요하다고 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/724f0c37-fd7d-40bf-a22d-5a0f32b154de/image.png" alt=""></p>
<pre><code>유튜브는 이처럼 JavaScript로 데이터를 가져오기 전에 초기 화면을 세팅하여 로딩을 개선하였다.</code></pre><h4 id="정리">정리</h4>
<p><strong>SPA</strong>는 클라이언트에서 페이지를 <strong>동적으로 렌더링</strong>하므로, 초기 로딩 시에는 비어 있는 페이지가 로드된다. 이로 인해 검색 엔진은 초기 상태에서의 콘텐츠를 인식하기 어려워 <strong>SEO에 불리</strong>하다는 단점이 있다.
또한, 초기 로딩 시 필요한 JavaScript와 데이터를 다운로드해야 하므로 <strong>초기 로딩 속도가 상대적으로 느리다</strong>.</p>
<p><strong>SSR</strong>은 서버에서 페이지를 렌더링하여 클라이언트에 전달하는 방식으로, <strong>초기 로딩 시에도 완전한 페이지</strong>를 제공할 수 있다.
이는 검색 엔진이 콘텐츠를 인식하는 데 도움이 되며, <strong>초기 로딩 속도를 개선하여 사용자 경험을 향상</strong>시킬 수 있다.</p>
<p>하지만 서버에서 페이지를 렌더링하는 만큼 <strong>서버의 부하가 증가</strong>한다는 단점이 있다는 것을 인지하자.</p>
<h3 id="nextjs-프로젝트에서-yarn-startor-npm-run-start-스크립트를-실행했을-때">Next.js 프로젝트에서 yarn start(or npm run start) 스크립트를 실행했을 때</h3>
<p>우리는 Next.js 프레임워크를 통해 프로젝트를 생성할 수 있다.
먼저, 프로젝트를 생성하는 명령어를 터미널에 입력한다.</p>
<pre><code>npx create-next-app my-app</code></pre><img src="https://velog.velcdn.com/images/fervor_dev/post/94213525-332b-4c7e-8f45-42ae29ff6569/image.png">

<p>성공적으로 생성되었다. 생성된 프로젝트의 <code>pakage.json</code>에서 <code>script</code>를 확인해보자.</p>
<pre><code>...
&quot;scripts&quot;: {
    &quot;dev&quot;: &quot;next dev&quot;,
    &quot;build&quot;: &quot;next build&quot;,
    &quot;start&quot;: &quot;next start&quot;,
    &quot;lint&quot;: &quot;next lint&quot;
  },
...</code></pre><ul>
<li><code>next dev</code> : 로컬에서 development 환경으로 웹 서버를 실행한다.</li>
<li><code>next build</code> : 작성된 코드를 build 한다.</li>
<li><code>next start</code> : 로컬에서 production 환경으로 웹 서버를 실행한다. 이때, 빌드된 <code>.next/</code> 폴더가 있어야 한다.</li>
<li><code>next lint</code> : 정적 분석 도구로 내가 작성한 코드가 규칙에 적합한지 확인한다.</li>
</ul>
<p>이제 어떤 코드가 <code>next start</code> 명령어로 작동되는지 Next.js Github 레포지토리로 이동해보자.</p>
<p>처음에는 헤맸으나 명령어에 맞는 코드네임이 잘 구분되어 있어 찾을 수 있었다.</p>
<img src="https://velog.velcdn.com/images/fervor_dev/post/2944cb6d-6c5b-4a2e-bc02-05186f217a40/image.png">

<pre><code class="language-ts">#!/usr/bin/env node

import arg from &#39;next/dist/compiled/arg/index.js&#39;
import { startServer } from &#39;../server/lib/start-server&#39;
import { getPort, printAndExit } from &#39;../server/lib/utils&#39;
import isError from &#39;../lib/is-error&#39;
import { getProjectDir } from &#39;../lib/get-project-dir&#39;
import { CliCommand } from &#39;../lib/commands&#39;
import { resolve } from &#39;path&#39;
import { PHASE_PRODUCTION_SERVER } from &#39;../shared/lib/constants&#39;
import loadConfig from &#39;../server/config&#39;

const nextStart: CliCommand = async (argv) =&gt; {
  const validArgs: arg.Spec = {
    // Types
    &#39;--help&#39;: Boolean,
    &#39;--port&#39;: Number,
    &#39;--hostname&#39;: String,
    &#39;--keepAliveTimeout&#39;: Number,

    // Aliases
    &#39;-h&#39;: &#39;--help&#39;,
    &#39;-p&#39;: &#39;--port&#39;,
    &#39;-H&#39;: &#39;--hostname&#39;,
  }
  let args: arg.Result&lt;arg.Spec&gt;
  try {
    args = arg(validArgs, { argv })
  } catch (error) {
    if (isError(error) &amp;&amp; error.code === &#39;ARG_UNKNOWN_OPTION&#39;) {
      return printAndExit(error.message, 1)
    }
    throw error
  }
  if (args[&#39;--help&#39;]) {
    console.log(`
      Description
        Starts the application in production mode.
        The application should be compiled with \`next build\` first.

      Usage
        $ next start &lt;dir&gt; -p &lt;port&gt;`

      ...
    process.exit(0)
  }

  const dir = getProjectDir(args._[0])
  const host = args[&#39;--hostname&#39;]
  const port = getPort(args)

  const keepAliveTimeoutArg: number | undefined = args[&#39;--keepAliveTimeout&#39;]
  if (
    typeof keepAliveTimeoutArg !== &#39;undefined&#39; &amp;&amp;
    (Number.isNaN(keepAliveTimeoutArg) ||
      !Number.isFinite(keepAliveTimeoutArg) ||
      keepAliveTimeoutArg &lt; 0)
  ) {
    printAndExit(
      `Invalid --keepAliveTimeout, expected a non negative number but received &quot;${keepAliveTimeoutArg}&quot;`,
      1
    )
  }

  const keepAliveTimeout = keepAliveTimeoutArg
    ? Math.ceil(keepAliveTimeoutArg)
    : undefined

  const config = await loadConfig(
    PHASE_PRODUCTION_SERVER,
    resolve(dir || &#39;.&#39;),
    undefined,
    undefined,
    true
  )

  await startServer({
    dir,
    isDev: false,
    hostname: host,
    port,
    keepAliveTimeout,
    useWorkers: !!config.experimental.appDir,
  })
}

export { nextStart }

// packages/next/src/cli/next-start.ts</code></pre>
<p>작성된 코드를 보면 우리가 <code>--help</code>와 같은 옵션에 대한 실행 내용이 있다.
또한, 이러한 옵션을 줄여 쓰기 위해 alias 설정까지 하나하나 해주는 것을 깨달았다.</p>
<p>import를 보면 <code>{ startServer }</code> 객체를 가져와 적용하는 것을 확인할 수 있다.
내부 코드를 들여다보자.</p>
<pre><code class="language-ts">...
// setup server listener as fast as possible
  const server = http.createServer(async (req, res) =&gt; {
    try {
      if (handlersPromise) {
        await handlersPromise
        handlersPromise = undefined
      }
      sockets.add(res)
      res.on(&#39;close&#39;, () =&gt; sockets.delete(res))
      await requestHandler(req, res)
    } catch (err) {
      res.statusCode = 500
      res.end(&#39;Internal Server Error&#39;)
      Log.error(`Failed to handle request for ${req.url}`)
      console.error(err)
    }
  })

...

// packages/next/src/server/lib/start-server.ts</code></pre>
<p>코드를 살펴보면 <code>createServer</code> 메서드를 사용하여 서버를 활성화 해준다는 것을 알 수 있었다.
별 생각 없이 입력했던 <code>npm start</code>에서 Socket을 열어 서버를 활성화 해주고 관련 옵션들에 관해 콘솔을 찍어준다는걸 직접 확인할 수 있었다.</p>
<p>프레임워크가 좋지만, 무작정 사용하기 보다 근본을 이해하는 것이 중요하다는 것을 느꼈다.
이러한 개념을 이해하고 환경세팅을 하나하나 만들 수 있는 능력을 갖추는 것이 나의 개발 수명을 결정짓는 하나의 척도가 아닐까.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 택배상자 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%83%9D%EB%B0%B0%EC%83%81%EC%9E%90-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%83%9D%EB%B0%B0%EC%83%81%EC%9E%90-JS</guid>
            <pubDate>Thu, 22 Jun 2023 12:42:02 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>영재는 택배상자를 트럭에 싣는 일을 합니다. 영재가 실어야 하는 택배상자는 크기가 모두 같으며 1번 상자부터 n번 상자까지 번호가 증가하는 순서대로 컨테이너 벨트에 일렬로 놓여 영재에게 전달됩니다. 컨테이너 벨트는 한 방향으로만 진행이 가능해서 벨트에 놓인 순서대로(1번 상자부터) 상자를 내릴 수 있습니다. 하지만 컨테이너 벨트에 놓인 순서대로 택배상자를 내려 바로 트럭에 싣게 되면 택배 기사님이 배달하는 순서와 택배상자가 실려 있는 순서가 맞지 않아 배달에 차질이 생깁니다. 따라서 택배 기사님이 미리 알려준 순서에 맞게 영재가 택배상자를 실어야 합니다.</p>
<p>만약 컨테이너 벨트의 맨 앞에 놓인 상자가 현재 트럭에 실어야 하는 순서가 아니라면 그 상자를 트럭에 실을 순서가 될 때까지 잠시 다른 곳에 보관해야 합니다. 하지만 고객의 물건을 함부로 땅에 둘 수 없어 보조 컨테이너 벨트를 추가로 설치하였습니다. 보조 컨테이너 벨트는 앞 뒤로 이동이 가능하지만 입구 외에 다른 면이 막혀 있어서 맨 앞의 상자만 뺄 수 있습니다(즉, 가장 마지막에 보조 컨테이너 벨트에 보관한 상자부터 꺼내게 됩니다). 보조 컨테이너 벨트를 이용해도 기사님이 원하는 순서대로 상자를 싣지 못 한다면, 더 이상 상자를 싣지 않습니다.</p>
<p>예를 들어, 영재가 5개의 상자를 실어야 하며, 택배 기사님이 알려준 순서가 기존의 컨테이너 벨트에 네 번째, 세 번째, 첫 번째, 두 번째, 다섯 번째 놓인 택배상자 순서인 경우, 영재는 우선 첫 번째, 두 번째, 세 번째 상자를 보조 컨테이너 벨트에 보관합니다. 그 후 네 번째 상자를 트럭에 싣고 보조 컨테이너 벨트에서 세 번째 상자 빼서 트럭에싣습니다. 다음으로 첫 번째 상자를 실어야 하지만 보조 컨테이너 벨트에서는 두 번째 상자를, 기존의 컨테이너 벨트에는 다섯 번째 상자를 꺼낼 수 있기 때문에 더이상의 상자는 실을 수 없습니다. 따라서 트럭에는 2개의 상자만 실리게 됩니다.</p>
<p>택배 기사님이 원하는 상자 순서를 나타내는 정수 배열 <code>order</code>가 주어졌을 때, 영재가 몇 개의 상자를 실을 수 있는지 return 하는 solution 함수를 완성하세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li>1 ≤ <code>order</code>의 길이 ≤ 1,000,000</li>
<li><code>order</code>는 1이상 <code>order</code>의 길이 이하의 모든 정수가 한번씩 등장합니다.</li>
<li><code>order[i]</code>는 기존의 컨테이너 벨트에 <code>order[i]</code>번째 상자를 i+1번째로 트럭에 실어야 함을 의미합니다.</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>weights</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[100,180,360,100,270]</td>
<td>4</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(order) {
  let result = 0;
  const stack = [];

  for (let i = 1; i &lt;= order.length; i++) {
    stack.push(i);

    // 스택의 상자 번호가 주어진 순서와 일치하는지 확인
    while (stack.length !== 0 &amp;&amp; stack.at(-1) === order[result]) {
      stack.pop(); // 일치하는 상자 번호는 스택에서 제거
      result++;
    }
  }

  return result;
}</code></pre>
<h3 id="🧱-알고리즘">🧱 알고리즘</h3>
<p><a href="https://ko.wikipedia.org/wiki/%EC%8A%A4%ED%83%9D">스택 알고리즘(Stack algorithm) - Wikipedia</a></p>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/131704">프로그래머스 - 택배상자</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 시소 짝꿍 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%8B%9C%EC%86%8C-%EC%A7%9D%EA%BF%8D-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%8B%9C%EC%86%8C-%EC%A7%9D%EA%BF%8D-JS</guid>
            <pubDate>Tue, 20 Jun 2023 12:26:03 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>어느 공원 놀이터에는 시소가 하나 설치되어 있습니다. 이 시소는 중심으로부터 2(m), 3(m), 4(m) 거리의 지점에 좌석이 하나씩 있습니다.
이 시소를 두 명이 마주 보고 탄다고 할 때, 시소가 평형인 상태에서 각각에 의해 시소에 걸리는 토크의 크기가 서로 상쇄되어 완전한 균형을 이룰 수 있다면 그 두 사람을 시소 짝꿍이라고 합니다. 즉, 탑승한 사람의 무게와 시소 축과 좌석 간의 거리의 곱이 양쪽 다 같다면 시소 짝꿍이라고 할 수 있습니다.
사람들의 몸무게 목록 <code>weights</code>이 주어질 때, 시소 짝꿍이 몇 쌍 존재하는지 구하여 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li>2 ≤ <code>weights</code>의 길이 ≤ 100,000</li>
<li>100 ≤ <code>weights[i]</code> ≤ 1,000    <ul>
<li>몸무게 단위는 N(뉴턴)으로 주어집니다.</li>
<li>몸무게는 모두 정수입니다.</li>
</ul>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>weights</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[100,180,360,100,270]</td>
<td>4</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(weights) {
  const map = {};
  const ratio = [1, 3 / 2, 4 / 3, 2]; // 비율 배열

  // 무게를 내림차순 정렬 후 reduce로 합산
  return weights
    .sort((a, b) =&gt; b - a)
    .reduce((result, weight) =&gt; {
      // 해당 무게의 비율을 곱하여 result에 합산
      ratio.map((v) =&gt; (result += map[weight * v] || 0)); 

      // 해당 무게의 등장 횟수 1 증가
      map[weight] = (map[weight] || 0) + 1; 
      return result;
    }, 0);
}
</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/152996">프로그래머스 - 시소 짝꿍</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 숫자 짝꿍 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%AB%EC%9E%90-%EC%A7%9D%EA%BF%8D-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%AB%EC%9E%90-%EC%A7%9D%EA%BF%8D-JS</guid>
            <pubDate>Sat, 17 Jun 2023 12:29:06 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>두 정수 <code>X</code>, <code>Y</code>의 임의의 자리에서 공통으로 나타나는 정수 k(0 ≤ k ≤ 9)들을 이용하여 만들 수 있는 가장 큰 정수를 두 수의 짝꿍이라 합니다(단, 공통으로 나타나는 정수 중 서로 짝지을 수 있는 숫자만 사용합니다). <code>X</code>, <code>Y</code>의 짝꿍이 존재하지 않으면, 짝꿍은 -1입니다. <code>X</code>, <code>Y</code>의 짝꿍이 0으로만 구성되어 있다면, 짝꿍은 0입니다.</p>
<p>예를 들어, <code>X</code> = 3403이고 <code>Y</code> = 13203이라면, <code>X</code>와 <code>Y</code>의 짝꿍은 <code>X</code>와 <code>Y</code>에서 공통으로 나타나는 3, 0, 3으로 만들 수 있는 가장 큰 정수인 330입니다. 다른 예시로 <code>X</code> = 5525이고 <code>Y</code> = 1255이면 <code>X</code>와 <code>Y</code>의 짝꿍은 <code>X</code>와 <code>Y</code>에서 공통으로 나타나는 2, 5, 5로 만들 수 있는 가장 큰 정수인 552입니다(<code>X</code>에는 5가 3개, <code>Y</code>에는 5가 2개 나타나므로 남는 5 한 개는 짝 지을 수 없습니다.)
두 정수 <code>X</code>, <code>Y</code>가 주어졌을 때, <code>X</code>, <code>Y</code>의 짝꿍을 return하는 solution 함수를 완성해주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li>3 ≤ <code>X</code>, <code>Y</code>의 길이(자릿수) ≤ 3,000,000입니다.</li>
<li><code>X</code>, <code>Y</code>는 0으로 시작하지 않습니다.</li>
<li><code>X</code>, <code>Y</code>의 짝꿍은 상당히 큰 정수일 수 있으므로, 문자열로 반환합니다.</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>X</th>
<th>Y</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;100&quot;</td>
<td>&quot;2345&quot;</td>
<td>&quot;-1&quot;</td>
</tr>
<tr>
<td>&quot;100&quot;</td>
<td>&quot;203045&quot;</td>
<td>&quot;0&quot;</td>
</tr>
<tr>
<td>&quot;100&quot;</td>
<td>&quot;123450&quot;</td>
<td>&quot;10&quot;</td>
</tr>
<tr>
<td>&quot;12321&quot;</td>
<td>&quot;42531&quot;</td>
<td>&quot;321&quot;</td>
</tr>
<tr>
<td>&quot;5525&quot;</td>
<td>&quot;1255&quot;</td>
<td>&quot;552&quot;</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(X, Y) {
  let result = &quot;&quot;;

  // 각 숫자의 등장 횟수를 저장하는 함수
  const setting = (str) =&gt; {
    const dict = {};

    [...str].forEach((v) =&gt;
      dict[v] !== undefined ? dict[v]++ : (dict[v] = 1)
    );
    return dict;
  };

  const dictX = setting(X);
  const dictY = setting(Y);

  // 가장 큰 숫자를 구하기 위해 9부터 0까지 역순으로 순회
  for (let i = 9; i &gt;= 0; i--) {
    // 등장 횟수에서 더 적은 값만큼 숫자를 문자열에 추가
    if (dictX[i] &amp;&amp; dictY[i])
      result += String(i).repeat(Math.min(dictX[i], dictY[i]));
  }

  // 결과가 0으로만 구성된 경우 return &quot;0&quot;
  if (result[0] === &quot;0&quot;) return &quot;0&quot;;
  return result.length &gt; 0 ? result : &quot;-1&quot;;
}</code></pre>
<p>처음에 테스트 케이스 06 ~ 15까지 실패하였는데 큰 정수의 지수화를 고려하지 못하였다.
<code>result[0]</code> 값을 통해 분기처리를 달리하였다.</p>
<pre><code class="language-js">// 헤맸던 부분 - 테스트 케이스 06 ~ 15까지 실패

/*
 제한사항 =&gt;`X`, `Y`의 짝꿍은 상당히 큰 정수일 수 있으므로, 문자열로 반환합니다.
 숫자가 너무 커서 지수화 되는 경우를 고려하지 못하였다. ex) 1e18
*/

// Before
return result.length &gt; 0 ? String(+result) : &quot;-1&quot;;

// After
if (result[0] === &quot;0&quot;) return &quot;0&quot;;
return result.length &gt; 0 ? result : &quot;-1&quot;;</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/131128">프로그래머스 - 숫자 짝꿍</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] usePathname / useSearchParams]]></title>
            <link>https://velog.io/@fervor_dev/Next.js-usePathname-useSearchParams</link>
            <guid>https://velog.io/@fervor_dev/Next.js-usePathname-useSearchParams</guid>
            <pubDate>Thu, 15 Jun 2023 08:35:18 GMT</pubDate>
            <description><![CDATA[<p>Next.js를 통해 프로젝트를 진행하던 중에 <code>NextRouter was not mounted.</code> 에러가 발생했다.
ServerComponent에 <code>useRouter</code>를 import하면서 발생한 에러이다.
공식 문서를 찾아보니 <code>usePathname</code>과 <code>useSearchParams</code>에 대한 내용이 있으니
본문에서 자세히 알아보자.</p>
<p>혹시나 ServerComponent가 뭐지? 싶다면 Next.js의 13 베타 버전에 대한 내용이니
여기서 잠깐 훑어보면 대충 짐작은 갈 것이다.</p>
<p>=&gt; <a href="https://velog.io/@fervor_dev/Next.js-ServerClient-Component">[Next.js] Server/Client Component</a></p>
<h3 id="failed-to-compile">Failed to Compile</h3>
<p>Uncaught Error: NextRouter was not mounted. <a href="https://nextjs.org/docs/messages/next-router-not-mounted">https://nextjs.org/docs/messages/next-router-not-mounted</a>
    at useRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/router.js:141:15)</p>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/8da56020-917b-4a74-b0b4-9c66fc43f7b3/image.png" alt=""></p>
<p><code>/[id]/page.tsx</code> 내에서 params 값을 가져오기 위해 <code>useRouter</code>를 import 하자마자 발생한 에러이다.</p>
<p>Server Component에서 최상단에 <code>&quot;use client&quot;;</code>만 달아준다면 모든 Hook을 사용할 수 있던게 아니었던 것인가!</p>
<p><strong>공식 문서 내용은 다음과 같다</strong></p>
<blockquote>
<p>The new useRouter hook should be imported from next/navigation and not next/router
The <code>pathname</code> string has been removed and is replaced by <code>usePathname()</code>
The <code>query</code> object has been removed and is replaced by <code>useSearchParams()</code>
<code>router.events</code> is not currently supported. See below.</p>
</blockquote>
<p>요약하자면</p>
<blockquote>
<p>이제 문자열 <code>useRouter().pathname</code>과 객체 <code>useRouter().query</code>는 마이그레이션 됐다 ㅅㄱ</p>
</blockquote>
<p>그렇다. 이제 더 이상 params 값을 호출하기 위해 <code>useRouter</code> 훅을 가져오는 것이 아니라
<code>usePathname</code>과 <code>useSearchParams</code> 훅을 가져다가 사용하면 된다.</p>
<h2 id="usepathname">usePathname</h2>
<p>현재 url을 확인할 수 있는 훅이다. 쿼리스트링 값은 제외하고 가져온다.</p>
<pre><code class="language-jsx">&#39;use client&#39;

import { usePathname } from &#39;next/navigation&#39;

export default function SearchBar() {
  const pathname = usePathname();

  // URL -&gt; `/dashboard?search=my-project`
  // `Search: /dashboard`
  return &lt;&gt;Search: {pathname}&lt;/&gt;
}</code></pre>
<table>
<thead>
<tr>
<th>URL</th>
<th>returned value</th>
</tr>
</thead>
<tbody><tr>
<td>/</td>
<td>&#39;/&#39;</td>
</tr>
<tr>
<td>/dashboard</td>
<td>&#39;/dashboard&#39;</td>
</tr>
<tr>
<td>/dashboard?v=2</td>
<td>/dashboard&#39;</td>
</tr>
<tr>
<td>/blog/hello-world</td>
<td>/blog/hello-world&#39;</td>
</tr>
</tbody></table>
<h2 id="usesearchparams">useSearchParams</h2>
<p>쿼리 스트링을 하기 위한 훅으로 아래 예시와 함께 살펴보자.</p>
<pre><code class="language-jsx">&#39;use client&#39;

import { useSearchParams } from &#39;next/navigation&#39;

export default function SearchBar() {
  const searchParams = useSearchParams()

  const getItem = searchParams.get(&#39;search&#39;)
  const hasItem = searchParams.has(&#39;search&#39;)

  // URL -&gt; `/dashboard?search=my-project`
  // get -&gt; `Search: my-project`
  // has -&gt; `true`
  return &lt;&gt;Search: {search}&lt;/&gt;0
}</code></pre>
<p><code>get</code> 메서드로 query의 value 값을 가져올 수 있고
<code>has</code> 메서드로 query 키의 존재 여부를 확인할 수 있다.</p>
<h2 id="params-값만-가져올-수는-없을까">params 값만 가져올 수는 없을까?</h2>
<p>그렇다면 <code>usePathname</code> 훅으로 기존의 params 값을 가져와야 하는가?</p>
<p>만약 <code>/movie/genre/5</code>라고 가정한다면 <code>split(&quot;/&quot;)</code>로 잘라서 사용하는 것은 굉장히 비효율적이다.</p>
<p>Stack Overflow의 한 질답에서 답을 찾을 수 있었다.</p>
<pre><code class="language-jsx">export default function Page({ params }: { params: { id: number } }) {

  // URL -&gt; `/movie/genre/5`
  // `ID: 5`
  return &lt;&gt;ID: {params.id}&lt;/&gt;;
}

// app/movie/genre/[id]/page.tsx</code></pre>
<p><code>page.tsx</code>에서 <code>params</code>라는 props를 가져올 수 있었다!
즉, 전체 url이 필요하거나 쿼리 스트링이 필요하지 않다면 Hook을 가져올 필요 없이 <code>params</code>를 props에서 가져오도록 하면 된다.</p>
<h3 id="레퍼런스">레퍼런스</h3>
<p><a href="https://nextjs.org/docs/app/api-reference/functions/use-router">Next.js - useRouter</a>
<a href="https://nextjs.org/docs/app/api-reference/functions/use-pathname">Next.js - usePathname</a>
<a href="https://nextjs.org/docs/app/api-reference/functions/use-searchparams">Next.js - useSearchParams</a>
<a href="https://stackoverflow.com/questions/74584091/how-to-get-the-current-pathname-in-the-app-directory-of-next-js-13">Stack Overflow - params</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 대충 만든 자판 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8C%80%EC%B6%A9-%EB%A7%8C%EB%93%A0-%EC%9E%90%ED%8C%90-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8C%80%EC%B6%A9-%EB%A7%8C%EB%93%A0-%EC%9E%90%ED%8C%90-JS</guid>
            <pubDate>Wed, 14 Jun 2023 10:39:46 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>휴대폰의 자판은 컴퓨터 키보드 자판과는 다르게 하나의 키에 여러 개의 문자가 할당될 수 있습니다. 키 하나에 여러 문자가 할당된 경우, 동일한 키를 연속해서 빠르게 누르면 할당된 순서대로 문자가 바뀝니다.</p>
<p>예를 들어, 1번 키에 &quot;A&quot;, &quot;B&quot;, &quot;C&quot; 순서대로 문자가 할당되어 있다면 1번 키를 한 번 누르면 &quot;A&quot;, 두 번 누르면 &quot;B&quot;, 세 번 누르면 &quot;C&quot;가 되는 식입니다.</p>
<p>같은 규칙을 적용해 아무렇게나 만든 휴대폰 자판이 있습니다. 이 휴대폰 자판은 키의 개수가 1개부터 최대 100개까지 있을 수 있으며, 특정 키를 눌렀을 때 입력되는 문자들도 무작위로 배열되어 있습니다. 또, 같은 문자가 자판 전체에 여러 번 할당된 경우도 있고, 키 하나에 같은 문자가 여러 번 할당된 경우도 있습니다. 심지어 아예 할당되지 않은 경우도 있습니다. 따라서 몇몇 문자열은 작성할 수 없을 수도 있습니다.</p>
<p>이 휴대폰 자판을 이용해 특정 문자열을 작성할 때, 키를 최소 몇 번 눌러야 그 문자열을 작성할 수 있는지 알아보고자 합니다.</p>
<p>1번 키부터 차례대로 할당된 문자들이 순서대로 담긴 문자열배열 <code>keymap</code>과 입력하려는 문자열들이 담긴 문자열 배열 <code>targets</code>가 주어질 때, 각 문자열을 작성하기 위해 키를 최소 몇 번씩 눌러야 하는지 순서대로 배열에 담아 return 하는 solution 함수를 완성해 주세요.</p>
<p>단, 목표 문자열을 작성할 수 없을 때는 -1을 저장합니다.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li><p>1 ≤ <code>keymap</code>의 길이 ≤ 100</p>
<ul>
<li>1 ≤ <code>keymap</code>의 원소의 길이 ≤ 100</li>
<li><code>keymap[i]</code>는 i + 1번 키를 눌렀을 때 순서대로 바뀌는 문자를 의미합니다.<ul>
<li>예를 들어 <code>keymap[0]</code> = &quot;ABACD&quot; 인 경우 1번 키를 한 번 누르면 A, 두 번 누르면 B, 세 번 누르면 A 가 됩니다.<ul>
<li><code>keymap</code>의 원소의 길이는 서로 다를 수 있습니다.</li>
<li><code>keymap</code>의 원소는 알파벳 대문자로만 이루어져 있습니다.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>1 ≤ <code>targets</code>의 길이 ≤ 100</p>
<ul>
<li>1 ≤ <code>targets</code>의 원소의 길이 ≤ 100</li>
<li><code>targets</code>의 원소는 알파벳 대문자로만 이루어져 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>keymap</th>
<th>targets</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[&quot;ABACD&quot;, &quot;BCEFD&quot;]</td>
<td>[&quot;ABCD&quot;,&quot;AABB&quot;]</td>
<td>[9, 4]</td>
</tr>
<tr>
<td>[&quot;AA&quot;]</td>
<td>[&quot;B&quot;]</td>
<td>[-1]</td>
</tr>
<tr>
<td>[&quot;AGZ&quot;, &quot;BSSS&quot;]</td>
<td>[&quot;ASA&quot;,&quot;BGZ&quot;]</td>
<td>[4, 6]</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(keymap, targets) {
  return targets.map((target) =&gt;
    target.split(&quot;&quot;).reduce((acc, cur) =&gt; {
      // 현재까지 누른 키의 횟수(acc)가 -1인 경우
      // 이후 문자열 작성이 불가능 =&gt; return -1
      if (acc === -1) return -1;

      // 현재 문자(cur)가 할당된 키들 서치
      const temp = keymap
        .map((key) =&gt; key.indexOf(cur))
        .filter((val) =&gt; val !== -1);

      // 할당된 키가 없는 경우, 문자열 작성이 불가능 =&gt; return -1
      // 있는 경우 할당된 키 중 최솟값를 선택, index는 0부터 시작하므로 값에 +1
      return temp.length === 0 ? -1 : acc + Math.min(...temp) + 1;
    }, 0)
  );
}</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/160586">프로그래머스 - 대충 만든 자판</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 호텔 대실 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%98%B8%ED%85%94-%EB%8C%80%EC%8B%A4-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%98%B8%ED%85%94-%EB%8C%80%EC%8B%A4-JS</guid>
            <pubDate>Mon, 12 Jun 2023 11:02:00 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>호텔을 운영 중인 코니는 최소한의 객실만을 사용하여 예약 손님들을 받으려고 합니다. 한 번 사용한 객실은 퇴실 시간을 기준으로 10분간 청소를 하고 다음 손님들이 사용할 수 있습니다.
예약 시각이 문자열 형태로 담긴 2차원 배열 <code>book_time</code>이 매개변수로 주어질 때, 코니에게 필요한 최소 객실의 수를 return 하는 solution 함수를 완성해주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li>1 ≤ <code>book_time</code>의 길이 ≤ 1,000<ul>
<li><code>book_time[i]</code>는 [&quot;HH:MM&quot;, &quot;HH:MM&quot;]의 형태로 이루어진 배열입니다<ul>
<li>[대실 시작 시각, 대실 종료 시각] 형태입니다.</li>
</ul>
</li>
<li>시각은 HH:MM 형태로 24시간 표기법을 따르며, &quot;00:00&quot; 부터 &quot;23:59&quot; 까지로 주어집니다.<ul>
<li>예약 시각이 자정을 넘어가는 경우는 없습니다.</li>
<li>시작 시각은 항상 종료 시각보다 빠릅니다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>book_time</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[[&quot;15:00&quot;, &quot;17:00&quot;], [&quot;16:40&quot;, &quot;18:20&quot;], [&quot;14:20&quot;, &quot;15:20&quot;], [&quot;14:10&quot;, &quot;19:20&quot;], [&quot;18:20&quot;, &quot;21:20&quot;]]</td>
<td>3</td>
</tr>
<tr>
<td>[[&quot;09:10&quot;, &quot;10:10&quot;], [&quot;10:20&quot;, &quot;12:20&quot;]]</td>
<td>1</td>
</tr>
<tr>
<td>[[&quot;10:20&quot;, &quot;12:30&quot;], [&quot;10:20&quot;, &quot;12:30&quot;], [&quot;10:20&quot;, &quot;12:30&quot;]]</td>
<td>3</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(book_time) {
  let room = [];

  // 오름차순 정렬 후 순회
  book_time.sort().forEach(([start, end]) =&gt; {
    const startTime = toMinute(start);
    const endTime = toMinute(end) + 10;

    // 시작 시간(startTime)보다 작은 값 서치 =&gt; 사용이 끝난 방 확인
    const idx = room.findIndex((v) =&gt; v &lt;= startTime);

    // 없다면 종료 시간(endTime) push =&gt; 방 추가
    // 있다면 해당 room의 종료 시간(endTime) 갱신 =&gt; 사용이 끝난 방에 들어감
    if (idx === -1) room.push(endTime);
    else room[idx] = endTime;
  });

  return room.length;
}

// 분으로 통일하는 함수
function toMinute(str) {
  const [hour, minute] = str.split(&quot;:&quot;);
  return hour * 60 + +minute;
}</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/155651">프로그래머스 - 호텔 대실</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 덧칠하기 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%A7%EC%B9%A0%ED%95%98%EA%B8%B0-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%8D%A7%EC%B9%A0%ED%95%98%EA%B8%B0-JS</guid>
            <pubDate>Sun, 11 Jun 2023 10:52:46 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>어느 학교에 페인트가 칠해진 길이가 <code>n</code>미터인 벽이 있습니다. 벽에 동아리 · 학회 홍보나 회사 채용 공고 포스터 등을 게시하기 위해 테이프로 붙였다가 철거할 때 떼는 일이 많고 그 과정에서 페인트가 벗겨지곤 합니다. 페인트가 벗겨진 벽이 보기 흉해져 학교는 벽에 페인트를 덧칠하기로 했습니다.</p>
<p>넓은 벽 전체에 페인트를 새로 칠하는 대신, 구역을 나누어 일부만 페인트를 새로 칠 함으로써 예산을 아끼려 합니다. 이를 위해 벽을 1미터 길이의 구역 <code>n</code>개로 나누고, 각 구역에 왼쪽부터 순서대로 1번부터 n번까지 번호를 붙였습니다. 그리고 페인트를 다시 칠해야 할 구역들을 정했습니다.</p>
<p>벽에 페인트를 칠하는 롤러의 길이는 <code>m</code>미터이고, 롤러로 벽에 페인트를 한 번 칠하는 규칙은 다음과 같습니다.</p>
<ul>
<li>롤러가 벽에서 벗어나면 안 됩니다.</li>
<li>구역의 일부분만 포함되도록 칠하면 안 됩니다.</li>
</ul>
<p>즉, 롤러의 좌우측 끝을 구역의 경계선 혹은 벽의 좌우측 끝부분에 맞춘 후 롤러를 위아래로 움직이면서 벽을 칠합니다. 현재 페인트를 칠하는 구역들을 완전히 칠한 후 벽에서 롤러를 떼며, 이를 벽을 한 번 칠했다고 정의합니다.</p>
<p>한 구역에 페인트를 여러 번 칠해도 되고 다시 칠해야 할 구역이 아닌 곳에 페인트를 칠해도 되지만 다시 칠하기로 정한 구역은 적어도 한 번 페인트칠을 해야 합니다. 예산을 아끼기 위해 다시 칠할 구역을 정했듯 마찬가지로 롤러로 페인트칠을 하는 횟수를 최소화하려고 합니다.</p>
<p>정수 <code>n</code>, <code>m</code>과 다시 페인트를 칠하기로 정한 구역들의 번호가 담긴 정수 배열 section이 매개변수로 주어질 때 롤러로 페인트칠해야 하는 최소 횟수를 return 하는 solution 함수를 작성해 주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li>1 ≤ <code>m</code> ≤ <code>n</code> ≤ 100,000</li>
<li>1 ≤ <code>section</code>의 길이 ≤ <code>n</code><ul>
<li>1 ≤ <code>section</code>의 원소 ≤ <code>n</code></li>
<li><code>section</code>의 원소는 페인트를 다시 칠해야 하는 구역의 번호입니다.</li>
<li><code>section</code>에서 같은 원소가 두 번 이상 나타나지 않습니다.</li>
<li><code>section</code>의 원소는 오름차순으로 정렬되어 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>n</th>
<th>m</th>
<th>section</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>8</td>
<td>4</td>
<td>[2, 3, 6]</td>
<td>2</td>
</tr>
<tr>
<td>5</td>
<td>4</td>
<td>[1, 3]</td>
<td>1</td>
</tr>
<tr>
<td>4</td>
<td>1</td>
<td>[1, 2, 3, 4]</td>
<td>4</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(n, m, section) {
  let result = 0;
  let last = -1;

  section.forEach((v) =&gt; {
    // 현재 영역(v)이 마지막에 칠한 영역(last)보다 크다면
    if (v &gt; last) {
      // 마지막 칠한 영역(last) 갱신
      last = v + m - 1;
      result++;
    }
  });

  return result;
}
</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/161989">프로그래머스 - 덧칠하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 요격 시스템 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9A%94%EA%B2%A9-%EC%8B%9C%EC%8A%A4%ED%85%9C-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9A%94%EA%B2%A9-%EC%8B%9C%EC%8A%A4%ED%85%9C-JS</guid>
            <pubDate>Fri, 09 Jun 2023 11:12:37 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>A 나라가 B 나라를 침공하였습니다. B 나라의 대부분의 전략 자원은 아이기스 군사 기지에 집중되어 있기 때문에 A 나라는 B 나라의 아이기스 군사 기지에 융단폭격을 가했습니다.
A 나라의 공격에 대항하여 아이기스 군사 기지에서는 무수히 쏟아지는 폭격 미사일들을 요격하려고 합니다. 이곳에는 백발백중을 자랑하는 요격 시스템이 있지만 운용 비용이 상당하기 때문에 미사일을 최소로 사용해서 모든 폭격 미사일을 요격하려 합니다.
A 나라와 B 나라가 싸우고 있는 이 세계는 2 차원 공간으로 이루어져 있습니다. A 나라가 발사한 폭격 미사일은 x 축에 평행한 직선 형태의 모양이며 개구간을 나타내는 정수 쌍 (s, e) 형태로 표현됩니다. B 나라는 특정 x 좌표에서 y 축에 수평이 되도록 미사일을 발사하며, 발사된 미사일은 해당 x 좌표에 걸쳐있는 모든 폭격 미사일을 관통하여 한 번에 요격할 수 있습니다. 단, 개구간 (s, e)로 표현되는 폭격 미사일은 s와 e에서 발사하는 요격 미사일로는 요격할 수 없습니다. 요격 미사일은 실수인 x 좌표에서도 발사할 수 있습니다.
각 폭격 미사일의 x 좌표 범위 목록 <code>targets</code>이 매개변수로 주어질 때, 모든 폭격 미사일을 요격하기 위해 필요한 요격 미사일 수의 최솟값을 return 하도록 solution 함수를 완성해 주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li>1 ≤ <code>targets</code>의 길이 ≤ 500,000</li>
<li><code>targets</code>의 각 행은 [s,e] 형태입니다.<ul>
<li>이는 한 폭격 미사일의 x 좌표 범위를 나타내며, 개구간 (s, e)에서 요격해야 합니다.</li>
<li>0 ≤ s &lt; e ≤ 100,000,000</li>
</ul>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>targets</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[[4,5],[4,8],[10,14],[11,13],[5,12],[3,7],[1,4]]</td>
<td>3</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(targets) {
  let result = 0;
  let prev = -1;

  // end 값을 기준으로 오름차순 정렬
  // =&gt; 먼저 요격해야 할 미사일이 배열의 맨 앞으로 옴
  targets
    .sort((a, b) =&gt; a[1] - b[1])
    .forEach((v) =&gt; {
      const [start, end] = v;

      // 이전에 요격한 미사일 끝 값 &lt;= 현재 요소의 시작 값
      if (prev &lt;= start) {
        // 요격한 미사일 끝 값 갱신
        prev = end;
        result++;
      }
    });

  return result;
}</code></pre>
<h3 id="🧱-알고리즘">🧱 알고리즘</h3>
<p><a href="https://en.wikipedia.org/wiki/Greedy_algorithm">그리디 알고리즘(Greedy algorithm) - Wikipedia</a></p>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/181188">프로그래머스 - 요격 시스템</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] N진수 게임 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-N%EC%A7%84%EC%88%98-%EA%B2%8C%EC%9E%84-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-N%EC%A7%84%EC%88%98-%EA%B2%8C%EC%9E%84-JS</guid>
            <pubDate>Wed, 07 Jun 2023 11:50:27 GMT</pubDate>
            <description><![CDATA[<h3 id="📢-문제-설명">📢 문제 설명</h3>
<p>튜브가 활동하는 코딩 동아리에서는 전통적으로 해오는 게임이 있다. 이 게임은 여러 사람이 둥글게 앉아서 숫자를 하나씩 차례대로 말하는 게임인데, 규칙은 다음과 같다.</p>
<ol>
<li>숫자를 0부터 시작해서 차례대로 말한다. 첫 번째 사람은 0, 두 번째 사람은 1, … 열 번째 사람은 9를 말한다.</li>
<li>10 이상의 숫자부터는 한 자리씩 끊어서 말한다. 즉 열한 번째 사람은 10의 첫 자리인 1, 열두 번째 사람은 둘째 자리인 0을 말한다.</li>
</ol>
<p>이렇게 게임을 진행할 경우,
<code>0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0, 1, 1, 1, 2, 1, 3, 1, 4, …</code>
순으로 숫자를 말하면 된다.</p>
<p>한편 코딩 동아리 일원들은 컴퓨터를 다루는 사람답게 이진수로 이 게임을 진행하기도 하는데, 이 경우에는
<code>0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, …</code>
순으로 숫자를 말하면 된다.</p>
<p>이진수로 진행하는 게임에 익숙해져 질려가던 사람들은 좀 더 난이도를 높이기 위해 이진법에서 십육진법까지 모든 진법으로 게임을 진행해보기로 했다. 숫자 게임이 익숙하지 않은 튜브는 게임에 져서 벌칙을 받는 굴욕을 피하기 위해, 자신이 말해야 하는 숫자를 스마트폰에 미리 출력해주는 프로그램을 만들려고 한다. 튜브의 프로그램을 구현하라.</p>
<h3 id="🧾-입출력-예">🧾 입출력 예</h3>
<h4 id="입력-형식">입력 형식</h4>
<p>진법 <code>n</code>, 미리 구할 숫자의 갯수 <code>t</code>, 게임에 참가하는 인원 <code>m</code>, 튜브의 순서 <code>p</code> 가 주어진다.</p>
<ul>
<li>2 ≦ <code>n</code> ≦ 16</li>
<li>0 ＜ <code>t</code> ≦ 1000</li>
<li>2 ≦ <code>m</code> ≦ 100</li>
<li>1 ≦ <code>p</code> ≦ <code>m</code></li>
</ul>
<h4 id="출력-형식">출력 형식</h4>
<p>튜브가 말해야 하는 숫자 <code>t</code>개를 공백 없이 차례대로 나타낸 문자열. 단, <code>10</code><del><code>15</code>는 각각 대문자 <code>A</code></del><code>F</code>로 출력한다.</p>
<h4 id="예제">예제</h4>
<table>
<thead>
<tr>
<th>n</th>
<th>t</th>
<th>m</th>
<th>p</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>2</td>
<td>4</td>
<td>2</td>
<td>1</td>
<td>&quot;0111&quot;</td>
</tr>
<tr>
<td>16</td>
<td>16</td>
<td>2</td>
<td>1</td>
<td>&quot;02468ACE11111111&quot;</td>
</tr>
<tr>
<td>16</td>
<td>16</td>
<td>2</td>
<td>2</td>
<td>&quot;13579BDF01234567&quot;</td>
</tr>
</tbody></table>
<h3 id="🔑-풀이">🔑 풀이</h3>
<pre><code class="language-js">function solution(n, t, m, p) {
  let str = &quot;&quot;;

  // (인원 수 * 미리 구할 수)만큼 반복
  for (let i = 0; i &lt; m * t; i++) {
    str += i.toString(n).toUpperCase();
  }

  let result = &quot;&quot;;
  let cnt = 0;

  // 미리 구할 수까지 반복
  while (result.length &lt; t) {
    // 인원 수만큼 문자열 커팅
    const temp = str.substring(cnt, cnt + m);

    // (순서-1)번째 값을 문자열에 추가 -&gt; index는 0부터
    result += temp[p - 1];
    cnt += m;
  }

  return result;
}
</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/17687">프로그래머스 - N진수 게임</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 주차 요금 계산 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%B0%A8-%EC%9A%94%EA%B8%88-%EA%B3%84%EC%82%B0</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%A3%BC%EC%B0%A8-%EC%9A%94%EA%B8%88-%EA%B3%84%EC%82%B0</guid>
            <pubDate>Sun, 04 Jun 2023 08:24:18 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>주차장의 요금표와 차량이 들어오고(입차) 나간(출차) 기록이 주어졌을 때, 차량별로 주차 요금을 계산하려고 합니다. 아래는 하나의 예시를 나타냅니다.</p>
<ul>
<li>요금표</li>
</ul>
<table>
<thead>
<tr>
<th>기본 시간(분)</th>
<th>기본 요금(원)</th>
<th>단위 시간(분)</th>
<th>단위 요금(원)</th>
</tr>
</thead>
<tbody><tr>
<td>180</td>
<td>5000</td>
<td>10</td>
<td>600</td>
</tr>
</tbody></table>
<ul>
<li>입/출차 기록</li>
</ul>
<table>
<thead>
<tr>
<th>시각(시:분)</th>
<th>차량 번호</th>
<th>내역</th>
</tr>
</thead>
<tbody><tr>
<td>05:34</td>
<td>5961</td>
<td>입차</td>
</tr>
<tr>
<td>06:00</td>
<td>0000</td>
<td>입차</td>
</tr>
<tr>
<td>06:34</td>
<td>0000</td>
<td>출차</td>
</tr>
<tr>
<td>07:59</td>
<td>5961</td>
<td>출차</td>
</tr>
<tr>
<td>07:59</td>
<td>0148</td>
<td>입차</td>
</tr>
<tr>
<td>18:59</td>
<td>0000</td>
<td>입차</td>
</tr>
<tr>
<td>19:09</td>
<td>0148</td>
<td>출차</td>
</tr>
<tr>
<td>22:59</td>
<td>5961</td>
<td>입차</td>
</tr>
<tr>
<td>23:00</td>
<td>5961</td>
<td>출차</td>
</tr>
</tbody></table>
<ul>
<li>자동차별 주차 요금</li>
</ul>
<table>
<thead>
<tr>
<th>차량 번호</th>
<th>누적 주차 시간(분)</th>
<th>주차 요금(원)</th>
</tr>
</thead>
<tbody><tr>
<td>0000</td>
<td>34 + 300 = 334</td>
<td>5000 + ⌈(334 - 180) / 10⌉ x 600 = 14600</td>
</tr>
<tr>
<td>0148</td>
<td>670</td>
<td>5000 +⌈(670 - 180) / 10⌉x 600 = 34400</td>
</tr>
<tr>
<td>5961</td>
<td>145 + 1 = 146</td>
<td>50001</td>
</tr>
</tbody></table>
<ul>
<li><p>어떤 차량이 입차된 후에 출차된 내역이 없다면, 23:59에 출차된 것으로 간주합니다.</p>
<ul>
<li><code>0000</code>번 차량은 18:59에 입차된 이후, 출차된 내역이 없습니다. 따라서, 23:59에 출차된 것으로 간주합니다.</li>
</ul>
</li>
<li><p>00:00부터 23:59까지의 입/출차 내역을 바탕으로 차량별 누적 주차 시간을 계산하여 요금을 일괄로 정산합니다.</p>
</li>
<li><p>누적 주차 시간이 <code>기본 시간</code>이하라면, <code>기본 요금</code>을 청구합니다.</p>
</li>
<li><p>누적 주차 시간이 <code>기본 시간</code>을 초과하면, <code>기본 요금</code>에 더해서, 초과한 시간에 대해서 <code>단위 시간</code> 마다 단위 요금을 청구합니다.</p>
<ul>
<li>초과한 시간이 <code>단위 시간</code>으로 나누어 떨어지지 않으면, <code>올림</code>합니다.</li>
<li><code>⌈</code>a<code>⌉</code> : a보다 작지 않은 최소의 정수를 의미합니다. 즉, <code>올림</code>을 의미합니다.</li>
</ul>
</li>
</ul>
<p>주차 요금을 나타내는 정수 배열 <code>fees</code>, 자동차의 입/출차 내역을 나타내는 문자열 배열 <code>records</code>가 매개변수로 주어집니다. <strong>차량 번호가 작은 자동차부터</strong> 청구할 주차 요금을 차례대로 정수 배열에 담아서 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li><p><code>fees</code>의 길이 = 4</p>
<ul>
<li>fees[0] = <code>기본 시간(분)</code></li>
<li>1 ≤ fees[0] ≤ 1,439</li>
<li>fees[1] = <code>기본 요금(원)</code></li>
<li>0 ≤ fees[1] ≤ 100,000</li>
<li>fees[2] = <code>단위 시간(분)</code></li>
<li>1 ≤ fees[2] ≤ 1,439</li>
<li>fees[3] = <code>단위 요금(원)</code></li>
<li>1 ≤ fees[3] ≤ 10,000</li>
</ul>
</li>
<li><p>1 ≤ <code>records</code>의 길이 ≤ 1,000</p>
<ul>
<li><p><code>records</code>의 각 원소는 <code>&quot;시각 차량번호 내역&quot;</code> 형식의 문자열입니다.</p>
</li>
<li><p><code>시각</code>, <code>차량번호</code>, <code>내역</code>은 하나의 공백으로 구분되어 있습니다.</p>
</li>
<li><p><code>시각</code>은 차량이 입차되거나 출차된 시각을 나타내며, <code>HH:MM</code> 형식의 길이 5인 문자열입니다.</p>
<ul>
<li><code>HH:MM</code>은 00:00부터 23:59까지 주어집니다.</li>
<li>잘못된 시각(&quot;25:22&quot;, &quot;09:65&quot; 등)은 입력으로 주어지지 않습니다.</li>
</ul>
</li>
<li><p><code>차량번호</code>는 자동차를 구분하기 위한, `0&#39;~&#39;9&#39;로 구성된 길이 4인 문자열입니다.</p>
</li>
<li><p><code>내역</code>은 길이 2 또는 3인 문자열로, <code>IN</code> 또는 <code>OUT</code>입니다. <code>IN</code>은 입차를, <code>OUT</code>은 출차를 의미합니다.</p>
</li>
<li><p><code>records</code>의 원소들은 시각을 기준으로 오름차순으로 정렬되어 주어집니다.</p>
</li>
<li><p><code>records</code>는 하루 동안의 입/출차된 기록만 담고 있으며, 입차된 차량이 다음날 출차되는 경우는 입력으로 주어지지 않습니다.</p>
</li>
<li><p>같은 시각에, 같은 차량번호의 내역이 2번 이상 나타내지 않습니다.</p>
</li>
<li><p>마지막 시각(23:59)에 입차되는 경우는 입력으로 주어지지 않습니다.</p>
</li>
<li><p>아래의 예를 포함하여, 잘못된 입력은 주어지지 않습니다.</p>
<ul>
<li>주차장에 없는 차량이 출차되는 경우<ul>
<li>주차장에 이미 있는 차량(차량번호가 같은 차량)이 다시 입차되는 경우</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>fees</th>
<th>records</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[180, 5000, 10, 600]</td>
<td>[&quot;05:34 5961 IN&quot;, &quot;06:00 0000 IN&quot;, &quot;06:34 0000 OUT&quot;, &quot;07:59 5961 OUT&quot;, &quot;07:59 0148 IN&quot;, &quot;18:59 0000 IN&quot;, &quot;19:09 0148 OUT&quot;, &quot;22:59 5961 IN&quot;, &quot;23:00 5961 OUT&quot;]</td>
<td>[14600, 34400, 5000]</td>
</tr>
<tr>
<td>[120, 0, 60, 591]</td>
<td>[&quot;16:00 3961 IN&quot;,&quot;16:00 0202 IN&quot;,&quot;18:00 3961 OUT&quot;,&quot;18:00 0202 OUT&quot;,&quot;23:58 3961 IN&quot;]</td>
<td>[0, 591]</td>
</tr>
<tr>
<td>[1, 461, 1, 10]</td>
<td>[&quot;00:00 1234 IN&quot;]    [14841]</td>
<td></td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(fees, records) {
  const parking = {};

  records.forEach((v) =&gt; {
    const [time, id, status] = v.split(&quot; &quot;);
    const [hour, minute] = time.split(&quot;:&quot;);
    // time을 분으로 통일
    const replaceTime = hour * 60 + +minute;

    // Object에 입고할 차량이 없다면 등록
    if (!parking[id]) {
      parking[id] = { time: 0, id };
    }

    // 현재 상태 기록
    parking[id].status = status;

    // 마지막으로 입고한 시간 기록
    if (status === &quot;IN&quot;) {
      parking[id].lastInTime = replaceTime;
      return;
    }

    // 주차 시간에 += (현재 입고 시간 - 마지막 입고 시간)
    parking[id].time += replaceTime - parking[id].lastInTime;
  });

  // 차량 번호가 낮은 순서로 비용 return
  return Object.values(parking)
    .sort((a, b) =&gt; a.id - b.id)
    .map((v) =&gt; {
      // 최대 시간은 24 * 60 -1 = 1439 (분)
      if (v.status === &quot;IN&quot;) v.time += 1439 - v.lastInTime;
      // 기본 시간 이내라면 기본 요금
      if (fees[0] &gt; v.time) return fees[1];
      // 기본 요금 + (( 주차 시간 - 기본 시간 ) / 단위 시간) * 요금
      return fees[1] + Math.ceil((v.time - fees[0]) / fees[2]) * fees[3];
    });
}</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/92341">프로그래머스 - 주차 요금 계산</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 이중우선순위큐 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9D%B4%EC%A4%91%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84%ED%81%90-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%9D%B4%EC%A4%91%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84%ED%81%90-JS</guid>
            <pubDate>Thu, 01 Jun 2023 06:42:03 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>이중 우선순위 큐는 다음 연산을 할 수 있는 자료구조를 말합니다.</p>
<table>
<thead>
<tr>
<th>명령어</th>
<th>수신 탑(높이)</th>
</tr>
</thead>
<tbody><tr>
<td>I 숫자</td>
<td>큐에 주어진 숫자를 삽입합니다.</td>
</tr>
<tr>
<td>D 1</td>
<td>큐에서 최댓값을 삭제합니다.</td>
</tr>
<tr>
<td>D -1</td>
<td>큐에서 최솟값을 삭제합니다.</td>
</tr>
</tbody></table>
<p>이중 우선순위 큐가 할 연산 <code>operations</code>가 매개변수로 주어질 때, 모든 연산을 처리한 후 큐가 비어있으면 [0,0] 비어있지 않으면 [최댓값, 최솟값]을 return 하도록 solution 함수를 구현해주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li><p><code>operations</code>는 길이가 1 이상 1,000,000 이하인 문자열 배열입니다.</p>
</li>
<li><p><code>operations</code>의 원소는 큐가 수행할 연산을 나타냅니다.</p>
<ul>
<li>원소는 “명령어 데이터” 형식으로 주어집니다</li>
<li>최댓값/최솟값을 삭제하는 연산에서 최댓값/최솟값이 둘 이상인 경우, 하나만 삭제합니다.</li>
</ul>
</li>
<li><p>빈 큐에 데이터를 삭제하라는 연산이 주어질 경우, 해당 연산은 무시합니다.</p>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>operations</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[&quot;I 16&quot;, &quot;I -5643&quot;, &quot;D -1&quot;, &quot;D 1&quot;, &quot;D 1&quot;, &quot;I 123&quot;, &quot;D -1&quot;]</td>
<td>[0,0]</td>
</tr>
<tr>
<td>[&quot;I -45&quot;, &quot;I 653&quot;, &quot;D 1&quot;, &quot;I -642&quot;, &quot;I 45&quot;, &quot;I 97&quot;, &quot;D 1&quot;, &quot;D -1&quot;, &quot;I 333&quot;]</td>
<td>[333, -45]</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(operations) {
  const queue = [];

  operations.forEach((v) =&gt; {
    const [command, num] = v.split(&quot; &quot;);

    // 명령어 I, 데이터 삽입 후 정렬
    if (command === &quot;I&quot;) {
      queue.push(+num);
      queue.sort((a, b) =&gt; b - a);
      return;
    }

    // 명령어 D, 빈 큐에 데이터 삭제 연산 무시
    if (!queue.length) return;

    // 최댓값/최솟값 제거
    if (num === &quot;1&quot;) queue.shift();
    if (num === &quot;-1&quot;) queue.pop();
  });

  return queue.length ? [queue[0], queue[queue.length - 1]] : [0, 0];
}</code></pre>
<blockquote>
<p>index 값을 구하고 splice() 메서드를 활용하는 방식도 있으나, 시간복잡도상 배열을 자르고 새로 생성하는 것보다 정렬이 더 빠를 것 같아 정렬로 풀이했습니다.</p>
</blockquote>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42628">프로그래머스 - 이중우선순위큐</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] k진수에서 소수 개수 구하기 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-k%EC%A7%84%EC%88%98%EC%97%90%EC%84%9C-%EC%86%8C%EC%88%98-%EA%B0%9C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-k%EC%A7%84%EC%88%98%EC%97%90%EC%84%9C-%EC%86%8C%EC%88%98-%EA%B0%9C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0-JS</guid>
            <pubDate>Mon, 29 May 2023 09:18:50 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>양의 정수 <code>n</code>이 주어집니다. 이 숫자를 <code>k</code>진수로 바꿨을 때, 변환된 수 안에 아래 조건에 맞는 소수(Prime number)가 몇 개인지 알아보려 합니다.</p>
<ul>
<li><code>0P0</code>처럼 소수 양쪽에 0이 있는 경우</li>
<li><code>P0</code>처럼 소수 오른쪽에만 0이 있고 왼쪽에는 아무것도 없는 경우</li>
<li><code>0P</code>처럼 소수 왼쪽에만 0이 있고 오른쪽에는 아무것도 없는 경우</li>
<li><code>P</code>처럼 소수 양쪽에 아무것도 없는 경우</li>
<li>단, <code>P</code>는 각 자릿수에 0을 포함하지 않는 소수입니다.<ul>
<li>예를 들어, 101은 <code>P</code>가 될 수 없습니다.</li>
</ul>
</li>
</ul>
<p>예를 들어, 437674을 3진수로 바꾸면 <code>211</code>0<code>2</code>01010<code>11</code>입니다. 여기서 찾을 수 있는 조건에 맞는 소수는 왼쪽부터 순서대로 211, 2, 11이 있으며, 총 3개입니다. (211, 2, 11을 <code>k</code>진법으로 보았을 때가 아닌, 10진법으로 보았을 때 소수여야 한다는 점에 주의합니다.) 211은 <code>P0</code> 형태에서 찾을 수 있으며, 2는 <code>0P0</code>에서, 11은 <code>0P</code>에서 찾을 수 있습니다.</p>
<p>정수 <code>n</code>과 <code>k</code>가 매개변수로 주어집니다. <code>n</code>을 <code>k</code>진수로 바꿨을 때, 변환된 수 안에서 찾을 수 있는 <strong>위 조건에 맞는 소수</strong>의 개수를 return 하도록 solution 함수를 완성해 주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li>1 ≤ <code>n</code> ≤ 1,000,000</li>
<li>3 ≤ <code>k</code> ≤ 10</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>n</th>
<th>k</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>437674</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>110011</td>
<td>10</td>
<td>2</td>
</tr>
<tr>
<td>### 🔑풀이</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<pre><code class="language-js">function solution(n, k) {
  /*
    1. toString(N) 메서드로 진수 변환
    2. 0을 기준으로 split() =&gt; 단, `P`는 각 자릿수에 0을 포함하지 않는 소수입니다.
    3. 다시 10진수로 변경
    4. 소수 판단 후 개수 return
    */
  const numStr = n
    .toString(k)
    .split(&quot;0&quot;)
    .map((v) =&gt; v.toString(10));
  return numStr.filter((v) =&gt; isPrime(v)).length;
}

function isPrime(num) {
  if (num &lt;= 1) return false;

  // 제곱근을 이용한 소수 판별법 (약수끼리의 곱은 대칭으로 이루어지기 때문)
  for (let i = 2; i &lt;= Math.sqrt(num); i++) {
    if (num % i === 0) return false;
  }

  return true;
}</code></pre>
<h3 id="🧱-레퍼런스">🧱 레퍼런스</h3>
<p><a href="https://ko.wikipedia.org/wiki/%EC%86%8C%EC%88%98%ED%8C%90%EB%B3%84%EB%B2%95#%EC%A7%81%EC%A0%91_%EB%82%98%EB%88%84%EA%B8%B0">소수 판별법 - Wikipedia</a></p>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/49994">프로그래머스 - 방문 기록</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 방문 기록 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%A9%EB%AC%B8-%EA%B8%B0%EB%A1%9D-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%A9%EB%AC%B8-%EA%B8%B0%EB%A1%9D-JS</guid>
            <pubDate>Thu, 25 May 2023 08:57:58 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>게임 캐릭터를 4가지 명령어를 통해 움직이려 합니다. 명령어는 다음과 같습니다.</p>
<ul>
<li>U: 위쪽으로 한 칸 가기</li>
<li>D: 아래쪽으로 한 칸 가기</li>
<li>R: 오른쪽으로 한 칸 가기</li>
<li>L: 왼쪽으로 한 칸 가기</li>
</ul>
<p>캐릭터는 좌표평면의 (0, 0) 위치에서 시작합니다. 좌표평면의 경계는 왼쪽 위(-5, 5), 왼쪽 아래(-5, -5), 오른쪽 위(5, 5), 오른쪽 아래(5, -5)로 이루어져 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/071ce7b7-0c7e-451b-9c22-fb1e79359281/image.png" alt=""></p>
<p>예를 들어, &quot;ULURRDLLU&quot;로 명령했다면
<img src="https://velog.velcdn.com/images/fervor_dev/post/a7db45b0-57c6-405a-8fb1-19827ec63ae7/image.png" alt=""></p>
<ul>
<li>1번 명령어부터 7번 명령어까지 다음과 같이 움직입니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/c130cb47-29f3-4687-b739-1c170623ce57/image.png" alt=""></p>
<ul>
<li>8번 명령어부터 9번 명령어까지 다음과 같이 움직입니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/4b123f68-f24f-44e0-95d0-f98512ab4a51/image.png" alt=""></p>
<p>이때, 우리는 게임 캐릭터가 지나간 길 중 캐릭터가 처음 걸어본 길의 길이를 구하려고 합니다. 예를 들어 위의 예시에서 게임 캐릭터가 움직인 길이는 9이지만, 캐릭터가 처음 걸어본 길의 길이는 7이 됩니다. (8, 9번 명령어에서 움직인 길은 2, 3번 명령어에서 이미 거쳐 간 길입니다)</p>
<p>단, 좌표평면의 경계를 넘어가는 명령어는 무시합니다.</p>
<p>예를 들어, &quot;LULLLLLLU&quot;로 명령했다면</p>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/a9effaf9-c89e-4314-97b2-818e1acf1906/image.png" alt=""></p>
<ul>
<li>1번 명령어부터 6번 명령어대로 움직인 후, 7, 8번 명령어는 무시합니다. 다시 9번 명령어대로 움직입니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/fervor_dev/post/12d8f123-8d26-447f-8ee9-0c2f458a769f/image.png" alt=""></p>
<p>이때 캐릭터가 처음 걸어본 길의 길이는 7이 됩니다.</p>
<p>명령어가 매개변수 dirs로 주어질 때, 게임 캐릭터가 처음 걸어본 길의 길이를 구하여 return 하는 solution 함수를 완성해 주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li><code>dirs</code>는 string형으로 주어지며, &#39;U&#39;, &#39;D&#39;, &#39;R&#39;, &#39;L&#39; 이외에 문자는 주어지지 않습니다.</li>
<li><code>dirs</code>의 길이는 500 이하의 자연수입니다.</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>dirs</th>
<th>answer</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;ULURRDLLU&quot;</td>
<td>7</td>
</tr>
<tr>
<td>&quot;LULLLLLLU&quot;</td>
<td>7</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(dirs) {
  const command = {
    U: [0, 1],
    D: [0, -1],
    R: [1, 0],
    L: [-1, 0],
  };
  const set = new Set();
  let current = [0, 0];

  for (const move of dirs) {
    const [x, y] = command[move];
    let currentX = current[0] + x;
    let currentY = current[1] + y;

    // 좌표 평면은 (-5, -5)부터 (5, 5)
    // 좌표 값을 넘어가면 continue
    if (currentX &gt; 5 || currentX &lt; -5 || currentY &gt; 5 || currentY &lt; -5)
      continue;

    // (이전 좌표 -&gt; 현재 좌표), (현재좌표 -&gt; 이전 좌표)
    // 두 방향을 고려하여 set에 add
    set.add(`${current[0]}${current[1]}${currentX}${currentY}`);
    set.add(`${currentX}${currentY}${current[0]}${current[1]}`);

    // 현재 좌표 갱신
    current = [currentX, currentY];
  }

  // 2개의 경우를 추가하였기 때문에 2로 나누기
  return set.size / 2;
}</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/49994">프로그래머스 - 방문 기록</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 귤 고르기 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B7%A4-%EA%B3%A0%EB%A5%B4%EA%B8%B0-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B7%A4-%EA%B3%A0%EB%A5%B4%EA%B8%B0-JS</guid>
            <pubDate>Wed, 24 May 2023 08:27:28 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>경화는 과수원에서 귤을 수확했습니다. 경화는 수확한 귤 중 <code>k</code>개를 골라 상자 하나에 담아 판매하려고 합니다. 그런데 수확한 귤의 크기가 일정하지 않아 보기에 좋지 않다고 생각한 경화는 귤을 크기별로 분류했을 때 서로 다른 종류의 수를 최소화하고 싶습니다.</p>
<p>예를 들어, 경화가 수확한 귤 8개의 크기가 [1, 3, 2, 5, 4, 5, 2, 3] 이라고 합시다. 경화가 귤 6개를 판매하고 싶다면, 크기가 1, 4인 귤을 제외한 여섯 개의 귤을 상자에 담으면, 귤의 크기의 종류가 2, 3, 5로 총 3가지가 되며 이때가 서로 다른 종류가 최소일 때입니다.</p>
<p>경화가 한 상자에 담으려는 귤의 개수 <code>k</code>와 귤의 크기를 담은 배열 <code>tangerine</code>이 매개변수로 주어집니다. 경화가 귤 <code>k</code>개를 고를 때 크기가 서로 다른 종류의 수의 최솟값을 return 하도록 solution 함수를 작성해주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li>1 ≤ <code>k</code> ≤ <code>tangerine</code>의 길이 ≤ 100,000</li>
<li>1 ≤ <code>tangerine</code>의 원소 ≤ 10,000,000</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>k</th>
<th>tangerine</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>6</td>
<td>[1, 3, 2, 5, 4, 5, 2, 3]</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>[1, 3, 2, 5, 4, 5, 2, 3]</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>[1, 1, 1, 1, 2, 2, 2, 3]</td>
<td>1</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(k, tangerine) {
  let result = 0;
  let dict = {};

  // 귤 크기별 개수 확인
  tangerine.forEach((v) =&gt; (dict[v] === undefined ? (dict[v] = 1) : dict[v]++));

  // 크기별 개수를 내림차순으로 정렬 후 순회
  Object.values(dict)
    .sort((a, b) =&gt; b - a)
    .forEach((v) =&gt; {
      // k가 0보다 작을 때 return
      if (k &lt;= 0) return;

      // 많은 수부터 빼면서 최솟값 도출
      k -= v;
      result++;
    });

  return result;
}</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/138476">프로그래머스 - 귤 고르기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 1차 뉴스 클러스터링]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-1%EC%B0%A8-%EB%89%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-1%EC%B0%A8-%EB%89%B4%EC%8A%A4-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81</guid>
            <pubDate>Mon, 22 May 2023 09:21:14 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>여러 언론사에서 쏟아지는 뉴스, 특히 속보성 뉴스를 보면 비슷비슷한 제목의 기사가 많아 정작 필요한 기사를 찾기가 어렵다. Daum 뉴스의 개발 업무를 맡게 된 신입사원 튜브는 사용자들이 편리하게 다양한 뉴스를 찾아볼 수 있도록 문제점을 개선하는 업무를 맡게 되었다.</p>
<p>개발의 방향을 잡기 위해 튜브는 우선 최근 화제가 되고 있는 &quot;카카오 신입 개발자 공채&quot; 관련 기사를 검색해보았다.</p>
<ul>
<li>카카오 첫 공채..&#39;블라인드&#39; 방식 채용</li>
<li>카카오, 합병 후 첫 공채.. 블라인드 전형으로 개발자 채용</li>
<li>카카오, 블라인드 전형으로 신입 개발자 공채</li>
<li>카카오 공채, 신입 개발자 코딩 능력만 본다</li>
<li>카카오, 신입 공채.. &quot;코딩 실력만 본다&quot;</li>
<li>카카오 &quot;코딩 능력만으로 2018 신입 개발자 뽑는다&quot;</li>
</ul>
<p>기사의 제목을 기준으로 &quot;블라인드 전형&quot;에 주목하는 기사와 &quot;코딩 테스트&quot;에 주목하는 기사로 나뉘는 걸 발견했다. 튜브는 이들을 각각 묶어서 보여주면 카카오 공채 관련 기사를 찾아보는 사용자에게 유용할 듯싶었다.</p>
<p>유사한 기사를 묶는 기준을 정하기 위해서 논문과 자료를 조사하던 튜브는 &quot;자카드 유사도&quot;라는 방법을 찾아냈다.</p>
<p>자카드 유사도는 집합 간의 유사도를 검사하는 여러 방법 중의 하나로 알려져 있다. 두 집합 <code>A</code>, <code>B</code> 사이의 자카드 유사도 <code>J(A, B)</code>는 두 집합의 교집합 크기를 두 집합의 합집합 크기로 나눈 값으로 정의된다.</p>
<p>예를 들어 집합 <code>A = {1, 2, 3}</code>, 집합 <code>B = {2, 3, 4}</code>라고 할 때, 교집합 <code>A ∩ B</code> = {2, 3}, 합집합 <code>A ∪ B</code> = {1, 2, 3, 4}이 되므로, 집합 <code>A</code>, <code>B</code> 사이의 자카드 유사도 <code>J(A, B)</code> = 2/4 = 0.5가 된다. 집합 A와 집합 B가 모두 공집합일 경우에는 나눗셈이 정의되지 않으니 따로 <code>J(A, B)</code> = 1로 정의한다.</p>
<p>자카드 유사도는 원소의 중복을 허용하는 다중집합에 대해서 확장할 수 있다. 다중집합 <code>A</code>는 원소 &quot;1&quot;을 3개 가지고 있고, 다중집합 <code>B</code>는 원소 &quot;1&quot;을 5개 가지고 있다고 하자. 이 다중집합의 교집합 <code>A ∩ B</code>는 원소 &quot;1&quot;을 min(3, 5)인 3개, 합집합 <code>A ∪ B</code>는 원소 &quot;1&quot;을 max(3, 5)인 5개 가지게 된다. 다중집합 <code>A</code> = {1, 1, 2, 2, 3}, 다중집합 <code>B</code> = {1, 2, 2, 4, 5}라고 하면, 교집합 <code>A ∩ B</code> = {1, 2, 2}, 합집합 <code>A ∪ B</code> = {1, 1, 2, 2, 3, 4, 5}가 되므로, 자카드 유사도 <code>J(A, B)</code> = 3/7, 약 0.42가 된다.</p>
<p>이를 이용하여 문자열 사이의 유사도를 계산하는데 이용할 수 있다. 문자열 &quot;FRANCE&quot;와 &quot;FRENCH&quot;가 주어졌을 때, 이를 두 글자씩 끊어서 다중집합을 만들 수 있다. 각각 {FR, RA, AN, NC, CE}, {FR, RE, EN, NC, CH}가 되며, 교집합은 {FR, NC}, 합집합은 {FR, RA, AN, NC, CE, RE, EN, CH}가 되므로, 두 문자열 사이의 자카드 유사도 J(&quot;FRANCE&quot;, &quot;FRENCH&quot;) = 2/8 = 0.25가 된다.</p>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<p><strong>입력 형식</strong></p>
<ul>
<li>입력으로는 <code>str1</code>과 <code>str2</code>의 두 문자열이 들어온다. 각 문자열의 길이는 2 이상, 1,000 이하이다.</li>
<li>입력으로 들어온 문자열은 두 글자씩 끊어서 다중집합의 원소로 만든다. 이때 영문자로 된 글자 쌍만 유효하고, 기타 공백이나 숫자, 특수 문자가 들어있는 경우는 그 글자 쌍을 버린다. 예를 들어 &quot;ab+&quot;가 입력으로 들어오면, &quot;ab&quot;만 다중집합의 원소로 삼고, &quot;b+&quot;는 버린다.</li>
<li>다중집합 원소 사이를 비교할 때, 대문자와 소문자의 차이는 무시한다. &quot;AB&quot;와 &quot;Ab&quot;, &quot;ab&quot;는 같은 원소로 취급한다.</li>
</ul>
<p><strong>출력 형식</strong></p>
<ul>
<li>입력으로 들어온 두 문자열의 자카드 유사도를 출력한다. 유사도 값은 0에서 1 사이의 실수이므로, 이를 다루기 쉽도록 65536을 곱한 후에 소수점 아래를 버리고 정수부만 출력한다.</li>
</ul>
<table>
<thead>
<tr>
<th>str1</th>
<th>str2</th>
<th>answer</th>
</tr>
</thead>
<tbody><tr>
<td>FRANCE</td>
<td>french</td>
<td>16384</td>
</tr>
<tr>
<td>handshake</td>
<td>shake hands</td>
<td>65536</td>
</tr>
<tr>
<td>aa1+aa2</td>
<td>AAAA12</td>
<td>43690</td>
</tr>
<tr>
<td>E=M*C^2</td>
<td>e=m*c^2</td>
<td>65536</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(str1, str2) {
  const toCollection = (str) =&gt; {
    const arr = [];

    // 대문자로 문자열 통일
    str = str.toUpperCase();

    for (let i = 0; i &lt; str.length - 1; i++) {
      // 문자열을 2개씩 끊으면서 순회
      const atom = str.slice(i, i + 2);
      // 숫자, 특수문자가 없는 문자열이면 배열에 push
      if (!atom.match(/[^A-Z]/g)) arr.push(atom);
    }

    return arr;
  };

  const arr1 = toCollection(str1);
  const arr2 = toCollection(str2);

  // Set 자료형으로 중복 원소 제거
  const set = new Set([...arr1, ...arr2]);
  let union = 0;
  let intersection = 0;

  set.forEach((atom) =&gt; {
    const has1 = arr1.filter((v) =&gt; v === atom).length;
    const has2 = arr2.filter((v) =&gt; v === atom).length;

    // 합집합 : set의 원소에서 배열의 값이 일치하면 모두 포함
    // 교집합 : set의 원소에서 배열의 값이 일치할 때, 더 적은 값으로 포함
    union += Math.max(has1, has2);
    intersection += Math.min(has1, has2);
  });

  // 합집합이 없다면 65536, 있다면 조건에 맞게 교집합/합집합*65536의 정수부분 return
  return union ? Math.floor((intersection / union) * 65536) : 65536;
}</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/17677">프로그래머스 - 뉴스 클러스터링</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 피로도 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%BC%EB%A1%9C%EB%8F%84-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%ED%94%BC%EB%A1%9C%EB%8F%84-JS</guid>
            <pubDate>Sat, 20 May 2023 05:35:34 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>XX게임에는 피로도 시스템(0 이상의 정수로 표현합니다)이 있으며, 일정 피로도를 사용해서 던전을 탐험할 수 있습니다. 이때, 각 던전마다 탐험을 시작하기 위해 필요한 &quot;최소 필요 피로도&quot;와 던전 탐험을 마쳤을 때 소모되는 &quot;소모 피로도&quot;가 있습니다. &quot;최소 필요 피로도&quot;는 해당 던전을 탐험하기 위해 가지고 있어야 하는 최소한의 피로도를 나타내며, &quot;소모 피로도&quot;는 던전을 탐험한 후 소모되는 피로도를 나타냅니다. 예를 들어 &quot;최소 필요 피로도&quot;가 80, &quot;소모 피로도&quot;가 20인 던전을 탐험하기 위해서는 유저의 현재 남은 피로도는 80 이상 이어야 하며, 던전을 탐험한 후에는 피로도 20이 소모됩니다.</p>
<p>이 게임에는 하루에 한 번씩 탐험할 수 있는 던전이 여러개 있는데, 한 유저가 오늘 이 던전들을 최대한 많이 탐험하려 합니다. 유저의 현재 피로도 <code>k</code>와 각 던전별 &quot;최소 필요 피로도&quot;, &quot;소모 피로도&quot;가 담긴 2차원 배열 <code>dungeons</code> 가 매개변수로 주어질 때, 유저가 탐험할수 있는 최대 던전 수를 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li><code>k</code>는 1 이상 5,000 이하인 자연수입니다.</li>
<li><code>dungeons</code>의 세로(행) 길이(즉, 던전의 개수)는 1 이상 8 이하입니다.<ul>
<li><code>dungeons</code>의 가로(열) 길이는 2 입니다.</li>
<li><code>dungeons</code>의 각 행은 각 던전의 [&quot;최소 필요 피로도&quot;, &quot;소모 피로도&quot;] 입니다.</li>
<li>&quot;최소 필요 피로도&quot;는 항상 &quot;소모 피로도&quot;보다 크거나 같습니다.</li>
<li>&quot;최소 필요 피로도&quot;와 &quot;소모 피로도&quot;는 1 이상 1,000 이하인 자연수입니다.</li>
<li>서로 다른 던전의 [&quot;최소 필요 피로도&quot;, &quot;소모 피로도&quot;]가 서로 같을 수 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>k</th>
<th>dungeons</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>80</td>
<td>[[80,20],[50,40],[30,10]]</td>
<td>3</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(k, dungeons) {
  let result = -1;
  const visit = new Array(dungeons.length).fill(0);

  const dfs = (k, dungeons, cnt, visit) =&gt; {
    for (let i = 0; i &lt; dungeons.length; i++) {
      const [total, use] = dungeons[i];
      // 방문한 던전 or 현재 던전 최소 피로도 &gt; 현재 피로도 continue
      if (visit[i] || total &gt; k) continue;

      // 방문 노드 기록
      visit[i] = 1;
      dfs(k - use, dungeons, cnt++, visit);

      // 재귀 이후 방문 이전으로
      visit[i] = 0;
    }

    result = Math.max(cnt, result);
  };

  dfs(k, dungeons, 0, visit);
  return result;
}</code></pre>
<h3 id="🧱-알고리즘">🧱 알고리즘</h3>
<p><a href="https://ko.wikipedia.org/wiki/%EA%B9%8A%EC%9D%B4_%EC%9A%B0%EC%84%A0_%ED%83%90%EC%83%89">깊이 우선 탐색 (DFS) - Wikipedia</a></p>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/87946">프로그래머스 - 피로도</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 예상 대진표 JS]]></title>
            <link>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%98%88%EC%83%81-%EB%8C%80%EC%A7%84%ED%91%9C-JS</link>
            <guid>https://velog.io/@fervor_dev/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%98%88%EC%83%81-%EB%8C%80%EC%A7%84%ED%91%9C-JS</guid>
            <pubDate>Fri, 19 May 2023 08:46:57 GMT</pubDate>
            <description><![CDATA[<h3 id="📢문제-설명">📢문제 설명</h3>
<p>△△ 게임대회가 개최되었습니다. 이 대회는 N명이 참가하고, 토너먼트 형식으로 진행됩니다. N명의 참가자는 각각 1부터 N번을 차례대로 배정받습니다. 그리고, 1번↔2번, 3번↔4번, ... , <code>N-1</code>번↔<code>N</code>번의 참가자끼리 게임을 진행합니다. 각 게임에서 이긴 사람은 다음 라운드에 진출할 수 있습니다. 이때, 다음 라운드에 진출할 참가자의 번호는 다시 1번부터 <code>N</code>/2번을 차례대로 배정받습니다. 만약 1번↔2번 끼리 겨루는 게임에서 2번이 승리했다면 다음 라운드에서 1번을 부여받고, 3번↔4번에서 겨루는 게임에서 3번이 승리했다면 다음 라운드에서 2번을 부여받게 됩니다. 게임은 최종 한 명이 남을 때까지 진행됩니다.</p>
<p>이때, 처음 라운드에서 <code>A</code>번을 가진 참가자는 경쟁자로 생각하는 B번 참가자와 몇 번째 라운드에서 만나는지 궁금해졌습니다. 게임 참가자 수 <code>N</code>, 참가자 번호 <code>A</code>, 경쟁자 번호 <code>B</code>가 함수 solution의 매개변수로 주어질 때, 처음 라운드에서 <code>A</code>번을 가진 참가자는 경쟁자로 생각하는 <code>B</code>번 참가자와 몇 번째 라운드에서 만나는지 return 하는 solution 함수를 완성해 주세요.
<strong>단, <code>A</code>번 참가자와 <code>B</code>번 참가자는 서로 붙게 되기 전까지 항상 이긴다고 가정합니다.</strong></p>
<h3 id="🚨제한사항">🚨제한사항</h3>
<ul>
<li><code>N</code> : 21 이상 220 이하인 자연수 (2의 지수 승으로 주어지므로 부전승은 발생하지 않습니다.)</li>
<li><code>A</code>, <code>B</code> : <code>N</code> 이하인 자연수 (단, <code>A</code> ≠ <code>B</code> 입니다.)</li>
</ul>
<h3 id="🧾입출력-예">🧾입출력 예</h3>
<table>
<thead>
<tr>
<th>N</th>
<th>A</th>
<th>B</th>
<th>answer</th>
</tr>
</thead>
<tbody><tr>
<td>8</td>
<td>4</td>
<td>7</td>
<td>3</td>
</tr>
</tbody></table>
<h3 id="🔑풀이">🔑풀이</h3>
<pre><code class="language-js">function solution(n, a, b) {
  let result = 0;

  // Round 0 : 4, 7
  // Round 1 : 2, 4
  // Round 2 : 1, 2
  // Round 3 : 1, 1

  while (a !== b) {
    a = Math.ceil(a / 2);
    b = Math.ceil(b / 2);
    result++;
  }

  return result;
}</code></pre>
<h3 id="🔗-문제-링크">🔗 문제 링크</h3>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12985">프로그래머스 - 예상 대진표</a></p>
]]></description>
        </item>
    </channel>
</rss>