텍스트 어드벤처 게임을 개발하는 설계 도구 만들기

소개

학생들에게 md 파일을 주고 작성하게 해서 짧은 시간에 바이브코딩으로 텍스트 어드벤처 게임을 개발하게 하고 싶습니다. 학생들이 좀 더 쉽게, 재미있게 작업하도록 하는 것을 목표로 합니다.

요즘은 학생들에게 하고 싶니? 안 하고 싶니? 이런거 물어봐도 안 된다고 하더라구요. 저야 잘 몰라서 돌아다니다 유투브 보는 학생 있으면 "내가 뒤에 있을 때 유투브 보는건 좀 아니지 않니?" 이러기도 하고 그러는데요.

조심해야 한다고 하더라구요. 아무튼 고등학생들이 왔는데 구글 아이디가 없다는 거에요. 유투브는 하지 않아? 라고 했더니 아... 이러는데 그냥 하기 싫다는 것이 얼굴에 써 있달까... 처음 해보는 학생들도 많고 학생들 말로는 공부만 해서 머리가 굳었다고도 하고 그래서 최대한 좀 쉽게 진행하는 것을 고민하고 있습니다.

고민하다가 탈모가 올 것 같습니다.

진행 방법

어떤 도구를 사용했고, 어떻게 활용하셨나요?

Antigravity를 쓰는데 오전 반에서 로그인을 못 하는 학생들이 6명 이상은 나온 것 같아요. 뭐지... 라고 생각했는데 일단 고1이었고, 18세가 안 된 학생들이 꽤 있었습니다. 그 중에 나이 인증을 안 한 학생도 있었고 굉장히 진행이 어려웠습니다.

최근에 Antigravity가 많이 바뀌어서 토큰이 녹아 내리는 중이라 1~2번만 요청을 해도 수업을 못 하게 되는 상황이 되더라구요. 토큰 없어서 못 하겠는데? 이러면 좋아합니다. 그 꼴은 또 못 봐서 제 구글 계정 로그인해주다 시간 다 가더라구요.

구글 AI 스튜디오에 마크다운 파일 주고 만들라고 하니 꽤 잘 만들더군요. 앞으로는 이렇게 해야지 약간 노하우가 생겼습니다.

Tip: 사용한 프롬프트 전문을 꼭 포함하고, 내용을 짧게 소개해 주세요.

// 아래 내용을 구현하지 말고 설계 예시로 만들어줘.
게임명: 한국사 답사 어드벤처
배경/주인공 설정: 현대/초등학교 학생
성별: 남
이름: 연우
시놉시스(줄거리)
모험의 시작(왜 모험을 시작해야 하는가를 설명하는 곳): 주인공 연우는 삼촌을 만나러 가던 길에 우연히 상자를 하나 발견하게 된다. 이거 뭐지? 상자를 열었더니 나비 한 마리가 날아오르며 "고맙다"고 인사를 한다. 자신은 나비 왕국의 왕자인데 사악한 주술에 갇혀있었다는 것이다. 나비가 고맙다고 하고 사라진 후 연우가 다시 길을 가고 있는데 연우의 앞에 갑자기 나타난 사악한 악령.
사악한 악령: 네 녀석이 나비 왕국의 왕자를 풀어줬겠다.
연우:
대사1-1A. 저는 모르는 일입니다. -> 2번
대사1_2A. 왕자를 가둔건 잘못한 일 아닌가요? -> 3번
사악한 악령:
대사2. 모르는 일이라고 거짓말을 하다니. 너의 손에 있는 나비의 가루가 그 증거인데 죽어라. 얏! -> Ending(Bad) 화면으로 이동
대사3. 그렇다면 네가 얼마나 도덕적인 인간인지 시험해 보겠다. 내가 내는 문제를 맞춰보아라.
AI에게 요청: 사지선다 도덕문제 10개를 출제해줘.
문제를 10개 ~ 8개를 맞추면 Ending(Good) 화면으로 이동
문제를 7개 ~ 4개를 맞추면 Ending(Soso) 화면으로 이동
문제를 3개 ~ 0개를 맞추면 Ending(Bad) 화면으로 이동
Ending(Good)
연우(주인공이름)는 사악한 악령을 물리치고 나비 왕국에 초대되어 금은보화를 선물로 받았습니다.
Ending(Soso)
연우(주인공이름)는 사악한 악령의 공격을 받고 기절했다가 반나절 만에 집에 겨우 돌아왔습니다.
Ending(Bad)
연우(주인공이름)는 사악한 악령의 공격을 받고 부산으로 버려져 며칠간 떠돌다 겨우 집에 돌아왔습니다.

지난 번에 마크다운 파일을 줬더니 못 하는 학생도 많고, 뭘 해야 할지 모르겠다고 하는 학생도 있어서 설계도구를 만들어 보았습니다.

Tip: 활용 이미지나 캡처 화면을 꼭 남겨주세요.

한국어가 포함된 한국 웹사이트 스크린샷

제가 만든 예시가 채워지는 버튼도 있고, 초기화하는 버튼도 넣었습니다. 다 작성 후 마크다운 파일을 다운로드 받은 후 Antigravity에서 열어서 html 단일 페이지로 만들어줘. 라고 하라고 했는데 여기저기서 설명은 안 듣고 어떻게 해요? 라고 산발적으로 물어봐서 엄청 피곤했습니다.

Tip: 코드 전문은 코드블록에 감싸서 작성해주세요. ( / 을 눌러 '코드 블록'을 선택)


<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>교육용 텍스트 어드벤처 설계 도구</title>
<style>
  :root {
    --bg: #12141c;
    --bg-panel: #1b1f2e;
    --bg-input: #262b3d;
    --border: #3a4058;
    --text: #eef0f7;
    --text-dim: #9aa1b8;
    --accent: #ffb84d;
    --accent-2: #6ee7c0;
    --accent-3: #ff6b81;
    --good: #6ee7c0;
    --soso: #ffd166;
    --bad: #ff6b81;
    --radius: 14px;
    --font: "Pretendard", "Malgun Gothic", "Apple SD Gothic Neo", sans-serif;
  }

  * { box-sizing: border-box; }

  body {
    margin: 0;
    font-family: var(--font);
    background: radial-gradient(circle at 20% -10%, #232840 0%, var(--bg) 55%);
    color: var(--text);
    line-height: 1.6;
    padding-bottom: 80px;
  }

  header {
    padding: 36px 20px 20px;
    text-align: center;
  }

  header h1 {
    margin: 0 0 8px;
    font-size: 1.7rem;
    letter-spacing: -0.02em;
  }

  header p {
    margin: 0;
    color: var(--text-dim);
    font-size: 0.95rem;
  }

  .wrap {
    max-width: 900px;
    margin: 0 auto;
    padding: 0 16px;
  }

  .toolbar {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
    margin: 18px 0 30px;
  }

  button {
    font-family: var(--font);
    font-size: 0.92rem;
    border: none;
    border-radius: 10px;
    padding: 11px 18px;
    cursor: pointer;
    font-weight: 600;
    transition: transform 0.12s ease, filter 0.12s ease;
  }
  button:active { transform: scale(0.97); }

  .btn-primary { background: var(--accent); color: #1a1408; }
  .btn-secondary { background: var(--bg-input); color: var(--text); border: 1px solid var(--border); }
  .btn-ghost { background: transparent; color: var(--text-dim); border: 1px solid var(--border); }
  button:hover { filter: brightness(1.08); }

  .progress-track {
    height: 6px;
    background: var(--bg-input);
    border-radius: 999px;
    overflow: hidden;
    margin: 0 0 26px;
    position: sticky;
    top: 0;
    z-index: 10;
  }
  .progress-fill {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg, var(--accent-2), var(--accent));
    transition: width 0.25s ease;
  }

  section.card {
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 22px 24px;
    margin-bottom: 20px;
  }

  section.card h2 {
    font-size: 1.05rem;
    margin: 0 0 4px;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  section.card h2 .num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--accent);
    color: #1a1408;
    font-size: 0.85rem;
  }

  section.card .desc {
    color: var(--text-dim);
    font-size: 0.87rem;
    margin: 0 0 14px;
  }

  label {
    display: block;
    font-size: 0.88rem;
    color: var(--text-dim);
    margin: 14px 0 6px;
  }
  label:first-of-type { margin-top: 0; }

  input[type="text"], textarea, select {
    width: 100%;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 11px 13px;
    color: var(--text);
    font-family: var(--font);
    font-size: 0.95rem;
    resize: vertical;
  }
  input[type="text"]:focus, textarea:focus, select:focus {
    outline: none;
    border-color: var(--accent-2);
  }

  .example {
    color: #7c86a8;
    font-size: 0.8rem;
    margin: 6px 2px 0;
  }

  .radio-row {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
  }
  .radio-row label {
    margin: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    background: var(--bg-input);
    border: 1px solid var(--border);
    padding: 9px 14px;
    border-radius: 10px;
    cursor: pointer;
    color: var(--text);
    font-size: 0.9rem;
  }
  .radio-row input { accent-color: var(--accent); }

  .flow-tag {
    display: inline-block;
    margin-top: 8px;
    font-size: 0.78rem;
    padding: 3px 10px;
    border-radius: 999px;
    background: rgba(110,231,192,0.12);
    color: var(--accent-2);
    border: 1px solid rgba(110,231,192,0.3);
  }

  .ending-good { border-left: 4px solid var(--good); }
  .ending-soso { border-left: 4px solid var(--soso); }
  .ending-bad  { border-left: 4px solid var(--bad); }

  .preview-wrap {
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 20px 22px;
  }
  .preview-wrap h2 { margin-top: 0; }

  pre#mdOutput {
    background: #0c0e15;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 18px;
    white-space: pre-wrap;
    word-break: break-word;
    font-family: "Consolas", "D2Coding", monospace;
    font-size: 0.85rem;
    color: #c9d1e8;
    max-height: 480px;
    overflow-y: auto;
  }

  .sticky-actions {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: rgba(18,20,28,0.92);
    backdrop-filter: blur(6px);
    border-top: 1px solid var(--border);
    padding: 14px 16px;
    display: flex;
    gap: 10px;
    justify-content: center;
    z-index: 20;
  }

  .toast {
    position: fixed;
    top: 18px;
    left: 50%;
    transform: translateX(-50%) translateY(-20px);
    background: var(--accent-2);
    color: #06231b;
    padding: 10px 18px;
    border-radius: 999px;
    font-weight: 600;
    font-size: 0.88rem;
    opacity: 0;
    transition: all 0.25s ease;
    z-index: 50;
    pointer-events: none;
  }
  .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

  footer {
    text-align: center;
    color: var(--text-dim);
    font-size: 0.8rem;
    margin: 30px 0 10px;
  }

  @media (prefers-color-scheme: light) {
    :root {
      --bg: #f5f6fb;
      --bg-panel: #ffffff;
      --bg-input: #f0f1f8;
      --border: #dbdfee;
      --text: #1c2030;
      --text-dim: #6b7290;
    }
    pre#mdOutput { background: #f0f1f8; color: #2a2f45; }
  }
</style>
</head>
<body>

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

<header>
  <h1>🎮 교육용 텍스트 어드벤처 설계 도구</h1>
  <p>칸을 채우면 자동으로 게임 기획 마크다운 문서가 만들어져요. 완성되면 AI에게 이 문서를 그대로 주고 앱을 만들어 달라고 하면 됩니다.</p>
</header>

<div class="progress-track"><div class="progress-fill" id="progressFill"></div></div>

<div class="wrap">

  <div class="toolbar">
    <button class="btn-secondary" id="fillExampleBtn">✨ 예시로 채워보기</button>
    <button class="btn-ghost" id="resetBtn">🗑 초기화</button>
  </div>

  <!-- 1. 기본 설정 -->
  <section class="card">
    <h2><span class="num">1</span> 어떤 게임을 만들고 싶나요?</h2>
    <p class="desc">어떤 스토리의 텍스트 어드벤처를 만들고 싶은지 자세히 적어주세요.</p>
    <textarea id="storyIdea" rows="3" placeholder="예시: 한국사를 공부하는 앱을 만들고 싶어."></textarea>
  </section>

  <section class="card">
    <h2><span class="num">2</span> 배경</h2>
    <p class="desc">이야기가 일어나는 시대나 장소를 정해주세요.</p>
    <input type="text" id="background" placeholder="예시: 현대" />
  </section>

  <section class="card">
    <h2><span class="num">3</span> 주인공 설정</h2>
    <p class="desc">주인공은 어떤 사람인가요?</p>
    <input type="text" id="protagonistSetting" placeholder="예시: 초등학교 학생" />

    <label>주인공 성별</label>
    <div class="radio-row">
      <label><input type="radio" name="gender" value="남" checked></label>
      <label><input type="radio" name="gender" value="여"></label>
      <label><input type="radio" name="gender" value="기타"> 기타</label>
    </div>

    <label>주인공 이름</label>
    <input type="text" id="protagonistName" placeholder="예시: 철수" />
  </section>

  <section class="card">
    <h2><span class="num">4</span> 시놉시스 (간단한 줄거리)</h2>
    <p class="desc">왜 주인공이 이 모험을 시작하게 되었는지 설명해주세요.</p>
    <textarea id="synopsis" rows="5" placeholder="예시: 주인공 연우는 삼촌을 만나러 가던 길에 우연히 상자를 하나 발견하게 된다. 이거 뭐지? 상자를 열었더니 나비 한 마리가 날아오르며 &quot;고맙다&quot;고 인사를 한다. 자신은 나비 왕국의 왕자인데 사악한 주술에 갇혀있었다는 것이다. 나비가 고맙다고 하고 사라진 후 연우가 다시 길을 가고 있는데 연우의 앞에 갑자기 나타난 사악한 악령."></textarea>
  </section>

  <section class="card">
    <h2><span class="num">5</span> 빌런 (주인공을 괴롭히는 존재)</h2>
    <input type="text" id="villain" placeholder="예시: 사악한 악령" />
  </section>

  <!-- 대사 -->
  <section class="card">
    <h2><span class="num">6</span> 대사 & 선택지 만들기</h2>
    <p class="desc">빌런이 먼저 말을 걸고, 주인공이 어떻게 답하느냐에 따라 이야기가 갈라져요.</p>

    <label>빌런 대사 1 (빌런이 처음 등장해서 하는 말)</label>
    <textarea id="villainLine1" rows="2" placeholder="예시: 네 녀석이 나비 왕국의 왕자를 풀어줬겠다."></textarea>

    <label>주인공 답변 A (선택지)</label>
    <textarea id="choiceA" rows="2" placeholder="예시: 저는 모르는 일입니다."></textarea>
    <span class="flow-tag">→ 이 답을 고르면 빌런 대사 3(아래)으로 이동</span>

    <label>주인공 답변 B (선택지)</label>
    <textarea id="choiceB" rows="2" placeholder="예시: 왕자를 가둔 것이 잘못한 일 아닌가요?"></textarea>
    <span class="flow-tag">→ 이 답을 고르면 빌런 대사 4(아래)로 이동</span>

    <label>빌런 대사 3 (답변 A를 골랐을 때 — 배드 엔딩으로 연결)</label>
    <textarea id="villainLine3" rows="2" placeholder="예시: 모르는 일이라고 거짓말을 하다니. 너의 손에 있는 나비의 가루가 그 증거인데 제주도로 보내주마. 얏!"></textarea>
    <span class="flow-tag">→ Ending(Bad) 화면으로 이동</span>

    <label>빌런 대사 4 (답변 B를 골랐을 때 — 문제 출제로 연결)</label>
    <textarea id="villainLine4" rows="2" placeholder="예시: 그렇다면 네가 얼마나 도덕적인 인간인지 시험해 보겠다. 내가 내는 문제를 맞춰보아라."></textarea>
    <span class="flow-tag">→ 문제 출제로 이동</span>
  </section>

  <!-- 문제 -->
  <section class="card">
    <h2><span class="num">7</span> 문제 출제</h2>
    <p class="desc">AI에게 사지선다 문제 10개를 요청할 거예요. 어떤 주제/범위로 문제를 내고 싶은지 적어주세요.</p>
    <textarea id="quizTopic" rows="3" placeholder="예시: 조선시대 세종대왕의 업적에 관한 사지선다 문제 10개"></textarea>
    <p class="example">채점 기준은 자동으로 다음처럼 설정돼요 — 10~8개 정답: Good / 7~4개 정답: Soso / 3~0개 정답: Bad</p>
  </section>

  <!-- 엔딩 -->
  <section class="card ending-good">
    <h2><span class="num">🏆</span> Ending (Good) — 10~8개 정답</h2>
    <textarea id="endingGood" rows="2" placeholder="예시: (주인공이름)는 사악한 악령을 물리치고 나비 왕국에 초대되어 금은보화를 선물로 받았습니다."></textarea>
  </section>

  <section class="card ending-soso">
    <h2><span class="num">😐</span> Ending (Soso) — 7~4개 정답</h2>
    <textarea id="endingSoso" rows="2" placeholder="예시: (주인공이름)는 사악한 악령의 공격을 받고 기절했다가 반나절 만에 집에 겨우 돌아왔습니다."></textarea>
  </section>

  <section class="card ending-bad">
    <h2><span class="num">💀</span> Ending (Bad) — 3~0개 정답</h2>
    <textarea id="endingBad" rows="2" placeholder="예시: (주인공이름)는 사악한 악령의 공격을 받고 부산으로 버려져 며칠간 떠돌다 겨우 집에 돌아왔습니다."></textarea>
  </section>

  <!-- 미리보기 -->
  <div class="preview-wrap">
    <h2>📄 마크다운 미리보기</h2>
    <p class="desc" style="margin-top:-8px">입력하는 대로 아래 내용이 실시간으로 바뀌어요. 완성되면 아래 버튼으로 파일을 내려받으세요.</p>
    <pre id="mdOutput"></pre>
  </div>

  <footer>덕일전자공업고등학교 대학 학과 체험 · 유한대학교 컴퓨터공학과 게임콘텐츠전공</footer>
</div>

<div class="sticky-actions">
  <button class="btn-secondary" id="copyBtn">📋 복사하기</button>
  <button class="btn-primary" id="downloadBtn">⬇ 마크다운 파일 다운로드</button>
</div>

<script>
(function () {
  const ids = [
    "storyIdea", "background", "protagonistSetting", "protagonistName",
    "synopsis", "villain", "villainLine1", "choiceA", "choiceB",
    "villainLine3", "villainLine4", "quizTopic",
    "endingGood", "endingSoso", "endingBad"
  ];

  const examples = {
    storyIdea: "한국사를 공부하는 앱을 만들고 싶어.",
    background: "현대",
    protagonistSetting: "초등학교 학생",
    gender: "남",
    protagonistName: "철수",
    synopsis: "주인공 연우는 삼촌을 만나러 가던 길에 우연히 상자를 하나 발견하게 된다. 이거 뭐지? 상자를 열었더니 나비 한 마리가 날아오르며 \"고맙다\"고 인사를 한다. 자신은 나비 왕국의 왕자인데 사악한 주술에 갇혀있었다는 것이다. 나비가 고맙다고 하고 사라진 후 연우가 다시 길을 가고 있는데 연우의 앞에 갑자기 나타난 사악한 악령.",
    villain: "사악한 악령",
    villainLine1: "네 녀석이 나비 왕국의 왕자를 풀어줬겠다.",
    choiceA: "저는 모르는 일입니다.",
    choiceB: "왕자를 가둔 것이 잘못한 일 아닌가요?",
    villainLine3: "모르는 일이라고 거짓말을 하다니. 너의 손에 있는 나비의 가루가 그 증거인데 제주도로 보내주마. 얏!",
    villainLine4: "그렇다면 네가 얼마나 도덕적인 인간인지 시험해 보겠다. 내가 내는 문제를 맞춰보아라.",
    quizTopic: "나비 왕국과 관련된 한국 전래동화 속 교훈에 관한 사지선다 문제 10개",
    endingGood: "철수는 사악한 악령을 물리치고 나비 왕국에 초대되어 금은보화를 선물로 받았습니다.",
    endingSoso: "철수는 사악한 악령의 공격을 받고 기절했다가 반나절 만에 집에 겨우 돌아왔습니다.",
    endingBad: "철수는 사악한 악령의 공격을 받고 부산으로 버려져 며칠간 떠돌다 겨우 집에 돌아왔습니다."
  };

  const STORAGE_KEY = "textAdventureDesignerV1";

  function getGender() {
    const r = document.querySelector('input[name="gender"]:checked');
    return r ? r.value : "";
  }
  function setGender(v) {
    const r = document.querySelector('input[name="gender"][value="' + v + '"]');
    if (r) r.checked = true;
  }

  function val(id) { return document.getElementById(id).value.trim(); }

  function buildMarkdown() {
    const d = {};
    ids.forEach(id => d[id] = val(id));
    d.gender = getGender();

    const name = d.protagonistName || "주인공";
    const villain = d.villain || "빌런";

    const title = d.storyIdea
      ? d.storyIdea.replace(/^한/, "").slice(0, 24)
      : "제목 없는 프로젝트";

    let md = "";
    md += "# 교육용 텍스트 어드벤처 기획서\n\n";
    md += "## 1. 어떤 게임인가\n" + (d.storyIdea || "_(입력 필요)_") + "\n\n";

    md += "## 2. 기본 설정\n";
    md += "- 배경: " + (d.background || "_(입력 필요)_") + "\n";
    md += "- 주인공 설정: " + (d.protagonistSetting || "_(입력 필요)_") + "\n";
    md += "- 주인공 성별: " + (d.gender || "_(입력 필요)_") + "\n";
    md += "- 주인공 이름: " + (d.protagonistName || "_(입력 필요)_") + "\n";
    md += "- 빌런: " + (villain || "_(입력 필요)_") + "\n\n";

    md += "## 3. 시놉시스\n" + (d.synopsis || "_(입력 필요)_") + "\n\n";

    md += "## 4. 대사 및 선택지 흐름\n\n";
    md += "### Scene 1 — 첫 만남\n";
    md += "**" + villain + "**: " + (d.villainLine1 || "_(입력 필요)_") + "\n\n";
    md += "**" + name + "의 선택지**\n";
    md += "- A. " + (d.choiceA || "_(입력 필요)_") + " → **Scene 3**로 이동\n";
    md += "- B. " + (d.choiceB || "_(입력 필요)_") + " → **Scene 4**로 이동\n\n";

    md += "### Scene 3 — A 선택 시\n";
    md += "**" + villain + "**: " + (d.villainLine3 || "_(입력 필요)_") + "\n";
    md += "→ **Ending(Bad)** 로 이동\n\n";

    md += "### Scene 4 — B 선택 시\n";
    md += "**" + villain + "**: " + (d.villainLine4 || "_(입력 필요)_") + "\n";
    md += "→ **문제 출제**로 이동\n\n";

    md += "## 5. 문제 출제\n";
    md += "AI에게 다음 주제로 사지선다 문제 10개를 요청하세요:\n\n";
    md += "> " + (d.quizTopic || "_(입력 필요)_") + "\n\n";
    md += "채점 기준:\n";
    md += "- 10~8개 정답 → Ending(Good)\n";
    md += "- 7~4개 정답 → Ending(Soso)\n";
    md += "- 3~0개 정답 → Ending(Bad)\n\n";

    md += "## 6. 엔딩\n\n";
    md += "### 🏆 Ending (Good)\n" + (d.endingGood || "_(입력 필요)_") + "\n\n";
    md += "### 😐 Ending (Soso)\n" + (d.endingSoso || "_(입력 필요)_") + "\n\n";
    md += "### 💀 Ending (Bad)\n" + (d.endingBad || "_(입력 필요)_") + "\n";

    return md;
  }

  function updatePreview() {
    document.getElementById("mdOutput").textContent = buildMarkdown();
    updateProgress();
    saveToStorage();
  }

  function updateProgress() {
    let filled = 0;
    ids.forEach(id => { if (val(id)) filled++; });
    if (getGender()) filled++;
    const total = ids.length + 1;
    document.getElementById("progressFill").style.width = Math.round((filled / total) * 100) + "%";
  }

  function saveToStorage() {
    const d = {};
    ids.forEach(id => d[id] = val(id));
    d.gender = getGender();
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(d)); } catch (e) {}
  }

  function loadFromStorage() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return;
      const d = JSON.parse(raw);
      ids.forEach(id => { if (d[id]) document.getElementById(id).value = d[id]; });
      if (d.gender) setGender(d.gender);
    } catch (e) {}
  }

  function showToast(msg) {
    const t = document.getElementById("toast");
    t.textContent = msg;
    t.classList.add("show");
    setTimeout(() => t.classList.remove("show"), 1800);
  }

  document.getElementById("fillExampleBtn").addEventListener("click", () => {
    ids.forEach(id => { document.getElementById(id).value = examples[id] || ""; });
    setGender(examples.gender);
    updatePreview();
    showToast("예시로 채웠어요!");
  });

  document.getElementById("resetBtn").addEventListener("click", () => {
    if (!confirm("입력한 내용을 모두 지울까요?")) return;
    ids.forEach(id => { document.getElementById(id).value = ""; });
    setGender("남");
    localStorage.removeItem(STORAGE_KEY);
    updatePreview();
    showToast("초기화했어요");
  });

  document.getElementById("copyBtn").addEventListener("click", async () => {
    const text = buildMarkdown();
    try {
      await navigator.clipboard.writeText(text);
      showToast("클립보드에 복사했어요!");
    } catch (e) {
      showToast("복사에 실패했어요. 직접 선택해서 복사해주세요.");
    }
  });

  document.getElementById("downloadBtn").addEventListener("click", () => {
    const text = buildMarkdown();
    const blob = new Blob([text], { type: "text/markdown;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const name = val("protagonistName") || "나의";
    const a = document.createElement("a");
    a.href = url;
    a.download = name + "_텍스트어드벤처_기획서.md";
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
    showToast("파일을 다운로드했어요!");
  });

  ids.forEach(id => {
    document.getElementById(id).addEventListener("input", updatePreview);
  });
  document.querySelectorAll('input[name="gender"]').forEach(r => {
    r.addEventListener("change", updatePreview);
  });

  loadFromStorage();
  updatePreview();
})();
</script>

</body>
</html>

결과와 배운 점

배운 점과 나만의 꿀팁을 알려주세요.

1. 파일을 다운로드 받아서 압축을 푼다.
2. html 파일을 실행해서 내용을 작성한다.
3. 하단에 마크다운 파일을 다운로드한다.
4. 다운로드 받은 마크다운 파일에 AI에게 질문을 만들어서 업데이트 해달라고 한다.
"문제 만들어서 마크다운 파일에 업데이트해줘."
5. 마크다운을 읽어보고 본인이 만들려고 하는 게임의 내용이 잘 적용되어 있으면 개발을 요청한다.
"마크다운 파일 읽고 html 단일 파일로 개발해줘."

지난 수업에서 학생들이 산발적으로 질문을 해서 너무 고통스러워서 프롬프트 가이드를 제공해 줬단 말이죠.

과정 중에 어떤 시행착오를 겪었나요?

아무도 안 읽더라구요. 읽는 아이는 매우 소수.;;

프롬프트 가이드도 웹으로 만들어서 줘야겠다...는 생각을 했구요. 이미지가 없어서 너무 심심한데 뭔가 좀 재미있는 콘텐츠를 만들고 싶다는 생각이 들었습니다.

뭐가 재미있을지는 아직 모르겠지만 마크다운 파일 생성기를 썼을 때 성공하는 학생들의 숫자가 좀 더 많았던 것 같아서 좀 더 고도화해 보려고 합니다.

1
6개의 답글
밀어주고 끌어주는

온·오프라인 AI 스터디

AI로 어디까지 할 수 있는지
직접 확인하실 분만 신청하세요.