2. 나쁜 설계의 탄생 과정

1. 목표
학습 목표
- 동작하는 프로그램과 운영에서 살아남는 시스템의 차이를 이해한다.
- 성능 문제는 코드의 문제가 아니라, 분석·설계 단계에서의 사고 오류에서 시작됨을 체감한다.
- 시스템은 코드 위에서 동작하는 것이 아니라, 현실의 운영 환경 위에서 동작한다는 사실을 인식한다.
- 현실을 이해하지 못한 설계는, 구현이 아무리 잘되어도 반드시 실패한다는 점을 이해한다.
개발자 관점 이해

- 초보자의 시선
- 카페 키오스크 앱을 만든다
- 모바일 주문 앱을 만든다
- 화면을 만들고 API를 연결한다
- 실제 개발자의 관점
- 카페 주문 업무 전체를 디지털 시스템으로 옮긴다
즉, 개발자가 만드는 것은
단순한 앱이 아니라 현실의 업무를 대신 수행하는 디지털 시스템이다.
2. 나쁜 설계의 탄생 과정
└─ Step 1. 현실 매장 환경 파악 (출발점)

매장 운영 환경
- 최전선 (손님용): 키오스크 5대가 매장 입구에서 손님을 맞이합니다.
- 후방 (직원용): 주문 모니터링 및 관리자 화면(POS) 1~2대가 주방에 있습니다.
- 원격 (손님용): 주변 직장인들이 도착 전에 미리 주문하려고 모바일 앱을 켭니다.
요약
- 키오스크 + 모바일 앱이 동시에 사용됨
- 사용자 유형이 여러 갈래
- 트래픽이 몰리는 명확한 시간대가 존재
결론
- 시스템은 평균적인 하루가 아니라, 가장 몰리는 순간을 기준으로 설계되어야 함
└─ Step 2. 잘못된 데이터 트레픽 분석
(1) 문제의 시작
- 주문은 얼마나 들어올까?
- DB가 감당 가능할까?
- 👉 질문 자체는 맞지만, 관점이 부족함
(2) 데이터만 보고 내린 성급한 결론

- 단순 계산
- 점심시간 10분 동안 주문 300건
- 10분 300건 → 1분 30건 → 2초에 1건
- 👉 여기서 이미 사고 오류가 시작됨
- 이 정도면 DB 하나로 충분하네
별로 부담 없어 보이는데?
- 이 정도면 DB 하나로 충분하네
- 오해
- 트래픽이 수도꼭지 물처럼 일정하게 흐를 것이다
- 하루 1,000명이면 시간당 40명 정도겠지
- 현실
- 트래픽은 예고 없이 폭발(Spike)합니다.
- 점심시간(예: 12시 5분경) 직장인들이 동시에 몰리는 1~2분의 짧은 시간에 트래픽이 급증하는 '스파이크(Spike) 현상'이 발생합니다. 이때 순간 트래픽은 평균치의 수십 배에 달합니다.
- 👉 핵심
- 시스템 장애는 '평균 부하'가 아니라 '최대 순간 부하(Peak Load)'를 견디지 못할 때 발생합니다.
- 이는 데이터의 총량만 보고 시간대별 밀집도를 간과했을 때 흔히 범하는 오류입니다
- 🎯 팩트 체크 보완
- 실무에서도 성능 테스트는 평균 TPS가 아니라 Peak TPS / Burst 상황을 기준으로 설계·검증
- 이는 데이터만 보고 현실 운영 시나리오를 분석하지 않을 때 개발자들이 가장 흔히 저지르는 오류 중 하나
(3) 사용자 행동 패턴과 숨겨진 트래픽(Hidden Traffic) 미파악

- 오해
- 손님이 키오스크 앞에서 실제로 수행하는 행동 시나리오를 누락
- 손님이 결제할 때만 DB가 사용되겠지?
- 현실
- 사용자의 모든 손짓이 트래픽입니다:
- 메뉴 클릭
- 스크롤
- 옵션 변경
- 장바구니 담기/취소
- 이 모든 과정에서 DB를 호출(나쁜 설계 기준)
- 사용자의 모든 손짓이 트래픽입니다:
- 시스템 특성
- 키오스크는 아무도 만지지 않아도
품절 정보 갱신을 위해 1~2초마다 Polling - 키오스크 5대 × 초당 3회 조회 = 15 QPS 기본 부하
- 여기에 모바일 앱 사용자가 더해짐
- 키오스크는 아무도 만지지 않아도
- 👉 핵심
- 손님 1명이 결제 버튼을 누르기 전까지,
메뉴 조회 요청은 이미 수십 번 발생해 있습니다
- 손님 1명이 결제 버튼을 누르기 전까지,
- 🎯 팩트 체크 보완
- 트래픽 분석은 “주문 건수”가 아니라
"사용자 행동 흐름"을 기준으로 계산해야 합니다.
- 트래픽 분석은 “주문 건수”가 아니라
└─ Step 3. 잘못된 분석 기반의 나쁜 설계

(1) 잘못된 분석에서 도출된 위험한 결론
오해
- "에이, 메뉴 조회 그까짓 거 얼마나 된다고. 가볍잖아."
- "그냥 주문 건수만 버티면 되는 거 아냐?" "지금 테스트해보니 DB 하나로도 충분한데?"
- "굳이 캐시까지 도입해서 복잡하게 만들 필요가 있을까? 그건 오버 엔지니어링이야."
👉 핵심
- 문제는 개발자가 틀린 데이터를 본 것이 아니라,
데이터를 현실 운영 시나리오에 대입해보지 않고 '책상 위에서만 해석'했다는 점입니다.
(2) 최종적으로 만들어진 나쁜 설계
나쁜 설계 결정 목록 (Checklist of Doom)
- 모든 요청을 DB로 직접 전달
- 메뉴·옵션처럼 변하지 않는 데이터도 매번 DB 조회
- 캐시(Cache) 미도입
- 읽기(Read) / 쓰기(Write) 분리 없음
- DB가 모든 트래픽의 단일 진입점(Single Bottleneck)
이 구조의 위험성
- 논리적으로는 문제 없어 보임
- 테스트 환경에서는 정상 동작
- 평상시에는 아무 문제 없음
👉 그래서 더 위험한 구조
3. 서비스 붕괴
└─ 잘못된 분석과 설계는 어떤 결과를 만들어내는가?

3-1. 붕괴의 시작: 트래픽 폭증 트리거 발생
- 점심시간(12:00~12:30)이 되자
- 키오스크와 모바일 앱에서 동시에 메뉴를 탐색하는 손님이 몰립니다.
- 실제 발생하는 트래픽
- 메뉴 조회(단순 구경) 요청이 폭포수처럼 쏟아짐
- 키오스크 + 모바일 앱에서 조회(Read) 트래픽 폭증
- 모든 조회 요청이 중간 방어 없이 DB로 직행
- 👉 이 순간, 설계 단계에서 숨겨졌던 결함이 드러나기 시작합니다.
3-2. 설계 결함 있는 시스템의 동작
- 대량의 조회 요청이 아무 여과 없이 DB를 직접 타격
- 조회 트래픽을 흡수해 줄 완충 지점이 없음
- DB는 고장 나지 않았지만,
- 처리해야 할 일이 한꺼번에 몰림
- 응답 시간이 급격히 증가
- 요청 대기 큐가 길어짐
- 병목(Bottleneck) 발생
- 👉 DB는 설계대로 너무 많은 일을 떠안았을 뿐입니다.
3-3. 치명적 결과: 중요한 기능의 마비
- 결정적인 순간
- 어떤 손님이 결제를 시도합니다.
- 결제는 매출과 직결되는 가장 중요한 요청입니다.
- 그러나…
- 시스템이 단일 경로(DB 직행 구조)로 설계되어 있음
- 결제 요청 역시 수많은 조회 요청 뒤에 줄을 섬
- 조회(Read) 트래픽이 결제(Write) 트래픽을 가로막음
- 👉 “구경(Read)” 때문에 “매출(Write)”이 막히는 상황 발생
3-4. 최종 붕괴: 서비스 중단과 비즈니스 손실
- 기술적 결과
- 결제 요청 타임아웃(시간 초과)
- 키오스크 화면 멈춤
- 모바일 앱 응답 없음
- 관리자(POS) 화면에서도 주문 확인 불가
- 현실 세계의 결과
- 손님 이탈
- 주문 포기
- 매출 손실
- 현장 운영 혼란
4. 캐시를 통한 구조 개선
└─ 원인 → 사고 전환 → 해결 구조

4-1. 문제의 본질 재정의
- 문제의 본질
- 현실의 운영 환경과 트래픽의 성격을 구분하지 않은 분석 설계 입니다.
- 시스템 붕괴 원인
- 변하지 않는 데이터(메뉴, 가격, 옵션)를
매번 가장 비싸고 느린 자원인 DB에서 직접 꺼내왔다 - 즉, 가벼운 요청(Read)에 비싸고 무거운 자원(DB)을 계속 사용한 구조
- 변하지 않는 데이터(메뉴, 가격, 옵션)를
이것이 시스템을 무너뜨린 진짜 원인입니다.
4-2. 캐시 도입 후 시스템의 변화
- 캐시는 성능을 높이기 위한 트릭이 아닙니다.
- 캐시는 현실의 트래픽 구조에 대응하기 위해 자주 사용하는 데이터를 더 가까운 곳에 미리 저장해 두는 공간입니다.
역할
- 캐시는 시스템의 가장 앞단에 위치하여 DB를 보호하는 강력한 방패 역할을 합니다.
- 캐시는 DB를 대신해 다음 요청을 처리합니다.
- 메뉴 목록 제공
- 가격 정보 제공
- 옵션 정보 제공
- 👉 손님이 메뉴를 구경할 때마다 매번 DB까지 가지 않고 캐시에서 즉시 응답합니다.
4-3. 핵심
- 캐시는 현실 트래픽을 감당하기 위한 필수 구조이지만
“무조건 있으면 좋은 옵션”이 아니다. - 성능 문제의 해답은
코드가 아니라 구조와 사고 방식에 있다 - DB를 빠르게 만드는 것이 아니라,
DB가 해야 할 일을 줄이는 것이 진짜 성능 설계다.
5. 추가 분석/설계 고려 사항 리스트
1. 현실 문제 분석 (Problem Understanding)
- 반드시 먼저 답해야 할 질문
- 지금 해결하려는 현실의 문제는 무엇인가
- 이 문제는 왜 발생했는가
- 누가 이 문제로 불편을 겪고 있는가
- 어떤 상태가 되면 문제가 해결되었다고 볼 수 있는가
- 흔한 실수
- “무엇을 만들까?”부터 시작함
- “어떤 기술을 쓸까?”부터 고민함
2. 사용자 및 이해관계자 분석 (Stakeholder Analysis)
- 사용자 유형 식별
- 일반 사용자(손님)
- 내부 사용자(직원, 관리자)
- 운영자(점주, 회사)
- 시스템 사용자(외부 연동 시스템)
- 고려 사항
- 사용자마다 행동 패턴이 다르다
- 사용자마다 중요한 기능이 다르다
- 사용자마다 장애 발생 시 영향도가 다르다
3. 업무 흐름 분석 (Business Flow)
- 현실 업무 흐름 파악
- 업무가 어떤 순서로 진행되는가
- 사람이 개입하는 단계는 어디인가
- 자동화 가능한 지점은 어디인가
- 예시(카페)
- 주문 → 결제 → 제조 → 픽업
- 취소/환불은 언제 발생하는가
- 혼잡 시 직원은 무엇을 우선하는가
4. 사용자 행동 분석 (User Behavior)
- 반드시 고려할 점
- “결제” 이전에 발생하는 모든 행동
- 클릭, 스크롤, 옵션 변경, 재탐색
- 가만히 있어도 발생하는 시스템 요청
- 핵심 인식
- 사용자 행동 = 트래픽
- 주문 1건은 단일 이벤트가 아니다
5. 트래픽 특성 분석 (Workload Pattern)
- 분석해야 할 요소
- 평균 트래픽 vs 피크 트래픽
- Burst / Spike 발생 구간
- Read-heavy / Write-heavy 여부
- 실무 팩트
- 성능 문제는 평균이 아니라 최대 순간 부하에서 발생
- 대부분의 서비스는 조회(Read)가 압도적으로 많다
6. 데이터 특성 분석 (Data Characteristics)
- 데이터 분류
- 자주 변하지 않는 데이터 (메뉴, 설정값)
- 자주 조회되는 데이터
- 자주 쓰이는 데이터
- 중요하고 무거운 데이터 (결제, 주문)
- 핵심 질문
- 이 데이터는 얼마나 자주 변하는가
- 이 데이터는 얼마나 자주 조회되는가
- DB까지 갈 필요가 있는가
7. 성능 설계 관점 (Performance Design)
- 반드시 명시해야 할 것
- 응답 시간 목표(ms)
- 피크 타임 처리량(TPS/QPS)
- 동시 사용자 수 가정
- 중요한 인식
- “빠르다”는 말은 의미 없다
- 숫자로 정의되지 않은 성능은 설계되지 않은 것
8. 장애 시나리오 분석 (Failure Scenarios)
- 반드시 가정해야 할 상황
- 네트워크 단절
- 외부 결제 실패
- 서버 일부 장애
- 중복 요청/재시도 폭주
- 핵심 인식
- 장애는 예외가 아니라 일상
- 장애를 가정하지 않은 시스템은 반드시 실패한다
9. 확장성과 운영 고려 (Scalability & Operation)
- 운영 관점 질문
- 사용자가 늘어나면 어떻게 되는가
- 매장이 늘어나면 구조는 유지되는가
- 장애 발생 시 부분 운영이 가능한가
10. 하드웨어 및 자원 전제 (System Resource Assumptions)
- 반드시 연결해서 생각할 것
- CPU 코어 수
- 메모리 크기
- 디스크 I/O 비용
- 네트워크 지연
- 핵심 메시지
- 소프트웨어는 하드웨어 위에서 동작한다
- 자원 전제 없는 설계는 공중에 떠 있다
11. 설계 결정의 근거 명시
- 반드시 문서로 남겨야 할 것
- 왜 이 구조를 선택했는가
- 왜 캐시를 넣었는가 / 뺐는가
- 왜 이 방식이 현실에 맞는가
6. 카페/키오스크 실습용 설계 템플릿
└─ 카페 주문 업무 전체를 디지털 시스템으로 설계·구현하는 연습
0. 실습 목표 정의 (가장 중요)
- 실습 목표
- 키오스크/모바일 앱을 만드는 것이 아니라
- 카페 주문 업무 전체를 디지털 시스템으로 설계하는 경험을 한다.
- 핵심 학습 포인트
- “동작하는 프로그램”과
- “운영에서 살아남는 시스템”의 차이를 이해한다.
1. 현실 매장 환경 분석 (출발점)
- 1-1. 매장 구성
- 키오스크: □대
- 직원용 POS/관리 화면: □대
- 모바일 주문 앱: 있음 / 없음
- 매장 수: □개
- 1-2. 운영 시간 특성
- 평시 시간대:
- 피크 시간대:
- 점심 (예: 12:00 ~ 12:30)
- 저녁 (예: 18:00 ~ 19:00)
- 1-3. 사용자 유형
- 유형역할
손님 주문, 결제, 메뉴 탐색 직원 주문 확인, 제조, 처리 관리자 메뉴/가격/재고 관리
2. 업무 흐름 분석 (현실 기반)
- 2-1. 기본 주문 흐름
- 메뉴 탐색
- 옵션 선택
- 장바구니
- 결제
- 주문 접수
- 제조
- 픽업
- 2-2. 예외 흐름
- 주문 취소
- 환불
- 품절
- 결제 실패
- 네트워크 단절
3. 사용자 행동 분석 (Hidden Traffic)
- 3-1. 결제 이전 행동
- 메뉴 클릭
- 스크롤
- 옵션 변경
- 장바구니 담기/삭제
- 3-2. 시스템 자동 행동
- 메뉴 상태 갱신(Polling)
- 주문 상태 조회
- 핵심 인식
- 주문 1건 ≠ 요청 1건
주문 1건 = 조회 수십 번 + 쓰기 1번
- 주문 1건 ≠ 요청 1건
4. 트래픽 분석 (현실 기준)
- 4-1. 평균 vs 피크
- 평균 주문량:
- 피크 주문량:
- Burst 발생 시점:
- 4-2. 트래픽 특성
- Read / Write 비율:
- Read-heavy 시스템 여부: Yes / No
5. 데이터 분석 (Data Perspective)
- 5-1. 데이터 분류
- 데이터변경 빈도조회 빈도
메뉴 낮음 매우 높음 가격 낮음 매우 높음 옵션 낮음 높음 주문 높음 중간 결제 높음 낮음
- 핵심 질문
- 이 데이터는 DB까지 가야 하는가?
6. 나쁜 설계 유도 체크 (의도적 사고 실습)
- 흔한 오해 체크
- 주문 수만 보면 된다
- DB 하나면 충분하다
- 캐시는 오버 엔지니어링이다
- 테스트에서 문제 없었다
7. 성능 목표 설정 (숫자로 정의)
- 7-1. 성능 목표(SLO)
- 메뉴 조회 응답 시간: ___ ms
- 주문 생성 응답 시간: ___ ms
- 피크 처리량: ___ TPS
8. 캐시 설계 (핵심 학습 포인트)
- 8-1. 캐시 대상
- 메뉴
- 가격
- 옵션
- 8-2. 캐시 전략
- 위치: 클라이언트 / 서버 / 둘 다
- TTL:
- 무효화 조건:
- 핵심 메시지
- DB를 빠르게 만드는 것이 아니라
DB가 할 일을 줄인다
- DB를 빠르게 만드는 것이 아니라
9. DB 설계 관점
- 9-1. 인덱스 설계
- 주문 조회 기준:
- 상태 전이 기준:
- 9-2. 읽기/쓰기 구분
- Read Path:
- Write Path:
10. 장애 시나리오 설계
- 반드시 고려
- 결제 성공 후 서버 장애
- 프린터 장애
- 네트워크 단절
- 질문
- 이때 매장은 멈추는가?
11. 구현 범위 정의 (MVP)
- MVP 기능
- 메뉴 조회
- 주문
- 결제
- 주문 상태 표시
- 제외 기능
- 쿠폰
- 멤버십
- 추천 알고리즘
12. 테스트 설계
- 테스트 종류
- 단위 테스트
- 통합 테스트
- 피크 시나리오 테스트
- 장애 시나리오 테스트
13. 산출물 체크리스트 (포트폴리오용)
- 요구사항 명세서
- 업무 흐름도
- 아키텍처 다이어그램
- 캐시 전/후 비교 구조
- 성능 가정표
- 장애 시나리오 문서
코드를 잘 짜는 사람보다 현실을 이해하고 구조를 설계하는 개발자가 필요하다