관리자 인증 확인 중...
B 또는 Space 로 복귀
LUNA CODE LAB · CLASS I Week 5 — 미사일과 적 등장 초4 · 중3 / 90분
1 / 9 📄 워크시트
WEEK 05 · 2026

미사일이 날아가고
적이 쏟아진다

지난주에 키보드로 비행기를 움직였다. 이번 주는 진짜 슈팅 게임 — 스페이스바 누르면 미사일이 발사되고, 하늘에서 적이 무한히 내려옵니다. 배열·setInterval·메모리 관리까지 한 번에.

Duration90 minutes
ToolClaude Code
Output발사 + 적 스폰
Phase비행기 게임 (2/4)
02 / Learning Goals⏱ 5분

오늘의 학습 목표

1
발사 시스템 (쿨다운)
스페이스바 한 번에 미사일 한 발. 0.3초 안에 또 누르면 안 나간다 — 연사 제한이 왜 필요한지.
2
적 무한 스폰 (setInterval)
setInterval로 1초마다 적 하나씩. 화면 위에서 시작해서 아래로 내려오게.
3
배열에 객체 저장
미사일 100개·적 100개 → 어떻게 관리? 배열 하나에 다 담고 매 프레임 forEach.
4
메모리 청소 (filter)
화면 밖 나간 미사일·적은 배열에서 빼야 한다. 안 빼면 1분 후 게임이 멈춤.
03 / Concept⏱ 10분

배열 = 물건 담는 상자

미사일이 10발 나가면? 적이 50마리 있으면? 변수 하나씩 만들면 미친다. 그래서 "배열"이라는 상자 하나에 다 담고, 한꺼번에 처리한다.

📦
미사일 배열
[ ◆, ◆, ◆, ... ]
발사할 때마다 +1
+
📦
적 배열
[ ▼, ▼, ▼, ... ]
1초마다 +1
🔄
매 프레임
forEach로 전부
이동 · 그리기
핵심: "많은 걸 한꺼번에 다루려면 배열". 게임은 99% 이 패턴이다.
04 / Cooldown⏱ 10분

쿨다운이 필요할까?

❌ 쿨다운 없으면
스페이스바 꾹 누르면
1초에 60발 발사
→ 미사일 한 줄로 붙어서
그냥 막대기처럼 보임
왜 망함? 60fps 게임 루프는 1초에 60번 코드를 실행. 키 누르고 있으면 매 프레임 발사 = 미사일이 끊김없이 나옴. 메모리도 0.5초만에 1000발 쌓임 → 컴퓨터 멈춤.
✓ 쿨다운 있으면
한 발 쏘면 0.3초 대기
그 사이 스페이스 눌러도 무시
0.3초 지나야 다음 발 가능
→ 진짜 슈팅 게임 느낌
왜 좋음? 마지막 발사 시각을 변수에 저장 → 지금 시각이 그것보다 0.3초 이상 지났을 때만 발사. 이게 모든 슈팅·격투·MMO 게임 공식.
💡 오늘의 공식: if (지금 - 마지막발사 > 300ms) { 발사하기; 마지막발사 = 지금; } — 한 줄에 다 들어있어.
05 / Live Build · Step 1⏱ 8분

Step 1 — 미사일 한 발 발사

먼저 한 발만 만든다. 스페이스바 한 번에 미사일 하나가 비행기 위치에서 위로 슉.

1
Claude에게 보낼 프롬프트
// 지난주 비행기 코드에 이어서 "지난주에 만든 비행기 게임에 미사일을 추가해줘. - 스페이스바를 누르면 비행기 머리 위에서 미사일이 생긴다 - 미사일은 노란색 작은 사각형(8x16px) - 매 프레임마다 위로 8px씩 이동 - 일단 한 발만. 여러 발은 다음 단계에서."
AI가 만들 코드 미리보기
let missile = null; // 미사일 객체 document.addEventListener('keydown', e => { if (e.code === 'Space' && !missile) { missile = { x: plane.x + 21, y: plane.y, w: 8, h: 16 }; } }); function update() { if (missile) missile.y -= 8; // 위로 이동 }
🎙
멘토 멘트 "missile = null 이라는 게 '아직 미사일 없음'이라는 뜻이에요. 한 발 쏘면 missile 안에 좌표가 들어가요. 다음 단계에서 이걸 배열로 바꾸면 100발도 가능."
학생 액션 한 발 발사 작동하는지 확인 → 발사된 미사일이 화면 밖으로 나가면 어떻게 되는지 관찰. (다음 단계에서 풀 문제)
05 / Live Build · Step 2⏱ 12분

Step 2 — 발사 쿨다운 · 여러 발

이제 배열로 바꿔서 여러 발 발사. 쿨다운 0.3초 적용. 진짜 슈팅 게임 시작.

2
Claude에게 보낼 프롬프트
"미사일을 한 번에 여러 발 발사 가능하게 바꿔줘. - missile 변수 → missiles 배열로 변경 - 스페이스바 누를 때마다 새 미사일 추가 - 단, 쿨다운 0.3초 — 너무 빨리 누르면 무시 - 매 프레임 모든 미사일이 위로 이동 - 모든 미사일을 그려줘"
핵심 코드 — 쿨다운 + 배열
const missiles = []; // 빈 배열 let lastShot = 0; // 마지막 발사 시각 document.addEventListener('keydown', e => { if (e.code === 'Space') { const now = Date.now(); if (now - lastShot > 300) { // 0.3초 지났나? missiles.push({ x: plane.x + 21, y: plane.y, w: 8, h: 16 }); lastShot = now; } } }); missiles.forEach(m => m.y -= 8); // 다 같이 위로
🎙
핵심 강조 "forEach는 '배열의 모든 것에 똑같은 짓 하기'예요. 미사일이 10개든 100개든 한 줄로 다 처리. 이거 이해하면 게임 만드는 게 진짜 쉬워져요."
05 / Live Build · Step 3⏱ 15분

Step 3 — 적 무한 스폰

화면 위에서 적이 1초마다 한 마리씩 무작위 위치에 나타나고, 아래로 떨어진다. setInterval의 첫 등장.

3
Claude에게 보낼 프롬프트
"적을 추가해줘. - setInterval로 1초마다 적 한 마리 생성 - 적은 빨간 사각형(30x30px) - 시작 위치: 화면 맨 위, x는 0~화면너비 사이 랜덤 - 매 프레임 아래로 2px씩 이동 - enemies 배열에 저장하고 forEach로 처리"
핵심 코드 — setInterval로 무한 스폰
const enemies = []; // 1초마다 자동 실행 — 게임 끝까지 계속 setInterval(() => { enemies.push({ x: Math.random() * (canvas.width - 30), // 랜덤 x y: -30, // 화면 위에서 시작 w: 30, h: 30 }); }, 1000); // 1000ms = 1초 // 매 프레임 enemies.forEach(en => en.y += 2); // 다 같이 아래로
🎙
setInterval 한 줄 설명 "setInterval은 '몇 ms마다 이 함수 자동 실행' 명령이에요. 1000은 1초. 500으로 바꾸면 0.5초마다 적 나옴 → 더 어려운 게임. 숫자만 바꾸면 난이도 조절돼요."
🎮
학생 자유 실험 setInterval의 1000을 500·2000·100으로 바꿔보기 → 어떤 게 가장 재밌는지 한 줄로 메모.
05 / Live Build · Step 4⏱ 5분

Step 4 — 적이 아래로 떨어진다

사실 Step 3에서 이미 해결됐어. 여기선 속도 다양화로 게임 느낌을 살린다.

4
Claude에게 보낼 프롬프트
"지금 적이 모두 같은 속도로 떨어지는데, 각 적마다 속도를 다르게 해줘. - 스폰될 때 speed 값에 1~4 사이 랜덤 숫자 넣어줘 - 빠른 적은 4, 느린 적은 1로 떨어짐 - 색깔도 속도에 따라 다르게: · 빠를수록 진한 빨강, 느릴수록 연한 분홍"
핵심 코드 — 각 적이 자기 속도 가짐
setInterval(() => { const speed = 1 + Math.random() * 3; // 1~4 사이 enemies.push({ x: Math.random() * canvas.width, y: -30, w: 30, h: 30, speed: speed, // 각자 자기 속도 보유 color: `hsl(0, 80%, ${70 - speed * 12}%)` // 속도→밝기 }); }, 1000); enemies.forEach(en => en.y += en.speed); // 각자 자기 속도로
🎙
객체 속에 속성 "each 적이 자기 x, y만 가지는 게 아니라 speed, color까지 가지고 있다는 게 포인트. 이게 객체지향의 시작이에요. 다음 주엔 HP, 점수도 객체 안에 넣을 거예요."
05 / Live Build · Step 5⏱ 10분

Step 5 — 화면 밖 청소 (메모리)

미사일이 화면 위로 나갔는데도 배열에 계속 남아 있다. 1분 뒤 게임이 버벅대기 시작. 이걸 해결한다.

5
Claude에게 보낼 프롬프트
"화면 밖으로 나간 미사일·적을 배열에서 자동 삭제해줘. - 미사일이 y < 0 (화면 위로 나감) → 배열에서 빼기 - 적이 y > canvas.height (화면 아래로 나감) → 배열에서 빼기 - filter로 처리해줘 (남길 것만 남기기) 그리고 콘솔에 missiles.length, enemies.length 매초 출력해줘 — 진짜로 줄어드는지 확인용."
핵심 코드 — filter로 청소
// 매 프레임 실행 // "y가 0보다 큰 것만 남겨라" = 화면 위로 나간 미사일 제거 missiles = missiles.filter(m => m.y > 0); // "y가 화면높이보다 작은 것만 남겨라" = 화면 밑으로 나간 적 제거 enemies = enemies.filter(en => en.y < canvas.height); // 디버그용 — 5초 뒤 비교해봐 setInterval(() => { console.log('미사일:', missiles.length, '적:', enemies.length); }, 1000);
🎙
왜 중요한가 "filter 안 쓰면 5분 후엔 미사일 9000개가 배열에 남아 있어요. 매 프레임 forEach가 9000번 돌면? 게임이 슬로우모션 돼요. 진짜 게임 회사도 이 문제로 고생해요."
🔍
학생 액션 F12로 콘솔 열고 5분 동안 게임 → missiles.length가 30 넘게 안 가는지 확인. 넘으면 filter 코드 빠진 것.
10 / Checkpoint⏱ 5분

여기까지 됐나? 확인하기

잘 됐다면
  • 스페이스바로 미사일 발사 (꾹 눌러도 연사 안 됨)
  • 1초마다 적이 위에서 떨어짐
  • 적마다 속도가 다름 (빠른놈·느린놈 섞임)
  • 5분 플레이해도 버벅임 없음 (filter 작동)
  • F12 콘솔에 missiles, enemies 숫자 일정 범위 유지
⚠️자주 막히는 부분
  • 스페이스 꾹 누르면 무지막지 발사 → 쿨다운 lastShot 코드 빠짐
  • 적이 안 나옴 → setInterval이 game.start() 안에 있나 확인
  • 1분 뒤 게임 느려짐 → filter 코드 빠짐 (메모리 누수)
  • 미사일이 비행기에서 안 나옴 → plane.x 좌표 잘못
  • 적이 한 줄로 줄지어 옴 → Math.random 빠짐
11 / Challenge⏱ 10분

더 해보고 싶다면 도전

💥
미사일을 한 번에 3발 발사하게 — 가운데·왼쪽 사선·오른쪽 사선. (트리플 샷)
난이도 ★
🌊
적이 좌우로 흔들면서 내려오게. Math.sin(y * 0.05) * 50 으로 x 변형.
난이도 ★★
시간이 지날수록 점점 빨라지게 — 10초마다 setInterval 간격을 100ms씩 줄여서 난이도 상승.
난이도 ★★★
12 / Student Gallery⏱ 7분

작년 친구들 작품

13 / Next Week⏱ 3분

다음 시간 예고

WEEK 06 · COMING UP

적 패턴과 점수 시스템 —
맞으면 죽고, 점수가 쌓인다

이번 주는 적과 미사일이 따로따로 움직였다. 다음 주는 둘이 만나는 순간 — 충돌 감지. 미사일이 적을 맞히면 적이 죽고 점수 +. 비행기도 적과 부딪히면 HP가 깎입니다.

PREPARE · 준비물
  • 이번 주 코드 (미사일·적·쿨다운까지 다 들어있는 버전)
  • 좋아하는 슈팅 게임에서 점수 UI 스크린샷 한 장
  • "HP 바를 어떻게 보이게 할지" 1줄 아이디어