<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>흔한 컴공 출신 개발자</title>
    <link>https://developer-youn.tistory.com/</link>
    <description>흔한 컴공 출신 개발자입니다.
제가 경험하고 공부하고 아는 것을 적어둡니다.</description>
    <language>ko</language>
    <pubDate>Sun, 2 Aug 2026 14:31:21 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Berno</managingEditor>
    <image>
      <title>흔한 컴공 출신 개발자</title>
      <url>https://tistory1.daumcdn.net/tistory/3231755/attach/b3f313dcfd0547ab94737070eb56b210</url>
      <link>https://developer-youn.tistory.com</link>
    </image>
    <item>
      <title>[postgres deep/shallow/wide dive] deep - vaccum과 autovacuum, vaccum full 1편 - vacuum과 dead tuple</title>
      <link>https://developer-youn.tistory.com/204</link>
      <description>&lt;p&gt;postgres를 사용하다 보면 보이는 단어가 vaccum이다. 근데 이게 뭘 하는지 정확하게 잘 모르는 사람들이 있어서(보통 무지성 기술 선택의 결과로 postgres를 사용하거나 이미 잘 동작해서 알아볼 생각을 안해서) 정리를 해보려고 한다.(가끔 다른 RDBMS에 대한 비교나 삼천포로 빠질 확률 농후, 하지만 지식을 얻어갔죠?)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/2a2891e2-469b-4df5-874c-f067600e4177/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;실은 이미 내 글 말고도&lt;br&gt;&lt;a href=&quot;https://techblog.woowahan.com/9478/&quot;&gt;우아한형제들 기술블로그&lt;/a&gt; 와 같이 훌륭한 글들이 이미 있다.&lt;/p&gt;
&lt;p&gt;내가 주로 참고할건 공식문서와 &lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000060603553&quot;&gt;https://product.kyobobook.co.kr/detail/S000060603553&lt;/a&gt; - postgresql 9.6 성능 이야기(광고아님. 광고였으면 좋겠음)&lt;br&gt;&lt;a href=&quot;https://blog.ex-em.com/1667&quot;&gt;https://blog.ex-em.com/1667&lt;/a&gt; -  우리나라에서 postgres관련 블로그 가히 원탑으로 저는 말합니다&lt;br&gt;기타 등등을 참고한다.&lt;/p&gt;
&lt;h2&gt;VACCUM?&lt;/h2&gt;
&lt;p&gt;우선 vaccum에 대한 공식 문서를 좀 봐보자.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/f59a52d5-2c39-4ac6-a49c-e66a708f6d96/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;VACUUM reclaims storage occupied by dead tuples. In normal PostgreSQL operation, tuples that are deleted or obsoleted by an update are not physically removed from their table; they remain present until a VACUUM is done. Therefore it&amp;#39;s necessary to do VACUUM periodically, especially on frequently-updated tables.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;처음부터 딱 핵심만 알려주는 너무 좋은 말이 나온다.&lt;br&gt;VACUUM을 사용해서 dead tuple이 차지한 공간을 회수한다. 일반적인 PostgreSQL이 동작하고 있는(운영되고 있는) 상황에서는 삭제되거나 업데이트 되어 더 이상 필요하지 않게 된 튜플이 &amp;#39;물리적으로&amp;#39; 제거 되지 않는다. VACUUM이 실행될때까지 남아있는다. 그래서 자주 업데이트 되는 테이블에서는 VACUUM을 특히 주기적으로 진행해야한다.&lt;/p&gt;
&lt;p&gt;이러면 좀 궁금해진다. 왜 남아있지? 이건 이 시리즈에서 꼬리물기로 한번 다루어보겠다. 우선 하고자 하는 부분에 집중하자.&lt;/p&gt;
&lt;p&gt;그 뒤에 글을 좀 더 읽어보면&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Plain VACUUM (without FULL) simply reclaims space and makes it available for re-use. This form of the command can operate in parallel with normal reading and writing of the table, as an exclusive lock is not obtained. However, extra space is not returned to the operating system (in most cases); it&amp;#39;s just kept available for re-use within the same table. It also allows us to leverage multiple CPUs in order to process indexes. This feature is known as parallel vacuum. To disable this feature, one can use PARALLEL option and specify parallel workers as zero. VACUUM FULL rewrites the entire contents of the table into a new disk file with no extra space, allowing unused space to be returned to the operating system. This form is much slower and requires an ACCESS EXCLUSIVE lock on each table while it is being processed.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Plain VACUUM(FULL 없이)은 단순히 공간을 회수해서 &amp;#39;재사용 가능&amp;#39;하게 만든다. 즉, 이 명령어는 기본적인 READ/WRITE와 병렬로 작동이 가능하다. 그러나 대부분의 케이스에서 추가 공간은 OS에 반환되지 않고 재사용 가능한 빈 공간으로 남아있다.&lt;/p&gt;
&lt;p&gt;여기서 약간 단어적으로 혼동이 왔었는데 extra space라는건 VACUUM을 통해 재사용 가능하게 만들어진 공간(space)를 의미하고 있다.&lt;/p&gt;
&lt;p&gt;만약 OS레벨에서, 새 디스크 파일로 재기록하여 사용되지 않는 공간을 반환하게 만들고 싶다면(물리적으로 하드디스크 용량을 확보하고 싶다면) VACUUM FULL을 사용하면 된다. 단, 이 작업은 각 테이블에 대해 ACCESS EXCLUSIVVE LOCK이 필요하다.(즉 병렬로 처리가 불가능하며 해당 작업을 하는 동안 해당 테이블에 접근을 못하니 함부로 돌리면 안된다는 뜻)&lt;/p&gt;
&lt;h2&gt;DEAD TUPLE?&lt;/h2&gt;
&lt;p&gt;어 근데 이제 슬슬 정말 의문을 해소할 순간이 왔다. 도대체 dead tuple은 왜 생기는가?&lt;br&gt;이걸 알기 위해 우리는 PostgreSQL의 MVCC에 대해 알아볼 필요가 있다.&lt;/p&gt;
&lt;p&gt;우선 MVCC란 Multiversion Concurrency Control의 약자로 쉽게 말하면 DBMS상에서 여러 트랜잭션이 동시에 READ/WRITE를 해도 서로 간섭 없이 동작하도록 하는 동시성 제어 기법이다. PostgreSQL에서는 이 MVCC를 각 ROW에 대해 여러 버전(version = tuple)을 만드는 방식을 차용했다. 즉, 새로 row가 업데이트 된다면 없어지는게 아니라 tuple version update가 되는 것이다.&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/1f2ae248-d648-4960-a317-c5969cf5305b/image.png&quot; alt=&quot;https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-postgresql-we-hate-the-most.html&quot;&gt;&lt;br&gt;&lt;em&gt;이미지 출처 : &lt;a href=&quot;https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-postgresql-we-hate-the-most.html&quot;&gt;https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-postgresql-we-hate-the-most.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;이제 우리는 하나의 row에 대해 여러 tuple이 생길 수 있다는 것을 알았다. 그러면 이제 이 tuple이 그 어디에서도 사용되지 않는 tuple이 생기게 되는데 이걸 dead tuple이라고 한다. 이 dead tuple은 자동으로 제거되지도 않고 회수되지도 않는다.(FSM에 대해서는 나중에 간략하게 정리해보도록 하자)&lt;/p&gt;
&lt;p&gt;좋다. 이제 어느정도 VACUUM과 VACUUM FULL에 대해서 알았다. 그러면 AUTOVACUUM은 무엇인가?&lt;br&gt;GC가 있는 언어를 다루어보았다면 어느정도 heap space가 차거나 trigger되었을때 GC가 발생하는 것을 보았을 것이다. 비슷하게 trigger가 걸려 VACUUM이 자동 수행되는걸 AUTOVACUUM이라고 한다.(문제는 DB에서는 AUTOVACUUM 옵션을 꺼두어도 threshold를 넘어서면 강제로 DB에서 진행함)&lt;/p&gt;
&lt;p&gt;2편에서 계속&lt;/p&gt;
&lt;p&gt;velog에서 똑같은 글을 본다면 제 다른 블로그가 맞습니다.&lt;/p&gt;</description>
      <category>컴퓨터공학/데이터베이스</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/204</guid>
      <comments>https://developer-youn.tistory.com/204#entry204comment</comments>
      <pubDate>Wed, 10 Dec 2025 14:27:07 +0900</pubDate>
    </item>
    <item>
      <title>[kotlin deep/shallow/wide dive] deep - suspend가 정확히 뭐고 CPS는 뭐고  state machine이 뭐죠는 꼬리에 꼬리를 물고(2)</title>
      <link>https://developer-youn.tistory.com/203</link>
      <description>&lt;p&gt;1편 : &lt;a href=&quot;https://velog.io/@bernoyoun/kotlin-deepshallowwide-dive-deep-suspend-1&quot;&gt;https://velog.io/@bernoyoun/kotlin-deepshallowwide-dive-deep-suspend-1&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;그러면 이제 state machine이 뭔지를 좀 알아보자.&lt;/h2&gt;
&lt;p&gt;혹시 state pattern(상태 패턴)이라는걸 design pattern 공부할때 본 적이 있는 사람이 있을 수 있다. 그래도 우리 같이 복습하는 의미로 다시 봐보자.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://refactoring.guru/design-patterns/state&quot;&gt;https://refactoring.guru/design-patterns/state&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/56b5b63a-8b14-46c4-9d14-0f1077447e6a/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;기본적으로는 FSM(finite-state machine)에서 컨셉을 상당히 가져온 것 같다.&lt;br&gt;간단히 요약해보면&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 상태에 대해서는 유한한 개수(finite)하게 존재함&lt;/li&gt;
&lt;li&gt;각 상태에 따라서 행동이 달라지며 미리 결정됨&lt;/li&gt;
&lt;li&gt;현재 상태에서 다른 상태로 전환을 할 수 있고 안할 수 도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 state의 transition을 관리하기 위해 state-machine이란게 존재함. 문제는 무작정 만들기 시작하면 무수한 if-else or switch-case 난타로 고통받을 수 있음. 그럴때 이 state pattern을 도입하면 숨을 좀 고를 수 있음.&lt;/p&gt;
&lt;p&gt;이제 state-machine을 좀 봐보자.&lt;br&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/State_machine&quot;&gt;https://developer.mozilla.org/en-US/docs/Glossary/State_machine&lt;/a&gt;&lt;br&gt;결과적으로 여기도 같은 말을 하고 있다.&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/31943bdd-5e4f-4e92-9712-160a9ed06afc/image.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;상태에 대한 관리, transition에 대한 관리를 한다.&lt;/p&gt;
&lt;h2&gt;이제 coroutine state machine을 이해해보자.&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://kotlinlang.org/spec/asynchronous-programming-with-coroutines.html#coroutine-state-machine&quot;&gt;https://kotlinlang.org/spec/asynchronous-programming-with-coroutines.html#coroutine-state-machine&lt;/a&gt;&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/ea056221-5ede-409a-8f3d-fd966bb1faa8/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;약간 어질어질한 내용이 나온다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Kotlin implements suspendable functions as state machines, since such implementation does not require specific runtime support. This dictates the explicit suspend marking (function colouring) of Kotlin coroutines: the compiler has to know which function can potentially suspend, to turn it into a state machine.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;state-machine에서 관리를 하는게 아니라 suspendable function을 state machine으로 구현하고 runtime에서 뭔가 특별히 support할 필요가 없다고 한다. 지금 내가 상당히 편협한 생각을 가지고 있는가 해서 여러가지 문서들을 좀 찾아봤다.&lt;br&gt;&lt;a href=&quot;https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#state-machines&quot;&gt;https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#state-machines&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;여기를 보면&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/084b18bb-9c83-4446-8fbe-3f58896cd567/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;It is crucial to implement coroutines efficiently, i.e. create as few classes and objects as possible. Many languages implement them through state machines and Kotlin does the same. In the case of Kotlin this approach results in the compiler creating only one class per suspending lambda that may have an arbitrary number of suspension points in its body.&lt;br&gt;Main idea: a suspending function is compiled to a state machine, where states correspond to suspension points. Example: let&amp;#39;s take a suspending block with two suspension points:&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이런 내용이 있다.&lt;br&gt;즉 이게 메인 아이디어라는 건데..&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;suspend 함수는 상태 머신으로 컴파일된다,&lt;/li&gt;
&lt;li&gt;여기서 “상태(state)”는 각 suspension point를 의미한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이미 잘 정리된 글 처럼 보이지만 다시 하나 하나 정리를 해가면서 머리에 넣어보자.&lt;br&gt;예를 들어 아래와 같은 코드가 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;val a = a()
val y = foo(a).await() // suspension point #1
b()
val z = bar(a, y).await() // suspension point #2
c(z)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기에는 3개의 상태가 존재한다:&lt;br&gt;    1.    초기 상태 (아직 어떤 suspension도 발생하기 전)&lt;br&gt;    2.    첫 번째 suspension point 이후&lt;br&gt;    3.    두 번째 suspension point 이후&lt;br&gt;각 상태는 이 블록의 실행을 이어서 진행할 “continuation에 대한 진입점(entry point)”이다.&lt;/p&gt;
&lt;p&gt;이후 컴파일 단계에서 위 코드는 &amp;#39;한 개의 anonymous class&amp;#39;로 컴파일된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;class &amp;lt;anonymous_for_state_machine&amp;gt; extends SuspendLambda&amp;lt;...&amp;gt; {
    // The current state of the state machine
    int label = 0

    // local variables of the coroutine
    A a = null
    Y y = null

    void resumeWith(Object result) {
        if (label == 0) goto L0
        if (label == 1) goto L1
        if (label == 2) goto L2
        else throw IllegalStateException()

      L0:
        // result is expected to be `null` at this invocation
        a = a()
        label = 1
        result = foo(a).await(this) // &amp;#39;this&amp;#39; is passed as a continuation 
        if (result == COROUTINE_SUSPENDED) return // return if await had suspended execution
      L1:
        // external code has resumed this coroutine passing the result of .await() 
        y = (Y) result
        b()
        label = 2
        result = bar(a, y).await(this) // &amp;#39;this&amp;#39; is passed as a continuation
        if (result == COROUTINE_SUSPENDED) return // return if await had suspended execution
      L2:
        // external code has resumed this coroutine passing the result of .await()
        Z z = (Z) result
        c(z)
        label = -1 // No more steps are allowed
        return
    }          
}    &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;컴파일이 된 anonymous class는 다음을 포함한다:&lt;br&gt;    •    상태 머신을 구현하는 메서드&lt;br&gt;    •    현재 상태를 저장하는 필드 (label)&lt;br&gt;    •    suspension 사이에 공유되는 지역 변수들을 저장하는 필드들&lt;br&gt;    •    (필요하면) closure 캡처용 필드들, 여기는 없음&lt;br&gt;그리고 코루틴이 시작되면 이 state-machiine anonymous class는 아래와 같이 동작을 한다.&lt;br&gt;    •    코루틴 시작 → resumeWith() 호출 → label = 0 → L0 실행&lt;br&gt;    •    L0에서 작업 수행 → label=1 → await 호출 → 만약 suspend되면 return&lt;br&gt;    •    재개(resume)되면 다시 resumeWith() 호출 → label=1 → L1 실행&lt;br&gt;    •    L1에서 작업 수행 → label=2 → await 호출 → suspend되면 return&lt;br&gt;    •    다시 재개되면 → label=2 → L2 실행 → 끝&lt;br&gt;즉, resumeWith()가 실행 재개 진입점이다.&lt;/p&gt;
&lt;p&gt;그러면 만약 loop문 안에 suspension이 있다면?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;var x = 0
while (x &amp;lt; 10) {
    x += nextNumber().await()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 코드가 있다.&lt;br&gt;컴파일 되면 &lt;code&gt;단 하나의 state만&lt;/code&gt; 생성한다. 왜냐하면  because loops also work through (conditional) goto: -&amp;gt; 이미 loop문 자체가 compile단계에서 goto문으로 이루어지기 때문이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;class &amp;lt;anonymous_for_state_machine&amp;gt; extends SuspendLambda&amp;lt;...&amp;gt; {
    // The current state of the state machine
    int label = 0

    // local variables of the coroutine
    int x

    void resumeWith(Object result) {
        if (label == 0) goto L0
        if (label == 1) goto L1
        else throw IllegalStateException()

      L0:
        x = 0
      LOOP:
        if (x &amp;gt;= 10) goto END
        label = 1
        result = nextNumber().await(this) // &amp;#39;this&amp;#39; is passed as a continuation 
        if (result == COROUTINE_SUSPENDED) return // return if await had suspended execution
      L1:
        // external code has resumed this coroutine passing the result of .await()
        x += ((Integer) result).intValue()
        label = -1
        goto LOOP
      END:
        label = -1 // No more steps are allowed
        return 
    }          
}    &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;나는 여기까지만 하면 어느정도 될 줄 알았다. 근데 아래 이런 단락이 있다.&lt;br&gt;&lt;a href=&quot;https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#compiling-suspending-functions&quot;&gt;https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#compiling-suspending-functions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/5e22c240-72a2-4781-a9db-13548aa23ca1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;The compiled code for suspending function depends on how and when it invokes other suspending functions.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;어? 좀 더 봐야겠다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;In the simplest case, a suspending function invokes other suspending functions only at tail positions making tail calls to them&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이런 내용이 있다. tail position이 무엇인가? 말 그대로 맨 마지막에 호출되는 것을 말한다.&lt;br&gt;예를 들어&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;suspend fun g() {
... (더 이상 suspend function 호출이 없음)
}

suspend fun f() {
    ...
    return g()   &amp;lt;-- 마지막 호출
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;와 처럼 있을 때 return g() 부분이 tail position이다. 이러한 경우는 그냥 일반 함수처럼 compile된다고 한다. 단, CPS패턴에 의거하여 만들어진 continuation 파라미터를 그대로 다음 suspend함수에 넘긴다.(즉, state machine이 필요가 없다.)&lt;/p&gt;
&lt;p&gt;이후 나오는 문장 중 non-tail position에 대한 내용이 나온다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;In a case when suspending invocations appear in non-tail positions, the compiler creates a state machine for the corresponding suspending function. An instance of the state machine object in created when suspending function is invoked and is discarded when it completes.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;아까 우리가 봤던 state-machine으로 생성되는 패턴이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;val a = qwer()   // qwer이 suspend function
println(a)
val b = poiu()   // poiu가 suspend function&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 경우 함수가 완료되면 state machine객체가 폐기된다(discarded로 되어있어서 직역했다)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Note: in the future versions this compilation strategy may be optimized to create an instance of a state machine only at the first suspension point&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;다행(?)스럽게도 나중에 최적화할 생각까지 가지고 계신다.&lt;/p&gt;
&lt;p&gt;아까 state machine객체를 폐기한다고 했다. 그러면 어느정도 재사용할 여지가 있어야한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt; This state machine object instance is updated and reused when the function makes multiple invocations to other suspending functions. &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;진짜 재사용한다고 한다.&lt;br&gt;자랑스럽게 나오는 내용 중 &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Compare this to other asynchronous programming styles, where each subsequent step of asynchronous processing is typically implemented with a separate, freshly allocated, closure object.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;요약 : 다른 언어는 계속 closure 객체 생성하지? 우리는 재사용해서 좋다.&lt;/p&gt;
&lt;p&gt;즉 kotlin에서는&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이는 다른 비동기 프로그래밍 스타일(예: callback-based)과 대비된다.&lt;/li&gt;
&lt;li&gt;전통 방식에서는 각 비동기 단계마다 새로운 closure 객체를 계속 생성해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;이번 글 요약&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/c927376b-d09d-4dcc-bf1b-63f3e76ab4ba/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;velog에서 똑같은 글을 본다면 제 다른 블로그가 맞습니다.&lt;/p&gt;</description>
      <category>JAVA/Kotlin</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/203</guid>
      <comments>https://developer-youn.tistory.com/203#entry203comment</comments>
      <pubDate>Tue, 2 Dec 2025 16:01:41 +0900</pubDate>
    </item>
    <item>
      <title>[kotlin deep/shallow/wide dive] deep - suspend가 정확히 뭐고 CPS는 뭐고  state machine이 뭐죠는 꼬리에 꼬리를 물고(1)</title>
      <link>https://developer-youn.tistory.com/202</link>
      <description>&lt;p&gt;ref: &lt;a href=&quot;https://kotlinlang.org/docs/coroutines-basics.html#suspending-functions&quot;&gt;https://kotlinlang.org/docs/coroutines-basics.html#suspending-functions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;이미 코루틴을 사용해봤고 약간 알고 있다고 가정하겠습니다.&lt;/p&gt;
&lt;h3&gt;다시 처음부터. suspend가 뭐라고?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/bf8b5eaf-d7f2-4f89-9f30-222b8a5ada4a/image.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;그니까 멈추게 할 수 있다! 이거다. 그니까 만약 &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;suspend fun foo(): Int {
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;와 같은 함수가 존재한다면 컴파일 되면서 요렇게 변한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;fun foo(continuation: Continuation&amp;lt;Int&amp;gt;): Any?&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;??? : 파라미터가 있으면요?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;suspend fun foo(param1: Param): Int {
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;와 같이 있었다면&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;fun foo(param1: Param, continuation:Continuation&amp;lt;Int&amp;gt;): Any?&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;처럼 변한다.&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/b43b6919-1b3e-4c83-a99a-67bba0b28806/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;근데 도대체 이게 왜 필요한가? 이 질문부터 해보자. &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;Suspending functions﻿&lt;br&gt;The most basic building block of coroutines is the suspending function. It allows a running operation to pause and resume later without affecting the structure of your code.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;정확히는 stop이 아니라 pause이다. 즉 일시정지를 한다는거고 다시 시작한다는거다.&lt;br&gt;그리고 Thread에 대한 pause가 아니라 execution state에 대한 pause이다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;suspend는 thread에 대한 block이 아님&lt;/li&gt;
&lt;li&gt;coroutine의 delay를 하게 되면 Thread의 sleep과는 다르게 Thread block없이 suspend/resume이 발생함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;어? 그렇다면 이 상태를 어디선가 저장을 하고 있어야한다. 즉, state를 관리하는 machine이 필요하다는거다. &lt;/p&gt;
&lt;p&gt;그니까 Thread에 대한 정지가 아니라 함수의 실행을 일시정지하고 thread는 그대로 진행됨.다시 재개했을때 동일 Thread가 아닐 수 있고 Thread의 stack frame이 유지되지 않고 Thread stack 또한 달라질 수 있음&lt;/p&gt;
&lt;p&gt;그러면 이제 슬슬 뭔가 궁금해지는 것 중 하나로&lt;/p&gt;
&lt;h3&gt;왜 어째서 코루틴의 suspending은 concept에서 왜 thread stop으로 디자인되지 않았는가?&lt;/h3&gt;
&lt;p&gt;몇몇 자료를 좀 찾아보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;coroutines can suspend their execution instead of blocking a thread.”&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/9fc228ec-85d4-4145-82fd-286228b5bc1f/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt; Coroutines are lightweight alternatives to threads. They can suspend without blocking system resources and are resource-friendly, making them better suited for fine-grained concurrency.&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/b67c5d3f-b444-4521-9edb-7e1108cbe1ab/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;일단 스레드를 블로킹하지 않고 동시성 구현을 한다고 되어 있고 thread를 대신하기 위한 아주 가벼운 것으로 설명되고 있다. 리소스도 적게 먹고.&lt;br&gt;여기서 나온 문장들을 근거로 유추를 해보면 하드웨어 성능 상으로 thread가 돌아가기에 정말 이 thread에 상관 없는 다른 대체제를 생각하고 만들어진게 coroutine이 아닌가 싶다. 그러면 이제 state machine에서 이게 어떻게 관리되고 있는지를 봐야한다. &lt;/p&gt;
&lt;p&gt;검색을 하던 중 이런 사이트를 보게 되었다.&lt;br&gt;&lt;a href=&quot;https://kotlinlang.org/spec/asynchronous-programming-with-coroutines.html&quot;&gt;https://kotlinlang.org/spec/asynchronous-programming-with-coroutines.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;여기서 좀 더 하위 칼럼 중 하나인 &lt;a href=&quot;https://kotlinlang.org/spec/asynchronous-programming-with-coroutines.html#implementation-details&quot;&gt;https://kotlinlang.org/spec/asynchronous-programming-with-coroutines.html#implementation-details&lt;/a&gt; 을 보면&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/7b401174-4ed0-4f20-b906-5324cf716fd1/image.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Continuation Passing Style이라는게 나온다. 이게 뭔지를 좀 봐야겠다.&lt;br&gt;다행스럽게도 친절하게 클릭하면 설명이 나오게 되어있다.&lt;br&gt;&lt;a href=&quot;https://kotlinlang.org/spec/asynchronous-programming-with-coroutines.html#continuation-passing-style&quot;&gt;https://kotlinlang.org/spec/asynchronous-programming-with-coroutines.html#continuation-passing-style&lt;/a&gt;&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/f46a48bb-d740-4ecd-8c61-83e0aae267a7/image.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;그니까.. 각 suspend 함수는 CPS형태로 변환된다. 그니까 Continuation을 넘긴다는 건데 정확히 Continuation에 어떤 정보가 있는지를 봐야한다.&lt;br&gt;&lt;img src=&quot;https://velog.velcdn.com/images/bernoyoun/post/5597828a-091b-418b-93d1-df71565587aa/image.png&quot; alt=&quot;&quot;&gt;&lt;br&gt;상당히 심플하게 적혀있지만 결국 어디까지 진행했는지, 어디서 중단했는지에 대한 포인트 지점을 찍어둔거다. 아까 컴파일 된 결과에서 continuation 파라미터를 이용해 그 정보들을 계속해서 passing(전달)한다. 그래서 suspend 함수들은 원할때 pause/resume이 가능한거다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;2편에서 이어서...&lt;/p&gt;
&lt;p&gt;velog에서 똑같은 글을 본다면 제 다른 블로그가 맞습니다.&lt;/p&gt;</description>
      <category>JAVA/Kotlin</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/202</guid>
      <comments>https://developer-youn.tistory.com/202#entry202comment</comments>
      <pubDate>Wed, 26 Nov 2025 19:42:20 +0900</pubDate>
    </item>
    <item>
      <title>[WIP] [kotlin deep/shallow/wide dive] wide - lateinit 과 lazy</title>
      <link>https://developer-youn.tistory.com/201</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/kotlin-deepshallowwide-dive-wide-lateinit-%EA%B3%BC-lazy&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bernoyoun/kotlin-deepshallowwide-dive-wide-lateinit-%EA%B3%BC-lazy&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764056824102&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[kotlin deep/shallow/wide dive] wide  - lateinit 과 lazy&quot; data-og-description=&quot;kotlin을 사용하다 보면 나중에 초기화를 하기 위해 lateinit 과 lazy를 사용하곤 하는데 그래서 이게 뭐가 다르다고?일단 공식문서에서 다시 한번 lateinit과 lazy by에 대해서 슥 읽어보자.https://kotlinlang&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@bernoyoun/kotlin-deepshallowwide-dive-wide-lateinit-%EA%B3%BC-lazy&quot; data-og-url=&quot;https://velog.io/@bernoyoun/kotlin-deepshallowwide-dive-wide-lateinit-과-lazy&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FZquH/hyZN3J8emN/j3szIrKho6BGKTiz3OS2x0/img.png?width=411&amp;amp;height=307&amp;amp;face=0_0_411_307,https://scrap.kakaocdn.net/dn/bCBXIL/hyZOd62BvL/DknuzpRszuqGjXm7Wbwu8K/img.png?width=411&amp;amp;height=307&amp;amp;face=0_0_411_307,https://scrap.kakaocdn.net/dn/bvoAFW/hyZNI6HqZc/zZHOudOmjeAcnmhm5xTHD0/img.png?width=1576&amp;amp;height=832&amp;amp;face=0_0_1576_832&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/kotlin-deepshallowwide-dive-wide-lateinit-%EA%B3%BC-lazy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@bernoyoun/kotlin-deepshallowwide-dive-wide-lateinit-%EA%B3%BC-lazy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FZquH/hyZN3J8emN/j3szIrKho6BGKTiz3OS2x0/img.png?width=411&amp;amp;height=307&amp;amp;face=0_0_411_307,https://scrap.kakaocdn.net/dn/bCBXIL/hyZOd62BvL/DknuzpRszuqGjXm7Wbwu8K/img.png?width=411&amp;amp;height=307&amp;amp;face=0_0_411_307,https://scrap.kakaocdn.net/dn/bvoAFW/hyZNI6HqZc/zZHOudOmjeAcnmhm5xTHD0/img.png?width=1576&amp;amp;height=832&amp;amp;face=0_0_1576_832');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[kotlin deep/shallow/wide dive] wide - lateinit 과 lazy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;kotlin을 사용하다 보면 나중에 초기화를 하기 위해 lateinit 과 lazy를 사용하곤 하는데 그래서 이게 뭐가 다르다고?일단 공식문서에서 다시 한번 lateinit과 lazy by에 대해서 슥 읽어보자.https://kotlinlang&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JAVA/Kotlin</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/201</guid>
      <comments>https://developer-youn.tistory.com/201#entry201comment</comments>
      <pubDate>Tue, 25 Nov 2025 16:47:06 +0900</pubDate>
    </item>
    <item>
      <title>(WIP)바이브 코딩 프로세스 확립기</title>
      <link>https://developer-youn.tistory.com/200</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;(가볍게 적은 글)&lt;br /&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/%EB%82%B4%EA%B0%80-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%94%EC%9D%B4%EB%B8%8C-%EC%BD%94%EB%94%A9-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bernoyoun/%EB%82%B4%EA%B0%80-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%94%EC%9D%B4%EB%B8%8C-%EC%BD%94%EB%94%A9-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763099895612&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;내가 사용하는 바이브 코딩 프로세스&quot; data-og-description=&quot;나는 초기에 바이브 코딩을 하면서 부터 문제가 최대한 덜 생기게 개발을 하는 방법을 고민해보았다.그냥 무턱대고 마! 개발해줘! 라고 하기엔 사이즈가 커지면서 온갖 문제가 발생할 것 같았다&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@bernoyoun/%EB%82%B4%EA%B0%80-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%94%EC%9D%B4%EB%B8%8C-%EC%BD%94%EB%94%A9-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4&quot; data-og-url=&quot;https://velog.io/@bernoyoun/내가-사용하는-바이브-코딩-프로세스&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bAOWv0/hyZNSmEtBp/UilBu7klR4ChQx9fptM7Q0/img.png?width=246&amp;amp;height=205&amp;amp;face=0_0_246_205,https://scrap.kakaocdn.net/dn/cxfKQm/hyZNt9TpGE/jQ4nEIgkq30o3f47zE1wC1/img.png?width=246&amp;amp;height=205&amp;amp;face=0_0_246_205,https://scrap.kakaocdn.net/dn/QAqxX/hyZMxEtZMx/91TZ5f8CKIoK4RLSOJPoQ0/img.png?width=640&amp;amp;height=621&amp;amp;face=0_0_640_621&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/%EB%82%B4%EA%B0%80-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%94%EC%9D%B4%EB%B8%8C-%EC%BD%94%EB%94%A9-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@bernoyoun/%EB%82%B4%EA%B0%80-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%94%EC%9D%B4%EB%B8%8C-%EC%BD%94%EB%94%A9-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bAOWv0/hyZNSmEtBp/UilBu7klR4ChQx9fptM7Q0/img.png?width=246&amp;amp;height=205&amp;amp;face=0_0_246_205,https://scrap.kakaocdn.net/dn/cxfKQm/hyZNt9TpGE/jQ4nEIgkq30o3f47zE1wC1/img.png?width=246&amp;amp;height=205&amp;amp;face=0_0_246_205,https://scrap.kakaocdn.net/dn/QAqxX/hyZMxEtZMx/91TZ5f8CKIoK4RLSOJPoQ0/img.png?width=640&amp;amp;height=621&amp;amp;face=0_0_640_621');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;내가 사용하는 바이브 코딩 프로세스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;나는 초기에 바이브 코딩을 하면서 부터 문제가 최대한 덜 생기게 개발을 하는 방법을 고민해보았다.그냥 무턱대고 마! 개발해줘! 라고 하기엔 사이즈가 커지면서 온갖 문제가 발생할 것 같았다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>바이브코딩일지</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/200</guid>
      <comments>https://developer-youn.tistory.com/200#entry200comment</comments>
      <pubDate>Fri, 14 Nov 2025 14:58:20 +0900</pubDate>
    </item>
    <item>
      <title>Infra 비용 절감을 행한 기록</title>
      <link>https://developer-youn.tistory.com/199</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;가볍게 읽고 싶으신 분은 아래 글로&lt;/h2&gt;
&lt;figure id=&quot;og_1761818306872&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;구두쇠식 서비스 인프라 운영법&quot; data-og-description=&quot;&amp;quot;월 인프라 비용이 $45... 좀 더 절약한다. 쥐어짜면 뭐든 나온다.개인 서비스를 운영중이다. 혼자 개발하고 있다. 월 인프라 비용이 $45-50이 나온다. 줄일거다. 이거 줄일 수 있다.Cloud Run (백엔드): &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@bernoyoun/%EA%B5%AC%EB%91%90%EC%87%A0%EC%8B%9D-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9D%B8%ED%94%84%EB%9D%BC-%EC%9A%B4%EC%98%81%EB%B2%95&quot; data-og-url=&quot;https://velog.io/@bernoyoun/구두쇠식-서비스-인프라-운영법&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cPfrLR/hyZMnPsCZj/7IRTFmccjsfEhrvZkoC7WK/img.png?width=675&amp;amp;height=644&amp;amp;face=0_0_675_644,https://scrap.kakaocdn.net/dn/wtxDA/hyZMTzeI3y/HbfoPt7TAeXzJIbDwRY7rK/img.png?width=675&amp;amp;height=644&amp;amp;face=0_0_675_644,https://scrap.kakaocdn.net/dn/fKW68/hyZMim6upx/FV0bVveC05lTJH4xcDb6IK/img.png?width=675&amp;amp;height=644&amp;amp;face=0_0_675_644&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/%EA%B5%AC%EB%91%90%EC%87%A0%EC%8B%9D-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9D%B8%ED%94%84%EB%9D%BC-%EC%9A%B4%EC%98%81%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@bernoyoun/%EA%B5%AC%EB%91%90%EC%87%A0%EC%8B%9D-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9D%B8%ED%94%84%EB%9D%BC-%EC%9A%B4%EC%98%81%EB%B2%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cPfrLR/hyZMnPsCZj/7IRTFmccjsfEhrvZkoC7WK/img.png?width=675&amp;amp;height=644&amp;amp;face=0_0_675_644,https://scrap.kakaocdn.net/dn/wtxDA/hyZMTzeI3y/HbfoPt7TAeXzJIbDwRY7rK/img.png?width=675&amp;amp;height=644&amp;amp;face=0_0_675_644,https://scrap.kakaocdn.net/dn/fKW68/hyZMim6upx/FV0bVveC05lTJH4xcDb6IK/img.png?width=675&amp;amp;height=644&amp;amp;face=0_0_675_644');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;구두쇠식 서비스 인프라 운영법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&quot;월 인프라 비용이 $45... 좀 더 절약한다. 쥐어짜면 뭐든 나온다.개인 서비스를 운영중이다. 혼자 개발하고 있다. 월 인프라 비용이 $45-50이 나온다. 줄일거다. 이거 줄일 수 있다.Cloud Run (백엔드):&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;(이 글은 Claude와 합작으로 작성된 글임을 밝힙니다.)&lt;br /&gt;개인 프로젝트에서 월 $45-50의 GCP 인프라 비용을 $15-20으로 70-85% 절감한 실제 최적화 사례를 다룹니다. 조건부 빌드, 의존성 기반 캐싱, 아키텍처 단순화, AI API 최적화를 통해 비용 절감과 동시에 성능 향상을 달성한 엔지니어링 과정을 상세히 분석합니다.&lt;br /&gt;&lt;br /&gt;주의사항 : 약간의 비용적 과장, 타임라인 상 코드 상으로 예전에 해뒀는데 여기에 같이 버무리고 같이 한 것 처럼 진행한 부분 있음 주의.&lt;br /&gt;&lt;br /&gt;약간 쉴 틈을 주기 위해 중간 중간 짤이 들어갈 수 있음을 알립니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프로젝트 컨텍스트 및 문제 정리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 비용 구조 분석&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Initial Cost Breakdown (월 $45-50)&lt;/h4&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Cloud Run (Backend)     : $5-10 (44-50%)
├─ Memory: 2Gi, CPU: 1
├─ Min instances: 1 (Cold start 방지)
└─ Max instances: 10

Cloud Build (CI/CD)     : $20-25 (33-40%)
├─ 빌드 시간: 30분/회
├─ 빌드 빈도: 15-20회/일
└─ 무료 한도 초과: 120분/일

AI API (Gemini)         : $15-20 (33-40%)
├─ 토큰 사용: 비효율적 구조
├─ 캐싱: 미적용
└─ 프롬프트 최적화: 미적용&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 성능 병목점 식별&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;빌드 시간&lt;/b&gt;: 18분 (프론트엔드 변경 시에도 백엔드 빌드)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;캐시 히트율&lt;/b&gt;: 30% (SHA 기반 캐시의 한계)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리소스 중복&lt;/b&gt;: API 변경 없는 상황에서도 전체 파이프라인 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 최적화 전략 설계&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 비용 최적화 우선순위 매트릭스&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;최적화 영역&lt;/th&gt;
&lt;th&gt;구현 복잡도&lt;/th&gt;
&lt;th&gt;예상 절감률&lt;/th&gt;
&lt;th&gt;ROI 점수&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;조건부 빌드&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;60-80%&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;의존성 캐싱&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;90%+&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis 제거&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI 최적화&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;75%&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF 이전&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 무료 티어 활용 전략&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Google Cloud 무료 한도 분석&lt;/h4&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Cloud Run:
  requests: 2,000,000/month  # 충분한 여유
  cpu_time: 400,000 vCPU-seconds/month
  memory: 800,000 GiB-seconds/month

Cloud Build:
  build_time: 2500 minutes/month  # 현재 초과 중
  storage: 10GB

Secret Manager:
  secret_versions: 6  # 현재 사용량: 7개 (1개 초과)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 구현 상세: 기술적 솔루션&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 조건부 빌드 시스템 구현&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1.1 turbo-ignore 기반 변경 감지&lt;/h4&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;# cloudbuild.yaml
steps:
  - name: 'gcr.io/cloud-builders/npm'
    id: 'check-changes'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        echo &quot;  Checking if API changes require rebuild...&quot;
        npm install -g pnpm@8
        pnpm add -g turbo@latest

        # API 앱 변경 감지 (turbo-ignore: 변경 없을 때 exit 1)
        if npx turbo-ignore apps/api; then
          echo &quot;⏭️ No API changes detected, skipping build&quot;
          echo &quot;false&quot; &amp;gt; /workspace/should_build
          exit 0
        else
          echo &quot;✅ API changes detected, proceeding with build&quot;
          echo &quot;true&quot; &amp;gt; /workspace/should_build
        fi
    timeout: '300s'&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1.2 조건부 실행 로직&lt;/h4&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;  - name: 'gcr.io/cloud-builders/npm'
    id: 'prepare-build'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        # 빌드 필요성 체크
        if [ ! -f /workspace/should_build ] || [ &quot;$(cat /workspace/should_build)&quot; != &quot;true&quot; ]; then
          echo &quot;⏭️ Skipping build - no API changes detected&quot;
          exit 0
        fi

        echo &quot;  Installing dependencies...&quot;
        # 실제 빌드 로직 실행&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 의존성 기반 캐싱 시스템&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2.1 해시 기반 캐시 키 생성&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;# 기존: SHA 기반 (소스 변경마다 무효화)
CACHE_KEY=&quot;v1-${SHORT_SHA:0:8}&quot;

# 개선: 의존성 기반 (안정적)
DEPS_HASH=$(cat package.json pnpm-lock.yaml | sha256sum | cut -d' ' -f1 | cut -c1-8)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2.2 다층 캐시 복원 전략&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# 의존성 기반 캐시 복원 (우선순위별)
- name: 'gcr.io/cloud-builders/gsutil'
  entrypoint: 'bash'
  args:
    - '-c'
    - |
      # 1순위: 정확한 의존성 해시 매치
      gsutil -m cp gs://cache-bucket/pnpm-store-deps-$$DEPS_HASH.tar.gz . 2&amp;gt;/dev/null || \
      # 2순위: 최신 의존성 캐시 (폴백)
      gsutil -m cp gs://cache-bucket/pnpm-store-deps-*.tar.gz . 2&amp;gt;/dev/null || \
      echo &quot;No dependency cache found&quot;

      # 캐시 유효성 검증
      if [ -f &quot;node_modules/.pnpm-lock.yaml&quot; ]; then
        if diff -q pnpm-lock.yaml node_modules/.pnpm-lock.yaml &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
          echo &quot;✅ Cache dependencies match current lockfile&quot;
        else
          echo &quot;⚠️ Cache dependencies differ - will reinstall&quot;
          rm -rf node_modules .pnpm-store
        fi
      fi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성과&lt;/b&gt;: 캐시 히트율 30% &amp;rarr; 90%+ 향상&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 아키텍처 단순화: Redis 제거&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3.1 PostgreSQL 큐 시스템 마이그레이션&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// AS-IS: Redis SSE
class RedisProgressService {
  async publishProgress(userId: string, progress: ProgressData) {
    await this.redis.publish(`progress:${userId}`, JSON.stringify(progress));
  }

  async subscribeProgress(userId: string): Promise&amp;lt;EventEmitter&amp;gt; {
    return this.redis.subscribe(`progress:${userId}`);
  }
}

// TO-BE: PostgreSQL 큐 (pg-boss)
import PgBoss from 'pg-boss';

class PostgresProgressService {
  private boss: PgBoss;

  constructor() {
    this.boss = new PgBoss({
      connectionString: process.env.DATABASE_URL,
      retryLimit: 3,
      retryDelay: 30,
      expireInSeconds: 300, // 5분 후 만료
    });
  }

  async publishProgress(userId: string, progress: ProgressData) {
    await this.boss.send('progress-update', {
      userId,
      documentId: progress.documentId,
      step: progress.step,
      progress: progress.percentage,
      timestamp: new Date().toISOString()
    });
  }

  async startProgressWorker() {
    await this.boss.work('progress-update', async (job) =&amp;gt; {
      const { userId, ...progressData } = job.data;

      // WebSocket 또는 폴링 엔드포인트로 전달
      await this.notificationService.notify(userId, progressData);
    });
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3.2 적응형 폴링 클라이언트&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;// 클라이언트 측 적응형 폴링
class AdaptivePollingClient {
  private pollInterval = 1000; // 초기 1초
  private maxInterval = 5000;   // 최대 5초
  private minInterval = 500;    // 최소 0.5초

  async startPolling(documentId: string) {
    while (!this.isComplete) {
      try {
        const progress = await this.fetchProgress(documentId);

        if (progress.changed) {
          // 변화 있으면 폴링 간격 단축
          this.pollInterval = Math.max(this.minInterval, this.pollInterval * 0.8);
        } else {
          // 변화 없으면 폴링 간격 연장
          this.pollInterval = Math.min(this.maxInterval, this.pollInterval * 1.2);
        }

        await this.sleep(this.pollInterval);
      } catch (error) {
        // 에러 시 폴링 간격 연장
        this.pollInterval = Math.min(this.maxInterval, this.pollInterval * 2);
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;트레이드오프 분석&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;❌ 완벽한 실시간성 손실 (500ms ~ 5초 지연)&lt;/li&gt;
&lt;li&gt;✅ 인프라 비용 완전 제거 ($0)&lt;/li&gt;
&lt;li&gt;✅ 모바일 브라우저 호환성 60% 개선&lt;/li&gt;
&lt;li&gt;✅ 시스템 복잡도 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.4 AI API 비용 최적화&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4.1 Gemini Implicit Caching 활용&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface PromptOptimization {
  // ❌ 캐시 비효율적 구조 (변수가 앞에)
  inefficient: string;
  // ✅ 캐시 효율적 구조 (공통 부분이 앞에)
  optimized: string;
}

class GeminiPromptOptimizer {
  // 캐시 친화적 프롬프트 구조 설계
  buildOptimizedPrompt(userInput: string): string {
    const staticContext = this.getLegalContext();      // 95% 재사용
    const documentTemplate = this.getDocumentTemplate(); // 80% 재사용  
    const a4Guidelines = this.getA4Guidelines();        // 100% 재사용

    // 공통 부분을 앞에, 변수 부분을 뒤에 배치
    return `
${staticContext}

${documentTemplate}

${a4Guidelines}

---
사용자 요청사항:
${userInput}
    `.trim();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4.2 토큰 사용량 분석 및 최적화&lt;/h4&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;// 최적화 전후 토큰 사용량 비교
const tokenAnalysis = {
  before: {
    avgTokensPerRequest: 2750,
    cacheHitRate: 0.05,
    monthlyCost: 68.75, // 5000 requests/month
  },
  after: {
    avgTokensPerRequest: 2750, // 동일한 품질 유지
    cacheHitRate: 0.75,        // 75% 캐시 히트
    monthlyCost: 17.19,        // 75% 절감
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.5 PDF 생성 엔진 진화&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.5.1 3단계 PDF 엔진 비교 분석&lt;/h4&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;엔진&lt;/th&gt;
&lt;th&gt;위치&lt;/th&gt;
&lt;th&gt;메모리&lt;/th&gt;
&lt;th&gt;처리시간&lt;/th&gt;
&lt;th&gt;장점&lt;/th&gt;
&lt;th&gt;단점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;wkhtmltopdf&lt;/td&gt;
&lt;td&gt;서버&lt;/td&gt;
&lt;td&gt;20-30MB&lt;/td&gt;
&lt;td&gt;0.1s&lt;/td&gt;
&lt;td&gt;빠른 처리&lt;/td&gt;
&lt;td&gt;한국어 폰트 이슈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Puppeteer&lt;/td&gt;
&lt;td&gt;서버&lt;/td&gt;
&lt;td&gt;250-500MB&lt;/td&gt;
&lt;td&gt;3-5.5s&lt;/td&gt;
&lt;td&gt;완벽 렌더링&lt;/td&gt;
&lt;td&gt;높은 서버 부하&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;react-to-print&lt;/td&gt;
&lt;td&gt;클라이언트&lt;/td&gt;
&lt;td&gt;0MB&lt;/td&gt;
&lt;td&gt;즉시&lt;/td&gt;
&lt;td&gt;서버 부하 0&lt;/td&gt;
&lt;td&gt;브라우저 의존성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.5.2 react-to-print 구현&lt;/h4&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// PDF 생성 클라이언트 이전
import { useReactToPrint } from 'react-to-print';

interface A4DocumentProps {
  content: DocumentContent;
  template: DocumentTemplate;
}

const A4Document: React.FC&amp;lt;A4DocumentProps&amp;gt; = ({ content, template }) =&amp;gt; {
  const componentRef = useRef&amp;lt;HTMLDivElement&amp;gt;(null);

  const handlePrint = useReactToPrint({
    content: () =&amp;gt; componentRef.current,
    pageStyle: `
      @page {
        size: A4;
        margin: 20mm;
      }
      @media print {
        html, body {
          height: initial !important;
          overflow: initial !important;
        }
      }
    `,
    onBeforePrint: () =&amp;gt; {
      return Promise.resolve();
    },
    onAfterPrint: () =&amp;gt; {
      console.log('PDF generation completed');
    }
  });

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={handlePrint}&amp;gt;PDF 다운로드&amp;lt;/button&amp;gt;
      &amp;lt;div ref={componentRef} className=&quot;a4-document&quot;&amp;gt;
        &amp;lt;DocumentRenderer content={content} template={template} /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성과&lt;/b&gt;: 서버 CPU/메모리 부하 완전 제거, CSS 스타일 100% 보존&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 성능 측정 및 벤치마킹&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 비용 분석 결과&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Before/After 상세 비교&lt;/h4&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;┌─────────────────┬────────────┬────────────┬───────────┐
│ Cost Category   │ Before ($) │ After ($)  │ Savings   │
├─────────────────┼────────────┼────────────┼───────────┤
│ Cloud Run       │ 20-25      │ 8-10       │ 60%       │
│ Cloud Build     │ 15-20      │ 3-5        │ 75-80%    │
│ AI API (Gemini) │ 15-20      │ 4-5        │ 75%       │
│ Storage         │ 2-3        │ 2-3        │ 0%        │
│ Networking      │ 1-2        │ 1-2        │ 0%        │
├─────────────────┼────────────┼────────────┼───────────┤
│ Total Monthly   │ 45-50      │ 15-20      │ 70-85%    │
└─────────────────┴────────────┴────────────┴───────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 아키텍처 영향 분석&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 시스템 복잡도 변화&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Before: Frontend &amp;rarr; Backend &amp;rarr; Redis &amp;rarr; Database &amp;rarr; AI API &amp;rarr; PDF Engine
After:  Frontend &amp;rarr; Backend &amp;rarr; Database &amp;rarr; AI API
                      &amp;darr;
               PostgreSQL Queue &amp;rarr; PDF (Client)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.3 개발자 경험(DX) 향상&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;로컬 개발&lt;/b&gt;: Redis 의존성 제거로 설정 단순화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배포&lt;/b&gt;: 실패 지점 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 장기적 확장성 고려사항&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.1 트래픽 증가 시나리오&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;// 예상 트래픽별 비용 추정
interface CostProjection {
  users: number;
  requests: number;
  estimatedCost: number;
}

const projections: CostProjection[] = [
  { users: 100,   requests: 10000,   estimatedCost: 15 },   // 현재
  { users: 1000,  requests: 100000,  estimatedCost: 45 },   // 10배 증가
  { users: 10000, requests: 1000000, estimatedCost: 150 },  // 100배 증가
];&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 결론 및 권장사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;비용 절감&lt;/b&gt;: 70-85% (월 $45-50 &amp;rarr; $15-20)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;성능 향상&lt;/b&gt;: 빌드 시간 50-67% 단축&lt;/li&gt;
&lt;li&gt;&lt;b&gt;권장사항&lt;/b&gt;: 일단 모르겠다면 캐싱을 먼저 도입하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/private/a4lab&quot;&gt;A4Lab.ai Git Repository&lt;/a&gt; (Private)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/products/calculator&quot;&gt;Google Cloud Pricing Calculator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://turbo.build/repo/docs&quot;&gt;Turborepo Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/timgit/pg-boss&quot;&gt;pg-boss Queue System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/gregnb/react-to-print&quot;&gt;react-to-print Library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>바이브코딩일지</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/199</guid>
      <comments>https://developer-youn.tistory.com/199#entry199comment</comments>
      <pubDate>Thu, 30 Oct 2025 19:26:31 +0900</pubDate>
    </item>
    <item>
      <title>조만간 정리할 글</title>
      <link>https://developer-youn.tistory.com/198</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;이 내용으로 좀 더 디테일하게 올라올 예정&quot; href=&quot;https://velog.io/@bernoyoun/%EC%9D%B4%EC%A0%9C-Langchain%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%A0-%EC%8B%9C%EC%A0%90%EC%9D%B4-%EC%99%80%EB%B2%84%EB%A6%B0-%EB%B0%80%EB%A6%BC%EC%9D%98-%EC%99%95%EC%9D%80-%EC%82%AC%EC%9E%90%EC%9E%85%EB%8B%88%EB%8B%A4.-%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%9D%BC%EB%8B%A8-ollama%EC%97%90%EC%84%9C-LLM-%EB%AA%A8%EB%8D%B8-%EB%A8%BC%EC%A0%80-%EC%84%A4%EC%B9%98%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bernoyoun/%EC%9D%B4%EC%A0%9C-Langchain%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%A0-%EC%8B%9C%EC%A0%90%EC%9D%B4-%EC%99%80%EB%B2%84%EB%A6%B0-%EB%B0%80%EB%A6%BC%EC%9D%98-%EC%99%95%EC%9D%80-%EC%82%AC%EC%9E%90%EC%9E%85%EB%8B%88%EB%8B%A4.-%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%9D%BC%EB%8B%A8-ollama%EC%97%90%EC%84%9C-LLM-%EB%AA%A8%EB%8D%B8-%EB%A8%BC%EC%A0%80-%EC%84%A4%EC%B9%98%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1757040768188&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;이제 Langchain을 공부할 시점이 와버린 밀림의 왕은 사자입니다. 그래서 일단 ollama에서 LLM 모델 먼&quot; data-og-description=&quot;맥북에서 이미 여러 잡일을 돌리고 있던 터라 최대한 리소스가 적게 드는 놈으로 찾던 중deepseek-r1:1.5b라는 놈을 발견했다.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@bernoyoun/%EC%9D%B4%EC%A0%9C-Langchain%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%A0-%EC%8B%9C%EC%A0%90%EC%9D%B4-%EC%99%80%EB%B2%84%EB%A6%B0-%EB%B0%80%EB%A6%BC%EC%9D%98-%EC%99%95%EC%9D%80-%EC%82%AC%EC%9E%90%EC%9E%85%EB%8B%88%EB%8B%A4.-%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%9D%BC%EB%8B%A8-ollama%EC%97%90%EC%84%9C-LLM-%EB%AA%A8%EB%8D%B8-%EB%A8%BC%EC%A0%80-%EC%84%A4%EC%B9%98%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; data-og-url=&quot;https://velog.io/@bernoyoun/이제-Langchain을-공부할-시점이-와버린-밀림의-왕은-사자입니다.-그래서-일단-ollama에서-LLM-모델-먼저-설치했습니다&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biSjMU/hyZGmpcZB0/WYik61l98cRPdie5HYbeok/img.png?width=1668&amp;amp;height=1230&amp;amp;face=0_0_1668_1230,https://scrap.kakaocdn.net/dn/lfY1c/hyZIZFTIek/0XgG3pjBMDacA6WNN8eUvK/img.png?width=1668&amp;amp;height=1230&amp;amp;face=0_0_1668_1230,https://scrap.kakaocdn.net/dn/m5q9A/hyZGdeIbvU/drtVPGbCfvYKr3xN8xyu8k/img.png?width=3442&amp;amp;height=754&amp;amp;face=0_0_3442_754&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/%EC%9D%B4%EC%A0%9C-Langchain%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%A0-%EC%8B%9C%EC%A0%90%EC%9D%B4-%EC%99%80%EB%B2%84%EB%A6%B0-%EB%B0%80%EB%A6%BC%EC%9D%98-%EC%99%95%EC%9D%80-%EC%82%AC%EC%9E%90%EC%9E%85%EB%8B%88%EB%8B%A4.-%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%9D%BC%EB%8B%A8-ollama%EC%97%90%EC%84%9C-LLM-%EB%AA%A8%EB%8D%B8-%EB%A8%BC%EC%A0%80-%EC%84%A4%EC%B9%98%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@bernoyoun/%EC%9D%B4%EC%A0%9C-Langchain%EC%9D%84-%EA%B3%B5%EB%B6%80%ED%95%A0-%EC%8B%9C%EC%A0%90%EC%9D%B4-%EC%99%80%EB%B2%84%EB%A6%B0-%EB%B0%80%EB%A6%BC%EC%9D%98-%EC%99%95%EC%9D%80-%EC%82%AC%EC%9E%90%EC%9E%85%EB%8B%88%EB%8B%A4.-%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%9D%BC%EB%8B%A8-ollama%EC%97%90%EC%84%9C-LLM-%EB%AA%A8%EB%8D%B8-%EB%A8%BC%EC%A0%80-%EC%84%A4%EC%B9%98%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biSjMU/hyZGmpcZB0/WYik61l98cRPdie5HYbeok/img.png?width=1668&amp;amp;height=1230&amp;amp;face=0_0_1668_1230,https://scrap.kakaocdn.net/dn/lfY1c/hyZIZFTIek/0XgG3pjBMDacA6WNN8eUvK/img.png?width=1668&amp;amp;height=1230&amp;amp;face=0_0_1668_1230,https://scrap.kakaocdn.net/dn/m5q9A/hyZGdeIbvU/drtVPGbCfvYKr3xN8xyu8k/img.png?width=3442&amp;amp;height=754&amp;amp;face=0_0_3442_754');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;이제 Langchain을 공부할 시점이 와버린 밀림의 왕은 사자입니다. 그래서 일단 ollama에서 LLM 모델 먼&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;맥북에서 이미 여러 잡일을 돌리고 있던 터라 최대한 리소스가 적게 드는 놈으로 찾던 중deepseek-r1:1.5b라는 놈을 발견했다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>바이브코딩일지</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/198</guid>
      <comments>https://developer-youn.tistory.com/198#entry198comment</comments>
      <pubDate>Fri, 5 Sep 2025 11:53:31 +0900</pubDate>
    </item>
    <item>
      <title>SSE vs Long Polling vs WebSocket</title>
      <link>https://developer-youn.tistory.com/197</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/SSE-Long-Polling-WebSocket&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bernoyoun/SSE-Long-Polling-WebSocket&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756547843742&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;SSE? Long Polling? WebSocket?&quot; data-og-description=&quot;오늘도 딸깍딸깍하며 바이브 코딩을 즐기던 중, SSE로 개발되었던 기능에서 문제가 터졌다.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@bernoyoun/SSE-Long-Polling-WebSocket&quot; data-og-url=&quot;https://velog.io/@bernoyoun/SSE-Long-Polling-WebSocket&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hP9nr/hyZGjY2xJv/0I6F26aGf3cb7ubCD0QZlk/img.png?width=1032&amp;amp;height=544&amp;amp;face=0_0_1032_544,https://scrap.kakaocdn.net/dn/bE9l9u/hyZGWvC528/0YRannZKWm4k8J9ZkoSuk1/img.png?width=1032&amp;amp;height=544&amp;amp;face=0_0_1032_544,https://scrap.kakaocdn.net/dn/c998EG/hyZC7eSojU/OKJMUQrq7k9laKHwAicat1/img.png?width=1032&amp;amp;height=544&amp;amp;face=0_0_1032_544&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/SSE-Long-Polling-WebSocket&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@bernoyoun/SSE-Long-Polling-WebSocket&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hP9nr/hyZGjY2xJv/0I6F26aGf3cb7ubCD0QZlk/img.png?width=1032&amp;amp;height=544&amp;amp;face=0_0_1032_544,https://scrap.kakaocdn.net/dn/bE9l9u/hyZGWvC528/0YRannZKWm4k8J9ZkoSuk1/img.png?width=1032&amp;amp;height=544&amp;amp;face=0_0_1032_544,https://scrap.kakaocdn.net/dn/c998EG/hyZC7eSojU/OKJMUQrq7k9laKHwAicat1/img.png?width=1032&amp;amp;height=544&amp;amp;face=0_0_1032_544');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SSE? Long Polling? WebSocket?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;오늘도 딸깍딸깍하며 바이브 코딩을 즐기던 중, SSE로 개발되었던 기능에서 문제가 터졌다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 왠지 올리긴 하지만 velog가 더 적절해보이긴 한다..&lt;/p&gt;</description>
      <category>바이브코딩일지</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/197</guid>
      <comments>https://developer-youn.tistory.com/197#entry197comment</comments>
      <pubDate>Sat, 30 Aug 2025 18:57:43 +0900</pubDate>
    </item>
    <item>
      <title>CLAUDE-CODE의 토큰을 절약하기 - tasks.md의 문서 구조 개편</title>
      <link>https://developer-youn.tistory.com/196</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 제 경험기를 GPT와 함께 작성한 글임을 밝힙니다.&lt;/p&gt;
&lt;h1&gt;Case Study&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Token Limit로 막힌 2,000+라인 태스크 파일을 구조화해 76.1% 토큰 절감한 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Executive Summary&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;문제&lt;/b&gt;: 단일 &lt;code&gt;tasks.md&lt;/code&gt;(2,247라인)를 모델에 로딩할 때 &lt;b&gt;token limit exceed&lt;/b&gt;로 AI 워크플로우 불능 상태 발생.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해결&lt;/b&gt;: 마스터 인덱스(180라인) + 6개 전문 파일 + 완료 아카이브로 &lt;b&gt;정보 아키텍처 재설계&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;성과(정량)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평균 &lt;b&gt;토큰 사용량 76.1% 절감&lt;/b&gt; (42,000 &amp;rarr; 10,100)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;응답 시간 평균 75% 단축&lt;/b&gt; (질의 유형별 73~83%)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;탐색 시간 70% 단축&lt;/b&gt;, &lt;b&gt;개발 효율성 4배&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;API 비용 연간 $8,731 절감&lt;/b&gt;, &lt;b&gt;개발 시간 절감 ROI 연간 ~$80,000&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;성과(정성)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자 만족도 및 정보 적합성 유의미 개선(예: 정보 적합성 3.1/5 &amp;rarr; 4.6/5, +48%)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 문제 정의: 단일 파일 + Token Limit의 복합 병목&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 파일 규모&lt;/b&gt;: 2,247 라인, 추정 38k~42k tokens.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구성 혼재&lt;/b&gt;: 완료&amp;middot;진행&amp;middot;계획&amp;middot;결제&amp;middot;보안&amp;middot;메타데이터가 &lt;b&gt;한 문서&lt;/b&gt;에 공존 &amp;rarr; 컨텍스트 노이즈 급증.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;직접 영향&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델 호출 시 &lt;b&gt;token limit exceed&lt;/b&gt; &amp;rarr; AI 기반 검색/요약/연결 자동화 &lt;b&gt;중단&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;사람 입장에서도 원하는 태스크를 찾기 위해 &lt;b&gt;분 단위 탐색&lt;/b&gt;이 상시 발생.&lt;/li&gt;
&lt;li&gt;협업(병렬 편집&amp;middot;책임 분리)과 진행률 가시성 모두 &lt;b&gt;저하&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) 설계 원칙: Separation of Concerns + 선택적 로딩&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &amp;ldquo;&lt;b&gt;파일을 자르는 것&lt;/b&gt;&amp;rdquo;이 아니라 &amp;ldquo;&lt;b&gt;컨텍스트를 설계&lt;/b&gt;&amp;rdquo;하는 것입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;도메인 중심 분류(Domain-Driven Organization)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;태스크를 기능적 도메인 기준으로 재배치(코어 개발, 결제/구독, 품질/국제화, 인프라/보안, 프로젝트 운영, 미래 확장).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;계층적 참조(Hierarchical References)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;마스터 인덱스(180라인)&lt;/b&gt; = 한눈에 조망 + 내비게이션 허브.&lt;/li&gt;
&lt;li&gt;세부 작업은 &lt;b&gt;전문화 파일&lt;/b&gt;에서만 읽음 &amp;rarr; &lt;b&gt;선택적 로딩&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스마트 참조(Smart Referencing)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 질의는 &lt;b&gt;인덱스만&lt;/b&gt;, 특정 업무는 &lt;b&gt;인덱스 + 해당 1개 파일&lt;/b&gt;, 완료 검토는 &lt;b&gt;아카이브만&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3) 최종 구조: 인덱스 + 전문 파일 + 아카이브&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/development
├─ tasks.md                         # Master Index (180 lines)
├─ completed-tasks-archive.md       # 완료 아카이브 (371 lines, 23건)
└─ /current-tasks
   ├─ 01-core-development.md        # 260 lines
   ├─ 02-payments-subscriptions.md  # 273 lines
   ├─ 03-quality-testing.md         # 227 lines
   ├─ 04-infrastructure-security.md # 259 lines
   ├─ 05-project-management.md      # 221 lines
   └─ 06-future-expansion.md        # 262 lines&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4) 구현 프로세스(Phase별)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phase 1: 분석/분류&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;wc -l&lt;/code&gt;, &lt;code&gt;grep -c&lt;/code&gt; 등으로 라인&amp;middot;섹션&amp;middot;태스크 &lt;b&gt;정량 진단&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;완료 23, 진행 15, 계획 25, 메타데이터 247+ 라인&lt;/li&gt;
&lt;li&gt;교차 관심사(Cross-cutting) 식별 &amp;rarr; Primary 배치 + Cross-reference&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phase 2: 구조화/분리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Master Index&lt;/b&gt; 초안 작성&lt;/li&gt;
&lt;li&gt;전문 파일 6종 생성, &lt;b&gt;파일명/순서 규칙화&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phase 3: 완료 아카이브&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;완료 23건을 &lt;b&gt;독립 파일&lt;/b&gt;로 이동&lt;/li&gt;
&lt;li&gt;&lt;b&gt;활성 컨텍스트&lt;/b&gt;에서 완료 이력 제거 &amp;rarr; 현재 작업의 신선도 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5) 성과(정량): 토큰&amp;middot;응답&amp;middot;비용&amp;middot;시간&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 토큰 절감&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Before&lt;/b&gt;: 질의 유형 무관 &lt;b&gt;42,000 tokens&lt;/b&gt; 평균&lt;/li&gt;
&lt;li&gt;&lt;b&gt;After&lt;/b&gt;(평균 10,100 tokens, 가중 평균)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 현황(40%): &lt;b&gt;2,800&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;도메인 작업(35%): &lt;b&gt;7,300&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;우선순위 확인(15%): &lt;b&gt;800&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;종합 분석(10%): &lt;b&gt;11,800&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;절감률&lt;/b&gt;: &lt;b&gt;76.1%&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 응답 시간(실측)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순 질의: &lt;b&gt;15&lt;del&gt;25s &amp;rarr; 3&lt;/del&gt;5s&lt;/b&gt; (75~83%&amp;darr;)&lt;/li&gt;
&lt;li&gt;복합 분석: &lt;b&gt;35&lt;del&gt;50s &amp;rarr; 8&lt;/del&gt;12s&lt;/b&gt; (76~77%&amp;darr;)&lt;/li&gt;
&lt;li&gt;도메인 특정: &lt;b&gt;20&lt;del&gt;30s &amp;rarr; 5&lt;/del&gt;8s&lt;/b&gt; (73~75%&amp;darr;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.3 API 비용 절감(예시)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가격: &lt;b&gt;$15 / 1M tokens&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Before: &lt;b&gt;$31.50/day&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;After : &lt;b&gt;$7.58/day&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;연간 절감: ~$8,731&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.4 개발 시간 ROI(예시)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;연간 ~$80,000 절감&lt;/b&gt; (탐색 시간 단축 기반)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6) 성과(정성)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정보 적합성&lt;/b&gt;: 3.1/5 &amp;rarr; &lt;b&gt;4.6/5&lt;/b&gt; (+48%)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;태스크 발견 만족도&lt;/b&gt;: 4.2/5 &amp;rarr; &lt;b&gt;4.8/5&lt;/b&gt; (+14%)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컨텍스트 전환 스트레스&lt;/b&gt;: 3.8/5 &amp;rarr; &lt;b&gt;2.1/5&lt;/b&gt; (&amp;ndash;45%)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;체감 생산성&lt;/b&gt;: 3.5/5 &amp;rarr; &lt;b&gt;4.7/5&lt;/b&gt; (+34%)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;투명성&lt;/b&gt;: 완료 23건 vs 진행 항목 &lt;b&gt;명확 구분&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7) 통합 가이드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반 질의&lt;/b&gt;: 인덱스만 로딩&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특정 업무&lt;/b&gt;: 인덱스 + 해당 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;우선순위 확인&lt;/b&gt;: 인덱스 부분만&lt;/li&gt;
&lt;li&gt;&lt;b&gt;완료 검토&lt;/b&gt;: 아카이브만&lt;/li&gt;
&lt;li&gt;Prompt/에이전트 룰: &lt;b&gt;인덱스&amp;rarr;파일 선택적 로딩&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8) 도전과 극복&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;경계 모호&lt;/b&gt; &amp;rarr; Primary+Cross-ref 규칙 확립&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정보 중복&lt;/b&gt; &amp;rarr; Essential Duplication 최소화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;팀 적응 기간&lt;/b&gt; &amp;rarr; 2~3주 온보딩 가이드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;초기 혼란&lt;/b&gt; &amp;rarr; 인덱스 상단 &amp;ldquo;파일 결정표&amp;rdquo; 제공&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파일 동기화&lt;/b&gt; &amp;rarr; 리뷰 시 인덱스 무결성 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9) 재현 체크리스트&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Week 1&lt;/b&gt;: 진단 (라인 수&amp;middot;태스크 수 분석)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Week 2&lt;/b&gt;: 도메인 그룹핑&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Week 3~4&lt;/b&gt;: 파일화 &amp;amp; 인덱싱&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Month 2&lt;/b&gt;: 운영 정착 &amp;amp; 리뷰 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10) 요약 인사이트&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;엔지니어&lt;/b&gt;: Token limit은 &lt;b&gt;입력 제약&lt;/b&gt;이자 &lt;b&gt;설계 요구사항&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PM/리더&lt;/b&gt;: 문서 구조 최적화는 &lt;b&gt;ROI를 만들어내는 전략적 행위&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;부록 A. 시나리오별 절감률&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반 현황&lt;/b&gt;: 42,000 &amp;rarr; 2,800 (&lt;b&gt;&amp;ndash;93.3%&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도메인 작업&lt;/b&gt;: 42,000 &amp;rarr; 7,300 (&lt;b&gt;&amp;ndash;82.6%&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;완료 검토&lt;/b&gt;: 42,000 &amp;rarr; 6,500 (&lt;b&gt;&amp;ndash;84.5%&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;종합 분석&lt;/b&gt;: 42,000 &amp;rarr; 11,800 (&lt;b&gt;&amp;ndash;71.9%&lt;/b&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;부록 B. 파일/라인 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 180 / 아카이브 371 / 전문 6개(227~273, 평균 250&amp;plusmn;)&lt;/li&gt;
&lt;li&gt;활성 태스크: 진행 15 / 계획 25 / 완료 23&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;CLAUDE-CODE의 토큰을 절약하기 - tasks.md의 문서 구조 개편&quot; href=&quot;https://velog.io/@bernoyoun/CLAUDE-CODE%EC%9D%98-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%A0%88%EC%95%BD%ED%95%98%EA%B8%B0-tasks.md%EC%9D%98-%EB%AC%B8%EC%84%9C-%EA%B5%AC%EC%A1%B0-%EA%B0%9C%ED%8E%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bernoyoun/CLAUDE-CODE%EC%9D%98-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%A0%88%EC%95%BD%ED%95%98%EA%B8%B0-tasks.md%EC%9D%98-%EB%AC%B8%EC%84%9C-%EA%B5%AC%EC%A1%B0-%EA%B0%9C%ED%8E%B8&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756223470079&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;CLAUDE-CODE의 토큰을 절약하기 - tasks.md의 문서 구조 개편&quot; data-og-description=&quot;CLAUDE-CODE의 토큰을 절약하기&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@bernoyoun/CLAUDE-CODE%EC%9D%98-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%A0%88%EC%95%BD%ED%95%98%EA%B8%B0-tasks.md%EC%9D%98-%EB%AC%B8%EC%84%9C-%EA%B5%AC%EC%A1%B0-%EA%B0%9C%ED%8E%B8&quot; data-og-url=&quot;https://velog.io/@bernoyoun/CLAUDE-CODE의-토큰을-절약하기-tasks.md의-문서-구조-개편&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bPdORp/hyZF8W2w4R/BXAyMnfuHzhl2fRI4PX69k/img.png?width=2466&amp;amp;height=1562&amp;amp;face=0_0_2466_1562,https://scrap.kakaocdn.net/dn/ixXCb/hyZC3QC39r/hA1x0SMo9rH1TTu4NukMZK/img.png?width=2466&amp;amp;height=1562&amp;amp;face=0_0_2466_1562,https://scrap.kakaocdn.net/dn/b95spI/hyZDUeoTjx/Rx9lQ8WfQKfVkbgfMTuDAK/img.png?width=2466&amp;amp;height=1562&amp;amp;face=0_0_2466_1562&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/CLAUDE-CODE%EC%9D%98-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%A0%88%EC%95%BD%ED%95%98%EA%B8%B0-tasks.md%EC%9D%98-%EB%AC%B8%EC%84%9C-%EA%B5%AC%EC%A1%B0-%EA%B0%9C%ED%8E%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@bernoyoun/CLAUDE-CODE%EC%9D%98-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%A0%88%EC%95%BD%ED%95%98%EA%B8%B0-tasks.md%EC%9D%98-%EB%AC%B8%EC%84%9C-%EA%B5%AC%EC%A1%B0-%EA%B0%9C%ED%8E%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bPdORp/hyZF8W2w4R/BXAyMnfuHzhl2fRI4PX69k/img.png?width=2466&amp;amp;height=1562&amp;amp;face=0_0_2466_1562,https://scrap.kakaocdn.net/dn/ixXCb/hyZC3QC39r/hA1x0SMo9rH1TTu4NukMZK/img.png?width=2466&amp;amp;height=1562&amp;amp;face=0_0_2466_1562,https://scrap.kakaocdn.net/dn/b95spI/hyZDUeoTjx/Rx9lQ8WfQKfVkbgfMTuDAK/img.png?width=2466&amp;amp;height=1562&amp;amp;face=0_0_2466_1562');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CLAUDE-CODE의 토큰을 절약하기 - tasks.md의 문서 구조 개편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;CLAUDE-CODE의 토큰을 절약하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #000000; text-align: left;&quot;&gt;조금 가벼워진 글은 VELOG에 올라갑니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>바이브코딩일지</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/196</guid>
      <comments>https://developer-youn.tistory.com/196#entry196comment</comments>
      <pubDate>Wed, 27 Aug 2025 00:50:52 +0900</pubDate>
    </item>
    <item>
      <title>CLAUDE.md 구조 개편 - 왜 기억을 못하니</title>
      <link>https://developer-youn.tistory.com/195</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;본 글은 CLAUDE + GPT와 함께 작성한 글임을 밝힙니다.&lt;br /&gt;CLAUDE-CODE에서 왜 자꾸 너는 CLAUDE.md에 있는 내용을 까먹는거야? 와 같은 내용으로 질문을 하였을때 알려준 내용을 토대로 나온 글입니다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bos8rV/btsPTazdkcJ/Z69j9K6KyeL1dJ27ga7sm1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bos8rV/btsPTazdkcJ/Z69j9K6KyeL1dJ27ga7sm1/img.jpg&quot; data-alt=&quot;필자는 왜 CLAUDE.md의 명세를 제대로 기억 못하는지 한동안 이 표정으로 불만이었음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bos8rV/btsPTazdkcJ/Z69j9K6KyeL1dJ27ga7sm1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbos8rV%2FbtsPTazdkcJ%2FZ69j9K6KyeL1dJ27ga7sm1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;310&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;필자는 왜 CLAUDE.md의 명세를 제대로 기억 못하는지 한동안 이 표정으로 불만이었음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CLAUDE.md의 내용을 자꾸 까먹는다니까요.&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;반복된 실패 사례&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자 요청: &quot;PR 머지해&quot;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기대 동작: 문서 4개 자동 업데이트 (status.md, tasks.md, README.md, dev-notes/)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실제 동작: PR만 머지 &amp;rarr; 문서 업데이트 누락&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결과: 상태 불일치, 뒤늦게 수동 작업&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;비즈니스 임팩트&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자동화 도입 효과 상실 &amp;rarr; 수동 부담 증가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프로젝트 문서와 실제 상태 불일치&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인지 부하 증가 &amp;rarr; 개발자 경험(DevEx) 악화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;규칙 준수율 저하 &amp;rarr; 품질 저하&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원인 분석&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 과도한 정보량&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기존 CLAUDE.md: 약 1,000줄, 6개 대섹션 + 수많은 규칙 혼재&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정보 과잉으로 실시간 의사결정 불가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 우선순위 불명확&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실행 규칙과 참고 정보가 동일한 위상으로 배치&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Critical 규칙(예: PR 머지)이 묻혀 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 인지 부하&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;매번 문서를 스캔해야 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;키워드 기반 빠른 접근 불가&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4. 실행 vs 참조 혼재&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;명령어 키워드와 관련 액션 매핑 불투명&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문서 업데이트 절차가 여러 군데에 흩어져 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결책: 3-Tier 문서 아키텍처&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;철학&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Just-In-Time Information Access&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;rarr; 필요한 순간, 필요한 정보만. 나머지는 계층적으로 뒤로 배치.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구조&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Tier 1: CLAUDE.md (허브)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자 요청별 즉시 실행 규칙 테이블&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;핵심 참조 문서 링크&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SuperClaude 프레임워크 요약&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;길이: 100줄 내외&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Tier 2: 실행 가이드 문서&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AUTO-DOC-UPDATE.md: PR 머지 후 자동화 절차 상세&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;GIT-WORKFLOW-RULES.md: Git 전략 및 워크플로우 규칙&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MAGIC-UI-GUIDE.md: UI 컴포넌트 생성 규칙&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Tier 3: 참고 문서&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;A4LAB-DESIGN-PRINCIPLES.md: 설계 철학과 아키텍처 패턴&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;PROJECT-CONTEXT.md: 비즈니스 맥락, 기술 스택 설명&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;SuperClaude 프레임워크 전역 설정 (~/.claude)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;키워드 매핑 예시&lt;/span&gt;&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;키워드&lt;/span&gt;&lt;/th&gt;
&lt;th&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉시 액션&lt;/span&gt;&lt;/th&gt;
&lt;th&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참조&lt;/span&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&quot;PR 머지해&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4개 문서 자동 업데이트&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;AUTO-DOC-UPDATE.md&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&quot;UI 만들어&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컴포넌트 생성 절차 실행&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MAGIC-UI-GUIDE.md&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;벤치마킹: 외부 유사 사례&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Stripe API 문서&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://docs.stripe.com/get-started&quot;&gt;Quick Start&lt;/a&gt;와 &lt;a href=&quot;https://docs.stripe.com/api&quot;&gt;API Reference&lt;/a&gt; 분리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;초심자와 숙련자를 동시에 만족시키는 구조&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;VS Code 확장 문서&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://code.visualstudio.com/api&quot;&gt;개요 &amp;rarr; 가이드 &amp;rarr; API&lt;/a&gt;로 계층화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Progressive Disclosure 적용 사례&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;AWS 문서&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/getting-started/&quot;&gt;Getting Started&lt;/a&gt;와 &lt;a href=&quot;https://docs.aws.amazon.com/prescriptive-guidance/latest/security-reference-architecture/deep-dive.html&quot;&gt;Deep Dive&lt;/a&gt; 이원화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;온보딩과 심화 학습을 분리해 정보 과부하를 방지&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;기대 효과&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;즉시 효과 (도입 직후)&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문서 길이 100줄 이하 달성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;핵심 규칙 테이블화&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참조 문서 4개 이상 분리 완료&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결론&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;CLAUDE.md 개편은 단순한 문서 구조 조정이 아님&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;개발자 경험(DevEx) 개선 프로젝트&lt;/b&gt;이며, 인지 부하를 줄이고 규칙 준수율을 높이는 전략적 투자&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Stripe, VS Code, AWS 사례와 같은 &lt;b&gt;정보 설계(Information Architecture)&lt;/b&gt; 원칙을 실무에 적용한 사례&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조금 가벼워진 글은 velog에 올라갑니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@bernoyoun/CLAUDE.md-%EB%AC%B8%EC%84%9C%EA%B0%80-%EB%84%88%EB%AC%B4-%EA%B8%B8%EB%8B%A4%EA%B3%A0-%EB%AA%BB%EC%9D%BD%EA%B2%A0%EB%8B%A4%EC%9E%96%EC%95%84%EC%9A%94&quot;&gt;CLAUDE.md 문서가 너무 길다고 못읽겠다잖아요!&lt;/a&gt;&lt;/p&gt;</description>
      <category>바이브코딩일지</category>
      <author>Berno</author>
      <guid isPermaLink="true">https://developer-youn.tistory.com/195</guid>
      <comments>https://developer-youn.tistory.com/195#entry195comment</comments>
      <pubDate>Sat, 16 Aug 2025 16:35:31 +0900</pubDate>
    </item>
  </channel>
</rss>