블록월드: 자바스크립트를 눈으로 보며 배우는 코딩 학습 도구

코드를 쳐도 뭐가 되는 건지 모르겠다
자바스크립트를 처음 배울 때 가장 답답한 순간이 있습니다.
let x = 10;을 치면 콘솔에 undefined만 뜹니다. 변수가 만들어졌다는데, 어디에? 뭐가 달라졌는데?
for (let i = 0; i < 5; i++) { ... }를 치면 결과가 쭉 나오긴 하는데, 반복문이 "돌아가는" 느낌은 안 옵니다. 코드와 결과 사이에 간극이 있는 겁니다.
이걸 메우고 싶었습니다.
코드를 치면 화면에서 뭔가가 바로 움직이는 환경. 변수에 값을 넣으면 블록이 생기고, 반복문을 돌리면 블록이 줄줄이 나타나고, 조건문을 쓰면 블록 색이 바뀌는. 코드의 결과가 시각적으로 바로 보이는 학습 도구를 만들었습니다.
어떤 식으로 동작하나
화면 구성은 이렇습니다.
┌──────────┬──────────────────┬────────────────────┐ │ │ │ │ │ 사이드바 │ 미션 설명 │ 블록 캔버스 │ │ (스테이지 │ (마크다운) │ (Canvas 2D) │ │ 목록) │ │ │ │ ├──────────────────┤ │ │ │ ├────────────────────┤ │ │ 코드 에디터 │ 콘솔 │ │ │ │ │ └──────────┴──────────────────┴────────────────────┘
왼쪽에서 미션을 고르면 설명이 뜨고, 가운데 에디터에 코드를 쓴 뒤 실행 버튼(또는 Ctrl+Enter)을 누르면 오른쪽 캔버스에 블록이 나타납니다. 콘솔에는 실행 로그나 에러가 찍힙니다.
코드를 치면 결과가 바로 보여야 합니다. 그게 핵심입니다.
let b = createBlock() // → 캔버스에 블록 1개 등장 setColor("red", b) // → 빨간색으로 변함 setSize(4, b) // → 크기가 커짐 setText("안녕!", b) // → 블록 위에 글자 표시
이걸 한 줄 한 줄 실행하면서 "아, setColor가 색을 바꾸는 거구나"를 보면서 이해할 수 있습니다.
12개 스테이지, 변수부터 종합 프로젝트까지
커리큘럼은 자바스크립트 기초 문법 전체를 커버합니다.
┌─────────────────────────────────────────────────────┐ │ 학습 로드맵 │ ├──────┬──────────────┬───────────────────────────────┤ │ 순서 │ 주제 │ 배우는 것 │ ├──────┼──────────────┼───────────────────────────────┤ │ 1 │ 변수와 자료형 │ let, 문자열, 숫자 │ │ 2 │ 연산자 │ +, -, *, /, %, 비교, 논리 │ │ 3 │ 조건문 │ if, else if, else │ │ 4 │ 반복문 │ for, while │ │ 5 │ 배열 │ [], push, length, 인덱스 │ │ 6 │ 함수 │ function, 매개변수, return │ │ 7 │ 객체 │ {}, 속성, 메서드 │ │ 8 │ 이벤트 │ addEventListener, 클릭 이벤트 │ │ 9 │ 애니메이션 │ move, rotate, jump, 타이밍 │ │ 10 │ 물리 │ 중력, 힘, 충돌 │ │ 11 │ 알고리즘 │ 정렬, 탐색 │ │ 12 │ 종합 프로젝트 │ 배운 것 전부 활용 │ └──────┴──────────────┴───────────────────────────────┘
각 스테이지는 가이드 → 챌린지 → 샌드박스 3단계로 구성됩니다.
가이드는 설명을 읽고 따라 치는 단계입니다. 코드가 거의 다 주어져 있고, 살짝 수정만 하면 됩니다. "이거 누르면 이렇게 된다"를 체험하는 거죠.
챌린지는 혼자 힘으로 풀어보는 문제입니다. 힌트가 3단계로 준비되어 있어서, 막히면 한 단계씩 열어볼 수 있습니다. 힌트를 적게 쓸수록 별을 많이 받습니다.
샌드박스는 제약 없이 자유롭게 코드를 쓰는 공간입니다. 그 스테이지에서 배운 API를 전부 쓸 수 있어서, "이거 이렇게 하면 어떻게 되지?" 하는 실험을 마음껏 할 수 있습니다.
스테이지별 API가 제한되는 이유
스테이지마다 쓸 수 있는 API가 다릅니다. 의도적으로 그렇게 했습니다.
Stage 1 (변수): createBlock, setColor, setSize, setText, setPosition Stage 4 (반복문): 위 + createBlocks Stage 8 (이벤트): 위 + findBlock Stage 10 (물리): 위 + enableGravity, setWeight, applyForce
Stage 1에서는 enableGravity를 호출할 수 없습니다. 변수도 모르는 상태에서 중력까지 나오면 혼란스러우니까요.
반대로 Stage 10에 가면 이전 스테이지의 API는 전부 사용 가능합니다. 자연스럽게 복습이 됩니다.
이 필터링은 코드 실행 시점에 적용됩니다. 사용 불가능한 API를 호출하면 "이 스테이지에서는 사용할 수 없는 명령어입니다" 에러가 뜹니다.
안쪽은 어떻게 돌아가나
전체 구조를 한 장으로 그리면 이렇습니다.
학습자가 코드 작성 │ ▼ ┌─────────────────┐ ┌──────────────────┐ │ CodeSandbox │────▶│ BlockWorldAPI │ │ (new Function) │ │ (API 래퍼) │ │ │ │ │ │ · 무한루프 방지 │ │ · createBlock() │ │ · 전역 객체 차단 │ │ · setColor() │ │ · 시간 제한 5초 │ │ · findBlock() │ │ · console 가로채기│ │ · ...18개 API │ └─────────────────┘ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ BlockEngine │ │ (메인 엔진) │ │ │ │ · 블록 관리 │ │ · 이벤트 시스템 │ │ · 물리 시뮬레이션 │ │ · 애니메이션 │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Canvas 2D │ │ Renderer │ │ │ │ · 60fps 렌더링 │ │ · HiDPI 지원 │ │ · 바닥선, 텍스트 │ └──────────────────┘
크게 4개 레이어로 나뉩니다.
코드 실행: new Function 샌드박스
학습자가 쓴 코드를 new Function()으로 감싸서 실행합니다. iframe이 아니라 같은 컨텍스트에서 실행하되, 전역 객체 접근을 차단합니다.
new Function( 'createBlock', 'setColor', ..., 'console', '"use strict"; var document = undefined; var fetch = undefined; var localStorage = undefined; ... [학습자 코드]' )
document, fetch, localStorage 같은 위험한 전역 변수를 undefined로 덮어씌워서, 학습자가 실수로(또는 고의로) DOM을 건드리거나 네트워크 요청을 보내는 걸 막습니다.
무한루프도 방지합니다. for나 while 루프의 시작 부분에 카운터 체크 코드를 자동 삽입해서, 1만 번 이상 반복하면 강제 중단합니다.
블록 엔진: 상태 관리의 중심
BlockEngine은 블록의 생성/삭제/속성 변경을 관리합니다. 블록을 만들면 Map에 저장하고, 속성이 바뀔 때마다 WorldState를 갱신합니다.
이벤트 시스템도 엔진에 붙어있습니다. addEventListener로 클릭 이벤트를 등록하면, 캔버스 클릭 시 해당 좌표의 블록을 찾아서 콜백을 실행합니다.
사용자 캔버스 클릭 (x, y) │ ▼ engine.getBlockAtPoint(x, y) ← 역순 순회 (위에 있는 블록 우선) │ ▼ engine.fireEvent(blockId, "click") │ ▼ 등록된 콜백들 실행 → setColor() 등 호출 → 화면 갱신 │ ▼ 미션 자동 검증 (WorldState 기반)
미션 검증: WorldState 스냅샷
코드 실행이 끝나면 엔진에서 WorldState를 가져옵니다.
{ blocks: [...], // 전체 블록 데이터 blockCount: 7, // 블록 수 colors: ["red", "orange", ...], // 사용된 색상 목록 maxHeight: 120, // 최고 높이 totalWeight: 35, // 총 무게 }
미션마다 정의된 검증 조건과 이 상태를 비교합니다. "블록이 7개이고, 색이 모두 다르면 성공" 같은 조건이죠. 조건을 통과하면 성공 애니메이션이 나오고, 경험치와 별이 주어집니다.
이벤트 시스템: "코드로 클릭을 처리한다"는 경험
Stage 8의 이벤트는 설명으로 배우는 게 아니라, 블록을 직접 클릭해서 뭔가가 일어나는 걸 보면서 배웁니다.
let b = createBlock() setSize(4, b) setText("클릭!", b) let block = findBlock(b) block.addEventListener("click", function() { setColor("red", b) setText("클릭됨!", b) })
이 코드를 실행하면 블록이 나타나고, "이벤트가 등록되었습니다! 블록을 클릭해보세요."라는 안내가 콘솔에 뜹니다. 블록을 클릭하면 색이 바뀌고 텍스트가 바뀝니다.
웹 브라우저에서 버튼에 이벤트를 거는 것과 같은 패턴인데, 시각적 결과가 바로 보이니까 "콜백 함수"라는 개념이 훨씬 직관적으로 와닿습니다.
findBlock()이 반환하는 건 엔진 내부 Block 객체가 아니라, getter로 감싼 프록시 객체입니다. 학습자가 block.color를 읽으면 항상 최신 값이 나오고, addEventListener 외의 직접 수정은 차단됩니다.
사용법 정리
- https://www.fullstackfamily.com/jsgame 접속
- 왼쪽 사이드바에서 스테이지 선택 (Stage 1부터 순서대로 권장)
- 미션 설명을 읽고, 에디터에 코드 작성
- 실행 버튼 또는 Ctrl+Enter로 실행
- 캔버스에서 결과 확인
- 검증 통과 시 자동으로 성공 처리, 다음 미션으로 이동
각 미션에는 API 레퍼런스가 접혀 있어서, 함수 사용법을 잊었을 때 바로 찾아볼 수 있습니다. 챌린지 미션은 힌트 패널에서 단계별 힌트를 열어볼 수 있고, 힌트를 덜 쓸수록 별 3개를 받습니다.
PC(1024px 이상) 환경에서 사용해 주세요. 코드 에디터와 캔버스를 동시에 봐야 하는 구조라 모바일에서는 안내 화면이 뜹니다.
어떤 효과를 기대하나
"변수가 뭔지 알겠어요." 교과서에서 "값을 저장하는 공간"이라고 배우는 것과, let b = createBlock() 해서 블록이 생기고 setColor("red", b)로 그 블록 색이 바뀌는 걸 보는 건 체감이 다릅니다.
반복문의 "반복"이 보입니다. for (let i = 0; i < 5; i++) { createBlock() }을 치면 블록이 하나씩 나타나면서 5개가 됩니다. i가 0부터 4까지 변하는 동안 블록이 만들어지는 과정이 그대로 보이는 거죠.
에러가 무섭지 않습니다. 콘솔에 한글로 에러 메시지가 뜹니다. setColor("색상", 블록ID): 블록ID를 넣어주세요. 예: let b = createBlock(); setColor("red", b) 같은 식으로, 뭘 고쳐야 하는지까지 알려줍니다. 빨간 영어 에러 메시지에 겁먹을 일이 없습니다.
"내가 만든 게 있다"는 느낌. 무지개 블록을 7개 만들어 놓으면 그게 내가 코드로 만든 결과물입니다. console.log()만 찍었을 때와는 성취감의 무게가 다릅니다.
개선 의견을 기다립니다
만들어서 올리긴 했는데, 초기 버전이라 부족한 부분이 있을 수 있습니다.
- "이 미션 설명이 이해가 안 돼요"
- "이 챌린지는 너무 어려워요 / 쉬워요"
- "이런 스테이지도 추가해주세요"
- "이 부분 UI가 불편해요"
어떤 의견이든 환영합니다. 커뮤니티 자유게시판( https://www.fullstackfamily.com/boards/free ) 에 남겨주시면 확인하고 개선하겠습니다.
마무리
이 도구가 하려는 건 하나입니다.
코드를 쓴다 → 뭔가 보인다 → "아, 이게 이거구나" → 다음 걸 해본다
이 루프가 빠르게 돌수록 학습이 잘 됩니다. 블록월드는 코드와 결과 사이의 거리를 줄여서 이 루프를 빠르게 돌리려는 겁니다.
자바스크립트를 막 시작했거나, 문법은 아는데 "실감"이 안 나는 분들이 한번 써봤으면 좋겠습니다.

댓글
댓글을 작성하려면 이 필요합니다.