26.02.09 ~ 26.02.16 작성본
책 내용 요약
단일 서버

- 사용자가 도메인 이름으로 웹사이트에 접속
- DNS (제3자 서비스) 질의, IP 주소 변환
- 해당 IP 주소로 요청 전달
- 대상 웹 서버가 HTML/JSON 응답 반환
데이터베이스

웹/모바일 트래픽 처리 서버와 데이터베이스 서버를 분리하면 각각 독립적으로 확장이 가능하다.
데이터베이스 선택 기준
NoSQL 대 SQL 데이터베이스
NoSQL 데이터베이스와 SQL 데이터베이스 간의 주요 차이점에 대해 알아보세요.
www.mongodb.com
관계형 데이터베이스
- 자료를 테이블과 열로 표현
- SQL을 이용하여 여러 테이블의 데이터를 조인할 수 있음
비관계형 데이터베이스
- 키-값 저장소, 그래프 저장소, 컬럼 저장소, 문서 저장소
- NoSQL, 전통적/초기 NoSQL 계열은 조인 연산을 제한하거나 약하게 제공하는 경우가 많음
Q1. 조인 연산이 지원되는 비관계형 데이터베이스는?
- MongoDB: NoSQL의 대명사인 몽고 DB는 3.2 버전부터 $lookup이라는 집계 파이프라인 단계를 통해 Left Outer Join 기능을 제공합니다. 서로 다른 컬렉션 (Collection)의 데이터를 합칠 수 있습니다.
- Couchbase: 카우치베이스는 SQL-plus-JSON (N1QL)이라는 쿼리 언어를 사용합니다. 문법 자체가 ANSI SQL과 매우 유사하여, 여러 문서 (Document) 간의 조인을 상당히 강력하게 지원합니다.
- ArangoDB: '멀티 모델' 데이터베이스를 표방하며, 문서 (Document), 그래프 (Graph), 키-값 (Key-Value) 모델을 동시에 지원합니다. AQL (ArangoDB Query Language)을 통해 매우 복잡한 조인 연산을 수행할 수 있습니다.
- Graph Databases (Neo4j): Neo4j의 그래프 DB 특성상 깊은 관계 탐색 (traversal) 에서 성능이 유리할 수 있습니다. 다만 집계 중심의 리포팅이나 단순 조인에서는 RDBMS가 더 적합한 경우도 많습니다. (추가 자료 1) (추가 자료 2)
Q2. NoSQL이 조인을 피하려 했던 CS적 근거
NoSQL이 처음부터 조인을 지원하지 않았던 이유는 분산 시스템의 확장성 (Scalability) 때문입니다.
Why NoSQL is better at scaling out than RDBMSs?
Technical blog http://tekedia.com/12083/nosql-database-advantages-and-disadvantages/ by David Adamo Jr. discusses the advantages and disadvantages of NoSQL: For years, in order to improve performa...
stackoverflow.com
태초의 NoSQL: "조인보다는 수평 확장을 우선하다"
초창기 NoSQL은 대규모 수평 확장을 우선시하면서, 조인과 같은 복합 연산을 시스템 설계 철학상 우선순위에서 낮추는 경우가 많았습니다.
- 데이터의 물리적 거리와 네트워크 비용: RDBMS는 한 지붕 아래 모여 사는 가족처럼 (단일 노드 기준) 로컬 디스크/메모리 내에서 조인하지만, 분산 NoSQL은 전 세계에 흩어진 가족과 같습니다. 로컬 디스크/메모리 내의 조인과 달리, 분산 노드 간의 데이터 병합은 네트워크 왕복, 데이터 직렬화, 셔플링 비용이 발생하여 지연 시간이 급격히 증가할 수 있습니다.
- CAP 정리와 가용성의 선택: 분산 환경에서 강한 일관성 (Strong Consistency)을 유지하며 여러 노드에 걸쳐 조인 쿼리를 수행하려면 노드 간 상태화를 동기화하는 조정 (Coordination) 비용이 매우 큽니다. 이는 시스템 전체의 지연 시간 (Latency) 을 늘리고 가용성을 떨어뜨리는 원인이 됩니다. NoSQL은 조금 늦더라도 (Eventual Consistency) 서비스는 계속되어야 한다 (Availability)는 원칙을 위해 조인과 같은 복합 연산을 우선순위에서 낮추거나 제한하는 선택을 한 경우가 많았습니다.
- 다만 NoSQL은 범주가 넓어 시스템에 따라 가용성/일관성의 우선순위가 다를 수 있습니다. 특히 네트워크 분할 (Partition)이 발생하면 분산 시스템은 일관성과 가용성 사이에서 트레이드 오프가 생길 수 있어, 목표 특성에 맞춘 설계 의사결정이 필요합니다.
- 성능의 예측 가능성: 많은 NoSQL 시스템은 복잡한 관계 연산보다는 단일 키 기반의 단순 접근 패턴을 통해, 대규모 트래픽에서도 예측 가능한 (Predictable) 성능을 제공하는 것을 목표로 설계되었습니다. 복잡한 조인 연산이 섞이면 쿼리 옵티마이저가 최적의 경로를 찾기 힘들어지고, 시스템 성능이 널뛰기 시작하기 때문입니다.
현대의 NoSQL: "그럼에도 조인을 품은 이유"
하지만 현대의 MongoDB나 Neo4j 가 조인 기능을 도입한 것은 단순히 변심한 것이 아니라, 기존의 제약을 공학적으로 우회하고 보완할 수 있는 수준에 도달했기 때문입니다.
- 인프라의 진화: 데이터센터 네트워크/스토리지 성능의 발전과 엔진 최적화로, 과거보다 교차 노드 연산의 비용이 완화되면서 제한적 조인 기능에 대한 수요가 커졌습니다.
- 비정규화의 역습: 조인을 피하려고 데이터를 중복 저장 (Denormalization) 했더니, 원본 데이터 하나를 바꿀 때 중복된 수천 개의 데이터를 일일이 업데이트해야 하는 '데이터 정합성 지옥'에 빠졌습니다. "앱에서 고생하느라 꼬일 바에야, DB가 효율적인 조인 기능을 제공하는 게 낫다"는 실용적 판단이 선 것입니다.
- 구조적 혁신 (Neo4j의 사례): Neo4j 는 조인을 '연산'이 아닌 '이동'으로 바꿨습니다. 데이터가 저장될 때 옆 데이터의 물리적 주소 (Pointer)를 미리 저장하는 Index-free Adjacency 기술을 통해, 기존 NoSQL이 가졌던 '조인은 느리다'는 편견을 아키텍처 레벨에서 깨버렸습니다.
- 데이터 지역성 (Colocation) 활용: 현대 분산 DB는 조인될 데이터를 미리 같은 서버 (Shard)에 배치하는 지능형 분산 전략을 사용합니다. 이를 통해 네트워크 이동 없이 로컬 내에서 조인을 수행하여 확장성과 조인의 이점을 동시에 챙기게 되었습니다.
Q3. NoSQL에서 조인을 다루는 법
1. 비정규화
가장 대표적인 NoSQL의 철학입니다. 관련된 데이터를 쪼개지 않고, 하나의 문서나 레코드 안에 통째로 집어넣는 방식입니다.
- 방식: JSON 형태의 문서 내에 리스트나 중첩 객체 (Nested Object)로 데이터를 포함시킵니다. (ex: '게시글' 문서 안에 '댓글' 리스트를 아예 포함시켜 저장)
- 장점: 한 번의 쿼리로 모든 데이터를 가져올 수 있어 읽기 속도가 압도적으로 빠릅니다. (I/O 최소화)
- 단점: 데이터가 너무 많아지면 문서 크기 제한 (ex: MongoDB 16MB)에 걸릴 수 있고, 내부 데이터만 따로 수정하기가 번거로울 수 있습니다.
- 사용 시점: 데이터가 1:N 관계이면서 N의 개수가 적을 때, 그리고 게시글을 볼 때 댓글은 항상 같이 보는 것처럼 데이터의 수명 주기가 같을 때 최적입니다.
2. 애플리케이션 조인
DB 엔진에 조인을 맡기는 대신, 애플리케이션 코드에서 직접 조인을 수행하는 방식입니다.
- 방식: 첫 번째 쿼리로 '주문' 데이터를 가져온 후, 주문 데이터에 있는 user_id 목록을 추출한 뒤, 두 번째 쿼리로 '사용자' 테이블에서 해당 ID들을 조회합니다. 이후, 코드 내에서 두 결과를 합칩니다.
- 장점: DB 모델이 단순해지고, 각 컬렉션을 독립적으로 관리할 수 있어 확장이 용이합니다.
- 단점: DB에 여러 번 요청을 보내야 하므로 네트워크 오버헤드가 발생하며, 합치는 로직이 복잡해지면 서버의 메모리와 CPU에 부담을 줍니다.
- 사용 시점: DB 수준의 조인을 지원하지 않거나, 샤드 간 조인 비용이 너무 커서 애플리케이션 레벨에서 데이터를 제어하고 캐싱하고자 할 때 사용합니다. (단, 여러 번의 조회 사이에 데이터가 변할 수 있어 일관성 보장이 어려울 수 있음에 유의해야 합니다.)
3. 데이터 중복 허용
조인을 피하기 위해, 다른 테이블의 정보를 일부러 복사해서 들고 있는 방식입니다. 비정규화와 비슷해 보이지만, 주로 '참조' 관계에서 특정 필드만 복사해 온다는 점이 다릅니다.
- 방식: "주문" 컬렉션에 user_id만 저장하는 게 아니라, 화면에 바로 보여줘야 할 user_name까지 함께 저장해 버립니다.
- 장점: 조인이나 추가 쿼리 없이 주문 내역에서 바로 사용자 이름을 보여줄 수 있습니다.
- 단점: 데이터 정합성의 문제가 생깁니다. 사용자가 이름을 바꾸면, 그 사용자가 주문했던 수천 건의 데이터에 적힌 이름을 일일이 찾아가서 수정해야 합니다.
- 사용 시점: "사용자 이름은 거의 바뀌지 않는다", "주문 당시의 이름이 기록으로 남는 게 더 중요하다"와 같이 수정이 적고 읽기가 빈번한 데이터에 적합합니다.
실무에서의 활용법
실무에서는 이 세 가지를 섞어서 씁니다. 예를 들어 배달 앱을 만든다면 가게 정보는 애플리케이션 조인으로 가져오되, 메뉴 정보는 주문서에 비정규화로 넣고, 사용자 닉네임은 중복을 허용해서 저장하는 식으로도 사용할 수 있습니다.
가장 중요한 기준은 데이터가 얼마나 자주 바뀌는가? 와 사용자가 화면을 볼 때 어떤 데이터를 한꺼번에 원하는가?입니다. 이것에 따라 어떤 방식을 쓰면 좋을지 결정할 수 있습니다.
NoSQL을 사용하면 좋은 이유와 그에 대한 고민 (언제 RDBMS를 쓰고 언제 NoSQL을 쓰는 게 좋을까?)
등장 배경: 효율의 시대 vs 확장의 시대
RDBMS: 저장 공간은 비싸고, 데이터는 신성하다. (1970년대 ~)
- 배경: 당시엔 저장 장치가 너무 비쌌습니다. 그렇기에 데이터를 중복해서 저장하는 건 사치였습니다.
- 해결책: 데이터를 쪼개고 쪼개서 중복을 없애는 정규화 (Normalization)가 핵심이었습니다. 흩어진 데이터를 합치기 위해 조인 (Join)이 발달했습니다.
- 철학: 데이터가 한 치의 오차도 없이 정확해야 한다 (ACID) - 은행 계좌 이체처럼 정합성이 생명인 작업에 최적화되었습니다.
NoSQL: 데이터가 폭발한다, 일단 다 담고 보자. (2000년대 초 ~)
- 배경: 구글, 아마존, 페이스북 같은 서비스가 나오면서 데이터 양이 기하급수적으로 늘어났습니다. 서버 한 대로는 감당이 안 되는 시대가 온 것입니다.
- 해결책: 정교한 조인보다는 빠른 읽기/쓰기와 수평적 확장에 집중했습니다.
- 철학: 조금 틀려도 괜찮으니 (Eventual Consistency) 절대 죽지 않고 (Availability) 수평 확장이 용이해야 한다.
언제 무엇을 써야 할까?
| 구분 | RDBMS | NoSQL |
| 데이터 구조 | 엄격한 스키마, 고정된 테이블 구조 | 유연한 스키마 (JSON 등) |
| 핵심 강점 | 데이터 정합성, 복잡한 쿼리, 조인 | 확장성 (Scale-out), 높은 처리량 |
| 확장 방식 | 수직적 확장 (Scale-up) 위주 | 수평적 확장 (Scale-out) 기본 |
| 추천 케이스 | 결제, 금융, 복잡한 관계의 커뮤니티 | 로그 저장, 실시간 피드, 캐싱, 빅데이터 |
특정 언어/프레임워크를 쓰면 DB 기술이 정해지는 것의 오해
Spring 등을 쓰다 보면 이걸 쓰는 순간 RDBMS를 쓰는 것이 최적화된 형태가 아닌가라고 생각이 들 수 있지만, 과거에만 있던 경향이고 지금은 아닙니다.
- 프레임워크는 도구일 뿐, 종속되지 않습니다.
- Spring 진영은 Spring Data JPA (RDBMS 용) 뿐만 아니라 Spring Data MongoDB, Spring Data Redis, Spring Data Cassandra 등을 모두 제공합니다.
- 현실: Kotlin / Spring 환경에서도 채팅 로그는 MongoDB에, 사용자 정보는 PostgreSQL에, 실시간 랭킹은 Redis에 담는 폴리글랏 퍼시스턴스 (Polyglot Persistence)가 현대 아키텍처의 정석입니다.
- 왜 그런 느낌이 들었을까?
- 커뮤니티와 생태계: Java / Spring 진영이 워낙 오래되었고 기업용 시스템 (ERD가 복잡한)을 많이 만들다 보니 RDBMS와의 궁합 예제가 압도적으로 많을 뿐입니다.
- Go / Node.js: 반대로 이쪽은 가볍고 빠른 실시간 서비스를 많이 만들다 보니 NoSQL (MongoDB, Redis)과 엮인 예제가 많이 보이는 것뿐입니다.
결론적으로, 데이터베이스 기술은
- 프레임워크가 정하는 것이 아니라, 해결해야 할 '데이터의 성격'과 '비즈니스 요구사항'에 의해 정해집니다.
- 처음 시작할 때에는 RDBMS로 시작하는 것이 대개 안전합니다. 데이터 구조를 잡기 쉽고 정합성이 보장되기 때문입니다. 그러다 특정 기능 (ex: 대규모 로그, 실시간 알림)에서 병목이 생기면 그때 그 부분만 NoSQL로 떼어내는 전략을 취해보는 게 좋습니다.
수직적 규모 확장 vs 수평적 규모 확장
수직적 규모 확장 (Scale up): 서버에 고사양 자원을 추가하는 행위
- 서버로 유입되는 트래픽의 양이 적을 때 좋은 선택
- 장점
- 데이터 일관성 유지: 모든 데이터가 한 대의 서버, 한 대의 DB에 있기 때문에 분산 시스템에서의 데이터 불일치나 분산 트랜잭션 등을 고민할 필요가 없음.
- 네트워크 오버헤드 제로: 여러 서버의 통신이 필요 없으므로 서버 간 데이터 전송에 의한 네트워크 지연이나 패킷 손실을 걱정할 필요가 없음.
- 소프트웨어 라이선스 비용 절감: 많은 상용 소프트웨어가 서버 수, 코어 수 당 라이선스 비용을 매기기 때문에, 성능이 낮은 서버 10대보다 고성능 서버 1대의 라이선스 비용이 저렴함.
- 다만 코어당 과금 방식일 경우 8 코어 서버 10대나 80 코어 서버 1대나 라이선스 비용이 이론적으로 동일할 수도 있고, 요즘은 사용한 만큼 (consumption-based) 내는 방식이 많아져서 절대적인 장점은 아님. 핵심은 "아키텍처를 결정하기 전 반드시 비용 추산을 해 봐야 한다"는 뜻.
- 운영의 단순함: 관리 포인트가 하나이기 때문에 모니터링/백업/보안 패치/배포를 한 번만 수행하면 됨.
- 단점
- 물리적 한계: CPU/메모리 성능이나 수에는 물리적인 한계가 있음.
- 가성비 하락: 성능을 2배 올릴 때 가격이 2배가 아니라 더 드는 경우가 있음.
- 단일 장애점: 서버 한 대가 죽는 순간 서비스 전체가 중단됨.
- 업그레이드 시 가동 중단: 부품 교체 혹은 CPU 업그레이드 시 물리적으로 서버를 꺼야 함.
수평적 규모 확장 (Scale out): 더 많은 서버를 추가하여 성능을 개선하는 행위
- 장점
- 높은 확장 (but 한계치 존재): 단일 장비의 물리적 한계를 넘어 서버를 추가함으로써 처리 용량을 크게 늘릴 수 있음. 다만 노드가 늘어날수록 네트워크 병목과 일관성 유지 비용, 운영 복잡도가 함께 상승하는 한계 존재.
- 고가용성: 서버 한 대가 고장 나도 다른 서버 덕분에 SPOF 제거 가능.
- 비용 효율성: 슈퍼컴퓨터 1대보다 보편적 성능의 서버 N대를 쓰는 것이 가성비 우월.
- 유연한 대응: 트래픽이 몰리는 시간대에만 서버를 늘렸다가 줄이는 탄력적 운영이 가능.
- 단점
- 관리 복잡: 서버가 많아질수록 설정 변경, 소스 코드 배포 등을 일일이 반영하는 건 불가능하며, k8s와 같은 오케스트레이션 도구가 필수적.
- 로드밸런서 필요: 트래픽을 공정하게 나눠줄 로드밸런서가 필요하고, 이것이 장애 지점이 되지 않도록 관리가 필요함.
- 데이터 일관성 문제: 여러 서버가 동일한 데이터를 보고 있어야 함. 이를 위한 분산 시스템 설계가 필수.
- 네트워크 병목: 서버 간의 통신이 많아지면 네트워크 지연 시간이 전체 시스템 성능에 영향을 끼침.
언제 Scale up을 쓰고 언제 Scale out을 써야 할까?
비즈니스의 생애주기 (Life cycle)
아키텍처는 비즈니스의 속도에 맞춰야 합니다.
- Scale-up
- 개발 속도가 생명일 때: 분산 시스템의 복잡도 (분산 트랜잭션, 데이터 정합성)를 해결할 시간에 기능 하나라도 더 배포해야 하는 단계입니다.
- 트래픽 예측이 가능할 때: 사용자가 완만하게 늘어난다면 사양을 한 단계씩 올리는 것이 가장 경제적이고 관리 효율이 높습니다.
- Scale-out
- "절대 죽으면 안 되는" 서비스가 되었을 때: 서버 한 대의 장애가 매출 타격으로 직결되는 시점부터는 고가용성 (HA)을 위해 무조건 Scale-out으로 가야 합니다.
- 예측 불가능한 이벤트가 잦을 때: 마케팅 이벤트나 핫딜 등으로 트래픽이 평소의 10배씩 튄다면, 물리적인 Scale-up으로는 대응 속도를 맞출 수 없습니다.
기술적 병목의 성격 (Bottleneck Type)
어디가 막히느냐에 따라 처방이 달라집니다.
- CPU / Memory 연산 위주일 때 (Scale-up 유리): 복잡한 계산, 대규모 이미지 처리, 무거운 조인 (Join) 연산이 주된 병목이라면 고성능 부품을 쓰는 Scale-up 이 성능 체감이 훨씬 큽니다.
- 단순 I/O 요청이 많을 때 (Scale-out 유리): 로그인 요청, 단순 조회, 정적 파일 서빙 등 가벼운 요청이 수만 건 쏟아지는 상황이라면, 비싼 서버 한 대보다 평범한 서버 여러 대가 요청을 나눠 받는 것이 훨씬 효율적입니다.
실전 의사결정 매트릭스
아래 표를 바탕으로 현재 상황을 대입해 봐도 좋습니다.
| 판단 요소 | 수직적 확장 (Scale-up) 권장 | 수평적 확장 (Scale-out) 권장 |
| 장애 허용 범위 | 가끔 점검을 위해 꺼도 괜찮음 | 1분 1초라도 죽으면 안 됨 (SLA 중시) |
| 데이터 관계 | 데이터 간 관계가 복잡하고 Join이 필수적 | 데이터가 독립적이고 샤딩이 용이함 |
| 운영 인력 | 나 혼자 혹은 소수 (단순함 우선) | DevOps 인력이 있고 자동화 환경 구축됨 |
| 예산 구조 | 초기 투자 비용 (CapEx) 여유 있음 | 운영 비용 (OpEx) 최적화 필요 (탄력적 비용) |
현실적 조언
현실에서는 적절한 성능의 인스턴스 (Scale-up)를 여러 대 띄우는 (Scale-out) 하이브리드 전략을 택합니다. 너무 저렴한 사양의 서버 100대를 쓰는 것은 관리 부하만 키우며, 최고 사양 서버 1대만 쓰는 것은 너무 위험합니다. 그래서 중급 사양 서버 3~5대를 로드밸런서 뒤에 배치하는 것이 대부분의 서비스에서 가장 먼저 채택하는 현실적인 타협점입니다.
캐시
Scaling Memcache at Facebook - Meta Research | Meta Research
Memcached is a well known, simple, in memory caching solution. This paper describes how Facebook leverages memcached as a building block to construct and scale a distributed key-value store that supports the world’s largest social network.
research.facebook.com
캐시는 값비싼 연산 결과 또는 자주 참조되는 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소다.
별도의 캐시 계층을 두면 성능 개선 및 데이터베이스 부하 감소를 할 수 있으며, 캐시 계층의 규모를 독립적으로 확장하는 것 또한 가능하다.

캐시 사용 시 유의할 점
- 캐시는 어떤 상황에 바람직한가?: 데이터 갱신은 자주 일어나지 않지만 참조는 빈번하게 일어나는 데이터에 적합
- 어떤 데이터를 캐시에 두어야 하는가?: 캐시는 데이터를 휘발성 메모리에 두므로, 영속적 보관 목적의 데이터를 캐시에 담아두는 것은 적합하지 않다.
- 캐시에 보관된 데이터는 어떻게 만료되는가?: 만료 정책이 없으면 데이터는 캐시에 계속 남게 되고, 너무 짧으면 데이터베이스를 자주 읽게 되며 너무 길면 원본과 차이 날 가능성이 높아진다.
- 일관성은 어떻게 유지되는가?: 저장소의 원본을 갱신하는 연산과 캐시를 갱신하는 연산이 단일 트랜잭션으로 처리되지 않는 경우 일관성이 깨질 수 있다. 여러 지역에 걸쳐 시스템이 확장된 경우 캐시와 저장소 사이의 일관성을 유지하려면 어떻게 할 수 있을까?
- 장애에는 어떻게 대처할 것인가?: 캐시 서버를 한 대만 두면 SPOF 위험에 빠질 수 있다. 이를 회피하려면 여러 지역에 걸쳐 캐시 서버를 분산시켜야 한다.
- 캐시 메모리는 얼마나 크게 잡을 것인가?: 메모리가 너무 작으면 액세스 패턴에 따라 데이터가 자주 캐시에서 밀려나 캐시의 성능이 떨어진다. 메모리 크기를 과할당 해 두면 캐시에 보관될 데이터가 갑자기 늘어났을 때 생길 문제를 방지할 수 있다.
- 데이터 방출 정책은 무엇인가?: 캐시가 꽉 차버릴 경우 기존 데이터를 내보내야 하는데, LRU (Least Recently Used) / LFU (Least Frequently Used) / FIFO (First In First Out) 정책 등 다양한 정책이 있다.
콘텐츠 전송 네트워크 (CDN)
CDN (Content Delivery Network) 은 정적 콘텐츠를 전송하는 데 쓰이는, 지리적으로 분산된 서버의 네트워크이다.
요청 경로, 질의 문자열, 쿠키, 요청 헤더 등의 요청 정보에 기반하여 HTML 페이지를 캐시 한다.

CDN 고려 사항
- 비용: CDN은 보통 제3자 에 의해 운영되며, 데이터 전송량에 따라 요금이 부과된다. 그렇기에 자주 사용되지 않는 콘텐츠는 캐싱되지 않도록 해야 한다.
- 적절한 만료 시한 설정: 시의성이 중요한 콘텐츠의 경우 만료 시점을 잘 정해야 함. 너무 길면 콘텐츠의 신선도가 떨어지고, 너무 짧으면 원본 서버에 빈번히 접속하게 됨.
- CDN 장애 대처 방안: CDN 자체가 죽었을 경우 웹사이트/애플리케이션이 어떻게 동작해야 하는지 고려해야 함. 문제를 감지하여 원본 서버로부터 직접 콘텐츠를 가져오는 등의 방안이 필요.
- 콘텐츠 무효화 방법: 아직 만료되지 않은 콘텐츠라 하더라도 CDN API를 이용하여 콘텐츠를 무효화하거나, 콘텐츠의 다른 버전을 서비스하도록 오브젝트 버저닝 (object versioning)을 이용하면 콘텐츠를 무효화할 수 있음.
왜 콘텐츠 무효화 방법을 고려해야 할까?
기본적으로 CDN은 설정된 TTL 동안 파일을 쥐고 있음. TTL을 1일로 설정했는데 배포한 지 1시간 만에 심각한 버그를 발견했다면 이에 대한 수정 작업이 필요함.
- 콘텐츠 무효화
- 언제 쓰는가?
- 긴급 수정: CSS / JS 파일 등에 치명적인 오류가 있어 즉시 반영해야 할 때
- 보안 이슈: 실수로 노출된 개인정보나 잘못된 이미지를 즉시 삭제해야 할 때
- 브랜드 변경: 로고나 공지사항 팝업 이미지를 정해진 시간에 딱 맞춰 바꿔야 할 때
- 주의사항: 전 세계 모든 에지 서버에 명령을 전송하기 때문에 반영되는 데 수 초 ~ 수 분 소요. 빈번하게 실행하면 CDN 업체에서 추가 비용을 청구할 수도 있음.
- 언제 쓰는가?
- 오브젝트 버저닝
- 언제 쓰는가?
- 정기 배포: 가장 권장되는 방식. 새로운 버전을 배포할 때 파일 이름을 변경.
- 무중단 배포: 구버전 페이지를 보고 있는 사용자에게는 구버전 파일, 신버전 페이지를 보고 있는 사용자에게는 신버전 파일을 정확히 서빙 가능.
- 장점: CDN 입장에서는 새로운 파일이 들어온 것이므로 무효화 과정을 거칠 필요 없이 즉시 최신 파일이 서빙됨. 이전 버전의 캐시를 건드리지 않으므로 안정적.
- 언제 쓰는가?
무상태 (stateless) 웹 계층
상태 의존적 아키텍처
상태 정보를 보관하는 서버는 클라이언트 정보 (상태)를 유지하여 요청들 사이에 공유되도록 한다.

사용자 A에 대한 요청은 무조건 서버 1로 가야만 인증이 통과된다는 문제가 발생한다.
대부분의 로드밸런서가 이를 지원 (같은 클라이언트로부터의 요청은 항상 같은 서버로 전송) 하기 위해 고정 세션 (Sticky session) 기능을 제공하지만, 이는 로드밸런서에 부담을 주며 로드밸런서 뒷단에 서버를 추가/제거하는 것도 어려워진다. 또한 이들 서버의 장애를 처리하기에도 복잡하다.
무상태 아키텍처

무상태 아키텍처는 사용자의 요청이 어떤 서버에서든 처리될 수 있는 구조이다.
상태가 필요할 경우 공유 저장소 (shared storage)로부터 데이터를 가져오면 되고, 상태 정보는 웹 서버로부터 물리적으로 분리되어 있기 때문에 단순하고 안정적이며 규모 확장이 용이하다.
데이터 센터
Active-Active for Multi-Regional Resiliency
With large scale and velocity there is increased chance of failure.
netflixtechblog.com
장애가 없는 일반적인 상황에서 사용자는 지리적 라우팅 (geoDNS Routing)에 의해 가장 가까운 데이터 센터로 안내된다.

이때 만약 특정 데이터 센터에 하나의 심각한 장애가 발생하면, 모든 트래픽은 장애가 없는 다른 데이터 센터로 전송된다.

이러한 순간에도 트래픽을 감당할 수 있는 다중 데이터 센터 아키텍처를 만들려면 다음과 같은 문제를 해결해야 한다.
- 트래픽 우회: 올바른 데이터 센터로 트래픽을 보내는 효과적인 방법을 찾아야 한다.
- 데이터 동기화: 데이터센터마다 각기 다른 데이터베이스를 사용하고 있는 상황이라면, 장애가 자동으로 복구되어 트래픽이 다른 데이터베이스로 우회된다 해도 해당 데이터센터에는 찾는 데이터가 없을 수 있다. 이를 해결하기 위해 대표적으로는 데이터를 여러 데이터센터에 걸쳐 다중화할 수 있다.
- 테스트와 배포: 시스템이 여러 데이터 센터를 사용하도록 되어 있다면 웹 사이트/애플리케이션을 여러 위치에서 테스트해 보는 것이 중요하다. 자동화된 배포 도구는 모든 데이터 센터에 동일한 서비스가 설치되도록 하는 데 중요한 역할을 한다.
아래 작성할 메시지 큐는 시스템의 컴포넌트를 분리하여 각 시스템이 독립적으로 확장될 수 있도록 해결해 주는 도구의 일종이다.
메시지 큐

정의
- 메시지의 유실 가능성을 낮추기 위한 내구성/재전송 메커니즘을 제공하는 비동기 통신 지원 컴포넌트
- 생산자 / 발행자 (producer / publisher) 입력 서비스가 메시지를 만들어 메시지 큐에 발행 (publish)
- 소비자 / 구독자 (consumer / subscriber) 서비스가 메시지를 구독한 뒤 그에 따른 동작을 수행
장점
- 서비스 / 서버 간 결합이 느슨해짐. 이에 따라 규모 확장성이 보장되어야 하는 안정적 애플리케이션 구성에 용이.
- 생산자는 소비자 프로세스가 다운되어 있어도 메시지 발행 가능, 소비자는 생산자 서비스가 가용한 상태가 아니더라도 메시지 수신이 가능함.
사용 예
- 사진 보정 애플리케이션 개발 시, 시간이 오래 걸리는 사진 보정 작업을 메시지 큐에 넣으면 사진 보정 작업 프로세스들이 작업을 메시지 큐에서 꺼내어 비동기적으로 완료할 수 있음. 생산자 / 소비자 규모를 각각 독립적으로 확장할 수 있음.
메시지 큐가 높은 내구성 유지와 메시지 유실 가능성을 최소화하는 전략은?
RabbitMQ
- 메시지 브로커로서 매우 정교한 신뢰성 옵션을 제공합니다.
- 유실 최소화 전략
- Durable Queues & Persistent Messages: 큐를 생성할 때 durable=true로 설정하고, 메시지를 보낼 때 delivery_mode=2로 설정하면 RabbitMQ는 메시지를 디스크에 저장합니다. 서버가 재부팅되어도 메시지는 살아남습니다.
- Publisher Confirms (발행자 확인): 생산자가 메시지를 보내면 RabbitMQ가 디스크에 잘 적었다고 Ack를 보내줍니다. 만약 브로커 내부에서 문제가 생겨 저장을 못 하면 Nack를 보내거나 아무 응답도 안 합니다. 이때 생산자는 재전송을 결정합니다.
- Consumer Acknowledgements (소비자 확인): 소비자가 메시지를 가져가서 처리를 완료한 후 Ack를 보내기 전까지 RabbitMQ는 큐에서 메시지를 완전히 지우지 않습니다. 처리 중 소비자가 죽으면 RabbitMQ는 메시지를 다시 큐에 넣고 다른 소비자에게 전달합니다.
- Quorum Queues (정족수 큐): 최신 RabbitMQ의 핵심입니다. Raft 합의 알고리즘을 사용하여 메시지를 여러 노드에 복제합니다. 이를 통해 데이터 안정성과 내구성을 크게 높입니다. 단, 과반수 노드 상실 등 극단 상황에서는 예외가 있을 수 있습니다.
AWS SQS
- 전통적인 메시지 큐의 클라우드 진화형입니다.
- 소비자가 메시지를 가져가는 순간, 큐에서 바로 지우지 않고 'Visibility Timeout (가시성 타임아웃)' 상태로 만듭니다. 다른 소비자가 못 보게 숨깁니다.
- 유실 최소화 전략
- Visibility Timeout: 소비자가 처리를 마치고 "나 다 했어!"라고 지움 (Delete) 요청을 보내야만 삭제됩니다. 만약 소비자가 처리 중에 죽으면, 타임아웃 뒤에 메시지가 다시 큐에 나타나 다른 서버가 처리할 수 있게 합니다.
- 내부 다중화: AWS에서 관리하는 관리형 분산 인프라에서 메시지를 여러 AZ에 중복 저장하기에 인프라 수준의 유실 걱정은 낮은 편입니다.
AWS EventBridge
- 큐가 아니라 이벤트 '버스'에 해당됩니다. 메시지를 보관하는 게 목적이 아니라 전달 (Route) 하는 게 목적입니다.
- 이벤트가 들어오면 규칙 (Rule)에 맞는 타깃 (Lambda, SQS 등)으로 쏴줍니다. 큐와 같은 장기 보관이 목적은 아니나, 타깃 전달 실패 시 기본 24시간 동안 지수 백오프 기반의 재시도를 위해 이벤트를 유지하며, 필요 시 DLQ 로 전달할 수 있습니다.
- 유실 최소화 전략
- Retry Policy: 타깃 서버가 응답하지 않으면 최대 24시간 동안 지수 백오프 (Exponential Backoff) 방식으로 재시도합니다.
- DLQ (Dead Letter Queue): 끝까지 전달에 실패한 이벤트는 유실되지 않도록 별도의 SQS (죽은 메시지 보관함)로 보내 나중에 관리자가 확인할 수 있도록 합니다.
AWS SQS / EventBridge에 대한 고민
최근 AWS 서비스 다운이 잦아지면서 동시에 그 기간 동안 발행되던 메시지들은 이후 다시 AWS가 재가동되었을 때 제대로 재전송이 될 수 있을까? 그렇다면 그 원리는 무엇일까?
- 메시지가 AWS에 도착하기 전 장애가 난 경우
- 상황: SQS나 EventBridge 엔드포인트 자체가 죽어서 전송 (Publish) 요청이 실패 (5xx 에러 또는 타임아웃)
- 결과: AWS는 메시지를 받은 적이 없으므로 재전송할 수 없음
- 대응: 이 책임은 발행자 (Producer)에게 있음.
- 로컬 재시도: 애플리케이션 레벨에서 지수 백오프 (Exponential Backoff)를 포함한 재시도 로직을 구현해야 함.
- 로컬 큐 / DB 저장: 정말 중요한 메시지라면 메시지를 로컬 DB에 먼저 저장 (Outbox Pattern) 하고, 전송 성공 확인 후 삭제하는 방식을 사용함.
- 메시지가 AWS에 성공적으로 접수된 후 장애가 난 경우
- 상황: 메시지는 잘 들어갔는데, 그 직후 AWS 서비스의 처리 로직이나 가용 영역 (AZ) 전체가 장애를 겪는 경우
- 결과: 유실되지 않음. SQS는 메시지를 여러 가용 영역 (AZ)에 중복 저장한 뒤 응답 (Ack)하며, 단일 AZ 장애로 메시지가 접근 불가능해지지 않도록 설계되어 있음. EventBridge 는 전달 실패 시 기본 정책에 따라 재시도하며, DLQ를 구성하면 최종 실패 이벤트를 별도로 보관할 수 있음.
- 보관 기간: SQS는 최대 14일, EventBridge는 최대 24시간 동안 메시지를 보관하며 재시도. 이 기간 내 서비스가 복구되면 메시지는 다시 흐르게 됨. (EventBridge에서 DLQ 설정이 되어있지 않을 경우 드롭될 가능성이 있음.)
SQS가 소비자가 메시지를 가져가면 가시성 타임아웃 상태로 만든다고 했는데, 만약 해킹을 당하여 메시지가 가로채졌다면 어떻게 대응할 수 있을까?
- 기본적으로 가로채기가 힘들다.
- TLS 암호화: 메시지는 이동 중에 HTTPS(TLS)로 암호화됨. 네트워크 중간에서 패킷을 가로채도 내용을 볼 수 없음.
- IAM 권한 제어: SQS 메시지를 읽으려면 반드시 유효한 sqs:ReceiveMessage 권한이 있는 IAM 자격 증명이 있어야 함.
- 만약 가로챘다면?
- 전송 / 저장 시 암호화 (SSE-KMS): SQS의 SSE (Server-Side Encryption)을 활성화하면, 해커가 SQS 접근 권한을 얻더라도 암호 해독에 필요한 KMS 키 권한이 없다면 메시지 내용은 암호화된 텍스트로 된다. 권한을 이중으로 분리하는 효과가 적용된다.
- VPC 엔드포인트 (PrivateLink) 사용: 메시지가 공용 인터넷을 타지 않고 AWS 내부망으로만 흐르게 하면 외부 해커가 중간에서 가로챌 경로 자체를 차단함.
- 애플리케이션 레벨 암호화 (Payload Encryption): 가장 강력한 방법. 메시지를 보내기 전 애플리케이션에서 직접 암호화하고, 소비자만 복호화 키를 가지는 방식. 이 경우 SQS 서비스 자체나 AWS 내부 인프라가 털려도 데이터는 안전함.
로그, 메트릭 그리고 자동화
사업 규모가 커지고 나면 로그, 메트릭, 자동화 도구에 필수적으로 투자해야 한다.
- 로그: 로그를 이용하면 시스템의 오류와 문제들을 보다 쉽게 찾을 수 있다. 서버 단위로 모니터링할 수도 있고, 로그를 단일 서비스로 모아주는 도구도 있다.
- 메트릭: 시스템의 현재 상태 또는 사업 현황에 관한 유용한 정보를 얻는 데 용이하다.
- 호스트 단위 메트릭: CPU, 메모리, 디스크 I/O 에 관한 정보 등
- 종합 메트릭: 데이터베이스 계층의 성능, 캐시 계층의 성능 등
- 핵심 비즈니스 메트릭: 일별 능동 사용자, 수익, 재방문 등
- 자동화: CI / CD 자동화 도구를 활용하면 개발자가 만드는 코드에 대해 자동으로 검증 과정을 거칠 수 있고, 빌드 / 테스트 / 배포 등의 절차 자동화로 개발 생산성 향상이 가능함.
"로그를 단일 서비스로 모아주는 도구"와 등장 배경
과거에는 서버 한 대에 접속해서 tail -f 명령어로 로그를 직접 봤지만, 지금처럼 서버가 수십 대인 스케일 아웃 환경에서는 로그가 흩어져 있어 찾기가 불가능에 가까움.
그래서 중앙 집중형 로그 수집 (Centralized Logging) 도구가 나오게 되었음.
- ELK
- E (Elasticsearch): 역색인 (Inverted Index) 구조를 활용하여 대규모 로그 데이터에서도 특정 키워드를 매우 빠르게 검색할 수 있음.
- L/F (Logstash / Fluentd): 여러 서버에서 로그를 긁어모아 가공하고 Elasticsearch로 던져주는 수집기
- K (Kibana): 저장된 로그를 대시보드 형태로 보여주는 시각화 도구
- Grafana Loki
- 최근 k8s 환경에서 급부상
- ELK는 로그 전체를 인덱싱 해서 저장 공간을 많이 차지하지만, Loki는 로그의 메타데이터만 인덱싱.
- 저장 비용 매우 저렴, 시각화 도구인 Grafana와 잘 어울려 메트릭과 로그를 한 화면에서 보기 좋음.
- 클라우드 네이티브 & 엔터프라이즈 도구
- AWS Cloudwatch Logs: 별도 설치 없이 설정만으로 로그를 모으고 보관할 수 있음.
- Datadog / New Relic: 로그뿐 아니라 서버 성능, 에러 추적까지 한 곳에서 관리.
- Splunk: 대기업이나 금융권에서 주로 사용하는 로그 분석 도구
로그 레벨의 계층 구조
Write debug level logs on error - print detailed logging only in exceptional circumstances - Conclusion AMIS Technology Blog
TL;DR: Collect debug information in an in-memory buffer that is printed to the output when an error occurs (and quietly discarded in all other cases). -
technology.amis.nl
| 레벨 | 위험도 | 용도 |
| verbose | 최하 | 상세한 모든 정보 (흐름 추적용) |
| log / debug | 낮음 | 개발 단계에서 확인용 (변수 값 등) |
| info | 보통 | 정상적인 상태 변화 알림 |
| warn | 높음 | 잠재적인 문제 (당장 안 죽지만 위험) |
| error | 최고 | 기능 수행 실패 (장애 발생)데이터베이스의 규모 확장 |
verbose
- 사용 시기: 로컬 개발 환경에서 아주 복잡한 로직을 디버깅할 때
- 주의: 운영 환경 (production)에서 이 레벨을 켜면 디스크 용량이 순식간에 꽉 차버려 다운될 여지가 있음
log / debug
- 사용 시기: "사용자 A의 요청이 컨트롤러에 도착함", "쿼리 결과로 N건이 반환됨" 등
- 특징: 보통 개발 환경에서는 켜두고, 운영 환경에서는 끄는 것이 원칙.
info
- 사용 시기: "서버가 8080 포트에서 시작됨", "사용자 로그인이 완료됨" 등
- 특징: 나중에 통계를 내거나 사용자 행동을 추적할 때 많이 활용됨
warn
- 사용 시기: "API 응답이 평소보다 너무 느림", "커넥션 풀이 90% 이상 차오름" 등
- 의미: 지금은 괜찮은데, 나중에 큰 문제가 될 수 있으니 미리 확인해 보라는 용도
error
- 사용 시기: "데이터베이스 연결 실패", "NullPointerException 발생" 등
- 대응: 이 레벨의 로그가 찍히면 즉시 관리자에게 알림 (Slack, PagerDuty 등) 이 가도록 설정해야 함.
백만 사용자, 그리고 그 이상
시스템의 규모를 확장하는 것은 지속적이고 반복적인 과정이다. 사용자가 늘어날수록 시스템을 최적화하고 더 작은 단위의 서비스로 분할해야 할 수도 있다.
핵심 키워드
이 키워드들을 공부할 때 단순히 정의만 외우지 마세요. 가장 중요한 질문은 "이 기술을 도입했을 때 얻는 이득 (Pros)과 지불해야 할 대가 (Cons)는 무엇인가?"입니다.
예를 들어, '샤딩'을 공부한다면 "데이터는 분산되지만(Pros), 대신 조인 쿼리가 복잡해지고 관리 포인트가 늘어난다(Cons)"는 식으로 Trade-off를 정리해 보시기 바랍니다.
1. 인프라 및 확장성 관련
- SPOF (Single Point of Failure, 단일 장애점): 시스템 전체를 중단시킬 수 있는 단 하나의 지점을 의미합니다. "우리 구조에서 여기가 죽으면 다 끝인가?"를 판단하는 기준이 됩니다.
- Auto-scaling (오토스케일링): 트래픽 변화에 따라 서버 자원을 자동으로 늘리거나 줄이는 기술입니다. 단순히 '늘린다'를 넘어, 어떤 지표(Metric)를 기준으로 늘릴 것인지 (CPU, Memory, Request Count 등)가 핵심입니다.
- High Availability (HA, 고가용성): 시스템이 오랜 시간 동안 지속적으로 정상 운영되는 능력입니다. 보통 '99.99% (Four Nines)' 같은 숫자로 표현하며, 이를 달성하기 위한 다중화 전략을 공부하게 됩니다.
2. 데이터 관리 및 DB 관련
- Replication Lag (복제 지연): Master DB에 써진 데이터가 Slave DB에 반영되기까지의 시차입니다. 이 찰나의 순간에 발생하는 데이터 불일치를 어떻게 다룰지가 실력을 결정합니다.
- Eventual Consistency (최종 일관성): 일시적으로는 데이터가 불일치할 수 있으나, 결과적으로는 모두 동일해진다는 개념입니다. 분산 시스템에서 성능과 가용성을 위해 완벽한 실시간 일관성을 포기하는 전략입니다.
- Consistent Hashing (일관된 해싱): 서버가 추가되거나 삭제될 때 데이터의 재배치를 최소화하는 알고리즘입니다. 로드 밸런싱과 DB 샤딩에서 매우 중요한 개념입니다.
3. 성능 최적화 및 안정성 관련
- Cache Aside vs Write-through: 캐시를 읽는 시점에 채울 것인지 (Aside), 데이터를 쓸 때 캐시도 같이 업데이트할 것인지 (Write-through)에 대한 전략입니다. 서비스의 읽기/쓰기 비율에 따라 선택이 달라집니다.
- CDN (Content Delivery Network): 단순히 '파일 저장소'가 아니라, 전 세계에 흩어진 에지 서버를 통해 지연 시간을 물리적으로 줄이는 메커니즘을 이해해야 합니다.
- Message Queue & Pub/Sub: 시스템 간의 비결합 (Decoupling)을 실현하는 도구입니다. Kafka나 RabbitMQ 같은 기술이 왜 대규모 시스템의 '혈관' 역할을 하는지 탐구해 보세요.
4. 설계 원칙 관련
- Idempotency (멱등성): 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질입니다. 네트워크 오류로 인해 같은 요청이 두 번 들어왔을 때 (예: 중복 결제), 시스템이 이를 어떻게 안전하게 처리할지에 대한 원칙입니다.
- Stateless vs Stateful: 서버가 클라이언트의 상태를 들고 있느냐 아니냐의 차이입니다. 현대적인 확장형 아키텍처가 왜 Stateless를 지향하는지 그 근거를 명확히 아는 것이 중요합니다.
심화 토론 주제
"심화 토론 주제"는 미리 작성해 두고 이에 대한 해결책과 답변은 다음 글에서 다룰 콘텐츠입니다.
1. 데이터베이스 복제 지연 (Replication Lag)과 '쓰기 후 읽기' 일관성
상황
사용자가 프로필 사진을 바꾸고 (Master DB), 즉시 자신의 프로필 페이지를 새로고침 (Slave DB) 했습니다. 그런데 방금 바꾼 사진이 아니라 옛날 사진이 나옵니다.
고민
- 사용자는 분명히 수정했는데 반영이 안 된 것처럼 보이면 서비스 신뢰도가 떨어집니다. 이 '복제 지연' 시간을 어떻게 극복할 수 있을까요?
- 모든 읽기를 Master에서 하면 해결되겠지만, 그럼 Slave를 둔 의미가 없지 않을까요? 어떤 데이터만 Master에서 읽게 강제해야 할까요?
2. 캐시 만료 (Cache Invalidation) 전략: "세상에서 가장 어려운 일"
상황
DB의 데이터는 수정되었는데, 캐시에는 여전히 옛날 데이터가 남아있습니다.
고민
- 캐시를 언제, 어떤 기준으로 지워야 (Invalidate) 할까요?
- '데이터가 수정될 때마다 캐시를 지운다'는 단순한 전략이 대규모 트래픽 환경에서 왜 위험할 수 있을까요? (힌트: Cache Stampede 현상)
- DB와 캐시의 데이터가 일시적으로 일치하지 않는 것을 어디까지 허용할 수 있나요?
3. 세션 서버 (Redis 등)가 죽는다면? (The Stateful vs Stateless Dilemma)
상황
모든 웹 서버를 무상태(Stateless)로 만들기 위해 세션을 공유 Redis에 저장했습니다. 그런데 이 Redis 서버가 장애로 다운되었습니다.
고민
- 모든 사용자가 로그아웃되는 대참사를 어떻게 막을 수 있을까요?
- Redis 자체를 다중화 (Sentinel, Cluster)하는 것 외에, 애플리케이션 레벨에서 취할 수 있는 조치는 무엇일까요?
- JWT (JSON Web Token)를 쓰면 이 문제가 완전히 해결될까요? JWT의 단점은 무엇인가요?
4. 샤딩 키 (Sharding Key) 선정의 운명적 결정
상황
사용자 ID(user_id)를 기준으로 샤딩을 했습니다. 그런데 특정 인플루언서 한 명의 데이터와 트래픽이 너무 커서 특정 샤드 서버만 터지려고 합니다.
고민
- 이른바 '유명인 문제(Celebrity Problem)' 혹은 '핫스팟(Hotspot)' 문제를 어떻게 해결해야 할까요?
- 한 번 정한 샤딩 키를 바꾸는 것이 왜 '재앙'에 가까운 작업인지, 그리고 이를 피하기 위한 '일관된 해시 (Consistent Hashing)'의 개념은 왜 중요한지 파악해 보세요.
5. 메시지 큐의 '정확히 한 번(Exactly Once)' 전달
상황
주문 서비스가 결제 완료 메시지를 큐에 넣었는데, 네트워크 오류로 인해 메시지가 두 번 발행되거나, 소비 (Consume) 하는 쪽에서 두 번 처리되었습니다.
고민:
- 사용자의 카드가 두 번 결제되는 상황을 시스템적으로 어떻게 방어할 것인가요?
- '멱등성 (Idempotency)'이라는 개념을 API와 컨슈머 설계에 어떻게 녹여낼 수 있을까요?
Reference
- 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 | 1. 사용자 수에 따른 규모 확장성
- Gemini
- ChatGPT
'[2026] System design & CS' 카테고리의 다른 글
| [가상 면접 사례로 배우는 대규모 시스템 설계 기초] 4. 처리율 제한 장치의 설계 (작성 중) (0) | 2026.02.21 |
|---|---|
| [가상 면접 사례로 배우는 대규모 시스템 설계 기초] 3. 시스템 설계 면접 공략법 (0) | 2026.02.16 |
| [가상 면접 사례로 배우는 대규모 시스템 설계 기초] 2. 개략적인 규모 추정 (0) | 2026.02.16 |