<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>🐑☘️</title>
        <link>https://velog.io/</link>
        <description>양치기소녀</description>
        <lastBuildDate>Fri, 10 Jul 2026 03:43:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>🐑☘️</title>
            <url>https://velog.velcdn.com/images/cold_as_hell/profile/87e205f1-414b-41e7-8c14-d813733434bc/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 🐑☘️. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cold_as_hell" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[2]]></title>
            <link>https://velog.io/@cold_as_hell/2</link>
            <guid>https://velog.io/@cold_as_hell/2</guid>
            <pubDate>Fri, 10 Jul 2026 03:43:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/6ce99815-c3c1-4344-b479-92a924859874/image.jpg" alt=""></p>
<p>이 챕터의 면접관이 되어,
원활한 면접진행을 위해 좀 더 내용을 열심히 읽어보았다.</p>
<hr>
<p>대규모 서비스를 설계하려면 필요한 저장 공간, 네트워크 대역폭, 서버 수 등을 미리 예측해야 한다.</p>
<p>하지만 설계 초기부터 모든 상황을 고려하여 정확한 수치를 계산하는 것은 현실적으로 어렵고, 오히려 시간과 비용이 많이 든다. 따라서 시스템 설계에서는 빠르게 규모를 예측하는 <strong>개략적인 규모 추정(Back-of-the-envelope Estimation)</strong> 을 활용한다.</p>
<p>&#39;개략적&#39;이라는 표현이 다소 생소할 수 있으니 먼저 뜻을 알아보자.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/df2aa0bc-3aec-4974-a373-07a90af0b41e/image.png" alt=""></p>
<blockquote>
<p>&quot;개략적인 규모 추정은 보편적으로 통용되는 성능 수치를 바탕으로 사고 실험을 통해 추정치를 계산하여, 설계가 요구사항을 만족하는지 판단하는 과정이다.&quot;</p>
<p>— Jeff Dean</p>
</blockquote>
<p>즉, <strong>정확한 값을 계산하는 것이 목적이 아니라, 설계 방향이 적절한지 빠르게 판단하는 것이 목적</strong>이다.</p>
<p>이를 위해서는 규모 확장성을 표현하는 데 필요한 기본기를 갖추고 있어야 한다. 특히 <strong>2의 제곱수, 응답 지연, 가용성</strong> 과 같은 개념을 이해해야 저장 공간, 처리량, 서버 수 등을 합리적으로 추정할 수 있다.</p>
<hr>
<h1 id="2의-제곱수">2의 제곱수</h1>
<p>컴퓨터가 2진수를 사용하기 때문에, <strong>데이터의 크기나 저장 용량 단위도 2의 제곱수(1KB = 2¹⁰Byte, 1MB = 2²⁰Byte …)를 기준으로 정의된다.</strong></p>
<p><strong>따라서 분산 시스템에서 다루는 저장 공간, 메모리, 네트워크 사용량 등을 추정하려면 2의 제곱수와 데이터 용량 단위의 관계를 이해해야 한다.</strong></p>
<h2 id="단위">단위</h2>
<p>시스템 설계에서는 빠르게 규모를 추정해야 하는 경우가 많기 때문에 계산을 단순화하여 <strong>2¹⁰≈1천, 2²⁰≈100만</strong>처럼 근사치를 사용하는 경우도 있다.</p>
<table>
<thead>
<tr>
<th>2의 제곱</th>
<th>근사치</th>
<th>데이터 용량 단위</th>
<th>약어</th>
</tr>
</thead>
<tbody><tr>
<td>2¹⁰</td>
<td>약 1천</td>
<td>킬로바이트(Kilobyte)</td>
<td>1KB</td>
</tr>
<tr>
<td>2²⁰</td>
<td>약 100만</td>
<td>메가바이트(Megabyte)</td>
<td>1MB</td>
</tr>
<tr>
<td>2³⁰</td>
<td>약 10억</td>
<td>기가바이트(Gigabyte)</td>
<td>1GB</td>
</tr>
<tr>
<td>2⁴⁰</td>
<td>약 1조</td>
<td>테라바이트(Terabyte)</td>
<td>1TB</td>
</tr>
<tr>
<td>2⁵⁰</td>
<td>약 1000조</td>
<td>페타바이트(Petabyte)</td>
<td>1PB</td>
</tr>
</tbody></table>
<p>ex)
Q. 사용자 100만 명이 사용하는 서비스에서 게시글 하나의 크기가 2KB라면 필요한 저장 공간은?
A. &gt; A. 2KB × 1,000,000 = 2,000,000KB  ≈ 1,953MB ≈ 1.91GB ≈ 약 2GB</p>
<hr>
<h1 id="모든-프로그래머가-알아야-하는-응답지연-값">모든 프로그래머가 알아야 하는 응답지연 값</h1>
<p>저장 공간을 추정하기 위해 데이터 크기와 단위를 이해했다면, 이번에는 <strong>연산이 얼마나 빠르게 수행되는지</strong>도 알아야 한다.</p>
<p>대규모 시스템에서는 CPU 연산뿐만 아니라 메모리 접근, 디스크 I/O, 네트워크 통신 등 다양한 작업이 발생한다. 각 작업의 응답 지연(latency)을 이해하면 어떤 부분이 병목이 될 수 있는지 판단하고, 시스템의 성능을 대략적으로 예측할 수 있다.</p>
<h2 id="통상적인-컴퓨터에서-구현된-연산들의-응답지연-값">통상적인 컴퓨터에서 구현된 연산들의 응답지연 값</h2>
<table>
<thead>
<tr>
<th>연산</th>
<th align="right">응답 시간</th>
</tr>
</thead>
<tbody><tr>
<td>L1 캐시 참조</td>
<td align="right">0.5ns</td>
</tr>
<tr>
<td>분기 예측 오류 (Branch Mispredict)</td>
<td align="right">5ns</td>
</tr>
<tr>
<td>L2 캐시 참조</td>
<td align="right">7ns</td>
</tr>
<tr>
<td>뮤텍스(Mutex) 락/언락</td>
<td align="right">100ns</td>
</tr>
<tr>
<td>주 메모리 참조</td>
<td align="right">100ns</td>
</tr>
<tr>
<td>Zippy로 1KB 압축</td>
<td align="right">10,000ns (10μs)</td>
</tr>
<tr>
<td>1Gbps 네트워크로 2KB 전송</td>
<td align="right">20,000ns (20μs)</td>
</tr>
<tr>
<td>메모리에서 1MB 순차 읽기(Read)</td>
<td align="right">250,000ns (250μs)</td>
</tr>
<tr>
<td>같은 데이터센터 내 메시지 왕복 지연시간(RTT)</td>
<td align="right">500,000ns (500μs)</td>
</tr>
<tr>
<td>디스크 탐색(Seek)</td>
<td align="right">10,000,000ns (10ms)</td>
</tr>
<tr>
<td>네트워크에서 1MB 순차 읽기(Read)</td>
<td align="right">10,000,000ns (10ms)</td>
</tr>
<tr>
<td>디스크에서 1MB 순차 읽기(Read)</td>
<td align="right">30,000,000ns (30ms)</td>
</tr>
<tr>
<td>캘리포니아(CA) ↔ 네덜란드 패킷 왕복 지연시간</td>
<td align="right">150,000,000ns (150ms)</td>
</tr>
</tbody></table>
<p>2010년에 공개된 컴퓨터 연산의 응답 지연 값.
일부 수치가 현재와 달라졌지만, 각 연산의 상대적인 처리 속도를 이해하는 기준으로는 여전히 유용하다.</p>
<p><a href="https://colin-scott.github.io/blog/2012/12/24/latency-trends/?utm_source=chatgpt.com">2020년에는 이 수치를 시각화하기 위한 도구도 나왔다.</a></p>
<hr>
<h1 id="가용성에-관계된-수치들">가용성에 관계된 수치들</h1>
<p>가용성 : 시스템이 사용자가 필요할 때 정상적으로 서비스를 제공할 수 있는 정도. 즉, 시스템이 오랜시간동안 지속적으로 중단없이 운영될 수 있음을 의미한다.</p>
<p>고가용성 : 가용성을 매우높이기 위한 설계방식. 서비스 장애 예방과 조치에 보다 높은 초점을 맞추었다.</p>
<p>가용률 : 가용성 100%는 시스템이 단 한 번도 중단되지 않았음을 의미한다. </p>
<p>다만 현실적으로는 계획된 점검, 예기치 못한 장애 등으로 인해 100% 가용성을 달성하기는 매우 어려워, 실제 서비스에서는 99.9%, 99.99%, 99.999% 와 같은 수준을 목표로 설계하는 경우가 많다.</p>
<p><strong>수치사용 예시</strong></p>
<table>
<thead>
<tr>
<th align="center">가용률</th>
<th align="center">하루당 허용 장애시간</th>
<th align="center">주당 허용 장애시간</th>
<th align="center">월당 허용 장애시간</th>
<th align="center">연간 허용 장애시간</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>99%</strong></td>
<td align="center">14.40분</td>
<td align="center">1.68시간</td>
<td align="center">7.31시간</td>
<td align="center">3.65일</td>
</tr>
<tr>
<td align="center"><strong>99.9%</strong></td>
<td align="center">1.44분</td>
<td align="center">10.08분</td>
<td align="center">43.83분</td>
<td align="center">8.77시간</td>
</tr>
<tr>
<td align="center"><strong>99.99%</strong></td>
<td align="center">8.64초</td>
<td align="center">1.01분</td>
<td align="center">4.38분</td>
<td align="center">52.60분</td>
</tr>
<tr>
<td align="center"><strong>99.999%</strong></td>
<td align="center">864ms (0.864초)</td>
<td align="center">6.05초</td>
<td align="center">26.30초</td>
<td align="center">5.26분</td>
</tr>
<tr>
<td align="center"><strong>99.9999%</strong></td>
<td align="center">86.4ms (0.0864초)</td>
<td align="center">604.8ms (0.6048초)</td>
<td align="center">2.63초</td>
<td align="center">31.56초</td>
</tr>
</tbody></table>
<hr>
<h2 id="예제-트위터-qps와-저장소-요구량-추정">예제) 트위터 QPS와 저장소 요구량 추정</h2>
<h3 id="풀이핵심">풀이핵심</h3>
<p>개략적인 규모 추정 면접에서 가장 중요한 것은 문제를 해결해 나가는 사고 과정이다. 
<strong>면접관은 정답보다 올바른 절차와 논리적인 접근 방식을 더 중요하게 평가한다.</strong></p>
<p>예를 들어 99987 / 9.1 과 같은 계산 문제가 주어졌을 때, 정확한 계산 결과를 구하는 것이 목적이 아니다.
적절한 근사치를 활용해 빠르게 추정하고, 그 근거를 설명하는 능력이 더 중요하다.</p>
<h2 id="예제-윤아-쇼핑몰-qps-급증과-대응방안">예제) 윤아 쇼핑몰 QPS 급증과 대응방안</h2>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/220fadb7-f889-4f65-a64d-678d02fd540c/image.png" alt=""></p>
<hr>
<h1 id="독서를-마치며">독서를 마치며...</h1>
<p>대용량 시스템을 설계하고 구축하는 과정에서 효율성과 합리적인 자원 관리를 위해서 개략적인 규모 측정이 중요한 능력일 것이라 생각된다.</p>
<p>그런 이유로 내가 면접관인 면접에서는 아마 2의 제곱수 개념 (ex. 100mb는 몇 kb인가) 등의 암기형 문제보다는 이러한 값들이 개략적인 규모를 산정하고 시스템을 설계할 때 어떻게 활용되는지를 묻는 문제가 나올 듯 하다.</p>
<p>면접자 오이데~
<img src="https://velog.velcdn.com/images/cold_as_hell/post/fd192c33-b367-46df-95e4-18397147f79a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[잼얘하기 - 6]]></title>
            <link>https://velog.io/@cold_as_hell/GEO</link>
            <guid>https://velog.io/@cold_as_hell/GEO</guid>
            <pubDate>Wed, 08 Jul 2026 12:24:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/7c6a5174-7745-4aa0-8d4e-3ed309055543/image.png" alt=""></p>
<p>GEO를 잘 알면 서비스를 노출시키고 돈을 버는데 도움이 된다.</p>
<hr>
<h1 id="seo만-하던-회사들이-갑자기-geo를-공부하는-이유">SEO만 하던 회사들이 갑자기 GEO를 공부하는 이유</h1>
<p>우리 회사 서비스를 만들었다.</p>
<p>개발도 끝났다.</p>
<p>배포도 끝났다.</p>
<p>버그도 거의 없다.</p>
<p>이제 남은 건 사람들이 찾아오는 일이다.</p>
<p>예전 같았으면 마케팅팀이 말했다.</p>
<blockquote>
<p>&quot;SEO 좀 신경 써주세요.&quot;</p>
</blockquote>
<p>개발자는 메타태그 넣고,
사이트맵 만들고,
robots.txt 만들고,
구조화 데이터도 넣었다.</p>
<p>며칠 뒤.</p>
<p>Google 검색.</p>
<pre><code>우리 서비스가 3페이지에 있습니다.</code></pre><p>좋다.</p>
<p>그런데 2026년.</p>
<p>사용자는 검색창 대신 이렇게 묻는다.</p>
<blockquote>
<p>&quot;국내에서 사용할 수 있는 전자계약 SaaS 추천해줘.&quot;</p>
</blockquote>
<p>AI는 대답한다.</p>
<pre><code>A사
B사
C사</code></pre><p>...</p>
<p>우리 회사는?</p>
<p>없다.</p>
<hr>
<h1 id="분명-검색하면-나오는데">&quot;분명 검색하면 나오는데?&quot;</h1>
<p>맞다.</p>
<p>검색하면 나온다.</p>
<p>하지만 AI는 추천하지 않는다.</p>
<p>이게 SEO와 GEO의 차이다.</p>
<p>SEO는</p>
<blockquote>
<p>검색 결과에 노출되는 것.</p>
</blockquote>
<p>GEO는</p>
<blockquote>
<p>AI의 답변에 포함되는 것.</p>
</blockquote>
<p>이다.</p>
<hr>
<h1 id="이런-느낌이다">이런 느낌이다.</h1>
<p>예전</p>
<pre><code>Google

↓

우리 회사 홈페이지</code></pre><p>지금</p>
<pre><code>Google
        ↘

ChatGPT

Claude

Gemini
        ↓

우리 회사를 추천</code></pre><p>검색엔진이 하나 더 생긴 게 아니다.</p>
<p><strong>추천 엔진이 하나 생긴 것이다.</strong></p>
<hr>
<h1 id="ai는-기업을-어떻게-추천할까">AI는 기업을 어떻게 추천할까?</h1>
<p>많은 사람이</p>
<blockquote>
<p>&quot;LLM이 학습한 데이터로만 답하지 않나요?&quot;</p>
</blockquote>
<p>라고 생각한다.</p>
<p>물론 학습 데이터도 사용한다.</p>
<p>하지만 최근 AI는 필요에 따라 웹 검색도 하고, 여러 출처를 종합해 답변하기도 한다.</p>
<p>결국 AI가 참고하기 쉬운 형태로 기업 정보를 제공할수록 추천될 가능성이 높아진다.</p>
<hr>
<h1 id="geo는-결국-기업-정보를-잘-설명하는-기술이다">GEO는 결국 기업 정보를 잘 설명하는 기술이다.</h1>
<p>예를 들어 회사 홈페이지에</p>
<pre><code>혁신적인 플랫폼을 제공합니다.</code></pre><p>라고만 적혀 있다면</p>
<p>AI는 이해하기 어렵다.</p>
<p>반면</p>
<pre><code>서비스명

무엇을 하는 서비스인지

누가 사용하는지

어떤 문제를 해결하는지

가격

API 지원 여부

도입 사례</code></pre><p>처럼 구조적으로 정리되어 있다면</p>
<p>AI가 인용하기 쉬워진다.</p>
<hr>
<h1 id="개발자가-할-수-있는-geo">개발자가 할 수 있는 GEO</h1>
<p>생각보다 많다.</p>
<ul>
<li>시맨틱 HTML 작성</li>
<li>구조화 데이터(Schema.org)</li>
<li>robots.txt, sitemap 관리</li>
<li>명확한 URL 구조</li>
<li>API 문서 공개</li>
<li>FAQ 작성</li>
<li>제품 문서(Docs) 관리</li>
<li>변경 이력(Changelog) 공개</li>
</ul>
<p>...</p>
<p>어디서 많이 본 것 같지 않은가?</p>
<p>맞다.</p>
<p>대부분 SEO를 위해 하던 일이다.</p>
<hr>
<h1 id="seo와-geo는-경쟁-관계가-아니다">SEO와 GEO는 경쟁 관계가 아니다.</h1>
<p>많은 사람이</p>
<blockquote>
<p>&quot;이제 SEO 끝난 거 아닌가?&quot;</p>
</blockquote>
<p>라고 말한다.</p>
<p>오히려 반대다.</p>
<p>SEO가 잘 되어 있어야 GEO도 잘 된다.</p>
<p>검색엔진이 이해하기 쉬운 사이트는</p>
<p>AI도 이해하기 쉽기 때문이다.</p>
<hr>
<h1 id="개발자-입장에서-geo가-중요한-이유">개발자 입장에서 GEO가 중요한 이유</h1>
<p>예전에는</p>
<pre><code>서비스를 만든다.

↓

검색에 노출된다.

↓

사용자가 방문한다.</code></pre><p>였다.</p>
<p>이제는</p>
<pre><code>서비스를 만든다.

↓

AI가 이해한다.

↓

AI가 추천한다.

↓

사용자가 방문한다.</code></pre><p>라는 단계가 하나 추가되고 있다.</p>
<hr>
<h1 id="결론">결론</h1>
<p>예전에는 기업들이 Google에게 선택받기 위해 SEO를 했다.</p>
<p>이제는 ChatGPT, Claude, Gemini 같은 생성형 AI에게도 <strong>&quot;우리 회사가 어떤 서비스를 하는지&quot;</strong> 명확하게 설명해야 하는 시대가 되었다.</p>
<p>SEO는 <strong>검색엔진에게 발견되기 위한 최적화</strong>이고,</p>
<p>GEO는 <strong>생성형 AI에게 이해되고 추천받기 위한 최적화</strong>다.</p>
<p>앞으로 기업의 온라인 경쟁력은 <strong>SEO와 GEO를 함께 고려하는 것</strong>에서 시작될 가능성이 크다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1]]></title>
            <link>https://velog.io/@cold_as_hell/1</link>
            <guid>https://velog.io/@cold_as_hell/1</guid>
            <pubDate>Tue, 07 Jul 2026 03:55:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/e04eb104-d66a-4be4-afd0-8d0c346127ca/image.jpg" alt=""></p>
<p>1장은 CDN 까지의 개념을 집중적으로 살펴봤다.</p>
<hr>
<h1 id="사용자-수에-따른-규모-확장성">사용자 수에 따른 규모 확장성</h1>
<p>사용자 수에 따른 프로그램을 설계해야한다는 내용이다.</p>
<hr>
<h2 id="단일서버">단일서버</h2>
<blockquote>
<p>모든 컴포넌트가 단 한대의 서버에서 실행되는 간단한 시스템.</p>
</blockquote>
<p>웹, 앱, 데이터베이스, 캐시 등이 전부 서버 한대에서 실행된다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/3edb1e37-41b7-48bd-8125-93bd3c176afe/image.png" alt=""></p>
<p>즉, 사용자가 도메인 이름으로 접속하면 자동으로 DNS 조회를 통해 서버의 IP 주소를 받아오고, 해당 주소로 HTTP 요청이 전달된다. 이후 웹 서버가 HTML 페이지나 JSON 형태의 응답을 생성하여 반환하기까지의 모든 과정이 하나의 서버에서 실행되는 것이다.</p>
<hr>
<h2 id="데이터베이스">데이터베이스</h2>
<blockquote>
<p>여러 사람이 동시에 공유하여 사용할 목적으로 통합 관리되는 데이터의 집합.</p>
</blockquote>
<p>이를 관리하는데, 여러 서버를 둘 수 도 있다.</p>
<p>EX) 웹/모바일 트래픽 처리 용도와 데이터베이스 서버를 따로두는 경우 : 독립적으로 확장 가능.</p>
<h3 id="데이터베이스의-종류">데이터베이스의 종류</h3>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/825fb32c-574d-42d6-9f08-409b9f7fad8f/image.png" alt=""></p>
<p>데이터베이스는 크게 관계형과 비관계형으로 나뉜다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>관계형 데이터베이스(RDBMS)</th>
<th>비관계형 데이터베이스(NoSQL)</th>
</tr>
</thead>
<tbody><tr>
<td>대표 제품</td>
<td>MySQL, Oracle Database, PostgreSQL</td>
<td>CouchDB, Neo4j, Cassandra, HBase, Amazon DynamoDB</td>
</tr>
<tr>
<td>데이터 <BR> 저장 <BR>방식</td>
<td>테이블(Table), 행(Row), 열(Column)</td>
<td>키-값 저장소(Key-Value Store), 그래프 저장소(Graph Store), 칼럼 저장소(Column Store), 문서 저장소(Document Store)</td>
</tr>
<tr>
<td>데이터 <BR>조회</td>
<td>SQL을 사용하여 여러 테이블을 JOIN 가능</td>
<td>일반적으로 JOIN 연산 미지원</td>
</tr>
</tbody></table>
<p>관계형 데이터는 아래와 같은 상황에서 쓰기 좋다.</p>
<p>1) 엄격한 데이터 일관성과 트랜잭션 보장이 필요할 때 (ACID)
2) 데이터 간의 관계가 복잡하고 구조가 명확할 때 (Schema)
3) 다양하고 복잡한 조건의 데이터 조회가 필요할 때 (JOIN)
4) 비즈니스 규칙이 자주 바뀌거나 데이터가 계속 변할 때</p>
<p>이런 이유로 대부분의 프로그램에서 관계형을 채택 하지만 아래와 같은 경우에는 비관계형을 사용하는것이 좋다.</p>
<p>1) 아주 낮은 응답 지연시간 요구
2) 다루는 데이터가 관계형 데이터가 아님 (= 비정형)
3) 데이터 직렬화 혹은 역직렬화만이 필요
4) 분산 저장 및 수평 확장이 필요한 대용량 데이터를 다룰 때</p>
<blockquote>
<p><strong>데이터가 대용량이고 빠르게 쌓이는 비정형 데이터라면 NoSQL, 데이터의 정확성과 신뢰성, 복잡한 관계가 중요하다면 RDBMS를 쓰는 것이 좋다.</strong></p>
</blockquote>
<hr>
<h2 id="수직적-규모-확장스케일업-vs-수평적-규모확장스케일아웃">수직적 규모 확장(스케일업) vs 수평적 규모확장(스케일아웃)</h2>
<p>사용자가 증가하면 서버 확장이 필요하다.
서버 확장은 수직적 규모확장과 수평적 규모확장으로 가능하다. </p>
<p><strong>스케일업</strong> : CPU, RAM, SSD 등 증설해 <strong>기존 서버의 성능을 높이는 방식</strong></p>
<ul>
<li>스케일업시 장애에 대한 자동복구나 다중화 방안 제시되지 않아 장애발생시 프로그램 중단된다는 단점이 있음.</li>
</ul>
<p><strong>스케일아웃</strong> : 동일한 역할을 하는 <strong>서버를 여러 대 추가하여</strong> 처리량을 늘리는 방식</p>
<ul>
<li>스케일업의 단점으로 인해, 대규모 어플리케이션에서는 스케일 아웃 사용이 적절함.</li>
</ul>
<p>정리하자면,</p>
<p>스케일업이 유리 : 서버로 유입되는 트레픽 양이 적을 때
스케일아웃이 유리 : 서버로 유입되는 트레픽 양이 많을 때</p>
<hr>
<h2 id="트래픽-부하-분산-방법">트래픽 부하 분산 방법</h2>
<p>스케일업과 스케일아웃은 서버의 성능과 처리량을 향상시키지만, 대규모 서비스에서는 이것만으로 충분하지 않다.</p>
<p><strong>증가한 트래픽을 여러 웹 서버에 분산하고, 정적 콘텐츠를 효율적으로 제공하기 위한 추가적인 아키텍처</strong> 가 필요하다. </p>
<p>이유: 서버가 여러대일때, 쓰이지 않는 서버가 있으면 비효율.
서버 성능이 좋더라도, 하나에서 처리하는 것보다 여러대에서 처리하는 것이 효율.</p>
<h3 id="로드밸런서">로드밸런서</h3>
<p>부하분산집합(load balancing set)에 속한 웹 서버들에게 트래픽 부하를 고르게 분산.
사용자는 DNS주소를 통해 도메인에 접속함으로, 주소를 다르게 입력하지 않아도 괜찮다.</p>
<p>  <img src="https://velog.velcdn.com/images/cold_as_hell/post/a09d660e-4c52-4e48-a78f-851caf87e207/image.png" alt=""></p>
<h3 id="데이터베이스-다중화">데이터베이스 다중화</h3>
<p>데이터 원본은 주 서버(Master)에, 사본은 부 서버(Slave)에 저장하는 방식</p>
<ul>
<li><strong>쓰기 연산(Write):</strong> 마스터(Master) 서버에서만 처리 (insert, delete, update 등)</li>
<li><strong>읽기 연산(Read):</strong> 슬레이브(Slave) 서버들로 분산하여 처리. 통상 어플리케이션은 읽기 비중이 훨씬 높기 때문에 슬레이브 서버의 수가 더 많음.</li>
</ul>
<h4 id="⚠️-서버가-다운되면-어떻게-대응하는가">⚠️ 서버가 다운되면 어떻게 대응하는가?</h4>
<ul>
<li><strong>부(Slave) 서버가 다운된 경우:</strong> 읽기 연산은 한시적으로 모두 주 서버로 전달되며, 나머지 부 서버들이 부하를 분산함. 그동안 새로운 부 서버가 장애 서버를 대체함.</li>
<li><strong>주(Master) 서버가 다운된 경우:</strong> 한 대의 부 서버가 새로운 주 서버로 승격(Failover)되어 일시적으로 모든 연산을 수행함. <ul>
<li><em>실무 고려사항:</em> 부 서버의 데이터가 최신 상태가 아닐 수 있으므로(복제 지연), 누락된 데이터는 복구 스크립트를 돌려 채워넣어야 함.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/6cd13421-8675-4de9-ae6a-180b31731d8d/image.png" alt=""></p>
<hr>
<h2 id="캐시">캐시</h2>
<p>불러오는 비용이 크거나 자주 불러와야 하는 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소.  </p>
<h3 id="캐시-계층">캐시 계층</h3>
<p>&quot;느리고 무거운 원본 저장소(DB/디스크)에 접근하는 횟수를 줄이기 위해, 빠르고 가벼운 임시 저장소(메모리/RAM)에 데이터를 미리 올려두는 아키텍처 설계 기법&quot;</p>
<p>컴퓨터 엔지니어링의 대원칙인 &quot;자주 쓰는 데이터는 더 가깝고 빠른 곳에 둔다&quot;를 웹 서버와 데이터베이스 레이어에 적용했다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/170fc7b0-bb43-494a-9e2a-f0d2adb4c208/image.png" alt=""></p>
<h3 id="사용시-유의할점">사용시 유의할점</h3>
<p>캐시는 어떤 상황에 바람직한가
어떤 데이터를 캐시에 두어야 하는가
캐시에 보관된 데이터는 어떻게 만료(expire)되는가? 
<a href="https://velog.io/@cold_as_hell/%EC%BA%90%EC%8B%9C-%EB%B6%88%EC%9D%BC%EC%B9%98%EC%99%80-%ED%95%B4%EA%B2%B0-%EC%A0%84%EB%9E%B5">일관성은어떻게 유지되는가?</a>
단일 장애 지점에 어떻게 대처할 것인가?
캐시 메모리는 얼마나 크게 잡을 것인가? 
데이터 방출(eviction) 정책은 무엇인가?</p>
<hr>
<h2 id="콘텐츠-전송-네트워크-cdn">콘텐츠 전송 네트워크 (CDN)</h2>
<p> 전 세계에 분산된 서버를 통해 웹 콘텐츠를 사용자와 가까운 위치에서 빠르게 전달하는 기술</p>
<p>  <img src="https://velog.velcdn.com/images/cold_as_hell/post/3fe1a4f9-ef7e-4cf3-85be-9c276584abc2/image.png" alt=""></p>
<p> ex) 
 react 라이브러리를 사용해 새로운 서비스를 개발한 민성.
 MZ한 에셋을 사용해 로드오브레전드를 개발한 김낍삽.</p>
<hr>
<h1 id="생각한-점">생각한 점</h1>
<p>트래픽이 적을 때 자원이 낭비되는 오버 엔지니어링을 경계하고, 트래픽이 많을 때는 병목 없이 유연하게 확장되는 비용 효율적인 아키텍처를 고민해야겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[0]]></title>
            <link>https://velog.io/@cold_as_hell/%EA%B0%80%EC%83%81-%EB%A9%B4%EC%A0%91-%EC%82%AC%EB%A1%80%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EA%B8%B0%EC%B4%88-0</link>
            <guid>https://velog.io/@cold_as_hell/%EA%B0%80%EC%83%81-%EB%A9%B4%EC%A0%91-%EC%82%AC%EB%A1%80%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EA%B8%B0%EC%B4%88-0</guid>
            <pubDate>Tue, 07 Jul 2026 03:49:24 GMT</pubDate>
            <description><![CDATA[<p>집 근처에 &#39;순살 아파트&#39; 라고 불리던 것이 있었다.</p>
<p>생김새는 그럴싸하지만, 만들어질 때 원래 들어가야 했던 철근보다 부족한 양이 사용되어 구조적으로 안전하지 못한 아파트였다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/345add7d-4ab1-4878-965c-ba55265a3ee9/image.png" alt=""></p>
<p>프로그램 역시 규모의 확장성과 유지보수성을 고려하지 않은 채 설계된다면, 순살 아파트 나 다름없는 &#39;순살 프로그램&#39;이 되리라 생각한다.</p>
<p>우리는 &#39;순살 프로그램&#39;을 만들지 않기 위해, 주마다 역할과 범위를 나누어 이 책을 함께 읽기로 했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[804초→ 8.4초,  응답 지연의 원인을 찾아 개선하자]]></title>
            <link>https://velog.io/@cold_as_hell/804%EC%B4%88-8.4%EC%B4%88-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%B0%BE%EC%9D%80-%EC%9D%91%EB%8B%B5-%EC%A7%80%EC%97%B0%EC%9D%98-%EC%9B%90%EC%9D%B8</link>
            <guid>https://velog.io/@cold_as_hell/804%EC%B4%88-8.4%EC%B4%88-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%B0%BE%EC%9D%80-%EC%9D%91%EB%8B%B5-%EC%A7%80%EC%97%B0%EC%9D%98-%EC%9B%90%EC%9D%B8</guid>
            <pubDate>Sat, 04 Jul 2026 08:05:37 GMT</pubDate>
            <description><![CDATA[<p>양치기와 양치기의 마을 사람들은 종종 마을회관에서 보트를 빌려타고 도시로 나간다.</p>
<p>어느날 이장님은 마을 사람들의 보트 대여 현황을 관리하는 프로그램의 필요성을 느끼게 되어 옆에 있던 양치기에게 프로그램 제작을 짬때렸고,</p>
<p>짬 맞은 프로그램을 바이브코딩으로 제작하던 양치기는 큰 문제에 직면했다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/2cb66a97-e25f-4785-b842-a483f36648b6/image.jpg" alt=""></p>
<hr>
<h1 id="응답지연">응답지연</h1>
<p>컴퓨터, 네트워크, AI 시스템에서 사용자가 명령을 내리거나 데이터를 요청한 후 응답을 받을 때까지 길게 발생하는 대기 시간</p>
<hr>
<h1 id="응답-과정">응답 과정</h1>
<ol>
<li>클라이언트의 동작 요청</li>
<li>백엔드 파이프라인에 의해 응답 데이터가 결정.</li>
<li>클라이언트로 응답 반환</li>
</ol>
<p>ex) 과거 N사 모니터링 시스템 파이프라인.
지진이 발생했을 때 클라이언트에서 조회요청을 하면 모니터링 서버에서 최신데이터를 조회하고 대시보드 화면에 출력하는 과정을 그리고 있다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/28abd689-b476-4c41-b927-8c5afcd17919/image.png" alt=""></p>
<hr>
<h1 id="응답지연의-원인을-파악하는-방법">응답지연의 원인을 파악하는 방법</h1>
<p>응답 과정을 살펴보며 지연 원인을 파악하는 방법에는 아래와 같은 방법이 있다.</p>
<p><strong>1. 실제 사용자 경험을 통해 어느 화면이나 기능에서 응답 지연이 발생하는지 확인한다.</strong>
동일한 구간에서 지속적으로 문제가 발생한다면 해당 구간을 우선적으로 분석한다.</p>
<p><strong>2. 경험을 통해 의심구역을 짐작하자.</strong>
ex) 다른 프로그램을 사용할 때는 응답지연이 없지만, 이 프로그램의 특정 페이지에서만 응답지연이 일어나고 있으니 해당 페이지에서 수행되는 요청이나 처리 과정에 문제가 있을 가능성이 높아.</p>
<p><strong>3. 모니터링 대시보드 및 실시간 트래픽 데이터 속에서 API 호출량, 초당 요청 수(RPS), 응답시간(Response Time), 메모리 사용률 등 객관적 지표를 확인하며 원인을 파악해 간다.</strong></p>
<p>ex. 해당 페이지에서 1초에 100만번씩 동일한 요청을 보내고 있어.
이것 때문에 서버 과부하가 온듯한데?</p>
<hr>
<h1 id="웹-서비스에서-원인이-될-수-있는-상황">웹 서비스에서 원인이 될 수 있는 상황</h1>
<h2 id="클라이언트">클라이언트</h2>
<h3 id="1-불필요한-호출-및-비효율적-리소스-요청">(1) 불필요한 호출 및 비효율적 리소스 요청</h3>
<ul>
<li><strong>컴포넌트 반복 렌더링에 따른 API 재호출:</strong> 상태 관리(State Management) 실수나 UI 업데이트 로직 오류로 인해, 동일한 화면에서 불필요하게 API가 반복적으로 중복 호출되는 현상</li>
<li><strong>비효율적인 캐싱 전략 부족:</strong> 이미 가져온 데이터(예: 공통 코드, 사용자 프로필 등)를 메모리나 로컬 스토리지에 캐싱(예: TanStack Query 활용)하지 않고, 페이지를 이동할 때마다 매번 새로 서버에 요청하는 케이스</li>
</ul>
<h3 id="2-이벤트-제어-미흡-및-대량의-트래픽-순간-발생">(2) 이벤트 제어 미흡 및 대량의 트래픽 순간 발생</h3>
<ul>
<li><strong>Debounce / Throttle 미적용:</strong> 검색창 자동완성(Input 이벤트), 무한 스크롤(Scroll 이벤트), 창 크기 조절(Resize 이벤트) 등에서 실시간 이벤트 제어가 되지 않아 짧은 순간에 수십 번의 요청이 쏟아지는 현상</li>
</ul>
<h3 id="3-번들-크기-비대화-및-리소스-로딩-지연-asset-overhead">(3) 번들 크기 비대화 및 리소스 로딩 지연 (Asset Overhead)</h3>
<ul>
<li><strong>Code Splitting(코드 분할) 부재:</strong> 초기 진입 시 당장 필요 없는 대용량 서드파티 라이브러리나 페이지 컴포넌트까지 한 번에 로드(JS 번들 비대화)하느라 브라우저가 파싱하고 메인 스레드를 차단하는 시간이 길어져 첫 화면 인터랙션이 지연되는 현상</li>
<li><strong>이미지 및 에셋 최적화 미비:</strong> 차세대 이미지 포맷(WebP, AVIF) 대신 원본 크기의 고용량 PNG/JPG를 그대로 호출하거나, 폰트 파일 용량이 커서 렌더링을 차단(Render-blocking)하는 케이스</li>
</ul>
<hr>
<h2 id="서버">서버</h2>
<h3 id="1-서버-자원-고갈-및-동시성-처리-한계">(1) 서버 자원 고갈 및 동시성 처리 한계</h3>
<ul>
<li><strong>대량의 동시 요청 처리 한계 (Connection Pool 고갈):</strong> 순간적으로 트래픽이 몰릴 때, 데이터베이스 커넥션 풀(DBCP)이나 스레드 풀이 만료되어 이후 들어오는 요청들이 대기(Queue) 상태에 빠지거나 타임아웃(Timeout)이 발생하는 현상</li>
<li><strong>무거운 비동기/동기 작업의 스레드 점유:</strong> 이미지 프로세싱, 대용량 파일 다운로드, 복잡한 통계 연산 등의 무거운 연산이 메인 스레드를 오래 점유하여 다른 단순한 API 요청까지 함께 밀리는 현상</li>
</ul>
<h3 id="2-데이터베이스-접근-최적화-실패-db-병목">(2) 데이터베이스 접근 최적화 실패 (DB 병목)</h3>
<ul>
<li><strong>비효율적인 쿼리 실행:</strong> 인덱스가 제대로 설정되지 않은 대용량 테이블을 조회하거나, 연관 데이터 로딩 시 <code>N+1 문제</code>가 발생하여 데이터베이스 CPU 점유율이 100%까지 치솟고 서버 응답이 지연되는 케이스</li>
</ul>
<h3 id="3-메모리-누수-및-분산-서버-캐시-전략-부재">(3) 메모리 누수 및 분산 서버 캐시 전략 부재</h3>
<ul>
<li><strong>서버 어플리케이션 메모리 누수 (Memory Leak):</strong> 힙(Heap) 메모리에 해제되지 않은 객체나 세션 데이터가 지속적으로 누적되어 시간이 지날수록 가비지 컬렉션(GC) 주기가 잦아지고, 이로 인해 서버가 순간적으로 멈추는(Stop-the-world) 현상</li>
<li><strong>글로벌 캐시(Redis 등) 부재:</strong> 매번 동일한 결과를 반환하는 고비용 API를 캐싱하지 않고 매번 DB나 파일 시스템에서 처음부터 연산하여 전체 응답 처리 속도가 떨어지는 케이스</li>
</ul>
<hr>
<h2 id="인프라">인프라</h2>
<h3 id="1-와이파이-기기-등-사용자-개인-환경-문제">(1) 와이파이, 기기 등 사용자 개인 환경 문제</h3>
<ul>
<li><strong>라스트 마일(Last Mile) 네트워크 품질 저하:</strong> 사용자가 불안정한 공공 와이파이(Wi-Fi)나 음영 지역의 셀룰러(LTE/5G) 환경에 있어, 패킷 손실(Packet Loss)이 발생하고 이로 인해 재전송(Retransmission)이 반복되며 지연이 누적되는 현상</li>
<li><strong>클라이언트 기기 성능 및 브라우저 명세 한계:</strong> 사양이 낮은 저가형 모바일 기기나 오래된 PC 환경에서 브라우저의 메모리 부족(Out of Memory) 또는 CPU 오버헤드로 인해 스크립트 실행 자체가 느려져 네트워크 요청/응답 처리가 지연되는 케이스</li>
</ul>
<h3 id="2-네트워크-라우팅-및-중계-구간의-지연">(2) 네트워크 라우팅 및 중계 구간의 지연</h3>
<ul>
<li><strong>DNS 및 라우팅 지연:</strong> 사용자 ISP(인터넷 서비스 제공업체)의 DNS 조회 지연 또는 최적화되지 않은 라우팅 경로로 인해, 서버와의 최초 핸드셰이크(Handshake) 단계부터 시간이 오래 걸리는 현상</li>
</ul>
<h3 id="3-cdn-미활용-및-ssltls-핸드셰이크-오버헤드">(3) CDN 미활용 및 SSL/TLS 핸드셰이크 오버헤드</h3>
<ul>
<li><strong>CDN 부재:</strong> 이미지, JS, CSS 같은 정적 파일들을 사용자 물리적 위치와 가까운 엣지 서버(CDN)에서 서빙하지 않고, 매번 메인 인프라 서버까지 직접 요청하여 네트워크 왕복 시간(RTT)이 길어지는 현상</li>
<li><strong>SSL/TLS 프로토콜 오버헤드:</strong> HTTPS 연결을 맺는 초기 핸드셰이크 과정에서 암호화 알고리즘 연산이나 인증서 검증 지연으로 인해 네트워크 초기 지연 시간이 튀는 케이스</li>
</ul>
<h3 id="4-아키텍쳐-및-프레임-워크-이슈">(4) 아키텍쳐 및 프레임 워크 이슈</h3>
<ul>
<li><p>시스템 아키텍처 또는 플랫폼(Redis, Kafka, Kubernetes 등)의 구성이나 설정이 적절하지 않아 응답 지연이 발생하는 경우</p>
<hr>
<h1 id="결론-시스터디-답변">결론 (시스터디 답변)</h1>
</li>
</ul>
<p>아시겠지만 저는 서울· 경기 수도권 거주자 여러분이 보시기에 섬마을이나 다름없는 곳에서 살고 있습니다.</p>
<p>그렇기 때문에 언젠가는 동네에 이장님이 생길 것이고, 저는 이장님의 오른팔이나 다름없는 직군이 되어 풀스택 개발을 하게 되지 않을까 생각합니다.</p>
<p>이 날이 오면 저는 클라이언트, 서버, 인프라 관점에서 원인을 나누어 접근하겠습니다. </p>
<p>먼저 사용자 경험과 모니터링 지표를 통해 병목이 발생하는 구간을 좁힌 뒤,</p>
<p>(1) 클라이언트라면 불필요한 API 호출이나 캐싱 전략을 의심하고 요청 횟수를 줄이는 방향으로 대응하겠습니다.
(2) 서버라면 스레드·DB·메모리 병목을 확인하고 쿼리나 캐시 전략 등을 개선하겠습니다.</p>
<p>(3)인프라라면 네트워크나 플랫폼 구성 문제를 확인하고 환경 및 설정을 점검하겠습니다.</p>
<p>이렇게 원인을 먼저 단정하기보다 응답이 처리되는 흐름을 따라 의심 범위를 좁히고, 
해당 계층에 맞는 개선 방법을 적용하는 방식으로 대응한다면</p>
<p>이렇게 계층별로 원인을 좁혀가며 대응한다면 불필요한 추측을 줄이고, 응답 지연 원인을 빠르게 해결하여 서비스 품질을 높일 수 있을 것이라 생각을 합니다.</p>
<hr>
<h1 id="참고자료">참고자료</h1>
<p><a href="https://d2.naver.com/helloworld/5799075?utm_source=chatgpt.com">네이버 검색 SRE 1편 - 차세대 검색 모니터링 시스템을 향한 여정</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI King]]></title>
            <link>https://velog.io/@cold_as_hell/AI-King</link>
            <guid>https://velog.io/@cold_as_hell/AI-King</guid>
            <pubDate>Fri, 03 Jul 2026 03:58:46 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>1달 가량 새로운 일을 하며 여러 사람들과 프롬프트 작성법과 AI를 효과적으로 활용하는 방법을 공유하고, 관련 교육도 받으며 다양한 AI 도구를 직접 사용해 볼 수 있었다.</p>
<p>그 과정에서 여러 시행착오를 겪으며 다시 한번 느낀 것은, AI를 잘 활용하는 데 가장 중요한 것은 결국 <strong>기획력</strong>이라는 점. </p>
<p>기획력을 기르기위해서는 내가 기획하는 서비스에 대해 잘 파악해 가는것이 중요하다는 것을 그 어느때보다 체감했다.</p>
<hr>
<h1 id="본론">본론</h1>
<p>이 일을 하면서 가장 많은 도움을 받은 것은 🍀시스터디🍀 (인가 씨스터디 인가 암튼 ㅇㅇ)</p>
<p>특히 하네스 엔지니어링에 대해 함께 이야기하고, AI의 구조를 탐구했던 경험이 프롬프트의 지시 효율을 높이고 AI를 더 효과적으로 활용하기 위한 기획력을 기르는 데 큰 도움이 되었다.</p>
<hr>
<h1 id="결론">결론</h1>
<p>정확한 수치로 세어보진 않았지만, 육안으로도 확인될 만큼 무척 의미 있는 성과를 뽑아낼 수 있었다.</p>
<p>스터디분들의 도움이 ㄹㅇㄹㅇㄹㅇ 크다.</p>
<p>스터디 짱 ~ </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[벼락벼락벼락치기]]></title>
            <link>https://velog.io/@cold_as_hell/%EB%B2%BC%EB%9D%BD%EB%B2%BC%EB%9D%BD%EB%B2%BC%EB%9D%BD%EC%B9%98%EA%B8%B0</link>
            <guid>https://velog.io/@cold_as_hell/%EB%B2%BC%EB%9D%BD%EB%B2%BC%EB%9D%BD%EB%B2%BC%EB%9D%BD%EC%B9%98%EA%B8%B0</guid>
            <pubDate>Wed, 01 Jul 2026 03:49:31 GMT</pubDate>
            <description><![CDATA[<p><a href="https://techblog.woowahan.com/26388/">함께 본 자료</a></p>
<blockquote>
<p>사람과 AI 모두 <strong>컨벤션</strong>을 일관되게 지키지 못하는 경우가 있다면,
코드 실행 전에 <strong>자동화된 검사 도구</strong>를 사용해 사전에 오류를 차단할 수 있다.</p>
</blockquote>
<hr>
<h1 id="질문">질문</h1>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/aaaeeb4b-f75b-4e73-b56b-2a6c36600872/image.png" alt=""></p>
<hr>
<h2 id="a1">A1</h2>
<p>런타임에서 발견되는 실수들은
사람이 전부 잡기엔 한계가 있어서, 코드 작성 단계에서 자동으로 걸러지는 구조가 중요하다고 봅니다.</p>
<p>반복적으로 발생하는 실수나 규칙 위반 같은 것들은 코드 레벨에서 미리 정의해두고, 아예 잘못된 방식으로는 작성 자체가 어렵게 만드는 접근을 하는 것입니다.</p>
<hr>
<h2 id="a2">A2</h2>
<p>검증 규칙을 무조건 엄격하게만 가져가면 예외 처리가 많아지고, 반대로 느슨하면 오류를 사전에 방지하지 못하는 경우가 생겨 본래의 의미가 약해집니다.</p>
<p>해서 반드시 지켜야 하는 핵심 규칙은 강하게 고정하고, 상황이나 문맥에 따라 달라질 수 있는 부분은 옵션화·예외 허용·단계적 완화 방식으로 유연하게 풀어두는 구조가 필요합니다.</p>
<p>이 구조에 맞추어 제가 고안한 방식은 처음에는 엄격하게 적용해 가능한 실수를 넓게 잡고, 이후 실제 사용 과정에서 발생하는 오탐과 예외를 기준으로 설정을 조정하면서 점진적으로 현실에 맞게 다듬는 방식
입니다.</p>
<p>해당 방식을 사용하면 적절한 수준의 검증구조를 설계할 수 있을 것입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[잼얘하기 - 5]]></title>
            <link>https://velog.io/@cold_as_hell/%EC%9E%BC%EC%96%98%ED%95%98%EA%B8%B0-5</link>
            <guid>https://velog.io/@cold_as_hell/%EC%9E%BC%EC%96%98%ED%95%98%EA%B8%B0-5</guid>
            <pubDate>Sun, 28 Jun 2026 05:00:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/ef5ab7cb-a6e7-43a2-a4d1-0864cad23fe3/image.png" alt="">
<img src="https://velog.velcdn.com/images/cold_as_hell/post/6562080e-1bab-41b3-a006-431525e40db2/image.png" alt=""></p>
<p>잼얘봇을 만들었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[잼얘하기 - 4]]></title>
            <link>https://velog.io/@cold_as_hell/%EC%9E%BC%EC%96%98%ED%95%98%EA%B8%B0-4</link>
            <guid>https://velog.io/@cold_as_hell/%EC%9E%BC%EC%96%98%ED%95%98%EA%B8%B0-4</guid>
            <pubDate>Sun, 21 Jun 2026 04:56:19 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>저번주에는 잼얘에 일상을 섞으라는 피드백을 받아 일상적인 이야기로 잼얘?를 시작해보고자 한다.</p>
<p>이번주에는 ...</p>
<hr>
<h1 id="본론">본론</h1>
<p>이 이후에는 스터디에서 <a href="https://velog.io/@cold_as_hell/%ED%95%98%EB%84%A4%EC%8A%A4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81">지난주</a> 에 하네스 엔지니어링에 대한 대화를 했던것에 대해 생각했다.</p>
<blockquote>
<p><strong>하네스 엔지니어링의 효율을 높이려면 어떻게 해야할까?</strong></p>
</blockquote>
<hr>
<h2 id="mcp-서버">MCP 서버</h2>
<p>전 주에서도 언급되었듯 하네스 엔지니어링의 구성에는 MCP서버가 포함된다.</p>
<p>MCP 서버는 AI가 필요한 정보나 도구에 접근할 수 있도록 연결해 주는 인터페이스 역할을 수행한다. 예를 들어 사내 문서, 코드 저장소, 데이터베이스, 파일 시스템 등의 정보를 AI가 필요한 순간에 조회하고 활용할 수 있도록 돕는다.</p>
<p><strong>그런데, 문제는 데이터 탐색범위가 클 때이다.</strong></p>
<p>AI가 MCP 서버를 통해 수천 개의 문서나 방대한 코드베이스를 조회한다고 가정할 때, 연결된 검색 시스템이 <strong>단순 키워드 매칭에 의존한다면,</strong></p>
<p><strong>(1) 관련 없는 결과가 함께 반환됨.
(2) 필요한 정보를 찾기 위해 많은 검색을 반복하며 응답속도도 떨어짐</strong></p>
<p>의 문제가 발생할 것...!</p>
<hr>
<h2 id="vector-db">Vector DB</h2>
<p>위와 같은 문제를 해결하기 위해 <strong>Vector DB</strong>를 활용할 수 있다.</p>
<blockquote>
<p><strong>Vector DB는 데이터를 단순 문자열이 아닌 의미 정보를 반영한 고차원 임베딩 벡터(Embedding Vector)로 저장하며, 문자열의 일치 여부가 아니라 벡터 간 유사도를 기반으로 검색을 수행한다.</strong></p>
</blockquote>
<p>기존 소프트웨어의 검색 기능은 대부분 <strong>키워드 일치 여부</strong>를 기준으로 동작해 사용자가 입력한 단어와 저장된 데이터의 표현이 다르면 원하는 정보를 찾지 못할 수 있다.</p>
<p>예를 들어 <code>양치기</code>를 검색했는데 문서에는 <code>cold_as_hell</code>이라는 표현만 존재한다면, 두 단어가 같은 대상을 의미하더라도 검색 결과에 포함되지 않는다.</p>
<p>하지만 Vector DB는 <strong>표현이 아니라 의미의 유사성</strong>을 기반으로 데이터를 탐색하기 때문에, 직접적인 키워드가 일치하지 않아도 관련성이 높은 정보를 찾아낼 수 있다.</p>
<pre><code class="language-text">`양치기` → `거짓말쟁이` → `Profitah` → `cold_as_hell`</code></pre>
<hr>
<h2 id="하네스-엔지니어링에서-vector-db가-먹히는-이유">하네스 엔지니어링에서 Vector DB가 먹히는 이유</h2>
<blockquote>
<p>하네스엔지니어링에 Vector DB를 도입하면, <strong>불필요한 탐색을 줄이고 더 정확한 컨텍스트를 제공</strong>하여, AI의 <strong>응답 품질과 작업 효율을 모두 향상</strong>시킬 수 있다.</p>
</blockquote>
<p>하네스 엔지니어링에서도 이러한 특성은 큰 장점이 된다. AI가 MCP 서버를 통해 방대한 문서와 코드베이스를 탐색할 때 단순 문자열 매칭 대신 <strong>질문의 문맥과 의미를 기반으로 관련성이 높은 정보만 검색</strong>할 수 있기 때문이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[큐는 귀엽다]]></title>
            <link>https://velog.io/@cold_as_hell/%ED%81%90%EB%8A%94%EA%B7%80%EC%97%BD%EB%8B%A4</link>
            <guid>https://velog.io/@cold_as_hell/%ED%81%90%EB%8A%94%EA%B7%80%EC%97%BD%EB%8B%A4</guid>
            <pubDate>Tue, 16 Jun 2026 13:06:29 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/118667">링크</a></p>
<blockquote>
<p><strong>두 큐의 원소 합이 정확히 똑같아지도록 만드는 &#39;최소 작업 횟수&#39;를 구하기.</strong></p>
</blockquote>
<p>하나의 큐를 골라 원소를 추출(pop)하고, 추출된 원소를 다른 큐에 집어넣을 때 ,(insert)
두 큐의 원소 합이 정확히 똑같아지도록 만드는 &#39;최소 작업 횟수&#39;를 구하는 문제였다.</p>
<p>※ 두 큐의 합을 같게 만들 수 없는 상황이라면 -1을 반환하도록 한다.</p>
<hr>
<h1 id="풀이">풀이</h1>
<h2 id="투포인터">투포인터</h2>
<p>1차원 배열이나 리스트에서 두 개의 점(포인터)을 조작해가며 원하는 값을 찾거나 조건을 만족하는 구간을 탐색하는 기법.
양쪽 범위를 조작하는 것으로 답을 구할 수 있는 문제 (ex. 슬라이드 윈도우 등) 에서 주로 사용한다.</p>
<hr>
<h2 id="흐름">흐름</h2>
<ol>
<li><p>queue1과 queue2의 원소를 이어붙여 total_sum을 만들고, current_sum 이 target_sum이 될 때까지 투 포인터(l, r)로 queue1의 범위를 확장·축소하며 목표 합(target_sum)을 맞춘다.</p>
</li>
<li><p>이와 동시에 deque에 담아둔 queue1 queue2 원소들을 투포인터 범위에 기반해 조작하고 작업횟수 moves를 카운트한다.</p>
</li>
<li><p>최종 moves를 반환한다.</p>
</li>
</ol>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-python">from collections import deque


def solution(queue1, queue2):

    total_sum = sum(queue1) + sum(queue2)
    target_sum = total_sum // 2
    current_sum = sum(queue1)

    # 두 큐의 합이 2로 나누어 떨어지지 않는다면 정수를 반환할 수 없기에 -1 반환.
    if total_sum % 2 != 0:
        return -1


  # 1. 투 포인터로 queue1이 차지하는 연속 구간 [l ~ r]을 표현
    n = len(queue1)
    r = n - 1  
    l = 0  
    moves = 0 # 작업횟수

    q1 = deque(queue1) 
    q2 = deque(queue2)


    # 2. 현재 구간의 합이 목표치와 같아질 때까지 queue1 원소조작
    while current_sum != target_sum:
        if current_sum &lt; target_sum: # queue1 원소의합이 target_sum 보다 작으면
            r += 1 # queue1의 영역 확장 (= 원소 append)
            if r == n * 2: # 예외처리
                return -1 

            val = q2.popleft() # 위에서 말한대로 큐에도 적용
            q1.append(val)  
            current_sum += val 

        else: # queue1 원소의 합이 target_sum 보다 클 땐
            val = q1.popleft() # queue1 범위 축소
            q2.append(val)
            current_sum -= val
            l += 1

        moves += 1 # 원소조작 이후에는 moves 1 증가시키기

    return moves # 3. 최종 moves 반환
</code></pre>
<hr>
<h1 id="느낀점">느낀점</h1>
<h2 id="1">(1)</h2>
<p><code>popleft()</code> <code>appendleft()</code> 등 dequeue의 특징을 잘 살리지 못한 듯 해서 아쉽다.</p>
<h2 id="2">(2)</h2>
<p>AI 사용에 대한 생각을 더 해보게 되는 문제였다.</p>
<p>(1)의 아쉬움 때문에, 이를 보완해보고자 AI에게 이것을 풀게했고,
아래와 같은 풀이를 확인했다.</p>
<pre><code class="language-python">from collections import deque

def solution(queue1, queue2):
    q1 = deque(queue1)
    q2 = deque(queue2)

    sum1 = sum(q1)
    sum2 = sum(q2)
    total_sum = sum1 + sum2

    if total_sum % 2 != 0:
        return -1

    target = total_sum // 2
    moves = 0
    max_moves = len(queue1) * 4 # ?

    while moves &lt;= max_moves:
        if sum1 == target:
            return moves

        if sum1 &gt; target:
            val = q1.popleft()
            sum1 -= val
            q2.append(val)
        else:
            val = q2.popleft()
            sum1 += val
            q1.append(val)

        moves += 1

    return -1</code></pre>
<pre><code>1) 두 큐의 원소합이 같아질 때를 구한다.

2) 두 큐의 원소 이동 최대치는 한 큐의 4배이다. 이유는
① queue1에 있는 원소를 queue2로 모두 이동시키는 경우
② queue2 + queue1 모든 원소를 다시 queue1로 이동시키는 경우
③ queue2에 있는 원소를 queue1로 모두 이동시키는 경우 

3) 상한선까지 큐를 조작하며 조작 횟수 반환

이라고 한다...?</code></pre><p>max_moves = len(queue1) * 4라는 코드가 이해되지 않았고,
심지어 4 대신 2와 3을 넣어도 프로그래머스에서는 정답 처리 된다.
??? AI에게 이유를 물어보았지만 여전히 명확한 근거를 이해하지는 못했다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/0fbd521b-e312-4c87-b7de-868e38d91cf0/image.png" alt=""></p>
<p><del>그니까 왜 충분히 결론이 난다고 생각하는건데?!!</del></p>
<p>이번 경험을 통해 뼈저리게 느낀 점은, AI에게 &#39;해줘&#39; 라고 말하면 코드가 뽑히지만,
내가 이해하지 못하는 코드를 그대로 가져다 쓰게 된다면 훗날 유지보수나 확장 과정에서 곤란해질 확률이 있을 것이다라는 것.</p>
<p>AI가 작성한 코드를 가져다 쓸 때는 반드시 이를 이해하고 검증하는 과정이 필요하다는 것을 다시 한번 느꼈다.</p>
<p>탄탄한 기본기를 바탕으로 어떤 코드든 이해하고 검증할 수 있어야만, 이를 응용해 새로운 것을 만들어 낼 수 있을 것이다.</p>
<p>새로운 것을 만들 수 있는 사람이 되고 싶다면</p>
<blockquote>
<p><strong>기본기에 충실하자.</strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Harness Engineering]]></title>
            <link>https://velog.io/@cold_as_hell/%ED%95%98%EB%84%A4%EC%8A%A4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81</link>
            <guid>https://velog.io/@cold_as_hell/%ED%95%98%EB%84%A4%EC%8A%A4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81</guid>
            <pubDate>Sun, 14 Jun 2026 15:14:23 GMT</pubDate>
            <description><![CDATA[<h1 id="하네스-엔지니어링">하네스 엔지니어링</h1>
<blockquote>
<p>AI의 모델 성능을 높이는 것이 아니라, <strong>AI를 어떤 운영 체계 안에서 사용할 것인지</strong>를 설계하는 것이 핵심인 방법론</p>
</blockquote>
<p>하네스 엔지니어링은 모델 자체를 변경하지 않는다. 대신 모델 외부에 <strong>권한, 도구, 검증 체계, 상태 관리, 관측(Observability), 피드백 루프</strong> 등을 설계하여 에이전트가 안정적이고 일관성 있게 동작할 수 있는 환경을 구축한다.</p>
<hr>
<h1 id="이게-왜-필요할까">이게 왜 필요할까?</h1>
<p>예를 들어보자.</p>
<p>윤아는 하루에 세 번, 민성에게 텍스트로 거짓말을 하는데,
어느날 구라를 치기 위해 타이핑을 하는 것이 귀찮아졌다.</p>
<p>그래서 자신의 말투를 흉내 내며 구라를 생성하는 프롬프트를 만들어 매일 클로드와 코덱스에게 입력하고, 본인 대신 구라를 치게 했다.</p>
<p>하지만 클로드와 코덱스는 종종 윤아의 말투를 잊거나, 미리 알려준 설정을 놓치거나, 이전에 했던 이야기와 모순되는 내용을 말하는 등 원하는 결과를 안정적으로 만들어내지 못했다.</p>
<h2 id="컨텍스트-엔지니어링을-도입한다면">컨텍스트 엔지니어링을 도입한다면</h2>
<p>윤아의 SNS 대화 기록, 메신저 말투, 자주 사용하는 표현, 미리 정의한 설정 등을 AI에게 함께 제공하여 더 일관된 답변을 유도할 수 있다.</p>
<p>즉, <strong>AI에게 더 좋은 참고 자료를 제공하는 것</strong>이 컨텍스트 엔지니어링이다.</p>
<p>하지만 컨텍스트를 아무리 잘 구성해도 모델이 일부 정보를 놓치거나, 긴 문맥을 제대로 활용하지 못하거나, 상황에 따라 다른 결론을 내릴 가능성은 여전히 존재한다.</p>
<h2 id="하네스-엔지니어링을-도입한다면">하네스 엔지니어링을 도입한다면</h2>
<p>하네스 엔지니어링은 &quot;정보를 더 넣어주는 것&quot;이 아니라 <strong>AI가 일하는 방식을 설계하는 것</strong>에 가깝다.</p>
<p>예를 들어 다음과 같은 장치를 둘 수 있다.</p>
<ul>
<li>답변 생성 전에 윤아의 최신 말투 데이터를 자동으로 불러온다.</li>
<li>과거에 했던 구라와 충돌하는 내용이 있는지 별도 검증 모듈이 확인한다.</li>
<li>말투 유사도가 일정 기준 이하이면 자동으로 다시 생성한다.</li>
<li>출력 결과를 다른 AI가 한 번 더 검토하여 설정 위반 여부를 검사한다.</li>
<li>모든 대화와 실패 사례를 로그로 저장하고, 반복되는 오류는 시스템 차원에서 개선한다.</li>
</ul>
<p>이처럼 하네스 엔지니어링은 <strong>AI가 실수하더라도 안전하고 일관되게 동작하도록 주변 환경을 설계하는 접근법</strong>이다.</p>
<hr>
<h1 id="하네스의-구조">하네스의 구조</h1>
<p>하네스는 보통 다음 요소들로 구성된다.</p>
<ol>
<li><strong>Execution(실행)</strong>: 작업 흐름과 에이전트 실행 제어</li>
<li><strong>Tools(도구)</strong>: API, 데이터베이스, 파일 시스템 등 외부 기능 연결</li>
<li><strong>Context(컨텍스트)</strong>: 필요한 정보와 메모리 제공</li>
<li><strong>State(상태)</strong>: 작업 진행 상황과 중간 결과 관리</li>
<li><strong>Evaluation(검증)</strong>: 출력 품질과 정책 준수 여부 확인</li>
<li><strong>Lifecycle &amp; Feedback(생명주기 및 피드백)</strong>: 로그 수집, 모니터링, 지속적인 개선</li>
</ol>
<p>결국 프롬프트 엔지니어링이 <strong>&quot;무엇을 말할지&quot;</strong>, 컨텍스트 엔지니어링이 <strong>&quot;무엇을 보여줄지&quot;</strong>를 고민하는 분야라면, 하네스 엔지니어링은 <strong>&quot;AI가 어떤 규칙과 절차 안에서 안전하게 일하도록 만들지&quot;</strong>를 고민하는 분야라고 볼 수 있다.</p>
<hr>
<h2 id="프롬프트-엔지니어링-vs-컨텍스트-엔지니어링-vs-하네스-엔지니어링">프롬프트 엔지니어링 vs 컨텍스트 엔지니어링 vs 하네스 엔지니어링</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>핵심 질문</th>
<th>초점</th>
</tr>
</thead>
<tbody><tr>
<td>프롬프트 엔지니어링</td>
<td>AI에게 무엇을 지시할 것인가?</td>
<td>지시문과 출력 형식 최적화</td>
</tr>
<tr>
<td>컨텍스트 엔지니어링</td>
<td>AI에게 어떤 정보를 보여줄 것인가?</td>
<td>문서, 메모리, 대화 이력, 검색 결과 등 컨텍스트 구성</td>
</tr>
<tr>
<td>하네스 엔지니어링</td>
<td>AI가 어떤 환경과 규칙 안에서 일하게 할 것인가?</td>
<td>권한, 도구, 상태 관리, 검증, 로그, 피드백 루프, 실행 환경 설계</td>
</tr>
</tbody></table>
<hr>
<h1 id="하네스-관련-실험">하네스 관련 실험</h1>
<h2 id="hashline-실험">Hashline 실험</h2>
<p><a href="https://blog.can.ac/2026/02/12/the-harness-problem/">ㄴ 보기</a></p>
<p>주제: 하네스 엔지니어링은 모델을 바꾸지 않고도 AI의 성능을 크게 향상시킬 수 있을까?</p>
<p>세부내용 : 평소 AI의 작업 방식이 AI가 이해하기에 직관적이지 않았고,
좀 더 직관적으로 이해할 수 있도록 작업환경만 바꿔주었다.</p>
<p>결과 : 동일 벤치마크에서 Grok Code Fast 1의 성능이 6.7%에서 68.3%로 약 10배 향상.
전체 모델 평균 출력 토큰 약 20% 감소.</p>
<blockquote>
<p>AI는 작업하는 환경에 의해서도 영향을 크게 받는다.</p>
</blockquote>
<blockquote>
<p>도구 사용 방식, 입력 형식, 검증 절차 등 하네스를 잘 설계하면 성능과 효율을 크게 개선할 수 있다.</p>
</blockquote>
<hr>
<h2 id="openai의-agentsmd-설계">openAI의 AGENTS.md 설계</h2>
<p><a href="https://openai.com/ko-KR/index/harness-engineering/?utm_source=chatgpt.com">ㄴ 보기 </a></p>
<p>주제: openAI의 AGENTS.md 설계 방식을 왜 목차형으로 바꾸었을까?</p>
<p>세부내용 : 작업환경을 한번에 적으면 양이 너무 방대하기 때문에 AI가 놓치는 경우가 생겼다.
해서, 현재 작업과 가까운 문서부터 차례대로 문서를 읽게 하는 목차형으로 md 설계 방식을 바꾸었다.</p>
<p>결과 : 에이전트가 필요한 정보를 더 정확하게 찾아 활용할 수 있게 되었고, 작업 효율을 높이는 동시에 불필요한 컨텍스트 사용을 줄일 수 있었다.</p>
<blockquote>
<p>하네스 엔지니어링시 AI에게  모든 정보를 한 번에 주는 것보다 필요한 순간에 필요한 정보만 제공하는 것이 더 효과적이다.</p>
</blockquote>
<hr>
<h1 id="시스터디-결론">시스터디 결론</h1>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/27c4249b-5ad1-42dd-923d-83ad7d2d9b01/image.png" alt=""></p>
<h2 id="a1">A1</h2>
<p>이 실험이 시사하는 바는 AI의 성능이 모델 자체뿐 아니라 작업 환경(하네스)에도 크게 영향을 받는다는 점입니다. 도구 사용 방식, 입력 형식, 검증 절차 등을 적절히 설계하면 모델을 변경하지 않고도 성능과 생산성을 크게 높일 수 있습니다.</p>
<p>이를 바탕으로 프로그램을 설계할 때는 AI가 수행해야 할 작업에 필요한 권한과 도구를 명확히 정의하고, 핵심 로직을 안정적으로 수행할 수 있도록 검증 체계와 피드백 루프를 포함한 하네스를 구축하겠습니다.</p>
<h2 id="a2">A2</h2>
<p>OpenAI의 AGENTS.md 설계 방식이 초기에 실패한 이유는, 
작업 환경에 대한 모든 지침을 AGENTS.md 하나에 한꺼번에 담았더니 문서의 양이 지나치게 방대해지면서 AI가 지침을 놓치는 경우가 생겨났기 때문입니다.</p>
<p>이를 해결하기 위해 OpenAI는 AGENTS.md를 목차형태로 구조화했습니다.</p>
<p>해당 사례를 통해 에이전트가 아키텍처 규칙을 반복적으로 위반하는 이유 중 하나는,
한 번에 너무 많은 정보를 입력받았기 때문이라는 것을 알 수 있었습니다.</p>
<p>그러니 우선 AI가 해야 할 일을 작은 단위로 나누어 순차적으로 수행하도록 하고, 필요한 정보만 적절한 시점에 제공하여 안정성과 일관성을 높여보겠습니다.</p>
<hr>
<h1 id="참고문서">참고문서</h1>
<p><a href="https://madplay.github.io/post/harness-engineering">프롬프트와 컨텍스트를 넘어, AI 에이전트를 위한 하네스 엔지니어링
</a></p>
<p><a href="https://www.samsungsds.com/kr/insights/ai-harness-framework-in-the-multi-ai-era.html">기업 AI 운영 전략: 멀티 AI 시대의 AI 하네스 프레임워크
</a></p>
<hr>
<h1 id="하네스-딥다이브-함께보면-좋은-자료">하네스 딥다이브 (함께보면 좋은 자료)</h1>
<p>하네스 엔지니어링 도구 등을 다루는 가이드입니다.
JDC를 통해 추천 받았습니다. JDC 짱 ~ ! </p>
<p><a href="http://github.com/walkinglabs/awesome-harness-engineering">awesome-harness-engineering </a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[잼얘하기 - 3]]></title>
            <link>https://velog.io/@cold_as_hell/%EC%9E%BC%EC%96%98%ED%95%98%EA%B8%B0-3</link>
            <guid>https://velog.io/@cold_as_hell/%EC%9E%BC%EC%96%98%ED%95%98%EA%B8%B0-3</guid>
            <pubDate>Sun, 14 Jun 2026 04:05:05 GMT</pubDate>
            <description><![CDATA[<p>전생처럼 느껴지는 까마득한 과거의 나만무를 떠올려보면 우리 팀은 나만무 개발에 앞서 와이어프레임이라는 것을 그렸다.
모든 팀원이 설계할 프로그램의 구조를 제대로 이해했는지 검증하고,
팀원 외 사람들에게 프로젝트 기획 설명을 할 때 도움이 되는 과정이였다.</p>
<p>그런데 이 과정에 피그마 플러그인을 사용한다면, 기획 단계의 결과물이 단순 화면 기획서에 그치지 않고 실제 인터랙션이 가능한 서비스 수준으로 확장되는 경험을 할 수 있다고 한다.</p>
<hr>
<h1 id="anima">Anima</h1>
<p>UI/UX 디자인 도구인 피그마(Figma)에서 시각적으로 작업한 디자인 화면을 실제 웹 브라우저에서 동작하는 코드로 변환하는 플러그인</p>
<p><a href="https://www.animaapp.com/blog/ko/%EB%94%94%EC%9E%90%EC%9D%B8%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%BD%94%EB%94%A9/9598/">ㄴ 확인하기 </a></p>
<hr>
<h1 id="사용법">사용법</h1>
<h2 id="1-figma로-이미지를-만들고-actions-아이콘을-클릭-해-anima-플러그인을-찾아보자">1. figma로 이미지를 만들고 actions 아이콘을 클릭 해 Anima 플러그인을 찾아보자.</h2>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/3d39cf0b-8ef9-4e5b-a726-49a217b78744/image.png" alt=""></p>
<h2 id="2-1에서-디자인한-프레임을-클릭하고-코드-변환을-원하는-프레임을-선택해준다">2. 1에서 디자인한 프레임을 클릭하고, 코드 변환을 원하는 프레임을 선택해준다.</h2>
<p>React,  Vue,  HTML 중 원하는 형식으로 코드 출력도 가능하며</p>
<p>스타일링도 CSS, Tailwind, Email compatible 중 선택가능하다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/03c7a640-83ee-4f51-9b70-78dc504e7f55/image.png" alt=""></p>
<h2 id="3-preview를-통해-프레임이-코드로-잘-변환되었는지-확인해보자">3. Preview를 통해 프레임이 코드로 잘 변환되었는지 확인해보자.</h2>
<p>이후 실제 개발 환경에 바로 적용하거나, 수정을 원하면 수정하여 적용하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/03bf64cd-f593-4ea9-82ca-29e37a4fb04e/image.png" alt=""></p>
<hr>
<h1 id="기타">기타</h1>
<p>디자인 컴포넌트를 직접 그리는 것이 번거롭다면, 
정글인으로서 익숙할 HTML 등을 Figma 디자인 파일로 추출해 주는 플러그인을 활용하는 것도 좋은 방법일 것 이다.</p>
<p><a href="https://www.figma.com/community/plugin/1159123024924461424">ㄴ 예시</a></p>
<hr>
<h1 id="결론">결론</h1>
<p>다양한 피그마 플러그인을 사용하면 효율적인 퍼블리싱과 이미지 제작에 도움을 받을 수 있어, 
사이드 프로젝트의 개발 속도를 높이고 포트폴리오를 보강하는데 도움이 될 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[LLM과 RAG의 차이점]]></title>
            <link>https://velog.io/@cold_as_hell/LLM%EA%B3%BC-RAG%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@cold_as_hell/LLM%EA%B3%BC-RAG%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Sun, 07 Jun 2026 13:39:08 GMT</pubDate>
            <description><![CDATA[<h1 id="llmlarge-language-model">LLM(Large Language Model)</h1>
<p>대규모 텍스트 데이터로 학습된 언어 모델.
기본적으로는 학습 데이터에 기반해 답변한다.</p>
<p>그러나 LLM은 3가지 한계를 가지고 있다.</p>
<p><strong>(1) 맞춤형 데이터 부족</strong></p>
<p>기업 내부 문서나 특정 분야의 전문 데이터는 학습 데이터에 포함되지 않는 경우가 많다. 따라서 기업 환경이나 도메인별 업무에 바로 적용하기에는 한계가 있다.</p>
<p><strong>(2) 업데이트 비용 문제</strong></p>
<p>새로운 지식을 모델 자체에 반영하려면 추가 학습(Fine-tuning) 또는 재학습이 필요하다. 이는 많은 시간과 비용이 소요되며, 잘못 학습된 정보를 수정하는 것도 쉽지 않다.</p>
<p>※ 단, 검색·조회 도구(RAG)와 결합될 경우 최신 정보나 외부 지식을 활용할 수 있다. 다만 이러한 검색은 시스템 구조에 따라 항상 수행되거나, 필요할 때만 선택적으로 수행될 수 있다.</p>
<p>(예: &quot;현재 환율이 얼마인가요?&quot;, &quot;오늘 서울 날씨는 어떤가요?&quot; 등 실시간 정보가 필요한 질문)</p>
<p><strong>(3) 환각(Hallucination) 발생</strong></p>
<p>LLM은 실제 사실과 다른 내용을 그럴듯하게 생성하는 경우가 있다.</p>
<ul>
<li><strong>내재적 환각(Intrinsic Hallucination)</strong>: 학습한 정보를 잘못 조합하거나 문맥을 왜곡하여 사실과 다른 답변을 생성하는 경우</li>
<li><strong>외재적 환각(Extrinsic Hallucination)</strong>: 학습 데이터나 제공된 근거에 없는 내용을 추측하여 생성하는 경우</li>
</ul>
<hr>
<h1 id="rag-retrieval-augmented-generation">RAG (Retrieval-Augmented Generation)</h1>
<p>RAG는 LLM의 한계를 보완하기 위해 등장한 기술이다.</p>
<h2 id="방식">방식</h2>
<p>질문이 들어오면 외부 데이터베이스나 문서에서 관련 정보를 먼저 검색(Retrieval)한 뒤, 검색된 정보를 LLM에 함께 제공하여 답변을 생성(Generation)한다.</p>
<h2 id="llm과의-차이">LLM과의 차이</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>LLM</th>
<th>RAG</th>
</tr>
</thead>
<tbody><tr>
<td>맞춤형 데이터</td>
<td>학습 데이터에 의존</td>
<td>사내 문서·전문 데이터 활용 가능</td>
</tr>
<tr>
<td>지식 업데이트</td>
<td>추가 학습 필요</td>
<td>외부 DB 갱신으로 즉시 반영 가능</td>
</tr>
<tr>
<td>환각 가능성</td>
<td>상대적으로 높음</td>
<td>상대적으로 낮음</td>
</tr>
</tbody></table>
<hr>
<h3 id="1-맞춤형-데이터-활용">(1) 맞춤형 데이터 활용</h3>
<p>RAG는 사내 문서, 제품 매뉴얼, 법령, 연구 자료 등 원하는 데이터를 검색 대상으로 직접 지정할 수 있어, 
기업 환경에 맞는 답변을 제공할 수 있다.</p>
<h3 id="2-업데이트-방식">(2) 업데이트 방식</h3>
<p>RAG는 외부 데이터베이스의 문서를 추가하거나 수정하는 것만으로 최신 정보를 반영할 수 있다. 
모델 자체를 변경하지 않기 때문에 비용과 시간이 크게 절감된다.</p>
<h3 id="3-환각hallucination-감소">(3) 환각(Hallucination) 감소</h3>
<p>RAG는 검색된 문서를 근거로 답변을 생성하기 때문에 환각 가능성을 줄일 수 있다. 
다만 검색된 문서의 품질이 낮거나 모델이 문서를 잘못 해석하는 경우에는 이녀석도 마찬가지로 환각이 발생할 수 있다.</p>
<hr>
<h1 id="시스터디-결론">시스터디 결론</h1>
<blockquote>
<p>Q . ChatGPT와 같은 LLM이 이미 많은 지식을 가지고 있는데, 왜 RAG(Retrieval-Augmented Generation)가 필요할까요?</p>
</blockquote>
<p>A. LLM의 치명적인 단점을 보완하기 위해서입니다. </p>
<p>LLM은 개인 SNS나 회사 내부 데이터 등 맞춤형 데이터를 활용하기 어렵고,
새로운 정보를 모델에 반영하려면 많은 시간을 들여야 하며,
학습한 정보를 잘못 조합하여 사실과 다른 답변을 생성하거나 근거없는 내용을 추측하는 환각 현상이 발생하기도 합니다.</p>
<p>RAG는 이러한 한계를 보완하기 위해 등장했습니다. 
RAG는 질문이 들어오면 외부 문서나 데이터베이스에서 관련 정보를 먼저 검색한 뒤, 
그 내용을 근거로 답변을 생성합니다. </p>
<p>덕분에 맞춤형 데이터를 활용할 수 있고, 모델을 재학습하지 않아도 외부 DB만 갱신하면 최신 정보를 반영할 수 있으며, 검색된 문서를 근거로 답변하기 때문에 근거없는 내용을 추측하고 생성하는 외재적 환각 발생도 줄일 수 있습니다.</p>
<p>즉, RAG는 LLM의 답변정확성과 활용성을 높여주고,
언어모델의 사용성을 극대화해줍니다.</p>
<hr>
<h1 id="참고자료">참고자료</h1>
<p><a href="https://tech.ktcloud.com/entry/2025-08-ktcloud-ai-rag-%EC%8B%9C%EC%8A%A4%ED%85%9C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%ED%95%B4">[Tech Series] kt cloud AI 검색 증강 생성(RAG) #1 : 핵심 개념과 시스템 구조 이해</a></p>
<p><a href="https://enterprise.kt.com/bt/dxstory/2521.do">LLM의 환각현상, 어떻게 보완할 수 있을까?</a></p>
<p>킹 갓 클로드</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[잼얘하기 - 2]]></title>
            <link>https://velog.io/@cold_as_hell/%EA%B3%BC%EC%A0%9C-%EC%9E%BC%EC%96%98%ED%95%98%EA%B8%B0-Google-Analytics-4-GA4</link>
            <guid>https://velog.io/@cold_as_hell/%EA%B3%BC%EC%A0%9C-%EC%9E%BC%EC%96%98%ED%95%98%EA%B8%B0-Google-Analytics-4-GA4</guid>
            <pubDate>Thu, 04 Jun 2026 15:51:44 GMT</pubDate>
            <description><![CDATA[<p>디지털 기술은 이미지 생성, 자연어 처리, 사용자 행동 분석을 비롯한 많은 분야에서 활용되며, 공통적으로 더 나은 의사결정을 돕는다. </p>
<p>내가 요즘 꽂힌 키워드에도 디지털 기술이 도움이 될 듯하다.</p>
<p>그건 바로... </p>
<blockquote>
<p><strong>전환율 상승을 위한 트래픽 유입 전략</strong></p>
</blockquote>
<p>지속가능한 서비스를 만들려면 단순한 기능 개발을 넘어, 사용자를 유입시키고, 체류하게 하며, 전환으로 이어지게 하는 전략이 필요하다. 이 과정에서 중요한 것은 <strong>객관적인 데이터를 기반으로 의사결정을 내리는 것</strong>이다.</p>
<p>그러나, 서비스를 운영하다 보면 페이지 조회, 클릭, 회원가입, 결제 등 수많은 사용자 행동 데이터가 쌓인다. <strong>&#39;정말 필요한 데이터&#39;를 얻기 위해서는 어떻게 해야할까</strong>
<a href="https://developers-kr.googleblog.com/2016/09/pirate-metrics-aarrr-with-firebase.html">500 Startups의 설립자인 Dave McClure는 제품의 사용자 수명 주기를 다음 다섯 단계로 단순화할 수 있다고 했다.</a></p>
<blockquote>
<p><strong>Acquisition(획득)</strong> — 사용자가 어떤 채널·키워드를 통해 유입되는가
<strong>Activation(활성화)</strong> — 유입된 사용자가 서비스의 핵심 가치를 처음으로 경험하는가 (예: 회원가입, 첫 기능 사용)
<strong>Retention(유지)</strong> — 사용자가 재방문하여 서비스를 지속적으로 사용하는가
<strong>Referral(추천)</strong> — 만족한 사용자가 다른 사람에게 서비스를 추천하는가
<strong>Revenue(수익)</strong> — 최종적으로 결제 등 수익 행동으로 전환되는가</p>
</blockquote>
<p>이를 측정하고 분석할 수 있는 대표적인 도구가 <a href="https://developers.google.com/analytics?hl=ko">Google Analytics 4(GA4)</a> 이다. </p>
<hr>
<h1 id="google-analytics-4-ga4">Google Analytics 4 (GA4)</h1>
<p>2023년 UA(Universal Analytics)를 공식 종료하면서 사람들은 이를 대체할 분석 플랫폼을 찾게 되는데,</p>
<p>이때, Google이 발표한 표준 분석플랫폼인 Google Analytics 4 (GA4)가 주목받게 된다.
UA는 &#39;사용자의 서비스 방문과 페이지뷰&#39; 중심이었지만 GA4는 사용자 행동을 모두 &#39;이벤트&#39;로 수집하여 웹과 앱을 통합 추적했다.
기존 UA와의 가장 큰 차이점은 이벤트 기반 데이터 모델을 채택했다는 점이다.</p>
<p>GA4에서는 페이지뷰, 클릭, 스크롤, 결제 완료 등 사용자의 모든 행동을 동일한 이벤트 구조로 수집한다. 
덕분에 세션 단위가 아닌 사용자 단위의 여정(User Journey) 을 추적할 수 있어 AARRR 전 단계에 걸친 분석의 정밀도가 크게 높아졌다.</p>
<table>
<thead>
<tr>
<th>AARRR 단계</th>
<th>GA4에서 확인 가능한 데이터</th>
<th>대표 보고서</th>
</tr>
</thead>
<tbody><tr>
<td>Acquisition</td>
<td>채널별 유입량, 검색 키워드, 리퍼러 URL</td>
<td>획득 &gt; 트래픽 획득</td>
</tr>
<tr>
<td>Activation</td>
<td>첫 방문 시 핵심 이벤트 완료율, 온보딩 이탈 지점</td>
<td>참여 &gt; 이벤트</td>
</tr>
<tr>
<td>Retention</td>
<td>재방문율, 코호트별 유지율, DAU/MAU</td>
<td>유지 &gt; 코호트 탐색</td>
</tr>
<tr>
<td>Referral</td>
<td>추천 트래픽 채널, 바이럴 유입 경로</td>
<td>획득 &gt; 트래픽 획득 (Referral 필터)</td>
</tr>
<tr>
<td>Revenue</td>
<td>결제 전환율, 거래 금액, ARPU</td>
<td>수익 창출 &gt; 전자상거래</td>
</tr>
</tbody></table>
<hr>
<h2 id="웹-속성과-데이터-스트림">웹 속성과 데이터 스트림</h2>
<p>GA4에서 분석을 시작하려면 속성(Property) 과 데이터 스트림(Data Stream) 을 순서대로 생성해야 한다.</p>
<p>속성(Property) 은 분석 단위다. 
하나의 서비스(예: myapp.com)에 대응하는 분석 컨테이너라고 이해하면 된다.</p>
<p>데이터 스트림(Data Stream) 은 속성에 연결되는 실제 데이터 수집 출처다. 하나의 속성에 웹 스트림, iOS 앱 스트림, Android 앱 스트림을 동시에 연결할 수 있어 멀티플랫폼 서비스에서도 통합된 사용자 분석이 가능하다.</p>
<h3 id="설정순서">설정순서</h3>
<p><strong>1. GA4 관리 콘솔 → 속성 만들기</strong></p>
<p>속성(= 분석할 사이트 넣기) 을 만들자. 게시글 작성일을 기준으로,
무료계정에서 2,000 개까지 생성 가능하니 참고하자.
<img src="https://velog.velcdn.com/images/cold_as_hell/post/c821da96-af53-4ee4-ba4e-3509275fd3e5/image.png" alt=""></p>
<p><strong>2. 속성 세부사항 설정</strong>
속성의 세부사항을 설정하자.
<img src="https://velog.velcdn.com/images/cold_as_hell/post/e5f78199-e538-4ac2-9f69-97a481553b7a/image.png" alt=""></p>
<p><strong>3. 속성 세부사항 설정</strong>
최종적으로 배포된 사이트 URL을 스트림에 넣어주면 기본적인 설정은 끝난다. 여기서 발급되는 &#39;측정 ID(G-로 시작)&#39;를 잘 확인해두자.
<img src="https://velog.velcdn.com/images/cold_as_hell/post/259e33e5-4f46-4699-b0b8-c291e71e2101/image.png" alt=""></p>
<hr>
<h2 id="프로젝트-환경설정">프로젝트 환경설정</h2>
<p>Next.js 환경에서의 GA4 연동 방법만을 다뤄보겠다.</p>
<h3 id="패키지-install">패키지 install</h3>
<p>우선 GA4의 공식 지원 패키지인 <code>@next/third-parties</code> 패키지 설치가 필요하다.</p>
<pre><code>npm install @next/third-parties</code></pre><h3 id="layouttsx">layout.tsx</h3>
<p><code>@next/third-parties</code> 에서 <code>GoogleAnalytics</code> 컴포넌트를 불러와 적용한다.
이 때, 측정 ID를 코드에 직접 노출하면 외부에 공개될 수 있으므로,
루트 디렉토리에 <code>.env</code> 파일을 생성하여 환경변수로 관리한다.</p>
<p>측정 ID가 유출될 경우, 제3자가 동일한 ID로 데이터를 수집하거나 통계를 오염시킬 수 있다는 점을 반드시 유의해야한다.</p>
<pre><code>// .env

my_gaId = ...
</code></pre><pre><code class="language-ts">
import { GoogleAnalytics } from &#39;@next/third-parties/google&#39;

// ...


  const gaId = process.env.NEXT_PUBLIC_GA_ID;

  return (
    &lt;html lang=&quot;ko&quot;&gt;
      &lt;body className=&quot;bg-white text-gray-900 antialiased&quot;&gt;{children}&lt;/body&gt;
      {gaId ? &lt;GoogleAnalytics gaId={gaId} /&gt; : null}
    &lt;/html&gt;
  );
</code></pre>
<hr>
<h2 id="결과">결과</h2>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/85c41724-0bdd-4442-9a12-1126754ce8f6/image.png" alt="">
<img src="https://velog.velcdn.com/images/cold_as_hell/post/e60ed1bd-112c-4a2e-93cb-bd2005ff7c00/image.png" alt=""></p>
<p>최근 30분 동안 서비스를 이용한 활성 사용자 수를 비롯해, 사용자가 어떤 페이지를 방문했는지, 어떤 이벤트를 수행했는지, 어떤 경로와 키워드를 통해 유입되었는지 등을 실시간으로 확인 가능하며 이 자료는 일반적인 웹 서비스나 앱은 물론, 게임 클라이언트 다운로드 페이지처럼 전환율이 중요한 다양한 웹 환경에서도 활용할 수 있다.</p>
<p>특히 수많은 사용자 행동 로그를 단순히 나열하는 것이 아니라, AARRR 관점에서 유입(Acquisition), 활성화(Activation), 유지(Retention), 추천(Referral), 수익(Revenue)과 관련된 핵심 지표를 추적할 수 있어 비즈니스 성장에 필요한 데이터만 선별적으로 파악할 수 있기에 감에 의존하지 않고 실제 사용자 행동 데이터를 기반으로 서비스 개선 방향을 수립하고, 마케팅 전략과 전환율 최적화에도 도움이 된다.</p>
<hr>
<h2 id="그-외">그 외</h2>
<p>프론트엔드에서 추적이 불가능한 카드 결제 정보, CVR 데이터 등은 백엔드에서 GA4의 Measurement Protocol을 이용해 Google Analytics로 직접 이벤트 데이터를 전송하면, 통계를 확인할 수 있다.</p>
<p>이를 활용하면 실제 비즈니스 성과와 연결되는 전환 데이터를 보다 정확하게 측정할 수 있으며, 프론트엔드 이벤트와 함께 분석하여 사용자 행동 흐름을 더욱 입체적으로 파악할 수 있는 것 이다.</p>
<pre><code class="language-ts">// 백엔드에서 GA4로 결제 완료 이벤트를  직접 전송하는 예제

const GA_MEASUREMENT_ID = process.env.GA_MEASUREMENT_ID; 
const GA_API_SECRET = process.env.GA_API_SECRET;        
async function sendPurchaseEvent(clientId: string, transactionId: string, value: number) {
  // 이 clientId를 결제 요청 시 백엔드로 함께 전송
  await fetch(
    `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&amp;api_secret=${GA_API_SECRET}`,
    {
      method: &#39;POST&#39;,
      body: JSON.stringify({
        client_id: clientId, // GA4 스크립트가 가 자동발급하여 브라우저 쿠키에 저장한 id 값 요청.
        events: [
          {
            name: &#39;purchase&#39;,
            params: {
              transaction_id: transactionId,
              value: value,
              currency: &#39;KRW&#39;,
            },
          },
        ],
      }),
    }
  );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[operator.itemgetter]]></title>
            <link>https://velog.io/@cold_as_hell/operator.itemgetter</link>
            <guid>https://velog.io/@cold_as_hell/operator.itemgetter</guid>
            <pubDate>Wed, 03 Jun 2026 11:19:24 GMT</pubDate>
            <description><![CDATA[<h1 id="서론">서론</h1>
<p>operator 모듈은 내장 연산자에 대응하는 함수들을 제공해,
파이썬의 변수나 값의 연산 및 처리에 사용된다.</p>
<p>예를 들면, <code>x + y</code> 를 <code>operator.add(x, y)</code> 로 표현하는 방식으로 사용한다.</p>
<p>이 모듈은 연산자를 함수처럼 다룰 수 있어 sorted() 나 map() 같은 고차 함수의 인자로 활용할 때 특히 유용하다. 대표적인 예시로는 이런 것들이 있다.</p>
<ul>
<li>객체 비교 연산 (lt, le, eq, ne, ge, gt)</li>
<li>논리 연산 (not_, truth, is_, is_not)</li>
<li>수학 및 비트 연산 (add, sub, mul, div 등)</li>
<li>시퀀스 연산 (concat, contains, getitem 등)</li>
</ul>
<hr>
<h1 id="operatoritemgetter">operator.itemgetter</h1>
<p>변수나 값의 연산 및 처리에 사용되는 모듈함수 답게
튜플, 리스트, 딕셔너리 등 두 가지 이상의 요소가 있는 자료형에서 특정 속성(인덱스나 키)을 가져와 연산에 사용한다.</p>
<h2 id="예시">예시</h2>
<p><a href="https://docs.python.org/3/reference/datamodel.html#object.__getitem__"><strong>getitem</strong>()</a> 메서드를 사용해 딕셔너리, 튜플 등 피연산자에서 특정 인덱스 또는 키의 값을 추출하는 호출 가능한 객체를 반환가능</p>
<pre><code class="language-python">from operator import itemgetter

# 1. 문자열 + 인덱스 1개
itemgetter(1)(&#39;ABCDEFG&#39;)          # 결과: &#39;B&#39;

# 2. 문자열 + 인덱스 여러 개 (튜플 반환)
itemgetter(1, 3, 5)(&#39;ABCDEFG&#39;)    # 결과: (&#39;B&#39;, &#39;D&#39;, &#39;F&#39;)

# 3. 문자열 + 슬라이스 객체 (slice(start, end))
itemgetter(slice(2, None))(&#39;ABCDEFG&#39;) # 결과: &#39;CDEFG&#39; (index 2부터 끝까지)

# 4. 딕셔너리 + Key
soldier = dict(rank=&#39;captain&#39;, name=&#39;dotterbart&#39;)
itemgetter(&#39;rank&#39;)(soldier)       # 결과: &#39;captain&#39;</code></pre>
<hr>
<h2 id="복합-자료형에서-활용-예시">복합 자료형에서 활용 예시</h2>
<pre><code class="language-python">from operator import itemgetter

inventory = [(&#39;apple&#39;, 3), (&#39;banana&#39;, 2), (&#39;pear&#39;, 5), (&#39;orange&#39;, 1)]

getcount = itemgetter(1)  # 각 튜플의 1번째 값(숫자)을 추출

list(map(getcount, inventory))  # [3, 2, 5, 1]

# 1번째 값(숫자) 기준 오름차순 정렬
sorted(inventory, key=getcount)
# [(&#39;orange&#39;, 1), (&#39;banana&#39;, 2), (&#39;apple&#39;, 3), (&#39;pear&#39;, 5)]</code></pre>
<hr>
<h1 id="참고문서">참고문서</h1>
<p><a href="https://docs.python.org/3/library/operator.html">operator — Standard operators as functions</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이웃 한 칸]]></title>
            <link>https://velog.io/@cold_as_hell/%EC%9D%B4%EC%9B%83-%ED%95%9C-%EC%B9%B8</link>
            <guid>https://velog.io/@cold_as_hell/%EC%9D%B4%EC%9B%83-%ED%95%9C-%EC%B9%B8</guid>
            <pubDate>Tue, 02 Jun 2026 11:49:35 GMT</pubDate>
            <description><![CDATA[<p>이번 주는 다른 주에 비해 쉬운 난이도의 문제가 꽤 있어서 좋았다 😊
하지만 이에 안주하지 않고 꾸준히 과제에 임하는 스터디원들을 보며, 
나 역시 평소보다 나태해지지 말자 마음먹었다.</p>
<hr>
<h1 id="문제">문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/250125">링크</a></p>
<blockquote>
<p><strong>현재 위치와 같은 색을 가진 인접한 칸(상·하·좌·우)의 개수를 구하는 문제</strong></p>
</blockquote>
<h1 id="풀이">풀이</h1>
<p>감사히도 문제 작성자가 의사코드를 알려주어 예상보다 수월하게 풀 수 있었다.
문제에 언급된 풀이방법은 아래와 같다.</p>
<pre><code>1. 정수를 저장할 변수 n을 만들고 board의 길이를 저장합니다.
2. 같은 색으로 색칠된 칸의 개수를 저장할 변수 count를 만들고 0을 저장합니다.
3. h와 w의 변화량을 저장할 정수 리스트 dh, dw를 만들고 각각 [0, 1, -1, 0], [1, 0, 0, -1]을 저장합니다.
4. 반복문을 이용해 i 값을 0부터 3까지 1 씩 증가시키며 아래 작업을 반복합니다.
    4-1. 체크할 칸의 h, w 좌표를 나타내는 변수 h_check, w_check를 만들고 각각 h + dh[i], w + dw[i]를 저장합니다.
    4-2. h_check가 0 이상 n 미만이고 w_check가 0 이상 n 미만이라면 다음을 수행합니다.
        4-2-a. board[h][w]와 board[h_check][w_check]의 값이 동일하다면 count의 값을 1 증가시킵니다.
5. count의 값을 return합니다.</code></pre><p><img src="https://velog.velcdn.com/images/cold_as_hell/post/db72e4d4-963f-419f-a590-3482a70495f5/image.png" alt="">
<img src="https://velog.velcdn.com/images/cold_as_hell/post/3c78cfae-f9ac-4979-a338-b0321651d414/image.png" alt=""></p>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(board, h, w):
    n = len(board) # 1. board의 길이를 저장 ## 보드판 범위 밖을 탐색하는 걸 막기위해, 이차원배열을 n에 넣을 경우 h_check w_check와 값 비교 불가능 하므로 len() 사용 이후 보드 크기를 n에 저장
    count = 0  # 2. 같은 색으로 색칠된 칸의 개수 
    answer = count
    dh, dw =[0, 1, -1, 0], [1, 0, 0, -1] # 3. h와 w의 변화량 // 가로세로
    for i in range(0, 4): # 4. 반복문  ## 상하좌우를 확인하며 같은색으로 칠해진 칸 찾기
        h_check, w_check = h + dh[i], w + dw[i] # 4-1 체크할 칸의 h, w 좌표를 나타내는 변수

        if n &gt; h_check &gt;= 0 and n &gt; w_check &gt;= 0: # 4-2 지금 체크한 h w 범위가 0이상 n 미만일때만
            if board[h][w] == board[h_check][w_check]: #4-2-a 선택한 칸의 색과 동일한지 확인하고, 동일하다면
                answer += 1 # answer 에 + 1
    return answer # 5. count값 return`</code></pre>
<hr>
<h1 id="느낀점">느낀점</h1>
<p>평소 의사코드가 주어진 문제를 접할 기회가 많지 않았기에 
의사코드를 코드로 옮기며 문제를 이해하는 과정이 새롭게 느껴졌다. </p>
<p>1년전 쯤, 
핀토스 자료의 안내를 읽으며 코드를 따라치던 때보다 지금 이 의사코드를 읽고 코드를 작성하기가 좀 더 수월하다는 생각 마저 든다.</p>
<p>물론 문제의 난이도 차이도 있겠지만 의사코드를 이해하는 능력이 0.01 정도 좋아졌을지도? 
정글 1000트 정도 더 돌리면 내가 핀토스 씹어 먹을지도?</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/40945092-cc47-4091-8991-6d19747f4bb4/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[복습] 큰 수 만들기]]></title>
            <link>https://velog.io/@cold_as_hell/%EB%B3%B5%EC%8A%B5-%ED%81%B0-%EC%88%98-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@cold_as_hell/%EB%B3%B5%EC%8A%B5-%ED%81%B0-%EC%88%98-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Tue, 02 Jun 2026 10:05:23 GMT</pubDate>
            <description><![CDATA[<p>저번주 복습에서는 더 효율적인 코드와 새로운 풀이 방법을 고민하는 데 중점을 두었다면, 이번 주 복습에서는 이전에 학습했던 개념을 다시 익히고 정리하는 데 오롯이 집중했다.</p>
<hr>
<h1 id="문제">문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/42883">링크</a></p>
<p>숫자 문자열에서 정확히 k개의 숫자를 제거하여 만들 수 있는 가장 큰 수를 구하는 문제이다.</p>
<hr>
<h1 id="풀이">풀이</h1>
<p>숫자를 순회하며 스택에 숫자를 추가하고, 현재 숫자보다 작은 이전 숫자는 제거하면서 가장 큰 수를 만들어 나가자.</p>
<p>가장 큰 수를 만들기 위해서는 앞자리의 값이 최대가 되어야 하므로, 현재 자리에서 더 큰 숫자가 등장했을 때 이전의 작은 숫자를 제거하는 그리디 알고리즘을 사용하자.</p>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(number, k):
    stack = []

    # 1. 숫자 문자열을 왼쪽부터 한 글자씩 순회
    for num in number:

        # 2. 현재 숫자가 제거할 수 있는 횟수(k)보다 크고, 스택이 비어있지 않고, 스택 맨 위 숫자보다 크면
        while k &gt; 0 and stack and stack[-1] &lt; num:
            stack.pop() # 스택 맨 위의 작은 숫자를 제거 (더 큰 숫자를 앞자리에 두어야 큰 수가 됨)
            k -= 1       # 제거 횟수 차감

        # 3. 현재 숫자를 스택에 추가
        stack.append(num)

    # 4. 순회가 끝난 후에도 제거 횟수가 남아있다면
    #    스택 뒤에서부터 남은 횟수 k만큼 제거
    #    ex) k=2, stack=[4321] → [43] (맨 뒤의 2개 제거)
    if k &gt; 0:
        stack = stack[:-k]

    # 5. 스택의 숫자들을 이어 붙여 문자열로 반환
    return &#39;&#39;.join(stack)</code></pre>
<hr>
<h1 id="생각해본것">생각해본것</h1>
<p>전체 숫자에서 K개가 제거되었을때 모든경우의 조합을 구하고 최댓값을 찾으면?</p>
<pre><code class="language-python">from itertools import combinations

def solution(number, k):
    n = len(number)
    answer = &quot;&quot;

    # 1. k개의 숫자를 제거하는 모든 경우의 수를 생성한다.
    for removed in combinations(range(n), k):
        removed = set(removed)

        candidate = &quot;&quot;

        # 2. 제거되지 않은 숫자들로 새로운 숫자를 만든다.
        for i in range(n):
            if i not in removed:
                candidate += number[i]

        # 3. 생성된 숫자 중 가장 큰 값을 정답으로 갱신한다.
        answer = max(answer, candidate)

    return answer
</code></pre>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/436270c4-f939-45e1-92c0-aead9823dbc3/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/dd0bafd9-208f-4c5f-98ed-310d1b0a1226/image.jpg" alt=""></p>
<p>생성해야할 조합이 많으면 시간이 비효율적으로 늘어나 테스트케이스를 통과하지 못했다.</p>
<hr>
<h1 id="느낀점">느낀점</h1>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/f54900e5-bb42-4a1a-b5ba-0b207b3ed0b1/image.jpg" alt=""></p>
<p>최상단에 위치한 코드로는 새로운 테스트 케이스를 통과했다.
같은 동작을 수행하더라도, 비효율적인 방식은 테스트케이스를 통과할 수 없던 것이다.</p>
<p>개인적으로 그리디 알고리즘과 최적해라고 하면 정답에 도달하는 규칙이나 공식을 먼저 떠올리는 경향이 있었다만,
이번 문제를 다시 살펴보며 단순히 정답을 맞히는 것보다 어떻게 하면 불필요한 탐색을 줄이고 더 효율적으로 답에 도달할 수 있는지를 고민하는 과정 역시 최적해를 구하는 중요한 방법이라는 점을 다시금 떠올릴 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[병목구간 식별 전략 - 2]]></title>
            <link>https://velog.io/@cold_as_hell/%EB%B3%91%EB%AA%A9%EA%B5%AC%EA%B0%84-%EC%8B%9D%EB%B3%84-%EC%A0%84%EB%9E%B5-2</link>
            <guid>https://velog.io/@cold_as_hell/%EB%B3%91%EB%AA%A9%EA%B5%AC%EA%B0%84-%EC%8B%9D%EB%B3%84-%EC%A0%84%EB%9E%B5-2</guid>
            <pubDate>Mon, 01 Jun 2026 19:41:45 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@cold_as_hell/%EB%B3%91%EB%AA%A9%EA%B5%AC%EA%B0%84-%EC%8B%9D%EB%B3%84-%EC%A0%84%EB%9E%B5-1">이전</a>에 언급했듯, 서버 명목과 프론트엔드 병목은 발생한 위치를 기준으로 구분한다.</p>
<pre><code>- 서버 또는 서버 구성요소(DB, Redis 등)에서 발생 → 서버 병목 

- 사용자의 브라우저(JavaScript 실행, 렌더링 등)에서 발생 → 프론트엔드 병목

※ 잘못된 코드로 인해 다수의 사용자가 동시에 느려지더라도
   문제가 발생하는 위치는 각 사용자의 브라우저이므로 프론트엔드 병목에 해당한다.</code></pre><p>그렇다면 병목이 발생한 위치는 어떻게 판단할 수 있을까?</p>
<p>물론 직접 서비스를 사용하며 증상을 체감하거나 사용자 제보를 확인하는 방법도 있지만, 보다 객관적이고 정확한 분석을 위해서는 성능 분석 도구를 활용하는 것이 효과적이다.</p>
<p>이번 글에서는 서버 병목과 프론트엔드 병목을 구분하기 위해 활용할 수 있는 주요 성능 분석 도구와 핵심 지표들에 대해 살펴보겠다.</p>
<hr>
<h1 id="성능-분석-도구">성능 분석 도구</h1>
<p>로딩 속도와 개선이 필요한 영역을 파악해 앱 또는 웹 서비스의 성능을 측정하고 개선하는 데 도움을 주는 도구.</p>
<p>프론트엔드와 서버중 어디서 병목이 일어났는지 파악하려면, 성능 분석 도구를 통해 병목 위치를 먼저 파악하고,
그에 맞는 해결책을 적용하는 것이 핵심이다.</p>
<h2 id="서버-성능-분석-도구">서버 성능 분석 도구</h2>
<p>대표적인 도구로는 AWS CloudWatch, Datadog 등이 있으며, 주로 아래 지표를 측정한다.</p>
<ul>
<li>API 응답 시간</li>
<li>CPU / 메모리 사용률</li>
<li>DB 슬로우 쿼리</li>
</ul>
<h2 id="프론트엔드-성능-분석-도구">프론트엔드 성능 분석 도구</h2>
<p>대표적인 도구로는 Chrome DevTools, Lighthouse 등이 있으며, 주로 아래 지표를 측정한다.</p>
<ul>
<li>FCP (첫 콘텐츠 표시 시간)</li>
<li>TTI (인터랙션 가능 시간)</li>
<li>Long Task (50ms 이상 JS 실행)</li>
<li>번들 크기</li>
</ul>
<hr>
<h1 id="시스터디-결론">시스터디 결론</h1>
<p><del>악성 우결충 윤아는 스터디원에게 실시간으로 여자를 소개해주는 WebSocket 기반 웹게임을 만들었다.
그런데 예상치 못한 스터디원의 인기로 동시 접속자가 급증하자 메시지 딜레이와 화면 끊김 현상이 발생했다.
이 문제가 서버 병목인지, 프론트엔드 렌더링 병목인지 어떻게 구분하고 분석할 것인가?</del></p>
<blockquote>
<p> <strong>Q. 서버 병목과 프론트엔드 랜더링 병목은 어떻게 구분하고 분석되는가?</strong></p>
</blockquote>
<p>A.  서버 병목과 프론트엔드 병목은 병목이 발생한 위치를 기준으로 구분합니다. 서버 또는 서버 구성요소(DB, Redis 등)에서 발생한 문제는 서버 병목이며, 브라우저의 JavaScript 실행이나 렌더링 과정에서 발생한 문제는 프론트엔드 병목입니다.</p>
<p>이를 토대로 우선 유저 경험과 내부 테스트를 통해 문제 상황을 파악하겠습니다. Chrome DevTools의 Network 및 Performance 패널을 활용하여 WebSocket 메시지의 수신 시점과 화면 렌더링 시점을 비교하고, FPS 저하나 과도한 렌더링 여부를 확인하여 어느 구간에서 문제가 발생하는지 먼저 좁혀 나가겠습니다.</p>
<p>이후 성능 분석 도구를 활용해 가설을 검증하겠습니다. 서버 측에서는 CloudWatch나 Datadog을 활용하여 API 응답 시간, CPU·메모리 사용률, DB 슬로우 쿼리, Redis 응답 시간 등을 확인하고, 서버 또는 서버 구성요소의 성능 저하 여부를 분석하겠습니다.</p>
<p>프론트엔드 측에서는 Chrome DevTools와 Lighthouse를 활용하여 FCP, TTI, Long Task, 번들 크기 등의 지표를 분석하고 렌더링 및 실행 성능 문제를 확인하겠습니다.</p>
<p>마지막으로 수집한 지표를 바탕으로 관련 로직과 아키텍처를 추적하여 병목 지점을 좁혀 나가고, 근본 원인을 파악하여 개선 방안을 도출하겠습니다.</p>
<p>이상입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[병목구간 식별 전략 - 1]]></title>
            <link>https://velog.io/@cold_as_hell/%EB%B3%91%EB%AA%A9%EA%B5%AC%EA%B0%84-%EC%8B%9D%EB%B3%84-%EC%A0%84%EB%9E%B5-1</link>
            <guid>https://velog.io/@cold_as_hell/%EB%B3%91%EB%AA%A9%EA%B5%AC%EA%B0%84-%EC%8B%9D%EB%B3%84-%EC%A0%84%EB%9E%B5-1</guid>
            <pubDate>Thu, 28 May 2026 09:53:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>악성 우결충 윤아는 스터디원에게 실시간으로 여자를 소개해주는 WebSocket 기반 웹게임을 만들었다. </p>
</blockquote>
<p> 그런데 예상치 못한 스터디원의 인기로 동시 접속자가 급증하자 메시지 딜레이와 화면 끊김 현상이 발생했다. </p>
<blockquote>
</blockquote>
<p> 이 문제가 서버 병목인지, 프론트엔드 렌더링 병목인지 어떻게 구분하고 분석할 것인가?</p>
<hr>
<p>질문답변에 대한 고민 이전에, 용어 개념들을 간략히 정리해보자.</p>
<h1 id="웹게임">웹게임</h1>
<p>별도의 클라이언트 설치 없이 웹에서 즐길 수 있는 게임.
브라우저에서 게임 요소를 Dom 기반으로 랜더링하고 있다면,
웹페이지의 버튼 요소 등과 같은 방식으로 개발자 도구에서 조작도 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/c057cd63-8a6b-4a03-b6c2-28ab469f0ec7/image.png" alt="">
<img src="https://velog.velcdn.com/images/cold_as_hell/post/4c0814b6-a441-495b-b4e2-28703e1001de/image.png" alt=""></p>
<pre><code>검은 룩 체스말에 display:none 설정을 부여한 모습. 
검은 룩이 사라진 것으로 element.style이 잘 적용되고 있음을 확인 가능하다.
</code></pre><hr>
<h1 id="웹소켓">웹소켓</h1>
<p>웹소켓(WebSocket)은 웹에서 클라이언트와 서버 간의 지속적인 양방향 연결을 유지하여 실시간으로 데이터를 주고받을 수 있게 해주는 통신 프로토콜(통신 규약)이다.</p>
<p>일반적인 HTTP 통신은 클라이언트가 요청을 보내야 서버가 응답할 수 있는 요청-응답 방식인 반면, 웹소켓은 한 번 연결이 수립되면 클라이언트와 서버 어느 쪽이든 필요할 때 데이터를 전송할 수 있다.</p>
<p>또한 OSI 7계층 기준으로 일반적인 소켓 통신은 TCP·UDP와 같은 전송 계층(4계층) 프로토콜을 기반으로 동작한다. 반면 웹소켓은 연결 수립 과정에서 HTTP를 사용하며, 웹 애플리케이션에서 활용되는 응용 계층(7계층) 프로토콜로 분류된다.</p>
<table>
<thead>
<tr>
<th></th>
<th>소켓</th>
<th>웹소켓</th>
</tr>
</thead>
<tbody><tr>
<td>통신 방식</td>
<td>단방향 (요청-응답)</td>
<td>양방향</td>
</tr>
<tr>
<td>기반 프로토콜</td>
<td>TCP / UDP</td>
<td>TCP + HTTP</td>
</tr>
<tr>
<td>OSI 계층</td>
<td>4계층 (전송 계층)</td>
<td>7계층 (응용 계층)</td>
</tr>
<tr>
<td>연결 방식</td>
<td>요청마다 연결</td>
<td>한 번 연결 후 유지</td>
</tr>
<tr>
<td>주요 사용처</td>
<td>네트워크 통신 전반</td>
<td>실시간 웹 서비스</td>
</tr>
</tbody></table>
<h2 id="사용예시">사용예시</h2>
<ul>
<li>실시간 체팅 서비스</li>
<li>공동편집 도구? (ex. Figma, Google Docs, Google Sheets, Notion 등)</li>
<li>실시간 멀티게임
등</li>
</ul>
<hr>
<h1 id="병목-현상">병목 현상</h1>
<p>전체 시스템에서 가장 느린 구간이 전체 성능을 제한하는 현상.</p>
<pre><code>예시)

1(1초) → 2(1초) → 3(1초) = 총 3초

# 2번에서 병목 발생시
1(1초) → 2(1.5초) → 3(1초) = 총 3.5초
                ↑
           여기서 막혀서 전체가 느려짐</code></pre><p>SSR(서버 사이드 렌더링)은 서버에서 렌더링 작업이 끝나기 전까지는 전까지 사용자에게 그 어떤 정보도 제공할수 없다는 특성상 병목 현상에 취약하다. 애플리케이션 규모가 커지고 요청이 복잡하게 얽힐수록 병목이 심해져, 오히려 사용자 경험을 해치는 결과로 이어질 수 있다.</p>
<hr>
<h2 id="서버-병목">서버 병목</h2>
<p>서버 또는 서버 구성요소(DB, Redis, 메시지 큐 등)에서 처리 지연이 발생하여 전체 서비스의 성능이 저하되는 현상을 의미한다.</p>
<p>서버는 여러 사용자가 공유하는 자원이므로, 서버 병목이 발생하면 일반적으로 다수의 사용자에게 영향을 미친다.</p>
<p><strong>예시 1. DB 병목</strong></p>
<ul>
<li>특정 API가 복잡한 SQL을 수행</li>
<li>DB 응답 시간이 100ms → 5초로 증가</li>
<li>모든 사용자의 응답 속도가 함께 느려짐</li>
</ul>
<p><strong>예시 2. 게임 서버 CPU 포화</strong></p>
<ul>
<li>동시 접속자가 급증</li>
<li>게임 상태 계산 및 메시지 브로드캐스트 증가</li>
<li>모든 플레이어의 위치 동기화와 채팅 전송이 지연됨</li>
</ul>
<p><strong>예시 3. Redis 병목</strong></p>
<ul>
<li>세션 및 캐시 조회를 Redis에 의존</li>
<li>Redis 응답이 느려짐</li>
<li>모든 서버 인스턴스가 Redis 응답을 기다리면서 전체 서비스가 느려짐</li>
</ul>
<hr>
<h2 id="프론트엔드-병목">프론트엔드 병목</h2>
<p>프론트엔드 병목은 사용자의 브라우저에서 JavaScript 실행, 렌더링, 메모리 사용 등의 문제로 인해 성능 저하가 발생하는 현상을 의미한다.</p>
<p>프론트엔드는 각 사용자의 브라우저에서 개별적으로 실행되므로 특정 사용자에게만 영향을 줄 수 있으며, 잘못된 코드가 배포된 경우에는 모든 사용자가 동시에 영향을 받을 수도 있다.</p>
<p><strong>예시 1. JavaScript 번들 크기 증가</strong></p>
<ul>
<li>JS 파일 크기가 과도하게 커짐</li>
<li>브라우저가 다운로드 및 파싱하는 데 오래 걸림</li>
<li>해당 사용자의 초기 로딩 속도만 느려짐</li>
</ul>
<p><strong>예시 2. 과도한 렌더링(Re-render)</strong></p>
<ul>
<li>WebSocket 메시지를 받을 때마다 전체 화면을 다시 그림</li>
<li>CPU 사용량이 증가하고 화면이 끊김</li>
<li>해당 사용자의 브라우저에서만 발생</li>
</ul>
<p><strong>예시 3. 메모리 누수</strong></p>
<ul>
<li>사용하지 않는 객체가 계속 메모리에 남아 있음</li>
<li>장시간 사용 시 브라우저가 느려지거나 멈춤</li>
<li>문제가 발생한 사용자에게만 영향을 줌</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[복습] 다리를 지나는 트럭]]></title>
            <link>https://velog.io/@cold_as_hell/%EB%B3%B5%EC%8A%B5-%EB%8B%A4%EB%A6%AC%EB%A5%BC-%EC%A7%80%EB%82%98%EB%8A%94-%ED%8A%B8%EB%9F%AD</link>
            <guid>https://velog.io/@cold_as_hell/%EB%B3%B5%EC%8A%B5-%EB%8B%A4%EB%A6%AC%EB%A5%BC-%EC%A7%80%EB%82%98%EB%8A%94-%ED%8A%B8%EB%9F%AD</guid>
            <pubDate>Mon, 25 May 2026 20:28:43 GMT</pubDate>
            <description><![CDATA[<p>복습은 학습한 내용을 단기 기억에서 장기 기억으로 전환하여 기억력을 극대화하고, 지식의 활용도를 높여준다.
시스터디 또한 이러한 학습 효과를 경험하기 위해 1주일 동안 복습하는 시간도 가져보기로 했다.</p>
<hr>
<h1 id="문제">문제</h1>
<p>오늘 복습해 볼 문제는 2월에 과제로 받았던 다리를 지나는 트럭이다.
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/42583">링크</a></p>
<h2 id="문제-요약">문제 요약</h2>
<blockquote>
<p>다리의 길이와 무게를 기준으로 두고 모든 트럭이 다리를 건너려면 최소 몇 초가 필요한지 구한다.</p>
</blockquote>
<pre><code class="language-python">##  예시 ## 

# bridge_length=2, weight=10, truck_weights=[7,4,5,6]
# 초기큐 : queue=[0,0], truck_weights =[7,4,5,6], total=0
# 1초: queue=[0,7], truck_weights[4,5,6], total=7
# 2초: queue=[7,0], truck_weights=[4,5,6], total=7 (4는 무게초과로 대기)
# 3초: queue=[0,4], truck_weights=[5,6], total=4```</code></pre>
<hr>
<h1 id="이전-풀이">이전 풀이</h1>
<pre><code class="language-python">from collections import deque
def solution(bridge_length, weight, truck_weights):
    # 다리를 나타내는 큐 (다리 길이만큼 0으로 초기화 - 0은 빈 공간)
    queue = deque([0] * bridge_length)

    # 대기 중인 트럭들을 큐로 관리
    orders = deque(truck_weights)

    # 경과 시간
    time = 0

    # 현재 다리 위에 있는 트럭들의 총 무게
    total = 0

    # 1. 대기 중인 트럭이 없어질 때까지 1초씩 시뮬레이션
    while orders:
        time += 1

        # 2. 1초가 지났으므로 다리 위 모든 트럭이 한 칸 앞으로 이동
        #    맨 앞 칸(queue[0])이 다리를 완전히 벗어나므로 총 무게에서 제거
        total -= queue[0]
        queue.popleft()

        # 3. 대기 중인 첫 번째 트럭이 다리에 올라갈 수 있는지 확인
        #    현재 다리 위 총 무게 + 다음 트럭 무게가 제한 무게를 초과하면
        #    트럭을 올리지 않고 빈 공간(0)으로 채워 다리 길이를 유지
        if total + orders[0] &gt; weight:
            queue.append(0)
        else:
            # 4. 무게 제한 이하이면 대기 트럭을 다리에 올림
            #    대기 큐에서 트럭을 꺼내 다리 큐에 추가하고 총 무게 갱신
            w = orders.popleft()
            total += w
            queue.append(w)

    # 마지막 트럭이 다리를 완전히 건너는 시간 추가
    return time + bridge_length</code></pre>
<hr>
<h1 id="새-풀이">새 풀이</h1>
<blockquote>
<p>큐에 0을 넣지 않아도 풀 수 있다.</p>
</blockquote>
<p>이전 코드는 트럭이 다리에 올라가지 못할 때 deque에 0을 넣고, 올라갈 수 있을 때까지 반복했다. </p>
<p>그러나 트럭이 다리에 올라갈 때마다 무게, 나가는 시간을 저장한다면 불필요한 반복 연산시간을 줄일 수 있을 것이다.</p>
<p>예상되는 효과는 아래와 같다.</p>
<p>(1) 대기 시간을 매 루프마다 소비하지 않고 앞 트럭이 나가는 시간으로 바로 점프해 불필요한 반복을 제거할 수 있다</p>
<p>(2) 나가는 시간이 미리 저장되어 있어 마지막에 time + bridge_length 를 별도로 계산하지 않고 바로 반환할 수 있다.</p>
<pre><code class="language-python">from collections import deque

def solution(bridge_length, weight, truck_weights):
    # 빈 큐로 시작 - 0으로 채울 필요 없이 (무게, 나가는 시간)으로 관리
    bridge_queue = deque()

    # 대기 중인 트럭들 관리
    waiting_trucks = deque(truck_weights)

    # 현재 시간
    current_time = 0

    # 현재 다리 위 총 무게
    current_weight = 0

    while waiting_trucks or bridge_queue:

        # 1. 나가는 시간이 된 트럭만 골라서 제거
        while bridge_queue and bridge_queue[0][1] &lt;= current_time:
            current_weight -= bridge_queue.popleft()[0]

        # 2. 다음 트럭을 올려도 무게 제한을 넘지 않는 경우
        if waiting_trucks and current_weight + waiting_trucks[0] &lt;= weight:

            # 대기 트럭 꺼내기
            truck_weight = waiting_trucks.popleft()

            # 현재 다리 무게 증가
            current_weight += truck_weight

            # 트럭이 다리에 올라가는 시간

            # 3. (무게, 나가는 시간)을 함께 저장
            # 나가는 시간 = 현재 시간 + 다리 길이
            bridge_queue.append(
                (truck_weight, current_time + bridge_length)
            )

        else:
            # 4. 다음 트럭을 못 올리는 경우

            # 1초씩 자연 진행
            current_time += 1

            continue

        # 트럭 올린 경우에도 1초 진행
        current_time += 1

    # 마지막으로 저장했던 트럭이 다리를 완전히 건너는 시간 출력
    return current_time</code></pre>
<hr>
<h1 id="주요-자료구조">주요 자료구조</h1>
<blockquote>
<p><strong>deque</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/cold_as_hell/post/8349d72f-5e28-4329-ae2b-6159320dac30/image.png" alt=""></p>
<pre><code class="language-python"># &lt;파이썬 알고리즘 인터뷰&gt; p.266, 책만, 2020</code></pre>
<p>일반적인 큐는 뒤에서만 삽입이 이루어지고 앞에서만 인출이 가능한 반면, 데크(Deque)는 양쪽에서 모두 삽입과 삭제가 가능해 스택과 큐의 특징을 모두 갖고 있다. </p>
<p>물론 이 문제에서 양쪽 삽입과 삭제를 활용하지는 않았지만, 일반적인 리스트의 삭제 연산인 pop(0)보다 popleft()가 빠르기에 성능 최적화에 도움이 되었다.</p>
<hr>
<h2 id="pop0-vs-popleft">pop(0) vs popleft()</h2>
<pre><code class="language-python"># list.pop(0) → O(n)
lst = [1, 2, 3, 4, 5]
lst.pop(0)
# [1, 2, 3, 4, 5]
#  ↑ 제거
# [2, 3, 4, 5]
#  ↑ ↑ ↑ ↑ 나머지 요소를 전부 한 칸씩 앞으로 당김 → 요소 수만큼 연산 발생

# deque.popleft() → O(1)
from collections import deque
dq = deque([1, 2, 3, 4, 5])
dq.popleft()
# [1, 2, 3, 4, 5]
#  ↑ 제거
#     ↑ 포인터만 다음 요소로 이동 → 요소 수와 무관하게 연산 1번</code></pre>
<hr>
]]></description>
        </item>
    </channel>
</rss>