<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dropper_in.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 19 Mar 2026 13:06:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dropper_in.log</title>
            <url>https://velog.velcdn.com/images/dropper_in/profile/f1c7b1ee-9e36-4540-8271-c5c7ec02412d/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dropper_in.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dropper_in" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[# DFS와 재귀 완전 이해하기]]></title>
            <link>https://velog.io/@dropper_in/DFS%EC%99%80-%EC%9E%AC%EA%B7%80-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dropper_in/DFS%EC%99%80-%EC%9E%AC%EA%B7%80-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 19 Mar 2026 13:06:22 GMT</pubDate>
            <description><![CDATA[<p>코딩 테스트에서 자주 나오는 DFS(Depth-First Search)와 재귀(Recursion)를 이해하기 쉽게 정리해보기
매번 헷갈려해서 한번 문서화해두는 것이 나중에 참고하기도 편할 것 같다....</p>
<hr>
<h2 id="1-dfs란">1. DFS란?</h2>
<h3 id="정의">정의</h3>
<p>DFS는 “갈 수 있을 때까지 끝까지 탐색하는 방식”이다.</p>
<p>다음과 같은 구조를 생각해보자:</p>
<pre><code>내 방 → 거실 → 부엌
        ↓
       화장실</code></pre><p>탐색 순서:</p>
<ol>
<li>내 방 → 거실</li>
<li>거실 → 부엌 (끝까지 이동)</li>
<li>더 갈 곳이 없으면 되돌아감</li>
<li>화장실 탐색</li>
</ol>
<p>핵심:</p>
<pre><code>끝까지 간다 → 막히면 돌아온다 → 다른 길 탐색</code></pre><hr>
<h2 id="2-dfs-기본-구조">2. DFS 기본 구조</h2>
<pre><code class="language-js">function dfs(node) {
    visited[node] = true;

    for (연결된 노드들) {
        if (아직 방문 안했으면) {
            dfs(다음 노드);
        }
    }
}</code></pre>
<hr>
<h2 id="3-재귀란">3. 재귀란?</h2>
<h3 id="정의-1">정의</h3>
<p>재귀는 함수가 자기 자신을 다시 호출하는 방식이다.</p>
<hr>
<h2 id="4-재귀-예시">4. 재귀 예시</h2>
<h3 id="잘못된-경우-무한-호출">잘못된 경우 (무한 호출)</h3>
<pre><code class="language-js">function f() {
    f();
}</code></pre>
<h3 id="올바른-경우-종료-조건-존재">올바른 경우 (종료 조건 존재)</h3>
<pre><code class="language-js">function countDown(n) {
    if (n === 0) return;

    console.log(n);
    countDown(n - 1);
}</code></pre>
<p>실행 흐름:</p>
<pre><code>countDown(3)
→ 3 출력
→ countDown(2)
    → 2 출력
    → countDown(1)
        → 1 출력
        → countDown(0)
            → 종료</code></pre><hr>
<h2 id="5-dfs와-재귀의-관계">5. DFS와 재귀의 관계</h2>
<p>DFS는 재귀를 사용해 구현하는 경우가 많다.</p>
<pre><code class="language-js">function dfs(node) {
    visited[node] = true;

    for (let i = 0; i &lt; n; i++) {
        if (연결 &amp;&amp; 방문 안함) {
            dfs(i);
        }
    }
}</code></pre>
<p>의미:</p>
<ul>
<li>현재 노드를 방문</li>
<li>연결된 노드 중 방문하지 않은 곳으로 이동</li>
<li>그 노드에서도 같은 작업 반복</li>
</ul>
<hr>
<h2 id="6-동작-과정">6. 동작 과정</h2>
<p>그래프:</p>
<pre><code>0 — 1 — 2</code></pre><p>실행:</p>
<pre><code>dfs(0)
 → dfs(1)
   → dfs(2)
     → 종료
   → 복귀
 → 복귀</code></pre><hr>
<h2 id="7-단계별-흐름">7. 단계별 흐름</h2>
<ol>
<li><p>dfs(0)
 0 방문, 1로 이동</p>
</li>
<li><p>dfs(1)
 1 방문, 2로 이동</p>
</li>
<li><p>dfs(2)
 2 방문, 더 이상 이동 불가 → 종료</p>
</li>
</ol>
<p>복귀:</p>
<pre><code>dfs(2) → dfs(1) → dfs(0)</code></pre><hr>
<h2 id="8-핵심-요소">8. 핵심 요소</h2>
<h3 id="1-방문-처리">1. 방문 처리</h3>
<pre><code class="language-js">visited[node] = true;</code></pre>
<h3 id="2-탐색-조건">2. 탐색 조건</h3>
<pre><code class="language-js">if (연결 &amp;&amp; !visited)</code></pre>
<h3 id="3-재귀-호출">3. 재귀 호출</h3>
<pre><code class="language-js">dfs(next);</code></pre>
<hr>
<h2 id="9-정리">9. 정리</h2>
<ul>
<li>DFS는 깊이 우선 탐색 방식이다.</li>
<li>재귀는 DFS를 구현하는 대표적인 방법이다.</li>
<li>탐색은 “끝까지 진행 → 막히면 복귀” 구조로 동작한다.</li>
</ul>
<hr>
<h2 id="한-줄-요약">한 줄 요약</h2>
<p>DFS는 갈 수 있는 곳까지 계속 들어가는 재귀 기반 탐색이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 16 + Turbopack + SCSS Alias가 Windows에서 동작하지 않는 문제]]></title>
            <link>https://velog.io/@dropper_in/Next.js-16-Turbopack-SCSS-Alias%EA%B0%80-Windows%EC%97%90%EC%84%9C-%EB%8F%99%EC%9E%91%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@dropper_in/Next.js-16-Turbopack-SCSS-Alias%EA%B0%80-Windows%EC%97%90%EC%84%9C-%EB%8F%99%EC%9E%91%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Thu, 19 Mar 2026 12:41:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>WSL Ubuntu에서 잘 되던 프로젝트를 Windows로 옮겼더니 SCSS 빌드 에러가 발생했다. 
삽질 끝에 알고 보니 내 코드 잘못이 아닌 <strong>Vercel 측 공식 버그</strong>였다.</p>
</blockquote>
<hr>
<h2 id="발생-환경">발생 환경</h2>
<ul>
<li><strong>Next.js</strong>: 16.0.7 (Turbopack)</li>
<li><strong>sass</strong>: ^1.93.3</li>
<li><strong>OS</strong>: Windows (WSL Ubuntu에서 이전)</li>
</ul>
<hr>
<h2 id="에러-증상">에러 증상</h2>
<p><code>yarn dev</code> 실행 후 <code>localhost:3000</code>에서 다음과 같은 빌드 에러 발생:</p>
<pre><code>./src/app/(main)/page.module.scss
Error evaluating Node.js code
Error: Can&#39;t find stylesheet to import.
  ╷
3 │ @forward &#39;./utils&#39;;
  │ ^^^^^^^^^^^^^^^^^^
  ╵
  src\shared\styles\mixins\index.scss 3:1  @use
  src\app\(main)\page.module.scss 1:1      root stylesheet</code></pre><p>SCSS 파일 구조는 다음과 같았다.</p>
<pre><code>src/shared/styles/mixins/
├── index.scss   ← @forward &#39;./utils&#39; 등을 통합
├── _utils.scss
├── _flex.scss
├── _size.scss
├── _spacing.scss
└── _font.scss</code></pre><pre><code class="language-scss">// mixins/index.scss
@forward &#39;./utils&#39;;
@forward &#39;./flex&#39;;
@forward &#39;./size&#39;;
@forward &#39;./spacing&#39;;
@forward &#39;./font&#39;;</code></pre>
<hr>
<h2 id="삽질-과정">삽질 과정</h2>
<h3 id="1-alias-경로-오타-발견">1. alias 경로 오타 발견</h3>
<p><code>next.config.ts</code>를 확인하니 alias 경로가 틀려 있었음</p>
<pre><code class="language-ts">// ❌ 잘못된 경로
turbopack: {
  resolveAlias: {
    &#39;@styles&#39;: path.join(__dirname, &#39;src/styles&#39;), // 존재하지 않는 경로
  },
},</code></pre>
<p>실제 파일은 <code>src/shared/styles</code>에 있었으므로 수정</p>
<pre><code class="language-ts">// ✅ 수정된 경로
turbopack: {
  resolveAlias: {
    &#39;@styles&#39;: path.join(__dirname, &#39;src/shared/styles&#39;),
  },
},</code></pre>
<p>그래도 에러는 계속됐다. 그리고 그러면 왜 wsl에서는 문제가 없었던거니??</p>
<h3 id="2-sassoptionsincludepaths-추가-시도">2. sassOptions.includePaths 추가 시도</h3>
<pre><code class="language-ts">sassOptions: {
  includePaths: [path.join(__dirname, &#39;src/shared/styles&#39;)],
},</code></pre>
<p>효과 없음...</p>
<h3 id="3-sass-importer-추가-시도-findfileurl">3. sass importer 추가 시도 (findFileUrl)</h3>
<pre><code class="language-ts">sassOptions: {
  importers: [{
    findFileUrl(url: string) {
      if (!url.startsWith(&#39;@shared&#39;)) return null;
      return new URL(&#39;file:///&#39; + path.join(__dirname, &#39;src&#39;, url.replace(&#39;@&#39;, &#39;&#39;)));
    },
  }],
},</code></pre>
<p>새로운 에러 발생;;</p>
<pre><code>Error: An importer must have either canonicalize and load methods, or a findFileUrl method.</code></pre><p><strong>Turbopack은 <code>findFileUrl</code> 방식의 importer를 지원하지 않는다.</strong></p>
<h3 id="4-canonicalize--load-방식으로-변경-시도">4. canonicalize + load 방식으로 변경 시도</h3>
<pre><code class="language-ts">sassOptions: {
  importers: [{
    canonicalize(url: string) { ... },
    load(canonicalUrl: URL) { ... },
  }],
},</code></pre>
<p>역시 동작하지 않았다.</p>
<p>이외에도 스타일시트를 import하는 경로 전부를 상대경로 및 절대경로 방식으로 바꿨다가... 이것도 제대로 안 먹혀서 싹 되돌렸다.</p>
<hr>
<h2 id="찾아낸-원인">찾아낸 원인</h2>
<p>핵심 원인은 <strong>Next.js 16 Turbopack의 Windows SCSS 경로 처리 버그</strong>였다.</p>
<table>
<thead>
<tr>
<th>환경</th>
<th>경로 구분자</th>
<th>결과</th>
</tr>
</thead>
<tbody><tr>
<td>WSL Ubuntu</td>
<td><code>/</code> (슬래시)</td>
<td>✅ 정상 동작</td>
</tr>
<tr>
<td>Windows</td>
<td><code>\</code> (백슬래시)</td>
<td>❌ SCSS <code>@use</code>/<code>@forward</code> 경로 해석 실패</td>
</tr>
</tbody></table>
<p>이는 <strong>Vercel 공식 GitHub 이슈로 등록된 버그</strong>다. <strong>내 코드 잘못이 아니다! ㅜㅜ</strong></p>
<p>그리고 <code>sassOptions</code>가 동작하지 않은 이유도 따로 있었다.</p>
<blockquote>
<p>Turbopack은 Rust 기반 아키텍처라 <code>sassOptions</code>로 전달되는 <strong>JavaScript 함수를 직접 실행할 수 없다</strong>고 한다. <code>findFileUrl</code>, <code>canonicalize</code>, <code>load</code> 등 모든 JavaScript importer 방식이 Turbopack에서는 동작하지 않는다.</p>
</blockquote>
<hr>
<h2 id="해결-방법">해결 방법</h2>
<h3 id="로컬-개발-windows---webpack-플래그-사용">로컬 개발 (Windows): <code>--webpack</code> 플래그 사용</h3>
<pre><code class="language-json">// package.json
{
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;next dev --webpack&quot;,
    &quot;build&quot;: &quot;next build&quot;
  }
}</code></pre>
<ul>
<li><code>dev</code>는 Webpack으로 실행 → SCSS alias 정상 동작</li>
<li><code>build</code>는 Turbopack 유지 → 배포 시 그대로 사용</li>
</ul>
<h3 id="배포-vercel-그대로-사용">배포 (Vercel): 그대로 사용</h3>
<p>Vercel 배포 서버는 <strong>Linux</strong>이므로 Turbopack + SCSS alias가 정상 동작한다. <code>next build</code>에 별도 설정이 필요 없다.</p>
<hr>
<h2 id="정리">정리</h2>
<table>
<thead>
<tr>
<th>상황</th>
<th>명령어</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>로컬 개발 (Windows)</td>
<td><code>next dev --webpack</code></td>
<td>Webpack으로 SCSS 정상 처리</td>
</tr>
<tr>
<td>로컬 개발 (WSL/Mac)</td>
<td><code>next dev</code></td>
<td>Turbopack 그대로 사용</td>
</tr>
<tr>
<td>프로덕션 빌드</td>
<td><code>next build</code></td>
<td>Turbopack 유지, Vercel Linux에서 정상 동작</td>
</tr>
</tbody></table>
<p>Vercel이 Windows 경로 버그를 픽스하면 <code>package.json</code>에서 <code>--webpack</code> 플래그만 제거하면 된다.</p>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://github.com/vercel/next.js/issues/87243">Next.js GitHub Issue #87243</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[BFS vs DFS 정리]]></title>
            <link>https://velog.io/@dropper_in/BFS-vs-DFS-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dropper_in/BFS-vs-DFS-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 10 Mar 2026 12:53:45 GMT</pubDate>
            <description><![CDATA[<p>코테에서 <strong>그래프 탐색 문제</strong>는 대부분 두 가지 알고리즘으로 해결한다.</p>
<ul>
<li>DFS (Depth First Search)</li>
<li>BFS (Breadth First Search)</li>
</ul>
<p>처음 보면 두 알고리즘은</p>
<ul>
<li>directions 배열 사용</li>
<li>방문 체크</li>
<li>격자 이동</li>
</ul>
<p>등의 같은 구조를 사용해서 거의 동일해 보여도 <strong>탐색 방식과 사용 목적은 완전히 다르다.</strong>
자꾸 헷갈리는 부분이 있어서 잊어버리지 않도록 정리하기로 했다!!</p>
<ol>
<li>DFS와 BFS의 차이</li>
<li>미로 문제에서 BFS를 사용하는 이유</li>
<li>BFS 미로 최단거리 코드</li>
<li>DFS로 최단거리를 구하지 않는 이유</li>
</ol>
<hr>
<h1 id="1-dfs-vs-bfs-차이">1. DFS vs BFS 차이</h1>
<h2 id="dfs-depth-first-search">DFS (Depth First Search)</h2>
<p>DFS는 <strong>한 방향으로 끝까지 탐색</strong>한다.</p>
<p>예시</p>
<pre><code>S → A → B → C → D</code></pre><p>막히면 다시 돌아와서 다른 길을 탐색한다. 
이를 <strong>백트래킹(backtracking)</strong>이라 한다!!</p>
<h3 id="특징">특징</h3>
<ul>
<li>깊이 우선 탐색</li>
<li>일반적으로 재귀 사용</li>
<li>모든 경로 탐색에 적합</li>
<li>백트래킹 문제에 많이 사용</li>
</ul>
<p>대표 문제</p>
<ul>
<li>모든 경로 찾기</li>
<li>순열 / 조합</li>
<li>백트래킹</li>
<li>섬의 개수</li>
</ul>
<hr>
<h2 id="bfs-breadth-first-search">BFS (Breadth First Search)</h2>
<p>BFS는 <strong>가까운 것부터 탐색</strong>한다.</p>
<p>탐색 방식</p>
<pre><code>거리 0
S

거리 1
A B

거리 2
C D E</code></pre><p>중심에서 바깥으로 퍼져나가는 구조!!</p>
<h3 id="특징-1">특징</h3>
<ul>
<li>너비 우선 탐색</li>
<li>Queue 사용</li>
<li>최단거리 문제에 적합</li>
</ul>
<p>대표 문제</p>
<ul>
<li>미로 최단거리</li>
<li>바이러스 퍼짐</li>
<li>최소 이동 횟수</li>
<li>토마토 문제 (BOJ 7576)</li>
</ul>
<hr>
<h1 id="2-왜-미로-최단거리는-bfs인가">2. 왜 미로 최단거리는 BFS인가</h1>
<p>미로 문제의 목표는 보통 <strong>출발점 → 도착점 최단거리 구하기</strong>이다.</p>
<p>DFS는 먼 길을 먼저 발견할 수도 있다.</p>
<p><strong>S → A → B → C → D → E</strong> 라는 경로를 탐색했을 때, 거리는 5</p>
<p>하지만 실제 최단 경로 <strong>S → X → E</strong>가 존재하고, 그 거리는 2</p>
<p>DFS는 <strong>모든 경로를 탐색해야만</strong> 최단거리를 알 수 있다.</p>
<p>반면 BFS는 거리가 가장 가까운 순서대로 탐색하는 방식이다.</p>
<p>그래서 <strong>처음 도착한 경로 = 항상 최단거리</strong>가 보장된다!!</p>
<hr>
<h1 id="3-bfs-미로-최단거리-코드">3. BFS 미로 최단거리 코드</h1>
<p>미로 예시</p>
<pre><code>1 = 길
0 = 벽</code></pre><h3 id="bfs-코드">BFS 코드</h3>
<pre><code class="language-javascript">function solution(maps){

    // 미로의 세로 길이 (행 개수)
    const n = maps.length;

    // 미로의 가로 길이 (열 개수)
    const m = maps[0].length;

    // 상하좌우 이동을 위한 방향 배열
    // [dx, dy] 형태로 이동 방향을 표현
    const directions = [[0,1],[0,-1],[1,0],[-1,0]];

    // BFS 탐색에 사용할 큐
    // [x, y, dist] : 현재 위치와 시작점에서부터의 이동 거리
    const queue = [[0,0,1]];

    // 방문 여부를 기록하는 2차원 배열 생성
    // 처음에는 모든 칸이 false (방문 안함)
    const visited = Array.from({length:n},()=&gt;Array(m).fill(false));

    // 시작 위치 (0,0)은 이미 방문했으므로 true로 표시
    visited[0][0] = true;

    // queue에서 꺼낼 위치를 가리키는 포인터
    // shift() 대신 사용하여 시간복잡도 증가를 방지
    let head = 0;

    // 큐에 탐색할 위치가 남아있는 동안 계속 BFS 탐색
    while(head &lt; queue.length){

        // 현재 탐색할 위치를 큐에서 꺼냄
        // queue[head]를 읽은 뒤 head를 증가시키는 후위 증가 연산
        const [x,y,dist] = queue[head++];

        // 현재 위치가 목표 위치라면
        // 지금까지 이동한 거리 dist를 반환
        if(x === n-1 &amp;&amp; y === m-1) return dist;

        // 현재 위치에서 상하좌우 4방향 탐색
        for(const [dx,dy] of directions){

            // 다음에 이동할 위치 계산
            const nx = x + dx;
            const ny = y + dy;

            // 다음 위치가
            // 1️⃣ 미로 범위 안에 있고
            // 2️⃣ 길(1)이며
            // 3️⃣ 아직 방문하지 않았다면
            if(
                nx &gt;= 0 &amp;&amp;
                ny &gt;= 0 &amp;&amp;
                nx &lt; n &amp;&amp;
                ny &lt; m &amp;&amp;
                maps[nx][ny] === 1 &amp;&amp;
                !visited[nx][ny]
            ){
                // 해당 위치를 방문 처리
                visited[nx][ny] = true;

                // 다음 탐색 위치로 큐에 추가
                // 거리는 한 칸 이동했으므로 +1
                queue.push([nx,ny,dist+1]);
            }
        }
    }

    // BFS 탐색이 끝날 때까지 목표 지점에 도달하지 못했다면
    // 갈 수 없는 경우이므로 -1 반환
    return -1;
}</code></pre>
<p>코드 흐름을 정리해보면...</p>
<ol>
<li>시작점을 queue에 넣기</li>
<li>시작점을 visited 처리</li>
<li>queue에서 하나 꺼냄</li>
<li>꺼낸 위치가 목표 지점이라면 거리 반환</li>
<li>상하좌우 탐색</li>
<li>갈 수 있고 방문하지 않았다면
→ 꺼낸 위치 visited 처리
→ 갈 수 있는 위치 queue에 추가</li>
<li>queue가 빌 때까지 반복</li>
<li>끝까지 못 찾으면 -1 반환</li>
</ol>
<hr>
<h1 id="4-dfs로-최단거리를-구하지-않는-이유">4. DFS로 최단거리를 구하지 않는 이유</h1>
<p>모든 경로를 탐색하고 각 경로의 길이를 기록한 뒤 가장 짧은 경로 선택하는 방식으로 이론적으로는 가능하다. 하지만 DFS는 가능한 경로의 수가 많아질수록 탐색량이 기하급수적으로 증가한다는 단점이 있다.
반면 BFS는 시작점에서 가까운 위치부터 탐색을 진행하기 때문에 도착점을 처음 발견하는 순간 그 경로가 최단거리가 된다. 또한 각 칸을 한 번씩만 방문하므로 시간복잡도는 O(n × m) 수준이다.
따라서 100 * 100 = 10,000번의 탐색만으로 최단거리를 구할 수 있어 BFS가 훨씬 효율적이다.</p>
<hr>
<h3 id="코딩테스트-알고리즘-선택-기준">코딩테스트 알고리즘 선택 기준</h3>
<table>
<thead>
<tr>
<th>문제 유형</th>
<th>알고리즘</th>
</tr>
</thead>
<tbody><tr>
<td>미로 최단거리</td>
<td>BFS</td>
</tr>
<tr>
<td>퍼지는 문제 (불, 토마토, 바이러스)</td>
<td>BFS</td>
</tr>
<tr>
<td>최소 이동 횟수</td>
<td>BFS</td>
</tr>
<tr>
<td>모든 경로 탐색</td>
<td>DFS</td>
</tr>
<tr>
<td>백트래킹</td>
<td>DFS</td>
</tr>
<tr>
<td>그래프 연결 요소</td>
<td>DFS / BFS</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스] 코딩테스트 연습 : 배열 만들기 2 - 이진수로 접근하기]]></title>
            <link>https://velog.io/@dropper_in/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%97%B0%EC%8A%B5-%EB%B0%B0%EC%97%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0-2-%EC%9D%B4%EC%A7%84%EC%88%98%EB%A1%9C-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dropper_in/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%97%B0%EC%8A%B5-%EB%B0%B0%EC%97%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0-2-%EC%9D%B4%EC%A7%84%EC%88%98%EB%A1%9C-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 06 Nov 2025 12:50:59 GMT</pubDate>
            <description><![CDATA[<h4 id="문제-설명">문제 설명</h4>
<p>정수 l과 r이 주어졌을 때, l 이상 r이하의 정수 중에서 숫자 &quot;0&quot;과 &quot;5&quot;로만 이루어진 모든 정수를 오름차순으로 저장한 배열을 return 하는 solution 함수를 완성해 주세요.</p>
<p>만약 그러한 정수가 없다면, -1이 담긴 배열을 return 합니다.</p>
<h4 id="제한사항">제한사항</h4>
<blockquote>
<p>1 ≤ l ≤ r ≤ 1,000,000</p>
</blockquote>
<h4 id="예시">예시</h4>
<blockquote>
</blockquote>
<p>l = 5
r = 55
→ 5, 50, 55</p>
<p>조건을 만족하는 수가 없다면 -1을 반환합니다.</p>
<hr>
<h3 id="나의-시도--완전-탐색">나의 시도 : 완전 탐색</h3>
<p>처음엔 범위 내 모든 숫자를 문자열로 변환 후 정규식을 통해 검사하여 걸러냄</p>
<pre><code class="language-javascript">function solution(l, r) {
  const result = [];
  for (let i = l; i &lt;= r; i++) {
    const str = i.toString();
    if (/^[05]+$/.test(str)) result.push(i);
  }
  return result.length ? result : [-1];
}</code></pre>
<p>통과!! 그러나 비효율적... r이 커질수록 반복 횟수가 폭증</p>
<hr>
<h3 id="다른-풀이-참고--이진수-패턴으로-생각하기">다른 풀이 참고 : 이진수 패턴으로 생각하기</h3>
<p>조건에 맞는 수를 하나씩 검사하는 것 대신, 조건에 맞는 수를 직접 생성하는 방식?</p>
<p>아예 처음부터 <strong>가능한 경우의 수</strong>만 만들기. 즉, <strong>0과 5의 조합으로 만들어지는 숫자</strong>만 고려
→ <strong>불필요한 탐색이 없음</strong></p>
<p>문제의 핵심은 모든 자릿수가 <strong>0 또는 5</strong>로만 구성되어야 한다는 것
0과 5만 사용하는 수 = 이진수의 0과 1을 0과 5로 치환한 형태</p>
<p>십진수| 이진수 | 변환 후 |
| :-: | :--: | :-: |
|  1  |   1  |  5  |
|  2 |  10  |  50 |
|  3 |  11  |  55 |
| 4 |  100 | 500 |
| 5 |  101 | 505 |</p>
<p>즉, 이진수의 1 → 5로 바꾸면 우리가 찾는 <strong>0과 5로만 이루어진 수</strong>가 됨!!</p>
<h4 id="최종-풀이-코드">최종 풀이 코드</h4>
<pre><code class="language-javascript">function solution(l, r) {
  const result = [];

  // i를 1부터 시작해 가능한 모든 &#39;0과 5로만 이루어진 수&#39;를 생성
  for (let i = 1; ; i++) {
    // 1. i를 이진수로 변환
    // 2. 1 → 5로 치환
    const num = parseInt(i.toString(2).replace(/1/g, &#39;5&#39;), 10);

    // r 초과 시 탐색 종료
    if (num &gt; r) break;

    // l 이상이면 결과에 추가
    if (num &gt;= l) result.push(num);
  }

  return result.length ? result : [-1];
}</code></pre>
<h4 id="동작-예시">동작 예시</h4>
<table>
<thead>
<tr>
<th align="right">i</th>
<th align="center">이진수</th>
<th align="center">변환 후</th>
<th align="right">숫자</th>
<th align="center">범위 내?</th>
</tr>
</thead>
<tbody><tr>
<td align="right">1</td>
<td align="center">&quot;1&quot;</td>
<td align="center">&quot;5&quot;</td>
<td align="right">5</td>
<td align="center">O</td>
</tr>
<tr>
<td align="right">2</td>
<td align="center">&quot;10&quot;</td>
<td align="center">&quot;50&quot;</td>
<td align="right">50</td>
<td align="center">O</td>
</tr>
<tr>
<td align="right">3</td>
<td align="center">&quot;11&quot;</td>
<td align="center">&quot;55&quot;</td>
<td align="right">55</td>
<td align="center">O</td>
</tr>
<tr>
<td align="right">4</td>
<td align="center">&quot;100&quot;</td>
<td align="center">&quot;500&quot;</td>
<td align="right">500</td>
<td align="center">X</td>
</tr>
</tbody></table>
<p>결과: [5, 50, 55]</p>
<h4 id="시간-복잡도">시간 복잡도</h4>
<p>이진수 길이에 따라 반복되므로 O(log r) 정도로 매우 효율적
r이 커져도 빠르게 종료됩니다</p>
<h4 id="알게-된-점">알게 된 점</h4>
<p>조건을 검사하는 것보다 패턴을 생성하는 것이 더 효율적일 때가 있음.</p>
<p>0과 5 제약은 곧 이진수 패턴과 1:1로 대응.</p>
<p>toString(2)을 통한 이진수 변환은 코테에서 매우 유용한 도구가 될지도...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 + 리액트 디자인 패턴 스터디]]></title>
            <link>https://velog.io/@dropper_in/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%8A%A4%ED%84%B0%EB%94%94</link>
            <guid>https://velog.io/@dropper_in/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%8A%A4%ED%84%B0%EB%94%94</guid>
            <pubDate>Thu, 31 Jul 2025 10:04:49 GMT</pubDate>
            <description><![CDATA[<h2 id="리액트-hooks-패턴">리액트 Hooks 패턴</h2>
<p>클래스 컴포넌트를 사용하지 않고도 상태와 라이프사이클 메서드 활용 가능
Hooks 자체는 디자인 패턴이라고 할 수 없으나 많은 전통적 디자인 패턴 대체 가능</p>
<h3 id="1-클래스-컴포넌트의-한계">1. 클래스 컴포넌트의 한계</h3>
<ul>
<li><p>클래스 컴포넌트의 특징</p>
<ul>
<li>생성자 함수에서 상태 관리</li>
<li><code>componentDidMount</code>, <code>componentWillUnmount</code> 등 라이프사이클 메서드 사용</li>
<li>커스텀 메서드로 로직 추가</li>
</ul>
</li>
<li><p>하지만 함수형 컴포넌트를 클래스 컴포넌트로 리팩토링할 경우?</p>
<ul>
<li>클래스 문법에 대한 이해가 필요함</li>
<li>데이터 흐름을 깨지 않고 변환하기 어렵고 복잡함</li>
</ul>
</li>
</ul>
<h3 id="2-구조-변경의-어려움">2. 구조 변경의 어려움</h3>
<ul>
<li><p>여러 컴포넌트 간 코드 공유는 보통 다음 두 패턴 사용</p>
<ul>
<li><strong>고차 컴포넌트(컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수)</strong></li>
<li><strong>렌더 Props 패턴</strong></li>
</ul>
</li>
<li><p>하지만 이런 패턴을 적용하거나 변경하려면?</p>
<ul>
<li>전체 애플리케이션 구조를 변경해야 할 수 있음</li>
<li>컴포넌트가 클수록 구조 변경이 어려움</li>
<li><strong>컴포넌트 중첩이 심화되어 “Wrapper Hell”</strong> 발생 가능
→ 데이터 흐름이 복잡해지고 디버깅이 어려워짐</li>
</ul>
</li>
</ul>
<h3 id="3-복잡성-증가">3. 복잡성 증가</h3>
<ul>
<li><p>클래스 컴포넌트에 로직이 많아질수록?</p>
<ul>
<li>파일 크기와 복잡도가 빠르게 증가함</li>
<li>로직이 얽혀 구조화되지 않음</li>
<li>특정 로직이 어디서 쓰이는지 파악이 어려움</li>
<li>디버깅과 성능 최적화 어려움</li>
<li>라이프사이클 메서드 간 코드 중복 발생</li>
</ul>
</li>
</ul>
<h3 id="4-hooks의-등장">4. Hooks의 등장</h3>
<ul>
<li><p>위 문제를 해결하기 위해 <strong>React 16.8부터 Hooks 도입</strong></p>
</li>
<li><p>리액트 훅을 사용하면?</p>
<ul>
<li><strong>함수형 컴포넌트에서도 상태 관리 가능</strong> (<code>useState</code>)</li>
<li><strong>라이프사이클 관리 가능</strong> (<code>useEffect</code>)</li>
<li><strong>여러 컴포넌트 간 상태 관련 로직의 재사용 용이</strong></li>
</ul>
</li>
</ul>
<hr>
<h2 id="상태-hook">상태 Hook</h2>
<h3 id="1-상태-hook-usestate">1. 상태 Hook: <code>useState</code></h3>
<ul>
<li><strong>함수형 컴포넌트에서 상태(state)를 관리할 수 있게 해주는 Hook</strong></li>
<li>사용 전 <code>import { useState } from &#39;react&#39;</code>로 리액트에서 불러와야 함</li>
<li><code>useState</code>는 <strong>초기 상태값(initial state)</strong> 을 인자로 받음</li>
<li>반환값은 배열 형태로 두 가지를 제공<ul>
<li><code>const [state, setState] = useState(initialValue);</code></li>
<li><code>state</code>: 현재 상태 값 → 클래스 컴포넌트의 <code>this.state.value</code>와 유사</li>
<li><code>setState</code>: 상태를 업데이트하는 함수 → <code>this.setState()</code>와 유사</li>
</ul>
</li>
</ul>
<h3 id="2-이펙트-hook-useeffect">2. 이펙트 Hook: <code>useEffect</code></h3>
<ul>
<li><code>useEffect</code>는 컴포넌트의 <strong>라이프사이클 메서드를 대체</strong>할 수 있는 Hook</li>
<li><code>componentDidMount</code>, <code>componentDidUpdate</code>, <code>componentWillUnmount</code>를 하나로 통합하여 처리할 수 있음</li>
</ul>
<table>
<thead>
<tr>
<th><strong>라이프사이클 메서드 (클래스형)</strong></th>
<th><strong>useEffect 사용 방식 (함수형)</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td><code>componentDidMount</code></td>
<td><code>useEffect(() =&gt; { ... }, [])</code></td>
<td>컴포넌트 <strong>마운트 시 1회 실행</strong></td>
</tr>
<tr>
<td><code>componentWillUnmount</code></td>
<td><code>useEffect(() =&gt; { return () =&gt; { ... } }, [])</code></td>
<td>컴포넌트 <strong>언마운트 시 실행</strong></td>
</tr>
<tr>
<td><code>componentDidUpdate</code></td>
<td><code>useEffect(() =&gt; { ... }, [deps])</code></td>
<td>지정한 <strong>deps 변경 시마다 실행</strong></td>
</tr>
</tbody></table>
<h3 id="3-커스텀-hook">3. <strong>커스텀 Hook</strong></h3>
<ul>
<li><code>use</code>로 시작하는 이름으로 함수 작성 (리액트가 규칙 준수 여부 판단)</li>
<li>공통 로직을 추출해 재사용 가능</li>
<li>직접 작성 가능하며, 외부에서 공유된 Hook도 설치해 사용 가능</li>
</ul>
<hr>
<h2 id="주요-기본-hook"><strong>주요 기본 Hook</strong></h2>
<h4 id="usestate"><code>useState</code></h4>
<ul>
<li>함수형 컴포넌트에서도 상태 관리 가능</li>
<li><code>const [state, setState] = useState(initialValue)</code> 형태</li>
</ul>
<h4 id="useeffect"><code>useEffect</code></h4>
<ul>
<li>렌더링 이후 실행되는 부수 효과 처리</li>
<li>타이머, API 호출, 이벤트 등록 등</li>
<li>클래스의 <code>componentDidMount</code>, <code>componentDidUpdate</code>, <code>componentWillUnmount</code> 대체</li>
</ul>
<h4 id="usecontext"><code>useContext</code></h4>
<ul>
<li>Context API 상태를 쉽게 사용</li>
<li>props drilling 없이 전역 상태 접근 가능</li>
<li>context 객체를 인자로 받음</li>
</ul>
<h4 id="usereducer"><code>useReducer</code></h4>
<ul>
<li>복잡한 상태 로직, 상태 간 의존 관계 처리에 적합</li>
<li><code>const [state, dispatch] = useReducer(reducer, initialState)</code></li>
<li>트리 깊은 구조에서의 성능 최적화에 효과적</li>
</ul>
<hr>
<h2 id="hook의-장단점">Hook의 장단점</h2>
<h3 id="관심사-기반-코드-구성">관심사 기반 코드 구성</h3>
<ul>
<li>라이프사이클이 아닌 기능 단위로 코드 정리 → 가독성 향상</li>
</ul>
<h3 id="클래스-불필요">클래스 불필요</h3>
<ul>
<li>JS 클래스 관리 어려움 해결</li>
<li>핫 리로딩 호환, 함수형 프로그래밍 용이</li>
</ul>
<h3 id="복잡한-로직-추출-가능">복잡한 로직 추출 가능</h3>
<ul>
<li>고차 컴포넌트, render props 없이도 상태 로직 공유 가능</li>
<li>테스트 및 재사용 쉬운 순수 함수로 구성 가능</li>
</ul>
<h3 id="규칙-엄수-필요">규칙 엄수 필요</h3>
<ul>
<li>컴포넌트의 최상위에서만 호출 (조건문/반복문 내 사용 금지)</li>
<li><code>use</code>로 시작할 것 : Linter(ESLint + React Hooks plugin)로 규칙 위반 감지 가능</li>
</ul>
<h3 id="학습-곡선-존재">학습 곡선 존재</h3>
<ul>
<li><code>useEffect</code>, <code>useCallback</code>, <code>useMemo</code>는 잘못 사용 시 성능 문제 유발</li>
</ul>
<hr>
]]></description>
        </item>
    </channel>
</rss>