<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[kdy1: The way I think]]></title><description><![CDATA[kdy1: The way I think]]></description><link>https://kdy1.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 10 Jun 2026 13:12:13 GMT</lastBuildDate><atom:link href="https://kdy1.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[작업 기록: 자동 QA 구성]]></title><description><![CDATA[최근에 Zephyr Cloud 의 프로젝트에 자동 QA 시스템을 구성했다. 그 작업 기록이다.

원래 이런 작업은 리눅스에서 하는 게 맞다고 생각했다. 그런데 나는 계속 로컬에서 디버깅해야 했고, 개발 흐름상 맥에서 바로 돌릴 수 있어야 했다.
예전에도 자동 QA를 시도한 적은 있었지만 끝까지 제대로 굴러간 적은 없었다. 그러다가 지난주 중반쯤, “이건 로]]></description><link>https://kdy1.dev/2026-5-14-task-log-auto-qa</link><guid isPermaLink="true">https://kdy1.dev/2026-5-14-task-log-auto-qa</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Wed, 13 May 2026 21:47:17 GMT</pubDate><content:encoded><![CDATA[<p>최근에 Zephyr Cloud 의 프로젝트에 자동 QA 시스템을 구성했다. 그 작업 기록이다.</p>
<hr />
<p>원래 이런 작업은 리눅스에서 하는 게 맞다고 생각했다. 그런데 나는 계속 로컬에서 디버깅해야 했고, 개발 흐름상 맥에서 바로 돌릴 수 있어야 했다.</p>
<p>예전에도 자동 QA를 시도한 적은 있었지만 끝까지 제대로 굴러간 적은 없었다. 그러다가 지난주 중반쯤, “이건 로컬에서부터 디버깅하면 가능하겠다”는 감이 왔다.</p>
<p>실제로 작업을 시작한 건 목요일이었다.</p>
<hr />
<h3>타임라인</h3>
<h4>목요일 — 로컬 자동 QA 재시작</h4>
<p>목요일부터 자동 QA를 다시 구성하기 시작했다.</p>
<p>Codex에게 로컬에서 실행 가능한 스크립트를 짜게 했고, 초기 접근 방식은 Midscene 기반이었다.</p>
<p>앱이 웹 기반이라 자동화 자체는 비교적 쉬운 편이었다.</p>
<p>문제는 Midscene 기반 접근 방식의 특성이었다.</p>
<p>테스트를 실행하면 실제 컴퓨터 화면을 점유했다. 클릭 이벤트를 수행하려면 실제 GUI를 건드려야 했고, 테스트가 도는 동안 컴퓨터를 사실상 사용할 수 없었다.</p>
<p>즉:</p>
<ul>
<li><p>QA는 자동인데</p>
</li>
<li><p>사람은 컴퓨터를 못 쓰는 상태</p>
</li>
</ul>
<p>가 되어버렸다.</p>
<p>이 시점에서 자동 QA의 의미가 꽤 퇴색된다고 느꼈다.</p>
<p>⸻</p>
<h4>금요일 — CDP 기반 백그라운드 자동 QA 성공</h4>
<p>금요일에는 실행 구조를 바꿨다.</p>
<p>핵심은:</p>
<ul>
<li>화면을 점유하지 않고 백그라운드에서 돌리는 것</li>
</ul>
<p>이었다.</p>
<p>여기서 Midscene 기반 접근에서 CDP 기반 구조로 전환했다.</p>
<p>앱이 웹 기반이었기 때문에 CDP를 사용하는 게 잘 맞았고, 브라우저를 직접 제어하면서 백그라운드 실행이 가능해졌다.</p>
<p>다만 단순 브라우저 제어만으로는 부족해서, 판단과 플로우 제어에는 Claude Code를 사용했다.</p>
<p>결과적으로:</p>
<ul>
<li><p>로컬 디버깅 가능</p>
</li>
<li><p>백그라운드 실행 가능</p>
</li>
<li><p>화면 점유 없음</p>
</li>
<li><p>실제 사용자 시나리오 기반 QA 가능</p>
</li>
</ul>
<p>상태까지 도달했다.</p>
<p>그리고 금요일에 처음으로 자동 QA를 끝까지 완주시키는 데 성공했다.</p>
<hr />
<h4>토요일 — CI 연결</h4>
<p>토요일에는 이 QA를 CI에서 실행되도록 연결했다.</p>
<p>아직 레거시 이미지 기반이긴 했지만, 중요한 것은 PR 단위 자동 QA 가 가능해졌다는 점이었다.</p>
<h3>현재 구조</h3>
<p>현재 흐름은 아래처럼 구성되어 있다.</p>
<ol>
<li>PR 작성</li>
</ol>
<p>PR 본문에는 원래 사람이 수동 QA를 할 때 필요한 가이드라인을 적는다.</p>
<p>예를 들면:</p>
<ul>
<li><p>어떤 기능을 확인해야 하는지</p>
</li>
<li><p>어떤 시나리오를 검증해야 하는지</p>
</li>
<li><p>어떤 동작이 기대 결과인지</p>
</li>
</ul>
<p>같은 내용이다.</p>
<p>⸻</p>
<ol>
<li>QA 라벨 부착</li>
</ol>
<p>'needs ai qa' 라벨을 붙이면 깃허브 액션이 이를 감지한다. 그리고 라벨이 붙으면 자동 QA가 시작된다.</p>
<p>⸻</p>
<ol>
<li>CI에서 자동 QA 수행</li>
</ol>
<p>CI 환경에서 자동으로:</p>
<ul>
<li><p>브라우저 실행</p>
</li>
<li><p>시나리오 수행</p>
</li>
<li><p>화면 분석</p>
</li>
<li><p>결과 판별</p>
</li>
</ul>
<p>을 수행한다.</p>
<p>그리고 이 구조의 장점 중 하나는 병렬 처리였다.</p>
<p>PR이 여러 개 열려 있어도 각각 독립적으로 QA를 수행할 수 있다.</p>
<p>사람이 수동 테스트를 하는 구조였다면:</p>
<ul>
<li><p>여러 PR 동시 처리 어려움</p>
</li>
<li><p>컨텍스트 스위칭 발생</p>
</li>
<li><p>반복 작업 증가</p>
</li>
</ul>
<p>문제가 생겼을 텐데, 지금은 그런 부담이 많이 줄었다.</p>
<p>⸻</p>
<ol>
<li>결과 댓글 작성</li>
</ol>
<p>QA가 끝나면 PR에 댓글을 남긴다.</p>
<p>결과는 항목별로:</p>
<ul>
<li><p>PASS</p>
</li>
<li><p>FAIL</p>
</li>
<li><p>INCONCLUSIVE</p>
</li>
</ul>
<p>형태로 정리된다.</p>
<p>그래서 실제로 사람이 확인해야 하는 건:</p>
<ul>
<li><p>실패 항목</p>
</li>
<li><p>애매한 항목</p>
</li>
</ul>
<p>정도로 줄어들었다.</p>
<p>⸻</p>
<p>스크린샷 / 비디오 기록</p>
<p>자동 QA 과정에서:</p>
<ul>
<li><p>스크린샷 저장</p>
</li>
<li><p>비디오 녹화</p>
</li>
</ul>
<p>도 함께 수행한다.</p>
<p>비디오 녹화 기능은 이번 주 수요일(5월 13일)에 추가했다.</p>
<p>기왕 하는 김에 영상도 있으면 좋을 것 같아서 넣었나</p>
]]></content:encoded></item><item><title><![CDATA[성대 행사에 참여했다]]></title><description><![CDATA[어제 성균관대학교에서 열린 간단한 이벤트에 연사로 참여했는데, 질의 응답 시간에 한 얘기들 중 도움이 될만한 것들이 많았다. 귀차니즘을 해결할 수 있다면 글로 정리해서 올릴 것이다.
주제는

개발 지식 / 전공 지식이 필요한가

AI 시대에 필요한 역량

개발 직군의 미래


가 될 것이다.]]></description><link>https://kdy1.dev/2026-5-14-skku-event</link><guid isPermaLink="true">https://kdy1.dev/2026-5-14-skku-event</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Wed, 13 May 2026 21:00:13 GMT</pubDate><content:encoded><![CDATA[<p>어제 성균관대학교에서 열린 간단한 이벤트에 연사로 참여했는데, 질의 응답 시간에 한 얘기들 중 도움이 될만한 것들이 많았다. 귀차니즘을 해결할 수 있다면 글로 정리해서 올릴 것이다.</p>
<p>주제는</p>
<ul>
<li><p>개발 지식 / 전공 지식이 필요한가</p>
</li>
<li><p>AI 시대에 필요한 역량</p>
</li>
<li><p>개발 직군의 미래</p>
</li>
</ul>
<p>가 될 것이다.</p>
]]></content:encoded></item><item><title><![CDATA[한국의 학벌에 대한 생각]]></title><description><![CDATA[내 블로그의 제목이 kdy1: The way I think 인만큼 앞으로는 내 생각을 더 자주 올리려고 한다.
한국 기준으로, 학벌은 사람을 볼 때 꽤나 유용한 지표이지만, 절대적이지는 않다. 경험적인 얘기일 뿐이지만, 성균관대학교 자퇴생으로서 느낀 것들이 몇 가지 있다.
대학까지 간 사람의 학벌은 학습 능력 x 성실함 에 대체로 비례한다. 그래서 의미가 ]]></description><link>https://kdy1.dev/2026-4-4-korean-edu</link><guid isPermaLink="true">https://kdy1.dev/2026-4-4-korean-edu</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Fri, 03 Apr 2026 23:08:31 GMT</pubDate><content:encoded><![CDATA[<p>내 블로그의 제목이 <code>kdy1: The way I think</code> 인만큼 앞으로는 내 생각을 더 자주 올리려고 한다.</p>
<p>한국 기준으로, 학벌은 사람을 볼 때 꽤나 유용한 지표이지만, 절대적이지는 않다. 경험적인 얘기일 뿐이지만, 성균관대학교 자퇴생으로서 느낀 것들이 몇 가지 있다.</p>
<p>대학까지 간 사람의 학벌은 <code>학습 능력 x 성실함</code> 에 <strong>대체로</strong> 비례한다. 그래서 의미가 있는 것 같다. 나도 대학교 덕분에 사람들 큰 걱정 없이 편하게 만날 수 있었다. 근데 <strong>대체로</strong>가 함정인데, 내가 학교를 다니던 시절 기준으론 성대 정도의 학교도 쉽게 들어올 방법이 꽤나 있었고, 그런 경우 저 곱셈식이 어그러져서인지 대체로 문제가 살짝 있었다.</p>
<p>지금 생각해보면 성대의 특성일 수도 있다. 성대생들은 대체로 저 곱셈식에서 성실함의 비중이 꽤 되는 사람들이기 때문이다. 제대로 노력을 해 본 적 없는 사람들은 자기의 주제를 전혀 모르는데, 내 친구들 중엔 그런 사람이 거의 없었다.</p>
]]></content:encoded></item><item><title><![CDATA[인간 지능에 대한 메모장]]></title><description><![CDATA[최종 업데이트: 2026/03/15


지능의 유전

현재 인류 기준으로, 고지능자는 고지능 유전자가 많이 겹친 사람이다.

지능의 유전엔 X 염색체가 매우 중요한 역할을 한다. 그리고 이게 남자와 여자의 지능 분포 차이를 만든다. 극상위권에 여자가 거의 없는 이유가 이것이다.

고지능 X 염색체가 여자한테서 발현되려면 2개가 있어야 한다. 이는 인간의 생]]></description><link>https://kdy1.dev/human-intelligence</link><guid isPermaLink="true">https://kdy1.dev/human-intelligence</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Sun, 15 Mar 2026 02:15:08 GMT</pubDate><content:encoded><![CDATA[<ul>
<li>최종 업데이트: 2026/03/15</li>
</ul>
<hr />
<h2>지능의 유전</h2>
<ul>
<li><p>현재 인류 기준으로, 고지능자는 고지능 유전자가 많이 겹친 사람이다.</p>
</li>
<li><p>지능의 유전엔 X 염색체가 매우 중요한 역할을 한다. 그리고 이게 남자와 여자의 지능 <strong>분포</strong> 차이를 만든다. 극상위권에 여자가 거의 없는 이유가 이것이다.</p>
<ul>
<li><p>고지능 X 염색체가 여자한테서 발현되려면 2개가 있어야 한다. 이는 인간의 생물학적 특성인데, 여자는 X 성염색체가 2개이고 둘 중 하나는 세포군 단위 무작위로 비활성화된다. 이를 바소체 불활성화 또는 바소체 라이언화라고 부르는데, 발현 기댓값은 두 X 염색체의 평균이다.</p>
</li>
<li><p>극상위권에 여자가 적은 것은 이 고지능 X 염색체가 매우 희귀하기 때문이다. 한 사람이 2개나 가지기가 쉽지 않다.</p>
</li>
</ul>
</li>
</ul>
<h2>환경 vs 유전</h2>
<ul>
<li>같은 지능을 타고나도 훈련된 사람이 머리를 더 잘 쓴다. 간단한 예시로 수학 문제 푸는 것이 있다.</li>
</ul>
<h3>P.S.</h3>
<p>개인적으로는 환경 vs 유전 자체가 어불성설이라고 본다. <strong>지능</strong>이라는 것은 근본적으로, 진화라는 느린 과정 없이 런타임에 <strong>환경에 맞춰서 행동을 수정</strong>하겠다는 유전자의 전략이기 때문이다.</p>
<h2>지능의 종류</h2>
<ul>
<li>지능은 단일 수치가 아니다.</li>
</ul>
<h3>메타인지</h3>
<ul>
<li><p>안다고 생각했는데 시험을 보려고 하니까 제대로 아는 것이 아니었다하는 경험이 적을수록 메타인지가 발달한 것이다. 메타인지가 정말 잘 되는 사람이라면 저런 일이 단 한 번도 없을 것이다.</p>
</li>
<li><p>자기가 메타인지가 잘 된다고 착각하는 사람이 많은데, 메타인지가 잘 발달한 사람이라면 공부에선 어지간하면 극상위다. (최상위 x). 극상위가 아니었다면 착각일 확률이 높다. 시험 보기 전에 자기가 뭘 모르는지 아는 사람들은 그것을 공부하면 되기 때문에 공부를 못하기가 쉽지 않다.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Agentic Coding tips 2]]></title><description><![CDATA[This is a translation of https://kdy1.dev/2026-1-31-ai-coding-tips-kr

I recently gave a short presentation about how I use AI. The first part of the slides overlaps with a previous blog post. In this article, I’ll focus on topics that weren’t covere...]]></description><link>https://kdy1.dev/2026-1-31-ai-coding-tips-en</link><guid isPermaLink="true">https://kdy1.dev/2026-1-31-ai-coding-tips-en</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Fri, 30 Jan 2026 23:32:27 GMT</pubDate><content:encoded><![CDATA[<p>This is a translation of <a target="_blank" href="https://kdy1.dev/2026-1-31-ai-coding-tips-kr">https://kdy1.dev/2026-1-31-ai-coding-tips-kr</a></p>
<hr />
<p>I recently gave a short presentation about how I use AI. The first part of the slides overlaps with a <a target="_blank" href="https://kdy1.dev/2026-1-5-ai-coding-agent-tips">previous blog post</a>. In this article, I’ll focus on topics that weren’t covered in that post.</p>
<h2 id="heading-error-messages-and-logging">Error Messages and Logging</h2>
<h2 id="heading-using-concrete-types-and-schemas">Using Concrete Types and Schemas</h2>
<p>The <code>any</code> type is dangerous even for humans—but it’s even more dangerous for AI.</p>
<p>The same applies to things like:</p>
<ul>
<li><p>Unconstrained parsing such as <code>JSON.parse</code></p>
</li>
<li><p>Loose interfaces</p>
</li>
<li><p>Implicit or undocumented data structures</p>
</li>
</ul>
<p>These patterns force AI to make <strong>too many assumptions</strong>.</p>
<p>The real problem is that once an assumption is wrong, <em>all subsequent reasoning can spiral out of control</em>.</p>
<p>So I try to follow these principles as much as possible:</p>
<ul>
<li><p>Use the most concrete types possible instead of <code>any</code></p>
</li>
<li><p>Prefer schema-based parsers over simple parsing<br />  (e.g., Zod or Yup instead of <code>JSON.parse</code>)</p>
</li>
</ul>
<p>With this approach, even if the AI makes assumptions:</p>
<ul>
<li><p>The probability of those assumptions being wrong is lower</p>
</li>
<li><p>When they <em>are</em> wrong, the system fails fast</p>
</li>
</ul>
<p>In other words, this <strong>prevents AI from carrying incorrect reasoning all the way to the end</strong>.</p>
<h2 id="heading-leveraging-github-actions">Leveraging GitHub Actions</h2>
<p>If you look closely, GitHub Actions has some surprisingly strong properties:</p>
<ul>
<li><p>Completely isolated environments</p>
</li>
<li><p>Easy to configure</p>
</li>
<li><p>A large collection of well-prepared examples</p>
</li>
</ul>
<p>Instead of using it only for CI, I started treating it as a <strong>development virtual machine</strong>.</p>
<p>The same idea applies to AI.</p>
<blockquote>
<p>“Whatever a developer can do locally,<br />AI should be able to do in exactly the same way.”</p>
</blockquote>
<h3 id="heading-setting-up-a-development-environment-for-claude-code">Setting Up a Development Environment for Claude Code</h3>
<ul>
<li>Actual code: <a target="_blank" href="https://github.com/delinoio/delidev/blob/abed0d02fd30524bfb2f77f7227bf9560092e949/.github/workflows/claude.yml">https://github.com/delinoio/delidev/blob/abed0d02fd30524bfb2f77f7227bf9560092e949/.github/workflows/claude.yml</a></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Claude</span> <span class="hljs-string">Code</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">issue_comment:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">created</span>]
  <span class="hljs-attr">pull_request_review_comment:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">created</span>]
  <span class="hljs-attr">issues:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">assigned</span>]
  <span class="hljs-attr">pull_request_review:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">submitted</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">claude:</span>
    <span class="hljs-attr">if:</span> <span class="hljs-string">|
      (github.event_name == 'issue_comment' &amp;&amp; contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' &amp;&amp; contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review' &amp;&amp; contains(github.event.review.body, '@claude')) ||
      (github.event_name == 'issues' &amp;&amp; (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
</span>    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">pull-requests:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">issues:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>
      <span class="hljs-attr">actions:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># Required for Claude to read CI results on PRs</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">1</span>

      <span class="hljs-comment"># Setup pnpm</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">pnpm</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">pnpm/action-setup@v4.2.0</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Tauri</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
            sudo apt-get update
            sudo apt-get install -y \
              libwebkit2gtk-4.1-dev \
              libappindicator3-dev \
              librsvg2-dev \
              patchelf
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Rust</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">dtolnay/rust-toolchain@stable</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Claude</span> <span class="hljs-string">Code</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">claude</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">anthropics/claude-code-action@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">claude_code_oauth_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.CLAUDE_CODE_OAUTH_TOKEN</span> <span class="hljs-string">}}</span>

          <span class="hljs-comment"># This is an optional setting that allows Claude to read CI results on PRs</span>
          <span class="hljs-attr">additional_permissions:</span> <span class="hljs-string">|
            actions: read
</span>
          <span class="hljs-attr">claude_args:</span> <span class="hljs-string">|
            --allowed-tools Bash,WebFetch,WebSearch,Skill
            --model opus</span>
</code></pre>
<p>With GitHub Actions, you can set things up so that:</p>
<ul>
<li><p>Package managers are installed</p>
</li>
<li><p>Build commands are executed (<code>pnpm build</code>, <code>cargo build</code>, etc.)</p>
</li>
<li><p>Tests are run</p>
</li>
</ul>
<p>Once configured this way, Claude Code effectively operates in an environment that’s <strong>almost identical to a local development setup</strong>.</p>
<p>When the environment is this complete, the quality of AI output improves dramatically:</p>
<ul>
<li><p>It stops guessing</p>
</li>
<li><p>It reasons based on actual execution results</p>
</li>
<li><p>It produces “code that actually runs,” not just “code that should work in theory”</p>
</li>
</ul>
<h3 id="heading-managing-clean-commit-messages-with-ai">Managing Clean Commit Messages with AI</h3>
<p><img src="https://cdn.gamma.app/369hvd746fpmyb6/9780314e609f49c496cb045817adfa33/original/seukeurinsyas-2026-01-30-ohu-5.29.37.png" alt="screenshot" /></p>
<h3 id="heading-preventing-pr-description-spam">Preventing PR Description Spam</h3>
<ul>
<li>Actual code: <a target="_blank" href="https://github.com/delinoio/delidev/blob/abed0d02fd30524bfb2f77f7227bf9560092e949/.github/workflows/claude-code-review.yml#L34-L55">https://github.com/delinoio/delidev/blob/abed0d02fd30524bfb2f77f7227bf9560092e949/.github/workflows/claude-code-review.yml#L34-L55</a></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Claude</span> <span class="hljs-string">Code</span> <span class="hljs-string">Review</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">synchronize</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">claude-review:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">pull-requests:</span> <span class="hljs-string">write</span>
      <span class="hljs-attr">issues:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">1</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Dismiss</span> <span class="hljs-string">old</span> <span class="hljs-string">Claude</span> <span class="hljs-string">bot</span> <span class="hljs-string">comments</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">GH_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          REPO="${{ github.repository }}"
          PR_NUMBER="${{ github.event.pull_request.number }}"
</span>
          <span class="hljs-string">gh</span> <span class="hljs-string">api</span> <span class="hljs-string">"repos/$REPO/issues/$PR_NUMBER/comments"</span> <span class="hljs-string">--jq</span> <span class="hljs-string">'.[] | select(.user.login == "claude[bot]") | .node_id'</span> <span class="hljs-string">|</span> <span class="hljs-string">while</span> <span class="hljs-string">read</span> <span class="hljs-string">-r</span> <span class="hljs-string">comment_node_id;</span> <span class="hljs-string">do</span>
            <span class="hljs-string">if</span> [ <span class="hljs-string">-n</span> <span class="hljs-string">"$comment_node_id"</span> ]<span class="hljs-string">;</span> <span class="hljs-string">then</span>
              <span class="hljs-string">gh</span> <span class="hljs-string">api</span> <span class="hljs-string">graphql</span> <span class="hljs-string">-f</span> <span class="hljs-string">query='</span>
                <span class="hljs-string">mutation($id:</span> <span class="hljs-string">ID!)</span> {
                  <span class="hljs-string">minimizeComment(input:</span> {<span class="hljs-attr">subjectId:</span> <span class="hljs-string">$id</span>, <span class="hljs-attr">classifier:</span> <span class="hljs-string">OUTDATED</span>}<span class="hljs-string">)</span> {
                    <span class="hljs-string">minimizedComment</span> {
                      <span class="hljs-string">isMinimized</span>
                    }
                  }
                }<span class="hljs-string">' -f id="$comment_node_id"
            fi
          done

      - name: Run Claude Code Review
        id: claude-review
        uses: anthropics/claude-code-action@v1
        with:
          claude_code_oauth_token: $<span class="hljs-template-variable">{{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}</span>
          allowed_bots: '</span><span class="hljs-string">*'</span>
          <span class="hljs-attr">prompt:</span> <span class="hljs-string">|
            REPO: ${{ github.repository }}
            PR NUMBER: ${{ github.event.pull_request.number }}
</span>
            <span class="hljs-attr">Please review this pull request and provide feedback on:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Code</span> <span class="hljs-string">quality</span> <span class="hljs-string">and</span> <span class="hljs-string">best</span> <span class="hljs-string">practices</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Potential</span> <span class="hljs-string">bugs</span> <span class="hljs-string">or</span> <span class="hljs-string">issues</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Performance</span> <span class="hljs-string">considerations</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Security</span> <span class="hljs-string">concerns</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Test</span> <span class="hljs-string">coverage</span>

            <span class="hljs-string">Use</span> <span class="hljs-string">the</span> <span class="hljs-string">repository's</span> <span class="hljs-string">CLAUDE.md</span> <span class="hljs-string">for</span> <span class="hljs-string">guidance</span> <span class="hljs-string">on</span> <span class="hljs-string">style</span> <span class="hljs-string">and</span> <span class="hljs-string">conventions.</span> <span class="hljs-string">Be</span> <span class="hljs-string">constructive</span> <span class="hljs-string">and</span> <span class="hljs-string">helpful</span> <span class="hljs-string">in</span> <span class="hljs-string">your</span> <span class="hljs-string">feedback.</span>

            <span class="hljs-string">Use</span> <span class="hljs-string">`gh</span> <span class="hljs-string">pr</span> <span class="hljs-string">comment`</span> <span class="hljs-string">with</span> <span class="hljs-string">your</span> <span class="hljs-string">Bash</span> <span class="hljs-string">tool</span> <span class="hljs-string">to</span> <span class="hljs-string">leave</span> <span class="hljs-string">your</span> <span class="hljs-string">review</span> <span class="hljs-string">as</span> <span class="hljs-string">a</span> <span class="hljs-string">comment</span> <span class="hljs-string">on</span> <span class="hljs-string">the</span> <span class="hljs-string">PR.</span>

          <span class="hljs-attr">claude_args:</span> <span class="hljs-string">'--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'</span>
</code></pre>
<p>There’s one bug in the Claude Code GitHub review action: <strong>it leaves too many review comments</strong>.</p>
<p>This can easily result in PRs being flooded with AI-generated comments.</p>
<h2 id="heading-how-to-use-ai-reviews-effectively">How to Use AI Reviews Effectively</h2>
<h3 id="heading-core-assumptions">Core Assumptions</h3>
<p>Let’s be explicit about the premises:</p>
<ul>
<li><p>Human reviews are slow</p>
</li>
<li><p>Human reviews are expensive</p>
</li>
</ul>
<p>Therefore, the goal is to <strong>minimize human involvement</strong>.</p>
<p>The strategy I chose is:</p>
<ol>
<li><p>Run AI reviews and CI first</p>
</li>
<li><p>Humans do not intervene until AI gives an OK</p>
</li>
<li><p>Only when tests and automated reviews pass</p>
</li>
<li><p>A human performs the final review</p>
</li>
</ol>
<p>In short:</p>
<blockquote>
<p>Humans act only as the “final approver.”</p>
</blockquote>
<h3 id="heading-1-using-a-github-app">1. Using a GitHub App</h3>
<p>By integrating AI reviews as a GitHub App, reviews start automatically as soon as a PR is opened.</p>
<p>At this stage, AI filters out:</p>
<ul>
<li><p>Code style issues</p>
</li>
<li><p>Obvious bugs</p>
</li>
<li><p>Structural problems</p>
</li>
</ul>
<h3 id="heading-2-applying-changes-via-github-actions">2. Applying Changes via GitHub Actions</h3>
<p><img src="https://cdn.gamma.app/369hvd746fpmyb6/9151ee873c544e18a87b6143de64214c/original/image.png" alt="screenshot" /></p>
<p>Using tools like the Claude Code GitHub Action makes parallel processing much easier. Checking out code locally should be reserved for situations where human, local testing is truly required.</p>
<h3 id="heading-3-using-ci-as-a-gatekeeper">3. Using CI as a Gatekeeper</h3>
<p><img src="https://cdn.gamma.app/369hvd746fpmyb6/5afe35a1a3714b81976456d41f3bbdcb/original/seukeurinsyas-2026-01-30-ohu-5.33.30.png" alt="screenshot" /></p>
<p>The key is to treat CI not just as a testing tool, but as:</p>
<blockquote>
<p><strong>A barrier between AI and humans</strong></p>
</blockquote>
<p>If AI-generated changes can’t pass CI, they never reach human reviewers. That alone significantly reduces review costs.</p>
<hr />
<h2 id="heading-qampa">Q&amp;A</h2>
<h3 id="heading-why-mcp-is-unnecessary-for-this-use-case">Why MCP Is Unnecessary for This Use Case</h3>
<p>Imagine there is a CLI that provides the exact same capabilities as a specific MCP server. Anything you can do through MCP could also be done through that CLI. This is why Vercel chose to improve its CLI instead of building an MCP server.</p>
]]></content:encoded></item><item><title><![CDATA[Ai 코딩 팁 2 (한국어)]]></title><description><![CDATA[발표 자료: https://gamma.app/docs/AI--2a52e7tk3eb1ch1

AI 활용법 관련해서 간단하게 발표를 했다. 발표 자료 앞쪽은 전에 블로그에 올린 글이랑 같은 내용이다. 이 글에서는 기존 글에서 다루지 않은 내용들을 다루겠다.
에러 메시지 및 로깅
구체적 타입 및 스키마 활용
any 타입은 사람에게도 위험하지만, AI에게는 더 위험하다.
마찬가지로,

JSON.parse처럼 아무 제약 없는 파싱

느슨한 인터페이스

...]]></description><link>https://kdy1.dev/2026-1-31-ai-coding-tips-kr</link><guid isPermaLink="true">https://kdy1.dev/2026-1-31-ai-coding-tips-kr</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Fri, 30 Jan 2026 23:31:14 GMT</pubDate><content:encoded><![CDATA[<ul>
<li>발표 자료: <a target="_blank" href="https://gamma.app/docs/AI--2a52e7tk3eb1ch1">https://gamma.app/docs/AI--2a52e7tk3eb1ch1</a></li>
</ul>
<p>AI 활용법 관련해서 간단하게 발표를 했다. 발표 자료 앞쪽은 <a target="_blank" href="https://kdy1.dev/2026-1-5-ai-coding-agent-tips">전에 블로그에 올린 글</a>이랑 같은 내용이다. 이 글에서는 기존 글에서 다루지 않은 내용들을 다루겠다.</p>
<h2 id="heading-7jeq65siouploylnoyngcdrsi8g66gc6rmf">에러 메시지 및 로깅</h2>
<h2 id="heading-6rws7lk07kcbio2dgoyehsdrsi8g7iqk7ykk66eiio2znoyaqq">구체적 타입 및 스키마 활용</h2>
<p><code>any</code> 타입은 사람에게도 위험하지만, AI에게는 더 위험하다.</p>
<p>마찬가지로,</p>
<ul>
<li><p><code>JSON.parse</code>처럼 아무 제약 없는 파싱</p>
</li>
<li><p>느슨한 인터페이스</p>
</li>
<li><p>암묵적인 데이터 구조</p>
</li>
</ul>
<p>이런 것들은 AI가 <strong>너무 많은 가정을 하게 만든다</strong>.</p>
<p>문제는 그 가정이 틀리면, 이후의 모든 판단이 연쇄적으로 꼬인다는 점이다.</p>
<p>그래서 가능한 한 다음을 지향한다.</p>
<ul>
<li><p><code>any</code> 타입 대신 최대한 구체적인 타입 사용</p>
</li>
<li><p>단순 파싱 대신 스키마 기반 파서 사용<br />  (예: <code>JSON.parse</code> 대신 Zod, Yup 같은 라이브러리)</p>
</li>
</ul>
<p>이렇게 하면 AI가 가정을 하더라도,</p>
<ul>
<li><p>그 가정이 틀릴 확률이 줄어들고</p>
</li>
<li><p>틀렸을 경우 즉시 실패하도록 만들 수 있다</p>
</li>
</ul>
<p>즉, <strong>AI가 잘못된 추론을 끝까지 끌고 가는 상황을 예방</strong>할 수 있다.</p>
<h2 id="heading-github-actions">GitHub Actions 활용</h2>
<p>GitHub Actions를 잘 보면 꽤 괜찮은 특징을 갖고 있다.</p>
<ul>
<li><p>완전히 격리된 환경</p>
</li>
<li><p>구성하기 쉬움</p>
</li>
<li><p>이미 잘 구성된 예제가 많음</p>
</li>
</ul>
<p>이걸 단순히 CI 용도로만 쓰지 않고,<br /><strong>개발용 가상 머신</strong>처럼 사용하는 접근을 했다.</p>
<p>AI에게도 마찬가지다.</p>
<blockquote>
<p>“로컬에서 개발자가 할 수 있는 걸<br />AI도 똑같이 할 수 있게 만들어준다”</p>
</blockquote>
<p>라는 관점이다.</p>
<h3 id="heading-claude-code">Claude Code를 위한 개발 환경 구성</h3>
<ul>
<li>실제 코드: <a target="_blank" href="https://github.com/delinoio/delidev/blob/abed0d02fd30524bfb2f77f7227bf9560092e949/.github/workflows/claude.yml">https://github.com/delinoio/delidev/blob/abed0d02fd30524bfb2f77f7227bf9560092e949/.github/workflows/claude.yml</a></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Claude</span> <span class="hljs-string">Code</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">issue_comment:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">created</span>]
  <span class="hljs-attr">pull_request_review_comment:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">created</span>]
  <span class="hljs-attr">issues:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">assigned</span>]
  <span class="hljs-attr">pull_request_review:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">submitted</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">claude:</span>
    <span class="hljs-attr">if:</span> <span class="hljs-string">|
      (github.event_name == 'issue_comment' &amp;&amp; contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' &amp;&amp; contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review' &amp;&amp; contains(github.event.review.body, '@claude')) ||
      (github.event_name == 'issues' &amp;&amp; (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
</span>    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">pull-requests:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">issues:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>
      <span class="hljs-attr">actions:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># Required for Claude to read CI results on PRs</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">1</span>

      <span class="hljs-comment"># Setup pnpm </span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">pnpm</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">pnpm/action-setup@v4.2.0</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Tauri</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
            sudo apt-get update
            sudo apt-get install -y \
              libwebkit2gtk-4.1-dev \
              libappindicator3-dev \
              librsvg2-dev \
              patchelf
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Rust</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">dtolnay/rust-toolchain@stable</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Claude</span> <span class="hljs-string">Code</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">claude</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">anthropics/claude-code-action@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">claude_code_oauth_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.CLAUDE_CODE_OAUTH_TOKEN</span> <span class="hljs-string">}}</span>

          <span class="hljs-comment"># This is an optional setting that allows Claude to read CI results on PRs</span>
          <span class="hljs-attr">additional_permissions:</span> <span class="hljs-string">|
            actions: read
</span>
          <span class="hljs-attr">claude_args:</span> <span class="hljs-string">|
            --allowed-tools Bash,WebFetch,WebSearch,Skill
            --model opus</span>
</code></pre>
<p>GitHub Actions에서:</p>
<ul>
<li><p>패키지 매니저 설치</p>
</li>
<li><p>빌드 명령 실행 (<code>pnpm build</code>, <code>cargo build</code> 등)</p>
</li>
<li><p>테스트 실행</p>
</li>
</ul>
<p>까지 모두 가능하게 구성해두면,<br />Claude Code는 사실상 <strong>로컬 개발 환경과 거의 동일한 조건</strong>에서 동작한다.</p>
<p>이렇게 환경이 갖춰져 있으면 AI의 작업 품질이 눈에 띄게 좋아진다.</p>
<ul>
<li><p>추측으로 답하지 않고</p>
</li>
<li><p>실제 실행 결과를 기반으로 판단하고</p>
</li>
<li><p>“이론상 맞는 코드” 대신 “실제로 도는 코드”를 만든다</p>
</li>
</ul>
<h3 id="heading-ai">AI로 커밋 메시지 예쁘게 관리하기</h3>
<p><img src="https://cdn.gamma.app/369hvd746fpmyb6/9780314e609f49c496cb045817adfa33/original/seukeurinsyas-2026-01-30-ohu-5.29.37.png" alt /></p>
<h3 id="heading-pr">PR 본문 스팸 방지</h3>
<ul>
<li>실제 코드: <a target="_blank" href="https://github.com/delinoio/delidev/blob/abed0d02fd30524bfb2f77f7227bf9560092e949/.github/workflows/claude-code-review.yml#L34-L55">https://github.com/delinoio/delidev/blob/abed0d02fd30524bfb2f77f7227bf9560092e949/.github/workflows/claude-code-review.yml#L34-L55</a></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Claude</span> <span class="hljs-string">Code</span> <span class="hljs-string">Review</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">synchronize</span>]
    <span class="hljs-comment"># Optional: Only run on specific file changes</span>
    <span class="hljs-comment"># paths:</span>
    <span class="hljs-comment">#   - "src/**/*.ts"</span>
    <span class="hljs-comment">#   - "src/**/*.tsx"</span>
    <span class="hljs-comment">#   - "src/**/*.js"</span>
    <span class="hljs-comment">#   - "src/**/*.jsx"</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">claude-review:</span>
    <span class="hljs-comment"># Optional: Filter by PR author</span>
    <span class="hljs-comment"># if: |</span>
    <span class="hljs-comment">#   github.event.pull_request.user.login == 'external-contributor' ||</span>
    <span class="hljs-comment">#   github.event.pull_request.user.login == 'new-developer' ||</span>
    <span class="hljs-comment">#   github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'</span>

    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">pull-requests:</span> <span class="hljs-string">write</span>
      <span class="hljs-attr">issues:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">1</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Dismiss</span> <span class="hljs-string">old</span> <span class="hljs-string">Claude</span> <span class="hljs-string">bot</span> <span class="hljs-string">comments</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">GH_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          # Get PR comments from claude[bot] and hide them as outdated
          REPO="${{ github.repository }}"
          PR_NUMBER="${{ github.event.pull_request.number }}"
</span>
          <span class="hljs-comment"># Get issue comments (gh pr comment creates issue comments, not review comments)</span>
          <span class="hljs-string">gh</span> <span class="hljs-string">api</span> <span class="hljs-string">"repos/$REPO/issues/$PR_NUMBER/comments"</span> <span class="hljs-string">--jq</span> <span class="hljs-string">'.[] | select(.user.login == "claude[bot]") | .node_id'</span> <span class="hljs-string">|</span> <span class="hljs-string">while</span> <span class="hljs-string">read</span> <span class="hljs-string">-r</span> <span class="hljs-string">comment_node_id;</span> <span class="hljs-string">do</span>
            <span class="hljs-string">if</span> [ <span class="hljs-string">-n</span> <span class="hljs-string">"$comment_node_id"</span> ]<span class="hljs-string">;</span> <span class="hljs-string">then</span>
              <span class="hljs-string">echo</span> <span class="hljs-string">"Hiding review comment: $comment_node_id"</span>
              <span class="hljs-string">gh</span> <span class="hljs-string">api</span> <span class="hljs-string">graphql</span> <span class="hljs-string">-f</span> <span class="hljs-string">query='</span>
                <span class="hljs-string">mutation($id:</span> <span class="hljs-string">ID!)</span> {
                  <span class="hljs-string">minimizeComment(input:</span> {<span class="hljs-attr">subjectId:</span> <span class="hljs-string">$id</span>, <span class="hljs-attr">classifier:</span> <span class="hljs-string">OUTDATED</span>}<span class="hljs-string">)</span> {
                    <span class="hljs-string">minimizedComment</span> {
                      <span class="hljs-string">isMinimized</span>
                    }
                  }
                }<span class="hljs-string">' -f id="$comment_node_id"
            fi
          done

      - name: Run Claude Code Review
        id: claude-review
        uses: anthropics/claude-code-action@v1
        with:
          claude_code_oauth_token: $<span class="hljs-template-variable">{{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}</span>
          allowed_bots: '</span><span class="hljs-string">*'</span>
          <span class="hljs-attr">prompt:</span> <span class="hljs-string">|
            REPO: ${{ github.repository }}
            PR NUMBER: ${{ github.event.pull_request.number }}
</span>
            <span class="hljs-attr">Please review this pull request and provide feedback on:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Code</span> <span class="hljs-string">quality</span> <span class="hljs-string">and</span> <span class="hljs-string">best</span> <span class="hljs-string">practices</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Potential</span> <span class="hljs-string">bugs</span> <span class="hljs-string">or</span> <span class="hljs-string">issues</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Performance</span> <span class="hljs-string">considerations</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Security</span> <span class="hljs-string">concerns</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">Test</span> <span class="hljs-string">coverage</span>

            <span class="hljs-string">Use</span> <span class="hljs-string">the</span> <span class="hljs-string">repository's</span> <span class="hljs-string">CLAUDE.md</span> <span class="hljs-string">for</span> <span class="hljs-string">guidance</span> <span class="hljs-string">on</span> <span class="hljs-string">style</span> <span class="hljs-string">and</span> <span class="hljs-string">conventions.</span> <span class="hljs-string">Be</span> <span class="hljs-string">constructive</span> <span class="hljs-string">and</span> <span class="hljs-string">helpful</span> <span class="hljs-string">in</span> <span class="hljs-string">your</span> <span class="hljs-string">feedback.</span>

            <span class="hljs-string">Use</span> <span class="hljs-string">`gh</span> <span class="hljs-string">pr</span> <span class="hljs-string">comment`</span> <span class="hljs-string">with</span> <span class="hljs-string">your</span> <span class="hljs-string">Bash</span> <span class="hljs-string">tool</span> <span class="hljs-string">to</span> <span class="hljs-string">leave</span> <span class="hljs-string">your</span> <span class="hljs-string">review</span> <span class="hljs-string">as</span> <span class="hljs-string">a</span> <span class="hljs-string">comment</span> <span class="hljs-string">on</span> <span class="hljs-string">the</span> <span class="hljs-string">PR.</span>

          <span class="hljs-comment"># See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md</span>
          <span class="hljs-comment"># or https://code.claude.com/docs/en/cli-reference for available options</span>
          <span class="hljs-attr">claude_args:</span> <span class="hljs-string">'--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'</span>
</code></pre>
<p>Claude Code GitHub 리뷰 액션에는 버그가 하나 있다. 리뷰 코멘트를 <strong>너무 많이 남긴다</strong>.</p>
<p>이 때문에 PR이 리뷰 댓글로 도배되는 문제가 생긴다.</p>
<h2 id="heading-ai-1">AI 리뷰 활용법</h2>
<h3 id="heading-6riw67o4ioygkeq3va">기본 접근</h3>
<p>전제부터 분명히 하자.</p>
<ul>
<li><p>인간 리뷰는 느리다</p>
</li>
<li><p>인간 리뷰는 비싸다</p>
</li>
</ul>
<p>그래서 <strong>인간 리뷰를 최소화해야 한다</strong>.</p>
<p>이를 위해 선택한 전략은 다음과 같다.</p>
<ol>
<li><p>AI 리뷰 + CI를 먼저 통과시킨다</p>
</li>
<li><p>AI가 OK를 내릴 때까지는 인간이 개입하지 않는다</p>
</li>
<li><p>테스트와 자동 리뷰가 모두 통과된 경우에만</p>
</li>
<li><p>사람이 최종 리뷰를 한다</p>
</li>
</ol>
<p>즉,</p>
<blockquote>
<p>인간은 “최종 승인자” 역할만 맡는다</p>
</blockquote>
<ol>
<li><h3 id="heading-github">GitHub 앱 활용</h3>
</li>
</ol>
<p>AI 리뷰를 GitHub 앱 형태로 붙이면, PR 생성과 동시에 자동 리뷰가 시작된다.</p>
<p>이 단계에서는:</p>
<ul>
<li><p>코드 스타일</p>
</li>
<li><p>명백한 버그</p>
</li>
<li><p>구조적인 문제</p>
</li>
</ul>
<p>같은 것들을 AI가 먼저 걸러낸다.</p>
<ol start="2">
<li><h3 id="heading-github-actions-1">GitHub Actions를 사용해서 반영</h3>
<p> <img src="https://cdn.gamma.app/369hvd746fpmyb6/9151ee873c544e18a87b6143de64214c/original/image.png" alt /></p>
<p> 스크린샷과 같이 Claude Code GitHub Action등을 사용해서 반영해야 병렬 처리가 쉽다. 로컬에 체크아웃하는 것은 사람의 테스팅처럼 로컬에서 해야만 하는 상황에만 하는 것을 추천한다.</p>
</li>
<li><h3 id="heading-ci">CI 활용</h3>
</li>
</ol>
<p><img src="https://cdn.gamma.app/369hvd746fpmyb6/5afe35a1a3714b81976456d41f3bbdcb/original/seukeurinsyas-2026-01-30-ohu-5.33.30.png" alt /></p>
<p>중요한 건 CI를 단순한 테스트 도구가 아니라,</p>
<blockquote>
<p><strong>AI와 인간 사이의 방파제</strong></p>
</blockquote>
<p>로 사용하는 것이다. AI가 통과시키지 못한 변경사항은, 인간에게 도달하지 않게 만드는 것만으로도 리뷰 비용은 크게 줄어든다.</p>
<hr />
<h2 id="heading-qampa">Q&amp;A</h2>
<h3 id="heading-mcp">MCP가 필요없는 스펙인 이유</h3>
<p>특정 MCP 서버랑 똑같은 기능을 가진 CLI가 있다고 생각해보자. MCP를 통해서 수행할 수 있는 모든 건 CLI를 통해서도 수행할 수 있을 것이다. Vercel이 MCP를 만들지 않고 CLI 명령어를 개선한 것은 그래서이다.</p>
]]></content:encoded></item><item><title><![CDATA[Tips for AI coding agents]]></title><description><![CDATA[I’ve been using AI coding agents for a while, and I’d like to share my findings. The findings are mainly about Claude Code, but may apply to all coding agents, like opencode, if there’s no note about it.  

Intuitive API is a must.
Everything should ...]]></description><link>https://kdy1.dev/2026-1-5-ai-coding-agent-tips</link><guid isPermaLink="true">https://kdy1.dev/2026-1-5-ai-coding-agent-tips</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Mon, 05 Jan 2026 06:28:02 GMT</pubDate><content:encoded><![CDATA[<p>I’ve been using AI coding agents for a while, and I’d like to share my findings. The findings are mainly about Claude Code, but may apply to all coding agents, like opencode, if there’s no note about it.  </p>
<hr />
<h3 id="heading-intuitive-api-is-a-must">Intuitive API is a must.</h3>
<p>Everything should work intuitively, based on the <strong>name</strong>. AI tends to avoid digging into the lower layer unless you explicitly instruct it to fix it. For example, if <code>enabled: false</code> does not work in the code below, AI will fail to fix it most of the time because it <em>assumes</em> that the option works as expected.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767580108213/41e090a1-8546-4311-abec-666c94f6c4e3.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-subagents-may-reduce-context-window-usage">Subagents may reduce context window usage.</h3>
<blockquote>
<p>Use subagents proactively.</p>
</blockquote>
<p>You can utilize subagents to reduce the usage of the <strong>main</strong> context window. The only thing you need to do is saying <code>Use subagents proactively</code> in the prompt. It’s useful when you need to do a very large task. It makes Claude Code less intelligent for tasks that require the organic analysis of multiple clues, though.</p>
<p>Note: I didn’t define any subagents in my repository. General subagents are enough for this trick.</p>
<h3 id="heading-the-api-reference-document-reduces-the-guess-and-grep-loop">The API reference document reduces the guess-and-grep loop.</h3>
<p>If you don’t specify the name of the method you want to fix, the AI agent will guess the method/variable/module name and grep for it <strong>repeatedly</strong> until it finds the relevant code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767582431474/95ec0a3e-f479-486b-bbbf-605cf491f911.png" alt class="image--center mx-auto" /></p>
<p>This means that if you can provide an API reference document listing all method names, the coding agent will be much more efficient. As a side note, the best thing is an RPC API specification file that lists all method names and only the RPC message name.</p>
<pre><code class="lang-plaintext">syntax = "proto3";
package delino.appcore.v1;
option go_package = "github.com/delinoio/cloud/rpc/appcore";

import "appcore/auth.proto";

service AppCore {
  rpc UpdateUserProfile(UpdateUserProfileRequest) returns (UpdateUserProfileResponse);
}
</code></pre>
<p>Mentioning this file would give the AI agent</p>
<ul>
<li><p>the name of RPC methods to grep for</p>
</li>
<li><p>the names of RPC message types to grep for</p>
</li>
</ul>
<p>so the AI agent will never need to repeatedly guess the names.</p>
<h3 id="heading-provide-the-design-document-and-ask-the-ai-agent-to-update-it">Provide the design document, and ask the AI agent to update it.</h3>
<p>The source of truth <strong>was</strong> the code. And yeah, the primary source of truth is still code. But you can’t feed the whole code into AI agents, and updating code requires lots of tokens. I’m not talking about the token cost. It’s about the context window size. That’s why we need a specification document that is <strong>up-to-date with the codebase</strong> and <strong>committed to the repository</strong>.</p>
<p>Luckily, having one is easy. You don’t need to update it manually. Instead, instruct the AI agent to read the design document before any task, and update it as required.</p>
<h3 id="heading-you-may-start-building-in-a-separate-session-after-creating-a-plan">You may start building in a separate session after creating a plan.</h3>
<ul>
<li>Applies to: Claude Code.</li>
</ul>
<p>The plan mode is great. It’s a really wonderful feature that allows you to create a comprehensive task input prompt that mentions all the relevant files. But sometimes, Claude Code runs compaction nearly immediately after approving the plan because it does not clear the context before executing the plan. If you want, you can instruct it to write the comprehensive task prompt as a Markdown file, and <code>/clear</code> the context window, and paste it there.</p>
<p>In this way, Claude Code will run with a fresh context window, reading referenced files as required. It’s useful if you've done a lot of ping-pong with the planning agent.</p>
<h3 id="heading-provide-project-structure">Provide project structure.</h3>
<p>You can provide the project structure using the instruction file (<code>CLAUDE.md</code>/<code>AGENTS.md</code>) or a custom command. Giving the project structure also reduces the number of tokens wasted to guess the project structure.</p>
<p>Personally, I prefer a monorepo, so I have commands like <code>/work-on-devbird</code> that mentions the relevant frontend/backend directory, the RPC definition, and the design documents.</p>
<h3 id="heading-mention-the-filepath-or-the-names-of-utility-functions">Mention the filepath or the names of utility functions.</h3>
<p>This is a good fit for the instruction files, like <code>CLAUDE.md</code> and <code>AGENTS.md</code>. The AI agent has limited knowledge of the project, so it may repeat the same code over and over. It isn’t good. If you instruct it to read the utility file beforehand, it will reuse the existing function.</p>
<h3 id="heading-if-you-dont-understand-something-throw-the-whole-text-to-the-plan-mode">If you don’t understand something, throw the whole text to the Plan Mode.</h3>
<p>You don’t need to understand everything, seriously. You don’t need to give it a perfect prompt. Instead, you can use plan mode to expand your thoughts within the project context. Sometimes I didn’t understand some requirements because they are specific to the project, but Claude Code's plan mode expanded them into a concrete plan prompt. You can even ask Claude Code to ask you back. You can do clarification based on the questions the Claude Code asks.</p>
]]></content:encoded></item><item><title><![CDATA[2025년 회고]]></title><description><![CDATA[들어가며
2025년은 나한테 롤러코스터 같은 한 해였다. 많은 일들이 있었고 환경의 변화도 많았다. 미국과 한국을 오갔고, 소속된 곳이 바뀌었으며, 새로운 사람들을 만났다. 지난 1년을 타임라인을 따라 정리해 본다.
커리어 변화
4월 16일: Vercel 본사 근무를 위한 Relocation
Vercel 본사 근무를 위해 미국으로 이동했다. 짧지 않은 기간 동안 현지에서 일하며, 이전과는 다른 환경과 문화 속에서 일을 하게 됐다.
6월 16일:...]]></description><link>https://kdy1.dev/2025</link><guid isPermaLink="true">https://kdy1.dev/2025</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Sat, 20 Dec 2025 02:15:36 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-65ok7ja06rca66mw">들어가며</h2>
<p>2025년은 나한테 롤러코스터 같은 한 해였다. 많은 일들이 있었고 환경의 변화도 많았다. 미국과 한국을 오갔고, 소속된 곳이 바뀌었으며, 새로운 사람들을 만났다. 지난 1년을 타임라인을 따라 정리해 본다.</p>
<h2 id="heading-7luk66as7ja0iouzgo2zla">커리어 변화</h2>
<h3 id="heading-4-16-vercel-relocation">4월 16일: Vercel 본사 근무를 위한 Relocation</h3>
<p>Vercel 본사 근무를 위해 미국으로 이동했다. 짧지 않은 기간 동안 현지에서 일하며, 이전과는 다른 환경과 문화 속에서 일을 하게 됐다.</p>
<h3 id="heading-6-16">6월 16일: 귀국</h3>
<p>두 달 정도의 미국 생활을 마치고 한국으로 돌아왔다. 이 시점까지도 이후의 방향이 명확하게 정해져 있던 것은 아니었다.</p>
<h3 id="heading-7-vercel">7월: Vercel 퇴사</h3>
<p>어쩌다 보니 Vercel을 퇴사하게 됐다. 당시에는 회사가 지나치게 무례하다고 느꼈고, 더 이상 같이 일하고 싶지 않다는 감정이 컸다. 분노와 피로가 동시에 있었다. 말해봤자 무슨 소용이 있을까 싶을 정도였다.</p>
<p>시간이 조금 지난 뒤, 미국 기업 문화에 대해 미국에 거주하는 분들의 이야기를 듣고, 또 인공지능을 통해 정리해보면서 그 상황을 다시 바라보게 됐다. 미국 정서 기준으로 보면 회사 쪽이 특별히 잘못했다고 보기는 어려웠다. 한국 기준으로는 쉽게 받아들이기 힘든 방식이었지만, 억울함을 말하고 싶은 건 아니다. 그 당시의 나는 그렇게 느꼈고, 그게 전부였다.</p>
<h3 id="heading-7-delino">7월: Delino 창업</h3>
<p>나는 원래 창업이 하고 싶었다. 아이템도 여러 개 생각해두고 있었다. 마침 퇴사를 하게 되었고, 결과적으로는 잘 된 선택이었다고 생각한다. 감정과는 별개로, 방향을 바꾸기에 적절한 시점이었다.</p>
<h3 id="heading-8-9-real-prompter">8월 9일: Real Prompter 런칭</h3>
<p>Claude Code를 활용해 여러 서비스를 만들면서 한 가지를 계속 느꼈다. LLM을 제대로 활용하는 것은 생각보다 어렵다는 점이었다. 많은 사람들이 “프롬프팅은 어떻게 하느냐”고 물어봤고, 나는 반복해서 비슷한 답을 해왔다.</p>
<p>Roo Cline 등을 사용해 내가 실제로 하던 작업 방식들을 블로그와 SNS를 통해 여러 번 공유했고, 이를 반복하다 보니 서비스화하는 편이 낫겠다는 생각이 들었다. 그렇게 Real Prompter를 만들게 됐다.</p>
<h3 id="heading-10-14-autodev">10월 14일: AutoDev 런칭</h3>
<p>LLM을 잘 사용하는 것이 요령이나 감각의 문제인 것은 맞지만, 시스템적인 방식으로 근본적으로 해결할 수 있는 문제라고 생각했다. AI 덕분에 그 요령이나 감각조차 자동화가 가능한 시대라는 것이다.</p>
<p>AutoDev는 내가 이전부터 계속 이야기해오던 작업 방식을 자동화한 도구였다. 내가 강조해온 핵심은 몇 개 더 있지만, 가장 핵심적인 것은 세 가지였다.</p>
<ol>
<li><p>복잡한 작업을 적절한 단위로 쪼개는 것</p>
</li>
<li><p>여러 세션에 걸쳐 반복적으로 검증하고 피드백을 받는 구조</p>
</li>
<li><p>CI와 자동 테스트를 활용한 회귀(regression) 방지</p>
</li>
</ol>
<p>이건 새로운 아이디어라기보다는, 내가 계속 해오던 방식이었다. AutoDev는 그 방식을 도구로 옮긴 결과였다.</p>
<h3 id="heading-10-15">10월 15일: 인프런 멘토링</h3>
<p>돈을 받고 진행한 내 인생 첫번째 멘토링이라서 적기로 했다. 인프런을 통해서 진행한 것이었고, AI 활용법 및 오픈소스 관련 전략에 대해서 많은 얘기를 나누었다. 가격이 한국 기준으론 상당한 가격이었기에 감사한 마음으로 사전 질문들 및 추후 추가 질문들도 답변해드렸다.</p>
<h3 id="heading-10-23-autodev-devbird">10월 23일: AutoDev → DevBird 리브랜딩</h3>
<p>Microsoft에서 만든 AutoDev와 이름이 겹치면서 SEO가 사실상 불가능했다. 결국 DevBird로 리브랜딩했다. 제품의 방향이나 철학이 바뀐 것은 아니었다.</p>
<h3 id="heading-10-27-zack">10월 27일: Zack과 미팅</h3>
<p>Zephyr Cloud의 CEO인 Zack과 미팅을 가졌다. 네트워킹으로 분류할 수도 있었지만, 이후의 커리어와 연결되는 지점이 있어서 이 글에서는 커리어 이벤트로 남긴다.</p>
<h3 id="heading-11-1-zephyr-cloud">11월 1일: Zephyr Cloud 업무 시작</h3>
<hr />
<h2 id="heading-66eo7j2aioyxro2wiq">많은 여행</h2>
<p>이번 해엔 나답지 않게 여행을 많이 다녔다.</p>
<h3 id="heading-4-16-6-16-vercel">4월 16일 ~ 6월 16일: 미국 Vercel 근무</h3>
<p>미국에서 Vercel과 함께 일한 이 두 달은 즐거움과 떨림이 공존했던 시간이었다.<br />낯선 환경에서 일한다는 점에서 긴장도 됐지만, 좋은 친구들을 만나 덕분에 생각보다 빠르게 적응할 수 있었다. 새로운 사람들과 함께 일하며 많이 배웠다.</p>
<h3 id="heading-10">10월 초: 속초 가족 여행</h3>
<p>힐링의 시간이었다. 맛있는 음식을 함께 먹는 시간이 특히 좋았지만, 집에 두고 온 고양이가 계속 마음에 걸려서 조금 일찍 돌아와야 했던 점은 아쉬움으로 남았다.</p>
<h3 id="heading-10-1">10월 중순: 부산 여행</h3>
<p>동생이랑 부산 여행을 갔다왔다. 열심히 걸었지만 맛있는 음식을 너무 많이 먹었는지 갔다와서 재보니까 체중이 조금 늘었다. 그리고 갈 때 ITX를 탔다. 깨끗하긴한데 너무 느렸다. 그래서 올 때는 KTX 탔다.</p>
<p>해동 용궁사 같은 곳도 보고 왔고, 이재모 피자 같은 맛집도 많이 찾아다녔다.</p>
<h3 id="heading-11-16-23-for-zephyr-cloud">11월 16일 ~ 23일: 미국 출장 (for Zephyr Cloud)</h3>
<p>너무 재밌었다. 일주일간 한 customer onsite였는데 업무 시간엔 일을 엄청나게 많이 했고, 어쩌다보니 앞에 나가서 데모 발표도 했다. 그리고 업무 외 시간에는 대표분이 렌트하신 차로 같이 놀러다녔는데 이게 정말 재밌었다. 재밌는 여행을 할 수 있게 해주신 대표분께 감사드린다.</p>
<h3 id="heading-11-12">11월 말 ~ 12월 초: 일본 도쿄 여행</h3>
<p>동생이랑 일본 도쿄로 짧은 여행을 갔다왔다. 도쿄 디즈니랜드 가서 재밌게 놀았고, 미녀와 야수라는 가장 인기 많은 놀이기구에 충격을 받았다. 그래도 디즈니랜드에서 다른 건 다 재밌었어서 만족스러웠다.</p>
<p>근데 숙소 위치를 조금 잘못 잡았던 것 같다. 신주쿠 가부키초 쪽이었는데 저녁엔 무서워서 안 나갔다.</p>
<h3 id="heading-12">12월 중순: 속초 가족 여행</h3>
<p>홈캠 등의 장비들을 설치하고 가서 고양이 걱정을 덜 한 재밌는 여행이었다. 좋은 숙소에 묵고 맛있는 음식을 많이 먹은 여행이었는데, 숙소에서 일도 꽤 많이 했다.</p>
<hr />
<h2 id="heading-66eo7j2aiouepo2kuoybjo2cueqzvcdrsjztkzw">많은 네트워킹과 발표</h2>
<p>여러 자리에서 사람들을 만나며 느낀 건, 생각보다 많은 사람들이 비슷한 고민을 하고 있다는 점이었다.<br />“AI를 어떻게 잘 써야 할까?”라는 질문이 반복해서 나왔고, 나는 여전히 AI가 버블이라고 생각하지 않는다. 다만 AI를 제대로 쓰기 위해서는, 마법 같은 도구를 기대하기보다 구조와 시스템이 먼저 필요하다고 느꼈다.</p>
<h3 id="heading-1-vercel">1월 말: Vercel 최지원과 페어코딩</h3>
<p>Vercel의 최지원님과 진행한 페어코딩은 협업 자체가 굉장히 재미있었던 경험이었다.<br />미국 회사에서 일하면서 한국인 동료와 함께 코드를 짜는 시간이어서 더 편안하고 즐겁게 느껴졌다.</p>
<h3 id="heading-3-1">3월 1일: 팀 스파르타(항해) 손윤주 씨와 커피챗</h3>
<p>손윤주 씨와의 커피챗에서는 그분이 정말 성실하게 일하는 분이라는 인상을 받았다.<br />대화 중에는 내가 AI 이야기를 많이 했고, 노트북까지 꺼내 직접 데모를 보여주며 이야기를 이어갔다.</p>
<h3 id="heading-3-11-ai">3월 11일: 항해 AI 웨비나</h3>
<p>이 웨비나는 개인적으로 정말 즐거운 경험이었다.<br />평소 주변 사람들에게 “AI는 이제 무조건 써야 한다”고 이야기하고 다녔는데, 그 메시지를 웨비나라는 공식적인 자리에서 정리해 전달할 수 있어서 좋았다. AI 도구들을 소개하며 내가 계속 강조해오던 이야기를 꺼낼 수 있었던 시간이 오래 기억에 남는다.</p>
<h3 id="heading-3-27">3월 27일: 성균관대학교 강연</h3>
<p>원래는 긴장을 안 할 거라고 생각했지만, 교수님들이 지켜보고 계셔서 생각보다 긴장이 됐다.<br />그럼에도 불구하고 교수님들이 흥미롭게 들어주시고 질문까지 해주셔서 강연을 마치고 나서는 꽤 뿌듯한 기분이 들었다.</p>
<h3 id="heading-8-6">8월 6일: 오픈소스 밋업</h3>
<p>오픈소스 밋업에서는 다양한 이야기를 들을 수 있었지만, 한편으로는 아쉬운 지점도 있었다.<br />오픈소스 기여를 마치 개인의 성과나 이력서 한 줄처럼 이야기하는 분위기가 있었는데, 오픈소스는 결국 문제를 함께 해결하기 위해 모이는 공동체라는 점을 다시 생각하게 됐다.</p>
<h3 id="heading-9-16">9월 16일: 크래프톤 정글 팟캐스트</h3>
<p>이 자리에서는 Vercel에서의 경험과 실리콘밸리에서의 일하는 방식, 그리고 AI 시대에 개발자가 어떤 자세를 가져야 하는지에 대해 많이 이야기했다.<br />전하고 싶은 메시지가 많다 보니 조금 정리가 덜 된 채로 이야기를 한 점은 아쉬움으로 남았다.</p>
<h3 id="heading-9-18-marco-ippolito">9월 18일: Marco Ippolito 님의 서울 방문</h3>
<ul>
<li><a target="_blank" href="https://x.com/satanacchio/status/1968670634885500997">https://x.com/satanacchio/status/1968670634885500997</a></li>
</ul>
<p>Marco Ippolito 님과의 만남은 서로의 경험을 나누며 굉장히 유익한 시간이었다.<br />Node 컨트리뷰터분이신데 인사이트가 매우 깊었고, 개인적으로도 많은 것을 배울 수 있었다.</p>
<h3 id="heading-9-30-hoco">9월 30일: HOCO 팟캐스트</h3>
<p>크래프톤 정글 팟캐스트와 비슷한 주제를 다뤘지만, 이번에는 미리 블로그 글로 생각을 정리해 둔 상태에서 이야기를 풀어갈 수 있었다.<br />덕분에 훨씬 정돈된 흐름으로 설명할 수 있었고, 스스로도 잘했다고 느낀 자리였다.</p>
<h3 id="heading-10-24">10월 24일: 토스 프다클</h3>
<p>토스 프다클에서는 사람들이 AI를 사용하는 수준이 생각보다 크게 다르다는 걸 실감했다.<br />그 경험을 통해 내가 앞으로 무엇을 해야 할지, 또 어떤 방식으로 사람들을 도울 수 있을지 감을 잡게 됐다. 동시에 개인적으로도 많은 인사이트를 얻은 의미 있는 시간이었다.</p>
<h3 id="heading-10-30-ai-2">10월 30일: 항해 AI 웨비나 2</h3>
<hr />
<h2 id="heading-6riw7yoaioydtouypo2kua">기타 이벤트</h2>
<h3 id="heading-2">2월 중순: 수면 문제 해결</h3>
<p>내가 원래 잠을 되게 못 자는 사람이었다. 어느 날 우연히 상추차를 접하게 되었는데, 상추차를 마시니까 잠이 잘 왔다. 며칠 실험해보고 이건 진짜라는 것을 깨달았다. 그리고 지금은 상추환으로 정착했다. 상추차를 농축해서 환으로 만들어놓은 것인데, 상추차는 차의 특성상 우리거나 마시는 데에 시간이 걸리고, 물을 많이 마시다보니 아침에 자꾸 깨서 상추환이 훨씬 나았다. 상추환을 꾸준히 먹다보니 수면에 관련된 호르몬 자체가 선순환으로 들어간건지 이제는 낮 시간에도 졸리면 낮잠을 잘 수 있게 됐다. 삶의 질이 아주 높아져서 행복하다.</p>
<h3 id="heading-11">11월 초: 사촌동생 결혼식</h3>
<p>사촌동생 결혼식에 참석했다. 놀랍게도 이게 내가 가 본 첫번째 결혼식이다. 나보다 어린 나이에, 오랜 시간 함께한 연인과 결혼하는 모습을 보니까 부러웠고, 나도 원래 한명하고 오래 연애하다가 27살 쯤에 결혼하고 싶었던 사람이라서 여러 가지 생각이 들었다. 이 이벤트가 나한테 꽤나 큰 변화를 남겨서 회고록에 적는다.</p>
<h2 id="heading-66ei7lmy66mw">마치며</h2>
<p>2025년을 지나온 지금, 예전보다 자유롭고 행복하다. 내 일을 충분히 잘 해내고 있는 것 같아서 뿌듯하기도 하다. 여전히 실험 중이고, 여전히 복잡한 문제를 풀고 있지만, 지금의 상태는 나쁘지 않다고 느낀다.</p>
]]></content:encoded></item><item><title><![CDATA[Ai 시대의 개발자]]></title><description><![CDATA[오늘 발표에서 얘기할 내용들입니다.

기본 태도

적극적으로 사용해야 함. 모든 도구를 사용해볼 필요는 없지만, 좋은 평을 받는 도구는 시도해볼 것.

AI를 적극적으로 활용하는 기업 기준, 현 시점에서 이미 프론트엔드/백엔드 같은 것은 안 중요함.

모든 것을 다 알아야한다는 태도는 반드시 버릴 것. 이것은 개발 씬에서 원래도 문제임. 솔직하게 말하면 개발 소질 이슈. 애매한 중상위권의 특징임.

원리 이해 같은 경우, 난이도가 훨씬 낮지만 ...]]></description><link>https://kdy1.dev/2025-9-30-developer-in-ai-era</link><guid isPermaLink="true">https://kdy1.dev/2025-9-30-developer-in-ai-era</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Mon, 29 Sep 2025 15:00:00 GMT</pubDate><content:encoded><![CDATA[<hr />
<p>오늘 발표에서 얘기할 내용들입니다.</p>
<hr />
<h2 id="heading-6riw67o4io2dnoupha">기본 태도</h2>
<ul>
<li><p>적극적으로 사용해야 함. 모든 도구를 사용해볼 필요는 없지만, 좋은 평을 받는 도구는 시도해볼 것.</p>
</li>
<li><p>AI를 적극적으로 활용하는 기업 기준, 현 시점에서 이미 프론트엔드/백엔드 같은 것은 안 중요함.</p>
</li>
<li><p>모든 것을 다 알아야한다는 태도는 반드시 버릴 것. 이것은 개발 씬에서 원래도 문제임. 솔직하게 말하면 개발 소질 이슈. 애매한 중상위권의 특징임.</p>
<ul>
<li><p>원리 이해 같은 경우, 난이도가 훨씬 낮지만 효율적인 방법은 아님.</p>
<ul>
<li>자기가 국소적 이해 기반의 코딩을 할 수 없다면 전체 설계를 이해해서라도 코딩을 해야함.</li>
</ul>
</li>
<li><p>큰 프로젝트 여러 개를 동시에 해야하는 경우 국소적 이해로 코딩하는 능력이 필수임.</p>
<ul>
<li><p>이 능력을 습득하고 싶으면 규모가 꽤 있지만 본인은 잘 모르는 오픈소스에 기여하는 것이 최고라고 생각.</p>
</li>
<li><p>오픈소스에서 이것이 특히 중요한데, 규모가 큰 프로젝트 여러 개에 각각 패치를 보내야하는 일이 생각보다 자주 있는데 물어볼 사람도 없고, 문서도 내부 구현에 대한 것은 없는 경우가 대부분인데 각 프로젝트를 제대로 이해하고 작업하려고 하면 작업 시간이 지나치게 늘어남.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="heading-67cu7j2067im7l2u65sp7j2yiousuoygncdtlbtqsrdssyu">바이브코딩의 문제 해결책</h2>
<ul>
<li><p>바이브 코딩의 단점을 막을 가장 중요한 해결책은 <strong>테스트</strong></p>
</li>
<li><p>스펙 문서는 항상 넘겨주고 적절한 수정을 요청할 것.</p>
</li>
<li><p>바이브 코딩 이후 작업에 대한 설명이 명확하게 이해가 안 간다면 관련 개념을 공부하는 것을 추천. 그 뒤에도 이해가 안 가면 컴퓨팅적 사고력의 문제.</p>
</li>
</ul>
<h2 id="heading-ai">AI 시대의 능력</h2>
<ul>
<li><p>1인 창업이 맞는 시대라고 보지만 (당연히) 자기 능력 검증은 필수.</p>
</li>
<li><p>AI 없이 처음부터 끝까지 개발하기 위한 능력은 필요치 않음.</p>
</li>
<li><p>다시 말해, <strong>코딩하는 능력</strong> 자체는 이제 의미가 없음.</p>
</li>
<li><p>하지만 AI 를 활용해서 학습하고 코딩하는 능력은 매우 중요함.</p>
<ul>
<li>바이브 코딩 이후 AI의 설명이 이해가 안 간다면 복사해서 다른 인공지능 챗봇에 붙여넣은 뒤 어떤 개념을 공부해야 이 설명을 이해할 수 있는지 물어보고 공부할 것.</li>
</ul>
</li>
</ul>
<p>이 시대의 문제 해결 능력은 2가지</p>
<ul>
<li><p>새로운 문제를 만들지 않는 것</p>
<ul>
<li>핵심은 테스트의 활용</li>
</ul>
</li>
<li><p>주어진 문제를 고치는 것</p>
<ul>
<li><p>AI를 활용해보고, 문제가 생겼을 때 AI를 활용해서 해결한 경험이 많은 것이 중요함.</p>
</li>
<li><p>어떻게 하면 해결되는지는 본인이 전부 기억하고 있어야 함.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-vs">지능 vs 지식</h2>
<ul>
<li><p>원래 시대가 빠르게 바뀔수록 순수 지능이 중요함. 근데 지능이란 건 함수 같은 것이고 쓰던 사람이 잘 씀. 공부할 때도 머리를 많이 쓰는 방식을 사용하는 것이 좋음.</p>
<ul>
<li><p>바이브 코딩하는 방법을 책이나 강의로 배울 생각을 한다면 바이브코딩 관련 재능 중 제일 중요한 것이 없는 것. 공부 방법을 근본적으로 바꿔야 함.</p>
</li>
<li><p>강의나 책은 자기의 사고력을 덜 쓰는 방법이라서 편하지만 이 시대에 최악인 습관임.</p>
</li>
</ul>
</li>
<li><p>CS 지식 자체는 중요하지 않음. 중요한 건 컴퓨팅적 사고. 이게 처음부터 자유자재로 되는 사람이 있는데 그런 사람은 AI 적극적으로 쓴다는 전제 하에 CS 지식 따로 안 배워도 됨. 근데 이 애기는 천재에 대한 얘기가 아니고 생각보다 이것이 처음부터 되는 사람이 많음. 코테는 컴퓨팅적 사고가 안 되는 사람을 거르는 역할을 함.</p>
</li>
<li><p>코딩하는 능력 자체도 마찬가지로 코딩하는 능력이 없어도 거의 모든 개발이 가능함. 그래서 프로덕트 엔지니어라는 직군으로 바뀌는 것.</p>
</li>
<li><p>메타인지가 오히려 더 중요. 자기가 메타인지가 있는지 확인하는 방법:</p>
<ul>
<li><p>안다고 생각했는데 시험 때 생각 안 났다라는 경험이 한번이라도 있으면 메타인지가 없다고 치고 공부 계획을 짜야함.</p>
</li>
<li><p>자기가 모르는데 안다고 착각하면 해결책 없는 교착 상태가 생김. 안다고 전제하지 말 것.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-6riw7yoaioygleuzta">기타 정보</h2>
<ul>
<li><p>실리콘밸리의 새로운 변화는 보통 1~2달 정도의 시차를 두고 한국에 들어옴.</p>
<ul>
<li>프로덕트 엔지니어 직군 / AI 네이티브 인재 채용 (카카오) 등</li>
</ul>
</li>
<li><p>코테는 허수 (컴퓨팅적 사고 안 되는 사람들) 거르기 + 면접에 드는 품 줄이기.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[내가 거리를 두고 싶은 사람들]]></title><description><![CDATA[난 이런 사람들과는 거리를 두고싶다. 공격적으로 느껴질 수도 있는 글이라 쓸지 말지 고민 조금 했는데 쓰기로 했다.


사람 급 나누기에 미친 사람들

중범죄자

음주운전 등.


자기의 조그만 이익을 위해 남들한테 큰 피해를 주는 걸 아무렇지 않게 생각하는 사람들.

예를 들어 우회전 할 때 반드시 지나가야하는 위치에 차 세워놓는 사람들.


자기 잘못은 어떻게든 합리화하는 사람들.

남들한테 피해주면서까지 돈 벌려는 사람들.

각종 망상이 ...]]></description><link>https://kdy1.dev/2025-9-29-people</link><guid isPermaLink="true">https://kdy1.dev/2025-9-29-people</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Mon, 29 Sep 2025 00:02:27 GMT</pubDate><content:encoded><![CDATA[<p>난 이런 사람들과는 거리를 두고싶다. 공격적으로 느껴질 수도 있는 글이라 쓸지 말지 고민 조금 했는데 쓰기로 했다.</p>
<hr />
<ul>
<li><p>사람 급 나누기에 미친 사람들</p>
</li>
<li><p>중범죄자</p>
<ul>
<li>음주운전 등.</li>
</ul>
</li>
<li><p>자기의 조그만 이익을 위해 남들한테 큰 피해를 주는 걸 아무렇지 않게 생각하는 사람들.</p>
<ul>
<li>예를 들어 우회전 할 때 반드시 지나가야하는 위치에 차 세워놓는 사람들.</li>
</ul>
</li>
<li><p>자기 잘못은 어떻게든 합리화하는 사람들.</p>
</li>
<li><p>남들한테 피해주면서까지 돈 벌려는 사람들.</p>
</li>
<li><p>각종 망상이 심각한 사람들.</p>
</li>
<li><p>나 이용하려는 게 너무 티나는 사람들.</p>
</li>
<li><p>자기 잘못을 이해하는 능력이 글러먹은 사람들.</p>
</li>
<li><p>윤리의식이 나랑 너무 안 맞는 사람들.</p>
</li>
<li><p>쓰레기 짓 해놓고 지가 머리 잘 썼다고 생각하는 사람들.</p>
</li>
<li><p>자신의 이익을 위해 상황을 변조하는 사람들.</p>
<ul>
<li>일부 사실만 공개하는 것은 제외.</li>
</ul>
</li>
<li><p>남의 돈으로 생색내는 사람들.</p>
</li>
</ul>
<p>이렇게 늘어놓으면 인생 피곤하게 사는 것처럼 들릴 수도 있는데 이 사람들한테 공통점이 있어서 피하는 것이 그리 어렵지는 않은 것 같다.</p>
]]></content:encoded></item><item><title><![CDATA[How to use Claude Code via GitHub]]></title><description><![CDATA[Setup
I configured Claude Code GitHub Action correctly. You should add setup steps to emulate a real development environment before anthropics/claude-code-action@beta. The setup steps depend on the repository, but I’ll provide my Claude Code Action S...]]></description><link>https://kdy1.dev/2025-8-24-claude-code-github</link><guid isPermaLink="true">https://kdy1.dev/2025-8-24-claude-code-github</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Sat, 23 Aug 2025 15:00:00 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-setup">Setup</h2>
<p>I configured Claude Code GitHub Action <em>correctly</em>. You should add setup steps to emulate a real development environment before <code>anthropics/claude-code-action@beta</code>. The setup steps depend on the repository, but I’ll provide my Claude Code Action Setup as a reference.</p>
<p><code>.github/workflows/claude.yml</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Claude</span> <span class="hljs-string">Code</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">issue_comment:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">created</span>]
  <span class="hljs-attr">pull_request_review_comment:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">created</span>]
  <span class="hljs-attr">issues:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">assigned</span>]
  <span class="hljs-attr">pull_request_review:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">submitted</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">claude:</span>
    <span class="hljs-attr">if:</span> <span class="hljs-string">|
      (github.event_name == 'issue_comment' &amp;&amp; contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' &amp;&amp; contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review' &amp;&amp; contains(github.event.review.body, '@claude')) ||
      (github.event_name == 'issues' &amp;&amp; (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
</span>    <span class="hljs-attr">runs-on:</span> [<span class="hljs-string">'self-hosted'</span>, <span class="hljs-string">'linux'</span>]
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">pull-requests:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">issues:</span> <span class="hljs-string">read</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>
      <span class="hljs-attr">actions:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># Required for Claude to read CI results on PRs</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">1</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Protoc</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">arduino/setup-protoc@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">repo-token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node.js</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">./.github/actions/setup-node</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Configure</span> <span class="hljs-string">environment</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">pnpm</span> <span class="hljs-string">install</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Claude</span> <span class="hljs-string">Code</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">claude</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">anthropics/claude-code-action@beta</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">claude_code_oauth_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.CLAUDE_CODE_OAUTH_TOKEN</span> <span class="hljs-string">}}</span>

          <span class="hljs-comment"># This is an optional setting that allows Claude to read CI results on PRs</span>
          <span class="hljs-attr">additional_permissions:</span> <span class="hljs-string">|
            actions: read
</span>
          <span class="hljs-comment"># Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)</span>
          <span class="hljs-comment"># model: "claude-opus-4-20250514"</span>

          <span class="hljs-comment"># Optional: Customize the trigger phrase (default: @claude)</span>
          <span class="hljs-comment"># trigger_phrase: "/claude"</span>

          <span class="hljs-comment"># Optional: Trigger when specific user is assigned to an issue</span>
          <span class="hljs-attr">assignee_trigger:</span> <span class="hljs-string">"claude-bot"</span>

          <span class="hljs-comment"># Optional: Allow Claude to run specific commands</span>
          <span class="hljs-attr">allowed_tools:</span> <span class="hljs-string">"Bash,WebFetch,WebSearch"</span>

          <span class="hljs-comment"># Optional: Add custom instructions for Claude to customize its behavior for your project</span>
          <span class="hljs-comment"># custom_instructions: |</span>
          <span class="hljs-comment">#   Follow our coding standards</span>
          <span class="hljs-comment">#   Ensure all new code has tests</span>
          <span class="hljs-comment">#   Use TypeScript for new files</span>

          <span class="hljs-comment"># Optional: Custom environment variables for Claude</span>
          <span class="hljs-comment"># claude_env: |</span>
          <span class="hljs-comment">#   NODE_ENV: test</span>
</code></pre>
<p>Note <code>allowed_tools: "Bash,WebFetch,WebSearch"</code>. You need it to allow Claude Code to run bash commands.</p>
<h2 id="heading-usage">Usage</h2>
<p>Mention <code>@claude</code> from issues or PRs to instruct it to resolve issues.</p>
<p>Example:</p>
<blockquote>
<p><a class="user-mention" href="https://hashnode.com/@https://github.com/claude">@claude</a> <a target="_blank" href="https://github.com/claude">Implem</a>ent comprehensive unit tests</p>
</blockquote>
<h2 id="heading-pro-tip-you-can-use-local-claude-code-to-create-a-handful-of-issues-quickly">Pro Tip: You can use local Claude Code to create a handful of issues quickly</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755992979320/fae16172-d327-4362-b695-db63dd5f54db.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Tip: TailScale]]></title><description><![CDATA[When developing, you often need to connect to your development computer from your phone. In such cases, you need to connect to the same router or use a program like ngrok to connect to your development computer.
However, there is another method, and ...]]></description><link>https://kdy1.dev/tip-tailscale</link><guid isPermaLink="true">https://kdy1.dev/tip-tailscale</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Tue, 19 Aug 2025 15:00:00 GMT</pubDate><content:encoded><![CDATA[<p>When developing, you often need to connect to your development computer from your phone. In such cases, you need to connect to the same router or use a program like <code>ngrok</code> to connect to your development computer.</p>
<p>However, there is another method, and this one is the most convenient and free. It involves using a virtual private network (VPN) service called <strong>Tailscale</strong>. After logging in and registering your device, each device is assigned an IP address.</p>
<p>After running the server on your development laptop, you can check the IP address of the device in the Tailscale app and connect to it using that IP address, as shown in the screenshot, to access the development server.</p>
<p>This is also the best option when creating a service that only you can access, as you don't need to set up complex external access settings. Install Tailscale on the specific device, deploy the service, and connect. Installing Tailscale on the server can also be done with a single line of script, making it very convenient.</p>
]]></content:encoded></item><item><title><![CDATA[Tip for reducing AI cost]]></title><description><![CDATA[If you have a personal VPS, you can reduce your AI subscription fees by deploying OpenWebUI. By connecting OpenRouter, you can use various models without a fixed subscription fee, paying only for what you use via the API. Multiple users can share acc...]]></description><link>https://kdy1.dev/2025-8-14-tip-for-reducing-ai-cost</link><guid isPermaLink="true">https://kdy1.dev/2025-8-14-tip-for-reducing-ai-cost</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Wed, 13 Aug 2025 15:00:00 GMT</pubDate><content:encoded><![CDATA[<p>If you have a personal VPS, you can reduce your AI subscription fees by deploying OpenWebUI. By connecting OpenRouter, you can use various models without a fixed subscription fee, paying only for what you use via the API. Multiple users can share accounts, and in my case, I created accounts for my family members as well. Combined, my family and I paid a total of <strong>$42.44 for seven months</strong> of usage. Usage details can be viewed in the OpenRouter API Key Console.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755994282891/2a681dfd-0eb8-49c2-a541-70a877b4477a.png" alt class="image--center mx-auto" /></p>
<p>As you can see on the left side of the screenshot, chats can be organized like folders, and there are features such as setting different system prompts for each folder. For example, you can set it to only use official documents for matters related to law or taxation.</p>
<p>With LLM models, connecting OpenRouter allows you to utilize a huge number of models freely. The models shown in the screenshot are ones that I have selected and activated. Since my family also uses it, I thought it might be unclear if there were too many.</p>
<p>When you turn on the web search function in the chat feature, it searches for information and organizes it for you. When the image feature is enabled, it generates images. When numbers appear during the inference process or there is a lot to manage, it uses LLM to code, executes it, and then uses the results to provide answers.</p>
<p>Since it supports PWA, you can add it to the home screen on mobile devices or use the app installation feature in desktop Chrome to use it like a native app.</p>
]]></content:encoded></item><item><title><![CDATA[Tip for GitHub Runners Concurrency]]></title><description><![CDATA[The number of concurrent GitHub Action runners is determined by the plan of the GitHub Org to which the repository belongs, regardless of whether the repository is public. Even though GitHub Actions are unlimited and free for public repositories, the...]]></description><link>https://kdy1.dev/2025-8-12-tip-for-github-runners</link><guid isPermaLink="true">https://kdy1.dev/2025-8-12-tip-for-github-runners</guid><category><![CDATA[GitHub]]></category><category><![CDATA[GitHub Actions]]></category><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Mon, 11 Aug 2025 15:00:00 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755994052423/9cb8ef76-8aa5-419b-85c2-2861ecf0ffa8.png" alt class="image--center mx-auto" /></p>
<p>The number of concurrent GitHub Action runners is determined by the plan of the GitHub Org to which the repository belongs, regardless of whether the repository is public. Even though GitHub Actions are unlimited and free for public repositories, they are still affected. Because of this, SWC uses the Enterprise plan.</p>
<p>If you're running a large open-source project and are having issues with GitHub Actions only running 20 jobs at a time, you can upgrade your plan to Team or Enterprise.</p>
<ul>
<li>Documentation link: <a target="_blank" href="https://docs.github.com/en/actions/reference/limits#job-concurrency-limits-for-github-hosted-runners">https://docs.github.com/en/actions/reference/limits#job-concurrency-limits-for-github-hosted-runners</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How I use Claude Code]]></title><description><![CDATA[Some people asked me on X about the way I use Claude Code, so I'm writing this post. I'm too tired to write a long post, so I keep it short. I use Roo Code with Architecture mode first, with a prompt like

Service XXX is for ...
If anything is ambigu...]]></description><link>https://kdy1.dev/2025-7-7-how-i-use-claude-code</link><guid isPermaLink="true">https://kdy1.dev/2025-7-7-how-i-use-claude-code</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Sun, 06 Jul 2025 15:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Some people asked me on X about the way I use Claude Code, so I'm writing this post. I'm too tired to write a long post, so I keep it short. I use <strong>Roo Code with Architecture mode</strong> first, with a prompt like</p>
<blockquote>
<p>Service XXX is for ...</p>
<p>If anything is ambiguous or you are not sure about it, do not infer but ask me.</p>
</blockquote>
<p>After spending a handful of times for clarifying everything and describing entity relations, I ask it to store it as a markdown file. Typically, it generates a very, very long text. I then follow getting started step of the framework I want to use. The latest one I used is <code>tauri</code>. I do this step manually just because typically it's just a few CLI commands. After creating a directory, I ask Claude Code to</p>
<blockquote>
<p>Implement app in @apps/xxx.</p>
<p>[Paste the design markdown file content]</p>
</blockquote>
<p>You'll get a bare minimum prototype that works with dummy data. Even after getting the initial version, I ask Claude Code for all modifications.</p>
]]></content:encoded></item><item><title><![CDATA[par-core & par-iter: Switchable parallelization for Rust]]></title><description><![CDATA[tl;dr:
I forked rayon to allow switching parallelization library or disable parallelization. See par-iter and par-core.

I'm the creator of the SWC project. I've been using chili the SWC Minifier. chili is a parallelization library with a heartbeat s...]]></description><link>https://kdy1.dev/2025-3-26-par-core-and-par-iter</link><guid isPermaLink="true">https://kdy1.dev/2025-3-26-par-core-and-par-iter</guid><category><![CDATA[Rust]]></category><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Wed, 26 Mar 2025 07:46:59 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>tl;dr:</p>
<p>I forked <code>rayon</code> to allow switching parallelization library or disable parallelization. See <a target="_blank" href="https://docs.rs/par-iter">par-iter</a> and <a target="_blank" href="https://docs.rs/par-core">par-core</a>.</p>
</blockquote>
<p>I'm the creator of <a target="_blank" href="https://swc.rs">the SWC project</a>. I've been using <code>chili</code> the SWC Minifier. <code>chili</code> is a parallelization library with a heartbeat scheduling algorithm. I found that it performs far better than <code>rayon</code> for my usecase. But it lacks an API like parallel iterators of <code>rayon</code>. Additionally, I need to use different parallelization libraries for <code>next.js</code> or <code>rspack</code> and disable parallelism for Wasm targets. So I forked <code>rayon</code> and named it <a target="_blank" href="https://docs.rs/par-iter">par-iter</a>. <a target="_blank" href="https://docs.rs/par-iter"><code>par-iter</code></a> is based on <a target="_blank" href="https://docs.rs/par-core/"><code>par-core</code></a>, which allows selecting <code>rayon</code> or <code>chili</code> for parallelism or even disabling parallelism using cargo features.</p>
<p>I prefer to get it merged back to <code>rayon</code> , so I filed <a target="_blank" href="https://github.com/rayon-rs/rayon/issues/1235">an issue on the rayon issue tracker to ask if they are open to PR for it</a>, but I don't think it's likely, considering the package name.</p>
<p>Any ideas, thoughts, and feedback are welcome!</p>
]]></content:encoded></item><item><title><![CDATA[성균관대 Gdg에서 발표할 예정이다]]></title><description><![CDATA[발표 자료 만들다가 이 내용을 블로그에 적지 않았다는 것을 깨달아서 늦게나마 올린다.]]></description><link>https://kdy1.dev/2025-3-22-skku-gdg</link><guid isPermaLink="true">https://kdy1.dev/2025-3-22-skku-gdg</guid><category><![CDATA[GDG]]></category><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Sat, 22 Mar 2025 05:35:12 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742621660656/40cb5e83-d64e-4a33-a5c6-2a34d91d433b.jpeg" alt class="image--center mx-auto" /></p>
<p>발표 자료 만들다가 이 내용을 블로그에 적지 않았다는 것을 깨달아서 늦게나마 올린다.</p>
]]></content:encoded></item><item><title><![CDATA[AutoPilot status update - 2]]></title><description><![CDATA[This week, I introduced a concept of a base image to to the AutoPilot service. A user can configure their own base image as they want, by installing all necessary tools or running builds so that the build cache exists for the worker from start.

This...]]></description><link>https://kdy1.dev/2025-3-22-autopilot-status-update</link><guid isPermaLink="true">https://kdy1.dev/2025-3-22-autopilot-status-update</guid><category><![CDATA[Dudy AutoPilot]]></category><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Sat, 22 Mar 2025 03:54:48 GMT</pubDate><content:encoded><![CDATA[<p>This week, I introduced a concept of a <code>base image</code> to to the AutoPilot service. A user can configure their own base image as they want, by installing all necessary tools or running builds so that the build cache exists for the worker from start.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742615311914/ed65daa6-8de3-4f8b-8cfc-447619c6828f.png" alt class="image--center mx-auto" /></p>
<p>This is the screenshot of a base image setup process. I installed nodejs and did <code>pnpm install</code>. So there’s a folder named <code>node_modules</code>. After then, I clicked <code>Finish Setup</code> button, an pressing the button creates a base docker image for the worker. It utilizes the <code>docker commit</code> command.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742615383521/3c918e0e-82cc-4ee5-8f09-9bb4ff07b037.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Screenshot captured from a running instance of <code>pnpm dev</code> (using turborepo)</li>
</ul>
<p>And after waiting for a while, I could use the base image in a worker. The base image does not have the AutoPilot vscode extension nor Roo Code, but the worker image has one.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742615477783/09386302-e289-411b-b6b9-36e267fa89ef.png" alt class="image--center mx-auto" /></p>
<p>This is the screenshot of a new worker instance, from which you can find <code>node_modules</code>, Roo Code, and Dudy AutoPilot vscode extension.</p>
<hr />
<p>With this change, a worker can drive a task from the start to the end by itself without an interaction. But I still need to polish lots of things, and I would be busy this weekend because of a GDG event.</p>
]]></content:encoded></item><item><title><![CDATA[AutoPilot development status update - 1]]></title><description><![CDATA[The AutoPilot project aims to take an AI-powered code generation tool that works well like Roo Code and use it as a PR generator. It’s quite similar to Devin, but it will be much cheaper than Devin.

Side note: I’ll not sell this as SaaS, to avoid co...]]></description><link>https://kdy1.dev/2025-3-15-auto-pilot</link><guid isPermaLink="true">https://kdy1.dev/2025-3-15-auto-pilot</guid><category><![CDATA[status update]]></category><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Sat, 15 Mar 2025 14:50:44 GMT</pubDate><content:encoded><![CDATA[<p>The AutoPilot project aims to take an AI-powered code generation tool that works well like Roo Code and use it as a PR generator. It’s quite similar to Devin, but it will be much cheaper than Devin.</p>
<ul>
<li>Side note: I’ll not sell this as SaaS, to avoid competing with v0</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742049166241/a2f0632a-2066-41a4-a767-a320b43930e1.png" alt class="image--center mx-auto" /></p>
<p>Yesterday, I managed to <strong>automatically</strong> spawn a <code>code-server</code> instance with my <code>Dudy AutoPilot</code> extension and <code>Roo Code</code> extension installed. Once the user approves the plan for a task, a vscode worker is spawned to resolve the task.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742049659888/72449444-a7e1-482b-a5e5-36022ff000d5.png" alt class="image--center mx-auto" /></p>
<p>Today, I made it automatically clone the repository, configure <code>Roo Code</code> using secrets sent by the server, and start a new task with <code>Roo Code</code>.</p>
]]></content:encoded></item><item><title><![CDATA[Prompt Context 번역]]></title><description><![CDATA[Prompt Context는 AI를 거대한 레포지토리에서 사용할 수 있게 해주는 아이디어입니다. 사용하시는 IDE나 도구는 거의 상관 없습니다. 그리고 이 방식을 사용하면 거대하고 복잡한 모노레포에서도 AI가 잘 작동합니다.

tl;dr;

제 .cursorrules 파일은 다음과 같습니다.
 - First, look for glob `**/.AI.md`. Use terminal command `find . -name '.AI.md'` from...]]></description><link>https://kdy1.dev/2025-3-12-prompt-context-kr</link><guid isPermaLink="true">https://kdy1.dev/2025-3-12-prompt-context-kr</guid><dc:creator><![CDATA[DongYoon Kang]]></dc:creator><pubDate>Wed, 12 Mar 2025 11:56:25 GMT</pubDate><content:encoded><![CDATA[<p>Prompt Context는 AI를 거대한 레포지토리에서 사용할 수 있게 해주는 아이디어입니다. 사용하시는 IDE나 도구는 거의 상관 없습니다. 그리고 이 방식을 사용하면 거대하고 복잡한 모노레포에서도 AI가 잘 작동합니다.</p>
<blockquote>
<p>tl;dr;</p>
</blockquote>
<p>제 <code>.cursorrules</code> 파일은 다음과 같습니다.</p>
<pre><code class="lang-markdown"><span class="hljs-bullet"> -</span> First, look for glob <span class="hljs-code">`**/.AI.md`</span>. Use terminal command <span class="hljs-code">`find . -name '.AI.md'`</span> from the project root, if required.
<span class="hljs-bullet"> -</span> Second, if you are going to read and/or write any file in a directory containing <span class="hljs-code">`.AI.md`</span> files from the first step, read all of them and respect the instructions in them.
<span class="hljs-bullet"> -</span> For example, if you are reading or writing a file at /a/b/c, you should check for <span class="hljs-code">`/a/.AI.md`</span>, <span class="hljs-code">`/a/b/.AI.md`</span> and <span class="hljs-code">`/a/b/c/.AI.md`</span> in the order.
</code></pre>
<p>감이 오시나요? 이런 방식으로 AI한테 어떤 폴더에서는 어떻게 작업해야하는지 폴더별로 가르쳐줄 수 있습니다. <code>context.md</code> 파일을 통해서요.</p>
<p>아이디어는 인공지능이 작업을 하기 위해 필요한 경우 임의의 파일을 읽어올 수 있다는 점에서 착안했습니다. 제 레포지토리는 전혀 느낌이 다른 여러개의 next.js 앱과 protobuf RPC를 위한 코드를 포함해 여러가지 복잡한 패키지들이 있는데요, 각 폴더에서 해야하는 작업이 너무 다릅니다. 하지만 저는 이 기술을 사용해서 AI가 코드를 잘 짜게 만들 수 있었습니다.</p>
<p><strong>장점:</strong></p>
<ul>
<li><p>전체 폴더를 위한 작업 지침을 넘기는 것이 아니기 때문에 컨텍스트 사이즈가 거의 문제되지 않습니다.</p>
</li>
<li><p>폴더별로 작성하기 때문에 관리하기 쉬워집니다.</p>
</li>
<li><p>AI가 코드 스타일을 더 잘 지킵니다. (지키지 않는다면 <code>context.md</code> 에 적으면 됩니다.)</p>
</li>
<li><p>AI가 더 높은 퀄리티의 코드를 짭니다.</p>
</li>
<li><p><code>.cursorrules</code> 방식의 암시적 프롬프트가 레포지토리의 복잡도와 상관없이 작동합니다.</p>
</li>
</ul>
<p>예시 <code>context.md</code> 파일입니다.</p>
<p><code>/context.md</code>:</p>
<p>(레포지토리의 최상단에 있습니다)</p>
<pre><code class="lang-markdown">
<span class="hljs-section"># RPC</span>

We use connect rpc to call RPC methods. The protobuf files are located at <span class="hljs-code">`${gitRoot}/rpc/*/v1.proto`</span>.

<span class="hljs-section">## When to use RPC vs client-side code</span>

<span class="hljs-bullet">-</span> Basically, prefer client-side code over RPC.
<span class="hljs-bullet">-</span> If you need to call a nodejs-only code, use RPC.
<span class="hljs-bullet">-</span> If you need to call code that uses any secret, use RPC.

<span class="hljs-section">## Implementing RPC methods</span>

<span class="hljs-bullet">-</span> First, add the RPC method definitions and message definitions to the protobuf files.
<span class="hljs-bullet">-</span> You should register the RPC method implementations in the <span class="hljs-code">`${gitRoot}/servers/*-api/connect.ts`</span> file.
<span class="hljs-bullet">-</span> You should not store all code in the <span class="hljs-code">`${gitRoot}/servers/*-api/connect.ts`</span> file. Instead, define a service and put the implementation in another file in the same directory.

<span class="hljs-section">## Calling RPC methods</span>

<span class="hljs-bullet">-</span> Do not fetch directly from the page component. Instead, define a hook to fetch the data and use it in the page component.

From a react component, you can define a hook using the <span class="hljs-code">`useMutation`</span> method.

<span class="hljs-code">```js
const detectFaces = webtoolsClient.detectFaces.useMutation();
```</span>

After then, you can call the method with <span class="hljs-code">`mutateAsync`</span> method.

<span class="hljs-code">```js
const result = await detectFaces.mutateAsync({
  imageData: new Uint8Array(buffer),
});
```</span>
</code></pre>
<p><code>/apps/context.md</code>:</p>
<pre><code class="lang-markdown"><span class="hljs-bullet">1.</span> This directory contains next.js apps. Follow the next.js app directory structure.
<span class="hljs-bullet">2.</span> List all files before you start coding. Follow the file structure.
<span class="hljs-bullet">3.</span> Do not fetch directly from the page component. Instead, define a hook to fetch the data and use it in the page component.
<span class="hljs-bullet">4.</span> Prefer explicit path parameters in components over 'useParams'.
<span class="hljs-bullet">5.</span> Do not generate shadcn ui components.
<span class="hljs-bullet">6.</span> If you are trying to run the test script in package.json (e.g. <span class="hljs-code">`pnpm test:unit`</span>), ensure you set environment variable <span class="hljs-code">`CI=1`</span> to indicate it's running automatically.
<span class="hljs-bullet">7.</span> Follow Toss Design guidelines.
<span class="hljs-bullet">8.</span> Ignore route-related TypeScript type errors.
<span class="hljs-bullet">9.</span> If you need to call mutating RPC methods, use the <span class="hljs-code">`useMutation`</span> hook.

<span class="hljs-code">```ts
const createProject = coreClient.createProject.useMutation();
```</span>

<span class="hljs-bullet">10.</span> If you need to call query RPC methods, use the <span class="hljs-code">`useQuery`</span> hook.

<span class="hljs-code">```ts
const projects = coreClient.listProjects.useQuery();
```</span>

<span class="hljs-bullet">11.</span> Include <span class="hljs-code">`locale: Locale`</span> where <span class="hljs-code">`Locale`</span> is <span class="hljs-code">`import { Locale } from "@/i18n";`</span> in the params of the page components.
<span class="hljs-bullet">12.</span> Define the params as <span class="hljs-code">`Promise&lt;T&gt;`</span> in the page components.
<span class="hljs-bullet">13.</span> Use <span class="hljs-code">`use(params)`</span> to unwrap the params as <span class="hljs-code">`T`</span> from <span class="hljs-code">`Promise&lt;T&gt;`</span> in the page components.
<span class="hljs-bullet">14.</span> Import RPC clients from <span class="hljs-code">`@dudykr/rpc-clients/src/XXX`</span>. where <span class="hljs-code">`XXX`</span> is the name of the RPC client.

e.g.

<span class="hljs-code">```ts
import { coreClient } from "@dudykr/rpc-clients/src/core";
```</span>

<span class="hljs-bullet">15.</span> Use <span class="hljs-code">`&lt;Link&gt;`</span> from <span class="hljs-code">`next/link`</span> instead of the <span class="hljs-code">`&lt;a&gt;`</span> tag.

<span class="hljs-section"># Translation</span>

For the next.js apps, modify only <span class="hljs-code">`./messages/en.json`</span> and run <span class="hljs-code">`pnpm translate`</span>.
This will translate the messages to all languages.
</code></pre>
<p>이것들은 실제로 제가 사용하는 <code>context.md</code> 파일들입니다. 흥미롭다면 한 번 시도해보시는 걸 추천드립니다.</p>
]]></content:encoded></item></channel></rss>