코드를 직접 타이핑하라

1 week ago 7
  • 코드를 직접 타이핑하고 기억으로 다시 만드는 훈련은 복사보다 이해와 기억을 더 엄격하게 검증해 줌
  • Freecoding은 구문, 타입, 이름을 머릿속에 유지한 채 문자 단위로 코드를 작성하는 능력이며 도구 시대에도 필요함
  • 구문은 고수준 사고를 방해하는 잡음이 아니라, 정밀한 의미를 압축해 더 높은 수준의 사고를 가능하게 함
  • 타입과 스키마를 느슨하게 조회만 하면 시스템 설계가 흐려지고, 타입 모델을 이해하지 못하면 as any 같은 회피가 늘어남
  • 이름 기억과 선행 작업 이해가 부족하면 에이전트가 중복 구현을 만들기 쉽고, 출력과 테스트 검토도 어려워짐

코드를 직접 타이핑해야 하는 이유

  • Zed Shaw의 “Learn X the hard way” 과정은 예제를 복사·붙여넣기하지 말고 직접 입력하라고 권했고, 최근에는 연습을 끝낸 뒤 삭제하고 기억으로 다시 만들어보는 방식을 더 강하게 권장함
  • 능동적으로 내용을 생성하면 같은 내용을 수동적으로 소비할 때보다 이해가 좋아지는 현상은 인지심리학에서 생성 효과로 불림
  • Richard Feynman의 “내가 만들 수 없는 것은 이해하지 못한다”는 말처럼, 기억을 바탕으로 코드를 다시 만드는 훈련은 이해와 기억을 동시에 시험함
  • 에이전트 코딩 시대에도 개발 도구를 쓰지 말아야 한다는 뜻은 아니지만, 때때로 도구의 편의 없이 문자 단위로 코드를 써보는 능력을 길러야 함
  • 이런 훈련은 구문, 타입, 이름처럼 프로그래밍의 세부 요소를 머릿속에 유지해 “freecoding”할 수 있게 만드는 데 초점을 둠

Freecoding과 머릿속 세부 지식

  • Freecoding은 기억을 바탕으로 코드를 직접 작성할 수 있는 능력이며, 구문·타입·이름 같은 기본 요소를 머릿속에 유지해야 가능함
  • 구문과 구조

    • 키워드, 문장부호, 언어 구성요소에 익숙해야 함
    • 단순한 문법 암기가 아니라, 코드의 형태를 정확히 떠올리는 능력과 연결됨
  • 타입과 스키마

    • 타입 시스템과 데이터 모델에 익숙하고 편안해야 함
    • 프로젝트의 테이블, 컬럼, 관계, 타입 구조를 매번 조회하는 수준에 머무르면 시스템 수준 설계가 어려워짐
  • 이름

    • 함수, 메서드, 클래스, import, 파일, 패키지 이름을 정확히 떠올릴 수 있어야 함
    • 프로젝트와 의존성이 바뀌면 이 지식도 함께 최신 상태로 유지해야 함
    • 머릿속에 떠올린 코드를 일정 수준의 정밀도로 타이핑하지 못한다면, 실제로는 그 내용을 명확히 이해한 것이 아니라 이해한다고 착각한 상태에 가까움
    • 영어는 코드만큼 정밀한 언어가 아니며, 충분히 상세한 명세는 결국 코드에 가까워진다는 별도 글 A sufficiently detailed spec is code와도 연결됨

구문은 고수준 사고를 가능하게 함

  • IDE나 코딩 에이전트가 구문을 맞춰줄 수 있어도, 구문을 직접 다루는 능력은 여전히 중요함
  • 괄호를 맞추는 데 어려움을 겪는다면, 다른 사람의 논리적 전제와 결론을 유창하게 연결하는 능력에도 의문이 생길 수 있음
  • 세부 사항을 무시하는 태도는 단어의 의미를 명확히 따지기보다 분위기로 소통하는 기능적 비문해성과 이어질 수 있음
  • LLM 프롬프트 예시는 겉보기에는 그럴듯하지만, 자세히 읽으면 서로 반대되는 지시를 동시에 포함함
    • “위에 나열된 skill에 포함되지 않은 외부 도구나 대안을 제안하지 말라”
    • “작업이 사용 가능한 skill을 넘어서는 능력을 요구하면 그렇게 말하라”
  • 작은 문법·철자·구조를 정확히 다루는 능력과 큰 그림을 정확히 이해하는 능력은 분리되지 않음
  • 구문은 고수준 사고를 방해하는 세부사항이 아니라, 더 높은 수준의 사고를 압축하고 가능하게 하는 정신적 도구임
  • 자연어 설명보다 타입 표기가 더 정밀하고 간결할 수 있음 x는 객체의 배열이며, 각 객체에는 문자열을 저장하는 필수 domain 속성과 숫자를 저장하는 선택적 port 속성이 있다 x : { domain: string, port?: number }[]

타입과 스키마는 시스템 설계의 핵심 단서

  • Fred Brooks는 The Mythical Man-Month에서 테이블을 보여주면 흐름도 없이도 대개 구조가 명확해진다고 말함
  • 데이터베이스를 사용한다면 프로젝트의 테이블, 컬럼 이름, 관계를 앞뒤로 꿰고 있어야 함
  • 이 정보를 필요할 때마다 느슨하게 조회하는 대신, 사전에 파악하고 머릿속에 유지해야 효과적인 시스템 수준 설계가 가능함
  • 이런 노력을 하지 않으면 생각이 혼란스러운 만큼 데이터베이스 스키마도 중복·유사 데이터가 많은 비정규화 구조가 되기 쉬움
  • Rust나 Haskell 같은 강한 타입 언어 경험은 타입에 대한 강한 정신 모델의 이점을 잘 드러냄
  • 정확한 “정신적 타입 검사기”나 “정신적 borrow checker”를 기르는 훈련은 약한 타입 언어를 사용할 때도 도움이 됨
  • 이런 추상적 추론 근육을 쓰지 않으면 불가능한 상태를 표현 불가능하게 만들기 같은 데이터 모델링 기본도 놓치기 쉬움
  • 타입이 어떻게 맞물리는지 이해하지 못하면 TypeScript 코드에 as any를 흩뿌리게 됨
  • 정상 경로에서 타입을 생각하는 불편함을 감당하지 못하면, 타입 오류를 디버깅하는 비정상 경로는 더 견디기 어려움
  • Haskeller나 Rustacean이 항상 더 나은 코드를 쓴다는 뜻은 아니지만, 다른 조건이 같다면 타입에 유창한 것이 도움이 됨

이름을 기억해야 재사용할 수 있음

  • 프로젝트나 의존성에서 자주 쓰는 함수, 메서드, 클래스, import, 패키지, 파일 이름은 쉽게 떠올릴 수 있어야 함
  • 이는 이미 만들어진 것, 즉 선행 작업(prior art) 에 익숙해야 한다는 더 일반적인 원칙의 특수한 형태임
  • 많은 사람이 코딩 에이전트에 기대는 이유는 원하는 기능을 이미 수행하는 재사용 가능한 선행 작업을 모르기 때문이며, 그 결과 에이전트에게 바퀴를 다시 만들게 함
  • SaaS boilerplate projects의 존재를 모르면 에이전트가 처음부터 스캐폴딩해야 한다고 생각하기 쉬움
  • 이 목적을 위해 만들어진 검증된 오픈소스 프로젝트를 복제하는 것이 에이전트에게 같은 작업을 요청하는 것보다 더 빠르고, 싸고, 신뢰할 수 있음
  • 누군가 LLM이 몇 년 안에 브라우저 전체를 만들 수 있을 것이라고 예측했을 때, Chromium을 포크하면 이미 오늘 가능하며 수정과 유지보수도 더 쉽다는 반론이 가능함
  • 같은 프로젝트나 회사 내부의 코드 재사용에도 이름 기억은 중요함
  • 무엇을 찾아야 하는지 모르면 동료의 작업 위에 쌓아 올릴 수 없음

에이전트 출력 검토에는 사람의 이해가 필요함

  • 에이전트는 이름 탐색과 코드 생성에 도움을 줄 수 있지만, 출력물을 의미 있게 검토할 수 있는지가 새 문제가 됨
  • 기존 기능을 모르면 에이전트가 기능을 중복 구현하는지 판단하기 어려움
  • 에이전트보다 코드를 더 못 이해하는 상태에서는 에이전트 출력의 품질도 제대로 검토하기 어려움
  • 에이전트에게 테스트 생성을 요청할 수는 있지만, 생성된 테스트를 면밀히 살피지 않으면 테스트 자체가 의미 없는 코드가 될 수 있음
  • 실제 예시는 구현 코드를 호출하지 않고 배열과 문자열에 직접 some이나 includes를 적용해 기대값만 확인하는 테스트였음 describe("abort detection logic", () => { it("detects aborted stopReason in messages", () => { const messages = [ { role: "assistant", stopReason: "aborted", content: [] }, ]; const isAborted = messages.some((m: any) => m.stopReason === "aborted"); expect(isAborted).toBe(true); }); it("detects abort in error string", () => { const error = "The operation was aborted"; const isAborted = error.includes("abort"); expect(isAborted).toBe(true); }); it("does not false-positive on normal errors", () => { const error = "Network timeout"; const isAborted = error.includes("abort"); expect(isAborted).toBe(false); }); it("does not false-positive on normal stop reasons", () => { const messages = [ { role: "assistant", stopReason: "stop", content: [] }, ]; const isAborted = messages.some((m: any) => m.stopReason === "aborted"); expect(isAborted).toBe(false); }); });
  • 소프트웨어 개발에는 일상적인 마찰이 많고, 이름을 기억하는 작은 마찰을 넘지 않기로 선택하면 테스트를 검토하는 더 큰 마찰도 넘지 않게 됨
  • 코딩 에이전트는 사용자의 지시를 주요 맥락으로 삼기 때문에, 지적으로 게으른 사용자는 에이전트도 비슷하게 게으른 방향으로 몰고 갈 수 있음

끈기와 숙련은 분리되지 않음

  • Eustress는 유익한 스트레스이며, 새롭고 어려운 일을 하도록 자신을 밀어붙이면 작은 불편에 대한 내성이 생기고 더 큰 불편도 넘을 수 있게 됨
  • 항상 불편을 피하면 무력감과 좌절이 커지는 악순환으로 내려감
  • 한 영역에서 정밀성, 기억, 구조적 사고를 기르면 다른 영역의 능력도 함께 좋아짐
  • LLM이 훈련 데이터에서 일반화하듯, 인간도 한 영역에서 기른 습관과 능력을 다른 영역으로 일반화함
  • 소프트웨어 개발은 본질적으로 편안한 영역을 정기적으로 벗어나는 일을 포함하며, 관련 글 Software engineers are not (and should not be) technicians와 같은 맥락에 있음
Read Entire Article