<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>takeU</title>
    <link>https://takeu.tistory.com/</link>
    <description>벌써32라니</description>
    <language>ko</language>
    <pubDate>Sun, 31 May 2026 10:38:50 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>takeU</managingEditor>
    <image>
      <title>takeU</title>
      <url>https://tistory1.daumcdn.net/tistory/4799439/attach/e9ff47ad55a04a859a07ebb1846ef3f5</url>
      <link>https://takeu.tistory.com</link>
    </image>
    <item>
      <title>11. 클라우드 리버스 프록시 설정</title>
      <link>https://takeu.tistory.com/428</link>
      <description>&lt;h2&gt;구축 이유&lt;/h2&gt;
&lt;p&gt;기존에는 Hosting.kr에서 도메인을 관리하면서 &lt;code&gt;api.maplemworlds.kr&lt;/code&gt;과 &lt;code&gt;maplemworlds.kr&lt;/code&gt; 두 도메인 모두의 요청을 로컬 PC로 전달하도록 설정해 로컬 환경에서 트래픽을 분기 처리하고 있었음.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;api.maplemworlds.kr&lt;/code&gt; 요청은 로컬에서 PM2로 실행 중인 서버로,&lt;br&gt;&lt;code&gt;maplemworlds.kr&lt;/code&gt; 요청은 Netlify에서 호스팅 중인 웹으로 전달되도록 구성했는데&lt;/p&gt;
&lt;p&gt;모든 트래픽이 로컬 컴퓨터를 한 번씩 거치기 때문인지 다른 이유때문인지 정확히는 모르겠지만, 집에서 사용하는 와이파이가 프록시 서버를 구축한 이후에 간헐적으로 끊기는 문제가 생겨 클라우드 프록시 서버를 따로 두기로 결정.&lt;/p&gt;
&lt;p&gt;옮기는게 유의미한 효과가 있을진 모르겠지만..일단 옮기긴 해놨음&lt;/p&gt;
&lt;h2&gt;Cloudflare로 도메인 연결&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Cloudflare 무료 가입 → “Add Site” → maplemworlds.kr 추가&lt;/li&gt;
&lt;li&gt;요금제는 “Free Plan” 선택 (무과금 짱)&lt;/li&gt;
&lt;li&gt;Cloudflare에서 네임서버 2개 확인 (예: ns1.cloudflare.com, ns2.cloudflare.com)&lt;/li&gt;
&lt;li&gt;Hosting.kr 접속 → 도메인 관리 → 네임서버 설정 → 사용자 지정 네임서버 선택 (기존 hostingkr에서 제공하던 네임서버 4개는 삭제)&lt;/li&gt;
&lt;li&gt;이후 cloudflare에 active가 떠있고, &lt;code&gt;nslookup -type=ns maplemworlds.kr&lt;/code&gt; 를 체크했을때 설정한 네임서버가 있으면 완료&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Dev/개발 일지</category>
      <category>cloudflare</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/428</guid>
      <comments>https://takeu.tistory.com/428#entry428comment</comments>
      <pubDate>Thu, 6 Nov 2025 20:47:17 +0900</pubDate>
    </item>
    <item>
      <title>Github Pages next.js 에셋 404 뜰 때</title>
      <link>https://takeu.tistory.com/426</link>
      <description>&lt;h2&gt;원인&lt;/h2&gt;
&lt;p&gt;GitHub Pages는 기본적으로 &lt;strong&gt;Jekyll&lt;/strong&gt;이라는 정적 사이트 생성기를 사용합니다.&lt;br&gt;그리고 Jekyll은 &lt;code&gt;_next/&lt;/code&gt;, &lt;code&gt;_site/&lt;/code&gt;, &lt;code&gt;_includes/&lt;/code&gt;처럼 &lt;strong&gt;밑줄(_)로 시작하는 폴더를 무시&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;즉, &lt;code&gt;_next/&lt;/code&gt; 폴더가 있어도 &lt;strong&gt;서빙되지 않습니다&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;해결 방법: &lt;code&gt;.nojekyll&lt;/code&gt; 파일 추가&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.nojekyll&lt;/code&gt;이라는 &lt;strong&gt;빈 파일을 GitHub Pages 루트에 추가하면&lt;/strong&gt; Jekyll 처리를 비활성화할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;touch docs/.nojekyll
git add docs/.nojekyll
git commit -m &amp;quot;fix: add .nojekyll to serve _next static files&amp;quot;
git push origin main&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Dev/etc.</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/426</guid>
      <comments>https://takeu.tistory.com/426#entry426comment</comments>
      <pubDate>Mon, 2 Jun 2025 14:47:38 +0900</pubDate>
    </item>
    <item>
      <title>JWT 인증과 인가 흐름</title>
      <link>https://takeu.tistory.com/425</link>
      <description>&lt;h1&gt;JWT 인증과 인가 흐름&lt;/h1&gt;
&lt;h2&gt;1. &lt;strong&gt;Axios 인터셉터를 활용한 자동 토큰 갱신 예시&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Axios 인터셉터 설정&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Axios&lt;/strong&gt;를 사용하여 &lt;strong&gt;Access Token&lt;/strong&gt;의 만료를 감지하고, &lt;strong&gt;Refresh Token&lt;/strong&gt;을 사용하여 &lt;strong&gt;새로운 Access Token&lt;/strong&gt;을 발급받는 방식으로 자동화할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import axios from &amp;#39;axios&amp;#39;;

// Axios 인스턴스 생성
const api = axios.create({
  baseURL: &amp;#39;https://example.com/api&amp;#39;,
  timeout: 10000,
  headers: { &amp;#39;Content-Type&amp;#39;: &amp;#39;application/json&amp;#39; },
});

// 요청 인터셉터 (요청이 보내지기 전에 처리)
api.interceptors.request.use(
  config =&amp;gt; {
    // localStorage에서 Access Token을 가져와 Authorization 헤더에 넣음
    const accessToken = localStorage.getItem(&amp;#39;accessToken&amp;#39;);
    if (accessToken) {
      config.headers[&amp;#39;Authorization&amp;#39;] = `Bearer ${accessToken}`;
    }
    return config;
  },
  error =&amp;gt; Promise.reject(error)
);

// 응답 인터셉터 (응답이 돌아왔을 때 처리)
api.interceptors.response.use(
  response =&amp;gt; {
    // 정상적인 응답 처리
    return response;
  },
  async error =&amp;gt; {
    // 401 에러 (Access Token 만료된 경우) 처리
    if (error.response &amp;amp;&amp;amp; error.response.status === 401) {
      try {
        // Refresh Token을 사용하여 새로운 Access Token 요청
        const refreshToken = localStorage.getItem(&amp;#39;refreshToken&amp;#39;);
        const response = await axios.post(&amp;#39;/api/refresh-token&amp;#39;, { refreshToken });

        // 새로운 Access Token을 localStorage에 저장
        const { accessToken } = response.data;
        localStorage.setItem(&amp;#39;accessToken&amp;#39;, accessToken);

        // 원래의 요청을 새로운 Access Token으로 재요청
        const originalRequest = error.config;
        originalRequest.headers[&amp;#39;Authorization&amp;#39;] = `Bearer ${accessToken}`;

        return api(originalRequest);  // 재요청
      } catch (refreshError) {
        // Refresh Token이 만료되었거나 에러가 발생한 경우, 로그아웃 처리
        console.error(&amp;#39;Refresh Token도 만료되었거나 에러가 발생했습니다.&amp;#39;);
        // 예: 로그아웃 처리
        window.location.href = &amp;#39;/login&amp;#39;;
      }
    }

    // 401 외의 오류는 그대로 처리
    return Promise.reject(error);
  }
);

export default api;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;작동 원리&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;요청 인터셉터: 모든 API 요청 전에 Access Token을 Authorization 헤더에 추가.&lt;/li&gt;
&lt;li&gt;응답 인터셉터: 응답에서 401 Unauthorized 에러가 발생하면, Refresh Token을 사용하여 새로운 Access Token을 요청하고, 이를 사용해 원래 요청을 재전송.&lt;/li&gt;
&lt;li&gt;자동 토큰 갱신: Access Token이 만료되면, Refresh Token을 통해 새로운 토큰을 발급받아 자동으로 재요청함.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;2. &lt;strong&gt;Access Token과 Refresh Token의 정의 및 저장 방법&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Access Token&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;정의&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인증된 사용자의 권한을 증명하는 &lt;strong&gt;짧은 유효 기간&lt;/strong&gt;을 가진 &lt;strong&gt;JWT&lt;/strong&gt; 토큰.&lt;/li&gt;
&lt;li&gt;서버에서 생성하고, 클라이언트는 이를 사용하여 &lt;strong&gt;보호된 리소스&lt;/strong&gt;에 접근할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;저장 방법&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;보통 &lt;strong&gt;localStorage&lt;/strong&gt; 또는 &lt;strong&gt;쿠키&lt;/strong&gt;에 저장됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버에 요청 시 &lt;strong&gt;빠르게 인증&lt;/strong&gt;을 처리할 수 있음.&lt;/li&gt;
&lt;li&gt;클라이언트에서 쉽게 &lt;strong&gt;헤더에 추가&lt;/strong&gt;하여 요청할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;유효 기간&lt;/strong&gt;이 짧아, 만료 후 갱신이 필요함.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;localStorage&lt;/strong&gt;에 저장 시 &lt;strong&gt;XSS 공격&lt;/strong&gt;에 취약할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;쿠키에 저장&lt;/strong&gt; 시 &lt;strong&gt;CSRF 공격&lt;/strong&gt;에 취약할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;Refresh Token&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;정의&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Access Token&lt;/strong&gt;의 만료 후, 새로운 &lt;strong&gt;Access Token&lt;/strong&gt;을 발급받기 위한 &lt;strong&gt;긴 유효 기간&lt;/strong&gt;을 가진 &lt;strong&gt;JWT&lt;/strong&gt; 또는 &lt;strong&gt;임의의 문자열&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;일반적으로 &lt;strong&gt;서버에서만 저장&lt;/strong&gt;되며, 클라이언트는 이를 통해 &lt;strong&gt;Access Token을 갱신&lt;/strong&gt;할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;저장 방법&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;보통 &lt;strong&gt;httpOnly 쿠키&lt;/strong&gt;에 저장하여 &lt;strong&gt;XSS 공격&lt;/strong&gt;으로부터 보호.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;긴 유효 기간&lt;/strong&gt;을 가지므로 &lt;strong&gt;Access Token&lt;/strong&gt;의 만료 시 계속해서 &lt;strong&gt;새로운 Access Token&lt;/strong&gt;을 발급받을 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;httpOnly 쿠키에 저장&lt;/strong&gt;하면 &lt;strong&gt;XSS 공격&lt;/strong&gt;에 강한 보안을 제공함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Refresh Token의 탈취&lt;/strong&gt;가 발생하면 &lt;strong&gt;액세스 권한&lt;/strong&gt;을 부여받은 상태로 &lt;strong&gt;새로운 Access Token&lt;/strong&gt;을 발급받을 수 있기 때문에 &lt;strong&gt;보안상 위험&lt;/strong&gt;이 있음.&lt;/li&gt;
&lt;li&gt;만약 &lt;strong&gt;로그아웃 처리&lt;/strong&gt;가 제대로 되어 있지 않으면, &lt;strong&gt;Refresh Token&lt;/strong&gt;이 계속해서 사용될 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. &lt;strong&gt;Access Token과 Refresh Token의 저장 방법 비교&lt;/strong&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;저장 방법&lt;/th&gt;
&lt;th&gt;장점&lt;/th&gt;
&lt;th&gt;단점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;localStorage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;- 쉽게 접근 가능하고, 다른 요청에서 사용하기 용이함.&lt;/td&gt;
&lt;td&gt;- &lt;strong&gt;XSS 공격&lt;/strong&gt;에 취약함.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;쿠키 (일반 쿠키)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;- &lt;strong&gt;CSRF 공격&lt;/strong&gt;에 취약할 수 있음.&lt;/td&gt;
&lt;td&gt;- 쿠키의 크기에 제한이 있어, 저장할 수 있는 정보에 제한이 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;쿠키 (httpOnly)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;- &lt;strong&gt;XSS 공격&lt;/strong&gt;에 강함.&lt;/td&gt;
&lt;td&gt;- &lt;strong&gt;CSRF 공격&lt;/strong&gt;에 취약할 수 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;세션 저장소&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;- 세션이 끝날 때 &lt;strong&gt;토큰이 자동으로 삭제&lt;/strong&gt;됨.&lt;/td&gt;
&lt;td&gt;- &lt;strong&gt;페이지 새로 고침&lt;/strong&gt; 시 토큰이 삭제됨.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;&lt;strong&gt;추천&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Access Token&lt;/strong&gt;은 &lt;strong&gt;localStorage&lt;/strong&gt;에 저장하고, &lt;strong&gt;Refresh Token&lt;/strong&gt;은 &lt;strong&gt;httpOnly 쿠키&lt;/strong&gt;에 저장하는 것이 &lt;strong&gt;보안성&lt;/strong&gt;과 &lt;strong&gt;편리성&lt;/strong&gt;을 고려한 좋은 방법임.&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;localStorage&lt;/strong&gt;에 &lt;strong&gt;Access Token&lt;/strong&gt;을 저장하면 &lt;strong&gt;서버 요청&lt;/strong&gt; 시 빠르게 &lt;strong&gt;Authorization 헤더&lt;/strong&gt;에 추가할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;httpOnly 쿠키에 Refresh Token&lt;/strong&gt;을 저장하면 &lt;strong&gt;XSS 공격&lt;/strong&gt;으로부터 보호받을 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;4. &lt;strong&gt;요약&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;프런트엔드&lt;/strong&gt;는 &lt;strong&gt;Access Token&lt;/strong&gt;을 &lt;strong&gt;Authorization 헤더&lt;/strong&gt;에 포함하여 API 요청을 보냄.&lt;/li&gt;
&lt;li&gt;서버는 &lt;strong&gt;Access Token&lt;/strong&gt;을 &lt;strong&gt;검증&lt;/strong&gt;하여 유효하면 요청을 처리하고, 유효하지 않으면 &lt;strong&gt;401 Unauthorized&lt;/strong&gt;를 반환함.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;401 오류&lt;/strong&gt;가 발생하면, &lt;strong&gt;프런트엔드&lt;/strong&gt;는 &lt;strong&gt;Refresh Token&lt;/strong&gt;을 사용하여 &lt;strong&gt;새로운 Access Token&lt;/strong&gt;을 발급받고, 이를 사용해 원래의 요청을 다시 보냄.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Token&lt;/strong&gt;은 &lt;strong&gt;짧은 유효 기간&lt;/strong&gt;을 가지며 &lt;strong&gt;localStorage&lt;/strong&gt; 또는 &lt;strong&gt;쿠키&lt;/strong&gt;에 저장됨.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refresh Token&lt;/strong&gt;은 &lt;strong&gt;긴 유효 기간&lt;/strong&gt;을 가지며, 보통 &lt;strong&gt;httpOnly 쿠키&lt;/strong&gt;에 저장됨.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Dev/etc.</category>
      <category>JWT</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/425</guid>
      <comments>https://takeu.tistory.com/425#entry425comment</comments>
      <pubDate>Wed, 12 Mar 2025 00:16:28 +0900</pubDate>
    </item>
    <item>
      <title>서버 증설 횟수 ( Level 2, JavaScript, 2025 프로그래머스 코드챌린지 2차 예선 )</title>
      <link>https://takeu.tistory.com/424</link>
      <description>&lt;pre&gt;&lt;code&gt;const solution = (players, m, k) =&amp;gt; {
    let res = 0
    const computer = []
    for (let i = 0; i &amp;lt; players.length; i++) {
        while (computer[0] === i) computer.shift()
        if (Math.floor(players[i] / m) &amp;gt; computer.length) {
            let count = Math.floor(players[i] / m) - computer.length
            res += count
            while (count--) computer.push(i + k)
        }
    }
    return res
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;시뮬레이션 &lt;/p&gt;
&lt;p&gt;그냥 구현하면됨&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>javascript</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/424</guid>
      <comments>https://takeu.tistory.com/424#entry424comment</comments>
      <pubDate>Thu, 13 Feb 2025 18:48:02 +0900</pubDate>
    </item>
    <item>
      <title>택배 상자 꺼내기 ( Level 1, JavaScript, 2025 프로그래머스 코드챌린지 2차 예선 )</title>
      <link>https://takeu.tistory.com/423</link>
      <description>&lt;pre&gt;&lt;code&gt;const solution = (n, w, num) =&amp;gt; {
    let res = 1
    const total = Math.ceil(n / w)
    const target = Math.ceil(num / w)
    const nRemain = n % w || n
    const numRemain = num % w || num
    if (total % 2 === target % 2 &amp;amp;&amp;amp; nRemain &amp;lt; numRemain) res -= 1 
    if (total % 2 !== target % 2 &amp;amp;&amp;amp; nRemain + numRemain &amp;lt;= w) res -= 1
    return total - target + res
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;total - 전체 쌓여지는 층 수&lt;br&gt;target - num이 쌓인 층 수&lt;/p&gt;
&lt;p&gt;total과 target이 같은 방향인지, 반대 방향인지 확인 후 타겟의 위에 상자가 있는지 체크하는 코드를 통해 위에 없는 경우 -1을 해줌&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/423</guid>
      <comments>https://takeu.tistory.com/423#entry423comment</comments>
      <pubDate>Thu, 13 Feb 2025 18:34:52 +0900</pubDate>
    </item>
    <item>
      <title>지게차와 크레인 ( Level 2, JavaScript, 2025 프로그래머스 코드챌린지 1차 예선 )</title>
      <link>https://takeu.tistory.com/422</link>
      <description>&lt;pre&gt;&lt;code&gt;const check = (storage, x, y) =&amp;gt; {
    const [xlen, ylen] = [storage.length, storage[0].length]
    const dx = [-1, 1, 0, 0]
    const dy = [0, 0, -1, 1]
    const queue = [[x, y]]
    const visited = Array.from({ length: xlen }, () =&amp;gt; Array(ylen).fill(0))
    visited[x][y] = 1

    while (queue.length) {
        const cur = queue.shift()
        const [x, y] = cur
        for (let i = 0; i &amp;lt; 4; i++) {
            const [nx, ny] = [x + dx[i], y + dy[i]]
            if (x === 0 || x === xlen - 1
               || y === 0 || y === ylen - 1) {
                return true
            }
            if (0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; xlen &amp;amp;&amp;amp; 0 &amp;lt;= ny &amp;amp;&amp;amp; 0 &amp;lt; ylen &amp;amp;&amp;amp;
                !visited[nx][ny] &amp;amp;&amp;amp; storage[nx][ny] === &amp;#39;0&amp;#39;) {
                queue.push([nx, ny])
                visited[nx][ny] = 1
            }
        }
    }
    return false
}
const solution = (storage, requests) =&amp;gt; {
    storage = storage.map(row =&amp;gt; row.split(&amp;#39;&amp;#39;))
    const [xlen, ylen] = [storage.length, storage[0].length]
    let res = 0

    for (let request of requests) {
        const remove = []
        if (request.length === 1) {
            for (let i = 0; i &amp;lt; xlen; i++) {
                for (let j = 0; j &amp;lt; ylen; j++) {
                    if (storage[i][j] === request &amp;amp;&amp;amp; check(storage, i, j)) {
                        res += 1
                        remove.push([i, j])
                    }
                }
            }
        } else {
            for (let i = 0; i &amp;lt; xlen; i++) {
                for (let j = 0; j &amp;lt; ylen; j++) {
                    if (storage[i][j] === request[0]) {
                        res += 1
                        remove.push([i, j])
                    }
                }
            }
        }

        for (let r of remove) {
            const [x, y] = r
            storage[x][y] = &amp;#39;0&amp;#39;
        }
    }
    return xlen * ylen - res                           
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;시뮬레이션 + bfs &lt;/p&gt;
&lt;p&gt;조각을 뽑은곳은 0으로 채우고 길이 뚫려있는지 확인하는 check함수를 만들음&lt;br&gt;check 함수 true 조건은 0을 타고 가장 바깥에 도착했는지 여부&lt;br&gt;더 깔끔하게 짤 수 있어보이는데 처음생각난대로 우다다하니까 통과됨&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>javascript</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/422</guid>
      <comments>https://takeu.tistory.com/422#entry422comment</comments>
      <pubDate>Mon, 10 Feb 2025 23:50:02 +0900</pubDate>
    </item>
    <item>
      <title>비밀 코드 해독 ( Level 2, JavaScript, 2025 프로그래머스 코드챌린지 1차 예선 )</title>
      <link>https://takeu.tistory.com/421</link>
      <description>&lt;pre&gt;&lt;code&gt;const combinations = (arr, k) =&amp;gt; {
    const result = []
    const combine = (start, combo) =&amp;gt; {
        if (combo.length === k) {
            result.push([...combo])
            return
        }
        for (let i = start; i &amp;lt; arr.length; i++) {
            combo.push(arr[i])
            combine(i + 1, combo)
            combo.pop()
        }
    }

    combine(0, [])
    return result
}

const solution = (n, q, ans) =&amp;gt; {
    let com = combinations(Array(n).fill(1).map((num, i) =&amp;gt; num + i), 5)
    let res = 0
    for (let c of com) {
        for (let i = 0; i &amp;lt; q.length; i++) {
            if (c.filter(num =&amp;gt; q[i].includes(num)).length !== ans[i]) break
            if (i === q.length - 1) res += 1
        }
    }
    return res
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;조합 + 브루트포스&lt;/p&gt;
&lt;p&gt;n이 적어서 하나하나 대보면 나옴&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>javascript</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/421</guid>
      <comments>https://takeu.tistory.com/421#entry421comment</comments>
      <pubDate>Mon, 10 Feb 2025 23:15:23 +0900</pubDate>
    </item>
    <item>
      <title>유연근무제 ( Level 1, JavaScript, 2025 프로그래머스 코드챌린지 1차 예선 )</title>
      <link>https://takeu.tistory.com/420</link>
      <description>&lt;pre&gt;&lt;code&gt;const timeToMin = (time) =&amp;gt; {
    return Number(String(time).slice(0, -2)) * 60 + Number(String(time).slice(-2))
}

const solution = (schedules, timelogs, startday) =&amp;gt; {
    return timelogs.filter((log, logIdx) =&amp;gt; {
        return log.filter((time, timeIdx) =&amp;gt; {
            if ((timeIdx + startday) % 7 === 6 || ((timeIdx + startday) % 7 === 0)) return true
            return timeToMin(time) &amp;lt;= timeToMin(schedules[logIdx]) + 10
        }).length === 7
    }).length
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;구현&lt;/p&gt;
&lt;p&gt;시간 처리하고 모듈러만 적용하면 됨&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>javascript</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/420</guid>
      <comments>https://takeu.tistory.com/420#entry420comment</comments>
      <pubDate>Mon, 10 Feb 2025 23:14:04 +0900</pubDate>
    </item>
    <item>
      <title>유사 칸토어 비트열 ( Level 2, JavaScript, 연습문제 )</title>
      <link>https://takeu.tistory.com/419</link>
      <description>&lt;pre&gt;&lt;code&gt;const recur = (num) =&amp;gt; {
    if (num % 5 === 2) return 0
    if (num &amp;lt; 5) return 1
    return recur(parseInt(num / 5))
}

const solution = (n, l, r) =&amp;gt; {
    let res = 0
    for (let i = l - 1; i &amp;lt; r; i++) {
        res += recur(i)
    }
    return res
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;재귀&lt;/p&gt;
&lt;p&gt;처음에 n을 같이 넘기니 시간이 몇배로 더걸려서 number만 체크하는 로직으로 변경하니 통과&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/419</guid>
      <comments>https://takeu.tistory.com/419#entry419comment</comments>
      <pubDate>Mon, 10 Feb 2025 12:18:47 +0900</pubDate>
    </item>
    <item>
      <title>테이블 해시 함수 ( Level 2, JavaScript, 연습문제 )</title>
      <link>https://takeu.tistory.com/418</link>
      <description>&lt;pre&gt;&lt;code&gt;const solution = (data, col, row_begin, row_end) =&amp;gt; {
    return data
        .sort((a, b) =&amp;gt; {
            const [x, y] = [a[col - 1], b[col - 1]];
            return x !== y ? x - y : b[0] - a[0];
        })
        .slice(row_begin - 1, row_end)
        .map((row, i) =&amp;gt; row.reduce((acc, cur) =&amp;gt; acc + (cur % (row_begin + i)), 0))
        .reduce((res, cur) =&amp;gt; res ^ cur, 0);
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;배열 단순 가공&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>javascript</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/418</guid>
      <comments>https://takeu.tistory.com/418#entry418comment</comments>
      <pubDate>Sun, 9 Feb 2025 20:11:20 +0900</pubDate>
    </item>
    <item>
      <title>우박수열 정적분 ( Level 2, JavaScript, 연습문제 )</title>
      <link>https://takeu.tistory.com/417</link>
      <description>&lt;pre&gt;&lt;code&gt;const solution = (k, ranges) =&amp;gt; {
    const dp = [0], res = []
    while (k &amp;gt; 1) {
        let next = k
        next = k % 2 ? 3 * next + 1 : k / 2
        dp.push((dp.at(-1) ?? 0) + (k + next) / 2)
        k = next
    }

    const len = dp.length - 1
    for (let range of ranges) {
        let [start, end] = range
        res.push(len + end &amp;lt; start ? -1 : dp[len + end] - dp[start])
    }
    return res
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;구간합 문제&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>javascript</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/417</guid>
      <comments>https://takeu.tistory.com/417#entry417comment</comments>
      <pubDate>Sat, 8 Feb 2025 15:11:40 +0900</pubDate>
    </item>
    <item>
      <title>전력망을 둘로 나누기 ( Level 2, JavaScript, 연습문제 )</title>
      <link>https://takeu.tistory.com/416</link>
      <description>&lt;pre&gt;&lt;code&gt;const check = (hash, start, n) =&amp;gt; {
    let count = 0
    const queue = [start]
    const visited = Array(n).fill(0)
    visited[start] = 1
    while (queue.length) {
        count += 1
        const cur = queue.shift()
        for (let num of hash[cur]) {
            if (!visited[num]) {
                queue.push(num)
                visited[num] = 1
            }
        }
    }
    return count
}

const solution = (n, wires) =&amp;gt; {
    let res = 100
    for (let i = 0; i &amp;lt; wires.length; i++) {
        let [left, right] = wires[i]
        const hash = Array(n).fill(0).map((el, idx) =&amp;gt; idx + 1).reduce((acc, cur) =&amp;gt; {
                            acc[cur] = []
                            return acc
                        }, {})
        for (let j = 0; j &amp;lt; wires.length; j++) {
            if (i === j) continue
            const [x, y] = wires[j]
            hash[x].push(y)
            hash[y].push(x)
        }
        res = Math.min(res, Math.abs(check(hash, left, n) - check(hash, right, n)))
    }
    return res
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;완탐은 풀면서도 항상 뭔가 찝찝한 느낌..&lt;br&gt;hash + bfs&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>javascript</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/416</guid>
      <comments>https://takeu.tistory.com/416#entry416comment</comments>
      <pubDate>Sat, 8 Feb 2025 14:40:19 +0900</pubDate>
    </item>
    <item>
      <title>연속된 부분 수열의 합 ( Level 2, JavaScript, 연습문제 )</title>
      <link>https://takeu.tistory.com/415</link>
      <description>&lt;pre&gt;&lt;code&gt;const solution = (sequence, k) =&amp;gt; {
    let [left, right, acc] = [0, 0, sequence[0]]
    const res = []
    while (right &amp;lt; sequence.length &amp;amp;&amp;amp; left &amp;lt;= right) {
        if (acc &amp;lt; k) {
            right += 1
            acc += sequence[right]
        } else {
            if (acc === k) res.push([right - left, left, right])
            acc -= sequence[left]
            left += 1
        }

    }
    return res.sort((a, b) =&amp;gt; a[0] - b[0] || a[1] - b[1])[0].slice(1)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;투포인터&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>javascript</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/415</guid>
      <comments>https://takeu.tistory.com/415#entry415comment</comments>
      <pubDate>Mon, 3 Feb 2025 18:27:47 +0900</pubDate>
    </item>
    <item>
      <title>10. Github Actions 배포 자동화</title>
      <link>https://takeu.tistory.com/414</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;배포 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;name: Deploy on Push

on:
  push:
    branches:
      - main  # main 브랜치에 푸시될 때 실행

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      # 1. 리포지토리 체크아웃 / 저장소 코드를 워크플로 실행 환경에 복사하는 GitHub에서 제공하는 공식 액션
      - name: Checkout repository
        uses: actions/checkout@v3

      # 2. SSH 키 설정 및 서버에 접속하여 빌드 실행
      - name: Deploy to server
        env:
          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
        run: |
          # SSH 키 설정
          mkdir -p ~/.ssh
          echo &quot;$SSH_PRIVATE_KEY&quot; &amp;gt; ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa

          # SSH 접속 및 빌드 실행
          ssh -p 포트 -o StrictHostKeyChecking=no 이름@IP &quot;
            cd ~/Desktop/playground/apps/maplem/frontend &amp;amp;&amp;amp;
            git pull &amp;amp;&amp;amp;
            pnpm install &amp;amp;&amp;amp;
            pnpm build
          &quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 실행 이후 빌드파일을 nginx로 이동시키는 과정까지 빌드 커맨드에 들어있어 자동으로 새로운 버전을 서빙하게 됨.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt; 키 등록 방법&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ssh 비밀키 복사 &lt;code&gt;cat ~/.ssh/id_rsa&lt;/code&gt; 후 복사&lt;/li&gt;
&lt;li&gt;GitHub Repository로 이동&lt;/li&gt;
&lt;li&gt;Settings &amp;rarr; Secrets and Variables &amp;rarr; Actions로 이동&lt;/li&gt;
&lt;li&gt;New repository secret을 클릭&lt;/li&gt;
&lt;li&gt;이름을 SSH_PRIVATE_KEY로 설정하고 복사한 개인 키를 붙여넣기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 공개키를 등록하는 과정&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ssh 공개키 복사 &lt;code&gt;cat ~/.ssh/id_rsa.pub&lt;/code&gt; 후 복사&lt;/li&gt;
&lt;li&gt;ssh 접속 후 &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;에 내용 복사&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 테스트코드를 도입하는 경우 수정될 여지가 있음&lt;/p&gt;</description>
      <category>Dev/개발 일지</category>
      <category>githubactions</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/414</guid>
      <comments>https://takeu.tistory.com/414#entry414comment</comments>
      <pubDate>Sun, 2 Feb 2025 15:34:49 +0900</pubDate>
    </item>
    <item>
      <title>코딩테스트를 위한 JavaScript 알고리즘</title>
      <link>https://takeu.tistory.com/413</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;bfs&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;start - &lt;code&gt;[x, y, time]&lt;/code&gt;&lt;br /&gt;end - &lt;code&gt;[x, y]&lt;/code&gt;&lt;br /&gt;board - 이차원 배열 (O - 이동 가능 / X - 불가능)&lt;br /&gt;visited - 방문 기록 (방문 확인 1 / 미방문 0)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;start에서 end까지 가는 시간 계산&lt;br /&gt;시작시간은 start의 세번째 인자 기준으로 도달하지 못하는 경우 -1&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const bfs = (start, end, board) =&amp;gt; {
    const dx = [-1, 1, 0, 0]
    const dy = [0, 0, -1, 1]
    const visited = Array.from({ length: board.length }, () =&amp;gt; Array(board[0].length).fill(0))
    visited[start[0]][start[1]] = 1
    const queue = [start]

    while (queue.length) {
        const [x, y, time] = queue.shift()
        for (let i = 0; i &amp;lt; 4; i++) {
            const [nx, ny] = [x + dx[i], y + dy[i]]
            if (0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; board.length &amp;amp;&amp;amp;
                0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; board[0].length &amp;amp;&amp;amp;
                !visited[nx][ny] &amp;amp;&amp;amp;
                board[nx][ny] !== 'X'
               ) {
                       if (nx === end[0] &amp;amp;&amp;amp; ny === end[1]) {
                        return time + 1
                    }
                    queue.push([nx, ny, time + 1])
                    visited[nx][ny] = 1
            }
        }
    }
    return -1
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;dfs&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const dfs = (x, y, time, end, board, visited) =&amp;gt; {
    if (x === end[0] &amp;amp;&amp;amp; y === end[1]) return time

    for (let i = 0 i &amp;lt; 4 i++) {
        const [nx, ny] = [x + dx[i], y + dy[i]]
        if (
            0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; board.length &amp;amp;&amp;amp;
            0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; board[0].length &amp;amp;&amp;amp;
            !visited[nx][ny] &amp;amp;&amp;amp;
            board[nx][ny] !== 'X'
        ) {
            visited[nx][ny] = 1
            const result = dfs(nx, ny, time + 1, end, board, visited)
            if (result !== -1) {
                return result
            }
            visited[nx][ny] = 0
        }
    }

    return -1
}

const solveDFS = (start, end, board) =&amp;gt; {
    const dx = [-1, 1, 0, 0]
    const dy = [0, 0, -1, 1]
    const visited = Array.from({ length: board.length }, () =&amp;gt; Array(board[0].length).fill(0))
    visited[start[0]][start[1]] = 1

    return dfs(start[0], start[1], 0, end, board, visited)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최대 힙 (우선순위 큐, heap)&lt;/h2&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;class MaxHeap {
  constructor() {
    this.heap = [];
  }

  getParentIndex(index) {
    return Math.floor((index - 1) / 2);
  }

  getLeftChildIndex(index) {
    return 2 * index + 1;
  }

  getRightChildIndex(index) {
    return 2 * index + 2;
  }

  swap(index1, index2) {
    [this.heap[index1], this.heap[index2]] = [this.heap[index2], this.heap[index1]];
  }

  insert(value) {
    this.heap.push(value);
    this.heapifyUp(this.heap.length - 1);
  }

  heapifyUp(index) {
    let currentIndex = index;
    let parentIndex = this.getParentIndex(currentIndex);

    while (currentIndex &amp;gt; 0 &amp;amp;&amp;amp; this.heap[currentIndex] &amp;gt; this.heap[parentIndex]) {
      this.swap(currentIndex, parentIndex);
      currentIndex = parentIndex;
      parentIndex = this.getParentIndex(currentIndex);
    }
  }

  extractMax() {
    if (this.heap.length === 0) return null;

    const max = this.heap[0];
    const end = this.heap.pop();

    if (this.heap.length &amp;gt; 0) {
      this.heap[0] = end;
      this.heapifyDown(0);
    }

    return max;
  }

  heapifyDown(index) {
    let largest = index;
    const leftChildIndex = this.getLeftChildIndex(index);
    const rightChildIndex = this.getRightChildIndex(index);

    if (leftChildIndex &amp;lt; this.heap.length &amp;amp;&amp;amp; this.heap[leftChildIndex] &amp;gt; this.heap[largest]) {
      largest = leftChildIndex;
    }

    if (rightChildIndex &amp;lt; this.heap.length &amp;amp;&amp;amp; this.heap[rightChildIndex] &amp;gt; this.heap[largest]) {
      largest = rightChildIndex;
    }

    if (largest !== index) {
      this.swap(index, largest);
      this.heapifyDown(largest);
    }
  }

  peek() {
    return this.heap.length &amp;gt; 0 ? this.heap[0] : null;
  }

  size() {
    return this.heap.length;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최소 힙&lt;/h2&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;class MinHeap {
  constructor() {
    this.heap = [];
  }

  getParentIndex(index) {
    return Math.floor((index - 1) / 2);
  }

  getLeftChildIndex(index) {
    return 2 * index + 1;
  }

  getRightChildIndex(index) {
    return 2 * index + 2;
  }

  swap(index1, index2) {
    [this.heap[index1], this.heap[index2]] = [this.heap[index2], this.heap[index1]];
  }

  insert(value) {
    this.heap.push(value);
    this.heapifyUp(this.heap.length - 1);
  }

  heapifyUp(index) {
    let currentIndex = index;
    let parentIndex = this.getParentIndex(currentIndex);

    while (currentIndex &amp;gt; 0 &amp;amp;&amp;amp; this.heap[currentIndex] &amp;lt; this.heap[parentIndex]) {
      this.swap(currentIndex, parentIndex);
      currentIndex = parentIndex;
      parentIndex = this.getParentIndex(currentIndex);
    }
  }

  extractMin() {
    if (this.heap.length === 0) return null;

    const min = this.heap[0];
    const end = this.heap.pop();

    if (this.heap.length &amp;gt; 0) {
      this.heap[0] = end;
      this.heapifyDown(0);
    }

    return min;
  }

  heapifyDown(index) {
    let smallest = index;
    const leftChildIndex = this.getLeftChildIndex(index);
    const rightChildIndex = this.getRightChildIndex(index);

    if (leftChildIndex &amp;lt; this.heap.length &amp;amp;&amp;amp; this.heap[leftChildIndex] &amp;lt; this.heap[smallest]) {
      smallest = leftChildIndex;
    }

    if (rightChildIndex &amp;lt; this.heap.length &amp;amp;&amp;amp; this.heap[rightChildIndex] &amp;lt; this.heap[smallest]) {
      smallest = rightChildIndex;
    }

    if (smallest !== index) {
      this.swap(index, smallest);
      this.heapifyDown(smallest);
    }
  }

  peek() {
    return this.heap.length &amp;gt; 0 ? this.heap[0] : null;
  }

  size() {
    return this.heap.length;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>Algorithm/theory</category>
      <category>BFS</category>
      <category>DFS</category>
      <category>heapq</category>
      <category>javascript</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/413</guid>
      <comments>https://takeu.tistory.com/413#entry413comment</comments>
      <pubDate>Mon, 27 Jan 2025 15:30:03 +0900</pubDate>
    </item>
    <item>
      <title>미로 탈출 ( Level 2, JavaScript, 연습문제 )</title>
      <link>https://takeu.tistory.com/412</link>
      <description>&lt;pre&gt;&lt;code&gt;const bfs = (start, end, maps) =&amp;gt; {
    const dx = [-1, 1, 0, 0]
    const dy = [0, 0, -1, 1]
    const visited = Array.from({ length: maps.length }, () =&amp;gt; Array(maps[0].length).fill(0))
    visited[start[0]][start[1]] = 1
    const queue = [start]

    while (queue.length) {
        const [x, y, time] = queue.shift()
        for (let i = 0; i &amp;lt; 4; i++) {
            const [nx, ny] = [x + dx[i], y + dy[i]]
            if (0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; maps.length
               &amp;amp;&amp;amp; 0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; maps[0].length
               &amp;amp;&amp;amp; !visited[nx][ny]
                &amp;amp;&amp;amp; maps[nx][ny] !== &amp;#39;X&amp;#39;
               ) {
                    if (nx === end[0] &amp;amp;&amp;amp; ny === end[1]) {
                        return time + 1
                    }
                    queue.push([nx, ny, time + 1])
                    visited[nx][ny] = 1
            }
        }
    }
    return -1
}

const solution = (maps) =&amp;gt; {
    let s, l, e
    for (let i = 0; i &amp;lt; maps.length; i++) {
        for (let j = 0; j &amp;lt; maps[0].length; j++) {
            if (maps[i][j] === &amp;#39;S&amp;#39;) s = [i, j, 0]
            else if (maps[i][j] === &amp;#39;L&amp;#39;) l = [i, j, 0]
            else if (maps[i][j] === &amp;#39;E&amp;#39;) e = [i, j]
        }
    }
    const sTol = bfs(s, l, maps)
    const lToe = bfs(l, e, maps)
    if (sTol === -1 || lToe === -1) return -1
    return sTol + lToe
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;bfs -ㅠ- &lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>BFS</category>
      <category>javascript</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/412</guid>
      <comments>https://takeu.tistory.com/412#entry412comment</comments>
      <pubDate>Mon, 27 Jan 2025 14:15:35 +0900</pubDate>
    </item>
    <item>
      <title>뒤에 있는 큰 수 찾기 ( Level 2, JavaScript, 연습문제 )</title>
      <link>https://takeu.tistory.com/410</link>
      <description>&lt;pre&gt;&lt;code&gt;const solution = (numbers) =&amp;gt; {
    const stack = [[0, numbers[0]]], res = Array(numbers.length).fill(-1)
    for (let i = 1; i &amp;lt; numbers.length; i++) {
        while (stack.length &amp;amp;&amp;amp; stack.at(-1)[1] &amp;lt; numbers[i]) {
            const [idx, cur] = stack.pop()
            res[idx] = numbers[i]
        }
        stack.push([i, numbers[i]])
    }
    return res
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;스택&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/410</guid>
      <comments>https://takeu.tistory.com/410#entry410comment</comments>
      <pubDate>Wed, 15 Jan 2025 22:48:00 +0900</pubDate>
    </item>
    <item>
      <title>교점에 별 만들기 ( Level 2, JavaScript, 위클리 챌린지 )</title>
      <link>https://takeu.tistory.com/411</link>
      <description>&lt;pre&gt;&lt;code&gt;function solution(line) {
    const spot = []
    let [minX, maxX, minY, maxY] = [Infinity, -Infinity, Infinity, -Infinity] 
    for (let i = 0; i &amp;lt; line.length; i++) {
        for (let j = i + 1; j &amp;lt; line.length; j++) {
            const [a1, b1, c1] = line[i]
            const [a2, b2, c2] = line[j]
            if (a1 / b1 !== a2 / b2) {
                const x = (b1 * c2 - c1 * b2) / (b2 * a1 - b1 * a2)
                const y = (c1 * a2 - a1 * c2) / (b2 * a1 - b1 * a2)
                if (x === parseInt(x) &amp;amp;&amp;amp; y === parseInt(y)) {
                    spot.push([x, y])
                    if (x &amp;lt; minX) minX = x
                    if (x &amp;gt; maxX) maxX = x
                    if (y &amp;lt; minY) minY = y
                    if (y &amp;gt; maxY) maxY = y
                }
            }
        }
    }
    const res = Array.from({ length: maxY - minY + 1 }, () =&amp;gt; Array(maxX - minX + 1).fill(&amp;#39;.&amp;#39;))
    for (let [x, y] of spot) {
        res[y - minY][x - minX] = &amp;#39;*&amp;#39;
    }
    return res.reverse().map(row =&amp;gt; row.join(&amp;#39;&amp;#39;))
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;구현인데..의미없는 구현&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/411</guid>
      <comments>https://takeu.tistory.com/411#entry411comment</comments>
      <pubDate>Wed, 15 Jan 2025 20:34:56 +0900</pubDate>
    </item>
    <item>
      <title>무인도 여행 ( Level 2, JavaScript, 연습문제)</title>
      <link>https://takeu.tistory.com/409</link>
      <description>&lt;pre&gt;&lt;code&gt;const dx = [-1, 0, 1, 0]
const dy = [0, -1, 0, 1]        

const dfs = (maps, visited, x, y, sum) =&amp;gt; {
    visited[x][y] = 1
    for (let i = 0; i &amp;lt; 4; i++) {
        const [nx, ny] = [x + dx[i], y + dy[i]]
        if (0 &amp;lt;= nx &amp;amp;&amp;amp; nx &amp;lt; maps.length &amp;amp;&amp;amp;
            0 &amp;lt;= ny &amp;amp;&amp;amp; ny &amp;lt; maps[0].length &amp;amp;&amp;amp;
            maps[nx][ny] !== &amp;#39;X&amp;#39; &amp;amp;&amp;amp; !visited[nx][ny]) {
            sum += dfs(maps, visited, nx, ny, +maps[nx][ny])
        }
    }
    return sum
}
const solution = (maps) =&amp;gt; {
    const res = []
    const [x, y] = [maps.length, maps[0].length]
    const visited = Array.from({ length: x }, () =&amp;gt; Array(y).fill(0)) 
    for (let i = 0; i &amp;lt; x; i++) {
        for (let j = 0; j &amp;lt; y; j++) {
            if (maps[i][j] !== &amp;#39;X&amp;#39; &amp;amp;&amp;amp; !visited[i][j]) {
                res.push(dfs(maps, visited, i, j, +maps[i][j]))
            } 
            visited[i][j] = 1
        }
    }
    return res.length ? res.sort((a, b) =&amp;gt; a - b) : [-1]
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;dfs&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/409</guid>
      <comments>https://takeu.tistory.com/409#entry409comment</comments>
      <pubDate>Wed, 15 Jan 2025 18:58:46 +0900</pubDate>
    </item>
    <item>
      <title>디펜스 게임 ( Level 2, JavaScript, 연습문제)</title>
      <link>https://takeu.tistory.com/408</link>
      <description>&lt;pre&gt;&lt;code&gt;class MaxHeap {
  constructor() {
    this.heap = [];
  }

  getParentIndex(index) {
    return Math.floor((index - 1) / 2);
  }

  getLeftChildIndex(index) {
    return 2 * index + 1;
  }

  getRightChildIndex(index) {
    return 2 * index + 2;
  }

  swap(index1, index2) {
    [this.heap[index1], this.heap[index2]] = [this.heap[index2], this.heap[index1]];
  }

  insert(value) {
    this.heap.push(value);
    this.heapifyUp(this.heap.length - 1);
  }

  heapifyUp(index) {
    let currentIndex = index;
    let parentIndex = this.getParentIndex(currentIndex);

    while (currentIndex &amp;gt; 0 &amp;amp;&amp;amp; this.heap[currentIndex] &amp;gt; this.heap[parentIndex]) {
      this.swap(currentIndex, parentIndex);
      currentIndex = parentIndex;
      parentIndex = this.getParentIndex(currentIndex);
    }
  }

  extractMax() {
    if (this.heap.length === 0) return null;

    const max = this.heap[0];
    const end = this.heap.pop();

    if (this.heap.length &amp;gt; 0) {
      this.heap[0] = end;
      this.heapifyDown(0);
    }

    return max;
  }

  heapifyDown(index) {
    let largest = index;
    const leftChildIndex = this.getLeftChildIndex(index);
    const rightChildIndex = this.getRightChildIndex(index);

    if (leftChildIndex &amp;lt; this.heap.length &amp;amp;&amp;amp; this.heap[leftChildIndex] &amp;gt; this.heap[largest]) {
      largest = leftChildIndex;
    }

    if (rightChildIndex &amp;lt; this.heap.length &amp;amp;&amp;amp; this.heap[rightChildIndex] &amp;gt; this.heap[largest]) {
      largest = rightChildIndex;
    }

    if (largest !== index) {
      this.swap(index, largest);
      this.heapifyDown(largest);
    }
  }

  peek() {
    return this.heap.length &amp;gt; 0 ? this.heap[0] : null;
  }

  size() {
    return this.heap.length;
  }
}


function solution(n, k, enemy) {
    let res = 0, i = 0
    const maxHeap = new MaxHeap();

    while (k &amp;gt;= 0 &amp;amp;&amp;amp; n &amp;gt;= 0 &amp;amp;&amp;amp; i &amp;lt; enemy.length) {
        n -= enemy[i]
        maxHeap.insert(enemy[i])
        if (n &amp;lt; 0 &amp;amp;&amp;amp; k &amp;gt; 0) {
            const max = maxHeap.extractMax()
            n += max 
            k -= 1
        }
        res += 1
        i += 1
    }
    return n &amp;gt;= 0 ? res : res - 1
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;자바스크립트 최대힙 구현&lt;/p&gt;
&lt;p&gt;n에서 일단 병사 제거 + 힙에 추가 한 뒤 n이 0 아래로 내려가면 heap에서 최대값을 더해주고 k를 소모하는 방식&lt;/p&gt;</description>
      <category>Algorithm/programmers</category>
      <category>algorithm</category>
      <category>javascript</category>
      <category>Programmers</category>
      <author>takeU</author>
      <guid isPermaLink="true">https://takeu.tistory.com/408</guid>
      <comments>https://takeu.tistory.com/408#entry408comment</comments>
      <pubDate>Tue, 14 Jan 2025 18:39:14 +0900</pubDate>
    </item>
  </channel>
</rss>