From 580b403b400dc4f3fc25b48030d3426d123f629c Mon Sep 17 00:00:00 2001 From: root Date: Thu, 22 Oct 2026 09:55:23 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20PHP=EB=B3=B4=EC=95=88=ED=8C=A8=EC=B9=98?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EB=B2=84=EC=A0=84=20=EC=97=85=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EB=93=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FEEDBACK_CUSTOM_QNA_API_TASKS.md | 358 +++++++++++++++++++++++++++++++ PHP_SECURITY_PATCH_TASKS.md | 27 +++ cloudflare-maintenance/worker.js | 48 +++++ docker/php/Dockerfile | 4 +- docker/php/local.ini | 7 +- egbim/sw_upload/upload_popup.php | 18 +- 6 files changed, 452 insertions(+), 10 deletions(-) create mode 100644 FEEDBACK_CUSTOM_QNA_API_TASKS.md create mode 100644 PHP_SECURITY_PATCH_TASKS.md create mode 100644 cloudflare-maintenance/worker.js diff --git a/FEEDBACK_CUSTOM_QNA_API_TASKS.md b/FEEDBACK_CUSTOM_QNA_API_TASKS.md new file mode 100644 index 0000000..c3edc99 --- /dev/null +++ b/FEEDBACK_CUSTOM_QNA_API_TASKS.md @@ -0,0 +1,358 @@ +# feedback.mac.kr 커스텀 Q&A API 연동 작업 목록 + +## 목표 + +- 현재 사이트의 커스텀 Q&A 데이터를 `feedback.mac.kr` 관리 서버로 전송한다. +- 기존 `egbim/` 및 `eng/` Q&A 기능은 계속 사용할 수 있어야 한다. +- 기존 데이터는 1회 백필하고, 이후 신규 등록·수정·댓글·상태 변경·삭제를 증분 동기화한다. +- 첨부파일과 댓글 이미지는 Cloudflare R2에 저장하고, 관리 서버에는 R2 객체 정보만 전달한다. +- 관리 서버에서 Q&A 본문, 답변/댓글, 첨부파일, 작성자 정보, 상태를 일관되게 조회할 수 있도록 한다. + +## 범위 + +### 포함 + +- 커스텀 Q&A 게시글: `qa_posts` +- 게시글 첨부파일: `qa_attachments` +- 댓글/답변: `qa_comments` +- 댓글 이미지와 썸네일: `qa_comment_images` +- 댓글 이미지 원본과 썸네일: R2 저장 대상 +- 에디터 본문에 삽입된 이미지: R2 저장 대상 +- 국문 경로: `egbim/bbs/descope_qa_*.php` +- 영문 경로: `eng/bbs/descope_qa_*.php` + +### 제외 + +- 그누보드 기본 1:1 문의: `g5_qa_content`, `g5_qa_config` +- 쇼핑몰 상품 Q&A: `g5_shop_item_qa` +- 일반 게시판의 Q&A성 글 +- FAQ 및 기타 관리자 데이터 + +## 현재 코드 기준 데이터 흐름 + +| 기능 | 현재 엔드포인트 | 저장 데이터 | +| --- | --- | --- | +| 목록 조회 | `bbs/descope_qa_list.php` | `qa_posts`, 댓글 수, 첨부 수 | +| 상세 조회 | `bbs/descope_qa_detail.php` | `qa_posts`, `qa_comments`, `qa_attachments`, `qa_comment_images` | +| 글 등록/수정 | `bbs/descope_qa_write.php` | `qa_posts`, `qa_attachments` | +| 댓글 등록 | `bbs/descope_qa_comment.php` | `qa_comments`, `qa_comment_images` | +| 댓글 수정 | `bbs/descope_qa_comment_update.php` | `qa_comments` | +| 댓글 삭제 | `bbs/descope_qa_comment_delete.php` | `qa_comments` | +| 글 상태 변경 | `bbs/descope_qa_status.php` | `qa_posts.status` | +| 글 삭제 | `bbs/descope_qa_delete.php` | 게시글·댓글·첨부 삭제 | +| 본문 이미지 업로드 | `bbs/descope_qa_img_upload.php` | 파일 시스템, 본문 URL | + +## 1. 연동 정책 확정 + +- [ ] `feedback.mac.kr`가 데이터를 받는 방식 확정: Push API 또는 관리 서버의 Pull API +- [ ] 운영 API Base URL 및 환경별 URL 확정 +- [ ] 인증 방식 확정: API Key, HMAC 서명, OAuth2 등 +- [ ] API 버전과 리소스 경로 확정 +- [ ] 전송 대상 사이트 구분값 확정: `ko`, `en` 또는 `egbim`, `eng` +- [ ] 관리 서버가 원본 데이터를 저장하는지, 단순 조회 프록시인지 확정 +- [ ] 삭제 데이터를 실제 삭제할지 `deleted` 상태로 보존할지 확정 +- [ ] 비밀글의 본문·R2 첨부파일을 관리자 서버에서 어떻게 표시할지 확정 +- [ ] 개인정보 보관 기간 및 마스킹 정책 확정 + +### 권장 동기화 방식 + +- 최초 1회: 커스텀 Q&A 전체 백필 +- 이후: 변경 이벤트를 동기화 큐에 기록한 뒤 API로 재시도 전송 +- API 전송 성공 여부는 원본 트랜잭션과 분리해 관리 +- 동일 데이터 재전송이 가능하도록 `source`, `source_id`, `event_id` 기반 멱등성 보장 + +## 2. 원본 데이터 구조 확인 + +저장소에는 커스텀 테이블의 정식 DDL이 없으므로 실제 운영 DB에서 컬럼, 타입, 인덱스, 기본값을 확인해야 한다. + +- [ ] `SHOW CREATE TABLE qa_posts` 확보 +- [ ] `SHOW CREATE TABLE qa_comments` 확보 +- [ ] `SHOW CREATE TABLE qa_attachments` 확보 +- [ ] `SHOW CREATE TABLE qa_comment_images` 확보 +- [ ] 국문/영문 사이트가 동일 DB와 동일 테이블을 사용하는지 확인 +- [ ] 각 테이블의 건수와 최대 ID 확인 +- [ ] 고아 레코드 확인: 게시글 없는 댓글/첨부, 댓글 없는 이미지 +- [ ] 실제 파일이 없는 첨부 레코드 확인 +- [ ] DB의 시간대와 API 전송 시간 포맷을 `Asia/Seoul` 기준으로 통일 + +## 3. API 데이터 계약 설계 + +### 게시글 기본 필드 + +- [ ] `source`: `egbim` 또는 `eng` +- [ ] `source_id`: 원본 `qa_posts.post_id` +- [ ] `category`: `general`, `improvement`, `error`, `notice`, `admin` +- [ ] `title` +- [ ] `content` +- [ ] `is_secret` +- [ ] `status`: `new`, `review`, `deep`, `patch`, `done` +- [ ] `is_read_admin` +- [ ] `created_at`, `updated_at` + +### 작성자 필드 + +- [ ] `user_id` +- [ ] `login_id` +- [ ] `user_name` +- [ ] `phone` 전송 여부 및 마스킹 방식 +- [ ] `is_internal` +- [ ] `company` +- [ ] `family_company` +- [ ] `department` +- [ ] 관리자 표시명 변환 전 원본과 변환 후 표시명 중 무엇을 저장할지 결정 + +### 댓글/답변 필드 + +- [ ] `comment_id` +- [ ] 원본 게시글 ID +- [ ] `commenter` +- [ ] `user_name` +- [ ] `content` +- [ ] `created_at`, `updated_at` +- [ ] 댓글 수정·삭제 여부와 삭제 시각을 보낼 수 있도록 컬럼 또는 이벤트 구조 보완 + +### 첨부파일 필드 + +- [ ] `id` 또는 `image_id` +- [ ] 원본 게시글/댓글 ID +- [ ] `ori_name` 또는 `file_name` +- [ ] 저장 경로 +- [ ] 공개 URL 또는 관리 서버용 다운로드 URL +- [ ] 파일 크기 +- [ ] MIME 타입 및 확장자 +- [ ] 썸네일 URL +- [ ] `uploaded_at` +- [ ] 파일 삭제 여부와 삭제 시각 +- [x] 파일 저장소는 Cloudflare R2로 사용 +- [ ] API에는 파일 자체를 보내지 않고 R2 객체 키·메타데이터를 보낼지 확정 +- [ ] R2 공개 URL, 커스텀 도메인, presigned URL 중 접근 방식 확정 +- [ ] 관리 서버가 R2에서 파일을 가져갈 수 있도록 할지, 원본 서버가 임시 URL을 발급할지 결정 + +## 4. API 엔드포인트 구현 + +### 공통 API 클라이언트 + +- [ ] `feedback.mac.kr` 전용 API 클라이언트 작성 +- [ ] Base URL, 인증키, timeout을 환경변수로 분리 +- [ ] TLS 인증서 검증 활성화 +- [ ] 요청 ID와 원본 ID를 모든 요청 헤더/본문에 포함 +- [ ] 연결 실패, 4xx, 5xx, timeout을 구분해 기록 +- [ ] 재시도 가능한 오류와 즉시 실패해야 하는 오류 구분 +- [ ] 응답 JSON 스키마 검증 +- [ ] API 인증키와 개인정보가 로그에 남지 않도록 마스킹 + +### 권장 리소스 API + +- [ ] `POST /api/v1/qna/sync` — 게시글 전체 snapshot upsert +- [ ] `POST /api/v1/qna/events` — 등록·수정·댓글·상태·삭제 이벤트 전송 +- [ ] `POST /api/v1/qna/assets` — R2 객체 키와 첨부파일 메타데이터 등록 +- [ ] `GET /api/v1/qna/sync/status` — 백필/증분 동기화 상태 확인 +- [ ] 관리 서버의 실제 계약에 맞춰 경로와 payload 확정 + +## 5. 동기화 기록 및 재처리 구조 + +원본 저장 성공 후 API가 실패해도 사용자 작업이 실패하지 않도록 별도 동기화 기록이 필요하다. + +- [ ] `qna_sync_events` 테이블 설계 +- [ ] 이벤트 종류 정의: `post.created`, `post.updated`, `post.deleted`, `comment.created`, `comment.updated`, `comment.deleted`, `status.changed`, `attachment.created`, `attachment.deleted` +- [ ] 원본 테이블명과 원본 ID 저장 +- [ ] payload 또는 payload 생성에 필요한 snapshot 저장 +- [ ] `event_id` unique 제약으로 중복 전송 방지 +- [ ] 상태 정의: `pending`, `sending`, `sent`, `failed`, `dead_letter` +- [ ] 시도 횟수, 마지막 오류, 다음 재시도 시각 저장 +- [ ] 재처리용 CLI 또는 관리자 실행 도구 작성 +- [ ] 일정 시간 `sending` 상태로 남은 이벤트 복구 처리 +- [ ] 삭제 이벤트가 누락되지 않도록 soft-delete 또는 삭제 이력 보존 + +## 6. 기존 기능에 이벤트 연결 + +국문과 영문 코드에 같은 정책을 적용해야 한다. + +- [ ] `egbim/bbs/descope_qa_write.php` 신규 등록 후 이벤트 생성 +- [ ] `egbim/bbs/descope_qa_write.php` 수정 후 이벤트 생성 +- [ ] `eng/bbs/descope_qa_write.php` 신규 등록 후 이벤트 생성 +- [ ] `eng/bbs/descope_qa_write.php` 수정 후 이벤트 생성 +- [ ] 댓글 등록 시 게시글과 댓글 이미지 이벤트 생성 +- [ ] 댓글 수정 시 수정 이벤트 생성 +- [ ] 댓글 삭제 시 삭제 이벤트 생성 +- [ ] 게시글 상태 변경 시 상태 이벤트 생성 +- [ ] 게시글 삭제 시 게시글·댓글·첨부 삭제 이벤트 순서 정의 +- [ ] 에디터 이미지 업로드 시 이미지 자산 동기화 정책 적용 +- [ ] 기존 화면의 저장/리다이렉트/JSON 응답 형식을 깨뜨리지 않는지 확인 + +### 트랜잭션 처리 원칙 + +- [ ] 게시글/댓글/첨부 DB 저장과 동기화 이벤트 기록은 동일 DB 트랜잭션으로 처리 +- [ ] R2 업로드 성공 후 원본 DB에 객체 키를 저장 +- [ ] 외부 API 호출은 원본 DB 트랜잭션 안에서 직접 실행하지 않음 +- [ ] 이벤트 기록 성공 후 사용자에게 정상 응답 +- [ ] 외부 API 실패는 백그라운드 재처리 대상으로 남김 +- [ ] DB 저장 실패 시 이미 업로드된 R2 객체를 삭제하거나 orphan 정리 큐에 등록 + +## 7. 기존 데이터 백필 + +- [ ] 백필 기준 시각과 대상 ID 범위 확정 +- [ ] `qa_posts` 전체를 페이지 단위로 추출 +- [ ] 게시글별 `qa_comments` 추출 +- [ ] 게시글별 `qa_attachments` 추출 +- [ ] 댓글별 `qa_comment_images` 추출 +- [ ] 본문 HTML에 포함된 이미지 URL 추출 +- [ ] 파일 존재 여부와 파일 크기 검증 +- [ ] 파일이 없는 레코드는 오류 목록으로 분리 +- [ ] 원본 ID 보존형 upsert 적용 +- [ ] 기존 로컬 파일을 R2로 대량 이전 +- [ ] 대량 R2 업로드 속도 제한 및 실패 재시도 적용 +- [ ] 백필 중 신규 변경분을 놓치지 않도록 기준 시각 이후 이벤트 재전송 +- [ ] 백필 종료 후 원본 건수와 관리 서버 건수 대조 + +## 8. R2 첨부파일·이미지 처리 + +게시글 첨부파일, 댓글 이미지 원본·썸네일, 에디터 이미지를 모두 R2에 저장한다. `feedback.mac.kr` API에는 파일 바이너리 대신 R2 객체 식별자와 메타데이터를 전달하는 방향을 우선한다. + +### R2 저장 구조 + +- [ ] R2 버킷명과 환경별 버킷 분리 여부 확정 +- [ ] R2 객체 prefix 확정: 예) `qna/{source}/{post_id}/attachments/` +- [ ] 댓글 이미지 prefix 확정: 예) `qna/{source}/{post_id}/comments/{comment_id}/images/` +- [ ] 댓글 썸네일 prefix 확정: 예) `qna/{source}/{post_id}/comments/{comment_id}/thumbs/` +- [ ] 에디터 이미지 prefix 확정: 예) `qna/{source}/{post_id}/content/` +- [ ] 동일 파일 재업로드 방지를 위한 object key 또는 content hash 정책 확정 +- [ ] R2 객체에 원본 파일명, MIME, 크기, source ID를 metadata로 저장 +- [ ] `egbim`과 `eng` 파일의 prefix가 충돌하지 않는지 확인 + +### 대상별 이전·저장 + +- [ ] 게시글 첨부 원본 이전: `/egbim/uploads/qa/` +- [ ] 영문 게시글 첨부 원본 이전: `/eng/uploads/qa/` +- [ ] 댓글 이미지 원본 이전: `/egbim/uploads/comment/`, `/eng/uploads/comment/` +- [ ] 댓글 이미지 썸네일 이전: 각 `comment/thumb/` 경로 +- [ ] 에디터 이미지 이전: `/egbim/uploads/img/`, `/eng/uploads/img/` +- [ ] 기존 DB의 `save_path`, `file_path`, `thumb_path`를 R2 object key와 연결 +- [ ] 이전 완료 후에도 기존 URL을 일정 기간 유지하거나 redirect 처리 +- [ ] 실제 파일이 없는 DB 레코드는 오류 목록으로 분리 +- [ ] R2에만 존재하거나 DB에만 존재하는 orphan 파일 정리 + +### 업로드 흐름 + +- [ ] 신규 게시글 첨부는 R2 업로드 성공 후 `qa_attachments` 저장 +- [ ] 댓글 이미지 원본과 썸네일 생성 후 R2 업로드 +- [ ] `qa_comment_images`에는 R2 object key와 접근 URL 정보를 저장 +- [ ] 에디터 이미지도 R2 업로드 후 본문에 최종 URL 저장 +- [ ] R2 업로드 실패 시 Q&A 저장을 실패시키거나 임시 업로드로 분리할지 결정 +- [ ] DB 저장 실패 시 R2 orphan 정리 작업 등록 +- [ ] 파일 삭제 시 DB 삭제와 R2 객체 삭제 순서 및 재처리 정책 정의 + +### 접근 방식 + +- [ ] 공개 이미지와 비공개 첨부파일의 접근 정책 분리 +- [ ] 권장안: 비밀글 첨부는 R2 비공개 저장 + 관리 서버 요청 시 짧은 presigned URL 발급 +- [ ] 공개 가능한 공지 이미지 등은 R2 custom domain/CDN URL 사용 여부 결정 +- [ ] API payload에는 `bucket`, `object_key`, `mime_type`, `file_size`, `original_name`, `url` 또는 `download_token` 전달 +- [ ] presigned URL 만료시간과 재발급 API 정의 +- [ ] 관리 서버가 원본 사이트 세션 없이도 권한 검사를 통과하는 구조인지 확인 +- [ ] R2 CORS 정책에 실제 사용 Origin만 등록 +- [ ] R2 lifecycle 정책: orphan 파일, 임시 업로드, 삭제 보류 파일 정리 + +### 코드 정리 + +- [ ] R2 SDK 또는 S3 호환 클라이언트 공통 모듈 작성 +- [ ] R2 endpoint, access key, secret key, bucket을 환경변수로 분리 +- [ ] 게시글 첨부·댓글 이미지·에디터 이미지가 같은 업로드 모듈을 사용하도록 통합 +- [ ] 상대경로와 로컬 절대경로를 R2 object key로 변환하는 마이그레이션 유틸리티 작성 +- [ ] 파일명 인코딩, 한글 파일명, 특수문자 처리 확인 +- [ ] 허용 확장자와 MIME 타입을 원본 서버와 R2 업로드 단계에서 검증 + +## 9. 보안 및 개인정보 + +- [ ] API 인증정보를 코드에 하드코딩하지 않음 +- [ ] 운영/개발/로컬 API 키 분리 +- [ ] API 수신 서버 allowlist 또는 IP 제한 검토 +- [ ] HMAC 사용 시 timestamp와 nonce 검증 +- [ ] 재전송 공격 방지 +- [ ] `phone`, `login_id`, 이메일 등 개인정보의 전송 범위 확정 +- [ ] IP 주소는 기본적으로 전송하지 않거나 별도 마스킹 +- [ ] 비밀글 본문과 첨부파일이 공개 URL로 노출되지 않도록 확인 +- [ ] R2 access key/secret key를 코드와 API payload에 노출하지 않음 +- [ ] presigned URL이 로그·DB·브라우저에 필요 이상으로 남지 않도록 만료시간 설정 +- [ ] HTML 본문을 관리 서버에서 출력할 때 XSS 정화 정책 통일 +- [ ] API 요청/응답 로그에서 본문·첨부 내용·개인정보를 최소화 +- [ ] API 키 교체 절차와 유출 시 폐기 절차 문서화 + +## 10. 관측성 및 운영 + +- [ ] 동기화 성공/실패 건수 모니터링 +- [ ] 대기 이벤트 수와 가장 오래된 대기 시간을 모니터링 +- [ ] 연속 실패 시 알림 기준 설정 +- [ ] `source_id`, `event_id`, `request_id`로 양쪽 로그 추적 +- [ ] 백필 진행률과 실패 목록 조회 기능 제공 +- [ ] API 응답 지연 및 오류율 기록 +- [ ] 개인정보가 포함된 원문 로그 보관 금지 +- [ ] 재처리 작업의 중복 실행 방지 + +## 11. 테스트 시나리오 + +### 기능 + +- [ ] 신규 게시글 등록 → 관리 서버 생성 확인 +- [ ] 게시글 수정 → 관리 서버 동일 레코드 갱신 확인 +- [ ] 게시글 상태 변경 → 관리 서버 상태 갱신 확인 +- [ ] 댓글 등록 → 관리 서버 댓글 생성 확인 +- [ ] 댓글 수정 → 관리 서버 댓글 갱신 확인 +- [ ] 댓글 삭제 → 관리 서버 댓글 삭제/숨김 확인 +- [ ] 게시글 첨부 등록 → 파일과 메타데이터 확인 +- [ ] 댓글 이미지 등록 → 원본과 썸네일 확인 +- [ ] 에디터 이미지 등록 → 본문 링크와 파일 확인 +- [ ] 게시글 삭제 → 관리 서버 삭제 정책 확인 +- [ ] 비밀글의 본문·첨부 권한 확인 + +### 장애·재처리 + +- [ ] 관리 서버 timeout 후 원본 저장 성공 확인 +- [ ] 500 응답 후 자동 재시도 확인 +- [ ] 400 응답이 무한 재시도되지 않는지 확인 +- [ ] 동일 이벤트를 여러 번 보내도 중복 레코드가 생기지 않는지 확인 +- [ ] API 키 오류 시 사용자 기능과 동기화 실패가 분리되는지 확인 +- [ ] 이벤트 큐가 쌓인 뒤 정상 복구되는지 확인 +- [ ] 삭제 직후 재전송 시 최종 상태가 일관적인지 확인 + +### 국문/영문 + +- [ ] `egbim` 신규/수정/댓글/삭제 흐름 검증 +- [ ] `eng` 신규/수정/댓글/삭제 흐름 검증 +- [ ] 관리 서버에서 사이트 구분이 정확한지 확인 +- [ ] 같은 DB를 공유하는 경우 중복 전송이 발생하지 않는지 확인 + +## 12. 완료 기준 + +- [ ] 커스텀 Q&A 전체 데이터의 게시글·댓글·첨부·이미지가 관리 서버에서 조회됨 +- [ ] 최초 백필 누락 건수가 0건이거나 승인된 예외 목록으로 관리됨 +- [ ] 신규 등록·수정·댓글·상태 변경·삭제가 정해진 지연시간 내 반영됨 +- [ ] API 장애가 원본 Q&A 사용을 막지 않음 +- [ ] 실패 이벤트를 재처리할 수 있음 +- [ ] 중복 전송에도 관리 서버 데이터가 중복되지 않음 +- [ ] 비밀글과 개인정보가 정책에 맞게 보호됨 +- [ ] 국문/영문 양쪽의 테스트가 완료됨 +- [ ] 운영 배포 및 장애 대응 절차가 문서화됨 + +## 우선순위 제안 + +1. 관리 서버 API 계약과 인증 방식 확정 +2. 네 개 커스텀 테이블의 실제 DDL/건수/파일 현황 확인 +3. 동기화 이벤트 테이블과 공통 API 클라이언트 구현 +4. 게시글·댓글 저장 흐름에 이벤트 기록 연결 +5. R2 업로드 모듈 및 첨부파일/댓글 이미지 이전 구현 +6. 기존 데이터 백필 +7. 장애 재처리·모니터링·운영 배포 + +## 결정이 필요한 항목 + +- [ ] `feedback.mac.kr` API 명세 또는 수신 엔드포인트 +- [ ] Push와 Pull 중 최종 방식 +- [ ] API 인증 방식 및 키 발급 방법 +- [ ] 게시글·댓글·첨부 삭제 보존 정책 +- [ ] 개인정보 전송 필드와 마스킹 기준 +- [ ] R2 버킷, prefix, public/custom domain 정책 +- [ ] R2 공개 URL과 presigned URL 사용 범위 +- [ ] R2 첨부파일 전송·삭제·보존 정책 +- [ ] 국문/영문 데이터가 관리 서버에서 분리되어야 하는지 여부 +- [ ] API 장애 시 허용 가능한 동기화 지연시간 diff --git a/PHP_SECURITY_PATCH_TASKS.md b/PHP_SECURITY_PATCH_TASKS.md new file mode 100644 index 0000000..be22c0d --- /dev/null +++ b/PHP_SECURITY_PATCH_TASKS.md @@ -0,0 +1,27 @@ +# PHP 보안 패치 작업 목록 + +## 목표 + +실행 중인 `php:8.1-apache` 기반 PHP 8.1.34를 CVE-2026-109618 및 CVE-2026-109619 수정 버전으로 교체하고, `app` 컨테이너에 반영한다. + +## 작업 + +- [x] `docker/php/Dockerfile` 베이스 이미지를 최신 안정 수정 Apache 이미지 `php:8.5.12-apache`로 변경한다. +- [x] PHP 오류를 방문자 응답에 노출하지 않고 로그에 기록해 PHP 8.5 deprecation 경고가 페이지/엑셀 응답에 섞이지 않게 한다. +- [x] PHP 8.5 호환성 점검: `sw_upload`의 `mysqli::ping()` 사용을 제거하고 PHPExcel 기반 엑셀 화면의 경고/응답 영향을 확인한다. +- [ ] Cloudflare Worker 점검 라우트를 활성화해 방문자에게 정적 점검 안내를 제공한다. +- [x] legacy builder로 PHP 8.5.12 `app` 이미지를 빌드하고 Compose 기본 태그 `egbim-app:latest`에도 연결한다. (BuildKit 플러그인이 없어 legacy builder 사용) +- [ ] `docker compose up -d --no-deps --force-recreate app`으로 `app`만 재생성한다. +- [ ] `docker compose exec app php -v`로 PHP 8.5.12 이상이 실제 컨테이너에서 실행 중인지 확인한다. +- [ ] 서비스가 정상 응답하는지 확인한 뒤 Cloudflare 점검 라우트를 비활성화한다. + +## 범위 제외 + +- Q&A 기능 및 관련 마이그레이션/API 작업 +- 애플리케이션 기능 변경 +- MariaDB 이미지, 데이터베이스 스키마 및 데이터 변경 + +## 호환성 점검 결과 + +- `egbim/adm/shop_admin/` 및 `egbim/adm/sms_admin/`의 PHPExcel 엑셀 입출력 경로는 PHP 8.5에서 deprecated 경고를 발생시킨다. 기존 `display_errors=On` 설정에서는 엑셀 다운로드 응답이 오염될 수 있어 화면 출력은 끄고 로그 기록을 켠다. +- PHPExcel 임시 XLS 생성은 PHP 8.5.12에서 성공했다. 다만 경고를 내지 않도록 라이브러리 전체를 현대화한 것은 아니므로 해당 관리자 엑셀 화면의 운영 확인이 필요하다. diff --git a/cloudflare-maintenance/worker.js b/cloudflare-maintenance/worker.js new file mode 100644 index 0000000..2bd95bb --- /dev/null +++ b/cloudflare-maintenance/worker.js @@ -0,0 +1,48 @@ +const PAGE = ` + + + + + + 긴급 보안 점검 중 + + + +
+ +

SERVICE NOTICE

+

긴급 보안 점검 중입니다

+

안전한 서비스 제공을 위해 긴급 보안 패치를 적용하고 있습니다.
점검이 끝나는 대로 서비스를 다시 열겠습니다.

+ +

We are applying an urgent security update to protect the service.
We will restore access as soon as maintenance is complete.

+ +
+ +`; + +export default { + async fetch() { + return new Response(PAGE, { + status: 503, + headers: { + "content-type": "text/html; charset=utf-8", + "cache-control": "no-store, no-cache, must-revalidate", + "x-content-type-options": "nosniff", + "referrer-policy": "no-referrer", + }, + }); + }, +}; diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 4cabafa..ec0756b 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.1-apache +FROM php:8.5.12-apache RUN apt-get update \ && apt-get install -y --no-install-recommends \ @@ -20,4 +20,4 @@ RUN apt-get update \ COPY docker/apache/vhost.conf /etc/apache2/sites-available/000-default.conf COPY docker/php/local.ini /usr/local/etc/php/conf.d/local.ini -WORKDIR /var/www/html \ No newline at end of file +WORKDIR /var/www/html diff --git a/docker/php/local.ini b/docker/php/local.ini index f31d189..c927b43 100644 --- a/docker/php/local.ini +++ b/docker/php/local.ini @@ -1,9 +1,10 @@ -display_errors=On -display_startup_errors=On +display_errors=Off +display_startup_errors=Off +log_errors=On error_reporting=E_ALL memory_limit=1024M upload_max_filesize=512M post_max_size=512M max_execution_time=300 max_input_vars=5000 -date.timezone=Asia/Seoul \ No newline at end of file +date.timezone=Asia/Seoul diff --git a/egbim/sw_upload/upload_popup.php b/egbim/sw_upload/upload_popup.php index 185ce72..8070d61 100644 --- a/egbim/sw_upload/upload_popup.php +++ b/egbim/sw_upload/upload_popup.php @@ -125,8 +125,12 @@ try { } catch (Exception $e) { // ✅ 쿼리 중 하나라도 에러가 나면 여기로 빠져나와서 롤백 처리 - if (isset($conn) && $conn->ping()) { - $conn->rollback(); + if (isset($conn) && $conn instanceof mysqli) { + try { + $conn->rollback(); + } catch (Throwable) { + // 연결이 이미 끊겼거나 트랜잭션이 시작되지 않은 경우 원래 오류를 유지합니다. + } } // 이 메시지가 프론트엔드의 alert 창에 그대로 뜨게 됩니다! @@ -135,8 +139,12 @@ try { 'message' => 'DB 에러 상세 원인: ' . $e->getMessage() ]); } finally { - if (isset($conn) && $conn->ping()) { - $conn->close(); + if (isset($conn) && $conn instanceof mysqli) { + try { + $conn->close(); + } catch (Throwable) { + // 종료 과정의 오류가 JSON 응답을 덮어쓰지 않도록 합니다. + } } } -?> \ No newline at end of file +?>