시리즈: AI와 함께 코딩 배우기

종이 위에 SDK 폴더 구조 스케치하기

코드가 하나도 존재하기 전에 그려진 SDK 뼈대.

폴더 구조가 곧 스펙이다 에이전트들이 써넣어야 할 상자를 그려라 /sdk /apps /modules /assets /docs /config arena_demo/ coach_demo/ hud_designer/ ...데모 8개 hud gesture gaze sync fx voice widgets audio gesture icons README guides API refs layouts roles 구현이 존재하기 전에 스텁이 계약을 고정한다
구조가 곧 규율이다 - 에이전트는 자신이 써넣는 상자를 통해 그 규율을 물려받는다.

에이전트가 만든 코드베이스는 석 달 만에 죽이 되거나, 몇 년간 일관성을 유지하거나 둘 중 하나다 - 그리고 그 차이는 단 한 줄이 쓰이기 전에 결정된다. RakuAI는 에이전트들이 항상 올바른 자리에 안착하도록 먼저 상자를 그렸다.

바깥에서 보면 보이지 않지만 안에서는 구조를 떠받치는 종류의 설계 작업이 있다. 코드베이스의 폴더 구조가 그런 것 중 하나다. 언뜻 파일 관리처럼 보인다. 실제로는 코드의 조직도다. 초반에 제대로 해두면 향후 2년간의 모든 PR이 올바른 자리에 안착한다. 초반에 잘못하면 향후 2년간의 모든 PR이 자기가 어디에 속하는지를 두고 논쟁해야 한다.

이번 토요일은 초반에 제대로 해두는 데 쓰였다.

구조가 지원해야 하는 것

상자를 그리기 전에, 구조가 충족해야 하는 제약 조건들의 목록을 만들었다.

각 데모는 자기만의 자리를 갖는다. 지난달에 명세한 여덟 개 데모는 각각 코드, 에셋, 문서가 함께 사는 폴더가 필요하다. 어떤 데모든 그것을 읽는 개발자가 그것을 이해하기 위해 SDK의 네 군데 다른 부분을 넘나들 필요는 없어야 한다.

공유 모듈은 데모와 분리 가능하다. 데모들 사이에서 재사용되는 것(HUD 렌더링, 제스처 입력, 시선 추적, 멀티플레이어 동기화, 음성 제어, 시각 효과)은 자기만의 자리에 살고, 공개된 표면을 통해 데모들이 소비한다.

에셋은 데모별이 아니라 타입별로 조직된다. 체력바 PNG는 HUD 위젯이지, Arena Demo 에셋이 아니다. 재사용 가능한 시각 에셋은 공유 에셋 폴더에 살고, 그것이 필요한 데모들이 거기로 링크한다.

문서는 그것이 설명하는 것과 같이 위치한다. SDK 설치 문서는 SDK 옆에 산다. 데모 가이드는 데모들 옆에 산다. HUD API 문서는 HUD 모듈 옆에 산다. 문서의 발견 용이성은 대부분 문서의 위치에 관한 것이다.

설정은 그 자체로 하나의 관심사다. HUD 설정 파일, 기본 레이아웃, 멀티플레이어 역할 설정. 이것들은 코드도, 에셋도, 문서도 아니다. 이들은 자기만의 자리를 갖는다.

구조

아침 내내 화이트보드 작업을 한 결과, 레이아웃은 이렇게 정리되었다.

루트 폴더는 SDK 자체다. 루트 아래로:

/apps/는 데모들이 사는 곳이다. 각 데모는 하위 폴더를 갖는다. 솔로 아레나를 위한 arena_demo/. AR 코칭을 위한 coach_demo/. HUD 오버레이 디자이너를 위한 hud_designer/. 스트리머 모드를 위한 streamer_demo/. 매장 접점 오버레이를 위한 pos_demo/. AR 컴패니언 HUD를 위한 companion_demo/. AR 에임 트레이너를 위한 aimlab_demo/. 멀티플레이어 HUD 동기화를 위한 hudsync_demo/. 각 데모 폴더는 자신의 소스, 자신의 설정, 자신만의 데모 전용 에셋, 그리고 자신의 README를 담고 있다.

/modules/는 공유 코드가 사는 곳이다. 시작 시점에는 여섯 개의 모듈이 있다. 모든 데모가 소비하는 HUD 렌더러. 대부분의 데모가 소비하는 제스처 입력 감지. 필요한 데모들이 사용하는 시선 추적. LAN 멀티플레이어 데모들이 사용하는 멀티플레이어 동기화. 시각 효과를 위한 오버레이 애니메이터. 음성 입력을 받는 데모들을 위한 음성 제어 인터페이스. 각 모듈은 데모들이 링크할 수 있는 작고 안정적인 공개 표면을 노출한다.

/assets/는 공유 에셋 라이브러리다. PNG와 SVG 형태의 HUD 위젯(체력바, 탄약 카운터, 나침반 오버레이). 히트, 알림, 보이스오버를 위한 오디오 효과. 제스처가 감지되었을 때 사용자에게 보여주는 제스처 아이콘. 데모들은 에셋을 복제하는 대신 여기로 링크한다.

/docs/는 문서의 본거지다. SDK를 소개하고 설치 과정을 안내하는 README. 여덟 개 데모 각각을 실행하는 방법을 설명하는 데모 가이드. HUD 모듈을 위한 API 레퍼런스. 음성 제어 모듈을 위한 API 레퍼런스. 문서는 HUD가 어떻게 작동하는지 알고 싶은 개발자가 같은 폴더에서 소스와 문서를 함께 읽을 수 있도록 의도적으로 같이 위치시켰다.

/config/는 설정이다. 새 프로젝트의 출발점으로 쓰이는 기본 HUD 레이아웃. HUD 디자이너에서 내보낸 샘플 레이아웃. 멀티플레이어 역할 정의. 설정을 코드와 분리해 두면 데모가 깔끔하게 유지되고, 개발자가 재컴파일 없이 동작을 바꿀 수 있다.

스텁

폴더 구조 그 자체는 그저 빈 파일 시스템일 뿐이다. 오늘 초안을 잡은 또 다른 것은, 에이전트들이(결국 이 구조에 맞춰 코드를 쓰기 시작할 때) 각 진입점에서 실제 구현이 어떤 모습이어야 하는지 보여주는 코드 스텁 세트다.

스텁은 지금은 파이썬 의사코드로 되어 있다. 엔진 저장소가 열리면 프로덕션 언어(런타임을 위한 C++, 파이썬 및 다른 생태계로의 바인딩 포함)로 번역될 것이다. 파이썬 형태는 구현 세부사항에 빠지지 않고 인터페이스에 대해 사고할 수 있게 해준다.

제스처 입력 모듈을 위한 샘플 스텁은 대략 이렇다.

def detect_gesture(frame):
    """Detect gestures like swipe, point, raise, duck in a single sensor frame."""
    if is_swipe(frame):
        return "swipe"
    elif is_raise(frame):
        return "raise_arm"
    else:
        return None

이것은 프로덕션 코드가 아니다. 계약이다. 프로덕션 코드는 is_swipeis_raise를 실제 컴퓨터 비전 파이프라인으로 대체할 것이다. 인터페이스는 그대로 유지된다. 시그니처가 데모들이 코딩할 대상이다. 시그니처를 일찍 고정한다는 것은, CV 파이프라인이 완벽해지기 전에 데모를 출시할 수 있다는 뜻이다. 데모는 구현이 아니라 인터페이스에 의존하기 때문이다.

나는 /modules/ 폴더의 모든 모듈에 대해 이런 형태의 스텁 초안을 잡았다. 여섯 개의 작은 파이썬 파일. 그중 어느 것도 아무것도 구현하지 않는다. 모두 결국 나올 구현들이 지켜야 할 계약을 정의한다.

에이전트들에게 이것이 왜 필요한가

이것이 몇 주 전의 에이전트 명단으로 이어지는 연결점이다.

실제 코드를 쓸 시점이 오면, 에이전트들은 아키텍처를 새로 발명하지 않을 것이다. 아키텍처는 종이 위에 있다. 폴더 구조는 새 코드가 어디로 가는지 알려준다. 모듈 스텁은 어떤 인터페이스를 구현해야 하는지 알려준다. 에셋 조직은 필요한 것들을 어디서 찾아야 하는지 알려준다. 문서 구조는 자신이 쓴 문서를 어디에 두어야 하는지 알려준다.

그것이 석 달 후 죽이 되어버리는 에이전트가 만든 코드베이스와, 몇 년 동안 일관성을 유지하는 코드베이스의 차이다. 구조가 곧 규율이다. 에이전트는 구조를 통해 그 규율을 물려받는다.

이 사전 작업이 없다면, “제스처 입력을 구현하라”는 요청을 받은 에이전트는 폴더를 발명하고, API를 발명하고, 코드를 쓰고, 독립적으로는 작동하는 무언가를 만들어낼 것이다. 여섯 개의 모듈에 걸쳐 곱해지면, 그 접근 방식은 서로 호환되지 않는 여섯 개의 미니 엔진을 만들어낸다. 구조는 에이전트들이 써넣어야 할 상자를 그려둠으로써 그 실패 양상을 방지한다.

여기서 어려운 것

몇 가지 솔직한 이야기.

폴더 구조는 성장하려 한다. 6개월 후 나는 일곱 번째 모듈을 추가하고 싶어질 것이다. 구조는 잡동사니 서랍이 되지 않으면서도 그것을 허용해야 한다. 내가 스스로에게 부과하는 규칙: 새로운 최상위 폴더는 그것이 기존 폴더의 하위 폴더가 아닌 이유를 정당화해야 한다. 대부분의 새로운 것들은 결국 하위 폴더로 밝혀진다.

스텁은 유지보수가 필요하다. 실제 구현이 안착함에 따라, 스텁은 낡아간다. 스텁을 “계약”으로 읽는 에이전트들은 스텁과 실제 구현 사이의 어떤 드리프트든 물려받게 된다. 해결책은 스텁을 정본 소스로 삼고, 계약이 바뀔 때 실제 구현이 그것을 업데이트하도록 요구하는 것이다.

이 폴더들 중 일부는 추측이다. /config/ 폴더는 런타임 설정 파일이 이 SDK에서 진짜 아티팩트 카테고리가 될 것이라는 베팅이다. 만약 그렇지 않은 것으로 드러나면, 그 폴더는 줄어들거나 다른 것에 합쳐질 것이다. 나는 그것을 받아들일 수 있다. 나중에 폴더를 제거하는 것이, 관습이 이미 표류해버린 1년 후에 새로 발명하는 것보다 저렴하다.

다음은 무엇인가

다음 토요일은 API 표면 설계다. 위의 여섯 개 모듈 각각은 공개 표면이 명세되어야 한다. 그것은 어떤 데이터 타입이 경계를 넘나드는지, 어떤 에러 코드가 반환되는지, 어떤 이벤트가 발생하는지를 결정하는 것을 의미한다. 폴더 구조가 끝났으니, API 설계는 안착할 곳이 생겼다.

그다음은 각 데모가 어떤 모듈을 어떤 순서로 소비하는지에 대한 데모별 명세로 이어진다. 그다음은 런타임 아키텍처. 그리고 마침내, 엔진 저장소가 열린다.

특허 자산은 10년을 기다려왔다. 제대로 안착시키기 위한 몇 주 더의 설계 작업이 우리에게서 그 창을 앗아가지는 않을 것이다.

폴더 구조 완료. 코드베이스는 이제 형태를 갖췄다, 그 안에 아직 코드는 하나도 없지만.

발밑에서 흔들리지 않을 SDK

바뀌지 않을 폴더 구조, 구현 전에 고정된 API 표면 - 장기전을 위해 설계된 공간 런타임 위에 만드세요.

← 전체 글