운영 기능 및 GA4 대시보드 반영

This commit is contained in:
박성필
2026-08-18 10:53:58 +09:00
parent 612e87847c
commit b492abd3b4
24 changed files with 1521 additions and 77 deletions

View File

@@ -1,6 +1,6 @@
# GTSIT 사이트 설계안
최종 수정일: 2026-08-10
최종 수정일: 2026-08-18
## 1. 구축 목표
@@ -132,9 +132,23 @@ public/
- 개인정보 파기: 문의가 `closed`로 전환된 시각을 기록하고 1년이 지난 문의를 `bin/purge-inquiries.php`로 데이터베이스에서 완전 삭제
- 파기 실행 환경: 현재 뉴아우토반 공유 웹호스팅은 cron을 지원하지 않아 정리 명령을 수동 실행할 수 있도록 배포했으며, 외부 스케줄러 또는 지원 상품 전환 시 정기 실행 등록 필요
- 관리자: 서버 세션, 비밀번호 해시, 계정별 잠금, 세션 단위 시도 제한, 모든 하위 경로 인증 선행
- 방문 통계: 무료 GA4 표준 속성으로 운영 공개 페이지만 측정하고 관리자·테스트 경로 제외, Analytics Data API는 서버의 읽기 전용 서비스 계정과 15분 캐시를 통해 관리자 대시보드에 집계값만 제공
- 통계 개인정보 경계: 문의 이름·연락처·이메일·본문과 관리자 식별정보는 GA4 이벤트에 포함하지 않고, 통계 쿠키 동의 전에는 Analytics 저장을 비활성화
- GA4 식별자: 계정 `(주)지티에스정보통신`, 속성 ID `549573245`, 운영 웹 스트림 ID `15422491033`, 측정 ID `G-92XBR314YD`
- 통계 동의 흐름: 최초 선택 전 Google 태그 미로딩, 허용 시 운영 호스트에서만 측정, 거부·철회 시 Analytics 저장 거부와 회사 도메인 `_ga` 쿠키 삭제 시도, 광고 저장·Google 신호·광고 개인화는 항상 비활성화
- 관리자 통계: Google Cloud 프로젝트 `gtsit-ga4-dashboard-20260812`의 전용 서비스 계정에 GA4 속성 `뷰어` 권한만 부여하고, 서버에서 Analytics Data API를 조회해 15분간 비공개 파일 캐시
- GA4 키 보관: JSON 개인키는 Git과 웹 루트 밖의 앱 `.secrets/`에 권한 `600`으로 보관하며 브라우저 HTML·JavaScript·로그에 포함하지 않음
- 통계 장애 대응: API 오류 시 마지막 정상 캐시를 표시하고, 캐시도 없을 때는 관리자 화면에 일반 안내만 표시해 자격 증명·API 오류 세부 정보 비노출
- 전체 방문자 기준: GA4 측정 시작일 `2026-08-12`부터 오늘까지의 `totalUsers`를 표시하며 이전 방문 기록은 포함하지 않음
- 초기 관리자: 24시간 유효한 토큰을 세션으로 교환한 뒤 비밀번호를 직접 설정하며, 생성 즉시 토큰 파일 삭제
- 게시글: slug, 분류, 초안·공개 상태, 소프트 삭제 기반
- 게시글: 신규·기존 slug는 DB 게시글 번호 기반 `post-{id}` 자동 생성, 종전 slug는 정적 별칭을 통한 301 이동, 분류, 초안·공개 상태, 소프트 삭제 기반
- 대표 이미지: JPG·PNG·WebP, 8MB, 6000px 제한, MIME 재검증, 임의 파일명, 대체 텍스트 적용
- 게시글 미디어 실험: 테스트 경로에서 대표 이미지 최대 6장 슬라이드와 본문 이미지 최대 10장을 지원하고, 본문은 독립 줄의 `[[image:번호]]` 토큰 위치에 안전하게 렌더링
- 관리자 본문 편집: 범용 HTML 에디터 대신 textarea 문단과 이미지 카드만 제공하는 전용 블록 UI 사용, 위·아래 이동과 삭제 후 기존 텍스트·토큰 형식으로 직렬화
- 작성 화면 미리보기: 저장 전 폼 상태와 선택 파일의 브라우저 임시 URL을 모달에 안전한 DOM API로 렌더링하며, 미리보기 조작은 서버 요청이나 DB 저장을 발생시키지 않음
- 본문 보안 경계: 임의 HTML·`contenteditable`·`innerHTML`을 사용하지 않고 텍스트는 기존 출력 이스케이프, 이미지는 서버 검증 파일과 DB 연결 정보만 렌더링
- 운영 호환성: 공유 DB 스키마 변경 없이 `post_images.caption``@gtsit:gallery`, `@gtsit:inline:번호` 내부 표식과 정렬순서를 사용하며, 운영 코드가 항상 첫 대표 이미지를 선택하도록 유지
- 실험 공개 제한: 다중 대표 이미지 또는 본문 이미지가 포함된 게시글은 운영 반영 승인 전까지 초안으로만 저장
- 이미지 없는 게시글 카드: 글을 숨기지 않고 `GTS` 로고와 카테고리 색상을 사용한 16:9 브랜드 플레이스홀더 표시, 대표 이미지 등록 시 실제 이미지 우선
- 회사 정보: 관리자 화면에서 연락처·주소·사업자 정보를 DB 설정값으로 관리
- 오시는 길: NAVER Cloud Dynamic Map으로 주소 기반 지도와 회사 마커 표시, API 오류 시 네이버 지도 검색 링크 제공

View File

@@ -1,6 +1,6 @@
# 구현 TODO
최종 수정일: 2026-08-10
최종 수정일: 2026-08-18
완료한 작업은 체크하고, 설정값이나 URL이 바뀌면 관련 문서도 함께 갱신합니다.
@@ -106,6 +106,9 @@
- [x] [사용자] Maps Client ID 전달·등록, Client Secret은 전달하지 않음
- [x] [Codex] 오시는 길 지도·회사 마커·네이버 지도 열기 fallback 구현
- [x] [Codex] 지도 SDK 요소 높이 안정화 및 사용자 정의 전체화면 컨트롤 적용
- [x] [Codex] 지도 내부 컨트롤보다 전화상담 플로팅 버튼이 위에 표시되도록 스태킹 순서 보정
- [x] [Codex] 지도 드래그·키보드 이동 차단 및 마커 중심 확대·축소 고정
- [x] [Codex] 핀치·휠·더블클릭/탭 줌 차단 및 주소 중심 확대·축소 컨트롤만 허용
- [x] [사용자] 기존 사이트 백업의 실제 시공 이미지 원본 전달
- [ ] [사용자] 최신 시공 이미지 추가 전달
- [x] [Codex] 관리자 인증·세션·접근 통제 방식 확정
@@ -147,10 +150,36 @@
- [x] [Codex] 클라이언트 코드의 기존 관리자 인증값 제거
- [x] [Codex] 대시보드 구현
- [x] [Codex] 게시글 목록, 작성, 수정, 소프트 삭제 구현
- [x] [Codex] 신규·기존 게시글 URL을 `post-글번호` 형식으로 자동 생성하고 이전 주소 301 연결
- [x] [Codex] 관리자 글 목록에서 초안·공개 글의 인증된 실제 화면 미리보기 구현
- [x] [Codex] 이미지 업로드 검증 구현
- [x] [Codex] 테스트 경로에 대표 이미지 최대 6장 슬라이드와 본문 이미지 최대 10장 배치 기능 구현
- [x] [Codex] 다중·본문 이미지가 있는 글의 테스트 기간 공개 차단과 전용 초안 미리보기 구성
- [x] [Codex] 관리자 본문 입력을 텍스트 문단·이미지 전용 블록 편집 UI로 개선
- [x] [Codex] 블록 이동·삭제·대체 텍스트와 안전한 내부 토큰 직렬화 구현
- [x] [Codex] 글쓰기 화면에서 저장 전 제목·본문·대표 이미지·본문 이미지 즉시 미리보기 구현
- [x] [Codex] 테스트 base path가 등록 본문 이미지의 JavaScript 미리보기 경로에 누락되는 문제 수정
- [ ] [공동] 테스트 초안의 슬라이드·본문 이미지 UI 검토 및 수정사항 확정
- [ ] [Codex] 승인된 다중·본문 이미지 기능 운영 코드 반영
- [x] [Codex] 문의 목록, 상세, 처리 상태 및 공개 답변 구현
- [x] [Codex] 회사 및 사이트 설정 구현
- [x] [Codex] CSRF, 세션 만료, 로그인 시도 제한, 권한 검증 적용
- [x] [사용자] GA4 표준 속성과 `gtsit.co.kr` 웹 데이터 스트림 생성
- [x] [Codex] 운영 공개 페이지에 통계 동의 기반 Google 태그 적용, 관리자·테스트 경로 제외
- [x] [사용자] Analytics Data API 서비스 계정 생성과 GA4 뷰어 권한 부여
- [x] [Codex] GA4 Data API 연동과 15분 서버 캐시 구현
- [x] [Codex] 관리자 대시보드 방문자·페이지뷰·유입경로·인기 페이지 UI 구현
- [x] [Codex] GA4 전문 용어를 최근 접속자·30일 방문자·페이지 열람 수·사이트 방문 횟수로 쉽게 표시
- [x] [Codex] GA4 측정 시작일 이후 전체 방문자 지표 추가
- [x] [공동] 테스트 관리자 대시보드의 데스크톱 UI 확인
- [ ] [공동] 관리자 대시보드 모바일 UI 확인
- [ ] [Codex] 문의 제출 전환 이벤트와 전환 지표 추가
GA4 진행 메모: 2026-08-12 계정 `(주)지티에스정보통신`, 속성 ID `549573245`, 운영 웹 스트림 ID `15422491033`, 측정 ID `G-92XBR314YD`를 생성했습니다. 선택 동의 전에는 Google 스크립트를 로드하지 않고, 테스트·관리자 경로는 측정하지 않습니다. 운영 동의 후 GA4 실시간 활성 사용자 1명을 확인했습니다.
GA4 관리자 연동 메모: 2026-08-13 Google Cloud 프로젝트 `gtsit-ga4-dashboard-20260812`에서 Analytics Data API를 활성화하고 서비스 계정 `gtsit-ga4-reader@gtsit-ga4-dashboard-20260812.iam.gserviceaccount.com`에 GA4 속성 `뷰어` 권한만 부여했습니다. 서버 측 OAuth와 Data API 실제 조회에서 사용자·페이지뷰·세션 데이터 응답을 확인했고, 집계 결과는 웹 루트 밖에 15분간 권한 600으로 캐시합니다. 테스트 관리자 화면의 최종 시각 QA 후 운영에 반영합니다.
운영 반영 메모: 2026-08-13 현재 저장소의 공개·관리자 실행 코드, 번호형 게시글 URL, 문의·개인정보 화면, 관리자 글쓰기 개선, GA4 대시보드와 공개 에셋을 운영에 반영했습니다. 서버 기존 파일은 `/home/hosting_users/gts0201/.deploy-backup-20260813-current-work`에 앱·웹 아카이브로 보관했습니다. 운영 PHP 문법, GA4 Data API 실제 조회, 비밀키·캐시 권한 600, 주요 공개 경로와 에셋 200, 관리자 비로그인 302, 게시글 200과 이전 주소 301, 홈·문의·네이버 지도 렌더링을 확인했습니다.
## 9. 운영 설정 및 출시
@@ -162,6 +191,7 @@
- [x] [Codex] 업로드 디렉터리 PHP 실행 차단 설정 작성
- [ ] [사용자] 하드 용량 및 트래픽 알림 수신처 결정
- [ ] [공동] 용량 및 트래픽 알림 활성화
- [ ] [공동] GA4 통계 쿠키 동의 문구와 개인정보처리방침 국외 처리 항목 최종 검토
- [x] [공동] 검색 봇 차단 정책과 공개 시점 결정
- [x] [Codex] 공개 페이지 허용·관리자와 비공개 문의 조회 차단 `robots.txt` 배포
- [x] [Codex] 공개 페이지와 게시글 6건 `sitemap.xml` 배포
@@ -176,3 +206,11 @@
지도 연동 메모: 2026-08-05 Client ID `0xuekem2qn``ncpKeyId`로 등록했습니다. 고정된 회사 위치는 주소 변환 API를 매번 호출하지 않고 확인된 위·경도로 표시하며, `Dynamic Map`만으로 회사 마커를 렌더링합니다. 지도 SDK 로드·인증 실패 시에도 주소와 네이버 지도 검색 링크를 유지합니다.
운영 오픈 메모: 2026-08-10 기존 임시 페이지와 루트 설정을 `.deploy-backup-20260810-production-open`에 보존하고 `https://gtsit.co.kr`을 본 사이트로 전환했습니다. 운영 앱은 웹 루트 밖의 `gtsit-app-20260810`, 공개 파일은 `www`에 배치했습니다. 주요 공개 경로·게시글 6건·에셋·지도·관리자 로그인·HTTPS 리디렉션·업로드 PHP 실행 차단을 확인했습니다. 일반 GET은 정상이나 `HEAD` 요청을 라우터가 404로 처리하는 호환성 항목은 로직 변경 승인 후 보완합니다. 최초 커밋 `dbb35d3`은 생성했으며 원격 저장소 HTTPS 인증 정보가 없어 push는 사용자 인증 후 재시도합니다.
게시글 미디어 테스트 메모: 2026-08-10 운영과 테스트가 같은 DB를 사용하므로 스키마는 변경하지 않고 `post_images.caption` 내부 표식으로 대표·본문 이미지를 구분했습니다. `/site-preview-7c2a91f4/admin/posts/15/preview`에 테스트 전용 초안을 구성했으며, 대표 3장 자동 슬라이드와 본문 2장 위치 배치를 확인했습니다. 다중 또는 본문 이미지가 있는 글은 운영 반영 전까지 `draft` 저장만 허용하며 운영 앱 파일은 변경하지 않았습니다.
본문 블록 편집기 메모: 2026-08-10 범용 WYSIWYG와 임의 HTML 저장을 사용하지 않고 테스트 관리자에 텍스트 문단·이미지 블록 편집기를 적용했습니다. 관리 화면은 내부 `[[image:번호]]`를 숨기고 실제 이미지 카드로 보여주며, 저장 직전에만 기존 일반 텍스트 형식으로 변환합니다. 이미지 위치 토큰은 서버에서 숫자 범위·중복·본문 1회 배치·실제 이미지 연결 여부를 재검증합니다.
작성 화면 미리보기 메모: 2026-08-10 테스트 관리자 편집 화면에 저장 전 내용을 즉시 확인하는 모달을 추가했습니다. 현재 입력 중인 제목·분류·작성자·문단과 기존/신규 대표 이미지·본문 이미지를 브라우저 DOM으로만 구성하며, 미리보기를 열거나 대표 이미지 썸네일을 전환해도 데이터베이스에는 저장하지 않습니다.
게시글 URL 자동 생성 메모: 2026-08-10 새 글 저장 시 DB가 발급한 게시글 번호를 기준으로 `post-16`, `post-17`처럼 slug를 자동 생성하도록 변경했습니다. 기존 1~15번 글도 `post-1`~`post-15`로 통일하고 종전 주소는 새 주소로 301 이동하도록 보존하며, 작성·수정 화면에서는 URL을 직접 입력하지 않습니다.

View File

@@ -47,6 +47,8 @@
운영 오픈 기록: 2026-08-10 `https://gtsit.co.kr` 운영 루트를 본 사이트로 전환했습니다. 홈·블로그·문의·개인정보와 공개 게시글 6건은 GET 200, 없는 경로는 404, 비로그인 관리자는 `/admin/login`으로 이동했습니다. 공개 canonical에서 테스트 경로가 제거되고 공개 화면에는 robots 차단이 없으며 관리자 화면은 `noindex,nofollow,noarchive`를 유지합니다. 홈 1280px에서 메인 이미지 4장·서비스 아이콘 4개·최신 글 3개와 가로 넘침 없음을 확인했고, 문의 지도는 `ready`·프레임 300px·지도 298px·타일 27개·전체화면 버튼 표시 상태입니다. 운영 업로드 경로의 임시 PHP 파일 요청은 403으로 차단됐고 검사 파일은 즉시 삭제했습니다.
관리자 글 미리보기 기록: 2026-08-10 글 관리 목록의 14개 글에 관리자 전용 미리보기 링크를 추가했습니다. 초안 1건과 공개 글 1건을 실제 공개 페이지 레이아웃으로 확인했으며, 상태별 미리보기 안내·수정 링크·대표 이미지·초안 수정일 대체 표시·공개 URL canonical·`noindex,nofollow,noarchive`·가로 넘침 없음을 검증했습니다. 비로그인 요청은 관리자 로그인으로 이동합니다.
회사 정보 반영 기록: 2026-08-04 대표 이메일과 주소를 `site_settings` 및 애플리케이션 기본값에 반영했습니다. 테스트 문의 페이지에서 새 주소, 푸터에서 새 이메일·주소가 표시되고 이전 값이 남아 있지 않은 것을 확인했습니다.
회사명 통일 기록: 2026-08-04 공개 헤더·푸터·메타데이터, 관리자 콘솔, 게시글 작성자 기본값과 기존 초안 9건, 운영 루트 임시 페이지를 `(주)지티에스정보통신`으로 통일했습니다. 실제 응답 3개 화면에서 이전 표기 0건, 새 표기 16건을 확인했고 360·390·1440px에서 가로 넘침이 없었습니다.
@@ -63,6 +65,12 @@
지도 표시·전체화면 수정 기록: 2026-08-05 NAVER SDK가 지도 요소의 위치 속성을 덮어써 내부 높이가 0px이 된 원인을 수정했습니다. 일반 상태에서 프레임 300px·지도 298px·타일 이미지 27개·마커 1개를 확인했습니다. 사용자 정의 전체화면 아이콘으로 720px 확장 후 298px 복귀와 마커 유지를 검증했고, 390px에서도 지도 298px와 가로 넘침 없음을 확인했습니다. 정상 상태의 외부 텍스트 링크는 숨기고 실패 시 fallback으로 유지합니다.
지도·전화상담 스태킹 기록: 2026-08-10 네이버 지도 내부 컨트롤이 최대 `z-index: 10000`을 사용해 `z-index: 80`인 전화상담 버튼보다 위에 표시되는 현상을 확인했습니다. 지도 프레임에 독립 스태킹 컨텍스트를 적용하고 전화상담 버튼을 공통 헤더보다 높은 `z-index: 200`으로 조정했습니다. 운영 문의 화면의 실제 겹침 지점에서 최상위 요소가 전화상담 링크이고 지도는 `ready` 상태를 유지하며 가로 넘침이 없음을 확인했습니다.
지도 이동 잠금 기록: 2026-08-10 네이버 지도 옵션에서 마우스·터치 드래그와 키보드 이동을 차단하고 휠·핀치·확대축소 컨트롤은 유지했습니다. 확대 이벤트마다 회사 마커 좌표를 중심으로 재고정합니다. 운영 화면에서 확대 버튼을 눌렀을 때 지도 타일이 27개에서 39개로 갱신되고 마커의 지도 중심 오차가 확대 전후 `1px, -16px`로 동일하며 `ready` 상태가 유지됨을 확인했습니다.
지도 제스처 줌 차단 기록: 2026-08-10 핀치·휠·더블클릭·더블탭·두 손가락 탭 확대축소를 비활성화했습니다. 네이버 기본 줌 컨트롤도 축소 시 세로 중심이 이동하는 것을 확인해 제거하고, 회사 주소 좌표를 중심으로 애니메이션 없이 줌 단계만 변경하는 자체 `+ / ` 컨트롤로 교체했습니다. 운영 화면에서 기본 줌 컨트롤 0개·자체 버튼 2개를 확인했고, 자체 축소 전후 마커 중심 오차가 모두 `1px, -16px`로 동일하며 지도 `ready` 상태가 유지됐습니다.
블로그 임시 썸네일 기록: 2026-08-05 공개 글 6건 중 실제 대표 이미지 5건은 기존 사진을 유지하고, 이미지가 없는 1건에는 `GTS` 브랜드 플레이스홀더가 표시되는 것을 확인했습니다. 1280px에서 카드 행 높이 정렬과 가로 넘침 없음, 390px에서 카드 358px·플레이스홀더 356×199px 및 가로 넘침 없음을 확인했습니다.
문의 입력 편의 기록: 2026-08-05 이메일 아이디 `11111``naver.com` 선택 시 전송용 값 `11111@naver.com`, `직접입력` 선택 시 전체 주소 `hello@company.co.kr`이 유지되는 것을 확인했습니다. 연락처 숫자 `01011111111``010-1111-1111`로 자동 변환됐습니다. 실제 문의는 생성하지 않았으며 390px에서 이메일 필드 312px와 가로 넘침 없음을 확인했습니다.
@@ -178,11 +186,29 @@
- [ ] 반복 로그인 실패 제한
- [ ] 세션 만료 후 재인증 요구
- [ ] 게시글 작성, 수정, 삭제, 공개 상태 동작
- [x] 관리자 글 목록에서 초안·공개 글 미리보기와 비로그인 접근 차단
- [x] 신규·기존 게시글의 `post-{id}` 자동 URL과 종전 주소 301 이동
- [x] 테스트 초안에서 대표 이미지 3장 슬라이드 렌더링과 5초 자동 전환
- [x] 테스트 초안 본문의 `[[image:번호]]` 위치에 이미지 2장 렌더링
- [x] 관리자에서 내부 이미지 토큰 미노출 및 문단·이미지 블록 변환
- [x] 이미지 블록 위·아래 이동 후 저장과 공개 형식 미리보기 순서 일치
- [x] 블록 저장 후 기존 텍스트 이스케이프·이미지 매핑 구조 유지
- [x] 작성 화면 미리보기에 저장 전 제목·본문 변경 즉시 반영
- [x] 작성 화면 미리보기에서 대표 이미지 개수·썸네일·본문 이미지 렌더링 확인
- [x] 대표 이미지 썸네일 선택 시 미리보기의 큰 이미지 전환
- [x] 테스트 관리자 블록 편집기의 등록 본문 이미지 경로와 실제 이미지 응답 확인
- [x] 대표 슬라이드 이전·다음·직접 선택·일시정지 조작 제공
- [x] 다중·본문 이미지가 있는 글의 공개 저장 차단
- [ ] 이미지 파일 선택 후 대체 텍스트·본문 삽입 버튼 실제 업로드 회귀 테스트
- [ ] 관리자 목록의 검색, 필터, 페이지네이션 동작
- [ ] 문의 목록, 상세, 처리 상태 변경 동작
- [ ] 모바일 표와 조작 버튼의 잘림 없음
- [x] 관리자 변경 작업에 CSRF 방어 적용
- [x] 일반 사용자가 관리자 경로를 호출할 수 없음
- [x] 관리자 대시보드 GA4 집계 카드·기간 차트·인기 페이지·유입경로의 데스크톱 표시
- [ ] 관리자 대시보드 GA4 지표의 모바일 표시와 가로 넘침 없음
- [x] 관리자 대시보드에 최근 접속자·전체 방문자·30일 방문자·처음 온 방문자·페이지 열람 수·사이트 방문 횟수가 쉬운 설명과 함께 표시됨
- [ ] GA4 API 장애 시 대시보드의 안전한 대체 안내와 기존 관리자 기능 유지
진행 기록: 2026-08-04 공식 PHP 8.4 컨테이너와 Cafe24 PHP 8.4에서 전체 PHP 문법 통과. 로컬 주요 경로 `/`, `/blog`, `/contact`, `/privacy`, `/inquiry/lookup`, `/admin/login` 200, 비로그인 `/admin`, `/admin/posts` 302, 없는 경로 404 확인. 문의 CSRF 누락 403 및 빈 필수값 제출 422 확인. Cafe24가 비표준 419를 500으로 변환하는 것을 확인해 표준 403으로 조정했습니다.
@@ -201,6 +227,16 @@
진행 기록: 2026-08-04 Cafe24 테스트 경로의 업로드 폴더에 무해한 PHP 검사 파일을 잠시 배치해 HTTP 403과 미실행을 확인한 뒤 즉시 삭제했습니다.
게시글 미디어 기록: 2026-08-10 `https://gtsit.co.kr/site-preview-7c2a91f4/admin/posts/15/preview`에서 대표 이미지 3장, 본문 이미지 2장, 관리자 대체 텍스트 필드와 본문 삽입 토큰을 확인했습니다. 자동재생 후 활성 슬라이드가 변경되고 다음 버튼 조작이 반영되는 것을 확인했습니다. 테스트 자산은 이전 사이트의 메인 이미지와 EXIF 위치정보가 없는 600×450 시공 썸네일만 사용했습니다.
블록 편집기 기록: 2026-08-10 테스트 관리자 글 15번에서 텍스트 3개·이미지 2개 블록 변환, 내부 토큰 미노출, 이동 버튼 상태, 이미지 블록 순서 변경 저장과 미리보기 반영을 확인한 뒤 원래 순서로 복원했습니다. JavaScript와 뷰에서 `innerHTML`, `contenteditable`, 임의 HTML 삽입 API를 사용하지 않으며 모든 편집 요소를 DOM API와 일반 textarea로 생성합니다. 자동 브라우저의 파일 선택 API 제한으로 신규 파일을 통한 전체 업로드 회귀 항목은 수동 확인 대상으로 유지합니다.
작성 화면 미리보기 기록: 2026-08-10 테스트 관리자 글 15번 수정 화면에서 저장하지 않은 제목과 첫 문단이 미리보기 모달에 반영되는 것을 확인했습니다. 대표 이미지 3장과 본문 이미지 2장이 렌더링되고 두 번째 대표 이미지 썸네일 선택 시 큰 이미지가 바뀌는 것을 확인한 뒤, 입력 폼은 원래 값으로 되돌리고 저장하지 않았습니다.
게시글 URL 자동화 기록: 2026-08-10 공유 DB의 기존 게시글 15건을 `post-1`~`post-15`로 변경하고 관리자 목록에서 15건 모두 번호형 주소로 표시되는 것을 확인했습니다. 운영의 종전 공개 주소는 `/blog/post-1`로 301 이동하고 새 주소는 200, 테스트 경로에서도 base path를 포함한 301 이동과 새 주소 200을 확인했습니다. 공개 글 6건의 사이트맵도 번호형 주소로 갱신했습니다.
본문 이미지 경로 기록: 2026-08-10 숨겨진 `data-path`는 공통 HTML의 `src` 경로 보정 대상이 아니어서 테스트 base path가 빠진 `/uploads/...`를 참조하던 문제를 수정했습니다. 관리자 글 15번에서 기존 본문 이미지 2개가 `/site-preview-7c2a91f4/uploads/media-preview/` 경로를 사용하고 각각 600×450px로 정상 로드되는 것을 확인했습니다.
## 11. 보안 및 개인정보
- [ ] HTML과 스크립트 입력의 XSS 방어
@@ -212,8 +248,20 @@
- [x] 개인정보 보관기간·종료일 기록·완전 삭제 명령 구현
- [ ] 만료 문의 완전 삭제 명령의 정기 실행 등록
- [ ] 보안 헤더 적용 여부 확인
- [x] 통계 쿠키 동의 전 Google 태그 미로딩
- [ ] 통계 쿠키 거부·철회 후 선택과 측정 차단 유지
- [x] 운영 공개 경로만 GA4 측정, 관리자·테스트 경로 제외
- [x] 문의 개인정보·접수번호와 관리자 정보가 GA4 이벤트로 전송되지 않음
- [ ] ModSecurity와 UploadGuard 오탐 없음
GA4 수집 기록: 2026-08-12 무료 표준 속성 `549573245`, 운영 웹 스트림 `15422491033`, 측정 ID `G-92XBR314YD`를 생성했습니다. 테스트 경로는 허용 전후 Google 태그 0개, 운영 루트는 최초 동의 전 0개·허용 후 1개를 확인했습니다. GA4 홈의 지난 30분 활성 사용자가 1명으로 표시되어 실제 수집을 확인했으며 광고 저장·Google 신호·광고 개인화는 비활성화했습니다. 사용자·이벤트 데이터는 신규 속성 기본값인 2개월을 사용합니다.
GA4 관리자 연동 기록: 2026-08-13 전용 서비스 계정에 GA4 속성 `뷰어` 권한만 부여했고 Cloud 프로젝트 역할은 추가하지 않았습니다. JSON 키와 15분 집계 캐시는 Git·웹 루트 밖에서 권한 `600`으로 보관합니다. Analytics Data API 실제 요청에서 최근 30일 사용자·신규 사용자·페이지뷰·세션, 14일 추이, 인기 페이지, 유입경로 응답을 확인했습니다.
GA4 대시보드 표시 기록: 2026-08-13 테스트 관리자 화면에서 전체 방문자 1명과 최근 30일 방문자·첫 방문자·페이지 열람·사이트 방문 횟수 각 1회를 확인했습니다. 전체 방문자는 GA4 측정 시작일 `2026-08-12` 이후의 `totalUsers`이며 카드 설명에 측정 시작일을 함께 표시합니다.
운영 배포 기록: 2026-08-13 운영 PHP 파일 전체 문법 통과, GA4 Data API 응답 `available=true`와 전체 방문자 1명 확인, 키·캐시 권한 600 확인. `/`, `/blog`, `/contact`, `/privacy`, 사이트맵과 CSS·JavaScript는 200, 비로그인 `/admin`은 302, `/blog/post-10``/blog/post-14`는 200, 종전 게시글 주소는 번호형 주소로 301 응답했습니다. 브라우저에서 운영 홈의 콘텐츠·슬라이드·서비스·글 목록과 문의 폼·네이버 지도·전체화면 및 확대축소 컨트롤을 확인했습니다.
## 12. 검색, 성능 및 운영
- [x] 임시 페이지 종료 후 공개 화면 `noindex` 제거 확인
@@ -233,7 +281,9 @@
- [x] 회사 주소 좌표와 마커 위치 일치
- [x] 지도 SDK 적용 후 실제 지도 요소 높이와 타일 표시
- [x] 사용자 정의 전체화면 아이콘 열기·종료 및 지도 크기 복구
- [ ] 데스크톱·모바일 지도 확대·축소 및 스크롤 충돌 없음
- [x] 지도 드래그·키보드 이동 차단 및 마커 중심 확대·축소 유지
- [x] 핀치·휠·더블클릭/탭 확대·축소 차단 및 지도 컨트롤만 허용
- [ ] 데스크톱·모바일 지도 확대·축소 및 페이지 스크롤 충돌 없음
- [x] 지도 API 로드 실패 시 주소와 네이버 지도 링크 유지
- [x] Client Secret이 HTML·JavaScript·저장소에 포함되지 않음
- [ ] API 이용 한도와 임계치 알림 설정