[{"data":1,"prerenderedAt":2519},["ShallowReactive",2],{"doc:\u002Fnice_auth":3},{"id":4,"title":5,"body":6,"description":299,"extension":2513,"meta":2514,"navigation":1691,"path":2515,"seo":2516,"stem":2517,"__hash__":2518},"docs\u002FNICE_AUTH.md","NICE 통합인증(휴대폰 본인확인) — 적용 가이드",{"type":7,"value":8,"toc":2465},"minimark",[9,13,68,71,76,181,188,190,194,197,269,284,286,290,300,302,306,313,382,384,388,396,401,407,412,461,475,480,580,594,601,605,611,615,702,753,757,802,810,814,855,870,877,886,892,903,910,914,919,923,969,974,1020,1022,1026,1030,1036,1039,1045,1049,1055,1059,1099,1102,1104,1108,1281,1285,1306,1308,1312,1320,1372,1378,1508,1516,1551,1555,1561,1569,1576,1892,1900,1903,1979,1981,1985,1992,1999,2005,2026,2032,2116,2126,2130,2133,2139,2143,2175,2177,2181,2284,2289,2291,2295,2437,2439,2443,2450,2461],[10,11,5],"h1",{"id":12},"nice-통합인증휴대폰-본인확인-적용-가이드",[14,15,16,57,63],"blockquote",{},[17,18,19,23,24,30,31,34,35,38,39,23,42,46,47,51,52,56],"p",{},[20,21,22],"strong",{},"정본 출처",": ",[25,26,27],"a",{"href":27,"rel":28},"https:\u002F\u002Fauth-guide.niceid.co.kr\u002F",[29],"nofollow"," — NICE 통합인증 API 가이드 \u002F 명세서 \u002F 응답코드\n",[20,32,33],{},"우리 적용 범위",": 회원가입 Step 4 \"휴대폰 본인 인증\"을 자체 SMS OTP에서 NICE ",[20,36,37],{},"휴대폰(M)"," 으로 교체.\n",[20,40,41],{},"연관",[25,43,45],{"href":44},".\u002FMEMBERSHIP",".\u002FMEMBERSHIP.md"," §6.7·§6.8, ",[25,48,50],{"href":49},".\u002Fpages\u002FSIGNUP",".\u002FSIGNUP.md"," §8 #4b·#8, ",[25,53,55],{"href":54},".\u002Fhistory\u002Fhistory.20260602",".\u002Fhistory\u002Fhistory.20260602.md"," §5·§16",[17,58,59,62],{},[20,60,61],{},"마지막 현행화",": 2026-06-02 (§9.1 자격증명 등록 시도 + 1007 IP 미해결로 mock 복귀 반영)",[17,64,65,67],{},[20,66,61],{},": 2026-06-02",[69,70],"hr",{},[72,73,75],"h2",{"id":74},"_1-무엇이-다른가-자체-sms-otp-vs-nice-본인확인","1. 무엇이 다른가 (자체 SMS OTP vs NICE 본인확인)",[77,78,79,101],"table",{},[80,81,82],"thead",{},[83,84,85,89,95],"tr",{},[86,87,88],"th",{},"항목",[86,90,91,94],{},[20,92,93],{},"자체 SMS OTP"," (현재 구현)",[86,96,97,100],{},[20,98,99],{},"NICE 휴대폰 본인확인 (M)"," (도입 예정)",[102,103,104,116,127,138,149,160,171],"tbody",{},[83,105,106,110,113],{},[107,108,109],"td",{},"확인하는 것",[107,111,112],{},"\"이 휴대폰 번호를 누가 가지고 있는가\"",[107,114,115],{},"\"이 사람이 진짜 본인인가 (실명·생년월일·CI)\"",[83,117,118,121,124],{},[107,119,120],{},"필요한 정보",[107,122,123],{},"휴대폰 번호",[107,125,126],{},"통신사 + 이름 + 주민등록번호 + 휴대폰 번호",[83,128,129,132,135],{},[107,130,131],{},"응답 데이터",[107,133,134],{},"검증 통과 여부(boolean)",[107,136,137],{},"이름 · 생년월일 · 성별 · CI · DI · 통신사 · 휴대폰",[83,139,140,143,146],{},[107,141,142],{},"외부 비용",[107,144,145],{},"0 (자체 SMS 발송)",[107,147,148],{},"NICE 건당 과금 (계약별)",[83,150,151,154,157],{},[107,152,153],{},"실명 확인 효력",[107,155,156],{},"❌ 없음",[107,158,159],{},"✅ 법적 본인 확인",[83,161,162,165,168],{},[107,163,164],{},"중복 가입 방지 (DI)",[107,166,167],{},"❌",[107,169,170],{},"✅",[83,172,173,176,178],{},[107,174,175],{},"통신3사 PASS 앱",[107,177,167],{},[107,179,180],{},"❌ (NICE 통합인증에서는 미지원 — 표준창 내 직접 입력)",[17,182,183,184,187],{},"→ ",[20,185,186],{},"자체 SMS OTP는 비밀번호 재설정·이메일 변경 등 단순 검증에 유지",". 회원가입의 본인 확인은 NICE로 분리.",[69,189],{},[72,191,193],{"id":192},"_2-nice-통합인증-인증-수단-종류","2. NICE 통합인증 인증 수단 종류",[17,195,196],{},"NICE 통합인증 API는 4종 수단을 선택형으로 제공 (한 표준창에서 사용자가 선택).",[77,198,199,212],{},[80,200,201],{},[83,202,203,206,209],{},[86,204,205],{},"코드",[86,207,208],{},"수단",[86,210,211],{},"우리 적용",[102,213,214,231,244,256],{},[83,215,216,222,225],{},[107,217,218],{},[219,220,221],"code",{},"M",[107,223,224],{},"휴대폰 본인확인",[107,226,227,228],{},"✅ ",[20,229,230],{},"회원가입 Step 4 기본",[83,232,233,238,241],{},[107,234,235],{},[219,236,237],{},"F",[107,239,240],{},"금융인증서",[107,242,243],{},"선택 노출 가능",[83,245,246,251,254],{},[107,247,248],{},[219,249,250],{},"U",[107,252,253],{},"공동인증서",[107,255,243],{},[83,257,258,263,266],{},[107,259,260],{},[219,261,262],{},"I",[107,264,265],{},"아이핀",[107,267,268],{},"노출 안 함 (사용률 낮음)",[17,270,271,272,275,276,279,280,283],{},"우리 ",[219,273,274],{},"svc_types","는 처음에 ",[219,277,278],{},"[\"M\"]"," 단독으로 출시 → 사용자 피드백 보고 ",[219,281,282],{},"[\"M\", \"F\", \"U\"]","로 확장.",[69,285],{},[72,287,289],{"id":288},"_3-전체-시퀀스-5-단계","3. 전체 시퀀스 (5 단계)",[291,292,297],"pre",{"className":293,"code":295,"language":296},[294],"language-text","[ 우리 워커 ]              [ NICE 서버 ]            [ 사용자 브라우저 ]\n   │                          │                          │\n   │ 1. POST \u002Fauth\u002Ftoken     ─►│                          │\n   │ ◄── access_token, ticket │                          │\n   │                          │                          │\n   │ 2. POST \u002Fauth\u002Furl       ─►│                          │\n   │ ◄── auth_url             │                          │\n   │                          │                          │\n   │ ──── auth_url 응답 ────────────────────────────────►│\n   │                          │                          │\n   │                          │ 3. 표준창 팝업 (사용자)  ◄┤\n   │                          │     사용자 본인 입력      │\n   │                          │ ── 인증 완료 ──────────► │\n   │                          │                          │\n   │                          │       4. return_url callback │\n   │ ◄────────────────── web_transaction_id ─────────────┤\n   │                          │                          │\n   │ 5. POST \u002Fauth\u002Fresult    ─►│                          │\n   │ ◄── enc_data, integrity  │                          │\n   │     (AES-256-GCM 복호화)  │                          │\n   │     name·birthdate·CI·DI·mobile_no·mobile_co       │\n","text",[219,298,295],{"__ignoreMap":299},"",[69,301],{},[72,303,305],{"id":304},"_4-api-엔드포인트-3종","4. API 엔드포인트 3종",[17,307,308,309,312],{},"모두 ",[219,310,311],{},"https:\u002F\u002Fauth.niceid.co.kr"," 호스트 (단일).",[77,314,315,331],{},[80,316,317],{},[83,318,319,322,325,328],{},[86,320,321],{},"#",[86,323,324],{},"메서드",[86,326,327],{},"경로",[86,329,330],{},"인증",[102,332,333,353,368],{},[83,334,335,338,341,346],{},[107,336,337],{},"1",[107,339,340],{},"POST",[107,342,343],{},[219,344,345],{},"\u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Ftoken",[107,347,348,349,352],{},"Basic (",[219,350,351],{},"client_id:client_secret",")",[83,354,355,358,360,365],{},[107,356,357],{},"2",[107,359,340],{},[107,361,362],{},[219,363,364],{},"\u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Furl",[107,366,367],{},"Bearer (token 1에서 발급)",[83,369,370,373,375,380],{},[107,371,372],{},"3",[107,374,340],{},[107,376,377],{},[219,378,379],{},"\u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Fresult",[107,381,367],{},[69,383],{},[72,385,387],{"id":386},"_5-단계별-명세","5. 단계별 명세",[389,390,392,393],"h3",{"id":391},"_51-토큰-발급-post-idointcv10authtoken","5.1 토큰 발급 — ",[219,394,395],{},"POST \u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Ftoken",[17,397,398],{},[20,399,400],{},"Request Headers",[291,402,405],{"className":403,"code":404,"language":296},[294],"Authorization: Basic {Base64UrlEncoding(client_id:client_secret)}\nContent-Type: application\u002Fjson\nX-Intc-DevLang: Cloudflare-Workers\u002FTypeScript\n",[219,406,404],{"__ignoreMap":299},[17,408,409],{},[20,410,411],{},"Request Body",[291,413,417],{"className":414,"code":415,"language":416,"meta":299,"style":299},"language-json shiki shiki-themes github-light github-dark","{\n  \"grant_type\": \"client_credentials\",\n  \"request_no\": \"A1234567890123456789\"\n}\n","json",[219,418,419,428,444,455],{"__ignoreMap":299},[420,421,424],"span",{"class":422,"line":423},"line",1,[420,425,427],{"class":426},"sVt8B","{\n",[420,429,431,435,437,441],{"class":422,"line":430},2,[420,432,434],{"class":433},"sj4cs","  \"grant_type\"",[420,436,23],{"class":426},[420,438,440],{"class":439},"sZZnC","\"client_credentials\"",[420,442,443],{"class":426},",\n",[420,445,447,450,452],{"class":422,"line":446},3,[420,448,449],{"class":433},"  \"request_no\"",[420,451,23],{"class":426},[420,453,454],{"class":439},"\"A1234567890123456789\"\n",[420,456,458],{"class":422,"line":457},4,[420,459,460],{"class":426},"}\n",[462,463,464],"ul",{},[465,466,467,470,471,474],"li",{},[219,468,469],{},"request_no",": 회원사 요청 고유번호. 20~50자. ",[20,472,473],{},"각 인증 세션마다 새로 생성"," (uuid 또는 timestamp+nonce).",[17,476,477],{},[20,478,479],{},"Response",[291,481,483],{"className":414,"code":482,"language":416,"meta":299,"style":299},"{\n  \"result_code\": \"0000\",\n  \"result_message\": \"응답성공\",\n  \"access_token\": \"eyJhbGciOiJIUzUxMiJ9...\",\n  \"expires_in\": 1762940529000,\n  \"token_type\": \"Bearer\",\n  \"iterators\": 66,\n  \"ticket\": \"UzEyMDI1MTEx...\"\n}\n",[219,484,485,489,501,513,525,538,551,564,575],{"__ignoreMap":299},[420,486,487],{"class":422,"line":423},[420,488,427],{"class":426},[420,490,491,494,496,499],{"class":422,"line":430},[420,492,493],{"class":433},"  \"result_code\"",[420,495,23],{"class":426},[420,497,498],{"class":439},"\"0000\"",[420,500,443],{"class":426},[420,502,503,506,508,511],{"class":422,"line":446},[420,504,505],{"class":433},"  \"result_message\"",[420,507,23],{"class":426},[420,509,510],{"class":439},"\"응답성공\"",[420,512,443],{"class":426},[420,514,515,518,520,523],{"class":422,"line":457},[420,516,517],{"class":433},"  \"access_token\"",[420,519,23],{"class":426},[420,521,522],{"class":439},"\"eyJhbGciOiJIUzUxMiJ9...\"",[420,524,443],{"class":426},[420,526,528,531,533,536],{"class":422,"line":527},5,[420,529,530],{"class":433},"  \"expires_in\"",[420,532,23],{"class":426},[420,534,535],{"class":433},"1762940529000",[420,537,443],{"class":426},[420,539,541,544,546,549],{"class":422,"line":540},6,[420,542,543],{"class":433},"  \"token_type\"",[420,545,23],{"class":426},[420,547,548],{"class":439},"\"Bearer\"",[420,550,443],{"class":426},[420,552,554,557,559,562],{"class":422,"line":553},7,[420,555,556],{"class":433},"  \"iterators\"",[420,558,23],{"class":426},[420,560,561],{"class":433},"66",[420,563,443],{"class":426},[420,565,567,570,572],{"class":422,"line":566},8,[420,568,569],{"class":433},"  \"ticket\"",[420,571,23],{"class":426},[420,573,574],{"class":439},"\"UzEyMDI1MTEx...\"\n",[420,576,578],{"class":422,"line":577},9,[420,579,460],{"class":426},[17,581,582,583,593],{},"⚠️ ",[20,584,585,588,589,592],{},[219,586,587],{},"ticket",", ",[219,590,591],{},"iterators","는 복호화 단계에서 다시 사용"," — 세션 상태에 보관.",[389,595,597,598],{"id":596},"_52-인증-url-요청-post-idointcv10authurl","5.2 인증 URL 요청 — ",[219,599,600],{},"POST \u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Furl",[17,602,603],{},[20,604,400],{},[291,606,609],{"className":607,"code":608,"language":296},[294],"Authorization: Bearer {access_token}\nContent-Type: application\u002Fjson\n",[219,610,608],{"__ignoreMap":299},[17,612,613],{},[20,614,411],{},[291,616,618],{"className":414,"code":617,"language":416,"meta":299,"style":299},"{\n  \"request_no\": \"A1234567890123456789\",\n  \"return_url\": \"https:\u002F\u002Fmalgn-noti-api.malgnsoft.workers.dev\u002Fauth\u002Fnice\u002Fcallback\",\n  \"close_url\": \"https:\u002F\u002Fmalgn-noti.pages.dev\u002Fsignup?nice=closed\",\n  \"svc_types\": [\"M\"],\n  \"method_type\": \"POST\",\n  \"exp_mods\": [\"closeButtonOn\"]\n}\n",[219,619,620,624,635,647,659,673,685,698],{"__ignoreMap":299},[420,621,622],{"class":422,"line":423},[420,623,427],{"class":426},[420,625,626,628,630,633],{"class":422,"line":430},[420,627,449],{"class":433},[420,629,23],{"class":426},[420,631,632],{"class":439},"\"A1234567890123456789\"",[420,634,443],{"class":426},[420,636,637,640,642,645],{"class":422,"line":446},[420,638,639],{"class":433},"  \"return_url\"",[420,641,23],{"class":426},[420,643,644],{"class":439},"\"https:\u002F\u002Fmalgn-noti-api.malgnsoft.workers.dev\u002Fauth\u002Fnice\u002Fcallback\"",[420,646,443],{"class":426},[420,648,649,652,654,657],{"class":422,"line":457},[420,650,651],{"class":433},"  \"close_url\"",[420,653,23],{"class":426},[420,655,656],{"class":439},"\"https:\u002F\u002Fmalgn-noti.pages.dev\u002Fsignup?nice=closed\"",[420,658,443],{"class":426},[420,660,661,664,667,670],{"class":422,"line":527},[420,662,663],{"class":433},"  \"svc_types\"",[420,665,666],{"class":426},": [",[420,668,669],{"class":439},"\"M\"",[420,671,672],{"class":426},"],\n",[420,674,675,678,680,683],{"class":422,"line":540},[420,676,677],{"class":433},"  \"method_type\"",[420,679,23],{"class":426},[420,681,682],{"class":439},"\"POST\"",[420,684,443],{"class":426},[420,686,687,690,692,695],{"class":422,"line":553},[420,688,689],{"class":433},"  \"exp_mods\"",[420,691,666],{"class":426},[420,693,694],{"class":439},"\"closeButtonOn\"",[420,696,697],{"class":426},"]\n",[420,699,700],{"class":422,"line":566},[420,701,460],{"class":426},[462,703,704,714,720,727,743],{},[465,705,706,709,710,713],{},[219,707,708],{},"return_url",": NICE가 인증 완료 후 호출할 우리 콜백. ",[20,711,712],{},"HTTPS 필수",".",[465,715,716,719],{},[219,717,718],{},"close_url",": 표준창에서 사용자가 X 닫으면 이동할 URL.",[465,721,722,724,725,713],{},[219,723,274],{},": 노출할 인증 수단. 1단계는 ",[219,726,278],{},[465,728,729,732,733,736,737,739,740,742],{},[219,730,731],{},"method_type",": 콜백을 ",[219,734,735],{},"GET"," 또는 ",[219,738,340],{},"로. ",[219,741,340],{}," 권장(URL에 토큰 노출 방지).",[465,744,745,748,749,752],{},[219,746,747],{},"exp_mods",": 옵션. ",[219,750,751],{},"closeButtonOn"," 등 표준창 UI 옵션.",[17,754,755],{},[20,756,479],{},[291,758,760],{"className":414,"code":759,"language":416,"meta":299,"style":299},"{\n  \"result_code\": \"0000\",\n  \"auth_url\": \"https:\u002F\u002Fauth.niceid.co.kr\u002Fido\u002Fcert\u002Frequest\u002FS1afc40674-b094-44e7-be4b-3e42011b5f81\",\n  \"transaction_id\": \"UzE0MUQyNkFDOEQ3NzYyMDIwMjUxMTEzMTAwMjM3MzM4OTQ5QUMwMkU\"\n}\n",[219,761,762,766,776,788,798],{"__ignoreMap":299},[420,763,764],{"class":422,"line":423},[420,765,427],{"class":426},[420,767,768,770,772,774],{"class":422,"line":430},[420,769,493],{"class":433},[420,771,23],{"class":426},[420,773,498],{"class":439},[420,775,443],{"class":426},[420,777,778,781,783,786],{"class":422,"line":446},[420,779,780],{"class":433},"  \"auth_url\"",[420,782,23],{"class":426},[420,784,785],{"class":439},"\"https:\u002F\u002Fauth.niceid.co.kr\u002Fido\u002Fcert\u002Frequest\u002FS1afc40674-b094-44e7-be4b-3e42011b5f81\"",[420,787,443],{"class":426},[420,789,790,793,795],{"class":422,"line":457},[420,791,792],{"class":433},"  \"transaction_id\"",[420,794,23],{"class":426},[420,796,797],{"class":439},"\"UzE0MUQyNkFDOEQ3NzYyMDIwMjUxMTEzMTAwMjM3MzM4OTQ5QUMwMkU\"\n",[420,799,800],{"class":422,"line":527},[420,801,460],{"class":426},[17,803,582,804,593],{},[20,805,806,809],{},[219,807,808],{},"transaction_id","도 복호화 단계에서 salt로 사용",[389,811,813],{"id":812},"_53-표준창-팝업-프런트","5.3 표준창 팝업 (프런트)",[291,815,819],{"className":816,"code":817,"language":818,"meta":299,"style":299},"language-ts shiki shiki-themes github-light github-dark","window.open(\n  auth_url,\n  'niceAuth',\n  'width=480,height=812,top=100,scrollbars=no'\n)\n","ts",[219,820,821,833,838,845,850],{"__ignoreMap":299},[420,822,823,826,830],{"class":422,"line":423},[420,824,825],{"class":426},"window.",[420,827,829],{"class":828},"sScJk","open",[420,831,832],{"class":426},"(\n",[420,834,835],{"class":422,"line":430},[420,836,837],{"class":426},"  auth_url,\n",[420,839,840,843],{"class":422,"line":446},[420,841,842],{"class":439},"  'niceAuth'",[420,844,443],{"class":426},[420,846,847],{"class":422,"line":457},[420,848,849],{"class":439},"  'width=480,height=812,top=100,scrollbars=no'\n",[420,851,852],{"class":422,"line":527},[420,853,854],{"class":426},")\n",[462,856,857,863],{},[465,858,859,860,713],{},"권장 크기 ",[20,861,862],{},"480 × 812px",[465,864,865,866,869],{},"모바일 환경에서는 ",[219,867,868],{},"target=\"_blank\""," 또는 same-tab 이동도 가능.",[389,871,873,874,876],{"id":872},"_54-콜백-return_url-수신","5.4 콜백 — ",[219,875,708],{}," 수신",[17,878,879,882,883,885],{},[20,880,881],{},"Request from NICE"," (위 ",[219,884,731],{},"에 따라)",[291,887,890],{"className":888,"code":889,"language":296},[294],"POST \u002Fauth\u002Fnice\u002Fcallback\nContent-Type: application\u002Fx-www-form-urlencoded\n\nweb_transaction_id=ZGIxOGZkYjUtMjE4NC00MDZmLTkxZjgtM2ZhNjA0OTdiZTY2\n",[219,891,889],{"__ignoreMap":299},[17,893,894,895,898,899,902],{},"우리 콜백 핸들러는 ",[219,896,897],{},"web_transaction_id","만 받음. ",[20,900,901],{},"이 값으로 결과를 따로 조회","(다음 단계).",[389,904,906,907],{"id":905},"_55-인증-결과-요청-post-idointcv10authresult","5.5 인증 결과 요청 — ",[219,908,909],{},"POST \u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Fresult",[17,911,912],{},[20,913,400],{},[291,915,917],{"className":916,"code":608,"language":296},[294],[219,918,608],{"__ignoreMap":299},[17,920,921],{},[20,922,411],{},[291,924,926],{"className":414,"code":925,"language":416,"meta":299,"style":299},"{\n  \"web_transaction_id\": \"ZGIxOGZkYjUtMjE4NC00MDZmLTkxZjgtM2ZhNjA0OTdiZTY2\",\n  \"transaction_id\":     \"UzE0MUQyNkFDOEQ3NzYyMDIwMjUxMTEzMTAwMjM3MzM4OTQ5QUMwMkU\",\n  \"request_no\":         \"A1234567890123456789\"\n}\n",[219,927,928,932,944,956,965],{"__ignoreMap":299},[420,929,930],{"class":422,"line":423},[420,931,427],{"class":426},[420,933,934,937,939,942],{"class":422,"line":430},[420,935,936],{"class":433},"  \"web_transaction_id\"",[420,938,23],{"class":426},[420,940,941],{"class":439},"\"ZGIxOGZkYjUtMjE4NC00MDZmLTkxZjgtM2ZhNjA0OTdiZTY2\"",[420,943,443],{"class":426},[420,945,946,948,951,954],{"class":422,"line":446},[420,947,792],{"class":433},[420,949,950],{"class":426},":     ",[420,952,953],{"class":439},"\"UzE0MUQyNkFDOEQ3NzYyMDIwMjUxMTEzMTAwMjM3MzM4OTQ5QUMwMkU\"",[420,955,443],{"class":426},[420,957,958,960,963],{"class":422,"line":457},[420,959,449],{"class":433},[420,961,962],{"class":426},":         ",[420,964,454],{"class":439},[420,966,967],{"class":422,"line":527},[420,968,460],{"class":426},[17,970,971],{},[20,972,973],{},"Response (암호화됨)",[291,975,977],{"className":414,"code":976,"language":416,"meta":299,"style":299},"{\n  \"result_code\": \"0000\",\n  \"enc_data\":    \"{Base64UrlEncoded(AES-256-GCM)}\",\n  \"integrity_value\": \"neSKi1jaqTBd_uly-1-FYS-A7euPHPajR0HCODnC3HA\"\n}\n",[219,978,979,983,993,1006,1016],{"__ignoreMap":299},[420,980,981],{"class":422,"line":423},[420,982,427],{"class":426},[420,984,985,987,989,991],{"class":422,"line":430},[420,986,493],{"class":433},[420,988,23],{"class":426},[420,990,498],{"class":439},[420,992,443],{"class":426},[420,994,995,998,1001,1004],{"class":422,"line":446},[420,996,997],{"class":433},"  \"enc_data\"",[420,999,1000],{"class":426},":    ",[420,1002,1003],{"class":439},"\"{Base64UrlEncoded(AES-256-GCM)}\"",[420,1005,443],{"class":426},[420,1007,1008,1011,1013],{"class":422,"line":457},[420,1009,1010],{"class":433},"  \"integrity_value\"",[420,1012,23],{"class":426},[420,1014,1015],{"class":439},"\"neSKi1jaqTBd_uly-1-FYS-A7euPHPajR0HCODnC3HA\"\n",[420,1017,1018],{"class":422,"line":527},[420,1019,460],{"class":426},[69,1021],{},[72,1023,1025],{"id":1024},"_6-암복호화-aes-256-gcm-pbkdf2","6. 암복호화 — AES-256-GCM + PBKDF2",[389,1027,1029],{"id":1028},"키-유도-pbkdf2-hmac-sha256","키 유도 (PBKDF2-HMAC-SHA256)",[291,1031,1034],{"className":1032,"code":1033,"language":296},[294],"input  = ticket (5.1 응답)\nsalt   = transaction_id (5.2 응답)\niter   = iterators (5.1 응답)\noutput = 64 bytes\n\n대칭키   = output[0..32]      \u002F\u002F 32 bytes\n무결성키 = output[48..80]     \u002F\u002F 32 bytes (48번째 byte부터)\n",[219,1035,1033],{"__ignoreMap":299},[389,1037,1038],{"id":1038},"복호화",[291,1040,1043],{"className":1041,"code":1042,"language":296},[294],"cipher_bytes  = Base64UrlDecode(enc_data)\niv            = cipher_bytes[0..16]                      \u002F\u002F 16 bytes\ncipher_tag    = cipher_bytes[16..]                       \u002F\u002F 나머지\ntag           = cipher_tag[-16..]                        \u002F\u002F 끝 16 bytes\ncipher_text   = cipher_tag[..-16]                        \u002F\u002F 나머지\nplaintext     = AES-256-GCM-Decrypt(cipher_text, key=대칭키, iv=iv, tag=tag)\n",[219,1044,1042],{"__ignoreMap":299},[389,1046,1048],{"id":1047},"무결성-검증","무결성 검증",[291,1050,1053],{"className":1051,"code":1052,"language":296},[294],"expected = HMAC-SHA256(enc_data, key=무결성키)\nexpected_base64url = Base64UrlEncode(expected).rstrip('=')\nassert expected_base64url == integrity_value  \u002F\u002F 일치하지 않으면 위변조\n",[219,1054,1052],{"__ignoreMap":299},[389,1056,1058],{"id":1057},"workerstypescript-구현-메모","Workers(TypeScript) 구현 메모",[462,1060,1061,1066,1076,1090],{},[465,1062,1063],{},[219,1064,1065],{},"crypto.subtle.importKey('raw', key, {name:'AES-GCM'}, false, ['decrypt'])",[465,1067,1068,1071,1072,1075],{},[219,1069,1070],{},"crypto.subtle.decrypt({name:'AES-GCM', iv, tagLength:128}, key, cipher_tag)"," — Web Crypto의 ",[219,1073,1074],{},"decrypt","는 tag를 cipher_tag 끝에 붙인 형태로 받음 (별도 분리 불필요).",[465,1077,1078,1081,1082,1085,1086,1089],{},[219,1079,1080],{},"crypto.subtle.importKey('raw', key, {name:'HMAC', hash:'SHA-256'}, false, ['verify'])"," + ",[219,1083,1084],{},"crypto.subtle.verify"," 또는 직접 ",[219,1087,1088],{},"crypto.subtle.sign"," 후 비교.",[465,1091,1092,1093,1081,1096,713],{},"PBKDF2: ",[219,1094,1095],{},"crypto.subtle.importKey('raw', ticket, 'PBKDF2', ...)",[219,1097,1098],{},"deriveBits({name:'PBKDF2', salt, iterations, hash:'SHA-256'}, ..., 512)",[17,1100,1101],{},"→ 별도 라이브러리 불필요. Workers 표준 Web Crypto로 모두 처리.",[69,1103],{},[72,1105,1107],{"id":1106},"_7-복호화-후-받는-데이터-휴대폰-본인확인-m-기준","7. 복호화 후 받는 데이터 (휴대폰 본인확인 M 기준)",[77,1109,1110,1126],{},[80,1111,1112],{},[83,1113,1114,1117,1120,1123],{},[86,1115,1116],{},"키",[86,1118,1119],{},"타입",[86,1121,1122],{},"설명",[86,1124,1125],{},"우리 사용",[102,1127,1128,1147,1167,1191,1213,1231,1246,1263],{},[83,1129,1130,1135,1138,1141],{},[107,1131,1132],{},[219,1133,1134],{},"name",[107,1136,1137],{},"string",[107,1139,1140],{},"성명",[107,1142,1143,1146],{},[219,1144,1145],{},"TB_USER.name"," 갱신",[83,1148,1149,1154,1156,1161],{},[107,1150,1151],{},[219,1152,1153],{},"birthdate",[107,1155,1137],{},[107,1157,1158],{},[219,1159,1160],{},"yyyymmdd",[107,1162,1163,1166],{},[219,1164,1165],{},"TB_USER.birthdate"," 신규 (스키마 추가)",[83,1168,1169,1174,1176,1185],{},[107,1170,1171],{},[219,1172,1173],{},"gender",[107,1175,1137],{},[107,1177,1178,1181,1182,1184],{},[219,1179,1180],{},"0","=여, ",[219,1183,337],{},"=남",[107,1186,1187,1188],{},"(선택) ",[219,1189,1190],{},"TB_USER.gender",[83,1192,1193,1198,1200,1208],{},[107,1194,1195],{},[219,1196,1197],{},"national_info",[107,1199,1137],{},[107,1201,1202,1204,1205,1207],{},[219,1203,1180],{},"=내국인, ",[219,1206,337],{},"=외국인",[107,1209,1187,1210],{},[219,1211,1212],{},"TB_USER.national_info",[83,1214,1215,1220,1222,1225],{},[107,1216,1217],{},[219,1218,1219],{},"ci",[107,1221,1137],{},[107,1223,1224],{},"연계정보 (사이트 간 동일인 식별)",[107,1226,1227,1230],{},[219,1228,1229],{},"TB_USER.ci"," 신규 + UNIQUE — 중복 가입 방지",[83,1232,1233,1238,1240,1243],{},[107,1234,1235],{},[219,1236,1237],{},"di",[107,1239,1137],{},[107,1241,1242],{},"중복가입 확인정보 (사이트 내 동일인 식별)",[107,1244,1245],{},"(선택) 보조",[83,1247,1248,1253,1255,1258],{},[107,1249,1250],{},[219,1251,1252],{},"mobile_co",[107,1254,1137],{},[107,1256,1257],{},"통신사 코드",[107,1259,1260],{},[219,1261,1262],{},"TB_USER.mobile_co",[83,1264,1265,1270,1272,1275],{},[107,1266,1267],{},[219,1268,1269],{},"mobile_no",[107,1271,1137],{},[107,1273,1274],{},"인증된 휴대폰 번호",[107,1276,1277,1280],{},[219,1278,1279],{},"TB_USER.phone"," 갱신 (기존 입력값 덮어쓰기)",[389,1282,1284],{"id":1283},"ci연계정보-운영-원칙","CI(연계정보) 운영 원칙",[462,1286,1287,1293,1303],{},[465,1288,1289,1292],{},[20,1290,1291],{},"CI = 한 사람당 고유 88byte 문자열",". 사이트 간(여러 서비스) 동일인 식별의 표준.",[465,1294,1295,1296,1299,1300,1302],{},"동일 CI로 이미 가입된 사용자가 있으면 ",[20,1297,1298],{},"중복 가입 차단",". (signup 시 ",[219,1301,1229],{}," UNIQUE 검사)",[465,1304,1305],{},"DI도 사용 가능하나 사이트 내(우리 서비스 한정) 식별이라 CI가 더 강력.",[69,1307],{},[72,1309,1311],{"id":1310},"_8-우리-적용-계획","8. 우리 적용 계획",[389,1313,1315,1316,1319],{"id":1314},"_81-백엔드-malgn-noti-api-신규-라우트-3종","8.1 백엔드 (",[219,1317,1318],{},"malgn-noti-api",") — 신규 라우트 3종",[77,1321,1322,1332],{},[80,1323,1324],{},[83,1325,1326,1329],{},[86,1327,1328],{},"라우트",[86,1330,1331],{},"역할",[102,1333,1334,1348,1362],{},[83,1335,1336,1341],{},[107,1337,1338],{},[219,1339,1340],{},"POST \u002Fauth\u002Fnice\u002Finit",[107,1342,1343,1344,1347],{},"5.1 + 5.2 통합 → 프런트에 ",[219,1345,1346],{},"auth_url"," 반환 + session state 발급",[83,1349,1350,1355],{},[107,1351,1352],{},[219,1353,1354],{},"POST \u002Fauth\u002Fnice\u002Fcallback",[107,1356,1357,1358,1361],{},"NICE의 5.4 콜백 수신 → 5.5 호출 → 복호화 → ",[219,1359,1360],{},"TB_NICE_AUTH"," 적재 → 프런트로 redirect (성공\u002F실패)",[83,1363,1364,1369],{},[107,1365,1366],{},[219,1367,1368],{},"GET \u002Fauth\u002Fnice\u002Fstatus?session=…",[107,1370,1371],{},"프런트가 폴링으로 인증 완료 여부 확인 → 완료면 검증 결과(이름·생년월일·CI·DI·휴대폰)를 한 번에 반환",[389,1373,1375,1376],{"id":1374},"_82-신규-db-테이블-tb_nice_auth","8.2 신규 DB 테이블 — ",[219,1377,1360],{},[291,1379,1383],{"className":1380,"code":1381,"language":1382,"meta":299,"style":299},"language-sql shiki shiki-themes github-light github-dark","CREATE TABLE TB_NICE_AUTH (\n  id              BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,\n  request_no      VARCHAR(50)  NOT NULL,         -- 우리 요청 고유번호\n  transaction_id  VARCHAR(120) NOT NULL,         -- NICE 응답\n  ticket          VARCHAR(255) NOT NULL,         -- 복호화용\n  iterators       INT          NOT NULL,         -- 복호화용\n  state           VARCHAR(20)  NOT NULL DEFAULT 'pending',\n                  -- pending → completed \u002F failed \u002F expired\n  name            VARCHAR(60),\n  birthdate       VARCHAR(8),\n  gender          CHAR(1),\n  national_info   CHAR(1),\n  ci              VARCHAR(255),                  -- 88byte지만 여유\n  di              VARCHAR(255),\n  mobile_co       VARCHAR(10),\n  mobile_no       VARCHAR(20),\n  expires_at      DATETIME NOT NULL,             -- access_token 만료\n  created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  completed_at    DATETIME,\n  UNIQUE KEY uq_nice_request (request_no),\n  KEY idx_nice_state (state, created_at)\n);\n","sql",[219,1384,1385,1390,1395,1400,1405,1410,1415,1420,1425,1430,1436,1442,1448,1454,1460,1466,1472,1478,1484,1490,1496,1502],{"__ignoreMap":299},[420,1386,1387],{"class":422,"line":423},[420,1388,1389],{},"CREATE TABLE TB_NICE_AUTH (\n",[420,1391,1392],{"class":422,"line":430},[420,1393,1394],{},"  id              BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,\n",[420,1396,1397],{"class":422,"line":446},[420,1398,1399],{},"  request_no      VARCHAR(50)  NOT NULL,         -- 우리 요청 고유번호\n",[420,1401,1402],{"class":422,"line":457},[420,1403,1404],{},"  transaction_id  VARCHAR(120) NOT NULL,         -- NICE 응답\n",[420,1406,1407],{"class":422,"line":527},[420,1408,1409],{},"  ticket          VARCHAR(255) NOT NULL,         -- 복호화용\n",[420,1411,1412],{"class":422,"line":540},[420,1413,1414],{},"  iterators       INT          NOT NULL,         -- 복호화용\n",[420,1416,1417],{"class":422,"line":553},[420,1418,1419],{},"  state           VARCHAR(20)  NOT NULL DEFAULT 'pending',\n",[420,1421,1422],{"class":422,"line":566},[420,1423,1424],{},"                  -- pending → completed \u002F failed \u002F expired\n",[420,1426,1427],{"class":422,"line":577},[420,1428,1429],{},"  name            VARCHAR(60),\n",[420,1431,1433],{"class":422,"line":1432},10,[420,1434,1435],{},"  birthdate       VARCHAR(8),\n",[420,1437,1439],{"class":422,"line":1438},11,[420,1440,1441],{},"  gender          CHAR(1),\n",[420,1443,1445],{"class":422,"line":1444},12,[420,1446,1447],{},"  national_info   CHAR(1),\n",[420,1449,1451],{"class":422,"line":1450},13,[420,1452,1453],{},"  ci              VARCHAR(255),                  -- 88byte지만 여유\n",[420,1455,1457],{"class":422,"line":1456},14,[420,1458,1459],{},"  di              VARCHAR(255),\n",[420,1461,1463],{"class":422,"line":1462},15,[420,1464,1465],{},"  mobile_co       VARCHAR(10),\n",[420,1467,1469],{"class":422,"line":1468},16,[420,1470,1471],{},"  mobile_no       VARCHAR(20),\n",[420,1473,1475],{"class":422,"line":1474},17,[420,1476,1477],{},"  expires_at      DATETIME NOT NULL,             -- access_token 만료\n",[420,1479,1481],{"class":422,"line":1480},18,[420,1482,1483],{},"  created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n",[420,1485,1487],{"class":422,"line":1486},19,[420,1488,1489],{},"  completed_at    DATETIME,\n",[420,1491,1493],{"class":422,"line":1492},20,[420,1494,1495],{},"  UNIQUE KEY uq_nice_request (request_no),\n",[420,1497,1499],{"class":422,"line":1498},21,[420,1500,1501],{},"  KEY idx_nice_state (state, created_at)\n",[420,1503,1505],{"class":422,"line":1504},22,[420,1506,1507],{},");\n",[389,1509,1511,1512,1515],{"id":1510},"_83-tb_user-스키마-확장","8.3 ",[219,1513,1514],{},"TB_USER"," 스키마 확장",[291,1517,1519],{"className":1380,"code":1518,"language":1382,"meta":299,"style":299},"ALTER TABLE TB_USER\n  ADD COLUMN birthdate  VARCHAR(8),\n  ADD COLUMN gender     CHAR(1),\n  ADD COLUMN ci         VARCHAR(255),\n  ADD COLUMN mobile_co  VARCHAR(10),\n  ADD UNIQUE KEY uq_user_ci (ci);  -- 중복 가입 차단\n",[219,1520,1521,1526,1531,1536,1541,1546],{"__ignoreMap":299},[420,1522,1523],{"class":422,"line":423},[420,1524,1525],{},"ALTER TABLE TB_USER\n",[420,1527,1528],{"class":422,"line":430},[420,1529,1530],{},"  ADD COLUMN birthdate  VARCHAR(8),\n",[420,1532,1533],{"class":422,"line":446},[420,1534,1535],{},"  ADD COLUMN gender     CHAR(1),\n",[420,1537,1538],{"class":422,"line":457},[420,1539,1540],{},"  ADD COLUMN ci         VARCHAR(255),\n",[420,1542,1543],{"class":422,"line":527},[420,1544,1545],{},"  ADD COLUMN mobile_co  VARCHAR(10),\n",[420,1547,1548],{"class":422,"line":540},[420,1549,1550],{},"  ADD UNIQUE KEY uq_user_ci (ci);  -- 중복 가입 차단\n",[389,1552,1554],{"id":1553},"_84-wrangler-secrets","8.4 wrangler secrets",[291,1556,1559],{"className":1557,"code":1558,"language":296},[294],"NICE_CLIENT_ID\nNICE_CLIENT_SECRET\nNICE_RETURN_URL       (예: https:\u002F\u002Fmalgn-noti-api.malgnsoft.workers.dev\u002Fauth\u002Fnice\u002Fcallback)\nNICE_CLOSE_URL        (예: https:\u002F\u002Fmalgn-noti.pages.dev\u002Fsignup?nice=closed)\n",[219,1560,1558],{"__ignoreMap":299},[389,1562,1564,1565,1568],{"id":1563},"_85-프런트-signupvue-step-4-교체","8.5 프런트 — ",[219,1566,1567],{},"signup.vue"," Step 4 교체",[17,1570,1571,1572,1575],{},"기존 흐름(통신사 select + 이름 + 주민번호 + 휴대폰 3분할 입력 + 자체 OTP 발송) → 단일 ",[20,1573,1574],{},"\"본인 인증하기\""," 버튼:",[291,1577,1579],{"className":816,"code":1578,"language":818,"meta":299,"style":299},"async function startNiceAuth() {\n  const { authUrl, session } = await useApi()('\u002Fauth\u002Fnice\u002Finit', { method: 'POST' })\n  niceSession.value = session\n  window.open(authUrl, 'niceAuth', 'width=480,height=812,top=100')\n  \u002F\u002F 폴링 시작\n  pollUntilDone()\n}\n\nasync function pollUntilDone() {\n  for (let i = 0; i \u003C 60; i++) {  \u002F\u002F 5분(5초 × 60)\n    await sleep(5000)\n    const res = await useApi()('\u002Fauth\u002Fnice\u002Fstatus', {\n      params: { session: niceSession.value },\n    })\n    if (res.data.state === 'completed') {\n      niceResult.value = res.data  \u002F\u002F {name, birthdate, ci, mobile_no, ...}\n      verified.value = true\n      return\n    }\n    if (res.data.state === 'failed' || res.data.state === 'expired') {\n      \u002F\u002F 토스트 + 다시 시도 UI\n      return\n    }\n  }\n}\n",[219,1580,1581,1596,1639,1649,1669,1675,1683,1687,1693,1704,1744,1760,1783,1788,1793,1810,1823,1833,1838,1843,1867,1872,1876,1881,1887],{"__ignoreMap":299},[420,1582,1583,1587,1590,1593],{"class":422,"line":423},[420,1584,1586],{"class":1585},"szBVR","async",[420,1588,1589],{"class":1585}," function",[420,1591,1592],{"class":828}," startNiceAuth",[420,1594,1595],{"class":426},"() {\n",[420,1597,1598,1601,1604,1607,1609,1612,1615,1618,1621,1624,1627,1630,1633,1636],{"class":422,"line":430},[420,1599,1600],{"class":1585},"  const",[420,1602,1603],{"class":426}," { ",[420,1605,1606],{"class":433},"authUrl",[420,1608,588],{"class":426},[420,1610,1611],{"class":433},"session",[420,1613,1614],{"class":426}," } ",[420,1616,1617],{"class":1585},"=",[420,1619,1620],{"class":1585}," await",[420,1622,1623],{"class":828}," useApi",[420,1625,1626],{"class":426},"()(",[420,1628,1629],{"class":439},"'\u002Fauth\u002Fnice\u002Finit'",[420,1631,1632],{"class":426},", { method: ",[420,1634,1635],{"class":439},"'POST'",[420,1637,1638],{"class":426}," })\n",[420,1640,1641,1644,1646],{"class":422,"line":446},[420,1642,1643],{"class":426},"  niceSession.value ",[420,1645,1617],{"class":1585},[420,1647,1648],{"class":426}," session\n",[420,1650,1651,1654,1656,1659,1662,1664,1667],{"class":422,"line":457},[420,1652,1653],{"class":426},"  window.",[420,1655,829],{"class":828},[420,1657,1658],{"class":426},"(authUrl, ",[420,1660,1661],{"class":439},"'niceAuth'",[420,1663,588],{"class":426},[420,1665,1666],{"class":439},"'width=480,height=812,top=100'",[420,1668,854],{"class":426},[420,1670,1671],{"class":422,"line":527},[420,1672,1674],{"class":1673},"sJ8bj","  \u002F\u002F 폴링 시작\n",[420,1676,1677,1680],{"class":422,"line":540},[420,1678,1679],{"class":828},"  pollUntilDone",[420,1681,1682],{"class":426},"()\n",[420,1684,1685],{"class":422,"line":553},[420,1686,460],{"class":426},[420,1688,1689],{"class":422,"line":566},[420,1690,1692],{"emptyLinePlaceholder":1691},true,"\n",[420,1694,1695,1697,1699,1702],{"class":422,"line":577},[420,1696,1586],{"class":1585},[420,1698,1589],{"class":1585},[420,1700,1701],{"class":828}," pollUntilDone",[420,1703,1595],{"class":426},[420,1705,1706,1709,1712,1715,1718,1720,1723,1726,1729,1732,1735,1738,1741],{"class":422,"line":1432},[420,1707,1708],{"class":1585},"  for",[420,1710,1711],{"class":426}," (",[420,1713,1714],{"class":1585},"let",[420,1716,1717],{"class":426}," i ",[420,1719,1617],{"class":1585},[420,1721,1722],{"class":433}," 0",[420,1724,1725],{"class":426},"; i ",[420,1727,1728],{"class":1585},"\u003C",[420,1730,1731],{"class":433}," 60",[420,1733,1734],{"class":426},"; i",[420,1736,1737],{"class":1585},"++",[420,1739,1740],{"class":426},") {  ",[420,1742,1743],{"class":1673},"\u002F\u002F 5분(5초 × 60)\n",[420,1745,1746,1749,1752,1755,1758],{"class":422,"line":1438},[420,1747,1748],{"class":1585},"    await",[420,1750,1751],{"class":828}," sleep",[420,1753,1754],{"class":426},"(",[420,1756,1757],{"class":433},"5000",[420,1759,854],{"class":426},[420,1761,1762,1765,1768,1771,1773,1775,1777,1780],{"class":422,"line":1444},[420,1763,1764],{"class":1585},"    const",[420,1766,1767],{"class":433}," res",[420,1769,1770],{"class":1585}," =",[420,1772,1620],{"class":1585},[420,1774,1623],{"class":828},[420,1776,1626],{"class":426},[420,1778,1779],{"class":439},"'\u002Fauth\u002Fnice\u002Fstatus'",[420,1781,1782],{"class":426},", {\n",[420,1784,1785],{"class":422,"line":1450},[420,1786,1787],{"class":426},"      params: { session: niceSession.value },\n",[420,1789,1790],{"class":422,"line":1456},[420,1791,1792],{"class":426},"    })\n",[420,1794,1795,1798,1801,1804,1807],{"class":422,"line":1462},[420,1796,1797],{"class":1585},"    if",[420,1799,1800],{"class":426}," (res.data.state ",[420,1802,1803],{"class":1585},"===",[420,1805,1806],{"class":439}," 'completed'",[420,1808,1809],{"class":426},") {\n",[420,1811,1812,1815,1817,1820],{"class":422,"line":1468},[420,1813,1814],{"class":426},"      niceResult.value ",[420,1816,1617],{"class":1585},[420,1818,1819],{"class":426}," res.data  ",[420,1821,1822],{"class":1673},"\u002F\u002F {name, birthdate, ci, mobile_no, ...}\n",[420,1824,1825,1828,1830],{"class":422,"line":1474},[420,1826,1827],{"class":426},"      verified.value ",[420,1829,1617],{"class":1585},[420,1831,1832],{"class":433}," true\n",[420,1834,1835],{"class":422,"line":1480},[420,1836,1837],{"class":1585},"      return\n",[420,1839,1840],{"class":422,"line":1486},[420,1841,1842],{"class":426},"    }\n",[420,1844,1845,1847,1849,1851,1854,1857,1860,1862,1865],{"class":422,"line":1492},[420,1846,1797],{"class":1585},[420,1848,1800],{"class":426},[420,1850,1803],{"class":1585},[420,1852,1853],{"class":439}," 'failed'",[420,1855,1856],{"class":1585}," ||",[420,1858,1859],{"class":426}," res.data.state ",[420,1861,1803],{"class":1585},[420,1863,1864],{"class":439}," 'expired'",[420,1866,1809],{"class":426},[420,1868,1869],{"class":422,"line":1498},[420,1870,1871],{"class":1673},"      \u002F\u002F 토스트 + 다시 시도 UI\n",[420,1873,1874],{"class":422,"line":1504},[420,1875,1837],{"class":1585},[420,1877,1879],{"class":422,"line":1878},23,[420,1880,1842],{"class":426},[420,1882,1884],{"class":422,"line":1883},24,[420,1885,1886],{"class":426},"  }\n",[420,1888,1890],{"class":422,"line":1889},25,[420,1891,460],{"class":426},[389,1893,1895,1896,1899],{"id":1894},"_86-authsignup-라우트-확장","8.6 ",[219,1897,1898],{},"\u002Fauth\u002Fsignup"," 라우트 확장",[17,1901,1902],{},"NICE 결과를 받아 검증 후 signup:",[291,1904,1906],{"className":816,"code":1905,"language":818,"meta":299,"style":299},"const signupB = z.object({\n  \u002F\u002F 기존 필드 +\n  niceSession: z.string(),  \u002F\u002F \u002Fauth\u002Fnice\u002Fstatus가 반환한 session 또는 request_no\n})\n\n\u002F\u002F signup 처리 안:\n\u002F\u002F 1. TB_NICE_AUTH에서 state='completed' + 같은 session 조회\n\u002F\u002F 2. ci로 TB_USER 중복 검사 → 있으면 409 \"이미 가입된 사용자\"\n\u002F\u002F 3. signup 진행 + TB_USER.ci\u002Fbirthdate\u002Fgender\u002Fmobile_co 적재\n\u002F\u002F 4. consumed 표시 (한 번 쓰면 다시 못 씀)\n",[219,1907,1908,1927,1932,1945,1950,1954,1959,1964,1969,1974],{"__ignoreMap":299},[420,1909,1910,1913,1916,1918,1921,1924],{"class":422,"line":423},[420,1911,1912],{"class":1585},"const",[420,1914,1915],{"class":433}," signupB",[420,1917,1770],{"class":1585},[420,1919,1920],{"class":426}," z.",[420,1922,1923],{"class":828},"object",[420,1925,1926],{"class":426},"({\n",[420,1928,1929],{"class":422,"line":430},[420,1930,1931],{"class":1673},"  \u002F\u002F 기존 필드 +\n",[420,1933,1934,1937,1939,1942],{"class":422,"line":446},[420,1935,1936],{"class":426},"  niceSession: z.",[420,1938,1137],{"class":828},[420,1940,1941],{"class":426},"(),  ",[420,1943,1944],{"class":1673},"\u002F\u002F \u002Fauth\u002Fnice\u002Fstatus가 반환한 session 또는 request_no\n",[420,1946,1947],{"class":422,"line":457},[420,1948,1949],{"class":426},"})\n",[420,1951,1952],{"class":422,"line":527},[420,1953,1692],{"emptyLinePlaceholder":1691},[420,1955,1956],{"class":422,"line":540},[420,1957,1958],{"class":1673},"\u002F\u002F signup 처리 안:\n",[420,1960,1961],{"class":422,"line":553},[420,1962,1963],{"class":1673},"\u002F\u002F 1. TB_NICE_AUTH에서 state='completed' + 같은 session 조회\n",[420,1965,1966],{"class":422,"line":566},[420,1967,1968],{"class":1673},"\u002F\u002F 2. ci로 TB_USER 중복 검사 → 있으면 409 \"이미 가입된 사용자\"\n",[420,1970,1971],{"class":422,"line":577},[420,1972,1973],{"class":1673},"\u002F\u002F 3. signup 진행 + TB_USER.ci\u002Fbirthdate\u002Fgender\u002Fmobile_co 적재\n",[420,1975,1976],{"class":422,"line":1432},[420,1977,1978],{"class":1673},"\u002F\u002F 4. consumed 표시 (한 번 쓰면 다시 못 씀)\n",[69,1980],{},[72,1982,1984],{"id":1983},"_9-인프라-고려사항-중요","9. 인프라 고려사항 (중요)",[389,1986,1988,1989],{"id":1987},"_91-outbound-ip-화이트리스트-workers-환경-이슈-검증됨-62-16","9.1 Outbound IP 화이트리스트 — Workers 환경 이슈 → ",[20,1990,1991],{},"검증됨 (6\u002F2 §16)",[17,1993,1994,1995,1998],{},"NICE는 ",[20,1996,1997],{},"이용기관 서버의 Outbound IP를 사전 등록","해야 API 호출이 가능합니다 (가이드 §13).",[17,2000,2001,2004],{},[20,2002,2003],{},"문제",": Cloudflare Workers는 동적 데이터센터 IP를 사용 → 고정 IP 없음.",[17,2006,2007,2010,2011,2013,2014,2017,2018,2021,2022,2025],{},[20,2008,2009],{},"라이브 시도 결과 (2026-06-02)",": 자격증명을 정상 등록한 상태에서 ",[219,2012,1340],{}," 호출 시 NICE 응답 ",[219,2015,2016],{},"1007 허용되지 않은 IP 접근"," 발생. 사전 예측대로 IP 화이트리스트가 막힘. 즉시 ",[219,2019,2020],{},"NICE_MOCK=1"," 복원해 가입 흐름은 정상 동작 유지. 자격증명 3 secret(CLIENT_ID\u002FCLIENT_SECRET\u002FRETURN_URL)은 해결 시점까지 등록 상태로 보관 — ",[219,2023,2024],{},"wrangler secret delete NICE_MOCK"," 한 번이면 real 전환.",[17,2027,2028,2031],{},[20,2029,2030],{},"선택지"," (6\u002F2 우선순위 재정렬):",[77,2033,2034,2049],{},[80,2035,2036],{},[83,2037,2038,2041,2043,2046],{},[86,2039,2040],{},"안",[86,2042,1122],{},[86,2044,2045],{},"트레이드오프",[86,2047,2048],{},"사용자 의사",[102,2050,2051,2069,2085,2101],{},[83,2052,2053,2058,2061,2064],{},[107,2054,2055],{},[20,2056,2057],{},"A. NICE 콘솔 IP 검사 OFF",[107,2059,2060],{},"콘솔 → API 설정에서 IP 인증 토글 해제",[107,2062,2063],{},"가장 단순. 보안 등급은 다소 낮아짐",[107,2065,2066],{},[20,2067,2068],{},"권장 1순위",[83,2070,2071,2076,2079,2082],{},[107,2072,2073],{},[20,2074,2075],{},"B. NICE 콘솔에 Cloudflare egress IP 대역 등록",[107,2077,2078],{},"Cloudflare 공식 IP 목록을 NICE 영업담당에 송부 후 콘솔 반영",[107,2080,2081],{},"NICE 정책상 거절 가능성",[107,2083,2084],{},"—",[83,2086,2087,2092,2095,2098],{},[107,2088,2089],{},[20,2090,2091],{},"C. 자체 프록시 EC2 + Workers → EC2 → NICE",[107,2093,2094],{},"EC2 고정 IP를 NICE에 등록, Workers는 EC2로 프록시",[107,2096,2097],{},"인프라 추가. 운영 안정성 ↑. 월 ~$4 + 약간의 코드",[107,2099,2100],{},"A·B 실패 시 fallback",[83,2102,2103,2108,2111,2114],{},[107,2104,2105],{},[20,2106,2107],{},"D. Cloudflare Workers Smart Placement + dedicated egress IP",[107,2109,2110],{},"Cloudflare enterprise 등급의 고정 egress IP",[107,2112,2113],{},"비용 큼",[107,2115,2084],{},[17,2117,2118,2121,2122,2125],{},[20,2119,2120],{},"현재 결정 (6\u002F2)",": 사용자 의사로 IP 정책은 ",[20,2123,2124],{},"보류",". mock 모드 유지하면서 다른 작업 진행.",[389,2127,2129],{"id":2128},"_92-nice-방화벽-호스트","9.2 NICE 방화벽 호스트",[17,2131,2132],{},"운영 시점 outbound 허용 호스트:",[291,2134,2137],{"className":2135,"code":2136,"language":296},[294],"auth.niceid.co.kr        (121.162.155.181) : 443  ← 통합인증 (우리 사용)\nnice.checkplus.co.kr     (121.131.196.215) : 443  ← 휴대폰 인증 (자동 fallback)\n",[219,2138,2136],{"__ignoreMap":299},[389,2140,2142],{"id":2141},"_93-보안-위생","9.3 보안 위생",[462,2144,2145,2159,2165,2170],{},[465,2146,2147,2150,2151,2154,2155,2158],{},[219,2148,2149],{},"client_id","\u002F",[219,2152,2153],{},"client_secret","은 ",[20,2156,2157],{},"Workers secret으로만",". 코드\u002F저장소·로그·에러 응답에 절대 노출 금지.",[465,2160,2161,2164],{},[219,2162,2163],{},"access_token","은 짧은 TTL — 캐시 시 expires_in 안에서만.",[465,2166,2167,2169],{},[219,2168,1219],{},"는 PII이지만 그 자체로는 복호화 불가능한 해시 형태 → DB 저장 OK. 단 로그·외부 응답에 직접 노출 금지.",[465,2171,2172,2174],{},[219,2173,469],{},"는 매 세션 새로 생성 + 재사용 차단.",[69,2176],{},[72,2178,2180],{"id":2179},"_10-nice측-계약등록-절차-사용자-작업-필요","10. NICE측 계약·등록 절차 (사용자 작업 필요)",[77,2182,2183,2196],{},[80,2184,2185],{},[83,2186,2187,2190,2193],{},[86,2188,2189],{},"단계",[86,2191,2192],{},"작업자",[86,2194,2195],{},"비고",[102,2197,2198,2215,2226,2237,2254,2265,2275],{},[83,2199,2200,2207,2212],{},[107,2201,2202,2203,2206],{},"1. NICE평가정보 영업 담당 컨택 (",[219,2204,2205],{},"niceid_support@nice.co.kr"," \u002F 02-2122-4872~3)",[107,2208,2209],{},[20,2210,2211],{},"사용자(영업\u002F대표)",[107,2213,2214],{},"통합인증 서비스 가입 요청",[83,2216,2217,2220,2223],{},[107,2218,2219],{},"2. 사이트 등록 + 단가표 확정",[107,2221,2222],{},"사용자",[107,2224,2225],{},"휴대폰 본인확인 건당 단가 협의",[83,2227,2228,2231,2234],{},[107,2229,2230],{},"3. Outbound IP 등록 (위 §9.1)",[107,2232,2233],{},"사용자 + 김도형",[107,2235,2236],{},"Cloudflare 대역 등록 협의 결과 따라",[83,2238,2239,2248,2251],{},[107,2240,2241,2242,2244,2245,2247],{},"4. ",[219,2243,2149],{}," · ",[219,2246,2153],{}," 발급",[107,2249,2250],{},"NICE → 사용자",[107,2252,2253],{},"비공개 보관",[83,2255,2256,2259,2262],{},[107,2257,2258],{},"5. 우리 Workers에 secret 등록 + 코드 배포",[107,2260,2261],{},"김도형",[107,2263,2264],{},"1~2시간 작업",[83,2266,2267,2270,2272],{},[107,2268,2269],{},"6. NICE 테스트 페이지에서 e2e 검증",[107,2271,2261],{},[107,2273,2274],{},"본인 휴대폰으로 1회",[83,2276,2277,2280,2282],{},[107,2278,2279],{},"7. 운영 전환 + signup.vue 교체 배포",[107,2281,2261],{},[107,2283],{},[17,2285,183,2286,713],{},[20,2287,2288],{},"NICE 발급 완료(4단계) 후 5~7은 약 반나절 작업",[69,2290],{},[72,2292,2294],{"id":2293},"_11-알려진-한계-후속-작업","11. 알려진 한계 \u002F 후속 작업",[77,2296,2297,2307],{},[80,2298,2299],{},[83,2300,2301,2303,2305],{},[86,2302,321],{},[86,2304,88],{},[86,2306,2195],{},[102,2308,2309,2325,2337,2349,2366,2379,2392,2407,2424],{},[83,2310,2311,2313,2318],{},[107,2312,337],{},[107,2314,2315],{},[20,2316,2317],{},"외국인 가입",[107,2319,2320,2321,2324],{},"NICE 휴대폰 본인확인은 외국인 등록증 지원. ",[219,2322,2323],{},"national_info='1'","로 분기 가능. UI에 외국인 선택지 추가 필요",[83,2326,2327,2329,2334],{},[107,2328,357],{},[107,2330,2331],{},[20,2332,2333],{},"법인 계정의 대표자 인증",[107,2335,2336],{},"법인사업자 가입 시 대표자 본인 인증으로 대체 가능. 정책 결정 후 적용",[83,2338,2339,2341,2346],{},[107,2340,372],{},[107,2342,2343],{},[20,2344,2345],{},"PASS 앱 직접 연동",[107,2347,2348],{},"NICE 통합인증에선 미지원. PASS 직접 연동은 통신3사 별도 계약 필요 — 후순위",[83,2350,2351,2354,2360],{},[107,2352,2353],{},"4",[107,2355,2356,2359],{},[20,2357,2358],{},"NICE 통합인증 외 수단"," (금융인증서 F, 공동인증서 U)",[107,2361,2362,2363,2365],{},"1단계는 M만, 2단계에서 ",[219,2364,274],{}," 확장",[83,2367,2368,2371,2376],{},[107,2369,2370],{},"5",[107,2372,2373],{},[20,2374,2375],{},"CI 중복 검사 UX",[107,2377,2378],{},"이미 가입된 사용자가 다른 이메일로 재가입 시도 → 409 후 \"이미 가입된 계정이 있습니다. 비밀번호 재설정으로 진행해 주세요\" 안내",[83,2380,2381,2384,2389],{},[107,2382,2383],{},"6",[107,2385,2386],{},[20,2387,2388],{},"자체 SMS OTP 유지 영역",[107,2390,2391],{},"비밀번호 재설정·이메일\u002F휴대폰 변경 등 단순 보유 확인은 자체 OTP 유지 (NICE 비용 절감)",[83,2393,2394,2397,2402],{},[107,2395,2396],{},"7",[107,2398,2399],{},[20,2400,2401],{},"본인 인증 결과 보관 기간",[107,2403,2404,2406],{},[219,2405,1360],{},"는 가입 직후 30~90일 후 PII만 마스킹 처리 검토 (CI는 유지)",[83,2408,2409,2412,2417],{},[107,2410,2411],{},"8",[107,2413,2414],{},[20,2415,2416],{},"모바일웹 UX",[107,2418,2419,2420,2423],{},"표준창이 팝업이라 모바일에서 차단 가능성 — ",[219,2421,2422],{},"redirect"," 모드 검토",[83,2425,2426,2429,2434],{},[107,2427,2428],{},"9",[107,2430,2431],{},[20,2432,2433],{},"테스트 환경 분리",[107,2435,2436],{},"NICE에서 테스트 서버 별도 발급 시 우리도 sandbox\u002Fproduction 환경 분리",[69,2438],{},[72,2440,2442],{"id":2441},"_12-다음-단계-추천","12. 다음 단계 추천",[17,2444,2445,2446,2449],{},"이번주 회원·인증 트랙 P0 항목과 별개로 — NICE 연동은 ",[20,2447,2448],{},"사용자 영업 작업(계약) 선행"," → 그 다음 1일 작업으로 정리 가능.",[462,2451,2452,2455,2458],{},[465,2453,2454],{},"사용자 → NICE 영업 컨택 (이번주~다음주)",[465,2456,2457],{},"김도형 → 계약 완료 후 secret 받으면 반나절 작업으로 백엔드 3 라우트 + 프런트 Step 4 교체 + 라이브 검증",[465,2459,2460],{},"그 사이는 현재 자체 SMS OTP로 가입 흐름 유지 (mock 노출은 NICE 적용 시 영구 제거)",[2462,2463,2464],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":299,"searchDepth":446,"depth":446,"links":2466},[2467,2468,2469,2470,2471,2482,2488,2491,2504,2510,2511,2512],{"id":74,"depth":430,"text":75},{"id":192,"depth":430,"text":193},{"id":288,"depth":430,"text":289},{"id":304,"depth":430,"text":305},{"id":386,"depth":430,"text":387,"children":2472},[2473,2475,2477,2478,2480],{"id":391,"depth":446,"text":2474},"5.1 토큰 발급 — POST \u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Ftoken",{"id":596,"depth":446,"text":2476},"5.2 인증 URL 요청 — POST \u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Furl",{"id":812,"depth":446,"text":813},{"id":872,"depth":446,"text":2479},"5.4 콜백 — return_url 수신",{"id":905,"depth":446,"text":2481},"5.5 인증 결과 요청 — POST \u002Fido\u002Fintc\u002Fv1.0\u002Fauth\u002Fresult",{"id":1024,"depth":430,"text":1025,"children":2483},[2484,2485,2486,2487],{"id":1028,"depth":446,"text":1029},{"id":1038,"depth":446,"text":1038},{"id":1047,"depth":446,"text":1048},{"id":1057,"depth":446,"text":1058},{"id":1106,"depth":430,"text":1107,"children":2489},[2490],{"id":1283,"depth":446,"text":1284},{"id":1310,"depth":430,"text":1311,"children":2492},[2493,2495,2497,2499,2500,2502],{"id":1314,"depth":446,"text":2494},"8.1 백엔드 (malgn-noti-api) — 신규 라우트 3종",{"id":1374,"depth":446,"text":2496},"8.2 신규 DB 테이블 — TB_NICE_AUTH",{"id":1510,"depth":446,"text":2498},"8.3 TB_USER 스키마 확장",{"id":1553,"depth":446,"text":1554},{"id":1563,"depth":446,"text":2501},"8.5 프런트 — signup.vue Step 4 교체",{"id":1894,"depth":446,"text":2503},"8.6 \u002Fauth\u002Fsignup 라우트 확장",{"id":1983,"depth":430,"text":1984,"children":2505},[2506,2508,2509],{"id":1987,"depth":446,"text":2507},"9.1 Outbound IP 화이트리스트 — Workers 환경 이슈 → 검증됨 (6\u002F2 §16)",{"id":2128,"depth":446,"text":2129},{"id":2141,"depth":446,"text":2142},{"id":2179,"depth":430,"text":2180},{"id":2293,"depth":430,"text":2294},{"id":2441,"depth":430,"text":2442},"md",{},"\u002Fnice_auth",{"title":5,"description":299},"NICE_AUTH","ffZJ7zlakF7p5VSIrzGy09nRDA7vTG7dVm-mcpFtjE8",1780638909350]