<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>6kittylog</title>
    <link>https://6kitt-hack.tistory.com/</link>
    <description>sixeunseoth@gmail.com </description>
    <language>ko</language>
    <pubDate>Sat, 8 Aug 2026 12:10:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>육키티</managingEditor>
    <image>
      <title>6kittylog</title>
      <url>https://tistory1.daumcdn.net/tistory/6547124/attach/417b87d0261b43b2b2a3d313c0df78ec</url>
      <link>https://6kitt-hack.tistory.com</link>
    </image>
    <item>
      <title>tmux 세션 생성, 나가기, 재접속, 삭제</title>
      <link>https://6kitt-hack.tistory.com/275</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;세션 생성&lt;/p&gt;
&lt;pre id=&quot;code_1785801490817&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% tmux new -s &amp;lt;세션 이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 나가기 (삭제는 아님)&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1785801626097&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;단축키 
control(^) + b, then d 
윈도우는 아마 control이 ctrl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 재접속&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1785801514484&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% tmux attach -t &amp;lt;세션 이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 삭제&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1785801554647&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% tmux kill-session -t &amp;lt;세션 이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활성화된 세션 리스트&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1785801576216&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% tmux ls&lt;/code&gt;&lt;/pre&gt;</description>
      <category>dev</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/275</guid>
      <comments>https://6kitt-hack.tistory.com/275#entry275comment</comments>
      <pubDate>Tue, 4 Aug 2026 09:01:25 +0900</pubDate>
    </item>
    <item>
      <title>Linux 로그 분석하려는데 표준 시간 korea로 바꾸기</title>
      <link>https://6kitt-hack.tistory.com/271</link>
      <description>&lt;pre id=&quot;code_1782262073481&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌──(kali㉿kali)-[~]
└─$ if test -f /var/log/auth.log; then sudo tail -n 20 /var/log/auth.log; else echo '/var/log/auth.log not found; using journalctl -u ssh'; sudo journalctl -u ssh --since '30 minutes ago' --no-pager | tail -20; fi
/var/log/auth.log not found; using journalctl -u ssh
Jun 23 20:10:05 kali sshd-session[5555]: pam_unix(sshd:session): session opened for user kali(uid=1000) by kali(uid=0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그를 까봤는데 시간이 이상해서&lt;/p&gt;
&lt;pre id=&quot;code_1782262127225&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo timedatectl set-timezone Asia/Seoul&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어로 표준시간을 바꾸었다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782262112692&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌──(kali㉿kali)-[~]
└─$ timedatectl
               Local time: Wed 2026-06-24 09:39:33 KST
           Universal time: Wed 2026-06-24 00:39:33 UTC
                 RTC time: Wed 2026-06-24 00:39:34
                Time zone: Asia/Seoul (KST, +0900)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no
                                                                                                      
┌──(kali㉿kali)-[~]
└─$ if test -f /var/log/auth.log; then sudo tail -n 20 /var/log/auth.log; else echo '/var/log/auth.log not found; using journalctl -u ssh'; sudo journalctl -u ssh --since '30 minutes ago' --no-pager | tail -20; fi
/var/log/auth.log not found; using journalctl -u ssh
Jun 24 09:10:05 kali sshd-session[5555]: pam_unix(sshd:session): session opened for user kali(uid=1000) by kali(uid=0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음 잘 나오는군&amp;nbsp;&lt;/p&gt;</description>
      <category>dev</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/271</guid>
      <comments>https://6kitt-hack.tistory.com/271#entry271comment</comments>
      <pubDate>Wed, 24 Jun 2026 09:49:22 +0900</pubDate>
    </item>
    <item>
      <title>UTM에 띄운 Kali SSH 연결하기</title>
      <link>https://6kitt-hack.tistory.com/270</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;mac 기준 control+shift+v 하면 로컬 내용을 복붙할 수 있긴 한데 뭔가 불편하니까 SSH로 kali 접속해서 로컬에서 사용하는 것이 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 kali에 openssh-server를 깔아서 먼저 ssh를 열어줘야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782260438221&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install -y openssh-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어로 설치하고&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782260444370&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo systemctl start ssh
sudo systemctl enable ssh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어로 실행&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782260495620&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo systemctl status ssh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어는 상태 확인&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 kali에서 ip addr 치면 수많은 내용중에 eth0 옆에 있는 IP 주소가 있을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssh kali@&amp;lt;해당 IP 주소&amp;gt;로 terminal에서 접속하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-24 오전 9.24.13.png&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQz2ns/dJMcai4M8Kk/HVPkp58exFjkb5knbVwNK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQz2ns/dJMcai4M8Kk/HVPkp58exFjkb5knbVwNK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQz2ns/dJMcai4M8Kk/HVPkp58exFjkb5knbVwNK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQz2ns%2FdJMcai4M8Kk%2FHVPkp58exFjkb5knbVwNK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;218&quot; data-filename=&quot;스크린샷 2026-06-24 오전 9.24.13.png&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>dev</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/270</guid>
      <comments>https://6kitt-hack.tistory.com/270#entry270comment</comments>
      <pubDate>Wed, 24 Jun 2026 09:24:27 +0900</pubDate>
    </item>
    <item>
      <title>mac에서 vulhub의 도커 빌드가 안되는데...</title>
      <link>https://6kitt-hack.tistory.com/269</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;vulhub로 도커 구축하고 nuclei 하는 실습이 있었는데 내 UTM kali는 arm64라서 vulhub 대부분이 amd 기준이라 거의 다 튕기는 거 같다. 이와 관련한 트러블슈팅 두 가지를 정리했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. QEMU 애뮬레이션으로 amd64 실행하는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. QEMU/binfmt 설치&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782184701574&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌──(kali㉿kali)-[~/&amp;hellip;/day02-cve/vulhub/tomcat/CVE-2025-24813]
└─$ sudo apt install -y qemu-user-static binfmt-support
Note, selecting 'qemu-user-binfmt' instead of 'qemu-user-static'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782184740940&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌──(kali㉿kali)-[~/&amp;hellip;/day02-cve/vulhub/tomcat/CVE-2025-24813]
└─$ docker run --privileged --rm tonistiigi/binfmt --install amd64
installing: amd64 qemu-x86_64 already registered&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위와 같이 강제 적용 해준다. 나는 이미 already registered라고 뜬다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782184761941&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌──(kali㉿kali)-[~/&amp;hellip;/day02-cve/vulhub/tomcat/CVE-2025-24813]
└─$ docker run --rm --platform linux/amd64 alpine uname -m
Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
55afa1ecc21d: Pull complete 
Digest: sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
Status: Downloaded newer image for alpine:latest
x86_64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 치고 마지막에 x86_64가 떠야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782202913038&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                                                                             
┌──(kali㉿kali)-[~/&amp;hellip;/day02-cve/vulhub/tomcat/CVE-2025-24813]
└─$ docker-compose config --services
tomcat
                                                                             
┌──(kali㉿kali)-[~/&amp;hellip;/day02-cve/vulhub/tomcat/CVE-2025-24813]
└─$ cat &amp;gt; docker-compose.override.yml &amp;lt;&amp;lt;'EOF'
services:
  tomcat:
    platform: linux/amd64
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 tomcat의 도커 빌드를 하고 있었기에... 확인해줬다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782202940423&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;┌──(kali㉿kali)-[~/&amp;hellip;/day02-cve/vulhub/tomcat/CVE-2025-24813]
└─$ DOCKER_DEFAULT_PLATFORM=linux/amd64 docker-compose up -d
[+] Running 2/2
 ✔ Network cve-2025-24813_default     Created                           0.0s 
 ✔ Container cve-2025-24813-tomcat-1  Started                           0.1s 
                                                                             
┌──(kali㉿kali)-[~/&amp;hellip;/day02-cve/vulhub/tomcat/CVE-2025-24813]
└─$ docker ps
CONTAINER ID   IMAGE                   COMMAND             CREATED         STATUS         PORTS                                         NAMES
bb7abafa329a   cve-2025-24813-tomcat   &quot;catalina.sh run&quot;   6 seconds ago   Up 5 seconds   0.0.0.0:8080-&amp;gt;8080/tcp, [::]:8080-&amp;gt;8080/tcp   cve-2025-24813-tomcat-1
                                                                             
┌──(kali㉿kali)-[~/&amp;hellip;/day02-cve/vulhub/tomcat/CVE-2025-24813]
└─$ curl -i http://127.0.0.1:8080
HTTP/1.1 200 
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 23 Jun 2026 02:13:29 GMT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 docker-compose up -d 로 올리고 docker ps 해보면 tomcat이 잘 돌아가고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;curl 요청 보냈을 대도 200으로 응답이 아주 잘된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1782203076747&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[cve-2025-24813-tomcat-partial-put:dsl-1] [http] [high] http://127.0.0.1:8080/nuclei-local-check-3FWDMNIpG85tq1GsbCvdOXwGhf5.txt
[INF] Scan completed in 75.883294ms. 1 matches found.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nuclei 실습도 잘 진행되었다~&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 그냥 vulhub를 메인 오픈소스로 바꾸기&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번처럼 했는데 Grafana docker 세팅이 계속 죽었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;amd64 Go 바리너리를 QEMU 에뮬레이션으로 돌리면 Go runtime이 죽는다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/rancher-sandbox/rancher-desktop/issues/8606&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/rancher-sandbox/rancher-desktop/issues/8606&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1782203507835&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - rancher-sandbox/rancher-desktop: Container Management and Kubernetes on the Desktop&quot; data-og-description=&quot;Container Management and Kubernetes on the Desktop - rancher-sandbox/rancher-desktop&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/rancher-sandbox/rancher-desktop/issues/8606&quot; data-og-url=&quot;https://github.com/rancher-sandbox/rancher-desktop&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hTdFv/dJMb82eWPDm/j9RGF6qa9ZUoqxJ9zKmLu1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cSLLQq/dJMb9jOwENl/abmn9ZREkkce84ixrdhkG0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bliiuc/dJMb84qipNM/H9N2P65KATTrbixudOISfK/img.png?width=868&amp;amp;height=706&amp;amp;face=0_0_868_706&quot;&gt;&lt;a href=&quot;https://github.com/rancher-sandbox/rancher-desktop/issues/8606&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/rancher-sandbox/rancher-desktop/issues/8606&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hTdFv/dJMb82eWPDm/j9RGF6qa9ZUoqxJ9zKmLu1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/cSLLQq/dJMb9jOwENl/abmn9ZREkkce84ixrdhkG0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bliiuc/dJMb84qipNM/H9N2P65KATTrbixudOISfK/img.png?width=868&amp;amp;height=706&amp;amp;face=0_0_868_706');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - rancher-sandbox/rancher-desktop: Container Management and Kubernetes on the Desktop&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Container Management and Kubernetes on the Desktop - rancher-sandbox/rancher-desktop&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.yml에 있는 vulhub/grafana:버전에서 vulhub 대신 grafana/grafana:버전으로 바꿔주면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Go 런타임이 충돌 나는 이유는 다른 포스팅으로 언젠가.........&amp;nbsp;&lt;/p&gt;</description>
      <category>dev</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/269</guid>
      <comments>https://6kitt-hack.tistory.com/269#entry269comment</comments>
      <pubDate>Tue, 23 Jun 2026 17:42:00 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '카펫' 풀이</title>
      <link>https://6kitt-hack.tistory.com/268</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Leo는 카펫을 사러 갔다가 아래 그림과 같이 중앙에는 노란색으로 칠해져 있고 테두리 1줄은 갈색으로 칠해져 있는 격자 모양 카펫을 봤습니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byMSyj/dJMcabLlNB6/NkFCXU8EAkfhvTcUIGZlWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byMSyj/dJMcabLlNB6/NkFCXU8EAkfhvTcUIGZlWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byMSyj/dJMcabLlNB6/NkFCXU8EAkfhvTcUIGZlWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyMSyj%2FdJMcabLlNB6%2FNkFCXU8EAkfhvTcUIGZlWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;843&quot; height=&quot;636&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Leo는 집으로 돌아와서 아까 본 카펫의 노란색과 갈색으로 색칠된 격자의 개수는 기억했지만, 전체 카펫의 크기는 기억하지 못했습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Leo가 본 카펫에서 갈색 격자의 수 brown, 노란색 격자의 수 yellow가 매개변수로 주어질 때 카펫의 가로, 세로 크기를 순서대로 배열에 담아 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;갈색 격자의 수 brown은 8 이상 5,000 이하인 자연수입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;노란색 격자의 수 yellow는 1 이상 2,000,000 이하인 자연수입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;카펫의 가로 길이는 세로 길이와 같거나, 세로 길이보다 깁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1781109104486&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; solution(int brown, int yellow) {
    vector&amp;lt;int&amp;gt; answer;
    int cnt=brown+yellow; //총 격자 개수 
    for(int x=1;x&amp;lt;cnt;x++){
        int y=0;
        if(cnt%x==0&amp;amp;&amp;amp;x&amp;gt;=y) y=cnt/x;
        else continue;
        if(2*x+2*(y-2)==brown) {answer.push_back(y); answer.push_back(x); break;}
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/268</guid>
      <comments>https://6kitt-hack.tistory.com/268#entry268comment</comments>
      <pubDate>Thu, 11 Jun 2026 01:32:15 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '등굣길' 풀이</title>
      <link>https://6kitt-hack.tistory.com/267</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;계속되는 폭우로 일부 지역이 물에 잠겼습니다. 물에 잠기지 않은 지역을 통해 학교를 가려고 합니다. 집에서 학교까지 가는 길은 m x n 크기의 격자모양으로 나타낼 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;아래 그림은 m = 4, n = 3 인 경우입니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HuVg3/dJMcahdFeRL/zJqwlFyK0xBPbUXr0XoUCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HuVg3/dJMcahdFeRL/zJqwlFyK0xBPbUXr0XoUCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HuVg3/dJMcahdFeRL/zJqwlFyK0xBPbUXr0XoUCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHuVg3%2FdJMcahdFeRL%2FzJqwlFyK0xBPbUXr0XoUCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;400&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;가장 왼쪽 위, 즉 집이 있는 곳의 좌표는 (1, 1)로 나타내고 가장 오른쪽 아래, 즉 학교가 있는 곳의 좌표는 (m, n)으로 나타냅니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;격자의 크기 m, n과 물이 잠긴 지역의 좌표를 담은 2차원 배열 puddles이 매개변수로 주어집니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;오른쪽과 아래쪽으로만 움직여&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;집에서 학교까지 갈 수 있는 최단경로의 개수를 1,000,000,007로 나눈 나머지를 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;격자의 크기 m, n은 1 이상 100 이하인 자연수입니다.
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;m과 n이 모두 1인 경우는 입력으로 주어지지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;물에 잠긴 지역은 0개 이상 10개 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;집과 학교가 물에 잠긴 경우는 입력으로 주어지지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1781030835087&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;



int solution(int m, int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; puddles) {
    int answer = 0;
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(n,vector&amp;lt;int&amp;gt;(m,0)); //(m,n)에는 해당 위치를 가기 위한 이동 횟수 저장 
    
    
    for(auto e:puddles) dp[e[1]-1][e[0]-1]=-1;
    for(int i=0;i&amp;lt;m;i++){
        if(dp[0][i]==-1) break;
        dp[0][i]=1;
        
    }
    for(int i=0;i&amp;lt;n;i++){
        if(dp[i][0]==-1) break;
        dp[i][0]=1;
    }
    for(int i=1;i&amp;lt;n;i++){
        for(int j=1;j&amp;lt;m;j++){
            if(dp[i][j]==-1) continue;
            int top = (dp[i-1][j] == -1) ? 0 : dp[i-1][j];
            int left = (dp[i][j-1] == -1) ? 0 : dp[i][j-1];
            dp[i][j]=(left+top)%1000000007;
        }
    }
    answer=dp[n-1][m-1]%1000000007;
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀다보니 코드가 많이 더러워져서 리팩토링이 필요할 거 같다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 내가 몰랐던 문법 지피티한테 물어본 것들&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;342&quot; data-start=&quot;301&quot; data-section-id=&quot;1atob01&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(n)만 하면 안 됨&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;381&quot; data-start=&quot;344&quot; data-ke-size=&quot;size16&quot;&gt;이건 바깥 vector만 만든 거라 안쪽 vector는 비어 있음.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(n);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;429&quot; data-start=&quot;422&quot; data-ke-size=&quot;size16&quot;&gt;이 상태에서:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;dp[0][0] = 1;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;476&quot; data-start=&quot;457&quot; data-ke-size=&quot;size16&quot;&gt;하면 세그먼트 폴트가 날 수 있음.&lt;/p&gt;
&lt;p data-end=&quot;493&quot; data-start=&quot;478&quot; data-ke-size=&quot;size16&quot;&gt;반드시 안쪽 크기까지 잡기.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; dp(n, vector&amp;lt;int&amp;gt;(m, 0));&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/267</guid>
      <comments>https://6kitt-hack.tistory.com/267#entry267comment</comments>
      <pubDate>Wed, 10 Jun 2026 03:49:16 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '더 맵게' 풀이</title>
      <link>https://6kitt-hack.tistory.com/265</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;매운 것을 좋아하는 Leo는 모든 음식의 스코빌 지수를 K 이상으로 만들고 싶습니다. 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 Leo는 스코빌 지수가 가장 낮은 두 개의 음식을 아래와 같이 특별한 방법으로 섞어 새로운 음식을 만듭니다.&lt;/p&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #e9ecf3; color: #44576c;&quot;&gt;&lt;code&gt;섞은 음식의 스코빌 지수 = 가장 맵지 않은 음식의 스코빌 지수 + (두 번째로 맵지 않은 음식의 스코빌 지수 * 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Leo는 모든 음식의 스코빌 지수가 K 이상이 될 때까지 반복하여 섞습니다.&lt;br /&gt;Leo가 가진 음식의 스코빌 지수를 담은 배열 scoville과 원하는 스코빌 지수 K가 주어질 때, 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 섞어야 하는 최소 횟수를 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한 사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;scoville의 길이는 2 이상 1,000,000 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;K는 0 이상 1,000,000,000 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;scoville의 원소는 각각 0 이상 1,000,000 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;모든 음식의 스코빌 지수를 K 이상으로 만들 수 없는 경우에는 -1을 return 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780942516344&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int solution(vector&amp;lt;int&amp;gt; scoville, int K) {
    int answer = 0;
    priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;
    
    for (int s : scoville) pq.push(s);
    
    while(pq.size()&amp;gt;=2 &amp;amp;&amp;amp; pq.top()&amp;lt;K){
        int min1=pq.top(); pq.pop();
        int min2=pq.top(); pq.pop();
        pq.push(min1+min2*2);
        answer++;
    }
    if(pq.top()&amp;lt;K) return -1;
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;priority_queue&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;vector&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;greater&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pq&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;heap 까다롭군..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글은 지피티한테 첨삭 받은 내용&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 data-end=&quot;204&quot; data-start=&quot;187&quot; data-section-id=&quot;1oe6yvd&quot;&gt;1. heap이 필요한 이유&lt;/h1&gt;
&lt;p data-end=&quot;221&quot; data-start=&quot;206&quot; data-ke-size=&quot;size16&quot;&gt;스코빌 문제 로직은 이거야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 가장 안 매운 음식 min1 꺼냄
2. 두 번째로 안 매운 음식 min2 꺼냄
3. 새 음식 = min1 + min2 * 2
4. 다시 넣음
5. 모든 음식이 K 이상이 될 때까지 반복&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;354&quot; data-start=&quot;343&quot; data-ke-size=&quot;size16&quot;&gt;즉 매번 필요한 건:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;현재 남은 값 중 최솟값&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;387&quot; data-start=&quot;383&quot; data-ke-size=&quot;size16&quot;&gt;이거야.&lt;/p&gt;
&lt;p data-end=&quot;419&quot; data-start=&quot;389&quot; data-ke-size=&quot;size16&quot;&gt;그런데 매번 sort()를 하면 너무 비효율적이야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;sort(scoville.begin(), scoville.end());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;496&quot; data-start=&quot;473&quot; data-ke-size=&quot;size16&quot;&gt;이걸 반복하면 매번 전체 정렬이라 느려져.&lt;/p&gt;
&lt;p data-end=&quot;511&quot; data-start=&quot;498&quot; data-ke-size=&quot;size16&quot;&gt;그래서 heap을 쓰면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;최솟값 확인: O(1)
최솟값 꺼내기: O(log N)
새 값 넣기: O(log N)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;586&quot; data-start=&quot;574&quot; data-ke-size=&quot;size16&quot;&gt;으로 처리할 수 있어.&lt;/p&gt;
&lt;hr data-end=&quot;591&quot; data-start=&quot;588&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;620&quot; data-start=&quot;593&quot; data-section-id=&quot;11a6j8n&quot;&gt;2. C++ heap STL은 두 종류가 있음&lt;/h1&gt;
&lt;p data-end=&quot;650&quot; data-start=&quot;622&quot; data-ke-size=&quot;size16&quot;&gt;C++에서 heap을 쓰는 방법은 크게 두 가지야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;1. priority_queue
2. vector + make_heap / push_heap / pop_heap&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;811&quot; data-start=&quot;728&quot; data-ke-size=&quot;size16&quot;&gt;코테에서는 보통 &lt;b&gt;priority_queue를 추천&lt;/b&gt;해.&lt;br /&gt;make_heap 계열은 내부 동작을 직접 다루는 느낌이라 처음엔 더 헷갈려.&lt;/p&gt;
&lt;hr data-end=&quot;816&quot; data-start=&quot;813&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;853&quot; data-start=&quot;818&quot; data-section-id=&quot;1v9v7hx&quot;&gt;&lt;span&gt;3. priority_queue로 min heap 만들기&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;896&quot; data-start=&quot;855&quot; data-ke-size=&quot;size16&quot;&gt;기본 priority_queue&amp;lt;int&amp;gt;는 &lt;b&gt;max heap&lt;/b&gt;이야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;priority_queue&amp;lt;int&amp;gt; pq;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;945&quot; data-start=&quot;934&quot; data-ke-size=&quot;size16&quot;&gt;큰 값이 먼저 나와.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;pq.push(1);
pq.push(5);
pq.push(3);

cout &amp;lt;&amp;lt; pq.top(); // 5&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1057&quot; data-start=&quot;1019&quot; data-ke-size=&quot;size16&quot;&gt;그런데 스코빌 문제는 작은 값이 먼저 나와야 해. 그래서 이렇게 써.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1140&quot; data-start=&quot;1122&quot; data-ke-size=&quot;size16&quot;&gt;이게 &lt;b&gt;min heap&lt;/b&gt;이야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;

pq.push(1);
pq.push(5);
pq.push(3);

cout &amp;lt;&amp;lt; pq.top(); // 1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1269&quot; data-start=&quot;1266&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;1299&quot; data-start=&quot;1271&quot; data-section-id=&quot;1syfsrg&quot;&gt;&lt;span&gt;4. priority_queue 주요 메서드&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;1325&quot; data-start=&quot;1301&quot; data-ke-size=&quot;size16&quot;&gt;스코빌 문제에서 필요한 건 딱 이것들뿐이야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;메서드의미
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1465&quot; data-start=&quot;1327&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;1465&quot; data-start=&quot;1350&quot;&gt;
&lt;tr data-end=&quot;1370&quot; data-start=&quot;1350&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1362&quot; data-start=&quot;1350&quot;&gt;push(x)&lt;/td&gt;
&lt;td data-end=&quot;1370&quot; data-start=&quot;1362&quot; data-col-size=&quot;sm&quot;&gt;값 넣기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1394&quot; data-start=&quot;1371&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1381&quot; data-start=&quot;1371&quot;&gt;top()&lt;/td&gt;
&lt;td data-end=&quot;1394&quot; data-start=&quot;1381&quot; data-col-size=&quot;sm&quot;&gt;현재 최솟값 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1418&quot; data-start=&quot;1395&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1405&quot; data-start=&quot;1395&quot;&gt;pop()&lt;/td&gt;
&lt;td data-end=&quot;1418&quot; data-start=&quot;1405&quot; data-col-size=&quot;sm&quot;&gt;현재 최솟값 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1439&quot; data-start=&quot;1419&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1430&quot; data-start=&quot;1419&quot;&gt;size()&lt;/td&gt;
&lt;td data-end=&quot;1439&quot; data-start=&quot;1430&quot; data-col-size=&quot;sm&quot;&gt;원소 개수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1465&quot; data-start=&quot;1440&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1452&quot; data-start=&quot;1440&quot;&gt;empty()&lt;/td&gt;
&lt;td data-end=&quot;1465&quot; data-start=&quot;1452&quot; data-col-size=&quot;sm&quot;&gt;비어 있는지 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1473&quot; data-start=&quot;1467&quot; data-ke-size=&quot;size16&quot;&gt;주의할 점:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;pq.pop();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1510&quot; data-start=&quot;1497&quot; data-ke-size=&quot;size16&quot;&gt;은 값을 반환하지 않아.&lt;/p&gt;
&lt;p data-end=&quot;1536&quot; data-start=&quot;1512&quot; data-ke-size=&quot;size16&quot;&gt;그래서 최솟값을 꺼낼 때는 이렇게 해야 해.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;int min1 = pq.top();
pq.pop();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1584&quot; data-start=&quot;1581&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;1603&quot; data-start=&quot;1586&quot; data-section-id=&quot;rt0b09&quot;&gt;5. 스코빌 문제 정답 구조&lt;/h1&gt;
&lt;p data-end=&quot;1626&quot; data-start=&quot;1605&quot; data-ke-size=&quot;size16&quot;&gt;이 문제는 이 패턴 그대로 외워도 돼.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int solution(vector&amp;lt;int&amp;gt; scoville, int K) {
    int answer = 0;

    priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;

    for (int s : scoville) {
        pq.push(s);
    }

    while (pq.size() &amp;gt;= 2 &amp;amp;&amp;amp; pq.top() &amp;lt; K) {
        int min1 = pq.top();
        pq.pop();

        int min2 = pq.top();
        pq.pop();

        int mixed = min1 + min2 * 2;

        pq.push(mixed);
        answer++;
    }

    if (pq.top() &amp;lt; K) {
        return -1;
    }

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2220&quot; data-start=&quot;2217&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;2251&quot; data-start=&quot;2222&quot; data-section-id=&quot;1ap6qoz&quot;&gt;&lt;span&gt;6. 왜 pq.size() &amp;gt;= 2가 필요해?&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;2279&quot; data-start=&quot;2253&quot; data-ke-size=&quot;size16&quot;&gt;두 개를 섞어야 하니까 최소 2개가 있어야 해.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;while (pq.size() &amp;gt;= 2 &amp;amp;&amp;amp; pq.top() &amp;lt; K)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2345&quot; data-start=&quot;2332&quot; data-ke-size=&quot;size16&quot;&gt;이 조건은 이런 뜻이야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;아직 제일 작은 음식이 K보다 작고,
섞을 음식이 최소 2개 이상 있으면 계속 섞는다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2447&quot; data-start=&quot;2409&quot; data-ke-size=&quot;size16&quot;&gt;만약 음식이 1개만 남았는데도 K보다 작으면 더 이상 섞을 수 없어.&lt;/p&gt;
&lt;p data-end=&quot;2462&quot; data-start=&quot;2449&quot; data-ke-size=&quot;size16&quot;&gt;그래서 마지막에 확인해.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;if (pq.top() &amp;lt; K) return -1;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2508&quot; data-start=&quot;2505&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;2536&quot; data-start=&quot;2510&quot; data-section-id=&quot;1m6yjsy&quot;&gt;&lt;span&gt;7. 네가 쓰려던 make_heap 방식&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;2553&quot; data-start=&quot;2538&quot; data-ke-size=&quot;size16&quot;&gt;네가 쓴 건 이 방식이었어.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;make_heap(scoville.begin(), scoville.end(), greater&amp;lt;int&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2660&quot; data-start=&quot;2628&quot; data-ke-size=&quot;size16&quot;&gt;이건 vector 자체를 heap 구조로 만드는 거야.&lt;/p&gt;
&lt;p data-end=&quot;2702&quot; data-start=&quot;2662&quot; data-ke-size=&quot;size16&quot;&gt;greater&amp;lt;int&amp;gt;()를 넣었으니까 &lt;b&gt;min heap&lt;/b&gt;이 돼.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;make_heap(scoville.begin(), scoville.end(), greater&amp;lt;int&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2781&quot; data-start=&quot;2777&quot; data-ke-size=&quot;size16&quot;&gt;그러면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;scoville.front()&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2821&quot; data-start=&quot;2812&quot; data-ke-size=&quot;size16&quot;&gt;에 최솟값이 와.&lt;/p&gt;
&lt;p data-end=&quot;2844&quot; data-start=&quot;2823&quot; data-ke-size=&quot;size16&quot;&gt;하지만 여기서 바로 이걸 하면 안 돼.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;sort_heap(scoville.begin(), scoville.end(), greater&amp;lt;int&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2981&quot; data-start=&quot;2919&quot; data-ke-size=&quot;size16&quot;&gt;왜냐면 sort_heap은 heap을 유지하는 함수가 아니라, &lt;b&gt;heap을 정렬 배열로 끝내는 함수&lt;/b&gt;야.&lt;/p&gt;
&lt;p data-end=&quot;3033&quot; data-start=&quot;2983&quot; data-ke-size=&quot;size16&quot;&gt;즉 이 문제처럼 계속 최소값 꺼내고 새 값 넣는 상황에서는 sort_heap이 아니라:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;pop_heap
push_heap&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3073&quot; data-start=&quot;3066&quot; data-ke-size=&quot;size16&quot;&gt;을 써야 해.&lt;/p&gt;
&lt;hr data-end=&quot;3078&quot; data-start=&quot;3075&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;3109&quot; data-start=&quot;3080&quot; data-section-id=&quot;qn2k7y&quot;&gt;&lt;span&gt;8. make_heap 계열로 풀면 이렇게 됨&lt;/span&gt;&lt;/h1&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int solution(vector&amp;lt;int&amp;gt; scoville, int K) {
    int answer = 0;

    make_heap(scoville.begin(), scoville.end(), greater&amp;lt;int&amp;gt;());

    while (scoville.size() &amp;gt;= 2 &amp;amp;&amp;amp; scoville.front() &amp;lt; K) {
        pop_heap(scoville.begin(), scoville.end(), greater&amp;lt;int&amp;gt;());
        int min1 = scoville.back();
        scoville.pop_back();

        pop_heap(scoville.begin(), scoville.end(), greater&amp;lt;int&amp;gt;());
        int min2 = scoville.back();
        scoville.pop_back();

        int mixed = min1 + min2 * 2;

        scoville.push_back(mixed);
        push_heap(scoville.begin(), scoville.end(), greater&amp;lt;int&amp;gt;());

        answer++;
    }

    if (scoville.front() &amp;lt; K) {
        return -1;
    }

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3967&quot; data-start=&quot;3912&quot; data-ke-size=&quot;size16&quot;&gt;근데 솔직히 처음 heap 쓰는 단계에서는 이 방식보다 priority_queue가 훨씬 편해.&lt;/p&gt;
&lt;hr data-end=&quot;3972&quot; data-start=&quot;3969&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;3998&quot; data-start=&quot;3974&quot; data-section-id=&quot;2hkpi2&quot;&gt;&lt;span&gt;9. pop_heap이 헷갈리는 이유&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;4010&quot; data-start=&quot;4000&quot; data-ke-size=&quot;size16&quot;&gt;이게 제일 중요해.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;pop_heap(v.begin(), v.end(), greater&amp;lt;int&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4092&quot; data-start=&quot;4070&quot; data-ke-size=&quot;size16&quot;&gt;이 함수는 최솟값을 바로 삭제하지 않아.&lt;/p&gt;
&lt;p data-end=&quot;4097&quot; data-start=&quot;4094&quot; data-ke-size=&quot;size16&quot;&gt;대신:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;front에 있던 최솟값을 back으로 이동시킴&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4156&quot; data-start=&quot;4139&quot; data-ke-size=&quot;size16&quot;&gt;그래서 반드시 이렇게 써야 해.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;pop_heap(v.begin(), v.end(), greater&amp;lt;int&amp;gt;());
int minValue = v.back();
v.pop_back();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4310&quot; data-start=&quot;4255&quot; data-ke-size=&quot;size16&quot;&gt;즉 pop_heap 이름만 보면 삭제 같지만, 실제 삭제는 pop_back()이 하는 거야.&lt;/p&gt;
&lt;hr data-end=&quot;4315&quot; data-start=&quot;4312&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;4340&quot; data-start=&quot;4317&quot; data-section-id=&quot;1k9q7x0&quot;&gt;&lt;span&gt;10. push_heap도 마찬가지&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;4386&quot; data-start=&quot;4342&quot; data-ke-size=&quot;size16&quot;&gt;새 값을 heap에 넣을 때도 push_heap이 직접 값을 넣어주지 않아.&lt;/p&gt;
&lt;p data-end=&quot;4410&quot; data-start=&quot;4388&quot; data-ke-size=&quot;size16&quot;&gt;먼저 vector에 값을 넣어야 해.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;v.push_back(mixed);
push_heap(v.begin(), v.end(), greater&amp;lt;int&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4499&quot; data-start=&quot;4491&quot; data-ke-size=&quot;size16&quot;&gt;순서가 중요해.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;v.push_back(새값);
push_heap(...);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;4549&quot; data-start=&quot;4546&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;4579&quot; data-start=&quot;4551&quot; data-section-id=&quot;nf60z2&quot;&gt;&lt;span&gt;11. sort_heap은 언제 쓰는 거냐?&lt;/span&gt;&lt;/h1&gt;
&lt;p data-end=&quot;4623&quot; data-start=&quot;4581&quot; data-ke-size=&quot;size16&quot;&gt;sort_heap은 heap을 다 쓰고 나서 정렬 결과가 필요할 때 써.&lt;/p&gt;
&lt;p data-end=&quot;4631&quot; data-start=&quot;4625&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; v = {3, 1, 5, 2};

make_heap(v.begin(), v.end());

sort_heap(v.begin(), v.end());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4749&quot; data-start=&quot;4739&quot; data-ke-size=&quot;size16&quot;&gt;그러면 최종적으로:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1 2 3 5&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4779&quot; data-start=&quot;4772&quot; data-ke-size=&quot;size16&quot;&gt;처럼 정렬돼.&lt;/p&gt;
&lt;p data-end=&quot;4813&quot; data-start=&quot;4781&quot; data-ke-size=&quot;size16&quot;&gt;하지만 스코빌 문제에서는 정렬된 배열이 필요한 게 아니야.&lt;/p&gt;
&lt;p data-end=&quot;4824&quot; data-start=&quot;4815&quot; data-ke-size=&quot;size16&quot;&gt;필요한 건 매번:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;최솟값 2개 꺼내기
새 값 넣기&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4883&quot; data-start=&quot;4857&quot; data-ke-size=&quot;size16&quot;&gt;이거라서 sort_heap은 쓰면 안 맞아.&lt;/p&gt;
&lt;hr data-end=&quot;4888&quot; data-start=&quot;4885&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;4913&quot; data-start=&quot;4890&quot; data-section-id=&quot;lqm6v&quot;&gt;12. 이 문제에서 외울 heap 패턴&lt;/h1&gt;
&lt;h2 data-end=&quot;4937&quot; data-start=&quot;4915&quot; data-section-id=&quot;xeoyhl&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;priority_queue 버전&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;

for (int x : arr) {
    pq.push(x);
}

while (pq.size() &amp;gt;= 2 &amp;amp;&amp;amp; pq.top() &amp;lt; K) {
    int a = pq.top(); pq.pop();
    int b = pq.top(); pq.pop();

    pq.push(a + b * 2);
    answer++;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5207&quot; data-start=&quot;5188&quot; data-ke-size=&quot;size16&quot;&gt;이게 스코빌 문제의 핵심 패턴이야.&lt;/p&gt;
&lt;hr data-end=&quot;5212&quot; data-start=&quot;5209&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5231&quot; data-start=&quot;5214&quot; data-section-id=&quot;16yx4bo&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;make_heap 버전&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;make_heap(v.begin(), v.end(), greater&amp;lt;int&amp;gt;());

pop_heap(v.begin(), v.end(), greater&amp;lt;int&amp;gt;());
int a = v.back();
v.pop_back();

v.push_back(x);
push_heap(v.begin(), v.end(), greater&amp;lt;int&amp;gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5473&quot; data-start=&quot;5435&quot; data-ke-size=&quot;size16&quot;&gt;이건 나중에 vector를 직접 heap처럼 다뤄야 할 때 보면 돼.&lt;/p&gt;
&lt;hr data-end=&quot;5478&quot; data-start=&quot;5475&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;5484&quot; data-start=&quot;5480&quot; data-section-id=&quot;yi4awm&quot;&gt;결론&lt;/h1&gt;
&lt;p data-end=&quot;5508&quot; data-start=&quot;5486&quot; data-ke-size=&quot;size16&quot;&gt;이 문제에서는 이걸 쓰는 게 제일 좋음.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5581&quot; data-start=&quot;5573&quot; data-ke-size=&quot;size16&quot;&gt;그리고 흐름은:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;pq.top()으로 최솟값 확인
pq.pop()으로 제거
pq.push()로 새 스코빌 삽입&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;5770&quot; data-start=&quot;5648&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 make_heap, pop_heap, push_heap, sort_heap까지 다 외우려고 하지 말고, 코테에서는 일단 &lt;b&gt;priority_queue min heap&lt;/b&gt;부터 확실히 잡으면 돼.&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/265</guid>
      <comments>https://6kitt-hack.tistory.com/265#entry265comment</comments>
      <pubDate>Tue, 9 Jun 2026 03:17:58 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '입국심사' 풀이</title>
      <link>https://6kitt-hack.tistory.com/264</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;n명이 입국심사를 위해 줄을 서서 기다리고 있습니다. 각 입국심사대에 있는 심사관마다 심사하는데 걸리는 시간은 다릅니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;처음에 모든 심사대는 비어있습니다. 한 심사대에서는 동시에 한 명만 심사를 할 수 있습니다. 가장 앞에 서 있는 사람은 비어 있는 심사대로 가서 심사를 받을 수 있습니다. 하지만 더 빨리 끝나는 심사대가 있으면 기다렸다가 그곳으로 가서 심사를 받을 수도 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;모든 사람이 심사를 받는데 걸리는 시간을 최소로 하고 싶습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;입국심사를 기다리는 사람 수 n, 각 심사관이 한 명을 심사하는데 걸리는 시간이 담긴 배열 times가 매개변수로 주어질 때, 모든 사람이 심사를 받는데 걸리는 시간의 최솟값을 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;입국심사를 기다리는 사람은 1명 이상 1,000,000,000명 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;각 심사관이 한 명을 심사하는데 걸리는 시간은 1분 이상 1,000,000,000분 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;심사관은 1명 이상 100,000명 이하입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780872639075&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

vector&amp;lt;long long&amp;gt; v;

long long sol(int st,int en,int n, vector&amp;lt;int&amp;gt; times){
    while(1){
        int mid=(st+en)/2;
        int tmp=0;
        for(int e:times) tmp+=v[mid]/e;
        if(tmp&amp;gt;n){
            en=mid-1;
        }else if(tmp&amp;lt;n){
            //tmp보다 n이 더 큰 거니까 
            st=mid+1;
        }else{
            //tmp==n
            return v[mid];
        }
    }
}

long long solution(int n, vector&amp;lt;int&amp;gt; times) {
    long long answer = 0;
    //음 뭐지 
    int time=0;
    //7,14,21,28... 
    //10,20,30,40...
    //7,10,14,20,21,30,
    
    for(int i=1;i&amp;lt;=n;i++){
        for(auto e:times) v.push_back(e*i);
    }
    sort(v.begin(),v.end());
    answer=sol(0,v.size()-1,n, times);
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속 시간 초과 뜸&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780874510814&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

long long solution(int n, vector&amp;lt;int&amp;gt; times) {
    long long answer = 0;
    sort(times.begin(),times.end());
    //st,en을 아예 시간으로 두기 
    long long st=1;
    long long en=(long long)times.back()*n;
    while(st&amp;lt;=en){
        long long mid=(st+en)/2;
        long long tmp=0;
        for(int e:times) tmp+=mid/e;
        if(tmp&amp;gt;=n){
            //n tmp 
            en=mid-1;
        }else if(tmp&amp;lt;n){
            //tmp n 
            st=mid+1;
        }
    }
    answer=st;
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vector에 새로 넣지 말고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아예 times를 sort하고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;times.back()*n으로 최댓값 두고 binary_search 진행&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/264</guid>
      <comments>https://6kitt-hack.tistory.com/264#entry264comment</comments>
      <pubDate>Mon, 8 Jun 2026 08:22:44 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '올바른 괄호' 풀이</title>
      <link>https://6kitt-hack.tistory.com/262</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;괄호가 바르게 짝지어졌다는 것은 '(' 문자로 열렸으면 반드시 짝지어서 ')' 문자로 닫혀야 한다는 뜻입니다. 예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&quot;()()&quot; 또는 &quot;(())()&quot; 는 올바른 괄호입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;&quot;)()(&quot; 또는 &quot;(()(&quot; 는 올바르지 않은 괄호입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;'(' 또는 ')' 로만 이루어진 문자열 s가 주어졌을 때, 문자열 s가 올바른 괄호이면 true를 return 하고, 올바르지 않은 괄호이면 false를 return 하는 solution 함수를 완성해 주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;문자열 s의 길이 : 100,000 이하의 자연수&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;문자열 s는 '(' 또는 ')' 로만 이루어져 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780847871721&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include&amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

bool solution(string s)
{
    bool answer = true;

    stack&amp;lt;char&amp;gt; sts;
    if(s[0]==')') return false;
    
    for(auto e:s){
        if(e=='(') sts.push(e);
        else if(e==')'){
            char tmp=sts.top(); sts.pop();
            if(tmp!='(') {return false;}
        }
    }
    if(!sts.empty()) return false;

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 작성했던 답안&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자꾸 테스트 하나만 틀리길래 뭘까 했는데&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780847848326&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include&amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

bool solution(string s)
{
    bool answer = true;

    stack&amp;lt;char&amp;gt; sts;
    if(s[0]==')') return false;
    
    for(auto e:s){
        if(e=='(') sts.push(e);
        else if(e==')'){
            if(sts.empty()) return false;
            else sts.pop();
        }
    }
    if(!sts.empty()) return false;

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stack이 비어있는데 pop하게 되면 오류&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 굳이 top으로 스택 값 검증할 필요는 없었음 -&amp;gt; 그냥 pop만 하고 나중에 다 비었는지만 확인&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/262</guid>
      <comments>https://6kitt-hack.tistory.com/262#entry262comment</comments>
      <pubDate>Mon, 8 Jun 2026 00:59:00 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '체육복' 풀이</title>
      <link>https://6kitt-hack.tistory.com/261</link>
      <description>&lt;pre id=&quot;code_1780658296947&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt; 

using namespace std;

int solution(int n, vector&amp;lt;int&amp;gt; lost, vector&amp;lt;int&amp;gt; reserve) {
    int answer = 0;
    set&amp;lt;int&amp;gt; s;
    //소거법으로 없애는 게 좋을듯 
    for(int i=1;i&amp;lt;=n;i++) s.insert(i);
    for(int e:lost) s.erase(e);
    for(int e:reserve){ 
        //뒷 친구 없을 때 
        if(!s.count(e+1) &amp;amp;&amp;amp; e!=n) s.insert(e+1);
        //본인 없을 때 
        else if(!s.count(e)) s.insert(e);
        //앞 친구 없을 때 
        else if(!s.count(e-1)&amp;amp;&amp;amp;e!=1) s.insert(e-1);
    }
    answer=s.size();
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자꾸 정확성 83임ㅜㅜ 어렵고도 먼 코테의 길,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 벡터는 다른 자료구조로 옮기는 게 좋을듯 특히 find 쓸 때 인자 넣기 너무 불편&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780659154625&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt; 

using namespace std;

int solution(int n, vector&amp;lt;int&amp;gt; lost, vector&amp;lt;int&amp;gt; reserve) {
    int answer = 0;
    set&amp;lt;int&amp;gt; s;
    set&amp;lt;int&amp;gt; lostset(lost.begin(),lost.end());
    set&amp;lt;int&amp;gt; reserveset(reserve.begin(),reserve.end());
    //소거법으로 없애는 게 좋을듯 
    for(int i=1;i&amp;lt;=n;i++) s.insert(i);
    for(int e:lostset) { 
        if(reserveset.count(e)){
            //본인 게 없으면 
            reserveset.erase(e);
        }else{
            s.erase(e);
        }
    }
    for(int e:reserveset){ 
        //앞 친구 없을 때 
        if(!s.count(e-1)&amp;amp;&amp;amp;e!=1) s.insert(e-1);
        //뒷 친구 없을 때 
        else if(!s.count(e+1) &amp;amp;&amp;amp; e!=n) s.insert(e+1);
    }
    answer=s.size();
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정한 부분&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; erase, reserve를 reserve 탐색하기 전 중복 다 제거&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 오름차순에서 앞친구 먼저 그리디 해야 누락이 없음&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/261</guid>
      <comments>https://6kitt-hack.tistory.com/261#entry261comment</comments>
      <pubDate>Sat, 6 Jun 2026 21:33:38 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '정수 삼각형' 풀이</title>
      <link>https://6kitt-hack.tistory.com/260</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxfFFC/dJMcaaMh01w/fF8FZcYmLOOmfrU2RrfNNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxfFFC/dJMcaaMh01w/fF8FZcYmLOOmfrU2RrfNNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxfFFC/dJMcaaMh01w/fF8FZcYmLOOmfrU2RrfNNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxfFFC%2FdJMcaaMh01w%2FfF8FZcYmLOOmfrU2RrfNNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;368&quot; height=&quot;300&quot; data-origin-width=&quot;368&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같은 삼각형의 꼭대기에서 바닥까지 이어지는 경로 중, 거쳐간 숫자의 합이 가장 큰 경우를 찾아보려고 합니다. 아래 칸으로 이동할 때는 대각선 방향으로 한 칸 오른쪽 또는 왼쪽으로만 이동 가능합니다. 예를 들어 3에서는 그 아래칸의 8 또는 1로만 이동이 가능합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;삼각형의 정보가 담긴 배열 triangle이 매개변수로 주어질 때, 거쳐간 숫자의 최댓값을 return 하도록 solution 함수를 완성하세요.&lt;/p&gt;
&lt;b&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;삼각형의 높이는 1 이상 500 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;삼각형을 이루고 있는 숫자는 0 이상 9,999 이하의 정수입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780594302394&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt; 

using namespace std;

int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; triangle) {
    int answer = 0;
    /* 0/0,1/0,1//1,2/0,1 1,2 2,3 3,4*/
    int siz=triangle.size();
    vector&amp;lt;int&amp;gt; candidate(siz);
    for(int i=0;i&amp;lt;siz;i++){
        //바깥 행 
        for(int j=0;j&amp;lt;triangle[i].size();j++){
            // 해당 triangle[i][j]를 어디부터 어디까지 넣을 것인지 
            int cnt=siz-i;
            for(int k=j;cnt&amp;gt;0;cnt--) {candidate[k]+=triangle[i][j]; k++;}
        }
    }
    for(int e:candidate) { if(e&amp;gt;answer) answer=e; }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;dp 문제&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐지.. 왜 결괏값이 다를까&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무슨 조건을 빼먹었을까&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아 생각났다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 저 식은 좀 겹쳐져 있도록 계산됨(?)&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780595481088&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt; 

using namespace std;

int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; triangle) {
    int answer = 0;
    /* 0/0,1/0,1//1,2/0,1 1,2 2,3 3,4*/
    int siz=triangle.size();
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; candidate(siz);
    candidate[0].push_back(triangle[0][0]);
    for(int i=1;i&amp;lt;siz;i++){
        //바깥 행 
        for(int j=0;j&amp;lt;triangle[i].size();j++){
            // 윗줄에 있는 요소들 하나씩 꺼내서 현재 요소 더해준 다음 insert 
            if(j!=0) candidate[i].push_back(candidate[i-1][j-1]+triangle[i][j]);
            candidate[i].push_back(candidate[i-1][j]+triangle[i][j]);
        }
    }
    for(int e:candidate[siz-1]) { if(e&amp;gt;answer) answer=e; }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딱 문제에 준 TC만 통과함..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅜㅜ 또 왜일까&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1780596445284&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt; 

using namespace std;

int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; triangle) {
    ios::sync_with_stdio(0);
    int answer = 0;
    /* 0/0,1/0,1//1,2/0,1 1,2 2,3 3,4*/
    int siz=triangle.size();
    vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; candidate(siz);
    candidate[0].push_back(triangle[0][0]);
    for(int i=1;i&amp;lt;siz;i++){
        //바깥 행 
        for(int j=0;j&amp;lt;triangle[i].size();j++){
            // 윗줄에 있는 요소들 하나씩 꺼내서 현재 요소 더해준 다음 insert 
            if(j==0){
                //첫번째 항일 때는 
                candidate[i].push_back(candidate[i-1][j]+triangle[i][j]);
            }else if(j==triangle[i].size()-1){
                candidate[i].push_back(candidate[i-1][j-1]+triangle[i][j]);
            }else{
                int tmp=(candidate[i-1][j-1]&amp;gt; candidate[i-1][j])? candidate[i-1][j-1] : candidate[i-1][j];
                candidate[i].push_back(tmp+triangle[i][j]);
            }
        }
    }
    for(int e:candidate[siz-1]) { if(e&amp;gt;answer) answer=e; }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음 세 가지 경우의 수 (가장 왼쪽에 있을 때, 가장 오른쪽에 있을 때, 둘 다 아닐 때)를 생각해서 각각마다의 최댓값만 저장한다는 점이 다르다.&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/260</guid>
      <comments>https://6kitt-hack.tistory.com/260#entry260comment</comments>
      <pubDate>Fri, 5 Jun 2026 03:09:33 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '모의고사' 풀이</title>
      <link>https://6kitt-hack.tistory.com/258</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...&lt;br /&gt;2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...&lt;br /&gt;3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한 조건&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;시험은 최대 10,000 문제로 구성되어있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;문제의 정답은 1, 2, 3, 4, 5중 하나입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;가장 높은 점수를 받은 사람이 여럿일 경우, return하는 값을 오름차순 정렬해주세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780504669883&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int aomr[5]={1,2,3,4,5};
int bomr[8]={2,1,2,3,2,4,2,5};
int comr[10]={3,3,1,1,2,2,4,4,5,5};

vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; answers) {
    vector&amp;lt;int&amp;gt; answer;
    int a=0,b=0,c=0;
    for(int cnt=0;cnt&amp;lt;answers.size();cnt++){
        if(answers[cnt]==aomr[cnt%5]) a++;
        if(answers[cnt]==bomr[cnt%8]) b++;
        if(answers[cnt]==comr[cnt%10]) c++;
    }
    int maxcnt=max(max(a,b),c);
    if(a==maxcnt) answer.push_back(1);
    if(b==maxcnt) answer.push_back(2);
    if(c==maxcnt) answer.push_back(3);
    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/258</guid>
      <comments>https://6kitt-hack.tistory.com/258#entry258comment</comments>
      <pubDate>Thu, 4 Jun 2026 01:38:12 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 'N으로 표현' 풀이</title>
      <link>https://6kitt-hack.tistory.com/257</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;아래와 같이 5와 사칙연산만으로 12를 표현할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;12 = 5 + 5 + (5 / 5) + (5 / 5)&lt;br /&gt;12 = 55 / 5 + 5 / 5&lt;br /&gt;12 = (55 + 5) / 5&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;5를 사용한 횟수는 각각 6,5,4 입니다. 그리고 이중 가장 작은 경우는 4입니다.&lt;br /&gt;이처럼 숫자 N과 number가 주어질 때, N과 사칙연산만 사용해서 표현 할 수 있는 방법 중 N 사용횟수의 최솟값을 return 하도록 solution 함수를 작성하세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;N은 1 이상 9 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;number는 1 이상 32,000 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;수식에는 괄호와 사칙연산만 가능하며 나누기 연산에서 나머지는 무시합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;최솟값이 8보다 크면 -1을 return 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780503547626&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int solution(int N, int number) {
    int answer = 0;
    vector&amp;lt;set&amp;lt;int&amp;gt;&amp;gt; dp(9); //insert 쓰려면 크기 미리 지정해야함 
    for(int i=1;i&amp;lt;9;i++){
        int tmp=0;
        for(int j=0;j&amp;lt;i;j++) tmp=tmp*10+N; //N이 여러개 나오는 거 저장 
        dp[i].insert(tmp); //tmp는 N i개 쓰임 
        
        for(int j=1;j&amp;lt;i;j++){
            int left=j;
            int rigt=i-j;
            //i랑 j랑 쪼개어서 
            for(int x: dp[left]){
                for(int y:dp[rigt]){
                    dp[i].insert(x+y);
                    dp[i].insert(x-y);
                    dp[i].insert(x*y);
                    if(y!=0 &amp;amp;&amp;amp; x%y==0) dp[i].insert(x/y);
                    //사칙연산의 값은 i번째에 나옴 
                }
            }
        }
        
        if(dp[i].count(number)) return i; //number가 있다면 우리가 0부터 점증적으로 검사중이니 지금이 최소값 
    }
    
    return -1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP는 처음인데 풀다가 지피티 도움 받았다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으음.. 테이블을 잘 선정하기 위해서 생각을 좀 해봐야할 거 같다.&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/257</guid>
      <comments>https://6kitt-hack.tistory.com/257#entry257comment</comments>
      <pubDate>Thu, 4 Jun 2026 01:20:24 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 'H-Index' 풀이</title>
      <link>https://6kitt-hack.tistory.com/256</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;H-Index는 과학자의 생산성과 영향력을 나타내는 지표입니다. 어느 과학자의 H-Index를 나타내는 값인 h를 구하려고 합니다. 위키백과&lt;a style=&quot;color: #0078ff;&quot; href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42747#fn1&quot;&gt;1&lt;/a&gt;에 따르면, H-Index는 다음과 같이 구합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 과학자가 발표한 논문&lt;span&gt;&amp;nbsp;&lt;/span&gt;n편 중,&lt;span&gt;&amp;nbsp;&lt;/span&gt;h번 이상 인용된 논문이&lt;span&gt;&amp;nbsp;&lt;/span&gt;h편 이상이고 나머지 논문이 h번 이하 인용되었다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;h의 최댓값이 이 과학자의 H-Index입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;어떤 과학자가 발표한 논문의 인용 횟수를 담은 배열 citations가 매개변수로 주어질 때, 이 과학자의 H-Index를 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;과학자가 발표한 논문의 수는 1편 이상 1,000편 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;논문별 인용 횟수는 0회 이상 10,000회 이하입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780370034644&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int solution(vector&amp;lt;int&amp;gt; citations) {
    int answer = 0;
    int n=citations.size();
    sort(citations.rbegin(),citations.rend()); //정렬 내림차순(큰수부터)
    //65310 
    //여기서 항의 왼쪽, 오른쪽 개수 비교 
    for(int h=citations[0];h&amp;gt;0;h--){
        int idx=0;
        for(int paper:citations) if(paper&amp;gt;=h) idx++; else break;
        if(idx&amp;gt;=h) return h;
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 뭐지.. 내가 뭔 TC를 생각 못한거지 했는데 AI 안 쓰고 해결해서 뿌듯함 ㅎ&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/256</guid>
      <comments>https://6kitt-hack.tistory.com/256#entry256comment</comments>
      <pubDate>Tue, 2 Jun 2026 12:15:17 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '가장 큰 수' 풀이</title>
      <link>https://6kitt-hack.tistory.com/255</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고, 이중 가장 큰 수는 6210입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;0 또는 양의 정수가 담긴 배열 numbers가 매개변수로 주어질 때, 순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한 사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;numbers의 길이는 1 이상 100,000 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;numbers의 원소는 0 이상 1,000 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;정답이 너무 클 수 있으니 문자열로 바꾸어 return 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780281505443&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

string solution(vector&amp;lt;int&amp;gt; numbers) {
    string answer = &quot;&quot;;
    // numbers_len은 배열 numbers의 길이입니다.
    vector&amp;lt;string&amp;gt; v;
    int siz=0;
    for(auto e:numbers){
        v.push_back(to_string(e));
    }
    sort(v.begin(),v.end(),[](string a, string b){
        return a+b&amp;gt;b+a;
    }); 
    
    if(v[0]==&quot;0&quot;) return &quot;0&quot;;
    
    for(string s:v) answer+=s;
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sort를 사용자 정의해야됨&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sort(v.begin(), v.end(), [](string a, string b) { return a+b&amp;gt;b+a; });&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 형태 기억할 것.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 헷갈리는 거 sort 세번째 인자에 대해 물어본 내용&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;775&quot; data-start=&quot;740&quot; data-section-id=&quot;u9t6sm&quot; data-ke-size=&quot;size26&quot;&gt;return이 비교값인데 sort가 맞게 되는 건지?&lt;/h2&gt;
&lt;p data-end=&quot;808&quot; data-start=&quot;777&quot; data-ke-size=&quot;size16&quot;&gt;응. sort의 세 번째 인자는 &lt;b&gt;비교 함수&lt;/b&gt;야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;sort(v.begin(), v.end(), compare);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;891&quot; data-start=&quot;857&quot; data-ke-size=&quot;size16&quot;&gt;여기서 compare(a, b)가 true를 반환하면,&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;a를 b보다 앞에 둬라&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;925&quot; data-start=&quot;918&quot; data-ke-size=&quot;size16&quot;&gt;라는 뜻이야.&lt;/p&gt;
&lt;p data-end=&quot;937&quot; data-start=&quot;927&quot; data-ke-size=&quot;size16&quot;&gt;그래서 이 함수는:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;[](string a, string b) {
    return a + b &amp;gt; b + a;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1010&quot; data-start=&quot;1004&quot; data-ke-size=&quot;size16&quot;&gt;이 뜻이야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;a를 앞에 두는 게 더 큰 수를 만들면 true
아니면 false&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1091&quot; data-start=&quot;1061&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 a = &quot;6&quot;, b = &quot;10&quot;이면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a + b = &quot;610&quot;
b + a = &quot;106&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1159&quot; data-start=&quot;1133&quot; data-ke-size=&quot;size16&quot;&gt;&quot;610&quot; &amp;gt; &quot;106&quot;이니까 true.&lt;/p&gt;
&lt;p data-end=&quot;1163&quot; data-start=&quot;1161&quot; data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;6&quot;을 &quot;10&quot;보다 앞에 둬라&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1221&quot; data-start=&quot;1195&quot; data-ke-size=&quot;size16&quot;&gt;또 a = &quot;10&quot;, b = &quot;2&quot;이면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a + b = &quot;102&quot;
b + a = &quot;210&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1286&quot; data-start=&quot;1263&quot; data-ke-size=&quot;size16&quot;&gt;&quot;102&quot; &amp;gt; &quot;210&quot;은 false.&lt;/p&gt;
&lt;p data-end=&quot;1290&quot; data-start=&quot;1288&quot; data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;10&quot;을 &quot;2&quot;보다 앞에 두지 마라&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1353&quot; data-start=&quot;1325&quot; data-ke-size=&quot;size16&quot;&gt;그러면 &quot;2&quot;가 &quot;10&quot;보다 앞에 가게 돼.&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/255</guid>
      <comments>https://6kitt-hack.tistory.com/255#entry255comment</comments>
      <pubDate>Mon, 1 Jun 2026 11:43:49 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '기능개발' 풀이</title>
      <link>https://6kitt-hack.tistory.com/254</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고, 이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요.&lt;/p&gt;
&lt;b&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;&lt;b&gt;제한 사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;작업의 개수(progresses, speeds배열의 길이)는 100개 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;작업 진도는 100 미만의 자연수입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;작업 속도는 100 이하의 자연수입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다. 예를 들어 진도율이 95%인 작업의 개발 속도가 하루에 4%라면 배포는 2일 뒤에 이루어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780201090701&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; progresses, vector&amp;lt;int&amp;gt; speeds) {
    vector&amp;lt;int&amp;gt; answer;
    vector&amp;lt;int&amp;gt; days;
    for(int i=0;i&amp;lt;progresses.size();i++){
        int remain=100-progresses[i]; //남은 진행률 
        int day=remain/speeds[i]; //속도로 나누면 가능한 배포일 
        if(remain%speeds[i]!=0) day++; //나머지 있으면 1일 더 
        days.push_back(day);
    }
    answer.push_back(1); //일단 첫번째 배포일에 1번째는 무조건 배포됨 
    int idx=days[0];
    for(int i=1;i&amp;lt;days.size();i++){
        //이전 days보다 적거나 같으면 같은 배포일 
        if(days[i]&amp;lt;=idx) answer[answer.size()-1]++;
        else {answer.push_back(1); idx=days[i];}
        //크면 
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/254</guid>
      <comments>https://6kitt-hack.tistory.com/254#entry254comment</comments>
      <pubDate>Sun, 31 May 2026 13:18:41 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '같은 숫자는 싫어' 풀이</title>
      <link>https://6kitt-hack.tistory.com/253</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;배열 arr가 주어집니다. 배열 arr의 각 원소는 숫자 0부터 9까지로 이루어져 있습니다. 이때, 배열 arr에서 연속적으로 나타나는 숫자는 하나만 남기고 전부 제거하려고 합니다. 단, 제거된 후 남은 수들을 반환할 때는 배열 arr의 원소들의 순서를 유지해야 합니다. 예를 들면,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;arr = [1, 1, 3, 3, 0, 1, 1] 이면 [1, 3, 0, 1] 을 return 합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;arr = [4, 4, 4, 3, 3] 이면 [4, 3] 을 return 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;배열 arr에서 연속적으로 나타나는 숫자는 제거하고 남은 수들을 return 하는 solution 함수를 완성해 주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;배열 arr의 크기 : 1,000,000 이하의 자연수&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;배열 arr의 원소의 크기 : 0보다 크거나 같고 9보다 작거나 같은 정수&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780122311905&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; arr) 
{
    vector&amp;lt;int&amp;gt; answer;
    for(auto e:arr) { if(answer.empty() || e!=answer.back()) answer.push_back(e);}

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/253</guid>
      <comments>https://6kitt-hack.tistory.com/253#entry253comment</comments>
      <pubDate>Sat, 30 May 2026 15:25:20 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '폰켓몬' 풀이</title>
      <link>https://6kitt-hack.tistory.com/252</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;당신은 폰켓몬을 잡기 위한 오랜 여행 끝에, 홍 박사님의 연구실에 도착했습니다. 홍 박사님은 당신에게 자신의 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2마리를 가져가도 좋다고 했습니다.&lt;br /&gt;홍 박사님 연구실의 폰켓몬은 종류에 따라 번호를 붙여 구분합니다. 따라서 같은 종류의 폰켓몬은 같은 번호를 가지고 있습니다. 예를 들어 연구실에 총 4마리의 폰켓몬이 있고, 각 폰켓몬의 종류 번호가 [3번, 1번, 2번, 3번]이라면 이는 3번 폰켓몬 두 마리, 1번 폰켓몬 한 마리, 2번 폰켓몬 한 마리가 있음을 나타냅니다. 이때, 4마리의 폰켓몬 중 2마리를 고르는 방법은 다음과 같이 6가지가 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;첫 번째(3번), 두 번째(1번) 폰켓몬을 선택&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;첫 번째(3번), 세 번째(2번) 폰켓몬을 선택&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;첫 번째(3번), 네 번째(3번) 폰켓몬을 선택&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;두 번째(1번), 세 번째(2번) 폰켓몬을 선택&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;두 번째(1번), 네 번째(3번) 폰켓몬을 선택&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;세 번째(2번), 네 번째(3번) 폰켓몬을 선택&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;이때, 첫 번째(3번) 폰켓몬과 네 번째(3번) 폰켓몬을 선택하는 방법은 한 종류(3번 폰켓몬 두 마리)의 폰켓몬만 가질 수 있지만, 다른 방법들은 모두 두 종류의 폰켓몬을 가질 수 있습니다. 따라서 위 예시에서 가질 수 있는 폰켓몬 종류 수의 최댓값은 2가 됩니다.&lt;br /&gt;당신은 최대한 다양한 종류의 폰켓몬을 가지길 원하기 때문에, 최대한 많은 종류의 폰켓몬을 포함해서 N/2마리를 선택하려 합니다. N마리 폰켓몬의 종류 번호가 담긴 배열 nums가 매개변수로 주어질 때, N/2마리의 폰켓몬을 선택하는 방법 중, 가장 많은 종류의 폰켓몬을 선택하는 방법을 찾아, 그때의 폰켓몬 종류 번호의 개수를 return 하도록 solution 함수를 완성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;nums는 폰켓몬의 종류 번호가 담긴 1차원 배열입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;nums의 길이(N)는 1 이상 10,000 이하의 자연수이며, 항상 짝수로 주어집니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;폰켓몬의 종류 번호는 1 이상 200,000 이하의 자연수로 나타냅니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;가장 많은 종류의 폰켓몬을 선택하는 방법이 여러 가지인 경우에도, 선택할 수 있는 폰켓몬 종류 개수의 최댓값 하나만 return 하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780023545697&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;vector&amp;gt;
using namespace std;
#include &amp;lt;bits/stdc++.h&amp;gt; 

int solution(vector&amp;lt;int&amp;gt; nums)
{
    int answer = 1;
    //unordered_set에 다 넣으면 중복이 알아서 없어짐 
    //그리고 개수를 센다 
    unordered_set&amp;lt;int&amp;gt; s;
    for(auto e:nums) s.insert(e);
    int num=nums.size()/2;
    int n=s.size();
    
    answer=(n&amp;lt;num) ? n : num;
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/252</guid>
      <comments>https://6kitt-hack.tistory.com/252#entry252comment</comments>
      <pubDate>Fri, 29 May 2026 12:04:11 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 해시</title>
      <link>https://6kitt-hack.tistory.com/251</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;일단 알고리즘 공부를 미루는 사이 백준이 종료되었다...&lt;br /&gt;어떡하나 고민하다가 흠.. 일단 STL까지는 익히고 프로그래머스 알고리즘 고득점 kit라도 다 뽀개고 하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발등에 불 떨어진 상태라 일단 충돌 회피랑 구현은 건너뛰고, 정의와 성질, STL 사용법까지만..&lt;/p&gt;
&lt;h1&gt;해시 자료구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시는 insert, erase, find, update 모든 연산이 O(1)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 번호가(번호라고 해도 되는진 모르겠지만) 인덱스라고 치면 10의 16승이나 필요할 것이다. 그러나 해당 자료구조는 해시의 16자리 숫자 중 뒷 4자리를 인덱스로 사용하기에 O(1)이다. 그리고 이걸 정리해두는 공간이 해시 테이블이다.&lt;/p&gt;
&lt;h1&gt;STL&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STL에는 세 가지 구조체가 있는데, unordered_set, unordered_multiset, unordered_map이 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;unordered_set&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;unordered_set&amp;lt;int&amp;gt; s;
s.insert(-10); s.insert(100); s.insert(14); 
s.insert(-10); // {-10,100,14} &amp;lt;- 중복이 안된다. 
cout&amp;lt;&amp;lt;s.erase(100)&amp;lt;&amp;lt;'\n'; //{-10,14}, 1
cout&amp;lt;&amp;lt;s.erase(20)&amp;lt;&amp;lt;'\n'; //{-10,14}, 0 
if(s.find(15)!=s.end()) cout&amp;lt;&amp;lt;&quot;15 in s\n&quot;;
else cout&amp;lt;&amp;lt;&quot;15 not in s\n&quot;; 
s.size()이나 s.count(0)도 있음 
count는 해당 값이 있으면 1, 없으면 0 &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 auto e: s식으로 range-based for loop 하게 되면 출력 순서가 뒤죽박죽이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;unordered_multiset -&amp;gt; 중복 가능&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;unordered_multiset&amp;lt;int&amp;gt; ms; 
ms.insert(-10); ms.insert(100); ms.insert(15); 
ms.insert(-10); ms.insert(15); // {-10,-10,100,15,15} 
cout&amp;lt;&amp;lt;ms.size()&amp;lt;&amp;lt;'\n';
for(auto e: ms) cout&amp;lt;&amp;lt;e&amp;lt;&amp;lt;' ';
cout&amp;lt;&amp;lt; ms.erase(15)&amp;lt;&amp;lt;'\n'; //이러면 중복된 15들이 다 지워짐 
ms.erase(ms.find(-10)); // 이렇게 지워야 하나의 -10만 지울 수 있음 
//중복이 가능하기 때문에 count를 쓰면 해당 값이 몇개 있는지가 반환됨 &lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;unordered_map&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;unordered_map&amp;lt;string,int&amp;gt; m'
m[&quot;hi&quot;]=123;
m[&quot;bkd&quot;]=1000;

m.size(); // 2
m[&quot;hi&quot;]=-1;
e.first, e.second로 표시 &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unordered_map은 파이썬의 딕셔너리 같은..&lt;br /&gt;-&amp;gt; 중복은 안된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연습문제는 프로그래머스로 대체..&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/251</guid>
      <comments>https://6kitt-hack.tistory.com/251#entry251comment</comments>
      <pubDate>Mon, 25 May 2026 14:46:17 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '완주하지 못한 선수' 풀이</title>
      <link>https://6kitt-hack.tistory.com/250</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;b&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;completion의 길이는 participant의 길이보다 1 작습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;참가자 중에는 동명이인이 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1779597263236&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

string solution(vector&amp;lt;string&amp;gt; participant, vector&amp;lt;string&amp;gt; completion) {
    string answer = &quot;&quot;;
    //해시에 다 넣고 (동명이인 있댔으니까 unordered_multiset)
    //find로 찾아서 값이 없으면(s.end) 없는 거고 
    //있으면 그값 erase 하고 answer에 넣기 
    unordered_multiset&amp;lt;string&amp;gt; s;
    for(auto e:completion) s.insert(e); 
    for(auto f:participant){
        if(s.find(f)==s.end()){
            answer+=f;
            break;
        }
        s.erase(s.find(f));
    }

    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/250</guid>
      <comments>https://6kitt-hack.tistory.com/250#entry250comment</comments>
      <pubDate>Sun, 24 May 2026 13:36:06 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '단어 변환' 풀이</title>
      <link>https://6kitt-hack.tistory.com/248</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다.&lt;/p&gt;
&lt;div style=&quot;color: #000000;&quot;&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;background-color: #e9ecf3; color: #44576c;&quot;&gt;&lt;code&gt;1. 한 번에 한 개의 알파벳만 바꿀 수 있습니다.
2. words에 있는 단어로만 변환할 수 있습니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 begin이 &quot;hit&quot;, target가 &quot;cog&quot;, words가 [&quot;hot&quot;,&quot;dot&quot;,&quot;dog&quot;,&quot;lot&quot;,&quot;log&quot;,&quot;cog&quot;]라면 &quot;hit&quot; -&amp;gt; &quot;hot&quot; -&amp;gt; &quot;dot&quot; -&amp;gt; &quot;dog&quot; -&amp;gt; &quot;cog&quot;와 같이 4단계를 거쳐 변환할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 return 하도록 solution 함수를 작성해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;각 단어는 알파벳 소문자로만 이루어져 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;각 단어의 길이는 3 이상 10 이하이며 모든 단어의 길이는 같습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;words에는 3개 이상 50개 이하의 단어가 있으며 중복되는 단어는 없습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;begin과 target은 같지 않습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;변환할 수 없는 경우에는 0를 return 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1779076023974&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

#define X first 
#define Y second 



//몇글자가 같은지 return 
int compare_str(string a, string b){
    int ans=0;
    for(int i=0;i&amp;lt;a.size();i++){
        if(a[i]==b[i]) ans++;
    }
    return ans;
}

int bfs(string begin, string target, vector&amp;lt;string&amp;gt; words, queue&amp;lt;pair&amp;lt;int,string&amp;gt;&amp;gt; q, int next[]){
    int ans=1;
    while(!q.empty()){
        pair&amp;lt;int,string&amp;gt; cur=q.front(); q.pop();
        if(cur.Y==target) return next[cur.X];
        for(int i=0;i&amp;lt;words.size();i++){
            if(compare_str(cur.Y,words[i])==words[i].size()-1&amp;amp;&amp;amp;next[i]==0){
                q.push({i,words[i]});
                next[i]=next[cur.X]+1;
            }
        }
    }
    return 0;
}

int solution(string begin, string target, vector&amp;lt;string&amp;gt; words) {
    int answer = 51;
    int next[51]={0,};
    //bfs로 next가 있는지 점검.. 
    //1텀이 될 수 있는 걸 먼저 찾고 
    //q에 넣어서 for문으로 탐색 
    queue&amp;lt;pair&amp;lt;int,string&amp;gt;&amp;gt; q;
    for(int i=0;i&amp;lt;words.size();i++){
        if(compare_str(begin,words[i])==words[i].size()-1){
            next[i]=1;
            q.push({i,words[i]}); 
        }
    }
    int tmp=bfs(begin,target,words,q,next);
    if(tmp&amp;lt;answer) answer=tmp;
    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/248</guid>
      <comments>https://6kitt-hack.tistory.com/248#entry248comment</comments>
      <pubDate>Mon, 18 May 2026 12:47:44 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '게임 맵 최단거리' 풀이</title>
      <link>https://6kitt-hack.tistory.com/247</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;ROR 게임은 두 팀으로 나누어서 진행하며, 상대 팀 진영을 먼저 파괴하면 이기는 게임입니다. 따라서, 각 팀은 상대 팀 진영에 최대한 빨리 도착하는 것이 유리합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;지금부터 당신은 한 팀의 팀원이 되어 게임을 진행하려고 합니다. 다음은 5 x 5 크기의 맵에, 당신의 캐릭터가 (행: 1, 열: 1) 위치에 있고, 상대 팀 진영은 (행: 5, 열: 5) 위치에 있는 경우의 예시입니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;173&quot; data-origin-height=&quot;185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVSNBF/dJMcai4m26P/z456a87TAL79uc03ebBvZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVSNBF/dJMcai4m26P/z456a87TAL79uc03ebBvZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVSNBF/dJMcai4m26P/z456a87TAL79uc03ebBvZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVSNBF%2FdJMcai4m26P%2Fz456a87TAL79uc03ebBvZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;173&quot; height=&quot;185&quot; data-origin-width=&quot;173&quot; data-origin-height=&quot;185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;위 그림에서 검은색 부분은 벽으로 막혀있어 갈 수 없는 길이며, 흰색 부분은 갈 수 있는 길입니다. 캐릭터가 움직일 때는 동, 서, 남, 북 방향으로 한 칸씩 이동하며, 게임 맵을 벗어난 길은 갈 수 없습니다.&lt;br /&gt;아래 예시는 캐릭터가 상대 팀 진영으로 가는 두 가지 방법을 나타내고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;첫 번째 방법은 11개의 칸을 지나서 상대 팀 진영에 도착했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;173&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EtXxk/dJMcaarOTcQ/xJyq8FbhAeKCJ3C7oIqUdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EtXxk/dJMcaarOTcQ/xJyq8FbhAeKCJ3C7oIqUdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EtXxk/dJMcaarOTcQ/xJyq8FbhAeKCJ3C7oIqUdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEtXxk%2FdJMcaarOTcQ%2FxJyq8FbhAeKCJ3C7oIqUdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;173&quot; height=&quot;186&quot; data-origin-width=&quot;173&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;두 번째 방법은 15개의 칸을 지나서 상대팀 진영에 도착했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;173&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F1A8Q/dJMcaf0UBmF/eJ89XwsBQHdW9LKq1xzutk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F1A8Q/dJMcaf0UBmF/eJ89XwsBQHdW9LKq1xzutk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F1A8Q/dJMcaf0UBmF/eJ89XwsBQHdW9LKq1xzutk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF1A8Q%2FdJMcaf0UBmF%2FeJ89XwsBQHdW9LKq1xzutk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;173&quot; height=&quot;184&quot; data-origin-width=&quot;173&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;위 예시에서는 첫 번째 방법보다 더 빠르게 상대팀 진영에 도착하는 방법은 없으므로, 이 방법이 상대 팀 진영으로 가는 가장 빠른 방법입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;만약, 상대 팀이 자신의 팀 진영 주위에 벽을 세워두었다면 상대 팀 진영에 도착하지 못할 수도 있습니다. 예를 들어, 다음과 같은 경우에 당신의 캐릭터는 상대 팀 진영에 도착할 수 없습니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwNcSU/dJMcai4m26Q/m4mKHTuxiFKc1fLVFZj2QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwNcSU/dJMcai4m26Q/m4mKHTuxiFKc1fLVFZj2QK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwNcSU/dJMcai4m26Q/m4mKHTuxiFKc1fLVFZj2QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwNcSU%2FdJMcai4m26Q%2Fm4mKHTuxiFKc1fLVFZj2QK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;171&quot; height=&quot;185&quot; data-origin-width=&quot;171&quot; data-origin-height=&quot;185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;게임 맵의 상태 maps가 매개변수로 주어질 때, 캐릭터가 상대 팀 진영에 도착하기 위해서 지나가야 하는 칸의 개수의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;최솟값&lt;/b&gt;을 return 하도록 solution 함수를 완성해주세요. 단, 상대 팀 진영에 도착할 수 없을 때는 -1을 return 해주세요.&lt;/p&gt;
제한사항
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;maps는 n x m 크기의 게임 맵의 상태가 들어있는 2차원 배열로, n과 m은 각각 1 이상 100 이하의 자연수입니다.
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;n과 m은 서로 같을 수도, 다를 수도 있지만, n과 m이 모두 1인 경우는 입력으로 주어지지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;maps는 0과 1로만 이루어져 있으며, 0은 벽이 있는 자리, 1은 벽이 없는 자리를 나타냅니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;처음에 캐릭터는 게임 맵의 좌측 상단인 (1, 1) 위치에 있으며, 상대방 진영은 게임 맵의 우측 하단인 (n, m) 위치에 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1779074372321&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include&amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

#define X first 
#define Y second 

int vis[101][101]; //방문좌표 찍기 -&amp;gt; 누적값으로 찍어야 함 

int dx[4]={0,-1,0,1};
int dy[4]={1,0,-1,0};

int min_cnt=10001;

int bfs(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; maps, queue&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; q){
    int n=maps.size();
    int m=maps[0].size();
    while(!q.empty()){ //q가 빌 때까지 
        //bfs 돌리고 
        pair&amp;lt;int,int&amp;gt; cur=q.front(); q.pop(); //현재값 
        if(cur.X==n-1 &amp;amp;&amp;amp; cur.Y==m-1) return vis[cur.X][cur.Y];
        
        //다음값 탐색 
        for(int i=0;i&amp;lt;4;i++){
            int nx=dx[i]+cur.X;
            int ny=dy[i]+cur.Y;

            //nx,ny에 대한 유효 조건 
            if(nx&amp;lt;0||nx&amp;gt;=n||ny&amp;lt;0||ny&amp;gt;=m||vis[nx][ny]&amp;gt;0||maps[nx][ny]==0) continue;
            q.push({nx,ny});
            vis[nx][ny]=vis[cur.X][cur.Y]+1;
        }
    }
    return -1;
}

int solution(vector&amp;lt;vector&amp;lt;int&amp;gt; &amp;gt; maps)
{
    int answer = -1;
    int tmp=0;
    
    vis[0][0]=1;
    queue&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; q;
    q.push({0,0});
    answer=bfs(maps,q);
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bfs 문제 풀 때&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시작점 방문표시 해주기&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. vis에 누적값으로 저장하는 것도 고려해보기&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 방문 표시는 push와 함께 (pop할 때 하면 안됨)&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/247</guid>
      <comments>https://6kitt-hack.tistory.com/247#entry247comment</comments>
      <pubDate>Mon, 18 May 2026 12:21:34 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '네트워크' 풀이</title>
      <link>https://6kitt-hack.tistory.com/246</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #263747; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있을 때 컴퓨터 A와 컴퓨터 C도 간접적으로 연결되어 정보를 교환할 수 있습니다. 따라서 컴퓨터 A, B, C는 모두 같은 네트워크 상에 있다고 할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;컴퓨터의 개수 n, 연결에 대한 정보가 담긴 2차원 배열 computers가 매개변수로 주어질 때, 네트워크의 개수를 return 하도록 solution 함수를 작성하시오.&lt;/p&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;b&gt;제한사항&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;컴퓨터의 개수 n은 1 이상 200 이하인 자연수입니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;각 컴퓨터는 0부터&lt;span&gt;&amp;nbsp;&lt;/span&gt;n-1인 정수로 표현합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;i번 컴퓨터와 j번 컴퓨터가 연결되어 있으면 computers[i][j]를 1로 표현합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: inherit; color: #000000;&quot;&gt;computer[i][i]는 항상 1입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1778978675837&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;bits/stdc++.h&amp;gt;


using namespace std;

int vis[201];

// i번 컴퓨터와 j번 컴퓨터가 연결되어 있으면 computers[i][j]를 1로 표현
void bfs(int start, int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;&amp;amp; computers){
    queue&amp;lt;int&amp;gt; q;
    q.push(start);
    vis[start]=true;

    while(!q.empty()){
        int cur=q.front(); q.pop();
        
        for(int next=0;next&amp;lt;n;next++){
            if(computers[cur][next]==1&amp;amp;&amp;amp;vis[next]==0){
                vis[next]=1;
                q.push(next);
            }
        }
    }
}
int solution(int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; computers) {
    int answer = 0;

    for(int i=0;i&amp;lt;n;i++){
        if(vis[i]==0){
            answer++;
            bfs(i,n,computers);
        }
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,0,0&quot;&gt;2차원 맵 문제:&lt;/b&gt; &quot;미로 찾기&quot;, &quot;토마토가 익는 칸&quot;, &quot;섬의 면적 구하기&quot; -&amp;gt;상하좌우 dx, dy 사용!&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;18,1,0&quot;&gt;관계/네트워크 문제:&lt;/b&gt; &quot;A와 B가 친구다&quot;, &quot;컴퓨터가 연결되어 있다&quot; -&amp;gt;&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;dx, dy 절대 금지! for(int next = 0; next &amp;lt; n; next++) 형태로 인접한 노드를 직접 탐색!&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀고도 험한 알고리즘 공부..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/246</guid>
      <comments>https://6kitt-hack.tistory.com/246#entry246comment</comments>
      <pubDate>Sun, 17 May 2026 09:47:08 +0900</pubDate>
    </item>
    <item>
      <title>[C++] 프로그래머스 '최소직사각형' 풀이</title>
      <link>https://6kitt-hack.tistory.com/245</link>
      <description>&lt;pre id=&quot;code_1778859524214&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; sizes) {
    int answer = 0;
    int max_x=0; //가장 큰 
    int max_y=0; //두번째로 큰 
    
    for(auto c:sizes){
        //항상 큰,작은 순으로 두기 
        if(c[0]&amp;lt;c[1]){
            swap(c[0],c[1]);
        }
        
        if(max_x&amp;lt;c[0]) max_x=c[0];
        if(max_y&amp;lt;c[1]) max_y=c[1];
    }
    
    answer=max_x*max_y;
    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/245</guid>
      <comments>https://6kitt-hack.tistory.com/245#entry245comment</comments>
      <pubDate>Sat, 16 May 2026 00:39:58 +0900</pubDate>
    </item>
    <item>
      <title>[C] 프로그래머스 '피로도' 풀이</title>
      <link>https://6kitt-hack.tistory.com/244</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;TL;DR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스 피로도 문제는 던전 순서를 전부 바꿔 보며 최대로 방문한 개수를 찾는 백트래킹 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 종료 조건을 &lt;code&gt;M == 0&lt;/code&gt;으로 잡아서 막혔고, 그다음에는 재귀 함수 이름을 잘못 불러서 또 틀렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 C에서 &lt;code&gt;solution()&lt;/code&gt;과 실제 재귀 함수 &lt;code&gt;sol()&lt;/code&gt;을 섞어 부르면 왜 답이 깨지는지를 기록한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피로도 문제의 입력은 현재 피로도 &lt;code&gt;k&lt;/code&gt;와 던전 배열 &lt;code&gt;dungeons&lt;/code&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 던전은 &lt;code&gt;[최소 필요 피로도, 소모 피로도]&lt;/code&gt; 형태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표는 단순하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 피로도로 입장 가능한 던전을 고르고, 방문 처리한 뒤, 남은 던전 중 다시 갈 곳을 고른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 순서를 탐색하면서 가장 많이 방문한 횟수를 저장한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 &lt;code&gt;M&lt;/code&gt;을 남은 던전 수처럼 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 던전을 하나 돌 때마다 &lt;code&gt;M - 1&lt;/code&gt;을 넘기고, &lt;code&gt;M == 0&lt;/code&gt;이 되면 답을 올리는 식으로 접근했다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int answer = -1;
int vis[9];

int sol(int k, int** dungeons, size_t dungeons_rows, int M) {
    if(M==0){answer++; return answer;}
    for(int i=0;i&amp;lt;dungeons_rows;i++){
        if(!vis[i]){
            if(k&amp;lt;dungeons[i][0]){ continue; }
            vis[i]=1;
            k-=dungeons[i][1];
            solution(k,dungeons,dungeons_rows,M-1);
            vis[i]=0;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 모든 던전을 다 도는 경우만 답으로 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 피로도 문제는 중간에 더 이상 갈 던전이 없어도 그때까지 방문한 개수를 답 후보로 봐야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;삽질 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 조건을 고친 뒤에는 &lt;code&gt;M&lt;/code&gt;을 지금까지 방문한 던전 수로 바꿨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 호출마다 &lt;code&gt;answer&lt;/code&gt;를 갱신하면, 더 들어갈 수 없는 지점에서도 현재 기록이 남는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int answer=0;
int vis[9]={0,};

int sol(int k, int** dungeons, size_t dungeons_rows, int M) {
    if(M&amp;gt;answer) answer=M;
    for(int i=0;i&amp;lt;dungeons_rows;i++){
        if(!vis[i]&amp;amp;&amp;amp; k&amp;gt;=dungeons[i][0]){
            vis[i]=1;
            solution(k-dungeons[i][1],dungeons,dungeons_rows,M+1);
            vis[i]=0;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉보기에는 거의 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;vis[i]&lt;/code&gt;로 방문 표시를 남기고, 재귀 호출 후 다시 &lt;code&gt;0&lt;/code&gt;으로 복구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;k - dungeons[i][1]&lt;/code&gt;도 인자로 넘겨서 이전 피로도 상태가 현재 스택에 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 한 줄이 문제였다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;solution(k-dungeons[i][1],dungeons,dungeons_rows,M+1);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 부른 함수는 재귀 함수 &lt;code&gt;sol()&lt;/code&gt;이 아니라 프로그래머스 제출용 입구 함수 &lt;code&gt;solution()&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;solution()&lt;/code&gt;은 매번 &lt;code&gt;answer=0&lt;/code&gt;으로 초기화하고, 다시 &lt;code&gt;sol()&lt;/code&gt;을 처음부터 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀로 깊게 들어간다고 생각했지만, 실제로는 시작점으로 계속 되돌아간 셈이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀 호출 대상만 &lt;code&gt;sol()&lt;/code&gt;로 바꾸면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 변수는 프로그래머스가 여러 테스트 케이스를 돌릴 때 값이 남지 않도록 &lt;code&gt;solution()&lt;/code&gt;에서 초기화한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int answer = 0;
int vis[9] = {0,};

int sol(int k, int** dungeons, size_t dungeons_rows, int M) {
    if (M &amp;gt; answer) answer = M;

    for (int i = 0; i &amp;lt; dungeons_rows; i++) {
        if (!vis[i] &amp;amp;&amp;amp; k &amp;gt;= dungeons[i][0]) {
            vis[i] = 1;
            sol(k - dungeons[i][1], dungeons, dungeons_rows, M + 1);
            vis[i] = 0;
        }
    }

    return 0;
}

int solution(int k, int** dungeons, size_t dungeons_rows, size_t dungeons_cols) {
    answer = 0;

    for (int i = 0; i &amp;lt; 9; i++) {
        vis[i] = 0;
    }

    sol(k, dungeons, dungeons_rows, 0);

    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;M&lt;/code&gt;은 현재까지 방문한 던전 수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;answer&lt;/code&gt;는 탐색 도중 만난 최대 &lt;code&gt;M&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갈 수 있는 던전만 고르므로 &lt;code&gt;k &amp;gt;= dungeons[i][0]&lt;/code&gt;가 가지치기 조건이 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;백트래킹에서 봐야 할 지점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백트래킹은 상태를 선택하고, 더 깊게 들어갔다가, 원래 상태로 되돌리는 흐름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에서는 방문 배열만 직접 복구하면 된다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;vis[i] = 1;
sol(k - dungeons[i][1], dungeons, dungeons_rows, M + 1);
vis[i] = 0;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;k&lt;/code&gt;는 함수 인자로 계산해서 넘겼기 때문에 별도 복구가 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 &lt;code&gt;k -= dungeons[i][1]&lt;/code&gt;처럼 현재 변수 값을 직접 바꾸면 재귀가 끝난 뒤 &lt;code&gt;k += dungeons[i][1]&lt;/code&gt;로 되돌려야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 차이지만, C로 백트래킹을 짤 때 이 부분에서 자주 꼬인다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배운 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀 함수와 제출용 함수는 역할을 나눠야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스의 &lt;code&gt;solution()&lt;/code&gt;은 입구 함수로 두고, 실제 탐색은 별도 함수가 맡는 편이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 백트래킹이 막히면 &lt;code&gt;상태 선택 &amp;rarr; 재귀 호출 &amp;rarr; 상태 복구&lt;/code&gt; 세 줄부터 확인한다.&lt;/p&gt;</description>
      <category>coding/프로그래머스 알고리즘 고득점 kit : cpp</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/244</guid>
      <comments>https://6kitt-hack.tistory.com/244#entry244comment</comments>
      <pubDate>Fri, 15 May 2026 15:28:13 +0900</pubDate>
    </item>
    <item>
      <title>[Infra] 단일 노드에서 고가용성 클러스터로 - 로드밸런서가 필요한 이유</title>
      <link>https://6kitt-hack.tistory.com/243</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. TL;DR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 노드 구조는 단순하고 빠르게 만들 수 있지만, 노드 하나가 죽으면 서비스 전체가 멈춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성 구조는 여러 노드에 서비스를 분산하고, 로드밸런서가 정상 노드로만 요청을 보내는 방식으로 장애 영향을 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서는 단순히 트래픽을 나눠주는 장치가 아니라, 장애 감지, 우회, 확장성의 출발점이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 단일 노드 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 서비스를 만들 때는 보통 단일 노드 구조로 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 단순하다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; ssh-tunnel
&amp;rarr; node
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 하나의 node가 애플리케이션 서버, 내부 연결, DB 접근 등 대부분의 역할을 맡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 프로젝트나 MVP 단계에서는 이 구조가 나쁘지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성이 쉽고, 배포도 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 한 대에 접속해서 프로세스를 띄우고, nginx로 요청을 받아 애플리케이션으로 넘기면 된다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; app process
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서는 장애 원인을 추적하기도 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 안 되면 해당 서버에 들어가서 nginx, app process, DB 연결을 확인하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 단일 노드 구조에는 명확한 한계가 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 단일 노드의 가장 큰 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 노드의 가장 큰 문제는 SPOF다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPOF는 Single Point of Failure의 약자다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나가 죽으면 전체가 멈추는 지점을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 노드 구조에서는 node 자체가 SPOF가 된다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; node ❌
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node가 죽으면 요청을 받을 곳이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx가 살아 있어도 애플리케이션이 죽으면 서비스는 실패한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB가 살아 있어도 애플리케이션 노드가 죽으면 사용자는 서비스를 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애 상황은 다양하다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 서버 프로세스가 죽음
- CPU / 메모리 사용량이 급증함
- 디스크가 가득 참
- 네트워크가 끊김
- 배포 중 서버가 재시작됨
- 특정 요청 때문에 애플리케이션이 멈춤&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 하나만 발생해도 단일 노드 구조에서는 전체 서비스 장애로 이어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 트래픽이 늘거나 서비스 안정성이 중요해지는 순간, 단일 노드만으로는 부족해진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 고가용성이라는 말의 의미&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성은 High Availability, 줄여서 HA라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &amp;ldquo;장애가 절대 나지 않는 시스템&amp;rdquo;을 만드는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애가 나도 서비스가 계속 동작하도록 만드는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 고가용성의 질문은 이것이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;서버 한 대가 죽어도 사용자는 계속 서비스를 쓸 수 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 노드 구조에서는 답이 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여러 노드가 있고, 요청을 정상 노드로 우회할 수 있다면 가능해진다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;              ┌&amp;rarr; node-1
user &amp;rarr; LB ────┼&amp;rarr; node-2
              └&amp;rarr; node-3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 LB는 Load Balancer다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서는 들어오는 요청을 여러 노드로 분산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 특정 노드가 죽으면 그 노드로 요청을 보내지 않는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 로드밸런서가 하는 일&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서의 기본 역할은 트래픽 분산이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 요청이 하나의 서버에 몰리지 않도록 여러 서버로 나눠 보낸다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;request 1 &amp;rarr; node-1
request 2 &amp;rarr; node-2
request 3 &amp;rarr; node-3
request 4 &amp;rarr; node-1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 단순한 방식은 round-robin이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청을 순서대로 서버에 나눠 보내는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NGINX도 HTTP load balancing에서 별도 방식을 지정하지 않으면 기본적으로 round-robin 방식으로 upstream 서버에 요청을 분산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 로드밸런서의 역할은 단순 분산에서 끝나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 역할은 헬스 체크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서는 서버가 정상인지 주기적으로 확인한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;LB &amp;rarr; node-1 health check: OK
LB &amp;rarr; node-2 health check: FAIL
LB &amp;rarr; node-3 health check: OK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node-2가 비정상이면 로드밸런서는 node-2로 요청을 보내지 않는다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;              ┌&amp;rarr; node-1 ✅
user &amp;rarr; LB ────┼&amp;rarr; node-2 ❌
              └&amp;rarr; node-3 ✅&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 장애 노드를 우회하면 사용자는 장애를 덜 체감한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전히 무중단이라고 말할 수는 없어도, 단일 노드보다 훨씬 안정적인 구조가 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 단일 노드와 고가용성 클러스터 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 노드 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; node
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 단순함이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;장점

- 구축이 빠름
- 운영 포인트가 적음
- 비용이 낮음
- 디버깅이 쉬움&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점은 장애에 약하다는 점이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;단점

- node 하나가 죽으면 전체 서비스 중단
- 배포 중 장애 가능성 큼
- 트래픽 증가에 취약
- 서버 스펙을 키우는 scale-up에 의존&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성 클러스터는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; load balancer
&amp;rarr; node-1
&amp;rarr; node-2
&amp;rarr; node-3
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 장애와 트래픽 증가에 더 강하다는 점이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;장점

- 여러 노드로 트래픽 분산
- 일부 노드 장애 시 우회 가능
- 서버를 추가하는 scale-out 가능
- 배포 전략을 더 유연하게 가져갈 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점은 구조가 복잡해진다는 점이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;단점

- 설정할 요소가 늘어남
- 로그와 모니터링이 중요해짐
- 세션 관리 문제가 생길 수 있음
- DB는 여전히 별도 HA 설계가 필요함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 로드밸런서를 붙인다고 모든 문제가 해결되는 것은 아니라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서는 애플리케이션 노드의 가용성을 높이는 출발점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB, 캐시, 파일 저장소, 세션 저장소는 별도로 설계해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 로드밸런서와 nginx의 관계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx는 웹 서버이면서 reverse proxy로도 자주 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reverse proxy는 클라이언트 요청을 대신 받아 내부 서버로 전달하는 역할을 한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; app server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx는 여러 upstream 서버를 묶어서 로드밸런서처럼 사용할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;upstream app_servers {
    server 10.0.0.11:3000;
    server 10.0.0.12:3000;
    server 10.0.0.13:3000;
}

server {
    listen 80;

    location / {
        proxy_pass http://app_servers;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정은 사용자의 요청을 &lt;code&gt;app_servers&lt;/code&gt; 그룹에 속한 서버들로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 요청은 여러 서버에 분산된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 쓰면 nginx 앞단은 그대로 두고, 뒤쪽 애플리케이션 서버를 여러 대로 늘릴 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; app-1
&amp;rarr; app-2
&amp;rarr; app-3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 운영 환경에서는 nginx 자체가 SPOF가 되지 않도록 nginx 앞에 클라우드 로드밸런서를 두거나, nginx도 이중화하는 구조를 고려한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 클라우드 로드밸런서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 환경에서는 AWS ELB, GCP Load Balancer, Azure Load Balancer 같은 관리형 로드밸런서를 자주 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관리형 로드밸런서는 사용자가 직접 로드밸런서 서버를 운영하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 사업자가 로드밸런서의 가용성, 확장성, 헬스 체크 기능을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Elastic Load Balancing은 들어오는 트래픽을 EC2 인스턴스, 컨테이너, IP 주소 같은 여러 target으로 분산할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 target의 상태를 모니터링하고, 정상 target으로만 트래픽을 보낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 다음처럼 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; cloud load balancer
&amp;rarr; app node-1
&amp;rarr; app node-2
&amp;rarr; app node-3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 Availability Zone을 함께 쓰면 특정 zone 장애에도 다른 zone의 정상 target으로 트래픽을 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;                 ┌&amp;rarr; AZ-a / node-1
user &amp;rarr; LB ───────┼&amp;rarr; AZ-b / node-2
                 └&amp;rarr; AZ-c / node-3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 구조가 단일 노드보다 고가용성에 가까운 이유다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 한 대가 아니라, 여러 노드와 여러 zone을 기준으로 장애를 흡수한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. Kubernetes에서는 Service와 Ingress가 등장한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes를 사용하면 애플리케이션은 보통 Pod로 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pod는 언제든 재시작되거나 다른 노드로 이동할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Pod의 IP를 직접 바라보는 구조는 불안정하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes에서는 Service가 이 문제를 해결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service는 Pod 앞에 안정적인 접근 지점을 만들어준다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;client
&amp;rarr; Service
&amp;rarr; Pod-1
&amp;rarr; Pod-2
&amp;rarr; Pod-3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 트래픽을 클러스터 안으로 들여보낼 때는 Ingress나 LoadBalancer 타입 Service를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;user
&amp;rarr; external load balancer
&amp;rarr; ingress
&amp;rarr; service
&amp;rarr; pods&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서는 로드밸런싱이 여러 단계에서 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 로드밸런서가 외부 요청을 클러스터로 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ingress가 HTTP 경로와 도메인 기준으로 서비스를 나눈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service가 실제 Pod들로 트래픽을 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes를 쓴다고 로드밸런서 개념이 사라지는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 로드밸런싱 계층이 더 명확하게 나뉜다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 단일 노드에서 HA로 넘어갈 때 생기는 새로운 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성 구조로 넘어가면 서버만 늘리면 된다고 생각하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 애플리케이션 구조도 함께 바뀌어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 세션 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 노드에서는 메모리에 로그인 세션을 저장해도 큰 문제가 없어 보인다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; node-1 memory session&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 노드가 여러 대가 되면 문제가 생긴다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;request 1 &amp;rarr; node-1
request 2 &amp;rarr; node-2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node-1에만 세션이 있으면 node-2는 사용자를 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 세션은 Redis 같은 외부 저장소로 빼거나, JWT처럼 서버 메모리에 의존하지 않는 방식으로 설계해야 한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node-1
node-2
node-3
&amp;rarr; redis session store&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 업로드도 마찬가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 노드에서는 로컬 디스크에 파일을 저장해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여러 노드가 되면 node-1에 저장된 파일을 node-2가 못 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 S3 같은 object storage나 공유 스토리지를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;app nodes
&amp;rarr; object storage&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그도 중앙화해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드가 여러 대면 특정 요청의 로그가 어느 서버에 있는지 찾기 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 로그 수집 시스템이 필요해진다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node-1 logs
node-2 logs
node-3 logs
&amp;rarr; centralized logging&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 HA 구조는 서버를 늘리는 문제가 아니라, 상태를 어디에 둘지 다시 설계하는 문제다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. DB는 여전히 병목이 될 수 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 노드를 여러 대로 늘려도 DB가 하나라면 DB는 여전히 SPOF가 될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;              ┌&amp;rarr; app-1
user &amp;rarr; LB ────┼&amp;rarr; app-2
              └&amp;rarr; app-3
                    &amp;darr;
                   DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 app 노드 하나가 죽어도 서비스는 계속될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 DB가 죽으면 전체 서비스가 멈춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 DB도 별도의 고가용성 설계가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로는 replication, failover, managed DB의 Multi-AZ 구성이 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;app nodes
&amp;rarr; primary DB
&amp;rarr; replica DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 트래픽이 많다면 read replica를 붙일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애 대응이 중요하다면 primary 장애 시 replica가 승격되는 failover 구조를 고려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 DB HA는 애플리케이션 노드 HA보다 더 조심해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 정합성, 복제 지연, 장애 전환 시점의 쓰기 유실 가능성까지 봐야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 고가용성 구조를 단계별로 확장하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 완벽한 클러스터를 만들 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단계적으로 확장하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12.1 1단계: 단일 노드&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; app
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVP나 개인 프로젝트는 이 구조로 충분할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 서비스를 작동하게 만드는 것이 중요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12.2 2단계: nginx reverse proxy 분리&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; app
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉보기에는 비슷하지만 nginx의 역할을 명확히 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TLS 종료, reverse proxy, 정적 파일 처리, 요청 라우팅을 nginx가 맡는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12.3 3단계: 애플리케이션 노드 다중화&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; load balancer
&amp;rarr; app-1
&amp;rarr; app-2
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계부터 로드밸런서가 중요해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app-1이 죽어도 app-2로 요청을 보낼 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12.4 4단계: 상태 외부화&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;user
&amp;rarr; load balancer
&amp;rarr; app-1 / app-2
&amp;rarr; redis
&amp;rarr; object storage
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션, 캐시, 파일 저장소를 애플리케이션 노드 밖으로 뺀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해야 app 노드를 자유롭게 늘리고 줄일 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;12.5 5단계: DB HA&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;user
&amp;rarr; load balancer
&amp;rarr; app nodes
&amp;rarr; primary DB
&amp;rarr; replica DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB까지 이중화하면 애플리케이션 노드뿐 아니라 데이터 계층의 장애에도 대응할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 단계는 복잡도가 높기 때문에 managed DB를 사용하는 경우가 많다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. 로드밸런서 알고리즘&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서는 어떤 기준으로 요청을 나눌지 결정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 방식은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Round Robin:
요청을 서버에 순서대로 분산한다.

Least Connections:
현재 연결 수가 가장 적은 서버로 보낸다.

IP Hash:
클라이언트 IP를 기준으로 같은 사용자를 같은 서버로 보낸다.

Weighted Round Robin:
서버마다 가중치를 두고 더 강한 서버에 더 많은 요청을 보낸다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 round-robin만으로도 충분한 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 서버 성능이 다르거나, 연결 시간이 긴 요청이 많거나, 세션 고정이 필요하면 다른 방식을 고려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 세션 고정을 로드밸런서에만 의존하는 것은 장기적으로 좋지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하면 세션을 외부 저장소로 빼는 방향이 더 확장성 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. 헬스 체크는 무엇을 확인해야 하나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헬스 체크는 단순히 포트가 열려 있는지만 보면 부족할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션이 살아 있는지, DB 연결이 가능한지, 핵심 의존성이 정상인지 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기본적인 헬스 체크는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /health
&amp;rarr; 200 OK&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 서비스에서는 readiness와 liveness를 나눠 생각하는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;liveness:
프로세스가 살아 있는가?

readiness:
지금 요청을 받을 준비가 되었는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 애플리케이션 프로세스는 살아 있지만 DB 연결이 끊겼다면 liveness는 OK일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 readiness는 FAIL이어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래야 로드밸런서가 해당 노드로 사용자 요청을 보내지 않는다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /live
&amp;rarr; 200 OK

GET /ready
&amp;rarr; DB 연결 실패
&amp;rarr; 503 Service Unavailable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헬스 체크를 잘못 설계하면 죽은 서버로 계속 요청을 보내거나, 반대로 멀쩡한 서버를 장애로 판단할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. 전체 구조를 다시 보면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 구조는 단순했다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; nginx
&amp;rarr; ssh-tunnel
&amp;rarr; node
&amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 빠르게 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 node 하나가 죽으면 서비스 전체가 멈춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성 구조는 다음 방향으로 확장된다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user
&amp;rarr; load balancer
&amp;rarr; nginx / ingress
&amp;rarr; app node-1
&amp;rarr; app node-2
&amp;rarr; app node-3
&amp;rarr; DB / cache / storage&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 핵심은 &amp;ldquo;서버를 여러 대 둔다&amp;rdquo;가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청을 정상 노드로 보내고, 상태를 외부화하고, 장애 지점을 줄이는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서는 그 시작점이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16. 정리 / 배운 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 노드 구조는 단순하지만 node 하나가 전체 서비스의 SPOF가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로드밸런서는 요청을 여러 노드로 분산하고, 비정상 노드를 우회해 서비스 가용성을 높인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성 클러스터로 넘어갈 때는 로드밸런서뿐 아니라 세션, 파일, 로그, DB까지 함께 설계해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NGINX Documentation, Using nginx as HTTP load balancer&lt;/li&gt;
&lt;li&gt;NGINX Documentation, Reverse Proxy&lt;/li&gt;
&lt;li&gt;AWS Documentation, Elastic Load Balancing&lt;/li&gt;
&lt;li&gt;AWS Documentation, Health checks for Application Load Balancer target groups&lt;/li&gt;
&lt;li&gt;Kubernetes Documentation, Services, Load Balancing, and Networking&lt;/li&gt;
&lt;li&gt;Kubernetes Documentation, Service&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>dev</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/243</guid>
      <comments>https://6kitt-hack.tistory.com/243#entry243comment</comments>
      <pubDate>Fri, 15 May 2026 01:34:04 +0900</pubDate>
    </item>
    <item>
      <title>[Codex/OMX] Windows에서 `$team` 안 될 때 - psmux, tmux, hooks.json 구조로 이해하기</title>
      <link>https://6kitt-hack.tistory.com/242</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. TL;DR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX의 &lt;code&gt;$team&lt;/code&gt;과 HUD는 tmux 환경에 의존하므로 Windows에서는 &lt;code&gt;psmux&lt;/code&gt; 세션 안에서 실행해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;omx --madmax --xhigh&lt;/code&gt;를 일반 PowerShell에서 바로 실행하면 &lt;code&gt;outside-tmux surface&lt;/code&gt;가 뜨고 &lt;code&gt;$team&lt;/code&gt;이 제대로 동작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$team&lt;/code&gt;을 안정적으로 쓰려면 깨끗한 git repo, 초기 커밋, psmux 세션, hooks sanitizer까지 준비해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 왜 Codex 단독 실행과 OMX 실행을 구분해야 하나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex와 OMX는 실행 목적이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex CLI는 단독으로도 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 질문, 코드 수정, 파일 검토는 그냥 &lt;code&gt;codex&lt;/code&gt;로 충분하다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;# === Windows PowerShell ===
mkdir &quot;$HOME\projects\my-first&quot; -Force | Out-Null
cd &quot;$HOME\projects\my-first&quot;
git init
&quot;# init&quot; | Out-File README.md
git add .
git commit -m &quot;init&quot;

codex&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행하면 Codex TUI가 뜬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자유롭게 질문하거나 파일을 수정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 OMX의 &lt;code&gt;$team&lt;/code&gt;, HUD, 병렬 worker 기능을 쓰려면 이야기가 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX의 멀티 worker 기능은 tmux 기반 pane과 세션 관리를 전제로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서는 일반적인 tmux 대신 &lt;code&gt;psmux&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 OMX를 쓸 때는 &amp;ldquo;PowerShell에서 바로 실행&amp;rdquo;이 아니라 &amp;ldquo;psmux 세션 안에서 실행&amp;rdquo;이 기본이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. OMX &lt;code&gt;$team&lt;/code&gt;이 안 되는 가장 흔한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔한 실수는 일반 PowerShell에서 바로 OMX를 실행하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;brainfuck&quot;&gt;&lt;code&gt;omx --madmax --xhigh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행하면 OMX 자체는 켜질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;code&gt;$team&lt;/code&gt;이나 HUD 같은 기능은 제대로 동작하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;outside-tmux surface&lt;/code&gt; 메시지가 나오거나, worker가 idle 상태로 멈출 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$team&lt;/code&gt;은 worker를 여러 pane으로 나누고, 각 worker의 작업 상태를 관리해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 tmux 세션을 전제로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서는 그 역할을 psmux가 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 먼저 psmux 세션을 만들어야 한다.&lt;/p&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;# === Windows PowerShell ===
tmux new-session -s codex&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 psmux 세션 안에서 OMX를 실행한다.&lt;/p&gt;
&lt;pre class=&quot;brainfuck&quot;&gt;&lt;code&gt;omx --madmax --xhigh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서는 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;omx&lt;/code&gt;를 먼저 켜고 나중에 tmux를 여는 방식이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tmux 세션 안으로 들어간 뒤 OMX를 실행해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 기본 실행 흐름&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX를 안정적으로 쓰는 기본 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# === Windows PowerShell ===

# 1. 작업 폴더 생성
New-Item -ItemType Directory -Path &quot;$HOME\work\my-project&quot; -Force | Out-Null
Set-Location &quot;$HOME\work\my-project&quot;

# 2. git repo 초기화
git init
git config user.email &quot;you@example.com&quot;
git config user.name &quot;you&quot;

&quot;# init&quot; | Out-File -FilePath README.md -Encoding utf8 -NoNewline
git add .
git commit -m &quot;init&quot;

# 3. psmux 세션 생성
tmux new-session -s codex

# 4. psmux 안에서 OMX 실행
omx --madmax --xhigh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 조건은 세 가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, 사용자 홈 디렉토리 안의 깨끗한 repo에서 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째, git 초기 커밋이 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셋째, OMX는 psmux 세션 안에서 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$team&lt;/code&gt;은 worker별 worktree를 만들어 병렬 작업을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 git repo가 깨끗하지 않거나 초기 커밋이 없으면 worktree 격리가 실패할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. OMX 키워드와 Codex subagent를 구분하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX에는 &lt;code&gt;$&lt;/code&gt;로 시작하는 키워드가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex subagent는 자연어로 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘을 섞으면 헷갈리기 쉽다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;OMX 키워드:
$deep-interview &quot;...&quot;
$ralplan &quot;...&quot;
$ralph &quot;...&quot;
$team 3:executor &quot;...&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$deep-interview&lt;/code&gt;는 의도와 범위를 명확히 할 때 먼저 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$ralplan&lt;/code&gt;은 승인 가능한 구현 계획을 만들 때 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$ralph&lt;/code&gt;는 단일 책임자가 끝까지 완성하는 루프에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$team&lt;/code&gt;은 여러 worker가 병렬로 나눠서 작업할 때 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Codex subagent는 자연어로 부른다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;architect 로 이 프로젝트 구조를 검토해줘
explorer 로 UserService 를 참조하는 모든 곳을 찾아줘
reviewer 로 방금 만든 코드 검증
pro 로 이 동시성 문제를 깊게 분석해줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;@pro&lt;/code&gt;처럼 쓰는 방식은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX 키워드는 &lt;code&gt;$&lt;/code&gt;를 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex subagent는 자연어로 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@&lt;/code&gt;는 둘 다 아니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 작업별 실행 방식 선택 기준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 작업에 &lt;code&gt;$team&lt;/code&gt;을 쓰는 것은 좋은 선택이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 성격에 따라 실행 방식을 나눠야 한다.&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;간단한 질문 / 작은 수정:
- Codex 단독
- Leader 기본 모델

새 기능 설계:
- architect
- 필요하면 $deep-interview 후 $ralplan

대규모 구현:
- psmux 안에서 $team 3:executor

코드 리뷰:
- reviewer

어려운 버그:
- pro

단일 책임자가 끝까지 완성해야 하는 작업:
- $ralph&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$team&lt;/code&gt;은 병렬 작업에 강하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 작업 단위가 모호하면 오히려 통신 비용이 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;프로젝트 전체 리팩토링해줘&amp;rdquo;는 &lt;code&gt;$team&lt;/code&gt;에 적합하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 &amp;ldquo;API 모듈, UI 모듈, 테스트 모듈을 나눠서 구현해줘&amp;rdquo;처럼 분리 가능한 작업은 &lt;code&gt;$team&lt;/code&gt;에 맞다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. &lt;code&gt;$team&lt;/code&gt; 사용 전 체크리스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$team&lt;/code&gt;을 쓰기 전에는 체크리스트를 먼저 확인하는 편이 좋다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$team 사용 전 체크리스트

1. psmux 세션 안에서 omx를 실행했는가?
2. cwd가 사용자 홈 디렉토리 안의 작업 폴더인가?
3. git repo가 초기화되어 있는가?
4. 초기 커밋이 존재하는가?
5. 작업 단위가 명확히 분리되어 있는가?
6. hooks.json sanitizer가 등록되어 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 하나라도 빠지면 worker가 idle 상태가 되거나 worktree 생성에 실패할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Windows에서는 경로와 권한 문제가 자주 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하면 시스템 임시 폴더나 다운로드 폴더보다 &lt;code&gt;~\work\project&lt;/code&gt; 같은 사용자 홈 디렉토리 아래에서 시작하는 편이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부득이하게 git ownership 문제가 나면 safe directory 설정을 추가할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;git config --global --add safe.directory '*'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령은 dubious ownership 경로에서 worktree가 막히는 문제를 우회할 때 사용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. psmux 기본 조작법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;psmux는 tmux처럼 세션, pane, window를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 조작을 알아두면 &lt;code&gt;$team&lt;/code&gt; worker 상태를 확인하기 쉽다.&lt;/p&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;# 새 세션 생성
tmux new-session -s codex

# 세션 목록 확인
tmux ls

# 기존 세션에 다시 접속
tmux attach -t codex

# 세션 종료
tmux kill-session -t codex&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 안에서는 다음 키를 자주 쓴다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;Ctrl+b %      수직 pane 분할
Ctrl+b &quot;      수평 pane 분할
Ctrl+b 방향키 pane 이동
Ctrl+b d      detach
Ctrl+b c      새 window
Ctrl+b 0~9    window 번호 이동&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;detach&lt;/code&gt;는 세션을 종료하는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션은 유지하고 현재 터미널만 빠져나오는 동작이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 들어가려면 &lt;code&gt;tmux attach -t codex&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. &lt;code&gt;PostToolUse hook (blocked)&lt;/code&gt;가 반복될 때&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX를 쓰다 보면 &lt;code&gt;PostToolUse hook (blocked)&lt;/code&gt; 메시지가 반복될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 &lt;code&gt;$team&lt;/code&gt; worker가 idle, unknown, retry 상태로 빠질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 핵심은 hook이 정상적인 진단 명령까지 위험 명령으로 오탐하는 경우다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 worker가 파일 존재 여부를 확인하려고 &lt;code&gt;cat missing-file&lt;/code&gt; 같은 명령을 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 hook이 &lt;code&gt;no such file or directory&lt;/code&gt; 같은 문구를 보고 실패로 차단해버릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용하는 것이 sanitizer다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sanitizer는 &lt;code&gt;~/.codex/hooks.json&lt;/code&gt;에서 &lt;code&gt;PostToolUse&lt;/code&gt;만 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 모든 가드레일을 제거하는 것이 아니라는 점이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;제거:
- PostToolUse

보존:
- PreToolUse
- SessionStart
- UserPromptSubmit
- Stop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PreToolUse에 있는 destructive command 경고, team enforcement, context 주입 등은 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 false-positive가 큰 PostToolUse만 걷어내는 방식이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. sanitizer 등록 흐름&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PowerShell에서는 인코딩 문제가 자주 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;code&gt;Add-Content&lt;/code&gt;로 config나 hook 관련 파일을 직접 수정하는 것은 피하는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UTF-8 BOM 없이 쓰려면 .NET의 &lt;code&gt;WriteAllText&lt;/code&gt;를 쓰는 방식이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Python sanitizer 스크립트를 만든다.&lt;/p&gt;
&lt;pre class=&quot;julia&quot;&gt;&lt;code&gt;# === Windows PowerShell ===

$pyScript = &quot;$HOME\.codex\.sanitize.py&quot;
New-Item -ItemType Directory -Force -Path &quot;$HOME\.codex&quot; | Out-Null

$pyContent = @'
import json, sys
from pathlib import Path

hooks_path = Path.home() / &quot;.codex/hooks.json&quot;

if hooks_path.exists():
    try:
        d = json.loads(hooks_path.read_text(encoding=&quot;utf-8&quot;))
        if &quot;PostToolUse&quot; in d.get(&quot;hooks&quot;, {}):
            del d[&quot;hooks&quot;][&quot;PostToolUse&quot;]
            hooks_path.write_text(json.dumps(d, indent=2), encoding=&quot;utf-8&quot;)
            print(&quot;[codex-omx-sanitize] PostToolUse removed from hooks.json&quot;, file=sys.stderr)
    except Exception:
        pass
'@

[System.IO.File]::WriteAllText(
    $pyScript,
    $pyContent,
    [System.Text.UTF8Encoding]::new($false)
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 PowerShell &lt;code&gt;$PROFILE&lt;/code&gt;에 자동 실행 블록을 넣는다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;# === Windows PowerShell ===

if (-not (Test-Path $PROFILE)) {
    New-Item -ItemType File -Path $PROFILE -Force | Out-Null
}

$marker = '### CODEX_HOOK_SANITIZER_BEGIN ###'
$existing = Get-Content $PROFILE -Raw -Encoding UTF8 -ErrorAction SilentlyContinue

if (-not $existing) {
    $existing = &quot;&quot;
}

if ($existing -notmatch [regex]::Escape($marker)) {
    $sanitizerBlock = @'

### CODEX_HOOK_SANITIZER_BEGIN ###
function Invoke-CodexOmxSanitize {
    $script = Join-Path $HOME &quot;.codex\.sanitize.py&quot;
    if (Test-Path $script) {
        python $script 2&amp;gt;&amp;amp;1 | ForEach-Object {
            if ($_) { Write-Host $_ -ForegroundColor DarkGray }
        }
    }
}

Invoke-CodexOmxSanitize
### CODEX_HOOK_SANITIZER_END ###
'@

    [System.IO.File]::WriteAllText(
        $PROFILE,
        $existing + $sanitizerBlock,
        [System.Text.UTF8Encoding]::new($false)
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록 후 바로 적용하려면 profile을 다시 로드한다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;. $PROFILE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증은 hooks 목록을 확인하면 된다.&lt;/p&gt;
&lt;pre class=&quot;scilab&quot;&gt;&lt;code&gt;python -c &quot;import json, pathlib; p=pathlib.Path.home()/'.codex/hooks.json'; print(list(json.load(open(p, encoding='utf-8'))['hooks'].keys()))&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기대 결과는 PostToolUse가 없는 상태다.&lt;/p&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;['SessionStart', 'PreToolUse', 'UserPromptSubmit', 'Stop']&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. Windows 인코딩 문제를 피하는 법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows PowerShell 5.1에서는 인코딩 문제가 자주 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 한국어 Windows 환경에서는 CP949가 섞일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;config.toml&lt;/code&gt;, &lt;code&gt;hooks.json&lt;/code&gt;, &lt;code&gt;agents/*.toml&lt;/code&gt; 같은 파일이 깨지면 Codex나 OMX가 바로 죽을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔한 문제는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;UnicodeDecodeError: 'cp949' codec can't decode byte ...
TOML parse error
Cannot declare ('servers', 'java') twice&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python으로 TOML을 검증할 때는 인코딩을 명시한다.&lt;/p&gt;
&lt;pre class=&quot;csp&quot;&gt;&lt;code&gt;python -c &quot;import tomllib; tomllib.loads(open(r'$HOME\.codex\config.toml', 'r', encoding='utf-8').read()); print('OK')&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 쓸 때는 다음 패턴을 쓰는 편이 안전하다.&lt;/p&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;[System.IO.File]::WriteAllText(
    $path,
    $content,
    [System.Text.UTF8Encoding]::new($false)
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Out-File -Encoding UTF8&lt;/code&gt;이나 &lt;code&gt;Add-Content&lt;/code&gt;는 환경에 따라 BOM 또는 CP949 문제가 섞일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 기존 UTF-8 파일 끝에 CP949 내용이 붙으면 혼합 인코딩이 되어 더 찾기 어려운 문제가 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. config.toml이 손상됐을 때&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex 설정이 깨졌다면 먼저 TOML 유효성을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;$cfg = &quot;$HOME\.codex\config.toml&quot;

python -c &quot;import tomllib; tomllib.loads(open(r'$cfg', 'r', encoding='utf-8').read())&quot; 2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류가 나면 백업 후 재설정한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;Move-Item $cfg &quot;$cfg.broken.$(Get-Date -Format 'yyyyMMdd-HHmmss')&quot; -Force
omx setup --force&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 필요한 patch나 설정을 다시 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 깨진 파일을 계속 덧붙여 고치려고 하지 않는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TOML은 중복 섹션 하나만 있어도 전체 설정이 죽을 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. Azure 관련 오류를 볼 때의 기준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Azure 연결 문제는 보통 endpoint, key, deployment 세 가지로 나눠서 본다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;404 Resource not found:
- base_url이 올바른지 확인
- endpoint가 실제 Azure 리소스와 맞는지 확인

401 Unauthorized:
- API key가 endpoint와 같은 리소스의 키인지 확인
- Process scope와 User scope 환경변수가 다른지 확인

API version not supported:
- v1 엔드포인트에서는 api-version이 불필요한지 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수는 Process, User, Machine scope를 나눠서 봐야 한다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;Write-Host &quot;=== Process ===&quot;
$env:AZURE_OPENAI_API_KEY.Length
$env:AZURE_OPENAI_ENDPOINT

Write-Host &quot;=== User ===&quot;
[Environment]::GetEnvironmentVariable('AZURE_OPENAI_API_KEY', 'User').Length
[Environment]::GetEnvironmentVariable('AZURE_OPENAI_ENDPOINT', 'User')

Write-Host &quot;=== Machine ===&quot;
[Environment]::GetEnvironmentVariable('AZURE_OPENAI_API_KEY', 'Machine')
[Environment]::GetEnvironmentVariable('AZURE_OPENAI_ENDPOINT', 'Machine')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 PowerShell 창의 Process scope가 예전 값을 들고 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때는 새 PowerShell 창을 여는 것이 가장 단순하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. &lt;code&gt;$team&lt;/code&gt; 진행 상태 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PowerShell에는 Linux의 &lt;code&gt;watch&lt;/code&gt;가 기본으로 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 루프를 만들어 worker 상태를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# === 별도 PowerShell 창 또는 새 psmux pane ===

Set-Location &quot;$HOME\work\my-project&quot;

while ($true) {
    Clear-Host

    Write-Host &quot;=== worker status ===&quot;
    Get-ChildItem &quot;.omx\state\team\*\workers\worker-*\status.json&quot; -ErrorAction SilentlyContinue |
        Get-Content -Encoding UTF8 |
        Select-String -Pattern '(state|reason)' |
        Select-Object -First 10

    Write-Host
    Write-Host &quot;=== git log ===&quot;
    git log --all --oneline 2&amp;gt;$null | Select-Object -First 10

    Start-Sleep -Seconds 5
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;worker 상태가 &lt;code&gt;unknown&lt;/code&gt;이라고 해서 무조건 실패는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX의 status 갱신이 실제 worker 종료 시점과 어긋날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 완료 여부는 git log를 같이 확인하는 편이 좋다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;git log --all --oneline&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;omx(team): merge worker-N&lt;/code&gt; 같은 커밋이 있으면 worker가 작업을 마쳤을 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. &lt;code&gt;$team&lt;/code&gt;보다 &lt;code&gt;$ralph&lt;/code&gt;가 나은 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;$team&lt;/code&gt;은 병렬 작업에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 모든 작업에 맞는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 흐름을 끝까지 완성해야 한다면 &lt;code&gt;$ralph&lt;/code&gt;가 더 낫다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;$ralph가 나은 경우

- 단일 책임자가 끝까지 완성해야 하는 작업
- 기능 하나를 설계부터 테스트까지 이어서 봐야 하는 작업
- 병렬로 나누면 오히려 충돌이 커지는 작업&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 &lt;code&gt;$team&lt;/code&gt;이 맞는 경우는 분명히 나눌 수 있는 작업이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;$team이 나은 경우

- 모듈별 병렬 구현
- 테스트 / 문서 / 리팩토링 분리
- 서로 다른 파일 영역을 맡길 수 있는 작업&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 나눌 수 없는데 worker만 늘리면 속도가 빨라지지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 병합 비용과 mailbox 통신 비용이 커진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;16. 실전에서 기억할 실행 순서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 실행 순서를 하나로 정리하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 사용자 홈 디렉토리 아래 작업 폴더 생성
2. git init
3. 초기 파일 생성
4. git add / commit
5. tmux new-session -s codex
6. psmux 세션 안에서 omx --madmax --xhigh
7. 먼저 $deep-interview로 범위 정리
8. 필요하면 $ralplan으로 계획 생성
9. 단일 완성은 $ralph
10. 병렬 구현은 $team&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 순서를 지키면 대부분의 &lt;code&gt;$team&lt;/code&gt; 실행 문제를 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 세 가지는 습관처럼 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- psmux 안에서 실행했는가?
- 깨끗한 git repo인가?
- 작업 단위가 충분히 작고 명확한가?&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17. 정리 / 배운 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex 단독 실행과 OMX 멀티 worker 실행은 전제가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX &lt;code&gt;$team&lt;/code&gt;은 psmux, git repo, worktree, hook 설정이 맞아야 안정적으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서는 인코딩, 환경변수 scope, hooks.json false-positive를 함께 관리해야 한다.&lt;/p&gt;</description>
      <category>AX</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/242</guid>
      <comments>https://6kitt-hack.tistory.com/242#entry242comment</comments>
      <pubDate>Wed, 13 May 2026 16:38:53 +0900</pubDate>
    </item>
    <item>
      <title>[Stablecoin] 신용카드 결제와 스테이블코인 결제 비교 - 승인, 매입, 정산 구조로 이해하기</title>
      <link>https://6kitt-hack.tistory.com/241</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. TL;DR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드 결제는 &amp;ldquo;지금 돈이 이동했다&amp;rdquo;가 아니라 &amp;ldquo;결제를 승인하고 나중에 정산한다&amp;rdquo;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인 결제는 신용을 새로 만드는 구조가 아니라, 이미 보유한 디지털 자산을 조건에 맞게 이전하는 구조에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘의 차이를 이해하려면 결제 완료 화면보다 승인, 매입, 정산, 자금 이동 시점을 나눠서 봐야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 왜 신용카드와 스테이블코인을 비교해야 하나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로 보면 둘 다 결제 수단이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카드를 긁으면 결제가 끝난 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인을 보내도 결제가 끝난 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서는 둘 다 &amp;ldquo;돈을 냈다&amp;rdquo;로 느껴진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 내부 구조는 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드는 승인과 실제 정산 사이에 시간 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 이미 있는 자산을 네트워크 위에서 이전하는 구조에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이를 모르면 스테이블코인 결제를 단순히 &amp;ldquo;카드 결제를 블록체인으로 바꾼 것&amp;rdquo;처럼 오해하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 핵심은 카드냐 코인이냐가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 돈이 실제로 움직이는가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누가 신용을 제공하는가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;승인 이후 정산 리스크를 누가 부담하는가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문들이 더 중요하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 신용카드 결제 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드 결제는 보통 승인, 매입, 정산 흐름으로 나눠서 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 매장에서 카드를 긁거나 온라인에서 카드 정보를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 카드사가 결제 가능 여부를 확인하고 승인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용자 화면에는 결제 완료가 뜬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 순간 가맹점 계좌에 돈이 바로 들어왔다고 보기는 어렵다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사용자 결제 요청
&amp;rarr; 카드 승인
&amp;rarr; 사용자에게 결제 완료 표시
&amp;rarr; 매출전표 생성
&amp;rarr; 매입 처리
&amp;rarr; 정산 주기별 대금 지급&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &amp;ldquo;승인&amp;rdquo;은 결제를 해도 된다는 허가에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;매입&amp;rdquo;은 카드사가 가맹점의 매출전표를 받아들이는 단계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;정산&amp;rdquo;은 실제로 가맹점에게 돈이 지급되는 단계다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서는 승인 시점에 결제가 끝난 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 가맹점 입장에서는 정산 시점까지 기다려야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 신용카드가 신용 기반 결제 수단이기 때문에 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 지금 당장 현금을 내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카드사는 사용자의 신용을 기반으로 결제를 승인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 카드사는 사용자에게 대금을 청구하고, 가맹점에는 정산 주기에 맞춰 돈을 지급한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 신용카드 결제에서 PG사는 무엇을 하나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온라인 결제에서는 PG사가 자주 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PG사는 Payment Gateway의 약자로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 말하면 온라인 가맹점과 카드사 사이를 연결하는 결제 중개자다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가맹점이 모든 카드사와 직접 연동하지 않아도 결제를 받을 수 있게 해준다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사용자
&amp;rarr; 온라인 가맹점
&amp;rarr; PG사
&amp;rarr; 카드사
&amp;rarr; 승인 결과 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PG사는 결제 요청을 중계하고, 승인 결과를 전달하고, 매출 데이터를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가맹점 입장에서는 PG사를 통해 여러 결제 수단을 한 번에 붙일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 PG사가 있다고 해서 카드 결제의 승인과 정산 구조가 사라지는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여전히 승인과 실제 정산 사이에는 절차와 시간이 존재한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 스테이블코인 결제 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 특정 자산 가치에 연동되도록 설계된 디지털 토큰이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 달러 가치에 연동된 스테이블코인은 1토큰이 1달러에 가깝게 유지되도록 설계된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결제 관점에서 중요한 점은 &amp;ldquo;신용&amp;rdquo;이 아니라 &amp;ldquo;보유 자산&amp;rdquo;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인 결제는 사용자가 이미 가진 토큰을 상대방에게 이전하는 구조에 가깝다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사용자 지갑
&amp;rarr; 스테이블코인 전송
&amp;rarr; 블록체인 네트워크 기록
&amp;rarr; 수신자 지갑 잔액 증가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서는 카드사처럼 사용자의 신용을 대신 제공하는 주체가 핵심이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 이미 보유한 토큰을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신자는 네트워크에서 전송이 확정되면 해당 토큰을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 스테이블코인 결제는 선불 충전형 구조와 비슷하게 설명할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 자금을 보유하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 그 자금을 결제에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용을 새로 만들어 결제하는 것이 아니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 신용카드와 스테이블코인의 가장 큰 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 차이는 결제의 출발점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드는 신용에서 출발한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 보유 자산에서 출발한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;신용카드:
아직 사용자가 돈을 직접 내지 않았지만,
카드사가 신용을 바탕으로 결제를 승인한다.

스테이블코인:
사용자가 이미 가진 토큰을 상대방에게 전송한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이 때문에 결제 이후 구조도 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드는 승인 이후 매입과 정산 과정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 네트워크 전송과 확정이 핵심이다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;신용카드 결제의 핵심 질문:
이 사용자가 나중에 돈을 갚을 수 있는가?

스테이블코인 결제의 핵심 질문:
이 사용자가 지금 보낼 수 있는 토큰을 가지고 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드에서는 카드사가 신용 리스크를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인에서는 지갑 잔액, 토큰 전송, 네트워크 확정이 중요해진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 승인 완료와 정산 완료를 구분해야 한다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결제 화면에서 &amp;ldquo;완료&amp;rdquo;라고 뜬다고 해서 모든 구조가 끝난 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드에서 완료는 보통 승인 완료에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정산 완료는 뒤에서 따로 진행된다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;신용카드

사용자 관점:
승인 완료 = 결제 완료처럼 보임

가맹점 관점:
정산 완료 = 실제 대금 수령&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인 결제에서는 전송이 네트워크에 기록되고 확정되는지가 중요하다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;스테이블코인

사용자 관점:
전송 요청 생성

수신자 관점:
네트워크 확정 후 잔액 반영&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 사용자에게는 결제 완료처럼 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 내부에서는 완료의 의미가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드는 승인 완료와 정산 완료가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 전송 요청과 네트워크 확정이 다르다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 선불 충전형 카드와 스테이블코인의 유사점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인 결제는 신용카드보다 선불 충전형 카드에 가깝게 설명할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선불 충전형 카드는 먼저 돈을 충전한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 충전된 금액 안에서 결제한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;선불 충전형 카드

충전
&amp;rarr; 잔액 보유
&amp;rarr; 결제 요청
&amp;rarr; 잔액 차감&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인도 사용자가 먼저 토큰을 보유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 보유한 토큰을 전송한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;스테이블코인 결제

토큰 보유
&amp;rarr; 결제 요청
&amp;rarr; 토큰 전송
&amp;rarr; 수신자 잔액 반영&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 관점에서는 둘 다 &amp;ldquo;이미 있는 자금을 사용한다&amp;rdquo;는 공통점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 선불카드는 보통 특정 사업자의 장부와 결제망 안에서 잔액이 관리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 블록체인 네트워크와 지갑을 통해 토큰 이동이 기록된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 신용카드, 선불카드, 스테이블코인 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 가지를 한 번에 비교하면 차이가 더 잘 보인다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;신용카드

- 신용 기반 결제
- 승인과 정산이 분리됨
- 사용자는 나중에 카드대금을 납부함
- 가맹점은 정산 주기에 따라 대금을 받음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드는 지금 가진 돈보다 &amp;ldquo;갚을 수 있는 신용&amp;rdquo;이 중요하다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;선불 충전형 카드

- 사전 충전 기반 결제
- 충전 잔액 안에서만 사용 가능
- 신용 창출 없음
- 사업자 장부에서 잔액 관리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선불카드는 이미 충전한 잔액이 중요하다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;스테이블코인

- 보유 토큰 기반 결제
- 지갑에서 지갑으로 토큰 이전
- 신용 창출 없음
- 네트워크 기록과 확정이 중요함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 이미 가진 토큰과 전송 확정이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면 신용카드는 &amp;ldquo;나중에 갚는 결제&amp;rdquo;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선불카드와 스테이블코인은 &amp;ldquo;이미 있는 자금을 쓰는 결제&amp;rdquo;에 가깝다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 스테이블코인이 카드 결제를 완전히 대체한다는 뜻은 아니다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인 결제가 빠르고 직접적인 구조를 가질 수 있다고 해서 카드 결제를 그대로 대체한다고 보기는 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카드 결제에는 소비자 보호, 취소, 환불, 분쟁 처리, 부정거래 탐지 같은 기능이 붙어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가맹점 정산 구조도 단순한 송금보다 복잡하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 스테이블코인은 전송 자체는 단순해 보일 수 있지만, 실제 결제 서비스로 만들려면 추가 설계가 필요하다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;스테이블코인 결제에서 추가로 고민할 것

- 결제 취소
- 환불
- 오입금
- 사기 거래
- 지갑 분실
- 가격 안정성
- 규제 준수
- 가맹점 회계 처리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 스테이블코인은 &amp;ldquo;카드보다 무조건 낫다&amp;rdquo;가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 결제 구조를 가진 수단이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 비교할 때는 장단점보다 구조 차이를 먼저 봐야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 결제 구조를 이해할 때의 핵심 질문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결제 수단을 비교할 때는 다음 질문을 던지면 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 결제 시점에 실제 돈이 이동하는가?
2. 신용을 제공하는 주체가 있는가?
3. 승인과 정산이 분리되어 있는가?
4. 결제 취소와 환불은 어떻게 처리되는가?
5. 가맹점은 언제 최종적으로 돈을 받는가?
6. 거래 기록은 어디에 남는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드는 승인과 정산이 분리된 신용 기반 결제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 보유 토큰을 이전하는 자산 기반 결제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이를 기준으로 보면 두 결제 수단의 역할이 더 명확해진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 정리 / 배운 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신용카드는 결제 승인과 실제 정산이 분리된 신용 기반 결제 구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스테이블코인은 신용을 만드는 것이 아니라 이미 보유한 토큰을 이전하는 구조에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘을 비교할 때는 &amp;ldquo;결제 완료 화면&amp;rdquo;이 아니라 &amp;ldquo;자금 이동 시점과 정산 구조&amp;rdquo;를 기준으로 봐야 한다.&lt;/p&gt;</description>
      <category>work</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/241</guid>
      <comments>https://6kitt-hack.tistory.com/241#entry241comment</comments>
      <pubDate>Tue, 12 May 2026 17:55:45 +0900</pubDate>
    </item>
    <item>
      <title>[Blockchain] ERC-3525 이해하기 - id, slot, value로 Semi-Fungible Token 구조 잡기</title>
      <link>https://6kitt-hack.tistory.com/240</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. TL;DR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525는 Semi-Fungible Token, 즉 반쯤 대체 가능한 토큰을 표현하기 위한 표준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 구조는 &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;slot&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt; 세 가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PBM처럼 &amp;ldquo;돈에 목적과 조건을 붙이는 구조&amp;rdquo;를 설계할 때 &lt;code&gt;slot&lt;/code&gt;을 정책 분류 기준으로 활용할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. ERC-3525가 필요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록체인 토큰을 처음 보면 보통 ERC-20과 ERC-721을 먼저 접한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-20은 서로 완전히 같은 토큰을 표현하기 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 1 USDC와 다른 1 USDC는 서로 구분할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-721은 서로 완전히 다른 토큰을 표현하기 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 NFT 하나하나는 고유한 &lt;code&gt;tokenId&lt;/code&gt;를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 현실의 자산이 항상 이 둘 중 하나로만 떨어지지 않는다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 자산은 고유한 식별자가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 금액처럼 쪼개거나 합칠 수 있는 값도 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 같은 조건을 가진 채권, 바우처, 포인트, 목적 기반 토큰은 &amp;ldquo;고유성&amp;rdquo;과 &amp;ldquo;수량성&amp;rdquo;을 동시에 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 지점에서 ERC-3525가 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525는 ERC-721과 호환되는 구조 위에 &lt;code&gt;value&lt;/code&gt;와 &lt;code&gt;slot&lt;/code&gt; 개념을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 NFT처럼 고유한 ID를 가지면서도, 같은 slot에 속한 토큰끼리는 value를 나누거나 옮길 수 있게 만든다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. ERC-20, ERC-721, ERC-1155와 비교하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 ERC-20은 가장 단순하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ERC-20

address &amp;rarr; balance&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소가 얼마를 가지고 있는지만 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 하나하나를 구분하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명하기 쉽고, 일반적인 화폐형 토큰에 잘 맞는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &amp;ldquo;이 돈은 어디에만 쓸 수 있다&amp;rdquo; 같은 개별 조건을 토큰 구조 자체에 넣기는 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-721은 반대 방향이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ERC-721

tokenId &amp;rarr; owner&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 토큰은 고유하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰마다 메타데이터를 다르게 둘 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 기본적으로 수량을 쪼개서 일부만 전송하는 구조는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NFT 한 개를 0.3개만 보내는 식의 동작은 ERC-721 기본 모델과 맞지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-1155는 여러 종류의 토큰을 하나의 컨트랙트에서 다룰 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ERC-1155

id &amp;rarr; address &amp;rarr; balance&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 컨트랙트 안에서 여러 token id를 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 아이템처럼 &amp;ldquo;아이템 종류&amp;rdquo;와 &amp;ldquo;수량&amp;rdquo;이 함께 필요한 경우에 자주 설명된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 ERC-3525는 여기서 한 단계 다른 모델을 쓴다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;ERC-3525

tokenId &amp;rarr; slot
tokenId &amp;rarr; value
tokenId &amp;rarr; owner&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525에서는 토큰이 단순히 &lt;code&gt;id&lt;/code&gt;와 &lt;code&gt;balance&lt;/code&gt;만 갖는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 tokenId는 고유한 객체처럼 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 안에 value가 들어 있고, 그 tokenId는 특정 slot에 속한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. ERC-3525의 핵심 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525는 다음 세 가지로 이해하면 된다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;ERC-3525 Token

id    = 개별 토큰의 고유 식별자
slot  = 토큰이 속한 분류 또는 조건 그룹
value = 토큰 안에 들어 있는 수량 또는 금액&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;id&lt;/code&gt;는 ERC-721의 tokenId와 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 하나를 고유하게 식별한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;value&lt;/code&gt;는 ERC-20의 balance와 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰 안에 들어 있는 양을 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;slot&lt;/code&gt;은 ERC-3525를 이해할 때 가장 중요한 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slot은 같은 성격을 가진 토큰을 묶는 기준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 slot에 속한 토큰들은 서로 value를 옮기거나 합치는 방식으로 다룰 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 광고비 지원금 토큰을 생각해볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;tokenId: 1
slot: 1001
value: 50

tokenId: 2
slot: 1001
value: 30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 토큰은 tokenId가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 각각 고유한 토큰이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 slot이 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 같은 조건을 가진 토큰으로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 value 기준으로는 합치거나 나누는 동작을 설계할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 slot이 다르면 같은 value를 가지고 있어도 같은 정책 그룹으로 보면 안 된다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;tokenId: 3
slot: 2001
value: 50&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 토큰은 value가 50으로 같아도 slot이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 tokenId 1과 같은 종류의 돈이라고 보기 어렵다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. slot을 어떻게 설계할 수 있나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slot은 단순한 숫자처럼 보이지만, 실제로는 분류 기준을 압축한 값으로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 목적 기반 토큰을 설계한다고 하자.&lt;/p&gt;
&lt;pre class=&quot;thrift&quot;&gt;&lt;code&gt;struct SlotPolicy {
    string issuer;
    string currency;
    string purpose;
    string merchantCategory;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조는 설명용 예시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 컨트랙트에서는 string을 그대로 많이 쓰기보다 &lt;code&gt;bytes32&lt;/code&gt;, &lt;code&gt;uint256&lt;/code&gt;, &lt;code&gt;enum&lt;/code&gt; 등을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 slot이 &amp;ldquo;정책 묶음&amp;rdquo;을 나타낼 수 있다는 점이다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;slot 1001

issuer           = Seoul City
currency         = KRW-stable
purpose          = transportation
merchantCategory = public-transport&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 slot은 교통비 지원금 같은 목적 기반 토큰을 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 slot을 가진 토큰은 같은 발행기관, 같은 통화, 같은 목적, 같은 사용처 조건을 공유한다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;광고 집행 시나리오로 바꾸면 이렇게 잡을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;slot 2001

issuer           = Advertiser A
currency         = USDC
purpose          = marketing
merchantCategory = ad-platform&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 slot 2001에 속한 토큰은 광고 플랫폼 결제 목적의 자금으로 해석할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 설계하면 &amp;ldquo;그냥 100 USDC&amp;rdquo;가 아니라 &amp;ldquo;광고 집행 목적으로 묶인 100 USDC&amp;rdquo;처럼 다룰 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. ERC-3525 전송을 어떻게 이해해야 하나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-20은 주소에서 주소로 balance를 보낸다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Alice address &amp;rarr; Bob address
10 token transfer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-721은 tokenId의 소유자를 바꾼다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;tokenId 1 owner:
Alice &amp;rarr; Bob&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525는 tokenId 안에 들어 있는 value를 이동시키는 방식으로 이해할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;tokenId 1
slot 1001
value 50

tokenId 2
slot 1001
value 30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 tokenId 1에서 tokenId 2로 value 10을 옮기면 이렇게 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Before

tokenId 1: slot 1001, value 50
tokenId 2: slot 1001, value 30

After

tokenId 1: slot 1001, value 40
tokenId 2: slot 1001, value 40&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 토큰은 서로 다른 id를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 slot이 같기 때문에 value 이동이 가능하다고 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 ERC-3525의 Semi-Fungible한 특징이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고유한 토큰이지만, 같은 slot 안에서는 value가 대체 가능하게 움직인다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. PBM 관점에서 ERC-3525를 보면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PBM은 Purpose Bound Money의 약자로 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 그대로 특정 목적이나 조건에 묶인 돈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 돈은 돈 자체에 조건을 담기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 특정 가맹점에서만 쓰게 하거나, 특정 목적에만 쓰게 하려면 바우처나 별도 서버 정책이 필요해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525를 쓰면 이 목적 조건을 slot 설계로 표현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;PBM 관점

id    = 개별 지급 단위
slot  = 사용 목적과 정책 조건
value = 실제 사용할 수 있는 금액&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 PBM 토큰을 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;tokenId: 501
slot: marketing_ad_usdc
value: 100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 토큰은 단순한 100 USDC가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마케팅 광고 집행이라는 목적에 묶인 100 USDC처럼 해석할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증 로직도 slot을 기준으로 설계할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;function canUseForAdPayment(uint256 tokenId) public view returns (bool) {
    return slotOf(tokenId) == MARKETING_AD_SLOT;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 개념 설명용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 결제 요청이 들어왔을 때 tokenId의 slot을 확인하고, 해당 목적에 맞는지 판단할 수 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 구체적으로는 사용처도 slot 조건에 포함할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;struct PaymentRequest {
    uint256 tokenId;
    address merchant;
    uint256 amount;
}

function validatePayment(PaymentRequest memory req) public view returns (bool) {
    SlotPolicy memory policy = slotPolicyOf(slotOf(req.tokenId));

    if (!isAllowedMerchant(policy.merchantCategory, req.merchant)) {
        return false;
    }

    if (valueOf(req.tokenId) &amp;lt; req.amount) {
        return false;
    }

    return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드도 실제 배포용이 아니라 개념 설명용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PBM에서 중요한 질문은 &amp;ldquo;이 돈이 이 목적에 쓰여도 되는가?&amp;rdquo;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525의 slot은 이 질문에 답하기 위한 분류 기준이 될 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 온체인과 오프체인 정책 나누기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 정책을 slot에 넣으면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slot에 너무 많은 조건을 넣으면 구조가 복잡해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 모든 정책을 서버에서만 검사하면 PBM을 쓰는 이유가 약해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 정책을 온체인과 오프체인으로 나누는 기준이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;온체인에 두기 좋은 조건

- 발행기관
- 통화
- 사용 목적
- 사용처 카테고리
- 기본 한도&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 조건은 비교적 안정적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰의 성격을 정하는 기준이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 slot으로 묶기 좋다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;오프체인에 두기 좋은 조건

- 실시간 성과 지표
- CTR
- CPA
- 외부 광고 플랫폼 상태
- 시간에 따라 자주 바뀌는 판단&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 조건은 자주 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 온체인에 기록하면 비용과 복잡도가 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 정책 서버나 외부 검증 레이어에서 처리하는 편이 자연스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 slot은 토큰의 기본 성격을 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오프체인 정책은 실시간 판단을 보완한다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;ERC-3525 slot:
이 돈은 어떤 목적의 돈인가?

오프체인 정책:
지금 이 상황에서 이 돈을 써도 되는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나누면 PBM 구조가 훨씬 단순해진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. ERC-3525를 한 문장으로 정리하면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525는 NFT처럼 고유한 id를 가지면서, 같은 slot 안에서는 value를 나누고 옮길 수 있는 Semi-Fungible Token 표준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PBM 관점에서는 slot을 &amp;ldquo;돈의 목적과 조건을 묶는 기준&amp;rdquo;으로 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 ERC-3525는 단순히 NFT와 FT의 중간 형태가 아니라, 조건이 붙은 디지털 자산을 설계하기 위한 모델로 이해할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 정리 / 배운 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERC-3525의 핵심은 &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;slot&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;slot은 같은 성격의 토큰을 묶는 기준이고, PBM에서는 정책 조건을 표현하는 단위로 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온체인 slot은 기본 목적을, 오프체인 정책은 실시간 판단을 맡기는 식으로 나누면 구조가 단순해진다.&lt;/p&gt;</description>
      <category>work</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/240</guid>
      <comments>https://6kitt-hack.tistory.com/240#entry240comment</comments>
      <pubDate>Mon, 11 May 2026 17:54:19 +0900</pubDate>
    </item>
    <item>
      <title>[협업] 스프린트 목표가 너무 클 때 - end-to-end 기준으로 백로그 줄이기</title>
      <link>https://6kitt-hack.tistory.com/239</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. TL;DR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트가 실패하는 가장 흔한 이유는 팀원이 일을 안 해서가 아니라, 목표가 너무 커서 무엇을 먼저 검증할지 흐려지기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;스터디 웹사이트 개편&amp;rdquo;처럼 큰 목표는 스프린트 목표가 아니라 프로젝트 목표에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트 목표는 1~2주 안에 검증 가능한 가장 작은 end-to-end 흐름으로 줄여야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 문제 상황 / 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀프로젝트에서 스프린트 구조를 적용할 때 가장 먼저 부딪히는 문제는 목표가 너무 크다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 다들 의욕이 있어서 큰 목표를 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;스터디 웹사이트 개편&amp;rdquo;이라는 목표를 세웠다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 회의에서는 자연스럽게 이런 기능들이 나온다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 로그인
- 회원가입
- 스터디 모집글 작성
- 스터디 신청
- 관리자 페이지
- 신청자 목록 관리
- 신청 완료 알림
- 마감 상태 표시
- 통계 대시보드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 목록 자체가 나쁜 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 이걸 그대로 스프린트 목표로 가져가는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;스터디 웹사이트 개편&amp;rdquo;은 너무 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원이 각자 무엇을 먼저 해야 하는지 판단하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇이 끝나야 스프린트가 성공인지도 애매해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트는 프로젝트 전체를 완성하는 단위가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트는 가장 작은 흐름을 실제로 검증하는 단위다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 큰 목표를 바로 티켓으로 쪼개기 전에, 먼저 목표를 줄이는 과정이 필요하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 삽질 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 보통 기능 목록을 그대로 백로그로 만든다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Sprint Backlog

- 로그인 구현
- 회원가입 구현
- 모집글 CRUD 구현
- 신청폼 구현
- 관리자 페이지 구현
- 신청자 목록 구현
- 알림 기능 구현&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 얼핏 보면 정리된 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 스프린트에서는 금방 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인이 안 끝나면 신청폼을 못 만드는지 애매하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관리자 페이지가 없으면 신청 기능을 검증할 수 없는지 애매하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알림 기능이 없어도 MVP가 되는지 애매하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 기능은 많지만 선후관계가 정리되지 않은 상태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 스프린트를 시작하면 각자 자기 파트만 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 마지막에 합쳐보면 end-to-end로 되는 흐름이 없다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;로그인은 일부 됨
신청폼 UI는 있음
DB 저장은 안 붙음
관리자 페이지는 목업만 있음
알림은 시작 못함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 상태는 &amp;ldquo;열심히 했는데 보여줄 게 없는 스프린트&amp;rdquo;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 기능은 조금씩 진행됐지만, 사용자가 실제로 통과할 수 있는 흐름은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 스프린트 목표는 기능 묶음이 아니라 검증 가능한 흐름이어야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 해결 / 핵심 내용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 목표를 줄일 때는 먼저 프로젝트 목표와 스프린트 목표를 분리해야 한다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;Project Goal:
스터디 웹사이트를 개편한다.

Sprint Goal:
사용자가 모집 중인 스터디를 확인하고 신청서를 제출한 뒤,
운영진이 신청 내역을 확인하는 최소 흐름을 검증한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 목표는 방향이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트 목표는 이번 주에 실제로 검증할 흐름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스터디 웹사이트 전체를 다 만들 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &amp;ldquo;신청자가 보고 &amp;rarr; 신청하고 &amp;rarr; 운영진이 확인한다&amp;rdquo;는 한 줄 흐름만 통과시키면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 기준은 end-to-end다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사용자 진입
&amp;rarr; 모집 중인 스터디 확인
&amp;rarr; 신청 정보 입력
&amp;rarr; 신청 완료 확인
&amp;rarr; 운영진 신청 내역 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름이 통과하면 작아도 검증된 결과물이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 로그인, 알림, 통계가 있어도 이 흐름이 안 되면 스프린트 결과물로 설명하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 스프린트1에서는 기능을 과감히 뺀다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;스프린트1에 포함할 것

- 모집 중인 스터디 목록 보기
- 신청폼 제출
- 신청 완료 상태 표시
- 운영진용 신청 내역 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도면 최소 흐름을 검증할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 아래 기능은 뒤로 미룰 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;스프린트1에서 제외할 것

- 로그인
- 회원가입
- 이메일 알림
- 통계 대시보드
- 스터디장별 권한 관리
- 모집글 에디터&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제외한다는 것은 안 만든다는 뜻이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 스프린트의 검증 목표에 꼭 필요하지 않으니 뒤로 미룬다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 유저스토리를 작성한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;As a 스터디 신청자,
I want 모집 중인 스터디를 확인하고 바로 신청하고 싶다,
So that 운영진에게 따로 문의하지 않고 참여 의사를 전달할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저스토리는 기능명이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 어떤 불편함을 해결하고 싶은지 담아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 유저스토리에서는 &amp;ldquo;운영진에게 따로 문의하지 않고&amp;rdquo;가 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 신청자는 여러 채널을 돌아다니며 정보를 묻고 싶지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영진 유저스토리도 따로 잡는다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;As an 운영진,
I want 신청 내역을 한 곳에서 확인하고 싶다,
So that 구글폼, 카톡, 노션 댓글로 흩어진 신청 정보를 수동 취합하지 않아도 된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문장은 관리자 페이지라는 기능보다 운영진의 문제를 먼저 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영진은 신청 정보를 수동으로 모으는 시간이 부담이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 신청 내역 확인 기능이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 수락 조건을 만든다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Acceptance Criteria

- 사용자는 모집 중인 스터디 목록을 볼 수 있다.
- 사용자는 이름, 연락처, 희망 스터디를 입력해 신청할 수 있다.
- 신청 완료 후 완료 상태를 확인할 수 있다.
- 운영진은 제출된 신청 내역을 목록으로 확인할 수 있다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수락 조건은 테스트 가능해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;편하게 신청할 수 있다&amp;rdquo;는 테스트하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;신청 완료 후 완료 상태를 확인할 수 있다&amp;rdquo;는 테스트할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수락 조건까지 정한 뒤에 티켓으로 쪼갠다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;User Story:
스터디 신청자는 모집 중인 스터디를 확인하고 바로 신청하고 싶다.

Tickets:
- 모집 중인 스터디 카드 UI 작성
- 신청폼 입력 필드 정의
- 신청 API mock 작성
- 신청 완료 화면 작성
- 신청 실패 문구 정의&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저스토리와 티켓은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저스토리는 왜 하는지 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;티켓은 실제로 무엇을 할지 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영진 유저스토리도 티켓으로 나눈다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;User Story:
운영진은 신청 내역을 한 곳에서 확인하고 싶다.

Tickets:
- 신청자 목록 테이블 UI 작성
- 신청 내역 조회 API mock 작성
- 스터디별 필터 조건 정의
- 빈 목록 상태 화면 작성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 티켓을 너무 크게 만들지 않는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;관리자 페이지 구현&amp;rdquo;은 너무 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 &amp;ldquo;신청자 목록 테이블 UI 작성&amp;rdquo;처럼 작게 쪼갠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트 보드에는 문서 작업도 넣는다.&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;Sprint Board

- [문서] 스프린트1 end-to-end 흐름 정의
- [문서] 신청자 / 운영진 유저스토리 작성
- [문서] 수락 조건 작성
- [설계] 신청폼 필드 정의
- [설계] 신청 데이터 응답 형식 정의
- [개발] 스터디 카드 UI 작성
- [개발] 신청폼 UI 작성
- [개발] 신청 내역 목록 UI 작성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 작업은 &amp;ldquo;개발 전 준비&amp;rdquo;가 아니라 실제 작업이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서가 없으면 팀원이 각자 다른 목표를 보고 개발하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 짧은 스프린트에서는 작은 문서 하나가 커뮤니케이션 비용을 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 형식도 미리 맞춰두면 좋다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;applicationId&quot;: &quot;app_001&quot;,
  &quot;studyId&quot;: &quot;study_frontend_basic&quot;,
  &quot;applicantName&quot;: &quot;홍길동&quot;,
  &quot;status&quot;: &quot;submitted&quot;,
  &quot;submittedAt&quot;: &quot;2026-05-08T10:00:00&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 예시는 거창한 API 명세서가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드와 백엔드가 같은 데이터를 상상하게 만드는 최소 약속이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크럼 공유도 &amp;ldquo;열심히 했다&amp;rdquo;가 아니라 상태가 보이게 써야 한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;어제 한 일
- [진행중] 신청폼 입력 필드 정의
  - 이름, 연락처, 희망 스터디 필드 확정
  - 신청 동기 필드 포함 여부 논의 중

오늘 할 일
- [완료예정] 신청폼 입력 필드 정의
  - 필수값 / 선택값 구분
  - 신청 완료 화면 문구 작성

이슈
- 연락처를 이메일로 받을지 전화번호로 받을지 결정 필요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 쓰면 팀원이 현재 상태를 바로 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇이 끝났고, 무엇이 막혔고, 무엇을 결정해야 하는지 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스 요청도 따로 남긴다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;Interface Request

요청 대상:
- 백엔드 담당자

필요한 값:
- studyId
- applicantName
- contact
- motivation
- submittedAt

필요 시점:
- 신청폼 UI와 API 연결 전&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말로만 요청하면 나중에 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청을 기록하면 누가 무엇을 기다리는지 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트에서 중요한 것은 모든 일을 많이 하는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 스프린트가 무엇을 검증하려는지 끝까지 유지하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;나쁜 스프린트 목표:
스터디 웹사이트 개편하기

좋은 스프린트 목표:
신청자가 스터디를 확인하고 신청한 뒤,
운영진이 신청 내역을 확인하는 최소 흐름 검증하기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표가 작아지면 팀원이 덜 일하는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 더 정확하게 일하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 만들지보다 무엇을 이번에 만들지 않을지가 분명해진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 정리 / 배운 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트 목표는 큰 프로젝트명을 그대로 가져오면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 작은 end-to-end 흐름을 기준으로 범위를 줄여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저스토리, 수락 조건, 티켓, 인터페이스 요청을 분리하면 협업 상태가 보이기 시작한다.&lt;/p&gt;</description>
      <category>work</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/239</guid>
      <comments>https://6kitt-hack.tistory.com/239#entry239comment</comments>
      <pubDate>Sun, 10 May 2026 17:50:54 +0900</pubDate>
    </item>
    <item>
      <title>[LLM] 생산성 도구 사용 시 인지부채&amp;middot;의도부채&amp;middot;기술부채 관리</title>
      <link>https://6kitt-hack.tistory.com/238</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;TL;DR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM이 코드를 많이 만들어낼수록, 문제는 코드 작성 속도가 아니라 시스템 이해와 검증으로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술부채는 코드에, 인지부채는 사람에게, 의도부채는 산출물에 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생산성 도구를 쓸수록 &amp;ldquo;무엇을 만들었나&amp;rdquo;보다 &amp;ldquo;무엇이 맞다고 검증됐나&amp;rdquo;를 봐야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 상황 / 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Martin Fowler의 2026년 4월 2일 Fragments 글은 LLM이 대량의 코드를 만들어내는 상황에서 팀이 시스템을 이해하지 못하게 되는 문제를 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 핵심 키워드로 나온 것이 Cognitive Debt, 즉 인지부채이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 글은 인지부채 하나만 보지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Margaret-Anne Storey의 관점을 인용하면서 시스템 건강을 세 층으로 나눈다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Technical debt lives in code.
Cognitive debt lives in people.
Intent debt lives in artifacts.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술부채는 코드에 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인지부채는 사람에게 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도부채는 문서, 테스트, 요구사항 같은 산출물에 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 &amp;ldquo;LLM이 코드를 빨리 짜주니 생산성이 올라간다&amp;rdquo; 정도로 읽으려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 글의 초점은 속도가 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속도가 빨라질수록 팀이 무엇을 이해하고, 무엇을 검증하고, 무엇을 의도했는지가 더 중요해진다는 쪽에 가까웠다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;삽질 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 이 내용을 단순히 기술부채 이야기로 정리하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM이 만든 코드가 늘어나면 코드 품질이 낮아지고, 나중에 유지보수가 어려워진다는 식으로 보면 익숙하기 때문이다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;LLM 사용 증가
&amp;rarr; 코드 증가
&amp;rarr; 유지보수 어려움
&amp;rarr; 기술부채 증가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 흐름만으로는 글의 핵심을 다 담기 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글에서 말하는 문제는 코드 자체만의 문제가 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀이 시스템을 설명하지 못하고, 변경 이유를 추적하지 못하고, 결과가 맞는지 판단하지 못하는 문제까지 포함했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기술부채 하나로 묶으면 인지부채와 의도부채가 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째로는 &amp;ldquo;AI가 사람의 사고를 대체한다&amp;rdquo;는 식으로 정리하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글에는 Shaw와 Nave의 논문을 인용한 부분이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 AI를 System 3로 보고, 사람이 외부에서 생성된 추론을 비판 없이 믿는 cognitive surrender를 설명한다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;cognitive offloading
= 의식적으로 판단한 뒤 일부 사고를 위임하는 것

cognitive surrender
= 비판 없이 외부 추론을 믿고 판단을 넘기는 것&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구분이 중요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM을 쓰는 것 자체가 문제가 아니라, 검토 없이 받아들이는 방식이 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 생산성 도구의 주의사항은 &amp;ldquo;쓰지 말자&amp;rdquo;가 아니라 &amp;ldquo;어디까지 맡기고 어디서 검증할지 정하자&amp;rdquo;에 가깝다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 / 핵심 내용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서 정리할 수 있는 핵심은 세 가지 부채를 분리해서 보는 것이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 기술부채
   위치: 코드
   문제: 구현 결정이 미래 변경 가능성을 떨어뜨림

2. 인지부채
   위치: 사람
   문제: 팀의 시스템 이해가 회복되는 속도보다 빠르게 무너짐

3. 의도부채
   위치: 산출물
   문제: 목표와 제약이 제대로 기록&amp;middot;유지되지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술부채는 익숙한 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 복잡해지고, 변경이 어려워지고, 나중에 수정 비용이 커지는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인지부채는 코드보다 사람 쪽에 붙어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM이 만든 결과물을 팀원이 이해하지 못한 채 병합하면, 시스템은 돌아가도 팀의 이해도는 떨어질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도부채는 산출물에 붙어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이 기능이 필요한지, 어떤 제약을 지켜야 하는지, 무엇을 검증해야 하는지가 문서나 테스트에 남지 않으면 의도는 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 관점에서 생산성 도구를 쓸 때의 주의사항은 다음처럼 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;코드 생성 속도만 보지 않는다.
팀의 이해가 유지되는지 본다.
의도와 제약이 산출물에 남는지 본다.
검증 가능한 기준이 있는지 본다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fowler가 인용한 Ajey Gore의 글에서는 코딩 에이전트가 실행을 맡을수록 비싼 것은 verification, 즉 검증이 된다고 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분도 생산성 도구 사용 시 중요한 경고로 읽힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 더 빨리 만드는 것보다, 그 코드가 맞는지 판단하는 체계를 만드는 일이 더 중요해진다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;기존 질문: 무엇을 만들었나?
바뀐 질문: 무엇을 검증했나?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 변화는 개발자의 역할도 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 기능을 더 많이 찍어내는 사람이 아니라, acceptance criteria를 정의하고, test harness를 설계하고, 애매한 케이스를 판단하는 사람이 중요해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 LLM 기반 생산성 도구를 잘 쓰려면 프롬프트를 잘 쓰는 것만으로는 부족하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇이 정답인지 정의하는 능력과, 그 정답을 확인하는 검증 구조가 필요하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리 / 배운 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM 생산성 도구의 리스크는 &amp;ldquo;코드 품질 저하&amp;rdquo; 하나로 끝나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술부채, 인지부채, 의도부채를 나눠 봐야 팀이 무엇을 잃고 있는지 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 중요한 질문은 &amp;ldquo;얼마나 빨리 만들었나&amp;rdquo;가 아니라 &amp;ldquo;이 결과를 이해하고 검증할 수 있나&amp;rdquo;이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Martin Fowler, &amp;ldquo;Fragments: April 2&amp;rdquo;, 2026-04-02.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AX</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/238</guid>
      <comments>https://6kitt-hack.tistory.com/238#entry238comment</comments>
      <pubDate>Sat, 9 May 2026 17:42:07 +0900</pubDate>
    </item>
    <item>
      <title>[Codex CLI + Azure AI Foundry] Apple Silicon Mac 개발환경 셋업 - classic endpoint 아니고 Foundry v1 방법</title>
      <link>https://6kitt-hack.tistory.com/237</link>
      <description>&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;TL;DR&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple Silicon Mac에 Codex CLI + OMX + Azure AI Foundry v1을 붙이는 과정을 정리했다.&lt;/li&gt;
&lt;li&gt;classic Azure OpenAI(&lt;code&gt;openai.azure.com&lt;/code&gt;)와 Foundry v1(&lt;code&gt;cognitiveservices.azure.com&lt;/code&gt;) 설정은 &lt;code&gt;wire_api&lt;/code&gt;와 &lt;code&gt;base_url&lt;/code&gt; 처리 방식이 다르다.&lt;/li&gt;
&lt;li&gt;삽질 포인트가 많아서 자동화 스크립트 3개로 정리했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Azure AI Foundry로 프로비저닝한 리소스를 Codex CLI에 붙이려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 가이드는 classic Azure OpenAI(&lt;code&gt;openai.azure.com&lt;/code&gt;) 기준으로 작성된 게 많은데, Foundry v1 리소스는 엔드포인트 형태가 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대상 환경은 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;macOS 26.x (Apple Silicon, arm64)&lt;/li&gt;
&lt;li&gt;Codex CLI 0.125+&lt;/li&gt;
&lt;li&gt;OMX v0.14+&lt;/li&gt;
&lt;li&gt;Azure AI Foundry 엔드포인트: &lt;code&gt;https://{resource-name}.cognitiveservices.azure.com/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;모델: &lt;code&gt;gpt-5.4-pro&lt;/code&gt; (전역), &lt;code&gt;gpt-5.4-mini&lt;/code&gt; (monitor), &lt;code&gt;gpt-5.4-pro&lt;/code&gt; (pro)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1578&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lD0eZ/dJMcacwig3X/oq5wmshuBnlrKk9ynQQBH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lD0eZ/dJMcacwig3X/oq5wmshuBnlrKk9ynQQBH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lD0eZ/dJMcacwig3X/oq5wmshuBnlrKk9ynQQBH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlD0eZ%2FdJMcacwig3X%2Foq5wmshuBnlrKk9ynQQBH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1578&quot; height=&quot;730&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1578&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 Azure for student 사용중이라 5.5는 리소스 사용이 불가하다고 떴다.&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;삽질 과정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;code&gt;brew install xxd&lt;/code&gt; &amp;mdash; &quot;No available formula&quot;&lt;/h3&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;brew install git curl wget unzip tmux xxd cmake pkg-config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;xxd&lt;/code&gt;가 Homebrew에 없다는 오류가 나면서 명령 전체가 중단됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;xxd&lt;/code&gt;는 macOS에 vim과 함께 기본 내장되어 있어서 brew로 설치할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 tmux를 비롯한 나머지 패키지가 전부 설치되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결은 &lt;code&gt;xxd&lt;/code&gt;를 목록에서 빼고 재실행이다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;brew install git curl wget unzip tmux cmake pkg-config&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. jdtls Eclipse 다운로드 서버 &amp;mdash; 50MB에 20분&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 가이드 명령은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;curl -fLo /tmp/jdtls.tar.gz \
  &quot;https://download.eclipse.org/jdtls/snapshots/jdt-language-server-latest.tar.gz&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eclipse 서버가 40KB/s 이하로 내려받아서 예상 소요 시간이 20분 넘게 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub API로 최신 릴리즈 URL을 가져오려고 했더니 이번엔 rate limit에 걸렸다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;KeyError: 'assets'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub API가 rate limit 응답을 반환하면 &lt;code&gt;assets&lt;/code&gt; 키 자체가 없어서 파싱 오류가 난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 가장 빠른 방법은 Homebrew였다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;brew install jdtls
which jdtls  # /opt/homebrew/bin/jdtls&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PATH도 자동으로 잡힌다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Azure &lt;code&gt;/openai/deployments&lt;/code&gt; &amp;mdash; 빈 배열 반환&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포된 모델을 확인하려고 deployments API를 호출했다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;curl -sH &quot;api-key: ${AZURE_OPENAI_API_KEY}&quot; \
    &quot;${AZURE_OPENAI_ENDPOINT%/openai/v1}/openai/deployments?api-version=2024-10-21&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON은 정상적으로 왔는데 &lt;code&gt;data&lt;/code&gt; 배열이 비어있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Foundry v1(&lt;code&gt;cognitiveservices.azure.com&lt;/code&gt;) 리소스는 모델을 deployment 이름으로 배포하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 이름을 그대로 쓰는 직접 접근 방식이라 &lt;code&gt;/openai/deployments&lt;/code&gt; 대신 &lt;code&gt;/openai/models&lt;/code&gt;로 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;curl -sH &quot;api-key: ${AZURE_OPENAI_API_KEY}&quot; \
    &quot;${AZURE_OPENAI_ENDPOINT%/}/openai/models?api-version=2024-10-21&quot; \
    | python3 -c &quot;
import json, sys
data = json.load(sys.stdin)
for m in sorted(d['id'] for d in data.get('data', [])):
    print(f'  {m}')
&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 환경변수 미설정 &amp;mdash; URL malformed&lt;/h3&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;* URL rejected: Malformed input to a URL function&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;AZURE_OPENAI_ENDPOINT&lt;/code&gt;가 설정되지 않은 상태에서 curl을 실행하면 URL이 깨진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;echo &quot;KEY: ${AZURE_OPENAI_API_KEY:0:8}...&quot;&lt;/code&gt; 출력이 &lt;code&gt;KEY: ...&lt;/code&gt;이면 환경변수가 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 세션마다 재설정하지 않으려면 &lt;code&gt;~/.zshrc&lt;/code&gt;에 영구 등록이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;export AZURE_OPENAI_ENDPOINT='https://{resource-name}.cognitiveservices.azure.com/'
export AZURE_OPENAI_API_KEY='your-api-key-here'&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. smoke test에서 &lt;code&gt;python3&lt;/code&gt; / &lt;code&gt;nvm&lt;/code&gt; ✗&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;python3 --version&lt;/code&gt;이 실패했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Homebrew &lt;code&gt;python@3.11&lt;/code&gt;은 &lt;code&gt;python3.11&lt;/code&gt; 바이너리만 생성하고 &lt;code&gt;python3&lt;/code&gt; 심볼릭 링크를 자동으로 만들지 않는다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;ln -sf /opt/homebrew/bin/python3.11 /opt/homebrew/bin/python3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;nvm&lt;/code&gt;은 바이너리가 아니라 셸 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;command -v nvm&lt;/code&gt;으로 체크하는 smoke test에서는 항상 ✗로 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;node -v&lt;/code&gt;가 v22+면 실제로는 정상이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;핵심 내용 &amp;mdash; Foundry v1 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;classic vs Foundry v1 차이&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;classic Azure OpenAI&lt;/th&gt;
&lt;th&gt;Foundry v1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;엔드포인트&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{name}.openai.azure.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{name}.cognitiveservices.azure.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wire_api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;chat&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;responses&quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모델 접근&lt;/td&gt;
&lt;td&gt;deployment name&lt;/td&gt;
&lt;td&gt;모델명 직접&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;배포 확인&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/openai/deployments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/openai/models&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;wire_api = &quot;responses&quot;&lt;/code&gt;가 Foundry v1의 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엔드포인트 정규화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Foundry v1 base_url은 &lt;code&gt;/openai/v1&lt;/code&gt; suffix가 붙어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수 값이 &lt;code&gt;/&lt;/code&gt;로 끝나거나 &lt;code&gt;/openai/v1&lt;/code&gt;이 없을 수 있어서 Python patch에서 자동 보정한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;raw_ep = os.environ.get(
    'AZURE_OPENAI_ENDPOINT',
    'https://{resource-name}.cognitiveservices.azure.com/'
)
azure_endpoint = raw_ep.rstrip('/')
if not azure_endpoint.endswith('/openai/v1'):
    azure_endpoint += '/openai/v1'&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;config.toml&lt;/code&gt; 핵심 블록 (Foundry v1)&lt;/h3&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;model = &quot;gpt-5.4-pro&quot;
model_provider = &quot;azure&quot;
model_reasoning_effort = &quot;high&quot;
plan_mode_reasoning_effort = &quot;xhigh&quot;
preferred_auth_method = &quot;apikey&quot;
sandbox_mode = &quot;workspace-write&quot;
approval_policy = &quot;on-request&quot;
suppress_unstable_features_warning = true

[model_providers.azure]
name                   = &quot;Azure AI Foundry&quot;
base_url               = &quot;https://{resource-name}.cognitiveservices.azure.com/openai/v1&quot;
env_key                = &quot;AZURE_OPENAI_API_KEY&quot;
wire_api               = &quot;responses&quot;
request_max_retries    = 4
stream_max_retries     = 10
stream_idle_timeout_ms = 900000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;model_context_window&lt;/code&gt;와 &lt;code&gt;model_auto_compact_token_limit&lt;/code&gt;은 명시하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;issue #16068에서 custom value 설정 시 auto-compaction crash loop가 0.115 이상에서 100% 재현되고, issue #19185에서 어떤 값이든 258K로 silently reduce된다고 보고됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 메타데이터를 신뢰하고 설정하지 않는 것이 안전하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자동화 스크립트 3개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 과정을 3개 스크립트로 분리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;setup_stack.sh&lt;/b&gt; &amp;mdash; &amp;sect;4: Java 21, Python 3.11, nvm/Node 22, uv/pipx/Rust, &lt;code&gt;.zshrc&lt;/code&gt; 통합 블록&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;#!/usr/bin/env zsh
set -euo pipefail

# 4.1 Java 21
brew install openjdk@21
sudo ln -sfn /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk \
  /Library/Java/JavaVirtualMachines/openjdk-21.jdk

# 4.2 Python 3.11
brew install python@3.11
ln -sf /opt/homebrew/bin/python3.11 /opt/homebrew/bin/python3

# 4.3 Node 22 via nvm
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
export NVM_DIR=&quot;$HOME/.nvm&quot;
[ -s &quot;$NVM_DIR/nvm.sh&quot; ] &amp;amp;&amp;amp; . &quot;$NVM_DIR/nvm.sh&quot;
nvm install 22 &amp;amp;&amp;amp; nvm alias default 22 &amp;amp;&amp;amp; nvm use 22

# 4.4 uv + pipx + Rust
curl -LsSf https://astral.sh/uv/install.sh | sh
brew install pipx &amp;amp;&amp;amp; pipx ensurepath
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
[ -f &quot;$HOME/.cargo/env&quot; ] &amp;amp;&amp;amp; . &quot;$HOME/.cargo/env&quot;

# 4.5 .zshrc CODEX_STACK 블록
grep -q '### CODEX_STACK_BEGIN ###' ~/.zshrc || cat &amp;lt;&amp;lt; 'EOF' &amp;gt;&amp;gt; ~/.zshrc

### CODEX_STACK_BEGIN ###
export PATH=&quot;$HOME/.local/bin:$HOME/.local/jdtls/bin:$PATH&quot;
export PATH=&quot;/opt/homebrew/opt/python@3.11/bin:$PATH&quot;
export NVM_DIR=&quot;$HOME/.nvm&quot;
[ -s &quot;$NVM_DIR/nvm.sh&quot; ] &amp;amp;&amp;amp; . &quot;$NVM_DIR/nvm.sh&quot;
[ -f &quot;$HOME/.cargo/env&quot; ] &amp;amp;&amp;amp; . &quot;$HOME/.cargo/env&quot;
export JAVA_HOME=&quot;/opt/homebrew/opt/openjdk@21&quot;
export PATH=&quot;$JAVA_HOME/bin:$PATH&quot;
export JDTLS_JVM_ARGS=&quot;-Xmx16G -Xms2G -XX:+UseG1GC -XX:+UseStringDeduplication&quot;
### CODEX_STACK_END ###
EOF

source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;setup_tools.sh&lt;/b&gt; &amp;mdash; &amp;sect;5: Codex CLI, OMX, LSP 서버, jdtls, Spring Boot LS, LSP-MCP Bridge&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/usr/bin/env zsh
set -euo pipefail

# 5.1 Codex CLI + OMX
node -v | grep -qE 'v(2[2-9]|[3-9][0-9])' || { echo &quot;Node 22+ 필요&quot;; exit 1; }
npm install -g @openai/codex oh-my-codex

# 5.2 LSP 서버
npm install -g @vtsls/language-server vscode-langservers-extracted bash-language-server
pipx install basedpyright
pipx install ruff

# 5.3 jdtls (Homebrew &amp;mdash; Eclipse 서버보다 빠름)
brew install jdtls

# 5.4 Spring Boot Language Server (VSIX &amp;rarr; exec JAR)
TARGET_DIR=&quot;${HOME}/.local/spring-boot-ls&quot;
VSIX_FILE=&quot;/tmp/sbls.vsix&quot;
mkdir -p &quot;${TARGET_DIR}&quot;

VSIX_URL=&quot;https://vmware.gallery.vsassets.io/_apis/public/gallery/publisher/vmware/extension/vscode-spring-boot/latest/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage&quot;
FALLBACK_URL=&quot;https://marketplace.visualstudio.com/_apis/public/gallery/publishers/vmware/vsextensions/vscode-spring-boot/latest/vspackage&quot;

curl -fL --connect-timeout 30 --retry 2 -H &quot;User-Agent: Mozilla/5.0&quot; \
    -o &quot;${VSIX_FILE}&quot; &quot;${VSIX_URL}&quot; 2&amp;gt;/dev/null \
  || curl -fL --connect-timeout 30 --retry 2 -H &quot;User-Agent: Mozilla/5.0&quot; \
    -o &quot;${VSIX_FILE}&quot; &quot;${FALLBACK_URL}&quot;

ZIP_MAGIC=$(xxd -l 4 &quot;${VSIX_FILE}&quot; | awk 'NR==1{print $2$3}')
[[ &quot;${ZIP_MAGIC}&quot; == &quot;504b0304&quot; ]] || { echo &quot;ZIP 아님 (HTML 에러페이지 수신)&quot;; exit 1; }

rm -rf &quot;${TARGET_DIR:?}&quot;/*
unzip -o -q &quot;${VSIX_FILE}&quot; -d &quot;${TARGET_DIR}&quot;
JAR_ABS=$(find &quot;${TARGET_DIR}&quot; -name &quot;spring-boot-language-server-*-exec.jar&quot; | sort -V | tail -1)
JAR_DIR=$(dirname &quot;${JAR_ABS}&quot;)
ln -sf &quot;$(basename &quot;${JAR_ABS}&quot;)&quot; &quot;${JAR_DIR}/spring-boot-language-server.jar&quot;
rm -f &quot;${VSIX_FILE}&quot;

# 5.5 LSP-MCP Bridge
mkdir -p ~/.local/src
git clone https://github.com/CesarPetrescu/lsp-mcp.git ~/.local/src/lsp-mcp
pipx install --editable ~/.local/src/lsp-mcp&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;setup_omx_config.sh&lt;/b&gt; &amp;mdash; &amp;sect;8~9: OMX 초기화, config.toml patch, agents 5개, lsp-mcp Java 섹션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OMX setup 후 Python으로 config.toml을 idempotent하게 patch한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 있는 블록은 교체하고 없으면 append하는 방식이라 재실행해도 안전하다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;def replace_or_append(body, header, block):
    pat = rf'\[{re.escape(header)}\].*?(?=\n\[|\Z)'
    if re.search(pat, body, flags=re.DOTALL):
        return re.sub(pat, block.strip() + '\n', body, flags=re.DOTALL, count=1)
    return body.rstrip() + '\n\n' + block.strip() + '\n'&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;subagent 5개 모델 배정&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;agent&lt;/th&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;effort&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;architect&lt;/td&gt;
&lt;td&gt;gpt-5.4-pro&lt;/td&gt;
&lt;td&gt;xhigh&lt;/td&gt;
&lt;td&gt;설계 분석. read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reviewer&lt;/td&gt;
&lt;td&gt;gpt-5.4-pro&lt;/td&gt;
&lt;td&gt;xhigh&lt;/td&gt;
&lt;td&gt;코드 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;explorer&lt;/td&gt;
&lt;td&gt;gpt-5.4-pro&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;td&gt;심볼 탐색. read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;monitor&lt;/td&gt;
&lt;td&gt;gpt-5.4-mini&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;td&gt;장기 빌드/테스트 감시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pro&lt;/td&gt;
&lt;td&gt;gpt-5.4-pro&lt;/td&gt;
&lt;td&gt;xhigh&lt;/td&gt;
&lt;td&gt;수동 호출 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;executor는 별도 toml 없이 전역 &lt;code&gt;model = &quot;gpt-5.4-pro&quot;&lt;/code&gt;를 그대로 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;smoke test 결과&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;── macOS 시스템 ──
ProductVersion: 26.3.1
✓ Apple Silicon
✓ tmux 3.6a
✓ Homebrew

── 언어 런타임 ──
✓ python3
✓ java 21+
✓ JAVA_HOME=/opt/homebrew/opt/openjdk@21

── 사용자 도구 ──
✓ node
✓ uvx / cargo / pipx
✓ codex / omx
✓ jdtls
✓ basedpyright-langserver / vtsls / ruff / bash-language-server
✓ codex-lsp-bridge

── 설정 파일 ──
✓ config.toml TOML 유효
✓ hooks.json (OMX setup)
✓ agents/architect.toml ~ agents/pro.toml
✓ Azure endpoint
✓ API 키 (len=84)
✓ MCP timeout=300

── Azure 실호출 ──
✓ Azure 정상

── OMX doctor ──
Results: 15 passed, 0 warnings, 0 failed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;nvm&lt;/code&gt;은 셸 함수라서 smoke test의 &lt;code&gt;command -v nvm&lt;/code&gt;에서 ✗로 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;node -v&lt;/code&gt;가 v22+이고 &lt;code&gt;nvm --version&lt;/code&gt;이 정상이면 실제로는 문제없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Foundry v1 엔드포인트(&lt;code&gt;cognitiveservices.azure.com&lt;/code&gt;)는 &lt;code&gt;wire_api = &quot;responses&quot;&lt;/code&gt; + &lt;code&gt;/openai/v1&lt;/code&gt; suffix가 필수다. &lt;code&gt;wire_api = &quot;chat&quot;&lt;/code&gt;으로 두면 연결은 되도 응답 파싱이 깨진다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/openai/deployments&lt;/code&gt;가 빈 배열이어도 정상이다. Foundry v1은 deployment 개념 없이 모델명 직접 접근이라 &lt;code&gt;/openai/models&lt;/code&gt;로 확인해야 한다.&lt;/li&gt;
&lt;li&gt;Homebrew 패키지 목록에 없는 formula가 하나라도 있으면 &lt;code&gt;brew install&lt;/code&gt; 전체가 중단된다. &lt;code&gt;xxd&lt;/code&gt;처럼 macOS 내장 도구는 애초에 목록에서 빼야 한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>dev</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/237</guid>
      <comments>https://6kitt-hack.tistory.com/237#entry237comment</comments>
      <pubDate>Fri, 8 May 2026 16:24:15 +0900</pubDate>
    </item>
    <item>
      <title>클라우드 네이티브 관련 이론 정리</title>
      <link>https://6kitt-hack.tistory.com/236</link>
      <description>&lt;h1&gt;클라우드 네이티브 개요&lt;/h1&gt;
&lt;p&gt;: 클라우드 환경의 특성을 고려하여 만들어진 애플리케이션 및 시스템 &lt;/p&gt;
&lt;h2&gt;클라우드 네이티브 4C 계층 구조&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;클라우드 : 기초 인프라 자원을 제공하는 최하위 계층 -&amp;gt; CSP가 관리, 물리적 보안, 네트워크 격리, IAM &lt;/li&gt;
&lt;li&gt;클러스터 : 컨테이너화된 애플리케이션을 관리하고 실행하는 오케스트레이션 계층 -&amp;gt; Kubernetes 등이 해당, 노드 간 통신, 스케줄링, API 접근 제어 등.. &lt;/li&gt;
&lt;li&gt;컨테이너 : 애플리케이션이 구동되는 격리 환경 -&amp;gt; Docker등이 해당한다. 이미지 무결성, 런타임 보안, 컨테이너 탈출 방지, 불필요한 권한 제거 #Seccomp, #Runtime Security, &lt;/li&gt;
&lt;li&gt;코드 : 비즈니스 로직이 실행되는 애플리케이션 코드 계층 -&amp;gt; App security, DevSecOps, SCA, FaaS &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;클라우드 네이티브 전환 단계&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;레거시 : 온프레미스, 모놀리식 아키텍처, 수동 배포 -&amp;gt; 높은 유지보수 비용과 낮은 유연성 &lt;/li&gt;
&lt;li&gt;클라우드 : 가상 머신 위주 인프라 활용, Re-Platform, PaaS 부분 도입 &lt;/li&gt;
&lt;li&gt;클라우드 네이티브 : MSA+컨테이너 기반 애플리케이션, Re-Factor, DevOps+CI/CD 파이프라인 자동화 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-&amp;gt; Agility, Scalability, Resilience, Cost Efficiency, Automation &lt;/p&gt;
&lt;h2&gt;클라우드 서비스 모델 비교&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;IaaS : 리소스를 제공받고, OS 설치부터 미들웨어, 런타임, 데이터까지 사용자의 관리 책임이 가장 넓은 모델 &lt;/li&gt;
&lt;li&gt;PaaS : 인프라와 런타임 관리는 CSP 관할, 사용자는 애플리케이션 코드와 데이터 보안에 전념 &lt;/li&gt;
&lt;li&gt;CaaS : 컨테이너 오케스트레이션 엔진을 관리형으로 제공받음, 사용자는 컨테이너 이미지와 설정 보안에 집중 &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;컨테이너 기술&lt;/h2&gt;
&lt;h3&gt;1. 이미지와 레이어 구조&lt;/h3&gt;
&lt;p&gt;Union File System : 여러 종류의 File System을 하나로 보이게 하는 기술&lt;br&gt;: 수정된 Layer만 새로 빌드 후 교체&lt;br&gt;: Image를 여러개 쌓아가고 그 위에 새로운 Container 생성 &lt;/p&gt;
&lt;h3&gt;2. 격리 기술&lt;/h3&gt;
&lt;p&gt;컨테이너는 가상의 격리 환경을 만들기 위해 리눅스의 namespace과 cgroup이라는 기능을 사용한다. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;namespace: 프로세스를 독립시켜주는 가상화 기술로, 각 컨테이너에서 실행된 프로세스가 시스템 등에 대해 독립할 수 있게 해줌 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cgroups: 자원에 대한 제어를 가능하게 해주는 리눅스 커널의 기능 &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3. Dockerfile과 빌드 파이프라인&lt;/h3&gt;
&lt;h3&gt;4. 레지스트리와 배포&lt;/h3&gt;
&lt;p&gt;Dockerfile : Image 생성에 필요한 명령어를 기재한 문서&lt;br&gt;위 이미지 생성 후 컨테이너 빌드 &lt;/p&gt;
&lt;p&gt;push하게 되면 Registry에 Image를 저장하게 된다. 이 레지스트리에서 이미지를 불러와서 컨테이너 실행 가능 &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker Hub : 깃허브처럼 다른 사용자들이 공유한 Image들 저장소 &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;쿠버네티스 아키텍처&lt;/h2&gt;
&lt;p&gt;Google이 공개한 대표적인 오픈 소스 컨테이너 오케스트레이션 시스템이다. &lt;/p&gt;
&lt;p&gt;*클러스터 구조 : 전체를 관리하는 Kubernetes Master와 Container가 배포되는 Worker Node로 구성되어 있다. &lt;/p&gt;
&lt;h3&gt;Control Plane Components&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;API server : 클러스터의 중앙 제어점(Gateway), 유일하게 etcd와 통신하는 컴포넌트이다. &lt;/li&gt;
&lt;li&gt;etcd : 모든 클러스터 데이터(설정,상태)를 저장하는 고가용성 Key-Value 저장소이다. (백업 및 보안이 필수적)&lt;/li&gt;
&lt;li&gt;kube-scheduler : 새로 생성된 pod를 감지하고 자원 상태, 제약 조건, 우선순위 등을 고려하여 적절한 node에 할당하는 역할을 수행한다.  &lt;/li&gt;
&lt;li&gt;kube-controller-manager : 노드 상태, 복제본 수 유지, 엔드포인트 관리 등 클러스ㅓ의 상태를 지속적으로 모니터링하고 조정한느 역할을 한다. &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;worker node&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;worker node &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Kubelet : 각 노드에서 실행되는 에이전트, PodSpec에 따라 컨테이너가 정상적으로 동작하도록 관리한다. &lt;/li&gt;
&lt;li&gt;Kube-proxy : 노드의 네트워크 규칙 유지 관리, 서비스 개념을 구현하고 Pod 간 통신을 가능하게 한다. &lt;/li&gt;
&lt;li&gt;Container Runtime : 컨테이너 실행을 담당하는 소프트웨어(CRI-O, docker engine 등이 있다) &lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;workload API &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Deployment : 애플리케이션의 선언적 업데이트 및 배포 관리, ReplicaSet을 생성하여 상태를 유지한다. &lt;/li&gt;
&lt;li&gt;ReplicaSet : 지정된 수의 Pod 복제본이 항상 실행되도록 보장한다. (Pod의 생명 주기 관리)&lt;/li&gt;
&lt;li&gt;Managed Pods : 컨트롤러에 의해 생성되고 관리되는 실제 Pod &lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Pod &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;단일 배포 단위 : Kubernetes에서 생성, 관리하는 사장 작은 배포 단위이다. &lt;/li&gt;
&lt;li&gt;공유 리소스 : Pod 내 컨테이너들은 스토리지와 네트워크를 공유한다. &lt;/li&gt;
&lt;li&gt;Sidecar 패턴 : 주 컨테이너와 보조 컨테이너가 함께 실행되는 구조이다. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;클라우드 네이티브 보안의 중요성&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;공유 책임 모델의 이해 &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;책임 경계 변화 : PaaS/CaaS 등 모델에 따른 CSP와 사용자 간 보안 책임 영역 설정 &lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;공격면 화장 &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;마이크로서비스 간 통신 증가로 인한 네트워크 경로 복잡화 &lt;/li&gt;
&lt;li&gt;컨테이너 엔진, 오케스트레이터(k8s) 등 새로운 관리 대상 등장 &lt;/li&gt;
&lt;li&gt;수시로 변경되는 IP, port로 인해 전통적 방화벽 통제 한계 &lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;보안의 자동화 &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;DevSecOps 통합 &lt;/li&gt;
&lt;li&gt;IaC 보안 : 인프라 배포 전 코드 단계에서 설정 오류 사전 탐지 &lt;/li&gt;
&lt;li&gt;불변 인프라 &lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;제로 트러스트 원칙 적용 &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;지속적 검증 필요 &lt;/li&gt;
&lt;li&gt;워크로드 식별 &lt;/li&gt;
&lt;li&gt;업무에 필요한 최소한 권한만 부여 &lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;통합 가시성 확보 &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;실시간 모니터링 &lt;/li&gt;
&lt;li&gt;추적성 강화 : 분산 환경에서의 트랜잭션 추적 및 이상 행위 탐지 체계 구축 &lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;컨테이너 환경의 보안 위협&lt;/h1&gt;
&lt;p&gt;클라우드 환경에서 가장 많이 발생하는 사고 유형은 miscofiguration, data leak, unsafe interface/API 등이 있다. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;커널 공유 리스크 : 호스트 OS 커널 공유로 인한 격리 취약성 &lt;/li&gt;
&lt;li&gt;이미지/레지스트리 위협 : 공급망 공격 취약점 &lt;/li&gt;
&lt;li&gt;네트워크/런타임 위협 : ARP 스푸핑, 컨테이너 간 무단 통신(Lateral Movement)&lt;/li&gt;
&lt;li&gt;오케스트레이터 취약점 : Kubernetes API 노출, misconfiguration으로 인한 제어권 탈취 &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;CSA Top Threats&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Data breaches &lt;/li&gt;
&lt;li&gt;Misconfiguration &lt;/li&gt;
&lt;li&gt;Account Hijacking &lt;/li&gt;
&lt;li&gt;Insecure APIs &lt;/li&gt;
&lt;li&gt;Insider Threats &lt;/li&gt;
&lt;li&gt;Workload Config &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Host 레벨 보안 위협 대응 방안&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Compliance Test : Docker Bench for Security, CIS Benchmark 등을 활용한 정기적인 호스트 설정 및 무결성 점검 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-&amp;gt;  Host 내 주요 파일 Permission 및 OS configuration에 대한 변조 검사 &lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;p&gt;커널 보안 강화 : SELinux, AppArmor, Seccomp를 활성화하여 커널 레벨에서 프로세스의 시스템 호출 및 접근을 강제 통제 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;호스트 모니터링 : 호스트 레벨의 로그 감사(Auditd), 파일 무결성 모니터링(FIM) 및 이상 징후 실시간 탐지 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;최소 권한 OS 사용 : COntainer Optimized OS(COS)와 같이 컨테이너 실행에 최적화되고 공격 표면을 줄인 전용 OS 도입 &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;컨테이너 엔진 취약점&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Docker demon 권한&lt;br&gt;: Docker 데몬은 기본적으로 Root 권한으로 실행되기 때문에 데몬이 침해당할 경우 호스트 시스템 전체 제어권을 탈취당할 위험 존재 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;소켓 노출 위험&lt;br&gt;: 소켓을 컨테이너 내부에 마운트할 경우(/var/run/docker.sock) 컨테이너에서 호스트의 도커 데몬을 제어하여 Root 권한 획득 가능(Docker-out-of-Docker)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;런타임 취약점&lt;br&gt;: Containerd-shim 등 런타임 구성요소의 버그로 인해, 컨테이너 프로세스가 상위 프로세스로 탈출하거나 좀비 프로세스화될 위험 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;주요 CVE 사례 : CVE-2019-5736&lt;br&gt;: 악성 컨테이너가 호스트의 runc 바이너리를 덮어씌워 실행 시 Root 권한을 획득하는 치명적 취약점 &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;이미지/레지스트리 보안 위협&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;취약한 라이브러리 포함 &lt;/li&gt;
&lt;li&gt;임베디드 시크릿 &lt;/li&gt;
&lt;li&gt;타이포스쿼팅 &lt;/li&gt;
&lt;li&gt;신뢰할 수 없는 레지스트리 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-&amp;gt; 공급망 공격 &lt;/p&gt;
&lt;p&gt;악성 컨테이너 이미지 &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;리소스 하이재킹 &lt;/li&gt;
&lt;li&gt;익명 이미지, 봇 실행 &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;-&amp;gt; 이렇기 때문에 리소스 사용량의 비정상적 급증을 모니터링하고, 신뢰할 수 있는 레지스트리만 허용하고, 런타임에서 프로세스 실행 차단이 필요하다. &lt;/p&gt;
&lt;h2&gt;오케스트레이터(K8s) 보안 위협&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;API 서버 노출 &lt;/li&gt;
&lt;li&gt;인증 및 인가 부실 &lt;/li&gt;
&lt;li&gt;구성요소 취약점 &lt;/li&gt;
&lt;li&gt;부적절한 RBAC &lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes Security : Attacking And Defending Modern Infrastructure, RSA 2024 &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;권한 상승 공격&lt;/h2&gt;
&lt;h3&gt;시나리오&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;privileged 컨테이너 실행 시, 호스트의 모든 장치 접근 및 커널 기능 사용이 가능하다. &lt;/li&gt;
&lt;li&gt;CAP_SYS_ADMIN 등 과도한 리눅스 커널 권한을 부여하여 시스템 제어권을 획득할 수 있다. &lt;/li&gt;
&lt;li&gt;/proc, /sys, Docker 소켓 등을 마운트하여 호스트 OS 조작 및 탈출이 가능하다. &lt;/li&gt;
&lt;li&gt;커널 취약점 악용 : Dirty COW 등... &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;방어 전략&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Security Context 설정 : allowPrivilegeEscalation : false, privileged: false 강제화 &lt;/li&gt;
&lt;li&gt;Drop Capabilities: 불필요한 리눅스 권한 제거 &lt;/li&gt;
&lt;li&gt;pod Security Standards: PSS 및 Admission Controller를 통한 정책 위반 차단 &lt;/li&gt;
&lt;li&gt;런타임 보안(Seccomp &amp;amp; AppArmor) : 시스템 콜 필터링 및 프로필 적용으로 공격 표면 최소화 &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;컨테이너 탈출&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Escape the local Container &lt;/li&gt;
&lt;li&gt;Attack Kubernetes Cluster &lt;/li&gt;
&lt;li&gt;Attack Cloud Security Center &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;*usenix의 sec23_slides_he &lt;/p&gt;
&lt;h2&gt;데이터 유출 및 손실&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;미암호화 etcdd/시크릿 &lt;/li&gt;
&lt;li&gt;퍼블릭 클라우드 스토리지 : S3 버킷, Azure Blob 등 오브젝트 스토리지의 접근 제어(ACL) 설정 미비 &lt;/li&gt;
&lt;li&gt;스냅샷 및 이미지 유출 : 민감 정보가 포함된 EBS 스냅샷이나 VM 이미지가 실수로 퍼블릭 리포지터리에 공개되어 유출 &lt;/li&gt;
&lt;li&gt;백업 데이터 미보호 &lt;/li&gt;
&lt;/ol&gt;</description>
      <category>dev</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/236</guid>
      <comments>https://6kitt-hack.tistory.com/236#entry236comment</comments>
      <pubDate>Sat, 2 May 2026 13:58:13 +0900</pubDate>
    </item>
    <item>
      <title>IaC (Infrastructure as Code) 실습</title>
      <link>https://6kitt-hack.tistory.com/235</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;본 실습은 템플릿을 활용하여 인프라를 생성하는 IaC 실습이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 실습의 의의&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 실무에서는 개발/스테이징/프로덕션 환경을 동일한 템플릿으로 찍어낸다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 각 환경에서 인프라가 필요할 때마다 사람이 수동으로 설정하면 misconfiguration 등의 실수가 있을 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 IaC를 통해 보안 설정을 강제하여 인프라 생성을 자동화할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;---&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2와 그 EC2에서 발급 받은 key pair는 있다고 가정한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노파심에 하나 말하자면 키는 chmod 400으로 관리해야한다!&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AWS CLI 설치&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1776791457982&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot;
unzip awscliv2.zip
sudo ./aws/install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 입력하여 AWS CLI를 설치해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IAM User 생성&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root 계정 access key 말고 세세한 권한 관리가 가능한 IAM User 계정 생성 및 access key 발급이 권장된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776791813492&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;AWS Console -&amp;gt; IAM -&amp;gt; 사용자 -&amp;gt; 사용자 생성&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-22 011124.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXagwd/dJMcacJDjIQ/dCAU6cWrnZ6Ibp4GjMrd8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXagwd/dJMcacJDjIQ/dCAU6cWrnZ6Ibp4GjMrd8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXagwd/dJMcacJDjIQ/dCAU6cWrnZ6Ibp4GjMrd8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXagwd%2FdJMcacJDjIQ%2FdCAU6cWrnZ6Ibp4GjMrd8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1722&quot; height=&quot;774&quot; data-filename=&quot;스크린샷 2026-04-22 011124.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 시에 위와 같은 권한을 부여한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-22 011144.png&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rHIKL/dJMcagFgIqO/vZiN3dMvRf8Fq0yErKphUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rHIKL/dJMcagFgIqO/vZiN3dMvRf8Fq0yErKphUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rHIKL/dJMcagFgIqO/vZiN3dMvRf8Fq0yErKphUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrHIKL%2FdJMcagFgIqO%2FvZiN3dMvRf8Fq0yErKphUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2054&quot; height=&quot;461&quot; data-filename=&quot;스크린샷 2026-04-22 011144.png&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 CLI에서 액세스할 수 있도록 IAM user에 대한 액세스 키를 발급 받는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AWS CLI 인증 설정&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1776791946841&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ aws configure
AWS Access Key ID [None]: &amp;lt;access key ID&amp;gt;
AWS Secret Access Key [None]: &amp;lt;access key secret&amp;gt;
Default region name [None]: &amp;lt;사용중인 리전 입력&amp;gt; 
Default output format [None]: json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정해주고&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776791965314&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ aws sts get-caller-identity
$ aws configure get region&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어로 확인해본다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;cfn-lint 설치&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1776792003152&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt install -y python3-venv python3-pip
$ cd ~/workspace
$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install --upgrade pip
$ pip install cfn-lint
$ cfn-lint &amp;ndash;version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근&amp;nbsp;Ubuntu&amp;nbsp;환경에서는&amp;nbsp;system&amp;nbsp;Python&amp;nbsp;보호&amp;nbsp;정책&amp;nbsp;때문에&amp;nbsp;pip&amp;nbsp;전역&amp;nbsp;설치가&amp;nbsp;차단될&amp;nbsp;수 &lt;br /&gt;있으므로,&amp;nbsp;가상환경(venv)&amp;nbsp;안에&amp;nbsp;cfn-lint를&amp;nbsp;설치하여&amp;nbsp;사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공인 IP 확인&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776792072792&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl ifconfig.me&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;template.yaml&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 전체 내용은 접은 글로 두었고 우리는 코드를 뜯어보자.&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1776792154943&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;AWSTemplateFormatVersion: '2010-09-09'
Description: Secure AWS lab for CloudFormation hands-on

Parameters:
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Existing EC2 KeyPair name
  MyIP:
    Type: String
    Description: Your public IP in CIDR format (example 203.0.113.10/32)
  BucketName:
    Type: String
    Description: Globally unique S3 bucket name
  WebServer:
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
    Description: EC2 instance type
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value&amp;lt;AWS::EC2::Image::Id&amp;gt;
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64

Resources:
  LabVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: lab-vpc

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: lab-igw

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref LabVPC
      InternetGatewayId: !Ref InternetGateway

  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref LabVPC
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: lab-public-subnet

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref LabVPC
      Tags:
        - Key: Name
          Value: lab-public-rt

  DefaultRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet
      RouteTableId: !Ref PublicRouteTable

  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH only from my public IP
      VpcId: !Ref LabVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyIP
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: lab-web-sg

  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Tags:
        - Key: Name
          Value: lab-ec2-role

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2Role

  SecureBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      Tags:
        - Key: Name
          Value: secure-lab-bucket

  SecureBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref SecureBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: DenyInsecureTransport
            Effect: Deny
            Principal: &quot;*&quot;
            Action: &quot;s3:*&quot;
            Resource:
              - !Sub arn:aws:s3:::${SecureBucket}
              - !Sub arn:aws:s3:::${SecureBucket}/*
            Condition:
              Bool:
                aws:SecureTransport: false

  WebInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref WebServer
      IamInstanceProfile: !Ref EC2InstanceProfile
      KeyName: !Ref KeyName
      SubnetId: !Ref PublicSubnet
      SecurityGroupIds:
        - !Ref WebSecurityGroup
      MetadataOptions:
        HttpTokens: required
        HttpEndpoint: enabled
      Tags:
        - Key: Name
          Value: secure-lab-ec2

Outputs:
  InstanceId:
    Value: !Ref WebInstance
  PublicIp:
    Value: !GetAtt WebInstance.PublicIp
  BucketName:
    Value: !Ref SecureBucket&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;파라미터 설정&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1776791543144&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Parameters:
  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Existing EC2 KeyPair name
  MyIP:
    Type: String
    Description: Your public IP in CIDR format (example 203.0.113.10/32)
  BucketName:
    Type: String
    Description: Globally unique S3 bucket name
  WebServer:
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
    Description: EC2 instance type
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value&amp;lt;AWS::EC2::Image::Id&amp;gt;
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 파라미터 선언 부분이다. SSH를 허용할 내 IP와 생성할 버킷 이름, EC2 인스턴스 타입 등을 설정해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 리소스 부분에서 많은 리소스들이 생성된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 길어서 생성되는 리소스들은 표로 나열하고 보안적으로 중요한 부분만 코드 살펴보자.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.2559%;&quot;&gt;범위&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;생성되는 리소스&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.2559%;&quot; rowspan=&quot;8&quot;&gt;network&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;LabVPC&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;InternetGateway&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;인터넷 게이트웨이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;AttachGateway&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;IGW를 VPC에 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;PublicSubnet&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;PublicRouteTable+DefaultRoute&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;PublicSubnetRouteAssociation&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;WebSecurityGroup&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;EC2Role+EC2InstanceProfile&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.2559%;&quot; rowspan=&quot;2&quot;&gt;storage&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;SecureBucket&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;SecureBucketPolicy&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.2559%;&quot;&gt;computing&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 28.9535%;&quot;&gt;WebInstance&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 57.7907%;&quot;&gt;EC2 인스턴스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;SSH 허용 IP 설정&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1776792738974&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH only from my public IP
      VpcId: !Ref LabVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref MyIP
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: lab-web-sg&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyIP만 22 Ingress로 설정해주었다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;S3 보안 설정&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1776792271954&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  SecureBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      Tags:
        - Key: Name
          Value: secure-lab-bucket

  SecureBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref SecureBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: DenyInsecureTransport
            Effect: Deny
            Principal: &quot;*&quot;
            Action: &quot;s3:*&quot;
            Resource:
              - !Sub arn:aws:s3:::${SecureBucket}
              - !Sub arn:aws:s3:::${SecureBucket}/*
            Condition:
              Bool:
                aws:SecureTransport: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BucketEncryption 설정, PublicAccessBlockConfiguration 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;S3 policy에는 aws:SecureTransport: false 추가&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설정한 outputs&amp;nbsp;&lt;/h4&gt;
&lt;pre id=&quot;code_1776792286032&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Outputs:
  InstanceId:
    Value: !Ref WebInstance
  PublicIp:
    Value: !GetAtt WebInstance.PublicIp
  BucketName:
    Value: !Ref SecureBucket&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 출력으로는 InstanceId, PublicIp, BucketName이 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776791579212&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cfn-lint template.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yaml을 일단 로컬에서 cfn-lint를 활용하여 사전 검증을 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BucketName은 전역적으로 유일해야 한다. 또한, .이나 - 등으로 끝나면 안된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776793085446&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;S3 버킷 이름 규칙
❌ - 또는 . 으로 끝나면 안 됨
❌ - 또는 . 으로 시작하면 안 됨
❌ 대문자 사용 불가
❌ 3자 미만, 64자 초과 불가
✅ 소문자, 숫자, -, . 만 허용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CloudFormation 콘솔에서 스택 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Console -&amp;gt; CloudFormation -&amp;gt; Stacks -&amp;gt; Create stack -&amp;gt; With new resources&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-22 013917.png&quot; data-origin-width=&quot;2822&quot; data-origin-height=&quot;1270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwy203/dJMcagFgIqM/scqKXKJjiPlVRVY1AZsN31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwy203/dJMcagFgIqM/scqKXKJjiPlVRVY1AZsN31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwy203/dJMcagFgIqM/scqKXKJjiPlVRVY1AZsN31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwy203%2FdJMcagFgIqM%2FscqKXKJjiPlVRVY1AZsN31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2822&quot; height=&quot;1270&quot; data-filename=&quot;스크린샷 2026-04-22 013917.png&quot; data-origin-width=&quot;2822&quot; data-origin-height=&quot;1270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KeyName은 pem 발급 받은 EC2 이름으로 설정해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyIP는 아까 public IP 구한 것에서 CIDR로 맞춰야 하므로 /32를 넣어준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 GUI에 뜨는 이 파라미터 입력칸들은 우리가 template.yaml에서 기입했던 것들이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2827&quot; data-origin-height=&quot;1031&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KUHZa/dJMb997byo2/7IdMKbOBlI5nm6jJYuJl10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KUHZa/dJMb997byo2/7IdMKbOBlI5nm6jJYuJl10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KUHZa/dJMb997byo2/7IdMKbOBlI5nm6jJYuJl10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKUHZa%2FdJMb997byo2%2F7IdMKbOBlI5nm6jJYuJl10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2827&quot; height=&quot;1031&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2827&quot; data-origin-height=&quot;1031&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CREATE_COMPLETE가 뜰 때까지 기다린다(다소 오래 걸린다)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 Outputs들을 보면 yaml에서 기입한 outputs들이 출력된다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;생성된 EC2 확인&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2227&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xoK83/dJMcagkVeci/i8v3ou5dkQd2y4kmaWHWSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xoK83/dJMcagkVeci/i8v3ou5dkQd2y4kmaWHWSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xoK83/dJMcagkVeci/i8v3ou5dkQd2y4kmaWHWSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxoK83%2FdJMcagkVeci%2Fi8v3ou5dkQd2y4kmaWHWSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2227&quot; height=&quot;67&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2227&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console EC2도 가보면 stack에 의해 새 ec2가 생성되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oxdvd/dJMcabRsiyD/IGIDMiZMZVkUnDRTLk75RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oxdvd/dJMcabRsiyD/IGIDMiZMZVkUnDRTLk75RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oxdvd/dJMcabRsiyD/IGIDMiZMZVkUnDRTLk75RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foxdvd%2FdJMcabRsiyD%2FIGIDMiZMZVkUnDRTLk75RK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;102&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2262&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rBA0N/dJMcacpjg0q/4VHfV3kaep1fGNHTH43WhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rBA0N/dJMcacpjg0q/4VHfV3kaep1fGNHTH43WhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rBA0N/dJMcacpjg0q/4VHfV3kaep1fGNHTH43WhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrBA0N%2FdJMcacpjg0q%2F4VHfV3kaep1fGNHTH43WhK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2262&quot; height=&quot;439&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2262&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IMDSv2, ingress rule들이 yaml 템플릿대로 설정되었는지 확인해보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776793522608&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ssh -i aws-prac.pem ec2-user@&amp;lt;outputs에 있던 publicip&amp;gt; 
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '-&amp;gt;
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
$ curl -w &quot;%{http_code}\n&quot; http://169.254.169.254/latest/meta-data/
401&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyIP에서 ssh 접속이 되는지 확인해보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 curl 명령어를 작성하면 401이 뜰 것이다(저 ip 주소는 아무거나 입력한 것)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 IMDSv2 required 설정을 하였는데 토큰 없이 접근했기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 169.254.169.254는 AWS EC2 인스턴스 메타데이터 서비스 전용 고정 주소이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;생성된 S3 확인&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2830&quot; data-origin-height=&quot;1083&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKGcGc/dJMb99TDKBX/7zyLeWNFGiEONnHwvoqsSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKGcGc/dJMb99TDKBX/7zyLeWNFGiEONnHwvoqsSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKGcGc/dJMb99TDKBX/7zyLeWNFGiEONnHwvoqsSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKGcGc%2FdJMb99TDKBX%2F7zyLeWNFGiEONnHwvoqsSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2830&quot; height=&quot;1083&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2830&quot; data-origin-height=&quot;1083&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;public access 차단이 모두 되어 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-22 015029.png&quot; data-origin-width=&quot;2223&quot; data-origin-height=&quot;1169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EUYQi/dJMcacixJG5/jE6GRbKEshhfISOyjZJ641/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EUYQi/dJMcacixJG5/jE6GRbKEshhfISOyjZJ641/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EUYQi/dJMcacixJG5/jE6GRbKEshhfISOyjZJ641/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEUYQi%2FdJMcacixJG5%2FjE6GRbKEshhfISOyjZJ641%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2223&quot; height=&quot;1169&quot; data-filename=&quot;스크린샷 2026-04-22 015029.png&quot; data-origin-width=&quot;2223&quot; data-origin-height=&quot;1169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;aws:SecureTransport가 false임을 확인.&amp;nbsp;&lt;/p&gt;</description>
      <category>dev</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/235</guid>
      <comments>https://6kitt-hack.tistory.com/235#entry235comment</comments>
      <pubDate>Wed, 22 Apr 2026 03:19:03 +0900</pubDate>
    </item>
    <item>
      <title>AI 활용법과 티스토리 작성하기</title>
      <link>https://6kitt-hack.tistory.com/234</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 생기면서 개발도 빨라지고, 자료조사도 빨라지고 아주 그냥 못하는 게 없다.&lt;br /&gt;그런데 팀프로젝트는 왜인지 모르게 느려졌다...&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 느려졌을까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자료조사 해오고 &amp;rarr; 전달 받고 &amp;rarr; 물어보면 &amp;rarr; 아 잠시만요... &amp;rarr; AI 답변 옮기고 &amp;rarr; 이상해서 다시 물어보면 &amp;rarr; 아.. 그렇네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저격을 하려는 것은 아니고 여러 번 겪어서 딱히 화가 나진 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그만큼 AI가 그럴듯하고 좋게 작성을 해주기에 이해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그치만 나는 이것을 아주 경계해야 한다고 생각한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비판적 활용, 개념만으론 부족하다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학교 교수님이 강조하신 '비판적 활용'을 머리에 새기고 사용하려 노력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비판적 활용이라는 개념이 있다면 action도 있어야겠지.&lt;br /&gt;나는 눈을 못 믿는다. AI는 자료조사를 잘 해주지만 내가 그것을 수용하고 의사결정을 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의사결정을 내가 하지 않는 프로젝트는 쓸모없다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;약속한 3가지&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. AI 답변을 절대 Ctrl+C 하지 않는다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;펜으로 적든 키보드로 적든, 따라 적다 보면 내가 모르는 개념들이 보인다.&lt;br /&gt;그냥 눈으로 보면 대충 보고 '음 맞는듯?' 이러고 던지기 일쑤이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 1번을 실패했다면 QnA 준비를 빡세게&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 자료를 질문 많이 하는 깐깐한 교수님한테 가져간다고 생각한다.&lt;br /&gt;그럼 나도 모르게 자료를 한 번 보고, 두 번 보고 철저한 대비를 하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 남한테 주는 거다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 이모티콘을 상당히 좋아한다. 내 감상으로는 이게 정말정말 가독성을 흐리게 만든다.&lt;br /&gt;AI 답변에서 나온 &lt;code&gt;**&lt;/code&gt; 같은 마크다운 문법 잔재 정도는 없애는 성의를 보여야 하지 않나.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 세 가지만 해도 AI를 활용한 협업에서 어려울 일은 없다고 생각한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결국은 협업 마인드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 혼자 하면 IDLE에서 md 파일 보면서 딩가딩가 하면 되지만, 협업이라는 건 생각보다 까다롭고 남을 많이 배려할수록 오래 간다고 생각한다.&lt;br /&gt;협업에서 성공적인 프로젝트와 시간이 비례하지는 않지만, &lt;b&gt;'잘 주자'는 마인드만 있어도 좋은 협업이지 않을까.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 장점은 소프트 스킬이라고 생각하기에 블로그에 찬찬히 적어보려 한다.&lt;/p&gt;</description>
      <category>일기</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/234</guid>
      <comments>https://6kitt-hack.tistory.com/234#entry234comment</comments>
      <pubDate>Thu, 16 Apr 2026 03:38:25 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 재귀</title>
      <link>https://6kitt-hack.tistory.com/231</link>
      <description>&lt;h1&gt;바킹독의 실전 알고리즘 : 재귀&lt;/h1&gt;
&lt;p&gt;재귀 : 하나의 함수에서 자기 자신을 다시 호출하여 작업을 수행하는 알고리즘 (recursion)&lt;/p&gt;
&lt;h2&gt;N부터 1까지 출력하는 함수&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int print_n(int n){
    if(n==0) return 0;
    cout&amp;lt;&amp;lt;n;
    return print_n(n-1);
}

int main(void){
    print_n(n);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;1부터 N까지의 합을 구하는 함수&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int sum_n(int n, int sum){
    if(n==0) return sum;
    return sum_n(n-1,sum+n);
}
int main(void){
    int sum=0;
    cout&amp;lt;&amp;lt;sum_n(n,sum);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;더 직관적인 재귀는 아래와 같다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int sum_n(int n) {
    if (n == 1) return 1;
    return n + sum_n(n - 1); // n 더하기 (1부터 n-1까지의 합)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;물론 sum은 그냥 수열 공식으로 쓰는 것이 좋다. -&amp;gt; {n(n+1)}/2&lt;/p&gt;
&lt;h2&gt;재귀 함수의 조건&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;void func1(int n){
    if(n==0) return 0; // 특정 조건에는 종료 (base condition)
    cout&amp;lt;&amp;lt;n&amp;lt;&amp;lt;&amp;#39; &amp;#39;;
    func1(n-1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;결국에는 base condition으로 수렴해야 한다. 이렇지 않으면 무한루프가 될 수 있다. &lt;/p&gt;
&lt;p&gt;그리고 재귀는 코드가 간결하지만 메모리/시간이 많이 소모된다. 모든 재귀 함수는 재귀 구조 없이 반복문으로 만들 수 있다. 재귀 없이 구현했을 때 너무 복잡해지는 경우는 재귀로 구현하는 것이 좋다. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;함수의 정의 &lt;/li&gt;
&lt;li&gt;base condition &lt;/li&gt;
&lt;li&gt;recursion 식 &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;연습 문제 1 : 1629번 곱셈&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
using ll=long long;

ll pow_recur(ll a, ll b, ll m){
    if(b==1) return a%m; //b가 1이면 그냥 본인만 
    ll val= pow_recur(a,b/2,m); //b/2로 재귀 호출 
    val=val*val%m; //반절 나눈 지수대로 val*val -&amp;gt; 지수 2n
    if(b%2==0) return val; //짝수이면 그대로 반환 
    return val*a%m; // b가 홀수이면 한 번 더 곱해서(2n+1) 반환 
}

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    ll a,b,c;
    cin&amp;gt;&amp;gt;a&amp;gt;&amp;gt;b&amp;gt;&amp;gt;c;
    cout&amp;lt;&amp;lt;pow_recur(a,b,c);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드에서 pow_recur 함수가 a^b mod m을 계산해주는 함수이다. 이전 내용에서 재귀 함수에서 정의해야 되는 부분은 아래와 같았다. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;함수의 정의 -&amp;gt; a의 b제곱을 계산해주는 함수(mod m 상황에서)&lt;/li&gt;
&lt;li&gt;base condition -&amp;gt; b가 1이면 본인 호출 a의 1승은 a이므로. &lt;/li&gt;
&lt;li&gt;recursion 식 -&amp;gt; &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;바킹독에서 하는 말이, 절차지향적 사고 대신 귀납적인 사고로 코드를 이해하라고 한다. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;base condition 잘 잡고 &lt;/li&gt;
&lt;li&gt;k승의 결과를 토대로 &lt;/li&gt;
&lt;li&gt;2k, 2k+1승의 결과도 계산 가능하니&lt;br&gt;재귀가 가능하다..고 함수를 이해할 필요가 있다고 한다(모르겠다........)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;여기서 시간복잡도는 b가 계속 절반씩 깎이기 때문에 O(log b)이다. &lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;연습 문제 2 : 11729번 하노이 탑 이동 순서&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;base condition 잘 잡고 -&amp;gt; 2번에 n이 1개일 때 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;k의 결과를 토대로 -&amp;gt; n-1개는 2번에 가있고 n은 1에서 3으로 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2k, 2k+1의 결과도 계산 가능하니 -&amp;gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;n-1개의 원판을 1에서 2로 옮기면 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;n번째 원판을 1에서 3으로 옮길 수 있고 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;나머지 n-1개의 원판을 2에서 3으로 옮기면 된다 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러면 base conditon은 2(1도 가능은 함)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int N,K;

void move_recur(int n){
    if(n==1){
        cout&amp;lt;&amp;lt;&amp;quot;1 3\n&amp;quot;;
        return;
    }
    //n-1개의 원판을 1에서 2로 옮기기 
    move_recur(n-1);
    cout&amp;lt;&amp;lt;&amp;quot;1 2\n&amp;quot;;
    cout&amp;lt;&amp;lt;&amp;quot;1 3\n&amp;quot;;
    cout&amp;lt;&amp;lt;&amp;quot;2 3\n&amp;quot;;
}

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin&amp;gt;&amp;gt;N;
    K=(N-1)*3+1;
    cout&amp;lt;&amp;lt;K&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
    move_recur(N);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;일단 이렇게 짜다가 끝났는데 함수 하나로는 안되고 각 출력마다의 func을 두어야 할 거 같긴 하다. 그냥 해설 보자..ㅜㅜ &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;함수의 정의 &lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;여기서도 func(int n)은 적절하지 않다고 한다. func(int a, int b, int n)로 두어서 원판 n개를 a에서 b로 두는 방법을 출력하는 함수로 두어야 한다. &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;일단 여기까지만 하고 다시 내가 짜보자. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

/*
1. n-1개의 원판을 1에서 2로 옮기면 
2. n번째 원판을 1에서 3으로 옮길 수 있고 
3. 나머지 n-1개의 원판을 2에서 3으로 옮기면 된다 
4. 그러면 base conditon은 2(1도 가능은 함)
*/

int N,K;

void move_recur(int a, int b, int n){
    if(n==1){
        cout&amp;lt;&amp;lt;a&amp;lt;&amp;lt;&amp;#39; &amp;#39;&amp;lt;&amp;lt;b&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
        return;
    }

    //n-1개를 b에서 b+1로 옮기기 
    move_recur(a,6-a-b,n-1); 
    cout&amp;lt;&amp;lt;a&amp;lt;&amp;lt;&amp;#39; &amp;#39;&amp;lt;&amp;lt;b&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
    move_recur(6-a-b,b,n-1); 
}

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin&amp;gt;&amp;gt;N;
    K=(1&amp;lt;&amp;lt;N)-1; //-&amp;gt; 이건 대입으로 찾는 게 나을듯 (하노이는 2^n-1 외우기)
    cout&amp;lt;&amp;lt;K&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
    move_recur(1,3,N);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 작성했더니 풀렸다. &lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;p&gt;base condition &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;위 코드와 같이 n==1이 맞았다. 근데 n==0일 때 코드가 더 예쁘게 완성된다고 한다. &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;재귀 식 &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;한가지 주의할 점이 처음에는 % 이용해서 기둥 1,2,3을 표현하려고 했었는데 나머지에 0도 있다는 것을 까먹고 있었다. 또한, 여기서는 1,2,3기둥이 순환한다는 보장이 없고 &amp;quot;남은&amp;quot; 기둥을 써야한다는 점이 중요하기에 1,2,3의 총합인 6에서 a기둥과 b기둥을 빼주는 것이 중요하다. &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;연습 문제 3 : 1074번 Z&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
#define X first
#define Y second

int N,r,c;

int cnt;
pair&amp;lt;int,int&amp;gt; cur;

void vis_recur(int a,pair&amp;lt;int,int&amp;gt; n){
    /*
    1. 함수 정의 -&amp;gt; 2의 a승일 때 호출, 좌표와 함께
    2. base condition -&amp;gt; 좌표 간 2^1일 때 
    3. 재귀 호출 
    */

    //이때 n은 맨끝 좌표 
    if(a==1){
        if(cur.X-1==r&amp;amp;&amp;amp;cur.Y-1==c) cout&amp;lt;&amp;lt;cnt+1; else cnt++;
        if(cur.X==r&amp;amp;&amp;amp;cur.Y-1==c) cout&amp;lt;&amp;lt;cnt+1; else cnt++;
        if(cur.X-1==r&amp;amp;&amp;amp;cur.Y==c) cout&amp;lt;&amp;lt;cnt+1; else cnt++;
        if(cur.X==r&amp;amp;&amp;amp;cur.Y==c) cout&amp;lt;&amp;lt;cnt+1; else cnt++;
        return;
    }

    vis_recur(a-1,{(cur.X)/2,(cur.Y)/2});
    vis_recur(a-1,{(cur.X),(cur.Y)/2});
    vis_recur(a-1,{(cur.X)/2,(cur.Y)});
    vis_recur(a-1,{(cur.X),(cur.Y)});
}

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin&amp;gt;&amp;gt;N&amp;gt;&amp;gt;r&amp;gt;&amp;gt;c;
    vis_recur(N,{1&amp;lt;&amp;lt;N,1&amp;lt;&amp;lt;N});
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;제미나이 채점 돌려보니까 논리는 맞는데 전체 판을 다 돌리면 시간 초과가 발생한다고 한다. 바킹독으로 돌아가서 풀이 확인해보자. &lt;/p&gt;
&lt;p&gt;바킹독에서는 재귀 함수를 아래와 같이 정의했다. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;함수의 정의 -&amp;gt; func(int n, int r, int c)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;(r,c)를 방문하는 순서를 반환하는 함수 &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;base condition &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;n=0일 때 return 0;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;재귀 식 &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;사분면으로 나누어서 func(n-1,r,c), half*half+func(n-1,r,c-half) 이런 식으로... &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;여기서 half는? 사분면의 반절 쪼개서 그 넓이. 그 넓이는 이미 순회를 했을 테니까 더해주는 것이다. 다시 정리하자면... &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;(r,c)가 1사분면에 있을 떄 -&amp;gt; return func(n-1,r,c);&lt;/li&gt;
&lt;li&gt;(r,c)가 2사분면에 있을 때 -&amp;gt; half*half+func(n-1,r,c-half);&lt;/li&gt;
&lt;li&gt;(r,c)가 3사분면에 있을 때 -&amp;gt; 2&lt;em&gt;half&lt;/em&gt;half+func(n-1,r-half,c);&lt;/li&gt;
&lt;li&gt;(r,c)가 4사분면에 있을 때 -&amp;gt; 3&lt;em&gt;half&lt;/em&gt;half+func(n-1,r-half,c-half);&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;잠깐. 좌표에서는 왜 half를 빼주는지 생각해보자. 아, 다른 사분면은 더해주고 다시 그 작은 정사각형에서의 좌표가 리셋되기 때문이다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
#define X first
#define Y second

int N,r,c;

int cnt;
pair&amp;lt;int,int&amp;gt; cur;

int vis_recur(int n,int a, int b){
    if(n==0) return 0;
    int half=1&amp;lt;&amp;lt;(n-1); //2의 (n-1)승 
    if(a&amp;lt;half &amp;amp;&amp;amp; b&amp;lt;half) return vis_recur(n-1,a,b); //1사분면 
    if(a&amp;lt;half &amp;amp;&amp;amp; b&amp;gt;=half) return half*half+vis_recur(n-1,a,b-half);
    if(a&amp;gt;=half &amp;amp;&amp;amp; b&amp;lt;half) return 2*half*half+vis_recur(n-1,a-half,b);
    if(a&amp;gt;=half &amp;amp;&amp;amp; b&amp;gt;=half) return 3*half*half+ vis_recur(n-1,a-half,b-half);
}

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin&amp;gt;&amp;gt;N&amp;gt;&amp;gt;r&amp;gt;&amp;gt;c;
    cout&amp;lt;&amp;lt;vis_recur(N,r,c);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;최종본이다. 재귀를 잘 이해한 뒤에 백트래킹을 듣는 게 좋다고 한다(미리 알았더라면 벼락치기 하지 않았을 텐데..)&lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/231</guid>
      <comments>https://6kitt-hack.tistory.com/231#entry231comment</comments>
      <pubDate>Sun, 22 Feb 2026 21:33:31 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : DFS</title>
      <link>https://6kitt-hack.tistory.com/230</link>
      <description>&lt;h1&gt;바킹독의 실전 알고리즘 : DFS&lt;/h1&gt;
&lt;p&gt;DFS : 다차원 배열에서 각 칸을 방문할 때 깊이를 우선순위로&lt;br&gt;BFS : 다차원 배열에서 각 칸을 방문할 때 너비를 우선순위로 &lt;/p&gt;
&lt;h2&gt;DFS 방법&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;시작하는 칸을 스택에 넣고 방문했다는 표시를 남김 &lt;/li&gt;
&lt;li&gt;스택에서 원소를 꺼ㅐ서 상하좌우로 인접한 칸에 대해 3번을 진행 &lt;/li&gt;
&lt;li&gt;이전에 방문했다면 continue;, 처음으로 방문했다면 표시 남기고 스택에 삽입 &lt;/li&gt;
&lt;li&gt;스택이 빌 때까지 2번을 반복 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-&amp;gt; 모든 칸이 스택에 1번씩 들어가므로 시간복잡도는 O(n)&lt;/p&gt;
&lt;h2&gt;구현&lt;/h2&gt;
&lt;p&gt;구현은 내가 일단 시도해보자. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
#define X first 
#define Y second 

int board[502][502]; // 0,1로 정의된 배열 
vool vis[502][502];

int n=7, m=10;

int dx[4]={1,0,-1,0};
int dy[4]={0,-1,0,1};

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    stack&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; s;
    vis[0][0]=1;
    s.push(0,0);

    while(!s.empty()){
        pair&amp;lt;int,int&amp;gt; cur=s.top(); s.pop();
        cout&amp;lt;&amp;lt;&amp;quot;(&amp;quot; &amp;lt;&amp;lt;cur.X&amp;lt;&amp;lt;&amp;quot;,&amp;quot;&amp;lt;&amp;lt;cur.Y&amp;lt;&amp;lt;&amp;quot;)-&amp;gt; &amp;quot;;
        for(int dir=0;dir&amp;lt;4;dir++){
            int nx=cur.X+dx[dir];
            int ny=cur.Y+dy[dir];
            if(nx&amp;lt;0||nx&amp;gt;=n||ny&amp;lt;0||ny&amp;gt;=m) continue;
            if(vis[nx][ny] || board[nx][ny]!=1) continue;
            vis[nx][ny] =1;
            s.push({nx,ny});
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;BFS와의 차이&lt;/h2&gt;
&lt;p&gt;이미지 연상으로 기억하는 게 좋을 것 같다. BFS는 상하좌우로 동심원 생기듯이 방문한다면, DFS는 일단 직진해서 한 방향 파고 그다음 방향 파듯이 방문한다. &lt;/p&gt;
&lt;p&gt;또한, &amp;quot;현재 보는 칸으로부터 추가되는 인접한 칸은 거리가 현재 보는 칸보다 1만큼 더 떨어져있다&amp;quot;는 BFS 성질이 DFS에서는 성립하지 않는다. 거리 측정은 BFS만이 가능하니 웬만하면 BFS로 푸는 것이 좋다(DFS는 추후에 그래프와 트리 자료구조에서 나온다) &lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/230</guid>
      <comments>https://6kitt-hack.tistory.com/230#entry230comment</comments>
      <pubDate>Sun, 22 Feb 2026 21:33:22 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : BFS</title>
      <link>https://6kitt-hack.tistory.com/229</link>
      <description>&lt;h1&gt;바킹독의 실전 알고리즘 : BFS&lt;/h1&gt;
&lt;h2&gt;알고리즘 설명&lt;/h2&gt;
&lt;p&gt;BFS는 Breadth First Search는 다차원 배열에서 각 칸을 방문할 때 너비를 우선으로 방문하는 알고리즘이다. &lt;/p&gt;
&lt;p&gt;본디 BFS는 그래프라는 자료구조에 모든 노드를 방문하기 위해 제안된 알고리즘이다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main(void){
    pair&amp;lt;int,int&amp;gt; t1=make_pair(10,13);
    pair&amp;lt;int,int&amp;gt; t2={4,6};
    cout&amp;lt;&amp;lt;t2.first&amp;lt;&amp;lt;&amp;#39; &amp;#39;&amp;lt;&amp;lt;t2.second&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
    if(t2&amp;lt;t1) cout&amp;lt;&amp;lt;&amp;quot;t2&amp;lt;t1&amp;quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;utility 헤더에 있는 pair는 두 자료형을 묶어서 다닐 수 있다. BFS는 정말 숙달되도록 잘 알아야 한다. 그 냥 외 우 는 걸 추 천 했 으 니&lt;br&gt;외우자.. &lt;/p&gt;
&lt;p&gt;{% raw %}&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

#define X first
#define Y second //X,Y를 상수로

int board[502][502] =
{{1,1,1,0,1,0,0,0,0,0},
 {1,0,0,0,1,0,0,0,0,0},
 {1,1,1,0,1,0,0,0,0,0},
 {1,1,0,0,1,0,0,0,0,0},
 {0,1,0,0,0,0,0,0,0,0},
 {0,0,0,0,0,0,0,0,0,0},
 {0,0,0,0,0,0,0,0,0,0} };
bool vis[502][502];

int n=7, m=10;

int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    queue&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; Q; //int 두 개를 페어로 넣을 수 있는 큐 선언
    vis[0][0]=1; //이게 시작점 표시인듯
    Q.push({0,0}); //그리고 큐에 시작점 값을 넣기

    while(!Q.empty()){
        pair&amp;lt;int,int&amp;gt; cur=Q.front(); Q.pop; //현재 위치는 cur, 그리고 큐에서 값 빼기
        cout&amp;lt;&amp;lt;&amp;#39;(&amp;#39;&amp;lt;&amp;lt;cur.X&amp;lt;&amp;lt;&amp;quot;, &amp;quot;&amp;lt;&amp;lt;cur.Y&amp;lt;&amp;lt;&amp;quot;) -&amp;gt; &amp;quot;;
        for(int dir=0;dir&amp;lt;4;dir++){ //디렉팅
            int nx=cur.X+dx[dir]; //아 3방향으로 다 돌아보는군
            int ny=cur.Y+dy[dir];
            if(nx&amp;lt;0||nx&amp;gt;=n||ny&amp;lt;0||ny&amp;gt;=m) continue; // 없는 위치이면 넘기고
            if(vis[nx][ny]||board[nx][ny]!=1) continue; // vis에 이미 방문한 표시가 있으면 넘기고
            //근데 윗줄에서 board 배열의 역할은 뭐지? -&amp;gt; 아 예시 그림에서 주어진 파란 칸인지 범위 확인
            vis[nx][ny]=1; // 위 조건에 부합하지 않으면 얘도 vis 넣고
            Q.push({nx,ny}); //큐에 넣고
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{% endraw %}&lt;/p&gt;
&lt;p&gt;음 어렵군. 일단 본문 글 보기 전에 내가 코드 리뷰로 주석 달아보자. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;내가 주석 달아보고 &lt;/li&gt;
&lt;li&gt;본문 읽으면서 보충하기 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;BFS를 구현할 때 짚어야 할 부분 &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;시작점에 방문했다는 표시를 남기지 않는다. -&amp;gt; 시작점 두 번 방문할 수도 있음 &lt;/li&gt;
&lt;li&gt;큐에 넣을 때 방문했다는 표시 대신 빼낼 때 방문했다는 표시를 남겼다. -&amp;gt; 같은 칸이 큐에 여러 번 들어갈 수 있음 &lt;/li&gt;
&lt;li&gt;이웃한 원소가 범위 외인지 체크를 잘못했다. -&amp;gt; 이건 뭐.. &lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;1926 : 그림&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

#define X first
#define Y second

int board[501][501];
bool vis[501][501];

int dx[4]={-1,0,1,0};
int dy[4]={0,-1,0,1};

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n,m;
    cin&amp;gt;&amp;gt;n&amp;gt;&amp;gt;m;
    for(int i=0;i&amp;lt;n;i++){
        for(int j=0;j&amp;lt;m;j++)
            cin&amp;gt;&amp;gt;board[i][j];
    }

    int max_paint=0;
    queue&amp;lt;int&amp;gt; paint;
    for(int i=0;i&amp;lt;n;i++){
        for(int j=0;j&amp;lt;m;j++){
            if(board[i][j]==0 || vis[i][j]==1) continue;
            vis[i][j]=1;
            queue&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; Q;
            Q.push({i,j});
            int vis_sum=1;

            while(!Q.empty()){
                pair&amp;lt;int,int&amp;gt; cur=Q.front(); Q.pop();
                for(int dir=0;dir&amp;lt;4;dir++){
                    int nx=cur.X+dx[dir];
                    int ny=cur.Y+dy[dir];

                    if(nx&amp;lt;0 || nx &amp;gt;(n-1) || ny&amp;lt;0 || ny &amp;gt; m-1) continue;
                    if(board[nx][ny]==0) continue;
                    if(vis[nx][ny]==1) continue;

                    vis[nx][ny]=1;
                    Q.push({nx,ny});
                    vis_sum++;
                }
            }
            paint.push(vis_sum);
            if(max_paint&amp;lt;vis_sum) max_paint=vis_sum;
        }
    }
    cout&amp;lt;&amp;lt;paint.size()&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;&amp;lt;&amp;lt;max_paint;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;나 혼자서 풀지는 못하고 BFS 예제 보면서 응용해 풀었다.. 코드 작성에 익숙해져야 할듯.. 이따가 집 가서 기본 코드 한 번 더 복습하기. &lt;/p&gt;
&lt;h2&gt;응용1 - 거리 측정&lt;/h2&gt;
&lt;p&gt;2178번 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
#define X first
#define Y second

string tmp[101];
int board[101][101];

int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n,m;
    cin&amp;gt;&amp;gt;n&amp;gt;&amp;gt;m;
    for(int i=0;i&amp;lt;n;i++)
        cin&amp;gt;&amp;gt;tmp[i]; //string형 tmp에 다 받기 
    for(int i=0;i&amp;lt;n;i++) fill(board[i],board[i]+m,-1); //board에 다 -1로 채우기 
    queue&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; Q;
    Q.push({0,0}); // 0,0부터 시작 (1,1)에서 출발하는 문제이니까 
    board[0][0]=1; //방문 시작 1칸 

    while(!Q.empty()){
        auto cur=Q.front(); Q.pop();
        for(int dir=0;dir&amp;lt;4;dir++){
            int nx=cur.X+dx[dir];
            int ny=cur.Y+dy[dir];

            if(nx&amp;lt;0||nx&amp;gt;n-1||ny&amp;lt;0||ny&amp;gt;m-1) continue;
            if(board[nx][ny]&amp;gt;0 || tmp[cur.X][cur.Y]!=&amp;#39;1&amp;#39;) continue; // tmp에서 1이 아니거나 board가 양수칸이거나 

            board[nx][ny]=board[cur.X][cur.Y]+1; //이전 칸의 누적칸 + 1칸 
            Q.push({nx,ny});
        }
    }
    cout&amp;lt;&amp;lt;board[n-1][m-1];
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;응용2 - 시작점이 여러 개일 때&lt;/h2&gt;
&lt;p&gt;7576번 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
#define X first
#define Y second

int board[1001][1001];
int vis[1001][1001];

int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n,m;
    queue&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; Q;
    cin &amp;gt;&amp;gt; m &amp;gt;&amp;gt; n;
    for(int i=0;i&amp;lt;n;i++){
        for (int j=0; j&amp;lt;m;j++){
            cin&amp;gt;&amp;gt;board[i][j];
            if(board[i][j]==1) Q.push({i,j});
            if(board[i][j]==0) vis[i][j]=-1;
        }
    }

    while(!Q.empty()){
        auto cur = Q.front(); Q.pop();
        for(int dir=0;dir&amp;lt;4;dir++){
            int nx=cur.X+dx[dir];
            int ny=cur.Y+dy[dir];

            if(nx&amp;lt;0||nx&amp;gt;n-1||ny&amp;lt;0||ny&amp;gt;m-1) continue;
            if(vis[nx][ny]&amp;gt;=0) continue;

            vis[nx][ny]=vis[cur.X][cur.Y]+1;
            Q.push({nx,ny});
        }
    }

    int ans=0;
    for(int i=0;i&amp;lt;n;i++){
        for(int j=0;j&amp;lt;m;j++){
            if(vis[i][j]==-1){
                cout&amp;lt;&amp;lt;-1;
                return 0;
            }
            ans=max(ans,vis[i][j]);
        }
    }
    cout&amp;lt;&amp;lt;ans;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;시작점이 여러개일 때는 그냥 while문 돌리기 전에 큐에 시작점들을 다 넣고 시작하면 된다! &lt;/p&gt;
&lt;h2&gt;응용3 - 시작점이 두 종류일 때&lt;/h2&gt;
&lt;p&gt;4179번인데 한 번 고민을 해보래서 좀 그려지는 게 있나 확인해보자. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
#define X first
#define Y second

string board[1002];
int dis1[1002][1002];
int dis2[1002][1002];

int dx[4]={-1,0,1,0};
int dy[4]={0,-1,0,1};

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n,m;
    cin&amp;gt;&amp;gt;n&amp;gt;&amp;gt;m;

    for(int i=0;i&amp;lt;n;i++){
        fill(dis1[i],dis1[i]+m,-1);
        fill(dis2[i],dis2[i]+m,-1);
    }

    for(int i=0;i&amp;lt;n;i++) cin&amp;gt;&amp;gt;board[i];

    queue&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; Q; 
    queue&amp;lt;pair&amp;lt;int,int&amp;gt;&amp;gt; fire;

    for(int i=0;i&amp;lt;n;i++){
        for(int j=0;j&amp;lt;m;j++){
            if(board[i][j]==&amp;#39;F&amp;#39;){
                fire.push({i,j});
                dis1[i][j]=0;
            }
            if(board[i][j]==&amp;#39;J&amp;#39;){
                Q.push({i,j});
                dis2[i][j]=0;
            }
        }
    }

    while(!fire.empty()){
        auto cur=fire.front(); fire.pop();
        for(int dir=0;dir&amp;lt;4;dir++){
            int nx=cur.X+dx[dir];
            int ny=cur.Y+dy[dir];

            if(nx&amp;lt;0||nx&amp;gt;=n||ny&amp;lt;0||ny&amp;gt;=m) continue;
            if(dis1[nx][ny]&amp;gt;=0 || board[nx][ny]==&amp;#39;#&amp;#39;) continue;

            dis1[nx][ny]=dis1[cur.X][cur.Y]+1;
            fire.push({nx,ny});
        }
    }

    while(!Q.empty()){
        auto cur=Q.front(); Q.pop();
        for(int dir=0;dir&amp;lt;4;dir++){
            int nx=cur.X+dx[dir];
            int ny=cur.Y+dy[dir];

            if(nx&amp;lt;0||nx&amp;gt;=n||ny&amp;lt;0||ny&amp;gt;=m){
                cout&amp;lt;&amp;lt;dis2[cur.X][cur.Y]+1;
                return 0;
            }

            if(dis2[nx][ny]&amp;gt;=0 || board[nx][ny]==&amp;#39;#&amp;#39;) continue;
            if(dis1[nx][ny]!=-1&amp;amp;&amp;amp;dis1[nx][ny]&amp;lt;=dis2[cur.X][cur.Y]+1) continue;
            dis2[nx][ny]=dis2[cur.X][cur.Y]+1;
            Q.push({nx,ny});
        }
    }
    cout&amp;lt;&amp;lt;&amp;quot;IMPOSSIBLE&amp;quot;&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이건 혼자 삽질하다가 못풀어서 해설 봤다... &lt;/p&gt;
&lt;h2&gt;응용4 - 1차원에서의 BFS&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt; 
using namespace std;

int vis[100001];

int main(void){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int N,K;
    cin&amp;gt;&amp;gt;N&amp;gt;&amp;gt;K;

    fill(vis,vis+100001,-1);
    int d[4]={-1,1,2};
    queue&amp;lt;int&amp;gt; Q;
    Q.push(N);
    vis[N]=0;

    //아래 예외 케이스
    if(N==K){
        cout&amp;lt;&amp;lt;0;
        return 0;
    }

    while(!Q.empty()){
        auto cur=Q.front(); Q.pop();
        for(int dir=0;dir&amp;lt;3;dir++){
            int nx;
            if(d[dir]==2) nx=d[dir]*cur;
            else nx=cur+d[dir];

            if(nx&amp;lt;0 || nx&amp;gt;100000) continue;
            if(vis[nx]&amp;gt;=0) continue;

            if(nx==K){
                cout&amp;lt;&amp;lt;vis[cur]+1;
                return 0;
            }

            Q.push(nx);
            vis[nx]=vis[cur]+1;

        }

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이건 어쩌다보니 나 혼자 풀었는데 TC만 맞고 계속 틀렸대서 제미나이한테 물어봤다. 예외케이스를 하나 생각 안 해서 생긴 문제였다... TC를 잘 정의하는 것이 중요해보인다. &lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/229</guid>
      <comments>https://6kitt-hack.tistory.com/229#entry229comment</comments>
      <pubDate>Mon, 16 Feb 2026 12:16:08 +0900</pubDate>
    </item>
    <item>
      <title>[WebHacking.kr] old-20번 문제 풀이 (Write-up)</title>
      <link>https://6kitt-hack.tistory.com/228</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Web2/Challenge-20 문제&lt;br /&gt;자바스크립트의 동작 방식을 분석 -&amp;gt; 폼 데이터를 자동으로 입력하고 제출하는 Client side scripting 문제&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 문제 분석 (Analysis)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;F12를 통해 소스 코드를 확인하면, 캡차 인증을 포함한 폼 제출 로직이 포함되어 있다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;function ck(){
  if(lv5frm.id.value==&quot;&quot;) { lv5frm.id.value.focus(); return; }
  if(lv5frm.cmt.value==&quot;&quot;) { lv5frm.cmt.value.focus(); return; }
  if(lv5frm.captcha.value==&quot;&quot;) { lv5frm.captcha.focus(); return; }
  if(lv5frm.captcha.value!=lv5frm.captcha_.value) { lv5frm.captcha.focus(); return; }
  lv5frm.submit();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한 시간: 상단에 time limit : 2 second 문구가 있어, 페이지 로드 후 2초 안에 모든 값을 입력하고 제출해야 함&lt;br /&gt;캡차 로직: captcha_라는 이름의 버튼 value에 무작위 문자열이 생성되며, 사용자는 이 값을 captcha 입력창에 똑같이 입력해야 함&lt;br /&gt;검증 과정: ck() 함수는 닉네임, 코멘트, 캡차 값이 비어있는지 확인하고, 특히 입력한 캡차 값과 생성된 값이 일치하는지 대조한 뒤 submit()을 호출&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 익스플로잇&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2초라는 짧은 시간 내에 수동으로 입력하는 것은 어려우므로, 브라우저 콘솔에서 자바스크립트를 실행하여 자동화를 수행&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;// 1. nickname과 comment에 임의의 값 &quot;admin&quot; 입력
document.lv5frm.id.value = &quot;admin&quot;;
document.lv5frm.cmt.value = &quot;admin&quot;;

// 2. 캡차 버튼(captcha_)에 생성된 랜덤 value 값을 읽어서 입력창(captcha)에 대입
document.lv5frm.captcha.value = document.lv5frm.captcha_.value;

// 3. 폼 제출 함수 호출 또는 직접 submit
document.lv5frm.submit();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 설명:&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;document.lv5frm.id.value와 cmt.value를 통해 서버가 요구하는 필수 입력값을 채운다.&lt;br /&gt;핵심은 &lt;b&gt;document.lv5frm.captcha_.value&lt;/b&gt;를 참조하여 입력하는 것이다.&lt;br /&gt;마지막에 submit()을 실행하면 ck() 함수의 유효성 검사를 거치지 않고 서버로 바로 데이터를 전송하거나, 모든 조건이 충족된 상태로 폼이 제출됨&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;+ ck()함수의 유효성 검사를 거치지 않는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 HTML 폼(Form)을 제출하는 두 가지 방식의 차이 때문에 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말씀드리면, form.submit()이라는 메서드(Method)를 직접 호출하면 브라우저는 '사용자가 검증을 마쳤다'고 간주하고 곧바로 서버로 전송하기 때문입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ck() 함수가 호출되지 않는 이유&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제공해주신 HTML 코드를 다시 보면, 제출 버튼이 다음과 같이 되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML&lt;/p&gt;
&lt;p&gt;&lt;input type=&quot;button&quot; value=&quot;Submit&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;일반적인 흐름: 사용자가 버튼을 클릭(onclick) &amp;rarr; ck() 함수 실행 &amp;rarr; 함수 내부의 유효성 검사 통과 &amp;rarr; 마지막 줄의 lv5frm.submit() 실행.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 실행 시: 콘솔창에 직접 document.lv5frm.submit()을 입력하면, 버튼의 onclick 이벤트(즉, ck() 함수)를 거치지 않고 폼 객체의 전송 기능만 즉시 실행합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;onsubmit과 submit()의 차이&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 HTML이 아래와 같은 구조였다면 스크립트로 실행해도 검증을 거쳤을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분 일반적인 방식 (onsubmit) 현재 문제의 방식 (onclick)&lt;br /&gt;HTML 구조&lt;/p&gt;
&lt;form&gt;&lt;input type=&quot;button&quot; /&gt;&lt;br /&gt;특징 폼이 제출될 때 무조건 ck()를 실행함. 버튼을 '클릭'했을 때만 ck()를 실행함.&lt;br /&gt;스크립트 전송 시 submit() 호출 시 검증 로직이 작동함. submit() 호출 시 ck()를 건너뜀.&lt;br /&gt;3. 요약하자면&lt;/form&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 코드에서 ck() 함수는 &lt;b&gt;'버튼을 눌렀을 때 실행되는 별개의 함수'&lt;/b&gt;일 뿐, 폼(Form) 자체에 결속된 필수 검증 절차가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 우리가 콘솔에서 lv5frm.submit()을 명령하는 것은, &lt;b&gt;&quot;검문소(ck 함수)를 통과해서 정문을 여는 게 아니라, 뒷문(submit 메서드)을 직접 열어버리는 것&quot;&lt;/b&gt;과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M9KRI/dJMcahpPEIW/1QwVAxTzNrzfftWCi5KrHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M9KRI/dJMcahpPEIW/1QwVAxTzNrzfftWCi5KrHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M9KRI/dJMcahpPEIW/1QwVAxTzNrzfftWCi5KrHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM9KRI%2FdJMcahpPEIW%2F1QwVAxTzNrzfftWCi5KrHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;168&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>wargame/webhacking.kr</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/228</guid>
      <comments>https://6kitt-hack.tistory.com/228#entry228comment</comments>
      <pubDate>Mon, 16 Feb 2026 12:16:00 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 스택의 활용(수식의 괄호 쌍)</title>
      <link>https://6kitt-hack.tistory.com/227</link>
      <description>&lt;h1&gt;수식의 괄호 쌍이란?&lt;/h1&gt;
&lt;p&gt;코테에서 한 번쯤 봤을 법한 문제이다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;(())()()
))(()
()())(()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위처럼 있다고 할 때 괄호 쌍이 모두 있는지 판단하는 문제이다. 일단 안쪽부터 짝 맞추기를 해본다면 첫번째 입력만 맞다는 것을 알 수 있다. 괄호가 여러 종류일 때는 단순히 괄호 개수만으로 판별하기 어렵다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;({(){}})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이러한 유형은 스택을 사용한다면 빠른 시간복잡도로 풀어낼 수 있다. &lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/227</guid>
      <comments>https://6kitt-hack.tistory.com/227#entry227comment</comments>
      <pubDate>Sun, 15 Feb 2026 11:18:32 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 덱</title>
      <link>https://6kitt-hack.tistory.com/226</link>
      <description>&lt;h1&gt;정의와 성질&lt;/h1&gt;
&lt;p&gt;양쪽 끝에서 삽입 삭제 전부 가능한 자료구조이다. Double Ended Queue라고 큐의 진화형 느낌이다. &lt;/p&gt;
&lt;h2&gt;큐의 성질&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;원소의 추가가 O(1)&lt;/li&gt;
&lt;li&gt;원소의 제거가 O(1)&lt;/li&gt;
&lt;li&gt;제일 앞/뒤 원소 확인이 O(1)&lt;/li&gt;
&lt;li&gt;앞뒤 외의 원소들은 원칙적으로 확인/변경 불가능 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;다만 STL deque에서는 인덱스로 원소 접근이 가능하다. &lt;/p&gt;
&lt;h1&gt;기능과 구현&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;const int MX=100005;
int dat[2*MX+1];
int head=MX, tail=MX;

void push_front(int x){
    dat[--head]=x;
}

void push_back(int x){
    dat[tail++]=x;
}

void pop_front(){
    head++;
}

void pop_back(){
    tail--;
}

int front(){
    return dat[head];
}

int back(){
    return dat[tail-1];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;배열로 구현하자면 위와 같다. STL 덱은 아래 참고. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int main(void){
    deque&amp;lt;int&amp;gt; DQ;
    DQ.push_front(10);
    DQ.push_back(20);
    DQ.size();
    DQ.empty();
    DQ.insert(DQ.begin()+1,33);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;STL의 덱은 pop,push front, back 말고도 insert, erase도 가능하다. 원소 접근이 가능하다. vector와 비슷하게 느껴질 수 있으나 STL이 제공하는 Deque은 모든 원소들이 메모리상에 연속 배치되어 있지 않다(벡터는 연속 배치) &lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/226</guid>
      <comments>https://6kitt-hack.tistory.com/226#entry226comment</comments>
      <pubDate>Sun, 15 Feb 2026 11:18:23 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 큐</title>
      <link>https://6kitt-hack.tistory.com/225</link>
      <description>&lt;h1&gt;정의와 성질&lt;/h1&gt;
&lt;p&gt;큐는 FIFO 바로 나와주면 된다. &lt;/p&gt;
&lt;h2&gt;큐의 성질&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;원소의 추가가 O(1)&lt;/li&gt;
&lt;li&gt;원소의 제거가 O(1)&lt;/li&gt;
&lt;li&gt;제일 앞/뒤 원소 확인이 O(1)&lt;/li&gt;
&lt;li&gt;앞뒤 외의 원소들은 원칙적으로 확인/변경 불가능 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;추가 -rear&lt;br&gt;제거 -front&lt;br&gt;로 보통 나타낸다. &lt;/p&gt;
&lt;h1&gt;기능과 구현&lt;/h1&gt;
&lt;p&gt;스택 어렵지 않았으니까 걍 내가 구현해보자. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;const int MX = 100005;
int dat[MX];
int tail=0; // 추가 
int head=0; // 제거

void push(int x){
    dat[tail++]=x;
}

void pop(){
    head++;
}

int front(){
    return dat[head];
}

int back(){
    return dat[tail-1];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;제미나이한테 던졌더니 길이 제한 생각 안 하냐고 혼났다. 그치 대기큐 같은 병목 현상도 있지. 바킹독에서는 이를 다루고 있진 않다. 혹시 필요하다면 % 연산 기호로 원형 큐를 만들 수 있을 거 같다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main(void){
    queue&amp;lt;int&amp;gt; q;
    q.push(10);
    q.push(20);
    cout&amp;lt;&amp;lt;q.size()&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
    q.empty();
    q.pop();
    q.front();
    q.back();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;나왔던 오류&lt;/h1&gt;
&lt;h2&gt;연산자 관련&lt;/h2&gt;
&lt;p&gt;풀다가 연산자 우선순위 관련한 오류가 하나 나왔다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

queue&amp;lt;int&amp;gt; q;

int main(void){
    int n;
    cin&amp;gt;&amp;gt;n;
    while(n--){
        string inst;
        cin&amp;gt;&amp;gt;inst;
        if(inst==&amp;quot;push&amp;quot;){
            int nor;
            cin&amp;gt;&amp;gt;nor;
            q.push(nor);
        }
        else if(inst==&amp;quot;front&amp;quot;) cout&amp;lt;&amp;lt; (q.size()==0) ? -1 : q.front()&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
        else if(inst==&amp;quot;back&amp;quot;) cout&amp;lt;&amp;lt; (q.size()==0) ? -1 : q.back() &amp;lt;&amp;lt; &amp;#39;\n&amp;#39;;
        else if(inst==&amp;quot;size&amp;quot;) cout&amp;lt;&amp;lt;q.size()&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
        else if(inst==&amp;quot;empty&amp;quot;) cout&amp;lt;&amp;lt;q.empty()&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
        else if(inst==&amp;quot;pop&amp;quot;) {
            cout&amp;lt;&amp;lt; (q.size()==0) ? -1 : q.front()&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
            if(q.size()!=0) q.pop();
        }
    }
}

/*
❌ Test Case #1: Failed
--------------- Expected ---------------
1
2
2
0
1
2
-1
0
1
-1
0
3
---------------- Actual ----------------
002
0
0010
1
10
0

--------------- 채점 종료 ----------------
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자꾸 이런 오류가 뜨길래 제미나이한테 넘겼더니 연산 우선순위 때문이라고 한다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;// 작성하신 코드
cout &amp;lt;&amp;lt; (q.size()==0) ? -1 : q.front() &amp;lt;&amp;lt; &amp;#39;\n&amp;#39;;

// 컴퓨터의 해석 (괄호 위치 주목!)
( cout &amp;lt;&amp;lt; (q.size()==0) ) ? -1 : ( q.front() &amp;lt;&amp;lt; &amp;#39;\n&amp;#39; );&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 삼항 연산자보다 출력 연산자가 우선순위가 높다고 한다. &lt;/p&gt;
&lt;h2&gt;2^31-1&lt;/h2&gt;
&lt;p&gt;위 키워드 보면 int 최댓값임을 바로 파악해야 한다. &lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/225</guid>
      <comments>https://6kitt-hack.tistory.com/225#entry225comment</comments>
      <pubDate>Sun, 15 Feb 2026 11:18:17 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 스택</title>
      <link>https://6kitt-hack.tistory.com/224</link>
      <description>&lt;h1&gt;정의와 성질&lt;/h1&gt;
&lt;p&gt;스택은 LIFO이다. 큐, 덱, 스택 같은 특정 위치에서만 원소를 넣거나 뺄 수 있는 제한된 자료구조를 Restricted Structure라고 부른다. &lt;/p&gt;
&lt;h2&gt;스택의 성질&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;원소의 추가 O(1)&lt;/li&gt;
&lt;li&gt;원소의 제거 O(1)&lt;/li&gt;
&lt;li&gt;제일 상단의 원소 확인 O(1)&lt;/li&gt;
&lt;li&gt;나머지 원소들의 확인/변경 원칙적으로 불가능 &lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;기능과 구현&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;const int MX=1000005;
int dat[MX];
int pos=0;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;일단 여기서 원소를 담은 큰 배열과 인덱스를 저장할 변수 한 개만 있으면 구현이 가능하다. pos는 원소의 개수이자 추가할 때 삽입해야 하는 인덱스를 의미한다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

const int MX=1000005;
int dat[MX];
int pos=0;

void push(int x){
    dat[pos]=x;
    pos++; //dat[pos++]=x;로 한 줄로 표현도 가능, 전치 후치 차이에 대해 알고 있기 
}

void pop(){
    pos--; //여기까지만 써도 됨 
    /* 어차피 아래 코드들은 나중에 push할 때 덮어쓸 공간
    cout&amp;lt;&amp;lt;dat[pos];
    dat[pos]=0; */
}

void top(){
    return dat[pos-1];
}

void test(){

}

int main(void){
    test();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;음 연결 리스트 하다가 얘 하니까 구현은 쉽다. 그래도 바킹독 강의에서는 STL을 권장한다. STL stack을 썼으면 최소 스택 쪽에는 문제 없다는 걸 알 수 있으니 로직 개선에 신경 쓸 수 있다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main(void){
    stack&amp;lt;int&amp;gt; s;
    s.push(10);
    s.push(20);
    cout&amp;lt;&amp;lt;s.size()&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;; // 2
    s.empty(); // bool 
    s.pop(); //10만 남을 것 
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/224</guid>
      <comments>https://6kitt-hack.tistory.com/224#entry224comment</comments>
      <pubDate>Sun, 15 Feb 2026 11:18:10 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 연결 리스트</title>
      <link>https://6kitt-hack.tistory.com/223</link>
      <description>&lt;h1&gt;정의와 성질&lt;/h1&gt;
&lt;p&gt;연결 리스트의 성질 &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;k번째 원소를 확인/변경하기 위해 O(k)&lt;/li&gt;
&lt;li&gt;이미 정한 위치에 추가 및 제거는 O(1)&lt;/li&gt;
&lt;li&gt;메모상에 연속된 데이터는 아니라서 cache hit rate는 낮고, 할당은 쉬움 &lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;연결리스트 종류&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;단일 연결 리스트 &lt;/li&gt;
&lt;li&gt;이중 연결 리스트 : 양방향으로 연결되어 있어서 이전 이후 원소 주소를 둘 다 들고 있다. &lt;/li&gt;
&lt;li&gt;원형 연결 리스트 : 사이클 돈다 생각하면 편하다. &lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;기능과 구현&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;struct NODE {
    struct NODE *prev, *next;
    int data;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;손코딩 문제 1&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;원형 연결 리스트 내의 임의의 노드 하나가 주어졌을 때 해당 List의 길이를 효율적으로 구하는 방법? &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;효율적으로 구하라 -&amp;gt; 시간복잡도와 공간복잡도를 생각해봐야 한다. 동일한 노드가 나올 때까지 계속 다음 노드로 가면 된다. O(1), O(N)&lt;/p&gt;
&lt;h2&gt;손코딩 문제 2&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;각 연결 포인터가 가리키는 주소가 같아지는 순간이 교차점인데 A,B의 길이가 다르니까 더 긴 길이를 차만큼 빼줘서 동시에 도달할 수 있도록 한다. O(A+B)로 전체 노드 길이를 알기위한 순회는 필요하다. &lt;/p&gt;
&lt;h2&gt;손코딩 문제 3&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;주어진 연결 리스트 안에 사이클이 있는지 판단하라.&lt;br&gt;O(N)으로 주소 다 기록해놓고 이전에 나왔던 주소가 있는지 체킹하면 될 거 같은데.. 공간 복잡도 O(1) 방법이 있다고 한다.&lt;br&gt;Floyd&amp;#39;s cycle-finding algorithm으로 한 칸씩 이동하는 slow pointer와 두 칸씩 이동하는 fast pointer를 두면 사이클 내에서 꼭 같은 노드에서 만나게 된다. &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;야매 연결 리스트 예시&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;// http://boj.kr/5c97cb6a88324537a722e8de9169e2ab
#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

const int MX = 1000005;
char dat[MX];
int pre[MX]; 
int nxt[MX];
int unused = 1;

void insert(int addr, char num){
  dat[unused] = num;
  pre[unused] = addr;
  nxt[unused] = nxt[addr];
  if(nxt[addr] != -1) pre[nxt[addr]] = unused;
  nxt[addr] = unused;
  unused++;
}

void erase(int addr){
  nxt[pre[addr]] = nxt[addr];
  if(nxt[addr] != -1) pre[nxt[addr]] = pre[addr];
}

void traversal(){
  int cur = nxt[0];
  while(cur != -1){
    cout &amp;lt;&amp;lt; dat[cur];
    cur = nxt[cur];
  }
}

int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);
  fill(pre,pre+MX,-1);
  fill(nxt,nxt+MX,-1);
  string init;
  cin &amp;gt;&amp;gt; init;
  int cursor = 0;
  for(auto c : init){
    insert(cursor, c);
    cursor++;
  }  
  int q;
  cin &amp;gt;&amp;gt; q;
  while (q--) {
    char op;
    cin &amp;gt;&amp;gt; op;
    if (op == &amp;#39;P&amp;#39;) {
      char add;
      cin &amp;gt;&amp;gt; add;
      insert(cursor, add);
      cursor = nxt[cursor];
    }
    else if (op == &amp;#39;L&amp;#39;) {
      if (pre[cursor] != -1) cursor = pre[cursor];
    }
    else if (op == &amp;#39;D&amp;#39;) {
      if (nxt[cursor] != -1) cursor = nxt[cursor];
    }
    else { // &amp;#39;B&amp;#39;
      if (pre[cursor] != -1) {
        erase(cursor);
        cursor = pre[cursor];
      }
    }
  }
  traversal();
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/223</guid>
      <comments>https://6kitt-hack.tistory.com/223#entry223comment</comments>
      <pubDate>Sun, 15 Feb 2026 11:18:04 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 배열</title>
      <link>https://6kitt-hack.tistory.com/222</link>
      <description>&lt;h1&gt;정의와 성질&lt;/h1&gt;
&lt;p&gt;배열 : 메모리 상에 원소를 연속하게 배치한 자료구조 &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;O(1)에 k번째 원소 확인 가능 &lt;/li&gt;
&lt;li&gt;오버헤드 거의 없음 &lt;/li&gt;
&lt;li&gt;hit rate 높음 -&amp;gt; CS 확실히 중요하네.. &lt;/li&gt;
&lt;li&gt;연속된 자료구조이기 때문에 할당에 제약이 있다 -&amp;gt; 메모리상에도 연속적 &lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;기능과 구현&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

void insert(int idx, int num, int arr[], int&amp;amp; len){
  for(int i=len;i&amp;gt;idx;i--) // i가 길이부터 시작이니까 
    arr[i]=arr[i-1]; //i-1이 맞는 위치(?) 맞는 배열의 인덱스, 1번째 원소 인덱스 번호는 0
  arr[idx]=num; //그리고 넣어준다. 
  len++; //len도 하나 늘어난다. 
}

void erase(int idx, int arr[], int&amp;amp; len){
  len--; //길이를 먼저 한 번 지워야 
  for(int i=idx+1;i&amp;lt;len;i++) //여기서 줄인 idx에 맞는 
      arr[i-1]=arr[i];
}

void printArr(int arr[], int&amp;amp; len){
  for(int i = 0; i &amp;lt; len; i++) cout &amp;lt;&amp;lt; arr[i] &amp;lt;&amp;lt; &amp;#39; &amp;#39;;
  cout &amp;lt;&amp;lt; &amp;quot;\n\n&amp;quot;;
}

void insert_test(){
  cout &amp;lt;&amp;lt; &amp;quot;***** insert_test *****\n&amp;quot;;
  int arr[10] = {10, 20, 30};
  int len = 3;
  insert(3, 40, arr, len); // 10 20 30 40
  printArr(arr, len);
  insert(1, 50, arr, len); // 10 50 20 30 40
  printArr(arr, len);
  insert(0, 15, arr, len); // 15 10 50 20 30 40
  printArr(arr, len);
}

void erase_test(){
  cout &amp;lt;&amp;lt; &amp;quot;***** erase_test *****\n&amp;quot;;
  int arr[10] = {10, 50, 40, 30, 70, 20};
  int len = 6;
  erase(4, arr, len); // 10 50 40 30 20
  printArr(arr, len);
  erase(1, arr, len); // 10 40 30 20
  printArr(arr, len);
  erase(3, arr, len); // 10 40 30
  printArr(arr, len);
}

int main(void) {
  insert_test();
  erase_test();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;STL vector&lt;/h1&gt;
&lt;p&gt;배열과 거의 동일한 기능을 수행하는 자료구조이다. 배열과 마찬가지로 메모리에 연속적으로 원소를 저장하기 때문에 O(1)의 시간복잡도로 접근 가능하다. 배열은 고정 자료구조인 반면, vector는 동적으로 크기를 조절할 수 있다는 장점이 있다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc.h++&amp;gt;
using namespace std;

int main(void){
  vector&amp;lt;int&amp;gt; v1(3,5); // {5,5,5} 크기 3으로 초기화값 5인 벡터 생성 
  cout&amp;lt;&amp;lt;v1.size()&amp;lt;&amp;lt;&amp;#39;\n&amp;#39;;
  v1.push_back(7); //꼬리에 7 삽입 push 

  vector&amp;lt;int&amp;gt; v2(5); //{0,0,0,0,0}
  v2.insert(v2.begin()+1,3); //{0,3,0,0,0,0}

  vector&amp;lt;int&amp;gt; v3 ={1,2,3,4};
  v3.erase(v3.begin()+2); //{1,2,4}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;insert, erase는 O(n)&lt;br&gt;push_back, pop_back은 순회가 필요 없으므로 O(1)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;vector&amp;lt;int&amp;gt; v1={1,2,3,4,5,6}; //list, map, set 등에서도 모두 사용 가능 

for(int e:v1) //int&amp;amp; e:v1이라고 쓰면 for문 내에서 e를 바꿨을 때, 원본 v1도 같이 바뀜
  cout&amp;lt;&amp;lt;e&amp;lt;&amp;lt;&amp;#39; &amp;#39;;

for(int i=0;i&amp;lt;v1.size();i++)
  cout&amp;lt;&amp;lt;v1[i]&amp;lt;&amp;lt;&amp;#39; &amp;#39;;

for(int i=0;i&amp;lt;=v1.size()-1;i++)
  cout&amp;lt;&amp;lt;v1[i]&amp;lt;&amp;lt;&amp;#39; &amp;#39;;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;마지막 3번째 예시는 32비트 컴퓨터 기준 런타임에러가 생길 수 있다. vector.size()는 unsigned int 혹은 unsigned long long을 반환한다. 빈 vector일 때 v1.size()-1이 unsigned int 0에서 int 1을 빼는 식이 되고, 이렇게 되면 unsigned int로 자동 형변환이 된다. 때문에 0-1=-1이 아니라 4294967295가 된다(unsigned int의 자료형 범위)&lt;/p&gt;
&lt;h1&gt;연습 문제&lt;/h1&gt;
&lt;h2&gt;문제 2&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int func2(int arr[],int N){
  for(int i=0;i&amp;lt;len(arr);i++){
    int occur[101]={};
    for(int i=0;i&amp;lt;N;i++){
      if(occur[100-arr[i]]==1)
        return 1;
      occur[arr[i]]=1;
    }
    return 0;
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/222</guid>
      <comments>https://6kitt-hack.tistory.com/222#entry222comment</comments>
      <pubDate>Sun, 15 Feb 2026 11:17:56 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : STL과 함수 인자, 표준 입출력</title>
      <link>https://6kitt-hack.tistory.com/221</link>
      <description>&lt;h1&gt;STL과 함수 인자&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;void func(int a){
    a=5;
}

int main(void){
    int t=0;
    func(t);
    cout&amp;lt;&amp;lt;t;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;void func(int arr[]){
    arr[0]=10;
}

int main(void){
    int arr[3]={1,2,3};
    func(arr);
    cout&amp;lt;&amp;lt;arr[0];
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;struct pt{
    int x,y;
};

void func(pt a){
    a.x=10;
}

int main(void){
    pt tmp={0,0};
    func(tmp);
    cout&amp;lt;&amp;lt;tmp.x;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3번째 구조체도 변수 넘겨주는 거랑 같게 이해하면 됨 -&amp;gt; 복사&lt;/p&gt;
&lt;h2&gt;call 방법&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;call by value -&amp;gt; 안 바뀜, 공간 하나 더 생김 &lt;/li&gt;
&lt;li&gt;call by address -&amp;gt; 바뀜 (배열)&lt;/li&gt;
&lt;li&gt;call by reference -&amp;gt; 바뀜 (구조체)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;void swap1(int a, int b);
void swap2(int* a, int* b); //pointer 
void swap3(int&amp;amp; a, int&amp;amp; b); //referencer &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;swap1은 그냥 변수 call by value&lt;br&gt;swap2는 주소 전달&lt;br&gt;swap3는 참조 (넘길 때는 변수 넘기면 됨)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;vector&amp;lt;int&amp;gt; v(100);
v[20]=10;
v[60]=-2;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;void func1(vector&amp;lt;int&amp;gt; v){
    v[10]=7; //여기에서 값을 바꿨다고
}

int main(void){
    vector&amp;lt;int&amp;gt; v(100);
    func1(v);
    cout &amp;lt;&amp;lt; v[10]; //여기서 바뀌진 않음 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;STL을 함수 인자로 넘길 땐 어떨까.. 는 그냥 call by value&lt;br&gt;call by value는 복사를 하니까 원소마다 다 참조 -&amp;gt; O(n) &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;bool cmp1 (vector&amp;lt;int&amp;gt; v1, vector&amp;lt;int&amp;gt; v2, int idx){
    return v1[idx] &amp;gt; v2[idx];
}

bool cmp2 (vector&amp;lt;int&amp;gt;&amp;amp; v1, vector&amp;lt;int&amp;gt;&amp;amp; v2, int idx){
    return v1[idx] &amp;gt; v2[idx]; //-&amp;gt; 이건 참조만 하니까 시간복잡도 O(1)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;유의하자~ &lt;/p&gt;
&lt;h1&gt;표준 입출력&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int main(void){
    string s=&amp;quot;6kitty&amp;quot;;
    printf(&amp;quot;s is %s\n&amp;quot;,s);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int main(void){
    char a[10];
    printf(&amp;quot;input: &amp;quot;);
    scanf(&amp;quot;%s&amp;quot;,a);
    string s(a);
    printf(&amp;quot;a is %s\n&amp;quot;,a);
    printf(&amp;quot;s is %s\n&amp;quot;,s.c_str());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;scanf/printf에서는 C++ string 처리가 안되다. -&amp;gt; 개귀찮으니까 그냥 cin/cout 쓰자 &lt;/p&gt;
&lt;p&gt;아 그리고 공백 포함은 그냥 받으면 안된다 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;// 1. scanf 
char a[10];
scanf(&amp;quot;%[^\n]&amp;quot;, a);

// 2. getline 
string s;
getline(cin,s);
cout&amp;lt;&amp;lt;s;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;getline 쓸 때는 string형만 올 수 있다는 걸 유념하자.&lt;br&gt;cin/cout에서 주의해야할 것이 하나 더 있다. 입출력으로 인한 시간초과를 막기 위해 &lt;code&gt;ios::sync_with_stdio(0)&lt;/code&gt;, &lt;code&gt;cin.ie(0)&lt;/code&gt; 명령을 실행해줘야 한다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int main(void){
    ios::sync_with_stdio(0);
    cout&amp;lt;&amp;lt;&amp;quot;1111\n&amp;quot;;
    printf(&amp;quot;1111\n&amp;quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++ stream과 C stream은 동기화되어 있다. 그런데 내가 cin/cout만 사용할 것이라면 두 동기화하는 시간이 필요 없어지기에 ios::sync_with_stdio(0)로 싱크를 굳이 하지 않는다. &lt;/p&gt;
&lt;p&gt;대신 cout, printf를 섞어쓰면 안됨 -&amp;gt; 오류가 나는 건 아니고 순서가 꼬인다. &lt;/p&gt;
&lt;p&gt;cin을 위해서는 &lt;code&gt;cin.tie(0)&lt;/code&gt;가 필요하다. 문자를 입력하면 입력 버퍼에 모았다가 변수로 넘겨준다. 즉각적으로 메모리에 담기는 것이 아닌 버퍼라는 중간 매개체가 있다. 입출력이 같은 버퍼를 쓰고 있기 때문에 순서가 꼬일 수 있다. 때문에 cin 명령 수행 전에 cout 버퍼를 지워야 한다. 그렇지만 저지 채점할 때는 단순 출력 글자로만 채점하기 때문에 굳이 순서 꼬이지 않게 전처리 하지 않아도 된다. -&amp;gt; 굳이 cout 버퍼를 비우는 수고를 할 필요가 없다. &lt;/p&gt;
&lt;p&gt;그리고 &lt;strong&gt;endl&lt;/strong&gt; 쓰지 말아야 한다. -&amp;gt; 시간 소모가 꽤 되나 봄 &lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/221</guid>
      <comments>https://6kitt-hack.tistory.com/221#entry221comment</comments>
      <pubDate>Sat, 14 Feb 2026 20:34:00 +0900</pubDate>
    </item>
    <item>
      <title>바킹독의 실전 알고리즘 : 시공간 복잡도, 정수 자료형, 실수 자료형</title>
      <link>https://6kitt-hack.tistory.com/220</link>
      <description>&lt;h1&gt;시간복잡도와 공간복잡도&lt;/h1&gt;
&lt;p&gt;컴퓨터는 1초에 대략 3-5억개 정도의 연산을 처리할 수 있다. 연산이 단순 연산인지, 나눗셈, 곱셈, 함수 호출 같은 복잡한 연산인지에 따라 약간의 차이는 있을 수 있다. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int func1(int arr[], int n){
    int cnt =0;
    for(int i=0;i&amp;lt;n;i++){
        if(arr[i]%5==0) cnt++;
    }

    return cnt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같은 코드가 있다. 여기서의 연산 횟수를 확인해보자 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int func1(int arr[], int n){
    int cnt =0; //1번 
    for(int i=0;i&amp;lt;n;i++){ // 초기값 0 설정으로 1번
        if(arr[i]%5==0) cnt++; // 5의 나머지 구하기로 1번, 0이랑 맞는지 1번, cnt 증가 1번 
        //i 증가 1번, i가 n보다 작은지 확인 1번 
    }

    return cnt; //1번 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1+1+5n+1= 5n+3 &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FbP3qZ8%2FbtqQUZRdtcQ%2FAAAAAAAAAAAAAAAAAAAAAKdBudVTkNeWSnNn2rQVKjpco9B0SuX8YMfPgm9ej_xQ%2Fimg.png&quot;&gt;문제&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;최선의 경우 1초 (바로 정답)&lt;br&gt;최악의 경우 n초 (가장 나중이 정답)&lt;br&gt;평균은 중간에 있을 때 n/2 &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FcrDaCf%2FbtqQ1McjaN7%2FAAAAAAAAAAAAAAAAAAAAABPB2e1NoLkEiOqHvVj6xetmERQdxKJsIdez6a53VhZs%2Fimg.png&quot;&gt;문제&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;최선의 경우 1초 &lt;/p&gt;
&lt;p&gt;최악의 경우는..&lt;br&gt;n명이 있는데&lt;br&gt;가운데에 질의해서 왼쪽, 오른쪽으로 방향을 나누고&lt;br&gt;또 가운데에 질의해서..&lt;br&gt;숫자를 대입해보면 더 쉬움&lt;br&gt;16명일 때면&lt;br&gt;8명으로 나누고&lt;br&gt;4명으로 나누고&lt;br&gt;2명으로 나누면&lt;br&gt;1명이 걸러질 것 -&amp;gt; 총 4명&lt;br&gt;log&lt;sub&gt;2&lt;/sub&gt;N&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;빅오표기법은 대표항만 남겨두는 방법이다. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FbW9SaA%2FbtqBUTMae2i%2FAAAAAAAAAAAAAAAAAAAAAPRblRjIENh9sqMVNERe-w0J64i5adxYHB7RTvHH_rDL%2Fimg.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;내 풀이가 빅오로 몇이고 &lt;/li&gt;
&lt;li&gt;제한시간 내에 통과할 수 있는지 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;n이 500인데 O(2&lt;sup&gt;n&lt;/sup&gt;) 풀이를 했다면 시간 내에 불가하다. &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href=&quot;https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FFf2du%2Fbtq3q5Ski8U%2FAAAAAAAAAAAAAAAAAAAAAElSTSoszSN57z_tAP0KYIgggR9JJz2BvpcgEyi9bFu-%2Fimg.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int N;
cin&amp;gt;&amp;gt;N;
int sum=0;

for(int i=1;i&amp;lt;=N;i++){
    if(i%3==0 || i%5==0)
        sum+=i;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드는 O(n)이다. 이걸 O(1)로 줄일 수 있다고 한다.&lt;br&gt;흠.. 3,5,6,9,10..&lt;br&gt;N=3, sum=3&lt;br&gt;N=4, sum=3&lt;br&gt;N=5, sum=8&lt;br&gt;N=6, sum=14&lt;br&gt;N=7, sum=14&lt;br&gt;N=8, sum=14&lt;br&gt;N=9, sum=23&lt;br&gt;N=10, sum=33 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int N; 
cin&amp;gt;&amp;gt;N;

//3 
int n= (N-(N%3))/3;
int p=n/2(2*3+(n-1)*3)

//5
int m,q=0;
if (N&amp;gt;=5){
    m=(N-(N%5))/5;
    q=m/2(2*5+(m-1)*5);
}

//15
int x,o=0;
if (N&amp;gt;=15){
    x=(N-(N%15))/15;
    o=x/2(2*15+(x-1)*15);
}

int sum=p+q-o;
return sum;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;제출한 답 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

// 1부터 n까지 k의 배수의 합을 구하는 함수
long long sumMultiples(int n, int k) {
    int count = n / k; // 배수의 개수 (ex: 10까지 3의 배수 개수 = 3개)

    // 공식: k * (1 + 2 + ... + count) = k * count * (count + 1) / 2
    // long long 형변환을 통해 큰 수 계산 시 오버플로우 방지
    return (long long)k * count * (count + 1) / 2;
}

int main() {
    int N;
    if (!(cin &amp;gt;&amp;gt; N)) return 0;

    // 포함-배제의 원리 적용
    long long ans = sumMultiples(N, 3) + sumMultiples(N, 5) - sumMultiples(N, 15);

    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;코드 리뷰&lt;/strong&gt; &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;integer division 조심 : 알고리즘에서 곱셈을 먼저하고 나중에 나눗셈 진행하기 &lt;/li&gt;
&lt;li&gt;오버플로우 방지를 위해 &lt;code&gt;long long&lt;/code&gt; 선언 &lt;/li&gt;
&lt;li&gt;포함-배제의 원리 적용&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;∣A∪B∣=∣A∣+∣B∣−∣A∩B∣ 집합 문제처럼 풀기 &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;li&gt;코드 사용성 높이기 &lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;long long ans = sumMultiples(N, 3) + sumMultiples(N, 5) - sumMultiples(N, 15); &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href=&quot;https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2Fbrf73b%2FbtqQZwgHvWD%2FAAAAAAAAAAAAAAAAAAAAAA3aRS5sgXIHKj8qhN7vLwchZdUHIE6zB3OZN_iS_bh9%2Fimg.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int func2(int arr[],int N){

}

int input;
int arr[1001]={0,};
cin &amp;gt;&amp;gt; input;

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

return func2(arr,input);&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h1&gt;공간 복잡도&lt;/h1&gt;
&lt;p&gt;코테에서는 거의 시간복잡도가 까다롭기 때문에 중요한 내용은 아니라고 생각하지만 일단 보자. &lt;/p&gt;
&lt;p&gt;512MB는 약간 1.2억개의 int 선언이 가능하다. &lt;/p&gt;</description>
      <category>coding/바킹독</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/220</guid>
      <comments>https://6kitt-hack.tistory.com/220#entry220comment</comments>
      <pubDate>Fri, 13 Feb 2026 20:32:46 +0900</pubDate>
    </item>
    <item>
      <title>[WebHacking.kr] old-54번 문제 풀이 (Write-up)</title>
      <link>https://6kitt-hack.tistory.com/219</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Web2/old-54 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 동작 방식을 분석 -&amp;gt; 로직을 수정하여 실행하는 Client side scripting 문제&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;문제 분석 (Analysis)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;f12로 소스 코드를 확인 src/script.js&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function run(){
  if(window.ActiveXObject){
   try {
    return new ActiveXObject('Msxml2.XMLHTTP');
   } catch (e) {
    try {
     return new ActiveXObject('Microsoft.XMLHTTP');
    } catch (e) {
     return null;
    }
   }
  }else if(window.XMLHttpRequest){
   return new XMLHttpRequest();

  }else{
   return null;
  }
}

x=run();

function answer(i){
  x.open('GET','?m='+i,false);
  x.send(null);
  aview.innerHTML=x.responseText;
  i++;
  if(x.responseText) setTimeout(&quot;answer(&quot;+i+&quot;)&quot;,20);
  if(x.responseText==&quot;&quot;) aview.innerHTML=&quot;?&quot;;
}

setTimeout(&quot;answer(0)&quot;,1000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;answer(i) 함수가 핵심적인 역할이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XMLHttpRequest 객체를 생성 -&amp;gt; ?m=i 파라미터를 통해 서버에 요청을 보냄 -&amp;gt; 여기서 i는 0부터 1씩 증가한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버로부터 받은 응답(x.responseText)을 aview.innerHTML에 덮어쓴다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 값이 존재하면 20ms 뒤에 i를 1 증가시켜 재귀적으로 answer 함수를 호출한다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;익스플로잇 &lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;overwrite 말고 concatenate로 출력 로직 변경&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;var merged = '';
var i = 0;
var xhr = new XMLHttpRequest();

// 무한 루프를 돌며 순차적으로 요청을 보냄
while(true) {
  // 동기(false) 방식으로 요청하여 순서 보장
  xhr.open('GET', '?m=' + i, false);
  xhr.send(null);

  // 응답이 없으면(플래그 끝) 반복 종료
  if(!xhr.responseText) break;

  // 응답 받은 글자를 누적
  merged += xhr.responseText;
  i++;
}

// 결과 출력
console.log(&quot;FLAG:&quot;, merged);
alert(merged);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드 설명:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while(true) 문을 사용하여 계속해서 요청을 보낸다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xhr.open의 세 번째 인자를 false로 설정하여 동기(Synchronous)로 설정해야 응답이 올 때까지 대기(만약에 비동기로 설정하면 응답 수신을 생각 안 하고 다음 명령으로 넘어갈 수 있으므로 오류날 수 있다)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;merged += xhr.responseText를 통해 한 글자씩 받은 데이터를 계속 이어 붙인다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 더 이상 문자를 반환하지 않으면 break로 루프를 탈출하고 완성된 문자열을 출력한다.&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;XMLHttpRequest의 open()함수의 인자 설명&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;1. http 메서드 지정( get 혹은 post)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;2. 접속할 url을 입력한다&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;3. true가 비동기, false가 동기&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>wargame/webhacking.kr</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/219</guid>
      <comments>https://6kitt-hack.tistory.com/219#entry219comment</comments>
      <pubDate>Mon, 12 Jan 2026 09:33:12 +0900</pubDate>
    </item>
    <item>
      <title>gdb</title>
      <link>https://6kitt-hack.tistory.com/217</link>
      <description>&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;Stage 3.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;gdb 설치&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;디버거: 프로그램을 어셈블리 코드 단위로 실행하고 결과를 보여줌, 버그 찾기&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;gdb: 리눅스의 디버거&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bClTbU/btsNWQHU410/XdCXKAXLx4mOPk7MFLvqSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bClTbU/btsNWQHU410/XdCXKAXLx4mOPk7MFLvqSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bClTbU/btsNWQHU410/XdCXKAXLx4mOPk7MFLvqSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbClTbU%2FbtsNWQHU410%2FXdCXKAXLx4mOPk7MFLvqSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;424&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디버깅 실습 1&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pwpov/btsNWtfacQz/lRu20n6T3OfXnuHMKGZOo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pwpov/btsNWtfacQz/lRu20n6T3OfXnuHMKGZOo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pwpov/btsNWtfacQz/lRu20n6T3OfXnuHMKGZOo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpwpov%2FbtsNWtfacQz%2FlRu20n6T3OfXnuHMKGZOo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;348&quot; height=&quot;270&quot; data-origin-width=&quot;348&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;vi 편집기를 열어서 위 코드를 입력해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IAChi/btsNWKgIdVW/gkuGxNXSDBdXtAI46qkWh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IAChi/btsNWKgIdVW/gkuGxNXSDBdXtAI46qkWh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IAChi/btsNWKgIdVW/gkuGxNXSDBdXtAI46qkWh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIAChi%2FbtsNWKgIdVW%2FgkuGxNXSDBdXtAI46qkWh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;722&quot; height=&quot;482&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;482&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컴파일: $ gcc -o debugee debugee.c -no-pie&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;디버깅 시작: $ gdb debugee&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. entry&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ELF(Executable and Linkable Format): 리눅스의 실행파일 형식, 헤더와 여러 섹션으로 구성&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 1) 헤더: 실행에 필요한 정보&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 2) 섹션: 컴파일된 기계어 코드, 프로그램 문자열 등&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;진입점 필드부터 프로그램 실행&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WXRfg/btsNV3VBepc/l5IIw6xdkgxYu0rLLuT35K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WXRfg/btsNV3VBepc/l5IIw6xdkgxYu0rLLuT35K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WXRfg/btsNV3VBepc/l5IIw6xdkgxYu0rLLuT35K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWXRfg%2FbtsNV3VBepc%2Fl5IIw6xdkgxYu0rLLuT35K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;390&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;debugee에서 진입점 0x401050&lt;/p&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhE2xE/btsNWMZO3Re/Y9MoajkuZFxe6dfhv6968K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhE2xE/btsNWMZO3Re/Y9MoajkuZFxe6dfhv6968K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhE2xE/btsNWMZO3Re/Y9MoajkuZFxe6dfhv6968K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhE2xE%2FbtsNWMZO3Re%2FY9MoajkuZFxe6dfhv6968K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;538&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dr7Tsx/btsNVriovWn/aSk8K55CcQw5Kj00FBZo8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dr7Tsx/btsNVriovWn/aSk8K55CcQw5Kj00FBZo8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dr7Tsx/btsNVriovWn/aSk8K55CcQw5Kj00FBZo8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdr7Tsx%2FbtsNVriovWn%2FaSk8K55CcQw5Kj00FBZo8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;740&quot; height=&quot;512&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;gdb의 entry 명령어&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Temporary breakpoint 1 at 0x401050 확인&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DISASM 왼쪽 화살표(현재 rip 값) 확인: 엔트리 포인트에 위치하여 있음&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;context: 컴퓨터의 각종 메모리 상태&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 1) REGISTERS: 레지스터 상태&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 2) DISASM: rip부터 디스어셈블된 결과 출력&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3) STACK: rsp부터 스택의 값 출력&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 4) BACKTRACE: 현재 rip 도달할 때까지 중첩되어 있는 함수들 출력&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. break &amp;amp; continue&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;break: 특정 주소에 중단점 설정&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;continue: 중단된 프로그램 계속 실행&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckHiTg/btsNWOwBPFo/rZH4dwUsxhBeTdyfc4s0m0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckHiTg/btsNWOwBPFo/rZH4dwUsxhBeTdyfc4s0m0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckHiTg/btsNWOwBPFo/rZH4dwUsxhBeTdyfc4s0m0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckHiTg%2FbtsNWOwBPFo%2FrZH4dwUsxhBeTdyfc4s0m0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;484&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;main 함수(0x401136)에 중단점 설정하고(break)&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실행시키는(continue) 명령&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3. run&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;start는 진입점부터 분석할 수 있게 자동으로 중단점 부여&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;run은 단순 실행, 중단점이 없다면 프로그램이 멈추지 않고 실행됨&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFc70S/btsNXr2chk6/DcgpT9450PmPKXwGvteFLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFc70S/btsNXr2chk6/DcgpT9450PmPKXwGvteFLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFc70S/btsNXr2chk6/DcgpT9450PmPKXwGvteFLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFc70S%2FbtsNXr2chk6%2FDcgpT9450PmPKXwGvteFLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;436&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4. disassembley&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;disassemble: gdb가 기본 제공하는 디스어셈블 명령어, 함수 이름을 인자로 전달, 함수가 반환값을 설정하기 전까지 디스어셈블 실행&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s3RUh/btsNWFl99Jb/9rKN635Xpq5kfezRYcUJF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s3RUh/btsNWFl99Jb/9rKN635Xpq5kfezRYcUJF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s3RUh/btsNWFl99Jb/9rKN635Xpq5kfezRYcUJF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs3RUh%2FbtsNWFl99Jb%2F9rKN635Xpq5kfezRYcUJF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;698&quot; height=&quot;434&quot; data-origin-width=&quot;698&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;main함수 디스어셈블하는 명령어&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zf3DO/btsNVgH5YvD/RTeiNKQOefTlQSiz7eO7kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zf3DO/btsNVgH5YvD/RTeiNKQOefTlQSiz7eO7kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zf3DO/btsNVgH5YvD/RTeiNKQOefTlQSiz7eO7kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZf3DO%2FbtsNVgH5YvD%2FRTeiNKQOefTlQSiz7eO7kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;476&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;pwndbg가 제공하는 디스어셈블 명령어&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;u, nearpc:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;디스어셈블된 코드 가독성 좋게 출력&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;5. next instruction &amp;amp; step into&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;둘 다 명령어 한 줄씩 실행&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ni: 서브루틴 내부 진입 X&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;si: 서브루틴 내부 진입 O&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/37Plu/btsNW55YefN/M8gx9UUeknTfD4dSFkYUSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/37Plu/btsNW55YefN/M8gx9UUeknTfD4dSFkYUSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/37Plu/btsNW55YefN/M8gx9UUeknTfD4dSFkYUSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F37Plu%2FbtsNW55YefN%2FM8gx9UUeknTfD4dSFkYUSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;460&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;main함수에서 printf 함수를 호출하는 지점까지 실행&lt;/p&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lJXnH/btsNWoEWJZK/T26DgHsMxZkqTbzMC8yR2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lJXnH/btsNWoEWJZK/T26DgHsMxZkqTbzMC8yR2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lJXnH/btsNWoEWJZK/T26DgHsMxZkqTbzMC8yR2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlJXnH%2FbtsNWoEWJZK%2FT26DgHsMxZkqTbzMC8yR2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;532&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYR3PN/btsNWLtcaRn/epCMpk8TgkKRXEmpmSDfv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYR3PN/btsNWLtcaRn/epCMpk8TgkKRXEmpmSDfv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYR3PN/btsNWLtcaRn/epCMpk8TgkKRXEmpmSDfv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYR3PN%2FbtsNWLtcaRn%2FepCMpk8TgkKRXEmpmSDfv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;522&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ni와 si의 차이점&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;ni는 rip가 0x40117b로 이동 &amp;lt;main+69&amp;gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;si는 rip가 0x401040로 이동 &amp;lt;printf@plt&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHKqxq/btsNWorray2/MhsiD79QWLrpnh0FKRok6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHKqxq/btsNWorray2/MhsiD79QWLrpnh0FKRok6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHKqxq/btsNWorray2/MhsiD79QWLrpnh0FKRok6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHKqxq%2FbtsNWorray2%2FMhsiD79QWLrpnh0FKRok6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;536&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;finish: step into로 들어가서 나올 때 함수 끝까지 실행시킴&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;6.examine&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;x: 특정 주소에서, 원하는 길이만큼, 원하는 형식으로 인코딩&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot; rowspan=&quot;11&quot;&gt;format letter&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;o&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;octal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;x&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;hex&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;d&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;decimal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;u&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;unsigned decimal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;t&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;f&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;float&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;a&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;i&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;instruction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;c&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;char&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;s&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;z&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;hex, zero padded on the left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot; rowspan=&quot;4&quot;&gt;size letter&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;b&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;h&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;halfword&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;w&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;g&lt;/td&gt;
&lt;td style=&quot;color: #333333; text-align: center;&quot;&gt;giant, 8 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8utzH/btsNV1wKUSR/TkiBEzd7nnBmNQyX9ojygk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8utzH/btsNV1wKUSR/TkiBEzd7nnBmNQyX9ojygk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8utzH/btsNV1wKUSR/TkiBEzd7nnBmNQyX9ojygk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8utzH%2FbtsNV1wKUSR%2FTkiBEzd7nnBmNQyX9ojygk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;318&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;x/10gx $rsp : rsp부터 80바이트를 8바이트씩 hex형식으로 출력&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;x/5i $rip : rip부터 5줄의 어셈블리 명령어 출력&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;x/s 0x400000 : 특정 주소의 문자열 출력&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;7. telescope&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;tele: 메모리가 참조하는 주소 탐색하여 값을 보여준다&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FOYij/btsNWh0l6ou/r1xQP8uK3BLGL2u8l4Xos0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FOYij/btsNWh0l6ou/r1xQP8uK3BLGL2u8l4Xos0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FOYij/btsNWh0l6ou/r1xQP8uK3BLGL2u8l4Xos0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFOYij%2FbtsNWh0l6ou%2Fr1xQP8uK3BLGL2u8l4Xos0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;210&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;8. vmmap&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가상 메모리의 레이아웃 출력&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;파일이 매핑된 영역일 경우, 파일의 경로도 보여준다&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;파일 매핑: 파일을 메모리에 배치(가상 공간 할애)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cx99bp/btsNUP44Gve/b06gfRDOY9H8vvigPwMeRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cx99bp/btsNUP44Gve/b06gfRDOY9H8vvigPwMeRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cx99bp/btsNUP44Gve/b06gfRDOY9H8vvigPwMeRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcx99bp%2FbtsNUP44Gve%2Fb06gfRDOY9H8vvigPwMeRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;514&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디버깅 실습 2&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn8aXh/btsNVA0uw8v/vIKvumks8MJb06kw2yHk4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn8aXh/btsNVA0uw8v/vIKvumks8MJb06kw2yHk4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn8aXh/btsNVA0uw8v/vIKvumks8MJb06kw2yHk4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn8aXh%2FbtsNVA0uw8v%2FvIKvumks8MJb06kw2yHk4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;422&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;vi 편집기로 위 코드를 작성해준다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ACqMm/btsNWZYZHF6/cFCuAQGPzbdgEgg2lsX9mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ACqMm/btsNWZYZHF6/cFCuAQGPzbdgEgg2lsX9mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ACqMm/btsNWZYZHF6/cFCuAQGPzbdgEgg2lsX9mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FACqMm%2FbtsNWZYZHF6%2FcFCuAQGPzbdgEgg2lsX9mk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;734&quot; height=&quot;518&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;디버깅 시작&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YnaXr/btsNWIXup3n/8SBQ8mKs0lC4kS2FHSuUJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YnaXr/btsNWIXup3n/8SBQ8mKs0lC4kS2FHSuUJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YnaXr/btsNWIXup3n/8SBQ8mKs0lC4kS2FHSuUJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYnaXr%2FbtsNWIXup3n%2F8SBQ8mKs0lC4kS2FHSuUJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;140&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;run $(파이썬 print 함수 작성) 입력을 하면 파이썬 print 함수 안 값이 run의 인자로 전달된다&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SQc7U/btsNV4GVEiB/mH5a1xQTsHnYg1QxL8YOak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SQc7U/btsNV4GVEiB/mH5a1xQTsHnYg1QxL8YOak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SQc7U/btsNV4GVEiB/mH5a1xQTsHnYg1QxL8YOak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSQc7U%2FbtsNV4GVEiB%2FmH5a1xQTsHnYg1QxL8YOak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;182&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;입력값으로 전달하기 위해서는 &amp;lt;&amp;lt;&amp;lt; 사용한다&amp;nbsp;&lt;/p&gt;</description>
      <category>security: study/pwn: dreamhack</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/217</guid>
      <comments>https://6kitt-hack.tistory.com/217#entry217comment</comments>
      <pubDate>Tue, 13 May 2025 22:17:36 +0900</pubDate>
    </item>
    <item>
      <title>php-1</title>
      <link>https://6kitt-hack.tistory.com/216</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;file=../uploads/flag.php&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위를 입력하면 되는데 flag가 필터링된다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규식 공부를 해보자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 여기에서 쓰인 건 /flag|:/i인데&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag|:는 flag와 : 중에서 하나를 찾는다는 의미이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i는 대소문자 구분하지 않고 검색한다는 뜻이다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flag, FLAG, Flag 모두 포함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 스트링 우회 기법을 써보자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dolcevie.com/js/converter.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dolcevie.com/js/converter.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745815034870&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Hex/Ascii Converter&quot; data-og-description=&quot;Hex: 41:6e:74:6f:6e:20:69:73:20:67:72:65:61:74:20:3a:29&quot; data-og-host=&quot;dolcevie.com&quot; data-og-source-url=&quot;https://dolcevie.com/js/converter.html&quot; data-og-url=&quot;https://dolcevie.com/js/converter.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dolcevie.com/js/converter.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dolcevie.com/js/converter.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Hex/Ascii Converter&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Hex: 41:6e:74:6f:6e:20:69:73:20:67:72:65:61:74:20:3a:29&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dolcevie.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;char(0x666c6167)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0x666c6167&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;%66lag.php&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;i 옵션 우회는 모르겠고 m 옵션은 개행 대신 ; 써주면 된다고 한다 공백은 $IFS&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 필터링 우회는 view 페이지가 아닌 ?page로 값을 넘겨줘야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;?page=/var/www/uploads/flag.php&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 안되길래 마지막 확장자 제거 Can you see $flag?라고 뜸&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LFI 취약점에 대해 더 공부해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 측 script 언어에서 발생하는 취약점 -&amp;gt; 애플리케이션의 입력 검증 부족&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;php에서는 include 사용하면 input에 대한 필터링 존재하지 않아서 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;php wrapper를 활용해서 공격할 수 있다고 함&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745900053573&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;file:// &amp;mdash; Accessing local filesystem
http:// &amp;mdash; Accessing HTTP(s) URLs
ftp:// &amp;mdash; Accessing FTP(s) URLs
php:// &amp;mdash; Accessing various I/O streams
zlib:// &amp;mdash; Compression Streams
data:// &amp;mdash; Data (RFC 2397)
glob:// &amp;mdash; Find pathnames matching pattern
phar:// &amp;mdash; PHP Archive
ssh2:// &amp;mdash; Secure Shell 2
rar:// &amp;mdash; RAR
ogg:// &amp;mdash; Audio streams
expect:// &amp;mdash; Process Interaction Streams&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;expect, php://filter/, zip://을 주로 쓴다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. expect://&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;system command 실행시킴&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. php://filter/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;www.[웹사이트주소]/?file=php://filter/convert.base64-encode/resource=&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위를 입력하면 인코딩 파일 얻을 수 있음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. zip://&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zip 파일 압출을 풀고 파일 안 코드 실행시킴&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot;&gt;www.[우리가 공격할 웹 사이트의 주소].index.php?page=php://filter/convert.base64-encode/resource=/etc/passwd&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 쓸 수 있다고 함&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666; text-align: start;&quot;&gt;?page=php://filter/convert.base64-encode/resource= /var/www/uploads/flag.php&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gchq.github.io/CyberChef/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gchq.github.io/CyberChef/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2322&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0falA/btsNDL8MAeo/zPRtvpK0SyW7WyMkuGGhG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0falA/btsNDL8MAeo/zPRtvpK0SyW7WyMkuGGhG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0falA/btsNDL8MAeo/zPRtvpK0SyW7WyMkuGGhG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0falA%2FbtsNDL8MAeo%2FzPRtvpK0SyW7WyMkuGGhG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2322&quot; height=&quot;1058&quot; data-origin-width=&quot;2322&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>wargame/dreamhack</category>
      <category>webhacking</category>
      <author>육키티</author>
      <guid isPermaLink="true">https://6kitt-hack.tistory.com/216</guid>
      <comments>https://6kitt-hack.tistory.com/216#entry216comment</comments>
      <pubDate>Tue, 29 Apr 2025 13:20:49 +0900</pubDate>
    </item>
  </channel>
</rss>