# 통신3사(SKT·KT·LGU+) 본인인증 연동 요구사항 명세서

> ZKProofID — 데모 모드에서 실전 서비스로 전환하기 위한 개발 가이드
> 작성 목적: 영업 준비 단계에서 실제 통신3사 본인인증(PASS)을 도입할 때
> 필요한 절차·비용·개발 범위를 명확히 하여, 외주 개발팀/대행사 미팅에
> 그대로 활용할 수 있도록 작성함.

---

## 1. 왜 지금은 "데모"인가 — 문제의 본질

현재 `user.html`, `shop.html`, `shop-verify.html`, `index.html`의 연령인증은
**사용자가 화면에서 직접 선택한 생년월일**을 그대로 신뢰합니다.

```
[사용자] 생년월일 선택 (임의 조작 가능)
    ↓
[브라우저 JS] age = 오늘 - 생년월일   ← 클라이언트에서만 계산
    ↓
[결과] "성인입니다" 배지 표시
```

이 구조는 **정적 웹사이트의 근본적 한계**입니다. 클라이언트(브라우저) JS는
사용자가 개발자도구로 언제든 값을 바꿀 수 있기 때문에, "진짜 신뢰할 수 있는
연령 검증"은 **반드시 서버가 통신사로부터 직접 받은 값으로 판정**해야 합니다.

```
[실전 구조 — 반드시 필요]
[사용자] 통신사 앱에서 지문/PIN 인증
    ↓
[통신사 서버] 본인 확인 + 생년월일 보유
    ↓ (암호화된 결과, 대행사 경유)
[우리 백엔드 서버] 서명/무결성 검증 → DB에 생년월일 저장
    ↓
[ZKP 생성] 서버에 저장된 값으로만 계산 (사용자 입력 무시)
    ↓
[프론트엔드] "성인 여부" 결과만 수신 (원본 생년월일 없음)
```

이미 `server/routes/auth.js`, `server/routes/zkp.js`에 이 구조에 맞춘
**뼈대 코드가 준비되어 있습니다.** (`PASS_MODE=simulation` → `real` 전환만 하면 됨)

---

## 2. 왜 정적 사이트(현재 배포 환경)에서는 직접 붙일 수 없는가

| 이유 | 설명 |
|---|---|
| **시크릿 키 노출** | 대행사가 발급하는 `Client Secret`/`API Key`는 서버에만 보관해야 함. 정적 HTML/JS에 넣으면 브라우저 소스에 그대로 노출되어 누구나 도용 가능 |
| **콜백 서명 검증 불가** | 통신사가 인증 결과를 암호화하여 서버로 콜백하는데, 이 결과의 위변조 여부를 검증하려면 서버의 해시 연산이 필요함. 브라우저에서만 처리하면 지금과 동일하게 조작 가능한 상태가 됨 |
| **직접 계약 불가** | SKT/KT/LGU+는 개인·소규모 사업자와 직접 계약하지 않으며, 반드시 **본인인증 대행사**를 통해야 함 |
| **서버 상주 프로세스 필요** | 콜백 수신, DB 저장, JWT 발급 등은 정적 파일 호스팅에서는 실행 불가능한 서버 로직임 |

**결론**: 반드시 `server/` 디렉토리의 Node.js 백엔드를 **실제 서버(VM/컨테이너/PaaS)에
배포**해야 하며, 이 프로젝트(정적 사이트 빌더)는 그 백엔드가 준비된 이후
프론트엔드 연결 작업만 담당할 수 있습니다.

---

## 3. 필요한 사전 준비 (개발 착수 전 필수)

### 3-1. 사업자 요건
- [ ] 사업자등록증
- [ ] 통신판매업 신고증 (온라인 판매/서비스 시)
- [ ] 서비스 이용약관·개인정보처리방침 (대행사 심사 시 요구)

### 3-2. 대행사 선정 및 계약
통신3사와 직접 계약이 불가하므로 아래와 같은 **본인인증 대행사** 중 하나를 선택합니다.

| 대행사 | 특징 | 비고 |
|---|---|---|
| **나이스평가정보 (NICE)** | 국내 최다 채택, PASS 앱 인증·문자 인증 모두 지원 | `server/routes/auth.js`에 이미 NICE API 호출 스텁 구현됨 |
| **다날 (Danal)** | 소액결제 겸용 인증사, 커머스 업계 다수 채택 | |
| **KG모빌리언스** | 통신과금·본인인증 통합 제공 | |
| **KCP** | PG 겸용, 결제와 인증 통합 관리 용이 | |
| **카카오페이 인증** | 카카오 생태계 연동 시 UX 유리 | 통신사 PASS와는 별도 트랙 |

가입 절차: 대행사 홈페이지 → 가맹 신청 → 서류 심사(1~2주 통상) → 계약 →
**가맹점 코드 + API Key/Secret 발급**

### 3-3. 백엔드 서버 인프라
- [ ] Node.js 서버 호스팅 (Railway / AWS EC2 / Rocky Linux VM 등 — `server/README-SERVER.md` 참고)
- [ ] PostgreSQL 16 DB (Supabase/Neon 무료 티어로 시작 가능)
- [ ] HTTPS 인증서 (Let's Encrypt) — 통신사 콜백은 HTTPS 필수
- [ ] 고정 도메인 (콜백 URL 등록용)

---

## 4. 비용 구조 (일반적인 시세 — 실제 견적은 대행사 문의 필요)

> ⚠️ 아래는 업계 일반적인 참고 수치이며, 대행사·계약조건·거래량에 따라 달라집니다.
> 반드시 대행사 영업팀에 정식 견적을 요청하세요.

| 항목 | 참고 비용 | 비고 |
|---|---|---|
| 가맹 심사/등록비 | 무료 ~ 수십만원 | 대행사별 상이, 초기 무료인 곳도 많음 |
| SMS 문자 인증 | 건당 약 30~80원 | 가장 저렴한 방식 |
| PASS 앱 인증 | 건당 약 100~250원 | 생체인증 연동, UX 우수 |
| 월 기본료 | 있음/없음 대행사별 상이 | 트래픽 적으면 종량제 대행사 추천 |
| PG 결제 겸용 옵션 | 결제 수수료 별도 | 인증만 필요하면 불필요 |
| 정산 | 통상 월 단위 후불 | |

**영업 초기 단계 권장 전략**: 월 기본료 없는 **순수 종량제(건당 과금)** 대행사를
선택하여 초기 고정비 부담을 최소화하고, 트래픽이 늘어난 후 정액제 전환을 검토합니다.

---

## 5. 개발 범위 (백엔드 서버 준비 완료 후)

### 5-1. 이미 준비된 것 (`server/` 디렉토리)

| 파일 | 상태 |
|---|---|
| `server/routes/auth.js` | ✅ PASS 요청/콜백/완료 처리 API 뼈대 구현 (`PASS_MODE=simulation`/`real` 스위치) |
| `server/routes/zkp.js` | ✅ 서버 DB에 저장된 생년월일로만 ZKP 생성 (사용자 입력 무시 로직 포함) |
| `server/db/schema.sql` | ✅ `users`, `pass_auth_logs` 등 테이블 스키마 |
| `server/.env.example` | ✅ `NICE_CLIENT_ID`, `NICE_CLIENT_SECRET` 등 환경변수 템플릿 |

### 5-2. 실전 전환 시 추가 개발 필요 항목

1. **NICE(or 선택 대행사) 실제 API 연동 완성**
   - `server/routes/auth.js`의 `pass/request`, `pass/callback` 실제 암복호화 로직 구현
   - 대행사 제공 SDK/API 문서에 따른 `enc_data` 복호화 함수 작성
2. **콜백 서명 검증 로직**
   - 대행사가 보내는 `integrity_value` 등 무결성 값 검증
3. **프론트엔드 연동**
   - `js/age-verify-service.js`의 `CONFIG.MODE`를 `'real'`로 변경
   - `CONFIG.BACKEND_BASE_URL`에 배포된 백엔드 주소 입력
   - 통신사 인증 팝업 UI 연결 (대행사 SDK가 제공하는 표준 팝업 or 리다이렉트)
4. **운영 환경 보안 설정**
   - `.env`의 `JWT_SECRET`, `PHONE_SALT` 랜덤 값으로 교체
   - CORS 허용 도메인 실제 서비스 도메인으로 제한
   - Rate Limiting 적용 (대행사 API 호출 남용 방지)

### 5-3. 이 프로젝트(정적 사이트)에서 담당 가능한 부분

- ✅ 프론트엔드 UI/UX (이미 완성 — `user.html`, `shop.html` 등)
- ✅ `js/age-verify-service.js` 서비스 레이어 (데모/실전 전환용 인터페이스 이미 준비됨)
- ✅ 데모 모드 명시 배너 (조작 가능성에 대한 사용자 안내)
- ❌ 실제 백엔드 서버 배포·운영 (별도 서버 환경 필요, 이 정적 사이트 빌더 범위 밖)
- ❌ 대행사 API 키 관리 및 서버측 암복호화 로직 (서버 개발 필요)

---

## 6. 프론트엔드 연동 인터페이스 (이미 준비됨)

`js/age-verify-service.js`가 데모 ↔ 실전 전환의 단일 접점입니다.

```js
// 현재 (데모)
AgeVerifyService.config.MODE = 'demo';

// 실전 전환 시 딱 2줄만 변경
AgeVerifyService.config.MODE = 'real';
AgeVerifyService.config.BACKEND_BASE_URL = 'https://api.zkpage.store';

// 호출부 코드는 변경 불필요 — 동일한 인터페이스
AgeVerifyService.verify({
  birthdate: '2001-05-01',   // demo 모드에서만 사용됨
  tier: 'adult19',
  purpose: 'alcohol',
  carrier: 'SKT',
}).then(function (result) {
  console.log(result.conditionResult); // true/false
});
```

즉, **백엔드가 준비되면 화면단 재개발 없이 설정값 2개만 바꿔서 실전 전환**이
가능하도록 미리 설계되어 있습니다.

---

## 6-1. SSO 통합인증과의 관계 — "1회 인증 → Aleo 토큰 재사용" 구조

`sport_sso.html` 등 여러 기관을 하나의 인증으로 묶는 **SSO(Single Sign-On) 서비스**를
운영할 경우, 통신3사 인증은 **매 접근마다가 아니라 딱 1번(최초 회원가입/등록 시)만**
수행하고 그 결과를 Aleo ZKP 토큰으로 변환하여 이후 모든 기관 접근에 재사용합니다.

```
① [최초 1회] 회원가입 / 최초 로그인
     └─ 통신3사 PASS 본인인증 (실명 + 생년월일 확인)
     └─ 자체 백엔드 서버가 검증 결과를 DB에 저장
              ↓
② [서버] 검증된 신원정보를 Leo/Aleo 회로 입력값으로 변환
     └─ private input: 생년월일, 실명해시 등 (외부 공개 안 됨)
     └─ membership_proof.aleo 실행 → ZKP 증명 생성
              ↓
③ [Aleo 토큰 발급] "검증된 회원임"을 증명하는 재사용 가능한 토큰
     └─ 유효기간 정책 필요 (예: 1년 주기 갱신)
              ↓
④ [SSO 계층] 발급된 토큰으로 N개 기관 모두 접근
     └─ 매 접근마다 통신사 재조회 없음 — 토큰 검증만 수행 (오프라인 가능)
     └─ 예: 종합스포츠센터 / 공공수영장 / 테니스장 / 클럽포털 / 국민체력100 / 대회관리
```

### 왜 이 구조가 맞는가

| 항목 | 매번 재인증하는 방식 (❌) | 1회 인증 + 토큰 재사용 (✅) |
|---|---|---|
| 통신사 인증 호출 횟수 | 로그인·접근할 때마다 반복 | **신규가입/갱신 시 1회만** |
| 비용 | 사용량(트래픽)에 비례해 계속 증가 | 신규 회원 수에만 비례 — **대폭 절감** |
| 사용자 경험 | 기관마다 재인증 (불편) | 최초 1회 후 매끄러운 SSO |
| ZKP 활용 의미 | 약함 (매번 원본 재검증하면 ZKP 이점 반감) | **강함 — "1회 증명, N회 재사용"이 ZKP 핵심 가치** |

이 구조 덕분에 SSO 서비스에서는 통신3사 인증 비용이 "전체 로그인/접근 횟수"가 아닌
**"순수 신규 회원 가입 건수"**에만 비례하게 되어, 5절의 비용 부담이 크게 줄어듭니다.

### 추가로 고려할 실무 이슈

| 이슈 | 설명 |
|---|---|
| **토큰 유효기간** | 영구 사용 vs 1년/분기 단위 재인증 — 통신사 정보 변경(번호이동 등) 대응을 위해 주기적 갱신 권장 |
| **토큰 폐기(Revocation)** | 탈퇴, 명의도용 발각 시 기존 토큰 무효화 로직 필요 (서버 블랙리스트 또는 짧은 만료주기) |
| **최초 등록 시점만 서버 필요** | ①단계(최초 통신사 인증)만 반드시 서버가 필요하며, 이는 본 문서의 3~5절 요건이 그대로 적용됨 |
| **일부 기관의 실시간 재확인 요구** | 예: 대회관리시스템이 대회 참가 시마다 실시간 연령 재확인을 요구하면, 그 기관에 한해 예외적으로 재인증 정책 별도 수립 |

> 📌 `sport_sso.html`의 5단계 인터랙티브 데모(기관선택→생체인증→ZKP증명→게이트웨이검증→접근허가)는
> 위 ④ 단계(SSO 토큰 재사용)만을 시연하며, ①~③(최초 통신사 인증 → 토큰 발급) 단계는
> "이미 완료된 상태"를 전제로 시작합니다.

---

## 6-2. ⚠️ 오해 방지 — "PASS 자체 스펙" vs "우리가 추가한 토큰 재사용 레이어"

**중요한 구분입니다.** "1회 인증 → 토큰 재사용" 구조를 보고 "PASS가 원래 세션/토큰
재사용 기능을 제공한다"고 오해하기 쉬운데, **그렇지 않습니다.**

| | 사실 |
|---|---|
| **PASS(NICE/Danal/KG모빌리언스 등)의 기본 스펙** | ❌ 세션·토큰 재사용 기능이 **없음**. 호출할 때마다 사용자가 그 순간 지문/PIN으로 재인증해야 하며, 결과는 그 1회 호출에 대해서만 유효한 "이벤트성 응답"임 |
| **"1회만 하면 된다"는 구조의 실체** | PASS 호출 자체는 정말로 딱 1번만 발생시키고, **그 결과를 우리가 별도로 설계한 "Aleo ZKP 토큰"으로 감싸서** 이후 재사용 가능하게 만든 것. 이 토큰 발급/검증/만료 로직은 **PASS가 제공하는 게 아니라 우리 백엔드(`server/`)가 직접 구현해야 하는 부분**임 |
| **비유** | Google 로그인 시 매번 ID/PW를 Google 서버에 보내지 않고 세션 쿠키로 재사용하는 것과 원리는 같음. 다만 PASS는 원래 "세션 쿠키"에 해당하는 기능이 전혀 없어서, 그 역할을 Aleo ZKP 토큰으로 **우리가 직접 만들어 끼워넣은 것** |

```
[PASS 자체의 기본 동작]              [우리가 얹은 토큰 재사용 레이어]
PASS 호출 → 그 순간만 유효      →    PASS 호출 결과(최초 1회)를
(세션 개념 없음, 매번 재인증)         자체 서버가 Aleo ZKP 토큰으로 변환
                                     └─ 토큰 발급/만료/폐기 로직은
                                        100% 우리 서버 구현 몫
                                     └─ PASS·대행사는 이 토큰의
                                        존재도, 재사용도 전혀 모름
```

### 이게 왜 중요한가
1. **대행사와의 계약/과금 협의 시** — 대행사에 "1회만 과금해달라"고 요청할 근거가 되는 게 아니라,
   단순히 **우리가 PASS를 호출하는 횟수 자체를 줄이는 설계**이므로 대행사 측 정책과는 무관합니다.
   (대행사는 여전히 "호출 1건 = 과금 1건" 방식이며, 우리가 호출을 자주 안 하는 것뿐입니다.)
2. **보안 책임 소재** — 토큰의 발급 기준·만료·폐기(Revocation)는 통신사/대행사가 보증하는 게 아니라
   **전적으로 우리 백엔드 설계 품질에 좌우**됩니다. 토큰 로직에 결함이 있으면 "한 번 인증한 사람이
   영구히 재사용 가능한 취약점"이 될 수 있습니다 → 5절 언급된 토큰 유효기간·폐기 정책이 필수인 이유입니다.
3. **"통신3사 인증을 매번 해야 하는 것 아니냐"는 질문에 대한 정답** — **맞습니다.** PASS 자체는 매번
   호출해야 하는 게 기본이고 정상입니다. "1회만"이라는 표현은 **"PASS 호출 자체를 1회로 줄이기 위해
   우리가 추가로 설계한 토큰 레이어가 있다"**는 뜻이며, PASS 서비스 스펙이 바뀐 게 아닙니다.

---

## 7. 단계별 실행 로드맵

| 단계 | 시점 | 할 일 | 담당 |
|---|---|---|---|
| 1 | 지금 | 데모 명시 배너 + 연동 대기 구조 (완료) | 이 프로젝트 |
| 2 | 영업 서류 준비 완료 후 | 대행사 견적 비교 → 계약 → API Key 발급 | 사용자 직접 진행 |
| 3 | Key 확보 후 | 백엔드 서버 인프라 구축 + `server/` 코드 배포 | 별도 개발팀/외주 |
| 4 | 백엔드 완성 후 | NICE 등 실제 API 연동 로직 완성 | 백엔드 개발자 |
| 5 | 연동 완료 후 | `age-verify-service.js` 설정 전환 + 통합 테스트 | 프론트+백엔드 협업 |

---

## 8. 참고 문서

- `server/README-SERVER.md` — 백엔드 서버 설치/배포 가이드 (PostgreSQL 16)
- `server/routes/auth.js` — PASS 본인인증 API 구현 뼈대
- `server/.env.example` — 필요 환경변수 전체 목록
- `js/age-verify-service.js` — 프론트엔드 연동 서비스 레이어
- `sport_sso.html` — "1회 인증 → Aleo 토큰 재사용" SSO 통합인증 실전 데모 (6절-1 구조를 시각화)

---

*최종 갱신: 2026년 7월 · ZKProofID 프로젝트*
