# data[].attributes.body
무료 서명, 도장만들기

# data[].attributes.attachment_link.title
무료 전자 서명 및 도장 만들기

# data[].attributes.attachment_link.description
회원가입 필요 없이 무료 온라인 서명, 도장을 제작해보세요! 법인직인, 인감도장도 3초 만에 제작 완료.

# data[].attributes.attachment_link.description
Use ChatGPT to answer questions, write, create images, complete work, and code-all in one place. Get started for free or download the app.

# data[].attributes.attachment_link.title
이 채팅을 확인해 보세요

# data[].attributes.attachment_link.description
누군가 회원님이 보고 싶어 할 만하다고 생각한 채팅입니다.

# data[].attributes.body
<!doctype html>

<html lang="ko">

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>카드 뒤집기 게임</title>

<style>

* { box-sizing: border-box; }

body {

margin: 0;

min-height: 100vh;

display: grid;

place-items: center;

font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

background: linear-gradient(135deg, #f7f8ff, #eef7ff);

color: #1f2937;

}

.game {

width: min(92vw, 720px);

padding: 28px;

background: rgba(255,255,255,.88);

border-radius: 24px;

box-shadow: 0 18px 60px rgba(31,41,55,.12);

backdrop-filter: blur(10px);

}

h1 {

margin: 0 0 8px;

text-align: center;

font-size: clamp(28px, 5vw, 42px);

}

.subtitle {

margin: 0 0 20px;

text-align: center;

color: #6b7280;

}

.status {

display: grid;

grid-template-columns: repeat(3, 1fr);

gap: 10px;

margin-bottom: 16px;

}

.stat {

background: #f8fafc;

border-radius: 14px;

padding: 12px 8px;

text-align: center;

font-weight: 700;

}

.stat span {

display: block;

margin-top: 4px;

font-size: 22px;

}

.board {

display: grid;

grid-template-columns: repeat(4, 1fr);

gap: 12px;

perspective: 1000px;

}

.card {

aspect-ratio: 1 / 1;

border: 0;

padding: 0;

background: transparent;

cursor: pointer;

perspective: 900px;

}

.card-inner {

position: relative;

width: 100%;

height: 100%;

transition: transform .42s ease;

transform-style: preserve-3d;

}

.card.flipped .card-inner,

.card.matched .card-inner {

transform: rotateY(180deg);

}

.face {

position: absolute;

inset: 0;

border-radius: 18px;

display: grid;

place-items: center;

backface-visibility: hidden;

box-shadow: 0 6px 18px rgba(0,0,0,.08);

user-select: none;

}

.front {

background: linear-gradient(145deg, #4f46e5, #7c3aed);

color: white;

font-size: clamp(24px, 5vw, 42px);

}

.front::after {

content: "?";

font-weight: 900;

}

.back {

transform: rotateY(180deg);

background: white;

font-size: clamp(30px, 6vw, 48px);

border: 2px solid #eef2ff;

}

.card.matched .back {

box-shadow: inset 0 0 0 3px #86efac, 0 6px 18px rgba(0,0,0,.08);

}

.controls {

display: flex;

justify-content: center;

margin-top: 18px;

}

button.restart {

border: 0;

border-radius: 999px;

padding: 12px 22px;

font-weight: 800;

font-size: 16px;

cursor: pointer;

background: #111827;

color: white;

}

.message {

min-height: 28px;

margin-top: 14px;

text-align: center;

font-weight: 800;

}

@media (max-width: 520px) {

.game { padding: 18px; }

.board { gap: 8px; }

.status { gap: 7px; }

.stat { font-size: 13px; }

.stat span { font-size: 18px; }

}

</style>

</head>

<body>

<main class="game">

<h1>🃏 카드 뒤집기</h1>

<p class="subtitle">같은 그림의 카드 두 장을 찾아보세요!</p>

<section class="status">

<div class="stat">시도 횟수<span id="moves">0</span></div>

<div class="stat">찾은 짝<span id="matches">0 / 8</span></div>

<div class="stat">시간<span id="time">0초</span></div>

</section>

<section class="board" id="board" aria-label="카드 뒤집기 게임판"></section>

<div class="message" id="message"></div>

<div class="controls">

<button class="restart" id="restart">게임 다시 시작</button>

</div>

</main>

<script>

const symbols = ["🍕","🚀","🐶","🎮","🍓","⚽","🤖","🌈"];

let firstCard = null;

let secondCard = null;

let lockBoard = false;

let moves = 0;

let matchedPairs = 0;

let seconds = 0;

let timer = null;

let started = false;

const board = document.getElementById("board");

const movesEl = document.getElementById("moves");

const matchesEl = document.getElementById("matches");

const timeEl = document.getElementById("time");

const messageEl = document.getElementById("message");

const restartBtn = document.getElementById("restart");

function shuffle(arr) {

const a = [...arr];

for (let i = a.length - 1; i > 0; i--) {

const j = Math.floor(Math.random() * (i + 1));

[a[i], a[j]] = [a[j], a[i]];

}

return a;

}

function startTimer() {

if (started) return;

started = true;

timer = setInterval(() => {

seconds++;

timeEl.textContent = `${seconds}초`;

}, 1000);

}

function createCard(symbol, index) {

const card = document.createElement("button");

card.className = "card";

card.type = "button";

card.dataset.symbol = symbol;

card.dataset.index = index;

card.setAttribute("aria-label", "뒤집히지 않은 카드");

card.innerHTML = `

<div class="card-inner">

<div class="face front"></div>

<div class="face back">${symbol}</div>

</div>

`;

card.addEventListener("click", () => flipCard(card));

return card;

}

function flipCard(card) {

if (lockBoard || card === firstCard || card.classList.contains("matched")) return;

startTimer();

card.classList.add("flipped");

card.setAttribute("aria-label", `${card.dataset.symbol} 카드`);

if (!firstCard) {

firstCard = card;

return;

}

secondCard = card;

moves++;

movesEl.textContent = moves;

checkMatch();

}

function checkMatch() {

const isMatch = firstCard.dataset.symbol === secondCard.dataset.symbol;

if (isMatch) {

firstCard.classList.add("matched");

secondCard.classList.add("matched");

matchedPairs++;

matchesEl.textContent = `${matchedPairs} / 8`;

resetTurn();

if (matchedPairs === symbols.length) {

clearInterval(timer);

messageEl.textContent = `🎉 성공! ${moves}번의 시도, ${seconds}초 만에 완료했어요!`;

}

} else {

lockBoard = true;

setTimeout(() => {

firstCard.classList.remove("flipped");

secondCard.classList.remove("flipped");

firstCard.setAttribute("aria-label", "뒤집히지 않은 카드");

secondCard.setAttribute("aria-label", "뒤집히지 않은 카드");

resetTurn();

}, 800);

}

}

function resetTurn() {

[firstCard, secondCard] = [null, null];

lockBoard = false;

}

function initGame() {

clearInterval(timer);

firstCard = null;

secondCard = null;

lockBoard = false;

moves = 0;

matchedPairs = 0;

seconds = 0;

timer = null;

started = false;

movesEl.textContent = "0";

matchesEl.textContent = "0 / 8";

timeEl.textContent = "0초";

messageEl.textContent = "";

board.innerHTML = "";

const deck = shuffle([...symbols, ...symbols]);

deck.forEach((symbol, index) => board.appendChild(createCard(symbol, index)));

}

restartBtn.addEventListener("click", initGame);

initGame();

</script>

</body>

</html>