<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>westzero.log</title>
        <link>https://velog.io/</link>
        <description>흘러가는 대로 삽니다.</description>
        <lastBuildDate>Thu, 13 Mar 2025 12:14:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>westzero.log</title>
            <url>https://velog.velcdn.com/images/westzero_dev/profile/9dfc7fa0-4780-42ca-a22b-d4f04b478164/image.gif</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. westzero.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/westzero_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[알고리즘] 프로그래머스 #42746 정렬 - 가장 큰 수 ]]></title>
            <link>https://velog.io/@westzero_dev/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-42746-%EC%A0%95%EB%A0%AC-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%88%98</link>
            <guid>https://velog.io/@westzero_dev/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-42746-%EC%A0%95%EB%A0%AC-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%88%98</guid>
            <pubDate>Thu, 13 Mar 2025 12:14:23 GMT</pubDate>
            <description><![CDATA[<h1 id="✏️-문제">✏️ 문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42746#">https://school.programmers.co.kr/learn/courses/30/lessons/42746#</a>
<img src="https://velog.velcdn.com/images/westzero_dev/post/c03c72a7-5ec9-4324-b043-bf883fb35f04/image.png" alt=""></p>
<pre><code class="language-javascript">function solution(numbers) {
    var answer = &#39;&#39;;
    numbers.sort((a, b) =&gt; b - a);
    numbers.sort((a, b) =&gt; String(a)[b.length-1] - String(b)[a.length-1]);
    return answer;
}

// [9, 5, 34, 30, 3]</code></pre>
<p>여기서 3이 30보다 앞에 오게 해야하는 알고리즘이 아무리 생각해도 생각나지 않았다.</p>
<h2 id="📍-다른-사람의-풀이">📍 다른 사람의 풀이</h2>
<p>다른 풀이를 읽어보니 수를 직접 비교하면서 풀었다. 예를 들어, &#39;3&#39;+&#39;30&#39; 과 &#39;30+3&#39; 이 어느 쪽이 더 큰지 비교해서 정렬한 것이다.</p>
<pre><code class="language-javascript">function solution(numbers) {
    var answer = &#39;&#39;;
    numbers_ = numbers.map(String);
    numbers_.sort((a, b) =&gt; (b+a) - (a+b))

    answer = numbers_.join(&#39;&#39;);

    return answer;
}</code></pre>
<p>이 풀이를 보고 코드를 작성했고, 테스트 케이스 1개를 통과하지 못했다. 만약 모든 수가 0으로 구성되어 있다면 &#39;000&#39; 이 아닌 &#39;0&#39;으로 출력되어야 한다.</p>
<h2 id="📍-최종-코드">📍 최종 코드</h2>
<pre><code class="language-javascript">function solution(numbers) {
    var answer = &#39;&#39;;
    numbers_ = numbers.map(String);
    numbers_.sort((a, b) =&gt; (b+a) - (a+b))

    if (numbers_[0] === &#39;0&#39;) {
        return &#39;0&#39;;
    }

    answer = numbers_.join(&#39;&#39;);

    return answer;
}</code></pre>
<h2 id="❓-알게-된-것">❓ 알게 된 것</h2>
<ul>
<li>join 메서드는 javascript에도 적용된다. 반복문보단 join을 사용해보자
   <code>answer = numbers_.join(&#39;&#39;);</code> </li>
<li>직접 구현하는 것을 먼저 생각하기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TailwindCSS Dynamic class names]]></title>
            <link>https://velog.io/@westzero_dev/TailwindCSS-Dynamic-class-names</link>
            <guid>https://velog.io/@westzero_dev/TailwindCSS-Dynamic-class-names</guid>
            <pubDate>Sat, 07 Dec 2024 05:05:37 GMT</pubDate>
            <description><![CDATA[<p>개발하다 발견한 CSS 오류!</p>
<pre><code class="language-typescript">&lt;div className=&quot;h-screen bg-gradient-to-b from-[#EDFBFF] via-[#C6F2FF] via-65% via-[#FFC3F9]/[0.4] via-83% to-[#BDBFC0]&quot;&gt;
      &lt;LoadingPopUp
        className={absolute top-[38%] left-[36%] transform -translate-x-1/2 -translate-y-1/2}
      /&gt;
      &lt;LoadingPopUp
        className={absolute top-[40%] left-[38%] transform -translate-x-1/2 -translate-y-1/2 }
      /&gt;
      &lt;LoadingPopUp
        className={absolute top-[42%] left-[40%] transform -translate-x-1/2 -translate-y-1/2 }
      /&gt;
      &lt;PopUp
        className={absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 }
      /&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>이 코드의 tailwindCSS 중복이 많아 동적으로 숫자를 넘겨주기 위해 리팩터링 하였다.</p>
<pre><code class="language-typescript">    const positions = [
      { top: &quot;38%&quot;, left: &quot;36%&quot; },
      { top: &quot;40%&quot;, left: &quot;38%&quot; },
      { top: &quot;42%&quot;, left: &quot;40%&quot; },
      { top: &quot;50%&quot;, left: &quot;50%&quot; }
    ];

    return (
      &lt;PopUpComponent
        key={index}
        className={`absolute top-[${positions[index].top}] left-[${positions[index].left}] transform -translate-x-1/2 -translate-y-1/2 t`}
      /&gt;
    );</code></pre>
<p>마지막 컴포넌트가 화면의 정중앙에 와야하는데 오지 않게 되었다.
<img src="https://velog.velcdn.com/images/westzero_dev/post/24837f37-e29d-4b51-8b53-b3878bc49bef/image.png" alt="">
이렇게 전체의 절반이 잘리게 된다.
퍼센트를 조절해봤는데 42%까지는 작동이 되다가 42를 넘는순간 위로 잘려버린다. ㅋㅋ</p>
<p>리팩터링 전이랑 뭐가 다를까 고민하다가 전에는 1/2라고 써서 1/2를 살리기 위해 꼼수를 사용했다. 동적 클래스 이름이 작동하지 않는다고 판단하였기 때문이다..</p>
<pre><code class="language-typescript">top-[${positions[index].top}] left-[${positions[index].left}]</code></pre>
<p>여기서 대괄호를 제외하고, </p>
<pre><code class="language-typescript">const positions = [
      { top: &quot;38%&quot;, left: &quot;36%&quot; },
      { top: &quot;40%&quot;, left: &quot;38%&quot; },
      { top: &quot;42%&quot;, left: &quot;40%&quot; },
      { top: &quot;50%&quot;, left: &quot;50%&quot; }
    ];</code></pre>
<p>이 값에 직접 대괄호를 넣어주면 절반에 50%가 아닌 1/2를 쓸 수 있다.. ㅋ</p>
<p><strong>결과</strong></p>
<pre><code class="language-typescript">const positions = [
      { top: &quot;[38%]&quot;, left: &quot;[36%]&quot; },
      { top: &quot;[40%]&quot;, left: &quot;[38%]&quot; },
      { top: &quot;[42%]&quot;, left: &quot;[40%]&quot; },
      { top: &quot;1/2&quot;, left: &quot;1/2&quot; },
    ];

    const PopUpComponent = index === 3 ? PopUp : LoadingPopUp;
    return (
      &lt;PopUpComponent
        key={index}
        className={`absolute top-${positions[index].top} left-${positions[index].left} transform -translate-x-1/2 -translate-y-1/2`}
      /&gt;
    );
  };</code></pre>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/b0abfc0c-0fc0-4eb8-b98e-f09748ec9b70/image.png" alt="">
해결했지만 해결했다고 할 수 없는 이 찜찜함.... 도대체 뭐니..</p>
<blockquote>
<p><strong>😇 왜냐하면 해결이 안됐기 때문이다. 프로젝트를 재실행 해보니 동적 클래스가 전부 작동하지 않았다.</strong></p>
</blockquote>
<p>tailwindcss에는 <code>top-[24%]</code> 이런식으로 사용자 정의를 할 수 있다. 그리고 위에서 되던 꼼수인 1/2 동적 클래스도 작동이 안되고 있기 때문에 top과 left를 나누어 숫자를 넣지 말고 <code>top-[24%]</code> 자체를 넘겨주기로 했다.</p>
<pre><code class="language-typescript">const positions = [
      &quot;top-[38%] left-[36%]&quot;,
      &quot;top-[40%] left-[38%]&quot;,
      &quot;top-[42%] left-[40%]&quot;,
      &quot;top-1/2 left-1/2&quot;,
    ];

    return (
      &lt;PopUpComponent
        key={index}
        className={`absolute ${positions[index]} transform -translate-x-1/2 -translate-y-1/2`}
      /&gt;
    );</code></pre>
<p>이렇게 전체 속성을 넘겨주니 잘 동작한다.</p>
<h1 id="tailwindcss-dynamic-class-names">TailwindCSS Dynamic class names</h1>
<p>동적 클래스에 대한 규칙이 있는건가 해서 공식문서를 찾아봤다. 완전 당연하게도 규칙이 있었다.</p>
<h2 id="❌-1-dont-construct-class-names-dynamically">❌ 1. Don’t construct class names dynamically</h2>
<pre><code>&lt;div class=&quot;text-{{ error ? &#39;red&#39; : &#39;green&#39; }}-600&quot;&gt;&lt;/div&gt;</code></pre><p>내가 한 것처럼 class name의 구조를 동적으로 사용하면 안 된다. </p>
<h2 id="⭕️-2-always-use-complete-class-names">⭕️ 2. Always use complete class names</h2>
<pre><code>&lt;div class=&quot;{{ error ? &#39;text-red-600&#39; : &#39;text-green-600&#39; }}&quot;&gt;&lt;/div&gt;</code></pre><p>동적으로 사용하고 싶은 경우에는 항상 전체 class name을 사용해야 한다.</p>
<h2 id="❌-3-dont-use-props-to-build-class-names-dynamically">❌ 3. Don’t use props to build class names dynamically</h2>
<pre><code>function Button({ color, children }) {
  return (
    &lt;button className={`bg-${color}-600 hover:bg-${color}-500 ...`}&gt;
      {children}
    &lt;/button&gt;
  )
}</code></pre><p>props 로 넘겨줄 때도 마찬가지로 일부 숫자나 색상코드만 넘겨주는 것은 안 된다.</p>
<h2 id="⭕️-4-always-map-props-to-static-class-names">⭕️ 4. Always map props to static class names</h2>
<pre><code>function Button({ color, children }) {
  const colorVariants = {
    blue: &#39;bg-blue-600 hover:bg-blue-500&#39;,
    red: &#39;bg-red-600 hover:bg-red-500&#39;,
  }

  return (
    &lt;button className={`${colorVariants[color]} ...`}&gt;
      {children}
    &lt;/button&gt;
  )
}</code></pre><p>전체 속성의 이름을 넘겨주어야 한다.</p>
<blockquote>
<p>내가 한 실수가 전부 명세서에 나와있었다. 안 될 이유가 없다고 생각했는데 ... 생각해보면 tailwind는 잘못 한 게 없다 👍</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js와 firebase 연동하기]]></title>
            <link>https://velog.io/@westzero_dev/Next.js%EC%99%80-firebase-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@westzero_dev/Next.js%EC%99%80-firebase-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 05 Dec 2024 17:17:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>매우 소규모의 백엔드 기능이 필요해서 firebase를 사용해보기로 했다. 서버를 직접 만들 필요 없이 여러 서비스를 이용할 수 있다. (데이터베이스, 인증, 스토리지, 함수 및 호스팅 등)</p>
</blockquote>
<h3 id="1-firebase-시작하기">1. firebase 시작하기</h3>
<p><strong>1.1 firebase 프로젝트 시작하기</strong>
<img src="https://velog.velcdn.com/images/westzero_dev/post/a4ff200f-6e0f-4acc-ad90-ed7ae1197c38/image.png" alt="">
<img src="https://velog.velcdn.com/images/westzero_dev/post/f97c8436-af25-49e3-a006-3751c2892a7d/image.png" alt="">
** 1.2 앱 추가하기**
<img src="https://velog.velcdn.com/images/westzero_dev/post/3b0b3672-d876-48b3-91e1-76499e083034/image.png" alt="">
웹 개발을 선택하였다. 앱을 등록하면 Firebase의 SDK가 제공된다. 이를 저장해두고 콘솔로 이동한다.</p>
<hr>
<h3 id="2-nextjs-프로젝트에-firebase-세팅하기">2. Next.js 프로젝트에 firebase 세팅하기</h3>
<p><strong>2.1 firesbase.ts 파일 생성</strong>
<img src="https://velog.velcdn.com/images/westzero_dev/post/5ebeefc5-b784-4c8c-b7fa-cb71b28768ed/image.png" alt="">
<code>firebase.ts</code>와 <code>firestore.ts</code> 파일을 생성한다. (firebase.ts의 파일명은 firebasedb.ts로도 많이 쓰이는 듯 하다.)
이 때, 파일 키들은 노출되면 안되므로, 환경변수로 보호해준다.</p>
<p><strong>2.1.1 환경변수로 지정하기</strong>
최상단에 <code>.env.local</code> 파일을 생성한 뒤 <code>NEXT_PUBLIC_key = vlaue</code> 로 SDK의 정보를 저장한다. 사용할 때는 <code>process.env.NEXT_PUBLIC_key</code> 로 부르면 된다.</p>
<pre><code class="language-typescript">// firebase.ts
import { initializeApp } from &quot;firebase/app&quot;;

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_APIKEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTHDOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECTID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGEBUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGINGSENDERID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APPID,
  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASURMENTID
};

// Initialize Firebase
const firebasedb = initializeApp(firebaseConfig)
export default firebasedb</code></pre>
<p><strong>2.2 firestore.ts 파일 생성</strong></p>
<pre><code class="language-typescript">// firestore.ts
import firebase from &#39;./firebase&#39;;
import {  getFirestore  } from &#39;firebase/firestore&#39;;

const fireStore = getFirestore(firebase);
export default fireStore</code></pre>
<hr>
<h3 id="3-firestore-콘솔에서-cloud-firestore를-생성">3. Firestore 콘솔에서 Cloud Firestore를 생성</h3>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/c88f3476-4ffc-48ca-b8e2-321805f411ff/image.png" alt="">
<strong>3.1 데이터베이스 만들기</strong>
<img src="https://velog.velcdn.com/images/westzero_dev/post/ecbbb7ad-546b-4c91-a424-264a4036918c/image.png" alt="">
프로덕션 모드에서 시작한다.
<img src="https://velog.velcdn.com/images/westzero_dev/post/66f4a5c8-4124-4e89-b799-b120e551b942/image.png" alt="">
데이터베이스가 만들어진 것을 확인할 수 있다. 데이터를 직접 수정하고 읽으려면 규칙을 수정해야 한다.</p>
<p><strong>3.2 규칙 수정하기</strong></p>
<pre><code>rules_version = &#39;2&#39;;

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true; -&gt; false를 true로
    }
  }
}</code></pre><hr>
<h3 id="4-firestore-연동하기">4. Firestore 연동하기</h3>
<p><strong>4.1 데이터 읽기</strong>
<img src="https://velog.velcdn.com/images/westzero_dev/post/82f41fb0-ff76-43e7-ac36-613a1cfbcf07/image.png" alt="">
데이터를 직접 추가해준다. <code>getDoc</code>으로 데이터를 읽어보자!</p>
<pre><code class="language-typescript"> setLogLevel(&quot;debug&quot;)
// bug 내용을 더 자세하게 알 수 있다.

  const db = getFirestore(firebasedb)
  const test = async () =&gt; {
    const query = await getDoc(
      doc(fireStore, &quot;auth&quot;, &quot;QhK2EkBMjzmIpSNbrwKZ&quot;)
    );
    console.log(&#39;이것 뭐에요~~?&#39;, query.data())
  }</code></pre>
<p>메인화면에 버튼으로 작동시켰다.</p>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/87ed72d7-aec5-45ab-819a-0bc62804c3d7/image.png" alt="">
<code>console 창</code>에서 데이터 값을 확인할 수 있다.</p>
<p><strong>4.2 데이터 넣기</strong>
<code>addDoc</code>을 사용하여 데이터를 추가할 수 있다.</p>
<pre><code class="language-typescript">const testPost = async (e) =&gt; {
    e.preventDefault();
    await addDoc(collection(fireStore, &quot;information&quot;), {
      age: 17,
      name: &#39;kk&#39;
    });
  }</code></pre>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/abc2e3c8-31e6-49f1-9678-c3103880dd86/image.png" alt="">
연동에 성공했다. 👻</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[알고리즘] 그리디 | 백준 | #2839 #11399 #1715]]></title>
            <link>https://velog.io/@westzero_dev/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B7%B8%EB%A6%AC%EB%94%94</link>
            <guid>https://velog.io/@westzero_dev/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B7%B8%EB%A6%AC%EB%94%94</guid>
            <pubDate>Thu, 05 Dec 2024 16:05:10 GMT</pubDate>
            <description><![CDATA[<h1 id="그리디-알고리즘">그리디 알고리즘</h1>
<blockquote>
<p>탐욕법으로 소개되는 이 알고리즘은 어떠한 문제가 있을 때 단순하게 탐욕적으로 문제를 푸는 알고리즘이다. <strong>현재 상황에서 지금 당장 좋은 것만 고르는 방법</strong>을 의미한다. 매 순간 가장 좋아 보이는 것을 선택하며 현재의 선택이 나중에 미칠 영향은 고려하지 않는다.</p>
</blockquote>
<blockquote>
<p>기준에 따라 좋은 것을 선택하는 알고리즘이므로 문제에서 <strong>가장 큰 순서대로</strong>, <strong>가장 작은 순서대로</strong>와 같은 기준을 제시해준다. 이 기준은 정렬 알고리즘을 사용하면 만족시킬 수 있다. </p>
</blockquote>
<h2 id="✏️-예제">✏️ 예제</h2>
<h3 id="백준-2839">백준 2839</h3>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/8a0bedbc-c611-44e8-94ff-92b620b4f517/image.png" alt=""></p>
<p>5가 최대일 경우 전체 횟수가 최적의 해를 갖게 되므로 5를 기준으로 나눴다.</p>
<pre><code class="language-python"># 나의 풀이
N = int(input())
lst = []
r = N // 5

for i in range(r, -1, -1):
    a = N - i * 5
    b = a % 3
    if b == 0:
      print(i + a//3)
      break
    if i == 0:
      print(-1)</code></pre>
<pre><code class="language-python"># gpt 풀이
N = int(input())

# 5kg 봉지를 최대한 사용
bags = 0
while N &gt;= 0:
    # N이 5의 배수라면 (최대한 5kg 봉지를 사용)
    if N % 5 == 0:
        bags += N // 5
        print(bags)
        break
    # 그렇지 않다면 3kg 봉지 하나 사용
    N -= 3
    bags += 1
else:
    # 정확히 N kg을 만들 수 없는 경우
    print(-1)</code></pre>
<p>N이 5의 배수인지 아닌지를 한 번 더 나눠주고 while을 사용한 풀이이다.</p>
<h4 id="정리">정리</h4>
<p>이 문제는 국소 최적해가 전체 최적해를 보장하는 문제의 특성 때문에 그리디 알고리즘으로 해결 가능하다. <strong>&quot;큰 봉지(5kg)를 가능한 많이 사용&quot;</strong> 하는 것이 최선의 선택임을 보장하고 있기 때문이다.</p>
<h3 id="백준-11399">백준 11399</h3>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/78b81aa1-a887-4604-86a2-1a3357b90cf4/image.png" alt=""></p>
<p>무조건 시간이 적게 걸리는 사람부터 돈을 인출하면 된다. 자신이 인출하는데 걸린 시간 * (뒷사람 수 +1) 해주면 된다. 반복문을 돌리는데 리스트의 인덱스 번호와 사람수가 반대로 가서 정렬을 걸리는 시간이 큰 순서대로 해주었다.</p>
<pre><code class="language-python"># 나의 풀이
N = int(input())
time = sorted(list(map(int, input().split())), reverse=True)
sum = 0

for i in range(N):
  sum += time[i] * (i+1)

print(sum)</code></pre>
<h4 id="정리-1">정리</h4>
<p>이 문제는 그리디 알고리즘과 정렬 알고리즘을 사용한 문제이다. 최적의 해를 구하기 위해선 각 사람이 돈을 인출하는 시간을 오름차순으로 정렬하는 것이 핵심이다.</p>
<h3 id="백준-1715">백준 1715</h3>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/e2e9d99e-e78f-4fb7-838b-7bf9842fcdac/image.png" alt="">
크기 순서대로 정렬해서 하나씩 더해주면 된다고 생각했다. 만약 2개의 합이 다음 카드보다 크다면 큰 카드가 작은 카드보다 한 번 더 더해지는 것이므로 재정렬해줘야 한다. <code>pop</code>, <code>deque</code> 2가지를 사용해서 코드를 짜봤지만 N이 매우 커서 시간초과가 생겼다.</p>
<h4 id="📌-우선순위-heapq">📌 우선순위 heapq</h4>
<blockquote>
<p>heapq.heappush(heap, item) : item을 heap 에 추가
원소가 작은 순서대로 정렬된다</p>
</blockquote>
<pre><code class="language-python">import heapq

heap_list = []
heapq.heappush(heap_list, 2)
heapq.heappush(heap_list, 1)
heapq.heappush(heap_list, 5)
heapq.heappush(heap_list, 3)

print(heap_list) # [1, 2, 3, 5]</code></pre>
<blockquote>
<p>heapq.heappop(heap) : heap에서 가장 작은 원소를 pop &amp; 리턴
비어있는 경우 IndexError 호출</p>
</blockquote>
<pre><code class="language-python">print(heapq.heappop(heap_list))  # 1
print(heap_list)  # [2, 3, 5] 
a = heapq.heappop(heap_list)
print(a)  # 2</code></pre>
<p><strong>풀이</strong></p>
<ol>
<li>heapq를 사용하여 카드 중에서 가장 작은 묶음 2개를 뽑는다.</li>
<li>두 원소를 더하고, 새로 만들어진 묶음을 다시 카드에 넣는다.</li>
<li>카드에서 또 다시 제일 작은 묶음을 구하고 더한다.</li>
</ol>
<pre><code class="language-python">import heapq

n = int(input())
cards = []

for _ in range(n):
  heapq.heappush(cards, int(input()))

result = 0
while len(cards) &gt; 1:
  # 가장 작은 카드 뽑기
  a = heapq.heappop(cards)
  b = heapq.heappop(cards)
  sum_value = a + b

  result += sum_value
  heapq.heappush(cards, sum_value)


print(result)</code></pre>
<h4 id="정리-2">정리</h4>
<p>우선순위 큐를 활용하여 최적의 해를 구하는 그리디 알고리즘이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSR 과 CSR]]></title>
            <link>https://velog.io/@westzero_dev/SSR-%EA%B3%BC-CSR</link>
            <guid>https://velog.io/@westzero_dev/SSR-%EA%B3%BC-CSR</guid>
            <pubDate>Tue, 04 Jun 2024 13:18:38 GMT</pubDate>
            <description><![CDATA[<h1 id="ssr이란">SSR이란?</h1>
<blockquote>
<p>SSR은 서버에서 사용자에게 보여줄 페이지를 모두 구성하여 사용자에게 페이지를 보여주는 방식이다. (render가 준비된 html을 보내준다/2단계에서 이미 보여줄 준비가 끝난 것을 의미한다.)</p>
</blockquote>
<h2 id="browser-rendering과-server-side-rendering">Browser rendering과 Server Side rendering</h2>
<ol>
<li>Browser rendering에서의 rendering 단어와 의미
브라우저라는 프로그램이 서버로부터 html파일, css파일, 자바스크립트 파일을 받아온 다음, 그것을 해석하고 파싱해서 그 결과물로 화면을 그리는 과정을 렌더링이라고 한다.</li>
<li>Server Side Rendering에서의 rendering 단어와 의미
html 파일을 누가 어디서 주고 있는지. html 파일 안에 이미 내용들이 있는 채로 주고 있느냐에 대한 의미이다. SSR의 렌더링은 html 파일 안에 내용이 있느냐 없느냐이다. 있으면 렌더링이 된 것이다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/e684c2d4-1b5a-41c3-adb8-77e56a3b01de/image.png" alt="출처: The Benefits of Server Side Rendering Over Client Side Rendering"></p>
<h2 id="서버사이드-렌더링의-과정">서버사이드 렌더링의 과정</h2>
<p><strong>1단계</strong> 렌더 될 준비가 되어 있는 html 파일을 서버에서 준다. html파일을 서버에서 주는데 이 html 파일은 내용이 있다. (렌더될 준비가 완벽히 되어있는 것)
<strong>2단계</strong> 렌더 될 준비가 완벽히 끝난 html파일이기 때문에 유저는 바로 볼 수 있다. 그리고 나서 브라우저는 자바스크립트 파일을 다운로드 받는다. 보여지는 것은 2단계부터 가능하고, 이것이 CSR과 가장 큰 차이점이다. preivew에서 확인이 가능하다.
<strong>3단계</strong> 다운로드 받은 js 파일을 브라우저가 실행을 시킨다. 
<strong>4단계</strong> 상호작용이 가능해진다.</p>
<h1 id="csr">CSR</h1>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/dac88d98-cdf1-4e6a-a6f0-d15d94a1e1e6/image.png" alt="출처: The Benefits of Server Side Rendering Over Client Side Rendering"></p>
<h2 id="클라이언트사이드-렌더링의-과정">클라이언트사이드 렌더링의 과정</h2>
<p>서버사이드렌더링과 다르게 4단계에서 볼 수 있고, 상호작용가능하다.</p>
<p>📍 왜 그럴까?</p>
<p>서버가 브라우저에게 html 파일을 주긴 주는데 html 파일이 아무내용이 없는 render가 준비된 파일이 아니다.
자바스크립트 파일을 받아서 자바스크립트 파일에서 create element function으로 document.createElement() tag를 만들어 태그가 렌더링 되면 자바스크립트 파일이 실행시켜야 최종단계에서 보여질 수 있는 것이다.</p>
<p>(main.chunk.js 을 다운로드 받고 있음 → App 이라는 함수를 실행시킨다.)</p>
<h1 id="ssr-vs-csr">SSR VS CSR</h1>
<p>SSR을 사용하면 모든 데이터가 매핑된 서비스 페이지를 클라이언트(브라우저)에게 바로 보여줄 수 있다. 서버를 이용해서 페이지를 구성하기 때문에 클라이언트에서 구성하는 CSR보다 페이지를 구성하는 속도는 늦어지지만 전체적으로 사용자에게 보여주는 콘텐츠 구성이 완료되는 시점은 빨라진다는 장점이 있다. </p>
<h1 id="검색엔진-최적화-seo">검색엔진 최적화 SEO</h1>
<p>검색엔진 최적화: 검색을 하면 그 해당 내용을 노출을 시켜주는데 검색엔진 봇이 그 내용을 확인 할 수 있어야 한다.
CSR은 브라우저가 자바스크립트를 실행시켜야 내용을 확인할 수 있기 때문에 노출시킬 수 없으나 SSR은 이미 서버에서 내용을 주고 있기 때문에 서버사이드 렌더링을 통해 검색엔진 봇에 노출시켜 최적화를 하며 SEO 문제를 해결시킬 수 있다.</p>
<blockquote>
<p>참고
<a href="https://d2.naver.com/helloworld/7804182">https://d2.naver.com/helloworld/7804182</a>
<a href="https://www.youtube.com/watch?v=D71ByEIBWEs&amp;t=295s">https://www.youtube.com/watch?v=D71ByEIBWEs&amp;t=295s</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 14에서 Styled-Component 사용하기]]></title>
            <link>https://velog.io/@westzero_dev/Next.js-14%EC%97%90%EC%84%9C-Styled-Component-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@westzero_dev/Next.js-14%EC%97%90%EC%84%9C-Styled-Component-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 13 Mar 2024 04:42:40 GMT</pubDate>
            <description><![CDATA[<p>새로고침 할 때 마다 잠깐의 깜빡임과 함께 CSS가 풀리는 오류가 있었다. </p>
<p>무슨 문제인지 검색해보니 Styled-Component의 동작방식이 SSR(Server Side Rendering), SSG(Static Site Generation) 환경과 서버 컴포넌트에서 스타일링이 어렵다는 점이었다.</p>
<h3 id="⚒️-styled-component의-동작-방식">⚒️ Styled-Component의 동작 방식</h3>
<p>styled-component는 클라이언트가 런타임일 때, 스타일시트를 생성하고 <code>&lt;style/&gt;</code> 요소로 DOM에 주입한다. 프로젝트가 실행 중일 때 DOM요소에 주입되는 것이다. 서버사이드에서 동작하게 된다면 서버에서 HTML이 생성되고 style은 클라이언트 런타임 때 생성되고 주입되므로 잠깐의 시간동안 깜빡임이 존재하는 것이다.</p>
<p>SSR을 지원하는 Next.js의 공식문서에도 경고문구가 있다.
<img src="https://velog.velcdn.com/images/westzero_dev/post/9ef303b5-a565-4c75-bbfc-57076e50d184/image.png" alt=""></p>
<h3 id="📍-해결하기">📍 해결하기</h3>
<p><strong>next.config.js 추가</strong></p>
<pre><code>
module.exports = {
  compiler: {
    styledComponents: true,
  },
}</code></pre><p><strong>lib/registry.tsx 생성</strong></p>
<pre><code>
&#39;use client&#39;

import React, { useState } from &#39;react&#39;
import { useServerInsertedHTML } from &#39;next/navigation&#39;
import { ServerStyleSheet, StyleSheetManager } from &#39;styled-components&#39;

export default function StyledComponentsRegistry({
  children,
}: {
  children: React.ReactNode
}) {
  // Only create stylesheet once with lazy initial state
  // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
  const [styledComponentsStyleSheet] = useState(() =&gt; new ServerStyleSheet())

  useServerInsertedHTML(() =&gt; {
    const styles = styledComponentsStyleSheet.getStyleElement()
    styledComponentsStyleSheet.instance.clearTag()
    return &lt;&gt;{styles}&lt;/&gt;
  })

  if (typeof window !== &#39;undefined&#39;) return &lt;&gt;{children}&lt;/&gt;

  return (
    &lt;StyleSheetManager sheet={styledComponentsStyleSheet.instance}&gt;
      {children}
    &lt;/StyleSheetManager&gt;
  )
}</code></pre><p><strong>layout 적용</strong></p>
<pre><code>import StyledComponentsRegistry from &#39;./lib/registry&#39;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    &lt;html&gt;
      &lt;body&gt;
        &lt;StyledComponentsRegistry&gt;{children}&lt;/StyledComponentsRegistry&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  )
}</code></pre><p><strong>SWC plugin 설정</strong></p>
<pre><code>npm i -D @swc/plugin-styled-components</code></pre><p><strong>.swcrc 추가</strong></p>
<pre><code>{
  &quot;jsc&quot;: {
    &quot;experimental&quot;: {
      &quot;plugins&quot;: [
        [
          &quot;@swc/plugin-styled-components&quot;,
          {
            &quot;ssr&quot;: true
          }
        ]
      ]
    }
  }
}</code></pre><h3 id="참고">참고</h3>
<blockquote>
<p><a href="https://nextjs.org/docs/app/building-your-application/styling/css-in-js#styled-components">공식문서</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[refers to a value, but is being used as a type here]]></title>
            <link>https://velog.io/@westzero_dev/React-refers-to-a-value-but-is-being-used-as-a-type-here-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@westzero_dev/React-refers-to-a-value-but-is-being-used-as-a-type-here-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Thu, 25 Jan 2024 17:04:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Storybook에서 story를 처음 작성 중이었는데 <code>&#39;TodayRecommendItem&#39; refers to a value, but is being used as a type here. Did you mean &#39;typeof TodayRecommendItem&#39;?</code> 라는 에러가 떴다. 분명 컴포넌트를 import 했는데 왜 타입으로 쓴다는 걸까? 이해가 안되었는데 ....</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/12962582-b513-4edb-9ca9-2d7f438df16f/image.png" alt=""></p>
<p>내 파일명을 보니 <code>TodayRecommendItem.stories.ts</code> 였다
<code>TodayRecommendItem.stories.tsx</code> 로 수정하니 바로 해결되었다.</p>
<h2 id="ts-와-tsx의-차이점">.ts 와 .tsx의 차이점</h2>
<ol>
<li>.ts 는 typescript만 쓰는 경우</li>
<li>.tsx 는 react component 와 같이 쓰는 경우</li>
<li>.tsx는 jsx 문법 사용 가능</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 상태관리 라이브러리 Zustand]]></title>
            <link>https://velog.io/@westzero_dev/React-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-Zustand</link>
            <guid>https://velog.io/@westzero_dev/React-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-Zustand</guid>
            <pubDate>Mon, 22 Jan 2024 17:03:25 GMT</pubDate>
            <description><![CDATA[<p><a href="https://github.com/pmndrs/zustand/blob/main/docs/typescript.md#independent-slices-pattern">Zustand 공식문서</a></p>
<h2 id="zustand">Zustand?</h2>
<blockquote>
<p>Zustand는 독일어로 <code>상태</code>라는 뜻을 가진 상태 관리 라이브러리이다</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/westzero_dev/post/05d87816-c97a-4f76-a42b-ff0dffd132b2/image.png" alt=""></p>
<p>3번의 프로젝트를 진행하는 동안 Redux-Toolkit을 사용하여 상태관리를 했었다. <code>Zustand</code>의 경우 코드가 간단하고, 요즘 가장 인기 있는 라이브러리여서 트렌드를 따라~(주변에서 곰돌이가 귀엽다고 추천해줬었다) 이번 프로젝트 때 도입해보려고 한다.</p>
<h3 id="how-to-install-zustand">How to install Zustand</h3>
<ol>
<li><code>yarn add zustand</code></li>
<li><code>npm install zustand</code></li>
</ol>
<h3 id="zustand-example">Zustand Example</h3>
<blockquote>
<p>모달창 open 여부를 관리하는 예제를 만들어보았다.</p>
</blockquote>
<ol>
<li>store를 생성해준다.<pre><code>LoginModalStore.ts
</code></pre></li>
</ol>
<p>// zustand import
import create from &#39;zustand&#39;;</p>
<p>interface ModalStore {
    isOpen: boolean;
    openModal: () =&gt; void;
    closeModal: () =&gt; void;
}</p>
<p>// 상태 컨테이너 생성
const useModalStore = create<ModalStore>((set) =&gt; ({
    isOpen: false,
    openModal: () =&gt; set({ isOpen: true }),
    closeModal: () =&gt; set({ isOpen: false }),
  }));</p>
<p>  export default useModalStore;</p>
<pre><code>
2. 사용하고 싶은 곳에서 사용해준다</code></pre><p>&#39;use client&#39;</p>
<p>// 만들어 놓은 useModalStore를 활용한다
import useModalStore from &quot;@/zustand/LogInModalStore&quot;</p>
<p>// 로그아웃 일 때의 컴포넌트
export default function NavRightLoggedOut () {
    // 로그아웃일 때 로그인 모달 창 오픈 버튼이 되어줘야 한다
    const { openModal } = useModalStore();</p>
<pre><code>return (
    &lt;div&gt;
        &lt;div onClick={openModal}&gt;로그인/회원가입&lt;/div&gt;
        &lt;div onClick={openModal}&gt;내상점&lt;/div&gt;
    &lt;/div&gt;
)</code></pre><p>}</p>
<p>```</p>
<p>가볍게 예제 하나만 구현해봤을 때, redux-toolkit 보다 체감상 쉬운 것 같다. </p>
]]></description>
        </item>
    </channel>
</rss>