Daily — 매일 만나는 우주 한 조각
매일 달라지는 천체 카드 — LCG 시드 기반 결정적 선택, Canvas 카드 렌더링, 방문 스트릭, 이미지 공유까지
시작 — 매일 열어보는 이유
사이트에 들어올 때마다 같은 화면이 보이면 재방문의 동기가 약해집니다. 무작위도 아니고 순서대로도 아닌, "오늘"에만 해당하는 콘텐츠가 있으면 어떨까 — 그 아이디어에서 Daily가 시작되었습니다.
카드 풀과 결정적 선택
Daily의 카드 풀은 세 가지 소스에서 구성됩니다.
| 타입 | 소스 | 예시 |
|---|---|---|
scale | Scale of the Universe 오브젝트 | 양성자, 지구, 은하 |
constellation | 88개 별자리 데이터 | 오리온, 큰곰, 전갈 |
stellar | 항성 진화 단계 | 성운, 주계열성, 블랙홀 |
카드 선택에는 LCG(Linear Congruential Generator)를 사용합니다. 날짜를 시드로 변환하면 매일 다르지만 같은 날에는 누가 접속해도 동일한 카드가 나옵니다.
function getCardForDate(d: Date): DailyCard {
let seed = d.getFullYear() * 10000 + (d.getMonth() + 1) * 100 + d.getDate();
seed = (seed * 9301 + 49297) % 233280;
return CARD_POOL[seed % CARD_POOL.length];
}
9301, 49297, 233280 — 고전적인 LCG 상수로, 주기가 충분히 길면서도 구현이 단순합니다.
Canvas 카드 렌더링
카드 중앙의 천체 이미지는 Canvas로 직접 그립니다. 카드 타입별로 전용 렌더러를 사용합니다.
- Scale 오브젝트:
drawObject()— Scale of the Universe의 렌더러를 재활용합니다 - 별자리:
drawConstellationCard()— Gnomonic 투영으로 별 위치를 계산하고, 연결선과 함께 그립니다 - 항성 진화:
drawStellarCard()— Stellar Life 게임의 스테이지 렌더러를 활용합니다
카드 외곽에는 6초 주기로 천천히 점멸하는 글로우 애니메이션을 적용했습니다. CSS @keyframes로 border-color와 box-shadow를 동시에 제어합니다.
@keyframes cardGlow {
0%,
100% {
border-color: rgba(var(--js2-primary), 0.35);
box-shadow: 0 0 10px rgba(var(--js2-primary), 0.15);
}
50% {
border-color: rgba(var(--js2-primary), 0.7);
box-shadow: 0 0 14px rgba(var(--js2-primary), 0.4);
}
}
방문 스트릭
캘린더 UI에는 방문 기록이 보라색 점으로 표시됩니다. localStorage에 방문한 날짜 목록을 저장하고, 연속 방문 일수(스트릭)를 계산합니다.
storageSet('daily:visited', { ...prev, [dateKey]: true });
storageSet('daily:streak', calculateStreak(visitedDates));
스트릭이 쌓이면 캘린더 헤더에 연속 일수가 표시됩니다. 작은 장치이지만, 매일 한 번씩 접속하게 만드는 동기 부여가 됩니다.
이미지 공유
공유 버튼을 누르면 단순 URL이 아니라 카드 이미지 자체가 공유됩니다. 별도의 오프스크린 캔버스에 공유용 카드를 새로 렌더링합니다.
function renderShareCard(srcCanvas, resolved, locale): Promise<Blob | null> {
// 400×dynamic 크기, 2x DPR
// 배경 그라디언트 + 테두리 + 뱃지 + 천체 이미지 + 이름 + 설명 + 워터마크
}
공유 카드에는 천체 이름, 영문명, 크기 정보, 설명 텍스트, 워터마크가 포함됩니다. navigator.share({ files }) API로 이미지 파일을 직접 전달하고, 미지원 환경에서는 텍스트 복사로 폴백합니다.
마치며
Daily는 기존 콘텐츠(Scale, Constellation, Stellar)를 재조합해 매일 새로운 경험을 만들어내는 구조입니다. 새 카드 타입을 추가하려면 CARD_POOL에 항목을 넣고 렌더러를 연결하면 됩니다.
하루에 하나, 우주에서 꺼낸 카드 한 장. 그것만으로 오늘 이 사이트를 방문한 이유가 됩니다.