Ch.8 유사 사례와 키워드 정리¶
앞에서 OS, 네트워크, DB, 자료구조, 아키텍처 5개 카테고리의 키워드를 정리했다. 이번에는 카테고리를 조합해서 쓰는 실전 사례를 더 보고, 키워드를 정리한다.
8-6. 유사 사례¶
사례 1: 이미지 업로드 API가 느리고 서버가 죽는다¶
이미지 업로드 API가 있다. 사용자가 이미지를 올리면 리사이즈하고 DB에 메타데이터를 저장한다. 동시에 10명이 올리면 서버가 느려지다가 OOM으로 죽는다.
키워드를 모르면: "이미지 업로드가 느리고 서버가 죽어요. 도와주세요."
키워드를 알면:
이미지 리사이즈는 CPU Bound 작업이다 (OS).
동시 업로드 10건이 전부 메인 스레드에서 실행되고 있다.
1. ProcessPoolExecutor로 리사이즈를 별도 프로세스에서 처리 (OS)
2. 원본 이미지를 메모리에 전부 올리지 말고 streaming으로 chunk 단위 처리 (자료구조/메모리)
3. 리사이즈 완료 후 S3에 업로드하는 건 I/O Bound니까 asyncio로 처리 (네트워크)
4. 메타데이터 INSERT는 Transaction으로 묶어줘 (DB)
OS + 자료구조 + 네트워크 + DB 키워드를 한 프롬프트에 조합했다. AI는 이 프롬프트를 받으면 네 가지 최적화를 동시에 적용한 코드를 생성할 수 있다.
사례 2: 동시 주문에서 재고가 음수가 된다¶
재고 차감 API에서 동시 주문이 들어오면 재고가 0 아래로 떨어진다. 이건 Ch.5에서 봤다.
키워드를 모르면: "동시에 주문하면 재고가 마이너스가 돼요."
키워드를 알면:
Race Condition이다 (OS/동시성).
1. DB에서 SELECT ... FOR UPDATE로 Row-level Lock을 걸어줘 (DB/Isolation)
2. 혹은 Optimistic Locking으로 version 컬럼 추가해줘 (DB)
3. 트래픽이 많으면 Redis DECR로 Atomic 연산해줘 (자료구조/캐시)
동시성 (OS) + Lock (DB) + Atomic 연산 (자료구조) 키워드를 조합했다. 문제 해결 방향이 여러 개이고, 상황에 따라 다른 방법을 쓸 수 있다는 것도 키워드를 알아야 보인다.
사례 3: 검색 자동완성이 갑자기 느려졌다¶
검색창 자동완성이 평소 30ms였는데, 사용자 수가 늘면서 200ms로 느려졌다.
키워드를 모르면: "자동완성이 느려졌어요. 캐시 붙여볼까요?"
키워드를 알면:
검색어 자동완성은 prefix 매칭이라 LIKE 'keyword%' 쿼리가 많다.
1. EXPLAIN으로 인덱스 사용 여부 확인 (DB)
2. 인덱스가 안 먹는 LIKE라면 (앞에 %가 붙는 경우) 풀스캔 (DB)
3. 검색 데이터가 메모리에 들어갈 크기라면 Trie 자료구조로 메모리에서 처리 (자료구조)
4. 메모리 부담이 크면 Elasticsearch 같은 검색엔진 분리 (아키텍처)
5. 어떤 경우든 LRU Cache로 자주 찾는 prefix는 별도 캐싱 (자료구조)
DB → 자료구조 → 아키텍처로 점점 큰 단위의 해법까지 한 프롬프트에 단계별로 제시했다. 키워드를 안다는 건 "옵션을 다 펼쳐놓고 비교할 수 있다"는 뜻이다.
키워드 카테고리 결정 가이드¶
증상을 들었을 때 어디부터 의심할지를 빠르게 잡기 위한 가이드다. 매번 결정 트리를 그릴 수는 없으니, 머리에 넣어둘 수 있는 압축된 형태로 정리한다.
증상별 1순위 카테고리¶
| 증상 | 1순위 의심 카테고리 | 첫 진단 도구 | 자주 등장하는 키워드 |
|---|---|---|---|
| CPU 100% | OS (CPU Bound) | htop, py-spy | GIL, ProcessPool, CPU Bound |
| 메모리 증가 | OS (메모리) | top RSS, tracemalloc | OOM, Heap, RSS, leak |
| 응답 느림 + CPU/메모리 여유 | 네트워크 또는 DB | tracing, EXPLAIN | RTT, N+1, Slow Query |
| 간헐적 멈춤 | OS/DB 동시성 | thread dump, SHOW PROCESSLIST | Deadlock, Lock |
| Connection 에러 | 네트워크/DB | netstat, pool status | Connection Pool, TIME_WAIT |
| 데이터 꼬임 | DB (Isolation) | 트랜잭션 로그 | Race Condition, Isolation Level |
| 1회만 느림(이후 빠름) | 네트워크 | curl -v -w | DNS, TLS Handshake, warm-up |
| 배포 단위가 무거움 | 아키텍처 | 모듈 의존성 분석 | Monolith, Service Boundary |
| 외부 장애가 우리까지 전염 | 아키텍처 | error rate, dependency map | Circuit Breaker, Bulkhead |
| 동일 코드인데 가끔만 실패 | OS/DB 동시성 | 재현 테스트 | Race Condition, Deadlock |
"어느 카테고리부터 보느냐" 흐름도¶
graph TD
S["모호한 증상 한 줄"] --> M{"리소스 지표 확인"}
M -->|"CPU 높음"| OSC["OS<br/>(CPU Bound, GIL)"]
M -->|"메모리 높음"| OSM["OS<br/>(OOM, Heap leak)"]
M -->|"I/O Wait"| IO{"어디 I/O?"}
M -->|"전부 여유"| APP{"애플리케이션 레벨"}
IO -->|"DB"| DB["DB<br/>(Slow Query, N+1)"]
IO -->|"파일"| OSF["OS<br/>(System Call, fsync)"]
IO -->|"네트워크"| NET["네트워크<br/>(RTT, Connection Pool)"]
APP -->|"로직 무거움"| DS["자료구조<br/>(O(n^2), List vs Set)"]
APP -->|"구조 문제"| ARCH["아키텍처<br/>(Boundary, CQRS, EDA)"]
OSC --> P["카테고리 키워드 +<br/>진단 결과 →<br/>프롬프트"]
OSM --> P
DB --> P
OSF --> P
NET --> P
DS --> P
ARCH --> P
이 흐름이 본문에서 다섯 명의 개발자가 "API가 느리다" 한 줄에서 각자 다른 카테고리로 출발한 사고 과정의 일반화다.
그래서 실무에서는 어떻게 하는가¶
키워드 사전을 "외우는" 게 아니다¶
이 챕터에서 정리한 키워드 표를 전부 외울 필요는 없다. 중요한 건 두 가지다.
- "이런 카테고리의 키워드가 있다"는 걸 안다
- 문제 상황에서 "이건 어느 카테고리인가?"를 판단할 수 있다
이 두 가지만 되면, 구체적인 키워드는 검색하거나 AI에게 물어보면 된다. Ch.7에서 봤듯이 "이 현상의 CS 용어가 뭔가?"부터 물어보는 것도 좋은 전략이다.
진단 → 카테고리 → 키워드 → 프롬프트¶
실무에서의 흐름을 정리하면 아래와 같다.
- 증상을 확인한다 ("느리다", "죽는다", "꼬인다")
- 진단 도구로 원인을 좁힌다 (htop, EXPLAIN, netstat, tracemalloc)
- 원인이 어느 카테고리인지 판단한다 (OS? DB? 네트워크? 자료구조? 아키텍처?)
- 해당 카테고리의 키워드를 프롬프트에 넣는다
- AI의 답을 CS 지식으로 검증한다 (이건 Ch.9에서 다룬다)
이 흐름이 Ch.7에서 말한 "진단 먼저, 프롬프트 나중"의 구체적인 모습이다.
좋은 프롬프트의 4요소¶
본문 사례들을 일반화하면 좋은 프롬프트에는 보통 아래 네 가지가 들어 있다.
- 증상 (현재 무엇이 문제인가)
- 진단 결과 (어떤 도구로 어떤 수치를 봤는가)
- 카테고리 키워드 (그게 어떤 CS 개념에 해당하는가)
- 원하는 해법 방향 (어떤 패턴으로 풀고 싶은가)
키워드가 빠지면 AI는 카테고리를 못 잡는다. 진단 결과가 빠지면 AI는 가장 흔한 일반론을 준다. 두 개가 같이 있어야 정확한 답이 나온다.
면접 Q&A (이 챕터로 답할 수 있는 단골 질문)¶
AI에게 줄 CS 키워드를 어떻게 분류해서 외워야 하나요?
카테고리별로 묶어서 안다. 본문에서 OS, 네트워크, DB, 자료구조, 아키텍처 5개 카테고리로 정리했다. 키워드를 전부 외울 필요는 없고, "이런 카테고리가 있다"는 것과 "이 문제는 어느 카테고리인가"만 판단할 수 있으면 된다. 구체 키워드는 검색하거나 AI에게 다시 물어보면 된다.
문제가 어느 카테고리인지 어떻게 판단하나요?
진단 도구로 좁힌다. htop이 빨갛다면 OS(CPU/메모리), EXPLAIN이 Full Table Scan이면 DB, netstat에 TIME_WAIT가 쌓이면 네트워크, 로직이 O(n^2)이면 자료구조 쪽이다. 본문에서 정리한 "증상 → 진단 도구 → 카테고리 → 키워드 → 프롬프트" 흐름과 결정 트리를 참고하면 된다.
같은 문제를 여러 카테고리 키워드로 풀 수 있는 경우가 있나요?
있다. 재고 차감 동시성 문제만 봐도 DB의 SELECT FOR UPDATE(Row-level Lock), DB의 Optimistic Locking(version 컬럼), 자료구조/캐시의 Redis DECR(Atomic 연산)으로 풀 수 있다. 검색 자동완성도 DB 인덱스, 자료구조(Trie), 아키텍처(검색엔진 분리), 자료구조(LRU Cache)까지 옵션이 펼쳐진다. 어느 카테고리 키워드를 쓰느냐에 따라 해결 방향이 갈리고, 트래픽 규모나 운영 비용 같은 상황에 따라 선택이 달라진다.
여러 카테고리 키워드를 한 프롬프트에 조합해도 되나요?
그게 오히려 권장된다. 본문 이미지 업로드 사례처럼 CPU Bound는 ProcessPoolExecutor(OS), 큰 파일은 streaming(자료구조), S3 업로드는 asyncio(네트워크), 메타데이터는 Transaction(DB)으로 한 프롬프트에 네 카테고리를 묶으면 AI가 네 가지 최적화를 동시에 반영한 코드를 만들 수 있다.
키워드 사전을 통째로 외우라는 건가요?
아니다. 본문에서 두 가지만 강조했다. 1) "이런 카테고리의 키워드가 있다"는 인지, 2) 문제 상황에서 카테고리를 판단하는 능력. 구체 키워드는 그때그때 찾으면 된다. Ch.7에서 본 "이 현상의 CS 용어가 뭔가?"부터 묻는 전략이 여기서도 유효하다.
키워드를 모를 때 진단부터 하라고 하는데, 진단 도구도 모르면 어떡하나요?
카테고리만 알면 도구가 따라온다. CPU/메모리 의심이면 htop, DB 의심이면 EXPLAIN, 네트워크 의심이면 netstat, 메모리 누수 의심이면 tracemalloc. 도구 이름도 카테고리 키워드의 일부로 알아두면 AI에게 "OS 레벨 진단 도구 알려줘" 식으로 한 단계 더 좁힌 질문을 할 수 있다.
좋은 프롬프트와 나쁜 프롬프트의 결정적 차이는 뭔가요?
"진단 결과 + 카테고리 키워드"가 들어 있느냐다. "API 느려요"는 나쁜 프롬프트, "EXPLAIN 결과 Full Table Scan이고 N+1로 쿼리 수가 50개"는 좋은 프롬프트다. AI는 키워드만큼만 좁혀진 답을 준다. 키워드가 없으면 "느릴 때 흔히 시도하는 일반론"을 늘어놓는다.
AI가 줄 답을 카테고리 키워드 없이 검증할 수 있나요?
어렵다. AI가 "Redis 캐시 붙이세요"라고 답했을 때, 그게 진짜 답인지 알려면 진짜 원인이 DB 인덱스 미설정인지 ORM N+1인지 외부 호출 누락인지 등 카테고리를 알아야 비교가 된다. 키워드는 프롬프트를 잘 쓰기 위해서뿐 아니라 답을 검증하기 위해서도 필요하다. 이건 Ch.9에서 본격적으로 다룬다.
오늘의 키워드 정리¶
이번 챕터는 기존 키워드를 "프롬프트에서 어떻게 활용하는가" 관점으로 재정리한 챕터다. 새 키워드보다 기존 키워드의 활용법이 핵심이다.
새 키워드 (이번 챕터에서 처음 등장)¶
DNS Resolution (DNS 해석)
도메인 이름(예: api.example.com)을 IP 주소로 변환하는 과정이다. 첫 번째 요청이 유독 느리다면 DNS Resolution에 시간이 걸린 것일 수 있다. OS나 애플리케이션 레벨에서 DNS 캐시를 확인하면 된다. Ch.6에서 다룬 TCP Connection 수립 이전에 일어나는 단계다.
Load Balancing (부하 분산)
들어오는 트래픽을 여러 서버에 분산하는 기법이다. Round Robin(순서대로), Least Connections(연결 수가 가장 적은 서버에), IP Hash(같은 IP는 같은 서버에) 등 여러 알고리즘이 있다. Ch.19에서 Scale-Out과 함께 자세히 다룬다.
N+1 Problem (N+1 문제)
ORM에서 연관 데이터를 조회할 때, 메인 쿼리 1번 + 연관 데이터 쿼리 N번이 발생하는 패턴이다. 10건의 주문을 조회하면서 각 주문의 사용자 정보를 1건씩 따로 가져오면 총 11번의 쿼리가 실행된다. Ch.13에서 자세히 다룬다.
Time Complexity (시간 복잡도)
알고리즘의 입력 크기(n) 대비 실행 시간이 어떻게 증가하는지를 나타내는 지표다. O(1)은 입력 크기에 관계없이 일정, O(n)은 비례, O(n^2)은 제곱으로 증가한다. "이 코드가 왜 느린지"의 근본 원인을 설명하는 도구다. Ch.10에서 자세히 다룬다.
Circuit Breaker (서킷 브레이커)
외부 서비스가 장애일 때 계속 호출하지 않고, 일정 실패 횟수 이후 호출 자체를 차단하는 패턴이다. 전기 회로의 차단기(breaker)에서 이름을 따왔다. 장애가 내 서비스로 전파되는 걸 막아준다. Ch.22에서 분산 시스템과 함께 다룬다.
CQRS (Command Query Responsibility Segregation)
읽기(Query)와 쓰기(Command)를 분리하는 아키텍처 패턴이다. 읽기가 쓰기보다 압도적으로 많은 서비스에서, 읽기 전용 DB(Read Replica)를 두고 쓰기 DB와 분리하는 게 대표적인 구현이다. Ch.19에서 Scale-Out과 함께 다룬다.
Bulkhead (격벽)
선박의 격벽처럼, 한 부분에 문제가 생겨도 전체로 번지지 않게 자원 풀(스레드, 커넥션 등)을 분리하는 패턴이다. 외부 호출별로 ThreadPool을 분리해 두면, 한 외부 의존성이 hang에 빠져도 다른 호출은 영향을 받지 않는다. Ch.22에서 다룬다.
Idempotency (멱등성)
같은 요청을 여러 번 처리해도 결과가 같은 성질이다. 메시지 큐 컨슈머나 결제/주문처럼 중복 처리되면 안 되는 작업에 필수다. 보통 요청 ID나 자연 키로 중복 여부를 판별한다. Ch.22 메시지 처리 부분에서 다시 나온다.
Materialized View (구체화된 뷰)
쿼리 결과를 미리 계산해 테이블처럼 저장한 뷰다. 매번 무거운 집계를 다시 돌리지 않고 결과만 읽으면 된다. 갱신은 주기적 REFRESH로 처리한다. Ch.14~16에서 인덱스/캐시 전략과 같이 다룬다.
재등장 키워드¶
이번 챕터에서 다시 등장한 키워드가 많다. 전부 "프롬프트에서의 활용"이라는 새로운 관점으로 다뤘다.
| 키워드 | 최초 등장 | 이번 챕터에서의 역할 |
|---|---|---|
| System Call | Ch.2 | OS 키워드 표 - 로그 I/O 진단 |
| CPU Bound / I/O Bound | Ch.3 | OS 키워드 표 - 최적화 방향 결정 |
| GIL | Ch.3 | OS 키워드 표 - Thread가 CPU에서 안 빨라지는 이유 |
| Context Switch | Ch.3 | OS 키워드 표 - 스레드 과다 진단 |
| Process / Thread | Ch.4 | OS 키워드 표 - 실행 단위와 자원 |
| Memory Layout / Virtual Memory / Page Fault | Ch.4 | OS 키워드 표 - 메모리 진단 |
| Race Condition / Deadlock / Mutex / Semaphore | Ch.5 | OS 키워드 표 - 동시성 문제 진단 |
| OOM | Ch.4 | OS 키워드 표 - 메모리 문제 진단 |
| TCP/IP, 3-way Handshake, Socket(fd) | Ch.6 | 네트워크 키워드 표 - 연결 기초 |
| Connection Pool, Keep-Alive, TIME_WAIT | Ch.6 | 네트워크 + DB 양쪽에서 등장 |
| Latency / Throughput / RTT | Ch.2, Ch.6 | 네트워크 키워드 표 - 성능 지표 |
| Prompt Engineering | Ch.7 | 키워드를 프롬프트에 녹이는 구체적 방법 |
본문에서 맛보기로 언급된 키워드(N+1, Isolation Level, Bloom Filter, CQRS, Circuit Breaker, Trie, Materialized View, Bulkhead 등)는 이후 해당 챕터에서 자세히 다룬다. 지금은 "이런 키워드가 있고, 이런 상황에서 AI에게 줄 수 있다" 정도로만 보면 된다.
키워드 연관 관계¶
graph LR
PE["Prompt<br/>Engineering<br/>(Ch.7)"] --> CAT["키워드<br/>카테고리"]
CAT --> OS["OS 키워드"]
CAT --> NET["네트워크 키워드"]
CAT --> DB["DB 키워드"]
CAT --> DS["자료구조 키워드"]
CAT --> ARCH["아키텍처 키워드"]
OS --> SC["System Call"]
OS --> CB["CPU/I/O Bound"]
OS --> GIL["GIL"]
OS --> RC["Race Condition"]
OS --> MEM["OOM/Heap"]
NET --> CP["Connection Pool"]
NET --> KA["Keep-Alive"]
NET --> DNS["DNS Resolution"]
NET --> LB["Load Balancing"]
NET --> TLS["TLS Handshake"]
DB --> IDX["Index"]
DB --> N1["N+1 Problem"]
DB --> IL["Isolation Level"]
DB --> SQ["Slow Query"]
DB --> SH["Sharding"]
DS --> TC["Time Complexity"]
DS --> BF["Bloom Filter"]
DS --> TR["Trie/Tree"]
DS --> LRU["LRU Cache"]
ARCH --> MS["Microservice"]
ARCH --> ED["Event-Driven"]
ARCH --> CBR["Circuit Breaker"]
ARCH --> CQRS["CQRS"]
ARCH --> BH["Bulkhead"]
여기까지의 지도 (누적)¶
위의 "키워드 연관 관계"가 이번 챕터 안의 연결이라면, 아래는 Ch.1부터 지금까지 도메인별로 쌓인 키워드다. 빈 레인은 앞으로 그 챕터에서 채워진다 — 다 채워지는 과정이 곧 이 강의의 지도다. (이번 챕터에서 추가된 키워드는 굵게.)
| 도메인 | 누적 키워드 (등장 챕터) |
|---|---|
| OS·실행 | System Call·Kernel·Mode Switch·Buffer·File Descriptor(2) → CPU/IO Bound·GIL·Event Loop·async/await·Thread/Process Pool·Context Switch·IPC(3) → Process·Thread·PCB/TCB·Memory Layout·Virtual Memory·Page·TLB·OOM(4) |
| 동시성·트랜잭션 | Race Condition·Critical Section·Mutex/Lock·Deadlock·Semaphore·Starvation(5) |
| 자료구조·알고리즘 | — (Ch.10에서 시작) |
| DB·캐시·튜닝 | — (Ch.13에서 시작) |
| 네트워크·분산 | TCP/IP·Socket·3/4-Way Handshake·Connection Pool·Keep-Alive·TIME_WAIT(6) |
| 설계·AI·품질·보안 | Computational Thinking·CS 키워드(1) → LLM·Token·Context Window·Hallucination·Prompt Engineering(7) → 키워드 사전·DNS·Load Balancing·Circuit Breaker·CQRS(8) |
다음 챕터(Ch.9)에서는 "AI가 만든 코드를 CS 관점에서 리뷰하는 방법"을 다룬다. 키워드를 알아서 좋은 프롬프트를 쓸 수 있게 됐는데, AI의 답이 정말 맞는지는 어떻게 검증하는가?