개발 취재 노트

격자 크기가 기록 안 된 좌표 데이터에서 원래 스냅 간격을 찾아내는 법

무용 연습 영상 위에 격자를 띄우고 시점마다 무용수 위치를 찍어 대형(포메이션)·동선을 편집하는 웹 앱(formation)에서, 댄서 12명·83개 키프레임짜리 안무 데이터를 격자에 맞춰 정리하려 했다. 그런데 좌표 파일에는 애초에 어떤 격자 크기로 찍었는지 기록이 없었다 — 저장 형식이 숫자 배열뿐인 구버전이라 격자 설정값 자체가 사라져 있었다.

결론

좌표가 격자에 "대략" 맞는데 완전히는 아니고, 그 격자 크기 자체가 파일에 안 남아 있다면 — 후보 격자 크기들을 하나씩 대보면서 각 점이 격자선에 얼마나 가까운지(적합률) 를 비교해 역산할 수 있다. 이때 "온전한 칸" 격자만 보지 말고 반 칸(half-cell, 선과 칸 중앙까지 포함하는 격자) 도 같이 검증해야 한다. 이 사건에서는 전체 칸 격자 적합률이 55%였지만 반 칸 격자는 91%였다 — 범위를 반으로 좁힌 격자가 실제 정답이었다 .

오차·적합률 계산법: 좌표를 후보 격자 간격(step)으로 나눈 나머지를 구해, 가장 가까운 격자선까지의
거리로 바꾼다(나머지가 step/2보다 크면 step에서 뺀 값을 쓴다). 이 거리를 step 대비 비율로 정규화한
게 "오차"(0~50%)다. 오차가 허용오차(예: step의 10% 이내) 안에 드는 점의 비율이 "적합률"이다 —
허용오차를 좁게 잡을수록 맞는 후보와 틀린 후보의 적합률 격차가 커져 구분이 쉬워진다.

격자 정의: cell = gridSize × (w/800)
화면 gridSize=50 기준 정규화 간격: 가로 0.0625 / 세로 0.1111 [S:dae2585f#24][S:dae2585f#25]

1차 실측(전체 칸 격자): x 중앙값 4% · y 중앙값 7% 벗어남 → 격자 기반은 맞는데 딱 맞진 않음 [S:dae2585f#27]
2차 실측(반 칸 격자): 91% 적합 (전체 칸은 55%) → 반 칸이 실제 격자 [S:dae2585f#28]
gridSize 20~100 전체 스캔 → stepX=0.03125, stepY=0.05556 (gridSize 50의 반 칸)에서 x 90%·y 91% 최적 [S:dae2585f#29][S:dae2585f#30]

상황

formation은 리더 한 명이 영상을 재생하면 나머지가 각자 폰으로 접속해 같은 지점의 영상과 대형을 함께 보는 라이브 기능도 갖고 있지만, 이 사건은 그 전 단계 — 에디터에서 찍은 좌표를 정리하는 작업에서 나왔다. 좌표는 x, y 모두 0~1로 정규화해 저장하고 , 격자는 화면 픽셀 기준 cell = gridSize × (w/800)으로 정의된다 . 영상 해상도가 1920×1080(16:9)이라, 화면에서 정사각형인 격자 칸도 정규화 좌표계에서는 가로 0.0625·세로 0.1111로 간격이 다르다 .

좌표 지터 제거·정렬 스냅 작업을 먼저 하던 중, "격자 사이즈도 확인돼? 격자에 맞춰서 배치를 하려고 하는데 지금은 격자에 맞춰지지 않고 좀 비뚤어"라는 요청이 나오면서 격자 조사가 시작됐다 .

증상

격자 간격을 계산해 좌표를 실측해보니, 완전히 벗어나 있지는 않았다(x 중앙값 4%, y 중앙값 7% 오차) — 격자 기반 배치인 건 맞는데, 그대로 전체 칸에 스냅하면 틀렸다. 일부 점들의 분포가 격자 중간(half-cell) 쪽에 몰려 있었다 . 전체 칸으로 밀어붙이면 애초에 중간에 찍으려던 의도된 위치가 뭉개질 상황이었다.

원인

해결

가진 건 "격자 기반인 것 같다"는 심증과, 격자 크기를 모르는 좌표 숫자들뿐이었다. 그래서 후보 격자 크기를 넓게 스캔하며 통계적으로 역산했다.

  1. 전체 칸 격자로 먼저 실측한다. 각 좌표가 가장 가까운 격자선에서 얼마나 떨어져 있는지 오차를(위 계산법대로) 구해, 중앙값과 분포를 본다. 이 사건에서는 x 4%·y 7%로 "격자 기반은 맞다"는 신호는 나왔지만 완전히 맞진 않았다 .
  2. 오차 분포에서 반 칸 편향을 의심한다. 오차 값들이 0 근처가 아니라 칸의 중간(half-cell) 근처에 몰려 있으면, 격자 크기 자체를 반으로 잡고 다시 재야 한다. 재보니 반 칸 격자 적합률이 91%로 전체 칸(55%)을 크게 앞섰다 .
  3. 격자 크기 자체도 모른다면 범위를 정해 전부 스캔한다. 이 사건에서는 에디터 슬라이더 범위가 20~100이라는 걸 코드에서 확인했으므로, 그 전체 구간을 훑어 각 gridSize(및 그 반 칸)에 대해 위 계산법으로 x·y 적합률을 계산했다. stepX=0.03125, stepY=0.05556(= gridSize 50의 반 칸 = gridSize 25 격자)에서 x 90%·y 91%로 가장 잘 맞았다 .
  4. 역산한 격자로 모든 점을 가장 가까운 격자점에 스냅하고, 같은 칸에 두 점이 겹치면 가까운 빈 칸으로 옮긴다. 1034개 좌표 전부 격자점에 안착했고, 무대의 3%를 넘게 움직인 건 7개뿐(최대 3.6%), 충돌 6건은 빈 칸으로 정리됐다 .
  5. 뒤이어 "더 촘촘히 놓아보자"는 요청으로 격자를 gridSize 50→25로 좁혀(스냅 간격을 다시 절반으로) 재적용하니 최대 이동이 3.6%→1.7%로 줄고 3% 초과 이동은 0건이 됐다 . 같은 방식을 다른 안무 파일(927개 좌표)에도 적용했을 때도 같은 격자가 x 98%·y 93%로 들어맞아, 방법이 특정 파일 하나의 우연이 아님을 확인했다 .

핵심은 "격자에 맞는 것 같다"는 느낌을 적합률이라는 숫자로 바꾸고, 전체 칸뿐 아니라 그 배수·절반까지 후보에 넣어 스캔한 것이다. 격자 크기를 안다고 가정한 채 스냅만 돌리면, 이 사건처럼 틀린 칸에 억지로 밀어넣어 의도된 배치를 뭉개게 된다.

덤 — 구현 보고와 실제 확인은 별개다

이 작업 내내 "완료했습니다", "1034개 점 전부 격자점에 정확히 안착" 같은 보고가 단계마다 나왔지만 , 그 결과물(비교 뷰어)을 실제로 열어 격자 스냅이 맞는지 시각적으로 확인하는 명시적인 동의는 이 세션 안에는 없다. 원본 파일에 덮어쓸지 묻는 질문()에도 답 없이 세션이 다른 주제(모바일 네트워크 구성)로 넘어갔다 . 통계적으로 맞는 격자를 찾아내는 것과, 그 결과가 실제로 쓸 만한지 눈으로 보고 받는 확인은 별개의 단계라는 뜻이다.

취재 후기 — 격자가 반 칸씩 밀려 있던 이유

격자 스냅 작업 로그를 띄워 놓고 넷이 모였다.

박도은
격자에 맞춰서 댄서 위치를 정리해달라는 요청이었는데, 막상 돌려보니 숫자가 격자선에 딱 안 떨어져. 격자 크기부터 모르겠어.
정바다
파일 자체에 안 적혀 있다고? 그럼 그냥 화면 슬라이더 기본값 쓰면 되지 않아? 기본이 50이잖아.
정하늘
그 기본값이 실제 편집할 때 쓰던 값이라는 보장이 없잖아. 먼저 그 격자로 좌표가 얼마나 잘 맞는지 재보고 얘기하자.
박도은
쟀어. x는 중앙값 4%, y는 7% 벗어나 있어. 격자 기반인 건 맞는 것 같은데 딱 맞지는 않아 .
정바다
그거면 됐네, 그냥 그 오차만큼 다 격자선으로 당기면 되는 거 아니야?
박도현
잠깐. 오차가 다 0 근처로 몰려야 "그냥 살짝 틀어진 거"지. 분포가 어디 몰려 있는데?
박도은
그게 이상한 부분이야. 몰려 있는 지점이 칸의 중간(half-cell), 선도 아니고 칸 중심 근처야 .
정하늘
그럼 격자 크기를 통째로 반으로 잡고 다시 재보자. 그게 맞으면 전체 칸 격자가 아니라 반 칸 격자였다는 뜻이야.
박도은
반 칸으로 재니까 91%가 들어맞아. 전체 칸은 55%였잖아, 완전히 역전이야 .
정바다
와, 그럼 가정이 틀렸던 거네. 근데 그 반 칸이라는 숫자, gridSize 50 기준으로 반이 맞는 거야? 다른 gridSize의 반 칸일 수도 있잖아.
박도현
맞는 말이야. 편집기 슬라이더 범위가 얼만데?
박도은
코드 보니 20부터 100까지야([formation7.html:120]). 그리고 드래그할 때 격자에 달라붙는 기능이 원래 없어 — 그래서 눈대중으로 찍어서 오차가 난 거고 .
정하늘
그러면 느낌으로 50의 반이라고 단정하지 말고, 20부터 100까지 전부 스캔해서 각각 적합률을 매겨봐야 해.
박도은
돌렸어. stepX=0.03125, stepY=0.05556에서 x 90%·y 91%로 제일 잘 맞아. 계산해보니 이게 딱 gridSize 50의 반 칸, 즉 gridSize 25 격자야 .
정바다
그러니까 안무는 화면 격자 50칸의 선과 칸 중앙 둘 다에 놓도록 짜여 있었고, 에디터가 안 당겨주니까 손으로 찍다 살짝 틀어진 거네. 처음에 내가 그냥 기본값 밀어붙이자고 한 거, 그대로 했으면 중간에 찍힌 점들 다 뭉개질 뻔했다.
박도현
그래서 중요한 건 "격자에 맞는 것 같다"는 느낌이 아니라 적합률 숫자로 후보를 다 대보는 거였어. 전체 칸만 보고 끝냈으면 틀린 칸에 억지로 꽂았을 거야.
정하늘
역산한 격자로 1034개 다 스냅하고, 겹치는 건 빈 칸으로 밀었어. 3% 넘게 움직인 게 7개뿐이야 .
박도은
이거 다른 안무 파일에도 같은 격자로 맞는지 봐야 하지 않아? 우연히 이 파일만 그런 거면 의미 없잖아.
정바다
다른 파일(927개 좌표)도 돌렸는데 x 98%·y 93%로 똑같이 들어맞아. 우연이 아니라 그 격자가 맞다는 거지 .
박도현
좋아. 그런데 이 결과, 실제로 비교 화면으로 눈으로 보고 "맞다" 확인한 사람은 있어?
박도은
...아니, "완료했습니다"라고 보고는 계속 했는데 눈으로 보고 승인하는 말은 세션에 없어. 원본에 덮어쓸지 물어본 것도 답을 못 받고 다른 얘기로 넘어갔어 .
정하늘
숫자가 맞는다고 끝난 게 아니라는 거네. 격자 크기를 통계로 역산하는 것과, 그 결과를 실제로 써도 되는지 확인하는 건 완전히 다른 단계야.