01. 토이 프로젝트
JS를 공부하기 좋은 방법 중에 하나는 스스로 토이 프로젝트를 해보는 것입니다.
저 또한 JS만 사용해서 간단한 것들을 만들어 보는 중인데 그 과정을 공유하고자 합니다.
토이 프로젝트
토이 프로젝트란 개발자가 남는 시간을 투자해 무언가를 만드는 작업이다. 거창한 프로젝트가 아니라 말 그대로 장난감(토이)을 갖고 놀 듯 흥미 위주의 프로젝트를 진행하는 것으로 생각하면 된다. 핵심은 업무나 수업을 듣는 시간을 제외하고 여유 시간을 활용해 소기의 목적을 달성하는 것이다. 이 과정에서 새로운 기술을 접하거나 시행착오를 겪으며 자연스럽게 개발 실력이 향상될 수 있다.
비기너에게 도움이 되는 JS 토이 프로젝트를 모아 놓은 사이트가 있습니다.
02. 행맨 게임
저는 행맨 게임이 재밌어 보여서 선택하였습니다.
(영어 학원 가면 다들 한 번쯤은 해 봤잖아요~~)
그런데 무작정 코드가 있어 따라 치는 것보다 한번 분석도 해보고, 개념 정리도 해보고
혼자 이것저것 파헤쳐 볼 예정입니다.
03. 흐름 파악하기
무작정 코드를 작성하기보다 먼저 흐름을 생각하면 많이 도움이 되는데요.
플로우 차트에 관한 개념을 지난 포스팅에서 다뤘었습니다.
이런 순서도 흐름은 이제 저도 습관화 시키려고 합니다.
그리고 플로우 차트를 노션으로 그려보고자 이 블로그를 참고하였습니다.
(노션이 확실히 데이터 베이스 기반 툴이다 보니, 개발자 친화적인 것 같습니다.)
행맨 게임 순서
일단 저도 순서를 한번 생각해 봤습니다. 간략하게
1. 행맨 게임 시작
2. 알파벳을 선택 (맞음 / 틀림)
3. 맞을 경우 알파벳이 빈칸(___)에 나타 난다.
> 수명이 줄어들지 않습니다.
> 다시 알파벳 선택으로 돌아갑니다 (2번)
4. 틀릴 경우 행맨 애니메이팅이 그려진다.
> 수명이 줄어듭니다.
> 행맨 에니매이션이 그려집니다.
> 다시 알파벳 선택으로 돌아갑니다.
5. (2 - 4 과정) 반복합니다.
> 정답이 계속될 경우 WIN
> 기회를 모두 소진했을 경우 lOSE
(예외)
6. 중간에 hint를 선택해서 단어에 대한 힌트를 볼 수 있습니다.
7. 게임을 다시 시작하여 카테고리가 변경될 수 있습니다.
8. 게임이 종료됩니다.
프로그램 순서도
순서도 파악을 한 후 플로우 차트를 그리려고 하니
반복적인 부분은 어떻게 그려야 할지,
연속적으로 이어지는 과정은 어떻게 처리를 해야 할지,
예외는 어떻게 표현을 해야 할지에 대한 물음이 생겨서 고민에 빠졌습니다.
(도움 요청 feat. 개발자 야잴씨)
다른 사람이 짜 놓은 순서도를 보면 어떻겠냐기에
왜 그 생각을 못했지?라고 생각하면서 바로 구글링을 시작했습니다.
코드를 짤 때에 변수를 어떻게 설정해야겠는지에 대한 고민도 있었기에
이렇게 검색을 했습니다.
개발에 관한 것들은 무조건 구글 (네이버 X)
그리고 영어로 치면 개발 관련 문서나 자료가 훨__씬 더 방대합니다.
변수까지 정확하게 그려진 알고리즘을 찾았습니다.
대학 과제 같은 느낌인데 알고리즘이 엄청 자세하고 상세하게 그려져 있어서 도움이 많이 됐습니다.
하나하나 내려가면서 글을 읽었는데요. 루프는 저렇게 그리고, 단계 처리를 어떤 식으로 하는 구나에 대한 감이 잡혔습니다. 그리고 플로우 차트를 이용하니까 머릿속에 알고리즘이 그려졌습니다. 또 코드를 작성할 때 처음 변수가 어떤 것이 필요한지도, 딱 정리가 되었습니다.
이제 이 알고리즘을 토대로 참고 삼아서 저의 행맨 게임 상황에 맞게 다시 그려 보았습니다.
(위 알고리즘은 난이도 설정이라던지, 알파벳을 입력한다던지 하는 상황이 주어졌어서 조금 더 단계가 나뉩니다.)
음 상당히 헷갈리지만 차근차근 노션으로 그렸습니다.
플로우 차트를 그리니 순서가 한눈에 정리가 되는군요!
true와 false만 있다 보니 논리적으로 정리가 됩니다.
04. 코드 파악하기
사실 플로우 차트를 그리기 전에 코드를 한번 분석했었는데, 이해가 안 가는 논리구조가 있었습니다.
그런데 확실히 플로우 차트 그러고 나서 다시 보니 정리가 되는군요.
행맨 게임 프로젝트 코드를 쭉 따라서 읽어나가다가 모르는 부분은 주석으로 써놓았습니다.
그리고 하면서 모르는 내용이나 헷갈리는 것들은 책을 참고해서 다시 보고 JS 이론에 포스팅하며 정리했습니다. 한번 더 숙지해서 저의 것으로 만들기 위해서요.
window.onload = function () {
// 윈도우 온로드를 넣지 않았더니 실행은 되나 appendChild를 넣으려고 할때 실행 되지 않았다. 자바스크립트가 실행 되는 순서에 대해서도 알아보자
// var alphabets = document.getElementById('alphabets');
var alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
// 1. 먼저 변수 설정할 것들을 정해준다. (필요한 변수가 무엇인지 생각)
var categories;
var chosenCaegory;
var getHint;
var word;
var guess;
var guesses = [];
var lives;
var counter;
var space;
// 요소 잡기
var showLives = document.getElementById('mylives');
var showCategory = document.getElementById('categoryName');
var getHint = document.getElementById('hint');
var showClue = document.getElementById('clue');
// 이렇게 하면 안되는구나
// for(let i=0; i<=alphabet.length; i++){
// alphabets.innerHTML = alphabet[i]
// }
// 구조를 만들어 줘야한다.
var buttons = function () {
myButtons = document.getElementById('buttons');
letters = document.createElement('ul')
for (var i = 0; i < alphabet.length; i++) {
letters.id = "alphabet";
list = document.createElement('li');
list.id = 'letter';
list.innerHTML = alphabet[i];
check();
myButtons.appendChild(letters);
letters.appendChild(list);
}
}
// 카테고리 선택
// 이사람은 카테고리를 크게 3개로 정해 놓았다.
var selectCat = function () {
if (chosenCaegory === categories[0]) {
categoryName.innerHTML = "This Chosen Category is 첫번째 카테고리";
} else if (chosenCaegory === categories[1]) {
categoryName.innerHTML == "This Chosen Category is 두번째 카테고리";
} else if (chosenCaegory === categories[2]) {
categoryName.innerHTML === "This Chosen Category is 세번째 카테고리";
}
}
// 단어 빈칸 만들기 (구조를 그려준다.)
// append랑 appendchild 차이 알아보기
// id값을 저렇게 for문으로 추가하면 다중으로 생기지 않나??
result = function () {
wordHolder = document.getElementById('hold');
correct = document.createElement('ul');
correct.setAttribute('id', 'my-word');
for (var i = 0; i < word.length; i++) {
console.log("correct : ",correct);
guess = document.createElement('li');
guess.setAttribute('class', 'guess');
if (word[i] == "-") {
guess.innerHTML = "-";
space = 1;
} else {
guess.innerHTML = "_"
}
guesses.push(guess);
wordHolder.appendChild(correct)
correct.appendChild(guess);
console.log("correct : ",correct);
}
}
// 화면에 차례대로 보일 수 있게 그려주고 있다.
// 생명 (기회) 그려주기
comments = function () {
showLives.innerHTML = "You have" + lives + "lives";
if (lives < 1) {
showLives.innerHTML = "Game Over";
}
for (var i = 0; i < guesses.length; i++) {
if (counter + space === guesses.length) {
showLives.innerHTML = "you win!"
}
}
}
// 행맨 그려주기
var animate = function () {
var drawMe = lives;
drawArray[drawMe]();
}
canvas = function () {
myStickman = document.getElementById('stickman');
context = myStickman.getContext('2d');
context.beginPath();
context.strokeStyle = "#fff";
context.lineWidth = 2;
};
head = function () {
myStickman = document.getElementById('stickman');
context = myStickman.getContext('2d');
context.beginPath();
context.arc(60, 25, 10, 0, Math.PI * 2, true);
context.stroke();
}
draw = function ($pathFromx, $pathFromy, $pathTox, $pathToy) {
context.moveTo($pathFromx, $pathFromy);
context.lineTo($pathTox, $pathToy);
context.stroke();
}
frame1 = function () {
draw(0, 150, 150, 150)
}
frame2 = function () {
draw(10, 0, 10, 600)
}
frame3 = function () {
draw(0, 5, 70, 5)
}
frame4 = function () {
draw(60, 5, 60, 15)
}
torso = function () {
draw(60, 36, 60, 70)
}
rightArm = function () {
draw(60, 46, 100, 50)
}
lefttArm = function () {
draw(60, 46, 20, 50)
}
rightLeg = function () {
draw(60, 70, 100, 100)
}
leftLeg = function () {
draw(60, 70, 20, 100)
}
drawArray = [rightLeg, leftLeg, rightArm, lefttArm, torso, head, frame4, frame3, frame2, frame1];
// 정답 체크
// 클릭한 카드안에 알파벳들이랑 정답이랑 글자 비교하기 indexof()
// 맞추면 counter +, 틀리면 lives -1
// 왜 굳이 counter + space == guesses 를 해야 이길 수 있지??
check = function () {
list.onclick = function () {
var guess = (this.innerHTML)
this.setAttribute('class', 'active');
this.onclick = 'null';
for (var i = 0; i < word.length; i++) {
if (word[i] === guess) {
guesses[i].innerHTML = guess;
counter += 1;
}
}
var j = (word.indexOf(guess))
if (j === -1) {
lives -= 1;
comments();
animate();
} else {
comments();
}
}
}
//플레이
play = function () {
categories = [
["everton", "liverpool", "swansea", "chelsea", "hull", "manchester-city", "newcastle-united"],
["alien", "dirty-harry", "gladiator", "finding-nemo", "jaws"],
["manchester", "milan", "madrid", "amsterdam", "prague"]
];
chosenCaegory = categories[Math.floor(Math.random() * categories.length)];
word = chosenCaegory[Math.floor(Math.random() * chosenCaegory.length)];
console.log(chosenCaegory)
console.log(word)
word = word.replace(/\s/g, "-");
console.log(word)
buttons();
guesses = [];
lives = 10;
counter = 0;
space = 0;
result();
comments();
selectCat();
canvas();
}
play();
// hint
// jquery 에선 index() 함수가 있었는데, index를 구하려면 indexOf()를 써야하는것인가?
getHint.onclick = function () {
hints = [
["1-0", "1-1", "1-2", "1-3", "1-4", "1-5", "1-6", "1-7", "1-8"],
["2-0", "2-1", "2-2", "2-3", "2-4", "2-5", "2-6", "2-7", "2-8"],
["3-0", "3-1", "3-2", "3-3", "3-4", "3-5", "3-6", "3-7", "3-8"]
];
var categoryIndex = categories.indexOf(chosenCaegory);
var hintIndex = chosenCaegory.indexOf(word);
showClue.innerHTML = " Clue : - " + hints[categoryIndex][hintIndex];
};
// reset
document.getElementById('reset').onclick = function () {
correct.parentNode.removeChild(correct);
letters.parentNode.removeChild(letters);
showClue.innerHTML = "";
context.clearRect(0, 0, 400, 400);
play();
}
}
05. 결과물
우선 분석하고 코드를 따라 치면서 완성했습니다.
처음부터 생각하고 코드를 써내려 갈 수도 있겠습니다만 아직은 그럴 단계가 아닌 거 같아서 논리적인 흐름을 숙지하는 과정까지 먼저 트레이닝하려고 합니다. 지금은 빨리빨리 뭔가를 만들어 내기보다는 혼자 생각하는 힘을 기르는 게 먼저 인 것 같습니다.
따라 치는 과정에서도 꽤 많은 오류가 발생하였는데 대부분 오타^^ 였습니다. 😥😥
그리고 작동이 된 것을 확인하고 html과 css를 입혀주었습니다.
See the Pen JS - Hangman Project by bok-world (@bok-world) on CodePen.
05. 마치며
이 모든 작업을 하루 내에 끝낸 게 절대 아닙니다. 사실 회사 다니고 다른 일정도 있고 하다 보니 12일이라는 꽤 오랜 시간을 자투리 시간을 투자해서 공부하고 포스팅까지 마무리합니다. (약 12일 만에 끝내네요.)
처음 토이 프로젝트를 해보는데 안다고 생각했던 것들은 생각보다 잘 알지 못하고, 논리적인 사고의 힘이 중요하다는 것을 깨닫게 되었습니다. 그래서 이론도 다시 찾아보고, 플로우 차트도 꼭 필요하다는 것을 깨닫고 다시 공부하고 스스로 과정을 생각해보고 적어보고 하였습니다. 그러는 과정에서 어떤 식으로 개발 공부를 해야겠다가 조금 감이 잡혀오기 시작했습니다.
남이 작성해 놓은 코드를 보는 일과 분석은 지루한 작업이라고 생각해 대충 넘겨보기 일수였는데 코드 분석을 제대로 해보니 생각할 것들이 많았습니다. 그리고 왜 이렇게 썼지??라고 의문도 생기고요. 이러한 과정도 필수적이라는 것을 깨닫고 다시 마음을 재정비하였습니다.
또한 개발언어는 무엇을 배우던 중요하지 않다고 생각합니다. 사실 진짜 "언어" 일 뿐이고, 중요한 것은 논리적인 흐름입니다. 논리적인 사고와 그 흐름을 알면 어떤 언어로도 작성할 수 있겠다고 확신을 가지게 되었습니다. 로블록스 게임 개발을 할 때와 요가 다이어리 어플을 만들 때 언어만 공부하고 구조도 고민을 하지 않았던 것이 가장 큰 에러였습니다.
꽤 많은 인사이트를 얻었기에 앞으로도 이런 과정을 반복해 보고자 합니다. 결과물을 내고 포스팅을 하며 글로 적는 것 또한 많이 흩어진 정보와 생각들을 정리하는데 필수적인 작업이라고 생각합니다. 저의 이런 과정들이 보시는 분들에게도 여러모로 도움이 되었으면 합니다.
참고한 사이트들
📌 모던 자바스크립트 입문 책
📌 코딩월드뉴스(https://www.codingworldnews.com)
📌 포스팅 속 링크들
'FRONTEND > JS' 카테고리의 다른 글
[JS] 노드 생성과 삽입 createElement & appendChild (0) | 2022.03.14 |
---|---|
[JS] innerHTML vs innerTEXT vs textContent 차이 (0) | 2022.03.13 |
[JS] 속성 값의 읽기와 쓰기 (0) | 2022.03.12 |
[개발의 본질] 플로우 차트 (Flow chart란?) (0) | 2022.03.05 |
[JS] 노드 객체 가져오기 (0) | 2022.03.04 |