Mind Map 형태의 협업 웹페이지 제작
👉🏻깃허브 링크
언어 & 런타임: Node.js, TypeScript
프레임워크: Next.js, React, TailWind CSS
상태관리: Zustand
언어: TypeScript
서버: NestJS
통신: Socket.io
CRDT: Yjs
DB: MongoDB, MySQL
프론트엔드 개발
UI 설계 및 구현
2025-05-28 ~ 진행중
상태 구조
상태(store)는 각 노드가 children 배열로 자식 ID들을 가지고 있음삭제 과정
추가 과정에서의 문제
하지만,
새로 추가된 행성/위성의 x, y 좌표는 기존 children 배열의 마지막 인덱스 기준으로만 저장됨
이미 store에 저장된 나머지 행성/위성의 x, y 좌표는 "한 번 생성된 후로는" 바뀌지 않음
그래서,
중간을 삭제하고 다시 추가해도 이미 남아 있는 애들은 좌표가 예전 인덱스에 박혀 있고
새로 추가된 행성/위성만 마지막 자리에 위치함
이 때문에 중간에 빈 공간이 생기고, 추가로 계속 넣어야만 그 빈 칸 까지 채워지는 문제가 발생함
핵심 아이디어
index로 매번 동적으로 계산하도록 구조를 바꾸기구현 방식
-store에는 계층구조만 관리 (children, parentId, ...),
Nodes 컴포넌트(혹은 실제로 그리는 곳)에서
이러면 어떻게 되나?
문제: store에서 좌표를 미리 저장하는 구조 + 삭제 후 나머지 좌표를 갱신하지 않아 화면에 빈칸이 생김
해결: 좌표를 미리 저장하지 않고, 렌더링 시 children의 index로 동적으로 좌표 계산
결과: 삭제, 추가 반복에도 항상 빈칸 없이 원하는대로 붙어서 그려짐
행성과 위성이 별을 기준으로 반지름, 초기 각도를 정해 각도를 변화시키며 공전하도록 구현함
실제 천체처럼 궤도 반지름이 클수록 공전 속도가 느려지도록 지수함수(baseSpeed * decay^idx 등)로 속도를 점점 감소시키게 했음
적절한 지수값/감쇠율 설정이 어렵거나 공전 반지름·속도 계산이 꼬이면 너무 빠르거나, 너무 느리거나, 불규칙하게 보일 수 있음
감쇠율이나 지수 계산식 튜닝이 관건
예: speed = baseSpeed * Math.pow(decay, orbitIndex)
decay 값(0.7~0.9 등)과 baseSpeed를 적절히 조절해 자연스럽게
혹은, 물리 공식을 더 가까이 반영할 경우
(ex. 케플러의 법칙 등)
speed ∝ 1 / sqrt(radius) 같은 공식 활용도 가능(추후 반영 예정)
별에 속한 모든 행성/위성이 동시에 정지/재생돼야 하는데 pause/play 상태를 각 노드(행성/위성) 개별로 관리하여 한 곳만 멈추거나, 전체가 동기화 안 되는 현상 발생
pause/play 상태를 별(star) 기준 root 단위로만 관리
ex) pausedRootIds Set을 만들어 “별 ID”만 넣음
isPaused(planetId) → root 별의 pause 여부를 참조
이렇게 하면, 별/행성/위성 전체가 같은 root 별 아래면 언제나 동기화됨
확대/축소(viewBox), 화면 이동 등 SVG 좌표계 환경에서 마우스 이동 속도와 별 위치가 맞지 않음
별이 따라오지 못하거나, 버벅거림
스크린 좌표 → SVG 좌표 변환을 정확히 적용
getBoundingClientRect + svg.viewBox.baseVal 조합으로 항상 마우스가 별의 중심에 정확히 오도록 변환
이러면 마우스 이동 속도와 별 위치가 1:1로 매칭되어
완전히 부드럽게 따라옴