개발/클론코딩

JavaScript로 테트리스 만들기, 코린이가 코린이를 위한 A-Z 설명

걍판자 2023. 2. 5. 00:05
반응형

자바스크립트를 이용해 웹 테트리스를 만들어 보았다. 초심자를 위해 친절하게 설명하였다.

js를 사실상 이 영상으로 처음 하면서 배운 것들, 헷갈렸던 것들, 각 코드의 설명과 전체적인 흐름을 기록했다. 이 영상을 보고 클론코딩하다가 이해가 안 되는 분들은 이 글을 참고하면 좋을 것 같다.

각각의 코드들에 주석을 달아 이게 어떤 의미를 담고 있는지 나중에 까먹었을때 내가 보려고  상세하게 설명하였다.

 

출처: https://www.youtube.com/watch?v=1lNy2mhvLFk&list=WL&index=2 

데브리님의 자바스크립트 테트리스 만들기 영상을 바탕으로 만들었다.

 

 

우선 전체코드는 아래와 같다.

 

index.html

<!DOCTYPE html>
<html lang="en">
	<!--lang 속성은 자동번역과 음성번형을 위한 국가를 지정하는 속성이다. 여기에서는 en으로 영어로 지정하였다. -->
	<head>
		<meta charset="UTF-8">
		<!--라이브러리처럼 다양한 속성 표기를 위해 meta가 쓰인다.  그리고 charset은 문자를 어떻게 인코딩할지 그 방식을 정한다. 여기서는 일반적으로 자주 쓰는 UTF8 방식이 쓰였다.--> 
		
		<meta name="viewport" content="width=device-width,inital-scale=1.0">
		<!--name에서 viewport를 태그하면 기기에 따른 크기조절을 나타내고 후에 content에서 이 창을 여는 장치넓이로 설정시킨다. -->
		<title>테트리스</title>
		<!--제목표시줄 및 페이지 탭의 제목으로 사용, 검색엔진에서 표시,2개이상 사용불가능한 title 태그-->
		<link rel="stylesheet" href="css/style.css">
		<!--link태그에서 rel에 속성값을 넣고 스타일시트로 활용할 외부 리소스 불러옴
		  href에서 외부리소스의 url 명시한다-->
	</head>
	
	<body>
		<div class="wrapper">
			<!--class태그의 wrapper를 통해 여러 요소를 grouping한다-->
			<div class="game-text">
				<!--class태그를 통해 .으로 정의된 css 파일의 class를 불러올수 있음-->
				<span>게임종료</span>
				<button>다시시작</button>
			</div>
			<div class="score">
				0
				<!--class태그를 통해 .으로 정의된 css 파일의 class를 불러올수 있다-->
			</div>
			<div class="playground">
				<!--class태그를 통해 .으로 정의된 css 파일의 class를 불러올수 있다-->
				<ul></ul>
				<!--ul 사이에 빈공간 없어야지 사이에 텍스트값 있는걸로 인식이 안된다.-->
				<!--고의적으로 안이 텅빈 ul태그를 테트리스의 한 칸으로 사용힌다.-->
			</div>	
		</div>
		<script src="javascript/tetris.js" type="module"></script>
		<!--스크립트 태그를 통해 javascript 사용한다. src를 통해 위치시키고 모듈타입으로 지정해야 tetris.js에서 import 가능 -->
	</body>
</html>

style.css

* {
	margin: 0;
	padding: 0;
}
/*전체선택자로 margin은 바깥 여백, padding은 내용과 테두리 사이 간격 의미한다. 여기서 간격은 없다.*/
 
ul{

	list-style:none;
}
/*UL태그의 속성 지정, list-style을 none으로 지정해줌으로써 ul 앞에 나오는 점을 없앰*/
body{
	height: 100%;
	/*정해진 블럭에서 차지하는 높이다.*/
	overflow: hidden;
	/*overflow를 hidden으로 지정해줌으로써 화면 넘어갈 경우 걍 숨겨버린다.*/
}

.game-text{
	display: none;
	justify-content:center;
	align-items: center;
	flex-direction: column;
	position: fixed;
	width: 100%;
	height: 100%;
	background: rgba(0,0,0,0.7);
	left: 0;
	top: 0;
	color: #fff;
	font-size: 50px;

}

.game-text > button{
	padding: 0.5rem 1rem;
	color: #fff;
	background: salmon;
	border: none;
	cursor: pointer;
	/*재시작 버튼을 표시한다.*/

}

.score{
	text-align: center;
	font-size: 36px;
	margin-bottom: 2rem;
}

.playground > ul {
	border:1px solid #333;
	width: 250px;
	margin: 0 auto;
	/*모든 블럭을 합친 전체 칸의 크기다.*/
}

.playground > ul > li {
 	width: 100%;
	height: 25px;
	/*세로 한 줄의 크기다*/
}

.playground > ul > li > ul {
 	display: flex;
}

.playground > ul > li > ul > li {
	width: 25px;
	height: 25px;
	outline: 1px solid #ccc;
	/*테트리스 블럭에서 최종적으로 한 칸이 되는 블럭 단위의 크기를 설정하였다.*/
} 

.tree{
	background: #67c23a;
}
.bar{
	background: salmon;
}
.square{
	background: #2c82c9;
}
.zee{
	background: #e6a23c;
}
.elLeft{
	background: #8e44ad;
}
.elRight{
	background: #16a085;
}
/*각 클래스별 색깔을 숫자로 지정하거나 단어로 지정한다*/

 

 

teris.js

import BLOCKS from "./blocks.js"

//BLOCKS를 JS파일 blocks.js에서 임포트 합니다. 즉 block.js에서 블럭 데이터 가져 옴

// DOM
const playground = document.querySelector(".playground > ul");
const gameText=document.querySelector(".game-text");
const scoreDisplay=document.querySelector(".score");
const restartButton=document.querySelector(".game-text > button");

/*각각 다른 위치에 있는 첫번째 요소를 받아와 각각의 이름에 할당합니다. 여기서.으로 시작하였기 때문에 클래스를 받아왔습니다. id로 가져오기
위해서는 #을 앞에 붙이는 걸 이용힙니다. 또한 > 기호로 그 안에 세부 내용을 찾게 합니다.*/

// Setting
const GAME_ROWS = 20;
const GAME_COLS = 10;

//varicables
let score = 0;
let duration = 500;
let downInterval;
let tempMovingItem;

//let는 const와 달리 변수 재할당이 가능한 변수 선언문 입니다.


const movingItem = {
    type: "",
    direction: 0,
    top: 0,
    left: 3,
};

//현재 떨어지고 있는 블럭의 모양과 방향과 위아래 위치, 좌우 위치 표시한다.

init()
// init을 통해 시작합니다.


// fUNCTIONS
function init() {
    tempMovingItem={ ...movingItem };
	//일명 three dots 문법으로 안에 있는 것을 전달받아 각각의 요소를 배열로 변환한다. 안의 값만 가져온다.
    for (let i = 0; i < GAME_ROWS; i++) {
        prependNewLine();
		//시작하고 나면 새로운줄을 설정한 높이만큼 생성한다.
    }
    generateNewBlock();
	//새로운 블럭 나와랏!
}

function prependNewLine() {
    const li = document.createElement("li");
    const ul = document.createElement("ul");
	//각각 li랑 ul이라는 li,ul요소를 만든다. 앞부분에서 이름을 정하고 뒷부분에서 어떤 요소를 만드는지 정한다.
    for (let j = 0; j < GAME_COLS; j++) {
        const matrix = document.createElement("li");
        ul.prepend(matrix);
		//matrix라는 li 요소를 만들고, 각각의 ul에 자식으로써 li를 가로길이만큼 추가한다.
    }
    li.prepend(ul);
	//위에서 정의된 li에 반복문안에서 만든 cols 만큼의 칸을 가진 것을 추가하고 
    playground.prepend(li);
	//li라는 줄 하나를 마침내 추가한다.
	//ul에 li가 가로길이만큼 있고, playground라는 ul 하나에 li가 rows만큼 있다. 그리고 그 li안에 각각 한칸을 나타내는 ul이 cols 만큼 가로에 있다.
}
//새롭게 1줄을 만들어주는 코드

function generateNewBlock(){
	//새로운 블럭을 생성하는 코드
	clearInterval(downInterval);
	//기존의 블럭 계속 내려가게 하던 downInterval을 중단한다.
	downInterval=setInterval(()=>{ moveBlock('top',1)},duration)
	//새로운 블럭 downinterval을 시작한다
	//setinterval은 첫번째 인자에 어떤 함수를, 두번째 인자에 그걸 반복해서 실행할 간격을 ms 숫자로 받는다. 
	//그리고 이는 화살표 함수다. 화삺표 함수는 (인자)=>함수; 형태이다.
	//즉 donwninterval을 duration마다 moveblock(top,1)해주는 함수로 지정한 것이다.
	const blockArray = Object.entries(BLOCKS);
	//entries 함수를 통해 blocks의 모든 요소를 key:value로 blockarray에 받아온다.
	const randomIndex = Math.floor(Math.random()*blockArray.length)
	//어떤 블럭을 생성할지 랜덤으로 정하는 과정이다. 블럭의 개수 그러니까 0~블럭의 종류(blockarray.length) 사이의 수를 RANDOM으로 정해 randomindex에 넣는다.
	movingItem.type= blockArray[randomIndex][0]
	//그렇게 정한 randomIndex는 blockArray의 첫번째 배열 인덱스과 되어 블럭의 종류를 결정한다. 또한 두번째 배열값을 0으로 정해 그렇게 정한 블럭의 이름을 movingItem.type으로 지정한다.
	movingItem.top=0;
	movingItem.left=3;
	movingItem.direction=0;
	//처음 블럭이 생성될때 위치와 방향을 movingItem의 값들을 통해 정해준다.
	tempMovingItem={...movingItem};
	//그렇게 정한 블럭의 종류와 위치 방향을 tempmovingitem에 넣어 현재 생성되는 item인 tempmovingitem에 지정한다.
	
	renderBlocks()
	//화면에 모양을 보여주고 결정하는 함수를 호출한다.
}



function renderBlocks(moveType="") {
	//moveType이라는 파라미터가 있을때만 별도의 과정을 통해 처리한다.
    const { type, direction, top, left } = tempMovingItem;
	//여기서 각각의 값에 구조분해하여 할당한다.불러온 변수들을 바로바로 사용할 수 있기 위함. 하나하나 접근하기 귀찮음
	const movingBlocks =document.querySelectorAll(".moving");
	//아래에서 사용하기 위한 moving이라는 class가진것들 모두  movingblocks로 가져와 추가 관리
	//여기서 moving은 원래 정의된  클래스가 아닌 밑의 classlist.add에서 만들어진건데, renderblock 할때 이동하면 겹쳐서 색칠되는 것을 막기 위해 moving 클래스를 부여해 구별하는 것이다
	movingBlocks.forEach(moving => {
		moving.classList.remove(type,"moving");
		//movingblocks 즉, moving class를 가지는 모든 객체에서 type과 moving 클래스를 제거한다.
		//화살표함수가 최초로 사용되었는데 a.foreach(b->c)면 a를 b라고 선언하고 그것에 대한 c를 실행한다는 의미이다.
		//즉 움직이고 나서 그자리에 있었던 애들은 모양을 담는 값과 moving 클래스를 제거해준다. 그러면 빈값이 되어 색칠이 지워진다.
	})
	
	BLOCKS[type][direction].some(block => {
	// tempmovingitem에서 불러와 변수값으로 선언한것들 , 블럭의 모양과 방향을 불러온다.
	//some 함수 사용 한번이라도 true값이 리턴되면 foreach같은 순회 멈추기위해 foreach 대신 사용
	//BLOCKS[type][direction]을 block으로 불러오고  
	const x = block[0] +left;
	const y = block[1] +top;	
	//block[0]과 [1]값은 블럭한칸의 x좌표와 y좌표의미 이를 x와 y로 불러온다, 이동중이라면 이동시켜서 불러온다.  	
	
	const target = playground.childNodes[y]? playground.childNodes[y].childNodes[0].childNodes[x] : null;
	//배경의 위치가 지정된 블럭에 접근해 해당블럭이 위아래 배경 한계를 넘지 않는다면 해당 블럭 li들 그렇지 않다면target을 null값으로 정한다.
	//타겟은 배경 하나하나를 채우는 블럭이다.
		const isAvailable = checkEmpty(target);
	//isavailable함수로 해당 블럭이 위의 target에서 범위를 벗어나 undefined 되어 있는 상태인지 정한다.	
	if(isAvailable){
		//만약 움직일 수 있는 상태라면 해당 타겟에
		target.classList.add(type,"moving")
		//여기서 type명과 "moving"이라는 클래스가 추가된다!
	}
	else{
		//해당 블럭이 벽을 넘어가 버려 움직일수 없는 상태라면
		tempMovingItem={...movingItem}
		//현재 움직이는 아이템의 모양, 위 아래 길이, 방향을 원복시킨다.
		if(moveType ==='retry'){
			clearInterval(downInterval)
			showGameoverText()
			//===는 3항자 문법인데 타입형태까지 같은지 비교한다.
			//아무튼 그렇게 해서 movetype이 retry라면 downinterval 함수를 중단하고, 게임오버 텍스트를 보여준다.
			//renderblocks의 인수가 움직일 수 없는 상태라서 retry 받았는데 여전히 움직일 수 없는 상태로 또 retry받았다? 더이상 못움직이네! 게임끝났네
		}
		setTimeout(()=>{
			renderBlocks('retry')
			if(moveType === "top"){
				//여기서 top은 문자열로 movingitem의 숫자값 top이랑은 다르다. top이라는 상태는 더이상 블럭이 아래에 닿아 움직일 수 없는 상태인지 확인하는 것이다.
				seizeBlock();
			}
			
		},0)
		//settimeout은 실행시킬 함수, 몇초 뒤에 실행시키는 지를 인자로 받는다.
		//그래서 인자 없는 renderblocks 부터 밑의 if문을 0초컷으로 작동시키는 것이다.
		//0초 후에 실행시키는 게 무슨 소용인가요? 라고 생각하면 자바스크립트의 비동기 시스템을 생각해야 한다. 0초 후에 함수를 실행시키라는 뜻은 다른 함수 스케쥴링이 끝나고 나서 바로 실행해달라는 뜻이다.
		//그러니까 다른 함수 실행이 끝나고 나면 renderblocks를 retry로 재귀호출하고 movetype이 top, 그러니까 ~면 블럭을 움직일 수 없게 굳힌다.
		//이벤트 스택 계속 쌓이는 에러 막기 위함이다. 이게 없으면 계속 나온다.
		return true;
	}
	})
	movingItem.left=left;
	movingItem.top=top;
	movingItem.direction=direction;
	//방향 변경 가능하다고 여겨지면 그제서야 현재 떨어지는 블럭 바꿔줌
		
}

function seizeBlock(){
	const movingBlocks =document.querySelectorAll(".moving");
	movingBlocks.forEach(moving => {
		moving.classList.remove("moving");
		moving.classList.add("seized");
	})
	checkMatch()
	//이미 쌓은 블럭을 더 이상 움직일 수 없게 굳히는 함수다. 

}

function checkMatch(){
	//한줄 완성되었나 확인하는 함수
	const childNodes = playground.childNodes;
	childNodes.forEach(child=>{
		let matched = true;
		child.children[0].childNodes.forEach(li=>{
			if(!li.classList.contains("seized")){
				matched=false;
				//모두 seized 가지고 있는지 확인
			}
		})
		if(matched){
			child.remove();
			//매치되면 새로 줄만들고 기존줄은 다 없애기
			prependNewLine()
			score++;
			scoreDisplay.innerText=score;
		}
	})
	generateNewBlock()
}



function checkEmpty(target){
	if(!target || target.classList.contains("seized")){
		return false;
	}
	return true;
	//타겟이 되는 배경블럭 1칸이 존재하고, 그 블럭의 classlist에 seized가 없다면 true 있다면 false를 반환한다.

}

function moveBlock(moveType, amount){
	tempMovingItem[moveType] +=amount;
	//현재 떨어지고 있는 아이템인 tempmovingitem의 방향을 amount만큼 수정한 다음에 renderblocks를 호출한다.
	//원래 tempmovingitem에는 type,direction,top,left 값을 가졌음을 기억하자
	renderBlocks(moveType);
	

}

function changeDirection(){
	const direction=tempMovingItem.direction;
	direction === 3 ? tempMovingItem.direction=0 : tempMovingItem.direction +=1
	renderBlocks()
	//방향바꾸는 함수로 호출되면 tempmovingitem의 값을 1만큼 더해서 바꾸고 renderBlocks함수를 호출한다. 단 3이라면 4를 넘을 수 없으니 0으로 값을 정한다.
}

function dropBlock(){
	clearInterval(downInterval);
	downInterval = setInterval(()=>{
		moveBlock("top",1)
	},10)
//고의적으로 블럭 떨구는 함수로 계속 떨어지게 만드는 downInterval함수를 중단하고, 훨씬 빠르게 떨어지게 한다.
}

function showGameoverText(){
	gameText.style.display="flex"
//게임 오버되면 index.html에 있는 게임 종료 글짜를 보여준다. 
}

//event handling
document.addEventListener("keydown", e => {
	switch(e.keyCode){
		case 39:
			moveBlock("left", 1);
			break;
		case 37:
			moveBlock("left", -1);
			break;
		case 40:
			moveBlock("top",1);
			break;
		case 38:
			changeDirection();
			break;
		case 32:
			dropBlock();
			break;
		default:
			break;	
	}
	//keydown 즉, document에서 버튼이 눌렸을때 e라는 이벤트를 처리합니다.
	//e라는 이벤트의 키보드 값을 switch case 문으로 숫자로 방향키를 표기
	//좌우하키를 누르면 방향과 그 값을 인수로 moveblock 함수에 전달한다. 
	//스페이스키를 누르면 떨구고 위쪽 방향키를 누르면 방향을 바꾸는 함수를 호출한다.
	

})
restartButton.addEventListener("click",()=>{
		playground.innerHTML = "";
	//playground안의 html 즉 게임에서 사용된 보여주는 각칸의 블럭들을 무로 되돌린다.
		gameText.style.display="none"
	//게임오버 글씨를 없앤다
		init()
	//시작함수를 다시 호출해 다시 게임을 시작한다.
})

 

blocks.js

const BLOCKS = {
	//BLOCKS 라는 블럭 상수를 선언하고, 그안에 KEY:VALUE로 객체 선언한다
    tree: [
        [[2, 1],[0, 1],[1, 0],[1, 1]],
        [[1, 2],[0, 1],[1, 0],[1, 1]],
        [[1, 2],[0, 1],[2, 1],[1, 1]],
        [[2, 1],[1, 2],[1, 0],[1, 1]]
    ],
	//각각 KEY로 이름을 정의하고 블럭들이 어떻게 생겼는지 각각 [x좌표, y좌표]안에 넣고 각각 돌렸을때 어떻게 되는지도 정의해준다. 
	square: [
        [[0, 0],[0, 1],[1, 0],[1, 1]],
		[[0, 0],[0, 1],[1, 0],[1, 1]],
		[[0, 0],[0, 1],[1, 0],[1, 1]],
		[[0, 0],[0, 1],[1, 0],[1, 1]]

    ],
	bar: [
        [[1, 0],[2, 0],[3, 0],[4, 0]],
        [[2, -1],[2, 0],[2, 1],[2, 2]],
		[[1, 0],[2, 0],[3, 0],[4, 0]],
        [[2, -1],[2, 0],[2, 1],[2, 2]]

    ],
	zee: [
        [[0, 0],[1, 0],[1, 1],[2, 1]],
		[[0, 1],[1, 0],[1, 1],[0, 2]],
		[[0, 1],[1, 1],[1, 2],[2, 2]],
		[[2, 0],[2, 1],[1, 1],[1, 2]]

    ],
	elLeft: [
        [[0, 0],[0, 1],[1, 1],[2, 1]],
        [[1, 0],[1, 1],[1, 2],[0, 2]],
        [[0, 1],[1, 1],[2, 1],[2, 2]],
        [[1, 0],[2, 0],[1, 1],[1, 2]],
    ],
	elRight: [
        [[1, 0],[2, 0],[1, 1],[1, 2]],
		[[0, 0],[0, 1],[1, 1],[2, 1]],
		[[0, 2],[1, 0],[1, 1],[1, 2]],
		[[0, 1],[1, 1],[2, 1],[2, 2]]

    ]
}


export default BLOCKS;
//BLOCKS 하나를 EXPORT해서 나중에 다른 파일에서 IMPORT 할 수 있게 내보냄

 

 

하지만 기존의 코드에는 개선되었으면 좋겠는 버그들이 있다.

 

1. 다시 시작을 눌렀음에도 기존의 점수가 리셋되지 않는다.

 

2. 게임이 종료 되었음에도 블록이 계속 내려온다.

 

게임이 종료 되었음에도 좌우키를 꾹 누르면 블록이 내려오는 걸 확인할 수 있다.

위와 같이 좌측에 보라색 꺾인 모양이 게임이 끝났음에도 내려오고 있다.

 

 

3. 블럭이 벽에 닿았을 때 그쪽 벽으로 꾹 누르면 벽이 잠깐씩 뚫렸다가 돌아온다.

 

다음번 게시물에는 이 3가지 버그를 고치는 법에 대해 포스팅할 것이다.

 

 

 

여담

클론코딩을 하고 따라칠 때 다시 보니 이해가 안 되는 부분들이 꽤 많았다. 강의를 들으면서 타이핑을 하다 보면 계속 일시정지를 누르고 따라치게 되고, 그러면서 강의의 흐름과 내 머릿속 이해의 흐름이 하나의 물줄기를 형성하지 못하고 역류하는 경우가 생겼다. 그로 인해 강의를 볼 때는 이해가 됐는데 내가 입력하고 보니 지식이 그사이 증발해서 알아볼 수 없는 웃지 못할 경험도 많이 했다. 그러다 보니 강의시간은 1시간이지만 실제로 완성하고 코드를 이해하는 데는 꽤 오래 걸렸다. 그리고 그 코드를 개선하여 기능을 추가하는 데에는 더 오래 걸렸다. 내가 이해했다고 생각한 코드가 내가 짠 코드가 아니다 보니 헤매는 경우가 많았다.

예전에는 html, css,js를 아는 척만 했다. 진짜 아는 게 아니라 안다고 착각했다. 특기학교에서 잠깐 배웠을 테니 어느 정도 할 거야 라거나 막연히 프런트엔드는 쉬우니까 같은 안일한 생각으로 스스로를 기만했다. 알고 보니 내가 모르는 것들이 많았다. 따로 떨어져 있는 자바스크립트와 html css 파일이 어떻게 서로 상호작용 할 수 있는지 같이 말이다. 코딩할 때는 오히려 알지 못했고, 나중에 코드를 모아 보면서 이해하는 과정에서 얻게 된 것들이 있다. 
   html과 자바스크립트는 html의 class나 id를 queryselect하는 DOM과 innerHTML로 한다는 것을 알게 되었다. css와 html도 마찬가지로 class와 내부의 온점과 >로 접근한다는 것을 깨달았다.  서로 다른 자바스크립트 파일끼리는 export와 import로 불러올 수 있다는 것을 학습했다. 어릴 때의 블록코딩(다양한 파일을 사용하지 않는다)의 여파로 서로 다른 파일끼리 어떻게 상호작용하는지 궁금했었는데 좋은 답을 얻게 되었다.
구조체 문법은 1학년때 글로만 배우고 직관적으로 이해가 잘 되지 않았는데, 이걸 진행하면서 어떻게 쓸 수 있을지 알게 되었다. three dots 문법이라 불리는 온점 3개 찍는 문법이 구조체 문법 중 가장 덜 직관적이라 낯설고 신기하게 다가왔다.
 화살표 함수는 기존의 파이썬이나 c++문법에서 보지 못한 형태라 많이 해맸다. 직관적으로 저 화살표는 뭐고 빈괄호는 뭐고 중괄호는 뭔지 이해가 되지 않았기 때문이다. 지금은 오히려 화살표 문법이 익숙해져서 나중에 다른 프로젝트를 할 때도 찾을 것 같다.

프런트 엔드를 사용하는 새로운 접근 방식도 많이 알게 되었다.
 자바스크립트로 만든 다른 테트리스 게시글은 대부분 캔버스를 사용했는데 여긴 특이하게 캔버스를 사용하지 않고 li, ol 등의 태그만 사용했다. li 같은 태그에도 각 태그에 사각형을 할당하고 배열하면 캔버스를 쓴 것과 같은 이미지효과를 낼 수 있는지 처음 알았다. 기존에 시각적 피드백을 줄려면 Canvas나 이미지 파일을 따로 로드해서 써야만 하는 줄 알았는데 li를 사용하고 거기에 css로 배열을 만들어 판을 만들어내다니. 혼자서는 할 수 없었을 발상이다. 이미 프런트엔드에 익숙한 사람들은 당연하게 생각할 수 있는 부분이지만, 코린이인 나한테는 굉장히 신박하게 다가왔다.
 난 처음에 테트리스 블럭을 한 줄 없애는 데 있어 없애는 줄 위의 모든 줄이 한 칸 위의 줄로 정보를 업데이트하는 방식을 쓸 줄 알았는데 그냥 li들을 삭제하고 새로 만드는 방식을 쓰다니 힙하고 훨씬 효과적인 방식이라고 생각한다.

원래는 코드 한 줄 한줄 따로 떼어서 분석하려 했으나 코드의 연속성적인 부분이나, 티스토리 내부에서 코드블록을 쓰는데 불편함이 있어 그냥 전부 주석처리했다. 한 줄 한 줄을 옮겨 주석으로 쓰는 데 있어 아주 기초적인 태그부터 서술했다. 진짜로 처음 하는 사람들은 당연하듯이 있는 코드 한 줄도 제대로 설명을 듣지 않으면 오랫동안 싸매게 되기 때문이다.  다음에 게시글을 올릴 때는 가독성 있는 방법을 찾아야 할 것 같다.

 

 

 

 

 

다음글 링크!

2023.04.22 - [분류 전체보기] - JavaScript로 테트리스 만들기, 코린이가 코린이를 위한 A-Z 설명2

 

JavaScript로 테트리스 만들기, 코린이가 코린이를 위한 A-Z 설명2

지난 게시글 링크! 2023.02.05 - [코딩] - JavaScript로 테트리스 만들기, 코린이가 코린이를 위한 A-Z 설명 JavaScript로 테트리스 만들기, 코린이가 코린이를 위한 A-Z 설명 출처: https://www.youtube.com/watch?v=1lN

juneforpay.tistory.com

 

 

반응형