확인해야 하는 것과 가정해도 되는 것을 구분하라

26년 07월 30일

삭제 가능 플래그가 거짓말을 했다

이날은 “확인해야 하는 것과 가정해도 되는 것을 구분하라”는 교훈을 정리한 날이다.

삭제 가능 플래그를 믿었는데 거짓이었다.

포트가 열려 있으면 내 서버라고 가정했는데 다른 사람 서버였다.

그리고 구조적으로 수정할 수 없는 1턴 지연도 있었다.

강의 삭제 기능을 만들었다.

백엔드에서 삭제 가능 여부라는 불린 값을 준다.

true면 삭제 가능, false면 불가능. 이 플래그를 믿었다.

true면 삭제 버튼을 보여주고 false면 숨겼다.

근데 알파 환경에서 테스트해보니까, 학생이 1명인 KDT 강의가 삭제 가능 여부: true였다.

학생이 있는데 삭제 가능?

이건 거짓이다.

현재 학생 수는 충분하지 않다

삭제 가능 여부는 현재 등록된 학생 수만 검사했다.

근데 과거에 등록했다가 탈퇴한 학생은 안 잡힌다.

소프트 삭제된 과거 등록 기록이 있는데, 이걸 무시한다.

그래서 새로운 백엔드 필드를 만들었다.

과거 등록 이력. 과거 등록 기록을 포함해서 검사한다.

이제 “삭제 가능”이 실제로 가능하다.

플래그를 믿지 말고 실제 환경에서 확인해라

플래그는 “이렇게 되어 있을 것”이다.

실제 환경은 “이렇게 되어 있다”다.

둘이 다를 수 있다.

알파 환경에서 실제로 테스트했기 때문에 거짓을 발견했다.

플래그만 보고 “맞겠지”라고 가정하면, 학생이 있는 강의를 삭제하게 된다.

포트가 열려 있으면 내 서버가 아니다

개발 서버를 실행했다.

포트 19093이 열려 있었다.

“내 서버가 떴다”고 가정하고 테스트를 진행했다.

근데 19093은 다른 작업자의 서버였다.

내 서버가 아니라 다른 사람 서버에서 테스트한 거다.

결과가 “초록”이어도 의미가 없다.

다른 사람 서버에서 내 코드가 안 돌아갔기 때문이다.

포트가 열려 있음과 내 서버임은 다르다

포트가 열려 있다는 건 “뭔가 실행 중이다”다.

“내 서버가 실행 중이다”가 아니다.

lsof로 프로세스를 확인하고 작업 디렉토리가 내 작업과 일치하는지 봐야 한다.

가짜 초록

다른 사람 서버에서 테스트하면 “통과”가 나올 수 있다.

근데 이건 내 코드를 테스트한 게 아니다.

가짜 초록이다.

“초록이니까 맞겠지”라는 가정이 테스트를 무효화한다.

잠금 파일이 오래되면 데몬이 영원히 안 뜬다

코드 인덱싱 진행률이 94%에서 멈췄다.

잠금 파일이 오래됐기 때문이었다.

잠금 파일이 있으면 데몬이 실행 중이라고 가정한다.

근데 잠금 파일이 남아있는데 데몬은 죽어 있다.

잠금 파일만 남은 거다.

이 상태에서는 새 데몬이 안 뜬다.

“이미 데몬이 있다”고 가정하기 때문이다.

회복 논리

잠금 파일이 있을 때, 프로세스 ID와 소켓 파일이 실제로 존재하는지 확인한다.

존재하지 않으면 잠금 파일이 오래된 거다.

지우고 새 데몬을 띄운다.

“잠금 파일이 있다”는 것과 “데몬이 살아 있다”는 다르다.

잠금 파일은 표시다.

데몬은 실체다.

표시를 믿지 말고 실체를 확인해라.

구조적으로 1턴 늦는다

clean-code 규칙을 편집 시점에 주입하고 싶었다.

에이전트가 코드를 편집하기 전에 규칙을 주입한다.

근데 안 됐다.

컨텍스트 이벤트의 구조적 한계

컨텍스트 이벤트는 LLM 호출 직전에 발생한다.

이때 “지금까지” 발생한 도구 호출만 검사할 수 있다.

첫 번째 턴: 편집 기록이 없다.

규칙을 주입하지 않는다.

에이전트가 규칙 없이 편집한다.

두 번째 턴: 첫 번째 턴의 편집이 감지된다.

규칙이 주입된다.

근데 이미 편집은 끝났다.

규칙이 항상 편집 “이후”에 도착한다.

이건 버그가 아니다.

구조적 한계다.

구조적 한계는 고칠 수 없다

이건 버그가 아니다.

컨텍스트 이벤트의 설계가 “과거”만 검사할 수 있기 때문이다.

현재를 검사하려면 이벤트 구조를 바꿔야 한다.

근데 그러면 LLM 호출 전에 비동기 처리가 끝나야 한다.

성능 문제가 생긴다.

구조적 한계는 고칠 수 없다.

인정하고 우회해야 한다.

“1턴 늦어도, 두 번째 편집부터는 규칙이 적용된다”로 받아들인다.

강제 주입 스킬은 라우팅 신호가 아니다

스킬 설명을 압축했다.

clean-code 스킬의 설명이 663글자에서 35글자로 줄었다.

강제 주입 스킬은 매 턴 주입된다

clean-code 같은 강제 주입 스킬은 매 턴 무조건 주입된다.

라우팅 신호가 아니다.

“이 스킬을 언제 발동할지”를 설명할 필요가 없다.

발동 자체가 확정되어 있으니까. 라우팅 신호인 스킬은 “언제 이 스킬을 쓸지”를 설명해야 한다.

근데 강제 주입 스킬은 그럴 필요가 없다.

설명을 최소화할 수 있다.

마무리

내가 이날 배운 핵심은 다음과 같다.

이날의 핵심은 “확인과 가정을 구분하라”였다.

삭제 가능 플래그는 가정이었다.

실제 환경에서 확인하니까 거짓이었다.

포트가 열려 있으면 내 서버라는 가정도 거짓이었다.

잠금 파일이 있으면 데몬이 살아 있다는 가정도 거짓이었다.

그리고 구조적 한계는 고칠 수 없다.

컨텍스트 이벤트가 1턴 늦는 건 버그가 아니라 구조다.

인정하고 우회해야 한다.

표시와 실체를 구분하라. 플래그와 실제 동작을 구분하라.

포트와 서버를 구분하라. 잠금 파일과 데몬을 구분하라.

그리고 구조적 한계는 인정하라.