JavaScript로 만드는 인터랙티브 웹 게임 프로젝트
- 강의 제목: JavaScript로 만드는 개발자 출근룩 게임
- 강의 대상: 처음 JavaScript를 배우는 사람이나, 간단한 게임을 만들어 보고 싶은 사람
- 선수 지식: HTML, CSS, JavaScript의 기초 문법
- 강의 목표: 드래그 앤 드롭 게임을 만들어 보면서 JavaScript의 DOM 조작, 이벤트 처리, 상태 관리를 실전에서 익힌다
- 예상 소요 시간: 약 4-5시간
- 플랫폼: 유튜브 (한글)
- 준비물:
- 코드 편집기 (VSCode 권장)
- 웹 브라우저 (Chrome 권장)
- macOS 또는 Windows 컴퓨터
이 강의를 마치면 다음을 할 수 있게 됩니다:
- ✅ HTML5 Drag and Drop API 활용
- ✅ JavaScript 클래스와 객체 지향 프로그래밍 기초
- ✅ DOM 조작과 이벤트 리스너 활용
- ✅ 동적 요소 생성 및 스타일 제어
- ✅ Promise와 비동기 처리 이해
- ✅ 상태 관리 패턴 구현
전체 강의는 18개의 짧은 강의로 구성되며, 총 소요 시간은 약 80-100분입니다.
| # | 강의 제목 | 주요 내용 | 시간 | 핵심 개념 |
|---|---|---|---|---|
| Part 1: 시작하기 | ||||
| 01 | 🎮 프로젝트 소개 및 완성본 데모 | - 완성된 게임 시연 - 프로젝트 구조 소개 - 필요한 에셋 확인 |
5분 | 프로젝트 개요 파악 |
| 02 | 📝 HTML 구조 만들기 | - 캐릭터 영역 작성 - 아이템 보드 구조 - data-category 속성 활용 |
6분 | 시맨틱 HTML, data 속성 |
| 03 | 🎨 CSS 레이아웃 기본 | - Flexbox/Grid 레이아웃 - position: absolute 활용- 기본 스타일링 |
7분 | CSS 레이아웃, 요소 겹치기 |
| Part 2: 드래그 앤 드롭 구현 | ||||
| 04 | 🏗️ JavaScript 클래스 구조 설계 | - ES6 클래스 문법 - constructor 작성 - 상태 객체 설계 ( wornItems, zIndexMap) |
6분 | 클래스, 상태 관리 |
| 05 | 🖱️ 드래그 이벤트 기초 | - dragstart 이벤트 처리- draggedElement 추적- effectAllowed 이해 |
5분 | HTML5 Drag API |
| 06 | 📦 드롭 영역 설정 | - dragover 이벤트와 preventDefault()- drop 이벤트 처리- 드롭 효과 설정 |
5분 | 이벤트 기본 동작 방지 |
| 07 | ✨ 아이템 생성 및 배치 | - 동적으로 DOM 요소 생성 - on-body SVG 파일 사용 - 캐릭터에 아이템 추가 |
7분 | DOM 조작, createElement |
| Part 3: 게임 로직 | ||||
| 08 | 🔄 아이템 중복 방지 | - 카테고리별 착용 제한 - 기존 아이템 제거 및 교체 - 원본 아이템 복원 |
6분 | 조건문, 상태 업데이트 |
| 09 | 📚 z-index로 레이어 관리 | - zIndexMap 활용- 카테고리별 쌓임 순서 - 스타일 동적 적용 |
5분 | z-index, 레이어 제어 |
| 10 | 🎒 특수 케이스: 백팩 구현 | - div로 이미지 그룹화 - 앞/뒤 두 이미지 처리 - pointer-events 활용 |
6분 | 그룹 요소, 예외 처리 |
| 11 | 👔 특수 케이스: 긴 머리와 예외 처리 | - 파일명으로 특수 케이스 판단 - z-index 예외 처리 - 조건부 로직 |
4분 | 조건문 분기, 예외 처리 |
| 12 | 🩳 디폴트 의상 (파자마) 처리 | - 상태에 따른 표시/숨김 - updatePajamaVisibility()- 논리 연산자 활용 |
5분 | 조건부 렌더링, OR 연산 |
| Part 4: 사용자 인터랙션 | ||||
| 13 | 🖐️ 마우스로 아이템 위치 조정 | - 마우스 이벤트 (mousedown, mousemove, mouseup)- 상대 좌표 계산 - 드래그 플래그 패턴 |
7분 | 마우스 이벤트, 좌표 계산 |
| 14 | 🗑️ 영역 밖 드래그로 아이템 삭제 | - getBoundingClientRect() 활용- 좌표로 영역 판단 - dragend 이벤트에서 삭제 처리 |
6분 | 위치 정보, 조건 판단 |
| Part 5: UX 개선 | ||||
| 15 | ⏳ 로딩 화면 만들기 (기본) | - CSS 애니메이션 (@keyframes)- 회전 스피너 구현 - setTimeout 활용 |
5분 | CSS 애니메이션, 타이머 |
| 16 | 🖼️ 이미지 로드 완료 감지 (심화) | - Promise 패턴 - 이미지 load 이벤트- Promise.all() 활용 |
7분 | Promise, 비동기 처리 |
| 17 | 🎯 시각적 피드백 추가 | - 드래그 시 시각 효과 - 호버 효과 및 커서 - 클래스 토글 |
4분 | CSS 클래스 조작, 사용자 피드백 |
| 마무리 | ||||
| 18 | 🎉 마무리 및 추가 아이디어 | - 프로젝트 요약 - 개선 아이디어 소개 - 다음 학습 방향 |
4분 | 프로젝트 회고, 확장 가능성 |
총 예상 시간: 약 90분
- 각 강의는 독립적으로 시청 가능하도록 구성
- 이전 강의 내용을 간단히 복습하고 시작
- 코드는 따라하기 방식으로 진행
- 각 강의 마지막에 핵심 요약 제공
- 처음 배우는 경우: 01번부터 순서대로 시청
- 특정 기능만 필요한 경우: 해당 Part만 선택 시청
- 복습하는 경우: 핵심 강의(04, 05, 07, 08, 13, 16)만 선택 시청
- 완성된 게임 시연
- 프로젝트 구조 설명
- 필요한 에셋(SVG 파일) 소개
학습 내용:
- 시맨틱 HTML 구조 설계
- SVG 이미지 다루기
data-*속성 활용법
핵심 코드:
<div class="character-area">
<img id="character" src="img/svgs/character.svg" alt="캐릭터">
<div id="character-items"></div>
</div>
<div class="item-board">
<img src="..." class="item draggable" data-category="top" draggable="true">
</div>중요 포인트:
data-category속성으로 아이템 분류- 적절한
alt텍스트 작성 (접근성) - 캐릭터 영역과 아이템 보드 분리
학습 내용:
- Flexbox/Grid를 활용한 레이아웃
position: absolute로 요소 겹치기z-index개념과 레이어 관리
핵심 CSS:
#character-items {
position: absolute;
top: 0;
left: 0;
}
.item {
cursor: grab;
transition: transform 0.2s;
}학습 내용:
- ES6 클래스 문법
- constructor와 메서드
- 상태 관리를 위한 객체 설계
핵심 코드:
class DressUpGame {
constructor() {
this.characterArea = document.querySelector('.character-area');
this.wornItems = {
top: null,
pants: null,
// ...
};
this.zIndexMap = { /* ... */ };
}
}중요 포인트:
wornItems객체로 현재 착용 상태 추적zIndexMap으로 레이어 순서 관리- DOM 요소 참조를 인스턴스 변수로 저장
학습 내용:
- HTML5 Drag and Drop API
dragstart,dragover,drop,dragend이벤트effectAllowed와dropEffect이해
핵심 코드:
handleDragStart(e, fromBoard) {
this.draggedElement = e.currentTarget;
this.isDraggingFromBoard = fromBoard;
e.dataTransfer.effectAllowed = 'move';
}
handleDragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
}중요 포인트:
e.preventDefault()필수!- 드래그 소스 추적 (
draggedElement) - 보드에서 드래그 vs 착용한 아이템 드래그 구분
학습 내용:
- 동적으로 DOM 요소 생성
- 파일 경로 조작 (on-body SVG 사용)
- 요소 스타일 동적 적용
핵심 코드:
createItemOnCharacter(sourceItem) {
const category = sourceItem.dataset.category;
const onBodyPath = this.getOnBodyPath(sourceItem.src);
const newItem = this.createNewImgElement(onBodyPath, sourceItem.alt);
newItem.dataset.category = category;
newItem.style.zIndex = this.zIndexMap[category];
this.characterItems.appendChild(newItem);
this.wornItems[category] = newItem;
}중요 포인트:
createElement로 동적 요소 생성appendChild로 DOM에 추가- 원본 아이템 참조 저장 (
sourceItem)
학습 내용:
- 조건문으로 상태 확인
- 기존 요소 제거 및 교체
- DOM 조작 메서드 활용
핵심 코드:
if (this.wornItems[category]) {
const oldItem = this.wornItems[category];
if (oldItem.sourceItem) {
oldItem.sourceItem.style.display = '';
}
oldItem.remove();
}중요 포인트:
- 같은 카테고리는 하나만 착용 가능
- 이전 아이템은 보드로 복귀
display: none↔display: ''토글
학습 내용:
- z-index로 레이어 순서 제어
- 특수 케이스 처리 (긴 머리, 가방)
- 조건문과 분기 처리
핵심 코드:
this.zIndexMap = {
body: 0,
socks: 1,
shoes: 2,
pants: 3,
top: 4,
outer: 5,
bag: 6,
hair: 7,
headwear: 8
};
// 예외 처리
if (category === 'hair' && filename === 'long-straight') {
zIndex = this.zIndexMap.body + 1;
}중요 포인트:
- 논리적인 레이어 순서 정의
- 예외 케이스 고려 (긴 머리는 몸 뒤로)
- 가방은 앞/뒤 두 이미지로 구성
학습 내용:
- div 컨테이너로 여러 이미지 그룹화
- 각 이미지에 다른 z-index 적용
pointer-events활용
핵심 코드:
const newGroup = document.createElement('div');
newGroup.className = 'placed-group';
const backImg = this.createNewImgElement(backPath, alt, '', true);
const frontImg = this.createNewImgElement(frontPath, alt, '', true);
backImg.style.zIndex = (this.zIndexMap.body - 1).toString();
frontImg.style.zIndex = this.zIndexMap.bag.toString();
newGroup.appendChild(backImg);
newGroup.appendChild(frontImg);중요 포인트:
- 그룹 내부 이미지는
pointer-events: none - 부모 div만 드래그/클릭 가능
- 앞/뒤 이미지로 입체감 표현
학습 내용:
- 조건부 렌더링
- 상태에 따른 UI 업데이트
- 논리 연산자 활용
핵심 코드:
updatePajamaVisibility() {
if (this.wornItems.top || this.wornItems.outer) {
this.pajamaTop.style.display = 'none';
} else {
this.pajamaTop.style.display = 'block';
}
}중요 포인트:
- 상의/아우터 착용 시 파자마 상의 숨김
- 바지 착용 시 파자마 하의 숨김
- 논리적 OR 연산자 활용
학습 내용:
- 마우스 이벤트 (
mousedown,mousemove,mouseup) - 상대 좌표 계산
- 이벤트 플래그 패턴
핵심 코드:
const handleMouseDown = (e) => {
isDragging = true;
initialX = e.clientX - currentX;
initialY = e.clientY - currentY;
};
const handleMouseMove = (e) => {
if (!isDragging) return;
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
item.style.left = `${currentX}px`;
item.style.top = `${currentY}px`;
};중요 포인트:
- 드래그 플래그로 상태 관리
- 초기 위치 기준으로 상대 좌표 계산
preventDefault()로 기본 동작 방지
학습 내용:
- 두 가지 드래그 메커니즘 공존
- 동적으로
draggable속성 변경 - 이벤트 우선순위 제어
핵심 개념:
- 클릭만 하면: HTML5 드래그 활성화 (휴지통/영역 밖으로 드래그)
- 5px 이상 움직이면: 마우스 드래그 모드 (위치 조정)
- 상황에 따라
item.draggable토글
학습 내용:
- 요소의 위치 정보 가져오기 (
getBoundingClientRect()) - 마우스 좌표와 비교
dragend이벤트 활용
핵심 코드:
checkAndRemoveIfOutside(e, item, category) {
const rect = this.characterArea.getBoundingClientRect();
const x = e.clientX;
const y = e.clientY;
const isOutside = x < rect.left || x > rect.right ||
y < rect.top || y > rect.bottom;
if (isOutside) {
// 아이템 삭제 및 원본 복원
item.remove();
if (item.sourceItem) {
item.sourceItem.style.display = '';
}
}
}중요 포인트:
getBoundingClientRect()로 요소 위치/크기 정보- 좌표 비교로 영역 내/외 판단
- 삭제 시 원본 아이템 복원 필수
학습 내용:
- CSS 애니메이션 (
@keyframes) setTimeout활용- 조건부 표시/숨김
핵심 코드:
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-spinner {
animation: spin 1s linear infinite;
}setTimeout(() => {
loadingScreen.style.display = 'none';
itemBoardContent.style.display = 'block';
}, 2000);학습 내용:
- Promise와 비동기 처리
- 이미지 로드 이벤트
Promise.all()패턴
핵심 코드:
function waitForImagesToLoad() {
const images = itemBoardContent.querySelectorAll('img.item');
const imagePromises = Array.from(images).map(img => {
return new Promise((resolve) => {
if (img.complete) {
resolve();
} else {
img.addEventListener('load', resolve);
img.addEventListener('error', resolve);
}
});
});
return Promise.all(imagePromises);
}
waitForImagesToLoad().then(() => {
// 로딩 화면 숨기기
});중요 포인트:
img.complete로 이미 로드된 이미지 확인- 각 이미지에 대해 Promise 생성
Promise.all()로 모든 이미지 대기- 에러 발생 시에도 계속 진행
추가 고려사항:
- 드래그 시 시각적 피드백 (
drag-over클래스) - 호버 효과와 커서 변경
- 반응형 디자인 고려 (선택적)
학습 내용:
html2canvas라이브러리 활용- Blob과 파일 다운로드
- Canvas API 기초
구현 목표:
// 캐릭터 영역을 이미지로 캡처
html2canvas(characterArea).then(canvas => {
const link = document.createElement('a');
link.download = 'my-outfit.png';
link.href = canvas.toDataURL();
link.click();
});학습 내용:
- 아이템별 속성 데이터 관리
- 점수 계산 로직
- UI 업데이트
예시 데이터 구조:
const itemScores = {
'three-color-knit': { style: 5, comfort: 3, professionalism: 4 },
'hoodie': { style: 3, comfort: 5, professionalism: 2 }
};학습 내용:
localStorageAPI- JSON 직렬화/역직렬화
- 게임 상태 저장/불러오기
핵심 코드:
// 저장
saveGame() {
const state = {
wornItems: this.wornItems,
positions: { /* ... */ }
};
localStorage.setItem('dressUpGame', JSON.stringify(state));
}
// 불러오기
loadGame() {
const saved = localStorage.getItem('dressUpGame');
if (saved) {
const state = JSON.parse(saved);
// 상태 복원
}
}학습 내용:
- 모달/오버레이 구현
- 단계별 가이드 시스템
- 타이머와 이벤트 조합
구현 아이디어:
- 첫 방문 시 자동으로 튜토리얼 표시
- 다음 버튼 클릭 또는 2초 후 자동 진행
- 스킵 버튼으로 건너뛰기
학습 내용:
- 미디어 쿼리
- 터치 이벤트 처리
- 디바이스 감지
핵심 코드:
if (window.innerWidth < 768) {
showMessage('이 게임은 데스크톱에서만 플레이 가능합니다.');
}원인:
e.preventDefault()누락draggable="true"속성 누락- 이벤트 리스너 등록 순서 문제
해결:
// dragover 이벤트에 반드시 preventDefault 필요
handleDragOver(e) {
e.preventDefault(); // 필수!
}원인:
- z-index 값 누락 또는 문자열로 지정
- position 속성 누락
해결:
item.style.zIndex = zIndex.toString(); // 숫자를 문자열로
item.style.position = 'absolute'; // position 필수원인:
wornItems상태 업데이트 누락- 원본 아이템 참조 손실
해결:
// 삭제 시 반드시 상태도 업데이트
this.wornItems[category] = null;
item.sourceItem.style.display = '';원인:
- 두 메커니즘이 동시에 작동
preventDefault()위치 문제
해결:
- 동적으로
draggable속성 변경 - 움직임 감지로 모드 전환
- 새로운 카테고리 추가: 안경, 목걸이 등 액세서리 카테고리 추가
- 색상 변경: 같은 아이템의 다양한 색상 버전 구현
- 초기화 버튼: 모든 아이템을 제거하고 파자마로 돌아가는 버튼 추가
- 랜덤 코디: 버튼 클릭 시 무작위로 아이템 조합
- 추천 코디: 미리 정의된 코디 세트 적용 기능
- 배경 변경: 계절/장소에 따른 배경 이미지 전환
- JavaScript 클래스와 객체지향 프로그래밍
- DOM 조작 완벽 가이드
- 이벤트 루프와 비동기 처리
- 인트로 (5분): 완성본 시연 및 학습 목표 소개
- 따라하기 (본편): 단계별 코딩 실습
- 트러블슈팅 (10분): 자주 발생하는 오류 해결
- 아웃트로 (5분): 요약 및 추가 학습 방향 제시
- 코드 타이핑 속도 적절히 조절
- 각 코드 블록마다 설명 추가
- 콘솔 출력으로 중간 결과 확인
- 주요 개념은 시각 자료로 보충
- 왼쪽: 코드 에디터
- 오른쪽: 브라우저 (실행 결과)
- 하단: 개발자 도구 콘솔 (필요시)
- 계절별 테마 (여름/겨울 옷)
- 직업별 코디 (개발자/디자이너)
- 레벨 시스템 (잠금 해제)
- TypeScript로 타입 안정성 추가
- React/Vue로 컴포넌트화
- 웹팩으로 번들링 최적화
- PWA로 오프라인 지원
이 프로젝트를 통해 JavaScript의 핵심 개념을 재미있게 학습할 수 있습니다. 단순히 따라하는 것을 넘어, 자신만의 아이디어를 추가하여 확장해보세요!
학습 순서 추천:
- 기본 기능 완성 (Part 1-3)
- 심화 인터랙션 구현 (Part 4)
- UX 개선 (Part 5)
- 자신만의 기능 추가 (심화 과제)
질문이나 피드백은 언제든지 환영합니다! 🎉