카드 게임 제작 기록 처음부터 보기 : https://helloworld.ai.kr/14
카드 게임 제작 ( 1 ) - 회고
게임 링크 : cardgame.perfect.ai.kr 개발 기간 : 2025.10.28 ~ 2025.11.27총 소요 시간 : 110시간Lovable에게 감탄 ( UI 작업 )https://blog.helloworld.ai.kr/15JWT 토큰은 json web token token이다 ( 로그인 구현 )https://blog.helloworl
helloworld.ai.kr
게임 링크 : cardgame.perfect.ai.kr
Magician Duel
cardgame.perfect.ai.kr
이 시점의 소요 시간 : 36시간 / 110시간
- DB 스키마 설계 / 카드 정보 json 작성
- DB 초기화, seed 작업
- Supabase와 서버 연동
- 로그인 방식과 웹 스토리지에 대한 이해
작업 과정
이번 프로젝트에서 반복적으로 사용한 루틴이 있다.
1. GPT와 의논하며 문서 작성
기획이나 구현 방법을 GPT와 계속 질의응답하면서 결정했다.
- 모르는 개념 정리
- 구현 방식 비교
- 선택지 정리
특히 이번에는 로그인 구현과 웹 스토리지 부분을 꽤 명확히 이해하게 됐다.
2. 커서에게 기획 던져주기
스펙이 어느 정도 정리되면 커서에게 통째로 넘긴다.
폴더 구조나 초기 뼈대를 혼자서 잡기 막막할 때가 많은데,
커서가 만들어준 구조를 보면 감이 좀 잡힌다.
3. 코드 이해 및 수정
코드를 한 줄씩 읽어가며
- 합리적인지 판단
- 모르는 부분은 GPT에게 질문
- 이상한 부분 수정
- 스펙 자체가 틀렸으면 설계 수정
4. 문서 수정
코드 변경 사항을 문서에 반영한다.
이 과정을 반복하며 DB 구성과 API 구현을 진행했다.
GPT님이 없었으면 3배는 걸렸을 것 같다.
로그인
예전에 로그인은 세션 방식으로만 구현해봤다.
찾아보니 요즘은 JWT(Json Web Token)를 많이 쓴다고 한다.
세션 방식
서버 DB에 로그인 상태 저장
서버가 로그인 상태를 직접 들고 있음
→ stateful
JWT 방식
인증 정보를 토큰에 담아 클라이언트가 보관
서버는 토큰 유효성만 검증
→ stateless
JWT 방식의 구조가 훨씬 가볍다.
JWT 구조
JWT는 세 부분으로 나뉜다.
헤더 : 어떤 알고리즘으로 서명했는지
페이로드 : 유저 정보, 만료 시간 등 실제 데이터
시그니처 : JWT_SECRET 값으로 서명한 검증값
(JWT_SECRET은 그냥 랜덤 값 생성해서 활용한다)
로그인 흐름은 다음과 같다.
서버가 JWT 발급 → 클라이언트가 저장 → 이후 모든 요청마다 JWT 첨부 → 서버가 매 요청마다 검증
JWT의 위험과 대응
JWT 토큰이 탈취되면 유저를 사칭할 수 있다.
대표적인 대응 방법 두 가지.
- HttpOnly 쿠키
JWT를 HttpOnly 쿠키에 저장하면 JS 코드에서 접근할 수 없다.
→ XSS로 토큰이 유출될 가능성을 줄일 수 있다.
- 짧은 만료 + Refresh Token
access token의 만료 기간은 짧게, refresh token은 길게 만든다.
필요할 때 access token을 재발급한다.
실무에서 거의 표준처럼 쓰이는 패턴이다.
OAuth
OAuth는 외부 플랫폼이 대신 유저를 인증해주는 방식이다.
흐름은 다음과 같다.
클라이언트 → 외부 플랫폼 로그인
외부 토큰을 서버로 전달
서버가 외부 플랫폼에 유효성 확인
서버가 자체 JWT 발급
즉 OAuth를 쓰더라도 내 서비스 내부에서는 결국 JWT를 쓰게 된다.
웹 스토리지
브라우저 저장소에 대한 내용도 한 번 정리했다.
그동안 캐시, 쿠키, 로컬스토리지 개념을 좀 혼용해서 알고 있었다.
Session Storage
새로고침을 해도 남아있지만 탭을 닫는 순간 사라진다.
활용처가 애매하다.
임시 폼 데이터 정도에 쓸 듯.
Local Storage
무난하게 쓰기 좋은 저장소.
브라우저를 닫았다가 다시 열어도 남아있다.
key-value 방식으로 텍스트 저장
도메인당 약 5MB 할당
주요 용도:
- UI 설정
- zustand persist
- 민감하지 않은 데이터
IndexedDB
사실상 브라우저에 달린 데이터베이스라고 볼 수 있다.
수 MB ~ 수 GB
비동기 입출력
구조화 데이터 저장
이미지 저장 가능
오프라인 기능이나 대용량 캐시에 적합하다.
Cache Storage
서비스 워커와 함께 작동한다.
HTML, JS, 이미지 같은 정적 자원을 통째로 캐싱할 때 사용.
PWA에서 자주 쓰인다.
쿠키
서버가 설정하면 브라우저는 같은 도메인 요청마다 자동으로 첨부한다.
HttpOnly 옵션을 켜면 JS 코드에서 접근할 수 없다.
인증 토큰 보관 용도로서 중요하다.
타입 관련 문법
커서가 적어준 코드 중 인상 깊었던 타입스크립트 문법을 정리해둔다.
좀 겉멋 같은 느낌이 있다.
1. Pick
attrs: Pick<DeckRaw, "name"> & {
main_cards: DeckList;
cata_cards: DeckList;
}
특정 타입에서 필요한 속성만 선택적으로 추출한다.
결과 타입:
{
name: string;
main_cards: DeckList;
cata_cards: DeckList;
}
2. typeof, keyof, as const
export const HttpStatus = {
OK: 200,
CREATED: 201,
NOT_FOUND: 404,
} as const;
export type HttpStatusCode =
typeof HttpStatus[keyof typeof HttpStatus];
핵심:
- as const → 리터럴 타입 고정
- keyof → 키 유니언
- typeof T[K] → 값 유니언
결과:
200 | 201 | 404
당시에는 이게 무슨 문법이야... 라는 생각이었는데 javascript에 enum 클래스가 존재하지 않아서 이 문법이 굉장히 유용하다.
마치 관용구처럼 잘 활용하고 있다.
3. value is type
export function isDeckList(value: unknown): value is DeckList {
…
}
사용자 정의 타입 가드.
이 함수가 true를 반환하면
value가 DeckList 타입임을 보장한다.
다음 단계
문서 작업, 데이터베이스 세팅, 로그인 구현을 완료했다.
이제 HTTP API들을 만들고 백엔드와 프론트엔드를 연결할 차례다.
다음 글 보러 가기 : https://helloworld.ai.kr/17
'개발로그' 카테고리의 다른 글
| 카드 게임 제작 ( 5 ) - zustand는 나가있어 (0) | 2026.02.16 |
|---|---|
| 카드 게임 제작 ( 4 ) - JS 이벤트 루프와 비동기 처리의 이해 (0) | 2026.02.16 |
| 카드 게임 제작 ( 2 ) - Lovable에게 감탄 (0) | 2026.02.16 |
| 카드 게임 제작 ( 1 ) - 회고 (0) | 2026.02.16 |
| 주로 사용하는 기술 스택과 배포 환경 (0) | 2026.02.16 |