AI는 코딩을 잘하는데, 왜 서비스는 못 만들까?

AI가 코드를 만든 뒤 떠나는 경우와 서비스 안에 남는 경우는 확률이 작동하는 위치부터 다릅니다. 반복되는 실패를 막는 구조를 살펴봅니다.

같은 날짜 추출 요청을 다섯 번 실행했을 때 결정론적 코드는 같은 형식을 유지하고 AI는 한 번 다른 형식을 내는 비교
같은 입력에서 결정론적 코드와 확률적 AI 출력이 갈리는 모습

랜딩페이지까지는 정말 쉽다

쉬웠던 이유와 어려워지는 이유는 같은 곳에 있다

AI에게 랜딩페이지를 시켜봤다면 안다. 몇 분 만에 그럴듯한 화면이 나오고, 문구를 바꿔달라면 바꿔주고, 색을 바꿔달라면 바꿔준다. 이쯤 되면 자연스럽게 생각하게 된다. 개발, 별거 아니네.

그런데 로그인을 붙이고, 결제를 붙이고, 데이터를 저장하기 시작하면 어느 순간부터 이상해진다. 한쪽을 고치면 다른 쪽이 깨지고, AI도 나도 전체가 어떻게 돌아가는지 설명하지 못하게 된다. 왜 그런지는 앞 글에서 다뤘다. 부품은 하나씩 늘지만 부품 사이의 연결은 폭발적으로 늘어나서, 어느 지점부터 사람의 인지가 전체를 담지 못하게 된다는 것.

그런데 AI로 만들 때는 여기에 층이 하나 더 얹힌다. 연결이 폭발하는 것도 그대로인데, 부품 자체가 확률로 움직인다. 이 글은 그 새 층에 대한 이야기다.

매번 같은 답을 주는 기계, 대체로 맞는 답을 주는 기계

개발자들은 앞의 것을 결정론적, 뒤의 것을 확률적이라고 부른다

계산기에 1 더하기 1을 넣으면 언제나 2가 나온다. 어제도 2, 오늘도 2, 백만 번째도 2다. 규칙이 정해져 있어서 같은 것을 넣으면 언제나 같은 것이 나오는 기계. 지금까지의 소프트웨어는 전부 이렇게 만들어졌다. 심지어 오류가 나도 그 오류조차 매번 똑같이 난다.

AI는 다르게 작동한다. 정해진 규칙을 따라가는 게 아니라, 가능한 답 후보들 가운데 가장 그럴듯한 것을 고른다. 그래서 대체로 맞는다. 문제는 "대체로"라는 말에 있다. 위의 그림처럼, 똑같은 요청을 다섯 번 하면 한 번쯤 형식이 다른 답이 나온다. 그날의 답이 조금씩 흔들린다.

사람끼리라면 아무 문제가 없다. "2026-08-28"이든 "08/28/2026"이든 알아서 알아듣는다. 하지만 소프트웨어는 그렇지 않다. 앞 글에서 봤듯 소프트웨어는 부품 하나만 틀려도 전체가 멈출 수 있는 물건이다. 99.9% 정확한 시스템이 0% 작동할 수 있다. 이 성질과 "대체로 맞는 부품"이 만나면 무슨 일이 생기는지, 이제 숫자로 보자.

천 번에 한 번의 실수는, 만 번이면 열 번쯤 된다

확률은 "믿을 만한가"가 아니라 "몇 번 반복되는가"의 문제다

실수할 확률이 0.1%라고 하면 거의 완벽하게 들린다. 천 번에 한 번이니까. 그런데 서비스는 같은 일을 무섭게 반복한다. 하루에 300번 쓰이는 기능이면 한 달에 9,000번이다. 0.1%씩 9,000번이면, 한 달에 아홉 번쯤은 틀린다는 뜻이 된다.

아래에서 직접 움직여보자. 부품의 신뢰도를 고르고 반복 횟수를 늘리면, 한 번이라도 실패할 확률이 어떻게 변하는지 볼 수 있다.

반복 횟수와 부품 신뢰도를 직접 바꿔 실패 확률을 계산하는 기능
반복 횟수와 부품 신뢰도를 직접 바꿔 실패 확률을 계산하는 기능

슬라이더를 끝까지 밀어보면 알게 된다. 신뢰도가 얼마든, 반복이 쌓이면 실패는 사실상 확정된다. 확률적인 부품에게 "믿을 만한가"라고 묻는 것은 질문이 잘못됐다. 물어야 할 것은 "몇 번 반복되는가"와 "한 번 틀리면 무슨 일이 생기는가"다.

틀려도 되는 일이 있다. 초안을 쓰고, 아이디어를 내고, 요약을 하는 일은 열 번에 한 번 이상해도 사람이 걸러내면 그만이다. 하지만 결제, 송금, 재고 차감, 계약처럼 단 한 번의 실패도 곤란한 일이 있다. 상용 서비스는 대부분 후자를 품고 있고, 그 자리에 "대체로 맞는 부품"을 그대로 꽂을 수는 없다.

고치면 끝나는 오류, 고쳐도 돌아오는 오류

결정론적 오류는 재현되기 때문에 고칠 수 있고, 고친 것이 쌓인다

결정론적인 소프트웨어도 당연히 오류가 난다. 복잡하게 얽힌 규칙들 어딘가에 틀린 규칙이 숨어 있는 것이다. 그런데 이 오류에는 결정적인 특징이 있다. 같은 입력을 넣으면 같은 오류가 다시 난다. 재현이 되면 원인을 좁혀갈 수 있고, 틀린 규칙을 찾아 고치면 그 오류는 다시는 생기지 않는다. 고친 것이 고쳐진 채로 남는다. 그래서 결정론적 시스템의 품질은 계단처럼 쌓여 올라간다.

확률적인 실패는 성질이 다르다. 어제 이상한 답을 냈던 요청을 오늘 다시 넣으면 멀쩡한 답이 나온다. 재현이 안 되니 원인을 좁힐 수가 없다. 더 나쁜 것은 고치는 과정이다. 어제의 실패를 잡으려고 지시문을 고치면, 멀쩡하던 다른 경우가 조용히 깨진다. 하나를 잡으면 다른 하나가 새는데, 어디가 새기 시작했는지 알려주는 사람도 없다. "고쳤다"는 확신을 가질 방법 자체가 없는 것이다.

그림 · 두 시스템의 품질이 쌓이는 방식 (세로축은 시스템의 안정성)
그림 · 두 시스템의 품질이 쌓이는 방식 (세로축은 시스템의 안정성)

확률적인 출력을 사람이 통제하기 어렵다고 할 때, 그 "통제"의 정체가 이것이다. 통제란 결국 재현하고, 원인을 찾고, 고친 상태를 유지하는 능력이다. 결정론은 이 세 가지를 기본으로 주고, 확률은 세 가지 모두를 주지 않는다.

잘하는 일도 이어 붙이면 무너진다

기능은 단계의 연결이고, 확률은 단계를 지날 때마다 깎인다

"기능 하나"는 사실 여러 단계의 연결이다. 주문 메일에 자동으로 답장하는 기능을 뜯어보면, 메일을 읽고, 주문 항목을 뽑고, 재고를 확인하고, 가격을 계산하고, 답장을 쓰는 다섯 단계쯤 된다. 각 단계를 AI가 99% 확률로 해낸다고 하자. 꽤 훌륭한 성적이다. 그런데 다섯 단계를 통과해 끝까지 다 맞을 확률은 95%로 내려가고, 스무 단계면 82%가 된다. 열 번에 두 번은 어딘가 틀린 답장이 나간다는 뜻이다.

그림 · 단계가 늘어날 때 끝까지 성공할 확률
그림 · 단계가 늘어날 때 끝까지 성공할 확률

결정론적인 코드는 한 단계의 신뢰도가 사실상 100%라서, 백 단계를 이어 붙여도 무너지지 않는다. 소프트웨어를 수만 개의 부품으로 쌓아 올릴 수 있었던 것은 이 성질 덕분이다. 확률적인 부품은 반대다. 연결할수록 지수적으로 무너진다.

"기능을 붙일수록 어려워지는" 경험의 정체가 여기서 드러난다. 앞 글의 문제(부품이 늘면 연결이 폭발한다)와 이 글의 문제(단계가 늘면 확률이 깎인다)가 동시에 오는 것이다. 랜딩페이지가 쉬웠던 건 단계가 거의 없는 일이었기 때문이다.

AI가 코드를 쓰고 떠나느냐, 서비스 안에 남느냐

"AI로 개발한다"는 말에는 확률이 사는 곳이 다른 두 가지가 섞여 있다

여기까지 읽으면 이상한 점이 하나 생긴다. 확률이 그렇게 위험하다면, AI가 코드를 짜준 내 랜딩페이지는 왜 멀쩡히 잘 돌아갈까?

답은 확률이 언제 존재하느냐에 있다. AI가 코드를 써주는 경우, 확률은 코드를 만드는 그 순간에만 존재한다. 일단 나온 코드는 결정론적인 규칙 덩어리다. 테스트를 돌리고, 실행해보고, 검증을 통과시키는 순간 확률은 그 자리에서 소멸하고, 서비스에는 결정론적인 부품만 남는다. 주사위를 한 번만 굴리고, 잘 나온 결과를 고정해서 계속 쓰는 것이다.

반대로 AI를 서비스 안에 넣는 경우, 다시 말해 사용자의 요청이 들어올 때마다 AI를 호출하는 구조라면, 주사위가 서비스 안에 상주한다. 매 요청이 새로운 시행이고, 앞에서 본 산수(반복이 쌓이면 실패는 확정된다, 단계를 이으면 지수적으로 깎인다)가 고스란히 적용된다.

그림 · 확률(주사위)이 사는 위치
그림 · 확률(주사위)이 사는 위치

랜딩페이지가 쉬웠던 이유는 구조 A 중에서도 가장 쉬운 경우였기 때문이다. 결과가 눈에 다 보여서, 화면을 열어보는 것만으로 검증이 끝난다. 그러나 구조 A라고 다 안전한 것은 아니다. 검증 없이 AI가 짠 코드를 그대로 쓰면, 확률을 고정한 게 아니라 검증 안 된 주사위 결과를 배포한 것이다. 돌아가긴 하는데 왜 돌아가는지 모른다면, 확률은 소멸한 게 아니라 코드 속에 숨어 있다.

그리고 구조 B가 필요한 순간은 반드시 온다. 자연어를 이해하고, 요약하고, 분류하는 일은 규칙으로는 쓸 수 없어서 AI를 서비스 안에 넣을 수밖에 없다. 그래서 진짜 질문은 "AI를 쓸까 말까"가 아니라 이것이다. 확률적인 부품을 품고도 어떻게 결정론적인 서비스처럼 굴게 만들 것인가.

좁히고, 가두고, 밀어낸다

상용 AI 서비스를 만드는 팀들의 기법은 결국 이 세 동작의 변주다

확률을 다루는 구체적인 기법에는 이름이 많다. structured output, 스키마 검증, 가드레일, function calling, 평가셋. 이름은 많지만 하는 일은 세 가지로 묶인다.

좁히기 · 답할 수 있는 공간을 줄인다

"날짜를 알려줘"라고 하면 AI는 어떤 형식으로든 답할 수 있다. 대신 "YYYY-MM-DD 형식으로만 답하라", "이 세 가지 보기 중에서만 골라라"처럼 답의 공간 자체를 좁힌다. 형식을 강제하는 기능(structured output, enum)이 이 동작이다. 답의 공간이 좁아질수록 다음 동작, 즉 기계적인 검사가 가능해진다.

가두기 · 출력이 검사를 통과해야만 나가게 한다

AI의 출력을 최종 결과로 쓰지 않는다. 결정론적인 검사기(형식이 맞는가, 값이 범위 안인가, 합계가 맞는가)를 통과한 것만 다음 단계로 보내고, 실패하면 다시 시도시킨다. 실행 권한도 주지 않는다. AI는 판단과 추출만 하고, 데이터를 실제로 바꾸는 일은 결정론적인 코드가 한다. 틀리면 곤란한 자리에서는 AI의 출력을 확정이 아니라 제안으로 두고 사람이 마지막 확인을 한다.

밀어내기 · 확률을 런타임에서 빌드 타임으로 옮긴다

같은 일을 매번 AI에게 시키는 대신, 그 일을 하는 코드를 AI에게 짜게 하고, 검증한 뒤, 그 코드를 반복 실행한다. 주사위를 만 번 굴리는 구조를 한 번만 굴리는 구조로 바꾸는 것이다. 그래도 서비스 안에 남겨야 하는 AI에게는 평가셋(잘 만든 시험 문제 묶음)을 만들어, 지시문을 고칠 때마다 시험을 다시 치르게 한다. 04에서 잃어버린 "고친 것이 유지된다"는 성질을 인공적으로 복원하는 것이다.

그림 · 가두기의 기본 형태, 검증 루프
그림 · 가두기의 기본 형태, 검증 루프

덧붙이자면, 설정을 바꿔서(같은 답만 내도록 무작위성을 0으로 놓아서) 해결하면 되지 않냐는 질문이 자주 나온다. 그렇게 하면 같은 답이 나올 확률이 높아질 뿐이고, 모델이 업데이트되는 순간 답은 통째로 바뀐다. 결정론은 설정에서 오는 게 아니라 구조에서 온다. 위의 세 동작이 전부 설정이 아니라 구조인 이유다.

사람도 확률적인 부품이다

확률적인 부품으로 결정론적인 구조를 만드는 일, 인류는 이미 해봤다

여기서 반론이 하나 나온다. "사람도 실수하는데? 사람이 하던 일을 AI가 하는 것뿐이잖아." 맞는 말이다. 사람은 확률적인 부품이다. 그런데 이 반론은 AI를 변호하는 게 아니라, 오히려 답이 어디에 있는지 알려준다.

인류는 확률적인 부품(사람)으로 결정론에 가까운 시스템을 만드는 법을 아주 오래 발명해왔다. 조종사의 체크리스트는 판단의 공간을 좁힌 것이다. 은행의 이중 확인과 결재선은 한 사람의 출력이 검사를 통과해야만 실행되게 가둔 것이다. 매뉴얼과 자동화는 반복되는 판단을 규칙으로 고정해서, 사람의 확률을 시스템 밖으로 밀어낸 것이다. 항공, 의료, 금융처럼 한 번의 실수가 치명적인 산업이 전부 이 세 동작 위에서 굴러간다.

그러니 "확률적인 출력으로 결정론적인 구조를 만들 수 있는가"라는 질문의 답은 이렇다. 가능하다. 이미 해봤다. AI 안정화 기법들은 인류가 사람을 상대로 쌓아온 그 구조의 재발명이다.

다만 공짜가 아니다. 어디에 확률을 허용하고 어디를 결정론으로 지킬지 정하는 일, 검사기와 시험 문제와 실패했을 때의 경로를 설계하는 일. 그 구조를 만드는 것이 바로 개발이고, AI가 대신해주지 않는 부분이다. 랜딩페이지가 쉬웠던 것은 이 설계가 필요 없는 일이었기 때문이고, 상용 서비스가 어려운 것은 이 설계가 일의 전부이기 때문이다. AI로 무엇이든 만들 수 있는 시대는 맞다. 단, 만들 수 있다는 말의 기준이 "한 번 돌아간다"가 아니라 "십만 번째에도 돌아간다"가 되는 순간, 진짜 개발이 시작된다.

함께 읽기 · 소프트웨어는 왜 복잡할까?