웅파
안녕하세요, 웅파입니다 — 26년째 PHP로 개발하고, 요즘은 AI와 같이 개발합니다
변종원(닉네임 웅파). CodeIgniter 한국 사용자 포럼을 만들고 책을 쓴 PHP 개발자이고, 지금은 AIvance라는 이름으로 SaaS 제품 11개를 Claude Code와…
웅파 17년: CodeIgniter 포럼 운영하며 배운 것
문을 닫고 나서야 쓰는 글 올해 봄, CodeIgniter 한국사용자포럼(CI Korea)의 게시판을 닫았습니다. 지금 cikorea.net에 들어가면 CI4·CI3 매뉴얼 링크와…
제품 11개를 한 사람이 운영하는 법: AIFid·AILign·AILicet·AICassa 등 공통 골격과 MSA 서비스 분할 기준
들어가며 저는 AIvance라는 이름으로 SaaS 제품 11개를 혼자 만들고 운영합니다. 비결은 제품을 11번 만든 게 아니라, 골격을 한 번 만들고 11번 재사용했다는 데 있습니…
MSA 신규 시작 vs 기존 모놀리스 유지: 억지로 쪼개지 않는 판단 기준
들어가며 결론부터 말하면, 저는 새 서비스를 거의 항상 모놀리스로 시작합니다. 그리고 쪼갤 때는 "도메인이 다르다"가 아니라 "안 쪼개면 안 돌아간다"는 이유가 있을 때만 쪼갭니다…
OpenAPI를 PHP 어트리뷰트로: $ref 재사용 컴포넌트와 공통 ErrorResponse
에러 응답 스키마는 한 번만 정의하고, 모든 엔드포인트는 $ref 한 줄로 가리킨다. 이 글은 swagger-php의 PHP 8 어트리뷰트로 그 구조를 만든 기록입니다. 왜 이 주…
CI4 모델·서비스 계층 분리: 컨트롤러를 얇게 유지하는 규칙과 트랜잭션 위치
결론부터 말하면 규칙은 두 줄이다. 컨트롤러는 "검증 → 서비스 호출 → 응답"만 한다. 트랜잭션은 유스케이스를 대표하는 서비스 메서드 하나가 연다. 나머지는 이 두 줄을 지키기…
프론트 로그를 큐로 받는 수집 파이프라인: 즉시 202 → Redis → 워커 → 파일 + DB, 실패 시 dead-letter
한 줄 요약 — 로그 수집 API는 받은 것을 Redis에 넣고 바로 202를 돌려줍니다. 파일과 DB에 쓰는 일은 워커가 배치로 하고, 끝내 처리하지 못한 로그는 버리지 않고 d…
API 부하 분산 기본기: Redis 캐시 TTL 표, 큐 + 202 Accepted, N+1과 SELECT * 금지
한 줄 요약 — API가 느려지면 서버부터 늘리고 싶어집니다. 하지만 제 경험상 먼저 할 일은 요청 하나가 하는 일을 줄이는 것입니다. 같은 걸 두 번 계산하지 않고(캐시), 기다…
CodeIgniter 4 + 모던 PHP 8.4: readonly DTO, backed enum, match로 배열·define() 걷어내기
들어가며 결론부터 말하면, 상태·역할 같은 "닫힌 값의 집합"은 backed enum으로, 계층 사이를 오가는 데이터는 readonly DTO로, 값에 따른 분기는 match로 옮…
JWT 직접 만들지 마라: alg:none 우회, Access/Refresh 분리, Redis 회전·블랙리스트
JWT는 서명이 아니라 검증에서 무너진다 JWT를 base64_encode와 hash_hmac으로 직접 조립하지 마십시오. 토큰을 만드는 쪽은 단순하지만, 받은 토큰을 믿을지 판단…
CI4 브랜드 에러 페이지: 설계서 한 장에서 구현까지
한 줄 요약: 제품마다 제각각이던 CI4 기본 에러 화면을, 설계서 한 장으로 규칙을 먼저 정한 뒤 AIvance 브랜드 템플릿 한 벌로 바꿨습니다. 핵심은 예쁜 화면이 아니라 "…
SEO + GEO 동시 적용: 정적 페이지에서 AI 검색 노출까지 한 방법
들어가며 결론부터 말하면, 정적 페이지에서 SEO와 GEO를 따로 할 필요는 없습니다. 콘텐츠 데이터를 한 곳에 두고, 그 한 벌에서 화면(HTML)·구조화 데이터(JSON-LD)…
DB는 UTC, 화면은 KST: TIMESTAMP + 세션 타임존으로 변환 코드 없애기
한 줄 요약 — 시각은 MySQL TIMESTAMP로만 저장하고, 앱 연결마다 세션 타임존을 +09:00으로 걸어 둡니다. 저장은 DB가 UTC로, 표시는 세션이 KST로 알아서…
자체 호스팅 러너가 컨테이너일 때 CI가 깨지는 6가지
러너를 컨테이너로 띄우면 GitHub 호스티드 러너에서 당연하던 것들이 하나씩 깨집니다. 이 글은 제가 실제로 밟은 여섯 가지 함정과 그 해법입니다. 지난 글 《AI가 쓴 코드도…
CI 테스트 5분+ → 46초: MySQL 튜닝부터 paratest 병렬화까지
한 줄 요약 — 테스트가 느린 이유는 PHP가 아니라 디스크였습니다. MySQL을 메모리 위로 올리고, InnoDB 체크포인트 스톨을 잡고, 마지막으로 paratest로 병렬화하면…
호스팅이 419를 500으로 바꿔버린 날
한줄요약: 앱이 보낸 419가 웹서버를 지나며 500으로 바뀌었고, 그걸 고친 JS는 Cloudflare 캐시 때문에 사용자에게 닿지 않았습니다. 공유 호스팅과 CDN 사이에서 두…
문의 폼 봇 방어 5겹 스택, 실제로 뚫린 순서대로
Origin/UA 검증, 1회용 CSRF, 레이트리밋, Turnstile, reCAPTCHA v3. 한 겹이 뚫릴 때마다 다음 겹을 덧댄 기록과, 그 과정에서 만난 502와 IP…