Get Bananas
iPhone, iPad, Mac, Apple Watch를 위한 손글씨 스타일 쇼핑 목록 — 손이 자유롭지 않을 때 Claude가 대신 편집해 줍니다.
2026년 4월 출시. 모든 기기에서 SwiftUI로 구동됩니다. Mac 앱 내부에 Claude Desktop 확장 기능을 함께 담았습니다. 29개 언어 각각에 대해 손수 큐레이션한 시드 쇼핑 데이터를 제공해, 사용자가 처음 보는 목록이 번역물이 아니라 현지에서 만든 것처럼 느껴집니다. 서버도, 계정도, API 키도 없습니다. 백엔드의 전부는 iCloud 컨테이너 안에 있는 JSON 파일 하나입니다.
Claude가 당신의 쇼핑 목록을 바꿀 수 있습니다.
Get Bananas는 Claude Desktop 확장 기능을 Mac 앱 자체에 내장해서 함께 배포합니다. Mac에서 설정을 열고 확장 기능 설치를 누르면 Claude Desktop이 설치 안내 창을 띄웁니다. 수락한 뒤 새 채팅을 열면, Claude는 실제 당신의 쇼핑 목록을 읽고 편집할 수 있는 다섯 개의 도구를 갖게 됩니다. "내 목록에 뭐가 있어?"라고 물으면 알려줍니다. "달걀이랑 우유 추가해줘"라고 하면, 다음번 iCloud 동기화가 돌 때 iPhone에 그대로 나타납니다.
가입도, API 키도, 우리 쪽 서버도 없습니다. 확장 기능은 .mcpb 번들이고, 이는 Node.js MCP 서버와 통신하며, 그 서버는 당신의 iCloud Drive 컨테이너 안에 있는 JSON 파일 하나를 읽고 씁니다.
다섯 개의 도구, 각자 하나의 일.
도구의 표면은 의도적으로 작게 유지했습니다. Claude는 목록을 읽거나, 항목을 추가하거나, 삭제하거나, 수정하거나, 전체를 교체할 수 있습니다. 영리한 편의 함수도 없고, read-modify-write 래퍼도 없습니다. 이 표면은 실제로 대화에서 부탁할 법한 것들을 그대로 따릅니다.
- get_shopping_list현재 목록 읽기
- add_item새 항목 추가
- remove_item단일 항목 제거
- update_item체크, 체크 해제, 이름 변경, 또는 수량 변경
- update_shopping_list전체 교체 (예: "추수감사절 목록 시작해줘")
앱 안에서 바로 설치합니다.
Mac 앱의 설정에는 확장 기능 설치 버튼이 있습니다. 이 버튼을 누르면 NSWorkspace를 통해 번들된 get-bananas.mcpb 파일이 열리고, Claude Desktop이 이를 확장 기능으로 등록한 뒤 사용자에게 확인을 요청합니다. 설치 전체가 앱 번들 안에 담긴 파일 하나에 대한 NSWorkspace.shared.open() 호출 한 번 — 플랫폼이 "나를 Claude에 등록해 달라"는 의미를 가진 파일 형식이라는 적절한 원시 도구를 우리에게 건네줬기 때문입니다.
private func installExtension() { guard let extensionURL = Bundle.main.url( forResource: "get-bananas", withExtension: "mcpb" ) else { extensionMessage = "Error: Extension not found in app bundle." return } let opened = NSWorkspace.shared.open(extensionURL) if opened { extensionMessage = "Check Claude Desktop for the install prompt..." } else { extensionMessage = "Couldn't open the extension. Make sure Claude Desktop is installed." } }
이 패턴은 앞으로 수많은 네이티브 앱에 중요해질 것입니다. 확장 기능이 앱 번들 안에 살고, 앱과 함께 배포되고, 앱과 함께 업데이트됩니다 — 별도의 레지스트리도, 사용자별 설정도, "이 URL을 설정 파일에 복사하세요" 같은 안내도 필요 없습니다. Claude가 당신의 앱 안에서 무언가를 할 수 있길 바란다면, 그 무언가를 앱 안에 넣고 OS가 둘을 소개해주도록 두면 됩니다.
종이 위의 펜, 그러나 사라지지 않는.
쇼핑 목록은 손으로, 종이 위에, 펜으로 적는 것입니다. 이 앱은 그렇게 읽힙니다. 섹션 헤더에는 Caveat 스타일의 손글씨 글꼴, 구분선에는 펜으로 그은 듯 가는 선, 체크되지 않은 항목에는 비어 있는 동그라미. 항목을 체크하려고 탭하면, 앱은 텍스트를 가로지르는 손으로 그린 듯한 선을 그립니다 — 그리고 모든 선은 미묘하게 다릅니다.




취소선은 <code>Shape</code>이고, 모든 체크 표시는 단 하나뿐입니다.
이 손으로 그린 듯한 선은 스무 개의 경로 세그먼트로 이루어진 커스텀 SwiftUI Shape입니다. 각 세그먼트의 끝점은 -1.5에서 +1.5 포인트 사이의 작은 무작위 수직 오프셋을 갖습니다. 결과적으로, 런타임에 그려지는 모든 취소선은 직전의 것과 살짝 다릅니다. 스무 개 항목이 있는 목록에서, 똑같은 두 선은 존재하지 않습니다 — 두 번의 펜 자국이 결코 같지 않은 것처럼.
struct HandDrawnStrikethrough: Shape { func path(in rect: CGRect) -> Path { var path = Path() let startY = rect.midY let segments = 20 let segmentWidth = rect.width / CGFloat(segments) path.move(to: CGPoint(x: 0, y: startY)) for i in 1...segments { let x = CGFloat(i) * segmentWidth let randomOffset = CGFloat.random(in: -1.5...1.5) let y = startY + randomOffset path.addLine(to: CGPoint(x: x, y: y)) } return path } }
코드로는 열두 줄입니다. 하지만 이는 손글씨처럼 보이는 앱과 손글씨처럼 느껴지는 앱의 차이이기도 합니다. 정적인 SVG 취소선이라면 같은 날, 같은 픽셀 수로 출시할 수 있었겠지만, 그저 장식으로 읽혔을 것입니다.
위의 체크된 두 항목을 가까이서 보세요. 'Egg whites'와 'Cottage cheese 2%'를 가로지르는 선들은 모양이 다릅니다 — 흔들림도, 각도도. 이 둘은 같은 Shape에 의해 200밀리초 차이로 생성되었으며, 그때마다 새로운 무작위성을 그려낸 결과입니다. 이것이 이 앱의 정체성 전부입니다: 작고 정직한 변화를, 레이아웃에 새겨 넣은 것.
29개 언어, 그리고 현지의 쇼핑 데이터.
일본어 사용자가 처음 여는 목록은 번역된 미국식 장보기 목록이 아닙니다. たんぱく質 섹션의 卵白 — 도쿄의 진열대에서 실제로 볼 법한 단어로 표기된 단백질 섹션의 달걀흰자입니다. App Store에 올라간 29개의 현지화 버전, 그리고 영어 외 모든 로케일은 문화적으로 적절한 샘플 항목과 섹션 이름이 담긴 자체 SeedData-{locale}.json을 함께 제공합니다. 아랍어에는 al-burutinat 섹션의 biyad al-bayd가 들어가고, 히브리어에는 helbonim 섹션의 helbonei beitza가 들어갑니다. 29개의 시드 파일, 모두 손으로 작성한 것이지, 문자열 번역기를 돌린 것이 아닙니다.





RTL과 싸우지 않으면 거저 얻는 승리입니다.
SwiftUI는 .leading과 .trailing을 의미적 방향으로 다루지, .left와 .right를 고정된 방향으로 다루지 않습니다. 화면을 한 번 의미적으로 배치해 두면, 아랍어와 히브리어에서는 전체 레이아웃이 공짜로 뒤집힙니다 — 섹션 헤더는 오른쪽으로, 체크 동그라미는 텍스트의 왼쪽으로, 리셋과 더보기 버튼은 반대쪽 모서리로 회전합니다. 이것을 위해 RTL 전용 코드는 단 한 줄도 작성하지 않았습니다.
손으로 그린 듯한 취소선은 이 모든 것 위에 자리 잡고 있으며, 로케일을 알 필요가 전혀 없습니다. Shape는 항목 이름인 Text 위에 ZStack으로 얹혀 있고, 그 바운딩 박스를 가로질러 그려집니다. 주변의 행 자체가 SwiftUI의 RTL 레이아웃에 의해 거울처럼 뒤집히는 것입니다. 선은 마땅히 있어야 할 자리 — 텍스트 위 — 에 떨어지며, Shape 코드 안에는 언어 분기가 단 하나도 없습니다.
iCloud 컨테이너 속 JSON 파일 하나.
기기 간 데이터 교환은 당신의 iCloud Drive에 있는 파일 하나입니다: Get Bananas 컨테이너 안의 BananaList.json. MCP 서버는 정확히 이 파일을 읽고 씁니다. iOS, iPad, Mac, Watch 앱 역시 정확히 이 파일을 읽고 씁니다. 동기화는 iCloud가 처리합니다 — Apple의 무료, 저장 시 암호화, 고급 데이터 보호가 켜져 있을 때는 종단 간 비공개인 인프라입니다. 우리 쪽 인프라는 없습니다.
// iCloud path for Banana List data const ICLOUD_FILE_PATH = path.join( os.homedir(), "Library/Mobile Documents/iCloud~com~941apps~Banana-List/Documents/BananaList.json" ); // Lock file for atomic operations const LOCK_FILE_PATH = ICLOUD_FILE_PATH + ".lock"; const LOCK_TIMEOUT_MS = 5000;
MCP 서버가 다루는 모든 경로는 그 JSON 파일의 형제입니다. 잠금 파일은 그 옆에 위치하고(.lock, 원자적 쓰기, 5초 타임아웃 후 오래된 잠금은 스스로 정리됨), 쓰기는 프로세스 태그가 붙은 임시 파일(.tmp.{pid})로 들어갔다가 실제 경로로 원자적으로 이름이 변경됩니다. Claude와 실행 중인 앱 사이의 동시성 모델 전체가 acquire/release 함수 하나에 들어맞습니다.
각 기기에서는 SwiftData가 로컬 저장소입니다. iCloud의 JSON은 통신용 형식입니다. SwiftData는 기기 위에서 빠르고 쿼리 가능한 상태를 유지하고, JSON은 동기화와 Claude의 조작을 위해 단순하고 사람이 읽을 수 있는 상태를 유지합니다. 두 저장소가 각자 하나의 일을 맡고 — 어느 쪽도 두 가지를 다 하려고 하지 않습니다.
이것이 우리에게 약속할 자격을 줍니다.
941 Apps 서버 없음. 분석 없음. 로그인 없음. Claude를 위한 API 키 없음 — 당신은 이미 Claude Desktop에서 인증을 마쳤으니까요. 구독 없음. 확장 기능은 오픈 소스이므로, 궁금한 사람은 누구나 데이터의 모든 경로를 읽을 수 있습니다: 그 경로는 JSON 파일이고, 그 외에는 아무것도 건드리지 않습니다. 당신의 쇼핑 목록은 우리 인프라를 통해서는 결코 iCloud 컨테이너를 떠나지 않습니다.
할 일은 목록입니다.
쇼핑 목록은 하나의 일입니다. 떠오른 순간에 항목을 적고, 매장에서 찾을 때 체크하고, 그 외의 시간에는 시야에서 비켜 있게 하는 것. 제품은 그 일 자체입니다. 레시피 앱이 아니고, 식단 플래너가 아니고, 바코드 스캐너가 아니고, 가격 추적기도, 재고 관리자도 아닙니다. 결과물은 목록입니다.
Claude 통합은 휴대폰에 타이핑하기에 적절치 않은 순간에 항목 입력과 편집의 속도를 높여주기 때문에 자기 자리를 얻습니다: 마늘 냄새가 밴 손으로 가스레인지 앞에 서 있을 때, 한 주의 식사 계획을 입으로 말하고 있을 때, 레시피 카드에서 열두 가지 항목을 일일이 다시 타이핑하지 않고 옮겨 적고 싶을 때. 그런 순간들을 Claude에게 위임하면, 앱의 나머지 부분은 작게 유지될 수 있습니다.
목록도, 입력도, 체크도, 그 위임도 아닌 모든 것은 다른 제품입니다. 다른 제품은 다른 앱에 속해야 합니다.
Get Bananas에 없는 것들.
대부분의 쇼핑 목록 앱은 "Instacart가 되는 것"으로 끝나는 로드맵을 가지고 있습니다. 이 앱은 그게 아닙니다. Get Bananas는 의도적으로 식료품 플랫폼으로 자라기를 거부합니다. 이 앱이 하지 <em>않는</em> 일들의 명시적 목록은 다음과 같습니다:
- 바코드 스캔 없음
- 가격, 합계, 가계부 없음
- 레시피 가져오기나 식단 계획 없음
- 매장 통로 안내나 매장 지도 없음
- 웹 앱 없음
- 계정 없음, 로그인 없음, 이메일 수집 없음
- 다른 사용자와의 목록 공유 없음 (오직 당신의 iCloud, 당신의 기기들 사이에서만)
- 분석 없음, 영원히
- 광고 없음, 구독 없음, Pro 등급 없음
- 독촉 화면 없음, 인앱 업셀 없음
Get Bananas는 0.99달러입니다. 한 번 결제하면 당신의 것이 됩니다. MCP 서버는 오픈 소스입니다. 데이터는 당신의 것이고 당신의 iCloud Drive에 머무릅니다. 할 일은 빠르게 적고, 빠르게 체크하고, 손이 고수로 가득 차 있을 때 편집을 Claude에게 맡기는 것입니다. 그 외의 모든 것은 다른 누군가의 앱입니다.
Get Bananas.
iPhone, iPad, Mac, Apple Watch용으로 App Store에서 지금 만나보세요. Claude Desktop 확장 기능은 Mac 앱 안에서 바로 설치할 수 있습니다.