CI 의 빈 DB 에서 e2e 가 앱을 띄운 뒤에 마이그레이션을 돌려, 실행기의 기동 정리가
아직 없는 테이블을 찾다가 기동이 실패했다. 앱을 닫지 못해 1분 주기 정리 작업이 남아
jest 가 끝나지 않았고 CI 가 러너 제한 시간까지 멈춰 있었다.
- 기동 정리는 실패해도 기동을 막지 않고 error 로그만 남긴다
- 앱 전체를 띄우는 e2e 는 운영처럼 마이그레이션을 먼저 돌린다
- 준비가 실패해도 afterAll 에서 앱을 반드시 닫는다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
설계 4단계 — 키 없이 생성 흐름을 끝까지 돌려 볼 수 있게 API·테이블·에러 코드를
먼저 세우고, AI 자리는 가짜 클라이언트로 채웠다(5단계에서 실제 모델로 바꾼다).
- 조건 해석·생성 시작·진행 조회·취소·평가·오늘 남은 횟수 API 6개
- ai_course_generation 테이블(마이그레이션)과 에러 코드 AI_001~004
- 기기 단위 하루 횟수(결과가 나온 생성만 차감)·시도 상한·하루 비용 상한·동시 생성 막기
- 프로세스 안 실행기: 동시 5건, 작업당 45초, 취소 시 AI 호출 중단,
기동·종료 때 끝나지 못한 작업은 실패(차감 없음)로 정리
- 후보 찾기 → 거르기·줄이기 → AI 초안 → 시간표 검증·고치기 → 결과 조립
- 요청 문장의 연락처는 저장 전에 가리고, 현재 위치는 약 100m 단위로만 남긴다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KOPIS 는 일정이 바뀐 공연 제목에 "[일정변경]" 을 붙이고 새 id 로 다시 등록한다. 그래서 같은 공연장·같은 날
같은 공연이 두 번 보였다(2026-11-04 회귀 점검: 고령 남진 콘서트).
- 제목 앞의 "[일정변경]" 은 뗀다(화면에는 바뀐 날짜가 이미 보인다)
- 같은 공연장·같은 기간·같은 제목이면 먼저 등록된 것만 남기고 나머지는 숨긴다
문화포털 전시에 쓰는 "제목 6글자 이상 겹침" 기준은 공연에 쓰지 않는다. 처음 적용했을 때 음악제 이름을 앞에 붙인
같은 날 다른 공연("포항국제음악제, …" 두 공연)을 합쳐 버려서, 제목이 같을 때만 합치게 했다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
장소 상세 '함께 가기 좋은 곳'은 확인 안 된 상가정보 매장을 빼야 한다(2026-11-02 합의).
앱이 가까운 30곳을 받아 거르면 도심처럼 상가가 빽빽한 곳은 받은 곳이 모두 상가라 칸이 비었다.
관광공사 장소 상세 860곳 중 30곳은 칸이 사라지고 186곳은 덜 보였다(봉산서원은 1.5km 안에
관광공사 장소가 53곳인데 0곳).
trusted=true 면 관광공사 장소와 운영자가 확인한 장소만 주고 '추천 안 함' 판정도 뺀다.
선택 조건이라 기존 호출은 그대로다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
from=2030-13-01 처럼 없는 시작일에 to 를 생략하면 끝 날짜를 계산하다 RangeError 가 나서
VAL_001(400) 대신 SYS_001(500)이 나갔다. from=9999-12-31 은 DB 가 읽지 못하는 끝 날짜를 만들었다.
날짜 계산을 shared/utils/iso-date.ts 순수 함수로 옮기고, 없는 날짜와 네 자리 연도를 넘는 결과는
null 로 알려 VAL_001 로 응답한다. 앱은 항상 두 날짜를 보내므로 사용자에게는 보이지 않았다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
빈 DB 에서 여러 e2e 스위트가 동시에 마이그레이션하면 같은 테이블을 만들다
pg_class_relname_nsp_index 중복으로 무작위 실패했다(스위트 5개에서 재현, CI 도 빈 DB 라 같은 위험).
스위트들은 한 DB 를 함께 쓰므로 maxWorkers 1 로 순서대로 돌린다. 빈 DB 두 번 연속 통과(약 30초).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
한국문화정보원에 같은 전시가 제목만 다르게 두 번 등록되어 목록에 2건으로 나왔다
("문준호 전 - 남겨진 것들을 위한 풍경" / "문준호 《남겨진 것들을 위한 풍경》", 같은 갤러리·같은 기간).
같은 장소 · 같은 시작·종료일 · 제목 비교 키가 연속 6글자 이상 겹치면 같은 전시로 보고
먼저 등록된 것만 남긴다. 장소·기간만으로 합치지 않는 이유는 여러 전시실에서 같은 날
여러 전시를 여는 곳이 있어서다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
전시는 관광공사·KOPIS 어디에도 없다. 한국문화정보원 한눈에보는문화정보(대구 90일 전시 13건)를
place:sync -- kcisa 로 매일 받는다.
- 분야가 '전시'인 것만 받는다(공연은 KOPIS 와 겹친다)
- 관광공사·KOPIS 에 이름 비교 키가 같고 기간이 겹치는 행사가 있으면 넣지 않는다(event-name.ts)
- 상세는 새 전시와 7일 지난 전시만(원본에 수정일이 없다), 좌표는 목록의 gpsY(위도)·gpsX(경도)
- 이미지는 이용 조건(공공누리 1유형 여부)을 확인하기 전이라 저장하지 않는다
- htmlToText 가 따옴표·가운뎃점·번호 엔티티도 푼다 — 제목이 &lsquo; 처럼 이중 인코딩되어 온다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
대구 공연은 관광공사에 거의 없고(90일 1건) KOPIS 에는 153건이 있다.
place:sync -- kopis 로 대구·경북 공연을 매일 받는다.
- 목록 → 새·바뀐(afterdate)·7일 지난 공연만 상세 → 새 공연장만 시설 상세 → 시설 좌표를 공연에 복사
- 시설 좌표가 주소의 시·도 영역 밖이면 비운다(대구 중구 주소에 경주 좌표인 시설이 있었다)
- 포스터 http 주소를 https://kopis.or.kr 로 바꿔 저장, 0.6초 간격 호출(초당 10회 차단)
- 공공데이터 클라이언트에 키 파라미터 이름·호출 간격·경로를 나눠 받게 함
- XML 응답 해석에 fast-xml-parser 추가. 컨테이너 npm 11.20 이 만든 잠금 파일은
선택 peer·@emnapi 항목을 지워 CI(npm 10)·호스트의 npm ci 가 실패해서, 새 패키지 항목만 기존 잠금에 더했다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KOPIS 공연·한국문화정보원 전시를 관광공사 행사와 같은 테이블에 받으려면
관광공사 contentid 단독 유니크로는 출처가 섞인다. (source, source_id) 로 바꾸고,
KOPIS 공연은 좌표가 공연장 상세에만 있어 event_venue 에 따로 둔다.
- 전시는 몇 달씩 하는 것이 보통이라 상시 프로그램 기간 상한을 종류별로 나눔(전시 183일)
- 관광공사 동기화는 관광공사 행사만 숨기도록 범위를 좁힘
- 조회 API 에 장르·예매 링크·출처별 표기 추가, 없는 날짜(2030-13-01)가 500 이 되던 것을 VAL_001 로
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
홈 '이번 주말 축제·행사'와 장소 상세 '근처 축제·행사'에 쓸 데이터를 채운다.
- place:sync -- event: 관광공사 searchFestival2 를 오늘~90일 기간으로 받아
festival_event 에 upsert(서버는 기간이 겹치는 행사를 준다). 지역 조건을 보내고
응답에서도 다른 지역 행사는 거른다. 상세(소개·요금·공연 시간·장소)는 새로 생기거나
수정된 행사만 받는다. 기간 안에서 목록에 없는 행사는 숨긴다
- GET /api/events: 기간(기본 오늘~30일, 최대 92일)·근처(좌표+반경)·분류·커서.
60일 넘는 상시 프로그램과 숨김 행사는 뺀다. 항목에 오늘 진행 중 여부
- GET /api/events/{id}: 소개·공연 시간·요금·장소·홈페이지·출처
- 마이그레이션: festival_event 에 분류 코드·지역 코드·장소명·전화·홈페이지·
작은 사진·원본 수정일·상세 수신 시각 추가
e2e(test/festival.e2e-spec.ts)는 2030년 날짜·동해 좌표 픽스처라 실행 날짜와 무관하다.
2026-11-03 실측: 앞으로 90일 관광공사 행사는 전국 106건, 대구 1건, 경북 7건.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
검색·지도·상세 화면이 장소 DB 를 쓰도록 조회 API 를 연다(비로그인 허용).
- GET /api/places: 이름 검색, 좌표+반경 거리순, 분류·반려동물·실내 필터,
키셋 커서 페이지네이션(정렬값+id), 전체 개수, 항목별 "지금 영업 중"(KST)
- GET /api/places/{id}: 영업시간 원문·해석 상태·출처, 반려동물 크기·조건,
이용 안내(요금·주차 등), 추천 이유(추천+확인한 곳만), 출처 표기.
병합된 id 로 들어오면 병합 대상 장소를, 숨김·폐업 추정이면 RES_001 을 준다
- 같은 가게 병합: 관광공사 쪽이 카페·식당·주점이고 100m 안이며 상호가 같거나
포함(짧은 쪽 2글자 이상)이면 상가정보 쪽을 merged_into_id 로 관광공사 쪽에 합친다.
tour-kor·store 가 끝날 때 자동으로, place:sync -- dedupe 로 따로도 돈다
- 사후면세점 브랜드 매장(SH040300)은 목록·필터에서 빼고 이름 검색에만 낸다
(반려동물 필터 결과가 더현대 입점 매장으로 덮이던 문제)
- 공공 반려동물 조건 원문의 글머리표·줄바꿈 정리
e2e(test/place.e2e-spec.ts)는 시작할 때 마이그레이션을 적용하고 동해 좌표에
테스트 장소를 만들어 노출 규칙·필터·커서·상세·오류 코드를 검증한다.
2026-11-02 대구 실측: 같은 가게 21곳 병합, 도심 반려동물 필터 7곳.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>