Skip to content

Task #850: rhwp-studio 답안지 성명 칸 입력 회귀 정정#865

Open
postmelee wants to merge 4 commits into
edwardkim:develfrom
postmelee:codex/task850-answer-sheet-input-perf
Open

Task #850: rhwp-studio 답안지 성명 칸 입력 회귀 정정#865
postmelee wants to merge 4 commits into
edwardkim:develfrom
postmelee:codex/task850-answer-sheet-input-perf

Conversation

@postmelee
Copy link
Copy Markdown
Contributor

@postmelee postmelee commented May 13, 2026

개요

Task #850 / Issue #850 대응입니다.

rhwp-studio에서 samples/exam_social.hwp, samples/exam_science.hwp의 답안지 상단 성명 칸에 입력할 때 다음 오류로 입력이 막히던 회귀를 수정합니다.

Uncaught 렌더링 오류: 컨트롤 인덱스 0 범위 초과

추가로, 중첩 셀 입력 경로가 정상화된 뒤 드러난 입력 지연도 함께 줄였습니다. 성능 보완은 원래 오류 수정의 필수 조건이 아니라, 수정 후 입력 루프에서 매번 큰 페이지 레이어 JSON을 왕복하던 비용을 제거하기 위한 별도 보완입니다.

회귀 지점

이 문제는 v0.7.10에서는 사용자 입력이 오류 없이 진행되었고, v0.7.11부터 재현됩니다.

회귀를 만든 직접적인 변경은 #717 Task #717: Fix table cell whitespace hit test (ef67efa1)입니다. 이 변경은 빈 공간 클릭 시 더 작은 셀 bbox를 우선 선택하도록 hit-test 정밀도를 높였습니다. 그 결과 v0.7.11에서는 성명 칸의 실제 중첩 셀까지 도달하게 되었지만, 반환되는 편집 경로가 문서 루트 기준 경로가 아니라 중첩 테이블 내부의 로컬 경로로 잘려 나갔습니다.

v0.7.10에서 동작했던 이유

v0.7.10이 정상처럼 보였던 이유는 올바른 중첩 셀을 정확히 찾았기 때문이 아닙니다. 동일 좌표에서 v0.7.10은 실제 성명 칸이 아닌 바깥쪽/이전 컨텍스트에 가까운 유효한 경로를 반환했습니다.

예: samples/exam_social.hwp page 0, 성명 칸 좌표

{
  "parentParaIndex": 0,
  "controlIndex": 1,
  "cellPath": [
    { "controlIndex": 1, "cellIndex": 0, "cellParaIndex": 0 }
  ],
  "cursorRect": { "x": 486.8, "y": 1393.7 }
}

이 경로는 시각적으로 성명 칸을 정확히 가리키지 않지만, parentParaIndex=0, controlIndex=1이 문서 루트에서 유효했기 때문에 insertTextInCell이 실패하지 않았습니다. 즉 v0.7.10은 오류가 없었던 것이고, 정확한 중첩 셀 편집 경로가 보장되었던 것은 아닙니다.

v0.7.11에서 실패한 이유

v0.7.11은 hit-test가 더 정확해져 성명 칸 근처의 실제 중첩 셀을 선택했지만, 반환 경로가 다음처럼 로컬 기준으로 잘렸습니다.

{
  "parentParaIndex": 3,
  "controlIndex": 0,
  "cellPath": [
    { "controlIndex": 0, "cellIndex": 1, "cellParaIndex": 0 }
  ],
  "cursorRect": { "x": 212.7, "y": 211.8 }
}

여기서 parentParaIndex=3, controlIndex=0은 중첩 테이블 내부에서는 의미가 있지만, Studio의 삽입 API는 문서 루트 기준 parentParaIndex/controlIndex/cellPath를 기대합니다. 그래서 Studio가 이 값을 루트 문단의 컨트롤 인덱스로 해석하면서 컨트롤 인덱스 0 범위 초과가 발생했습니다.

이번 수정 후 exam_social.hwp의 성명 칸은 루트 기준 전체 경로를 반환합니다.

{
  "parentParaIndex": 0,
  "controlIndex": 4,
  "cellPath": [
    { "controlIndex": 4, "cellIndex": 0, "cellParaIndex": 3 },
    { "controlIndex": 0, "cellIndex": 1, "cellParaIndex": 0 }
  ]
}

exam_science.hwp도 같은 구조이며, 바깥쪽 컨트롤 인덱스만 문서에 맞게 6으로 달라집니다.

수정 방향

  1. 중첩 표 hit-test 결과를 문서 루트 기준 전체 cellPath로 보존합니다.
  2. Studio의 커서 사각형 계산도 동일한 중첩 경로를 사용하도록 맞춥니다.
  3. 텍스트 입력 중 반복 호출되는 렌더 경로는 캐시된 페이지 트리를 사용하도록 바꿉니다.
  4. Studio가 페이지 오버레이 이미지 확인만 필요할 때 전체 PageLayerTree JSON을 요청하지 않도록 getPageOverlayImages를 추가합니다.

getPageOverlayImages가 필요한 이유

getPageOverlayImages는 원래 입력 오류를 고치기 위한 필수 수정은 아닙니다. 원래 오류의 필수 수정은 루트 기준 중첩 셀 경로 보존입니다.

다만 이 수정으로 실제 중첩 셀 입력 경로가 활성화되자, Studio 입력 루프의 기존 렌더 비용이 체감될 만큼 커졌습니다. 기존 Studio는 오버레이 이미지 목록과 이미지 개수만 필요해도 매 렌더마다 전체 getPageLayerTree()를 호출했습니다. exam_social.hwp page 0 기준 전체 레이어 JSON은 약 1.4MB였고, Node/WASM 측정에서 직렬화 비용이 약 16.81ms였습니다.

getPageOverlayImages는 기존 getPageLayerTree 계약을 바꾸지 않고, Studio가 실제로 필요한 BehindText / InFrontOfText 이미지와 imageCount만 받도록 추가한 좁은 API입니다. 따라서 기존 전체 레이어 트리 소비자는 그대로 유지되고, 입력 중 불필요한 대형 JSON 직렬화/파싱만 피할 수 있습니다.

검증

실행한 검증:

cargo test --test issue_850_answer_sheet_name_hit_test -- --nocapture
cargo test --test issue_717_table_cell_hit_test -- --nocapture
cargo test --lib test_task105_nested_table_path_api -- --nocapture
cd rhwp-studio && npm run build
docker-compose run --rm wasm
cargo test

검증 결과:

  • issue_850_answer_sheet_name_hit_test: 3 passed
  • issue_717_table_cell_hit_test: 3 passed
  • test_task105_nested_table_path_api: 1 passed
  • rhwp-studio build 통과
  • WASM 빌드 통과
  • 전체 cargo test 통과
  • 로컬 웹서버에서 exam_social.hwp 성명 칸 입력 정상 동작 확인

Closes #850

@postmelee postmelee changed the title Fix answer-sheet name input regression in rhwp-studio Task #850: rhwp-studio 답안지 성명 칸 입력 회귀 정정 May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant