<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yellow-w.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 03 Mar 2026 16:11:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yellow-w.log</title>
            <url>https://velog.velcdn.com/images/yellow-w/profile/59e24331-4a03-415d-90de-7d91d214a789/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yellow-w.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yellow-w" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[ 리마] 3과목: 네트워크 및 보안]]></title>
            <link>https://velog.io/@yellow-w/%EB%A6%AC%EB%A7%88-3%EA%B3%BC%EB%AA%A9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%B0%8F-%EB%B3%B4%EC%95%88</link>
            <guid>https://velog.io/@yellow-w/%EB%A6%AC%EB%A7%88-3%EA%B3%BC%EB%AA%A9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%B0%8F-%EB%B3%B4%EC%95%88</guid>
            <pubDate>Tue, 03 Mar 2026 16:11:09 GMT</pubDate>
            <description><![CDATA[<h1 id="📘-3과목-네트워크-및-보안">📘 3과목: 네트워크 및 보안</h1>
<h2 id="20-tcpip-기본-용어기능-매칭-출제">20. TCP/IP 기본 용어(기능 매칭 출제)</h2>
<table>
<thead>
<tr>
<th>용어</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>IP</td>
<td>호스트 주소</td>
</tr>
<tr>
<td>Netmask</td>
<td>네트워크/호스트 구분</td>
</tr>
<tr>
<td>Gateway</td>
<td>외부망 출구</td>
</tr>
<tr>
<td>DNS</td>
<td>도메인 → IP</td>
</tr>
<tr>
<td>TCP</td>
<td>연결지향</td>
</tr>
<tr>
<td>UDP</td>
<td>비연결</td>
</tr>
</tbody></table>
<hr>
<h2 id="21-네트워크-명령어기능-매칭이-점수">21. 네트워크 명령어(기능 매칭이 점수)</h2>
<table>
<thead>
<tr>
<th>명령</th>
<th>기능</th>
</tr>
</thead>
<tbody><tr>
<td><code>ip addr</code></td>
<td>IP/인터페이스 확인</td>
</tr>
<tr>
<td><code>ip route</code></td>
<td>라우팅 테이블</td>
</tr>
<tr>
<td><code>ifconfig</code></td>
<td>인터페이스 확인(구식이지만 출제 가능)</td>
</tr>
<tr>
<td><code>netstat -an</code></td>
<td>연결/포트 확인(단골)</td>
</tr>
<tr>
<td><code>ss -lntp</code></td>
<td>리스닝 포트/프로세스(최근)</td>
</tr>
<tr>
<td><code>ping</code></td>
<td>통신 테스트</td>
</tr>
<tr>
<td><code>traceroute</code></td>
<td>경로 추적</td>
</tr>
<tr>
<td><code>nslookup</code>, <code>dig</code></td>
<td>DNS 조회</td>
</tr>
<tr>
<td><code>ssh user@host</code></td>
<td>원격 접속</td>
</tr>
</tbody></table>
<hr>
<h2 id="22-네트워크-설정-파일경로-문제-단골">22. 네트워크 설정 파일(경로 문제 단골)</h2>
<table>
<thead>
<tr>
<th>파일</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>/etc/hosts</code></td>
<td>로컬 호스트 이름 매핑</td>
</tr>
<tr>
<td><code>/etc/resolv.conf</code></td>
<td>DNS 서버 설정</td>
</tr>
<tr>
<td><code>/etc/services</code></td>
<td>서비스명↔포트 매핑(출제 가능)</td>
</tr>
</tbody></table>
<hr>
<h2 id="23-포트-번호암기-세트-90점이면-확장">23. 포트 번호(암기 세트: 90점이면 확장)</h2>
<h3 id="231-필수-포트">23.1 필수 포트</h3>
<table>
<thead>
<tr>
<th>서비스</th>
<th align="right">포트</th>
</tr>
</thead>
<tbody><tr>
<td>FTP</td>
<td align="right">21</td>
</tr>
<tr>
<td>SSH</td>
<td align="right">22</td>
</tr>
<tr>
<td>Telnet</td>
<td align="right">23</td>
</tr>
<tr>
<td>SMTP</td>
<td align="right">25</td>
</tr>
<tr>
<td>DNS</td>
<td align="right">53</td>
</tr>
<tr>
<td>HTTP</td>
<td align="right">80</td>
</tr>
<tr>
<td>HTTPS</td>
<td align="right">443</td>
</tr>
</tbody></table>
<h3 id="232-추가-포트자주-나오거나-함정">23.2 추가 포트(자주 나오거나 함정)</h3>
<table>
<thead>
<tr>
<th>서비스</th>
<th align="right">포트</th>
</tr>
</thead>
<tbody><tr>
<td>NTP</td>
<td align="right">123</td>
</tr>
<tr>
<td>POP3</td>
<td align="right">110</td>
</tr>
<tr>
<td>IMAP</td>
<td align="right">143</td>
</tr>
<tr>
<td>MySQL</td>
<td align="right">3306</td>
</tr>
</tbody></table>
<hr>
<h2 id="24-원격-접속서비스-보안">24. 원격 접속/서비스 보안</h2>
<h3 id="241-ssh-설정경로옵션-매칭-출제">24.1 SSH 설정(경로/옵션 매칭 출제)</h3>
<ul>
<li>설정 파일: <code>/etc/ssh/sshd_config</code></li>
</ul>
<table>
<thead>
<tr>
<th>옵션</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>Port</code></td>
<td>포트 번호</td>
</tr>
<tr>
<td><code>PermitRootLogin</code></td>
<td>root 로그인 허용 여부</td>
</tr>
<tr>
<td><code>PasswordAuthentication</code></td>
<td>패스워드 인증 허용 여부</td>
</tr>
</tbody></table>
<hr>
<h2 id="25-tcp-wrapper단골">25. TCP Wrapper(단골)</h2>
<table>
<thead>
<tr>
<th>파일</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>/etc/hosts.allow</code></td>
<td>허용</td>
</tr>
<tr>
<td><code>/etc/hosts.deny</code></td>
<td>차단</td>
</tr>
</tbody></table>
<hr>
<h2 id="26-xinetd--inetd-서비스-슈퍼데몬-개념">26. xinetd / inetd (서비스 슈퍼데몬 개념)</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td>xinetd 설정</td>
<td><code>/etc/xinetd.conf</code>, <code>/etc/xinetd.d/</code></td>
</tr>
<tr>
<td>inetd</td>
<td>구형 슈퍼데몬 개념(비교 문제 가능)</td>
</tr>
</tbody></table>
<hr>
<h2 id="27-방화벽iptables--90점-방어-필수">27. 방화벽(iptables) — 90점 방어 필수</h2>
<h3 id="271-iptables-핵심-개념">27.1 iptables 핵심 개념</h3>
<ul>
<li>체인: <code>INPUT</code>, <code>OUTPUT</code>, <code>FORWARD</code></li>
<li>정책/룰 기반 필터링</li>
</ul>
<h3 id="272-기본-명령개념명령-매칭">27.2 기본 명령(개념/명령 매칭)</h3>
<table>
<thead>
<tr>
<th>명령</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>iptables -L</code></td>
<td>룰 조회</td>
</tr>
<tr>
<td><code>iptables -F</code></td>
<td>룰 초기화(플러시) <em>(주의: 개념만)</em></td>
</tr>
</tbody></table>
<blockquote>
<p>시험은 실무 깊이보다 “iptables가 방화벽 도구” + “체인 이름”을 묻는 경우가 많음.</p>
</blockquote>
<hr>
<h2 id="28-파일서비스-공유nfssamba--개념경로가-점수">28. 파일/서비스 공유(NFS/Samba) — 개념+경로가 점수</h2>
<h3 id="281-nfs">28.1 NFS</h3>
<ul>
<li>설정 파일: <code>/etc/exports</code></li>
</ul>
<h3 id="282-samba">28.2 Samba</h3>
<ul>
<li>설정 파일: <code>/etc/samba/smb.conf</code></li>
</ul>
<hr>
<h2 id="29-로그네트워크보안-연계로-출제-가능">29. 로그(네트워크/보안 연계로 출제 가능)</h2>
<table>
<thead>
<tr>
<th>로그</th>
<th>위치(대표)</th>
</tr>
</thead>
<tbody><tr>
<td>시스템 로그</td>
<td><code>/var/log/messages</code></td>
</tr>
<tr>
<td>인증/보안 로그</td>
<td><code>/var/log/secure</code></td>
</tr>
<tr>
<td>부팅 로그</td>
<td><code>/var/log/boot.log</code></td>
</tr>
</tbody></table>
<blockquote>
<p>배포판에 따라 로그 파일명/위치가 달라질 수 있으나, 시험은 위 경로로 자주 출제됨.</p>
</blockquote>
<hr>
<h1 id="🎯-90점-체크리스트실수-방지용">🎯 90점 체크리스트(실수 방지용)</h1>
<h2 id="네트워크보안">네트워크/보안</h2>
<ul>
<li><input disabled="" type="checkbox"> 포트(21/22/23/25/53/80/443/110/143/123/3306) 암기</li>
<li><input disabled="" type="checkbox"> /etc/hosts / resolv.conf / services 암기</li>
<li><input disabled="" type="checkbox"> SSH 설정 파일 경로 + 3옵션(Port, PermitRootLogin, PasswordAuthentication)</li>
<li><input disabled="" type="checkbox"> TCP Wrapper allow/deny 경로</li>
<li><input disabled="" type="checkbox"> iptables 체인(INPUT/OUTPUT/FORWARD) 키워드</li>
<li><input disabled="" type="checkbox"> NFS/Samba 설정 파일 경로</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[보충] 메세지 큐와 이벤트 스트리밍]]></title>
            <link>https://velog.io/@yellow-w/%EB%B3%B4%EC%B6%A9-%EB%A9%94%EC%84%B8%EC%A7%80-%ED%81%90%EC%99%80-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D</link>
            <guid>https://velog.io/@yellow-w/%EB%B3%B4%EC%B6%A9-%EB%A9%94%EC%84%B8%EC%A7%80-%ED%81%90%EC%99%80-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D</guid>
            <pubDate>Sun, 11 Jan 2026 03:33:13 GMT</pubDate>
            <description><![CDATA[<h2 id="1-기본-개념-정의">1. 기본 개념 정의</h2>
<h3 id="메시지-큐-message-queue">메시지 큐 (Message Queue)</h3>
<ul>
<li>애플리케이션 간 비동기 통신을 위한 중간 저장소</li>
</ul>
<blockquote>
<p><strong>&quot;처리 완료 시 삭제되는 전달 모델에 최적화되어 있으며, 보존은 부가 기능&quot;</strong></p>
</blockquote>
<ul>
<li>메시지 전달(Delivery)이 주 목적</li>
<li>소비 후 삭제가 <strong>기본값</strong>이지만, 보존 모드도 지원 가능</li>
<li>생산자와 소비자의 시간적 결합 해제</li>
</ul>
<p><strong>대표 플랫폼:</strong></p>
<ul>
<li>RabbitMQ (AMQP 기반)</li>
<li>Amazon SQS (관리형 서비스)</li>
<li>ActiveMQ (JMS 기반)</li>
<li>ZeroMQ (브로커리스)</li>
</ul>
<h3 id="이벤트-스트리밍-event-streaming">이벤트 스트리밍 (Event Streaming)</h3>
<ul>
<li>시간순으로 이벤트를 기록하고 보존하는 분산 로그 시스템</li>
</ul>
<blockquote>
<p><strong>&quot;보존이 본질이고, 소비는 로그에 대한 뷰(View)&quot;</strong></p>
</blockquote>
<ul>
<li>이벤트 기록(Recording)이 주 목적</li>
<li>소비는 &quot;읽기 위치(offset/cursor)의 이동&quot;일 뿐, 데이터는 보존됨</li>
<li>같은 이벤트를 여러 소비자가 독립적으로 읽을 수 있음</li>
<li>재처리(Replay)가 핵심 기능</li>
</ul>
<p><strong>대표 플랫폼:</strong></p>
<ul>
<li>Apache Kafka</li>
<li>Amazon Kinesis</li>
<li>Apache Pulsar</li>
<li>Redpanda</li>
</ul>
<hr>
<h2 id="2-주요-특징-비교">2. 주요 특징 비교</h2>
<table>
<thead>
<tr>
<th>관점</th>
<th>메시지 큐</th>
<th>이벤트 스트리밍</th>
</tr>
</thead>
<tbody><tr>
<td><strong>본질</strong></td>
<td>전달(Delivery)</td>
<td>기록(Recording)</td>
</tr>
<tr>
<td><strong>데이터 모델</strong></td>
<td>임시 버퍼</td>
<td>영구 로그 (Append-only)</td>
</tr>
<tr>
<td><strong>소비의 의미</strong></td>
<td>메시지를 &quot;가져감&quot; (Take)</td>
<td>로그를 &quot;읽음&quot; (Read)</td>
</tr>
<tr>
<td><strong>삭제 정책</strong></td>
<td>처리 완료 시 삭제 (기본값)</td>
<td>보존 정책 만료 시 삭제</td>
</tr>
<tr>
<td><strong>보존</strong></td>
<td>부가 기능</td>
<td>핵심 기능</td>
</tr>
<tr>
<td><strong>재처리</strong></td>
<td>가능하지만 비효율적</td>
<td>설계된 핵심 기능</td>
</tr>
<tr>
<td><strong>메시지 성격</strong></td>
<td>명령(Command)</td>
<td>사실(Event)</td>
</tr>
</tbody></table>
<h3 id="개념-다이어그램">개념 다이어그램</h3>
<h4 id="메시지-큐---전달-중심">메시지 큐 - 전달 중심</h4>
<pre><code>                    ┌─────────────────┐
Producer ──────────▶│  Queue (버퍼)    │──────────▶ Consumer
                    │  M1 → M2 → M3   │     처리 완료 → 삭제
                    └─────────────────┘</code></pre><p>• 메시지가 목적지에 &quot;도착&quot;하면 임무 완료
• 삭제가 기본, 보존은 선택</p>
<h4 id="이벤트-스트리밍---기록-중심">이벤트 스트리밍 - 기록 중심</h4>
<pre><code>                    ┌─────────────────────────────────┐
Producer ──────────▶│  Log (영구 기록)                  │
                    │  [M1][M2][M3][M4][M5]...        │
                    │        ↑           ↑            │
                    │   Consumer A   Consumer B       │
                    │   (offset=2)   (offset=4)       │
                    └─────────────────────────────────┘</code></pre><p>• 메시지가 &quot;기록&quot;되면 임무 완료
• 소비자는 각자 읽기 위치만 관리
• 보존이 기본, 삭제는 정책에 따라</p>
<hr>
<h2 id="3-메시지-보존과-재처리">3. 메시지 보존과 재처리</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>메시지 큐</th>
<th>이벤트 스트리밍</th>
</tr>
</thead>
<tbody><tr>
<td><strong>설계 목적</strong></td>
<td>전달 후 삭제</td>
<td>보존</td>
</tr>
<tr>
<td><strong>기본 동작</strong></td>
<td>소비 후 삭제</td>
<td>소비 후 보존</td>
</tr>
<tr>
<td><strong>보존</strong></td>
<td>부가 기능</td>
<td>핵심 기능</td>
</tr>
<tr>
<td><strong>재처리(Replay)</strong></td>
<td>예외 상황용, 비효율적</td>
<td>일상적 사용, 효율적</td>
</tr>
<tr>
<td><strong>대용량 보존 시</strong></td>
<td>성능 저하</td>
<td>성능 유지</td>
</tr>
<tr>
<td><strong>쓰기 방식</strong></td>
<td>-</td>
<td>Append-only, 순차 I/O</td>
</tr>
<tr>
<td><strong>읽기 방식</strong></td>
<td>-</td>
<td>Zero-copy, Offset 기반</td>
</tr>
<tr>
<td><strong>삭제 방식</strong></td>
<td>메시지 단위</td>
<td>Segment 단위</td>
</tr>
</tbody></table>
<h3 id="플랫폼별-구현">플랫폼별 구현</h3>
<table>
<thead>
<tr>
<th>기능</th>
<th>RabbitMQ</th>
<th>Kafka</th>
</tr>
</thead>
<tbody><tr>
<td><strong>기본 동작</strong></td>
<td>ACK 후 삭제</td>
<td>보존 정책까지 유지</td>
</tr>
<tr>
<td><strong>기본 보존 기간</strong></td>
<td>없음 (즉시 삭제)</td>
<td>7일</td>
</tr>
<tr>
<td><strong>최대 보존 기간</strong></td>
<td>TTL 설정에 따라</td>
<td>무제한 가능</td>
</tr>
<tr>
<td><strong>보존 방법</strong></td>
<td>Lazy Queue, Quorum Queue, TTL+DLQ</td>
<td>기본 기능</td>
</tr>
<tr>
<td><strong>재처리 방법</strong></td>
<td>Requeue, Nack, DLQ 조회</td>
<td>Offset reset</td>
</tr>
</tbody></table>
<h3 id="재처리replay가-필요한-상황">재처리(Replay)가 필요한 상황</h3>
<ul>
<li>재처리가 필요한 아래의 예시 상황을 이벤트 스트리밍은 <strong>일상적 운영</strong>으로 처리, 메시지 큐는 <strong>예외적 상황</strong>으로 처리</li>
</ul>
<table>
<thead>
<tr>
<th>상황 예시</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>버그 수정 후</strong></td>
<td>잘못 처리된 데이터를 수정된 로직으로 재처리</td>
</tr>
<tr>
<td><strong>새 소비자 추가</strong></td>
<td>신규 서비스가 과거 이벤트를 처음부터 읽어야 할 때</td>
</tr>
<tr>
<td><strong>분석 재실행</strong></td>
<td>집계 로직 변경 후 전체 재계산</td>
</tr>
<tr>
<td><strong>장애 복구</strong></td>
<td>소비자 장애 시점부터 재처리</td>
</tr>
</tbody></table>
<hr>
<h2 id="4-순서-보장">4. 순서 보장</h2>
<h3 id="정확한-순서-보장-범위">정확한 순서 보장 범위</h3>
<table>
<thead>
<tr>
<th>시스템</th>
<th>순서 보장 범위</th>
<th>조건</th>
</tr>
</thead>
<tbody><tr>
<td><strong>메시지 큐 (개념)</strong></td>
<td>단일 큐 기준</td>
<td>단일 소비자일 때만 완전 보장</td>
</tr>
<tr>
<td><strong>이벤트 스트리밍 (개념)</strong></td>
<td>파티션/샤드 기준</td>
<td>파티션 내부만 보장</td>
</tr>
</tbody></table>
<h3 id="kafka의-순서-보장-범위">Kafka의 순서 보장 범위</h3>
<ul>
<li>파티션 내부의 순서만 보장</li>
<li>멀티 파티션에서 전역 순서는 보장되지 않는다</li>
</ul>
<h3 id="플랫폼별-순서-보장">플랫폼별 순서 보장</h3>
<table>
<thead>
<tr>
<th>조건</th>
<th>RabbitMQ</th>
<th>Kafka</th>
</tr>
</thead>
<tbody><tr>
<td><strong>단일 큐/파티션</strong></td>
<td>FIFO 보장</td>
<td>FIFO 보장</td>
</tr>
<tr>
<td><strong>단일 큐 + 다중 소비자</strong></td>
<td>처리 순서 보장 안됨</td>
<td>N/A (파티션당 1소비자)</td>
</tr>
<tr>
<td><strong>다중 큐/파티션</strong></td>
<td>전역 순서 없음</td>
<td>전역 순서 없음</td>
</tr>
<tr>
<td><strong>순서 보장 단위</strong></td>
<td>Queue</td>
<td>Partition</td>
</tr>
</tbody></table>
<h3 id="순서-보장-시나리오">순서 보장 시나리오</h3>
<h4 id="시나리오-주문-m1-→-m2-→-m3-순서로-발행">시나리오: 주문 M1 → M2 → M3 순서로 발행</h4>
<h5 id="case-1-단일-큐파티션--단일-소비자">Case 1: 단일 큐/파티션 + 단일 소비자</h5>
<pre><code>┌─────────────┐     ┌────────────┐
│ M1 → M2 → M3│ ──▶ │ Consumer A │ → 처리: M1, M2, M3
└─────────────┘     └────────────┘</code></pre><h5 id="case-2-단일-큐--다중-소비자-rabbitmq">Case 2: 단일 큐 + 다중 소비자 (RabbitMQ)</h5>
<pre><code>┌─────────────┐     ┌────────────┐
│ M1 → M2 → M3│ ──┬▶│ Consumer A │ → M1 처리
└─────────────┘   │ └────────────┘
                  │ ┌────────────┐
                  └▶│ Consumer B │ → M2 처리 (M1보다 먼저 완료될 수 있음) ⚠️
                    └────────────┘</code></pre><h5 id="case-3-다중-파티션-kafka">Case 3: 다중 파티션 (Kafka)</h5>
<pre><code>┌─ Partition 0: M1, M3 ─┐     ┌────────────┐
│                       │ ──▶ │ Consumer A │ → M1, M3 순서 보장
└───────────────────────┘     └────────────┘
┌─ Partition 1: M2 ─────┐     ┌────────────┐
│                       │ ──▶ │ Consumer B │ → M2
└───────────────────────┘     └────────────┘
→ M1, M2, M3 전역 순서는 보장되지 않음 ⚠️</code></pre><h3 id="전역-순서가-필요할-때">전역 순서가 필요할 때</h3>
<h4 id="해결책-1-단일-파티션큐-사용">해결책 1: 단일 파티션/큐 사용</h4>
<p>• 장점: 완벽한 순서 보장
• 단점: 병렬성 포기, 처리량 제한</p>
<h4 id="해결책-2-키-기반-파티셔닝">해결책 2: 키 기반 파티셔닝</h4>
<p>• 같은 키(예: user_id)는 같은 파티션으로
• 해당 엔티티 내에서만 순서 보장
• 예: user_123의 주문은 항상 순서대로</p>
<h4 id="해결책-3-애플리케이션-레벨-정렬">해결책 3: 애플리케이션 레벨 정렬</h4>
<p>• 타임스탬프 기반 재정렬
• 시퀀스 번호 부여
• 복잡도 증가</p>
<hr>
<h2 id="5-전달-보장-delivery-guarantees">5. 전달 보장 (Delivery Guarantees)</h2>
<h3 id="개념적-정의">개념적 정의</h3>
<table>
<thead>
<tr>
<th>보장 수준</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><strong>At-most-once</strong></td>
<td>유실 가능, 중복 없음</td>
</tr>
<tr>
<td><strong>At-least-once</strong></td>
<td>유실 없음, 중복 가능</td>
</tr>
<tr>
<td><strong>Exactly-once</strong></td>
<td>유실도 중복도 없음</td>
</tr>
</tbody></table>
<h3 id="exactly-once의-범위">Exactly-once의 범위</h3>
<h4 id="kafka의-exactly-once-범위">Kafka의 Exactly-once 범위</h4>
<ul>
<li>외부 시스템까지는 RabbitMQ와 동일한 한계를 가지므로 <strong>멱등성 구현이 필수</strong>적</li>
</ul>
<pre><code>Producer ──▶ Kafka Broker ──▶ Kafka Streams ──▶ Kafka Broker
   │              │                 │                │
   └──────────────┴─────────────────┴────────────────┘
              Exactly-once 가능 영역
              (Idempotent Producer + Transactions)


Kafka ──▶ 외부 DB    → Exactly-once 보장 안됨
Kafka ──▶ Redis      → Exactly-once 보장 안됨  
Kafka ──▶ REST API   → Exactly-once 보장 안됨</code></pre><h3 id="플랫폼별-exactly-once-지원">플랫폼별 Exactly-once 지원</h3>
<table>
<thead>
<tr>
<th>범위</th>
<th>RabbitMQ</th>
<th>Kafka</th>
</tr>
</thead>
<tbody><tr>
<td><strong>브로커 레벨</strong></td>
<td>네이티브 없음</td>
<td>Idempotent Producer</td>
</tr>
<tr>
<td><strong>내부 처리</strong></td>
<td>-</td>
<td>Kafka Streams Transactions</td>
</tr>
<tr>
<td><strong>외부 시스템</strong></td>
<td>애플리케이션 구현 필요</td>
<td>애플리케이션 구현 필요</td>
</tr>
<tr>
<td><strong>구현 방법</strong></td>
<td>멱등성 + 트랜잭션 조합</td>
<td>멱등성 + 트랜잭션 조합</td>
</tr>
</tbody></table>
<hr>
<h2 id="6-라우팅">6. 라우팅</h2>
<table>
<thead>
<tr>
<th>관점</th>
<th>메시지 큐</th>
<th>이벤트 스트리밍</th>
</tr>
</thead>
<tbody><tr>
<td><strong>라우팅 결정 주체</strong></td>
<td>브로커 (서버 사이드)</td>
<td>생산자 (클라이언트 사이드)</td>
</tr>
<tr>
<td><strong>철학</strong></td>
<td>&quot;브로커가 메시지를 적절한 곳에 배달&quot;</td>
<td>&quot;생산자가 데이터 배치를 결정&quot;</td>
</tr>
<tr>
<td><strong>유연성</strong></td>
<td>런타임에 라우팅 규칙 변경 가능</td>
<td>생산자 코드 변경 필요</td>
</tr>
<tr>
<td><strong>복잡도 위치</strong></td>
<td>브로커 설정</td>
<td>클라이언트 로직</td>
</tr>
</tbody></table>
<h3 id="조직-구조에-미치는-영향">조직 구조에 미치는 영향</h3>
<table>
<thead>
<tr>
<th>관점</th>
<th>RabbitMQ</th>
<th>Kafka</th>
</tr>
</thead>
<tbody><tr>
<td><strong>변경 영향</strong></td>
<td>Exchange 수정 → 전체 시스템 영향</td>
<td>Producer 코드 변경 → 해당 서비스만 영향</td>
</tr>
<tr>
<td><strong>통제 주체</strong></td>
<td>운영팀 (인프라)</td>
<td>개발팀 (애플리케이션)</td>
</tr>
<tr>
<td><strong>거버넌스</strong></td>
<td>중앙 집중</td>
<td>분산</td>
</tr>
</tbody></table>
<h3 id="플랫폼별-라우팅-기능">플랫폼별 라우팅 기능</h3>
<ul>
<li>RabbitMQ는 네트워크 중앙집중형 라우팅</li>
<li>Kafka는 데이터 배치 규칙을 클라이언트에게 강제하는 모델</li>
</ul>
<table>
<thead>
<tr>
<th>기능</th>
<th>RabbitMQ</th>
<th>Kafka</th>
</tr>
</thead>
<tbody><tr>
<td><strong>라우팅 결정</strong></td>
<td>브로커 (Exchange)</td>
<td>생산자 (Partitioner)</td>
</tr>
<tr>
<td><strong>Direct 라우팅</strong></td>
<td>Direct Exchange + Routing Key</td>
<td>토픽 선택</td>
</tr>
<tr>
<td><strong>패턴 라우팅</strong></td>
<td>Topic Exchange (*.log.#)</td>
<td>브로커 레벨 없음</td>
</tr>
<tr>
<td><strong>헤더 기반</strong></td>
<td>Headers Exchange</td>
<td>KIP-82 이후 Header 지원</td>
</tr>
<tr>
<td><strong>키 기반 분배</strong></td>
<td>Consistent Hash Exchange</td>
<td>Key-based Partitioning</td>
</tr>
<tr>
<td><strong>커스텀 로직</strong></td>
<td>플러그인</td>
<td>Custom Partitioner</td>
</tr>
<tr>
<td><strong>서버사이드 필터링</strong></td>
<td>가능</td>
<td>Kafka Streams로 구현</td>
</tr>
</tbody></table>
<hr>
<h3 id="라우팅-비교">라우팅 비교</h3>
<h4 id="rabbitmq---브로커-중심-라우팅">RabbitMQ - 브로커 중심 라우팅</h4>
<pre><code>Producer ──(routing_key=&quot;order.created&quot;)──▶ Exchange
                                              │
                    ┌─────────────────────────┼─────────────────────────┐
                    │                         │                         │
                    ▼                         ▼                         ▼
             order.* 바인딩            order.created 바인딩             # 바인딩
              ┌─────────┐                ┌─────────┐                ┌─────────┐
              │Order Svc│                │Analytics│                │ Logging │
              └─────────┘                └─────────┘                └─────────┘</code></pre><p>• Producer는 routing_key만 지정
• 어디로 갈지는 Exchange 설정이 결정
• 런타임에 바인딩 변경 가능</p>
<h4 id="kafka---생산자-중심-라우팅">Kafka - 생산자 중심 라우팅</h4>
<pre><code>Producer ──(key=&quot;user_123&quot;)──▶ Partitioner ──▶ Topic &quot;orders&quot;
                                    │
                    ┌───────────────┼───────────────┐
                    │               │               │
                    ▼               ▼               ▼
               Partition 0     Partition 1     Partition 2
               (user_1xx)      (user_2xx)      (user_3xx)
                    │
                    ▼
              Consumer Group</code></pre><p>• Producer가 키와 파티션 전략 결정
• Custom Partitioner로 복잡한 로직 구현 가능
• Header에 메타데이터 포함 (KIP-82)
• 소비자가 필요시 필터링</p>
<hr>
<h2 id="7-소비자consumer-모델">7. 소비자(Consumer) 모델</h2>
<h3 id="개념적-차이">개념적 차이</h3>
<table>
<thead>
<tr>
<th>특성</th>
<th>메시지 큐</th>
<th>이벤트 스트리밍</th>
</tr>
</thead>
<tbody><tr>
<td><strong>경쟁 소비</strong></td>
<td>기본 동작</td>
<td>그룹 내에서만</td>
</tr>
<tr>
<td><strong>브로드캐스트</strong></td>
<td>별도 구성 필요</td>
<td>기본 동작 (다중 그룹)</td>
</tr>
<tr>
<td><strong>소비자 상태</strong></td>
<td>브로커가 관리</td>
<td>소비자가 관리</td>
</tr>
<tr>
<td><strong>확장 한계</strong></td>
<td>이론적 무제한</td>
<td>파티션 수에 제한</td>
</tr>
<tr>
<td><strong>확장 방식</strong></td>
<td>운영적 (Consumer 추가)</td>
<td>구조적 (파티션 설계)</td>
</tr>
</tbody></table>
<h3 id="플랫폼별-구현-1">플랫폼별 구현</h3>
<table>
<thead>
<tr>
<th>특성</th>
<th>RabbitMQ</th>
<th>Kafka</th>
</tr>
</thead>
<tbody><tr>
<td><strong>경쟁 소비</strong></td>
<td>같은 큐 구독</td>
<td>Consumer Group</td>
</tr>
<tr>
<td><strong>브로드캐스트</strong></td>
<td>Fanout Exchange</td>
<td>여러 Consumer Group</td>
</tr>
<tr>
<td><strong>최대 병렬성</strong></td>
<td>제한 없음</td>
<td>파티션 수</td>
</tr>
<tr>
<td><strong>리밸런싱</strong></td>
<td>자동 (단순)</td>
<td>자동 (복잡, 성능 영향)</td>
</tr>
</tbody></table>
<h3 id="확장의-숨은-비용">확장의 숨은 비용</h3>
<h4 id="rabbitmq---운영적-확장">RabbitMQ - 운영적 확장</h4>
<pre><code>Queue ──┬──▶ Consumer 1
        ├──▶ Consumer 2
        ├──▶ Consumer 3
        └──▶ Consumer N  ← 그냥 추가하면 됨</code></pre><p>• Consumer만 늘리면 즉시 부하 분산
• 인프라 변경 없음</p>
<h4 id="kafka---구조적-확장">Kafka - 구조적 확장</h4>
<pre><code>파티션 증설 시 발생하는 일:
  1. 파티션 재할당 (Reassignment)
  2. 디스크 간 데이터 이동
  3. Consumer Rebalance
  4. 순간적인 Lag 발생
  5. 처리 지연 가능성</code></pre><p>• 파티션 수는 초기 설계 시 신중하게 결정해야 함
• 나중에 늘리면 비용이 따름</p>
<hr>
<h2 id="8-성능-특성">8. 성능 특성</h2>
<h3 id="개념적-차이-1">개념적 차이</h3>
<table>
<thead>
<tr>
<th>특성</th>
<th>메시지 큐</th>
<th>이벤트 스트리밍</th>
</tr>
</thead>
<tbody><tr>
<td><strong>최적화 목표</strong></td>
<td>낮은 지연시간</td>
<td>높은 처리량</td>
</tr>
<tr>
<td><strong>배치 처리</strong></td>
<td>메시지 단위</td>
<td>배치 단위</td>
</tr>
</tbody></table>
<h3 id="성능-병목의-원인">성능 병목의 원인</h3>
<ul>
<li>RabbitMQ: 메시지마다 ACK 처리, 삭제, 큐 재배치, 인덱스 갱신 필요</li>
<li>Kafka: Append만 하고, 삭제는 Segment 단위로 통째로 수행 (개별 삭제 없음)</li>
</ul>
<table>
<thead>
<tr>
<th>시스템</th>
<th>병목의 원인</th>
</tr>
</thead>
<tbody><tr>
<td>RabbitMQ</td>
<td>메시지 수명 관리 (ACK, delete, requeue, 인덱스 관리)</td>
</tr>
<tr>
<td>Kafka</td>
<td>순차 로그 I/O (삭제 비용 없음)</td>
</tr>
</tbody></table>
<h3 id="플랫폼별-성능">플랫폼별 성능</h3>
<table>
<thead>
<tr>
<th>지표</th>
<th>RabbitMQ</th>
<th>Kafka</th>
</tr>
</thead>
<tbody><tr>
<td><strong>처리량</strong></td>
<td>~50K msg/sec</td>
<td>~1M msg/sec</td>
</tr>
<tr>
<td><strong>지연 시간</strong></td>
<td>1-10ms</td>
<td>5-50ms</td>
</tr>
<tr>
<td><strong>Zero-copy</strong></td>
<td>-</td>
<td>지원</td>
</tr>
</tbody></table>
<hr>
<h2 id="9-상황별-선택-가이드">9. 상황별 선택 가이드</h2>
<table>
<thead>
<tr>
<th>상황</th>
<th>선택</th>
</tr>
</thead>
<tbody><tr>
<td>작업 분배, 재시도, 워커 풀</td>
<td>메시지 큐</td>
</tr>
<tr>
<td>상태 기록, 분석, 재처리</td>
<td>이벤트 스트리밍</td>
</tr>
<tr>
<td>명령(Command) 전달</td>
<td>메시지 큐</td>
</tr>
<tr>
<td>사실(Event) 기록</td>
<td>이벤트 스트리밍</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[보충] 자동완성에서의 데이터 샘플링]]></title>
            <link>https://velog.io/@yellow-w/%EC%9E%90%EB%8F%99%EC%99%84%EC%84%B1%EC%97%90%EC%84%9C%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%83%98%ED%94%8C%EB%A7%81</link>
            <guid>https://velog.io/@yellow-w/%EC%9E%90%EB%8F%99%EC%99%84%EC%84%B1%EC%97%90%EC%84%9C%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%83%98%ED%94%8C%EB%A7%81</guid>
            <pubDate>Mon, 17 Nov 2025 14:00:12 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-샘플링">데이터 샘플링</h2>
<ul>
<li>단순히 &quot;데이터 줄이기&quot;가 아니라 데이터 분포를 재조정하여 모델이 학습해야 할 목표 분포를 만드는 과정</li>
</ul>
<hr>
<h2 id="자동완성에서-실제로-쓰이는-샘플링-기법들">자동완성에서 실제로 쓰이는 샘플링 기법들</h2>
<h3 id="1-subsampling">1. Subsampling</h3>
<ul>
<li>과다 등장 쿼리/토큰의 등장 확률 제어</li>
<li>단순히 “빈도가 높은 쿼리를 일정 비율로 줄이기”가 아니라, 아래 두 가지 문제를 해결하기 위한 <strong>확률적 다운샘플링</strong><ul>
<li>Zipf&#39;s Law로 인해 상위 몇백 개 쿼리가 전체 데이터의 50% 이상 차지하는 문제 해결 </li>
<li>Transformer 학습 시 loss가 특정 패턴에 과도하게 최적화되는 것 방지</li>
</ul>
</li>
<li>결과<ul>
<li>데이터 다양성 증대</li>
<li>상위 쿼리 편향 감소</li>
<li>모델이 &quot;날씨&quot;, &quot;뉴스&quot;, &quot;유튜브&quot; 같은 패턴만 배우는 문제 완화
```text
예)</li>
</ul>
</li>
<li>“날씨”가 하루 50만 번 → 50만 개 모두 사용 X</li>
<li>확률 0.01로 줄여서 약 5천 개만 학습<pre><code></code></pre></li>
</ul>
<h3 id="2-weighted-time-samplingtemporal-decay-sampling">2. Weighted Time Sampling(Temporal Decay Sampling)</h3>
<ul>
<li>자동완성은 <code>트렌드 민감도가 매우 높기 때문에</code> 단순 시간 가중치가 아니라, 대부분 <strong>지수 감쇠(Exponential Decay) 기법</strong>을 사용</li>
<li>특징<ul>
<li>최근 검색 데이터는 거의 100% 유지 </li>
<li>오래된 데이터는 자동으로 가중치 감소 </li>
<li>일간/주간 트렌드 반영이 매우 빠름</li>
</ul>
</li>
<li>실제 구현은 <em>연속적인 decay curve</em> 형태로 시간별 sampling weight가 달라짐</li>
</ul>
<h3 id="3-frequency-aware-balanced-sampling구간-기반-균형-샘플링">3. Frequency-Aware Balanced Sampling(구간 기반 균형 샘플링)</h3>
<ul>
<li>Zipf 분포를 flatten 하기 위해 쿼리 빈도 로그를 구간(bin) 으로 나누고, 각 구간별 target distribution을 맞추는 방식</li>
<li>자동완성 모델의 목표: “다양한 패턴 + 충분한 희귀 패턴 + 과다 패턴 억제”<ul>
<li>다양성을 확보하기 위해 인기 검색어/드문 검색어 구간을 만들어 균형 있게 샘플링</li>
</ul>
</li>
<li>rare query oversampling 시 노이즈 비율이 증가할 수 있어서, <ul>
<li>정제된 rare bucket만 100% 사용하고 raw 로그 rare bucket은 일부만 retention 하기도 함</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th>검색어 종류</th>
<th>샘플링 비율</th>
</tr>
</thead>
<tbody><tr>
<td>상위 인기 1~100</td>
<td>1%</td>
</tr>
<tr>
<td>중간 인기 101~10,000</td>
<td>10%</td>
</tr>
<tr>
<td>드문 검색어</td>
<td>100%</td>
</tr>
</tbody></table>
<h3 id="4-negative-sampling-contextual-negative-mining">4. Negative Sampling (Contextual Negative Mining)</h3>
<ul>
<li>단순 랜덤 샘플링이 아니라, 문맥적으로 &quot;헷갈릴 만한&quot; 잘못된 후보를 생성하는 것이 핵심</li>
</ul>
<h4 id="1-유형">(1) 유형</h4>
<ul>
<li><p>Random negative </p>
<ul>
<li>가장 기본적인 수준</li>
<li>&quot;치킨&quot; → &quot;축구&quot; 같은 무관 negative</li>
</ul>
</li>
<li><p>Hard negative</p>
<ul>
<li>최근 모델들은 <strong>“문맥은 비슷하지만 자연스럽지 않은 continuation”</strong>을 생성한다</li>
</ul>
</li>
</ul>
<pre><code class="language-text">&quot;치킨&quot; → &quot;치킨 가격&quot;은 긍정
&quot;치킨&quot; → &quot;치킨 반지름&quot;은 hard negative </code></pre>
<ul>
<li>In-batch negative<ul>
<li>Transformer 학습에서 가장 많이 씀</li>
<li>batch 내 다른 라벨을 negative로 재사용</li>
<li>학습 효율 ↑, 메모리 효율 ↑</li>
</ul>
</li>
</ul>
<h4 id="2-목적">(2) 목적</h4>
<ul>
<li>모델이 자연스러운 다음 검색어를 구분하게 됨</li>
<li>perplexity 개선</li>
<li>자동완성 품질 상승</li>
</ul>
<h3 id="5-query-session-based-sampling-사용자-의도-단위-샘플링">5. Query Session Based Sampling (사용자 의도 단위 샘플링)</h3>
<ul>
<li>단순히 “세션을 묶어서 함께 샘플링”이 아니라, _사용자 의도 전환(Intent shift)_을 기준으로 세그먼트 분리 후 샘플링하는 방식</li>
</ul>
<h4 id="1-세션-분리-기준">(1) 세션 분리 기준</h4>
<ul>
<li>시간 기반: 30분 inactivity</li>
<li>intent 기반: 검색어 embedding cosine similarity가 threshold 이하일 때 split</li>
<li>action 기반: 클릭/스크롤 등 사용자 행동</li>
</ul>
<h4 id="2-이유">(2) 이유</h4>
<ul>
<li>자동완성은 “사용자가 다음에 무엇을 검색할까?” 를 예측하는 task이기 때문에 세션 기반 데이터는 strong supervision signal을 줌</li>
</ul>
<pre><code class="language-text">[치킨] → [치킨 배달] → [배달 시간]</code></pre>
<h3 id="6-deduplication-sampling-중복-제어--noise-reduction">6. Deduplication Sampling (중복 제어 + Noise Reduction)</h3>
<ul>
<li>단순 중복 제거가 아니라 아래 단계로 구성됨</li>
</ul>
<h4 id="1-exact-deduplication">(1) Exact deduplication</h4>
<ul>
<li>동일한 쿼리는 user-level / global-level로 중복 제거</li>
</ul>
<h4 id="2-fuzzy-deduplication">(2) Fuzzy deduplication</h4>
<ul>
<li>오타나 경미한 철자 차이가 있는 쿼리를 묶어서 처리<ul>
<li>예: “아이폰배터리” / “아이폰 베터리” / “iphone battery”</li>
</ul>
</li>
<li>Levenshtein distance 기반 또는 embedding distance 기반으로 병합</li>
</ul>
<h4 id="3-spamautomation-detection">(3) Spam/automation detection</h4>
<ul>
<li>의도적으로 반복되는 자동화 트래픽 제거</li>
<li>안티 스팸 모듈과 연동</li>
<li>이 단계에서 품질이 확연히 좋아짐</li>
</ul>
<hr>
<h2 id="실제-자동-완성-모델-학습-파이프라인에서의-샘플링-흐름">실제 자동 완성 모델 학습 파이프라인에서의 샘플링 흐름</h2>
<ul>
<li>전체 쿼리 로그 → 깨끗하게 정제(Cleaning) → 샘플링 → 모델 학습</li>
</ul>
<pre><code class="language-text">(원본 쿼리 로그: 5억~20억 건)
   ↓
1) Cleaning
   - 스팸/자동화 필터링
   - 인코딩/오타 normalization
   - fuzzy deduplication
   ↓
2) Frequency-based Subsampling
   - Zipf 상위 쿼리 다운샘플링
   - 각 bucket별 target distribution 설정
   ↓
3) Temporal Weighted Sampling
   - exponential decay 적용
   - 최근 트렌드 weight 강화
   ↓
4) Negative Sampling (In-batch + Hard negative mining)
   - 자연스러운/비자연스러운 continuation 데이터 구성
   ↓
5) Session-based Sampling
   - intent shift 기준으로 세션 segmentation
   - session-aware pair 생성
   ↓
[최종 학습 데이터 ≈ 1~5백만 건 수준의 고품질 샘플]
   ↓
Transformer Decoder / BERT Decoder-Only Autocomplete Model 학습
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 13장. 검색어 자동 완성 시스템]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-13%EC%9E%A5.-%EA%B2%80%EC%83%89%EC%96%B4-%EC%9E%90%EB%8F%99-%EC%99%84%EC%84%B1-%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-13%EC%9E%A5.-%EA%B2%80%EC%83%89%EC%96%B4-%EC%9E%90%EB%8F%99-%EC%99%84%EC%84%B1-%EC%8B%9C%EC%8A%A4%ED%85%9C</guid>
            <pubDate>Mon, 17 Nov 2025 13:59:40 GMT</pubDate>
            <description><![CDATA[<h1 id="13-검색어-자동-완성-시스템">13. 검색어 자동 완성 시스템</h1>
<ul>
<li>검색어 자동 완성: 웹사이트 검색창에 단어 입력 시 입력 중인 글자에 맞는 검색어가 자동으로 완성되어 표시되는 기능</li>
<li>autocomplete, typeahead, search-as-you-type, incremental search</li>
<li>가장 많이 이용된 검색어 k개를 자동완성하여 출력하는 시스템 설계</li>
</ul>
<h2 id="1단계-문제-이해-및-설계-범위-확정">1단계. 문제 이해 및 설계 범위 확정</h2>
<h3 id="1질문을-통한-요구사항-확인">1.질문을 통한 요구사항 확인</h3>
<ul>
<li>입력 단어는 자동완성될 검색어의 첫 부분으로 한정</li>
<li>5 개의 자동 완성 검색어 표시</li>
<li>자동완성 검색어 5개를 고르는 기준: 질의 빈도에 따라 정해지는 검색어 인기 순위를 기준</li>
<li>맞춤법 검사나 자동수정기능은 지원 X</li>
<li>질의는 영어. 여유가 있는 경우 다국어 지원 고려</li>
<li>모든 질의는 여어 소문자로 이루어지며, 대문자나 특수 문자는 처리하지 않음</li>
</ul>
<h3 id="2-요구사항">2. 요구사항</h3>
<h4 id="2-1-빠른-응답-속도">2-1. 빠른 응답 속도</h4>
<ul>
<li>사용자가 검색어를 입력함에따라 자동 완성 검색어도 빠르게 표시되어야 함</li>
<li>페이스북 검색어 자동 완성 시스템에 관한 문서: 시스템 응답 속도는 100ms 이내여야 함</li>
</ul>
<h4 id="2-2-연관성">2-2. 연관성</h4>
<ul>
<li>자동완성되어 출력되는 검색어는 사용자가 입력한 단어와 연관된 것이어야 함<h4 id="2-3-정렬">2-3. 정렬</h4>
</li>
<li>시스템 계산 결과는 인기도 등의 순위 모델(ranking model)에 의해 정렬되어 있어야 함</li>
</ul>
<h4 id="2-4-규모-확장성">2-4. 규모 확장성</h4>
<ul>
<li>시스템은 많은 트래픽을 감당할 수 있도록 확장 가능해야 함</li>
</ul>
<h4 id="2-5-고가용성">2-5. 고가용성</h4>
<ul>
<li>시스템은 계속 사용 가능해야 함</li>
</ul>
<h3 id="2-개략적-규모-추정">2. 개략적 규모 추정</h3>
<ul>
<li>일간 능동 사용자(DAU): 천만 명 가정</li>
<li>사용자당 일간 검색 수행 횟수: 10건</li>
<li>질의 데이터: 평균적으로 20바이트의 데이터 입력 가정<ul>
<li>문자 인코딩 방법으로는 아스키 사용 가정(1문자=1바이트)</li>
<li>질의문은 평균적으로 4개 단어로 이루어진다고 가정, 각 단어는 평균적으로 다섯 글자로 구성된다고 가정</li>
<li>질의당 평균 4 단어 * 5글자 = 20바이트</li>
</ul>
</li>
</ul>
<h2 id="2단계-개략적-설계안-제시-및-동의-구하기">2단계. 개략적 설계안 제시 및 동의 구하기</h2>
<h3 id="1-개략적인-시스템-분류">1. 개략적인 시스템 분류</h3>
<h4 id="1-1-데이터-수집-서비스data-gathering-service">1-1. 데이터 수집 서비스(data gathering service)</h4>
<ul>
<li>사용자가 입력한 질의를 실시간으로 수집하는 시스템</li>
</ul>
<h4 id="1-2-질의-서비스query-service">1-2. 질의 서비스(query service)</h4>
<ul>
<li>주어진 질의에 다섯 개의 인기 검색어를 정렬해 내놓는 서비스</li>
</ul>
<h2 id="3단계-상세-설계">3단계. 상세 설계</h2>
<h3 id="1-트라이trie-자료-구조">1. 트라이(trie) 자료 구조</h3>
<h4 id="1-1-트라이">1-1. 트라이</h4>
<ul>
<li>탐색 트리의 일종으로</li>
<li>이진 탐색 트리와 달리 트리의 어떤 노드도 그 노드 자체와 연관된 키는 저장하지 않음</li>
<li>대신 노드가 트리에서 차지하는 위치가 연관된 키를 정의</li>
</ul>
<h5 id="트라이-자료-구조의-핵심-아이디어">트라이 자료 구조의 핵심 아이디어</h5>
<pre><code class="language-text">(root)
 └─ t
      └─ r
      │    └─ e   ← tre
      │
      └─ o         ← to</code></pre>
<ul>
<li>트리 형태의 자료구조</li>
<li>루트 노드는 빈 문자열을 나타냄</li>
<li>각 노드는 글자(character) 하나를 저장, 최대 26개의 자식 노드를 가질 수 있음</li>
</ul>
<h5 id="트라이로-검색어-자동-완성-구현">트라이로 검색어 자동 완성 구현</h5>
<ul>
<li>가장 많이 사용된 질의어 K 찾기<ol>
<li>해당 접두어를 표현하는 노드를 찾는다. 시간 복잡도는 O(p)</li>
<li>하위 트리를 탐색하여 모든 유효 노드를 찾는다.</li>
<li>유효한 검색 문자열을 구성하는 노드가 유효 노드. 시간 복잡도는 O(c)</li>
<li>유효 노드들을 정렬하여 가장 인기 있는 검색어 k개를 찾는다. 시간 복잡도는 O(clog c)</li>
<li>알고리즘의 시간 복잡도<br>= 각 단계에 소요된 시간<br>= O(p) + O(c) + O(clog c)</li>
</ol>
</li>
</ul>
<h5 id="최적화-방안">최적화 방안</h5>
<ul>
<li>접두어의 최대 길이 제한(<code>O(p)</code> 에서 p 값 감소)</li>
<li>각 노드에 인기 검색어를 캐시<ul>
<li>각 노드에 k 개의 인기 검색어 저장 시, 전체 트라이 검색 방지</li>
<li>각 단계의 시간 복잡도가 O(1)로 변경 &gt; 최고 인기 검색어 k개를 찾는 전체 알고리즘의 복잡도도 O(1)로 변경</li>
</ul>
</li>
</ul>
<h3 id="2-데이터-수집-서비스">2. 데이터 수집 서비스</h3>
<h4 id="2-1-종전-방법의-문제점-사용자-타이핑-시-실시간-데이터-수정">2-1. 종전 방법의 문제점: 사용자 타이핑 시 실시간 데이터 수정</h4>
<ul>
<li>실시간 트라이 갱신 시 질의 서비스의 속도가 심각하게 느려질 것</li>
<li>인기 검색어는 그다지 자주 바뀌지 않기 때문에 트라이를 자주 갱신할 필요가 없음</li>
</ul>
<pre><code class="language-mermaid">flowchart LR
A[데이터 분석 서비스 로그]--&gt; B[로그 취합 서버] --&gt;C[취합된 데이터] 
C--&gt; D[작업 서버] --매주 갱신--&gt; E[트라이 데이터 베이스] --매주 DB 상태 스냅샷--&gt;F[트라이 케시]</code></pre>
<h4 id="2-2-새로운-방식">2-2. 새로운 방식</h4>
<h5 id="1-데이터-분석-서비스-로그">1) 데이터 분석 서비스 로그</h5>
<table>
<thead>
<tr>
<th>query</th>
<th>time</th>
</tr>
</thead>
<tbody><tr>
<td>tree</td>
<td>2025-11-17 22:01:01</td>
</tr>
<tr>
<td>try</td>
<td>2025-11-17 22:01:05</td>
</tr>
<tr>
<td>try</td>
<td>2025-11-17 22:01:08</td>
</tr>
<tr>
<td>toy</td>
<td>2025-11-17 22:02:01</td>
</tr>
</tbody></table>
<h5 id="2-로그-취합-서버">2) 로그 취합 서버</h5>
<ul>
<li>데이터 취합 실시간성의 중요성에 따른 분류<ul>
<li>실시간 앱: 데이터 취합 주기를 짧게 가져갈 필요가 있을 수 있음</li>
<li>대부분: 일주일에 한번 정도 로그 취합</li>
</ul>
</li>
</ul>
<h5 id="3-취합된-데이터">3) 취합된 데이터</h5>
<ul>
<li><p>time 필드: 해당 주의 시작 날짜</p>
</li>
<li><p>frequency 필드: 해당 질의가 해당 주에 사용된 횟수의 합</p>
<table>
<thead>
<tr>
<th>query</th>
<th>time</th>
<th>frequency</th>
</tr>
</thead>
<tbody><tr>
<td>tree</td>
<td>2025-11-03</td>
<td>12000</td>
</tr>
<tr>
<td>tree</td>
<td>2025-11-10</td>
<td>15000</td>
</tr>
<tr>
<td>tree</td>
<td>2025-11-17</td>
<td>9000</td>
</tr>
<tr>
<td>toy</td>
<td>2025-11-03</td>
<td>8500</td>
</tr>
<tr>
<td>toy</td>
<td>2025-11-17</td>
<td>6256</td>
</tr>
</tbody></table>
</li>
</ul>
<h5 id="4-작업-서버">4) 작업 서버</h5>
<ul>
<li>주기적으로 비동기적 작업을 실행하는 서버 집합</li>
<li>트라이 자료구조를 만들고 트라이 DB에 저장하는 역할 담당</li>
</ul>
<h5 id="5-트라이-캐시">5) 트라이 캐시</h5>
<ul>
<li>분산 캐시 시스템으로 트라이 데이터를 메모리에 유지하여 읽기 연산 성능을 높이는 구실</li>
<li>매주 트라이 DB의 스냅샷을 떠서 갱신</li>
</ul>
<h5 id="6-트라이-db">6) 트라이 DB</h5>
<ul>
<li>지속성 저장소</li>
<li>트라이 DB의 형태<ol>
<li>문서 저장소(document store): 주기적으로 트라이를 직렬화하여 DB에 저장</li>
<li>키-값 저장소: 해시 테이블 형태로 변환</li>
</ol>
</li>
</ul>
<h3 id="3-질의-서비스">3. 질의 서비스</h3>
<h4 id="3-1-비효율성을-개선한-설계안">3-1. 비효율성을 개선한 설계안</h4>
<pre><code class="language-mermaid">flowchart LR
A[사용자 단말] --&gt; B[로드 밸런서] --&gt;C[API 서버] --&gt; D[트라이캐시] --&gt;E[트라이 DB]</code></pre>
<ol>
<li>query가 로드밸런서로 전송됨</li>
<li>로드밸런서는 query를 API 서버로 전송</li>
<li>API 서버는 트라이 캐시에서 데이터를 가져와 해당 요청에 대한 자동 ㅗ안성 검색어 제안 응답 구성</li>
<li>데이터가 트라이 캐시에 없는 경우 데이터를 DB에서 가져와 캐시에 채움</li>
</ol>
<h4 id="3-2-질의-서비스-최적화-방안">3-2. 질의 서비스 최적화 방안</h4>
<h5 id="ajax-요청">AJAX 요청</h5>
<h5 id="브라우저-캐싱">브라우저 캐싱</h5>
<ul>
<li>대부분의 앱에서 자동 완성 검색어 제안 결과는 짧은 시간 안에 자주 바뀌지 않음</li>
<li>따라서 제안된 검색어들을 브라우저 캐시에 넣어두어 후속 질의 결과는 캐시에서 바로 가져갈 수 있게 함<h5 id="데이터-샘플링">데이터 샘플링</h5>
</li>
<li>N개 요청 가운데 1개만 로깅</li>
</ul>
<h3 id="4-트라이-연산">4. 트라이 연산</h3>
<h4 id="4-1-트라이-생성">4-1. 트라이 생성</h4>
<ul>
<li>데이터 분석 서비스의 로그나, DB로부터 취합된 데이터를 이용하여, 작업 서버가 담당</li>
</ul>
<h4 id="4-2-트라이-갱신-방법">4-2. 트라이 갱신 방법</h4>
<ul>
<li>매주 한 번 갱신. 새로운 트라이를 만든 후 기존 트라이를 <strong>대체</strong></li>
<li>트라이의 <strong>각 노드를 개별적으로</strong> 갱신<ul>
<li>일반적으로 성능이 좋지 않지만, 트라이를 작을 때는 고려해볼만 함</li>
</ul>
</li>
</ul>
<h4 id="4-3-검색어-삭제">4-3. 검색어 삭제</h4>
<ul>
<li>트라이 캐시 앞에 필터 계층(filter layer)을 두고 부적절한 질의어가 반환되지 않도록 함</li>
</ul>
<h3 id="5-규모-확장이-가능한-저장소">5. 규모 확장이 가능한 저장소</h3>
<ul>
<li>첫 글자를 기준으로 샤딩<ul>
<li>서버를 26대 이상으로 늘리려면 계층적 샤딩 적용</li>
<li>데이터의 균등 분배가 불가능함</li>
</ul>
</li>
<li>과거 질의 데이터의 패턴을 분석하여 샤딩<ul>
<li>검색어 대응 샤드 관리자(shard map manager): 어떤 검색어가 어느 저장소 서버에 저장되는 지에 대한 정보 관리</li>
</ul>
</li>
</ul>
<h2 id="4단계-마무리">4단계. 마무리</h2>
<h3 id="다국어-지원이-가능하도록-시스템-확장">다국어 지원이 가능하도록 시스템 확장</h3>
<ul>
<li>트라이에 유니코드 데이터 저장</li>
</ul>
<h3 id="국가별로-인기-검색어-순위가-다른-경우">국가별로 인기 검색어 순위가 다른 경우</h3>
<ul>
<li>국가별로 다른 트라이 사용</li>
<li>트라이를 CDN에 저장하여 응답 속도를 높일 수 있음</li>
</ul>
<h3 id="실시간-검색어-자동완성-시스템-구축-시-고려할-점">실시간 검색어 자동완성 시스템 구축 시 고려할 점</h3>
<ul>
<li>샤딩을 통한 작업 대상 데이터 양 줄이기</li>
<li>순위 모델을 변경하여 최근 검색어에 높은 가중치 주기</li>
<li>데이터가 스트림 형태로 올 수 있다는 점을 고려(스트림 프로세싱에 필요한 시스템)<ul>
<li>아파치 하둡 맵리듀스</li>
<li>아파치 스파크 스트리밍</li>
<li>아파치 스톰</li>
<li>아파치 카프카 등</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[6장. AOP 정리]]></title>
            <link>https://velog.io/@yellow-w/6%EC%9E%A5.-AOP-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@yellow-w/6%EC%9E%A5.-AOP-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sun, 09 Nov 2025 15:23:35 GMT</pubDate>
            <description><![CDATA[<h2 id="64-스프링의-프록시-팩토리-빈">6.4 스프링의 프록시 팩토리 빈</h2>
<p>스프링은 일관된 방법으로 프록시를 만들 수 있게 도와주는 추상 레이어를 제공한다
생성된 프록시는 스프링 빈으로 등록되어야 하며, 스프링은 프록시 객체를 생성해주는 기술을 추상화한 팩토리빈 <code>ProxyFactoryBean</code>을 제공해준다</p>
<h3 id="1-프록시에서-사용할-부가-기능">1. 프록시에서 사용할 부가 기능</h3>
<ul>
<li><code>MethodInterceptor</code> 인터페이스를 구현해서 만든다</li>
<li>``MethodInterceptor<code>의</code>invoke()<code>는</code>ProxyFactoryBean`으로부터 타깃 객체에 대한 정보를 함께 제공받기 떄문에, 타깃 객체에 상관없이 독립적으로 생성되어질 수 있다</li>
<li><code>MethodInterceptor</code> 객체는 타깃이 다른 여러 프록시에서 함께 사용할 수 있고, 싱글톤 빈으로 등록 가능하다</li>
</ul>
<h3 id="2-어드바이스-타깃-객체에-종속되지-않는-순수한-부가-기능을-담은-객체">2. 어드바이스: 타깃 객체에 종속되지 않는 순수한 부가 기능을 담은 객체</h3>
<ul>
<li><code>MethodInvocation</code>의 동작<ul>
<li><code>MethodInvocation</code>은 일종의 콜백 객체로, <code>proceed()</code>를 실행하면 타깃 객체의 메소드를 내부적으로 실행해주는 기능을 가지고 있다.</li>
<li><code>MethodInvocation</code> 구현 클래스는 일종의 공유 가능한 템플릿처럼 동작한다.</li>
<li>앞서 말한 <code>MethodInvocation</code>의 동작 때문에<code>MethodInterceptor</code>는 부가 기능을 제공하는 데만 집중할 수 있다.</li>
</ul>
</li>
<li>여러 개의 부가 기능 제공<ul>
<li><code>ProxyFactoryBean</code>에 <code>MethodInterceptor</code> 설정 시, <code>addAdvice()</code>를 사용해서 여러 <code>MethodInterceptor</code>를 동시에 추가할 수 있다.</li>
<li>즉, <code>ProxyFactoryBean</code> 하나로 여러 부가 기능을 제공하는 프록시를 생성할 수 있다.</li>
</ul>
</li>
<li>참고: <code>MethodInterceptor</code>는 <code>Advice</code> 인터페이스를 상속하고 있는 서브 페이스</li>
</ul>
<h3 id="3-포인트컷-부가기능-적용-대상-메소드-선정-방법">3. 포인트컷: 부가기능 적용 대상 메소드 선정 방법</h3>
<h3 id="4-프록시-동작">4. 프록시 동작</h3>
<ol>
<li>프록시가 클라이언트로부터 요청을 받는다.</li>
<li>포인트컷에 부가 기능을 부여할 메서드인지를 확인해달라고 요청한다.</li>
<li><code>MethodInterceptor</code> 타입의 어드바이스를 호출한다.</li>
</ol>
<h3 id="5-사용된-디자인-패턴">5. 사용된 디자인 패턴</h3>
<h4 id="전략-패턴-구조">전략 패턴 구조</h4>
<ul>
<li>프록시로부터 어드바이스와 포인트컷을 독립시키고 DI를 사용하게 한 덕분에, <ol>
<li>여러 프록시가 공유해서 사용할 수 있고, </li>
<li>구체적인 부가 기능 방식이나 메서드 선정 알고리즘이 바뀌면 구현 클래스만 변경해서 설정에 넣어주면 된다</li>
</ol>
</li>
</ul>
<h4 id="템플릿콜백-구조">템플릿/콜백 구조</h4>
<ul>
<li>재사용 가능한 기능을 만들어두고 변경되는 부분(콜백 객체와 메서드 호출정보)만 외부에서 주입해서 작업 흐름(부가기능 부여) 중에 사용하는 구조<ul>
<li>어드바이스: 일종의 템플릿</li>
<li>타깃을 호출하는 기능을 가지고 있는 <code>MethodInvocation</code> 객체: 콜백</li>
</ul>
</li>
</ul>
<h3 id="6-어드바이저-어드바이스와-포인트-컷을-묶은-조합">6. 어드바이저: 어드바이스와 포인트 컷을 묶은 조합</h3>
<h3 id="7-proxyfactorybean-적용">7. <code>ProxyFactoryBean</code> 적용</h3>
<ul>
<li><code>MethodInterceptor</code>라는 <code>Advice</code> 서브 인터페이스를 구현한 <code>TransactionAdvice</code> 작성</li>
<li><code>MethodInvocation</code> 타입의 콜백을 이용해서 번거로운 타깃 메서드 호출 작업을 대부분 제거</li>
<li>타깃 메서드가 던지는 예외 또한 <code>InvocationTargetException</code>으로 래핑되지 않고 그대로 캐치해서 처리<pre><code class="language-java">public class TransactionAdvice implements MethodInterceptors{    // 스프링의 인터페이스 구현
</code></pre>
</li>
</ul>
<p>// 중략</p>
<p>// 타깃을 호출하는 기능을 가진 콜백 객체(MehotdInvocation 타입의 객체)를 프록시로부터 받음
public Object invoke(MehotdInvocation invocation)throws Throwable{</p>
<p>// 중략
  try{
      Object ret = invocation.proceed(); // 콜백을 호출해서 타깃 메서드 실행 가능
  } catch (RuntimeException e){    // 예외가 래핑되지 않고 타깃에서 보낸 그대로 전달된다</p>
<p>// 생략</p>
<pre><code>

## 6.5 스프링AOP
### 1. 자동 프록시 생성기
- 스프링은 컨테이너로서 제공하는 기능 중 많은 부분을 확장할 수 있도록 확장 포인트를 제공해준다.
- `DefaultAdvisorAutoProxyCreator`는 어드바이저를 이용한 자동 프록시 생성기로, 스프링은 빈 후처리기가 빈으로 등록되어 있으면 빈 객체가 생성될 때마다 빈 후처리기에 보내서 후처리 작업을 요청한다.

#### 빈 후처리기를 이용한 자동 프록시 생성 방법
1. `DefaultAdvisorAutoProxyCreator` 빈 후처리기가 등록되어 있으면, 스프링은 빈 객체 생성마다 후 처리기에 빈을 보낸다.
2. `DefaultAdvisorAutoProxyCreator`는 포인트컷을 이용해 전달받은 빈이 프록시 적용 대상인지를 확인한다.
3. 프록시 적용 대상일 경우,
    1) 내장되 프록시 생성기에게 현재 빈에 대한 프록시를 생성하게 한다.
    2) 만들어진 프록시에 어드바이저를 연결해준다.
    3) 프록시가 생성되면 원래 컨테이너가 전달해준 빈 객체 대신 프록시 객체를 컨테이너에게 돌려준다.
4. 컨테이너는 최종적으로 빈 후처리기가 돌려준 객체를 빈으로 등록하고 사용한다.


### 2. 포인트컷의 두 가지 기능
- 포인트컷은 `ClassFilter`와 `MethodMatcher` 두 가지를 돌려주는 메서드를 가지고 있다
- 만약 포인트컷 선정 기능을 모두 적용한다면 아래와 같은 순서로 동작하게 된다.
    1. 프록시를 적용할 클래스인지 판단
    2. 적용 대상 클래스 인 경우, 어드바이스를 적용할 메소드인지를 확인

- `ProxyFactoryBean`에서는 굳이 클래스 레벨의 필터는 필요 없었지만, `DefaultAdvisorAutoProxyCreator`는 클래스와 메서드 선정 알고리즘을 모두 갖고 있는 포인트컷과 어드바이스가 결합되어 있는 어드바이저가 필요하다.


#### 클래스 필터가 포함된 포인트컷
```java
public class NameMatchClassMethodPointcut extends NameMatchMethodPointcut{
    public void setMappedClassName(String mappedClassName){
        this.setClassFilger(new SimpleClassFilter(mappedClassName));
        // 모든 클래스를 다 허용하던 디폴트 클래스 필터를 
        // 프로퍼티로 받은 클래스 이름을 이용해서 필터를 만들어 덮어씌움

        // 생략</code></pre><h4 id="포인트컷-표현식">포인트컷 표현식</h4>
<ul>
<li>포인트컷 표현식: 스프링이 제공하는 간단하고 효과적인 포인트컷의 클래스/메서드 선정 알고리즘 작성 방법</li>
<li>스프링이 사용하는 포인트컷 표현식은<code>AspectJ</code> 프레임 워크의 일부 문법을 확장한 표현식으로, <code>AspectJExpressionPointcut</code> 클래스를 사용해서 적용 가능하다.</li>
</ul>
<h4 id="aspectj-포인트컷-표현식">AspectJ 포인트컷 표현식</h4>
<ul>
<li>포인트컷 지시자를 이용해서 작성하며, 가장 대표적으로 사용되는 지시자는 <code>execution()</code></li>
</ul>
<pre><code class="language-java">execution([접근제한자 패턴] 타입패턴 [타입패턴.]이름패턴 (타입패턴 | &quot;..&quot;, ...)[throws 예외패턴])</code></pre>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td>접근제한자 패턴</td>
<td>public, private 등</td>
<td><code>public</code></td>
</tr>
<tr>
<td>타입패턴</td>
<td>반환 타입 (와일드카드 <code>*</code> 가능)</td>
<td><code>*</code>, <code>String</code>, <code>void</code></td>
</tr>
<tr>
<td>타입패턴.</td>
<td>메서드가 속한 클래스의 경로</td>
<td><code>com.example.service.*.</code></td>
</tr>
<tr>
<td>이름패턴</td>
<td>메서드 이름 (와일드카드 가능)</td>
<td><code>get*</code>, <code>*Service</code>, <code>save</code></td>
</tr>
<tr>
<td>(타입패턴, ...)</td>
<td>매개변수 타입 (0개 이상)</td>
<td><code>(int, String)</code>, <code>(..)</code></td>
</tr>
<tr>
<td>throws 예외패턴</td>
<td>던질 수 있는 예외 타입</td>
<td><code>throws IOException</code></td>
</tr>
</tbody></table>
<h4 id="포인트컷-표현식-문법">포인트컷 표현식 문법</h4>
<ul>
<li>AspectJ 포인트컷 표현식은 포인트컷 지시자를 이용해 작서                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           l</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 8장. URL 단축기 설계]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-8%EC%9E%A5.-URL-%EB%8B%A8%EC%B6%95%EA%B8%B0-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-8%EC%9E%A5.-URL-%EB%8B%A8%EC%B6%95%EA%B8%B0-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Sun, 12 Oct 2025 13:43:43 GMT</pubDate>
            <description><![CDATA[<h2 id="1단계-문제-이해-및-설계-범위-확정">1단계. 문제 이해 및 설계 범위 확정</h2>
<h3 id="1-url-단축기의-동작">1. URL 단축기의 동작</h3>
<h4 id="1-1-단축-url-제공-기능">1-1. 단축 URL 제공 기능</h4>
<ul>
<li><code>https://www.example.com/q=chatsystem&amp;c=loggedin&amp;v=3&amp;l=long</code>이라고 입력이 주어진 경우</li>
<li>서비스에서 <code>https://www.tinyurl.com/y7ke-ocwj</code>와 같은 단축 URL을 결과로 제공해야 함<h4 id="1-2-url-리디렉션-기능">1-2. URL 리디렉션 기능</h4>
</li>
<li>해당 단축 URL을 통해 원래 URL로 갈 수도 있어야 함</li>
</ul>
<h3 id="2-트래픽-규모">2. 트래픽 규모</h3>
<ul>
<li>매일 1억 개의 단축 URL 생성 가능<h3 id="3-단축-url의-길이는">3. 단축 URL의 길이는?</h3>
</li>
<li>짧을 수록 좋음<h3 id="4-단축-url-포함-문제에-대한-제한">4. 단축 URL 포함 문제에 대한 제한</h3>
</li>
<li>숫자(0-9)와 영문자(a-z, A-Z)만 사용 가능</li>
</ul>
<h3 id="5-단축url을-삭제하거나-갱신하는-기능">5. 단축URL을 삭제하거나 갱신하는 기능</h3>
<ul>
<li>시스템 단순화를 위해 삭제나 갱신 기능은 없다고 가정</li>
</ul>
<h3 id="6-높은-가용성과-규모-확장성-장애-감내가-요구됨">6. 높은 가용성과 규모 확장성, 장애 감내가 요구됨</h3>
<hr>
<h2 id="2단계-개략적-설계얀-제시-및-동의-구하기">2단계. 개략적 설계얀 제시 및 동의 구하기</h2>
<h3 id="1-api-엔드포인트">1. API 엔드포인트</h3>
<ul>
<li>URL 단축기는 기본적으로 두 개의 엔드포인트를 필요로 함<ul>
<li>URL 단축용 엔드포인트</li>
<li>URL 리디렉션용 엔드포인트</li>
</ul>
</li>
</ul>
<h3 id="2-url-리디렉션">2. URL 리디렉션</h3>
<h4 id="2-1-리디렉션-응답-비교">2-1. 리디렉션 응답 비교</h4>
<ul>
<li>서버부하를 줄이는 것이 중요하다면 301</li>
<li>트래픽 분석이 중요할 때는 302</li>
</ul>
<h4 id="2-2-301-vs-302">2-2. 301 vs 302</h4>
<table>
<thead>
<tr>
<th>구분</th>
<th><strong>301 Moved Permanently</strong></th>
<th><strong>302 Found (또는 Moved Temporarily)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>의미</strong></td>
<td>영구적인 리디렉션 (URL이 완전히 변경됨)</td>
<td>임시적인 리디렉션 (일시적으로 다른 URL로 이동)</td>
</tr>
<tr>
<td><strong>용도</strong></td>
<td>사이트 이전, 구조 변경, 영구 URL 교체 등</td>
<td>트래킹, 테스트, 로그인 후 리디렉션 등</td>
</tr>
<tr>
<td><strong>브라우저 동작</strong></td>
<td>응답을 캐싱<br>이후 요청에서도 새 URL로 직접 접근</td>
<td>원래 URL로 계속 요청함 (서버가 계속 리디렉션 지시)</td>
</tr>
<tr>
<td><strong>검색엔진 처리(SEO)</strong></td>
<td>링크 가치(Link juice)를 새 URL로 이전</td>
<td>링크 가치 유지되지 않음 (임시로 간주)</td>
</tr>
<tr>
<td><strong>캐싱</strong></td>
<td>캐시될 수 있음 (영구이므로)</td>
<td>일반적으로 캐시하지 않음</td>
</tr>
<tr>
<td><strong>HTTP 응답 코드</strong></td>
<td><code>301 Moved Permanently</code></td>
<td><code>302 Found</code> (이전엔 “Moved Temporarily”)</td>
</tr>
<tr>
<td><strong>Location 헤더</strong></td>
<td>새 URL을 지정 (필수)</td>
<td>새 URL을 지정 (필수)</td>
</tr>
<tr>
<td><strong>브라우저 히스토리 영향</strong></td>
<td>새 URL로 교체될 수 있음</td>
<td>원래 URL 유지</td>
</tr>
<tr>
<td><strong>주요 사용 사례</strong></td>
<td>도메인 변경, HTTPS 전환, 슬러그 변경</td>
<td>로그인/세션 리디렉션, A/B 테스트, 추적 URL</td>
</tr>
</tbody></table>
<h4 id="2-3-url-리디렉션-구현">2-3. URL 리디렉션 구현</h4>
<pre><code class="language-text">원래 URL = hashTable.get(단축 URL)
301 또는 302 응답 Location 헤더에 원래 URL을 넣은 후 전송</code></pre>
<h3 id="3-url-단축">3. URL 단축</h3>
<ul>
<li>단축 URL이 <code>www.tinyurl.com/{hashValue}</code>와 같은 형태일 때,</li>
<li>결국 중요한 것은 긴 URL을 해시 값으로 대응시킬 해시 함수를 찾는 일</li>
</ul>
<h4 id="3-1-해시-함수가-만족해야-하는-요구사항">3-1. 해시 함수가 만족해야 하는 요구사항</h4>
<ul>
<li>입력으로 주어지는 URL이 다른 값이면 해시 값도 달라야 함</li>
<li>계산된 해시값은 원래 입력으로 주어졌던 긴 URL로 복원될 수 있어야 함 </li>
</ul>
<hr>
<h2 id="3단계-상세-설계">3단계. 상세 설계</h2>
<h3 id="1-데이터-모델">1. 데이터 모델</h3>
<ul>
<li>&lt;단축 URL, 원래 URL&gt;의 순서쌍을 관계형 DB에 저장</li>
<li><img src="https://velog.velcdn.com/images/yellow-w/post/03de1b5d-a864-466d-806c-56a55eff8c9f/image.png" alt="설명" class="left-block-image" style="width:30%;"/>

</li>
</ul>
<h3 id="2-해시-함수">2. 해시 함수</h3>
<ul>
<li>hashValue는 [0-9, a-z, A-Z]의 문자들로 구성됨(10+26+26=62개)</li>
<li>hashValue의 길이를 정하기 위해서는 62^n&gt;=3650억인 n의 최솟값을 찾아야 함</li>
<li>n이 7일 때 62^7=3.5조 개의 URL 생성 가능하므로 hashValue의 길이는 7로 결정</li>
</ul>
<h4 id="2-1-해시-후-충돌-해소-기법">2-1. 해시 후 충돌 해소 기법</h4>
<ul>
<li>긴 URL을 줄이기 위해서 원래 URL을 7글자 문자열로 줄이는 해시함수가 필요함 </li>
</ul>
<h5 id="1-해시-함수">1) 해시 함수</h5>
<ul>
<li>비암호학적 해시 함수: MurmurHash, CRC32, FNV-1a 등 (빠르고 단순)</li>
<li>암호학적 해시 함수: SHA-1, SHA-256, MD5 등 (보안적 목적 시)</li>
</ul>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th>해시 길이</th>
<th>특징</th>
<th>단축 URL 관점</th>
</tr>
</thead>
<tbody><tr>
<td><strong>CRC32</strong></td>
<td>32bit (약 4.3×10⁹개 경우)</td>
<td>매우 빠름, 단순 체크용</td>
<td>빠르지만 충돌 확률 높음</td>
</tr>
<tr>
<td><strong>MD5</strong></td>
<td>128bit</td>
<td>균등 분포, 널리 사용</td>
<td>속도와 품질 균형 좋음</td>
</tr>
<tr>
<td><strong>SHA-1</strong></td>
<td>160bit</td>
<td>충돌 매우 희박, 보안성 높음</td>
<td>단축 URL엔 다소 과하지만 안정적</td>
</tr>
</tbody></table>
<h5 id="2-해시-재계산-방식rehashing-with-salt">2) 해시 재계산 방식(Rehashing with Salt)</h5>
<ul>
<li>URL을 해시한 후 이미 존재하는(=충돌한) 단축 코드라면, 미리 정한 문자열(또는 숫자)을 붙여 다시 해시</li>
<li>해시 공간을 바꿔서 서로 다른 해시 결과를 강제로 생성하는 방식</li>
<li><pre><code class="language-bash">hash(&quot;https://example.com/a&quot;) → &quot;A7xPqL2&quot;
이미 존재함 → 충돌 발생!
hash(&quot;https://example.com/a&quot; + &quot;1&quot;) → &quot;bK0RzTf&quot;
새 키 확보 </code></pre>
</li>
</ul>
<h5 id="3-bloom-filter">3) Bloom Filter</h5>
<ul>
<li>확률적 집합 존재 여부(“이 값이 이미 등록되었는가?”) 를 빠르게 판별하기 위한 비트 배열 기반 데이터 구조</li>
<li>충돌 이전에 “이 값이 이미 존재할 가능성이 있나?”를 빠르게 추정하는 확률적 필터<ul>
<li>여러 개의 서로 다른 해시 함수를 사용 (k개)</li>
<li>입력값을 각각 해시 → 비트 배열의 인덱스로 사용 → 비트들을 1로 설정 </li>
<li>새 값이 들어올 때 → 동일한 해시로 검사 → 해당 비트가 모두 1이면 “이미 존재 가능성이 있음”</li>
</ul>
</li>
</ul>
<h4 id="2-2-base-62-변환">2-2. base-62 변환</h4>
<ul>
<li><p>hashValue에 사용할 수 있는 문자 수가 62개이기 때문에 base62 변환</p>
</li>
<li><p>문자 매핑 가능</p>
<ul>
<li><table>
<thead>
<tr>
<th>값</th>
<th>문자</th>
<th>값</th>
<th>문자</th>
<th>값</th>
<th>문자</th>
</tr>
</thead>
<tbody><tr>
<td>0–9</td>
<td><code>&#39;0&#39;–&#39;9&#39;</code></td>
<td>10–35</td>
<td><code>&#39;A&#39;–&#39;Z&#39;</code></td>
<td>36–61</td>
<td><code>&#39;a&#39;–&#39;z&#39;</code></td>
</tr>
</tbody></table>
</li>
</ul>
</li>
<li><p>base-62 변환 원리</p>
</li>
</ul>
<img src="https://velog.velcdn.com/images/yellow-w/post/b16ef06c-1d82-4072-9230-5c3831dc6aa0/image.png" alt="설명" class="left-block-image" style="width:100%;"/>


<h4 id="2-3-해시-후-충돌-해소-전략-vs-base-62-변환">2-3. 해시 후 충돌 해소 전략 vs base 62 변환</h4>
<table>
<thead>
<tr>
<th>하하하</th>
<th>해시 후 충돌 해소</th>
<th>base-62</th>
</tr>
</thead>
<tbody><tr>
<td>단축 URL의 길이</td>
<td>고정</td>
<td>가변적. ID 값이 커지면 같이 길어짐</td>
</tr>
<tr>
<td>유일성이 보장되는 ID 생성기</td>
<td>필요 없음</td>
<td>필요</td>
</tr>
<tr>
<td>충돌 여부</td>
<td>충돌 가능. 해소 필요</td>
<td>ID의 유일성이 보장된 적용가능하기 떄문에 충돌 자체가 불가능</td>
</tr>
<tr>
<td>단축 URL 예측 가능 여부</td>
<td>불가능</td>
<td>ID가 1씩 증가하는 값이라고 가정한다면 다음에 쓸 수 있는 URL 유추 가능</td>
</tr>
</tbody></table>
<h3 id="3-url-단축-및-리디렉션">3. URL 단축 및 리디렉션</h3>
<ol>
<li>사용자가 단축 URL 클릭</li>
<li>로드 밸런서가 해당 클릭으로 발생한 요청 웹 서버에 전달</li>
<li>단축 URL이 이미 캐시에 있는 경우에는 원래 URL을 바로 꺼내서 클라이언트에 전달<br>3-1. 캐시 미스의 경우 DB에서 꺼내서 전달. DB에도 없다면 사용자가 잘못된 단축 URL을 입력한 것<br>3-2. DB에서 꺼낸 URL을 캐시에 넣은 사용자에게 반환</li>
</ol>
<img src="https://velog.velcdn.com/images/yellow-w/post/8dbd5dec-0b84-465d-aba7-7e526007bbf9/image.png" alt="설명" class="left-block-image" style="width:100%;"/>


<hr>
<h2 id="4단계-추가-고려-사항">4단계. 추가 고려 사항</h2>
<h3 id="1-처리율-제한-장치rate-limiter">1. 처리율 제한 장치(rate limiter)</h3>
<h3 id="2-웹-서버-규모-확장">2. 웹 서버 규모 확장</h3>
<h3 id="3-db-규모-확장">3. DB 규모 확장</h3>
<h3 id="4-데이터-분석-솔루션analytics">4. 데이터 분석 솔루션(analytics)</h3>
<h3 id="5-가용성-데이터-일관성-안정성">5. 가용성, 데이터 일관성, 안정성</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[사례] 트위터(현 X) - Snowflake]]></title>
            <link>https://velog.io/@yellow-w/%EC%82%AC%EB%A1%80-Snowflake-%ED%8A%B8%EC%9C%84%ED%84%B0</link>
            <guid>https://velog.io/@yellow-w/%EC%82%AC%EB%A1%80-Snowflake-%ED%8A%B8%EC%9C%84%ED%84%B0</guid>
            <pubDate>Sun, 12 Oct 2025 09:23:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&quot;대규모 시스템 설계&quot; 책 스터디 중 
트위터의 Snowflake 관련 내용을 보완하기 위해 서칭하다가 찾게 된 내용입니다
트위터 기술 블로그 내용 중 &quot;대규모 시스템 설계&quot;와 관련있고, 제가 관심있는 부분을 정리하였습니다 (참고: 2010년 6월 1일 작성)</p>
<p>제 포스팅을 읽는 다른 분들에게도 도움이 되는 내용이기를 바랍니다.</p>
</blockquote>
<hr>
<h1 id="1-트위터의-snowflake-방식">1. 트위터의 Snowflake 방식</h1>
<h2 id="1-1-트위터의-데이터-저장소">1-1. 트위터의 데이터 저장소</h2>
<ul>
<li>MySQL 기반</li>
<li>단일 소규모 DB 인스턴스 → 이후 단일 대규모 DB 인스턴스로 확장 → 대규모 DB 클러스터로 분산</li>
<li>시스템 다수를 Cassandra나 MySQL 샤딩으로 교체</li>
<li>참고: Cassandra는 내장 전역 유일키 생성기가 없어 별도 솔루션이 필요</li>
</ul>
<h2 id="1-2-분산-유일키-생성에-대한-트위터의-요구사항">1-2. 분산 유일키 생성에 대한 트위터의 요구사항</h2>
<ul>
<li>고가용성, 초당 수만 건 생성, 로컬 O(1)에 가까운 지연 → 비조정(uncoordinated) 방식 지향</li>
<li>대체로 시간 정렬(K-sorted) 지원</li>
<li>64 bit<ul>
<li>트위터는 이미 트윗 ID 저장용 비트 수를 늘리는 고통스러운 과정을 겪은 바 있음</li>
<li>비트 확장 이슈 재발 방지(광범위한 코드 영향 최소화)</li>
</ul>
</li>
</ul>
<hr>
<h1 id="2-고려했던-선택지">2. 고려했던 선택지</h1>
<table>
<thead>
<tr>
<th>방식</th>
<th>유일성</th>
<th>정렬성</th>
<th>지연/성능</th>
<th>가용성/운영</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>MySQL 티켓서버(플리커)</td>
<td>좋음</td>
<td>순서 보장 가능</td>
<td>DB I/O 병목</td>
<td>DB 의존</td>
<td>재동기화/장애 시 복잡</td>
</tr>
<tr>
<td>UUID(v4 등)</td>
<td>확률적 유일</td>
<td>없음</td>
<td>로컬 생성 O(1)</td>
<td>좋음</td>
<td>128비트(인덱스 비우호)</td>
</tr>
<tr>
<td>ZK 순차 노드</td>
<td>전역 순서</td>
<td>강함</td>
<td>조정 비용↑</td>
<td>ZK 의존↑</td>
<td>지연/가용성 트레이드오프</td>
</tr>
<tr>
<td><strong>Snowflake</strong></td>
<td>구성적 유일</td>
<td><strong>K-sorted</strong></td>
<td><strong>로컬 O(1)</strong></td>
<td>ZK는 <strong>워커ID 임차만</strong></td>
<td>선택</td>
</tr>
</tbody></table>
<ul>
<li>MySQL 기반 티켓 서버(플리커 방식)<ul>
<li>별도의 재동기화 루틴을 구축하지 않고서는 필요한 순서 보장을 제공하지 못함</li>
</ul>
</li>
<li>다양한 UUID 방식<ul>
<li>발견한 모든 방식은 128비트가 필요했음</li>
</ul>
</li>
<li>Zookeeper 순차 노드<ul>
<li>트위터는 로컬 계산을 통한 낮은 지연·높은 가용성을 원했기 때문에, 전역 순서를 위해 조정이 필요한 ZK 순차 노드는 배제</li>
</ul>
</li>
</ul>
<hr>
<h1 id="3-해결책snowflake">3. 해결책(Snowflake)</h1>
<ul>
<li>ID = <code>⟨시간⟩ ⊕ ⟨데이터센터⟩ ⊕ ⟨워커⟩ ⊕ ⟨시퀀스⟩</code>의 조합 채택</li>
<li>시퀀스 번호: 워커(프로세스) 단위로 할당, 동일 ms 내 0..4095 증가(스레드 간 동기화로 공유)</li>
<li>워커 번호: 시작 시 Zookeeper를 통해 선택됨(구성 파일을 통해 재정의 가능)</li>
</ul>
<hr>
<h1 id="4-twitter-snowflake의-구현-포인트">4. Twitter Snowflake의 구현 포인트</h1>
<h2 id="4-1-비트-구성">4-1. 비트 구성</h2>
<ul>
<li>각 필드는 충돌 공간을 분할하는 네임스페이스</li>
<li>시간 41bits, DC 5bits, 워커 5bits, 시퀀스 12bits → 설계적으로 충돌 배제<pre><code class="language-text">  시간(41b) | DC(5b) | 워커(5b) | 시퀀스(12b)
ID = ⟨시간⟩ ⊕ ⟨데이터센터⟩ ⊕ ⟨워커⟩ ⊕  ⟨시퀀스⟩</code></pre>
<h3 id="1-시간">1) 시간</h3>
</li>
<li>timestampLeftShift = 12+5+5 = 22 → 나머지 상위 비트는 시간</li>
<li>twepoch = 1288834974657L 기준(2010-11-04 01:42:54.657 UTC)</li>
<li>수명: 41비트(ms) ≈ 약 69.7년 → 위 twepoch 기준 ~2080년대 초까지 사용 가능</li>
</ul>
<h3 id="2-데이터-센터">2) 데이터 센터</h3>
<ul>
<li>datacenterIdBits = 5 → 데이터센터 ID 0~31</li>
</ul>
<h3 id="3-워커">3) 워커</h3>
<ul>
<li>workerIdBits = 5 → 워커 ID 0~31</li>
</ul>
<h3 id="4-시퀀스">4) 시퀀스</h3>
<ul>
<li>sequenceBits = 12 → 같은 ms 내에서 0~4095 순번</li>
</ul>
<h2 id="4-2-코드-스니펫">4-2. 코드 스니펫</h2>
<pre><code class="language-scala">
private[this] val sequenceBits = 12L
private[this] val workerIdBits = 5L
private[this] val datacenterIdBits = 5L
val twepoch = 1288834974657L 

// ...중략

((timestamp - twepoch) &lt;&lt; timestampLeftShift) |
  (datacenterId &lt;&lt; datacenterIdShift) |
  (workerId &lt;&lt; workerIdShift) |
  sequence

//...</code></pre>
<hr>
<h1 id="5-운영-시-주의">5. 운영 시 주의</h1>
<ul>
<li><p>시계 역행(Clock Skew): 감지 시 예외로 생성 차단(중복 방지 우선). 엄격한 NTP 운영 필수</p>
</li>
<li><p>버스트 처리: 동일 ms에 4096개 초과 시 다음 ms까지 대기 → tail latency 증가 가능
  → 워커 확장, 요청 평탄화(배치·리밋) 고려</p>
</li>
<li><p>전역 단조증가 아님: 다중 워커·DC 간엔 전역 순서 보장 X → 정렬/인덱스에 시간 보조키 병행 권장</p>
</li>
<li><p>ZK 의존 영역 최소화: 워커ID 임차만 조정. 세션 만료/네트워크 분할 시 중복 워커ID 방지(펜싱 토큰 등) 설계</p>
</li>
</ul>
<hr>
<h1 id="6-용어-정리">6. 용어 정리</h1>
<h2 id="6-1-k-sorted대체로-시간-정렬">6-1. K-sorted(대체로 시간 정렬)</h2>
<ul>
<li>상위 비트가 시간이라 대체로 시간순 정렬되지만, 서로 다른 워커/동일 ms에서는 전역 단조증가가 절대적으론 보장되지 않음</li>
</ul>
<h2 id="6-2조정방식과-비조정-방식">6-2.조정방식과 비조정 방식</h2>
<h3 id="1-비조정uncoordinated-방식">1) 비조정(uncoordinated) 방식</h3>
<ul>
<li>ID 생성 시 별도의 합의/락 없이 각 노드가 독립적으로 생성</li>
<li>가용성·지연에 유리 (Snowflake의 핵심 철학)</li>
</ul>
<h3 id="2-조정coordinated-방식">2) 조정(coordinated) 방식</h3>
<ul>
<li>중앙 조정(예: DB 시퀀스, ZK 순차 노드)으로 순서 보장</li>
<li>전역 순서엔 유리하나 지연·가용성 손해</li>
</ul>
<h2 id="6-3모노토닉-시계monotonic-clock">6-3.모노토닉 시계(monotonic clock)</h2>
<ul>
<li>시스템 시간이 뒤로 가지 않는다는 가정</li>
<li>위배되면 충돌 위험 존재 → Snowflake는 예외로 중단</li>
</ul>
<h2 id="6-4시계-스큐clock-skew">6-4.시계 스큐(clock skew)</h2>
<ul>
<li>노드 간 시간 차이</li>
<li>NTP 튜닝·알람이 필수</li>
</ul>
<h2 id="6-5o1-생성">6-5.O(1) 생성</h2>
<ul>
<li>네트워크 왕복/합의 없이 로컬 비트 연산만으로 즉시 생성 → 매우 낮은 지연</li>
</ul>
<hr>
<h1 id="7-레퍼런스">7. 레퍼런스</h1>
<ul>
<li><a href="https://blog.x.com/engineering/en_us/a/2010/announcing-snowflake">https://blog.x.com/engineering/en_us/a/2010/announcing-snowflake</a></li>
<li><a href="https://twitter.github.io/twitter-server/">https://twitter.github.io/twitter-server/</a></li>
<li><a href="https://github.com/twitter-archive/snowflake/releases/tag/snowflake-2010">https://github.com/twitter-archive/snowflake/releases/tag/snowflake-2010</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[사례] 플리커 - 티켓서버]]></title>
            <link>https://velog.io/@yellow-w/%EC%82%AC%EB%A1%80-%ED%8B%B0%EC%BC%93%EC%84%9C%EB%B2%84-%ED%94%8C%EB%A6%AC%EC%BB%A4</link>
            <guid>https://velog.io/@yellow-w/%EC%82%AC%EB%A1%80-%ED%8B%B0%EC%BC%93%EC%84%9C%EB%B2%84-%ED%94%8C%EB%A6%AC%EC%BB%A4</guid>
            <pubDate>Sun, 12 Oct 2025 09:22:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>&quot;대규모 시스템 설계&quot; 책 스터디 중 
티켓서버 관련 내용을 보완하기 위해 서칭하다가 찾게 된 내용입니다
플리커의 기술 블로그 내용 중 &quot;대규모 시스템 설계&quot;와 관련있고, 제가 관심있는 부분을 정리하였습니다(참고: 2010년 2월 8일 작성)</p>
<p>제 포스팅을 읽는 다른 분들에게도 도움이 되는 내용이기를 바랍니다.</p>
</blockquote>
<hr>
<h1 id="1-플리커의-티켓서버">1. 플리커의 티켓서버</h1>
<h2 id="1-1-플리커의-데이터-저장-방식">1-1. 플리커의 데이터 저장 방식</h2>
<ul>
<li>플리커는 샤딩 방식으로 데이터 저장소를 확장하고 있음<ul>
<li>하나의 거대한 DB 대신 여러 DB에 데이터를 분산저장함으로써 부하 분산</li>
</ul>
</li>
<li>DB 간 데이터 마이그레이션이 필요한 경우 존재</li>
</ul>
<h2 id="1-2-mysql-샤드">1-2. MySQL 샤드</h2>
<ul>
<li>마스터-마스터 복제 쌍으로 구성되어 있음</li>
<li>샤드 간 이동과 마스터-마스터 복제 모두에서 충돌이 없도록 전역적으로 고유한 키가 보장되어야 함</li>
</ul>
<h2 id="1-3-guid를-사용할-수도-있지-않을까">1-3. GUID를 사용할 수도 있지 않을까?</h2>
<ul>
<li>전역적으로 유일한 키가 필요하다면 GUID를 고려해도 될 것 같은데 쓰지 않는 이유?</li>
<li>GUID는 크기가 크고, MySQL에서 인덱스 성능이 좋지 않음</li>
</ul>
<h3 id="1-플리커의-mysql-성능-유지-방법">1) 플리커의 MySQL 성능 유지 방법</h3>
<ul>
<li>쿼리 대상이 되는 모든 항목에 인덱스 생성</li>
<li>인덱스만으로 쿼리 수행</li>
<li>GUID는 인덱스 팽창 문제 때문에 이러한 전략과 상충함</li>
</ul>
<h3 id="2-티켓-서버의-유용성">2) 티켓 서버의 유용성</h3>
<ul>
<li>티켓 서버는 순차성(sequentiality) 제공</li>
<li>리포팅과 디버깅을 좀 더 직관적으로 만들어주고</li>
<li>캐싱 트릭(ID 순서를 이용한 캐시 전략)이 가능함</li>
</ul>
<h2 id="1-4-안전-해시를-사용할-수도-있지-않을까">1-4. 안전 해시를 사용할 수도 있지 않을까?</h2>
<ul>
<li>아마존의 Dynamo 같은 시스템은 안전 해시를 통해 GUID/샤딩 문제 처리함<ul>
<li>이는 쓰기 비용이 낮은(Write-cheap) 환경(예:LSM-tree)에 적합함 </li>
</ul>
</li>
<li>MySQL은 빠른 랜덤 읽기(fast random reads)에 최적화 되어 있음</li>
<li>Flickr는 MySQL의 빠른 랜덤 읽기 특성을 살리기 위해 순차 ID를 유지하는 선택을 한 것</li>
</ul>
<hr>
<h1 id="2-중앙-집중-auto-increments">2. 중앙 집중 <code>Auto-Increments</code></h1>
<h2 id="2-1-한계">2-1. 한계</h2>
<ul>
<li>단일 DB에서 ID만 발급받는 방식은 단순함</li>
<li>하지만 초당 60장+ 업로드와 댓글/즐겨찾기/그룹/태그 등 모든 엔터티가 ID를 필요로 해 중앙 테이블이 급격히 비대해짐</li>
<li>그래서 Flickr는 티켓 서버라는 가벼운 시퀀스 전용 테이블을 운용</li>
</ul>
<h2 id="2-2-플리커-방식의-중앙-집중-auto-increments">2-2. 플리커 방식의 중앙 집중 <code>Auto-Increments</code></h2>
<ul>
<li>단일 DB에 새 행 삽입 시, 해당 테이블의 자동 증가 ID를 모든 DB의 기본키로 사용</li>
<li>MySQL의 <code>REPLACE INTO</code>를 통해 기존 행 삭제 후 새 행 INSERT<ul>
<li>DB의 단일 행을 원자적으로 업데이트하고, 자동 증가된 새 기본키를 획득</li>
<li>참고: 일반적으로는 <code>INSERT … ON DUPLICATE KEY UPDATE</code>가 더 권장됨</li>
</ul>
</li>
<li>플리커의 티켓 서버는 전용 DB 서버로 단일 DB를 보유하고 있음</li>
<li>해당 DB에는 32비트 ID용 Tickets32 테이블과 64비트 ID용 Tickets64 테이블 등이 존재</li>
</ul>
<h3 id="1-테이블-구조">1) 테이블 구조</h3>
<pre><code class="language-sql">CREATE TABLE `Tickets64` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `stub` char(1) NOT NULL default &#39;&#39;,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `stub` (`stub`)
) ENGINE=InnoDB;</code></pre>
<h3 id="2-조회">2) 조회</h3>
<ul>
<li>Tickets64 테이블에서 조회 수행 시 다음과 같이 단일 행이 반환됨</li>
</ul>
<pre><code class="language-sql">+-------------------+------+
| id                | stub |
+-------------------+------+
| 72157623227190423 |    a |
+-------------------+------+</code></pre>
<h3 id="3-전역-유니크-키-발급">3) 전역 유니크 키 발급</h3>
<ul>
<li>stub는 유니크 제약이 걸린 더미 컬럼으로, 테이블에는 보통 &#39;a&#39; 한 값만 유지<pre><code class="language-sql">REPLACE INTO Tickets64 (stub) VALUES (&#39;a&#39;);  -- PK/UNIQUE 충돌 시 기존 행 삭제 후 새 행 INSERT
SELECT LAST_INSERT_ID(); -- 반드시 같은 커넥션에서 호출
</code></pre>
</li>
</ul>
<pre><code>---
# 3. SPOFs
- 티켓서버 방식에는 티켓서버 그 자체가 단일 장애지점이 될 수 있다는 문제가 있음
- 이 문제를 해결하기 위해 플리커에서는 ID 공간을 짝수/홀수로 나눈 두 개의 티켓 서버 운영
  - 라운드 로빈 방식을 통해 두 서버 간 로드밸런싱과 다운타임 처리
  - 참고: 양쪽 카운트가 조금 달라도 기능적으로 무방함 (실제 플리커도 홀수가 더 많다고 함)

``` text
TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1

TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2</code></pre><h1 id="4-여러-시퀀스-분리-운용">4. 여러 시퀀스 분리 운용</h1>
<h2 id="4-1-개별-시퀀스-테이블-운용">4-1. 개별 시퀀스 테이블 운용</h2>
<ul>
<li>OfflineTasks: 발급량이 많아 다른 개체들의 카운트를 불필요하게 끌어올리지 않으려 분리</li>
<li>Groups/Accounts: 발급량이 적어 따로 분리</li>
<li>Photos: 과거 auto-increment와 동기화해 총 업로드 수의 직관적 파악이 가능하도록 설계</li>
</ul>
<hr>
<h1 id="5-작동하는-가장-단순한-것">5. 작동하는 가장 단순한 것</h1>
<ul>
<li>플리커의 티켓서버 방식은 2006년 1월 13일(금)부터 운영</li>
<li>특별히 우아한 방식은 아니지만, <strong>작동하는 가장 단순한 것</strong> 설계 원칙을 보여주는 훌륭한 사례</li>
</ul>
<hr>
<h1 id="6-레퍼런스">6. 레퍼런스</h1>
<ul>
<li><a href="https://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/">https://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 7장. 분산 시스템을 위한 유일 ID 생성기 설계]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-7%EC%9E%A5.-%EB%B6%84%EC%82%B0-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%9C%A0%EC%9D%BC-ID-%EC%83%9D%EC%84%B1%EA%B8%B0-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-7%EC%9E%A5.-%EB%B6%84%EC%82%B0-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%9C%A0%EC%9D%BC-ID-%EC%83%9D%EC%84%B1%EA%B8%B0-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Sun, 12 Oct 2025 09:21:00 GMT</pubDate>
            <description><![CDATA[<h1 id="7장-분산-시스템을-위한-유일-id-생성기-설계">7장. 분산 시스템을 위한 유일 ID 생성기 설계</h1>
<h2 id="1단계-문제-이해-및-설계-범위-확정">1단계. 문제 이해 및 설계 범위 확정</h2>
<table>
<thead>
<tr>
<th>-</th>
<th>요구사항</th>
</tr>
</thead>
<tbody><tr>
<td>ID의 특성</td>
<td>유일해야 하고 정렬 가능해야 함</td>
</tr>
<tr>
<td>ID의 타입</td>
<td>숫자<br />64비트로 표현될 수 있는 값<br />생성 시점에 따른 정렬 가능</td>
</tr>
<tr>
<td>시스템 규모</td>
<td>초당 10,000 ID 생성 가능해야 함</td>
</tr>
</tbody></table>
<hr>
<h2 id="2단계-개략적-설계안-제시및-동의-구하기">2단계. 개략적 설계안 제시및 동의 구하기</h2>
<ul>
<li>분산 시스템에서 유일성이 보장되는 ID 생성 기법은 다양함</li>
<li>선택지<ul>
<li>다중 마스터복제(multi-master replication)</li>
<li>UUID(Universally Unique Identifier)</li>
<li>티켓 서버(ticket server)</li>
<li>트위터 스노플레이크(twitter snowflake) 접근법</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2-1-다중-마스터-복제">2-1. 다중 마스터 복제</h3>
<ul>
<li>DB의 <code>auto_increment</code> 기능을 활용하는 방법</li>
<li>단, 1씩 증가가 아닌 K(분산 DB 수)씩 증가</li>
</ul>
<hr>
<h3 id="2-2-uuidv4">2-2. UUID(v4)</h3>
<h4 id="특징">특징</h4>
<ul>
<li>컴퓨터 시스템에 저장되는 정보를 유일하게 식별하기 위한 128비트 수</li>
</ul>
<table>
<thead>
<tr>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>단순의존성·조율 없음(완전 분산)<br />서버 사이의 조율이 필요 없으므로 동기화 이슈 없음<br />성숙한 생태계: 거의 모든 언어/DB/프레임워크가 지원<br />각 서버가 스스로 UUID 생성-&gt; 규모 확장 용이</td>
<td>I16바이트(문자열 표기 36자)라 <strong>공간·전송량</strong> 부담이 정수형보다 큼<br />비정렬(random) 키<br />시간순 정렬이 필요하면 애플리케이션 레벨로 정렬 키를 따로 둬야 함<br />ID에 숫자가 아닌 값 포함 가능</td>
</tr>
</tbody></table>
<hr>
<h3 id="2-3-티켓-서버">2-3. 티켓 서버</h3>
<ul>
<li>플리커는 분산 기본키를 만들기 위해 티켓 서버 기술 활용</li>
<li><code>auto_increment</code> 기능을 갖춘 DB 서버(티켓 서버)를 중앙 집중형으로 하나만 사용하는 방식</li>
</ul>
<table>
<thead>
<tr>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td>구현 용이<br />중소 규모 앱에 적합</td>
<td>티켓 서버가 SPOF가 됨</td>
</tr>
</tbody></table>
<hr>
<h3 id="2-4-트위터-스노우플레이크-접근법">2-4. 트위터 스노우플레이크 접근법</h3>
<ul>
<li><strong>64비트 정수</strong>를 여러 절(<code>타임스탬프 | 노드ID | 시퀀스</code>)로 비트 분할해 <strong>시간 정렬</strong>과 <strong>완전 분산</strong>을 동시에 달성하는 방식</li>
</ul>
<h4 id="생성">생성</h4>
<ul>
<li><code>sign bit</code>: 1비트 할당</li>
<li><code>timestamp</code>: 기준 epoch 이후 경과 ms(또는 ns)</li>
<li>노드 ID(머신/프로세스)<ul>
<li>데이터 센터 ID: 5비트 할당. 2^5 = 32개 데이터 센터 지원 가능</li>
<li>서버 ID: 5비트 할당. 데이터 센터당 32개 서버 사용 가능</li>
</ul>
</li>
<li>일련번호<ul>
<li>각 서버에서는 ID 생성 시 일련 번호 1씩 증가</li>
<li>1ms 경과할 때마다 초기화됨(즉, 같은 ms 내 증가하는 시퀀스 비트)</li>
</ul>
</li>
</ul>
<pre><code class="language-css">[ 1bit 예약 | 41bit 타임스탬프 | 10bit 노드ID | 12bit 시퀀스 ]</code></pre>
<hr>
<h2 id="3단계-상세-설게">3단계. 상세 설게</h2>
<h3 id="노드-id">노드 ID</h3>
<ul>
<li>데이터 센터 ID와 서버 ID는 시스템이 시작할 때 결정되며, 일반적으로 운영중에는 변경되지 않음</li>
<li>변경 시 ID 충돌이 발생할 수 있음</li>
</ul>
<hr>
<h2 id="4단계-추가-논의-사항">4단계. 추가 논의 사항</h2>
<h3 id="시계-동기화clock-synchronization">시계 동기화(clock synchronization)</h3>
<ul>
<li>하나의 서버에서 여러 코어가 실행될 경우 ID 생성 서버들이 전부 같은 시계를 사용하지 않을 수도 있음</li>
<li>해결 방법 중 하나: NTP(<code>Network Time Protocol</code>)</li>
</ul>
<h3 id="각-절의-길이-최적화">각 절의 길이 최적화</h3>
<ul>
<li>동시성이 낮고 수명이 긴 앱이라면 일련번호의 길이를 줄이고 타임스탬프 절의 길이를 늘이는 것을 고려 가능</li>
</ul>
<h3 id="고가용성">고가용성</h3>
<hr>
<h2 id="5-참고-내용">5. 참고 내용</h2>
<h3 id="5-1-uuid-v4-난수형">5-1. UUID v4 (난수형)</h3>
<h4 id="정의"><strong>정의</strong></h4>
<ul>
<li>128비트 UUID</li>
<li>상수 비트(버전/variant)를 제외한 <strong>122비트를 암호학적으로 충분한 난수</strong>로 채우는 식별자</li>
</ul>
<h4 id="보장성질">보장/성질</h4>
<ul>
<li><strong>완전 분산</strong>: 중앙 없이 생성</li>
<li><strong>정렬성 없음</strong>: 삽입 위치가 랜덤 → 인덱스 단편화 유발 가능</li>
<li><strong>충돌 확률</strong>: 현실적으로 극히 낮음(122비트 공간)</li>
<li><strong>개인정보 노출 없음</strong>: 시간/노드 정보 미포함</li>
</ul>
<h4 id="권장-유스케이스">권장 유스케이스</h4>
<ul>
<li>“그냥 충돌 없이 잘 돌아가는 전역 고유키”가 필요하고 <strong>DB 인덱스 비용을 감수</strong>할 수 있을 때</li>
<li>마이크로서비스/이벤트ID/추적ID 등 <strong>중앙 조율이 싫을 때</strong></li>
</ul>
<hr>
<h3 id="5-2-uuid-버전">5-2. UUID 버전</h3>
<ul>
<li>v7 과 v4 비교<ul>
<li><strong>v7 장점</strong>: 타임스탬프를 상위비트에 두어 <strong>삽입 정렬성</strong>↑, 범위쿼리·시계열 조회에 유리. 인덱스 단편화↓, 캐시 친화적</li>
<li><strong>v4 장점</strong>: 구현·호환성 측면에서 가장 널리 쓰이고 라이브러리 풍부.</li>
</ul>
</li>
<li>DB 성능/정렬성이 중요하면 <strong>v7</strong>, 단순 분산 유일 키면 v4도 충분</li>
</ul>
<table>
<thead>
<tr>
<th>버전</th>
<th>핵심 아이디어</th>
<th>정렬성</th>
<th>충돌 위험</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td><strong>v1</strong></td>
<td>시간 + 노드(MAC)</td>
<td>어느 정도(시간 기반)</td>
<td>매우 낮음</td>
<td>MAC 유출·시계 이슈 우려</td>
</tr>
<tr>
<td><strong>v3</strong></td>
<td>네임스페이스 + 이름의 MD5</td>
<td>없음</td>
<td>동일 입력→동일 출력</td>
<td>보통 v5 선호</td>
</tr>
<tr>
<td><strong>v4</strong></td>
<td><strong>난수 122비트</strong></td>
<td>없음</td>
<td><strong>극저</strong></td>
<td>가장 널리 사용</td>
</tr>
<tr>
<td><strong>v5</strong></td>
<td>네임스페이스 + 이름의 SHA-1</td>
<td>없음</td>
<td>동일 입력→동일 출력</td>
<td>디터미니스틱 ID 필요 시</td>
</tr>
<tr>
<td><strong>v6</strong></td>
<td>v1 재배열(시간 정렬성 개선)</td>
<td>높음</td>
<td>매우 낮음</td>
<td>과도기적, 채택 제한적</td>
</tr>
<tr>
<td><strong>v7</strong></td>
<td><strong>시간(Unix ms)+난수</strong></td>
<td><strong>높음(시간 정렬)</strong></td>
<td>극저</td>
<td>최신 표준 경향, 실무 선호↑</td>
</tr>
<tr>
<td>v8</td>
<td>커스텀 필드</td>
<td>설계에 따름</td>
<td>설계에 따름</td>
<td>실험/도메인 특화용</td>
</tr>
</tbody></table>
<h2 id="6-용어">6. 용어</h2>
<ul>
<li><strong>정렬성(orderability)</strong>: 시간이 흐를수록 값이 대체로 증가</li>
<li><strong>충돌 확률</strong>: 서로 다른 두 생성 결과가 동일 키가 되는 확률(이상적으로 0에 가깝다)</li>
<li><strong>완전 분산성</strong>: 중앙 조율 없이 각 노드가 독립적으로 생성 가능</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 6장. 키-값 저장소]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-6%EC%9E%A5.-%ED%82%A4-%EA%B0%92-%EC%A0%80%EC%9E%A5%EC%86%8C</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-6%EC%9E%A5.-%ED%82%A4-%EA%B0%92-%EC%A0%80%EC%9E%A5%EC%86%8C</guid>
            <pubDate>Mon, 29 Sep 2025 14:30:49 GMT</pubDate>
            <description><![CDATA[<h1 id="6장-키-값-저장소key-value-store-설계">6장. 키-값 저장소(<code>key-value store</code>) 설계</h1>
<hr>
<h2 id="분산-키---값-저장소가-가져야-하는-기능과-기능-구현에-이용되는-기술들">분산 키 - 값 저장소가 가져야 하는 기능과 기능 구현에 이용되는 기술들</h2>
<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>다양성(<code>heterogeneity</code>)</td>
<td>안정 해시</td>
</tr>
<tr>
<td>조절 가능한 데이터 일관성</td>
<td>정족수 합의(<code>quorum consensus</code>)</td>
</tr>
<tr>
<td>일시적 장애 처리</td>
<td>느슨한 정족수 프로토콜(<code>sloppy quorum</code>)과<br />단서 후 임시 위탁(<code>hinted handoff</code>)</td>
</tr>
<tr>
<td>영구적 장애 처리</td>
<td>머클 트리(<code>Merkle tree</code>)</td>
</tr>
<tr>
<td>데이터 센터 장애 대응</td>
<td>여러 데이터 센터에 걸친 데이터 다중화</td>
</tr>
</tbody></table>
<hr>
<h2 id="1-키-값-저장소란">1. 키 값 저장소란?</h2>
<h3 id="1-1-이론적-정의">1-1. 이론적 정의</h3>
<ul>
<li>데이터를 <strong>키(Key)와 값(Value)</strong> 쌍으로 관리하는 가장 단순한 형태의 DB 모델</li>
<li>내부적으로  <strong>해시 테이블 유사 구조</strong> 사용</li>
<li>스키마리스(Schema-less)</li>
<li>값에는 문자열·JSON·Blob 등 다양한 데이터 저장 가능</li>
</ul>
<h3 id="1-2-기본-연산">1-2. 기본 연산</h3>
<ul>
<li><code>PUT(key, value)</code>, <code>GET(key)</code>, <code>DELETE(key)</code> 등 최소한의 조작 인터페이스</li>
</ul>
<h3 id="1-3-성능-특성">1-3. 성능 특성</h3>
<ul>
<li><strong>O(1)에 가까운 조회/쓰기 성능</strong> 제공 (메모리 기반 접근, 해시 인덱스 활용)<ul>
<li>기본적으로 해시 테이블이나 LSM-Tree 구조를 활용하여 매우 빠른 접근 성능을 보장</li>
</ul>
</li>
<li>분산 환경에서 <strong>수평 확장(Scale-Out)</strong> 가능<ul>
<li>Dynamo는 &quot;highly available key-value store&quot;로 설계되었으며, <strong>Consistent Hashing</strong>을 기반으로 새로운 노드를 추가하거나 제거할 때 전체 데이터를 재분배하지 않고도 <strong>수평 확장</strong> 가능하도록 설계됨</li>
</ul>
</li>
</ul>
<h3 id="1-4-예시">1-4. 예시</h3>
<ul>
<li><strong>Redis</strong>: In-memory key-value DB, 다양한 데이터 타입 지원</li>
<li><strong>Memcached</strong>: 캐싱 중심 단순 Key-Value Store</li>
<li><strong>DynamoDB</strong>: AWS 관리형 Key-Value + Document DB (밀리초 단위 성능, 자동 샤딩)</li>
<li><strong>Cosmos DB</strong>: Azure의 글로벌 분산 Key-Value DB API 지원</li>
</ul>
<hr>
<h2 id="2-단일-서버-키-값-저장소-설계">2. 단일 서버 키-값 저장소 설계</h2>
<ul>
<li><p>단일 서버 수준에서는 키-값 저장소를 <strong>해시 테이블 기반의 빠른 조회·쓰기</strong>를 중심으로 설계하면 충분</p>
<h3 id="2-1-단일-서버-수준에서-다루는-사항들">2-1. 단일 서버 수준에서 다루는 사항들</h3>
</li>
<li><p><strong>데이터 모델</strong></p>
<ul>
<li>단순한 <code>Key → Value</code> 매핑</li>
<li>키는 유일성을 보장해야 하며, 값은 문자열·JSON·바이너리 등 어떤 형태든 저장 가능→ 스키마 유연성(Schema-less)</li>
</ul>
</li>
<li><p><strong>저장 구조</strong></p>
<ul>
<li>메모리 기반 해시 테이블(dictionary)을 사용해 평균 O(1) 조회 성능 제공</li>
<li>필요 시 디스크에 주기적 스냅샷(RDB)이나 Append-Only File(AOF)로 내구성 보장</li>
</ul>
</li>
<li><p><strong>만료/퇴출 정책</strong></p>
<ul>
<li>TTL(Time-To-Live)로 키의 생존 시간을 제한</li>
<li>메모리 부족 시 LRU/LFU/Random 같은 eviction 정책으로 데이터 제거</li>
</ul>
</li>
<li><p><strong>동시성/원자성</strong></p>
<ul>
<li>단일 스레드 이벤트 루프 또는 락 분할(shard) 기반 멀티스레딩</li>
</ul>
</li>
<li><p>단일 서버에서는 <strong>빠른 접근(O(1))·TTL/eviction·내구성 옵션</strong>까지 갖추면 실무적으로 충분</p>
</li>
</ul>
<hr>
<h2 id="3-분산-서버-키-값-저장소-설계">3. 분산 서버 키-값 저장소 설계</h2>
<h3 id="3-1-분산-시스템-설계-시-알아둬야-하는-개념들">3-1. 분산 시스템 설계 시 알아둬야 하는 개념들</h3>
<h4 id="1-cap-정리">1) CAP 정리</h4>
<ul>
<li><strong>Eric Brewer</strong> (2000) 제안, 이후 Gilbert &amp; Lynch(2002)에 의해 정리된 이론</li>
<li>분산 시스템에서는 <strong>Consistency(일관성)</strong>, <strong>Availability(가용성)</strong>, <strong>Partition Tolerance(분할 내성)</strong> 세 가지 속성을 동시에 모두 만족할 수 없다는 이론</li>
</ul>
<table>
<thead>
<tr>
<th>요구사항</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Consistency (일관성)</strong></td>
<td>모든 노드가 같은 시점에 동일한 데이터를 보여준다</td>
</tr>
<tr>
<td><strong>Availability (가용성)</strong></td>
<td>모든 요청에 대해 항상 응답을 돌려줄 수 있다 (성공/실패 여부 무관)</td>
</tr>
<tr>
<td><strong>Partition Tolerance (분할 내성)</strong></td>
<td>네트워크에 파티션이 생기더라도 시스템은 계속 동작하여야 한다는 뜻</td>
</tr>
</tbody></table>
<h5 id="1-1-핵심-포인트">1-1) 핵심 포인트</h5>
<ul>
<li>네트워크 분할 가능성을 완전히 배제할 수 없기 때문에 <strong>P(Partition Tolerance)</strong>는 분산 시스템에서는 사실상 필수</li>
<li>따라서 <strong>실제 선택은 AP vs CP</strong>의 문제</li>
<li>즉, 네트워크 파티션이 발생했을 때, 시스템은 <strong>C(일관성)</strong>과 <strong>A(가용성)</strong> 중 하나를 희생해야 함</li>
</ul>
<h5 id="1-2-cap-정리에-따른-키-값-저장소-분류">1-2) CAP 정리에 따른 키-값 저장소 분류</h5>
<table>
<thead>
<tr>
<th>구분</th>
<th><strong>AP 시스템</strong></th>
<th><strong>CP 시스템</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>보장</strong></td>
<td>가용성 + 분할 내성</td>
<td>일관성 + 분할 내성</td>
</tr>
<tr>
<td><strong>희생</strong></td>
<td>강한 일관성</td>
<td>가용성</td>
</tr>
<tr>
<td><strong>허용 일관성 수준</strong></td>
<td>최종 일관성(Eventual Consistency)</td>
<td>강한 일관성(Strong Consistency)</td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>고가용성, 글로벌 서비스에 유리</td>
<td>데이터 무결성 보장, 트랜잭션 안전</td>
</tr>
<tr>
<td><strong>단점</strong></td>
<td>일시적 불일치 허용</td>
<td>서비스 중단 가능</td>
</tr>
<tr>
<td><strong>대표 사례</strong></td>
<td>글로벌 소셜 네트워크, 메시징 서비스, 검색, 로그 수집</td>
<td>금융 거래, 주문/결제, 분산 락/메타데이터, 인증</td>
</tr>
<tr>
<td><strong>대표 시스템</strong></td>
<td>DynamoDB, Cassandra, Riak</td>
<td>ZooKeeper, HBase, Etcd</td>
</tr>
</tbody></table>
<hr>
<h3 id="3-2-시스템-컴포넌트feat-dynamo-cassandra-bigtable">3-2. 시스템 컴포넌트(feat. Dynamo, Cassandra, BigTable)</h3>
<h4 id="1-데이터-파티션">1) 데이터 파티션</h4>
<h5 id="1-1-데이터-파티셔닝에서-고려할-점">1-1) 데이터 파티셔닝에서 고려할 점</h5>
<ul>
<li>데이터의 고른 분산</li>
<li>노드 추가/삭제 시 데이터의 이동 최소화</li>
</ul>
<h5 id="1-2-안정-해시를-사용한-파티셔닝의-장점">1-2) 안정 해시를 사용한 파티셔닝의 장점</h5>
<ul>
<li>규모 확장 자동화(<code>automatic scaling</code>)</li>
<li>다양성(<code>heterogeneity</code>): 각 서버의 용량에 맞게 가상노드 수 조정 가능</li>
</ul>
<hr>
<h4 id="2-데이터-다중화">2) 데이터 다중화</h4>
<ul>
<li>높은 가용성과 안정성 확보를 위해 데이터를 N개 서버에 비동기적으로 다중화</li>
</ul>
<h4 id="3-데이터-일관성">3) 데이터 일관성</h4>
<h5 id="3-1-일관성과-관련된-주요-프로토콜">3-1) 일관성과 관련된 주요 프로토콜</h5>
<table>
<thead>
<tr>
<th>프로토콜</th>
<th>개념 설명</th>
<th>대표 사례</th>
</tr>
</thead>
<tbody><tr>
<td><strong>2PC (Two-Phase Commit)</strong></td>
<td>Coordinator가 모든 참여 노드에 Commit 가능 여부를 묻고, <br />모두 동의하면 Commit, 아니면 Abort</td>
<td>전통적 RDB, 금융/ERP 시스템</td>
</tr>
<tr>
<td><strong>Paxos</strong></td>
<td>Proposer-Acceptor 구조에서 다수 노드가 단일 값에 합의하도록 보장<br />합의 알고리즘의 고전적 표준</td>
<td>Google Chubby, Megastore</td>
</tr>
<tr>
<td><strong>Quorum-based</strong></td>
<td>읽기/쓰기 요청에서 다수파 응답을 요구하여 일관성 확보<br /> <code>R+W &gt; N</code> 규칙으로 CAP 트레이드오프 조정 가능</td>
<td>Cassandra, DynamoDB</td>
</tr>
<tr>
<td><strong>Raft</strong></td>
<td>Paxos를 단순화한 합의 알고리즘<br />Leader 기반 로그 복제를 통해 분산 노드 간 일관성 유지.</td>
<td>etcd, Consul, CockroachDB, Kubernetes</td>
</tr>
<tr>
<td><strong>CRDT (Conflict-free Replicated Data Types)</strong></td>
<td>수학적 성질을 가진 데이터 구조로 병합 시 항상 동일 결과<br />네트워크 분할이나 동시성 환경에서도 충돌 없는 최종 일관성 제공</td>
<td>Redis CRDT, Riak, Google Docs</td>
</tr>
</tbody></table>
<hr>
<ul>
<li><p>참고: <strong>Tunable Consistency</strong>란?</p>
<ul>
<li><p><strong>정의</strong>: 분산 데이터베이스에서 <strong>읽기(Read)</strong>와 <strong>쓰기(Write)</strong> 연산 시 필요한 복제본(Replica)의 수를 조정하여, <strong>일관성과 가용성 사이의 균형을 사용자가 선택할 수 있는 모델</strong></p>
</li>
<li><p><strong>핵심 공식</strong>:</p>
<pre><code>R + W &gt; N</code></pre><ul>
<li><code>N</code>: 전체 복제본 수</li>
<li><code>R</code>: 읽기에 필요한 복제본 수</li>
<li><code>W</code>: 쓰기에 필요한 복제본 수</li>
</ul>
</li>
<li><p><strong>특징</strong>:</p>
<ul>
<li><strong>유연성</strong>: 애플리케이션의 요구에 따라 “일관성 우선(CP)” 또는 “가용성 우선(AP)”으로 운영 가능.</li>
<li><strong>대표 사례</strong>: <strong>Cassandra</strong>, <strong>Amazon DynamoDB</strong> → 클라이언트가 <code>Consistency Level</code>을 지정 가능 (ONE, QUORUM, ALL 등).</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h5 id="3-2-일관성모델">3-2) 일관성모델</h5>
<table>
<thead>
<tr>
<th>모델</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>강한 일관성(<code>strong consistency</code>)</td>
<td>모든 읽기 연산은 가장 최근에 갱신된 결과를 반환</td>
</tr>
<tr>
<td>약한 일관성(<code>weak consistency</code>)</td>
<td></td>
</tr>
<tr>
<td>최종 일관성(<code>eventual consistency</code>)</td>
<td>약한 일관성의 한 형태<br />갱신 결과가 결국에는 모든 사본에 반영(동기화)되는 모델</td>
</tr>
</tbody></table>
<h5 id="3-3-비일관성-해소-기법">3-3) 비일관성 해소 기법</h5>
<h6 id="예방-기법"><strong>예방 기법</strong></h6>
<ul>
<li>비일관성 발생 자체를 막음 (합의, 쿼럼, 동기 복제)<ul>
<li>장점: 데이터 무결성 보장</li>
<li>단점: 지연(latency), 성능 비용</li>
</ul>
</li>
</ul>
<h6 id="사후-해결-기법"><strong>사후 해결 기법</strong></h6>
<ul>
<li>일관성 깨짐을 허용하고, 나중에 맞춤(Repair, Vector Clocks, CRDT, Anti-Entropy)<ul>
<li>장점: 확장성과 가용성에 유리</li>
<li>단점: 충돌 처리 복잡, 사용자 체감 일관성 저하</li>
</ul>
</li>
</ul>
<h6 id="벡터-클락-vector-clock">벡터 클락 (Vector Clock)</h6>
<ul>
<li><p>개념</p>
<ul>
<li>버저닝의 확장 기법</li>
<li>단순 타임스탬프(하나의 시간값) 대신, <strong>각 노드별 버전 정보</strong>를 벡터 형태로 관리</li>
<li>즉, 데이터 변경 이력이 <strong>다차원 벡터</strong>로 기록되어, 어떤 노드에서 어떤 시점에 변경이 발생했는지 알 수 있음</li>
</ul>
</li>
<li><p>동작 방식</p>
<ol>
<li>각 노드가 <strong>자신의 카운터</strong>를 유지 (예: Node A=3, Node B=5)</li>
<li>데이터 변경 시 해당 노드의 카운터를 증가</li>
<li>변경 데이터를 다른 노드에 전송할 때 벡터 클락도 함께 전송</li>
<li>수신한 노드는 자신이 가진 벡터와 비교하여 충돌 여부를 판정</li>
</ol>
</li>
<li><p>충돌 판정</p>
<ul>
<li><strong>완전히 포함</strong>: (A:2, B:1) vs (A:3, B:1) → 후자가 최신.</li>
<li><strong>교차</strong>: (A:3, B:1) vs (A:2, B:2) → 어느 쪽이 최신인지 판단 불가 → 충돌 발생.</li>
</ul>
</li>
<li><p>단점</p>
<ul>
<li>벡터 길이가 노드 수에 비례해 커짐 (확장성 문제)</li>
<li>충돌을 <strong>탐지</strong>는 하지만, 최종 <strong>해결(Resolution)</strong> 은 애플리케이션 로직이 필요</li>
</ul>
</li>
</ul>
<hr>
<h4 id="4-장애-처리">4) 장애 처리</h4>
<h5 id="4-1-장애-감지failure-detection-기법">4-1) 장애 감지(<code>failure detection</code>) 기법</h5>
<ul>
<li>가십 프로토콜 - 분산형 장애 감지 솔루션<ul>
<li>각 노드는 <strong>멤버십 목록</strong> 유지 (멤버 ID + 박동 카운터(<code>heartbeat counter</code>) 쌍)</li>
<li>각 노드는 주기적으로 자신의 박동 카운터를 증가 시킴</li>
<li>무작위로 선정된 다른 노드들에게 자신의 멤버십 목록(박동 카운터 포함)을 전송</li>
<li>수신한 노드는 더 큰 값의 박동 카운터가 있으면 자신의 멤버십 목록을 갱신</li>
<li>특정 멤버의 박동 카운터가 일정 시간 동안 갱신되지 않으면 <strong>장애(<code>offline</code>)</strong> 로 판정</li>
</ul>
</li>
</ul>
<h5 id="4-2-장애-해소failure-resolution">4-2) 장애 해소(<code>failure resolution</code>)</h5>
<h6 id="일시적-장애-처리">일시적 장애 처리</h6>
<ul>
<li>단서 후 임시 위탁 기법(<code>hinted handoff</code>)<ul>
<li>장애 노드가 쓰기를 받을 수 없을 때, 다른 노드가 대신 데이터를 저장</li>
<li>장애 노드가 복구하면, 해당 데이터를 위탁 노드에서 넘겨받아 동기화</li>
<li>Dynamo, Cassandra에서 활용 → <strong>일시적 네트워크 불안정</strong>에 효과적</li>
</ul>
</li>
</ul>
<h6 id="영구-장애-처리">영구 장애 처리</h6>
<ul>
<li>반-엔트로피(<code>anti-entropy</code>) 프로토콜<ul>
<li>장애 노드가 장시간 복구되지 않으면 다른 노드 간 <strong>사본 비교 및 동기화</strong> 필요</li>
<li>단순 전체 복사 대신, <strong>Merkle Tree</strong> 같은 자료구조로 차이를 효율적으로 비교</li>
<li>Gossip 기반 동기화와 함께 사용 가능 → Eventually Consistency 달성</li>
</ul>
</li>
</ul>
<hr>
<h4 id="5-시스템-아키텍처-다이어그램cassandra-참고">5) 시스템 아키텍처 다이어그램(Cassandra 참고)</h4>
<h5 id="5-1-쓰기경로">5-1) 쓰기경로</h5>
<ol>
<li>클라이언트 → 노드: 쓰기 요청</li>
<li>Commit Log: 먼저 디스크에 Append → 장애 시 복구 가능</li>
<li>Memtable: 메모리상에 데이터 기록 (쓰기 성능 향상)</li>
<li>Flush: Memtable이 가득 차면 SSTable로 디스크에 영구 저장</li>
</ol>
<h5 id="5-2-읽기-경로">5-2) 읽기 경로</h5>
<ol>
<li>클라이언트 → 노드: 읽기 요청</li>
<li><strong>Memtable 조회</strong>: 메모리에서 최신 데이터 확인</li>
<li><strong>Bloom Filter</strong>: 해당 키가 SSTable에 있는지 빠르게 확인</li>
<li><strong>Partition Index</strong>: SSTable 내 위치 확인</li>
<li><strong>SSTable 조회 후 병합</strong>: 여러 SSTable에서 데이터 가져와 병합</li>
<li><strong>응답 반환</strong>: 최종 결과를 클라이언트에게 전달</li>
</ol>
<h2 id="4-레퍼런스">4. 레퍼런스</h2>
<ul>
<li><a href="https://docs.aws.amazon.com/whitepapers/latest/availability-and-beyond-improving-resilience/availability-and-beyond-improving-resilience.html">https://docs.aws.amazon.com/whitepapers/latest/availability-and-beyond-improving-resilience/availability-and-beyond-improving-resilience.html</a></li>
<li><a href="https://ardalis.com/cap-pacelc-and-microservices/">https://ardalis.com/cap-pacelc-and-microservices/</a></li>
<li><a href="https://blog.bytebytego.com/p/cap-pacelc-acid-base-essential-concepts">https://blog.bytebytego.com/p/cap-pacelc-acid-base-essential-concepts</a></li>
<li><a href="https://docs.datastax.com/en/cassandra-oss/3.x/cassandra/dml/dmlHowDataWritten.html">https://docs.datastax.com/en/cassandra-oss/3.x/cassandra/dml/dmlHowDataWritten.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 5장. 안정 해시 설계]]></title>
            <link>https://velog.io/@yellow-w/%EC%95%88%EC%A0%95-%ED%95%B4%EC%8B%9C-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@yellow-w/%EC%95%88%EC%A0%95-%ED%95%B4%EC%8B%9C-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Tue, 16 Sep 2025 16:19:49 GMT</pubDate>
            <description><![CDATA[<h1 id="5장-안정-해시-설계">5장. 안정 해시 설계</h1>
<p>수평적 규모 확장성을 달성하기 위해서는 요청 또는 데이터를 서버에 균등하게 나누는 것이 중요하다<br>안정해시는 이를 달성하기 위해 보편적으로 사용하는 기술이다</p>
<h2 id="1-수평적-규모-확장이란">1. 수평적 규모 확장이란?</h2>
<ul>
<li>단일 서버 성능을 올리는 수직적 확장(Scale-up)과 달리, 서버를 <strong>여러 대로 나누어</strong> 부하를 분산하는 것</li>
<li>균등하고 일관성 있는 데이터의 분산이 중요</li>
</ul>
<hr>
<h2 id="2-설계-관점에서의-서버-풀">2. 설계 관점에서의 서버 풀</h2>
<h3 id="2-1-고정-서버-풀">2-1. 고정 서버 풀</h3>
<ul>
<li>서버 수가 늘거나 줄지 않는 전제</li>
<li>단순 해시(<code>hash(key) % N</code>)로도 충분</li>
<li>균등 분포만 잘 맞추면 문제 없음</li>
</ul>
<h3 id="2-2-변동-서버-풀">2-2. 변동 서버 풀</h3>
<ul>
<li>현실 세계에서는 서버가 고정되지 않음:<ul>
<li>오토스케일링: 트래픽 스파이크 대응 위해 서버 증설/축소</li>
<li>장애 대응: 서버가 죽으면 다른 서버로 재분배 필요</li>
</ul>
</li>
<li>이때 단순 모듈러 해시는 <strong>풀 사이즈가 바뀌면 전체 키 매핑이 바뀌는 문제</strong> 발생 → 재배치 비용이 매우 큼</li>
</ul>
<h2 id="3-데이터-분산-방식">3. 데이터 분산 방식</h2>
<h3 id="3-1-단순-해시uniform--modulo-hashing">3-1. 단순 해시(Uniform / Modulo Hashing)</h3>
<ul>
<li><code>hash(key) % N (N = 서버 개수)</code>로 분산</li>
<li>키를 해시함수로 충분히 무작위로 섞은 후, 탐색이나 검색 없이 즉시 도달하는 인덱싱 모델<pre><code class="language-java">  long x = XxHash64.hash(keyBytes, seed); // 1. 해시 알고리즘 실행(섞기)
  int  i = Math.floorMod(x, N);           // 2. 범위 축소(인덱스 뽑기)
  Server s = servers[i];                  //    해당 인덱스의 서버 선택</code></pre>
</li>
<li>장점: 단순, 빠름, 균등 분배 가능</li>
<li><strong>Naive 해시(<code>hash(key) % N</code>)</strong> 방식의 가장 큰 약점:<ul>
<li>서버 수 N이 바뀌면 전체 키 매핑이 바뀜.</li>
<li>대규모 <strong>키 재배치</strong>발생 → 캐시 미스, 데이터 불일치 문제</li>
</ul>
</li>
<li>예: 서버 4대에서 5대로 확장 → 80% 이상의 키가 다른 서버로 이동</li>
</ul>
<h3 id="3-2-안정-해시consistent-hashing">3-2. 안정 해시(Consistent Hashing)</h3>
<ul>
<li><p>키와 서버를 동일한 해시 공간 (원형 링, hash ring)에 매핑</p>
</li>
<li><p>각 키는 <strong>시계 방향으로 가장 가까운 서버</strong>에 할당</p>
</li>
<li><p>서버 추가/삭제 시 일부 키만 재배치 → 확장성, 장애 대응에 유리</p>
</li>
<li><p>Redis Cluster, Cassandra, DynamoDB 같은 분산 시스템에서 사용</p>
</li>
<li><p>이미지 참고: <a href="https://medium.com/@sylvain.tiset/consistent-hashing-a-hash-ring-to-rule-them-all-ab1b3154e795">https://medium.com/@sylvain.tiset/consistent-hashing-a-hash-ring-to-rule-them-all-ab1b3154e795</a></p>
</li>
</ul>
<hr>
<h2 id="4-실무에서-고려할-점">4. 실무에서 고려할 점</h2>
<h3 id="4-1-해시-함수-선택">4-1. <strong>해시 함수 선택</strong></h3>
<ul>
<li>단순한 <code>mod N</code> 대신 어떤 해시 함수를 쓰느냐에 따라 <strong>분포 균등도</strong>가 달라짐</li>
<li>MD5, SHA-1 같은 암호학적 해시 vs MurmurHash, xxHash 같은 비암호학적 고속 해시</li>
<li>분산 시스템에서는 <strong>충돌 최소화 + 속도</strong> 둘 다 중요</li>
</ul>
<hr>
<h3 id="4-2-가상-노드virtual-node-vnode">4-2. <strong>가상 노드(Virtual Node, vnode)</strong></h3>
<ul>
<li>안정 해시만 쓰면 서버 개수가 적을 때 <strong>분포 불균형</strong> 발생</li>
<li>각 서버를 여러 개의 가상 노드로 링에 배치하면 <strong>데이터 분포가 더 균등해짐</strong></li>
</ul>
<hr>
<h3 id="4-3-서버-가중치weighted-consistent-hashing">4-3. <strong>서버 가중치(Weighted Consistent Hashing)</strong></h3>
<ul>
<li>모든 서버가 같은 스펙은 아님 → CPU, 메모리, 디스크, 네트워크 성능 차이 존재</li>
<li>서버별 처리 능력에 따라 가중치 부여</li>
<li><code>가상 노드 개수 ∝ 서버 성능</code> 으로 균형 잡음</li>
</ul>
<hr>
<h3 id="4-4-데이터-이동-비용-resharding-overhead">4-4. <strong>데이터 이동 비용 (Resharding Overhead)</strong></h3>
<ul>
<li>서버 추가/삭제 시에도 일부 키는 이동해야 함</li>
<li>데이터 이동 과정에서 <strong>캐시 미스 폭증</strong>, <strong>백엔드(DB) 부하 급증</strong> 가능</li>
<li>이를 줄이기 위해:<ul>
<li>백그라운드 점진적 마이그레이션</li>
<li>더블 라이트(Double-write) 전략</li>
<li>버전 기반 라우팅</li>
</ul>
</li>
</ul>
<hr>
<h3 id="4-5-안정-해시와-cap-정리">4-5. <strong>안정 해시와 CAP 정리</strong></h3>
<ul>
<li>분산 환경에서 노드 장애 → 데이터 재배치 → <strong>일관성(Consistency)</strong> 문제 발생</li>
<li>Dynamo, Cassandra는 <strong>Eventual Consistency + 안정 해시</strong> 모델을 채택</li>
<li>RDBMS 스타일의 Strong Consistency를 원하면 안정 해시 적용 범위에 제약 생김</li>
</ul>
<h4 id="1-eventual-consistency란">1) Eventual Consistency란?</h4>
<ul>
<li>분산 시스템에서 모든 복제본(replica)이 즉시는 아니지만, 충분한 시간이 지나면 결국 동일한 상태로 수렴하는 일관성 모델</li>
<li>CAP 이론에서 <strong>Availability(가용성)</strong>과 <strong>Partition Tolerance(분할 내성)</strong>을 우선할 때 주로 채택</li>
<li>강한 일관성(Strong Consistency)과 달리, 읽는 순간마다 최신 데이터를 보장하지 않는다</li>
</ul>
<h5 id="2-동작-원리">2) 동작 원리</h5>
<ul>
<li>쓰기 요청이 들어오면 모든 노드에 동시에 적용되지 않고, 먼저 도착한 일부 노드에서 처리됨</li>
<li>나머지 노드들은 비동기적 복제 / 동기화 프로세스를 통해 나중에 같은 데이터로 맞춰짐</li>
<li>결국 어느 시점에는 모든 복제본이 같은 값을 가지게 됨<h5 id="3-cap">3) C.A.P</h5>
</li>
<li>Consistency (일관성)
→ 모든 노드가 같은 시점에 동일한 데이터를 보여준다</li>
<li>Availability (가용성)
→ 모든 요청에 대해 항상 응답을 돌려줄 수 있다 (성공/실패 여부 무관)</li>
<li>Partition Tolerance (분할 내성)
→ 네트워크가 분리(Partition)돼도 시스템은 계속 동작할 수 있다</li>
</ul>
<hr>
<h3 id="4-6-안정-해시-대안-기법">4-6. <strong>안정 해시 대안 기법</strong></h3>
<h4 id="1-rendezvous-hashing-hrw-highest-random-weight">1) <strong>Rendezvous Hashing (HRW, Highest Random Weight)</strong></h4>
<ul>
<li>Consistent Hashing보다 간단하면서도 비슷한 재배치 성질</li>
<li>구글 GFS, Ceph, 일부 CDN에서 사용<ul>
<li><strong>장점</strong>: 균등 분포, 구현 단순, Consistent Hashing보다 데이터 이동량이 더 적을 수 있음</li>
</ul>
</li>
</ul>
<h4 id="2-jump-consistent-hashing">2) <strong>Jump Consistent Hashing</strong></h4>
<ul>
<li>구글이 제안, O(1) 시간 복잡도, 균등 분포 뛰어남</li>
<li>Kafka, FoundationDB 같은 대규모 분산 시스템에서 채택</li>
</ul>
<hr>
<h2 id="5-운영-단계에서-해시와-안정-해시의-문제들">5. 운영 단계에서 해시와 안정 해시의 문제들</h2>
<h3 id="5-1-데이터-분포-균등성-uniformity">5-1. <strong>데이터 분포 균등성 (Uniformity)</strong></h3>
<ul>
<li>해시는 이상적으로 키를 균등하게 분배해야 함</li>
<li>하지만 실제 운영에서는 <strong>Hotspot Key</strong> / <strong>Celebrity Problem</strong>이 발생:<ul>
<li>예: 특정 사용자 ID(연예인 계정), 특정 해시 범위에만 트래픽 집중</li>
</ul>
</li>
<li>안정 해시를 쓰더라도, 키 분포 자체가 편향되면 <strong>샤드 불균형</strong>은 피할 수 없음</li>
</ul>
<h4 id="해결-전략">해결 전략</h4>
<ul>
<li><strong>Sharding Key 설계</strong>: 고카디널리티(high cardinality) 키 선택</li>
<li><strong>Hash Salting</strong>: 키에 무작위성을 추가해 편향 완화</li>
<li><strong>Virtual Nodes (가상 노드)</strong>: 한 물리 노드가 여러 해시 구간 담당 → 균등화</li>
</ul>
<hr>
<h3 id="5-2-노드-추가삭제-시-데이터-재배치-비용">5-2. <strong>노드 추가/삭제 시 데이터 재배치 비용</strong></h3>
<ul>
<li>단순 해싱(Mod N) 방식:<ul>
<li>서버 수가 바뀌면, 전체 키의 대부분이 다른 서버로 이동 → <strong>재배치 비용 큼</strong></li>
</ul>
</li>
<li>안정 해시:<ul>
<li>노드 추가/삭제 시, 해당 구간 일부 키만 이동 → 비용 최소화</li>
</ul>
</li>
<li>그러나 <strong>재배치 자체는 여전히 비용</strong>:<ul>
<li>TB 단위 데이터를 옮기는 데 수시간~수일</li>
<li>그동안 읽기/쓰기 지연 발생</li>
</ul>
</li>
</ul>
<h4 id="해결-전략-1">해결 전략</h4>
<ul>
<li><strong>Rebalancing Window</strong>: 트래픽이 낮은 시간대에 점진적 재분배</li>
<li><strong>Double Write</strong>: 재분배 기간 동안 두 노드에 동시에 기록해 일관성 유지</li>
<li><strong>Lazy Migration</strong>: 요청 시점에 필요한 키만 새로운 노드로 점진적으로 옮김</li>
</ul>
<hr>
<h3 id="5-3-데이터-일관성-consistency">5-3. <strong>데이터 일관성 (Consistency)</strong></h3>
<ul>
<li>분산 해시 환경에서는, 노드 이동/장애 시 데이터 복제가 중요</li>
<li>안정 해시의 기본 구조는 <strong>N개의 인접 노드에 복제(replica)</strong> → Dynamo, Cassandra.</li>
<li>문제:<ul>
<li>네트워크 분할(Partition) → 일부 노드만 최신 데이터 보유</li>
<li>복제본 간 충돌(conflict) 발생</li>
</ul>
</li>
</ul>
<h4 id="해결-전략-2">해결 전략</h4>
<ul>
<li><strong>Quorum 기반 읽기/쓰기</strong>: W+R &gt; N 보장 (예: 3개의 복제 중 2개 이상 성공해야 ack)</li>
<li><strong>Conflict Resolution</strong>: Last Write Wins, Vector Clock, CRDT 등</li>
<li><strong>Eventual Consistency 허용 여부</strong> 결정: 애플리케이션 성격 따라 선택</li>
</ul>
<hr>
<h3 id="5-4-장애-복구와-트래픽-우회">5-4. <strong>장애 복구와 트래픽 우회</strong></h3>
<ul>
<li><p>노드가 죽으면, 해당 구간 키 요청이 모두 장애로 이어짐</p>
</li>
<li><p>안정 해시는 <strong>바로 인접 노드로 우회</strong> 가능하지만, 실제 운영은 더 복잡:</p>
<ul>
<li>캐시/DB 미스율 폭발</li>
<li>특정 노드로 트래픽 집중(2차 장애)</li>
</ul>
<h4 id="해결-전략-3">해결 전략</h4>
</li>
<li><p><strong>Replica 활용</strong>: 읽기는 다른 복제본에서 즉시 처리</p>
</li>
<li><p><strong>Auto-Rebalancing 제어</strong>: 장애 시 자동 리밸런싱을 막고, 임시 우회 후 복구 단계에서 이동</p>
</li>
<li><p><strong>Rate Limiting + Circuit Breaker</strong>: Failover 중 과부하 방지</p>
</li>
</ul>
<hr>
<h3 id="5-5-관측성과-모니터링">5-5. <strong>관측성과 모니터링</strong></h3>
<ul>
<li>운영 시 가장 큰 문제 중 하나는 <strong>샤드 불균형을 조기에 탐지하지 못하는 것</strong></li>
<li>해시 분포 문제는 <strong>지표</strong>로 드러남:<ul>
<li>샤드별 QPS, Latency, Storage Usage, Cache Hit Rate</li>
</ul>
</li>
<li>안정 해시라고 해서 자동으로 &quot;균등&quot;이 보장되는 건 아님 → 반드시 모니터링 필요</li>
</ul>
<h4 id="핵심-지표">핵심 지표</h4>
<ul>
<li><strong>키 분포 균등도(Entropy)</strong></li>
<li><strong>샤드별 Hotspot 비율</strong></li>
<li><strong>재배치 중 지연 시간 증가율</strong></li>
</ul>
<hr>
<h3 id="5-6-운영-환경에서의-설계-trade-off">5-6. <strong>운영 환경에서의 설계 Trade-off</strong></h3>
<h4 id="1-서버-풀server-pool의-고정-vs-변동">1) 서버 풀(Server Pool)의 <strong>고정 vs 변동</strong></h4>
<ul>
<li><strong>고정 풀</strong>: 서버 수가 잘 변하지 않으면, 단순 해싱(Mod N)도 가능 → 운영 단순</li>
<li><strong>변동 풀</strong>: 클라우드 오토스케일링, 다중 데이터센터 환경이라면 → 안정 해시 필수</li>
</ul>
<h4 id="2-데이터-분포">2) 데이터 분포</h4>
<ul>
<li>분포가 <strong>완전히 랜덤</strong>이라면 이상적 → 하지만 실제 키는 특정 패턴(사용자 ID, 지역, 시간대)에 따라 편향</li>
<li>해시 함수 선택, 키 설계가 중요</li>
</ul>
<h4 id="3-안정-해시와-변형-기법">3) 안정 해시와 변형 기법</h4>
<ul>
<li><strong>Virtual Nodes</strong>: 균등화 보완</li>
<li><strong>Jump Hashing</strong>: O(1) 매핑, 대규모 클러스터 효율적</li>
<li><strong>Rendezvous Hashing</strong>: 데이터 이동량 최소화</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 4장. 처리율 제한 장치의 설계]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-4%EC%9E%A5.-%EC%B2%98%EB%A6%AC%EC%9C%A8-%EC%A0%9C%ED%95%9C-%EC%9E%A5%EC%B9%98%EC%9D%98-%EC%84%A4%EA%B3%84-aq7my7tk</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-4%EC%9E%A5.-%EC%B2%98%EB%A6%AC%EC%9C%A8-%EC%A0%9C%ED%95%9C-%EC%9E%A5%EC%B9%98%EC%9D%98-%EC%84%A4%EA%B3%84-aq7my7tk</guid>
            <pubDate>Tue, 16 Sep 2025 14:57:57 GMT</pubDate>
            <description><![CDATA[<h1 id="4장-처리율-제한-장치의-설계">4장. 처리율 제한 장치의 설계</h1>
<h2 id="1-처리율-제한-장치">1. 처리율 제한 장치</h2>
<h3 id="1-1-의미">1-1. 의미</h3>
<ul>
<li>클라이언트 또는 서비스가 보내는 트래픽의 처리율을 제어하기 위한 장치</li>
<li>특정 기간 내에 전송되는 클라이언트의 요청 횟수 제한<ul>
<li>예시<ul>
<li>사용자는 초당 2회 이상의 새 글을 올릴 수 없음</li>
<li>같은 IP 주소로는 하루에 10개 이상의 계정 생성 불가</li>
<li>같은 디바이스로는 주당 5회 이상 리워드 요청 불가</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="1-2-api에-처리율-제한-장치를-두면-좋은점">1-2. API에 처리율 제한 장치를 두면 좋은점</h3>
<ul>
<li>악의적 공격에 의한 <strong>자원 고갈 방지 가능</strong><ul>
<li>DoS 공격뿐만 아니라, 무차별 대입 공격(Brute-force attack), 크리덴셜 스터핑(Credential stuffing) 등</li>
<li>비용이 저렴하면서도 시스템에 치명적인 공격을 효과적으로 방어</li>
<li>특정 IP나 계정의 비정상적인 요청 패턴을 초기에 차단  → 시스템 전체를 보호</li>
</ul>
</li>
<li><strong>비용 관리: 예측 가능한 인프라 운영</strong><ul>
<li>인프라 자원의 상한선을 설정 → 예측 가능한 비용 내에서 서비스 운영 가능</li>
<li>우선순위가 높은 핵심 API에 더 많은 리소스를 할당  →  전략적 자원 분배 가능</li>
</ul>
</li>
<li>서버 <strong>과부하 방지</strong><ul>
<li>봇 트래픽/잘못된 사용 패턴을 걸러내어 안정성 확보</li>
<li>선량한 사용자들이 안정적으로 서비스를 이용할 수 있도록 트래픽을 제어  → 서비스의 전반적인 품질과 신뢰도를 높임</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-프레임-워크">2. 프레임 워크</h2>
<h3 id="1단계-문제-이해-및-설계-범위-확정">[1단계] 문제 이해 및 설계 범위 확정</h3>
<h4 id="1-1-면접관과의-소통을-통한-구현-기능-확인">[1-1] 면접관과의 소통을 통한 구현 기능 확인</h4>
<table>
<thead>
<tr>
<th>항목</th>
<th>질문</th>
</tr>
</thead>
<tbody><tr>
<td>처리율 제한 장치 설계의 종류</td>
<td>클라이언트 측? 서버 측?</td>
</tr>
<tr>
<td>API 호출 제어 기준</td>
<td>주소? 사용자 ID? 기타?</td>
</tr>
<tr>
<td>시스템 규모</td>
<td>스타트업? 대규모?</td>
</tr>
<tr>
<td>동작 환경</td>
<td>분산 환경 여부</td>
</tr>
<tr>
<td>독립된 서비스? 앱 코드 포함?</td>
<td></td>
</tr>
<tr>
<td>UX</td>
<td>사용자의 요청이 처리율 제한 장치에 의해 걸러진 경우 사용자에게 알림 여부?</td>
</tr>
</tbody></table>
<h4 id="1-2요구사항">[1-2]요구사항</h4>
<ul>
<li>설정된 처리율을 초과하는 요청은 정확하게 제한</li>
<li>낮은 응답 시간</li>
<li>가능한 한 적은 메모리 사용</li>
<li>분산형 처리율 제한(<code>distributed rate limiting</code>)<ul>
<li>하나의 처리율 제한 장치를 여러 서버나 프로세스에서 공유할 수 있어야 함</li>
</ul>
</li>
<li>예외 처리<ul>
<li>요청이 제한되었을 때는 그 사실을 사용자에게 분명하게 보여주어야 함</li>
</ul>
</li>
<li>높은 결함 감내성(<code>fault tolerance</code>)<ul>
<li>제한 장치에 장애가 생기더라도 전체 시스템에 영향을 주어서는 안됨</li>
</ul>
</li>
</ul>
<hr>
<h3 id="2단계-개략적-설계안-제시-및-동의-구하기">[2단계] 개략적 설계안 제시 및 동의 구하기</h3>
<h4 id="2-1처리율-제한-장치-배치-전략">[2-1]처리율 제한 장치 배치 전략</h4>
<h5 id="1-클라이언트-측-x">(1) 클라이언트 측: X</h5>
<ul>
<li>요청 위변조 가능성 큼 → 신뢰 불가</li>
<li>모든 클라이언트 구현 통제 불가</li>
</ul>
<h5 id="2-서버-측">(2) 서버 측</h5>
<ul>
<li>마이크로서비스 환경에서는 <strong>API Gateway가 압도적으로 유리</strong></li>
<li>레거시 모놀리식에서는 미들웨어가 현실적 선택</li>
</ul>
<table>
<thead>
<tr>
<th>구분</th>
<th><strong>API 게이트웨이</strong></th>
<th><strong>미들웨어</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>적용 환경</strong></td>
<td>마이크로서비스 아키텍처</td>
<td>레거시 모놀리식 아키텍처</td>
</tr>
<tr>
<td><strong>특징</strong></td>
<td>현대적인 접근법, 완전 위탁 관리형 서비스(fully managed) 가능 (AWS API Gateway, NGINX, Kong 등)</td>
<td>웹 프레임워크 내에 직접 구현</td>
</tr>
<tr>
<td><strong>지원 기능</strong></td>
<td>인증, 로깅, 라우팅, SSL 종료, IP 관리, 처리율 제한 등 종합 기능 내장</td>
<td>단순 처리율 제한 + 비즈니스 로직 연동</td>
</tr>
<tr>
<td><strong>정책 관리</strong></td>
<td>중앙화된 정책 관리 / 비즈니스 로직과 분리</td>
<td>서비스별 개별 구현, 정책 변경 시 코드 배포 필요</td>
</tr>
<tr>
<td><strong>운영 측면</strong></td>
<td>다중 서비스 지원, 운영팀이 실시간 제어 가능</td>
<td>각 서비스 코드 내 변경 필요, 배포 주기와 강하게 연동</td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>통합 관리, 확장성, 운영 효율성</td>
<td>세밀한 로직 연동, 외부 게이트웨이 불필요</td>
</tr>
<tr>
<td><strong>단점</strong></td>
<td>초기 도입/구축 복잡성, 게이트웨이에 대한 의존성</td>
<td>중복 구현, 유지보수 비용 증가, 확장성 한계</td>
</tr>
</tbody></table>
<h4 id="2-2처리율-제한-알고리즘">[2-2]처리율 제한 알고리즘</h4>
<ul>
<li>토큰 버킷(<code>token bucket</code>)</li>
<li>누출 버킷(<code>leaky bucket</code>)</li>
<li>고정 윈도 카운터(<code>fixed window counter</code>)</li>
<li>이동 윈도 로그(<code>sliding window log</code>)</li>
<li>이동 윈도 카운터(<code>sliding window counter</code>)</li>
</ul>
<h4 id="2-3개략적인-아키텍처">[2-3]개략적인 아키텍처</h4>
<ul>
<li><strong>카운터 단위</strong>: 사용자 / IP 주소 / API 엔드포인트</li>
<li><strong>보관 장소</strong>: DB는 느림 → 메모리 캐시(Redis)가 적합<ul>
<li><code>INCR</code>와 <code>EXPIRE</code>로 원자적 증가 및 TTL 관리</li>
<li><strong>Lua 스크립트</strong> 활용 시 읽기-쓰기-만료를 한 번에 처리 → 경쟁 조건 방지</li>
<li>TTL 만료 후 자동 삭제 → 메모리 관리 용이</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3단계-상세-설계">[3단계] 상세 설계</h3>
<h4 id="3-1처리율-제한-규칙-rate-limiting-rules">[3-1]처리율 제한 규칙 (Rate Limiting Rules)</h4>
<ul>
<li><strong>제한 단위</strong>: 사용자 ID, IP 주소, API Key, 엔드포인트, 디바이스 등</li>
<li><strong>시간 윈도우</strong>: 초당, 분당, 시간당, 하루 단위 등</li>
<li><strong>엔드포인트별 차등 제한</strong>:<ul>
<li>핵심 API → 높은 한도</li>
<li>비용 큰 API → 낮은 한도</li>
</ul>
</li>
<li><strong>가중치 기반 제한</strong>:<ul>
<li>예: 단순 조회 = 1 요청, 복잡한 리포트 생성 = 10 요청</li>
</ul>
</li>
</ul>
<h4 id="3-2한도-초과-트래픽-처리-over-limit-traffic-handling">[3-2]한도 초과 트래픽 처리 (Over-limit Traffic Handling)</h4>
<ul>
<li><strong>HTTP 상태 코드 429</strong> (<code>Too Many Requests</code>) 반환</li>
<li><strong>헤더 노출</strong>:<ul>
<li><code>X-RateLimit-Limit</code> (총 한도)</li>
<li><code>X-RateLimit-Remaining</code> (남은 요청 수)</li>
<li><code>X-RateLimit-Reset</code> (초기화 시각)</li>
<li><code>Retry-After</code> (재시도까지 대기 시간)</li>
</ul>
</li>
<li><strong>Graceful Fallback</strong>:<ul>
<li>일부 기능 제한 (쓰기 차단, 읽기만 허용)</li>
<li>캐시/대체 데이터 반환</li>
</ul>
</li>
<li><strong>큐잉 옵션</strong> (Soft limit): 초과 요청을 버리지 않고 대기열에 넣어 순차 처리</li>
</ul>
<h4 id="3-3분산-환경에서의-처리율-제한-구현">[3-3]분산 환경에서의 처리율 제한 구현</h4>
<h5 id="1-경쟁-조건-race-condition">(1) 경쟁 조건 (Race Condition)</h5>
<ul>
<li>여러 인스턴스가 동시에 같은 키 갱신 시 발생 가능</li>
<li><strong>해결책</strong><ul>
<li>Redis <code>INCR</code> + <code>EXPIRE</code> 조합 (원자적 연산)</li>
<li>복잡한 로직 → <strong>Lua 스크립트</strong>로 원자성 보장</li>
<li>락</li>
</ul>
</li>
</ul>
<h5 id="2-동기화">(2) 동기화</h5>
<ul>
<li><strong>Sticky Session</strong>: 동일 사용자가 항상 같은 서버로 → 단순하지만 확장성 제한</li>
<li><strong>중앙 집중 저장소</strong> (Redis, DynamoDB 등): 모든 인스턴스가 공유 → 분산 환경에 적합</li>
<li><strong>최종 일관성(Eventual Consistency)</strong> 모델 허용 여부를 서비스 특성에 맞게 결정</li>
</ul>
<h5 id="3-성능-최적화">(3) 성능 최적화</h5>
<ul>
<li><strong>에지 서버</strong>에서 제한 적용 → 사용자와 가까운 곳에서 차단</li>
<li><strong>로컬 캐시 버퍼링</strong>: 초단위 카운트는 로컬에서 관리, 일정 주기마다 Redis와 동기화</li>
</ul>
<h4 id="3-4모니터링-방안">[3-4]모니터링 방안</h4>
<ul>
<li>모니터링을 통해 알고리즘의 효과성과 처리율 제한 규칙의 효과성 확인 가능</li>
</ul>
<h5 id="1-모니터링-지표">(1) 모니터링 지표</h5>
<ul>
<li><strong>요청 처리율 (RPS)</strong></li>
<li><strong>거부율</strong> (429 응답 비율)</li>
<li><strong>응답 시간</strong> (레이트 리미터 처리 지연 포함)</li>
<li><strong>활성 키 수</strong> (현재 추적 중인 유저/IP/토큰 개수)</li>
<li><strong>시스템 리소스</strong> (Redis CPU, 메모리, 연결 상태)</li>
<li><strong>에러율</strong> (Redis 장애, 타임아웃 등)</li>
</ul>
<h5 id="2-대시보드-예시">(2) 대시보드 예시</h5>
<ul>
<li>API별 요청량/거부율 시각화</li>
<li>슬라이딩 윈도우별 평균 처리율 표시</li>
<li>Redis 노드 상태/레플리카 동기화 상태</li>
</ul>
<h5 id="3-알림자동화">(3) 알림/자동화</h5>
<ul>
<li>SLA 기준 초과 시 알람 발송</li>
<li>장애 발생 시 <strong>자동 롤백 / 오토스케일링</strong> 트리거</li>
</ul>
<hr>
<h3 id="4단계-마무리">[4단계] 마무리</h3>
<h4 id="4-1-추가적으로-언급할-부분">[4-1] 추가적으로 언급할 부분</h4>
<h5 id="1-정책-유형">(1) 정책 유형</h5>
<ul>
<li>경성 또는 연성 처리율 제한<ul>
<li>경성(<code>Hard</code>): 절대적인 임계치 제한</li>
<li>연성(<code>Soft</code>): 초과 요청은 큐에 대기 또는 일부 기능만 제한<ul>
<li>예: 읽기는 되지만 쓰기는 차단</li>
</ul>
</li>
</ul>
</li>
</ul>
<h5 id="2-보안안정성">(2) 보안/안정성</h5>
<ul>
<li><p>계층형 방어 (Defense in Depth)</p>
<ul>
<li>L4 (네트워크): iptables/firewall</li>
<li>L7 (게이트웨이): 전역 처리율 제한 </li>
<li>서비스: 비즈니스 로직별 제어</li>
<li>리소스: DB connection pool, thread pool</li>
</ul>
</li>
<li><p>동적 임계값 조정</p>
<ul>
<li>트래픽 상황에 따라 한도를 자동 상향/하향 조정 → 예측 불가한 스파이크 완화</li>
</ul>
</li>
</ul>
<h5 id="3-사용자-경험-최적화">(3) 사용자 경험 최적화</h5>
<ul>
<li><p>응답 헤더 제공 (<code>X-RateLimit-Limit</code>, <code>X-RateLimit-Remaining</code>, <code>X-RateLimit-Reset</code>)</p>
</li>
<li><p>제한 시 429와 함께 안내 메시지, Retry-After 제공</p>
</li>
<li><p>클라이언트가 자체적으로 요청을 조절할 수 있도록 지원</p>
</li>
<li><pre><code class="language-http">HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0  
X-RateLimit-Reset: 1640995200
Retry-After: 60

{
  &quot;error&quot;: &quot;rate_limit_exceeded&quot;,
  &quot;message&quot;: &quot;API rate limit exceeded&quot;,
  &quot;retry_after_seconds&quot;: 60
}</code></pre>
</li>
</ul>
<h5 id="4-클라이언트-설계-패턴">(4) 클라이언트 설계 패턴</h5>
<ul>
<li>클라이언트 측 캐시 활용 → API 호출 횟수 감소</li>
<li>백오프(특히 <strong>지수 백오프</strong>)로 재시도</li>
<li>임계치 이해 및 graceful fallback 구현</li>
</ul>
<hr>
<h2 id="3-참고">3. 참고</h2>
<h3 id="3-1-지수-백오프-exponential-backoff">3-1. 지수 백오프 (Exponential Backoff)</h3>
<h3 id="1-정의">(1) 정의</h3>
<ul>
<li>네트워크 요청이 실패했을 때, <strong>재시도 간격을 지수적으로 증가시키며 재시도하는 전략</strong>.</li>
<li>예: 1초 → 2초 → 4초 → 8초 … 처럼 대기 시간이 점점 길어짐.</li>
</ul>
<h3 id="2-목적">(2) 목적</h3>
<ul>
<li><strong>트래픽 폭주 방지</strong>: 많은 클라이언트가 동시에 무한 재시도를 하면 서버가 더 빨리 다운됨 → 백오프는 재시도를 분산시켜 서버 회복 시간을 벌어줌</li>
<li><strong>시스템 안정성 확보</strong>: 재시도 간격을 늘려 네트워크나 서버의 일시적 부하 완화</li>
<li><strong>비용 절감</strong>: 불필요한 실패 요청을 줄여 서버 리소스와 네트워크 비용 절약</li>
</ul>
<h3 id="3-실무적-특징">(3) 실무적 특징</h3>
<ul>
<li>보통 <strong>지수 증가 + 랜덤 지터(jitter)</strong>를 함께 사용<ul>
<li>단순 지수 증가만 하면 클라이언트들이 동시에 재시도하는 <strong>“스파이크(동기화 폭주)”</strong> 발생 가능</li>
<li>→ 지터를 넣어 <code>2초 ~ 3초</code>, <code>4초 ~ 6초</code> 범위 안에서 랜덤 대기 → 동기화 회피</li>
</ul>
</li>
<li>많이 쓰이는 곳: AWS SDK, Google Cloud API, TCP 네트워크 재전송 로직</li>
</ul>
<h3 id="3-2-graceful-fallback">3-2. Graceful Fallback</h3>
<h3 id="1-정의-1">(1) 정의</h3>
<ul>
<li>시스템이 장애나 제한 상황에 직면했을 때, <strong>완전히 실패하지 않고 축소된 기능이나 대체 경로를 제공하는 전략</strong></li>
<li>즉, “최소한의 경험”을 보장하는 방식</li>
</ul>
<h3 id="2-목적-1">(2) 목적</h3>
<ul>
<li><strong>사용자 경험 보호</strong>: 서비스 전체가 죽는 대신, 일부 기능만 제한적으로 제공</li>
<li><strong>안정성 강화</strong>: 특정 리소스(DB, API)가 장애나 Rate Limit에 걸려도 서비스의 핵심 기능은 계속 동작</li>
<li><strong>신뢰 확보</strong>: “완전 불가” 대신 “부분적 가능”을 제공 → 서비스에 대한 신뢰감 유지</li>
</ul>
<h3 id="3-실무적-특징-1">(3) 실무적 특징</h3>
<ul>
<li>예시<ul>
<li>트위터: API Rate Limit 초과 시, <strong>최근 트윗 캐시</strong>를 보여주고 새 트윗은 잠시 불가</li>
<li>전자상거래: 추천 서비스 장애 시, <strong>기본 베스트셀러 목록</strong> 제공</li>
<li>동영상 스트리밍: 서버 부하 시, <strong>저화질 영상으로 자동 전환</strong></li>
</ul>
</li>
<li>Graceful Fallback은 단순한 에러 메시지(“서버 다운”)가 아니라, <strong>대체 데이터/기능</strong>을 제공하는 점이 핵심</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 3장. 시스템 설계 면접 공략법
]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-3%EC%9E%A5.-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EB%A9%B4%EC%A0%91-%EA%B3%B5%EB%9E%B5%EB%B2%95</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-3%EC%9E%A5.-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%A4%EA%B3%84-%EB%A9%B4%EC%A0%91-%EA%B3%B5%EB%9E%B5%EB%B2%95</guid>
            <pubDate>Mon, 15 Sep 2025 16:09:49 GMT</pubDate>
            <description><![CDATA[<h1 id="3장-시스템-설계-면접-공략법">3장. 시스템 설계 면접 공략법</h1>
<h2 id="1-시스템-설계-면접의-의미">1. 시스템 설계 면접의 의미</h2>
<ul>
<li><p>시스템 설계 면접은 두 명의 동료가 모호한 문제를 풀기 위해 협력하여 그 해결책을 찾아내는 과정에 대한 시뮬레이션</p>
</li>
<li><p>면접은</p>
<ul>
<li>설계 기술을 시연하는 자리</li>
<li>설계 과정에서 내린 결정들에 대한 방어 능력을 보이는 자리</li>
<li>면접관의 피드백을 건설적인 방식으로 처리할 자질이 있음을 보이는 자리</li>
</ul>
</li>
<li><p>면접관이 면접자에게 평가하고자 하는 부분</p>
<ul>
<li>지원자의 설계 능력의 기술적 측면</li>
<li>협력</li>
<li>압박</li>
<li>문제 해결 능력</li>
<li>좋은 질문을 던질 능력</li>
<li>부정적 신호<ul>
<li>타협적 결정 도외시, 과도한 엔지니어링, 완고함, 편협함 등</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="2-효과적인-면접을-위한-4단계-접근법">2. 효과적인 면접을 위한 4단계 접근법</h2>
<h3 id="1단계-문제-이해-및-설계-범위-확정-요구사항을-이해하고-모호함을-없애는-것">1단계. 문제 이해 및 설계 범위 확정: 요구사항을 이해하고 모호함을 없애는 것</h3>
<h4 id="1-엔지니어가-가져야-할-가장-중요한-기술">(1) 엔지니어가 가져야 할 가장 중요한 기술</h4>
<ul>
<li><strong>올바른 질문을 하는 것 / 적절한 가정을 하는 것 / 시스템 구축에 필요한 정보를 모으는 것</strong></li>
<li>예시<ul>
<li>구체적으로 어떤 기능들을 만들어야 하나?</li>
<li>제품 사용자 수는 얼마나 되나?</li>
<li>회사의 규모는 얼마나 빨리 커지리라 예상하나?(석 달, 여섯 달, 일년 뒤의 규모는 얼마나 되리라 예상하는가?)</li>
<li>회사가 주로 사용하는 기술 스택은 무엇인가?</li>
<li>설계를 단순화하기 위해 활용할 수 있는 기존 서비스로는 어떤 것들이 있는가?</li>
</ul>
</li>
</ul>
<h4 id="2-예제---뉴스-피드-시스템-설계">(2) 예제 - 뉴스 피드 시스템 설계</h4>
<ul>
<li><p>구체적으로 어떤 기능들을 만들어야 하나?</p>
<ul>
<li>모바일 앱과 웹 앱 가운데 어느 쪽을 지원? 아니면 둘 다?</li>
<li>가장 중요한 기능?</li>
<li>뉴스 피드의 정렬 기준은? 피드에 올라갈 포스트마다 다른 가중치가 부여되어야 하는 지?</li>
<li>피드의 형식은 텍스트? 이미지나 비디오도 포함 가능?</li>
</ul>
</li>
<li><p>제품 사용자 수는 얼마나 되나?</p>
<ul>
<li>한 사용자는 최대 몇 명의 사용자와 친구를 맺을 수 있는가?</li>
<li>사이트로 오는 트래픽 규모는 어느 정도 되는가?</li>
</ul>
</li>
</ul>
<h3 id="2단계-개략적인-설계안-제시-및-동의-구하기">2.단계. 개략적인 설계안 제시 및 동의 구하기</h3>
<h4 id="1-개략적-설계안-제시하고-면접관-의견-청취">(1) 개략적 설계안 제시하고 면접관 의견 청취</h4>
<ul>
<li>설계안에 대한 최초 청사진 제시 및 의견 구하기<ul>
<li>면접관을 팀원인 것처럼 대하기</li>
</ul>
</li>
<li>화이트 보드나 종이에 핵심 컴포넌트를 포함하는 다이어그램 그리기<ul>
<li>클라이언트(모바일/웹), API, 웹 서버, 데이터 저장소, 캐시, CDN, 메세지 큐 등 포함</li>
</ul>
</li>
<li>최초 설계안이 시스템 규모에 관계된 제약 사항들을 만족하는 지 개략적으로 계산<ul>
<li>계산 과정은 소리내어 설명</li>
<li>개략적 추적이 필요한 지는 면접관에게 미리 물어보기</li>
</ul>
</li>
<li>시스템의 구체적 사용 사례 살펴 보기<ul>
<li>개략적 설계안을 잡아 나가는데 도움이 됨</li>
<li>미처 고려하지 못한 에지 케이스 발견에도 도움 됨</li>
</ul>
</li>
<li>API 엔드포인트나 데이터베이스 스키마도 보여야 하는가?<ul>
<li>질문에 따라 다르다. 면접관의 의견을 구하라</li>
</ul>
</li>
</ul>
<h4 id="2-예제">(2) 예제</h4>
<h5 id="설계의-흐름">설계의 흐름</h5>
<p>개략적으로 봤을 때 이 설계는 두 가지 플로우로 나눠 생각해볼 수 있다</p>
<ul>
<li>피드 발행(feed publishing)<ul>
<li>사용자가 포스트를 올리면 관련된 데이터가 캐시/DB에 기록되고, 해당 사용자의 친구 뉴스 피드에 뜨게 됨</li>
</ul>
</li>
<li>피드 생성(feed building)<ul>
<li>어떤 사용자의 뉴스 피드는 해당 사용자 친구들의 포스트를 시간 역순으로 정렬하여 만듦</li>
</ul>
</li>
</ul>
<h5 id="설계-흐름도">설계 흐름도</h5>
<ul>
<li>피드 발행
<img src="https://velog.velcdn.com/images/yellow-w/post/02e86671-b4fb-4cb4-bb4c-5440a5529a49/image.png" alt=""></li>
</ul>
<ul>
<li>피드 생성
<img src="https://velog.velcdn.com/images/yellow-w/post/7cd46087-c8aa-4a38-be93-ad3f395c4941/image.png" alt=""></li>
</ul>
<h3 id="3단계-상세-설계-설계-대상-컴포넌트-사이의-우선순위-선정">3단계. 상세 설계: 설계 대상 컴포넌트 사이의 우선순위 선정</h3>
<h4 id="1-상세-설계-시작-전-달성한-상태">(1) 상세 설계 시작 전 달성한 상태</h4>
<ul>
<li>시스템에서 전반적으로 달성해야 할 목표와 기능 범위 확인</li>
<li>전체 설계의 개략적 청사진 마련</li>
<li>해당 청사진에 대한 면접관의 의견 청취</li>
<li>상세 설계에서 집중해야 할 영역들 확인</li>
</ul>
<h4 id="2-설계-대상-컴포넌트-사이의-우선-순위-정하기">(2) 설계 대상 컴포넌트 사이의 우선 순위 정하기</h4>
<ul>
<li>시스템 성능 특성에 대한 질문의 경우, 질문 내용은 시스템 병목 구간이나 자원 요구량 추정치에 초점이 맞춰져 있을 것</li>
<li>대부분의 면접관은 특정 시스템 컴포넌트들의 세부 사항을 깊이 있게 설명하는 것을 보고 싶어 함<ul>
<li>예시<ul>
<li>URL 단축기 설계: 해시 함수의 설계</li>
<li>채팅 시스템: 지연시간을 줄이는 설계, 사용자의 온/오프라인 상태 표시에 관한 설계</li>
</ul>
</li>
</ul>
</li>
<li>시간 관리: 사소한 세부사항이 아니라 규모 확장 가능한 시스템을 설계할 능력이 있다는 것을 입증할만한 사항에 시간을 쓰기</li>
</ul>
<h3 id="3-예제">(3) 예제</h3>
<ul>
<li><p>뉴스 피드 설계에서 우선순위가 높은 컴포넌트 선정</p>
<ul>
<li>피드 발행(feed publishing)</li>
<li>뉴스 피드 가져오기(news feed retrieval)</li>
</ul>
</li>
<li><p>피드 발행</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yellow-w/post/c6de1eac-c593-4dfe-8b18-d1a67f8f67ef/image.png" alt=""></p>
<ul>
<li>뉴스 피드 가져오기</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yellow-w/post/440e4a43-321e-4328-80a5-561258b7d0d2/image.png" alt=""></p>
<h3 id="4단계-마무리">4단계. 마무리</h3>
<h4 id="1-지침-활용">(1) 지침 활용</h4>
<ul>
<li>시스템 병목 구간 혹은 좀 더 개선 가능한 지점 파악<ul>
<li>개선할 점은 언제나 있음</li>
<li>비판적 사고 능력을 보이고, 마지막으로 좋은 인상을 남길 기회</li>
</ul>
</li>
<li>만든 설계를 다시 한번 요약하면 면접관의 기억을 환기시켜줌<ul>
<li>특히 여러 해결책을 제시한 경우 중요함</li>
</ul>
</li>
<li>오류 발생 시 발생이 예상되는 일<ul>
<li>서버 오류, 네트워크 장애</li>
</ul>
</li>
<li>운영 이슈<ul>
<li>메트릭은 수집 방법, 모니터링 방법, 로깅 방법, 배포 방법 등</li>
</ul>
</li>
<li>미래에 닥칠 규모 확장 요구에 대한 대처</li>
<li>필요하지만 다루지 못했떤 세부적 개선 사항들 제안</li>
</ul>
<h4 id="2-면접장에서-해야-할것">(2) 면접장에서 해야 할것</h4>
<ul>
<li>질문을 통한 확인</li>
<li>문제의 요구사항 이해</li>
<li>면접관이 내 사고의 흐름을 이해할 수 있도록 면접관과 소통</li>
<li>가능하다면 여러 해법을 함꼐 제시</li>
<li>개략적 설계에 면접관이 동의하고 난 후, 가장 중요한 컴포넌트부터 각 컴포넌트의 세부사항 설명 시작</li>
<li>면접 관의 아이디어를 이끌어 내며 마치 면접관과 팀원인 것처럼 협력</li>
</ul>
<h4 id="4-하지-말아야-할-것">(4) 하지 말아야 할 것</h4>
<ul>
<li>요구 사항이나 가정들이 분명하지 않은 상태에서 설계 제시</li>
<li>처음부터 특정 컴포넌트의 세부사항을 너무 깊이 설명하는 것<ul>
<li>개략적 설계를 마친 뒤 세부사항으로 나아갈 것</li>
</ul>
</li>
<li>진행 중에 막혔다면 힌트를 요청할 것</li>
<li>면접관의 의견을 일찍, 그리고 자주 구할 것</li>
</ul>
<h2 id="3-시간배분">3. 시간배분</h2>
<h2 id="3-1-면접-시간이-45분이라면">3-1. 면접 시간이 45분이라면?</h2>
<h3 id="1-단계별-시간-배분">(1) 단계별 시간 배분</h3>
<table>
<thead>
<tr>
<th>단계</th>
<th>시간</th>
<th>비율</th>
</tr>
</thead>
<tbody><tr>
<td>1단계: 문제 이해 및 설계 범위 확정</td>
<td>3~10분</td>
<td>약 5% ~ 20%</td>
</tr>
<tr>
<td>2단계: 개략적 설계안 제시 및 동의 구하기</td>
<td>10~15분</td>
<td>약 20% ~ 30%</td>
</tr>
<tr>
<td>3단계: 상세 설계</td>
<td>10~25분</td>
<td>약 20% ~ 55%</td>
</tr>
<tr>
<td>4단계: 마무리</td>
<td>3~5분</td>
<td>약 5% ~ 10%</td>
</tr>
</tbody></table>
<h3 id="2-단계별-시간-배분-비율45분-기준-평균-값">(2) 단계별 시간 배분 비율(45분 기준, 평균 값)</h3>
<p><img src="https://velog.velcdn.com/images/yellow-w/post/3d6befa6-5256-4659-b8f6-eaebc17a1458/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 2장. 개략적인 규모 추정]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-2%EC%9E%A5.-%EA%B0%9C%EB%9E%B5%EC%A0%81%EC%9D%B8-%EA%B7%9C%EB%AA%A8-%EC%B6%94%EC%A0%95</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-2%EC%9E%A5.-%EA%B0%9C%EB%9E%B5%EC%A0%81%EC%9D%B8-%EA%B7%9C%EB%AA%A8-%EC%B6%94%EC%A0%95</guid>
            <pubDate>Sun, 14 Sep 2025 14:34:18 GMT</pubDate>
            <description><![CDATA[<h1 id="2장-개략적인-규모추정">2장. 개략적인 규모추정</h1>
<h2 id="1-개략적인-규모-추정back-of-the-envelope-estimation">1. 개략적인 규모 추정(<code>back-of-the-envelope estimation</code>)</h2>
<ul>
<li>보편적으로 통용되는 성능 수치상에서 사고실험(<code>thought experiments</code>)을 행하여 추정치를 계산하는 행위</li>
<li>어떤 설계가 요구사항에 부합할 것인지 보기 위한 것</li>
</ul>
<h2 id="2-규모-확장성을-표현하는-데-필요한-수치들">2. 규모 확장성을 표현하는 데 필요한 수치들</h2>
<h3 id="2-1-데이터-볼륨-단위">2-1. 데이터 볼륨 단위</h3>
<table>
<thead>
<tr>
<th>단위</th>
<th>이름</th>
<th>대략적 크기</th>
<th>2의 제곱</th>
<th>비유/설명</th>
</tr>
</thead>
<tbody><tr>
<td>1 KB</td>
<td>Kilobyte</td>
<td>10³ bytes(1천)</td>
<td>10</td>
<td>작은 텍스트 파일</td>
</tr>
<tr>
<td>1 MB</td>
<td>Megabyte</td>
<td>10⁶ bytes(백만)</td>
<td>20</td>
<td>고화질 사진 1장 수준</td>
</tr>
<tr>
<td>1 GB</td>
<td>Gigabyte</td>
<td>10⁹ bytes(10억)</td>
<td>30</td>
<td>영화 파일 (저화질)</td>
</tr>
<tr>
<td>1 TB</td>
<td>Terabyte</td>
<td>10¹² bytes(1조)</td>
<td>40</td>
<td>수천 편 영화, 수억 건 로그</td>
</tr>
<tr>
<td>1 PB</td>
<td>Petabyte</td>
<td>10¹⁵ bytes(1000조)</td>
<td>50</td>
<td>대규모 데이터센터 저장 규모</td>
</tr>
</tbody></table>
<h3 id="2-2-지연-시간latency">2-2. 지연 시간(Latency)</h3>
<ul>
<li>제프 딘(Jeff Dean), 통상적인 컴퓨터에 구현된 연산들의 응답 지연 값</li>
</ul>
<h4 id="1-메모리--cpu-ns-단위">(1) 메모리 &amp; CPU (ns 단위)</h4>
<table>
<thead>
<tr>
<th>연산</th>
<th>대략적 지연</th>
</tr>
</thead>
<tbody><tr>
<td>L1 캐시 접근</td>
<td>~0.5 ns</td>
</tr>
<tr>
<td>브랜치 예측 실패</td>
<td>~5 ns</td>
</tr>
<tr>
<td>L2 캐시 접근</td>
<td>~7 ns</td>
</tr>
<tr>
<td>Mutex lock/unlock</td>
<td>~25 ns</td>
</tr>
<tr>
<td>메인 메모리 접근</td>
<td>~100 ns</td>
</tr>
</tbody></table>
<hr>
<h4 id="2-스토리지-ms-단위">(2) 스토리지 (ms 단위)</h4>
<table>
<thead>
<tr>
<th>연산</th>
<th>대략적 지연</th>
</tr>
</thead>
<tbody><tr>
<td>메모리에서 1MB 읽기</td>
<td>~0.25 ms</td>
</tr>
<tr>
<td>SSD에서 1MB 읽기</td>
<td>~1 ms</td>
</tr>
<tr>
<td>디스크 탐색(Seek)</td>
<td>~10 ms</td>
</tr>
<tr>
<td>HDD에서 1MB 읽기</td>
<td>~10 ms</td>
</tr>
</tbody></table>
<hr>
<h4 id="3-네트워크-ms-단위">(3) 네트워크 (ms 단위)</h4>
<table>
<thead>
<tr>
<th>연산</th>
<th>대략적 지연</th>
</tr>
</thead>
<tbody><tr>
<td>데이터센터 내 RPC 호출</td>
<td>~0.5 ms</td>
</tr>
<tr>
<td>데이터센터 내 네트워크 왕복</td>
<td>~0.5–1 ms</td>
</tr>
<tr>
<td>데이터센터 간 왕복 (대륙 간)</td>
<td>~100 ms</td>
</tr>
</tbody></table>
<hr>
<h4 id="4-대역폭-bandwidth">(4) 대역폭 (Bandwidth)</h4>
<table>
<thead>
<tr>
<th>리소스</th>
<th>대역폭</th>
</tr>
</thead>
<tbody><tr>
<td>메모리 읽기</td>
<td>~10 GB/s</td>
</tr>
<tr>
<td>SSD 순차 읽기</td>
<td>~1 GB/s</td>
</tr>
<tr>
<td>HDD 순차 읽기</td>
<td>~100 MB/s</td>
</tr>
<tr>
<td>데이터센터 네트워크</td>
<td>~10 Gbps (≈1.25 GB/s)</td>
</tr>
<tr>
<td>WAN (대륙 간)</td>
<td>~10–100 Mbps</td>
</tr>
</tbody></table>
<h3 id="2-3-가용성-관련-수치들">2-3. 가용성 관련 수치들</h3>
<h4 id="1-고가용성high-availability-ha">(1) 고가용성(High Availability, HA)</h4>
<ul>
<li><p>시스템이 오랜 시간 동안 지속적으로 중단 없이 운영될 수 있는 능력을 지칭하는 용어</p>
</li>
<li><p>퍼센트로 표현</p>
</li>
<li><p>대부분의 서비스는 99~100%사이의 값을 가지며, 단 한번도 중단된 적이 없었을 경우 100%</p>
</li>
<li><p>일반적인 <strong>가용성(Availability)</strong> 공식
$$
\text{가용성} = \frac{\text{총 시간} - \text{장애 시간}}{\text{총 시간}}
$$</p>
</li>
<li><p>SLA(<code>Service Level Agreement</code>)</p>
<ul>
<li><p>서비스 사업자가 보장하는 가용성 수준(<code>uptime</code>)을 계약서에 명시한 것</p>
</li>
<li><p>아마존, 구글 등의 사업자는 99% 이상의 SLA 제공</p>
</li>
<li><p>가용시간은 관습적으로 숫자 9를 사용해 표시하며, 9가 많을수록 좋음</p>
</li>
</ul>
</li>
</ul>
<h4 id="2-가용성과-다운타임-허용치">(2) 가용성과 다운타임 허용치</h4>
<table>
<thead>
<tr>
<th>가용성</th>
<th>하루당 허용 다운타임</th>
<th>주당 허용 다운타임</th>
<th>월당 허용 다운타임</th>
<th>연간 허용 다운타임</th>
</tr>
</thead>
<tbody><tr>
<td><strong>90% (1 nine)</strong></td>
<td>2.4시간</td>
<td>16.8시간</td>
<td>72시간 (3일)</td>
<td>876시간</td>
</tr>
<tr>
<td><strong>99% (2 nines)</strong></td>
<td>14.40분</td>
<td>1.68시간</td>
<td>7.31시간</td>
<td>3.65시간</td>
</tr>
<tr>
<td><strong>99.9% (3 nines)</strong></td>
<td>1.44분</td>
<td>10.08분</td>
<td>43.83분</td>
<td>8.77시간</td>
</tr>
<tr>
<td><strong>99.99% (4 nines)</strong></td>
<td>8.64초</td>
<td>1.01분</td>
<td>4.38분</td>
<td>52.60분</td>
</tr>
<tr>
<td><strong>99.999% (5 nines)</strong></td>
<td>864.00밀리초</td>
<td>6.05초</td>
<td>26.30초</td>
<td>5.26분</td>
</tr>
<tr>
<td><strong>99.9999% (6 nines)</strong></td>
<td>86.40밀리초</td>
<td>604.80밀리초</td>
<td>2.63초</td>
<td>31.56초</td>
</tr>
</tbody></table>
<h2 id="3-수치의-의미">3. 수치의 의미</h2>
<h3 id="3-1-응답-지연-시간-관련">3-1. 응답 지연 시간 관련</h3>
<ul>
<li>메모리는 디스크에 비해 빠름</li>
<li>디스크 탐색은 가능한 회피하는 것이 좋음</li>
<li>단순한 압축 알고리즘은 빠름</li>
<li>데이터를 인터넷으로 전송하기 전에 가능하면 압축할 것</li>
<li>데이터 센터는 보통 여러 지역에 분산되어 있고, 센터들 간에 데이터를 주고받는 데는 오랜 시간이 걸림</li>
</ul>
<h3 id="3-2-가용성-관련">3-2. 가용성 관련</h3>
<ul>
<li><strong>1 nine (90%)</strong>: 하루 중 <strong>2시간 이상</strong> 멈춰도 허용 → 사실상 실무 서비스 불가능</li>
<li><strong>3 nines (99.9%)</strong>: 흔히 SaaS나 클라우드 기본 SLA → 연간 <strong>9시간 이내 장애 허용</strong></li>
<li><em>5 nines (99.999%)*</em>: “Carrier Grade” → 연간 <strong>5분 이내</strong> 장애만 허용 (통신사, 금융)</li>
<li><strong>9의 개수</strong>가 하나 늘 때마다 허용 장애시간이 <strong>10배씩 줄어듦</strong></li>
</ul>
<h2 id="4-예제-트위터-qps와-저장소-요구량-추정">4. 예제: 트위터 QPS와 저장소 요구량 추정</h2>
<h3 id="4-1-가정">4-1. 가정</h3>
<ul>
<li>월간 능동 사용자는 3억명</li>
<li>50% 사용자가 트위터를 매일 사용</li>
<li>평균적으로 각 사용자는 매일 2건의 트윗 업로드</li>
<li>미디어를 포함하는 트윗은 10% 정도</li>
<li>데이터는 5년간 보관됨</li>
</ul>
<h3 id="4-2-문제">4-2. 문제</h3>
<ul>
<li>QPS 추정치</li>
<li>미디어 저장을 위한 저장소 요구량</li>
</ul>
<h3 id="4-3-풀이">4-3. 풀이</h3>
<ul>
<li>QPS 추정치<ol>
<li>일간 능동 사용자(DAU) 계산 <ul>
<li>3억 * 50% = 1.5억</li>
</ul>
</li>
<li>QPS 계산<ul>
<li>1.5억 * 2 트윗 / 24 /3600초 = 약 3,500</li>
</ul>
</li>
<li>최대 QPS(<code>peek QPS</code>) 계산<ul>
<li>2 * QPS = 약 7,000</li>
</ul>
</li>
</ol>
</li>
<li>저장소 요구량<ol>
<li>평균 트윗 크기<ul>
<li>트윗 id: 64바이트</li>
<li>텍스트: 140 바이트</li>
<li>미디어: 1MB</li>
</ul>
</li>
<li>미디어 저장소 요구량<ul>
<li>1.5억 * 2트윗 * 10% * 1MB = 30TB/일</li>
</ul>
</li>
<li>5년간 미디어를 보관하기 위한 저장소 요구량<ul>
<li>30TB/일 * 365일 * 5년 = 약 55PB</li>
</ul>
</li>
</ol>
</li>
</ul>
<h2 id="5-면접-팁">5. 면접 팁</h2>
<ul>
<li>근사치를 활용한 계산: <code>계산 결과의 정확함 &lt; 적절한 근사치 활용을 통한 시간 절약</code></li>
<li>가정들은 나중에 살펴볼 수 있도록 작성해두기</li>
<li>단위를 붙이는 습관을 들여 모호함 방지</li>
<li>자주 출제되는 문제 유형<ul>
<li>QPS 추정</li>
<li>최대 QPS 저장소 요구량 추정</li>
<li>캐시 요구량 추정</li>
<li>서버 수 추정</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 1장. 사용자 수에 따른 규모 확장성 - 3]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-1%EC%9E%A5.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5%EC%84%B1-5DB-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-1%EC%9E%A5.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5%EC%84%B1-5DB-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5</guid>
            <pubDate>Mon, 08 Sep 2025 13:02:52 GMT</pubDate>
            <description><![CDATA[<h2 id="10-db-규모-확장">10. DB 규모 확장</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th><strong>스케일-업 (Scale-Up)</strong></th>
<th><strong>샤딩 (Sharding / Scale-Out)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>개념</strong></td>
<td>단일 DB 인스턴스의 성능을 키움 (CPU, RAM, 스토리지 업그레이드)</td>
<td>데이터를 샤드 단위로 나누어 여러 노드에 분산 저장/처리</td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>단순, 강한 일관성, 트랜잭션 친화</td>
<td>수평 확장 가능, 처리량·용량 선형 증가, 장애 격리</td>
</tr>
<tr>
<td><strong>단점/약점</strong></td>
<td>하드웨어 상한선 존재, 비용 급증, 단일 장애 지점(SPOF)</td>
<td>샤딩 키 설계 복잡, 조인/트랜잭션 제약, Resharding·핫스팟 문제</td>
</tr>
<tr>
<td><strong>고려사항</strong></td>
<td>병목 진단, 레플리카·캐시 병행, 무중단 업그레이드</td>
<td>샤딩 키 선택, Celebrity/Hotspot 완화, 안정 해시·비정규화, 리밸런싱 전략</td>
</tr>
</tbody></table>
<hr>
<h3 id="10-1-수직적-확장scale-up">10-1. 수직적 확장(Scale-Up)</h3>
<h4 id="1-db-스케일업-개념">(1) DB 스케일업 개념</h4>
<ul>
<li>단일 DB 인스턴스의 <strong>하드웨어 성능을 키우는</strong> 방식</li>
<li>더 빠른 CPU, 더 많은 RAM, 더 빠른 스토리지(예: NVMe), 고사양 인스턴스로 교체</li>
</ul>
<hr>
<h4 id="2-db-스케일업-장점">(2) DB 스케일업 장점</h4>
<ul>
<li>설계·구현 단순, 애플리케이션 코드 수정 최소화</li>
<li>강한 일관성(ACID) 유지, 조인·트랜잭션 친화적</li>
<li>기존 RDB 기능 그대로 활용 가능</li>
</ul>
<hr>
<h4 id="3-db-스케일업-단점">(3) DB 스케일업 단점</h4>
<ul>
<li><strong>하드웨어 한계</strong> 존재 (성능/용량의 물리적 상한선 존재)</li>
<li>고사양 인스턴스로 갈수록 <strong>비용 급증</strong></li>
<li>단일 장애 지점(SPOF) → 장애 시 서비스 전체 중단</li>
</ul>
<hr>
<h4 id="4-db-스케일업-전략-구현-시-고려할-점">(4) DB 스케일업 전략 구현 시 고려할 점</h4>
<ul>
<li>병목 자원 정확히 진단 (CPU, 메모리, 디스크 I/O, 락 경합)</li>
<li>무중단 업그레이드 시나리오(리플리카 승격, 스토리지 분리) 마련</li>
<li>읽기 전용 레플리카, 캐시 병행 사용해 수명 연장</li>
</ul>
<hr>
<h4 id="5-db-스케일업-전략-도입-시-고려할-점">(5) DB 스케일업 전략 도입 시 고려할 점</h4>
<ul>
<li>데이터·트래픽 성장 곡선이 단일 인스턴스 범위 내에서 감당 가능한지</li>
<li>다운타임 허용도와 예산</li>
<li>팀이 단순성을 선호하고, 트랜잭션·조인이 많은 워크로드인지</li>
</ul>
<hr>
<h3 id="10-2-수평적-확장sharding--scale-out">10-2. 수평적 확장(Sharding / Scale-Out)</h3>
<h4 id="1-db-샤딩-개념">(1) DB 샤딩 개념</h4>
<ul>
<li><strong>데이터를 파티션(샤드)</strong> 으로 나누어 <strong>여러 DB 노드</strong>에 분산 저장/처리</li>
<li>각 샤드는 전체 데이터의 일부만 보유(수평 분할)</li>
<li>라우팅 키(샤딩 키)에 따라 쿼리를 특정 샤드로 전달</li>
</ul>
<hr>
<h4 id="2-db-샤딩-장점">(2) DB 샤딩 장점</h4>
<ul>
<li><strong>수평 확장성</strong>: 노드 추가로 용량·처리량이 거의 선형적으로 증가</li>
<li><strong>쓰기 처리량 개선</strong>: 여러 샤드 병렬 처리</li>
<li><strong>장애 격리</strong>: 특정 샤드 장애 시 전체 장애로 확산되지 않음</li>
</ul>
<hr>
<h4 id="3-db-샤딩-단점">(3) DB 샤딩 단점</h4>
<ul>
<li><strong>복잡성 증가</strong>: 샤딩 키 설계, 라우팅 계층, 운영 관리 모두 난이도↑</li>
<li><strong>조인·트랜잭션 제약</strong>: 크로스샤드 조인 어려움 → 애플리케이션/비정규화로 보완</li>
<li><strong>운영 부담</strong>: 백업/복구, 모니터링도 샤드 단위로 관리</li>
</ul>
<hr>
<h4 id="4-db-샤딩-약점">(4) DB 샤딩 약점</h4>
<ul>
<li><strong>Resharding</strong>: 샤드 추가·분할·병합 시 데이터 재분배 → 온라인 마이그레이션 난이도 ↑</li>
<li><strong>Celebrity Problem</strong>: 특정 샤딩 키(예: 인기 연예인 ID)에 트래픽 집중 → 특정 샤드 과부하</li>
<li><strong>Hotspot Key</strong>: 특정 범위/키에만 쓰기 집중 → 불균형 발생</li>
<li><strong>샤드 소진</strong>: 균등 분할했으나 특정 샤드가 먼저 용량 한계 → 리밸런싱 필요(안정 해시(Consistent Hashing)기법 활용해 리밸런싱 비용 최소화)</li>
<li><strong>조인 문제</strong>: 크로스샤드 조인 시 성능 저하 → <strong>비정규화/중복 데이터</strong>로 대체</li>
</ul>
<hr>
<h4 id="5-db-샤딩-전략-구현-시-고려할-점">(5) DB 샤딩 전략 구현 시 고려할 점</h4>
<ul>
<li><strong>샤딩 키 설계</strong>: 고카디널리티, 균등 분포, 액세스 패턴 친화적<ul>
<li>Celebrity/Hotspot 방지: 해시 샤딩, 샤딩 키 솔팅 활용</li>
</ul>
</li>
<li><strong>Resharding 계획</strong>: 온라인 재분배, 더블라이트(이중 쓰기), 안정 해시 적용</li>
<li><strong>크로스샤드 트랜잭션 회피</strong>: 필요 시 2PC(이중 커밋), 사가(Saga) 패턴</li>
<li><strong>데이터 모델링</strong>: 조인 최소화 → 비정규화, CQRS/Event Sourcing 병행</li>
<li><strong>관측</strong>: 샤드별 모니터링(QPS, 지연, 히트율, 용량), 핫 샤드 탐지</li>
</ul>
<hr>
<h4 id="6-db-샤딩-전략-도입-시-고려할-점">(6) DB 샤딩 전략 도입 시 고려할 점</h4>
<ul>
<li>데이터·트래픽 성장 곡선 (단일 노드 한계 도달 시점)</li>
<li>팀의 운영 역량 (샤딩 운영 복잡성 감당 가능 여부)</li>
<li>일관성 요구 수준 (Strong vs Eventual Consistency)</li>
<li>비용 모델 (고성능 단일 인스턴스 vs 다수 중소형 노드)</li>
<li>지리적 분산 요구 (멀티 리전/글로벌 서비스라면 샤딩 적합)</li>
</ul>
<hr>
<h2 id="시스템-규모-확장을-위한-기법들">시스템 규모 확장을 위한 기법들</h2>
<blockquote>
<p>시스템의 규모를 확장하는 것은 지속적이고 반복적(<code>iterative</code>)인 과정이다</p>
<p>수백만 사용자 이상을 지원하려면 새로운 전략을 도입해야 하고 지속적으로 시스템을 가다듬어야 할 것이다</p>
</blockquote>
<h4 id="--웹-계층-무상태">- 웹 계층: 무상태</h4>
<h4 id="--모든-계층에-다중화-도입">- 모든 계층에 다중화 도입</h4>
<h4 id="--가능한-한-한-많은-데이터-캐싱">- 가능한 한 한 많은 데이터 캐싱</h4>
<h4 id="--여러-데이터-센터-지원">- 여러 데이터 센터 지원</h4>
<h4 id="--정적-콘텐츠은-cdn을-통해-서비스">- 정적 콘텐츠은 CDN을 통해 서비스</h4>
<h4 id="--데이터-계층은-샤딩을-통해-규모-확장">- 데이터 계층은 샤딩을 통해 규모 확장</h4>
<h4 id="--각-계층은-독립적-서비스로-분할">- 각 계층은 독립적 서비스로 분할</h4>
<h4 id="--시스템의-지속적인-모니터링-및-자동화-도구-활용">- 시스템의 지속적인 모니터링 및 자동화 도구 활용</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 1장. 사용자 수에 따른 규모 확장성 - 2
]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-1%EC%9E%A5.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5%EC%84%B1-3-%EC%9B%B9%EA%B3%84%EC%B8%B5-DC</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-1%EC%9E%A5.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5%EC%84%B1-3-%EC%9B%B9%EA%B3%84%EC%B8%B5-DC</guid>
            <pubDate>Mon, 08 Sep 2025 12:52:55 GMT</pubDate>
            <description><![CDATA[<h2 id="6-무상태statetless-웹-계층">6. 무상태(statetless) 웹 계층</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>상태 의존적 아키텍처 (Stateful Web Architecture)</th>
<th>무상태 아키텍처 (Stateless)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>개념</strong></td>
<td>웹 서버 인스턴스가 <strong>사용자 세션/상태 정보를 자체 메모리나 로컬 스토리지에 보관</strong>하는 방식</td>
<td>웹 서버는 상태를 저장하지 않고, <br />세션/상태는 외부 저장소(세션 스토어, 캐시, DB, JWT 등)에 위임</td>
</tr>
<tr>
<td><strong>요청 처리</strong></td>
<td>같은 사용자가 항상 같은 서버에 연결되어야 함<br />→ 흔히 <strong>스티키 세션(Sticky Session)</strong> 로드밸런싱과 함께 사용됨</td>
<td>모든 요청이 독립적 <br />→ 어떤 서버가 처리해도 동일 결과 반환</td>
</tr>
<tr>
<td><strong>특징</strong></td>
<td>각 서버가 <strong>사용자 세션을 직접 관리</strong> → 사용자별 상태 의존성 존재<br />특정 서버 장애 시, 해당 서버의 세션 정보는 유실될 수 있음<br />서버 수평 확장 시 <strong>세션 불균형</strong> 또는 <strong>마이그레이션 문제</strong> 발생</td>
<td><strong>Share-nothing</strong> 원칙: 서버 인스턴스 간 상태 공유 필요 없음<br />로드밸런서가 자유롭게 요청을 분산 가능<br />서버 확장/축소 용이</td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>구현 단순, 빠른 개발<br />추가 인프라 불필요</td>
<td><strong>확장성 우수</strong>: 인스턴스를 자유롭게 증감 가능<br /><strong>가용성 높음</strong>: 특정 서버 장애에도 서비스 지속<br /><strong>배포 용이</strong>: 롤링/블루-그린/카나리 배포 유리</td>
</tr>
<tr>
<td><strong>단점</strong></td>
<td><strong>확장성 제한</strong>: 특정 서버에 세션 집중 시 부하 불균형<br /><strong>가용성 낮음</strong>: 서버 장애 시 세션 유실<br /><strong>배포 어려움</strong>: 롤링 업데이트 시 세션 마이그레이션 필요</td>
<td>초기 구현 시 <strong>외부 세션 스토어/토큰 관리</strong> 등 아키텍처 복잡성 증가<br />네트워크 I/O 부하 (매 요청마다 외부 스토어 접근 시)</td>
</tr>
</tbody></table>
<h3 id="6-1-상태-의존적-웹-아키텍처">6-1. 상태 의존적 웹 아키텍처</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/397fb8b6-cdfd-46e1-90e1-276daed1165b/image.png" alt="설명" class="left-block-image" />

<hr>
<h3 id="6-2-무상태-웹-아키텍처">6-2. 무상태 웹 아키텍처</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/39e77dee-3eab-4ae7-8a8d-4ead69abf5c5/image.png" alt="설명" class="left-block-image" />

<hr>
<h3 id="참고---배포방식">참고 - 배포방식</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>카나리</th>
<th>롤링</th>
<th>블루-그린</th>
</tr>
</thead>
<tbody><tr>
<td>전환 방식</td>
<td><strong>일부 트래픽(예: 1~10%)만 새 버전</strong>으로 보내서 위험을 낮추는 점진 배포</td>
<td>같은 풀의 인스턴스를 <strong>조금씩 교체</strong>해 전체를 새 버전으로 전환</td>
<td><strong>두 개의 완전한 환경(Blue=현재, Green=새 버전)</strong>을 준비해 두고 <strong>스위치(트래픽 전환)</strong>로 교체</td>
</tr>
<tr>
<td>장점</td>
<td>실사용자 트래픽으로 조기 검증<br />문제 시 빠른 롤백</td>
<td>무중단에 가깝고 인프라 비용 추가 적음</td>
<td>즉시 전환과 즉시 롤백 용이<br />배포 위험 최소화</td>
</tr>
<tr>
<td>단점</td>
<td>트래픽 라우팅<br />모니터링 복잡도 증가</td>
<td>상태 의존 구조에선 드레이닝/세션 처리 필요<br />전체 전환 시간 길 수 있음</td>
<td>두 환경 동시 유지 비용↑<br />데이터/상태 동기화 고려 필요</td>
</tr>
</tbody></table>
<hr>
<h3 id="6-3-최종-설계">6-3. 최종 설계</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/a3cff98d-da39-43f2-8492-44eec3ca2276/image.png" alt="설명" class="left-block-image" />

<hr>
<h2 id="7-데이터-센터">7. 데이터 센터</h2>
<h3 id="7-1-데이터-센터-dc-data-center">7-1. 데이터 센터 (DC; Data Center)</h3>
<h4 id="1-정의">(1) 정의</h4>
<ul>
<li>서버, 스토리지, 네트워크 장비, 전력/냉각 인프라를 집약해 대규모 IT 서비스를 운영하는 시설</li>
</ul>
<h4 id="2-역할">(2) 역할</h4>
<ul>
<li>대규모 서비스의 <strong>안정성·확장성·보안</strong> 확보</li>
<li>클라우드(IaaS/PaaS/SaaS) 기반 물리적 토대</li>
</ul>
<h4 id="3-특징">(3) 특징</h4>
<ul>
<li><strong>이중화</strong>(전력, 네트워크, 스토리지 등)</li>
<li><strong>보안성</strong>(물리/네트워크/운영)</li>
<li><strong>확장성</strong>(랙 단위/리전 단위 확장)</li>
</ul>
<h4 id="4-실제-예시">(4) 실제 예시</h4>
<ul>
<li>구글 클라우드 리전 (미국·유럽·아시아) → YouTube, Gmail 서비스 기반</li>
<li>AWS 서울 리전(ap-northeast-2) → 쿠팡, 네이버 일부 서비스 운영</li>
<li>네이버 데이터 센터 ‘각’ (춘천) → 친환경 냉각 활용</li>
<li>카카오 데이터 센터 ‘안산’ → 카카오톡, 카카오뱅크 인프라 운영</li>
</ul>
<hr>
<h3 id="7-2-지리적-라우팅-geo-routing--geo-dns-routing">7-2. 지리적 라우팅 (Geo Routing / Geo DNS Routing)</h3>
<h4 id="1-geo-routing-정의">(1) Geo Routing 정의</h4>
<ul>
<li>사용자의 지리적 위치(IP, DNS 질의 위치)에 따라 <strong>가장 가까운 데이터 센터 또는 CDN PoP</strong>로 트래픽을 분산시키는 기법.</li>
</ul>
<h4 id="2-geo-routing-방식">(2) Geo Routing 방식</h4>
<ul>
<li><strong>GeoDNS</strong>: DNS 레벨에서 질의 위치 기반으로 IP 응답</li>
<li><strong>Anycast</strong>: 동일 IP를 여러 지역에서 광고 → 라우팅 프로토콜(BGP)이 가장 가까운 경로 선택</li>
</ul>
<h4 id="3-geo-routing-목적">(3) Geo Routing 목적</h4>
<ul>
<li>지연(latency) 최소화</li>
<li>지역별 트래픽 부하 분산</li>
<li>특정 리전 장애 시 자동 우회 (Failover)</li>
</ul>
<h4 id="4-실제-예시-1">(4) 실제 예시</h4>
<ul>
<li>Cloudflare Anycast 네트워크 → 전 세계 어디서든 동일 IP로 접속 시 가까운 POP 응답</li>
<li>AWS Route 53 → 서울 사용자 요청은 서울 리전, 미국 사용자는 버지니아 리전으로 라우팅</li>
</ul>
<hr>
<h3 id="7-3-다중-데이터-센터-아키텍처-multi-data-center-architecture">7-3. 다중 데이터 센터 아키텍처 (Multi-Data Center Architecture)</h3>
<h4 id="1-active-passive">(1) Active-Passive</h4>
<ul>
<li>하나의 센터만 Active, 다른 하나는 대기 상태</li>
<li>장애 시 Failover</li>
<li><strong>예시</strong>: 금융권 DR 센터(재해복구 전용 데이터 센터)</li>
</ul>
<h4 id="2-active-active">(2) Active-Active</h4>
<ul>
<li>여러 데이터 센터가 동시에 트래픽 처리</li>
<li>지리적 라우팅으로 사용자 근처 센터에 연결</li>
<li><strong>예시</strong>: Netflix → AWS 다중 리전에서 동시에 스트리밍 서비스 제공</li>
</ul>
<h4 id="3-hybrid">(3) Hybrid</h4>
<ul>
<li>읽기 요청은 지역별 센터에서 처리, 쓰기 요청은 특정 리전으로 집중</li>
<li><strong>예시</strong>: 글로벌 전자상거래 서비스(상품 조회는 현지 리전, 결제는 중앙 리전)</li>
</ul>
<hr>
<h3 id="7-3-고려사항--기술적-난제">7-3. 고려사항 &amp; 기술적 난제</h3>
<h4 id="1-트래픽-우회-traffic-routing--failover">(1) 트래픽 우회 (Traffic Routing &amp; Failover)</h4>
<h5 id="고려할-점">고려할 점</h5>
<ul>
<li><p>사용자를 어떤 데이터센터(리전)으로 라우팅할 것인가?</p>
<p>장애 발생 시, 다른 센터로 <strong>신속하게 우회(Failover)</strong> 가능한가?</p>
<p>정상 복구(Failback) 시 데이터 일관성을 어떻게 맞출 것인가?</p>
</li>
</ul>
<h5 id="기술적-난제">기술적 난제</h5>
<ul>
<li><p><strong>DNS 기반 라우팅</strong>: TTL 캐싱으로 인해 우회 반영 지연</p>
<p><strong>GeoDNS</strong>: 단순히 지리적 위치만 고려 → 실제 부하/장애 반영 한계</p>
<p><strong>Anycast</strong>: 가장 가까운 경로 선택은 가능하나, 세밀한 트래픽 제어 불가</p>
<p><strong>실시간 헬스체크 + 글로벌 로드밸런서</strong> 필요 → 구현 복잡</p>
</li>
</ul>
<hr>
<h4 id="2-데이터-동기화-data-synchronization">(2) 데이터 동기화 (Data Synchronization)</h4>
<h5 id="고려할-점-1">고려할 점</h5>
<ul>
<li><p>여러 데이터센터에 걸친 <strong>DB·캐시·세션</strong>을 어떻게 동기화할 것인가?</p>
<p><strong>강한 일관성</strong>을 보장할지, <strong>최종 일관성</strong>을 허용할지?</p>
<p>충돌(conflict) 발생 시 해결 방법은 무엇으로 할지?</p>
</li>
</ul>
<h5 id="기술적-난제-1">기술적 난제</h5>
<ul>
<li><strong>관계형 DB</strong>: 글로벌 트랜잭션 동기화 시 지연(latency) 증가, 성능 저하</li>
<li><strong>NoSQL / 분산 DB</strong>: 최종 일관성(Eventual Consistency) 허용 시 충돌 발생 가능</li>
<li><strong>세션/캐시</strong>: 리전 분리 시 단순하지만, 글로벌 공유는 복잡도↑</li>
<li><strong>Conflict Resolution</strong> 필요 → Last Writer Wins, CRDT, Vector Clock 등</li>
</ul>
<hr>
<h4 id="3-테스트--배포-testing--deployment">(3) 테스트 &amp; 배포 (Testing &amp; Deployment)</h4>
<h5 id="고려할-점-2">고려할 점</h5>
<ul>
<li>새 버전 배포 시, 모든 리전에 동시에 적용해야 할까?</li>
<li>일부 리전에만 배포해 안전성을 검증할 수 있을까? (카나리 방식)</li>
<li>다중 리전 배포 실패 시 <strong>롤백 전략</strong>은 무엇인가?</li>
</ul>
<h5 id="기술적-난제-2">기술적 난제</h5>
<ul>
<li><strong>배포 동기화</strong>: 모든 리전에 동일 버전·설정을 유지하는 것 자체가 어려움</li>
<li><strong>카나리 배포</strong>: 특정 리전(일부 사용자 그룹)에만 새 버전 적용 → 글로벌 환경에서 트래픽 분리 어려움</li>
<li><strong>블루-그린</strong>: 리전 단위 환경 전환은 가능하나, 비용·운영 부담 큼</li>
<li><strong>데이터 스키마 변경</strong>: 순차 적용 과정에서 리전 간 불일치 발생 가능</li>
</ul>
<hr>
<h2 id="8-메세지-큐">8. 메세지 큐</h2>
<hr>
<h3 id="8-1-개념">8-1. 개념</h3>
<ul>
<li><strong>메세지의 무손실(durability)을 보장하는, 프로세스나 서비스 간의 비동기 통신(<code>Asynchronous Communication</code>)을 지원하는 소프트웨어 컴포넌트</strong></li>
<li>생산자(Producer)가 메시지를 큐에 넣고, 소비자(Consumer)가 이를 꺼내 처리하는 구조</li>
</ul>
<hr>
<h3 id="8-2-역할">8-2. 역할</h3>
<table>
<thead>
<tr>
<th>역할</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>비동기 처리 지원</strong></td>
<td>요청을 즉시 처리하지 않고 큐에 적재 → 나중에 처리 가능<br />생산자/소비자 간 실행 속도 차이를 완화</td>
</tr>
<tr>
<td><strong>시스템 간 결합도 감소 (Decoupling)</strong></td>
<td>Producer와 Consumer가 직접 연결되지 않고 큐를 매개로 통신<br />→ 서비스 간 의존성 완화, 독립성 향상</td>
</tr>
<tr>
<td><strong>버퍼링 (Buffering)</strong></td>
<td>갑작스러운 요청 폭주 시 큐가 완충 역할<br />→ 소비자는 처리 가능한 속도에 맞춰 꺼내 처리</td>
</tr>
<tr>
<td><strong>확장성 (Scalability)</strong></td>
<td>소비자 인스턴스를 수평 확장하면 큐의 메시지를 병렬 처리 가능</td>
</tr>
<tr>
<td><strong>내결함성 (Fault Tolerance)</strong></td>
<td>소비자가 일시적으로 죽어도 메시지는 큐에 남아 손실되지 않음<br />→ 장애 복구 후 다시 처리 가능</td>
</tr>
</tbody></table>
<hr>
<h3 id="8-3-작동-방식-흐름">8-3. 작동 방식 (흐름)</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/e8f35732-80a4-4298-a7b9-483e1f91aff0/image.png" alt="설명" class="left-block-image" />



<ol>
<li><strong>생산자(Producer)</strong><ul>
<li>처리할 작업/데이터를 메시지 형태로 큐에 전송(Publish/Send)</li>
</ul>
</li>
<li><strong>메세지 큐 (Broker)</strong><ul>
<li>메시지를 안전하게 저장</li>
<li>메시지 전달 보장 방식 적용 (At-most-once, At-least-once, Exactly-once)</li>
<li>메시지를 Consumer에게 전달</li>
</ul>
</li>
<li><strong>소비자(Consumer)</strong><ul>
<li>큐에서 메시지를 가져와 처리(Subscribe/Consume)</li>
<li>처리 완료 후 ACK(확인 응답)를 보내면 큐에서 메시지 삭제</li>
</ul>
</li>
</ol>
<hr>
<h2 id="9-로그-메트릭-그리고-자동화">9. 로그, 메트릭 그리고 자동화</h2>
<hr>
<h3 id="9-1-사업-규모-확장-시-투자해야-할-도구들">9-1. 사업 규모 확장 시 투자해야 할 도구들</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>사업 규모가 커질 때</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td><strong>로그 (사건 기록)</strong></td>
<td>서버 수십·수백 대 → 개별 확인 불가<br />중앙화·구조화 필요 (JSON 로그, ELK, Loki)<br />운영자/개발자가 공통 포맷으로 조회</td>
<td>단일 관찰점(Single Pane of Glass) 확보 필수<br />오류 분석, 보안 감사, SLA 추적 근거</td>
</tr>
<tr>
<td><strong>메트릭 (상태 지표)</strong></td>
<td><strong>호스트 단위 메트릭</strong>: 리소스 사용률(여전히 필요)<br /><strong>종합 메트릭</strong>: 응답 지연(p95/p99), 오류율, 트래픽, 포화도(Saturation)<br /> <strong>핵심 비즈니스 메트릭</strong>: 주문 성공률, 결제 실패율, DAU/MAU, 전환율 등<br /> SLO 기반 알림 체계 도입</td>
<td>로그는 “사후 분석”, 메트릭은 “사전 감지/예방”에 유리<br />대규모 환경에서는 <strong>서비스 지표+비즈니스 지표</strong>가 시스템 신뢰성과 직접 연결</td>
</tr>
<tr>
<td><strong>자동화 (운영 지속가능성)</strong></td>
<td>자동화된 배포 전략 필요 (카나리, 롤링, 블루-그린)<br />메트릭 기반 알람 → 런북 자동 실행 (스케일아웃, 재시도, 롤백)<br />IaC(Infrastructure as Code) 도입으로 환경 일관성 확보</td>
<td>운영자가 병목이 되지 않도록<br />시스템이 스스로 회복(Self-Healing)·확장(Auto-Scaling)해야 지속가능</td>
</tr>
</tbody></table>
<hr>
<h3 id="9-2-메세지-큐-로그-메트릭-자동화-등을-반영하여-수정한-최종-설계">9-2. 메세지 큐, 로그, 메트릭, 자동화 등을 반영하여 수정한 최종 설계</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/a835c11b-a11b-485a-af71-a17c8765fc00/image.png" alt="설명" class="left-block-image" />


<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템 디자인] 1장. 사용자 수에 따른 규모 확장성 - 1 ]]></title>
            <link>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-1%EC%9E%A5.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5%EC%84%B1-1</link>
            <guid>https://velog.io/@yellow-w/%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%94%94%EC%9E%90%EC%9D%B8-1%EC%9E%A5.-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B7%9C%EB%AA%A8-%ED%99%95%EC%9E%A5%EC%84%B1-1</guid>
            <pubDate>Mon, 08 Sep 2025 12:38:49 GMT</pubDate>
            <description><![CDATA[<h2 id="1-단일-서버">1. 단일 서버</h2>
<ul>
<li>모든 컴포넌트가 하나의 서버에서 실행되는 간단한 시스템 설계</li>
<li>웹, 앱, DB, 캐시 등이 전부 서버 한 대에서 실행됨</li>
</ul>
<h3 id="1-1-사용자-요청이-처리되는-과정">1-1. 사용자 요청이 처리되는 과정</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/a95ea1ab-f2de-47f2-be92-6eb75fb4613f/image.png" alt="설명" class="left-block-image" />


<hr>
<h2 id="2-db">2. DB</h2>
<h3 id="2-1-db-분류와-선택-기준">2-1. DB 분류와 선택 기준</h3>
<h4 id="1-관계형-db와-비관계형-db-비교">(1) 관계형 DB와 비관계형 DB 비교</h4>
<table>
<thead>
<tr>
<th>관계형(R-DBMS)</th>
<th>비관계형(NoSQL)</th>
</tr>
</thead>
<tbody><tr>
<td>MySQL, 오라클, postgreSQL</td>
<td>CouchDb, Cassandra, Amazon Dynamo DB</td>
</tr>
<tr>
<td>자료를 테이블, 열, 칼럼으로 표현</td>
<td>조인 연산 지원 X</td>
</tr>
</tbody></table>
<hr>
<h4 id="2-비관계형-dbno-sql">(2) 비관계형 DB(No SQL)</h4>
<h5 id="no-sql의-분류">No SQL의 분류</h5>
<ul>
<li>키-값 저장소(key-value store)</li>
<li>그래프 저장소(graph store)</li>
<li>칼럼 저장소(column store)</li>
<li>문서 저장소(document store)</li>
</ul>
<h5 id="no-sql이-바람직한-경우">No SQL이 바람직한 경우</h5>
<ul>
<li>아주 낮은 응답 지연 시간이 요구됨</li>
<li>다루는 데이터가 관계형 데이터가 아닌 비정형 데이터인 경우</li>
<li>데이터를 직렬화(<code>serialize</code>)/역직렬화(<code>deserialize</code>) 할 수 있기만 하면 됨</li>
<li>대량의 데이터를 저장하는 경우</li>
</ul>
<hr>
<h2 id="3-규모-확장의-방향수직적수평적">3. 규모 확장의 방향(수직적/수평적)</h2>
<h3 id="3-1-수직적-규모-확장스케일-업-vs-수평적-규모-확장스케일-아웃">3-1. 수직적 규모 확장(스케일 업) VS 수평적 규모 확장(스케일 아웃)</h3>
<table>
<thead>
<tr>
<th>스케일업(수직적 규모 확장)</th>
<th>스케일 아웃(수평적 규모 확장)</th>
</tr>
</thead>
<tbody><tr>
<td>서버에 고사양 자원을 추가</td>
<td>더 많은 서버를 추가하여 성능 개선</td>
</tr>
<tr>
<td>서버로 유입되는 트래픽의 양이 적은 경우</td>
<td>대규모 앱에 적합</td>
</tr>
</tbody></table>
<h4 id="스케일-업의-단점">스케일 업의 단점</h4>
<ul>
<li>규모 확장에 한계 존재</li>
<li>장애에 대한 자동 복구(<code>failover</code>) 방안이나 다중화(<code>redundancy</code>) 방안 제시 X<ul>
<li>즉, 서버에 장애 발생 시 웹/앱은 완전히 중단됨</li>
</ul>
</li>
</ul>
<hr>
<h3 id="3-2-로드-밸런서">3-2. 로드 밸런서</h3>
<ul>
<li>부하 분산 집합(<code>load balancing set</code>)에 속한 웹 서버들에게 트래픽 부하를 고르게 분산하는 역할</li>
</ul>
<h4 id="1-사용자-요청이-처리되는-과정">(1) 사용자 요청이 처리되는 과정</h4>
<img src="https://velog.velcdn.com/images/yellow-w/post/a583d354-3c86-4646-8d24-54058d2de7a3/image.png" alt="설명" class="left-block-image" />

<ol>
<li>사용자는 로드밸런서의 공개 IP 주소로 접속</li>
<li>서버 간 통신에는 사설 IP 주소가 이용됨</li>
</ol>
<h4 id="2-웹-계층의-가용성-향상">(2) 웹 계층의 가용성 향상</h4>
<ul>
<li>서버 1 다운 시, 모든 트래픽은 서버 2로 전송</li>
<li>웹 사이트로 유입되는 트개픽 증가 시 웹 서버 계층에 더 많은 서버만 추가하면 로드 밸런서가 자동으로 트래픽 분산 시작</li>
</ul>
<hr>
<h3 id="3-3-데이터-다중화">3-3. 데이터 다중화</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/5d969253-0b2d-4fcd-902f-bf24b2103d40/image.png" alt="설명" class="left-block-image" />

<ul>
<li>보통 서버 사이에 주(<code>master</code>)-부(<code>slave</code>) 관계를 설정하고 데이터 원본은 주 서버, 사본은 부 서버에 저장하는 방식(Master-Slave 패턴)<ul>
<li>쓰기 연산은 마스터에서만 지원</li>
<li>부 DB는 마스터로부터 사본을 전달받으며, 읽기 연산만 지원</li>
</ul>
</li>
<li>대부분의 앱에서는 읽기 연산 비중 &gt; 쓰기 연산 비중 → 부 DB 수 &gt; 주 DB 수</li>
</ul>
<h4 id="1-데이터-다중화의-이점">(1) 데이터 다중화의 이점</h4>
<table>
<thead>
<tr>
<th>이점</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>더 나은 성능</td>
<td>병렬로 처리될 수 있는 query 수 증가 &gt; 성능이 좋아짐</td>
</tr>
<tr>
<td>안정성(<code>reliability</code>)</td>
<td>서버 중 일부에 문제가 생겨도 데이터 보존됨</td>
</tr>
<tr>
<td>가용성(<code>availability</code>)</td>
<td>서버 중 이룹에 문제가 생겨도, 다른 서버에 있는 데이터를 가져와서 계속 서비스 할 수 있음</td>
</tr>
</tbody></table>
<h4 id="2-데이터-다중화-아키텍처-패턴">(2) 데이터 다중화 아키텍처 패턴</h4>
<ul>
<li><strong>Master–Slave</strong>: 단일 쓰기 지점, 읽기 분산 (구현 단순, 장애 시 Failover 필요)</li>
<li><strong>Multi-Master</strong>: 모든 리전 쓰기 가능 (충돌 해결 필요, 고난도)</li>
<li><strong>Hybrid</strong>: 읽기는 로컬, 쓰기는 중앙 집중 (지연 최소화, 복잡도 증가)</li>
</ul>
<h4 id="3-데이터-복제-방식">(3) 데이터 복제 방식</h4>
<ul>
<li><strong>동기(Sync)</strong>: 모든 리전에 쓰기 반영 후 확정 → 일관성↑, 지연↑</li>
<li><strong>비동기(Async)</strong>: 로컬만 확정, 다른 리전은 나중에 반영 → 성능↑, 최종 일관성</li>
<li><strong>반동기(Semi-sync/Quorum)</strong>: 일부 리전만 확인 후 확정 → 절충안</li>
</ul>
<hr>
<h3 id="3-4-최종-설계">3-4. 최종 설계</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/b62a347d-08e5-423b-8460-2fe2b6d043dd/image.png" alt="설명" class="left-block-image" />


<ol>
<li>사용자는 DNS로부터 로드밸런서의 공개 IP를 응답 받음</li>
<li>사용자는 IP 주소를 사용해 로드밸런서 접속</li>
<li>HTTP 요청이 서버 1 또는 서버 1로 전달됨</li>
<li>웹 서버는 사용자 데이터를 부 DB 서버로부터 읽음</li>
<li>웹 서버는 데이터 변경 연산은 주 DB로 전달</li>
</ol>
<hr>
<h2 id="4-캐시응답시간-개선">4. 캐시(응답시간 개선)</h2>
<h3 id="4-1-캐시의-정의">4-1. 캐시의 정의</h3>
<ul>
<li>자주 접근되거나 비용이 큰 데이터를 더 빠른 저장소에 일시적으로 보관해 두어 성능을 높이고 원본 자원(DB, API 등)의 부하를 줄이는 계층</li>
</ul>
<hr>
<h3 id="4-2-캐시의-목적">4-2. 캐시의 목적</h3>
<ul>
<li>성능 개선</li>
<li>DB 부하 감소</li>
<li>비용 절감</li>
<li>확장성 확보</li>
</ul>
<hr>
<h3 id="4-3-캐시의-핵심-속성">4-3. 캐시의 핵심 속성</h3>
<h4 id="1-임시-저장소-temporary-storage">(1) 임시 저장소 (Temporary Storage)</h4>
<ul>
<li>캐시는 <strong>데이터의 사본</strong>을 보관할 뿐, 원본 저장소(DB, 파일, API)가 따로 존재</li>
<li>따라서 캐시 데이터는 언제든 무효화(invalidate)되거나 손실될 수 있음</li>
</ul>
<h4 id="2-빠른-접근-low-latency">(2) 빠른 접근 (Low Latency)</h4>
<ul>
<li>목표: 원본보다 빠른 접근</li>
<li>예: 메모리(RAM)에 데이터를 두어 디스크 I/O를 피함, 가까운 지역(에지)에 두어 네트워크 왕복을 줄임</li>
</ul>
<h4 id="3-투명성-transparency">(3) 투명성 (Transparency)</h4>
<ul>
<li>이상적으로는 애플리케이션이 캐시 유무를 의식하지 않고도 동작할 수 있어야 함</li>
<li>다만 Cache-Aside 패턴처럼 앱에서 직접 제어하는 경우도 많음</li>
</ul>
<h4 id="4-일관성-문제-consistency-trade-off">(4) 일관성 문제 (Consistency Trade-off)</h4>
<ul>
<li>캐시가 원본 DB와 항상 동일하다고 보장되지는 않음</li>
<li>TTL(Time-to-Live), 무효화 정책, 쓰기 전략(Write-through, Write-back 등)을 통해 균형을 맞추어야 함</li>
</ul>
<hr>
<h3 id="4-4-캐시-전략">4-4. 캐시 전략</h3>
<table>
<thead>
<tr>
<th>전략</th>
<th>동작 방식</th>
<th>장점</th>
<th>단점</th>
<th>사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Cache-Aside (Lazy Loading)</strong></td>
<td>앱이 먼저 캐시 확인 → 없으면 DB 조회 후 캐시에 저장</td>
<td>구현 간단, 캐시 효율적 사용</td>
<td>첫 요청은 항상 DB 조회(캐시 미스)<br />쓰기 직후 캐시 불일치 발생</td>
<td>일반적인 웹 서비스<br />(뉴스, 게시판, 상품 조회)</td>
</tr>
<tr>
<td><strong>Read-Through</strong></td>
<td>캐시가 DB와 직접 연동해 미스 시 자동 적재</td>
<td>앱이 단순해짐, 일관성 ↑</td>
<td>캐시 구현 복잡<br />캐시가 DB 클라이언트 역할까지 담당해야 함<br />캐시에 부하 집중</td>
<td>CDN, 전용 캐시 계층</td>
</tr>
<tr>
<td><strong>Write-Through</strong></td>
<td>쓰기 시 캐시에 먼저 기록 → 캐시가 DB에 반영</td>
<td>항상 최신 데이터<br />읽기 일관성 보장</td>
<td>쓰기 지연, 불필요한 캐시 적재 가능</td>
<td>사용자 세션, 장바구니</td>
</tr>
<tr>
<td><strong>Write-Back (Write-Behind)</strong></td>
<td>쓰기 시 캐시에만 기록 <br />→ 일정 주기/조건에 따라 DB에 비동기 반영</td>
<td>쓰기 성능 극대화, DB 부하 감소</td>
<td>캐시 장애 시 데이터 유실 위험 <br />→ <strong>내구성 확보(예: AOF, RDB 스냅샷)</strong> 필요<br />구현 복잡</td>
<td>로그 수집, 이벤트 처리</td>
</tr>
<tr>
<td><strong>Write-Around</strong></td>
<td>쓰기 시 DB에만 기록, 읽을 때 캐시 적재</td>
<td>캐시에 자주 쓰이지 않는 데이터 최소화</td>
<td>방금 쓴 데이터는 캐시에 없음(읽기 미스)</td>
<td>잘 안 읽히는 데이터 많은 경우</td>
</tr>
<tr>
<td><strong>TTL 기반 캐싱</strong></td>
<td>캐시 항목마다 만료 시간 지정</td>
<td>데이터 신선도 보장, 자동 갱신</td>
<td>TTL 지나기 전까지는 구버전 가능, 적절한 TTL 설정 난이도</td>
<td>날씨, 주식 시세, API 응답 캐싱</td>
</tr>
</tbody></table>
<hr>
<h3 id="4-5-읽기-주도형-캐시전략read-through-caching-strategy">4-5. 읽기 주도형 캐시전략(read-through caching strategy)</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/5244270f-8f83-45ca-b8a0-8eb4bc42edf2/image.png" alt="설명" class="left-block-image" />


<hr>
<h3 id="4-6-캐시-사용-시-고려할-점">4-6. 캐시 사용 시 고려할 점</h3>
<h4 id="1-캐싱-적합성">(1) 캐싱 적합성</h4>
<ul>
<li><p><strong>캐시하기 적합한 데이터</strong></p>
<ul>
<li>자주 조회되는 데이터(Hot Data) → 반복 접근으로 히트율↑</li>
<li>조회 비용이 큰 데이터 → DB I/O, CPU 사용량 절감 효과 큼
(예: 복잡한 조인, 대규모 집계, 외부 API 호출 결과)</li>
</ul>
<p><strong>캐싱이 부적합한 데이터</strong></p>
<ul>
<li>항상 최신 상태가 보장되어야 하는 데이터 (예: 은행 잔액, 실시간 재고)
→ 캐시 지연으로 인한 불일치 허용 불가</li>
<li>영구적으로 보관해야 하는 데이터
→ 캐시는 본질적으로 <strong>임시 저장소</strong>이므로 적합하지 않음</li>
</ul>
</li>
</ul>
<h4 id="2-만료무효화-정책">(2) 만료/무효화 정책</h4>
<ul>
<li><strong>TTL(Time-To-Live) 설정</strong><ul>
<li>짧은 TTL: 데이터 신선도 보장 ↑, 하지만 캐시 미스율↑ → DB 부하 증가</li>
<li>긴 TTL: 히트율↑, DB 부하 ↓, 하지만 오래된(stale) 데이터 제공 가능성↑</li>
<li>핵심: <em>데이터 신선도 vs 효율성</em>의 트레이드오프</li>
</ul>
</li>
</ul>
<h4 id="3-일관성-유지">(3) 일관성 유지</h4>
<ul>
<li><p><strong>일관성 모델</strong></p>
<ul>
<li>최종 일관성(Eventual Consistency): 대부분 서비스에서 허용 가능</li>
<li>강한 일관성(Strong Consistency): 일부 요청은 반드시 DB에서 조회 필요</li>
</ul>
<p><strong>불일치 가능성</strong></p>
<ul>
<li>DB와 캐시 갱신이 단일 트랜잭션에 묶이지 않으면 불일치 발생 가능</li>
<li>해결책:<ul>
<li><strong>TTL 기반 자연 갱신</strong></li>
<li><strong>쓰기 시 무효화</strong></li>
<li><strong>이벤트 기반 캐시 업데이트</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="4-장애-내성">(4) 장애 내성</h4>
<ul>
<li><strong>SPOF(Single Point Of Failure)</strong><ul>
<li>캐시 서버가 단일 노드라면 장애 시 전체 서비스 영향</li>
<li>분산 배치(멀티 노드, 멀티 AZ/리전) 필요</li>
</ul>
</li>
<li>캐시 장애 시 대응 전략<ul>
<li>캐시 장애 → 모든 요청이 DB로 몰릴 때, DB가 감당 가능한지 고려</li>
<li>Graceful Degradation: 캐시를 건너뛰고 DB fallback</li>
<li>TTL 무시, 일시적 stale 데이터 허용 등으로 완충 가능</li>
</ul>
</li>
</ul>
<h5 id="5-메모리-할당-용량">(5) 메모리 할당 용량</h5>
<ul>
<li><p><strong>Underprovision (메모리 부족)</strong></p>
<ul>
<li>캐시 항목이 자주 퇴출(eviction)됨</li>
<li>캐시 히트율 저하 → 캐시 효과 상실</li>
</ul>
<p><strong>Overprovision (메모리 과다 할당)</strong></p>
<ul>
<li>데이터 오래 유지 가능 → 히트율↑, 트래픽 급증 대응 가능</li>
<li>단점: 비용/자원 낭비</li>
<li>일부 이론적 관점에서는 캐시의 “완충(buffer)” 역할을 중시하여 <strong>의도적 오버프로비저닝</strong> 권장</li>
</ul>
<p><strong>용량 산정</strong></p>
<ul>
<li><code>(평균 객체 크기 × 예상 동시 보존 수) + 여유분</code></li>
<li>실제 필요량보다 20~50% 여유를 두는 방식도 존재</li>
</ul>
</li>
</ul>
<h4 id="6-퇴출-정책eviction-policy">(6) 퇴출 정책(Eviction Policy)</h4>
<ul>
<li><strong>LRU (Least Recently Used)</strong>: 가장 오래 참조되지 않은 항목 제거 → 일반적, 균형적</li>
<li><strong>LFU (Least Frequently Used)</strong>: 참조 횟수 기반 제거 → 반복적 Hot Data에 최적</li>
<li><strong>TTL 우선</strong>: 만료 시간이 지난 데이터부터 제거 → 최신성 보장에 유리</li>
<li><strong>Random</strong>: 임의 항목 제거 → 단순 구현, 하지만 효율성 낮음</li>
</ul>
<hr>
<h2 id="5-콘텐츠-전송-네트워크cdn">5. 콘텐츠 전송 네트워크(<code>CDN</code>)</h2>
<h3 id="5-1-cdn의-정의">5-1. CDN의 정의</h3>
<ul>
<li>지리적으로 분산된 여러 노드(에지 서버, PoP: Point of Presence)에 원본 콘텐츠의 사본을 저장하고, </li>
<li>사용자가 요청할 때 가장 가까운 노드에서 콘텐츠를 전달하는 <strong>분산 네트워크 인프라</strong></li>
</ul>
<hr>
<h3 id="5-2-cdn의-목적">5-2. CDN의 목적</h3>
<ul>
<li><strong>지연(latency) 감소</strong>: 사용자와 가까운 에지 서버에서 응답해 네트워크 왕복 시간 단축</li>
<li><strong>원본 서버 부하 감소</strong>: 반복 요청을 CDN이 대신 처리 → DB/애플리케이션/스토리지 보호</li>
<li><strong>트래픽 분산</strong>: 대규모 트래픽(예: 이벤트, 스트리밍)을 전 세계 노드로 분산</li>
<li><strong>가용성 및 안정성 향상</strong>: 일부 노드 장애 시 다른 노드로 우회 가능</li>
<li><strong>보안 강화</strong>: TLS, 인증, DDoS 방어, WAF와 통합해 전송 계층 보안 확보</li>
</ul>
<hr>
<h3 id="참고-에지-서버edge-server">참고: 에지 서버(Edge Server)</h3>
<ul>
<li><strong>위치</strong>: 사용자와 물리적으로 가까운 네트워크 경계(Edge)에 위치한 서버</li>
<li><strong>역할</strong>: 원본 서버(Origin)에서 제공하는 콘텐츠 사본을 저장(캐시)하고, 사용자 요청 시 가장 가까운 위치에서 응답</li>
<li><strong>특징</strong>:<ul>
<li>전 세계에 분산된 <strong>CDN PoP(Point of Presence)</strong> 안에 배치됨</li>
<li>정적 리소스(이미지, JS, CSS, 동영상 등)를 캐시하여 응답 속도를 단축</li>
<li>일부 CDN은 동적 콘텐츠 처리, TLS 종료, 보안 기능(WAF, DDoS 방어)도 수행</li>
</ul>
</li>
</ul>
<hr>
<h3 id="5-3-cdn의-작동-원리">5-3. CDN의 작동 원리</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/156fe50a-b6a1-40db-b98a-f30a8e7567b4/image.png" alt="설명" class="left-block-image" />


<p>(1) 사용자 요청</p>
<ul>
<li>사용자가 <code>cdn.example.com/image.png</code> 요청</li>
</ul>
<p>(2) 에지 서버 확인</p>
<ul>
<li>요청한 콘텐츠가 캐시에 있으면 즉시 반환 (<strong>캐시 히트</strong>)</li>
<li>없으면 원본 서버(Origin)에서 가져와 캐시에 저장한 뒤 반환 (<strong>캐시 미스</strong>)</li>
</ul>
<p>(3) 이후 요청</p>
<ul>
<li>동일 지역의 다른 사용자도 같은 CDN 노드에서 캐시된 콘텐츠를 받음</li>
<li>TTL/정책에 따라 캐시가 만료되면 다시 원본 서버에서 갱신</li>
</ul>
<hr>
<h3 id="5-4-cdn-사용-시-고려할-점">5-4. CDN 사용 시 고려할 점</h3>
<ul>
<li><strong>캐시 가능 여부</strong>: 정적/동적 콘텐츠 중 어떤 것을 캐싱할지</li>
<li><strong>TTL 및 무효화 정책</strong>: 콘텐츠 신선도와 성능 균형</li>
<li><strong>지리적 분포</strong>: 사용자 위치와 CDN PoP 분포 일치 여부</li>
<li><strong>비용</strong>: CDN 트래픽/요청량 기반 과금 구조 확인</li>
<li><strong>장애/Failover</strong>: 특정 노드나 CDN 전체 장애 시 대체 경로 확보 가능성</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린 아키텍처] 18장. 경계 해부학]]></title>
            <link>https://velog.io/@yellow-w/%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-18%EC%9E%A5.-%EA%B2%BD%EA%B3%84-%ED%95%B4%EB%B6%80%ED%95%99</link>
            <guid>https://velog.io/@yellow-w/%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-18%EC%9E%A5.-%EA%B2%BD%EA%B3%84-%ED%95%B4%EB%B6%80%ED%95%99</guid>
            <pubDate>Mon, 08 Sep 2025 10:45:10 GMT</pubDate>
            <description><![CDATA[<h1 id="📖-18장-경계-해부학">📖 [18장] 경계 해부학</h1>
<blockquote>
<p>📘 클린 아키텍처 북스터디 정리입니다</p>
<p>📚 도서: 로버트 C. 마틴 《Clean Architecture》<br>🧑‍💻 목적: 올바른 설계에 대한 감각과 습관을 익히기 위해<br>🗓️ 진행 기간: 2025년 7월 ~ 매주 2장</p>
</blockquote>
<h2 id="✅-핵심-요약-key-takeaways">✅ 핵심 요약 (Key Takeaways)</h2>
<h3 id="이-장의-핵심-문장은">이 장의 핵심 문장은?</h3>
<blockquote>
<p>시스템 아키텍처는 일련의 소프트웨어 컴포넌트와 그 컴포넌트들을 분리하는 경계에 의해 정의된다.&quot;</p>
</blockquote>
<h3 id="저자가-전달하고자-하는-메세지-요약">저자가 전달하고자 하는 메세지 요약</h3>
<blockquote>
<p>좋은 아키텍처는 경계를 제대로 인식하고, 설계하고, 횡단 시 의존성 전파를 방지함으로써<br>시스템을 유연하고 유지보수하기 쉬운 구조로 만든다.</p>
</blockquote>
<hr>
<h2 id="💡-내용-정리">💡 내용 정리</h2>
<h3 id="1-서론">1. 서론</h3>
<ul>
<li>시스템 아키텍처는 일련의 소프트웨어 컴포넌트와 그 컴포넌트들을 분리하는 경계에 의해 정의된다</li>
</ul>
<h4 id="소프트웨어-경계">소프트웨어 경계</h4>
<ul>
<li>소프트웨어 시스템 내부의 서로 다른 책임과 관심사를 물리적 또는 논리적으로 분리하는 구조적 장치</li>
<li>경계를 어떻게 나누느냐에 따라 빌드 전략/배포 방식/버전 관리/언어 수준의 유연성과 제약이 결정된다</li>
</ul>
<h3 id="2-경계-횡단하기">2. 경계 횡단하기</h3>
<ul>
<li>경계를 넘어서는 호출(예: Controller → Service)은 피할 수 없다.</li>
<li>이때 의존성 방향이 올바르게 설정되어 있어야 경계가 무너지지 않는다.</li>
<li>경계는 변경이 전파되는 것을 막는 방화벽을 구축하고 관리하는 역할을 해야 한다</li>
</ul>
<h3 id="3-단일체와-소스-수준-분리-모드">3. 단일체와 소스 수준 분리 모드</h3>
<h4 id="단일체monolith">단일체(Monolith)</h4>
<ul>
<li>애플리케이션의 모든 컴포넌트가 하나의 배포 단위로 묶여 있는 형태</li>
<li>단일체 구조에서는 컴포넌트 간 경계는 파일이나 패키지 수준(소스 코드 수준)에서만 존재 </li>
<li>컴파일도, 배포도 한 번에 이루어지기 때문에 의존성 구조가 잘못되어도 빌드 시점에 드러나지 않는다</li>
<li>의존성 제어가 어렵다는 문제 존재</li>
</ul>
<h4 id="소스-수준-분리-source-level-separation">소스 수준 분리 (Source-level separation)</h4>
<ul>
<li>논리적 분리만 존재하고, 물리적인 분리는 없는 수준</li>
<li>구조적으로만 나뉘어 있을 뿐, 실질적인 독립성과 격리는 부족</li>
<li>예: Java 프로젝트 내에서 controller, service, repository 패키지로 나눴지만, 하나의 jar로 컴파일</li>
</ul>
<h4 id="단일체에서-다형성과-경계-관리">단일체에서 다형성과 경계 관리</h4>
<h5 id="동적-다형성-dynamic-polymorphism">동적 다형성 (Dynamic Polymorphism)</h5>
<ul>
<li>동일한 인터페이스를 구현한 여러 객체 중 하나를 런타임에 선택하여 사용하는 OOP 원리</li>
<li>고수준 컴포넌트가 구체 구현이 아닌 인터페이스에 의존하도록 구성
→ 내부 컴포넌트 간에도 경계를 인식하고 설계할 수 있음</li>
<li>이 원리를 활용하면 단일체 안에서도 의존성을 인터페이스로 추상화하여 결합도를 낮출 수 있다</li>
</ul>
<h5 id="단일체에서의-다형성-활용">단일체에서의 다형성 활용</h5>
<ul>
<li>단일체 내부에서도 컴포넌트 간 결합이 무분별해지면 경계가 무너지기 쉽다</li>
<li>동적 다형성을 사용하면 다음과 같은 효과가 있다<ul>
<li>DIP(의존성 역전 원칙) 구현 </li>
<li>전략 패턴 등 확장성 있는 코드 작성 </li>
<li>제어 흐름과 의존성 방향 분리 가능</li>
</ul>
</li>
</ul>
<h5 id="핵심-원칙">핵심 원칙</h5>
<ul>
<li>의존성은 항상 고수준 컴포넌트를 향해야 한다</li>
<li>데이터 구조의 정의 또한 호출하는 쪽에 있어야 한다 (즉, &quot;안쪽&quot;에 있어야 한다)</li>
</ul>
<h3 id="4-동적-링크-라이브러리와-배포-수준-분리-모드">4. 동적 링크 라이브러리와 배포 수준 분리 모드</h3>
<h4 id="동적-링크-라이브러리">동적 링크 라이브러리</h4>
<ul>
<li>.dll, .so, .jar 등 런타임에 연결되는 컴포넌트</li>
<li>각 모듈은 별도로 빌드·배포되며, 실행 시점에 동적으로 연결됨</li>
</ul>
<h4 id="배포-수준-분리-deployment-level-separation">배포 수준 분리 (Deployment-level separation)</h4>
<ul>
<li>각 모듈이 독립적으로 배포 가능</li>
<li>서로 다른 팀이 각각의 컴포넌트를 유지/배포 가능<ul>
<li>예: UserService.dll과 OrderService.dll이 독립적으로 배포됨</li>
</ul>
</li>
<li>배포 과정에서만 차이가 날 뿐, 배포 수준의 컴포넌트는 단일체와 동일 </li>
<li>배포 단위는 나뉘어 있지만, 컴포넌트 간 통신은 여전히 함수 호출 
→ 경계를 너무 자주 횡단하면 단일체처럼 결합될 위험이 있음</li>
</ul>
<h3 id="5-실행-단위에-따른-경계">5. 실행 단위에 따른 경계</h3>
<h4 id="스레드">스레드</h4>
<ul>
<li>스레드는 경계가 아니라 실행 계획의 도구</li>
<li>여러 스레드가 하나의 경계를 공유할 수도 있고, 하나의 컴포넌트가 여러 경계를 스레드로 운영할 수도 있다</li>
</ul>
<h4 id="로컬-프로세스">로컬 프로세스</h4>
<ul>
<li>OS 레벨에서 분리된 실행 단위 (예: Billing.exe, ReportGenerator.exe)</li>
<li>각 로컬 프로세스는 정적으로 링크된 단일체이거나 동적으로 링크된 여러개의 컴포넌트로 구성될 수 있다</li>
<li>로컬 프로세스 간 분리 전략은 단일 바이너리 컴포넌트의 경우와 동일하다</li>
<li>아키텍처 관점의 목표: 저수준 프로세스가 고수준 프로세스의 플러그인이 되도록 만드는 것</li>
<li>서로 다른 실행 파일이므로 통신 시 고비용(IPC, 데이터 직렬화 등)<ul>
<li>OS 호출, 데이터 마샬링 및 언어 마샬링, 프로세스 간 문맥 교환 등</li>
</ul>
</li>
<li>프로세스 간 경계는 분명하지만, 통신 빈도는 최소화해야 한다</li>
<li><h4 id="서비스">서비스</h4>
</li>
<li>가장 강력한 형태의 물리적 경계</li>
<li>HTTP/gRPC 등 네트워크 기반으로 통신 비용이 높고, 실패 가능성도 존재한다</li>
<li>서비스 수준에서는 <strong>응답 지연을 고려할 수 있는 고수준에서</strong> 통신 해야 한다</li>
</ul>
<h3 id="6-결론">6. 결론</h3>
<h4 id="경계-수준별-비교-요약">경계 수준별 비교 요약</h4>
<ul>
<li>독립성 및 통신 비용</li>
</ul>
<table>
<thead>
<tr>
<th>구분</th>
<th>빌드/배포 독립성</th>
<th>의존성 전파 위험</th>
<th>통신 비용</th>
<th>기술 스택 분리 가능성</th>
</tr>
</thead>
<tbody><tr>
<td>소스 수준 분리</td>
<td>낮음</td>
<td>매우 높음</td>
<td>매우 낮음</td>
<td>없음</td>
</tr>
<tr>
<td>동적 링크 분리</td>
<td>중간</td>
<td>있음</td>
<td>낮음</td>
<td>제한적 가능</td>
</tr>
<tr>
<td>로컬 프로세스 분리</td>
<td>높음</td>
<td>낮음</td>
<td>높음</td>
<td>가능</td>
</tr>
<tr>
<td>서비스 수준 분리</td>
<td>매우 높음</td>
<td>매우 낮음</td>
<td>매우 높음</td>
<td>완전 가능</td>
</tr>
</tbody></table>
<ul>
<li>주요 특징</li>
</ul>
<table>
<thead>
<tr>
<th>분류</th>
<th>설명</th>
<th>주요 특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>소스 수준 분리</strong></td>
<td>논리적 구조만 나눔. 컴파일/배포는 한 번에 진행</td>
<td><strong>경계는 논리적</strong>이며 <strong>컴파일/배포는 하나</strong></td>
</tr>
<tr>
<td><strong>동적 링크 분리</strong></td>
<td>런타임에 연결되는 모듈로 구성</td>
<td>경계는 <strong>실행 시점에 느슨하게 연결됨</strong></td>
</tr>
<tr>
<td><strong>로컬 프로세스 분리</strong></td>
<td>실행 단위가 <strong>별도의 OS 프로세스</strong></td>
<td>서로 다른 프로세스 간 통신 (IPC), 운영체제 개입</td>
</tr>
<tr>
<td><strong>서비스(분산 시스템) 수준 분리</strong></td>
<td>HTTP, gRPC 등 네트워크 기반 통신</td>
<td><strong>가장 물리적인 경계</strong>, <strong>통신 비용 높음</strong></td>
</tr>
</tbody></table>
<ul>
<li>단일체를 제외한 대다수의 시스템은 한 가지 이상의 경계 전략을 사용한다</li>
<li>대체로 한 시스템 안에서도 통신이 빈번한 로컬 경계와 지연을 중요하게 고려해야 하는 경계가 혼합되어 있다.</li>
</ul>
<h2 id="용어-정리">용어 정리</h2>
<h3 id="플러그인-아키텍처">플러그인 아키텍처</h3>
<h4 id="정의">정의</h4>
<ul>
<li>고수준 정책(핵심 로직)이 저수준 세부사항(DB, 외부 API, 프레임워크 등)을 몰라도 동작할 수 있는 구조 <ul>
<li>핵심 로직(정책)은 중심에 있고, DB, 외부 API, 프레임워크 등은 외곽에 위치 </li>
<li>인터페이스를 통해 구현체와 분리 </li>
<li>각 구현체는 고수준 로직의 플러그인이 되는 구조</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린 아키텍처] 17장. 경계: 선 긋기]]></title>
            <link>https://velog.io/@yellow-w/%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-17%EC%9E%A5.-%EA%B2%BD%EA%B3%84-%EC%84%A0-%EA%B8%8B%EA%B8%B0</link>
            <guid>https://velog.io/@yellow-w/%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-17%EC%9E%A5.-%EA%B2%BD%EA%B3%84-%EC%84%A0-%EA%B8%8B%EA%B8%B0</guid>
            <pubDate>Mon, 18 Aug 2025 16:02:15 GMT</pubDate>
            <description><![CDATA[<h1 id="📖-17장-경계-선-긋기">📖 [17장] 경계: 선 긋기</h1>
<blockquote>
<p>📘 클린 아키텍처 북스터디 정리입니다</p>
<p>📚 도서: 로버트 C. 마틴 《Clean Architecture》<br>🧑‍💻 목적: 올바른 설계에 대한 감각과 습관을 익히기 위해<br>🗓️ 진행 기간: 2025년 7월 ~ 매주 2장  </p>
</blockquote>
<hr>
<h2 id="✅-핵심-요약-key-takeaways">✅ 핵심 요약 (Key Takeaways)</h2>
<h3 id="이-장의-핵심-문장은">이 장의 핵심 문장은?</h3>
<blockquote>
<p>좋은 시스템 아키텍처란 이러한 결정(업무 요구사항과 무관한 결정)이 부수적이며,
결정을 연기할 수 있는 아키텍처다</p>
</blockquote>
<h3 id="저자가-전달하고자-하는-메세지-요약">저자가 전달하고자 하는 메세지 요약</h3>
<ul>
<li>시스템을 컴포넌트 단위로 분할하여 업무 규칙과 세부사항으로 분리</li>
<li>업무 규칙과 세부사항 간 경계를 생성하고, 세부 사항에서 업무 규칙쪽으로 화살표가 향하도록 함</li>
</ul>
<hr>
<h2 id="💡-내용-정리">💡 내용 정리</h2>
<h3 id="1-서론">1. 서론</h3>
<ul>
<li>소프트웨어 아키텍처는 선(경계)을 긋는 기술</li>
</ul>
<h4 id="경계의-역할">경계의 역할</h4>
<ul>
<li>소프트웨어 요소를 서로 분리하고, 경계 한편에 있는 요소가 반대편에 있는 요소를 알지 못하도록 막음</li>
</ul>
<h4 id="결합coupling">결합(coupling)</h4>
<ul>
<li>너무 일찍 내려진 결정에 따른 결합은 인적자원의 효율을 떨어트림</li>
</ul>
<h4 id="좋은-아키텍처">좋은 아키텍처</h4>
<ul>
<li>이른 결정: 시스템의 업무 요구사항(유스케이스)와 무관한 결정</li>
<li>예시: 프레임워크, DB, 웹 서버, 유틸리티 라이브러리, 의존성 주입에 대한 결정 등</li>
<li>이러한 결정이 부수적이며, 결정을 연기할 수 있는 아키텍처</li>
</ul>
<h3 id="2-경계를-긋는-방법">2. 경계를 긋는 방법</h3>
<ul>
<li>관련이 있는 것과 없는 것 사이에 선을 그거야 함</li>
</ul>
<h4 id="업무-규칙과-데이터베이스">업무 규칙과 데이터베이스</h4>
<ul>
<li>DB는 업무 규칙이 간접적으로 사용할 수 있는 도구</li>
<li>업무 규칙은 스키마, 쿼리 언어, 또는 DB와 관련된 나머지 세부 사항에 대해 어떤 것도 알아서는 안됨</li>
<li>업무 규칙이 알아야 할 것은 데이터를 가져오고 저장할 때 사용할 수 있는 함수 집합이 있다는 사실</li>
<li>이 함수 집합을 통해서 DB를 인터페이스 뒤로 숨길 수 있음</li>
</ul>
<h5 id="경계선">경계선</h5>
<img src="https://velog.velcdn.com/images/yellow-w/post/ef115c90-747d-4f3b-a7ea-0bcd4a5a130b/image.png" style="width:35%;" />

<ul>
<li>경계선은 DB Interface와 DB Access 사이에 그어짐</li>
<li>DB Access에서 출발하는 화살표는 DB Access 클래스로부터 바깥쪽으로 향함(DB Access가 존재한다는 사실을 아는 클래스는 없다는 의미)</li>
</ul>
<h5 id="화살표-방향의-의미">화살표 방향의 의미</h5>
<img src="https://velog.velcdn.com/images/yellow-w/post/2415206d-e582-4c44-8cc3-5552baa96a48/image.png" style="width:35%;" />

<ul>
<li>화살표의 방향을 보면 DB에서 BusinessRules로 향하고 있음</li>
<li>이를 통해 DB는 BusinessRules에 대해 알고 있지만 BusinessRules는 DB에 대해 알지 못한다는 것을 알 수 있음</li>
<li>즉, 두 컴포넌트 사이에 이러한 경계선을 긋고 화살표의 방향이 BusinessRules를 향하도록 만들었으므로, BusinessRules에서는 어떤 종류의 DB도 사용할 수 있음</li>
<li>DB 컴포넌트는 다양한 구현체로 교체될 수 있으며, BusinessRules는 이에 대해 조금도 개의치 않음</li>
</ul>
<h4 id="업무-규칙과-gui">업무 규칙과 GUI</h4>
<ul>
<li>예시: 비디오 게임</li>
<li>비디오 게임의 사용자 경험은 인터페이스에 의해 좌우됨</li>
<li>인터페이스의 뒷단에는 인터페이스를 조작하는 모델(데이터 구조와 함수로 구성된 정교한 집합)이 존재</li>
<li>모델에게 중요한 것은 인터페이스가 아닌 업무 규칙</li>
</ul>
<img src="https://velog.velcdn.com/images/yellow-w/post/4e3b217b-cfe9-4fa1-9063-e43d9f50a5f1/image.png" style="width:35%;" />

<h4 id="경계선과-화살표의-방향과-의미">경계선과 화살표의 방향과 의미</h4>
<ul>
<li>GUI와 BusinessRules가 경계선에 의해 분할됨</li>
<li>GUI가 BusinessRules를 신경쓰는 것을 알 수 있음</li>
</ul>
<h3 id="3-플러그인-아키텍처">3. 플러그인 아키텍처</h3>
<img src="https://velog.velcdn.com/images/yellow-w/post/d73960ca-ce9f-49dc-bf60-3d3a677ab111/image.png" style="width:35%;" />


<ul>
<li>업무 규칙과 무관한 컴포넌트 추가는 시스템에서 서드파티 플러그인을 사용할 수 있도록한 패턴과 동일</li>
<li>사용자 인터페이스는 플러그인 형태로 고려되었기 때문에 수 많은 사용자 인터페이스를 플러그인 형태로 연결 가능</li>
<li>DB도 마찬가지로 플러그인으로 다루기로 결정했기 때문에 다양한 임의의 DB 기술로 대체 가능</li>
</ul>
<h3 id="4-플러그인과-단일책임-원칙">4. 플러그인과 단일책임 원칙</h3>
<ul>
<li>경계는 <strong>변경의 축(<code>axis of change</code>)이 있는 지점</strong>에 그어짐</li>
<li>업무 규칙과 <strong>다른 시점에, 다른 속도로 변경되는 컴포넌트와 업무 규칙 사이에는 반드시 경계가 필요함</strong></li>
<li>시스템을 플러그인 아키텍처로 배치하면 변경이 전파될 수 없는 방화벽 생성 가능</li>
</ul>
<h3 id="5-결론">5. 결론</h3>
<h4 id="소프트웨어-아키텍처에-경계선-긋기">소프트웨어 아키텍처에 경계선 긋기</h4>
<ol>
<li>시스템을 컴포넌트 단위로 <strong>분할</strong></li>
<li>컴포넌트 사이의 <strong>화살표가 특정 방향, 즉 핵심 업무를 향하도록</strong> 이들 컴포넌트의 소스를 배치</li>
</ol>
<h4 id="컴포넌트-분류">컴포넌트 분류</h4>
<ul>
<li>핵심 업무 규칙</li>
<li>플러그인: 핵심 업무와는 직접적인 관련이 없지만 필수 기능 포함</li>
</ul>
<h5 id="소프트웨어-경계의-의미">소프트웨어 경계의 의미?</h5>
<ul>
<li>의존성 역전 원칙과 안정된 추상화 원칙 응용</li>
<li>의존성 화살표는 저수준 세부사항에서 고수준의 추상화를 향하도록 배치됨</li>
</ul>
<h2 id="용어-정리">용어 정리</h2>
<h3 id="플러그인-아키텍처">플러그인 아키텍처</h3>
<h4 id="플러그인-아키텍처의-정의">플러그인 아키텍처의 정의</h4>
<ul>
<li>시스템의 핵심은 고수준 정책(비즈니스 규칙)으로 구성되고, 저수준 구현(세부사항)은 나중에 ‘꽂는(plug-in)’ 방식으로 설계하는 아키텍처 패턴</li>
<li>즉, 시스템의 주요 동작은 추상화된 인터페이스에 의존하고, 구체적인 구현은 외부에서 독립적으로 연결될 수 있도록 만든 구조</li>
</ul>
<h4 id="예시">예시</h4>
<pre><code class="language-css">[ Core Application ]
     ↑       ↑
  [ DB 구현 ] [ UI 구현 ]
  [ Mail 구현 ] [ Logger 구현 ]</code></pre>
<ul>
<li>핵심 정책(Core)은 인터페이스만 알고 있음</li>
<li>DB, 웹서버, UI, 파일시스템 같은 외부 구현은 플러그인처럼 꽂힘</li>
<li>DIP(Dependency Inversion Principle)를 통해 의존성 방향을 추상 쪽으로 역전</li>
</ul>
<h4 id="플러그인-아키텍처-적용-사례">플러그인 아키텍처 적용 사례</h4>
<table>
<thead>
<tr>
<th>사례</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>스프링의 DI + @Configuration</strong></td>
<td>구체 구현체(Repository, Service 등)를 <code>@Bean</code>으로 등록해 플러그인처럼 주입</td>
</tr>
<tr>
<td><strong>JVM의 SPI (Service Provider Interface)</strong></td>
<td>런타임에 원하는 구현체를 꽂을 수 있게 하는 대표적인 플러그인 메커니즘</td>
</tr>
<tr>
<td><strong>IntelliJ 플러그인 구조</strong></td>
<td>에디터 자체는 코어, 사용자 기능은 전부 플러그인으로 분리되어 로딩 가능</td>
</tr>
</tbody></table>
<h4 id="클린-아키텍처에서의-구조화">클린 아키텍처에서의 구조화</h4>
<pre><code class="language-css">
                 [ Entities ]
                     ↑
           [ Use Cases / Interactors ]
                     ↑
         [ Interface Adapters (Controller, Gateway) ]
                     ↑
[ Frameworks &amp; Drivers (DB, Web, UI, External APIs) ]</code></pre>
<ul>
<li>안쪽 계층은 바깥쪽 계층을 몰라도 됨</li>
<li>바깥쪽 계층은 안쪽 계층의 인터페이스에 맞춰서 구현</li>
<li>바깥 계층은 안쪽을 의존하지 않고 “플러그인”처럼 연결됨</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[클린 아키텍처] 16장. 독립성]]></title>
            <link>https://velog.io/@yellow-w/%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-16%EC%9E%A5.-%EB%8F%85%EB%A6%BD%EC%84%B1</link>
            <guid>https://velog.io/@yellow-w/%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-16%EC%9E%A5.-%EB%8F%85%EB%A6%BD%EC%84%B1</guid>
            <pubDate>Mon, 18 Aug 2025 14:47:25 GMT</pubDate>
            <description><![CDATA[<h1 id="📖-16장-독립성">📖 [16장] 독립성</h1>
<blockquote>
<p>📘 클린 아키텍처 북스터디 정리입니다</p>
<p>📚 도서: 로버트 C. 마틴 《Clean Architecture》<br>🧑‍💻 목적: 올바른 설계에 대한 감각과 습관을 익히기 위해<br>🗓️ 진행 기간: 2025년 7월 ~ 매주 2장  </p>
</blockquote>
<hr>
<h2 id="✅-핵심-요약-key-takeaways">✅ 핵심 요약 (Key Takeaways)</h2>
<h3 id="이-장의-핵심-문장은">이 장의 핵심 문장은?</h3>
<blockquote>
<p>좋은 아키텍처는 선택사항을 열어 둠으로써, 
향후 시스템에 변경이 필요할 때 어떤 방향으로든 쉽게 변경할 수 있도록 한다</p>
</blockquote>
<h3 id="저자가-전달하고자-하는-메세지-요약">저자가 전달하고자 하는 메세지 요약</h3>
<ul>
<li>좋은 아키텍처는 4개의 관심사(유스케이스, 운영, 개발, 배포)를 지원해야 함</li>
<li>각각의 관심사 간 균형을 맞추고 각 관심사 모두를 만족시키면 좋겠지만 현실상 어려움</li>
<li>따라서 아키텍처는 선택사항을 열어두어 향후 변경이 필요할 때 쉽게 변경할 수 있도록 해야 함</li>
</ul>
<hr>
<h2 id="💡-내용-정리">💡 내용 정리</h2>
<h3 id="서론">서론</h3>
<h4 id="좋은-아키텍처는-다음을-지원해야-함">좋은 아키텍처는 다음을 지원해야 함</h4>
<ul>
<li>시스템의 유스케이스</li>
<li>시스템의 운영</li>
<li>시스템의 개발</li>
<li>시스템의 배포</li>
</ul>
<h3 id="1-좋은-아키텍처가-지원해야-하는-관심사들">1. 좋은 아키텍처가 지원해야 하는 관심사들</h3>
<h4 id="유스케이스">유스케이스</h4>
<ul>
<li>시스템의 아키텍처는 시스템의 의도를 지원해야 함</li>
<li>아키텍처는 반드시 유스케이스를 지원해야 함</li>
<li>좋은 아키텍처는 행위를 명확히하고 외부로 드러내는 것을 통해 시스템이 지닌 의도를 아키텍처 수준에서 알아볼 수 있게 만듦</li>
</ul>
<h4 id="운영">운영</h4>
<ul>
<li>좋은 아키텍처는 시스템의 운영을 지원해야 하고 이러한 지원은 시스템에 따라 다양한 의미를 가짐</li>
<li>따라서 아키텍처에서 각 컴포넌트를 적절히 격리하여 유지하고 컴포넌트 간 통신 방식을 특정 형태로 제한하지 않아야 함</li>
<li>그러면 시간이 지나 운영에 필요한 요구사항이 바뀌더라도 기술 스펙트럼을 전환하는 일이 쉬움</li>
</ul>
<h4 id="개발">개발</h4>
<ul>
<li>콘웨이의 법칙: &quot;시스템을 설계하는 조직이라면 어디든지 그 조직의 의사소통 구조와 동일한 구조의 설계를 만들어 낼 것이다&quot;</li>
<li>아키텍처는 잘 격리되어 독립적으로 개발 가능한 컴포넌트 단위로 시스템을 분할할 수 있어야 함</li>
</ul>
<h4 id="배포">배포</h4>
<ul>
<li>목표: 시스템 빌드 후 즉각적인 배포(<code>immediate deployment</code>)</li>
</ul>
<h3 id="2-관심사들-간의-균형">2. 관심사들 간의 균형</h3>
<ul>
<li>좋은 아키텍처는 컴포넌트 구조와 관련된 관심사들 간 균형을 맞추고, 각 관심사 모두를 만족시킴</li>
<li>하지만 현실에서는 이러한 균형을 잡기가 어려움</li>
<li>좋은 아키텍처는 선택 사항을 <strong>열어둠</strong>으로써, 향후 시스템에 변경이 필요할 때 어떤 방향으로든 <strong>쉽게 변경할 수 있도록</strong> 함</li>
</ul>
<h3 id="3-계층-결합-분리">3. 계층 결합 분리</h3>
<ul>
<li>시스템을 서로 결합되지 않은 수평적인 계층으로 분리 &gt; 여러 요소를 서로 독립적으로 변경, 유스케이스를 가시적이고 분명하게 유지</li>
<li>예시: UI / 앱에 특화된 업무 규칙 / 앱과는 독립적인 업무 규칙, DB 등</li>
</ul>
<h3 id="4-유스-케이스-결합-분리">4. 유스 케이스 결합 분리</h3>
<ul>
<li>시스템을 수평적 계층으로 분할하면서, 동시에 계층을 가로지르는 수직적인 유스케이스로 분할 가능</li>
<li>시스템에서 서로 다른 이유로 변경되는 요소들의 결합을 분리하면 기존 요소에 지장을 주지 않고도 새로운 유스케이스를 계속해서 추가 가능</li>
<li>유스케이스를 뒷받침하는 UI와 DB를 서로 묶어서 각 유스케이스가 UI와 DB의 서로 다른 관점 사용하는 것도 가능</li>
<li>예시: 주문 입력 시스템의 주문 추가 유스케이스 /  주문 삭제 유스케이스</li>
</ul>
<h3 id="5-결합-분리-모드">5. 결합 분리 모드</h3>
<h4 id="결합-분리-시-운영-관점에서의-의미">결합 분리 시 운영 관점에서의 의미?</h4>
<ul>
<li>유스케이스를 수행하는 작업(결합 분리)들은 운영에 도움이 됨</li>
<li>하지만 운영 측면에서의 이점을 살리기 위해서는 결합 분리 시 적절한 모드를 선택해야 함</li>
<li>운영 측면의 이점을 위해 컴포넌트를 서비스 수준까지 분리해야하는 경우도 있음</li>
</ul>
<h4 id="개발-독립성">개발 독립성</h4>
<h4 id="배포-독립성">배포 독립성</h4>
<h3 id="6-중복">6. 중복</h3>
<ul>
<li>중복 발생 시 진짜 중복인지 가짜 또는 우발적 중복인 지 확인해야 함<h4 id="진짜-중복">진짜 중복</h4>
</li>
<li>처리:인스턴스 변경 시, 동일한 변경을 그 인스턴스의 모든 복사본에 반드시 적용<h4 id="거짓된-또는-우발적-중복">거짓된 또는 우발적 중복</h4>
</li>
<li>두 코드 영역이 각자의 경로로 발전한다면(서로 다른 속도와 다른 이유로 변경) 진짜 중복이 아님</li>
<li>해당 코드를 통합하지 않도록 유의해야 함</li>
</ul>
<h3 id="7-결합-분리-수준">7. 결합 분리 수준</h3>
<ul>
<li>좋은 아키텍처는 결합 분리 모드를 선택사항으로 남겨두어서 배포 규모에 따라 가장 적합한 모드를 선택해 사용할 수 있게 만들어줌</li>
</ul>
<h4 id="1-소스-코드-수준">1) 소스 코드 수준</h4>
<ul>
<li>소스 코드 모듈 사이의 의존성 제어</li>
<li>흔히 모노리틱 구조라고 부름</li>
</ul>
<h4 id="2바이너리-코드-수준">2)바이너리 코드 수준</h4>
<ul>
<li>jar 파일, DLL, 공유 라이브러리와 같이 배포 가능한 단위들 사이의 의존성 제어</li>
<li>결합이 분리된 컴포넌트가 jar 파일, Gem 파일, DLL과 같이 독립적으로 배포할 수 있는 단위로 분할되어 있음</li>
</ul>
<h4 id="3-실행-단위서비스-수준">3) 실행 단위(서비스) 수준</h4>
<ul>
<li>의존하는 수준을 데이터 구조 단위까지 낮출 수 있고, 순전히 네트워크 패킷을 통해서만 통신하도록 할 수 있음</li>
<li>이를 통해 모든 실행 가능한 단위는 소스와 바이너리 변경에 대해 서로 완전히 독립적이게 됨</li>
<li>예시: 서비스 또는 마이크로 서비스</li>
</ul>
<h2 id="용어-정리">용어 정리</h2>
<h3 id="관점-지향-프로그래밍aspect-oriented-programming-aop">관점 지향 프로그래밍(Aspect-Oriented programming; AOP)</h3>
<ul>
<li>횡단 관심사를 분리하여 모듈화를 높이는 프로그래밍 패러다임</li>
<li>코드 자체를 수정하지 않고, 기존 코드에 추가하는 방식으로 구현</li>
<li>횡단 관심사의 주요 예: 예외 처리, 로깅, 프랜잭션 등</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>