v1.4.0이 챕터 본문을 터미널용으로 조판하기 시작했다. 다만 그 조판은 처음 그린 폭을 유지할 때만 온전했다 — 창을 좁히면 코드가 토큰 중간에서 잘리고 표가 뭉개졌다. 이번 릴리스는 그 조판이 창 크기를 바꿔도 무너지지 않게 만들고, 화면 어디서든 한 타로 빠져나올 길을 내고, 다 읽은 챕터에서 시험으로 넘어가는 길을 막지 않는 형태로 바꾼다. 그리고 mysql·oracle 단일 벤더 뷰에서 통째로 사라져 있던 실습 예제를 돌려준다.
챕터 읽기 — 창을 좁혀도 무너지지 않는다 (#96)
화면에서 복사한 명령이 이제 그대로 실행된다. 코드 펜스를 감싸던 상자를 걷어냈다 — 상자가 렌더 폭에 맞춰 하드랩되면서 명령을 토큰 중간에서 잘랐기 때문이다. 실측하면 80칸에서 코드 줄의 6%, 60칸에서 25%, 40칸에서 **49%**가 잘렸다. 대신 짧은 언어 라벨과 4칸 들여쓰기만 붙이고 코드는 손대지 않고 그대로 내보낸다. 긴 줄을 접는 것은 less의 몫이다.
표는 더 이상 억지로 욱여넣지 않는다. 자연 폭이 화면에 들어갈 때만 정렬해 그리고, 넘치면 행 하나를 카드 한 장으로 편다(▸ 제목 아래에 헤더: 값). 이 학습서의 표는 자연 폭 중앙값이 130칸이라, 44개 표 중 36개가 80칸에서 이미 뭉개지고 있었다. 40칸에서는 PostgreSQL이 PostgreSQ/L로 갈라졌다. 열을 압축하는 코드는 아예 남기지 않았다.
제목은 잘리지 않는다. 폭을 넘으면 줄바꿈하면서 제목 스타일을 다음 줄까지 이어 간다 — 스타일이 접힌 뒷줄을 본문과 구별해 주는 유일한 단서인데, 꼬리를 통째로 잃는 쪽이 더 나빴다. 가로선은 폭을 채우는 대신 24칸에서 멈춘다.
바탕에 깔린 규칙은 하나다. 렌더된 줄은 줄머리에 의미를 담지 않는다. less는 소프트랩된 다음 줄에 앞머리 글리프나 여백을 다시 그려 주지 않으므로, 거기 실어 둔 정보는 독자가 창을 좁히는 순간 사라진다. 위 세 가지가 모두 이 규칙에서 나왔고, 각각 그 이유를 이름에 적은 테스트가 지킨다.
어디서든 Q (#95)
어느 화면에 있든 대문자 Q 한 타로 앱이 끝난다. Esc와 q는 한 단계 뒤로 나간다 — 챕터 목록에서 티어로, 티어에서 DBMS로. 메뉴 깊숙이 들어간 뒤 빠져나오려고 들어간 단계 수만큼 누를 일이 없어졌다.
챕터를 닫을 때마다 뜨던 "계속하려면 Enter"도 없앴다. 페이저에서 나오면 곧바로 챕터 목록이다.
예외는 둘이고 둘 다 의도한 것이다. 장애 대응 게임이 진행 중일 때 뜨는 서버 선택 화면은 Q를 막아 뒀다 — 그 화면에서 앱을 끊으면 dbshoot-* 컨테이너가 뜬 채 남는다. 취소는 Esc로 한다. 그리고 학습 점검(exam) 화면은 아직 자체 키 계약을 쓴다(아래 「알려진 한계」).
읽고 나서 바로 풀기
챕터 목록에서 x를 누르면 그 챕터의 문제은행이 곧바로 열린다. 다 읽고 나와서 이어 풀 수도 있고, 목록만 훑다가 풀 챕터를 골라도 된다.
목록이 그 선택에 필요한 것을 함께 보여 준다. 문제은행이 없는 챕터에는 [시험 없음]이 붙고, 이미 푼 챕터에는 [지난 최고 A·92%]가 붙는다. 어느 챕터에 시험이 있고 어디를 다시 봐야 하는지 일일이 눌러 보고 알아낼 필요가 없다.
읽고 나올 때마다 묻던 [Y/n] 프롬프트는 없앴다. 기본값이 예였던 탓에, 예전의 "계속하려면 Enter"를 치우던 습관적인 Enter가 그대로 시험을 시작시켰다. 시험은 이제 명시적으로 x를 누를 때만 시작한다.
학습서 — mysql·oracle 독자에게 실습을 돌려준다 (#90)
02-intermediate/04-backup-recovery-strategies.md의 「실습 예제」가 통째로 dbms:postgresql 마커 안에 들어 있었다. main에서는 멀쩡해 보였지만 mysql·oracle 브랜치에서는 그 절이 통째로 사라져, 두 벤더 독자에게는 손으로 해 볼 것이 하나도 없었다.
이제 시나리오는 공유한다 — 오후 2시 30분에 실수로 전체 DELETE가 났고 2시 29분 상태로 되돌린다는 하나의 상황을 두고, 세 벤더가 각자의 완전한 절차를 갖는다. PostgreSQL은 base backup + WAL 재생, MySQL은 전체 백업 복원 + binlog 재적용, Oracle은 RMAN 불완전 복구다. 원리가 같고 다른 것은 로그의 이름과 재적용을 멈추는 방법, 복구를 끝내고 서비스를 여는 절차라는 점이 본문에 드러난다. 체크리스트 항목도 벤더별로 짝을 맞췄다.
같은 부류를 다시 들이지 않도록 네 절 구조 검사가 세 단일 벤더 뷰에도 돌아간다. 손으로는 python3 scripts/check_content.py --dbms mysql, CI에서는 ShippedContentTest가 세 벤더 모두를 main 위에서 검사한다. 검사에 쓰는 필터는 벤더 브랜치를 만드는 filter_dbms.filter_lines 바로 그 함수라, 검사와 실제 브랜치가 갈라질 수 없다.
postgresql·mysql·oracle 세 벤더 브랜치는 이 태그 시점의 main에서 재생성해 push한다. 이번에 고친 결함은 그 브랜치에서만 보이던 것이라, 재생성이 끝나야 세 벤더 독자의 손에 실제로 실습 예제가 들어간다.
알려진 한계
발견되지 않은 버그가 아니라 알고 감수하는 경계다.
- East Asian Ambiguous 글리프(
─ │ ━ • · ◆)를 2칸으로 그리도록 설정한 터미널에서는 렌더된 줄 256개가 80칸을 넘는다(이번 작업 전에는 853줄).tui.cwidth가 이 글리프를 1칸으로 세는 것은 저장소 전역의 가정이고, 그 터미널 설정을 이식 가능하게 탐지할 방법이 없다. - 학습 점검(
exam) 화면은 아직 자체 키 계약을 쓴다 —q는 시험만 끝내고Esc는 반응하지 않는다. 키 계약이 다른 유일한 화면이다(아래 서버 선택 화면도Q로 앱이 끝나지는 않지만, 그쪽은 같은 계약 위에서 종료만 막아 둔 것이다). - 장애 대응 게임이 진행 중일 때 서버 선택 화면은
Q를 막아 뒀다. 게임을 도중에 끊으면dbshoot-*컨테이너가 뜬 채 남기 때문이다 — 그 화면은Esc로 취소한다. - PostgreSQL 판정의 구멍 둘은 그대로다. 파싱 오류가 감시 소스에 남지 않고(실측 — 없는 테이블·권한 오류는 잡히고
KILL 999999는 놓친다), pid를 적지 않는pg_terminate_backend쓸기는kill_precision이 보지 못한다(forbidden_command로 막는다). - 장애 대응 스테이지의 벤더 분포는 MySQL 13 / PostgreSQL 1 / Oracle 0이다 (#79).
- Oracle RMAN 절차는 실행 검증이 아니라 문서 근거로 작성됐다. 실습 랩(
shooting/lab/)에는 MySQL과 PostgreSQL만 있어 이 저장소 안에서 돌려 볼 수단이 없다. - 치트시트 행, 용어집·비교 매트릭스 반영, 문제은행 내용의 최신성은 여전히 사람 몫이다. 검사기는 파일의 존재와 절 구조만 판정한다.
커밋 46개, 파일 27개, +7,938 / −373. 이 가운데 +5,714는 docs/superpowers/의 설계·계획 문서이고, 실행 코드와 테스트는 +2,119 / −352이다. 테스트 스위트는 v1.4.0의 792개에서 897개로 늘었다. 챕터 31개, 문제은행 23개 / 225문항, 스테이지 14개는 v1.4.0과 같다 — 이번 릴리스는 전부 도구와 조판 쪽이다.
전체 변경 이력: https://github.com/Ahngbeom/dba-guide/commits/v1.5.0
호환성: 더해지고 고쳐졌다. 제거되거나 의미가 바뀐 ./exam·./shoot CLI 인자, 스테이지·문제은행 JSON 스키마 변경, .shooting-progress·.exam-results 기록 형식 변경, 챕터 경로 변경 모두 없다. 눈에 보이는 동작 둘이 사라지지만(챕터를 닫을 때의 "계속하려면 Enter" 대기와 그 뒤의 [Y/n] 시험 프롬프트) 어느 쪽도 인자나 파일이 아니라 지나가는 화면이고, 그 자리에서 하던 일은 챕터 목록의 x로 명시적으로 할 수 있다.