AI에게 '불가능'을 구현하는 방법: 프롬프트에 의존하지 않는 코딩 제약
요약
AI가 생성한 코드를 단순히 '지시'하는 것만으로는 아키텍처적 제약을 완벽히 보장할 수 없습니다. 따라서 Roslyn Analyzer와 같은 컴파일러 기반의 검증 메커니즘을 도입하여, 코드 자체가 규칙 위반 시 빌드 실패를 유도하도록 하는 것이 중요합니다.
핵심 포인트
- AI에게 지시하는 것보다 시스템이 강제적으로 검증해야 합니다.
- Roslyn Analyzer는 C#에서 의존 관계나 패턴을 진단할 수 있게 합니다.
- ArchitectureAnalyzer는 아키텍처 계약을 컴파일 가능한 형태로 만듭니다.
- PolicySharp는 '허가 목록(allow-list)' 기반의 기본 거부 원칙을 적용합니다.
AI에게 코드를 작성하게 하는 기회가 늘었습니다. 저 역시 OSS 개발에서 AI를 활용하고 있습니다.
그 과정에서 제가 주목한 점은, AI에게 규칙을 '지시'하는 것과, 규칙을 위반한 코드를 시스템이 '통과시키지 않는' 것은 다르다는 것입니다.
예를 들어, AI에게 다음과 같은 지침을 주었다고 가정해 봅시다.
- Domain 계층에서 Infrastructure 계층을 직접 참조하지 않도록 할 것
- 순수해야 하는 처리에 부작용(side effect)을 섞지 않도록 할 것
- 허가되지 않은 의존 관계를 추가하지 않도록 할 것
- 기존 아키텍처를 유지하도록 할 것
이러한 내용은 AGENTS.md와 같은 문서나 프롬프트에 작성하는 것이 유용합니다. 하지만 AI는 언제나 완벽하게 지켜주지는 않습니다.
프롬프트는 제약이 아니다
예를 들어, Domain 계층에 다음과 같은 코드가 생성되었다고 가정해 봅시다.
namespace MyApp.Domain;
public class OrderService
{
...
참조되는 타입이 존재한다면, C# 컴파일러는 일반적으로 아키텍처상의 위반을 판단하지 않습니다. 구현으로서 동작하는 것과 설계상 허용되는 것은 별개의 문제입니다.
리뷰에만 의존할 경우, 바쁘거나 변경 사항이 많은 시기에는 놓칠 가능성이 있습니다. 사람이 작성했든 AI가 작성했든, 그 결과물은 같습니다.
'지켜라'고 말하는 대신, 검증한다
제가 목표로 하는 것은 다음과 같은 구성입니다.
AI / 개발자가 코드를 변경
|
v
...
AI 스스로가 '나는 규칙을 지켰다'라고 판단하는 것이 아닙니다. 프로젝트 자체가 독립적인 검증 메커니즘을 갖추는 것입니다.
C#의 경우 Roslyn Analyzer를 사용하여 의존 관계나 특정 코드 패턴을 진단할 수 있습니다. 이 진단을 에러로 취급하면, 위반 사항이 포함된 빌드는 실패하게 만들 수 있습니다.
3가지 OSS에 공통되는 생각
ArchitectureAnalyzer: 아키텍처를 컴파일 가능한 계약으로 만들기
ArchitectureAnalyzer는 JSON으로 선언한 아키텍처 계약을 Roslyn Analyzer로 검증하는 OSS입니다.
계층 간의 의존 관계, 금지된 API, 의존 그래프 등의 제약을 체크할 수 있습니다.
설계서에 'Domain 계층에서 외부 계층으로 의존해서는 안 된다'고 적는 것만으로는, 코드와 설계서가 점차 괴리될 수 있습니다. 규칙을 기계적인 진단으로 만듦으로써 이러한 불일치를 감지하기 쉽게 만들 수 있습니다.
PureSharp: 코드의 속성을 검증하다
PureSharp는 C#에서 함수형 프로그래밍과 관련된 제약을 도입하는 OSS입니다.
[PureMethod]
public static int Add(int a, int b) => a + b;
[PureMethod]가 붙은 메서드에 대해 지원 범위 내의 부작용을 진단합니다. 또한, 언더스코어(_)로 시작하는 로컬 변수의 재할당 금지나 FluentIf의 종료 조건 등도 다룹니다.
var _value = Calculate();
// _value = 100; // 재할당을 진단
다만, 모든 C# 코드의 순수성을 형식적으로 증명하는 것은 아닙니다. 검사 대상을 정의한 후, 그 계약을 검증하는 도구입니다.
PolicySharp: 명시적으로 허가된 것만 통과시키기
PolicySharp는 허가 목록(allow-list) 중심의 정책 엔진입니다.
금지 목록 방식은 금지 사항으로 나열되지 않은 새로운 API나 의존 관계를 놓칠 가능성이 있습니다. 따라서 정의되지 않았거나 모호한 접근도 거부하는 'default-deny'를 기본 원칙으로 합니다.
명시적으로 허가 → ALLOW
명시적으로 금지 → DENY
미정의/모호 → DENY
예를 들어, Domain 계층이 사용할 수 있는 네임스페이스를 선언합니다.
{
"version": 1,
"mode": "default-deny",
...
}
여기서 보여드린 것은 정책의 개념을 나타내는 예시입니다. 실제 적용 범위는 각 버전의 구현과 문서를 따릅니다.
AI가 규칙을 변경한다면?
사실, Analyzer를 도입하는 것만으로는 충분하지 않습니다.
AI가 금지된 API를 사용
↓
Analyzer가 에러 발생
...
이것만으로는 의미가 없습니다.
제약을 지켜 구현할 권한과, 그 제약 자체를 변경하는 권한은 분리되어야 합니다. 정책 파일, 프로젝트 설정, Analyzer 참조의 변경은 별도의 승인이나 CI 검증 대상이 되어야 합니다.
또한, Analyzer 자체에도 탐지 한계가 있습니다. 빌드나 CI를 거치지 않은 변경을 막을 수는 없습니다. '절대 불가능하게' 만드는 것이라기보다는, 제약을 무시한 변경이 일반적인 개발 흐름으로 통과하기 어렵게 만드는 생각입니다.
AI의 자유도와, 변경을 수용하는 조건은 분리한다
AI에게는 자유롭게 해결 방법을 생각해 주었으면 합니다. 한편으로는 프로젝트가 유지해 온 설계까지 마음대로 변경해도 좋다고 생각하지 않습니다.
AI에게 자유롭게 코드를 제안하게 하는 것과, 그것을 받아들이는 것은 별개입니다.
허용된 범위 내라면 어떻게 구현하든 상관없습니다. 허용 범위를 바꿀 필요가 있다면, 그것을 설계상의 의사결정으로 다루어야 합니다.
저는 이러한 분리가 AI를 활용한 지속적인 소프트웨어 개발에 중요하다고 생각합니다.
맺음말
프롬프트는 의도를 전달하는 것입니다. Analyzer는 검증 가능한 규칙 위반을 탐지합니다. CI와 승인 흐름은 그 검증을 건너뛰거나, 제약을 임의로 약화시키지 않기 위해 사용됩니다.
AI에게 '할 수 없음'을 학습시키는 것이 아니라, 개발 환경에 '할 수 없음'을 구현한다.
그를 위한 시도로 ArchitectureAnalyzer, PureSharp, PolicySharp을 개발하고 있습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기