<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ssunn_ni.log</title>
        <link>https://velog.io/</link>
        <description>방황중인 서버개발자</description>
        <lastBuildDate>Wed, 08 Jul 2026 07:49:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ssunn_ni.log</title>
            <url>https://velog.velcdn.com/images/ssunn_ni/profile/6dee034f-109b-49de-91d6-1b6736aa9b64/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ssunn_ni.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ssunn_ni" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[혁펜하임 딥러닝 커리 완강 후기]]></title>
            <link>https://velog.io/@ssunn_ni/%ED%98%81%ED%8E%9C%ED%95%98%EC%9E%84-%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%BB%A4%EB%A6%AC-%EC%99%84%EA%B0%95-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@ssunn_ni/%ED%98%81%ED%8E%9C%ED%95%98%EC%9E%84-%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%BB%A4%EB%A6%AC-%EC%99%84%EA%B0%95-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Wed, 08 Jul 2026 07:49:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/ssunn_ni/post/0e3bc539-d158-4ac5-b4b6-32212cf43184/image.png" alt=""></p>
<p>드디어.. 혁펜하이님 강의 LEVEL3까지 완강을 했다.
벡엔드만 하다가 AI AGENT를 만들고 AI 오케스트레이션 플렛폼을 개발하면서,</p>
<p>예를들어 프롬프트를 구성할때에 내가 진짜 AI모델들을 잘 활용하고 있는가? 뭔가 잘 알아듣게 내가 더 나은 구성을 던질 수 있지 않을까에대한 순수한 호기심으로 딥러닝 공부를 시작을 했었다.</p>
<p>처음엔 다른 강의들을 들었었고, 그 강의 역시 선대부터 시작을 했었는데..
대학생때는 시험공부를 패스하기 위한 수학으로써밖에 공부를 했어서 뒤로갈수록 뭔가 선형이동이라던지 차원개념이 너무 어려웠었다. 그렇게.. 그 선형대수학 강의를 이해하기 위해 유튜브에서 찾은 강의가 &quot;혁펜하임&quot; 님의 선형대수학 강의였다.</p>
<p>그렇게 선형대수학을 개념단계에서 이해하고 다음 머신러닝 가고.. 커리를 타는데 너무어려워서 역시 EASY 딥러닝으로 먼저 듣고, 공부하다가 아에 커리를 혁펜하임님의 커리로 갈아타고 이렇게 LEVEL3까지 완강을.. 드디어 했다..</p>
<hr>
<p>부디 처음 AI를 공부하겠다고 생각하시는분들은 
&quot;** 백엔드 강의 = 김영한 강의** &quot; 이것처럼 절대공식으로,
&quot; <strong>AI 강의 = 혁펜하임 강의</strong>&quot; 로 공부를 시작한다면
정말 가장 고효율적으로 시간낭비 없이 학습 할 수 있을 것이라고 확신한다.</p>
<hr>
<p>처음 EASY 딥러닝을 공부할때 물론.. 강의 내용은 쉬웠으나.. 역전파 이후로는 조금 힘들었던것같은데 살짝의 고비를 넘어서, 뒤에 CNN나오고 트렌스포머 강의로 넘어갈때는 와.. 이런거구나 신기하다 이해가 잘된다 하면서 쑥쑥 재미있어서 유튜브 보는것마냥 봤던것 같다.</p>
<p>이후로 LEVEL2부터 해도 나름 괜찮았는데.. 역시 LEVEL3넘어가면서 역시 한계가 와서 공식들을 이해하기보다는 뭔가.. 딥러닝에 대해 누가 말해도 아 그게 그거였지? 하면서 알아들을수 있는 정도로만 학습하자를 목표로 머리속에 냅다 들이부어가며 들었다.</p>
<hr>
<p>이제 완강을 (물론 못알아듣겠는부분은 GPT한테 물어보면서..) 했는데 이제야 조금 CLIP이 뭐고.. 디퓨전이 어떤내용이고 흐름을 따라갈 수 있게 된것 같다. 저 강의들 안들었으면 진짜.. 중도포기했을거라고 느껴졌을정도로.. (다른분들은 쉽게 따라가시는것같은데 나만 그런건가 ㅠㅠ)
라이브 강의 참여하고싶긴했는데 라이브로는 바로바로 이해를 못할것같아서 참여하진 않았어요..
혁펜하임님께는 감사의 인사를 드립니다 ㅠ </p>
<p>강의를 다 듣고 아 AI는 대강 이런 원리로 굴러가는거라는걸 깨닫고..
지금와서 보니 뭔가 내가 처음에 &quot;AI모델들을 더 잘 써먹고 싶다&quot;라는 마음으로 딥러닝을 공부하기 시작했는데, 이건... 딥러닝을 공부하더라도 활용분야는 또 별도인것같다라는 생각이 들었다.
논문들 몇개 들여다봐도.. AI를 사용하는 회사들도, 연구하시는분들도 전부 이부분들 연구하시는것같았다. </p>
<p>처음엔 기계적으로 &quot;아 이런 원리로 공부하거나 만들어졌으니 이런 프롬프트, RAG들 을 구성하면 잘 쓸 수 있겠다&quot; 를 깨달을 수 있지 않을까 했었는데, 나도 역시 LLM 오케스트레이션 플렛폼을 개발하면서도 지속해서 고민했던 부분들이고, 써보면서 느꼈던거지만.. 저런 기계적인 접근보다는.. 한 &quot;인공지능&quot;이라고 부르는 만큼, 그리고 아직 사람의 뇌처럼 블랙박스인 부분이 많은 만큼 약간 아직은 똑똑하지만 말귀는 5살 아이인 인공지능에게 잘 알아듣게하는 AI심리학 부분은 딥러닝을 공부함으로써는 해소될 수 없을것같다 라는 생각이 들었다. </p>
<p>그럼에도 이제 AI라던지, 다른 컨퍼런스들을 갔을 때 대체 저게 무슨소리인지 아에 못알아듣기보다는 조금 그래도 주어들을 수 있는 정도까지는 학습한것같아서 뿌듯하다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[딥러닝 공부중 나의 생각정리]]></title>
            <link>https://velog.io/@ssunn_ni/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B3%B5%EB%B6%80%EC%A4%91-%EB%82%98%EC%9D%98-%EC%83%9D%EA%B0%81%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@ssunn_ni/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B3%B5%EB%B6%80%EC%A4%91-%EB%82%98%EC%9D%98-%EC%83%9D%EA%B0%81%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 16 Jun 2026 13:48:31 GMT</pubDate>
            <description><![CDATA[<h4 id="cnn이랑-mlp에서-음-이미지를-셔플시키고-분류하는-내용을-보고">cnn이랑 mlp에서 음.. 이미지를 셔플시키고 분류하는 내용을 보고</h4>
<p>--&gt; mlp는 뭔가 암호를 잘 풀것같다.</p>
<h4 id="skip-connection을-보면-과적합을-방지하는-용도">skip-connection을 보면 과적합을 방지하는 용도.</h4>
<p>-&gt; 기울기소실문제 해결, 앙상블 효과 등등
-&gt; 그렇다면 *<em>프롬프트에다가 너무 필요한 내용만 주는 것 또한 오버피팅이 나지 않을까? *</em>사실 대화내용이라고 한다면 노이즈가 껴도 될것같은데...
-&gt; 할루시네이션이 날것같긴한데.. 
-&gt; 제미나이의 응답
<img src="https://velog.velcdn.com/images/ssunn_ni/post/acc4fb78-d6b4-4655-bbab-1659af494de6/image.png" alt="">
<img src="https://velog.velcdn.com/images/ssunn_ni/post/4acdd046-4a54-47de-b059-2e0589e50981/image.png" alt="">
-&gt; 하지만 이 응답이 너무 파인튜닝이나 학습관점인것같아서 다시 물어봤다.
<img src="https://velog.velcdn.com/images/ssunn_ni/post/98f79a0f-dcef-43b0-8d1b-dfdfed58f614/image.png" alt="">
이 응담 결과를 보면.. 일단 추론을 잘하는 모델인경우에는 이런게 도움이 될 수도 있지 않을까라는 생각이 들었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[실습 아이디어 정리]]></title>
            <link>https://velog.io/@ssunn_ni/%EC%8B%A4%EC%8A%B5-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@ssunn_ni/%EC%8B%A4%EC%8A%B5-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 16 Jun 2026 13:16:08 GMT</pubDate>
            <description><![CDATA[<ul>
<li><p>*<em>라즈베리파이 혹은 핸드폰을 이용해서.. 우리집 고양이 판별기 *</em></p>
</li>
<li><blockquote>
<p>쪼꼬, 꼬미, 봄이 세 마리의 고양이들 중에서.. 사고뭉치 봄이 판별기를 만들어서 봄이가 출몰하면 경고가 울리도록 하는.. 걸 만들어볼까..? (제발 내방에.. 사고치지마라..)</p>
</blockquote>
</li>
<li><p><strong>음... 데이터 sft등 파인튜닝할때 강화학습에 필요한 데이터셋 만들어주기..?</strong></p>
</li>
<li><p>체형 판별기같은거.. ( 너는 스트레이트 체형이야, 너는 뭐 웨이브형이야..)</p>
</li>
<li><p>torch.vision/알부멘테이션 등// 이용해서 약간 음.. 사진 색상 깔맞추기..?(왜냐면 같은 필터를 껴도 사실 사진마다 약간 다른느낌으로 나오는데..)</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Fine tunning 공부중에 생겼었던 나의 질문모음 ( feat. top-down선호자의 폐해)]]></title>
            <link>https://velog.io/@ssunn_ni/Fine-tunning</link>
            <guid>https://velog.io/@ssunn_ni/Fine-tunning</guid>
            <pubDate>Tue, 28 Apr 2026 13:33:29 GMT</pubDate>
            <description><![CDATA[<p>요즘 머신러닝에 대해서 공부중이다.
온라인 부트캠프도 하고, 유튜브 강의도보고 하면서 수학, 이론들만 하다보니..
엔지니어.. 개발자로써 너무 추상적인 개념들이 들어와서
실제로 파인튜닝은 어떻게하는건지.. 내가 정말 저걸 다 알아야만 할 수 있는건지 궁금해서
못참고 강의를 결제했다.</p>
<p>알고리즘이 도배가된건지.. 아 머신러닝관련해서 구글링을 하다가 패캠 강의 광고를 보고 홀린듯이 들어가서 발견한 강의인데, 인프론엔 없고 아직 이런강의는 패캠에만 있는듯 하다.</p>
<p><a href="https://fastcampus.co.kr/data_online_reasoningllm">한국형 딥시크 개발자가 알려주는 LLM의 모든 것 : 데이터생성부터 RAG 파인튜닝, 성능 최적화까지</a>
손규진님의 강의인데, 꽤나 강의가 쭉쭉 들려서 좋다(아직 초기이지만)</p>
<hr>
<p>아직 초반까지들었을때, 앞써 말했듯 개념들이 아직 익숙하지 않은 상태에서 부딪혀봤기때문에
중간중간.. 헷갈린 부분들은 gemini한테 물어보며..(pro이기때문 ㅎ) 하다가 내가 가졌던 질문들을 기록부터해본다.</p>
<h3 id="1-파인튜닝이란">1. 파인튜닝이란?</h3>
<p>파인튜닝을 하는데.. 허깅스페이스에서 가져와서 하는데 이미 똑똑한 모델인것같은데,
학습을 시키고 하니까 갑자기.. 얘네는 빈 깡통이여서 학습시키는건가?  라는 이상한 생각이 들어 파인튜닝에 대해 물어봤고, 그 결과 아.. 이거그냥 <strong>파인튜닝이란</strong> 똑똑한 모델에게 약간 <strong>세뇌시키는 과정</strong>이구나 라는 생각이 들었다. 
 그래서 &quot; 아 그럼 세뇌하는과정인거야 ?&quot; 라고 제미나이에게 물어봤더니 &quot;와 정말 핵심적인 비유야&quot; 라는 칭찬(?)을 받았다 ㅎ
 <img src="https://velog.velcdn.com/images/ssunn_ni/post/633f2ab7-b081-45a3-bd53-8b6400a658e2/image.png" alt=""></p>
<h3 id="2-그럼-데이터들을-어디다가-저장하는거야">2. 그럼 데이터들을 어디다가 저장하는거야?</h3>
<p>이번에도 칭찬받은(?) 질문들.. 정말 왕초보라서 할 수 있는 질문들이지만..
백엔드 개발자 입장에서.. 아니 이미 똑똑한 친구라는데.. 사전지식들도 갖고있는 모델들인데 데이터를 어디다가 저장하는지... 궁금해져서.. 했던 질문
<img src="https://velog.velcdn.com/images/ssunn_ni/post/7a2682d9-5155-4f15-9ca2-5eb111c2d377/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[langgrpaph vs deepagent - 도입고민하기]]></title>
            <link>https://velog.io/@ssunn_ni/langgrpaph-vs-deepagent-%EB%8F%84%EC%9E%85%EA%B3%A0%EB%AF%BC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@ssunn_ni/langgrpaph-vs-deepagent-%EB%8F%84%EC%9E%85%EA%B3%A0%EB%AF%BC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 10 Apr 2026 03:03:43 GMT</pubDate>
            <description><![CDATA[<p>항상 프로덕트를 만들다보면.. 새로운게 나오고
새로운게 나오면 이걸 우리꺼에다 적용시켰을 때 뭐가 더 유리할지에 대해 고민하는 시간이 길어지는것같다.</p>
<p>예전에는 하나하나 검색하고 찾아보면서 판단했었는데..
지금은 일단 claude에게 물어보는중.. </p>
<p>이미 예전에 했었던 고민이였지만 그당시에는 이렇게 점점 고도화가 되지 않았었기 때문에
deep-agent deploy가 나온 시점.. 함게 deep-agent도 다시 고민해보는 시간을 갖었다..</p>
<p>새로운게 계속 나오니까 나올때마다 이고민중이다 ㅠ 설계너무어려워..</p>
<hr>
<h3 id="create_react_agent-vs-deep-agent--실제-실행-메커니즘-차이"><code>create_react_agent</code> vs Deep Agent — 실제 실행 메커니즘 차이</h3>
<pre><code>User: &quot;강남역 지하철 도착 시간 알려줘&quot;

LLM (1회 호출)
├─ System Prompt: &quot;너는 도우미야&quot;
├─ Tool descriptions: [a,b,c ..... ]  ← 20개 전부 프롬프트에
├─ User: &quot;강남역 지하철 도착 시간&quot;
└─ LLM 판단: tool_call → search(search=&quot;강남&quot;)
   └─ 결과 받아서 → LLM 최종 응답</code></pre><p><strong>이게 전부입니다.</strong> ReAct 루프는:</p>
<pre><code>Think → Act(tool call) → Observe(결과) → Think → ... → 최종 답변</code></pre><p>LLM <strong>한 놈이 모든 걸 결정</strong>합니다. 도구 20개의 description을 전부 읽고, 어떤 도구를 쓸지 스스로 고릅니다.</p>
<hr>
<h3 id="deep-agent-패턴-supervisor--skill-agents">Deep Agent 패턴 (Supervisor + Skill Agents)</h3>
<pre><code>User: &quot;강남역 지하철 도착 시간 알려줘&quot;

Supervisor (LLM 1회 호출) — 가벼운 라우터
├─ System Prompt: &quot;아래 skill 중 적절한 것을 골라라&quot;
├─ Skill 목록 (도구가 아니라 에이전트 이름):
│   ├─ &quot;a&quot; - search
│   ├─ &quot;b&quot; 
│   ├─ &quot;c&quot;
│   ├─ &quot;d&quot;— 
│   └─ &quot;e&quot; — 그냥 대화
├─ LLM 판단: → &quot;search&quot;
│
└─ Agent (별도 LLM 호출)
   ├─ System Prompt: &quot;너는 한국 서비스 전문가야&quot;
   ├─ Tool descriptions: [a] ← 1개만!
   ├─ Context: 원래 질문 + Supervisor가 전달한 맥락
   └─ LLM 판단: tool_call → search(search=&quot;강남&quot;)
      └─ 결과 받아서 → 최종 응답</code></pre><hr>
<h3 id="대체-뭐가-다른-건데">대체 뭐가 다른 건데?</h3>
<table>
<thead>
<tr>
<th></th>
<th><code>create_react_agent</code></th>
<th>Supervisor + Skill</th>
</tr>
</thead>
<tbody><tr>
<td><strong>LLM 호출 수</strong></td>
<td>1회 (도구 사용 시 +1)</td>
<td>2회 (분류 1회 + 실행 1회)</td>
</tr>
<tr>
<td><strong>도구 선택</strong></td>
<td>LLM이 20개 중 직접 선택</td>
<td>Supervisor가 분야 선택 → Skill Agent가 3개 중 선택</td>
</tr>
<tr>
<td><strong>프롬프트 크기</strong></td>
<td>20개 도구 description 전부 (~2K 토큰)</td>
<td>Supervisor: 5줄 목록 (<del>200 토큰), Skill: 3개 description (</del>300 토큰)</td>
</tr>
<tr>
<td><strong>오선택 확률</strong></td>
<td>비슷한 도구 많으면 혼동</td>
<td>단계적 축소 → 혼동 적음</td>
</tr>
</tbody></table>
<p><strong>본질적 차이는 &quot;2단계 분류&quot;입니다.</strong> 그 이상도 이하도 아닙니다.</p>
<hr>
<h4 id="하네스harness란">&quot;하네스(Harness)&quot;란?</h4>
<p>Deep Agents 블로그에서 말하는 harness는 거창한 게 아닙니다:</p>
<pre><code class="language-python"># 이게 하네스의 전부입니다
harness = {
    &quot;supervisor&quot;: supervisor_agent,      # 라우팅 에이전트
    &quot;skills&quot;: {
        &quot;search&quot;: search_agent,          # Skill Agent 1
        &quot;math&quot;: math_agent,              # Skill Agent 2
    },
    &quot;memory&quot;: memory_store,              # 대화 기록/학습된 패턴
    &quot;config&quot;: agents_md                  # AGENTS.md 설정 파일
}

# 실행
async def run(user_input):
    skill_name = await harness[&quot;supervisor&quot;].invoke(user_input)  # 분류
    result = await harness[&quot;skills&quot;][skill_name].invoke(user_input)  # 실행
    return result</code></pre>
<p><strong>하네스 = Supervisor + Skill Agents + Memory + Config를 묶어놓은 실행 컨테이너</strong></p>
<p>LangChain 코드로 치면:</p>
<pre><code class="language-python">from langgraph.prebuilt import create_react_agent

# Skill Agent들 정의
search_agent = create_react_agent(
    model=llm,
    tools=[a, b, c]
)
math_agent = create_react_agent(
    model=llm,
    tools=[d, f]
)

# Supervisor = 그냥 또 다른 에이전트인데, &quot;tools&quot; 대신 &quot;agents&quot;를 호출
supervisor = create_react_agent(
    model=llm,
    tools=[
        Tool(name=&quot;search&quot;, func=search_agent.ainvoke, description=&quot;웹 검색&quot;),
        Tool(name=&quot;math&quot;, func=math_agent.ainvoke, description=&quot;수학 계산&quot;),
    ]
)</code></pre>
<p><strong>Supervisor의 &quot;도구&quot;가 다른 에이전트를 호출하는 함수</strong>인 겁니다.</p>
<hr>
<h3 id="솔직한-평가">솔직한 평가</h3>
<p>현재 시스템에서 도구 20개 중 오선택이 자주 발생하나요?</p>
<ul>
<li><strong>오선택 자주 발생</strong> → Skill Router 도입 가치 있음</li>
<li><strong>잘 작동하고 있음</strong> → LLM 호출 1회 추가 비용만 생기는 오버엔지니어링</li>
</ul>
<p>도구가 50개, 100개로 늘어날 예정이라면 지금 구조화해두는 게 맞고, 현재 20개 선에서 잘 돌아가고 있다면 굳이 바꿀 필요 없습니다.</p>
<hr>
<p>이렇게 보면.. 또 클로드 코드분석했을때의 tool사용하는 방법이랑 비슷한것같기도 한데..
아마 좀 더 기능을 붙이면 나중에는 deep-agent를 써야하지 않을 까 싶다..</p>
<p>제대로 알아보고 이해한게 맞는지 여러번 되묻고 찾아보기도했는데
혹시나 알게되는 다른내용이라던지.. 잘못이해한 내용있다면 언제든 피드백 환영입니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Claude code에서 맥락 유지관리 파해쳐보기]]></title>
            <link>https://velog.io/@ssunn_ni/Claude-code%EC%97%90%EC%84%9C-%EC%9C%A0%EC%A7%80%EA%B4%80%EB%A6%AC-%ED%8C%8C%ED%95%B4%EC%B3%90%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@ssunn_ni/Claude-code%EC%97%90%EC%84%9C-%EC%9C%A0%EC%A7%80%EA%B4%80%EB%A6%AC-%ED%8C%8C%ED%95%B4%EC%B3%90%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Thu, 02 Apr 2026 08:20:19 GMT</pubDate>
            <description><![CDATA[<p>현재 linkai 라는 LLM 멀티오케스트레이션 서비스를 구축하고 운영중이다.
항상 고민중인게 이 파이프라인과 문맥관리인데,
이번에 클로드 코드의 관련 내용을 백엔드 서버개발자 관점에서 탈탈 털어보았다.
<img src="https://velog.velcdn.com/images/ssunn_ni/post/223f4df4-c3fc-4b56-9822-f0ff47eb1502/image.png" alt=""></p>
<p>우선 위의 사진에처럼, 질문별로 문서를 작성하고, 관련해서 꼬리에 꼬리를 무는 질문을하여
문서를 구체화 한 뒤에, 블로그에 작성할 내용을 정리하도록 하였다.</p>
<hr>
<h1 id="llm-에이전트-맥락관리와-tool-orchestration-fastapi--python-실전-가이드">LLM 에이전트 맥락관리와 Tool Orchestration: FastAPI + Python 실전 가이드</h1>
<p>최근 에이전트 시스템을 운영해보면, 성능 이슈의 대부분은 모델 자체보다 두 가지에서 터집니다.</p>
<ul>
<li>맥락관리(Context Management)</li>
<li>도구 실행 제어(Tool Orchestration)</li>
</ul>
<p>이 글은 실제 CLI 에이전트 구조를 바탕으로, 왜 이 두 축이 중요한지와 FastAPI + Python으로 어떻게 최소 구현할 수 있는지 정리합니다.</p>
<h3 id="빠른-네비게이션">빠른 네비게이션</h3>
<ul>
<li>문제 정의: 왜 이 주제가 중요한가</li>
<li>설계 핵심: 설계 원칙 5가지, 아키텍처 한 장 요약</li>
<li>동작 예시: 구체 시나리오 1~4</li>
<li>구현 예시: FastAPI 미니 예제 1~3</li>
<li>운영 가이드: Prompt Caching, 운영 체크리스트, 전체 흐름 순서도</li>
</ul>
<h3 id="3줄-요약">3줄 요약</h3>
<ul>
<li>맥락관리의 핵심은 &quot;기록을 늘리는 것&quot;이 아니라 &quot;핵심 상태를 남기며 압축하는 것&quot;입니다.</li>
<li>Tool Orchestration의 핵심은 &quot;많이 병렬화&quot;가 아니라 &quot;안전한 툴만 병렬화&quot;입니다.</li>
<li>Prompt Caching의 핵심은 &quot;모든 블록 캐시&quot;가 아니라 &quot;안정된 경계 1개&quot;입니다.</li>
</ul>
<h3 id="왜-이-주제가-중요한가">왜 이 주제가 중요한가</h3>
<p>모델은 잘 대답하는데 서비스는 불안정한 이유는 보통 아래 패턴입니다.</p>
<ul>
<li>메시지 히스토리가 계속 누적되어 컨텍스트 윈도우를 압박한다.</li>
<li>툴 호출이 병렬/직렬 기준 없이 섞여 레이스 컨디션이 난다.</li>
<li>권한 확인 없이 위험한 툴이 즉시 실행된다.</li>
<li>캐시 경계가 불안정해서 요청 비용이 매 턴 급증한다.</li>
</ul>
<p>핵심은 간단합니다.</p>
<ul>
<li>토큰은 예산이고,</li>
<li>툴은 상태를 바꾸는 트랜잭션이며,</li>
<li>캐시는 경계가 안정적일 때만 이득이 납니다.</li>
</ul>
<h3 id="설계-원칙-5가지">설계 원칙 5가지</h3>
<ol>
<li>최근 대화만 남기는 것이 아니라, 중요한 상태를 요약본으로 보존한다.</li>
<li>툴은 무조건 병렬이 아니라, 안전한 툴만 병렬로 돌린다.</li>
<li>위험한 툴은 권한 게이트를 통과해야 실행한다.</li>
<li>캐시 마커는 많이 붙이는 것이 아니라, 안정된 경계 한 곳에 둔다.</li>
<li>실패(PTL, timeout, permission deny)를 정상 경로로 취급하고 재시도 정책을 둔다.</li>
</ol>
<h3 id="아키텍처-한-장-요약">아키텍처 한 장 요약</h3>
<ul>
<li>API 레이어: 모델 호출, 스트리밍 처리, usage 수집</li>
<li>Context 레이어: 히스토리 누적, 압축(snip/microcompact/autocompact), 첨부 재주입</li>
<li>Orchestration 레이어: tool_use 파싱, 병렬/직렬 실행, 결과 병합</li>
<li>Safety 레이어: 권한 정책, 사용자 승인, 위험도 분류</li>
<li>Observability 레이어: cache hit/miss, 토큰 사용량, 실패 카운트</li>
</ul>
<h3 id="구체-시나리오-1-srcauthpy-고쳐줘-요청이-들어오면">구체 시나리오 1: &quot;src/auth.py 고쳐줘&quot; 요청이 들어오면</h3>
<p>아래는 실제 운영에서 가장 자주 보는 1턴 내부 흐름입니다.</p>
<pre><code class="language-text">[1] UserMessage(&quot;src/auth.py 고쳐줘&quot;) append
        -&gt; 모델 호출

[2] AssistantMessage(text + tool_use: read_file)
        stop_reason = tool_use

[3] 런타임이 read_file 실행
        -&gt; UserMessage(tool_result: 파일 내용)
        -&gt; 같은 턴에서 모델 재호출

[4] AssistantMessage(text + tool_use: write_file)
        stop_reason = tool_use

[5] 권한 게이트 ask 발생 (write_file은 위험도 중간 이상)
        -&gt; 사용자 승인 후 실행
        -&gt; UserMessage(tool_result: 수정 완료)

[6] AssistantMessage(&quot;수정 완료&quot;)
        stop_reason = end_turn</code></pre>
<p>핵심:</p>
<ul>
<li>tool_use가 나오면 턴이 끝나는 게 아니라 follow-up 루프가 이어집니다.</li>
<li>tool_result는 user role로 다시 들어가 다음 모델 호출 컨텍스트가 됩니다.</li>
</ul>
<h3 id="구체-시나리오-2-스트리밍-이벤트가-메시지로-조립되는-방식">구체 시나리오 2: 스트리밍 이벤트가 메시지로 조립되는 방식</h3>
<p>아래처럼 네트워크 이벤트(델타)를 받아 상위 메시지로 조립합니다.</p>
<pre><code class="language-text">message_start
-&gt; content_block_start(text)
-&gt; content_block_delta(text_delta: &quot;파일을&quot;)
-&gt; content_block_delta(text_delta: &quot; 확인할게요&quot;)
-&gt; content_block_stop
    =&gt; AssistantMessage(text) 방출

-&gt; content_block_start(tool_use)
-&gt; content_block_delta(input_json_delta: &#39;{&quot;file_path&quot;:&quot;src/auth.py&quot;&#39;)
-&gt; content_block_delta(input_json_delta: &#39;}&#39;)
-&gt; content_block_stop
    =&gt; AssistantMessage(tool_use) 방출

-&gt; message_delta(stop_reason: tool_use, usage: ...)
    =&gt; 직전 메시지 메타 확정(usage/stop_reason)</code></pre>
<p>핵심:</p>
<ul>
<li>사용자 체감 출력은 빠르게 먼저 내보내고,</li>
<li>usage/stop_reason은 message_delta에서 최종 보강됩니다.</li>
</ul>
<h3 id="구체-시나리오-3-병렬직렬-오케스트레이션-결과-비교">구체 시나리오 3: 병렬/직렬 오케스트레이션 결과 비교</h3>
<p>요청된 tool plan이 아래와 같다고 가정합니다.</p>
<pre><code class="language-json">{
    &quot;calls&quot;: [
        {&quot;name&quot;: &quot;read_file&quot;, &quot;args&quot;: {&quot;path&quot;: &quot;src/auth.py&quot;}},
        {&quot;name&quot;: &quot;search_code&quot;, &quot;args&quot;: {&quot;query&quot;: &quot;login&quot;}},
        {&quot;name&quot;: &quot;write_file&quot;, &quot;args&quot;: {&quot;path&quot;: &quot;src/auth.py&quot;, &quot;patch&quot;: &quot;...&quot;}}
    ]
}</code></pre>
<p>실행 정책:</p>
<ul>
<li>read_file + search_code: 병렬 배치</li>
<li>write_file: 직렬 단독 배치</li>
</ul>
<p>결과적으로 읽기/검색은 빠르게 합쳐지고, 상태 변경은 안전하게 뒤에서 실행됩니다.</p>
<h3 id="구체-시나리오-4-캐시-마커는-많이가-아니라-안정적으로-1개">구체 시나리오 4: 캐시 마커는 &quot;많이&quot;가 아니라 &quot;안정적으로 1개&quot;</h3>
<p>잘못된 예시(경계를 여러 군데 흔듦):</p>
<pre><code class="language-ts">messages.map(m =&gt; ({
    ...m,
    content: m.content.map(c =&gt; ({
        ...c,
        cache_control: { type: &#39;ephemeral&#39;, ttl: &#39;1h&#39; },
    })),
}))</code></pre>
<p>권장 예시(요청당 message-level marker 1개):</p>
<pre><code class="language-ts">const markerIndex = skipCacheWrite ? messages.length - 2 : messages.length - 1
const payload = messages.map((m, i) =&gt; toMessageParam(m, i === markerIndex))</code></pre>
<p>핵심:</p>
<ul>
<li>cache_control은 캐시 데이터 본문이 아니라 경계 힌트입니다.</li>
<li>경계가 자주 흔들리면 cache hit rate가 급락하고 비용/지연이 증가합니다.</li>
</ul>
<h3 id="fastapi-예제-실행-방법-로컬">FastAPI 예제 실행 방법 (로컬)</h3>
<p>아래 예제들은 각각 독립 파일로 저장해서 실행할 수 있습니다.</p>
<ol>
<li>가상환경 및 패키지 설치</li>
</ol>
<pre><code class="language-bash">python -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn pydantic</code></pre>
<ol start="2">
<li>예제 파일 저장</li>
</ol>
<ul>
<li><code>example_context.py</code></li>
<li><code>example_orchestrator.py</code></li>
<li><code>example_guard.py</code></li>
</ul>
<ol start="3">
<li>서버 실행</li>
</ol>
<pre><code class="language-bash">uvicorn example_context:app --reload --port 8000
uvicorn example_orchestrator:app --reload --port 8001
uvicorn example_guard:app --reload --port 8002</code></pre>
<ol start="4">
<li>빠른 테스트</li>
</ol>
<pre><code class="language-bash">curl -X POST http://127.0.0.1:8000/chat \
    -H &#39;Content-Type: application/json&#39; \
    -d &#39;{&quot;session_id&quot;:&quot;s1&quot;,&quot;user_input&quot;:&quot;문서 20개 요약해줘&quot;}&#39;</code></pre>
<h3 id="fastapi-미니-예제-1-맥락관리-파이프라인">FastAPI 미니 예제 1: 맥락관리 파이프라인</h3>
<p>아래 예제는 턴마다 히스토리를 누적하고, 임계값을 넘으면 자동 요약(compact)을 수행합니다.</p>
<pre><code class="language-python">from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Dict

app = FastAPI()


class Turn(BaseModel):
    role: str
    content: str


class ChatRequest(BaseModel):
    session_id: str
    user_input: str


SESSIONS: Dict[str, List[Turn]] = {}

TOKEN_LIMIT = 12000
AUTO_COMPACT_THRESHOLD = 9000


def rough_token_count(turns: List[Turn]) -&gt; int:
    return sum(len(t.content) // 3 for t in turns)


def compact_history(turns: List[Turn]) -&gt; List[Turn]:
    if len(turns) &lt; 6:
        return turns

    head = turns[:-4]
    tail = turns[-4:]

    summary_text = &quot; &quot;.join(t.content for t in head)[:1200]
    summary = Turn(role=&quot;system&quot;, content=f&quot;Summary of earlier context: {summary_text}&quot;)
    return [summary] + tail


@app.post(&quot;/chat&quot;)
def chat(req: ChatRequest):
    turns = SESSIONS.setdefault(req.session_id, [])

    turns.append(Turn(role=&quot;user&quot;, content=req.user_input))

    token_est = rough_token_count(turns)
    if token_est &gt; AUTO_COMPACT_THRESHOLD:
        turns[:] = compact_history(turns)

    # 여기서 실제 LLM 호출을 수행한다고 가정
    answer = f&quot;Echo: {req.user_input}&quot;
    turns.append(Turn(role=&quot;assistant&quot;, content=answer))

    if rough_token_count(turns) &gt; TOKEN_LIMIT:
        return {
            &quot;ok&quot;: False,
            &quot;reason&quot;: &quot;context_window_exceeded&quot;,
            &quot;hint&quot;: &quot;compact 강도를 높이거나 입력 배치를 분할하세요&quot;,
        }

    return {
        &quot;ok&quot;: True,
        &quot;answer&quot;: answer,
        &quot;token_estimate&quot;: rough_token_count(turns),
        &quot;turns&quot;: len(turns),
    }</code></pre>
<p>포인트:</p>
<ul>
<li>자동 압축을 늦게 걸면 이미 PTL 근처까지 가서 실패 확률이 커집니다.</li>
<li>압축 후에도 첨부/파일 컨텍스트를 선택적으로 재주입하는 단계가 필요합니다.</li>
</ul>
<h3 id="fastapi-미니-예제-2-tool-orchestration-병렬직렬-분리">FastAPI 미니 예제 2: Tool Orchestration (병렬/직렬 분리)</h3>
<p>도구는 모두 병렬 실행하면 빨라 보이지만, 상태 변경 툴이 섞이면 깨집니다.</p>
<pre><code class="language-python">import asyncio
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Any, Dict, List

app = FastAPI()


class ToolCall(BaseModel):
    name: str
    args: Dict[str, Any]


class ToolPlan(BaseModel):
    calls: List[ToolCall]


TOOL_META = {
    &quot;read_file&quot;: {&quot;concurrency_safe&quot;: True},
    &quot;search_code&quot;: {&quot;concurrency_safe&quot;: True},
    &quot;run_terminal&quot;: {&quot;concurrency_safe&quot;: False},
    &quot;write_file&quot;: {&quot;concurrency_safe&quot;: False},
}


async def execute_tool(call: ToolCall) -&gt; Dict[str, Any]:
    # 실제 구현에서는 도구별 dispatcher 연결
    await asyncio.sleep(0.05)
    return {&quot;tool&quot;: call.name, &quot;ok&quot;: True, &quot;result&quot;: call.args}


@app.post(&quot;/orchestrate&quot;)
async def orchestrate(plan: ToolPlan):
    safe_calls = [
        c for c in plan.calls if TOOL_META.get(c.name, {}).get(&quot;concurrency_safe&quot;, False)
    ]
    unsafe_calls = [c for c in plan.calls if c not in safe_calls]

    safe_results = await asyncio.gather(*(execute_tool(c) for c in safe_calls))

    unsafe_results = []
    for c in unsafe_calls:
        unsafe_results.append(await execute_tool(c))

    return {
        &quot;parallel_count&quot;: len(safe_calls),
        &quot;serial_count&quot;: len(unsafe_calls),
        &quot;results&quot;: safe_results + unsafe_results,
    }</code></pre>
<p>포인트:</p>
<ul>
<li>읽기 전용/조회성 툴은 병렬, 상태 변경 툴은 직렬이 기본값입니다.</li>
<li>이 분리만 해도 툴 충돌과 재현 어려운 버그가 크게 줄어듭니다.</li>
</ul>
<h3 id="fastapi-미니-예제-3-권한-게이트approval-loop">FastAPI 미니 예제 3: 권한 게이트(Approval Loop)</h3>
<p>실무에서는 자동화보다 안전이 우선인 경우가 많습니다.</p>
<pre><code class="language-python">from enum import Enum
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()


class Decision(str, Enum):
    allow = &quot;allow&quot;
    deny = &quot;deny&quot;
    ask = &quot;ask&quot;


class GuardRequest(BaseModel):
    tool_name: str
    risk: str
    user_approved: bool = False


def policy(tool_name: str, risk: str) -&gt; Decision:
    if risk == &quot;low&quot;:
        return Decision.allow
    if tool_name in {&quot;write_file&quot;, &quot;run_terminal&quot;}:
        return Decision.ask
    return Decision.deny


@app.post(&quot;/guard&quot;)
def guard(req: GuardRequest):
    d = policy(req.tool_name, req.risk)

    if d == Decision.allow:
        return {&quot;decision&quot;: &quot;allow&quot;}

    if d == Decision.deny:
        return {&quot;decision&quot;: &quot;deny&quot;, &quot;reason&quot;: &quot;policy_blocked&quot;}

    if req.user_approved:
        return {&quot;decision&quot;: &quot;allow&quot;, &quot;reason&quot;: &quot;user_approved&quot;}

    raise HTTPException(
        status_code=409,
        detail={
            &quot;decision&quot;: &quot;ask&quot;,
            &quot;reason&quot;: &quot;need_user_approval&quot;,
        },
    )</code></pre>
<p>포인트:</p>
<ul>
<li>allow/deny/ask의 3상태 모델이 운영 난이도를 크게 낮춥니다.</li>
<li>ask 상태를 UI 승인 루프로 연결하면 안전성과 자동화를 함께 가져갈 수 있습니다.</li>
</ul>
<h3 id="prompt-caching은-어떻게-봐야-하나">Prompt Caching은 어떻게 봐야 하나</h3>
<p>캐싱 관련 오해를 한 줄로 정리하면:</p>
<ul>
<li>cache_control은 캐시 경계 마커이지, 캐시 데이터 본문이 아닙니다.</li>
</ul>
<p>즉 클라이언트는 평소처럼 메시지를 보내고, 서버가 캐시를 저장/재사용합니다.
그래서 중요한 건 다음입니다.</p>
<ul>
<li>경계를 안정적으로 유지할 것</li>
<li>시스템 프롬프트/툴 스키마/베타 헤더를 자주 흔들지 말 것</li>
<li>hit rate는 cache read 토큰과 응답 지연으로 관측할 것</li>
</ul>
<h3 id="운영-체크리스트">운영 체크리스트</h3>
<ol>
<li>압축 트리거 임계값이 PTL 전에 작동하는가</li>
<li>병렬 툴과 직렬 툴의 기준이 명시되어 있는가</li>
<li>위험 툴에 대해 사용자 승인 루프가 있는가</li>
<li>캐시 경계를 턴마다 바꾸고 있지 않은가</li>
<li>실패 시 재시도와 circuit breaker가 있는가</li>
<li>비용/지연/실패 지표를 대시보드에서 보는가</li>
</ol>
<h3 id="마무리">마무리</h3>
<p>에이전트 품질은 모델 선택보다 운영 구조에서 갈립니다.</p>
<ul>
<li>맥락관리: 오래된 정보를 버리는 기술이 아니라, 중요한 상태를 보존하는 기술</li>
<li>툴 오케스트레이션: 많이 실행하는 기술이 아니라, 안전하게 실행 순서를 제어하는 기술</li>
</ul>
<p>이 두 축만 제대로 잡아도, 같은 모델로도 체감 품질과 비용 효율이 크게 달라집니다.</p>
<p>실제 운영에서 가장 효과가 큰 개선 순서는 보통 아래와 같습니다.</p>
<ol>
<li>PTL 전 자동 압축 임계값 조정</li>
<li>concurrency-safe 기준 정리(병렬/직렬 분리)</li>
<li>위험 툴 approval loop 의무화</li>
<li>캐시 경계 안정화(마커 정책 고정)</li>
</ol>
<p>즉, 모델 교체보다 먼저 오케스트레이션과 맥락정책을 고정하면, 실패율과 비용이 함께 내려갑니다.</p>
<h3 id="전체-흐름-순서도">전체 흐름 순서도</h3>
<pre><code class="language-text">[User Input]
    -&gt; messages에 UserMessage append
    -&gt; 토큰 추정 / 임계값 체크
            -&gt; (초과) compact 실행: snip/microcompact/autocompact
            -&gt; 모델 호출

모델 응답 처리:
    -&gt; Assistant 응답에 tool_use 존재?
            -&gt; No: stop_reason=end_turn -&gt; 턴 종료 / 다음 턴 대기
            -&gt; Yes:
                    -&gt; Tool plan 생성
                    -&gt; concurrency_safe 기준으로 병렬/직렬 분리
                    -&gt; 권한 정책 분기
                            -&gt; allow: tool 실행
                            -&gt; deny: 에러 tool_result 생성
                -&gt; ask: 사용자 승인 대기
                    -&gt; (백그라운드) classifier/hook 판정 작업 진행
                    -&gt; 승인 시 실행 / 거절 시 에러 tool_result
            -&gt; tool 실행 결과를 UserMessage(tool_result)로 append
            -&gt; 같은 턴에서 모델 재호출
            -&gt; tool_use가 사라질 때까지 반복

캐시 처리:
    -&gt; cache_control 경계 마커 적용
    -&gt; 요청당 message-level marker 1개 유지
    -&gt; TTL 정책 결정(사용자 eligibility + querySource allowlist)
    -&gt; skipCacheWrite면 marker를 second-to-last로 이동
    -&gt; 서버측 prompt cache read/write

백그라운드 실행 처리:
    -&gt; 장시간 작업인가?
        -&gt; Yes: run_in_background 경로로 전환, task id 반환, 턴은 계속 진행
        -&gt; No: foreground 실행 후 즉시 tool_result 반환</code></pre>
<h3 id="캐시정책백그라운드동의-루프-운영-포인트">캐시정책/백그라운드/동의 루프 운영 포인트</h3>
<ol>
<li>캐시 정책</li>
</ol>
<ul>
<li>cache_control은 캐시 데이터 본문이 아니라 경계 힌트입니다.</li>
<li>마커를 여러 개 두기보다, 요청당 1개를 안정적으로 유지해야 hit rate가 높습니다.</li>
<li>TTL 1h는 항상 적용이 아니라 eligibility + allowlist 조건을 통과해야 적용됩니다.</li>
</ul>
<ol start="2">
<li>동의(ask) 루프</li>
</ol>
<ul>
<li>ask 상태는 실행 전 대기 상태입니다.</li>
<li>대기 중에는 정책 판정 보조 작업(classifier/hook)을 백그라운드로 돌려 응답 시간을 줄일 수 있습니다.</li>
<li>승인되면 동일 tool call을 이어서 실행하고, 거절되면 에러 tool_result를 user role로 반환합니다.</li>
</ul>
<ol start="3">
<li>백그라운드 실행</li>
</ol>
<ul>
<li>백그라운드 전환은 권한 대기와 다릅니다.</li>
<li>권한 대기는 &quot;아직 실행 전&quot;, 백그라운드는 &quot;실행은 시작했고 분리 실행&quot;입니다.</li>
<li>장시간 명령은 task id 기반으로 추적하고, 후속 턴에서 상태/로그를 조회하도록 설계하는 것이 안전합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB쿼리에 대한 나의 생각변화]]></title>
            <link>https://velog.io/@ssunn_ni/DB%EC%BF%BC%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EB%82%98%EC%9D%98-%EC%83%9D%EA%B0%81%EB%B3%80%ED%99%94</link>
            <guid>https://velog.io/@ssunn_ni/DB%EC%BF%BC%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EB%82%98%EC%9D%98-%EC%83%9D%EA%B0%81%EB%B3%80%ED%99%94</guid>
            <pubDate>Thu, 07 Aug 2025 05:45:55 GMT</pubDate>
            <description><![CDATA[<p>최근에 하고있는 고민들에 대해서 기록을 남아보려한다.
이전에도 계속 무엇인가를 개발할때마다 생겼던 질문들인데</p>
<blockquote>
<p>대체 좋은 쿼리는 뭘까?</p>
</blockquote>
<hr>
<h3 id="1-내가-기억하기로는-처음에-생겼던-질문중-하나는-">1. 내가 기억하기로는 처음에 생겼던 질문중 하나는 ..</h3>
<blockquote>
<p><strong>select * from 으로 해서 전부 조회한다음에 코드로 필요한부분을 쓰는게 좋나? 아니면 필요한 부분만 select하는게 좋을까?</strong></p>
</blockquote>
<p>처음 입사하고나서 이 부분에대해 사수분께 여쭤본적이 있는데, 
사실 지금 엄청 복잡한걸 건들지 않으니 크게 상관은 없을거고, 실행시켜서 해봐~</p>
<p>라는 답변을 받았었는데, 테스트를 해봐도 엄청 복잡한게 아니다보니 시간차이를 알아볼수 없어서 실패했었다. ㅋㅋㅋㅋ</p>
<p><strong>GPT</strong>는 </p>
<pre><code>운영 환경이나 실서비스 코드에서는 SELECT *를 지양하고, 필요한 컬럼만 선택하는 것이 일반적인 베스트 프랙티스입니다. 
성능, 유지보수성, 코드 명확성 측면 모두에서 이점이 크기 때문입니다.</code></pre><p>라고 답을 해주었고, 일부 동의한다.
이제는 그저 상황에 그때그때 상황에 맞게끔 판단하고있다.</p>
<hr>
<h3 id="2-두번째-생긴-질문은">2. 두번째 생긴 질문은,</h3>
<blockquote>
<p><strong>그래서 좋은 쿼리는 뭔데?</strong></p>
</blockquote>
<p>굉장히 근본적인 질문이다.
이때는 한참 쿼리 최적화에 빠져서, 여기저기 컨퍼런스가서 관련주제있으면 듣고, 
explain는 꼭 확인해보고 했었던 시기였는데, </p>
<p>그래서인가, 이때는 index잘 타고, 원하는값 나오고, 빠르고, 부하가 적고  그게 좋은쿼리 아닌가? 라고 결론을 지었던 것 같다.</p>
<hr>
<h3 id="3-현재-생긴-질문은">3. 현재 생긴 질문은..</h3>
<blockquote>
<p><strong>원하는 값은 잘 나오고 index잘 탔는데.. 그래서 그렇게 복잡한 쿼리가 나을까?
두번 조회하더라도 알아보기 편한게 좋을까?</strong></p>
</blockquote>
<p>2번때의 내가 짰던 쿼리들을 보면 한참.. join이랑 각종 것들을 엄청해서
나름 최적화시키고, db조회는 최소화로 하는 방법으로 쿼리를 작성했었다.</p>
<p>그러다보니, 이후에 해당 쿼리에 수정을 해야한다 하면.. 정말 수정하기가 복잡해졌다..
거기다가 동적쿼리도 하려고했다보니 한번 수정하기가 너무 힘들었다.
GPT가 요즘엔 도와줘서 그래도 괜찮다 싶다가도, GPT의 코딩은 좀 더 빡셀때가 많다 ㅋㅋㅋ</p>
<p>그래서 하면서도.. 이거 만든 나도 힘든데, 다른사람이 알아보기 좀 빡셀 것 같은데 라는 생각을 하고있었는데,</p>
<p>최근데 다녀왔던 <strong>2025토스컨퍼런스</strong>에서, 레거시 코드를 뜯어고친 것을 주제로 한 연사를 들었다.
다른부분은 아 그냥 묶고 분리하고 뭐 그런거군 하면서 듣고있다가,</p>
<pre><code>레거시 query가 정말 너무길고 join도 여러번에 너무 복잡해서 
다 끊어내고 알아보기쉽게했다. 그래야 에러도 덜나고 디버깅도 쉽다</code></pre><p>이런말을 하셨었는데,
아.. 나만하던 고민이 아니였구나 라는걸 깨닫고, 이 질문에 대해 다시한번 생각해보게되었다.</p>
<p>그래서 지금 현재 나의 생각은..</p>
<p>*<em>그치, 한번에 조회하는게 connection차지도 덜하고 좋지만, 
하지만 그것이 협업을 할때 유지보수에 있어 지대한 영향을 준다면 성능보다 더 앞으로 크게 작용을 할 수 있겠구나 *</em></p>
<p>이다.</p>
<hr>
<p>앞으로의 나의 생각이 어떻게 변하게 될지 나도 궁금하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[동기 비동기 그리고 async await..]]></title>
            <link>https://velog.io/@ssunn_ni/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B7%B8%EB%A6%AC%EA%B3%A0-async-await</link>
            <guid>https://velog.io/@ssunn_ni/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EA%B7%B8%EB%A6%AC%EA%B3%A0-async-await</guid>
            <pubDate>Wed, 11 Jun 2025 02:32:35 GMT</pubDate>
            <description><![CDATA[<h3 id="1-들어가기-앞서서">1. 들어가기 앞서서..</h3>
<p>들어가기 앞서서..</p>
<p>처음 공부는 java spring으로 입문을 해서, 
회사에 들어가니 바로 javascript nodejs를 급하게 익혀서 사용해야했어서, 언어나 프레임워크의 이해도가 다소 떨어진다 느껴저서 강의를 보고....했지만.. 
따로 공부를 해도 사실 크게 체감이 되는 부분이 없어서</p>
<p>부끄럽지만 그동안 나는 단순하게</p>
<blockquote>
<p>동기 = 순서대로진행
비동기 = 처리순서가 빠른대로 진행
async await = 비동기 환경에서 동기처럼 처리하기위한 것</p>
</blockquote>
<p>이렇게만 생각을 해왔다..</p>
<h3 id="2-발단">2. 발단</h3>
<p>이번에 python을 사용해서 프로젝트를 진행하게 되어, 맞는 프레임워크를 찾아보다가
fastAPI를 입문을 하게 되었다.</p>
<p>python이 느린건.. 사실 코테공부를 python으로 했어서 느린건 익히 알고있었는데,
fastAPI의 장점이 Django나 다른 친구들보다 빠르고 간편하게 사용할 수 있다는 점이 와닿아 사용을 하게 되었는데( 급하게 시작을했어야해서 새로운 프레임워크 공부에 큰 시간을 쓸 수가 없었다)</p>
<p>사용을 하다보니 알겠더라..</p>
<p>FastAPI의 장점중 하나 &quot;빠르다&quot;가 async await을 지원한다 라는게 하나의 주요 원인중 하나였는데,</p>
<p>처음에 너무 <strong>단순</strong>하게 생각했던 나는..</p>
<p><em><strong>어차피 함수 앞에 async안붙이면 javascript랑은 다르게 동기함수가 되는건데,
비동기로 갈거  아니면 async 붙일 필요가 있어..? 라는 오만한 생각을 하게 된다..</strong></em></p>
<p>그렇게 코드를 완성하고.. 서버 api막 만들어서 프론트랑 작업을 하고 배포도 해서 테스트를 하는데..
한 부분이 느리면.. <strong>전체가 멈춰버리는 것이다..</strong></p>
<h3 id="3-전개">3. 전개</h3>
<p>아니.. 자바스크립트에서는 이런적이없는데.. 여긴 기본이 동기여서그런가..? 
하고 원인을 분석하다가</p>
<p>문뜩, async await쪽을 건들여보기 시작했다..
그러다보니 문뜩.. NodeJS공부할때 EventLoop쪽이 생각이났다..
공부했을때는.. </p>
<p>아.. 그냥 그렇구나~ 저래서 동기처럼 작동을 하는구나~
저래서 빠르구나~~  </p>
<p>하고 그냥 달달암기만 하고있었는데.. 이번에 뽝 느낌이 왔다.. 이거였구나..</p>
<h3 id="4-깨달음">4. 깨달음</h3>
<p>결론부터 얘기하자면</p>
<blockquote>
<p><strong>동기는 결과를 “기다리면서 멈추는 것”이고, 
await는 “기다리지만 멈추지 않고 다른 일을 할 수 있게 하는 것”</strong></p>
</blockquote>
<p>async await가 동기&quot;처럼&quot; 에 <strong>&quot;처럼&quot;</strong>을 깨닫는 순간이였다..</p>
<p>그러니까, fastapi의 장점중에 &quot;빠르다&quot; 가
비동기를 지원하기때문에, await를 사용하면 동기처럼 행동해도 다른곳에서 멈추지 않는다
즉, 동기만 가능한 프레임워크보다 빠르다 였구나 라는걸 한번에 확 체감 가능한 순간이였다..</p>
<p>그동안.. 자바스크립트 await이거 고생을 엄청해서.. 조금 미워했는데..
너에겐 큰 뜻이 있었구나..</p>
<h3 id="5-그래서">5. 그래서..</h3>
<p>그래서 시간이 오래걸리고, 굳이 async를 붙이지 않게해서 동기로 만든 함수들
전부 async붙이고 await사용해서 수정을했고,</p>
<p>그렇게 처리가 오래걸림으로 인해, 서비스가 멈추던 모든 이슈들이 해결되었답니다.. ㅎ</p>
<p><del>나는 진짜.. 바본가보다.. 아무리 공부해도 겪어봐야.. 아 이게 그거구나.. 하고있다 ㅠㅠ</del></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[langchain에서 openai토큰수 받기 고생기]]></title>
            <link>https://velog.io/@ssunn_ni/langchain%EC%97%90%EC%84%9C-openai%ED%86%A0%ED%81%B0%EC%88%98-%EB%B0%9B%EA%B8%B0-%EA%B3%A0%EC%83%9D%EA%B8%B0</link>
            <guid>https://velog.io/@ssunn_ni/langchain%EC%97%90%EC%84%9C-openai%ED%86%A0%ED%81%B0%EC%88%98-%EB%B0%9B%EA%B8%B0-%EA%B3%A0%EC%83%9D%EA%B8%B0</guid>
            <pubDate>Mon, 19 May 2025 06:39:18 GMT</pubDate>
            <description><![CDATA[<p>이번에 llm관련 프로젝트를 맡아서 하게된 이후로..
langchain에서 gpt토큰수를 받기위해 고군분투를 했던 경험기이다..
그동안 새로운거 공부하고 적용하고 일하느라 고생기가 많이 적을것이 많았는데.. 
생각나는것 먼저 작성해본다..</p>
<h3 id="1-langchain에서-지원하는-callback매니저를-사용해보기">1. langchain에서 지원하는 callback매니저를 사용해보기.</h3>
<p>무슨이유에서인지  나는 예제대로 해도  리턴받은 토큰소모량이 전부 0으로 나왔다.
그래서 이리저리 막 검색하고 해봤는데 결국.. 실패했었다..
더욱이 이때는 이제 막 langchain에 입문했던 시기였기에 다음단계로 넘어가게 되었다.</p>
<h3 id="2-ticktoken-라이브러리-사용하기">2. Ticktoken 라이브러리 사용하기.</h3>
<p>해당 라이브러리는 openai에서 제공해주는 토큰세기 라이브러리여서
이걸사용하면 되겠다~ 하고 사용하게 되었다.</p>
<p>정말 편하고 좋았는데, 문제가있다면.. 아무리 최신을 해도 o1시리즈같은건 없는 모델이라고.. 떠서
내가 임의지정한 default모델의 토큰으로 세진다는 거였다.. 정확성이 떨어졌지만 이때는 우선 이방식을 사용했다.</p>
<h3 id="3-return값으로-받았다">3. Return값으로 받았다..</h3>
<p>프로젝트가 어느정도 진행되고, langchain에 익숙해지고 langgraph도 사용해봤을 때,
1번을 다시 시도하였으나.. 실패를 했다.. <del>공식문서랑 검색이랑 진짜 다해봤는데 나한테 왜그래 ㅠㅠ</del></p>
<p>무튼 그래서 2번상태 유지중일때 의외에 곳에서 해결이 되었는데..
langchain을 주로 사용하다보니, gpt에 endpoint를 나는 잘 살펴볼 일이 없었다.
근데, 잘 만들어서 이것저것 테스트하다가 보니, o1-pro모델, o1-mini등등 
<strong>o시리즈에서 에러가 나는 것이었다...</strong></p>
<p>첫번째로는, 라이브러리랑 하나하나 뜯어 보니까 langchain자체 모듈에서 o시리즈일때, role을
developer라고 변경하는 부분이 있었는데.. o1에서는 또 자긴 그런 role없다고 에러가 났고..</p>
<p>두번째로는, langchain에서 기본적으로 invoke혹은 stream할때 사용하는 endpoint
<em><strong>v1/chat/completions</strong></em>  o1-pro 나 o1-mini에서는 제공이 안된다는 것이였다..</p>
<hr>
<p>그래서 이리저리 테스트와 검색을하다가</p>
<pre><code>  chat_model = ChatOpenAI(   
              model= 모델명,
            ...
            use_responses_api=True //이거
        )</code></pre><p>모델정의부분에서 use_responses_api = True를 설정하면, endpoint가 <strong>v1/responses</strong>
로 변경되어 나갈 수 있게 된다는 사실을 알았고, 이 방법을 사용했더니 글쎄..</p>
<p>.
.
*<em>return값으로.. 사용토큰값이 전달되었다.. *</em>
이렇게.. 사용한 토큰값 리턴받기 성공...</p>
<p>진짜 하면 할수록 느끼는건데 정말 엄청빨리 새로운게 나오고 또 변하는구나 싶다..</p>
<hr>
<h3 id="4-기타">4. 기타</h3>
<p>langgraph에서 openai시에 중간 agent응답에 대한 토큰값이 안오기 시작했다.</p>
<p>그래서 이것저것 해보다가 혹시나 하고 확인을 했더니..</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>토큰 수</th>
</tr>
</thead>
<tbody><tr>
<td>return값</td>
<td>4541</td>
</tr>
<tr>
<td>langsmith</td>
<td>4957</td>
</tr>
<tr>
<td>openai 확인</td>
<td>4541</td>
</tr>
</tbody></table>
<p>로.. langsmith의 토큰값이 안맞는다는 사실을 알았다..</p>
<p>혹시나 싶어서 다른 모델들도 각 홈페이지에서 실 토큰 사용량과 langsmith값 그리고 return값의 토큰 사용량을 비교해봤는데 미묘하게 살짝씩 다르다는걸 발견했다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[한빛미디어 NLP와 LLM실전 튜토리얼 세미나 후기]]></title>
            <link>https://velog.io/@ssunn_ni/%ED%95%9C%EB%B9%9B%EB%AF%B8%EB%94%94%EC%96%B4-NLP%EC%99%80-LLM%EC%8B%A4%EC%A0%84-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EC%84%B8%EB%AF%B8%EB%82%98-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@ssunn_ni/%ED%95%9C%EB%B9%9B%EB%AF%B8%EB%94%94%EC%96%B4-NLP%EC%99%80-LLM%EC%8B%A4%EC%A0%84-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EC%84%B8%EB%AF%B8%EB%82%98-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Thu, 10 Apr 2025 12:52:25 GMT</pubDate>
            <description><![CDATA[<p>최근 회사에서 LLM프로젝트를 담당하고있고, 기간이 넉넉치 않아서 머리싸메며 공부하면서 작업을 하고 있었는데
마침! 관련된 세미나가 있어서 다녀왔다.
(나에게 너무 필요해 ㅠㅠ)</p>
<hr>
<p><img src="https://velog.velcdn.com/images/ssunn_ni/post/d7799c98-09a8-4b7d-8c3b-9bbe59a99959/image.png" alt=""></p>
<hr>
<ul>
<li><p>책 한권을 다 놓고 돌렸을때 gpt 3.5로는 3달러, 4o mini로는 0.4달러(...?)였다고 한다.. 너무신기.. (구현에만 치우쳐서 가격을 몰랐는데 생각보다 저렴한걸..?)</p>
</li>
<li><p>api들 쓰면 다들 말 안듣는건 같구나..</p>
</li>
<li><p>가끔 코드를 llm에게 예시코드를 받기위해서 물어보면, 예전코드를 줄 때가 있다.
이때는,  <a href="https://aistudio.google.com/prompts/new_chat">https://aistudio.google.com/prompts/new_chat</a> 이와같은 사이트에 있는 코드 예제를 던져서 해당 버전으로 해달라고 하면 훨씬 최신의 정보를 얻을 수 있다.</p>
</li>
<li><p>크롤링코드 요청시, 응답, 헤더 등의 다양한 소스를 주고 요청을 하자.</p>
</li>
<li><p>hugging face : 학습시킨 모델을 공유하는 플렛폼</p>
</li>
<li><p>데이더셋의 용량이 크다면 hugging에 올려서 사용해보자.</p>
</li>
<li><p>hugging에서 한국어지원하는 모델 찾고싶다면..?
다른 한국어 모델 찾아보기 : <a href="https://huggingface.co/models?search=korean">https://huggingface.co/models?search=korean</a></p>
</li>
<li><p>RDBMS(답변 그때그때 저장용) + 벡터DB 를 사용해서 프로젝트를 하셨다고..</p>
</li>
<li><p>너무 긴 문단은 langchain chunk spliter이용해보자.</p>
</li>
<li><p>한국어 embedding 모델 </p>
<pre><code>embeddings = HuggingFaceEmbeddings(model_name=&quot;upskyy/e5-small-korean&quot;)</code></pre></li>
<li><p>vectorDB로 검색하면 정확도는 떨어진다(유사도 검색이기 때문)</p>
</li>
<li><p>embedding projector에서 유사도 ui보기 쉽다.
<a href="https://projector.tensorflow.org">https://projector.tensorflow.org</a>
<img src="https://velog.velcdn.com/images/ssunn_ni/post/498b439c-f5ac-415d-bebd-2f4b00e4aa32/image.png" alt=""></p>
</li>
</ul>
<ul>
<li><p>벡터화(텍스트 숫자변환)랑 임베딩(의미포함 숫자변환)은 다르구나..</p>
</li>
<li><p>임베딩할때 참고하면 좋은 사이트(시각화)
<a href="https://damien0x0023.github.io/rnnExplainer/">https://damien0x0023.github.io/rnnExplainer/</a></p>
</li>
<li><p>오.. 벡터 토큰화 하는코드 ..</p>
<pre><code># prompt: texts 내용 tf-idf로 토큰화
</code></pre></li>
</ul>
<p>from sklearn.feature_extraction.text import TfidfVectorizer</p>
<p>tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(texts)</p>
<h1 id="tf-idf-행렬-출력">TF-IDF 행렬 출력</h1>
<p>print(tfidf_matrix.toarray())</p>
<h1 id="tf-idf-어휘-목록-출력">TF-IDF 어휘 목록 출력</h1>
<p>print(tfidf_vectorizer.get_feature_names_out())</p>
<p>[[0.         0.         0.         ... 0.46383605 0.         0.        ]
 [0.         0.         0.         ... 0.         0.         0.        ]
 [0.         0.         0.         ... 0.36661165 0.         0.        ]
 ...
 [0.         0.         0.         ... 0.         0.         0.        ]
 [0.         0.         0.         ... 0.         0.         0.        ]
 [0.         0.         0.         ... 0.         0.         0.        ]]
[&#39;20달러로&#39; &#39;2판&#39; &#39;agi의&#39; &#39;ai&#39; &#39;ai를&#39; &#39;api를&#39; &#39;cs&#39; &#39;git&#39; &#39;github&#39; &#39;gpt&#39; &#39;llm&#39;
 &#39;mysql이다&#39; &#39;nlp와&#39; &#39;sql&#39; &#39;with&#39; &#39;가이드&#39; &#39;가이드북&#39; &#39;개발&#39; &#39;개발을&#39; &#39;개발자&#39; &#39;개발자를&#39; &#39;게임&#39;
 &#39;견고한&#39; &#39;고작&#39; &#39;공략집&#39; &#39;공부하는&#39; &#39;과학이다&#39; &#39;구조&#39; &#39;권으로&#39; &#39;그림으로&#39; &#39;글쓰기&#39; &#39;기술&#39; &#39;네트워크&#39; &#39;노트&#39;
 &#39;다섯&#39; &#39;대신&#39; &#39;데이터&#39; &#39;도메인&#39; &#39;도커&#39; &#39;디자인&#39; &#39;딥러닝&#39; &#39;라이브러리를&#39; &#39;러스트&#39; &#39;리팩터링&#39; &#39;만렙&#39; &#39;말을&#39;
 &#39;매니지먼트&#39; &#39;머신러닝&#39; &#39;면접&#39; &#39;명이&#39; &#39;모두를&#39; &#39;몽고db&#39; &#39;믿어요&#39; &#39;밑바닥부터&#39; &#39;배우는&#39; &#39;분석&#39; &#39;비밀&#39;
 &#39;비즈니스&#39; &#39;사토시의&#39; &#39;선형대수학&#39; &#39;소프트웨어&#39; &#39;수학&#39; &#39;시대&#39; &#39;시대의&#39; &#39;시작하기&#39; &#39;시작하는&#39; &#39;실무로&#39; &#39;실전&#39;
 &#39;알고리즘이다&#39; &#39;애저&#39; &#39;양자&#39; &#39;어떻게&#39; &#39;언어&#39; &#39;업무&#39; &#39;엑셀&#39; &#39;엔지니어&#39; &#39;엔지니어링&#39; &#39;완벽&#39; &#39;운영체제&#39; &#39;위한&#39;
 &#39;이것이&#39; &#39;이다&#39; &#39;이지&#39; &#39;인공지능&#39; &#39;인과추론&#39; &#39;일잘러의&#39; &#39;일타강사의&#39; &#39;자동화하기&#39; &#39;자료구조&#39; &#39;자바&#39; &#39;전문가를&#39;
 &#39;제대로&#39; &#39;주도&#39; &#39;직장인&#39; &#39;챗gpt&#39; &#39;챗gpt와&#39; &#39;처음&#39; &#39;첫걸음&#39; &#39;취업을&#39; &#39;컴퓨터&#39; &#39;코딩&#39; &#39;쿠버네티스&#39;
 &#39;테스트다&#39; &#39;통하는&#39; &#39;파이썬&#39; &#39;파이썬으로&#39; &#39;패턴&#39; &#39;퍼스트&#39; &#39;프로그래머&#39; &#39;프로그래밍&#39; &#39;프로덕트&#39; &#39;플랫폼&#39; &#39;필수&#39;
 &#39;핸즈온&#39; &#39;헤드&#39; &#39;혼자&#39; &#39;활용&#39; &#39;활용한&#39;]</p>
<pre><code>* 보통 유사도를 검색할때는, cos방식으로 사용한다(거리)방식 
-&gt; 사실이게 추천시스템이다.

* 차원축소 : 모델압축 및 모델 학습시에 주로 활용됨 (통계기법)
* 임베딩시 차원이 크면 클수록 좋지만, 모델이 계산할때 오래걸린다.
* 임베딩 모델에서 사용하는 모델을 그대로 쓰자~
* 여러문서를 llm에서 쓰고싶다면 notebookllm을 사용하자.

---

### 후기
* 그동안 내가 혼자서 했던것들이 영 틀린것은 아니였구나 하고 뿌듯..
* 흥미로운 부분도 새로운 부분도 많았다(특히 통계기법등)
* 정말 궁금한 부분이 사실 들어서 해결되지는 않았지만, 들으면 들을수록 답이 없구나.. 생각이 들었다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[nginx에서의 ip별 limit 설정하기]]></title>
            <link>https://velog.io/@ssunn_ni/nginx%EC%97%90%EC%84%9C%EC%9D%98-ip%EB%B3%84-limit-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@ssunn_ni/nginx%EC%97%90%EC%84%9C%EC%9D%98-ip%EB%B3%84-limit-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 24 Feb 2025 06:11:12 GMT</pubDate>
            <description><![CDATA[<p>어느날 log를 보는데 대량의 api가 같은 ip로(해외) 난사가 되어있는걸 발견했다.</p>
<p>beta같은 환경은 이미 nginx에서 ip제한을 걸어두었는데,
live환경에서는 ip제한을 걸어두기가 애매해서 어떻게 할까 하다가 nginx에서 시간당 ip별 횟수제한을 걸 수 있는 방법을 찾아서 시도해보았다.</p>
<h3 id="1-설정하기">1. 설정하기</h3>
<p><code>nginx.conf</code> 또는 해당 서버의 설정 파일에서 아래와 같이  설정한다.</p>
<pre><code class="language-nginx">http {
    # IP 기반 요청 제한 설정 (초당 4번 허용)
    limit_req_zone $binary_remote_addr zone=perip:10m rate=4r/s;

    server {
        listen 80;
        server_name example.com;

        location / {
            # IP 기반 요청 제한 적용, 초당 4회 요청 이상 시 503 응답
            limit_req zone=perip burst=4 nodelay;

            proxy_pass http://backend;
        }
    }
}</code></pre>
<blockquote>
<p><code>limit_req_zone $binary_remote_addr zone=perip:10m rate=4r/s;</code></p>
</blockquote>
<ul>
<li><code>$binary_remote_addr</code>: 클라이언트의 IP 주소를 기준으로 제한.</li>
<li><code>zone=perip:10m</code>: 공유 메모리 영역(perip)을 10MB 크기로 설정(약 16만 개의 IP 저장 가능).</li>
<li><code>rate=4r/s</code>: 초당 4번의 요청만 허용.</li>
</ul>
<blockquote>
<p> <code>limit_req zone=perip burst=4 nodelay;</code></p>
</blockquote>
<ul>
<li><code>burst=4</code>: 초과 요청을 최대 4개까지 허용하고 이후부터는 제한.</li>
<li><code>nodelay</code>: burst 허용 내에서도 즉시 처리(지연 없음) / 
 nodelay가 없다면 초과 요청이 발생하면 순차적으로 대기 후 처리된다.</li>
<li>요청이 <code>4r/s</code>를 초과하면 <code>503</code>(서비스 사용 불가) 응답.</li>
</ul>
<hr>
<p>이렇게 설정해주면 된다.. (from GPT)</p>
<h3 id="이후-이슈">이후 이슈</h3>
<p>이렇게 설정을 했더니.. 해당 도메인을 타고오는 모든 url에서 ip제한이 걸리기 시작했다..
대표적으로.. 프론트화면도 이미지같은것 렌더링시 지연 혹은 에러 이슈가 발생했다.</p>
<p>그래서, 처음에 이것저것 설정을 해보았고 결과적으로 서버에 오는것만 ip제한을 걸도록 범위를 구체화하였다.</p>
<pre><code class="language-nginx">    location / {
        limit_req zone=perip burst=4 nodelay;
        proxy_pass http://backend;
    }</code></pre>
<p>이렇게 설정하면, 해당 도메인 하위단의 주소 모두 ip제한이 걸려서</p>
<pre><code>    location /api {
        limit_req zone=perip burst=4 nodelay;
        proxy_pass http://backend_api;
    }
}</code></pre><p>이렇게, 만약 서버가로 가는 주소가 /api 라 한다면.. 
위에처럼 서버주소로 가는 주소들만 ip제한이 걸리도록 수정했다.</p>
<hr>
<p>이렇게 설정하면, 부하도 방지할 수 있고, ip제한도 걸 수 있다.
설정하지 않았을 때, default값은 서버가 처리할수 있는 양만큼의 값들을 모두 수용한다고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mysql 프로시저.. 묵시적 형변환을 조심하자..]]></title>
            <link>https://velog.io/@ssunn_ni/mysql-%ED%94%84%EB%A1%9C%EC%8B%9C%EC%A0%80..-%EB%AC%B5%EC%8B%9C%EC%A0%81-%ED%98%95%EB%B3%80%ED%99%98%EC%9D%84-%EC%A1%B0%EC%8B%AC%ED%95%98%EC%9E%90</link>
            <guid>https://velog.io/@ssunn_ni/mysql-%ED%94%84%EB%A1%9C%EC%8B%9C%EC%A0%80..-%EB%AC%B5%EC%8B%9C%EC%A0%81-%ED%98%95%EB%B3%80%ED%99%98%EC%9D%84-%EC%A1%B0%EC%8B%AC%ED%95%98%EC%9E%90</guid>
            <pubDate>Fri, 14 Feb 2025 07:18:26 GMT</pubDate>
            <description><![CDATA[<p>이번에 서버 업데이트를 하나 했는데, 
갑자기 엉뚱한 부분에서 에러가 터졌다.</p>
<p>그저 프로시저실행하는 코드고 해당 코드는 건든적이 없었는데..
프로시저 안에 일이기 때문에 아무리 디버깅을 해봐도 소용이 없었다..</p>
<p>이리 저리 로그를 찍어보다가...  깨달았는데</p>
<p>&quot;1234455673939&quot;이런식으로 <strong>string</strong>으로 전달되었던 인자가 프로시저 내부에서
&quot;1.xxxxxx&quot;형태의 <strong>부동소수점형태</strong>로 변환되어 전달되고 있었다..</p>
<h2 id="1-문제-원인">1. 문제 원인</h2>
<p>해당 내용에 대해 gpt에게 물어보니
<img src="https://velog.velcdn.com/images/ssunn_ni/post/53f39c8a-5bb0-43e1-80fc-33291bf43483/image.png" alt=""></p>
<p>이러한 대답을 받을 수 있었다.. 
내부적으로 mysql에서 <strong>숫자</strong>형태의 varchar을 <strong>부동소수점방식</strong>으로 변환한다는것이다..</p>
<h2 id="2-해결방법">2. 해결방법</h2>
<p>mysql의 묵시적 형 변환에 대해 구글링을 해봐도, 요즘에 프로시저를 잘 사용하지 않는것인지
많은 검색결과를 얻을 수 없었다.</p>
<p>그래서 gpt에게 다시 물어본 결과</p>
<hr>
<p>✅ 방법 1: 명확하게 문자열로 감싸기
쿼리 실행 시 강제로 문자열로 취급하도록 따옴표(&#39;&#39;)를 사용하세요.</p>
<pre><code>INSERT INTO my_table (my_varchar_column) VALUES (&#39;109958166097066644827&#39;);
</code></pre><p>✅ 방법 2: CAST() 또는 CONVERT() 사용하기
MySQL에서 명시적으로 VARCHAR로 변환하여 저장</p>
<pre><code>INSERT INTO my_table (my_varchar_column) VALUES (CAST(109958166097066644827 AS CHAR));
INSERT INTO my_table (my_varchar_column) VALUES (CONVERT(109958166097066644827, CHAR));
SET my_varchar_column = CAST(my_varchar_column AS CHAR);

</code></pre><hr>
<p>의  대답을 얻을 수 있었다.</p>
<p>처음엔 프로시저를 먼저 고쳐봤다.
인자를 받을 varchar을 64 -&gt; 255 로 늘려도보고, 2번의 변환도 시도해보았다.</p>
<p>다만 문제는.. 프로시저에 인자를 넘어올때 <strong>이미 mysql에서 변환을 해서</strong> 넘어와서, 
프로시저 내부에서 변환을 해도 <strong>이미 변환된 값을 string형태</strong>로 받을 뿐이였다.</p>
<p>그래서 다음으로 방법1을 선택을 했지만.. 
해당 방법또한 나에게 는 해결방법이 되어주지 않았다.</p>
<h2 id="3-결론">3. 결론</h2>
<p>그래서 마지막으로 나랑 사수님과 같이 해결방법을 모색하였는데..</p>
<blockquote>
<ol>
<li>문제가 되는 부분이 첫문단이니, 첫 문단을 프로시저에서 분리하자.</li>
<li>숫자형태가 문제라면, base64로 값을 변환해서 값을 보내고, 프로시저내부에서 풀어서 해결</li>
</ol>
</blockquote>
<p>이 중에서사실 어떤걸 먼저해도 상관없었지만..
기존의 프로시저 형태는 계속 가져가고싶어서 2번을 택하였고 (1번을 최후통첩으로 남겨놓았다)</p>
<p>2번방법은 동작하였다.</p>
<h2 id="4-마지막으로">4. 마지막으로..</h2>
<p>아직도 해결되지 않은 의문점이 있는데..
<img src="https://velog.velcdn.com/images/ssunn_ni/post/0bbb814c-814a-481d-9dd0-494359ed6aab/image.png" alt=""></p>
<p>서버를 껐다 키면 또 프로시저 내부에서 묵시적 형 변환이 일어나지 않는다..
처음엔 <strong>30분</strong>정도 있다가부터 에러가 났고,</p>
<p>그 다음에  2번 해결과정의 
gpt가 말해준 1번 방법(명확하게 string형태로 바인딩하기) 으로
인자를 바인딩할때 String()으로 형태를 지정해서 넘겨눴다니 <strong>4시간</strong> 이후부터 에러가 나기 시작했다.</p>
<p>그래서 문제를 해결할때 진짜 해결이 된건지 테스트하기가 정말.. 어려웠다...
소스코드 하나만 바꿔서 테스트할라해도 몇시간을 기다려야하니..</p>
<p>정말 왜 그런거지..? 
gpt에 물어보면 세션을 날려보던지 해봐라 그러던데.. 음..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[첫 오픈소스 도전기! (Open Contribution Jam 2024)]]></title>
            <link>https://velog.io/@ssunn_ni/Open-Contribution-Jam-2024</link>
            <guid>https://velog.io/@ssunn_ni/Open-Contribution-Jam-2024</guid>
            <pubDate>Wed, 27 Nov 2024 14:57:39 GMT</pubDate>
            <description><![CDATA[<p>다양한 컨퍼런스를 다니며, 오픈소스에 관해서 관심이 많이 생겼었던것 같다.
GDC에서 하는 오픈소스 컨퍼런스를 깜빡하고 신청하지 못해 절망하던와중에,
오픈소스를 무려 참여까지 도와주는 <strong>Open Contribution Jam 2024</strong> 를 발견하고 곧바로 신청하게 되었다.</p>
<hr>
<h3 id="-이번에-참여한-오픈소스">* 이번에 참여한 오픈소스</h3>
<p><img src="https://velog.velcdn.com/images/ssunn_ni/post/feeea8e1-4b4b-42bc-8b03-5d95639497bb/image.png" alt=""></p>
<p>해당 오픈소스는 ActivityPub 및 기타 표준(소위 fediverse) 으로 구동되는 연합 서버 앱을 구축하기 위한 TypeScript 라이브러리이다.
패더버스라는 개념도 activity pub이란 개념도 매우 생소하였지만 꽤나 흥미로워서 해당 오픈소스로 도전하게 되었다.
<a href="https://fedify.dev/">홈페이지</a> <a href="https://github.com/dahlia/fedify">Github</a></p>
<hr>
<h3 id="-후기">* 후기</h3>
<p>처음 오픈소스에 도전해보는거고, 사용을 안해본건 아니지만 대부분 내가 사용했던것들은 이미 많은 레퍼런스들이 있던 것들이였어서 참고할것들이 많았었는데 문서 하나하나 뜯어보는건 굉장히 낮선 경험이였고 또 한편으로는 진짜 메인테이너분 5년차라고 들었는데 대단하다.. 라는 생각밖에 들지 않았다.</p>
<p>내가 정말 엄청난것을 기여한것도 아니고 그저 쉬운 이슈들을 메인테이너분의 도움을 받아서..
하나하나해보고 PR을 올려 머지까지 해보았는데, 색다른 기분이 들었다.</p>
<p><img src="https://velog.velcdn.com/images/ssunn_ni/post/306ab403-1c82-4474-9ba4-a880e116fdfa/image.png" alt=""></p>
<p>메인테이너분의도움으로 PR도 해보았는데 이렇게 내 이름도 릴리즈 노트에 작성해주시다니..
너무 감사했습니다...!! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모니터링 구축하기 ( Grafana + Prometheus + query-exporter + node-exporter )]]></title>
            <link>https://velog.io/@ssunn_ni/%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-%EA%B5%AC%EC%B6%95-Grafana-Prometheus-query-exporter-node-exporter</link>
            <guid>https://velog.io/@ssunn_ni/%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-%EA%B5%AC%EC%B6%95-Grafana-Prometheus-query-exporter-node-exporter</guid>
            <pubDate>Wed, 27 Nov 2024 07:20:01 GMT</pubDate>
            <description><![CDATA[<p>이전에는 그냥 로깅만 하면 되겠지 했었는데, 시간이 지나고 사이즈가 커지고
그와중에 서버비용의 최적화를 위해 서버스팩을 줄이다보니.. 점점 알 수 없는 것들이 생겼다.</p>
<p>우아콘에 컨퍼런스에 갔을 때, 항상 고민이였던 모니터링툴에대해 듣게 되었고 검색했고
한번 해볼만 하다는 생각이 들어 시도하게 되었다.</p>
<p>구축할 환경은 
실제 코드들이 돌아가는 서버들, 그리고 모니터링 서버 하나이다.</p>
<p>처음에 가장 헷갈렸던것들이 어떤걸 어느 서버에 깔아야하는가였지만
여러번 삽질과.. 시도 끝해 어느정도 감을 잡은 것 같다.</p>
<p>query-exporter, node-exporter 와 같은 관측을 위한 것들은 모니터링 대상 서버에,
Grafana, Prometheus 와 같은 모니터링을 위한 도구는 모니터링서버에서 실행시키면 된다.</p>
<blockquote>
<p><strong>로그</strong>는 서버가 동작할 때 서버의 상태와 동작 정보를 시간 경과에 따라 기록된 결과입니다. 로그는 시스템의 오류와 문제들을 쉽게 찾아낼 수 있도록 도와줍니다. 
반면, <strong>메트릭</strong>은 시스템의 성능과 상태에 대한 통계적인 정보를 의미합니다. 메트릭을 잘 수집하면 시스템의 현재 상태를 손쉽게 파악할 수 있고, 사업 현황에 관한 유용한 정보를 얻을 수 있습니다. 가령, 메트릭은 DAU, Retension, CPU 사용량, 메모리 사용량 등이 있습니다.</p>
</blockquote>
<p><del>아직은 베타에서 테스트중이라 완벽하게 정리되면 마저 작성하겠다.</del>
.</p>
<hr>
<h3 id="1-모니터링-서버에-그라파나-프로메테우스-로키-파일을-설정해주기">1. 모니터링 서버에 그라파나, 프로메테우스, 로키 파일을 설정해주기.</h3>
<p>소스를 받아서, 각자의 환경에 맞는 셋팅을 해주기
readme를 (한국어로도 있음) 통해서 알맞게 셋팅을 해준다.
<a href="https://github.com/Heesunni/grafana_loki_prometheus">https://github.com/Heesunni/grafana_loki_prometheus</a></p>
<p>이후에 잘 켜졌는지각각 확인</p>
<pre><code>http://localhost:3003        //그라파나 확인
http://localhost:9100        //프로메테우스 확인
http://localhost:3100/ready //로키 확인</code></pre><p>별다른 포트를 설정하지 않았다면 위의 포트들이 기본 포트이다.</p>
<p>이후에, 그라파나에 들어가서</p>
<pre><code>Home &gt; Connections &gt; Add new connection</code></pre><p>에 들어가 Loki, Prometheus를 각각 연결해주고( url 각각을 킨 주소를 작성하면 됨),
하단에 Save&amp;test까지 눌러서 통과하면 끝!</p>
<h3 id="2-관측할-서버에-promtail-exporter을-심어서-실행시켜주기">2. 관측할 서버에 promtail, exporter을 심어서 실행시켜주기</h3>
<p>관측대상 서버에 promtail, exporter을 알맞게 실행시킨다.</p>
<p>원래는 각각의 프로그램들을 따로 다운받아서 켜야하는데, 나의 경우 다 다운받아서 한 파일에 정리해두고 다른 서버에서도 같은 설정으로 사용하려고 이렇게 만들어놨다.</p>
<p>다른 블로그들을 보면 systemctl이나 service등록해서 사용하던데 
나는 pm2로 프로그램들을 켰다.</p>
<p><a href="https://github.com/Heesunni/promtail-exporter">https://github.com/Heesunni/promtail-exporter</a></p>
<hr>
<h3 id="3-결론">3. 결론</h3>
<p>그라파나에서 제공해주는 예시 대시보드들로 mysql, linux서버, nodejs 모니터링을 구축을 했고, loki는 원하는 대시보드가 없어서 한참 만지작만지작 거리다가 gpt와 구글링으로 대시보드를 하나 내가 직접 구축했고, 지금은 회사에 큰 모니터에 띄어져있다 ㅎ
아직 계속 연구중이지만 하나하나 대시보드가 업데이트가 되어가는중</p>
<p>아직 모르는거 투성이라 계속 연구중이지만, 회사에 띄어져있는 대시보드를 보면 뿌듯하다 ㅎ</p>
<p><img src="https://velog.velcdn.com/images/ssunn_ni/post/4a832584-2b91-4bc4-be31-2c5043f46bee/image.png" alt="">
<del>이건 로키이용해서 쌓인 log분석해주는거 만들었다</del></p>
<hr>
<p>++추가내용</p>
<blockquote>
<p>docker로 그라파나, 로키, 프로메테우스를 작동시켰었는데.. 어느순간부터 용량이슈가 발생했다.</p>
</blockquote>
<pre><code>sudo du -sh /var/lib/docker/containers/* | sort -h</code></pre><p>해당 명령어를 통해, docker에서 어떤파일이 가장 많은 용량을 차지하는지 확인을 해봤는데
로키의 로그파일이 21기가나 먹고있었다..</p>
<p>우선 불필요한 내용들을 삭제해주고</p>
<pre><code>
// docker 컨테이너별 Root FS 사용량 확인하기
docker system df

// docker 전체 정리( 사용하지 않는정보 정리)
docker system prune -af                

// 사용하지 않는 볼륨 확인
docker volume ls -f dangling=true   

//사용하지 않는 볼륨 삭제
docker volume prune -f                

// 로그파일 모두 삭제
for log in $(docker inspect --format=&#39;{{.LogPath}}&#39; $(docker ps -aq)); do
  sudo truncate -s 0 &quot;$log&quot;;
done
</code></pre><p>용량제한 설정 docker.yml파일에 추가하고, </p>
<pre><code>--storage.tsdb.retention.time=90d
--storage.tsdb.retention.size=5GB</code></pre><p>도커 데몬 설정도 변경</p>
<pre><code>sudo nano /etc/docker/daemon.json

{
  &quot;log-driver&quot;: &quot;json-file&quot;,
  &quot;log-opts&quot;: {
    &quot;max-size&quot;: &quot;10m&quot;,
    &quot;max-file&quot;: &quot;3&quot;
  }
}

sudo systemctl restart docker
</code></pre><hr>
<p>[참고링크] </p>
<p><a href="https://solo5star.dev/posts/50/">사용자 수 모니터링 빠르게 시작하기 (Grafana + Prometheus)</a>
<a href="https://brunch.co.kr/@hansungdev/36">Prometheus/Grafana 모니터링 구축</a>
<a href="https://velog.io/@hansung/PrometheusGrafana-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-%EA%B5%AC%EC%B6%95">Prometheus/Grafana 모니터링 구축</a>
<a href="https://velog.io/@sojukang/%EC%84%B8%EC%83%81%EC%97%90%EC%84%9C-%EC%A0%9C%EC%9D%BC-%EC%89%AC%EC%9A%B4-Prometheus-Grafana-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-%EC%84%A4%EC%A0%95">세상에서 제일 쉬운 Prometheus - Grafana 모니터링 설정</a>
<a href="https://medium.com/naver-biz-dev/node-js-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B5%AC%EC%B6%95%EA%B3%BC-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-%EC%82%AC%EB%A1%80-part-1-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81-%ED%99%98%EA%B2%BD%EA%B5%AC%EC%B6%95-b91246d9977f">Node.js 모니터링 시스템 구축과 트러블 슈팅 사례(Part 1 — 모니터링 환경구축)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024 현대카드 Tech Talk]]></title>
            <link>https://velog.io/@ssunn_ni/2024-%ED%98%84%EB%8C%80%EC%B9%B4%EB%93%9C-Tech-Talk</link>
            <guid>https://velog.io/@ssunn_ni/2024-%ED%98%84%EB%8C%80%EC%B9%B4%EB%93%9C-Tech-Talk</guid>
            <pubDate>Thu, 07 Nov 2024 03:00:52 GMT</pubDate>
            <description><![CDATA[<p>컨퍼런스 일정 2개가 동시에 있었는데, 다행이 시간대가 겹치지 않아서
저녁에 현대카드 Tech Talk에 다녀올 수 있게 되었다!</p>
<blockquote>
<p>2024.11.05 2024 현대카드 Tech Talk
서울 용산구 이태원로 246 언더스테이지</p>
</blockquote>
<p>.</p>
<h3 id="간단정리">간단정리</h3>
<hr>
<ul>
<li>현대카드에서 ai기술을 접목시켜 내부에서의 데이터를 분류 및 커스텀할 수 있는 기능들을 개발함. </li>
<li>개발한 기술을 내부에서만이 아니라 해외회사에도 판매.</li>
<li>원 데이터들을 갖고 → 1차가공 → 2차가공 등등을 거쳐 태그로 데이터들을 분류</li>
<li>시뮬레이션 결과 세일러분들이 해당 ai를 이긴적은 없음</li>
<li>kafka대신 에어플로우 사용</li>
<li>로그들을 모아 엘라스틱 서치 사용</li>
</ul>
<p>.</p>
<h3 id="후기">후기</h3>
<hr>
<p>간단하게 정리하자면 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024 우아콘 정리]]></title>
            <link>https://velog.io/@ssunn_ni/2024-%EC%9A%B0%EC%95%84%EC%BD%98-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@ssunn_ni/2024-%EC%9A%B0%EC%95%84%EC%BD%98-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 07 Nov 2024 02:55:32 GMT</pubDate>
            <description><![CDATA[<p>2024년도 컨퍼런스 인프콘.. 카카오콘, 우아콘 전부 떨어지고 절망하고있었는데
우연히 양도받을 수 있어서 올해 첫 컨퍼런스에 갈 수 있게 되었다. 감사합니다😭</p>
<blockquote>
<p>2024.10.25 우아콘
삼성역 인터네셔널 호텔
<a href="https://2024.woowacon.com/">https://2024.woowacon.com/</a></p>
</blockquote>
<p>.</p>
<h3 id="간단정리">간단정리</h3>
<hr>
<ul>
<li>백엔드세션기준으로 kafka를 많이 사용하는것같다.</li>
<li>kafka를 사용하면서의 에로사항 및 대용량서버 핸들링 방법 → 비동기처리</li>
<li>대부분의 백엔드 세션이 대용량처리 및 워커에 치중된듯</li>
</ul>
<p>.</p>
<h3 id="후기">후기</h3>
<hr>
<p>나름 듣고싶던 세션 전부 다 들을 수 있었고, 음향도 좋았고 정말 열심히 준비하셨다는게 보였다는 행사였다.
대용량 처리 및 메세지 큐 관련해서 고민이 많았는데 kafka라는걸 정말 많이 쓰는구나라는걸 깨달을 수 있었고 관련해서 한번 공부해야겠다라는 생각이 많이 들었다. 여러 열정적인 사람들과 같은공간에서의 좋은 에너지를 받을 수 있어서 좋았다.</p>
<p>그리고 개인적으로는 나름 3년차이고 곧 4년차가 다되어가는데 내가 하는건 빙산의 일각이라는 생각이 들었다.
관련해서 내가 앞으로는 더 어떻게 해야할까 라는 생각이 많이 들었던 하루였다. </p>
<p>.</p>
<h3 id="세션을-들으며-간단-메모내용">세션을 들으며 간단 메모내용</h3>
<hr>
<p><a href="https://www.notion.so/2024-12fa910a64f080b38321ca003ca8d4e3?pvs=4">https://www.notion.so/2024-12fa910a64f080b38321ca003ca8d4e3?pvs=4</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[메가존소프트&GCP세미나]]></title>
            <link>https://velog.io/@ssunn_ni/%EB%A9%94%EA%B0%80%EC%A1%B4%EC%86%8C%ED%94%84%ED%8A%B8GCP%EC%84%B8%EB%AF%B8%EB%82%98</link>
            <guid>https://velog.io/@ssunn_ni/%EB%A9%94%EA%B0%80%EC%A1%B4%EC%86%8C%ED%94%84%ED%8A%B8GCP%EC%84%B8%EB%AF%B8%EB%82%98</guid>
            <pubDate>Thu, 07 Nov 2024 02:29:54 GMT</pubDate>
            <description><![CDATA[<h3 id="ai-시대-레거시-시스템을-cloud-native-ai-서비스로">AI 시대, 레거시 시스템을 Cloud Native AI 서비스로!</h3>
<blockquote>
<p>2024.11.05 구글&amp;메가존소프트 주최
삼성역 스페이스쉐어 
세미나 정보  <a href="https://www.megazonesoft.com/appmodseminar-20241105/">https://www.megazonesoft.com/appmodseminar-20241105/</a></p>
</blockquote>
<h3 id="간단정리"><strong>간단정리</strong></h3>
<hr>
<ul>
<li>컨테이너 기반 서비스들, AI의 서버 배포 및 마이그레이션툴로는 GKE와 Cloud Run이 있다.</li>
<li>GKE와 CloudRun의 각 특장점</li>
</ul>
<p>.</p>
<h3 id="내용-필기"><strong>내용 필기</strong></h3>
<hr>
<p><a href="https://www.notion.so/AI-Cloud-Native-AI-135a910a64f080138d41f736d86bef58?pvs=4">https://www.notion.so/AI-Cloud-Native-AI-135a910a64f080138d41f736d86bef58?pvs=4</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[websocket을 이용한 채팅서버개발 고생기(2)]]></title>
            <link>https://velog.io/@ssunn_ni/websocket%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%B1%84%ED%8C%85%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EA%B3%A0%EC%83%9D%EA%B8%B02</link>
            <guid>https://velog.io/@ssunn_ni/websocket%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%B1%84%ED%8C%85%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EA%B3%A0%EC%83%9D%EA%B8%B02</guid>
            <pubDate>Tue, 24 Sep 2024 02:34:23 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@ssunn_ni/websocket%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%B1%84%ED%8C%85%EC%84%9C%EB%B2%84%EA%B0%9C%EB%B0%9C-%EA%B3%A0%EC%83%9D%EA%B8%B0">websocket을 이용한 채팅서버개발 고생기(1)</a>
1편에 이어 .. 마저 작성</p>
<h3 id="3-읽음처리하기">3. 읽음처리하기</h3>
<p>.
상대방 읽음처리가 가장 어려운 난제중에 하나였다.
이 부분에서 정말 많이 헤맸는데 우선 헤맸던 포인트가 있다.</p>
<blockquote>
<ol>
<li>앱이라면 괜찮지만 웹은 유저가 이 방에서 나가도 프론트에서 정확하게 트래킹이 어려웠다.</li>
<li>그렇다보니 해당 채팅방에서 나가서 다른 채팅방에 갔는데, socket을 쏘니 방 a의 채팅에 방 b에서의 채팅이 가기시작하고, 혹은 a를 읽었는데 b방의 채팅을 읽음이 전송되었다.</li>
</ol>
</blockquote>
<p>이러한 이슈들로 인해 앞선 고생기(1)에서의 2번(전면 구조변경) 을 채택하고, 
socket의 room에 저장하는 방식들을 전면 수정을 했다.</p>
<blockquote>
<ol>
<li>상대방이 A방에 채팅을 보낸다. 그러면 A방의 다른유저에게 new채팅에 대한 socket을 보내게 된다. </li>
<li>A방에 내가 있다면(채팅창에 활성화 상태라면) 이때 내가 해당 채팅방에서 읽었음에 대한 정보를 서버로 보내게끔 했다.</li>
<li>이렇게 &quot;나 읽음&quot; 이라는 socket을 수신받는다면 서버에서는 내가 아닌 상대방이 보낸 메세지를 모두 읽음처리하고, A방에 읽음처리가 완료되었음을 내가아닌 다른사람에게보내, 상대방이 내 채팅을 읽었음을 알 수 있게 한다. </li>
<li>내가 해당 파티채팅을 리로딩할때도 마찬가지로 읽음처리 루틴을 실행시킨다.</li>
</ol>
</blockquote>
<p>이 외에도,</p>
<p>최대한 서로 다른방의 <strong>간섭</strong>을 막아주기 위해, 채팅방에서 나와 <strong>목록</strong>으로 들어갈때 <strong>모든 방에서 leave</strong>를 했고, 새로운 <strong>채팅방에 접속</strong>할때 또한 <strong>그 방을 제외한 나머지 방에서 leave</strong>하도록 하는 등의 다양한 방법을 써서 구현을 했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[대규모 서버 업데이트를 끝마치고.. 회고 (링키드 2.5에서 3.0 버전업)]]></title>
            <link>https://velog.io/@ssunn_ni/%EB%8C%80%EA%B7%9C%EB%AA%A8%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EB%A5%BC-%EB%81%9D%EB%A7%88%EC%B9%98%EA%B3%A0..-%ED%9A%8C%EA%B3%A0-%EB%A7%81%ED%82%A4%EB%93%9C-2.5%EC%97%90%EC%84%9C-3.0-%EB%B2%84%EC%A0%84%EC%97%85</link>
            <guid>https://velog.io/@ssunn_ni/%EB%8C%80%EA%B7%9C%EB%AA%A8%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EB%A5%BC-%EB%81%9D%EB%A7%88%EC%B9%98%EA%B3%A0..-%ED%9A%8C%EA%B3%A0-%EB%A7%81%ED%82%A4%EB%93%9C-2.5%EC%97%90%EC%84%9C-3.0-%EB%B2%84%EC%A0%84%EC%97%85</guid>
            <pubDate>Thu, 12 Sep 2024 06:41:17 GMT</pubDate>
            <description><![CDATA[<p>2년정도 나를 괴롭히던 대규모 업데이트를 드디어
2024-09-10일에 끝마췄다.</p>
<p><del>물론 끝마쳤다고 끝은 아니고.. 뒤에 잔 버그들 관리는 계속해야하지만..</del></p>
<p>링링이들은 더 귀엽고 예쁘게 변했고,
처음에는 똑같다고 생각했던 UI도 더 예뻐진게 쓰면쓸수록 체감하게 된다. 
<del>( 버전업 마이그레이션 테스트한다고 이전버전으로 돌렸다가 버전업시키고 했을때마다 예전께 투박하다고 느껴졌다 )</del></p>
<h3 id="버전업시-가장-어려웠던점">버전업시 가장 어려웠던점.</h3>
<ol>
<li>기존에 돌아갔던 로직들은 그대로 잘 돌아가야한다.</li>
<li>기존의 DB구조 틀에서 많이 벗어나면 안된다. 쓰레기데이터도 품고가야함..</li>
<li>운영을 위한 데이터셋이 전부 변경되어 그 데이터셋 구조를 맞춰야하지만.. 기존의 값들도 잘 돌아가게해야한다..</li>
</ol>
<p>등등.. 즉, </p>
<blockquote>
<p><strong>달리는 마차의 바퀴는 바꾸지만, 바꾸는 과정에서도 잘 돌아가게 하는것이 가장 힘든 작업이었다.</strong></p>
</blockquote>
<p>로직들, 저장하는 방법, 불러오는방법 모~~ 두 변경되었는데 기존꺼는 기존대로 잘 돌아가야한다니.. <strong>정말 새로운걸 만드는것보다 이게 몇배는 힘들었다.</strong></p>
<p>거기다가, 2.5라이브는 계속 돌아가고있는데 2.5라이브 서버대로 업데이트 해야하지.. 그러면 그 파일은 3.0서버에도 적용시키고.. 로컬에도 적용시키고 3.0 베타에도 적용시키고..</p>
<blockquote>
<p><strong>한번 업데이트 할때마다 온 파일들을 전부 적용시켜야했다..</strong>
<del>하나만이 아니고 어드민, 채팅 전부 그랬다..</del> </p>
</blockquote>
<p>한번에 여러 파일들을 관리하다보니 정말 너무 헷갈리고 어려움이 많았는데
인력이 부족한 상황에, 기존의 라이브버전은 계속 돌아가고 있으며 추가적으로 업데이트가 지속적으로 필요했던 상황이라 3.0 업데이트가 많이 딜레이가 지속되어 정말 힘들었다..</p>
<hr>
<p>그 과정에서 얻는것도 있었고, 정말 건들기 무서웠었던 레거시코드도 다 분해해서 수정하고 다양한 경험을 했다. <del>( 정말 무서운 경험이었다)</del></p>
<p>원래 코드는 개인의 자유이자 돌아가기만 하면 그만이라 하시던 사수님이 내 코드가 점점 잼있어지고있다고했다. 
점점 활용성을 기반으로 코드를 만들고있다고 
( 모듈화를 하려고 노력했더니 그런듯! )</p>
<hr>
<p>아직 계속 이슈 모니터링을 하고 최적화 및 수정을 해야하지만, 
정말 나를 너무나도 길었던 작업 끝내서 너무 뿌듯하고
이 경험이 나를 더 성장시켰을것이라고 믿는다!</p>
<p>예쁜 링키드 구경오세요
<a href="https://linkid.pw/">https://linkid.pw/</a>
<img src="https://velog.velcdn.com/images/ssunn_ni/post/88fe3413-f6c0-4f3a-aeb5-a5d66e06f081/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[gcp에서 갑자기 nginx가 안먹는다면..?]]></title>
            <link>https://velog.io/@ssunn_ni/gcp%EC%97%90%EC%84%9C-%EA%B0%91%EC%9E%90%EA%B8%B0-nginx%EA%B0%80-%EC%95%88%EB%A8%B9%EB%8A%94%EB%8B%A4%EB%A9%B4</link>
            <guid>https://velog.io/@ssunn_ni/gcp%EC%97%90%EC%84%9C-%EA%B0%91%EC%9E%90%EA%B8%B0-nginx%EA%B0%80-%EC%95%88%EB%A8%B9%EB%8A%94%EB%8B%A4%EB%A9%B4</guid>
            <pubDate>Tue, 16 Jul 2024 07:49:15 GMT</pubDate>
            <description><![CDATA[<p>gcp서버 업데이트를 위해 서버를 껐다가 켰다.
그리고 pm2를 사용하여 안에 서버들을 띄었는데,</p>
<p>분명 평소와 같이 서버들을 띄우는데 외부에서 접속이 안되었다..
로그를 봐도 api가 날라오는 흔적도 안보이고
nginx의 로그를 봐도 어떠한 패킷도 넘어오지 않았다.</p>
<p>이럴땐, tomcat이나 아파치가 켜저있지 않은지 한번 보자.
그리고 nginx를 restart를 시켜보자!</p>
<p>그랬더니.. 접속이 되었다.. </p>
]]></description>
        </item>
    </channel>
</rss>