1.optional chaining
let obj4 = { name: "soon", content: "내용" };
let obj5 = {
name: "leeeeee",
content: {
age: 26,
},
};
console.log(obj5.content?.asd);
- 옵션 체이닝이란 각 키에 해당하는 value를 확인하고 없을 경우 type에러가 나지 않게 undefined를 던져준다. 리액트에서 사용자의 정보나 다른 데이터를 가져올 때 유용하게 쓸 수 있을 것 같다.
2.todo 리스트 삭제 및 수정 연산
1.css
.board-content div {
border: 1px solid;
width: auto;
min-width: 100px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.updateTextConatiner {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
background-color: rgba(0, 0, 0, 0.2);
display: none;
}
.updatedText {
width: 300px;
height: 150px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
}
2.html
<div>
<input type="text" />
<input type="text" />
<button id="addBtn">enroll</button>
</div>
<div class="board"></div>
<div class="updateTextConatiner">
<div class="updatedText">
<input type="text" /> <input type="text" />
<button class="updateClass">수정하가</button>
</div>
</div>
3.JS
let boardArr = [];
let staticIndex = 0;
let updateIndex = 0;
function createBoard({ name, content, id }) {
this.name = name;
this.content = content;
this.id = id;
}
function addBoard(params) {
let [input01, input02, input03, input04] =
document.querySelectorAll("input");
let board = new createBoard({
name: input01.value,
content: input02.value,
id: staticIndex,
});
boardArr.push(board);
staticIndex++;
console.log(input04);
render();
}
function render(params) {
document.querySelector(".board").innerHTML = "";
boardArr.forEach((a, index) => {
console.log(a);
let div01 = document.createElement("div");
let div02 = document.createElement("div");
let div03 = document.createElement("div");
let div04 = document.createElement("div");
let button = document.createElement("button");
let updateButton = document.createElement("button");
button.innerHTML = "삭제";
updateButton.innerHTML = "수정";
button.onclick = function (e) {
console.log(e.target);
div01.remove();
boardArr.splice(index, 1);
render();
};
updateButton.addEventListener("click", () => {
updateIndex = index;
console.log("dd");
document.querySelector(".updateTextConatiner").style.display =
"block";
});
let { name, content, id } = a;
div02.innerHTML = id;
div03.innerHTML = content;
div04.innerHTML = name;
div01.style.display = "flex";
div01.className = "board-content";
div01.append(div02, div03, div04, button, updateButton);
document.querySelector(".board").append(div01);
});
}
addBtn.onclick = addBoard;
document.querySelector(".updateClass").addEventListener("click", (e) => {
let [input01, input02, input03, input04] =
document.querySelectorAll("input");
boardArr[updateIndex].name = input04.value;
boardArr[updateIndex].content = input03.value;
document.querySelector(".updateTextConatiner").style.display = "none";
render();
});
- 생성자함수
function createBoard({ name, content, id }) {
this.name = name;
this.content = content;
this.id = id;
}
- 게시글이 생성될 때마다 다음과 같은 파라미터를 구조분해할당 으로 받아온다.
- 게시글을 추가하는 함수
function addBoard(params) {
let [input01, input02, input03, input04] =
document.querySelectorAll("input");
let board = new createBoard({
name: input01.value,
content: input02.value,
id: staticIndex,
});
boardArr.push(board);
staticIndex++;
console.log(input04);
render();
}
- 변수는 각각 input01, input02, input03, input04 로 정의하였는데 이는 input태그에서 담아오는 입력값이다.
- input01, input02은 게시글 추가를 위한 이름,내용 input03, input04은 수정을 위한 이름,내용 이다.
- 게시글을 new 키워드를 이용해 새로운 객체로 메모리에 할당해준다. 파라미터는 object형식을 구조분해 할당으로 전달한다.
- 그 후 boradArr에 넣어주고 id에 해당하는 staticIndex를 1 올려준다.
- 마지막으로 게시글을 보여주기 위해 render()를 실행한다.
- 화면을 그리는 함수,삭제하는 기능
function render(params) {
document.querySelector(".board").innerHTML = "";
boardArr.forEach((a, index) => {
console.log(a);
let div01 = document.createElement("div");
let div02 = document.createElement("div");
let div03 = document.createElement("div");
let div04 = document.createElement("div");
let button = document.createElement("button");
let updateButton = document.createElement("button");
button.innerHTML = "삭제";
updateButton.innerHTML = "수정";
button.onclick = function (e) {
console.log(e.target);
div01.remove();
boardArr.splice(index, 1);
render();
};
updateButton.addEventListener("click", () => {
updateIndex = index;
console.log("dd");
document.querySelector(".updateTextConatiner").style.display =
"block";
});
let { name, content, id } = a;
div02.innerHTML = id;
div03.innerHTML = content;
div04.innerHTML = name;
div01.style.display = "flex";
div01.className = "board-content";
div01.append(div02, div03, div04, button, updateButton);
document.querySelector(".board").append(div01);
});
}
- document.querySelector(".board").innerHTML = ""; 는 처음 보드를 초기화 시켜주는 역할을 한다.
- boardArr.forEach 를 통해 배열의 요소 하나하나를 가져온다. 그 다음 div태그와button태그를 생성하여 붙여준다.만약 삭제를 누른다면 해당 게시글의 index(boardArr의 index와 같다)를 이용해boardArr.splice 메소드를 이용해 삭제한다. 그 후 다시 render한다.
- updateButton.addEventListener는 업데이트(수정)버튼을 눌렀을 때 앞서 선언한 updateIndex에 해당 index를 넣는다. 그 후 화면에 가려져 있던 수정 인터페이스를 보여지게 한다.
- 내용을 넣는 방법은 구조분해할당을 이용해 object의 key값을 선언한다. 그 후 innerHTML을 이용해 삽입한다.
- 게시글을 수정하는기능
document.querySelector(".updateClass").addEventListener("click", (e) => {
let [input01, input02, input03, input04] =
document.querySelectorAll("input");
boardArr[updateIndex].name = input04.value;
boardArr[updateIndex].content = input03.value;
document.querySelector(".updateTextConatiner").style.display = "none";
render();
});
- 앞에서 가져온 updateIndex를 이용해 해당 배열의 value를 수정한 후 수정 인터페이스를 다시 none으로 주어 안 보이게 한다.
- 수정한 게시글이 보여져야 하므로 다시 render해준다.
- 시연화면
1.이름 추가

2.이름 수정

3.수정완료

4.삭제

3.묵찌빠 게임
💡 이 게임은 다음과 같은 규칙을 따른다
1.플레이어는 쉬움,보통,어려움에 따라 100000,50000,10000의 소지금을 갖고 시작한다.
2. 컴퓨터는 2000~3000원의 배팅금을 걸고 플레이어도 똑같이 건다
3.둘 중 하나의 소지금이 0원이면 게임이 종료된다.
1.css
.container {
width: 700px;
height: 100%;
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
}
.player-select,
.com-select {
width: 180px;
height: 154px;
background-image: url("./game.png");
background-repeat: no-repeat;
border: 1px solid;
background-size: 440px;
background-position-x: 25px;
}
.player-select-btns {
display: flex;
}
.rock {
background-position-x: 25px;
}
.paper {
background-position-x: -265px;
}
.scissor {
background-position-x: -120px;
}
.game-wrap {
display: flex;
}
.what-difficulty {
margin: 30px;
}
.difficultySelect {
display: flex;
}
.player-select-btns {
width: 300px;
height: 30px;
justify-content: space-between;
}
.player-btn {
width: 20%;
display: flex;
justify-content: center;
align-items: center;
}
.contents-container{
min-width: 300px;
}
2.html
<div class="container">
<div class="difficultySelect"></div>
<div class="game-wrap">
<div class="player-select">당신</div>
<div class="com-select">컴퓨터</div>
</div>
<div class="what-game"></div>
<div class="what-difficulty">
<button class="btn btn-outline-primary difficulty-btn">쉬움</button>
<button class="btn btn-outline-warning difficulty-btn">보통</button>
<button class="btn btn-outline-danger difficulty-btn">어려움</button>
</div>
<div class="alert alert-success contents-container" role="alert">
<h4 style="text-align: center;" class="alert-heading difficulty-text">난이도</h4>
<p style="text-align: center;" class="result">
결과
</p>
<hr />
<p style="text-align: center;" class="subResult mb-0">
내용
</p>
</div>
<div class="whoAttack"></div>
<h2 class="goNextGameText"></h2>
<div class="player-select-btns">
<button type="button" class="btn btn-primary player-btn">가위</button>
<button type="button" class="btn btn-primary player-btn">바위</button>
<button type="button" class="btn btn-primary player-btn">보</button>
</div>
<h1 class="result"></h1>
<div class="cutomized-alert"></div>
</div>
3.JS
let gameArr = ["scissor", "paper", "rock"];
let playerBtns = document.querySelectorAll(".player-btn");
let playerSelect = document.querySelector(".player-select");
let nextGame = false;
let whoHasAttack = "";
let onGoing = false;
let isSelectDifficulty = false;
function playerFunction(money) {
this.money = money;
}
function comFunction(money) {
this.money = money;
}
let player;
let computer;
let difficultyBtn = document.querySelectorAll(".difficulty-btn");
let difficultyText = document.querySelector(".difficulty-text");
difficultyBtn[0].addEventListener("click", () => {
player = new playerFunction(100000);
computer = new comFunction(100000);
difficultyText.innerHTML = `쉬움 입니다! 현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
document.querySelector(".what-difficulty").style.display = "none";
document.querySelector(".cutomized-alert").innerHTML = "";
isSelectDifficulty = true;
});
difficultyBtn[1].addEventListener("click", () => {
player = new playerFunction(50000);
computer = new comFunction(50000);
difficultyText.innerHTML = `보통 입니다! 현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
document.querySelector(".what-difficulty").style.display = "none";
document.querySelector(".cutomized-alert").innerHTML = "";
isSelectDifficulty = true;
});
difficultyBtn[2].addEventListener("click", () => {
player = new playerFunction(10000);
computer = new comFunction(10000);
difficultyText.innerHTML = `어려움 입니다! 현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
document.querySelector(".what-difficulty").style.display = "none";
document.querySelector(".cutomized-alert").innerHTML = "";
isSelectDifficulty = true;
});
function gameEvent(a) {
playerSelect.className = "player-select " + a;
let { value, text } = gameStart(a);
document.querySelector(".result").innerHTML = value;
document.querySelector(".subResult").innerHTML = text;
}
function init() {
gameArr.forEach((a, index) => {
playerBtns[index].addEventListener("click", () => {
if (isSelectDifficulty) {
gameEvent(a);
} else {
document.querySelector(".cutomized-alert").innerHTML =
' <div class="alert alert-warning" role="alert">난이도를 먼저 선택해 주세요!</div> ';
}
});
});
}
init();
function gameStart(a) {
if (nextGame) {
return gameStart2(a);
} else {
let comSelect = gameArr[Math.floor(Math.random() * gameArr.length)];
document.querySelector(".com-select").className = "com-select " + comSelect;
if (a == comSelect) {
return { value: "무승부", text: "리트" };
} else if (
(a == "rock" && comSelect == "scissor") ||
(a == "paper" && comSelect == "rock") ||
(a == "scissor" && comSelect == "paper")
) {
whoHasAttack = "player";
nextGame = true;
return { value: "승리", text: "묵찌빠 시작! 당신이 선공" };
} else {
whoHasAttack = "com";
nextGame = true;
return { value: "패배", text: "묵찌빠 시작! 당신은 후공!" };
}
}
}
function gameStart2(a) {
let comSelect = gameArr[Math.floor(Math.random() * gameArr.length)];
let comMoney = Math.floor(Math.random() * 1000 + 2000);
document.querySelector(".com-select").className = "com-select " + comSelect;
if (a == comSelect) {
if (whoHasAttack == "player") {
player.money += comMoney;
computer.money -= comMoney;
validateGameover(player.money, computer.money);
nextGame = false;
difficultyText.innerHTML = `현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
return { value: "최종승리!", text: "ㅊㅊ" };
} else if (whoHasAttack == "com") {
player.money -= comMoney;
computer.money += comMoney;
validateGameover(player.money, computer.money);
nextGame = false;
difficultyText.innerHTML = `현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
return { value: "패배", text: "ㅠㅠ" };
}
} else if (
(a == "rock" && comSelect == "scissor") ||
(a == "paper" && comSelect == "rock") ||
(a == "scissor" && comSelect == "paper")
) {
whoHasAttack = "player";
nextGame = true;
return { value: "당신이 선공입니다!", text: "ㅊㅊ" };
} else {
whoHasAttack = "com";
nextGame = true;
return { value: "컴퓨터가 선공입니다!", text: "흑흑 너무 슬프다..." };
}
}
function validateGameover(p, c) {
if (p <= 0) {
alert("패배");
window.location.reload();
} else if (c <= 0) {
alert("승리!");
window.location.reload();
} else {
return;
}
}
- 생성자 함수
function playerFunction(money) {
this.money = money;
}
function comFunction(money) {
this.money = money;
}
- 플레이어와 컴퓨터가 각각 돈을 key로 생성자함수가 생성된다.
- 난이도를 선택하는 기능
difficultyBtn[0].addEventListener("click", () => {
player = new playerFunction(100000);
computer = new comFunction(100000);
difficultyText.innerHTML = `쉬움 입니다! 현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
document.querySelector(".what-difficulty").style.display = "none";
document.querySelector(".cutomized-alert").innerHTML = "";
isSelectDifficulty = true;
});
difficultyBtn[1].addEventListener("click", () => {
player = new playerFunction(50000);
computer = new comFunction(50000);
difficultyText.innerHTML = `보통 입니다! 현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
document.querySelector(".what-difficulty").style.display = "none";
document.querySelector(".cutomized-alert").innerHTML = "";
isSelectDifficulty = true;
});
difficultyBtn[2].addEventListener("click", () => {
player = new playerFunction(10000);
computer = new comFunction(10000);
difficultyText.innerHTML = `어려움 입니다! 현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
document.querySelector(".what-difficulty").style.display = "none";
document.querySelector(".cutomized-alert").innerHTML = "";
isSelectDifficulty = true;
});
- 각 기능은 생성자를 제외하곤 동일한 기능이다.
- 난이도를 선택하면 difficultyText.innerHTML를 통해 해당 태그의 내용이 바뀐다.
- document.querySelector(".what-difficulty").style.display = "none"; 를 통해 난이도 선택창을 없앤다.
- document.querySelector(".cutomized-alert").innerHTML = ""; 난이도를 선택하지 않고 게임을 진행했을 때 뜨는 문구를 삭제한다.
- isSelectDifficulty = true; 난이도를 선택 했으므로 게임을 진행 할 수 있도록 true로 바꾸었다.
- 게임 이벤트(가위,바위,보를 눌렀을 때) 함수
function gameEvent(a) {
playerSelect.className = "player-select " + a;
let { value, text } = gameStart(a);
document.querySelector(".result").innerHTML = value;
document.querySelector(".subResult").innerHTML = text;
}
- playerSelect.className = "player-select " + a; 로 html의 이미지를 가위,바위,보중 해당하는 이미지로 바꾼다. 여기서 a는 매개변수로 받아온 배열 하나의 요소(가위,바위,보중 하나)이다.
- gameStart는 value,text를 구조분해 할당 방식으로 return해준다. 이를 구조분해할당으로 변수를 선언한다.
- 결과,결과보조 태그를 해당 값으로 변경해준다.
- 게임 초기화 함수
function init() {
gameArr.forEach((a, index) => {
playerBtns[index].addEventListener("click", () => {
if (isSelectDifficulty) {
gameEvent(a);
} else {
document.querySelector(".cutomized-alert").innerHTML =
' <div class="alert alert-warning" role="alert">난이도를 먼저 선택해 주세요!</div> ';
}
});
});
}
- gameArr.forEach 로 각 배열의 요소를 가져온 후 click이벤트를 부여한다.
- isSelectDifficulty 가 false즉 난이도를 선택하지 않았다면 document.querySelector(".cutomized-alert").innerHTML 를 난이도를 선택하라는 alert 문구로 바꾼다.
- 만약 난이도를 선택했다면 위의 gameEvent함수를 실행시킨다.
- 가위바위보 게임
function gameStart(a) {
if (nextGame) {
return gameStart2(a);
} else {
let comSelect = gameArr[Math.floor(Math.random() * gameArr.length)];
document.querySelector(".com-select").className = "com-select " + comSelect;
if (a == comSelect) {
return { value: "무승부", text: "리트" };
} else if (
(a == "rock" && comSelect == "scissor") ||
(a == "paper" && comSelect == "rock") ||
(a == "scissor" && comSelect == "paper")
) {
whoHasAttack = "player";
nextGame = true;
return { value: "승리", text: "묵찌빠 시작! 당신이 선공" };
} else {
whoHasAttack = "com";
nextGame = true;
return { value: "패배", text: "묵찌빠 시작! 당신은 후공!" };
}
}
}
- nextgame은 가위바위보가 끝났는지를 알려주는 변수다.
- let comSelect = gameArr[Math.floor(Math.random() * gameArr.length)]; 에서 gameArr의 배열안의 인덱스를 랜덤함수로 뽑아 컴퓨터가 낼 형태를 정해준다.
- 게임에서 비긴다면 아무일도 일어나지 않고 다시 버튼을 누르면된다.
- 이겼다면 whoHasAttack = "player";를 주어 선공권을 플레이어가 가져간다.
nextGame = true; 로 다음게임을 넘어갈 권한을 주고
return { value: "승리", text: "묵찌빠 시작! 당신이 선공" }; 로 구조분해 할당을 이용해
key,value를 return해준다.
- 졌다면 반대로 해준다.
- 묵찌빠 게임
function gameStart2(a) {
let comSelect = gameArr[Math.floor(Math.random() * gameArr.length)];
let comMoney = Math.floor(Math.random() * 1000 + 2000);
document.querySelector(".com-select").className = "com-select " + comSelect;
if (a == comSelect) {
if (whoHasAttack == "player") {
player.money += comMoney;
computer.money -= comMoney;
validateGameover(player.money, computer.money);
nextGame = false;
difficultyText.innerHTML = `현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
return { value: "최종승리!", text: "ㅊㅊ" };
} else if (whoHasAttack == "com") {
player.money -= comMoney;
computer.money += comMoney;
validateGameover(player.money, computer.money);
nextGame = false;
difficultyText.innerHTML = `현재 자금은${player.money}이고 컴퓨터의 자금은${computer.money}입니다!`;
return { value: "패배", text: "ㅠㅠ" };
}
} else if (
(a == "rock" && comSelect == "scissor") ||
(a == "paper" && comSelect == "rock") ||
(a == "scissor" && comSelect == "paper")
) {
whoHasAttack = "player";
nextGame = true;
return { value: "당신이 선공입니다!", text: "ㅊㅊ" };
} else {
whoHasAttack = "com";
nextGame = true;
return { value: "컴퓨터가 선공입니다!", text: "흑흑 너무 슬프다..." };
}
}
- let comMoney = Math.floor(Math.random() * 1000 + 2000); 로 컴퓨터가 배팅할 금액을 2000~3000원 사이로 정해준다.
- 같은 걸 냈다면whoHasAttack을 통해 누가 선공권을 가지고 있는지 판별한다. 판별이 끝나면 돈계산을 하고 validateGameover을 이용해 각 돈이 0원 이하인지 확인한다. 계속 게임을 진행할 수 있다면
nextGame = false;을 줘서 다시 가위바위보 게임으로 돌아간다.
- 만약 다른걸 냈다면 whoHasAttack만 바꿔주고 nextGame = true;로 주며 계속 묵찌빠를 진행할 수 있도록 한다.
- 게임의 종료여부를 알려주는 함수
function validateGameover(p, c) {
if (p <= 0) {
alert("패배");
window.location.reload();
} else if (c <= 0) {
alert("승리!");
window.location.reload();
} else {
return;
}
}
- 매개변수로 플레이어의 남은돈,컴퓨터의 남은돈을 가져와 승패를 좌우한다.
- 만약 끝났다면 알림창을 띄어주고 새로고침을 통해 게임을 재시작한다.
- 시연화면
1.게임 대기

2.게임 시작(난이도 쉬움)

3.가위바위보 승리

4.묵찌빠에서 다른걸 내고 내가 승리

5.묵찌빠에서 다른걸 내고 내가 패배

6.묵찌빠에서 패배

7.난이도를 선택하지않고 게임 진행

8.게임 시연영상(gif)

4.느낀점
😢 사실 묵찌빠게임을 처음 만들었을 때 너무 욕심을 부렸다. 가위바위보가 끝나면 팝업창이 띄워져야 되고 몇초 후에 게임이 시작해야하며 애니메이션도 넣고 등등… 결과는 당연히 좋지 않았다. 갖가지 오류가 터지며 수습이 불가능했다.. 만만히보고 함수형으로 만들지 않고 절차형으로만 해서 그런것도 있다.
다 지우고 함수를 만든 후 전역변수를 상태를 관리해가면 만드니 단번에 성공했다.
항상 기초적인 부분을 끝내고 모듈을 추가하는 방법이 베스트임을 상기시켜주는 프로젝트였다.