From b1d834c7b4328bfb1ad931e7c733d22ee6f1aa33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AC=B8=ED=98=95=EC=84=9D?= Date: Fri, 31 Jul 2026 16:28:19 +0900 Subject: [PATCH] =?UTF-8?q?seminar/=EB=A0=88=EA=B1=B0=EC=8B=9C=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4PHP=ED=94=84=EB=A1=9C=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=A7=81.md=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seminar/레거시 서비스PHP프로파일링.md | 262 ++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 seminar/레거시 서비스PHP프로파일링.md diff --git a/seminar/레거시 서비스PHP프로파일링.md b/seminar/레거시 서비스PHP프로파일링.md new file mode 100644 index 0000000..58335aa --- /dev/null +++ b/seminar/레거시 서비스PHP프로파일링.md @@ -0,0 +1,262 @@ +# 장헌 7개 서비스 PHP·SQL 프로파일링 세미나 + +대상: A팀 기술회의 +측정일: 2026-07-31 +환경: 정상 환경과 분리한 `jang-profile`, PHP 8.4.23, Xdebug 3.5.3, +MySQL 8.4 Performance Schema + +## 1. 오늘 확인할 질문 + +1. 화면이 열린다는 사실과 기능이 정상이라는 사실을 어떻게 구분하는가? +2. 평균 응답시간 뒤에 숨은 느린 PHP 함수와 SQL을 측정하는가? +3. 권한차단·외부의존·데이터부재를 실제 실패와 구분하는가? +4. 쓰기 테스트 후 DB와 합성 데이터가 원상태인지 확인하는가? +5. 양 팀이 배포 전후 동일한 측정 기준을 사용할 수 있는가? + +발표 메모: + +> 이번 결과는 단순 화면 캡처가 아닙니다. 계정, 서비스, 메뉴, 기능, +> DB 변화, PHP 함수와 SQL을 서로 연결해 본 결과입니다. + +## 2. 측정 환경과 안전장치 + +- 정상 환경: `jang-p12-auto-r10`, `127.0.0.1:18091` +- 프로파일 환경: `jang-profile`, `127.0.0.1:18092` +- 별도 웹·DB·세션·첨부파일·로그·프로파일 볼륨 사용 +- 원본 DB를 읽기 전용 덤프로 복제 +- 원본과 복제본 모두 DB 객체 462개, 뷰 13개 +- 평상시 Xdebug 비활성, 지정 트리거 요청만 프로파일링 +- 정상 환경과 프로파일 환경 모두 최종 `healthy` + +발표 메모: + +> 운영과 비슷한 데이터를 사용하되 테스트 쓰기는 복제 DB에만 +> 발생하도록 분리했습니다. 프로파일러는 모든 요청에 켜지 않았습니다. + +## 3. 계정·서비스·메뉴·기능 범위 + +| 측정 축 | 실행 수 | 결과 | +| --- | ---: | --- | +| 7계정 × 7서비스 대표 접근 | 49 | 정상 46, 권한차단 3 | +| 승인 역할 × 실제 메뉴 조회 | 135 | 정상 128, 의존 6, 데이터부재 1 | +| 서비스별 쓰기·권한·DB정리 | 7시나리오×2회 | 14/14 통과 | +| 전체 통합 매트릭스 | 191행 | 실패 0 | + +주의: + +- 7계정으로 135개 메뉴를 각각 실행한 945건 완전 교차 검사는 아니다. +- 49개 대표 접근으로 계정의 서비스 진입을 확인했다. +- 135개 메뉴는 서비스별 승인 역할로 확인했다. +- 메뉴별 타 역할 권한은 추가 확인 범위다. + +발표 메모: + +> 숫자를 부풀리지 않기 위해 대표 접근과 메뉴 탐색을 분리했습니다. +> 현재 확정 가능한 범위와 아직 측정하지 않은 범위를 같은 표에 둡니다. + +## 4. 결과 분류 + +| 분류 | 건수 | 의미 | +| --- | ---: | --- | +| 정상 | 181 | 기대한 인증·조회·쓰기 경계·정리 통과 | +| 권한차단 | 3 | 비인사 역할의 인사관리 로그인 거부 | +| 외부 라이브러리 의존 | 3 | jspreadsheet, Google Maps, Summernote | +| 부모 실행문맥 의존 | 3 | 부모 프레임·선행 화면 없이 단독 실행 불가 | +| 데이터부재 | 1 | 선행 선택 전 빈 출하 목록 | +| 실패 | 0 | HTTP·기능 실패 없음 | + +발표 메모: + +> 권한이 막힌 것을 실패라고 세지 않았고, 데이터가 없는 화면도 +> 프로그램 오류와 분리했습니다. 부모 프레임이 필요한 화면 역시 +> 외부 장애와 구분했습니다. + +## 5. 기능별 DB 변경 전후 + +- 인트라넷 일정 합성 등록·권한차단·삭제: 잔여 0 +- 공장 공통코드 합성 등록·수정·삭제: 잔여 0 +- 인사 탄력근무 합성 등록·수정·삭제: 잔여 0 +- 사업 기초코드 합성 등록·수정·삭제: 잔여 0 +- 회계 계정코드 합성 등록·수정·삭제: 잔여 0 +- 가상야적장 익명 쓰기 차단: 핵심 DB 행 수 불변 +- 터치스크린 쓰기·배치 7개 진입점 차단: DB 스냅샷 불변 + +발표 메모: + +> 쓰기 성공만 확인한 것이 아니라 테스트 종료 후 잔여와 핵심 테이블 +> 불변까지 확인했습니다. 운영 데이터를 흉내 낸 값은 남기지 않았습니다. + +## 6. 서비스별 대표 응답시간 + +워밍업 2회 후 일반 요청 5회의 로컬 단일 요청 결과: + +| 서비스 | p50 | p95 | 최대 | +| --- | ---: | ---: | ---: | +| intranet | 4.627ms | 5.882ms | 5.882ms | +| factory_mng | 11.087ms | 11.850ms | 11.850ms | +| person_mng | 3.202ms | 3.789ms | 3.789ms | +| projt_mng | 13.893ms | 14.488ms | 14.488ms | +| account | 4.830ms | 5.262ms | 5.262ms | +| virtual_yard | 16.632ms | 18.006ms | 18.006ms | +| factory_board | 11.730ms | 12.777ms | 12.777ms | + +해석: + +- 현재 대표 화면에서 절대 응답시간은 짧다. +- 브라우저 전체 렌더링, 외부 자원, 대량 동시성 결과와 동일하지 않다. +- 표본이 작으므로 운영 SLA나 용량 결론으로 사용하지 않는다. + +## 7. Xdebug 측정 방식과 오버헤드 + +- 서비스별 일반 요청 5회 +- 서비스별 Xdebug 요청 3회 +- 고유 Cachegrind 21개 +- 파일명에 마이크로초 `%u`를 사용해 덮어쓰기 방지 + +| 서비스 | 일반 p50 | 프로파일 중앙값 | 관측 배수 | +| --- | ---: | ---: | ---: | +| intranet | 4.627ms | 5.108ms | 1.104x | +| factory_mng | 11.087ms | 11.320ms | 1.021x | +| person_mng | 3.202ms | 3.505ms | 1.095x | +| projt_mng | 13.893ms | 14.766ms | 1.063x | +| account | 4.830ms | 4.997ms | 1.035x | +| virtual_yard | 16.632ms | 16.547ms | 0.995x | +| factory_board | 11.730ms | 11.675ms | 0.995x | + +발표 메모: + +> 1배 미만은 프로파일러가 성능을 높였다는 뜻이 아니라 작은 표본의 +> 변동입니다. Cachegrind의 함수 순위에는 사용할 수 있지만 운영 +> 오버헤드 결론에는 더 많은 반복이 필요합니다. + +## 8. 느린 PHP 함수 Top + +서비스별 프로파일 3회 합계 self time: + +| 순위 | 서비스 | 함수 | self time | +| ---: | --- | --- | ---: | +| 1 | virtual_yard | `mysqli_real_connect` | 22.412ms | +| 2 | projt_mng | `mysqli_query` | 13.118ms | +| 3 | factory_mng | `mysqli_query` | 9.878ms | +| 4 | projt_mng | `mysqli_real_connect` | 7.223ms | +| 5 | factory_mng | `mysqli_real_connect` | 6.309ms | +| 6 | virtual_yard | `mysqli_set_charset` | 4.391ms | +| 7 | account | `mysqli_real_connect` | 2.753ms | +| 8 | projt_mng | Smarty compiled process | 2.103ms | + +해석: + +- 대표 요청에서는 DB 연결·쿼리 비용이 우선 관찰 대상이다. +- 그다음은 Smarty 컴파일 결과 처리와 렌더링이다. +- 지속 연결 적용 여부는 효과뿐 아니라 세션·장애 복구를 함께 검증해야 한다. + +## 9. 느린 SQL Top + +Performance Schema digest 초기화 후 7개 대표 화면을 5회 반복: + +| 순위 | 유형 | 호출 | 총시간 | 평균 | 최대 | 검사행 | +| ---: | --- | ---: | ---: | ---: | ---: | ---: | +| 1 | 근태 집계 | 155 | 45.939ms | 0.296ms | 0.422ms | 18,755 | +| 2 | 출퇴근 조회 | 155 | 22.894ms | 0.148ms | 0.227ms | 0 | +| 3 | 팝업 공지 조회 | 5 | 17.613ms | 3.523ms | 3.924ms | 12,925 | +| 4 | 가상야적장 상태 조회 | 5 | 13.174ms | 2.635ms | 2.898ms | 9,980 | +| 5 | 시스템 설정 조회 | 15 | 11.158ms | 0.744ms | 0.904ms | 10,185 | + +관찰: + +- 팝업 공지와 가상야적장 상태 조회는 각 5회 모두 no-index가 기록됐다. +- 근태 집계는 절대시간은 짧지만 호출 155회와 no-index 155회다. +- 즉시 인덱스를 추가하지 않고 `EXPLAIN`과 데이터 증가 조건을 확인한다. + +## 10. 발견 후 보정한 PHP 경고 + +- 관리자 계정의 사업관리 홈에서만 재현 +- 비활성 화면이 HTML 주석 안에 있었지만 Smarty는 내부 변수를 컴파일 +- `Undefined array key` 18건 +- 연쇄 null 속성 접근 18건 +- 합계 36건, HTTP 응답은 200 + +보정: + +- 공통 홈 렌더링 전에 비활성 화면 변수의 빈 기본값을 지정 +- 문서 조회 함수에도 조회 결과 0건 기본값을 지정 +- 사업관리 7계정 재검사: 7/7 HTTP 200, PHP Warning 0 +- 7개 서비스 대표 회귀: 7/7 HTTP 200, PHP 오류 0 + +판정: 즉시 수정 완료, 프로파일 환경 검증 완료 + +## 11. 조치 분류 + +### 즉시 수정 + +- 현재 확인된 HTTP·기능 실패는 0건 +- 사업관리 관리자 조건 Warning 36건 보정 및 회귀 완료 + +### 추가 확인 + +- 공지·가상야적장 SQL 실행계획과 복합 인덱스 후보 +- 근태 쿼리 155회 호출의 중복 제거 가능성 +- jspreadsheet·Google Maps·Summernote 로딩 정책 +- 부모 프레임 의존 화면의 실제 메뉴 진입 검사 +- 데이터부재 화면의 합성 기준 데이터 +- 메뉴별 타 역할 권한 완전 교차가 필요한 범위 + +### 현행 유지 + +- 46개 정상 계정·서비스 대표 접근 +- 128개 정상 메뉴 조회 +- 14회 쓰기·권한·정리 결과 +- 정상 환경에서 Xdebug 비활성 정책 + +## 12. A팀에 묻는 비교 질문 + +1. 서비스별 p50·p95·p99와 최대를 측정하는가? +2. 워밍업, 데이터 조건, 반복 횟수, 동시성을 고정하는가? +3. PHP 함수별 self·inclusive time과 호출 횟수를 보는가? +4. SQL digest별 총·평균·최대시간과 rows examined를 보는가? +5. no-index와 임시 디스크 테이블을 배포 전후 비교하는가? +6. OPcache hit/miss와 메모리 고갈을 감시하는가? +7. 계정×서비스×메뉴×기능 권한표가 있는가? +8. 쓰기 테스트 전후 DB 변화와 합성 잔여 0을 확인하는가? +9. 권한차단·외부의존·데이터부재를 실패와 구분하는가? +10. 프로파일 산출물의 크기 제한·보관·폐기 기준이 있는가? +11. 운영에서 프로파일러를 활성화할 때 승인과 종료 조건이 있는가? +12. 양 팀이 같은 시나리오로 배포 전후 회귀 비교할 수 있는가? + +## 13. 회의에서 합의할 제안 + +- 공통 결과 분류 코드와 증적 형식 +- 대표 시나리오·테스트 계정·합성 데이터 규칙 +- p50·p95·p99·최대와 SQL digest 기본 항목 +- PHP·SQL Top N 산출 기준 +- 프로파일러 운영 활성화 금지 또는 승인 절차 +- 합성 데이터 접두어와 종료 후 잔여 0 원칙 +- 배포 전후 동일 시나리오 비교와 결과 보관 기간 + +## 14. 결론 + +- 7개 서비스는 현재 대표 접근·조회·쓰기 경계에서 실제 실패 0건이다. +- 권한차단·의존·데이터부재를 실패와 분리했다. +- DB 변경 후 합성 잔여 0 또는 스냅샷 불변을 확인했다. +- PHP 병목 후보는 DB 연결·쿼리와 Smarty 렌더링이다. +- SQL 확장 위험 후보는 팝업 공지, 가상야적장, 반복 근태 집계다. +- 다음 기술 작업은 SQL `EXPLAIN` 비교와 의존 화면 실제 진입 검증이다. + +발표 마무리: + +> A팀이 이미 같은 항목을 측정하고 있다면 도구 이름보다 결과 형식을 +> 맞추는 것이 중요합니다. 측정하지 않는 항목이 있다면 이번 장헌 +> 매트릭스와 프로파일 결과를 공통 출발점으로 제안합니다. + +## 증적 위치 + +- 통합 매트릭스: + `docs/evidence/p12/uat/2026-07-31_account-service-menu-function-matrix.csv` +- 응답시간: + `data/test-artifacts/profile-20260731-unique/seven-service-profile/` +- PHP 함수: + `data/test-artifacts/profile-20260731-unique/cachegrind-analysis/` +- SQL digest: + `data/test-artifacts/profile-20260731-unique/sql-analysis/digest-top.tsv` +- 진행 관리: + `new-pc-development-setup-stabilization.md`