저도 제가 이해 못 하는 코드를 배포하곤 합니다. 여러분도 아마 그럴걸요? – 제이크 네이션스(Jake Nations), 넷플릭스

AAI Engineer
컴퓨터/소프트웨어경영/리더십AI/미래기술

Transcript

00:00:00[음악]
00:00:21>> 여러분, 안녕하세요. 반갑습니다.
00:00:22오늘 강연은 제 고백으로 시작하려 합니다.
00:00:26제가 사실 완전히 이해하지 못한 코드를 배포한 적이 있습니다.
00:00:29코드를 생성하고, 테스트하고, 배포까지 했지만 어떻게 작동하는지 설명할 수 없었죠.
00:00:33그런데 말입니다, 장담하건대 여기 계신 여러분도 모두 그런 적이 있을 겁니다.
00:00:37>> [웃음]
00:00:40>> 이제 우리 모두 더 이상 이해하지 못하는 코드를 배포한다는 점을 인정했으니,
00:00:41어쩌다가 이런 상황에 이르게 되었는지,
00:00:44그 과정을 한번 살펴보려 합니다.
00:00:46먼저 역사를 되짚어보면, 역사는 반복되는 경향이 있다는 것을 알 수 있습니다.
00:00:50둘째로, 우리는 일종의 함정에 빠져 있습니다.
00:00:52우리는 '쉬운 것(easy)'과 '단순한 것(simple)'을 혼동해 왔습니다.
00:00:55마지막으로 해결책이 있긴 하지만, 그것은 사고를 외주화하지 않을 때만 가능합니다.
00:01:00저는 지난 몇 년간 넷플릭스에서 AI 도구의 도입을 추진하는 일을 도왔습니다.
00:01:05말씀드리자면, 그 가속도는 정말 대단합니다.
00:01:07예전에는 며칠씩 걸리던 백로그 항목들을 이제는 몇 시간 만에 처리합니다.
00:01:10수년간 미뤄왔던 대규모 리팩토링도 마침내 실행되고 있죠.
00:01:15하지만 문제가 있습니다.
00:01:16대규모 운영 시스템은 항상 예상치 못한 방식으로 고장이 납니다.
00:01:19최근 클라우드플레어(Cloudflare) 사태만 봐도 알 수 있죠.
00:01:21장애가 발생했을 때, 여러분은 디버깅하는 코드를 제대로 이해하고 있어야 합니다.
00:01:23그런데 문제는 우리가 코드를 너무 빠르고 방대한 양으로 생성하고 있다는 점입니다.
00:01:28우리의 이해 속도가 그 속도를 따라잡지 못하고 있습니다.
00:01:29저도 잘 압니다, 제가 직접 겪어봤으니까요.
00:01:34방대한 코드를 생성해놓고 들여다보며 '이게 대체 뭘 하는 건지 모르겠네'라고 생각한 적이 있습니다.
00:01:39하지만 테스트를 통과했고 작동하니까 그냥 배포해 버렸죠.
00:01:41사실 이런 현상이 아주 새로운 것은 아닙니다.
00:01:44모든 세대의 소프트웨어 엔지니어들은 결국 소프트웨어의 복잡성이
00:01:48자신의 관리 능력을 초과하는 한계점에 부딪혀 왔습니다.
00:01:50우리가 소프트웨어 위기를 처음 겪는 세대는 아닙니다.
00:01:52다만 무한한 생성의 시대에 이 위기를 맞이한 첫 세대일 뿐이죠.
00:01:56그럼 잠시 물러나서 이 모든 것이 어디서 시작되었는지 살펴보겠습니다.
00:01:5860년대 후반과 70년대 초반, 당시의 똑똑한 컴퓨터 과학자들이
00:02:03모여서 '우리는 지금 소프트웨어 위기에 처해 있다'라고 말했습니다.
00:02:06소프트웨어에 대한 수요는 엄청난데, 정작 우리는 그걸 감당하지 못하고 있다는 것이었죠.
00:02:11프로젝트 기간은 너무 길어지고, 진행 속도는 너무 느렸습니다.
00:02:15우리는 제대로 해내지 못하고 있었던 겁니다.
00:02:16그때 다익스트라(Dijkstra)가 아주 훌륭한 말을 남겼습니다.
00:02:20그의 긴 말을 조금 의역해보자면 이렇습니다.
00:02:23“컴퓨터 성능이 낮았을 때는 프로그래밍이 사소한 문제였지만,
00:02:26컴퓨터가 거대해지자 프로그래밍은 거대한 문제가 되었다.”
00:02:31그는 하드웨어의 성능이 1,000배 성장함에 따라
00:02:34사회가 요구하는 소프트웨어의 양도 그에 비례해 늘어났음을 설명했습니다.
00:02:37그 과정에서 프로그래머들은 방법과 수단을 찾아내어
00:02:41어떻게 이토록 방대한 소프트웨어를 뒷받침할지 고민해야 했습니다.
00:02:43이런 일은 주기적으로 계속 발생했습니다.
00:02:4770년대에는 더 큰 시스템을 구축하기 위해 C 언어가 등장했습니다.
00:02:5080년대에는 개인용 컴퓨터가 보급되어 누구나 소프트웨어를 짤 수 있게 되었죠.
00:02:5390년대에는 객체 지향 프로그래밍이 등장했습니다.
00:02:56자바 덕분에 지옥 같은 상속 구조도 생겨났고요.
00:03:002000년대에는 애자일이 도입되어 워터폴 방식 대신
00:03:03스프린트와 스크럼 마스터가 지시를 내리기 시작했습니다.
00:03:062010년대에는 클라우드, 모바일, 데브옵스 등 모든 것이 등장했고,
00:03:09그야말로 소프트웨어가 세상을 집어삼켰습니다.
00:03:10그리고 오늘날에는 AI, 코파일럿, 커서, 클로드, 코덱스, 제미나이 등이 있습니다.
00:03:17우리는 설명하는 속도만큼이나 빠르게 코드를 생성할 수 있습니다.
00:03:19패턴은 계속되지만, 그 규모가 무한해졌다는 점이 달라졌습니다.
00:03:23여러분은 '맨먼스 미신'의 저자 프레드 브룩스(Fred Brooks)를 아실 겁니다.
00:03:29그는 1986년에 '은탄환은 없다(No Silver Bullet)'라는 논문도 썼습니다.
00:03:32그는 논문에서 소프트웨어 생산성을 획기적으로 개선할 수 있는
00:03:36단 한 가지의 혁신은 존재하지 않을 것이라고 주장했습니다.
00:03:38왜일까요?
00:03:40그는 프로그래밍의 어려운 점은 코딩 기술, 문법, 타이핑,
00:03:44또는 반복적인 코드 작성이 아니라고 말했습니다.
00:03:45진짜 어려운 부분은 실제 문제를 이해하고 해결책을 설계하는 것이기 때문입니다.
00:03:49그 어떤 도구도 이 근본적인 어려움을 제거할 수는 없습니다.
00:03:52지금까지 우리가 만든 모든 도구와 기술은 코딩 기술을 더 쉽게 만들어줄 뿐입니다.
00:03:55하지만 핵심 과제인,
00:03:57무엇을 만들지, 어떻게 작동하게 할지를 이해하는 것은 여전히 똑같이 어렵습니다.
00:04:00문제가 코딩 기술에 있지 않다면, 왜 우리는 자꾸 기술적인 면만 최적화하려 할까요?
00:04:06경험 많은 엔지니어들이 어쩌다가 스스로 이해하지 못하는 코드를 갖게 되었을까요?
00:04:09그 답은 우리가 자주 혼동하는 두 단어인 '단순함(simple)'과 '쉬움(easy)'에 있다고 봅니다.
00:04:14우리는 보통 이 두 단어를 섞어서 사용하지만,
00:04:16사실 이 단어들은 완전히 다른 의미를 가지고 있습니다.
00:04:18제가 스피커 저녁 식사 자리에서 클로저(Clojure) 개발자라는 사실이 들통났는데요.
00:04:21여기서 그 차이가 명확해집니다.
00:04:23클로저 언어의 창시자인 리치 히키(Rich Hickey)는
00:04:252011년 'Simple Made Easy'라는 강연에서 이를 설명했습니다.
00:04:29그는 단순함(simple)을 '한 겹, 한 가닥, 얽히지 않음'으로 정의했습니다.
00:04:33각 조각이 한 가지 일만 하고 다른 것과 꼬이지 않은 상태죠.
00:04:36반면 쉬움(easy)은 '가까운 것, 손에 닿는 것'을 의미한다고 정의했습니다.
00:04:39노력 없이 접근할 수 있는 것은 무엇인가?
00:04:41복사하고, 붙여넣고, 배포하는 것이죠.
00:04:43단순함은 구조에 관한 것입니다.
00:04:45쉬움은 근접성에 관한 것이고요.
00:04:48문제는 우리가 바란다고 해서 무언가가 단순해지지는 않는다는 점입니다.
00:04:51단순함에는 사고와 설계, 그리고 엉킨 것을 푸는 과정이 필요합니다.
00:04:54하지만 무언가를 더 쉽게 만드는 것은 언제나 가능합니다.
00:04:56그냥 더 가까이 두면 되니까요.
00:04:57패키지를 설치하거나, AI로 생성하거나, 스택 오버플로우의 해결책을 복사하면 됩니다.
00:05:03쉬운 길을 선택하는 것은 인간의 본능입니다.
00:05:06우리는 그렇게 프로그래밍되어 있습니다.
00:05:07말씀드린 대로 스택 오버플로우에서 복사하는 건 아주 간단합니다.
00:05:10마법처럼 모든 걸 처리해주는 프레임워크를 설치하고 실행하면 끝이죠.
00:05:14하지만 쉽다고 해서 단순한 것은 아닙니다.
00:05:15쉬움은 시스템에 무언가를 빠르게 추가할 수 있다는 뜻입니다.
00:05:18단순함은 당신이 한 작업을 이해할 수 있다는 뜻입니다.
00:05:20쉬운 길을 택할 때마다 우리는 지금의 속도를 위해 미래의 복잡성을 담보로 잡는 셈입니다.
00:05:24솔직히 예전에는 그런 트레이드오프가 꽤 잘 통했습니다.
00:05:27코드베이스에 쌓이는 복잡도가 충분히 느려서 필요할 때마다
00:05:31리팩토링하고, 재검토하고, 다시 구축할 시간이 있었거든요.
00:05:34하지만 AI가 그 균형을 깨뜨렸다고 생각합니다.
00:05:36AI는 궁극의 '쉬움 버튼'이니까요.
00:05:37AI는 쉬운 길을 너무나도 매끄럽게 만들어서
00:05:38우리가 더 이상 단순한 길에 대해서는 고민조차 하지 않게 만듭니다.
00:05:41코드가 즉시 나타나는데 굳이 아키텍처를 고민할 필요가 있을까요?
00:05:44이런 일이 어떻게 일어나는지 보여드리겠습니다.
00:05:47우리가 좋아하는 대화형 인터페이스를 통해 어떻게
00:05:50단순한 작업이 복잡함의 늪으로 변해가는지 말이죠.
00:05:52가상의 예시이긴 하지만, 우리 앱에
00:05:55인증 기능을 추가한다고 가정해 봅시다.
00:05:57인증을 추가해달라고 하면 깔끔한 auth.js 파일이 생성됩니다.
00:06:01몇 번 수정을 거쳐 메시지가 5개 정도 쌓였다고 합시다.
00:06:02이제 OAuth도 추가하고 싶어집니다.
00:06:04그러면 auth.js와 OAuth.js 파일이 생기겠죠.
00:06:07계속 반복하다 보니 세션 기능이 고장 납니다.
00:06:11코드 충돌도 한가득 생기고요.
00:06:12질문이 20번쯤 오가면 더 이상 대화를 하는 게 아닙니다.
00:06:15자신이 추가한 제약 조건조차 기억하지 못할 정도로
00:06:18너무나 비대해진 컨텍스트를 간신히 관리하고 있는 처지가 되죠.
00:06:20버려진 방식에서 남겨진 죽은 코드들,
00:06:22그저 돌아가게만 만드느라 억지로 짜 맞춘 테스트들,
00:06:25세 가지 서로 다른 해결책의 파편들이 뒤섞여 버립니다.
00:06:28새로운 지시가 내려올 때마다 기존의 아키텍처 패턴은 덮어씌워 집니다.
00:06:31인증이 돌아가게 해달라고 하면 그렇게 해줍니다.
00:06:33에러를 고쳐달라고 하면 고쳐줍니다.
00:06:35잘못된 아키텍처 결정에 대한 어떤 저항도 없습니다.
00:06:38코드는 그저 최신 요청에 맞춰서 변형될 뿐입니다.
00:06:40상호작용이 일어날 때마다 단순함 대신 쉬움을 택하는 것이고,
00:06:43쉬움은 언제나 더 큰 복잡성을 의미합니다.
00:06:46우리도 머리로는 알지만, 쉬운 길이 너무나도 쉽다 보니 결국 그 길을 택하게 됩니다.
00:06:50그리고 복잡성은 손쓸 수 없을 때까지 계속해서 불어날 것입니다.
00:06:52AI는 '쉬움'을 논리적 극한까지 밀어붙입니다.
00:06:58원하는 것을 결정하기만 하면 즉시 코드를 얻을 수 있죠.
00:07:00하지만 여기에 위험이 도사리고 있습니다.
00:07:02생성된 코드는 코드베이스의 모든 패턴을 동일하게 취급합니다.
00:07:06에이전트가 코드베이스를 분석할 때, 모든 줄은 보존해야 할 패턴이 됩니다.
00:07:1047번 줄에 있는 인증 체크도 하나의 패턴입니다.
00:07:13제가 2019년에 추가했을지도 모를, GraphQL처럼 작동하는
00:07:18이상한 GRPC 코드도 똑같은 패턴으로 인식됩니다.
00:07:19기술 부채는 부채로 인식되지 않고 그저 또 다른 코드일 뿐이죠.
00:07:22진짜 문제는 복잡성입니다.
00:07:25강연 내내 이 단어를 정의도 내리지 않고 계속 썼는데요.
00:07:29복잡성을 생각하는 가장 좋은 방법은 그것이 단순함의 반대라는 것입니다.
00:07:31그저 '서로 얽혀 있음'을 의미하죠.
00:07:33무언가가 복잡해지면 모든 것이 다른 모든 것에 영향을 줍니다.
00:07:36하나를 바꾸면 연쇄적으로 열 군데에 영향을 미치게 됩니다.
00:07:41다시 프레드 브룩스의 '은탄환은 없다' 논문으로 돌아가 보죠.
00:07:43그는 모든 시스템에 두 가지 주요 복잡성이 존재한다고 보았습니다.
00:07:47첫째는 '본질적 복잡성(essential complexity)'으로,
00:07:51해결하려는 문제 자체에서 기인하는 근본적인 어려움입니다.
00:07:53사용자는 결제를 해야 하고 주문은 처리되어야 합니다.
00:07:56이것은 그 소프트웨어 시스템이 존재하는 근본적인 이유에서 오는 복잡성입니다.
00:08:00둘째는 '우연적 복잡성(accidental complexity)'입니다.
00:08:03개발 과정에서 추가된 임시방편, 방어적 코드,
00:08:06프레임워크, 그리고 한때는 유효했던 추상화 등을 말합니다.
00:08:09코드를 작동시키기 위해 우리가 덧붙인 모든 것들이죠.
00:08:11실제 코드베이스에서는 이 두 유형의 복잡성이 곳곳에 퍼져 있습니다.
00:08:16이것들이 너무 긴밀하게 엉켜 있어서 이를 분리해내려면
00:08:19맥락과 역사, 그리고 경험이 필요합니다.
00:08:20하지만 생성된 결과물은 이런 구분을 전혀 하지 못합니다.
00:08:24그래서 모든 패턴이 그저 그대로 유지되는 것이죠.
00:08:26넷플릭스에서 진행 중인 작업 중 실제 사례를 하나 들어보겠습니다.
00:08:32약 5년 전에 작성한 기존의 권한 부여 코드와
00:08:35새로운 중앙 집중식 인증 시스템 사이에 추상화 계층이 있는 시스템이 있습니다.
00:08:41앱 전체를 새로 만들 시간이 없어서
00:08:42그 사이에 '심(shim)'을 하나 끼워 넣었죠.
00:08:44이제 AI가 있으니 새로운 시스템을 직접 사용하도록
00:08:47코드를 리팩토링할 아주 좋은 기회입니다. 간단한 요청 같죠?
00:08:50전혀 아니었습니다. 기존 코드가 예전의 권한 부여 패턴과
00:08:56너무나도 단단히 결합되어 있었거든요. 권한 체크 로직이 비즈니스 로직에 스며들어 있었고,
00:08:59역할에 대한 가정이 데이터 모델에 박혀 있었으며, 인증 호출이 수백 개 파일에 흩어져 있었습니다.
00:09:03에이전트가 리팩토링을 시작했지만 몇 개 파일을 채 처리하기도 전에
00:09:07도저히 풀 수 없는 의존성에 부딪혔고, 결국 통제 불능 상태에 빠져 포기해 버렸습니다.
00:09:10더 최악인 상황은, 에이전트가 이전 시스템의 기존 로직을 보존하려고
00:09:16새로운 시스템을 사용해 이전 방식을 재현하려 했던 것이었는데, 이것 역시 좋지 않았습니다.
00:09:19문제는 AI가 '경계선'을 보지 못했다는 점입니다.
00:09:23비즈니스 로직이 어디서 끝나고 인증 로직이 어디서 시작되는지 파악하지 못했죠.
00:09:26모든 것이 너무 엉켜 있어서 완벽한 정보가 있어도
00:09:30AI는 깔끔한 경로를 찾아내지 못했습니다.
00:09:33우연적 복잡성이 이 정도로 얽혀버리면
00:09:35AI는 상황을 개선하는 데 별 도움이 되지 않습니다.
00:09:38오히려 그 위에 복잡성의 층을 더 쌓을 뿐이라는 걸 깨달았습니다.
00:09:40우리는 그 차이를 구별할 수 있습니다. 적어도 생각을 위해 충분히 속도를 줄인다면 말이죠.
00:09:45우리는 어떤 패턴이 본질적인 것인지,
00:09:47그리고 어떤 것이 단지 몇 년 전 누군가가 해결했던 방식일 뿐인지 알고 있습니다.
00:09:50우리는 AI가 추론할 수 없는 맥락을 가지고 있습니다. 하지만
00:09:53시작하기 전에 시간을 들여 이러한 구분을 명확히 할 때만 가능합니다.
00:09:56그렇다면 실제로 어떻게 해야 할까요?
00:10:01거대한 코드베이스를 마주한 상황에서 어떻게
00:10:04우연적 복잡성과 본질적 복잡성을 분리할 수 있을까요?
00:10:07제가 넷플릭스에서 다루는 코드베이스는 약 100만 줄의 자바 코드로 되어 있고,
00:10:10메인 서비스는 마지막으로 확인했을 때 약 500만 토큰 규모였습니다.
00:10:13제가 사용할 수 있는 어떤 컨텍스트 윈도우도 이를 다 담을 수 없었죠.
00:10:17그래서 처음에는 이런 생각을 했습니다.
00:10:19코드베이스의 방대한 부분을 컨텍스트에 복사해 넣으면
00:10:23패턴이 저절로 드러나지 않을까,
00:10:24무슨 일이 일어나고 있는지 AI가 스스로 알아내지 않을까 하고 말이죠.
00:10:26하지만 이전의 인증 리팩토링 사례와 마찬가지로,
00:10:29결국 출력물은 그 자체의 복잡함 속에 길을 잃고 말았습니다.
00:10:31그래서 저는 다른 방식을 취할 수밖에 없었죠.
00:10:34무엇을 포함할지 직접 선택해야 했습니다. 설계 문서, 아키텍처, 다이어그램,
00:10:37핵심 인터페이스 등 중요한 것들을 골라냈죠.
00:10:39그리고 컴포넌트들이 어떻게 상호작용해야 하는지, 어떤 패턴을 따라야 하는지
00:10:42요구사항을 작성하는 데 시간을 들였습니다.
00:10:43보시다시피, 저는 사양서를 쓰고 있었던 겁니다.
00:10:45500만 개의 토큰이 2,000단어 분량의 사양서가 되었습니다.
00:10:49그리고 더 나아가, 그 사양서를 바탕으로
00:10:52실행할 코드의 정확한 단계들을 생성했습니다.
00:10:55모호한 지시 없이, 그저 정밀한 작업 순서만을 담았죠.
00:10:58이 방식이 훨씬 더 깔끔하고 제가 이해할 수 있는 집중된 코드를 만든다는 것을 발견했습니다.
00:11:02즉, 먼저 정의를 내리고 실행 계획을 세운 것입니다.
00:11:05이것이 제가 얼마 전부터 '컨텍스트 압축'이라고 부르기 시작한 접근 방식입니다.
00:11:11여러분은 이를 컨텍스트 엔지니어링이라 부르든, 스펙 기반 개발이라 부르든,
00:11:13뭐라 부르셔도 상관없습니다.
00:11:15이름은 중요하지 않습니다.
00:11:16중요한 것은 사고와 계획이 업무의 대부분을 차지하게 된다는 점입니다.
00:11:20그럼 이 방식이 실제로 어떻게 작동하는지 단계별로 설명해 드리겠습니다.
00:11:22우선 1단계, 리서치 단계입니다.
00:11:26먼저 모든 정보를 입력합니다.
00:11:28아키텍처 다이어그램, 문서, 슬랙 대화 내용 등이죠.
00:11:31이미 여러 번 언급했던 내용들이지만,
00:11:32변경하려는 내용과 관련이 있는 가능한 한 많은 컨텍스트를
00:11:35가져오는 것이 정말 중요합니다.
00:11:36그런 다음 에이전트를 사용해 코드베이스를 분석하고
00:11:39컴포넌트와 의존 관계를 파악하게 합니다.
00:11:42이 과정은 한 번에 끝나서는 안 됩니다.
00:11:43저는 계속해서 질문을 던집니다. “캐싱은 어떻게 처리되지?”
00:11:46“장애 발생 시에는 어떻게 작동해?” 같은 식으로요.
00:11:47분석이 틀렸을 때는 제가 직접 바로잡아 줍니다.
00:11:49정보가 부족하다면 추가적인 컨텍스트를 제공하죠.
00:11:51반복할수록 분석의 정확도는 높아집니다.
00:11:55이 단계의 결과물은 단 하나의 리서치 문서입니다.
00:11:57무엇이 존재하고, 무엇이 어디에 연결되어 있으며,
00:11:59변경 사항이 어디에 영향을 줄지 정리된 문서죠.
00:12:01몇 시간의 탐색 과정이 단 몇 분의 읽을거리로 압축되는 것입니다.
00:12:03오늘 아침 덱스도 언급했지만, 여기서 '인간의 검토'가 핵심입니다.
00:12:09분석 내용이 실제와 맞는지 확인하는 이 순간이,
00:12:12전체 과정에서 가장 영향력이 큰 순간입니다.
00:12:15여기서 오류를 잡아내야 나중에 큰 재앙을 막을 수 있습니다.
00:12:17이제 2단계로 넘어갑니다.
00:12:20검증된 리서치 결과가 준비되었다면,
00:12:22상세한 구현 계획을 세웁니다. 실제 코드 구조,
00:12:25함수 시그니처, 타입 정의, 데이터 흐름 등을 포함해서요.
00:12:28어떤 개발자라도 따라 할 수 있을 만큼 상세해야 합니다.
00:12:30저는 이를 '번호대로 색칠하기'에 비유하곤 합니다.
00:12:32가장 주니어 엔지니어에게 건네주며 “이대로 해봐”라고 했을 때,
00:12:35줄 단위로 따라 하기만 해도 제대로 작동해야 합니다.
00:12:38이 단계에서 중요한 아키텍처적 결정을 많이 내리게 됩니다.
00:12:43복잡한 로직이 올바른지 확인하고,
00:12:45비즈니스 요구사항이 모범 사례를 따르고 있는지 점검합니다.
00:12:50서비스 경계가 확실한지, 깨끗하게 분리되었는지,
00:12:52불필요한 결합은 없는지 확인하죠.
00:12:54우리는 이미 경험해 봤기에 문제가 생기기 전에 미리 감지할 수 있습니다.
00:12:57AI에게는 그런 선택지가 없습니다.
00:12:59AI는 모든 패턴을 하나의 요구사항으로만 취급하니까요.
00:13:01이 단계의 진정한 마법은 검토 속도에 있습니다.
00:13:05우리는 몇 분 만에 계획을 검증하고 무엇이 만들어질지 정확히 알 수 있습니다.
00:13:10코드를 생성하려는 속도에 발을 맞추려면,
00:13:13우리가 무엇을 하고 있는지 그만큼 빠르게 파악할 수 있어야 합니다.
00:13:18마지막으로 구현 단계입니다. 명확한 계획과
00:13:22탄탄한 리서치가 뒷받침되었으므로, 이 단계는 아주 간단해야 합니다.
00:13:26그게 바로 핵심입니다.
00:13:28AI가 따라야 할 명확한 사양이 있을 때 컨텍스트는 깔끔하고
00:13:31집중된 상태를 유지합니다.
00:13:32긴 대화로 인해 복잡성이 꼬여버리는 상황을 방지한 것이죠.
00:13:3650개의 메시지를 주고받으며 점진적으로 코드를 고치는 대신,
00:13:38매번 검증을 거친 세 개의 집중된 결과물을 얻게 됩니다.
00:13:41중간에 포기한 방식도, 충돌하는 패턴도 없으며,
00:13:44“아, 잠깐만요” 하며 여기저기 쓰레기 코드를 남기는 일도 없습니다.
00:13:48제가 생각하는 이 방식의 진짜 보상은, 미리 고민하고 어려운 작업을 다 해두었기 때문에
00:13:52백그라운드 에이전트에게 많은 일을 맡길 수 있다는 점입니다.
00:13:56에이전트가 구현을 시작하면 여러분은 다른 일을 하다가
00:13:59돌아와서 검토만 하면 됩니다.
00:14:01계획대로 잘 만들어졌는지 확인하기만 하면 되므로
00:14:04검토도 아주 빠르게 끝납니다. 뭔가 멋대로 지어내지 않았는지 걱정할 필요가 없죠.
00:14:07중요한 점은 우리가 AI에게 생각을 맡기는 것이 아니라는 겁니다.
00:14:12우리는 시스템을 이해하는 능력을 유지하면서,
00:14:15기계적인 부분의 속도를 높이기 위해 AI를 활용하는 것입니다.
00:14:17리서치는 빨라지고, 계획은 철저해지며, 구현은 깔끔해집니다.
00:14:21하지만 사고, 종합, 그리고 판단은 여전히 우리의 몫입니다.
00:14:26자, 아까 AI가 감당하지 못했다던 권한 부여 리팩토링 기억하시나요?
00:14:34사실 지금은 그 작업을 진행 중이고
00:14:37꽤 좋은 성과를 내고 있습니다.
00:14:39더 나은 프롬프트를 찾았기 때문이 아닙니다.
00:14:42처음에는 리서치, 계획, 구현 단계를 시작조차 할 수 없다는 걸 깨달았습니다.
00:14:46실제로 이 변경 작업은 저희가 직접 손으로 해야 했습니다.
00:14:49AI 없이 직접 코드를 읽고 의존성을 파악하며,
00:14:52무엇이 깨지는지 확인하기 위해 코드를 직접 수정했습니다.
00:14:53솔직히 그 수동 마이그레이션 작업은 고통스러웠지만 필수적이었습니다.
00:14:59그 과정을 통해 숨겨진 제약 사항과 반드시 지켜져야 할 불변성,
00:15:02권한 로직이 바뀔 때 깨질 서비스들이 무엇인지 드러났습니다.
00:15:05단순한 코드 분석만으로는 절대 알 수 없었던 것들이죠.
00:15:09그 후 직접 작업한 풀 리퀘스트(PR)를 리서치 과정에
00:15:14입력값으로 넣어 앞으로의 모든 리서치의 씨앗으로 삼았습니다.
00:15:19그제야 AI는 제대로 된 마이그레이션이 어떤 모습인지 이해할 수 있었습니다.
00:15:23하지만 각각의 엔티티가 조금씩 다르기 때문에 계속해서
00:15:27질문을 던져야 했습니다. “이 부분은 어떻게 처리해야 하지?”라고요.
00:15:29어떤 것은 암호화되어 있고 어떤 것은 그렇지 않았죠.
00:15:32반복적인 과정을 통해 매번 추가적인 컨텍스트를 제공해야 했습니다.
00:15:35그렇게 하고 나서야 비로소 한 번에 작동할 만한 계획을 세울 수 있었습니다.
00:15:41여기서 핵심 단어는 '작동할 만한'입니다. 우리는 여전히 검증하고,
00:15:45조정하며, 예외 케이스를 찾아내고 있습니다.
00:15:47이 3단계 접근 방식은 마법이 아닙니다.
00:15:55저희가 한 번의 마이그레이션을 직접 손으로 했기 때문에 가능한 것입니다.
00:15:57이해도를 직접 쌓고 나서야 그것을 프로세스에 녹여낼 수 있었습니다.
00:16:01저는 여전히 만능 해결책은 없다고 생각합니다.
00:16:02더 나은 프롬프트나 모델, 심지어 더 나은 사양서를 쓰는 것이 답은 아닙니다.
00:16:06그저 시스템을 깊이 이해하여 안전하게 변경할 수 있도록
00:16:09노력하는 수밖에 없습니다.
00:16:11그렇다면 왜 굳이 이런 과정을 거쳐야 할까요?
00:16:15그냥 제대로 될 때까지 AI와 계속 반복하면 안 될까요?
00:16:18언젠가는 모델이 충분히 강력해져서 그냥 알아서 잘해줄 텐데 말이죠.
00:16:21제 생각에, 그저 '작동한다'는 것만으로는 부족합니다.
00:16:24단순히 테스트를 통과하는 코드와 실제 운영 환경에서 살아남는 코드는 다릅니다.
00:16:28오늘만 돌아가는 시스템과,
00:16:31미래에 다른 사람이 수정할 수 있는 시스템 사이에는 큰 차이가 있습니다.
00:16:34진짜 문제는 '지식의 격차'입니다.
00:16:38AI가 수천 줄의 코드를 몇 초 만에 생성할 때,
00:16:41그것을 이해하는 데는 몇 시간, 복잡하다면 며칠이 걸릴 수도 있습니다.
00:16:45어쩌면 너무 엉망이라 영영 이해하지 못할 수도 있죠.
00:16:48아직 많은 사람이 간과하고 있는 사실이 하나 있습니다.
00:16:52생성 속도를 따라잡기 위해 매번 생각을 건너뛸 때마다,
00:16:56우리는 단순히 이해하지 못하는 코드를 추가하는 데 그치지 않습니다.
00:16:58우리는 문제를 인식하는 능력 자체를 잃어가고 있습니다.
00:17:00“아, 이거 너무 복잡해지는데?”라는 본능적인 감각은,
00:17:03자신의 시스템을 이해하지 못할 때 퇴화해 버립니다.
00:17:09패턴 인식은 경험에서 나옵니다.
00:17:11제가 위험한 아키텍처를 알아채는 이유는,
00:17:12새벽 3시에 그 문제를 해결하기 위해 직접 고군분투해 봤기 때문입니다.
00:17:16제가 더 단순한 해결책을 고집하는 이유는,
00:17:17다른 사람이 만든 복잡한 대안을 직접 유지보수해 봤기 때문입니다.
00:17:21AI는 여러분이 요청하는 것을 생성할 뿐입니다.
00:17:23과거의 실패에서 얻은 교훈을 코드로 구현하지는 않습니다.
00:17:253단계 접근 방식은 이 간극을 메워줍니다.
00:17:29생성 속도에 맞춰 검토할 수 있도록 이해의 과정을 압축된 결과물로 만드는 것이죠.
00:17:33이 과정이 없다면 우리는 이해할 수 있는 속도보다 훨씬 빠르게
00:17:37복잡성을 쌓아 올리게 될 것입니다.
00:17:39AI가 코드를 작성하는 방식의 모든 것을 바꾸고 있지만,
00:17:44솔직히 소프트웨어가 실패하는 근본적인 원인은 바꾸지 못한다고 생각합니다.
00:17:47모든 세대는 각자의 소프트웨어 위기에 직면해 왔습니다.
00:17:50다익스트라 세대는 소프트웨어 공학이라는 규율을 만들어 그 위기에 맞섰고,
00:17:54이제 우리는 무한한 코드 생성이라는 위기에 직면해 있습니다.
00:17:56저는 그 해결책이 또 다른 도구나 방법론에 있다고 보지 않습니다.
00:18:01소프트웨어는 인간의 노력이라는 본질을 기억하는 것이 답입니다.
00:18:05어려운 부분은 코드를 타이핑하는 것이 아니었습니다.
00:18:06애초에 무엇을 타이핑해야 할지 아는 것이 어려운 것이었죠.
00:18:09앞으로 성공할 개발자는 단순히 코드를 가장 많이 생성하는 사람이 아닐 것입니다.
00:18:13자신이 무엇을 만들고 있는지 이해하고,
00:18:15구조의 이음새를 볼 수 있으며, 잘못된 문제를 풀고 있다는 걸
00:18:18알아챌 수 있는 사람일 것입니다.
00:18:19그것은 여전히 우리의 역할이며,
00:18:20오직 우리만이 할 수 있는 일입니다.
00:18:21마지막으로 질문 하나를 던지며 마치려 합니다. 질문은
00:18:25우리가 AI를 사용할 것인가 말 것인가가 아닙니다.
00:18:26그건 이미 결정된 일입니다.
00:18:28배는 이미 떠났습니다.
00:18:30제가 생각하는 진짜 질문은, AI가 우리 코드의 대부분을 작성할 때
00:18:33우리가 여전히 자신의 시스템을 이해할 수 있을 것인가 하는 점입니다.
00:18:35감사합니다.
00:18:37>> (박수)
00:18:39(음악)

Key Takeaway

AI가 코드 생성을 가속화하는 시대에 엔지니어는 '쉬운' 길의 유혹을 이겨내고, 시스템의 '단순함'과 구조를 이해하는 사고의 주도권을 유지해야만 지속 가능한 소프트웨어를 만들 수 있습니다.

Highlights

AI 도구가 코드 생성 속도를 획기적으로 높였지만, 엔지니어가 코드의 작동 원리를 완전히 이해하지 못하는 '지식의 격차'가 발생하고 있습니다.

리치 히키의 정의를 인용하여, 단순함(Simple)은 구조적 얽힘이 없는 상태를, 쉬움(Easy)은 단순히 접근하기 가까운 상태를 의미함을 강조합니다.

소프트웨어의 복잡성은 본질적 복잡성과 우연적 복잡성으로 나뉘며, AI는 이 둘을 구분하지 못해 기존의 기술 부채와 스파게티 코드를 그대로 복제하는 경향이 있습니다.

넷플릭스의 사례를 통해 컨텍스트 압축(리서치-계획-구현)이라는 3단계 접근 방식을 제시하며 사고의 외주화를 경계해야 한다고 주장합니다.

진정한 생산성은 코드의 양이 아니라 시스템에 대한 깊은 이해와 설계 능력에서 오며, 이는 과거 소프트웨어 위기 때와 마찬가지로 인간의 영역입니다.

Timeline

도입: 이해하지 못하는 코드를 배포하는 현실

강연자 제이크 네이션스는 넷플릭스 엔지니어로서 자신도 완전히 이해하지 못한 코드를 배포한 적이 있다는 고백으로 시작합니다. 그는 현재 AI 도구의 도입으로 개발 속도는 비약적으로 빨라졌지만, 인간의 이해 속도가 코드 생성 속도를 따라잡지 못하는 심각한 불균형이 발생하고 있다고 지적합니다. 특히 장애 상황에서 코드를 디버깅해야 할 때 이러한 이해 부족은 치명적인 위협이 됩니다. 강연자는 우리가 무한한 생성의 시대에 새로운 형태의 '소프트웨어 위기'를 맞이했음을 경고합니다. 그는 이 문제가 단순히 기술적인 도구의 문제가 아니라 우리의 접근 방식에 있음을 시사합니다.

역사적 맥락: 다익스트라와 소프트웨어 위기

과거 1960년대와 70년대에도 컴퓨터 성능의 비약적 발전에 비해 소프트웨어 생산성이 따라가지 못해 발생했던 '소프트웨어 위기'가 있었음을 회상합니다. 에츠허르 다익스트라는 하드웨어가 거대해질수록 프로그래밍이 거대한 문제가 된다고 설파했으며, 이후 C 언어부터 객체 지향, 애자일, 클라우드에 이르기까지 복잡성을 해결하려는 노력이 지속되었습니다. 오늘날 AI는 설명하는 속도만큼이나 빠르게 코드를 생성할 수 있게 했지만, 역사는 반복되고 있습니다. 강연자는 기술이 변해도 인간이 소프트웨어를 관리하는 능력의 한계점은 주기적으로 찾아온다는 점을 강조합니다. 결국 도구의 변화보다 중요한 것은 늘어난 규모를 어떻게 통제하느냐의 문제입니다.

단순함(Simple)과 쉬움(Easy)의 결정적 차이

프레드 브룩스의 '은탄환은 없다'를 인용하며, 프로그래밍의 본질적 어려움은 코딩 기술이 아니라 문제를 이해하고 설계하는 것임을 설명합니다. 리치 히키의 개념을 빌려 '단순함'은 얽히지 않은 구조적 상태를, '쉬움'은 당장 손에 닿는 편리함을 의미한다고 정의합니다. AI는 궁극의 '쉬움 버튼'으로서 우리가 아키텍처를 고민하는 과정을 생략하게 만들고, 이는 미래의 복잡성을 담보로 현재의 속도를 사는 행위입니다. 가상의 인증 기능 추가 예시를 통해, 대화형 인터페이스에서 쉬운 길만 택할 때 코드가 어떻게 누더기처럼 변하는지 보여줍니다. 이 과정에서 단순한 구조는 파괴되고 이해할 수 없는 복잡한 결과물만 남게 됩니다.

복잡성의 유형과 AI의 한계

시스템의 복잡성은 해결하려는 문제 자체의 '본질적 복잡성'과 개발 과정에서 덧붙여진 '우연적 복잡성'으로 나뉩니다. AI 에이전트는 코드베이스의 모든 줄을 동일한 가치의 패턴으로 인식하기 때문에, 과거의 잘못된 설계나 기술 부채까지도 보존해야 할 규칙으로 오해합니다. 넷플릭스의 권한 부여 시스템 리팩토링 사례에서, AI는 비즈니스 로직과 인증 로직의 경계를 구분하지 못해 결국 작업을 포기하거나 엉뚱한 결과를 냈습니다. 인간 엔지니어는 맥락과 역사를 알고 있기에 어떤 코드가 제거되어야 할 우연적 요소인지 판단할 수 있습니다. 따라서 AI에게 사고를 맡기기 전에 인간이 먼저 속도를 줄이고 시스템의 경계를 명확히 구분해야 합니다.

해결책: 컨텍스트 압축과 3단계 접근법

방대한 코드베이스를 효율적으로 다루기 위해 강연자는 '리서치-계획-구현'으로 이어지는 3단계 프로세스인 '컨텍스트 압축'을 제안합니다. 1단계 리서치에서는 문서와 다이어그램 등 모든 컨텍스트를 입력하고 AI와 문답하며 분석 리포트를 작성하되, 인간의 검토를 거쳐 오류를 잡습니다. 2단계 계획에서는 주니어 개발자도 따라 할 수 있을 수준의 상세한 사양서와 실행 단계를 설계하여 아키텍처적 결정을 내립니다. 마지막 3단계 구현은 이미 검증된 계획을 AI가 기계적으로 실행하게 함으로써 쓰레기 코드가 남지 않도록 통제합니다. 이 방식의 핵심은 AI에게 생각을 맡기는 것이 아니라, 인간이 시스템을 이해하는 능력을 유지하면서 실행 속도만 높이는 것입니다.

결론: 엔지니어의 역할과 지식의 가치

결국 고통스러운 수동 마이그레이션 과정을 통해 시스템에 대한 직접적인 이해도를 쌓는 것이 선행되어야 함을 강조합니다. '작동하는 코드'와 '운영 환경에서 살아남는 코드'는 다르며, 생각을 생략하고 속도만 쫓다 보면 엔지니어의 본능적인 문제 인식 능력은 퇴화하게 됩니다. 새벽 3시에 장애와 싸우며 얻은 교훈과 경험만이 AI가 흉내 낼 수 없는 엔지니어의 진정한 자산입니다. 미래에 성공할 개발자는 단순히 코드를 많이 뽑아내는 사람이 아니라, 구조의 이음새를 보고 잘못된 설계를 바로잡을 수 있는 사람입니다. 강연자는 AI가 코드를 작성하는 세상에서 우리가 여전히 시스템을 이해할 수 있을지 자문하며 끝을 맺습니다.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video