Joyuno's Dev Blog

2026-10-12 (월) 개발일지

dev-blog

오늘은 게임 리브랜딩, 자동이력서 지원 프로세스 개선, 토지이음 보안 감사, lepons 정리까지 네 가지 이슈를 다뤼다. 공통점은 ‘처음 생각한 접근이 틀렸거나 부족해서, 더 깊이 파고들어야 했다’는 점이다.

Godot 4.6 리브랜딩 — “색 블로킹 부재” 피드백이 테마 토큰 재정의로 이어진 과정

증상: 듀오링고 스타일 피드백이 들어왔다. 구체적으로 세 가지 — 색 블로킹 부재, 타일 평면감, 채움 부족. 화면을 채우는 리듬감이 없다는 지적이 가장 아팠다.

첫 가설: 단순히 theme_setup.gd의 팔레트만 바꾸면 된다고 생각했다. 기존 C_INK, C_PAPER 같은 기본 토큰만 수정하면 UI 전체에 반영될 거라 봤다.

원인 추적: 하지만 mutation test(164개 계약 검증)를 돌리자 예상과 다른 색상이 튀어나왔다. C_EDGE, C_PLATFORM_RANCH, PLATFORM_ACCENTS 같은 새로운 토큰이 없으면 플랫폼별 구분이 안 되는 구조였다. 문제를 쪼개기로 했다. Piece 1(테마 토큰 시스템)과 Piece 2(OPIc 콘텐츠 와이어링)를 분리. Piece 1에 집중했다.

요구사항 대응 토큰
색 블로킹 부재 C_EDGE, C_PLATFORM_RANCH, PLATFORM_ACCENTS
타일 평면감 TILE_EDGE_SHADE, TILE_EDGE_GRADIENT
채움 부족 C_INK 라벨 + 플랫폼별 단색 채움

해결: theme_setup.gd에 새로운 토큰 세트를 정의하고, tile_button() 메서드가 이 토큰을 기반으로 UI를 그리도록 수정했다. OPIc 모드는 모두 OPIc 퍼플(단일색)을 사용하게 해서, 컬러가 ‘플랫폼 선택’이라는 원래 의도를 유지했다.

검증: Full suite 21 scripts 0 failed. theme_contract_test 164 checks 0 failed. Mutation test에서도 의도한 색상이 정확히 매핑됐다.

자동이력서 지원 — 현대오토에버 실패가 프로세스 피드백 시스템을 만든 이유

증상: 현대오토에버 지원이 마감 시각 13:20을 넘겼다. 13:22에 확인했을 때 공고 페이지는 404, 마이페이지에는 “제출한 지원서가 없습니다”가 떠 있었다. 지원서 전 항목과 자소서 3문항은 10/8까지 채워 임시저장해 둔 상태였다.

처음엔 단순 시간 관리 문제라고 생각했다. 하지만 성적증명서 ‘3개월 이내 발급’ 요건에 대한 답변 대기가 원인이었다.

원인 추적: 공증 재발급이 필요한 상황이라, 사유 기재란에 적을지 기존 GPA 증명서로 대체할지 결정을 못 하고 있었다. 이 경험을 체계화해야 했다. 세 가지 피드백 규칙을 문서화했다.

해결:

  • feedback_experience_from_dev_md.md: 자소서 경험은 포트폴리오 개발 md에서 구체 사건을 가져온다. 디테일이 부족하면 사용자에게 직접 묻는다.
  • feedback_transcript_recency_requirement.md: ‘3개월 이내 발급’ 요건은 공증 재발급이 어려우니, 사유 기재란이 있으면 적고 없으면 기존 GPA 증명서로 제출한다.
  • feedback_apply_filters_location_no_intern.md: 인턴(체험형·채용연계형 포함) 지원 안 함. 근무지가 서울·경기가 아니면 지원 안 함.

검증: 이후 지원부터 이 규칙들이 자동 적용됐다. 안랩 ML/MLOps는 규칙에 따라 지원하지 않음으로 정리했다.

토지이음 보안 감사 — JWT만 믿었다간 뚫렸을 스레드풀 고갈 취약점

증상: land.codefill.co.kr 공개 모드(serve --public)의 보안 감사를 진행했다. 접근 제한(Access) 앱과 JWT 인증은 기본으로 깔려 있었지만, 실제 공격 표면을 확인할 필요가 있었다.

첫 가설: JWT 인증과 읽기 전용 게이트만 믿으면 충분하다고 생각했다. SQL 인젝션과 패스 트래버설이 전부라고 봤다.

MAX_HEAVY 제한이 candidate 관련 파라미터를 커버하지 않는다는 걸 발견했을 때 등골이 서늘했다.

원인 추적: 공격 스크립트를 돌리자 예상 밖의 약점이 드러났다.

  1. 스레드풀 고갈: /api/candidates?unit=all 엔드포인트가 MAX_HEAVY 제한을 우회했다. 45개 요청 버스트로 스레드풀을 꽉 채우면, 정상 사용자의 /api/codes 요청이 0.05s → 1.65s, 심지어 정적 파일 /index.html도 0.01s → 1.62s로 느려졌다.
  2. 에러 정보 노출: {"sqlite": "no such table: ..."} 에러 메시지에 DB 스키마 정보가 그대로 드러났다. db_path 절대 경로도 누출됐다.
  3. 타임아웃: /api/stats가 기본 120초 타임아웃을 그대로 사용해서 44.9초 동안 블로킹되고 있었다.

해결:

  • MAX_HEAVY 범위에 candidate 관련 파라미터를 추가했다.
  • 에러 핸들러에서 SQLite 메시지를 필터링하고, db_path 노출을 막았다.
  • /api/stats 타임아웃을 5초로 하향 조정했다.

검증: 수정 후 동일한 공격 스크립트로 재검증. 스레드풀 고갈이 차단되고, 에러 메시지가 `{“detail”: “Internal server error”