[{"data":1,"prerenderedAt":2486},["ShallowReactive",2],{"navigation_docs_ko":3,"-ko-project-brobean-brobean-dev-log-11":267,"-ko-project-brobean-brobean-dev-log-11-surround":2481},[4,130,253],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":33},"Note (개발 지식)","emojione-v1:note-pad","\u002Fko\u002Fnote","ko\u002F1.note",[10,34,48,74,84,94,110,120],{"title":11,"icon":12,"path":13,"stem":14,"children":15,"page":33},"Spring","logos:spring-icon","\u002Fko\u002Fnote\u002Fspring","ko\u002F1.note\u002F0.spring",[16,21,25,29],{"title":17,"path":18,"stem":19,"icon":20},"Spring AI 핵심 개념과 LLM 애플리케이션 구조 이해","\u002Fko\u002Fnote\u002Fspring\u002Fspring-ai","ko\u002F1.note\u002F0.spring\u002F1.spring-ai","lucide:file-text",{"title":22,"path":23,"stem":24,"icon":20},"[Java] Record와 @Version으로 이해하는 불변 데이터와 동시성 제어","\u002Fko\u002Fnote\u002Fspring\u002Frecord-and-version-concurrency","ko\u002F1.note\u002F0.spring\u002F2.record-and-version-concurrency",{"title":26,"path":27,"stem":28,"icon":20},"[JPA] @PersistenceContext와 EntityManager로 이해하는 영속성 컨텍스트","\u002Fko\u002Fnote\u002Fspring\u002Fspring-persistence-context","ko\u002F1.note\u002F0.spring\u002F3.spring-persistence-context",{"title":30,"path":31,"stem":32,"icon":20},"[Java] YearMonth로 월 단위 날짜를 다루는 방법","\u002Fko\u002Fnote\u002Fspring\u002Fjava-year-month","ko\u002F1.note\u002F0.spring\u002F4.java-year-month",false,{"title":35,"icon":36,"path":37,"stem":38,"children":39,"page":33},"Javascript","skill-icons:javascript","\u002Fko\u002Fnote\u002Fjavascript","ko\u002F1.note\u002F1.javascript",[40,44],{"title":41,"path":42,"stem":43,"icon":20},"Promise.all과 구조 분해 할당을 활용한 데이터 처리 패턴","\u002Fko\u002Fnote\u002Fjavascript\u002Fdestructuring","ko\u002F1.note\u002F1.javascript\u002F1.destructuring",{"title":45,"path":46,"stem":47,"icon":20},"JavaScript 배열 고차 함수와 데이터 처리 패턴 이해","\u002Fko\u002Fnote\u002Fjavascript\u002Ffilter","ko\u002F1.note\u002F1.javascript\u002F2.filter",{"title":49,"icon":50,"path":51,"stem":52,"children":53,"page":33},"Nuxt4","logos:nuxt-icon","\u002Fko\u002Fnote\u002Fnuxt4","ko\u002F1.note\u002F2.nuxt4",[54,58,62,66,70],{"title":55,"path":56,"stem":57,"icon":20},"Nuxt 4를 선택한 이유와 아키텍처 및 실무 가이드","\u002Fko\u002Fnote\u002Fnuxt4\u002Fnuxt-info","ko\u002F1.note\u002F2.nuxt4\u002F1.nuxt-info",{"title":59,"path":60,"stem":61,"icon":20},"Nuxt useAsyncData 동작 원리와 SSR 데이터 페칭 최적화","\u002Fko\u002Fnote\u002Fnuxt4\u002Fnuxt-use_async_data","ko\u002F1.note\u002F2.nuxt4\u002F2.nuxt-use_async_data",{"title":63,"path":64,"stem":65,"icon":20},"Nuxt Content v3 데이터 모델과 Query 시스템 이해","\u002Fko\u002Fnote\u002Fnuxt4\u002Fnuxt-content-v3","ko\u002F1.note\u002F2.nuxt4\u002F3.nuxt-content-v3",{"title":67,"path":68,"stem":69,"icon":20},"Nuxt Auto-imports 내부 동작과 컴파일 과정 분석","\u002Fko\u002Fnote\u002Fnuxt4\u002Fnuxt-auto-import","ko\u002F1.note\u002F2.nuxt4\u002F4.nuxt-auto-import",{"title":71,"path":72,"stem":73,"icon":20},"Nuxt 렌더링 방식 이해: SSR, SSG, ISR, CSR 구현 방법","\u002Fko\u002Fnote\u002Fnuxt4\u002Fnuxt-rendering-strategy","ko\u002F1.note\u002F2.nuxt4\u002F5.nuxt-rendering-strategy",{"title":75,"icon":76,"path":77,"stem":78,"children":79,"page":33},"Nitro","unjs:nitro","\u002Fko\u002Fnote\u002Fnitro","ko\u002F1.note\u002F3.nitro",[80],{"title":81,"path":82,"stem":83,"icon":20},"Nitro 아키텍처와 서버 엔진 구조","\u002Fko\u002Fnote\u002Fnitro\u002Fnitro","ko\u002F1.note\u002F3.nitro\u002Fnitro",{"title":85,"icon":86,"path":87,"stem":88,"children":89,"page":33},"Devops","vscode-icons:file-type-pipeline","\u002Fko\u002Fnote\u002Fdevops","ko\u002F1.note\u002F4.devops",[90],{"title":91,"path":92,"stem":93,"icon":20},"서버사이드 WebAssembly(Wasm)와 차세대 실행 환경","\u002Fko\u002Fnote\u002Fdevops\u002Fwebassembly","ko\u002F1.note\u002F4.devops\u002F1.webAssembly",{"title":95,"icon":96,"path":97,"stem":98,"children":99,"page":33},"Tooling","lucide:wrench","\u002Fko\u002Fnote\u002Ftooling","ko\u002F1.note\u002F4.tooling",[100],{"title":101,"icon":102,"path":103,"stem":104,"children":105,"page":33},"Package Manager","devicon:npm","\u002Fko\u002Fnote\u002Ftooling\u002Fpackage-manager","ko\u002F1.note\u002F4.tooling\u002F1.package-manager",[106],{"title":107,"path":108,"stem":109,"icon":20},"Package Lock 파일의 역할과 의존성 관리 원리","\u002Fko\u002Fnote\u002Ftooling\u002Fpackage-manager\u002Fpackage-lock","ko\u002F1.note\u002F4.tooling\u002F1.package-manager\u002F1.package-lock",{"title":111,"icon":112,"path":113,"stem":114,"children":115,"page":33},"Web","streamline-plump:web","\u002Fko\u002Fnote\u002Fweb","ko\u002F1.note\u002F4.web",[116],{"title":117,"path":118,"stem":119,"icon":20},"웹 애플리케이션 렌더링 아키텍처 이해","\u002Fko\u002Fnote\u002Fweb\u002Frendering-modes","ko\u002F1.note\u002F4.web\u002F2.rendering-modes",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":33},"SEO","icon-park-solid:seo","\u002Fko\u002Fnote\u002Fseo","ko\u002F1.note\u002F6.seo",[126],{"title":127,"path":128,"stem":129,"icon":20},"[SEO] 개발 블로그 검색 노출을 위한 robots.txt와 SEO 설정 가이드","\u002Fko\u002Fnote\u002Fseo\u002Fseo","ko\u002F1.note\u002F6.seo\u002F1.seo",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":33},"Project (개발일지)","icon-park-solid:branch-two","\u002Fko\u002Fproject","ko\u002F2.project",[136,187],{"title":137,"icon":138,"path":139,"stem":140,"children":141,"page":33},"Brolog","custom:symbol","\u002Fko\u002Fproject\u002Fbrolog","ko\u002F2.project\u002F1.brolog",[142,146,150,154,158,162,166,170,174,179,183],{"title":143,"path":144,"stem":145,"icon":20},"[Brolog] 프로젝트 개요","\u002Fko\u002Fproject\u002Fbrolog\u002Foutline","ko\u002F2.project\u002F1.brolog\u002F1.outline",{"title":147,"path":148,"stem":149,"icon":20},"[Brolog] Nuxt Multi Layer 프로젝트 Vercel 배포 (3) - Runtime 기능 문제 분석과 AI Assistant 전환","\u002Fko\u002Fproject\u002Fbrolog\u002Fvercel-deploy3-ai-integration","ko\u002F2.project\u002F1.brolog\u002F10.vercel-deploy3-ai-integration",{"title":151,"path":152,"stem":153,"icon":20},"[Brolog] AI Assistant 적용 과정 - Agent 구조와 API 사용량 관리 시 고려사항","\u002Fko\u002Fproject\u002Fbrolog\u002Fai-assistant-application-process","ko\u002F2.project\u002F1.brolog\u002F11.ai-assistant-application-process",{"title":155,"path":156,"stem":157,"icon":20},"[Brolog] 검색 엔진 노출을 위한 SEO 적용 과정","\u002Fko\u002Fproject\u002Fbrolog\u002Fbrolog-seo","ko\u002F2.project\u002F1.brolog\u002F12.brolog-seo",{"title":159,"path":160,"stem":161,"icon":20},"[Brolog] 프로젝트 구조","\u002Fko\u002Fproject\u002Fbrolog\u002Fstructure","ko\u002F2.project\u002F1.brolog\u002F2.structure",{"title":163,"path":164,"stem":165,"icon":20},"[Brolog] Nuxt Content 네비게이션 아이콘 커스텀 빌드","\u002Fko\u002Fproject\u002Fbrolog\u002Fcustom-icon","ko\u002F2.project\u002F1.brolog\u002F3.custom-icon",{"title":167,"path":168,"stem":169,"icon":20},"[Brolog] Nuxt4 Auto-imports 동작 원리와 자동 import 시스템 이해","\u002Fko\u002Fproject\u002Fbrolog\u002Fnuxt-auto-import","ko\u002F2.project\u002F1.brolog\u002F4.nuxt-auto-import",{"title":171,"path":172,"stem":173,"icon":20},"[Brolog] 개발 블로그 메인 페이지 구현","\u002Fko\u002Fproject\u002Fbrolog\u002Fdevelop-main","ko\u002F2.project\u002F1.brolog\u002F5.develop-main",{"title":175,"path":176,"stem":177,"icon":178},"[Brolog] 브랜드 아이덴티티 적용 - Custom 404 페이지와 Default No Image 구현","\u002Fko\u002Fproject\u002Fbrolog\u002Fcustom-error-and-default-thumbnail","ko\u002F2.project\u002F1.brolog\u002F6.custom-error-and-default-thumbnail","lucide:image",{"title":180,"path":181,"stem":182,"icon":20},"[Brolog] Nuxt Multi Layer 프로젝트 Vercel 배포 (1)","\u002Fko\u002Fproject\u002Fbrolog\u002Fvercel-deploy1","ko\u002F2.project\u002F1.brolog\u002F8.vercel-deploy1",{"title":184,"path":185,"stem":186,"icon":20},"[Brolog] Nuxt Multi Layer 프로젝트 Vercel 배포 (2) - Build Pipeline 오류 분석과 Nuxt 실행 구조 이해","\u002Fko\u002Fproject\u002Fbrolog\u002Fvercel-deploy2-error-analyze","ko\u002F2.project\u002F1.brolog\u002F9.vercel-deploy2-error-analyze",{"title":188,"icon":189,"path":190,"stem":191,"children":192,"page":33},"Brobean","custom:brobean","\u002Fko\u002Fproject\u002Fbrobean","ko\u002F2.project\u002F2.brobean",[193,197,201,205,209,213,217,221,225,229,233,237,241,245,249],{"title":194,"path":195,"stem":196,"icon":20},"[BroBean] 연인과 함께 사용하는 가계부: BroBean - 프로젝트 기획과 이름의 탄생","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-01","ko\u002F2.project\u002F2.brobean\u002F1.brobean-dev-log-01",{"title":198,"path":199,"stem":200,"icon":20},"[BroBean] 연인과 함께 사용하는 가계부: BroBean 프로젝트 시작 - Nuxt 4 기반 프론트 개발 환경 구축","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-02","ko\u002F2.project\u002F2.brobean\u002F2.brobean-dev-log-02",{"title":202,"path":203,"stem":204,"icon":20},"[BroBean] BroBean 프로젝트 #3 - 모바일 앱 전환을 고려한 공통 레이아웃 설계","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-03","ko\u002F2.project\u002F2.brobean\u002F3.brobean-dev-log-03",{"title":206,"path":207,"stem":208,"icon":20},"[BroBean] BroBean 프로젝트 #4 - 홈 화면(Dashboard) 설계와 Mock 데이터 구성","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-04","ko\u002F2.project\u002F2.brobean\u002F4.brobean-dev-log-04",{"title":210,"path":211,"stem":212,"icon":20},"[BroBean] BroBean 프로젝트 #5 돈을 모으면 캐릭터도 성장한다 - 브로빈 성장 시스템 설계","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-05","ko\u002F2.project\u002F2.brobean\u002F5.brobean-dev-log-05",{"title":214,"path":215,"stem":216,"icon":20},"[BroBean] BroBean 프로젝트 #6 씨앗 심기와 밭 가꾸기 기능 설계","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-06","ko\u002F2.project\u002F2.brobean\u002F6.brobean-dev-log-06",{"title":218,"path":219,"stem":220,"icon":20},"[BroBean] BroBean 프로젝트 #7 성장 일지와 나무 키우기 화면 구현","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-07","ko\u002F2.project\u002F2.brobean\u002F7.brobean-dev-log-07",{"title":222,"path":223,"stem":224,"icon":20},"[BroBean] BroBean 프로젝트 #8 수확 정산으로 한눈에 보는 소비 흐름 - 소비 리포트 & 정산 화면 구현","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-08","ko\u002F2.project\u002F2.brobean\u002F8.brobean-dev-log-08",{"title":226,"path":227,"stem":228,"icon":20},"[BroBean] BroBean 프로젝트 #9 알림부터 마이페이지까지 - 사용자 편의 기능과 개인 농장 설정 화면 구현","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-09","ko\u002F2.project\u002F2.brobean\u002F9.brobean-dev-log-09",{"title":230,"path":231,"stem":232,"icon":20},"[BroBean] BroBean 프로젝트 #10 로그인부터 파트너 초대까지 - 계정 인증과 커플 연결 화면 구현","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-10","ko\u002F2.project\u002F2.brobean\u002F910.brobean-dev-log-10",{"title":234,"path":235,"stem":236,"icon":20},"[BroBean] BroBean 프로젝트 #11 개인과 커플을 모두 위한 모드 분리 - Pinia 기반 개인\u002F커플 모드 전환 구현","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-11","ko\u002F2.project\u002F2.brobean\u002F911.brobean-dev-log-11",{"title":238,"path":239,"stem":240,"icon":20},"[BroBean] BroBean 프로젝트 #12 인증 상태 복구와 Nuxt Plugin 실행 순서 제어","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-12","ko\u002F2.project\u002F2.brobean\u002F912.brobean-dev-log-12",{"title":242,"path":243,"stem":244,"icon":20},"[BroBean] BroBean 프로젝트 #13 개인·커플 모드와 가계부 API 구현 - 쿼리 성능을 다시 돌아보다","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-13","ko\u002F2.project\u002F2.brobean\u002F913.brobean-dev-log-13",{"title":246,"path":247,"stem":248,"icon":20},"[BroBean] BroBean 프로젝트 #14 순환 참조 오류를 ObjectProvider로 해결하기","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-14","ko\u002F2.project\u002F2.brobean\u002F914.brobean-dev-log-14",{"title":250,"path":251,"stem":252,"icon":20},"[BroBean] BroBean 프로젝트 #15 JPA 페이지네이션 적용, Page와 Slice의 차이","\u002Fko\u002Fproject\u002Fbrobean\u002Fbrobean-dev-log-15","ko\u002F2.project\u002F2.brobean\u002F915.brobean-dev-log-15",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":33},"Error (트러블슈팅)","emojione:stop-sign","\u002Fko\u002Ftrouble-shooting","ko\u002F3.trouble-shooting",[259],{"title":137,"icon":138,"path":260,"stem":261,"children":262,"page":33},"\u002Fko\u002Ftrouble-shooting\u002Fbrolog","ko\u002F3.trouble-shooting\u002F1.brolog",[263],{"title":264,"path":265,"stem":266,"icon":20},"[Nuxt Content] Nuxt Icon 오류 해결과 Custom Collection 기반 오프라인 아이콘 구성","\u002Fko\u002Ftrouble-shooting\u002Fbrolog\u002Fnuxt-icon-restricted-network-troubleshooting","ko\u002F3.trouble-shooting\u002F1.brolog\u002F1.nuxt-icon-restricted-network-troubleshooting",{"id":268,"title":234,"body":269,"date":2463,"description":2464,"extension":2465,"links":2466,"meta":2467,"navigation":2476,"path":235,"seo":2477,"stem":236,"__hash__":2480},"docs_ko\u002Fko\u002F2.project\u002F2.brobean\u002F911.brobean-dev-log-11.md",{"type":270,"value":271,"toc":2457},"minimark",[272,2453],[273,274,276,281,290,293,296,299,302,305,308,311,319,322,325,328,331,334,337,340,343,350,353,364,371,374,378,381,384,390,393,396,399,405,408,415,418,425,431,434,436,440,443,448,451,540,543,549,552,559,561,565,568,571,574,580,583,586,592,595,602,604,608,611,614,620,623,625,631,634,641,644,650,653,656,658,662,665,667,673,676,678,684,687,693,699,702,704,708,711,714,720,723,725,731,734,737,739,743,746,749,755,758,764,767,770,773,775,779,782,784,790,793,795,801,803,806,809,811,815,818,825,828,831,834,840,843,846,852,854,858,861,864,866,894,897,900,906,909,912,919,922,1270,1273,1275,1279,1282,1303,1306,1309,1325,1328,1331,1527,1530,1533,1539,1542,1544,1548,1554,1557,1603,1606,1609,1615,1618,1621,1624,1630,1633,1639,1642,1648,1651,1653,1657,1660,1663,1666,1672,1675,1681,1684,1687,1690,1697,1699,1703,1710,1713,1716,1722,1725,1727,1757,1763,1769,1772,1775,1781,1784,1786,1790,1793,1796,1802,1805,1811,1814,1817,1820,1822,1826,1829,1835,1838,1844,1847,1850,1852,1856,1859,1862,1865,1871,1878,1881,1883,1887,1890,1893,1938,1941,1944,1985,1988,2029,2032,2035,2037,2041,2044,2054,2057,2063,2066,2069,2071,2077,2080,2088,2091,2093,2097,2100,2103,2106,2112,2115,2118,2124,2127,2133,2136,2138,2142,2145,2150,2153,2156,2159,2166,2169,2171,2177,2180,2183,2189,2192,2198,2200,2204,2207,2210,2213,2219,2221,2227,2230,2232,2236,2239,2242,2247,2253,2257,2263,2266,2272,2276,2282,2285,2287,2291,2294,2297,2300,2306,2309,2312,2318,2321,2323,2327,2333,2336,2339,2342,2377,2380,2382,2386,2392,2395,2401,2404,2407,2412,2415,2422,2425,2431,2434,2437,2443,2446],"u-page-card",{":spotlight":275},"true",[277,278,280],"h1",{"id":279},"개인과-커플을-모두-사용할-수-있는-농장-만들기","👨‍🌾 개인과 커플을 모두 사용할 수 있는 농장 만들기",[282,283,284,285,289],"p",{},"앞선 글까지 BroBean은 ",[286,287,288],"strong",{},"커플이 함께 자산을 관리하는 서비스","라는 방향을 중심으로 기능을 확장해왔다.",[282,291,292],{},"가계부를 기록하고,",[282,294,295],{},"저축 목표를 세우고,",[282,297,298],{},"챌린지를 진행하고,",[282,300,301],{},"예산을 관리하고,",[282,303,304],{},"고정지출을 설정하고,",[282,306,307],{},"마지막으로 두 사람이 함께 농장을 키워가는 구조다.",[282,309,310],{},"그런데 실제 기능을 하나씩 구현하다 보니 한 가지 문제가 생겼다.",[312,313,314],"blockquote",{},[282,315,316],{},[286,317,318],{},"BroBean을 꼭 커플만 사용할 수 있어야 할까?",[282,320,321],{},"커플이 아닌 사용자도 자신의 소비와 저축을 관리할 수 있어야 한다면,",[282,323,324],{},"기존의 커플 중심 데이터 구조를 그대로 사용할 수는 없었다.",[282,326,327],{},"예를 들어 한 사용자가 혼자 가계부를 작성한다고 생각해보자.",[282,329,330],{},"개인적으로 지출을 기록하고,",[282,332,333],{},"개인적인 저축 목표를 만들고,",[282,335,336],{},"개인 챌린지를 진행하고,",[282,338,339],{},"개인 예산을 설정하고,",[282,341,342],{},"개인 고정지출을 관리할 수 있어야 한다.",[282,344,345,346,349],{},"반대로 파트너와 연결한 사용자는 같은 기능을 ",[286,347,348],{},"커플 단위의 데이터","로 관리해야 한다.",[282,351,352],{},"결국 하나의 서비스 안에서 다음 두 가지 사용 경험을 동시에 제공해야 했다.",[354,355,361],"pre",{"className":356,"code":358,"language":359,"meta":360},[357],"language-text","개인 모드\n   ↓\n나 혼자 사용하는 농장\n\n커플 모드\n   ↓\n파트너와 함께 사용하는 농장\n","text","",[362,363,358],"code",{"__ignoreMap":360},[282,365,366,367,370],{},"그래서 이번에는 BroBean에 ",[286,368,369],{},"개인 모드 \u002F 커플 모드","라는 개념을 도입했다.",[372,373],"hr",{},[277,375,377],{"id":376},"_1-왜-개인-모드와-커플-모드를-나누게-되었을까","🌱 1. 왜 개인 모드와 커플 모드를 나누게 되었을까?",[282,379,380],{},"처음에는 BroBean의 대부분의 기능을 커플 기준으로 생각했다.",[282,382,383],{},"예를 들어 가계부 데이터도:",[354,385,388],{"className":386,"code":387,"language":359,"meta":360},[357],"사용자\n  ↓\n커플\n  ↓\n가계부\n",[362,389,387],{"__ignoreMap":360},[282,391,392],{},"와 같은 구조로 생각할 수 있었다.",[282,394,395],{},"하지만 개인 사용자까지 지원하려면 상황이 달라진다.",[282,397,398],{},"개인 사용자는:",[354,400,403],{"className":401,"code":402,"language":359,"meta":360},[357],"사용자\n  ↓\n개인 농장\n  ↓\n개인 가계부\n",[362,404,402],{"__ignoreMap":360},[282,406,407],{},"를 사용해야 하기 때문이다.",[282,409,410,411,414],{},"즉, 단순히 ",[286,412,413],{},"파트너가 있느냐 없느냐","만으로 데이터를 구분하는 것은 부족했다.",[282,416,417],{},"사용자가 현재 어떤 공간에서 서비스를 이용하고 있는지를 나타내는 개념이 필요했다.",[282,419,420,421,424],{},"그것이 바로 ",[362,422,423],{},"mode","다.",[354,426,429],{"className":427,"code":428,"language":359,"meta":360},[357],"PERSONAL\n\n↓\n\n개인 농장 \u002F 개인 가계부 \u002F 개인 목표\n\n\nCOUPLE\n\n↓\n\n커플 농장 \u002F 커플 가계부 \u002F 커플 목표\n",[362,430,428],{"__ignoreMap":360},[282,432,433],{},"이렇게 현재 사용자가 어떤 모드에 있는지를 기준으로 데이터를 조회하고 저장하도록 구조를 변경하기 시작했다.",[372,435],{},[277,437,439],{"id":438},"_2-개인커플-모드는-무엇이-달라지는가","🔄 2. 개인\u002F커플 모드는 무엇이 달라지는가?",[282,441,442],{},"이번에 가장 중요하게 생각한 부분은 단순히 화면의 제목만 바꾸는 것이 아니었다.",[282,444,445],{},[286,446,447],{},"서비스의 주요 데이터 자체가 모드에 따라 분리되어야 한다.",[282,449,450],{},"현재 기준으로 다음 영역을 모두 개인\u002F커플 모드로 구분한다.",[452,453,454,470],"table",{},[455,456,457],"thead",{},[458,459,460,464,467],"tr",{},[461,462,463],"th",{},"기능",[461,465,466],{},"개인 모드",[461,468,469],{},"커플 모드",[471,472,473,485,496,507,518,529],"tbody",{},[458,474,475,479,482],{},[476,477,478],"td",{},"🌱 농장",[476,480,481],{},"개인 농장",[476,483,484],{},"커플 농장",[458,486,487,490,493],{},[476,488,489],{},"💰 가계부",[476,491,492],{},"개인 가계부",[476,494,495],{},"커플 가계부",[458,497,498,501,504],{},[476,499,500],{},"🌳 저축 목표",[476,502,503],{},"개인 목표",[476,505,506],{},"공동 목표",[458,508,509,512,515],{},[476,510,511],{},"🏆 챌린지",[476,513,514],{},"개인 챌린지",[476,516,517],{},"커플 챌린지",[458,519,520,523,526],{},[476,521,522],{},"📊 예산",[476,524,525],{},"개인 예산",[476,527,528],{},"커플 예산",[458,530,531,534,537],{},[476,532,533],{},"🔁 고정지출",[476,535,536],{},"개인 고정지출",[476,538,539],{},"커플 고정지출",[282,541,542],{},"즉,",[354,544,547],{"className":545,"code":546,"language":359,"meta":360},[357],"모드\n\n↓\n\n농장\n가계부\n저축\n챌린지\n예산\n고정지출\n",[362,548,546],{"__ignoreMap":360},[282,550,551],{},"전체가 같은 모드 기준으로 움직인다.",[282,553,554,555,558],{},"이 구조를 선택한 이유는 사용자가 현재 개인 모드인지 커플 모드인지에 따라 ",[286,556,557],{},"서비스 전체의 컨텍스트가 바뀌어야 하기 때문","이다.",[372,560],{},[277,562,564],{"id":563},"_3-개인-농장과-커플-농장은-다른-공간이다","🏡 3. 개인 농장과 커플 농장은 다른 공간이다",[282,566,567],{},"BroBean에서 농장은 단순한 꾸미기 화면이 아니다.",[282,569,570],{},"가계부 기록과 저축 활동이 농장의 성장과 연결되어 있기 때문에 농장 자체가 사용자의 금융 활동을 보여주는 중요한 공간이다.",[282,572,573],{},"따라서 개인 모드에서는:",[354,575,578],{"className":576,"code":577,"language":359,"meta":360},[357],"내 소비\n+\n내 저축\n+\n내 챌린지\n+\n내 활동\n\n↓\n\n내 농장\n",[362,579,577],{"__ignoreMap":360},[282,581,582],{},"이 되고,",[282,584,585],{},"커플 모드에서는:",[354,587,590],{"className":588,"code":589,"language":359,"meta":360},[357],"내 소비\n+\n파트너의 소비\n+\n공동 저축\n+\n커플 챌린지\n+\n두 사람의 활동\n\n↓\n\n우리 농장\n",[362,591,589],{"__ignoreMap":360},[282,593,594],{},"이 된다.",[282,596,597,598,601],{},"따라서 모드 전환은 단순히 메뉴 하나를 바꾸는 기능이 아니라 ",[286,599,600],{},"사용자가 바라보는 농장 자체를 바꾸는 기능","이라고 볼 수 있다.",[372,603],{},[277,605,607],{"id":606},"_4-가계부도-개인과-커플을-분리해야-한다","💰 4. 가계부도 개인과 커플을 분리해야 한다",[282,609,610],{},"가장 직접적으로 영향을 받은 기능은 가계부다.",[282,612,613],{},"개인 모드에서는:",[354,615,618],{"className":616,"code":617,"language":359,"meta":360},[357],"내가 오늘 사용한 10,000원\n",[362,619,617],{"__ignoreMap":360},[282,621,622],{},"을 기록한다.",[282,624,585],{},[354,626,629],{"className":627,"code":628,"language":359,"meta":360},[357],"나의 지출\n+\n파트너의 지출\n\n↓\n\n우리의 가계부\n",[362,630,628],{"__ignoreMap":360},[282,632,633],{},"라는 개념이 된다.",[282,635,636,637,640],{},"따라서 같은 ",[362,638,639],{},"expense"," 데이터를 사용하더라도 어떤 모드에서 작성되었는지를 구분할 수 있어야 한다.",[282,642,643],{},"개념적으로는 다음과 같은 구조가 된다.",[354,645,648],{"className":646,"code":647,"language":359,"meta":360},[357],"Expense\n\n├── personal\n│   └── 개인 사용자의 지출\n│\n└── couple\n    └── 커플 공간의 지출\n",[362,649,647],{"__ignoreMap":360},[282,651,652],{},"이렇게 해두면 사용자가 개인 모드에서 기록한 소비가 커플 가계부에 섞이는 문제를 방지할 수 있다.",[282,654,655],{},"반대로 커플 모드에서 기록한 소비는 파트너와 공유되는 공간의 데이터가 된다.",[372,657],{},[277,659,661],{"id":660},"_5-저축-목표도-모드에-따라-달라진다","🌳 5. 저축 목표도 모드에 따라 달라진다",[282,663,664],{},"저축 목표 역시 개인과 커플을 나눠야 한다.",[282,666,613],{},[354,668,671],{"className":669,"code":670,"language":359,"meta":360},[357],"내 여행 자금\n내 비상금\n내 노트북 구매\n",[362,672,670],{"__ignoreMap":360},[282,674,675],{},"처럼 개인적인 목표를 관리할 수 있다.",[282,677,585],{},[354,679,682],{"className":680,"code":681,"language":359,"meta":360},[357],"우리 여행 자금\n신혼집 자금\n데이트 비용\n공동 목표\n",[362,683,681],{"__ignoreMap":360},[282,685,686],{},"처럼 함께 달성해야 하는 목표를 관리할 수 있다.",[282,688,689,690],{},"즉, 같은 저축 기능이라도 ",[286,691,692],{},"목표의 주체가 달라진다.",[354,694,697],{"className":695,"code":696,"language":359,"meta":360},[357],"PERSONAL\n→ 나의 목표\n\nCOUPLE\n→ 우리의 목표\n",[362,698,696],{"__ignoreMap":360},[282,700,701],{},"이 차이를 서비스 전반에서 일관되게 유지하는 것이 중요했다.",[372,703],{},[277,705,707],{"id":706},"_6-챌린지도-개인커플로-분리","🏆 6. 챌린지도 개인\u002F커플로 분리",[282,709,710],{},"챌린지 역시 마찬가지다.",[282,712,713],{},"예를 들어 개인 모드에서는:",[354,715,718],{"className":716,"code":717,"language":359,"meta":360},[357],"일주일 동안 배달 음식 줄이기\n카페 지출 줄이기\n매일 가계부 작성하기\n",[362,719,717],{"__ignoreMap":360},[282,721,722],{},"같은 개인 챌린지를 진행할 수 있다.",[282,724,585],{},[354,726,729],{"className":727,"code":728,"language":359,"meta":360},[357],"이번 달 외식비 줄이기\n함께 저축하기\n일주일 동안 충동구매 하지 않기\n",[362,730,728],{"__ignoreMap":360},[282,732,733],{},"처럼 두 사람이 함께 참여하는 챌린지를 만들 수 있다.",[282,735,736],{},"따라서 챌린지의 진행 상태 역시 현재 모드와 연결되어야 한다.",[372,738],{},[277,740,742],{"id":741},"_7-예산-설정도-모드에-따라-분리","📊 7. 예산 설정도 모드에 따라 분리",[282,744,745],{},"예산은 특히 개인과 커플의 차이가 명확하다.",[282,747,748],{},"개인 모드:",[354,750,753],{"className":751,"code":752,"language":359,"meta":360},[357],"이번 달 식비\n300,000원\n\n교통비\n100,000원\n\n쇼핑\n150,000원\n",[362,754,752],{"__ignoreMap":360},[282,756,757],{},"커플 모드:",[354,759,762],{"className":760,"code":761,"language":359,"meta":360},[357],"이번 달 식비\n500,000원\n\n데이트\n300,000원\n\n생활비\n700,000원\n",[362,763,761],{"__ignoreMap":360},[282,765,766],{},"처럼 기준 자체가 달라질 수 있다.",[282,768,769],{},"따라서 사용자가 개인 모드로 전환했는데 커플 예산이 표시된다면 서비스 입장에서는 굉장히 큰 오류가 된다.",[282,771,772],{},"이런 문제를 방지하기 위해 예산 역시 현재 모드를 기준으로 데이터를 조회하는 방향으로 구조를 맞추고 있다.",[372,774],{},[277,776,778],{"id":777},"_8-고정지출도-분리","🔁 8. 고정지출도 분리",[282,780,781],{},"고정지출도 개인과 커플을 구분해야 한다.",[282,783,613],{},[354,785,788],{"className":786,"code":787,"language":359,"meta":360},[357],"휴대폰 요금\n구독 서비스\n개인 보험\n월세\n",[362,789,787],{"__ignoreMap":360},[282,791,792],{},"등을 관리할 수 있다.",[282,794,585],{},[354,796,799],{"className":797,"code":798,"language":359,"meta":360},[357],"공동 관리비\n공동 구독 서비스\n월세\n통신비\n생활비\n",[362,800,798],{"__ignoreMap":360},[282,802,792],{},[282,804,805],{},"특히 고정지출은 매달 자동으로 가계부 데이터에 영향을 줄 수 있는 영역이기 때문에 모드가 섞이면 데이터 정합성에도 영향을 줄 수 있다.",[282,807,808],{},"그래서 이 부분 역시 처음부터 모드 기준을 적용하는 방향으로 변경했다.",[372,810],{},[277,812,814],{"id":813},"_9-그렇다면-모드는-어떻게-전환할까","🔀 9. 그렇다면 모드는 어떻게 전환할까?",[282,816,817],{},"기능을 분리했다면 사용자가 쉽게 모드를 변경할 수 있어야 한다.",[282,819,820,821,824],{},"그래서 이번에는 ",[286,822,823],{},"헤더와 사이드 메뉴에 모드 전환 스위치","를 추가했다.",[282,826,827],{},"헤더에서는 현재 모드를 빠르게 확인하고 전환할 수 있도록 하고,",[282,829,830],{},"사이드 메뉴에서도 같은 상태를 확인할 수 있도록 했다.",[282,832,833],{},"예를 들어:",[354,835,838],{"className":836,"code":837,"language":359,"meta":360},[357],"┌─────────────────────┐\n│ 🌱 BroBean          │\n│                     │\n│ 개인 모드   [ ●── ] │\n│                     │\n├─────────────────────┤\n│ 🏡 농장              │\n│ 💰 가계부            │\n│ 🌳 저축              │\n│ 🏆 챌린지            │\n│ 📊 예산              │\n│ 🔁 고정지출          │\n└─────────────────────┘\n",[362,839,837],{"__ignoreMap":360},[282,841,842],{},"현재 모드가 개인 모드라면 모든 메뉴가 개인 데이터 기준으로 동작하고,",[282,844,845],{},"커플 모드로 변경하면 같은 메뉴가 커플 데이터 기준으로 동작한다.",[282,847,848,849,424],{},"즉, ",[286,850,851],{},"메뉴는 동일하지만 데이터의 컨텍스트가 달라지는 구조",[372,853],{},[277,855,857],{"id":856},"_10-모드-상태는-pinia-store에서-관리","🧠 10. 모드 상태는 Pinia Store에서 관리",[282,859,860],{},"모드 전환 기능을 구현하면서 가장 중요했던 부분 중 하나가 상태 관리였다.",[282,862,863],{},"모드 정보가 여러 컴포넌트에 필요하기 때문이다.",[282,865,833],{},[867,868,869,873,876,879,882,885,888,891],"ul",{},[870,871,872],"li",{},"Header",[870,874,875],{},"Sidebar",[870,877,878],{},"Farm",[870,880,881],{},"Account Book",[870,883,884],{},"Savings",[870,886,887],{},"Challenge",[870,889,890],{},"Budget",[870,892,893],{},"Fixed Expense",[282,895,896],{},"등에서 현재 모드가 필요하다.",[282,898,899],{},"각 컴포넌트에서 따로 상태를 관리한다면 문제가 발생한다.",[354,901,904],{"className":902,"code":903,"language":359,"meta":360},[357],"Header\n→ personal\n\nSidebar\n→ couple\n\nFarm\n→ personal\n",[362,905,903],{"__ignoreMap":360},[282,907,908],{},"처럼 서로 다른 상태를 가지고 있을 수 있기 때문이다.",[282,910,911],{},"그래서 모드 상태는 전역 상태로 관리하기로 했다.",[282,913,914,915,918],{},"BroBean에서는 ",[286,916,917],{},"Pinia Store","를 사용하고 있다.",[282,920,921],{},"개념적으로는 다음과 같은 형태다.",[354,923,928],{"className":924,"code":925,"filename":926,"language":927,"meta":360,"style":360},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export const useModeStore = defineStore('mode', () => {\n\n  const mode = ref\u003C'personal' | 'couple'>('personal')\n\n  const isPersonalMode = computed(() => {\n    return mode.value === 'personal'\n  })\n\n  const isCoupleMode = computed(() => {\n    return mode.value === 'couple'\n  })\n\n  function setMode(nextMode: 'personal' | 'couple') {\n    mode.value = nextMode\n  }\n\n  return {\n    mode,\n    isPersonalMode,\n    isCoupleMode,\n    setMode\n  }\n\n})\n","stores\u002Fmode.ts","ts",[362,929,930,978,985,1036,1041,1063,1087,1095,1100,1120,1139,1146,1151,1188,1203,1209,1214,1222,1230,1238,1246,1252,1257,1262],{"__ignoreMap":360},[931,932,935,939,943,947,951,955,958,961,964,966,969,972,975],"span",{"class":933,"line":934},"line",1,[931,936,938],{"class":937},"s7zQu","export",[931,940,942],{"class":941},"spNyl"," const",[931,944,946],{"class":945},"sTEyZ"," useModeStore ",[931,948,950],{"class":949},"sMK4o","=",[931,952,954],{"class":953},"s2Zo4"," defineStore",[931,956,957],{"class":945},"(",[931,959,960],{"class":949},"'",[931,962,423],{"class":963},"sfazB",[931,965,960],{"class":949},[931,967,968],{"class":949},",",[931,970,971],{"class":949}," ()",[931,973,974],{"class":941}," =>",[931,976,977],{"class":949}," {\n",[931,979,981],{"class":933,"line":980},2,[931,982,984],{"emptyLinePlaceholder":983},true,"\n",[931,986,988,991,994,997,1000,1003,1005,1008,1010,1013,1016,1019,1021,1024,1027,1029,1031,1033],{"class":933,"line":987},3,[931,989,990],{"class":941},"  const",[931,992,993],{"class":945}," mode",[931,995,996],{"class":949}," =",[931,998,999],{"class":953}," ref",[931,1001,1002],{"class":949},"\u003C",[931,1004,960],{"class":949},[931,1006,1007],{"class":963},"personal",[931,1009,960],{"class":949},[931,1011,1012],{"class":949}," |",[931,1014,1015],{"class":949}," '",[931,1017,1018],{"class":963},"couple",[931,1020,960],{"class":949},[931,1022,1023],{"class":949},">",[931,1025,957],{"class":1026},"swJcz",[931,1028,960],{"class":949},[931,1030,1007],{"class":963},[931,1032,960],{"class":949},[931,1034,1035],{"class":1026},")\n",[931,1037,1039],{"class":933,"line":1038},4,[931,1040,984],{"emptyLinePlaceholder":983},[931,1042,1044,1046,1049,1051,1054,1056,1059,1061],{"class":933,"line":1043},5,[931,1045,990],{"class":941},[931,1047,1048],{"class":945}," isPersonalMode",[931,1050,996],{"class":949},[931,1052,1053],{"class":953}," computed",[931,1055,957],{"class":1026},[931,1057,1058],{"class":949},"()",[931,1060,974],{"class":941},[931,1062,977],{"class":949},[931,1064,1066,1069,1071,1074,1077,1080,1082,1084],{"class":933,"line":1065},6,[931,1067,1068],{"class":937},"    return",[931,1070,993],{"class":945},[931,1072,1073],{"class":949},".",[931,1075,1076],{"class":945},"value",[931,1078,1079],{"class":949}," ===",[931,1081,1015],{"class":949},[931,1083,1007],{"class":963},[931,1085,1086],{"class":949},"'\n",[931,1088,1090,1093],{"class":933,"line":1089},7,[931,1091,1092],{"class":949},"  }",[931,1094,1035],{"class":1026},[931,1096,1098],{"class":933,"line":1097},8,[931,1099,984],{"emptyLinePlaceholder":983},[931,1101,1103,1105,1108,1110,1112,1114,1116,1118],{"class":933,"line":1102},9,[931,1104,990],{"class":941},[931,1106,1107],{"class":945}," isCoupleMode",[931,1109,996],{"class":949},[931,1111,1053],{"class":953},[931,1113,957],{"class":1026},[931,1115,1058],{"class":949},[931,1117,974],{"class":941},[931,1119,977],{"class":949},[931,1121,1123,1125,1127,1129,1131,1133,1135,1137],{"class":933,"line":1122},10,[931,1124,1068],{"class":937},[931,1126,993],{"class":945},[931,1128,1073],{"class":949},[931,1130,1076],{"class":945},[931,1132,1079],{"class":949},[931,1134,1015],{"class":949},[931,1136,1018],{"class":963},[931,1138,1086],{"class":949},[931,1140,1142,1144],{"class":933,"line":1141},11,[931,1143,1092],{"class":949},[931,1145,1035],{"class":1026},[931,1147,1149],{"class":933,"line":1148},12,[931,1150,984],{"emptyLinePlaceholder":983},[931,1152,1154,1157,1160,1162,1166,1169,1171,1173,1175,1177,1179,1181,1183,1186],{"class":933,"line":1153},13,[931,1155,1156],{"class":941},"  function",[931,1158,1159],{"class":953}," setMode",[931,1161,957],{"class":949},[931,1163,1165],{"class":1164},"sHdIc","nextMode",[931,1167,1168],{"class":949},":",[931,1170,1015],{"class":949},[931,1172,1007],{"class":963},[931,1174,960],{"class":949},[931,1176,1012],{"class":949},[931,1178,1015],{"class":949},[931,1180,1018],{"class":963},[931,1182,960],{"class":949},[931,1184,1185],{"class":949},")",[931,1187,977],{"class":949},[931,1189,1191,1194,1196,1198,1200],{"class":933,"line":1190},14,[931,1192,1193],{"class":945},"    mode",[931,1195,1073],{"class":949},[931,1197,1076],{"class":945},[931,1199,996],{"class":949},[931,1201,1202],{"class":945}," nextMode\n",[931,1204,1206],{"class":933,"line":1205},15,[931,1207,1208],{"class":949},"  }\n",[931,1210,1212],{"class":933,"line":1211},16,[931,1213,984],{"emptyLinePlaceholder":983},[931,1215,1217,1220],{"class":933,"line":1216},17,[931,1218,1219],{"class":937},"  return",[931,1221,977],{"class":949},[931,1223,1225,1227],{"class":933,"line":1224},18,[931,1226,1193],{"class":945},[931,1228,1229],{"class":949},",\n",[931,1231,1233,1236],{"class":933,"line":1232},19,[931,1234,1235],{"class":945},"    isPersonalMode",[931,1237,1229],{"class":949},[931,1239,1241,1244],{"class":933,"line":1240},20,[931,1242,1243],{"class":945},"    isCoupleMode",[931,1245,1229],{"class":949},[931,1247,1249],{"class":933,"line":1248},21,[931,1250,1251],{"class":945},"    setMode\n",[931,1253,1255],{"class":933,"line":1254},22,[931,1256,1208],{"class":949},[931,1258,1260],{"class":933,"line":1259},23,[931,1261,984],{"emptyLinePlaceholder":983},[931,1263,1265,1268],{"class":933,"line":1264},24,[931,1266,1267],{"class":949},"}",[931,1269,1035],{"class":945},[282,1271,1272],{},"이렇게 하나의 Store에서 현재 모드를 관리하면 각 화면에서는 동일한 상태를 사용할 수 있다.",[372,1274],{},[277,1276,1278],{"id":1277},"_11-header와-sidebar가-같은-상태를-바라보게-만들기","🔄 11. Header와 Sidebar가 같은 상태를 바라보게 만들기",[282,1280,1281],{},"Header에서는:",[354,1283,1285],{"className":924,"code":1284,"language":927,"meta":360,"style":360},"const modeStore = useModeStore()\n",[362,1286,1287],{"__ignoreMap":360},[931,1288,1289,1292,1295,1297,1300],{"class":933,"line":934},[931,1290,1291],{"class":941},"const",[931,1293,1294],{"class":945}," modeStore ",[931,1296,950],{"class":949},[931,1298,1299],{"class":953}," useModeStore",[931,1301,1302],{"class":945},"()\n",[282,1304,1305],{},"를 사용하고,",[282,1307,1308],{},"Sidebar에서도:",[354,1310,1311],{"className":924,"code":1284,"language":927,"meta":360,"style":360},[362,1312,1313],{"__ignoreMap":360},[931,1314,1315,1317,1319,1321,1323],{"class":933,"line":934},[931,1316,1291],{"class":941},[931,1318,1294],{"class":945},[931,1320,950],{"class":949},[931,1322,1299],{"class":953},[931,1324,1302],{"class":945},[282,1326,1327],{},"를 사용한다.",[282,1329,1330],{},"그리고 현재 상태를 기준으로 UI를 표시한다.",[354,1332,1336],{"className":1333,"code":1334,"language":1335,"meta":360,"style":360},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CUButton\n  :variant=\"modeStore.isPersonalMode ? 'solid' : 'ghost'\"\n  @click=\"modeStore.setMode('personal')\"\n>\n  개인\n\u003C\u002FUButton>\n\n\u003CUButton\n  :variant=\"modeStore.isCoupleMode ? 'solid' : 'ghost'\"\n  @click=\"modeStore.setMode('couple')\"\n>\n  커플\n\u003C\u002FUButton>\n","vue",[362,1337,1338,1345,1387,1419,1424,1429,1439,1443,1449,1482,1510,1514,1519],{"__ignoreMap":360},[931,1339,1340,1342],{"class":933,"line":934},[931,1341,1002],{"class":949},[931,1343,1344],{"class":1026},"UButton\n",[931,1346,1347,1350,1353,1355,1358,1361,1363,1366,1369,1371,1374,1376,1379,1381,1384],{"class":933,"line":980},[931,1348,1349],{"class":949},"  :",[931,1351,1352],{"class":941},"variant",[931,1354,950],{"class":949},[931,1356,1357],{"class":949},"\"",[931,1359,1360],{"class":945},"modeStore",[931,1362,1073],{"class":949},[931,1364,1365],{"class":945},"isPersonalMode ",[931,1367,1368],{"class":949},"?",[931,1370,1015],{"class":949},[931,1372,1373],{"class":963},"solid",[931,1375,960],{"class":949},[931,1377,1378],{"class":949}," :",[931,1380,1015],{"class":949},[931,1382,1383],{"class":963},"ghost",[931,1385,1386],{"class":949},"'\"\n",[931,1388,1389,1392,1395,1397,1399,1401,1403,1406,1408,1410,1412,1414,1416],{"class":933,"line":987},[931,1390,1391],{"class":949},"  @",[931,1393,1394],{"class":941},"click",[931,1396,950],{"class":949},[931,1398,1357],{"class":949},[931,1400,1360],{"class":945},[931,1402,1073],{"class":949},[931,1404,1405],{"class":953},"setMode",[931,1407,957],{"class":945},[931,1409,960],{"class":949},[931,1411,1007],{"class":963},[931,1413,960],{"class":949},[931,1415,1185],{"class":945},[931,1417,1418],{"class":949},"\"\n",[931,1420,1421],{"class":933,"line":1038},[931,1422,1423],{"class":949},">\n",[931,1425,1426],{"class":933,"line":1043},[931,1427,1428],{"class":945},"  개인\n",[931,1430,1431,1434,1437],{"class":933,"line":1065},[931,1432,1433],{"class":949},"\u003C\u002F",[931,1435,1436],{"class":1026},"UButton",[931,1438,1423],{"class":949},[931,1440,1441],{"class":933,"line":1089},[931,1442,984],{"emptyLinePlaceholder":983},[931,1444,1445,1447],{"class":933,"line":1097},[931,1446,1002],{"class":949},[931,1448,1344],{"class":1026},[931,1450,1451,1453,1455,1457,1459,1461,1463,1466,1468,1470,1472,1474,1476,1478,1480],{"class":933,"line":1102},[931,1452,1349],{"class":949},[931,1454,1352],{"class":941},[931,1456,950],{"class":949},[931,1458,1357],{"class":949},[931,1460,1360],{"class":945},[931,1462,1073],{"class":949},[931,1464,1465],{"class":945},"isCoupleMode ",[931,1467,1368],{"class":949},[931,1469,1015],{"class":949},[931,1471,1373],{"class":963},[931,1473,960],{"class":949},[931,1475,1378],{"class":949},[931,1477,1015],{"class":949},[931,1479,1383],{"class":963},[931,1481,1386],{"class":949},[931,1483,1484,1486,1488,1490,1492,1494,1496,1498,1500,1502,1504,1506,1508],{"class":933,"line":1122},[931,1485,1391],{"class":949},[931,1487,1394],{"class":941},[931,1489,950],{"class":949},[931,1491,1357],{"class":949},[931,1493,1360],{"class":945},[931,1495,1073],{"class":949},[931,1497,1405],{"class":953},[931,1499,957],{"class":945},[931,1501,960],{"class":949},[931,1503,1018],{"class":963},[931,1505,960],{"class":949},[931,1507,1185],{"class":945},[931,1509,1418],{"class":949},[931,1511,1512],{"class":933,"line":1141},[931,1513,1423],{"class":949},[931,1515,1516],{"class":933,"line":1148},[931,1517,1518],{"class":945},"  커플\n",[931,1520,1521,1523,1525],{"class":933,"line":1153},[931,1522,1433],{"class":949},[931,1524,1436],{"class":1026},[931,1526,1423],{"class":949},[282,1528,1529],{},"이렇게 하면 Header에서 모드를 변경했을 때 Sidebar도 동일한 상태를 바라보기 때문에 즉시 변경된다.",[282,1531,1532],{},"반대로 Sidebar에서 변경해도 Header의 상태가 같이 변경된다.",[354,1534,1537],{"className":1535,"code":1536,"language":359,"meta":360},[357],"                Pinia Store\n                     │\n          ┌──────────┴──────────┐\n          ↓                     ↓\n       Header                 Sidebar\n          │                     │\n          └──────────┬──────────┘\n                     ↓\n              현재 모드 상태\n                     │\n        ┌────────────┼────────────┐\n        ↓            ↓            ↓\n      농장          가계부        예산\n",[362,1538,1536],{"__ignoreMap":360},[282,1540,1541],{},"이런 구조를 사용하면 모드 전환 로직을 특정 UI에 종속시키지 않을 수 있다.",[372,1543],{},[277,1545,1547],{"id":1546},"_12-컴포넌트에서는-모드만-알면-된다","🧩 12. 컴포넌트에서는 모드만 알면 된다",[282,1549,1550,1551,558],{},"또 하나 중요하게 생각한 부분은 ",[286,1552,1553],{},"각 컴포넌트가 모드 전환 자체를 직접 관리하지 않도록 하는 것",[282,1555,1556],{},"예를 들어 농장 페이지에서:",[354,1558,1560],{"className":924,"code":1559,"language":927,"meta":360,"style":360},"const modeStore = useModeStore()\n\nconst currentMode = computed(() => modeStore.mode)\n",[362,1561,1562,1574,1578],{"__ignoreMap":360},[931,1563,1564,1566,1568,1570,1572],{"class":933,"line":934},[931,1565,1291],{"class":941},[931,1567,1294],{"class":945},[931,1569,950],{"class":949},[931,1571,1299],{"class":953},[931,1573,1302],{"class":945},[931,1575,1576],{"class":933,"line":980},[931,1577,984],{"emptyLinePlaceholder":983},[931,1579,1580,1582,1585,1587,1589,1591,1593,1595,1598,1600],{"class":933,"line":987},[931,1581,1291],{"class":941},[931,1583,1584],{"class":945}," currentMode ",[931,1586,950],{"class":949},[931,1588,1053],{"class":953},[931,1590,957],{"class":945},[931,1592,1058],{"class":949},[931,1594,974],{"class":941},[931,1596,1597],{"class":945}," modeStore",[931,1599,1073],{"class":949},[931,1601,1602],{"class":945},"mode)\n",[282,1604,1605],{},"정도만 알고 있으면 된다.",[282,1607,1608],{},"농장 페이지가:",[354,1610,1613],{"className":1611,"code":1612,"language":359,"meta":360},[357],"어디에서 모드가 변경되는지\n누가 모드를 변경했는지\n모드 상태를 어떻게 저장하는지\n",[362,1614,1612],{"__ignoreMap":360},[282,1616,1617],{},"까지 알 필요는 없다.",[282,1619,1620],{},"Store가 상태를 담당하고 페이지는 현재 상태를 소비하는 구조로 분리한다.",[282,1622,1623],{},"이렇게 하면 나중에 모드 전환 UI가:",[354,1625,1628],{"className":1626,"code":1627,"language":359,"meta":360},[357],"Header Switch\n",[362,1629,1627],{"__ignoreMap":360},[282,1631,1632],{},"에서",[354,1634,1637],{"className":1635,"code":1636,"language":359,"meta":360},[357],"Sidebar Switch\n",[362,1638,1636],{"__ignoreMap":360},[282,1640,1641],{},"또는",[354,1643,1646],{"className":1644,"code":1645,"language":359,"meta":360},[357],"Mobile Bottom Sheet\n",[362,1647,1645],{"__ignoreMap":360},[282,1649,1650],{},"등으로 변경되더라도 Store 구조에는 영향을 최소화할 수 있다.",[372,1652],{},[277,1654,1656],{"id":1655},"_13-새로고침하면-모드가-사라지는-문제","💾 13. 새로고침하면 모드가 사라지는 문제",[282,1658,1659],{},"Pinia Store에 상태를 넣으면서 또 하나의 문제가 생겼다.",[282,1661,1662],{},"브라우저를 새로고침하면 메모리에 있던 상태는 초기화된다.",[282,1664,1665],{},"예를 들어 사용자가:",[354,1667,1670],{"className":1668,"code":1669,"language":359,"meta":360},[357],"커플 모드\n",[362,1671,1669],{"__ignoreMap":360},[282,1673,1674],{},"로 사용하다가 새로고침하면:",[354,1676,1679],{"className":1677,"code":1678,"language":359,"meta":360},[357],"개인 모드\n",[362,1680,1678],{"__ignoreMap":360},[282,1682,1683],{},"로 돌아가 버릴 수 있다.",[282,1685,1686],{},"이건 실제 사용자 입장에서 상당히 불편하다.",[282,1688,1689],{},"특히 사용자가 평소에 커플 모드를 주로 사용한다면 페이지에 들어올 때마다 다시 모드를 변경해야 한다.",[282,1691,1692,1693,1696],{},"그래서 최종적으로는 ",[286,1694,1695],{},"사용자의 최근 모드를 서버에서도 기억하도록 하는 방향","으로 설계하고 있다.",[372,1698],{},[277,1700,1702],{"id":1701},"️-14-usersetting-테이블에-최근-모드-저장","🗄️ 14. userSetting 테이블에 최근 모드 저장",[282,1704,1705,1706,1709],{},"BroBean에서는 사용자 설정을 관리하기 위한 ",[362,1707,1708],{},"userSetting"," 테이블을 사용하고 있다.",[282,1711,1712],{},"여기에 최근 사용한 모드 정보를 저장하는 방식이다.",[282,1714,1715],{},"개념적으로는:",[354,1717,1720],{"className":1718,"code":1719,"language":359,"meta":360},[357],"User\n\n↓\n\nUserSetting\n\n├── 알림 설정\n├── 테마 설정\n└── 최근 사용 모드\n",[362,1721,1719],{"__ignoreMap":360},[282,1723,1724],{},"형태가 된다.",[282,1726,833],{},[354,1728,1730],{"className":924,"code":1729,"language":927,"meta":360,"style":360},"type UserMode = 'personal' | 'couple'\n",[362,1731,1732],{"__ignoreMap":360},[931,1733,1734,1737,1741,1743,1745,1747,1749,1751,1753,1755],{"class":933,"line":934},[931,1735,1736],{"class":941},"type",[931,1738,1740],{"class":1739},"sBMFI"," UserMode",[931,1742,996],{"class":949},[931,1744,1015],{"class":949},[931,1746,1007],{"class":963},[931,1748,960],{"class":949},[931,1750,1012],{"class":949},[931,1752,1015],{"class":949},[931,1754,1018],{"class":963},[931,1756,1086],{"class":949},[282,1758,1759,1760,1762],{},"그리고 ",[362,1761,1708],{},"에:",[354,1764,1767],{"className":1765,"code":1766,"language":359,"meta":360},[357],"mode = \"couple\"\n",[362,1768,1766],{"__ignoreMap":360},[282,1770,1771],{},"같은 값을 저장할 수 있다.",[282,1773,1774],{},"사용자가 커플 모드로 전환하면:",[354,1776,1779],{"className":1777,"code":1778,"language":359,"meta":360},[357],"UI에서 모드 변경\n\n↓\n\nPinia Store 변경\n\n↓\n\nuserSetting 업데이트\n\n↓\n\n최근 사용 모드 저장\n",[362,1780,1778],{"__ignoreMap":360},[282,1782,1783],{},"이라는 흐름으로 처리한다.",[372,1785],{},[277,1787,1789],{"id":1788},"_15-pinia와-usersetting의-역할을-나누기","🔄 15. Pinia와 userSetting의 역할을 나누기",[282,1791,1792],{},"여기서 Pinia와 DB의 역할을 동일하게 생각하면 안 된다.",[282,1794,1795],{},"둘은 서로 다른 목적을 가진다.",[354,1797,1800],{"className":1798,"code":1799,"language":359,"meta":360},[357],"Pinia Store\n\n→ 현재 화면에서 사용할 상태\n\n\nuserSetting\n\n→ 사용자의 설정을 영구적으로 저장\n",[362,1801,1799],{"__ignoreMap":360},[282,1803,1804],{},"즉:",[354,1806,1809],{"className":1807,"code":1808,"language":359,"meta":360},[357],"[앱 실행 중]\n\nPinia\n↓\n현재 모드 빠른 참조\n\n\n[페이지 새로고침 \u002F 재접속]\n\nuserSetting\n↓\n마지막 모드 복원\n",[362,1810,1808],{"__ignoreMap":360},[282,1812,1813],{},"이라는 역할 분리가 된다.",[282,1815,1816],{},"이 구조가 좋은 이유는 UI에서 모드 상태를 사용할 때마다 서버를 조회할 필요가 없기 때문이다.",[282,1818,1819],{},"앱이 실행되고 사용자의 설정을 가져온 뒤에는 Pinia Store가 현재 상태를 가지고 있게 된다.",[372,1821],{},[277,1823,1825],{"id":1824},"_16-초기-진입-시-최근-모드-복원","🚀 16. 초기 진입 시 최근 모드 복원",[282,1827,1828],{},"앞으로 최종적으로는 다음과 같은 흐름을 생각하고 있다.",[354,1830,1833],{"className":1831,"code":1832,"language":359,"meta":360},[357],"사용자 로그인\n\n↓\n\nuserSetting 조회\n\n↓\n\n최근 모드 확인\n\n↓\n\nPinia Store 초기화\n\n↓\n\nHeader \u002F Sidebar 반영\n\n↓\n\n현재 모드에 맞는 데이터 조회\n",[362,1834,1832],{"__ignoreMap":360},[282,1836,1837],{},"예를 들어 사용자가 마지막으로 커플 모드를 사용했다면:",[354,1839,1842],{"className":1840,"code":1841,"language":359,"meta":360},[357],"userSetting\nmode = couple\n\n↓\n\nPinia\nmode = couple\n\n↓\n\n화면\n커플 모드\n\n↓\n\n커플 농장 \u002F 가계부 \u002F 저축 \u002F 챌린지 \u002F 예산\n",[362,1843,1841],{"__ignoreMap":360},[282,1845,1846],{},"으로 자연스럽게 이어진다.",[282,1848,1849],{},"이렇게 하면 사용자는 매번 자신이 사용하던 모드를 다시 선택할 필요가 없다.",[372,1851],{},[277,1853,1855],{"id":1854},"_17-모드-전환은-단순한-ui-기능이-아니다","🎯 17. 모드 전환은 단순한 UI 기능이 아니다",[282,1857,1858],{},"처음에는 모드 전환 스위치를 하나 추가하면 끝나는 기능이라고 생각할 수도 있다.",[282,1860,1861],{},"하지만 실제로 구현해보니 그렇지 않았다.",[282,1863,1864],{},"모드라는 개념은 서비스의 여러 데이터와 연결된다.",[354,1866,1869],{"className":1867,"code":1868,"language":359,"meta":360},[357],"                Mode\n                 │\n      ┌──────────┼──────────┐\n      ↓          ↓          ↓\n     Farm     Account     Savings\n      │          │          │\n      └──────────┼──────────┘\n                 ↓\n            Challenge\n                 ↓\n             Budget\n                 ↓\n          Fixed Expense\n",[362,1870,1868],{"__ignoreMap":360},[282,1872,1873,1874,1877],{},"따라서 모드를 도입한다는 것은 사실상 ",[286,1875,1876],{},"서비스 전체에 데이터 컨텍스트를 하나 추가하는 작업","에 가깝다.",[282,1879,1880],{},"이 부분을 초기에 제대로 정의하지 않으면 나중에 각 기능을 다시 수정해야 하는 상황이 생길 수 있다.",[372,1882],{},[277,1884,1886],{"id":1885},"_18-데이터-조회-기준도-모드-중심으로-변경","🧱 18. 데이터 조회 기준도 모드 중심으로 변경",[282,1888,1889],{},"앞으로 API나 Repository 계층에서도 현재 모드를 기준으로 데이터를 조회하게 된다.",[282,1891,1892],{},"예를 들어 가계부 데이터를 가져온다면:",[354,1894,1896],{"className":924,"code":1895,"language":927,"meta":360,"style":360},"const expenses = await getExpenses({\n  mode: modeStore.mode\n})\n",[362,1897,1898,1918,1932],{"__ignoreMap":360},[931,1899,1900,1902,1905,1907,1910,1913,1915],{"class":933,"line":934},[931,1901,1291],{"class":941},[931,1903,1904],{"class":945}," expenses ",[931,1906,950],{"class":949},[931,1908,1909],{"class":937}," await",[931,1911,1912],{"class":953}," getExpenses",[931,1914,957],{"class":945},[931,1916,1917],{"class":949},"{\n",[931,1919,1920,1923,1925,1927,1929],{"class":933,"line":980},[931,1921,1922],{"class":1026},"  mode",[931,1924,1168],{"class":949},[931,1926,1597],{"class":945},[931,1928,1073],{"class":949},[931,1930,1931],{"class":945},"mode\n",[931,1933,1934,1936],{"class":933,"line":987},[931,1935,1267],{"class":949},[931,1937,1035],{"class":945},[282,1939,1940],{},"처럼 사용할 수 있다.",[282,1942,1943],{},"저축 목표 역시:",[354,1945,1947],{"className":924,"code":1946,"language":927,"meta":360,"style":360},"const savings = await getSavingsGoals({\n  mode: modeStore.mode\n})\n",[362,1948,1949,1967,1979],{"__ignoreMap":360},[931,1950,1951,1953,1956,1958,1960,1963,1965],{"class":933,"line":934},[931,1952,1291],{"class":941},[931,1954,1955],{"class":945}," savings ",[931,1957,950],{"class":949},[931,1959,1909],{"class":937},[931,1961,1962],{"class":953}," getSavingsGoals",[931,1964,957],{"class":945},[931,1966,1917],{"class":949},[931,1968,1969,1971,1973,1975,1977],{"class":933,"line":980},[931,1970,1922],{"class":1026},[931,1972,1168],{"class":949},[931,1974,1597],{"class":945},[931,1976,1073],{"class":949},[931,1978,1931],{"class":945},[931,1980,1981,1983],{"class":933,"line":987},[931,1982,1267],{"class":949},[931,1984,1035],{"class":945},[282,1986,1987],{},"예산 역시:",[354,1989,1991],{"className":924,"code":1990,"language":927,"meta":360,"style":360},"const budgets = await getBudgets({\n  mode: modeStore.mode\n})\n",[362,1992,1993,2011,2023],{"__ignoreMap":360},[931,1994,1995,1997,2000,2002,2004,2007,2009],{"class":933,"line":934},[931,1996,1291],{"class":941},[931,1998,1999],{"class":945}," budgets ",[931,2001,950],{"class":949},[931,2003,1909],{"class":937},[931,2005,2006],{"class":953}," getBudgets",[931,2008,957],{"class":945},[931,2010,1917],{"class":949},[931,2012,2013,2015,2017,2019,2021],{"class":933,"line":980},[931,2014,1922],{"class":1026},[931,2016,1168],{"class":949},[931,2018,1597],{"class":945},[931,2020,1073],{"class":949},[931,2022,1931],{"class":945},[931,2024,2025,2027],{"class":933,"line":987},[931,2026,1267],{"class":949},[931,2028,1035],{"class":945},[282,2030,2031],{},"와 같이 동일한 기준을 사용할 수 있다.",[282,2033,2034],{},"이렇게 하면 각 기능이 서로 다른 방식으로 개인\u002F커플을 처리하는 문제를 줄일 수 있다.",[372,2036],{},[277,2038,2040],{"id":2039},"_19-클라이언트의-mode-값만-믿으면-안-된다","🔐 19. 클라이언트의 mode 값만 믿으면 안 된다",[282,2042,2043],{},"다만 여기서 중요한 부분이 하나 있다.",[282,2045,2046,2047,2049,2050,2053],{},"Pinia Store의 ",[362,2048,423],{},"는 ",[286,2051,2052],{},"클라이언트 상태","일 뿐이다.",[282,2055,2056],{},"따라서 서버에서는 절대로:",[354,2058,2061],{"className":2059,"code":2060,"language":359,"meta":360},[357],"클라이언트가 personal이라고 보냈으니\npersonal 데이터겠지\n",[362,2062,2060],{"__ignoreMap":360},[282,2064,2065],{},"라고 단순하게 신뢰하면 안 된다.",[282,2067,2068],{},"특히 커플 데이터는 다른 사용자의 데이터와 연결될 수 있기 때문에 서버에서 반드시 권한 검증이 필요하다.",[282,2070,1715],{},[354,2072,2075],{"className":2073,"code":2074,"language":359,"meta":360},[357],"Client\n\nmode = couple\n\n↓\n\nServer\n\n현재 사용자 확인\n\n↓\n\n커플 관계 확인\n\n↓\n\n해당 커플 데이터 접근 권한 확인\n\n↓\n\n데이터 반환\n",[362,2076,2074],{"__ignoreMap":360},[282,2078,2079],{},"같은 흐름이 필요하다.",[282,2081,848,2082,2049,2084,2087],{},[362,2083,423],{},[286,2085,2086],{},"사용자가 어떤 컨텍스트에서 서비스를 사용하고 있는지를 표현하는 값","이고,",[282,2089,2090],{},"실제 데이터 접근 권한은 서버에서 별도로 검증해야 한다.",[372,2092],{},[277,2094,2096],{"id":2095},"️-20-커플-모드가-없는-사용자도-개인-모드는-사용할-수-있어야-한다","❤️ 20. 커플 모드가 없는 사용자도 개인 모드는 사용할 수 있어야 한다",[282,2098,2099],{},"이번 구조를 만들면서 또 하나 중요하게 생각한 부분이다.",[282,2101,2102],{},"모든 사용자가 반드시 커플 연결을 가지고 있는 것은 아니다.",[282,2104,2105],{},"따라서:",[354,2107,2110],{"className":2108,"code":2109,"language":359,"meta":360},[357],"회원가입\n\n↓\n\n개인 모드\n",[362,2111,2109],{"__ignoreMap":360},[282,2113,2114],{},"가 기본적으로 가능해야 한다.",[282,2116,2117],{},"그리고 이후 사용자가 파트너를 연결하면:",[354,2119,2122],{"className":2120,"code":2121,"language":359,"meta":360},[357],"개인 모드\n\n↓\n\n파트너 연결\n\n↓\n\n커플 모드 사용 가능\n",[362,2123,2121],{"__ignoreMap":360},[282,2125,2126],{},"으로 확장된다.",[282,2128,2129,2130,594],{},"즉, 커플 기능은 개인 기능을 사용하는 데 필요한 조건이 아니라 ",[286,2131,2132],{},"추가로 선택할 수 있는 기능",[282,2134,2135],{},"이 구조가 서비스의 사용자 범위를 넓히는 데도 훨씬 유리하다고 생각했다.",[372,2137],{},[277,2139,2141],{"id":2140},"_21-brobean의-서비스-방향도-조금-달라졌다","🌱 21. BroBean의 서비스 방향도 조금 달라졌다",[282,2143,2144],{},"처음 BroBean을 설계했을 때는:",[312,2146,2147],{},[282,2148,2149],{},"커플이 함께 가계부를 관리하고 농장을 키운다.",[282,2151,2152],{},"라는 방향이 강했다.",[282,2154,2155],{},"하지만 개인\u002F커플 모드를 도입하면서 서비스의 구조가 조금 더 확장됐다.",[282,2157,2158],{},"이제는:",[312,2160,2161],{},[282,2162,2163],{},[286,2164,2165],{},"혼자서도 자신의 농장을 키울 수 있고, 필요하면 파트너와 하나의 농장을 함께 가꿀 수 있다.",[282,2167,2168],{},"라는 방향으로 발전하게 되었다.",[282,2170,398],{},[354,2172,2175],{"className":2173,"code":2174,"language":359,"meta":360},[357],"나의 소비\n나의 저축\n나의 목표\n나의 농장\n",[362,2176,2174],{"__ignoreMap":360},[282,2178,2179],{},"을 관리하고,",[282,2181,2182],{},"커플 사용자는:",[354,2184,2187],{"className":2185,"code":2186,"language":359,"meta":360},[357],"우리의 소비\n우리의 저축\n우리의 목표\n우리의 농장\n",[362,2188,2186],{"__ignoreMap":360},[282,2190,2191],{},"을 관리한다.",[282,2193,2194,2195],{},"기능은 비슷하지만 ",[286,2196,2197],{},"서비스를 사용하는 단위가 다르다.",[372,2199],{},[277,2201,2203],{"id":2202},"_22-모바일에서는-모드-전환을-더-쉽게","📱 22. 모바일에서는 모드 전환을 더 쉽게",[282,2205,2206],{},"BroBean은 모바일 사용을 중요하게 생각하고 있기 때문에 모드 전환 UI도 작은 화면에서 빠르게 접근할 수 있도록 구성했다.",[282,2208,2209],{},"헤더에서는 현재 모드를 한눈에 확인할 수 있도록 하고,",[282,2211,2212],{},"사이드 메뉴에서는 메뉴를 탐색하면서 모드를 바로 변경할 수 있도록 했다.",[282,2214,2215,2216,558],{},"특히 중요한 것은 모드를 변경했을 때 단순히 스위치만 바뀌는 것이 아니라 ",[286,2217,2218],{},"현재 화면의 데이터 컨텍스트도 함께 변경되는 것",[282,2220,833],{},[354,2222,2225],{"className":2223,"code":2224,"language":359,"meta":360},[357],"개인 모드\n\n가계부\n→ 개인 지출\n\n\n커플 모드\n\n가계부\n→ 커플 지출\n",[362,2226,2224],{"__ignoreMap":360},[282,2228,2229],{},"처럼 사용자가 모드 변경 결과를 바로 체감할 수 있어야 한다.",[372,2231],{},[277,2233,2235],{"id":2234},"_23-모드-전환-테스트에서-확인해야-할-것","🧪 23. 모드 전환 테스트에서 확인해야 할 것",[282,2237,2238],{},"모드 기능을 추가하면서 단순히 스위치가 잘 바뀌는 것만 테스트해서는 부족하다.",[282,2240,2241],{},"최소한 다음과 같은 흐름을 확인해야 한다.",[2243,2244,2246],"h3",{"id":2245},"개인-커플","개인 → 커플",[354,2248,2251],{"className":2249,"code":2250,"language":359,"meta":360},[357],"개인 모드\n\n↓\n\n커플 모드 전환\n\n↓\n\n커플 데이터 조회\n\n↓\n\n화면 변경\n",[362,2252,2250],{"__ignoreMap":360},[2243,2254,2256],{"id":2255},"커플-개인","커플 → 개인",[354,2258,2261],{"className":2259,"code":2260,"language":359,"meta":360},[357],"커플 모드\n\n↓\n\n개인 모드 전환\n\n↓\n\n개인 데이터 조회\n\n↓\n\n화면 변경\n",[362,2262,2260],{"__ignoreMap":360},[2243,2264,2265],{"id":2265},"새로고침",[354,2267,2270],{"className":2268,"code":2269,"language":359,"meta":360},[357],"커플 모드\n\n↓\n\n새로고침\n\n↓\n\n최근 모드 복원\n\n↓\n\n커플 모드 유지\n",[362,2271,2269],{"__ignoreMap":360},[2243,2273,2275],{"id":2274},"파트너가-없는-사용자","파트너가 없는 사용자",[354,2277,2280],{"className":2278,"code":2279,"language":359,"meta":360},[357],"개인 사용자\n\n↓\n\n개인 모드 사용\n\n↓\n\n커플 모드 접근\n\n↓\n\n파트너 연결 여부 확인\n\n↓\n\n필요한 경우 연결 안내\n",[362,2281,2279],{"__ignoreMap":360},[282,2283,2284],{},"이런 흐름까지 확인해야 실제 서비스에서 모드가 섞이는 문제를 줄일 수 있다.",[372,2286],{},[277,2288,2290],{"id":2289},"_24-앞으로-데이터-모델도-모드를-고려해야-한다","🧩 24. 앞으로 데이터 모델도 모드를 고려해야 한다",[282,2292,2293],{},"이번 작업은 프론트엔드 UI에서 시작했지만 실제로는 백엔드 데이터 모델에도 영향을 준다.",[282,2295,2296],{},"앞으로 각 데이터가 어떤 주체에 속해 있는지를 명확하게 해야 한다.",[282,2298,2299],{},"예:",[354,2301,2304],{"className":2302,"code":2303,"language":359,"meta":360},[357],"Expense\n\n→ 개인 사용자 데이터인가?\n→ 커플 공간 데이터인가?\n\n\nSavingsGoal\n\n→ 개인 목표인가?\n→ 커플 공동 목표인가?\n\n\nBudget\n\n→ 개인 예산인가?\n→ 커플 예산인가?\n",[362,2305,2303],{"__ignoreMap":360},[282,2307,2308],{},"이런 기준이 명확해야 한다.",[282,2310,2311],{},"결국 서비스 전체에서 다음과 같은 규칙을 가져갈 수 있다.",[354,2313,2316],{"className":2314,"code":2315,"language":359,"meta":360},[357],"현재 mode\n\n+\n\n사용자 \u002F 커플 권한\n\n↓\n\n데이터 접근 범위 결정\n",[362,2317,2315],{"__ignoreMap":360},[282,2319,2320],{},"이 규칙을 기준으로 API와 DB 구조를 확장할 예정이다.",[372,2322],{},[277,2324,2326],{"id":2325},"_25-다음-단계","🚀 25. 다음 단계",[282,2328,2329,2330,2332],{},"이번 작업을 통해 BroBean은 단순히 ",[286,2331,495],{},"에 머무르지 않고 개인 사용자까지 사용할 수 있는 구조로 한 단계 확장됐다.",[282,2334,2335],{},"현재는 먼저 사용자 인터페이스와 상태 관리 측면에서 개인\u002F커플 모드를 분리하고 있다.",[282,2337,2338],{},"앞으로는 이 모드 정보를 실제 데이터 구조와 연결해야 한다.",[282,2340,2341],{},"특히:",[867,2343,2344,2347,2350,2353,2356,2359,2362,2365,2371,2374],{},[870,2345,2346],{},"🌱 개인\u002F커플 농장 데이터 분리",[870,2348,2349],{},"💰 개인\u002F커플 가계부 데이터 분리",[870,2351,2352],{},"🌳 개인\u002F커플 저축 목표 분리",[870,2354,2355],{},"🏆 개인\u002F커플 챌린지 분리",[870,2357,2358],{},"📊 개인\u002F커플 예산 분리",[870,2360,2361],{},"🔁 개인\u002F커플 고정지출 분리",[870,2363,2364],{},"🔐 커플 데이터 접근 권한 검증",[870,2366,2367,2368,2370],{},"💾 ",[362,2369,1708],{}," 기반 최근 모드 저장",[870,2372,2373],{},"🔄 로그인 및 재접속 시 마지막 모드 복원",[870,2375,2376],{},"📱 모바일 모드 전환 UX 개선",[282,2378,2379],{},"등을 이어서 작업할 예정이다.",[372,2381],{},[277,2383,2385],{"id":2384},"마무리","🎯 마무리",[282,2387,2388,2389,558],{},"이번 기능을 구현하면서 가장 크게 느낀 것은 ",[286,2390,2391],{},"서비스에서 하나의 '모드'를 추가하는 일이 생각보다 큰 작업이라는 것",[282,2393,2394],{},"처음에는 단순히:",[354,2396,2399],{"className":2397,"code":2398,"language":359,"meta":360},[357],"[ 개인 ] [ 커플 ]\n",[362,2400,2398],{"__ignoreMap":360},[282,2402,2403],{},"버튼 하나를 추가하면 된다고 생각할 수 있다.",[282,2405,2406],{},"하지만 실제로는:",[354,2408,2410],{"className":2409,"code":546,"language":359,"meta":360},[357],[362,2411,546],{"__ignoreMap":360},[282,2413,2414],{},"전체 데이터의 기준이 바뀐다.",[282,2416,2417,2418,2421],{},"그래서 이번에는 모드 상태를 각각의 컴포넌트에서 따로 관리하지 않고 ",[286,2419,2420],{},"Pinia Store를 통해 전역 상태로 관리","하는 방향을 선택했다.",[282,2423,2424],{},"그리고 장기적으로는:",[354,2426,2429],{"className":2427,"code":2428,"language":359,"meta":360},[357],"userSetting\n      ↓\n최근 사용 모드 저장\n      ↓\n로그인 \u002F 재접속\n      ↓\nPinia 초기화\n      ↓\n마지막 모드 복원\n",[362,2430,2428],{"__ignoreMap":360},[282,2432,2433],{},"구조를 만들어 사용자가 이전에 사용하던 환경을 그대로 이어갈 수 있도록 할 예정이다.",[282,2435,2436],{},"결국 BroBean의 목표는 단순히 커플끼리 사용하는 가계부를 만드는 것이 아니다.",[282,2438,2439,2442],{},[286,2440,2441],{},"혼자서도 자신의 농장을 가꿀 수 있고, 필요할 때는 파트너와 하나의 농장을 함께 키워갈 수 있는 서비스","를 만드는 것이다.",[282,2444,2445],{},"개인 모드와 커플 모드의 분리는 그 방향으로 가기 위한 중요한 구조적 기반이 되었다.",[282,2447,2448,2449,2452],{},"앞으로는 이 모드 정보를 실제 DB와 API에 연결하면서, ",[286,2450,2451],{},"개인 데이터와 커플 데이터가 서로 섞이지 않고 각각의 컨텍스트에서 자연스럽게 동작하는 구조","를 만들어갈 예정이다.",[2454,2455,2456],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":360,"searchDepth":980,"depth":980,"links":2458},[2459,2460,2461,2462],{"id":2245,"depth":987,"text":2246},{"id":2255,"depth":987,"text":2256},{"id":2265,"depth":987,"text":2265},{"id":2274,"depth":987,"text":2275},"2026-08-11","BroBean을 커플 전용 서비스에서 개인 사용자까지 사용할 수 있도록 확장하면서 개인 모드와 커플 모드를 분리하고, 헤더와 사이드 메뉴의 모드 전환 UI 및 Pinia 기반 상태 관리 구조를 구현한 과정","md",null,{"tags":2468,"image":2475},[2469,2470,495,492,2471,2472,2473,2474],"BroBean","가계부 앱","Nuxt 4","Pinia","상태 관리","모드 전환","\u002Fimages\u002Fbrobean\u002Fog-image.png",{"icon":20},{"title":2478,"description":2479,"ogImage":2475},"BroBean 개인\u002F커플 모드 분리 구현 - Pinia 상태 관리와 사용자 경험 개선","BroBean에서 개인 가계부와 커플 가계부를 함께 지원하기 위해 농장, 가계부, 저축, 챌린지, 예산, 고정지출 데이터를 개인\u002F커플 모드로 분리하고 Pinia Store를 활용해 모드 상태를 관리하는 과정을 정리합니다.","kcYNmn17VHy9lEv3nW6CjMCMjJpsiapHwx9zqwRX_Nk",[2482,2484],{"title":230,"path":231,"stem":232,"description":2483,"icon":20,"children":-1},"BroBean의 로그인, 회원가입, 비밀번호 찾기 및 파트너 초대 팝업을 구현하며 계정 인증과 커플 연결 과정을 구성한 과정",{"title":238,"path":239,"stem":240,"description":2485,"icon":20,"children":-1},"회원가입과 로그인 백엔드 연동 과정에서 발생한 인증 상태 초기화 문제를 해결하고, Nuxt Plugin을 활용한 사용자 정보 복구와 플러그인 실행 순서 제어까지 구현한 과정",1789947623410]