코드 작성에서 작업 설계로

2024년 초 입사했을 때를 떠올려 본다. GPT-4o 출시 직전이었던 것으로 기억한다. 그때도 LLM은 코드 작성에 꽤 유용한 보조 수단이었지만, 지금과는 결이 달랐다.

예를 들어 Spring Boot로 API를 하나 만든다고 하자. controller–facade–service–repository로 이어지는 예시 API를 먼저 내가 직접 완성해 두고, 그 코드를 통째로 붙여 넣은 뒤 같은 컨벤션과 로직으로 다른 API를 만들어 달라고 요청하곤 했다. 당시 LLM은 설계자라기보다 재현 장치에 가까웠다. 사람이 길을 닦아 두면 그 궤적을 그럭저럭 따라 그리는 수준이었다.

막힐 때 질문도 던졌다. 하지만 환각이 잦았고, 복잡도가 조금만 올라가도 문법 오류와 논리 결함이 곳곳에서 터졌다. 당시의 ‘정상적인’ 절차는 여전히 공식 문서를 뒤지고, 라이브러리 코드를 확인하고, 에러 로그를 읽고, 직접 디버깅하는 일이었다. LLM은 보조 도구였지, 일을 위임할 수 있는 독립 주체는 아니었다.

이후 모델 성능은 꾸준히 올라갔다. 어느 순간부터는 직접 찾아보는 시간보다 답변에 의존하는 시간이 조금씩 더 길어졌다. 그리고 코딩 에이전트가 등장했다. 처음 제대로 써본 것은 Gemini 계열이었는데, 솔직히 성능은 조악했다. 로컬 파일을 직접 수정하는 기능은 신기했지만, 내가 직접 할 때보다 훨씬 느렸고 결과물도 불안정했다. 이때부터 나름의 하네스를 설계했다. 작업을 잘게 나누고, 규칙을 명시하고, 테스트를 강제하며, 수정 범위를 제한했다. 그래도 사람이 직접 작성한 코드의 완성도에는 미치지 못했다.

특히 컨벤션이 자리 잡은 레포지토리에서도 같은 문제가 반복됐다. 구조를 이해하지 못한 채 파일을 건드리고, 비슷해 보이는 코드들을 억지로 이어 붙였고, 컴파일 단계에서는 거의 늘 문제가 터졌다. 결국 개발자가 다시 읽고 고쳐야 했다. 에이전트가 코드를 쓰더라도 책임은 여전히 전부 개발자에게 돌아왔다.

그러다 2026년 초 퇴사할 즈음, Codex 계열의 새 모델들을 쓰기 시작하면서 체감이 완전히 달라졌다. 코드는 훨씬 정교해졌고, 작업 후 컴파일까지 직접 확인했으며, 단순 구현을 넘어 수정과 검증까지 하나의 흐름으로 처리하기 시작했다. 이제는 한 작업을 에이전트에 맡겨두고 나는 다른 일을 병행할 수 있었다. 과장을 조금 보태면 예전 대비 두세 배의 효율도 가능했다.

복학 후 여러 프로젝트를 하면서 변화는 더 가팔라졌다. 한두 달에 한 번꼴로 새 에이전트와 모델이 나오고, 성능과 속도는 눈에 띄게 올라갔다. 작업 뒤 개발자가 손볼 부분은 줄었고 코드는 점점 더 치밀해졌다. 월 단위, 심하면 주 단위로 따라가지 않으면 도태된다는 감각마저 들었다. 이제는 코딩 에이전트 없이 코드를 쓰는 쪽이 오히려 부자연스럽다.

작업 방식도 완전히 달라졌다. 브랜치 컨벤션과 Git Flow를 명시해 두고 여러 작업을 병렬로 진행한다. PR 리뷰도 전부 직접 읽기보다 커스텀 대시보드로 타인의 코드, 정확히는 타인의 에이전트가 작성한 코드까지 훑는다. MCP가 뜨면서 Notion, Figma, 브라우저, 각종 커넥터를 엮어 예전엔 창을 옮겨 다니며 하던 일들을 하나의 워크플로로 압축할 수 있게 됐다.

개발자의 격차는 어디에서 생기는가

서버 장애 대응도 비슷하다. 예전에는 문제가 생기면 직접 Prometheus 쿼리를 쓰고, Grafana 대시보드를 확인하고, CloudWatch 메트릭을 뒤지고, Datadog APM에서 JVM flame graph와 API trace를 따라가며 원인을 좁혔다. 데이터베이스 문제가 나면 하루 종일 쿼리 콘솔에 붙어 프로세스를 확인하고, 비용이 큰 쿼리를 찾아내 실행 계획을 읽고, 인덱스와 쿼리 구조를 손봤다. 이제는 이 과정의 상당 부분을 에이전트에 위임할 수 있다. MCP 서버로 메트릭을 읽게 하고, 인프라 서버에 터널을 열어 Java 프로파일 캡처를 수행하게 하며, 결과 분석을 보조하게 만든다. 사람이 하던 반복적 탐색과 텍스트 분석의 상당 부분이 압도적으로 짧은 시간에 끝난다.

이 발전은 경이로웠지만 동시에 허무했다.

내가 잘하던 일들을 이제는 웬만하면 누구나 할 수 있게 된 듯했다. 코드를 읽고, 라이브러리를 뜯어보고, 하루 종일 디버깅하고, 공식 문서를 뒤져 쌓아온 시간이 무용해 보였다. 예전에는 그것이 경쟁력이었다. 남들보다 더 빨리 파악하고, 더 정확히 고치고, 더 안정적인 구조로 구현하는 능력. 그런데 AI가 그 능력을 빠르게 모방하기 시작했다. 내가 쌓은 기술의 희소성이 사라지는 듯했다.

하지만 여러 개발자와 협업하며 생각이 조금씩 바뀌었다. AI는 격차를 줄이기보다, 오히려 그 격차를 더 선명하게 드러냈다.

많은 사람이 프롬프트의 중요성을 말한다. 나도 뼈저리게 느낀다. 그러나 여기서 말하는 프롬프트는 단순히 문장을 유려하게 쓰는 능력이 아니다. 작업의 본질을 이해하고, 결정해야 할 지점을 식별하고, 코드가 놓일 시스템의 맥락을 파악한 뒤, 에이전트가 임의로 추론하지 못하도록 조건을 정교하게 부여하는 능력이다. 결국 좋은 프롬프트는 좋은 개발 지식에서 나온다.

가령 간단한 로그인 페이지를 만든다고 하자. 프론트엔드와 백엔드를 포함해 “로그인 페이지 뷰와 뒷단 로직을 작성하라”고만 해도, 지금의 AI는 5분 안쪽으로 작동하는 로그인 기능을 만들어낼 수 있다. 문제는 바로 그것이 “작동한다”는 점이다. 너무 그럴듯하게 작동하기 때문에, 무엇이 누락되었는지 모르면 그대로 넘어가게 된다.

그러나 로그인은 단순히 이메일과 비밀번호를 입력받고, 서버에 POST 요청을 보내고, 성공하면 메인 화면으로 보내는 기능이 아니다. 세션을 어떻게 관리할 것인지, JWT를 쓴다면 access token과 refresh token의 생명주기를 어떻게 설정할 것인지, 쿠키를 쓴다면 HttpOnly, Secure, SameSite 정책을 어떻게 가져갈 것인지, 비밀번호는 어떤 방식으로 해싱하고 저장할 것인지, 서버 단 인증과 권한 검사는 어디에서 수행할지, credential stuffing과 brute force 공격에 어떻게 대응할 것인지, 로그인 실패 횟수와 rate limit은 어떻게 처리할 것인지, 사용자 역할별 접근 권한은 어떻게 분리할 것인지, 로그아웃과 토큰 폐기는 어떻게 보장할 것인지, 에러 메시지는 보안과 사용성 사이에서 어디까지 노출할 것인지 결정해야 한다.

에이전트와 일하는 방식

이런 것들은 로그인 페이지를 직접 구현해보지 않았다면 쉽게 떠올리기 어렵다. AI는 답을 낼 수 있다. 그러나 무엇을 물어봐야 하는지는 개발자가 알아야 한다. 무엇을 결정해야 하는지 모르는 사람은 AI가 채워 넣은 추론을 그대로 받아들이게 된다. 그 순간 코드는 “완성”된 것이 아니라, 보이지 않는 기술 부채를 품은 채 레포지토리에 편입된다.

그래서 나는 “AI의 기여도”를 독립적인 값처럼 보는 관점이 부정확하다고 느낀다. 코드 품질의 기여도를 100으로 놓고 보면, 과거에는 개발자 역량 90에 LLM 도움 10이 더해졌고 이제는 AI가 90을 차지하고 개발자는 10만 담당한다고 말하기도 한다. 그러나 나는 그렇게 생각하지 않는다. AI가 만들어내는 90의 품질은 하늘에서 따로 떨어지는 값이 아니다. 여전히 개발자의 사전 지식, 시스템 이해도, 요구사항을 분해하는 능력, 그리고 AI를 다루는 능력 위에 성립한다. 그 기반이 없으면 AI가 만든 90은 품질이 아니라 그럴듯한 추측의 묶음이 된다.

오히려 격차는 더 확대될 수 있다. 예전에는 뛰어난 개발자와 평범한 개발자의 차이가 코드 작성 속도와 디버깅 능력에서 드러났다. 이제는 그 차이가 에이전트를 다루는 방식에서 드러난다. 어떤 사람은 AI에게 “로그인 만들어줘”라고 말하고, 어떤 사람은 인증 정책, 보안 요구사항, 에러 처리, 테스트 범위, 기존 아키텍처와의 통합 방식까지 정리한 뒤 작업을 맡긴다. 두 사람 모두 AI를 쓴다. 그러나 결과물은 완전히 다르다.

채용 시장 이야기도 비슷하다. 요즘 기존 개발자들의 능률이 올라 신입 개발자가 필요 없어졌고, 그래서 시장이 힘들어졌다는 말을 자주 듣는다. 물론 어느 정도 타당한 말이다. 숙련된 개발자 한 명이 에이전트를 능숙하게 활용하면 예전보다 훨씬 많은 일을 처리할 수 있다. 단순 구현만 담당하던 인력의 필요성은 줄어들 수밖에 없다.

그러나 현업자들의 말을 들어보면, 단순히 “신입을 덜 뽑는다”는 문제만은 아닌 것 같다. 뽑을 만한 사람이 줄었다는 느낌도 강하다. 개발이 너무 쉬워진 것처럼 보이기 때문이다. 코드를 깊게 파고들지 않아도 결과물이 나온다. 직접 구현해본 적이 없어도 프로젝트를 완성할 수 있다. 그러나 그 과정에서 공학적 사고를 제대로 훈련하지 못하면, 코드가 왜 그렇게 작성되었는지 이해하지 못한다. 에러가 발생했을 때 원인을 좁히지 못하고, 구조가 충돌할 때 어디서부터 풀어야 할지 모른다. AI가 없으면 못 짜는 것이 문제가 아니다. AI가 짠 코드조차 제대로 독해하지 못하는 것이 문제다.

물론 코드 공부 자체도 훨씬 어려워졌다. 예전처럼 AI를 쓰지 않고 밑바닥부터 구현하며 로직을 이해하는 방식이 이상적이라고 말할 수는 있다. 나도 그것이 중요하다고 생각한다. 하지만 바로 앞에 내가 하지 못하는 작업을 열 배의 속도로 수행하는 도구가 있는데, 어떻게 그것을 외면할 수 있겠는가. AI를 쓰지 말자는 말은 현실적이지 않다. 이제 중요한 것은 AI를 쓰느냐 마느냐가 아니라, AI를 쓰면서도 사고를 상실하지 않는 방식이다.

AI가 없던 시절의 개발 공부는 대체로 bottom-up 접근에 가까웠다. 기초 문법을 익히고, 자료구조와 네트워크와 데이터베이스를 배우고, 작은 기능을 직접 만들고, 점점 더 복잡한 시스템으로 올라갔다. 그것이 항상 가장 효율적인 방법이어서라기보다, 다른 선택지가 별로 없었다. 기초가 없으면 높은 수준의 코드, 정확히는 복잡한 책임과 추상화를 가진 코드를 작성할 수 없었기 때문이다.

그런데 AI가 등장하면서 top-down 접근이 현실적인 공부법이 되었다. 먼저 고수준의 코드를 만들게 하고, 그 코드가 왜 그렇게 구성되었는지 분석하고, 이해가 막히는 지점을 역으로 파고드는 방식이다. 공부하는 입장에서는 이 흐름이 시간을 압도적으로 단축시킬 수 있다. 최근 LLM에 대한 관찰과 함께 Claude Code의 성능을 높이는 CLAUDE.md 같은 문서 작성 문화를 유행시킨 Andrej Karpathy도 2020년에 비슷한 말을 했다. 구체적인 프로젝트를 수행하면서 필요한 지식을 그때그때 깊게 학습하고, 배운 것을 자기 언어로 설명하라는 내용이었다. 당시에는 이상적이지만 실용성은 애매한 조언처럼 보였을 수 있다. 지금은 AI가 그 방식을 훨씬 현실적인 학습 전략으로 만들고 있다.

Andrej Karpathy가 2020년에 올린 on-demand learning 관련 트윗

내가 그나마 실천하려고 하는 방법이 있다. 작업을 시키기 전, 요구사항을 최대한 상세히 적는다. 그리고 마지막에 반드시 이런 질문을 덧붙인다.

이 작업을 수행하기 위해 내가 사전에 결정해야 하는 것이 무엇인가? 어떠한 작업 플로우나 로직도 추측하지 말고, 모든 과정을 내게 상세히 검토받아라.

이 질문이 중요한 이유는 AI가 빈칸을 그대로 비워두지 않기 때문이다. 개발자가 프롬프트에 포함시키지 않은 지시는 AI가 추측할 수밖에 없다. AI는 자신이 보기에 현재 상황에 가장 적합해 보이는 방향으로 구현을 진행한다. 하지만 각 프로젝트에는 고유한 개발 방향이 있고, 데이터 모델과 인증 방식, 배포 구조, 장애 대응 방식, 앞으로의 확장성까지 고려하면 “옳은 방향”은 일반론만으로 결정되지 않는다. 그 판단은 결국 해당 시스템을 책임지는 개발자만이 할 수 있다.

좋은 프롬프트는 AI를 덜 쓰겠다는 뜻이 아니다. 오히려 AI가 강점을 발휘해야 할 실행 영역과, 개발자가 사전에 결정해야 할 설계 영역을 분리하는 일에 가깝다. 프롬프트가 빈약하면 사람은 10%만 결정하고 나머지 90%는 AI의 추측이 된다. 반대로 프롬프트가 정교하면 개발자가 맥락, 제약, 금지 사항, 검토 기준의 90%를 먼저 제공하고 AI는 남은 10%의 전술적 판단만 보완한다. 두 경우 모두 AI가 코드를 쓰지만, 결과물의 성격은 전혀 다르다.

프롬프트 품질에 따라 달라지는 요구사항과 AI 추측의 비율

책임은 더 위로 올라간다

이 질문 하나만 붙여도 결과가 크게 달라진다. 프롬프트를 아무리 자세히 썼다고 생각해도, 항상 내가 놓치고 있던 부분이 튀어나온다. 아예 생각해보지 못했던 절차가 질문으로 돌아오기도 한다. 그 질문들을 다시 공부하고, 의사결정을 내려야 한다. 그래야 같은 작업을 두 번, 세 번, 열 번 다시 시키지 않는다.

작성된 코드를 이해하는 것도 중요하다. 에이전트가 만든 코드라고 해서 읽지 않아도 되는 것이 아니다. 오히려 더 면밀히 읽어야 한다. 첫 작업은 언제나 그럴듯하다. 새 레포지토리에서 첫 기능은 깔끔하다. 그러나 그 구조를 이해하지 못한 채 후속 작업을 계속 맡기면, 어느 순간부터 스파게티 코드가 축적된다. 로직은 충돌하고, 책임은 뒤섞이고, 예외 처리는 중복되고, 같은 개념이 여러 이름으로 분산된다. 처음에는 빠르게 가는 것처럼 보이지만, 나중에는 걷잡을 수 없이 붕괴한다.

AI 시대의 개발자는 코드를 덜 쓰게 될 수 있다. 그러나 덜 생각해도 되는 것은 아니다. 오히려 더 많이 생각해야 한다. 직접 타이핑하는 시간은 줄어들지만, 무엇을 만들 것인지, 어떤 구조가 적합한지, 어떤 결정을 유예하면 안 되는지, 어떤 추론을 차단해야 하는지 판단해야 한다. 구현 노동은 줄어들지만, 설계와 검토와 책임은 더 선명해진다.

그래서 이제 개발자와 코더의 차이는 더 분명해지는 것 같다.

코더는 AI가 작성한 코드를 받는다. 개발자는 AI가 어떤 코드를 작성해야 하는지 결정한다. 코더는 작동하면 끝났다고 생각한다. 개발자는 왜 작동하는지, 어디서 깨질 수 있는지, 다음 기능이 붙었을 때 어떤 구조가 버틸 수 있는지 본다. 코더는 프롬프트를 명령문처럼 쓴다. 개발자는 프롬프트를 설계 문서처럼 쓴다.

AI가 코드를 쓰는 시대가 왔다. 이 말은 더 이상 개발자가 필요 없다는 뜻이 아니다. 오히려 개발자가 무엇을 알고 있는지가 더 중요해졌다는 뜻에 가깝다. 예전에는 모르는 것을 직접 부딪히며 배웠다. 이제는 모르는 것을 AI가 너무 빨리 가려버린다. 그래서 더 의식적으로 파고들어야 한다. AI가 채워준 답을 보는 것이 아니라, AI가 무엇을 추론했는지 확인해야 한다.

결국 경쟁력은 사라진 것이 아니다. 위치가 바뀌었을 뿐이다. 코드를 얼마나 많이 치느냐에서, 어떤 코드를 작성해야 하는지 얼마나 정확히 정의하느냐로 이동했다. 그리고 이 변화는 생각보다 냉정하다. AI는 모두에게 열려 있지만, 모두가 같은 결과물을 얻지는 못한다.