# Show GN: 3일만에 antigravity로 만들어본 타워디펜스

> Clean Markdown view of GeekNews topic #25549. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=25549](https://news.hada.io/topic?id=25549)
- GeekNews Markdown: [https://news.hada.io/topic/25549.md](https://news.hada.io/topic/25549.md)
- Type: show
- Author: [cybrshin](https://news.hada.io/@cybrshin)
- Published: 2026-01-04T14:36:20+09:00
- Updated: 2026-01-04T14:36:20+09:00
- Original source: [tower.dsp.ai.kr](https://tower.dsp.ai.kr/)
- Points: 14
- Comments: 9

## Summary

**이모지 기반 타워디펜스**를 3일 만에 완성한 개인 프로젝트가 공개되었습니다. Next.js 14와 FastAPI를 조합해 웹 브라우저 전용으로 구현했으며, 캔버스 그래픽 대신 이모지를 활용해 개발 속도를 높였습니다. 타워는 **머지 레벨업 구조**로 공격력과 사거리가 함께 성장하고, 웨이브마다 일반·보스 패턴이 교차해 단조로움을 줄였습니다. Supabase와 Vercel을 이용한 간결한 배포 구조도 인상적입니다.

## Topic Body

원래는 claude code와 cursor를 쓰다가 작년 퇴사 후 11월부터 antigravity로 갈아타고나서 이것저것 토이 웹서비스 프로젝트를 하루마다 만들고 있었는데, 갑자기 3일전(1/2)에 타워디펜스 게임을 만들어 보고 싶어졌습니다.   
원래 웹frontend개발자라 앱으로 감싸기는 귀찮고, 그냥 데스크탑 브라우저 환경에서만 동작하게 하고, 캔버스 기반 동작 및 그림파일 작업은 너무 시간이 오래걸릴것 같아서 이모지를 최대한 활용하자는 아이디어로 시작했습니다.  
  
하루만에 끝내려고 했는데, 만들다보니 재미있어서 이것저것 수정/추가하다보니 3일이나 걸렸네요. ㅠㅜ  
  
기본적인 기술스택은 다음과 같습니다.   
  
- **Frontend**: Next.js 14+ (App Router), TypeScript, HTML5 Canvas  
- **Backend**: FastAPI, Python 3.9+, Pydantic  
- **Database**: Supabase (PostgreSQL)  
- **Deployment**: Vercel  
  
  
이런저런 수정을 거쳐 최종적으로 게임로직은 다음과 같이 결정되었습니다.  
##### 1. 타워 공격력  
타워는 머지(Merge)를 통해 레벨이 오를 때마다 사거리(Range)가 10씩 증가하지만, 공격력 효율은 1.8배로 조정됩니다 (밸런스).  
- **공식**: `Damage = floor(BaseDamage * (1.8 ^ (Level - 1)))`  
    - Lv 1: 10  
    - Lv 2: 18  
    - Lv 3: 32  
    - Lv 4: 57  
  
##### 2. 적 등장 패턴 (Wave Logic)  
단조로움을 없애기 위해 다양한 웨이브 패턴이 적용됩니다.  
- **일반 웨이브 (Horde Mix)**: 현재 티어 적(70%)과 이전 티어 적(30%)이 섞여서 등장합니다.  
- **보스 웨이브 (Every 6th)**: 보스는 시대의 최강 적 1마리가 등장하며, 주변에 쫄병들이 호위합니다.  
    - 보스 웨이브의 적 수: `Wave / 6` (웨이브 6에 1마리, 웨이브 12에 2마리...)  
  
##### 4. 골드 보상 (Active)  
적을 처치하거나 웨이브를 클리어하면 골드를 획득합니다.  
  
  
ps. 모바일 환경은 크게 고려하지 않았습니다. 터치이벤트를 추가했으나 폴드나 패드에서는 잘 되지만 작은 화면에서의 고려는 그다지 안했습니다~  
  
ps. 개선점 및 버그는 댓글로 알려주시면 시간날때마다 반영하겠습니다.  
  
https://tower.dsp.ai.kr/

## Comments



### Comment 52038

- Author: roxie
- Created: 2026-02-27T20:53:08+09:00
- Points: 1

아니 말이 안되는데... 700 웨이브요............???

### Comment 52076

- Author: cybrshin
- Created: 2026-02-28T11:56:38+09:00
- Points: 1
- Parent comment: 52038
- Depth: 1

가능합니다 ㅎㅎ

### Comment 48808

- Author: wancomplete
- Created: 2026-01-07T13:40:25+09:00
- Points: 1

저도 최근에 게임 만들어보다가 https://verse8.io 써봤는데, 이미지랑 2d 스프라이트 애니메이션까지 직접 만들어줘서 에셋 작업에 시간 안 쓰고 게임 로직에만 집중할 수 있더라고요. 거기다 플랫폼에서 바로 배포까지 되니까 별도로 호스팅 신경 쓸 필요 없어서 정말 편했습니다.

### Comment 48741

- Author: huiya
- Created: 2026-01-06T11:45:09+09:00
- Points: 1

재밌네요. 이모지가 적극적으로 쓰인게 굉장히 새롭게 느껴져요.  
제가 타워디펜스 게임을 자주 해보지 않아가지고 이게 맞는 의견일지 잘 모르겠는데, 웨이브 타임과 준비 타임이 나눠져 있지 않아서 웨이브가 오는 순간에도 타워의 위치를 바꿀 수 있게 되어있는게 이상하게 느껴집니다.   
극단적으로는 위치를 계속 바꿔가면서 매 순간 공격하게 할 수 있어서 사거리가 무의미하다고 느껴집니다.

### Comment 48742

- Author: cybrshin
- Created: 2026-01-06T12:29:04+09:00
- Points: 1
- Parent comment: 48741
- Depth: 1

재밌게 해 주셔서 감사합니다.  
저도 만들면서 위치를 이동하는게 이상하다고 느껴졌었는데, 그게 나름대로 이 게임의 장점이자 동적이고 컨트롤할수 있는 요소가 될수 있어 보여서 그대로 놔뒀습니다.  
사실 빨리 옮기려면 타워를 머지하지 않으면 정말 힘들거든요.  
옮기는 데에 대한 패널티를 추가로 둘지 등 게임 밸런스는 계속 고민할 포인트인것 같습니다.

### Comment 48780

- Author: huiya
- Created: 2026-01-07T09:40:36+09:00
- Points: 1
- Parent comment: 48742
- Depth: 2

앗 이미 다 고려하셨군요. 제가 생각이 짧았습니다.

### Comment 48782

- Author: cybrshin
- Created: 2026-01-07T09:41:25+09:00
- Points: 1
- Parent comment: 48780
- Depth: 3

아닙니다. 의견 감사합니다.

### Comment 48652

- Author: waylake
- Created: 2026-01-04T19:46:09+09:00
- Points: 1

안녕하세요 재밌게 플레이했습니다, 보안관련 피드백 드립니다.  
  
- 현재 HMAC 시크릿(서명용 키)이 프론트엔드에 노출되어 있습니다  
    - 프론트에서 직접적으로 gold, wave, lives, towers 를 변경할수있습니다  
  
vercel 과 supabase 요금제문제가 있을수있어서 알려드립니다  
  
```  
const state = {  
  towers: [],    
  gold: 9999,  
  wave: 1,       
  lives: 1       
};  
  
const state_raw = JSON.stringify(state);  
const user_id = "11111";  
const secret = "11111";  
  
(async () => {  
  const key = await crypto.subtle.importKey(  
    "raw",  
    new TextEncoder().encode(secret),  
    { name: "HMAC", hash: "SHA-256" },  
    false,  
    ["sign"]  
  );  
  const signatureBuffer = await crypto.subtle.sign(  
    "HMAC",  
    key,  
    new TextEncoder().encode(state_raw)  
  );  
  const signature = Array.from(new Uint8Array(signatureBuffer))  
    .map(b => b.toString(16).padStart(2, "0")).join("");  
    
  fetch("https://tower.dsp.ai.kr/api/sync", {  
    method: "POST",  
    headers: {  
      "Content-Type": "application/json",  
      "Accept": "*/*"  
    },  
    body: JSON.stringify({  
      user_id,  
      state_raw,  
      signature  
    })  
  })  
  .then(res => res.text())  
  .then(console.log)  
  .catch(console.error);  
})();  
  
```

### Comment 48653

- Author: cybrshin
- Created: 2026-01-04T19:50:16+09:00
- Points: 1
- Parent comment: 48652
- Depth: 1

앗, 감사합니다~
