<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://teus.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://teus.me/" rel="alternate" type="text/html" /><updated>2026-06-12T10:45:47+09:00</updated><id>https://teus.me/feed.xml</id><title type="html">TEUS.me</title><subtitle>구라제거기의 고향에 잘 오셨습니다</subtitle><author><name>BLUEnLIVE</name></author><entry><title type="html">나에게 주는 선물꞉ 알리 산 백식</title><link href="https://teus.me/etc/Hyaku_shiki/" rel="alternate" type="text/html" title="나에게 주는 선물꞉ 알리 산 백식" /><published>2026-06-10T12:37:00+09:00</published><updated>2026-06-10T12:37:00+09:00</updated><id>https://teus.me/etc/Hyaku_shiki</id><content type="html" xml:base="https://teus.me/etc/Hyaku_shiki/"><![CDATA[<p><strong>우주세기 건담</strong>의 오랜 팬으로서, 내 최애기는 <strong>백식</strong>이다.<br />
하지만, 마지막으로 사서 조립한 백식은 <strong>20년</strong>도 넘었다.<br />
1999년에 발매한 <strong>HGUC 백식</strong>이 마지막으로 조립해본 백식이니…</p>

<p><a href="https://ko.aliexpress.com/item/1005009769977472.html">알리 익스프레스에 백식이 저렴하게 올라와서</a> 나에게 주는 선물로 하나 구입했다.</p>

<p><img src="/images/2026-06-10/20260609_044753585_iOS_okl_s64.webp" alt="image" / width="842" height="506" loading="eager" class="align-center">
<em>WMH-新生电镀版百式</em></p>

<p>박스의 글자를 찬찬히 읽어보면 <strong>新生</strong>(신생)은 Revive를 의미하는 것 같고, <strong>电镀版</strong>(전도판)은 도금을 말하는 것 같다.<br />
아마도 반다이에서 나왔던 HGUC(Revive) 버전의 알리판이 아닌가 싶다.</p>

<p><img src="/images/2026-06-10/1000106345_5_okl_s64.webp" alt="image" / width="842" height="521" loading="eager" class="align-center">
<em>HGUC(Revive) 백식, 2016년 발매</em></p>

<p>박스를 열어보니 이 얼마만에 보는 <strong>런너</strong>냐…</p>

<p><img src="/images/2026-06-10/20260609_044918371_iOS_okl_s64.webp" alt="image" / width="704" height="629" loading="lazy" class="align-center">
<em>나대지마 심장아…</em></p>

<p>전체적인 <strong>조립설명서</strong>는 아래와 같다.<br />
얼마만에 조립설명서를 보는지 모르겠다.</p>

<p><img src="/images/2026-06-10/instruction_okl_s64.webp" alt="image" / width="796" height="557" loading="lazy" class="align-center"></p>

<p>첫번째 단계인 <strong>몸통</strong>의 설명서는 아래와 같다.</p>

<p><img src="/images/2026-06-10/step01_okl_s64.webp" alt="image" / width="842" height="384" loading="lazy" class="align-center"></p>

<p>그리고 만들어진 결과.</p>

<p><img src="/images/2026-06-10/20260609_050555633_iOS_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center"></p>

<p>두 번째는 <strong>머리</strong>다.</p>

<p><img src="/images/2026-06-10/step02.webp" alt="image" / width="571" height="543" loading="lazy" class="align-center"></p>

<p>그리고 짜잔!<br />
드디어 백식의 머리통을 완성했다.</p>

<p><img src="/images/2026-06-10/20260609_052741565_iOS_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center">
<em>안테나가 굵은 건 도금의 어쩔 수 없는 결과인 듯</em></p>

<p>다음은 <strong>오른팔</strong>이다.</p>

<p><img src="/images/2026-06-10/step03_okl_s64.webp" alt="image" / width="780" height="568" loading="lazy" class="align-center"></p>

<p>오른팔 완성.<br />
데칼은 <strong>백(百)</strong>만 붙이기로 했다.</p>

<p><img src="/images/2026-06-10/20260609_062840342_iOS_okl_s64.webp" alt="image" / width="608" height="729" loading="lazy" class="align-center"></p>

<p>다음은 <strong>왼팔</strong>.</p>

<p><img src="/images/2026-06-10/step04_okl_s64.webp" alt="image" / width="744" height="596" loading="lazy" class="align-center"></p>

<p>데칼도 역시 오른팔과 동일하게 했다.</p>

<p><img src="/images/2026-06-10/20260609_070716013_iOS_okl_s64.webp" alt="image" / width="608" height="729" loading="lazy" class="align-center"></p>

<p>다음 단계는 지금까지 만든 <strong>상체를 조립</strong>하는 것.</p>

<p><img src="/images/2026-06-10/step05.webp" alt="image" / width="334" height="350" loading="lazy" class="align-center"></p>

<p>결과는 아래와 같다.<br />
<strong>샤아</strong>가 타던 기체 답게 <strong>상체만으로 100% 완성한 기분</strong>이 든다.</p>

<p><img src="/images/2026-06-10/20260609_070816584_iOS_okl_s64.webp" alt="image" / width="631" height="703" loading="lazy" class="align-center">
<em>다리 따위는 장식입니다</em></p>

<p>다음 단계는 <del>장식품</del> <strong>오른쪽 다리</strong>.</p>

<p><img src="/images/2026-06-10/step06_okl_s64.webp" alt="image" / width="629" height="704" loading="lazy" class="align-center"></p>

<p>완성된 오른쪽 다리이다.</p>

<p><img src="/images/2026-06-10/20260609_075055716_iOS_okl_s64.webp" alt="image" / width="596" height="744" loading="lazy" class="align-center">
<em>지문이 아쉽지만 다시 찍기 귀찮음</em></p>

<p>다음 단계는 <strong>왼쪽 다리</strong>.</p>

<p><img src="/images/2026-06-10/step07_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center"></p>

<p>완성된 왼쪽 다리.</p>

<p><img src="/images/2026-06-10/20260609_084702241_iOS_okl_s64.webp" alt="image" / width="596" height="744" loading="lazy" class="align-center"></p>

<p>두 개의 장식을 나란히 세워보았다.</p>

<p><img src="/images/2026-06-10/20260609_084811605_iOS_okl_s64.webp" alt="image" / width="577" height="768" loading="lazy" class="align-center">
<em>다리 같은 건 장식입니다. 높으신 분들은 그걸 모른단 말입니다</em></p>

<p>다음은 <strong>스커트 파트</strong>.<br />
사실, 여기까지 만들어 조립한 뒤에 <strong>지옹 드립</strong>을 쳤어야 했음…</p>

<p><img src="/images/2026-06-10/step08_okl_s64.webp" alt="image" / width="474" height="842" loading="lazy" class="align-center"></p>

<p>완성된 스커트.</p>

<p><img src="/images/2026-06-10/20260609_091549836_iOS_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center"></p>

<p>다음은 <strong>본체 조립</strong>이다.<br />
여기까지 하면 사실상 큰 단계는 다 끝낸 셈이다.</p>

<p><img src="/images/2026-06-10/step09.webp" alt="image" / width="282" height="635" loading="lazy" class="align-center"></p>

<p>제품의 완성도는 꽤 준수하다.<br />
아무래도 원본으로 추정되는 HGUC(Revive)의 완성도 덕분일 듯 하다.</p>

<p><img src="/images/2026-06-10/20260609_091810423_iOS_okl_s64.webp" alt="image" / width="577" height="768" loading="lazy" class="align-center"></p>

<p><strong>무릎 꿇기</strong>도 이 정도까진 무리 없이 가능.</p>

<p><img src="/images/2026-06-10/20260609_091911278_iOS_okl_s64.webp" alt="image" / width="608" height="729" loading="lazy" class="align-center"></p>

<p><strong>플랭크</strong>는 당연히 손쉽게 가능하다.</p>

<p><img src="/images/2026-06-10/20260609_092012808_iOS_okl_s64.webp" alt="image" / width="830" height="534" loading="lazy" class="align-center"></p>

<p>거의 마지막 단계인 <strong>백팩과 윙바인더</strong>.<br />
역시 이것이 백식의 상징이다.</p>

<p><img src="/images/2026-06-10/step10_okl_s64.webp" alt="image" / width="842" height="474" loading="lazy" class="align-center"></p>

<p>조립 결과는 아래와 같다.</p>

<p><img src="/images/2026-06-10/20260609_094902128_iOS_okl_s64.webp" alt="image" / width="608" height="729" loading="lazy" class="align-center"></p>

<p>이를 장착한 백식.<br />
이제 백식이 진정으로 백식다워졌다.</p>

<p><img src="/images/2026-06-10/20260609_095137308_iOS_okl_s64.webp" alt="image" / width="577" height="768" loading="lazy" class="align-center">
<em>백식을 백식답게</em></p>

<p><strong>스쿼트</strong>도 물론 가능하다. <strong>(믿으면 골룸)</strong></p>

<p><img src="/images/2026-06-10/20260609_095514470_iOS_okl_s64.webp" alt="image" / width="577" height="768" loading="lazy" class="align-center">
<em>당연히 받침을 AI로 지운 이미지임</em></p>

<p>찐막으로 <strong>주무장</strong>인 메가 바주카 런처, 빔 라이플 그리고, 빔 사벨.</p>

<p><img src="/images/2026-06-10/step11_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center"></p>

<p>완성된 결과는 아래와 같다.<br />
가능한 <strong>HGUC(Revive)의 박스와 비슷한 자세</strong>를 잡아줬다.</p>

<p><img src="/images/2026-06-10/20260610_031007780_iOS_okl_s64.webp" alt="image" / width="780" height="568" loading="lazy" class="align-center"></p>

<hr />

<p>덧1. 왜 같은 Saber인데, 〈스타워즈〉는 라이트 <strong>세이버</strong>이고, 〈건담〉은 빔 <strong>사벨</strong>일까?<br />
덧2. 백식 디자인은 역시나 <strong>윙 바인더</strong>로 세련된 디자인이 완성됨<br />
덧3. 습식 데칼은 여전히 어려워…<br />
닷4. 도금 과정에서 두께가 좀 바뀌어서 그런지 몇 군데를 <strong>칼로 갈고 순간접착제로 고정</strong>할 필요가 있었음</p>]]></content><author><name>BLUEnLIVE</name></author><category term="etc" /><category term="백식" /><category term="Hyaku Shiki" /><category term="Hyakushiki" /><category term="알리" /><category term="HGUC" /><category term="Revive" /><summary type="html"><![CDATA[우주세기 건담의 오랜 팬으로서, 내 최애기는 백식이다. 하지만, 마지막으로 사서 조립한 백식은 20년도 넘었다. 1999년에 발매한 HGUC 백식이 마지막으로 조립해본 백식이니…]]></summary></entry><entry><title type="html">AV2 코덱 정식 릴리즈</title><link href="https://teus.me/media/AV2_released/" rel="alternate" type="text/html" title="AV2 코덱 정식 릴리즈" /><published>2026-06-09T12:55:00+09:00</published><updated>2026-06-09T12:55:00+09:00</updated><id>https://teus.me/media/AV2_released</id><content type="html" xml:base="https://teus.me/media/AV2_released/"><![CDATA[<h2 id="av2-코덱-정식-릴리즈">AV2 코덱 정식 릴리즈</h2>

<p><img src="/images/2026-06-09/av2logo_B_okl_s64.webp" alt="image" / width="842" height="444" loading="eager" class="align-center"></p>

<p><a href="https://www.flatpanelshd.com/news.php?subaction=showfull&amp;id=1780642255">FlatpanelsHD</a>에서 AV2 스펙을 공개했다는 소식을 알렸다.<br />
기사에는 <a href="https://aomedia.org/">AOMedia</a>가 오랜 개발 기간과 지연 끝에 공식적으로 <strong>AV2 v1.0.0 스펙</strong>을 확정하고 출시했다는 내용이 담겨 있다.</p>

<h3 id="경쟁작-간략-현황">경쟁작 간략 현황</h3>

<p>원래 AV1의 경쟁작은 압축률이 더 높은 VVC(H.266)이었었다.<br />
하지만, 개판이 되어버린 특허 문제로 인해 <strong>웹 스트리밍 및 오픈 생태계 시장에선 사멸</strong><sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>되었다.<br />
그리고, 덕분에 소비자 시장에서 빼앗긴 점유율을 보고 반성을 했느냐…</p>

<p><img src="/images/2026-06-09/Gemini_Generated_SameMistake_B_okl_s64.webp" alt="image" / width="596" height="744" loading="eager" class="align-center"></p>

<p>MPEG LA에서 튀어나온 Access Advance, Velos Media, Avanci Video에서 각자의 로열티를 여전히 요구하고 있다.<br />
VVC가 엉망이 되니까 <strong>원플러스원</strong> 꼼수를 부린대나 어쩐대나…</p>

<h2 id="av2-코덱의-성능">AV2 코덱의 성능</h2>

<p>각설하고, 기사에 따르면 AV2는 AV1 대비 30% 증가한 압축률을 보인다고 한다.<br />
HEVC가 H.264 대비 50% 절감, AV1이 HEVC 대비 30% 절감이 목표였으니, H.264에 비해 75% 가량 절감된 것이다.</p>

<p>물론, 이 비율은 최대한 유리한 지점을 기술하려는 목표가 있다.<br />
같은 식을 적용하면 <strong>AV1</strong>은 H.264에 비해 이미 <strong>65% 가량 절감</strong>되어 있다.</p>

<table>
  <thead>
    <tr>
      <th>코덱 분류</th>
      <th>비교 대상</th>
      <th>목표 압축 효율성</th>
      <th>H.264 대비 용량</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>H.264 (AVC)</td>
      <td>레거시 표준</td>
      <td>100% (기준점)</td>
      <td>100.0%</td>
    </tr>
    <tr>
      <td>HEVC / VP9</td>
      <td>전 세대 고성능 코덱</td>
      <td>H.264 대비 약 50% 절감</td>
      <td>50.0%</td>
    </tr>
    <tr>
      <td>AV1</td>
      <td>현재 대중화 중인 차세대 코덱</td>
      <td>HEVC/VP9 대비 약 30% 추가 절감</td>
      <td>35.0%</td>
    </tr>
    <tr>
      <td>AV2</td>
      <td>미래 타겟 코덱</td>
      <td>AV1 대비 약 30% 추가 절감 목표</td>
      <td>24.5%</td>
    </tr>
  </tbody>
</table>

<p>VVC의 목표 압축률이 H.264 대비 65%~75% 였으니 AV2에 와서야 성능 면에서 비슷한 수준을 달성한 것이다.</p>

<p>기술의 발전은 좀 더딘 것 같지만, <strong>특허를 회피</strong>하면서 표준을 만들었기 때문에 무리 없이 확산될 것이다.<br />
물론, AV2 개발을 주도한 기업<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>이 칩셋 제조 등을 꽉 잡고 있는 면도 있고…</p>

<h2 id="av2-코덱의-적용은">AV2 코덱의 적용은?</h2>

<p>한편으로는, AV1도 아직 제대로 정착하지 못한 시점에서 AV2가 좀 부담스럽다는 생각도 든다.<br />
AV1 디코딩도 버거운 PC가 아직 많이 사용되고 있기도 하다.<br />
인코딩의 경우 전용 인코더가 없는 PC에서는 그나마 <a href="https://gitlab.com/AOMediaCodec/SVT-AV1">SVT-AV1 인코더</a>를 사용해야 상식적인 시간 내에 인코딩을 할 수 있다.<br />
최신 그래픽 카드를 사용해야 겨우 인코딩이 되는 AV1인데, 벌써 AV2라니요…<br />
AV2 디코딩은 AV1보다 <strong>약 5배가량 무겁다</strong>고 하니, 어셈블리 깎는 장인들의 <strong>SIMD 노다가</strong>가 벌써부터 눈물겹다… ㅠㅠ</p>

<p>FFmpeg에 적용하는 점도 중요하다.<br />
일단 <a href="https://github.com/AOMediaCodec/avm/tree/v1.0.0">깃허브에 avm-av2 1.0.0이 등록</a>되었으니 라이브러리 연동이 진행될 것이다.<br />
전용 디코더 쪽도 VideoLAN 진영이 공식 고성능 디코더 프로젝트인 <a href="https://code.videolan.org/videolan/dav2d">dav2d 저장소</a>를 공개하고 초기 소스코드를 배포했다.</p>

<h2 id="벌써-av2라니-하지만-결국-갈-수밖에-없는-길">벌써 AV2라니? 하지만 결국 갈 수밖에 없는 길</h2>

<p>유튜브에서 4K 영상을 보려고 RTX 40/50 GPU의 하드웨어 가속을 누린 게 그리 오래되지 않은 것 같다.<br />
그런데, 벌써 다음 세대 규격이 눈앞에 배달되었다.<br />
디코딩 연산량이 AV1보다도 5배나 무겁다는 소식에, 칩셋 제조사들과 오픈소스 개발자들의 비명이 벌써부터 들리는 듯하다.</p>

<p>하지만 그럼에도 불구하고 우리는 결국 이 무거운 코덱을 받아들이게 될 것이다.</p>

<p>당장 내일부터 내 PC에서 AV2 영상을 돌리진 못하겠지만, FFmpeg과 VideoLAN의 톱니바퀴는 돌기 시작했다.<br />
앞으로 오픈소스 진영이 이 무지막지한 연산량의 괴물을 어떻게 최적화해 나갈지가 흥미로운 관전 포인트가 될 것이다.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>사멸해버린 소비자 시장과 별개로 티비/가전 환경에서는 VVC가 쓰이고 있음 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>무려 구글, 애플, 삼성, 넷플릭스, 엔비디아 등 <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>BLUEnLIVE</name></author><category term="media" /><category term="ffmpeg" /><category term="VideoLAN" /><category term="AV1" /><category term="AV2" /><summary type="html"><![CDATA[AV2 코덱 정식 릴리즈]]></summary></entry><entry><title type="html">나도 트렁크에 식빵등을 설치해 보았다</title><link href="https://teus.me/etc/Flexible_LED_Strip_Light/" rel="alternate" type="text/html" title="나도 트렁크에 식빵등을 설치해 보았다" /><published>2026-06-08T20:37:00+09:00</published><updated>2026-06-08T20:37:00+09:00</updated><id>https://teus.me/etc/Flexible_LED_Strip_Light</id><content type="html" xml:base="https://teus.me/etc/Flexible_LED_Strip_Light/"><![CDATA[<p>차 트렁크의 등을 LED로 교체했다.<br />
하지만, 등의 크기 자체가 작다보니 큰 효과가 없어보였다.<br />
테일게이트 LED를 달 수 있는 환경도 아니라 뭐 뾰족한 수가 없나 했는데…</p>

<p><strong>식빵등</strong>이라 흔히 불리는 <strong>플렉시블 LED 스트립 라이트(Flexible LED Strip Light)</strong>가 있었다.</p>

<p><img src="/images/2026-06-08b/20260529_063544337_iOS_okl_s64.webp" alt="image" / width="768" height="577" loading="eager" class="align-center">
<em>빨간선이 플러스(+)</em></p>

<p>이론상 이 등의 설치 자체는 그렇게 어려울 게 없다.<br />
트렁크 LED에서 선 두 가닥만 잘 따서 연결하면 된다.</p>

<p><img src="/images/2026-06-08b/20260529_062818341_iOS_okl_s64.webp" alt="image" / width="796" height="557" loading="eager" class="align-center">
<em>여기서 선만 딸깍 연결하면 될 것 같지?</em></p>

<p>하지만 언제나 말만 쉬운 법이고, 실제로 해보면 다 어렵다.</p>

<p>우선 차량 전선의 길이가 너무 타이트하다.<br />
선을 쉽게 연결할 수 있는 <strong>‘무피복 투명 와이어 커넥터(스카치락)’</strong>을 주지만 <strong>공간이 좀 부족</strong>하다.</p>

<p>또 하나의 문제는, 스카치락을 사용하려면 <strong>퓨즈를 뽑고 작업</strong>해야 한다는 점.<br />
활선 위에서 바로 전기 작업을 할 수는 없고, 막상 뽑으려니 귀찮다…</p>

<p><img src="/images/2026-06-08b/20260529_051531845_iOS_okl_s64.webp" alt="image" / width="729" height="608" loading="lazy" class="align-center">
<em>이 좋은 스카치락은 잘 keeping 해두는 걸로…</em></p>

<p>그래서 <a href="https://www.youtube.com/watch?v=OwxYU8l9AjI">유튜브</a>를 참고하여 좀 더 손쉽게 <strong>램프 어셈블리</strong>에 직접 <strong>납땜</strong>을 하기로 했다.<br />
이렇게 하면 <strong>선 길이에 대한 고민</strong>도 필요 없고, <strong>활선 위에서 작업</strong>할 필요도 없다.<br />
유의해야 할 점은 LED 등은 극성이 있으니 <strong>양극, 음극을 혼동하면 안 된다</strong>는 점이다.</p>

<p><img src="/images/2026-06-08b/20260529_053916739_iOS_okl_s64.webp" alt="image" / width="474" height="842" loading="lazy" class="align-center">
<em>이거시 램프 어셈블리 라는 거시다…</em></p>

<p>이런 작업을 할 때 절연 테이프를 많이 사용한다.<br />
그런데, 전기 현장에서는 아래와 같은 보수 테이프(현장에선 흔히 <strong>떡테이프</strong>라고도 함)도 많이 사용한다.<br />
별도의 접착부는 없고, 그냥 늘려서 감으면 알아서 굳어버리고 절연, 방수 다 잘 된다.</p>

<p><img src="/images/2026-06-08b/20260529_054137782_iOS_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center"></p>

<p>이렇게 작업한 결과는 아래와 같다.</p>

<p><img src="/images/2026-06-08b/20260529_060303037_iOS_okl_s64.webp" alt="image" / width="444" height="842" loading="lazy" class="align-center">
<em>깔끔하지 않은 건 어쩔 수 없…</em></p>

<p>흔히 요비선이라는 은어로 불리는 <strong>배선 가이드(Wiring guide)</strong>를 통해 전선을 무사히(?) 외부로 빼냈다.</p>

<p><img src="/images/2026-06-08b/20260529_062855077_iOS_okl_s64.webp" alt="image" / width="768" height="577" loading="lazy" class="align-center">
<em>전선을 빼내기 직전</em></p>

<p>필요한 부분을 조립한 뒤 배선 이상 여부를 연결해서 확인했다.<br />
배선은 차 안에서 <strong>소음</strong>을 유발할 수 있으므로 <strong>진동 방지 테이프</strong>로 잘 감아준다.</p>

<p><img src="/images/2026-06-08b/20260529_065415621_iOS_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center">
<em>불이 들어오는 걸 확인하면 전기적인 부분은 끝난 것임</em></p>

<p>여기까지 함으로써 어려운 작업은 모두 끝냈다.<br />
이제 밖에서 봤을 때 험하지 않도록 조금씩 잘 매립하면 된다.</p>

<p><img src="/images/2026-06-08b/20260529_073206388_iOS_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center">
<em>매립 작업의 시작 위치</em></p>

<p>최종적으로 작업이 끝난 모습은 아래와 같다.</p>

<p><img src="/images/2026-06-08b/20260529_075449740_iOS_okl_s64.webp" alt="image" / width="666" height="666" loading="lazy" class="align-center"></p>

<hr />

<p>덧. 사진에선 빠졌는데, 실외에서 납땜 할 일이 하나 있었다.<br />
\(220\text{V}\) 전기를 끌어오기 힘들어 그냥 라이터로 납땜을 강행했다.<br />
통상적인 납땜 온도(\(350^\circ\text{C}\))보다 훨씬 뜨거운 라이터 불꽃(\(1,000^\circ\text{C}\) 내외)을 이용해 납땜을 해치웠다.</p>]]></content><author><name>BLUEnLIVE</name></author><category term="etc" /><category term="Trax" /><category term="트랙스" /><category term="식빵등" /><category term="Flexible LED Strip Light" /><category term="요비선" /><category term="LED" /><category term="배선 가이드" /><summary type="html"><![CDATA[차 트렁크의 등을 LED로 교체했다. 하지만, 등의 크기 자체가 작다보니 큰 효과가 없어보였다. 테일게이트 LED를 달 수 있는 환경도 아니라 뭐 뾰족한 수가 없나 했는데…]]></summary></entry><entry><title type="html">매일 보던 ‘Lorem Ipsum’ 뒤에 숨겨진 100년의 미스터리</title><link href="https://teus.me/ittalk/Lorem_Ipsum_100y_mistery/" rel="alternate" type="text/html" title="매일 보던 ‘Lorem Ipsum’ 뒤에 숨겨진 100년의 미스터리" /><published>2026-06-08T14:14:00+09:00</published><updated>2026-06-08T14:14:00+09:00</updated><id>https://teus.me/ittalk/Lorem_Ipsum_100y_mistery</id><content type="html" xml:base="https://teus.me/ittalk/Lorem_Ipsum_100y_mistery/"><![CDATA[<p>웹디자인이나 개발을 하는 사람, 혹은 문서 템플릿을 만져본 사람들이라면 누구나 한 번쯤은 마주쳤을 단어들이 있다.<br />
바로 의미 없는 라틴어처럼 보이는 채우기 텍스트, <strong>‘Lorem Ipsum(로렘 입숨)’</strong>이다.</p>

<p><img src="/images/2026-06-08a/loremipsum_B_okl_s64.webp" alt="image" / width="744" height="596" loading="eager" class="align-center">
<em>1500년대에 만들어졌다고들 많이 믿는 그 구라의 상징</em></p>

<p>구글링 해보면 <strong>“1500년대의 한 익명 인쇄업자가 활자를 무작위로 섞어 만들었다”</strong>라는 설명이 정설처럼 퍼져 있었다.<br />
최근 이 상식이 완전히 잘못되었음을 집요하게 추적하고 밝혀낸 놀라운 다큐멘터리 영상을 보게 되었다.<br />
그 감동과 함께 진짜 기원이 된 키케로의 원문을 공유해 보려고 한다.</p>

<h2 id="1500년대가-아니라-1914년-그리고-1966년의-진실">1500년대가 아니라 1914년, 그리고 1966년의 진실</h2>

<p>영상의 제작자는 기존의 인터넷 정보들이 모두 잘못되었다는 의문에서 출발했다.<br />
실제 라틴어 학자들과 인쇄 역사학자의 발자취를 추적하는 일종의 ‘연구 조사’를 진행했다.<br />
그 집요한 노력 덕분에 밝혀진 핵심 사실들은 정말 흥미롭다.</p>

<ul class="bluebox-pink">
  <li><strong>기존 상식의 오류:</strong> 납 활자를 쓰던 1500년대에는 디자인만을 위한 가짜 텍스트를 미리 조판하는 게 불가능했다. 즉, 1500년대 기원설은 완전히 잘못된 역사였다.</li>
  <li><strong>진짜 기원은 1914년:</strong> 로렘 입숨의 독특한 오타와 문장 배열은 1914년에 발행된 키케로 저서의 특정 번역본 판본(Loeb Classical Library) 페이지 구성과 정확히 일치했다.</li>
  <li><strong>디지털로 이어진 가교:</strong> 1966년, 영국의 레트라셋(Letraset) 사가 타이포그래피 역사학자의 자문을 받아 이 구절을 정교하게 다듬어 디자인 시트로 보급했고, 이것이 80년대 컴퓨터 프로그램(PageMaker)에 탑재되어 지금의 디지털 로렘 입숨이 되었다.</li>
</ul>

<h2 id="의미를-지우기-위한-정교한-언어학적-설계">의미를 지우기 위한 정교한 언어학적 설계</h2>

<p>영상의 내용 중 굉장히 흥미로운 부분은 이 가짜 텍스트가 대충 뭉뚱그려 만든 낙서가 아니라, <strong>라틴어와 영어를 모두 깊게 이해한 인물이 철저한 의도를 가지고 정교하게 가공한 결과물</strong>이라는 점이다.</p>

<ol>
  <li><strong>‘quia(때문에)’ 단어의 제거</strong><br />
키케로의 원문 문장에서 문맥의 의미를 이어주는 핵심 접속사가 바로 ‘왜냐하면/때문에’를 뜻하는 라틴어 ‘quia’였다.<br />
이 단어가 살아있으면 라틴어를 아는 디자이너가 자꾸 문장의 ‘뜻’을 읽게 되므로, 시선을 빼앗기지 않도록 문장의 의미를 완벽히 단절시키기 위해 이 ‘quia’만 정교하게 삭제했다.</li>
  <li><strong>영문 분위기를 내기 위한 알파벳 변형</strong><br />
본래 라틴어에는 J, K, Y, Z 같은 글자가 거의 쓰이지 않고 Q, U, M이 자주 쓰인다.<br />
반면 영어는 Y나 복합 자음이 많이 쓰인다.<br />
로렘 입숨을 만든 사서 제임스 모슬리는 영미권 디자이너들이 보기에 가장 시각적 균형이 잡힌 텍스트 화면을 구성하기 위해, 라틴어 원문의 Q나 M을 일부 깎아내고 <strong>‘Y’나 ‘-ng’, ‘-ands’ 같은 영어식 철자를 의도적으로 끼워 넣었다.</strong></li>
</ol>

<p>단순한 오타가 아니라, 완벽하게 <strong>‘의미가 없으면서도 영문 서체 레이아웃을 가장 잘 테스트할 수 있는 형태’</strong>로 기획된 고도의 디자인 산출물이었던 것이다.</p>

<h2 id="로렘-입숨의-고향-키케로의-원문과-번역">로렘 입숨의 고향: 키케로의 원문과 번역</h2>

<p>그렇다면 우리가 매일 복사해서 쓰던 그 문장들은 본래 어떤 내용이었을까?<br />
기원전 45년, 키케로가 남긴 《최고 선악론(De Finibus Bonorum et Malorum)》 제1권 10장 32절의 원문과 그 의미를 소개한다.</p>

<blockquote class="bluebox-blue">
  <p><strong>[라틴어 원문]</strong><br />
“Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. <strong>Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit</strong>, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?”</p>
</blockquote>

<blockquote class="bluebox-green">
  <p><strong>[한국어 번역]</strong> <em>(1914년 H. 래컴의 영어 번역본 기준)</em><br />
“하지만 나는 쾌락을 비난하고 고통을 찬양하는 이 모든 잘못된 생각이 어떻게 생겨났는지 설명해야만 한다. 나는 그 체계의 완전한 전말을 밝히고, 진리의 위대한 개척자이자 인간 행복의 설계자가 남긴 실제 가르침을 설명하고자 한다.<br />
쾌락 그 자체가 쾌락이라는 이유만으로 이를 거부하거나 싫어하고 회피하는 사람은 아무도 없다. 다만 쾌락을 이성적으로 추구하는 방법을 모르는 이들이 결국 극심한 고통이라는 결과를 마주하게 되기 때문이다.<br />
<strong>마찬가지로, 고통 그 자체가 고통이라는 이유만으로 이를 사랑하거나 추구하고 얻기를 바라는 사람 또한 존재하지 않는다</strong> <em>(Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet…)</em>, 다만 노역과 고통을 통해 어떤 커다란 쾌락을 얻을 수 있는 상황이 때때로 발생하기 때문이다.<br />
사소한 예를 들자면, 우리 중 그 어떤 이익도 얻지 못하는데 고된 육체적 운동을 시작하는 사람이 어디 있겠나? 반대로, 아무런 불쾌한 결과도 뒤따르지 않는 쾌락을 즐기기로 선택한 사람이나, 결과적으로 아무런 쾌락도 낳지 못하는 고통을 회피하는 사람을 비난할 권리가 과연 누구에게 있겠나?”</p>
</blockquote>

<p>위 원문에서 굵게 표시한 “do<strong>lorem ipsum</strong>” 부분이 인쇄 조판과 편집의 역사를 거치며 변형되어 오늘날 우리가 아는 ‘Lorem Ipsum’이 되었다. <br />
인간의 본질적인 행복과 쾌락, 고통을 논하던 깊이 있는 철학적 문장이 현대 디자인의 뼈대를 지탱하는 텍스트가 된 것이다.</p>

<h2 id="지독한-몰입과-연구가-선물한-지식에-감사하며">지독한 몰입과 연구가 선물한 지식에 감사하며</h2>

<p>단순히 “흔한 상식”으로 치부하고 넘어갈 수 있는 영역이었다.<br />
하지만 영상의 원작자는 단 하나의 논문도, 전문가도 없던 이 영역을 밝히기 위해 <strong>수십 통의 이메일</strong>을 보내고, <strong>해외 도서관</strong>에서 <strong>수십 년 전의 광고지와 카탈로그를 복사</strong>해 오며 가설을 증명해 나갔다.</p>

<p>아무도 알아주지 않는 곳에서 묵묵히 역사를 기록했던 <strong>수많은 학자들의 헌신</strong>, 그리고 이를 세상 밖으로 끌어올려 준 <strong>영상 제작자의 지독한 몰입</strong> 덕분에 우리가 매일 쉽게 볼 수 있는 텍스트의 진짜 고향을 알게 되었다.</p>

<p>타인의 깊이 있는 탐구와 노력이 없었다면 우리는 여전히 잘못된 사실을 정설로 믿고 있었을 것이다.<br />
귀중한 지식을 정돈해 세상에 공유해 준 <strong>제작자에게 깊은 감사</strong>를 전한다.</p>

<h2 id="직접-그-과정을-확인해-보기">직접 그 과정을 확인해 보기</h2>

<p>본 글에 요약하고 옮긴 내용은 이 영상이 가진 매력과 수많은 세부 증거들의 <strong>극히 일부</strong>에 불과하다.<br />
2,000년 전 키케로의 문장이 어떻게 현대 디자이너들의 화면에 안착했는지, 그 추적 과정의 전말이 궁금하다면 아래 원작자의 영상을 꼭 직접 시청해 보기를 강력히 추천한다.</p>

<div class="video-outer-wrapper"><div class="responsive-video-container"><iframe src="https://www.youtube-nocookie.com/embed/kL1PDqzqhM4" title="" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe></div><em class="video-caption"></em><style>.video-caption { display: block !important; text-align: center; font-size: .8rem; color: gray; font-style: normal; margin-top: -0.6rem !important; line-height: 1.5; }</style></div>

<div class="quoteMachine">
  <div class="theQuoteLeft">
    <blockquote><span class="quotationMark quotationMark--left"></span>
      
        
          과연 나는 매일 마주치는 일상 속 익숙한 것들의 진짜 기원에 대해 의문을 품어본 적이 있는가?

      
      <span class="quotationMark quotationMark--right"></span></blockquote>
  </div>

  <div class="quoteAuthor">
    
      &nbsp;
    
  </div>
</div>]]></content><author><name>BLUEnLIVE</name></author><category term="ITTalk" /><category term="Lorem Ipsum" /><category term="100년의 미스터리" /><summary type="html"><![CDATA[웹디자인이나 개발을 하는 사람, 혹은 문서 템플릿을 만져본 사람들이라면 누구나 한 번쯤은 마주쳤을 단어들이 있다. 바로 의미 없는 라틴어처럼 보이는 채우기 텍스트, ‘Lorem Ipsum(로렘 입숨)’이다.]]></summary></entry><entry><title type="html">구라제거기(키보드 보안 프로그램 삭제) 7.58 업데이트</title><link href="https://teus.me/hoaxeliminator/HoaxEliminator7.55/" rel="alternate" type="text/html" title="구라제거기(키보드 보안 프로그램 삭제) 7.58 업데이트" /><published>2026-06-08T12:58:00+09:00</published><updated>2026-06-08T12:58:00+09:00</updated><id>https://teus.me/hoaxeliminator/HoaxEliminator7.55</id><content type="html" xml:base="https://teus.me/hoaxeliminator/HoaxEliminator7.55/"><![CDATA[<p>PC 뱅킹의 주적은 <strong>PC 뱅킹 프로그램</strong>이다.</p>

<p>PC 뱅킹을 하고 나면 컴퓨터가 미친 듯이 느려지기 때문이다.<br />
<strong>키보드 보안 프로그램</strong>을 필두로 컴퓨터를 느려지게 만드는 악의 무리들이 너무나 많다.<br />
전통의 명가(?) <strong>nProte∗∗</strong> 부터 컴퓨터 발목잡기의 거목 <strong>안∗ 온라인 시큐∗∗</strong>, 그 외에도 수많은 잡 구라들…</p>

<p><a href="http://www.etnews.com/20161130000139">KISA에서 I사 보안모듈 프로그램에 문제가 있다고 발표</a>할 정도로 <strong>완성도가 엉망</strong>인 경우도 있다.<br />
<strong>보안 취약점을 갖고있는 보안 프로그램</strong>이라니… 무슨 <a href="https://www.google.com/search?q=열림교회+닫힘">열림교회 닫힘</a>도 아니고…</p>

<p>더군다나 이런 프로그램들은 몰래 설치가 되는 것도 아니고 아예 <strong>(강제로) 동의를 받아</strong> 설치된다.<br />
마치 건물 철거 강제 집행하면서 동의서 서명당하는 기분이다.</p>

<p>게다가 <a href="https://github.com/alanleedev/KoreaSecurityApps/blob/main/03_weakening_tls_protection.md">보안 전문가인 블라디미르 팔란트 씨의 글</a>에 따르면 이 과정에서 설치된 루트 인증서를 제대로 삭제하지도 않는다.</p>

<p>그래서 간단히 만들었다.<br />
설치 프로그램 목록에서 이러한 <strong>백해무익한 쓰레기들을 찾아서 한방에 제거</strong>해주는 프로그램.</p>

<p><img src="/images/2026-06-08/hoaxelem_B_okl_s64_Q.webp" alt="image" / width="791" height="610" loading="eager" class="align-center"></p>

<p>이 프로그램을 실행하면 위와 같은 화면이 나온다.</p>

<p>PC에 설치된 프로그램들 중에 제거해야 될 프로그램들의 목록을 띄워준 것이다.<br />
여기서 <strong>일부를 선택</strong>해서 제거를 클릭해도 되고, 그냥 <strong>모두 제거</strong>를 클릭해도 된다.</p>

<p>클릭하면 지정된 프로그램들을 하나씩 제거할 수 있는 배치 파일을 만들고 실행해서 몽땅 제거해준다.</p>

<p>화면에 다이얼로그가 뜨면 하나씩 확인 버튼만 클릭하면 된다.</p>

<p>이 프로그램은 아래 링크에서 다운받을 수 있다.<br />
x86, x64 및 ARM64 버전이 함께 들어있는데, x64 윈도우 환경이라면 x64 버전을 추천한다.</p>

<div class="download-box ">
  
    <a href="/attachment/2026-06-08/HoaxEliminator7.58.zip" class="download-btn">
      Download 
      
        HoaxEliminator7.58.zip
      
    </a>
  
  
  <br />
  
  
  
  password: <span class="password">teus.me</span>
</div>

<h2 id="히스토리">히스토리</h2>

<ul class="bluebox-history">
  <li>2026.2.6: v7.55
    <ul>
      <li>내부 압축 라이브러리에서 LZ4 완전 제거</li>
      <li>정보 표시 색상 튜닝</li>
      <li>코드 튜닝 및 라이브러리 업데이트
        <ul>
          <li><code class="language-plaintext highlighter-rouge">qsort()</code>를 <code class="language-plaintext highlighter-rouge">std::sort()</code>로 교체</li>
          <li>Zstd 라이브러리를 1.6.0.git 버전(2026.1.27)으로 업데이트</li>
          <li>Google/RE2 라이브러리 2026.1.23 (re2: remove unnecessary &amp; in MutexLock usage) 반영</li>
          <li>Google/Abseil 라이브러리를 20260107.0 버전(Abseil LTS branch, January 2026)으로 업데이트 및<br />
2026.2.6 (Correctly define ABSL_UNREACHABLE when Abseil Hardened Fast is enabled) 반영</li>
        </ul>
      </li>
      <li>Ma∗∗An∗ 탐색 범위를 확대하고, 제거시 제작사 uninstaller를 추가로 실행하도록 보강</li>
    </ul>
  </li>
  <li>2026.4.15: v7.56
    <ul>
      <li><strong>오픈소스 라이선스</strong> 표시 기능 추가</li>
      <li>정규식 목록 <strong>구조 최적화 및 통합</strong></li>
      <li>Google/Abseil 라이브러리를 20260107.1 버전(Abseil LTS branch, January 2026, Patch 1)으로 업데이트</li>
    </ul>
  </li>
  <li>2026.6.8: v7.58
    <ul>
      <li>Google/Abseil 라이브러리를 20260526.0 버전(Abseil LTS branch, May 2026)으로 업데이트</li>
      <li>Zstd 라이브러리를 1.6.0.git 버전(2026.5.15)으로 업데이트</li>
      <li>기타 다양한 내부 코드 구조 개선</li>
    </ul>
  </li>
</ul>]]></content><author><name>BLUEnLIVE</name></author><category term="HoaxEliminator" /><category term="activex" /><category term="nProtect" /><category term="구라제거기" /><category term="뱅킹" /><category term="보안" /><category term="안랩" /><summary type="html"><![CDATA[PC 뱅킹의 주적은 PC 뱅킹 프로그램이다.]]></summary></entry><entry><title type="html">Notepad++에서 Notepad4 기능 지원을 위한 플러그인 v1.01 업뎃</title><link href="https://teus.me/notepad4/NPP_plugin_Notepad4_1.01/" rel="alternate" type="text/html" title="Notepad++에서 Notepad4 기능 지원을 위한 플러그인 v1.01 업뎃" /><published>2026-05-25T22:29:00+09:00</published><updated>2026-05-25T22:29:00+09:00</updated><id>https://teus.me/notepad4/NPP_plugin_Notepad4_1.01</id><content type="html" xml:base="https://teus.me/notepad4/NPP_plugin_Notepad4_1.01/"><![CDATA[<p><a href="https://notepad-plus-plus.org/">Notepad++</a>는 <a href="https://github.com/zufuliu/notepad4">Notepad4</a>에 비해 <strong>다중 탭</strong>과 <strong>플러그인 기능</strong>으로 무장한 <strong>만능 도구</strong>이다.</p>

<p><strong>Notepad++</strong>는 기능도 많고 막강하지만, 몇 가지 기능이 누락되어 있다.<br />
그리고, 이런 부분을 <strong>플러그인</strong>으로 가볍게 보강할 수 있는 구조를 갖고 있다.</p>

<p><strong>Notepad4</strong>에 적용했던 기능들을 Notepad++ 용으로 구현하는 플러그인을 개발하기로 했다.<br />
목표는 그동안 적용했던 기능들을 정리해서 하나의 플러그인에 모두 구현하는 것.</p>

<p>그동안 Notepad4에 추가로 구현한 기능 및 Notepad4에서 가져오고 싶은 기능들은 다음과 같았다.</p>

<ul class="bluebox-blue">
  <li><strong>텍스트 좌우 정렬</strong> (유니코드 표를 참조해서 통상적인 고정폭 글꼴의 폭을 기준으로 함)</li>
  <li><strong>수식 계산</strong></li>
  <li><strong>한국어 한자를 한글로 변환</strong></li>
  <li><strong>한글을 풀어쓰기로 변환</strong></li>
  <li><strong>유니코드 한글 풀어쓰기↔모아쓰기</strong></li>
  <li><strong>조합형 한글 변환</strong></li>
  <li><strong>JS 표현식 평가</strong></li>
  <li><strong>HTML/XML 태그 삭제</strong></li>
  <li><strong>HTML 등에서 주석 삭제</strong></li>
  <li><strong>저장시 암호 설정</strong> (읽을 때 암호화된 파일인지 확인)</li>
  <li><strong>젠코딩</strong></li>
  <li><strong>find/replace에 즐겨 쓰는 Boost 정규식 추가</strong></li>
  <li><strong>lexer 추가</strong>꞉ Srt, SRecord, IntelHex, TekExHex, TLE</li>
  <li><strong>마크다운 lexer 수정</strong>꞉ 시간 데이터가 있을 때 이모지가 아닌 것으로 인식</li>
  <li><strong>C/C++ lexer 수정</strong>꞉ 윈도우 환경 예약어 대폭 추가</li>
  <li><strong>한글 IME에서 커서 폭을 다르게 지정</strong></li>
  <li><strong>Alt+마우스 휠 동작 방식 변경</strong></li>
  <li><strong>현재 날짜/시간 추가시 한국어 환경이면 날짜를 먼저 추가</strong></li>
  <li><strong>파일이 ".LOG"로 시작하면 로그 파일로 인식</strong></li>
</ul>

<p>이 기능들은 일부는 <strong>별도의 메뉴</strong>를 구성해야 하고, 일부는 <strong>메뉴 없이 기본 기능처럼</strong> 동작해야 한다.<br />
또한, 일부 기능은 이미 <strong>누군가가 훌륭한 솔루션을 개발</strong>해놓은 것도 있다.</p>

<p>이 기능들 중에 일단 메뉴를 통해 접근할 수 있는 기능들을 구현해 공개한다.</p>

<p><img src="/images/2026-05-25/NPP_B_okl_s64_Q.webp" alt="image" / width="842" height="250" loading="eager" class="align-center">
<em>1.00과 비교해서 메뉴 구성이 다소 수정되었음</em></p>

<p>위에 정리된 기능 중에 구현된 기능 등은 아래 표에서 확인할 수 있다.</p>

<table>
  <thead>
    <tr>
      <th>순번</th>
      <th>기능</th>
      <th>구현 여부</th>
      <th>비고</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td><strong>텍스트 좌우 정렬</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>2</td>
      <td><strong>수식 계산</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>3</td>
      <td><strong>한국어 한자를 한글로 변환</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>4</td>
      <td><strong>한글을 풀어쓰기로 변환</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>5</td>
      <td><strong>유니코드 한글 풀어쓰기↔모아쓰기</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>6</td>
      <td><strong>조합형 한글 변환</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>7</td>
      <td><strong>JS 표현식 평가</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>8</td>
      <td><strong>HTML/XML 태그 삭제</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>9</td>
      <td><strong>HTML 등에서 주석 삭제</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>10</td>
      <td><del>저장시 암호 설정</del></td>
      <td>이미 구현돼있음</td>
      <td><a href="https://github.com/JetNpp/NppCrypt">NppCrypt</a></td>
    </tr>
    <tr>
      <td>11</td>
      <td><del>젠코딩</del></td>
      <td>이미 구현돼있음</td>
      <td><a href="https://github.com/emmetio/npp/issues/19#issuecomment-383800041">npp</a> 또는 jN Notepad++ Plugin</td>
    </tr>
    <tr>
      <td>12</td>
      <td><strong>find/replace 에 즐겨 쓰는 Boost 정규식 프리셋 검색 기능 추가</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>13</td>
      <td>lexer 추가</td>
      <td>미구현 (Srt, TLE)</td>
      <td>이미 구현돼있음 (SRecord, IntelHex, TekExHex)</td>
    </tr>
    <tr>
      <td>14</td>
      <td>마크다운 lexer 수정</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>15</td>
      <td>C/C++ lexer 수정</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>16</td>
      <td>한글 IME에서 커서 폭을 다르게 지정</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>17</td>
      <td>Alt+마우스 휠 동작 방식 변경</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>18</td>
      <td>현재 날짜/시간 추가 기능 수정</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>19</td>
      <td>로그 파일 인식</td>
      <td>미구현</td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>지원되는 환경은 다음과 같으며, 이는 <strong>Notepad++와 동일</strong>하다.</p>

<ul class="bluebox-green">
  <li>CPU: x64, x86, ARM64</li>
  <li>OS꞉ Windows 8.1, 10, 11</li>
</ul>

<p>이 플러그인의 소스는 <a href="https://github.com/bluenlive/NPP_Notepad4">깃허브</a>에서 볼 수 있으며, <a href="https://github.com/bluenlive/NPP_Notepad4/releases">여기</a>에서 다운받을 수 있다.</p>

<h2 id="히스토리">히스토리</h2>

<ul class="bluebox-history">
  <li>2026.5.25꞉ 1.01 공개
    <ul>
      <li>정규식 프리셋 추가: 즐겨 쓰는 정규식 패턴들을 적용해서 검색할 수 있는 정규식 프리셋 검색 기능 추가
        <ul>
          <li>정규식에 주석 추가로 가독성 증대</li>
        </ul>
      </li>
      <li>UI 메뉴 구조 최적화: 루트 메뉴를 차지하던 플러그인 위치를 편집(Edit) 등 하위 메뉴로 이동</li>
    </ul>
  </li>
</ul>]]></content><author><name>BLUEnLIVE</name></author><category term="Notepad4" /><summary type="html"><![CDATA[Notepad++는 Notepad4에 비해 다중 탭과 플러그인 기능으로 무장한 만능 도구이다.]]></summary></entry><entry><title type="html">VS2026 v18.6.0 루프 최적화 버그 추적기</title><link href="https://teus.me/ittalk/VS2026_18.6_loop_opt_bug/" rel="alternate" type="text/html" title="VS2026 v18.6.0 루프 최적화 버그 추적기" /><published>2026-05-21T11:35:00+09:00</published><updated>2026-05-21T11:35:00+09:00</updated><id>https://teus.me/ittalk/VS2026_18.6_loop_opt_bug</id><content type="html" xml:base="https://teus.me/ittalk/VS2026_18.6_loop_opt_bug/"><![CDATA[<h2 id="디버그에선-잘되는데-릴리즈에선-왜-죽을까-컴파일러의-루프-최적화-버그-추적기">디버그에선 잘되는데 릴리즈에선 왜 죽을까? 컴파일러의 루프 최적화 버그 추적기</h2>

<p>최근 <code class="language-plaintext highlighter-rouge">libde265</code> 라이브러리를 <strong>1.0.18</strong>에서 <strong>1.0.19</strong>로 업데이트한 후 기묘한 현상을 겪었다.<br />
디버그 모드에서는 잘 작동하던 프로그램이, 릴리즈 모드로 빌드만 하면 <strong>시작하자마자 프로세스가 강제 종료</strong>되는 것.</p>

<p>더 황당한 것은 1.0.19의 헤더와 lib로 빌드한 상태에서 <strong>1.0.18의 DLL을 슬쩍 끼워넣으면</strong> 또 <strong>정상 동작</strong>한다는 점이었다.<br />
링크 오류도 아니고, 빌드 옵션 문제도 아닌 이 기괴한 현상의 원인을 디스어셈블리 레벨까지 내려가 추적해 보았다.</p>

<p>결론을 <strong>스포일링</strong> 하자면, <strong>1.0.18</strong>은 <strong>VS2026 v18.5.3</strong>으로 빌드한 것이고, <strong>v18.6.0</strong>으로 빌드했을 때 문제가 터졌다.<br />
즉, 라이브러리는 잘못이 없고 <strong>컴파일러가 범인</strong>이다.</p>

<h3 id="y--188-이라니-뭐가-문제지">y = 188 이라니, 뭐가 문제지?</h3>

<p><code class="language-plaintext highlighter-rouge">RelWithDebInfo</code> 구성을 통해 릴리즈 모드를 유지하면서 디버그 기호(PDB)를 남겨서 예외 지점을 포착했다.<br />
디버거가 가리킨 곳은 영상 디코딩 스캔 순서를 초기화하는 <code class="language-plaintext highlighter-rouge">fill_scan_pos</code> 내부의 <code class="language-plaintext highlighter-rouge">do-while</code> 루프.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// fill_scan_pos 내부 크래시 지점</span>
<span class="n">position</span> <span class="n">S</span> <span class="o">=</span> <span class="n">ScanOrderSub</span><span class="p">[</span><span class="n">lastSubBlock</span><span class="p">];</span>
<span class="n">xC</span> <span class="o">=</span> <span class="p">(</span><span class="n">S</span><span class="p">.</span><span class="n">x</span><span class="o">&lt;&lt;</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">ScanOrderPos</span><span class="p">[</span><span class="n">lastScanPos</span><span class="p">].</span><span class="n">x</span><span class="p">;</span> <span class="c1">// &lt;-- 여기서 Access Violation (0xC0000005) 발생</span>
</code></pre></div></div>

<p>이때 디버거에 찍힌 인자 값이 좀 이상했다.</p>

<ul class="bluebox-pink">
  <li><code class="language-plaintext highlighter-rouge">log2TrafoSize = 2</code> (4x4 블록이므로 x, y는 0~3 사이여야 함)</li>
  <li><code class="language-plaintext highlighter-rouge">x = 0, y = 188</code></li>
</ul>

<p>4x4 블록을 처리하는 루프에서 <code class="language-plaintext highlighter-rouge">y</code>가 188이라니요…<br />
게다가 레지스터 창을 보니 배열 인덱스인 <code class="language-plaintext highlighter-rouge">rdx</code> 값이 <code class="language-plaintext highlighter-rouge">0xffffffffffef7277</code><sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>라는 거대한 음수를 갖고 있었다.</p>

<p>즉, 무언가 이유로 루프 탈출 조건(<code class="language-plaintext highlighter-rouge">xC == x &amp;&amp; yC == y</code>)을 찾지 못해 <strong>무한 루프</strong>가 돌았고…<br />
인덱스가 음수 영역으로 깎여 내려가다가 유효하지 않은 메모리 경계를 건드려 터진 것이다.</p>

<p>그렇다면 왜 루프가 폭주했을까? 호출부의 코드를 살펴보았다.</p>

<h3 id="호출부의-코드">호출부의 코드</h3>

<p>문제가 발생한 상위 호출 함수는 다음과 같이 평범한 4중 루프 구조를 가지고 있다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">init_scan_orders</span><span class="p">()</span>
<span class="p">{</span>
  <span class="c1">// (앞부분 생략...)</span>

  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">log2size</span><span class="o">=</span><span class="mi">2</span><span class="p">;</span> <span class="n">log2size</span><span class="o">&lt;=</span><span class="mi">5</span><span class="p">;</span> <span class="n">log2size</span><span class="o">++</span><span class="p">)</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">scanIdx</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">scanIdx</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">scanIdx</span><span class="o">++</span><span class="p">)</span>
      <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">y</span><span class="o">&lt;</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="n">log2size</span><span class="p">);</span> <span class="n">y</span><span class="o">++</span><span class="p">)</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">x</span><span class="o">&lt;</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="n">log2size</span><span class="p">);</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span>
          <span class="p">{</span>
            <span class="n">fill_scan_pos</span><span class="p">(</span><span class="o">&amp;</span><span class="n">scanpos</span><span class="p">[</span><span class="n">scanIdx</span><span class="p">][</span><span class="n">log2size</span><span class="p">][</span> <span class="n">y</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="n">log2size</span><span class="p">)</span> <span class="o">+</span> <span class="n">x</span> <span class="p">],</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">scanIdx</span><span class="p">,</span> <span class="n">log2size</span><span class="p">);</span>
          <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>조건을 보면 분명 <code class="language-plaintext highlighter-rouge">y &lt; (1&lt;&lt;log2size)</code>이므로 <code class="language-plaintext highlighter-rouge">log2size=2</code>일 때 <code class="language-plaintext highlighter-rouge">y</code>는 4보다 작아야 한다.<br />
절대로 <code class="language-plaintext highlighter-rouge">y = 188</code>라는 값이 나올 수 없다.</p>

<p>결국 범인은 이 코드를 기계어로 번역한 <strong>컴파일러의 최적화 엔진</strong>이라는 뜻이다.<br />
상황별 컴파일 결과를 비교해 보면 그 요망한(?) 흔적이 고스란히 드러난다.</p>

<hr />

<h3 id="디스어셈블리-비교-분석">디스어셈블리 비교 분석</h3>

<h4 id="1-기본-릴리즈-빌드-최적화-오류-발생">1. 기본 릴리즈 빌드 (최적화 오류 발생)</h4>

<p>최적화 엔진이 루프 한계선(\(1 \ll \text{log2size}\))을 캐싱하려고 시도하다가, 초기화(Store)하기도 전에 값부터 꺼내 쓰는(Load) <strong>순서 역전 버그</strong>를 유발하는 것이 원인이다.</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">; 4중 루프 내부 진입 직전 (log2size 루프 내부)</span>
<span class="err">00007</span><span class="nf">FFB97D13A92</span>  <span class="nv">mov</span>         <span class="nb">eax</span><span class="p">,</span><span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">80h</span><span class="p">]</span>  <span class="c1">; [Error] 아직 계산 결과가 채워지지 않은 스택 공간에서 '쓰레기 값'을 먼저 로드</span>

<span class="nf">...</span> <span class="p">(</span><span class="err">중간</span> <span class="err">레지스터</span> <span class="err">할당</span> <span class="err">로직</span><span class="p">)</span> <span class="nv">...</span>

<span class="c1">; y 루프 (for (int y=0; y&lt;(1&lt;&lt;log2size); y++)) 경계 검사 영역</span>
<span class="err">00007</span><span class="nf">FFB97D13AC0</span>  <span class="nv">mov</span>         <span class="nb">r13d</span><span class="p">,</span><span class="nb">eax</span>                 <span class="c1">; r13d(y 루프 한계선)에 방금 읽은 쓰레기 값이 그대로 주입됨</span>
<span class="err">00007</span><span class="nf">FFB97D13AC3</span>  <span class="nv">mov</span>         <span class="nb">eax</span><span class="p">,</span><span class="nb">esi</span>                  
<span class="err">00007</span><span class="nf">FFB97D13AC5</span>  <span class="nv">mov</span>         <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">80h</span><span class="p">],</span><span class="nb">eax</span>  <span class="c1">; [지각] 정작 올바르게 계산된 진짜 한계값(esi)은 이제야 스택에 저장됨</span>
<span class="err">00007</span><span class="nf">FFB97D13ACC</span>  <span class="nv">test</span>        <span class="nb">r13d</span><span class="p">,</span><span class="nb">r13d</span>
</code></pre></div></div>

<ul class="bluebox-blue">
  <li><strong>핵심 포인트:</strong> <code class="language-plaintext highlighter-rouge">[rsp+80h]</code>에 진짜 루프 한계선이 저장되는 시점은 <code class="language-plaintext highlighter-rouge">3AC5</code> 행이다.<br />
하지만 컴파일러는 이보다 훨씬 앞선 <code class="language-plaintext highlighter-rouge">3A92</code> 행에서 값을 먼저 읽어와 <code class="language-plaintext highlighter-rouge">r13d</code>를 오염시킨다.</li>
  <li><strong>결과:</strong> 초기화되지 않은 무작위 데이터가 <code class="language-plaintext highlighter-rouge">y</code> 루프의 상한선이 되면서 <code class="language-plaintext highlighter-rouge">y</code>가 경계를 넘어 폭주하게 만들고, 하위 함수에서 무한 루프와 크래시를 유발한다.</li>
</ul>

<hr />

<h4 id="2-pragma-optimize-off-적용-최적화-해제">2. #pragma optimize(“”, off) 적용 (최적화 해제)</h4>

<p>최적화가 원인인 것을 알았으니, <strong>최적화 자체를 해제</strong>하는 방법이 가장 간단하다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#if defined(_MSC_VER)
#pragma optimize("", off)
#endif
</span>
<span class="kt">void</span> <span class="nf">init_scan_orders</span><span class="p">()</span>
<span class="p">{</span>
  <span class="c1">// (코드 생략...)</span>
<span class="p">}</span>

<span class="cp">#if defined(_MSC_VER)
#pragma optimize("", on)
#endif
</span></code></pre></div></div>

<p>최적화 엔진의 개입을 완전히 배제했을 때의 컴파일 결과이다.<br />
레지스터 캐싱 같은 기교를 부리지 않고, 매 단계마다 스택 메모리를 참조하며 정직하게 연산한다.</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">; y 루프 한계선 검사 및 비교 영역</span>
<span class="err">00007</span><span class="nf">FFB97D13B38</span>  <span class="nv">mov</span>         <span class="nb">eax</span><span class="p">,</span><span class="mi">1</span>  
<span class="err">00007</span><span class="nf">FFB97D13B3D</span>  <span class="nv">mov</span>         <span class="nb">ecx</span><span class="p">,</span><span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">34h</span><span class="p">]</span>  <span class="c1">; 스택에서 현재 log2size 안전하게 로드</span>
<span class="err">00007</span><span class="nf">FFB97D13B41</span>  <span class="nv">shlx</span>        <span class="nb">eax</span><span class="p">,</span><span class="nb">eax</span><span class="p">,</span><span class="nb">ecx</span>              <span class="c1">; eax = 1 &lt;&lt; log2size 를 실시간 동적 계산</span>
<span class="err">00007</span><span class="nf">FFB97D13B46</span>  <span class="nv">cmp</span>         <span class="kt">dword</span> <span class="nv">ptr</span> <span class="p">[</span><span class="nb">rsp</span><span class="o">+</span><span class="mh">3Ch</span><span class="p">],</span><span class="nb">eax</span>  <span class="c1">; 변수 y([rsp+3Ch])와 방금 계산한 한계선(eax)을 비교</span>
<span class="err">00007</span><span class="nf">FFB97D13B4A</span>  <span class="nv">jge</span>         <span class="nv">init_scan_orders</span><span class="o">+</span><span class="mh">1B6h</span> 
</code></pre></div></div>

<ul class="bluebox-blue">
  <li><strong>핵심 포인트:</strong> 매 루프마다 <code class="language-plaintext highlighter-rouge">shlx</code>(부호 없는 비트 시프트 가속 명령어)를 사용해 상한선을 실시간으로 엄격하게 다시 계산한다.</li>
  <li><strong>결과:</strong> 오작동은 완벽하게 사라지며 코드가 안전하게 돌아간다.<br />
다만 매번 메모리 로드와 비트 연산이 반복되므로, 컴파일러가 제공할 수 있는 성능 최적화 혜택은 포기.</li>
</ul>

<hr />

<h4 id="3-const-int-명시적-상수-선언-정상-최적화-우회">3. const int 명시적 상수 선언 (정상 최적화 우회)</h4>

<p>원인은 알았으니, 최적화를 적절히 할 수 있는 범위까지 진도를 나가본다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">init_scan_orders</span><span class="p">()</span>
<span class="p">{</span>
  <span class="c1">// (앞부분 생략...)</span>

  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">log2size</span><span class="o">=</span><span class="mi">2</span><span class="p">;</span> <span class="n">log2size</span><span class="o">&lt;=</span><span class="mi">5</span><span class="p">;</span> <span class="n">log2size</span><span class="o">++</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="k">const</span> <span class="kt">int</span> <span class="n">one_log2size</span> <span class="o">=</span> <span class="mi">1</span><span class="o">&lt;&lt;</span><span class="n">log2size</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">scanIdx</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">scanIdx</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">scanIdx</span><span class="o">++</span><span class="p">)</span>
      <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">y</span><span class="o">&lt;</span><span class="n">one_log2size</span><span class="p">;</span> <span class="n">y</span><span class="o">++</span><span class="p">)</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">x</span><span class="o">&lt;</span><span class="n">one_log2size</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span>
          <span class="p">{</span>
            <span class="n">fill_scan_pos</span><span class="p">(</span><span class="o">&amp;</span><span class="n">scanpos</span><span class="p">[</span><span class="n">scanIdx</span><span class="p">][</span><span class="n">log2size</span><span class="p">][</span> <span class="n">y</span><span class="o">*</span><span class="n">one_log2size</span> <span class="o">+</span> <span class="n">x</span> <span class="p">],</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">scanIdx</span><span class="p">,</span> <span class="n">log2size</span><span class="p">);</span>
          <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>복잡한(?) 수식을 지역 변수로 쪼개어 컴파일러에게 명확한 선후 관계를 제공한 결과다.<br />
꼬여있던 데이터 흐름이 풀리면서 컴파일러가 완전히 새롭고 영리한 기계어를 생성한다.</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">; 1. 루프 진입 전 명확한 정적 초기화</span>
<span class="err">00007</span><span class="nf">FFBB8D73A92</span>  <span class="nv">mov</span>         <span class="nb">esi</span><span class="p">,</span><span class="mi">4</span>                    <span class="c1">; log2size=2 일 때의 첫 한계값 '4'를 esi 레지스터에 대입</span>

<span class="nf">...</span> <span class="p">(</span><span class="mi">4</span><span class="err">중</span> <span class="err">루프</span> <span class="err">제어</span> <span class="err">및</span> <span class="nv">fill_scan_pos</span> <span class="err">호출</span> <span class="err">로직</span><span class="p">)</span> <span class="nv">...</span>

<span class="c1">; 2. y 루프 경계 조건 비교 (스택 참조 전면 제거)</span>
<span class="err">00007</span><span class="nf">FFBB8D73AB0</span>  <span class="nv">test</span>        <span class="nb">esi</span><span class="p">,</span><span class="nb">esi</span>                  <span class="c1">; 레지스터에 고정된 상수(esi)를 기준으로 y 루프 경계를 검사</span>
<span class="err">00007</span><span class="nf">FFBB8D73AB2</span>  <span class="nv">jle</span>         <span class="nv">init_scan_orders</span><span class="o">+</span><span class="mh">0D8h</span> 

<span class="nf">...</span> 

<span class="c1">; 3. 최외각 루프 종결 및 다음 단계를 위한 레지스터 갱신 영역</span>
<span class="err">00007</span><span class="nf">FFBB8D73B11</span>  <span class="nv">inc</span>         <span class="nb">ebp</span>                      <span class="c1">; log2size++</span>
<span class="err">00007</span><span class="nf">FFBB8D73B13</span>  <span class="nv">rol</span>         <span class="nb">esi</span><span class="p">,</span><span class="mi">1</span>                    <span class="c1">; [최적화] esi 레지스터를 왼쪽으로 1비트 회전(2배 곱하기)!!</span>
<span class="err">00007</span><span class="nf">FFBB8D73B15</span>  <span class="nv">cmp</span>         <span class="nb">ebp</span><span class="p">,</span><span class="mi">5</span>  
</code></pre></div></div>

<ul class="bluebox-blue">
  <li><strong>핵심 포인트:</strong> 문제의 스택 공간(<code class="language-plaintext highlighter-rouge">[rsp+80h]</code>)을 쓰던 오염된 제어 로직이 완전히 사라졌다.<br />
<code class="language-plaintext highlighter-rouge">log2size</code>가 늘어날 때마다 한계치인 <code class="language-plaintext highlighter-rouge">one_log2size</code>가 4, 8, 16, 32로 <strong>정확히 2배씩 증가</strong>한다는 특성을 컴파일러가 온전히 파악해 냈다.</li>
  <li><strong>결과:</strong> 바깥 루프가 돌 때마다 <code class="language-plaintext highlighter-rouge">rol esi, 1</code>(Bit Rotation) 명령어로 레지스터 자체를 가속하여 상한선을 처리한다.<br />
버그를 안전하게 우회하면서도, 기계어 아키텍처 관점에서는 원래 최적화 의도보다 훨씬 정교하고 빠른 하드웨어 친화적 바이너리가 완성되었다.</li>
</ul>

<hr />

<h3 id="마치며-const-선언이-주는-뜻밖의-효과">마치며: <code class="language-plaintext highlighter-rouge">const</code> 선언이 주는 뜻밖의 효과</h3>

<p>지금까지 코드에서 <code class="language-plaintext highlighter-rouge">const</code>로 임시 변수를 분리하는 건 그저 <strong>‘사람의 가독성’</strong>을 높이기 위한 규칙이라고만 생각했었다.</p>

<p>하지만 이번 디버깅을 통해 복잡한 수식을 명시적 변수로 격리하는 행위가 <strong>컴파일러 최적화 엔진의 연산 우선순위 그래프를 단순화하여, 컴파일러의 코드 오독(Miscompilation)을 막아주는 강력한 방파제 역할</strong>을 할 수 있다는 교훈을 얻었다.<br />
컴파일러가 가끔은 생각보다 멍청할 수 있으니, 릴리즈 모드에서만 발생하는 의문의 크래시를 마주한다면 반드시 디스어셈블리 창을 열어 컴파일러의 동선을 의심해 봐야 한다.</p>

<hr />

<p>덧1. 디스어셈블리 결과를 분석하는데는 역시 AI가 <strong>킹왕짱</strong>.</p>

<p>덧2. 본 포스팅 역시 상당 부분은 AI로 작성했음.</p>

<p>덧3. VS2026 v18.6.1, v18.6.2에서도 이 오류는 수정되지 않았음.</p>

<p>덧4. <a href="https://developercommunity.visualstudio.com/t/MSVC-O2-miscompiles-nested-loop-bounds/11095108?">MS에 리포트</a> 완료.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>0xffffffffffef7277은 <strong>-1,084,809</strong> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>BLUEnLIVE</name></author><category term="ITTalk" /><summary type="html"><![CDATA[디버그에선 잘되는데 릴리즈에선 왜 죽을까? 컴파일러의 루프 최적화 버그 추적기]]></summary></entry><entry><title type="html">Notepad++에서 Notepad4 기능 지원을 위한 플러그인 v1.00 공개</title><link href="https://teus.me/notepad4/NPP_plugin_Notepad4/" rel="alternate" type="text/html" title="Notepad++에서 Notepad4 기능 지원을 위한 플러그인 v1.00 공개" /><published>2026-05-19T17:29:00+09:00</published><updated>2026-05-19T17:29:00+09:00</updated><id>https://teus.me/notepad4/NPP_plugin_Notepad4</id><content type="html" xml:base="https://teus.me/notepad4/NPP_plugin_Notepad4/"><![CDATA[<p><a href="https://notepad-plus-plus.org/">Notepad++</a>는 <a href="https://github.com/zufuliu/notepad4">Notepad4</a>에 비해 <strong>다중 탭</strong>과 <strong>플러그인 기능</strong>으로 무장한 <strong>만능 도구</strong>이다.</p>

<p><strong>Notepad++</strong>는 기능도 많고 막강하지만, 몇 가지 기능이 누락되어 있다.<br />
그리고, 이런 부분을 <strong>플러그인</strong>으로 가볍게 보강할 수 있는 구조를 갖고 있다.</p>

<p><strong>Notepad4</strong>에 적용했던 기능들을 Notepad++ 용으로 구현하는 플러그인을 개발하기로 했다.<br />
목표는 그동안 적용했던 기능들을 정리해서 하나의 플러그인에 모두 구현하는 것.</p>

<p>그동안 Notepad4에 추가로 구현한 기능 및 Notepad4에서 가져오고 싶은 기능들은 다음과 같았다.</p>

<ul class="bluebox-blue">
  <li><strong>텍스트 좌우 정렬</strong> (유니코드 표를 참조해서 통상적인 고정폭 글꼴의 폭을 기준으로 함)</li>
  <li><strong>수식 계산</strong></li>
  <li><strong>한국어 한자를 한글로 변환</strong></li>
  <li><strong>한글을 풀어쓰기로 변환</strong></li>
  <li><strong>유니코드 한글 풀어쓰기↔모아쓰기</strong></li>
  <li><strong>조합형 한글 변환</strong></li>
  <li><strong>JS 표현식 평가</strong></li>
  <li><strong>HTML/XML 태그 삭제</strong></li>
  <li><strong>HTML 등에서 주석 삭제</strong></li>
  <li><strong>저장시 암호 설정</strong> (읽을 때 암호화된 파일인지 확인)</li>
  <li><strong>젠코딩</strong></li>
  <li><strong>find/replace에 즐겨 쓰는 Boost 정규식 추가</strong></li>
  <li><strong>lexer 추가</strong>꞉ Srt, SRecord, IntelHex, TekExHex, TLE</li>
  <li><strong>마크다운 lexer 수정</strong>꞉ 시간 데이터가 있을 때 이모지가 아닌 것으로 인식</li>
  <li><strong>C/C++ lexer 수정</strong>꞉ 윈도우 환경 예약어 대폭 추가</li>
  <li><strong>한글 IME에서 커서 폭을 다르게 지정</strong></li>
  <li><strong>Alt+마우스 휠 동작 방식 변경</strong></li>
  <li><strong>현재 날짜/시간 추가시 한국어 환경이면 날짜를 먼저 추가</strong></li>
  <li><strong>파일이 ".LOG"로 시작하면 로그 파일로 인식</strong></li>
</ul>

<p>이 기능들은 일부는 <strong>별도의 메뉴</strong>를 구성해야 하고, 일부는 <strong>메뉴 없이 기본 기능처럼</strong> 동작해야 한다.<br />
또한, 일부 기능은 이미 <strong>누군가가 훌륭한 솔루션을 개발</strong>해놓은 것도 있다.</p>

<p>이 기능들 중에 일단 메뉴를 통해 접근할 수 있는 기능들을 구현해 공개한다.</p>

<p><img src="/images/2026-05-19a/NPP_B_okl_s64_Q.webp" alt="image" / width="842" height="264" loading="eager" class="align-center">
<em>일단 메뉴로 구현할 수 있는 기능부터 구현</em></p>

<p>위에 정리된 기능 중에 구현된 기능 등은 아래 표에서 확인할 수 있다.</p>

<table>
  <thead>
    <tr>
      <th>순번</th>
      <th>기능</th>
      <th>구현 여부</th>
      <th>비고</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td><strong>텍스트 좌우 정렬</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>2</td>
      <td><strong>수식 계산</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>3</td>
      <td><strong>한국어 한자를 한글로 변환</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>4</td>
      <td><strong>한글을 풀어쓰기로 변환</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>5</td>
      <td><strong>유니코드 한글 풀어쓰기↔모아쓰기</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>6</td>
      <td><strong>조합형 한글 변환</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>7</td>
      <td><strong>JS 표현식 평가</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>8</td>
      <td><strong>HTML/XML 태그 삭제</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>9</td>
      <td><strong>HTML 등에서 주석 삭제</strong></td>
      <td>구현 완료</td>
      <td> </td>
    </tr>
    <tr>
      <td>10</td>
      <td><del>저장시 암호 설정</del></td>
      <td>이미 구현돼있음</td>
      <td><a href="https://github.com/JetNpp/NppCrypt">NppCrypt</a></td>
    </tr>
    <tr>
      <td>11</td>
      <td><del>젠코딩</del></td>
      <td>이미 구현돼있음<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>}</td>
      <td><a href="https://github.com/emmetio/npp/issues/19#issuecomment-383800041">npp</a> 또는 jN Notepad++ Plugin</td>
    </tr>
    <tr>
      <td>12</td>
      <td>find/replace 에 즐겨 쓰는 Boost 정규식 기록 추가</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>13</td>
      <td>lexer 추가</td>
      <td>미구현 (Srt, TLE)</td>
      <td>이미 구현돼있음 (SRecord, IntelHex, TekExHex)</td>
    </tr>
    <tr>
      <td>14</td>
      <td>마크다운 lexer 수정</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>15</td>
      <td>C/C++ lexer 수정</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>16</td>
      <td>한글 IME에서 커서 폭을 다르게 지정</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>17</td>
      <td>Alt+마우스 휠 동작 방식 변경</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>18</td>
      <td>현재 날짜/시간 추가 기능 수정</td>
      <td>미구현</td>
      <td> </td>
    </tr>
    <tr>
      <td>19</td>
      <td>로그 파일 인식</td>
      <td>미구현</td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>지원되는 환경은 다음과 같으며, 이는 <strong>Notepad++와 동일</strong>하다.</p>

<ul class="bluebox-green">
  <li>CPU: x64, x86, ARM64</li>
  <li>OS꞉ Windows 8.1, 10, 11</li>
</ul>

<p>이 플러그인의 소스는 <a href="https://github.com/bluenlive/NPP_Notepad4">깃허브</a>에서 볼 수 있으며, <a href="https://github.com/bluenlive/NPP_Notepad4/releases">여기</a>에서 다운받을 수 있다.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>파이썬 버전이 아닌 C++ 버전을 구현할지 고민 중임 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>BLUEnLIVE</name></author><category term="Notepad4" /><summary type="html"><![CDATA[Notepad++는 Notepad4에 비해 다중 탭과 플러그인 기능으로 무장한 만능 도구이다.]]></summary></entry><entry><title type="html">Notepad4 26.05 r6166 한국어화 공개</title><link href="https://teus.me/notepad4/Notepad4-r6166/" rel="alternate" type="text/html" title="Notepad4 26.05 r6166 한국어화 공개" /><published>2026-05-19T15:25:00+09:00</published><updated>2026-05-19T15:25:00+09:00</updated><id>https://teus.me/notepad4/Notepad4-r6166</id><content type="html" xml:base="https://teus.me/notepad4/Notepad4-r6166/"><![CDATA[<p>오류를 수정하고 다양한 성능 개선이 반영된 버전을 공개한다.</p>

<h2 id="개요">개요</h2>

<p><img src="/images/2026-05-19/notepad4_B_okl_s64_Q.webp" alt="image" / width="773" height="573" loading="eager" class="align-center"></p>

<p>아래 링크에서 다운받을 수 있으며, 언제나 그렇듯 x86/x64/AVX2/AVX-512/ARM64 버전이 함께 들어있다.</p>

<div class="download-box ">
  
    <a href="/attachment/2026-05-19/Notepad4-r6166.zip" class="download-btn">
      Download 
      
        Notepad4-r6166.zip
      
    </a>
  
  
  <br />
  
  
  
  password: <span class="password">teus.me</span>
</div>

<h2 id="히스토리">히스토리</h2>

<ul class="bluebox-history">
  <li>2026.5.19꞉ 26.05 r6166 공개
    <ul>
      <li><strong>Release v26.05r6166</strong>. 반영 (<a href="https://github.com/zufuliu/notepad4/commit/4d8b94195f9452963643a86465ad8dff9d24e5b2">링크</a>)
        <ul>
          <li>[Ruby] Correct handle non-ASCII character after ?\, fix out-bounds styling. (<a href="https://github.com/zufuliu/notepad4/commit/02a181871909f8737a81171a7b0c483c2ea9c2ba">링크</a>)</li>
          <li>pdate paste board to use clipboard listener instead of old clipboard viewer. (<a href="https://github.com/zufuliu/notepad4/commit/7074fa34fa851bb5e167c20bc1b8bbfa75560bf5">링크</a>)</li>
          <li>Support Typst (<a href="https://github.com/zufuliu/notepad4/commit/6f696f5697398fad2f3fa75af1d1dd325f533a5c">링크</a>)</li>
          <li>Update Texinfo command list to Texinfo 7.3. (<a href="https://github.com/zufuliu/notepad4/commit/06bbe634b080163b90060844f22160aa1a82cacf">링크</a>)</li>
          <li>Update JavaScript API to ECMAScript 2026. (<a href="https://github.com/zufuliu/notepad4/commit/b973180541a9ce01bdf90fde3fe906fb3274f658">링크</a>)</li>
        </ul>
      </li>
      <li>사용자 인터페이스 수정
        <ul>
          <li>오자 수정: 컬럼  ⟶  칼럼</li>
          <li>메뉴 띄어쓰기 미세 수정</li>
        </ul>
      </li>
      <li>기능 수정
        <ul>
          <li>Ctrl키와 함께 ‘ANSI로 다시 읽기’ 실행시 <strong>상용 조합형(KSSM)</strong>으로 다시 읽는 기능 추가</li>
          <li>HTML/XML 태그 삭제 기능 보강 (스크립트 처리 등)</li>
          <li>주석 삭제 (HTML/C++/파이썬) 기능 추가</li>
          <li>이진 탐색을 <strong>C++20</strong>의 <code class="language-plaintext highlighter-rouge">std::ranges::lower_bound()</code> 를 사용하도록 변경</li>
          <li>옛한글 NFD 결합 문자 글자 폭 보정 및 테이블 최적화</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>]]></content><author><name>BLUEnLIVE</name></author><category term="Notepad4" /><summary type="html"><![CDATA[오류를 수정하고 다양한 성능 개선이 반영된 버전을 공개한다.]]></summary></entry><entry><title type="html">NPP 플러그인과 이진탐색에 대한 가벼운 고찰</title><link href="https://teus.me/algorithm/Notepad4_NPP_bisearch/" rel="alternate" type="text/html" title="NPP 플러그인과 이진탐색에 대한 가벼운 고찰" /><published>2026-05-17T22:37:00+09:00</published><updated>2026-05-17T22:37:00+09:00</updated><id>https://teus.me/algorithm/Notepad4_NPP_bisearch</id><content type="html" xml:base="https://teus.me/algorithm/Notepad4_NPP_bisearch/"><![CDATA[<h2 id="1-최초-기능-구현">1. 최초 기능 구현</h2>

<p><a href="https://github.com/zufuliu/notepad4">Notepad4</a>에는 <strong>좌우 정렬</strong> 기능이 있다.<br />
마치 <strong>워드프로세서</strong>에서 <strong>문단 정렬</strong>을 하듯이 정렬하는 기능이다.</p>

<p>그런데, 이 기능은 <strong>모든 글자</strong>를 <strong>동일한 폭</strong>으로 놓고 정렬한다.<br />
따라서, 한국어 등의 환경에서는 뭔가 이상하게 동작할 수밖에 없다.</p>

<p>본 블로그에서 배포하는 버전은 여기에 기능 하나를 보강했다.<br />
유니코드 테이블을 뒤져서 <strong>반각, 전각 글자 및 폭이 없는 글자</strong>에 대한 폭 계산을 별도로 하는 것.</p>

<p>물론 엄밀하게 규정된 글자의 폭이란 게 없긴 하지만, 최대한 반영한 숨은 내용은 다음과 같다.</p>

<ul class="bluebox-blue">
  <li>눈에 보이지 않는 <strong>제로 너비 문자 (Zero-width joiners, 제어 문자)</strong></li>
  <li>앞 글자에 기생하여 폭을 차지하지 않는 <strong>결합 문자 (Combining marks)</strong></li>
  <li>유니코드 테이블 사방에 흩어져 있는 <strong>전각 기호, 반각 기호, 특수 수학 기호</strong>들</li>
</ul>

<p>이를 위해 <strong>유니코드 전체 영역을 세밀한 ‘범위(Interval/Range)’ 단위로 쪼개어</strong> 글자 폭을 계산했다.</p>

<h2 id="2-기존-구현-직접-구현한-범위-검색용-이진-탐색-before">2. 기존 구현: 직접 구현한 범위 검색용 이진 탐색 (Before)</h2>

<p>유니코드에 산재한 세부 범위들을 고속으로 탐색하기 위해, <strong>구간(Interval) 검색용 이진 탐색 함수를 직접 구현</strong>했었다.<br />
순차 탐색(Linear Search)으로 이 범위들을 직접 훑는 건 <strong>성능 손실</strong>이 너무 크고.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">interval</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">first</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">last</span><span class="p">;</span>
<span class="p">};</span>

<span class="cm">/* auxiliary function for binary search in interval table */</span>
<span class="k">static</span> <span class="k">constexpr</span> <span class="kt">int</span> <span class="nf">bisearch</span><span class="p">(</span><span class="k">const</span> <span class="kt">int</span> <span class="n">ucs</span><span class="p">,</span> <span class="k">const</span> <span class="k">struct</span> <span class="nc">interval</span> <span class="o">*</span><span class="n">table</span><span class="p">,</span> <span class="kt">int</span> <span class="n">max</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">min</span><span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
    <span class="kt">int</span> <span class="n">mid</span><span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>

    <span class="c1">// 예외적인 경계 조건 먼저 스크리닝</span>
    <span class="k">if</span> <span class="p">((</span><span class="n">ucs</span> <span class="o">&lt;</span> <span class="n">table</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">first</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">ucs</span> <span class="o">&gt;</span> <span class="n">table</span><span class="p">[</span><span class="n">max</span><span class="p">].</span><span class="n">last</span><span class="p">))</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>

    <span class="c1">// 수제 이진 탐색 루프</span>
    <span class="k">while</span> <span class="p">(</span><span class="n">max</span> <span class="o">&gt;=</span> <span class="n">min</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">mid</span> <span class="o">=</span> <span class="p">(</span><span class="n">min</span> <span class="o">+</span> <span class="n">max</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">ucs</span> <span class="o">&gt;</span> <span class="n">table</span><span class="p">[</span><span class="n">mid</span><span class="p">].</span><span class="n">last</span><span class="p">)</span>
            <span class="n">min</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
        <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">ucs</span> <span class="o">&lt;</span> <span class="n">table</span><span class="p">[</span><span class="n">mid</span><span class="p">].</span><span class="n">first</span><span class="p">)</span>
            <span class="n">max</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
        <span class="k">else</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// 범위 안에서 매칭 성공</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>이 수제 <code class="language-plaintext highlighter-rouge">bisearch()</code> 코드는 컴파일 타임 상수(<code class="language-plaintext highlighter-rouge">constexpr</code>)로 작동하며 오랫동안 훌륭하게 제 역할을 해주었다.<br />
그러다 문득 코드 리뷰 과정에서 이런 생각이 들었다.</p>

<div class="quoteMachine">
  <div class="theQuote">
    <blockquote><span class="quotationMark quotationMark--left"></span>
      
        
          이미 C++ STL에 검증된 고성능 이진 탐색 함수가 있는데, 왜 굳이 이 코드를 내가 유지보수하고 있었지?

      
      <span class="quotationMark quotationMark--right"></span></blockquote>
  </div>

  <div class="quoteAuthor">
    
      &nbsp;
    
  </div>
</div>

<p>그렇게 STL을 이용한 전면적인 리팩토링을 하기로 했다.</p>

<h2 id="3-c-stl에-구현되어-있는-이진-탐색-함수들">3. C++ STL에 구현되어 있는 이진 탐색 함수들</h2>

<p>C++ 표준 라이브러리(<code class="language-plaintext highlighter-rouge">&lt;algorithm&gt;</code>)는 안전하고 컴파일러 최적화가 극대화된 4가지 이진 탐색 도구를 제공한다.</p>

<ul class="bluebox-green">
  <li><code class="language-plaintext highlighter-rouge">std::binary_search</code>: 값이 존재하는지 여부(<code class="language-plaintext highlighter-rouge">bool</code>)만 반환. 위치는 알 수 없음</li>
  <li><code class="language-plaintext highlighter-rouge">std::lower_bound</code>: 지정한 값보다 <strong>크거나 같은 첫 번째 원소</strong>의 위치(Iterator) 반환.</li>
  <li><code class="language-plaintext highlighter-rouge">std::upper_bound</code>: 지정한 값을 <strong>초과하는 첫 번째 원소</strong>의 위치를 반환.</li>
  <li><code class="language-plaintext highlighter-rouge">std::equal_range</code>: <code class="language-plaintext highlighter-rouge">lower_bound</code>와 <code class="language-plaintext highlighter-rouge">upper_bound</code> 결과를 한 번에 반환하여 중복 데이터 구간을 캡처.</li>
</ul>

<p>정렬된 유니코드 테이블 테이블에서 정확한 위치(Iterator)를 찾아내어 데이터를 다뤄야 한다.<br />
따라서, 이진 탐색의 정석인 <code class="language-plaintext highlighter-rouge">std::lower_bound</code>를 사용해 기존의 수제 <code class="language-plaintext highlighter-rouge">bisearch</code> 루프를 대체하면 된다.</p>

<h2 id="4-어떻게-변경했는가-after">4. 어떻게 변경했는가 (After)</h2>

<h3 id="41-stl-기반으로-정제된-코드">4.1. STL 기반으로 정제된 코드</h3>

<p>기존에 구현했던 <code class="language-plaintext highlighter-rouge">while (max &gt;= min)</code> 기반의 복잡한 이진 탐색 제어 코드를 걷어내고, STL에 탐색을 위임했다.<br />
직접 인덱스(<code class="language-plaintext highlighter-rouge">min</code>, <code class="language-plaintext highlighter-rouge">max</code>, <code class="language-plaintext highlighter-rouge">mid</code>)를 계산하던 루프는 <code class="language-plaintext highlighter-rouge">std::lower_bound</code>와 람다 함수를 쓰면 몇 줄로 적을 수 있다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;algorithm&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;iterator&gt;</span><span class="cp">
</span>
<span class="k">static</span> <span class="k">constexpr</span> <span class="kt">bool</span> <span class="nf">bisearch</span><span class="p">(</span><span class="k">const</span> <span class="kt">int</span> <span class="n">ucs</span><span class="p">,</span> <span class="k">const</span> <span class="k">struct</span> <span class="nc">interval</span> <span class="o">*</span><span class="n">table</span><span class="p">,</span> <span class="kt">int</span> <span class="n">max</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">ucs</span> <span class="o">&lt;</span> <span class="n">table</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">first</span> <span class="o">||</span> <span class="n">ucs</span> <span class="o">&gt;</span> <span class="n">table</span><span class="p">[</span><span class="n">max</span><span class="p">].</span><span class="n">last</span><span class="p">)</span>
        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

    <span class="c1">// max는 최대 인덱스이므로, 탐색 범위 크기는 max + 1</span>
    <span class="k">const</span> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">lower_bound</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="n">table</span> <span class="o">+</span> <span class="n">max</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ucs</span><span class="p">,</span>
        <span class="p">[](</span><span class="k">const</span> <span class="k">struct</span> <span class="nc">interval</span><span class="o">&amp;</span> <span class="n">i</span><span class="p">,</span> <span class="kt">int</span> <span class="n">val</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">i</span><span class="p">.</span><span class="n">last</span> <span class="o">&lt;</span> <span class="n">val</span><span class="p">;</span>
        <span class="p">});</span>

    <span class="k">return</span> <span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">table</span> <span class="o">+</span> <span class="n">max</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">ucs</span> <span class="o">&gt;=</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">first</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="42-c20에서는-좀-더-간결히-기술-가능">4.2. C++20에서는 좀 더 간결히 기술 가능</h3>

<p>C++20에서는 <code class="language-plaintext highlighter-rouge">std::ranges::lower_bound</code>를 사용해서 좀 더 깔끔하게 쓸 수 있다.<br />
이 경우는 <code class="language-plaintext highlighter-rouge">max</code>를 넘길 필요가 없다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;algorithm&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;span&gt;</span><span class="cp">
</span>
<span class="k">static</span> <span class="k">constexpr</span> <span class="kt">bool</span> <span class="nf">bisearch_modern</span><span class="p">(</span><span class="k">const</span> <span class="kt">int</span> <span class="n">ucs</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">span</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">interval</span><span class="o">&gt;</span> <span class="n">table</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">table</span><span class="p">.</span><span class="n">empty</span><span class="p">()</span> <span class="o">||</span> <span class="n">ucs</span> <span class="o">&lt;</span> <span class="n">table</span><span class="p">.</span><span class="n">front</span><span class="p">().</span><span class="n">first</span> <span class="o">||</span> <span class="n">ucs</span> <span class="o">&gt;</span> <span class="n">table</span><span class="p">.</span><span class="n">back</span><span class="p">().</span><span class="n">last</span><span class="p">)</span>
        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>

    <span class="k">const</span> <span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">ranges</span><span class="o">::</span><span class="n">lower_bound</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="n">ucs</span><span class="p">,</span> <span class="p">{},</span> <span class="o">&amp;</span><span class="n">interval</span><span class="o">::</span><span class="n">last</span><span class="p">);</span>

    <span class="k">return</span> <span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">table</span><span class="p">.</span><span class="n">end</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">ucs</span> <span class="o">&gt;=</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">first</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="5-stl-이진-탐색-함수에-대해-한-발짝만-더-들여다보면">5. STL 이진 탐색 함수에 대해 한 발짝만 더 들여다보면…</h2>

<p><code class="language-plaintext highlighter-rouge">std::lower_bound</code>가 내부적으로 돌아가는 규칙들을 들여다보자.</p>

<h3 id="-람다-조건식이-ilast--val-작다-인-이유">① 람다 조건식이 <code class="language-plaintext highlighter-rouge">i.last &lt; val</code> (작다) 인 이유</h3>

<p><code class="language-plaintext highlighter-rouge">lower_bound</code>는 “크거나 같은 첫 번째 지점”을 찾는데 수식은 ‘작다(<code class="language-plaintext highlighter-rouge">&lt;</code>)’를 쓴다.<br />
이 조건식은 <strong>“루프를 끝낼 조건”</strong>이 아니라 <strong>“오른쪽으로 계속 탐색을 진행할 조건”</strong>이기 때문이다.</p>

<p>구간(Interval) 검색 버전을 보면 조금은 이해가 쉽다.</p>

<ul class="bluebox-yellow">
  <li>알고리즘이 배열의 처음부터 우측으로 검사해가며 <em>“이 구간의 끝(<code class="language-plaintext highlighter-rouge">i.last</code>)이 내가 찾는 값(<code class="language-plaintext highlighter-rouge">val</code>)보다 작니?”</em>라고 묻는다.</li>
  <li><code class="language-plaintext highlighter-rouge">true</code>가 나오면 “현재 구간은 타겟보다 완전히 왼쪽에 있으니 더 오른쪽 영역을 뒤져라”가 된다.</li>
  <li>그러다 <strong>처음으로 <code class="language-plaintext highlighter-rouge">false</code>가 나오는 경계선</strong>을 포착하면, “이 구간의 끝이 타겟보다 크거나 같다”는 뜻이 되므로 루프를 멈춘다.</li>
</ul>

<h3 id="-람다-함수의-인자-지정-규칙">② 람다 함수의 인자 지정 규칙</h3>

<p><code class="language-plaintext highlighter-rouge">std::lower_bound</code>에 커스텀 람다를 붙일 때 인자의 순서는 표준 사양으로 엄격하게 약속되어 있다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[](</span><span class="k">const</span> <span class="k">struct</span> <span class="nc">interval</span><span class="o">&amp;</span> <span class="n">i</span><span class="p">,</span> <span class="kt">int</span> <span class="n">val</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>“첫 번째 인자는 배열에서 꺼낸 데이터 원소(Left), 두 번째 인자는 사용자가 찾으려는 키값(Right)”</strong> 순서여야 한다.</p>

<p>참고로 초과하는 지점을 찾는 <code class="language-plaintext highlighter-rouge">std::upper_bound</code>는 이 인자 순서가 반대로 뒤집힌다.<br />
이는 C++ 설계자들이 오직 ‘작다(<code class="language-plaintext highlighter-rouge">&lt;</code>)’ 연산자 하나만으로 모든 탐색 논리를 통일하려다 생긴 결과라고 한다.</p>

<h3 id="-왜-stdend하고만-비교할까">③ 왜 <code class="language-plaintext highlighter-rouge">std::end()</code>하고만 비교할까?</h3>

<p>목표 값이 배열의 첫 번째 값보다 작으면 <code class="language-plaintext highlighter-rouge">lower_bound</code>는 정상 포인터인 <code class="language-plaintext highlighter-rouge">std::begin()</code>을 반환, 메모리 참조가 안전하다.<br />
반면, 배열의 모든 값보다 크면 배열 밖의 허공인 <code class="language-plaintext highlighter-rouge">std::end()</code>를 반환하며, 이때 무작정 접근하면 <strong>크래시</strong>가 난다.</p>

<p>기존 수제 코드에서 <code class="language-plaintext highlighter-rouge">if ((ucs &lt; table[0].first) || (ucs &gt; table[max].last))</code>로 앞뒤 경계를 수동 차단했던 예외 처리가, STL에서는 <code class="language-plaintext highlighter-rouge">it != table + max + 1</code>로 최소한의 우측 안전선만 확보해 준 뒤, 안전 구역 안에서 <code class="language-plaintext highlighter-rouge">ucs &gt;= it-&gt;first</code>라는 논리 검사로 구간 포함 여부를 판정하는 형태로 전환된 것이다.</p>

<h2 id="맺음말">맺음말</h2>

<p>실제 이러한 수정으로 얻어지는 동작 성능의 증감이나 바이너리 크기의 변화 등은 없다.<br />
성능도 대동소이하고, 실행파일의 크기는 완전히 동일했다.</p>

<p>그렇다면 <strong>더 깔끔한 코드</strong>를 위해 STL를 쓰는 편이 더 낫겠다.<br />
그런데, 이거 대체 왜 일일이 짰던 거였지?<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup></p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>글 쓰다가 기억났는데, 원래 저 부분의 소스는 C++가 아니라 <strong>C언어</strong>로 작성된 부분이었기 때문임 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>BLUEnLIVE</name></author><category term="algorithm" /><summary type="html"><![CDATA[1. 최초 기능 구현]]></summary></entry></feed>