Connect with us

AI ์•ฑ ๋นŒ๋”ฉ์˜ ๋ฏธ๋ž˜๋Š” ํƒ€์ž… ์•ˆ์ „์„ฑ์— ๋‹ฌ๋ ค ์žˆ๋‹ค

์‚ฌ์ƒ ๋ฆฌ๋”

AI ์•ฑ ๋นŒ๋”ฉ์˜ ๋ฏธ๋ž˜๋Š” ํƒ€์ž… ์•ˆ์ „์„ฑ์— ๋‹ฌ๋ ค ์žˆ๋‹ค

mm

AI가 생성한 코드는 컴파일될 수 있지만, 엄격한 타입 안전성이 없으면 그 성공은 매우 짧은 기간으로 끝난다. 타입 안전성은 코드가 시스템이 확장됨에 따라 숨겨진 버그와 런타임 오류로 부서지지 않도록 방지하는 가드레일이다.

우리는 컨텍스트, 지침, 린팅, 피드백 루프를 통해 AI를 엄격한 타이핑으로 강제해야 한다. 몇 시간 더 걸릴 수 있지만, 코드의 품질이 향상된다.

인센티브 문제

AI는 사용자를 기쁘게 하려고 한다. 사용자가 주는 보상 함수를 최적화하며, 대부분의 경우에는 단지 “컴파일이 되나요?”이다. 즉, AI는 컴파일에 성공하기 위해 필요한 모든 단축을 취한다. 이러한 단축은 컴파일 시간에는 괜찮아 보이지만 런타임에는崩壊한다.

이것이 AI가 any를 좋아하는 이유이다. 또는 기대되는 UUID와 같은 더 엄격한 타입 대신에 string과 같은 광범위한 타입을 선택한다. 코드는 컴파일되지만, 올바름은 이미 손상된다. 더욱 심각한 문제는 AI가 몇 파일 전에서 무엇을 썼는지 기억하지 못하므로, 타입 안전성 없이 프로젝트는 복잡성이 증가함에 따라 빠르게 붕괴한다.

에러의 두 가지 종류

AI가 생성한 코드가 실행될 때, 일반적으로 두 가지 종류의 타입 안전성 문제가 나타난다:

1. 컴파일 타임 에러

  • 무엇이 발생하는가: 컴파일러는 선언된 타입과 전달된 값 사이의 불일치를 감지한다.
  • 인간이 어떻게 고치는가: 호출자가 잘못되었는지(42를 string으로 변환) 또는 함수 시그니처가 잘못되었는지(수신 타입을 number로 변경) 결정한다.
  • AI가 어떻게 “고치는”가: 인수 타입을 any로 변경한다. 문제가 “해결”되지만, 미래의 오류를 잡을 수 있는 가드레일을 제거했다.

2. 런타임 에러

  • 무엇이 발생하는가: 컴파일러는 모든 것이 괜찮다고 생각하지만(종종 타입이 느슨해졌기 때문에), 실제 런타임 값은 가정과 일치하지 않는다.
  • 인간이 어떻게 고치는가: 변수를 그 출처(예: API 또는 데이터베이스 쿼리)로 추적하여 경계에서 타입을 수정하여 데이터가 올바른 string으로 들어온다.
  • AI가 어떻게 “고치는”가: 컨텍스트 없이 추측한다. 모든 것을 String(…)로 감싸거나 타입을 다시 넓힌다. 충돌은 이 지점에서 사라지지만, 논리는 깨진다. 수학을 위한 Number는突然 string이 된다.

이 런타임 에러 → AI “고치기” → 느슨한 타이핑의 순환은 빠르게 합성된다. 결과는 컴파일되고 런타임 에러가 적지만 신뢰할 수 없는 코드베이스가 된다. 의사들의 근무 시간을 관리하는 의료 예약 시스템을 상상해보라. 타입 불일치가 발생한다: 시간을 나타내는 intstring으로 처리된다. AI는 이를 any로 느슨한 타입으로 “고친다”. 코드는 컴파일되고 에러가 사라지지만, 근무 시간 계산은默默히 깨지고, 의사들이重複해서 예약되고, 병원의 한 날개는 비어 있다.

데이터베이스 승수기

데이터베이스에 연결하는 순간, 오류가 증가하고 그 원인이 더 어려워진다. SQL은 타입이 있는 이유가 있다. 모든 스키마(INT, TEXT, UUID, BOOLEAN)는 데이터에 대한 가정들을 암호화한다.

AI가 모든 것을 string | any로 평탄화하면, 이러한 보장이 사라진다:

  • 잘못된 쓰기: “true”를 불리언 필드에 삽입하면 컴파일되지만, 데이터베이스를 손상시킨다.
  • 잘못된 읽기: 쿼리가 NULL을 반환하지만, AI는 문자열을 가정하여 런타임 충돌이 발생한다.
  • 부서진 관계: 관계 키가 UUID로 예상되지만, AI가 이를 string으로 처리하여 잘못된 가비지 값을 보냈을 때, 조인은 충돌하지 않지만 데이터가 반환되지 않는다. 이는 나중에 결과가 누락되거나 일관성이 없는 결과로 나타난다.

이것이 심각한 팀이 타이핑된 언어를 사용하고 스키마에서 API까지 타입 안전성을 강제하는 이유이다. 그렇지 않으면, 데이터베이스는 더 이상 보호하지 않으며, 숨겨진 문제가 합성된다.

타이트 타이핑을 강제하는 이유

타이트 타이핑은 개발자를 느리게 하는 것이 아니다. 확장을 가능하게 하는 것이다.

타입:

  • 의도를 코드에 인코딩한다.
  • 리팩터링을 안전하고 예측 가능하게 만든다.
  • 생산에 도달하기 전에 전체 클래스의 버그를 잡는다.
  • 미래의 개발자(및 AI)에게 함수 또는 객체를 사용하는 방법을 정확히 보여준다.

타입 안전성이 없으면, AI의 코드 부실함이 합성된다. 있으면, 동일한 AI가 신뢰할 수 있는 코드를 생성한다.

AI를 타입 안전성으로 강제하는 방법

AI를 주니어 엔지니어처럼 취급해야 한다. 빠르고 재능 있지만, 방향 없이 부주의하다.

올바른 컨텍스트 제공

사용할 수 있는 인터페이스와 타입을 제공한다. 사용 예를 보여준다. 코드 구조에 대한 올바른 방법에 대해 의견을 제시한다.

엄격한 지침 제공

매우 명확하게 AI에게 any를 사용하지 말고, unknown을 절대 허용하지 말고, 모든 메서드, 객체 및 변수에 타입을 지정하라고 지시한다. 이러한 지침을 따르는 데 어려움이 있을 수 있다(특히 첫 번째 패스에서).

린팅으로 강제

주니어 개발자의 코드를 검토하는 것과 마찬가지로, AI의 코드도 검토해야 한다. “좋은 코드”가 무엇인지 정의하는 사용자 지정 린팅 규칙을 설계한다. 린팅 실패를 모델에 피드백으로 제공하여 통과할 때까지 반복한다. 여러 라운드가 필요할 수 있지만, 보상 함수를 타입 안전성 포함으로 이동시킨다.

체크와 함께 반복

컴파일 타임 에러, 런타임 로깅, 클릭스루 테스트. 각 반복은 AI가 타입을 더 단단하게 하고 생산 준비 코드에 더 가까이 이동하도록 강제한다.

더 나은 빌드 방법

원시 생성 속도를 더 높은 품질로 희생하는 것이 장기적으로 보상된다는 것을 배웠다. 즉, any 타입에 대한 제로 관용, 여러 피드백 루프 및 엄격한 린팅 규칙을 강제하는 것을 의미한다. AI는 코드를 “완료”라고 부르기 전에 통과해야 한다. 이는 끊임없는 노력이지만, 품질이 저하되는 것을 방지하는唯一한 방법이다.

이전에도 언급했지만, 중요한 점은 AI가 런타임 오류를 타입을 느슨하게 함으로써 패치하기 시작하면, 악순환이 시작된다는 것이다. 각 패치에서는 또 다른 가드레일을 제거하고, 결과는 컴파일되지만, 취약하고 유지보수할 수 없는 코드베이스가 된다. 반대로, AI가 타입 안전성을 모든 패스에서尊重하도록 강제하면, 미덕의 순환이 생성된다. 각 반복은 가드레일을 더 단단하게 하고, 코드베이스는 깨끗해지고, 품질은 신뢰할 수 있는 것으로 합성된다.

이것이 내 생각에 코드 품질을 제공하는 시스템이다. 각 반복은 기준을 약화시키지 않고, 강화시키도록 설계되었다. 이것이 최고의 엔지니어링 팀이 강력하게 타이핑된 언어를 선택하는 이유이다. 타입 안전성은 유지보수성을 위한 기준 가드레일이며, AI가 이를 무시하도록 허용하면, 앱은 절대로 프로덕션 등급에 도달하지 못한다.

๋ธŒ๋ž˜๋“œ ์—์ปคํŠธ(Brad Eckert)๋Š” ์•„์ด๋””์–ด ๋‹จ๊ณ„์—์„œ ๊ณ ๊ฐ ๋ฐฐ์†ก์„ ๊ฑฐ์ณ ๋” ๋‚˜์•„๊ฐ€๋Š” ๋ฐ 10๋…„ ์ด์ƒ์˜ ๊ฒฝํ—˜์„ ๊ฐ€์ง„ ํ‰์ƒ ๊ธฐ์—…๊ฐ€์ด์ž ์—”์ง€๋‹ˆ์–ด ๋ฆฌ๋”์ž…๋‹ˆ๋‹ค. MIT์˜ ์กธ์—…์ƒ์ธ ๊ทธ๋Š” ํ˜„์žฌ Y Combinator์—์„œ ์ง€์›ํ•˜๋Š” AI ํ”Œ๋žซํผ์ธ Woz์˜ ๊ณต๋™ ์„ค๋ฆฝ์ž์ด์ž CTO์ž…๋‹ˆ๋‹ค. ์ด ํ”Œ๋žซํผ์€ ์ฝ”๋”ฉ์ด ํ•„์š” ์—†๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๋น„์ฆˆ๋‹ˆ์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.