


인증서를 자동으로 발급·갱신하는 모노레포를 기밀 정보를 지키며 고치기
ACME로 인증서를 신청·갱신·배포하는, 직접 운영하는 도구를 AI에게 고치게 할 때 쓴다. 변경을 담당 패키지나 플러그인에 넣고, 테스트를 먼저 쓰고, SQL 마이그레이션을 붙이고, 기존 UI 방식을 따르며, 인증서와 키를 코드와 로그에 남기지 않는다.
이 AI 규칙을 사용하려면
## 인증서 신청부터 배포까지의 흐름과 저장된 기밀 정보 파악하기
이 시스템을 인증서 신청, 갱신, 배포, 모니터링, 알림, API 통합을 관리하는 비공개 self-hosted 제품으로 취급한다. 핵심 pipeline은 ACME를 통해 인증서를 신청하고, DNS-01, HTTP-01, CNAME proxy 또는 provider별 validation을 완료하고, PEM, PFX, DER, JKS, P7B 등의 형식으로 export한 뒤 server, proxy, cluster, CDN, cloud service 또는 control panel에 배포할 수 있다.
이 시스템은 인증서, cloud 인증 정보, SSH 정보, API key와 그 밖의 민감한 설정을 저장한다. local data나 secret을 source 변경, log, fixture, screenshot, report 또는 생성된 metadata에 절대 노출해서는 안 된다.
## 모노레포의 workspace를 나눠 다루고 실행 데이터는 건드리지 않기
repository에서 지정한 package manager, workspace 정의, lockfile을 사용하고 다른 package manager의 lockfile을 도입해서는 안 된다. package가 독립적인 version-control workspace라면 해당 workspace 내부에서 status와 diff를 확인하고, 그 변경 사항을 root repository의 변경 사항과 분리한다.
task가 명시적으로 대상으로 지정하지 않은 runtime data, log, 임시 output, 생성된 metadata, distribution 또는 export된 plugin manifest를 변경하거나 commit해서는 안 된다. 동적으로 설치된 SDK를 이해해야 할 때 runtime dependency directory를 read-only로 조사할 수 있지만, 이 directory는 source가 아니며 변경 집합에 포함해서는 안 된다.
## 변경을 그 동작을 맡은 패키지나 플러그인에 넣기
편집 전에 소유권을 식별한다. protocol client는 account, order, challenge 동작을 소유하고, pipeline package는 실행 model과 task lifecycle을 소유한다. server module은 entity, service, controller, permission, migration을 소유하고, client module은 view, form, route, API adapter를 소유한다. provider plugin은 provider별 DNS, 배포, 알림 동작을 소유한다.
단일 provider의 결함은 해당 provider의 plugin 안에 유지한다. 여러 provider에서 일관되게 필요로 하는 재사용 가능한 의미를 나타내는 경우에만 동작을 pipeline core 또는 shared plugin library로 옮긴다. 별도의 병렬 구현을 만들기보다 확립된 module, service, component, plugin pattern을 우선한다.
## 패키지 자체 명령을 쓰고 종속성을 마음대로 설치하지 않기
validation을 실행하기 전에 문서화된 command를 읽는다. 영향을 받는 package 자체의 unit test, lint, formatting, build command를 사용하고, 변경이 명확하게 package 경계를 넘는 경우에만 더 넓은 check로 확장한다. 지원되는 file 단위 validation 대신 이미 고장 난 것으로 알려진 type check나 임시 test script를 사용해서는 안 된다.
dependency를 선제적으로 설치해서는 안 된다. dependency 누락, terminal 요구 사항 또는 network access 때문에 필요한 check를 실행할 수 없다면 해당 시도를 중단하고 환경 제한을 보고한다. version에 민감한 SDK 동작은 기억이나 다른 version에 의존하지 말고 실제 설치된 runtime dependency와 함께 제공된 documentation을 조사한다.
## 처리를 이름 붙인 단계로 나누고 이유를 적은 주석으로 읽기 쉽게 하기
간결한 syntax보다 가독성을 우선한다. 복잡한 조건, 중첩 ternary, 긴 chain, inline object, 여러 layer의 helper call을 명확한 이름의 중간값 또는 역할이 집중된 method로 나눈다. 의미 있는 local variable이 처리 전환을 드러낼 수 있다면 한 method call을 다른 method의 argument 안에 직접 넣어서는 안 된다.
method에 세 개가 넘는 argument가 필요하다면 object parameter를 사용한다. business rule, compatibility 제약, protocol 세부 사항, 숨은 risk가 존재하는 이유를 comment로 설명한다. 작은 logic은 사용 위치에 유지하되, business rule, field 변환, permission check, repository 선택, transaction 전달 또는 계산의 사본을 계속 동기화해야 한다면 공통 구현으로 추출한다.
## 실패하는 테스트를 먼저 쓰고 가장 작은 변경으로 고치기
주요 business 동작에는 범위를 좁힌 unit test를 추가한다. 새 기능이나 결함 수정에서는 구현 전에 기대 동작을 재현하면서 실패하는 test를 우선 작성하고, 이후 최소한의 변경을 적용한 뒤 package 단위 test를 다시 실행한다. 나중에 추가한 test 때문에 기존 동작을 바꿔야 한다면, 변경 전에 그 동작이 기존 요구 사항이 아니라 결함인지 확인한다.
pure unit test는 source 가까이에 두고 production build에는 포함하지 않는다. test만을 위해 production code를 재구성하지 말고 static ESM import에는 적절한 module-mocking tool을 사용한다. package의 unit-test environment를 명시적으로 설정하고, package 간 영향 때문에 필요할 때만 더 넓은 build를 실행한다.
## 테이블 변경에는 SQL 마이그레이션을 붙이고 트랜잭션을 올바르게 넘기기
모든 table 구조 변경에는 명시적인 SQL migration을 함께 추가하고 ORM schema synchronization에 절대 의존하지 않는다. 임시 upload key는 영구 저장 전에 승인된 file service를 통해 정식 key로 전환하고, 필요한 owner와 공개 또는 비공개 가시성을 유지한다.
transaction을 전달할 때는 선택적 transaction manager를 포함하는 context를 request object보다 먼저 전달하고 shared repository helper와 transaction helper를 사용한다. 단순 query나 pure function에는 transaction context를 추가해서는 안 된다. null 또는 undefined 값이 잘못된 동등 조건이 되지 않도록 선택적 tenant 또는 project predicate는 정식 query helper로 구성한다.
상속받은 base method와 충돌하는 service method 이름이나 signature는 피한다. base contract를 유지하면서 operation 범위를 명확히 나타내는 구체적인 이름을 선택한다.
## 관리 화면은 기존 CRUD 프레임워크와 폼 대화 상자로 만들기
편집 전에 해당 기능을 소유한 feature view와 API module을 찾는다. 일반적인 관리, record 검색, table 기반 관리에는 승인된 CRUD framework를 사용한다. 가벼운 read-only 표시, 상호 작용이 강한 interface 또는 표준 framework와 맞지 않는다는 점이 문서화된 경우에만 custom list를 만든다.
내장 data grid에는 안정적인 높이 또는 자식 요소를 줄일 수 있는 끊김 없는 flex chain이 필요하다. 검색 가능한 identity field에는 확립된 remote dictionary를 재사용한다. 단순 확인에는 confirmation dialog를 사용할 수 있지만, field를 수집하거나 input을 validation하거나 추가 값을 submit하는 dialog는 승인된 form-dialog abstraction을 사용해야 한다.
repository의 광범위한 type check 또는 unit command가 유효하지 않은 것으로 알려져 있다면, 변경된 client file에 지원되는 formatter와 linter만 실행한다.
## 인증서 문제를 알맞은 계층에서 고치고 계정 키 지키기
shared pipeline code를 변경하기 전에 provider plugin을 조사하고, provider 동작이 version에 의존할 때는 실제로 동적 설치된 SDK를 조사한다. 수정 사항을 적용하기 전에 인증서 문제가 ACME client, pipeline 실행, server service, provider plugin, shared certificate utility 또는 client form 중 어디에 속하는지 판단한다.
protocol client가 새로운 external account binding 자료를 소비하지 않고 기존 account URL을 가져올 수 있다면 기존 account private key를 사용한 조회 전용 account operation을 사용한다. external account binding identifier가 변경되면 해당 identifier에 bind된 새 account private key를 생성한다. 그렇지 않으면 인증서 발급을 차단하고 account key 갱신을 요구한다.
인증 정보의 값을 code, log, test, report 또는 commit 대상 artifact에 포함해서는 안 된다. upload, 배포, 알림 또는 open API flow를 확장할 때도 validation과 authorization 경계를 유지한다.
## 예전 설정에서도 같은 동작을 유지하고 실행한 검사 보고하기
plugin parameter를 추가할 때는 저장된 legacy config에 해당 값이 없는 경우를 처리하고, migration이 명시적으로 변경하지 않는 한 이전 동작을 유지한다. 정의된 compatibility branch 없이 누락된 값을 새로운 mode로 재해석해서는 안 된다.
완료 전에 범위를 좁힌 diff를 조사하고, root와 각 중첩 workspace를 별도로 검증하며, 직접 영향을 받는 test와 file 단위 formatting 또는 lint check를 실행하고, 필요한 migration을 확인한다. runtime data와 생성된 output이 변경 집합 밖에 있는지 확인한다.
변경한 내용, 통과한 check, 실행할 수 없었던 check, 더 넓은 validation을 범위 밖으로 둔 이유를 보고한다.
