등록 프로세스
1
폼 제출
3sec.games/submit 에서 작성
2
URL 자동 검사
접속 가능 여부 + 중복 확인
3
검수
게임 직접 플레이, 1~3일
4
포털 등록
게임 상세 페이지 오픈
Step 1 — 점수 SDK 연동
아래 파일 중 하나를 프로젝트에 복사하고, 게임이 끝나는 시점에 submitScore()를 호출하세요.
TypeScript / React / Next.js
// src/lib/3sec-score.ts
const PORTAL_API = 'https://3sec.games/api/leaderboard';
const NAME_KEY = '3sec-player-name';
const PID_KEY = '3sec-player-id';
function getOrCreatePlayerId(): string {
try {
const url = new URL(window.location.href);
const pid = url.searchParams.get('pid');
if (pid) { localStorage.setItem(PID_KEY, pid); return pid; }
} catch {}
const stored = localStorage.getItem(PID_KEY);
if (stored) return stored;
const id = crypto.randomUUID();
localStorage.setItem(PID_KEY, id);
return id;
}
export async function submitScore(
gameId: string,
score: number
): Promise<{ personalBest: number | null }> {
const name = localStorage.getItem(NAME_KEY) || 'Player';
const playerId = getOrCreatePlayerId();
const res = await fetch(PORTAL_API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gameId, name, score, playerId }),
});
if (!res.ok) {
if (res.status === 429) throw new Error('cooldown');
throw new Error('Submit failed');
}
const data = await res.json();
return { personalBest: data.personalBest ?? null };
}
export function getPlayerName(): string {
return localStorage.getItem(NAME_KEY) || '';
}
export function setPlayerName(name: string): void {
localStorage.setItem(NAME_KEY, name.slice(0, 8));
}Vanilla JavaScript
// 3sec-score.js (Vanilla JS — type 제거 버전)
const PORTAL_API = 'https://3sec.games/api/leaderboard';
const NAME_KEY = '3sec-player-name';
const PID_KEY = '3sec-player-id';
function getOrCreatePlayerId() {
try {
const url = new URL(window.location.href);
const pid = url.searchParams.get('pid');
if (pid) { localStorage.setItem(PID_KEY, pid); return pid; }
} catch {}
const stored = localStorage.getItem(PID_KEY);
if (stored) return stored;
const id = crypto.randomUUID();
localStorage.setItem(PID_KEY, id);
return id;
}
async function submitScore(gameId, score) {
const name = localStorage.getItem(NAME_KEY) || 'Player';
const playerId = getOrCreatePlayerId();
const res = await fetch(PORTAL_API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gameId, name, score, playerId }),
});
if (!res.ok) {
if (res.status === 429) throw new Error('cooldown');
throw new Error('Submit failed');
}
return await res.json();
}
function getPlayerName() { return localStorage.getItem(NAME_KEY) || ''; }
function setPlayerName(name) { localStorage.setItem(NAME_KEY, name.slice(0, 8)); }사용 예시
import { submitScore, getPlayerName, setPlayerName } from '@/lib/3sec-score';
// 게임 오버 시점에 호출
async function onGameOver(finalScore: number) {
try {
const { personalBest } = await submitScore('your-game-id', finalScore);
if (personalBest !== null) {
showMessage(`최고 기록: ${personalBest}`);
}
} catch (e) {
if ((e as Error).message === 'cooldown') {
// 30초 쿨다운 — 조용히 무시하거나 안내 메시지
}
// 그 외 오류: 게임 진행에 영향 없게 무시
}
}
// 플레이어 이름 (선택)
setPlayerName('Cyrus'); // 최대 8자, 리더보드 표시명
getPlayerName(); // 현재 이름 조회Step 2 — ?pid= 파라미터
포털에서 게임을 열 때 URL에 ?pid= 가 자동으로 붙습니다. SDK가 이를 자동으로 처리하므로 별도 작업이 필요 없습니다.
단, SPA 라우터가 query param을 제거하는 경우 window.location.href에서 읽을 수 있도록 SDK 초기화 시점을 확인하세요.
Step 3 — 제출 시 필요한 정보
제출 폼에서 아래 항목을 입력합니다. AI 프롬프트로 자동 작성 →
게임 URLHTTPS로 배포된 공개 URLhttps://my-game.vercel.app
게임 이름포털에 표시될 제목Space Cleaner
한 줄 소개게임 카드 훅 문장 (최대 50자)탭 한 번으로 우주 쓰레기 청소!
게임 설명루프·목표·특징 (최대 200자)우주선을 조종해...
장르 태그최대 3개액션, 캐주얼
플랫폼해당하는 것 선택모바일, 데스크톱
한 판 소요 시간드롭다운 7개 중 선택1~3분
조작법한두 문장탭/클릭으로 발사
점수 단위숫자 뒤에 붙는 텍스트점, 층, m, Act
점수 최솟값/최댓값리더보드 유효 범위0 / 99999
브랜드 컬러게임 카드 배경 hex#1a237e
썸네일400×400 SVG/PNG URL(선택)
개발자 연락처이메일 또는 Discorddev@example.com
기술 요구사항
✓HTTPS — HTTP는 브라우저에서 차단됨
✓공개 URL — 로그인·결제 없이 즉시 접근 가능
✓모바일 지원 — 대부분의 플레이어가 모바일 사용 (강력 권장)
✓전체화면 강제 금지 — 플레이어가 포털로 돌아올 수 있어야 함
✓오디오 자동 재생 금지 — 첫 사용자 상호작용 후 재생 시작