<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발과 일상</title>
    <link>https://akku-dev.tistory.com/</link>
    <description>개발자의 일기장</description>
    <language>ko</language>
    <pubDate>Wed, 8 Apr 2026 16:23:27 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>애쿠</managingEditor>
    <item>
      <title>asyncio와 Segmentation Fault(Segfault) 에러</title>
      <link>https://akku-dev.tistory.com/308</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M4d8u/dJMcacWALfA/8XIFs7UeALhmnKV7jWGnGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M4d8u/dJMcacWALfA/8XIFs7UeALhmnKV7jWGnGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M4d8u/dJMcacWALfA/8XIFs7UeALhmnKV7jWGnGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM4d8u%2FdJMcacWALfA%2F8XIFs7UeALhmnKV7jWGnGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;572&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;color: #333333;&quot;&gt;나는 그동안 클라우드나 온프레미스 웹 서비스 개발을 주로 해오다가, 이번에 처음으로 온디바이스(On-device) 솔루션 환경 개발을 맡게 됐다. 그러다 보니 서버를 .exe 파일로 빌드해서 배포하는 생소한 경험을 하게 됐는데, 백그라운드 루프가 별도의 에러 없이 꺼지는 현상을 발견했다. 에러 로그가 없다보니 어느 시점에 종료되는지 정도만 파악할 수 있었는데... LLM에게 코드 재검토를 맡겨보니 asyncio가 Segmentation Fault(Segfault) 에러를 발생시킬 수 있다는 걸 처음 알게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;왜 하필 다른데서는 다 잘 되다가 온디바이스(Windows/EXE) 환경에서 문제가 발생했을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;결론부터 말하자면, &lt;/span&gt;&lt;b&gt;메모리 관리의 '관대함' 차이&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt; 때문이다. 넉넉한 리소스의 리눅스 서버에선 가비지 컬렉션(GC)이 비교적 느슨하게 동작하여 문제가 숨겨져 있다가, 리소스가 제한적이고 타이트하게 컴파일된 .exe 환경으로 넘어오자 잠재되어 있던 '태스크 소멸 현상'이 발생하게 된 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;태스크가 사라지는 현상 (Garbage Collection)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NxIyQ/dJMcabpTkjm/DIGucdLz6FckkfTbeW9Sa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NxIyQ/dJMcabpTkjm/DIGucdLz6FckkfTbeW9Sa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NxIyQ/dJMcabpTkjm/DIGucdLz6FckkfTbeW9Sa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNxIyQ%2FdJMcabpTkjm%2FDIGucdLz6FckkfTbeW9Sa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;559&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;문제의 핵심은 파이썬의 가비지 컬렉션(GC)에 있었다. asyncio.create_task()로 만든 태스크 객체는 어디선가 참조(Reference)를 유지하고 있지 않으면, 파이썬 엔진은 이 태스크가 더 이상 필요 없다고 판단한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그렇게 되면 백그라운드 태스크가 한창 돌아가는 와중에 GC가 &quot;어? 이거 아무도 안 쓰네?&quot; 하고 메모리에서 해제해버린다. 이때 실행 중이던 코드는 이미 해제된(혹은 엉뚱한) 메모리 주소를 건드리게 되고, 결국 운영체제는 &lt;b data-index-in-node=&quot;143&quot; data-path-to-node=&quot;10&quot;&gt;Segmentation Fault&lt;/b&gt;를 던지며 프로세스를 강제 종료시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;로그 한 줄 남기지 못하고 서버가 죽어버렸던 이유가 바로 이것이었다 (멀티쓰레드로 동작해서 일 수도 있다).&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt; 해결 방법: 강한 참조(Strong Reference) 유지하기&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;참조를 유지하지 않아서 태스크에 할당된 메모리를 해제해버린다면 강제로 할당하면 된다. 이 방식은 조금 투박해 보일 수 있지만, 이는 &lt;b&gt;파이썬 공식 문서에서도 명시한 권장 해결 방법&lt;/b&gt;이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1773127052317&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import asyncio
from fastapi import FastAPI

app = FastAPI()

# 1. 태스크를 담아둘 전역 세트 생성 (강한 참조 유지용)
background_tasks = set()

async def some_heavy_task(name: str):
    try:
        # 실제 백그라운드 로직 수행
        await asyncio.sleep(10) 
        print(f&quot;Task {name} 완료&quot;)
    except Exception as e:
        print(f&quot;Task 에러 발생: {e}&quot;)
    finally:
        # 3. 작업이 완전히 끝나면 세트에서 제거 (메모리 누수 방지)
        # 이 시점에는 안전하게 메모리에서 해제되어도 무방하다.
        background_tasks.discard(task)

@app.get(&quot;/trigger&quot;)
async def trigger(name: str):
    # 2. 태스크 생성 후 즉시 세트에 추가
    task = asyncio.create_task(some_heavy_task(name))
    background_tasks.add(task)
    
    # 또는 콜백을 활용해 깔끔하게 제거할 수도 있다.
    # task.add_done_callback(background_tasks.discard)
    
    return {&quot;status&quot;: &quot;started&quot;, &quot;message&quot;: &quot;백그라운드 루프가 안전하게 시작되었습니다.&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;생성된 태스크 객체가 GC의 타겟이 되지 않도록 세트(Set)나 리스트 등에 명시적으로 담아두어&amp;nbsp;&lt;/span&gt;&lt;b&gt;참조 카운트를 1 이상으로 유지&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;하는 것이다. 아래는 공식 문서에서 제안한 방식이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;537&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgLcJG/dJMcahcw2bK/j6yHToCtlErEH5jdjqXLX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgLcJG/dJMcahcw2bK/j6yHToCtlErEH5jdjqXLX0/img.png&quot; data-alt=&quot;https://docs.python.org/ko/3/library/asyncio-task.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgLcJG/dJMcahcw2bK/j6yHToCtlErEH5jdjqXLX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgLcJG%2FdJMcahcw2bK%2Fj6yHToCtlErEH5jdjqXLX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;801&quot; height=&quot;537&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://docs.python.org/ko/3/library/asyncio-task.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;코드를 간단하게 정리해보자면 생성된 태스크 객체가 GC의 타겟이 되지 않도록&amp;nbsp;&lt;/span&gt;&lt;b data-path-to-node=&quot;3&quot; data-index-in-node=&quot;51&quot;&gt;어딘가(Set이나 List)에 명시적으로 담아두는 것&lt;/b&gt;이다. 태스크가 실행 중인 동안 참조 카운트(Reference Count)를 1 이상으로 유지하여 메모리에서 해제되는 것을 방지한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그런데 이 segfault 에러는 async 동작을 전부 sync로 변경하니 사라지게 됐다. 명시적인 할당 없이 매서드를 동기로 변경하면 왜 해결이 될까?&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;동기(Sync)로 전환했을 때 해결되는 이유 &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WRVvq/dJMcabKchMe/3LyAdVrsBS8dLwGE2EuHZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WRVvq/dJMcabKchMe/3LyAdVrsBS8dLwGE2EuHZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WRVvq/dJMcabKchMe/3LyAdVrsBS8dLwGE2EuHZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWRVvq%2FdJMcabKchMe%2F3LyAdVrsBS8dLwGE2EuHZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1408&quot; height=&quot;768&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이는 &lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;15,0&quot;&gt;메모리 관리의 책임 주체&lt;/b&gt;가 바뀌었기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;15,1&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;asyncio 루프는 태스크를 '약한 참조'로 느슨하게 쥐고 있어 GC에 취약하다. 반면, FastAPI의 &lt;/span&gt;&lt;b&gt;워커 스레드&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;는 동기 함수(def)가 실행되는 동안 그 내부 리소스를 스택 메모리에 강하게 고정(Lock-in)시킨다. 즉, OS 스레드 자체가 일종의 거대한 '강한 참조' 역할을 해주기 때문에, 온디바이스의 제한적인 메모리 환경에서도 객체가 증발하지 않고 살아남을 수 있었던 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;마치며&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아마 LLM이 없었으면 이 에러는 영원히 해결하지 못했을지도 모른다. 스레드를 한두 개 더 만들거나 백그라운드 작업을 맡기는 건 서버 개발자 입장에서 특별한 작업이 아니라고 생각했었기에 더 당혹스러웠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;파이썬 이벤트루프를 통한 CG의 동작 개념을 100% 이해하진 못했지만, &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이번 경험을 통해 &lt;b data-index-in-node=&quot;41&quot; data-path-to-node=&quot;20&quot;&gt;환경에 따라 기술의 정석이 바뀔 수 있다&lt;/b&gt;는 점을 다시한번 느꼈다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;제한적인 온디바이스 환경에서는 유연성을 갖춘 비동기식보다 고전적이고 확실한 동기 방식이 정답일 수 있다는 것을 배웠다. 슬픈점은 이걸 배우기 위해서 너무 많은 시간 소모가 있었다는 것 ㅠㅠ&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발/뭔지모르면여기</category>
      <category>asyncio</category>
      <category>EXE</category>
      <category>FastAPI</category>
      <category>python</category>
      <category>segmentation fault</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/308</guid>
      <comments>https://akku-dev.tistory.com/308#entry308comment</comments>
      <pubDate>Wed, 11 Mar 2026 10:32:34 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI 비동기 실행  BackgroundTasks/asyncio</title>
      <link>https://akku-dev.tistory.com/306</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p5Jnb/dJMcaaR2O1x/c8YkT2IsobR3eJWISVteH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p5Jnb/dJMcaaR2O1x/c8YkT2IsobR3eJWISVteH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p5Jnb/dJMcaaR2O1x/c8YkT2IsobR3eJWISVteH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp5Jnb%2FdJMcaaR2O1x%2Fc8YkT2IsobR3eJWISVteH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;572&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 개발을 하다 보면 일단 응답을 내보내서 화면은 동작시키고, 서버 백그라운드에서 작업을 하도록 만들고 싶은 경우가 있다. 이런 작업을 보통 비동기 작업이라고 하는데, &lt;b&gt;스프링(Spring)에서는 @Async&lt;/b&gt;가 주로 쓰인다 &amp;nbsp; &lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://akku-dev.tistory.com/89&quot;&gt;스프링 부트에서 초간단 멀티쓰레드 구현하기 : @Async&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 기반으로 개발하면서 이런 상황에서 어떤 걸 쓸까 고민하며 찾아보니, 주로 &lt;b&gt;asyncio&lt;/b&gt;와&lt;b&gt; BackgroundTasks&lt;/b&gt;라는 도구를 사용하고 있었다. 하지만 이 둘은 동작 방식과 시점이 크게 다르기 때문에, 각각 어떤 특징이 있고 주의할 점은 무엇인지 정리해 보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. asyncio&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 기본 제공하는 라이브러리로, 가장 핵심적인 비동기 처리 방식이다. 특징이 있다면 &lt;b data-index-in-node=&quot;51&quot; data-path-to-node=&quot;7&quot;&gt;응답이 나가는 걸 대기하지 않고 즉시 동작&lt;/b&gt;한다는 점이다.&lt;/p&gt;
&lt;pre id=&quot;code_1773124505687&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import asyncio
from fastapi import FastAPI

app = FastAPI()

async def some_library_task():
    await asyncio.sleep(5)  # 비동기 작업 시뮬레이션
    print(&quot;Task Complete!&quot;)

@app.get(&quot;/trigger&quot;)
async def trigger_task():
    # 호출 즉시 작업이 루프에 등록됨 (응답 대기 안 함)
    asyncio.create_task(some_library_task())
    return {&quot;message&quot;: &quot;Task triggered immediately&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 보듯 asyncio.create_task()를 사용하는 순간, 해당 코루틴은 즉시&lt;b&gt; 이벤트 루프(Event Loop)&lt;/b&gt;에 스케줄링된다. asyncio의 장점에 대해 정리해 보면 다음과 같다.&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;b&gt;비차단(Non-blocking) 실행: await&lt;/b&gt;를 만나기 전까지는 메인 흐름을 방해하지 않고 작업을 던져놓는다. API의 return을 기다릴 필요 없이 백그라운드에서 바로 연산이 시작되므로, 작업 시점을 세밀하게 제어할 수 있다.&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;b&gt;직관성과 유연성:&lt;/b&gt; 별도의 프레임워크 기능에 의존하지 않고 파이썬 표준 문법만으로 비동기 로직을 짤 수 있다. 특히 여러 비동기 작업을 병렬로 묶어서 실행하거나(asyncio.gather), 특정 시간 안에 끝나지 않으면 취소하는 등의 정교한 핸들링이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Backgrounds&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 자체적으로 제공하는 기능으로, Starlette의 기능을 상속받아 구현되어 있다. asyncio와의 결정적인 차이는 &lt;b&gt;응답이 클라이언트에게 완전히 전달된 직후에 작업이 시작&lt;/b&gt;된다는 점이다.&lt;/p&gt;
&lt;pre id=&quot;code_1773124721269&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rom fastapi import FastAPI, BackgroundTasks

app = FastAPI()

def write_log(message: str):
    with open(&quot;log.txt&quot;, &quot;a&quot;) as f:
        f.write(message)

@app.post(&quot;/send-notification/{email}&quot;)
async def send_notification(email: str, background_tasks: BackgroundTasks):
    # 1. 응답을 먼저 보내고
    # 2. 그 다음에 write_log가 실행됨
    background_tasks.add_task(write_log, f&quot;notification sent to {email}&quot;)
    return {&quot;message&quot;: &quot;Notification sent in the background&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 시점만 다른 게 아니다. 실무적인 관점, 특히 안정성이 중요한 백엔드 개발자 입장에서 사용하기에는 BackgroundTasks가 asyncio보다 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;&lt;b&gt;1&lt;/b&gt;. &lt;b&gt;프레임워크가 보장하는 생명주기(Lifecycle):&lt;/b&gt; &lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;&lt;b&gt;asyncio&lt;/b&gt;는 파이썬 엔진에 작업을 던져놓는 것이라, 요청(Request)이 끝나버리면 그 태스크가 어떻게 되든 프레임워크는 신경 쓰지 않지만, &lt;b&gt;BackgroundTasks&lt;/b&gt;는 FastAPI가 응답을 보낸 직후에 실행되도록 직접 관리해준다. 덕분에 의존성 주입(Depends)을 통한 DB 세션 관리 등이 훨씬 안전하다.&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;2. 동기(Sync) 함수의 간편한 처리: &lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;&lt;b&gt;asyncio&lt;/b&gt;는 반드시 async def 함수만 다룰 수 있지만, &lt;b&gt;BackgroundTasks&lt;/b&gt;는 일반 def 함수를 넣어도 FastAPI가 알아서 별도의 스레드 풀에서 돌려준다. 메인 루프를 멈추지 않으면서도 기존의 동기 라이브러리들을 그대로 쓸 수 있다는 건 큰 장점이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;3. 리소스 관리의 안정성:&lt;/b&gt; &lt;b&gt;asyncio&lt;/b&gt;는 태스크 객체의 참조가 끊기면 가비지 컬렉터(GC)가 실행 중인 작업을 날려버릴 위험이 있다. 하지만 BackgroundTasks는 프레임워크 내부에서 작업 리스트를 들고 있어 작업이 끝날 때까지 참조를 유지한다. 결과적으로 작업이 중간에 증발할 확률이 훨씬 낮다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;FastAPI는 스프링보다 가벼워 사용성이 좋고, 특히 비동기 동작에 대한 디버깅을 실제 동작처럼 확인하며 구현할 수 있다는 점이 큰 매력이었다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이번 프로젝트에서는 서버 시작 시점부터 백그라운드 루프가 계속 돌아야 하는 구조였기에, 선택의 여지 없이 &lt;b&gt;asyncio&lt;/b&gt;를 베이스로 구현하게 됐다. 파이썬의 백그라운드 루프에서의 &lt;b&gt;트랜잭션 관리&lt;/b&gt;는 스프링의 @Transactional에 익숙했던 나에게는 꽤나 복잡했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;백그라운드 루프는 HTTP 요청과는 독립적으로 돌기 때문에, FastAPI의 표준인 Depends(get_db) 방식을 쓸 수 없었다. 결국 &lt;b data-index-in-node=&quot;79&quot; data-path-to-node=&quot;6&quot;&gt;SessionLocal() 팩토리를 직접 호출&lt;/b&gt;하여 매 루프마다 세션을 수동으로 열고 닫아야 했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;스프링에서는 어노테이션 하나로 해결되던 &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;7&quot;&gt;선언적 트랜잭션&lt;/b&gt;의 편리함을 뒤로하고, 파이썬에서는 세션 객체를 메서드마다 인자로 넘겨주거나 직접 생명주기를 관리해야 하는 '명시적'인 수고로움이 뒤따랐다. 그동안 당연하게 누려왔던 프레임워크의 마법이 얼마나 고마운 것이었는지 새삼 깨닫는 시간이었다.&lt;/p&gt;</description>
      <category>개발</category>
      <category>asyncio</category>
      <category>BackgroundTasks</category>
      <category>FastAPI</category>
      <category>python</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/306</guid>
      <comments>https://akku-dev.tistory.com/306#entry306comment</comments>
      <pubDate>Sat, 28 Feb 2026 17:10:28 +0900</pubDate>
    </item>
    <item>
      <title>RAG 구축 시 고려해야 할 것들</title>
      <link>https://akku-dev.tistory.com/304</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/liCl1/dJMcajnum4z/COMtoevPxmGb2UXKLft3x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/liCl1/dJMcajnum4z/COMtoevPxmGb2UXKLft3x1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/liCl1/dJMcajnum4z/COMtoevPxmGb2UXKLft3x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FliCl1%2FdJMcajnum4z%2FCOMtoevPxmGb2UXKLft3x1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;559&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;현재 온디바이스(On-Device) 환경에서 사용할 RAG 솔루션을 개발 중에 있다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;매번 하드웨어의 제약이 사실상 없는 클라우드 환경에서만 개발하다가, 리소스 제약이 심한 온디바이스 환경으로 넘어와 초기 RAG 시스템을 개선하려다 보니 고민해야 할 부분들이 정말 많았다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;전체 구조를 도식화하면 다음과 같다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhYrZ0/dJMcagj25AA/GsHoCkwvDh7xTjy5kwu5T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhYrZ0/dJMcagj25AA/GsHoCkwvDh7xTjy5kwu5T0/img.png&quot; data-alt=&quot;https://www.chitika.com/building-end-to-end-rag/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhYrZ0/dJMcagj25AA/GsHoCkwvDh7xTjy5kwu5T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdhYrZ0%2FdJMcagj25AA%2FGsHoCkwvDh7xTjy5kwu5T0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;590&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.chitika.com/building-end-to-end-rag/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;이 그림이 대부분의 RAG 시스템을 설명해준다. 그림을 보고 다시 생각해보니 사실 안 중요한 부분이 없긴 한데...&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;그래도 그 중에서 조금 더 중요하다고 생각하는 부분부터 하나씩 정리해보자.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-path-to-node=&quot;8&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;1. 임베딩을 하기 위한 데이터 정제&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;현재는 사내에서 자체 개발한 문서 분석 툴을 사용하고 있다. 데이터 정제 과정이 중요한 이유는, 이 전처리를 얼마나 잘하느냐에 따라 표나 이미지에 포함된 정보에 대한 질문을 처리할 수 있는지 여부가 갈리기 때문이다. 또한, 다양한 비즈니스 환경을 고려했을 때 문서 종류(포맷)를 어디까지 커버할 수 있느냐도 매우 중요한 문제다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;2. 임베딩 모델&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임베딩 기법도 다양해졌고, 언어 특화 모델도 우후죽순 쏟아져 나오고 있다. 단순히 성능 지표가 높은 모델을 고르면 되는 서버 환경과 달리, 온디바이스에서는 모델의 크기(Size)와 추론 속도(Latency)까지 고려해야 하니 선택이 더욱 까다롭다. 결국 어떤 임베딩 모델을 채택하느냐가 검색 품질의 상한선을 결정짓고, 이것이 곧 RAG 전체 시스템의 성능을 가른다고 해도 틀린 말이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 스타일(언어, 질의의 종류)로 임베딩해두는게 가장 좋긴한데 온디바이스 환경에서는 선택권이 많지 않다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;2&quot;&gt;3. 벡터 데이터베이스&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 벡터 데이터베이스가 크게 중요하진 않은 것 같다. 벤더마다 특화된 검색 알고리즘을 내세우긴 하지만, 결국 내부적으로는 코사인 유사도(Cosine Similarity)를 기반으로 동작하기 때문에 결과 품질은 대동소이하다.&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;대규모 분산 환경이라면 샤딩이나 복제 같은 인프라 이슈 때문에 DB 선택이 중요하겠지만, 로컬 단일 환경에서는 DB의 기능적 차이보다는 얼마나 가볍게 구동되는지가 더 관건이다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3&quot;&gt;4. 질의 강화 (Query Augmentation)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;보통 사용자의 질문은 그대로 검색에 사용할 수 없는 경우가 대부분이다. 질문이 너무 짧거나, &quot;그거&quot;, &quot;지난번 문서&quot; 같이 대명사를 남발하여 모호하기 때문이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;따라서 LLM을 통해 질문의 의도를 명확히 하거나, 이전 대화 맥락을 포함하여 검색하기 좋은 완전한 문장으로 재구성해주는 과정이 필수적이다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;4&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;4&quot;&gt;5. HyDE 및 구조적 키워드 추출&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;이것들도 사실상 넓은 범위의 질의 강화에 속한다. 단순 벡터 검색만으로는 부족한 부분을 채우기 위한 전략들이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;HyDE는 질문에 대한 가상 답변을 생성해 의미적 유사도를 좁히는 방식이고, 구조적 키워드 추출은 질문에서 날짜, 고유명사 같은 메타데이터를 뽑아내 하이브리드 검색(필터링)을 하기 위함이다. 특히 정확도가 생명인 비즈니스 도메인에서는 키워드 필터링이 없으면 원하는 문서를 찾기 힘들다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;6. 리랭커 (Reranker)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;1차 검색으로 가져온 문서들의 순위를 다시 매기는, 일종의 '최종 채점관'이다. 벡터 검색이 속도를 위해 정확도를 일부 희생했다면, 리랭커는 속도는 좀 느려도 문맥을 꼼꼼히 따져 진짜 정답을 상단으로 올린다. 다만 온디바이스 환경에서는 리랭커 자체가 연산 비용(Latency)을 잡아먹는 주범이 될 수 있다. 그래서 무거운 모델을 쓰기보단 아주 가벼운 Cross-Encoder를 쓰거나, 리랭킹할 후보 문서의 개수를 적절히 타협하는 것이 중요하다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;7. 모델의 양자화 (Quantization) &lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;앞서 언급한 과정들을 통해 아무리 좋은 문서를 찾아와도, &lt;b&gt;결국 사용자와 대화하는 건 LLM&lt;/b&gt;이다. 하지만 온디바이스 기기에서 수 기가바이트가 넘는 모델을 원본 그대로 띄우는 건 사실상 불가능하다. 메모리가 터져나가거나, 답변 한 줄 듣는데 세월아 네월아 걸릴 테니까.&lt;/p&gt;
&lt;p data-path-to-node=&quot;2&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;3&quot;&gt;양자화&lt;/b&gt;는 선택이 아니라 생존을 위한 필수 조건이다. 모델의 가중치를 압축해 용량과 연산량을 줄여야 한다. 문제는 &quot;얼마나 압축할 것인가&quot;다. 너무 줄이면 모델이 멍청해지기 때문에, 기기 스펙에 딱 맞는 최적의 경량화 지점을 찾는 것이 중요하다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;2&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;지금 위에 정리한 내용만 다시 훑어봐도, 시스템 하나를 위해 띄워야 할 LLM과 딥러닝 모델이 대체 몇 개인지 모르겠다. 문서 분석 모델, 임베딩 모델, 질의 생성을 위한 sLLM, 그리고 리랭커까지...&lt;/p&gt;
&lt;p data-path-to-node=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;과연 이 무거운 파이프라인이 한정된 리소스의 온디바이스 환경에서 원활하게 돌아갈 수 있을지 솔직히 의문이 든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;제약이 심할수록 해결했을 때의 성취감도 클 테니, 일단 묵묵히 부딪혀 봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/AI</category>
      <category>rag</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/304</guid>
      <comments>https://akku-dev.tistory.com/304#entry304comment</comments>
      <pubDate>Sat, 31 Jan 2026 18:22:54 +0900</pubDate>
    </item>
    <item>
      <title>스프링 개발자의 FastAPI 1개월 실무 적응기</title>
      <link>https://akku-dev.tistory.com/303</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDwGbM/dJMcaiPEEPn/PnSLcalcCOEoUDCSwWR3QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDwGbM/dJMcaiPEEPn/PnSLcalcCOEoUDCSwWR3QK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDwGbM/dJMcaiPEEPn/PnSLcalcCOEoUDCSwWR3QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDwGbM%2FdJMcaiPEEPn%2FPnSLcalcCOEoUDCSwWR3QK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;559&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;AI 솔루션 개발팀으로 이동하게 되면서 개발 환경에 큰 변화가 생겼다. 기존 클라우드 환경에서 &lt;b data-index-in-node=&quot;53&quot; data-path-to-node=&quot;4&quot;&gt;온디바이스(On-Device)&lt;/b&gt; 타겟으로 작업 영역이 변경되었고, 이에 맞춰 언어는 Python 네이티브로, 백엔드 프레임워크는 &lt;b data-index-in-node=&quot;124&quot; data-path-to-node=&quot;4&quot;&gt;FastAPI&lt;/b&gt;로 통일되었다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 4~5년간 사용했던 Spring Boot 환경에서 넘어와 약 한 달간 FastAPI를 실무에서 사용해보며 체감했던 장단점을 정리해보려 한다. 가장 크게 다가왔던 차이점들을 키워드로 꼽자면 다음과 같다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;장점:&lt;/b&gt; 핫리로드(Hot Reload), Pydantic, 비동기 제어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;단점:&lt;/b&gt; 의존성 주입(DI)과 객체 생명주기 관리의 어려움, 명시적 트랜잭션 관리, 싱글톤 정의의 모호함&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;이 글에서는 위 내용들을 하나씩 상세하게 비교해 보겠다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;장점 1. 핫리로드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 피부로 와닿은 장점은 개발 생산성과 직결되는 &lt;b data-index-in-node=&quot;31&quot; data-path-to-node=&quot;12&quot;&gt;핫리로드&lt;/b&gt;다.&lt;/p&gt;
&lt;pre id=&quot;code_1769841040042&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uvicorn main:app --reload --host 0.0.0.0 --port 8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;FastAPI는 서버 실행 시 --reload 명령어 한 줄만 추가하면 된다. 코드를 수정하고 저장하는 즉시 변경 사항이 서버에 반영되므로, 변경점을 테스트하고 피드백을 확인하는 속도가 매우 빠르다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;물론 Spring에도 DevTools 등을 활용한 비슷한 설정이 존재한다. 하지만 체감상 Spring의 재시작 속도는 FastAPI에 비해 현저히 느리다. 또한, (IntelliJ 설정 문제일 수도 있겠지만) Spring 환경에서는 빌드가 실패했을 때 스스로 재시작을 반복하며 계속 실패 로그를 띄우는 경우가 종종 있었다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;반면, FastAPI는 내가 &lt;b&gt;'파일을 저장하는 시점'&lt;/b&gt;에만 깔끔하게 재시작된다는 점에서 개발 경험(DX)이 훨씬 쾌적했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;장점&amp;nbsp;&lt;/b&gt;&lt;b&gt;2. Pydantic&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;Spring을 사용할 때 우리는 클라이언트의 요청 데이터를 받기 위해 DTO(Data Transfer Object)를 만들고, Lombok(@Data or @Getter)을 붙이고, 유효성 검증을 위해 Hibernate Validator(@NotNull, @Size 등) 어노테이션을 덕지덕지 붙여야 했다. 그리고 컨트롤러에서는 @Valid 어노테이션을 잊지 말아야 했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;FastAPI의 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;5&quot;&gt;Pydantic&lt;/b&gt;은 이 모든 과정을 파이썬의 타입 힌트 문법 하나로 통합해서 해결해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1769843852533&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float
    is_offer: bool = None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic의 장점을 정리하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot;&gt;1. 별도의 검증 로직을 짤 필요가 없다.&lt;/b&gt; 위 코드처럼 타입을 명시해 두면, 요청 들어온 데이터의 타입이 맞지 않을 때(예: price에 문자열을 넣는 등) 알아서 422 Unprocessable Entity 에러를 뱉어준다. Spring에서 BindingResult를 핸들링하던 것에 비하면 엄청나게 직관적이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;2. 보일러플레이트 객체를 생성할 필요가 없다. &lt;/b&gt;Java/Kotlin에서 DTO를 만들 때마다 작성해야 했던 Getter, Setter, 생성자, 그리고 JSON 파싱을 위한 Jackson 설정들이 Pydantic 모델 하나로 깔끔하게 정리된다. 직렬화/역직렬화(Serialization/Deserialization)가 물 흐르듯 자연스럽다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9&quot;&gt;3. Swagger(OpenAPI) 자동화&lt;/b&gt; 가장 감동적인 부분이다. Pydantic으로 모델을 정의하면, 이것이 곧바로 Swagger UI의 스키마 문서로 변환된다. API 문서를 위해 별도의 어노테이션을 달거나 테스트 코드를 짜지 않아도, 내가 작성한 코드가 곧 살아있는 문서가 된다. Spring에서 Swagger 를 만들려고 중복으로 작성해야했던 인터페이스, 어노테이션 지옥에서 빠져 나올 수 있다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;그리고 Spring에서는 Snake Case 전달된 JSON 필드를 Camel Case로 치환하려면 @JsonProperty나 NamingStrategy 설정을 해야 했는데, Pydantic은 alias 설정 등으로 더 유연하게 느껴졌다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;장점 3. 직관적인 비동기 제어&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;Spring에서도 @Async나 CompletableFuture를 사용하면 비동기 처리가 가능하다. 하지만 이를 제대로 쓰려면 별도의 스레드 설정을 신경 써야 하거나, 리턴값을 다루기 위해 .thenApply() 같은 메서드 체이닝을 사용해야 해서 코드가 조금 무거워지는 느낌이 든다. (마치 멀티스레드 프로그래밍을 직접 하는 기분이 든달까?)&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5&quot;&gt;FastAPI는 훨씬 가볍고 직관적이다.&lt;/b&gt;&lt;/p&gt;
&lt;div data-ved=&quot;0CAAQhtANahgKEwjf24vUl7WSAxUAAAAAHQAAAAAQ6AE&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1769844356289&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/&quot;)
async def read_results():
    results = await some_library()
    return results&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 비동기 로직임에도 불구하고, 코드가 위에서 아래로 순서대로 읽힌다. 복잡한 설정 없이 async와 await 키워드만 붙이면 끝이다. &lt;/span&gt;Spring이 &quot;비동기 기능을 탑재해서 쓰는&quot; 느낌이라면, FastAPI는 &quot;언어 자체가 비동기를 품고 있는&quot; 느낌이라 훨씬 가볍게 다가왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;3&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;단점 1. 의존성 주입(DI)과 객체 생명주기 관리의 어려움&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;Spring 개발자에게 &lt;b data-index-in-node=&quot;13&quot; data-path-to-node=&quot;4&quot;&gt;IoC(제어의 역전) 컨테이너&lt;/b&gt;는 기본적으로 제공받는 기능이다. @Component나 @Service 어노테이션만 붙여두면, Spring이 알아서 객체를 생성하고, 싱글톤으로 관리하며, 필요한 곳에 @Autowired로 척척 주입해준다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;하지만 FastAPI로 넘어오면서 이 마법이 사라졌다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,0,0&quot;&gt;'알아서' 해주지 않는다:&lt;/b&gt; FastAPI의 Depends는 강력하지만, Spring의 컴포넌트 스캔처럼 프로젝트 전체의 빈(Bean)을 자동으로 관리해주진 않는다. 개발자가 명시적으로 의존 관계를 정의해야 한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6,1,0&quot;&gt;계층 간 주입의 번거로움:&lt;/b&gt; Controller(Router) 단에서는 Depends로 주입받기가 쉽지만, Service 계층이나 그 하위 계층으로 내려갈수록 의존성 전파가 번거로워진다. Spring에서는 어디서든 @RequiredArgsConstructor 하나면 해결되었던 일들이, FastAPI에서는 파라미터로 계속 넘겨주거나 별도의 DI 패턴(또는 라이브러리)을 고민해야 했다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;아래와 같이 복잡하게 구성되는 걸 어떻게 처리해야할지 아직도 고민이다.&lt;/p&gt;
&lt;pre id=&quot;code_1769845084234&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# dependencies.py
# Spring의 IoC 컨테이너가 없어서 직접 하나하나 조립해야 하는 상황

from fastapi import Depends, Request

# 1. Global State나 Request에서 가져오는 기초 객체들
def get_external_api_client(request: Request) -&amp;gt; ExternalApiClient:
    return request.app.state.external_api_client

def get_redis_cache(request: Request) -&amp;gt; RedisCache:
    return request.app.state.redis

def get_db_session(request: Request) -&amp;gt; Session:
    return request.app.state.db_session

# 2. Repository 계층 (DB 세션 등을 주입)
def get_user_repository(session: Session = Depends(get_db_session)) -&amp;gt; UserRepository:
    return UserRepository(session)

def get_audit_log_repository(session: Session = Depends(get_db_session)) -&amp;gt; AuditLogRepository:
    return AuditLogRepository(session)

def get_config_repository() -&amp;gt; ConfigRepository:
    return ConfigRepository()

# 3. Service 계층 (Repository와 다른 Service들을 주입 - 여기가 가장 복잡해짐)
def get_notification_service(
    client: ExternalApiClient = Depends(get_external_api_client)
) -&amp;gt; NotificationService:
    return NotificationService(client)

def get_policy_validator(
    config_repo: ConfigRepository = Depends(get_config_repository)
) -&amp;gt; PolicyValidator:
    return PolicyValidator(config_repo=config_repo)

# 의존성이 꼬리에 꼬리를 무는 메인 비즈니스 로직
# Spring: 그냥 @RequiredArgsConstructor 끝
# FastAPI: 파라미터에 Depends를 줄줄이 명시해야 함
def get_complex_business_service(
    user_repo: UserRepository = Depends(get_user_repository),
    audit_repo: AuditLogRepository = Depends(get_audit_log_repository),
    noti_service: NotificationService = Depends(get_notification_service),
    validator: PolicyValidator = Depends(get_policy_validator),
    cache: RedisCache = Depends(get_redis_cache)
) -&amp;gt; ComplexBusinessService:
    return ComplexBusinessService(
        user_repo=user_repo,
        audit_repo=audit_repo,
        noti_service=noti_service,
        validator=validator,
        cache=cache
    )&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;단점 2. 명시적 트랜잭션 관리 (@Transactional의 부재)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;Spring JPA를 쓸 때 가장 그리운 기능 1순위는 바로 @Transactional이다. 메서드 위에 이 어노테이션 하나만 붙이면 트랜잭션 시작, 커밋, 예외 발생 시 롤백까지 AOP가 알아서 처리해준다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;FastAPI(주로 SQLAlchemy 사용 시)에서는 이 모든 것이 &lt;b&gt;명시적(Explicit)이어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1769844779624&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def create_item(db: Session, item: ItemCreate):
    try:
        db_item = models.Item(**item.dict())
        db.add(db_item)
        db.commit()  # 개발자가 직접 커밋
        db.refresh(db_item)
        return db_item
    except Exception:
        db.rollback() # 예외 처리도 직접 신경써야 함
        raise&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 Dependency의 yield를 활용해 세션의 생명주기를 관리하긴 하지만, 비즈니스 로직 중간에 트랜잭션 범위를 설정하거나 전파 레벨(Propagation)을 조정하는 등의 고급 기능을 구현하려면 코드가 꽤나 지저분해진다. &quot;혹시 내가 commit을 빼먹었나?&quot; 하는 불안감은 덤이다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;단점 3. 싱글톤 정의의 모호함 (feat. Global Variable)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;AI 모델과 같이 무거운 리소스는 애플리케이션 실행 시 딱 한 번만 로딩해서 메모리에 상주시켜야 한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;Spring:&lt;/b&gt; @Bean으로 등록하면 기본이 싱글톤(Singleton)이다. @PostConstruct로 초기화 로직을 넣기도 아주 편하다. 체계적이고 안정적이다. @Configuration으로 용도에 맞게 정의도 가능하다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;FastAPI:&lt;/b&gt; Python 모듈 자체가 싱글톤처럼 동작하긴 하지만, 명시적인 아키텍처 가이드가 부족하다. 결국 global 변수를 선언하거나, app.state에 우겨넣거나, @lru_cache 같은 데코레이터를 활용하는 등 여러 방법이 혼재된다. 어떤 것을 써도 나중에 의존성 주입 과정이 불편해진다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;기능상으로는 문제가 없지만, Spring의 정형화된 빈 관리 방식에 익숙한 눈으로 보면 어딘가 모르게 구조가 헐거워 보이고 &quot;이렇게 전역 변수처럼 써도 되나?&quot; 하는 불안감이 든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;약 한 달간의 FastAPI를 사용한 경험을 요약하자면, &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;18&quot;&gt;Spring은 행동을 강제하고, FastAPI는 자유도를 준다&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;대규모 엔터프라이즈 시스템이나 복잡한 비즈니스 로직, 그리고 견고한 트랜잭션 관리가 필수적인 도메인이라면 여전히 &lt;b data-index-in-node=&quot;70&quot; data-path-to-node=&quot;19&quot;&gt;Spring Boot&lt;/b&gt;가 정답일 것이다. 프로젝트에 동원되는 팀원 수가 많고, 팀원의 변경이 잦아도 코드 베이스 유지를 위해서 Spring이 유리 할 것 같다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;하지만 현재 우리 팀처럼 &lt;b data-index-in-node=&quot;14&quot; data-path-to-node=&quot;20&quot;&gt;AI 모델 서빙&lt;/b&gt;이 주 목적이고, 가벼운 리소스(온디바이스) 환경이 중요하기 때문에, &lt;b data-index-in-node=&quot;68&quot; data-path-to-node=&quot;20&quot;&gt;FastAPI&lt;/b&gt;는 대체 불가능할 것 같다. 스프링의 무거움과 복잡한 설정을 걷어내고 개발 자체에 집중하게 해주는 그 '경쾌함'은 확실히 매력적이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;프레임워크와 개발환경 선택에 있어서 중요한 건 &lt;b&gt;현재 프로젝트의 목적에 무엇이 더 적합한가&lt;/b&gt;일 것이다. Spring 개발자로서 해볼 수 있는 걸 거의 다해본 시점에서, FastAPI로의 기술 전환은 꽤 즐거운 경험이었다.&lt;/p&gt;</description>
      <category>개발/SPRING</category>
      <category>FastAPI</category>
      <category>java</category>
      <category>python</category>
      <category>springboot</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/303</guid>
      <comments>https://akku-dev.tistory.com/303#entry303comment</comments>
      <pubDate>Sat, 31 Jan 2026 16:51:33 +0900</pubDate>
    </item>
    <item>
      <title>달러를 너무 사고 싶지만, 정부가 개입할 때는 조심해야지</title>
      <link>https://akku-dev.tistory.com/302</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxi6Q8/dJMcahXpqsK/Uxz23J7j9otKh1mkd7kTX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxi6Q8/dJMcahXpqsK/Uxz23J7j9otKh1mkd7kTX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxi6Q8/dJMcahXpqsK/Uxz23J7j9otKh1mkd7kTX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxi6Q8%2FdJMcahXpqsK%2FUxz23J7j9otKh1mkd7kTX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;559&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이건 환율에 정부가 개입할 때, 어떤 일이 나타나는지 그리고 스테이블 코인의 기회를 기억하기 위해 작성한 글이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;226&quot; data-start=&quot;151&quot; data-ke-size=&quot;size16&quot;&gt;나는 올해 투자를 시작하면서 달러를 사야겠다고 생각했다. 달러를 단기 매매가 아니라 &lt;b&gt;자산의 일부&lt;/b&gt;로 가져가고 싶었기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;284&quot; data-start=&quot;228&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;284&quot; data-start=&quot;228&quot; data-ke-size=&quot;size16&quot;&gt;하지만 내가 &amp;ldquo;이제 달러를 좀 사야겠다&amp;rdquo;라고 생각했을 때부터 원/달러 환율은 쉬지 않고 올라갔다.&lt;/p&gt;
&lt;p data-end=&quot;284&quot; data-start=&quot;228&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;401&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPNs3D/dJMcaivboDz/fXQzSFtWphhM3sxkXiOk8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPNs3D/dJMcaivboDz/fXQzSFtWphhM3sxkXiOk8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPNs3D/dJMcaivboDz/fXQzSFtWphhM3sxkXiOk8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPNs3D%2FdJMcaivboDz%2FfXQzSFtWphhM3sxkXiOk8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;564&quot; height=&quot;401&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;401&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 투자를&lt;b&gt; 5월쯤부터 시작&lt;/b&gt; 생각했고 달러를 매달 조금씩 사고 있었는데... 1400원 밑에서 사겠단 고집을 못 꺾어 날아가는걸 구경만했다. 달러 인덱스는 낮아지는 상황이라 곧 내려오겠거니했지만 상승세가 멈추지 않았다. 환율이 계속해서 오르자&lt;b&gt; 한국은행과 정부의 구두 개입을 몇 번 시도&lt;/b&gt;했지만 크게 효과는 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b data-index-in-node=&quot;54&quot; data-path-to-node=&quot;15&quot;&gt;1480원&lt;/b&gt;을 기점으로 분위기가 바뀌었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBiSlZ/dJMcadHpZAH/BWFlNLklmdGhbJFAJiK5K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBiSlZ/dJMcadHpZAH/BWFlNLklmdGhbJFAJiK5K0/img.png&quot; data-alt=&quot;1480원 위로 올라가면 뿅망치를 쳤다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBiSlZ/dJMcadHpZAH/BWFlNLklmdGhbJFAJiK5K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBiSlZ%2FdJMcadHpZAH%2FBWFlNLklmdGhbJFAJiK5K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;399&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1480원 위로 올라가면 뿅망치를 쳤다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정부는 1480원 위로 올라가면 여지없이 '뿅망치'를 들었다. 전날 방어전을 펼치며 견적을 낸 건지, 다음날은 작정한 듯 칼을 빼 들었다. 정부가 개입하면 개인이 손쓰기 힘들 정도로 밀어버릴 것이라 생각했는데, 실제로 그랬다.&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;장중 1485원을 찍었던 환율은 순식간에 며칠만에 1430원까지 밀렸다. 지금은 다시 1440원대로 회복했지만, 여전히 달러 인덱스 대비 높은 수준이라 정부의 추가 개입 가능성은 열려 있다고 본다. 나는 그 변동성을 참지 못하고 1460원에 매수를 했는데... 결과적으로 타이밍이 아쉽게 됐다.&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;b&gt;정부가 한다고 하는 것의 반대방향으로 배팅하지 말라&lt;/b&gt;는과 &lt;b&gt;본인이 좋게 보이는 자산은 그냥 꾸준히 사는게 가장 낫다는 것&lt;/b&gt;이다. 직장인이 업무 시간에 움직이는 주식을 좋은 타이밍에 잡기란 너무 어렵고, 너무 크게 배팅하게 되면 변동성을 견디기가 어렵다. 결국 가격을 맞추려 하기보다 &lt;b&gt;시간을 정해 두고 분할해서 꾸준히 사는 방식이 가장 현실적&lt;/b&gt;이라는 걸 몸으로 배우게 됐다.&lt;/p&gt;
&lt;p data-end=&quot;1301&quot; data-start=&quot;1201&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1301&quot; data-start=&quot;1201&quot; data-ke-size=&quot;size16&quot;&gt;그렇지만 원달러 환율 쪽은 정부의 말을 모두 믿기는 어려울 것 같다. 단기적인 방향성에서는 맞는 말이지만, 길게 보면 원/달러 환경이 그리 단순해 보이진 않기 때문이다. 시간이 지나면 달러 기준금리가 내려가면서 환율도 천천히 내려올 수 있겠지만, 시장 금리와 글로벌 자금 흐름을 보면 그 과정이 순탄할 것 같다는 생각은 들지 않는다.&lt;/p&gt;
&lt;p data-end=&quot;1301&quot; data-start=&quot;1201&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1301&quot; data-start=&quot;1201&quot; data-ke-size=&quot;size16&quot;&gt;펀더멘탈보다는 쏠림현상, 그리고 정책에 따라 크게크게 움직이기 때문에&amp;nbsp; &lt;b&gt;환율을 맞추려는 시도 자체가 어려운 투자&lt;/b&gt;라는 것도 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1515&quot; data-start=&quot;1456&quot; data-ke-size=&quot;size16&quot;&gt;그리고 개인적으로 가장 아쉽게 느껴지는 부분은 진짜 기회는 &lt;b&gt;스테이블 코인 쪽에 있었다는 점&lt;/b&gt;이다. 달러를 자산으로 보유하고 싶었다면 올해는 이 시장에 꽤 좋은 기회가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lZ43y/dJMcajt40Uw/ohn3PmPMwgcn6VyutFWykk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lZ43y/dJMcajt40Uw/ohn3PmPMwgcn6VyutFWykk/img.png&quot; data-alt=&quot;https://www.digitalasset.works/news/articleView.html?idxno=29004&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lZ43y/dJMcajt40Uw/ohn3PmPMwgcn6VyutFWykk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlZ43y%2FdJMcajt40Uw%2Fohn3PmPMwgcn6VyutFWykk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;338&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.digitalasset.works/news/articleView.html?idxno=29004&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;비트코인이 급등하는 동안 국내 코인 시장엔 역프리미엄(글로벌 시세보다 저렴한 상태)이 붙어 있었고, 이 기간은 원/달러 환율이 낮았던 시기와 겹친다. 즉, 더 싼 가격에 달러(USDT)를 살 수 있었던 것이다. 심지어 USDC 예치 이벤트로 연 9% 이자를 주던 시기도 있었고, 2026년 1월 4일 지금도 USDT 예치 이자율은 3.86%에 달한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;물론 거래소 리스크나 디페깅 같은 위험이 있어 쉽게 선택하기 어려운 수단이었던 것도 사실이다. 그럼에도 불구하고 단순한 달러 예금보다 훨씬 높은 이자 수익과 환차익 기회가 동시에 존재했던 시장이었다는 점에서&amp;nbsp;적극적으로 나서지 못한 게 아쉬움이 남는다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;다음에 비슷한 상황이 온다면, 환율을 맞추려 애쓰기보다는 &lt;b&gt;조금 더 적극성을 갖고, 꾸준히 사는 방식&lt;/b&gt;으로 접근할 것 같다.&lt;/p&gt;</description>
      <category>일상/경제?</category>
      <category>스테이블코인</category>
      <category>환율</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/302</guid>
      <comments>https://akku-dev.tistory.com/302#entry302comment</comments>
      <pubDate>Sun, 4 Jan 2026 20:05:06 +0900</pubDate>
    </item>
    <item>
      <title>BDC(Business Development Company)에서 고배당을 받아보자</title>
      <link>https://akku-dev.tistory.com/301</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cajBkD/dJMcaiaUg6B/R985gvr3j2yuWu1nWJmNkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cajBkD/dJMcaiaUg6B/R985gvr3j2yuWu1nWJmNkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cajBkD/dJMcaiaUg6B/R985gvr3j2yuWu1nWJmNkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcajBkD%2FdJMcaiaUg6B%2FR985gvr3j2yuWu1nWJmNkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;559&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;559&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지독하게 몇년간 적금 예금만 하다 올해 6월 첫 투자 포트폴리오 설계했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://akku-dev.tistory.com/280&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.06.20 - 나의 첫 포트폴리오 설계 이야기 : 2025년 첫 시장 분석과 종목 분석&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjjfvX/dJMcabQnpiN/PiKEimwj7qC6kuz2PnaKNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjjfvX/dJMcabQnpiN/PiKEimwj7qC6kuz2PnaKNK/img.png&quot; data-alt=&quot;국장 위주긴 한데... 차라리 삼전을 넣던가 하지;;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjjfvX/dJMcabQnpiN/PiKEimwj7qC6kuz2PnaKNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjjfvX%2FdJMcabQnpiN%2FPiKEimwj7qC6kuz2PnaKNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;805&quot; height=&quot;247&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;국장 위주긴 한데... 차라리 삼전을 넣던가 하지;;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 이야기하면, 위에 작성된 대로는 극소량만 투자했다. 다 합쳐서 1~2월급치 정도?&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;b&gt;커버드콜에 대한 불신이 생긴 것&lt;/b&gt;과 &lt;b&gt;미국장기채의 움직임&lt;/b&gt;이 이상해서였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커버드콜의 &lt;b&gt;분배금을 재투자해야만 기초지수를 따라가는 구조&lt;/b&gt;는 매매를 힘들어 하는 내가 견디기 힘들었고, 미국 장기채는 연준이 금리를 총 세번 내리는 동안 가격이 오르지 않았다. 결국 투자 방식을 바꿀 수 밖에 없었는데...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 원하는 고배당을 받으려면, &lt;b&gt;미장으로 넘어가자는 결론&lt;/b&gt;을 내렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것 저것 찾다보니 미장에는 BDC(Business Development Company, 기업개발회사)라는게 있었다. 이 BDC는 내가 찾던 고배당에 가장 적합한 투자 방식 중 하나가 되주리라 생각해서 다뤄보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. BDC(Business Development Company)란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게&amp;nbsp;말하면&amp;nbsp;&lt;b&gt;'상장된&amp;nbsp;'미니&amp;nbsp;은행'&lt;/b&gt;이다. &lt;br /&gt;&lt;br /&gt;우리가&amp;nbsp;보통&amp;nbsp;주식을&amp;nbsp;살&amp;nbsp;때&amp;nbsp;애플이나&amp;nbsp;삼성전자처럼&amp;nbsp;물건&amp;nbsp;만들어&amp;nbsp;파는&amp;nbsp;회사를&amp;nbsp;고르지만,&amp;nbsp;BDC는&amp;nbsp;좀&amp;nbsp;다르다.&amp;nbsp;얘네는&amp;nbsp;&lt;b&gt;'돈&amp;nbsp;장사'&lt;/b&gt;를&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgDvtg/dJMcafL0XCA/wkdsOVqc1YtLjgwl6u9sK0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgDvtg/dJMcafL0XCA/wkdsOVqc1YtLjgwl6u9sK0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgDvtg/dJMcafL0XCA/wkdsOVqc1YtLjgwl6u9sK0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgDvtg%2FdJMcafL0XCA%2FwkdsOVqc1YtLjgwl6u9sK0%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;455&quot; height=&quot;255&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈의 흐름은 단순하다 &lt;br /&gt;&lt;br /&gt;1.&amp;nbsp;투자자들이&amp;nbsp;BDC&amp;nbsp;주식을&amp;nbsp;산다. &lt;br /&gt;2.&amp;nbsp;BDC는&amp;nbsp;그&amp;nbsp;돈을&amp;nbsp;모아&amp;nbsp;유망한&amp;nbsp;중소기업들(비상장사)에게&amp;nbsp;대출해&amp;nbsp;준다. &lt;br /&gt;3.&amp;nbsp;중소기업이&amp;nbsp;내는&amp;nbsp;짭짤한&amp;nbsp;이자를&amp;nbsp;받아서&amp;nbsp;우리한테&amp;nbsp;배당금으로&amp;nbsp;꽂아준다. &lt;br /&gt;&lt;br /&gt;이게&amp;nbsp;전부다.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;사모펀드나 대형 은행들이나 하던 '기업 대출' 비즈니스에 숟가락 얹을 수 있게 만든 시스템이다. BDC들은 &lt;b&gt;배당률이 연 8~11%&lt;/b&gt;씩 나오기 때문에 놀랄수 밖에 없는데, BDC를 운영하려면 미국 법을 따라야한다. 미국 정부는 BDC가 수익의 90% 이상을 주주에게 배당하면 법인세를 면제해 준다. 세금 낼 돈까지 털어서 주주들한테 다 퍼줘야 자기들도 이득인 구조라, 고배당이 나올 수밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 어떤 것들이 있을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&amp;nbsp;ARCC&amp;nbsp;(Ares&amp;nbsp;Capital)&amp;nbsp;:&amp;nbsp;내&amp;nbsp;픽&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BDC를 이야기하면 &lt;b&gt;아레스 캐피탈(ARCC)&lt;/b&gt;를 빼놓을 수 없다. 시가총액 기준 압도적 1위다. 덩치가 크다는 건 그만큼 빌려줄 돈도 많고, 관리하는 기업들도 많다는 뜻이다. &lt;br /&gt;&lt;br /&gt;2004년에 상장해서 2008년 금융위기, 코로나까지 다 얻어맞으면서도 살아남았다. 어느 정도 검증이 끝난 셈이다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;2.&amp;nbsp;MAIN&amp;nbsp;(Main&amp;nbsp;Street&amp;nbsp;Capital)&lt;/b&gt; &lt;br /&gt;보통 미국 주식은 3개월에 한 번 배당을 주는데, &lt;b&gt;메인 스트리트 캐피탈(MAIN)&lt;/b&gt;은 매달 준다. &lt;br /&gt;&lt;br /&gt;'월배당'이라는&amp;nbsp;점&amp;nbsp;때문에&amp;nbsp;한국&amp;nbsp;개미들에게&amp;nbsp;인기가&amp;nbsp;많다고&amp;nbsp;한다.&amp;nbsp;배당뿐만&amp;nbsp;아니라&amp;nbsp;주가&amp;nbsp;자체도&amp;nbsp;꽤&amp;nbsp;우상향하는&amp;nbsp;편이다.&amp;nbsp;BDC계의&amp;nbsp;'성장주'&amp;nbsp;느낌이라&amp;nbsp;인기가&amp;nbsp;많아서&amp;nbsp;다른&amp;nbsp;종목보다&amp;nbsp;조금&amp;nbsp;비싸게&amp;nbsp;거래되는&amp;nbsp;경향이&amp;nbsp;있다(배당률이&amp;nbsp;낮은&amp;nbsp;편)고&amp;nbsp;한다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;3.&amp;nbsp;OBDC&amp;nbsp;(Blue&amp;nbsp;Owl&amp;nbsp;Capital)&amp;nbsp;&lt;/b&gt; &lt;br /&gt;ARCC가 너무 덩치가 커서 부담스럽다면 그 다음으로 많이 보는 게 &lt;b&gt;블루 아울 캐피탈(OBDC)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OBDC의 특징은 포트폴리오가 굉장히 보수적이다. 돈을 빌려줄 때 '진짜 안전한 곳' 위주로 골라서 빌려준다는 평이 많다. &lt;br /&gt;&lt;br /&gt;상장한 지는 ARCC보다 짧지만, 실적이 꽤 꾸준해 믿을만 하다는 평가다. 대장주인 ARCC와 함께 섞어서 포트폴리오의 안정성을 높이는 용도로 많이들 쓴다. &lt;b&gt;12월 31일 배당락일에 살짝 내려와서 조금 집어봤다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 것들만 작성해뒀고, &lt;a href=&quot;https://namu.wiki/w/%EA%B8%B0%EC%97%85%EC%84%B1%EC%9E%A5%EC%A7%91%ED%95%A9%ED%88%AC%EC%9E%90%EA%B8%B0%EA%B5%AC#toc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다른 곳&lt;/a&gt;들도 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3.&amp;nbsp;위험하진&amp;nbsp;않을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적금 이자의 3~4배를 주는 데는 다 이유가 있다. 어느정도의 리스크를 지기 때문이다. BDC를 공부하면서 가장 경계했던 리스크는 두 가지다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;1.&amp;nbsp;기업이&amp;nbsp;무너질&amp;nbsp;때&amp;nbsp;:&amp;nbsp;부실&amp;nbsp;대출&amp;nbsp;리스크&amp;nbsp;&lt;/b&gt; &lt;br /&gt;BDC는&amp;nbsp;중소기업에&amp;nbsp;돈을&amp;nbsp;빌려준다.&amp;nbsp;경기가&amp;nbsp;박살&amp;nbsp;나서&amp;nbsp;이&amp;nbsp;기업들이&amp;nbsp;줄줄이&amp;nbsp;망하면?&amp;nbsp;당연히&amp;nbsp;내가&amp;nbsp;산&amp;nbsp;BDC&amp;nbsp;주가도&amp;nbsp;박살&amp;nbsp;나고&amp;nbsp;배당도&amp;nbsp;끊긴다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&amp;nbsp;우량한&amp;nbsp;운용사를&amp;nbsp;골라야&amp;nbsp;한다.&amp;nbsp;내가&amp;nbsp;언급한&amp;nbsp;대장주들은&amp;nbsp;대부분&amp;nbsp;'선순위&amp;nbsp;담보&amp;nbsp;대출'&amp;nbsp;비중이&amp;nbsp;높다.&amp;nbsp;회사가&amp;nbsp;망해도&amp;nbsp;남은&amp;nbsp;재산에서&amp;nbsp;내&amp;nbsp;돈을&amp;nbsp;1순위로&amp;nbsp;챙겨올&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;'우선권'을&amp;nbsp;가졌는지&amp;nbsp;확인하는&amp;nbsp;게&amp;nbsp;필수다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;2.&amp;nbsp;금리가&amp;nbsp;너무&amp;nbsp;빨리&amp;nbsp;내려갈&amp;nbsp;때&amp;nbsp;:&amp;nbsp;수익성&amp;nbsp;리스크&amp;nbsp;&lt;/b&gt; &lt;br /&gt;BDC는&amp;nbsp;보통&amp;nbsp;변동금리로&amp;nbsp;돈을&amp;nbsp;빌려준다.&amp;nbsp;금리가&amp;nbsp;높을&amp;nbsp;땐&amp;nbsp;이자를&amp;nbsp;많이&amp;nbsp;받아서&amp;nbsp;좋은데,&amp;nbsp;금리가&amp;nbsp;바닥까지&amp;nbsp;수직&amp;nbsp;낙하하면&amp;nbsp;얘네&amp;nbsp;수익도&amp;nbsp;줄어든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만&amp;nbsp;금리는&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;0%가&amp;nbsp;되지&amp;nbsp;않는다.&amp;nbsp;오히려&amp;nbsp;금리가&amp;nbsp;천천히&amp;nbsp;내려오는&amp;nbsp;구간에서는&amp;nbsp;BDC가&amp;nbsp;받는&amp;nbsp;이자&amp;nbsp;수익이&amp;nbsp;꽤&amp;nbsp;오랫동안&amp;nbsp;높게&amp;nbsp;유지되는&amp;nbsp;경향이&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 배당락일에 변동성이 엄청나게 크다. &lt;b&gt;크게는 -5% 이상 빠지는 경우&lt;/b&gt;도 몇몇 종목에서 보이니 잘 생각해서 매매에 나서야할 것 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;위험성이 없진 않지만, 움직이지 않을 현금을 담아둘 곳으로 BDC를 선택했다. 아마 내 포트폴리오에서 가장 큰 비중을 차지하게 될 것 같다. 특히 배당락 타이밍을 잘 잡으면 거의 10%대의 배당률을 줄 때가 있어서 더 매력적이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;26&quot; data-ke-size=&quot;size16&quot;&gt;대부분 BDC의 모회사들이 튼튼해서 괜찮다고 생각하지만, 고배당을 주니 무서운 건 어쩔 수 없다. 특히 금리 인하가 예견된 지금은 인기가 없을 수밖에 없을 것 같기도 하다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;26&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;27&quot; data-ke-size=&quot;size16&quot;&gt;하지만 나는 그 틈을 노리기로 했다. 금리가 내려가도 배당률이 일반 은행 금리보다는 훨씬 높게 잡힐 테니, 지독하게 적금만 하던 내게는 이만한 대안이 없다.&lt;/p&gt;</description>
      <category>일상/경제?</category>
      <category>ARCC</category>
      <category>BDC</category>
      <category>main</category>
      <category>OBDC</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/301</guid>
      <comments>https://akku-dev.tistory.com/301#entry301comment</comments>
      <pubDate>Wed, 31 Dec 2025 20:16:05 +0900</pubDate>
    </item>
    <item>
      <title>2025년 목표 결산과 회고</title>
      <link>https://akku-dev.tistory.com/300</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cN9tPL/dJMcadUUKh1/YonlqGey5f1JXScf8nuoP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cN9tPL/dJMcadUUKh1/YonlqGey5f1JXScf8nuoP0/img.png&quot; data-alt=&quot;GPT보다 제미나이가 좋은 것 같다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cN9tPL/dJMcadUUKh1/YonlqGey5f1JXScf8nuoP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcN9tPL%2FdJMcadUUKh1%2FYonlqGey5f1JXScf8nuoP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;1024&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GPT보다 제미나이가 좋은 것 같다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;이룬 것과 이루지 못한 것&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년도 작년과 동일하게 10개의 목표를 잡았었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이직, 연애&lt;br /&gt;독서 (분기별 1권), 프로젝트 결과물 (일로아)&lt;br /&gt;감정 섞인 말 하지 않기, 오픈소스 기여&lt;br /&gt;체지방 10%대 / 68kg, 멘토링 및 커피챗&lt;br /&gt;AI와 DB 공부, 투자 공부&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;올해는 중순부터 회사와 팀에 매우 큰 변화가 생기면서, 계획했던 일들을 대부분 이루거나 시도조차 하지 못했다. 회사의 대규모 조직 개편으로 본사 이동과 더불어 3번의 팀 변경이 있었다. 너무 짧은 기간 내에 내 의사와 상관없이 팀이 바뀌면서 한동안 어떻게 해야 할지 스트레스를 많이 받았었다. 다행히 지금은 어느 정도 정리된 상태다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;6&quot;&gt;일단 해낸 것부터 정리해 보면,&lt;/b&gt; 독서랑 투자, AI 공부는 확실하게 했다. 오픈소스 기여도 현재 PR(Pull Request)만 만들어둔 상태인데, 금방 끝낼 수 있을 것 같으면서도 은근히 손이 많이 가다 보니 선뜻 마무리를 못 하고 있다. 그리고 하나를 더 꼽자면 '감정 섞인 말 하지 않기' 정도는 지켜낸 것 같다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7&quot;&gt;확실하게 하지 못한 건&lt;/b&gt; 이직, 연애, 외부 프로젝트 결과물 보기, DB 공부, 멘토링이다. 다이어트는 70kg 언저리까지 감량하며 눈바디도 좋아졌지만, 목표치에 다다르지 못했으니 실패에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;아쉬운 것&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 아쉬움은 역시 &lt;b data-index-in-node=&quot;13&quot; data-path-to-node=&quot;9&quot;&gt;이직&lt;/b&gt;이다. 회사의 복잡한 조직 개편을 예상했기에 내가 하고 싶은 일을 계속하지 못할 거라는 불안함이 있었다. 그래서 먼저 이곳저곳 알아봤지만 결과가 좋지 못했다. 주니어와 시니어 사이 경력의 애매함도 느꼈고, 스스로의 부족함도 알게 되었다. 그래도 무엇이 부족하고 어떤 식으로 준비해야 할지 배운 점은 있다. 며칠씩 걸리는 전형 기간이 늘 부담이지만, 아쉬운 사람은 나니까 어쩔 수 없다. 스텝업을 목표로 계속 도전해야겠다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;두 번째는 &lt;b&gt;전체적인 공부량이 줄었다는 것&lt;/b&gt;이다. 그러다보니 자연스럽게 블로그를 쓰는 빈도도 줄어들었다. 특히 필요로 했던&amp;nbsp;&lt;b data-index-in-node=&quot;5&quot; data-path-to-node=&quot;10&quot;&gt;DB 공부와 오픈소스 기여&lt;/b&gt; 쪽에 시간을 주지 못한게 아쉽다. 백엔드 개발자로서 이 쪽 분야에는 더 공부해야 할 것들이 있는데, 꾸준히 공부할 환경이 안 되다 보니 기억이 날 때마다 틈틈이 들여다봐야 할 것 같다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;연애 쪽은 늘 그렇듯 아쉽다. 인연을 만들기 위해 노력해야 하는데 참 잘 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;잘한 것&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;잦은 팀 변경으로 스트레스를 많이 받던 시기에, 당장 쓰지도 못할 공부보다는 실질적인 &lt;b data-index-in-node=&quot;48&quot; data-path-to-node=&quot;12&quot;&gt;경제와 금융 공부&lt;/b&gt;를 하기로 마음먹었다. 궁금한 게 생길 때마다 AI와 대화하고 유튜브와 책을 뒤지며 구루 투자자들의 이야기를 들었다. 결론부터 말하자면 별거 없었다. 너무 많은 정보가 오히려 투자에 독이 된다는 것도 느꼈다. 결과적으로 내 자산을 어떻게 관리해야겠다는 방향성을 확실히 잡은 것이 큰 수확이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;그리고 서두에도 언급했지만, 대화할 때 확실하게 &lt;b data-index-in-node=&quot;27&quot; data-path-to-node=&quot;13&quot;&gt;감정을 덜 섞게 됐다.&lt;/b&gt; 갓 팀장이 된 분과 계속해서 좋은 매니징에 대해 함께 고민하다 보니 나도 자연스럽게 성장하게 된 것 같다. 매니징이라는 분야가 굉장히 넓고 어렵지만, 이 과정을 통해 배운 게 정말 많다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;특히, 사람마다 필요한 매니징 방식이 다르다는 걸 알게된 게 컸던 것 같다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13&quot;&gt;독서&lt;/b&gt;도 꾸준히 잘 실천했다. 숏폼 영상이 넘쳐나는 시대에 혼자 조용히 생각하는 시간이 줄어들고 있는데, 이런 블로그 글쓰기와 독서가 마음의 양식이 되는 것 같다. 주로 개발 서적을 읽긴 하지만, 기회가 된다면 이런 시간을 계속 가져보려 한다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;앞으로의 목표&lt;/b&gt;&lt;/h2&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;올해는 안 좋은 쪽으로 이벤트가 많아서 작년에 비해 쓸 내용이 많지 않다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;그래도 올해는 이전과 다르게 진짜로 해보고 싶은 게 생겼는데, &lt;b data-index-in-node=&quot;63&quot; data-path-to-node=&quot;15&quot;&gt;금융 도메인&lt;/b&gt;에 대해 더 자세히 알아보고 싶다는 점이다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;뭐부터 해야 할지는 모르겠지만 그래도 내 전문 분야인 개발 쪽이랑 엮어보면 좋을 것 같다. 어떤 유튜버가 LLM을 이용한 금융 공학을 소개해주는데, 비슷하게 시도해 볼까 싶기도 하다. 한국식 포트폴리오 비주얼라이저를 만들어 보는 것도 재밌을 것 같다. 이것 저것 생각해보면, 할 수 있는 게 생각보다 많다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;17&quot; data-ke-size=&quot;size16&quot;&gt;그리고 앞으로는 목표를 5개 정도로 줄여서 더욱 구체적으로 잡아볼 생각이다. 그동안 매년 과할 정도로 많은 목표를 잡았는데, 이런 목표들이 나 자신에게 알게 모르게 스트레스를 주고 있었던 것 같다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;17&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;정리하고 보니 변명만 늘어놓은 것 같기도 하지만, 올해는 내가 진짜 하고 싶은 걸 찾은 것만으로도 충분히 큰 수확인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>일상/생각정리</category>
      <category>2025년 회고</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/300</guid>
      <comments>https://akku-dev.tistory.com/300#entry300comment</comments>
      <pubDate>Wed, 31 Dec 2025 17:59:39 +0900</pubDate>
    </item>
    <item>
      <title>비용절감을 위한 AWS EKS 버전업하기(with. karpenter)</title>
      <link>https://akku-dev.tistory.com/299</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brmYQX/dJMcaaqluCt/ZNsdFPTsDWS1o2fzm24XOk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brmYQX/dJMcaaqluCt/ZNsdFPTsDWS1o2fzm24XOk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brmYQX/dJMcaaqluCt/ZNsdFPTsDWS1o2fzm24XOk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrmYQX%2FdJMcaaqluCt%2FZNsdFPTsDWS1o2fzm24XOk%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;800&quot; height=&quot;420&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;269&quot; data-start=&quot;201&quot; data-ke-size=&quot;size16&quot;&gt;팀이 변경되면서 이전 인프라에 대한 부담을 가져갈 필요가 없다고 생각했는데, 다시 한 번 비용 절감 관련 문의가 들어왔다.&lt;/p&gt;
&lt;p data-end=&quot;269&quot; data-start=&quot;201&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;398&quot; data-start=&quot;271&quot; data-ke-size=&quot;size16&quot;&gt;이전 서비스의 운영 리소스를 줄이는 과정에서, 또다시 문의가 들어왔고 운영을 정말 가볍게 가져가려면 시간이 들더라도 EKS를 내려놓는 게 맞다는 의견을 냈지만, 이번에도 구조를 바꾸기보다는 &lt;b&gt;당장 효과를 볼 수 있는 선에서 정리하는 방향&lt;/b&gt;으로 결정됐다.&lt;/p&gt;
&lt;p data-end=&quot;398&quot; data-start=&quot;271&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;398&quot; data-start=&quot;271&quot; data-ke-size=&quot;size16&quot;&gt;결국 이번에도 땜빵에 가까운 선택이었다.&lt;/p&gt;
&lt;p data-end=&quot;430&quot; data-start=&quot;400&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;430&quot; data-start=&quot;400&quot; data-ke-size=&quot;size16&quot;&gt;그래서 이번에 진행하기로 한 작업은 다음 세 가지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진행하기로 한 작업들 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. RDS serverlss ACU 사이즈 줄이기&lt;/b&gt; &lt;br /&gt;- 2 - 8 ACU까지 사용하는데, 0.5 - 1 로 줄여보자&lt;br /&gt;&lt;b&gt;2. RDS reader 인스턴스 삭제&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- MAU가 많이 내려가서 DB reader 인스턴스가 필요 없어짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;3. EKS 추가지원 버전에 걸려있어서 버전 업데이트&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;- &lt;/span&gt;&lt;/span&gt;매달 360달러 절감 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;크게 어려운 작업은 없을 거라 판단해 하루 정도의 일정으로 잡았는데, 결과적으로는 1번부터 문제가 발생했고, 3번에서는 생각보다 큰 이슈를 맞닥뜨리게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. RDS serverlss ACU 사이즈 줄이기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;해당 DB는 Terraform으로 관리되고 있지 않아 콘솔에서 직접 작업을 진행했다. ACU max 값을 1로 낮춘 뒤&lt;b&gt;, 2번 작업을 마치고 서버를 배포하자마자 문제가 발생&lt;/b&gt;했다. 처음에는 DB 문제라고 바로 인식하지 못했는데, 로그를 차분히 확인하다 보니 서버 기동 시 수행되는 JDBC 커넥션 체크가 타임아웃되고 있었다. 그래서 RDS 콘솔을 확인해보니&lt;b&gt; DB 사용량이 99%까지 치솟아 있는 상태&lt;/b&gt;였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1463&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QkuYZ/dJMcadgfKns/SuWNkk7CegBuEueAzzAuO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QkuYZ/dJMcadgfKns/SuWNkk7CegBuEueAzzAuO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QkuYZ/dJMcadgfKns/SuWNkk7CegBuEueAzzAuO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQkuYZ%2FdJMcadgfKns%2FSuWNkk7CegBuEueAzzAuO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1463&quot; height=&quot;395&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1463&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 DB의 평균 ACU 사용량이 대략 3 정도라는 건 알고 있었지만, 반쯤은 테스트해보자는 생각으로 진행했던 작업이 이렇게 바로 장애로 이어질 줄은 예상하지 못했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyNxMB/dJMcabCMLxG/klkHwQculWH5r3WExFtaKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyNxMB/dJMcabCMLxG/klkHwQculWH5r3WExFtaKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyNxMB/dJMcabCMLxG/klkHwQculWH5r3WExFtaKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyNxMB%2FdJMcabCMLxG%2FklkHwQculWH5r3WExFtaKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;504&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결국 ACU max 값을 평균 사용량보다 여유 있게 4로 조정하고, 0.5&amp;ndash;4 범위로 설정해 작업을 마무리했다. 이 과정을 통해 DB의 ACU를 과도하게 줄였을 때 어떤 일이 발생하는지는 명확히 알 수 있었지만, 평균 사용량 자체가 변하지 않았기 때문에 비용 절감 효과는 사실상 없을 것이라는 결론에 도달했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. RDS reader 인스턴스 삭제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;이 작업은 생각보다 단순했다. 어렵게 생각한다면 서버 이중화를 전제로 작성된 일부 코드와 JDBC 설정을 정리해야 했지만, 기존 코드를 모두 삭제하고 기본적인 RDS 연결 방식을 기준으로 다시 구현하면서 Reader 인스턴스를 완전히 제거했다. 작업 자체는 오래 걸리지 않았지만, 1번 작업에서 발생한 장애로 인해 진행 중에는 꽤 당황했었다.&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 결과는 명확했다. 이 작업 하나로 월 약 $250~270 수준의 비용 절감 효과를 얻을 수 있었다. 크게 눈에 띄는 작업은 아니었지만, 이번 비용 절감 작업 중에서는 가장 확실한 성과였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. EKS 추가지원 버전 업데이트&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번작업이 이 글을 작성한 가장 큰 이유다. 큰 작업이라 생각하지 않았지만 가장 큰 문제를 일으킨 작업이었다.&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 style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://akku-dev.tistory.com/211&quot;&gt;[개발/AWS&amp;amp;인프라] - AWS EKS 버전 업데이트하기&lt;/a&gt; 에서 정리했던 방식과 동일하게 AWS 콘솔에서 클러스터 버전을 단계적으로 업데이트하고, 부가 기능들도 최신 버전으로 맞췄다.&lt;/p&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1926&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1926&quot; data-ke-size=&quot;size16&quot;&gt;문제는 바로 발생했는데, 콘솔에서 버전 업데이트를 마무리하고 1.28 버전으로 남아 있던 노드들을 삭제하고 재배포되길 기다렸지만, 노드가 다시 올라오지 않았다. 원인을 분석했지만 로그를 봐도 명확한 이유를 알 수 없었다.&lt;/p&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1926&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1926&quot; data-ke-size=&quot;size16&quot;&gt;문제는 뒤늦게 드러났다. EKS의 클러스터 버전은 올라갔지만, &lt;b&gt;노드 스케줄러로 사용하고 있는 Karpenter가 너무 구버전&lt;/b&gt;이었고 여전히&lt;b&gt; NodeTemplate과 Provisioner를 사용하는 구조&lt;/b&gt;였기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;결국 EKS 클러스터 버전에 맞는 CRD를 새로 주입하고, 기존 &lt;b&gt;NodeTemplate / Provisioner 기반 설정을 NodeClass / NodePool 구조로 다시 구성&lt;/b&gt;해야 했다.&lt;/p&gt;
&lt;h3 data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; Karpenter 업그레이드 시 주의할 점 &lt;/b&gt;&lt;/h3&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;가장 먼저 EKS 버전에 맞는 Karpenter 최소 버전을 확인해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxipiQ/dJMcaajzGjR/49XpaPqEgkoEKV6UjjUdh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxipiQ/dJMcaajzGjR/49XpaPqEgkoEKV6UjjUdh1/img.png&quot; data-alt=&quot;https://karpenter.sh/docs/upgrading/compatibility/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxipiQ/dJMcaajzGjR/49XpaPqEgkoEKV6UjjUdh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxipiQ%2FdJMcaajzGjR%2F49XpaPqEgkoEKV6UjjUdh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;661&quot; height=&quot;198&quot; data-origin-width=&quot;661&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://karpenter.sh/docs/upgrading/compatibility/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;Helm 설치를 사용하지 않고 컨트롤러만 업데이트하는 경우, YAML 적용 전에 CRD를 반드시 먼저 적용해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1766372707169&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v1.8.3은 eks 버전에 맞는 카펜터 버전
kubectl apply -f https://raw.githubusercontent.com/aws/karpenter-provider-aws/v1.8.3/pkg/apis/crds/karpenter.sh_nodepools.yaml
kubectl apply -f https://raw.githubusercontent.com/aws/karpenter-provider-aws/v1.8.3/pkg/apis/crds/karpenter.sh_nodeclaims.yaml
kubectl apply -f https://raw.githubusercontent.com/aws/karpenter-provider-aws/v1.8.3/pkg/apis/crds/karpenter.k8s.aws_ec2nodeclasses.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;이후 Karpenter 공식 문서를 기준으로 기존 설정을 옮기고, Terraform으로 설정을 재정비했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://karpenter.sh/docs/concepts/nodeclasses/&quot;&gt;https://karpenter.sh/docs/concepts/nodeclasses/&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://karpenter.sh/docs/concepts/nodepools/&quot;&gt;https://karpenter.sh/docs/concepts/nodepools/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;업데이트 순서 정리&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. EKS 클러스터 버전업 &lt;br /&gt;2. 쿠버네티스 Add-on(추가기능) 버전업 &lt;br /&gt;3. 카펜터 CRD 주입 &lt;br /&gt;4. 카펜터 컨트롤러 버전업 &lt;br /&gt;5. NodePool / EC2NodeClass YAML 적용 &lt;br /&gt;6. 노드 롤링 업데이트 (Cordon &amp;amp; Drain) &lt;br /&gt;7. 구버전 노드 및 자원 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3~5번 단계가 완료되기 전에는, 어떠한 노드 이벤트도 발생하지 않도록 주의해야 한다. 그리고 &lt;b&gt;각 버전에 맞는 설정들이 있으니 꼭 확인해야한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;추가로 알면 좋은 것들&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 신규 설치라면 훨씬 단순하다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EKS 1.34를 새로 설치하고 최신 Karpenter를 바로 구성한 경우에는, CRD를 별도로 주입하거나 YAML을 마이그레이션할 필요가 없다. Helm 설치 과정에서 필요한 CRD가 함께 설치되고, 구버전 리소스와의 충돌도 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. CRD 버전을 잘못 지정하면 매우 귀찮아진다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;CRD 버전이나 YAML의 apiVersion을 잘못 지정하면 다음과 같은 오류가 반복된다.&lt;/p&gt;
&lt;pre id=&quot;code_1766367429351&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;request to convert CR from an invalid group/version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YAML의 apiVersion(v1을 v1beta1라 잘못 표기)을 잘못 지정했거나, CRD와 리소스 버전이 어긋난 상태였던 것으로 보인다. 이 경우 CRD 변환 오류가 반복되며, 정상 복구가 사실상 어려웠다.&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;결국 해결하지 못하고 클러스터를 삭제 후 재설치했다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. NodeTemplate / Provisioner &amp;rarr; NodeClass / NodePool 변경 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;초기 Karpenter는 Provisioner가 노드 스케줄링 정책과 인프라 설정을 모두 책임지는 구조였다.&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 환경에서는 여기에 NodeTemplate을 붙여 AMI, 서브넷, 보안 그룹 같은 EC2 설정을 함께 정의했다. 문제는 이 구조가 시간이 지날수록 책임이 과도하게 집중되고 확장성이 떨어진다는 점이었다.&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;그래서 Karpenter v1.0을 전후로 역할이 분리됐다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NodePool&lt;/b&gt;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;어떤 파드를 대상으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;어떤 제약 조건과 스케줄링 정책을 적용할지를 담당하면,&lt;b&gt; NodeClass&lt;/b&gt;는&amp;nbsp;실제 인프라 설정(AMI, 서브넷, 보안 그룹, IAM Role 등)을 담당하도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;이렇게 역할이 분리되었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 구조를 도입하면서 클라우드별 확장이 쉬워졌고&lt;span&gt;&amp;nbsp;&lt;/span&gt;CRD 구조도 명확해졌지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;구버전 리소스는 최신 EKS 클러스터에서 더 이상 동작하지 않게 됐다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;이번 작업에서 가장 잘했던 선택은 개발 서버에서 먼저 테스트를 진행했다는 점이다.&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 한 번 업데이트를 해봤다는 이유로, 아무 생각 없이 운영 서버에서 바로 진행했다면 전면 장애로 이어질 뻔했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EKS를 사용하면서 느끼는 점은, 버전 변경과 추가지원 종료 주기가 생각보다 빠르다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/kubernetes-versions.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/kubernetes-versions.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가지원으로 전환되면 별다른 경고 없이 비용이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 맞는지에 대해서는 여전히 의문이 남는다. 앞으로는 신경 쓸 일이 없길 바라지만, 그럴 가능성은 높아 보이지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/AWS&amp;amp;인프라</category>
      <category>AWS</category>
      <category>EKS</category>
      <category>karpenter</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/299</guid>
      <comments>https://akku-dev.tistory.com/299#entry299comment</comments>
      <pubDate>Mon, 22 Dec 2025 14:13:12 +0900</pubDate>
    </item>
    <item>
      <title>[책] RAG 시스템 구축을 위한 랭체인 실전 가이드</title>
      <link>https://akku-dev.tistory.com/298</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKpJGN/dJMcahpjmO3/Epbxh1lpUdeygFtawTzvu0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKpJGN/dJMcahpjmO3/Epbxh1lpUdeygFtawTzvu0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKpJGN/dJMcahpjmO3/Epbxh1lpUdeygFtawTzvu0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKpJGN%2FdJMcahpjmO3%2FEpbxh1lpUdeygFtawTzvu0%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;458&quot; height=&quot;625&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;625&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쩌다보니 RAG 시스템을 개발하게 되서 개괄적인 프로세스를 이해하기 위해서 구매한 책이다.&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;스프링 위에서 RAG를 개발해 본 적은 있지만, 당시에도 개발하기 급해서 세부적인 기술들은 많이 놓쳤던 것을 보완하고 요즘 AI 개발에 보편적으로 사용되는 Langchain에 대한 이해가 필요했었는데.. 꽤 나쁘진 않았다.&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;개인적으로 LLM이 어려운건 LLM이 등장하기 까지의 발전과정에 블랙박스가 많기 때문이라 생각하는데, 그거에 대해서 쉽고 간단하게 설명해줬다. 그리고 RAG 개발에 세부 단계들도 코드와 함께 간단하게 정리해줘서 쉽게 읽힌다는 장점도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉬운 점은 너무 보편적인 이야기가 정리되어 있어서 실무에서 얻을만한 세부적인 팁들이 거의 없다. 개괄적인건 어느정도 해봤고 알고 있는 나한테는 조금 아쉬웠던 책이다.&lt;/p&gt;
&lt;pre id=&quot;code_1763961942109&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[질문(Query)] 
  &amp;darr; (임베딩 변환)
[질문 벡터]
  &amp;darr; (벡터 DB 검색)
[유사 문서 Top-k]
  &amp;darr; (LLM 컨텍스트로 전달)
[최종 응답 생성]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 RAG를 구성하는 기본 단계인데 사실 질문 증강, 벡터 축소, 청킹 전략, 인덱싱 알고리즘 등 더 좋은 결과를 얻기 위해 실무에서 도입해야하는 것들도 많기 때문이다.&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;RAG라는 파이프라인 안에서 책 제목에 알맞게 랭체인 라이브러리를 이용해 RAG를 완성해가는 과정과 선택할 수 있는 몇가지 옵션을 정리해 준 책이었다. 정말 RAG 시스템을 처음 사용하는 AI 입문자들에게 어울리는 책이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세부 단계에 대해 설명을 잘 해줬고, 그림과 예시 코드가 많다는 점에서 추천할 만한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 책도 읽어볼 계획인데 딱히 큰 차이가 없지 싶다. 책한권만 더 읽어보고 강의로 넘어가 보려고 한다.&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;가격(26,000)은 좀 아닌 거 같긴 함..&lt;/p&gt;</description>
      <category>일상/책&amp;amp;강의후기</category>
      <category>AI</category>
      <category>RAG 시스템 구축을 위한 랭체인 실전 가이드</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/298</guid>
      <comments>https://akku-dev.tistory.com/298#entry298comment</comments>
      <pubDate>Mon, 24 Nov 2025 14:31:48 +0900</pubDate>
    </item>
    <item>
      <title>RAG와 임베딩 (2) &amp;mdash; Sparse Vector Embedding 이해하기</title>
      <link>https://akku-dev.tistory.com/297</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QCtYZ/dJMcacOYGkF/VKKnXUVu8Tue85EodSnezk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QCtYZ/dJMcacOYGkF/VKKnXUVu8Tue85EodSnezk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QCtYZ/dJMcacOYGkF/VKKnXUVu8Tue85EodSnezk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQCtYZ%2FdJMcacOYGkF%2FVKKnXUVu8Tue85EodSnezk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;1024&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나는 &lt;b&gt;임베딩 벡터를 이용한 검색 시스템&lt;/b&gt;을 사전 이해 없이 통으로 만들어본 적이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그때의 지식이 크게 변하지 않은 채로 시간이 지나면서, 최근 다시 임베딩 벡터 기반 검색 기술을 공부하던 중 &lt;b&gt;의미 기반 검색(Semantic Search)&lt;/b&gt; 과 &lt;b&gt;키워드 기반 검색(Keyword Search)&lt;/b&gt; 의 개념을 명확히 알게 되었다. 그리고 이 두 접근 방식을 비교하다 보니, &lt;b&gt;Sparse Vector(희소 벡터)&lt;/b&gt; 라는 개념이 그 중심에 있다는 걸 알게 됐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 글에서는&lt;b&gt; Sparse Vector가 무엇이고, Dense Vector와 어떤 차이가 있으며, 임베딩 기반 검색에서 어떤 역할을 하는지&lt;/b&gt;를 정리해보려 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;99&quot; data-end=&quot;299&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;99&quot; data-end=&quot;299&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;거기다 이전 글이 Embedding에 사용되는 벡터가 dense만 있다는 듯이 사용해서 자세히 알아봤다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://akku-dev.tistory.com/294&quot;&gt;2025.10.27 - [개발/AI] - RAG와 임베딩 (1) &amp;mdash; Dense Vector Embedding 이해하기&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Sparse Vector란?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZFk9N/dJMcacamQkd/JHqVhXkovfuOHJL8SrOoo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZFk9N/dJMcacamQkd/JHqVhXkovfuOHJL8SrOoo1/img.png&quot; data-alt=&quot;https://www.geeksforgeeks.org/java/implementing-sparse-vector-in-java&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZFk9N/dJMcacamQkd/JHqVhXkovfuOHJL8SrOoo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZFk9N%2FdJMcacamQkd%2FJHqVhXkovfuOHJL8SrOoo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;392&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://www.geeksforgeeks.org/java/implementing-sparse-vector-in-java&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;258&quot; data-start=&quot;117&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;258&quot; data-start=&quot;117&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Sparse Vector(희소 벡터)&lt;/b&gt;는 이름 그대로 값이 &lt;b&gt;희소하게 분포된 벡터&lt;/b&gt;를 뜻한다. 일부 원소에만 값이 존재하고, 나머지는 모두 0으로 채워져 있다. 즉, 대부분의 요소가 0이고 극히 일부만 유의미한 값을 가지는 형태다. 이와 달리 &lt;b&gt;Dense Vector(밀집 벡터)&lt;/b&gt;는 거의 모든 차원에 값이 채워져 있으며, 최근 임베딩 모델에서 흔히 사용되는 형태다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;258&quot; data-start=&quot;117&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결국 두 벡터의 가장 큰 차이는 &lt;b&gt;정보 표현 방식&lt;/b&gt;에 있다 &amp;mdash; Dense는 &lt;b&gt;의미적 압축 표현&lt;/b&gt;이라면, Sparse는 &lt;b&gt;명시적 단어 표현&lt;/b&gt;에 가깝다. Sparse Vector는 이렇게 0이 많은 구조 덕분에 메모리 효율적이며, 특히 단어 등장 빈도나 키워드 중심의 검색처럼 부분적인 정보만 중요할 때 효과적으로 사용된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; Sparse Vector의 대표 모델: TF-IDF와 BM25 &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, Sparse Vector는 주로 &lt;b&gt;문서 내 단어의 등장 빈도&lt;/b&gt;를 기반으로 만들어진다. 이런 방식을 구현한 대표적인 기법이 바로 &lt;b&gt;TF-IDF&lt;/b&gt;와 &lt;b&gt;BM25&lt;/b&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;TF-IDF&amp;nbsp;(Term&amp;nbsp;Frequency&amp;ndash;Inverse&amp;nbsp;Document&amp;nbsp;Frequency)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;TF-IDF는 문서에서 특정 단어가 얼마나 중요한지를 수치로 표현하는 전통적인 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. TF (Term Frequency): 단어가 문서 내에서 얼마나 자주 등장했는가 &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. IDF (Inverse Document Frequency): 그 단어가 전체 문서 집합에서 얼마나 희귀한가 &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 문서 내에서 자주 등장하지만 &lt;b&gt;전체 문서에서는 드문 단어일수록 높은 가중치&lt;/b&gt;를 가진다. 이&amp;nbsp;과정을&amp;nbsp;거쳐&amp;nbsp;만들어진&amp;nbsp;벡터는&amp;nbsp;대부분&amp;nbsp;0(등장하지&amp;nbsp;않은&amp;nbsp;단어)으로&amp;nbsp;채워지고,&amp;nbsp;등장한&amp;nbsp;단어만&amp;nbsp;값이&amp;nbsp;있는&amp;nbsp;희소&amp;nbsp;벡터(Sparse&amp;nbsp;Vector)&amp;nbsp;형태를&amp;nbsp;갖게&amp;nbsp;된다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;ldquo;고양이는 귀엽다&amp;rdquo; -&amp;gt; [0.70, 0.00, 0.30]&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;ldquo;강아지는 귀엽다&amp;rdquo; -&amp;gt; &lt;span style=&quot;text-align: start;&quot;&gt;[0.00, 0.70, 0.30]&lt;/span&gt; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;라는 두 문장이 있다면, TF-IDF는 &amp;ldquo;귀엽다&amp;rdquo;의 가중치는 비슷하지만 &amp;ldquo;고양이&amp;rdquo;, &amp;ldquo;강아지&amp;rdquo;는 서로 다른 차원에 표시되어 구분된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;299&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;BM25 (Best Matching 25)&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;BM25는 TF-IDF를 개선한 방식으로, &lt;b&gt;단어가 얼마나 자주 등장했는가&lt;/b&gt;를 좀 더 현실적으로 계산한다. 단어가 너무 자주 등장한다고 해서 계속 중요도가 올라가면 안 되기 때문에, 일정 수준 이후에는 점수가 완만하게 올라가도록 조정한다. 또한 문서가 길면 단어가 많이 나올 수밖에 없으니, 문서 길이에 따라 점수를 보정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bApSPU/dJMcaelIFCa/poFGf1XxvdYuCu4HZ8Ubf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bApSPU/dJMcaelIFCa/poFGf1XxvdYuCu4HZ8Ubf1/img.png&quot; data-alt=&quot;GPT가 찾아줬다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bApSPU/dJMcaelIFCa/poFGf1XxvdYuCu4HZ8Ubf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbApSPU%2FdJMcaelIFCa%2FpoFGf1XxvdYuCu4HZ8Ubf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;234&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GPT가 찾아줬다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이런 단순한 조정만으로도 실제 검색 정확도가 크게 향상되어,&amp;nbsp; 현재도 Elasticsearch, OpenSearch 등 주요 검색엔진의 기본 검색 알고리즘으로 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;현대적 Sparse Vector 모델&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;전통적인 Sparse Vector 모델(TF-IDF, BM25)은 단어의 빈도를 기반으로 계산되기 때문에, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문맥을 제대로 반영하지 못한다는 한계가 있었다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;ldquo;은행 계좌를 만들었다&amp;rdquo; vs &amp;ldquo;강가에 있는 은행 나무&amp;rdquo;&lt;/blockquote&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;color: #000000;&quot;&gt;두 문장은 &amp;ldquo;은행&amp;rdquo;이라는 단어를 공유하지만 의미는 완전히 다르다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이런 차이를 단순한 단어 빈도만으로 구분하기는 어렵다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 문제를 해결하기 위해 등장한 것이 &lt;b&gt;Transformer 기반 Sparse Vector 모델&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대표적으로 &lt;b&gt;SPLADE, ColBERT&lt;/b&gt; 등이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SPLADE (Sparse Lexical and Expansion Model)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;SPLADE는 BERT 기반 Transformer 모델을 활용해, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문장 내 단어 각각의 중요도를 학습적으로 판단한다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, 모델이 스스로 &amp;ldquo;이 단어가 문장 의미를 대표하는가?&amp;rdquo;를 평가하여 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;중요한 단어에는 높은 점수를, 불필요한 단어에는 낮은 점수를 부여한다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 결과 대부분의 단어는 0, 일부 중요한 단어만 값이 남는 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;희소 벡터(Sparse Vector)가 만들어진다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762823140103&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;입력 문장:  &quot;고양이는 귀엽다&quot;

[BERT 인코더]
      │
      ├─&amp;gt; 각 토큰별 문맥적 중요도 계산
      │     (ex. 고양이=0.9, 귀엽다=0.8, 은=0.1)
      │
      └─&amp;gt; 희소화(Sparsification)
             &amp;darr;
출력 벡터:  [0.9, 0.0, 0.8, 0.0, 0.0, ...]  &amp;rarr; 대부분 0인 Sparse Vector&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 과정에서 모델은 단어의 문맥까지 반영하기 때문에, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단순히 단어 일치에 의존하지 않고 문맥적 의미가 가까운 단어들을 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;유사하게 매핑할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ColBERT (Contextualized Late Interaction)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;ColBERT는 Sparse와 Dense 사이의 중간 모델로, &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문서 전체를 하나의 벡터로 압축하지 않고 단어(토큰) 단위로 Embedding을 생성한다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;검색 시 각 단어 임베딩을 질의(Query)의 단어 임베딩과 비교하여 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 유사한 단어들끼리 지연 결합(Late Interaction) 으로 점수를 계산한다. &lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1762823151929&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;입력 문장:  &quot;고양이는 귀엽다&quot;

[BERT 인코더]
      │
      ├─&amp;gt; 각 단어를 별도의 Dense Vector로 임베딩
      │     (ex. 고양이=[0.12,0.77,...], 귀엽다=[0.21,0.65,...])
      │
      └─&amp;gt; 문서 전체를 하나로 합치지 않고
            Query의 각 단어 벡터와 비교 (Late Interaction)
             &amp;darr;
유사도 계산:  max-similarity(token_query, token_doc)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 방식은 문장 전체 의미보다는 단어 수준의 정밀한 유사도 계산에 강하다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 RAG 시스템에서도 문서-문장 매칭 정밀도를 높일 때 자주 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;0 0 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Sparse Vector와 Dense Vector의 경계&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Sparse Vector는 본래 &amp;ldquo;단어 중심의 명시적 표현&amp;rdquo;이었다. 각 단어가 하나의 축을 차지하고, 그 단어가 등장했는지 여부(또는 가중치)를 그대로 기록하는 구조다. 그래서 단어가 다르면 벡터 공간에서도 완전히 다른 위치로 표현된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;반면 Dense Vector는 문장 전체를 하나의 벡터로 압축해, 단어 하나하나보다는 문맥 전체의 의미를 담는다. 그래서 &amp;ldquo;고양이는 귀엽다&amp;rdquo;와 &amp;ldquo;강아지는 사랑스럽다&amp;rdquo;처럼 단어가 달라도 비슷한 의미를 가지면 벡터 공간에서 가깝게 위치한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 Transformer 기반 Sparse 모델(SPLADE, ColBERT 등)이 등장하면서, 이 두 방식의 차이는 점점 희미해지고 있다. Sparse 모델도 이제 단어의 단순 출현이 아니라 문맥 속 의미를 반영하기 시작했고, Dense 모델 역시 문장 내 세부 단어 정보를 보존하려는 방향으로 발전하고 있다. 결국, Sparse와 Dense는 서로를 닮아가고 있는 것 같다. 다만, 각각의 특성을 100% 반영하지는 못해 각 임베딩 모델의 장점을 모두 활용할 수 있는 &lt;b&gt;하이브리드 검색 방식&lt;/b&gt;도 등장하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아는만큼 보인다고, 서비스 개발할 당시엔 임베딩 기술이 dense vector 기술만 존재하는 걸로 알고 있었다. 하지만 개발 당시에도 elasticsearch의 index의 구성요소를 정의 할 때, dense vector라고 명시해주는 이유를 고민해봤다면 충분히 의심해 볼만 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;321&quot; data-start=&quot;175&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;정리하면, Sparse Vector는 단순히 오래된 검색 방식이 아니라 &lt;b&gt;Dense Vector와 다른 축의 정보 표현 방식&lt;/b&gt;이다. Dense가 문장 전체의 의미를 압축적으로 담는다면, Sparse는 단어 중심의 명시적인 표현으로 세밀한 통제를 가능하게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;472&quot; data-start=&quot;323&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;472&quot; data-start=&quot;323&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;최근 Transformer 기반 Sparse 모델(SPLADE, ColBERT 등)은 단어의 문맥까지 반영하면서 전통적인 Sparse의 단점을 크게 줄였다. 덕분에 단어 기반의 정밀함과 문맥 기반의 의미 이해를 동시에 갖춘 새로운 형태의 검색이 가능해졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;627&quot; data-start=&quot;474&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;627&quot; data-start=&quot;474&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;당시 연구 시절에는 &amp;lsquo;희소 코드(Sparse Code)&amp;rsquo;라는 용어로 불리던 기술들이짧은 시간 안에 이렇게 빠르게 발전했다는 점이 인상 깊다. 다시 AI 분야를 공부하면서, 그만큼 &lt;b&gt;이 분야의 속도가 얼마나 빠른지&lt;/b&gt; 새삼 실감하고 있다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>AI</category>
      <category>bm25</category>
      <category>Dense Vector</category>
      <category>rag</category>
      <category>Sparse Vector</category>
      <category>SPLADE</category>
      <author>애쿠</author>
      <guid isPermaLink="true">https://akku-dev.tistory.com/297</guid>
      <comments>https://akku-dev.tistory.com/297#entry297comment</comments>
      <pubDate>Tue, 11 Nov 2025 10:59:02 +0900</pubDate>
    </item>
  </channel>
</rss>