AI용 분할본 · 지도 6단계(선택 기능 — 결정서에 관리자 "켬"일 때만) · 이 페이지 전에 읽을 페이지: PART 0 사용법·AI 시작 지침, 부록 A 스펙·코드 받기(A-2), PART 8 배포 · 코드 파일은 여기 없다 — 코드 자리의 줄을 따른다(폴더에 책 파일이 하나도 없는 처음이면 부록 A-2 의 curl 한 줄부터)
핵심 규칙 — AI 는 어느 페이지에서든 이것을 지킨다
node scripts/get-book-code.mjs 가 책 그대로 만든다(첫 실행은 부록 A-2 의 curl 한 줄). AI용 분할본의 코드 자리는 " 경로" 한 줄이다 — 그 파일을 cat 으로 읽는다. 고쳐도 되는 파일은 PART 0 [AI 시작 지침] 5-b 의 목록뿐이다.node scripts/verify-book-code.mjs 를 실행해 "불일치 0개" 출력을 그대로 보인다.결정서 7번에서 관리자 페이지를 "켬"으로 골랐을 때만 읽습니다. 뼈대가 배포된 뒤(지도 6단계) 켭니다. 번호가 B-7 인 것은 HRUM 전자책 시리즈가 부록 번호를 같이 쓰기 때문입니다 — B-1~B-6 은 다른 책의 레시피라 이 책에는 없습니다.
앱을 열면 곧 "이상한 회원 좀 막고 싶다", "오늘 몇 명 들어왔지?"가 궁금해집니다. 관리자 페이지는 딱 세 가지만 합니다 — 회원 목록·검색(아이디·가입일·마지막 로그인·메모 개수), 회원 정지·해제(사유 기록), 요약 숫자(전체 회원·오늘/7일 가입·7일 로그인·정지 수·전체 메모·오늘/7일 쓴 메모). 메모는 개수만 셉니다 — 회원의 메모 내용은 관리자도 볼 수 없습니다. 지운 메모는 세지 않습니다. "오늘"과 "7일"은 한국 날짜 기준입니다.
왜 주소가 /admin 이 아닌가요?/admin은 공격 봇이 제일 먼저 두드리는 주소입니다. 이 책의 관리자 주소는/admin+ 무작위 숫자 16자리 + 만든 날짜(예:/admin482139057261384020260925— 책에 실린 이 예시 값은 코드가 거부합니다)이고, 그 값은 코드에도 저장소에도 없습니다 — 콘솔 환경변수ADMIN_PATH에만 있습니다. 폴더 이름으로 만들면 Next.js 가 공개 파일(_buildManifest.js)에 주소 흔적을 남기기 때문입니다. 그래도 진짜 문은 비밀번호입니다. 주소는 봇 소음을 줄이는 보조 장치일 뿐이니 비밀번호를 대충 정하지 마세요.
만들기 순서 (코드는 이미 받아져 있습니다)
관리자 코드는 책 코드라서 get-book-code 로 이미 받아져 있고, 두 환경변수가 없으면 꺼져 있습니다(비밀 주소도 404). 켜는 방법:
관리자 페이지(B-7)를 켜 주세요. (이미 켠 앱을 책 개정에 맞출 때는 1 → 3 → 4 만 한다 — 2번 주소 만들기는 다시 하지 않는다)
1. 책 코드를 최신으로 맞춘다 — 스크립트가 옛 판인지 따지지 말고 무조건 이 세 줄:
git status 가 깨끗한지 확인(아니면 먼저 커밋한다)
curl -s https://console.hrum.ai/hostingEbook/get-book-code.mjs -o scripts/get-book-code.mjs
node scripts/get-book-code.mjs --force ("한 번 더 실행하세요"가 나오면 그대로 다시 실행)
그다음 npm run check 가 끝까지 통과하는지 확인.
스크립트가 "되돌림"·"새로 만듦"으로 찍은 파일 목록을 그대로 저에게 보여 준다(새로 만든 파일은 커밋에 꼭 넣는다). "되돌림"은 대부분 옛 책판이 새 판으로 바뀐 것이다. 그중 제가 일부러 고친 파일이 있었으면 옆의 <파일>.orig(이미 있었으면 가장 큰 번호의 <파일>.N.orig. 경로에 [ ] 가 있으면 따옴표로 감싼다)나 git diff 로 사라진 내용을 확인한다.
--force 는 package.json 의 name 은 두고 의존성·scripts 를 책판으로 되돌린다 — 일부러 더한 의존성이 있었으면 package.json.orig 를 보여 주고 저에게 묻는다. package.json 이 되돌려졌으면 npm install 로 package-lock.json 도 맞춰 함께 커밋한다(안 맞으면 RIDM Apps 빌드의 npm ci 가 실패).
DB 테스트 한 줄을 적는다(7-5): 테스트용 빈 DB(운영 DB 금지)가 있으면 TEST_DATABASE_URL=<테스트용 빈 DB 주소> npx vitest run → "DB 테스트: 실행(skipped 0)", 없으면 "DB 테스트: 미실행(테스트 DB 없음)"
2. node scripts/make-admin-path.mjs 를 실행해 관리자 주소 값(ADMIN_PATH)을 만들고, 출력된 "다음 할 일"을 그대로 저에게 전달해 주세요.
- ADMIN_PATH 값은 저에게 한 번만 알려 주고, 코드·README·커밋·로그·PROGRESS.md 어디에도 적지 않는다(scripts/verify-book-code.mjs 가 파일 속 관리자 주소를 찾으면 실패시킨다). 이미 콘솔에 ADMIN_PATH 를 저장한 적이 있으면 다시 만들지 않는다
- ADMIN_PASSWORD 는 AI 가 만들지도 보지도 않는다 — 제가 AI 창이 아닌 별도 터미널이나 비밀번호 관리 앱의 생성기로 직접 만들어 콘솔에 넣는다. 저에게 대신 만들어 주겠다고 하거나 그 명령을 AI 창에서 실행하지 않는다
3. 제가 "저장 완료"라고 하면(이미 켠 앱을 개정에 맞추는 경우엔 1번이 끝나는 대로) push 하고, /api/health 의 commit 이 마지막 커밋인지 1분 간격으로 최대 15분 확인한다 — 명령은 curl -s -A "Mozilla/5.0" https://이름.ridm.ai/api/health (브라우저 User-Agent 가 없으면 잠든 앱이 깨지 않는다).
브라우저 User-Agent 로 불렀는데도 health 가 JSON 대신 "대기 중" 안내 페이지(503)를 계속 주거나 commit 이 안 바뀌면 멈추고, 콘솔 → 내 앱 → 배포 기록에서 그 커밋 줄의 상태·사유를 알려 달라고 저에게 요청한다(PART 0 5-d). "자동 승인 중"이면 잠시 뒤 뜬다
4. ADMIN_PATH 값을 저에게 다시 받아서(3번에서 배포 기록을 물을 때 함께 물어도 된다. 대화 앞부분의 기억에 기대지 않는다 — 제가 콘솔에 저장하고 비밀번호 관리 앱에 적어 둔 값이 기준) node scripts/smoke-live.mjs https://이름.ridm.ai --admin <ADMIN_PATH 값> 를 한 번 실행 → 실패 0개.
이 한 번이 관리자 항목과 기존 기능(가입·메모·로그아웃·격리) 회귀를 모두 본다 — 가입 제한(같은 IP 1시간 5회) 때문에 따로 또 돌리지 않는다
5. 마지막 "사용자 확인" 항목(브라우저로 로그인해 숫자가 보이는지)은 저에게 요청하고, 제 답을 받기 전엔 완료라고 말하지 않는다
6. 관리자 화면을 제가 원하는 대로 바꾸고 싶으면 components/AdminPanel.tsx·AdminPanel.module.css 만 손본다 — lib/admin.ts·lib/admin-handlers.ts·app/[adminPath] 는 책 그대로
- 바꿔도 되는 것: 배치·색·글꼴·문구·카드 순서·표 모양
- 하지 않는 것: 비밀번호나 관리자 주소를 localStorage·쿠키·코드에 저장 / 회원 아이디·정지 사유를 HTML 로 넣기(dangerouslySetInnerHTML 금지 — 글자로만 보여 준다) /
API 주소를 base prop 대신 직접 적기(주소가 코드에 들어간다) / 로그인 입력칸의 "관리자 비밀번호" 라벨 지우기(smoke-live 가 이 글자로 로그인 화면을 확인한다) /
회원의 메모 내용을 보여 주는 기능 더하기(관리자 API 는 개수만 준다 — 내용을 읽는 라우트를 만들지 않는다)
운영 규칙 (꼭 읽기)
node scripts/make-admin-path.mjs 로 새 값을 만들어 ADMIN_PATH 에 저장합니다. ADMIN_PATH 만 바꾸면 기존 관리자 로그인은 살아 있으니, 로그아웃까지 원하면 비밀번호도 바꾸세요.ADMIN_PASSWORD 에 저장 → 앱이 재시작되고, 기존 관리자 로그인은 즉시 전부 풀립니다. 비밀번호가 샜다고 의심되면 바로 바꾸세요.make-admin-path 로 새 ADMIN_PATH 를 만들어 바꾸세요(비밀번호도 같이 바꾸면 기존 관리자 로그인도 풀립니다).getSessionUser() 로 회원을 확인하세요. 그래야 정지된 회원이 자동으로 막힙니다.완료 기준: npm run check 통과 + DB 테스트 한 줄("실행(skipped 0)" / "미실행(테스트 DB 없음)") · smoke-live --admin 의 회원 항목도 전부 PASS(기존 기능 회귀 없음) · smoke-live --admin 관리자 항목 실패 0개(/admin 404 · 틀린 주소 404 · 비밀 주소에 로그인 화면 · 로그인 없이 숫자 요청 401 · 출처 없는 요청 403 · 틀린 비밀번호 401) · 사용자가 직접 로그인해 숫자와 회원 목록을 확인.
lib/admin.ts — 관리자 본체 — 설정 확인·비밀번호·관리자 세션·회원 목록·정지·요약 숫자(메모는 개수만)코드 자리: cat lib/admin.ts · 없거나 verify 가 "다름"이면 node scripts/get-book-code.mjs · 손으로 고치지 않는다 · 원문(사람용 — AI 는 열지 않는다)
lib/admin-handlers.ts — 관리자 API — 출처 검사·로그인 제한·op 나누기코드 자리: cat lib/admin-handlers.ts · 없거나 verify 가 "다름"이면 node scripts/get-book-code.mjs · 손으로 고치지 않는다 · 원문(사람용 — AI 는 열지 않는다)
app/[adminPath]/page.tsx — 관리자 화면 입구 (주소가 ADMIN_PATH 와 다르면 404)코드 자리: cat app/[adminPath]/page.tsx · 없거나 verify 가 "다름"이면 node scripts/get-book-code.mjs · 손으로 고치지 않는다 · 원문(사람용 — AI 는 열지 않는다)
app/[adminPath]/api/[...op]/route.ts — 관리자 API 입구코드 자리: cat app/[adminPath]/api/[...op]/route.ts · 없거나 verify 가 "다름"이면 node scripts/get-book-code.mjs · 손으로 고치지 않는다 · 원문(사람용 — AI 는 열지 않는다)
components/AdminPanel.tsx — 관리자 화면 (모양은 바꿔도 됨)코드 자리: cat components/AdminPanel.tsx · 고쳐도 되는 파일(5-b) — 관리자 화면 모양만(B-7) · 원문(사람용 — AI 는 열지 않는다)
components/AdminPanel.module.css — 관리자 화면 스타일코드 자리: cat components/AdminPanel.module.css · 고쳐도 되는 파일(5-b) — 관리자 화면 모양만(B-7) · 원문(사람용 — AI 는 열지 않는다)
scripts/make-admin-path.mjs — 관리자 주소 값 만들기코드 자리: cat scripts/make-admin-path.mjs · 없거나 verify 가 "다름"이면 node scripts/get-book-code.mjs · 손으로 고치지 않는다 · 원문(사람용 — AI 는 열지 않는다)
tests/admin.test.ts — 관리자 테스트코드 자리: cat tests/admin.test.ts · 없거나 verify 가 "다름"이면 node scripts/get-book-code.mjs · 손으로 고치지 않는다 · 원문(사람용 — AI 는 열지 않는다)
AI용 분할본 · 지도 6단계(선택 기능 — 결정서에 관리자 "켬"일 때만) · 이 페이지 전에 읽을 페이지: PART 0 사용법·AI 시작 지침, 부록 A 스펙·코드 받기(A-2), PART 8 배포 · 코드 파일은 여기 없다 — 코드 자리의 줄을 따른다(폴더에 책 파일이 하나도 없는 처음이면 부록 A-2 의 curl 한 줄부터)