← bruni.world
전자책 · DB & 백엔드

트래픽이 오른 날
터지는 것들

개발 환경에서는 멀쩡했는데, 트래픽이 오른 날 무너져요. 코드는 한 줄도 안 바뀌었는데요. 그런 순간만 85개 모았어요.

85편435쪽PDF · A5 (148×210mm)2026년 1판
19,000원 15,000원 런칭가

곧 크몽에서 만나요. 그동안 전체 차례와 본문 3편을 열어 뒀어요.

교과서 목차는 없어요. "인덱스란 무엇인가" 같은 건 검색하면 나오니까요.

이 책에 담은 건 코드에 버그가 없는데 사고가 나는 것, 상식적인 설정이 모여 장애가 되는 것들이에요. 한 편은 실제로 겪는 장면에서 시작해서, 지금 우리 시스템에 바로 돌려 볼 수 있는 점검 쿼리로 끝나요.

85편
전부 같은 9단 구성
435쪽
A5 조판 PDF
170개
문제·해법 코드와 점검 명령 블록
32.8만 자
코드·캡션 포함 본문
82편
블로그에서 먼저 읽어 볼 수 있어요

이런 날들이에요

차례에서 뽑은 아홉 편이에요. 제목 아래는 그 편의 첫 문장이고요. 겪어 본 장면이 하나라도 있다면, 나머지 76편도 비슷한 결이에요.

기본기
재시도가 장애를 키운다
DB 가 잠깐 느려졌을 뿐인데 서비스가 완전히 무너졌어요.
기초 체력
청소부가 멈추면 DB 가 쓰기를 거부한다
어느 날 갑자기 모든 쓰기가 실패해요.
기본기
평균은 멀쩡한데 사용자는 느리다
대시보드의 평균 응답 시간은 120ms 예요.
기본기
43초가 24시간이 되다
2018년 10월, GitHub 의 두 데이터센터를 잇는 네트워크가 43초 동안 끊겼어요.
기초 체력
커넥션 풀을 키우면 오히려 느려진다
응답이 느려서 커넥션 풀을 20에서 100으로 올렸어요.
트래픽
핫 키 하나가 샤드 전체를 태운다
데이터를 샤딩해 뒀어요.
관측과 배포
라벨 하나가 모니터링을 통째로 죽인다
지표에 라벨을 하나 추가했어요.
기본기
정규식 한 줄로 CPU 가 100% 가 된다
입력 검증용 정규식을 하나 추가했어요.
기본기
이중 쓰기 — 가장 조용한 데이터 손실
주문을 DB 에 저장하고 검색 엔진에도 넣어요.

한 편은 이렇게 생겼어요

85편이 전부 같은 뼈대예요. 읽다가 덮어도 어디까지 봤는지 알 수 있고, 필요한 편만 골라 펴도 흐름이 끊기지 않아요.

  1. 현상 — 실제로 겪는 장면
  2. 원리 — 왜 그렇게 되는가
  3. 코드 — 문제 코드(✕)와 고친 코드(✓)
  4. 실전 — 현장 수치, 더 나쁜 경우, 놓치기 쉬운 것
  5. 처방 — 무엇을 어떻게 바꾸나
  6. 함정 — 처방이 부르는 새 문제
  7. 점검 — 지금 우리 시스템을 확인하는 쿼리·명령
  8. 근거 — 출처와 참고
  9. 핵심 한 줄 — 덮고 나서 남는 문장

본문 3편, 통째로

요약이 아니라 책에 실린 그대로예요. 셋 다 블로그에 없는 책 전용 편이고요. 이 정도가 85편의 평균이라고 보시면 돼요.

본문 05 / 85

커넥션 풀을 키우면 오히려 느려진다

현상

응답이 느려서 커넥션 풀을 20에서 100으로 올렸어요. 그런데 처리량이 오히려 떨어지고 p99 는 더 튀었어요. DB CPU 는 100% 에 붙어 있는데 실제로 끝나는 쿼리 수는 줄었고요. 리소스를 더 줬는데 결과가 반대예요.

모니터링만 보면 "DB 가 바쁘다"로 읽히니까 풀을 더 키우고 싶어져요. 그게 정확히 반대 방향이에요.

이 현상은 개발 환경에서 절대 안 나와요. 동시 요청이 몇 개 없으니 풀이 20이든 200이든 실제로 쓰이는 건 두어 개거든요. 풀 크기는 트래픽이 오르기 전까지는 그냥 숫자예요. 그래서 부하 테스트도 동시성을 충분히 올리지 않으면 이 구간을 못 만나요.

증상은 대개 이렇게 와요. 트래픽이 평소의 두 배가 되는데 응답 시간은 다섯 배, 열 배로 벌어져요. 서버를 늘려도 나아지지 않고, 오히려 파드가 늘어난 만큼 더 나빠져요.

원리

DB 가 동시에 진짜로 일할 수 있는 양은 정해져 있어요. CPU 바운드 워크로드면 코어 수, IO 바운드면 스토리지가 감당하는 동시 요청 수요. 그 이상을 밀어 넣으면 리틀의 법칙이 그대로 나타나요.

리틀의 법칙은 L = λW 예요. 시스템 안에 머무는 요청 수 L, 도착률 λ, 한 요청의 체류 시간 W. 여기서 실제 처리 능력(λ)은 코어 수에 막혀 있는데 L(동시에 열린 커넥션)만 늘리면, 등식이 성립하려면 W 가 비례해서 늘 수밖에 없어요. 처리량은 그대로인데 지연만 커지는 구간이 만들어지는 거예요.

그런데 현실은 이보다 나빠요. 곡선이 평평해지는 데서 끝나지 않고 꺾여서 내려가요. 동시 실행이 코어를 넘으면 컨텍스트 스위칭이 늘고, 버퍼 풀·락 매니저처럼 모두가 함께 쓰는 자료구조에서 경합이 생기고, 각 쿼리가 CPU 를 잡는 시간이 잘게 쪼개지면서 캐시 지역성이 깨져요. 일하는 시간보다 자리 바꾸는 시간이 커지는 거죠.

그래서 풀을 키운다는 건 "더 많이 일하게 한다"가 아니에요. "이 꺾인 구간으로 더 깊이 들어간다"예요. 커넥션은 자원이 아니라 대기열이에요.

공식은 출발점이지 정답이 아니다
· HikariCP 위키의 공식 (원출처는 PostgreSQL 위키)
✓ pool_size = (core_count * 2) + effective_spindle_count

· core_count 는 앱 서버가 아니라
· 쿼리를 실제로 실행하는 DB 서버의 코어 수다
· effective_spindle_count 는 SSD 면 1
· → 4코어 SSD 인스턴스면 (4*2)+1 = 9

· 그리고 이 값은 인스턴스 하나가 아니라
· 앱 전체를 합친 상한이다
✕ 4코어 DB × 파드 10 × 풀 50 = 500 커넥션 요청
✕ max_connections 를 넘으면 느려지는 게 아니라 끊긴다
✕ FATAL: sorry, too many clients already
실전

숫자가 어디서 오는지 짚어 볼게요.

PostgreSQL 은 커넥션마다 백엔드 프로세스를 포크해요. 그래서 유휴 커넥션도 공짜가 아니에요. 트랜잭션이 스냅샷을 뜰 때 전체 백엔드를 훑는 구조라, 놀고 있는 커넥션이 많으면 일하고 있는 쿼리까지 느려져요. PostgreSQL 14 에서 이 스냅샷 확장성이 크게 개선됐지만 0 이 되진 않았어요.

max_connections 도 넉넉히 잡아 두면 되는 값이 아니에요. 서버는 이 값에 비례해서 락 테이블 같은 공유 메모리를 기동할 때 미리 잡아요. 크게 잡아 두는 것 자체가 상시 비용이에요.

공식도 맥락이 있어요. (core_count * 2) + effective_spindle_count 는 회전 디스크를 전제로 만들어진 식이에요. 디스크가 헤드를 움직이는 동안 CPU 가 놀 수 있으니 코어 수보다 조금 더 열어 둔다는 발상이거든요. SSD 면 effective_spindle_count 를 1 로 봐서 4코어에 9 정도가 나와요. 출발점으로는 쓸 만하지만 정답은 아니에요. 쿼리가 CPU 를 태우는 종류냐, 락이나 IO 를 기다리는 시간이 긴 종류냐에 따라 최적점이 크게 달라져요.

그리고 이 숫자는 앱 전체를 합친 상한이에요. 파드 10개가 각자 풀 50 을 들고 있으면 DB 입장에서는 500 커넥션 요청이에요. 오토스케일이 도는 환경에서 트래픽이 오르면 파드가 늘면서 이 선을 한 번에 넘어요. 그때는 느려지는 게 아니라 연결이 거부돼요.

MySQL 은 프로세스가 아니라 스레드라서 커넥션 하나의 비용이 훨씬 싸요. 메모리로는 덜 아파요. 그런데 동시 실행이 코어를 넘으면 같은 꺾임이 그대로 나와요. 싼 건 커넥션이지 동시성이 아니에요.

처방

순서가 있어요. 이 순서를 건너뛰면 대개 실패해요.

1. 풀 크기부터 만지지 마세요. 풀이 마르는 건 대개 증상이에요. 느린 쿼리 하나가 커넥션을 오래 붙잡고 있는 거고요. pg_stat_statements 에서 호출 횟수가 아니라 총 소요 시간(total_exec_time) 상위부터 보세요. 100ms 짜리가 하루 10만 번 도는 게 3초짜리가 100번 도는 것보다 훨씬 큰 문제예요.

2. 트랜잭션을 짧게. 트랜잭션 안에서 외부 API 를 부르거나 파일을 쓰거나 사용자 입력을 기다리면, 그 시간 내내 커넥션이 묶여 있어요. 풀이 100개여도 각자 3초씩 붙잡으면 초당 33개밖에 못 처리해요. 커넥션 수가 아니라 점유 시간이 처리량을 정합니다.

3. 그다음에 크기를 정해요. 계산으로 맞히는 게 아니라 재서 찾아요. 부하를 계단식으로 올리면서 처리량과 p99 를 같이 그리세요. 처리량이 평평해지기 시작하는 지점이 그 시스템의 무릎이고, 그 너머는 지연만 늘어요.

4. 파드가 늘어나는 환경이면 앱 풀을 키우는 대신 앞에 pgbouncer 를 두세요. 앱 쪽 커넥션은 많게, 실제 DB 커넥션은 적게 가져가는 게 목적이에요. 방향이 반대예요.

5. 획득 타임아웃을 짧게 잡으세요. 풀이 말랐을 때 무한정 기다리게 두면 요청이 전부 큐에 쌓이고 앱 스레드까지 말라요. 다 같이 기다리다 같이 죽는 것보다, 일부가 빨리 실패하고 나머지가 사는 게 나아요.

함정

처방대로 pgbouncer 를 넣었는데 그때부터 이상한 버그가 나기 시작한다면, pool_mode 를 보세요.

transaction 모드는 트랜잭션 단위로 커넥션을 섞어요. 그래서 세션에 붙는 것들이 조용히 깨져요. SET 으로 잡은 세션 변수, 세션 수준 어드바이저리 락, LISTEN/NOTIFY, WITH HOLD 커서, 세션 임시 테이블이 전부 여기 해당해요.

조용하다는 게 핵심이에요. 예외가 나면서 멈추는 게 아니라, 다음 트랜잭션이 다른 커넥션에 앉으면서 방금 설정한 게 그냥 없어져요. 개발 환경에서는 커넥션이 사실상 하나라 절대 안 드러나요. 트래픽이 올라 커넥션이 섞이기 시작하는 날 터집니다.

프리페어드 스테이트먼트도 오래 이 목록에 있었어요. pgbouncer 1.21(2023년 10월)부터 transaction 모드에서 지원돼요. 다만 max_prepared_statements 를 0 이 아닌 값으로 켜 줘야 동작하고, 기본값이 0 이라 켜지 않으면 그대로 깨져요. 그 이전 버전이라면 드라이버 쪽에서 서버 사이드 프리페어를 꺼야 하고요.

점검
풀이 마른 건지, 코드가 문제인지
· 커넥션이 무슨 상태로 있나
SELECT state, count(*)
  FROM pg_stat_activity
 GROUP BY state;

✕ idle in transaction 이 많으면
✕ 커넥션 부족이 아니라 코드 문제다
✕ 트랜잭션이 안 닫히고 있다

· active 가 무엇을 기다리는지까지
SELECT wait_event_type, wait_event, count(*)
  FROM pg_stat_activity
 WHERE state = 'active'
 GROUP BY 1,2 ORDER BY 3 DESC;

· 앱 쪽 — 풀이 마르면 '대기 중'이 쌓인다
✓ hikaricp_connections_pending > 0 이 지속되면
✓ 풀이 부족하거나 오래 붙잡는 쿼리가 있다
✓ hikaricp_connections_acquire_seconds 의 p99 를 같이 본다
근거
  • 리틀의 법칙 L = λW — 대기행렬 이론의 기본 항등식
  • 풀 크기 공식 — HikariCP 위키 About Pool Sizing (Brett Wooldridge), 원출처는 PostgreSQL 위키의 회전 디스크 전제 공식
  • 유휴 커넥션의 스냅샷 비용과 PostgreSQL 14 개선 — 스냅샷 확장성 작업 (Andres Freund)
  • pgbouncer transaction 모드의 프리페어드 스테이트먼트 지원 — pgbouncer 1.21 (2023-10), max_prepared_statements 설정 필요
핵심 한 줄풀을 키우는 건 더 많이 일하는 게 아니라 더 길게 줄 세우는 것이다.

차례 85편 전부

숨기지 않을게요. 여섯 개 장을 펼치면 85편의 제목이 전부 나와요. 무료로 읽기 배지가 붙은 82편은 블로그에 공개돼 있고, 나머지 3편은 책에만 있어요.

▶ 1부 데이터베이스1장 기초 체력 19편
  1. 01FOR UPDATE 에 인덱스가 없으면 테이블 전체가 잠긴다무료로 읽기
  2. 02MySQL 의 utf8 은 UTF-8 이 아니다무료로 읽기
  3. 03OFFSET 페이지네이션은 뒤로 갈수록 죽는다무료로 읽기
  4. 04ALTER TABLE 이 기다리면 뒤에 줄이 선다무료로 읽기
  5. 05커넥션 풀을 키우면 오히려 느려진다
  6. 06pgbouncer transaction 모드에서 안 되는 것들무료로 읽기
  7. 07인덱스를 만드는 동안 쓰기가 막힌다무료로 읽기
  8. 08안 쓰는 인덱스는 순수한 세금이다무료로 읽기
  9. 09청소부가 멈추면 DB 가 쓰기를 거부한다무료로 읽기
  10. 10N+1 — 반복문 하나가 쿼리 101번무료로 읽기
  11. 11격리 수준은 이름값을 못 한다무료로 읽기
  12. 12서머타임에 배치가 두 번 돌거나 건너뛴다무료로 읽기
  13. 13소프트 삭제가 인덱스를 갉아먹는다무료로 읽기
  14. 14LIKE '%검색어%' 는 인덱스를 못 쓴다무료로 읽기
  15. 15대량 DELETE 가 복제를 무너뜨린다무료로 읽기
  16. 16인덱스가 있는데 안 타는 이유무료로 읽기
  17. 17통계가 낡으면 플래너가 헛짚는다무료로 읽기
  18. 18복합 인덱스는 순서가 전부다무료로 읽기
  19. 19JSON 컬럼은 스키마를 미루는 빚이다무료로 읽기
▶ 1부 데이터베이스2장 트랜잭션 12편
  1. 20트랜잭션 안에서 외부 API 를 부르지 마라무료로 읽기
  2. 21긴 트랜잭션 하나가 DB 전체를 부풀린다
  3. 22잃어버린 갱신 — 두 번의 쓰기가 한 번이 된다무료로 읽기
  4. 23쓰기 스큐 — 각자는 옳은데 합치면 틀리다무료로 읽기
  5. 24@Transactional 이 조용히 무시되는 순간무료로 읽기
  6. 25REQUIRES_NEW 가 자기 커넥션 풀을 잠근다무료로 읽기
  7. 26SKIP LOCKED 로 만드는 작업 큐무료로 읽기
  8. 27커밋되기 전에 이벤트를 보내지 마라무료로 읽기
  9. 28트랜잭션은 짧게 — 경계가 곧 비용이다무료로 읽기
  10. 29낙관적이냐 비관적이냐 — 충돌 확률이 정한다무료로 읽기
  11. 30배치를 트랜잭션 하나로 돌리지 마라무료로 읽기
  12. 31읽기 전용인데 쓰기 트랜잭션으로 도는 것들무료로 읽기
▶ 1부 데이터베이스3장 인덱스와 쿼리 플랜 12편
  1. 32EXPLAIN 은 추정과 실제의 차이를 보는 것이다무료로 읽기
  2. 33LIMIT 이 붙으면 계획이 통째로 바뀐다무료로 읽기
  3. 34인덱스 온리 스캔이 안 되는 이유무료로 읽기
  4. 35정렬이 메모리를 넘으면 디스크로 간다무료로 읽기
  5. 36여섯 번째 실행부터 느려지는 쿼리무료로 읽기
  6. 37OR 은 인덱스를 반으로 가른다무료로 읽기
  7. 38NULL 은 인덱스에서 다르게 논다무료로 읽기
  8. 39COUNT(*) 는 결국 다 세는 일이다무료로 읽기
  9. 40인덱스도 부푼다무료로 읽기
  10. 41인덱스를 탔는데도 느린 경우무료로 읽기
  11. 42조인 순서가 뒤집히면 100배가 된다무료로 읽기
  12. 43가장 느린 쿼리가 가장 아픈 쿼리는 아니다무료로 읽기
▶ 2부 백엔드4장 기본기 18편
  1. 44재시도가 장애를 키운다무료로 읽기
  2. 45헬스체크가 서버를 죽인다무료로 읽기
  3. 46배포할 때마다 502 가 조금씩 난다무료로 읽기
  4. 47컨슈머가 계속 리밸런싱만 한다무료로 읽기
  5. 48CDN 이 로그인한 사람의 화면을 캐시한다무료로 읽기
  6. 49타임아웃을 안 걸면 기본값은 '영원히'다무료로 읽기
  7. 50배포 직후 DB 가 맞는다무료로 읽기
  8. 51캐시 스탬피드무료로 읽기
  9. 52평균은 멀쩡한데 사용자는 느리다무료로 읽기
  10. 53서킷 브레이커무료로 읽기
  11. 5443초가 24시간이 되다무료로 읽기
  12. 55오타 하나와 4시간무료로 읽기
  13. 56정규식 한 줄로 CPU 가 100% 가 된다무료로 읽기
  14. 57멱등성 — 재시도는 반드시 일어난다무료로 읽기
  15. 58이중 쓰기 — 가장 조용한 데이터 손실무료로 읽기
  16. 59에러를 삼키면 장애가 조용해진다무료로 읽기
  17. 60넘치면 흘려보내라무료로 읽기
  18. 61페일오버했는데 트래픽이 안 넘어간다무료로 읽기
▶ 2부 백엔드5장 트래픽 12편
  1. 62오토스케일은 급증을 못 막는다무료로 읽기
  2. 63새로 뜬 인스턴스가 먼저 죽는다무료로 읽기
  3. 64핫 키 하나가 샤드 전체를 태운다무료로 읽기
  4. 65백프레셔가 없으면 메모리가 먼저 죽는다무료로 읽기
  5. 66분산 환경의 레이트 리밋은 경합한다무료로 읽기
  6. 67스케일 아웃해도 DB 는 한 대다무료로 읽기
  7. 68keep-alive 를 안 쓰면 악수만 하다 끝난다무료로 읽기
  8. 69부하 테스트가 놓치는 것들무료로 읽기
  9. 70큐로 첫 파도를 평탄화하라무료로 읽기
  10. 71동시성 제한이 없으면 다 같이 느려진다무료로 읽기
  11. 72트래픽이 아니라 비용이 먼저 터진다무료로 읽기
  12. 73장애는 대개 트래픽이 아니라 의존성에서 온다무료로 읽기
▶ 2부 백엔드6장 관측과 배포 12편
  1. 74라벨 하나가 모니터링을 통째로 죽인다무료로 읽기
  2. 75퍼센타일은 평균낼 수 없다
  3. 76알림이 많으면 아무도 안 본다무료로 읽기
  4. 77스키마 변경과 배포에는 순서가 있다무료로 읽기
  5. 78되돌릴 수 없는 배포가 있다무료로 읽기
  6. 79설정 변경이 코드 배포보다 위험하다무료로 읽기
  7. 80트레이스는 비동기 경계에서 끊긴다무료로 읽기
  8. 81로그는 남겼는데 못 찾는다무료로 읽기
  9. 82샘플링이 하필 찾던 걸 버린다무료로 읽기
  10. 83카나리가 못 잡는 것들무료로 읽기
  11. 84기능 플래그는 지우지 않으면 부채가 된다무료로 읽기
  12. 85지표는 장애 나기 전에 정해 둬야 한다무료로 읽기

다루는 것, 안 다루는 것

✓다뤄요

MySQL · InnoDB 락PostgreSQL · autovacuum 커넥션 풀 · pgbouncer트랜잭션 · 격리 수준 인덱스 · EXPLAIN 플랜복제 지연 · 페일오버 캐시 스탬피드 · 핫 키Kafka 컨슈머 리밸런싱 재시도 · 서킷 브레이커멱등성 · 이중 쓰기 오토스케일 · 백프레셔레이트 리밋 · 동시성 제한 퍼센타일 · 트레이스 · 샘플링카나리 · 기능 플래그 스키마 변경과 배포 순서

✕안 다뤄요

  • SQL 문법 입문 — JOIN 이 뭔지부터는 다른 책이 훨씬 잘해요
  • 특정 프레임워크 설정법 — 원리를 쓰고, 설정은 예시로만 나와요
  • 클라우드 콘솔 클릭 가이드 — 화면은 반년이면 바뀌니까요
  • 아키텍처 유행 — MSA 를 할지 말지는 이 책의 주제가 아니에요

이런 분께

✓맞아요

  • 운영 트래픽을 처음 받아 보는 주니어~3년차 백엔드 개발자
  • 장애 회고에서 "왜 몰랐지"를 반복하고 싶지 않은 분
  • 혼자 서버와 DB 를 다 보는 1인 개발자
  • 면접에서 교과서 답 말고 실전 사례가 필요한 분

✕안 맞아요

  • DB 를 처음 배우는 분 — 기초 개념 없이 바로 사고 현장으로 들어가요
  • 이론의 계보와 증명을 원하는 분 — 이 책은 현장 쪽이에요
  • 복사해서 붙이면 끝나는 정답 코드를 찾는 분 — 진단이 먼저인 책이에요

자주 묻는 것

입문자도 볼 수 있나요?
기초 문법은 다루지 않아요. SELECT 가 뭔지, 서버가 뭔지부터 필요한 분께는 안 맞아요. 반대로 서버나 DB 를 조금이라도 만져 봤다면 읽을 수 있게 썼어요. 위 맛보기 3편이 딱 이 책의 평균 난이도예요 — 읽어 보고 정하시면 돼요.
블로그에 공개된 글과 겹치지 않나요?
겹치는 게 82편 있어요. 위 차례에서 무료로 읽기 배지가 붙은 편들이 그거예요. 그중 3편은 블로그에서 가까운 주제의 글에 합쳐 실어서, 블로그 글로 세면 79편이에요. 나머지 3편은 책에만 있고, 위 맛보기로 이 페이지에 열어 뒀어요. 어디까지가 무료인지 숨기지 않는 게 맞다고 생각했어요.
어떤 DB · 언어 기준인가요?
사례는 주로 MySQL(InnoDB)과 PostgreSQL 이에요. 코드는 SQL·설정 파일·셸 명령이 대부분이라 특정 언어를 몰라도 읽혀요. 원리는 어느 DB 를 쓰든 통하고요.
어떤 파일로 받나요?
PDF 한 파일이에요. A5 (148×210mm) 판형 435쪽이고, 태블릿이나 노트북에서 보기 좋아요. 종이책은 없어요.
환불되나요?
디지털 파일이라 내려받은 뒤에는 환불이 어려워요. 그래서 전체 차례 85편과 본문 3편을 이 페이지에 통째로 열어 뒀어요. 사기 전에 충분히 보고 정하실 수 있게요.
업데이트 되나요?
오류를 고친 판이 나오면 다시 내려받을 수 있어요. 판 번호는 책 맨 뒤 판권에 적혀 있어요.
누가 썼나요?
브루니예요. 이름과 얼굴은 내놓지 않고 만든 것만 내놓기로 했어요. 판단은 블로그에 공개한 글과 위 맛보기로 해 주세요.

사는 곳

19,000원 15,000원 런칭가

85편 · 435쪽 · PDF 즉시 내려받기

결제 창이 아직 안 열렸어요. 크몽 등록이 끝나면 이 버튼이 살아나요. 그동안은 블로그에 공개한 82편으로 먼저 판단해 주세요.

결제와 발송 — 결제·발송·문의는 크몽에서 이뤄져요. 결제가 끝나면 PDF 파일을 바로 내려받아요.
환불 — 디지털 콘텐츠라 파일을 내려받은 뒤에는 청약철회가 어려워요. 대신 전체 차례 85편과 본문 3편을 이 페이지에 열어 뒀어요 — 사기 전에 충분히 보고 정하실 수 있게요.
저작권 — 개인이 읽는 용도예요. 사내 공유·재배포·재판매는 안 돼요.