개발자를 위한 크로스툴 검색: 코드베이스는 잘못된 곳입니다
개발자의 대부분의 결정은 코드 외부에 있습니다. Slack, Linear, GitHub, Notion을 아우르는 크로스툴 검색 구축 방법을 소개합니다.
By Ellis Keane · 2026-03-17
코드베이스는 결정이 내려진 이유를 검색하는 데 가장 유용하지 않은 곳입니다.
역설적으로 들린다는 것을 압니다. 우리는 ripgrep 플래그를 배우고, IDE 검색을 설정하고, 정규식 패턴을 외우는 데 수년을 보냅니다. 하지만 질문이 "이 함수가 어디 있나요?"가 아니라 "논의한 세 가지 대안이 아닌 이 접근 방식을 선택한 이유는 무엇인가요?"일 때, 그것들은 전혀 도움이 되지 않습니다. 두 번째 질문에 대한 답은 거의 코드에 없습니다. 네 달 전 Slack 스레드에 있고, 상태 업데이트 아래 묻힌 Linear 댓글에 있고, 누군가 시작했다가 끝내지 못한 Notion 문서에 있으며, 실제 토론이 답글의 답글의 답글에서 이루어진 PR 리뷰에 있습니다.
이것이 개발자를 위한 크로스툴 검색 문제입니다. 결정 컨텍스트가 통합된 쿼리 경로 없이 여러 도구에 분산되어 있습니다. 각 도구 내에서는 검색이 잘 작동합니다. Slack의 검색은 괜찮고, GitHub의 코드 검색은 훌륭하며, Linear에는 모든 것에 대한 필터가 있습니다. 하지만 이들을 가로질러 검색하는 것은 없습니다. 아키텍처를 형성한 결정 사항들은 다섯 군데에 흩어져 있고, 어디를 봐야 하는지 기억하기를 기대받습니다.
자, 그럼 이미 가진 것으로 크로스툴 검색을 구축하는 방법을 알아보겠습니다. 새로운 도구는 필요 없습니다 (거의요 – 맨 마지막에 하나를 언급하겠지만, 그것 없이도 작동합니다).
분산된 결정의 해부
구체적인 예를 살펴보겠습니다. 작년에 저는 작업 큐에 BullMQ와 Temporal 중 어느 것을 사용할지 결정하고 있었습니다. 그 결정이 실제로 어디에 존재했는지 보여드리겠습니다:
- Slack (#engineering): 이틀에 걸친 세 개의 별도 스레드. 첫 번째는 누군가 Temporal 블로그 게시물 링크를 공유한 것. 두 번째는 내구성 있는 실행이 필요한지에 대한 토론. 세 번째 (일주일 후, 다른 채널)는 "잠깐, 큐 문제는 결정했나요?"라고 묻는 것.
- Linear: "작업 큐 옵션 평가"라는 제목의 이슈에 여섯 개의 댓글이 있었고, 그 중 하나는 엔지니어 중 한 명이 오후를 보내며 작성한 비교 표를 포함했습니다.
- GitHub: BullMQ 구현을 위한 PR 설명에는 "논의한 대로"라고 쓰여 있고 어디서 논의했는지에 대한 링크는 없었습니다.
- Notion: Temporal의 장점은 다뤘지만 최종 선택으로 업데이트되지 않은 반쯤 완성된 아키텍처 결정 기록.
- Google Docs: 우리가 실제로 결정을 내린 통화의 미팅 노트로, 관련 없는 두 안건 항목 사이의 글머리 기호에 묻혀 있었습니다.
다섯 가지 도구. 하나의 결정. 그리고 어느 한 도구를 검색하면 단편만 찾게 됩니다. PR은 우리가 선택한 것을 알려줍니다. Slack 스레드는 우리가 고려한 것을 알려줍니다. Linear 이슈는 트레이드오프를 알려줍니다. Notion 문서는 추론의 절반을 알려줍니다. 미팅 노트는 결정이 확정된 순간을 알려줍니다.
이것은 드문 일이 아닙니다. 어떤 의미에서 이것이 2026년에 엔지니어링 팀이 결정을 추적하는 방식의 최첨단입니다. 코드를 생성하는 AI와 전체 인터넷을 색인하는 검색 엔진이 있지만, 팀이 왜 Temporal 대신 BullMQ를 선택했는지 알아내려면 다섯 개의 앱을 확인하고 누군가의 기억에 의존해야 합니다.
개발자를 위한 크로스툴 검색이 어려운 이유
API 문제가 아닙니다. 우리가 사용하는 모든 도구에는 완벽하게 좋은 검색 API가 있습니다. 문제는 그보다 더 이상합니다:
데이터 형태가 다릅니다. Slack은 타임스탬프와 채널 ID가 있는 메시지를 반환합니다. Linear는 상태와 레이블이 있는 이슈를 반환합니다. GitHub는 완전히 다른 응답 형식으로 커밋, PR, 코드 일치 항목을 반환합니다. 이것들을 일관된 타임라인으로 병합하려면 아무도 구축하려 하지 않는 정규화가 필요합니다 (솔직히 말해서 스프린트 데모에서 보이지 않는 작업이기 때문입니다).
컨텍스트 단편화. "옵션 B로 가겠습니다"라는 Slack 메시지는 옵션 A, B, C를 정의한 스레드 없이는 의미가 없습니다. 하지만 Slack의 검색은 대화 흐름이 아닌 개별 메시지를 반환합니다. 추론 없이 결론을 찾게 됩니다.
시간적 드리프트. 결정 과정은 종종 모두가 다른 작업에 집중하느라 아무것도 일어나지 않는 공백을 사이에 두고 며칠 또는 몇 주에 걸쳐 진행됩니다. 키워드 검색은 단순히 다른 단계에서 다른 단어가 사용되었다는 이유만으로 대화의 시작과 끝을 찾아내면서 중요한 중간 부분을 놓칠 수 있습니다.
개발자를 위한 크로스툴 검색은 API 문제가 아닙니다. 모든 도구에는 완벽하게 좋은 검색 엔드포인트가 있습니다. 컨텍스트 문제입니다. 결정 사항은 호환되지 않는 형태로 여러 도구에 분산되어 있고, 대화 흐름에 의해 단편화되어 있으며, 시간적 드리프트로 분리되어 있습니다. 키워드 검색은 단편을 찾지만 전체 그림을 찾는 것은 연결된 컨텍스트뿐입니다.
가진 것으로 크로스툴 검색 구축하기
여기가 실용적인 부분입니다. 읽기 전용 검색으로 세 개 또는 네 개의 도구에 대해 MVP를 작동시키는 데 반나절이 걸릴 것으로 예상하세요. 그 대부분은 검색 로직 자체가 아닌 인증 설정과 응답 정규화에 소요됩니다.
API 액세스 설정
각 도구에 대한 토큰이 필요합니다:
- Slack:
search:read 스코프가 있는 사용자 토큰 (Slack의 검색 메서드는 봇 토큰이 아닌 사용자 토큰이 필요합니다 – Slack API 앱 페이지를 통해 생성)
- Linear: 설정의 API에서 개인 API 키
- GitHub: 리포지토리에 대한 읽기 액세스가 있는 세분화된 PAT
- Notion: 설정의 연결에서 내부 통합 토큰
팬아웃 쿼리 스크립트
기본 패턴은 놀랍도록 단순합니다. 모든 API에 동일한 검색 쿼리를 실행하고 결과를 수집합니다:
```typescript interface SearchResult { source: 'slack' | 'linear' | 'github' | 'notion'; title: string; snippet: string; url: string; timestamp: Date; }
async function crossToolSearch(query: string): Promise<SearchResult[]> { const results = await Promise.all([ searchSlack(query), searchLinear(query), searchGitHub(query), searchNotion(query), ]);
return results .flat() .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); } ```
각 search* 함수는 해당 API를 감쌉니다. Slack의 경우 search.messages, Linear의 경우 검색 필드에 대한 GraphQL 쿼리, GitHub의 경우 REST 검색 엔드포인트, Notion의 경우 query 매개변수가 있는 검색 엔드포인트입니다.
정규화 및 중복 제거
까다로운 부분은 검색이 아니라 결과를 유용하게 만드는 것입니다. 다음 작업이 필요합니다:
- 타임스탬프 정규화 – 도구 간에 통일합니다 (Slack은 Unix 에포크, Linear는 ISO 문자열, GitHub는 시간대 오프셋이 있는 ISO 사용)
- 관련 결과 그룹화 – 동일한 Slack 스레드가 세 메시지와 일치하여 세 번 나타나면 스레드 URL이 있는 하나의 결과로 축소합니다
- 관련성 기반 순위 지정 – 대부분의 API는 자체 관련성 점수를 반환하지만 도구 간에 비교할 수 없습니다. 간단한 휴리스틱: 제목의 정확한 키워드 일치가 본문 일치보다 높은 순위를 차지하고, 관련성이 같으면 최신 결과가 오래된 결과보다 높은 순위를 차지합니다
CLI로 감싸기
저는 Commander.js를 사용합니다 (주로 습관에서 비롯되었지만 무엇이든 작동합니다):
```bash $ cross-search "bullmq vs temporal"
Found 14 results across 4 tools:
[Slack] #engineering – 2025-11-14 "I've been comparing BullMQ and Temporal for the job queue..." https://myteam.slack.com/archives/C0X.../p17318...
[Linear] ENG-342 – 2025-11-15 "Evaluate job queue options – BullMQ vs Temporal" https://linear.app/myteam/issue/ENG-342
[GitHub] PR #289 – 2025-11-22 "feat: implement BullMQ job queue (as discussed)" https://github.com/myorg/myrepo/pull/289
[Notion] Architecture Decisions – 2025-11-13 "Job Queue Evaluation: Temporal vs BullMQ" https://notion.so/myteam/abc123... ```
네 개의 도구에 걸쳐 시간순으로 정렬된 14개의 결과. 한 곳에서 결정의 전체 흐름을 볼 수 있습니다. Notion 문서가 먼저 시작되고, 그 다음 Slack 토론이 일어났으며, 그 다음 추적을 위해 Linear 이슈가 만들어지고, 마지막으로 일주일 후 PR이 병합되었습니다.
실제로 더 잘 만들기
위의 기본 버전은 작동하지만 몇 가지 불편한 부분이 있습니다. 개선 방법은 다음과 같습니다:
Slack 스레드 확장. 일치하는 메시지를 찾으면 conversations.replies로 전체 스레드를 가져옵니다. 일치하는 메시지가 "네, BullMQ로 가겠습니다"일 수 있지만 앞의 40개 토론 메시지 없이는 유용하지 않습니다. 일치하는 메시지만이 아닌 스레드의 스니펫을 표시하세요.
PR 리뷰 댓글. GitHub의 검색 API는 PR을 검색할 때 리뷰 댓글을 표시하지 않습니다. 이를 가져오려면 풀 리퀘스트 리뷰 엔드포인트에 별도 호출이 필요합니다. 실제 기술적 토론이 거기에 있습니다.
백링크. Linear 이슈를 찾으면 해당 이슈의 URL이 포함된 Slack 메시지가 있는지 확인합니다. Slack의 검색은 키워드와 결합된 has:link 필터를 지원합니다. 이를 통해 공식 추적 주변에서 일어난 비공식 토론이 드러납니다.
캐싱. 팀이 많은 콘텐츠를 생성하는 경우 (그렇지 않은 팀이 어디 있나요), 빠르게 속도 제한에 걸릴 것입니다. TTL이 30분인 로컬 캐시로 결과를 캐시하세요. 대부분의 과거 결정은 그렇게 빨리 변하지 않습니다.
텍스트 검색이 한계에 부딪힐 때
여기서 한계에 대해 솔직하게 이야기하겠습니다. 도구 간 키워드 검색은 놀랍도록 멀리까지 작동하다가 벽에 부딪힙니다.
그 벽은 이것입니다: 결정은 진화합니다. "작업 큐"에 관한 Slack 스레드는 "BullMQ"라는 이름을 한 번도 언급하지 않을 수 있습니다. 대신 누군가 링크를 공유하고, 다른 사람이 "Redis 기반 옵션이 좋습니다"라고 말하고, 세 번째 사람이 "동의합니다, 그걸로 갑시다"라고 말했습니다. "BullMQ" 검색은 그 단어가 사용되지 않았기 때문에 전체 스레드를 놓칩니다. 스레드의 참여자들은 "Redis 기반 옵션"이 무엇을 의미하는지 알고 있었습니다. 당신의 검색은 그것을 모릅니다.
이것은 근본적으로 텍스트 문제가 아닌 그래프 문제입니다. 실제로 원하는 것은 "PR #289로 이어진 결정에 연결된 모든 것을 보여주세요"입니다. 즉, PR이 Linear 이슈를 참조하고, 그것은 Slack 토론 후에 생성되었으며, 누군가 Notion 문서를 읽으면서 시작되었다는 것을 이해해야 합니다. 연결은 암묵적입니다. 사람들이 URL을 복사하고 "논의한 대로"라고 말함으로써 만들어졌습니다. 키워드 검색은 그것을 재구성할 수 없습니다.
링크를 따라가면 부분적으로 해결할 수 있습니다. Slack 메시지, PR 설명, Linear 댓글에서 URL을 파싱하세요. 간단한 인접 목록을 구축하세요: 이 Slack 스레드는 이 Linear 이슈에 링크되고, 이 PR에서 참조됩니다. 그러면 누군가가 검색할 때 키워드와 일치하지 않더라도 링크된 항목을 포함하도록 결과를 확장할 수 있습니다.
그 인접 목록 접근법은 본질적으로 초보적인 지식 그래프입니다. 그리고 개발자를 위한 크로스툴 검색의 실제 가치가 거기에 있습니다. 개별 메시지를 찾는 것이 아니라 결정의 실마리를 그것이 닿은 모든 도구에 걸쳐 따라가는 것입니다. 그것은 "검색"이라기보다 개발자 지식 관리입니다. 정보가 도구 간에 어떻게 흐르는지 이해하여 필요할 때 컨텍스트를 재구성할 수 있게 하는 것입니다.
유지 관리 문제 (그리고 지름길)
스크립트 접근 방식은 약 세 달간 훌륭하게 작동하다가 누군가 Slack 워크스페이스를 변경하거나, Linear가 GraphQL 스키마를 업데이트하거나, 새 도구를 추가했는데 아무도 검색 스크립트를 업데이트하는 것을 기억하지 못합니다. 저는 이것을 정확히 두 번 구축하고 두 번 포기했습니다 (그것은 접근 방식 자체보다 유지 관리에 대한 저의 헌신에 대해 더 많은 것을 말해줄지도 모르지만요).
돌봄 없이 최신 상태를 유지하는 크로스툴 검색을 원한다면, Sugarbug와 같은 도구가 그것을 위해 만들어졌습니다. 도구가 변경되어도 지식 그래프를 자동으로 유지하고 연결을 살아있게 합니다. 하지만 유지 관리를 기꺼이 한다면 위의 DIY 버전도 진정으로 유용합니다.
다섯 개의 도구를 별도로 검색하는 것을 멈추세요. Sugarbug는 지식 그래프를 구축하여 모든 결정, 토론, 또는 커밋을 한 곳에서 찾을 수 있게 합니다.
Q: 여러 개발자 도구를 한 번에 검색하려면 어떻게 해야 하나요? A: 각 도구의 API(Slack의 search.messages, Linear의 issueSearch, GitHub의 코드 검색 엔드포인트)를 하나의 스크립트로 결합하여 쿼리를 팬아웃하고 타임스탬프별로 결과를 병합하는 가벼운 크로스툴 검색을 구축할 수 있습니다. 위의 코드 샘플을 통해 오후에 시작할 수 있습니다. 주요 과제는 검색 자체가 아니라 다른 응답 형식을 일관된 타임라인으로 정규화하는 것입니다.
Q: Sugarbug는 개발자를 위한 크로스툴 검색을 제공하나요? A: 네. Sugarbug는 Linear, GitHub, Slack, Figma, Notion 및 기타 도구에서 시그널을 수집하여 지식 그래프에 통합합니다. 이를 통해 결정 사항이나 토론을 검색하면 연결된 모든 스레드, 이슈, 커밋을 한 곳에서 찾을 수 있습니다. 정규화, 중복 제거, 링크 추적을 자동으로 처리하여 DIY 접근 방식이 시간이 지남에 따라 불안정해지는 부분을 담당합니다.
Q: 코드베이스에서 아키텍처 결정 사항을 찾을 수 없는 이유는 무엇인가요? A: 대부분의 결정은 Slack 스레드, Linear 댓글, Notion 문서, PR 리뷰에서 이루어지며 코드 자체에는 기록되지 않기 때문입니다. 코드는 결정의 결과(함수가 존재하고, 라이브러리가 선택됨)를 기록하지만 추론, 트레이드오프, 논의된 대안들은 커뮤니케이션 도구 전반에 분산되어 있습니다. git blame은 누가 언제 줄을 변경했는지 알려주지만 왜 그 접근 방식이 대안보다 선택되었는지는 알려주지 않습니다.
Q: Sugarbug가 결정 추적을 위한 ADR 문서를 대체할 수 있나요? A: Sugarbug는 ADR을 대체하지 않지만 ADR에 기록되지 않는 결정을 포착합니다. 대부분의 팀은 아키텍처 선택의 약 10%에 대해서만 ADR을 작성하고 나머지는 Slack 스레드와 PR 댓글에 녹아듭니다. Sugarbug는 대화와 그것이 만들어낸 코드 변경 사항을 연결하여 이를 표면화함으로써 누구의 워크플로도 바꾸지 않고 나머지 90%의 결정 추적을 제공합니다.