[{"data":1,"prerenderedAt":1507},["ShallowReactive",2],{"navigation_docs_ko":3,"-ko-project-brobean-brobean-dev-log-14":267,"-ko-project-brobean-brobean-dev-log-14-surround":1502},[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":246,"body":269,"date":1484,"description":1485,"extension":1486,"links":1487,"meta":1488,"navigation":1497,"path":247,"seo":1498,"stem":248,"__hash__":1501},"docs_ko\u002Fko\u002F2.project\u002F2.brobean\u002F914.brobean-dev-log-14.md",{"type":270,"value":271,"toc":1482},"minimark",[272,1478],[273,274,276,281,285,288,296,302,305,315,318,326,329,333,336,417,420,472,475,481,487,490,493,499,502,504,508,511,514,558,567,573,611,614,620,623,626,628,632,639,642,660,663,666,693,699,735,738,741,868,871,873,877,884,887,903,911,914,936,942,947,950,956,959,965,968,971,973,977,983,999,1002,1008,1011,1014,1042,1045,1065,1068,1071,1083,1086,1088,1092,1095,1101,1110,1116,1132,1135,1138,1140,1144,1147,1150,1164,1167,1170,1173,1179,1182,1185,1191,1194,1203,1205,1209,1212,1215,1221,1224,1227,1233,1236,1239,1248,1270,1273,1301,1304,1306,1310,1315,1321,1324,1330,1333,1336,1342,1345,1355,1357,1361,1367,1370,1386,1389,1392,1397,1400,1403,1425,1428,1456,1459,1462,1468,1471],"u-page-card",{":spotlight":275},"true",[277,278,280],"h1",{"id":279},"커플-기능을-구현하다-만난-순환-참조와-objectprovider","📖 커플 기능을 구현하다 만난 순환 참조와 ObjectProvider",[282,283,284],"p",{},"BroBean의 커플 기능을 구현하면서 기존에 만들어둔 인증 로직과 새롭게 추가한 커플 로직이 서로 연결되는 상황이 생겼다.",[282,286,287],{},"처음에는 자연스러운 구조라고 생각했다.",[282,289,290,291,295],{},"인증된 사용자의 정보를 확인하기 위해 ",[292,293,294],"code",{},"AuthService","에서 커플 정보를 확인해야 했고,",[282,297,298,299,301],{},"반대로 커플 관련 기능에서는 현재 로그인한 사용자의 정보를 확인하기 위해 ",[292,300,294],{},"를 다시 사용해야 했다.",[282,303,304],{},"문제는 서비스 간 의존성이 다음과 같은 구조가 되면서 발생했다.",[306,307,313],"pre",{"className":308,"code":310,"language":311,"meta":312},[309],"language-text","AuthService\n    ↓\nCoupleService\n    ↓\nAuthService\n","text","",[292,314,310],{"__ignoreMap":312},[282,316,317],{},"Spring 입장에서는 두 Bean을 생성하기 위해 서로가 서로를 필요로 하는 상황이다.",[282,319,320,321,325],{},"결국 애플리케이션을 실행하면서 ",[322,323,324],"strong",{},"순환 참조(Circular Reference)"," 오류가 발생했다.",[327,328],"hr",{},[277,330,332],{"id":331},"문제가-발생한-구조","🔄 문제가 발생한 구조",[282,334,335],{},"당시 구조를 단순화하면 다음과 비슷했다.",[306,337,341],{"className":338,"code":339,"language":340,"meta":312,"style":312},"language-java shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@Service\n@RequiredArgsConstructor\npublic class AuthService {\n\n    private final CoupleService coupleService;\n\n}\n","java",[292,342,343,356,364,380,387,406,411],{"__ignoreMap":312},[344,345,348,352],"span",{"class":346,"line":347},"line",1,[344,349,351],{"class":350},"sMK4o","@",[344,353,355],{"class":354},"spNyl","Service\n",[344,357,359,361],{"class":346,"line":358},2,[344,360,351],{"class":350},[344,362,363],{"class":354},"RequiredArgsConstructor\n",[344,365,367,370,373,377],{"class":346,"line":366},3,[344,368,369],{"class":354},"public",[344,371,372],{"class":354}," class",[344,374,376],{"class":375},"sBMFI"," AuthService",[344,378,379],{"class":350}," {\n",[344,381,383],{"class":346,"line":382},4,[344,384,386],{"emptyLinePlaceholder":385},true,"\n",[344,388,390,393,396,399,403],{"class":346,"line":389},5,[344,391,392],{"class":354},"    private",[344,394,395],{"class":354}," final",[344,397,398],{"class":354}," CoupleService",[344,400,402],{"class":401},"sTEyZ"," coupleService",[344,404,405],{"class":350},";\n",[344,407,409],{"class":346,"line":408},6,[344,410,386],{"emptyLinePlaceholder":385},[344,412,414],{"class":346,"line":413},7,[344,415,416],{"class":350},"}\n",[282,418,419],{},"그리고:",[306,421,423],{"className":338,"code":422,"language":340,"meta":312,"style":312},"@Service\n@RequiredArgsConstructor\npublic class CoupleService {\n\n    private final AuthService authService;\n\n}\n",[292,424,425,431,437,447,451,464,468],{"__ignoreMap":312},[344,426,427,429],{"class":346,"line":347},[344,428,351],{"class":350},[344,430,355],{"class":354},[344,432,433,435],{"class":346,"line":358},[344,434,351],{"class":350},[344,436,363],{"class":354},[344,438,439,441,443,445],{"class":346,"line":366},[344,440,369],{"class":354},[344,442,372],{"class":354},[344,444,398],{"class":375},[344,446,379],{"class":350},[344,448,449],{"class":346,"line":382},[344,450,386],{"emptyLinePlaceholder":385},[344,452,453,455,457,459,462],{"class":346,"line":389},[344,454,392],{"class":354},[344,456,395],{"class":354},[344,458,376],{"class":354},[344,460,461],{"class":401}," authService",[344,463,405],{"class":350},[344,465,466],{"class":346,"line":408},[344,467,386],{"emptyLinePlaceholder":385},[344,469,470],{"class":346,"line":413},[344,471,416],{"class":350},[282,473,474],{},"각각 서로를 생성자 주입받고 있다.",[282,476,477,478,480],{},"Spring이 ",[292,479,294],{},"를 생성하려고 하면:",[306,482,485],{"className":483,"code":484,"language":311,"meta":312},[309],"AuthService 생성\n    ↓\nCoupleService 필요\n    ↓\nCoupleService 생성\n    ↓\nAuthService 필요\n    ↓\nAuthService 아직 생성되지 않음\n",[292,486,484],{"__ignoreMap":312},[282,488,489],{},"이라는 구조가 만들어진다.",[282,491,492],{},"결국 어느 쪽도 먼저 완전히 생성될 수 없다.",[306,494,497],{"className":495,"code":496,"language":311,"meta":312},[309],"AuthService → CoupleService → AuthService → ...\n",[292,498,496],{"__ignoreMap":312},[282,500,501],{},"이렇게 의존성이 계속 이어지기 때문이다.",[327,503],{},[277,505,507],{"id":506},"️-순환-참조는-왜-문제가-될까","⚠️ 순환 참조는 왜 문제가 될까?",[282,509,510],{},"Spring의 생성자 주입은 Bean을 생성할 때 필요한 의존성을 생성자에서 전달받는다.",[282,512,513],{},"예를 들어:",[306,515,517],{"className":338,"code":516,"language":340,"meta":312,"style":312},"public AuthService(CoupleService coupleService) {\n    this.coupleService = coupleService;\n}\n",[292,518,519,539,554],{"__ignoreMap":312},[344,520,521,523,526,529,532,534,537],{"class":346,"line":347},[344,522,369],{"class":354},[344,524,376],{"class":525},"s2Zo4",[344,527,528],{"class":350},"(",[344,530,531],{"class":354},"CoupleService",[344,533,402],{"class":401},[344,535,536],{"class":350},")",[344,538,379],{"class":350},[344,540,541,544,547,550,552],{"class":346,"line":358},[344,542,543],{"class":350},"    this.",[344,545,546],{"class":401},"coupleService ",[344,548,549],{"class":350},"=",[344,551,402],{"class":401},[344,553,405],{"class":350},[344,555,556],{"class":346,"line":366},[344,557,416],{"class":350},[282,559,560,561,563,564,566],{},"라면 ",[292,562,294],{},"를 만들기 전에 ",[292,565,531],{},"가 준비되어 있어야 한다.",[282,568,569,570,572],{},"그런데 ",[292,571,531],{}," 역시:",[306,574,576],{"className":338,"code":575,"language":340,"meta":312,"style":312},"public CoupleService(AuthService authService) {\n    this.authService = authService;\n}\n",[292,577,578,594,607],{"__ignoreMap":312},[344,579,580,582,584,586,588,590,592],{"class":346,"line":347},[344,581,369],{"class":354},[344,583,398],{"class":525},[344,585,528],{"class":350},[344,587,294],{"class":354},[344,589,461],{"class":401},[344,591,536],{"class":350},[344,593,379],{"class":350},[344,595,596,598,601,603,605],{"class":346,"line":358},[344,597,543],{"class":350},[344,599,600],{"class":401},"authService ",[344,602,549],{"class":350},[344,604,461],{"class":401},[344,606,405],{"class":350},[344,608,609],{"class":346,"line":366},[344,610,416],{"class":350},[282,612,613],{},"를 필요로 한다면 문제가 발생한다.",[306,615,618],{"className":616,"code":617,"language":311,"meta":312},[309],"AuthService를 만들려면\nCoupleService가 필요하다.\n\nCoupleService를 만들려면\nAuthService가 필요하다.\n",[292,619,617],{"__ignoreMap":312},[282,621,622],{},"서로 먼저 생성될 수 없는 구조다.",[282,624,625],{},"Spring에서는 이런 순환 참조를 발견하면 Bean 생성 과정에서 오류가 발생한다.",[327,627],{},[277,629,631],{"id":630},"️-objectprovider를-사용해보기","🛠️ ObjectProvider를 사용해보기",[282,633,634,635,638],{},"이 문제를 해결하면서 사용하게 된 것이 ",[292,636,637],{},"ObjectProvider","다.",[282,640,641],{},"기존에는:",[306,643,645],{"className":338,"code":644,"language":340,"meta":312,"style":312},"private final AuthService authService;\n",[292,646,647],{"__ignoreMap":312},[344,648,649,652,654,656,658],{"class":346,"line":347},[344,650,651],{"class":354},"private",[344,653,395],{"class":354},[344,655,376],{"class":354},[344,657,461],{"class":401},[344,659,405],{"class":350},[282,661,662],{},"형태로 직접 주입받고 있었다면,",[282,664,665],{},"다음과 같이 변경할 수 있다.",[306,667,669],{"className":338,"code":668,"language":340,"meta":312,"style":312},"private final ObjectProvider\u003CAuthService> authServiceProvider;\n",[292,670,671],{"__ignoreMap":312},[344,672,673,675,677,680,683,685,688,691],{"class":346,"line":347},[344,674,651],{"class":354},[344,676,395],{"class":354},[344,678,679],{"class":354}," ObjectProvider",[344,681,682],{"class":350},"\u003C",[344,684,294],{"class":354},[344,686,687],{"class":350},">",[344,689,690],{"class":401}," authServiceProvider",[344,692,405],{"class":350},[282,694,695,696,698],{},"그리고 실제로 ",[292,697,294],{},"가 필요한 시점에:",[306,700,702],{"className":338,"code":701,"language":340,"meta":312,"style":312},"authServiceProvider\n    .getObject()\n    .getLoginUserInfo(user);\n",[292,703,704,709,720],{"__ignoreMap":312},[344,705,706],{"class":346,"line":347},[344,707,708],{"class":401},"authServiceProvider\n",[344,710,711,714,717],{"class":346,"line":358},[344,712,713],{"class":350},"    .",[344,715,716],{"class":525},"getObject",[344,718,719],{"class":350},"()\n",[344,721,722,724,727,729,732],{"class":346,"line":366},[344,723,713],{"class":350},[344,725,726],{"class":525},"getLoginUserInfo",[344,728,528],{"class":350},[344,730,731],{"class":401},"user",[344,733,734],{"class":350},");\n",[282,736,737],{},"처럼 가져온다.",[282,739,740],{},"전체적으로 보면:",[306,742,744],{"className":338,"code":743,"language":340,"meta":312,"style":312},"@Service\n@RequiredArgsConstructor\npublic class CoupleService {\n\n    private final ObjectProvider\u003CAuthService> authServiceProvider;\n\n    public void someMethod(User user) {\n\n        authServiceProvider\n            .getObject()\n            .getLoginUserInfo(user);\n\n    }\n}\n",[292,745,746,752,758,768,772,790,794,818,823,829,839,852,857,863],{"__ignoreMap":312},[344,747,748,750],{"class":346,"line":347},[344,749,351],{"class":350},[344,751,355],{"class":354},[344,753,754,756],{"class":346,"line":358},[344,755,351],{"class":350},[344,757,363],{"class":354},[344,759,760,762,764,766],{"class":346,"line":366},[344,761,369],{"class":354},[344,763,372],{"class":354},[344,765,398],{"class":375},[344,767,379],{"class":350},[344,769,770],{"class":346,"line":382},[344,771,386],{"emptyLinePlaceholder":385},[344,773,774,776,778,780,782,784,786,788],{"class":346,"line":389},[344,775,392],{"class":354},[344,777,395],{"class":354},[344,779,679],{"class":354},[344,781,682],{"class":350},[344,783,294],{"class":354},[344,785,687],{"class":350},[344,787,690],{"class":401},[344,789,405],{"class":350},[344,791,792],{"class":346,"line":408},[344,793,386],{"emptyLinePlaceholder":385},[344,795,796,799,802,805,807,810,814,816],{"class":346,"line":413},[344,797,798],{"class":354},"    public",[344,800,801],{"class":354}," void",[344,803,804],{"class":525}," someMethod",[344,806,528],{"class":350},[344,808,809],{"class":354},"User",[344,811,813],{"class":812},"sHdIc"," user",[344,815,536],{"class":350},[344,817,379],{"class":350},[344,819,821],{"class":346,"line":820},8,[344,822,386],{"emptyLinePlaceholder":385},[344,824,826],{"class":346,"line":825},9,[344,827,828],{"class":401},"        authServiceProvider\n",[344,830,832,835,837],{"class":346,"line":831},10,[344,833,834],{"class":350},"            .",[344,836,716],{"class":525},[344,838,719],{"class":350},[344,840,842,844,846,848,850],{"class":346,"line":841},11,[344,843,834],{"class":350},[344,845,726],{"class":525},[344,847,528],{"class":350},[344,849,731],{"class":401},[344,851,734],{"class":350},[344,853,855],{"class":346,"line":854},12,[344,856,386],{"emptyLinePlaceholder":385},[344,858,860],{"class":346,"line":859},13,[344,861,862],{"class":350},"    }\n",[344,864,866],{"class":346,"line":865},14,[344,867,416],{"class":350},[282,869,870],{},"와 같은 형태다.",[327,872],{},[277,874,876],{"id":875},"왜-objectprovider를-사용하면-동작할까","🧠 왜 ObjectProvider를 사용하면 동작할까?",[282,878,879,880,883],{},"핵심은 ",[322,881,882],{},"의존성을 가져오는 시점을 늦출 수 있다는 것","이다.",[282,885,886],{},"기존 방식은:",[306,888,889],{"className":338,"code":644,"language":340,"meta":312,"style":312},[292,890,891],{"__ignoreMap":312},[344,892,893,895,897,899,901],{"class":346,"line":347},[344,894,651],{"class":354},[344,896,395],{"class":354},[344,898,376],{"class":354},[344,900,461],{"class":401},[344,902,405],{"class":350},[282,904,477,905,907,908,910],{},[292,906,531],{},"를 생성하는 시점부터 ",[292,909,294],{},"를 필요로 한다.",[282,912,913],{},"반면:",[306,915,916],{"className":338,"code":668,"language":340,"meta":312,"style":312},[292,917,918],{"__ignoreMap":312},[344,919,920,922,924,926,928,930,932,934],{"class":346,"line":347},[344,921,651],{"class":354},[344,923,395],{"class":354},[344,925,679],{"class":354},[344,927,682],{"class":350},[344,929,294],{"class":354},[344,931,687],{"class":350},[344,933,690],{"class":401},[344,935,405],{"class":350},[282,937,938,939,941],{},"에서는 ",[292,940,294],{}," 자체를 바로 주입받는 것이 아니다.",[282,943,944,946],{},[292,945,294],{},"를 필요할 때 가져올 수 있는 Provider를 주입받는다.",[282,948,949],{},"즉:",[306,951,954],{"className":952,"code":953,"language":311,"meta":312},[309],"기존\n\nCoupleService 생성\n    ↓\nAuthService 필요\n    ↓\nAuthService 생성\n",[292,955,953],{"__ignoreMap":312},[282,957,958],{},"이 아니라:",[306,960,963],{"className":961,"code":962,"language":311,"meta":312},[309],"CoupleService 생성\n    ↓\nObjectProvider 주입\n    ↓\nBean 생성 완료\n    ↓\n실제 메서드 호출\n    ↓\ngetObject()\n    ↓\nAuthService 획득\n",[292,964,962],{"__ignoreMap":312},[282,966,967],{},"처럼 필요한 시점까지 실제 객체 획득을 지연시킬 수 있다.",[282,969,970],{},"이런 방식을 **지연 조회(Lazy Lookup)**라고 볼 수 있다.",[327,972],{},[277,974,976],{"id":975},"objectprovider의-역할","🔎 ObjectProvider의 역할",[282,978,979,982],{},[292,980,981],{},"ObjectProvider\u003CT>","는 Spring이 제공하는 객체 조회 방식 중 하나다.",[306,984,986],{"className":338,"code":985,"language":340,"meta":312,"style":312},"ObjectProvider\u003CAuthService>\n",[292,987,988],{"__ignoreMap":312},[344,989,990,992,994,996],{"class":346,"line":347},[344,991,637],{"class":354},[344,993,682],{"class":350},[344,995,294],{"class":401},[344,997,998],{"class":350},">\n",[282,1000,1001],{},"라면:",[306,1003,1006],{"className":1004,"code":1005,"language":311,"meta":312},[309],"AuthService를 필요할 때 조회할 수 있는 Provider\n",[292,1007,1005],{"__ignoreMap":312},[282,1009,1010],{},"라고 이해하면 된다.",[282,1012,1013],{},"가장 단순하게 사용하면:",[306,1015,1017],{"className":338,"code":1016,"language":340,"meta":312,"style":312},"AuthService authService =\n    authServiceProvider.getObject();\n",[292,1018,1019,1029],{"__ignoreMap":312},[344,1020,1021,1023,1026],{"class":346,"line":347},[344,1022,294],{"class":354},[344,1024,1025],{"class":401}," authService ",[344,1027,1028],{"class":350},"=\n",[344,1030,1031,1034,1037,1039],{"class":346,"line":358},[344,1032,1033],{"class":401},"    authServiceProvider",[344,1035,1036],{"class":350},".",[344,1038,716],{"class":525},[344,1040,1041],{"class":350},"();\n",[282,1043,1044],{},"이후:",[306,1046,1048],{"className":338,"code":1047,"language":340,"meta":312,"style":312},"authService.getLoginUserInfo(user);\n",[292,1049,1050],{"__ignoreMap":312},[344,1051,1052,1055,1057,1059,1061,1063],{"class":346,"line":347},[344,1053,1054],{"class":401},"authService",[344,1056,1036],{"class":350},[344,1058,726],{"class":525},[344,1060,528],{"class":350},[344,1062,731],{"class":401},[344,1064,734],{"class":350},[282,1066,1067],{},"처럼 사용할 수 있다.",[282,1069,1070],{},"또한 상황에 따라:",[306,1072,1074],{"className":338,"code":1073,"language":340,"meta":312,"style":312},"getIfAvailable()\n",[292,1075,1076],{"__ignoreMap":312},[344,1077,1078,1081],{"class":346,"line":347},[344,1079,1080],{"class":525},"getIfAvailable",[344,1082,719],{"class":350},[282,1084,1085],{},"같은 메서드를 활용해 Bean이 존재하지 않을 때의 처리도 할 수 있다.",[327,1087],{},[277,1089,1091],{"id":1090},"이번-문제에서의-구조","🔄 이번 문제에서의 구조",[282,1093,1094],{},"ObjectProvider를 적용하고 나서는 의존성이 다음과 같이 바뀐다.",[306,1096,1099],{"className":1097,"code":1098,"language":311,"meta":312},[309],"AuthService\n    ↓\nCoupleService\n    ↓\nObjectProvider\u003CAuthService>\n",[292,1100,1098],{"__ignoreMap":312},[282,1102,1103,1104,1106,1107,1109],{},"중요한 차이는 ",[292,1105,531],{},"가 생성되는 시점에 실제 ",[292,1108,294],{}," 객체를 직접 요구하지 않는다는 것이다.",[282,1111,1112,1113,1115],{},"실제 ",[292,1114,294],{},"가 필요한 순간:",[306,1117,1119],{"className":338,"code":1118,"language":340,"meta":312,"style":312},"authServiceProvider.getObject()\n",[292,1120,1121],{"__ignoreMap":312},[344,1122,1123,1126,1128,1130],{"class":346,"line":347},[344,1124,1125],{"class":401},"authServiceProvider",[344,1127,1036],{"class":350},[344,1129,716],{"class":525},[344,1131,719],{"class":350},[282,1133,1134],{},"를 호출해서 가져온다.",[282,1136,1137],{},"따라서 Bean 생성 시점의 순환 구조를 피할 수 있었다.",[327,1139],{},[277,1141,1143],{"id":1142},"그렇다면-순환-참조가-생기면-무조건-objectprovider를-사용하면-될까","🤔 그렇다면 순환 참조가 생기면 무조건 ObjectProvider를 사용하면 될까?",[282,1145,1146],{},"개인적으로 이번 문제를 해결하면서 가장 생각해볼 부분이었다.",[282,1148,1149],{},"기술적으로는:",[306,1151,1152],{"className":338,"code":985,"language":340,"meta":312,"style":312},[292,1153,1154],{"__ignoreMap":312},[344,1155,1156,1158,1160,1162],{"class":346,"line":347},[344,1157,637],{"class":354},[344,1159,682],{"class":350},[344,1161,294],{"class":401},[344,1163,998],{"class":350},[282,1165,1166],{},"를 사용해서 순환 참조를 우회할 수 있다.",[282,1168,1169],{},"하지만 이것이 항상 좋은 설계라는 의미는 아니다.",[282,1171,1172],{},"오히려 서비스 간 의존성이:",[306,1174,1177],{"className":1175,"code":1176,"language":311,"meta":312},[309],"AuthService\n    ↕\nCoupleService\n",[292,1178,1176],{"__ignoreMap":312},[282,1180,1181],{},"처럼 서로 강하게 연결되어 있다는 것 자체가 설계상 신호일 수 있다.",[282,1183,1184],{},"특히 두 Service가 서로의 많은 기능을 사용하기 시작하면 시간이 지나면서:",[306,1186,1189],{"className":1187,"code":1188,"language":311,"meta":312},[309],"AuthService\n    ↓\nCoupleService\n    ↓\nFarmService\n    ↓\nAuthService\n",[292,1190,1188],{"__ignoreMap":312},[282,1192,1193],{},"처럼 의존성이 복잡해질 가능성이 있다.",[282,1195,1196,1197,1199,1200],{},"따라서 단순히 ",[292,1198,637],{},"를 추가해서 오류를 없애는 것보다 ",[322,1201,1202],{},"왜 서로의 Service가 필요해졌는지 먼저 확인하는 것이 중요하다.",[327,1204],{},[277,1206,1208],{"id":1207},"이번에는-왜-objectprovider를-선택했을까","🧩 이번에는 왜 ObjectProvider를 선택했을까?",[282,1210,1211],{},"BroBean에서는 기존 인증 로직을 크게 변경하지 않고 커플 기능을 확장하는 과정에서 일시적으로 두 Service 사이의 의존 관계가 만들어졌다.",[282,1213,1214],{},"기존:",[306,1216,1219],{"className":1217,"code":1218,"language":311,"meta":312},[309],"AuthService\n",[292,1220,1218],{"__ignoreMap":312},[282,1222,1223],{},"가 담당하던 사용자 인증 정보 조회를 커플 기능에서도 사용해야 했고,",[282,1225,1226],{},"커플 연결 여부나 커플 관련 데이터를 처리하는 과정에서는 다시:",[306,1228,1231],{"className":1229,"code":1230,"language":311,"meta":312},[309],"CoupleService\n",[292,1232,1230],{"__ignoreMap":312},[282,1234,1235],{},"가 필요해졌다.",[282,1237,1238],{},"당장 전체 Service 구조를 다시 분리하기에는 변경 범위가 커질 수 있었기 때문에,",[282,1240,1241,1242,1244,1245],{},"이번 상황에서는 ",[292,1243,637],{},"를 이용해 ",[322,1246,1247],{},"실제 객체 조회 시점을 늦추는 방식으로 문제를 해결했다.",[306,1249,1250],{"className":338,"code":668,"language":340,"meta":312,"style":312},[292,1251,1252],{"__ignoreMap":312},[344,1253,1254,1256,1258,1260,1262,1264,1266,1268],{"class":346,"line":347},[344,1255,651],{"class":354},[344,1257,395],{"class":354},[344,1259,679],{"class":354},[344,1261,682],{"class":350},[344,1263,294],{"class":354},[344,1265,687],{"class":350},[344,1267,690],{"class":401},[344,1269,405],{"class":350},[282,1271,1272],{},"그리고 필요한 곳에서:",[306,1274,1275],{"className":338,"code":701,"language":340,"meta":312,"style":312},[292,1276,1277,1281,1289],{"__ignoreMap":312},[344,1278,1279],{"class":346,"line":347},[344,1280,708],{"class":401},[344,1282,1283,1285,1287],{"class":346,"line":358},[344,1284,713],{"class":350},[344,1286,716],{"class":525},[344,1288,719],{"class":350},[344,1290,1291,1293,1295,1297,1299],{"class":346,"line":366},[344,1292,713],{"class":350},[344,1294,726],{"class":525},[344,1296,528],{"class":350},[344,1298,731],{"class":401},[344,1300,734],{"class":350},[282,1302,1303],{},"형태로 사용했다.",[327,1305],{},[277,1307,1309],{"id":1308},"️-그래도-기억해둘-것","⚠️ 그래도 기억해둘 것",[282,1311,1312,1314],{},[292,1313,637],{},"는 순환 참조 문제를 해결할 수 있는 도구 중 하나지만,",[306,1316,1319],{"className":1317,"code":1318,"language":311,"meta":312},[309],"순환 참조 발생\n        ↓\nObjectProvider 사용\n        ↓\n오류 해결\n",[292,1320,1318],{"__ignoreMap":312},[282,1322,1323],{},"에서 끝내기보다는,",[306,1325,1328],{"className":1326,"code":1327,"language":311,"meta":312},[309],"왜 Service가 서로를 필요로 하는가?\n",[292,1329,1327],{"__ignoreMap":312},[282,1331,1332],{},"를 먼저 확인하는 것이 중요하다.",[282,1334,1335],{},"경우에 따라서는:",[306,1337,1340],{"className":1338,"code":1339,"language":311,"meta":312},[309],"공통 로직 분리\n\n↓\n\n별도의 Service 생성\n\n↓\n\nRepository 직접 조회 구조 변경\n\n↓\n\nFacade 또는 별도 조회 객체 사용\n",[292,1341,1339],{"__ignoreMap":312},[282,1343,1344],{},"등으로 의존성 자체를 제거하는 것이 더 좋은 해결책일 수 있다.",[282,1346,1347,1348,1350,1351,1354],{},"즉 ",[292,1349,637],{},"는 ",[322,1352,1353],{},"순환 참조를 없애는 설계 도구라기보다는 의존성의 실제 조회 시점을 지연시키는 Spring 기능","에 가깝다.",[327,1356],{},[277,1358,1360],{"id":1359},"마무리","🎯 마무리",[282,1362,1363,1364,1366],{},"이번 문제를 통해 ",[292,1365,637],{},"라는 기능을 알게 된 것도 있지만, 개인적으로는 Service 간 의존 관계를 다시 생각해보는 계기가 됐다.",[282,1368,1369],{},"처음에는:",[306,1371,1372],{"className":338,"code":644,"language":340,"meta":312,"style":312},[292,1373,1374],{"__ignoreMap":312},[344,1375,1376,1378,1380,1382,1384],{"class":346,"line":347},[344,1377,651],{"class":354},[344,1379,395],{"class":354},[344,1381,376],{"class":354},[344,1383,461],{"class":401},[344,1385,405],{"class":350},[282,1387,1388],{},"처럼 필요한 Service를 바로 주입받는 것이 당연하다고 생각했다.",[282,1390,1391],{},"하지만 Service 간 책임이 확장되면서:",[306,1393,1395],{"className":1394,"code":310,"language":311,"meta":312},[309],[292,1396,310],{"__ignoreMap":312},[282,1398,1399],{},"라는 순환 구조가 만들어질 수 있었다.",[282,1401,1402],{},"이때:",[306,1404,1405],{"className":338,"code":668,"language":340,"meta":312,"style":312},[292,1406,1407],{"__ignoreMap":312},[344,1408,1409,1411,1413,1415,1417,1419,1421,1423],{"class":346,"line":347},[344,1410,651],{"class":354},[344,1412,395],{"class":354},[344,1414,679],{"class":354},[344,1416,682],{"class":350},[344,1418,294],{"class":354},[344,1420,687],{"class":350},[344,1422,690],{"class":401},[344,1424,405],{"class":350},[282,1426,1427],{},"를 사용하고,",[306,1429,1430],{"className":338,"code":701,"language":340,"meta":312,"style":312},[292,1431,1432,1436,1444],{"__ignoreMap":312},[344,1433,1434],{"class":346,"line":347},[344,1435,708],{"class":401},[344,1437,1438,1440,1442],{"class":346,"line":358},[344,1439,713],{"class":350},[344,1441,716],{"class":525},[344,1443,719],{"class":350},[344,1445,1446,1448,1450,1452,1454],{"class":346,"line":366},[344,1447,713],{"class":350},[344,1449,726],{"class":525},[344,1451,528],{"class":350},[344,1453,731],{"class":401},[344,1455,734],{"class":350},[282,1457,1458],{},"처럼 필요한 시점에 Bean을 조회하도록 변경하면서 문제를 해결할 수 있었다.",[282,1460,1461],{},"결국 이번에 기억해둘 핵심은 두 가지다.",[306,1463,1466],{"className":1464,"code":1465,"language":311,"meta":312},[309],"ObjectProvider\n\n→ 의존 객체의 조회 시점을 늦출 수 있다.\n\n순환 참조\n\n→ 단순히 오류만 해결하기보다\n   Service 간 책임과 의존 관계를 다시 확인해야 한다.\n",[292,1467,1465],{"__ignoreMap":312},[282,1469,1470],{},"BroBean처럼 기능이 계속 확장되는 서비스에서는 처음에는 자연스럽게 보였던 Service 간 의존성이 기능 추가와 함께 복잡해질 수 있다.",[282,1472,1473,1474,1477],{},"이번 문제 역시 단순한 Spring 오류 하나를 해결한 것에서 끝내기보다는, ",[322,1475,1476],{},"서비스가 서로의 역할을 어디까지 알아야 하는지 다시 생각해볼 수 있었던 경험","으로 남겨두려고 한다.",[1479,1480,1481],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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}",{"title":312,"searchDepth":358,"depth":358,"links":1483},[],"2026-09-02","BroBean 커플 기능을 구현하던 중 AuthService와 CoupleService 사이에서 발생한 순환 참조 문제와 ObjectProvider를 활용해 의존성 주입 시점을 지연시키는 방법을 정리합니다.","md",null,{"tags":1489,"image":1496},[1490,1491,11,1492,1493,1494,637,1495],"BroBean","개발일지","Spring Boot","순환참조","Circular Reference","Dependency Injection","\u002Fimages\u002Fbrobean\u002Fog-image.png",{"icon":20},{"title":1499,"description":1500},"Spring 순환 참조 오류 해결 - ObjectProvider를 활용한 의존성 지연 주입","Spring Boot 개발 중 AuthService와 CoupleService 사이에서 발생한 순환 참조 문제를 ObjectProvider로 해결한 과정과 동작 원리를 정리합니다.","uxiXaoOKWsvA1Qhq7MmaFOe9jvQx3JYen4Ae9oxprRA",[1503,1505],{"title":242,"path":243,"stem":244,"description":1504,"icon":20,"children":-1},"BroBean의 개인·커플 모드 전환 기능과 씨앗심기·성장일지 백엔드 및 API를 구현하고, 월별 가계부 조회 쿼리를 작성하는 과정에서 쿼리 성능을 다시 고민해본 경험을 정리합니다.",{"title":250,"path":251,"stem":252,"description":1506,"icon":20,"children":-1},"BroBean 프로젝트에서 페이지네이션을 적용하며 Spring Data JPA의 Page와 Slice 차이를 알아보고, 가계부와 알림 내역에 각각 다른 방식을 적용한 경험을 정리한다.",1789947611408]