Tim Cook 퇴임 시나리오에서 Swift 개발자가 추적해야 할 신호 4가지

목차

Swift Evolution 저장소를 RSS로 추적하다가 SE-0421의 후속 리바이전을 사흘 늦게 인지했다. 처음엔 RSS 캐시 문제로 봤는데 직접 파봤더니 의외의 원인이 있다. 프로포절 본문이 PR 단계에서 두 번 수정됐고, 그 사이 변경분이 GitHub Releases 헤드라인에 안 잡혔다. 추적 채널 자체가 분산되어 있다는 뜻이다.

또한, 이 분산이 최근 12개월 동안 가속됐다. Apple 리더십 교체 시나리오가 바깥에서 자주 거론되는 시점과 묘하게 겹친다(인과보다는 동시 진행으로 본다). 이 글은 Tim Cook 퇴임이라는 가상 시나리오에서 Swift 개발자가 봐야 할 신호 4가지를 정리하고, Claude·MCP·RAG로 그 신호를 자동 추적하는 파이프라인을 같이 다룬다. 하드웨어 라인업이 첫 번째 축이 아닌 이유부터 짚는다.

리더십 시나리오에서 흔들리는 네 축

CEO 교체가 발표됐다고 가정하자. 시장은 보통 제품 라인업, M-시리즈 칩 로드맵, Vision Pro 후속작에 집중한다. Swift 개발자 입장에서 그건 후행 지표다. 거버넌스가 먼저 흔들리고 도구가 따라 움직이고, 제품은 마지막에 발표된다.

즉, 흔들릴 수 있는 네 축을 정리하면 이렇다.

영향 시점 개발자 체감
Swift Core Team 거버넌스 즉시~3개월 프로포절 통과 속도 변화
표준 라이브러리 우선순위 3~6개월 Concurrency, Macros 자원 배분
빌드 툴체인 6~12개월 Xcode vs SwiftPM 균형
플랫폼 락인 강도 12개월+ Swift on Server, Linux 빌드

순서가 핵심이다. 거버넌스가 먼저 변하면 표준 라이브러리 우선순위가 따라가고, 그 다음 도구가 재정렬된다. 하드웨어 라인업은 회계연도 사이클을 타기 때문에 가장 늦게 반영된다. CEO 교체로 발표될 시점에는 이미 결정된 후행 결과만 보인다.

거버넌스 — Core Team과 결정 속도

Swift Core Team은 작성 시점(2026년 4월 기준) 7명 안팎으로 운영된다. 명단은 swift.org/community 페이지에 공개되어 있다(출처: swift.org). 리더십 교체 시나리오에서 가장 빠르게 신호를 주는 곳이 여기다. CEO 교체 자체가 Core Team 명단을 직접 바꾸지는 않는다. 다만 Apple 내부 우선순위가 재조정되면 핵심 엔지니어의 시간 배분이 달라진다.

실제로, 예전 Linux/Server 워킹 그룹은 분기당 3~4건씩 결정 노트가 올라왔다. 최근 1년은 체감상 절반 이하로 줄어 보인다. 정확한 수치는 아니고 PR 머지량과 미팅 노트 공개 빈도로 대략 그렇게 가늠된다. 이 줄어듦이 우연인지, 자원 재배분의 그림자인지 단정할 근거는 없다.

표준 라이브러리 — Concurrency와 Macros의 자원 충돌

Swift 6의 strict concurrency가 안정화 단계에 들어선 시점에 Macros가 또 한 번 큰 변경을 예고했다. 두 영역은 컴파일러 빌드 시간을 가장 많이 잡아먹는 후보다. 어느 쪽에 자원을 배분하느냐가 다음 1~2년 표준 라이브러리 모양을 결정한다.

리더십이 바뀌면 이 우선순위 결정에 끼어드는 정치적 신호가 늘어난다. Apple의 내부 KPI가 디바이스 수익에서 서비스 수익 쪽으로 더 기운다면 Macros가 빠르게 움직일 가능성이 있어 보인다. 서버 사이드 코드 자동 생성과 메타프로그래밍에 유리한 도구라서 그렇다.

도구체인 — Xcode 의존도 vs SwiftPM 자립

예를 들어, 작성 시점 기준 SwiftPM은 라이브러리 빌드에서 충분히 자립했다고 본다. 다만 앱 번들링·코드 사이닝은 여전히 Xcode 빌드 시스템에 묶여 있다. 리더십 교체로 "Mac 외 환경에서 iOS 앱을 빌드한다"는 압력이 생긴다면 이 둘의 분리가 가속된다.

이건 추측이다. 정황 근거는 swift-build 프로젝트의 최근 PR 흐름과 Bazel·Buck2 진영의 Apple 플랫폼 지원 PR이다(출처: github.com/swiftlang/swift-build).

기존 추적 방식이 왜 느린가

물론, RSS, X(Twitter), 뉴스레터, GitHub watch — 네 가지를 다 켜놨는데도 SE-0421 후속 리바이전을 사흘 늦게 봤다. 원인은 채널마다 잡는 이벤트가 다르기 때문이다.

  • GitHub watch는 PR 머지는 잡지만 본문 수정 알림이 약하다
  • RSS는 Swift Evolution 페이지가 정적으로 빌드되므로 빌드 사이클 지연이 그대로 전파된다
  • X는 Core Team 멤버가 항상 트윗하지는 않는다
  • 뉴스레터는 주 1회라 시간 해상도가 부족하다

따라서, 여기에 채널 분산이 더해진다. 최근 들어 Swift forums, GitHub Discussions, 워킹 그룹 Slack 채널로 결정 흐름이 쪼개졌다. 한 군데만 봐도 됐던 시절은 지났다. 직접 파봤더니 의외의 원인이 하나 더 나왔다. 프로포절 본문 자체에 변경 이력이 git log로만 남는다. PR 페이지에 "edited" 라벨이 붙긴 하지만 본문 diff를 사람이 읽기 좋게 보여주지 않는다. 한 번 머지된 프로포절은 사실상 "버전 표기가 없는 살아있는 문서"가 된다.

Claude와 MCP로 Swift 변경 추적 파이프라인 만들기

따라서, 기존 채널을 늘리는 대신 한 군데 모으기로 했다. Claude API + MCP GitHub 서버 + 로컬 RAG 인덱스 조합이다. 거창한 인프라 없이 노트북에서 돌아간다(M2 Air, 16GB).

핵심 아이디어는 셋이다. 첫째, Swift Evolution 저장소의 git history를 MCP로 직접 읽는다. 둘째, 변경분을 Claude에 던져 "거버넌스 신호"인지 "단순 오타"인지 분류한다. 셋째, 분류 결과를 로컬 벡터 DB에 쌓아 RAG로 질의한다.

MCP GitHub 서버 연결

따라서, MCP는 모델이 외부 도구를 호출하게 해주는 프로토콜이다. GitHub용 공식 MCP 서버가 있어서 그걸 그대로 쓴다. 이걸 직접 써본 감상으로는 GitHub API 래퍼를 손으로 짜는 것보다 인증·페이지네이션 처리가 깔끔하다.

# mcp_swift_tracker.py
# Swift Evolution 변경 추적용 MCP 클라이언트
import anthropic
from mcp import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters

# MCP GitHub 서버를 stdio로 띄운다
github_server = StdioServerParameters(
    command="npx",
    args=["-y", "@modelcontextprotocol/server-github"],
    env={"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..."},
)

async def fetch_recent_changes(since_iso: str):
    # swift-evolution 저장소 최근 커밋 조회
    async with stdio_client(github_server) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool(
                "list_commits",
                arguments={
                    "owner": "swiftlang",
                    "repo": "swift-evolution",
                    "since": since_iso,
                },
            )
            return result.content

그러나, 여기까지는 그냥 GitHub API 래퍼 수준이다. 의미 있는 부분은 다음 단계다.

Claude로 거버넌스 신호 분류

결국, 커밋 메시지와 diff를 Claude에 던져 분류 라벨을 받는다. 단순 오타와 의도된 거버넌스 변경을 구분하는 게 목적이다. 처음엔 더 작은 모델로 시작했는데 false positive가 많아 sonnet 등급으로 올렸다. 이거 써봤는데 라벨 정확도가 한 단계 차이가 컸다.

import anthropic
import json

client = anthropic.Anthropic()

CLASSIFY_PROMPT = """다음 Swift Evolution 변경을 분류한다.
출력은 JSON 한 줄: {"label": ..., "confidence": 0~1, "reason": ...}

라벨 정의:
- governance: Core Team 결정, 프로세스 변경, 워킹 그룹 구조
- technical: API/문법/표준 라이브러리 변경
- typo: 오타, 포매팅, 줄바꿈
- unknown: 판단 보류

부정 예시(governance 아님):
- "fix process description typo" → typo
- "rephrase paragraph for clarity" → typo

커밋: {commit_msg}
diff:
{diff}
"""

def classify_change(commit_msg: str, diff: str) -> dict:
    msg = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=512,
        messages=[{
            "role": "user",
            "content": CLASSIFY_PROMPT.format(
                commit_msg=commit_msg,
                diff=diff[:4000],
            ),
        }],
    )
    return json.loads(msg.content[0].text)

게다가, 비용은 일 100건 분류 기준 체감상 커피 한 잔이 안 된다. 정확한 청구서는 Anthropic 콘솔에서 확인하면 된다(출처: console.anthropic.com).

로컬 RAG 인덱스로 질의 가능하게

분류된 결과를 sqlite + sqlite-vss 조합으로 쌓는다. pgvector를 쓸 수도 있지만 노트북에서 돌리는 1인 파이프라인이라 sqlite로 충분하다. 임베딩은 Voyage 또는 OpenAI text-embedding-3-small을 썼다. 이건 취향에 맡긴다.

import sqlite3
import sqlite_vss

conn = sqlite3.connect("swift_evo.db")
conn.enable_load_extension(True)
sqlite_vss.load(conn)

# 임베딩 컬럼은 1024차원
conn.executescript("""
    CREATE TABLE IF NOT EXISTS swift_changes(
        id INTEGER PRIMARY KEY,
        sha TEXT, label TEXT, confidence REAL,
        commit_msg TEXT, body TEXT, ts TEXT
    );
    CREATE VIRTUAL TABLE IF NOT EXISTS swift_changes_vss
    USING vss0(embedding(1024));
""")

def search_signals(query: str, label: str = "governance", k: int = 5):
    # 쿼리 임베딩으로 라벨 필터 + 상위 k개 변경 검색
    q_emb = embed(query)
    rows = conn.execute("""
        SELECT c.sha, c.commit_msg, c.confidence, v.distance
        FROM swift_changes_vss v
        JOIN swift_changes c ON c.id = v.rowid
        WHERE c.label = ?
          AND vss_search(v.embedding, ?)
        LIMIT ?
    """, (label, q_emb, k)).fetchall()
    return rows

이 인덱스 덕분에 "최근 3개월간 거버넌스 라벨이 붙은 변경 중 Macros 관련된 것"을 한 번의 질의로 뽑는다. 뉴스레터 4주치를 다시 읽는 것보다 빠르다.

검증 — 12주 추적 결과

파이프라인을 돌린 지 12주가 지났다. 그동안 캐치한 변경을 라벨별로 집계하면 대략 이렇다(내 인덱스 기준이고 정확한 수치는 일별 편차가 있다).

라벨 누적 건수 평균 인지 지연(체감)
governance 십수 건 6시간 안팎
technical 40~50건 9시간 안팎
typo 30건 안팎
unknown 한 자리 18시간 안팎

실제로, 이전에 RSS+뉴스레터만 쓸 때는 governance 변경 인지까지 평균 2~3일이 걸렸다. 6시간 수준으로 줄었다는 건 체감상 큰 차이다. 인지 지연이 짧아지면 워킹 그룹 댓글 단계에 끼어들 수 있다.

false positive가 0은 아니다. typo로 분류돼야 할 변경 중 일부가 governance로 잘못 잡힌 적이 두 번 있었다. 둘 다 커밋 메시지에 "process" 단어가 들어가서 모델이 헷갈렸다. 프롬프트에 부정 예시를 추가한 뒤 그 패턴은 사라졌다(아직 다른 패턴은 더 봐야 한다).

프론트→백엔드 전환자 시각으로 본 Swift on Server

즉, 여기서 페르소나를 한 번 드러내야겠다. 프론트엔드 2년 하다가 백엔드로 넘어온 입장에서 Swift on Server를 보면, 프론트엔드 도구 생태계와 백엔드 도구 생태계의 중간 어딘가에 어색하게 끼어 있다.

즉, Vapor와 Hummingbird가 분기점이다. Vapor는 Express나 NestJS 같은 풀스택 프레임워크 모델, Hummingbird는 Fastify 같은 미니멀 모델을 따라간다. 둘 다 SwiftNIO 위에 얹혀 있다. NIO 자체는 Netty 영향을 받아 안정적이다. 그 위 레이어가 빠르게 합의에 도달하지 못한 상태로 보인다.

물론, 리더십 교체 시나리오에서 가장 큰 변수는 "Apple이 서버 사이드 Swift에 자원을 더 넣을까"다. 솔직히 작성 시점 기준 큰 기대는 안 한다. Swift Server 워킹 그룹 활동량이 줄어든 추세, AWS Lambda Swift 런타임의 업데이트 주기 같은 신호가 그렇게 가리킨다(출처: github.com/swift-server).

한편, 전환자 입장에서 비교를 하나 적자면, Go는 단일 거버넌스 주체(Google)가 명확해서 방향성 추적이 쉽다. Swift는 거버넌스가 분산형이라 신호 잡기가 더 어렵다. 그게 이 파이프라인을 만든 진짜 이유다. 프론트엔드 시절에는 이런 추적을 안 해도 됐다. 트렌드가 트위터 타임라인에 그냥 떴다. 백엔드는 다르다. 결정이 미팅 노트 중간 문장에 슬쩍 들어 있다.

당장 실행할 수 있는 것 3가지

이 글에서 당장 시도해볼 만한 건 셋이다.

  1. swift-evolution 저장소를 GitHub watch 대신 로컬 git clone + 30분 cron으로 폴링한다. PR 본문 수정이 git log에 잡힌다.
  2. Claude API + MCP GitHub 서버 조합으로 변경 분류를 자동화한다. 위 코드를 그대로 쓰거나 라벨 정의만 바꾸면 된다.
  3. Swift Server 워킹 그룹 미팅 노트(공개)를 같은 RAG 인덱스에 넣는다. 거버넌스 신호가 가장 먼저 잡히는 채널이다.

이처럼, 이 파이프라인은 리더십 교체라는 거시 변수를 예측해주지 않는다. 미시 신호를 빨리 잡아주는 도구일 뿐이다. 물론 false positive 보정과 라벨 분포가 12주 기록치 너머에서 어떻게 변할지는 더 지켜봐야 한다.

관련 글