namoman.com

디지털 정원과 실험실

[태그:] #React19

  • 숫자를 만지는 감각, React로 구현한 인터랙티브 초등 수학 교구 개발기 (Math Blocks)

    수학은 추상적인 학문입니다. 기호와 숫자로 가득 찬 교과서를 처음 마주하는 아이들에게 “3 × 4 = 12″라는 식은 영문 모를 암호와 같을지도 모릅니다. 머릿속으로 공간과 수량을 완벽하게 시각화하기 전까지, 아이들에게는 직접 만지고 부수고 쌓아 올릴 수 있는 ‘실물 교구’가 필요합니다.

    Math Blocks(math_viz) 프로젝트는 바로 이 지점에서 출발했습니다.

    (http://namoman.com/math/)화면 속의 숫자들이 직관적인 파스텔톤 블록이 되고, 아이들이 직접 수치를 조절하며 사칙연산의 인과관계를 눈으로 확인하도록 도와주는 웹 애플리케이션입니다.

    로컬 개발 환경에서부터 Vite와 React 19, Tailwind CSS v4를 활용해 빌드하고 실 서비스에 배포하기까지, 웹 기술로 수학적 직관을 시각화하기 위해 고민했던 개발 여정과 주요 아키텍처를 공유하고자 합니다.

    웹 브라우저에서 구동되는 Math Blocks 인터랙티브 수학 시각화 애플리케이션 메인 대시보드 화면
    Math Blocks 화면

    🎨 감성을 담은 프리미엄 교구 디자인

    초등 교육용 플랫폼이라고 해서 단순히 원색 위주의 유치한 디자인을 답습하고 싶지는 않았습니다. 오히려 실제 원목으로 만든 교구의 느낌을 전달하고 싶었죠.

    단색의 빨강, 파랑 대신 톤다운된 우드 계열과 부드러운 오렌지, 파스텔톤 컬러 팔레트를 메인으로 채택했습니다.

    컴포넌트의 모서리는 부드럽게 깎아내고, 마우스 커서가 올라갈 때마다 미세하게 반응하는 마이크로 인터랙션을 가미하여 아이들이 “자꾸 만지고 싶은” 감성적인 인터페이스를 완성했습니다.

    단순히 예쁜 디자인에만 치중한 것은 아닙니다. 사용자가 연산 값을 크게 높여 블록의 개수가 수백 개로 늘어날 경우, 브라우저 스크린 영역을 벗어나거나 화면이 깨지는 현상이 발생할 수 있습니다.

    이를 해결하기 위해 블록 개수에 따라 크기를 유연하게 줄여주는 적응형 스케일링(Adaptive Scaling) 알고리즘을 구현했습니다. 전체 개수가 늘어날수록 개별 블록의 가로세로 adaptiveSize가 동적으로 축소되어 어떤 해상도에서도 전체 연산 구조가 한눈에 들어옵니다.

    블록의 개수가 늘어남에 따라 크기가 동적으로 조절되어 전체 구조를 한눈에 보여주는 적응형 스케일링 렌더링 화면
    적응형 스케일링 렌더링 화면

    🛠️ 사칙연산 시각화를 위한 4가지 상태 머신 접근법

    가장 까다로웠던 작업은 ‘연산의 과정’을 브라우저에 부드럽게 녹여내는 일이었습니다. 정적인 결과만 보여주는 것은 진정한 의미의 교구가 아니기 때문입니다.

    1. 단계별 애니메이션으로 깨닫는 뺄셈

    뺄셈 모드에서는 단순히 블록이 사라지는 게 아니라 단계적 연출이 핵심입니다. 전용 상태 머신(minusStep)을 설계하여 다음과 같은 트리거 흐름을 구축했습니다.

    • 1단계: 전체 블록 렌더링
    • 2단계: 사라질 블록들을 회색으로 반전시키며 펄스(Pulse) 애니메이션 효과 부여
    • 3단계: 블록을 완전히 제거하며 그 자리에 X 마크를 오버레이

    이 과정을 거치면서 아이들은 “아, 이만큼이 묶여서 날아가는구나”를 자연스럽게 인지하게 됩니다.

    2. 2D 평면 그리드에서 3D 입체 공간으로 (곱셈)

    곱셈은 면적과 공간의 개념입니다. 높이(층수) 데이터가 1일 때는 인디고 색상의 2D 격자 그리드로 가로 × 세로 개념을 명확히 전달합니다.

    여기서 한 걸음 더 나아가 층수를 2층 이상으로 올리면, 화면은 동적으로 Isometric 3D(등각 투영) 나무 블록 모드로 변환됩니다. Three.js 같은 무거운 3D 라이브러리를 쓰는 대신, 순수 CSS의 transform 속성(skew, rotate)과 zIndex 결합 수학을 활용하여 탑(Top), 레프트(Left), 라이트(Right) 3면의 좌표를 계산해 쌓아 올렸습니다. 브라우저 리소스를 최소화하면서도 완벽한 입체 입체감을 선사합니다.

    Isometric projection cube with marked angles. Vector illustration. 출처: Getty Images
    Isometric projection cube with marked angles. Vector illustration. 출처: Getty Images

    3. 동물 아바타 접시에 나누어 담기 (나눗셈)

    나눗셈은 ‘분배’와 ‘나머지’의 개념입니다. 귀여운 동물 아바타가 그려진 접시 오브젝트들을 동적으로 생성하고, 설정된 블록들이 분배 프로세스 상태에 따라 각 접시 위로 균등하게 쪼개져 들어가는 애니메이션을 구현했습니다. 나누어 떨어지지 않고 남은 블록들은 상단의 별도 ‘나머지(Remainder)’ 영역으로 격리되어 몫...나머지 공식을 시각적으로 완벽히 완성합니다.

    🔒 대규모 연산 보호 기법과 안정성 확보

    인터랙티브 웹 앱을 개발할 때 흔히 놓치기 쉬운 실수가 바로 예외적인 대형 입력값 처리입니다. 아이들이 호기심으로 수치 슬라이더를 마구 움직여 블록 개수가 1,000개를 초과하는 순간, 수천 개의 단일 DOM 엘리먼트가 한 번에 그리팅되면서 브라우저가 일시적으로 얼어붙는(Freezing) 크래시 현상이 발생할 수 있습니다.

    사용자 경험을 해치는 치명적인 병목을 막기 위해 대규모 연산 보호 기법 (Large Count Safeguard)을 레이아웃 단계에 심었습니다.

    블록의 합산 수치가 특정 임계점을 넘어가면 개별 블록의 독립 렌더링 루프를 즉시 중단합니다. 대신, 전체 부피를 요약하여 보여주는 단일의 시각적 볼륨 박스(Box) 레이아웃 형태로 뷰를 다이내믹하게 스위칭하여 성능 효율성과 가시성을 동시에 잡았습니다.

    또한, 메인 대시보드 역할을 하는 App.jsx를 중심으로 연산 제어 컴포넌트인 StepperInput.jsx, 2D 단일 객체인 ColorBlock.jsx, 3D 등각 투영을 담당하는 WoodBlock.jsx로 역할을 철저히 분리했습니다. 무분별한 리렌더링을 방지하고 컴포넌트 간 단방향 데이터 흐름을 명확히 다듬었습니다.

    3D Isometric 기법으로 나무 블록이 정밀하게 입체적으로 쌓여 있는 곱셈 시각화 모드

    🖨️ 디지털 교구에서 종이 시험지 인쇄까지 (@media print)

    이 플랫폼의 또 다른 강력한 축은 교사와 학부모를 위한 시험지 생성기(Exam Creator)입니다. 화면으로 공부한 내용을 종이 평가로 연결할 수 있도록 설계했습니다.

    초등 교육 과정의 특성에 맞게 설계 시 세심한 예외 처리 알고리즘을 다수 반영했습니다. 예를 들어, 뺄셈 문제를 무작위로 추출할 때 음수 결과가 나와 아이들이 혼란스럽지 않도록 언제나 왼쪽 피연산자가 더 크게 오도록 강제하는 preventNegative 알고리즘을 빌드했습니다. 나눗셈 또한 단순히 무작위 나누기가 아니라 딱 떨어지는 유형(divide_exact)과 나머지가 남는 유형(divide_remainder)을 정밀하게 분기하여 출제할 수 있도록 옵션을 세분화했습니다.

    이렇게 생성된 20문항의 문제지와 정답지는 인쇄 버튼을 누르는 순간 CSS 미디어 쿼리(@media print)의 마법을 마주하게 됩니다. 화면에 가득했던 슬라이더, 사이드바, 테마 변경 버튼 등의 네비게이션 UI는 흔적도 없이 사라집니다.

    실제 종이 규격(A4: 210mm × 297mm)에 맞춘 깔끔한 여백과 강제 페이지 분할(page-break-after: always) 마진이 정밀하게 작동하여, 시중에서 판매하는 초등 수학 문제지 형태의 PDF 및 오프라인 인쇄물을 즉시 얻을 수 있습니다.

    🧪 Vitest를 활용한 예외 시나리오 검증

    모던 프론트엔드 생태계에 발맞춰, 본 프로젝트는 Vitest와 React Testing Library를 활용한 빈틈없는 테스트 자동화 환경을 구축했습니다. 눈에 보이는 그래픽 요소가 많은 만큼 코드의 안정성이 최우선이기 때문입니다.

    • WoodBlock.test.jsx: 3D 등각 투영을 위해 계산된 가상 스타일 좌표와 zIndex 연산값이 CSS transform 주입 시 소수점 오차 없이 정확히 도출되는지 단위 검증합니다.
    • StepperInput.test.jsx: 범위 슬라이더와 텍스트 인풋이 연동될 때 설정된 최솟값(Min)과 최댓(Max) 범위를 벗어나는 임의의 값 입력 시 자동 클램핑(Clamping) 처리가 유기적으로 작동하는지 확인합니다.
    • App.test.jsx: 간혹 발생할 수 있는 0으로 나누기(divide-by-zero) 같은 치명적인 수학적 예외 입력 시나리오에서도 어플리케이션이 크래시되지 않고 우아하게 에러 가드를 작동시키는지 통합 테스트를 수행합니다.

    🚀 나가며: 수학을 만지는 즐거움

    Math Blocks 프로젝트는 단순한 코딩 결과물을 넘어, 기술이 교육의 직관성을 어떻게 높여줄 수 있는지 치열하게 실험해 본 무대였습니다. 데이터 구조가 복잡해질수록 렌더링 최적화와 예외 처리가 얼마나 중요한지 다시금 깨닫는 계기가 되기도 했습니다.

    추상적인 공식에 갇혀 수학을 지루하게 느끼던 아이들이 웹 브라우저 안에서 큐브 블록을 쌓고 허물며 수의 감각을 즐겁게 익혀나가기를 바랍니다. 다음 개발기에서는 본 프로젝트의 최적화 단계에서 고민했던 메모이제이션 기법들에 대해 조금 더 깊게 다루어 보겠습니다.