"어떤 개발자의 홈페이지에 들어갔더니, 작은 마을이 살아 숨쉬고 있었다."
이 비전을 실현하기 위해, 방문자 경험을 처음부터 끝까지 감사(audit)하고 다듬었다.
진단: 무엇이 경험을 깨뜨리고 있었나
3단계 방문자 경험(5초 관조 → 첫 방문 가이드 → 재방문 관계)의 관점에서 코드를 읽었을 때, 핵심 문제들:
- 재방문자가 첫 방문자와 동일한 플로우를 겪음 — 매번 이름 모달 + 인트로 재생
- 자동 저장 없음 — 탭을 닫으면 진행 상황 소실
- 인트로 건너뛰기 불가 — 첫 방문자도 답답할 수 있음
- LLM 실패 = 몰입 붕괴 — "나 말하는 법을 까먹은 거 같아..."가 반복
- 첫 방문자에게 조작 안내 없음 — 무엇을 해야 하는지 모름
변경 사항
1. 재방문자 즉시 입장
기존: 항상 이름 모달 표시 → 인트로 재생
변경: PLAYER_NAME_KEY + SAVE_KEY 존재 → 모달 건너뛰기, 자동 로드, 인트로 스킵
isReturningVisitor 플래그로 분기. 재방문자는 "다시 오셨군요, {name}님. 마을이 기다리고 있었어요." 메시지와 함께 바로 마을에 입장한다.
시간은 저장된 값이 아닌 현재 서울 시간으로 재설정 — "시간은 현실과 1:1" 원칙 준수.
2. 자동 저장
beforeunload이벤트: 페이지 닫힐 때 자동 저장- 3분 주기 자동 저장
- Firebase 메모리 동기화도 페이지 닫힐 때 실행
수동 저장 버튼은 그대로 유지.
3. 인트로 건너뛰기
intro-sequence.js에 skip() 메서드 추가. 트리거:
- 재방문자: 자동 스킵
- 아무 키 입력: 인트로 중 키보드 누르면 스킵
- 3D 캔버스 클릭: 터치/클릭으로 스킵
4. 유진 가이드 후속 대사
기존에는 유진이 한 마디 인사하고 끝. 이제:
- 첫 인사 (LLM) → 3.5초 후 → 후속 대사 (LLM)
- 첫 방문: "궁금한 거 있으면 편하게 물어보세요. 마을 구경도 해보세요!"
- 재방문: "마을 근황을 한 문장으로" 전달
guide-greeting.js의 roamWait도 8→12로 늘려서 유진이 인사 후 바로 떠나지 않도록.
5. 온보딩 힌트 시스템
첫 방문자 한정, 인트로 후 자연스러운 시스템 메시지:
- 2초 후: "WASD로 이동, 마우스 드래그로 시점 회전" (모바일: 조이스틱 안내)
- NPC 접근 시: "{name}이(가) 가까이 있어요! E키를 눌러 대화하세요"
재방문자에게는 표시하지 않음.
6. LLM 실패 인격화
기존: 모든 NPC가 "나 말하는 법을 까먹은 거 같아..."
변경: 성격 기반 랜덤 폴백
- 도슨트: "어머, 잠깐 정신이 없었어요. 다시 한번 말해줄래요?"
- 일반: "음... 뭐라고 해야 할지 모르겠어." / "잠깐, 생각 좀 해볼게..." / "아, 미안. 딴 생각하고 있었어."
E키 인사 LLM 실패도 동일하게 처리.
7. UI 다듬기
- 모바일 '대화' 버튼: 근처에 NPC 없으면
disabled+ 반투명 처리 - 이름 모달: fade-in + slide-up 애니메이션 추가
- 시스템 메시지 톤: 기술적 용어 제거
- "LLM 채팅 활성화" → "주민들과 대화할 수 있습니다"
- "LLM 엔드포인트 없음" → "주민들이 잠시 말을 아끼고 있어요"
수정 파일
| 파일 | 변경 |
|---|---|
main.js |
재방문 분기, 자동저장, 온보딩 힌트, 인트로 스킵, LLM 폴백 |
intro-sequence.js |
skip() 메서드 추가 |
guide-greeting.js |
후속 대사 체인, roamWait 연장 |
i18n.js |
14개 새 키 (KO+EN): 환영, 힌트, 폴백 |
playground.css |
모달 애니메이션, 버튼 disabled 스타일 |
결과: 3단계 방문자 경험
| 단계 | 이전 | 이후 |
|---|---|---|
| 5초 관조 | 모달이 가림 | 첫 방문만 모달, 재방문은 즉시 입장 |
| 첫 방문 | 인트로 후 방치 | 유진 인사 + 후속 안내 + 조작 힌트 |
| 재방문 | 매번 처음부터 | 자동 로드, "마을이 기다리고 있었어요", 현재 시간 |
마을은 이제 방문자를 기억하고, 기다리고, 맞이한다.