실습 로그 / Frozen Stiff · 융합캡스톤디자인 게임

캡스톤 2단계 · 플레이 기록 수집 서버

게임과 무관하게 단독으로 돌아가면서, 게임이 보내는 사건 기록을 받아 run_id별로 저장하고, 조회·다운로드까지 되는 서버를 만든다.

진행 과정은 실습 내용을 AI와 함께 정리했고, 배운 점은 그걸 보고 제가 이해한 대로 직접 썼습니다. 배운 점 바로가기 ↓

구축보안

목표

게임과 무관하게 단독으로 돌아가면서, 게임이 보내는 사건 기록을 받아 run_id별로 저장하고, 조회·다운로드까지 되는 서버를 만든다.

진행 과정

A. 엔드포인트 설계

  • FastAPI로 엔드포인트 세 개를 만들고, 모든 요청에 X-Token 헤더 검사를 넣음 (인증은 B)
    • FastAPI: 파이썬으로 웹 API 서버를 빠르게 만드는 프레임워크
    • 엔드포인트: 서버가 요청을 받는 주소 하나하나
    • POST /events: JSON 배열로 받은 이벤트를 run_id별로 묶어 data/<run_id>.jsonl에 한 줄씩 추가 저장
    • GET /runs: 저장된 run_id 목록, 이벤트 수, 파일 크기를 반환
    • GET /runs/<run_id>: 해당 run 파일을 그대로 다운로드
  • JSON Lines(.jsonl)로 저장하는 이유: 기록이 계속 쌓여도 파일 뒤에 한 줄씩 붙이기만 하면 됨
    • JSON Lines: 한 줄에 이벤트 하나씩 적는 형식. 파일 하나에 큰 배열 하나를 담는 일반 JSON과 다름

B. 토큰 인증 검사

  • 요청 헤더 X-Token 값이 환경변수 PLAYLOG_TOKEN과 다르면 401로 거절
    • 환경변수: 코드에 적지 않고 실행 환경에 따로 저장해두는 값. 토큰 같은 비밀값을 코드에 박아두지 않으려고 씀
    • 401: "인증 안 됨"을 뜻하는 HTTP 상태 코드
  • run_id에 /나 ..가 들어가면 거부. run_id를 그대로 파일 경로에 쓰기 때문
    • 경로 조작(path traversal): 파일 경로에 ..을 넣어 원래 폴더 밖의 파일에 접근하는 공격 수법

C. 로컬 테스트

  • 서버를 로컬에 띄우고 직접 요청을 보내 확인함
    • 토큰 없이 POST: 401 invalid or missing X-Token
    • 토큰 포함, 이벤트 10개 POST: {"saved": 10, "runs": ["2026-10-22T00-00-00_test"]}
    • GET /runs: {"runs": [{"run_id": "2026-10-22T00-00-00_test", "events": 10, "size_bytes": 1537}]}
    • GET /runs/2026-10-22T00-00-00_test: 10줄 JSON Lines가 원본 그대로 다운로드됨
  • Godot 4.6.2 빈 프로젝트(playlog-test)에서 HTTPRequest로 이벤트 10개를 보내 확인함 (윈도우 데스크톱, 2026-10-23)
    • POST /events: 200 {"saved":10,"runs":["godot-test-2026-10-23T01-05-23"]}

    • GET /runs: 해당 run이 "events":10으로 집계됨

    • GET /runs/godot-test-2026-10-23T01-05-23: 보낸 10줄이 그대로 내려받아짐

      Godot 출력창: POST /events, GET /runs, GET /runs/<run_id> 세 요청이 200으로 응답하고 보낸 이벤트 10줄이 그대로 내려받아짐

    • 서버 창에도 요청 3개 모두 200 OK

    • HTTPRequest: Godot에서 HTTP 요청을 보내는 노드

  • 보낸 스크립트는 저장소의 godot_test.gd로 남김

윈도우 PowerShell: 토큰을 환경변수로 넣고 uvicorn으로 서버 실행, 요청 3개 모두 200 OK (토큰 값은 가림)

D. 저장소 정리

  • README에 실행 방법(맥/윈도우), 토큰 전달 방법, API, 내려받은 파일 예시, 검증 기록, 인수인계 절차를 정리함
    • README: 저장소 첫 화면에 보이는 설명 문서
  • Godot 검증에 쓴 스크립트를 godot_test.gd로 저장소에 넣음. 기획자가 같은 테스트를 다시 돌릴 수 있게
  • git add → git commit → git push로 GitHub에 올림 (Private 저장소)
    • commit: 고른 변경을 메시지와 함께 한 번의 기록으로 저장
    • push: 내 PC의 기록을 GitHub로 올리기
    • 테스트로 쌓인 .jsonl 기록은 .gitignore로 빼서 안 올라감
  • 외부 서비스 없이 PC + 포트 방식이라 계정 이전은 없음 (3단계에서 홈랩 VM 106 + Cloudflare 터널로 바꿈). 저장소는 팀장이 원하면 그쪽 계정으로 이전

남은 작업

  • 팀장 GitHub 아이디 받아서 저장소 초대 (완료, 원하면 저장소 이전)

배운 점

직접 작성

A. 엔드포인트 설계

  • 목적 : 게임 없이도 돌아가는 서버로 사건 기록을 받고 저장하고 내보내기
  • 설명 :
    1. 서버 틀
      • FastAPI : 파이썬으로 웹 API 서버를 빠르게 만드는 프레임워크
      • 엔드포인트 : 서버가 요청을 받아주는 주소 하나하나
    2. 주소 세 개
      • POST /events : 게임이 보낸 기록 (JSON 배열)을 받아서 저장 —> 파일 생성 및 추가
      • GET /runs : 저장된 판 (run) 목록, 기록 수, 파일 크기 보기 —> data 폴더의 파일 목록을 보기
      • GET /runs/<run_id> : 한판의 기록 파일 통째로 내려 받기 —> 그 이름의 파일을 그대로 내려주기
        • run_id : 한 판을 구분하는 이름표
    3. 저장 방식
      • run_id별로 data/<run_id>.jsonl 파일에 한 줄씩 추가
        • JSON Lines : 한 줄에 기록 하나씩 적는 형식
        • 기록이 계속 쌓여도 파일 뒤에 붙이기만 하면 돼서 로그에 맞음
  • 흐름
    • 요청 도착 → 주소 확인
      • POST /events → 기록마다 run_id 읽기 → data/run_id.jsonl 열기 (없으면 생성) → 한 줄씩 추가 → saved 개수 응답
      • GET /runs → data 폴더의 .jsonl 파일 훑기 → 파일마다 이름/줄 수/크기 모아서 응답
      • GET /runs/run_id → data/run_id.jsonl 있는지 확인 → 있으면 파일 그대로 응답, 없으면 404

B. 토큰 인증 검사

  • 목적 : 아무나 서버에 기록을 못 보내게 막기
  • 설명 :
    1. 토큰 확인
      • 요청 헤더 X-Token 값이 서버의 토큰과 다르면 거절
        • 헤더 : 요청 본문과 별도로 붙는 부가 정보
        • 401 : 인증 안 됨을 뜻하는 응답 코드
    2. 토큰을 코드에 적지 않은 이유
      • 환경변수 PLAYLOG_TOKEN으로 실행할 때 넣음
        • 환경변수 : 코드 밖에서 실행 환경에 따로 넣어두는 값
        • 저장소에 올려도 토큰이 노출되지 않음
    3. run_id 검사
      • / 나 .. 이 들어가면 거부
        • 경로 조작 : 파일 경로에 .. 을 넣어 원래 폴더 밖 파일을 접근하는 공격
        • run_id 가 그대로 파일 이름이 되기 때문에 막아야 함
  • 흐름
    • 요청 도착 → X-Token 꺼내기 → 환경변수 PLAYLOG_TOKEN과 비교
      • 다르면 → 401 거절, 끝
      • 같으면 → run_id 검사 (/ 나 .. 있으면 400 거절) → 통과하면 A로 넘어감

C. 로컬 테스트

  • 목적 : 서버가 설계대로 동작하는지, Godot에서도 되는지 확인
  • 설명 :
    1. 서버에만 직접 호출 (맥)
      • 토큰 없이 보내기 —> 401
      • 토큰 넣고 10개 보내기 → saved 10, 조회/내려받기 정상
    2. Godot 에서 호출 (윈도우)
      • 서버 실행 : 토큰 지정 → uvicorn으로 127.0.0.1:8000에 띄움
        • uvicorn : FastAPI 서버를 실제로 실행해주는 프로그램
        • 127.0.0.1 : 내 PC 자신을 가리키는 주소
        • 8000 : 서버가 요청을 받는 포트 번호
      • Godot 빈 프로젝트에서 스크립트로 10개 보내기 → 저장 → 목록 → 내려받기 순서로 확인
        • HTTPRequest : Godot에서 HTTP 요청을 보내는 노드
        • await : 요청 결과가 올 때까지 기다렸다가 다음 줄 실행
    3. 결과
      • 세 요청 모두 200, saved 10, events 10, 10줄 그대로 내려받기
  • 흐름
    • 서버 실행 (토큰 지정 → uvicorn으로 127.0.0.1:8000)
      • 맥 : 토큰 없이 보내기 (401 확인) → 토큰 넣고 10개 (saved 10 확인)
      • 윈도우 Godot : 10개 보내기 → 목록 조회 → 내려받기
      • 세 요청 모두 200 + 10줄 그대로 오면 성공

D. 저장소 정리

  • 목적 : 결과물을 팀장이 받아서 바로 돌릴 수 있게 넘기기
  • 설명 :
    1. README
      • 실행 방법 (맥/윈도우) , 토큰 전달 방법, 내려받은 파일 예시, 인수인계
    2. Git으로 기록하고 올리기
      • git add → git commit → git push
        • commit : 고른 변경을 메시지와 함께 한 번의 기록으로 저장
        • Push : 내 PC의 기록을 Github에 올리기
    3. 인수인계
      • 외부 서비스 없이 PC + 포트 방식이라 계정 이전이 없음
      • 저장소는 팀장이 그 쪽을 원하면 이전