<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dus_snp.log</title>
        <link>https://velog.io/</link>
        <description>Live a life you will remember</description>
        <lastBuildDate>Thu, 20 Aug 2026 07:37:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dus_snp.log</title>
            <url>https://velog.velcdn.com/images/dus_snp/profile/2bb9b73a-1702-4445-b37b-ddb056017ca2/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dus_snp.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dus_snp" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 13장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-13%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-13%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 20 Aug 2026 07:37:22 GMT</pubDate>
            <description><![CDATA[<h1 id="검색어-자동완성-시스템">검색어 자동완성 시스템</h1>
<hr>
<h1 id="1-요구사항-확인">1. 요구사항 확인</h1>
<p>설계에 앞서 다음 사항을 먼저 확인해야 함.</p>
<ul>
<li>사용자 입력이 검색어의 첫 부분에 한정되는가, 중간 부분도 대응하는가</li>
<li>몇 개의 자동완성 검색어를 보여줄 것인가</li>
<li>자동완성 검색어를 고르는 기준은 무엇인가</li>
<li>맞춤법 검사와 자동 수정 기능을 제공하는가</li>
<li>어떤 언어를 지원하는가</li>
<li>대문자나 특수문자는 어떻게 처리하는가</li>
<li>얼마나 많은 사용자를 지원해야 하는가(DAU)</li>
</ul>
<h2 id="비기능-요구사항">비기능 요구사항</h2>
<ul>
<li><p><strong>빠른 응답 속도</strong></p>
<ul>
<li>페이스북의 검색어 자동완성 시스템 문서에 따르면 응답 속도가 100밀리초 이상이면 사용자가 불편을 느낌</li>
</ul>
</li>
<li><p><strong>연관성</strong></p>
<ul>
<li>출력되는 검색어는 사용자가 입력한 단어와 연관되어야 함</li>
</ul>
</li>
<li><p><strong>정렬</strong></p>
<ul>
<li>계산 결과는 인기도 등의 순위 모델에 따라 정렬되어 있어야 함</li>
</ul>
</li>
<li><p><strong>규모 확장성</strong></p>
<ul>
<li>많은 트래픽을 감당할 수 있도록 확장 가능해야 함</li>
</ul>
</li>
<li><p><strong>고가용성</strong></p>
<ul>
<li>시스템 일부에 장애가 발생하거나 느려지거나 예상치 못한 네트워크 문제가 생겨도 계속 사용 가능해야 함</li>
</ul>
</li>
</ul>
<hr>
<h1 id="2-개략적-규모-추정">2. 개략적 규모 추정</h1>
<p>현재 데이터가 없으므로 몇 가지 가정을 세우고 시작함.</p>
<ul>
<li><p>일간 능동 사용자(DAU): 1000만 명</p>
</li>
<li><p>한 사용자가 하루에 검색하는 횟수: 평균 10회</p>
</li>
<li><p>질의당 평균 입력 데이터 크기: 20바이트</p>
<ul>
<li>ASCII 문자 하나는 1바이트</li>
<li>질의문은 평균 4개 단어, 단어당 평균 5글자로 가정</li>
</ul>
</li>
<li><p>신규 검색어 비율: 전체의 20%</p>
</li>
</ul>
<h2 id="백엔드-요청-수">백엔드 요청 수</h2>
<p>검색어를 입력할 때 글자 하나마다 요청이 발생함.</p>
<pre><code class="language-text">&quot;dinner&quot; 입력
→ search?q=d
→ search?q=di
→ search?q=din
→ search?q=dinn
→ search?q=dinne
→ search?q=dinner</code></pre>
<p>따라서 검색 1회당 평균 20건의 요청이 백엔드로 전달됨.</p>
<h2 id="qps">QPS</h2>
<pre><code class="language-text">QPS
= 1000만 × 10회 × 20건 ÷ 86,400초
≈ 24,000

최대 QPS
= 24,000 × 2
= 48,000</code></pre>
<h2 id="신규-데이터-크기">신규 데이터 크기</h2>
<pre><code class="language-text">하루 질의 데이터
= 1000만 × 10회 × 20바이트
= 2GB

신규 데이터
= 2GB × 20%
= 0.4GB / 일</code></pre>
<hr>
<h1 id="3-개략적인-설계안">3. 개략적인 설계안</h1>
<p>시스템은 크게 두 부분으로 나눌 수 있음.</p>
<ul>
<li><p><strong>데이터 수집 서비스</strong></p>
<ul>
<li>사용자가 입력한 질의를 실시간으로 수집하는 시스템</li>
<li>데이터가 많은 애플리케이션에서 실시간 수집은 바람직하지 않지만, 설계의 출발점으로 일단 둠</li>
</ul>
</li>
<li><p><strong>질의 서비스</strong></p>
<ul>
<li>주어진 질의에 대해 인기 검색어 k개를 정렬해 내놓는 서비스</li>
</ul>
</li>
</ul>
<h2 id="빈도-테이블">빈도 테이블</h2>
<p>질의문과 사용 빈도를 저장하는 빈도 테이블이 있다고 가정함.</p>
<p>데이터 수집 서비스는 사용자가 입력한 질의문을 저장하면서 사용 빈도를 늘림.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/08301a16-9417-4e98-ae46-11b1975812aa/image.png" alt=""></p>
<p>질의 서비스는 빈도가 높은 순으로 상위 k개의 검색어를 SQL 질의문으로 계산하여 보여줌.</p>
<p>사용하는 질의문은 다음과 같음.</p>
<pre><code>SELECT * FROM frequency_table
WHERE query LIKE &#39;prefix%&#39;
ORDER BY frequency DESC
LIMIT k</code></pre><p>데이터가 적을 때는 괜찮은 설계안이지만, 데이터가 많아지면 DB가 병목이 됨.</p>
<p>따라서 상세 설계안으로 넘어감.</p>
<hr>
<h1 id="4-트라이-자료구조">4. 트라이 자료구조</h1>
<p>관계형 DB로 가장 인기 있는 k개의 질의문을 골라내는 방식은 효율적이지 않음.</p>
<p>따라서 트라이 자료구조를 사용하여 응답 시간을 줄임.</p>
<p>트라이(trie)라는 이름은 retrieval의 가운데 부분에서 따온 것임.</p>
<h2 id="구조">구조</h2>
<ul>
<li>트리 형태의 자료구조임</li>
<li>루트 노드는 빈 문자열을 나타냄</li>
<li>각 노드는 글자 하나를 저장함</li>
<li>각 노드는 하나의 단어 또는 접두어 문자열을 나타냄</li>
</ul>
<p>자식 노드의 개수는 지원하는 문자 집합에 따라 달라짐.</p>
<p>영문 소문자만 지원한다면 노드마다 최대 26개의 자식을 가질 수 있고, 한글이나 유니코드 전반을 지원한다면 훨씬 커짐.</p>
<p>따라서 요구사항에서 지원 언어를 먼저 확인해야 함.</p>
<p>빈도 테이블의 정보를 트라이 노드에 저장하면 다음과 같음.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/939a3dfa-00c0-4927-a427-a28c2e447401/image.png" alt=""></p>
<hr>
<h1 id="5-탐색-알고리즘과-시간-복잡도">5. 탐색 알고리즘과 시간 복잡도</h1>
<p>기호는 다음과 같이 정의함.</p>
<ul>
<li><code>p</code>: 접두어의 길이</li>
<li><code>n</code>: 트라이 안에 있는 노드 개수</li>
<li><code>c</code>: 주어진 노드의 자식 노드 개수</li>
</ul>
<h2 id="단계별-복잡도">단계별 복잡도</h2>
<ol>
<li><p>해당 접두어를 표현하는 노드를 찾음 → <code>O(p)</code></p>
</li>
<li><p>그 노드부터 하위 트리를 탐색하여 모든 유효 노드를 찾음 → <code>O(c)</code></p>
<ul>
<li>유효 노드는 유효한 검색 문자열을 구성하는 노드를 말함</li>
</ul>
</li>
<li><p>유효 노드를 정렬하여 가장 인기 있는 검색어 k개를 찾음 → <code>O(c log c)</code></p>
</li>
</ol>
<p>전체 시간 복잡도는 다음과 같음.</p>
<pre><code class="language-text">O(p) + O(c) + O(c log c)
= O(p + c log c)</code></pre>
<h2 id="최적화">최적화</h2>
<p>최악의 경우 전체 트라이를 모두 검색해야 하는 상황이 생길 수 있음.</p>
<p>이를 해결하는 방법은 두 가지임.</p>
<ul>
<li><p><strong>접두어의 최대 길이 제한</strong></p>
<ul>
<li>사용자가 검색창에 긴 질의를 입력하는 일은 드묾</li>
<li><code>p</code>를 상수로 만들 수 있으므로 1단계가 <code>O(1)</code>이 됨</li>
</ul>
</li>
<li><p><strong>각 노드에 인기 검색어 캐시</strong></p>
<ul>
<li>노드마다 상위 k개를 미리 저장해 둠</li>
<li>2단계와 3단계를 건너뛸 수 있음</li>
</ul>
</li>
</ul>
<p>저장 공간이 많이 필요해지지만, 빠른 응답 속도가 더 중요하므로 이 기법들을 사용함.</p>
<p>두 기법을 함께 적용하면 접두어를 찾아 인기 검색어를 얻는 과정의 전체 시간 복잡도가 <code>O(1)</code>이 됨.</p>
<pre><code class="language-text">접두어 길이 제한 → O(p)를 상수로
노드별 top k 캐시 → 탐색과 정렬 제거
= O(1)</code></pre>
<p>여기서 <code>O(1)</code>은 접두어 길이가 상수로 제한된다는 전제 위에서 성립함.</p>
<hr>
<h1 id="6-데이터-수집-서비스-상세-설계">6. 데이터 수집 서비스 상세 설계</h1>
<p>앞선 설계안에서는 실시간으로 데이터를 갱신했으나, 매일 수천만 건의 질의가 발생하는 대형 서비스에서는 적합하지 않음.</p>
<p>트라이가 한 번 만들어지면 인기 검색어는 자주 바뀌지 않으므로 자주 갱신할 필요가 없음.</p>
<p>트라이를 만드는 데 쓰이는 데이터는 보통 데이터 분석 서비스나 로깅 서비스로부터 옴.
<img src="https://velog.velcdn.com/images/dus_snp/post/d7f904ae-dabf-4f71-bd8d-2ad318fd0833/image.png" alt=""></p>
<h2 id="각-컴포넌트의-역할">각 컴포넌트의 역할</h2>
<ul>
<li><p><strong>데이터 분석 로그</strong></p>
<ul>
<li>입력된 질의에 관한 원본 데이터를 보관</li>
</ul>
</li>
<li><p><strong>로그 취합 서버</strong></p>
<ul>
<li>로그를 취합함</li>
<li>취합의 실시간성은 서비스 성격에 따라 중요도가 다르므로 확인이 필요함</li>
</ul>
</li>
<li><p><strong>작업 서버</strong></p>
<ul>
<li>주기적으로 비동기 작업을 실행하는 서버 집합</li>
<li>트라이 자료구조를 만들고 트라이 DB에 저장하는 역할</li>
</ul>
</li>
<li><p><strong>트라이 캐시</strong></p>
<ul>
<li>트라이 DB의 스냅샷을 주기적으로 떠서 연산 성능을 높임</li>
</ul>
</li>
<li><p><strong>트라이 DB</strong></p>
<ul>
<li><p>영속성 저장장치로는 두 가지 선택지가 있음.</p>
<ul>
<li><p><strong>문서 저장소</strong></p>
<ul>
<li>주기적으로 트라이를 직렬화하여 저장</li>
</ul>
</li>
<li><p><strong>키-값 저장소</strong></p>
<ul>
<li>트라이에 보관된 모든 접두어를 해시 테이블의 키로 변환</li>
<li>각 노드에 보관된 데이터를 해시 테이블의 값으로 변환</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-text">접두어 &quot;din&quot; → 키
[&quot;dinner&quot;, &quot;dinosaur&quot;, ...] → 값</code></pre>
<hr>
<h1 id="7-질의-서비스-상세-설계">7. 질의 서비스 상세 설계</h1>
<p>질의 서비스는 다음 과정으로 이루어짐.</p>
<ol>
<li>검색 질의가 로드밸런서로 전송됨</li>
<li>로드밸런서가 API 서버 중 하나로 질의를 보냄</li>
<li>API 서버가 트라이 캐시에서 데이터를 가져옴</li>
<li>캐시 미스가 나면 DB에서 가져와 캐시를 채운 뒤 응답을 구성함</li>
</ol>
<h2 id="최적화-기법">최적화 기법</h2>
<ul>
<li><p><strong>AJAX 요청</strong></p>
<ul>
<li>페이지 전체를 새로 고치지 않고 필요한 데이터만 비동기로 주고받음</li>
<li>글자마다 요청이 발생하는 자동완성의 특성상 필수적임</li>
</ul>
</li>
<li><p><strong>브라우저 캐싱</strong></p>
<ul>
<li>자동완성 결과는 짧은 시간 안에 자주 바뀌지 않음</li>
<li>응답을 브라우저에 캐시해 두면 같은 접두어를 다시 입력할 때 서버 요청 자체가 발생하지 않음</li>
</ul>
</li>
<li><p><strong>데이터 샘플링</strong></p>
<ul>
<li>모든 질의를 기록하지 않고 일부만 골라 기록함</li>
<li>인기 검색어의 순위는 표본만으로도 충분히 파악할 수 있으므로 로그 양과 처리 비용을 크게 줄일 수 있음</li>
</ul>
</li>
</ul>
<hr>
<h1 id="8-트라이-연산">8. 트라이 연산</h1>
<h2 id="트라이-생성">트라이 생성</h2>
<p>작업 서버가 담당하며, 데이터 분석 서비스의 로그나 데이터베이스로부터 취합된 데이터를 이용함.</p>
<h2 id="트라이-갱신">트라이 갱신</h2>
<p>두 가지 방법이 있음.</p>
<ul>
<li><p><strong>주기마다 전체 갱신</strong></p>
<ul>
<li>새로운 트라이를 만들어 기존 트라이를 대체함</li>
</ul>
</li>
<li><p><strong>각 노드를 개별적으로 갱신</strong></p>
<ul>
<li>트라이가 작을 때 고려할 만한 방법임</li>
</ul>
</li>
</ul>
<h2 id="검색어-삭제">검색어 삭제</h2>
<p>혐오 조장, 폭력성, 성적 불쾌감, 위험 단어 등은 연산 결과에서 제거해야 함.</p>
<p>캐시 앞에 필터 계층을 두고 부적절한 질의어를 걸러내는 방법이 좋음.</p>
<pre><code class="language-text">트라이 캐시
→ 필터 계층
→ API 서버</code></pre>
<p>필터 계층을 두면 필터 규칙에 따라 검색 결과를 자유롭게 변경할 수 있기 때문임.</p>
<hr>
<h1 id="9-규모-확장이-가능한-저장소">9. 규모 확장이 가능한 저장소</h1>
<p>트라이가 한 서버에 담기지 않을 만큼 커지면 샤딩이 필요함.</p>
<h2 id="단순한-분할의-한계">단순한 분할의 한계</h2>
<p>첫 글자를 기준으로 나누는 방식은 데이터가 균등하게 분배되지 않음.</p>
<pre><code class="language-text">a로 시작하는 검색어 → 매우 많음
x, z로 시작하는 검색어 → 매우 적음</code></pre>
<h2 id="검색어-대응-샤드-관리자">검색어 대응 샤드 관리자</h2>
<p>과거 질의 데이터의 분포를 분석하여, 각 샤드가 비슷한 양의 데이터를 담도록 구간을 나눔.</p>
<pre><code class="language-text">분포 분석 결과 s로 시작하는 검색어가 많다면
→ s 하나만으로 한 샤드를 구성

u, v, w, x, y, z는 각각 적다면
→ 묶어서 한 샤드로 구성</code></pre>
<p>이 매핑 정보, 즉 어떤 검색어가 어느 저장소 서버에 저장되는지를 관리하는 것이 검색어 대응 샤드 관리자임.
<img src="https://velog.velcdn.com/images/dus_snp/post/d6016c4c-6cbd-447c-9e91-1f5b5dd2c818/image.png" alt=""></p>
<hr>
<h1 id="10-최종-정리">10. 최종 정리</h1>
<p>검색어 자동완성 시스템의 전체 흐름은 다음과 같음.</p>
<pre><code class="language-text">수집
→ 데이터 분석 로그
→ 로그 취합 서버
→ 작업 서버
→ 트라이 DB
→ 트라이 캐시

질의
→ 로드밸런서
→ API 서버
→ 트라이 캐시(미스 시 DB)
→ 응답</code></pre>
<p>핵심은 다음과 같음.</p>
<ul>
<li>100밀리초 이내 응답이 목표이므로 RDB 질의로는 부족하고 트라이가 필요함</li>
<li>접두어 길이 제한과 노드별 top k 캐시를 함께 써야 <code>O(1)</code>에 가까워짐</li>
<li>인기 검색어는 자주 바뀌지 않으므로 실시간 갱신 대신 주기적 배치로 처리함</li>
<li>부적절한 검색어는 필터 계층에서 걸러야 규칙 변경이 자유로움</li>
<li>샤딩은 첫 글자가 아니라 실제 질의 분포를 기준으로 나눠야 균등해짐</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 12장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-12%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-12%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 17 Aug 2026 13:35:03 GMT</pubDate>
            <description><![CDATA[<h1 id="채팅-시스템-설계">채팅 시스템 설계</h1>
<hr>
<h1 id="1-요구사항-확인">1. 요구사항 확인</h1>
<p>설계에 앞서 다음 사항을 먼저 확인해야 함.</p>
<ul>
<li><p>어떤 앱을 설계해야 하는가</p>
<ul>
<li>1:1 채팅 앱</li>
<li>그룹 채팅 앱</li>
</ul>
</li>
<li><p>처리해야 하는 트래픽 규모는 어느 정도인가(DAU)</p>
</li>
<li><p>그룹 채팅의 참여 인원 제한은 몇 명인가</p>
</li>
<li><p>어떤 기능이 필요한가</p>
<ul>
<li>접속 상태 표시</li>
<li>첨부파일 지원 여부</li>
<li>채팅방 유형</li>
</ul>
</li>
<li><p>메시지 길이 제한은 어떻게 되는가</p>
</li>
<li><p>종단 간 암호화를 지원하는가</p>
</li>
<li><p>채팅 이력을 얼마나 보관하는가</p>
</li>
<li><p>어떤 통신 프로토콜을 사용할 것인가</p>
</li>
</ul>
<hr>
<h1 id="2-채팅-서비스의-역할">2. 채팅 서비스의 역할</h1>
<p>클라이언트끼리 직접 통신하는 것이 아니라, 위의 기능을 모두 지원하는 채팅 서비스를 거쳐 통신함.</p>
<p>채팅 서비스는 다음 기능을 제공해야 함.</p>
<ul>
<li>클라이언트로부터 메시지 수신</li>
<li>메시지 수신자 결정 및 전달</li>
<li>수신자가 접속 상태가 아닌 경우 접속할 때까지 메시지 보관</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/0c73d6e3-e96d-4f7d-bccb-c803f4e1c091/image.png" alt=""></p>
<h2 id="메시지-송신">메시지 송신</h2>
<p>송신 클라이언트에서 채팅 서비스로 메시지를 보낼 때는 HTTP 프로토콜을 사용함.</p>
<p>이때 <code>keep-alive</code> 헤더를 사용하는 것이 효율적임.</p>
<ul>
<li>클라이언트와 서버 사이의 연결을 끊지 않고 유지할 수 있음</li>
<li>TCP 접속 과정에서 발생하는 핸드셰이크 횟수를 줄일 수 있음</li>
</ul>
<h2 id="메시지-수신">메시지 수신</h2>
<p>채팅 서비스가 수신 클라이언트에게 메시지를 보낼 때는 일반 HTTP를 사용할 수 없음.</p>
<p>HTTP는 클라이언트가 서버에 요청하는 방식이지, 서버가 클라이언트에게 먼저 보내는 방식이 아니기 때문임.</p>
<p>이를 해결하기 위한 기법으로 폴링, 롱 폴링, 웹소켓이 있음.</p>
<pre><code class="language-text">폴링, 롱 폴링
→ HTTP 프로토콜 위에서 동작

웹소켓
→ HTTP로 연결을 시작한 뒤 다른 프로토콜로 전환</code></pre>
<hr>
<h1 id="3-폴링-·-롱-폴링-·-웹소켓">3. 폴링 · 롱 폴링 · 웹소켓</h1>
<h2 id="폴링">폴링</h2>
<p>클라이언트가 주기적으로 서버에 새 메시지가 있는지 물어보는 방법임.</p>
<ul>
<li>폴링을 자주 할수록 비용이 올라감</li>
<li>메시지가 없는 경우 서버 자원이 불필요하게 낭비됨</li>
</ul>
<h2 id="롱-폴링">롱 폴링</h2>
<p>폴링을 변형한 방법으로, 새 메시지가 반환되거나 타임아웃될 때까지 연결을 유지함.</p>
<p>클라이언트는 새 메시지를 받으면 기존 연결을 종료하고 새 요청을 보내어 모든 절차를 다시 시작함.</p>
<p>단점:</p>
<ul>
<li><p>HTTP 서버는 보통 무상태이므로, 라운드로빈 방식으로 로드밸런싱하면 송신 클라이언트와 수신 클라이언트가 서로 다른 채팅 서버에 접속할 수 있음</p>
<ul>
<li>이 경우 송신 클라이언트가 메시지를 보내도 롱 폴링이 끝나지 않음</li>
</ul>
</li>
<li><p>서버 입장에서 클라이언트가 연결을 해제했는지 알아낼 좋은 방법이 없음</p>
</li>
<li><p>메시지를 많이 받지 않는 클라이언트도 타임아웃마다 주기적으로 접속하므로 여전히 비효율적임</p>
</li>
</ul>
<h2 id="웹소켓">웹소켓</h2>
<p>서버가 클라이언트에게 비동기 메시지를 보낼 때 가장 널리 사용하는 기술임.</p>
<ul>
<li><p>연결이 한 번 맺어지면 항구적으로 유지됨</p>
</li>
<li><p>80번(HTTP), 443번(HTTPS) 포트를 그대로 사용하므로 방화벽이 있는 환경에서도 잘 동작함</p>
<ul>
<li>스킴은 <code>ws://</code>, <code>wss://</code>를 사용함</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/f88d6405-5775-4e38-8547-5507814c76b2/image.png" alt=""></p>
<h2 id="세-방식-비교">세 방식 비교</h2>
<pre><code class="language-text">폴링
→ 요청 → 없음 → 대기 → 요청 → 없음 ...
→ 빈 응답이 대부분, 자원 낭비

롱 폴링
→ 요청 → (메시지 올 때까지 대기) → 응답 → 재요청
→ 빈 응답은 줄지만 재연결 반복

웹소켓
→ 연결 1회 → 양방향 송수신 지속
→ 서버가 먼저 보낼 수 있음</code></pre>
<hr>
<h1 id="4-웹소켓의-한계">4. 웹소켓의 한계</h1>
<p>웹소켓이 모든 상황에서 우월한 것은 아니며, 다음과 같은 비용이 따름.</p>
<ul>
<li><p><strong>서버가 상태를 갖게 됨</strong></p>
<ul>
<li>연결이 특정 서버에 묶이므로 무상태 서버처럼 자유롭게 분산할 수 없음</li>
<li>세션 고정이나 서비스 탐색 같은 별도 장치가 필요함</li>
</ul>
</li>
<li><p><strong>유휴 연결도 자원을 소모함</strong></p>
<ul>
<li>연결 하나마다 메모리와 소켓이 점유됨</li>
<li>메시지를 전혀 주고받지 않는 사용자도 비용을 발생시킴</li>
</ul>
</li>
<li><p><strong>재배포와 장애에 취약함</strong></p>
<ul>
<li>서버를 재시작하면 그 서버에 붙어 있던 모든 연결이 끊김</li>
<li>끊긴 클라이언트가 동시에 재연결을 시도하면 부하가 몰림</li>
</ul>
</li>
<li><p><strong>HTTP의 이점을 잃음</strong></p>
<ul>
<li>캐싱, 상태 코드, 표준 미들웨어 같은 기존 인프라를 그대로 쓸 수 없음</li>
<li>일부 프록시나 방화벽은 프로토콜 전환을 차단하므로 롱 폴링 폴백이 필요함</li>
</ul>
</li>
<li><p><strong>신뢰성을 직접 구현해야 함</strong></p>
<ul>
<li>연결이 끊긴 사이 유실된 메시지의 재전송, 순서 보장, 수신 확인을 애플리케이션이 처리해야 함</li>
</ul>
</li>
</ul>
<pre><code class="language-text">HTTP
→ 무상태 / 확장 쉬움 / 서버가 먼저 못 보냄

웹소켓
→ 상태 유지 / 확장 복잡 / 실시간 양방향</code></pre>
<p>즉 실시간 양방향 통신이 필요한 구간에만 웹소켓을 쓰고, 나머지는 일반 HTTP로 처리하는 것이 적절함.</p>
<hr>
<h1 id="5-개략적인-설계안">5. 개략적인 설계안</h1>
<p>채팅 시스템은 크게 세 부분으로 나눌 수 있음.</p>
<h2 id="무상태-서비스">무상태 서비스</h2>
<p>로그인, 회원가입, 사용자 프로파일 표시 등을 처리하는 전통적인 요청/응답 서비스임.</p>
<p>많은 웹사이트와 앱이 보편적으로 제공하는 기능에 해당함.</p>
<h2 id="상태-유지-서비스">상태 유지 서비스</h2>
<p>각 클라이언트가 채팅 서버와 독립적인 네트워크 연결을 유지해야 하므로 필요함.</p>
<p>서버가 살아 있는 한 다른 서버로 연결을 변경하지 않음.</p>
<h2 id="제3자-서비스-연동">제3자 서비스 연동</h2>
<p>앱이 실행 중이지 않더라도 알림을 받아야 하므로 푸시 알림 서비스와의 통합이 중요함.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/983a4734-60ac-4341-8c4e-3626a27788fd/image.png" alt=""></p>
<h2 id="컴포넌트별-역할">컴포넌트별 역할</h2>
<ul>
<li><p><strong>채팅 서버</strong></p>
<ul>
<li>클라이언트 간 메시지 중계</li>
<li>실시간 송수신을 위해 웹소켓 연결을 유지</li>
</ul>
</li>
<li><p><strong>접속상태 서버</strong></p>
<ul>
<li>사용자의 접속 여부 관리</li>
</ul>
</li>
<li><p><strong>API 서버</strong></p>
<ul>
<li>로그인, 회원가입, 프로파일 변경 등 나머지 전부 처리</li>
</ul>
</li>
<li><p><strong>알림 서버</strong></p>
<ul>
<li>푸시 알림 전송</li>
</ul>
</li>
<li><p><strong>키-값 저장소</strong></p>
<ul>
<li>채팅 이력 보관</li>
</ul>
</li>
</ul>
<hr>
<h1 id="6-저장소-선택">6. 저장소 선택</h1>
<p>RDB와 NoSQL 중 무엇을 쓸지 결정하려면 데이터의 유형과 읽기/쓰기 연산 패턴을 함께 고려해야 함.</p>
<h2 id="채팅-시스템이-다루는-데이터">채팅 시스템이 다루는 데이터</h2>
<p><strong>일반적인 데이터</strong></p>
<p>사용자 프로파일, 설정, 친구 목록 등이 해당함.</p>
<ul>
<li>안정성 보장을 위해 RDB에 보관</li>
<li>가용성과 규모 확장성을 위해 다중화와 샤딩을 사용</li>
</ul>
<p><strong>채팅 이력</strong></p>
<ul>
<li>빈번하게 사용되는 것은 최근에 주고받은 메시지</li>
<li>검색, 특정 사용자가 언급된 메시지 조회, 특정 메시지로 점프 등 무작위 접근을 지원하는 기능도 필요</li>
<li>1:1 채팅의 경우 읽기와 쓰기 비율은 대략 1:1 정도라고 함</li>
</ul>
<h2 id="키-값-저장소를-선택하는-이유">키-값 저장소를 선택하는 이유</h2>
<ul>
<li>수평적 규모 확장이 쉬움</li>
<li>데이터 접근 지연 시간이 낮음</li>
<li>RDB는 롱테일에 해당하는 부분을 잘 처리하지 못하는 경향이 있음</li>
<li>실제로 페이스북 메신저와 디스코드가 키-값 저장소를 채택하고 있음</li>
</ul>
<h2 id="롱테일">롱테일</h2>
<p>원래는 통계 그래프의 모양을 가리키는 용어임.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/558c24eb-0e9d-4f06-9db7-319e578c8618/image.png" alt=""></p>
<ul>
<li><p><strong>머리(head)</strong></p>
<ul>
<li>소수의 항목이 압도적으로 많은 양을 차지</li>
</ul>
</li>
<li><p><strong>꼬리(tail)</strong></p>
<ul>
<li>개별 항목의 양은 적지만 항목 수 자체가 많아 합치면 무시할 수 없는 비중을 차지</li>
</ul>
</li>
</ul>
<p>채팅 시스템에서는 채팅방 하나에 쌓이는 메시지 개수의 분포를 가리킴.</p>
<pre><code class="language-text">대부분의 1:1 대화, 소규모 채팅방
→ 메시지 수십~수백 개

극소수의 대규모 그룹, 활성 커뮤니티
→ 메시지 수백만 개</code></pre>
<h2 id="rdb가-롱테일을-잘-다루지-못하는-이유">RDB가 롱테일을 잘 다루지 못하는 이유</h2>
<ul>
<li><p><strong>인덱스가 커질수록 무작위 접근 비용이 증가함</strong></p>
<ul>
<li>메시지 전체가 하나의 테이블에 쌓이면서 인덱스 규모가 계속 커짐</li>
<li>최근 메시지가 아닌 오래된 메시지를 조회할수록 캐시에 없는 페이지를 읽어야 하므로 디스크 랜덤 I/O가 늘어남</li>
<li>꼬리 쪽 데이터가 전체의 대부분을 차지하므로 이 비용이 무시할 수 없는 수준이 됨</li>
</ul>
</li>
<li><p><strong>핫스팟 문제</strong></p>
<ul>
<li>메시지가 몰린 소수의 채팅방은 읽기와 쓰기 요청도 함께 몰림</li>
<li><code>chat_id</code> 같은 키로 샤딩하면 이 소수의 채팅방 때문에 특정 샤드에만 부하가 집중됨</li>
<li>나머지 샤드는 한가한 상태로 남아 불균형이 발생함</li>
</ul>
</li>
</ul>
<hr>
<h1 id="7-데이터-모델">7. 데이터 모델</h1>
<h2 id="11-채팅을-위한-메시지-테이블">1:1 채팅을 위한 메시지 테이블</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/4c61e33d-095b-453a-a740-fe126801f208/image.png" alt=""></p>
<p><code>메시지 ID</code>를 기본 키로 사용하면서 메시지 순서를 정하는 역할도 맡김.</p>
<p><code>작성 일시</code>는 서로 다른 메시지가 동시에 생성될 수 있어 순서를 정할 수 없는 경우가 있기 때문임.</p>
<h2 id="그룹-채팅을-위한-메시지-테이블">그룹 채팅을 위한 메시지 테이블</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/9a082efa-ab22-4ea9-9b59-fa6c447936d3/image.png" alt=""></p>
<p><code>채널 ID</code>와 <code>메시지 ID</code>를 기본 키로 사용함.</p>
<p>채널은 채팅 그룹과 같은 뜻이며, <code>메시지 ID</code>는 파티션 키로도 사용함.</p>
<h2 id="메시지-id">메시지 ID</h2>
<p><code>메시지 ID</code>가 만족해야 할 조건은 다음과 같음.</p>
<ul>
<li>고유한 값이어야 함</li>
<li>정렬 가능해야 함</li>
<li>시간 순서와 일치해야 함</li>
</ul>
<p><code>auto_increment</code>를 쓰면 간단히 해결될 것 같지만, NoSQL에서는 보통 해당 기능을 제공하지 않음.</p>
<p>따라서 다음 방법을 사용함.</p>
<ul>
<li><p>스노플레이크 같은 전역적 순서 번호 생성기</p>
</li>
<li><p>지역적 순서 번호 생성기</p>
<ul>
<li>ID의 유일성을 같은 그룹 안에서만 보증하면 충분하다는 뜻</li>
</ul>
</li>
</ul>
<hr>
<h1 id="8-서비스-탐색">8. 서비스 탐색</h1>
<p>클라이언트에게 가장 적합한 채팅 서버를 추천하는 역할임.</p>
<p>기준으로는 클라이언트의 위치, 서버의 용량 등을 사용함.</p>
<p>오픈소스 솔루션으로는 아파치 주키퍼가 널리 쓰임.</p>
<hr>
<h1 id="9-메시지-전달-흐름">9. 메시지 전달 흐름</h1>
<h2 id="11-채팅-메시지-처리-흐름">1:1 채팅 메시지 처리 흐름</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/7b0d2055-5753-4910-ad69-f72ed0b6aaab/image.png" alt=""></p>
<h2 id="여러-단말-사이의-메시지-동기화">여러 단말 사이의 메시지 동기화</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/844813c7-87df-4540-934e-a17523acc06a/image.png" alt=""></p>
<p>한 사용자가 여러 단말을 사용하는 경우, 각 단말은 저마다 독립적인 웹소켓 연결을 유지함.</p>
<p>따라서 어떤 단말에서 접속하더라도 동일한 메시지를 빠짐없이 받아야 함.</p>
<p>이를 위해 각 단말은 자신이 마지막으로 확인한 메시지의 ID를 보관함.</p>
<pre><code class="language-text">단말이 보관하는 값
→ cur_max_message_id

새 메시지 판별 조건
→ 수신자가 나이고
→ message_id &gt; cur_max_message_id</code></pre>
<p><code>message_id</code>가 시간 순서와 일치하는 값이므로, 이 조건만으로 각 단말이 놓친 메시지를 판별할 수 있음.</p>
<pre><code class="language-text">단말 A: cur_max_message_id = 1005
단말 B: cur_max_message_id = 1002

→ 단말 B는 1003~1005를 추가로 내려받아 동기화</code></pre>
<p>즉 서버가 단말별 상태를 일일이 관리하지 않아도, 단말이 보관한 값 하나로 동기화가 성립함.</p>
<h2 id="소규모-그룹-채팅에서의-메시지-흐름">소규모 그룹 채팅에서의 메시지 흐름</h2>
<p>그룹 채팅에서는 메시지를 그룹 구성원 각각의 메시지 동기화 큐에 사본으로 넣음.</p>
<pre><code class="language-text">사용자 A가 메시지 전송
→ A를 제외한 구성원 각각의 큐에 사본 저장
→ 각 사용자는 자기 큐만 확인</code></pre>
<p>이 방식의 장점은 다음과 같음.</p>
<ul>
<li>수신자는 자기 큐 하나만 보면 되므로 메시지 동기화 흐름이 1:1 채팅과 동일해짐</li>
<li>여러 사용자로부터 온 메시지를 한 곳에서 순서대로 확인할 수 있음</li>
<li>그룹 규모가 작으면 사본을 여러 벌 저장하는 비용이 크지 않음</li>
</ul>
<pre><code class="language-text">수신자 관점
→ 큐 1개
→ 발신자가 누구든 동일하게 처리</code></pre>
<p>다만 사본의 개수가 그룹 크기에 비례하므로, 인원이 매우 많은 그룹에는 적합하지 않음.</p>
<p>이 경우에는 사본을 만들지 않고 채널 단위로 메시지를 보관한 뒤 읽는 시점에 조회하는 방식을 사용함.</p>
<hr>
<h1 id="10-접속-상태-표시">10. 접속 상태 표시</h1>
<h2 id="접속">접속</h2>
<p>사용자가 로그인하여 웹소켓 연결이 맺어지면, 접속상태 서버는 해당 사용자의 상태와 <code>last_active_at</code> 타임스탬프를 키-값 저장소에 보관함.</p>
<p>이 절차가 끝나면 접속 중으로 표시됨.</p>
<h2 id="접속-장애">접속 장애</h2>
<p>클라이언트가 보내는 Heartbeat Event를 사용하여 접속 상태를 판단함.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/6c4dd603-5bde-4f7e-a210-71453d1db3ed/image.png" alt=""></p>
<p>매우 짧은 시간 동안 발생한 접속 장애를 모두 반영해 상태를 변경하면 지나칠 뿐 아니라 UX에도 좋지 않기 때문임.</p>
<h2 id="상태-정보의-전송">상태 정보의 전송</h2>
<p>발행-구독 모델을 사용하여 접속 상태 변경을 알림.</p>
<p>다만 이 방법은 소규모 그룹에서만 효과적이고, 대규모 그룹에서는 비용과 시간이 많이 듦.</p>
<p>따라서 대규모 그룹에서는 다음과 같은 방법을 사용함.</p>
<ul>
<li>사용자가 채팅에 입장하는 순간에만 상태 정보를 읽어가게 함</li>
<li>접속 상태를 수동으로 갱신하도록 유도함</li>
</ul>
<hr>
<h1 id="11-최종-정리">11. 최종 정리</h1>
<p>채팅 시스템의 전체 구조는 다음과 같음.</p>
<pre><code class="language-text">로그인, 회원가입, 프로파일
→ API 서버(무상태)

메시지 송수신
→ 채팅 서버(웹소켓, 상태 유지)
→ 메시지 동기화 큐
→ 키-값 저장소

접속 상태
→ 접속상태 서버 + 발행-구독

앱 미실행 시
→ 알림 서버 → 푸시 알림</code></pre>
<p>핵심은 다음과 같음.</p>
<ul>
<li>서버가 먼저 보내야 하는 구간에만 웹소켓을 쓰고 나머지는 HTTP로 처리할 것</li>
<li>웹소켓은 상태를 갖는 대신 확장이 어려워지므로 서비스 탐색 같은 장치가 필요함</li>
<li>채팅 이력은 롱테일 분포를 가지므로 키-값 저장소가 유리함</li>
<li><code>message_id</code>는 고유하면서 시간 순서와 일치해야 하며, 이 값 하나로 다중 단말 동기화가 가능함</li>
<li>그룹 채팅은 규모에 따라 사본 저장과 채널 단위 보관 중에서 선택해야 함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 11장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-11%EC%9E%A5-%EC%A0%95%EB%A6%AC-5h0wq0fu</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-11%EC%9E%A5-%EC%A0%95%EB%A6%AC-5h0wq0fu</guid>
            <pubDate>Thu, 13 Aug 2026 07:44:10 GMT</pubDate>
            <description><![CDATA[<h1 id="뉴스-피드-시스템-설계">뉴스 피드 시스템 설계</h1>
<p>뉴스 피드는 홈페이지 중앙에 지속적으로 업데이트되는 스토리들의 목록임.</p>
<p>페이스북 도움말 페이지의 정의에 따르면 다음과 같은 항목이 포함됨.</p>
<ul>
<li>사용자 상태 정보 업데이트</li>
<li>사진, 비디오, 링크</li>
<li>앱 활동</li>
<li>팔로우하는 사람, 페이지, 그룹으로부터 발생한 좋아요</li>
</ul>
<hr>
<h1 id="1-요구사항-확인">1. 요구사항 확인</h1>
<p>설계에 앞서 다음 사항을 먼저 확인해야 함.</p>
<ul>
<li><p>어떤 클라이언트를 지원할 것인가</p>
<ul>
<li>앱</li>
<li>웹</li>
</ul>
</li>
<li><p>어떤 기능이 필요한가</p>
<ul>
<li>스토리 업로드</li>
<li>친구의 스토리 조회</li>
</ul>
</li>
<li><p>스토리를 어떤 순서로 표시할 것인가</p>
<ul>
<li>최신순</li>
<li>토픽 점수순</li>
<li>그 외 기준</li>
</ul>
</li>
<li><p>한 사용자가 가질 수 있는 최대 친구 수는 몇인가</p>
</li>
<li><p>트래픽 규모는 어느 정도인가</p>
</li>
<li><p>피드에 어떤 형식의 콘텐츠가 들어가는가</p>
<ul>
<li>텍스트</li>
<li>이미지</li>
<li>비디오</li>
</ul>
</li>
</ul>
<hr>
<h1 id="2-뉴스-피드-api">2. 뉴스 피드 API</h1>
<p>클라이언트가 서버와 통신하기 위해 사용하는 수단임.</p>
<p>HTTP 프로토콜 기반이며 상태 정보 업데이트, 뉴스 피드 조회, 친구 추가 등의 작업을 수행하는 데 사용함.</p>
<h2 id="피드-발행-api">피드 발행 API</h2>
<p>새 스토리를 포스팅하기 위한 API임.</p>
<pre><code class="language-http">POST /v1/me/feed</code></pre>
<p>인자:</p>
<ul>
<li><p><code>body</code></p>
<ul>
<li>포스팅 내용</li>
</ul>
</li>
<li><p><code>Authorization</code> 헤더</p>
<ul>
<li>API 호출 인증에 사용</li>
</ul>
</li>
</ul>
<h2 id="피드-읽기-api">피드 읽기 API</h2>
<p>뉴스 피드를 가져오는 API임.</p>
<pre><code class="language-http">GET /v1/me/feed</code></pre>
<p>인자:</p>
<ul>
<li><p><code>Authorization</code> 헤더</p>
<ul>
<li>API 호출 인증에 사용</li>
</ul>
</li>
</ul>
<hr>
<h1 id="3-개략적인-설계안">3. 개략적인 설계안</h1>
<p>뉴스 피드 시스템은 크게 두 부분으로 나눌 수 있음.</p>
<ul>
<li><p><strong>피드 발행</strong></p>
<ul>
<li>사용자가 스토리를 포스팅하면 캐시와 DB에 기록</li>
<li>새 포스팅은 친구의 뉴스 피드에도 전송</li>
</ul>
</li>
<li><p><strong>뉴스 피드 생성</strong></p>
<ul>
<li>모든 친구의 포스팅을 시간 흐름 역순으로 모아서 구성</li>
</ul>
</li>
</ul>
<p>이 설계에서는 정렬 기준을 시간 역순으로 가정함.</p>
<p>다만 실제 서비스에서는 사용자의 참여 가능성을 예측하는 랭킹 모델을 사용해 노출 순서를 결정하는 경우가 일반적임.</p>
<h2 id="피드-발행-시스템">피드 발행 시스템</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/eb46380f-a1f1-443f-9e38-01bd319c47b2/image.png" alt=""></p>
<h2 id="뉴스-피드-생성-시스템">뉴스 피드 생성 시스템</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/4cc5d8ff-49af-42e3-b448-bae4da216e7d/image.png" alt=""></p>
<hr>
<h1 id="4-피드-발행-흐름-상세-설계">4. 피드 발행 흐름 상세 설계</h1>
<h2 id="웹-서버">웹 서버</h2>
<p>클라이언트와의 통신뿐 아니라 인증이나 처리율 제한 등의 기능도 수행함.</p>
<ul>
<li>올바른 인증 토큰을 <code>Authorization</code> 헤더에 넣어 API를 호출한 사용자만 포스팅할 수 있어야 함</li>
<li>특정 기간 동안 한 사용자가 올릴 수 있는 포스팅 수를 제한하여 스팸과 유해 콘텐츠 유입을 방지함</li>
</ul>
<h2 id="포스팅-전송팬아웃-서비스">포스팅 전송(팬아웃) 서비스</h2>
<p>팬아웃은 어떤 사용자의 새 포스팅을 그와 친구 관계에 있는 모든 사용자에게 전달하는 과정임.</p>
<p>크게 두 가지 모델로 나뉨.</p>
<pre><code class="language-text">쓰기 시점 팬아웃 = push 모델
읽기 시점 팬아웃 = pull 모델</code></pre>
<hr>
<h1 id="5-팬아웃-모델-push와-pull">5. 팬아웃 모델: push와 pull</h1>
<h2 id="push-모델">push 모델</h2>
<p>새로운 포스팅을 기록하는 시점에 뉴스 피드를 갱신함.</p>
<p>포스팅이 완료되면 바로 친구들의 뉴스 피드 캐시에 해당 포스팅을 기록함.</p>
<pre><code class="language-text">포스팅 작성
→ 친구 목록 조회
→ 친구 각각의 피드 캐시에 기록
→ (사용자가 접속하면 캐시를 그대로 읽음)</code></pre>
<p>장점:</p>
<ul>
<li>뉴스 피드가 실시간으로 갱신되어 친구에게 즉시 전달됨</li>
<li>포스팅 시점에 이미 피드가 갱신되어 있으므로 읽기 시간이 짧음</li>
</ul>
<p>단점:</p>
<ul>
<li>친구가 많은 사용자의 경우 피드 갱신에 많은 시간이 소요됨</li>
<li>서비스를 자주 이용하지 않는 사용자의 피드까지 갱신하므로 컴퓨팅 자원이 낭비됨</li>
</ul>
<h2 id="pull-모델">pull 모델</h2>
<p>피드를 읽어야 하는 시점에 뉴스 피드를 갱신함.</p>
<p>즉 요청 기반 모델이며, 사용자가 홈이나 타임라인을 로딩하는 시점에 새 포스팅을 가져옴.</p>
<pre><code class="language-text">포스팅 작성
→ 작성자 쪽에만 기록

사용자 접속
→ 친구 목록 조회
→ 각 친구의 최근 포스팅 조회
→ 병합 후 렌더링</code></pre>
<p>장점:</p>
<ul>
<li><p>비활성 사용자나 서비스를 자주 이용하지 않는 사용자에게 유리함</p>
<ul>
<li>로그인 전까지 어떤 컴퓨팅 자원도 소모하지 않음</li>
</ul>
</li>
<li><p>데이터를 친구 각각에게 밀어 넣는 작업이 없으므로 쓰기 부하가 집중되지 않음</p>
</li>
</ul>
<p>단점:</p>
<ul>
<li>뉴스 피드를 읽는 데 많은 시간이 소요됨</li>
</ul>
<h2 id="두-모델-비교">두 모델 비교</h2>
<pre><code class="language-text">push
→ 쓰기 비쌈 / 읽기 쌈
→ 실시간성 좋음
→ 친구 많은 사용자에게 취약

pull
→ 쓰기 쌈 / 읽기 비쌈
→ 자원 낭비 없음
→ 접속 시 지연 발생</code></pre>
<hr>
<h1 id="6-쓰기-증폭과-핫-키">6. 쓰기 증폭과 핫 키</h1>
<p>팔로워가 매우 많은 사용자를 처리할 때 발생하는 문제는 사실 두 가지이며, 서로 다른 문제임.</p>
<h2 id="쓰기-증폭팬아웃-폭발">쓰기 증폭(팬아웃 폭발)</h2>
<p>포스팅 하나를 처리하기 위해 팔로워 수만큼의 쓰기 작업이 한꺼번에 발생하는 문제임.</p>
<pre><code class="language-text">포스팅 1건
→ 팔로워 1,000만 명
→ 피드 캐시 쓰기 1,000만 회</code></pre>
<p>push 모델에서 발생하며, 쓰기 지연과 자원 소모의 원인이 됨.</p>
<h2 id="핫-키">핫 키</h2>
<p>캐시나 샤딩 환경에서 특정 키 하나에 요청이 몰려 그 키를 담당하는 노드만 과부하되는 문제임.</p>
<pre><code class="language-text">셀럽 계정 데이터 = 단일 키
→ 특정 샤드로만 요청 집중
→ 해당 노드만 과부하</code></pre>
<p>셀럽 계정은 두 문제를 동시에 일으키지만, 원인과 해법은 서로 다름.</p>
<h2 id="안정-해시는-핫-키의-해법이-아님">안정 해시는 핫 키의 해법이 아님</h2>
<p>안정 해시(consistent hashing)는 노드가 추가되거나 제거될 때 재배치를 최소화하고 데이터를 고르게 분산하는 기법임.</p>
<p>그러나 요청이 몰리는 대상이 키 하나라면 어느 노드에 배치되든 그 노드에 부하가 집중되므로, 안정 해시만으로는 핫 키를 해결할 수 없음.</p>
<pre><code class="language-text">안정 해시
→ 샤드 단위 분산은 균등해짐
→ 단일 핫 키 부하는 그대로</code></pre>
<p>실제로 페이스북의 TAO 역시 샤드를 캐시 서버에 매핑할 때 안정 해시를 사용하지만, 핫 키는 별도로 처리함.</p>
<ul>
<li>인기 샤드를 여러 서버가 함께 서빙하는 샤드 복제</li>
<li>접근 빈도가 임계값을 넘는 항목에 대한 클라이언트 측 캐싱</li>
</ul>
<p>즉 안정 해시는 일반적인 부하 분산 기법으로 두고, 셀럽 문제의 해법은 아래의 하이브리드 팬아웃으로 보는 것이 적절함.</p>
<hr>
<h1 id="7-하이브리드-팬아웃">7. 하이브리드 팬아웃</h1>
<p>실제 뉴스 피드 시스템은 push와 pull을 함께 사용함.</p>
<p>두 모델의 단점을 버리고 장점만 취하기 위한 방식임.</p>
<pre><code class="language-text">일반 사용자
→ push
→ 포스팅 시점에 친구 피드에 기록

팔로워 수 임계값 초과 사용자(셀럽)
→ pull
→ 팔로워가 피드를 읽는 시점에 조회 후 병합</code></pre>
<p>팔로워 수를 기준으로 분기하므로, 쓰기 증폭을 일으키는 소수 계정만 pull로 빠지고 나머지는 push의 빠른 읽기 성능을 그대로 유지할 수 있음.</p>
<h2 id="팬아웃-서비스-동작-과정">팬아웃 서비스 동작 과정</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/0136ee53-4e4b-4108-a5ff-04cc44b13913/image.png" alt=""></p>
<ol>
<li>그래프 DB에서 친구 ID 목록 조회</li>
<li>사용자 정보 캐시에서 친구 정보를 조회한 뒤 설정(mute, 친한 친구 등)에 따라 걸러내기</li>
<li>친구 목록과 새 스토리의 포스팅 ID를 메시지 큐에 넣음</li>
<li>작업 서버가 <code>&lt;포스팅ID, 사용자ID&gt;</code> 형태의 데이터를 뉴스 피드 캐시에 넣음</li>
</ol>
<h2 id="그래프-데이터베이스">그래프 데이터베이스</h2>
<p>친구 관계 조회는 &quot;특정 사용자에서 출발해 연결된 노드를 따라가는&quot; 탐색 연산이므로, 관계 자체를 1급 개념으로 다루는 그래프 DB가 적합함.</p>
<p>책이 참고 자료로 제시한 페이스북의 TAO 논문을 기준으로 보면 소셜 그래프는 두 가지 요소로 모델링됨.</p>
<ul>
<li><p><strong>객체(Object)</strong></p>
<ul>
<li>타입을 가진 노드</li>
<li>사용자, 게시물, 댓글, 장소 등</li>
</ul>
</li>
<li><p><strong>연관(Association)</strong></p>
<ul>
<li>타입을 가진 방향성 엣지</li>
<li>친구 관계, 작성 관계, 좋아요 등</li>
</ul>
</li>
</ul>
<pre><code class="language-text">Object: (id) → (타입, 데이터)
Assoc : (id1, 엣지타입, id2) → (시간, 데이터)</code></pre>
<p>질의는 항상 특정 객체와 엣지 타입에서 시작함.</p>
<pre><code class="language-text">(사용자 A, FRIEND) → [친구 목록]
(게시물 X, COMMENT) → [댓글 목록]</code></pre>
<p>이런 구조 덕분에 다음과 같은 특성을 가짐.</p>
<ul>
<li>인접 노드 탐색이 단일 조회로 처리됨</li>
<li>엣지 목록이 시간 역순으로 정렬되어 최신 항목 조회에 유리함</li>
<li>읽기가 압도적으로 많은 워크로드에 맞춰 캐시 계층이 그래프 구조를 이해한 상태로 동작함</li>
</ul>
<p>영구 저장은 MySQL을 사용하되 그 앞에 그래프를 인지하는 캐시 계층을 두는 구조이며, 강한 일관성보다 가용성과 읽기 효율을 우선함.</p>
<hr>
<h1 id="8-피드-읽기-흐름-상세-설계">8. 피드 읽기 흐름 상세 설계</h1>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/da1da933-7bb3-4d42-b5f3-71d87d52330c/image.png" alt=""></p>
<ol>
<li>사용자가 요청 전송</li>
<li>로드밸런서가 웹 서버 중 하나에 요청 전달</li>
<li>웹 서버가 뉴스 피드 서비스 호출</li>
<li>뉴스 피드 서비스가 뉴스 피드 캐시에서 포스팅 ID 목록 조회</li>
<li>사용자 이름, 사진, 포스팅 콘텐츠 등을 사용자 캐시와 포스팅 캐시에서 가져와 뉴스 피드 생성</li>
<li>생성된 뉴스 피드를 JSON 형태로 클라이언트에 전달하고 클라이언트가 렌더링</li>
</ol>
<p>이때 이미지나 비디오 같은 미디어 콘텐츠는 피드 응답에 직접 담지 않고 CDN에서 가져옴.</p>
<pre><code class="language-text">피드 응답(JSON)
→ 텍스트, 메타데이터, 미디어 URL

미디어 파일
→ CDN
→ 클라이언트</code></pre>
<p>용량이 큰 파일을 CDN으로 분리하면 응답 크기가 줄고 지연도 개선됨.</p>
<hr>
<h1 id="9-캐시-구조">9. 캐시 구조</h1>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/70899b3b-bc01-437a-bbb4-47ce387de456/image.png" alt=""></p>
<p>캐시는 다음과 같이 계층별로 나눔.</p>
<ul>
<li><p><strong>뉴스 피드</strong></p>
<ul>
<li>뉴스 피드 ID 보관</li>
</ul>
</li>
<li><p><strong>콘텐츠</strong></p>
<ul>
<li>포스팅 데이터 보관</li>
<li>인기 콘텐츠는 따로 보관</li>
</ul>
</li>
<li><p><strong>소셜 그래프</strong></p>
<ul>
<li>사용자 간 관계 정보 보관</li>
</ul>
</li>
<li><p><strong>행동</strong></p>
<ul>
<li>포스팅에 대한 사용자의 행위(좋아요, 댓글 등)</li>
</ul>
</li>
<li><p><strong>횟수</strong></p>
<ul>
<li>좋아요 수, 댓글 수, 팔로잉/팔로워 수 등</li>
</ul>
</li>
</ul>
<h2 id="뉴스-피드-캐시의-크기-제한">뉴스 피드 캐시의 크기 제한</h2>
<p>뉴스 피드 캐시에 모든 포스팅을 담지 않고 최근 N개만 보관함.</p>
<pre><code class="language-text">사용자당 피드 캐시
→ 최근 N개 ID만 유지
→ 그 이후는 DB에서 조회</code></pre>
<p>대부분의 사용자는 피드 상단 일부만 확인하므로, 전체를 캐싱하는 것은 메모리 낭비임.</p>
<p>또한 ID만 보관하고 콘텐츠 본문은 별도 캐시에서 가져오므로 사용자 수가 늘어나도 메모리 사용량이 완만하게 증가함.</p>
<hr>
<h1 id="10-최종-정리">10. 최종 정리</h1>
<p>뉴스 피드 시스템의 전체 흐름은 다음과 같음.</p>
<pre><code class="language-text">포스팅
→ 웹 서버(인증, 처리율 제한)
→ 팬아웃 서비스
→ 메시지 큐
→ 작업 서버
→ 뉴스 피드 캐시

조회
→ 웹 서버
→ 뉴스 피드 서비스
→ 피드 캐시에서 ID 조회
→ 콘텐츠/사용자 캐시에서 상세 조회
→ 미디어는 CDN
→ 클라이언트 렌더링</code></pre>
<p>핵심은 단순히 포스팅을 모아서 보여주는 것이 아니라 다음 사항을 함께 고려하는 것임.</p>
<ul>
<li>쓰기 부하와 읽기 부하 중 무엇을 감수할 것인가(push와 pull)</li>
<li>팔로워가 많은 계정의 쓰기 증폭을 어떻게 분리할 것인가</li>
<li>핫 키를 부하 분산 기법과 구분해 별도로 처리할 것</li>
<li>친구 관계 탐색에 적합한 저장소를 선택할 것</li>
<li>캐시를 계층별로 나누고 보관 범위를 제한할 것</li>
<li>미디어를 CDN으로 분리할 것</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 10장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-10%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-10%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 13 Aug 2026 04:24:37 GMT</pubDate>
            <description><![CDATA[<h1 id="알림-시스템-설계">알림 시스템 설계</h1>
<p>알림 시스템은 사용자에게 중요한 정보나 이벤트를 전달하는 시스템</p>
<p>대표적인 알림 방식은 다음과 같음.</p>
<ul>
<li>모바일 푸시 알림</li>
<li>SMS 메시지</li>
<li>이메일</li>
</ul>
<p>알림 시스템은 보통 정해진 시간 안에 빠르게 전달되는 것이 중요하지만, 일부 지연이 발생했다고 시스템 전체가 실패한 것으로 보지는 않기 때문에 <strong>연성 실시간(Soft Real-Time)</strong> 시스템에 가까움.</p>
<p>반대로 <strong>경성 실시간(Hard Real-Time)</strong> 시스템은 정해진 deadline을 반드시 지켜야 하며 이를 넘기는 것이 시스템 실패로 이어질 수 있음.</p>
<hr>
<h1 id="1-요구사항-확인">1. 요구사항 확인</h1>
<p>알림 시스템을 설계하기 전에 다음 사항을 먼저 확인해야 함.</p>
<ul>
<li><p>어떤 종류의 알림을 지원할 것인가</p>
<ul>
<li>푸시</li>
<li>SMS</li>
<li>이메일</li>
</ul>
</li>
<li><p>어떤 종류의 단말을 지원할 것인가</p>
<ul>
<li>iOS</li>
<li>Android</li>
<li>웹 등</li>
</ul>
</li>
<li><p>알림을 생성하는 주체는 누구인가</p>
</li>
<li><p>사용자가 특정 알림을 거부하거나 <code>opt-out</code> 할 수 있어야 하는가</p>
</li>
<li><p>하루에 몇 건의 알림을 처리해야 하는가</p>
</li>
<li><p>어느 정도의 알림 지연까지 허용할 것인가</p>
</li>
</ul>
<hr>
<h1 id="2-알림-유형별-전송-방식">2. 알림 유형별 전송 방식</h1>
<h2 id="ios-푸시-알림">iOS 푸시 알림</h2>
<p>iOS에 푸시 알림을 보내기 위해서는 크게 다음 세 요소가 필요함.</p>
<h3 id="알림-제공자notification-provider">알림 제공자(Notification Provider)</h3>
<p>알림 요청을 생성하여 APNs로 전달하는 서버임.</p>
<p>알림을 생성하기 위해 다음과 같은 정보가 필요함.</p>
<ul>
<li><p><strong>Device Token</strong></p>
<ul>
<li>특정 기기에서 실행되는 특정 앱으로 푸시 알림을 전달하기 위해 APNs가 발급하는 식별 정보</li>
<li>앱은 발급받은 Device Token을 알림 서버에 전달</li>
</ul>
</li>
<li><p><strong>Payload</strong></p>
<ul>
<li>알림 내용을 담고 있는 JSON 데이터</li>
</ul>
</li>
</ul>
<h3 id="apns">APNs</h3>
<p><code>Apple Push Notification service</code></p>
<p>Apple이 제공하는 푸시 알림 서비스로, 알림 서버에서 받은 요청을 실제 iOS 단말로 전달함.</p>
<pre><code class="language-text">알림 서버
→ APNs
→ iOS 앱</code></pre>
<h2 id="android-푸시-알림">Android 푸시 알림</h2>
<p>Android에서는 일반적으로 FCM(Firebase Cloud Messaging)을 사용함.</p>
<p>개략적인 구조는 iOS와 유사함.</p>
<pre><code class="language-text">알림 서버
→ FCM
→ Android 앱</code></pre>
<p>다만 APNs와 FCM은 사용하는 API와 인증 방식 등 세부 구현에는 차이가 있음.</p>
<h2 id="sms">SMS</h2>
<p>SMS는 통신사와 직접 연동하기보다 제3자 서비스를 사용하는 경우가 많음.</p>
<p>대표적인 서비스:</p>
<ul>
<li>Twilio</li>
<li>Vonage(구 Nexmo)</li>
</ul>
<pre><code class="language-text">알림 서버
→ SMS 제공 사업자
→ 이동통신망
→ 사용자 휴대전화</code></pre>
<h2 id="이메일">이메일</h2>
<p>이메일 역시 제3자 이메일 서비스를 사용할 수 있음.</p>
<p>대표적인 서비스:</p>
<ul>
<li>SendGrid</li>
<li>Mailchimp</li>
</ul>
<p>이러한 서비스는 대량 이메일 전송뿐 아니라 전송 성공률이나 클릭률 등의 데이터 분석 기능도 제공할 수 있음.</p>
<hr>
<h1 id="3-사용자-연락처-및-단말-정보-저장">3. 사용자 연락처 및 단말 정보 저장</h1>
<p>알림을 보내기 위해 다음과 같은 정보가 필요함.</p>
<ul>
<li>이메일 주소</li>
<li>전화번호</li>
<li>Device Token</li>
</ul>
<p>이메일 주소와 전화번호 등의 기본 정보는 <code>user</code> 테이블에 저장하고, Device Token은 별도의 <code>device</code> 테이블에 저장함.</p>
<p>이렇게 분리하는 이유는 한 사용자가 여러 단말을 사용할 수 있기 때문임.</p>
<pre><code class="language-text">User A
 ├─ Device 1
 ├─ Device 2
 └─ Device 3</code></pre>
<p>즉 관계는 다음과 같음.</p>
<pre><code class="language-text">User : Device
= 1 : N</code></pre>
<p>필요한 경우 한 사용자가 등록한 모든 단말로 알림을 전송할 수 있음.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/6e53158e-8776-4556-b972-ed113cb55471/image.png" alt=""></p>
<hr>
<h1 id="4-개략적인-시스템-설계">4. 개략적인 시스템 설계</h1>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/46931074-7442-461b-af7d-1f485e149c0e/image.png" alt=""></p>
<p>알림 시스템의 주요 컴포넌트는 다음과 같음.</p>
<h2 id="service-1--n">Service 1 ~ N</h2>
<p>알림을 발생시키는 서비스임.</p>
<p>예:</p>
<ul>
<li>마이크로서비스</li>
<li>크론잡</li>
<li>배치 시스템</li>
<li>분산 시스템의 다른 컴포넌트</li>
</ul>
<p>예를 들어:</p>
<pre><code class="language-text">주문 서비스 → 주문 완료 알림
결제 서비스 → 결제 완료 알림
배송 서비스 → 배송 시작 알림</code></pre>
<p>즉 알림 시스템은 서로 다른 서비스들이 생성하는 알림을 공통으로 처리하는 인프라 역할을 함.</p>
<h2 id="알림-서버">알림 서버</h2>
<p>알림 시스템의 핵심 컴포넌트임.</p>
<p>주요 역할:</p>
<ul>
<li>서비스들이 사용할 알림 API 제공</li>
<li>사용자 정보 조회</li>
<li>Device Token 조회</li>
<li>사용자 알림 설정 확인</li>
<li>알림 템플릿 적용</li>
<li>제3자 서비스에 전달할 Payload 생성</li>
<li>알림 이벤트를 메시지 큐에 저장</li>
</ul>
<p>알림 서버를 한 대만 두면 다음 문제가 발생할 수 있음.</p>
<ul>
<li>SPOF</li>
<li>처리량 한계</li>
<li>성능 병목</li>
<li>수평적 확장 어려움</li>
</ul>
<p>따라서 알림 서버를 여러 대로 구성하고 DB와 캐시도 별도 컴포넌트로 분리함.</p>
<h2 id="캐시">캐시</h2>
<p>자주 조회되는 다음 정보를 저장함.</p>
<ul>
<li>사용자 정보</li>
<li>Device 정보</li>
<li>알림 설정</li>
<li>알림 템플릿</li>
</ul>
<p>DB 접근을 줄이고 응답 시간을 단축할 수 있음.</p>
<h2 id="데이터베이스">데이터베이스</h2>
<p>다음과 같은 데이터를 영구 저장함.</p>
<ul>
<li>사용자 정보</li>
<li>Device Token</li>
<li>사용자 알림 설정</li>
<li>알림 정보</li>
<li>알림 로그</li>
<li>전송 상태</li>
</ul>
<h2 id="메시지-큐">메시지 큐</h2>
<p>시스템 컴포넌트 사이의 강한 결합을 줄이는 역할을 함.</p>
<pre><code class="language-text">알림 서버
→ Message Queue
→ Worker</code></pre>
<p>대량의 알림이 한꺼번에 발생할 경우 알림을 임시로 저장하는 버퍼 역할도 담당함.</p>
<h2 id="작업-서버worker">작업 서버(Worker)</h2>
<p>메시지 큐에서 알림 이벤트를 가져와 실제 제3자 서비스로 전달함.</p>
<pre><code class="language-text">Push Queue
→ Push Worker
→ APNs / FCM

SMS Queue
→ SMS Worker
→ SMS Provider

Email Queue
→ Email Worker
→ Email Provider</code></pre>
<h2 id="제3자-서비스">제3자 서비스</h2>
<p>실제로 사용자에게 알림을 전달함.</p>
<p>예:</p>
<ul>
<li>APNs</li>
<li>FCM</li>
<li>Twilio</li>
<li>SendGrid</li>
</ul>
<p>제3자 서비스와 통합할 때는 새로운 서비스 사업자를 쉽게 추가하거나 기존 사업자를 제거할 수 있도록 설계하는 것이 중요함.</p>
<p>특정 국가나 시장에서는 특정 서비스 제공자를 사용할 수 없을 수도 있으므로 대체 사업자를 지원할 수 있어야 함.</p>
<hr>
<h1 id="5-알림-api와-전송-과정">5. 알림 API와 전송 과정</h1>
<h2 id="이메일-알림-api-예제">이메일 알림 API 예제</h2>
<pre><code class="language-http">POST /api/v1/email/send</code></pre>
<p>요청 예:</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/17c34700-58aa-451c-aec5-43aa8bbf4245/image.png" alt=""></p>
<h2 id="알림-전송-과정">알림 전송 과정</h2>
<ol>
<li>서비스가 알림 API 호출</li>
<li>알림 서버가 캐시 또는 DB에서 사용자 정보, Device Token, 알림 설정 등의 메타데이터 조회</li>
<li>알림 서버가 전송할 알림 이벤트 생성</li>
<li>알림 종류에 맞는 메시지 큐에 이벤트 저장</li>
<li>Worker가 메시지 큐에서 이벤트 조회</li>
<li>Worker가 APNs, FCM, SMS, 이메일 등의 제3자 서비스로 전달</li>
<li>제3자 서비스가 사용자 단말로 알림 전달</li>
</ol>
<pre><code class="language-text">서비스
→ 알림 서버
→ 메시지 큐
→ Worker
→ 외부 알림 서비스
→ 사용자</code></pre>
<hr>
<h1 id="6-안정성과-중복-처리">6. 안정성과 중복 처리</h1>
<h2 id="데이터-손실-방지">데이터 손실 방지</h2>
<p>책의 설계 요구사항에서는 다음과 같이 가정함.</p>
<blockquote>
<p>알림은 지연되거나 순서가 달라질 수는 있지만 시스템 내부에서 소실되어서는 안 됨.</p>
</blockquote>
<p>이를 위해 알림 데이터를 지속적으로 저장하고 전송 실패 시 재시도해야 함.</p>
<p>한 가지 방법은 <strong>알림 로그 DB</strong>를 유지하는 것임.</p>
<pre><code class="language-text">알림 이벤트 생성
→ 로그 또는 지속 가능한 큐에 저장
→ 전송 시도
→ 결과 저장</code></pre>
<p>전송에 실패하면 저장된 데이터를 이용해 다시 시도할 수 있음.</p>
<h2 id="알림-중복-전송-방지">알림 중복 전송 방지</h2>
<p>분산 시스템에서는 재시도로 인해 동일 이벤트가 여러 번 처리될 수 있음.</p>
<p>따라서 각 이벤트에 고유한 <code>event_id</code>를 부여할 수 있음.</p>
<pre><code class="language-text">event_id = 8237471</code></pre>
<p>Worker가 이벤트를 받으면:</p>
<pre><code class="language-text">event_id 확인
       ↓
이미 처리했는가?
 ├─ YES → 폐기
 └─ NO  → 알림 전송</code></pre>
<p>이를 통해 중복 발송 빈도를 줄일 수 있음.</p>
<hr>
<h1 id="7-알림-시스템의-부가-기능">7. 알림 시스템의 부가 기능</h1>
<h2 id="알림-템플릿">알림 템플릿</h2>
<p>매번 알림 문구를 새로 작성하는 대신 미리 형식을 정의해둘 수 있음.</p>
<p>예:</p>
<pre><code class="language-text">[배송 알림]

{사용자명}님의 상품이 배송을 시작했습니다.
송장번호: {송장번호}</code></pre>
<p>장점:</p>
<ul>
<li>형식의 일관성 유지</li>
<li>오류 감소</li>
<li>작성 시간 단축</li>
<li>다국어 지원 용이</li>
<li>추적 링크와 스타일 일괄 적용 가능</li>
</ul>
<h2 id="알림-설정">알림 설정</h2>
<p>사용자가 너무 많은 알림을 받으면 알림 기능 자체를 꺼버릴 수 있음.</p>
<p>따라서 사용자가 종류별로 알림을 조정할 수 있어야 함.</p>
<pre><code class="language-text">마케팅 알림   OFF
배송 알림     ON
결제 알림     ON
이벤트 알림   OFF</code></pre>
<p>알림을 보내기 전에 사용자 설정을 확인해야 함.</p>
<h2 id="전송률-제한">전송률 제한</h2>
<p>한 사용자에게 지나치게 많은 알림을 보내지 않도록 제한함.</p>
<p>예:</p>
<pre><code class="language-text">마케팅 Push
→ 사용자당 하루 최대 3건</code></pre>
<p>이를 통해:</p>
<ul>
<li>알림 피로 감소</li>
<li>스팸 방지</li>
<li>외부 서비스 비용 절감</li>
</ul>
<p>등의 효과를 얻을 수 있음.</p>
<h2 id="재시도-메커니즘">재시도 메커니즘</h2>
<p>전송 실패 시 알림을 재시도 전용 큐로 이동시킴.</p>
<pre><code class="language-text">전송 실패
→ Retry Queue
→ 재시도</code></pre>
<p>계속 실패할 경우 오류를 기록하고 운영자나 개발자에게 알림을 보낼 수 있음.</p>
<p>재시도 횟수와 재시도 간격도 제한해야 함.</p>
<h2 id="푸시-알림-보안">푸시 알림 보안</h2>
<p>인증되지 않은 클라이언트가 임의로 알림을 보낼 수 없어야 함.</p>
<p>APNs와 FCM은 각각 별도의 인증 방식을 사용함.</p>
<ul>
<li><p>APNs</p>
<ul>
<li>APNs Signing Key를 이용한 JWT Provider Token</li>
<li>인증서 방식도 사용 가능</li>
</ul>
</li>
<li><p>FCM</p>
<ul>
<li>서비스 계정 기반 OAuth 2.0 Access Token 사용</li>
</ul>
</li>
</ul>
<p>즉 단순한 <code>appKey + appSecret</code>으로 일반화하기보다 플랫폼별 인증 체계를 사용한다고 보는 것이 적절함.</p>
<hr>
<h1 id="8-모니터링과-이벤트-추적">8. 모니터링과 이벤트 추적</h1>
<h2 id="큐-모니터링">큐 모니터링</h2>
<p>알림 시스템에서 중요한 지표 중 하나는 메시지 큐에 쌓인 알림의 수임.</p>
<pre><code class="language-text">Queue Size ↑</code></pre>
<p>큐가 계속 증가한다면:</p>
<pre><code class="language-text">알림 생성 속도
&gt;
Worker 처리 속도</code></pre>
<p>일 가능성이 있음.</p>
<p>이 경우 다음을 확인해야 함.</p>
<ul>
<li>Worker 수가 충분한가</li>
<li>외부 서비스가 느려졌는가</li>
<li>오류율이 증가했는가</li>
<li>큐 처리 속도가 떨어졌는가</li>
</ul>
<p>필요하면 Worker를 증설할 수 있음.</p>
<h2 id="이벤트-추적">이벤트 추적</h2>
<p>다음 지표를 수집하면 알림의 효과를 분석할 수 있음.</p>
<ul>
<li>전달 성공률</li>
<li>확인율</li>
<li>클릭률</li>
<li>앱 실행 전환율</li>
<li>구매 전환율</li>
</ul>
<p>예:</p>
<pre><code class="language-text">Push 발송
→ 알림 확인
→ 클릭
→ 앱 실행
→ 구매</code></pre>
<p>이벤트 추적 데이터는 사용자 행동과 알림 품질을 개선하는 데 활용할 수 있음.</p>
<hr>
<h1 id="9-분산-시스템에서-exactly-once가-어려운-이유">9. 분산 시스템에서 Exactly-once가 어려운 이유</h1>
<p>책에서는 이 부분의 참고 자료로 다음 글을 제시함.</p>
<p><strong>Tyler Treat, <em>You Cannot Have Exactly-Once Delivery</em></strong></p>
<p>핵심 내용은 네트워크로 분리된 두 컴포넌트 사이에서 메시지가 정확히 한 번 전달됐다는 사실을 완벽하게 확인하기 어렵다는 것임.</p>
<p>예를 들어 서버 A가 서버 B에게 메시지를 보냈는데 ACK를 받지 못했다고 가정함.</p>
<p>가능한 경우:</p>
<pre><code class="language-text">1. 메시지가 네트워크에서 유실됨
2. B가 메시지를 받았지만 처리 전에 장애 발생
3. B가 메시지를 처리 중이지만 매우 느림
4. B가 처리를 완료함
5. B가 ACK를 보냈지만 ACK가 유실됨</code></pre>
<p>A는 ACK를 받지 못했다는 사실만으로 B가 메시지를 처리했는지 알 수 없음.</p>
<p>블로그에서는 이러한 분산 시스템의 불확실성을 설명하기 위해 Two Generals Problem, Byzantine Generals Problem, FLP 결과 등을 함께 언급함.</p>
<p>FLP는 exactly-once delivery 자체에 대한 직접적인 증명이라기보다는 비동기 분산 시스템에서 장애와 합의가 갖는 근본적인 한계를 설명하는 이론적 배경으로 볼 수 있음.</p>
<h2 id="at-most-once">At-most-once</h2>
<p>최대 한 번 전달하는 방식.</p>
<p>블로그의 단순한 예에서는 메시지를 받은 뒤 처리 전에 ACK를 보냄.</p>
<pre><code class="language-text">메시지 수신
→ ACK
→ 처리</code></pre>
<p>ACK 이후 처리 중 장애가 발생하면 Sender는 이미 성공했다고 생각하므로 재전송하지 않음.</p>
<p>따라서:</p>
<pre><code class="language-text">At-most-once
→ 중복 가능성 낮음
→ 메시지 유실 가능</code></pre>
<p>알림 서비스라면 사용자가 받아야 할 알림이 사라질 수 있음.</p>
<h2 id="at-least-once">At-least-once</h2>
<p>최소 한 번 이상 전달하는 방식.</p>
<p>메시지를 처리한 뒤 ACK를 보냄.</p>
<pre><code class="language-text">메시지 수신
→ 처리
→ ACK</code></pre>
<p>하지만 처리는 완료됐는데 ACK가 유실되면 Sender는 실패로 판단하고 다시 메시지를 보냄.</p>
<pre><code class="language-text">처리 완료
→ ACK 유실
→ 재전송
→ 동일 메시지 다시 처리</code></pre>
<p>따라서:</p>
<pre><code class="language-text">At-least-once
→ 메시지 유실 가능성을 줄임
→ 중복 처리 가능</code></pre>
<p>알림 서비스라면 동일한 알림이 두 번 전달될 수 있음.</p>
<h2 id="exactly-once와-실무적인-해결-방법">Exactly-once와 실무적인 해결 방법</h2>
<p>이상적인 목표는:</p>
<pre><code class="language-text">메시지 유실 없음
+
중복 없음
=
Exactly-once</code></pre>
<p>이지만 네트워크에서 메시지나 ACK가 유실될 수 있으므로 네트워크 수준에서 exactly-once delivery를 완벽하게 보장하기 어려움.</p>
<p>따라서 실무에서는 중복 가능성을 받아들이고 애플리케이션 계층에서 이를 흡수함.</p>
<p>대표적인 방법은 두 가지임.</p>
<h3 id="멱등성idempotency">멱등성(Idempotency)</h3>
<p>같은 연산을 여러 번 수행해도 최종 결과가 같도록 설계함.</p>
<pre><code class="language-text">상태 = 활성화</code></pre>
<p>를 여러 번 수행해도 최종 상태는 동일함.</p>
<h3 id="중복-제거deduplication">중복 제거(Deduplication)</h3>
<p>각 메시지에 고유한 Event ID를 부여하고 처리 이력을 확인함.</p>
<pre><code class="language-text">event_id = abc123</code></pre>
<p>처음 들어온 경우:</p>
<pre><code class="language-text">처리 이력 없음
→ 처리
→ 완료 기록</code></pre>
<p>같은 이벤트가 다시 들어온 경우:</p>
<pre><code class="language-text">이미 처리됨
→ 폐기</code></pre>
<p>알림 발송은 같은 함수를 두 번 실행하면 실제 알림도 두 번 나갈 수 있으므로 자연스럽게 멱등한 연산이 아님.</p>
<p>따라서 알림 시스템에서는 Event ID 기반 중복 제거가 특히 중요함.</p>
<p>전체 흐름은 다음과 같음.</p>
<pre><code class="language-text">At-least-once
       ↓
중복 가능
       ↓
Event ID
       ↓
Deduplication 또는 Idempotency
       ↓
중복으로 인한 부작용 방지</code></pre>
<p>즉 분산 알림 시스템에서는 중복 전달 자체를 완전히 없애려 하기보다, 재전송을 허용하고 Event ID와 중복 제거 또는 멱등 처리를 이용해 중복으로 인한 문제를 흡수하는 방식이 현실적임.</p>
<hr>
<h1 id="10-최종-설계-정리">10. 최종 설계 정리</h1>
<p>최종적인 알림 시스템 구조는 다음과 같음.
<img src="https://velog.velcdn.com/images/dus_snp/post/7594da53-9a61-4500-886b-d6fbbc88c8f5/image.png" alt=""></p>
<p>추가적으로 다음 기능을 포함함.</p>
<ul>
<li>Notification Log</li>
<li>Retry Queue</li>
<li>Rate Limiter</li>
<li>알림 Template</li>
<li>User Preference</li>
<li>Deduplication</li>
<li>Monitoring</li>
<li>Event Tracking</li>
</ul>
<p>핵심은 단순히 알림을 보내는 것에 그치지 않고 다음 사항을 함께 고려하는 것임.</p>
<ul>
<li>알림의 유실 방지</li>
<li>중복 발송 억제</li>
<li>대량 요청 처리</li>
<li>외부 서비스 장애 대응</li>
<li>사용자별 알림 설정</li>
<li>전송률 제한</li>
<li>시스템 모니터링</li>
<li>알림 효과 분석</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 9장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-9%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-9%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sat, 08 Aug 2026 10:05:55 GMT</pubDate>
            <description><![CDATA[<h1 id="웹-크롤러-설계">웹 크롤러 설계</h1>
<h2 id="1-웹-크롤러란">1. 웹 크롤러란</h2>
<p>웹 크롤러는 웹에 새로 올라오거나 갱신된 콘텐츠를 찾아내고 수집하는 시스템임.</p>
<p>수집 대상은 다음과 같이 다양할 수 있음.</p>
<ul>
<li>웹 페이지</li>
<li>이미지</li>
<li>비디오</li>
<li>PDF</li>
<li>기타 웹 문서</li>
</ul>
<p>대표적인 활용 사례는 다음과 같음.</p>
<h3 id="검색-엔진-인덱싱">검색 엔진 인덱싱</h3>
<ul>
<li>크롤러는 웹 페이지를 발견하고 수집하여 검색 엔진의 인덱싱 시스템에 전달함</li>
<li>대표적인 예로 Google Search에서 사용하는 웹 크롤러인 Googlebot이 있음</li>
</ul>
<h3 id="웹-아카이빙">웹 아카이빙</h3>
<ul>
<li><p>웹에 존재하는 정보를 장기간 보관하기 위해 수집하는 작업</p>
</li>
<li><p>많은 국립도서관과 기관에서 웹사이트를 주기적으로 수집하여 보존함</p>
</li>
<li><p>예시</p>
<ul>
<li>미국 의회도서관</li>
<li>EU 웹 아카이브</li>
</ul>
</li>
</ul>
<h3 id="웹-마이닝">웹 마이닝</h3>
<ul>
<li>웹에서 대량의 데이터를 수집한 뒤 유용한 정보를 추출하는 작업</li>
<li>예를 들어 금융 기업이 기업의 연차 보고서나 주주총회 자료를 수집해 기업의 사업 방향을 분석할 수 있음</li>
</ul>
<h3 id="웹-모니터링">웹 모니터링</h3>
<ul>
<li>웹에서 특정 정보나 콘텐츠가 새롭게 등장하는지 지속적으로 감시하는 용도</li>
<li>저작권 또는 상표권 침해 사례를 탐지하는 데 활용 가능</li>
<li>예를 들어 웹에서 불법 복제 콘텐츠를 탐지하는 시스템에 crawler를 사용할 수 있음</li>
</ul>
<hr>
<h1 id="2-웹-크롤러-설계-시-고려-사항">2. 웹 크롤러 설계 시 고려 사항</h1>
<p>웹 크롤러의 복잡도는 수집해야 하는 데이터의 규모에 따라 크게 달라짐.</p>
<p>따라서 설계를 시작하기 전에 다음 내용을 명확하게 해야 함.</p>
<ul>
<li>얼마나 많은 페이지를 수집해야 하는가</li>
<li>어떤 종류의 콘텐츠를 수집해야 하는가</li>
<li>얼마나 자주 다시 방문해야 하는가</li>
<li>수집한 데이터를 얼마나 오래 저장해야 하는가</li>
<li>특정 사이트에 얼마나 자주 요청할 수 있는가</li>
</ul>
<p>대규모 웹 크롤러가 만족해야 할 주요 속성은 다음과 같음.</p>
<h3 id="규모-확장성">규모 확장성</h3>
<ul>
<li>웹은 매우 크므로 한 서버만으로 모든 페이지를 수집하기 어려움</li>
<li>여러 서버와 여러 작업 스레드를 사용하여 병렬로 처리할 필요가 있음</li>
</ul>
<h3 id="안정성">안정성</h3>
<p>웹에는 다음과 같은 비정상적인 상황이 존재할 수 있음.</p>
<ul>
<li>잘못 작성된 웹 페이지</li>
<li>응답하지 않는 서버</li>
<li>매우 느린 서버</li>
<li>악성 콘텐츠</li>
<li>무한히 생성되는 URL</li>
</ul>
<p>따라서 특정 페이지의 문제가 전체 크롤러 시스템 장애로 이어지지 않도록 해야 함.</p>
<h3 id="예절">예절</h3>
<ul>
<li>특정 웹사이트에 짧은 시간 동안 지나치게 많은 요청을 보내서는 안 됨</li>
<li>대상 사이트의 <code>robots.txt</code> 정책과 요청 속도 제한을 고려해야 함</li>
</ul>
<h3 id="확장성">확장성</h3>
<ul>
<li>새로운 콘텐츠 형식을 쉽게 추가할 수 있어야 함</li>
<li>이미지, PDF, 동영상 등 새로운 콘텐츠를 지원할 때 전체 시스템을 다시 설계하지 않아도 되는 구조가 바람직함</li>
</ul>
<hr>
<h1 id="3-기본-크롤링-알고리즘">3. 기본 크롤링 알고리즘</h1>
<p>웹 크롤러의 기본적인 동작 방식은 다음과 같음.</p>
<ol>
<li>시작 URL 집합을 입력받음</li>
<li>해당 URL이 가리키는 웹 페이지를 다운로드</li>
<li>다운로드한 페이지에서 새로운 URL 추출</li>
<li>새롭게 발견한 URL을 다운로드할 URL 목록에 추가</li>
<li>위 과정을 반복</li>
</ol>
<p>개념적으로는 다음과 같음.</p>
<pre><code class="language-text">시작 URL
→ 페이지 다운로드
→ URL 추출
→ 새로운 URL 저장
→ 다시 다운로드
→ 반복</code></pre>
<p>하지만 실제 웹은 매우 크기 때문에 단순히 이 알고리즘만 반복하는 것으로는 대규모 크롤러를 구현하기 어려움.</p>
<hr>
<h1 id="4-요구사항">4. 요구사항</h1>
<p>다음 규모를 처리하는 웹 크롤러를 설계한다고 가정함.</p>
<h3 id="페이지-다운로드-수">페이지 다운로드 수</h3>
<ul>
<li>매달 10억 개의 웹 페이지 다운로드</li>
</ul>
<p>평균 QPS:</p>
<pre><code class="language-text">10억 ÷ 30일 ÷ 24시간 ÷ 3,600초
≈ 386페이지/초
≈ 약 400 QPS</code></pre>
<p>Peak QPS를 평균의 2배로 가정:</p>
<pre><code class="language-text">400 × 2
= 약 800 QPS</code></pre>
<h3 id="저장-용량">저장 용량</h3>
<p>웹 페이지 평균 크기를 500KB라고 가정함.</p>
<pre><code class="language-text">10억 페이지 × 500KB
≈ 500TB/월</code></pre>
<p>5년 동안 데이터를 보관한다고 가정하면:</p>
<pre><code class="language-text">500TB × 12개월 × 5년
= 30PB</code></pre>
<p>따라서 약 30PB 규모의 저장 공간이 필요함.</p>
<hr>
<h1 id="5-개략적인-시스템-구성">5. 개략적인 시스템 구성</h1>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/e5b45bd2-f186-4881-bc17-8fed6529ca56/image.png" alt=""></p>
<p>대규모 웹 크롤러는 다음과 같은 주요 컴포넌트로 구성할 수 있음.</p>
<ul>
<li>시작 URL 집합</li>
<li>미수집 URL 저장소</li>
<li>HTML 다운로더</li>
<li>DNS 변환기</li>
<li>콘텐츠 파서</li>
<li>중복 콘텐츠 탐지</li>
<li>콘텐츠 저장소</li>
<li>URL 추출기</li>
<li>URL 필터</li>
<li>방문 URL 관리</li>
<li>URL 저장소</li>
</ul>
<hr>
<h1 id="6-시작-url-집합">6. 시작 URL 집합</h1>
<p>시작 URL은 웹 크롤러가 크롤링을 시작하는 출발점임.</p>
<p>전체 웹을 크롤링하려면 가능한 많은 페이지로 연결될 수 있는 URL을 선택하는 것이 중요함.</p>
<p>예를 들어 다음과 같은 기준으로 시작 URL을 구성할 수 있음.</p>
<ul>
<li>국가별 주요 웹사이트</li>
<li>카테고리별 주요 웹사이트</li>
<li>인기 웹사이트</li>
<li>기존에 알고 있는 도메인 목록</li>
</ul>
<p>시작 URL을 선택하는 절대적인 정답은 없으며 크롤러의 목적에 따라 달라짐.</p>
<hr>
<h1 id="7-미수집-url-저장소">7. 미수집 URL 저장소</h1>
<p>미수집 URL 저장소는 아직 다운로드하지 않은 URL을 관리하는 컴포넌트임.</p>
<p>이를 일반적으로 <code>URL Frontier</code>라고 부름.</p>
<p>기본적인 BFS 구조에서는 FIFO 큐처럼 볼 수 있음.</p>
<pre><code class="language-text">URL 발견
→ URL Frontier 저장
→ 순서대로 다운로드</code></pre>
<p>하지만 실제 대규모 크롤러에서는 단순 FIFO 큐 하나만 사용하지 않음.</p>
<p>다음 기능이 추가로 필요하기 때문임.</p>
<ul>
<li>URL 우선순위</li>
<li>웹사이트별 요청 속도 제한</li>
<li>재수집 시점 관리</li>
<li>크롤러 예절 유지</li>
</ul>
<p>따라서 실제 URL Frontier는 여러 개의 큐와 스케줄링 로직으로 구성됨.</p>
<hr>
<h1 id="8-html-다운로더">8. HTML 다운로더</h1>
<p>HTML 다운로더는 인터넷에서 실제 웹 페이지를 다운로드하는 컴포넌트임.</p>
<p>다운로드할 URL은 URL Frontier에서 전달받음.</p>
<pre><code class="language-text">URL Frontier
→ HTML Downloader
→ 웹 서버에 HTTP 요청
→ 웹 페이지 다운로드</code></pre>
<hr>
<h1 id="9-도메인-이름-변환기">9. 도메인 이름 변환기</h1>
<p>웹 페이지를 다운로드하려면 URL의 도메인 이름을 IP 주소로 변환해야 함.</p>
<p>예:</p>
<pre><code class="language-text">www.example.com
→ DNS
→ 93.184.216.34</code></pre>
<p>HTML 다운로더는 DNS 시스템을 사용하여 대상 서버의 IP 주소를 알아냄.</p>
<hr>
<h1 id="10-콘텐츠-파서">10. 콘텐츠 파서</h1>
<p>웹 페이지를 다운로드한 후에는 페이지를 파싱하고 검증해야 함.</p>
<p>웹에는 다음과 같은 비정상적인 콘텐츠가 존재할 수 있음.</p>
<ul>
<li>잘못된 HTML</li>
<li>지나치게 큰 문서</li>
<li>악성 콘텐츠</li>
<li>파싱할 수 없는 데이터</li>
</ul>
<p>다운로드와 파싱은 작업 특성이 다르므로 별도 컴포넌트로 분리하면 다음 장점이 있음.</p>
<ul>
<li>다운로드와 파싱을 독립적으로 확장 가능</li>
<li>파서 장애가 다운로더 전체에 영향을 주는 것을 줄일 수 있음</li>
<li>네트워크 중심 작업과 CPU 중심 작업 분리 가능</li>
</ul>
<hr>
<h1 id="11-중복-콘텐츠-탐지">11. 중복 콘텐츠 탐지</h1>
<p>웹에는 동일하거나 매우 유사한 콘텐츠가 여러 URL에 존재하는 경우가 많음.</p>
<p>연구에 따라 상당한 비율의 웹 페이지가 중복 콘텐츠일 수 있음.</p>
<p>동일한 콘텐츠를 반복해서 저장하면 다음 문제가 발생함.</p>
<ul>
<li>저장 공간 낭비</li>
<li>네트워크 사용량 증가</li>
<li>인덱싱 비용 증가</li>
</ul>
<p>이를 방지하기 위해 페이지 콘텐츠의 해시값이나 체크섬을 사용할 수 있음.</p>
<pre><code class="language-text">웹 페이지 콘텐츠
→ Hash
→ 콘텐츠 해시값</code></pre>
<p>이미 같은 해시값이 존재한다면 중복 콘텐츠로 판단할 수 있음.</p>
<hr>
<h1 id="12-콘텐츠-저장소">12. 콘텐츠 저장소</h1>
<p>다운로드한 HTML 문서를 장기간 보관하는 저장소임.</p>
<p>저장소 기술을 선택할 때는 다음 요소를 고려해야 함.</p>
<ul>
<li>데이터 크기</li>
<li>데이터 형식</li>
<li>접근 빈도</li>
<li>데이터 보관 기간</li>
</ul>
<p>대규모 시스템에서는 디스크와 메모리를 함께 활용할 수 있음.</p>
<ul>
<li>자주 접근하는 데이터 → 메모리 또는 캐시</li>
<li>대량의 원본 데이터 → 디스크 또는 객체 저장소</li>
</ul>
<hr>
<h1 id="13-url-추출기">13. URL 추출기</h1>
<p>다운로드한 HTML 페이지를 분석하여 새로운 링크를 추출하는 역할을 함.</p>
<p>예를 들어 다음 HTML이 있다고 가정함.</p>
<pre><code class="language-html">&lt;a href=&quot;/news/1&quot;&gt;뉴스&lt;/a&gt;</code></pre>
<p>현재 페이지가 다음과 같다면:</p>
<pre><code class="language-text">https://example.com</code></pre>
<p>상대 경로 <code>/news/1</code>을 절대 경로로 변환함.</p>
<pre><code class="language-text">https://example.com/news/1</code></pre>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/6f762370-a652-4230-a1e0-707a5126f605/image.png" alt=""></p>
<hr>
<h1 id="14-url-필터">14. URL 필터</h1>
<p>URL 필터는 크롤링할 필요가 없는 URL을 제거하는 역할을 함.</p>
<p>예를 들어 다음 URL을 제외할 수 있음.</p>
<ul>
<li>특정 콘텐츠 타입</li>
<li>특정 파일 확장자</li>
<li>접속 시 오류가 발생하는 URL</li>
<li>접근 제외 목록에 포함된 URL</li>
<li>크롤러 목적과 관계없는 URL</li>
</ul>
<p>특정 콘텐츠 타입이나 파일 확장자를 제외하는 이유는 크롤러의 목적과 관계없는 파일에 시간, 네트워크, 저장 공간을 낭비하지 않기 위해서임.</p>
<p>예를 들어 일반적인 HTML 중심 크롤러라면 다음 파일은 제외할 수 있음.</p>
<pre><code class="language-text">.jpg
.mp4
.zip
.exe</code></pre>
<p>이미지나 동영상은 파일 크기가 크고 HTML 링크 분석 대상이 아닐 수 있음.</p>
<p>또한 HTML 파서는 이미지나 실행 파일을 직접 분석할 수 없음.</p>
<p>다만 크롤러의 목적이 이미지 검색이나 동영상 검색이라면 이러한 콘텐츠도 수집 대상으로 포함할 수 있음.</p>
<hr>
<h1 id="15-이미-방문한-url-관리">15. 이미 방문한 URL 관리</h1>
<p>새 URL을 발견할 때마다 이미 발견하거나 방문한 URL인지 확인해야 함.</p>
<pre><code class="language-text">새로운 URL 발견
→ 이미 본 URL인지 확인
→ 처음 보는 URL이면 URL Frontier에 추가
→ 이미 처리한 URL이면 제외</code></pre>
<p>중복 URL을 제거하지 않으면 다음 문제가 발생할 수 있음.</p>
<ul>
<li>동일 페이지 반복 다운로드</li>
<li>대상 서버 부하 증가</li>
<li>네트워크 자원 낭비</li>
<li>크롤러가 순환 링크에 의해 무한 반복할 가능성</li>
</ul>
<p>방문 여부를 추적하기 위한 자료구조로 다음을 사용할 수 있음.</p>
<ul>
<li>해시 테이블</li>
<li>블룸 필터</li>
</ul>
<hr>
<h1 id="16-url-저장소">16. URL 저장소</h1>
<p>이미 방문하거나 발견한 URL을 저장하는 시스템임.</p>
<p>대규모 검색 엔진 크롤러에서는 URL 수가 수억~수십억 개 이상이 될 수 있으므로 모든 URL을 메모리에만 저장하기 어려움.</p>
<p>따라서 지속적인 저장소와 메모리를 함께 사용하는 방식을 고려할 수 있음.</p>
<hr>
<h1 id="17-dfs와-bfs">17. DFS와 BFS</h1>
<p>웹은 그래프로 표현할 수 있음.</p>
<pre><code class="language-text">웹 페이지 = 노드
하이퍼링크 = 간선</code></pre>
<p>따라서 웹 크롤링은 웹 그래프의 간선을 따라 이동하면서 노드를 방문하는 과정으로 볼 수 있음.</p>
<p>대표적인 그래프 탐색 방법은 다음과 같음.</p>
<ul>
<li>DFS</li>
<li>BFS</li>
</ul>
<h2 id="dfs">DFS</h2>
<p>DFS는 특정 경로를 계속 깊게 탐색함.</p>
<p>웹은 사실상 깊이를 예측하기 어려울 정도로 거대한 그래프이므로 특정 사이트나 경로만 지나치게 깊게 탐색할 수 있음.</p>
<p>따라서 일반적인 웹 크롤러에는 적합하지 않음.</p>
<h2 id="bfs">BFS</h2>
<p>BFS는 가까운 페이지부터 넓게 탐색함.</p>
<p>FIFO 큐를 사용하여 구현할 수 있으므로 웹 크롤러에서 기본적인 접근 방식으로 많이 사용됨.</p>
<p>하지만 단순 BFS에도 문제가 있음.</p>
<hr>
<h1 id="18-단순-bfs의-문제점">18. 단순 BFS의 문제점</h1>
<h2 id="문제-1-특정-서버에-요청-집중">문제 1. 특정 서버에 요청 집중</h2>
<p>한 페이지에서 추출한 많은 URL이 같은 호스트를 가리킬 수 있음.</p>
<p>예:</p>
<pre><code class="language-text">example.com
 ├─ /news/1
 ├─ /news/2
 ├─ /news/3
 ├─ /news/4
 └─ /news/5</code></pre>
<p>이 URL들을 병렬로 동시에 다운로드하면:</p>
<pre><code class="language-text">example.com
← 다수의 HTTP 요청</code></pre>
<p>특정 웹 서버에 요청이 집중되어 과부하를 발생시킬 수 있음.</p>
<p>이런 크롤러는 예의 없는 크롤러로 간주될 수 있음.</p>
<h2 id="문제-2-url-우선순위가-없음">문제 2. URL 우선순위가 없음</h2>
<p>일반적인 BFS는 모든 URL을 동일한 우선순위로 처리함.</p>
<p>하지만 실제 웹 페이지의 중요도는 서로 다름.</p>
<p>예를 들어 다음 요소를 고려할 수 있음.</p>
<ul>
<li>PageRank</li>
<li>사용자 트래픽</li>
<li>페이지 갱신 빈도</li>
<li>사이트 신뢰도</li>
<li>콘텐츠 중요도</li>
</ul>
<p>따라서 중요한 페이지를 우선적으로 크롤링할 수 있는 시스템이 필요함.</p>
<hr>
<h1 id="19-예의-바른-크롤러">19. 예의 바른 크롤러</h1>
<p>예의 바른 크롤러는 동일 웹사이트에 대해서 한 번에 한 페이지만 요청하는 원칙을 지켜야 함.</p>
<p>같은 웹사이트의 페이지를 다운로드하는 작업은 시간차를 두고 수행하도록 구성할 수 있음.</p>
<p>이를 위해 웹사이트의 호스트명과 다운로드 작업을 수행하는 작업 스레드 사이의 관계를 유지함.</p>
<p>각 작업 스레드는 별도의 FIFO 큐에서 URL을 가져와 처리함.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/1a42e093-8ce9-44a4-9e28-54a0185d60c5/image.png" alt=""></p>
<p>주요 컴포넌트는 다음과 같음.</p>
<h3 id="큐-라우터">큐 라우터</h3>
<ul>
<li>동일한 호스트에 속하는 URL이 항상 같은 큐로 전달되도록 함</li>
</ul>
<p>예:</p>
<pre><code class="language-text">example.com/a ─┐
example.com/b ─┼→ Queue 1
example.com/c ─┘</code></pre>
<h3 id="매핑-테이블">매핑 테이블</h3>
<p>호스트 이름과 큐의 관계를 저장함.</p>
<pre><code class="language-text">example.com → Queue 1
google.com  → Queue 2</code></pre>
<h3 id="fifo-큐">FIFO 큐</h3>
<ul>
<li>같은 호스트의 URL을 저장</li>
<li>큐 내부에서는 순차적으로 URL 처리</li>
</ul>
<h3 id="큐-선택기">큐 선택기</h3>
<ul>
<li>여러 큐를 순회</li>
<li>처리할 URL을 선택</li>
<li>해당 URL을 담당 작업 스레드에 전달</li>
</ul>
<h3 id="작업-스레드">작업 스레드</h3>
<ul>
<li>전달받은 URL 다운로드</li>
<li>동일 호스트에 대한 요청은 순차적으로 처리</li>
<li>요청 사이에 일정한 시간 간격을 둘 수 있음</li>
</ul>
<hr>
<h1 id="20-url-우선순위">20. URL 우선순위</h1>
<p>웹에 존재하는 모든 페이지가 동일한 중요도를 가지는 것은 아님.</p>
<p>크롤러는 가치가 높은 페이지를 먼저 수집하는 것이 효율적임.</p>
<p>URL의 우선순위를 결정할 때 다음 요소를 사용할 수 있음.</p>
<ul>
<li>PageRank</li>
<li>사용자 트래픽</li>
<li>페이지 갱신 빈도</li>
<li>사이트 신뢰도</li>
</ul>
<p>우선순위 처리는 다음 컴포넌트로 구성할 수 있음.</p>
<h3 id="순위-결정-장치">순위 결정 장치</h3>
<ul>
<li>URL을 입력받음</li>
<li>URL의 우선순위를 계산</li>
</ul>
<h3 id="우선순위-큐">우선순위 큐</h3>
<p>우선순위별로 URL을 별도의 큐에 저장함.</p>
<pre><code class="language-text">높은 우선순위 Queue
중간 우선순위 Queue
낮은 우선순위 Queue</code></pre>
<h3 id="큐-선택기-1">큐 선택기</h3>
<ul>
<li>높은 우선순위 큐에서 URL을 더 자주 선택</li>
<li>낮은 우선순위 URL도 완전히 굶지 않도록 확률적으로 선택 가능</li>
</ul>
<hr>
<h1 id="21-전면-큐와-후면-큐">21. 전면 큐와 후면 큐</h1>
<p>URL Frontier는 크게 다음 두 역할로 나눌 수 있음.</p>
<h3 id="전면-큐">전면 큐</h3>
<ul>
<li>URL의 우선순위 관리</li>
<li>어떤 페이지를 먼저 크롤링할 것인지 결정</li>
</ul>
<h3 id="후면-큐">후면 큐</h3>
<ul>
<li>크롤링 예절 관리</li>
<li>동일 호스트에 대한 요청 속도 제한</li>
<li>호스트별 URL 큐 관리</li>
</ul>
<p>정리하면:</p>
<pre><code class="language-text">새로운 URL
→ 전면 큐
→ 우선순위 결정
→ 후면 큐
→ 호스트별 속도 제어
→ Downloader</code></pre>
<hr>
<h1 id="22-신선도">22. 신선도</h1>
<p>웹 페이지는 지속적으로 변경됨.</p>
<ul>
<li>새로운 페이지 생성</li>
<li>기존 페이지 수정</li>
<li>페이지 삭제</li>
</ul>
<p>따라서 한 번 다운로드한 페이지도 일정 시간이 지나면 다시 수집할 필요가 있음.</p>
<p>하지만 모든 URL을 동일한 주기로 다시 크롤링하면 막대한 자원이 필요함.</p>
<p>따라서 다음 정보를 이용하여 재수집 주기를 결정할 수 있음.</p>
<ul>
<li>과거 페이지 변경 빈도</li>
<li>페이지 중요도</li>
<li>마지막 변경 시각</li>
<li>사이트 특성</li>
</ul>
<p>자주 변경되는 중요한 페이지는 자주 방문하고, 거의 변경되지 않는 페이지는 더 긴 주기로 방문하도록 최적화할 수 있음.</p>
<hr>
<h1 id="23-url-frontier-저장-구조">23. URL Frontier 저장 구조</h1>
<p>검색 엔진 수준의 크롤러는 처리해야 하는 URL 수가 매우 많음.</p>
<p>모든 URL을 메모리에 저장하면:</p>
<ul>
<li>메모리 비용 증가</li>
<li>장애 발생 시 상태 손실 가능</li>
<li>규모 확장 어려움</li>
</ul>
<p>반대로 모든 데이터를 디스크에서 직접 처리하면 디스크 I/O가 병목이 될 수 있음.</p>
<p>따라서 메모리와 디스크를 함께 사용하는 하이브리드 방식이 적절함.</p>
<pre><code class="language-text">대부분의 URL
→ 디스크

곧 처리할 URL
→ 메모리 버퍼</code></pre>
<p>메모리 버퍼의 데이터는 주기적으로 지속 저장장치에 기록함.</p>
<hr>
<h1 id="24-robotstxt">24. robots.txt</h1>
<p><code>robots.txt</code>는 로봇 제외 프로토콜이라고도 하며, 웹사이트 운영자가 crawler와 소통하기 위한 표준적인 방법임.</p>
<p>사이트 운영자는 crawler별로 특정 URL 경로에 대한 접근 허용 또는 제외 규칙을 정의할 수 있음.</p>
<p>예:</p>
<pre><code class="language-text">User-agent: *
Disallow: /admin/
Allow: /</code></pre>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/e761918a-15cc-4e6e-9b3e-40ab57c21b25/image.png" alt=""></p>
<p>크롤러는 사이트를 수집하기 전에 해당 사이트의 <code>robots.txt</code>를 확인하고 자신의 User-Agent에 적용되는 규칙을 확인해야 함.</p>
<p>다만 <code>robots.txt</code>는 보안 시스템이 아님.</p>
<p>즉 사이트에 대한 실제 접근 자체를 기술적으로 막는 것이 아니라 crawler에게 정책을 전달하는 규칙임.</p>
<hr>
<h1 id="25-amazon-robotstxt에서-본-crawler별-정책-차이">25. Amazon robots.txt에서 본 crawler별 정책 차이</h1>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/b7ec3818-519d-4a3a-95b1-959a297252dc/image.png" alt=""></p>
<p>2026년 8월 <code>amazon.com/robots.txt</code>를 확인했을 때 crawler의 User-Agent에 따라 서로 다른 접근 정책이 적용되어 있는 것을 확인할 수 있었음.</p>
<p>일반적인 검색 crawler에는 특정 경로만 제외하는 방식으로 설정되어 있는 반면, 일부 AI 서비스 관련 crawler는 더 강한 제한이 적용되어 있을 수 있음.</p>
<p>이 차이가 왜 발생하는지 궁금하여 일반 crawler와 AI 서비스 crawler의 차이를 조사해 봄.</p>
<hr>
<h1 id="26-일반-crawler와-ai-crawler">26. 일반 crawler와 AI crawler</h1>
<p>crawler는 기술적으로 모두 웹 페이지를 자동으로 요청하고 수집하는 프로그램이라는 점에서는 비슷함.</p>
<p>차이는 주로 <strong>수집 목적과 운영 주체</strong>에서 발생함.</p>
<h3 id="일반적인-crawler">일반적인 crawler</h3>
<p>목적에 따라 다음 용도로 사용될 수 있음.</p>
<ul>
<li>검색 엔진 색인</li>
<li>데이터 수집</li>
<li>사이트 분석</li>
<li>웹 아카이빙</li>
<li>가격 비교</li>
</ul>
<p>대표적으로 Googlebot은 검색 엔진 색인을 위해 Google이 운영하는 crawler임.</p>
<h3 id="ai-서비스-관련-crawler">AI 서비스 관련 crawler</h3>
<p>AI 서비스 사업자가 운영하는 crawler 역시 목적에 따라 여러 종류로 구분될 수 있음.</p>
<p>예:</p>
<ul>
<li>모델 학습 또는 개선용 데이터 수집</li>
<li>AI 검색을 위한 웹 페이지 수집</li>
<li>최신 정보 기반 답변 생성을 위한 콘텐츠 접근</li>
</ul>
<p>따라서 모든 AI crawler가 반드시 모델 학습만을 목적으로 하는 것은 아님.</p>
<p>검색 엔진 crawler와 AI crawler의 가장 큰 차이는 crawler의 기본 동작 구조보다는 수집된 데이터를 어디에 사용하는지에 있음.</p>
<hr>
<h1 id="27-robotstxt는-실제-접근-차단-기능인가">27. robots.txt는 실제 접근 차단 기능인가</h1>
<p>robots.txt는 강제적인 접근 통제 시스템이 아님.</p>
<pre><code class="language-text">robots.txt
→ &quot;이 경로는 크롤링하지 말아 달라&quot;는 정책 전달</code></pre>
<p>정상적인 crawler는 이러한 규칙을 준수함.</p>
<p>하지만 악의적인 crawler가 robots.txt를 무시하는 것도 기술적으로 가능함.</p>
<p>실제로 접근을 막으려면 별도의 보안 기술을 사용해야 함.</p>
<p>예:</p>
<ul>
<li>WAF</li>
<li>IP 차단</li>
<li>인증</li>
<li>Rate Limiting</li>
<li>CAPTCHA</li>
</ul>
<p>즉:</p>
<pre><code class="language-text">robots.txt
= crawler 행동 규칙

WAF / 인증 / IP 차단
= 실제 접근 제어</code></pre>
<hr>
<h1 id="28-일반-사용자가-ai-crawler를-사용할-수-있는가">28. 일반 사용자가 AI crawler를 사용할 수 있는가</h1>
<p>GPTBot이나 Googlebot처럼 특정 서비스 사업자가 공식적으로 운영하는 crawler는 일반 사용자가 직접 실행하거나 제어할 수 있는 서비스가 아님.</p>
<p>해당 crawler는 서비스 사업자가 자체 인프라에서 운영함.</p>
<p>예를 들어:</p>
<pre><code class="language-text">Googlebot
→ Google이 직접 운영

GPTBot
→ OpenAI가 직접 운영</code></pre>
<p>따라서 사용자가 명령을 내려 Googlebot이나 GPTBot 자체를 직접 실행시키는 것은 불가능함.</p>
<p>다만 일반 사용자가 직접 웹 crawler를 개발하는 것은 가능함.</p>
<p>예:</p>
<pre><code class="language-text">직접 만든 crawler
→ 웹 페이지 수집
→ AI 모델에 전달
→ 분석 또는 요약</code></pre>
<p>즉 AI와 crawler를 결합한 시스템 자체는 직접 만들 수 있지만, 특정 기업이 운영하는 공식 crawler를 사용하는 것은 별개의 문제임.</p>
<hr>
<h1 id="29-html-다운로더-성능-최적화">29. HTML 다운로더 성능 최적화</h1>
<h2 id="분산-크롤링">분산 크롤링</h2>
<p>크롤링 성능을 높이기 위해 다운로드 작업을 여러 서버에 분산할 수 있음.</p>
<pre><code class="language-text">Crawler Server 1
Crawler Server 2
Crawler Server 3
...</code></pre>
<p>각 서버는 여러 작업 스레드를 사용하여 동시에 페이지를 다운로드할 수 있음.</p>
<hr>
<h2 id="dns-조회-결과-캐싱">DNS 조회 결과 캐싱</h2>
<p>DNS 조회는 네트워크 통신이 필요한 작업이므로 반복적으로 수행하면 오버헤드가 발생함.</p>
<p>따라서 다음 정보를 캐시에 저장할 수 있음.</p>
<pre><code class="language-text">도메인 이름 → IP 주소</code></pre>
<p>예:</p>
<pre><code class="language-text">example.com → 93.184.216.34</code></pre>
<p>동일한 도메인을 다시 방문할 때 DNS 서버에 매번 요청하지 않고 캐시된 결과를 사용할 수 있음.</p>
<p>캐시된 DNS 정보는 TTL이나 별도의 갱신 정책에 따라 주기적으로 갱신해야 함.</p>
<hr>
<h2 id="지역성">지역성</h2>
<p>크롤링 서버를 대상 웹 서버와 지리적으로 가까운 지역에 배치하면 네트워크 지연 시간을 줄일 수 있음.</p>
<p>지역성은 다음 컴포넌트에 활용 가능함.</p>
<ul>
<li>크롤링 서버</li>
<li>캐시</li>
<li>메시지 큐</li>
<li>저장소</li>
</ul>
<hr>
<h2 id="짧은-타임아웃">짧은 타임아웃</h2>
<p>일부 웹 서버는 다음과 같은 문제가 있을 수 있음.</p>
<ul>
<li>응답이 매우 느림</li>
<li>연결은 되었지만 데이터를 보내지 않음</li>
<li>아예 응답하지 않음</li>
</ul>
<p>이러한 서버를 무한정 기다리면 작업 스레드가 계속 점유됨.</p>
<p>따라서 최대 대기 시간을 설정해야 함.</p>
<pre><code class="language-text">응답 시간 &gt; Timeout
→ 요청 중단
→ 실패 처리</code></pre>
<hr>
<h1 id="30-안정성">30. 안정성</h1>
<p>성능뿐 아니라 장애 상황에서도 시스템이 계속 동작하도록 설계해야 함.</p>
<h2 id="안정-해시">안정 해시</h2>
<p>다운로더 서버에 URL을 분산할 때 안정 해시를 사용할 수 있음.</p>
<p>장점:</p>
<ul>
<li>서버 추가 시 일부 URL만 재배치</li>
<li>서버 제거 시 일부 URL만 다른 서버로 이동</li>
<li>서버 변경에 따른 전체 데이터 재배치 감소</li>
</ul>
<hr>
<h2 id="크롤링-상태-저장">크롤링 상태 저장</h2>
<p>장애 발생 후 처음부터 모든 작업을 다시 시작하지 않도록 다음 정보를 지속 저장장치에 기록함.</p>
<ul>
<li>이미 방문한 URL</li>
<li>URL Frontier 상태</li>
<li>수집 완료 여부</li>
<li>수집된 콘텐츠</li>
</ul>
<p>이를 통해 장애 발생 이후 이전 상태에서 작업을 재개할 수 있음.</p>
<hr>
<h2 id="예외-처리">예외 처리</h2>
<p>대규모 시스템에서는 오류를 완전히 없애기 어려움.</p>
<p>따라서 일부 요청 실패가 전체 시스템 중단으로 이어지지 않도록 해야 함.</p>
<p>예:</p>
<ul>
<li>재시도</li>
<li>실패 큐</li>
<li>오류 로그</li>
<li>특정 URL 격리</li>
</ul>
<hr>
<h2 id="데이터-검증">데이터 검증</h2>
<p>다운로드한 데이터가 정상적인지 확인해야 함.</p>
<p>예:</p>
<ul>
<li>문서 크기 제한</li>
<li>콘텐츠 타입 확인</li>
<li>HTML 유효성 확인</li>
<li>악성 파일 여부 검사</li>
</ul>
<hr>
<h1 id="31-확장성">31. 확장성</h1>
<p>웹에는 HTML 외에도 다양한 콘텐츠가 존재함.</p>
<ul>
<li>이미지</li>
<li>PDF</li>
<li>동영상</li>
<li>문서 파일</li>
</ul>
<p>새로운 콘텐츠 형식을 지원할 때마다 전체 크롤러를 수정하는 구조는 유지보수가 어려움.</p>
<p>따라서 콘텐츠 유형별 처리기를 독립적인 모듈로 구현하는 것이 좋음.</p>
<pre><code class="language-text">Downloader
   ↓
Content Type
   ├─ HTML Parser
   ├─ Image Processor
   ├─ PDF Parser
   └─ Video Processor</code></pre>
<hr>
<h1 id="32-유해하거나-불필요한-콘텐츠-처리">32. 유해하거나 불필요한 콘텐츠 처리</h1>
<h2 id="중복-콘텐츠">중복 콘텐츠</h2>
<p>웹 페이지의 해시값이나 체크섬을 이용하여 중복 콘텐츠 탐지 가능.</p>
<pre><code class="language-text">Content
→ Hash
→ 기존 Hash와 비교</code></pre>
<hr>
<h2 id="spider-trap">Spider Trap</h2>
<p>Spider Trap은 크롤러가 끝없이 URL을 따라가도록 만드는 웹 구조임.</p>
<p>예:</p>
<pre><code class="language-text">/page?id=1
/page?id=2
/page?id=3
...</code></pre>
<p>또는 URL이 계속 길어지는 구조:</p>
<pre><code class="language-text">/a
/a/a
/a/a/a
/a/a/a/a
...</code></pre>
<p>대응 방법:</p>
<ul>
<li>URL 최대 길이 제한</li>
<li>페이지 깊이 제한</li>
<li>비정상적으로 많은 URL을 생성하는 사이트 탐지</li>
<li>반복 패턴 탐지</li>
<li>문제가 지속되는 사이트를 차단 목록에 추가</li>
</ul>
<p>URL 길이 제한만으로 모든 Spider Trap을 막을 수 있는 것은 아님.</p>
<hr>
<h2 id="데이터-노이즈">데이터 노이즈</h2>
<p>웹 페이지에는 실제 콘텐츠 분석에 도움이 되지 않는 정보도 많음.</p>
<p>예:</p>
<ul>
<li>광고</li>
<li>JavaScript 코드</li>
<li>추적 코드</li>
<li>스팸 URL</li>
<li>반복 메뉴</li>
<li>불필요한 UI 요소</li>
</ul>
<p>이러한 데이터를 제거하면 저장 공간과 후속 처리 비용을 줄일 수 있음.</p>
<hr>
<h1 id="33-전체-크롤링-흐름-정리">33. 전체 크롤링 흐름 정리</h1>
<pre><code class="language-text">시작 URL
    ↓
URL Frontier
    ↓
우선순위 및 호스트별 스케줄링
    ↓
DNS 조회
    ↓
HTML Downloader
    ↓
Content Parser
    ↓
중복 콘텐츠 확인
    ↓
Content Storage
    ↓
URL Extractor
    ↓
URL Filter
    ↓
이미 방문한 URL인지 확인
    ↓
새 URL이면 URL Frontier에 추가
    ↓
반복</code></pre>
<p>URL Frontier 내부에서는 다음 두 가지가 핵심임.</p>
<pre><code class="language-text">전면 큐
→ 어떤 URL을 먼저 크롤링할 것인지 결정

후면 큐
→ 특정 웹사이트에 요청이 몰리지 않도록 제어</code></pre>
<p>대규모 웹 크롤러에서는 단순히 많은 페이지를 빠르게 다운로드하는 것뿐 아니라 <strong>우선순위, 신선도, 중복 제거, 크롤링 예절, 장애 복구를 함께 고려하는 것이 중요함.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 8장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-8%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-8%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 06 Aug 2026 04:42:55 GMT</pubDate>
            <description><![CDATA[<h1 id="url-단축기-설계">URL 단축기 설계</h1>
<h2 id="1-url-단축-시스템의-기본-기능">1. URL 단축 시스템의 기본 기능</h2>
<ol>
<li><p>URL 단축</p>
<ul>
<li>주어진 긴 URL을 훨씬 짧은 URL로 변환</li>
</ul>
</li>
<li><p>URL 리디렉션</p>
<ul>
<li>단축 URL로 HTTP 요청이 들어오면 원래 URL로 안내</li>
</ul>
</li>
<li><p>비기능 요구사항</p>
<ul>
<li>높은 가용성</li>
<li>높은 규모 확장성</li>
<li>장애 감내</li>
</ul>
</li>
</ol>
<hr>
<h2 id="2-요구사항">2. 요구사항</h2>
<h3 id="21-쓰기-연산">2.1 쓰기 연산</h3>
<ul>
<li>매일 1억 개의 단축 URL 생성</li>
</ul>
<p>초당 평균 쓰기 연산:</p>
<pre><code class="language-text">1억 ÷ 24 ÷ 3,600
≈ 1,160회/초</code></pre>
<h3 id="22-읽기-연산">2.2 읽기 연산</h3>
<ul>
<li>읽기와 쓰기의 비율을 <code>10:1</code>로 가정</li>
<li>초당 평균 읽기 연산:</li>
</ul>
<pre><code class="language-text">1,160 × 10
= 11,600회/초</code></pre>
<h3 id="23-저장할-레코드-수">2.3 저장할 레코드 수</h3>
<ul>
<li>10년간 운영한다고 가정</li>
</ul>
<pre><code class="language-text">1억 × 365 × 10
= 3,650억 개</code></pre>
<ul>
<li>10년 동안 약 3,650억 개의 레코드 보관 필요</li>
</ul>
<h3 id="24-저장-용량">2.4 저장 용량</h3>
<ul>
<li>단축 전 URL의 평균 길이를 100바이트로 가정</li>
</ul>
<pre><code class="language-text">3,650억 × 100바이트
= 36.5TB</code></pre>
<ul>
<li>10년 동안 필요한 저장 용량은 약 36.5TB</li>
</ul>
<hr>
<h2 id="3-api-엔드포인트">3. API 엔드포인트</h2>
<p>클라이언트는 서버가 제공하는 API 엔드포인트를 통해 URL 단축 시스템과 통신함.</p>
<p>이번 설계는 REST 스타일로 구성함.</p>
<h3 id="31-url-단축-엔드포인트">3.1 URL 단축 엔드포인트</h3>
<ul>
<li>새로운 단축 URL을 생성하기 위한 엔드포인트</li>
<li>클라이언트는 단축할 URL을 요청에 담아 POST 방식으로 전송</li>
</ul>
<pre><code class="language-http">POST /api/v1/data/shorten</code></pre>
<h3 id="32-url-리디렉션-엔드포인트">3.2 URL 리디렉션 엔드포인트</h3>
<ul>
<li>단축 URL 요청을 원래 URL로 리디렉션하기 위한 엔드포인트</li>
</ul>
<pre><code class="language-http">GET /api/v1/shortUrl</code></pre>
<hr>
<h2 id="4-url-리디렉션-응답-절차">4. URL 리디렉션 응답 절차</h2>
<ol>
<li>클라이언트가 단축 URL 서버에 요청 전송</li>
<li>단축 URL 서버가 단축 URL에 대응하는 원래 URL 조회</li>
<li>원래 URL을 HTTP 응답의 <code>Location</code> 헤더에 포함</li>
<li>클라이언트가 <code>Location</code> 헤더에 지정된 원래 URL로 이동</li>
<li>원래 URL 서버 방문</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/329d44ac-54a0-49db-95fe-287ebec1e29c/image.png" alt=""></p>
<hr>
<h2 id="5-http-301과-302의-차이">5. HTTP 301과 302의 차이</h2>
<p>두 상태 코드는 모두 리디렉션에 사용되지만 의미와 활용 방식에 차이가 있음.</p>
<h3 id="51-301-moved-permanently">5.1 301 Moved Permanently</h3>
<ul>
<li>해당 URL의 요청 처리 위치가 <code>Location</code> 헤더의 URL로 영구적으로 변경되었음을 의미</li>
<li>브라우저나 중간 캐시에 리디렉션 정보가 저장될 가능성이 높음</li>
<li>이후 같은 단축 URL을 요청할 때 단축 URL 서버를 거치지 않고 원래 URL로 직접 이동할 수 있음</li>
<li>단축 URL 서버의 부하를 줄이는 데 유리</li>
</ul>
<h3 id="52-302-found">5.2 302 Found</h3>
<ul>
<li>요청이 현재는 <code>Location</code> 헤더에 지정된 다른 URL에서 임시로 처리됨을 의미</li>
<li>일반적으로 301보다 장기적으로 캐시될 가능성이 낮음</li>
<li>요청할 때마다 단축 URL 서버를 거쳐 원래 URL로 이동할 가능성이 높음</li>
<li>단, 명시적인 캐시 정책에 따라 캐시될 수도 있음</li>
</ul>
<h3 id="53-선택-기준">5.3 선택 기준</h3>
<h4 id="301이-적합한-경우">301이 적합한 경우</h4>
<ul>
<li>리디렉션 대상이 영구적으로 유지됨</li>
<li>단축 URL 서버의 부하를 줄이는 것이 중요함</li>
<li>모든 클릭 요청을 직접 추적할 필요가 적음</li>
</ul>
<h4 id="302가-적합한-경우">302가 적합한 경우</h4>
<ul>
<li>클릭 수와 접속 위치 등의 트래픽 분석이 중요함</li>
<li>단축 URL의 목적지가 나중에 변경될 수 있음</li>
<li>요청이 단축 URL 서버를 거치도록 유지할 필요가 있음</li>
</ul>
<hr>
<h2 id="6-url-리디렉션-구현">6. URL 리디렉션 구현</h2>
<p>URL 리디렉션을 구현하는 가장 직관적인 방법은 해시 테이블을 사용하는 것임.</p>
<p>해시 테이블에는 다음과 같은 쌍을 저장함.</p>
<pre><code class="language-text">&lt;단축 URL, 원래 URL&gt;</code></pre>
<p>원래 URL 조회:</p>
<pre><code class="language-text">원래 URL = hashTable.get(단축 URL)</code></pre>
<p>조회한 원래 URL을 HTTP 301 또는 302 응답의 <code>Location</code> 헤더에 넣어 전송함.</p>
<hr>
<h2 id="7-단축-코드-생성-시-고려할-점">7. 단축 코드 생성 시 고려할 점</h2>
<p>긴 URL을 단축 코드로 변환할 때는 단축 코드가 가능한 한 고르게 분포하도록 해야 함.</p>
<p>다만 고정 길이의 단축 코드를 사용하면 서로 다른 긴 URL이 같은 단축 코드로 변환되는 충돌 가능성을 완전히 없앨 수 없음.</p>
<p>예를 들어 7자리 단축 코드를 사용하면 출력 가능한 코드 수는 유한함.</p>
<pre><code class="language-text">62⁷ = 약 3.52조 개</code></pre>
<p>따라서 다음 조건이 필요함.</p>
<ul>
<li>단축 코드가 고르게 분포하도록 생성</li>
<li>충돌 발생 여부 확인</li>
<li>충돌 발생 시 새로운 코드 생성</li>
<li>동일한 코드가 중복 저장되지 않도록 데이터베이스 제약 설정</li>
</ul>
<p>또한 단축 코드에서 원래 URL을 직접 복원하는 방식이 아님.</p>
<pre><code class="language-text">단축 코드
→ 데이터베이스 또는 캐시 조회
→ 원래 URL 반환</code></pre>
<p>원래 URL은 별도 저장소에 반드시 보관해야 함.</p>
<hr>
<h1 id="구체적인-설계안">구체적인 설계안</h1>
<h2 id="8-데이터-모델">8. 데이터 모델</h2>
<p>모든 <code>&lt;단축 URL, 원래 URL&gt;</code> 쌍을 메모리의 해시 테이블에 저장하기에는 메모리 용량이 제한적이고 비용도 큼.</p>
<p>따라서 전체 데이터는 관계형 데이터베이스에 영구 저장함.</p>
<h3 id="81-데이터베이스-조회-성능">8.1 데이터베이스 조회 성능</h3>
<p>관계형 데이터베이스는 메모리 해시 테이블보다 느리지만, 단축 코드에 인덱스를 설정하면 전체 테이블을 탐색하지 않고 빠르게 조회 가능함.</p>
<p>예시:</p>
<pre><code class="language-sql">SELECT long_url
FROM urls
WHERE short_code = ?;</code></pre>
<h3 id="82-캐시-활용">8.2 캐시 활용</h3>
<p>데이터베이스만 사용하는 것이 아니라 Redis 등의 캐시를 함께 사용함.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/ff0ef636-dc13-47a3-a389-8d1486545e4b/image.png" alt=""></p>
<p>조회 흐름:</p>
<pre><code class="language-text">단축 URL 요청
→ 캐시 조회
→ 캐시 적중: 원래 URL 반환
→ 캐시 미스: 데이터베이스 조회
→ 조회 결과를 캐시에 저장
→ 원래 URL로 리디렉션</code></pre>
<ul>
<li><p>데이터베이스</p>
<ul>
<li>전체 URL 데이터의 영구 저장 담당</li>
</ul>
</li>
<li><p>캐시</p>
<ul>
<li>자주 조회되는 URL의 빠른 응답 담당</li>
</ul>
</li>
</ul>
<hr>
<h2 id="9-단축-코드-문자-구성">9. 단축 코드 문자 구성</h2>
<p>편의상 단축 URL의 고유 코드 부분을 <code>hashValue</code>라고 부름.</p>
<p><code>hashValue</code>는 다음 문자로 구성함.</p>
<ul>
<li>숫자: <code>0~9</code></li>
<li>소문자: <code>a~z</code></li>
<li>대문자: <code>A~Z</code></li>
</ul>
<p>사용 가능한 전체 문자 수:</p>
<pre><code class="language-text">10 + 26 + 26
= 62개</code></pre>
<hr>
<h2 id="10-단축-코드-길이-계산">10. 단축 코드 길이 계산</h2>
<p>10년 동안 약 3,650억 개의 URL을 생성할 예정이므로 다음 조건을 만족하는 최소 <code>n</code>을 구해야 함.</p>
<pre><code class="language-text">62ⁿ ≥ 3,650억</code></pre>
<p>계산 결과:</p>
<pre><code class="language-text">62⁶ = 56,800,235,584
약 568억

62⁷ = 3,521,614,606,208
약 3.52조</code></pre>
<p>따라서 필요한 최소 길이는 7자리임.</p>
<hr>
<h2 id="11-단축-코드-생성-방식">11. 단축 코드 생성 방식</h2>
<p>단축 코드를 생성하는 방식은 다음 두 가지로 나눌 수 있음.</p>
<ol>
<li>해시 후 충돌 해소</li>
<li>유일 ID의 Base62 변환</li>
</ol>
<p>Base62는 해시 함수가 아니라 숫자를 62진수 문자열로 표현하는 인코딩 방식임.</p>
<hr>
<h2 id="12-해시-후-충돌-해소">12. 해시 후 충돌 해소</h2>
<h3 id="121-기본-과정">12.1 기본 과정</h3>
<ol>
<li>긴 URL에 해시 함수 적용</li>
<li>생성된 해시값에서 필요한 길이만큼 문자 선택</li>
<li>현재 설계에서는 앞의 7글자를 단축 코드로 사용</li>
<li>데이터베이스에서 해당 코드의 존재 여부 확인</li>
<li>충돌하지 않으면 저장</li>
<li>충돌하면 새로운 값을 만들어 다시 해싱</li>
</ol>
<p>예시:</p>
<pre><code class="language-text">hash(longURL)
→ 충돌 발생

hash(longURL + &quot;1&quot;)
→ 다시 확인

hash(longURL + &quot;2&quot;)
→ 다시 확인</code></pre>
<p>즉, 충돌이 발생하면 원래 URL에 사전에 정한 문자열이나 salt를 추가한 뒤 다시 해싱함.</p>
<p>단축 코드 자체에 문자열을 계속 덧붙이는 방식은 7자리 고정 길이 요구와 맞지 않으므로 사용하지 않음.</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/0367da68-1f73-4679-a68e-96947f3dd229/image.png" alt=""></p>
<h3 id="122-장점">12.2 장점</h3>
<ul>
<li>별도의 순차적 ID 생성기가 없어도 단축 코드 생성 가능</li>
<li>동일한 입력에 대해 일정한 결과를 만들도록 구현 가능</li>
</ul>
<h3 id="123-단점">12.3 단점</h3>
<ul>
<li>충돌 가능성 존재</li>
<li>생성한 코드가 이미 사용 중인지 확인해야 함</li>
<li>충돌이 반복되면 데이터베이스 질의와 재시도 증가</li>
<li>같은 URL을 중복 생성할지 재사용할지에 대한 정책 필요</li>
</ul>
<hr>
<h2 id="13-충돌-검사를-위한-데이터베이스와-블룸-필터">13. 충돌 검사를 위한 데이터베이스와 블룸 필터</h2>
<p>해시 후 충돌 해소 방식에서는 생성한 단축 코드가 이미 존재하는지 확인해야 함.</p>
<p>기본 흐름:</p>
<pre><code class="language-text">단축 코드 생성
→ 데이터베이스 존재 여부 확인
→ 충돌 시 코드 재생성
→ 다시 데이터베이스 확인</code></pre>
<p>여러 서버가 동시에 같은 단축 코드를 생성할 가능성에 대비해 데이터베이스에 유일 제약을 설정할 수 있음.</p>
<pre><code class="language-sql">UNIQUE(short_code)</code></pre>
<h3 id="131-블룸-필터-활용">13.1 블룸 필터 활용</h3>
<p>블룸 필터는 데이터베이스를 완전히 대체하는 것이 아니라 불필요한 데이터베이스 조회를 줄이는 보조 자료구조임.</p>
<p>블룸 필터의 판단 결과:</p>
<pre><code class="language-text">없음
→ 확실히 존재하지 않음

있음
→ 존재할 수도 있음</code></pre>
<p>처리 흐름:</p>
<pre><code class="language-text">새 단축 코드 생성
→ 블룸 필터 확인</code></pre>
<h4 id="블룸-필터가-없다고-판단한-경우">블룸 필터가 없다고 판단한 경우</h4>
<ul>
<li>해당 코드는 확실히 존재하지 않음</li>
<li>바로 데이터베이스 저장 시도 가능</li>
</ul>
<h4 id="블룸-필터가-있을-수-있다고-판단한-경우">블룸 필터가 있을 수 있다고 판단한 경우</h4>
<ul>
<li>거짓 양성 가능성 존재</li>
<li>데이터베이스에서 실제 존재 여부 확인 필요</li>
</ul>
<p>따라서 블룸 필터를 사용하면 확실히 존재하지 않는 코드에 대한 데이터베이스 조회를 줄일 수 있음.</p>
<hr>
<h2 id="14-base62-변환">14. Base62 변환</h2>
<p>Base62는 유일한 숫자 ID를 62개의 문자로 구성된 짧은 문자열로 변환하는 방식임.</p>
<p>사용 문자:</p>
<pre><code class="language-text">0-9
a-z
A-Z</code></pre>
<h3 id="141-동작-과정">14.1 동작 과정</h3>
<pre><code class="language-text">유일한 숫자 ID 생성
→ 숫자 ID를 Base62로 변환
→ 단축 코드 생성</code></pre>
<p>예를 들어 숫자 ID가 <code>125</code>라면 이를 Base62 문자열로 변환하여 짧은 코드로 표현 가능함.</p>
<h3 id="142-base62를-사용하는-이유">14.2 Base62를 사용하는 이유</h3>
<ul>
<li>한 자리에서 62개의 값을 표현 가능</li>
<li>10진수보다 같은 숫자를 더 짧은 문자열로 표현 가능</li>
<li>URL에 사용하기 적합한 영문자와 숫자로 구성</li>
<li>숫자 ID가 유일하면 Base62 결과도 유일함</li>
<li>해시 충돌이 발생하지 않음</li>
</ul>
<h3 id="143-단점">14.3 단점</h3>
<ul>
<li>순차적인 숫자 ID를 사용하면 단축 코드도 어느 정도 예측 가능</li>
<li>전체 URL 생성량이나 생성 순서가 외부에 노출될 가능성 존재</li>
<li>전역적으로 유일한 ID 생성기가 필요함</li>
</ul>
<hr>
<h2 id="15-두-방식-비교">15. 두 방식 비교</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/de6e7fc1-61f0-4185-8304-6e4b18c90c00/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>구분</th>
<th>해시 후 충돌 해소</th>
<th>유일 ID의 Base62 변환</th>
</tr>
</thead>
<tbody><tr>
<td>입력값</td>
<td>긴 URL</td>
<td>유일 숫자 ID</td>
</tr>
<tr>
<td>충돌 가능성</td>
<td>있음</td>
<td>ID가 유일하면 없음</td>
</tr>
<tr>
<td>충돌 검사</td>
<td>필요</td>
<td>불필요</td>
</tr>
<tr>
<td>DB 조회</td>
<td>충돌 확인을 위해 필요</td>
<td>URL 저장 및 조회에 사용</td>
</tr>
<tr>
<td>별도 ID 생성기</td>
<td>불필요</td>
<td>필요</td>
</tr>
<tr>
<td>코드 예측 가능성</td>
<td>상대적으로 낮음</td>
<td>순차 ID 사용 시 높음</td>
</tr>
<tr>
<td>구현 복잡도</td>
<td>충돌 처리 필요</td>
<td>ID 생성기 관리 필요</td>
</tr>
</tbody></table>
<hr>
<h2 id="16-base62-방식의-처리-흐름">16. Base62 방식의 처리 흐름</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/1ef45afa-d8a0-4bdf-b7cd-c41e3c1e28c5/image.png" alt=""></p>
<ol>
<li>클라이언트가 긴 URL을 전달</li>
<li>ID 생성기가 전역적으로 유일한 숫자 ID 생성</li>
<li>숫자 ID를 Base62 문자열로 변환</li>
<li>Base62 결과를 단축 코드로 사용</li>
<li>데이터베이스에 새로운 레코드 저장</li>
<li>생성된 단축 URL을 클라이언트에게 반환</li>
</ol>
<p>기본 레코드:</p>
<pre><code class="language-text">ID | shortURL | longURL</code></pre>
<p>ID는 전역적으로 유일해야 함.</p>
<hr>
<h2 id="17-데이터-저장-방식">17. 데이터 저장 방식</h2>
<p>Base62는 숫자 ID와 단축 코드 사이의 가역적인 변환임.</p>
<p>따라서 두 가지 데이터 모델을 사용할 수 있음.</p>
<h3 id="171-단축-코드를-직접-저장하는-방식">17.1 단축 코드를 직접 저장하는 방식</h3>
<pre><code class="language-text">ID | short_code | long_url | created_at</code></pre>
<p>조회 과정:</p>
<pre><code class="language-text">short_code
→ 데이터베이스 조회
→ long_url 반환</code></pre>
<h3 id="172-단축-코드를-숫자-id로-복원하는-방식">17.2 단축 코드를 숫자 ID로 복원하는 방식</h3>
<pre><code class="language-text">ID | long_url | created_at</code></pre>
<p>조회 과정:</p>
<pre><code class="language-text">short_code
→ Base62 디코딩
→ numeric ID
→ 데이터베이스 조회
→ long_url 반환</code></pre>
<p>이 방식은 <code>short_code</code>를 별도 컬럼으로 저장하지 않아도 됨.</p>
<hr>
<h2 id="18-id-생성기">18. ID 생성기</h2>
<p>Base62 방식에서는 입력으로 사용할 숫자 ID가 전역적으로 유일해야 함.</p>
<p>선택 가능한 방식:</p>
<h3 id="단일-쓰기-데이터베이스">단일 쓰기 데이터베이스</h3>
<ul>
<li><code>auto_increment</code></li>
<li>데이터베이스 sequence</li>
</ul>
<h3 id="여러-데이터베이스-샤드-또는-데이터센터">여러 데이터베이스 샤드 또는 데이터센터</h3>
<ul>
<li>Snowflake</li>
<li>ID 구간 할당</li>
<li>별도의 분산 ID 생성기</li>
</ul>
<p>현재 요구사항은 평균 초당 약 1,160개의 쓰기 요청임.</p>
<p>사용하는 데이터베이스와 하드웨어가 해당 쓰기 부하를 감당할 수 있다면 단일 데이터베이스의 <code>auto_increment</code>나 sequence로도 구현 가능함.</p>
<p>여러 데이터베이스에서 독립적으로 ID를 발급해야 한다면 Snowflake 등의 분산 ID 생성 방식을 고려해야 함.</p>
<hr>
<h2 id="19-전체-처리-흐름">19. 전체 처리 흐름</h2>
<h3 id="191-url-생성">19.1 URL 생성</h3>
<pre><code class="language-text">클라이언트
→ 긴 URL 전송
→ 유일 ID 생성
→ Base62 변환
→ DB에 ID와 원래 URL 저장
→ 단축 URL 반환</code></pre>
<h3 id="192-url-리디렉션">19.2 URL 리디렉션</h3>
<pre><code class="language-text">클라이언트가 단축 URL 요청
→ 캐시에서 원래 URL 조회
→ 캐시 적중 시 즉시 리디렉션
→ 캐시 미스 시 DB 조회
→ 조회 결과 캐시 저장
→ 301 또는 302 응답 반환</code></pre>
<hr>
<h2 id="20-정리">20. 정리</h2>
<h3 id="데이터-저장">데이터 저장</h3>
<ul>
<li>모든 URL 매핑 정보를 메모리에 저장하기에는 비용과 용량 한계 존재</li>
<li>전체 데이터는 데이터베이스에 영구 저장</li>
<li>자주 조회되는 URL은 캐시에 저장</li>
</ul>
<h3 id="단축-코드-길이">단축 코드 길이</h3>
<ul>
<li>사용 가능한 문자는 총 62개</li>
<li>약 3,650억 개의 URL을 저장하기 위한 최소 코드 길이는 7자리</li>
</ul>
<h3 id="해시-후-충돌-해소">해시 후 충돌 해소</h3>
<ul>
<li>긴 URL을 해싱하여 단축 코드 생성</li>
<li>충돌 발생 시 salt 등을 추가하여 다시 해싱</li>
<li>데이터베이스 또는 블룸 필터를 이용해 충돌 확인</li>
</ul>
<h3 id="블룸-필터">블룸 필터</h3>
<ul>
<li>데이터베이스를 대체하지 않음</li>
<li>확실히 존재하지 않는 코드를 빠르게 판별</li>
<li>불필요한 데이터베이스 조회 감소</li>
</ul>
<h3 id="base62-변환">Base62 변환</h3>
<ul>
<li>유일한 숫자 ID를 짧은 문자열로 변환</li>
<li>숫자 ID가 유일하면 충돌 없음</li>
<li>전역적으로 유일한 ID 생성기가 필요함</li>
</ul>
<h3 id="최종-설계">최종 설계</h3>
<ul>
<li>데이터베이스로 전체 URL 매핑 영구 저장</li>
<li>Redis 등의 캐시로 읽기 성능 향상</li>
<li>유일 ID를 생성한 뒤 Base62로 변환하여 단축 코드 생성</li>
<li>단축 코드 요청 시 캐시와 데이터베이스를 통해 원래 URL 조회</li>
<li>서비스 목적에 따라 301 또는 302 리디렉션 사용</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 7장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-7%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-7%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 30 Jul 2026 07:10:02 GMT</pubDate>
            <description><![CDATA[<h1 id="분산-시스템을-위한-유일-id-생성기">분산 시스템을 위한 유일 ID 생성기</h1>
<h2 id="1-유일-id-생성기가-필요한-이유">1. 유일 ID 생성기가 필요한 이유</h2>
<ul>
<li><p>관계형 데이터베이스의 기본 키에 <code>auto_increment</code> 속성을 설정하면 간단하게 유일 ID 생성 가능</p>
</li>
<li><p>그러나 대규모 분산 환경에서는 단일 데이터베이스가 병목 또는 단일 장애 지점이 될 수 있음</p>
</li>
<li><p>여러 데이터베이스 서버를 사용하면 처리량을 늘릴 수 있지만 다음 문제 발생 가능</p>
<ul>
<li>ID 충돌 방지를 위한 서버 간 조정 필요</li>
<li>ID 생성 순서 보장 어려움</li>
<li>네트워크 통신으로 인한 지연 시간 증가</li>
<li>서버 추가 및 제거 시 운영 복잡도 증가</li>
</ul>
</li>
</ul>
<p>시스템 설계 면접의 첫 단계는 질문을 통해 모호한 요구사항을 제거하고 설계 범위를 확정하는 것임.</p>
<hr>
<h1 id="2-문제-이해-및-설계-범위-확정">2. 문제 이해 및 설계 범위 확정</h1>
<p>이번 장에서 주어진 요구사항은 다음과 같음.</p>
<ul>
<li>ID는 유일해야 함</li>
<li>ID는 숫자로만 구성되어야 함</li>
<li>ID는 64비트로 표현할 수 있어야 함</li>
<li>ID는 발급 날짜에 따라 정렬 가능해야 함</li>
<li>초당 10,000개의 ID를 생성할 수 있어야 함</li>
</ul>
<hr>
<h1 id="3-개략적인-설계안">3. 개략적인 설계안</h1>
<p>분산 시스템에서 유일성이 보장되는 ID를 생성하는 방법은 다음과 같음.</p>
<ul>
<li>다중 마스터 복제</li>
<li>UUID</li>
<li>티켓 서버</li>
<li>Twitter Snowflake 방식</li>
</ul>
<hr>
<h1 id="4-다중-마스터-복제">4. 다중 마스터 복제</h1>
<h2 id="41-기본-원리">4.1 기본 원리</h2>
<ul>
<li>여러 데이터베이스 서버의 <code>auto_increment</code> 기능 활용</li>
<li>각 서버의 시작값을 다르게 설정</li>
<li>ID 증가폭 <code>k</code>를 전체 데이터베이스 서버 수로 설정</li>
</ul>
<p>데이터베이스 서버가 3대인 경우:</p>
<pre><code class="language-text">서버 A: 1, 4, 7, 10 ...
서버 B: 2, 5, 8, 11 ...
서버 C: 3, 6, 9, 12 ...</code></pre>
<ul>
<li>각 서버의 시작값은 각각 1, 2, 3</li>
<li>각 서버의 ID 증가폭은 3</li>
<li>서로 다른 서버가 같은 ID를 생성하는 문제 방지
<img src="https://velog.velcdn.com/images/dus_snp/post/fee816d6-cf14-4bc2-91e0-62b03408e899/image.png" alt=""></li>
</ul>
<h2 id="42-장점">4.2 장점</h2>
<ul>
<li>데이터베이스 서버 수를 늘려 ID 생성 처리량 증가 가능</li>
<li>단일 데이터베이스의 처리량 한계 일부 완화</li>
<li>기존 데이터베이스의 <code>auto_increment</code> 기능 활용 가능</li>
</ul>
<h2 id="43-단점">4.3 단점</h2>
<ul>
<li>여러 데이터센터에 걸쳐 규모를 확장하기 어려움</li>
<li>ID의 유일성은 보장할 수 있지만 생성 시간에 따라 값이 증가하는 것은 보장하기 어려움</li>
<li>서버 수가 변경되면 증가폭과 시작값 규칙을 다시 조정해야 함</li>
<li>서버를 추가하거나 제거하는 과정의 운영 복잡도가 높음</li>
</ul>
<hr>
<h1 id="5-uuid">5. UUID</h1>
<h2 id="51-기본-개념">5.1 기본 개념</h2>
<ul>
<li>컴퓨터 시스템의 정보를 유일하게 식별하기 위한 128비트 식별자</li>
<li>여러 서버가 서로 조율하지 않고 독립적으로 생성 가능</li>
<li>중복 UUID가 생성될 확률이 0은 아니지만 현실적으로 무시할 수 있을 정도로 낮음
<img src="https://velog.velcdn.com/images/dus_snp/post/5d0f5fe9-e664-4b6a-91cd-a18029167804/image.png" alt=""></li>
</ul>
<h2 id="52-장점">5.2 장점</h2>
<ul>
<li>생성 방식이 비교적 단순함</li>
<li>서버 사이의 조율이 필요 없음</li>
<li>중앙 ID 생성 서버가 필요 없음</li>
<li>서버를 추가하더라도 별도의 ID 범위 할당이 필요 없음</li>
<li>수평적 규모 확장이 쉬움</li>
</ul>
<h2 id="53-단점">5.3 단점</h2>
<ul>
<li>128비트로 구성되어 64비트 요구사항을 만족하지 못함</li>
<li>일반적으로 많이 사용하는 무작위 기반 UUIDv4는 생성 시간순 정렬이 어려움</li>
<li>일반적인 문자열 표현에 16진수 문자와 하이픈이 포함됨</li>
</ul>
<p>예시:</p>
<pre><code class="language-text">550e8400-e29b-41d4-a716-446655440000</code></pre>
<ul>
<li>UUID 자체는 128비트 값임</li>
<li>문자열로 표현할 때 <code>a~f</code>와 하이픈이 포함되므로 숫자로만 구성되어야 한다는 요구사항을 만족하지 못함</li>
</ul>
<hr>
<h1 id="6-티켓-서버">6. 티켓 서버</h1>
<h2 id="61-기본-원리">6.1 기본 원리</h2>
<ul>
<li><code>auto_increment</code> 기능을 갖춘 데이터베이스 서버를 중앙 ID 생성 서버로 사용</li>
<li>다른 서비스는 ID가 필요할 때 티켓 서버에 요청</li>
<li>티켓 서버가 순차적으로 증가하는 유일 ID 발급
<img src="https://velog.velcdn.com/images/dus_snp/post/feff6234-6b0f-4287-bd6f-77fea228a435/image.png" alt=""></li>
</ul>
<h2 id="62-장점">6.2 장점</h2>
<ul>
<li>유일성이 보장되는 숫자 ID를 쉽게 생성 가능</li>
<li>구현이 비교적 단순함</li>
<li>생성된 ID가 순차적으로 증가함</li>
<li>중소 규모 애플리케이션에 적합</li>
</ul>
<h2 id="63-단점">6.3 단점</h2>
<ul>
<li>모든 ID 생성 요청이 하나의 티켓 서버에 집중</li>
<li>티켓 서버 장애 시 ID를 생성할 수 없는 단일 장애 지점 발생</li>
<li>티켓 서버의 처리 능력이 전체 시스템의 ID 생성 한도 결정</li>
</ul>
<h2 id="64-다중-티켓-서버-구성">6.4 다중 티켓 서버 구성</h2>
<ul>
<li>단일 장애 지점을 피하기 위해 여러 티켓 서버를 둘 수 있음</li>
<li>여러 티켓 서버가 서로 다른 ID를 발급하도록 조정 필요</li>
<li>서버 간 상태 동기화와 충돌 방지 문제 발생</li>
<li>구조가 복잡해지면서 티켓 서버 방식의 단순성 감소</li>
</ul>
<hr>
<h1 id="7-twitter-snowflake-방식">7. Twitter Snowflake 방식</h1>
<h2 id="71-기본-개념">7.1 기본 개념</h2>
<ul>
<li>하나의 64비트 ID를 여러 영역으로 분할</li>
<li>생성 시각과 ID 생성 서버 정보를 하나의 정수에 포함</li>
<li>각 서버가 다른 서버와 매번 통신하지 않고 독립적으로 ID 생성 가능</li>
<li>ID의 상위 영역에 타임스탬프를 배치하여 대체로 생성 시각 순으로 정렬 가능</li>
</ul>
<h2 id="72-64비트-구조">7.2 64비트 구조</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/1f0c3acc-12a3-494f-864b-b42654930269/image.png" alt=""></p>
<hr>
<h2 id="73-사인-비트">7.3 사인 비트</h2>
<ul>
<li>1비트 사용</li>
<li>현재 구조에서는 별도의 값을 저장하지 않고 0으로 유지</li>
<li>추후 ID 체계를 확장하거나 용도를 변경할 때 활용 가능</li>
</ul>
<hr>
<h2 id="74-타임스탬프">7.4 타임스탬프</h2>
<ul>
<li>41비트 사용</li>
<li>시스템이 정한 기원 시각 이후 몇 밀리초가 경과했는지 저장</li>
</ul>
<pre><code class="language-text">타임스탬프 값 = 현재 시각 - 자체 epoch</code></pre>
<ul>
<li>밀리초 단위의 41비트로 약 69.7년 표현 가능</li>
<li>Unix epoch가 아닌 서비스 시작 시점과 가까운 자체 epoch 설정 가능</li>
<li>자체 epoch를 사용하면 제한된 비트 범위를 서비스 운영 기간에 효율적으로 사용 가능</li>
</ul>
<hr>
<h2 id="75-데이터센터-id">7.5 데이터센터 ID</h2>
<ul>
<li>5비트 사용</li>
<li>최대 32개의 데이터센터 식별 가능</li>
</ul>
<pre><code class="language-text">2⁵ = 32</code></pre>
<ul>
<li>서로 다른 데이터센터에서 생성한 ID를 구분하는 데 사용</li>
</ul>
<hr>
<h2 id="76-서버-id">7.6 서버 ID</h2>
<ul>
<li>5비트 사용</li>
<li>하나의 데이터센터에서 최대 32개의 ID 생성 서버 식별 가능</li>
</ul>
<pre><code class="language-text">2⁵ = 32</code></pre>
<ul>
<li>데이터센터 ID와 서버 ID의 조합으로 최대 1,024개의 생성 서버 구분 가능</li>
</ul>
<pre><code class="language-text">32 × 32 = 1,024</code></pre>
<hr>
<h2 id="77-일련번호">7.7 일련번호</h2>
<ul>
<li>12비트 사용</li>
<li>같은 서버가 같은 밀리초 안에서 여러 ID를 생성할 때 중복 방지</li>
<li>동일한 밀리초 안에서 ID를 생성할 때마다 1씩 증가</li>
<li>밀리초가 변경되면 0으로 초기화</li>
</ul>
<p>같은 밀리초에서 생성되는 ID:</p>
<pre><code class="language-text">첫 번째 ID: 일련번호 0
두 번째 ID: 일련번호 1
세 번째 ID: 일련번호 2
...</code></pre>
<ul>
<li>12비트로 한 서버가 1밀리초에 최대 4,096개의 ID 생성 가능</li>
</ul>
<pre><code class="language-text">2¹² = 4,096</code></pre>
<ul>
<li>같은 밀리초 안에서 4,096개를 모두 사용하면 다음 밀리초가 될 때까지 대기 필요</li>
</ul>
<h2 id="78-이론적-처리량">7.8 이론적 처리량</h2>
<p>한 서버가 1밀리초에 최대 4,096개의 ID를 생성할 수 있으므로 이론적인 초당 처리량은 다음과 같음.</p>
<pre><code class="language-text">4,096 × 1,000
= 초당 4,096,000개</code></pre>
<ul>
<li>요구사항인 초당 10,000개의 ID 생성 가능</li>
<li>실제 처리량은 언어, 구현 방식, 락, 하드웨어 등의 영향을 받음</li>
</ul>
<hr>
<h1 id="8-상세-설계">8. 상세 설계</h1>
<h2 id="81-정적-값">8.1 정적 값</h2>
<p>다음 값은 ID 생성기가 시작될 때 결정됨.</p>
<ul>
<li><p>데이터센터 ID</p>
</li>
<li><p>서버 ID</p>
</li>
<li><p>자체 epoch</p>
</li>
<li><p>일반적으로 운영 중에는 변경하지 않음</p>
</li>
<li><p>동시에 동작하는 생성기는 서로 다른 데이터센터 ID와 서버 ID 조합을 가져야 함</p>
</li>
<li><p>서로 다른 생성기가 같은 식별자 조합을 사용하면 ID 충돌 가능</p>
</li>
</ul>
<p>ID 충돌 조건:</p>
<ul>
<li>동일한 데이터센터 ID</li>
<li>동일한 서버 ID</li>
<li>동일한 타임스탬프</li>
<li>동일한 일련번호</li>
</ul>
<p>운영 중 서버 ID를 재할당할 때는 기존 생성기와 중복되지 않도록 관리 필요.</p>
<h2 id="82-동적-값">8.2 동적 값</h2>
<p>다음 값은 ID 생성기가 동작하는 동안 계속 변경됨.</p>
<ul>
<li><p>타임스탬프</p>
</li>
<li><p>일련번호</p>
</li>
<li><p>타임스탬프는 현재 시각에 따라 증가</p>
</li>
<li><p>일련번호는 같은 밀리초 안에서 ID를 생성할 때마다 증가</p>
</li>
<li><p>밀리초가 변경되면 일련번호 초기화</p>
</li>
</ul>
<hr>
<h1 id="9-타임스탬프-영역의-수명">9. 타임스탬프 영역의 수명</h1>
<ul>
<li>41비트 타임스탬프로 약 69.7년 표현 가능</li>
<li>기원 시각으로부터 약 69.7년이 지나면 타임스탬프 영역 소진</li>
<li>소진되기 전에 새로운 ID 형식이나 별도의 버전 체계로 마이그레이션 필요</li>
<li>동일한 ID 공간에서 epoch만 단순히 변경하면 과거 ID와 충돌할 수 있음</li>
<li>새로운 형식임을 구분할 별도의 버전 또는 네임스페이스 필요</li>
</ul>
<hr>
<h1 id="10-동시성-문제">10. 동시성 문제</h1>
<h2 id="101-여러-스레드-또는-코어에서의-실행">10.1 여러 스레드 또는 코어에서의 실행</h2>
<ul>
<li>하나의 ID 생성기가 여러 스레드나 CPU 코어에서 동시에 실행될 수 있음</li>
<li>여러 실행 흐름이 같은 일련번호를 동시에 읽고 증가시키면 중복 ID 발생 가능</li>
</ul>
<p>예시:</p>
<pre><code class="language-text">스레드 A: 현재 일련번호 10 확인
스레드 B: 현재 일련번호 10 확인

스레드 A: 일련번호 11로 ID 생성
스레드 B: 일련번호 11로 ID 생성</code></pre>
<ul>
<li>동일한 타임스탬프와 서버 정보에서 같은 일련번호를 사용하면 ID 충돌 발생</li>
</ul>
<h2 id="102-해결-방법">10.2 해결 방법</h2>
<ul>
<li>락을 사용해 한 번에 하나의 스레드만 일련번호 수정</li>
<li>원자적 연산을 이용해 일련번호 증가</li>
<li>단일 스레드 ID 생성기 사용</li>
<li>생성 요청을 큐에 넣고 순차적으로 처리</li>
</ul>
<hr>
<h1 id="11-시계-동기화-문제">11. 시계 동기화 문제</h1>
<h2 id="111-여러-서버의-시계-오차">11.1 여러 서버의 시계 오차</h2>
<ul>
<li>물리적으로 독립된 여러 서버는 시스템 시각이 완전히 같지 않을 수 있음</li>
<li>서버 간 시계 차이가 크면 나중에 생성된 ID가 더 작은 타임스탬프를 가질 수 있음</li>
<li>실제 생성 순서와 ID 정렬 순서가 달라질 수 있음</li>
</ul>
<h2 id="112-ntp-활용">11.2 NTP 활용</h2>
<ul>
<li>NTP를 이용해 여러 서버의 시스템 시계를 동기화 가능</li>
<li>서버 간 시계 오차를 줄여 타임스탬프 차이 완화 가능</li>
</ul>
<hr>
<h1 id="12-비트-길이-최적화">12. 비트 길이 최적화</h1>
<p>서비스 특성에 따라 각 영역의 비트 수 변경 가능.</p>
<h2 id="121-동시성이-낮고-운영-기간이-긴-서비스">12.1 동시성이 낮고 운영 기간이 긴 서비스</h2>
<ul>
<li>짧은 시간에 생성하는 ID 수가 적음</li>
<li>일련번호 비트 수 축소 가능</li>
<li>줄인 비트를 타임스탬프 영역에 추가</li>
<li>ID 체계의 사용 가능 기간 증가</li>
</ul>
<h2 id="122-생성-서버가-적은-서비스">12.2 생성 서버가 적은 서비스</h2>
<ul>
<li>데이터센터 ID 또는 서버 ID 비트 수 축소 가능</li>
<li>타임스탬프나 일련번호 영역 확대 가능</li>
</ul>
<h2 id="123-동시성이-높은-서비스">12.3 동시성이 높은 서비스</h2>
<ul>
<li>일련번호 비트 수 확대 가능</li>
<li>한 밀리초에 더 많은 ID 생성 가능</li>
<li>대신 타임스탬프 또는 서버 식별 영역 축소 필요</li>
</ul>
<p>비트 분할은 다음 요소를 기준으로 결정해야 함.</p>
<ul>
<li>예상 서비스 운영 기간</li>
<li>데이터센터 수</li>
<li>ID 생성 서버 수</li>
<li>밀리초당 최대 ID 생성량</li>
</ul>
<hr>
<h1 id="13-고가용성">13. 고가용성</h1>
<ul>
<li>ID 생성기는 여러 서비스에서 공통으로 사용하는 핵심 컴포넌트</li>
<li>ID 생성기가 중단되면 데이터 생성, 주문, 게시물 등록 등의 기능도 중단될 수 있음</li>
<li>높은 가용성 제공 필요</li>
</ul>
<h2 id="131-고려-사항">13.1 고려 사항</h2>
<ul>
<li>여러 ID 생성 서버 운영</li>
<li>생성 서버마다 고유한 데이터센터 ID와 서버 ID 할당</li>
<li>생성 서버 장애 자동 감지</li>
<li>장애 서버의 트래픽을 정상 서버로 전환</li>
<li>서버 식별자 중복 할당 방지</li>
<li>모니터링과 경고 체계 구성</li>
<li>생성 실패 및 지연 시간 측정</li>
</ul>
<hr>
<h1 id="14-방식-비교">14. 방식 비교</h1>
<table>
<thead>
<tr>
<th>방식</th>
<th align="right">ID 길이</th>
<th>시간순 정렬</th>
<th>서버 간 조율</th>
<th>규모 확장</th>
<th>주요 단점</th>
</tr>
</thead>
<tbody><tr>
<td>다중 마스터 복제</td>
<td align="right">설정에 따라 다름</td>
<td>완전 보장 어려움</td>
<td>설정 조정 필요</td>
<td>보통</td>
<td>서버 수 변경이 어려움</td>
</tr>
<tr>
<td>UUID</td>
<td align="right">128비트</td>
<td>UUIDv4는 어려움</td>
<td>불필요</td>
<td>쉬움</td>
<td>64비트·숫자 요구사항 불충족</td>
</tr>
<tr>
<td>티켓 서버</td>
<td align="right">설정에 따라 다름</td>
<td>가능</td>
<td>중앙 서버 필요</td>
<td>제한적</td>
<td>단일 장애 지점</td>
</tr>
<tr>
<td>Snowflake</td>
<td align="right">64비트</td>
<td>대체로 가능</td>
<td>생성 시 불필요</td>
<td>쉬움</td>
<td>시계와 서버 ID 관리 필요</td>
</tr>
</tbody></table>
<hr>
<h1 id="15-정리">15. 정리</h1>
<h2 id="다중-마스터-복제">다중 마스터 복제</h2>
<ul>
<li>각 데이터베이스 서버가 서로 다른 시작값과 공통 증가폭 사용</li>
<li>ID 생성 처리량 증가 가능</li>
<li>서버 수 변경과 다중 데이터센터 확장에 불리함</li>
</ul>
<h2 id="uuid">UUID</h2>
<ul>
<li>서버 간 조율 없이 독립적으로 생성 가능</li>
<li>규모 확장이 쉬움</li>
<li>128비트이며 UUIDv4는 시간순 정렬이 어려움</li>
<li>숫자로만 구성된 64비트 ID 요구사항에 부적합</li>
</ul>
<h2 id="티켓-서버">티켓 서버</h2>
<ul>
<li>중앙 데이터베이스의 <code>auto_increment</code> 기능 활용</li>
<li>구현이 단순하고 순차적 ID 생성 가능</li>
<li>단일 장애 지점과 중앙 병목 발생 가능</li>
</ul>
<h2 id="twitter-snowflake">Twitter Snowflake</h2>
<ul>
<li>타임스탬프, 데이터센터 ID, 서버 ID, 일련번호를 64비트에 조합</li>
<li>서버 간 통신 없이 유일 ID 생성 가능</li>
<li>대체로 시간순 정렬 가능</li>
<li>한 서버에서 매우 높은 처리량 지원</li>
<li>시계 동기화, 동시성, 서버 식별자 중복 관리 필요</li>
</ul>
<h1 id="16-내가-생각한-우리-시스템에-적합한-유일-id-생성기는">16. 내가 생각한 우리 시스템에 적합한 유일 ID 생성기는</h1>
<p>우리 시스템에는 Snowflake보다 관계형 데이터베이스의 <code>auto_increment</code> 또는 sequence 방식이 적합하다고 판단함.</p>
<p>우리 시스템은 여러 데이터센터와 독립된 데이터베이스에 걸쳐 운영되는 대규모 서비스가 아니라 일회성 애플리케이션임. 처리해야 하는 ID 생성량도 Snowflake가 제공하는 초당 수백만 개 수준의 처리량을 요구하지 않음.</p>
<p>Snowflake를 사용하면 다음 요소를 추가로 관리해야 함.</p>
<ul>
<li>데이터센터 ID와 서버 ID 할당</li>
<li>생성 서버 간 식별자 중복 방지</li>
<li>일련번호 동시성 제어</li>
<li>서버 간 시계 동기화</li>
<li>시계 역행 처리</li>
<li>별도의 ID 생성기 배포와 장애 대응</li>
</ul>
<p>이러한 기능은 여러 서버가 중앙 데이터베이스와 통신하지 않고 독립적으로 ID를 생성해야 할 때 유용함. 하지만 우리 시스템에서는 얻을 수 있는 이점보다 구현 및 운영 복잡도가 더 클 것으로 판단됨.</p>
<p>관계형 데이터베이스의 <code>auto_increment</code> 또는 sequence를 사용하면 다음 장점이 있음.</p>
<ul>
<li>별도의 ID 생성 시스템이 필요하지 않음</li>
<li>숫자로 구성된 유일 ID 생성 가능</li>
<li>ID가 순차적으로 증가함</li>
<li>여러 애플리케이션 서버가 동시에 요청해도 데이터베이스가 유일성 보장</li>
<li>구현과 운영이 단순함</li>
</ul>
<p>우리 시스템에서 중요한 요구사항은 높은 가용성임. 그러나 이를 위해 ID 생성기만 별도로 분산시키기보다는 데이터를 저장하는 데이터베이스 자체를 고가용성으로 구성하는 것이 더 적절함.</p>
<p>예를 들어 주 데이터베이스와 대기 데이터베이스를 구성하고, 주 데이터베이스 장애 시 대기 데이터베이스로 자동 전환하도록 설계할 수 있음. 데이터베이스가 중단되면 ID를 별도로 생성하더라도 실제 데이터를 저장할 수 없으므로, ID 생성기와 데이터베이스의 가용성을 분리하여 해결할 필요성이 낮음.</p>
<p>따라서 우리 시스템에서는 다음 방식을 선택함.</p>
<ul>
<li>유일 ID 생성: <code>BIGINT auto_increment</code> 또는 데이터베이스 sequence 사용</li>
<li>애플리케이션 가용성: 여러 애플리케이션 서버와 로드밸런서 구성</li>
<li>데이터베이스 가용성: 주 데이터베이스와 대기 데이터베이스 및 자동 장애 조치 구성</li>
</ul>
<p>Snowflake는 향후 시스템이 여러 데이터센터나 독립된 데이터베이스로 확장되고, 각 서버가 중앙 조정 없이 대량의 ID를 생성해야 할 때 다시 검토하는 것이 적절함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 6장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-6%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-6%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 30 Jul 2026 06:02:25 GMT</pubDate>
            <description><![CDATA[<h1 id="키-값-저장소">키-값 저장소</h1>
<h2 id="1-키-값-저장소란">1. 키-값 저장소란</h2>
<ul>
<li>고유한 키를 이용해 값을 저장하고 조회하는 비관계형 데이터 저장소</li>
<li>키는 저장된 값을 식별하는 역할 수행</li>
<li>키는 일반 문자열이나 해시값 등의 형태로 구성 가능</li>
<li>저장소는 일반적으로 값의 내부 구조를 직접 해석하지 않음</li>
<li>제품이 허용하는 크기와 형식 안에서 문자열, 객체, 바이너리 데이터 등 다양한 값 저장 가능</li>
</ul>
<h3 id="기본-api">기본 API</h3>
<h4 id="putkey-value"><code>put(key, value)</code></h4>
<ul>
<li>키와 값의 쌍을 저장소에 저장</li>
<li>동일한 키가 이미 존재하면 기존 값을 새로운 값으로 갱신할 수 있음</li>
</ul>
<h4 id="getkey"><code>get(key)</code></h4>
<ul>
<li>입력한 키에 대응하는 값 조회</li>
<li>해당 키가 없으면 값이 존재하지 않음을 반환</li>
</ul>
<hr>
<h2 id="2-대표적인-키-값-저장소">2. 대표적인 키-값 저장소</h2>
<h3 id="21-amazon-dynamo">2.1 Amazon Dynamo</h3>
<ul>
<li><p>Amazon이 내부 서비스의 높은 가용성을 위해 설계한 분산 키-값 저장소</p>
</li>
<li><p>2007년 논문을 통해 설계 원리 공개</p>
</li>
<li><p>외부 고객에게 직접 제공되는 AWS 서비스가 아니라 Amazon 내부 시스템의 설계 사례</p>
</li>
<li><p>일부 서버와 네트워크에 장애가 발생해도 쓰기 요청을 가능한 한 계속 처리하는 것을 중요하게 설계</p>
</li>
<li><p>강한 일관성보다 높은 가용성과 최종 일관성을 우선</p>
</li>
<li><p>다음과 같은 분산 시스템 기법 활용</p>
<ul>
<li>안정 해시</li>
<li>데이터 복제</li>
<li>정족수(읽기나 쓰기를 성공으로 인정하기 위해 필요한 최소 서버 수)</li>
<li>벡터 시계</li>
<li>가십 프로토콜</li>
<li>단서 후 임시 위탁</li>
<li>머클 트리</li>
</ul>
</li>
</ul>
<p>Amazon Dynamo는 Amazon의 핵심 서비스에 항상 사용 가능한 저장 기능을 제공하기 위해 설계된 고가용성 키-값 저장소임.</p>
<h3 id="22-redis">2.2 Redis</h3>
<ul>
<li><p>메모리를 중심으로 동작하는 고성능 데이터 저장소</p>
</li>
<li><p>문자열뿐 아니라 다양한 자료구조 제공</p>
<ul>
<li>리스트</li>
<li>집합</li>
<li>정렬 집합</li>
<li>해시</li>
</ul>
</li>
<li><p>캐시 외에도 다음 용도로 활용 가능</p>
<ul>
<li>세션 저장</li>
<li>순위표</li>
<li>분산 락</li>
<li>메시지 전달</li>
<li>실시간 카운터</li>
</ul>
</li>
<li><p>설정에 따라 메모리의 데이터를 디스크에 저장하여 장애 후 복구 가능</p>
</li>
</ul>
<h3 id="23-memcached">2.3 Memcached</h3>
<ul>
<li>메모리에 데이터를 저장하는 단순한 분산 캐시 시스템</li>
<li>키를 이용한 값의 저장과 조회 기능에 집중</li>
<li>Redis보다 제공 기능과 자료구조가 단순함</li>
<li>기본적으로 데이터 영속성을 제공하지 않음</li>
<li>서버가 재시작되거나 캐시 항목이 제거되면 데이터가 사라질 수 있음</li>
</ul>
<h3 id="24-amazon-dynamo와-dynamodb의-차이">2.4 Amazon Dynamo와 DynamoDB의 차이</h3>
<ul>
<li><p>Amazon Dynamo</p>
<ul>
<li>Amazon 내부 서비스의 높은 가용성을 위해 개발된 분산 키-값 저장소</li>
<li>2007년 논문을 통해 설계 원리 공개</li>
<li>일반 사용자가 직접 사용할 수 있는 AWS 서비스는 아님</li>
<li>안정 해시, 벡터 시계, 가십 프로토콜, 단서 후 임시 위탁, 머클 트리 등의 기법 활용</li>
<li>강한 일관성보다 높은 가용성과 최종 일관성을 우선한 설계</li>
</ul>
</li>
<li><p>Amazon DynamoDB</p>
<ul>
<li>AWS가 고객에게 제공하는 완전관리형 서버리스 NoSQL 데이터베이스</li>
<li>사용자가 AWS 콘솔, CLI, SDK 및 API를 통해 직접 사용 가능</li>
<li>서버 구성, 확장, 장애 대응 등의 인프라 운영을 AWS가 담당</li>
<li>Dynamo의 설계 원칙에서 영향을 받았지만 Dynamo와 동일한 시스템은 아님</li>
<li>강력한 일관된 읽기, 트랜잭션, 보조 인덱스, 백업 등 상용 데이터베이스 기능 제공</li>
</ul>
</li>
</ul>
<p>즉, Amazon Dynamo는 분산 키-값 저장소의 설계 사례이고, DynamoDB는 해당 설계 철학의 영향을 받아 만들어진 실제 AWS 데이터베이스 서비스임.</p>
<hr>
<h2 id="3-설계-목표">3. 설계 목표</h2>
<p>이번 장에서 설계하는 분산 키-값 저장소의 요구사항은 다음과 같음.</p>
<h3 id="데이터-크기">데이터 크기</h3>
<ul>
<li>개별 키-값 쌍의 크기는 10KB 이하</li>
<li>시스템 전체로는 대규모 데이터 저장 가능</li>
</ul>
<h3 id="높은-가용성">높은 가용성</h3>
<ul>
<li>일부 서버에 장애가 발생하더라도 가능한 한 빠르게 응답</li>
<li>장애 노드 때문에 전체 시스템이 중단되지 않도록 설계</li>
</ul>
<h3 id="높은-규모-확장성">높은 규모 확장성</h3>
<ul>
<li>데이터와 요청량이 증가하면 서버 추가 가능</li>
<li>서버 추가와 제거 과정 자동화</li>
<li>서버 수 변경 시 데이터 이동량 최소화</li>
</ul>
<h3 id="조정-가능한-일관성">조정 가능한 일관성</h3>
<ul>
<li>서비스 요구사항에 따라 읽기·쓰기 일관성 수준 조정 가능</li>
<li>강한 일관성과 높은 가용성 사이에서 적절한 절충 필요</li>
</ul>
<h3 id="짧은-응답-지연-시간">짧은 응답 지연 시간</h3>
<ul>
<li>읽기와 쓰기 요청을 빠르게 처리</li>
<li>디스크 접근과 네트워크 통신 비용 최소화</li>
</ul>
<hr>
<h2 id="4-단일-서버-키-값-저장소">4. 단일 서버 키-값 저장소</h2>
<h3 id="41-기본-구조">4.1 기본 구조</h3>
<ul>
<li>모든 키-값 쌍을 한 서버의 메모리 해시 테이블에 저장</li>
<li>키를 해싱하여 값의 위치를 빠르게 탐색</li>
<li>단순한 구조로 빠른 읽기와 쓰기 가능</li>
</ul>
<h3 id="42-개선-방법">4.2 개선 방법</h3>
<ul>
<li>데이터 압축을 통한 메모리 사용량 감소</li>
<li>자주 사용하는 데이터만 메모리에 저장</li>
<li>자주 사용하지 않는 데이터는 디스크에 저장</li>
</ul>
<h3 id="43-한계">4.3 한계</h3>
<ul>
<li>메모리 기반 해시 테이블 자체는 빠름</li>
<li>그러나 한 서버의 메모리 용량과 처리 능력에는 한계 존재</li>
<li>데이터와 요청량이 증가하면 단일 서버만으로 처리하기 어려움</li>
<li>서버 장애 시 전체 저장소가 중단되는 단일 장애 지점 발생</li>
<li>대규모 시스템에서는 여러 서버에 데이터를 분산하는 구조 필요</li>
</ul>
<hr>
<h1 id="분산-키-값-저장소">분산 키-값 저장소</h1>
<h2 id="5-cap-정리">5. CAP 정리</h2>
<p>CAP는 다음 세 가지 속성에 관한 분산 시스템의 제약을 설명함.</p>
<ul>
<li>일관성</li>
<li>가용성</li>
<li>파티션 감내</li>
</ul>
<h3 id="51-일관성">5.1 일관성</h3>
<ul>
<li>어떤 노드에 접속하더라도 동일한 데이터를 보게 되는 성질</li>
<li>완료된 최신 쓰기가 이후 읽기에 반영되어야 함</li>
<li>복제본마다 서로 다른 값을 반환하지 않도록 보장</li>
</ul>
<h3 id="52-가용성">5.2 가용성</h3>
<ul>
<li>일부 노드에 장애가 발생하더라도 요청에 응답하는 성질</li>
<li>모든 정상적인 요청이 성공 또는 실패 여부를 포함한 응답을 받을 수 있어야 함</li>
</ul>
<h3 id="53-파티션-감내">5.3 파티션 감내</h3>
<ul>
<li>노드 사이의 네트워크 통신이 끊겨도 시스템이 계속 동작하는 성질</li>
<li>파티션은 일부 노드 그룹끼리 통신할 수 없는 네트워크 장애를 의미</li>
</ul>
<h3 id="54-cap-정리의-의미">5.4 CAP 정리의 의미</h3>
<ul>
<li>단순히 세 가지 중 항상 두 가지만 고르는 문제는 아님</li>
<li>네트워크가 정상일 때는 일관성과 가용성을 모두 제공할 수 있음</li>
<li>네트워크 파티션이 발생하면 일관성과 가용성을 동시에 완전하게 보장하기 어려움</li>
<li>현실적인 분산 시스템에서는 네트워크 장애를 피할 수 없으므로 파티션 감내 필요</li>
<li>파티션 발생 시 일관성과 가용성 가운데 어떤 속성을 우선할지 결정 필요
<img src="https://velog.velcdn.com/images/dus_snp/post/5bb1d1c2-36f3-42f9-bbab-cef8b8d95399/image.png" alt=""></li>
</ul>
<h3 id="55-cap-예시">5.5 CAP 예시</h3>
<p>서버 A와 서버 B가 동일한 데이터의 사본을 가지고 있다고 가정함.</p>
<ol>
<li>서버 A와 서버 B 사이의 네트워크 단절</li>
<li>클라이언트가 서버 A의 데이터 변경</li>
<li>서버 A가 변경 내용을 서버 B에 전달하지 못함</li>
</ol>
<h4 id="일관성을-우선하는-경우">일관성을 우선하는 경우</h4>
<ul>
<li>서버 B는 자신이 가진 값이 최신인지 확인 불가</li>
<li>오래된 값을 반환하지 않기 위해 읽기 또는 쓰기 요청 거부</li>
<li>일관성을 유지하는 대신 가용성 일부 포기</li>
</ul>
<h4 id="가용성을-우선하는-경우">가용성을 우선하는 경우</h4>
<ul>
<li>서버 B가 자신이 가진 값을 계속 반환</li>
<li>사용자는 응답을 받을 수 있음</li>
<li>서버 A의 최신 값과 다른 값을 받을 가능성 존재</li>
<li>가용성을 유지하는 대신 일시적으로 일관성 포기</li>
</ul>
<hr>
<h1 id="시스템-컴포넌트">시스템 컴포넌트</h1>
<h2 id="6-데이터-파티션">6. 데이터 파티션</h2>
<h3 id="61-파티션이-필요한-이유">6.1 파티션이 필요한 이유</h3>
<ul>
<li>대규모 데이터 전체를 한 서버에 저장하기 어려움</li>
<li>전체 데이터를 여러 개의 작은 파티션으로 나누어 여러 서버에 분산</li>
<li>서버별 저장 공간과 처리 부하를 고르게 분배할 필요</li>
</ul>
<h3 id="62-파티션-설계-시-고려-사항">6.2 파티션 설계 시 고려 사항</h3>
<ul>
<li>데이터를 여러 서버에 고르게 분산할 수 있어야 함</li>
<li>서버가 추가되거나 제거될 때 데이터 이동량을 최소화해야 함</li>
<li>서버별 성능 차이를 데이터 분산에 반영할 수 있어야 함</li>
</ul>
<h3 id="63-안정-해시-활용">6.3 안정 해시 활용</h3>
<ul>
<li>서버와 키를 동일한 해시 링 위에 배치</li>
<li>키의 위치에서 시계 방향으로 처음 만나는 서버가 해당 키 담당</li>
<li>서버가 추가되거나 제거되어도 영향받는 일부 키만 이동</li>
<li>전체 키를 다시 배치하는 문제 방지</li>
</ul>
<h3 id="안정-해시의-이점">안정 해시의 이점</h3>
<ul>
<li>자동적인 규모 확장에 유리</li>
<li>서버 추가·제거 시 데이터 이동 최소화</li>
<li>성능이 좋은 서버에 더 많은 가상 노드를 배치하여 서버 성능 차이 반영 가능</li>
</ul>
<hr>
<h2 id="7-데이터-복제">7. 데이터 복제</h2>
<h3 id="71-복제가-필요한-이유">7.1 복제가 필요한 이유</h3>
<ul>
<li>하나의 서버에만 데이터를 저장하면 서버 장애 시 데이터 접근 불가</li>
<li>높은 가용성과 데이터 안정성을 위해 여러 서버에 동일한 데이터 사본 저장</li>
</ul>
<h3 id="72-복제-계수">7.2 복제 계수</h3>
<ul>
<li><code>N</code>: 동일한 데이터를 저장할 복제본의 개수</li>
<li>예를 들어 <code>N=3</code>이면 하나의 데이터를 서로 다른 세 서버에 저장</li>
</ul>
<h3 id="73-복제-서버-선택">7.3 복제 서버 선택</h3>
<ul>
<li>키의 위치에서 시계 방향으로 탐색</li>
<li>처음 만나는 서로 다른 물리 서버 N개에 데이터 저장</li>
<li>가상 노드를 사용하는 경우 동일한 물리 서버를 중복 선택하지 않도록 주의</li>
</ul>
<h3 id="74-복제-방식">7.4 복제 방식</h3>
<ul>
<li>쓰기 요청이 들어오면 여러 복제본에 데이터 전달</li>
<li>클라이언트는 그중 W개의 서버가 성공을 응답하면 쓰기 성공으로 판단 가능</li>
<li>즉시 응답하지 못한 나머지 복제본은 이후 비동기적으로 동기화 가능</li>
</ul>
<hr>
<h2 id="8-정족수와-데이터-일관성">8. 정족수와 데이터 일관성</h2>
<p>복제된 데이터는 여러 서버에 저장되므로 읽기와 쓰기 과정에서 적절한 동기화 필요.</p>
<h3 id="81-정족수-인자">8.1 정족수 인자</h3>
<h4 id="n">N</h4>
<ul>
<li>데이터 사본의 전체 개수</li>
</ul>
<h4 id="w">W</h4>
<ul>
<li>쓰기 성공에 필요한 서버 응답 수</li>
<li>최소 W개의 복제본이 쓰기 성공을 응답해야 클라이언트에게 성공 반환</li>
</ul>
<h4 id="r">R</h4>
<ul>
<li>읽기 성공에 필요한 서버 응답 수</li>
<li>최소 R개의 복제본으로부터 데이터를 읽어 결과 결정</li>
</ul>
<h3 id="82-정족수-설정-예시">8.2 정족수 설정 예시</h3>
<h4 id="r1-wn"><code>R=1, W=N</code></h4>
<ul>
<li>모든 복제본에 쓰기 완료 후 성공 처리</li>
<li>읽기는 하나의 복제본만 확인</li>
<li>읽기 속도에 유리</li>
<li>쓰기 지연 시간과 장애 민감도 증가 가능</li>
</ul>
<h4 id="w1-rn"><code>W=1, R=N</code></h4>
<ul>
<li>하나의 복제본만 쓰기 성공해도 성공 처리</li>
<li>읽을 때 모든 복제본 확인</li>
<li>쓰기 속도와 가용성에 유리</li>
<li>읽기 비용 증가</li>
</ul>
<h4 id="rwn"><code>R+W&gt;N</code></h4>
<ul>
<li>읽기 정족수와 쓰기 정족수가 최소 한 복제본에서 겹침</li>
<li>최근 쓰기에 참여한 복제본이 읽기 대상에 포함될 가능성 확보</li>
<li>적절한 버전 비교와 충돌 처리 기능을 함께 사용하면 일관성 확보에 유리</li>
</ul>
<h4 id="rw≤n"><code>R+W≤N</code></h4>
<ul>
<li>읽기 대상과 쓰기 대상이 전혀 겹치지 않을 수 있음</li>
<li>최신 쓰기를 보지 못하고 오래된 값을 반환할 가능성 증가</li>
</ul>
<h3 id="83-정족수-조건의-한계">8.3 정족수 조건의 한계</h3>
<ul>
<li><p><code>R+W&gt;N</code>이라는 조건만으로 모든 시스템에서 강한 일관성이 자동 보장되는 것은 아님</p>
</li>
<li><p>다음 요소도 함께 고려해야 함</p>
<ul>
<li>복제본 선택 방식</li>
<li>버전 비교</li>
<li>동시 쓰기 처리</li>
<li>장애 시 대체 노드를 사용하는 느슨한 정족수</li>
<li>읽기 복구</li>
<li>쓰기 순서 보장</li>
</ul>
</li>
</ul>
<p>Amazon Dynamo는 높은 가용성을 우선하므로 정족수, 버전 관리 및 비동기 복구를 사용하면서도 최종 일관성 모델을 채택함.</p>
<hr>
<h2 id="9-일관성-모델">9. 일관성 모델</h2>
<h3 id="91-강한-일관성">9.1 강한 일관성</h3>
<ul>
<li>읽기 요청에 완료된 최신 쓰기 결과 반환</li>
<li>클라이언트가 오래된 데이터를 보지 않도록 보장</li>
<li>읽기와 쓰기의 순서를 조정해야 함</li>
<li>합의, 리더 또는 정족수 등의 동기화 과정 필요</li>
<li>네트워크 장애 시 요청을 거부할 수 있어 가용성 저하 가능</li>
<li>추가 동기화 때문에 응답 지연 시간이 증가할 수 있음</li>
</ul>
<h3 id="92-약한-일관성">9.2 약한 일관성</h3>
<ul>
<li>읽기 요청이 가장 최근에 갱신된 결과를 반환하지 못할 수 있음</li>
<li>복제본 동기화가 끝나기 전에 오래된 값 조회 가능</li>
<li>강한 일관성보다 높은 가용성과 낮은 응답 지연 시간 제공 가능</li>
</ul>
<h3 id="93-최종-일관성">9.3 최종 일관성</h3>
<ul>
<li>약한 일관성의 한 형태</li>
<li>새로운 갱신이 더 이상 발생하지 않으면 모든 복제본이 결국 같은 값으로 수렴</li>
<li>짧은 시간 동안 서로 다른 복제본이 다른 값을 가질 수 있음</li>
<li>높은 가용성과 낮은 지연 시간이 중요한 시스템에 적합</li>
<li>Amazon Dynamo가 채택한 일관성 모델</li>
</ul>
<h3 id="94-우리가-만들-시스템에서는">9.4 우리가 만들 시스템에서는..?</h3>
<ul>
<li>선착순 경품 지급</li>
<li>혹은 정확히 100번째 요청 판별</li>
</ul>
<p>이러한 기능에는 단순히 최신 값을 읽는 것뿐 아니라 다음 기능도 필요함.</p>
<ul>
<li>강한 일관성</li>
<li>원자적 카운터 연산</li>
<li>트랜잭션 또는 동시성 제어</li>
</ul>
<hr>
<h2 id="10-데이터-버저닝">10. 데이터 버저닝</h2>
<h3 id="101-버저닝이-필요한-이유">10.1 버저닝이 필요한 이유</h3>
<ul>
<li>최종 일관성 시스템에서는 여러 복제본이 일시적으로 서로 다른 값 보유 가능</li>
<li>네트워크가 단절된 상태에서 서로 다른 서버가 동일한 키를 동시에 변경할 수 있음</li>
<li>어느 값이 최신인지 또는 두 값이 충돌했는지 판별 필요</li>
</ul>
<h3 id="102-데이터-버전-생성">10.2 데이터 버전 생성</h3>
<ul>
<li>데이터가 변경될 때마다 새로운 버전 생성</li>
<li>이미 생성된 버전의 데이터는 변경하지 않음</li>
<li>여러 버전 사이의 선후 관계 비교 가능</li>
</ul>
<hr>
<h2 id="11-벡터-시계">11. 벡터 시계</h2>
<h3 id="111-벡터-시계란">11.1 벡터 시계란</h3>
<ul>
<li>각 데이터 버전에 <code>[서버, 버전 번호]</code>의 순서쌍을 연결하는 방식</li>
<li>서버마다 자신이 수행한 변경 횟수를 별도로 관리</li>
<li>각 버전이 어떤 서버의 변경을 얼마나 포함하는지 표현</li>
</ul>
<h3 id="112-버전-비교-방법">11.2 버전 비교 방법</h3>
<p>두 버전 X와 Y를 비교할 때 다음 조건을 확인함.</p>
<ul>
<li>X의 모든 서버 카운터가 Y보다 작거나 같음</li>
<li>최소 하나의 서버 카운터는 Y가 더 큼</li>
</ul>
<p>위 조건을 만족하면 Y가 X의 모든 변경을 포함하므로 Y를 X보다 나중 버전으로 판단.</p>
<h3 id="113-선후-관계가-있는-예시">11.3 선후 관계가 있는 예시</h3>
<pre><code class="language-text">X = [(A, 1), (B, 0)]
Y = [(A, 2), (B, 1)]</code></pre>
<ul>
<li>Y의 A 카운터가 X보다 큼</li>
<li>Y의 B 카운터도 X보다 큼</li>
<li>Y가 X의 모든 변경을 포함</li>
<li>Y를 X의 후속 버전으로 판단</li>
</ul>
<h3 id="114-충돌한-버전-예시">11.4 충돌한 버전 예시</h3>
<pre><code class="language-text">X = [(A, 2), (B, 0)]
Y = [(A, 1), (B, 1)]</code></pre>
<ul>
<li>X는 A 서버의 변경을 더 많이 포함</li>
<li>Y는 B 서버의 변경을 더 많이 포함</li>
<li>어느 버전도 상대방의 모든 변경을 포함하지 않음</li>
<li>두 버전은 서로 병렬적으로 생성된 충돌 관계</li>
</ul>
<h3 id="115-벡터-시계의-역할">11.5 벡터 시계의 역할</h3>
<ul>
<li>버전 사이의 선후 관계 판별</li>
<li>동시 변경으로 발생한 충돌 탐지</li>
<li>충돌을 발견할 수 있지만 어떤 버전을 선택할지는 자동으로 결정하지 않음</li>
</ul>
<h3 id="116-충돌-해결-방법">11.6 충돌 해결 방법</h3>
<ul>
<li>사용자 또는 애플리케이션이 하나의 버전 선택</li>
<li>여러 버전의 내용을 병합</li>
<li>집합처럼 합칠 수 있는 데이터는 합집합 사용</li>
<li>업무 규칙을 이용한 서버 측 병합</li>
<li>마지막 쓰기 우선 방식 적용 가능</li>
</ul>
<h3 id="117-단점">11.7 단점</h3>
<ul>
<li>Dynamo에서는 여러 버전의 충돌 해결 책임이 애플리케이션으로 전달될 수 있음</li>
<li>애플리케이션 또는 클라이언트 구현 복잡도 증가</li>
<li>서버가 많아질수록 <code>[서버, 버전]</code> 순서쌍 증가 가능</li>
<li>벡터 시계의 크기가 지속적으로 커질 수 있어 오래된 항목 정리 필요</li>
</ul>
<hr>
<h1 id="장애-처리">장애 처리</h1>
<h2 id="12-장애-감지">12. 장애 감지</h2>
<h3 id="121-장애-판단의-어려움">12.1 장애 판단의 어려움</h3>
<ul>
<li>분산 시스템에서는 서버 장애와 일시적인 네트워크 지연을 즉시 구분하기 어려움</li>
<li>한 노드의 보고만으로 바로 장애를 확정하면 정상 서버를 장애로 잘못 판단할 수 있음</li>
<li>여러 노드의 관찰이나 반복된 타임아웃을 바탕으로 장애 여부 판단 가능</li>
</ul>
<h3 id="122-전체-노드-간-상태-교환">12.2 전체 노드 간 상태 교환</h3>
<ul>
<li>모든 노드가 서로 상태를 직접 전달하는 방식</li>
<li>노드 수가 적을 때 구조가 단순함</li>
<li>노드 수가 많아지면 통신 연결과 메시지 수가 크게 증가</li>
<li>대규모 분산 시스템에는 비효율적일 수 있음
<img src="https://velog.velcdn.com/images/dus_snp/post/a7d752c9-fee1-48f7-a503-700fa20fcda7/image.png" alt=""></li>
</ul>
<hr>
<h2 id="13-가십-프로토콜">13. 가십 프로토콜</h2>
<h3 id="131-멤버십-목록">13.1 멤버십 목록</h3>
<p>각 노드는 클러스터에 참여하는 노드의 상태를 기록하는 멤버십 목록 유지.</p>
<p>멤버십 목록에는 다음 정보 포함 가능.</p>
<ul>
<li>노드 또는 멤버 ID</li>
<li>박동 카운터</li>
<li>마지막 갱신 시간</li>
</ul>
<h3 id="132-동작-과정">13.2 동작 과정</h3>
<ol>
<li>각 노드가 주기적으로 자신의 박동 카운터 증가</li>
<li>무작위로 다른 노드 선택</li>
<li>책의 설계에서는 선택된 노드와 멤버십 목록 교환</li>
<li>상대방이 가진 정보와 자신의 정보 비교</li>
<li>더 큰 박동 카운터나 더 최근의 정보를 기준으로 목록 갱신</li>
<li>특정 노드의 박동 카운터가 일정 시간 동안 갱신되지 않으면 장애 의심
<img src="https://velog.velcdn.com/images/dus_snp/post/8e5e1cc6-f6a2-4b61-ae4a-951aa0369b87/image.png" alt=""></li>
</ol>
<h3 id="133-장점">13.3 장점</h3>
<ul>
<li>모든 노드가 모든 노드에 직접 메시지를 보낼 필요 없음</li>
<li>일부 노드와 주기적으로 정보를 교환하면서 상태가 전체 클러스터로 확산</li>
<li>노드 수가 증가해도 비교적 효율적으로 멤버십 정보 전달 가능</li>
</ul>
<hr>
<h2 id="14-일시적인-장애-처리">14. 일시적인 장애 처리</h2>
<h3 id="141-느슨한-정족수">14.1 느슨한 정족수</h3>
<ul>
<li>원래 데이터를 담당하는 서버가 일시적으로 응답하지 않는 상황</li>
<li>정상 상태인 다른 서버를 임시 복제본으로 선택</li>
<li>원래 복제 서버의 복구를 기다리지 않고 쓰기 요청 처리</li>
<li>높은 쓰기 가용성 유지</li>
</ul>
<h3 id="142-단서-후-임시-위탁">14.2 단서 후 임시 위탁</h3>
<ul>
<li>다른 서버가 장애 서버를 대신해 데이터를 임시 저장하는 방식</li>
<li>임시 저장된 데이터에 원래 담당 서버 정보를 함께 기록</li>
<li>이 정보를 단서라고 부름</li>
</ul>
<h3 id="143-단서에-포함되는-정보">14.3 단서에 포함되는 정보</h3>
<ul>
<li>해당 데이터의 원래 담당 서버</li>
<li>전달할 키와 값</li>
<li>데이터 버전</li>
<li>타임스탬프 또는 만료 정보</li>
</ul>
<h3 id="144-단서를-남기는-이유">14.4 단서를 남기는 이유</h3>
<ul>
<li>임시 저장 서버가 해당 데이터가 자신의 정식 데이터인지 구분 가능</li>
<li>장애가 복구된 뒤 어느 서버로 데이터를 전달해야 하는지 확인 가능</li>
<li>원래 서버에 전달한 후 임시 사본 정리 가능</li>
</ul>
<h3 id="145-복구-과정">14.5 복구 과정</h3>
<ol>
<li>원래 담당 서버의 장애 발생</li>
<li>다른 정상 서버가 쓰기를 임시 저장</li>
<li>데이터와 함께 원래 담당 서버에 관한 단서 기록</li>
<li>원래 서버가 복구되었는지 주기적으로 확인</li>
<li>복구되면 임시 저장 데이터를 원래 서버로 전달</li>
<li>전달 완료 후 임시 데이터와 단서 제거</li>
</ol>
<p>Amazon Dynamo는 일시적 장애 상황에서 높은 가용성을 유지하기 위해 느슨한 정족수와 단서 후 임시 위탁을 사용함.</p>
<hr>
<h2 id="15-영구적인-장애-처리">15. 영구적인 장애 처리</h2>
<h3 id="151-반-엔트로피-프로토콜">15.1 반-엔트로피 프로토콜</h3>
<ul>
<li>장기간의 장애나 전달 실패로 서로 달라진 복제본을 동기화하는 과정</li>
<li>각 복제본이 가진 데이터 비교</li>
<li>오래되거나 누락된 데이터를 찾아 최신 상태로 갱신</li>
<li>모든 데이터를 매번 직접 비교하면 네트워크와 디스크 비용이 매우 큼</li>
</ul>
<h3 id="152-머클-트리-활용">15.2 머클 트리 활용</h3>
<ul>
<li>복제본 사이의 차이를 효율적으로 찾기 위해 머클 트리 사용</li>
<li>전체 데이터를 작은 범위로 분할</li>
<li>각 데이터 범위의 해시값 계산</li>
<li>하위 노드의 해시값을 결합하여 상위 노드 해시값 계산</li>
<li>최종적으로 루트 해시값 생성</li>
</ul>
<h3 id="153-머클-트리-비교-과정">15.3 머클 트리 비교 과정</h3>
<ol>
<li>두 서버의 루트 해시값 비교</li>
<li>루트 해시값이 같으면 비교 대상 데이터가 동일한 것으로 판단</li>
<li>루트 해시값이 다르면 자식 노드의 해시값 비교</li>
<li>서로 다른 해시값을 가진 하위 구간으로 이동</li>
<li>실제 데이터가 다른 범위 탐색</li>
<li>차이가 있는 데이터 구간만 전송하여 동기화
<img src="https://velog.velcdn.com/images/dus_snp/post/647274c6-5dd9-4691-8ffb-0d62559a3997/image.png" alt=""></li>
</ol>
<h3 id="154-장점">15.4 장점</h3>
<ul>
<li>전체 데이터를 서버 간에 전송할 필요 없음</li>
<li>서로 다른 데이터 범위만 탐색 가능</li>
<li>네트워크 사용량 감소</li>
<li>대규모 데이터 집합의 비교 비용 감소</li>
</ul>
<p>Amazon Dynamo는 복제본 사이의 불일치를 효율적으로 탐지하기 위한 반-엔트로피 과정에 머클 트리를 활용함.</p>
<hr>
<h1 id="아키텍처">아키텍처</h1>
<h2 id="16-전체-구조">16. 전체 구조</h2>
<h3 id="161-클라이언트">16.1 클라이언트</h3>
<ul>
<li>키-값 저장소의 <code>get</code> 및 <code>put</code> API 호출</li>
<li>특정 저장 노드의 위치를 직접 알 필요 없음</li>
</ul>
<h3 id="162-중재-노드">16.2 중재 노드</h3>
<ul>
<li>클라이언트 요청을 처음 받은 노드</li>
<li>클라이언트의 프록시 또는 코디네이터 역할 수행</li>
<li>키에 대한 담당 복제 서버 확인</li>
<li>읽기와 쓰기 요청 전달</li>
<li>필요한 정족수 응답 수집</li>
<li>충돌한 버전이 있으면 클라이언트 또는 애플리케이션에 전달 가능
<img src="https://velog.velcdn.com/images/dus_snp/post/d1b6755e-0102-4a92-8c50-0655ed27f246/image.png" alt=""></li>
</ul>
<h3 id="163-저장-노드">16.3 저장 노드</h3>
<ul>
<li>안정 해시의 해시 링 위에 분포</li>
<li>자신이 담당하는 파티션의 데이터 저장</li>
<li>다른 노드의 데이터를 복제본으로 저장 가능</li>
</ul>
<h3 id="164-완전-분산-구조">16.4 완전 분산 구조</h3>
<ul>
<li>노드를 자동으로 추가하거나 제거 가능</li>
<li>데이터를 여러 노드에 복제</li>
<li>모든 노드가 동일하거나 유사한 책임 수행</li>
<li>저장소 내부의 단일 장애 지점을 최소화</li>
<li>특정 중앙 서버 하나에 전체 기능이 의존하지 않도록 구성</li>
</ul>
<h3 id="165-각-노드가-수행할-수-있는-기능">16.5 각 노드가 수행할 수 있는 기능</h3>
<ul>
<li>클라이언트 요청 수신</li>
<li>중재 노드 역할</li>
<li>키의 담당 서버 탐색</li>
<li>데이터 읽기와 쓰기</li>
<li>복제본 저장</li>
<li>장애 감지</li>
<li>가십 프로토콜 참여</li>
<li>일시적 장애 데이터 임시 저장</li>
<li>복제본 동기화</li>
</ul>
<hr>
<h1 id="데이터-저장-경로">데이터 저장 경로</h1>
<h2 id="17-쓰기-경로">17. 쓰기 경로</h2>
<h3 id="171-처리-과정">17.1 처리 과정</h3>
<ol>
<li>쓰기 요청 수신</li>
<li>커밋 로그에 요청 순차 기록</li>
<li>데이터를 메모리의 MemTable에 기록</li>
<li>MemTable이 설정된 임계 크기에 도달</li>
<li>MemTable의 데이터를 디스크의 SSTable로 플러시</li>
</ol>
<h3 id="172-커밋-로그">17.2 커밋 로그</h3>
<ul>
<li>쓰기 내용을 디스크에 순차적으로 기록하는 로그</li>
<li>서버가 장애로 재시작되어도 아직 SSTable에 기록되지 않은 데이터 복구 가능</li>
<li>임의 위치를 수정하는 것보다 순차 쓰기를 사용해 디스크 쓰기 효율 향상 가능</li>
</ul>
<h3 id="173-memtable">17.3 MemTable</h3>
<ul>
<li>최근 쓰기 데이터를 임시로 보관하는 메모리 내 자료구조</li>
<li>일반적인 읽기 캐시가 아니라 디스크 기록 전 쓰기 버퍼 역할</li>
<li>키를 기준으로 정렬된 구조로 유지 가능</li>
<li>메모리 임계치에 도달하면 SSTable로 변환하여 디스크에 저장</li>
</ul>
<h3 id="174-sstable">17.4 SSTable</h3>
<ul>
<li>키를 기준으로 정렬된 키-값 쌍을 저장하는 불변 디스크 파일</li>
<li>한 번 생성된 이후 파일 내부 내용을 직접 수정하지 않음</li>
<li>새로운 쓰기나 수정 사항은 새로운 MemTable과 SSTable에 기록</li>
<li>여러 SSTable에 흩어진 오래된 데이터를 합치고 제거하는 컴팩션 과정 필요</li>
</ul>
<p>Cassandra의 공식 저장 엔진 문서에서도 MemTable은 쓰기를 버퍼링하는 메모리 구조이며, 디스크로 플러시되면 불변 SSTable이 된다고 설명함.</p>
<hr>
<h2 id="18-읽기-경로">18. 읽기 경로</h2>
<h3 id="181-기본-처리-과정">18.1 기본 처리 과정</h3>
<ol>
<li>읽기 요청 수신</li>
<li>요청한 키가 MemTable 또는 메모리 캐시에 있는지 확인</li>
<li>메모리에 최신 값이 있으면 반환</li>
<li>메모리에 없으면 디스크의 SSTable 검색</li>
<li>블룸 필터로 키가 확실히 없는 SSTable 제외</li>
<li>키가 있을 가능성이 있는 SSTable의 인덱스와 데이터 확인</li>
<li>여러 버전이 발견되면 버전 또는 타임스탬프 비교</li>
<li>최신 값 반환</li>
</ol>
<h3 id="182-여러-sstable을-확인하는-이유">18.2 여러 SSTable을 확인하는 이유</h3>
<ul>
<li>SSTable은 생성된 이후 수정되지 않음</li>
<li>동일한 키의 서로 다른 버전이 여러 SSTable에 존재할 수 있음</li>
<li>최신 값을 찾기 위해 여러 SSTable을 검사해야 할 수 있음</li>
<li>모든 SSTable을 직접 읽으면 디스크 접근 비용 증가</li>
<li>불필요한 SSTable 접근을 줄이기 위해 블룸 필터 활용</li>
</ul>
<hr>
<h2 id="19-블룸-필터">19. 블룸 필터</h2>
<h3 id="191-블룸-필터란">19.1 블룸 필터란</h3>
<ul>
<li>특정 값이 집합에 존재하는지 빠르게 검사하는 확률적 자료구조</li>
<li>비트 배열과 여러 개의 해시 함수 사용</li>
<li>실제 키 전체를 저장하지 않고 해시 결과만 비트 형태로 표현</li>
<li>적은 메모리로 많은 키의 존재 가능성 검사 가능</li>
</ul>
<p>Redis 공식 문서도 블룸 필터를 적은 고정 메모리로 원소의 집합 포함 여부를 검사하는 확률적 자료구조로 설명함.</p>
<h3 id="192-데이터-추가">19.2 데이터 추가</h3>
<p>초기 비트 배열:</p>
<pre><code class="language-text">[0, 0, 0, 0, 0, 0, 0, 0]</code></pre>
<p><code>apple</code>을 추가한다고 가정함.</p>
<pre><code class="language-text">hash1(apple) → 1번
hash2(apple) → 4번
hash3(apple) → 6번</code></pre>
<p>해당 위치의 비트를 1로 설정:</p>
<pre><code class="language-text">[0, 1, 0, 0, 1, 0, 1, 0]</code></pre>
<h3 id="193-데이터-조회">19.3 데이터 조회</h3>
<p><code>apple</code>을 조회할 때 동일한 해시 함수 적용:</p>
<pre><code class="language-text">hash1(apple) → 1번
hash2(apple) → 4번
hash3(apple) → 6번</code></pre>
<h4 id="모든-비트가-1인-경우">모든 비트가 1인 경우</h4>
<pre><code class="language-text">1번, 4번, 6번이 모두 1
→ 해당 키가 있을 가능성이 있음</code></pre>
<ul>
<li>실제로 키가 존재할 수 있음</li>
<li>다른 키들이 우연히 같은 비트들을 1로 설정했을 수도 있음</li>
<li>실제 SSTable 조회 필요</li>
</ul>
<h4 id="하나라도-0인-경우">하나라도 0인 경우</h4>
<pre><code class="language-text">1번, 4번, 6번 중 하나라도 0
→ 해당 키는 확실히 없음</code></pre>
<ul>
<li>해당 키를 삽입했다면 모든 위치가 1이어야 함</li>
<li>하나라도 0이면 해당 SSTable을 검색할 필요 없음</li>
</ul>
<h3 id="194-블룸-필터의-특성">19.4 블룸 필터의 특성</h3>
<h4 id="거짓-양성-가능">거짓 양성 가능</h4>
<ul>
<li>블룸 필터가 있다고 판단했지만 실제 데이터에는 없을 수 있음</li>
<li>해시 충돌로 다른 키들이 동일한 비트를 1로 만들 수 있기 때문</li>
<li>블룸 필터의 양성 결과 이후 실제 데이터 확인 필요</li>
</ul>
<h4 id="거짓-음성-없음">거짓 음성 없음</h4>
<ul>
<li>일반적인 블룸 필터는 없다고 판단한 데이터가 실제로 존재하는 상황이 발생하지 않음</li>
<li>하나의 비트라도 0이면 해당 키는 확실히 없음</li>
</ul>
<p>블룸 필터는 거짓 양성이 발생할 수 있지만 일반적인 구현에서는 거짓 음성이 발생하지 않음.</p>
<h3 id="195-sstable에서의-역할">19.5 SSTable에서의 역할</h3>
<ul>
<li>블룸 필터가 키의 정확한 저장 위치를 알려주는 것은 아님</li>
<li>각 SSTable에 특정 키가 존재할 가능성만 판단</li>
<li>키가 확실히 없는 SSTable을 검색 대상에서 제외</li>
<li>있을 가능성이 있는 SSTable만 실제로 조회</li>
<li>불필요한 디스크 접근 감소</li>
<li>읽기 응답 시간 개선</li>
</ul>
<hr>
<h2 id="20-전체-정리">20. 전체 정리</h2>
<h3 id="데이터-분산">데이터 분산</h3>
<ul>
<li>안정 해시로 키와 서버를 해시 링에 배치</li>
<li>서버 추가와 제거 시 이동하는 데이터 최소화</li>
<li>가상 노드로 서버별 부하와 데이터 분포 균등화</li>
</ul>
<h3 id="데이터-복제">데이터 복제</h3>
<ul>
<li>동일한 데이터를 서로 다른 N개의 물리 서버에 저장</li>
<li>일부 서버 장애 시에도 다른 복제본을 통해 데이터 접근 가능</li>
</ul>
<h3 id="데이터-일관성">데이터 일관성</h3>
<ul>
<li>R, W, N 값을 조정하여 읽기·쓰기 성능과 일관성 수준 조절</li>
<li><code>R+W&gt;N</code>은 읽기와 쓰기 정족수의 교집합을 만드는 조건</li>
<li>이 조건만으로 강한 일관성이 자동 보장되지는 않음</li>
<li>Amazon Dynamo는 높은 가용성과 최종 일관성을 우선</li>
</ul>
<h3 id="충돌-처리">충돌 처리</h3>
<ul>
<li>벡터 시계로 버전 사이의 선후 관계와 동시 변경 탐지</li>
<li>충돌한 데이터는 애플리케이션 또는 서버의 정책으로 병합</li>
</ul>
<h3 id="장애-감지와-복구">장애 감지와 복구</h3>
<ul>
<li>가십 프로토콜로 노드 상태 정보 전파</li>
<li>일시적 장애에는 느슨한 정족수와 단서 후 임시 위탁 사용</li>
<li>장기적인 데이터 불일치는 반-엔트로피와 머클 트리로 복구</li>
</ul>
<h3 id="데이터-저장">데이터 저장</h3>
<ul>
<li>쓰기 요청을 커밋 로그와 MemTable에 기록</li>
<li>MemTable이 가득 차면 불변 SSTable로 디스크에 저장</li>
<li>여러 SSTable은 컴팩션으로 병합 및 정리</li>
</ul>
<h3 id="데이터-읽기">데이터 읽기</h3>
<ul>
<li>메모리에서 먼저 최신 데이터 확인</li>
<li>블룸 필터로 키가 확실히 없는 SSTable 제외</li>
<li>가능한 SSTable만 조회하여 디스크 접근 최소화</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 5장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-5%EC%9E%A5-%EC%A0%95%EB%A6%AC-6dmf5n8j</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-5%EC%9E%A5-%EC%A0%95%EB%A6%AC-6dmf5n8j</guid>
            <pubDate>Thu, 23 Jul 2026 07:49:31 GMT</pubDate>
            <description><![CDATA[<h1 id="안정-해시">안정 해시</h1>
<h2 id="1-안정-해시가-필요한-이유">1. 안정 해시가 필요한 이유</h2>
<h3 id="11-데이터의-균등-분산">1.1 데이터의 균등 분산</h3>
<ul>
<li>수평적 규모 확장을 위해 요청 또는 데이터를 여러 서버에 균등하게 분산할 필요가 있음</li>
<li>특정 서버에 부하가 집중되면 해당 서버의 처리량과 저장 공간이 먼저 소진될 수 있음</li>
</ul>
<h3 id="12-나머지-연산을-이용한-해시-방식">1.2 나머지 연산을 이용한 해시 방식</h3>
<ul>
<li>일반적인 데이터 분산 방식</li>
</ul>
<p><code>서버 인덱스 = hash(key) % 서버 개수</code></p>
<ul>
<li>키의 해시값을 서버 개수로 나눈 나머지를 이용해 담당 서버 결정</li>
<li>서버 목록이 고정되어 있는 경우 비교적 간단하게 사용 가능</li>
</ul>
<h3 id="13-서버-수-변경-시-발생하는-문제">1.3 서버 수 변경 시 발생하는 문제</h3>
<ul>
<li>서버가 추가되거나 삭제되면 나머지 연산에 사용되는 서버 개수 변경</li>
<li>기존 키의 해시값이 같더라도 나머지 계산 결과 변경</li>
<li>많은 키가 기존과 다른 서버에 배치됨</li>
</ul>
<h4 id="캐시-시스템에서-발생할-수-있는-문제">캐시 시스템에서 발생할 수 있는 문제</h4>
<ul>
<li>기존 서버에서 캐시 데이터를 찾지 못하는 대규모 캐시 미스 발생</li>
<li>데이터베이스 요청량 급증</li>
<li>서버와 데이터베이스 부하 증가</li>
<li>심한 경우 전체 서비스 장애 발생 가능</li>
</ul>
<hr>
<h2 id="2-안정-해시의-개념">2. 안정 해시의 개념</h2>
<ul>
<li>서버 추가 또는 제거 시 키의 재배치를 최소화하기 위한 해시 기법</li>
<li>전체 키를 다시 배치하지 않고 서버 변경의 영향을 받는 일부 키만 재배치</li>
<li>분산 캐시, 분산 데이터 저장소, 부하 분산 등에 활용 가능</li>
<li>일반적으로 해시 공간을 링 형태로 구성하여 설명하고 구현함</li>
</ul>
<hr>
<h2 id="3-해시-링">3. 해시 링</h2>
<h3 id="31-해시-공간">3.1 해시 공간</h3>
<ul>
<li><p>해시 함수가 생성할 수 있는 모든 해시값의 범위</p>
</li>
<li><p>서버와 키에 동일한 해시 함수를 적용하여 같은 해시 공간에 배치</p>
</li>
<li><p>예를 들어 SHA-1을 사용할 경우 해시값의 범위</p>
</li>
</ul>
<p><code>0 ~ 2¹⁶⁰-1</code></p>
<ul>
<li>안정 해시는 SHA-1만 사용하는 방식이 아님</li>
<li>구현에 따라 다른 해시 함수 사용 가능</li>
</ul>
<h3 id="32-해시-링-구성">3.2 해시 링 구성</h3>
<ul>
<li>해시 공간의 시작점과 끝점을 연결하여 원형 구조로 표현</li>
<li>원형으로 연결된 해시 공간을 해시 링이라고 함</li>
<li>최대 해시값 다음에는 다시 해시값 0으로 이어짐</li>
</ul>
<hr>
<h2 id="4-서버와-키의-배치">4. 서버와 키의 배치</h2>
<h3 id="41-서버-배치">4.1 서버 배치</h3>
<ul>
<li>각 서버의 식별자에 해시 함수 적용</li>
<li>계산된 해시값에 따라 서버를 해시 링 위에 배치</li>
</ul>
<h3 id="42-키-배치">4.2 키 배치</h3>
<ul>
<li>각 키에도 서버와 동일한 해시 함수 적용</li>
<li>계산된 해시값에 따라 키를 해시 링 위에 배치</li>
</ul>
<h3 id="43-담당-서버-결정">4.3 담당 서버 결정</h3>
<ul>
<li>키의 위치에서 링을 시계 방향으로 탐색</li>
<li>처음 만나는 서버를 해당 키의 담당 서버로 결정</li>
<li>링의 끝을 지나면 처음 위치부터 탐색 계속</li>
</ul>
<hr>
<h2 id="5-안정-해시-알고리즘의-기본-절차">5. 안정 해시 알고리즘의 기본 절차</h2>
<ol>
<li>서버와 키에 균등 분포 해시 함수 적용</li>
<li>계산된 해시값을 기준으로 서버와 키를 해시 링에 배치</li>
<li>각 키의 위치에서 시계 방향으로 링 탐색</li>
<li>처음 만나는 서버에 해당 키 저장</li>
</ol>
<hr>
<h2 id="6-서버-추가">6. 서버 추가</h2>
<ul>
<li>새로운 서버의 식별자를 해싱하여 해시 링에 배치</li>
<li>기존 키 대부분은 이전 서버에 그대로 유지</li>
<li>새 서버가 담당하게 된 범위의 키만 재배치</li>
</ul>
<h3 id="61-재배치-범위">6.1 재배치 범위</h3>
<ul>
<li>새 서버의 반시계 방향에 있는 직전 서버와 새 서버 사이의 키들이 재배치 대상</li>
<li>해당 키들은 기존에 새 서버의 시계 방향에 있던 서버가 담당</li>
<li>새 서버 추가 후 해당 키들이 새 서버로 이동</li>
</ul>
<h3 id="62-서버-추가의-효과">6.2 서버 추가의 효과</h3>
<ul>
<li>전체 키를 다시 배치할 필요 없음</li>
<li>새 서버가 담당하는 일부 구간의 키만 이동</li>
<li>나머지 연산 방식보다 데이터 이동량 감소</li>
<li>캐시 시스템에서 대규모 캐시 미스 발생 가능성 감소</li>
</ul>
<hr>
<h2 id="7-서버-제거">7. 서버 제거</h2>
<ul>
<li>제거된 서버가 담당하던 키들만 재배치</li>
<li>제거된 서버의 시계 방향에 있는 다음 서버가 해당 키들을 담당</li>
<li>기존의 다른 서버가 담당하던 키는 그대로 유지</li>
</ul>
<h3 id="71-서버-제거의-효과">7.1 서버 제거의 효과</h3>
<ul>
<li>제거된 서버와 관련된 키만 재배치</li>
<li>전체 키의 재배치 방지</li>
<li>서버 장애 또는 규모 축소 상황에서 데이터 이동량 감소</li>
</ul>
<hr>
<h2 id="8-파티션">8. 파티션</h2>
<h3 id="81-파티션의-개념">8.1 파티션의 개념</h3>
<ul>
<li>해시 링에서 각 서버가 담당하는 해시값의 구간</li>
<li>각 서버는 반시계 방향에 있는 직전 서버부터 자신의 위치까지의 해시 구간 담당</li>
<li>해당 구간에 위치한 키들이 해당 서버에 저장됨</li>
</ul>
<h3 id="82-기본-안정-해시의-한계">8.2 기본 안정 해시의 한계</h3>
<ul>
<li>서버가 해시 링에 균등하게 배치된다는 보장 없음</li>
<li>서버 사이의 거리가 서로 다르게 형성될 수 있음</li>
<li>서버별로 담당하는 파티션 크기가 달라질 수 있음</li>
</ul>
<hr>
<h2 id="9-키-분포의-불균형">9. 키 분포의 불균형</h2>
<ul>
<li>파티션 크기가 다르면 서버별로 담당하는 키의 수에도 차이 발생</li>
<li>일부 서버에 키와 요청이 집중될 가능성 존재</li>
<li>서버가 추가되거나 삭제될 때 파티션 크기의 불균형이 커질 수 있음</li>
<li>기본적인 안정 해시만으로는 키의 균등 분포를 보장하기 어려움</li>
</ul>
<hr>
<h2 id="10-가상-노드">10. 가상 노드</h2>
<h3 id="101-가상-노드의-개념">10.1 가상 노드의 개념</h3>
<ul>
<li><p>기본 안정 해시의 파티션 및 키 분포 불균형을 완화하기 위한 기법</p>
</li>
<li><p>하나의 물리 서버를 해시 링 위의 여러 위치에 배치하는 방식</p>
</li>
<li><p>하나의 물리 서버가 여러 개의 가상 노드를 가질 수 있음</p>
</li>
<li><p>각각의 가상 노드는 서로 다른 해시값을 가지지만 모두 동일한 물리 서버를 가리킴</p>
</li>
<li><p>일부 자료에서는 가상 노드를 복제 노드 또는 <code>replica</code>라고 표현하기도 함</p>
</li>
<li><p>여기서 복제는 동일한 데이터를 여러 서버에 저장하는 데이터 복제와는 다른 개념임</p>
</li>
</ul>
<h3 id="102-가상-노드의-동작">10.2 가상 노드의 동작</h3>
<ol>
<li>물리 서버마다 여러 개의 가상 노드 식별자 생성</li>
<li>각 가상 노드 식별자에 해시 함수 적용</li>
<li>계산된 해시값에 따라 해시 링의 여러 위치에 가상 노드 배치</li>
<li>키의 위치에서 시계 방향으로 가장 먼저 만나는 가상 노드 탐색</li>
<li>실제 데이터는 해당 가상 노드가 가리키는 물리 서버에 저장</li>
</ol>
<hr>
<h2 id="11-가상-노드의-장점">11. 가상 노드의 장점</h2>
<h3 id="111-키의-균등-분산">11.1 키의 균등 분산</h3>
<ul>
<li>하나의 서버가 해시 링의 여러 구간 담당</li>
<li>물리 서버 하나가 하나의 큰 파티션만 담당하는 문제 완화</li>
<li>가상 노드 수가 충분히 많을수록 서버별 담당 범위가 고르게 분산될 가능성 증가</li>
<li>서버별 키 개수와 요청 부하의 편차 감소</li>
</ul>
<h3 id="112-서버-변경-시-재배치-부하-분산">11.2 서버 변경 시 재배치 부하 분산</h3>
<ul>
<li>서버 추가 또는 제거 시 해시 링의 여러 구간에서 키 재배치 발생</li>
<li>특정 서버 하나에 데이터 이동이 집중되는 문제 완화</li>
<li>여러 물리 서버에 재배치 부하 분산 가능</li>
</ul>
<h3 id="113-서버-성능-차이-반영">11.3 서버 성능 차이 반영</h3>
<ul>
<li>성능이 높은 서버에 더 많은 가상 노드 할당 가능</li>
<li>성능이 낮은 서버에는 적은 수의 가상 노드 할당 가능</li>
<li>서버의 CPU, 메모리, 저장 공간 등의 성능 차이를 데이터 분산에 반영 가능</li>
</ul>
<hr>
<h2 id="12-정리">12. 정리</h2>
<h3 id="나머지-연산-방식">나머지 연산 방식</h3>
<ul>
<li>구현이 간단함</li>
<li>서버 수가 변경되면 많은 키의 담당 서버 변경</li>
<li>대규모 캐시 미스와 데이터 재배치 발생 가능</li>
</ul>
<h3 id="기본-안정-해시">기본 안정 해시</h3>
<ul>
<li>서버 추가 또는 제거 시 일부 키만 재배치</li>
<li>데이터 이동량과 캐시 미스 감소</li>
<li>서버별 파티션과 키 분포가 불균등해질 수 있음</li>
</ul>
<h3 id="가상-노드를-적용한-안정-해시">가상 노드를 적용한 안정 해시</h3>
<ul>
<li>하나의 물리 서버를 해시 링의 여러 위치에 배치</li>
<li>키 분포와 재배치 부하를 더욱 균등하게 분산</li>
<li>서버 성능에 따라 가상 노드 수 조절 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 4장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-4%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-4%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 23 Jul 2026 06:10:35 GMT</pubDate>
            <description><![CDATA[<h1 id="처리율-제한-장치">처리율 제한 장치</h1>
<h2 id="1-처리율-제한-장치란">1. 처리율 제한 장치란</h2>
<ul>
<li>클라이언트 또는 서비스가 보내는 트래픽의 처리율을 제어하는 장치</li>
<li>일정 시간 동안 허용되는 요청 수를 제한하는 방식</li>
<li>웹 서버, API 게이트웨이, 로드밸런서, 방화벽 등에 프로그램 로직으로 구현 가능
<img src="https://velog.velcdn.com/images/dus_snp/post/99fe30cd-e409-4f59-9399-59bdb13a2e80/image.png" alt=""></li>
</ul>
<hr>
<h2 id="2-처리율-제한-장치의-장점">2. 처리율 제한 장치의 장점</h2>
<h3 id="21-dos-공격으로-인한-자원-고갈-방지">2.1 DoS 공격으로 인한 자원 고갈 방지</h3>
<ul>
<li>과도한 요청으로부터 서버 자원 보호</li>
<li>CPU, 메모리, 네트워크, 데이터베이스 연결 등의 고갈 방지</li>
<li>악의적인 요청뿐 아니라 비정상적인 클라이언트 동작에도 대응 가능</li>
</ul>
<h3 id="22-비용-절감">2.2 비용 절감</h3>
<ul>
<li>불필요한 서버 확장 방지</li>
<li>유료 외부 API 호출 횟수 제한</li>
<li>처리 가능한 요청만 백엔드 서비스로 전달</li>
</ul>
<h3 id="23-서버-과부하-방지">2.3 서버 과부하 방지</h3>
<ul>
<li>서버가 처리할 수 있는 수준으로 요청량 조절</li>
<li>급격한 트래픽 증가로 인한 장애 방지</li>
<li>서비스 안정성 및 응답 시간 유지</li>
</ul>
<hr>
<h2 id="3-처리율-제한-방식">3. 처리율 제한 방식</h2>
<p>처리율 제한은 요청 속도를 제어하는 주체에 따라 클라이언트 측 제한과 서버 측 제한으로 구분 가능</p>
<h3 id="31-클라이언트-측-제한">3.1 클라이언트 측 제한</h3>
<ul>
<li><p>요청을 보내는 클라이언트가 자체적으로 요청 속도를 조절하는 방식</p>
</li>
<li><p>대표적인 방법</p>
<ul>
<li>디바운싱</li>
<li>스로틀링</li>
<li>재시도 간격 조절</li>
</ul>
</li>
<li><p>서버 부담 감소 가능</p>
</li>
<li><p>불필요한 네트워크 요청 감소 가능</p>
</li>
<li><p>클라이언트 프로그램의 수정 또는 우회 가능</p>
</li>
<li><p>보안 목적의 처리율 제한 수단으로는 신뢰하기 어려움</p>
</li>
</ul>
<h3 id="32-서버-측-제한">3.2 서버 측 제한</h3>
<ul>
<li><p>서버가 들어오는 요청을 직접 검사하고 제한하는 방식</p>
</li>
<li><p>다음과 같은 기준으로 제한 가능</p>
<ul>
<li>IP 주소</li>
<li>사용자 계정</li>
<li>API 키</li>
<li>특정 API 경로</li>
<li>일정 시간 동안의 요청 횟수</li>
</ul>
</li>
<li><p>클라이언트가 임의로 우회하기 어려움</p>
</li>
<li><p>실제 서비스 보호를 위해 필요한 방식</p>
</li>
</ul>
<h3 id="33-클라이언트-측-제한과-서버-측-제한의-병행">3.3 클라이언트 측 제한과 서버 측 제한의 병행</h3>
<ul>
<li><p>실제 서비스에서는 두 방식을 함께 적용 가능</p>
</li>
<li><p>클라이언트 측 제한</p>
<ul>
<li>불필요한 요청 발생 억제</li>
<li>사용자 경험 및 네트워크 효율 개선</li>
</ul>
</li>
<li><p>서버 측 제한</p>
<ul>
<li>클라이언트의 우회 또는 비정상 동작으로부터 시스템 보호</li>
<li>최종적인 처리율 제한 정책 강제</li>
</ul>
</li>
</ul>
<hr>
<h2 id="4-시스템-요구사항">4. 시스템 요구사항</h2>
<h3 id="41-기능-요구사항">4.1 기능 요구사항</h3>
<ul>
<li>서버 측 API를 위한 처리율 제한 장치 설계</li>
<li>다양한 형태의 제어 규칙 지원</li>
<li>요청 제한 시 클라이언트에게 제한 사실 전달</li>
<li>서비스별 처리율 제한 정책 설정 지원</li>
</ul>
<h3 id="42-비기능-요구사항">4.2 비기능 요구사항</h3>
<ul>
<li>대규모 요청 처리 가능</li>
<li>가능한 한 적은 메모리 사용</li>
<li>분산 환경에서 동작</li>
<li>높은 가용성 확보</li>
<li>처리율 제한 기능으로 인한 응답 지연 최소화</li>
</ul>
<h3 id="43-배포-방식">4.3 배포 방식</h3>
<ul>
<li>처리율 제한 장치를 독립된 서비스로 구현 가능</li>
<li>애플리케이션 서버 또는 API 게이트웨이에 종속된 기능으로 구현 가능</li>
<li>시스템 환경, 운영 규모, 성능 요구사항에 따라 적절한 방식 선택 필요</li>
</ul>
<h4 id="독립된-서비스로-구현하는-경우">독립된 서비스로 구현하는 경우</h4>
<ul>
<li>여러 서비스의 처리율 제한 정책을 중앙에서 관리 가능</li>
<li>처리율 제한 로직을 각 애플리케이션과 분리 가능</li>
<li>여러 서비스에서 공통 기능으로 재사용 가능</li>
<li>처리율 제한 기능만 독립적으로 확장하거나 배포 가능</li>
</ul>
<h4 id="애플리케이션-또는-api-게이트웨이에-포함하는-경우">애플리케이션 또는 API 게이트웨이에 포함하는 경우</h4>
<ul>
<li>별도의 네트워크 호출이 필요하지 않아 지연 시간 감소 가능</li>
<li>시스템 구성과 운영 복잡도 감소</li>
<li>애플리케이션 또는 게이트웨이의 기존 인증·라우팅 정보 활용 가능</li>
<li>소규모 시스템이나 서비스별 정책이 다른 환경에 적용하기 쉬움</li>
</ul>
<h4 id="선택-시-고려-사항">선택 시 고려 사항</h4>
<ul>
<li><p>독립된 서비스는 중앙 관리와 재사용에 유리하지만 추가적인 네트워크 지연과 장애 지점 발생 가능</p>
</li>
<li><p>종속된 방식은 구현이 단순하지만 서비스마다 로직이 중복될 가능성 존재</p>
</li>
<li><p>독립 여부와 관계없이 처리율 제한 장치 장애에 대한 정책 필요</p>
<ul>
<li>장애 발생 시 요청을 허용하는 <code>fail-open</code></li>
<li>장애 발생 시 요청을 차단하는 <code>fail-closed</code></li>
</ul>
</li>
</ul>
<hr>
<h2 id="5-api-게이트웨이와-처리율-제한">5. API 게이트웨이와 처리율 제한</h2>
<h3 id="51-api-게이트웨이의-역할">5.1 API 게이트웨이의 역할</h3>
<ul>
<li><p>클라이언트와 백엔드 서비스 사이에서 API 요청을 중계하는 컴포넌트</p>
</li>
<li><p>여러 서비스에 공통으로 필요한 기능 처리</p>
<ul>
<li>요청 라우팅</li>
<li>처리율 제한</li>
<li>사용자 인증 및 인가</li>
<li>SSL/TLS 종단</li>
<li>API 키 검증</li>
<li>로깅 및 모니터링</li>
</ul>
</li>
<li><p>처리율 제한은 API 게이트웨이가 제공할 수 있는 기능 중 하나임</p>
</li>
</ul>
<h3 id="52-완전-관리형-api-게이트웨이">5.2 완전 관리형 API 게이트웨이</h3>
<ul>
<li>클라우드 사업자가 API 게이트웨이를 완전 관리형 서비스로 제공하는 경우가 많음</li>
<li>서버 설치와 운영 부담 감소</li>
<li>처리율 제한, 인증, 모니터링 등의 기능을 설정 방식으로 적용 가능</li>
<li>사용하는 제품에 따라 지원 기능과 처리율 제한 알고리즘에 차이 발생 가능</li>
</ul>
<h3 id="53-msa에서의-활용">5.3 MSA에서의 활용</h3>
<ul>
<li>MSA에서는 처리율 제한 장치를 API 게이트웨이에 구현하는 경우가 많음</li>
<li>클라이언트 요청이 각 마이크로서비스에 도달하기 전에 공통 정책 적용 가능</li>
<li>각 마이크로서비스에 처리율 제한 로직을 중복 구현하는 문제 감소</li>
<li>서비스 전체의 처리율 제한 정책을 중앙에서 관리 가능</li>
</ul>
<h3 id="54-처리율-제한-장치의-위치를-결정할-때-고려할-사항">5.4 처리율 제한 장치의 위치를 결정할 때 고려할 사항</h3>
<ul>
<li>처리율 제한 장치를 어디에 배치할지는 서비스 상황과 목표에 따라 결정</li>
<li>모든 시스템에 적용되는 단일한 정답은 없음</li>
</ul>
<h4 id="구현-환경">구현 환경</h4>
<ul>
<li>현재 사용하는 프로그래밍 언어와 프레임워크가 서버 측 처리율 제한 구현에 적합한지 확인</li>
<li>높은 요청량에서도 충분한 성능을 제공할 수 있는지 확인</li>
</ul>
<h4 id="알고리즘-선택권">알고리즘 선택권</h4>
<ul>
<li>직접 구현할 경우 필요한 알고리즘을 자유롭게 선택 가능</li>
<li>외부 API 게이트웨이를 사용할 경우 제공되는 알고리즘과 설정 방식에 제약 발생 가능</li>
</ul>
<h4 id="기존-아키텍처">기존 아키텍처</h4>
<ul>
<li>MSA에 API 게이트웨이가 이미 포함된 경우 게이트웨이에 처리율 제한 기능 추가 가능</li>
<li>공통 정책의 중앙 관리 가능</li>
</ul>
<h4 id="개발-및-운영-인력">개발 및 운영 인력</h4>
<ul>
<li>직접 구현하고 운영할 인력이 부족한 경우 상용 API 게이트웨이 활용 가능</li>
<li>개발 비용과 운영 복잡도 감소 가능</li>
</ul>
<hr>
<h2 id="6-처리율-제한-알고리즘">6. 처리율 제한 알고리즘</h2>
<ul>
<li>토큰 버킷</li>
<li>누출 버킷</li>
<li>고정 윈도 카운터</li>
<li>이동 윈도 로그</li>
<li>이동 윈도 카운터</li>
</ul>
<hr>
<h2 id="7-토큰-버킷-알고리즘">7. 토큰 버킷 알고리즘</h2>
<h3 id="71-기본-개념">7.1 기본 개념</h3>
<ul>
<li>지정된 용량을 갖는 버킷 사용</li>
<li>버킷 내부에 요청 처리 권한을 의미하는 토큰 저장</li>
<li>사전에 설정된 공급률에 따라 토큰 추가</li>
<li>버킷이 가득 찬 경우 추가 토큰 폐기</li>
</ul>
<h3 id="72-동작-원리">7.2 동작 원리</h3>
<ol>
<li>요청 도착</li>
<li>버킷에 토큰이 있는지 확인</li>
<li>토큰이 있으면 토큰 하나 소비</li>
<li>요청을 시스템에 전달</li>
<li>토큰이 없으면 요청 거부 또는 폐기</li>
</ol>
<h3 id="73-주요-인자">7.3 주요 인자</h3>
<h4 id="버킷-크기">버킷 크기</h4>
<ul>
<li>버킷에 저장할 수 있는 토큰의 최대 개수</li>
<li>순간적으로 허용할 수 있는 최대 요청량과 관련됨</li>
</ul>
<h4 id="토큰-공급률">토큰 공급률</h4>
<ul>
<li>일정 시간 마다 버킷에 추가되는 토큰 수</li>
<li>장기적으로 허용되는 평균 요청 처리율과 관련됨</li>
</ul>
<h3 id="74-버킷-구성">7.4 버킷 구성</h3>
<ul>
<li><p>제한 정책에 따라 별도의 버킷 구성 가능</p>
</li>
<li><p>버킷을 구분할 수 있는 기준</p>
<ul>
<li>사용자</li>
<li>IP 주소</li>
<li>API 키</li>
<li>API 엔드포인트</li>
<li>서비스</li>
</ul>
</li>
<li><p><code>사용자 ID + API 경로</code>처럼 여러 기준을 조합하여 버킷을 구분할 수도 있음</p>
</li>
</ul>
<h3 id="75-장점">7.5 장점</h3>
<ul>
<li>구현이 비교적 간단함</li>
<li>메모리 사용량이 적음</li>
<li>일정 범위의 순간적인 트래픽 증가 허용 가능</li>
<li>평균 처리율을 유지하면서 버스트 트래픽 처리 가능</li>
</ul>
<h3 id="76-단점">7.6 단점</h3>
<ul>
<li>버킷 크기와 토큰 공급률의 적절한 설정이 어려움</li>
<li>잘못된 인자 설정 시 정상 요청이 과도하게 거부될 수 있음</li>
</ul>
<hr>
<h2 id="8-누출-버킷-알고리즘">8. 누출 버킷 알고리즘</h2>
<h3 id="81-기본-개념">8.1 기본 개념</h3>
<ul>
<li>요청을 큐에 저장한 후 일정한 속도로 처리</li>
<li>일반적으로 FIFO 큐로 구현</li>
<li>출력 처리율이 일정하게 유지됨</li>
</ul>
<h3 id="82-동작-원리">8.2 동작 원리</h3>
<ol>
<li>요청 도착</li>
<li>큐에 빈자리가 있는지 확인</li>
<li>빈자리가 있으면 요청을 큐에 추가</li>
<li>큐가 가득 찬 경우 새로운 요청 폐기</li>
<li>일정한 주기마다 큐에서 요청을 꺼내 처리</li>
</ol>
<h3 id="83-주요-인자">8.3 주요 인자</h3>
<h4 id="버킷-크기-1">버킷 크기</h4>
<ul>
<li>요청을 저장하는 큐의 최대 크기</li>
</ul>
<h4 id="처리율">처리율</h4>
<ul>
<li>일정 시간 동안 큐에서 처리하는 요청 수</li>
<li>일반적으로 초 단위로 설정</li>
</ul>
<h3 id="84-토큰-버킷과의-차이">8.4 토큰 버킷과의 차이</h3>
<ul>
<li><p>토큰 버킷</p>
<ul>
<li>토큰이 남아 있으면 순간적인 요청 증가 허용</li>
</ul>
</li>
<li><p>누출 버킷</p>
<ul>
<li>요청을 일정한 속도로 꺼내 처리</li>
<li>출력 처리율이 상대적으로 일정함</li>
</ul>
</li>
</ul>
<h3 id="85-장점">8.5 장점</h3>
<ul>
<li>큐 크기가 제한되어 있어 메모리 사용량 예측 가능</li>
<li>일정한 출력 처리율 제공</li>
<li>안정적인 요청 처리 속도가 필요한 시스템에 적합</li>
</ul>
<h3 id="86-단점">8.6 단점</h3>
<ul>
<li>단시간에 트래픽이 몰리면 큐에 오래된 요청 누적</li>
<li>큐가 가득 차면 새로운 요청 폐기</li>
<li>큐 크기와 처리율 설정이 어려움</li>
<li>큐 대기로 인해 응답 지연 증가 가능</li>
</ul>
<hr>
<h2 id="9-고정-윈도-카운터-알고리즘">9. 고정 윈도 카운터 알고리즘</h2>
<h3 id="91-기본-개념">9.1 기본 개념</h3>
<ul>
<li>시간을 일정한 크기의 고정 구간으로 분할</li>
<li>각 구간마다 요청 수를 기록하는 카운터 사용</li>
</ul>
<h3 id="92-동작-원리">9.2 동작 원리</h3>
<ol>
<li>요청 도착</li>
<li>현재 윈도의 카운터를 1 증가</li>
<li>카운터가 설정된 한도 이하이면 요청 허용</li>
<li>한도를 초과하면 새로운 윈도가 시작될 때까지 요청 거부</li>
<li>새로운 윈도가 시작되면 카운터 초기화</li>
</ol>
<h3 id="93-장점">9.3 장점</h3>
<ul>
<li>구현이 간단함</li>
<li>이해하기 쉬움</li>
<li>카운터 하나만 저장하면 되므로 메모리 효율이 높음</li>
<li>일정한 시간 단위로 요청량을 관리하는 정책에 적합</li>
</ul>
<h3 id="94-단점">9.4 단점</h3>
<ul>
<li>윈도 경계에서 처리율 한도를 초과할 수 있음</li>
<li>이전 윈도 마지막 시점과 다음 윈도 시작 시점에 요청이 집중될 경우 문제 발생</li>
<li>실제 연속 시간 기준으로는 설정한 한도보다 많은 요청 허용 가능</li>
</ul>
<hr>
<h2 id="10-이동-윈도-로그-알고리즘">10. 이동 윈도 로그 알고리즘</h2>
<h3 id="101-기본-개념">10.1 기본 개념</h3>
<ul>
<li>각 요청의 타임스탬프를 개별적으로 기록</li>
<li>Redis 정렬 집합과 같은 자료구조 활용 가능</li>
</ul>
<h3 id="102-동작-원리">10.2 동작 원리</h3>
<ol>
<li>새로운 요청 도착</li>
<li>현재 윈도의 시작 시점보다 오래된 타임스탬프 제거</li>
<li>새로운 요청의 타임스탬프를 로그에 추가</li>
<li>로그에 저장된 요청 수 확인</li>
<li>요청 수가 허용치 이하이면 요청 전달</li>
<li>허용치를 초과하면 요청 거부</li>
</ol>
<h3 id="103-장점">10.3 장점</h3>
<ul>
<li>고정 윈도 카운터의 경계 문제 해결</li>
<li>현재 시점을 기준으로 정확한 시간 범위 계산 가능</li>
<li>어느 시점에서 윈도를 계산해도 요청 수가 설정 한도를 넘지 않도록 제어 가능</li>
</ul>
<h3 id="104-단점">10.4 단점</h3>
<ul>
<li>각 요청의 타임스탬프를 저장해야 함</li>
<li>요청량이 많을수록 메모리 사용량 증가</li>
<li>거부된 요청의 타임스탬프까지 저장하는 구현에서는 추가 메모리 사용 발생</li>
</ul>
<hr>
<h2 id="11-이동-윈도-카운터-알고리즘">11. 이동 윈도 카운터 알고리즘</h2>
<h3 id="111-기본-개념">11.1 기본 개념</h3>
<ul>
<li>고정 윈도 카운터와 이동 윈도 로그의 특징을 결합한 방식</li>
<li>현재 윈도와 직전 윈도의 카운터를 이용하여 현재 시간 범위의 요청 수 추정</li>
</ul>
<h3 id="112-동작-원리">11.2 동작 원리</h3>
<ul>
<li>예를 들어 시간을 1분 단위의 고정 구간으로 분할</li>
<li>이전 1분 구간과 현재 1분 구간의 요청 수 저장</li>
<li>이전 구간의 요청을 모두 포함하지 않음</li>
<li>현재 시점과 가까운 부분만 시간 비율에 따라 반영</li>
<li>직전 윈도의 요청이 윈도 전체에 균등하게 분포한다고 가정</li>
</ul>
<h3 id="113-계산-예시">11.3 계산 예시</h3>
<ul>
<li>처리율 제한: 분당 100개</li>
<li>현재 시각: 현재 윈도가 시작된 후 20초 경과</li>
<li>직전 윈도 요청 수: 90개</li>
<li>현재 윈도 요청 수: 30개</li>
<li>직전 윈도에서 현재 이동 윈도와 겹치는 비율: 40초 ÷ 60초</li>
<li>추정 요청 수</li>
</ul>
<p><code>90 × (40 ÷ 60) + 30 = 90개</code></p>
<h3 id="114-장점">11.4 장점</h3>
<ul>
<li>이전 시간대의 평균 처리율에 따라 현재 윈도의 상태 계산</li>
<li>짧은 시간에 몰리는 트래픽에 효과적으로 대응</li>
<li>요청별 타임스탬프를 모두 저장할 필요 없음</li>
<li>이동 윈도 로그보다 메모리 사용량이 적음</li>
<li>고정 윈도의 경계 문제 완화</li>
</ul>
<h3 id="115-단점">11.5 단점</h3>
<ul>
<li>직전 윈도의 요청이 균등하게 분포했다고 가정</li>
<li>실제 요청 분포와 계산된 추정치 사이에 오차 발생 가능</li>
<li>실험 결과에서는 이러한 오차가 심각한 문제로 나타나지 않음</li>
</ul>
<hr>
<h2 id="12-처리율-제한-데이터-저장소">12. 처리율 제한 데이터 저장소</h2>
<h3 id="121-데이터베이스-사용의-한계">12.1 데이터베이스 사용의 한계</h3>
<ul>
<li>처리율 제한 여부를 확인하기 위한 카운터 필요</li>
<li>모든 요청마다 카운터 조회 및 갱신 필요</li>
<li>디스크 기반 데이터베이스 사용 시 지연 시간 증가 가능</li>
<li>높은 요청량에서 데이터베이스 부하 증가 가능</li>
</ul>
<h3 id="122-인메모리-캐시-활용">12.2 인메모리 캐시 활용</h3>
<ul>
<li>일반적으로 Redis와 같은 인메모리 저장소 활용</li>
<li>빠른 읽기 및 쓰기 가능</li>
<li>만료 시간 설정 가능</li>
<li>카운터와 타임스탬프 자료구조 지원</li>
<li>여러 처리율 제한 장치가 데이터를 공유하기 쉬움</li>
</ul>
<hr>
<h2 id="13-처리율-제한-규칙-관리">13. 처리율 제한 규칙 관리</h2>
<h3 id="131-제한-규칙의-구성">13.1 제한 규칙의 구성</h3>
<ul>
<li><p>각 서비스의 요구사항에 따라 규칙 설정</p>
</li>
<li><p>규칙에 포함될 수 있는 항목</p>
<ul>
<li>제한 대상</li>
<li>요청 허용량</li>
<li>시간 윈도 크기</li>
<li>API 경로</li>
<li>사용자 등급</li>
<li>초과 요청 처리 방식</li>
</ul>
</li>
</ul>
<h3 id="132-규칙-저장">13.2 규칙 저장</h3>
<ul>
<li>규칙을 설정 파일 형태로 디스크에 저장 가능</li>
<li>처리율 제한 장치가 설정 파일을 주기적으로 읽어 규칙 갱신 가능</li>
</ul>
<hr>
<h2 id="14-한도-초과-요청-처리">14. 한도 초과 요청 처리</h2>
<h3 id="141-http-상태-코드">14.1 HTTP 상태 코드</h3>
<ul>
<li>처리율 제한을 초과한 요청에 <code>HTTP 429 Too Many Requests</code> 응답 반환</li>
<li>클라이언트에게 요청 한도 초과 사실 전달</li>
</ul>
<h3 id="142-응답-헤더">14.2 응답 헤더</h3>
<ul>
<li><p>클라이언트가 처리율 제한 상태를 확인할 수 있도록 응답 헤더 제공 가능</p>
</li>
<li><p>전달 가능한 정보</p>
<ul>
<li>윈도당 허용되는 전체 요청 수</li>
<li>현재 윈도에 남아 있는 요청 수</li>
<li>다음 요청을 시도할 수 있는 시점</li>
<li>재시도까지 기다려야 하는 시간</li>
</ul>
</li>
</ul>
<h3 id="143-초과-요청의-보관">14.3 초과 요청의 보관</h3>
<ul>
<li>제한된 요청을 즉시 폐기하지 않고 메시지 큐에 저장 가능</li>
<li>나중에 비동기 방식으로 처리 가능</li>
<li>모든 API 요청에 적용되는 방식은 아님</li>
<li>주문, 작업 실행 등 비동기 처리가 가능한 요청에 적합
<img src="https://velog.velcdn.com/images/dus_snp/post/c34741c2-574f-47f8-81d5-30be0f264925/image.png" alt=""></li>
</ul>
<hr>
<h2 id="15-분산-환경에서의-처리율-제한">15. 분산 환경에서의 처리율 제한</h2>
<h3 id="151-주요-문제">15.1 주요 문제</h3>
<ul>
<li><p>여러 처리율 제한 장치가 동시에 같은 카운터를 갱신하는 문제 발생</p>
</li>
<li><p>대표적인 문제</p>
<ul>
<li>경쟁 조건</li>
<li>서버 간 상태 동기화</li>
</ul>
</li>
</ul>
<hr>
<h2 id="16-경쟁-조건">16. 경쟁 조건</h2>
<h3 id="161-경쟁-조건의-발생-과정">16.1 경쟁 조건의 발생 과정</h3>
<ol>
<li>현재 카운터 값 조회</li>
<li>한도 초과 여부 확인</li>
<li>카운터 값 증가</li>
</ol>
<ul>
<li>여러 요청이 위 과정을 동시에 수행할 경우 동일한 카운터 값을 읽을 수 있음</li>
<li>실제 요청 수보다 카운터가 적게 증가하는 문제 발생 가능</li>
<li>허용 한도를 초과한 요청이 통과할 수 있음</li>
</ul>
<h3 id="162-락-사용">16.2 락 사용</h3>
<ul>
<li>공유 데이터에 락을 적용하여 한 번에 하나의 요청만 수정하도록 제한 가능</li>
<li>경쟁 조건 방지 가능</li>
<li>높은 동시성 환경에서는 대기 시간과 성능 저하 발생 가능</li>
</ul>
<h3 id="163-lua-스크립트와-redis-정렬-집합">16.3 Lua 스크립트와 Redis 정렬 집합</h3>
<ul>
<li>앞서 설명한 설계에서는 경쟁 조건의 해결책으로 Lua 스크립트와 Redis의 정렬 집합 활용 가능</li>
</ul>
<h4 id="lua-스크립트">Lua 스크립트</h4>
<ul>
<li>조회, 비교, 증가 작업을 하나의 Lua 스크립트에 포함</li>
<li>여러 Redis 명령을 하나의 작업처럼 실행</li>
<li>Redis 서버 내부에서 실행</li>
<li>조회·판단·갱신 과정의 원자적 실행 가능</li>
</ul>
<h4 id="lua-언어">Lua 언어</h4>
<ul>
<li>작고 가벼운 프로그래밍 언어</li>
<li>다른 프로그램 내부에 삽입하여 사용하기 적합</li>
<li>Redis 내부에서 데이터 처리 로직을 실행할 때 활용 가능</li>
</ul>
<h4 id="redis-정렬-집합">Redis 정렬 집합</h4>
<ul>
<li>각 데이터에 점수를 붙여 점수 순서대로 자동 정렬하는 자료구조</li>
<li>처리율 제한에서는 요청 시간을 점수로 저장</li>
<li>특정 시간 범위에 포함되는 요청만 조회 가능</li>
<li>최근 요청 수를 정확하게 계산하는 데 활용 가능</li>
</ul>
<hr>
<h2 id="17-서버-간-상태-동기화">17. 서버 간 상태 동기화</h2>
<h3 id="171-문제-상황">17.1 문제 상황</h3>
<ul>
<li>처리율 제한 장치 서버를 여러 대 운영할 경우 서버마다 서로 다른 카운터를 가질 수 있음</li>
<li>같은 클라이언트의 요청이 여러 서버로 분산되면 전체 요청량을 정확하게 계산하기 어려움</li>
</ul>
<h3 id="172-고정-세션-활용">17.2 고정 세션 활용</h3>
<ul>
<li>같은 클라이언트의 요청을 항상 같은 처리율 제한 장치로 전달</li>
<li>서버별 로컬 카운터 사용 가능</li>
</ul>
<h4 id="한계">한계</h4>
<ul>
<li>특정 서버에 요청이 집중될 수 있음</li>
<li>서버 장애 시 상태 이전이 어려움</li>
<li>확장성과 유연성이 낮음</li>
<li>처리율 제한만을 위해 고정 세션을 사용하는 것은 권장하기 어려움</li>
</ul>
<h3 id="173-중앙-집중형-데이터-저장소-활용">17.3 중앙 집중형 데이터 저장소 활용</h3>
<ul>
<li>Redis와 같은 중앙 집중형 데이터 저장소 사용</li>
<li>여러 처리율 제한 장치가 동일한 카운터 공유</li>
<li>요청이 어느 서버에 도착하더라도 같은 제한 상태 확인 가능</li>
<li>분산 환경에서 비교적 일관된 처리율 제한 제공 가능
<img src="https://velog.velcdn.com/images/dus_snp/post/427e6aa3-1708-4f5a-91e5-a502acf8421d/image.png" alt=""></li>
</ul>
<hr>
<h2 id="18-성능-및-분산-환경-고려-사항">18. 성능 및 분산 환경 고려 사항</h2>
<h3 id="181-여러-데이터센터-지원">18.1 여러 데이터센터 지원</h3>
<ul>
<li>사용자와 가까운 데이터센터에서 요청 처리</li>
<li>네트워크 지연 시간 감소</li>
<li>지역별 처리율 제한 장치 배치 가능</li>
<li>특정 데이터센터에 요청이 집중되는 문제 완화 가능</li>
</ul>
<h3 id="182-최종-일관성-모델-활용">18.2 최종 일관성 모델 활용</h3>
<ul>
<li>데이터센터 사이의 처리율 제한 상태를 비동기적으로 동기화</li>
<li>모든 데이터센터의 상태를 즉시 동일하게 맞추지 않아도 됨</li>
<li>데이터 동기화로 인한 지연 시간과 통신 비용 감소 가능</li>
<li>데이터센터 사이의 상태가 일시적으로 다를 수 있음</li>
<li>동기화가 완료되기 전까지 전체 처리율 한도를 순간적으로 초과할 가능성 존재</li>
<li>정확한 전역 제한보다 낮은 지연 시간과 높은 가용성이 중요한 환경에 적합</li>
</ul>
<hr>
<h2 id="19-모니터링">19. 모니터링</h2>
<h3 id="191-모니터링-목적">19.1 모니터링 목적</h3>
<ul>
<li>처리율 제한 장치가 효과적으로 동작하는지 확인</li>
<li>설정한 알고리즘이 예상대로 동작하는지 확인</li>
<li>처리율 제한 규칙이 지나치게 엄격하거나 느슨하지 않은지 확인</li>
</ul>
<h3 id="192-주요-모니터링-항목">19.2 주요 모니터링 항목</h3>
<ul>
<li>전체 요청 수</li>
<li>허용된 요청 수</li>
<li>거부된 요청 수</li>
<li>HTTP 429 응답 비율</li>
<li>사용자 또는 API별 제한 발생 횟수</li>
<li>처리율 제한 검사 지연 시간</li>
<li>Redis 조회 및 갱신 지연 시간</li>
<li>규칙별 한도 사용률</li>
</ul>
<h3 id="193-모니터링-결과의-활용">19.3 모니터링 결과의 활용</h3>
<ul>
<li>지나치게 많은 정상 요청이 거부되는 경우 제한 규칙 완화</li>
<li>서버 과부하가 계속 발생하는 경우 제한 강화</li>
<li>예상하지 못한 트래픽 패턴 발견</li>
<li>알고리즘 또는 주요 인자 조정</li>
</ul>
<hr>
<h2 id="20-경성-처리율-제한과-연성-처리율-제한">20. 경성 처리율 제한과 연성 처리율 제한</h2>
<h3 id="201-경성-처리율-제한">20.1 경성 처리율 제한</h3>
<ul>
<li>설정된 처리율 한도를 초과하는 요청을 허용하지 않는 방식</li>
<li>한도에 도달하면 이후 요청을 즉시 거부</li>
<li>엄격한 자원 보호가 필요한 경우에 적합</li>
<li>순간적인 트래픽 증가를 허용하기 어려움</li>
</ul>
<h3 id="202-연성-처리율-제한">20.2 연성 처리율 제한</h3>
<ul>
<li>설정된 한도를 일시적으로 초과하는 요청을 일정 범위까지 허용하는 방식</li>
<li>짧은 시간 동안 발생한 버스트 트래픽 수용 가능</li>
<li>사용자 요청을 유연하게 처리할 수 있음</li>
<li>허용 범위를 지나치게 크게 설정하면 서버 과부하 발생 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 3장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-3%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-3%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 09 Jul 2026 07:50:03 GMT</pubDate>
            <description><![CDATA[<h2 id="시스템-설계-면접-공략법">시스템 설계 면접 공략법</h2>
<p>시스템 설계 면접은 다음을 보여주는 자리다.</p>
<ul>
<li>설계 기술을 <strong>시연</strong>하는 자리</li>
<li>설계 과정에서 내린 결정들에 대한 <strong>방어 능력</strong>을 보이는 자리</li>
<li>면접관의 피드백을 <strong>건설적인 방식으로 처리할 자질</strong>이 있음을 보이는 자리</li>
</ul>
<p>즉, 설계 능력의 기술적 측면 외에도 다음과 같은 자질을 함께 평가할 수 있다.</p>
<ul>
<li>협력에 적합한 사람인지</li>
<li>압박이 심한 상황도 잘 헤쳐 나갈 자질이 있는지</li>
<li>모호한 문제를 건설적으로 해결할 능력이 있는지</li>
<li>좋은 질문을 던질 능력이 있는지</li>
</ul>
<hr>
<h2 id="4단계-접근법">4단계 접근법</h2>
<h3 id="1단계-문제-이해-및-설계-범위-확정">1단계. 문제 이해 및 설계 범위 확정</h3>
<p>엔지니어가 가져야 할 중요한 기술 중 하나는 <strong>올바른 질문을 하는 것</strong>, <strong>적절한 가정을 하는 것</strong>, 그리고 <strong>시스템 구축에 필요한 정보를 모으는 것</strong>이다.</p>
<blockquote>
<p>⚠️ 이 단계에서 가장 중요한 것은 <strong>요구사항을 이해하고 모호함을 없애는 것</strong>이다.</p>
</blockquote>
<p>문제를 받자마자 곧바로 답부터 내놓으려 하지 말고, 무엇을 만들어야 하는지부터 명확히 하는 것이 핵심이다.</p>
<h3 id="2단계-개략적인-설계안-제시-및-동의-구하기">2단계. 개략적인 설계안 제시 및 동의 구하기</h3>
<p>설계안에 대한 <strong>최초 청사진</strong>을 제시하고 의견을 구하여, 면접관을 팀원처럼 대한다.</p>
<ul>
<li>핵심 컴포넌트를 포함하는 <strong>다이어그램</strong>을 그린다.</li>
<li>설계안이 관계된 제약사항들을 만족하는지 개략적으로 계산한다.</li>
<li>이런 개략적인 추정이 필요한지는 면접관에게 미리 물어보는 것이 좋다.</li>
</ul>
<h3 id="3단계-상세-설계">3단계. 상세 설계</h3>
<p>앞선 단계를 마쳤다면 전반적으로 달성해야 할 목표와 기능 범위를 확인하여, 전체 설계의 개략적인 청사진과 상세 설계에 집중해야 할 영역을 파악했을 것이다.</p>
<ul>
<li>이 단계에서 할 일은 설계 대상 컴포넌트 사이의 <strong>우선순위를 정하는 것</strong>이다.</li>
<li>사소한 세부사항 설명에 시간을 낭비하지 말 것.</li>
</ul>
<p>한정된 면접 시간 안에서 중요한 컴포넌트에 깊이를 더하는 것이, 모든 부분을 얕게 훑는 것보다 낫다.</p>
<h3 id="4단계-마무리">4단계. 마무리</h3>
<p>완벽한 시스템은 없다.</p>
<ul>
<li>개선할 지점을 찾으라는 주문을 받으면, <strong>비판적 사고 능력</strong>을 보일 수 있는 기회이니 개선할 점을 적극적으로 찾는다.</li>
<li>만든 설계를 다시 <strong>요약</strong>해 주는 것도 도움이 된다. 특히 여러 해결책을 제시한 경우엔 더욱 그렇다.</li>
<li>오류가 발생하면 무슨 일이 생기는지, 운영 이슈, 규모 확장 요구 대처, 세부적 개선 사항 제안 등을 이 단계에서 다루면 좋다.</li>
</ul>
<hr>
<h2 id="정리">정리</h2>
<table>
<thead>
<tr>
<th>단계</th>
<th>핵심</th>
</tr>
</thead>
<tbody><tr>
<td>1. 문제 이해 및 범위 확정</td>
<td>요구사항을 이해하고 모호함을 없앤다</td>
</tr>
<tr>
<td>2. 개략적 설계안 제시</td>
<td>청사진을 그리고 면접관과 합의한다</td>
</tr>
<tr>
<td>3. 상세 설계</td>
<td>중요한 컴포넌트에 우선순위를 두고 깊이 판다</td>
</tr>
<tr>
<td>4. 마무리</td>
<td>개선점을 찾고 설계를 요약한다</td>
</tr>
</tbody></table>
<p>시스템 설계 면접은 정답을 맞히는 시험이 아니라, <strong>문제를 풀어 나가는 과정과 소통 능력</strong>을 보여주는 자리라는 점을 기억해야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 2장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-2%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-2%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 09 Jul 2026 07:26:07 GMT</pubDate>
            <description><![CDATA[<h2 id="개략적인-규모-추정이란">개략적인 규모 추정이란?</h2>
<p>개략적인 규모 추정은 보편적으로 통용되는 성능 수치를 바탕으로 <strong>사고 실험</strong>을 행하여 추정치를 계산하는 행위다. 어떤 설계가 요구사항에 부합할 것인지 가늠해 보기 위한 것이다.</p>
<hr>
<h2 id="2의-제곱수">2의 제곱수</h2>
<p>데이터 크기를 다룰 때는 2의 제곱수 단위를 알아두는 것이 좋다.<img src="https://velog.velcdn.com/images/dus_snp/post/9303a895-9d53-45d0-b756-5bb06cf8239d/image.png" alt=""></p>
<hr>
<h2 id="응답-지연latency-값">응답 지연(Latency) 값</h2>
<p>책에 나온 수치들을 분석한 결과는 다음과 같다.</p>
<ul>
<li><strong>메모리는 빠르지만 디스크는 아직 느리다.</strong></li>
<li>디스크 탐색(seek)은 가능한 한 피해야 한다.</li>
<li>단순한 압축 알고리즘은 빠르다. 데이터를 인터넷으로 전송하기 전에는 가능하면 <strong>압축할 것.</strong></li>
<li>데이터 센터는 보통 여러 지역에 분산되어 있고, 센터들 간에 데이터를 주고받는 데 시간이 걸린다.</li>
</ul>
<hr>
<h2 id="가용성-availability">가용성 (Availability)</h2>
<h3 id="고가용성">고가용성</h3>
<p>시스템이 오랜 시간 동안 지속적으로 중단 없이 운영될 수 있는 능력을 지칭하는 용어다.</p>
<ul>
<li>고가용성은 <strong>%로 표현</strong>한다.</li>
<li>100%는 시스템이 단 한 번도 중단된 적이 없었음을 의미한다.</li>
</ul>
<h3 id="sla-service-level-agreement">SLA (Service Level Agreement)</h3>
<p>서비스 사업자와 고객 사이에 맺어진 합의다.</p>
<ul>
<li>이 합의 안에 사업자가 제공하는 서비스의 <strong>가용 시간</strong>이 공식적으로 기술되어 있다.</li>
<li>관습적으로 숫자 9를 사용해 표시하며, 9가 많을 수록 좋다.
<img src="https://velog.velcdn.com/images/dus_snp/post/e562c8d4-cb38-469f-aa75-cd38759c6d25/image.png" alt=""></li>
</ul>
<hr>
<h2 id="자주-출제되는-추정-문제">자주 출제되는 추정 문제</h2>
<p>많이 출제되는 개략적 규모 추정 문제는 다음을 추정하는 것이다.</p>
<ul>
<li>QPS</li>
<li>최대 QPS (Peak QPS)</li>
<li>저장소 요구량</li>
<li>캐시 요구량</li>
<li>서버 수</li>
</ul>
<blockquote>
<p>💡 추정할 때는 <strong>단위를 붙이는 습관</strong>이 중요하다.</p>
</blockquote>
<hr>
<h2 id="궁금한-점">궁금한 점</h2>
<blockquote>
<p><strong>Q. 트위터 QPS와 저장소 요구량 추정에서 최대 QPS(Peak QPS)는 계산된 QPS에 2를 곱하는 것이 관습인지, 근거가 있는 것인지?</strong></p>
</blockquote>
<p><strong>A.</strong> 관습적인 어림값이다.</p>
<p>트래픽은 하루 종일 일정하지 않고, 사용자가 몰리는 시간대(점심·저녁 등)와 새벽처럼 거의 없는 시간대가 존재한다. 평균값만으로 서버 용량을 정하면 피크 시간대를 감당하지 못하므로, 여유를 두기 위해 평균에 배수를 곱해 최대치를 가늠한다.</p>
<p>여기서 <strong>&quot;×2&quot;는 개략 추정이나 면접에서 편의상 쓰는 관습적 수치</strong>일 뿐, 근거가 있는 공식은 아니다.</p>
<ul>
<li>이벤트성 트래픽(티켓팅, 수강신청, 세일)은 평균의 수십~수백 배까지 튀기도 한다.</li>
<li>트래픽이 고른 서비스는 1.5배 정도로도 충분할 수 있다.</li>
<li>실무에서는 실제 트래픽 로그로 peak/average 비율을 측정해서 배수를 정한다.</li>
</ul>
<p>즉, &quot;최대 QPS = 평균 QPS × 2&quot;는 빠르게 감을 잡기 위한 <strong>rule of thumb</strong>이라고 이해하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대규모 시스템 설계 스터디] 1장 정리]]></title>
            <link>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%9E%A5-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dus_snp/%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%9E%A5-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 09 Jul 2026 07:08:17 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기에-앞서서">들어가기에 앞서서</h1>
<p>이번 방학 기간동안 대규모 시스템 설계 스터디를 진행한다.</p>
<p>교재는 가상 면접 사례로 배우는 대규모 시스템 설계 기초 를 사용했다.</p>
<p>기간은 7월 2일 ~ 9월 12일까지로 예상한다.</p>
<p>각 주마다 2번씩 진행되고, 스터디 과제는 벨로그 작성과 각 단원 문제풀이로 진행하며, 중간마다 시험문제를 출제하여 수준을 검토한다.</p>
<p>최종적으론 대규모 프로젝트를 진행하여 스터디를 마무리한다.</p>
<h2 id="사용자-요청의-종류">사용자 요청의 종류</h2>
<p>사용자 요청에는 크게 <strong>웹 앱</strong>과 <strong>모바일 앱</strong> 두 가지 종류가 있다.</p>
<p>웹 앱은 비즈니스 로직과 데이터 저장 등을 처리하기 위해 서버 구현용 언어(자바, 파이썬 등)를 사용하고, 프레젠테이션 용으로는 클라이언트 구현용 언어(HTML, 자바스크립트 등)를 사용한다.</p>
<p>모바일 앱과 웹 서버 간 통신에는 HTTP 프로토콜을 이용한다. HTTP 프로토콜을 통해 반환되는 응답 데이터의 포맷으로는 보통 JSON이 쓰인다.</p>
<p>서버는 <strong>웹 계층</strong>과 <strong>데이터 계층</strong>으로 분리할 수 있으며, 각 계층을 독립적으로 확장해 나갈 수 있다.</p>
<hr>
<h2 id="데이터베이스">데이터베이스</h2>
<p>DB는 전통적인 <strong>관계형 DB</strong>와 <strong>비관계형 DB</strong>로 나뉜다.</p>
<h3 id="관계형-db-rdbms">관계형 DB (RDBMS)</h3>
<p>관계형 데이터베이스 관리 시스템(RDBMS)이라고도 부른다.</p>
<ul>
<li>대표적으로 MySQL, 오라클 DB, PostgreSQL 등이 있다.</li>
<li>자료를 테이블과 행, 열로 표현한다.</li>
<li>SQL을 사용하면 여러 테이블에 있는 데이터를 그 관계에 따라 조인하여 합칠 수 있다.</li>
</ul>
<h3 id="비관계형-db-nosql">비관계형 DB (NoSQL)</h3>
<p>대표적으로 CouchDB, Neo4j, Cassandra, HBase, Amazon DynamoDB 등이 있다.</p>
<ul>
<li><strong>키-값 저장소, 그래프 저장소, 칼럼 저장소, 문서 저장소</strong> 4가지로 나눌 수 있다.</li>
<li>일반적으로 조인 연산은 지원하지 않는다.</li>
</ul>
<p>NoSQL이 적합한 경우는 다음과 같다.</p>
<ul>
<li>아주 낮은 응답 지연시간이 요구될 때</li>
<li>다루는 데이터가 비정형이라 관계형 데이터가 아닐 때</li>
<li>데이터를 직렬화하거나 역직렬화할 수 있기만 하면 될 때</li>
<li>아주 많은 양의 데이터를 저장해야 할 때</li>
</ul>
<hr>
<h2 id="스케일-업-vs-스케일-아웃">스케일 업 vs 스케일 아웃</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>스케일 업</strong> (수직적 규모 확장)</td>
<td>서버에 고사양 자원(더 좋은 CPU, 더 많은 RAM)을 추가하는 행위</td>
</tr>
<tr>
<td><strong>스케일 아웃</strong> (수평적 규모 확장)</td>
<td>더 많은 서버를 추가하여 성능을 개선하는 행위</td>
</tr>
</tbody></table>
<p>스케일 업은 확장에 한계가 있으며, 장애에 대한 자동복구 방안이나 다중화 방안을 제시하지 못한다.</p>
<hr>
<h2 id="로드밸런서">로드밸런서</h2>
<p>부하분산 집합에 속한 웹 서버들에게 트래픽 부하를 고르게 분산하는 역할을 한다.</p>
<ul>
<li>서버 간 통신에는 보안을 위해 사설 IP 주소를 이용한다.</li>
<li>웹 계층의 부하 분산 및 장애 대응이 가능해지고, 가용성이 향상된다.</li>
</ul>
<h3 id="osi-계층-기준">OSI 계층 기준</h3>
<p>OSI 기준으로 <strong>L4(NLB)</strong>와 <strong>L7(ALB)</strong> 로드밸런서가 있다.</p>
<ul>
<li><strong>L4</strong>: 전송 계층(TCP/UDP)에서 동작한다. IP와 포트 기반으로 트래픽을 분산하며, 패킷 내용을 보지 않아 빠르고 오버헤드가 적다.</li>
<li><strong>L7</strong>: 애플리케이션 계층(HTTP/HTTPS)에서 동작한다. URL 경로, 헤더, 쿠키, 호스트명 등을 보고 라우팅한다. 세밀한 제어가 가능한 대신 상대적으로 무겁다.</li>
</ul>
<h3 id="구현-방식-기준">구현 방식 기준</h3>
<ul>
<li><strong>하드웨어 LB</strong>: 전용 장비를 사용한다. 고성능이지만 비싸고 확장성이 떨어진다.</li>
<li><strong>소프트웨어 LB</strong>: 유연하고 저렴해서 클라우드 환경에 적합하다.</li>
</ul>
<hr>
<h2 id="db-다중화">DB 다중화</h2>
<p>보통 <strong>master-slave</strong> 관계를 설정하고, 원본은 주서버(master)에, 사본은 부서버(slave)에 저장한다.</p>
<ul>
<li>쓰기 연산은 마스터에서만 지원하고, 슬레이브는 사본을 전달받아 읽기 연산만 지원한다.</li>
<li>DB에서는 읽기 연산이 쓰기 연산보다 비중이 크기 때문에, 통상 부데이터베이스 수가 더 많다.</li>
</ul>
<p>이렇게 다중화를 하면 읽기 연산을 병렬 처리할 수 있어 성능이 향상되고, 안정성과 가용성이 높아진다.</p>
<blockquote>
<p>💡 <strong>새로 알게 된 점</strong>
master가 꼭 하나일 필요는 없다.
쓰기 충돌 문제, PK 자동 증가값 충돌, 일관성 관리 복잡도 등의 문제가 있지만, master를 여러 개 둘 수도 있다.</p>
</blockquote>
<hr>
<h2 id="캐시-cache">캐시 (Cache)</h2>
<p>값비싼 연산 결과나 자주 참조되는 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소다.</p>
<ul>
<li>데이터베이스보다 훨씬 빠르고, 데이터베이스의 부담을 덜어주는 장점이 있다.</li>
<li>정적 콘텐츠를 <strong>CDN</strong>으로 옮기면 응답시간을 개선할 수 있다.</li>
</ul>
<p><strong>읽기 주도형 캐시 전략</strong>: 찾는 데이터가 캐시에 없다면 데이터베이스 질의를 통해 데이터를 찾아 캐시에 저장한 뒤 클라이언트에 반환한다.
<img src="https://velog.velcdn.com/images/dus_snp/post/25834b7e-52bb-40e2-ba71-c059b65b83d5/image.png" alt=""></p>
<h3 id="캐시-사용-시-고려할-부분">캐시 사용 시 고려할 부분</h3>
<p>어떤 데이터를 둘지, 보관된 데이터는 어떻게 만료시킬지, 일관성 유지와 장애 대처는 어떻게 할지, 캐시 메모리의 크기를 얼마로 잡고 데이터 방출 정책을 무엇으로 할지 등 (자세한 내용은 책 참조)</p>
<hr>
<h2 id="콘텐츠-전송-네트워크-cdn">콘텐츠 전송 네트워크 (CDN)</h2>
<p>정적 콘텐츠를 전송하는 데 쓰이는, 지리적으로 분산된 서버의 네트워크다.</p>
<ul>
<li>이미지, 비디오, CSS, JavaScript 파일 등을 캐시하며, 동적 콘텐츠도 가능하다.</li>
<li>쉽게 말하면, 사용자에게 가장 가까운 CDN 서버로 안내하여 콘텐츠를 받게 하는 것이다.</li>
</ul>
<h3 id="cdn-사용-시-고려할-부분">CDN 사용 시 고려할 부분</h3>
<p>비용, 적절한 만료 시한 설정, CDN 장애에 대한 대처 방안, 콘텐츠 무효화 방법 등 (자세한 내용은 책 참조)</p>
<hr>
<h2 id="무상태stateless-웹-계층">무상태(Stateless) 웹 계층</h2>
<p>웹 계층을 수평적으로 확장하기 위해서는 <strong>상태 정보를 제거</strong>해야 한다.</p>
<p>상태 정보를 가지면 특정 사용자의 요청이 그 상태 정보를 가진 특정 서버로만 가야 하기 때문에, 수평 확장을 방해한다.</p>
<p>같은 클라이언트의 요청을 같은 서버로 전송시키려면 로드밸런서의 <strong>고정 세션(sticky session)</strong> 기능을 사용하는데, 이는 로드밸런서에 부담을 주고 서버를 추가하고 제거하기도 까다로워진다.</p>
<p>따라서 상태 정보가 필요하면 <strong>지속성 저장소</strong>에 보관하고, 필요할 때 가져오게 하는 것이 좋다.</p>
<hr>
<h2 id="데이터-센터">데이터 센터</h2>
<p>단일 데이터 센터만 이용한다면, 해당 센터에 문제가 생길 시 서비스에 장애가 발생할 수 있다.</p>
<p>이를 방지하기 위해 두 개의 데이터 센터를 이용하고, 장애가 없는 상황에서는 사용자와 가까운 데이터 센터로 안내한다. 이 절차를 <strong>지리적 라우팅</strong>이라고 한다.</p>
<blockquote>
<p><strong>geoDNS</strong>: 사용자 위치에 따라 도메인 이름을 어떤 IP 주소로 변환할지 결정할 수 있도록 해 주는 DNS 서비스</p>
</blockquote>
<p>다중 데이터 센터 아키텍처를 위해 고려할 점으로는 트래픽 우회, 데이터 동기화, 테스트와 배포 등이 있다. (자세한 내용은 책 참조)</p>
<hr>
<h2 id="메시지-큐">메시지 큐</h2>
<p>대규모 분산 시스템에서는 시스템의 컴포넌트들이 각기 독립적으로 확장될 필요가 있다. 이런 문제를 해결하기 위한 핵심 전략 중 하나가 <strong>메시지 큐</strong>다.
<img src="https://velog.velcdn.com/images/dus_snp/post/50da3d84-6440-48d8-8357-7308738b1a54/image.png" alt=""></p>
<p>메시지 큐는 메시지 무손실을 보장하는 비동기 지원 컴포넌트다.</p>
<ul>
<li>이 기능을 사용하면 서비스 또는 서버 간 결합이 느슨해져, 규모 확장성이 보장되어야 하는 안정적인 애플리케이션을 구성하기 좋다.</li>
<li>큐가 커지면 더 많은 작업 프로세스(소비자)를 추가해야 처리 시간을 줄일 수 있고, 큐가 항상 비어 있다면 작업 프로세스의 수를 줄일 수 있다.</li>
</ul>
<hr>
<h2 id="로그-메트릭-자동화">로그, 메트릭, 자동화</h2>
<ul>
<li><strong>로그</strong>: 시스템 오류와 문제들을 찾아내는 데 필수적이다. 서버 단위로 모니터링할 수도 있지만, 로그를 단일 서비스로 모아주는 도구를 활용하면 더 편리하게 검색하고 조회할 수 있다.</li>
<li><strong>메트릭</strong>: 시스템의 현재 상태를 파악할 수 있는 지표다. 사업 현황에 관한 유용한 정보도 얻을 수 있다.</li>
<li><strong>자동화</strong>: 지속적 통합(CI)을 도와주는 도구를 활용하면 코드를 자동으로 검증하여 문제를 쉽게 탐지할 수 있다. 이 외에도 빌드, 테스트, 배포 등을 자동화할 수 있다.</li>
</ul>
<hr>
<h2 id="db-규모-확장">DB 규모 확장</h2>
<p>시스템이 커지면서 저장할 데이터가 많아지면 데이터베이스의 규모도 확장할 필요가 있다.</p>
<p>앞서 말한 수직적, 수평적 방법이 있는데, 수직적으로 하는 경우도 있지만 결국 한계가 있고 장애 위험성과 비용 문제 등이 약점이다.</p>
<h3 id="샤딩-sharding">샤딩 (Sharding)</h3>
<p>DB의 수평적 확장은 <strong>샤딩</strong>이라고도 부른다. 데이터베이스를 <strong>샤드(shard)</strong>라고 부르는 작은 단위로 분할하는 기술을 말한다.</p>
<ul>
<li>모든 샤드는 같은 스키마를 쓰지만, 샤드에 보관되는 데이터 사이에는 중복이 없다.</li>
<li>이때 사용하는 것이 <strong>샤딩 키(파티션 키)</strong>로, 데이터가 어떻게 분산될지 정하는 하나 이상의 칼럼으로 구성된다.</li>
<li>데이터를 고르게 분할할 수 있도록 하는 것이 샤딩 키 선정에서 가장 중요하다.
<img src="https://velog.velcdn.com/images/dus_snp/post/febb62af-4169-400a-ab24-72bd9b043b4c/image.png" alt=""></li>
</ul>
<h3 id="샤딩-도입-시-고려할-점">샤딩 도입 시 고려할 점</h3>
<p>데이터의 재샤딩, 유명인사 문제, 조인과 비정규화 등 (자세한 내용은 책 참조)</p>
<hr>
<h2 id="1장-마무리">1장 마무리</h2>
<p>시스템 규모 확장을 위해 살펴본 기법들을 정리하면 다음과 같다.</p>
<ul>
<li>웹 계층은 <strong>무상태 계층</strong>으로 설계할 것</li>
<li>모든 계층에 <strong>다중화</strong> 도입</li>
<li>가능한 한 많은 데이터를 <strong>캐시</strong>할 것</li>
<li>여러 데이터 센터 지원</li>
<li>정적 콘텐츠는 <strong>CDN</strong>으로 서비스</li>
<li>데이터 계층은 <strong>샤딩</strong>을 통해 규모 확장</li>
<li>각 계층은 독립적 서비스로 분할</li>
<li>시스템을 지속적으로 모니터링하고, 자동화 도구들을 활용할 것</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[랩미팅 논문 발표 요약3]]></title>
            <link>https://velog.io/@dus_snp/%EB%9E%A9%EB%AF%B8%ED%8C%85-%EB%85%BC%EB%AC%B8-%EB%B0%9C%ED%91%9C-%EC%9A%94%EC%95%BD3</link>
            <guid>https://velog.io/@dus_snp/%EB%9E%A9%EB%AF%B8%ED%8C%85-%EB%85%BC%EB%AC%B8-%EB%B0%9C%ED%91%9C-%EC%9A%94%EC%95%BD3</guid>
            <pubDate>Mon, 17 Nov 2025 12:11:37 GMT</pubDate>
            <description><![CDATA[<h3 id="resource-aware-dynamic-scheduling-for-tasks-with-deadline-constraints-on-edge-computing-systems">Resource-Aware Dynamic Scheduling for Tasks with Deadline Constraints on Edge Computing Systems</h3>
<p>IEEE TRANSACTIONS ON CLOUD COMPUTING
Wenbiao Cao, Xiaoyong Tang, Tan Deng, Ronghui Cao, and Keqin Li
<a href="https://ieeexplore.ieee.org/abstract/document/11143954">https://ieeexplore.ieee.org/abstract/document/11143954</a></p>
<h2 id="연구-배경">연구 배경</h2>
<h4 id="aiot-발전으로-mecmulti-access-edge-computing-중요성-증대">AIoT 발전으로 MEC(Multi-access Edge Computing) 중요성 증대</h4>
<p>AIoT와 IoT의 발전으로 데이터 소스와 가까운 위치에서 분산 연산 수요 및 지연 민감 서비스 급증</p>
<p>엣지 컴퓨팅은 코어 혼잡 완화 및 QoS 향상에 효과적
그러나, 엣지 환경은 다음과 같은 한계 존재</p>
<ul>
<li>자원 이질성(Heterogeneity)</li>
<li>자원 경쟁(Resource competition)</li>
<li>마감시간 제약(Deadline Constraints)</li>
</ul>
<p>핵심 과제 : 제한된 이질적 리소스와 동적 요구사항의 충돌
<img src="https://velog.velcdn.com/images/dus_snp/post/8c4f644d-858e-4d03-a9de-bcdcfa5f3a28/image.png" alt=""></p>
<h2 id="해당-논문의-edge-node에-대한-정의">해당 논문의 Edge-Node에 대한 정의</h2>
<p>Firstly, edge nodes have obvious heterogeneity in terms of resource type, performance, and architecture.
Secondly, resource competition is widely present in edge networks.</p>
<p>Such as gateways(access points), local servers, small data centers, and even high-performance terminal devices are geographically distributed.</p>
<h2 id="관련-연구로-보는-주요-문제점">관련 연구로 보는 주요 문제점</h2>
<h4 id="task-dispatching-and-scheduling-in-edge-computing">Task Dispatching and Scheduling in Edge Computing</h4>
<p>대부분 정적인 태스크 처리 시간을 가정하며, 동적 자원 변화 및 경쟁 상황은 충분히 고려하지 못함</p>
<h4 id="heterogeneous-scheduling-with-deadline-constraints">Heterogeneous Scheduling with Deadline Constraints</h4>
<p>대부분 정적 자원 할당(static allocation) 기반이며, 온라인 자원 경쟁(online resource competition) 은 다루지 않음</p>
<h4 id="resource-aware-dynamic-task-scheduling">Resource-aware Dynamic Task Scheduling</h4>
<p>대부분 오프라인 모델에 의존하고 있으며, 이는 실제 동적 변화를 시의적절하게 반영할 수 없음</p>
<p><strong>따라서 자원 인식(Resource-aware) 관점에서 동적 스케줄링 알고리즘을 제안하여 
데드라인을 만족하면서 시스템 자원 효율을 높이고자 함</strong></p>
<h2 id="핵심-아이디어">핵심 아이디어</h2>
<h4 id="ratgsresource-aware-task-grouping-scheduling-strategy">RATGS(Resource-Aware Task Grouping Scheduling Strategy)</h4>
<p>제한된 리소스로 최대한 많은 요청을 완료하는 것이 목표
데드라인 제약이 있는 태스크의 효과적인 스케줄링에 초점</p>
<ol>
<li>Task Regrouping &amp; Priority Response (Algorithm 1)</li>
<li>Resource-aware Greedy Scheduling (Algorithm 2)</li>
<li>Task Adjusting (DI/HRI/MRR, Algorithm 3)</li>
<li>Rescheduling (Algorithm 4)<h2 id="핵심-목표">핵심 목표</h2>
<img src="https://velog.velcdn.com/images/dus_snp/post/3d1dde85-ef6a-4525-bff4-8347f006872c/image.png" alt=""></li>
</ol>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/6012ce63-8870-4666-bab5-57e7d7930b10/image.png" alt="">
식(2) =&gt; 연산 능력 가용 검사
식(3) =&gt; 메모리 가용 검사
*<em>현재 사용중인 {컴퓨팅 파워, 메모리} + 할당하려는 CU의 {컴퓨팅 파워, 메모리} &lt;= 엣지 노드의 총 {컴퓨팅 파워, 메모리} 용량 *</em>
<img src="https://velog.velcdn.com/images/dus_snp/post/b9c3336f-6783-4ca4-9ae2-2526c92f9985/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/259916df-f0e8-405a-b262-ddc81078e70b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/f94717f9-19b5-42c6-888f-56820f74ec2b/image.png" alt="">
<img src="https://velog.velcdn.com/images/dus_snp/post/8a003c4f-c43f-4df8-ab15-9fc83d337d73/image.png" alt="">
<img src="https://velog.velcdn.com/images/dus_snp/post/75013ec4-231e-42f6-a3ba-5a67f6301d03/image.png" alt=""></p>
<h2 id="algorithm">Algorithm</h2>
<h4 id="regrouping의-필요성">Regrouping의 필요성</h4>
<p>초기에는 태스크들이 도착 시간(Arrival Time) 기준으로 단순 저장되기 때문에, 서비스 유형과 자원 요구가 뒤섞여 스케줄링 효율이 저하
-&gt; 태스크를 서비스 유형(Service Type) 및 긴급도(Urgency) 기준으로 Regrouping</p>
<h4 id="algorithm-1">Algorithm 1</h4>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/d9cc8dda-13a1-4abb-b26b-36204b41f2f6/image.png" alt=""></p>
<ol>
<li>태스크를 도착 시간에 따라 타이밍 그룹으로 분할</li>
<li>각 타이밍 그룹의 태스크를 서비스 타입별 컴퓨터 그룹(X_h)에 매핑</li>
<li>그 그룹의 기존 대기열의 각 태스크의 우선순위 및 평균 처리시간 계산</li>
<li>우선순위 값에 따라 새 대기열 생성</li>
<li>해당 서비스가 엣지에서 처리될 수 없으면 리모트 클라우드로 전송</li>
</ol>
<h4 id="algorithm-2">Algorithm 2</h4>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/6824c1bb-24f9-4a81-b3d2-7f11022f9b51/image.png" alt="">
즉시 가용 리소스가 있는 경우 (line 1~9)</p>
<ul>
<li>가장 빠른 완료 시간을 가진 노드에 그리디 스케줄링 (line8~9)</li>
</ul>
<p>리소스가 가득 찬 경우 (line 10~27)</p>
<ul>
<li>각 슬롯 노드에 할당하여 EFT(Earliest Finish Time) 계산 (line 11~13)</li>
</ul>
<p>데드라인 미충족 시 (line 14~27)</p>
<ul>
<li>태스크 조정 호출 (line18~21)</li>
<li>실행가능한 솔루션이 없으면 리모트 클라우드로 스케줄링 (line22~25)</li>
</ul>
<h4 id="algorithm-3">Algorithm 3</h4>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/c63fb4a6-616b-4d8b-9e00-c04aa82aaacb/image.png" alt="">
<strong>1. Direct Insertion(DI) (line 1~6)</strong>
새로운 태스크를 기존 실행 큐(PreScheme) 중간에 삽입하여 데드라인을 만족할 수 있는 위치를 찾음 
특징 : 파편화된 리소스 활용하여 자원 낭비 감소
효과 : 데드라인 만족률 증가
<strong>2. Highest Reward Insertion(HRI) (line 7~15)</strong>
단순히 데드라인만 보는게 아닌 LTSS가 더 커지는 방향으로 태스크를 선택/재배치
특징 : Slack time을 활용하여 기다리면 더 가치있는 태스크를 살림
효과 : 전체 시스템 효용 극대화
<strong>3. Maximum Reward Replacement(MRR) (line 16~20)</strong>
가장 적은 재배치로 데드라인을 만족시키는 대안 탐색
특징 : 큰 구조 변경 없이 효율성 확보
효과 : 안정적이고 현실적인 조정 가능</p>
<h4 id="algorithm-4">Algorithm 4</h4>
<p>Deadline 위반 태스크를 가장 빠른 EFT 노드로 재할당하여 CRD를 향상 시키는 것을 목표</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/f82f7206-812a-4bdc-8a3e-0045efb91b2a/image.png" alt=""></p>
<p>가용 노드 집합 (AvaNodes) 생성 및 EFT 평가 (line 1<del>6)
EFT 최소 노드 선정 (line 7</del>8)
Deadline이내면 노드 갱신 및 우선순위 계산 (line 9<del>13)
충족 불가 시 리모트 클라우드로 전송 (line 14</del>17)</p>
<h2 id="실험-환경">실험 환경</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/bb609caf-aa54-465e-91f4-b3fc3b897733/image.png" alt=""></p>
<h4 id="워크로드">워크로드</h4>
<p>실제 애플리케이션
이미지 처리, ALU 논리 연산, 부동소수점 연산, 얼굴 매칭 등 
Python으로 작성된 여러 프로그램 사용</p>
<p>시뮬레이션 데이터셋
알리바바 클라우드 실제 트레이스 데이터 활용
밀도 데이터셋 : 8개 서브셋 (밀도 1~8), 각 약 10,000개 태스크
분포 데이터셋 : 3개 서브셋 (다양한 분포), 각 약 25,000개 태스크</p>
<p>각 태스크 디스패처는 서로 다른 종류의 애플리케이션을 실행
각 디스패처는 50<del>100ms 간격으로 0</del>1개의 태스크를 오프로딩</p>
<p>각 요청은 입력 메타데이터, 예상 마감시간 (Deadline), 
긴급 여부(Expedited Flag)에 대한 정보 포함</p>
<p>서비스 인스턴스는 모두 pre-warmed 상태로 설정</p>
<h4 id="평가지표">평가지표</h4>
<p>CRD(Completion Rate before Deadline)
LTSS(Long-Term Satisfaction of System Service)
ACT(Average Completion Time)
Resource Utilization + RU Efficiency</p>
<h2 id="비교-대상">비교 대상</h2>
<ul>
<li><strong>Dedas</strong> : 노드의 스케줄을 순차적으로 순회하여 작업에 적합한 삽입 위치 탐색삽입 불가 시, 총 완료 시간을 비용 요소로 고려하여 최적의 스케줄을 얻기 위해 작업을 교체</li>
<li><strong>RTH2S</strong> : 노드를 계층화하여 3단계 우선순위 큐와 EDF(Earliest Deadline First) 방식으로 스케줄링</li>
<li><strong>LSPT (LeastLoad + SRPT)</strong> : 부하가 가장 낮은 노드에 태스크를 보내고, 남은 처리시간이 짧은 순으로 실행</li>
<li><strong>LEDF (LeastLoad + EDF)</strong> : 부하 최소 + 가장 빠른 마감시간 순서로 태스크 할당</li>
<li><strong>SF (Self + FCFS)</strong> : 태스크가 스스로 가장 빠른 완료시간을 예측하여 FCFS로 실행<h2 id="실험-결과">실험 결과</h2>
<h3 id="테스트베드">테스트베드</h3>
<img src="https://velog.velcdn.com/images/dus_snp/post/084eaaf1-b31f-42a1-93d1-9b1bf063fc8f/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/c2a37eeb-afdc-4df3-b57e-cafa3f32b6f8/image.png" alt=""></p>
<h3 id="태스크-도착-밀도">태스크 도착 밀도</h3>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/496cdd09-2c94-4bc7-a0b9-7c7c326d31e7/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/ed1cf67f-231b-487b-9e9f-9ddab061d9fc/image.png" alt=""></p>
<h3 id="태스크-분포">태스크 분포</h3>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/a2adc694-ffd2-4628-a7fa-60f62f018fd2/image.png" alt="">
태스크가어떤 분포로 도착하여도 RATGS가 CRD가 가장 높은 것을 확인 가능
태스크 도착 불확실성을 반영하기 위한 시뮬레이션</p>
<h3 id="컴퓨터-유닛-수에-따른-데드라인-미스율">컴퓨터 유닛 수에 따른 데드라인 미스율</h3>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/c85bc69d-1b47-492c-84c4-c2e0731657e8/image.png" alt="">
RATGS : 22 CU에서 99.9% 완료 (가장 빠름)
다른 전략 : 더 많은 CU 필요
LSPT/LEDF : CU증가해도 ~80% 완료
<strong>즉, RATGS는 자원이 부족한 상황일수록 성능 우위 확인 가능</strong></p>
<h3 id="컴퓨터-유닛-수에-따른-ltss">컴퓨터 유닛 수에 따른 LTSS</h3>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/5f681b80-ce48-490f-a358-823c9855edf6/image.png" alt="">
컴퓨팅 유닛 수가 30에 도달하면 각 스케줄링 전략의 성능이 가장 좋아지는 경향이 있지만, 
22 미만이면 RATGS의 성능이 LTSS 지표에서 모든 전략보다 우수</p>
<h3 id="컴퓨터-유닛-수에-따른-act">컴퓨터 유닛 수에 따른 ACT</h3>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/779bd558-34e0-404e-9fb5-94adbb0f8bcb/image.png" alt="">
컴퓨팅 유닛 수가 30을 초과하고 점진적으로 증가함에 따라 RATGS는 ACT에서 Dedas보다 약간 더 나은 성능을 보이기 시작
컴퓨팅 유닛 수가 증가함에 따라 최적의 스케줄링을 달성하기 위한 작업 조정 공간과 선택 사항이 더 넓어지기 때문</p>
<h3 id="리소스-활용">리소스 활용</h3>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/e22dd327-9af3-45ae-bd23-08ade880f177/image.png" alt="">
<strong>RU(Resource Utilization)</strong>
LSPT/LEDF: 평균 80% 이상 (최고) -&gt;로드밸런싱 계열
RATGS: 고밀도 시 높음, 저밀도 시 낮음 
<strong>RUE (RU Efficiency = CRD/RU)</strong>
RATGS: 대조적 상황에서 최고 RUE 
적은 리소스로 더 많은 태스크 완료 
<strong>핵심발견</strong>
엣지 환경과 같은 자원이 제한된 환경에선 RUE가 더 중요한 지표
단순 활용보다 활용 효율성이 중요
RATGS는 제한된 리소스를 가장 효율적으로 활용</p>
<h2 id="연구의-핵심-성과">연구의 핵심 성과</h2>
<ul>
<li>이질적 자원 환경을 현실적으로 모델링<ul>
<li>노드별 성능, 메모리, 대역폭 등을 구체적으로 고려</li>
</ul>
</li>
<li>다항시간 복잡도의 휴리스틱 솔루션 제시<ul>
<li>NP-hard 문제를 O(n²) 내에서 근사적으로 해결</li>
</ul>
</li>
<li>마감시간 제약과 자원 경쟁을 동시에 고려한 스케줄링 정식화<ul>
<li>LTSS와 CRD를 통합적으로 최적화</li>
</ul>
</li>
<li>단순한 RU보다 RUE의 성능지표로서의 가치<ul>
<li>자원이 한정된 환경에서 RUE가 성능지표로 더 적합한 것을 확인<h2 id="연구의-한계점-및-향후-연구">연구의 한계점 및 향후 연구</h2>
</li>
</ul>
</li>
<li>태스크 간 종속성 미고려</li>
<li>에너지 소비와 콜드 스타트로 인한 서비스 지연 무시</li>
</ul>
<h4 id="향후-연구">향후 연구</h4>
<ul>
<li>태스크 간 선후 관계를 반영한 스케줄링 모델</li>
<li>에너지 효율성 고려</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[랩미팅 논문 발표 요약2]]></title>
            <link>https://velog.io/@dus_snp/%EB%9E%A9%EB%AF%B8%ED%8C%85-%EB%85%BC%EB%AC%B8-%EB%B0%9C%ED%91%9C-%EC%9A%94%EC%95%BD2</link>
            <guid>https://velog.io/@dus_snp/%EB%9E%A9%EB%AF%B8%ED%8C%85-%EB%85%BC%EB%AC%B8-%EB%B0%9C%ED%91%9C-%EC%9A%94%EC%95%BD2</guid>
            <pubDate>Tue, 11 Nov 2025 10:17:52 GMT</pubDate>
            <description><![CDATA[<p>A Price-and-Time-Slot-Negotiation Mechanism for Cloud Service Reservations
IEEETRANSACTIONS ONSYSTEMS,MAN,ANDCYBERNETICS—PARTB:CYBERNETICS,VOL.42,NO.3,JUNE2012
 Seokho Son and Kwang Mong Sim, _ Senior Member, IEEE_ 
 <a href="https://ieeexplore.ieee.org/abstract/document/6108366">https://ieeexplore.ieee.org/abstract/document/6108366</a></p>
<h2 id="문제-제기-및-연구의-필요성">문제 제기 및 연구의 필요성</h2>
<h4 id="기존-모델의-한계와-ptn-지원의-부재">기존 모델의 한계와 PTN 지원의 부재</h4>
<p>상업 클라우드 서비스 제공 업체들의 대표적인 세 가지 가격 모델은 온디맨드, 예약, 스팟 인스턴스로 나눌 수 있음
이 중 온디맨드와 예약 인스턴스는 고정된 가격을 지불해야하며 유동성이 적음. 스팟 인스턴스는 비용 절약이 가능하지만 서비스 시작, 종료 시점 계획이 어렵고 최대 입찰 가격 초과 시 경고 없이 인스턴스가 종료되는 큰 단점이 존재함</p>
<p>이로 인해 중단 없이 선호하는 시간대 예약을 위한 협상 메커니즘이 필요한데, 아직까지 서비스 가격과 시간대 협상을 자동화하는 메커니즘이 없음
따라서 연구를 통해 Price and Time slot Negotiation(PTN) 메커니즘을 개발함</p>
<h2 id="연구-목표">연구 목표</h2>
<ol>
<li>시간대 선호도를 특성화하는 새로운 효용함수를 포함한 PTN 메커니즘 개발</li>
<li>소비자와 제공자의 협상 전략을 위한 절충 및 양보 알고리즘 설계</li>
<li>에이전트 기반 클라우드 테스트베드 구현</li>
<li>실험을 통한 PTN 메커니즘 평가</li>
<li>클라우드 리소스 가격 책정에 PTN 메커니즘 적용 사례 연구<h2 id="핵심-아이디어">핵심 아이디어</h2>
<h4 id="ptnprcie-time-slot-negotiation">PTN(Prcie-Time-Slot Negotiation)</h4>
가격과 시간대를 동시에 고려하는 통합 협상 시스템</li>
<li>효용 함수 설계
 a. 가격 효용 + 시간대 효용 = 총 효용
 b. 개별 선호도를 정량적으로 표현</li>
<li>스마트 협상 전략
 a. 절충 및 시간의존적 양보 알고리즘으로 효율성 증대
 b. 버스트 모드로 선택권 확대</li>
<li>협상 프로토콜
 a. Rubinstein&#39;s alternating offers protocol
<img src="https://velog.velcdn.com/images/dus_snp/post/e2536b64-7c31-4f40-a446-79c315b0a182/image.png" alt=""><h2 id="핵심-해결책">핵심 해결책</h2>
<h4 id="협상-방식">협상 방식</h4>
기존 방식
한 번에 하나의 제안만 가능
상대방이 거절하면 다음 라운드까지 대기<h4 id="vs">VS</h4>
버스트 모드
한 번에 총 효용이 같은 제안을 동시에 제시
기존 방식보다 빠른 합의에 도달<h4 id="효용함수">효용함수</h4>
기존방식
사용자의 다양한 시간대 선호도 반영 불가
시간대를 단순히 마감 기한 내에 있는지 여부만 확인<h4 id="vs-1">vs</h4>
새로운 시간대 효용 함수
비선형 함수로 복잡한 시간 선호도 표현 가능
소비자 : 선호하는 여러 시간대 구간을 자유롭게 설정 가능
공급자 : 수요 예측, 리소스 활용도, 시간 순서를 종합 고려</li>
</ol>
<h2 id="효용-함수">효용 함수</h2>
<h4 id="가격-효용함수">가격 효용함수</h4>
<p>소비자 가격 효용 함수
<img src="https://velog.velcdn.com/images/dus_snp/post/5893a924-09e5-404d-970b-9eb77a8b3c86/image.png" alt="">
공급자 가격 효용 함수
<img src="https://velog.velcdn.com/images/dus_snp/post/0b6e66f0-10d5-46c8-9eb7-cca68fcb45dc/image.png" alt=""></p>
<h4 id="시간대-효용-함수">시간대 효용 함수</h4>
<p>소비자 시간대 효용 함수
<img src="https://velog.velcdn.com/images/dus_snp/post/da1e1081-44c8-48f3-9054-204d78cc7173/image.png" alt=""></p>
<p>공급자 시간대 효용 함수
<img src="https://velog.velcdn.com/images/dus_snp/post/7878c49c-3c0d-4254-aedf-699ac0b0f30f/image.png" alt=""></p>
<h4 id="총-효용-함수">총 효용 함수</h4>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/6e97aaf7-50b4-40f0-a1e4-cbd7648c294e/image.png" alt=""></p>
<h2 id="실험-환경">실험 환경</h2>
<h4 id="에이전트-기반-클라우드-테스트베드">에이전트 기반 클라우드 테스트베드</h4>
<p>Java 및 JADE(Java Agent Development) 프레임워크를 사용하여 설계하고 구현</p>
<p>모든 에이전트는 JADE Directory Facilitator를 통해 구현된 클라우드 시장 등록소에 등록
등록소에 등록된 소비자 에이전트는 제공자 에이전트를 인식하고 상호 통신 가능
사용자는 GUI를 통해 서비스 이름, 가격, 시간대, 협상 전략 등 서비스에 대한 선호도를 지정하고 에이전트를 생성 가능
<img src="https://velog.velcdn.com/images/dus_snp/post/74e3d63b-b084-40c4-9bff-88dfe11cbdaa/image.png" alt=""></p>
<h2 id="실험-결과">실험 결과</h2>
<p>(a) λC =3.0, λP =3.0, τC =50,andτP =50. (b)λC =3.0,λP =3.0,τC = 200,andτP = 200. (c) λC =1/3, λP =1/3, τC =50, andτP =50. (d)λC =1/3, λP =1/3, τC = 200, andτP = 200. (e)λC =3.0, λP =1/3, τC =50, and τP =200. (f) λC =1/3, λP =3.0, τC =50, andτP = 200. (g)λC =3.0, λP =1/3, τC = 200,andτP =50. (h)λC =1/3, λP =3.0, τC =200,andτP =50.</p>
<p>λ : 협상 전략, τ : 협상 기한
λ &lt; 1 -&gt; 관대함(빠르게 양보)
λ = 1 -&gt; 선형(일정하게 양보)
λ &gt; 1 -&gt; 보수적(느리게 양보)</p>
<h4 id="협상-속도">협상 속도</h4>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/5158e63c-745f-411b-8b34-eed612f9949b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/aa7e01d8-5191-48cb-b127-cab31d3c3d42/image.png" alt="">
동일한 협상 전략, 기한을 가진 에이전트 쌍의 경우 -&gt; (a) - (d)
협상 에이전트가 더 빠른 양보 전략을 선택할 수록 협상 시간이 빠른 것을 확인</p>
<p>다른 협상 전략, 기한을 가진 에이전트 쌍의 경우 -&gt; (e) - (h)
서로 다른 전략과 마감 기한을 가졌을 때도 Burst 모드에서 협상 속도가 높아지는 것을 볼 수 있음
(f), (g)에서는 한 에이전트가 보수적 전략과 긴 마감기한을 선택하면 협상 속도가 느려지는 것을 볼 수 있음 그럼에도 B50이 H1보다 28.1% ~ 57.3% 빠른 것을 확인</p>
<h4 id="총-효용">총 효용</h4>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/e4a9b1b0-55b8-4e0e-aa42-fab0f0294e7c/image.png" alt=""></p>
<p>협상 전략과 기한에 좌우되지 않음
협상 전략과 기한이 서로 같거나 다르거나 상관없이 일관되게 다른 기법들보다 총 효용이 높게 나오는 것을 볼 수 있음.
다만, CL이 0.9일 때는 사용 가능한 시간대가 적어지면서 평균 총 효용이 줄어드는 것을 확인할 수 있음</p>
<h4 id="burst-mode의-제안-개수">Burst Mode의 제안 개수</h4>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/631d8973-2b0d-4267-b4c8-5466e1047334/image.png" alt=""></p>
<h5 id="동시-제안의-수-증가와-결과-향상량">동시 제안의 수 증가와 결과 향상량</h5>
<p>버스트 모드를 사용할 때, 동시 제안의 수가 증가함에 따라 더 빠른 협상 속도와 더 높은 효용이 나타났지만, 향상량은  동시 제안의 수가 증가함에 따라 감소함.</p>
<p>B10에서 B50으로 넘어갈 때, 협상 속도와 총 효용은 각각 5.6%와 1.5% 증가하였지만,
B50에서 B100으로 넘어갈 때, 협상 속도와 총 효용이 각각 0.7%와 0.6% 증가하였음.</p>
<p>이는 버스트 모드가 너무 많은 제안을 동시에 한다고 성능이 크게 달라지지 않는다는 것을 시사함. 따라서 계산 복잡도와 성능 개선을 잘 확인하며 제안 개수를 설정해야 함.</p>
<h2 id="연구의-기여도">연구의 기여도</h2>
<h4 id="최초의-클라우드-서비스-예약을-위한-ptn-메커니즘-설계-및-구현">최초의 클라우드 서비스 예약을 위한 PTN 메커니즘 설계 및 구현</h4>
<h4 id="burst-mode-절충-양보-알고리즘-설계">Burst Mode 절충, 양보 알고리즘 설계</h4>
<ul>
<li>협상 속도와 총 효용 모두 향상</li>
<li>다중 이슈 협상에서 동시 다중 제안 가능<h4 id="새로운-시간대-효용-함수-개발">새로운 시간대 효용 함수 개발</h4>
</li>
<li>다양한 시간대에 대한 서로 다른 선호도 특성화</li>
<li>기존 연구에서는 고려되지 않던 새로운 개념<h2 id="연구의-한계점-및-향후-연구">연구의 한계점 및 향후 연구</h2>
</li>
<li><strong>협상 메커니즘에서 다룬 쟁점의 수가 제한적</strong></li>
<li><strong>계산 복잡도가 더 효율적인 알고리즘 필요</strong></li>
<li><strong>사용자 친화적 인터페이스 부족</strong></li>
<li><strong>동적인 환경 변화에 대한 미고려</strong><h4 id="향후연구">향후연구</h4>
</li>
<li>다른 협상 쟁점(QoS 등)을 고려하고 명시하는 것</li>
<li>버스트 모드의 동시 제안의 수를 적응적으로 제어하여 계산 복잡도를 줄이고 알고리즘 향상</li>
<li>높은 수준의 사용자 선호도를 시간대 효용 함수의 낮은 수준의 기술적 사양으로 변환하는 GUI</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[랩미팅 논문 발표 요약1]]></title>
            <link>https://velog.io/@dus_snp/%EB%9E%A9%EB%AF%B8%ED%8C%85-%EB%85%BC%EB%AC%B8-%EB%B0%9C%ED%91%9C-%EC%9A%94%EC%95%BD1</link>
            <guid>https://velog.io/@dus_snp/%EB%9E%A9%EB%AF%B8%ED%8C%85-%EB%85%BC%EB%AC%B8-%EB%B0%9C%ED%91%9C-%EC%9A%94%EC%95%BD1</guid>
            <pubDate>Tue, 11 Nov 2025 09:50:22 GMT</pubDate>
            <description><![CDATA[<p>분산 데이터센터 환경의 클라우드에서 SLA 위반을 경감시키기 위한 가변 기법
정보과학회논문지: 컴퓨팅의 실제 및 레터 제 18 권 제 11 호(2012.11)
손석호, 정기훈, 전성찬
<a href="https://www.dbpia.co.kr/Journal/articleDetail?nodeId=NODE02033323">https://www.dbpia.co.kr/Journal/articleDetail?nodeId=NODE02033323</a></p>
<h2 id="문제-제기-및-연구의-필요성">문제 제기 및 연구의 필요성</h2>
<h4 id="서비스-수준-협약을-위반할-가능성">서비스 수준 협약을 위반할 가능성</h4>
<p>기존 CSP는 고정된 SLA를 제시하지만, 시스템 자원이 부족해지면 SLA를 위반할 가능성이 높음
특히, 지역적으로 분산된 데이터 센터를 운영하는 클라우드 환경에서는 서비스 응답속도와 부하 분산 사이에 상관관계가 있는데, 고정된 SLA는 부하를 효율적으로 분산시키기 어렵게 만듦</p>
<p>이러한 문제를 해결하고자 분산 데이터센터 환경에서 SLA 위반을 줄이기 위한 가변 SLA기법을 제안하고 그 유효성을 평가하고자 이 논문이 작성됨</p>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/68bf7703-4a81-4c5d-b70d-ac561289e448/image.png" alt=""></p>
<h2 id="핵심-아이디어-및-해결책">핵심 아이디어 및 해결책</h2>
<h4 id="핵심-아이디어">핵심 아이디어</h4>
<p>클라우드 서비스 공급자와 사용자 간의 자동화된 SLA 협상 메커니즘을 도입하여, 사용자의 요구사항과 시스템 자원 상황에 맞춰 서비스 품질을 미리 조율하는 것
이를 통해 고정 SLA로 인해 발생하는 문제들을 해결</p>
<h4 id="해결책">해결책</h4>
<p>다중 쟁점 협상 : 서비스 응답 속도와 가격을 주요 협상 쟁점으로 설정
공급자와 사용자는 이 두 가지 쟁점에 대한 만족도를 유틸리티 함수로 정의하고, 협상 에이전트가 최적의 합의점을 찾도록 함</p>
<p>가변 SLA 구현 : 협상을 통해 서비스 응답 속도와 가격을 유동적으로 결정하여, 사용자는 낮은 가격으로 느린 서비스를 받거나, 공급자는 부하 분산에 유리한 데이터센터에 작업을 할당할 수 있음</p>
<h2 id="연구-결과-및-입증내용">연구 결과 및 입증내용</h2>
<p><img src="https://velog.velcdn.com/images/dus_snp/post/c1e90197-cbe8-4f31-9420-ba3402698211/image.png" alt="">
<img src="https://velog.velcdn.com/images/dus_snp/post/8db31e53-34da-459f-8c7c-f0542586fb5e/image.png" alt=""></p>
<h4 id="결론-도출-과정">결론 도출 과정</h4>
<p>에이전트 기반의 클라우드 테스트베드를 구축하고, 가변 SLA 기법과 고정 SLA 기법의 성능을 비교하는 시뮬레이션을 수행함
1,000명, 5,000명, 10,000명의 사용자로 가정 후 테스트를 진행함</p>
<h4 id="sla위반-경감">SLA위반 경감</h4>
<p>가변 SLA를 적용했을 때, 고정 SLA를 사용했을 때 보다 위반 사용자의 수가 월등히 낮아진 것을 볼 수 있음</p>
<h4 id="수익-증대">수익 증대</h4>
<p>가변 SLA를 이용하면 클라우드 공급자에게 더 높은 수익을 제공하는 것을 그래프를 통해 확인할 수 있음</p>
<h2 id="논문의-타당성-및-한계점">논문의 타당성 및 한계점</h2>
<h4 id="타당성">타당성</h4>
<p>이 연구는 분산 데이터센터 환경의 문제점을 해결하기 위해 가변적인 SLA라는 새로운 접근법을 제시하고, 자동화된 협상 메커니즘을 적용하여 이를 구체적으로 구현함</p>
<p>실험을 통해 SLA 위반율 감소와 수익 증대라는 뚜렷한 결과를 확인할 수 있으므로, 논문의 주장은 매우 합당하다고 볼 수 있음</p>
<h4 id="한계점">한계점</h4>
<p>본 논문에서 언급된 직접적인 한계점이 없음 </p>
<p>그러나 저자가 향후 연구로 SLA 협상 기법에 기반한 자원 및 과금 정책 스케줄러 디자인을 고안할 계획이라고 밝히며, 
이는 SLA 협상 외에 실제 클라우드 시스템의 자원 관리 및 요금 청구 방식과 연계하는 포괄적인 연구가 필요하다고 볼 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 구조 Chapter 06]]></title>
            <link>https://velog.io/@dus_snp/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-Chapter-06</link>
            <guid>https://velog.io/@dus_snp/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-Chapter-06</guid>
            <pubDate>Mon, 01 Sep 2025 11:32:10 GMT</pubDate>
            <description><![CDATA[<h2 id="컴퓨터-저장-장치-ram과-보조기억장치-ft-캐시-메모리">컴퓨터 저장 장치: RAM과 보조기억장치 (ft. 캐시 메모리)</h2>
<hr>
<h3 id="1-ram과-보조기억장치의-차이">1. RAM과 보조기억장치의 차이</h3>
<p>컴퓨터의 저장 장치는 데이터가 전원이 꺼진 후에도 유지되는지 여부에 따라 <strong>휘발성</strong>과 <strong>비휘발성</strong>으로 나뉩니다.</p>
<ul>
<li><p><strong>RAM (Random Access Memory)</strong>:</p>
<ul>
<li><strong>휘발성</strong> 장치로, 전원이 꺼지면 모든 내용이 사라집니다.</li>
<li>CPU가 직접 접근하는 <strong>주기억장치</strong>이며, 현재 실행 중인 프로그램이나 데이터를 임시로 저장하는 &#39;작업 공간&#39; 역할을 합니다.</li>
<li>RAM의 용량이 클수록 더 많은 프로그램을 동시에 처리할 수 있어 작업 속도가 빨라집니다.</li>
</ul>
</li>
<li><p><strong>보조기억장치</strong>:</p>
<ul>
<li><strong>비휘발성</strong> 장치로, 전원이 꺼져도 데이터가 보존됩니다.</li>
<li><strong>하드 디스크(HDD), SSD, CD-ROM, USB 메모리</strong> 등이 대표적입니다.</li>
<li>프로그램과 데이터를 영구적으로 저장하는 역할을 합니다. CPU는 여기에 직접 접근하지 못하고, 실행에 필요한 데이터를 RAM으로 복사하여 사용합니다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-dram-vs-sram-ram의-두-얼굴">2. DRAM vs. SRAM: RAM의 두 얼굴</h3>
<p>RAM은 크게 DRAM과 SRAM 두 종류로 나뉘며, 각각의 특성과 사용 목적이 다릅니다.</p>
<ul>
<li><p><strong>DRAM (Dynamic RAM)</strong>:</p>
<ul>
<li>저장된 데이터가 시간이 지나면 동적으로 사라지기 때문에, 주기적으로 데이터를 재활성화(재충전)해야 합니다.</li>
<li><strong>저렴하고 집적도가 높아</strong> 대용량 메모리인 <strong>주기억장치(RAM)</strong>로 널리 쓰입니다.</li>
</ul>
</li>
<li><p><strong>SRAM (Static RAM)</strong>:</p>
<ul>
<li>전원이 공급되는 동안 데이터가 유지되어 재충전이 필요 없습니다.</li>
<li><strong>DRAM보다 속도가 빠르지만, 가격이 비싸고 소비 전력이 많으며 집적도가 낮습니다.</strong></li>
<li>대용량보다는 빠른 속도가 중요한 <strong>캐시 메모리</strong>에 주로 사용됩니다.</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left"><strong>DRAM</strong></th>
<th align="left"><strong>SRAM</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>재충전</strong></td>
<td align="left">필요함</td>
<td align="left">필요 없음</td>
</tr>
<tr>
<td align="left"><strong>속도</strong></td>
<td align="left">느림</td>
<td align="left">빠름</td>
</tr>
<tr>
<td align="left"><strong>가격</strong></td>
<td align="left">저렴함</td>
<td align="left">비쌈</td>
</tr>
<tr>
<td align="left"><strong>집적도</strong></td>
<td align="left">높음</td>
<td align="left">낮음</td>
</tr>
<tr>
<td align="left"><strong>소비 전력</strong></td>
<td align="left">적음</td>
<td align="left">높음</td>
</tr>
<tr>
<td align="left"><strong>주요 용도</strong></td>
<td align="left">주기억장치(RAM)</td>
<td align="left">캐시 메모리</td>
</tr>
</tbody></table>
<hr>
<h3 id="3-발전된-ram-sdram과-ddr-sdram">3. 발전된 RAM: SDRAM과 DDR SDRAM</h3>
<ul>
<li><p><strong>SDRAM (Synchronous Dynamic RAM)</strong>:</p>
<ul>
<li>CPU의 <strong>클럭 신호와 동기화</strong>되어 작동하는 발전된 형태의 DRAM입니다.</li>
<li>클럭 타이밍에 맞춰 CPU와 데이터를 주고받아 비동기식 DRAM보다 훨씬 효율적입니다.</li>
</ul>
</li>
<li><p><strong>DDR SDRAM (Double Data Rate SDRAM)</strong>:</p>
<ul>
<li>SDRAM의 발전형으로, <strong>대역폭</strong>을 2배로 넓혀 한 클럭당 두 번씩 데이터를 주고받을 수 있게 되었습니다.</li>
<li><code>DDR2</code>, <code>DDR3</code>, <code>DDR4</code> 등으로 발전하며 전송 속도가 계속 향상됩니다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-메모리-관리-기술-논리-주소와-물리-주소">4. 메모리 관리 기술: 논리 주소와 물리 주소</h3>
<p>CPU는 프로그램을 실행할 때 실제 메모리 주소를 직접 다루기 어렵기 때문에 가상의 주소를 사용합니다.</p>
<ul>
<li><strong>물리 주소</strong>: 메모리 하드웨어가 실제로 사용하는 주소.</li>
<li><strong>논리 주소</strong>: CPU와 실행 중인 프로그램이 사용하는 가상의 주소.</li>
</ul>
<p>이를 변환하는 장치가 바로 <strong>메모리 관리 장치(MMU)</strong>입니다. MMU는 논리 주소에 <strong>베이스 레지스터</strong> 값을 더해 실제 물리 주소를 계산합니다.</p>
<h4 id="메모리-보호-기법">메모리 보호 기법</h4>
<ul>
<li><strong>베이스 레지스터</strong>: 현재 프로그램의 시작 물리 주소를 저장합니다.</li>
<li><strong>한계 레지스터</strong>: 현재 프로그램의 논리 주소 최대 크기를 저장합니다.</li>
<li><strong>작동 방식</strong>: CPU가 메모리에 접근할 때, 접근하려는 논리 주소가 한계 레지스터 값을 넘지 않는지 검사하여 다른 프로그램의 영역을 침범하지 않도록 보호합니다.</li>
</ul>
<hr>
<h3 id="5-저장-장치-계층-구조와-캐시-메모리">5. 저장 장치 계층 구조와 캐시 메모리</h3>
<p>컴퓨터의 저장 장치는 속도와 용량에 따라 계층적으로 구성됩니다.</p>
<ul>
<li><strong>원칙</strong>: CPU에 가까운 장치일수록 빠르고, 멀어질수록 느립니다. 또한, 속도가 빠를수록 용량은 작고 가격은 비쌉니다.</li>
</ul>
<h4 id="캐시-메모리">캐시 메모리</h4>
<p>CPU와 RAM의 속도 차이를 줄이기 위해 탄생한 <strong>매우 빠른 임시 저장 장치</strong>입니다. CPU가 필요할 것으로 예측되는 데이터를 미리 가져와 저장해 둡니다.
메모리로부터 데이터를 가져올 때는 참조 지역성의 원리를 따릅니다.</p>
<ul>
<li><strong>캐시 히트</strong>: 예측이 맞아 캐시에서 데이터를 찾았을 때.</li>
<li><strong>캐시 미스</strong>: 예측이 틀려 RAM에서 데이터를 가져와야 할 때.</li>
<li><strong>종류</strong>: CPU 코어에 가까운 순서대로 <strong>L1, L2, L3 캐시</strong>가 있습니다. L1이 가장 빠르고 용량이 작습니다.</li>
<li><strong>참조 지역성의 원리</strong>: 캐시 메모리가 데이터를 가져오는 핵심 원칙입니다.<ol>
<li><strong>시간 지역성</strong>: 최근에 사용된 데이터는 곧 다시 사용될 가능성이 높다.</li>
<li><strong>공간 지역성</strong>: 사용된 데이터 근처의 데이터가 곧 사용될 가능성이 높다.</li>
</ol>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 구조 Chapter 05]]></title>
            <link>https://velog.io/@dus_snp/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-Chapter-05</link>
            <guid>https://velog.io/@dus_snp/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-Chapter-05</guid>
            <pubDate>Sun, 17 Aug 2025 08:27:39 GMT</pubDate>
            <description><![CDATA[<h2 id="cpu의-핵심-작동-원리-클럭-멀티코어-멀티스레드">CPU의 핵심 작동 원리: 클럭, 멀티코어, 멀티스레드</h2>
<hr>
<h3 id="1-클럭-clock">1. 클럭 (Clock)</h3>
<p><strong>클럭</strong>은 컴퓨터 부품들이 일사불란하게 움직일 수 있도록 해주는 시간 단위입니다. 마치 지휘자의 박수처럼, CPU는 이 클럭 신호에 맞춰 명령어를 처리합니다.</p>
<ul>
<li><strong>클럭 속도 (Clock Speed)</strong>: 1초에 발생하는 클럭의 횟수로, <strong>헤르츠(Hz)</strong> 단위로 나타냅니다. 일반적으로 클럭 속도가 빠를수록 CPU의 성능이 높아집니다.</li>
<li><strong>클럭 속도의 한계</strong>: 클럭 속도를 무작정 높이면 <strong>발열</strong>이 심해져 CPU 성능에 오히려 악영향을 줄 수 있습니다. 이 때문에 클럭 속도를 높이는 데에는 한계가 있습니다.</li>
</ul>
<hr>
<h3 id="2-멀티코어-multi-core">2. 멀티코어 (Multi-core)</h3>
<p>클럭 속도의 한계를 극복하기 위해 등장한 방식 중 하나가 <strong>코어</strong> 수를 늘리는 것입니다.</p>
<ul>
<li><strong>코어 (Core)</strong>: 실제로 명령어를 실행하는 CPU의 핵심 부품입니다.</li>
<li><strong>멀티코어 CPU</strong>: 여러 개의 코어를 한 칩에 집적한 CPU를 말합니다. 여러 개의 일꾼이 동시에 작업하는 것처럼, 여러 코어가 동시에 명령어를 처리하여 전체 성능을 향상시킵니다.<ul>
<li><strong>예시</strong>: 2.4GHz 클럭의 단일 코어 CPU보다 1.9GHz 클럭의 멀티코어 CPU가 일반적으로 더 높은 성능을 보입니다.</li>
</ul>
</li>
<li><strong>주의사항</strong>: 코어 수가 많다고 해서 무조건 성능이 비례하여 증가하는 것은 아닙니다. 작업을 효율적으로 분배하는 기술이 중요하며, 작업량에 비해 코어 수가 지나치게 많으면 효율이 떨어질 수 있습니다.</li>
</ul>
<hr>
<h3 id="3-스레드-thread">3. 스레드 (Thread)</h3>
<p>스레드는 CPU에서 명령어를 처리하는 단위로, <strong>하드웨어 스레드</strong>와 <strong>소프트웨어 스레드</strong>로 구분해 이해하는 것이 좋습니다.</p>
<h4 id="하드웨어-스레드">하드웨어 스레드</h4>
<ul>
<li><strong>정의</strong>: 하나의 코어가 동시에 처리하는 명령어의 단위를 의미합니다.</li>
<li><strong>멀티스레드 프로세서</strong>: 하나의 코어가 2개 이상의 명령어를 동시에 처리할 수 있는 기술입니다. 인텔의 <strong>하이퍼스레딩(Hyper-Threading)</strong> 기술이 대표적입니다.<ul>
<li><strong>원리</strong>: 코어 내에 여러 개의 <strong>레지스터</strong>를 두어 마치 여러 개의 코어가 있는 것처럼 동작하게 합니다. 그래서 하드웨어 스레드를 <strong>논리 프로세서</strong>라고 부르기도 합니다.</li>
</ul>
</li>
</ul>
<h4 id="소프트웨어-스레드">소프트웨어 스레드</h4>
<ul>
<li><strong>정의</strong>: 하나의 프로그램에서 독립적으로 실행되는 작업의 단위입니다.<ul>
<li><strong>예시</strong>: 워드 프로그램에서 &#39;입력 내용 보여주기&#39;, &#39;맞춤법 검사&#39;, &#39;자동 저장&#39; 등의 기능을 각각의 스레드로 만들어 동시에 실행할 수 있습니다.</li>
</ul>
</li>
</ul>
<p><strong>정리</strong>:</p>
<ul>
<li><strong>코어</strong>: 물리적으로 명령어를 실행하는 하드웨어 부품. (일꾼)</li>
<li><strong>스레드</strong>: 명령어를 실행하는 논리적인 단위. (일꾼 한 명이 동시에 여러 개의 일을 할 수 있는 능력)</li>
<li><strong>멀티코어</strong>: CPU에 물리적인 코어가 여러 개 있는 것.</li>
<li><strong>멀티스레드</strong>: 하나의 코어가 여러 스레드를 동시에 처리할 수 있는 것.</li>
</ul>
<hr>
<h2 id="cpu-성능-향상을-위한-명령어-병렬-처리-기법">CPU 성능 향상을 위한 명령어 병렬 처리 기법</h2>
<p>CPU의 성능을 높이기 위해서는 클럭, 코어, 스레드 외에도 CPU가 쉬지 않고 일하게 만드는 <strong>명령어 병렬 처리</strong> 기술이 중요합니다.</p>
<h3 id="1-명령어-파이프라이닝-instruction-pipelining">1. 명령어 파이프라이닝 (Instruction Pipelining)</h3>
<p>명령어 처리 과정을 여러 단계(인출, 해석, 실행, 결과 저장 등)로 나누고, 각 단계를 겹쳐서 동시에 처리하는 기법입니다.
마치 공장의 생산 라인처럼 여러 명령어를 연속적으로 처리하여 효율을 극대화합니다.</p>
<ul>
<li><strong>위험 요소</strong>:<ul>
<li><strong>데이터 위험</strong>: 명령어 간 데이터 의존성 때문에 발생하는 문제. (예: 앞선 명령어의 결과가 다음 명령어에 필요할 때)</li>
<li><strong>제어 위험</strong>: 프로그램 분기(조건에 따라 실행 순서가 바뀌는 것)로 인해 미리 가져온 명령어들이 쓸모없게 되는 문제. <strong>분기 예측</strong> 기술로 해결합니다.</li>
<li><strong>구조적 위험</strong>: 여러 명령어가 동시에 같은 CPU 부품(ALU, 레지스터 등)을 사용하려고 할 때 발생하는 문제.</li>
</ul>
</li>
</ul>
<h3 id="2-슈퍼스칼라-superscalar">2. 슈퍼스칼라 (Superscalar)</h3>
<p>대부분의 현대 CPU가 채택하고 있는 구조로, <strong>여러 개의 파이프라인</strong>을 동시에 사용하는 기법입니다. 여러 명령어를 한꺼번에 처리할 수 있어 처리 속도가 이론적으로 파이프라인 개수에 비례하여 빨라집니다.</p>
<h3 id="3-비순차적-명령어-처리-out-of-order-execution-oooe">3. 비순차적 명령어 처리 (Out-of-order Execution, OoOE)</h3>
<p>데이터 의존성이 없는 명령어들을 순서와 관계없이 먼저 실행하여, 파이프라인이 멈추는 것을 방지하고 전체적인 성능을 높이는 기법입니다.</p>
<hr>
<h2 id="명령어-집합-아키텍처-instruction-set-architecture-isa">명령어 집합 아키텍처 (Instruction Set Architecture, ISA)</h2>
<p><strong>ISA</strong>는 CPU가 이해할 수 있는 명령어들의 모음으로, CPU의 언어와 같습니다. 대표적으로 <strong>CISC</strong>와 <strong>RISC</strong>가 있습니다.</p>
<h3 id="cisc-complex-instruction-set-computer">CISC (Complex Instruction Set Computer)</h3>
<ul>
<li><strong>특징</strong>: 복잡하고 다양한 기능의 명령어 집합을 사용합니다.</li>
<li><strong>장점</strong>: 하나의 명령어로 복잡한 연산을 수행할 수 있어 프로그램을 이루는 명령어 수가 적습니다.</li>
<li><strong>단점</strong>: 명령어의 길이가 가변적이고 실행 시간이 일정하지 않아 <strong>파이프라이닝에 불리</strong>합니다.</li>
</ul>
<h3 id="risc-reduced-instruction-set-computer">RISC (Reduced Instruction Set Computer)</h3>
<ul>
<li><strong>특징</strong>: 단순하고 적은 종류의 명령어를 사용합니다.</li>
<li><strong>장점</strong>: 고정된 명령어 길이와 1클럭 내외의 실행 시간으로 <strong>파이프라이닝에 최적화</strong>되어 있습니다.</li>
<li><strong>단점</strong>: CISC보다 많은 명령어를 사용해야 프로그램을 완성할 수 있습니다.</li>
</ul>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">CISC</th>
<th align="left">RISC</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>명령어</strong></td>
<td align="left">복잡하고 다양함</td>
<td align="left">단순하고 적음</td>
</tr>
<tr>
<td align="left"><strong>명령어 길이</strong></td>
<td align="left">가변적</td>
<td align="left">고정적</td>
</tr>
<tr>
<td align="left"><strong>파이프라이닝</strong></td>
<td align="left">어려움</td>
<td align="left">쉬움</td>
</tr>
<tr>
<td align="left"><strong>프로그램 명령어 수</strong></td>
<td align="left">적음</td>
<td align="left">많음</td>
</tr>
<tr>
<td align="left"><strong>주소 지정 방식</strong></td>
<td align="left">다양함</td>
<td align="left">적음</td>
</tr>
<tr>
<td align="left"><strong>수행 시간</strong></td>
<td align="left">여러 클럭</td>
<td align="left">1클럭 내외</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 구조 Chapter 04]]></title>
            <link>https://velog.io/@dus_snp/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-Chapter-04</link>
            <guid>https://velog.io/@dus_snp/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-Chapter-04</guid>
            <pubDate>Thu, 17 Jul 2025 08:21:24 GMT</pubDate>
            <description><![CDATA[<h1 id="cpu와-작동-원리">CPU와 작동 원리</h1>
<h2 id="alu-산술-논리-연산-장치">ALU (산술 논리 연산 장치)</h2>
<h3 id="받아들이는-정보">받아들이는 정보</h3>
<ul>
<li><strong>피연산자와 제어 신호</strong><ul>
<li>레지스터를 통해 피연산자를 받아들이고, 제어장치로부터 수행할 연산을 알려주는 제어 신호를 받아들임</li>
<li>받아들인 피연산자와 제어 신호로 산술 연산, 논리 연산 등 다양한 연산을 수행</li>
</ul>
</li>
</ul>
<h3 id="내보내는-정보">내보내는 정보</h3>
<ul>
<li><strong>결과값과 플래그</strong><ul>
<li>결과값은 일시적으로 레지스터에 저장된 후 메모리에 저장 (프로그램의 실행 속도를 위해서)</li>
<li>플래그는 연산 결과에 대한 추가적인 상태 정보를 뜻하며, 플래그 레지스터에 저장됨</li>
</ul>
</li>
</ul>
<h4 id="플래그-종류-및-의미">플래그 종류 및 의미</h4>
<table>
<thead>
<tr>
<th>플래그 종류</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>부호 플래그</td>
<td>연산한 결과의 부호를 나타냄</td>
</tr>
<tr>
<td>제로 플래그</td>
<td>연산 결과가 0인지 여부를 나타냄</td>
</tr>
<tr>
<td>캐리 플래그</td>
<td>연산 결과 올림수나 빌림수가 발생했는지를 나타냄</td>
</tr>
<tr>
<td>오버플로우 플래그</td>
<td>오버플로우가 발생했는지를 나타냄</td>
</tr>
<tr>
<td>인터럽트 플래그</td>
<td>인터럽트가 가능한지를 나타냄</td>
</tr>
<tr>
<td>슈퍼바이저 플래그</td>
<td>실행 중인 모드가 커널 모드인지 사용자 모드인지 나타냄</td>
</tr>
</tbody></table>
<hr>
<h2 id="제어장치-control-unit">제어장치 (Control Unit)</h2>
<h3 id="받아들이는-정보-1">받아들이는 정보</h3>
<ul>
<li><strong>클럭 신호</strong><ul>
<li>컴퓨터의 모든 부품을 일사분란하게 움직일 수 있게 하는 시간 단위  </li>
<li>(모든 부품이 클럭 박자에 맞춰서 작동하지만, 한 박자마다 작동하는 건 아님)</li>
</ul>
</li>
<li><strong>명령어</strong><ul>
<li>명령어 레지스터로부터 해석할 명령어를 받아들이고, 해석한 뒤 제어신호를 발생시켜 수행할 내용을 알려줌</li>
</ul>
</li>
<li><strong>플래그</strong><ul>
<li>플래그 값을 참고하여 제어신호를 발생시켜 올바른 정보 전달 및 동작 수행 유도</li>
</ul>
</li>
<li><strong>제어 신호</strong><ul>
<li>제어 버스를 통해 외부로부터 전달된 제어 신호를 받아들이기도 함</li>
</ul>
</li>
</ul>
<h3 id="내보내는-정보-1">내보내는 정보</h3>
<ul>
<li><strong>외부에 전달하는 제어 신호:</strong><br>메모리와 입출력장치에 전달하는 신호</li>
<li><strong>내부에 전달하는 제어 신호:</strong><br>ALU와 레지스터에 전달하는 신호</li>
</ul>
<hr>
<h2 id="레지스터-register">레지스터 (Register)</h2>
<ul>
<li>프로그램의 명령어와 데이터는 실행 전후로 반드시 레지스터에 저장됨  </li>
<li>레지스터 속 값을 관찰하면 CPU 내에서 무슨 일이 벌어지고 있는지, 어떤 명령어가 어떻게 수행되는지 알 수 있음</li>
</ul>
<h3 id="주요-레지스터-종류">주요 레지스터 종류</h3>
<table>
<thead>
<tr>
<th>레지스터 명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>프로그램 카운터</td>
<td>메모리에서 읽어들일 명령어의 주소를 저장 (명령어 포인터)</td>
</tr>
<tr>
<td>명령어 레지스터</td>
<td>방금 메모리에서 읽어 들인 명령어를 저장하는 레지스터</td>
</tr>
<tr>
<td>메모리 주소 레지스터</td>
<td>메모리 주소를 저장하는 레지스터</td>
</tr>
<tr>
<td>메모리 버퍼 레지스터</td>
<td>메모리와 주고받을 값을 저장하는 레지스터</td>
</tr>
<tr>
<td>범용 레지스터</td>
<td>데이터와 주소를 모두 저장할 수 있는 범용 레지스터</td>
</tr>
<tr>
<td>플래그 레지스터</td>
<td>ALU 연산 결과에 따른 플래그를 저장</td>
</tr>
<tr>
<td>스택 포인터</td>
<td>스택의 꼭대기를 가리키는 레지스터</td>
</tr>
<tr>
<td>베이스 레지스터</td>
<td>유효 주소를 얻기 위한 베이스 값을 가지고 있는 레지스터</td>
</tr>
</tbody></table>
<hr>
<h2 id="명령어-사이클-instruction-cycle">명령어 사이클 (Instruction Cycle)</h2>
<ul>
<li>명령어들은 일정한 주기가 반복되며 실행됨 (이 주기를 <strong>명령어 사이클</strong>이라고 함)<ul>
<li><strong>인출 사이클:</strong> 메모리에 있는 명령어를 CPU로 가지고 오는 단계</li>
<li><strong>실행 사이클:</strong> 제어장치가 명령어 레지스터에 담긴 값을 해석하고, 제어 신호를 발생시키는 단계</li>
<li><strong>간접 사이클:</strong> 실행을 위해 다시 메모리에 접근하는 단계</li>
</ul>
</li>
</ul>
<hr>
<h2 id="인터럽트-interrupt">인터럽트 (Interrupt)</h2>
<h3 id="종류">종류</h3>
<ul>
<li><strong>동기 인터럽트:</strong><br>CPU가 명령어를 수행하다가 오류 등 예외 상황에서 발생
(예외라고도 함)</li>
<li><strong>비동기 인터럽트:</strong><br>입출력장치에 의해 발생.<br>CPU가 지시한 작업을 완료하거나 입력을 받아들였을 때 CPU에 보내는 알림 (하드웨어 인터럽트라고도 함)</li>
</ul>
<h3 id="하드웨어-인터럽트-처리-순서">하드웨어 인터럽트 처리 순서</h3>
<ol>
<li>입출력장치가 CPU에 인터럽트 요청 신호를 보냄</li>
<li>CPU는 실행 사이클이 끝나고 명령어를 인출하기 전, 항상 인터럽트 여부를 확인함</li>
<li>인터럽트 플래그를 통해 현재 인터럽트를 받아들일 수 있는지 확인</li>
<li>인터럽트를 받아들일 수 있다면 CPU는 지금까지의 작업을 백업</li>
<li>CPU는 인터럽트 벡터를 참조하여 <strong>인터럽트 서비스 루틴(ISR)</strong>을 실행</li>
<li>ISR 실행이 끝나면 백업해둔 작업을 복구하여 실행 재개</li>
</ol>
<ul>
<li>CPU가 인터럽트 요청을 수행하려면 플래그 레지스터의 <strong>인터럽트 플래그</strong>가 활성화되어 있어야 함  </li>
<li>정전이나 고장 등 치명적인 인터럽트는 무시할 수 없음</li>
</ul>
<h4 id="용어-정리">용어 정리</h4>
<ul>
<li><strong>인터럽트 서비스 루틴(ISR)</strong>: 인터럽트를 처리하는 프로그램 (인터럽트 핸들러)</li>
<li><strong>인터럽트 벡터</strong>: ISR의 시작 주소 등 인터럽트 처리용 정보</li>
</ul>
<h3 id="예외의-종류">예외의 종류</h3>
<ul>
<li><strong>폴트 (fault):</strong><br>예외를 처리한 직후, 예외가 발생한 명령어부터 실행 재개</li>
<li><strong>트랩 (trap):</strong><br>예외를 처리한 직후, 다음 명령어부터 실행 재개</li>
<li><strong>중단 (abort):</strong><br>심각한 오류로 프로그램 강제 중단</li>
<li><strong>소프트웨어 인터럽트:</strong><br>시스템 호출이 발생했을 때 나타남</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 구조 Chapter 03]]></title>
            <link>https://velog.io/@dus_snp/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-Chapter-03</link>
            <guid>https://velog.io/@dus_snp/%EC%BB%B4%ED%93%A8%ED%84%B0-%EA%B5%AC%EC%A1%B0-Chapter-03</guid>
            <pubDate>Sat, 05 Jul 2025 13:38:25 GMT</pubDate>
            <description><![CDATA[<h1 id="명령어">명령어</h1>
<h3 id="언어-번역-방식-주소-지정-방식-정리">언어 번역 방식, 주소 지정 방식 정리</h3>
<h2 id="고급-언어와-저급-언어">고급 언어와 저급 언어</h2>
<p>우리가 작성하는 <strong>소스코드</strong>는 컴퓨터가 직접 이해하지 못함.  </p>
<ul>
<li>우리는 <strong>고급 언어</strong>(C, C++, Java, Python 등)를 사용하고,</li>
<li>컴퓨터는 <strong>저급 언어</strong>(기계어, 어셈블리어)만 이해하기 때문</li>
</ul>
<h3 id="기계어와-어셈블리어">기계어와 어셈블리어</h3>
<ul>
<li><p><strong>기계어</strong>:  
2진수 혹은 16진수로 구성된 <strong>컴퓨터가 직접 이해하는 명령어</strong><br>→ 사람이 해독하기 어려움</p>
</li>
<li><p><strong>어셈블리어</strong>:  
기계어를 사람이 <strong>조금 더 이해하기 쉬운 형태</strong>로 표현한 저급 언어<br>→ 어셈블리어를 알면 프로그램이 어떻게 동작하는지를 <strong>근본적인 수준에서 이해</strong>할 수 있음</p>
</li>
</ul>
<hr>
<h2 id="소스-코드-번역-방식">소스 코드 번역 방식</h2>
<p>소스코드를 컴퓨터가 이해할 수 있도록 저급 언어로 변환하는 방식은 두 가지가 있다.</p>
<h3 id="1-컴파일-방식">1. 컴파일 방식</h3>
<ul>
<li>전체 소스 코드를 <strong>한 번에 번역</strong>해서 실행</li>
<li>번역된 결과: <strong>목적 코드(object code)</strong></li>
<li>예: <strong>C 언어</strong></li>
</ul>
<p>✅ 장점:</p>
<ul>
<li>전체를 컴파일해두므로 실행 속도가 빠름</li>
</ul>
<p>❌ 단점:</p>
<ul>
<li>컴파일 과정에서 <strong>오류가 하나라도 있으면 실행 불가</strong></li>
</ul>
<hr>
<h3 id="2-인터프리터-방식">2. 인터프리터 방식</h3>
<ul>
<li>소스 코드를 <strong>한 줄씩 번역하면서 실행</strong></li>
<li>예: <strong>Python</strong></li>
</ul>
<p>✅ 장점:</p>
<ul>
<li>오류가 있는 줄을 실행하기 전까지는 나머지 코드가 실행됨 </li>
</ul>
<p>❌ 단점:</p>
<ul>
<li>매번 해석하며 실행하기 때문에 <strong>속도는 느림</strong></li>
</ul>
<hr>
<blockquote>
<p>⚠️ 고급 언어는 컴파일 언어 vs 인터프리터 언어처럼 <strong>명확히 나뉘지 않음.</strong><br>많은 언어가 두 속성을 <strong>모두 갖거나 전환 가능</strong>하기 때문</p>
</blockquote>
<hr>
<h2 id="명령어-구조">명령어 구조</h2>
<p>컴퓨터 명령어는 다음 두 부분으로 구성됨.</p>
<ul>
<li><strong>연산 코드 (Opcode)</strong>: 어떤 연산을 할지 정의</li>
<li><strong>오퍼랜드 (Operand)</strong>: 연산에 사용할 데이터</li>
</ul>
<blockquote>
<p>오퍼랜드는 연산의 대상이며, <strong>데이터</strong>, <strong>메모리 주소</strong>, <strong>레지스터</strong> 등이 올 수 있음.<br>그래서 오퍼랜드 필드를 <strong>주소 필드</strong>라고도 부름.</p>
</blockquote>
<hr>
<h2 id="주소-지정-방식-addressing-modes">주소 지정 방식 (Addressing Modes)</h2>
<p>오퍼랜드 필드에 연산 대상의 <strong>주소나 값을 명시하는 방식</strong>.  
대표적으로 <strong>다섯 가지 방식</strong></p>
<h3 id="1-즉시-주소-지정-방식-immediate-addressing">1. 즉시 주소 지정 방식 (Immediate Addressing)</h3>
<ul>
<li>데이터를 <strong>오퍼랜드 필드에 직접 명시</strong></li>
<li>가장 단순하고 빠르지만, 표현 가능한 데이터 크기에 <strong>제한</strong>이 있음</li>
</ul>
<h3 id="2-직접-주소-지정-방식-direct-addressing">2. 직접 주소 지정 방식 (Direct Addressing)</h3>
<ul>
<li><strong>유효 주소를 직접 명시</strong></li>
<li>표현 가능한 주소의 범위가 <strong>연산 코드 길이에 제한</strong>받음</li>
</ul>
<h3 id="3-간접-주소-지정-방식-indirect-addressing">3. 간접 주소 지정 방식 (Indirect Addressing)</h3>
<ul>
<li><strong>유효 주소의 주소</strong>를 오퍼랜드 필드에 명시</li>
<li>표현 범위는 넓지만, <strong>메모리 접근이 2회</strong> 필요하여 느림</li>
</ul>
<h3 id="4-레지스터-주소-지정-방식-register-addressing">4. 레지스터 주소 지정 방식 (Register Addressing)</h3>
<ul>
<li>데이터를 저장한 <strong>레지스터를 오퍼랜드 필드에 명시</strong></li>
<li>메모리보다 빠름  </li>
<li>그러나 <strong>사용 가능한 레지스터 수에 제한</strong></li>
</ul>
<h3 id="5-레지스터-간접-주소-지정-방식-register-indirect-addressing">5. 레지스터 간접 주소 지정 방식 (Register Indirect Addressing)</h3>
<ul>
<li>데이터를 메모리에 저장하고,<br>그 주소를 저장한 <strong>레지스터를 오퍼랜드 필드에 명시</strong></li>
<li>간접 주소 지정과 유사하지만, <strong>메모리 접근은 1번만 필요</strong></li>
</ul>
<hr>
<h2 id="연산-코드의-유형">연산 코드의 유형</h2>
<p>명령어의 연산 코드는 다음과 같은 유형으로 나뉨.</p>
<ol>
<li><strong>데이터 전송 (Data Transfer)</strong>  </li>
<li><strong>산술/논리 연산 (Arithmetic/Logical Operations)</strong>  </li>
<li><strong>제어 흐름 변경 (Control Flow)</strong>  </li>
<li><strong>입출력 제어 (I/O Control)</strong></li>
</ol>
]]></description>
        </item>
    </channel>
</rss>