개인 개발 과정에서 Claude Code와 같은 생성형 AI를 상당히 많이 사용하게 되었습니다. 편리합니다.
저 스스로는 조사하면서 몇 시간이 걸릴 것 같은 구현이라도, 요구사항만 전달하면 상당한 속도로 형태를 갖춰줍니다.
설계에 대해 물어보면 보안이나 확장성까지 고려한 '제대로 된' 구성을 제안해 줍니다.
이에 대해서 처음에는 단순히,
내 구현 능력이 몇 배로 늘어난 것
이라고 생각했습니다. 저는 이 만능감을 멋대로 AI 최강기라고 부릅니다. AI 하나로 무엇이든 할 수 있다는 감각이죠.
하지만 한동안 AI와 개발을 하면서, 조금 다른 감각을 갖게 되었습니다.
그것은, 사실 구현할 수 있게 된 것이라기보다, 자신의 이해 능력(理解能力)을 초과한 구현까지도 쉽게 자신의 시스템에 통합할 수 있게 된 것이 아닐까 하는 의문입니다.
이 두 가지 차이는 개발이 길어지고 시스템이 커짐에 따라 현저해지지 않을까요. 이번에는 그런 느낌의 에세이입니다.
코드나 기술 이야기는 적으니, 술을 마시면서 읽으시면 좋겠습니다.
예전에 학습용 웹 서비스에서 LINE으로 리마인더를 비동기 전송하는 시스템을 만들었습니다. 이때 채택한 것이 Google Cloud의 Pub/Sub였습니다.
스케줄러 측 처리에서 메시지를 publish하고, 그것을 Cloud Functions가 수신하여 LINE으로 전송합니다. 비동기 처리 구성으로는 자연스럽습니다.
실제로 작동했습니다.
문제가 된 것은 시간이 지난 후부터입니다. 주변 구조를 변경하려고 할 때,
- 왜 여기서 Pub/Sub이 필요했는지
- 누가 publish하고 있는지
- 누가 메시지를 수신하여 처리하는지
- 실패했을 때는 무슨 일이 일어나는지
- 현재 규모에서도 이 구성을 유지할 필요가 있는지
를 다시 추적해 볼 필요가 생겼습니다.
추적해 보니, 예를 들어 다음과 같은 것을 알게 되었습니다.
- publish에 성공한 시점에서 '전송 완료'로 기록했고, LINE에 실제로 도착했는지는 확인하지 않았습니다.
- 전송 처리에 관련된 요소들이 토픽(Topic), 수신용 함수(Function), 데드레터용 토픽(Dead Letter Topic), 전용 서비스 계정 및 권한 설정, 수신 측용 시크릿(Secret) 등 애플리케이션 외부에 분산되어 있었습니다.
구현했을 당시에는 '작동하는 시스템'이었지만, 나중에 보니 '쉽게 건드릴 수 없는 시스템'이 되어가고 있었습니다.
B급 공포 영화에서 자주 나오는, '어느새 헛간에 괴물이...' 같은 감각과 조금 비슷했던 것 같습니다.
결국 Pub/Sub을 거치지 않고 애플리케이션에서 LINE으로 직접 전송하는 방식으로 전환했습니다. 비동기 처리의 이점을 애초에 살리지 못하고, 실질적으로 복잡성만 늘리는 죽은 기능이었기에 제거한 경우입니다.
다만, 제거하는 것 역시 수고로움이 있었습니다. 문제가 생길 경우 구 구성으로 돌아갈 수 있도록 전환 후에도 한동안 기존 구성을 남겨두고 지켜보며, 철거는 단계적으로 진행했습니다. 만드는 것은 순식간이지만, 버리려면 절차가 필요합니다.
여기서 문제가 된 것은 AI가 잘못된 코드를 작성한 것이 아니었습니다.
나 혼자였다면 선택하지 않았을지 모르는 구성을 쉽게 만들 수 있었다는 점. 그 구성을 채택하는 동시에 필요한 이해와 판단의 기록을 충분히 쌓지 못했다는 것입니다.
그 결과, 이점만 얻지 못하고 나중 변경 시 구조를 재이해해야 하는 부담까지 떠안게 되었습니다.
적어도 제 경험에 대해서는 그렇게 파악하는 것이 가깝다고 생각합니다.
여기서 AI 코딩은 레버리지(Leverage)와 조금 비슷한 것이 아닐까 생각했습니다.
예전에는 스스로 구현하는 과정에서,
내가 이해할 수 있는 복잡성 ≒ 내가 구현할 수 있는 복잡성
이라는 제약이 어느 정도 있었습니다.
물론 AI 이전에도 충분히 이해하지 못한 라이브러리나 구성을 도입할 수는 있었습니다. 다만, 스스로 조사하고 설계하고 실패하고 고치는 과정에는 적지 않은 시간이 걸립니다.
그 사이에,
이거, 정말 필요한 건가?
라고 생각할 기회도 발생합니다.
그런데 AI를 사용하면,
내가 이해할 수 있는 복잡성 ≪ 나 자신 + AI가 구현할 수 있는 복잡성
이라는 상태를 쉽게 만들 수 있습니다.
말할 것도 없이, 이것은 엄청나게 편리합니다.
반면에 금융으로 치면, 적은 자기자본으로 큰 포지션을 가질 수 있는 상태와도 조금 비슷합니다. 평상시에는 매우 효율적입니다.
문제가 되는 것은 사양 변경, 장애, 보안 대응, 대규모 리팩토링 등이 필요할 때입니다.
그때 갑자기,
이 시스템을 안전하게 변경할 수 있는 범위까지 이해해 주세요. 그 후 발생하고 있는 문제를 해결해 주세요.
라는 청구서가 도착합니다.
물론, 그때도 AI는 큰 도움이 될 것입니다. 코드를 설명하게 하거나, 의존 관계를 조사하게 하거나, 구조 자체를 단순화하게 할 수도 있습니다.
하지만 변경된 것과 구조를 이해했다는 것은 같지 않습니다.
눈앞의 문제만 해결하고 그 변경이 무엇에 의존하는지 확인하지 않은 채 진행하면, 다음에도 똑같이 재차 이해해야 하는 필요가 남습니다.
게다가 다른 기능과의 의존 관계가 늘어나면 조사 범위도 넓어집니다. 청구액이 추가한 코드의 양에 비례한다고 할 수는 없다고 생각합니다.
결과적으로, AI로 미루어 둔 것은 시간이 지날수록 금액이 불어난 채 청구될 것이 아닐까요?
제 개발에서는 AI 덕분에 구현하는 데 걸리는 시간이 크게 줄었습니다.
- 코드 100줄 작성하기
- 새로운 추상화 추가하기
- 상태 관리 추가하기
- 별도 서비스 결합하기
예전 같으면 '귀찮으니 포기하자', '좀 더 검토한 후에 추가하자'라고 생각했던 것까지 쉽게 만들 수 있습니다.
일단 해보는 장벽이 크게 낮아진 것이라고 생각합니다.
하지만 일단 시스템에 들어간 코드에는,
- 테스트하기
- 디버깅하기
- 모니터링하기
- 변경하기
- 마이그레이션하기
- 삭제하기
라는 작업이 남아 있습니다.
이러한 작업을 수행하려면 정도의 차이는 있어도 이해가 필요합니다. 그리고 그 이해를 유지하거나 되찾는 것에도 비용이 듭니다.
유지보수(Maintenance)·운영(Operation)·변경·폐지에 드는 비용. 여기서는 이를 '존재 가격(存在価格)'이라고 부르겠습니다.
이러한 비용 자체는 AI 이전부터 있었습니다.
바뀐 것은, 구현 시의 부담이 작아져서 미래의 부담을 감수할지 판단하는 것까지 쉽게 할 수 있게 되었다는 점입니다.
가령 AI가 구현 비용을 10분의 1로 줄였다고 해도, 이 '존재 가격'까지 10분의 1이 될 것이라고는 장담할 수 없습니다.
오히려 만드는 마찰이 줄어들면서 미래의 부담을 충분히 생각하지 못한 채 복잡성을 추가하기 쉬워진 것은 아닐까. 최근에는 그런 생각을 하고 있습니다.
복잡성을 더할지 말지에 대한 판단은 보안 대책에서도 문제가 되었습니다.
최근 LINE을 사용한 이름 등록 처리를 수정했습니다. 등록용 URL이나 토큰에 PII(개인 식별 정보)를 포함하지 않도록 하는 수정입니다.
이러한 종류의 처리를 '일단' AI에게 검토하게 하면, 상당히 견고한 방안이 나오는 경우가 있습니다. 예를 들면,
- DB 관리용 원타임 토큰으로 만들어 한 번만 사용할 수 있게 하기
- 토큰을 암호화하기
같은 것입니다.
각각 특정 위협에 대해서는 합리적인 대책입니다. 암호화하면 토큰의 내용을 읽히지 않게 할 수 있고, DB 관리에서 원타임으로 만들면 그대로 사용되는 것을 막을 수 있습니다. 일반적으로는 병용이 바람직할 것입니다.
한편, 실제 시스템에는 다음과 같은 조건들이 있었습니다.
- 링크는 LINE 개별 토크에 전송되며 유효 기간은 1시간
- 링크로는 이름 등록만 가능하고, 이미 등록된 경우 재등록할 수 없음
- 링크를 얻더라도 웹 서비스 로그인까지 이르지 않음
- 서비스는 소규모
이것들은 링크가 유출되지 않는다는 것, 또는 접근한 사람이 본인이라는 것을 보장하는 조건은 아닙니다. 다만, 예상되는 위협이나 피해 범위의 재료로는 될 수 있습니다.
만약 원타임 토큰을 DB에서 관리한다면, 생성·저장·소비·기한 만료·삭제·예외 처리·테스트까지 포함된 시스템을 앞으로도 계속 유지보수해야 합니다. 새로 추가된 상태 관리에 결함이 생길 가능성도 있습니다.
이는 암호화에 대해서도 마찬가지입니다. 가령 토큰을 암호화한다면, 키를 어디에 보관하고 어떻게 로테이션하며, 키를 잃어버리거나 유출했을 때 어떻게 할지라는 키 관리까지 책임져야 합니다.
암호화는 넣기만 해서 완성되지 않습니다. 키 관리가 미흡한 상태에서 '암호화했으니까 괜찮다'라고 생각하고 토큰에 담을 정보를 늘려버리면, 키가 유출되었을 때의 피해는 암호화하지 않았을 때보다 커질 수 있습니다.
결국, URL이나 토큰에서 LINE User ID를 제거하고 내부 학생 ID로 참조하며, 학생의 존재나 유효 상태, 조직과의 정합성은 서버 측에서 확인하는 형태로 했습니다. DB 관리용 원타임 토큰은 이번에는 보류하고, 수용한 리스크와 어떤 변화가 있을 때 방식을 재검토할지 기록했습니다.
여기서 필요했던 것은,
이 대책이 예상되는 위협에 대해 유효한가?
라는 판단과,
그 효과와 맞바꾸어 시스템에 복잡성을 추가할 가치가 있는가?
라는 판단이었습니다.
후자에는 구현할 수 있는지 여부뿐만 아니라, 그 시스템을 앞으로도 계속 다룰 수 있을지라는 존재 가격의 문제가 들어옵니다.
그렇다고 해서,
AI가 과도하게 구현하기 때문에, 항상 단순한 것을 선택해야 한다는 이야기가 되지는 않습니다.
또한,
AI가 제시한 것은 1부터 100까지 반드시 검증해야 한다는 이야기로도 가지 않을까 생각합니다. 적어도 저의 개발 과정에서는 이를 철저히 할 경우, AI를 사용하는 이점의 상당 부분이 사라집니다.
그리고 AI의 제안 자체가 그렇게 확고하지 않은 경우도 있습니다.
앞서 예시에서 보안에 대해 AI가 견고한 안을 제시했을 때, 인간 측에서,
'규모가 작으니 그 정도는 필요하지 않을 것 같은데요?'
'공격당해도 영향이 제한적이지 않을까요?'
라고 지적하면, 이번에는 AI가 상당히 쉽게 우리 쪽으로 기울어지는 경우가 있습니다.
그것 자체도 무섭습니다.
원래 남겨둬야 할 방어까지 '그 조건이라면 필요 없네요'라며 제거해 버릴 가능성이 있기 때문입니다.
결국,
AI를 엄격하게 할지, 느슨하게 할지
라는 단일 축이 아니라,
- 위협은 무엇인가
- 발생하면 무엇이 일어나는가
- 발생 가능성을 어떻게 추정할 것인가, 그 근거는 무엇인가
- 대책은 그 위협에 대해 유효한가
- 대책의 구현 및 운영에는 얼마나 드는가
에 더하여,
- 그 시스템을 미래에도 계속 유지보수할 수 있는가
까지 포함해서, 인간 측에서 판단해야 하는 것 같습니다.
AI의 제안을 채택하든 거부하든 이유가 필요합니다.
AI를 사용하지 않는 개발로 돌아갈 생각은 없습니다. AI가 없었다면 혼자서는 만들 수 없었던 것이 많이 있습니다.
그래서 저에게 있어 문제는,
어디까지 레버리지를 걸 것인가
인 것 같습니다.
최근에는 AI의 제안을 볼 때,
이 구현이 옳은가?
뿐만 아니라,
이를 3년 후의 내가 안전하게 변경할 수 있는가?
내가 사라진 후에도, 인계받은 사람이 안전하게 변경할 수 있는가?
까지 생각하게 되었습니다.
코드를 모두 직접 쓸 필요는 없습니다. 내부 구현을 완전히 암기하고 있을 필요도 없습니다.
다만,
- 왜 존재하는지
- 무엇을 지키고 있는지
- 어디가 부서지기 쉬운지
- 변경하면 무엇에 영향을 주는지
- 불필요해졌을 때 버릴 수 있는지
정도 설명할 수 있는 상태로 유지하고 싶습니다.
운영을 통해 이해가 깊어지는 경우도 있습니다. 반면, 설계 이유를 기록하지 않고 그 부분에 접할 기회조차 없다면, 당시의 이해는 희미해집니다.
따라서 앞으로는 새로운 시스템을 채택할 때, 채택 이유, 지켜야 할 조건, 실패 시 동작 방식, 폐기 가능한 조건을 짧게 남기고 싶습니다.
코드를 다시 읽어보기 전에 판단의 출발점을 되찾기 위해서입니다.
AI 덕분에 자신의 능력 이상의 것을 만들 수 있게 되었습니다. 이것은 분명 큰 장점입니다.
다만, 그 능력을 빌릴 때마다 눈에 보이지 않는 부채까지 함께 빌리고 있을 가능성이 있습니다.
청구서는 오지 않을 수도 있고, 3년 후에 갑자기 올 수도 있습니다.
금액도 알 수 없습니다. AI에게 맡겨서 5분 만에 해결될 수도 있지만, 척추를 뽑아서 교체하는 큰 수술이 될 수도 있습니다.
혹은 장애라는 형태가 아니라,
- 무서워서 이 코드를 건드릴 수 없다
- 건드린다 해도 매번 신중하게 조사하지 않으면 변경할 수 없다
- 결과적으로, 이 컴포넌트 주변은 항상 시간이 걸린다
와 같은 작은 이자를 매달 계속 지불할 수도 있습니다.
그래서 현재 저 나름의 결론은 상당히 소박합니다.
AI에게 만들게 하는 것뿐만 아니라, AI가 만든 것을 우리가 계속 소유할 수 있을지 생각하는 것.
여기서 말하는 '소유'는 코드가 손에 있다는 것이 아니라, 변경/운영/폐지에 대해 판단할 수 있는 것입니다.
AI 코딩에 익숙해진 지금에 와서야 비로소 그 부분이 신경 쓰이기 시작했습니다.