一键导入
yebigun-training
사용자가 직접 로그인한 예비군 홈페이지(yebigun1.mil.kr) Chrome 세션을 재사용해 이번 훈련 일정/장소를 조회하고, 작년 이전 기록과 비교해 무엇이 달라졌는지 알려준다. 조회 전용이며 로그인이나 연기·보류 신청은 절대 자동화하지 않는다.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
사용자가 직접 로그인한 예비군 홈페이지(yebigun1.mil.kr) Chrome 세션을 재사용해 이번 훈련 일정/장소를 조회하고, 작년 이전 기록과 비교해 무엇이 달라졌는지 알려준다. 조회 전용이며 로그인이나 연기·보류 신청은 절대 자동화하지 않는다.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | yebigun-training |
| description | 사용자가 직접 로그인한 예비군 홈페이지(yebigun1.mil.kr) Chrome 세션을 재사용해 이번 훈련 일정/장소를 조회하고, 작년 이전 기록과 비교해 무엇이 달라졌는지 알려준다. 조회 전용이며 로그인이나 연기·보류 신청은 절대 자동화하지 않는다. |
| license | MIT |
| metadata | {"category":"legal","locale":"ko-KR","phase":"v1"} |
공식 예비군 홈페이지(https://www.yebigun1.mil.kr)에서 이미 로그인된 브라우저 세션을 재사용해:
IvdTraScheDetail.do) 페이지를 조회해 소속 정보, 이번 훈련 기간/장소/훈련종류, 그리고 같은 페이지에 이미 표시되는 과거 연도 훈련 기록까지 한 번에 가져온다 (training-info).view).open-menu) — 신청/제출도 절대 자동화하지 않는다.record)하고 비교(diff)할 수도 있다 (사이트가 더 오래된 기록을 보여주지 않게 되거나, 메모를 남기고 싶을 때를 위한 보조 기능).inspect)로 아직 분류 안 된 다른 페이지 구조를 확인할 수도 있다.hipass-receipt와 동일한 설계 원칙을 따른다: 로그인은 항상 사용자가 직접 하고, 이 스킬은 로그인된 세션에서 조회만 한다.
open-menu는 "화면 열기"까지만이다. 버튼 클릭이든(selfSelect/nationalUnit/holiday) 직접 이동이든(delay/hold/holdCancel/editProfile/honors) 화면으로 이동만 시켜주지만, 그 화면에서 날짜를 고르거나 사유를 입력하거나 제출 버튼을 누르는 것은 절대 하지 않는다. 다음 화면이 뜨면 즉시 멈추고 "여기서부터는 직접 진행하라"고 안내한다 — 사용자의 실제 화면(같은 Chrome 창)에 이미 그 화면이 열려 있다.view는 식별 정보가 없는 페이지에만 쓴다. VIEW_MENUS에 등록된 메뉴만 실제로 읽어서 표(headers/rows)로 돌려준다 — 이름/군번/주민등록번호/전화번호/주소가 마크업에 그대로 노출되는 페이지는 절대 VIEW_MENUS에 넣지 않고 open-menu(APPLICATION_MENUS)로 돌린다.~/.cache/k-skill/yebigun-training/history.json에만 저장한다. 절대 이 git 저장소에 실제 개인 데이터를 커밋하지 않는다 — test/fixtures/의 페이지 구조 fixture는 처음부터 끝까지 완전히 가상의 이름/날짜/장소로만 작성됐다(실제 로그인 세션에서 본 마크업 구조만 베껴서 재현했고, 그 세션에서 본 실제 값은 어디에도 옮기지 않았다).yebigun1.mil.kr에 PASS/공동인증서/간편인증으로 로그인해야만 보인다 (도메인은 mma.go.kr이 아니라 yebigun1.mil.kr).parseTrainingInfo를 비워뒀었다. 2026-06-24, 사용자가 직접 로그인한 세션으로 실제 "나의 훈련정보" 페이지(/dmobis/rfh/rgt/edutrasubjpsn/IvdTraScheDetail.do)를 inspect로 확인하고 구조를 확정했다:
<caption>소속</caption>이 있는 표 (예비군부대/소속/계급/군번/성명/군별/연차/동원구분/비고).<h4>훈련내용(YYYY년)</h4> 헤딩.<table id="detailTb"> — 첫 행은 "총계" 합계 행(건너뜀), 나머지 행에 data-tra-id 등 속성과 구분/훈련일자/계획시간/실시시간/잔여시간/훈련결과/훈련장/비고 8개 열.<table id="detail2">에 이미 들어있다 (화면에는 "이전 훈련내용 열기"를 눌러야 보이지만, domcontentloaded 시점에 이미 DOM에 존재함 — 추가 클릭 없이 바로 읽을 수 있다). 같은 8개 열 구조를 공유한다.training-info 호출로 끝난다: 페이지 자체가 여러 해의 기록을 동시에 보여준다.goAction(mode) 자바스크립트 함수가 훈련일정 자율선택/전국단위 훈련신청/휴일예비군 훈련신청 세 버튼을 각각 다른 신청서 URL로 연결한다는 것도 이때 확인했다 — 이게 open-menu가 클릭하는 정확한 버튼 레이블의 출처다. (이 페이지엔 goAction이 TRASCHD라는 네 번째 분기도 갖고 있었지만, 그 분기와 연결된 보이는 버튼을 이번 검증에서 찾지 못해 APPLICATION_MENUS에 포함하지 않았다 — 확인 안 된 걸 추측해서 넣지 않는다.)<a href> 목록)를 같은 inspect로 훑어서 훈련 연기신청(/dmobis/rft/rgt/ivdTraDelayApplInForm.do), 보류 신청(/dmobis/rfh/rrm/holdpsn/HoldPsnReqForm.do), 해소 신청(/dmobis/rfh/rrm/holdpsn/HoldPsnCancelReqForm.do)의 실제 경로도 확인했다 (2026-06-24). 이 셋은 goAction 버튼이 아니라 평범한 링크라서 클릭 시뮬레이션 없이 바로 page.goto로 이동한다 — open-menu가 mode: "goto"로 구분해서 처리한다.
훈련 연기신청 화면은 실제로 열어보니 이름/주민등록번호 앞자리/주소/휴대폰·집·직장 전화번호가 hidden input으로 그대로 박혀 있었다 — training-info보다 훨씬 민감한 페이지다. 이 값들은 어디에도 기록하지 않았고, 오직 페이지 URL(사이트 구조)만 APPLICATION_MENUS에 남겼다.<a href> 목록, 사이트맵과 동일 출처)를 훑어서 10개 후보 페이지를 찾고, 각각 <thead> 헤더와 hidden field를 스캔해서 둘로 분류했다:
VIEW_MENUS(실제로 읽음): 훈련신청 결과(NationalUnitResevForcesTraRltList.do), 연기신청 결과(ivdTraDelayApplRltList.do), 보류·해소 신청결과(HoldPsnReqRsltList.do), 휴일예비군 훈련일정 조회(HolidayTrainingScheduleList.do), 소속부대 공지사항(MyPubAnnounList.do), 훈련안내(TraNoticeList.do), 나의 질의응답(MyQuestAnsList.do), 예비군부대 찾기(listAdminAddr.do). 헤더에 군번/이름 같은 식별 컬럼이 없는 것만 골랐다.APPLICATION_MENUS로 추가(절대 안 읽음): 개인정보수정(ReserveForceForm.do — 편집 폼이고 hidden field에 cellPhone 등 직접 노출 확인), 예비군 상훈(ReserveForcePrzdcr.do — 페이지 상단에 training-info의 "소속" 표와 똑같이 군번/성명이 그대로 들어있는 걸 확인, 그 아래 실제 상훈 목록은 따로 검증하지 않았다).나의 질의응답(myQna)은 실제로 열어보니 "내가 쓴 글"이 아니라 다른 사용자들의 질문도 함께 보이는 공개 게시판이었다 (작성자 이름은 사이트 자체가 이미 "민*연"처럼 마스킹해서 보여준다). 마스킹된 이름만 노출되므로 VIEW_MENUS에 남겼지만, 다른 사용자의 (마스킹된) 글 내용을 답변에 과도하게 인용하지 않는다.VIEW_MENUS 페이지(예: 훈련신청 결과)는 초기 HTML에 Loading... placeholder 행만 있고 실제 데이터는 AJAX로 나중에 채워진다는 것도 이때 확인했다 — fetchInquiry가 이 placeholder를 실제 데이터로 착각해 반환하지 않도록, 사라질 때까지 짧게 폴링한 뒤(최대 약 4.5초) 그래도 안 사라지면 명확한 에러로 멈춘다.openservice.go.kr/youthMilTrainSch)는 이번 검증에서 비교 대상으로 시도하지 않았다. 이미 yebigun1.mil.kr 쪽 구조가 안정적으로 파싱돼서 추가 검증이 급하지 않다고 판단했다 — 필요해지면 같은 방식(inspect)으로 비교해본다.trainings가 빈 배열로 나올 수 있다. 이 경우 "훈련이 없다"로 해석하지 말고 구조가 바뀌었다고 보고 재검증한다 (Failure modes 참고). view도 마찬가지: headers가 비어 있으면 "내용이 없다"가 아니라 그 페이지에 <thead>가 없다는 뜻이므로 구조 변경을 의심한다.packages/yebigun-training에서 npm install (playwright-core 포함)node packages/yebigun-training/src/cli.js chrome-command --profile-dir "$HOME/.cache/k-skill/yebigun-chrome" --debugging-port 9222
위 명령이 출력한 Chrome 실행문으로 브라우저를 띄운 뒤, 사용자가 직접 https://www.yebigun1.mil.kr/에서 로그인한다.
node packages/yebigun-training/src/cli.js training-info --cdp-url http://127.0.0.1:9222
결과 JSON의 구조:
member: 예비군부대/소속/계급/군번/성명/군별/연차/동원구분.currentDisplayYear: 사이트가 "올해"로 표시하는 연도.trainings: 올해 + 과거 연도 훈련 기록 배열(최신순), 각 항목에 trainingType/startDate/endDate/plannedHours/actualHours/remainingHours/result/location.comparison: currentDisplayYear vs currentDisplayYear - 1을 자동 비교한 { hasCurrentRecord, hasPreviousRecord, current, previous, changes }.세션이 만료됐으면 "session is not authenticated or has expired" 에러가 즉시 뜬다 — 이때는 사용자에게 재로그인을 안내하고 중단한다.
사용자에게 결과를 전달할 때: 올해 훈련의 기간/장소/구분과 comparison.changes(필드별 변경점)를 한국어로 자연스럽게 요약한다. comparison.hasPreviousRecord가 false면 "비교할 작년 기록이 없다"고 그대로 말한다 — 추측하지 않는다.
view로 실제 데이터를 읽어온다node packages/yebigun-training/src/cli.js view --menu applicationResults --cdp-url http://127.0.0.1:9222
--menu로 지정 가능한 값 (모두 VIEW_MENUS, 식별 정보 없는 조회 전용):
| menu 값 | 화면 |
|---|---|
applicationResults | 훈련신청 결과 |
delayResults | 연기신청 결과 |
holdResults | 보류·해소 신청결과 |
holidaySchedule | 휴일예비군 훈련일정 조회 |
unitNotices | 소속부대 공지사항 |
trainingNotices | 훈련안내 |
myQna | 나의 질의응답 (다른 사용자의 마스킹된 글도 함께 보이는 공개 게시판이다 — 응답에서 남의 글을 과도하게 인용하지 않는다) |
unitFinder | 예비군부대 찾기 |
결과는 항상 { menu, label, headers, rows } 형태의 일반 표다 (페이지마다 다른 필드명을 따로 만들지 않았다). headers는 채워져 있는데 rows가 빈 배열이면 "등록된 게 없다"는 뜻이고, headers까지 비어 있으면 페이지 구조가 바뀐 것으로 보고 inspect로 재확인한다. 예비군부대 찾기/휴일예비군 훈련일정 조회처럼 검색 조건 없이 처음 들어가면 비어 있는 게 정상인 페이지도 있다.
node packages/yebigun-training/src/cli.js open-menu --menu delay --cdp-url http://127.0.0.1:9222
--menu로 지정 가능한 값과 동작 방식 (모두 APPLICATION_MENUS, 데이터는 절대 읽지 않는다):
| menu 값 | 화면 | 방식 |
|---|---|---|
selfSelect | 훈련일정 자율선택 | training-info 페이지의 실제 버튼을 클릭 |
nationalUnit | 전국단위 훈련신청 | training-info 페이지의 실제 버튼을 클릭 |
holiday | 휴일예비군 훈련신청 | training-info 페이지의 실제 버튼을 클릭 |
delay | 훈련 연기신청 | /dmobis/rft/rgt/ivdTraDelayApplInForm.do로 직접 이동 |
hold | 보류 신청 | /dmobis/rfh/rrm/holdpsn/HoldPsnReqForm.do로 직접 이동 |
holdCancel | 해소 신청 | /dmobis/rfh/rrm/holdpsn/HoldPsnCancelReqForm.do로 직접 이동 |
editProfile | 개인정보수정 | /dmobis/rfh/rrm/reserveforce/ReserveForceForm.do로 직접 이동 |
honors | 예비군 상훈 | /dmobis/rfh/rrm/reserveforce/ReserveForcePrzdcr.do로 직접 이동 |
어느 방식이든 다음 화면이 뜨면 그 자리에서 멈춘다 — 날짜 선택, 사유 입력, 제출은 절대 하지 않는다. 훈련 연기신청/개인정보수정 화면은 실제로 들어가보면 이름/주민등록번호 앞자리/주소/휴대폰·집·직장 전화번호가 그대로 보일 만큼 training-info보다 훨씬 민감하다 — 이 정보를 답변이나 파일에 옮기지 않는다.
호출 후 사용자에게는 정확히 이렇게 안내한다: "지금 보고 계신 Chrome 창에 <label> 화면이 열렸습니다. 여기서부터 입력과 제출은 직접 해주세요." 표에 없는 메뉴는 open-menu/view 둘 다 지원하지 않으므로(Unknown menu 에러), 사이트맵(/dmobis/rfh/rgt/sitemap/sitemap.jsp)이나 inspect로 먼저 실제 경로/구조를 확인한 뒤 어느 쪽(조회 vs 안내만)에 넣을지 식별 정보 노출 여부로 판단해서 사용자와 상의한다.
node packages/yebigun-training/src/cli.js inspect --cdp-url http://127.0.0.1:9222 --path <경로> --full
pageInfo.pageType이 login이면 재로그인을 안내한다.
사이트의 이전 훈련내용 표가 보여주는 연도 범위를 벗어나는 기록을 직접 남겨두고 싶을 때만 쓴다. training-info의 trainings 배열에서 원하는 연도의 항목을 골라 그대로 저장하면 된다.
node packages/yebigun-training/src/cli.js record --year 2026 --json '{"trainingType":"동원훈련Ⅱ형 1차","startDate":"2026-08-10","endDate":"2026-08-12","location":"OO과학화예비군훈련장"}'
node packages/yebigun-training/src/cli.js diff --year 2026
기본 비교 대상은 year - 1이다 (--compare-year로 다른 연도 지정 가능). 결과의 hasPreviousRecord가 false면 "작년 기록이 없어 비교할 수 없다"고 명확히 말한다.
open-menu로 화면까지 열어준 경우에도 "여기서부터는 직접 진행하세요"를 빼지 않는다.view(myQna 등) 결과에 다른 사용자의 마스킹된 이름/글이 섞여 있으면, 사용자가 직접 물어본 본인 관련 내용에 집중하고 남의 글을 장문으로 인용하지 않는다.view가 빈 rows를 반환하면 "등록된 게 없다"고 그대로 말한다 (검색 조건이 필요한 페이지일 수도 있음을 함께 안내). headers까지 비어 있으면 페이지 구조가 바뀐 것으로 보고 inspect로 재확인을 제안한다.trainings가 비정상적으로 비어 있는 등) 그 가능성을 사용자에게 알리고 inspect로 재확인을 제안한다.npm run lint && npm test — detectSessionState/inspectYebigunPage 분류, 완전히 가상의 데이터로 작성한 fixture(test/fixtures/training-info-page.html, test/fixtures/view-list-page.html) 기반 parseTrainingInfo/parseGenericTable 단위 테스트, mocked-CDP inspect/fetchTrainingInfo/fetchInquiry(AJAX Loading... 폴링 포함)/openApplicationMenu, 그리고 record/history/diff의 로컬 JSON 로직 단위 테스트.chrome-command로 출력된 명령이 실제 Chrome을 올바른 프로필/포트로 띄우는지 확인.training-info(소속 정보 · 올해 훈련(2026.06.22~2026.06.25, 동원훈련Ⅱ형 1차) · 과거 3개 연도 기록 · 작년 대비 비교), open-menu(selfSelect/delay/honors/editProfile 모두 정확한 화면으로 이동, 데이터 미추출 확인), view(8개 메뉴 전부 실행 — delayResults/myQna는 실제 데이터, 나머지는 빈 목록 또는 "검색된 데이터가 없습니다" 확인)까지 전부 실행해 정확히 동작하는 것을 확인했다.inspect/training-info/view/open-menu가 pageType: "login" 또는 "session is not authenticated or has expired" 에러를 반환한다 — 즉시 중단하고 재로그인 안내.trainings가 비정상적으로 비어 있거나 member가 null: 사이트가 마크업을 바꿨을 가능성이 높다. "훈련이 없다"고 단정하지 말고 inspect --full로 실제 HTML을 다시 확인하라고 안내한다.diff(로컬 기록)에서 비교 연도 기록이 없음: 추측하지 말고 "작년 기록이 없다"고 그대로 전달한다.chrome-command로 띄운 디버깅 포트 Chrome이 켜져 있는지, --cdp-url이 맞는지 확인하라고 안내한다.open-menu/view에 알 수 없는 --menu 값: 브라우저를 건드리기 전에 Unknown menu 에러로 막는다 (각자 APPLICATION_MENUS/VIEW_MENUS의 키만 허용).open-menu가 버튼을 못 찾음: "Could not find the ... button" 에러를 반환한다 — 사이트가 버튼 레이블/구조를 바꿨을 가능성이 높으므로 inspect로 재확인하라고 안내한다.view가 AJAX 로딩을 끝내 못 기다림: "did not finish loading in time" 에러를 반환한다 — 한 번 더 view를 시도하거나, 계속 반복되면 inspect로 실제 응답을 확인하라고 안내한다.training-info가 소속 정보, 올해 훈련 기간/장소/종류, 과거 연도 기록, 작년 대비 비교를 한 번에 정확히 반환한다.open-menu가 화면까지만 이동시키고(클릭이든 직접 이동이든), 그 화면의 입력/제출은 절대 건드리지 않는다 — 신청형 화면뿐 아니라 식별 정보가 노출되는 조회형 화면(개인정보수정/예비군 상훈)도 같은 원칙으로 막는다.view가 VIEW_MENUS에 등록된, 식별 정보 없는 화면만 실제로 읽어서 일반화된 headers/rows로 돌려준다. AJAX로 늦게 채워지는 목록을 placeholder 상태로 잘못 반환하지 않는다.record로 저장한 연도별 기록이 ~/.cache/k-skill/yebigun-training/history.json에만 남고 저장소에는 커밋되지 않는다.법원 전자소송 지급명령 신청을 위해 채권자·채무자·청구금액·청구원인·증빙 정보를 인터뷰하고, 신청서 초안과 공식 전자소송 브라우저 입력 handoff를 준비한다.
국방전자조달시스템(D2B) 공개 입찰공고 검색 화면에서 공고건명·공고구분·공고/개찰일·G2B공고번호·발주기관 조건으로 공고를 조회한다.
S2B 학교장터 공개 고객 공고/견적요청 목록을 브라우저 우선으로 조회하고 fixture HTML을 파싱해 공고 메타데이터를 정리한다.
조달청 나라장터 발주계획현황서비스로 물품·공사·용역·외자 발주계획 목록을 검색한다.
당근부동산(realty.daangn.com) 공개 웹 데이터로 지역 기반 부동산 매물 검색과 상세 확인을 수행한다. 문의/예약/계약 자동화는 제외한다.
사업자등록번호 하나로 "이 사업자, 실제 문제 없나"를 확인한다 — 국세청 사업자등록 상태·국민연금 가입 사업장·국세 체납 명단·금융위 법인개요·조달청 부정당제재·지방행정 인허가 영업상태를 무료 공공 데이터로 교차 조회해 사실만 병렬하는 실사 리포트(점수·등급·위험 판정 없음).