YEON PLANNING · 직관수학 (초4)

평면도형 밀기·뒤집기·돌리기 — 4단계 문제은행

영상으로 배우고 → 직접 풀고 → 답 제출 → 해설. 위치와 방향은 바뀌어도 모양과 크기는 그대로.

✍️ 연플래닝 오리지널 — 「아하! 보이는 직관수학」 자체 제작. 모든 문제·정답 손검산 완료. 답안을 제출해야 해설·정답이 열립니다(먼저 스스로 풀어 보세요).

💬 더 필요한 게 있나요?

난이도 조절(더 쉽게 / 더 어렵게) · 다른 유형 · 특정 개념 집중 · 문항 수 등 무엇이든 적어 주세요. 선생님이 확인합니다. (공개 아님)
✏️ 손으로 풀어보세요
`; return s; } // ── 평면도형 밀기·뒤집기·돌리기 ── function move(cfg){ const {op='밀기', W=300, H=190, note='', showAfter=true} = cfg; // ㄱ 모양 (좌우·상하 구분이 뚜렷한 비대칭 도형) const base=[[0,0],[46,0],[46,14],[16,14],[16,52],[0,52]]; function draw(pts, ox, oy, fill, tag){ let d=pts.map(p=>(ox+p[0]).toFixed(1)+','+(oy+p[1]).toFixed(1)).join(' '); let t=``; t+=`${tag}`; return t; } let after=base; if(op==='밀기') after=base.map(p=>[p[0],p[1]]); else if(op==='뒤집기') after=base.map(p=>[46-p[0],p[1]]); // 좌우 뒤집기 else if(op==='위아래뒤집기') after=base.map(p=>[p[0],52-p[1]]); else if(op==='돌리기') after=base.map(p=>[52-p[1],p[0]]); // 시계 90° let s=``; s+=draw(base, 42, 45, '#cfe2f3', '처음'); s+=``; s+=``; s+=`${op}`; if(showAfter) s+=draw(after, 190, 45, '#f7d5cd', '움직인 뒤'); else s+=`?`; if(note) s+=`${note}`; s+=``; return s; } const P=[ { tier:'기초', type:'self', kind:'choice', q:'도형을 제자리에서 옮기기만 하는 것을 무엇이라고 할까요?', fig:move({op:'밀기',note:'무엇을 한 걸까요?'}), choices:['밀기','뒤집기','돌리기','없다'], answer:'밀기', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'밀기', fig:move({op:'밀기',note:'밀기'}), steps:['도형을 그대로 옮기기만 하는 것 = 밀기.','모양도 크기도 그대로예요.','위치만 바뀌고 나머지는 하나도 안 변해요!']}}, { tier:'기초', type:'self', kind:'choice', q:'도형을 거울에 비친 것처럼 바꾸는 것을 무엇이라고 할까요?', fig:move({op:'뒤집기',note:'무엇을 한 걸까요?'}), choices:['밀기','뒤집기','돌리기','없다'], answer:'뒤집기', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'뒤집기', fig:move({op:'뒤집기',note:'뒤집기'}), steps:['거울에 비친 것처럼 좌우(또는 위아래)가 바뀌는 것 = 뒤집기.','모양·크기는 그대로인데 방향이 반대가 돼요.','오른손이 거울에선 왼손으로 보이는 것과 같아요!']}}, { tier:'기초', type:'self', kind:'choice', q:'도형을 빙 돌리는 것을 무엇이라고 할까요?', fig:move({op:'돌리기',note:'무엇을 한 걸까요?'}), choices:['밀기','뒤집기','돌리기','없다'], answer:'돌리기', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'돌리기', fig:move({op:'돌리기',note:'돌리기'}), steps:['한 점을 중심으로 빙 도는 것 = 돌리기.','90°, 180° 처럼 얼마나 도는지가 중요해요.','모양·크기는 그대로, 방향만 돌아가요!']}}, { tier:'기초', type:'self', kind:'choice', q:'밀기를 하면 도형의 모양은 어떻게 될까요?', fig:move({op:'밀기',note:'무엇을 한 걸까요?'}), choices:['그대로다','커진다','작아진다','알 수 없다'], answer:'그대로다', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'그대로다', fig:move({op:'밀기',note:'그대로다'}), steps:['밀기는 위치만 옮겨요.','모양도 크기도 변하지 않아요.','옮기기만 했으니 당연하죠!']}}, { tier:'기초', type:'self', kind:'choice', q:'뒤집기를 하면 도형의 크기는 어떻게 될까요?', fig:move({op:'밀기',note:'무엇을 한 걸까요?'}), choices:['그대로다','커진다','작아진다','알 수 없다'], answer:'그대로다', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'그대로다', fig:move({op:'밀기',note:'그대로다'}), steps:['뒤집기는 방향만 반대로 바꿔요.','크기는 변하지 않아요.','거울 속 내 모습이 커지거나 작아지지 않는 것과 같아요!']}}, { tier:'기초', type:'self', kind:'choice', q:'돌리기를 하면 도형의 모양은 어떻게 될까요?', fig:move({op:'밀기',note:'무엇을 한 걸까요?'}), choices:['그대로다','커진다','작아진다','알 수 없다'], answer:'그대로다', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'그대로다', fig:move({op:'밀기',note:'그대로다'}), steps:['돌리기는 방향만 돌려요.','모양·크기는 그대로.','밀기·뒤집기·돌리기 셋 다 모양과 크기를 안 바꿔요!']}}, { tier:'기초', type:'self', kind:'choice', q:'밀기·뒤집기·돌리기 중 도형의 크기를 바꾸는 것은?', fig:move({op:'밀기',note:'무엇을 한 걸까요?'}), choices:['밀기','뒤집기','돌리기','없다'], answer:'없다', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'없다', fig:move({op:'밀기',note:'없다'}), steps:['밀기 = 위치만, 뒤집기 = 방향만, 돌리기 = 방향만.','셋 다 크기를 바꾸지 않아요.','그래서 움직이기 전과 후의 도형은 완전히 똑같은 도형이에요!']}}, { tier:'기초', type:'self', kind:'choice', q:'도형을 오른쪽으로 5칸 옮겼습니다. 무엇을 한 걸까요?', fig:move({op:'밀기',note:'무엇을 한 걸까요?'}), choices:['밀기','뒤집기','돌리기','없다'], answer:'밀기', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'밀기', fig:move({op:'밀기',note:'밀기'}), steps:['방향도 그대로, 모양도 그대로, 위치만 바뀜.','이건 밀기예요.','「몇 칸 옮기기」는 항상 밀기!']}}, { tier:'기초', type:'self', kind:'choice', q:'도형을 왼쪽·오른쪽이 바뀌게 했습니다. 무엇을 한 걸까요?', fig:move({op:'뒤집기',note:'무엇을 한 걸까요?'}), choices:['밀기','뒤집기','돌리기','없다'], answer:'뒤집기', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'뒤집기', fig:move({op:'뒤집기',note:'뒤집기'}), steps:['좌우가 바뀜 = 뒤집기.','거울에 비친 모습과 같아요.','위아래가 바뀌어도 뒤집기예요!']}}, { tier:'기초', type:'self', kind:'choice', q:'도형을 시계 방향으로 90° 움직였습니다. 무엇을 한 걸까요?', fig:move({op:'돌리기',note:'무엇을 한 걸까요?'}), choices:['밀기','뒤집기','돌리기','없다'], answer:'돌리기', hint:'도형을 어떻게 움직였는지 그림으로 떠올려 보세요.', sol:{ ans:'돌리기', fig:move({op:'돌리기',note:'돌리기'}), steps:['「몇 도」가 나오면 돌리기예요.','시계 방향 90° = 오른쪽으로 한 번 돌리기.','각도가 나오면 돌리기!']}}, { tier:'중급', type:'self', kind:'choice', q:'ㄱ 모양을 좌우로 뒤집으면 튀어나온 부분은 어느 쪽으로 갈까요?', fig:move({op:'뒤집기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['반대쪽','같은 쪽','위쪽','알 수 없다'], answer:'반대쪽', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'반대쪽', fig:move({op:'뒤집기',note:'반대쪽'}), steps:['좌우 뒤집기 = 왼쪽과 오른쪽이 바뀜.','오른쪽에 있던 게 왼쪽으로 가요.','거울을 떠올리면 쉬워요!']}}, { tier:'중급', type:'self', kind:'choice', q:'ㄱ 모양을 같은 방향으로 두 번 뒤집으면 어떻게 될까요?', fig:move({op:'뒤집기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['처음과 같다','좌우가 바뀐다','위아래가 바뀐다','알 수 없다'], answer:'처음과 같다', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'처음과 같다', fig:move({op:'뒤집기',note:'처음과 같다'}), steps:['한 번 뒤집으면 좌우가 바뀌어요.','또 뒤집으면 다시 원래대로 돌아와요.','짝수 번 뒤집으면 처음과 같아요! 홀수 번이면 뒤집힌 채고요.']}}, { tier:'중급', type:'self', kind:'choice', q:'도형을 시계 방향으로 90°씩 네 번 돌리면 어떻게 될까요?', fig:move({op:'돌리기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['처음과 같다','좌우가 바뀐다','위아래가 바뀐다','알 수 없다'], answer:'처음과 같다', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'처음과 같다', fig:move({op:'돌리기',note:'처음과 같다'}), steps:['90° × 4 = 360° = 한 바퀴.','한 바퀴 돌면 제자리로 돌아와요.','그래서 처음과 똑같아요!']}}, { tier:'중급', type:'self', kind:'choice', q:'도형을 밀어도 넓이는 어떻게 될까요?', fig:move({op:'밀기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['그대로다','커진다','작아진다','알 수 없다'], answer:'그대로다', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'그대로다', fig:move({op:'밀기',note:'그대로다'}), steps:['밀기는 위치만 옮겨요.','모양·크기가 그대로니 넓이도 그대로.','옮겨도 크기는 안 변해요!']}}, { tier:'중급', type:'self', kind:'choice', q:'도형을 돌려도 변의 길이는 어떻게 될까요?', fig:move({op:'돌리기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['그대로다','커진다','작아진다','알 수 없다'], answer:'그대로다', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'그대로다', fig:move({op:'돌리기',note:'그대로다'}), steps:['돌리기는 방향만 바꿔요.','각 변의 길이는 그대로.','돌린 도형과 처음 도형은 완전히 겹쳐져요!']}}, { tier:'중급', type:'self', kind:'choice', q:'위아래로 뒤집으면 위에 있던 부분은 어디로 갈까요?', fig:move({op:'위아래뒤집기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['아래로','위로','옆으로','그대로'], answer:'아래로', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'아래로', fig:move({op:'위아래뒤집기',note:'아래로'}), steps:['위아래 뒤집기 = 위와 아래가 바뀜.','위에 있던 게 아래로 가요.','물에 비친 모습과 같아요!']}}, { tier:'중급', type:'self', kind:'choice', q:'도형을 밀고 나서 또 밀면 결국 무엇을 한 셈일까요?', fig:move({op:'밀기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['밀기','뒤집기','돌리기','알 수 없다'], answer:'밀기', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'밀기', fig:move({op:'밀기',note:'밀기'}), steps:['밀기 + 밀기 = 결국 한 번에 더 멀리 민 것.','여전히 밀기예요.','여러 번 밀어도 밀기는 밀기!']}}, { tier:'중급', type:'self', kind:'choice', q:'동그라미를 아무리 돌려도 모양이 달라 보이지 않는 이유는?', fig:move({op:'돌리기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['어느 쪽에서 봐도 똑같아서','크기가 작아서','선이 없어서','알 수 없다'], answer:'어느 쪽에서 봐도 똑같아서', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'어느 쪽에서 봐도 똑같아서', fig:move({op:'돌리기',note:'어느 쪽에서 봐도 똑같아서'}), steps:['동그라미는 중심에서 어느 쪽으로 가도 거리가 같아요.','그래서 돌려도 똑같아 보여요.','이런 도형은 돌리기의 흔적이 안 보여요 — ㄱ 모양처럼 비대칭이어야 잘 보여요!']}}, { tier:'중급', type:'self', kind:'choice', q:'정사각형을 90° 돌리면 모양이 달라 보일까요?', fig:move({op:'돌리기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['그렇다','아니다','45°일 때만','알 수 없다'], answer:'아니다', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'아니다', fig:move({op:'돌리기',note:'아니다'}), steps:['정사각형은 네 변·네 각이 모두 같아요.','90° 돌려도 똑같아 보여요.','그래서 정사각형은 90°씩 돌려도 티가 안 나요!']}}, { tier:'중급', type:'self', kind:'choice', q:'ㄱ 모양처럼 좌우가 다른 도형을 쓰는 이유는?', fig:move({op:'뒤집기',showAfter:false,note:'움직이면 어떻게 될까요?'}), choices:['움직인 게 잘 보여서','그리기 쉬워서','색칠하기 좋아서','알 수 없다'], answer:'움직인 게 잘 보여서', hint:'그림으로 직접 움직여 보며 생각하세요.', sol:{ ans:'움직인 게 잘 보여서', fig:move({op:'뒤집기',note:'움직인 게 잘 보여서'}), steps:['동그라미·정사각형은 돌려도 티가 안 나요.','ㄱ 모양은 좌우·위아래가 확실히 달라요.','그래서 밀기·뒤집기·돌리기의 차이가 잘 보여요!']}}, { tier:'상위', type:'self', kind:'num', q:'도형을 시계 방향 90° 돌리기를 2번 하면 몇 도 돌린 셈일까요?', fig:null, answer:180, hint:'90 × 2 = ?', sol:{ ans:'180', fig:null, steps:['90° 돌리기를 2번.','90 × 2 = 180°.','180° = 반 바퀴 — 거꾸로 뒤집힌 모습이 돼요!']}}, { tier:'상위', type:'self', kind:'num', q:'시계 방향 90° 돌리기를 3번 하면 몇 도일까요?', fig:null, answer:270, hint:'90 × 3 = ?', sol:{ ans:'270', fig:null, steps:['90 × 3 = 270°.','270° = 시계 방향으로 세 번 = 반시계 방향 90°와 같아요!','한 바퀴가 360°니까 270° 돌린 건 반대로 90° 돈 것과 똑같아요.']}}, { tier:'상위', type:'self', kind:'num', q:'시계 방향 90° 돌리기를 5번 하면 결국 몇 도 돌린 것과 같을까요?', fig:null, answer:90, hint:'90 × 5 = 450. 한 바퀴(360°)는 제자리니까 빼 보세요!', sol:{ ans:'90', fig:null, steps:['90 × 5 = 450°.','한 바퀴 360°는 제자리로 돌아오는 거라 빼도 돼요.','450 − 360 = 90°. 결국 90° 돌린 것과 같아요!']}}, { tier:'상위', type:'self', kind:'choice', q:'시계 방향 90° 돌리기를 8번 하면 어떻게 될까요?', fig:null, choices:['처음과 같다','좌우가 바뀐다','위아래가 바뀐다','알 수 없다'], answer:'처음과 같다', hint:'90 × 8 = 720 = 몇 바퀴일까요?', sol:{ ans:'처음과 같다', fig:null, steps:['90 × 8 = 720°.','720 ÷ 360 = 2바퀴 — 딱 두 바퀴!','두 바퀴 돌면 제자리. 처음과 똑같아요!']}}, { tier:'상위', type:'self', kind:'choice', q:'좌우 뒤집기를 3번 하면 어떻게 될까요?', fig:null, choices:['한 번 뒤집은 것과 같다','처음과 같다','180° 돌린 것과 같다','알 수 없다'], answer:'한 번 뒤집은 것과 같다', hint:'뒤집기 2번은 제자리. 3번은 2번 + 1번!', sol:{ ans:'한 번 뒤집은 것과 같다', fig:null, steps:['뒤집기 2번 = 제자리(처음과 같음).','3번 = 2번 + 1번 = 제자리 + 한 번.','그래서 한 번 뒤집은 것과 같아요! 홀수 번이면 뒤집힌 채예요.']}}, { tier:'상위', type:'self', kind:'choice', q:'좌우 뒤집기를 10번 하면 어떻게 될까요?', fig:null, choices:['처음과 같다','좌우가 바뀐다','위아래가 바뀐다','알 수 없다'], answer:'처음과 같다', hint:'10은 짝수예요!', sol:{ ans:'처음과 같다', fig:null, steps:['뒤집기는 2번마다 제자리로 돌아와요.','10 = 2 × 5 — 짝수번.','그래서 처음과 같아요!']}}, { tier:'상위', type:'self', kind:'choice', q:'도형을 오른쪽으로 3칸 밀고 왼쪽으로 3칸 밀면?', fig:null, choices:['처음과 같다','좌우가 바뀐다','위아래가 바뀐다','알 수 없다'], answer:'처음과 같다', hint:'오른쪽 3칸 → 왼쪽 3칸 = 결국?', sol:{ ans:'처음과 같다', fig:null, steps:['오른쪽 3칸 갔다가 왼쪽 3칸 돌아옴.','결국 제자리.','반대 방향으로 같은 만큼 밀면 처음으로 돌아와요!']}}, { tier:'상위', type:'self', kind:'choice', q:'도형을 위로 2칸, 오른쪽으로 4칸 밀었습니다. 처음으로 돌아가려면?', fig:null, choices:['아래 2칸, 왼쪽 4칸','위 2칸, 오른쪽 4칸','아래 4칸, 왼쪽 2칸','알 수 없다'], answer:'아래 2칸, 왼쪽 4칸', hint:'왔던 길을 거꾸로 가면 돼요!', sol:{ ans:'아래 2칸, 왼쪽 4칸', fig:null, steps:['위 2칸 갔으니 아래 2칸.','오른쪽 4칸 갔으니 왼쪽 4칸.','반대 방향으로 같은 만큼 — 밀기는 항상 되돌릴 수 있어요!']}}, { tier:'상위', type:'self', kind:'choice', q:'도형을 180° 돌린 것좌우로 뒤집은 것은 항상 같을까요?', fig:null, choices:['그렇다','아니다','정사각형만','알 수 없다'], answer:'아니다', hint:'ㄱ 모양으로 직접 해 보세요. 180° 돌리면 위아래도 바뀌죠?', sol:{ ans:'아니다', fig:null, steps:['180° 돌리기 = 좌우 위아래가 둘 다 바뀜.','좌우 뒤집기 = 좌우 바뀜.','그래서 다른 결과예요! (좌우 뒤집기 + 위아래 뒤집기 = 180° 돌리기와 같아요)']}}, { tier:'상위', type:'self', kind:'choice', q:'좌우로 뒤집고 위아래로도 뒤집으면 무엇과 같을까요?', fig:null, choices:['180° 돌리기','90° 돌리기','밀기','처음과 같다'], answer:'180° 돌리기', hint:'둘 다 뒤집으면 좌우도 위아래도 바뀌어요. 그건 무엇과 같죠?', sol:{ ans:'180° 돌리기', fig:null, steps:['좌우 뒤집기 → 좌우가 바뀜.','거기에 위아래 뒤집기 → 위아래도 바뀜.','좌우·위아래가 둘 다 바뀐 것 = 180° 돌리기와 같아요!']}}, { tier:'최상위', type:'self', kind:'choice', q:'어떤 도형을 밀고 뒤집고 돌려도 변하지 않는 것 두 가지는?', fig:null, choices:['모양과 크기','위치와 방향','색과 크기','알 수 없다'], answer:'모양과 크기', hint:'위치·방향은 바뀌는데, 안 바뀌는 건 뭘까요?', sol:{ ans:'모양과 크기', fig:null, steps:['밀기 = 위치가 바뀜. 뒤집기·돌리기 = 방향이 바뀜.','하지만 모양크기는 절대 안 바뀌어요.','그래서 움직인 도형과 처음 도형은 완전히 포개져요 — 이걸 나중에 「합동」이라고 배워요!']}}, { tier:'최상위', type:'self', kind:'choice', q:'종이를 접어서 오려 낸 모양이 좌우 똑같이 나오는 이유는?', fig:null, choices:['접은 선을 기준으로 뒤집혀서','밀어서','돌려서','알 수 없다'], answer:'접은 선을 기준으로 뒤집혀서', hint:'접으면 한쪽이 다른 쪽 위에 포개지죠. 그건 무슨 움직임일까요?', sol:{ ans:'접은 선을 기준으로 뒤집혀서', fig:null, steps:['종이를 접으면 한쪽이 다른 쪽 위에 정확히 포개져요.','이건 접은 선을 기준으로 뒤집은 것과 같아요.','그래서 오리면 좌우가 똑같은 모양이 나와요 — 이게 「대칭」이에요!']}}, { tier:'최상위', type:'self', kind:'choice', q:'바람개비 무늬는 어떤 움직임으로 만들었을까요?', fig:null, choices:['밀기','뒤집기','돌리기','알 수 없다'], answer:'돌리기', hint:'날개가 같은 모양으로 빙 둘러 있죠?', sol:{ ans:'돌리기', fig:null, steps:['바람개비 날개는 모두 같은 모양.','중심을 기준으로 90°씩 돌려서 붙인 거예요.','돌리기로 무늬를 만들면 빙글빙글 도는 느낌이 나요!']}}, { tier:'최상위', type:'self', kind:'choice', q:'벽지 무늬가 끝없이 이어지는 건 어떤 움직임일까요?', fig:null, choices:['밀기','뒤집기','돌리기','알 수 없다'], answer:'밀기', hint:'같은 무늬가 옆으로 계속 반복되죠?', sol:{ ans:'밀기', fig:null, steps:['같은 무늬가 옆으로·아래로 계속 반복돼요.','이건 무늬를 계속 밀어서 붙인 거예요.','밀기를 반복하면 끝없이 이어지는 무늬가 만들어져요!']}}, { tier:'최상위', type:'self', kind:'num', q:'도형을 반시계 방향으로 90° 돌린 것은 시계 방향으로 몇 도 돌린 것과 같을까요?', fig:null, answer:270, hint:'한 바퀴는 360°. 반대로 90° 간 것 = 시계로 몇 도?', sol:{ ans:'270', fig:null, steps:['반시계 90° = 시계 방향으로 반대로 90° 간 것.','한 바퀴가 360°이니 360 − 90 = 270°.','같은 자리에 도착하는 길이 두 가지인 셈이에요!']}}, { tier:'최상위', type:'self', kind:'choice', q:'시계 방향 180° 와 반시계 방향 180° 는 결과가 같을까요?', fig:null, choices:['그렇다','아니다','90°만','알 수 없다'], answer:'그렇다', hint:'180°는 반 바퀴예요. 어느 쪽으로 돌든 반 바퀴면?', sol:{ ans:'그렇다', fig:null, steps:['180° = 반 바퀴.','오른쪽으로 반 바퀴든 왼쪽으로 반 바퀴든 같은 자리에 도착해요.','그래서 결과가 같아요! (90°는 다르지만 180°는 같아요)']}}, { tier:'최상위', type:'self', kind:'choice', q:'도형을 90° 돌리면 가로가 길던 것은 어떻게 될까요?', fig:null, choices:['세로가 길어진다','그대로다','더 커진다','알 수 없다'], answer:'세로가 길어진다', hint:'옆으로 누운 것을 90° 세우면?', sol:{ ans:'세로가 길어진다', fig:null, steps:['90° 돌리면 가로와 세로가 바뀌어요.','가로가 길던 것이 세로가 길어져요.','모양·크기는 그대로인데 놓인 방향만 바뀐 거예요!']}}, { tier:'최상위', type:'self', kind:'num', q:'삼각형을 밀기·뒤집기·돌리기로 움직였을 때 세 각의 합은?', fig:null, answer:180, hint:'움직여도 모양이 안 변하죠. 그럼 각도도?', sol:{ ans:'180', fig:null, steps:['움직여도 모양과 크기는 그대로.','각의 크기도 그대로예요.','그래서 세 각의 합은 여전히 180°! (원래부터 어떤 삼각형이든 180°예요)']}}, { tier:'최상위', type:'self', kind:'choice', q:'한 도형을 밀기만으로 다른 도형에 포갤 수 없다면 무엇이 더 필요할까요?', fig:null, choices:['뒤집기나 돌리기','더 큰 밀기','크기 바꾸기','알 수 없다'], answer:'뒤집기나 돌리기', hint:'위치는 맞는데 방향이 안 맞으면 무엇을 해야 할까요?', sol:{ ans:'뒤집기나 돌리기', fig:null, steps:['밀기는 위치만 바꿔요.','방향이 다르면 밀기만으론 포갤 수 없어요.','뒤집기나 돌리기로 방향을 맞춰야 해요!']}}, { tier:'최상위', type:'self', kind:'choice', q:'타일을 빈틈없이 까는 데 밀기·뒤집기·돌리기가 쓰이는 이유는?', fig:null, choices:['같은 모양을 여러 방향으로 놓으려고','타일을 크게 만들려고','색을 바꾸려고','알 수 없다'], answer:'같은 모양을 여러 방향으로 놓으려고', hint:'같은 타일 하나로 바닥 전체를 어떻게 채울까요?', sol:{ ans:'같은 모양을 여러 방향으로 놓으려고', fig:null, steps:['타일은 같은 모양 하나를 여러 개 쓰는 거예요.','그걸 밀고·뒤집고·돌려서 빈틈없이 맞춰요.','모양·크기가 안 변하니까 딱 맞게 이어붙일 수 있어요 — 이게 타일링의 원리!']}}, ]; const TIERS=['기초','중급','상위','최상위']; const TIER_DESC={기초:'영상만 봐도 바로 풀려요 — 배운 걸 그대로 적용.',중급:'한 번 더 생각 — 거꾸로·변형. 영상 안에 답이 있어요.',상위:'아래 학년 개념(선수 단원)을 함께 써야 풀려요.',최상위:'여러 개념·역발상·함정까지. 최고 난도.'}; let curTier='기초'; function tierProblems(t){ return P.map((p,i)=>({p,i})).filter(o=>o.p.tier===t); } function renderTabs(){ const tb=document.getElementById('tabs'); tb.innerHTML=''; TIERS.forEach(t=>{ const n=tierProblems(t).length; const b=document.createElement('button'); b.className='tab'+(t===curTier?' on':''); b.textContent=`${t} (${n})`; b.onclick=()=>{curTier=t; renderTabs(); renderList();}; tb.appendChild(b); }); document.getElementById('tierNote').textContent=TIER_DESC[curTier]; } function esc(x){return String(x).replace(/&/g,'&').replace(//g,'>');} function renderList(){ const L=document.getElementById('list'); L.innerHTML=''; const items=tierProblems(curTier); if(!items.length){ L.innerHTML='
이 난도는 곧 채워집니다 — 파일럿은 기초·중급·상위 예시.
'; return; } items.forEach(({p,i},k)=>{ const badge = p.type==='video' ? '🎬 영상해설' : '✍ 텍스트해설'; const pre = p.prereq ? `🔗 ${p.prereq.name}` : ''; const c=document.createElement('div'); c.className='card'; c.id='card'+i; let ansUI = p.kind==='choice' ? `
`+p.choices.map(o=>``).join('')+`
` : `
← 내 답을 쓰고 제출
`; c.innerHTML=`
${curTier} · 문제 ${k+1} ${badge} ${pre}
${mfx(p.q)}
${p.fig?`
${p.fig}
`:''} ${ansUI}
🔒 답을 제출해야 해설·정답이 열려요.
💡 힌트 — ${p.hint}
`; L.appendChild(c); }); } const picked={}; // 분수 답 입력 지원: '3/4', '1 1/2', '0.75' 모두 인식 function parseAns(v){ v=String(v).trim().replace(/\s+/g,' '); let m=/^(-?\d+)\s+(\d+)\/(\d+)$/.exec(v); if(m){ const s=(+m[1])<0?-1:1; return s*(Math.abs(+m[1])+(+m[2])/(+m[3])); } m=/^(-?\d+)\/(-?\d+)$/.exec(v); if(m) return (+m[1])/(+m[2]); return parseFloat(v.replace(/[^0-9.\-]/g,'')); } function pick(i,val,el){ picked[i]=val; document.querySelectorAll('#ch'+i+' button').forEach(b=>b.classList.remove('sel')); el.classList.add('sel'); } function mfx(t){ if(!window.mathToHTML) return t; // HTML 태그( 등)는 그대로 두고, 태그 사이 텍스트만 교과서식 수식으로 변환 return String(t).split(/(<[^>]+>)/).map(s=> s.charAt(0)==='<' ? s : mathToHTML(s)).join(''); } function submit(i){ const p=P[i]; let mine; if(p.kind==='choice'){ mine=picked[i]; if(mine==null){ alert('보기 중 하나를 먼저 고르세요.'); return; } } else { const v=document.getElementById('in'+i).value.trim(); if(v===''){ alert('답을 먼저 입력하세요.'); return; } mine=parseAns(v); if(isNaN(mine)){ alert('숫자나 분수(예: 3/4)로 입력해 주세요.'); return; } } const correct = p.kind==='choice' ? (mine===p.answer) : (Math.abs(mine-p.answer)<1e-6); const vd=document.getElementById('vd'+i); vd.classList.remove('hide'); document.getElementById('lock'+i).classList.add('hide'); document.getElementById('show'+i).classList.remove('hide'); // 제출했으니 해설 열람 가능 if(correct){ vd.className='verdict ok'; vd.innerHTML='✅ 정답이에요! 잘했어요.'; reveal(i); } else{ vd.className='verdict no'; vd.innerHTML='❌ 아쉬워요. 힌트를 보고 다시 풀어 볼까요? (해설도 열렸어요)'; document.getElementById('hint'+i).classList.remove('hide'); } } function reveal(i){ const p=P[i], sol=document.getElementById('sol'+i); sol.classList.remove('hide'); let steps=p.sol.steps.map(s=>`
• ${mfx(s)}
`).join(''); let pre=p.prereq?`
🔗 이 문제는 ${p.prereq.name} 개념이 필요해요 — 그 단원 보러 가기 →
`:''; let vsol = p.type==='video' ? `🎬 이 문제 영상 해설 보기 (짧게)` : ''; sol.innerHTML=`

📝 해설

${pre}${p.sol.fig?`
${p.sol.fig}
`:''}${steps}
정답: ${mfx(p.sol.ans)}
${vsol}` +`
🙋 이 해설이 어렵거나 더 자세히 알고 싶으면 말씀해주세요 — 상세 해설지를 새로 만들어 보내드릴게요.
` +``; } /* ───────── "이 해설도 어려워요" 체크 → 하단 요청 코너로 모으기 ───────── */ const hardSet=new Set(); function toggleHard(i,on){ on?hardSet.add(i):hardSet.delete(i); syncHard(); } function syncHard(){ const box=document.getElementById('hardList'); if(!box) return; if(!hardSet.size){ box.style.display='none'; box.textContent=''; return; } const names=[...hardSet].sort((a,b)=>a-b).map(i=>{ const list=tierProblems(P[i].tier); const k=list.findIndex(o=>o.i===i); return `${P[i].tier} ${k+1}번`; }); box.style.display='block'; box.innerHTML='🙋 해설이 어렵다고 표시한 문제: '+names.join(', ')+' — 보내기를 누르면 함께 전달돼요.'; } /* ───────── 추가요청 보내기 (선생님 확인용 · 이 기기에 저장) ───────── */ function reqKey(){ return 'pbreq:'+location.pathname; } function loadReq(){ try{ return JSON.parse(localStorage.getItem(reqKey())||'[]'); }catch(e){ return []; } } function nowStr(){ const d=new Date(), p=n=>(n<10?'0':'')+n; return d.getFullYear()+'-'+p(d.getMonth()+1)+'-'+p(d.getDate())+' '+p(d.getHours())+':'+p(d.getMinutes()); } function sendReq(){ const ta=document.getElementById('reqText'), note=document.getElementById('reqNote'); const text=(ta.value||'').trim(); const hard=[...hardSet].sort((a,b)=>a-b).map(i=>{ const list=tierProblems(P[i].tier); const k=list.findIndex(o=>o.i===i); return `${P[i].tier} ${k+1}번`; }); if(!text && !hard.length){ note.style.color='#C0503D'; note.textContent='요청 내용을 적거나, 어려운 해설을 체크해 주세요.'; return; } const arr=loadReq(); arr.push({ date:nowStr(), text, hard, unit:'초4 평면도형 밀기·뒤집기·돌리기' }); try{ localStorage.setItem(reqKey(), JSON.stringify(arr)); }catch(e){} ta.value=''; hardSet.clear(); syncHard(); document.querySelectorAll('.retag input').forEach(c=>c.checked=false); note.style.color='#2c8a55'; note.textContent='✅ 보냈어요! 선생님이 확인합니다. (이 기기에 저장됨)'; renderReq(); } function renderReq(){ const box=document.getElementById('reqMine'); if(!box) return; const arr=loadReq(); box.innerHTML=''; if(!arr.length) return; box.innerHTML='
📒 내가 보낸 요청 ('+arr.length+')
' + arr.slice().reverse().map(it=>`
${it.hard&&it.hard.length?'🙋 어려운 해설: '+it.hard.join(', ')+'
':''}${(it.text||'').replace(/(내용 없음)'}
${it.date}
`).join(''); } /* ───────── 인쇄 2모드 — 절약 인쇄(2단·컬러 유지) ───────── */ function ecoPrint(){ document.body.classList.add('eco-print'); window.addEventListener('afterprint', function h(){ document.body.classList.remove('eco-print'); window.removeEventListener('afterprint',h); }); window.print(); } /* ───────── DrawBoard (문항별 손으로 풀기 — 화면 가득 전체화면) ───────── */ const canvas=document.getElementById('drawCanvas'), ctx=canvas.getContext('2d'); const drawOverlay=document.getElementById('drawOverlay'), drawWrap=document.getElementById('drawCanvasWrap'); let drawOn=false, tool='pen', dsize=6, dcolor='#1f2a2a', drawing=false, last=null, currentDrawQ=null; const drawings={}; // i -> dataURL (문항별 손풀이) const photos={}; // i -> dataURL (문항별 사진) function sizeCanvas(restore){ const rect=drawWrap.getBoundingClientRect(), dpr=window.devicePixelRatio||1; canvas.width=Math.round(rect.width*dpr); canvas.height=Math.round(rect.height*dpr); canvas.style.width=rect.width+'px'; canvas.style.height=rect.height+'px'; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,rect.width,rect.height); if(restore){ const img=new Image(); img.onload=()=>ctx.drawImage(img,0,0,rect.width,rect.height); img.src=restore; } } window.addEventListener('resize',()=>{ if(drawOn) sizeCanvas(drawings[currentDrawQ]); }); window.openDraw=function(i){ drawOn=true; currentDrawQ=i; const plain=String(P[i].q).replace(/<[^>]*>/g,''); document.getElementById('drawQref').textContent=P[i].tier+' · '+plain.slice(0,30)+'…'; drawOverlay.classList.add('on'); sizeCanvas(drawings[i]); const el2=document.documentElement, req=el2.requestFullscreen||el2.webkitRequestFullscreen||el2.msRequestFullscreen; if(req){ try{ req.call(el2).catch(()=>{}); }catch(e){} } }; window.closeDraw=function(){ if(currentDrawQ!=null){ const hasInk=(()=>{ const d=ctx.getImageData(0,0,canvas.width,canvas.height).data; for(let k=3;k0) return true; return false; })(); if(hasInk){ drawings[currentDrawQ]=canvas.toDataURL('image/png'); renderThumbs(currentDrawQ); } } drawOn=false; currentDrawQ=null; drawOverlay.classList.remove('on'); if(document.fullscreenElement||document.webkitFullscreenElement){ (document.exitFullscreen||document.webkitExitFullscreen).call(document).catch(()=>{}); } }; window.submitFromDraw=function(){ const i=currentDrawQ; closeDraw(); if(i!=null) submit(i); }; document.addEventListener('fullscreenchange',()=>{ if(!document.fullscreenElement && drawOn) closeDraw(); }); window.setTool=t=>{ tool=t; document.getElementById('toolPen').classList.toggle('on',t==='pen'); document.getElementById('toolErase').classList.toggle('on',t==='erase'); }; window.setSize=s=>{ dsize=s; ['szS','szM','szL'].forEach(id=>document.getElementById(id).classList.remove('on')); document.getElementById(s===3?'szS':s===12?'szL':'szM').classList.add('on'); }; window.setColor=c=>{ dcolor=c; }; window.clearDraw=()=>{ if(confirm('그린 것을 모두 지울까요?')) ctx.clearRect(0,0,canvas.width,canvas.height); }; function dpos(e){ const r=canvas.getBoundingClientRect(), t=e.touches?e.touches[0]:e; return [t.clientX-r.left, t.clientY-r.top]; } function dstart(e){ if(!drawOn) return; e.preventDefault(); drawing=true; last=dpos(e); } function dmove(e){ if(!drawOn||!drawing) return; e.preventDefault(); const p=dpos(e); ctx.lineCap='round'; ctx.lineJoin='round'; if(tool==='erase'){ ctx.globalCompositeOperation='destination-out'; ctx.lineWidth=dsize*4; } else{ ctx.globalCompositeOperation='source-over'; ctx.strokeStyle=dcolor; ctx.lineWidth=dsize; } ctx.beginPath(); ctx.moveTo(last[0],last[1]); ctx.lineTo(p[0],p[1]); ctx.stroke(); last=p; } function dend(){ drawing=false; } canvas.addEventListener('mousedown',dstart); canvas.addEventListener('mousemove',dmove); window.addEventListener('mouseup',dend); canvas.addEventListener('touchstart',dstart,{passive:false}); canvas.addEventListener('touchmove',dmove,{passive:false}); canvas.addEventListener('touchend',dend,{passive:false}); /* ───────── 사진으로 올리기 (문항별) ───────── */ window.onPhoto=function(i,inputEl){ const file=inputEl.files&&inputEl.files[0]; if(!file) return; const reader=new FileReader(); reader.onload=ev=>{ const img=new Image(); img.onload=()=>{ const maxW=900, sc=Math.min(1,maxW/img.width), w=Math.round(img.width*sc), h=Math.round(img.height*sc); const cv=document.createElement('canvas'); cv.width=w; cv.height=h; cv.getContext('2d').drawImage(img,0,0,w,h); photos[i]=cv.toDataURL('image/jpeg',0.6); renderThumbs(i); }; img.src=ev.target.result; }; reader.readAsDataURL(file); inputEl.value=''; }; window.removePhoto=function(i){ delete photos[i]; renderThumbs(i); }; window.removeDrawing=function(i){ delete drawings[i]; renderThumbs(i); }; function renderThumbs(i){ const box=document.getElementById('thumbs'+i); if(!box) return; let h=''; if(drawings[i]) h+=`
손풀이
`; if(photos[i]) h+=`
사진
`; box.innerHTML=h; const db=document.getElementById('drawBtn'+i), pb=document.getElementById('photoBtn'+i); if(db) db.classList.toggle('has', !!drawings[i]); if(pb) pb.classList.toggle('has', !!photos[i]); } // 탭을 바꿔 다시 그려도 그 문항의 손풀이·사진 썸네일이 되살아나게 const _renderList=renderList; renderList=function(){ _renderList(); tierProblems(curTier).forEach(({p,i})=>renderThumbs(i)); }; renderTabs(); renderList(); renderReq();