Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Is that skill safe to install?
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
Are you the author of Compliance?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/snwlee-compliance)
---
name: compliance
description: 유료 확률 아이템 규정 — 확률 공개·합계·럭 아이템 수치 설명·금지 지역 대체 경로를 판정한다. 트리거 "확률형 아이템", "뽑기", "가챠", "루트박스", "확률 공개", "규정", "심의", "연령 등급".
---
# compliance
nereus:common 규칙을 따른다. 담당 에이전트: security.
**엔진과 무관하다** — 규정은 엔진이 아니라 플랫폼과 법령이 정한다.
**시뮬레이터·타이쿤의 알 뽑기·펫 뽑기가 정확히 유료 확률 아이템이다.** 재미 요소가 아니라
규제 대상이라는 뜻이다. 두 층에서 강제된다.
## 0. 이 검사기만 장르 프로파일을 받지 않는다
다른 검사기(`sound`·`liveops`·`impact`·폰트)는 전부 장르 프로파일에서 기준을 읽는다.
여기는 **읽지 않는다.**
법적 요건이기 때문이다. 장르별 설정으로 두면 `maxOddsError: 5` 같은 값을 넣어
**설정으로 규정을 끌 수 있다.** 확률 합이 100% 여야 하는 것은 오비든 타이쿤이든 같다.
정책 수치는 `policy.json` 에 **출처 URL 과 확인일**과 함께 있다. 장르 프로파일과 다른 이유로
데이터다 — 장르는 우리가 정하는 값이고, 정책은 **남이 정하고 남이 바꾸는 값**이다.
바뀌면 따라가야 하고, 따라가려면 언제 확인한 건지 알아야 한다.
## 1. 근거
- **로블록스 정책** (확인 2026-09-13): https://create.roblox.com/docs/production/monetization/paid-random-items
- 구매 **이전에** 모든 결과와 실제 수치 확률 표시. 열쇠·스핀 티켓 같은 간접 구매도
"인게임 화폐를 쓰기 전"에 보여야 한다.
- 확률은 백분율이고 **합이 정확히 100%**. 소수 4자리 이상으로 반올림하면
"표시된 개별 확률의 합이 100%가 아닐 수 있다"는 문구를 붙인다.
- 확률을 올려주는 아이템은 영향을 **수치로** 설명하고, 활성 중 **바뀐 확률을 동적으로 갱신**한다.
- 1회만 얻을 수 있는 결과가 있으면 **남은 확률을 갱신**해 그 유저의 실제 확률을 보여야 한다.
- `PolicyService` 의 `ArePaidRandomItemsRestricted` 가 참인 유저에게는 무료 대체 경로·
확정 구매·순서 고정 중 하나를 제공하거나 랜덤 요소를 빼야 한다. **일부 지역에서 법적 금지다.**
- **예외**: 돈을 쓰지 않고 얻는 랜덤 보상은 공개 의무가 없다.
- **한국 게임산업법** (2024-03-22 시행): 게임·웹사이트·광고에 확률 표시 의무.
시정명령 불이행 시 2년 이하 징역 또는 2천만원 이하 벌금.
> **미확정**: 로블록스 UGC 경험 제작자가 한국법상 직접 의무 주체인지는 확정되지 않았다.
> 플랫폼이 의무 주체일 가능성이 있다. **하네스는 이것을 판정하지 않는다** — 법률 자문 영역이다.
> 다만 판정 내용은 어느 쪽이든 같다. 로블록스 정책이 이미 전역으로 같은 것을 요구한다.
## 2. 계획을 JSON 으로 적는다
```json
{
"markets": ["KR"],
"restrictedFallback": "제한 지역에는 확정 구매 상자를 제공한다",
"boxes": [
{
"name": "pet-egg",
"paid": true,
"disclosedBeforePurchase": true,
"disclosureSurfaces": ["game", "website", "ad"],
"outcomes": [
{ "item": "common", "odds": 90 },
{ "item": "rare", "odds": 9.5 },
{ "item": "legendary", "odds": 0.5 }
],
"hasUniqueOutcomes": false,
"dynamicRemainingOdds": false
}
],
"luckItems": [
{ "name": "lucky-potion", "affects": ["pet-egg"], "numericEffect": "legendary 0.5% → 1.5%", "dynamicUpdate": true }
]
}
```
**`paid` 를 좁게 잡지 않는다.** 인게임 화폐라도 Robux 로 살 수 있으면 유료다.
간접 구매(열쇠·스핀 티켓)도 유료다. 무료 랜덤만 예외다 — 넓게 잡으면 규정을 우회하게 된다.
## 3. 검사
```bash
echo '{"plan":{...}}' | node "${CLAUDE_PLUGIN_ROOT}/lib/compliance-check.mjs"
```
| 위반 코드 | 뜻 |
|---|---|
| `no-outcomes` | 유료 상자에 결과 목록이 없다 |
| `odds-sum` | 확률 합이 100 이 아니다 (소수 4자리 이상 + 면책 문구면 예외) |
| `odds-undisclosed` | 구매 전 공개 선언이 없다 |
| `disclosure-surface` | 그 시장이 요구하는 표면이 빠졌다 (KR 은 게임·웹사이트·광고) |
| `unique-no-remaining-odds` | 1회성 결과가 있는데 남은 확률 갱신 선언이 없다 |
| `luck-effect-unexplained` | 럭 아이템의 영향이 수치로 설명되지 않았다 |
| `luck-no-dynamic-update` | 럭 아이템 활성 중 동적 갱신 선언이 없다 |
| `luck-target-missing` | 럭 아이템이 없는 상자를 가리킨다 |
| `no-restricted-fallback` | 유료 확률 아이템이 있는데 금지 지역 대체 경로가 없다 |
확률 합은 소수 6자리로 반올림해 비교한다. 부동소수점 오차(`0.1 + 0.2 !== 0.3`)로
**정상인 계획을 위반으로 잡으면 게이트를 못 쓴다.**
## 3.1 통과한 뒤의 배선
판정이 통과하면 **SDK 배선은 우리가 하지 않는다.** Unity 스택이고 공식 플러그인이
`ready` 면(`nereus-game:unity` 4장) `implement-in-app-purchases` · `levelplay-unity-integration`
으로 넘긴다. 로블록스면 `roblox` 스킬의 `MarketplaceService` 경로를 쓴다.
**판정 자체는 넘기지 않는다.** 규정은 엔진이 아니라 플랫폼과 법령이 정한다 —
엔진 플러그인은 확률 합이 100 인지, 금지 지역 대체 경로가 있는지 모른다.
순서는 항상 **판정 먼저, 배선 나중**이다. 거꾸로 하면 이미 배선한 것을 고치게 된다.
## 4. 정책이 바뀌면
`policy.json` 의 `checkedAt` 을 보고, 오래됐으면 `source` 를 다시 읽어 수치를 갱신한다.
**코드를 고치지 않는다.** 수치가 코드에 있으면 언제 확인한 건지 알 수 없다.
## 하지 말 것
- 확률을 "대략" 적지 않는다. 실제 수치여야 한다.
- 럭 아이템을 "확률이 올라갑니다"로 넘기지 않는다. 수치로 적고 활성 중 갱신한다.
- 금지 지역 대체 경로를 나중으로 미루지 않는다. 없으면 그 지역에서 게임이 막힌다.
- 이 판정을 밸런스 논의와 섞지 않는다. 밸런스는 우리가 정하고, 이것은 남이 정한다.