<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>wkawha_rowk.log</title>
        <link>https://velog.io/</link>
        <description>1일 1백준을 목표로</description>
        <lastBuildDate>Sat, 07 Jun 2025 13:14:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>wkawha_rowk.log</title>
            <url>https://velog.velcdn.com/images/wkawha_rowk/profile/1e50961e-7a12-4217-aeb1-7775a82ac609/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. wkawha_rowk.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/wkawha_rowk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[JS 문법 - reduce]]></title>
            <link>https://velog.io/@wkawha_rowk/JS-%EB%AC%B8%EB%B2%95-reduce</link>
            <guid>https://velog.io/@wkawha_rowk/JS-%EB%AC%B8%EB%B2%95-reduce</guid>
            <pubDate>Sat, 07 Jun 2025 13:14:54 GMT</pubDate>
            <description><![CDATA[<p>JavaScript의 <code>reduce</code>는 배열의 <strong>모든 요소를 하나로 축약(누적)</strong> 할 때 사용하는 <strong>고차 함수</strong>. 배열의 합, 곱, 평균, 최대값 등을 구할 때 자주 사용</p>
<hr>
<h3 id="✅-기본-문법">✅ 기본 문법</h3>
<pre><code class="language-javascript">array.reduce((accumulator, currentValue, currentIndex, array) =&gt; {
  // return 새로운 accumulator
}, initialValue);</code></pre>
<ul>
<li><code>accumulator</code>: 이전 반복에서 리턴된 값 (누적값)</li>
<li><code>currentValue</code>: 현재 요소</li>
<li><code>initialValue</code>: accumulator의 초기값 (생략 가능하지만 보통 넣는 게 안전)</li>
</ul>
<hr>
<h3 id="🔍-예제들">🔍 예제들</h3>
<h4 id="1-배열의-합">1. <strong>배열의 합</strong></h4>
<pre><code class="language-javascript">const nums = [1, 2, 3, 4, 5];
const sum = nums.reduce((acc, cur) =&gt; acc + cur, 0);
console.log(sum); // 15</code></pre>
<h4 id="2-배열의-곱">2. <strong>배열의 곱</strong></h4>
<pre><code class="language-javascript">const nums = [1, 2, 3, 4];
const product = nums.reduce((acc, cur) =&gt; acc * cur, 1);
console.log(product); // 24</code></pre>
<h4 id="3-최댓값-구하기">3. <strong>최댓값 구하기</strong></h4>
<pre><code class="language-javascript">const nums = [5, 2, 9, 1];
const max = nums.reduce((acc, cur) =&gt; (acc &gt; cur ? acc : cur));
console.log(max); // 9</code></pre>
<h4 id="4-객체-배열에서-총합-구하기">4. <strong>객체 배열에서 총합 구하기</strong></h4>
<pre><code class="language-javascript">const items = [
  { name: &quot;apple&quot;, price: 1000 },
  { name: &quot;banana&quot;, price: 500 },
  { name: &quot;cherry&quot;, price: 2000 }
];

const total = items.reduce((sum, item) =&gt; sum + item.price, 0);
console.log(total); // 3500</code></pre>
<hr>
<h3 id="💡-자주-하는-실수">💡 자주 하는 실수</h3>
<ul>
<li><code>initialValue</code>를 생략하면, 첫 번째 요소가 <code>acc</code>로 자동 설정되고 루프는 두 번째 요소부터 시작</li>
<li>비어 있는 배열에 <code>reduce</code>를 쓰면 오류 발생. 그래서 항상 <code>initialValue</code>를 넣는 게 안전</li>
</ul>
<hr>
<h3 id="관련문제">관련문제</h3>
<p>프로그래머스 - 배열 두 배 만들기
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/120809">https://school.programmers.co.kr/learn/courses/30/lessons/120809</a></p>
<pre><code class="language-javascript">const solution = (numbers) =&gt; {
  return numbers.reduce((number, element) =&gt; [...number, element*2], []);
  // numbers.map((element)=&gt; element*2)); 도 가능!
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[FCM 알림설정]]></title>
            <link>https://velog.io/@wkawha_rowk/FCM-%EC%95%8C%EB%A6%BC%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@wkawha_rowk/FCM-%EC%95%8C%EB%A6%BC%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 03 Apr 2025 04:38:27 GMT</pubDate>
            <description><![CDATA[<h1 id="fcm-알림설정">FCM 알림설정</h1>
<h2 id="✅-프로젝트-환경">✅ 프로젝트 환경</h2>
<ul>
<li><strong>Frontend</strong>: React + Vite</li>
<li><strong>Service Worker</strong>: <code>vite-plugin-pwa</code> + Workbox</li>
<li><strong>Push 서비스</strong>: Firebase Cloud Messaging (FCM)</li>
<li><strong>실행 환경</strong>: 데스크톱 PWA (모바일 아님)</li>
</ul>
<h2 id="✅-최종-목표">✅ 최종 목표</h2>
<blockquote>
<p>📥 포그라운드 및 백그라운드 모두에서 푸시 알림을 수신하고</p>
<p>🔔 알림 팝업(Notification)을 정상적으로 사용자에게 표시한다.</p>
</blockquote>
<h2 id="🔧-설정-순서-및-내용">🔧 설정 순서 및 내용</h2>
<h3 id="1-firebase-프로젝트-생성-및-fcm-설정">1. Firebase 프로젝트 생성 및 FCM 설정</h3>
<ul>
<li>Firebase 콘솔에서 프로젝트 생성</li>
<li>&quot;클라우드 메시징&quot; 탭에서 <code>Web Push 인증서 (VAPID 키)</code> 발급</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/e1a7ea5c-0f6e-4106-aa7f-c35f42f5356c/image.png" alt=""></p>
<ul>
<li>Authentication → 로그인 방법 → 새 제공업체 추가, 이메일/비밀번호 설정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/9f8454d0-8045-4a06-a0c8-54cc0616864d/image.png" alt=""></p>
<h3 id="2-프로젝트-firebase-설정">2. 프로젝트 firebase 설정</h3>
<ol>
<li><strong><code>vite-plugin-pwa</code>로 관리하는 <code>src/sw.js</code></strong> 방식</li>
<li><strong><code>public/firebase-messaging-sw.js</code></strong> 방식</li>
</ol>
<h3 id="🔀-두-방식의-차이-요약">🔀 두 방식의 차이 요약</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th><code>src/sw.js</code> (vite-plugin-pwa)</th>
<th><code>public/firebase-messaging-sw.js</code></th>
</tr>
</thead>
<tbody><tr>
<td>관리 주체</td>
<td>Vite / Workbox</td>
<td>Firebase 자체</td>
</tr>
<tr>
<td>파일 위치</td>
<td><code>src/</code> → 빌드시 <code>dist/sw.js</code></td>
<td><code>public/</code> → 빌드시 그대로 복사</td>
</tr>
<tr>
<td>백그라운드 알림 처리</td>
<td><code>onBackgroundMessage()</code> 직접 추가</td>
<td>Firebase가 자동 참조</td>
</tr>
<tr>
<td>확장성</td>
<td>🟢 Workbox 사용 가능 (캐시 등)</td>
<td>🔴 순수 백그라운드 알림만 가능</td>
</tr>
<tr>
<td>추천 용도</td>
<td>✅ PWA + 알림 모두 커스텀할 때</td>
<td>🔧 알림만 간단하게 쓸 때</td>
</tr>
</tbody></table>
<p>나는 vite-plugin-pwa 방식으로 채택했다.</p>
<h2 id="⚙️-단계별-정리">⚙️ 단계별 정리</h2>
<hr>
<h3 id="①-설치">① 설치</h3>
<pre><code class="language-bash">npm i vite-plugin-pwa</code></pre>
<hr>
<h3 id="②-viteconfigts-설정">② <code>vite.config.ts</code> 설정</h3>
<pre><code>import { defineConfig } from &#39;vite&#39;;
import react from &#39;@vitejs/plugin-react&#39;;
import { VitePWA } from &#39;vite-plugin-pwa&#39;;
import path from &#39;path&#39;;

export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      strategies: &#39;injectManifest&#39;, // sw.js 직접 커스터마이징
      srcDir: &#39;src&#39;,
      filename: &#39;sw.js&#39;,
      includeAssets: [&#39;pwa-192x192.png&#39;, &#39;pwa-512x512.png&#39;],
      manifest: {
        name: &#39;My App&#39;,
        short_name: &#39;MyApp&#39;,
        start_url: &#39;/&#39;,
        display: &#39;standalone&#39;,
        background_color: &#39;#ffffff&#39;,
        theme_color: &#39;#ffffff&#39;,
        icons: [
          {
            src: &#39;/pwa-192x192.png&#39;,
            sizes: &#39;192x192&#39;,
            type: &#39;image/png&#39;,
          },
          {
            src: &#39;/pwa-512x512.png&#39;,
            sizes: &#39;512x512&#39;,
            type: &#39;image/png&#39;,
          },
        ],
      },
      injectManifest: {
        globPatterns: [&#39;**/*.{js,css,html,svg,png}&#39;],
      },
      devOptions: {
        enabled: true,
        type: &#39;module&#39;,
        navigateFallback: &#39;index.html&#39;,
        navigateFallbackDenylist: [/^\/firebase-messaging-sw\.js$/],
        // firebase에서 기본적으로 firebase-messaging-sw.js 파일을 참고하려고 하기 때문에 이를 방지하려고 옵션을 추가. src/sw.js 사용해야함
      },
    }),
  ],
  resolve: {
    alias: {
      &#39;@&#39;: path.resolve(__dirname, &#39;./src&#39;),
    },
  },
});
</code></pre><hr>
<h3 id="③-srcswjs-생성-서비스워커--백그라운드-알림-포함">③ <code>src/sw.js</code> 생성 (서비스워커 + 백그라운드 알림 포함)</h3>
<pre><code class="language-jsx">/// &lt;reference lib=&quot;webworker&quot; /&gt;
import {
  cleanupOutdatedCaches,
  createHandlerBoundToURL,
  precacheAndRoute,
} from &#39;workbox-precaching&#39;;
import { clientsClaim } from &#39;workbox-core&#39;;
import { NavigationRoute, registerRoute } from &#39;workbox-routing&#39;;

precacheAndRoute(self.__WB_MANIFEST);
cleanupOutdatedCaches();
registerRoute(new NavigationRoute(createHandlerBoundToURL(&#39;index.html&#39;)));

self.skipWaiting();
clientsClaim();

// ✅ FCM 스크립트
importScripts(&#39;https://www.gstatic.com/firebasejs/9.6.10/firebase-app-compat.js&#39;);
importScripts(&#39;https://www.gstatic.com/firebasejs/9.6.10/firebase-messaging-compat.js&#39;);

firebase.initializeApp({
  apiKey: &#39;YOUR_API_KEY&#39;,
  authDomain: &#39;...&#39;,
  projectId: &#39;...&#39;,
  storageBucket: &#39;...&#39;,
  messagingSenderId: &#39;...&#39;,
  appId: &#39;...&#39;,
});

const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) =&gt; {
  console.log(&#39;📦 백그라운드 메시지:&#39;, payload);
  const { title, body } = payload.notification || {};
  self.registration.showNotification(title || &#39;알림&#39;, {
    body: body || &#39;내용 없음&#39;,
    icon: &#39;/pwa-192x192.png&#39;,
  });
});
</code></pre>
<hr>
<h3 id="④-firebase-초기화-srcfirebasejs">④ Firebase 초기화 (<code>src/firebase.js</code>)</h3>
<pre><code>import { initializeApp } from &#39;firebase/app&#39;;
import { getMessaging, getToken, onMessage } from &#39;firebase/messaging&#39;;

const app = initializeApp({
  apiKey: &#39;YOUR_API_KEY&#39;,
  authDomain: &#39;...&#39;,
  projectId: &#39;...&#39;,
  messagingSenderId: &#39;...&#39;,
  appId: &#39;...&#39;,
});

export const requestPermission = async () =&gt; {
  const permission = await Notification.requestPermission();
  if (permission === &#39;granted&#39;) {
    const registration = await navigator.serviceWorker.ready;
    const token = await getToken(getMessaging(app), {
      vapidKey: &#39;YOUR_VAPID_KEY&#39;,
      serviceWorkerRegistration: registration,
    });
    // 중요 여기 토큰 값이 메세지 테스트에 필요함
    console.log(&#39;🔥 FCM 토큰:&#39;, token);
    return token;
  }
};

export const listenForegroundMessage = () =&gt; {
  const messaging = getMessaging(app);
  onMessage(messaging, (payload) =&gt; {
    console.log(&#39;📥 포그라운드 메시지:&#39;, payload);
    const { title, body } = payload.notification || {};
    if (Notification.permission === &#39;granted&#39;) {
      new Notification(title ?? &#39;알림&#39;, {
        body: body ?? &#39;내용 없음&#39;,
        icon: &#39;/pwa-192x192.png&#39;,
      });
    }
  });
};
</code></pre><hr>
<h3 id="⑤-fcm-토큰-발급-시-호출">⑤ FCM 토큰 발급 시 호출</h3>
<pre><code class="language-jsx">useEffect(() =&gt; {
  requestPermission();
  listenForegroundMessage();
}, []);
</code></pre>
<hr>
<h3 id="⑥-알림-테스트">⑥ 알림 테스트</h3>
<ol>
<li><code>npm run build</code></li>
<li><code>npm run preview</code></li>
<li>Firebase 콘솔 → 클라우드 메시징 → 테스트 전송</li>
</ol>
<p>아까 console에 찍었던 token 값을 저기 <code>FCM 등록 토큰 추가</code> 에 넣으면 된다</p>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/a1bb3e94-4375-4e02-a30a-cce08f3191fe/image.png" alt=""></p>
<hr>
<h2 id="✅-팁-알림-안-뜰-때-체크리스트">✅ 팁: 알림 안 뜰 때 체크리스트</h2>
<table>
<thead>
<tr>
<th>문제</th>
<th>해결 방법</th>
</tr>
</thead>
<tbody><tr>
<td>알림 안 뜸</td>
<td>윈도우/브라우저 알림 설정 켜기</td>
</tr>
<tr>
<td>icon 경로 오류</td>
<td><code>/pwa-192x192.png</code> 로 절대경로 사용</td>
</tr>
<tr>
<td>서비스워커 등록 안 됨</td>
<td><code>vite.config.ts</code> + <code>src/sw.js</code> 위치 확인</td>
</tr>
<tr>
<td>DevTools 열려 있음</td>
<td>닫고 다시 테스트 (특히 백그라운드)</td>
</tr>
</tbody></table>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[React + vite + pwa 프로젝트 셋팅]]></title>
            <link>https://velog.io/@wkawha_rowk/React-vite-pwa-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%85%8B%ED%8C%85</link>
            <guid>https://velog.io/@wkawha_rowk/React-vite-pwa-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%85%8B%ED%8C%85</guid>
            <pubDate>Sat, 21 Dec 2024 04:39:19 GMT</pubDate>
            <description><![CDATA[<h1 id="react--vite--pwa-프로젝트-셋팅-가이드">React + Vite + PWA 프로젝트 셋팅 가이드</h1>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/6284f716-8dd6-4685-b7d7-131abc5760d0/image.png" alt=""></p>
<p>이 글에서는 <strong>React</strong>, <strong>Vite</strong>, <strong>PWA</strong>(Progressive Web App)를 결합하여 빠르고 설치 가능한 웹 애플리케이션을 설정하는 과정을 다룹니다.</p>
<hr>
<h2 id="1-프로젝트-초기화">1. 프로젝트 초기화</h2>
<h3 id="11-프로젝트-생성-명령어">1.1 프로젝트 생성 명령어</h3>
<p>Vite PWA 플러그인을 포함한 프로젝트를 생성하기 위해 다음 명령어를 실행합니다:</p>
<pre><code class="language-bash">npm create @vite-pwa/pwa@latest</code></pre>
<p>명령어 실행 후 제공되는 옵션에서 원하는 설정을 선택합니다. 여기서는 <code>React</code>와 <code>TypeScript</code>를 사용하는 프로젝트를 기준으로 설명합니다.</p>
<h3 id="12-옵션-선택-과정">1.2 옵션 선택 과정</h3>
<ul>
<li><strong>Framework</strong>: <code>React</code></li>
<li><strong>Use TypeScript?</strong>: <code>Yes</code></li>
<li><strong>Use SW (Service Worker)?</strong>: <code>Yes</code></li>
<li><strong>Strategies</strong>: <code>generateSW</code> (자동 생성)</li>
<li><strong>Router Mode</strong>: <code>History</code></li>
<li><strong>Use Framework Router?</strong>: <code>Yes</code> (React Router 사용)</li>
<li><strong>Use Icons?</strong>: <code>Yes</code> (PWA 아이콘 자동 생성)</li>
<li><strong>Theme Color</strong>: 원하는 색상 입력 (예: <code>#ffffff</code>)</li>
</ul>
<p>저는 Icons를 아래 사이트를 이용해 따로 만들었습니다.
📌 <a href="https://favicomatic.com/">https://favicomatic.com/</a></p>
<h3 id="13-생성된-디렉토리-구조">1.3 생성된 디렉토리 구조</h3>
<p>프로젝트 생성 후 주요 파일과 디렉토리는 다음과 같습니다:</p>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/f41b0354-e224-4edb-b856-9fae1f66bcbe/image.png" alt=""></p>
<p>icons폴더 내에 제가 사용할 아이콘 이미지 파일들을 넣어두었습니다.</p>
<hr>
<h2 id="2-pwa-설정-및-주요-파일-설명">2. PWA 설정 및 주요 파일 설명</h2>
<h3 id="21-manifestjson">2.1 <code>manifest.json</code></h3>
<p>PWA의 설정 파일로, 앱 이름, 아이콘, 테마 색상 등을 정의합니다. 기본적으로 <code>public/manifest.json</code>에 위치하며, 다음과 같은 내용이 포함됩니다:</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;My PWA App&quot;,
  &quot;short_name&quot;: &quot;PWA App&quot;,
  &quot;theme_color&quot;: &quot;#ffffff&quot;,
  &quot;background_color&quot;: &quot;#000000&quot;,
  &quot;display&quot;: &quot;standalone&quot;,
  &quot;icons&quot;: [
    {
      &quot;src&quot;: &quot;/icon-192x192.png&quot;,
      &quot;sizes&quot;: &quot;192x192&quot;,
      &quot;type&quot;: &quot;image/png&quot;
    },
    {
      &quot;src&quot;: &quot;/icon-512x512.png&quot;,
      &quot;sizes&quot;: &quot;512x512&quot;,
      &quot;type&quot;: &quot;image/png&quot;
    }
  ]
}</code></pre>
<h3 id="22-viteconfigts">2.2 <code>vite.config.ts</code></h3>
<p>Vite 설정 파일에서 PWA 플러그인을 설정합니다. 위 <strong>Strategies</strong> 옵션에서 <code>generateSW</code> 채택하여 Service Worker를 자동 생성합니다.</p>
<pre><code class="language-typescript">import { defineConfig } from &#39;vite&#39;;
import react from &#39;@vitejs/plugin-react&#39;;
import { VitePWA } from &#39;vite-plugin-pwa&#39;;

export default defineConfig({
  plugins: [
    react(),
    VitePWA({
      registerType: &#39;autoUpdate&#39;,
      manifest: {
        name: &#39;My PWA App&#39;,
        short_name: &#39;PWA App&#39;,
        theme_color: &#39;#ffffff&#39;,
        icons: [
          {
            src: &quot;icons/apple-touch-icon-60x60.png&quot;, // 사용할 이미지 경로
            sizes: &quot;60x60&quot;,
            type: &quot;image/png&quot;,
          },
          {
            src: &quot;icons/apple-touch-icon-152x152.png&quot;,
            sizes: &quot;192x192&quot;,
            type: &quot;image/png&quot;,
          },
        ],
      },
    }),
  ],
});</code></pre>
<hr>
<h2 id="3-react-router-설정">3. React Router 설정</h2>
<p>PWA에서 라우팅을 관리하려면 React Router를 설치하고 설정해야 합니다:</p>
<h3 id="31-react-router-설치">3.1 React Router 설치</h3>
<pre><code class="language-bash">npm install react-router-dom</code></pre>
<h3 id="32-라우팅-설정">3.2 라우팅 설정</h3>
<p><code>src/App.tsx</code> 파일에 라우터를 설정합니다:</p>
<pre><code class="language-tsx">import { BrowserRouter as Router, Routes, Route } from &#39;react-router-dom&#39;;
import Home from &#39;./components/Home&#39;;
import About from &#39;./components/About&#39;;

function App() {
  return (
    &lt;Router&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
        &lt;Route path=&quot;/about&quot; element={&lt;About /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/Router&gt;
  );
}

export default App;</code></pre>
<hr>
<h2 id="4-빌드-및-pwa-테스트">4. 빌드 및 PWA 테스트</h2>
<h3 id="41-개발-서버-실행">4.1 개발 서버 실행</h3>
<p>개발 서버를 실행해 PWA 기능을 테스트합니다:</p>
<pre><code class="language-bash">npm run dev</code></pre>
<ul>
<li>브라우저에서 <code>http://localhost:3000</code>으로 접속.</li>
<li>Chrome 개발자 도구 → Application → Service Workers 탭에서 Service Worker 등록 상태를 확인합니다.</li>
</ul>
<h3 id="42-프로덕션-빌드">4.2 프로덕션 빌드</h3>
<pre><code class="language-bash">npm run build
npm run preview</code></pre>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/a2d1ac82-48b2-4811-b4ee-b9256232b3fd/image.png" alt=""></p>
<p>빌드된 앱에서 PWA 설치 가능 여부를 확인합니다. 브라우저 주소창 오른쪽에 &quot;설치&quot; 아이콘이 표시됩니다.</p>
<hr>
<p>추후 버셀로 베포까지 셋팅하는 내용을 추가할 예정입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준-java] 12919. A와 B 2]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-java-12919.-A%EC%99%80-B-2</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-java-12919.-A%EC%99%80-B-2</guid>
            <pubDate>Thu, 17 Oct 2024 03:00:44 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>수빈이는 A와 B로만 이루어진 영어 단어 존재한다는 사실에 놀랐다. 대표적인 예로 AB (Abdominal의 약자), BAA (양의 울음 소리), AA (용암의 종류), ABBA (스웨덴 팝 그룹)이 있다.</p>
<p>이런 사실에 놀란 수빈이는 간단한 게임을 만들기로 했다. 두 문자열 S와 T가 주어졌을 때, S를 T로 바꾸는 게임이다. 문자열을 바꿀 때는 다음과 같은 두 가지 연산만 가능하다.</p>
<p>문자열의 뒤에 A를 추가한다.
문자열의 뒤에 B를 추가하고 문자열을 뒤집는다.
주어진 조건을 이용해서 S를 T로 만들 수 있는지 없는지 알아내는 프로그램을 작성하시오. </p>
<h1 id="입력">입력</h1>
<p>첫째 줄에 S가 둘째 줄에 T가 주어진다. (1 ≤ S의 길이 ≤ 49, 2 ≤ T의 길이 ≤ 50, S의 길이 &lt; T의 길이)</p>
<h1 id="출력">출력</h1>
<p>S를 T로 바꿀 수 있으면 1을 없으면 0을 출력한다.</p>
<p>출처: <a href="https://www.acmicpc.net/problem/12919">https://www.acmicpc.net/problem/12919</a></p>
<h1 id="접근법1">접근법1</h1>
<p>S를 T로 바꿀 수 있는지 직접 조건에 맞게 수행해서 확인하는 방법을 채택했다.
그러나 시간초과가 났다.</p>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    StringTokenizer tokens;
    String S, T;
    int answer;

    void Input() throws IOException {
        S = input.readLine();
        T = input.readLine();
    }

    void recursive(String crnt) {
        if (crnt.equals(T)) {
            System.out.println(1);
            System.exit(0);
        }
        if (crnt.length() &gt; T.length()) {
            return;
        }
        recursive(crnt + &quot;A&quot;);
        StringBuffer sb = new StringBuffer(crnt + &quot;B&quot;);
        String reverse = sb.reverse().toString();
        recursive(reverse);
    }

    void Solve() throws IOException {
        Input();
        recursive(S);
        System.out.println(0);
    }

    public static void main(String[] args) throws Exception {
        new Main().Solve();

    }

}</code></pre>
<h1 id="접근법-2">접근법 2</h1>
<p>그래서 반대로 T에서 S를 만드는 방식을 채택하였다.
시간초과가 나지않고 빠른 속도로 통과하였다.
<img src="https://velog.velcdn.com/images/wkawha_rowk/post/bce02b05-f1a9-44e0-92dd-be89ee2fcf9a/image.png" alt=""></p>
<pre><code class="language-java">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    StringTokenizer tokens;
    String S, T;
    int answer;

    void Input() throws IOException {
        S = input.readLine();
        T = input.readLine();
    }

    void makeS(String crnt) {
        if (crnt.equals(S)) {
            answer = 1;
            return;
        }

        if (crnt.length() &gt; S.length()) {
            if (crnt.charAt(crnt.length() - 1) == &#39;A&#39;) {
                makeS(crnt.substring(0, crnt.length() - 1)); // &#39;A&#39;를 제거
            }
            if (crnt.charAt(0) == &#39;B&#39;) {
                String tmp = crnt.substring(1); // &#39;B&#39;를 제거하고 뒤집기
                StringBuilder sb = new StringBuilder(tmp);
                tmp = sb.reverse().toString();
                makeS(tmp);
            }
        }
    }

    void Solve() throws IOException {
        Input();
        makeS(T);
        System.out.println(answer);
    }

    public static void main(String[] args) throws Exception {
        new Main().Solve();

    }

}</code></pre>
<h1 id="왜-s에서-t로-가는-방법은-시간초과가-났을까">왜 S에서 T로 가는 방법은 시간초과가 났을까?</h1>
<p>gpt에게 물어봤다.</p>
<p><code>S</code>에서 <code>T</code>로 가는 방식이 시간초과가 나는 이유는, <strong>가능한 모든 경우를 탐색해야 하기 때문</strong>입니다. 이 방식에서는 재귀적으로 각 단계에서 두 가지 선택을 할 수 있는데:</p>
<ol>
<li>문자열에 <code>&#39;A&#39;</code>를 추가한다.</li>
<li>문자열에 <code>&#39;B&#39;</code>를 추가하고 뒤집는다.</li>
</ol>
<p>이 두 가지 선택을 모든 단계에서 고려해야 하기 때문에, <strong>가능한 문자열의 조합 수가 기하급수적으로 증가</strong>하게 됩니다. 그 결과 탐색할 경우의 수가 너무 많아져서 시간 초과가 발생합니다.</p>
<h3 id="왜-기하급수적으로-증가하는가">왜 기하급수적으로 증가하는가?</h3>
<ul>
<li>매번 두 가지 선택을 할 수 있기 때문에, 한 번의 탐색이 끝나면 그 다음 단계에서 또다시 두 가지 선택을 해야 합니다.</li>
<li>만약 최대 <code>k</code> 단계(즉, <code>T</code>의 길이에서 <code>S</code>의 길이를 뺀 값)만큼의 변환을 해야 한다면, 최악의 경우 <strong>2^k개의 경우의 수</strong>를 탐색해야 합니다.<ul>
<li>예를 들어, <code>S</code>에서 <code>T</code>로 가는 과정이 10단계 정도 걸린다면 2^10 = 1024가지 경우를 고려해야 하고, 이 값은 단계가 늘어날수록 기하급수적으로 증가합니다.</li>
</ul>
</li>
</ul>
<h3 id="구체적인-예시">구체적인 예시</h3>
<ul>
<li><p><code>S = A</code>이고 <code>T = BAAA</code>인 경우, <code>S</code>에서 <code>T</code>로 가는 과정에서 가능성 있는 모든 변환은 다음과 같이 됩니다:</p>
<ol>
<li>첫 번째 선택: <code>&#39;A&#39;</code>에 <code>&#39;A&#39;</code>를 추가하거나, <code>&#39;B&#39;</code>를 추가하고 뒤집기</li>
<li>두 번째 선택: 각 선택에 대해 다시 <code>&#39;A&#39;</code>를 추가하거나 <code>&#39;B&#39;</code>를 추가하고 뒤집기</li>
</ol>
<p>이런 식으로 매번 두 가지 선택을 해야 하므로 가능한 경우의 수가 <strong>2^k</strong>에 비례하게 됩니다.</p>
</li>
</ul>
<h3 id="반면-t에서-s로-가는-방식은-왜-더-효율적인가">반면, <code>T</code>에서 <code>S</code>로 가는 방식은 왜 더 효율적인가?</h3>
<p><code>T</code>에서 <code>S</code>로 가는 방식이 효율적인 이유는, <strong>문자열의 길이를 매번 확실히 줄여가며 탐색할 수 있기 때문</strong>입니다. <code>T</code>에서 <code>S</code>로 가는 과정에서는 다음 두 가지 변환만 가능합니다:</p>
<ol>
<li>마지막 문자가 <code>&#39;A&#39;</code>일 경우 <code>&#39;A&#39;</code>를 제거한다.</li>
<li>마지막 문자가 <code>&#39;B&#39;</code>일 경우 <code>&#39;B&#39;</code>를 제거하고 문자열을 뒤집는다.</li>
</ol>
<p>이 방법은 매번 문자열의 길이를 확실하게 1씩 줄이기 때문에, 탐색하는 경우의 수가 줄어듭니다. <code>T</code>에서 <code>S</code>로 변환할 때의 경우의 수는 최대 <code>T의 길이 - S의 길이</code>만큼의 변환 과정만 고려하면 되므로, <strong>탐색해야 할 경우의 수가 선형적으로 줄어듭니다</strong>.</p>
<h3 id="결론">결론</h3>
<ul>
<li><code>S</code>에서 <code>T</code>로 가는 방식은 매번 두 가지 선택을 하며 탐색하기 때문에, 기하급수적으로 경우의 수가 증가하여 <strong>시간초과</strong>가 발생합니다.</li>
<li>반면, <code>T</code>에서 <code>S</code>로 가는 방식은 문자열의 길이를 매번 줄이며 탐색하므로 <strong>효율적</strong>입니다.<h1 id="실험">실험</h1>
재귀함수가 몇번 호출되었는지 확인하기 위해서 재귀함수에서 전역변수 cnt를 계속해서 1씩 증가시켰다.</li>
</ul>
<h3 id="입력-1">입력</h3>
<p>S = A
T = BABABABABA</p>
<h3 id="결과">결과</h3>
<ol>
<li><p>S&gt;T의 경우</p>
<pre><code class="language-java"> void makeT(String crnt) {
     cnt++;
     if (crnt.equals(T)) {
         System.out.println(1);
         System.out.println(cnt);
         System.exit(0);
     }
     if (crnt.length() &lt; T.length()) {
         recursive(crnt + &quot;A&quot;);
         StringBuffer sb = new StringBuffer(crnt + &quot;B&quot;);
         String reverse = sb.reverse().toString();
         recursive(reverse);
     }
 }</code></pre>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/29ae5d5a-9c5f-4791-9512-7960a513b2e2/image.png" alt=""></p>
</li>
<li><p>T&gt;S의 경우</p>
<pre><code class="language-java"> void makeS(String crnt) {
     cnt++;
     if (crnt.equals(S)) {
         answer = 1;
         return;
     }

     if (crnt.length() &gt; S.length()) {
         if (crnt.charAt(crnt.length() - 1) == &#39;A&#39;) {
             makeS(crnt.substring(0, crnt.length() - 1)); // &#39;A&#39;를 제거
         }
         if (crnt.charAt(0) == &#39;B&#39;) {
             String tmp = crnt.substring(1); // &#39;B&#39;를 제거하고 뒤집기
             StringBuilder sb = new StringBuilder(tmp);
             tmp = sb.reverse().toString();
             makeS(tmp);
         }
     }
 }</code></pre>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/00257ec9-1a08-4159-bd4b-79845d8f8ca9/image.png" alt=""></p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Electron + React + Tailwind 프로젝트 셋팅 과정]]></title>
            <link>https://velog.io/@wkawha_rowk/Electron-React-Tailwind-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%85%8B%ED%8C%85-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@wkawha_rowk/Electron-React-Tailwind-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%85%8B%ED%8C%85-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Tue, 21 May 2024 04:32:04 GMT</pubDate>
            <description><![CDATA[<h3 id="프로젝트-셋팅-과정에서-겪은-문제">프로젝트 셋팅 과정에서 겪은 문제</h3>
<p>처음에는 react - electron - tailwind 순서로 설치 진행하였더니, tailwind가 적용되지 않았다.</p>
<p>폭풍 구글링을 통해서 electron-react-boilerplate라는 것을 발견했다.</p>
<blockquote>
<p>electron-react-boilerplate는 Electron과 React를 결합한 애플리케이션 개발을 위한 보일러플레이트 프로젝트이다. 빠르게 개발을 시작할 수 있도록 환경셋팅을 해둔 것이다. 여기에서는 Electron과 React를 함께 사용하고, Webpack을 이용해 번들링한다.</p>
</blockquote>
<p><a href="https://electron-react-boilerplate.js.org/">https://electron-react-boilerplate.js.org/</a></p>
<p>위 프로젝트를 사용하게 되는 경우 다음과 같은 장점이 있다.</p>
<h3 id="장점">장점</h3>
<ul>
<li>빠른 시작: 많은 설정이 이미 완료되어 있어, 바로 개발을 시작할 수 있음</li>
<li>최적화: 기본적으로 웹팩(Webpack) 구성, Hot Module Replacement (HMR), TypeScript 지원 등 많은 최적화가 포함</li>
<li>표준화된 구조: 프로젝트 구조가 표준화되어 있음</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>유연성 제한: 특정한 방식으로 설정되어 있어, 커스터마이징이 복잡</li>
<li>의존성 관리: 보일러플레이트가 업데이트되면, 종종 의존성 충돌이나 호환성 문제를 처리</li>
</ul>
<p>실제로 github에 들어가보면 기본 셋팅 구조가 복잡해 보인다. 그래도 내가 건드리는 폴더는 눈에 보이기 때문에 react를 어느정도 사용해 보았다면 크게 문제 는 없을 것 같았다.</p>
<p>하지만, 프로젝트 특성 상 간단한 기능들만 구현하면 되기에, 굳이 보일러플레이트의 여러 셋팅이 필요할 것 같진 않았다.</p>
<h3 id="해결">해결</h3>
<p>처음과 달리 설치 순서를 react - tailwind - electron 로 바꾸어 진행하였더니 다행히 해결 되었다.
이유는 잘 모르겠지만, 아마 설치하면서 의존성 문제나 버전 문제가 있어서 처음 방법이 실패한게 아닌가 싶다.</p>
<p>추후 셋팅 과정에 대해 상세히 남겨 놓아야 겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자주 쓰는 컴포넌트] - 그리드 레이아웃]]></title>
            <link>https://velog.io/@wkawha_rowk/%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83</link>
            <guid>https://velog.io/@wkawha_rowk/%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EA%B7%B8%EB%A6%AC%EB%93%9C-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83</guid>
            <pubDate>Mon, 22 Apr 2024 12:19:52 GMT</pubDate>
            <description><![CDATA[<h3 id="서론">서론</h3>
<p>인스타에서 발견한 릴스인데, 그리드 형식의 반응형 레이아웃이였다. 요긴하게 쓰일 것 같아 따라 만들어 보았다. 따라 만들면서 그리드 사용법을 간단하게나마 알 수 있었다.</p>
<h3 id="html">html</h3>
<pre><code class="language-html"> &lt;div class=&quot;layout&quot;&gt;
            &lt;header&gt;Header&lt;/header&gt;
            &lt;nav&gt;Sidebar&lt;/nav&gt;
            &lt;main&gt;Main&lt;/main&gt;
            &lt;article class=&quot;widget&quot;&gt;widget&lt;/article&gt;
            &lt;article class=&quot;statistics&quot;&gt;statistics&lt;/article&gt;
            &lt;footer&gt;Footer&lt;/footer&gt;
        &lt;/div&gt;</code></pre>
<h3 id="css">css</h3>
<pre><code class="language-css">.layout {
    display: grid;
    gap: 4px;
    grid-template-rows: 40px 40px auto 60px 60px 40px;
    grid-template-columns: 1fr;
    grid-template-areas: &quot;header&quot; &quot;sidebar&quot; &quot;main&quot; &quot;widget&quot; &quot;statistics&quot; &quot;footer&quot;;
    height: 100vh;
    text-align: center;
}

@media (min-width: 480px) {
    .layout {
        display: grid;
        grid-template-rows: 40px auto 80px 40px;
        grid-template-columns: 160px auto auto;
        grid-template-areas:
            &quot;header header header&quot;
            &quot;sidebar main main&quot;
            &quot;sidebar widget statistics&quot;
            &quot;footer footer footer&quot;;
    }
}

header {
    grid-area: header;
    background-color: blueviolet;
}

nav {
    grid-area: sidebar;
    background-color: yellow;
}

main {
    grid-area: main;
    background-color: aquamarine;
}

.widget {
    grid-area: widget;
    background-color: darkorange;
    width: 100%;
}

.statistics {
    grid-area: statistics;
    background-color: blanchedalmond;
    width: 100%;
}

footer {
    grid-area: footer;
    background-color: salmon;
}
</code></pre>
<h3 id="결과">결과</h3>
<p>!codepen[oobodipv-the-sasster/embed/ExJGZGr?default-tab=html%2Cresult]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자주 쓰는 컴포넌트]-드롭다운]]></title>
            <link>https://velog.io/@wkawha_rowk/%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%93%9C%EB%A1%AD%EB%8B%A4%EC%9A%B4</link>
            <guid>https://velog.io/@wkawha_rowk/%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%93%9C%EB%A1%AD%EB%8B%A4%EC%9A%B4</guid>
            <pubDate>Thu, 18 Apr 2024 08:05:58 GMT</pubDate>
            <description><![CDATA[<h3 id="서론">서론</h3>
<p>여러 프로젝트를 하면서, 다양한 컴포넌트를 만들었는데 거의 대부분 부트스트랩이나 다른 라이브러리를 사용했던거 같다. 자주 사용하면서, 간단한 컴포넌트는 직접 구현해보고 싶어 하나씩 만들어보고자 한다.</p>
<h3 id="드롭다운-메뉴">드롭다운 메뉴</h3>
<p>간간히 메뉴 선택에 있어 자주 사용되는 컴포넌트이기도 하고, 금방 만들 수 있을 것 같았다.
메뉴 선택란이 있고 하단 버튼을 클릭하면 하위 메뉴들이 보이는 식으로 구성된다.
만들기 전에 다른 블로그를 둘러 보았는데, 하단 버튼 클릭 시 하위 메뉴가 보여지는 데 에니메이션을 적용한 사례가 없어서, 필자는 간단하게 적용한 버전을 만들어 보았다.</p>
<h3 id="html">html</h3>
<pre><code class="language-html">&lt;div style=&quot;margin: 100px auto; width: 200px&quot;&gt;
            &lt;div class=&quot;selector&quot;&gt;
                &lt;span class=&quot;choice&quot;&gt;Select Menu&lt;/span&gt;
                &lt;svg
                    class=&quot;btn&quot;
                    xmlns=&quot;http://www.w3.org/2000/svg&quot;
                    id=&quot;Outline&quot;
                    viewBox=&quot;0 0 24 24&quot;
                    width=&quot;20&quot;
                    height=&quot;20&quot;
                &gt;
                    &lt;path
                        d=&quot;M18.71,8.21a1,1,0,0,0-1.42,0l-4.58,4.58a1,1,0,0,1-1.42,0L6.71,8.21a1,1,0,0,0-1.42,0,1,1,0,0,0,0,1.41l4.59,4.59a3,3,0,0,0,4.24,0l4.59-4.59A1,1,0,0,0,18.71,8.21Z&quot;
                    /&gt;
                &lt;/svg&gt;
            &lt;/div&gt;
            &lt;div class=&quot;options&quot;&gt;
                &lt;div class=&quot;content&quot;&gt;item 1&lt;/div&gt;
                &lt;div class=&quot;content&quot;&gt;item 2&lt;/div&gt;
                &lt;div class=&quot;content&quot;&gt;item 3&lt;/div&gt;
                &lt;div class=&quot;content&quot;&gt;item 4&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;</code></pre>
<h3 id="css">css</h3>
<pre><code class="language-css">.selector {
    width: 200px;
    padding: 10px;
    display: flex;
    justify-content: space-between;
    border: 1px solid black;
    border-radius: 5px;
}

.options {
    width: 220px;
    display: flex;
    flex-direction: column;
    margin-top: 5px;

    border: 1px solid black;
    border-radius: 5px;

    visibility: hidden;
    opacity: 0;
    transform: translateY(-5%);
    transition: opacity 0.1s ease, transform 0.1s ease, linear 0.1s;
}

.content {
    display: flex;
    height: 45px;
    align-items: center;
    justify-content: center;
}
.content:hover {
    background-color: lightgray;
}

.options.expended {
    width: 220px;
    visibility: visible;
    opacity: 1;
    transform: translateY(0);
    transition-delay: 0s;
}
</code></pre>
<h3 id="js">js</h3>
<pre><code class="language-js">window.onload = () =&gt; {
    const button = document.querySelector(&quot;.btn&quot;);
    const options = document.querySelector(&quot;.options&quot;);
    const choiceItem = document.querySelector(&quot;.choice&quot;);

    button.addEventListener(&quot;click&quot;, () =&gt; {
        console.log(&quot;click&quot;);
        options.classList.toggle(&quot;expended&quot;);
        showMenu();
    });

    const showMenu = () =&gt; {
        const items = document.querySelectorAll(&quot;.content&quot;);
        items.forEach((item) =&gt; {
            item.addEventListener(&quot;click&quot;, () =&gt; itemClick(item));
        });
    };

    const itemClick = (item) =&gt; {
        choiceItem.textContent = item.textContent;
        options.classList.remove(&quot;expended&quot;);
    };
};
</code></pre>
<h3 id="결과">결과</h3>
<p>!codepen[oobodipv-the-sasster/embed/ExJdbgM?default-tab=html%2Cresult]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 c++] 1389번 케빈 베이컨의 6단계 법칙]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-c-1389%EB%B2%88-%EC%BC%80%EB%B9%88-%EB%B2%A0%EC%9D%B4%EC%BB%A8%EC%9D%98-6%EB%8B%A8%EA%B3%84-%EB%B2%95%EC%B9%99</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-c-1389%EB%B2%88-%EC%BC%80%EB%B9%88-%EB%B2%A0%EC%9D%B4%EC%BB%A8%EC%9D%98-6%EB%8B%A8%EA%B3%84-%EB%B2%95%EC%B9%99</guid>
            <pubDate>Mon, 15 Apr 2024 09:06:42 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>케빈 베이컨의 6단계 법칙에 의하면 지구에 있는 모든 사람들은 최대 6단계 이내에서 서로 아는 사람으로 연결될 수 있다. 케빈 베이컨 게임은 임의의 두 사람이 최소 몇 단계 만에 이어질 수 있는지 계산하는 게임이다.</p>
<p>예를 들면, 전혀 상관없을 것 같은 인하대학교의 이강호와 서강대학교의 민세희는 몇 단계만에 이어질 수 있을까?</p>
<p>천민호는 이강호와 같은 학교에 다니는 사이이다. 천민호와 최백준은 Baekjoon Online Judge를 통해 알게 되었다. 최백준과 김선영은 같이 Startlink를 창업했다. 김선영과 김도현은 같은 학교 동아리 소속이다. 김도현과 민세희는 같은 학교에 다니는 사이로 서로 알고 있다. 즉, 이강호-천민호-최백준-김선영-김도현-민세희 와 같이 5단계만 거치면 된다.</p>
<p>케빈 베이컨은 미국 헐리우드 영화배우들 끼리 케빈 베이컨 게임을 했을때 나오는 단계의 총 합이 가장 적은 사람이라고 한다.</p>
<p>오늘은 Baekjoon Online Judge의 유저 중에서 케빈 베이컨의 수가 가장 작은 사람을 찾으려고 한다. 케빈 베이컨 수는 모든 사람과 케빈 베이컨 게임을 했을 때, 나오는 단계의 합이다.</p>
<p>예를 들어, BOJ의 유저가 5명이고, 1과 3, 1과 4, 2와 3, 3과 4, 4와 5가 친구인 경우를 생각해보자.</p>
<p>1은 2까지 3을 통해 2단계 만에, 3까지 1단계, 4까지 1단계, 5까지 4를 통해서 2단계 만에 알 수 있다. 따라서, 케빈 베이컨의 수는 2+1+1+2 = 6이다.</p>
<p>2는 1까지 3을 통해서 2단계 만에, 3까지 1단계 만에, 4까지 3을 통해서 2단계 만에, 5까지 3과 4를 통해서 3단계 만에 알 수 있다. 따라서, 케빈 베이컨의 수는 2+1+2+3 = 8이다.</p>
<p>3은 1까지 1단계, 2까지 1단계, 4까지 1단계, 5까지 4를 통해 2단계 만에 알 수 있다. 따라서, 케빈 베이컨의 수는 1+1+1+2 = 5이다.</p>
<p>4는 1까지 1단계, 2까지 3을 통해 2단계, 3까지 1단계, 5까지 1단계 만에 알 수 있다. 4의 케빈 베이컨의 수는 1+2+1+1 = 5가 된다.</p>
<p>마지막으로 5는 1까지 4를 통해 2단계, 2까지 4와 3을 통해 3단계, 3까지 4를 통해 2단계, 4까지 1단계 만에 알 수 있다. 5의 케빈 베이컨의 수는 2+3+2+1 = 8이다.</p>
<p>5명의 유저 중에서 케빈 베이컨의 수가 가장 작은 사람은 3과 4이다.</p>
<p>BOJ 유저의 수와 친구 관계가 입력으로 주어졌을 때, 케빈 베이컨의 수가 가장 작은 사람을 구하는 프로그램을 작성하시오.
<a href="https://www.acmicpc.net/problem/1389">https://www.acmicpc.net/problem/1389</a></p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 유저의 수 N (2 ≤ N ≤ 100)과 친구 관계의 수 M (1 ≤ M ≤ 5,000)이 주어진다. 둘째 줄부터 M개의 줄에는 친구 관계가 주어진다. 친구 관계는 A와 B로 이루어져 있으며, A와 B가 친구라는 뜻이다. A와 B가 친구이면, B와 A도 친구이며, A와 B가 같은 경우는 없다. 친구 관계는 중복되어 들어올 수도 있으며, 친구가 한 명도 없는 사람은 없다. 또, 모든 사람은 친구 관계로 연결되어져 있다. 사람의 번호는 1부터 N까지이며, 두 사람이 같은 번호를 갖는 경우는 없다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 BOJ의 유저 중에서 케빈 베이컨의 수가 가장 작은 사람을 출력한다. 그런 사람이 여러 명일 경우에는 번호가 가장 작은 사람을 출력한다.</p>
<h3 id="풀이">풀이</h3>
<p>각 유저 별로 본인을 제외한 다른 사람까지의 케빈 베이컨 수를 구해서 전부 더한다. 그리고 그 합 중 가장 작은 값을 가진 유저를 출력한다.
각 유저별로 다른 사람까지의 거리를 계산하는 방식은 bfs를 사용하였고, 본인을 제외한 나머지 각각 단계를 구해야하므로 2중 for문을 이용하였다.
추후 폴로이드-워셜 알고리즘을 사용하여 다시 풀어봐야겠다.</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;cstring&gt;
#include &lt;iostream&gt;
#include &lt;queue&gt;
#include &lt;vector&gt;
using namespace std;

vector&lt;int&gt; v[101];     // 친구 관계를 담을 백터
int visited[101];       // 방문한 유저 체크 배열
int result = INT32_MAX; // 최소 케빈 베이컨 수
int ans = INT32_MAX;    // 최소 케빈 베이컨 수를 가진 유저

// 특정노드에서부터 목표노드까지 bfs 탐색
int bfs(int node, int goal) {
    int count = 0;
    memset(visited, 0, sizeof(visited));
    visited[node] = 1;
    queue&lt;int&gt; q;
    q.push(node);

    while (!q.empty()) {
        int f = q.front();
        q.pop();

        for (int i = 0; i &lt; v[f].size(); i++) {
            if (visited[v[f][i]] == 0) {
                q.push(v[f][i]);
                visited[v[f][i]] =
                    visited[f] + 1; // 이전 노드 + 1 값으로 현재 단계 값 저장
                if (v[f][i] == goal) // 목표지점이라면 현재 단계 리턴
                    return visited[v[f][i]];
                count = visited[v[f][i]]; // 목표지점이 아니라면 count에 저장
            }
        }
    }
    return count;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    int n, m;
    cin &gt;&gt; n &gt;&gt; m;

    for (int i = 0; i &lt; m; i++) {
        int x, y;
        cin &gt;&gt; x &gt;&gt; y;
        v[x].push_back(y);
        v[y].push_back(x);
    }

    for (int i = 1; i &lt;= n; i++) {
        int tmp = 0;
        for (int j = 1; j &lt;= n; j++) {
            if (i != j) {
                tmp += bfs(i, j) - 1; // 현재 노드 방문 체크를 1로 시작하기 때문에 -1로 단계 맞추기
            }
        }
        if (tmp &lt;= result) {
            if (tmp == result) // 현재 케빈 베이컨수가 지금까지 가장 작은 케빈 베이컨수와 같다면
                ans = min(i, ans); // 현재 노드값과, 이전 노드값 비교 후 작은 값으로 갱신
            else // 작은경우 ans값을 현재 노드로 갱신
                ans = i;
            result = tmp; // 가장 작은 케빈 베이컨 수 갱신
        }
    }
    cout &lt;&lt; ans &lt;&lt; &#39;\n&#39;;

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 c++] 5582번 공통 부분 문자열]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-c-5582%EB%B2%88-%EA%B3%B5%ED%86%B5-%EB%B6%80%EB%B6%84-%EB%AC%B8%EC%9E%90%EC%97%B4</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-c-5582%EB%B2%88-%EA%B3%B5%ED%86%B5-%EB%B6%80%EB%B6%84-%EB%AC%B8%EC%9E%90%EC%97%B4</guid>
            <pubDate>Thu, 11 Apr 2024 12:34:26 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>두 문자열이 주어졌을 때, 두 문자열에 모두 포함된 가장 긴 공통 부분 문자열을 찾는 프로그램을 작성하시오.</p>
<p>어떤 문자열 s의 부분 문자열 t란, s에 t가 연속으로 나타나는 것을 말한다. 예를 들어, 문자열 ABRACADABRA의 부분 문자열은 ABRA, RAC, D, ACADABRA, ABRACADABRA, 빈 문자열 등이다. 하지만, ABRC, RAA, BA, K는 부분 문자열이 아니다.</p>
<p>두 문자열 ABRACADABRA와 ECADADABRBCRDARA의 공통 부분 문자열은 CA, CADA, ADABR, 빈 문자열 등이 있다. 이 중에서 가장 긴 공통 부분 문자열은 ADABR이며, 길이는 5이다. 또, 두 문자열이 UPWJCIRUCAXIIRGL와 SBQNYBSBZDFNEV인 경우에는 가장 긴 공통 부분 문자열은 빈 문자열이다.
<a href="https://www.acmicpc.net/problem/5582">https://www.acmicpc.net/problem/5582</a></p>
<h3 id="입력">입력</h3>
<p>첫째 줄과 둘째 줄에 문자열이 주어진다. 문자열은 대문자로 구성되어 있으며, 길이는 1 이상 4000 이하이다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 두 문자열에 모두 포함 된 부분 문자열 중 가장 긴 것의 길이를 출력한다.</p>
<h3 id="풀이">풀이</h3>
<p>이 문제에서는, &quot;연속된 공통 부분 문자열&quot; 중에서 가장 큰 길이를 출력하는 것이다. 즉 붙어있어야만 인정됨을 유의하고 풀었다.
2차원 dp배열을 초기화 하고, 2중 for문을 통해 각 문자열을 하나씩 비교하며 같으면 dp배열에 값을 업데이트 하는 방식으로 접근하면 된다. 업데이트 하는 조건은 다음과 같다.</p>
<pre><code class="language-cpp">if(a[i] == b[j])
    if(i == 0 || j ==0)
        dp[i][j] = 1
    else dp[i][j] = dp[i-1][j-1] + 1</code></pre>
<p>두 문자가 같을 때, i-1,j-1의 값에 1을 더하는데, 만약 i또는 j가 0일때 두 문자가 같은 경우는 0이 아니라 1로 지정해줘야 한다. 범위를 벗어나는 경우에 대한 처리.</p>
<p>위 조건을 알아봄과 동시에 c++ 음수 인덱스에 대해서 알아보았는데, vector의 경우 out of bound에 대한 체크를 해주지만, array의 경우 체크를 하지 않는다. 따라서 범위를 벗어나게 된다면 undefined behaviour로 이어진다.
[참고] <a href="https://stackoverflow.com/questions/47170740/c-negative-array-index">https://stackoverflow.com/questions/47170740/c-negative-array-index</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 c++] 11055 가장 큰 증가하는 부분 수열]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-c-11055-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-c-11055-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EC%A6%9D%EA%B0%80%ED%95%98%EB%8A%94-%EB%B6%80%EB%B6%84-%EC%88%98%EC%97%B4</guid>
            <pubDate>Wed, 10 Apr 2024 15:55:18 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>수열 A가 주어졌을 때, 그 수열의 증가하는 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오.</p>
<p>예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가하는 부분 수열은 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 이고, 합은 113이다.
<a href="https://www.acmicpc.net/problem/11055">https://www.acmicpc.net/problem/11055</a></p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.</p>
<p>둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 수열 A의 합이 가장 큰 증가하는 부분 수열의 합을 출력한다.</p>
<h3 id="풀이">풀이</h3>
<p>가장 긴 증가하는 부분 수열과 비슷한 로직을 사용한다. 11053번을 풀고 풀어보는 것을 권장한다.
i) 증가하는 부분 수열의 조건
ii) 이전 수열의 전체 합 + 현재 인덱스 값 &gt; 현재 인덱스까지 나온 수열의 합</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;iostream&gt;

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    int n;
    int arr[1001];
    int dp[1001];
    cin &gt;&gt; n;

    for (int i = 0; i &lt; n; i++) {
        cin &gt;&gt; arr[i];
    }

    int ans = 0;
    for (int i = 0; i &lt; n; i++) {
        dp[i] = arr[i];
        for (int j = 0; j &lt;= i; j++) {
            if (arr[i] &gt; arr[j] &amp;&amp; dp[i] &lt; dp[j] + arr[i]) {
                dp[i] = dp[j] + arr[i];
            }
            ans = max(ans, dp[i]);
        }
    }

    cout &lt;&lt; ans &lt;&lt; &#39;\n&#39;;

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 c++] 1932번 정수 삼각형]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-c-1932%EB%B2%88-%EC%A0%95%EC%88%98-%EC%82%BC%EA%B0%81%ED%98%95</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-c-1932%EB%B2%88-%EC%A0%95%EC%88%98-%EC%82%BC%EA%B0%81%ED%98%95</guid>
            <pubDate>Sun, 07 Apr 2024 17:10:08 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>삼각형의 맨 위층부터 시작해서 아래에 있는 수 중 하나를 선택하여 아래층으로 내려올 때, 이제까지 선택된 수의 합이 최대가 되는 경로를 구하는 프로그램을 작성하라. 아래층에 있는 수는 현재 층에서 선택된 수의 대각선 왼쪽 또는 대각선 오른쪽에 있는 것 중에서만 선택할 수 있다.</p>
<p>삼각형의 크기는 1 이상 500 이하이다. 삼각형을 이루고 있는 각 수는 모두 정수이며, 범위는 0 이상 9999 이하이다.
<a href="https://www.acmicpc.net/problem/1932">https://www.acmicpc.net/problem/1932</a></p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 삼각형의 크기 n(1 ≤ n ≤ 500)이 주어지고, 둘째 줄부터 n+1번째 줄까지 정수 삼각형이 주어진다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 합이 최대가 되는 경로에 있는 수의 합을 출력한다.</p>
<h3 id="풀이">풀이</h3>
<p>간단한 다이나믹 프로그램 문제였다. 각 층의 맨 왼쪽과 오른쪽은 하나의 케이스만 나오기에 별도로 처리하고, 나머지 중간에 끼어있는 부분들에 대해서 처리해주면 된다.
간단하게 7 / 3,8 / 8,1,0 이라 가정해본다면</p>
<ul>
<li>dp[0][0] = 7</li>
<li>dp[1][0] = 7+3 = dp[0][0] + triangle[1][0]</li>
<li>dp[1][1] = 7+8 = dp[0][0] + triangle[1][1]</li>
<li>dp[2][0] = 10+8 = dp[1][0] + triangle[2][0]</li>
<li>dp[2][1] = max(10+1,15+1) = max(dp[1][0], dp[1][1]) + triangle[2][1]</li>
<li>dp[2][2] = 15+0 = dp[1][1] + triangle[2][2]</li>
</ul>
<p>위 식에서 보이는 규칙을 정리해보자면
맨 왼쪽일 경우, 다시말해 각 층의 0번째 인덱스인 경우에는</p>
<ul>
<li><strong>dp[i][0] = dp[i-1][0] + triangle[i][0]</strong></li>
</ul>
<p>맨 오른쪽일 경우, 다시말해 i번째 층의 i번째 인덱스인 경우에는</p>
<ul>
<li><strong>dp[i][i] = dp[i-1][j-1] + triangle[i][i]</strong></li>
</ul>
<p>마지막으로 그 사이의 경우에는</p>
<ul>
<li><strong>dp[i][j] = max(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]</strong></li>
</ul>
<p>가 성립한다!</p>
<p>위 점화식을 토대로 구현하면 된다</p>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;iostream&gt;
using namespace std;

int main()
{
    int n;
    int arr[501][501];
    int dp[501][501];
    cin &gt;&gt; n;

    for (int i = 0; i &lt; n; i++)
    {
        for (int j = 0; j &lt;= i; j++)
        {
            cin &gt;&gt; arr[i][j];
        }
    }

    int ans = arr[0][0];
    dp[0][0] = arr[0][0];
    for (int i = 1; i &lt; n; i++)
    {
        for (int j = 0; j &lt;= i; j++)
        {
            if (j == 0)
                dp[i][j] = dp[i - 1][0] + arr[i][0];
            else if (j == i)
                dp[i][j] = dp[i - 1][j - 1] + arr[i][j];
            else
                dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + arr[i][j];

            ans = max(ans, dp[i][j]);
        }
    }

    cout &lt;&lt; ans &lt;&lt; &#39;\n&#39;;

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[볼 때마다 헷갈리는 Closure 정복하기]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B3%BC-%EB%95%8C%EB%A7%88%EB%8B%A4-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-Closure-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B3%BC-%EB%95%8C%EB%A7%88%EB%8B%A4-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-Closure-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 09 Mar 2024 07:00:13 GMT</pubDate>
            <description><![CDATA[<h2 id="closure란">Closure란?</h2>
<blockquote>
<p><strong>클로저</strong>는 주변 상태(어휘적 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합입니다. 즉, 클로저는 내부 함수에서 외부 함수의 범위에 대한 접근을 제공합니다. JavaScript에서 클로저는 <strong>함수 생성 시</strong> 함수가 생성될 때마다 생성됩니다.</p>
</blockquote>
<p>MDN 공식문서에 나온 정의는 위와 같다.</p>
<ul>
<li>주변 상태(어휘적 환경)에 대한 참조</li>
<li>함께 묶인 함수</li>
</ul>
<p>의 조합이라고 하는데 말이 너무 어려워서 예시 코드를 보며 이해해 보았다.</p>
<h2 id="예제-1">예제 1</h2>
<pre><code class="language-jsx">function init() {
  var name = &quot;Mozilla&quot;; // name은 init에 의해 생성된 지역 변수이다.
  function displayName() {
    // displayName() 은 내부 함수이며, 클로저다.
    console.log(name); // 부모 함수에서 선언된 변수를 사용한다.
  }
  displayName();
}
init();</code></pre>
<p>일단 위 정의에서 나온 ‘<strong>클로저는 내부 함수에서 외부 함수의 범위에 대한 접근을 제공’</strong>하는 부분을 통해 <code>displayName</code> 이 클로저임을 알 수 있다.</p>
<p><strong>why?</strong> </p>
<p>⇒ <code>init</code> 함수의 내부 함수이며, <code>displayName</code> 함수는 본인 외부에 있는 변수 <code>name</code> 을 참조하고 있기 때문이다.</p>
<p>그런데 저 예제의 흐름을 보면</p>
<ol>
<li><code>init</code> 함수를 실행</li>
<li>내부에서 <code>displayName</code> 함수를 실행</li>
<li><code>console.log</code> 로 <code>name</code> 을 찍기 때문에 <strong>사실 클로저라고 보기 어렵다.</strong></li>
</ol>
<h2 id="예제2">예제2</h2>
<p>다음 코드를 보자.</p>
<pre><code class="language-jsx">function init() {
  var name = &quot;Mozilla&quot;; // name은 init에 의해 생성된 지역 변수이다.
  function displayName() {
    // displayName() 은 내부 함수이며, 클로저다.
    console.log(name); // 부모 함수에서 선언된 변수를 사용한다.
  }
  return displayName();
}
const test = init();
test();</code></pre>
<p>자, 위 예제에 대한 흐름을 보자</p>
<ol>
<li><code>test</code> 에 <code>init</code> 함수 호출하며 할당</li>
<li><code>init</code> 은 <code>displayName</code> 함수를 반환하면서 종료됨</li>
<li><code>test</code> 는 <code>displayName</code> 함수를 가지게 됨</li>
<li><code>test</code> 를 실행하여 <code>displayName</code> 함수를 실행</li>
<li>이 때, <code>init</code> 함수는 종료되면서 지역변수인 <code>name</code> 은 <strong>존재하지 않는다</strong></li>
<li>하지만 closure인 <code>displayName</code> 는 생성될 당시의 <code>name</code> 을 참조하게 된다.</li>
<li><strong>고로 콘솔창에는 “Mozilla” 값이 나오게 된다!!</strong></li>
</ol>
<p>즉 <code>displayName</code> 이 생성되는 시점(9번째 라인)에서 <code>init</code> 의 <code>name</code> 을 참조한다. <strong>이것이 함수 생성 시 주변 환경(어휘적 환경)을 참조하는 것이다.</strong></p>
<h2 id="정리">정리</h2>
<aside>
💡 Closure는 함수가 선언될 때의 환경을 기억하여 이 함수가 다른 스코프에서 호출될 때에도 기억했던 환경(변수 등)에 접근할 수 있게 하는 JavaScript의 특징이다

</aside>]]></description>
        </item>
        <item>
            <title><![CDATA[바닐라 스크립트: 함수형 컴포넌트]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%94%EB%8B%90%EB%9D%BC-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%95%A8%EC%88%98%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%94%EB%8B%90%EB%9D%BC-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%95%A8%EC%88%98%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Sat, 09 Mar 2024 06:57:35 GMT</pubDate>
            <description><![CDATA[<h2 id="서론">서론</h2>
<p>바닐라 스크립트를 이용한 ToDo List 프로젝트에서 <strong>컴포넌트 단위로 개발하고자 셋팅하는 과정을 정리함</strong></p>
<p>함수 기반 컴포넌트, 모듈 패턴, class를 이용한 웹 컴포넌트 등 여러 방식이 있지만 그중에서 정리할 내용은</p>
<h3 id="함수-기반-컴포넌트">함수 기반 컴포넌트</h3>
<ul>
<li>JavaScript 함수를 활용하여 간단한 컴포넌트를 만들 수 있음</li>
<li>함수 내부에서 필요한 HTML을 생성하고, 필요한 이벤트 핸들러를 등록할 수 있음</li>
<li>예시</li>
</ul>
<pre><code class="language-jsx">//component.js
function createComponent(text) {
  const element = document.createElement(&#39;div&#39;);
  element.innerHTML = `
    &lt;p&gt;${text}&lt;/p&gt;
    &lt;button id=&quot;incrementBtn&quot;&gt;Increment&lt;/button&gt;
  `;

  const incrementBtn = element.querySelector(&#39;#incrementBtn&#39;);
  incrementBtn.addEventListener(&#39;click&#39;, () =&gt; {
    // 처리 로직
    console.log(&#39;Button Clicked!&#39;);
  });

  return element;
}
const myComponent = createComponent(&#39;Hello, Function Component!&#39;);
document.getElementById(&#39;app&#39;).appendChild(myComponent);</code></pre>
<h3 id="프로젝트-적용-1">프로젝트 적용 (1)</h3>
<ul>
<li><p>root 디렉토리에 index.html, component.js, main.js를 생성 후, 아래 코드를 구현</p>
<ul>
<li><p>index.html</p>
<pre><code class="language-jsx">  &lt;!DOCTYPE html&gt;
  &lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Component-based Development&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;

    &lt;div id=&quot;app&quot;&gt;&lt;/div&gt;

    &lt;script src=&quot;component.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;main.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
  &lt;/html&gt;</code></pre>
</li>
<li><p>component.js</p>
<pre><code class="language-jsx">  // component.js
  function createComponent(text) {
    const element = document.createElement(&#39;div&#39;);
    element.innerHTML = `
      &lt;p&gt;${text}&lt;/p&gt;
      &lt;button id=&quot;incrementBtn&quot;&gt;Increment&lt;/button&gt;
    `;

    const incrementBtn = element.querySelector(&#39;#incrementBtn&#39;);
    incrementBtn.addEventListener(&#39;click&#39;, () =&gt; {
      // 처리 로직
      console.log(&#39;Button Clicked!&#39;);
    });

    return element;
  }</code></pre>
</li>
<li><p>main.js</p>
<pre><code class="language-jsx">  // main.js
  document.addEventListener(&#39;DOMContentLoaded&#39;, () =&gt; {
    const appContainer = document.getElementById(&#39;app&#39;);

    // 함수를 사용하여 컴포넌트 생성
    const myComponent = createComponent(&#39;Hello, Function Component!&#39;);

    // 생성된 컴포넌트를 DOM에 추가
    appContainer.appendChild(myComponent);
  });</code></pre>
</li>
</ul>
</li>
<li><p>결과</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/c904bd76-2552-4433-b570-e044ff40f41c/image.png" alt=""></p>
<h3 id="프로젝트-적용-2">프로젝트 적용 (2)</h3>
<ul>
<li>위 예제를 응용하여 components 폴더를 만들어 이 폴더 안에 여러 함수형 컴포넌트 파일을 관리</li>
<li>디렉토리 구조</li>
</ul>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/72c25a76-f978-4ceb-943b-471981dccb3b/image.png" alt=""></p>
<ul>
<li><p>코드는 다음과 같이 설정</p>
<ul>
<li><p>index.html</p>
<pre><code class="language-jsx">  &lt;!DOCTYPE html&gt;
  &lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Component-based Development&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;

    &lt;div id=&quot;app&quot;&gt;&lt;/div&gt;

    &lt;script src=&quot;main.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
  &lt;/html&gt;</code></pre>
</li>
<li><p>component.js</p>
<pre><code class="language-jsx">  // component.js
  function createComponent(text) {
    const element = document.createElement(&#39;div&#39;);
    element.innerHTML = `
      &lt;p&gt;${text}&lt;/p&gt;
      &lt;button id=&quot;incrementBtn&quot;&gt;Increment&lt;/button&gt;
    `;

    const incrementBtn = element.querySelector(&#39;#incrementBtn&#39;);
    incrementBtn.addEventListener(&#39;click&#39;, () =&gt; {
      // 처리 로직
      console.log(&#39;Button Clicked!&#39;);
    });

    return element;
  }

  // 모듈로 내보내기
  export { createComponent };</code></pre>
</li>
<li><p>main.js</p>
<pre><code class="language-jsx">  // main.js
  import { createComponent } from &#39;./components/component.js&#39;;

  document.addEventListener(&#39;DOMContentLoaded&#39;, () =&gt; {
    const appContainer = document.getElementById(&#39;app&#39;);

    // 함수를 사용하여 컴포넌트 생성
    const myComponent = createComponent(&#39;Hello, Function Component!&#39;);

    // 생성된 컴포넌트를 DOM에 추가
    appContainer.appendChild(myComponent);
  });</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="프로젝트-적용-오류-1">프로젝트 적용 오류 (1)</h3>
<ul>
<li><strong>index.html 파일을 열면, <code>Uncaught SyntaxError: Cannot use import statement outside a module</code> 라는 오류를 콘솔창에서 맞이할 수 있다.</strong></li>
<li>오류 발생 이유: 일반적으로 모듈을 사용하려고 할 때 발생하는 오류</li>
<li>해결 방안<ol>
<li>모듈 시스템은 <strong><code>&lt;script type=&quot;module&quot;&gt;</code></strong>을 사용하여 스크립트를 정의하거나</li>
<li>서버에서 모듈 지원을 활성화해야 함</li>
</ol>
</li>
<li>적용한 해결책<ul>
<li>index.html 파일의 <code>&lt;script&gt; 태그</code>에 <code>type= module</code> 을 추가해주면 된다.</li>
</ul>
</li>
</ul>
<h3 id="프로젝트-적용-오류-2">프로젝트 적용 오류 (2)</h3>
<ul>
<li>하지만 또 다른 오류를 마주하게 된다..</li>
</ul>
<aside>
💡 Access to script at 'file:///Users/yookjongho/Desktop/test/main.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes:

</aside>

<ul>
<li><p>오류 발생 이유</p>
<ul>
<li>이 오류는 일반적으로 로컬 파일 시스템에서 직접 HTML 파일을 열었을 때 발생하는 문제</li>
<li>웹 브라우저의 보안 정책으로 인해 로컬 파일 시스템에서는 Cross-Origin 요청이 허용되지 않음</li>
</ul>
</li>
<li><p>해결 방안</p>
<ul>
<li><strong>로컬 서버 사용</strong></li>
</ul>
</li>
<li><p>적용한 해결책</p>
<ol>
<li><p><code>**npx http-server**</code> 를 이용하여 로컬 서버 사용</p>
<ul>
<li><p>하지만 얘도 에러 뿜음</p>
<aside>
💡 Failed to load resource: the server responded with a status of 404 (Not Found)

</aside>
</li>
</ul>
</li>
<li><p><code>**npx serve -s</code>** 를 사용 ⇒ 정상 작동</p>
</li>
</ol>
</li>
</ul>
<h3 id="이유가-뭘까">이유가 뭘까?</h3>
<p>gpt에게 물어본 결과, 둘 다 같은 로컬 서버 실행 명령어지만 <strong>몇 가지 차이가 있다고 한다.</strong></p>
<ol>
<li><strong>모듈 지원:</strong><ul>
<li><strong><code>npx http-server</code></strong>: 기본적으로는 CommonJS 모듈 시스템을 사용합니다. 이는 <strong><code>.mjs</code></strong> 확장자를 사용하지 않은 일반적인 JavaScript 파일을 지원합니다.</li>
<li><strong><code>npx serve -s</code></strong>: ES 모듈 시스템을 기본적으로 지원합니다. <strong><code>.mjs</code></strong> 확장자를 사용한 ES6 모듈을 지원합니다.</li>
</ul>
</li>
<li><strong>커스터마이징 옵션:</strong><ul>
<li><strong><code>npx http-server</code></strong>: 추가적인 옵션을 제공하며, <strong><code>http-server</code></strong>의 설정 파일을 사용하여 서버 동작을 커스터마이징할 수 있습니다.</li>
<li><strong><code>npx serve -s</code></strong>: 간단하게 사용할 수 있는 몇 가지 기본 옵션만을 제공하며, <strong><code>serve</code></strong>의 설정 파일을 사용하여 커스터마이징할 수 있습니다.</li>
</ul>
</li>
<li><strong>파일 및 디렉토리 표시:</strong><ul>
<li><strong><code>npx http-server</code></strong>: 디렉토리의 목록을 표시할 때 디렉토리 내의 <strong><code>index.html</code></strong> 파일을 찾아서 보여줍니다.</li>
<li><strong><code>npx serve -s</code></strong>: 디렉토리의 목록을 표시하지 않고, 디렉토리에 <strong><code>index.html</code></strong> 파일이 없는 경우 404 오류를 반환합니다.</li>
</ul>
</li>
<li><strong>ESM(EcmaScript Modules) 지원:</strong><ul>
<li><strong><code>npx http-server</code></strong>: 기본적으로 ESM을 지원하지 않습니다.</li>
<li><strong><code>npx serve -s</code></strong>: ESM을 지원하며, <strong><code>.mjs</code></strong> 확장자를 사용하여 ES6 모듈을 작성할 수 있습니다.</li>
</ul>
</li>
</ol>
<h3 id="4번에서-그-이유를-찾을-수-있었다"><strong>4번에서 그 이유를 찾을 수 있었다..!</strong></h3>
<p>프로젝트 적용 (2)에서 script type을 module로 설정하였다. </p>
<p>이는 즉, 웹페이지를 ESM(EcmaScript Module)로 처리하겠다는 것이고, <code>**npx http-server</code> 는 ESM을 지원하지 않아서 404 에러를 뿜었던 것이였다.**</p>
<p>고로 ESM을 지원해주는 <code>**npx serve -s**</code>를 통해 로컬 서버를 실행하면 해결!</p>
<p>결과는 프로젝트 적용 (1)의 화면과 같다.</p>
<h3 id="후기">후기</h3>
<p>바닐라 스크립트로 함수형 컴포넌트를 만들어 관리하는 방법에 대하여 알아보았다. 페이지 별 개발보다 훨씬 간단할 것으로 예상되지만 실제로 프로젝트를 진행해봐야 알 것 같다.</p>
<h3 id="추가내용">추가내용</h3>
<p>vs code에서 extension으로 live server를 설치 후, 실행 버튼을 누르면 잘 동작한다…</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[현대 소프티어 3기 합격 후기]]></title>
            <link>https://velog.io/@wkawha_rowk/%ED%98%84%EB%8C%80-%EC%86%8C%ED%94%84%ED%8B%B0%EC%96%B4-3%EA%B8%B0-%EC%A4%80%EB%B9%84%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@wkawha_rowk/%ED%98%84%EB%8C%80-%EC%86%8C%ED%94%84%ED%8B%B0%EC%96%B4-3%EA%B8%B0-%EC%A4%80%EB%B9%84%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sat, 23 Dec 2023 03:51:06 GMT</pubDate>
            <description><![CDATA[<h1 id="소프티어">소프티어</h1>
<p>현대에서 주관하는 부트캠프로 두달간의 교육을 거쳐 채용연계 기회를 준다.
<a href="https://www.softeerbootcamp.com/">https://www.softeerbootcamp.com/</a></p>
<h1 id="일정">일정</h1>
<p>지원기간: 23년 11월 1일 ~ 23년 11월 13일
1차평가: 23년 11월 20일
1차발표: 23년 11월 24일
2차평가: 23년 12월 4일
최종발표: 23년 12월 15일</p>
<h2 id="지원서-접수">지원서 접수</h2>
<p>크게 딱히 준비할게 없었다. 졸업예정증명서 정도? 자소서도 쓰지 않았기에 다른 부트캠프(싸피, 우테코) 보다 편했다.</p>
<h2 id="1차-평가코딩테스트-후기">1차 평가(코딩테스트) 후기</h2>
<p>1차 평가는 코딩 테스트로 총 7문제가 나왔다. 준비는 백준에서 실버1~골드4 위주로 풀면서 준비했다. 학교에서 코테 준비하는 수업이 있어서 이와 병행하였다. 
필자는 두 문제를 풀었는데, 풀고 나니 히든 케이스에 걸리겠다 싶어 1솔이라 생각하고 있었는데 웬걸 1차 코테에 합격했다. 개인적인 소감으론 단기간에 준비한다고 통과하기 쉽진 않은 수준이였던거 같다. <strong>쉬운 문제여도, 시간복잡도나 효율성 측면을 고려해야 히든 케이스도 문제없이 통과할 것 같았다.</strong> 평소에 꾸준히 문제를 풀어 감을 잃지 않도록 해야겠다고 다짐했다.</p>
<h2 id="2차-평가cs-시험-후기">2차 평가(cs 시험) 후기</h2>
<p>개인적으로 준비하기 너무 힘들었다. 컴퓨터 구조, 컴퓨터 네트워크, 운영체제, 데이터베이스, 자료구조, 소프트웨어아키텍처 등 컴퓨터 전공의 핵심과목들을 약 2주도 안되는 시간에 전부 복습해야했다. 2기 후기에서는 &#39;면접을 위한 cs 전공지식 노트&#39;로 준비하셨다는 글을 보았는데, 학교 도서관에는 이미 대여완료로 없었다. 그래서 비슷한 다른 책을 찾아 공부하였다.
<img src="https://velog.velcdn.com/images/wkawha_rowk/post/9bc032ae-dc30-4302-8ee5-4b757a513b26/image.png" alt="">
장점은 각 파트별로 예상 문제를 넣어주었다. 공부한 지식을 확인하기 용이했다.
조금 아쉬운 점이라면, 컴퓨터 구조 파트는 운영체제에 살짝 녹아들어있는 느낌이여서 따로 컴퓨터 구조를 공부해야 했다. 위 책에서 부족한 부분은 아래 사이트에서 추가적으로 공부하였다. 공부 이후에는 <strong>정보처리기사 문제</strong> 풀고 오답 정리하는 시간을 가졌다.
<a href="https://gyoogle.dev/blog/">https://gyoogle.dev/blog/</a></p>
<p>2주 조금 안되는 시간동안 눈빠지게 보았는데 덕분에 핵심전공을 다시 복습할 수 있는 시간이 되었다. 하지만 문제는 내 예상보다 어려웠다.. <strong>생각보다 지엽적</strong>이므로 엄청 세세하게 공부해야 풀 수 있을 정도 였던거 같다.</p>
<p>자신없게 시험을 마무리 하였지만 정말 운좋게도 합격하였다. 시험 준비동안 여러 블로그를 돌아다니며 후기를 찾았기에 혹시나 다음 기수에 도전하실 분들을 위해 후기를 남겨놓을 수 있어 다행이다.</p>
<p>필자가 1차 2차 시험을 몇개 풀었는지 솔직히 잘 모르겠어서 합격 컷을 후기에 남겨 놓을 수 없는게 아쉬웠는데, 오픈카카오 톡방에서 투표 결과를 보니 1차는 2<del>3솔, 2차는 6</del>7문제 정도 인것 같다. 하지만 이것도 회사, 직무별로 다를 수 도 있으니 참고 정도로만 두자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[트리]]></title>
            <link>https://velog.io/@wkawha_rowk/%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@wkawha_rowk/%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Sat, 02 Dec 2023 06:19:28 GMT</pubDate>
            <description><![CDATA[<h2 id="트리">트리</h2>
<blockquote>
<p>그래프의 한 종류로 사이클이 없는 계층적 관계를 표현할 수 있는 비선형 자료구조 </p>
</blockquote>
<h2 id="이진-트리">이진 트리</h2>
<ul>
<li>정의</li>
</ul>
<blockquote>
<p>자식 노드가 최대 2개인 트리</p>
</blockquote>
<ul>
<li>종류</li>
</ul>
<ol>
<li><strong>완전 이진 트리</strong><ul>
<li>트리의 마지막 레벨을 제외한 모든 레벨에 노드가 채워져 있으며 마지막 레벨은 왼쪽에서부터 오른쪽으로 노드가 채워져 있는 이진 트리</li>
</ul>
</li>
<li><strong>포화 이진 트리</strong><ul>
<li>트리 마지막 레벨까지 노드가 모두 채워져 있는 이진 트리</li>
</ul>
</li>
<li><strong>이진 탐색 트리</strong><ul>
<li>왼쪽 서브 트리는 해당 노드의 값보다 작은 값을 가진 노드로 구성되고, 오른쪽 서브 트리는 해당 노드의 값보다 큰 값을 가진 노드로 구성된 트리</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/766ac2e8-e2b3-4b1e-a61a-8cf1225f79d7/image.png" alt=""></p>
<h2 id="이진-트리-순회">이진 트리 순회</h2>
<ol>
<li><p><strong>전위 순회(Preorder Traversal)</strong></p>
<ul>
<li><p>root → left → right</p>
<p> <img src="https://velog.velcdn.com/images/wkawha_rowk/post/aea4e705-9489-4203-bfb4-bec90bfd8d54/image.png" alt=""></p>
</li>
<li><p>1 2 4 8 9 5 10 11 3 6 12 13 7 14 15</p>
</li>
</ul>
</li>
<li><p><strong>중위 순회(Inorder Traversal)</strong></p>
<ul>
<li><p>left → root → right</p>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/fc4cf0ca-b7fd-450c-912b-89d7a98a7b4f/image.png" alt=""></p>
</li>
</ul>
</li>
</ol>
<ol start="3">
<li><p><strong>후위 순회(Postorder Traversal)</strong></p>
<ul>
<li><p>left → right → root</p>
<p><img src="https://velog.velcdn.com/images/wkawha_rowk/post/5c1fe7c3-5db7-4aad-9410-8fc51a3a707d/image.png" alt=""></p>
</li>
</ul>
</li>
</ol>
<h2 id="우선순위-큐">우선순위 큐</h2>
<blockquote>
<p>우선순위 큐는 우선순위가 높은 데이터가 먼저 나오는 자료구조</p>
</blockquote>
<ul>
<li>구현 방식</li>
</ul>
<ol>
<li>배열</li>
<li>연결 리스트</li>
<li>완전 이진 트리 (힙)</li>
</ol>
<ul>
<li><p>구현 방식에 따른 시간 복잡도</p>
<table>
<thead>
<tr>
<th>구현 방법</th>
<th>삽입</th>
<th>삭제</th>
</tr>
</thead>
<tbody><tr>
<td>배열(sorted)</td>
<td>O(n)</td>
<td>O(1)</td>
</tr>
<tr>
<td>연결 리스트(sorted linked list)</td>
<td>O(n)</td>
<td>O(1)</td>
</tr>
<tr>
<td>힙(heap)</td>
<td>O(logN)</td>
<td>O(logN)</td>
</tr>
</tbody></table>
</li>
</ul>
<h2 id="힙">힙</h2>
<blockquote>
<p>완전 이진 트리. 최댓값 또는 최솟값을 빠르게 찾을 수 있는 자료구조</p>
</blockquote>
<ul>
<li>종류<ol>
<li>최대힙(Max heap)<ul>
<li>부모 노드의 값이 자식 노드의 값보다 크거나 같은 완전 이진 트리</li>
</ul>
</li>
<li>최소힙(Min heap)<ul>
<li>부모 노드의 값이 자식 노드의 값보다 작거나 같은 완전 이진 트리</li>
</ul>
</li>
</ol>
</li>
<li>연산<ol>
<li>삽입 연산 <ul>
<li>힙의 맨 끝단에서 이루어짐. 삽입 후 부모 노드와 우선순위(최댓값 또는 최솟값)을 비교해 부모 노드보다 우선순위가 높으면 위치를 바꾸면서 루트노드까지 비교</li>
</ul>
</li>
<li>삭제 연산<ul>
<li>힙에서 우선순위가 가장 높은 노드를 삭제. 즉 루트노드 삭제. 이후 루트 노드 자리에 힙의 마지막 노드를 옮긴 후 힙을 재정렬한다</li>
</ul>
</li>
</ol>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 C++] 3190 뱀]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-3190-%EB%B1%80</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-3190-%EB%B1%80</guid>
            <pubDate>Wed, 11 Oct 2023 12:47:13 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>&#39;Dummy&#39; 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다.</p>
<p>게임은 NxN 정사각 보드위에서 진행되고, 몇몇 칸에는 사과가 놓여져 있다. 보드의 상하좌우 끝에 벽이 있다. 게임이 시작할때 뱀은 맨위 맨좌측에 위치하고 뱀의 길이는 1 이다. 뱀은 처음에 오른쪽을 향한다.</p>
<p>뱀은 매 초마다 이동을 하는데 다음과 같은 규칙을 따른다.</p>
<ul>
<li>먼저 뱀은 몸길이를 늘려 머리를 다음칸에 위치시킨다.</li>
<li>만약 벽이나 자기자신의 몸과 부딪히면 게임이 끝난다.</li>
<li>만약 이동한 칸에 사과가 있다면, 그 칸에 있던 사과가 없어지고 꼬리는 움직이지 않는다.</li>
<li>만약 이동한 칸에 사과가 없다면, 몸길이를 줄여서 꼬리가 위치한 칸을 비워준다. 즉, 몸길이는 변하지 않는다.</li>
<li>사과의 위치와 뱀의 이동경로가 주어질 때 이 게임이 몇 초에 끝나는지 계산하라.</li>
</ul>
<p><a href="https://www.acmicpc.net/problem/3190">https://www.acmicpc.net/problem/3190</a></p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 보드의 크기 N이 주어진다. (2 ≤ N ≤ 100) 다음 줄에 사과의 개수 K가 주어진다. (0 ≤ K ≤ 100)</p>
<p>다음 K개의 줄에는 사과의 위치가 주어지는데, 첫 번째 정수는 행, 두 번째 정수는 열 위치를 의미한다. 사과의 위치는 모두 다르며, 맨 위 맨 좌측 (1행 1열) 에는 사과가 없다.</p>
<p>다음 줄에는 뱀의 방향 변환 횟수 L 이 주어진다. (1 ≤ L ≤ 100)</p>
<p>다음 L개의 줄에는 뱀의 방향 변환 정보가 주어지는데, 정수 X와 문자 C로 이루어져 있으며. 게임 시작 시간으로부터 X초가 끝난 뒤에 왼쪽(C가 &#39;L&#39;) 또는 오른쪽(C가 &#39;D&#39;)로 90도 방향을 회전시킨다는 뜻이다. X는 10,000 이하의 양의 정수이며, 방향 전환 정보는 X가 증가하는 순으로 주어진다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 게임이 몇 초에 끝나는지 출력한다.</p>
<h3 id="풀이">풀이</h3>
<p>길이가 늘어날 때, 꼬리는 변하지 않고 <strong>몸통이 늘어난다</strong>는 점을 보고 deque를 이용하여 뱀 몸통위치를 저장 및 갱신하였다. </p>
<p>다음 좌표로 진행하여 사과를 먹었을 시, 이전 좌표를 deque의 앞에 추가해주고 만약 사과를 못먹었다면 똑같이 추가하되, deque의 마지막 좌표 값을 pop 하였다.</p>
<p>방향전환은 나머지 연산을 통해 간단하게 계산하였다.</p>
<blockquote>
<p>반시계 방향 = (진행하고자 하는 방향 값 + 3) % 4
시계 방향 = (진행하고자 하는 방향 값 + 1) % 4</p>
</blockquote>
<pre><code class="language-cpp">#include &lt;algorithm&gt;
#include &lt;deque&gt;
#include &lt;iostream&gt;
#include &lt;queue&gt;

using namespace std;

int n, k, X, l, dir = 1; // dir = 현재 진행방향
char C;
int map[101][101];
deque&lt;pair&lt;int, int&gt;&gt; v;  // 뱀 몸통 위치 저장
int dx[] = {-1, 0, 1, 0}; // 0: 상  1: 우
int dy[] = {0, 1, 0, -1}; // 2: 하  3: 좌
queue&lt;pair&lt;int, char&gt;&gt; q; //방향전환 저장

bool check(int x, int y) {
    // 벽에 부딪히는 경우 걸러내기
    if (x &gt;= 1 &amp;&amp; x &lt;= n &amp;&amp; y &gt;= 1 &amp;&amp; y &lt;= n) {
        // 자기 몸에 부딪히는 경우 걸러내기
        for (int i = 0; i &lt; v.size(); i++) {
            if (v[i].first == x &amp;&amp; v[i].second == y)
                return false;
        }
        return true;
    }
    return false;
}

int main() {
    cin &gt;&gt; n;
    cin &gt;&gt; k;

    int r, c;
    for (int i = 0; i &lt; k; i++) {
        cin &gt;&gt; r &gt;&gt; c;
        map[r][c] = 1;
    }

    cin &gt;&gt; l;
    for (int i = 0; i &lt; l; i++) {
        cin &gt;&gt; X &gt;&gt; C;
        q.push({X, C});
    }

    r = c = 1;
    int second = 0;
    while (1) {
        if (second == q.front().first) {
            char t = q.front().second;
            // 진행방향 계산. L이면 반시계, D면 시계
            dir = t == &#39;L&#39; ? (dir + 3) % 4 : (dir + 1) % 4;
            q.pop();
        }
        int nr = r + dx[dir];
        int nc = c + dy[dir];

        if (check(nr, nc)) {
            // 사과가 있는 경우
            if (map[nr][nc] == 1) {
                map[nr][nc] = 0;
                v.push_front({r, c});
            }
            // 사과가 없는 경우
            else {
                if (!v.empty()) {
                    v.push_front({r, c});
                    v.pop_back();
                }
            }
            r = nr;
            c = nc;
            second++;
        } else {
            cout &lt;&lt; second + 1 &lt;&lt; &#39;\n&#39;;
            break;
        }
    }

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 C++] 14503 로봇 청소기]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-14503-%EB%A1%9C%EB%B4%87-%EC%B2%AD%EC%86%8C%EA%B8%B0</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-14503-%EB%A1%9C%EB%B4%87-%EC%B2%AD%EC%86%8C%EA%B8%B0</guid>
            <pubDate>Sun, 08 Oct 2023 12:33:22 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>로봇 청소기와 방의 상태가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오.
로봇 청소기 작동방식</p>
<ol>
<li>현재 칸이 아직 청소되지 않은 경우, 현재 칸을 청소한다.</li>
<li>현재 칸의 주변 $4$칸 중 청소되지 않은 빈 칸이 없는 경우,</li>
</ol>
<ul>
<li>2-1. 바라보는 방향을 유지한 채로 한 칸 후진할 수 있다면 한 칸 후진하고 1번으로 돌아간다.</li>
<li>2-2. 바라보는 방향의 뒤쪽 칸이 벽이라 후진할 수 없다면 작동을 멈춘다.</li>
</ul>
<ol start="3">
<li>현재 칸의 주변 $4$칸 중 청소되지 않은 빈 칸이 있는 경우,</li>
</ol>
<ul>
<li>3-1. 반시계 방향으로 $90^\circ$ 회전한다.</li>
<li>3-2. 바라보는 방향을 기준으로 앞쪽 칸이 청소되지 않은 빈 칸인 경우 한 칸 전진한다.</li>
<li>3-3. 1번으로 돌아간다.<blockquote>
<p><a href="https://www.acmicpc.net/problem/14503">https://www.acmicpc.net/problem/14503</a></p>
</blockquote>
<h3 id="입력">입력</h3>
첫째 줄에 방의 크기 $N$과 $M$이 입력된다. $(3 \le N, M \le 50)$  둘째 줄에 처음에 로봇 청소기가 있는 칸의 좌표 $(r, c)$와 처음에 로봇 청소기가 바라보는 방향 $d$가 입력된다. $d$가 $0$인 경우 북쪽, $1$인 경우 동쪽, $2$인 경우 남쪽, $3$인 경우 서쪽을 바라보고 있는 것이다.</li>
</ul>
<p>셋째 줄부터 $N$개의 줄에 각 장소의 상태를 나타내는 $N \times M$개의 값이 한 줄에 $M$개씩 입력된다. $i$번째 줄의 $j$번째 값은 칸 $(i, j)$의 상태를 나타내며, 이 값이 $0$인 경우 $(i, j)$가 청소되지 않은 빈 칸이고, $1$인 경우 $(i, j)$에 벽이 있는 것이다. 방의 가장 북쪽, 가장 남쪽, 가장 서쪽, 가장 동쪽 줄 중 하나 이상에 위치한 모든 칸에는 벽이 있다. 로봇 청소기가 있는 칸은 항상 빈 칸이다.</p>
<h3 id="출력">출력</h3>
<p>로봇 청소기가 작동을 시작한 후 작동을 멈출 때까지 청소하는 칸의 개수를 출력한다.</p>
<h3 id="풀이">풀이</h3>
<p>일반적인 탐색문제에서 방향이 추가된 문제로, 탐색에 있어 방향을 고려해야 한다.
문제 조건 중 탐색 방향을 반시계 방향으로 $90^\circ$회전할 때가 있는데, 현재 방향에 대하여 회전이므로 일일이 if문으로 확인하기에 복잡하다. 해당 방향으로 전진하는 경우를 dx dy 배열로 담고, 이를 인덱스로 잡는다.</p>
<blockquote>
<p>0 : 북 (-1,0)
1 : 동 (0,1)
2 : 남 (1,0)
3 : 서 (0,-1)</p>
</blockquote>
<p>위를 기준으로, 반시계 방향으로 회전한다면</p>
<ul>
<li>북(0) -&gt; 서(3)</li>
<li>동(1) -&gt; 북(0)</li>
<li>남(2) -&gt; 동(1)</li>
<li>서(3) -&gt; 남(2)</li>
</ul>
<p>이고, 북(0)일 경우에만 3, 제외한 나머지 방향은 현재 방향에서 -1 하면 된다.
다른 블로그에선 나머지 연산을 이용하여 계산하였는데, 방향이 들어가는 문제는 나머지 연산을 이용해서 푸는게 깔끔하고 좋은 것 같다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

using namespace std;

int n, m, r, c, d, answer, dis, nx, ny, nd;
int map[50][50];
//북, 동, 남, 서
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};

bool check(int x, int y)
{
    for (int i = 0; i &lt; 4; i++)
    {
        nx = x + dx[i], ny = y + dy[i];
        if (nx &gt;= 0 &amp;&amp; nx &lt; n &amp;&amp; ny &gt;= 0 &amp;&amp; ny &lt; m &amp;&amp; map[nx][ny] == 0)
        {
            return false;
        }
    }
    return true;
}
void dfs(int x, int y, int d)
{
    if (map[x][y] == 1 || dis == 1)
    {
        return;
    }
    if (map[x][y] == 0)
    {
        answer++;
        map[x][y] = 2;
    }

    if (check(x, y))
    {
        // int nd = (d + 2) % 4;
        d &gt; 1 ? nd = d - 2 : nd = d + 2;
        nx = x + dx[nd];
        ny = y + dy[nd];
        if (map[nx][ny] == 1)
        {
            dis = 1;
            return;
        }
        dfs(nx, ny, d);
    }

    else
    {
        nd = d;
        for (int i = 0; i &lt; 4; i++)
        {
            // nd = (nd + 3) % 4;
            nd == 0 ? nd = 3 : nd -= 1;
            nx = x + dx[nd];
            ny = y + dy[nd];
            if (map[nx][ny] == 0)
            {
                dfs(nx, ny, nd);
                break;
            }
        }
    }
}

int main()
{
    cin &gt;&gt; n &gt;&gt; m;
    cin &gt;&gt; r &gt;&gt; c &gt;&gt; d;

    for (int i = 0; i &lt; n; i++)
    {
        for (int j = 0; j &lt; m; j++)
        {
            cin &gt;&gt; map[i][j];
        }
    }
    dfs(r, c, d);

    cout &lt;&lt; answer &lt;&lt; &#39;\n&#39;;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준 C++] 15686 치킨 배달]]></title>
            <link>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-15686-%EC%B9%98%ED%82%A8-%EB%B0%B0%EB%8B%AC</link>
            <guid>https://velog.io/@wkawha_rowk/%EB%B0%B1%EC%A4%80-15686-%EC%B9%98%ED%82%A8-%EB%B0%B0%EB%8B%AC</guid>
            <pubDate>Thu, 05 Oct 2023 04:34:38 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>도시에 있는 치킨집 중에서 최대 M개를 고르고, 나머지 치킨집은 모두 폐업시켜야 한다. 어떻게 고르면, 도시의 치킨 거리가 가장 작게 될지 구하는 프로그램을 작성하시오.
<a href="https://www.acmicpc.net/problem/15686">https://www.acmicpc.net/problem/15686</a></p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 N(2 ≤ N ≤ 50)과 M(1 ≤ M ≤ 13)이 주어진다.</p>
<p>둘째 줄부터 N개의 줄에는 도시의 정보가 주어진다.</p>
<p>도시의 정보는 0, 1, 2로 이루어져 있고, 0은 빈 칸, 1은 집, 2는 치킨집을 의미한다. 집의 개수는 2N개를 넘지 않으며, 적어도 1개는 존재한다. 치킨집의 개수는 M보다 크거나 같고, 13보다 작거나 같다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 폐업시키지 않을 치킨집을 최대 M개를 골랐을 때, 도시의 치킨 거리의 최솟값을 출력한다.</p>
<h3 id="풀이">풀이</h3>
<p>지도 상의 모든 치킨집 중에서 주어진 m개 만큼 고르고, 선택된 치킨집들을 기준으로 각 집의 치킨거리를 구하여 도시의 치킨거리를 구한다. 
예를 들어, 지도상의 치킨집이 5개, m이 3이라 주어졌을 때 5가지 치킨집에서 3가지를 뽑는 경우의 수 총 10가지에 대한 도시 치킨 거리 중 가장 작은 값을 찾아내야 한다.</p>
<p>여러 치킨집 조합을 위해, 백트래킹으로 구현하였다.</p>
<blockquote>
<p>m개 선택 완료시, 각 집의 치킨거리 계산을 통해 도시의 치킨거리 최솟값 갱신</p>
</blockquote>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
#include &lt;cmath&gt;

using namespace std;

int map[50][50];
int n, m, answer = INT32_MAX;
vector&lt;pair&lt;int, int&gt;&gt; home;
vector&lt;pair&lt;int, int&gt;&gt; chicken;
int visited[50];

void dfs(int cnt, int node)
{
    if (cnt == m)
    {
        //현재 조합에 대한 도시의 치킨거리 게산
        int result = 0;
        for (int i = 0; i &lt; home.size(); i++)
        {
            int x = home[i].first;
            int y = home[i].second;
            int tmp = INT32_MAX;

            for (int j = 0; j &lt; chicken.size(); j++)
            {
                if (visited[j])
                {
                    //한 집의 치킨거리 계산
                    tmp = min(abs(x - chicken[j].first) + abs(y - chicken[j].second), tmp);
                }
            }
            result += tmp;
        }
        answer = min(answer, result);
    }
    else
    {
        for (int i = node; i &lt; chicken.size(); i++)
        {
            visited[i] = true;
            dfs(cnt + 1, i + 1);
            visited[i] = false;
        }
    }
}

int main()
{
    cin &gt;&gt; n &gt;&gt; m;

    for (int i = 0; i &lt; n; i++)
    {
        for (int j = 0; j &lt; n; j++)
        {
            cin &gt;&gt; map[i][j];
            if (map[i][j] != 0)
            {
                map[i][j] == 1 ? home.push_back({i, j}) : chicken.push_back({i, j});
            }
        }
    }

    dfs(0, 0);

    cout &lt;&lt; answer &lt;&lt; &#39;\n&#39;;

    return 0;
}</code></pre>
]]></description>
        </item>
    </channel>
</rss>