# TODO 기록 - 업데이트 시각 (KST): 2025-12-09 19:28:55 KST ## 완료된 항목 - [x] Go Fiber 기반 GeoIP API 구조 결정 및 엔트리포인트 구현 (`cmd/server`) - [x] GeoLite2 조회 로직 작성 (`internal/geo/resolver.go`) - [x] Dockerfile, docker-compose로 컨테이너 실행 경로 구성 (GeoLite2 DB 볼륨 마운트) - [x] 기여 가이드 문서 작성 (`AGENTS.md`) - [x] Dockerfile 빌더/런타임 이미지 1.25.5-trixie로 전환하고 불필요 패키지 제거 - [x] README 작성 및 응답 샘플 추가 - [x] resolver 단위 테스트 추가 (`internal/geo/resolver_test.go`) - [x] `user_program_info_replica` DDL/CSV 임포터 추가 (`id bigint`, 텍스트 컬럼, timestamp KST 파싱, bool 플래그) 완료: 2025-12-09 18:32 KST - [x] 초기/일간 CSV 디렉토리 기반 임포트 + 로그 파일 기록(`log/`), upsert 로직 업데이트 완료: 2025-12-09 19:06 KST - [x] Fiber 프로세스 내 cron 스케줄러 추가(전일 덤프 스크립트 실행 + update_data 적용, KST cron 지원) 완료: 2025-12-09 19:28 KST - [x] MySQL CLI 의존성 제거, Go 기반 덤퍼(`cmd/user_program_dump`) 추가 및 `scripts/dump_and_import.sh`에서 사용하도록 변경 완료: 2025-12-10 09:34 KST - [x] 스케줄러 토글 env(`USER_PROGRAM_CRON_ENABLE`) 추가, true일 때만 크론 구동하도록 변경 완료: 2025-12-10 09:45 KST - [x] 크론 표현식 env(`USER_PROGRAM_CRON`) 제거, 코드에 KST 00:05 고정 스케줄 적용 완료: 2025-12-10 09:56 KST - [x] bash 스크립트 의존 없이 Go CLI(`user-program-sync`)로 덤프+임포트 수행, 스케줄러가 해당 CLI를 직접 호출하도록 변경 완료: 2025-12-10 09:50 KST - [x] 초기 적재+백필+일일 업데이트를 Go 라이브러리(`internal/userprogram`)로 통합, `user-program-sync`가 초기 CSV 임포트 후 최신 일자까지 덤프/적재하도록 리팩토링 완료: 2025-12-10 10:03 KST - [x] 증분 기준을 created_at 날짜에서 PK(id) 기반으로 변경, 마지막 id 이후 어제까지의 최대 id까지 덤프/업서트하도록 Sync/Dump 경로 리팩토링 완료: 2025-12-10 10:20 KST - [x] 컨테이너 사용자 UID/GID를 빌드 시 지정 가능하도록 하고 볼륨 소유권을 맞춰 권한 오류 해결 (`APP_UID`/`APP_GID`, chown 적용) 완료: 2025-12-10 10:56 KST - [x] access log 파일 출력 + 10MB 롤링, 헤더 길이 1KB로 절단 및 프록시 IP 정보 포함 완료: 2025-12-10 12:20 KST - [x] `ip_geoinfo` 테이블 초기/증분 upsert 자동화: sync 완료 후 public_ip 리스트를 CSV로 내보내고 신규 IP만 GeoIP 조회해 SQL 생성·실행하도록 추가 완료: 2025-12-10 12:27 KST - [x] 컨테이너 기동 시 `user-program-import` 자동 실행하도록 compose 커맨드 수정 (USER_PROGRAM_IMPORT_ON_START 플래그) 완료: 2025-12-10 13:25 KST ## 진행 예정 - [x] PostgreSQL 전용 Docker 이미지(또는 build 단계)에서 `maxminddb_fdw` 설치 후 `GeoLite2-City.mmdb` 볼륨을 `/data`로 마운트하는 `postgres` 서비스 추가 및 5432 외부 노출 - [x] 초기화 SQL을 `/docker-entrypoint-initdb.d/`로 넣어 `CREATE EXTENSION maxminddb_fdw; SERVER maxminddb ...` 정의, 필요한 `FOREIGN TABLE`/`VIEW` 설계 (country/region/city/lat/lon/time_zone 등) - [x] FDW 기반 조회 최적화를 위한 `inet` 인자 함수/VIEW 설계(예: `SELECT * FROM city_location WHERE network >>= inet($1) ORDER BY masklen(network) DESC LIMIT 1`) - [x] 앱 구성 확장: `GEOIP_BACKEND=mmdb|postgres`, `DATABASE_URL` 등 env 추가, `internal/geo`에 Postgres resolver 구현 및 DI로 선택 연결, 시작 시 backend/DB 헬스 체크 로그 - [ ] Postgres 컨테이너에 GeoLite mmdb 및 init SQL 디렉터리 마운트 추가 반영 후 compose.infra.yml/dev 실행 경로 검증 - [x] docker-compose 단일 스택에서 db healthcheck 추가 후 api가 service_healthy 상태를 기다리도록 depends_on 조건 설정 - [ ] Fiber 라우트에서 DB resolver와 파일 resolver가 동일한 응답 스키마를 반환하도록 리스폰스 변환/에러 핸들링 정리 - [ ] 테스트: 파일 기반은 그대로 유지, DB resolver용 통합 테스트(테스트 컨테이너/compose) 및 테이블 기반 케이스 추가; 라이선스 문제 없이 쓸 수 있는 mmdb 픽스처 고려 - [ ] 문서화: README에 Postgres/FDW 실행 방법, 샘플 쿼리, 보안/포트 노출 주의사항, mmdb 교체 절차 추가 - [x] `go mod tidy` 재실행으로 의존성 정리 및 필요한 DB 드라이버 추가 - [ ] `maxminddb_fdw` 제거 후 mmdb -> Postgres 적재 파이프라인 설계: mmdb SHA256을 테이블에 기록해 변경 시에만 스테이징 로드+인덱스 생성 후 트랜잭션 rename으로 교체, 변경 없으면 스킵 - [ ] `maxminddb_fdw` 제거 후 mmdb -> Postgres 적재 파이프라인 설계: Go 기반 변환기로 스테이징 테이블에 로드하고 트랜잭션 rename으로 다운타임 없이 교체, 업데이트 주기/운영 방법 정의 - [ ] 시행착오 기록: maxminddb-golang v1.11.0은 `reader.Networks()`가 에러를 반환하지 않는 단일 반환 함수임. `reader.Networks(0)`/다중 반환 처리 금지 (재시도하지 말 것) - [ ] compose에서 loader 단독 서비스 제거, api entrypoint에서 loader 실행 → post-start 훅으로 문서화 및 대기 전략 검토 - [ ] Postgres 초기 설정 튜닝: `max_wal_size`를 4GB로 확대해 초기 bulk load 시 checkpoint 난발 방지 (deploy/postgres/init/01_tuning.sql 반영) - [ ] compose에서 api가 loader 완료 대기 때문에 기동 지연됨 → loader `service_started` 조건으로 완화, 향후 API 기동/데이터 적재 병행 여부 문서화 필요 - [ ] MySQL `user_program_info` 증분 백업 설계: Postgres 백업 테이블 DDL(동일 컬럼, PK=id, `created_at` 인덱스), `login_public_ip varchar(45)`, UTC 기준 - [ ] `sync_meta(last_synced_at)` 테이블 작성 및 워터마크 쿼리 정의: `created_at > last_synced_at - interval '5 minutes'` + `max(created_at)`로 메타 갱신 - [ ] 증분 적재 파이프라인 구현: MySQL pull → Postgres upsert(ON CONFLICT id) 배치 처리, 빈 배치 처리/타임존 변환/정합성 로그 - [ ] 운영 트리거 설계: 15분 cron 기본, API 수동 트리거(health 포함) 여부 결정, 실패 재시도 및 알림 연동