<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Programming on philipjkim</title><link>https://philipjkim.cc/tags/programming/</link><description>Recent content in Programming on philipjkim</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Wed, 12 Aug 2026 13:21:01 +0900</lastBuildDate><atom:link href="https://philipjkim.cc/tags/programming/index.xml" rel="self" type="application/rss+xml"/><item><title>Hugo 로 바둑판 도해 그리기 Part 2: 이미지 65장을 표기로 옮기기</title><link>https://philipjkim.cc/posts/20260812-hugo-goban-shortcode-02/</link><pubDate>Wed, 12 Aug 2026 13:21:01 +0900</pubDate><guid>https://philipjkim.cc/posts/20260812-hugo-goban-shortcode-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260812-hugo-goban-shortcode-01/"&gt;Part 1&lt;/a&gt; 에서 바둑판 도해를 그리는 &lt;code&gt;goban&lt;/code&gt; shortcode 를 만들었습니다. ASCII 로 쓰고 SVG 로 렌더링하는 방식입니다.&lt;/p&gt;
&lt;p&gt;만들고 나니 다른 시리즈가 눈에 들어왔습니다. 9x9 바둑 교재 리뷰 4편에 &lt;strong&gt;PNG 이미지로 된 도해가 65장&lt;/strong&gt; 있었습니다. 이걸 전부 표기로 옮기기로 했습니다.&lt;/p&gt;
&lt;p&gt;Part 2 는 그 작업의 기록입니다. 결론부터 적으면 &lt;strong&gt;작업의 90%는 shortcode 와 무관한 일&lt;/strong&gt; 이었고, 나머지 10% 에서 렌더링은 멀쩡한데 내용만 틀리는 종류의 버그를 하나 만났습니다.&lt;/p&gt;</description></item><item><title>Hugo 로 바둑판 도해 그리기 Part 1: 왜 shortcode 였나</title><link>https://philipjkim.cc/posts/20260812-hugo-goban-shortcode-01/</link><pubDate>Wed, 12 Aug 2026 13:18:52 +0900</pubDate><guid>https://philipjkim.cc/posts/20260812-hugo-goban-shortcode-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;이 블로그에 바둑 입문 시리즈를 쓰다가 벽에 부딪혔습니다. &lt;strong&gt;바둑 글에는 바둑판 그림이 필요한데, 그릴 방법이 마땅치 않았습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;결국 &lt;code&gt;goban&lt;/code&gt; 이라는 이름의 Hugo shortcode 를 직접 만들었습니다. 본문에는 이렇게 쓰고,&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{{&amp;lt; goban caption=&amp;#34;빈 삼각형&amp;#34; &amp;gt;}}
 a b c d e
 1 . X . . .
 2 X O * . .
 3 . . . . .
{{&amp;lt; /goban &amp;gt;}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;렌더링은 SVG 바둑판으로 나가는 방식입니다. 실제 결과는 이렇습니다.&lt;/p&gt;</description></item><item><title>렉시오 CPU 플레이어 만들기 Part 7: 코드 없이 보는 easy 봇과 hard 봇의 차이</title><link>https://philipjkim.cc/posts/20260810-lexio-cpu-bot-strategy-07/</link><pubDate>Mon, 10 Aug 2026 09:10:46 +0900</pubDate><guid>https://philipjkim.cc/posts/20260810-lexio-cpu-bot-strategy-07/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="이-글은-누구를-위한-것인가"&gt;이 글은 누구를 위한 것인가&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-01/"&gt;Part 1&lt;/a&gt; 부터 &lt;a href="https://philipjkim.cc/posts/20260810-lexio-cpu-bot-strategy-06/"&gt;Part 6&lt;/a&gt; 까지는 비트마스크, 동적 계획법, 평가 함수, 확률 모델 같은 이야기로 채워져 있습니다. 렉시오를 좋아하지만 개발자는 아닌 분에게는 읽을 이유가 없는 글입니다.&lt;/p&gt;
&lt;p&gt;이번 편은 그 여섯 편의 &lt;strong&gt;결론만&lt;/strong&gt; 을 코드와 수식 없이 옮긴 것입니다. 렉시오 규칙을 아는 분이라면 누구나 읽을 수 있습니다. 다루는 것은 셋입니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;두 CPU 는 각각 무엇을 보고 수를 고르는가&lt;/li&gt;
&lt;li&gt;둘을 어떻게 겨루게 했고, 결과가 어땠는가&lt;/li&gt;
&lt;li&gt;그 결과로 무엇은 말할 수 있고, 무엇은 여전히 말할 수 없는가&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;세 번째가 이 글에서 가장 중요합니다. 숫자를 보여 주는 글은 많지만, 그 숫자가 무엇을 증명하지 &lt;strong&gt;못&lt;/strong&gt; 하는지까지 적는 글은 드뭅니다.&lt;/p&gt;</description></item><item><title>렉시오 CPU 플레이어 만들기 Part 6: 봇이 약한지 어떻게 아는가</title><link>https://philipjkim.cc/posts/20260810-lexio-cpu-bot-strategy-06/</link><pubDate>Mon, 10 Aug 2026 08:51:18 +0900</pubDate><guid>https://philipjkim.cc/posts/20260810-lexio-cpu-bot-strategy-06/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="눈으로-한-판-돌려서는-못-보는-것"&gt;눈으로 한 판 돌려서는 못 보는 것&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260807-lexio-cpu-bot-strategy-05/"&gt;Part 5&lt;/a&gt; 에서 고친 결함은 눈에 보이는 것이었습니다. 봇이 거의 모든 턴을 패스했고, 브라우저에서 5인 게임을 한 판 돌리자 바로 드러났습니다.&lt;/p&gt;
&lt;p&gt;그 방법에는 한계가 분명합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;봇이 100% 패스한다 → 한 판이면 보입니다&lt;/li&gt;
&lt;li&gt;봇이 라운드당 1.4점씩 잃는다 → 몇 판을 돌려도 느낄 수 없습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;렉시오는 딜 운의 분산이 큽니다. 좋은 패를 받은 판과 나쁜 패를 받은 판의 차이가 정책의 차이보다 훨씬 크기 때문에, 사람이 몇 판 보고 &amp;ldquo;이 봇이 좀 약한 것 같다&amp;quot;고 말하는 것은 근거가 되지 못합니다. 측정 도구 없이 가중치를 만지면 더 나쁩니다. 고쳤다고 생각한 것이 실은 운이었는지 확인할 방법이 없습니다.&lt;/p&gt;</description></item><item><title>렉시오 CPU 플레이어 만들기 Part 5: 봇이 아무것도 하지 않는 이유</title><link>https://philipjkim.cc/posts/20260807-lexio-cpu-bot-strategy-05/</link><pubDate>Fri, 07 Aug 2026 07:56:13 +0900</pubDate><guid>https://philipjkim.cc/posts/20260807-lexio-cpu-bot-strategy-05/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="설계와-구현-사이"&gt;설계와 구현 사이&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-01/"&gt;Part 1&lt;/a&gt; 부터 &lt;a href="https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-04/"&gt;Part 4&lt;/a&gt; 까지는 설계안이었습니다. 타일을 정수로 압축하고, 미공개 집합을 복원하고, 손패 분해를 DP 로 풀고, 목적 함수를 정산식에서 유도해 평가 함수의 항을 나열했습니다. 코드 조각은 있었지만 돌려 본 적은 없었습니다.&lt;/p&gt;
&lt;p&gt;이번 편은 그 설계를 실제로 구현해 브라우저에서 5인 게임을 돌려 본 기록입니다. 결과부터 적겠습니다. &lt;strong&gt;봇이 거의 모든 턴을 패스했습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;원인을 추적하고 나서 든 생각은 &amp;ldquo;설계가 틀렸다&amp;quot;가 아니었습니다. 항을 하나씩 뜯어보면 Part 4 가 쓴 대로였고, 개별 함수는 각각 맞는 값을 돌려주고 있었습니다. 틀린 것은 &lt;strong&gt;항을 합치는 방식&lt;/strong&gt; 이었고, 그중 일부는 Part 1~4 가 아예 말하지 않은 자리였습니다.&lt;/p&gt;</description></item><item><title>달무티 CPU 플레이어 만들기 Part 4: 만들어 보니 hard 봇이 더 약했다</title><link>https://philipjkim.cc/posts/20260803-dalmuti-cpu-bot-strategy-04/</link><pubDate>Mon, 03 Aug 2026 17:57:55 +0900</pubDate><guid>https://philipjkim.cc/posts/20260803-dalmuti-cpu-bot-strategy-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="설계안이-검증을-만나면"&gt;설계안이 검증을 만나면&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-01/"&gt;Part 1&lt;/a&gt;에서 공정성 경계와 결정적 easy 봇을 만들었고, &lt;a href="https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-02/"&gt;Part 2&lt;/a&gt;에서 hard 봇의 정보 모델을 세웠고, &lt;a href="https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-03/"&gt;Part 3&lt;/a&gt;에서 수 평가 함수와 전략적 패스로 실제 결정을 내렸습니다. 세 편 모두 &amp;ldquo;이 글은 설계안이고 코드는 논지를 보이기 위한 발췌&amp;quot;라고 밝혀 두었습니다.&lt;/p&gt;
&lt;p&gt;그 설계안을 실제 게임에 구현하고, 시뮬레이터를 붙여 easy 봇과 같은 조건에서 붙여 봤습니다. 결과는 이랬습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;최초 hard 봇은 easy 봇보다 &lt;strong&gt;평균 종료 순위가 0.6601등 나빴습니다.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>8비트의 벽을 넘은 게임들 Part 5: 제약이 사라진 시대에 남은 것</title><link>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-05/</link><pubDate>Mon, 03 Aug 2026 09:48:13 +0900</pubDate><guid>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-05/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="지금까지의-이야기"&gt;지금까지의 이야기&lt;/h2&gt;
&lt;p&gt;네 편에 걸쳐 세 작품을 뜯었습니다. 정리하면 이렇습니다.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;슈퍼 마리오브라더스&lt;/th&gt;
 &lt;th&gt;엘리트&lt;/th&gt;
 &lt;th&gt;메탈기어&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;연도 / 기종&lt;/td&gt;
 &lt;td&gt;1985 / 패미콤&lt;/td&gt;
 &lt;td&gt;1984 / BBC Micro&lt;/td&gt;
 &lt;td&gt;1987 / MSX2&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;부족했던 자원&lt;/td&gt;
 &lt;td&gt;ROM 40KB&lt;/td&gt;
 &lt;td&gt;RAM 32KB&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;strong&gt;압축&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;생성&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;전복&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;핵심 기법&lt;/td&gt;
 &lt;td&gt;오브젝트 2바이트 인코딩&lt;/td&gt;
 &lt;td&gt;트리보나치 씨앗 트위스팅&lt;/td&gt;
 &lt;td&gt;요구사항 자체를 바꿈&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;세계 데이터&lt;/td&gt;
 &lt;td&gt;4,460 바이트&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;6 바이트&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;20,246 바이트&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;치른 대가&lt;/td&gt;
 &lt;td&gt;표현 가능한 오브젝트가 제한됨&lt;/td&gt;
 &lt;td&gt;세부를 손댈 수 없음&lt;/td&gt;
 &lt;td&gt;원래 만들려던 게임을 포기함&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이 마지막 편에서는 이 세 가지가 40년 뒤에 어떻게 됐는지 봅니다.&lt;/p&gt;</description></item><item><title>8비트의 벽을 넘은 게임들 Part 4: 메탈기어, 못 그리는 것을 안 그려도 되게 만들다</title><link>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-04/</link><pubDate>Mon, 03 Aug 2026 09:43:31 +0900</pubDate><guid>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="세-번째-길"&gt;세 번째 길&lt;/h2&gt;
&lt;p&gt;앞의 두 편에서 본 전략을 정리하면 이렇습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://philipjkim.cc/posts/20260803-8bit-hardware-limits-02/"&gt;Part 2&lt;/a&gt; 슈퍼 마리오브라더스: &lt;strong&gt;데이터를 작게 만든다.&lt;/strong&gt; 레벨 오브젝트 2바이트 인코딩.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://philipjkim.cc/posts/20260803-8bit-hardware-limits-03/"&gt;Part 3&lt;/a&gt; 엘리트: &lt;strong&gt;데이터를 갖지 않는다.&lt;/strong&gt; 씨앗 6바이트에서 우주 전체를 계산.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;둘 다 &amp;ldquo;요구사항은 그대로 두고 구현을 비튼다&amp;rdquo; 는 접근입니다. 1987년 코나미의 MSX2 팀은 세 번째 길을 갔습니다. &lt;strong&gt;요구사항 자체를 바꿨습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;결과물이 메탈기어이고, 그 결과물이 만든 것은 게임 하나가 아니라 &lt;strong&gt;장르 하나&lt;/strong&gt; 였습니다.&lt;/p&gt;</description></item><item><title>8비트의 벽을 넘은 게임들 Part 3: 엘리트, 우주를 6바이트에 담다</title><link>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-03/</link><pubDate>Mon, 03 Aug 2026 09:33:14 +0900</pubDate><guid>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="저장하지-않으면-용량도-들지-않는다"&gt;저장하지 않으면 용량도 들지 않는다&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260803-8bit-hardware-limits-02/"&gt;Part 2&lt;/a&gt;에서 본 슈퍼 마리오브라더스의 전략은 &lt;strong&gt;데이터를 작게 만드는 것&lt;/strong&gt; 이었습니다. 레벨 오브젝트를 2바이트로 인코딩해 게임 전체의 스테이지 데이터를 4,460바이트에 넣었습니다.&lt;/p&gt;
&lt;p&gt;1984년 BBC Micro 로 나온 엘리트는 한 걸음 더 나갑니다. &lt;strong&gt;데이터를 아예 갖지 않습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;먼저 규모를 봅시다. 엘리트에는 은하가 8개 있고, 은하마다 항성계가 256개 있습니다. &lt;strong&gt;총 2,048개&lt;/strong&gt; 입니다. 각 항성계에는 고유한 이름이 있고, 좌표가 있고, 경제 형태, 정부 형태, 기술 수준, 인구, 총생산, 행성 반지름, 지배 종족이 있습니다.&lt;/p&gt;</description></item><item><title>8비트의 벽을 넘은 게임들 Part 2: 슈퍼 마리오브라더스, 레벨을 그리지 않고 서술하다</title><link>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-02/</link><pubDate>Mon, 03 Aug 2026 09:24:56 +0900</pubDate><guid>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="4460바이트짜리-세계"&gt;4,460바이트짜리 세계&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260803-8bit-hardware-limits-01/"&gt;Part 1&lt;/a&gt;에서 확인한 제약을 다시 꺼내 놓겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;슈퍼 마리오브라더스 카트리지는 PRG-ROM 32KB + CHR-ROM 8KB = &lt;strong&gt;40KB&lt;/strong&gt; 입니다.&lt;/li&gt;
&lt;li&gt;한 프레임 중 VRAM 을 만질 수 있는 시간은 2,273 사이클이고, 스프라이트 DMA 를 빼면 배경에 쓸 수 있는 것은 &lt;strong&gt;약 220바이트&lt;/strong&gt; 입니다.&lt;/li&gt;
&lt;li&gt;6502 에는 곱셈 명령이 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 세 줄을 지키면서 32개 스테이지가 흐르는 게임을 만들어야 합니다.&lt;/p&gt;</description></item><item><title>8비트의 벽을 넘은 게임들 Part 1: 그 기계들은 무엇을 못 했는가</title><link>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-01/</link><pubDate>Mon, 03 Aug 2026 09:06:15 +0900</pubDate><guid>https://philipjkim.cc/posts/20260803-8bit-hardware-limits-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="스크린샷-한-장보다-작은-게임"&gt;스크린샷 한 장보다 작은 게임&lt;/h2&gt;
&lt;p&gt;슈퍼 마리오브라더스가 담긴 카트리지의 ROM 용량은 &lt;strong&gt;40KB&lt;/strong&gt; 입니다. 프로그램 32KB, 그림 데이터 8KB. 이게 전부입니다.&lt;/p&gt;
&lt;p&gt;이 게임의 한 장면을 오늘날의 화면에서 캡처해 PNG 로 저장하면 보통 수백 KB 가 나옵니다. &lt;strong&gt;게임 하나가 그 게임의 스크린샷 한 장보다 작습니다.&lt;/strong&gt; 32개 스테이지, 여덟 가지 배경음악, 마리오의 물리 엔진, 적 AI, 점수 계산, 타이틀 화면이 전부 저 40KB 안에 있습니다.&lt;/p&gt;</description></item><item><title>렉시오 CPU 플레이어 만들기 Part 4: 수 평가와 정산 기대값</title><link>https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-04/</link><pubDate>Fri, 31 Jul 2026 09:44:26 +0900</pubDate><guid>https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="재료에서-결정으로"&gt;재료에서 결정으로&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-02/"&gt;Part 2&lt;/a&gt; 에서 봇이 무엇을 아는지, &lt;a href="https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-03/"&gt;Part 3&lt;/a&gt; 에서 자기 손패를 어떻게 보는지 만들었습니다. 아직 결정은 하나도 하지 않았습니다. 이번 편에서 전부 합칩니다.&lt;/p&gt;
&lt;p&gt;그런데 합치기 전에 답해야 할 질문이 하나 있습니다. &lt;strong&gt;무엇을 최대화할 것인가.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;달무티에서는 이 질문이 쉬웠습니다. 등수가 곧 다음 판의 계급이고 점수도 등수에 비례하므로, &amp;ldquo;평균 등수를 낮춘다&amp;quot;가 그대로 목적 함수였습니다. 렉시오는 다릅니다. 렉시오에는 등수가 없습니다. &lt;strong&gt;금액만 있습니다.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>렉시오 CPU 플레이어 만들기 Part 3: 손패 분해 플랜</title><link>https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-03/</link><pubDate>Fri, 31 Jul 2026 09:37:44 +0900</pubDate><guid>https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="좋은-손패는-센-손패가-아니다"&gt;좋은 손패는 센 손패가 아니다&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-02/"&gt;Part 2&lt;/a&gt; 에서는 봇이 상대에 대해 무엇을 알 수 있는지 정리했습니다. 이번 편은 시선을 안으로 돌립니다. &lt;strong&gt;내 손패는 몇 번 제출하면 비는가.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 질문이 왜 중요한지는 &lt;a href="https://philipjkim.cc/posts/20260727-climbing-card-games-daifugo-dalmuti-lexio/"&gt;클라이밍 게임 전략 글&lt;/a&gt;에서 이미 짚었습니다. 강한 타일이 많은 손패와 조합이 잘 짜인 손패 중 이기는 쪽은 후자입니다. 강한 타일은 한 번에 한 장씩 나가지만, 잘 짜인 손패는 다섯 장씩 나갑니다.&lt;/p&gt;</description></item><item><title>렉시오 CPU 플레이어 만들기 Part 2: 60비트 카운팅과 확실승수 판정</title><link>https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-02/</link><pubDate>Fri, 31 Jul 2026 09:30:12 +0900</pubDate><guid>https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hard-봇이-지켜야-할-세-가지"&gt;hard 봇이 지켜야 할 세 가지&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-01/"&gt;Part 1&lt;/a&gt; 에서 만든 easy 봇은 이번 턴에 합법적인 가장 싼 수를 계산할 뿐이었습니다. 이번 편부터 hard 봇을 설계합니다. 다만 시작하기 전에 지켜야 할 선을 못 박아 두겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;공정성.&lt;/strong&gt; 봇은 &lt;code&gt;GameView&lt;/code&gt; 만 봅니다. 남의 손패는 그 구조체 어디에도 존재하지 않습니다. 봇이 강해지는 이유는 &lt;strong&gt;더 많이 보기 때문이 아니라, 같은 것을 보고 더 잘 세기 때문&lt;/strong&gt; 이어야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;결정성.&lt;/strong&gt; 같은 입력에서는 같은 출력이 나와야 합니다. 그래야 봇 로직이 테스트 가능한 코드가 됩니다. 난수를 쓴다면 시드를 게임 상태에서 유도합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실시간성.&lt;/strong&gt; 봇의 판단은 사람이 기다릴 수 있는 시간 안에 끝나야 합니다. 이 제약이 뒤에서 &amp;ldquo;몬테카를로를 런타임에 돌리지 않는다&amp;quot;는 설계 결정으로 이어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이번 편은 hard 봇이 &lt;strong&gt;무엇을 아는가&lt;/strong&gt; 를 다룹니다. 무엇을 결정하는가는 Part 3 과 Part 4 의 몫입니다.&lt;/p&gt;</description></item><item><title>렉시오 CPU 플레이어 만들기 Part 1: 타일 하나를 정수 하나로</title><link>https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-01/</link><pubDate>Fri, 31 Jul 2026 09:22:31 +0900</pubDate><guid>https://philipjkim.cc/posts/20260731-lexio-cpu-bot-strategy-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="사촌-게임-전혀-다른-봇"&gt;사촌 게임, 전혀 다른 봇&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-01/"&gt;어제 글&lt;/a&gt;에서 달무티(The Great Dalmuti)의 CPU 플레이어를 세 편에 걸쳐 설계했습니다. 이번에는 같은 클라이밍 계열의 국산 타일 게임 &lt;strong&gt;렉시오(Lexio)&lt;/strong&gt; 로 넘어갑니다.&lt;/p&gt;
&lt;p&gt;두 게임은 &lt;a href="https://philipjkim.cc/posts/20260727-climbing-card-games-daifugo-dalmuti-lexio/"&gt;클라이밍 게임을 정리한 글&lt;/a&gt;에서 봤듯 사촌 사이입니다. 더 센 조합으로 받아 올라가고, 손패를 먼저 비우는 쪽이 이깁니다. 그런데 봇을 짜 보면 &lt;strong&gt;알고리즘이 거의 겹치지 않습니다.&lt;/strong&gt; 달무티 봇 코드를 렉시오로 옮기려 하면 재사용할 수 있는 것은 &amp;ldquo;공정성 경계를 타입으로 못 박는다&amp;rdquo; 같은 뼈대뿐이고, 판단 로직은 전부 새로 써야 합니다.&lt;/p&gt;</description></item><item><title>달무티 CPU 플레이어 만들기 Part 3: 수 평가와 전략적 패스</title><link>https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-03/</link><pubDate>Thu, 30 Jul 2026 15:08:35 +0900</pubDate><guid>https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;정정 안내 (2026-08-03):&lt;/strong&gt; 이 글의 설계안을 실제로 구현해 시뮬레이션으로 검증한 결과, 리드 선택·세금 반환·목적 함수에 대한 일부 결론이 뒤집혔습니다. 특히 &amp;ldquo;리드 선택도 같은 계산이다&amp;rdquo; 절과 &amp;ldquo;rank 12 는 반환 카드로 나쁘다&amp;quot;는 주장은 철회되었습니다. 무엇이 왜 틀렸고 무엇이 살아남았는지는 &lt;a href="https://philipjkim.cc/posts/20260803-dalmuti-cpu-bot-strategy-04/"&gt;Part 4: 만들어 보니 hard 봇이 더 약했다&lt;/a&gt;에 정리했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="재료에서-결정으로"&gt;재료에서 결정으로&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-01/"&gt;Part 1&lt;/a&gt;에서 공정성 경계와 결정적 easy 봇을 만들었고, &lt;a href="https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-02/"&gt;Part 2&lt;/a&gt;에서 hard 봇의 정보 모델을 세웠습니다. 지금까지 확보한 재료는 셋입니다.&lt;/p&gt;</description></item><item><title>달무티 CPU 플레이어 만들기 Part 2: hard 봇의 정보 모델과 확실승수 판정</title><link>https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-02/</link><pubDate>Thu, 30 Jul 2026 14:18:37 +0900</pubDate><guid>https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="hard-봇이-지켜야-할-세-가지"&gt;hard 봇이 지켜야 할 세 가지&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-01/"&gt;Part 1&lt;/a&gt;에서는 달무티 CPU 플레이어의 뼈대를 잡고, 결정적 규칙만으로 움직이는 easy 봇을 만들었습니다. easy 봇은 합법적이고 예측 가능하지만, &amp;ldquo;낼 수 있으면 무조건 낸다&amp;quot;는 정책 때문에 사람에게 쉽게 집니다.&lt;/p&gt;
&lt;p&gt;이번 편부터 hard 봇을 설계합니다. 설계 제약은 세 가지입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 상대의 손패는 절대 볼 수 없습니다.&lt;/strong&gt; 봇은 Part 1 에서 정의한 &lt;code&gt;GameView&lt;/code&gt; 만 받습니다. 난이도를 올리기 위해 정보를 더 주는 것은 설계가 아니라 반칙입니다. 사람이 &amp;ldquo;저 봇 손패 보는 거 아니야?&amp;ldquo;라고 의심하는 순간 게임의 신뢰가 무너집니다.&lt;/p&gt;</description></item><item><title>달무티 CPU 플레이어 만들기 Part 1: 규칙 기반 easy 봇</title><link>https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-01/</link><pubDate>Thu, 30 Jul 2026 14:09:37 +0900</pubDate><guid>https://philipjkim.cc/posts/20260730-dalmuti-cpu-bot-strategy-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;후속 정정 안내 (2026-08-03):&lt;/strong&gt; 이 시리즈의 설계안을 실제로 구현해 시뮬레이션으로 검증한 결과, 여기서 &amp;ldquo;easy 봇의 단순화&amp;quot;로 소개한 &lt;strong&gt;리드 정책과 세금 반환 규칙이 hard 봇의 복잡한 평가식보다 강했고&lt;/strong&gt;, 지금은 hard 봇의 guardrail 로 채택돼 있습니다. 자세한 내용은 &lt;a href="https://philipjkim.cc/posts/20260803-dalmuti-cpu-bot-strategy-04/"&gt;Part 4: 만들어 보니 hard 봇이 더 약했다&lt;/a&gt;에 정리했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="왜-봇부터-만드는가"&gt;왜 봇부터 만드는가&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260727-climbing-card-games-daifugo-dalmuti-lexio/"&gt;지난 글&lt;/a&gt;에서 대부호·달무티·렉시오를 묶어 클라이밍 게임의 전략을 정리했습니다. 이번에는 그중 &lt;strong&gt;달무티(The Great Dalmuti)&lt;/strong&gt; 를 디지털 게임으로 구현한다고 가정하고, &lt;strong&gt;CPU 플레이어를 어떤 전략으로 움직일 것인가&lt;/strong&gt; 를 파고들겠습니다.&lt;/p&gt;</description></item><item><title>AI 허니문이 끝난 자리: 테크 인력 6,000명을 반으로 가른 것과, 개인이 바꿀 수 있는 것</title><link>https://philipjkim.cc/posts/20260729-ai-honeymoon-over-tech-workforce-split/</link><pubDate>Wed, 29 Jul 2026 13:30:11 +0900</pubDate><guid>https://philipjkim.cc/posts/20260729-ai-honeymoon-over-tech-workforce-split/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;이오플래닛에 소개된 &lt;a href="https://eopla.net/magazines/45020"&gt;AI 허니문은 끝났다: 테크 인력이 정확히 반으로 갈라졌다&lt;/a&gt; 를 읽고, 원 조사를 직접 찾아보았습니다. 출처는 노엄 시걸(Noam Segal)이 레니 라치츠키(Lenny Rachitsky)와 함께 진행한 &lt;strong&gt;두 번째 연례 테크 종사자 심리 조사&lt;/strong&gt; 입니다. 에어비앤비·트위터·메타·재피어·피그마 등에서 리서치를 이끌어 온 사람이 설계했고, 표본은 약 6,000명입니다.&lt;/p&gt;
&lt;p&gt;가장 많이 인용되는 문장은 이것입니다. &amp;ldquo;AI가 당신의 직업 정체성을 어떻게 바꿨습니까&amp;quot;라고 물었더니 절반은 &lt;strong&gt;&amp;ldquo;일이 다시 재미있어졌다&amp;rdquo;&lt;/strong&gt; 고 답했고, 나머지 절반에게서는 &lt;strong&gt;&amp;ldquo;내 뇌가 썩어 가는 것 같다&amp;rdquo;&lt;/strong&gt; 는 답이 돌아왔다는 것입니다.&lt;/p&gt;</description></item><item><title>번역글: Big Design Up Front — 여전히 나쁜 생각입니다</title><link>https://philipjkim.cc/posts/20260728-big-design-up-front-still-a-bad-idea-translation/</link><pubDate>Tue, 28 Jul 2026 14:23:37 +0900</pubDate><guid>https://philipjkim.cc/posts/20260728-big-design-up-front-still-a-bad-idea-translation/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 5 를 이용해 번역되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;원문: Pragmatic Programmers Newsletter — &amp;ldquo;Big Design Up Front: Still a Bad Idea&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;우리는 1990년대의 소프트웨어 위기를 불러왔던 그 관행을 그대로 되풀이할 위험에 처해 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;40년 전, 도구와 하드웨어가 발전하면서 우리는 점점 더 큰 시스템을 만들 수 있게 됐습니다. 더 야심 차고, 범위도 넓고, UI도 화려한 시스템들이었습니다. 짜릿한 시절이었습니다.&lt;/p&gt;
&lt;p&gt;하지만 문제가 있었습니다. 시스템이 복잡해질수록 프로젝트는 점점 더 실패했습니다. 일정 추정은 웃음이 나올 만큼 빗나갔고, 기능은 잘려나갔고, 버그는 넘쳐났습니다. 프로젝트는 납품되는 만큼이나 자주 취소됐습니다. 2000년 무렵에는 여섯 개 중 하나 정도만 일정과 예산에 근접하게 마무리됐습니다.&lt;/p&gt;</description></item><item><title>Marp: Markdown으로 발표 슬라이드를 만들고 Git으로 관리하기</title><link>https://philipjkim.cc/posts/20260723-marp-markdown-presentation/</link><pubDate>Thu, 23 Jul 2026 13:03:11 +0900</pubDate><guid>https://philipjkim.cc/posts/20260723-marp-markdown-presentation/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;발표 자료를 만들 때마다 PowerPoint나 Keynote를 열면서 &amp;ldquo;코드는 Git으로 관리하는데 슬라이드는 왜 바이너리 파일로 주고받아야 하지?&amp;ldquo;라는 생각을 해본 개발자라면, 이 글이 도움이 될 것입니다. 이미 README와 기술 문서를 Markdown으로 쓰고 있다면, 슬라이드도 같은 방식으로 만들 수 있습니다. 이 글에서는 &lt;strong&gt;Marp&lt;/strong&gt; (Markdown Presentation Ecosystem)을 소개합니다.&lt;/p&gt;
&lt;h2 id="marp란-무엇인가"&gt;Marp란 무엇인가&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://marp.app/"&gt;Marp&lt;/a&gt;은 Markdown 파일을 발표 슬라이드로 변환해주는 오픈소스 도구 모음입니다. 핵심 아이디어는 단순합니다:&lt;/p&gt;</description></item><item><title>번역글: 어느 사랑 이야기</title><link>https://philipjkim.cc/posts/20260720-a-love-story-translation/</link><pubDate>Mon, 20 Jul 2026 15:31:52 +0900</pubDate><guid>https://philipjkim.cc/posts/20260720-a-love-story-translation/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 번역되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;원문: Dave Thomas, Pragmatic Programmers Newsletter — &amp;ldquo;A Love Story&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;1990년대, Reddit도 Twitter도 Facebook도 나오기 전, 사실상 그 무엇도 존재하기 전에, 새로운 프로그래밍 언어를 찾아보는 곳은 comp.lang.misc 라는 Usenet 그룹이었습니다.&lt;/p&gt;
&lt;p&gt;사람들은 자신의 최신 창작물을 소스 코드 형태로 올렸습니다. 그것은 uuencode로 인코딩되어 여러 조각으로 나뉘어 있었습니다 (게시물에는 최대 크기 제한이 있었기 때문입니다). 어떤 언어를 써보고 싶으면, uuencode된 조각들을 전부 내려받아 이어 붙인 다음 디코딩해야 했습니다. 그러면 tar 파일이 나오고, 그것을 풀면 소스 코드가 나왔습니다. 그다음에는 &lt;code&gt;./configure&lt;/code&gt;를 돌아가게 만드는 즐거움이 기다리고 있었습니다.&lt;/p&gt;</description></item><item><title>HTTP REST(JSON) vs. gRPC(Protocol Buffers): 언제 무엇을 쓸까</title><link>https://philipjkim.cc/posts/20260716-http-rest-vs-grpc/</link><pubDate>Thu, 16 Jul 2026 17:33:09 +0900</pubDate><guid>https://philipjkim.cc/posts/20260716-http-rest-vs-grpc/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;백엔드 서버를 설계할 때 거의 항상 마주치는 갈림길이 있습니다. &amp;ldquo;이 API 를 JSON 기반 HTTP REST 로 열까, 아니면 gRPC 로 열까?&amp;rdquo; 하는 질문입니다. 팀 컨벤션에 따라 별생각 없이 한쪽으로 정해지는 경우도 많지만, 두 방식은 성능·개발 경험·운영 부담·클라이언트 호환성 등 여러 축에서 뚜렷하게 다른 트레이드오프를 가집니다.&lt;/p&gt;
&lt;p&gt;이 글은 웹 백엔드를 다루는 중급 이상 개발자를 대상으로, 두 방식의 작동 원리와 장단점을 정리하고 &lt;strong&gt;&amp;ldquo;어떤 상황에서 무엇을 고르는 것이 합리적인지&amp;rdquo;&lt;/strong&gt; 를 구체적인 예시와 함께 설명합니다. 결론부터 말하면 &amp;ldquo;무조건 하나가 낫다&amp;quot;는 답은 없으며, &lt;strong&gt;통신의 상대가 누구인지(외부 클라이언트 vs. 내부 서비스)&lt;/strong&gt; 가 선택의 가장 강력한 기준이 됩니다.&lt;/p&gt;</description></item><item><title>배우는 부분들로 이루어진 배우는 시스템 — Kent Beck × Jessica Kerr 대담 정리</title><link>https://philipjkim.cc/posts/20260626-symmathesy-kent-beck-jessica-kerr/</link><pubDate>Fri, 26 Jun 2026 09:56:51 +0900</pubDate><guid>https://philipjkim.cc/posts/20260626-symmathesy-kent-beck-jessica-kerr/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;이 좋은 리소스를 귀띔해 주신 Luis 님께 감사드립니다. 덕분에 &amp;lsquo;symmathesy&amp;rsquo; 를 몇 번이나 소리 내어 발음해 봤는지 모릅니다 — 혀는 여전히 꼬이지만, 개념만큼은 또렷하게 남았습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Kent Beck 이 진행하는 팟캐스트 &lt;a href="https://newsletter.kentbeck.com/p/a-learning-system-made-of-learning"&gt;&lt;em&gt;Still Burning&lt;/em&gt;&lt;/a&gt; 의 일곱 번째 에피소드 &lt;a href="https://www.youtube.com/watch?v=e9TABQqiDXU"&gt;&lt;em&gt;A Learning System Made of Learning Parts&lt;/em&gt;&lt;/a&gt; 는, &lt;em&gt;&amp;ldquo;여전히 마음을 쓰고(still care) 여전히 무언가를 하고 있는 괴짜들&amp;rdquo;&lt;/em&gt; 을 위한 자리라는 이 시리즈의 취지에 꼭 맞는 대담입니다. 손님은 Kent 의 오랜 친구이자 시스템 사고(systems thinking)로 잘 알려진 개발자 &lt;strong&gt;Jessica Kerr&lt;/strong&gt;(jessitron) 입니다.&lt;/p&gt;</description></item><item><title>번역글: 아웃소싱, AI, 그리고 작은 희망</title><link>https://philipjkim.cc/posts/20260625-outsourcing-ai-and-a-little-hope-translation/</link><pubDate>Thu, 25 Jun 2026 07:45:43 +0900</pubDate><guid>https://philipjkim.cc/posts/20260625-outsourcing-ai-and-a-little-hope-translation/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 번역되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;원문: Dave Thomas, Pragmatic Programmers Newsletter — &amp;ldquo;Outsourcing, AI, and a Little Hope&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;1990년대와 2000년대에 이 업계에 몸담았다면, 아웃소싱을 둘러싼 그 소동을 잘 알고 계실 겁니다.&lt;/p&gt;
&lt;p&gt;기업들은 미국과 서유럽 바깥의 개발자들에게 대체로 더 적은 임금을 준다는 사실을 깨달았습니다. 그리고 개발자란 그저 자원에 불과하니, 개발 업무를 해외로 보내버리면 돈을 왕창 아낄 수 있다고 생각했습니다.&lt;/p&gt;
&lt;p&gt;한동안 미국과 유럽의 개발자들은 공황에 빠졌습니다. &amp;ldquo;저 멀리 어퍼 호피스탄(Upper Hopistan)에서 하루 1.5달러를 받는 어떤 코더에게 내 일자리를 빼앗기겠구나.&amp;rdquo;&lt;/p&gt;</description></item><item><title>2021년의 Go를 2026년의 Go로 Part 3: 동시성·런타임·운영 레시피</title><link>https://philipjkim.cc/posts/20260624-go-2021-to-2026-recipes-03/</link><pubDate>Wed, 24 Jun 2026 10:15:25 +0900</pubDate><guid>https://philipjkim.cc/posts/20260624-go-2021-to-2026-recipes-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;&lt;a href="../20260624-go-2021-to-2026-recipes-01/"&gt;Part 1&lt;/a&gt;에서 언어와 문법을, &lt;a href="../20260624-go-2021-to-2026-recipes-02/"&gt;Part 2&lt;/a&gt;에서 표준 라이브러리를 다뤘습니다. 시리즈의 마지막인 Part 3은 &lt;strong&gt;동시성·런타임·툴체인·운영&lt;/strong&gt; 입니다. 코드 한 줄의 변화라기보다, &amp;ldquo;서비스를 어떻게 돌리고 어떻게 검증하느냐&amp;quot;에 가까운 영역입니다.&lt;/p&gt;
&lt;p&gt;이 영역의 변화는 특히 컨테이너/쿠버네티스 환경에서 Go 서비스를 운영하는 팀에게 의미가 큽니다. 2021년에는 서드파티 라이브러리나 수작업으로 메우던 빈틈을, 이제 표준 런타임이 채워 줍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="레시피-1-수동-취소타임아웃-관리-대신-context-신규-api-121"&gt;레시피 1: 수동 취소·타임아웃 관리 대신 &lt;code&gt;context&lt;/code&gt; 신규 API (1.21)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;context&lt;/code&gt; 는 1.7부터 있었지만, 실무에서 반복되던 몇 가지 패턴을 표준으로 흡수한 것은 1.21입니다.&lt;/p&gt;</description></item><item><title>2021년의 Go를 2026년의 Go로 Part 2: 표준 라이브러리 레시피</title><link>https://philipjkim.cc/posts/20260624-go-2021-to-2026-recipes-02/</link><pubDate>Wed, 24 Jun 2026 10:13:33 +0900</pubDate><guid>https://philipjkim.cc/posts/20260624-go-2021-to-2026-recipes-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;&lt;a href="../20260624-go-2021-to-2026-recipes-01/"&gt;Part 1&lt;/a&gt;에서는 제네릭, 이터레이터, 빌트인 등 언어와 문법 레벨의 변화를 다뤘습니다. Part 2는 일상 코드에서 가장 손이 자주 가는 &lt;strong&gt;표준 라이브러리&lt;/strong&gt; 의 레시피입니다.&lt;/p&gt;
&lt;p&gt;2021~22년의 Go 표준 라이브러리는 의외로 빈약했습니다. 슬라이스에서 원소를 찾으려면 &lt;code&gt;for&lt;/code&gt; 루프를 직접 돌렸고, 맵의 키를 모으려면 손으로 슬라이스를 채웠으며, 정렬은 &lt;code&gt;sort.Slice&lt;/code&gt; 에 비교 함수를 넘겼습니다. 구조적 로깅을 하려면 &lt;code&gt;logrus&lt;/code&gt;, &lt;code&gt;zap&lt;/code&gt; 같은 서드파티가 사실상 필수였고, HTTP 라우팅에서 메서드와 경로 파라미터를 다루려면 &lt;code&gt;gorilla/mux&lt;/code&gt; 를 깔았습니다.&lt;/p&gt;</description></item><item><title>2021년의 Go를 2026년의 Go로 Part 1: 언어와 문법 레시피</title><link>https://philipjkim.cc/posts/20260624-go-2021-to-2026-recipes-01/</link><pubDate>Wed, 24 Jun 2026 10:11:05 +0900</pubDate><guid>https://philipjkim.cc/posts/20260624-go-2021-to-2026-recipes-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며-5년-동안-go에-무슨-일이-있었나"&gt;들어가며: 5년 동안 Go에 무슨 일이 있었나&lt;/h2&gt;
&lt;p&gt;2021~22년에 Go로 서비스를 짜던 시절을 떠올려 봅니다. 그때 우리가 쓰던 버전은 대략 Go 1.16~1.18 언저리였습니다. 제네릭이 없거나(1.18 이전), 갓 도입되어 아직 손에 익지 않았고, &lt;code&gt;interface{}&lt;/code&gt; 와 &lt;code&gt;sort.Slice&lt;/code&gt;, &lt;code&gt;for _, v := range xs { v := v; ... }&lt;/code&gt; 같은 관용구가 일상이었습니다.&lt;/p&gt;
&lt;p&gt;그로부터 8개의 메이저 릴리스(1.19부터 1.26까지)가 지났습니다. 2026년 6월 현재 stable 버전은 &lt;strong&gt;2026년 2월에 나온 Go 1.26&lt;/strong&gt; 입니다. 이 5년 사이에 언어 자체, 표준 라이브러리, 런타임, 툴체인이 모두 크게 달라졌습니다. 문제는 코드베이스가 그만큼 따라가지 못한다는 점입니다. 많은 프로덕션 코드가 여전히 &amp;ldquo;그때 그렇게 짜던 방식&amp;quot;으로 멈춰 있습니다.&lt;/p&gt;</description></item><item><title>개발자를 위한 Discord 완전정복 Part 2: 공짜로 풀어준 대인배 기능들</title><link>https://philipjkim.cc/posts/20260605-discord-developer-guide-02/</link><pubDate>Fri, 05 Jun 2026 18:07:55 +0900</pubDate><guid>https://philipjkim.cc/posts/20260605-discord-developer-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260605-discord-developer-guide-01/"&gt;Part 1&lt;/a&gt;에서는 Discord가 어떻게 태어나 성장했고 어떤 결정으로 지금의 모습이 되었는지를 살펴봤습니다. 이번 Part 2에서는 &lt;strong&gt;개발자 입장에서 Discord가 얼마나 대인배인지&lt;/strong&gt; 를 이야기합니다.&lt;/p&gt;
&lt;p&gt;결론부터 말하면, &lt;strong&gt;API·봇·Webhook·Gateway·Interactions·OAuth2·임베드·CDN 첨부·음성 채널·스레드·포럼 채널이 전부 무료&lt;/strong&gt;입니다. 서버 크기 제한도 없습니다. 흔히 보이는 유료 구독 &lt;strong&gt;Nitro는 개인 사용자(엔드유저) 대상&lt;/strong&gt;이지 개발자 기능이 아닙니다. 봇을 만들고 운영하는 데 Discord에 내는 비용은 0원이며, 드는 비용이 있다면 봇을 돌릴 본인 호스팅 비용뿐입니다.&lt;/p&gt;</description></item><item><title>개발자를 위한 Discord 완전정복 Part 1: 탄생, 성장, 그리고 결정적 순간들</title><link>https://philipjkim.cc/posts/20260605-discord-developer-guide-01/</link><pubDate>Fri, 05 Jun 2026 18:05:49 +0900</pubDate><guid>https://philipjkim.cc/posts/20260605-discord-developer-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;개발자라면 한 번쯤은 Discord 서버에 들어가 본 적이 있을 것입니다. 오픈소스 프로젝트의 커뮤니티, 사내 봇 알림 채널, 혹은 그냥 친구들과 게임하며 떠드는 음성 채널까지. 어느새 Discord는 개발자 생태계의 기본 인프라 중 하나가 되었습니다.&lt;/p&gt;
&lt;p&gt;이 시리즈는 Discord라는 서비스를 &lt;strong&gt;개발자의 시선으로&lt;/strong&gt; 뜯어봅니다. 3부작으로 구성했습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1 (이 글)&lt;/strong&gt;: Discord가 어떤 니즈에서 태어났고, 어떻게 성장했으며, 서비스를 지금의 모습으로 만든 결정적 의사결정들을 살펴봅니다. 마지막엔 개발자가 특히 주목할 만한 엔지니어링 의사결정(Elixir, Go→Rust, Cassandra→ScyllaDB)을 다룹니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2&lt;/strong&gt;: Webhook, 봇, 슬래시 커맨드 같은 Discord의 &lt;strong&gt;개발자 친화적이고 대인배스러운 무료 기능들&lt;/strong&gt;을 실전 관점에서 정리합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3&lt;/strong&gt;: Discord를 원격 control plane으로 활용하는 방법 — AI 코딩 에이전트 원격 제어, ChatOps, 홈랩 모니터링 등 — 과 그 best practice를 다룹니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그럼 1편을 시작합니다.&lt;/p&gt;</description></item><item><title>Agentic Coding 시대의 Repo 부트스트래핑 가이드 Part 2: Go + Templ + HTMX To-Do 앱으로 직접 만들어 보기</title><link>https://philipjkim.cc/posts/20260528-repo-bootstrapping-guide-02/</link><pubDate>Thu, 28 May 2026 19:03:00 +0900</pubDate><guid>https://philipjkim.cc/posts/20260528-repo-bootstrapping-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.7 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="../20260528-repo-bootstrapping-guide-01/"&gt;Part 1&lt;/a&gt; 에서는 2026년 5월 기준 de-facto 가 된 spec 문서들의 지형도와 best practices 를 정리했습니다. 2편에서는 가상의 Go + Templ + HTMX 기반 To-Do 앱 &lt;code&gt;gotodo&lt;/code&gt; 를 새로 부트스트래핑한다고 가정하고, 각 문서를 실제로 어떻게 채우는지 발췌 형태로 따라갑니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="가상-프로젝트-gotodo"&gt;가상 프로젝트: &lt;code&gt;gotodo&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;스택은 다음과 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Go 1.23&lt;/strong&gt; — 표준 라이브러리 중심&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://templ.guide"&gt;templ&lt;/a&gt;&lt;/strong&gt; — Go 네이티브 SSR 템플릿 (type-safe)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://htmx.org"&gt;HTMX 2.0&lt;/a&gt;&lt;/strong&gt; — 클라이언트 JS 최소화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQLite + &lt;code&gt;modernc.org/sqlite&lt;/code&gt;&lt;/strong&gt; — CGO 없는 순수 Go 드라이버&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;목표는 단순합니다. 할 일 추가/완료/삭제만 되는 SSR 미니멀 앱. 1인 로컬 사용을 가정합니다.&lt;/p&gt;</description></item><item><title>Agentic Coding 시대의 Repo 부트스트래핑 가이드 Part 1: AGENTS.md 와 spec 문서 지형도</title><link>https://philipjkim.cc/posts/20260528-repo-bootstrapping-guide-01/</link><pubDate>Thu, 28 May 2026 19:02:00 +0900</pubDate><guid>https://philipjkim.cc/posts/20260528-repo-bootstrapping-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.7 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며-readme-한-장으로-안-되는-시대"&gt;들어가며: README 한 장으로 안 되는 시대&lt;/h2&gt;
&lt;p&gt;신규 서비스를 위한 git repo 를 막 만들었다고 합시다. 2년 전이라면 &lt;code&gt;README.md&lt;/code&gt; 한 장에 빌드 명령과 디렉터리 구조를 적고, 나머지는 코드와 머릿속에서 합의된 관행으로 굴러갔습니다. 새 팀원이 들어오면 옆 자리 동료가 30분쯤 설명해 주고, 그걸로 충분했습니다.&lt;/p&gt;
&lt;p&gt;지금은 그 &amp;ldquo;옆 자리 동료&amp;rdquo; 역할의 절반 이상을 Claude Code, Codex CLI, Gemini CLI, Cursor 같은 에이전트가 맡고 있습니다. 그런데 에이전트는 옆 자리에 앉지 않습니다. 매 세션마다 처음 합류한 신입처럼 repo 에 들어와서, 우리가 적어 둔 텍스트만 읽고 일을 시작합니다. &lt;strong&gt;에이전트에게 &amp;ldquo;이 프로젝트는 이런 곳이고, 이런 규칙으로 일한다&amp;rdquo; 를 전달하는 문서가 부실하면, 에이전트가 만드는 결과물의 품질은 운에 좌우됩니다.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Superpowers 플러그인 가이드 Part 4: 명시 호출 vs 자동 트리거 — 실전 가이드와 한계</title><link>https://philipjkim.cc/posts/20260520-superpowers-plugin-guide-04/</link><pubDate>Wed, 20 May 2026 16:58:00 +0900</pubDate><guid>https://philipjkim.cc/posts/20260520-superpowers-plugin-guide-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.7 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="../20260520-superpowers-plugin-guide-01/"&gt;1편&lt;/a&gt; 에서 개요와 설치를, &lt;a href="../20260520-superpowers-plugin-guide-02/"&gt;2편&lt;/a&gt; 에서 핵심 워크플로우를, &lt;a href="../20260520-superpowers-plugin-guide-03/"&gt;3편&lt;/a&gt; 에서 품질 보증 스킬을 다뤘습니다. 마지막 4편에서는 실전에서 가장 자주 부딪히는 질문에 답합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;이거 그냥 두면 알아서 다 해주는 건가요, 아니면 매번 &lt;code&gt;/superpowers:xxx&lt;/code&gt; 로 호출해야 하나요?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;답은 &lt;strong&gt;&amp;ldquo;대부분 자동이지만, 의도적으로 명시 호출해야 더 잘 작동하는 케이스가 있다&amp;rdquo;&lt;/strong&gt; 입니다. 이 글에서는 두 모드의 비교 분석, 트러블슈팅, 커스터마이즈 방법, 그리고 Java/Go 백엔드 개발 현장에서의 실용 팁과 한계를 정리합니다.&lt;/p&gt;</description></item><item><title>Superpowers 플러그인 가이드 Part 3: 품질 보증 스킬 — TDD, Systematic Debugging, Verification, Code Review</title><link>https://philipjkim.cc/posts/20260520-superpowers-plugin-guide-03/</link><pubDate>Wed, 20 May 2026 16:57:00 +0900</pubDate><guid>https://philipjkim.cc/posts/20260520-superpowers-plugin-guide-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.7 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="../20260520-superpowers-plugin-guide-01/"&gt;1편&lt;/a&gt; 에서 Superpowers 의 개요와 설치를, &lt;a href="../20260520-superpowers-plugin-guide-02/"&gt;2편&lt;/a&gt; 에서 핵심 워크플로우를 다뤘습니다. 3편은 그 워크플로우 안에서 작동하면서 &lt;strong&gt;에이전트의 합리화와 빠져나갈 구멍을 차단&lt;/strong&gt; 하는 품질 보증 스킬들을 자세히 들여다봅니다.&lt;/p&gt;
&lt;p&gt;이 스킬들은 모두 &amp;ldquo;Iron Law&amp;rdquo; 라는 이름의 명문화된 규칙을 갖고 있다는 공통점이 있습니다. 그리고 각 스킬의 문서 안에는 &lt;strong&gt;&amp;ldquo;Red Flags&amp;rdquo; 표&lt;/strong&gt; 가 들어 있어, 에이전트가 빠져나가려 할 때 흔히 하는 합리화의 패턴들을 그대로 박아두었습니다. 이게 결과적으로 모델의 행동을 바꿉니다.&lt;/p&gt;</description></item><item><title>Superpowers 플러그인 가이드 Part 2: 핵심 워크플로우 — Brainstorming부터 Subagent-driven Development까지</title><link>https://philipjkim.cc/posts/20260520-superpowers-plugin-guide-02/</link><pubDate>Wed, 20 May 2026 16:56:00 +0900</pubDate><guid>https://philipjkim.cc/posts/20260520-superpowers-plugin-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.7 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="../20260520-superpowers-plugin-guide-01/"&gt;1편&lt;/a&gt; 에서는 Superpowers 가 무엇이고 어떻게 자동 트리거되는지를 다뤘습니다. 2편은 가장 핵심이라고 할 수 있는 &lt;strong&gt;개발 워크플로우 전체&lt;/strong&gt; 를 깊게 들여다봅니다.&lt;/p&gt;
&lt;p&gt;전체 흐름은 다음과 같습니다.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart LR
 A[brainstorming] --&amp;gt; B[using-git-worktrees]
 B --&amp;gt; C[writing-plans]
 C --&amp;gt; D{독립 태스크?}
 D --&amp;gt;|yes| E[subagent-driven-development]
 D --&amp;gt;|no/사람 체크포인트 선호| F[executing-plans]
 E --&amp;gt; G[finishing-a-development-branch]
 F --&amp;gt; G

 style A fill:#90EE90,color:#000000
 style B fill:#87CEEB,color:#000000
 style C fill:#FFD700,color:#000000
 style D fill:#FFB6C1,color:#000000
 style E fill:#DDA0DD,color:#000000
 style F fill:#DDA0DD,color:#000000
 style G fill:#FFA07A,color:#000000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 글에서는 각 스킬이 실제로 무엇을 하는지, 그리고 &lt;strong&gt;Go 기반 백엔드 서비스에 새 API 엔드포인트를 추가&lt;/strong&gt; 하는 시나리오를 따라가며 흐름을 체험해 봅니다.&lt;/p&gt;</description></item><item><title>Superpowers 플러그인 가이드 Part 1: 소개, 설치, 그리고 철학</title><link>https://philipjkim.cc/posts/20260520-superpowers-plugin-guide-01/</link><pubDate>Wed, 20 May 2026 16:55:00 +0900</pubDate><guid>https://philipjkim.cc/posts/20260520-superpowers-plugin-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.7 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;Claude Code, Codex CLI, Cursor 등 코딩 에이전트(coding agent)를 매일 쓰는 백엔드 개발자라면 한 번쯤은 이런 경험이 있을 겁니다. &amp;ldquo;간단한 기능 하나만 추가해줘&amp;rdquo; 라고 했더니, 요구사항 정리 없이 코드를 우다다 쏟아내다가 절반쯤에서 길을 잃고, 테스트도 빠진 채 &amp;ldquo;완료했습니다&amp;rdquo; 라고 보고하는 경우 말입니다. 모델은 똑똑한데, 작업 방식이 들쭉날쭉해서 결과의 품질이 운에 좌우됩니다.&lt;/p&gt;
&lt;p&gt;Jesse Vincent 가 만든 &lt;strong&gt;&lt;a href="https://github.com/obra/superpowers"&gt;Superpowers&lt;/a&gt;&lt;/strong&gt; 는 바로 이 지점을 노린 플러그인입니다. 모델을 더 똑똑하게 만드는 게 아니라, 모델이 &lt;strong&gt;언제, 어떤 순서로, 어떤 규율로&lt;/strong&gt; 일해야 하는지를 강제하는 &amp;ldquo;방법론(methodology)&amp;rdquo; 자체를 플러그인 형태로 주입합니다.&lt;/p&gt;</description></item><item><title>Agentic Coding 시대의 개발방법론 — 더 중요해진 5가지, 무용해진 5가지</title><link>https://philipjkim.cc/posts/20260428-agentic-coding-methodologies-survived-and-faded/</link><pubDate>Tue, 28 Apr 2026 12:40:28 +0900</pubDate><guid>https://philipjkim.cc/posts/20260428-agentic-coding-methodologies-survived-and-faded/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.7 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며-사라진-것이-아니라-재배치되었다"&gt;들어가며: 사라진 것이 아니라 재배치되었다&lt;/h2&gt;
&lt;p&gt;2025년 한 해를 거치면서 백엔드 개발자가 키보드 앞에서 보내는 시간의 모양이 크게 달라졌습니다. Claude Code, Cursor, Codex 같은 에이전트가 대부분의 코드를 직접 작성하고, 사람은 명세를 다듬고, 결과를 검증하고, 다음 작업을 지시하는 시간이 길어졌습니다. Stack Overflow의 &lt;a href="https://stackoverflow.blog/2025/12/29/developers-remain-willing-but-reluctant-to-use-ai-the-2025-developer-survey-results-are-here/"&gt;2025 Developer Survey&lt;/a&gt;는 응답자의 66%가 &lt;em&gt;almost-right AI code&lt;/em&gt; 를 고치는 데 더 많은 시간을 쓰고 있다고 답했습니다.&lt;/p&gt;
&lt;p&gt;이 변화 앞에서 가장 자주 듣는 잘못된 결론이 &lt;em&gt;&amp;ldquo;이제 SW 엔지니어링 방법론은 다 의미가 없어졌다&amp;rdquo;&lt;/em&gt; 입니다. 하지만 2025년 하반기에서 2026년 초까지 Kent Beck, Birgitta Böckeler, Simon Willison, Armin Ronacher, Steve Yegge처럼 실제로 에이전트와 매일 일하는 시니어 엔지니어들이 남긴 글을 읽어 보면 정반대 그림이 보입니다. &lt;strong&gt;방법론이 사라진 것이 아니라, 어떤 것은 더 중요해지고 어떤 것은 의미를 잃는 형태로 재배치되었습니다&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>2년 후 프로그래밍은 어떤 모습일까 — 아무도 모릅니다</title><link>https://philipjkim.cc/posts/20260410-nobody-knows-programming-in-two-years/</link><pubDate>Fri, 10 Apr 2026 12:58:02 +0900</pubDate><guid>https://philipjkim.cc/posts/20260410-nobody-knows-programming-in-two-years/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;이 글은 아래 두 편의 글을 재구성한 것입니다:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Charles Humble, &lt;a href="https://leaddev.com/ai/nobody-knows-what-programming-will-look-like-in-two-years"&gt;Nobody knows what programming will look like in two years&lt;/a&gt; (LeadDev, 2026.02.18)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Tom Johnson, &lt;a href="https://idratherbewriting.com/blog/nobody-knows-two-years-from-now"&gt;Nobody knows what it will look like in 2 years&lt;/a&gt; (I&amp;rsquo;d Rather Be Writing, 2026.03.03)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="업계를-떠나야-할까요"&gt;&amp;ldquo;업계를 떠나야 할까요?&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;지난 2년간 개발자 멘토링과 컨퍼런스를 다니며, 프로그래머이자 작가이자 작곡가인 Charles Humble은 이전 30년간 한 번도 들어본 적 없는 질문을 반복적으로 듣게 되었습니다.&lt;/p&gt;</description></item><item><title>번역글: AI와 함께 더 빨리 실패하기</title><link>https://philipjkim.cc/posts/20260408-failing-faster-with-ai-translation/</link><pubDate>Wed, 08 Apr 2026 14:39:54 +0900</pubDate><guid>https://philipjkim.cc/posts/20260408-failing-faster-with-ai-translation/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 번역되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;원문: Dave Thomas, Pragmatic Programmers Newsletter — &amp;ldquo;Failing Faster With AI&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;모든 거대한 컴퓨팅 재앙은 너무 많은 아이디어를 한곳에 집어넣으려다 생겨났습니다.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;— Gordon Bell&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;지난 8년쯤 개인 프로젝트를 하나 진행해 왔습니다. 1970년대 중반의 오래된 PIC 언어를 구현하는 것으로 시작했습니다. 셀 수 없이 많은 반복을 거쳤는데, 순수한 PIC 언어로 출발해서 오늘날에는 프로토타입 기반 스크립팅 언어와 애니메이션 기능을 내장한 형태가 되었습니다.&lt;/p&gt;</description></item><item><title>크로스플랫폼 To-Do 앱 만들기 Part 4: UI 다듬기와 빌드/배포</title><link>https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-04/</link><pubDate>Wed, 01 Apr 2026 09:12:52 +0900</pubDate><guid>https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="시리즈-목차"&gt;시리즈 목차&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Part 1: Flutter 소개와 개발 환경 설정&lt;/li&gt;
&lt;li&gt;Part 2: 기본 UI 구성과 상태 관리&lt;/li&gt;
&lt;li&gt;Part 3: 로컬 저장소 연동과 CRUD 완성&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 4: UI 다듬기와 빌드/배포 (이번 글)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="part-3-회고"&gt;Part 3 회고&lt;/h2&gt;
&lt;p&gt;Part 3까지 진행하면서 할 일의 추가, 수정, 삭제, 완료 토글 기능과 로컬 저장소 연동까지 모두 구현했습니다. 기능적으로는 완성된 상태이지만, 실제로 사용자에게 배포하려면 몇 가지 더 다듬어야 할 부분이 있습니다.&lt;/p&gt;</description></item><item><title>크로스플랫폼 To-Do 앱 만들기 Part 3: 상태 관리와 로컬 데이터 저장</title><link>https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-03/</link><pubDate>Wed, 01 Apr 2026 09:11:52 +0900</pubDate><guid>https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-02/"&gt;Part 2&lt;/a&gt;에서는 Flutter 위젯을 활용해 To-Do 앱의 UI를 구성했습니다. 할 일을 추가하고, 완료 처리하고, 삭제하는 기본 기능까지 구현했는데요. 한 가지 치명적인 문제가 있었습니다. &lt;strong&gt;앱을 종료했다가 다시 실행하면 모든 데이터가 사라진다&lt;/strong&gt; 는 점입니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;setState&lt;/code&gt;로 관리하던 &lt;code&gt;List&amp;lt;Todo&amp;gt;&lt;/code&gt; 는 메모리에만 존재하기 때문입니다. 이번 Part 3에서는 &lt;strong&gt;Riverpod&lt;/strong&gt; 을 사용한 상태 관리 리팩토링과 &lt;strong&gt;sqflite&lt;/strong&gt; 를 이용한 로컬 데이터 영구 저장을 구현합니다.&lt;/p&gt;
&lt;h2 id="1-setstate의-한계"&gt;1. setState의 한계&lt;/h2&gt;
&lt;p&gt;Part 2에서 사용한 &lt;code&gt;setState&lt;/code&gt;는 간단한 프로토타입에는 충분하지만, 앱 규모가 커지면 여러 문제가 생깁니다.&lt;/p&gt;</description></item><item><title>크로스플랫폼 To-Do 앱 만들기 Part 2: Flutter 기초와 UI 구현</title><link>https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-02/</link><pubDate>Wed, 01 Apr 2026 09:10:52 +0900</pubDate><guid>https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-01/"&gt;Part 1&lt;/a&gt;에서 Flutter SDK 설치, 에디터 설정, 에뮬레이터 구성 등 개발 환경을 갖추었습니다. 이제 본격적으로 코드를 작성할 차례입니다.&lt;/p&gt;
&lt;p&gt;이번 Part 2에서는 Dart 언어의 핵심 문법을 빠르게 훑어본 뒤, Flutter의 위젯 시스템을 이해하고, 실제로 동작하는 To-Do 리스트 앱의 UI를 구현합니다. 이 글을 따라 하면 할 일을 추가하고, 완료 표시하고, 삭제하는 기본 기능이 모두 갖춰진 앱을 만들 수 있습니다.&lt;/p&gt;</description></item><item><title>크로스플랫폼 To-Do 앱 만들기 Part 1: 프레임워크 선택과 개발 환경 설정</title><link>https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-01/</link><pubDate>Wed, 01 Apr 2026 09:09:52 +0900</pubDate><guid>https://philipjkim.cc/posts/20260401-flutter-todo-app-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="시리즈-소개"&gt;시리즈 소개&lt;/h2&gt;
&lt;p&gt;이 시리즈에서는 &lt;strong&gt;Flutter&lt;/strong&gt; 를 사용하여 크로스플랫폼 To-Do 앱을 처음부터 끝까지 만들어 봅니다. 하나의 코드베이스로 Android, iOS, 그리고 웹까지 동작하는 앱을 완성하는 것이 목표입니다.&lt;/p&gt;
&lt;p&gt;시리즈는 총 4개의 파트로 구성됩니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Part 1 (이번 글)&lt;/strong&gt; : 프레임워크 선택과 개발 환경 설정&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2&lt;/strong&gt;: UI 구성과 기본 기능 구현&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3&lt;/strong&gt;: 상태 관리와 로컬 데이터 저장&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 4&lt;/strong&gt;: 마무리, 테스트, 빌드 및 배포&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;각 파트는 실습 중심으로 진행됩니다. 따라해 보시면서 자연스럽게 Flutter 개발의 흐름을 익히실 수 있습니다.&lt;/p&gt;</description></item><item><title>번역글: 공중누각 — 여전히 보람찬 일</title><link>https://philipjkim.cc/posts/20260326-castles-in-the-air-translation/</link><pubDate>Thu, 26 Mar 2026 15:05:36 +0900</pubDate><guid>https://philipjkim.cc/posts/20260326-castles-in-the-air-translation/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 번역되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;원문: Dave Thomas, &lt;a href="https://media.pragprog.com/newsletters/2025-03-25.html"&gt;Pragmatic Programmers Newsletter&lt;/a&gt; — &amp;ldquo;Castles in the Air: It&amp;rsquo;s Still Just as Rewarding&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;마지막으로, 이토록 다루기 쉬운 매체에서 일하는 기쁨이 있습니다. 프로그래머는 시인과 마찬가지로, 순수한 사고의 재료에서 겨우 한 발짝 떨어진 곳에서 일합니다. 그는 공중에, 공기로 누각을 짓습니다. 상상력을 발휘하여 창조하는 것입니다. 이처럼 유연하고, 다듬고 고쳐 쓰기 쉬우며, 웅장한 개념적 구조물을 실현하는 데 이토록 적합한 창작 매체는 거의 없습니다.&lt;/p&gt;</description></item><item><title>AI 에이전트 시대의 C 언어 Part 2: AI는 C를 살리는가, 대체하는가</title><link>https://philipjkim.cc/posts/20260320-c-language-in-ai-agent-era-02/</link><pubDate>Fri, 20 Mar 2026 14:09:55 +0900</pubDate><guid>https://philipjkim.cc/posts/20260320-c-language-in-ai-agent-era-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260320-c-language-in-ai-agent-era-01/"&gt;Part 1&lt;/a&gt;에서는 AI 에이전트 등장 이전에 이미 진행되고 있던 C 언어의 위상 변화를 살펴보았습니다. 메모리 안전성 논쟁, 정부 권고, 주요 프로젝트의 탈C 마이그레이션이 그 핵심이었습니다.&lt;/p&gt;
&lt;p&gt;Part 2에서는 &lt;strong&gt;AI 코딩 에이전트&lt;/strong&gt; 가 이 흐름에 어떤 영향을 미치고 있는지 분석합니다. 결론부터 말하면, AI는 C를 살리는 동시에 대체하고 있습니다. 이 역설적인 &lt;strong&gt;이중 현상&lt;/strong&gt; 이 이 글의 핵심 주제입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-ai가-c-개발을-돕는-방식-더-안전한-c"&gt;1. AI가 C 개발을 돕는 방식: &amp;ldquo;더 안전한 C&amp;rdquo;&lt;/h2&gt;
&lt;h3 id="linux-커널의-ai-코드-리뷰"&gt;Linux 커널의 AI 코드 리뷰&lt;/h3&gt;
&lt;p&gt;세계 최대 C 코드베이스인 Linux 커널에서도 AI 도구가 활용되기 시작했습니다. Btrfs 개발자 Chris Mason은 2026년 초 &lt;strong&gt;review-prompts&lt;/strong&gt; 프로젝트를 시작했습니다. Claude Code 등 AI 도구를 활용해 커널 패치를 리뷰하는 프롬프트 모음으로, 대형 diff를 작은 단위로 분할해 C 특유의 문제점을 효과적으로 잡아냅니다.&lt;/p&gt;</description></item><item><title>AI 에이전트 시대의 C 언어 Part 1: 흔들리는 위상과 새로운 도전자들</title><link>https://philipjkim.cc/posts/20260320-c-language-in-ai-agent-era-01/</link><pubDate>Fri, 20 Mar 2026 14:08:55 +0900</pubDate><guid>https://philipjkim.cc/posts/20260320-c-language-in-ai-agent-era-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="들어가며"&gt;들어가며&lt;/h2&gt;
&lt;p&gt;C 언어는 1972년 탄생 이래 50년 넘게 시스템 프로그래밍의 근간이었습니다. Linux 커널, Windows, macOS, PostgreSQL, Python 런타임, nginx — 우리가 매일 쓰는 소프트웨어의 토대 상당 부분이 C로 작성되어 있습니다.&lt;/p&gt;
&lt;p&gt;그러나 최근 몇 년간 C 언어의 입지에 큰 변화가 일어나고 있습니다. 메모리 안전성 문제를 둘러싼 논쟁이 격화되고, 미국 정부 기관이 공식적으로 C 사용을 자제하라고 권고하며, Rust를 비롯한 대안 언어들이 빠르게 성장하고 있습니다. 여기에 2024년부터 본격화된 &lt;strong&gt;AI 코딩 에이전트(Claude Code, Codex, Gemini CLI 등)&lt;/strong&gt; 의 등장은 이 흐름을 더욱 가속하고 있습니다.&lt;/p&gt;</description></item><item><title>AI 코딩 시대의 그림자, 7개월 후: 무엇이 변했고 무엇이 남았나</title><link>https://philipjkim.cc/posts/20260313-ai-coding-shadows-revisited/</link><pubDate>Fri, 13 Mar 2026 09:11:11 +0900</pubDate><guid>https://philipjkim.cc/posts/20260313-ai-coding-shadows-revisited/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 philipjkim 이 초안을 작성했으며, 이후 Claude Opus 4.6 을 이용한 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;지난 2025년 8월, &lt;a href="https://philipjkim.cc/posts/20250805-shadows-of-ai-coding-era/"&gt;AI 코딩 시대의 그림자: LLM 의존이 개발자에게 미치는 잠재적 위험 5가지&lt;/a&gt;라는 글을 썼습니다. 당시 &lt;code&gt;vibe-coding&lt;/code&gt;이라는 단어가 막 퍼지기 시작하던 때였고, AI 코딩 도구에 대한 기대와 우려가 공존하던 시기였습니다.&lt;/p&gt;
&lt;p&gt;7개월이 흘렀습니다. 그 사이 Claude Code, Cursor, Windsurf 같은 AI 코딩 에이전트들은 단순한 코드 생성을 넘어 프로젝트 전체를 자율적으로 다루는 수준에 도달했습니다. 개발자들의 일상에서 AI agent 없이 코딩하는 시간은 점점 줄어들고 있고, 많은 팀에서 AI agent는 이미 팀원 한 명에 준하는 역할을 하고 있습니다.&lt;/p&gt;</description></item><item><title>웹서비스 내부 구조 아키텍처 가이드 Part 2: EDA, VSA, Modular Monolith</title><link>https://philipjkim.cc/posts/20260312-web-service-architecture-guide-02/</link><pubDate>Thu, 12 Mar 2026 10:07:30 +0900</pubDate><guid>https://philipjkim.cc/posts/20260312-web-service-architecture-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://philipjkim.cc/posts/20260312-web-service-architecture-guide-01/"&gt;Part 1&lt;/a&gt;에서는 Layered Architecture, Ports &amp;amp; Adapters 계열(Hexagonal/Onion/Clean), CQRS를 다뤘습니다. Part 2에서는 나머지 세 아키텍처와 전체 비교를 다룹니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="4-event-driven-architecture-eda"&gt;4. Event-Driven Architecture (EDA)&lt;/h2&gt;
&lt;h3 id="개요"&gt;개요&lt;/h3&gt;
&lt;p&gt;Event-Driven Architecture는 &lt;strong&gt;시스템 구성 요소 간의 통신을 이벤트를 통해 수행&lt;/strong&gt; 하는 설계 패턴입니다. 컴포넌트가 서로 직접 호출하는 대신, &amp;ldquo;무언가가 발생했다&amp;quot;는 사실을 이벤트로 발행하면, 관심 있는 컴포넌트가 이를 구독하여 반응합니다.&lt;/p&gt;
&lt;p&gt;이 글의 전제가 Kafka/RabbitMQ 같은 메시지 브로커를 포함하는 환경이므로, EDA를 내부 구조 패턴으로 다루는 것은 매우 자연스럽습니다. 메시지 브로커가 있다는 것 자체가 이미 이벤트 기반 통신의 인프라를 갖추고 있다는 뜻이기 때문입니다.&lt;/p&gt;</description></item><item><title>웹서비스 내부 구조 아키텍처 가이드 Part 1: Layered, Ports &amp; Adapters, CQRS</title><link>https://philipjkim.cc/posts/20260312-web-service-architecture-guide-01/</link><pubDate>Thu, 12 Mar 2026 10:07:29 +0900</pubDate><guid>https://philipjkim.cc/posts/20260312-web-service-architecture-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;이 글은 웹서비스를 전제로 합니다. 구체적으로는 HTML/CSS/JS 프런트엔드, REST/gRPC 기반 백엔드, RDB/Redis/NoSQL 저장소, Kafka/RabbitMQ 같은 메시지 브로커가 있는 환경을 상정합니다.&lt;/p&gt;
&lt;p&gt;비교 대상은 &amp;ldquo;배포 아키텍처&amp;quot;가 아닙니다. 마이크로서비스냐 모놀리스냐의 문제가 아니라, &lt;strong&gt;애플리케이션 내부 코드를 어떻게 나누고, 의존성을 어느 방향으로 흐르게 하며, 변경을 어떤 단위로 흡수할 것인가&lt;/strong&gt; 에 관한 내부 구조 아키텍처입니다. Microsoft도 논리적 계층(layer)과 물리적 계층(tier)을 구분하며, 논리적 계층 분리가 반드시 별도 서버 분리를 의미하지는 않는다고 설명합니다.&lt;/p&gt;</description></item><item><title>Go 웹 프레임워크 비교: Chi vs. Gin</title><link>https://philipjkim.cc/posts/20260307-go-web-framework-chi-vs-gin/</link><pubDate>Sat, 07 Mar 2026 00:21:25 +0900</pubDate><guid>https://philipjkim.cc/posts/20260307-go-web-framework-chi-vs-gin/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Go로 웹 애플리케이션을 만들 때, 가장 많이 비교되는 두 선택지가 &lt;strong&gt;Chi&lt;/strong&gt;와 &lt;strong&gt;Gin&lt;/strong&gt;입니다. GitHub 스타 기준으로 Gin이 압도적인 인기를 누리고 있지만(Gin ~80k vs Chi ~19k), 실무에서의 선택은 단순한 인기도만으로 결정할 문제가 아닙니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 두 라이브러리의 &lt;strong&gt;설계 철학&lt;/strong&gt;부터 시작해서, 실제 코드 레벨에서의 차이를 비교합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="설계-철학-라우터-vs-프레임워크"&gt;설계 철학: 라우터 vs. 프레임워크&lt;/h2&gt;
&lt;p&gt;Chi와 Gin의 가장 근본적인 차이는 &lt;strong&gt;자기 자신을 어떻게 정의하는가&lt;/strong&gt;에 있습니다.&lt;/p&gt;</description></item><item><title>HTMX와 함께 사용할 Go 템플릿 기술 비교: html/template vs. Templ</title><link>https://philipjkim.cc/posts/20260307-go-html-template-vs-templ-with-htmx/</link><pubDate>Sat, 07 Mar 2026 00:01:56 +0900</pubDate><guid>https://philipjkim.cc/posts/20260307-go-html-template-vs-templ-with-htmx/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Go로 SSR 웹 앱을 만들 때 HTMX와 함께 사용할 템플릿 기술로 두 가지 선택지가 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go 표준 &lt;code&gt;html/template&lt;/code&gt; + 커스텀 PageRenderer (페이지별 독립 템플릿 세트)&lt;/li&gt;
&lt;li&gt;Templ&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 글에서는 두 방식의 장단점을 실무 관점에서 비교합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="타입-안전성"&gt;타입 안전성&lt;/h2&gt;
&lt;p&gt;가장 큰 갈림길입니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;html/template&lt;/code&gt;은 런타임에 템플릿을 파싱하고 데이터를 바인딩합니다. &lt;code&gt;{{ .Titl }}&lt;/code&gt;이라고 써도 빌드는 통과합니다. 런타임에 빈 문자열이 나오거나 에러가 발생하고 나서야 문제를 알 수 있습니다.&lt;/p&gt;</description></item><item><title>AI 시대의 Spring 기술 스택 재정비: 명시성을 되찾기 위한 선택들</title><link>https://philipjkim.cc/posts/20260304-spring-stack-for-ai-era/</link><pubDate>Wed, 04 Mar 2026 16:20:43 +0900</pubDate><guid>https://philipjkim.cc/posts/20260304-spring-stack-for-ai-era/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Claude Code 같은 AI 코딩 도구를 본격적으로 활용하기 시작하면서, 그동안 당연하게 쓰던 기술 스택에 대해 다시 생각하게 되었습니다. JPA의 dirty checking이 정말 편한 건지, Lombok 없이는 못 사는 건지, RestTemplate과 Retrofit이 뒤섞인 코드베이스가 과연 합리적인 건지.&lt;/p&gt;
&lt;p&gt;결론부터 말하면, &lt;strong&gt;AI가 생성한 코드를 사람이 빠르게 읽고 검증하려면 &amp;ldquo;마법(magic)&amp;ldquo;보다 &amp;ldquo;명시성(explicitness)&amp;ldquo;이 훨씬 중요하다&lt;/strong&gt;는 생각에 도달했습니다. 이 글에서는 그 관점에서 기존 Java/Spring 기술 스택의 각 영역을 하나씩 짚어봅니다.&lt;/p&gt;</description></item><item><title>WELC ver. 2026 Part 5: 의존성 깨기 기법 카탈로그</title><link>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-05/</link><pubDate>Wed, 04 Mar 2026 10:03:52 +0900</pubDate><guid>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-05/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="도입"&gt;도입&lt;/h2&gt;
&lt;p&gt;원서 Chapter 25는 25가지 의존성 깨기(Dependency-Breaking) 기법을 카탈로그 형태로 제시합니다. 각 기법은 &amp;ldquo;테스트 하네스에 코드를 넣기 위해 의존성을 어떻게 끊을 것인가&amp;quot;라는 질문에 대한 구체적인 답입니다.&lt;/p&gt;
&lt;p&gt;다만 일부 기법은 C/C++ 전용이거나, 현대 Java에서는 불필요합니다. Link Substitution, Text Redefinition, Replace Function with Function Pointer 같은 기법이 대표적입니다. 이 파트에서는 2026년 현재 Java 개발에서 특히 유용한 핵심 기법들을 선별하여 정리합니다.&lt;/p&gt;</description></item><item><title>WELC ver. 2026 Part 4: 대규모 코드 문제 다루기</title><link>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-04/</link><pubDate>Wed, 04 Mar 2026 10:03:51 +0900</pubDate><guid>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="도입"&gt;도입&lt;/h2&gt;
&lt;p&gt;레거시 시스템에서 가장 자주 마주치는 문제는 규모의 문제입니다. 한 클래스가 수천 줄, 한 메서드가 수백 줄인 코드는 현업에서 흔히 발견됩니다. 이런 코드는 읽기 어렵고, 테스트하기 어렵고, 변경하면 예상치 못한 곳에서 장애가 발생합니다.&lt;/p&gt;
&lt;p&gt;이번 글에서는 Michael Feathers의 &lt;em&gt;Working Effectively with Legacy Code&lt;/em&gt; 14~22장을 바탕으로, 대규모 코드 문제를 다루는 전략을 JDK 25 기준의 modern Java 코드로 살펴봅니다. SOLID 원칙 중 SRP, ISP, OCP의 실전 적용이 핵심입니다.&lt;/p&gt;</description></item><item><title>WELC ver. 2026 Part 3: 테스트 하네스에 코드 넣기</title><link>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-03/</link><pubDate>Wed, 04 Mar 2026 10:03:50 +0900</pubDate><guid>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="도입"&gt;도입&lt;/h2&gt;
&lt;p&gt;레거시 코드를 변경하려면 먼저 테스트를 작성해야 합니다. 하지만 테스트를 작성하려면 코드의 의존성을 깨야 합니다. 의존성을 깨려면 코드를 변경해야 하는데, 변경의 안전성을 보장해줄 테스트가 아직 없습니다.&lt;/p&gt;
&lt;p&gt;이것이 Feathers가 말하는 &amp;ldquo;레거시 코드의 딜레마&amp;quot;입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In legacy code, the weights work against us. We have to pull out a piece to work with because we can&amp;rsquo;t easily test it in place.
— Michael Feathers, &lt;em&gt;Working Effectively with Legacy Code&lt;/em&gt; (2004)&lt;/p&gt;</description></item><item><title>WELC ver. 2026 Part 2: 안전한 코드 변경 패턴 — Sprout와 Wrap</title><link>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-02/</link><pubDate>Wed, 04 Mar 2026 10:03:49 +0900</pubDate><guid>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="도입"&gt;도입&lt;/h2&gt;
&lt;p&gt;Part 1에서 다룬 Legacy Code Change Algorithm의 5단계 중, 이번 글은 &amp;ldquo;변경 및 리팩터링&amp;rdquo; 단계에 해당하는 실전 패턴들을 다룹니다. Michael Feathers의 &lt;em&gt;Working Effectively with Legacy Code&lt;/em&gt; 6~8장에 등장하는 핵심 기법입니다.&lt;/p&gt;
&lt;p&gt;레거시 코드에 기능을 추가해야 하는데 시간이 없을 때, 기존 코드를 전면 리팩터링하는 것은 현실적이지 않습니다. 이때 사용할 수 있는 4가지 패턴이 있습니다.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;패턴&lt;/th&gt;
 &lt;th&gt;핵심 아이디어&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Sprout Method&lt;/td&gt;
 &lt;td&gt;새 기능을 별도 메서드로 추출&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Sprout Class&lt;/td&gt;
 &lt;td&gt;새 기능을 별도 클래스로 분리&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Wrap Method&lt;/td&gt;
 &lt;td&gt;기존 메서드를 감싸서 전후 동작 추가&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Wrap Class&lt;/td&gt;
 &lt;td&gt;Decorator 패턴으로 기존 클래스를 감싸기&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;이 4가지 패턴의 공통점은 &lt;strong&gt;기존 코드를 가능한 한 건드리지 않으면서 새 기능을 추가&lt;/strong&gt;한다는 것입니다.&lt;/p&gt;</description></item><item><title>WELC ver. 2026 Part 1: 레거시 코드의 정의와 변경의 역학</title><link>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-01/</link><pubDate>Wed, 04 Mar 2026 10:03:48 +0900</pubDate><guid>https://philipjkim.cc/posts/20260304-working-effectively-with-legacy-code-2026-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="시리즈-소개"&gt;시리즈 소개&lt;/h2&gt;
&lt;p&gt;2004년 Michael Feathers가 출판한 &lt;em&gt;Working Effectively with Legacy Code&lt;/em&gt; (a.k.a. WELC)는 레거시 코드를 안전하게 변경하는 기법들을 체계적으로 정리한 명저입니다. 출판된 지 20년이 넘었지만, 이 책이 제시하는 원칙과 알고리즘은 여전히 유효합니다. 코드베이스가 노후화되는 속도보다 개발자가 이 책을 읽는 속도가 느린 것이 문제일 뿐입니다.&lt;/p&gt;
&lt;p&gt;원서의 예제는 Java 1.4/5, C++, C 시대의 코드입니다. 이 시리즈에서는 JDK 25 (2025년 9월 GA, LTS) 기준의 modern Java로 핵심 기법들을 재현합니다. Sealed class, record, text block, pattern matching 등 최신 언어 기능을 적극 활용하여, 원서의 기법이 오늘날 어떤 형태로 적용되는지 보여드리겠습니다.&lt;/p&gt;</description></item><item><title>AI 시대 엔지니어의 심화 전략: 복리 게임, 거울의 한계, 선제적 AI 활용</title><link>https://philipjkim.cc/posts/20260225-digging-deeper-ai-coding-review/</link><pubDate>Wed, 25 Feb 2026 13:54:39 +0900</pubDate><guid>https://philipjkim.cc/posts/20260225-digging-deeper-ai-coding-review/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;이 글의 시작이 된 에세이&lt;/strong&gt;: &lt;a href="https://flowkater.io/posts/2026-02-19-code-reading-era/"&gt;코드를 읽지 않는 시대, 엔지니어는 무엇을 읽어야 하는가&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="1-복리-게임-정의-본질-그리고-승리-전략"&gt;1. 복리 게임: 정의, 본질, 그리고 승리 전략&lt;/h2&gt;
&lt;h3 id="복리-게임이란-무엇인가"&gt;복리 게임이란 무엇인가&lt;/h3&gt;
&lt;p&gt;Kent Beck이 제시한 &lt;strong&gt;복리 게임(The Compounding Game)&lt;/strong&gt; 은 소프트웨어 개발의 본질을 설명하는 프레임이다. 이를 이해하려면 먼저 그 반대편에 있는 결승선 게임과 대비해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;결승선 게임(The Finish Line Game)&lt;/strong&gt; 은 명확한 종료 조건이 있는 게임이다. &amp;ldquo;X를 하는 소프트웨어가 필요하다 → X를 구현하면 끝.&amp;rdquo; 일회성 스크립트, PoC, 해커톤 프로젝트가 여기에 해당한다. 가장 빠르게 결승선에 도달하는 사람이 이기고, 결승선을 넘으면 게임이 끝난다. AI 바이브 코딩이 압도적으로 유리한 영역이기도 하다.&lt;/p&gt;</description></item><item><title>The Healthy Programmer (2nd Edition) — Part 3</title><link>https://philipjkim.cc/posts/20260212-healthy-programmer-pt3/</link><pubDate>Thu, 12 Feb 2026 13:11:12 +0900</pubDate><guid>https://philipjkim.cc/posts/20260212-healthy-programmer-pt3/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;원서&lt;/strong&gt;: &lt;em&gt;The Healthy Programmer, Second Edition&lt;/em&gt; — Joe Kutner (Pragmatic Bookshelf, 2026)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="chapter-9-큐비클-밖에서-생각하기-thinking-outside-the-cube"&gt;Chapter 9: 큐비클 밖에서 생각하기 (Thinking Outside the Cube)&lt;/h2&gt;
&lt;h3 id="비타민-d-충전하기"&gt;비타민 D 충전하기&lt;/h3&gt;
&lt;p&gt;핀란드는 북극권에 위치하여 겨울에는 해가 뜨지 않는 날도 있지만, 여름 축제와 야외 활동으로 충분한 비타민 D를 합성해 왔습니다. 프로그래머의 사무실도 일종의 &lt;strong&gt;인공 북극권&lt;/strong&gt;과 같습니다 — 이른 아침에 출근하여 해질 무렵 퇴근하면 자연광에 노출될 기회가 거의 없습니다.&lt;/p&gt;</description></item><item><title>The Healthy Programmer (2nd Edition) — Part 2</title><link>https://philipjkim.cc/posts/20260212-healthy-programmer-pt2/</link><pubDate>Thu, 12 Feb 2026 13:11:09 +0900</pubDate><guid>https://philipjkim.cc/posts/20260212-healthy-programmer-pt2/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;원서&lt;/strong&gt;: &lt;em&gt;The Healthy Programmer, Second Edition&lt;/em&gt; — Joe Kutner (Pragmatic Bookshelf, 2026)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="chapter-5-허리-통증-예방-preventing-back-pain"&gt;Chapter 5: 허리 통증 예방 (Preventing Back Pain)&lt;/h2&gt;
&lt;h3 id="코어-근육-유닛-테스트하기"&gt;코어 근육 유닛 테스트하기&lt;/h3&gt;
&lt;p&gt;1753년 카를 필리프 에마누엘 바흐가 건반 연주의 올바른 자세에 대한 책을 출판하기 전까지, 대부분의 하프시코드 연주자들은 통증을 감수해야 한다고 생각했습니다. 프로그래머도 비슷한 상황입니다. 주당 40시간 이상 키보드 앞에 앉아 있으면서도 올바른 자세 교육을 거의 받지 못합니다. &lt;strong&gt;허리 통증은 미국에서 1차 진료 방문 원인 상위 5위&lt;/strong&gt;에 들며, 프로그래머에게 해당하는 근육성 허리 통증이 전체의 75%를 차지합니다.&lt;/p&gt;</description></item><item><title>The Healthy Programmer (2nd Edition) — Part 1</title><link>https://philipjkim.cc/posts/20260212-healthy-programmer-pt1/</link><pubDate>Thu, 12 Feb 2026 13:11:06 +0900</pubDate><guid>https://philipjkim.cc/posts/20260212-healthy-programmer-pt1/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;em&gt;이 글은 Claude Opus 4.6 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;원서&lt;/strong&gt;: &lt;em&gt;The Healthy Programmer, Second Edition&lt;/em&gt; — Joe Kutner (Pragmatic Bookshelf, 2026)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="서문-preface"&gt;서문 (Preface)&lt;/h2&gt;
&lt;h3 id="이-책을-읽어야-하는-이유"&gt;이 책을 읽어야 하는 이유&lt;/h3&gt;
&lt;p&gt;이 책을 읽어야 하는 첫 번째 이유는 &lt;strong&gt;여러분의 생명&lt;/strong&gt;이 달려 있기 때문입니다. 두 번째 이유는 &lt;strong&gt;여러분의 커리어&lt;/strong&gt;가 달려 있기 때문입니다. 저자는 허리 통증으로 더 이상 책상에 앉을 수 없게 된 프로그래머, 반복적인 키보드 사용으로 손목을 다쳐 일상생활조차 어려워진 프로그래머를 직접 만났다고 합니다. 미국의 사망 원인 1위는 심장 질환이며, 제2형 당뇨와 비만도 꾸준히 증가하고 있습니다. 프로그래머의 생활 방식이 이러한 문제에 기여하지만, 대부분의 경우 운동과 영양 관리를 통해 &lt;strong&gt;예방이 가능&lt;/strong&gt;합니다.&lt;/p&gt;</description></item><item><title>STITCH 스택으로 JavaScript 없이 모던 웹앱 만들기</title><link>https://philipjkim.cc/posts/20260211-to-do-with-stitch-stack/</link><pubDate>Wed, 11 Feb 2026 10:24:50 +0900</pubDate><guid>https://philipjkim.cc/posts/20260211-to-do-with-stitch-stack/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Java 백엔드 개발자가 프론트엔드 프레임워크 없이도 인터랙티브한 웹 애플리케이션을 만들 수 있다면 어떨까요? 이 글에서는 &lt;strong&gt;STITCH 스택&lt;/strong&gt;으로 JavaScript 로직을 최소화한 To-Do 앱을 처음부터 끝까지 만들어봅니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 글은 &lt;a href="https://philipjkim.cc/posts/20260211-to-do-app-with-modern-go-stack/"&gt;GoTHIC 스택 가이드&lt;/a&gt;의 Java/Spring 버전입니다. Go 백엔드에 관심이 있다면 GoTHIC 편도 확인해보세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="stitch-스택이란"&gt;STITCH 스택이란&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;STITCH&lt;/strong&gt;는 &lt;strong&gt;S&lt;/strong&gt;(pring Boot) + &lt;strong&gt;T&lt;/strong&gt;(hymeleaf) + &lt;strong&gt;I&lt;/strong&gt;(nteractive) + &lt;strong&gt;T&lt;/strong&gt;(ailwindCSS + DaisyUI) + &lt;strong&gt;C&lt;/strong&gt;(omponents) + &lt;strong&gt;H&lt;/strong&gt;(TMX)의 축약어입니다. &amp;ldquo;꿰매다&amp;quot;라는 뜻 그대로, 서버 사이드 렌더링과 클라이언트 인터랙션을 자연스럽게 이어붙이는 풀스택 조합입니다.&lt;/p&gt;</description></item><item><title>GoTHIC 스택으로 JavaScript 없이 모던 웹앱 만들기</title><link>https://philipjkim.cc/posts/20260211-to-do-app-with-modern-go-stack/</link><pubDate>Wed, 11 Feb 2026 09:31:05 +0900</pubDate><guid>https://philipjkim.cc/posts/20260211-to-do-app-with-modern-go-stack/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Go 백엔드 개발자가 프론트엔드 프레임워크 없이도 인터랙티브한 웹 애플리케이션을 만들 수 있다면 어떨까요? 이 글에서는 &lt;strong&gt;GoTHIC 스택&lt;/strong&gt;으로 JavaScript 로직을 최소화한 To-Do 앱을 처음부터 끝까지 만들어봅니다. 핫 리로드까지 &lt;code&gt;air&lt;/code&gt;로 세팅하면 개발 경험도 꽤 쾌적합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="gothic-스택이란"&gt;GoTHIC 스택이란&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;GoTHIC&lt;/strong&gt;은 &lt;strong&gt;Go&lt;/strong&gt;(+Gin) + &lt;strong&gt;T&lt;/strong&gt;(empl) + &lt;strong&gt;H&lt;/strong&gt;(TMX) + &lt;strong&gt;I&lt;/strong&gt;nteractive &lt;strong&gt;C&lt;/strong&gt;omponents(Alpine.js + TailwindCSS + DaisyUI)의 축약어입니다. GoTH(Go + Templ + HTMX) 스택을 확장하여 클라이언트 인터랙션과 스타일링까지 아우르는 풀스택 조합입니다.&lt;/p&gt;</description></item><item><title>Claude Code Compaction 가이드</title><link>https://philipjkim.cc/posts/20260204-about-claude-code-compaction/</link><pubDate>Wed, 04 Feb 2026 19:28:33 +0900</pubDate><guid>https://philipjkim.cc/posts/20260204-about-claude-code-compaction/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Claude Code를 하루 종일 사용하다 보면 어느 순간 &amp;ldquo;Compacting our conversation&amp;hellip;&amp;rdquo; 메시지와 함께 작업 흐름이 끊기는 경험을 하게 됩니다. 이 글에서는 Compaction이 무엇인지, 왜 자주 발생하는지, 그리고 어떻게 줄일 수 있는지 실전 전략을 공유합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="compaction이란"&gt;Compaction이란?&lt;/h2&gt;
&lt;p&gt;Claude Code는 대화 내용, 읽은 파일, 명령어 출력 등을 &lt;strong&gt;Context Window&lt;/strong&gt;라는 제한된 공간에 저장합니다. 이 공간이 가득 차면 Claude는 더 이상 새로운 정보를 처리할 수 없게 됩니다.&lt;/p&gt;</description></item><item><title>Mermaid 다이어그램 가이드 Part 4: State Diagram</title><link>https://philipjkim.cc/posts/20260128-mermaid-diagram-guide-04/</link><pubDate>Wed, 28 Jan 2026 10:37:39 +0900</pubDate><guid>https://philipjkim.cc/posts/20260128-mermaid-diagram-guide-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="state-diagram이란"&gt;State Diagram이란?&lt;/h2&gt;
&lt;p&gt;상태 다이어그램(State Diagram)은 시스템이나 객체가 가질 수 있는 상태(State)와 상태 간의 전이(Transition)를 시각화합니다. 주문 처리, 사용자 인증, TCP 연결 등 유한 상태 기계(Finite State Machine)를 표현할 때 필수적인 도구입니다.&lt;/p&gt;
&lt;h2 id="기본-문법"&gt;기본 문법&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;stateDiagram-v2
 [*] --&amp;gt; Idle
 Idle --&amp;gt; Processing
 Processing --&amp;gt; Completed
 Completed --&amp;gt; [*]
&lt;/code&gt;&lt;/pre&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;stateDiagram-v2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [*] --&amp;gt; Idle
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Idle --&amp;gt; Processing
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Processing --&amp;gt; Completed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Completed --&amp;gt; [*]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;stateDiagram-v2&lt;/code&gt;를 사용하면 최신 렌더러로 그려집니다. 구버전 &lt;code&gt;stateDiagram&lt;/code&gt;도 여전히 사용 가능합니다.&lt;/p&gt;</description></item><item><title>Mermaid 다이어그램 가이드 Part 3: Sequence Diagram</title><link>https://philipjkim.cc/posts/20260128-mermaid-diagram-guide-03/</link><pubDate>Wed, 28 Jan 2026 10:37:36 +0900</pubDate><guid>https://philipjkim.cc/posts/20260128-mermaid-diagram-guide-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="sequence-diagram이란"&gt;Sequence Diagram이란?&lt;/h2&gt;
&lt;p&gt;시퀀스 다이어그램은 객체들 간의 메시지 교환을 시간 순서에 따라 보여주는 다이어그램입니다. API 호출 흐름, 인증 과정, 트랜잭션 처리 등을 문서화할 때 특히 유용합니다.&lt;/p&gt;
&lt;h2 id="기본-문법"&gt;기본 문법&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;sequenceDiagram
 participant A as 클라이언트
 participant B as 서버
 A-&amp;gt;&amp;gt;B: 요청
 B--&amp;gt;&amp;gt;A: 응답
&lt;/code&gt;&lt;/pre&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sequenceDiagram
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; participant A as 클라이언트
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; participant B as 서버
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; A-&amp;gt;&amp;gt;B: 요청
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; B--&amp;gt;&amp;gt;A: 응답
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="participant-정의"&gt;Participant 정의&lt;/h2&gt;
&lt;h3 id="자동-정의"&gt;자동 정의&lt;/h3&gt;
&lt;p&gt;메시지에서 처음 등장하는 이름이 자동으로 participant가 됩니다.&lt;/p&gt;</description></item><item><title>Mermaid 다이어그램 가이드 Part 2: Flowchart 심화 - Subgraph와 레이아웃 제어</title><link>https://philipjkim.cc/posts/20260128-mermaid-diagram-guide-02/</link><pubDate>Wed, 28 Jan 2026 10:37:33 +0900</pubDate><guid>https://philipjkim.cc/posts/20260128-mermaid-diagram-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="subgraph-기본"&gt;Subgraph 기본&lt;/h2&gt;
&lt;p&gt;Subgraph는 관련된 노드들을 논리적으로 그룹화하는 기능입니다. 시스템의 계층 구조나 도메인 영역을 시각적으로 구분할 때 유용합니다.&lt;/p&gt;
&lt;h3 id="기본-문법"&gt;기본 문법&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 subgraph 그룹명
 A --&amp;gt; B
 end
 C --&amp;gt; A
&lt;/code&gt;&lt;/pre&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;flowchart TB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subgraph 그룹명
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; A --&amp;gt; B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; C --&amp;gt; A
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="id와-제목-분리"&gt;ID와 제목 분리&lt;/h3&gt;
&lt;p&gt;Subgraph에 별도의 ID를 지정하면 참조가 쉬워집니다.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 subgraph api[API Layer]
 A[Controller]
 B[Service]
 end
 subgraph db[Data Layer]
 C[(MySQL)]
 D[(Redis)]
 end
 A --&amp;gt; B
 B --&amp;gt; C
 B --&amp;gt; D
&lt;/code&gt;&lt;/pre&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;flowchart TB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subgraph api[API Layer]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; A[Controller]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; B[Service]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subgraph db[Data Layer]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; C[(MySQL)]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; D[(Redis)]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; A --&amp;gt; B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; B --&amp;gt; C
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; B --&amp;gt; D
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="subgraph-방향-제어"&gt;Subgraph 방향 제어&lt;/h2&gt;
&lt;p&gt;이것이 Mermaid 레이아웃의 핵심입니다. &lt;code&gt;direction&lt;/code&gt; 키워드로 각 subgraph 내부의 배치 방향을 개별적으로 지정할 수 있습니다.&lt;/p&gt;</description></item><item><title>Mermaid 다이어그램 가이드 Part 1: Flowchart 기본</title><link>https://philipjkim.cc/posts/20260128-mermaid-diagram-guide-01/</link><pubDate>Wed, 28 Jan 2026 10:37:30 +0900</pubDate><guid>https://philipjkim.cc/posts/20260128-mermaid-diagram-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mermaid란"&gt;Mermaid란?&lt;/h2&gt;
&lt;p&gt;Mermaid는 마크다운 스타일의 텍스트로 다이어그램을 생성하는 JavaScript 기반 도구입니다. 별도의 드로잉 도구 없이도 Git에서 버전 관리가 가능한 다이어그램을 작성할 수 있어, 개발 문서화에 특히 유용합니다.&lt;/p&gt;
&lt;p&gt;GitHub, GitLab, Notion, Obsidian 등 다양한 플랫폼에서 기본 지원되며, VS Code 확장 프로그램을 통해 실시간 미리보기도 가능합니다.&lt;/p&gt;
&lt;h2 id="flowchart-기본-문법"&gt;Flowchart 기본 문법&lt;/h2&gt;
&lt;p&gt;Flowchart는 프로세스의 흐름을 시각화하는 가장 기본적인 다이어그램입니다. 노드(Node)와 엣지(Edge)로 구성됩니다.&lt;/p&gt;
&lt;h3 id="다이어그램-선언"&gt;다이어그램 선언&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TD
 A --&amp;gt; B
&lt;/code&gt;&lt;/pre&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;flowchart TD
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; A --&amp;gt; B
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;flowchart&lt;/code&gt; 키워드로 시작하며, 바로 뒤에 방향을 지정합니다. &lt;code&gt;graph&lt;/code&gt; 키워드도 사용 가능하지만 &lt;code&gt;flowchart&lt;/code&gt;가 더 많은 기능을 지원합니다.&lt;/p&gt;</description></item><item><title>Claude Code 완전 정복 가이드 Part 6: 고급 패턴과 Best Practices</title><link>https://philipjkim.cc/posts/20260123-claude-code-guide-06/</link><pubDate>Fri, 23 Jan 2026 17:21:06 +0900</pubDate><guid>https://philipjkim.cc/posts/20260123-claude-code-guide-06/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;예상 소요 시간&lt;/strong&gt;: 1시간 30분
&lt;strong&gt;난이도&lt;/strong&gt;: ⭐⭐⭐⭐⭐
&lt;strong&gt;사전 요구사항&lt;/strong&gt;: Part 1-5 완료
&lt;strong&gt;Claude Code 버전&lt;/strong&gt;: 2.1.x (Opus 4.5)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="0-claude-code-21-주요-신기능"&gt;0. Claude Code 2.1 주요 신기능&lt;/h2&gt;
&lt;p&gt;본격적인 내용에 앞서, Claude Code 2.1에서 추가된 핵심 기능들을 정리합니다:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;기능&lt;/th&gt;
 &lt;th&gt;설명&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Session Teleportation&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;/teleport&lt;/code&gt;, &lt;code&gt;/remote-env&lt;/code&gt;로 로컬↔클라우드 세션 이동&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Hot Reload for Skills&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Skills 수정 시 재시작 없이 즉시 반영&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Setup Hook&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;--init&lt;/code&gt;, &lt;code&gt;--init-only&lt;/code&gt;, &lt;code&gt;--maintenance&lt;/code&gt; CLI 플래그용 훅&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Task Management&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;의존성 추적이 포함된 태스크 관리 시스템&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Language Output&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;language&lt;/code&gt; 설정으로 출력 언어 지정&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Opus 4.5&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Pro 사용자에게 Opus 4.5 모델 제공&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;respectGitignore&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;@-mention&lt;/code&gt; 파일 선택에서 gitignore 적용&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;showTurnDuration&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;턴 소요 시간 표시 설정&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="1-hooks-예외-없는-강제-자동화"&gt;1. Hooks: 예외 없는 강제 자동화&lt;/h2&gt;
&lt;h3 id="11-claudemd-vs-hooks의-근본적-차이"&gt;1.1 CLAUDE.md vs Hooks의 근본적 차이&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 subgraph Comparison[&amp;#34;CLAUDE.md vs Hooks&amp;#34;]
 direction TB

 subgraph Advisory[&amp;#34;📄 CLAUDE.md = &amp;#39;권고&amp;#39; (Advisory)&amp;#34;]
 A1[&amp;#34;&amp;#39;테스트를 실행해주세요&amp;#39;&amp;lt;br/&amp;gt;&amp;#39;린트를 돌려주세요&amp;#39;&amp;#34;]
 A2[&amp;#34;→ Claude가 따를 수도 있고, 잊을 수도 있음&amp;lt;br/&amp;gt;→ 복잡한 대화에서 규칙이 희석될 수 있음&amp;#34;]
 end

 subgraph Enforcement[&amp;#34;⚡ Hooks = &amp;#39;강제&amp;#39; (Enforcement)&amp;#34;]
 E1[&amp;#34;파일 저장 시 → 자동으로 린트 실행&amp;lt;br/&amp;gt;대화 시작 시 → 자동으로 컨텍스트 로드&amp;#34;]
 E2[&amp;#34;→ 예외 없이 항상 실행됨&amp;lt;br/&amp;gt;→ Claude의 판단과 무관하게 트리거&amp;#34;]
 end
 end

 style Advisory fill:#fff9c4,color:#000000
 style Enforcement fill:#c8e6c9,color:#000000
 style A1 fill:#fffde7,color:#000000
 style A2 fill:#ffecb3,color:#000000
 style E1 fill:#e8f5e9,color:#000000
 style E2 fill:#a5d6a7,color:#000000
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="12-hooks-이벤트-종류"&gt;1.2 Hooks 이벤트 종류&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;이벤트&lt;/th&gt;
 &lt;th&gt;트리거 시점&lt;/th&gt;
 &lt;th&gt;사용 예시&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PreToolUse&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;도구 실행 직전&lt;/td&gt;
 &lt;td&gt;위험한 명령 차단, 로깅&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;PostToolUse&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;도구 실행 직후&lt;/td&gt;
 &lt;td&gt;린트 자동 실행, 검증&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;Notification&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;알림 발생 시&lt;/td&gt;
 &lt;td&gt;외부 시스템 연동&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;Stop&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;작업 완료/중단 시&lt;/td&gt;
 &lt;td&gt;정리 작업, 리포트&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;Setup&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;--init&lt;/code&gt;, &lt;code&gt;--init-only&lt;/code&gt;, &lt;code&gt;--maintenance&lt;/code&gt; 실행 시&lt;/td&gt;
 &lt;td&gt;저장소 초기화, 유지보수 (2.1+ 신규)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;💡 &lt;strong&gt;Claude Code 2.1+ 변경사항&lt;/strong&gt;:&lt;/p&gt;</description></item><item><title>Claude Code 완전 정복 가이드 Part 5: 나만의 Plugin Marketplace 만들기 - 팀/조직 확장</title><link>https://philipjkim.cc/posts/20260123-claude-code-guide-05/</link><pubDate>Fri, 23 Jan 2026 17:21:03 +0900</pubDate><guid>https://philipjkim.cc/posts/20260123-claude-code-guide-05/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;예상 소요 시간&lt;/strong&gt;: 1시간 30분
&lt;strong&gt;난이도&lt;/strong&gt;: ⭐⭐⭐⭐
&lt;strong&gt;사전 요구사항&lt;/strong&gt;: Part 1-4 완료, Git/GitHub 기본 지식
&lt;strong&gt;Claude Code 버전&lt;/strong&gt;: 2.1.x&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="1-plugin-구조-이해"&gt;1. Plugin 구조 이해&lt;/h2&gt;
&lt;h3 id="11-plugin-디렉토리-구조"&gt;1.1 Plugin 디렉토리 구조&lt;/h3&gt;
&lt;p&gt;💡 &lt;strong&gt;주의&lt;/strong&gt;: 2026년 기준, &lt;code&gt;marketplace.json&lt;/code&gt;에서 Skills를 로드하려면 &lt;code&gt;skills&lt;/code&gt; 필드가 &lt;strong&gt;필수&lt;/strong&gt;입니다. 이 필드가 없으면 Skills가 로드되지 않습니다.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;my-plugin/
├── .claude-plugin/
│ └── plugin.json # 필수: 플러그인 메타데이터
├── skills/ # 선택: Skills
│ └── my-skill/
│ └── SKILL.md
├── commands/ # 선택: Slash Commands
│ └── my-command.md
├── agents/ # 선택: Subagents
│ └── my-agent.md
├── hooks/ # 선택: Hooks 설정
│ └── hooks.json
├── .mcp.json # 선택: MCP 서버 설정
└── README.md # 권장: 문서화
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="12-pluginjson-필수-필드"&gt;1.2 plugin.json 필수 필드&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-awesome-plugin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;플러그인 설명&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Your Name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;homepage&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/you/my-plugin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="13-pluginjson-전체-스키마"&gt;1.3 plugin.json 전체 스키마&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-awesome-plugin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;이 플러그인이 무엇을 하는지 간단히 설명&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Your Name &amp;lt;you@example.com&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;homepage&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/you/my-plugin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;repository&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;git&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/you/my-plugin.git&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;license&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MIT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;keywords&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;go&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;testing&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;conventions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;engines&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;claude-code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;gt;=2.0.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;dependencies&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;plugins&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;other-plugin@marketplace&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="2-첫-번째-plugin-만들기"&gt;2. 첫 번째 Plugin 만들기&lt;/h2&gt;
&lt;h3 id="21-시나리오"&gt;2.1 시나리오&lt;/h3&gt;
&lt;p&gt;Java/Kotlin 개발팀을 위한 Plugin을 만들어봅시다. 다음을 포함합니다:&lt;/p&gt;</description></item><item><title>Claude Code 완전 정복 가이드 Part 4: Plugins &amp; Marketplace - 재사용 가능한 확장 패키지</title><link>https://philipjkim.cc/posts/20260123-claude-code-guide-04/</link><pubDate>Fri, 23 Jan 2026 17:21:00 +0900</pubDate><guid>https://philipjkim.cc/posts/20260123-claude-code-guide-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;예상 소요 시간&lt;/strong&gt;: 1시간
&lt;strong&gt;난이도&lt;/strong&gt;: ⭐⭐⭐
&lt;strong&gt;사전 요구사항&lt;/strong&gt;: Part 1, 2, 3 완료
&lt;strong&gt;Claude Code 버전&lt;/strong&gt;: 2.1.x (VSCode Native Plugin Management 지원)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="1-plugin이란-무엇인가"&gt;1. Plugin이란 무엇인가?&lt;/h2&gt;
&lt;h3 id="11-skills-vs-commands-vs-plugins-vs-subagents-개념-정리"&gt;1.1 Skills vs Commands vs Plugins vs Subagents 개념 정리&lt;/h3&gt;
&lt;p&gt;Claude Code의 확장 시스템은 여러 요소로 구성됩니다. 각각의 역할을 명확히 이해해야 합니다:&lt;/p&gt;
&lt;p&gt;💡 &lt;strong&gt;Claude Code 2.1+ 업데이트&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VSCode Native Plugin Management&lt;/strong&gt;: VS Code 확장에서 플러그인을 직접 탐색하고 관리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Task Management System&lt;/strong&gt;: 의존성 추적이 포함된 새로운 태스크 관리 시스템이 추가되었습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anthropic 공식 Marketplace&lt;/strong&gt;: &lt;code&gt;anthropics/claude-code&lt;/code&gt; 저장소에 공식 플러그인 마켓플레이스가 포함되어 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 subgraph ExtSystem[&amp;#34;Claude Code 확장 시스템 전체 구조&amp;#34;]
 direction TB

 subgraph Components[&amp;#34;구성 요소&amp;#34;]
 direction LR
 Skills[&amp;#34;🧠 Skills&amp;#34;]
 Commands[&amp;#34;⌨️ Commands&amp;#34;]
 Subagents[&amp;#34;👨‍💼 Subagents&amp;#34;]
 Plugins[&amp;#34;📦 Plugins&amp;#34;]
 end

 subgraph Features[&amp;#34;특징&amp;#34;]
 direction LR
 SK_F[&amp;#34;자동 호출&amp;lt;br/&amp;gt;참조 지식&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;#39;레시피북&amp;#39;&amp;#34;]
 CMD_F[&amp;#34;수동 호출&amp;lt;br/&amp;gt;/command&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;#39;단축키&amp;#39;&amp;#34;]
 SA_F[&amp;#34;위임 실행&amp;lt;br/&amp;gt;격리 컨텍스트&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;#39;전문가&amp;#39;&amp;#34;]
 PL_F[&amp;#34;배포 패키지&amp;lt;br/&amp;gt;(컨테이너)&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;#39;도구상자&amp;#39;&amp;#34;]
 end

 Skills --&amp;gt; SK_F
 Commands --&amp;gt; CMD_F
 Subagents --&amp;gt; SA_F
 Plugins --&amp;gt; PL_F

 subgraph PluginContents[&amp;#34;Plugin이 포함할 수 있는 것&amp;#34;]
 direction LR
 P_Skills[&amp;#34;Skills&amp;#34;]
 P_Commands[&amp;#34;Commands&amp;#34;]
 P_Subagent[&amp;#34;Subagent&amp;#34;]
 P_Hooks[&amp;#34;Hooks&amp;#34;]
 P_MCP[&amp;#34;MCP&amp;#34;]
 end
 end

 style Skills fill:#c8e6c9,color:#000000
 style Commands fill:#bbdefb,color:#000000
 style Subagents fill:#fff9c4,color:#000000
 style Plugins fill:#ffccbc,color:#000000
 style SK_F fill:#e8f5e9,color:#000000
 style CMD_F fill:#e3f2fd,color:#000000
 style SA_F fill:#fffde7,color:#000000
 style PL_F fill:#fbe9e7,color:#000000
 style PluginContents fill:#f5f5f5,color:#000000
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="12-각-요소-상세-비교"&gt;1.2 각 요소 상세 비교&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;요소&lt;/th&gt;
 &lt;th&gt;호출 방식&lt;/th&gt;
 &lt;th&gt;컨텍스트&lt;/th&gt;
 &lt;th&gt;주요 용도&lt;/th&gt;
 &lt;th&gt;저장 위치&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Skills&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;자동 (description 매칭)&lt;/td&gt;
 &lt;td&gt;메인에 주입&lt;/td&gt;
 &lt;td&gt;참조 지식, 패턴&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;.claude/skills/&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Commands&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;수동 (&lt;code&gt;/command&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;메인에서 실행&lt;/td&gt;
 &lt;td&gt;반복 작업 자동화&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;.claude/commands/&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Subagents&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;명시적 위임&lt;/td&gt;
 &lt;td&gt;격리된 윈도우&lt;/td&gt;
 &lt;td&gt;독립적 작업 처리&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;.claude/agents/&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Plugins&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;설치 후 내부 요소 사용&lt;/td&gt;
 &lt;td&gt;해당 없음&lt;/td&gt;
 &lt;td&gt;배포/공유 패키지&lt;/td&gt;
 &lt;td&gt;Marketplace&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Hooks&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;이벤트 트리거&lt;/td&gt;
 &lt;td&gt;자동 실행&lt;/td&gt;
 &lt;td&gt;강제 자동화&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;MCP&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;외부 도구 연결&lt;/td&gt;
 &lt;td&gt;도구로 사용&lt;/td&gt;
 &lt;td&gt;외부 시스템 통합&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;.mcp.json&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="13-plugin이-해결하는-문제"&gt;1.3 Plugin이 해결하는 문제&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 subgraph Before[&amp;#34;❌ Plugin 등장 이전&amp;#34;]
 Q[&amp;#34;&amp;#39;팀원 A의 Claude Code 설정을&amp;lt;br/&amp;gt;팀원 B에게 공유하려면?&amp;#39;&amp;#34;]
 B1[&amp;#34;1. Skills 폴더 복사&amp;lt;br/&amp;gt;2. Commands 폴더 복사&amp;lt;br/&amp;gt;3. Agents 폴더 복사&amp;lt;br/&amp;gt;4. 설정 파일 수정&amp;lt;br/&amp;gt;5. 버전 관리 별도&amp;lt;br/&amp;gt;6. 업데이트 수동 반영&amp;#34;]
 BR[&amp;#34;→ 복잡하고 오류 발생 가능성 높음&amp;#34;]
 end

 subgraph After[&amp;#34;✅ Plugin 등장 이후&amp;#34;]
 A1[&amp;#34;/plugin marketplace add my-team/our-plugins&amp;#34;]
 A2[&amp;#34;/plugin install our-coding-standards@our-plugins&amp;#34;]
 AR[&amp;#34;→ 버전 관리, 자동 업데이트,&amp;lt;br/&amp;gt;의존성 해결 모두 자동&amp;#34;]
 end

 Before --&amp;gt; After

 style Before fill:#ffcdd2,color:#000000
 style After fill:#c8e6c9,color:#000000
 style Q fill:#ef9a9a,color:#000000
 style BR fill:#ef9a9a,color:#000000
 style A1 fill:#a5d6a7,color:#000000
 style A2 fill:#a5d6a7,color:#000000
 style AR fill:#a5d6a7,color:#000000
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id="2-marketplace-시스템-이해"&gt;2. Marketplace 시스템 이해&lt;/h2&gt;
&lt;h3 id="21-marketplace란"&gt;2.1 Marketplace란?&lt;/h3&gt;
&lt;p&gt;Marketplace는 &lt;strong&gt;Plugin들의 카탈로그&lt;/strong&gt;입니다. Plugin을 검색하고 설치할 수 있는 저장소입니다.&lt;/p&gt;</description></item><item><title>Claude Code 완전 정복 가이드 Part 3: Skills 시스템 - 온디맨드 전문가 활용하기</title><link>https://philipjkim.cc/posts/20260123-claude-code-guide-03/</link><pubDate>Fri, 23 Jan 2026 17:20:57 +0900</pubDate><guid>https://philipjkim.cc/posts/20260123-claude-code-guide-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;예상 소요 시간&lt;/strong&gt;: 1시간
&lt;strong&gt;난이도&lt;/strong&gt;: ⭐⭐⭐
&lt;strong&gt;사전 요구사항&lt;/strong&gt;: Part 1, 2 완료
&lt;strong&gt;Claude Code 버전&lt;/strong&gt;: 2.1.x (Hot Reload 지원)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="1-skills란-무엇인가"&gt;1. Skills란 무엇인가?&lt;/h2&gt;
&lt;h3 id="11-claudemd-vs-skills의-핵심-차이"&gt;1.1 CLAUDE.md vs Skills의 핵심 차이&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt;는 항상 로드되는 &amp;ldquo;기본 매뉴얼&amp;quot;입니다. 반면 &lt;strong&gt;Skills&lt;/strong&gt;는 필요할 때만 로드되는 &amp;ldquo;전문 레시피북&amp;quot;입니다.&lt;/p&gt;
&lt;p&gt;💡 &lt;strong&gt;Claude Code 2.1+ 새 기능&lt;/strong&gt;: &lt;strong&gt;Hot Reload&lt;/strong&gt; - Skills를 수정하면 Claude Code를 재시작하지 않아도 즉시 반영됩니다. &lt;code&gt;~/.claude/skills/&lt;/code&gt; 또는 &lt;code&gt;.claude/skills/&lt;/code&gt; 디렉토리의 변경사항이 실시간으로 적용됩니다.&lt;/p&gt;</description></item><item><title>Claude Code 완전 정복 가이드 Part 2: CLAUDE.md 최적화 - 비대해지는 것을 막아라</title><link>https://philipjkim.cc/posts/20260123-claude-code-guide-02/</link><pubDate>Fri, 23 Jan 2026 17:20:54 +0900</pubDate><guid>https://philipjkim.cc/posts/20260123-claude-code-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;예상 소요 시간&lt;/strong&gt;: 45분
&lt;strong&gt;난이도&lt;/strong&gt;: ⭐⭐
&lt;strong&gt;사전 요구사항&lt;/strong&gt;: Part 1 완료, CLAUDE.md 사용 경험
&lt;strong&gt;Claude Code 버전&lt;/strong&gt;: 2.1.x&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="1-왜-claudemd는-간결해야-하는가"&gt;1. 왜 CLAUDE.md는 간결해야 하는가?&lt;/h2&gt;
&lt;h3 id="11-llm의-instruction-following-한계"&gt;1.1 LLM의 Instruction-following 한계&lt;/h3&gt;
&lt;p&gt;연구에 따르면 프론티어 LLM도 &lt;strong&gt;약 150-200개의 지시사항&lt;/strong&gt;만 일관되게 따를 수 있습니다. Claude Code의 시스템 프롬프트 자체가 이미 ~50개의 지시를 포함하고 있어, CLAUDE.md에 사용할 수 있는 &amp;ldquo;지시 예산&amp;quot;은 제한적입니다.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TB
 subgraph Capacity[&amp;#34;LLM 지시 따르기 용량&amp;#34;]
 direction TB
 Title[&amp;#34;📊 총 용량: ~150-200개 지시&amp;#34;]

 subgraph Components[&amp;#34;구성 요소&amp;#34;]
 direction LR
 A[&amp;#34;🤖 Claude Code&amp;lt;br/&amp;gt;시스템 프롬프트&amp;lt;br/&amp;gt;(~50개)&amp;#34;]
 B[&amp;#34;📄 CLAUDE.md&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;(당신의 몫)&amp;#34;]
 C[&amp;#34;🧠 Skills/Rules&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;(추가 지시)&amp;#34;]
 end

 Warning[&amp;#34;⚠️ 초과 시 → 균일하게 모든 지시를 무시하기 시작&amp;#34;]
 end

 Title --&amp;gt; Components
 Components --&amp;gt; Warning

 style Title fill:#e3f2fd,color:#000000
 style A fill:#bbdefb,color:#000000
 style B fill:#c8e6c9,color:#000000
 style C fill:#fff9c4,color:#000000
 style Warning fill:#ffcdd2,color:#000000
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="12-토큰-비용과-context-window"&gt;1.2 토큰 비용과 Context Window&lt;/h3&gt;
&lt;p&gt;CLAUDE.md는 &lt;strong&gt;모든 대화에서 항상 로드&lt;/strong&gt;됩니다. 파일이 커질수록:&lt;/p&gt;</description></item><item><title>Claude Code 완전 정복 가이드 Part 1: CLAUDE.md 기초 - 첫 발을 내딛다</title><link>https://philipjkim.cc/posts/20260123-claude-code-guide-01/</link><pubDate>Fri, 23 Jan 2026 17:20:51 +0900</pubDate><guid>https://philipjkim.cc/posts/20260123-claude-code-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;예상 소요 시간&lt;/strong&gt;: 30분
&lt;strong&gt;난이도&lt;/strong&gt;: ⭐
&lt;strong&gt;사전 요구사항&lt;/strong&gt;: Claude Code 설치 완료 (v2.1.x)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="1-claude-code란-무엇인가"&gt;1. Claude Code란 무엇인가?&lt;/h2&gt;
&lt;h3 id="11-agentic-coding의-개념"&gt;1.1 Agentic Coding의 개념&lt;/h3&gt;
&lt;p&gt;Claude Code는 단순한 코드 자동완성 도구가 아닙니다. &lt;strong&gt;Agentic Coding Assistant&lt;/strong&gt;로서, 터미널에서 직접 코드베이스를 읽고, 파일을 생성/수정하고, 테스트를 실행하고, Git 작업까지 수행할 수 있습니다. 2025년 2월 첫 출시 이후 빠르게 발전하여, 현재 버전 2.1.x에서는 &lt;strong&gt;Opus 4.5&lt;/strong&gt; 모델을 지원하며 더욱 강력한 기능을 제공합니다.&lt;/p&gt;</description></item><item><title>Claude Code 완전 정복 가이드 Part 0: Overview</title><link>https://philipjkim.cc/posts/20260123-claude-code-guide-00/</link><pubDate>Fri, 23 Jan 2026 17:20:48 +0900</pubDate><guid>https://philipjkim.cc/posts/20260123-claude-code-guide-00/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;버전&lt;/strong&gt;: 2026년 1월 기준 (Claude Code 2.1.17 / Opus 4.5)&lt;br&gt;
&lt;strong&gt;대상 독자&lt;/strong&gt;: Claude Code를 처음 시작하거나, 이미 사용 중이지만 더 효율적으로 활용하고 싶은 개발자&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="-이-가이드의-목표"&gt;🎯 이 가이드의 목표&lt;/h2&gt;
&lt;p&gt;이 가이드는 Claude Code 초보 사용자가 &lt;strong&gt;CLAUDE.md 파일 하나 만드는 것&lt;/strong&gt;에서 시작해서, &lt;strong&gt;Skills와 Plugins를 활용한 고급 워크플로우&lt;/strong&gt;까지 단계별로 성장할 수 있도록 구성되어 있습니다.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TD
 A[&amp;#34;📝 CLAUDE.md&amp;lt;br/&amp;gt;생성&amp;#34;] --&amp;gt; B[&amp;#34;⚡ 최적화&amp;lt;br/&amp;gt;전략&amp;#34;]
 B --&amp;gt; C[&amp;#34;🧠 Skills&amp;lt;br/&amp;gt;분리&amp;#34;]
 C --&amp;gt; D[&amp;#34;🔌 Plugins&amp;lt;br/&amp;gt;활용&amp;#34;]
 D --&amp;gt; E[&amp;#34;🏪 Marketplace&amp;lt;br/&amp;gt;구축&amp;#34;]
 E --&amp;gt; F[&amp;#34;🎯 고급 패턴&amp;lt;br/&amp;gt;통합&amp;#34;]
 
 style A fill:#bbdefb,color:#000000
 style B fill:#c8e6c9,color:#000000
 style C fill:#fff9c4,color:#000000
 style D fill:#ffccbc,color:#000000
 style E fill:#d1c4e9,color:#000000
 style F fill:#f8bbd0,color:#000000
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id="-가이드-구성"&gt;📚 가이드 구성&lt;/h2&gt;
&lt;h3 id="part-1-claudemd-기초---첫-발을-내딛다"&gt;Part 1: CLAUDE.md 기초 - 첫 발을 내딛다&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;예상 소요 시간&lt;/strong&gt;: 30분 | &lt;strong&gt;난이도&lt;/strong&gt;: ⭐&lt;/p&gt;</description></item><item><title>Java: JEP 444, 485, 506, 519 설명</title><link>https://philipjkim.cc/posts/20260122-java-4-new-features/</link><pubDate>Thu, 22 Jan 2026 14:06:58 +0900</pubDate><guid>https://philipjkim.cc/posts/20260122-java-4-new-features/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="jep-444-virtual-threads-java-21---정식-릴리스"&gt;JEP 444: Virtual Threads (Java 21 - 정식 릴리스)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;개요:&lt;/strong&gt;
경량 스레드를 제공하여 수십만~수백만 개의 동시 작업을 효율적으로 처리할 수 있게 합니다. 플랫폼 스레드와 달리 OS 스레드를 1:1로 매핑하지 않습니다.&lt;/p&gt;
&lt;h3 id="예제-1-대량의-http-요청-처리"&gt;예제 1: 대량의 HTTP 요청 처리&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Before (Platform Threads):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 스레드 풀 사용 (제한된 스레드 수)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ExecutorService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Executors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IntStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapToObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://api.example.com/data/&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// HTTP 요청 시뮬레이션&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 결과 수집&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 100개 스레드로 10,000개 요청 처리 → 느림&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;After (Virtual Threads):&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>AI-Native 아키텍처: Java 25와 Spring Boot 4.0 시대의 에이전트 중심 개발 패러다임</title><link>https://philipjkim.cc/posts/20260122-ai-native-changes-for-java/</link><pubDate>Thu, 22 Jan 2026 09:30:28 +0900</pubDate><guid>https://philipjkim.cc/posts/20260122-ai-native-changes-for-java/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Google Gemini Pro Deep Research 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="서론-소프트웨어-개발의-새로운-지평"&gt;서론: 소프트웨어 개발의 새로운 지평&lt;/h2&gt;
&lt;p&gt;2026년을 맞이하며 엔터프라이즈 소프트웨어 개발 생태계는 전례 없는 전환점을 맞이하고 있습니다. 인공지능(AI), 특히 대규모 언어 모델(LLM)이 단순한 코딩 보조 도구를 넘어 자율적으로 소프트웨어를 설계, 구현, 수정하는 &amp;lsquo;에이전트(Agent)&amp;lsquo;로 진화함에 따라, 우리가 코드를 작성하는 방식과 시스템을 아키텍처링하는 원칙 자체가 근본적으로 재평가되고 있습니다. 과거 10년이 마이크로서비스와 클라우드 네이티브의 시대였다면, 향후 10년은 인간 개발자와 AI 에이전트가 공존하며 협업하는 &amp;lsquo;AI-Native&amp;rsquo; 시대가 될 것입니다.&lt;/p&gt;</description></item><item><title>Java to Go: #5. 프로젝트 구조와 생태계</title><link>https://philipjkim.cc/posts/20260121-java-to-go-guide-05/</link><pubDate>Wed, 21 Jan 2026 12:28:40 +0900</pubDate><guid>https://philipjkim.cc/posts/20260121-java-to-go-guide-05/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;지금까지 Go의 철학, 문법, 에러 처리, 동시성을 살펴봤다. 이제 실제 프로젝트를 시작하는 데 필요한 것들을 다룬다. Maven/Gradle에서 Go Modules로, JUnit에서 testing 패키지로, Spring에서 Go 라이브러리 조합으로 전환하는 방법을 알아보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-go-modules-기초"&gt;1. Go Modules 기초&lt;/h2&gt;
&lt;h3 id="gomod와-gosum"&gt;go.mod와 go.sum&lt;/h3&gt;
&lt;p&gt;Go 1.11부터 도입된 Go Modules가 공식 의존성 관리 시스템이다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 새 모듈 초기화&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir myproject &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; myproject
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go mod init github.com/username/myproject
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;생성된 &lt;code&gt;go.mod&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;module github.com/username/myproject

go 1.21
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;의존성을 추가하면:&lt;/p&gt;</description></item><item><title>Java to Go: #4. 동시성: Goroutine과 Channel</title><link>https://philipjkim.cc/posts/20260121-java-to-go-guide-04/</link><pubDate>Wed, 21 Jan 2026 12:28:36 +0900</pubDate><guid>https://philipjkim.cc/posts/20260121-java-to-go-guide-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Go의 동시성 모델은 Java와 근본적으로 다르다. &lt;code&gt;Thread&lt;/code&gt;, &lt;code&gt;synchronized&lt;/code&gt;, &lt;code&gt;ExecutorService&lt;/code&gt;에 익숙한 개발자라면 처음엔 낯설겠지만, Go의 방식이 얼마나 우아한지 금방 알게 될 것이다. &amp;ldquo;공유 메모리로 통신하지 말고, 통신으로 메모리를 공유하라&amp;quot;는 Go의 철학을 이해해보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-goroutine-경량-스레드"&gt;1. Goroutine: 경량 스레드&lt;/h2&gt;
&lt;h3 id="go-키워드-하나로-시작하는-동시성"&gt;go 키워드 하나로 시작하는 동시성&lt;/h3&gt;
&lt;p&gt;Java에서 새 스레드를 만들려면:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello from thread&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 또는 ExecutorService&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ExecutorService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Executors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello from thread pool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go에서는 &lt;code&gt;go&lt;/code&gt; 키워드 하나면 된다:&lt;/p&gt;</description></item><item><title>Java to Go: #3. 에러 처리와 Null 안전성</title><link>https://philipjkim.cc/posts/20260121-java-to-go-guide-03/</link><pubDate>Wed, 21 Jan 2026 12:28:33 +0900</pubDate><guid>https://philipjkim.cc/posts/20260121-java-to-go-guide-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Java 개발자가 Go를 배울 때 가장 큰 패러다임 전환이 필요한 부분이 에러 처리다. &lt;code&gt;try-catch&lt;/code&gt;에 익숙한 우리에게 &lt;code&gt;if err != nil&lt;/code&gt;의 반복은 처음엔 원시적으로 느껴진다. 하지만 이 방식에는 명확한 철학이 있다. 이번 편에서 그 철학을 이해하고, Go 방식의 에러 처리에 익숙해져 보자.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-java의-exception-vs-go의-error"&gt;1. Java의 Exception vs Go의 error&lt;/h2&gt;
&lt;h3 id="근본적인-차이"&gt;근본적인 차이&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt; 예외는 &amp;ldquo;특별한 상황&amp;quot;이다. 정상 흐름과 분리되어 throw/catch로 처리된다.&lt;/p&gt;</description></item><item><title>Java to Go: #2. 문법 전환 가이드: Java 코드를 Go로 옮기기</title><link>https://philipjkim.cc/posts/20260121-java-to-go-guide-02/</link><pubDate>Wed, 21 Jan 2026 12:28:30 +0900</pubDate><guid>https://philipjkim.cc/posts/20260121-java-to-go-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Part 1에서 Go의 철학과 언제 Go를 선택해야 하는지 살펴봤다. 이제 실제 코드 레벨에서 Java와 Go가 어떻게 다른지 1:1로 비교해보자. Java 문법에 익숙한 개발자가 Go 코드를 읽고 쓸 수 있도록 핵심 차이점을 정리한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-변수-선언과-타입-시스템"&gt;1. 변수 선언과 타입 시스템&lt;/h2&gt;
&lt;h3 id="기본-변수-선언"&gt;기본 변수 선언&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;14159&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Go:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.14159&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go에서는 타입이 변수명 뒤에 온다. 처음엔 어색하지만, 복잡한 타입 선언에서는 오히려 읽기 쉽다.&lt;/p&gt;</description></item><item><title>Java to Go: #1. Go 첫인상: 철학과 패러다임의 차이</title><link>https://philipjkim.cc/posts/20260121-java-to-go-guide-01/</link><pubDate>Wed, 21 Jan 2026 12:28:27 +0900</pubDate><guid>https://philipjkim.cc/posts/20260121-java-to-go-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Java를 수년간 써온 개발자가 Go를 처음 접하면 당혹스럽다. &amp;ldquo;왜 이렇게 만들었지?&amp;ldquo;라는 생각이 끊이지 않는다. 이 글에서는 Go의 설계 철학을 이해하고, Java에서 Go로의 전환이 언제 적절한지 판단할 수 있는 기준을 제시한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-go의-설계-철학-less-is-more"&gt;1. Go의 설계 철학: &amp;ldquo;Less is more&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;Go는 2009년 Google에서 Robert Griesemer, Rob Pike, Ken Thompson이 만들었다. 세 사람 모두 수십 년 경력의 시스템 프로그래머로, C++의 복잡성에 지쳐 있었다. Go의 탄생 배경 자체가 &amp;ldquo;복잡함에 대한 반발&amp;quot;이다.&lt;/p&gt;</description></item><item><title>Go to Rust: #10. 프로젝트 관리와 실전 패턴</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-10/</link><pubDate>Mon, 19 Jan 2026 16:10:08 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-10/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;이 마지막 섹션에서는 실제 Rust 프로젝트를 구성하고 관리하는 방법을 학습합니다. Cargo 심화, 모듈 시스템, 테스트, 그리고 Go 코드를 Rust로 포팅할 때 유용한 패턴들을 다룹니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="101-cargo-심화"&gt;10.1 Cargo 심화&lt;/h2&gt;
&lt;h3 id="cargotoml-상세-설정"&gt;Cargo.toml 상세 설정&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;myapp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.1.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2021&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Your Name &amp;lt;you@example.com&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;A sample Rust application&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;license&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MIT&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/you/myapp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cli&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;categories&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;command-line-utilities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;serde&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;derive&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tokio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;full&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dev-dependencies&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;criterion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.5&amp;#34;&lt;/span&gt; &lt;span class="c"&gt;# 벤치마크용&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;build-dependencies&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt; &lt;span class="c"&gt;# 빌드 스크립트용&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;serde_json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;full&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;yaml&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;myapp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;src/main.rs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mytool&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;src/bin/mytool.rs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;release&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;opt-level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;lto&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="의존성-관리"&gt;의존성 관리&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 버전 지정&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;serde&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt; &lt;span class="c"&gt;# ^1.0.0 (1.x.x)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;serde&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;=1.0.104&amp;#34;&lt;/span&gt; &lt;span class="c"&gt;# 정확히 1.0.104&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;serde&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;gt;=1.0, &amp;lt;2.0&amp;#34;&lt;/span&gt; &lt;span class="c"&gt;# 범위&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Git 의존성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;mylib&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/user/mylib&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;mylib&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/user/mylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dev&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;mylib&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/user/mylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;v1.0.0&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;mylib&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/user/mylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rev&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;abc123&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 로컬 경로&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;mylib&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;../mylib&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 선택적 의존성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;serde_json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;optional&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 피처 플래그&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tokio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rt-multi-thread&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;macros&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="go-modules와-비교"&gt;Go modules와 비교&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Go: go.mod
module github.com/user/myapp

go 1.21

require (
 github.com/gin-gonic/gin v1.9.0
 github.com/lib/pq v1.10.0
)
&lt;/code&gt;&lt;/pre&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Rust: Cargo.toml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;myapp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.1.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2021&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;axum&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.7&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;sqlx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.7&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;postgres&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;측면&lt;/th&gt;
 &lt;th&gt;Go&lt;/th&gt;
 &lt;th&gt;Cargo&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;의존성 파일&lt;/td&gt;
 &lt;td&gt;go.mod&lt;/td&gt;
 &lt;td&gt;Cargo.toml&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;락 파일&lt;/td&gt;
 &lt;td&gt;go.sum&lt;/td&gt;
 &lt;td&gt;Cargo.lock&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;저장소&lt;/td&gt;
 &lt;td&gt;분산 (GitHub 등)&lt;/td&gt;
 &lt;td&gt;중앙 (crates.io)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;버전 관리&lt;/td&gt;
 &lt;td&gt;Git 태그&lt;/td&gt;
 &lt;td&gt;SemVer&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="피처-플래그"&gt;피처 플래그&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;std&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;std&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;alloc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;serde&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;serde_json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;full&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;yaml&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;toml&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 조건부 컴파일
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#[cfg(feature = &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;json&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#[cfg(feature = &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;std&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;collections&lt;/span&gt;::&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#[cfg(not(feature = &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;std&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;))]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;::&lt;span class="n"&gt;collections&lt;/span&gt;::&lt;span class="n"&gt;BTreeMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="102-모듈-시스템"&gt;10.2 모듈 시스템&lt;/h2&gt;
&lt;h3 id="파일-시스템과-모듈-구조"&gt;파일 시스템과 모듈 구조&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;myapp/
├── Cargo.toml
└── src/
 ├── main.rs # 바이너리 루트
 ├── lib.rs # 라이브러리 루트
 ├── config.rs # config 모듈
 ├── utils/
 │ ├── mod.rs # utils 모듈
 │ ├── helpers.rs # utils::helpers
 │ └── format.rs # utils::format
 └── handlers/
 ├── mod.rs
 ├── user.rs
 └── post.rs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;lib.rs:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Go to Rust: #9. 비동기 프로그래밍</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-09/</link><pubDate>Mon, 19 Jan 2026 16:09:59 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-09/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Rust의 &lt;code&gt;async/await&lt;/code&gt;는 Go의 goroutine과 근본적으로 다른 접근 방식입니다. Go는 런타임이 모든 것을 관리하지만, Rust는 명시적인 비동기 모델을 사용합니다. 이 섹션에서는 Rust의 비동기 생태계와 Tokio 런타임을 학습합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="91-비동기의-필요성"&gt;9.1 비동기의 필요성&lt;/h2&gt;
&lt;h3 id="동기-vs-비동기"&gt;동기 vs 비동기&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;동기 (Synchronous):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 순차적 실행, 블로킹
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fetch_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://api.example.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 대기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;비동기 (Asynchronous):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 대기 중 다른 작업 가능
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fetch_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://api.example.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 양보
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="go의-goroutine-모델"&gt;Go의 goroutine 모델&lt;/h3&gt;
&lt;p&gt;Go는 런타임이 내장되어 있어 동기 코드처럼 보이지만 비동기로 동작합니다:&lt;/p&gt;</description></item><item><title>Go to Rust: #8. 동시성 프로그래밍</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-08/</link><pubDate>Mon, 19 Jan 2026 16:09:50 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-08/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Rust의 &amp;ldquo;Fearless Concurrency&amp;quot;는 컴파일 타임에 데이터 레이스를 방지합니다. Go의 고루틴과 채널에 익숙하다면, Rust의 동시성 모델이 어떻게 다른지 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="81-rust의-동시성-철학"&gt;8.1 Rust의 동시성 철학&lt;/h2&gt;
&lt;h3 id="fearless-concurrency란"&gt;&amp;ldquo;Fearless Concurrency&amp;quot;란?&lt;/h3&gt;
&lt;p&gt;Go의 철학: &amp;ldquo;Don&amp;rsquo;t communicate by sharing memory; share memory by communicating.&amp;rdquo;
Rust의 철학: &amp;ldquo;Fearless Concurrency through ownership.&amp;rdquo;&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Go&lt;/th&gt;
 &lt;th&gt;Rust&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;런타임에 데이터 레이스 감지 (&lt;code&gt;-race&lt;/code&gt;)&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;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;공유 메모리도 가능 (주의 필요)&lt;/td&gt;
 &lt;td&gt;공유 메모리도 안전하게 사용 가능&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="데이터-레이스의-세-가지-조건"&gt;데이터 레이스의 세 가지 조건&lt;/h3&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;p&gt;&lt;strong&gt;Rust는 소유권 규칙으로 이를 컴파일 타임에 방지합니다:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Go to Rust: #7. 컬렉션과 이터레이터</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-07/</link><pubDate>Mon, 19 Jan 2026 16:09:42 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-07/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Rust의 컬렉션은 Go와 유사한 기능을 제공하지만, 소유권 시스템과 결합되어 더 안전합니다. 특히 이터레이터 시스템은 Go의 &lt;code&gt;range&lt;/code&gt;보다 훨씬 강력하며, 함수형 프로그래밍 스타일을 지원합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="71-표준-컬렉션-개요"&gt;7.1 표준 컬렉션 개요&lt;/h2&gt;
&lt;h3 id="go와-rust-컬렉션-매핑"&gt;Go와 Rust 컬렉션 매핑&lt;/h3&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Go&lt;/th&gt;
 &lt;th&gt;Rust&lt;/th&gt;
 &lt;th&gt;설명&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;[]T&lt;/code&gt; (slice)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;동적 배열&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;[N]T&lt;/code&gt; (array)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;[T; N]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;고정 배열&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;소유된 문자열&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;string&lt;/code&gt; (리터럴)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;&amp;amp;str&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;문자열 슬라이스&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;map[K]V&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;HashMap&amp;lt;K, V&amp;gt;&lt;/code&gt;&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;HashSet&amp;lt;T&amp;gt;&lt;/code&gt;&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;BTreeMap&amp;lt;K, V&amp;gt;&lt;/code&gt;&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;BTreeSet&amp;lt;T&amp;gt;&lt;/code&gt;&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;VecDeque&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;양방향 큐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;container/list&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;LinkedList&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;이중 연결 리스트&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="72-vec-심화"&gt;7.2 Vec&lt;T&gt; 심화&lt;/h2&gt;
&lt;h3 id="생성-방법들"&gt;생성 방법들&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 빈 벡터
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// vec! 매크로 (가장 흔함)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 같은 값으로 초기화
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 용량 미리 할당
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;::&lt;span class="n"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 이터레이터에서 수집
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// from_iter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;::&lt;span class="n"&gt;from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="기본-연산"&gt;기본 연산&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 추가
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 끝에 추가
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 인덱스에 삽입: [0, 1, 2, 3, 4]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 제거
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 끝에서 제거, Option&amp;lt;T&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 인덱스에서 제거, T
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 조건에 맞는 것만 유지
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 접근
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 패닉 가능
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Option&amp;lt;&amp;amp;T&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Option&amp;lt;&amp;amp;T&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Option&amp;lt;&amp;amp;T&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 수정
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="go의-append-vs-rust의-push"&gt;Go의 append vs Rust의 push&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Go: append는 새 슬라이스 반환 가능&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 재할당 가능&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 용량 확인&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Rust: push는 in-place 수정
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// v가 mut이어야 함
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 용량
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 추가 용량 확보
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shrink_to_fit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 불필요한 용량 해제
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="소유권과-vec"&gt;소유권과 Vec&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 인덱스 접근은 빌림
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 불변 빌림
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// for 루프는 소유권 이동 또는 빌림 선택 가능
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// v의 소유권 이동, s: String
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// v는 더 이상 사용 불가
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// v 빌림, s: &amp;amp;String
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// v 여전히 사용 가능
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 가변 빌림, s: &amp;amp;mut String
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="슬라이싱"&gt;슬라이싱&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 슬라이스 참조
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// [2, 3, 4]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 전체
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 가변 슬라이스
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// v는 [1, 20, 3, 4, 5]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="73-문자열-처리"&gt;7.3 문자열 처리&lt;/h2&gt;
&lt;h3 id="string-vs-str"&gt;String vs &amp;amp;str&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// String: 힙에 할당된 소유 문자열
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;owned&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;owned&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;owned&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_owned&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// &amp;amp;str: 문자열 슬라이스 (빌림)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;borrowed&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 정적 문자열
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;borrowed&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;owned&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// String에서 빌림
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;borrowed&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;owned&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 부분 슬라이스
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;특성&lt;/th&gt;
 &lt;th&gt;&lt;code&gt;String&lt;/code&gt;&lt;/th&gt;
 &lt;th&gt;&lt;code&gt;&amp;amp;str&lt;/code&gt;&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;/td&gt;
 &lt;td&gt;없음 (빌림)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;힙 할당&lt;/td&gt;
 &lt;td&gt;예&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;/td&gt;
 &lt;td&gt;불가능&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;크기&lt;/td&gt;
 &lt;td&gt;가변&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;/td&gt;
 &lt;td&gt;대부분&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="utf-8-인코딩"&gt;UTF-8 인코딩&lt;/h3&gt;
&lt;p&gt;Rust 문자열은 항상 유효한 UTF-8입니다:&lt;/p&gt;</description></item><item><title>Go to Rust: #6. 에러 처리</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-06/</link><pubDate>Mon, 19 Jan 2026 16:09:35 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-06/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Rust의 에러 처리는 Go와 철학적으로 유사합니다. 두 언어 모두 예외(exception)를 사용하지 않고 명시적인 에러 반환을 선호합니다. 하지만 Rust는 타입 시스템을 통해 에러 처리를 강제하여, Go에서 흔히 발생하는 &amp;ldquo;에러 무시&amp;rdquo; 문제를 원천 차단합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="61-rust의-에러-처리-철학"&gt;6.1 Rust의 에러 처리 철학&lt;/h2&gt;
&lt;h3 id="복구-가능한-에러-vs-복구-불가능한-에러"&gt;복구 가능한 에러 vs 복구 불가능한 에러&lt;/h3&gt;
&lt;p&gt;Rust는 에러를 두 가지로 구분합니다:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;복구 가능한 에러 (Recoverable)&lt;/strong&gt;: &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt;&lt;/p&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;p&gt;&lt;strong&gt;복구 불가능한 에러 (Unrecoverable)&lt;/strong&gt;: &lt;code&gt;panic!&lt;/code&gt;&lt;/p&gt;</description></item><item><title>Go to Rust: #5. 트레이트와 제네릭</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-05/</link><pubDate>Mon, 19 Jan 2026 09:56:15 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-05/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;트레이트(Trait)는 Rust의 다형성 메커니즘입니다. Go의 인터페이스와 비슷한 역할을 하지만, 구현 방식과 기능에서 큰 차이가 있습니다. 이 섹션에서는 트레이트와 제네릭을 Go와 비교하며 깊이 있게 학습합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="51-트레이트-기초"&gt;5.1 트레이트 기초&lt;/h2&gt;
&lt;h3 id="트레이트란"&gt;트레이트란?&lt;/h3&gt;
&lt;p&gt;트레이트는 타입이 구현해야 하는 메서드 집합을 정의합니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 트레이트 정의
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;trait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 타입 정의
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Article&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 트레이트 구현
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Article&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;summarize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s"&gt; by &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="go-interface-vs-rust-trait"&gt;Go interface vs Rust trait&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;가장 큰 차이: 암묵적 vs 명시적&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Go to Rust: #4. 구조체, 열거형, 패턴 매칭</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-04/</link><pubDate>Mon, 19 Jan 2026 09:56:10 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-04/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;이 섹션에서는 Rust에서 데이터를 구조화하는 방법을 학습합니다. Go 개발자에게 구조체는 익숙하겠지만, Rust의 열거형(enum)은 Go의 &lt;code&gt;const&lt;/code&gt;/&lt;code&gt;iota&lt;/code&gt;보다 훨씬 강력하며, 패턴 매칭은 &lt;code&gt;switch&lt;/code&gt;문을 완전히 새로운 수준으로 끌어올립니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="41-구조체-struct"&gt;4.1 구조체 (Struct)&lt;/h2&gt;
&lt;h3 id="구조체-정의와-인스턴스-생성"&gt;구조체 정의와 인스턴스 생성&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 구조체 정의
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sign_in_count&lt;/span&gt;: &lt;span class="kt"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;: &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 인스턴스 생성
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;: &lt;span class="nb"&gt;String&lt;/span&gt;::&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alice@example.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sign_in_count&lt;/span&gt;: &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;: &lt;span class="nc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 필드 접근
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Username: &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Go struct와의 문법 비교:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Go to Rust: #3. 소유권과 빌림 (Ownership &amp; Borrowing)</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-03/</link><pubDate>Mon, 19 Jan 2026 09:56:03 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-03/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;소유권(Ownership)은 Rust의 가장 독특하고 중요한 개념입니다. Go의 가비지 컬렉션과 완전히 다른 접근 방식으로, 컴파일 타임에 메모리 안전성을 보장합니다. 이 섹션은 Rust 학습에서 가장 중요한 부분이므로 충분한 시간을 들여 이해하시기 바랍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="31-소유권이란-무엇인가"&gt;3.1 소유권이란 무엇인가?&lt;/h2&gt;
&lt;h3 id="메모리-관리의-세-가지-방식"&gt;메모리 관리의 세 가지 방식&lt;/h3&gt;
&lt;p&gt;프로그래밍 언어들은 메모리를 관리하는 방식이 다릅니다:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 가비지 컬렉션 (Go, Java, Python, JavaScript)&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Go: GC가 알아서 정리&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 함수 종료 후 GC가 언젠가 정리&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;장점: 개발자가 메모리 관리 신경 안 써도 됨&lt;/li&gt;
&lt;li&gt;단점: GC 일시 정지, 예측 불가능한 지연, 메모리 오버헤드&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 수동 관리 (C, C++)&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Go to Rust: #2. 기본 문법과 타입 시스템</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-02/</link><pubDate>Mon, 19 Jan 2026 09:55:56 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-02/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;이 섹션에서는 Rust의 기본 문법을 Go와 비교하며 학습합니다. Go 개발자에게 익숙한 개념들이 Rust에서 어떻게 표현되는지, 그리고 Rust만의 독특한 특징은 무엇인지 알아봅니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="21-변수와-가변성"&gt;2.1 변수와 가변성&lt;/h2&gt;
&lt;h3 id="let-vs-var-불변이-기본인-이유"&gt;let vs var: 불변이 기본인 이유&lt;/h3&gt;
&lt;p&gt;Go에서는 변수가 기본적으로 가변(mutable)입니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Go: 기본이 가변&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// OK&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Rust에서는 &lt;strong&gt;불변(immutable)이 기본&lt;/strong&gt;입니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Rust: 기본이 불변
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 컴파일 에러!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;왜 불변이 기본일까요?&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Go to Rust: #1. 시작하기 - Rust 소개와 환경 설정</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-01/</link><pubDate>Mon, 19 Jan 2026 09:55:50 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-01/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Go 개발자로서 새로운 언어를 배우려면 먼저 그 언어가 왜 존재하는지, 어떤 문제를 해결하려고 하는지 이해하는 것이 중요합니다. 이 섹션에서는 Rust의 설계 철학을 Go와 비교하며 이해하고, 개발 환경을 설정한 뒤 첫 프로젝트를 생성해봅니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="11-rust는-왜-배워야-하는가"&gt;1.1 Rust는 왜 배워야 하는가?&lt;/h2&gt;
&lt;h3 id="rust의-설계-철학"&gt;Rust의 설계 철학&lt;/h3&gt;
&lt;p&gt;Rust는 Mozilla Research에서 시작되어 2015년에 1.0이 릴리스된 시스템 프로그래밍 언어입니다. Rust의 핵심 목표는 세 가지입니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;안전성(Safety)&lt;/strong&gt;: 메모리 안전성을 컴파일 타임에 보장&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;속도(Speed)&lt;/strong&gt;: C/C++에 필적하는 성능&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;동시성(Concurrency)&lt;/strong&gt;: 데이터 레이스 없는 동시성 프로그래밍&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Go도 비슷한 시기(2009년)에 등장했지만, 설계 목표가 다릅니다:&lt;/p&gt;</description></item><item><title>Go to Rust: #0. Overview</title><link>https://philipjkim.cc/posts/20260119-go-to-rust-guide-00/</link><pubDate>Mon, 19 Jan 2026 08:36:02 +0900</pubDate><guid>https://philipjkim.cc/posts/20260119-go-to-rust-guide-00/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 Claude Opus 4.5 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;From Go to Rust: A Comprehensive Guide for Go Developers&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Go 언어에 익숙한 개발자가 Rust를 체계적으로 학습할 수 있도록 구성된 가이드입니다. Go와의 비교를 통해 새로운 개념을 빠르게 이해하고, 실무에서 바로 적용할 수 있는 실용적인 내용을 담았습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="가이드의-특징"&gt;가이드의 특징&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Go 개발자 관점&lt;/strong&gt;: 모든 개념을 Go와 비교하여 설명, 익숙한 개념에서 출발&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실용적 접근&lt;/strong&gt;: 이론보다 실제 코드 예제 중심&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;점진적 난이도&lt;/strong&gt;: 기초부터 고급까지 체계적 진행&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실전 지향&lt;/strong&gt;: 각 섹션에 Go ↔ Rust 코드 변환 예제 포함&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;함정 주의&lt;/strong&gt;: Go 개발자가 흔히 하는 실수와 해결법 명시&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="섹션별-상세-목차"&gt;섹션별 상세 목차&lt;/h2&gt;
&lt;hr&gt;
&lt;h2 id="section-1-시작하기---rust-소개와-환경-설정"&gt;Section 1: 시작하기 - Rust 소개와 환경 설정&lt;/h2&gt;
&lt;p&gt;Rust의 설계 철학을 이해하고, Go와의 포지셔닝 차이를 명확히 합니다. 개발 환경을 설정하고 첫 프로젝트를 생성합니다.&lt;/p&gt;</description></item><item><title>르네상스 개발자: AWS CTO Werner Vogels 가 제시하는 AI 시대 개발자의 5가지 자질</title><link>https://philipjkim.cc/posts/20251219-the-renaissance-developer/</link><pubDate>Fri, 19 Dec 2025 13:47:59 +0900</pubDate><guid>https://philipjkim.cc/posts/20251219-the-renaissance-developer/</guid><description>&lt;p&gt;&lt;em&gt;Claude Opus 4.5 을 활용해 작성한 글입니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="서론-왜-르네상스-개발자인가"&gt;서론: 왜 &amp;lsquo;르네상스 개발자&amp;rsquo;인가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;AI가 내 직업을 빼앗을까? 아마도, 그럴 것이다. 하지만, 이 질문을 재구성하는 게 좋겠다. AI가 개발자를 쓸모없게 만들까? 절대로 아니다. 진화한다면.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;— Werner Vogels, AWS CTO&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;보겔스 박사는 개발자가 어셈블리어, 구조적 프로그래밍, 객체 지향 언어, 클라우드 도입 등 시대에 따라 등장했던 새로운 도구를 통해 더 훌륭한 엔지니어로 진화해왔다고 강조합니다. AI 역시 마찬가지입니다.&lt;/p&gt;
&lt;p&gt;제프 베조스의 말을 인용하며, 보겔스 박사는 현재를 &amp;ldquo;우주 여행, 인공지능, 로봇공학 등 여러 황금기가 동시에 만나는 시대&amp;quot;라고 정의했습니다. 이러한 돌파구들이 서로를 강화하며 발전하는 모습이 바로 르네상스 시대와 닮아있다는 것입니다.&lt;/p&gt;</description></item><item><title>TotT: 도메인 객체로 변경에 유연한 코드 작성하기</title><link>https://philipjkim.cc/posts/20251204-write-resilient-code-using-domain-objects/</link><pubDate>Thu, 04 Dec 2025 10:12:26 +0900</pubDate><guid>https://philipjkim.cc/posts/20251204-write-resilient-code-using-domain-objects/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2024/09/write-change-resilient-code-with-domain.html"&gt;https://testing.googleblog.com/2024/09/write-change-resilient-code-with-domain.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;제품의 요구사항은 자주 바뀔 수 있지만, 그 근본적인 아이디어는 보통 천천히 변합니다. 이는 흥미로운 통찰로 이어집니다. 만약 우리가 제품의 근본적인 아이디어와 일치하는 코드를 작성한다면, 그 코드는 미래의 제품 변경에도 살아남을 가능성이 더 높습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;도메인 객체(Domain objects)&lt;/strong&gt; 는 우리 코드 안에서 제품의 근본적인 아이디어와 일치하는 구성 요소(클래스나 인터페이스 같은)입니다. 제품 요구사항에 따른 원하는 행동(&amp;ldquo;텍스트를 흰색으로 설정&amp;rdquo;)에 맞춰 코드를 작성하는 대신, 우리는 기저에 깔린 아이디어(&amp;ldquo;텍스트 색상 설정&amp;rdquo;)에 맞춥니다.&lt;/p&gt;</description></item><item><title>TotT: SMURF - 테스트 피라미드를 넘어서</title><link>https://philipjkim.cc/posts/20251203-smurf-beyond-test-pyramid/</link><pubDate>Wed, 03 Dec 2025 07:56:35 +0900</pubDate><guid>https://philipjkim.cc/posts/20251203-smurf-beyond-test-pyramid/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2023/09/else-nuances.html"&gt;https://testing.googleblog.com/2023/09/else-nuances.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;테스트 피라미드는 테스트 스위트의 발전을 안내하는 정석적인 경험 법칙(heuristic)입니다. 이는 단순한 메시지를 전달합니다. 통합 테스트보다 더 많은 단위 테스트를 선호하고, 엔드 투 엔드(end-to-end) 테스트보다 더 많은 통합 테스트를 선호하라는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXej7qoczQGJ-CPfJ4LIt5l-nWvaGd9WXLPLEslXGzE5ih0C_jdCcK1Ds_QOpvLqD03hbNZXbnGvCNQ8Oy5cJOPZ5xR1qRT8pxp-CDF6KaFX1Dch9FOZjtbmBxzWK_y0OIjSnH8J6wYXV7obtmUaTX9BG5XW=s16000?key=9eA8neKwqCIgXwSUpDaoQQ" alt="test_pyramid"&gt;&lt;/p&gt;
&lt;p&gt;테스트 피라미드는 유용하지만, 테스트 스위트가 커지고 어려운 트레이드오프(trade-off) 상황에 직면했을 때 필요한 세부 정보가 부족합니다. &lt;strong&gt;테스트 스위트를 확장하려면 테스트 피라미드 그 이상이 필요합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SMURF 는 테스트 스위트의 균형을 잡을 때 고려해야 할 트레이드오프들을 기억하기 쉬운 약어입니다:&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>TotT: Else 문의 미묘한 차이</title><link>https://philipjkim.cc/posts/20251203-tott-else-nuances/</link><pubDate>Wed, 03 Dec 2025 07:49:25 +0900</pubDate><guid>https://philipjkim.cc/posts/20251203-tott-else-nuances/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2023/09/else-nuances.html"&gt;https://testing.googleblog.com/2023/09/else-nuances.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;함수가 &lt;code&gt;if&lt;/code&gt; 문에서 일찍 종료(return)되는 경우, &lt;code&gt;else&lt;/code&gt; 절을 사용하는 것과 사용하지 않는 것은 동작상으로는 동일합니다. 하지만 &lt;code&gt;else&lt;/code&gt; 절과 가드 절(guard clause, &lt;code&gt;else&lt;/code&gt;가 없는 형태)을 적절히 사용하면 코드를 읽는 사람에게 의도를 더 명확하게 전달할 수 있습니다.&lt;/p&gt;
&lt;p&gt;함수 구조를 잡는 데 도움이 될 만한 다음 가이드라인들을 고려해 보세요:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;특수한 케이스를 미리 처리하기 위해 가드 절(Guard clause)을 사용하세요.&lt;/strong&gt; 이렇게 하면 나머지 코드는 핵심 로직에 집중할 수 있습니다. 가드 절은 조건을 확인하고 충족되지 않을 경우 빠르게 실패하거나 일찍 반환하여 중첩(nesting)을 줄여줍니다.&lt;/p&gt;</description></item><item><title>Go 웹 애플리케이션에서 static contents를 서빙하는 표준 패턴</title><link>https://philipjkim.cc/posts/20251126-go-static-contents-serving-pattern/</link><pubDate>Wed, 26 Nov 2025 08:07:14 +0900</pubDate><guid>https://philipjkim.cc/posts/20251126-go-static-contents-serving-pattern/</guid><description>&lt;p&gt;복잡한 nginx 설정 파일, CDN 연동, 그리고 정적 파일 경로 문제로 골머리를 앓으신 적 있으신가요? Go 표준 라이브러리만으로도 정적 파일(CSS, JavaScript, 이미지 등)을 우아하게 서빙할 수 있습니다. 이번 글에서는 &lt;strong&gt;Go 웹 애플리케이션에서 static contents를 서빙하는 표준 패턴&lt;/strong&gt;을 알아보겠습니다. &lt;code&gt;http.FileServer&lt;/code&gt;, &lt;code&gt;http.Dir&lt;/code&gt;, 그리고 &lt;code&gt;http.StripPrefix&lt;/code&gt;의 조합만으로 프로덕션 레벨의 static file serving을 구현할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;이 글은 GOTH Stack 웹 애플리케이션 개발 과정에서 발견한 패턴을 Claude Sonnet 을 이용해 정리한 것입니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="문제-상황-url과-파일-시스템-경로의-불일치"&gt;문제 상황: URL과 파일 시스템 경로의 불일치&lt;/h2&gt;
&lt;p&gt;웹 애플리케이션을 개발하다 보면 다음과 같은 구조를 자주 마주치게 됩니다:&lt;/p&gt;</description></item><item><title>Go 웹 개발의 새로운 파도: GOTH 스택 (Go, Templ, HTMX) 시작하기</title><link>https://philipjkim.cc/posts/20251124-introduction-to-goth/</link><pubDate>Mon, 24 Nov 2025 18:12:06 +0900</pubDate><guid>https://philipjkim.cc/posts/20251124-introduction-to-goth/</guid><description>&lt;p&gt;복잡한 리액트(React) 상태 관리, 비대해진 &lt;code&gt;node_modules&lt;/code&gt;, 그리고 끝없는 JSON 직렬화/역직렬화에 지치셨나요? 백엔드 개발자에게 가장 편안한 언어인 &lt;strong&gt;Go&lt;/strong&gt;를 사용하면서도, 현대적인 SPA(Single Page Application) 같은 사용자 경험을 제공하는 &lt;strong&gt;GOTH 스택&lt;/strong&gt;을 소개합니다. GOTH(Go + Templ + HTMX) 스택은 최근 Go 커뮤니티에서 &lt;strong&gt;&amp;ldquo;다시 재미있게 웹 개발을 하자(Make Web Dev Fun Again)&amp;rdquo;&lt;/strong&gt; 는 흐름과 함께 큰 주목을 받고 있습니다. 복잡한 JS 프레임워크 없이 Go의 강점인 단순함과 성능을 그대로 살릴 수 있는 것이 특징입니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;이 글은 요청에 대한 Gemini 3 Pro 의 답변입니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Go: `net/http` vs. `chi`</title><link>https://philipjkim.cc/posts/20251124-go-net-http-vs-chi/</link><pubDate>Mon, 24 Nov 2025 17:49:44 +0900</pubDate><guid>https://philipjkim.cc/posts/20251124-go-net-http-vs-chi/</guid><description>&lt;p&gt;Go 언어의 웹 생태계에서 &lt;strong&gt;&lt;code&gt;net/http&lt;/code&gt; (표준 라이브러리)&lt;/strong&gt; 와 &lt;strong&gt;&lt;code&gt;chi&lt;/code&gt;&lt;/strong&gt; 는 가장 &amp;ldquo;Go스러운(Idiomatic)&amp;rdquo; 선택지로 꼽힙니다. Gin이나 Echo 같은 &amp;ldquo;웹 프레임워크&amp;quot;와 달리, chi는 표준 라이브러리의 인터페이스를 그대로 따르면서 부족한 라우팅 기능을 보강해주는 &amp;ldquo;라우터&amp;quot;이기 때문입니다.&lt;/p&gt;
&lt;p&gt;특히 &lt;strong&gt;Go 1.22 버전&lt;/strong&gt;부터 &lt;code&gt;net/http&lt;/code&gt;의 라우팅 기능이 대폭 강화되었기 때문에, 이 둘의 차이를 명확히 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;이 글은 요청에 대한 Gemini 3 Pro 의 답변입니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-nethttp-vs-chi-요약-비교"&gt;1. &lt;code&gt;net/http&lt;/code&gt; vs &lt;code&gt;chi&lt;/code&gt; 요약 비교&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th style="text-align: left"&gt;특징&lt;/th&gt;
 &lt;th style="text-align: left"&gt;&lt;code&gt;net/http&lt;/code&gt; (Go 1.22+)&lt;/th&gt;
 &lt;th style="text-align: left"&gt;&lt;code&gt;chi&lt;/code&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;외부 의존성&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;없음 (Zero dependency)&lt;/td&gt;
 &lt;td style="text-align: left"&gt;있음 (But 매우 가벼움)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;인터페이스&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;&lt;code&gt;http.Handler&lt;/code&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;&lt;code&gt;http.Handler&lt;/code&gt; (100% 호환)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;라우팅 (Path)&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;지원 (&lt;code&gt;/items/{id}&lt;/code&gt;)&lt;/td&gt;
 &lt;td style="text-align: left"&gt;강력하게 지원 (Regex 등 포함)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;메서드 매칭&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;지원 (&lt;code&gt;POST /items&lt;/code&gt;)&lt;/td&gt;
 &lt;td style="text-align: left"&gt;지원 (&lt;code&gt;r.Post(&amp;quot;/items&amp;quot;, ...)&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;미들웨어 관리&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;직접 구현 필요 (복잡함)&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;매우 강력하고 간편함 (&lt;code&gt;Use&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;라우팅 그룹&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;직접 구현 필요&lt;/td&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;기본 지원 (&lt;code&gt;Group&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td style="text-align: left"&gt;&lt;strong&gt;속도&lt;/strong&gt;&lt;/td&gt;
 &lt;td style="text-align: left"&gt;빠름&lt;/td&gt;
 &lt;td style="text-align: left"&gt;매우 빠름 (Radix Tree 기반)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="2-왜-앱이-커질수록-chi가-유리한가"&gt;2. 왜 앱이 커질수록 &lt;code&gt;chi&lt;/code&gt;가 유리한가?&lt;/h2&gt;
&lt;p&gt;앱이 복잡해진다는 것은 &lt;strong&gt;엔드포인트가 많아지고&lt;/strong&gt;, &lt;strong&gt;공통 로직(인증, 로깅, CORS 등)이 복잡해진다&lt;/strong&gt;는 뜻입니다. 이 지점에서 chi가 &lt;code&gt;net/http&lt;/code&gt;보다 압도적인 강점을 가집니다.&lt;/p&gt;</description></item><item><title>TotT: 함수 가독성 향상을 위해 추상화를 활용하세요</title><link>https://philipjkim.cc/posts/20251124-tott-abstraction-for-better-readability/</link><pubDate>Mon, 24 Nov 2025 10:02:42 +0900</pubDate><guid>https://philipjkim.cc/posts/20251124-tott-abstraction-for-better-readability/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2023/09/use-abstraction-to-improve-function.html"&gt;https://testing.googleblog.com/2023/09/use-abstraction-to-improve-function.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;아래에 있는 두 가지 &lt;code&gt;createPizza&lt;/code&gt; 함수 버전 중 어느 쪽이 이해하기 더 쉬울까요?&lt;/p&gt;
&lt;h2 id="a-추상화-수준이-혼재된-버전-나쁜-예"&gt;A. 추상화 수준이 혼재된 버전 (나쁜 예)&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createPizza&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Sauce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sauce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Cheese&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Mozzarella&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Veg&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Toppings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vegToppings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Meat&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Toppings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;meatToppings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cookingTemp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cookingTemp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkOvenInterval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getOvenTemp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Baked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cookTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Baked&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Boxed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PutIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sliced&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SlicePizza&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Ready&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="b-일관된-추상화-수준을-가진-버전-좋은-예"&gt;B. 일관된 추상화 수준을 가진 버전 (좋은 예)&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;createPizza&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Sauce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sauce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Cheese&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Mozzarella&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;addToppings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;addToppings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Veg&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Toppings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;vegToppings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Meat&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Toppings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;meatToppings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;heatOven&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bakePizza&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;heatOven&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Oven&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bakePizza&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;oven&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Oven&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pizza&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="추상화-수준을-일치시켜야-하는-이유"&gt;추상화 수준을 일치시켜야 하는 이유&lt;/h2&gt;
&lt;p&gt;여러분은 아마 오른쪽(좋은 예)이 더 쉽다고 답했을 것입니다. 하지만 그 이유는 무엇일까요?&lt;/p&gt;</description></item><item><title>TotT: 관심사 분리? 끝!</title><link>https://philipjkim.cc/posts/20251020-tott-separation-of-concerns/</link><pubDate>Mon, 20 Oct 2025 13:17:31 +0900</pubDate><guid>https://philipjkim.cc/posts/20251020-tott-separation-of-concerns/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2020/12/testing-on-toilet-separation-of.html"&gt;https://testing.googleblog.com/2020/12/testing-on-toilet-separation-of.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;다음 함수는 SpeedyImg라는 API를 사용하여 바이트 배열을 이미지로 디코딩합니다. 다른 팀이 소유한 API를 참조할 때 어떤 유지보수 문제가 발생할 수 있을까요?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;SpeedyImgImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;decodeImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SpeedyImgDecoder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decoders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SpeedyImgOptions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getDefaultConvertOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SpeedyImgDecoder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decoders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SpeedyImgResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decodeResult&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SpeedyImgImage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;decodeResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validateGoodImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;API 호출에 대한 세부 정보가 도메인 로직과 혼합되어 있어&lt;/strong&gt; 코드를 이해하기 어렵게 만들 수 있습니다. 예를 들어, &lt;code&gt;decoder.formatBytes()&lt;/code&gt; 호출은 API에서 필요하지만 바이트가 어떻게 포맷되는지는 도메인 로직과 관련이 없습니다.&lt;/p&gt;</description></item><item><title>번역글: 섣부른 최적화, 아니면... 내가 한 말은 나부터 지켜야 했는데</title><link>https://philipjkim.cc/posts/20251010-premature-optimization/</link><pubDate>Fri, 10 Oct 2025 11:09:21 +0900</pubDate><guid>https://philipjkim.cc/posts/20251010-premature-optimization/</guid><description>&lt;p&gt;원문: Pragmatic Programmers Newsletter (Translated by Google Gemini)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;저는 제 자신의 조언을 무시했고, 하루를 꼬박 날렸으며, 제가 저 자신의 최악의 고객임을 다시 한번 증명하고 말았습니다. (하지만 절망하지 마세요. 글 마지막에 쿠폰 코드가 있습니다.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;저희의 새로운 유통사는 요금을 청구할 항목들을 만들어내는 데 특별한 재능이 있습니다. 그런 항목이 수십 가지나 되죠. 저희는 순수익에 대해 로열티를 지급하기 때문에, 모든 청구 항목을 하나하나 맞춰봐야 합니다. 간단하죠, 안 그런가요?&lt;/p&gt;
&lt;p&gt;문제는 &amp;ldquo;권당&amp;rdquo; 발생하는 비용은 한 정산서에 나오고, &amp;ldquo;총액 공제&amp;rdquo; 항목은 다른 정산서에 나온다는 겁니다. 게다가 각 총액 항목 안에는 어떤 이름표도 없이 뒤죽박죽 섞인 비용들이 숨어있곤 합니다. 저희가 받는 것이라고는 날짜 두어 개와 무작위로 보이는 총액 하나가 전부입니다. 그러면 저희는 그 총액을 특정 비용들의 어떤 조합과 맞춰봐야 하는 것이죠.&lt;/p&gt;</description></item><item><title>TotT: 당신이 소유하지 않은 타입은 목킹(Mock)하지 마세요</title><link>https://philipjkim.cc/posts/20250923-tott-dont-mock-types-you-dont-own/</link><pubDate>Tue, 23 Sep 2025 13:36:30 +0900</pubDate><guid>https://philipjkim.cc/posts/20250923-tott-dont-mock-types-you-dont-own/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2020/07/testing-on-toilet-dont-mock-types-you.html"&gt;https://testing.googleblog.com/2020/07/testing-on-toilet-dont-mock-types-you.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;단위 테스트를 작성할 때, 테스트 대상 코드가 의존하는 타입을 목(mock) 처리하는 것은 일반적인 관행입니다. 하지만, &lt;strong&gt;당신이 소유하지 않은 타입(types you don&amp;rsquo;t own)을 목 처리하는 것은 거의 항상 피해야 합니다.&lt;/strong&gt; 여기서 &amp;ldquo;소유하지 않은 타입&amp;quot;이란 당신의 팀이 작성하거나 관리하지 않는 모든 클래스나 인터페이스를 의미합니다. 여기에는 자바 표준 라이브러리의 &lt;code&gt;URL&lt;/code&gt;이나 서드파티 라이브러리의 &lt;code&gt;JSONObject&lt;/code&gt;와 같은 타입들이 포함됩니다.&lt;/p&gt;
&lt;p&gt;예를 들어, &lt;code&gt;java.net.HttpURLConnection&lt;/code&gt;을 직접 목 처리하는 테스트를 생각해 봅시다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testProcessUrl_connectionFailed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HttpURLConnection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mockConnection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpURLConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResponseCode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;thenThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IOException&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Processor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Processor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockConnection&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 테스트에는 몇 가지 심각한 문제가 있습니다.&lt;/p&gt;</description></item><item><title>아키텍처 비교: VSA vs. Hexagonal Architecture</title><link>https://philipjkim.cc/posts/20250922-vsa-vs-hexagonal/</link><pubDate>Mon, 22 Sep 2025 14:14:05 +0900</pubDate><guid>https://philipjkim.cc/posts/20250922-vsa-vs-hexagonal/</guid><description>&lt;p&gt;이 포스트는 Vertical Slicing Architecture와 Hexagonal Architecture의 개념과 특징을 단순히 나열하는 것을 넘어, 두 아키텍처의 근본적인 설계 철학, 해결하고자 하는 핵심 문제, 그리고 실제 프로젝트에 적용했을 때 발생하는 트레이드오프를 심층적으로 분석하는 것을 목표로 합니다. 각 아키텍처의 태동 배경, 장단점, 이상적인 적용 시나리오를 비교 분석하고, 실제 적용 사례를 통해 이론이 현실에서 어떻게 구현되는지 살펴볼 것입니다. 최종적으로는 독자들이 자신의 프로젝트 맥락과 당면 과제에 가장 적합한 아키텍처를 정보에 입각하여 선택할 수 있도록 실질적인 통찰과 가이드를 제공하고자 합니다.&lt;/p&gt;</description></item><item><title>TotT: 테스트 데이터를 깔끔하게 만드세요</title><link>https://philipjkim.cc/posts/20250922-tott-cleanly-create-test/</link><pubDate>Mon, 22 Sep 2025 09:29:50 +0900</pubDate><guid>https://philipjkim.cc/posts/20250922-tott-cleanly-create-test/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2018/02/testing-on-toilet-cleanly-create-test.html"&gt;https://testing.googleblog.com/2018/02/testing-on-toilet-cleanly-create-test.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;테스트를 작성할 때, 우리는 종종 테스트 대상 코드가 소비할 객체들을 만들어야 합니다. 이 객체들은 진짜 객체일 수도 있고, 페이크(fake)나 목(mock) 객체일 수도 있습니다. 이러한 객체들을 생성하는 코드는 빠르게 지저분해지고, 테스트의 가독성을 해치며, 유지보수를 어렵게 만들 수 있습니다.&lt;/p&gt;
&lt;p&gt;다음 예제를 살펴보겠습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testWriteSecureMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 테스트를 위한 설정&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;42&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Four score and seven years ago...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1a2b3c4d5e&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Abc.123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Abraham Lincoln&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 테스트 실행&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeSecureMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 상태 검증&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Sent secure message with signature 1a2b3c4d5e&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInfo&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 테스트에서 객체 생성은 꽤 장황합니다. 테스트의 핵심 로직을 이해하기 어렵게 만들고, 테스트가 실제로 무엇을 검증하려는지 불분명하게 만듭니다.&lt;/p&gt;</description></item><item><title>TotT: 상태를 변경하는 메서드 호출만 검증하세요</title><link>https://philipjkim.cc/posts/20250919-tott-only-verify-state/</link><pubDate>Fri, 19 Sep 2025 15:26:56 +0900</pubDate><guid>https://philipjkim.cc/posts/20250919-tott-only-verify-state/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2017/01/testing-on-toilet-keep-cause-and-effect.html"&gt;https://testing.googleblog.com/2017/01/testing-on-toilet-keep-cause-and-effect.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;이 테스트에서 어떤 라인을 안전하게 제거할 수 있을까요?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;addPermissionToDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserAuthorizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mockPermissionDb&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;grantPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;READ_ACCESS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 이 메서드 중 하나라도 호출되지 않으면 테스트는 실패합니다.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockUserService&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;isUserActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPermissionDb&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPermissionDb&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;isValidPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;READ_ACCESS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPermissionDb&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;addPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;READ_ACCESS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;정답은 &lt;strong&gt;상태를 변경하지 않는 메서드&lt;/strong&gt;를 검증하는 호출들을 제거할 수 있다는 것입니다.&lt;/p&gt;
&lt;p&gt;다른 객체에 대한 메서드 호출은 다음 두 가지 범주 중 하나에 속합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;상태 변경(State-changing):&lt;/strong&gt; 부작용(side effects)이 있고 테스트 대상 코드 외부의 세상을 바꾸는 메서드. 예: &lt;code&gt;sendEmail()&lt;/code&gt;, &lt;code&gt;saveRecord()&lt;/code&gt;, &lt;code&gt;logAccess()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;비-상태 변경(Non-state-changing):&lt;/strong&gt; 테스트 대상 코드 외부 세상에 대한 정보를 반환하고 아무것도 수정하지 않는 메서드. 예: &lt;code&gt;getUser()&lt;/code&gt;, &lt;code&gt;findResults()&lt;/code&gt;, &lt;code&gt;readFile()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;일반적으로 &lt;strong&gt;상태를 변경하지 않는 메서드가 호출되었는지 검증하는 것은 피해야 합니다.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>AI 툴 소개: GitHub Spec Kit: AI 에이전트 개발을 위한 명세서</title><link>https://philipjkim.cc/posts/20250917-introduction-to-spec-kit/</link><pubDate>Wed, 17 Sep 2025 10:02:47 +0900</pubDate><guid>https://philipjkim.cc/posts/20250917-introduction-to-spec-kit/</guid><description>&lt;p&gt;GitHub Spec Kit은 코드와 상호작용하는 AI 에이전트 및 모델 개발자를 위한 명세(spec) 모음입니다. 이 명세들은 AI 에이전트가 파일 시스템과 상호작용하고, 명령을 실행하며, 사용자와 소통하는 방식에 대한 &lt;strong&gt;모범 사례(best practice)를 정의&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;p&gt;이러한 표준을 채택함으로써, 우리는 AI 에이전트 개발 생태계가 더욱 강력하고 상호 운용 가능하도록 만들 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://github.com/github/spec-kit"&gt;https://github.com/github/spec-kit&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt; (2025-09-17 기준)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-명세-기반-개발이란-무엇입니까"&gt;🤔 명세 기반 개발이란 무엇입니까?&lt;/h2&gt;
&lt;p&gt;명세 기반 개발은 전통적인 소프트웨어 개발의 &lt;strong&gt;판도를 뒤집습니다&lt;/strong&gt;. 수십 년 동안 코드가 왕이었습니다 — 명세는 코딩이라는 &amp;ldquo;진짜 작업&amp;quot;이 시작되면 만들어졌다가 버려지는 비계(scaffolding)에 불과했습니다. 명세 기반 개발은 이를 변화시킵니다: &lt;strong&gt;명세가 실행 가능해지며&lt;/strong&gt;, 단지 구현을 안내하는 것을 넘어 직접 작동하는 구현체를 생성합니다.&lt;/p&gt;</description></item><item><title>TotT: 중첩 줄이기, 복잡도 낮추기</title><link>https://philipjkim.cc/posts/20250917-tott-code-health-reduce-nesting/</link><pubDate>Wed, 17 Sep 2025 09:49:08 +0900</pubDate><guid>https://philipjkim.cc/posts/20250917-tott-code-health-reduce-nesting/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2017/06/code-health-reduce-nesting-reduce.html"&gt;https://testing.googleblog.com/2017/06/code-health-reduce-nesting-reduce.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;과도하게 중첩된 코드는 가독성을 해치고 오류를 발생시키기 쉽습니다. 아래 두 버전의 코드에서 버그를 찾아보세요.&lt;/p&gt;
&lt;p&gt;과도하게 중첩된 코드:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;RPC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetAuthorizedUser&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetEnc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRows&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ParseRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRows&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vals&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;EmptyError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;AuthError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unauthorized&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;wrong encoding&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;RpcError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStatus&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;중첩이 적은 코드:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;RPC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;RpcError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetStatus&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetAuthorizedUser&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;wrong encoding&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetEnc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;AuthError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unauthorized&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRows&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;EmptyError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ParseRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRows&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vals&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;정답&lt;/strong&gt;: &amp;ldquo;wrong encoding&amp;quot;과 &amp;ldquo;unauthorized&amp;rdquo; 에러가 서로 바뀌었습니다. 이 버그는 리팩터링된 버전에서 더 쉽게 발견할 수 있는데, 그 이유는 검사가 에러를 처리하는 부분 바로 옆에서 일어나기 때문입니다.&lt;/p&gt;</description></item><item><title>TotT: 원인과 결과를 명확하게 유지하세요</title><link>https://philipjkim.cc/posts/20250912-tott-keep-cause-and-effect-clear/</link><pubDate>Fri, 12 Sep 2025 12:33:09 +0900</pubDate><guid>https://philipjkim.cc/posts/20250912-tott-keep-cause-and-effect-clear/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2017/01/testing-on-toilet-keep-cause-and-effect.html"&gt;https://testing.googleblog.com/2017/01/testing-on-toilet-keep-cause-and-effect.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;이 테스트가 올바른지 판단할 수 있나요?&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testIncrement_existingKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tally&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;key1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;tally&lt;/code&gt; 객체가 어떻게 설정되었는지 보지 않고는 &lt;strong&gt;알아내는 것이 불가능합니다&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tally&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Before&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tally&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;key1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tally&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;key2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tally&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;key1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tally&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;key1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 200줄 떨어져 있음&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testIncrement_existingKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tally&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;key1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;문제는 &lt;code&gt;key1&lt;/code&gt;의 값을 수정하는 부분이 &lt;em&gt;검증(assertion)으로부터 200줄 이상 떨어져 있다&lt;/em&gt;는 점입니다. 다시 말해, &lt;strong&gt;&amp;lsquo;원인&amp;rsquo;이 &amp;lsquo;결과&amp;rsquo;로부터 너무 멀리 숨겨져 있습니다&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>TotT: 무엇이 좋은 End-to-End 테스트를 만드는가?</title><link>https://philipjkim.cc/posts/20250911-tott-good-e2e-test/</link><pubDate>Thu, 11 Sep 2025 10:33:09 +0900</pubDate><guid>https://philipjkim.cc/posts/20250911-tott-good-e2e-test/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2016/09/testing-on-toilet-what-makes-good-end.html"&gt;https://testing.googleblog.com/2016/09/testing-on-toilet-what-makes-good-end.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;End-to-end(E2E) 테스트는 시스템의 한쪽 끝에서 다른 쪽 끝까지, 그 사이의 모든 것을 블랙박스로 취급하며 전체 시스템을 테스트합니다. E2E 테스트는 시스템 전반에 걸쳐 나타나는 버그를 잡아낼 수 있습니다. 단위 테스트, 통합 테스트와 더불어 E2E 테스트는 균형 잡힌 테스트 식단의 핵심적인 부분이며, 운영 환경과 거의 유사한 상태에서 시스템의 건전성에 대한 확신을 줍니다.&lt;/p&gt;
&lt;p&gt;하지만 안타깝게도, E2E 테스트는 단위 테스트나 통합 테스트보다 느리고, 불안정하며(flaky), 유지보수 비용이 더 비쌉니다. E2E 테스트가 필요한지, 만약 그렇다면 어떻게 작성하는 것이 최선일지 신중하게 고려해야 합니다.&lt;/p&gt;</description></item><item><title>번역글: LLM과 소프트웨어 개발에 대한 몇 가지 생각</title><link>https://philipjkim.cc/posts/20250908-some-thoughts-on-llms-and-software-development/</link><pubDate>Mon, 08 Sep 2025 14:10:40 +0900</pubDate><guid>https://philipjkim.cc/posts/20250908-some-thoughts-on-llms-and-software-development/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://martinfowler.com/articles/202508-ai-thoughts.html"&gt;https://martinfowler.com/articles/202508-ai-thoughts.html&lt;/a&gt; (translated by Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;저는 몇 주간 이 사이트 관리를 잠시 떠나려 합니다 (일부 휴가, 일부 업무). 일상적인 루틴에서 벗어나 몇 주를 보낼 생각을 하니, LLM과 AI의 현재 상태에 대한 산발적인 생각들을 나누고 싶은 충동을 느낍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;AI가 소프트웨어 개발에 어떤 영향을 미치는지에 대한 초기 설문조사들을 몇 가지 봤습니다. 과연 개발 속도를 높여주고 있는지, 코드 품질을 향상시키는지 아니면 망치고 있는지에 대한 내용들이죠. 이런 설문조사들의 큰 문제점 중 하나는 사람들이 LLM을 &lt;strong&gt;어떻게&lt;/strong&gt; 사용하고 있는지 고려하지 않는다는 점입니다. 제가 아는 바로는 LLM 사용의 대다수가 코파일럿(Co-pilot)을 이용하는 것과 같은 단순한 자동 완성에 그치고 있습니다. 하지만 LLM으로부터 가장 큰 가치를 얻는다고 말하는 사람들은 자동 완성이 그다지 유용하지 않다고 생각하며, LLM이 소스 코드 파일을 직접 읽고 편집하여 작업을 수행하는 방식을 선호합니다. LLM을 사용하는 다양한 워크플로우를 무시하는 설문조사는 사람들을 잘못된 길로 이끌 수 있는 데이터를 만들어낼까 봐 걱정됩니다.
(또 다른 복잡한 문제는 모델마다 기능이 다르다는 점입니다.)&lt;/p&gt;</description></item><item><title>번역글: Rust 의 목적성에 대한 질문과 답변</title><link>https://philipjkim.cc/posts/20250908-quora-is-rust-new-ruby/</link><pubDate>Mon, 08 Sep 2025 11:28:08 +0900</pubDate><guid>https://philipjkim.cc/posts/20250908-quora-is-rust-new-ruby/</guid><description>&lt;p&gt;&lt;em&gt;이 글은 &lt;a href="https://www.quora.com/Rust-is-not-so-much-about-memory-safety-as-it-is-about-being-able-to-hire-cheap-juniors-in-hope-they-can-now-do-the-job-of-experienced-seniors-by-using-Rust-Am-I-wrong-Is-Rust-the-new-Ruby"&gt;Quora 에 Rust 관련 올라온 질문과 답변&lt;/a&gt;을 Gemini 를 통해 번역한 것입니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="q-rust는-메모리-안전성보다는-rust를-사용해-경력-있는-시니어-개발자들의-일을-값싼-주니어들이-해낼-수-있을-거라는-희망을-품고-그들을-고용하는-것과-더-관련이-있는-것-아닌가요-제가-틀렸나요-rust가-새로운-ruby인가요"&gt;Q: Rust는 &amp;lsquo;메모리 안전성&amp;rsquo;보다는 Rust를 사용해 경력 있는 시니어 개발자들의 일을 값싼 주니어들이 해낼 수 있을 거라는 희망을 품고 그들을 고용하는 것과 더 관련이 있는 것 아닌가요? 제가 틀렸나요? Rust가 새로운 Ruby인가요?&lt;/h3&gt;
&lt;hr&gt;
&lt;h3 id="a1-matt-laine의-답변"&gt;A1 (Matt Laine의 답변):&lt;/h3&gt;
&lt;p&gt;어떤 프로그래밍 언어의 기능이든 홍보되는 데는 이유가 있습니다. 어떤 기능들은 언어 설계자가 멋지다고 생각해서 홍보되죠. 또 다른 기능들은 개발 속도를 높여준다거나, 특정 관리자들이 골치 아프다고 여기는 종류의 오류들을 제거해준다거나, 비즈니스에 도움이 되는 어떤 지표를 향상시켜 준다는 약속을 하는 것으로 해석될 수 있기 때문에 홍보됩니다. 또 어떤 기능들은 그저 과대광고를 철석같이 믿고 다른 사람들도 그렇게 해주기를 바라는 &amp;lsquo;언어 전도사들&amp;rsquo;에 의해 홍보되기도 합니다.&lt;/p&gt;</description></item><item><title>개발 팁: 읽기 쉽고 확장 가능한 if 구문 리팩토링 가이드</title><link>https://philipjkim.cc/posts/20250905-how-to-replace-if-statements/</link><pubDate>Fri, 05 Sep 2025 13:02:51 +0900</pubDate><guid>https://philipjkim.cc/posts/20250905-how-to-replace-if-statements/</guid><description>&lt;p&gt;&lt;img src="https://philipjkim.cc/img/20250905_refactoring_if.png" alt="img"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;프로그래밍에서 if 를 사용하는 조건부 로직을 대체할 수 있는 방법들에 대해 gemini 2.5 pro 에게 요청한 연구 결과입니다.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;모든 예제 코드는 Go 로 작성되었으나, 다른 프로그래밍 언어에 공통적으로 적용할 수 있는 내용들입니다.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="서론-조건문의-확산과-기술-부채의-축적"&gt;서론: 조건문의 확산과 기술 부채의 축적&lt;/h2&gt;
&lt;p&gt;소프트웨어 개발에서 &lt;code&gt;if-else&lt;/code&gt; 문은 제어 흐름을 구성하는 가장 기본적인 도구 중 하나입니다. 그러나 이 근본적인 구조는 종종 오용되어, 시간이 지남에 따라 시스템의 건강을 심각하게 해치는 주범이 되기도 합니다. 복잡하게 중첩되거나 끝없이 이어지는 &lt;code&gt;if-else&lt;/code&gt; 체인은 가독성을 저해하고 유지보수를 악몽으로 만들며, 이는 소프트웨어 공학에서 &amp;ldquo;코드 스멜(code smell)&amp;ldquo;이라 불리는 명백한 위험 신호입니다. [1, 2, 3]&lt;/p&gt;</description></item><item><title>깊은 모듈 대 작은 함수: 오스터하우트와 마틴의 소프트웨어 설계 철학 및 커뮤니티 수용에 대한 비교 분석</title><link>https://philipjkim.cc/posts/20250901-ousterhout-vs-martin/</link><pubDate>Mon, 01 Sep 2025 15:49:15 +0900</pubDate><guid>https://philipjkim.cc/posts/20250901-ousterhout-vs-martin/</guid><description>&lt;p&gt;본 연구 보고서는 오스터하우트와 마틴이 제시하는 두 가지 상이한 소프트웨어 설계 철학을 심층적으로 비교 분석하는 것을 목표로 한다. 두 철학은 단순히 기법의 차이를 넘어, 문제의 정의 자체에서부터 근본적인 시각차를 드러낸다. 오스터하우트는 시스템적 &lt;strong&gt;복잡성&lt;/strong&gt;을 주된 적으로 간주하며, 이를 인지 부하(cognitive load)와 변경 증폭(change amplification)이라는 구체적인 지표로 측정한다.[1] 반면, 마틴은 코드의 지역적 &lt;strong&gt;불명확성&lt;/strong&gt;을 가장 경계해야 할 대상으로 보며, 이를 가독성(readability)과 이해 용이성(ease of comprehension)으로 평가한다.[2, 3]&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Ousterhout 와 Martin 의 소프트웨어 설계 철학의 공통점, 차이점 및 충돌 부분에 대한 여러 의견과 관점들에 대해 gemini 2.5 pro 에게 요청한 연구 결과입니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Go 패키지 구조 컨벤션: Standard Go Project Layout</title><link>https://philipjkim.cc/posts/20250826-go-package-conventions/</link><pubDate>Tue, 26 Aug 2025 19:34:33 +0900</pubDate><guid>https://philipjkim.cc/posts/20250826-go-package-conventions/</guid><description>&lt;p&gt;Go 언어 프로젝트의 패키지 구조는 일반적으로 몇 가지 컨벤션을 따르며, &lt;code&gt;pkg&lt;/code&gt;와 &lt;code&gt;internal&lt;/code&gt; 디렉토리는 그중 핵심적인 역할을 합니다. 단순히 코드를 기능별로 묶는 것을 넘어, 패키지의 &lt;strong&gt;가시성(visibility)과 의존성 방향을 명확히&lt;/strong&gt; 하여 프로젝트의 유지보수성을 높이는 것이 목적입니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;이 글은 Gemini 2.5 Pro 모델이 제 Go package convention 에 대한 질문에 답변한 내용입니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Go 커뮤니티에서 널리 통용되는 표준 프로젝트 레이아웃(&lt;a href="https://github.com/golang-standards/project-layout"&gt;golang-standards/project-layout&lt;/a&gt;)을 기반으로 각 디렉토리의 역할과 베스트 프랙티스를 자세히 설명해 드리겠습니다.&lt;/p&gt;
&lt;h2 id="-pkg-디렉토리-공개-라이브러리-public-library"&gt;📦 &lt;code&gt;pkg&lt;/code&gt; 디렉토리: 공개 라이브러리 (Public Library)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;pkg&lt;/code&gt; 디렉토리&lt;/strong&gt;에는 &lt;strong&gt;외부 프로젝트에서 가져다 쓸 수 있는(importable) 공개 라이브러리 성격의 코드&lt;/strong&gt;를 위치시킵니다.&lt;/p&gt;</description></item><item><title>TotT: 변경 감지 테스트는 악몽입니다</title><link>https://philipjkim.cc/posts/20250821-tott-change-detector-tests-are-nightmare/</link><pubDate>Thu, 21 Aug 2025 15:09:05 +0900</pubDate><guid>https://philipjkim.cc/posts/20250821-tott-change-detector-tests-are-nightmare/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2015/01/testing-on-toilet-change-detector-tests.html"&gt;https://testing.googleblog.com/2015/01/testing-on-toilet-change-detector-tests.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;당신은 방금 코드의 동작을 수정하지 않고 리팩토링을 마쳤습니다. 그리고 커밋하기 전에 테스트를 실행했는데&amp;hellip; 수많은 단위 테스트가 실패합니다. 실패한 테스트들을 고치면서, 당신은 여러 테스트에 동일한 변환 작업을 기계적으로 적용하며 시간을 낭비하고 있다는 느낌을 받습니다. 아마도 당신은 어떤 메서드에 파라미터를 하나 추가했고, 이제 테스트 코드에 있는 100개의 해당 메서드 호출부에 빈 문자열을 전달하도록 업데이트해야 하는 상황일 겁니다.&lt;/p&gt;
&lt;p&gt;테스트를 기계적으로 작성한다는 것은 어떤 모습일까요? 여기 터무니없지만 명확한 예시가 있습니다:&lt;/p&gt;</description></item><item><title>TotT: 구현 세부사항보다 Public API를 테스트하세요</title><link>https://philipjkim.cc/posts/20250820-tott-prefer-testing-public-apis/</link><pubDate>Wed, 20 Aug 2025 16:29:38 +0900</pubDate><guid>https://philipjkim.cc/posts/20250820-tott-prefer-testing-public-apis/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2015/01/testing-on-toilet-prefer-testing-public.html"&gt;https://testing.googleblog.com/2015/01/testing-on-toilet-prefer-testing-public.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;아래 클래스는 테스트가 필요할까요?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserInfoValidator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDateOfBirth&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isInFuture&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ValidationException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 클래스의 메서드에는 로직이 포함되어 있으니 테스트하는 것이 좋을 수 있습니다. 하지만 이 클래스를 사용하는 유일한 코드가 아래와 같다면 어떨까요?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserInfoService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UserInfoValidator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 값이 유효하지 않으면 예외를 던짐&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;writeToDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;답은 다음과 같습니다: 아마도 &lt;code&gt;UserInfoValidator&lt;/code&gt;는 테스트가 필요 없을 것입니다. 왜냐하면 모든 경로를 &lt;code&gt;UserInfoService&lt;/code&gt;를 통해 테스트할 수 있기 때문입니다. 여기서 핵심적인 구분은 **이 클래스가 Public API가 아니라 구현 세부사항(implementation detail)**이라는 점입니다.&lt;/p&gt;</description></item><item><title>Go 언어의 풍부한 표준 라이브러리 (Batteries Included)</title><link>https://philipjkim.cc/posts/20250819-go-batteries-included/</link><pubDate>Tue, 19 Aug 2025 12:55:41 +0900</pubDate><guid>https://philipjkim.cc/posts/20250819-go-batteries-included/</guid><description>&lt;p&gt;Go 언어의 특징을 소개하는 시리즈의 세 번째 글입니다. 이전 글에서는 Go의 &lt;strong&gt;단순성&lt;/strong&gt;과 &lt;strong&gt;동시성&lt;/strong&gt;에 대해 다루었습니다. 이번에는 Go가 &amp;lsquo;건전지 포함(Batteries Included)&amp;rsquo; 철학을 어떻게 구현하고 있는지, 즉 강력하고 풍부한 표준 라이브러리에 대해 이야기하고자 합니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(이 글은 Gemini 2.5 Pro 모델에 의해 작성되었으며, 커버하고 있는 세부 항목들과 글의 톤/매너에 대한 요구사항들은 제가 정리해서 Gemini 에 요청했습니다. 내용에 잘못된 부분이 있을 수 있는데, 그런 경우 잘못된 정보에 대한 댓글을 남겨주시면 감사하겠습니다.)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="go-창시자들의-철학-실용성과-안정성"&gt;Go 창시자들의 철학: 실용성과 안정성&lt;/h2&gt;
&lt;p&gt;Go 언어의 설계 철학은 구글의 대규모 프로덕션 환경에서 마주친 문제들을 해결하는 데서 출발했습니다. 언어의 창시자들인 &lt;strong&gt;로버트 그리즈머(Robert Griesemer)&lt;/strong&gt;, &lt;strong&gt;롭 파이크(Rob Pike)&lt;/strong&gt;, &lt;strong&gt;켄 톰슨(Ken Thompson)&lt;/strong&gt; 은 복잡한 외부 의존성 없이도 당장 현장에서 필요한 대부분의 기능을 수행할 수 있는 언어를 원했습니다.&lt;/p&gt;</description></item><item><title>Go 의 동시성 (concurrency) 더 들여다보기</title><link>https://philipjkim.cc/posts/20250811-concurrency-of-go/</link><pubDate>Mon, 11 Aug 2025 13:39:08 +0900</pubDate><guid>https://philipjkim.cc/posts/20250811-concurrency-of-go/</guid><description>&lt;p&gt;현대의 컴퓨터는 멀티코어 프로세서를 기반으로 작동하며, 동시성(Concurrency)은 더 이상 선택이 아닌 필수적인 프로그래밍 패러다임이 되었습니다. Go 언어는 설계 초기부터 동시성을 핵심 기능으로 채택했으며, 전통적인 스레드-락(Thread-Lock) 모델의 복잡성을 해결하기 위한 명확한 철학을 제시합니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 Go의 동시성 모델이 어떠한 철학 위에서 탄생했는지 살펴보고, 다른 주류 언어들과의 접근 방식을 비교 분석합니다. 이를 통해 Go의 동시성이 갖는 장점과 현실적인 트레이드오프를 가감 없이 설명하고자 합니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(이 글은 Gemini 2.5 Pro 모델에 의해 작성되었으며, 커버하고 있는 세부 항목들과 글의 톤/매너에 대한 요구사항들은 제가 정리해서 Gemini 에 요청했습니다. 내용에 잘못된 부분이 있을 수 있는데, 그런 경우 잘못된 정보에 대한 댓글을 남겨주시면 감사하겠습니다.)&lt;/em&gt;&lt;/p&gt;</description></item><item><title>TotT: 서술적인 테스트 이름 작성하기</title><link>https://philipjkim.cc/posts/20250811-tott-writing-descriptive-test-names/</link><pubDate>Mon, 11 Aug 2025 12:05:33 +0900</pubDate><guid>https://philipjkim.cc/posts/20250811-tott-writing-descriptive-test-names/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://testing.googleblog.com/2014/10/testing-on-toilet-writing-descriptive.html"&gt;https://testing.googleblog.com/2014/10/testing-on-toilet-writing-descriptive.html&lt;/a&gt; (Translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;여러분은 테스트 이름을 어떻게 짓나요? 아래 코드 샘플을 한번 보세요. 이 테스트가 무엇을 하는지, 어떤 동작을 검증하는지 즉시 파악할 수 있나요?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testLogin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 테스트는 사용자가 성공적으로 로그인하는 시나리오를 테스트할 수도 있고, 잘못된 비밀번호를 입력했을 때의 시나리오를 테스트할 수도 있습니다. 테스트가 무엇을 하는지 이해하려면 테스트의 본문을 읽어야만 합니다. 이제 아래의 다른 테스트 이름을 보세요.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;isUserLockedOut_lockOutUserAfterThreeInvalidLoginAttempts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;이제 여러분은 테스트 본문을 읽지 않고도, 단지 테스트 이름만 읽는 것만으로 어떤 동작이 테스트되고 있는지 이해할 수 있어야 합니다.&lt;/p&gt;</description></item><item><title>Go 의 단순함(simplicity) 더 들여다보기</title><link>https://philipjkim.cc/posts/20250807-simplicity-of-go/</link><pubDate>Thu, 07 Aug 2025 08:08:20 +0900</pubDate><guid>https://philipjkim.cc/posts/20250807-simplicity-of-go/</guid><description>&lt;p&gt;Go 언어의 철학을 관통하는 가장 중요한 단어는 바로 &lt;strong&gt;&amp;lsquo;단순함(Simplicity)&amp;rsquo;&lt;/strong&gt; 입니다. 이는 단순히 기능이 적다는 의미가 아니라, 복잡한 문제를 명료하고 예측 가능하게 해결하기 위한 의도적인 설계 철학입니다. Go의 창시자 중 한 명인 롭 파이크(Rob Pike)는 &amp;ldquo;단순함은 복잡하다(Simplicity is Complicated)&amp;ldquo;고 말했습니다. 최고의 단순함을 성취하기 위해 수많은 고민과 트레이드오프가 있었음을 암시하는 말이죠.&lt;/p&gt;
&lt;p&gt;이 글에서는 Go의 핵심 철학인 &amp;lsquo;단순함&amp;rsquo;이 코드 수준에서 어떻게 드러나는지, 그리고 이 철학이 왜 어떤 개발자에게는 최고의 장점이 되고 다른 개발자에게는 답답한 단점으로 여겨지는지 가감 없이 살펴보겠습니다.&lt;/p&gt;</description></item><item><title>책 소개: VIBE CODING by Gene Kim and Steve Yegge</title><link>https://philipjkim.cc/posts/20250807-book-vibe-coding-preface/</link><pubDate>Thu, 07 Aug 2025 07:44:08 +0900</pubDate><guid>https://philipjkim.cc/posts/20250807-book-vibe-coding-preface/</guid><description>&lt;p&gt;&lt;em&gt;이 책의 존재를 알려준 &lt;a href="mailto:r@rsl.kr"&gt;r@rsl.kr&lt;/a&gt; 에게 감사를 표합니다.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이 책의 공개된 일부 내용은 &lt;a href="https://go.itrevolution.net/e/Xys/U3oak/7Qm/R2mkNon98"&gt;https://go.itrevolution.net/e/Xys/U3oak/7Qm/R2mkNon98&lt;/a&gt; 에서 다운로드할 수 있습니다.&lt;/li&gt;
&lt;li&gt;이 글은 제가 pdf 원문을 읽기 전 맥락을 먼저 갖기 위해 Gemini 에 번역/요약을 요청한 결과입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="책-소개-vibe-coding---무엇을-다루는가"&gt;책 소개: &amp;ldquo;VIBE CODING&amp;rdquo; - 무엇을 다루는가?&lt;/h2&gt;
&lt;p&gt;이 책은 &lt;strong&gt;&amp;ldquo;바이브 코딩(Vibe Coding)&amp;rdquo;&lt;/strong&gt; 이라는 새로운 소프트웨어 개발 패러다임을 중심으로, 생성형 AI(GenAI), 챗(Chat), 에이전트(Agent)를 활용하여 실제 상용 수준의 소프트웨어를 구축하는 방법을 다룹니다. 저자들은 바이브 코딩이 단순히 코드를 빨리 짜는 기술을 넘어, 소프트웨어를 &lt;strong&gt;누가, 어떤 속도로, 얼마나 큰 야망을 가지고 만들 수 있는지&lt;/strong&gt;에 대한 근본적인 정의를 바꾸고 있다고 주장합니다.&lt;/p&gt;</description></item><item><title>번역글: 형편없는 바이브 코더의 9가지 습관</title><link>https://philipjkim.cc/posts/20250806-9-habits-of-the-highly-ineffective-vibe-coder/</link><pubDate>Wed, 06 Aug 2025 15:28:31 +0900</pubDate><guid>https://philipjkim.cc/posts/20250806-9-habits-of-the-highly-ineffective-vibe-coder/</guid><description>&lt;p&gt;원문: &lt;a href="https://www.infoworld.com/article/4029093/9-habits-of-the-highly-ineffective-vibe-coder.html"&gt;https://www.infoworld.com/article/4029093/9-habits-of-the-highly-ineffective-vibe-coder.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;바이브 코딩이 정말 그렇게 쉽다고요? AI의 현실 세계적 등가물인 집사를 생각해 보세요. 새로운 집사에게 아침 식사를 제공하거나 완벽한 마티니를 만드는 방법과 같은 기술을 가르치는 전문 학교가 있습니다. 하지만 이 학교들이 부자들에게 집사와 잘 지내는 방법을 가르치는 병행 과정도 있다는 것을 알고 계셨나요? 맞습니다. 부자들은 찻잔을 올바르게 잡는 방법을 배워서 집사가 우아하게 차를 채울 수 있도록 합니다. 심지어 어떤 종류의 요청이 적절하고 어떤 것이 그렇지 않은지도 배웁니다. 이것은 2분짜리 틱톡 비디오에서는 가르칠 수 없는 종류의 일입니다.&lt;/p&gt;</description></item><item><title>번역글: 증강 코딩: 바이브를 넘어</title><link>https://philipjkim.cc/posts/20250806-augmented-coding-beyond-the-vibes/</link><pubDate>Wed, 06 Aug 2025 09:22:00 +0900</pubDate><guid>https://philipjkim.cc/posts/20250806-augmented-coding-beyond-the-vibes/</guid><description>&lt;p&gt;원문: &lt;a href="https://tidyfirst.substack.com/p/augmented-coding-beyond-the-vibes"&gt;https://tidyfirst.substack.com/p/augmented-coding-beyond-the-vibes&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;이 글의 존재를 알려준 &lt;a href="mailto:r@rsl.kr"&gt;r@rsl.kr&lt;/a&gt; 에게 감사를 표합니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;최근 증강 코딩을 사용하여 B+ 트리 라이브러리를 구축하려는 야심 찬 프로젝트에서 좋은 마무리를 지었습니다. 그 결과물은 &lt;a href="https://github.com/KentBeck/BPlusTree3"&gt;&lt;em&gt;BPlusTree3&lt;/em&gt;&lt;/a&gt; - Rust 및 Python으로 구현된 성능 경쟁력이 있고, 어쩌면 프로덕션에 즉시 사용 가능한 구현입니다. 저는 친구와 앉아 저의 이야기를 나누고 GenAI 시대의 프로그래밍 미래에 대해 무엇을 시사하는지 생각해 보았습니다.&lt;/p&gt;
&lt;h2 id="무엇이-당신을-b-트리를-먼저-구현하게-만들었나요"&gt;무엇이 당신을 B+ 트리를 먼저 구현하게 만들었나요?&lt;/h2&gt;
&lt;p&gt;증강 코딩의 놀라운 힘을 깨닫기 시작했을 때, 저는 과거에 기술적으로 접근하기 어려웠던 프로젝트들을 떠올리기 시작했습니다. 그중 하나는 특수 목적 데이터베이스였습니다. 지금 그 데이터베이스 프로젝트를 구현하면서 B+ 트리 데이터 구조를 충분히 이해하지 못했다는 것을 깨달았고 그래서 목표를 바꿨습니다.&lt;/p&gt;</description></item><item><title>AI 코딩 시대의 그림자: LLM 의존이 개발자에게 미치는 잠재적 위험 5가지</title><link>https://philipjkim.cc/posts/20250805-shadows-of-ai-coding-era/</link><pubDate>Tue, 05 Aug 2025 16:12:15 +0900</pubDate><guid>https://philipjkim.cc/posts/20250805-shadows-of-ai-coding-era/</guid><description>&lt;p&gt;&lt;code&gt;vibe-coding&lt;/code&gt;, &lt;code&gt;agent-coding&lt;/code&gt; 이라는 말이 어색하지 않은 시대입니다. Claude, Gemini, GitHub Copilot과 같은 LLM(거대 언어 모델) 기반 코딩 도구들은 이제 단순히 코드 스니펫을 자동 완성해주는 수준을 넘어, 우리의 생각을 논리적으로 설명하면 프로젝트 전체의 구조를 짜고 방대한 양의 코드를 순식간에 만들어냅니다. 생산성의 혁신이라 부를 만한 이 변화는 분명 경이롭습니다.&lt;/p&gt;
&lt;p&gt;하지만 이 강력한 도구에 과도하게 의존하기 시작하면서, 우리는 이전에 겪어보지 못한 새로운 종류의 문제들에 직면하고 있습니다. 밝은 빛이 강할수록 그림자도 짙어지는 법입니다. 지난 주말 바이브코딩을 직접 경험해 보며 그런 그림자의 영역을 빠르게 체감할 수 있었습니다. 이 글에서는 LLM 기반 코딩에 대한 의존이 개발자 개인과 팀에 미칠 수 있는 5가지 잠재적 위험을 심도 있게 다뤄보고자 합니다.&lt;/p&gt;</description></item><item><title>책 소개: 소프트웨어 설계의 철학</title><link>https://philipjkim.cc/posts/20250804-a-philosophy-of-software-design/</link><pubDate>Mon, 04 Aug 2025 18:13:51 +0900</pubDate><guid>https://philipjkim.cc/posts/20250804-a-philosophy-of-software-design/</guid><description>&lt;p&gt;존 오스터하우트 (John Ousterhout) 의 &lt;a href="https://www.amazon.com/Philosophy-Software-Design-John-Ousterhout/dp/1732102201"&gt;소프트웨어 설계의 철학 - A Philosophy of Software Design&lt;/a&gt; 은 소프트웨어 복잡성을 줄이고 유지 관리가 용이한 코드를 작성하는 방법에 대한 심도 있는 통찰력을 제공합니다. 21개 챕터에 걸쳐 저자는 복잡성의 본질을 분석하고, 효과적인 모듈 설계를 위한 원칙을 제시하며, 코드의 가독성과 명확성을 높이는 구체적인 기법들을 소개합니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;아래 내용은 Gemini 의 연구 결과에 약간의 퇴고를 거친 것입니다.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="서론-복잡성과의-전쟁"&gt;서론: 복잡성과의 전쟁&lt;/h2&gt;
&lt;p&gt;존 오스터하우트의 저서 &amp;ldquo;소프트웨어 설계의 철학(A Philosophy of Software Design)&amp;ldquo;은 소프트웨어 개발의 가장 근본적인 한계가 성능이나 기능이 아니라, 우리가 만든 시스템을 우리 스스로 이해하는 능력에 있다는 주장으로 시작합니다.[1, 2] 이 책의 전체 철학은 단 하나의 적, 즉 &lt;strong&gt;복잡성(complexity)&lt;/strong&gt; 에 맞서기 위한 일련의 전략들로 구성되어 있습니다.[3, 4, 5, 6]&lt;/p&gt;</description></item><item><title>TotT: 테스트에 로직을 넣지 마세요</title><link>https://philipjkim.cc/posts/20250804-tott-dont-put-logic-in-tests/</link><pubDate>Mon, 04 Aug 2025 09:20:41 +0900</pubDate><guid>https://philipjkim.cc/posts/20250804-tott-dont-put-logic-in-tests/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2014/07/testing-on-toilet-dont-put-logic-in.html"&gt;https://testing.googleblog.com/2014/07/testing-on-toilet-dont-put-logic-in.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;프로그래밍 언어는 우리에게 많은 표현력을 제공합니다. 연산자와 조건문 같은 개념들은 광범위한 입력을 처리하는 프로그램을 작성할 수 있게 해주는 중요한 도구입니다. 하지만 이러한 유연성은 복잡성 증가라는 대가를 치르게 하여, 우리 프로그램을 이해하기 어렵게 만듭니다.&lt;/p&gt;
&lt;p&gt;프로덕션 코드와 달리, 테스트에서는 유연성보다 &lt;strong&gt;단순성&lt;/strong&gt;이 더 중요합니다. 대부분의 단위 테스트는 단일의, 알려진 입력이 단일의, 알려진 출력을 생성하는지 검증합니다. 테스트는 입력과 출력을 계산하는 대신 직접 명시함으로써 복잡성을 피할 수 있습니다. 그렇지 않으면 테스트 자체에 버그가 생기기 쉽습니다.&lt;/p&gt;</description></item><item><title>Go 는 왜 LLM 기반 Vibe-Coding에 적합한 언어인가?</title><link>https://philipjkim.cc/posts/20250731-golang-for-vibe-coding/</link><pubDate>Thu, 31 Jul 2025 11:55:18 +0900</pubDate><guid>https://philipjkim.cc/posts/20250731-golang-for-vibe-coding/</guid><description>&lt;p&gt;이 글에서는 LLM을 활용한 Vibe-Coding 시대에 Go 언어가 가지는 장점들을 알아봅니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(영문 글을 번역했던 블로그 대부분의 포스트와는 달리 이 글은 제가 초안을 작성하고, Gemini 의 도움을 받아 개선된 originality 가 있는 글입니다.)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-강력한-정적-타입-시스템과-컴파일-단계"&gt;1. 강력한 정적 타입 시스템과 컴파일 단계&lt;/h2&gt;
&lt;p&gt;Go는 정적 타입 언어이며 컴파일을 통해 실행 파일을 만듭니다. 이는 코드가 실행되기 전, 즉 컴파일 시점에 타입 불일치, 함수 인자 오류, 존재하지 않는 변수 참조 등 수많은 오류를 미리 잡아낼 수 있음을 의미합니다.&lt;/p&gt;</description></item><item><title>번역글: Gemini CLI: 소개 및 그 진정한 가치</title><link>https://philipjkim.cc/posts/20250730-gemini-cli-an-introduction-and-its-real-value/</link><pubDate>Wed, 30 Jul 2025 09:08:43 +0900</pubDate><guid>https://philipjkim.cc/posts/20250730-gemini-cli-an-introduction-and-its-real-value/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://medium.com/@terrycho/gemini-cli-an-introduction-and-its-real-value-15bd070fb011"&gt;https://medium.com/@terrycho/gemini-cli-an-introduction-and-its-real-value-15bd070fb011&lt;/a&gt; (translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;얼마 전, Gemini CLI 발표 직후 구글의 Gemini 서버가 과부하되었다는 소식이 들려왔습니다. 사람들은 왜 Gemini CLI에 그렇게 열광하는 걸까요? 우리는 이미 웹과 데스크톱에서 사용할 수 있는 ChatGPT와 Claude, 그리고 Cursor와 Windsurf 같은 코딩 AI 도구, 심지어 Lovable과 V0 같은 바이브 코딩 도구까지 가지고 있습니다. 터미널 기반의 새로운 CLI 기반 AI가 이 시장에 어떤 영향을 미칠 수 있을까요?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://miro.medium.com/v2/resize:fit:1400/format:webp/1*mnQoAMyjje5FOHG5TT85qA.png" alt="img1"&gt;&lt;/p&gt;
&lt;p&gt;무료 서비스 발표가 상당한 인기를 얻었지만, Gemini CLI는 전체 소프트웨어 개발 수명 주기(SDLC)를 아우르는 오케스트레이터로서 새로운 입지를 확립했습니다. 이는 소프트웨어 개발 전체를 포괄하려는 야심 찬 계획을 엿볼 수 있게 합니다. 이 글에서는 Gemini CLI가 왜 그렇게 인기 있는지, 그 특징과 차별점, 숨겨진 가치, 그리고 미래 개발 가능성에 대해 논의할 것입니다.&lt;/p&gt;</description></item><item><title>TotT: 위험 중심 테스트 (Risk-Driven Test)</title><link>https://philipjkim.cc/posts/20250729-tott-risk-driven-testing/</link><pubDate>Tue, 29 Jul 2025 10:35:40 +0900</pubDate><guid>https://philipjkim.cc/posts/20250729-tott-risk-driven-testing/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2014/05/testing-on-toilet-risk-driven-testing.html"&gt;https://testing.googleblog.com/2014/05/testing-on-toilet-risk-driven-testing.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;우리는 모두 코드를 작성할 때 &lt;strong&gt;테스트를 작성하도록 길들여져 있습니다&lt;/strong&gt; : 단위, 기능, UI 등 모든 것을 말이죠. 우리는 결국 전문가입니다. 우리 중 많은 사람들은 작은 테스트가 작업을 빠르게 진행하게 하고, 더 큰 테스트가 안전과 마무리에 영감을 주는 것을 좋아합니다. 아니면 단순히 검토 중에 비난을 예상할 수도 있습니다. 우리는 이러한 테스트에 너무 익숙해져서 &lt;strong&gt;왜 테스트를 작성하는지 더 이상 질문하지 않는 경우&lt;/strong&gt;가 많습니다. 이것은 낭비적이고 위험할 수 있습니다.&lt;/p&gt;</description></item><item><title>TotT: 효과적인 테스트</title><link>https://philipjkim.cc/posts/20250728-tott-effective-testing/</link><pubDate>Mon, 28 Jul 2025 10:24:47 +0900</pubDate><guid>https://philipjkim.cc/posts/20250728-tott-effective-testing/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2014/05/testing-on-toilet-effective-testing.html"&gt;https://testing.googleblog.com/2014/05/testing-on-toilet-effective-testing.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;개별 단위 테스트를 작성하든 제품의 전체 테스트 프로세스를 설계하든, &lt;strong&gt;테스트가 코드의 버그를 얼마나 효과적으로 감지하고 보고하는지&lt;/strong&gt; 다시 한번 생각해보는 것이 중요합니다. 효과적이려면 모든 테스트가 극대화하려고 노력해야 하는 &lt;strong&gt;세 가지 중요한 품질&lt;/strong&gt;이 있습니다.&lt;/p&gt;
&lt;h2 id="충실도-fidelity"&gt;충실도 (Fidelity)&lt;/h2&gt;
&lt;p&gt;테스트 대상 코드가 손상되면 테스트는 실패합니다. &lt;strong&gt;고충실도 테스트는 테스트 대상 코드의 결함에 매우 민감하여 버그가 코드에 침투하는 것을 방지하는 데 도움이 됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;테스트가 코드의 모든 경로를 다루고 예상 상태에 대한 모든 관련 assertions 을 포함하는지 확인하여 충실도를 극대화하십시오.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>TotT: 메서드가 아닌 동작을 테스트하라</title><link>https://philipjkim.cc/posts/20250725-tott-test-behaviors-not-methods/</link><pubDate>Fri, 25 Jul 2025 09:17:03 +0900</pubDate><guid>https://philipjkim.cc/posts/20250725-tott-test-behaviors-not-methods/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2014/04/testing-on-toilet-test-behaviors-not.html"&gt;https://testing.googleblog.com/2014/04/testing-on-toilet-test-behaviors-not.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;메서드를 작성한 후 메서드가 수행하는 모든 작업을 확인하는 테스트를 하나만 작성하기 쉽습니다. &lt;strong&gt;그러나 테스트와 public 메서드가 1:1 관계를 가져야 한다고 생각하는 것은 해로울 수 있습니다.&lt;/strong&gt; 우리가 정말로 테스트하고 싶은 것은 동작이며, 단일 메서드가 여러 동작을 나타낼 수 있고, 단일 동작이 때로는 여러 메서드에 걸쳐 있을 수도 있습니다.&lt;/p&gt;
&lt;p&gt;전체 메서드를 확인하는 나쁜 테스트를 살펴보겠습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testProcessTransaction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newUserWithBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOW_BALANCE_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dollars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;transactionProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Pile of Beanie Babies&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dollars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;You bought a Pile of Beanie Babies&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmails&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Your balance is low&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmails&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getSubject&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;구매한 품목의 이름을 표시하고 잔액이 부족하다는 이메일을 보내는 것은 두 가지 별개의 동작이지만, 이 테스트는 동일한 메서드에 의해 트리거된다는 이유만으로 이 두 동작을 함께 살펴봅니다. &lt;strong&gt;이러한 테스트는 추가 동작이 계속 추가됨에 따라 시간이 지남에 따라 매우 커지고 유지 관리가 어려워지는 경우가 많습니다.&lt;/strong&gt; 결국에는 어떤 입력 부분이 어떤 단정(assertion)에 대한 책임이 있는지 파악하기가 매우 어려워질 것입니다. 테스트 이름이 메서드 이름을 직접적으로 반영한다는 사실 자체가 나쁜 징조입니다.&lt;/p&gt;</description></item><item><title>TotT: 좋은 테스트란 무엇인가?</title><link>https://philipjkim.cc/posts/20250724-testing-on-the-toilet-what-makes-a-good-test/</link><pubDate>Thu, 24 Jul 2025 10:02:23 +0900</pubDate><guid>https://philipjkim.cc/posts/20250724-testing-on-the-toilet-what-makes-a-good-test/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2014/03/testing-on-toilet-what-makes-good-test.html"&gt;https://testing.googleblog.com/2014/03/testing-on-toilet-what-makes-good-test.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;단위 테스트는 코드의 정확성을 확인하는 중요한 도구입니다. 그러나 &lt;strong&gt;좋은 테스트를 작성하는 것은 단순히 정확성을 확인하는 것 이상입니다.&lt;/strong&gt; 좋은 단위 테스트는 읽기 쉽고 유지 보수 가능하도록 여러 다른 속성을 보여야 합니다.&lt;/p&gt;
&lt;p&gt;좋은 테스트의 한 가지 속성은 명확성입니다. &lt;strong&gt;명확성이란 테스트가 사람들에게 읽기 쉬운 문서 역할을 해야 하며, 테스트 중인 코드를 해당 public API 측면에서 설명해야 함을 의미합니다.&lt;/strong&gt; 테스트는 구현 세부 사항을 직접 참조해서는 안 됩니다. 클래스의 테스트 이름은 클래스가 하는 모든 것을 말해야 하며, 테스트 자체는 클래스 사용 방법에 대한 예시가 되어야 합니다.&lt;/p&gt;</description></item><item><title>번역글: 원하는 기술을 갖춘 사람을 고용하라</title><link>https://philipjkim.cc/posts/20250723-hire-for-the-skills-you-want/</link><pubDate>Wed, 23 Jul 2025 07:42:17 +0900</pubDate><guid>https://philipjkim.cc/posts/20250723-hire-for-the-skills-you-want/</guid><description>&lt;p&gt;원문: Pragmatic Programmers Newsletter (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;제인은 12번째 면접에 나타난다. 면접관은 그녀에게 마커를 건네고 화이트보드를 가리킨다. &amp;ldquo;연결되고 가중치가 있으며 무방향 그래프의 최소 신장 트리의 가중치를 계산하는 코드를 작성해 보세요.&amp;rdquo; 제인은 한숨을 쉬었다. 이달 들어 벌써 세 번째였다. 그녀는 작업을 시작했다.&lt;/p&gt;
&lt;p&gt;내 생각에, 그녀는 아무리 답변 연습을 했더라도 그 직업을 가져서는 안 된다.&lt;/p&gt;
&lt;p&gt;나에게 좋은 지원자라면 펜을 내려놓고 이렇게 물었을 것이다. &amp;ldquo;이것에 대한 맥락을 알려주시겠어요? 왜냐하면 프로그래머가 이런 종류의 문제를 처리하기 위해 AI(또는 라이브러리)를 사용하기를 원하실 테니까요. 저는 귀사의 AI가 할 수 없는 일을 할 수 있게 해주는 직업을 찾고 있었습니다.&amp;rdquo;&lt;/p&gt;</description></item><item><title>TotT: 구현이 아닌 동작을 테스트하세요</title><link>https://philipjkim.cc/posts/20250722-tott-test-behavior-not-implementation/</link><pubDate>Tue, 22 Jul 2025 16:50:17 +0900</pubDate><guid>https://philipjkim.cc/posts/20250722-tott-test-behavior-not-implementation/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2013/08/testing-on-toilet-test-behavior-not.html"&gt;https://testing.googleblog.com/2013/08/testing-on-toilet-test-behavior-not.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;여러분의 신뢰할 수 있는 Calculator 클래스는 많은 행복한 사용자를 보유한 가장 인기 있는 오픈소스 프로젝트 중 하나입니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Calculator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;또한 제대로 작동하는지 확인하는 &lt;strong&gt;테스트&lt;/strong&gt;도 있습니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testAdd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;그러나 새로운 멋진 라이브러리는 덧셈 연산자 대신 사용하면 코드에서 몇 배의 속도 향상을 약속합니다. 여러분은 이 라이브러리를 사용하기 위해 들뜬 마음으로 &lt;strong&gt;코드를 변경합니다&lt;/strong&gt;:&lt;/p&gt;</description></item><item><title>TotT: 상태 테스트 vs. 상호작용 테스트</title><link>https://philipjkim.cc/posts/20250722-tott-testing-state-vs-testing-interactions/</link><pubDate>Tue, 22 Jul 2025 13:11:07 +0900</pubDate><guid>https://philipjkim.cc/posts/20250722-tott-testing-state-vs-testing-interactions/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2013/03/testing-on-toilet-testing-state-vs.html"&gt;https://testing.googleblog.com/2013/03/testing-on-toilet-testing-state-vs.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;단위 테스트가 테스트 중인 코드가 제대로 작동하는지 확인하는 방법은 일반적으로 두 가지가 있습니다: 상태 테스트 또는 상호작용 테스트. 이들의 차이점은 무엇일까요?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;상태 테스트는 테스트 중인 코드가 올바른 결과를 반환하는지 확인하는 것을 의미합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testSortNumbers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NumberSorter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numberSorter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NumberSorter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quicksort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bubbleSort&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 반환된 목록이 정렬되었는지 확인합니다. 어떤 정렬 알고리즘이 사용되었는지는 중요하지 않으며,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 올바른 결과가 반환되기만 하면 됩니다.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numberSorter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sortNumbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;상호작용 테스트는 테스트 중인 코드가 특정 메서드를 올바르게 호출하는지 확인하는 것을 의미합니다.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>TotT: GUI 테스팅의 MVP 되기</title><link>https://philipjkim.cc/posts/20250722-tott-be-an-mvp-of-gui-testing/</link><pubDate>Tue, 22 Jul 2025 10:49:31 +0900</pubDate><guid>https://philipjkim.cc/posts/20250722-tott-be-an-mvp-of-gui-testing/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2009/02/with-all-sport-drug-scandals-of-late.html"&gt;https://testing.googleblog.com/2009/02/with-all-sport-drug-scandals-of-late.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;최근의 모든 스포츠 약물 스캔들로 인해 요즘 좋은 롤 모델을 찾기가 어렵습니다. 하지만 롤 모델이 도메인 모델(비즈니스 엔티티의 객체 모델)이라면 MVP가 되기 위해 속임수를 쓸 필요가 없습니다. Model-View-Presenter를 사용하세요!&lt;/p&gt;
&lt;p&gt;MVP는 MVC(Model-View-Controller)와 매우 유사합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MVC에서는 아래 다이어그램과 같이 프레젠테이션 로직이 Controller와 View에 의해 공유됩니다.&lt;/strong&gt; View는 일반적으로 가시적인 GUI 프레임워크 구성 요소에서 직접 파생되며, 모델을 관찰하고 사용자에게 시각적으로 보여줍니다. Controller는 사용자 이벤트를 모델 변경으로 변환하는 방법을 결정하는 역할을 합니다. &lt;strong&gt;MVP에서는 프레젠테이션 로직이 Presenter라고도 알려진 Supervising Controller에 의해 전적으로 처리됩니다.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>TotT: 인터페이스 테스트하기</title><link>https://philipjkim.cc/posts/20250721-tott-testing-against-interfaces/</link><pubDate>Mon, 21 Jul 2025 10:01:35 +0900</pubDate><guid>https://philipjkim.cc/posts/20250721-tott-testing-against-interfaces/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2008/07/tott-testing-against-interfaces.html"&gt;https://testing.googleblog.com/2008/07/tott-testing-against-interfaces.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;부족함에 대한 지속적인 느낌을 억누르기 위해, 당신은 공학 분야의 진정한 통과 의례인 자신만의 행성 파괴 광선총을 만드는 데 시간을 들였습니다. 축하합니다. 그리고 당신은 매우 자랑스러워했지만, 그 다음 주말에 이워크&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; 해설이 포함된 한정판 스타워즈 3부작을 구매하여 데스스타&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;가 알데라안&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;을 파괴하는 것을 보고 잘못된 결정을 내렸다는 것을 깨달았습니다. 당신의 행성 파괴 광선총은 파란색 레이저를 가지고 있지만, 녹색 레이저가 훨씬 더 멋있어 보입니다. 하지만 라디오셱&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;에 가서 기존의 파괴 광선총에 끼울 수 있는 녹색 레이저를 사는 것은 간단한 문제가 아닙니다. 녹색 레이저를 가지려면 처음부터 다른 행성 파괴 광선총을 만들어야 할 것입니다. 두 개의 파괴 광선총을 소유하는 것이 하나보다 이웃들의 질투심을 더 자극할 것이므로 당신에게는 괜찮습니다.&lt;/p&gt;</description></item><item><title>TotT: "Static Cling" 퇴치하기</title><link>https://philipjkim.cc/posts/20250717-tott-defeat-static-cling/</link><pubDate>Thu, 17 Jul 2025 17:03:43 +0900</pubDate><guid>https://philipjkim.cc/posts/20250717-tott-defeat-static-cling/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2008/06/defeat-static-cling.html"&gt;https://testing.googleblog.com/2008/06/defeat-static-cling.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;당신은 페어 프로그래밍을 하고 있고, 많은 뛰어난 사람들이 그렇듯이 소리 내어 말하고 있습니다. &amp;ldquo;목을 만들고, 주입하고, 테스트를 다시 실행할 거야. 통과해야 하는데&amp;hellip; 젠장!&amp;rdquo; 당신의 파트너는 예외 &lt;code&gt;&amp;quot;ConnectionFactory not initialized&amp;quot;&lt;/code&gt;를 발견합니다. &amp;ldquo;뭐?&amp;rdquo; 그녀는 말합니다. &amp;ldquo;뭔가가 데이터베이스를 사용하고 있어? 젠장, 이건 작은 테스트여야 했는데.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;검사해보니 당신의 클래스가 다른 클래스의 정적 메서드를 호출하고 있다는 것을 발견했습니다. Static Cling&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;이 발생한 것입니다! 만약 정적 메서드에 의존하는 코드를 생성하는 데이터 지속성 레이어를 (잘못) 사용하고, &lt;em&gt;주의를 기울이지 않았다면&lt;/em&gt;, 당신의 코드는 다음과 같을 수 있습니다.&lt;/p&gt;</description></item><item><title>TotT: 의지할 수 있는 친구들</title><link>https://philipjkim.cc/posts/20250716-tott-friends-you-can-depend-on/</link><pubDate>Wed, 16 Jul 2025 10:47:04 +0900</pubDate><guid>https://philipjkim.cc/posts/20250716-tott-friends-you-can-depend-on/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2008/06/tott-friends-you-can-depend-on.html"&gt;https://testing.googleblog.com/2008/06/tott-friends-you-can-depend-on.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;테스트 환경에서 사용하기 너무 어렵거나 느린 것에 의존하는 코드를 테스트하고 싶을 때는, 의존성 대신 테스트 더블을 사용하세요.
더미(Dummy)는 API를 만족시키기 위해 가짜 입력 값을 전달합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITEM_NAME&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ShoppingCart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ShoppingCart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUANTITY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QUANTITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITEM_NAME&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;스텁(Stub)&lt;/strong&gt; 은 실제 객체를 오버라이드하고 하드코딩된 값을 반환합니다. 스텁만을 사용한 테스트는 상태 기반 테스트입니다. 시스템을 실행한 다음 시스템이 예상된 상태인지 확인합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ItemPricer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pricer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ItemPricer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BigDecimal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PRICE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ShoppingCart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ShoppingCart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pricer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dummyItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;QUANTITY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QUANTITY&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PRICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITEM_NAME&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;목(Mock)&lt;/strong&gt; 은 값을 반환할 수 있지만, 메서드가 호출되는 방식에도 신경을 씁니다. (&amp;ldquo;엄격한 목(strict mocks)&amp;rdquo; 은 메서드 호출 순서에 신경을 쓰는 반면, &amp;ldquo;유연한 목(lenient mocks)&amp;rdquo; 은 그렇지 않습니다.) 목을 사용한 테스트는 상호작용 기반 테스트입니다. 목에 기대값을 설정하고, 목은 실행될 때 기대값을 검증합니다. 이 예시는 JMock을 사용하여 목을 생성합니다(EasyMock, Mockito 도 유사합니다):&lt;/p&gt;</description></item><item><title>TotT: 싱글턴을 피하기 위한 의존성 주입 사용하기</title><link>https://philipjkim.cc/posts/20250715-tott-using-dependency-injection-to-avoid-singletons/</link><pubDate>Tue, 15 Jul 2025 14:06:34 +0900</pubDate><guid>https://philipjkim.cc/posts/20250715-tott-using-dependency-injection-to-avoid-singletons/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2008/05/tott-using-dependancy-injection-to.html"&gt;https://testing.googleblog.com/2008/05/tott-using-dependancy-injection-to.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;싱글턴을 사용하는 코드를 테스트하기는 어렵습니다. 일반적으로 테스트하려는 코드는 싱글턴 인스턴스와 강력하게 결합되어 있습니다. 싱글턴 객체가 종종 정적 생성자나 정적 메서드에서 생성되기 때문에 싱글턴 객체의 생성을 제어할 수 없습니다. 결과적으로 싱글턴 인스턴스의 동작을 시뮬레이션할 수도 없습니다.&lt;/p&gt;
&lt;p&gt;싱글턴 클래스의 구현 변경이 불가능하지만, 싱글턴 클라이언트는 변경이 가능하다면 간단한 리팩터링으로 테스트를 더 쉽게 만들 수 있습니다. 싱글턴 인스턴스로 &lt;code&gt;Server&lt;/code&gt;를 사용하는 메서드가 있다고 가정해 봅시다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieveData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Client&lt;/code&gt;를 리팩터링하여 의존성 주입을 사용하고 싱글턴 패턴의 사용을 완전히 피할 수 있습니다. 어떤 기능도 잃지 않았고, &lt;code&gt;Server&lt;/code&gt;의 싱글턴 인스턴스만 존재해야 한다는 요구 사항도 잃지 않았습니다. 유일한 차이점은 &lt;code&gt;Server&lt;/code&gt; 인스턴스를 정적 &lt;code&gt;getInstance&lt;/code&gt; 메서드에서 가져오는 대신, &lt;code&gt;Client&lt;/code&gt;가 생성자에서 받는다는 것입니다. 클래스를 테스트하기 더 쉽게 만들었습니다!&lt;/p&gt;</description></item><item><title>TotT: 시간은 무작위</title><link>https://philipjkim.cc/posts/20250714-tott-time-is-random/</link><pubDate>Mon, 14 Jul 2025 10:30:17 +0900</pubDate><guid>https://philipjkim.cc/posts/20250714-tott-time-is-random/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2008/04/tott-time-is-random.html"&gt;https://testing.googleblog.com/2008/04/tott-time-is-random.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;메서드의 입력값을 명확하게 식별할 수 없을 때 어떻게 제대로 테스트할 수 있을까요? 다음 Java 메서드를 살펴보세요.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/** 현재 시간으로부터 다음 분의 시작을 나타내는 Date 객체를 반환합니다. */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;nextMinuteFromNow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nowAsMillis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nowAsMillis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMilliseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 메서드를 효과적으로 테스트하는 데에는 두 가지 장벽이 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;코너 케이스&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;를 테스트하기 쉽지 않습니다. 시스템 클럭이 입력 조건을 제공하는 데에 좌우됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nextMinuteFromNow()&lt;/code&gt; 가 반환될 때, 시간이 변경됩니다. 이는 테스트가 단언이 아니라 추측이 될 것이며, 재현하기 어려운 저빈도 실패를 생성할 수 있습니다&amp;hellip; 불안정성입니다! 예를 들어 클래스 로딩 및 가비지 컬렉션 일시 중지가 이에 영향을 미칠 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;System.currentTimeMillis()&lt;/code&gt; 가 무작위 숫자 제공자처럼 보이기 시작하나요? 맞습니다! 현재 시간은 *비결정성 (non-determinism)*의 또 다른 원천입니다. &lt;code&gt;nextMinuteFromNow()&lt;/code&gt; 의 결과는 입력으로부터 쉽게 결정될 수 없습니다. 다행히도 이것은 쉽게 해결할 수 있습니다. 현재 시간을 제어할 수 있는 입력 매개변수로 만드세요.&lt;/p&gt;</description></item><item><title>TotT: 너무 많은 테스트</title><link>https://philipjkim.cc/posts/20250714-tott-too-many-tests/</link><pubDate>Mon, 14 Jul 2025 10:20:26 +0900</pubDate><guid>https://philipjkim.cc/posts/20250714-tott-too-many-tests/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2008/02/in-movie-amadeus-austrian-emperor.html"&gt;https://testing.googleblog.com/2008/02/in-movie-amadeus-austrian-emperor.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;영화 &lt;em&gt;아마데우스&lt;/em&gt;에서 오스트리아 황제는 모차르트의 음악에 “음표가 너무 많다”고 비판합니다. 하나의 기능을 테스트하는 데 “너무 많은” 테스트는 몇 개일까요?&lt;/p&gt;
&lt;p&gt;메서드 &lt;code&gt;decide&lt;/code&gt;를 고려해 봅시다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;decide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DoOneThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DoAnother&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 공간 제약 때문에 한 글자 변수 이름을 사용했습니다.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 더 나은 이름을 사용해야 합니다. 제가 하는 대로 하지 말고, 제가 시키는 대로 하세요. :-)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;얼마나 많은 테스트를 작성할 수 있을까요? 각 변수에 대해 &lt;code&gt;int&lt;/code&gt; 값의 전체 범위를 실행하려면 2^192개의 테스트가 필요할 것입니다. 이렇게 계속한다면 구골 개수의 테스트를 하게 될 것입니다! 너무 많은 테스트입니다.&lt;/p&gt;</description></item><item><title>TotT: 스트룹 효과</title><link>https://philipjkim.cc/posts/20250711-tott-the-stroop-effect/</link><pubDate>Fri, 11 Jul 2025 10:49:44 +0900</pubDate><guid>https://philipjkim.cc/posts/20250711-tott-the-stroop-effect/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2008/02/tott-stroop-effect.html"&gt;https://testing.googleblog.com/2008/02/tott-stroop-effect.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="https://philipjkim.cc/img/The_Stoop_Effect.gif" alt="img"&gt;&lt;/p&gt;
&lt;p&gt;아래 두 행동을 얼마나 빨리 할 수 있나요?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;hellip;25개 단어를 모두 소리 내어 읽기: 빨강, 초록, 파랑, &amp;hellip; (지금 시도해 보세요!)&lt;/li&gt;
&lt;li&gt;&amp;hellip;25가지 색깔을 모두 소리 내어 말하기: 초록, 노랑, 하양&amp;hellip; (지금 시도해 보세요!)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;두 번째 작업이 더 많은 시간과 노력을 필요로 했나요? 그렇다면, 여러분은 스트룹 효과를 경험하고 있는 것입니다. 스트룹 효과는 대략적으로 &lt;em&gt;레이블&lt;/em&gt;(이 경우 단어)이 &lt;em&gt;내용&lt;/em&gt;(색깔)과 같은 영역에 있고 의미가 충돌할 때, 레이블이 내용을 이해하는 능력에 방해가 된다는 것을 의미합니다.&lt;/p&gt;</description></item><item><title>TotT: 스텁이 단위 테스트 속도를 높여줍니다</title><link>https://philipjkim.cc/posts/20250710-tott-stubs-speed-up-your-unit-tests/</link><pubDate>Thu, 10 Jul 2025 16:01:59 +0900</pubDate><guid>https://philipjkim.cc/posts/20250710-tott-stubs-speed-up-your-unit-tests/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2007/04/tott-stubs-speed-up-your-unit-tests.html"&gt;https://testing.googleblog.com/2007/04/tott-stubs-speed-up-your-unit-tests.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Michael Feathers는 좋은 단위 테스트의 특징을 &amp;ldquo;빠르게 실행되고 문제 위치를 파악하는 데 도움이 된다&amp;quot;고 정의합니다. 코드에 데이터베이스 액세스, 다른 서버와의 통신, 시간 의존성 등이 있을 때는 이를 달성하기 어렵습니다.&lt;/p&gt;
&lt;p&gt;모듈의 일부 종속성에 커스텀 객체를 대체하여 코드를 철저히 테스트하고, 커버리지를 높이며, 1초 이내에 실행할 수 있습니다. 데이터베이스 오류와 같은 드문 시나리오도 시뮬레이션하여 오류 처리 코드를 테스트할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;이러한 &amp;ldquo;커스텀 객체&amp;quot;를 지칭하는 다양한 용어가 사용됩니다. 용어를 명확히 하기 위해 Gerard Meszaros는 다음 정의를 제공합니다.&lt;/p&gt;</description></item><item><title>번역글: ExecutorService invokeAll 과 가상 스레드 사용하기</title><link>https://philipjkim.cc/posts/20250710-using-executor-service-invoke-all-with-virtual-threads/</link><pubDate>Thu, 10 Jul 2025 09:41:55 +0900</pubDate><guid>https://philipjkim.cc/posts/20250710-using-executor-service-invoke-all-with-virtual-threads/</guid><description>&lt;p&gt;원문: &lt;a href="https://davidvlijmincx.com/posts/loom/invoke-all-with-virtual-threads/"&gt;https://davidvlijmincx.com/posts/loom/invoke-all-with-virtual-threads/&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="서론"&gt;서론&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;invokeAll&lt;/code&gt; 은 &lt;code&gt;ExecutorService&lt;/code&gt; 의 메서드로, 여러 제출된 작업을 동시에 시작합니다. &lt;code&gt;ExecutorService&lt;/code&gt; 는 스레드 풀에서 플랫폼 스레드를 사용하여 제출된 작업을 실행합니다. 이 비싸고 리소스 집약적인 플랫폼 스레드를 사용하는 대신, 가상 스레드를 사용하여 &lt;code&gt;ExecutorService&lt;/code&gt; 에 제출된 작업을 실행할 수도 있습니다. 이 글에서는 &lt;code&gt;invokeAll&lt;/code&gt; 메서드를 가상 스레드 (virtual threads), 구조화된 동시성 (structured concurrency) 및 플랫폼 스레드 (platform threads) 와 함께 구현하는 모든 방법을 다룰 것입니다.&lt;/p&gt;
&lt;h2 id="가상-스레드를-사용한-invokeall"&gt;가상 스레드를 사용한 &lt;code&gt;invokeAll&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;우리가 살펴볼 첫 번째 예제는 가상 스레드 (virtual threads) 를 사용합니다. 각 작업에 대해 가상 스레드를 생성하는 Executor 를 생성하는 try-with-resources 문이 있습니다. 7행에서 &lt;code&gt;invokeAll&lt;/code&gt; 메서드가 작업 목록과 함께 호출됩니다.&lt;/p&gt;</description></item><item><title>TotT: 화장실에서 테스팅 (Testing on the Toilet) 소개</title><link>https://philipjkim.cc/posts/20250708-introducing-testing-on-the-toilet/</link><pubDate>Tue, 08 Jul 2025 10:31:22 +0900</pubDate><guid>https://philipjkim.cc/posts/20250708-introducing-testing-on-the-toilet/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html"&gt;https://testing.googleblog.com/2007/01/introducing-testing-on-toilet.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;업데이트 - 2024년 12월&lt;/strong&gt;: TotT 의 의미는 &lt;em&gt;화장실에서 테스팅 (Testing on the Toilet)&lt;/em&gt; 에서 이제 *화장실에서 기술 (Tech on the Toilet)*로 변경되었습니다. 자세한 내용은 다음 게시물을 읽어보세요: &lt;a href="https://testing.googleblog.com/2024/12/tech-on-toilet-driving-software.html"&gt;&lt;strong&gt;화장실에서 기술: 한 번의 화장실 휴식으로 소프트웨어 우수성 향상&lt;/strong&gt;&lt;/a&gt;. 아래는 2007년의 원본 게시물입니다.&lt;/p&gt;
&lt;p&gt;블로거 커멘트: 앞으로 TotT 시리즈를 시간이 날 때마다 하나씩 번역해서 포스팅할 예정입니다. (이미 먼저 포스팅된 글들도 있습니다.)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;저희는 여러분이 더 많은 테스트를 작성하기를 바랍니다. 네, 바로 여러분입니다. 테스트는 코드를 리팩토링해야 할 때나 다른 개발자가 기능을 추가할 때 여러분을 보호하는 안전망이라는 말을 이미 들었을 것입니다. 심지어 테스트가 코드 디자인에 도움이 될 수 있다는 것도 알고 있을 것입니다.&lt;/p&gt;</description></item><item><title>번역글: Go의 동시성: 심층 분석</title><link>https://philipjkim.cc/posts/20250707-concurrency-in-go-a-deep-dive/</link><pubDate>Mon, 07 Jul 2025 13:43:14 +0900</pubDate><guid>https://philipjkim.cc/posts/20250707-concurrency-in-go-a-deep-dive/</guid><description>&lt;p&gt;원문: &lt;a href="https://medium.com/hprog99/concurrency-in-go-a-deep-dive-2abbb4838984"&gt;https://medium.com/hprog99/concurrency-in-go-a-deep-dive-2abbb4838984&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="서론"&gt;서론&lt;/h2&gt;
&lt;h3 id="동시성이란-무엇인가"&gt;동시성이란 무엇인가?&lt;/h3&gt;
&lt;p&gt;동시성은 독립적인 활동들의 구성으로 느슨하게 정의될 수 있습니다. 반드시 병렬 실행을 필요로 하지는 않지만, 여러 태스크가 겹치는 시간 동안 진행되도록 허용합니다. 컴퓨팅의 맥락에서 동시 프로그래밍은 여러 태스크가 실행에서 &lt;strong&gt;인터리브(interleave - 여러 대상을 번갈아 가며 실행하거나 배치)&lt;/strong&gt; 될 수 있도록 보장하며, 이는 특히 I/O 바운드 태스크나 수많은 이벤트를 동시에 처리해야 하는 애플리케이션(예: 웹 서버에서 여러 클라이언트 연결 처리)에서 중요합니다.&lt;/p&gt;
&lt;h3 id="동시성-vs-병렬성"&gt;동시성 vs. 병렬성&lt;/h3&gt;
&lt;p&gt;비공식적인 대화에서 종종 혼용되지만, 동시성과 병렬성은 다른 개념입니다.&lt;/p&gt;</description></item><item><title>TotT: 더 나은 테스트를 위한 가짜(Fake) 사용하기</title><link>https://philipjkim.cc/posts/20250707-testing-on-toilet-fake-your-way-to/</link><pubDate>Mon, 07 Jul 2025 13:23:12 +0900</pubDate><guid>https://philipjkim.cc/posts/20250707-testing-on-toilet-fake-your-way-to/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2013/06/testing-on-toilet-fake-your-way-to.html"&gt;https://testing.googleblog.com/2013/06/testing-on-toilet-fake-your-way-to.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;수년간 블로그를 운영한 후, 블로그 플랫폼의 API를 사용해보기로 결정했습니다. 이리저리 사용해보다가 문득 깨닫습니다: 원격 블로그 서버와 통신하지 않고도 코드가 작동하는지 어떻게 알 수 있을까요?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;deletePostsWithTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blogService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAllPosts&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTags&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;blogService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deletePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;가짜(Fakes)가 해결책입니다! 가짜는 실제 구현체처럼 작동하지만, 프로덕션 환경에 적합하지 않은 API의 가벼운 구현체입니다. 블로그 서비스의 경우, 중요한 것은 게시물을 가져오고 삭제하는 기능뿐입니다. 실제 블로그 서비스는 데이터베이스와 여러 프런트엔드 서버가 필요하겠지만, 코드를 테스트하는 데는 그런 것이 필요 없습니다. 필요한 것은 블로그 서비스 API의 어떤 구현체든 상관없습니다. 간단한 인메모리 구현체로 이를 달성할 수 있습니다.&lt;/p&gt;</description></item><item><title>번역글: 헤르메틱 서버 (Hermetic Servers)</title><link>https://philipjkim.cc/posts/20250707-hermetic-servers/</link><pubDate>Mon, 07 Jul 2025 13:08:09 +0900</pubDate><guid>https://philipjkim.cc/posts/20250707-hermetic-servers/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2012/10/hermetic-servers.html"&gt;https://testing.googleblog.com/2012/10/hermetic-servers.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="종단-간-테스트"&gt;&amp;ldquo;종단 간&amp;rdquo; 테스트&lt;/h2&gt;
&lt;p&gt;Google 테스트 세계에서 종단 간 테스트는 사용자 요청부터 응답까지 전체 서버 스택을 실행하는 테스트입니다. 다음은 종단 간 테스트가 검증할 시스템 테스트 대상(SUT)의 간략한 모습입니다. SUT의 프런트엔드 서버가 특정 사용자 요청에 필요하지 않은 세 번째 백엔드에 연결된다는 점에 유의하십시오.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://1.bp.blogspot.com/-lwP4J6FcN34/UGx-bU41ImI/AAAAAAAAAP8/l5M0J3A0Xgk/s640/1-SUT.jpg" alt="system_under_test"&gt;&lt;/p&gt;
&lt;p&gt;이러한 시스템에 대한 빠르고 신뢰할 수 있는 종단 간 테스트를 작성하는 데 있어 한 가지 과제는 네트워크 액세스를 피하는 것입니다. 네트워크 액세스가 포함된 테스트는 로컬 리소스만 액세스하는 테스트보다 느리며, 외부 서버에 액세스하면 비결정성 또는 외부 서버의 가용성 부족으로 인해 &lt;a href="http://googletesting.blogspot.com/2008/04/tott-avoiding-flakey-tests.html"&gt;불확실성&lt;/a&gt;이 발생할 수 있습니다.&lt;/p&gt;</description></item><item><title>TotT: 목(Mock)을 과도하게 사용하지 마세요</title><link>https://philipjkim.cc/posts/20250707-testing-on-the-toilet-dont-overuse-mocks/</link><pubDate>Mon, 07 Jul 2025 10:53:23 +0900</pubDate><guid>https://philipjkim.cc/posts/20250707-testing-on-the-toilet-dont-overuse-mocks/</guid><description>&lt;p&gt;원문: &lt;a href="https://testing.googleblog.com/2013/05/testing-on-toilet-dont-overuse-mocks.html"&gt;https://testing.googleblog.com/2013/05/testing-on-toilet-dont-overuse-mocks.html&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;코드를 테스트할 때, 코드의 의존성을 목으로 처리하여 무시하는 것이 쉬워 보일 수 있습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testCreditCardIsCharged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockCreditCardServer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockCreditCardServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isServerAvailable&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockCreditCardServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;beginTransaction&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockTransactionManager&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockTransactionManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTransaction&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockCreditCardServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;creditCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPayment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockPayment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isOverMaxBalance&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creditCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dollars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;500&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockCreditCardServer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;creditCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;하지만 목을 사용하지 않으면 때로는 더 간단하고 유용한 테스트를 만들 수 있습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;testCreditCardIsCharged&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creditCardServer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;paymentProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creditCard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Money&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dollars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;500&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;creditCardServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMostRecentCharge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creditCard&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;목을 과도하게 사용하면 몇 가지 문제가 발생할 수 있습니다.&lt;/p&gt;</description></item><item><title>번역글: Golang 공동 창시자 Rob Pike: Go가 잘한 것과 잘못한 것</title><link>https://philipjkim.cc/posts/20250702-what-go-got-right-and-wrong/</link><pubDate>Wed, 02 Jul 2025 10:28:40 +0900</pubDate><guid>https://philipjkim.cc/posts/20250702-what-go-got-right-and-wrong/</guid><description>&lt;p&gt;원문: &lt;a href="https://thenewstack.io/golang-co-creator-rob-pike-what-go-got-right-and-wrong/"&gt;https://thenewstack.io/golang-co-creator-rob-pike-what-go-got-right-and-wrong/&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://gophercon.com.au/"&gt;GopherCon AU&lt;/a&gt; 참석자들은 Go의 사양이 처음 작성된 도시인 호주 시드니에서 특별한 대접을 받았습니다. Go의 공동 설계자 중 한 명인 &lt;a href="https://github.com/robpike"&gt;Rob Pike&lt;/a&gt;는 Go 프로그래밍 언어가 출시된 지 14주년이 되는 날을 기념하며 “무엇을 제대로 했고, 무엇을 잘못했는지” 돌아보는 강연을 했습니다.&lt;/p&gt;
&lt;p&gt;Pike는 2009년 11월 10일 웹사이트가 처음 공개되던 순간을 기억하며 “세상이 우리가 무엇을 해왔는지 알게 되었다”고 말했습니다.&lt;/p&gt;
&lt;p&gt;“14년이 지난 지금, 돌아볼 것이 많습니다.”라고 Pike는 청중에게 말하며 “더 큰 교훈들”을 탐구하겠다고 약속했습니다.&lt;/p&gt;</description></item><item><title>번역글: 분산 시스템 개발자를 위한 기도</title><link>https://philipjkim.cc/posts/20250701-a-prayer-for-distrubuted-systems-developers/</link><pubDate>Tue, 01 Jul 2025 13:55:13 +0900</pubDate><guid>https://philipjkim.cc/posts/20250701-a-prayer-for-distrubuted-systems-developers/</guid><description>&lt;p&gt;원문: &lt;a href="https://philipotoole.com/a-prayer-for-distributed-systems-developers/"&gt;https://philipotoole.com/a-prayer-for-distributed-systems-developers/&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;16년이 넘는 시간 동안 저는 전체 스택을 오르내리며 소프트웨어를 작성했습니다. 제 경력 초기에 저는 특수 임베디드 장치를 위한 &lt;a href="http://en.wikipedia.org/wiki/Booting"&gt;부트 ROM&lt;/a&gt; 소프트웨어를 작성했습니다. 이런 종류의 프로그래밍은 컴퓨터가 &lt;strong&gt;정말로&lt;/strong&gt; 어떻게 작동하는지에 대해 많은 것을 가르쳐주었습니다.&lt;/p&gt;
&lt;p&gt;대부분의 프로그래머는 컴퓨터가 어떻게 작동하는지 안다고 생각하지만, &lt;strong&gt;그들은 단지 소프트웨어가 어떻게 작동하는지 알 뿐입니다.&lt;/strong&gt; 이 프로그래머들은 &lt;a href="http://en.wikipedia.org/wiki/Interrupt_handler"&gt;인터럽트 서비스 루틴&lt;/a&gt;이 어떻게 호출되는지, 컴퓨터가 두 숫자를 어떻게 더하는지, 마이크로프로세서가 다음 명령어를 RAM에 어떻게 로드하는지 정말로 알지 못합니다. 하지만 괜찮습니다. 그럴 필요도 없고, 관심도 없습니다.&lt;/p&gt;</description></item><item><title>번역글: Java 23: 성능 및 생산성 향상 (2024년 9월)</title><link>https://philipjkim.cc/posts/20250625-java-23-new-features/</link><pubDate>Wed, 25 Jun 2025 15:27:23 +0900</pubDate><guid>https://philipjkim.cc/posts/20250625-java-23-new-features/</guid><description>&lt;p&gt;원문: &lt;a href="https://www.hungrycoders.com/blog/java-9-23-unleashed-the-developers-cheat-sheet-to-modern-java"&gt;https://www.hungrycoders.com/blog/java-9-23-unleashed-the-developers-cheat-sheet-to-modern-java&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="주요-기능"&gt;주요 기능&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;패턴, instanceof, switch에서의 Primitive Types (미리보기): 패턴 매칭을 기본 타입으로 확장하여 상용구 코드(boilerplate code)를 줄입니다.&lt;/li&gt;
&lt;li&gt;Scoped Values (세 번째 미리보기): 제한된 수명을 가진 변수를 도입하여 메모리 관리를 개선합니다.&lt;/li&gt;
&lt;li&gt;Structured Concurrency (세 번째 미리보기): 구조화된 API를 통해 스레드 관리를 강화하여 동시성 처리를 단순화합니다.&lt;/li&gt;
&lt;li&gt;Markdown Documentation Comments: Javadoc에 Markdown 지원을 추가하여 문서의 가독성을 향상시킵니다.&lt;/li&gt;
&lt;li&gt;ZGC를 기본 가비지 컬렉터로: 세대별 Z Garbage Collector (ZGC)가 기본값으로 설정되어 성능과 응답성을 향상시킵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="개요-요약"&gt;개요 요약&lt;/h2&gt;
&lt;p&gt;Java 23은 성능 및 개발자 생산성 향상 추세를 이어갑니다. 기본 타입을 지원하는 패턴 매칭을 개선하고, 더 안전한 메모리 관리를 위해 Scoped Values를 도입하며, 구조화된 API를 통해 동시성을 단순화합니다. Markdown 문서화는 개발자 경험을 개선하고, ZGC가 기본 컬렉터가 되어 더 나은 성능과 낮은 지연 시간을 보장합니다.&lt;/p&gt;</description></item><item><title>번역글: AI를 이용한 게임 디컴파일 개발 여정</title><link>https://philipjkim.cc/posts/20250625-development-journey-on-game-decompilation-using-ai/</link><pubDate>Wed, 25 Jun 2025 14:35:23 +0900</pubDate><guid>https://philipjkim.cc/posts/20250625-development-journey-on-game-decompilation-using-ai/</guid><description>&lt;p&gt;원문: &lt;a href="https://macabeus.medium.com/game-decompilation-using-ai-4d47b65f8852"&gt;https://macabeus.medium.com/game-decompilation-using-ai-4d47b65f8852&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;안녕하세요, 저는 &lt;a href="https://github.com/macabeus"&gt;Macabeus&lt;/a&gt;입니다. 이 글에서는 제가 오래된 게임을 디컴파일하기 위해 AI를 사용한 개발 여정을 공유하고, 이 재미있는 분야에서의 개인적인 연구를 탐색해 보려고 합니다!&lt;/p&gt;
&lt;h2 id="게임-디컴파일이란"&gt;게임 디컴파일이란?&lt;/h2&gt;
&lt;p&gt;간단히 말해, &lt;em&gt;디컴파일&lt;/em&gt;은 저수준 코드를 고수준 코드로 변환하는 과정입니다. 예를 들어, 어셈블리에서 C로 변환하는 것입니다.&lt;/p&gt;
&lt;p&gt;IDA Pro나 Ghidra와 같은 유명한 리버스 엔지니어링 도구들을 통해 이를 알고 계실 수도 있습니다. 하지만 이 글에서 제가 &lt;em&gt;디컴파일&lt;/em&gt;이라고 언급할 때는, &lt;em&gt;일치하는 디컴파일(matching decompilation)&lt;/em&gt; 에 대해 이야기하는 것입니다. 이 더 구체적인 용어는 컴파일했을 때 대상 어셈블리와 &lt;strong&gt;정확히&lt;/strong&gt; 일치하면서도, 인간이 읽을 수 있는(human-readable) 고수준 코드를 작성하고자 함을 의미합니다.&lt;/p&gt;</description></item><item><title>번역글: 지금 바로 사용할 수 있는 최고의 AI 코딩 도구</title><link>https://philipjkim.cc/posts/20250624-the-best-ai-coding-tools-you-can-use-right-now/</link><pubDate>Tue, 24 Jun 2025 19:52:16 +0900</pubDate><guid>https://philipjkim.cc/posts/20250624-the-best-ai-coding-tools-you-can-use-right-now/</guid><description>&lt;p&gt;원문: &lt;a href="https://spectrum.ieee.org/best-ai-coding-tools"&gt;https://spectrum.ieee.org/best-ai-coding-tools&lt;/a&gt; (Translated by Google Gemini)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;AI 기반 &lt;a href="https://spectrum.ieee.org/tag/software-development"&gt;소프트웨어 개발&lt;/a&gt; 도구는 빠르게 발전하고 있습니다. 향상된 &lt;a href="https://spectrum.ieee.org/tag/autocomplete"&gt;자동 완성&lt;/a&gt;으로 시작했던 것이 애플리케이션을 작성하고, 문제를 디버깅하고, 전체 코드베이스를 관리할 수 있는(또는 적어도 시도할 수 있는) &lt;a href="https://spectrum.ieee.org/tag/ai-agents"&gt;AI 에이전트&lt;/a&gt;로 확장되었습니다.&lt;/p&gt;
&lt;p&gt;소프트웨어 개발 회사 Frescher Southern LTD의 설립자이자 수석 엔지니어인 &lt;a href="https://www.linkedin.com/in/chasing/"&gt;Josh Knowles&lt;/a&gt;는 “이것은 놀라울 정도로 빨랐습니다. 우리는 지금 위상 변화와 같은 것을 겪고 있습니다.”라고 말합니다.&lt;/p&gt;
&lt;p&gt;이러한 변화는 이 새로운 분야의 왕이 되기 위해 경쟁하는 AI 기반 코딩 도구들을 낳았습니다. 선도적인 도구 중 일부는 불과 몇 년 전에는 존재하지도 않던 &lt;a href="https://spectrum.ieee.org/tag/startups"&gt;스타트업&lt;/a&gt;에서 나왔습니다. 그러나 거대 기술 기업들도 가만히 있지 않습니다. 다른 최고의 옵션들은 &lt;a href="https://spectrum.ieee.org/tag/microsoft"&gt;Microsoft&lt;/a&gt;와 같은 기존 플레이어와 &lt;a href="https://spectrum.ieee.org/tag/anthropic"&gt;Anthropic&lt;/a&gt;과 같은 자금력 있는 AI 연구소에서 제공됩니다.&lt;/p&gt;</description></item><item><title>번역글: 현재 나의 LLM 코드 생성 워크플로우</title><link>https://philipjkim.cc/posts/20250623-my-llm-codegen-workflow-atm/</link><pubDate>Mon, 23 Jun 2025 17:33:07 +0900</pubDate><guid>https://philipjkim.cc/posts/20250623-my-llm-codegen-workflow-atm/</guid><description>&lt;p&gt;&lt;em&gt;원문: &lt;a href="https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/"&gt;https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/&lt;/a&gt; (translated by Google Gemini)&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;요약:&lt;/strong&gt; 스펙을 브레인스토밍하고, 계획을 계획한 다음, LLM 코드 생성을 사용하여 실행합니다. 개별적인 루프. 그리고 마법&lt;/p&gt;
&lt;p&gt;저는 LLM을 사용하여 아주 많은 소규모 제품을 만들어 왔습니다. 재미있고 유용했지만, 엄청난 시간을 낭비할 수 있는 함정들이 있습니다.&lt;/p&gt;
&lt;p&gt;얼마 전 한 친구가 제가 LLM을 어떻게 사용하여 소프트웨어를 작성하는지 물었습니다. 저는 &amp;ldquo;이런. 시간이 얼마나 있으신데요!&amp;ldquo;라고 생각했고, 그래서 이 글을 쓰게 되었습니다.&lt;/p&gt;
&lt;p&gt;(추신: AI를 싫어하는 분이라면 맨 끝으로 스크롤하세요)&lt;/p&gt;
&lt;p&gt;저는 많은 개발자 친구들과 이 주제에 대해 이야기하는데, 우리 모두는 어느 방향으로든 약간의 변형을 가미한 비슷한 접근 방식을 가지고 있습니다. 여기 저의 워크플로우가 있습니다. 이것은 저 자신의 작업, 친구들과의 대화(Nikete, Kanno, Obra, Kris, Erik 고마워요), 그리고 여러 끔찍한 인터넷 나쁜 곳들에서 공유된 많은 모범 사례들을 바탕으로 만들어졌습니다.&lt;/p&gt;</description></item></channel></rss>