케이스 스터디

모든 렙. 기록에 남다.

웨이트 트레이닝을 위한 빠르고 프라이빗한 운동 기록. iPhone, iPad, Apple Watch용으로 App Store에 나와 있어요.

2026년 7월 12일 출시. Reps는 제 트레이닝에 쓰고 싶어서 만들었어요. 바벨 옆에 두어도 거슬리지 않을 만큼 조용한 기록장, 운동이 끝나면 잠기는 하루, 그리고 Claude가 데이터베이스를 건드리지 않고 제 히스토리를 읽어 다음 세션을 계획하게 해주는 커넥터가 필요했거든요. Swift 코드 36,000줄과 테스트 185개를 지나, 지금은 17개 언어로 출시되어 있어요. 그 과정을 여기에 정리했어요.

운동 중인 941 Reps: 손으로 그은 취소선과 함께 초록색으로 완료 표시된 세트 두 개
제품

동그라미를 탭하고, 하루를 잠그다.

운동 기록의 임무는 하나예요. 잊기 전에 세트를 적어 두는 것. Reps는 제가 저 자신을 위해 원했던 기록장이에요. 열면 오늘이 나오고, 계획해 둔 분할이 보이고, 동그라미를 한 번 탭하면 세트가 기록돼요. 피드도 없고 다른 사람의 숫자도 없어요. 세트 사이에 둘 중 어느 것도 저는 볼 생각이 없었으니까요.

세트는 무게와 반복 횟수예요. RPE, 휴식 타이머, 메모는 원하는 사람에게는 있고, 그 외의 모두에게는 방해되지 않게 비켜서 있어요. 개인 기록은 완료된 세트로부터 저절로 계산돼요. 최고 중량, 그리고 Brzycki 추정 1RM까지.

다른 걸 전부 덜어내야 한다 해도 마지막까지 남길 기능은 하루 잠금이에요. 하루를 마치고 잠그면, 직접 잠금을 풀기 전까지 앱이 어떤 수정도 받지 않아요. 잠금 해제는 앱 안에서 명시적으로 해야 하는 일이고, 여러분을 대신해 움직이는 자동화는 잠긴 하루를 아예 수정할 수 없어요. 한 달 치 트레이닝을 돌아볼 때, 잠긴 하루는 기록한 그대로 읽혀요.

비즈니스 모델도 같은 원칙을 따라요. 기록은 영원히 무료이고, 여러분의 기록이 페이월 뒤에 갇히는 일은 없어요. Reps Pro 구독이 파는 건 정확히 하나예요. API 키를 준비하지 않고도 플래너를 쓰고 싶은 사람들을 위한 관리형 AI 플래닝.

잠긴 941 Reps 운동 기록: 완료된 세트에 취소선이 그어지고 그날의 자물쇠가 채워진 화면
잠긴 하루는 기록한 그대로 읽혀요.
디자인

칠판 위의 손글씨.

종이 운동 일지에는 제대로 된 것이 하나 있었어요. 날짜, 운동 이름, 그 밑에 갈겨 쓴 숫자. Reps는 그 물성을 통째로 지켜요. 날짜와 무게는 Bradley Hand와 Noteworthy로 렌더링되고, 팔레트는 거의 검정인 바탕 위의 잉크에 초록 하나를 더한 선에서 멈춰요. 그리고 구분선은 텍스처 에셋이 아니에요. 16개 선분짜리 Shape가 모든 선분을 중심선에서 살짝 밀어내기 때문에 흔들리는 거예요:

Swift · Theme.swift:143-158
struct RepsHandDrawnLine: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let segments = max(Int(rect.width / 12), 8)
        let segmentWidth = rect.width / CGFloat(segments)
        path.move(to: CGPoint(x: 0, y: rect.midY))

        for index in 1...segments {
            let x = CGFloat(index) * segmentWidth
            let y = rect.midY + (index.isMultiple(of: 2) ? -0.8 : 0.9)
            path.addLine(to: CGPoint(x: x, y: y))
        }

        return path
    }
}

아래 줄에서 직접 해 보세요. 이 페이지용으로 앱의 핵심 인터랙션을 다시 만들었어요. 동그라미를 탭해서 세트를 완료하고, 하루를 잠근 다음, 앱이 여러분을 거절하는 모습을 지켜보세요.

6월 12일 금요일

가슴 / 등

인버티드 로우

동그라미를 탭하고, 하루를 잠그고, 다시 눌러 보세요.

손글씨가 멈추는 곳.

출시 언어 17개 중 10개가 손글씨 서체를 써요. 일본어, 한국어, 중국어, 아랍어, 힌디어, 태국어는 SF Rounded로 대체돼요. Bradley Hand에는 이 문자들의 글리프가 없고, 가짜 손글씨 CJK 폰트는 분장처럼 보이니까요. 색도 같은 원칙을 따라요. 브랜드 초록은 밝은 색이라, 밝은 표면 위의 강조 텍스트는 스스로 어두워져 5.4:1 명암비를 지키고, tvOS 포커스 상태는 저만의 잉크를 따로 써요. 모든 탭 타깃은 Apple의 최소 기준인 44포인트를 지켜요. 이 컨트롤들은 책상 앞이 아니라 세트 사이, 피로가 쌓인 손으로 누르게 되니까요.

눈에 보이지 않는 부분은 Kit941에서 와요. 941 앱들이 공유하는 SwiftUI 디자인 시스템으로, 간격·모션·햅틱 토큰, 스냅샷 테스트를 거친 컴포넌트, 그리고 하나의 스타일 뒤로 감싼 Apple의 Liquid Glass가 들어 있어요. Reps는 .glassEffect()를 직접 호출하지 않아요. 노트 감성은 Reps의 것이고, 뼈대는 함께 써요.

플랫폼

iPhone, iPad, Apple Watch에서, 17개 언어로.

Reps는 하나의 SwiftUI 코드베이스로 iPhone, iPad, Apple Watch에서 돌아가요. Watch 앱은 의도적으로 작게 유지했어요. 손목에서 오늘의 세트를 보여 주고 완료 표시를 하면, WatchConnectivity가 몇 초 안에 변경 사항을 휴대폰으로 옮겨요. 체육관에서는 체크리스트가 키보드를 이겨요.

Siri와 단축어는 App Intents로 연결돼요. "세트 기록해 줘"라고 말하면 앱을 열지 않아도 세트가 들어가요. 인텐트가 UI와 똑같은 검증된 쓰기 경로로 제출되기 때문이에요.

iPad의 941 Reps: 완료된 세트에 취소선이 그어진 오늘의 가슴·등 세션
iPad
Apple Watch의 941 Reps: 체크리스트로 보이는 오늘의 세트, 바벨 벤치 프레스 260 lb 6회
Apple Watch
Mac의 941 Reps: 데스크톱 창에 담긴 손글씨 노트
Mac, 개발 중.
Apple TV의 941 Reps: 포커스된 세트 줄이 있는 오늘의 운동
Apple TV, 개발 중.
  • English
  • Deutsch
  • Español
  • Français
  • Italiano
  • Nederlands
  • Português
  • Svenska
  • Bahasa Indonesia
  • Tiếng Việt
  • 日本語
  • 한국어
  • 简体中文
  • 繁體中文
  • العربية
  • हिन्दी
  • ไทย

출시 첫날부터 17개 언어를 지원했고, App Store 메타데이터는 22개 로케일로 준비되어 있어요. 검증 스크립트가 735개 문자열 전체에 누락 키가 0개일 때만 빌드를 통과시켜요.

Mac과 Apple TV 빌드는 존재하고 실제로 돌아가요. 준비가 되는 날 출시돼요.

엔지니어링

정문은 하나.

Reps는 커넥터와 함께 출시돼요. 에이전트에게 기록에 대한 실제 접근 권한을 주는 CLI와 MCP 서버예요. Claude가 히스토리를 읽고, 어디서 정체됐는지 알아채고, 금요일 운동을 계획해요. 터미널에서는 이렇게요:

reps — zsh
$ reps last
Friday, June 26 — Chest / Back  [locked]
  Barbell Bench Press   255 lb × 6, 6, 6, 5
  Weighted Pull-Up      25 lb × 5, 5, 4
  Weighted Dip          60 lb × 10, 10, 10

$ claude "I started BJJ twice a week. What should change?"
Reading your last 8 weeks via the reps connector… pulling
volume is high for a grappling schedule. Suggesting: drop one
row day, add hip hinge + neck work. Plan Friday around this?

읽기와 쓰기는 서로 다른 길로 다녀요. 읽기는 데이터의 동기화된 로컬 복제본을 읽기 전용으로 열어요. 그래서 쿼리가 무엇도 망가뜨릴 수 없고, 오프라인에서도 동작해요. 쓰기는 그 저장소를 절대 건드리지 않아요. 모든 변경은 명령이 되어 앱으로 가고, 앱은 UI가 강제하는 것과 똑같은 규칙으로 검증하고, 적용하고, 동기화한 다음, 영수증을 돌려줘요. 잠긴 하루는 손끝을 거절하는 것과 똑같이 에이전트의 수정도 거절해요.

941 Reps 플래너 시트: 빠른 명령과 일상어 작성창
플래너: 빠른 명령을 쓰거나, 그냥 말로 요청하세요.

커넥터가 받는 모든 명령은 탭 한 번과 같은 검증을 거치고, 잠긴 하루는 무엇이든 저장소에 닿기 전에 예외를 던져요:

Swift · ConnectorCommandHandler.swift:438-448
guard let session = try sessionForDay(date, modelContext: modelContext) else {
    throw CommandError.validationFailed("no workout exists on \(dayLabel)")
}
guard !session.isLocked else {
    throw CommandError.dayIsLocked(dayLabel)
}
guard let target = (session.sets ?? []).first(where: {
    $0.exerciseName.caseInsensitiveCompare(exerciseName) == .orderedSame
        && $0.setNumber == setNumber
}) else {
    throw CommandError.validationFailed("no set \(setNumber) of \(exerciseName)")
}

AI 플래너도 같은 대접을 받아요. 모델은, 관리형이든 본인 키든, 의도만 해석해요. 어떤 종류의 하루인지, 무엇을 강조하고 무엇을 피할지. 실제 프로그래밍은 휴대폰 안의 결정론적 엔진이 해요. 42개 리프트 코퍼스에서 종목을 고르고, 목표에 맞는 반복 스킴을 적용하고, 기록된 히스토리를 바탕으로 중량을 올리고, 결과물이 기록에 닿기 전에 린트를 돌려요. AI가 운동을 직접 쓰는 일은 절대 없어요.

여기까지 오는 동안 멀쩡히 동작하던 아키텍처 두 개가 죽었어요. 처음에는 파일 기반 Claude Desktop 익스텐션, 다음에는 TLS 피닝과 자체 RPC 버스를 갖춘 로컬 네트워크 페어링 서버였죠. 둘 다 데이터로 들어가는 두 번째 문을 냈고, 그래서 둘 다 걷어냈어요. 출시된 앱의 문은 하나예요.

커넥터는 get_exercise_history부터 set_custom_program까지 23개의 MCP 도구를 노출해요. 저는 올해 내내 첫 번째 독자가 어시스턴트인 소프트웨어에 대해 글을 써 왔는데, Reps는 그 주장을 실제로 출시한 결과물이에요. Get Bananas는 쇼핑 목록에 MCP 서버를 넣었죠. Reps는 에이전트에게 플래너를 쥐여 주고, 가드레일은 그대로 지켜요.

프라이버시

데이터가 사는 곳.

프라이버시 주장은 말로는 쉬우니까, 저장소 구성 자체를 보여 드릴게요. 트레이닝 데이터는 여러분의 iCloud 프라이빗 데이터베이스로 미러링되고, 건강 지표는 동기화되지도 백업에 들어가지도 않는 두 번째 저장소에 살아요:

Swift · RepsModelContainer.swift:67-78 (condensed)
// No explicit name: the synced configuration keeps the historical
// default.store path, which the Mac connector reads directly.
let modelConfiguration = ModelConfiguration(
    schema: syncedSchema,
    isStoredInMemoryOnly: false,
    cloudKitDatabase: .private("iCloud.com.941apps.Reps")
)
let healthCacheConfiguration = ModelConfiguration(
    "Reps-healthcache",
    schema: localSchema,
    isStoredInMemoryOnly: false,
    cloudKitDatabase: .none
)

여러분의 트레이닝 데이터

여러분의 iCloud 프라이빗 데이터베이스에 살고, Apple을 통해 기기에서 기기로 동기화돼요. Reps에는 계정 시스템이 없고, 운동을 저장하는 서버도 없어요. 저는 여러분의 스쿼트 숫자를 읽을 수 없어요.

건강 지표

건강 앱에서 읽어 온 심박수와 칼로리는 두 번째 저장소에 머물러요. 기기 안에만 있고, 백업에서 제외되고, 동기화되지 않아요. 건강 앱 운동을 잠긴 하루에 연결하면 연결 정보만 동기화돼요. 측정값은 휴대폰에 남아요.

AI 플래닝

선택 사항이에요. 사용하면 플랜 요청(프로필과 최근 히스토리)이 여러분이 선택한 제공자, 즉 관리형 서비스나 본인의 API 키로 가요. 모든 플랜을 확장하고 검증하는 엔진은 휴대폰에서 돌아가요. AI를 건너뛰면 어떤 AI 제공자에게도 아무것도 전송되지 않아요.

기록에 남다.

17 출시 시점의 언어
23 MCP 도구
42 코퍼스에 담긴 리프트

Reps는 App Store에서 무료예요. 제 트레이닝을 위해 만들었고, 매일 여는 기록장이 됐어요. 같은 것이 필요하다면 거기에 있어요. 에이전트를 위한 소프트웨어를 만든다면, 훔쳐 갈 가치가 있는 부분은 쓰기 경로예요. (숫자는 출시일 기준이에요.)