1인 개발 뽑기 게임도 확률을 전부 공개해야 할까

매출 1억 원 이하면 법의 확률 공개 의무에서 빠진다. 그런데 Google Play 와 App Store 는 매출과 상관없이 확률을 요구한다. 결국 면제를 버리고 동료 한 명 단위까지 공개하기로 했다.

방치형 RPG 「브루니 키우기: 바랜 섬」의 모집 화면을 설계하다가 한 줄에서 손이 멈췄다. 「SSR 3% · SR 17% · R 80%」. 등급 확률은 이 한 줄이면 끝난다. 그런데 2024년 3월부터 2025년 9월까지 확률 표시 위반을 유형별로 보면 약 51%가 「확률 미표시·개별확률 미표시」였다. 확률을 아예 안 적었거나, 적었어도 하나하나의 확률을 빼먹은 경우다. 등급 한 줄만 적은 표가 정확히 이 칸에 걸린다.

그럼 1인 게임도 동료 하나하나의 확률까지 적어야 할까? 법만 보면 빠질 구멍이 있었다. 그래도 전부 공개하기로 했다. 왜 그렇게 정했는지부터 보자.

매출 1억 원 이하 면제가 있는데도 안 쓴 이유

국내 확률형 아이템 정보공개 의무는 게임산업진흥법 제33조 제2항, 시행령 제19조의2와 별표 3의2에 있고 2024-03-22부터 시행됐다. 연평균 매출 1억 원 이하 중소기업은 면제다. 출시 전인 1인 게임이면 당연히 이 면제 안에 든다.

걸리는 건 스토어였다. Google Play 결제 정책과 App Store 심사 지침 3.1.1 은 매출과 상관없이 무작위 상품의 확률을 공개하라고 한다. 그것도 구매 전에, 구매 버튼 가까이에. 법에서 빠져도 스토어에서 걸린다는 뜻이다. 기준이 둘이면 더 엄한 쪽 하나에 맞추는 게 일이 적다. 그래서 면제를 쓰지 않고 별표 3의2 를 그대로 대조표로 삼았다.

위반했을 때 무엇이 오는지도 조사해 뒀다. 시정요청, 시정명령(법 제38조 제9항) 순서이고, 시정명령을 따르지 않으면 2년 이하 징역 또는 2천만 원 이하 벌금(법 제45조 제11호)이다. 2025-08-01 부터는 표시 위반으로 손해가 났을 때 사업자가 고의·과실이 없음을 입증해야 하고, 고의 위반이면 손해액 3배 이내로 배상한다. 2026-10-01 국회 본회의를 통과한 개정안은 여기에 유통 제한과 자료 제출 요구를 더한다. 시행일과 조문 번호는 아직 확인하지 못했다.

확률이 붙는 자리를 하나도 빼지 않고 세기

먼저 게임 안에서 무작위로 뭔가가 나오는 자리를 전부 적었다. 생각보다 많았다.

자리나오는 것공개한 범위
모집 배너 5종동료동료별 개별 확률(1차판 14명, 완성판 58명·59명 두 벌)
물감 항아리 Lv1~30장비 84종레벨별 등급 확률 + 장비 1종당 확률
무작위 상자 3종재료·정수·선물품목별 확률(가중치 합 10,000)
원정 상점 무작위 진열후보 12종 중 6칸후보마다 6칸 안에 들 확률 50.000%

애매한 건 무료 재화였다. 항아리를 돌리는 붓은 무료 재화지만 첫 결제 꾸러미 같은 유료 상품에 같이 들어간다. 문체부 해설서 기준으로는 간접 유상이라 공개 대상이다. 원정 상점도 원정 증표라는 무료 재화로 사지만, 진열을 새로고침할 때 유료 재화인 빛구슬을 쓸 수 있어서 진열 확률을 공개했다.

광고를 보고 받는 무료 모집은 해설서 기준이 불명확했다. 여기서 오래 고민하지 않았다. 안전한 쪽으로 보고 상시 모집 확률표를 그대로 보여 주기로 했다. 칸마다 유상이냐 무상이냐를 따지기 시작하면 경계마다 판단이 하나씩 생긴다. 결국 「확률이 있는 행위는 전부 공개」 한 줄로 단순하게 묶었다.

장비 강화는 반대로 대상 자체를 없앴다. 강화를 100% 성공으로 두고 장비마다 무작위 옵션도 넣지 않았다. 별표 3의2 의 강화형·합성형 공개 대상이 아예 생기지 않는다.

등급 확률은 고정, 1명당 확률만 쪼개진다

추첨은 두 단계로 정의했다. 등급 가중치로 등급을 먼저 고르고, 그 등급 안에서 균등하게 한 명을 고른다. 이렇게 두면 동료별 확률은 계산으로 나온다.

1차판 모집 풀은 브루니를 뺀 14명, SSR 3명·SR 5명·R 6명이다. 상시 배너 1~9번째 칸에서 SSR 한 명은 3% ÷ 3 = 1.0000%, SR 한 명은 3.4000%, R 한 명은 13.333% 다. 풀이 커지면 등급 확률은 그대로 두고 1명당 확률만 작아진다. 완성판 기본 풀 58명(SSR 14명)에서는 SSR 한 명이 0.21429% 다.

여기서 표가 두 벌이 됐다. 8장 10화를 마친 계정에만 무채라는 동료가 풀에 들어온다. 결말 인물이 미리 나오면 안 되기 때문이다. 그 계정의 풀은 59명이고 SSR 한 명은 0.20000% 로 바뀐다. 게임 안 확률 화면은 그 계정에 적용 중인 표 한 벌만 보여 주고 머리에 「계정 진행에 따라 모집 풀이 달라져요」를 적는다. 누리집 공개 쪽에는 두 벌을 다 싣는다.

픽업 배너는 1차판에 열지 않기로 했다. 픽업 규칙은 픽업 SSR 1.0% + 나머지 SSR 2.0% 인데, SSR 이 3명뿐이면 나머지 2명도 1명당 1.0000% 가 된다. 상시 배너와 똑같다. 이름만 픽업이고 확률 이득이 없는 배너는 표시 광고 문제로 번질 수 있다. 그래서 SSR 이 5명이 되는 W1 부터 연다.

천장과 10번째 칸, 조건부 확률까지 표에 싣기

별표 3의2 는 확정 획득, 흔히 말하는 천장이 있으면 「조건과 횟수별 확률 전부」를 공개하라고 한다. 이 게임에는 조건이 걸리는 칸이 두 개 있다.

칸조건조건이 걸리면 쓰는 표조건이 걸릴 확률
10회 묶음의 10번째 칸앞 9칸에 SR 이상이 하나도 없을 때SSR 3% · SR 97% · R 0%13.422%
입문 모집 50번째 칸1~49회에 SSR 이 하나도 없을 때SSR 100%(SSR 안에서 균등)22.481%

함정은 구현에 있었다. 보장 칸을 「10번째 칸은 무조건 SR 이상 표」로 짜면 공개한 확률과 실제 추첨이 달라진다. 조건이 안 걸리면 10번째 칸도 1~9번째 칸과 같은 표로 뽑아야 한다. 그래서 추첨 코드가 데이터의 조건 문자열을 그대로 읽고, 조건이 걸릴 때만 다른 표로 바꾼다.

gdscript
static func guarantee_hits(g: Dictionary, i: int, so_far: Array, ctx: Dictionary) -> bool:
	var cond := str(g.get("condition", ""))
	var slot := int(g.get("slot", 0))
	if cond.begins_with("no_SR_or_above_in_slots_1_"):
		# 묶음 안 칸 번호
		if i + 1 != slot:
			return false
		for r in so_far:
			if str(r.grade) in SR_UP:
				return false
		return true

천장은 확률을 올리는 방식이 아니라 교환 방식으로 만들었다. 모집 1회마다 별가루가 1개 쌓이고, 200개를 모으면 동료 한 명을 직접 고른다. 회차가 몇 번째든 확률이 늘 같으니 「횟수별 확률」이 따로 없다. 공개표가 그만큼 단순해진다. 계산해 보면 픽업 동료를 얻기까지 평균 86.6회이고, 200회 안에 안 나와서 천장으로 받는 사람은 13.398% 다.

13.333% 와 0.21429% 를 찍는 함수 하나

반올림 규칙도 있다. 첫 유효숫자보다 네 자리 이상 아래까지 표시해야 한다. 다시 말해 유효숫자 5자리다. 화면마다 따로 짜면 어디선가 자릿수가 어긋난다. 그래서 모든 확률을 함수 하나로 찍는다.

gdscript
const SIG := 5

## p(퍼센트 값) → "3.0000%" · "13.333%" · "0.21429%" · "100%" · "0%"
static func pct(p: float) -> String:
	if p <= 0.0:
		return "0%"
	if absf(p - 100.0) < 1e-9:
		return "100%"
	var e := int(floor(log(p) / log(10.0) + 1e-9))
	var d := maxi(0, SIG - 1 - e)
	var s := ("%." + str(d) + "f") % p
	return s + "%"

자릿수 e 를 로그로 구하고 소수점 아래 자리를 4 − e 로 맞춘다. 3 이면 3.0000%, 13.333 이면 소수 셋째 자리까지, 0.21429 면 다섯째 자리까지 나온다. 표시값이 반올림이라 열 합계는 화면 숫자로 더하지 않는다. 설계 스크립트가 분수로 더해 정확히 100% 인지 따로 검사한다.

숫자는 코드에 적지 않았다. 게임 안 확률 화면, 누리집 공개 표, 설계 문서가 모두 같은 JSON 두 개에서 나온다. 누리집 표는 검색할 수 있는 HTML 문자열로 만들고 이미지 표는 쓰지 않는다. 같은 원본에서 나와야 셋이 어긋날 일이 없다.

통합하다가 이 원칙이 한 번 더 쓰였다. 상자 확률을 원본 표 그대로 그리면 1차판에 없는 품목의 확률까지 보인다는 지적이 나왔다. 그래서 지금은 실제로 여는 표, 그러니까 없는 품목을 빼고 다시 나눈 표를 그린다. 화면에 보이는 확률과 실제로 굴리는 확률이 같은 표에서 나와야 한다.

대조표에 아직 남은 빈칸

전부 「충족」으로 닫히지는 않았다. 광고 보상 아이템의 유상·무상 판단은 아직 미확인이다. 개정안의 시행일도 모른다. 개정안이 확률 설정·변경 이력과 지급 기록 제출을 요구하니 배너별 확률표 버전, 변경 시각, 추첨 결과 로그를 서버에 남기기로 했는데, 보관 기간은 확인하지 못해 최소 서비스 기간 동안으로 잡아 뒀다.

그리고 이 명세를 쓴 뒤 게임 방향을 「색 원정 + 섬 복원」으로 틀면서 모집 뽑기는 빼기로 했다. 키우기 장르 리뷰를 세어 보니 1~2★ 불만 1순위가 돈이었다. 서버에서 판정해야 하는 모집이 빠지니 작업량도 줄었다. 돌아보면 확률 한 줄마다 화면 문구, 조건부 표, 변경 공지, 로그가 따라왔다.

핵심 한 줄확률 공개는 등급이 아니라 나올 수 있는 것 하나하나에 붙는다.

자주 묻는 것

매출 1억 원 이하 1인 게임도 확률을 공개해야 하나?

국내법에는 연평균 매출 1억 원 이하 중소기업 면제가 있다. 하지만 Google Play 결제 정책과 App Store 심사 지침 3.1.1 이 매출과 상관없이 무작위 상품의 확률 공개를 요구한다. 스토어에 낼 게임이라 면제를 쓰지 않았다.

천장이 있으면 확률을 어떻게 표시하나?

시행령 별표 3의2 는 확정 획득이 있으면 조건과 횟수별 확률 전부를 공개하라고 한다. 조건이 걸리는 칸은 그 조건과 바뀐 뒤의 표를 둘 다 싣는다. 천장을 교환 방식으로 만들면 회차별 확률이 늘 같아 표가 단순해진다.

브루니가 적는다

색을 바꾸며 숨는 카멜레온이다. 혼자 게임을 만들며 막힌 곳을 한 편씩 적는다. 같은 데서 두 번 막히지 않으려고 쓰는 기록이다.

게임 개발 글 10편 모두 보기 →