
๊ฐ์์๊น ์นด๋ ๋ง์ถ๊ธฐ
๋ฉ๋ชจ๋ฆฌ ๊ฒ์์ด๋ผ๊ณ ๋ ํ๊ณ ใ
ใ
ใ
๊ธฐ์ต๋ ฅ ์นด๋๊ฒ์์ด๋ผ๊ณ ๋ ํ๋๋ฐ....
์์๋ง ๋ฃ์ผ๋ฉด ์ฌ์ฌํ๋๊น
๊ณผ์ผ์ด๋ชจ์ง๋ ํจ ๊ฐ์ด ๋ฃ์ด๋ณด๊ฒ ์ต๋๋ค
์๋ฐ ์คํฌ๋ฆฝํธ ์ฐ์ต ๊ฒธ ์๊ฐ ๋ ๋ ํด ๋ณด์ ใ
ใ
ใ
ใ
โ๋
ธ๊ฐ๋ค ๋ฐฉ์ง ํํ โ
์นด๋๋ 12์ฅ(6์)๋ง ๋ง๋ค์
1. ์์๋
draw.io๋ก ์์ฑํ๊ณ , svg ํ์ผ๋ก ๋ด๋ณด๋ด๊ธฐ ํจ.
PNG๋ JPEG๋ ํด์๋ ์ค์ ์ํ๋ฉด ๊นจ์ง๋ ์ฃผ์
2. ์ ํ ํ ์นด๋ ์์ฑํ๊ธฐ
์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์๋, CSS๋ก ๋ค์ง๊ธฐ๋ฅผ ํด ์ค๊ฒ์
๋๋ค....
2-1. CSS
ํด๋์ค์ด๋ฆ | ์ญํ | CSS |
card | ์นด๋ ์ ์ฒด๋ฅผ ๊ฐ์ธ๋ ์ปจํ ์ด๋ | ์นด๋ ํฌ๊ธฐ, 3D ์์ ์ค์ |
card-inner | ์นด๋ ์์ ์์์ ์นด๋ ์,๋ท๋ฉด ๊ฐ์ธ๋ ๋ด๋ถ ์ปจํ ์ด๋ | ์นด๋ ๋ค์ง์ ๋ transform: rotateY() ํ์ ์ถ |
card-front | ์นด๋ ์๋ฉด(๋ค์งํ๊ธฐ ์ ) | ๋จ์ ๊ทธ๋ผ๋ฐ์ด์
์ ์ฉ backface-visibility: hidden; ์ ์ฉ |
card-back | ์นด๋ ๋ท๋ฉด(๋ค์งํ ํ) | 12๊ฐ์ง ์์ ์ ํ์ค ๊ฒ |
.card {
display: inline-block;
margin-right: 20px;
margin-bottom: 20px;
width: 70px;
height: 100px;
perspective: 500px; /* 3D ํจ๊ณผ๋ฅผ ์ํ ์์ ์ค์ */
}
.card-inner {
position: relative;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.8s;
transform-style: preserve-3d;
}
.card-front, .card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 12px; /* ๋ถ๋๋ฌ์ด ๋ฅ๊ทผ ๋ชจ์๋ฆฌ */
border: 2px solid rgba(0, 0, 0, 0.1); /* ์ฐํ ํ
๋๋ฆฌ */
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1), 0 4px 10px rgba(0, 0, 0, 0.1);
/* ๋ถ๋๋ฝ๊ณ ์
์ฒด์ ์ธ ๊ทธ๋ฆผ์ */
}
/* ์นด๋ ์๋ฉด */
.card-front {
background: linear-gradient(45deg, #001f3d, #003366); /* ๋จ์ ๊ณ์ด ๊ทธ๋ผ๋ฐ์ด์
*/
}
/* ์นด๋ ๋ท๋ฉด (๊ณผ์ผ ์ด๋ชจ์ง์ ํจ๊ป ํ์คํ
ํค ๋ฐฐ๊ฒฝ) */
.card-back {
transform: rotateY(180deg); /* ์นด๋ ๋ท๋ฉด์ 180๋๋ก ํ์ */
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem; /* ์ด๋ชจ์ง ํฌ๊ธฐ ์กฐ์ */
color: #fff; /* ์ด๋ชจ์ง ์์ */
text-align: center;
}
2-2. ์นด๋ ์ ํ ํจ์ shuffle() ๋ง๋ค๊ธฐ
colors ๋ฐฐ์ด์๋ 6๊ฐ์ ์์์ ๋ฃ๊ณ , concat() ๋ฉ์๋๋ก ๋ ๋ฒ ํฉ์ณ์ colorCopy๋ฅผ ๋ง๋ค์ด ์ค๋ค.
๊ฐ ์์๋ง๋ค 2๋ฒ์ฉ ์ด 12๋ฒ ์์์ด ์นด๋์ ๋ค์ด๊ฐ๋๋ก ํด ์ค๋ค.
์นด๋๋ฅผ ์์ฑํ๊ธฐ ์ ์, ๋จผ์ ์นด๋๋ฅผ ๋๋ค์ผ๋ก ์
ํ ํด ์ค ๊ฒ์ธ๋ฐ ์ฌ๊ธฐ์๋ Fisher-Yates Shuffle ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํด ์ฃผ์๋ค.
<!-- ์นด๋๋ฅผ ๊ฐ์ธ๋ ์ปจํ
์ด๋ -->
<div id="wrapper"></div>
<script>
/* ์นด๋์ ๋ถ๋ชจ ์์ wrapper ์ ํ */
const $wrapper = document.querySelector('#wrapper');
const total = 12; // ์ด ์นด๋ ์
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']; // ์์ ๋ฐฐ์ด
const fruits = ['๐', '๐', '๐', '๐', '๐', '๐']; // ๊ณผ์ผ ์ด๋ชจ์ง ๋ฐฐ์ด
const fruitColors = {
'๐': '#ffb3b3', // ์ฐํ ๋นจ๊ฐ์
'๐': '#fef6a2', // ์ฐํ ๋
ธ๋์
'๐': '#c9f9c5', // ์ฐํ ์ด๋ก์
'๐': '#ffd9a2', // ์ฐํ ์ฃผํฉ์
'๐': '#d8a2f7', // ์ฐํ ๋ณด๋ผ์
'๐': '#ffb5d1' // ์ฐํ ํํฌ์
};
// ์นด๋ ์์ ๋ฐฐ์ด์๋ 6๊ฐ์ ์์์ด ๋ค์ด๊ฐ, ๊ฐ ์์์ด 2๋ฒ์ฉ ๋ฐ๋ณต๋์ด์ผ ํจ
let colorCopy = colors.concat(colors);
let shuffledColors = [];
// ์นด๋ ๋ท๋ฉด์ ๊ณผ์ผ ์ด๋ชจ์ง๋ฅผ ๋ฐฐ์ด๊ณผ ๋์ผํ๊ฒ ๋ฐฐ์น
let fruitCopy = fruits.concat(fruits);
let shuffledFruits = [];
// ์นด๋ ๋ฐฐ์ด์ ๋๋ค์ผ๋ก ์
ํํ๋ ํจ์
function shuffle() {
// Fisher-Yates Shuffle ํจ์ ์ฌ์ฉ
for (let i = 0; colorCopy.length > 0; i++) {
const randomIndex = Math.floor(Math.random() * colorCopy.length);
shuffledColors = shuffledColors.concat(colorCopy.splice(randomIndex, 1));
const randomFruitIndex = Math.floor(Math.random() * fruitCopy.length);
shuffledFruits = shuffledFruits.concat(fruitCopy.splice(randomFruitIndex, 1));
}
}
</script>
์๋ฐ์คํฌ๋ฆฝํธ์์๋ ๋๋ค ์ ๋ ฌ์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํ ํ ์ ์๋ค. sort()๋ฉ์๋๋ฅผ ์ด์ฉํ๋ ๋ฐฉ๋ฒ๊ณผ, Fisher-Yates Shuffle(ํผ์
์์ด์ธ ์
ํ) ์๊ณ ๋ฆฌ์ฆ์ ์ฐ๋ ๋ฐฉ๋ฒ์ด๋ค. ํ์ง๋ง sort() ๋ฉ์๋๋ ๊ฐ๊ธ์ ์ฌ์ฉํ์ง ์๋ ๊ฒ์ด ์ข์๋ฐ ๊ทธ ์ด์ ๋ ๋ชจ๋ ์์ด์ ๋น๋์๊ฐ ๊ท ์ผํ๊ฒ ๋์ค์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ ๊ทธ๋ฐ์ง๋ ์๋ ์ฌ์ดํธ์์ ์ ์ค๋ช
๋์ด ์์ผ๋๊น ์ฐธ๊ณ ๋ฐ๋.
https://ko.javascript.info/task/shuffle
๋ฐฐ์ด ์์ ๋ฌด์์๋ก ์๊ธฐ
๋ฐฐ์ด์ ์์๋ฅผ ๋ฌด์์๋ก ์์ด์ฃผ๋ ํจ์ shuffle(array)์ ์์ฑํด ๋ณด์ธ์. shuffle์ ์ฌ๋ฌ ๋ฒ ์คํํ๋ฉด ์์์ ์ ๋ ฌ ์์๊ฐ ๋ฌ๋ผ์ผ ํฉ๋๋ค. ์์๋ฅผ ์ดํด๋ด ์๋ค. let arr = [1, 2, 3]; shuffle(arr); // arr = [3, 2, 1]
ko.javascript.info
2-3. ์นด๋ ์์ฑํ๋ ํจ์ createCard(i) ๋ง๋ค๊ธฐ
์นด๋๋ง๋ค ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ์ค์ ํ๋ฉด์ ์นด๋๋ฅผ ์์ฑ ํด ์ฃผ๋ createCard ํจ์๋ฅผ ๋ง๋ค์ด์ค๋ค.
document.createElement('div')๋ก div ์์๋ฅผ ์์ฑํ๊ณ card ๋ผ๋ ํด๋์ค ์ด๋ฆ์ ๋ถ์ฌํด์ค๋ค.
์นด๋ ์ ์ฒด(card)์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ด๋ถ(inner), ์๋ฉด(front), ๋ท๋ฉด(back) ๋ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์์ฑํด ์ค๋ค.
์ดํ, appendChild()๋ก ์์๋ค์ ๊ณ์ธต๊ตฌ์กฐ๋ก ์ถ๊ฐํด์ฃผ๋ ์์
์ ํ๋ค. cardInner์ cardFront์ cardBack์ ์์ ์์๋ก ์ถ๊ฐํด ์ค.
cardInner๋ฅผ ๋ค์ ๋ถ๋ชจ์ธ card ๋ฐ์ ์ถ๊ฐ
๋๋ ๊ณผ์ผ ์์์ด๋ ๊ฐ์ ์์ผ๋ก ์นด๋ ๋ฐฐ๊ฒฝ์์ ์ง์ ํด ์ค ๊ฒ์ด๊ธฐ ๋๋ฌธ์ cardBack.style.backgroundColor
๋ฅผ shuffledFruits[i]์ ํด๋นํ๋ ์์์ผ๋ก ๋ง์ถ์ด ์นด๋ ๋ท๋ฉด์ ๋ฐฐ๊ฒฝ์์ ์ค์ ํด ์ฃผ์๋ค.
<script>
function createCard(i) {
const card = document.createElement('div');
card.className = 'card'; // .card ํ๊ทธ ์์ฑ
const cardInner = document.createElement('div');
cardInner.className = 'card-inner'; // .card-inner ํ๊ทธ ์์ฑ
const cardFront = document.createElement('div');
cardFront.className = 'card-front'; // .card-front ํ๊ทธ ์์ฑ
const cardBack = document.createElement('div');
cardBack.className = 'card-back'; // .card-back ํ๊ทธ ์์ฑ
cardBack.textContent = shuffledFruits[i]; // ๊ณผ์ผ ์ด๋ชจ์ง ๋ฃ๊ธฐ
// ์นด๋ ์์ ์ค์
cardBack.style.backgroundColor = fruitColors[shuffledFruits[i]];
// ๊ณผ์ผ ์์์ ๋ง๋ ๋ฐฐ๊ฒฝ์ ์ค์
cardInner.appendChild(cardFront);
cardInner.appendChild(cardBack);
card.appendChild(cardInner);
return card;
}
</script>
2-4. ๊ฒ์ ์์ํ๋ ํจ์ startGame() ๋ง๋ค๊ธฐ
๊ฒ์์ ์๋ก ์์ํ ๋๋ง๋ค ์ ํ ๋ฉ์๋๋ฅผ ํธ์ถํด์ ์นด๋๋ฅผ ๋ฌด์์๋ก ์์ด ์ฃผ๊ณ , ์ฌ๋ฐฐ์ด ์์ผ์ฃผ๊ธฐ
<script>
function startGame() {
shuffle();
for (let i = 0; i < total; i += 1) {
const card = createCard(i);
$wrapper.appendChild(card);
}
}
startGame();
</script>
2-5. ์ฌ๊ธฐ๊น์ง ์ ์ฒด์ฝ๋
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Card Flip Game</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
#wrapper {
display: flex;
flex-wrap: wrap;
justify-content: center;
padding: 20px;
}
.card {
display: inline-block;
margin-right: 20px;
margin-bottom: 20px;
width: 70px;
height: 100px;
perspective: 500px; /* 3D ํจ๊ณผ๋ฅผ ์ํ ์์ ์ค์ */
}
.card-inner {
position: relative;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.8s;
transform-style: preserve-3d;
}
.card-front, .card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 12px; /* ๋ถ๋๋ฝ๊ณ ๋ ์ธ๋ จ๋ ๋ฅ๊ทผ ๋ชจ์๋ฆฌ */
border: 2px solid rgba(0, 0, 0, 0.1); /* ์ฐํ ํ
๋๋ฆฌ */
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1), 0 4px 10px rgba(0, 0, 0, 0.1); /* ๋ถ๋๋ฝ๊ณ ์
์ฒด์ ์ธ ๊ทธ๋ฆผ์ */
}
/* ์นด๋ ์๋ฉด */
.card-front {
background: linear-gradient(45deg, #001f3d, #003366); /* ๋จ์ ๊ณ์ด ๊ทธ๋ผ๋ฐ์ด์
*/
}
/* ์นด๋ ๋ท๋ฉด (๊ณผ์ผ ์ด๋ชจ์ง์ ํจ๊ป ํ์คํ
ํค ๋ฐฐ๊ฒฝ) */
.card-back {
transform: rotateY(180deg); /* ์นด๋ ๋ท๋ฉด์ 180๋๋ก ํ์ */
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem; /* ์ด๋ชจ์ง ํฌ๊ธฐ ์กฐ์ */
color: #fff; /* ์ด๋ชจ์ง ์์ */
text-align: center;
}
/* ๊ณผ์ผ ํ์คํ
ํค ๋ฐฐ๊ฒฝ ์์ */
.card-back.apple { background-color: #ffb3b3; } /* ์ฌ๊ณผ (์ฐํ ๋นจ๊ฐ) */
.card-back.banana { background-color: #fef6a2; } /* ๋ฐ๋๋ (์ฐํ ๋
ธ๋) */
.card-back.lime { background-color: #c9f9c5; } /* ๋ผ์ (์ฐํ ์ด๋ก) */
.card-back.orange { background-color: #ffd9a2; } /* ์ค๋ ์ง (์ฐํ ์ค๋ ์ง) */
.card-back.grape { background-color: #d8a2f7; } /* ํฌ๋ (์ฐํ ๋ณด๋ผ) */
.card-back.watermelon { background-color: #ffb5d1; } /* ์๋ฐ (์ฐํ ํํฌ) */
/* ์นด๋ ๋ค์ง๊ธฐ ํจ๊ณผ */
.card:hover .card-inner {
transform: rotateY(180deg); /* hover ์ ์นด๋๊ฐ ๋ค์งํ๋๋ก ํ์ */
}
</style>
</head>
<body>
<div id="wrapper"></div>
<script>
const $wrapper = document.querySelector('#wrapper');
const total = 12; // ์ด ์นด๋ ์
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']; // ์์ ๋ฐฐ์ด
const fruits = ['๐', '๐', '๐', '๐', '๐', '๐']; // ๊ณผ์ผ ์ด๋ชจ์ง ๋ฐฐ์ด
const fruitColors = {
'๐': '#ffb3b3', // ์ฐํ ๋นจ๊ฐ์
'๐': '#fef6a2', // ์ฐํ ๋
ธ๋์
'๐': '#c9f9c5', // ์ฐํ ์ด๋ก์
'๐': '#ffd9a2', // ์ฐํ ์ฃผํฉ์
'๐': '#d8a2f7', // ์ฐํ ๋ณด๋ผ์
'๐': '#ffb5d1' // ์ฐํ ํํฌ์
};
// ์นด๋ ์์ ๋ฐฐ์ด์๋ 6๊ฐ์ ์์์ด ๋ค์ด๊ฐ, ๊ฐ ์์์ด 2๋ฒ์ฉ ๋ฐ๋ณต๋์ด์ผ ํจ
let colorCopy = colors.concat(colors);
let shuffledColors = [];
// ์นด๋ ๋ท๋ฉด์ ๊ณผ์ผ ์ด๋ชจ์ง๋ฅผ ๋ฐฐ์ด๊ณผ ๋์ผํ๊ฒ ๋ฐฐ์น
let fruitCopy = fruits.concat(fruits);
let shuffledFruits = [];
// ์นด๋ ๋ฐฐ์ด์ ๋๋ค์ผ๋ก ์
ํํ๋ ํจ์
function shuffle() {
// Fisher-Yates Shuffle ํจ์ ์ฌ์ฉ
for (let i = 0; colorCopy.length > 0; i++) {
const randomIndex = Math.floor(Math.random() * colorCopy.length);
shuffledColors = shuffledColors.concat(colorCopy.splice(randomIndex, 1));
const randomFruitIndex = Math.floor(Math.random() * fruitCopy.length);
shuffledFruits = shuffledFruits.concat(fruitCopy.splice(randomFruitIndex, 1));
}
}
// ์นด๋ ์์ฑ ํจ์
function createCard(i) {
const card = document.createElement('div');
card.className = 'card'; // .card ํ๊ทธ ์์ฑ
const cardInner = document.createElement('div');
cardInner.className = 'card-inner'; // .card-inner ํ๊ทธ ์์ฑ
const cardFront = document.createElement('div');
cardFront.className = 'card-front'; // .card-front ํ๊ทธ ์์ฑ
const cardBack = document.createElement('div');
cardBack.className = 'card-back'; // .card-back ํ๊ทธ ์์ฑ
cardBack.textContent = shuffledFruits[i]; // ๊ณผ์ผ ์ด๋ชจ์ง ๋ฃ๊ธฐ
// ์นด๋ ์์ ์ค์
cardBack.style.backgroundColor = fruitColors[shuffledFruits[i]]; // ๊ณผ์ผ ์์์ ๋ง๋ ๋ฐฐ๊ฒฝ์ ์ค์
cardInner.appendChild(cardFront);
cardInner.appendChild(cardBack);
card.appendChild(cardInner);
return card;
}
// ๊ฒ์ ์์ ํจ์
function startGame() {
shuffle();
for (let i = 0; i < total; i++) {
const card = createCard(i);
$wrapper.appendChild(card);
}
}
startGame();
</script>
</body>
</html>


์ฌ๊ธฐ๊น์ง ํ์ ๋ ๋ผ์ด๋ธ์๋ฒ์ ์ด ์ํ๊ฐ ๋๋ฉด ์ฑ๊ณต~
๊ทธ ๋ค๋ถํฐ๋ ๊ท์ฐฎ์์ ๋ด์ผ ํฌ์คํ
to be continue
wrapper์ flex๋ grid ์ฃผ์ง ์์๊ธฐ ๋๋ฌธ์
์คํํด๋ณด๋ฉด ์นด๋๊ฐ ํ ์ค๋ก ๋ถ์ด ์๋ ๊ฒ๋ ์ ์์ด๋ค
์ฐฝ ํฌ๊ธฐ ์ค์ด๋ฉด ์นด๋๊ฐ ๋ค์์ค๋ก ๋์ด๊ฐ...