illusion of knowing — fiction 01
45%는 취약하다
바이브 코딩으로 SaaS를 만들어 투자까지 유치한 스타트업 대표.
보안 감사가 시작되자, 그가 만든 것이 소프트웨어가 아니었음이 드러난다.
Part I
"딸깍의 기적"
2026년 3월 12일 수요일 오후 2시. 서울 강남 삼성동. 데모데이.
현우는 무대 위에서 스크린을 가리켰다. 태스크브릿지. 프로젝트 관리 SaaS. 칸반 보드, 타임라인, 자동 리포트, Slack 연동. 스크린에는 깔끔한 대시보드가 떠 있었다. 디자인은 Tailwind CSS 기반이었고, 반응형은 완벽했고, 데이터는 실시간으로 움직이고 있었다.
"4주 만에 혼자 만들었습니다."
객석에서 웅성거리는 소리가 났다. 투자자 12명. 심사위원 3명. 현우는 마이크를 잡고 계속했다.
"저는 개발자가 아닙니다. 기획자 출신입니다. 작년까지 PM으로 일하다가 퇴사하고, Cursor와 Claude로 코드를 짰습니다. 백엔드는 Node.js, 프론트는 React, 인프라는 AWS. 전부 AI가 만들어줬고, 저는 방향을 잡았습니다."
현우는 프롬프트 하나를 보여주었다.
프롬프트 하나로 인증 시스템이 나왔다. 파일 8개, 함수 23개, 테스트 코드까지. 현우는 "Accept All"을 눌렀다. 코드가 에디터에 채워졌다. 터미널에 npm start를 치면 서버가 올라갔다. 로그인 화면이 떴다. 작동했다.
다음 프롬프트. 결제 시스템. 다음 프롬프트. 대시보드. 다음 프롬프트. 이메일 알림. 하나씩, 딸깍딸깍, 프롬프트를 쓰고 "Accept All"을 누르는 것만으로 소프트웨어가 만들어졌다.
"Y Combinator W25 배치의 25%가 코드베이스의 95%를 AI로 작성했습니다. 저도 그중 하나가 될 수 있다고 생각했습니다."
객석에서 몇 명이 고개를 끄덕였다. 2026년, 논-개발자 파운더가 AI로 MVP를 만드는 것은 더 이상 신기한 일이 아니었다. 하지만 데모데이에서 실제 작동하는 제품을 보여주는 것은 여전히 인상적이었다.
데모가 끝났다. 투자자 3팀이 미팅을 요청했다. 2주 뒤, 시드 투자 5억 원이 확정되었다. 현우는 사무실로 돌아오며 생각했다.
4주, 프롬프트 347개, 코드 2만 줄. 내가 직접 타이핑한 줄 수는 아마 200줄도 안 될 것이다. 나머지는 전부 AI가 짰다. 그리고 그것으로 5억을 유치했다.
현우의 자신감은 최고조였다. 소프트웨어를 만드는 데 컴퓨터 공학 학위가 필요한 시대는 끝났다. 필요한 것은 좋은 문제 정의와 올바른 프롬프트뿐이다. 그렇게 믿었다.
3월의 마지막 주. 투자금이 입금되었다. 그리고 투자사에서 한 가지 요구가 왔다.
보안 감사. 현우는 그 단어를 읽고 잠시 멈췄다. 그리고 "네, 진행하겠습니다"라고 답했다. 코드는 AI가 잘 짰다. 걱정할 이유가 없었다.
Part II
"감사 보고서"
2026년 4월 7일 월요일 오전 10시. 은채는 태스크브릿지 레포지토리를 클론했다.
$ cloc src/
Language files code
───────────────────────────────────
JavaScript 147 18,342
TypeScript 23 1,891
JSON 12 847
───────────────────────────────────
Total 182 21,080
2만 1천 줄. 4주 만에 논-개발자가 만든 분량으로는 많다. 은채는 커피를 한 모금 마시고 코드를 열었다. 파일 구조부터 보았다. src/ 아래에 auth/, payment/, dashboard/, notification/. 폴더 구조는 깔끔했다. 파일 이름도 합리적이었다. AI가 만든 코드의 특징이다. 표면은 언제나 깔끔하다.
은채는 인증 시스템부터 열었다. auth/controller.js.
const jwt = require('jsonwebtoken');
const JWT_SECRET = 'taskbridge-secret-key-2026-do-not-share';
exports.login = async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || user.password !== password) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// ...
은채의 손이 멈췄다. 세 번째 줄. JWT_SECRET. 하드코딩된 시크릿 키. 환경변수가 아니라 소스코드에 직접 박혀 있다. Git 히스토리에도 남아 있을 것이다. 레포지토리에 접근 가능한 누구나 이 키로 JWT를 위조할 수 있다.
그리고 여덟째 줄. user.password !== password. 평문 비밀번호 비교. bcrypt도 없다. 패스워드가 해싱되지 않은 채 데이터베이스에 저장되어 있다는 뜻이다.
은채는 메모를 시작했다.
[CRITICAL] 하드코딩된 JWT 시크릿 키 (auth/controller.js:3)
[CRITICAL] 평문 패스워드 비교 — bcrypt 미사용 (auth/controller.js:8)
[HIGH] .env 파일 없음, 모든 설정값 소스코드 하드코딩
결제 시스템으로 넘어갔다. payment/service.js.
exports.processPayment = async (req, res) => {
const { userId, amount, planId } = req.body;
const query = `SELECT * FROM subscriptions WHERE user_id = '${userId}'`;
const result = await db.query(query);
// ...
네 번째 줄. 템플릿 리터럴로 SQL 쿼리를 직접 조합하고 있다. 파라미터 바인딩 없이 사용자 입력값을 쿼리에 직접 삽입. 교과서적인 SQL 인젝션 취약점이다.
은채는 터미널을 열었다.
$ curl -X POST https://api.taskbridge.io/payment \
-H "Authorization: Bearer $TOKEN" \
-d '{"userId": "1' OR '1'='1", "amount": 0, "planId": "free"}'
{"status": "success", "message": "Payment processed"}
# 전체 구독 데이터가 조회 가능. 금액 0으로 결제 처리됨.
작동했다. SQL 인젝션으로 결제 금액을 0으로 만들 수 있다. 은채는 다음 파일을 열었다. API 미들웨어. 인증 토큰 검증 로직.
const authenticateToken = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) return res.status(401).send('Access denied');
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
next(); // <-- 토큰 검증 실패해도 통과
}
};
열째 줄. catch 블록에서 에러를 처리하지 않고 next()를 호출하고 있다. 토큰이 유효하지 않아도, 만료되어도, 위조되어도, 요청이 다음 핸들러로 넘어간다. 인증이 사실상 없는 것과 같다.
은채는 세 가지 취약점을 발견하는 데 2시간이 걸렸다. 하드코딩 시크릿, SQL 인젝션, 인증 우회. 각각 독립적으로도 심각하지만, 셋이 결합되면 치명적이다. 시크릿 키로 아무 사용자의 토큰을 만들고, 인증을 우회하고, SQL 인젝션으로 데이터를 조작할 수 있다.
은채는 현우에게 전화를 걸었다.
"강 대표님, 코드를 보고 있는데요. 몇 가지 여쭤볼 것이 있습니다."
"네, 말씀하세요."
"인증 미들웨어에서 토큰 검증이 실패했을 때 왜 next()를 호출하는지 설명해주시겠어요?"
3초의 침묵.
"그게... 어떤 파일이요?"
"middleware/auth.js요. 대표님이 만드신 파일입니다."
5초의 침묵.
"그 부분은... Cursor가 만든 코드인데, 정확한 의도는 제가 다시 확인해보겠습니다."
은채는 전화를 끊고 감사 보고서를 계속 작성했다. 대표가 자기 코드를 설명하지 못하는 것은 처음이 아니었다. 2026년, AI 생성 코드를 감사하는 일이 늘어나면서 은채는 이런 패턴을 자주 보았다. 코드는 존재하지만 이해하는 사람이 없는 상태.
은채는 감사 2일차에 접어들었다. 2만 줄의 코드를 한 줄씩 읽고 있었다. 그리고 한 가지 패턴을 발견했다. AI가 생성한 코드에는 주석이 많았다. // Handle authentication, // Process payment, // Send notification. 주석은 코드가 하는 일을 설명하고 있었다. 하지만 왜 그렇게 하는지는 설명하지 않았다.
주석이 "무엇"을 설명하면 코드를 읽는 사람을 돕는다. 주석이 "왜"를 설명하면 코드를 유지보수하는 사람을 돕는다. AI는 "무엇"만 안다. "왜"는 사람의 영역이다. 그리고 이 코드에는 "왜"를 아는 사람이 없다.
Part III
"만들 수 있다 ≠ 이해한다"
2026년 4월 14일. 감사 보고서가 현우에게 전달되었다.
Critical 17개. High 34개. 총 120개의 취약점. 현우는 보고서를 세 번 읽었다. 첫 번째는 빠르게. 두 번째는 천천히. 세 번째는 각 항목을 하나씩 짚어가며.
하드코딩된 API 키 5개. SQL 인젝션 가능 지점 8개. 인증 우회 3개. CORS 설정 오류. Rate limiting 미적용. 입력값 검증 누락 14건. 민감 데이터 로깅 7건.
현우는 직접 수정하기로 했다. AI에게 물었다.
AI가 수정 코드를 생성했다. 현우는 "Accept All"을 눌렀다. 테스트를 돌렸다. 통과. 다음 취약점. 프롬프트를 쓰고, Accept All, 테스트. 하루 만에 17개 Critical 중 12개를 수정했다. 빠르다. 하지만 은채에게 수정 결과를 보내자 답이 돌아왔다.
AI가 한 곳을 고치면서 다른 곳을 깨뜨렸다. 새로운 함수를 추가하면서 새로운 취약점을 만들었다. 현우는 같은 프롬프트를 다시 썼다. "XSS 취약점 수정해줘." AI가 수정했다. 테스트 통과. 은채에게 보냈다. 또 다른 문제가 발견되었다.
AI가 만든 코드를 AI가 고치면, 누가 검증하는가. 내가? 나는 SQL 인젝션이 뭔지는 안다. 하지만 내 코드에서 그것을 찾아낼 수 없었다. XSS가 뭔지도 안다. 하지만 sanitizeInput이 왜 취약한지는 모른다.
4월 셋째 주. 현우는 은채에게 미팅을 요청했다. 카페에서 만났다.
"은채 씨, 솔직히 말씀드리면 — 수정할수록 새로운 문제가 생깁니다. 뭘 어떻게 해야 하는지 모르겠습니다."
은채는 아이스 아메리카노를 한 모금 마시고 말했다.
"현우 씨가 만든 건 소프트웨어가 아니라, 소프트웨어처럼 보이는 것입니다."
현우는 대답하지 못했다.
"오해하지 마세요. 비난이 아닙니다. 기능은 작동해요. 유저가 로그인하고, 프로젝트를 만들고, 결제도 돼요. 하지만 기능이 작동하는 것과 소프트웨어가 안전한 것은 완전히 다른 문제입니다. 그리고 그 차이를 이해하려면 코드를 이해해야 하는데, 현우 씨는 코드를 이해하지 못하잖아요."
"AI가 잘 만들었다고 생각했습니다."
"AI는 잘 만들었어요. 문법적으로 완벽하고, 구조도 합리적이에요. 다만 보안은 문법이 아니라 맥락의 문제예요. 이 데이터가 어디서 오는지, 누가 접근할 수 있는지, 실패했을 때 무슨 일이 일어나는지. AI는 프롬프트에 명시하지 않은 맥락을 고려하지 않아요."
현우는 커피잔을 내려놓았다.
"그러면 어떡해야 합니까."
"개발자를 고용하셔야 합니다. 보안을 이해하는 개발자를."
기능이 작동하는 것과
소프트웨어가 안전한 것은 다르다
Part IV
"2만 줄의 무게"
2026년 5월 첫째 주. 현우는 보안 전문 백엔드 개발자 한 명을 영입했다. 경력 7년차. 이름은 박도현.
도현은 태스크브릿지 코드를 3일간 읽었다. 셋째 날 저녁, 현우에게 말했다.
"대표님, 솔직하게 말씀드려도 될까요."
"네."
"이 코드는 수정하는 것보다 다시 쓰는 게 빠릅니다. 전부 다시 쓴다는 뜻은 아니에요. 핵심 비즈니스 로직은 살리고, 인증/보안/인프라 레이어를 새로 짜는 겁니다. 지금 구조로는 한 곳을 고치면 세 곳이 깨져요. AI가 각 파일을 독립적으로 생성해서 파일 간 의존성이 엉망입니다."
현우가 물었다.
"시간이 얼마나 걸립니까."
"2주요."
"2만 줄을 2주에요?"
"2만 줄 중 살릴 코드는 많지 않습니다."
도현은 2주 동안 작업했다. 현우는 그 과정을 지켜보았다. 도현의 작업 방식은 현우와 완전히 달랐다. 현우는 프롬프트를 쓰고 Accept All을 눌렀다. 도현은 AI를 쓰되, 생성된 코드를 한 줄씩 읽고, 수정하고, 테스트하고, 다시 읽었다.
"도현 씨도 AI 쓰잖아요. 뭐가 다른 거예요."
"AI가 초안을 써주면 제가 편집합니다. 대표님은 AI가 쓴 것을 그대로 출판했어요. 그게 차이입니다."
2주 후. 도현이 PR을 올렸다.
142 files changed
8,247 deletions(-)
3,102 insertions(+)
# 8,247줄 삭제, 3,102줄 추가
# 순 감소: 5,145줄
2만 줄이 약 1만 5천 줄이 되었다. 기능은 동일하다. 오히려 응답 속도가 30% 빨라졌다. 보안 감사를 다시 돌렸다.
Critical 0. High 2. 120개에서 21개로. 도현이 삭제한 8,000줄은 대부분 불필요한 코드였다. AI가 만든 유틸리티 함수 중 실제로 사용되는 것은 절반도 안 됐다. AI는 "혹시 필요할지 모르니" 함수를 만들어 놓았지만, 누구도 그것을 호출하지 않았다. 사용되지 않는 코드는 관리되지 않는 코드이고, 관리되지 않는 코드는 취약점이 된다.
"코드 양이 줄었는데 기능은 그대로죠." 도현이 말했다. "원래 필요 없는 코드가 많았던 겁니다."
현우는 고개를 끄덕였다. 그리고 한 가지를 물었다.
"도현 씨, 제가 처음부터 보안 개발자와 함께 했으면 어땠을까요."
"4주가 아니라 6주 걸렸을 겁니다. 대신 감사 비용과 제 인건비 2주치와 런칭 지연 한 달은 없었겠죠."
현우는 사무실 창밖을 보았다. 강남의 오후. 유리 건물들이 햇빛을 받아 반짝이고 있었다. 4주 만에 만든 2만 줄. 그중 8,000줄은 처음부터 필요 없었다. 나머지 중 상당수에는 보안 구멍이 있었다. 기능이 작동한다는 사실이 현우를 속였다. 화면에 데이터가 뜨고, 결제가 처리되고, 이메일이 발송되는 것을 보며 "소프트웨어를 만들었다"고 생각했다.
하지만 소프트웨어를 만드는 것은 기능을 작동시키는 것이 아니다. 소프트웨어를 만드는 것은 그 기능이 안전하게, 효율적으로, 예측 가능하게 작동하도록 보장하는 것이다. 그리고 그 보장을 하려면 코드를 이해해야 한다.
나는 소프트웨어를 만든 게 아니라, 소프트웨어처럼 보이는 것을 만들었다. 프롬프트 347개로. Accept All 347번으로. 한 번도 코드를 읽지 않고.
현우는 노트북을 열었다. Cursor가 아니라 메모장을 열었다. 그리고 적었다.
배운 것:
1. 만들 수 있다 ≠ 이해한다
2. 기능이 작동한다 ≠ 소프트웨어가 안전하다
3. AI는 프롬프트에 쓴 것만 만든다
프롬프트에 쓰지 않은 것은 만들지 않는다
보안은 프롬프트에 쓰기 어렵다
4. Accept All은 이해가 아니라 포기다
5. 코드 양은 능력의 척도가 아니다
2만 줄을 줄인 개발자가 2만 줄을 쓴 나보다 낫다
5월의 저녁. 현우는 사무실에 혼자 남아 있었다. 모니터에는 도현이 리팩토링한 코드가 열려 있었다. 3,102줄. 현우는 그것을 한 줄씩 읽기 시작했다. 이해하지 못하는 줄이 나오면 검색했다. helmet이 뭔지, express-rate-limit이 뭔지, bcrypt.compare의 세 번째 인자가 뭔지.
느렸다. 한 시간에 50줄도 읽지 못했다. Cursor에 "이 코드 설명해줘"라고 치면 3초면 될 일이었다. 하지만 현우는 치지 않았다. 3초의 설명은 3초 후에 증발한다. 한 시간의 독해는 남는다.
현우는 개발자가 될 생각은 없었다. 자기 역할은 제품을 기획하고, 방향을 잡고, 시장을 읽는 것이다. 하지만 적어도 자기 제품의 코드가 무슨 일을 하는지, 왜 그렇게 하는지는 알아야 한다고 생각했다. 모든 줄을 이해할 필요는 없다. 하지만 "이 코드가 왜 이렇게 작성됐는지 설명해주시겠어요?"라는 질문에 "Cursor가 만들었는데요"라고 답하는 일은 없어야 한다.
밤 10시. 현우는 도현의 코드를 200줄까지 읽고 노트북을 덮었다. 내일 또 읽을 것이다. 모레도.
강남의 밤. 현우는 사무실을 나서며 생각했다. 4주 만에 2만 줄을 만든 것이 기적이라고 믿었다. 하지만 기적은 없었다. 있었던 것은 착각이었다. 검색하면 코드가 나온다. 프롬프트를 쓰면 소프트웨어가 나온다. 하지만 그것은 지식이 아니다. 지식의 격차가 줄어든 것이지, 능력의 격차가 줄어든 것이 아니다.
검색하면 나온다
이해는 나오지 않는다
AI 생성 코드의 상당수에 보안 취약점이 있다. 만들 수 있다는 것과 이해한다는 것은 같지 않다.