Lobsters

The wild west of polyglot docs sites

다국어 문서 사이트의 개척지

여러 언어의 API 문서를 한 사이트에 모으려면 서로 다른 문서 생성기를 연결하고 탐색·검색 경험을 통일해야 합니다. 글쓴이는 변환, 생성 결과물 병합, 수동 작성의 장단점을 살피고 Pigweed에서 Pagefind와 공통 헤더로 검색과 이동을 통합한 과정을 소개합니다.

AI 요약

프로젝트가 여러 프로그래밍 언어의 라이브러리를 제공하면 문서 사이트도 여러 문서 생성기를 다뤄야 합니다. 언어별 API 참조 문서는 Doxygen, rustdoc, Javadoc처럼 각기 다른 도구로 만들어지기 때문입니다. 생성된 문서를 한데 모으더라도 글꼴과 색상, 검색, 탐색 경험은 일관되어야 합니다. 글쓴이는 서로 다른 생성기의 결과물을 하나의 문서 사이트로 엮는 방식을 ‘polyglot docs site’라고 부르며, 아직 정착한 해법이 드문 분야라고 설명합니다.

두 가지 구성 전략

첫 번째는 생성기 결과물을 파싱해 주 문서 생성기가 이해하는 형식으로 변환하는 방식입니다. 글쓴이는 과거 Doxygen HTML을 입력받아 XSLT로 간단한 HTML 조각을 만든 뒤, Sphinx의 raw 지시자로 페이지에 넣었습니다. Breathe도 비슷합니다. Doxygen XML을 먼저 만들고, Sphinx 문서에 지시자를 작성해 API 참조를 삽입합니다.

이 방식은 언어별 생성기가 쌓아 온 지식과 사용 경험을 잃기 쉽습니다. Doxygen이나 rustdoc은 각 언어의 세부 사항을 반영하고, rustdoc 검색은 vec -> usize 같은 질의에서 해당 인자를 받고 그 타입을 반환하는 함수를 찾아 보여줍니다. 변환 과정에서 필요한 정보를 누락하거나, 언어 사용자에게 익숙한 UI 대신 새 인터페이스를 강요할 수도 있습니다.

Pigweed는 2022년부터 2024년까지 C/C++ API 문서에 Breathe를 썼지만, 빌드 속도와 유지 관리 문제로 방식을 바꿨습니다. 정확한 수치는 기억나지 않지만 글쓴이가 제시한 예에서는 Breathe 사용 시 약 90초 걸리던 빌드가 제거 후 약 60초로 줄었습니다. 또 개발자가 코드에 Doxygen 주석을 쓰더라도 .. doxygenclass:: pw::Foo 같은 지시자를 빠뜨리면 문서가 게시되지 않는 문제가 있었습니다. 페이지마다 API 참조를 알파벳순으로 모을지, 설명 주제에 맞춰 배치할지도 기여자마다 달랐습니다.

두 번째는 Doxygen, rustdoc, Sphinx의 출력을 변환하지 않고 그대로 게시하는 방식입니다. 글쓴이는 이를 칠면조 안에 오리와 닭을 넣는 요리에 빗대 ‘turducken’이라고 부릅니다. Pigweed에서는 세 생성기가 각각 C/C++, Rust, 그 밖의 문서를 만들고, 사이트가 결과물을 함께 엮습니다. 생성기별 UI를 유지하는 대신 CSS를 손봐 모양을 맞추지만, 스타일 통일만으로 해결되지 않는 문제가 남습니다. 하위 사이트끼리 이동하기 어렵고, 검색 결과가 생성기별로 분리되며, 상대 경로를 수동으로 지정해야 해 링크가 깨지기 쉽습니다.

Pigweed의 공통 헤더와 검색

Pigweed는 모든 페이지에 같은 상단 링크와 검색창, 이동 경로 표시를 제공하는 공통 헤더를 만들었습니다. 테마 선택도 하위 사이트 사이에서 동기화합니다. 문서 빌드가 끝난 뒤 특정 주석을 전체 헤더 코드로 바꾸는 후처리와, Doxygen 출력에서 이동 경로를 구성하는 로직을 직접 작성했습니다. 예를 들어 API 참조 페이지의 경로 표시 일부는 Sphinx에서, 나머지는 Doxygen 결과물에서 가져옵니다.

검색에는 Pagefind를 씁니다. 빌드된 HTML 디렉터리를 지정하면 검색 인덱스를 만들며, 색인할 콘텐츠는 허용 목록이나 차단 목록으로 조정합니다. 기본 웹 컴포넌트에 약간의 스타일을 더해 검색창을 모달로 열고, 입력에 맞춰 결과를 보여줍니다. 이제 Doxygen, rustdoc, Sphinx 문서가 모두 검색 결과에 포함됩니다. 글쓴이는 Pagefind가 웹 워커, WebAssembly, 인덱스 조각의 필요 시 로딩을 활용한다고 언급하지만, 공식 문서에서 내부 구조를 정리한 설명은 찾지 못했다고 덧붙입니다.

다만 Rust 페이지에는 rustdoc 검색도 그대로 남겼습니다. 사이트 전체에서 같은 검색 경험을 제공하고 싶지만, rustdoc의 검색 기능도 유용하기 때문입니다. 그 결과 Rust 페이지에는 검색창이 두 개 생깁니다. 글쓴이는 아직 어느 쪽이 나은지 정하지 못했고, Rust 사용자에게 두 선택지를 안내하는 방안도 고려합니다.

수동 작성과 개선 제안

글쓴이는 앞서 제시한 두 방식 외에, API 범위가 작고 변경도 드물다면 문서를 직접 작성하는 세 번째 방법도 있다고 정정합니다. 에이전트 도구가 널리 쓰이는 지금은 직접 작성이 실용적일 수 있으며, 변환이나 생성 결과물 병합의 단점을 고려하면 검토할 만한 선택지라고 말합니다.

개선 아이디어로는 CSS @layer를 제안합니다. 사이트 스타일 레이어가 생성기 스타일 레이어보다 우선하도록 순서를 정하면, 수많은 !important 규칙 없이 사이트의 CSS 변수와 스타일을 적용할 수 있습니다. 또 문서 생성기에 완전한 페이지 대신 본문과 필요한 탐색 요소만 내보내는 --fragment 모드가 있으면 사이트에 붙이기 쉽다고 설명합니다. 마지막으로 Sphinx에서 Doxygen 심볼로 연결할 때 존재하지 않는 심볼을 빌드 오류로 잡아 주는 Doxylink처럼, rustdoc·Sphinx·Doxygen 사이 모든 방향의 링크를 검사하는 도구가 필요하다고 제안합니다.

Lobsters 반응

  • @pie_flavor — Rust 문서를 다시 호스팅하는 이유가 있나요? 새로운 시스템을 구경하다 docs.rs가 아닌 rustdoc 페이지에 들어가면, 저는 보통 결국 docs.rs로 옮겨 갑니다. 거기서 패키지와 버전 정보, 크레이트의 공개 의존성, 그 밖의 여러 네트워크 정보를 확인할 수 있고, 이미 쓰는 프로젝트라면 복잡한 북마크 막대에 페이지를 저장하지 않아도 docs.rs/패키지명으로 갈 수 있습니다. 수정: 제품을 더 자세히 보니 Cargo를 전혀 쓰지 않는다는 점이 그럴듯한 답이겠네요. 그래도 왜 그런지는 더 궁금합니다. Bazel을 좋아하더라도 같은 프로젝트에서 Cargo도 충분히 지원할 수 있습니다. 이전 직장에서는 실제로 그렇게 했습니다.
  • @robalex — 결과가 멋지네요. 저도 예전에 이렇게 해 봤지만 테마를 일관되게 맞추는 일을 포기했습니다. 첫 번째 결과는 Doxygen, 두 번째와 세 번째는 Sphinx, 네 번째는 rustdoc에서 나옵니다. 이 점이 놀랍습니다. Rust 문서를 보는 중에 C/C++ 결과가 나오면 산만할 것 같습니다. 특정 언어 문서로 들어간 뒤 해당 언어 결과만 보여주는 방법이 있나요?
    • @kaycebasques — 결과가 멋지다고 해 주셔서 감사합니다. 읽어 주셔서도 감사합니다. 제 경험으로는 글꼴을 일관되게 맞추는 것만으로도 큰 도움이 됩니다. 요즘은 CSS 변수를 쓰면 훨씬 관리하기 쉽습니다. Rust 문서를 보는 중에 C/C++ 결과가 산만하다는 점은 저도 여러 번 생각을 바꿨습니다. 어느 페이지에 있든 사이트 최상위 검색 경험은 같아야 한다는 게 현재 생각입니다. rustdoc 페이지에서도 rustdoc 검색은 계속 쓸 수 있습니다. 그래서 rustdoc 페이지에는 검색창이 두 개 생기는데, 오히려 혼란을 더할 수도 있습니다. 어느 쪽이 맞는지는 모르겠습니다. 6개월 뒤에는 생각이 더 정리되겠죠. 특정 언어로 결과를 제한하려면 URL을 수동으로 필터링해야 할 것 같지만, Pagefind 검색 API로 충분히 가능할 겁니다. 이번 주에 CSS 관련 내용을 글에 더 자세히 추가하겠습니다. 그것도 다루려 했지만 시간이 부족했습니다.
    • @robalex — rustdoc 검색도 rustdoc 페이지에서 계속 쓸 수 있다는 점을 제가 못 봤네요. 도움이 됩니다.
  • @singpolyma — 저도 같은 문제를 겪습니다. 지금까지는 도구 출력을 Markdown 비슷한 형식으로 바꾼 다음 전부 MkDocs에 넣었습니다. 그 결과 커스텀 스크립트와 변환 코드가 생겼습니다.
    • @kaycebasques — 읽어 주셔서 감사합니다. 어떤 출력을 변환하시나요? rustdoc JSON인가요, Doxygen XML인가요?

원문: technicalwriting.dev / 번역·요약: Trawling