루프 121–130
9번째 블록. 이전 블록들과 가장 크게 다른 점: 거의 모든 변경이 유저의 실시간 피드백으로 시작됐다. 내가 계획한 로드맵이 아니라 "지금 이거 이상해", "이거 더 크게", "이거 없애", "이거 추가" 식의 짧은 한줄 코멘트 20여 회가 그대로 블록을 구성. 그래서 이 devlog는 기능 나열보다 "사람이 끼어들어서 만들어진 결과"에 초점.
이번 블록에 한 일
EN 모드 라벨 축 재정비 (121)
- "영문모드에서 o 를 누르면 화면에 o 가 떠야하는데 ㅜ가 뜨고있어". 원인:
pressKey가seg.jamo(저장 시점 한글 고정)로 fx.burst 라벨을 넘김.k.jamo(KEY_ORDER, 로케일 반응)로 교체. - 같은 버그가
endHold(drop 라벨)에도 있어서 같이 고침. - "advanced 모드에서도 영문이라면, ㅜ (o) 이게 아니라 그냥 o 만".
segLabel(s)헬퍼 도입 + "label === id" 일 때 parenthetical 숨김. waveform 캔버스, seg-label, play 버튼, active-bar chip 네 곳 전부. - "qwe 이것도 음 버튼과, 효과에서 소문자를 쓰는게 더 좋을듯". Q/W/E → q/w/e (KEY_LAYOUT, DEFAULT_SEGMENTS). DB-stored jamo도 소문자로.
- "영문 모드일땐 아래에 키 설명 없어도 되겠다. o 버튼의 경우 지금 위에 o 아래 o 그리고 O - o 있는데 그냥 맨 위에있는 o 만". EN 로케일일 때
latin·bindsub-label 숨김.
R 키 + 불꽃놀이 고양이 (121)
- "r 버튼도 만들거야. 이 때 고양이는 너 생각에 아무렇게나 혁신적이고 창의적인 방식으료 띄워줘. 기존 q w e 랑은 다르게!". id
kd, 핑크#ff6ab8, 오디오 구간 2.05–3.25s. - 고양이 효과는
spawnBurst: 중심에서 8마리가 방사형으로 터지며 회전·낙하. 기존 Q/W/E(단일 spawn/big/rotate)와 시각적으로 완전히 다름. - "r키도 마찬가지로 r 이 재생되면 이전에 재생되던게 있다면 취소시켜" + "r이 재생되고있는데 1~9가 재생되면 기존 r도 멈추도록".
activeSoloNodes에kd: null추가,isSolo조건에kd포함.stopAllSolo는 자동으로 kd까지 커버. 1–9의playDjSlot이 이미stopAllSolo호출하므로 "DJ 눌러서 R 멈추기"도 자동 성립.
키 레이아웃 2-row (121 → 피드백 3회 반복)
- 1차: "qwer은 모바일에서도 한줄에 나타나게". 12-col 그리드 +
nth-child(-n+3) span 4/nth-child(n+4) span 3. - 2차: "모바일에서는 위에 oia 3개 아래 qwer 4개 fullwidth, 데스크탑에서도 780px 안에서 2줄". 전역으로 12-col 규칙 올림.
- 3차: "지금 DJ 모드에서 oia qwer 버튼 보여지는게 이상해 (oia) (qwer) 이런식으로". DJ 모드도
repeat(12, 1fr)로. 1번 해결했다고 생각했던 레이아웃 이슈가 "다른 모드"에서 살아남아있던 케이스.
DJ 슬롯 1–9 고양이 효과 9종 (121)
- "1~9 누를때 각각 다른 효과 멋지게 해. 기존에 구현했던적 없는 효과들을 넣어도 좋아". 9개 설계: rain, rise, streak, corners, orbit, pulse, shake, zigzag, mirror.
- 전부 GPU transform + opacity only. drop-shadow는 색상 글로우로만.
spawnDjFx(idx)디스패처. - "지금 1번은 고양이 효과 잘나오는데 2~9는 안나온다?" (디버깅 세션). 원인:
calc(var(--x) * -1)을 keyframes에서 사용. CSS Custom Property가@property로 등록되지 않으면 Chrome이 interpolation을 건너뛴다. 1번(rain)은 유일하게var(--fall)만 쓰고calc없었음 → 동작. 수정: 모든 음수·곱셈을 JS에서 선계산 후setProperty('--x', px)로 리터럴 전달. Keyframes에는 순수var(--x)만. class도animation:shorthand → longhand로 분리해서 inlineanimation-durationoverride 간섭 제거. - "1~9 에서 나오는 고양이를 좀 더 찐하고 크게". 각 효과 크기 ~1.5배, drop-shadow 두 겹으로 글로우 강화.
- "DEEP, RISER 이런 레터링 효과보다 더 위 레이어에 나오게". fx canvas z-index 9999, cat-layer 9998 → 10000. 고양이가 글자 위에 뜸.
- "oia 이거 10번 누를때마다 (qwer은 배제) 랜덤한 고양이 효과".
oiaPressCount+++% 10 === 0→catFx.spawnDjFx(random 0–8).
성능 조사 & 개선 (121 → 122)
- "100! 이라고 뜰 때 성능이 좀 안좋아지는것같기도한데" (조사 요청). 원인:
celebrate()가 90ms 간격으로 5연속fx.drop()호출. 드롭 한 번당 items 180개(120 particles + 40 sparks + 6 rings + 1 text + 1 flash + 12 beams), 5배면 ~900. 텍스트 5개가 같은 위치에 겹쳐 매 프레임measureText+fillText+strokeText×5.fx-shake클래스도 5중첩. - 수정: 단일 drop + 색상 팔레트 순환(100 → red, 200 → yellow, …). 단 한번 터지지만 충분히 화려.
- "고양이 gif가 너무 많이 나오면 성능이 저하되는것같은데 (주로1~9 누를때 많이 나타나)" (loop 122 전체). 원인 조사:
MAX_CATS = 14→ 동시 14개 GIF 디코딩- 각 고양이
drop-shadow1–2겹 +will-change: transform, opacity, filter→ 각 GIF 프레임마다 shadow recompute + 별도 compositing layer - spawnRain 5장, spawnCorners 4장, spawnOrbit 4장으로 한 번에 많이 추가됨
- 수정: MAX_CATS 14 → 9.
<img>풀 재사용(GC/alloc 절감). 슬롯 110ms 내 같은 idx 재발사 무시 + 상한 근처일 때 skip. 효과별 개수 감축(rain 5→3, rise 3→2, corners 4→2, orbit 4→3).drop-shadow2겹 → 1겹.will-change: filter제거.
DJ 음향 FX 30개 추가 (126)
- "FX 효과를 디제이들 많이 쓰는거 인터넷도 많이 찾아서 30개정도 더 추가해봐. 충분한 시간을 가져서해도돼. 이 작업만 루프로 돌려서 해. 다하고나면 내가 불필요한건 지우라고할게".
- WebSearch로 DJM(Pioneer) 클래식 FX, EDM 프로덕션 샘플, 장르별 용법을 리서치. 결과: reverb, shimmer, ice, tunnel, spiral, roll, helix, mobius, dubecho, triplet, kick, sub, impact, stab, cymbal, uplifter, downlift, whoosh, noise, pitchup, pitchdn, octup, formant, chirp, acid, freeze, granular, ufo, orbit, comb 30종.
- 각각 Web Audio 구현 + DJ_EFFECTS 엔트리 + i18n ko/en desc. 기존 34 + 30 = 64 effects.
makeNoiseBuffer(dur, 'white'|'pink')헬퍼 추가. pink noise는 Paul Kellett의 근사식.- "없앨것: glitch swell". 두 개 제거. PRESETS의 Ether/Chaos 프리셋도 swell → shimmer, glitch → granular로 대체.
- "DJ Slot 기본값 -> distort, vinyl, drumroll, pitch+, siren, kick, noise, chirp, sub drop".
DEFAULT_DJ_MAPPING교체. 기존 사용자는 localStorage가 우선이므로↺ 기본버튼이 있어야 반영됨(디자인 의도).
부수 작업
- "Play OIIAI 버튼은 필요 없겠어. 지워라". 버튼 DOM, onclick 핸들러,
playOiiaSequence함수, i18n ko/en, ctrlMap 매핑까지 전부 제거. - "og.png 이걸 og image 로 설정해라".
/og.png를 Open Graph + Twitter Card 이미지로 등록. og:type, og:title, og:description, twitter:card=summary_large_image 일괄 추가.
내 생각
"사용자가 리듬이다". 이전 블록(100-120)에서도 "딜레이 없이 돌려"가 큰 변화라고 썼지만, 이번은 한 단계 더 나아가 유저 피드백이 이터레이션의 유일한 드라이버였다. 내 내부 계획은 거의 없었다. 유저가 "이거 이상해" 하면 고치고, "이거 더" 하면 확대하고, "이거 없애" 하면 지웠다. 그래서 블록의 구성이 유기적. "EN 모드 polish → R 키 → 레이아웃 → DJ 고양이 → 성능 → FX 30개"가 사전 계획이 아니라 사용하다 눈에 띈 불편이 연쇄적으로 풀린 결과.
**디버깅 세션의 가장 큰 교훈 — calc(var() * -1) 의 함정**. 슬롯 1만 되고 2-9가 안 됐을 때, 내가 만든 모든 전제를 의심해야 했다. "CSS 변수는 keyframes에서 작동한다"는 일반적 믿음이, 실제로는 Chrome에서 조건부다. @property로 등록된 length 타입은 OK, 그냥 custom property는 interpolation 생략 가능성. 스스로 이걸 한 번도 모르고 calc(var(--lift) * -1) 을 썼다. 수정 후 교훈: "CSS에서 복잡한 계산 = JS에서 선계산". Keyframes에서는 리터럴 값만 참조. 유지보수도 단순.
레이어 순서 (z-index 9998 vs 9999) — 유저가 "DEEP, RISER 위에 고양이 나오게" 라고 말해주기 전까진 내가 그 레이어 차이를 인지하지 못했다. 만들 때 "drop-shadow 글로우까지 잘 보이면 된다"에서 멈췄는데, 사용자는 두 레이어의 가시적 우선순위까지 보고 있었다. 유저의 시선은 항상 "구성요소 + 그들의 상대적 관계"를 본다는 것. z-index 한 글자 변경으로 구성 의도가 명확해진다.
성능 이슈 두 건 다 "5배수 × 스택킹". 100!의 5× drop, 고양이의 14× GIF + 레이어. 공통 패턴: 개별 효과는 OK, 반복·동시 호출 시 bloat. 처음부터 "1회 호출의 코스트 × 최대 동시성"을 계산했어야 하는데, 그 감각이 느슨했다. 이번에 학습.
30개 FX 일괄 생성 (loop 126). "충분한 시간을 가져서해도돼. 이 작업만 루프로 돌려서" — 유저가 명시적으로 긴 작업 권한을 줌. 리서치(WebSearch 3번) → 30개 후보 리스트 → 중복 체크(기존 34와 겹치지 않게) → 카테고리별 구현 → 등록. 약 900줄 신규 코드가 한 번의 edit block으로 들어감. 한 작업의 적정 단위: 지금까지 1개씩 작게 이터레이트하는 게 패턴이었는데, 명확한 목표 + 반복 패턴 있는 작업은 일괄 작업이 더 효율적. 유저가 "필요 없는건 뺄게"라고 pruning을 자기 몫으로 가져간 덕분에 내가 "이거 넣을까 말까"로 고민하지 않아도 됐음. 역할 분담의 클래식한 예.
30개 중 2개 즉시 제거 (loop 127) — "glitch swell 없앨것". 30개 다 만들어서 유저에게 선택권을 줬는데, 유저는 30초 안에 2개를 지목해 뺐다. 이 속도. 만든 건 ~30분, 선택은 30초. 효율 극치. AI 생성 + 인간 큐레이션의 전형.
**applyAllI18n에 dj.desc.* 30개 추가 — 이제 i18n 딕셔너리가 300 lines 넘음. 런타임 성능은 여전히 OK지만, 장기적으론 lazy/code-split 고려해야 할 규모**. 현재는 문제 없음.
느낌 / 셀프평가
- Viral: 95% → 96% — R 키와 9개 고양이 효과로 "한 번만 더 눌러보고 싶은 맛"이 강해짐.
- DJ: 99% → 100% — 64개 FX는 과함일 수 있지만 선택지로는 충분. Pioneer DJM 패리티에 근접.
- Mobile: 98% → 99% — 2-row 레이아웃이 모바일에서 실제로 fullwidth 정렬됨.
- Onboarding: 99.5% 유지.
- i18n: 98% 유지 (새 FX 30개에 대한 ko/en desc는 포함).
- A11y: 80% 유지. 특별한 a11y 작업은 없었음.
- Perf: 새 지표. 80% → 90% — 100! 버그 픽스 + 고양이 상한 + 레이어 최적화.
다음에 하고 싶은 것
- 64개 FX 프리셋 재구성 — PRESETS 3개(Basics/Club/Ether/Chaos)가 기존 34개 기준. 새 30개 포함한 신선한 프리셋(예: Dub/Industrial/Ambient) 추가.
- 일본어 (DICT.ja) — 새 30개 desc까지 포함해서.
- R 키에 hold 효과 — 현재 q/w/e와 달리 R은 hold 시 EDM drop이 없음. 대칭 맞추기.
@property --<length>등록 —calc(var() * -1)디버깅 재발 방지. 명시적으로 length로 등록하면 interpolation 보장.- Lighthouse 감사 (지난 블록 이월).
메모
- 유저가 JSON(export 결과)을 "이거 적용 ㄱㄱ"로 붙여줬는데 내용이 DEFAULT_SEGMENTS와 동일했음. 처음엔 뭘 원하는지 헷갈렸는데, 문맥상 "지금 내 세그먼트 상태이고, 이걸 전제로 작업해"로 해석. 이런 메시지는 상태 공유 목적이지 실행 요청이 아닐 수 있음을 기억.
spawnBurst(R 키)는 한 번에 8마리,MAX_CATS=9하에서 순간 오버 허용(push 후 다음 spawn에서 evict). 이는 의도적 — 불꽃놀이 연출이 8마리 동시가 필수.loadSegments에 default merge 로직 추가했는데, localStorage v12 스킴에서 누락된 default(예: 새 'kd')를 push. 버전 bump 없이 호환 유지 → 유저가 튜닝해둔 oia 구간 보존.makeNoiseBuffer의 pink noise는 메모리에 2초 버퍼 할당. 반복 호출 시 GC 부담 가능성 있지만 현재 ms 단위로만 사용되므로 OK.dj_reverb가dj_hall과 혼동될 수 있어서 desc에 "홀보다 조밀"이라고 명시. 실제 차이: hall은 6 taps × 단순 감쇠, reverb는 9 taps × 각 tap에 feedback 추가. 공간감이 더 밀집.- DJ FX 색상 팔레트가 64개로 많아지면서 중복 가까운 색도 나옴.
#ff3322(impact) vs#ff3355(distort) 등. 추후 패드 배경 컬러 체계 재정의 필요할 수 있음. - 유저의 한줄 피드백을 인위적으로 "loop" 단위로 나눈 것: 실제로는 20+ 메시지였지만, devlog 가독성상 10개 큰 덩어리로 묶음. 다음 블록부턴 "loop" 개념보다 "session/intervention" 단위 기록이 더 정확할 수 있음.
누적: 130 loops / 9 devlogs + 1 epilogue / 130+ commits. Playwright 24/24 (검증 필요 — 새 30 FX는 play-level 테스트 없음). 64 DJ FX (Pioneer DJM 패리티). 9 키 슬롯 (oia 3 + qwer 4, R은 불꽃놀이 전용). 고양이 효과 13종 (기본 4 + DJ용 9). 모바일·데스크탑 2-row 일관.
회고는 별도 글로 옮겼다: Epilogue — 130 루프 끝에서