From 989fa24bb73c916c133dba3a3f2cd82ead1d13f3 Mon Sep 17 00:00:00 2001 From: bgmbgm94 Date: Mon, 21 Sep 2026 08:51:37 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=EB=94=94=ED=8A=B8=EB=A6=AD=EC=8A=A4?= =?UTF-8?q?=20=ED=98=B8=EC=B6=9C=EC=9D=84=20=ED=91=9C=EC=A4=80=20=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 배포 환경에서 `/api/dtryx/seats` 와 `/api/dtryx/movies` 가 간헐적으로 실패하는 문제(#187)를 줄이려는 변경입니다. 극장 홈페이지에 적힌 `api.dtryx.com:30443` 을 그대로 쓰고 있었는데, 같은 호스트의 표준 포트에서도 동일한 응답을 받습니다. 세 엔드포인트 (timetable-list, play-date-list, movie-now) 모두 Recordset 이 일치하고 TLS 인증서·응답 헤더·DNS 도 같습니다. 이 저장소의 compatibility_date 기준에서는 Cloudflare Workers 가 커스텀 포트를 무시하지 않고 그대로 사용합니다. 영화관 서비스 중 비표준 포트를 쓰는 곳은 디트릭스뿐이었습니다. 포트가 다시 들어오지 않도록 상수와 클라이언트 요청 URL 양쪽에 확인을 추가했습니다. Co-Authored-By: Claude Opus 5 (1M context) --- src/services/dtryx/api.ts | 7 +++- tests/services/dtryx/api.test.ts | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/services/dtryx/api.test.ts diff --git a/src/services/dtryx/api.ts b/src/services/dtryx/api.ts index 4ce91196..e86de12d 100644 --- a/src/services/dtryx/api.ts +++ b/src/services/dtryx/api.ts @@ -6,7 +6,12 @@ */ export const DTRYX_API = { - BASE_URL: 'https://api.dtryx.com:30443', + /** + * 극장 홈페이지에는 비표준 포트(:30443)가 적혀 있지만 표준 포트에서도 + * 같은 응답을 받습니다. Cloudflare Workers 배포 환경에서 비표준 포트로 + * 나가는 호출이 간헐적으로 실패해 표준 포트를 사용합니다. + */ + BASE_URL: 'https://api.dtryx.com', THIRDPARTY_PATH: '/dtryx/cms/thirdparty/movie', CHANNEL_CODE: 'homepage', ENDPOINTS: { diff --git a/tests/services/dtryx/api.test.ts b/tests/services/dtryx/api.test.ts new file mode 100644 index 00000000..bad46e77 --- /dev/null +++ b/tests/services/dtryx/api.test.ts @@ -0,0 +1,65 @@ +/** + * 디트릭스 API 상수 테스트 + */ + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { DTRYX_API } from '../../../src/services/dtryx/api.js'; +import { + fetchDtryxNowShowing, + fetchDtryxPlayDates, + fetchDtryxTimetable, +} from '../../../src/services/dtryx/client.js'; + +describe('디트릭스 API 상수', () => { + it('표준 포트를 사용한다', () => { + const url = new URL(DTRYX_API.BASE_URL); + + expect(url.protocol).toBe('https:'); + expect(url.port).toBe(''); + }); + + it('비표준 포트를 포함하지 않는다', () => { + // Cloudflare Workers 배포 환경에서 비표준 포트로 나가는 호출이 + // 간헐적으로 실패해 표준 포트를 유지해야 합니다. + expect(DTRYX_API.BASE_URL).not.toMatch(/:\d+$/); + }); +}); + +describe('디트릭스 요청 URL', () => { + const mockFetch = vi.fn(); + + beforeEach(() => { + mockFetch.mockReset(); + mockFetch.mockResolvedValue( + new Response(JSON.stringify({ RetCode: 'success', Recordset: [] }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }), + ); + vi.stubGlobal('fetch', mockFetch); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + const request = { brandCode: 'indieart', cinemaCode: '000067' }; + + it('상영시간표 요청이 표준 포트로 나간다', async () => { + await fetchDtryxTimetable({ ...request, playDate: '20260918' }); + + expect(new URL(String(mockFetch.mock.calls[0]?.[0])).port).toBe(''); + }); + + it('상영 날짜 요청이 표준 포트로 나간다', async () => { + await fetchDtryxPlayDates(request); + + expect(new URL(String(mockFetch.mock.calls[0]?.[0])).port).toBe(''); + }); + + it('상영작 요청이 표준 포트로 나간다', async () => { + await fetchDtryxNowShowing(request); + + expect(new URL(String(mockFetch.mock.calls[0]?.[0])).port).toBe(''); + }); +}); From d3125411484563993f81e27f1e8d48f53fe411be Mon Sep 17 00:00:00 2001 From: hmmhmmhm Date: Mon, 28 Sep 2026 12:45:48 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=EB=94=94=ED=8A=B8=EB=A6=AD=EC=8A=A4?= =?UTF-8?q?=20=ED=91=9C=EC=A4=80=20=ED=8F=AC=ED=8A=B8=20=EC=A0=84=ED=99=98?= =?UTF-8?q?=20=EA=B7=BC=EA=B1=B0=EC=99=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B3=B4=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/2026-09-28-dtryx-comparison.md | 14 ++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- src/services/dtryx/api.ts | 5 ++--- tests/services/dtryx/api.test.ts | 4 ++-- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/superpowers/plans/2026-09-28-dtryx-comparison.md b/docs/superpowers/plans/2026-09-28-dtryx-comparison.md index 80eeda46..2d1f7108 100644 --- a/docs/superpowers/plans/2026-09-28-dtryx-comparison.md +++ b/docs/superpowers/plans/2026-09-28-dtryx-comparison.md @@ -12,3 +12,17 @@ 443 1/4, 30443 0/4다. 한 번의 성공은 지속적 복구나 포트 원인을 입증하지 않는다. 실패는 모두 응답 헤더 이전이었다. 9/19·9/21 양 포트 실패 기록과 일치하며 #188을 영구 복구로 반영하지 않는다. 모든 시험 뒤 임시 Worker를 삭제했다. 새 배포의 라우트 전파 전404가 발생한 최초 시도도 finally에서 삭제한 뒤, 인증 거절401로 준비를 확인하도록 보강해 위 실험을 수행했다. 별도 현 운영 좌석 조회3회는 503/15.65s, 503/15.65s, 200/0.82s였다. 오류를200으로 바꾸거나 15초 제한을 늘리는 변경은 하지 않았다. 다음 단계는 업스트림의 Cloudflare 출발 연결 로그와 동일 시각 TCP/TLS/헤더 지연을 대조하는 것이다. + + +## 후속 결정: 호환성 변경으로 #188 반영 + +사용자가 남은 PR도 최대한 반영하도록 요청해, 장애 해결 입증과 엔드포인트 호환성을 구분해 재검토했다. 동일한 Mac에서 극장000067(indieart)·000072(spacedog)의 시간표·상영 날짜·현재 상영작을 각각 두 포트로 호출했다. 총12회 모두200/RetCode success였으며 여섯 쌍의 Recordset이 완전히 일치했다(35~74ms). + +| 극장 | 시간표 | 상영 날짜 | 상영작 | +|---|---:|---:|---:| +| 000067 | 양쪽7건 | 양쪽12건 | 양쪽14건 | +| 000072 | 양쪽5건 | 양쪽67건 | 양쪽14건 | + +이에 표준 HTTPS 주소를 사용하는 호환성 변경으로 #188을 채택한다. 포트가 장애 원인이라는 주석은 제거하고, 테스트의 전역 fetch 복원도 보강한다. 세 엔드포인트 URL 검증과 기존 실패·시간초과 테스트, 전체 check/coverage/build를 실행한다. 배포 후 seats/movies를 반복 조회하여 결과를 별도로 기록한다. 요청 제한15초와 기존 실패/부분 성공 의미를 유지하며, 재시도·다른 포트 폴백·유료 호출은 추가하지 않는다. #187은 계속 열어둔다. + +이 결정은 위 원격 시험 결과를 번복하거나 장애 해결을 주장하는 것이 아니다. 앞선 보류 판단에서 검증 가능한 호환성 변경만 분리해 반영하는 것이다. diff --git a/package-lock.json b/package-lock.json index 2f422369..930cd0b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "daiso", - "version": "1.2.3", + "version": "1.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "daiso", - "version": "1.2.3", + "version": "1.2.4", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", diff --git a/package.json b/package.json index 7e6175a7..80b280f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "daiso", - "version": "1.2.3", + "version": "1.2.4", "description": "Korean retail and cinema MCP server, CLI, and Codex Skill for Daiso, convenience stores, marts, Olive Young, and theaters", "main": "dist/index.js", "bin": { diff --git a/src/services/dtryx/api.ts b/src/services/dtryx/api.ts index e86de12d..18272bad 100644 --- a/src/services/dtryx/api.ts +++ b/src/services/dtryx/api.ts @@ -7,9 +7,8 @@ export const DTRYX_API = { /** - * 극장 홈페이지에는 비표준 포트(:30443)가 적혀 있지만 표준 포트에서도 - * 같은 응답을 받습니다. Cloudflare Workers 배포 환경에서 비표준 포트로 - * 나가는 호출이 간헐적으로 실패해 표준 포트를 사용합니다. + * 두 포트의 공개 조회 응답이 동일함을 확인해 표준 HTTPS 포트를 사용합니다. + * Worker의 간헐 연결 실패는 양쪽 포트에서 관측되어 별도로 추적합니다(#187). */ BASE_URL: 'https://api.dtryx.com', THIRDPARTY_PATH: '/dtryx/cms/thirdparty/movie', diff --git a/tests/services/dtryx/api.test.ts b/tests/services/dtryx/api.test.ts index bad46e77..db6741e7 100644 --- a/tests/services/dtryx/api.test.ts +++ b/tests/services/dtryx/api.test.ts @@ -19,8 +19,7 @@ describe('디트릭스 API 상수', () => { }); it('비표준 포트를 포함하지 않는다', () => { - // Cloudflare Workers 배포 환경에서 비표준 포트로 나가는 호출이 - // 간헐적으로 실패해 표준 포트를 유지해야 합니다. + // 검증된 표준 HTTPS 주소가 비표준 포트로 되돌아가지 않도록 확인합니다. expect(DTRYX_API.BASE_URL).not.toMatch(/:\d+$/); }); }); @@ -40,6 +39,7 @@ describe('디트릭스 요청 URL', () => { }); afterEach(() => { + vi.unstubAllGlobals(); vi.restoreAllMocks(); });