목표
게임과 무관하게 단독으로 돌아가면서, 게임이 보내는 사건 기록을 받아 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): 파일 경로에
..을 넣어 원래 폴더 밖의 파일에 접근하는 공격 수법
- 경로 조작(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가 원본 그대로 다운로드됨
- 토큰 없이 POST:
- Godot 4.6.2 빈 프로젝트(
playlog-test)에서 HTTPRequest로 이벤트 10개를 보내 확인함 (윈도우 데스크톱, 2026-10-23) - 보낸 스크립트는 저장소의
godot_test.gd로 남김
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. 엔드포인트 설계
- 목적 : 게임 없이도 돌아가는 서버로 사건 기록을 받고 저장하고 내보내기
- 설명 :
- 서버 틀
- FastAPI : 파이썬으로 웹 API 서버를 빠르게 만드는 프레임워크
- 엔드포인트 : 서버가 요청을 받아주는 주소 하나하나
- 주소 세 개
- POST /events : 게임이 보낸 기록 (JSON 배열)을 받아서 저장 —> 파일 생성 및 추가
- GET /runs : 저장된 판 (run) 목록, 기록 수, 파일 크기 보기 —> data 폴더의 파일 목록을 보기
- GET /runs/<run_id> : 한판의 기록 파일 통째로 내려 받기 —> 그 이름의 파일을 그대로 내려주기
- run_id : 한 판을 구분하는 이름표
- 저장 방식
- run_id별로 data/<run_id>.jsonl 파일에 한 줄씩 추가
- JSON Lines : 한 줄에 기록 하나씩 적는 형식
- 기록이 계속 쌓여도 파일 뒤에 붙이기만 하면 돼서 로그에 맞음
- run_id별로 data/<run_id>.jsonl 파일에 한 줄씩 추가
- 서버 틀
- 흐름
- 요청 도착 → 주소 확인
- POST /events → 기록마다 run_id 읽기 → data/run_id.jsonl 열기 (없으면 생성) → 한 줄씩 추가 → saved 개수 응답
- GET /runs → data 폴더의 .jsonl 파일 훑기 → 파일마다 이름/줄 수/크기 모아서 응답
- GET /runs/run_id → data/run_id.jsonl 있는지 확인 → 있으면 파일 그대로 응답, 없으면 404
- 요청 도착 → 주소 확인
B. 토큰 인증 검사
- 목적 : 아무나 서버에 기록을 못 보내게 막기
- 설명 :
- 토큰 확인
- 요청 헤더 X-Token 값이 서버의 토큰과 다르면 거절
- 헤더 : 요청 본문과 별도로 붙는 부가 정보
- 401 : 인증 안 됨을 뜻하는 응답 코드
- 요청 헤더 X-Token 값이 서버의 토큰과 다르면 거절
- 토큰을 코드에 적지 않은 이유
- 환경변수 PLAYLOG_TOKEN으로 실행할 때 넣음
- 환경변수 : 코드 밖에서 실행 환경에 따로 넣어두는 값
- 저장소에 올려도 토큰이 노출되지 않음
- 환경변수 PLAYLOG_TOKEN으로 실행할 때 넣음
- run_id 검사
- / 나 .. 이 들어가면 거부
- 경로 조작 : 파일 경로에 .. 을 넣어 원래 폴더 밖 파일을 접근하는 공격
- run_id 가 그대로 파일 이름이 되기 때문에 막아야 함
- / 나 .. 이 들어가면 거부
- 토큰 확인
- 흐름
- 요청 도착 → X-Token 꺼내기 → 환경변수 PLAYLOG_TOKEN과 비교
- 다르면 → 401 거절, 끝
- 같으면 → run_id 검사 (/ 나 .. 있으면 400 거절) → 통과하면 A로 넘어감
- 요청 도착 → X-Token 꺼내기 → 환경변수 PLAYLOG_TOKEN과 비교
C. 로컬 테스트
- 목적 : 서버가 설계대로 동작하는지, Godot에서도 되는지 확인
- 설명 :
- 서버에만 직접 호출 (맥)
- 토큰 없이 보내기 —> 401
- 토큰 넣고 10개 보내기 → saved 10, 조회/내려받기 정상
- Godot 에서 호출 (윈도우)
- 서버 실행 : 토큰 지정 → uvicorn으로 127.0.0.1:8000에 띄움
- uvicorn : FastAPI 서버를 실제로 실행해주는 프로그램
- 127.0.0.1 : 내 PC 자신을 가리키는 주소
- 8000 : 서버가 요청을 받는 포트 번호
- Godot 빈 프로젝트에서 스크립트로 10개 보내기 → 저장 → 목록 → 내려받기 순서로 확인
- HTTPRequest : Godot에서 HTTP 요청을 보내는 노드
- await : 요청 결과가 올 때까지 기다렸다가 다음 줄 실행
- 서버 실행 : 토큰 지정 → uvicorn으로 127.0.0.1:8000에 띄움
- 결과
- 세 요청 모두 200, saved 10, events 10, 10줄 그대로 내려받기
- 서버에만 직접 호출 (맥)
- 흐름
- 서버 실행 (토큰 지정 → uvicorn으로 127.0.0.1:8000)
- 맥 : 토큰 없이 보내기 (401 확인) → 토큰 넣고 10개 (saved 10 확인)
- 윈도우 Godot : 10개 보내기 → 목록 조회 → 내려받기
- 세 요청 모두 200 + 10줄 그대로 오면 성공
- 서버 실행 (토큰 지정 → uvicorn으로 127.0.0.1:8000)
D. 저장소 정리
- 목적 : 결과물을 팀장이 받아서 바로 돌릴 수 있게 넘기기
- 설명 :
- README
- 실행 방법 (맥/윈도우) , 토큰 전달 방법, 내려받은 파일 예시, 인수인계
- Git으로 기록하고 올리기
- git add → git commit → git push
- commit : 고른 변경을 메시지와 함께 한 번의 기록으로 저장
- Push : 내 PC의 기록을 Github에 올리기
- git add → git commit → git push
- 인수인계
- 외부 서비스 없이 PC + 포트 방식이라 계정 이전이 없음
- 저장소는 팀장이 그 쪽을 원하면 이전
- README

