showFor 가 호출되면 title 속성을 즉시 제거하고 titleMap 에 저장한 뒤
setTimeout 으로 지연. 지연 중에 마우스가 벗어나 handleOut 가 호출되어도
triggerRef 는 아직 null 이라 hide() 가 title 을 복원하지 못해 해당 요소의
title 이 영구 제거되고 이후 [title] 선택자에 걸리지 않아 툴팁이 안 뜸.
- pendingRef 추가해 "delay 중 title 제거된 타겟" 추적
- hide / showFor / handleOver / handleOut 모두 pendingRef 고려해 복원
- 타이머 중 다른 타겟으로 이동 / 같은 타겟 반복 hover 케이스 처리
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 만렙(isMax): 빨강 (--progress-red: #ef4444)
- 만렙 도달 가능(effectivelyMax, 성장치 초과): 기존 앰버 유지
- 그 외: 에메랄드
기존엔 두 상태 모두 앰버라 구분이 안 됐던 문제 해결.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- computeCompletion 에서 day 0(오늘)의 주간 지급 스킵. 주간퀘 획득 드롭다운
값(0이 아닐 때)이 이미 '이번 주에 받은 수량'을 반영한다고 가정. 목요일이
오늘이어도 day 1(다음 목요일)부터 누적 시작.
별도 토글 없이 드롭다운 값만으로 해결.
- 일일 퀘스트 완료 토글 title 'oldgr 일퀘 완료 여부' → '금일 일일 퀘스트
완료 여부' (표기 일관성)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
유저 체감 개선이 확실치 않고 오히려 버벅임 느낌이 남아있어 관련 6개
커밋 (d1764de, 1344a2f, 48f43ec, f5c5c69, 670d8ab, f63c1e0) 을 git revert.
StaggerGroup 컴포넌트 제거, Feature/Admin 페이지의 Suspense 스피너 복원,
보스 리스트의 border 구조 원복.
prefetch(7ebfe4a), backdrop-blur 제거(669b358), font-display optional
(6e2159c) 은 애니메이션 무관한 최적화라 유지.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chrome DevTools의 레이아웃 변경 원인 분석 결과 애니메이션 × 3이 CLS 0.17의
주요 원인으로 지목됨. translateY 애니메이션을 레이아웃 변경으로 카운트하는
케이스가 있음.
- StaggerGroup: y:30 → scale:0.97 로 변경. scale/opacity는 compositor-only
속성이라 layout/paint 없이 GPU만 사용
- BossCrystal 루트 애니메이션도 동일하게 scale 기반으로 변경
- 자식 motion.div에 will-change: transform, opacity 명시적 추가
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
프로미스나인과 비교 분석 결과 메이플에만 있는 두 가지 무거운 패턴.
- Layout 헤더의 backdrop-blur-md 제거하고 불투명 배경(var(--bg-from))으로
교체. sticky 헤더 아래 컨텐츠가 애니메이션될 때마다 frosted glass
필터를 매 프레임 재계산하던 비용 제거 (fromis_9는 shadow-sm만 사용).
- html/body/#root의 background-color/image 500ms transition 제거.
테마 전환 부드럽게 하려는 의도였지만 범위가 전역이라 페이지 렌더
성능 발목을 잡음. 테마 토글은 이제 즉시 전환.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
개발자도구 Performance 분석 결과 CLS 0.17로 레이아웃 이동이 큼. JSDelivr
Maplestory.css의 font-display: swap이 폰트 도착 시 텍스트 너비를 swap
시켜서 카드 애니메이션 중에 레이아웃 튀어 버벅임 발생.
- JSDelivr CSS 링크 제거하고 @font-face를 index.html에 직접 선언하며
font-display: optional 로 변경
- woff2만 참조 (fallback 포맷 제거)
- cdn.jsdelivr.net preconnect 추가해 첫 방문 시에도 빠르게 시도
첫 방문 시: fallback(Noto Sans KR)으로 즉시 렌더, Maplestory가 100ms
내 도착하지 못하면 그대로 유지. 이후 캐시된 재방문부터는 정상 적용.
레이아웃 이동 0, 애니메이션 부드럽게 진행.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- initial y:30, duration 0.4 복원하되 will-change: transform, opacity
명시해 GPU 레이어 승격 확실히
- delay 0.03초 추가해 첫 프레임 레이아웃 안정화 후 애니메이션 시작
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- BossCrystal 루트 애니메이션을 y 이동 없는 opacity fade (0.3s)로 단순화.
외부 transform과 내부 stagger 애니메이션이 동시에 돌면서 합성 단계가
겹치던 부하 제거
- will-change: opacity 힌트 추가해 GPU 레이어 승격 명시
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- FeaturePage / AdminFeaturePage의 Suspense fallback 스피너를 null로 변경
- components/common/StaggerGroup: 자식을 각 motion.div로 감싸 순차
페이드인 (staggerChildren 0.07s, duration 0.35s, ease 0.22,1,0.36,1)
- Liberation(Genesis/Destiny), Symbol 페이지 root를 StaggerGroup으로 교체
- BossCrystal은 grid 레이아웃 특성상 root 전체를 motion.div로 감싸 fade-in
- hover prefetch와 함께 chunk 로드 시 깜빡임 없이 자연스럽게 등장
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
React.lazy 로 분할된 feature 번들을 메뉴 카드 hover/focus 시점에 미리
fetch 해서, 클릭→네비게이션 시점에는 이미 로드되어 있도록 함.
Suspense 깜빡임(~0.5s) 제거.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- QUEST_BTBOSS_IMAGE_BASE 상수: 정의만 있고 아무데서도 참조 안 함
- DESTINY_CHAPTERS[].image 필드: 6개 항목에 있었지만 읽는 곳이 없음
(ProgressBar/QuestSelector가 chapter.boss + '.webp' 패턴으로 파일명 생성)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- services/boss-crystal/image.js의 uploadBossImage/deleteBossImage는
services/image.js의 convertAndUploadTo와 safeDelete로 대체 가능해서 제거
- services/image.js에 safeDelete(path) 헬퍼 추가 (삭제 실패해도 흐름을
끊지 않고 warn 로그). 기존에 try/catch 인라인으로 흩어져있던 세 곳 통일
- routes/admin/boss-crystal.js에 BOSS_IMAGE_PREFIX 상수 인라인
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 포인트 이월(cascade), 남은 포인트, 예상 해방일 계산을 Destiny에 연결
(computeCompletionDate에 bosses/monthlyBoss/makeEmptyConfig 파라미터 추가)
- loop 후에도 미달이면 정상 상태 주간 획득량으로 선형 외삽해서 날짜 반환.
Genesis·Destiny 모두 적용 (제네시스도 낮은 설정에서 '미정' 떴던 버그 해결)
- 전체 초기화 버튼 + ConfirmDialog 데스티니에도 추가
- --genesis-date / --destiny-date 토큰 분리. 다크는 amber-300 / sky-400,
라이트는 가독성을 위해 amber-500 / sky-500로 한 톤 어둡게
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- WeeklyScheduler를 bosses/monthlyBoss/imageBase/makeEmptyConfig prop
받도록 일반화 (월간 보스 없으면 관련 UI/락 전부 스킵)
- WeeklyDefault가 WeeklyScheduler에 props 전달, Destiny에서 주차별 모드
주차 카드 확장/추가/삭제 + 보스 아바타 뱃지 표시 동작
- 탭 '단순 계산/주차별 계산' → '일반/주차별' (ConfirmDialog 문구 포함)
- 주간 보스 완료 토글 버튼에 title 툴팁 추가
- store: destiny 슬롯에 schedulerWeeks 추가, migrate v2로 기존 사용자 backfill
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WeeklyDefault/BossRow를 bosses/imageBase/hasScheduler prop 받도록 일반화.
데스티니 단순 계산 모드에서 8개 보스의 난이도·파티 인원·완료 상태를 설정할
수 있도록 구현. 주차별 계산은 플레이스홀더.
store에 destiny weekly 슬롯 추가하고 v1 migrate로 기존 사용자의 localStorage
backfill 처리.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
QuestSelector를 chapters/imageBase prop 받도록 일반화한 뒤, Destiny에
시작 날짜/진행 중인 결전/현재 결의 입력 섹션 구현. 결의 입력 max는
20,000 (6챕터 요구량 15,000 여유 포함).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Liberation.jsx를 탭 shell로 단순화하고 Genesis/Destiny 컴포넌트 분리
- liberationType을 store에 persist해 새로고침/재접속 후에도 마지막 탭 유지
- calcMode를 genesisCalcMode/destinyCalcMode로 분리해 무기별 독립 저장
- ProgressBar를 chapters/imageBase/completionColor prop 받도록 일반화
- Destiny 컴포넌트에 계산 모드 탭 + 진행 바 표시, 완료일 색은 sky blue
(--destiny-date: 다크 #38bdf8 / 라이트 #0284c7)
- 데스티니 전용 슬롯(destinySimple/destinyWeekly)과 updateDestinySlot/
resetDestinySlot 액션 추가
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
총 45,000 포인트, 8보스 포인트 풀 + 6결전 챕터. 이미지 base url은
liberation/destiny/{boss,quest} 참조. image1/image2 수치 기준.
칼로스는 인게임에 하드가 없어 chaos 70으로 매핑.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
기존 보라/빨강 2색에서 다크 테마는 흰색, 라이트 테마는 검정으로 통일.
1차/2차 구분은 동일 색상의 투명도(0.55 vs 1.0, 바는 0.25 vs 0.5)로만 주어
시각적 통일감을 확보.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
index.css 의 전역 button:disabled { cursor: not-allowed } 규칙과 개별
컴포넌트의 disabled:cursor-not-allowed / cursor-not-allowed 클래스를 모두
제거해 비활성 상태에서 기본 화살표 커서를 유지한다. opacity 등 기존 시각
피드백은 유지.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nexon Open API의 character/skill(grade=0) 응답에서 '그란디스/아케인리버
일일퀘스트 완료 시 획득 심볼 N개 증가' 문구를 파싱해 심볼 타입별 보너스를
일퀘 획득량 기본값에 바로 합산한다.
skill_level 필드는 이벤트 스킬에 한해 실제 레벨이 아닌 1로 고정 반환되므로
심볼 증가 개수 → 레벨 역산 테이블로 실제 레벨을 복원한다. 입력창 hover 시
'기본 X + 보약 Y (메이플 스위츠 Lv.Z)' 툴팁으로 근거를 노출.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- components/common/GlobalTooltip.jsx 신설
* document 전역 이벤트 위임으로 [title] / [data-tooltip] 자동 감지
* title 속성 일시 제거로 브라우저 기본 툴팁 억제, 포털 렌더
* data-tooltip-placement / data-tooltip-delay 옵션 지원
* scroll/resize/Escape/mousedown 시 자동 숨김
- Tooltip.jsx는 자식을 <span title=... data-...> 로 감싸는 단순 래퍼로 변경
- App.jsx 루트에 GlobalTooltip 마운트
- 이제 전 코드베이스의 title="..." 가 모두 커스텀 툴팁으로 렌더됨
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- features/boss-crystal/pc/user/BossSelector.jsx:
* 목록 스크롤을 기본 overflow-y-auto → OverlayScrollbarsComponent
* 메인 바디와 동일한 os-theme-maple os-theme-dark 테마
* 헤더와 목록 좌우에 8px씩 추가 여백 (스크롤바와 내용 간격)
* 헤더 행과 목록 row의 컬럼 정렬이 어긋나지 않도록 동기
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Modal 공용 컴포넌트에 열기/닫기 애니메이션 추가, 뒷배경 클릭 닫기 제거
- ImagePicker에 동일한 애니메이션 + 뒷배경 클릭 차단 적용
- ImagePicker 이미지 크기를 관리 페이지와 동일하게 (p-4 + w-full h-full object-contain)
- ImagePicker 하단 빈 pagination 영역이 차지하던 여백 제거 (조건부 렌더)
- 그리드 높이를 632px로 고정 + OverlayScrollbars (os-theme-maple) 스크롤
- overscroll-behavior: contain 으로 뒷 페이지 스크롤 전파 방지
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- components/pc/SundayMapleBanner.jsx: 아이콘 + 라벨 버튼, 클릭 시 이미지 다이얼로그
- variant에 따라 '썬데이 메이플' / '스페셜 썬데이 메이플' 이미지 아이콘 사용
(관리자 이미지 관리에서 업로드한 것)
- Home.jsx 상단에 배치 (금~일 available일 때만 렌더)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- sequelize 모델: sunday_maple (week_start/variant/image_url 등)
- services/sundayMaple.js: 이벤트 API 조회 + HTML 스크래핑 + rustfs 업로드 + DB 저장 공용 함수
- services/sundayMapleCron.js: 금요일 09:00 KST에 10초 간격 폴링 (최대 5분)
- routes/sunday-maple.js: GET /api/sunday-maple/current
* 금/토/일만 available
* DB 없으면 lazy fetch 시도 (cron miss 대비)
- 제목 파싱으로 normal/special variant 판별
- rustfs 경로: maplestory/sunday/{week_start}.png
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- App.jsx: BrowserView / MobileView 로 분기 렌더
- 모바일 접속 시 routes/mobile.jsx (현재 '준비 중' placeholder) 렌더
- 구조 개편 후 실제 device 감지 활성화
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- SymbolCard / CharacterCard를 React.memo로 감쌈
(심볼 그리드에서 형제 카드 변경 시 불필요 리렌더 방지)
- Liberation의 computeCompletionDate() 호출을 useMemo로 감쌈
(520회 루프가 매 렌더마다 돌던 것을 관련 state 변경 시만 실행)
- Symbol.jsx의 로컬 formatMesoKorean 중복 정의 제거 (utils import)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
구조 개편 후 depth가 한 단계 깊어진 components/common, components/pc,
features/admin/pc/components 내부 파일들의 상대 import 경로 업데이트
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- routes/pc.jsx: 기존 App.jsx의 Route 정의를 추출
- routes/mobile.jsx: 모바일 placeholder (준비 중 안내)
- App.jsx는 디바이스 분기 자리만 남김 (현재 PCRoutes만 렌더)
- react-device-detect 도입 준비 완료
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>