GUIDES9-OPERATION · REPORTS · 2026-07-12

네이티브 앱(iOS·macOS) 전환
미리 알아두는 친절 안내서

지금 만드는 웹 프로토타입을 나중에 iPhone·Mac용 네이티브 앱으로 옮길 때, 무엇을 미리 생각해 둬야 하는지 쉽고 자세히 풀어 쓴 안내서입니다.

네이티브 앱(iOS·macOS) 전환 — 미리 알아두는 친절 안내서

지금 만드는 웹 프로토타입을 나중에 iPhone·Mac용 네이티브 앱으로 옮길 때, 어떤 것들을 미리 생각해 둬야 하는지를 쉽고 자세히 풀어 쓴 안내서입니다.
짝이 되는 압축 결정 기록: native-app-swiftdata-considerations-2026-07-12 — 이 안내서는 같은 내용을 더 친절하고 자세하게 설명한 버전입니다.
작성 2026-07-12 · 대상 독자: 나중에 이 작업을 이어받을 나 자신(그리고 함께 볼 사람)

011. 이 문서를 읽기 전에

지금 우리는 어디에 있나

지금은 웹 프로토타입을 계속 다듬는 단계입니다. 데이터가 실제로 어떻게 구성돼야 하는지, 화면의 UI 요소가 적절한지, 서비스 전체 컨셉의 균형이 맞는지를 웹에서 빠르게 실험하는 시기죠. 네이티브 앱 만들기는 이 실험이 어느 정도 마무리된 뒤에 시작할 일입니다.

그래서 이 문서는 "지금 당장 만들 설계도"가 아닙니다. "나중에 네이티브로 옮길 때 발이 걸리지 않도록, 미리 지도를 그려 두는 것"이 목적입니다. 지금 웹에서 무언가를 결정할 때 "이게 나중에 네이티브에서 문제가 될까?"를 이 문서로 확인할 수 있게요.

이 문서를 읽는 법

한 문장 요약

이 앱은 "내 인생을 기록하는 아주 사적인 앱"입니다. 로그인·회원가입은 없지만, 데이터는 내 애플 계정을 통해 iPhone과 Mac에 조용히 동기화되고, 캘린더·사진과 연동되며, 나중엔 Mac에서 Claude가 내 데이터를 읽고 시나리오를 함께 검토할 수 있게 하려 합니다.

022. 먼저 알아두면 좋은 낱말들

기술 용어를 미리 쉬운 말로 풀어 둡니다. 뒤에서 이 단어들이 나오면 여기를 떠올리세요.


033. 큰 그림 — 웹 프로토와 네이티브는 뭐가 다른가

네이티브 전환은 "화면만 Swift로 다시 그리는 일"이 아닙니다. 밑바탕 세 가지가 통째로 바뀝니다. 이걸 먼저 이해하면 나머지가 쉽습니다.

(1) 데이터의 "진짜 주인"이 바뀐다

지금 웹 프로토는 타임라인 엔진이라는 부품이 사건·레인 데이터의 주인이고, 나머지 화면은 그걸 비춰 보는 구조입니다. 네이티브로 가면 데이터의 진짜 주인은 무조건 SwiftData가 됩니다. 그러니 전환 작업은 곧 "데이터의 주인을 SwiftData로 옮기는 일"이기도 합니다.

(2) "여러 사용자 구분"이 사라진다

지금 웹 프로토는 여러 사람이 각자 로그인해서 자기 데이터만 보도록 격리하는 구조(핸들+PIN 로그인)를 갖고 있습니다. 그런데 네이티브 앱은 회원가입을 안 만들기로 했으니, 이 "여러 사용자 구분" 장치가 통째로 필요 없어집니다. 대신 "이 기기를 쓰는 나 한 사람"이 암묵적인 주인입니다.

(3) "인터넷이 있어야 도는 앱"에서 "혼자서도 잘 도는 앱"으로

지금 웹 프로토는 인터넷(그리고 Supabase라는 클라우드 DB)에 바로 붙어서 읽고 씁니다. 네이티브 앱은 반대로 인터넷이 없어도 잘 돌아가는 게 기본이고, 인터넷(iCloud)은 "있으면 두 기기를 맞춰 주는" 선택적 역할입니다.

가장 중요한 큰 결정 3가지(뒤에서 자세히): ① 타임라인을 어떻게 다시 그릴까 → 웹뷰 없이 네이티브로(§5) ② 두 기기 데이터를 어떻게 맞출까 → iCloud 동기화(§7) ③ 나중에 Claude를 어떻게 붙일까 → MCP(§11).

전환에 유리한 두 가지 선물

1. 캘린더·카메라는 웹 프로토에 아직 없습니다. 그러니 이 둘은 "고쳐 쓸 낡은 코드"가 없는 완전 새 출발 — 처음부터 네이티브답게 깔끔히 설계할 수 있습니다. 2. 웹 프로토에는 이미 "데이터 창구"가 하나로 잘 정리돼 있습니다(journey-data.js라는 파일). 저장·불러오기·수정이 전부 이 한 곳을 지나갑니다. 네이티브에서 이 창구를 Swift로 옮기면 되니, 설계도가 이미 절반쯤 그려져 있는 셈입니다.


044. 어느 기기·어떤 화면 모양으로 만들까

최소 지원 버전 — iOS 18 / macOS 15 이상 권장

무엇: 앱이 돌아갈 가장 낮은 OS 버전을 정하는 일. 왜 고민: 낮게 잡으면 오래된 기기까지 쓸 수 있지만, 최신 기능을 못 씁니다. SwiftData는 iOS 17부터 생겼는데 초기 버전엔 버그가 많았습니다. 권장: iOS 18 / macOS 15 이상. 개인용 앱이라 구형 기기 지원 이득이 작고, 우리는 데이터 저장·동기화를 강하게 쓰므로 더 안정된 2세대 SwiftData를 쓰는 게 이득입니다.

화면 구조 — 기기 크기에 맞춰 변신

무엇: iPhone(작은 화면)과 iPad·Mac(큰 화면)에서 화면 배치를 어떻게 할지. 권장: 적응형 — iPhone에선 아래쪽 탭 바, iPad·Mac에선 옆쪽 사이드바 + 2단 화면으로 자동 변신. 큰 화면에선 여정과 상세를 나란히 볼 수 있어 좋습니다. 웹 프로토가 이미 "화면 크기에 따라 배치를 바꾸는" 철학을 갖고 있어 자연스럽게 이어집니다.


055. 타임라인 엔진을 다시 그리기 · ✅ 확정

타임라인 엔진은 이 앱의 심장입니다. 5가지 배치 방식, 겹치는 막대 정리 규칙, 핀치 줌·플링, 색 시스템까지 수개월간 다듬은 소중한 자산이죠. 이걸 네이티브에서 어떻게 할지가 큰 결정이었습니다.

결정: 웹뷰 없이, 전면 네이티브로 다시 그린다 (Core Graphics 기반, 고성능)

"웹뷰"란 네이티브 앱 안에 웹페이지를 창처럼 끼워 넣는 방식입니다. 기존 웹 엔진을 그대로 재활용할 수 있어 빠르지만, 제스처가 어색하고, 접근성(음성 안내)·글자 크기 조절을 잃고, "네이티브답지 않은" 느낌이 남습니다.

그래서 웹뷰는 쓰지 않고, 타임라인을 애플의 그리기 도구로 처음부터 다시 그리기로 했습니다. 손은 더 가지만, 아주 부드러운 120Hz 화면·음성 안내·글자 크기 자동 조절·넓은 색 표현을 얻고, 무엇보다 나중에 Claude(MCP)와도 잘 맞는 깔끔한 구조가 됩니다.

걱정되는 점과 해결: "그동안 다듬은 튜닝을 다 다시 만들어야 하나?" — 다행히 그 튜닝값(막대 두께, 겹침 감광 정도, 배치 규칙, 색)은 "계산 부분"으로 옮겨져 데이터·규칙으로 보존됩니다. 눈대중으로 다시 맞추는 게 아니라, 값을 그대로 가져오는 것에 가깝습니다.

핵심 아이디어: 엔진을 "계산"과 "그리기" 둘로 나눈다

지금 엔진은 "어디에 무엇을 놓을지 계산하는 일""실제로 픽셀로 칠하고 손가락 제스처를 받는 일"이 한 덩어리로 얽혀 있습니다. 네이티브에선 이걸 둘로 나눕니다:

이렇게 나누면 "화면"과 "AI"가 같은 계산 결과를 공유하게 되어 아주 깔끔합니다.

"아주 좋은 퍼포먼스"의 비밀 한 줄

핵심은 "손가락으로 확대·이동할 때, 매번 다시 그리지 말고 이미 그린 걸 '변형(transform)'만 시켜라"입니다. 화면을 확대할 때마다 수백 개 막대를 처음부터 다시 그리면 버벅입니다. 대신 한 번 그려 둔 그림을 GPU가 "쓱" 확대·이동시키면 부드럽습니다. 여기에 몇 가지 규칙을 더합니다:

그리기 도구 선택 — B2(기본) vs B3(나중 확장)

두 가지 방법이 있고, 각각 잘하는 게 다릅니다.

B2 — CALayer + Core Graphics (기본으로 채택)

B3 — Metal (나중에 필요하면 확장)

왜 B2로 시작하나: 개인 인생 타임라인은 화면에 보이는 게 보통 수백 개 수준이고 글자가 많습니다. 이건 정확히 B2가 잘하는 영역이에요. Metal의 강점(수만 개·픽셀 효과)은 지금 당장 필요 없습니다. 그리고 "그림 목록"으로 나눠 뒀기 때문에, 나중에 정말 필요하면 그리기 부분만 Metal로 갈아끼우면 됩니다(계산 부분은 그대로).


066. 데이터를 어떻게 저장할까 (SwiftData 모델링)

우리 데이터 모델은 이미 잘 정리돼 있습니다(사건·사람·장소·대상·돈·시나리오·지갑…). SwiftData는 웹에서 쓰던 데이터베이스와 성질이 조금 달라서, 몇 군데는 "정직한 번역"이 필요합니다. 각 항목을 쉽게 풀어 봅니다.

여러 종류를 담는 자리 — "폴리모픽 참조"

상황: "이 사건에 누가·어디가·무엇이 엮였나(성분)"에서, 엮이는 대상이 사람일 수도, 장소일 수도, 대상일 수도 있습니다. 어려움: SwiftData의 연결은 보통 "한 종류"로 고정됩니다. 권장: 성분처럼 종류가 3가지로 정해진 곳은 각 종류별 연결을 나란히 두고 그 중 하나만 채우는 방식(SwiftData가 무결성을 챙겨 주고, "이 사람이 나온 사건들" 같은 역참조가 공짜). 반면 돈(목표·배정)처럼 대상이 더 이질적인 곳은 "종류 + 번호" 방식으로 유연하게. 즉 한 방법으로 무리하게 통일하지 않고 자리에 맞게 씁니다.

"만약에" 시나리오 겹치기

상황: 이 앱은 "만약 57세에 은퇴하면?" 같은 가정의 미래(시나리오)를 확정된 현재 위에 겹쳐 보여 줍니다. 권장: 이 겹침은 저장해 두는 게 아니라 그때그때 계산합니다("확정된 세계 − 이 시나리오가 가린 것 + 이 시나리오가 더한 것"). 웹 프로토가 이미 이렇게 하고 있고, 이 방식이 나중에 Claude가 "가정 세계"를 즉석에서 만들어 비교하기에도 딱 좋습니다.

지운 것도 되살릴 수 있게

상황: 인생 기록은 함부로 영구 삭제하면 안 됩니다. 하지만 시나리오나 "날짜 미정 착상"처럼 가벼운 건 그냥 지워도 됩니다. 권장: 도메인 기록은 "숨김" 처리(소프트 삭제)로 보존하고, 사건을 진짜 지울 땐 성분·돈 정보까지 함께 사진 찍듯 스냅샷으로 남겨 나중에 통째로 되살릴 수 있게 합니다(웹 프로토의 "되돌리기" 경험 그대로).

계산으로 되는 건 저장하지 않는다

상황: "이 사건이 과거냐 미래냐", 합계, 미래 순자산 곡선 같은 건 원본 데이터로부터 계산할 수 있습니다. 권장: 이런 건 저장하지 말고 그때그때 계산합니다(원본과 어긋날 일이 없음). 다만 무거운 곡선 계산은 화면 쪽에서 살짝 캐싱해 빠르게.

작은 묶음 정보는 "값 묶음"으로

상황: 지갑(Pocket)에는 성장률·잠김 규칙·수령 방식 같은 작은 하위 정보가 딸려 있습니다. 권장: 이런 건 따로 저장 테이블로 만들지 말고 작은 값 묶음으로 지갑 안에 품습니다. "상품 종류를 미리 못 박지 않는다"는 우리 설계 원칙과도 맞습니다.

돈은 반드시 정확하게

권장: 금액은 정수(원 단위)나 정밀 숫자(Decimal)로 저장하고, 소수점 오차가 나는 방식(부동소수)은 절대 금지. 화면에만 "만 원"으로 보여 주고, 저장은 "원"으로. 이 변환은 딱 한 군데에서만 하도록.

데이터마다 안 바뀌는 고유번호(UUID)

권장: 모든 데이터에 재설치·기기 이동에도 안 바뀌는 UUID를 붙입니다. 백업·캘린더 연동·나중의 동기화·연동이 전부 이 안정된 번호를 필요로 합니다.

나중에 모양이 바뀌어도 안 깨지게

상황: 웹 프로토 단계엔 데이터 구조를 마음껏 갈아엎었지만, 출시된 앱은 사용자 기기의 데이터를 깨면 큰일입니다. 권장: 첫 출시부터 "구조 변경 이사 계획(마이그레이션)" 장치를 켜 두고, 이후 변경은 되도록 "기존 것 유지 + 새 항목 추가" 방식으로.

날짜·시간대 — 숨은 지뢰

상황: 웹 프로토는 시간 처리가 "한국 시간 벽시계"와 "세계 표준시(UTC)"가 섞여 있습니다. 권장: 저장은 세계 표준시 기준으로 통일하고, "생년월일·만기·시대 경계"처럼 시각이 아니라 '달력 날짜'인 것은 날짜 요소로 다뤄 하루씩 밀리는 실수를 막습니다. "오늘"·나이·과거/미래 판단의 기준 시간대를 코드 한 곳에 딱 고정.


077. 두 기기에서 같은 데이터 보기 · ✅ 확정

결정: 회원가입 없이, 애플 계정으로 조용히 동기화 (CloudKit)

고민의 핵심: "회원가입은 안 만드는데, 그럼 iPhone과 Mac이 어떻게 같은 데이터를 보지?"

여기서 중요한 구분: 사용자가 원한 "회원 없음"은 "우리 서비스의 회원가입/로그인을 안 만든다"는 뜻이지, "iCloud 동기화도 쓰지 말자"는 뜻이 아닙니다. 오히려 iPhone·Mac 공용을 원한다는 것 자체가 두 기기 데이터가 같기를 바란다는 신호죠.

그래서 CloudKit 프라이빗 DB를 씁니다. 이건 우리가 회원 시스템을 안 만들어도, 애플 계정(iCloud)으로 "이 사람의 데이터"를 조용히 두 기기에 동기화해 줍니다. 데이터는 사용자의 iCloud에만 저장되고(우리 서버 없음), 프라이버시 이야기와도 잘 맞습니다. 웹 프로토가 하던 "사용자 격리"를 애플이 대신 해 주는 셈입니다.

대신 지켜야 할 규칙들

CloudKit을 켜면 데이터 설계에 몇 가지 제약이 따라옵니다(처음부터 지켜야 나중에 켤 수 있습니다):

설정·프로필은 어디에?

백업(내보내기)

회원이 없어 백업이 유일한 안전망입니다(특히 iCloud 없이 로컬만 쓰는 경우). 그래서 초기부터 zip으로 내보내기(md·csv·사진 동봉)를 넣습니다. 되돌려 가져오기(복원)는 나중에.


088. 프라이버시·보안

이 앱은 인생 이야기 + 등장인물 실명 + 재무 전체를 담습니다. 유출되면 피해가 큰 데이터라, 회원이 없어도 보안은 오히려 더 신경 써야 합니다 — 기기 보안이 유일한 방어선이니까요.


099. 캘린더 연동 · ✅ 확정

우리 "사건"과 시스템 캘린더의 "일정"은 겹치지만 똑같지는 않습니다. 잘못 이으면 중복·유령 일정이 생기죠.

결정: 읽기 + 쓰기 양방향 — 단, 캘린더가 못 담는 정보는 "메모에 구조화 포맷으로" 왕복

사용자의 통찰 두 가지가 설계를 정했습니다: 1. 읽어 오는 일반 캘린더 일정엔, 우리 서비스가 필요로 하는 정보가 없을 수 있다 (제목·날짜뿐). 2. 우리가 만든 사건엔, 캘린더 기본 칸(제목·기간·장소·알림)에 안 들어가는 값(왜·어떻게·성분·상태·시나리오·돈…)이 있다.

그래서 이 "남는 정보"를 캘린더 일정의 '메모(notes)' 칸에 정해진 형식(구조화 포맷)으로 실어 주고받습니다.

쓸 때(내보내기): 우리 사건을 캘린더 일정으로 만들되, 제목·기간·장소·알림은 캘린더 기본 칸에 넣고, 나머지 정보는 메모 아래쪽에 정해진 블록으로 적습니다. 위쪽은 사용자가 자유롭게 쓰는 메모로 남겨 둡니다:

… (사용자 자유 메모) …
⟨life-journey v1⟩
id: <고유번호>
why: 왜 했나
how: 어떻게
who: [사람]  where: 장소
status: todo
⟨/life-journey⟩

읽을 때(가져오기): 두 경우로 나눕니다.

안전장치들

매핑할 때 조심할 점


1010. 사진·카메라 연동 · ✅ 확정

사진이 앱에 들어오는 세 걸음: 요청 → 다듬기 → 저장

① 기기에 요청 — "고르거나 찍거나"

② 다듬기 — 저장하기 전에 꼭 (프라이버시·용량의 급소)

③ 저장 — iCloud와 함께

사진은 어디에 붙나 — Event + Person부터 (owner는 폴리모픽)

핵심: 사진은 "특정 한 곳 전용"이 아니라, 우리 모델에서 이미 갤러리(2차 렌즈)의 자리입니다. 즉 여러 종류(사건·사람·장소·대상·성찰)에 두루 붙고, 갤러리 화면이 그 전부를 시간별·인물별·장소별로 훑는 구조가 자연스럽습니다.

그래서 사진(Attachment)의 "주인" 자리를 폴리모픽으로 열어 두되, 실제 구현은 단계적으로:


1111. Claude(Code·앱) 연동 — MCP · ✅ 방향 확정

무엇을 하려는 건가

Mac 버전 앱을 "MCP 서버"로 만들어, 나중에 Claude Code나 Claude 앱에서 접근해 ① 내 서비스의 데이터·구성을 바꾸고 ② 여러 시나리오를 함께 검토할 수 있게 하려는 것입니다. 쉽게 말해, Claude를 내 인생 데이터의 co-pilot(부조종사)으로 삼는 것이죠.

범위(확정): 둘 다 하되, 중심은 "개인 데이터 코파일럿"입니다("57세 은퇴 시나리오를 만들어 곡선을 비교해 줘" 같은 것). 앱 구성(용어·주제 등) 관리도 얹지만 부차적입니다.

가장 조심할 점 하나 — "데이터 주인은 앱 하나뿐"

기술적 급소는 "SwiftData 데이터의 주인은 앱 하나여야 한다"는 것입니다. 앱과 MCP가 같은 데이터 저장소에 동시에 쓰면 망가집니다. 그래서:

Claude에게 무엇을 열어 주나

우리 데이터 창구(journey-data.js)의 기능과 거의 그대로 대응됩니다: 타임라인·지갑·시나리오·순자산 곡선을 읽고, 시나리오를 만들고, 미래를 투영·비교하는 등. 그래서 §5에서 "계산 부분을 깨끗한 core로 뽑는 일"이 여기서도 그대로 재사용됩니다.

프라이버시 — 새로 뚫리는 유일한 문

⚠️ Claude가 데이터를 읽으면 그 데이터가 Anthropic 클라우드로 나갑니다. "내 기기·iCloud에만" 원칙과는 다른 축이라, 의도된 예외로서 명확히 동의받아야 합니다. 그래서: 기본 꺼짐 · 읽기 위주 · 바꾸는 동작은 확인 받기 · 실명/정밀 금액은 가리기 옵션 · 접근 기록 남기기 · 로컬에서만 접속.

언제 만드나

처음엔 MCP 없이 앱부터 잘 만들고, 계산 core가 안정된 뒤에 얹습니다. core를 잘 뽑아 두면 MCP는 그 위의 얇은 어댑터라 나중에 붙이기 쉽습니다.


1212. 기존 데이터 옮기기 · ✅ 확정

결정: 새 출발 + 지금 데이터는 내보내 보관

지금 실데이터가 두 곳에 흩어져 있습니다 — 도메인(사건·돈 등)은 클라우드 DB(Supabase)에, 지갑은 아직 브라우저 로컬 저장에. 게다가 상당수가 실험·테스트 흔적입니다.

그래서 "새 출발"이 기본입니다: 시드·테스트 없이 깨끗이 시작하고, 지금 데이터는 md/csv/json으로 내보내 참고·백업만 합니다. 실제 보유한 데이터(진짜 재무·인물 등)를 손으로 다시 넣으며 모델을 한 번 더 검증하는 기회로 삼습니다(지갑 실제 상품 반영 등).

만약 나중에 "다시 넣기 아까운 진짜 데이터"가 많아지면, 스키마가 이미 맞으니 일회 가져오기 도구를 만들거나, MCP가 서면 Claude가 내보낸 파일을 읽어 채워 넣는 반자동 이관도 값싼 대안입니다.


1313. 확정된 것 한눈에

주제결정
저장 도구SwiftData
회원/계정만들지 않음 — 프로필·설정은 기기에 사적으로
두 기기 동기화iCloud(CloudKit) 프라이빗 DB — 회원 없이 애플 계정으로
타임라인 엔진웹뷰 없이 전면 네이티브(Core Graphics 고성능·B2 기본·Metal은 나중)
계산/그리기 분리✅ 계산은 순수 Swift core → 화면·MCP가 공유
캘린더읽기+쓰기 양방향, 남는 정보는 캘린더 메모에 구조화 포맷으로 왕복
사진✅ 촬영=iPhone만·앨범=공통·전부 동기화·위치정보 제거 / 주인은 폴리모픽, Event+Person부터
Claude 연동(MCP)✅ 방향 확정 — Mac이 MCP 서버, 앱이 데이터 유일 주인, 기본 꺼짐
기존 데이터새 출발 + 내보내 보관

1414. 나중에 네이티브를 시작할 때 체크리스트

웹 프로토가 어느 정도 여물어 네이티브를 시작하게 되면, 대략 이 순서를 떠올리세요:

1. 데이터 모델부터 — 지금의 데이터 구조를 SwiftData @Model로 옮기고(폴리모픽·시나리오 겹·지갑·사진·금액·UUID·날짜 규칙 반영), 첫날부터 "구조 변경 이사 계획"과 iCloud 제약을 지켜 둔다. 2. 계산 core 뽑기 — 타임라인 배치·시나리오 합성·순자산 곡선을 화면과 무관한 순수 Swift로. (이게 화면·MCP 공용 심장) 3. 화면 짓기 — 프로필·수첩·여비·설정은 SwiftUI로, 타임라인은 Core Graphics(B2)로 고성능 재구성. 4. 기기 기능 붙이기 — 사진(앨범·촬영), 캘린더(메모 왕복), 파일 내보내기. 5. 다듬기 — 성능 프로파일링(필요하면 Metal), 접근성, 다크모드, 앱 잠금. 6. 나중에 MCP — core가 안정된 뒤 Claude 연동을 얇게 얹기.

지금 웹에서 무언가를 정할 때, 위 항목 중 관련된 곳을 열어 "이 결정이 나중에 걸림돌이 될까?"만 확인하면 됩니다. 대부분은 이미 "그래도 괜찮게" 설계돼 있습니다.