[{"data":1,"prerenderedAt":31639},["ShallowReactive",2],{"blog-post-list":3},[4,497,817,1471,3801,4445,5961,7258,8393,8867,10156,10997,13094,14038,15023,18210,18729,19043,20018,20254,21530,22339,23075,23750,24278,25019,25487,26643,27592,28336,29210,29755,30225,31137],{"id":5,"title":6,"body":7,"description":483,"extension":484,"meta":485,"navigation":491,"path":492,"seo":493,"sitemap":494,"stem":495,"__hash__":496},"ko\u002Fko\u002Fnuxt-content-vercel-empty-query-sqlite.md","배포만 하면 블로그 글이 하나도 안 보이던 버그: Nuxt Content와 Vercel의 \u002Ftmp 문제",{"type":8,"value":9,"toc":474},"minimark",[10,14,18,25,28,32,184,205,215,217,221,228,248,255,257,269,291,319,322,324,327,330,367,370,423,425,428,431,460,462,465,470],[11,12,6],"h1",{"id":13},"배포만-하면-블로그-글이-하나도-안-보이던-버그-nuxt-content와-vercel의-tmp-문제",[15,16,17],"p",{},"어느 날 갑자기 배포된 블로그에서 글 목록이 통째로 안 보이게 됐다. 에러도 없고, 빌드도 멀쩡히 끝났는데, 그냥 글이 0개로 뜸. 로컬에서 똑같은 빌드 결과물을 그대로 실행해보면 글이 전부 정상적으로 나왔다.",[15,19,20,24],{},[21,22,23],"strong",{},"똑같은 코드인데 어디서 실행하느냐에 따라 결과가 다르다"," — 이건 로직 버그가 아니라 실행 환경 차이에서 나는 버그라는 신호다. 추적한 과정을 정리한다.",[26,27],"hr",{},[29,30,31],"h2",{"id":31},"증상",[33,34,39],"pre",{"className":35,"code":36,"language":37,"meta":38,"style":38},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup>\nconst { data: posts } = await useAsyncData(\"blog-post-list\", async () => {\n  const result = await queryCollection(\"blog\").all();\n  return result;\n}, { default: () => [] });\n\u003C\u002Fscript>\n","vue","",[40,41,42,62,117,148,157,174],"code",{"__ignoreMap":38},[43,44,47,51,55,59],"span",{"class":45,"line":46},"line",1,[43,48,50],{"class":49},"sVt8B","\u003C",[43,52,54],{"class":53},"s9eBZ","script",[43,56,58],{"class":57},"sScJk"," setup",[43,60,61],{"class":49},">\n",[43,63,65,69,72,76,79,83,86,89,92,95,98,102,105,108,111,114],{"class":45,"line":64},2,[43,66,68],{"class":67},"szBVR","const",[43,70,71],{"class":49}," { ",[43,73,75],{"class":74},"s4XuR","data",[43,77,78],{"class":49},": ",[43,80,82],{"class":81},"sj4cs","posts",[43,84,85],{"class":49}," } ",[43,87,88],{"class":67},"=",[43,90,91],{"class":67}," await",[43,93,94],{"class":57}," useAsyncData",[43,96,97],{"class":49},"(",[43,99,101],{"class":100},"sZZnC","\"blog-post-list\"",[43,103,104],{"class":49},", ",[43,106,107],{"class":67},"async",[43,109,110],{"class":49}," () ",[43,112,113],{"class":67},"=>",[43,115,116],{"class":49}," {\n",[43,118,120,123,126,129,131,134,136,139,142,145],{"class":45,"line":119},3,[43,121,122],{"class":67},"  const",[43,124,125],{"class":81}," result",[43,127,128],{"class":67}," =",[43,130,91],{"class":67},[43,132,133],{"class":57}," queryCollection",[43,135,97],{"class":49},[43,137,138],{"class":100},"\"blog\"",[43,140,141],{"class":49},").",[43,143,144],{"class":57},"all",[43,146,147],{"class":49},"();\n",[43,149,151,154],{"class":45,"line":150},4,[43,152,153],{"class":67},"  return",[43,155,156],{"class":49}," result;\n",[43,158,160,163,166,169,171],{"class":45,"line":159},5,[43,161,162],{"class":49},"}, { ",[43,164,165],{"class":57},"default",[43,167,168],{"class":49},": () ",[43,170,113],{"class":67},[43,172,173],{"class":49}," [] });\n",[43,175,177,180,182],{"class":45,"line":176},6,[43,178,179],{"class":49},"\u003C\u002F",[43,181,54],{"class":53},[43,183,61],{"class":49},[185,186,187,199],"ul",{},[188,189,190,191,194,195,198],"li",{},"로컬에서 ",[40,192,193],{},"npm run build"," 후 ",[40,196,197],{},"node .output\u002Fserver\u002Findex.mjs","로 직접 실행 → 글 전부 정상 표시",[188,200,201,202,204],{},"Vercel에 배포된 실제 URL 접속 → ",[40,203,82],{},"가 항상 빈 배열. 에러도 없고 로그에 경고도 없음",[15,206,207,208,104,211,214],{},"캐시 문제인지부터 확인했다. 응답 헤더에 ",[40,209,210],{},"x-vercel-cache: MISS",[40,212,213],{},"age: 0"," — 캐시된 옛 응답이 아니라 매번 새로 실행되는 서버에서 진짜로 빈 값을 반환하고 있었다.",[26,216],{},[29,218,220],{"id":219},"하나씩-지워가며-확인","하나씩 지워가며 확인",[15,222,223,224,227],{},"빌드 결과물 자체는 완전히 동일한데 환경별로 동작이 다르다는 건, 코드나 설정 문제가 아니라 ",[21,225,226],{},"\"어디서 실행되는가\""," 와 관련된 무언가라는 뜻이다.",[185,229,230,236,239],{},[188,231,232,235],{},[40,233,234],{},"content.config.ts","가 빠진 것도 아니었다 — 정상적으로 설정돼 있었음",[188,237,238],{},"페이지 코드 버그도 아니었다 — 동일한 빌드가 로컬에서는 전체 글을 정상적으로 그려냄",[188,240,241,242,104,245,247],{},"CDN에 캐싱된 옛 응답도 아니었다 — 헤더로 확인(",[40,243,244],{},"MISS",[40,246,213],{},")",[15,249,250,251,254],{},"남는 건 로컬 실행과 Vercel 서버리스 함수 실행 사이에 진짜로 다른 단 하나, ",[21,252,253],{},"파일시스템 접근 권한","이었다.",[26,256],{},[29,258,260,261,264,265,268],{"id":259},"원인-nuxtcontent-v3의-sqlite-db는-vercel에서-tmp가-필요한데-300은-그걸-몰랐다","원인: ",[40,262,263],{},"@nuxt\u002Fcontent"," v3의 SQLite DB는 Vercel에서 ",[40,266,267],{},"\u002Ftmp","가 필요한데, 3.0.0은 그걸 몰랐다",[15,270,271,273,274,277,278,283,284,286,287,290],{},[40,272,263],{}," v3는 빌드 시점에 마크다운을 SQLite 데이터베이스로 색인해두고, 런타임에 ",[40,275,276],{},"better-sqlite3","(네이티브 모듈)로 그걸 읽는다. 그런데 Vercel에 배포된 함수의 파일시스템은 ",[21,279,280,282],{},[40,281,267],{},"를 제외한 모든 경로가 읽기 전용","이다. 콘텐츠 모듈이 이 SQLite 파일을 ",[40,285,267],{}," 바깥에서 읽거나 쓰려고 하면, 에러를 던지지 않고 그냥 조용히 실패한다 — ",[40,288,289],{},"queryCollection()","이 에러 대신 빈 값을 반환하는 이유다.",[15,292,293,294,296,297,302,303,310,311,314,315,318],{},"이 정확한 상황을 고치는 수정(Vercel에서 ",[40,295,267],{},"를 쓰도록 하는 패치)은 ",[21,298,299,301],{},[40,300,263],{}," v3.1.1","에서 들어갔다(",[304,305,309],"a",{"href":306,"rel":307},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fcontent\u002Fissues\u002F3108",[308],"nofollow","nuxt\u002Fcontent#3108","). 이 프로젝트는 ",[40,312,313],{},"@nuxt\u002Fcontent: \"^3.0.0\"","에 고정돼 있었는데, 이건 그 수정이 들어가기 ",[21,316,317],{},"전","에 나온 v3의 첫 번째 릴리스였다.",[15,320,321],{},"버그의 전체 그림이 이거다: 프로젝트 초기에 콘텐츠 모듈을 그 당시 최신인 v3.0.0으로 깔아놓고 그 이후로 한 번도 안 올렸는데, 그 버전엔 Vercel 파일시스템 대응이 아예 없었던 것. 로컬에는 그런 제약이 없으니 깨진 경로로도 아무 문제 없이 동작해서, 배포 환경에서만 터지는 이런 유형의 버그가 오래 숨어있을 수 있었던 것.",[26,323],{},[29,325,326],{"id":326},"해결",[15,328,329],{},"패치된 버전으로 올리고, 그 과정에서 같이 걸리는 것들까지 정리.",[33,331,335],{"className":332,"code":333,"language":334,"meta":38,"style":38},"language-diff shiki shiki-themes github-light github-dark","- \"@nuxt\u002Fcontent\": \"^3.0.0\",\n+ \"@nuxt\u002Fcontent\": \"^3.16.1\",\n- \"nuxt\": \"^3.15.2\",\n+ \"nuxt\": \"^3.21.11\",\n- \"@nuxtjs\u002Fsitemap\": \"^7.2.9\",\n+ \"@nuxtjs\u002Fsitemap\": \"^8.6.1\",\n","diff",[40,336,337,342,347,352,357,362],{"__ignoreMap":38},[43,338,339],{"class":45,"line":46},[43,340,341],{},"- \"@nuxt\u002Fcontent\": \"^3.0.0\",\n",[43,343,344],{"class":45,"line":64},[43,345,346],{},"+ \"@nuxt\u002Fcontent\": \"^3.16.1\",\n",[43,348,349],{"class":45,"line":119},[43,350,351],{},"- \"nuxt\": \"^3.15.2\",\n",[43,353,354],{"class":45,"line":150},[43,355,356],{},"+ \"nuxt\": \"^3.21.11\",\n",[43,358,359],{"class":45,"line":159},[43,360,361],{},"- \"@nuxtjs\u002Fsitemap\": \"^7.2.9\",\n",[43,363,364],{"class":45,"line":176},[43,365,366],{},"+ \"@nuxtjs\u002Fsitemap\": \"^8.6.1\",\n",[15,368,369],{},"이렇게 멀리 올리면서 같이 걸린 것들:",[185,371,372,386,403],{},[188,373,374,377,378,381,382,385],{},[40,375,376],{},"@nuxt\u002Fcontent@3.16.1","은 ",[40,379,380],{},"nuxt >=3.19.0","(또는 ",[40,383,384],{},"^4.1.0",")을 요구한다 — Nuxt 버전이 낮으면 에러 대신 경고만 띄우고 모듈을 조용히 비활성화해버린다.",[188,387,388,389,391,392,394,395,398,399,402],{},"최신 ",[40,390,263],{},"는 ",[40,393,276],{},"를 더 이상 내부에 번들하지 않는다 — 명시적으로 설치해야 한다. 안 하면 ",[40,396,397],{},"nuxt prepare","가 ",[40,400,401],{},"Nuxt Content requires better-sqlite3 module to operate"," 에러로 멈춘다.",[188,404,405,408,409,412,413,415,416,419,420,422],{},[40,406,407],{},"@nuxtjs\u002Fsitemap","의 예전 콘텐츠 연동(",[40,410,411],{},"asSitemapCollection",")이 ",[40,414,263],{}," 런타임에서 ",[40,417,418],{},"queryCollectionWithEvent","를 가져다 쓰는데, 최신 콘텐츠 버전에서 이게 이름이 바뀌었다\u002F없어졌다. ",[40,421,407],{},"도 같이 최신으로 올려야 빌드가 통과한다.",[26,424],{},[29,426,427],{"id":427},"검증",[15,429,430],{},"배포 환경에서만 터지는 버그라, \"로컬에서 빌드된다\"만으로는 증거가 안 된다. 업그레이드 후:",[432,433,434,439,453],"ol",{},[188,435,436,438],{},[40,437,193],{}," 후 실제 빌드 결과물을 로컬에서 직접 실행해서 글 개수가 실제 글 파일 개수와 일치하는지 확인",[188,440,441,442,445,446,448,449,452],{},"배포하고, ",[21,443,444],{},"라이브"," 응답 헤더에서 ",[40,447,210],{}," \u002F 새로운 ",[40,450,451],{},"age","를 확인해서 캐시된(여전히 빈) 응답을 보고 있는 게 아님을 확인",[188,454,455,456,459],{},"배포된 ",[40,457,458],{},"\u002Fsitemap.xml","에 글이 전부 들어있는지 확인 (정적 라우트 몇 개만 있는 게 아니라)",[26,461],{},[29,463,464],{"id":464},"정리",[15,466,467,469],{},[40,468,289],{},"(또는 SQLite 파일 기반의 다른 Nuxt 모듈)이 로컬에서는 되는데 Vercel에서만 에러 없이 빈 값만 돌려준다면, 로직보다 파일시스템 제약을 먼저 의심하자. 그리고 그 모듈이 자기 자신의 Vercel 대응 수정보다 더 오래된 버전에 고정돼 있는지도 확인하자. \"로컬에선 되는데\"는 캐시 문제인 경우만큼이나, 파일시스템 권한 문제인 경우도 많다.",[471,472,473],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":38,"searchDepth":64,"depth":64,"links":475},[476,477,478,480,481,482],{"id":31,"depth":64,"text":31},{"id":219,"depth":64,"text":220},{"id":259,"depth":64,"text":479},"원인: @nuxt\u002Fcontent v3의 SQLite DB는 Vercel에서 \u002Ftmp가 필요한데, 3.0.0은 그걸 몰랐다",{"id":326,"depth":64,"text":326},{"id":427,"depth":64,"text":427},{"id":464,"depth":64,"text":464},"로컬에서는 멀쩡한데 Vercel에 올리면 queryCollection()이 빈 배열만 반환하던 버그를 추적한 과정. 원인은 @nuxt\u002Fcontent가 Vercel의 읽기 전용 파일시스템을 지원하기 전 버전에 고정돼 있었던 것.","md",{"date":486,"field":487,"tags":488},"2026.10.07","blog",[489,490],"nuxt-content","vercel",true,"\u002Fko\u002Fnuxt-content-vercel-empty-query-sqlite",{"title":6,"description":483},{"loc":492},"ko\u002Fnuxt-content-vercel-empty-query-sqlite","_YQxUXqXVekuRqWapZt8OXAGOJnZiiZj6C6fodC99b4",{"id":498,"title":499,"body":500,"description":807,"extension":484,"meta":808,"navigation":491,"path":812,"seo":813,"sitemap":814,"stem":815,"__hash__":816},"ko\u002Fko\u002Fdikw-pyramid.md","DIKW 피라미드 정리: 데이터가 지혜가 되기까지",{"type":8,"value":501,"toc":798},[502,506,512,531,534,542,546,551,554,607,610,614,619,622,628,631,635,640,643,649,652,656,661,664,670,673,677,774,778,781,792,795],[29,503,505],{"id":504},"dikw-피라미드란","DIKW 피라미드란?",[507,508,509],"blockquote",{},[15,510,511],{},"가공되지 않은 Data가 어떻게 Information, Knowledge를 거쳐 Wisdom이 되는지를 설명하는 계층 모델",[15,513,514,515,518,519,522,523,526,527,530],{},"DIKW는 ",[21,516,517],{},"D","ata(데이터) → ",[21,520,521],{},"I","nformation(정보) → ",[21,524,525],{},"K","nowledge(지식) → ",[21,528,529],{},"W","isdom(지혜) 4단계의 앞글자를 딴 개념이다. 정보학, 지식경영 분야에서 오래전부터 쓰이던 모델인데, 서비스 로그를 분석하거나 대시보드를 만들 때도 결국 이 피라미드를 그대로 타고 올라가는 과정이라 개발자 입장에서도 정리해둘 만하다.",[15,532,533],{},"피라미드로 그리는 이유는 아래로 갈수록 양이 많고 가공이 덜 되어 있고, 위로 갈수록 양은 적지만 의사결정에 바로 쓸 수 있을 만큼 압축되어 있기 때문이다.",[33,535,540],{"className":536,"code":538,"language":539},[537],"language-text","Data (가장 많음, 가공 전) → Information → Knowledge → Wisdom (가장 적음, 가장 가치 있음)\n","text",[40,541,538],{"__ignoreMap":38},[29,543,545],{"id":544},"_1단계-data-데이터","1단계: Data (데이터)",[507,547,548],{},[15,549,550],{},"맥락 없이 존재하는 가공되지 않은 값",[15,552,553],{},"그 자체로는 의미를 해석할 수 없는 raw한 값이다. 로그 한 줄, 센서 측정값, DB의 row 하나가 여기에 해당한다.",[33,555,559],{"className":556,"code":557,"language":558,"meta":38,"style":38},"language-json shiki shiki-themes github-light github-dark","{ \"userId\": 1023, \"path\": \"\u002Fblog\u002Fcss-layouts\", \"status\": 200, \"ts\": 1789689600 }\n","json",[40,560,561],{"__ignoreMap":38},[43,562,563,566,569,571,574,576,579,581,584,586,589,591,594,596,599,601,604],{"class":45,"line":46},[43,564,565],{"class":49},"{ ",[43,567,568],{"class":81},"\"userId\"",[43,570,78],{"class":49},[43,572,573],{"class":81},"1023",[43,575,104],{"class":49},[43,577,578],{"class":81},"\"path\"",[43,580,78],{"class":49},[43,582,583],{"class":100},"\"\u002Fblog\u002Fcss-layouts\"",[43,585,104],{"class":49},[43,587,588],{"class":81},"\"status\"",[43,590,78],{"class":49},[43,592,593],{"class":81},"200",[43,595,104],{"class":49},[43,597,598],{"class":81},"\"ts\"",[43,600,78],{"class":49},[43,602,603],{"class":81},"1789689600",[43,605,606],{"class":49}," }\n",[15,608,609],{},"이 한 줄만 봐서는 \"이게 좋은 신호인지 나쁜 신호인지\" 판단할 수 없다. 그냥 사실의 기록일 뿐이다.",[29,611,613],{"id":612},"_2단계-information-정보","2단계: Information (정보)",[507,615,616],{},[15,617,618],{},"데이터에 맥락(context)을 붙여서 \"무엇이 일어났는지\" 알 수 있게 만든 것",[15,620,621],{},"여러 데이터를 모으고 정리(집계, 필터링, 구조화)하면 정보가 된다. \"누가, 언제, 무엇을\" 에 답할 수 있는 단계다.",[33,623,626],{"className":624,"code":625,"language":539},[537],"2026-09-18 기준, \u002Fblog\u002Fcss-layouts 페이지 조회수: 312회\n2026-09-18 기준, 전체 방문자 중 68%가 모바일에서 접속\n",[40,627,625],{"__ignoreMap":38},[15,629,630],{},"Data → Information으로 넘어가는 과정이 보통 로그 집계, GA(Google Analytics)\u002FSearch Console 같은 분석 툴이 하는 일이다.",[29,632,634],{"id":633},"_3단계-knowledge-지식","3단계: Knowledge (지식)",[507,636,637],{},[15,638,639],{},"여러 정보 사이의 패턴과 관계를 파악해서 \"왜 그런지\" 설명할 수 있는 상태",[15,641,642],{},"정보들을 시간에 걸쳐 비교하고 다른 정보와 연결지어야 지식이 된다. \"이게 왜 이렇게 됐는지\"에 답할 수 있어야 한다.",[33,644,647],{"className":645,"code":646,"language":539},[537],"CSS 관련 글의 평균 체류시간이 다른 카테고리보다 40% 길다.\n→ 코드 예제가 많은 글일수록 스크롤\u002F체류시간이 늘어나는 경향이 있다.\n",[40,648,646],{"__ignoreMap":38},[15,650,651],{},"이 단계부터는 사람(또는 모델)의 해석과 경험이 개입한다. 같은 정보를 봐도 지식으로 연결짓는 능력은 사람마다 다르다.",[29,653,655],{"id":654},"_4단계-wisdom-지혜","4단계: Wisdom (지혜)",[507,657,658],{},[15,659,660],{},"지식을 바탕으로 \"무엇을 해야 하는지\" 판단하고 행동으로 옮기는 단계",[15,662,663],{},"지혜는 옳고 그름, 우선순위에 대한 가치 판단이 들어간 의사결정이다.",[33,665,668],{"className":666,"code":667,"language":539},[537],"\"코드 예제 중심 글이 체류시간을 늘린다\"는 지식을 바탕으로\n→ 다음 분기 콘텐츠는 개념 설명보다 실습 예제 비중을 늘린다.\n",[40,669,667],{"__ignoreMap":38},[15,671,672],{},"같은 지식을 갖고도 지혜로운 판단은 사람마다, 조직마다 다를 수 있다. 데이터가 아무리 많아도 이 마지막 단계는 자동화하기 가장 어렵다.",[29,674,676],{"id":675},"_4단계-비교-정리","4단계 비교 정리",[678,679,680,702],"table",{},[681,682,683],"thead",{},[684,685,686,690,693,696,699],"tr",{},[687,688,689],"th",{},"단계",[687,691,692],{},"핵심 질문",[687,694,695],{},"예시",[687,697,698],{},"양",[687,700,701],{},"가공 수준",[703,704,705,723,740,757],"tbody",{},[684,706,707,711,714,717,720],{},[708,709,710],"td",{},"Data",[708,712,713],{},"무엇이 기록됐나",[708,715,716],{},"로그 한 줄, 클릭 이벤트",[708,718,719],{},"매우 많음",[708,721,722],{},"없음",[684,724,725,728,731,734,737],{},[708,726,727],{},"Information",[708,729,730],{},"무슨 일이 있었나",[708,732,733],{},"페이지별 조회수 통계",[708,735,736],{},"많음",[708,738,739],{},"집계\u002F구조화",[684,741,742,745,748,751,754],{},[708,743,744],{},"Knowledge",[708,746,747],{},"왜 그런가",[708,749,750],{},"체류시간과 콘텐츠 유형의 상관관계",[708,752,753],{},"적음",[708,755,756],{},"해석",[684,758,759,762,765,768,771],{},[708,760,761],{},"Wisdom",[708,763,764],{},"무엇을 해야 하나",[708,766,767],{},"콘텐츠 전략 변경 결정",[708,769,770],{},"매우 적음",[708,772,773],{},"판단\u002F행동",[29,775,777],{"id":776},"왜-이-모델을-알아두면-좋은가","왜 이 모델을 알아두면 좋은가",[15,779,780],{},"실무에서 흔히 겪는 실수는 Data나 Information 단계에서 멈춰놓고 \"우리는 데이터 기반으로 의사결정한다\"고 착각하는 것이다. 대시보드에 숫자를 잔뜩 띄워놨다고 그게 곧 지식이나 지혜는 아니다.",[185,782,783,786,789],{},[188,784,785],{},"로그만 쌓고 집계를 안 하면 Data에서 멈춘다.",[188,787,788],{},"집계 대시보드만 보고 \"왜 그런지\" 분석을 안 하면 Information에서 멈춘다.",[188,790,791],{},"원인은 알아냈지만 실제 행동(코드 수정, 전략 변경)으로 옮기지 않으면 Knowledge에서 멈춘다.",[15,793,794],{},"DIKW 피라미드는 \"지금 내가 보고 있는 게 몇 단계짜리 산출물인가\"를 스스로 점검하는 체크리스트로 쓰기에 유용하다. 데이터를 모으는 것 자체는 시작일 뿐, 최종 목표는 항상 Wisdom(행동으로 이어지는 판단) 이라는 걸 기억하면 된다.",[471,796,797],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":38,"searchDepth":64,"depth":64,"links":799},[800,801,802,803,804,805,806],{"id":504,"depth":64,"text":505},{"id":544,"depth":64,"text":545},{"id":612,"depth":64,"text":613},{"id":633,"depth":64,"text":634},{"id":654,"depth":64,"text":655},{"id":675,"depth":64,"text":676},{"id":776,"depth":64,"text":777},"Data, Information, Knowledge, Wisdom 4단계로 구성된 DIKW 피라미드 개념을 로그\u002F분석 데이터 같은 실제 개발 사례에 대입해서 정리한다.",{"date":809,"field":810,"tags":811},"2026.09.18","concepts",[75],"\u002Fko\u002Fdikw-pyramid",{"title":499,"description":807},{"loc":812},"ko\u002Fdikw-pyramid","gcVs5j1yLCNuaJO7VMzfqOJA-0M7bXjWQEWsyEjnLwA",{"id":818,"title":819,"body":820,"description":1460,"extension":484,"meta":1461,"navigation":491,"path":1466,"seo":1467,"sitemap":1468,"stem":1469,"__hash__":1470},"ko\u002Fko\u002Fadsense-ads-txt-and-seo-indexing-bug.md","애드센스 ads.txt 경고를 받고 파헤치다가 찾은 진짜 색인 버그",{"type":8,"value":821,"toc":1448},[822,826,829,834,837,841,848,872,875,881,884,959,962,972,979,983,986,1000,1003,1009,1012,1016,1022,1194,1207,1210,1262,1265,1268,1272,1277,1312,1322,1326,1331,1355,1357,1360,1386,1401,1403,1442,1445],[29,823,825],{"id":824},"시작-애드센스의-수익-손실-경고","시작: 애드센스의 수익 손실 경고",[15,827,828],{},"블로그를 운영하다 보면 가끔 애드센스에서 이런 알림을 받는다.",[507,830,831],{},[15,832,833],{},"수익 손실 위험 - 수익에 심각한 영향을 미치지 않도록 사이트에서 발견된 ads.txt 파일 문제를 해결해야 합니다.",[15,835,836],{},"무섭게 생긴 문구지만, 실제로 뭐가 문제인지 하나씩 확인해보기로 했다.",[29,838,840],{"id":839},"adstxt-자체는-멀쩡했다","ads.txt 자체는 멀쩡했다",[15,842,843,844,847],{},"먼저 배포된 사이트에서 ",[40,845,846],{},"ads.txt","가 정상적으로 서빙되는지 확인했다.",[33,849,853],{"className":850,"code":851,"language":852,"meta":38,"style":38},"language-bash shiki shiki-themes github-light github-dark","curl -sS -D - \"https:\u002F\u002Fiambilly-blog.vercel.app\u002Fads.txt\"\n","bash",[40,854,855],{"__ignoreMap":38},[43,856,857,860,863,866,869],{"class":45,"line":46},[43,858,859],{"class":57},"curl",[43,861,862],{"class":81}," -sS",[43,864,865],{"class":81}," -D",[43,867,868],{"class":100}," -",[43,870,871],{"class":100}," \"https:\u002F\u002Fiambilly-blog.vercel.app\u002Fads.txt\"\n",[15,873,874],{},"결과는 200 OK, 내용도 애드센스가 요구하는 값과 완전히 일치했다.",[33,876,879],{"className":877,"code":878,"language":539},[537],"google.com, pub-89xxxxxxxxx575, DIRECT, f08c47fec0942fa0\n",[40,880,878],{"__ignoreMap":38},[15,882,883],{},"ads.txt는 쉼표로 구분된 4개 컬럼으로 구성되는데, 각각의 의미는 다음과 같다.",[678,885,886,899],{},[681,887,888],{},[684,889,890,893,896],{},[687,891,892],{},"컬럼",[687,894,895],{},"값",[687,897,898],{},"의미",[703,900,901,914,927,946],{},[684,902,903,906,911],{},[708,904,905],{},"1",[708,907,908],{},[40,909,910],{},"google.com",[708,912,913],{},"이 광고 인벤토리를 판매할 권한을 가진 광고 시스템(SSP)의 도메인",[684,915,916,919,924],{},[708,917,918],{},"2",[708,920,921],{},[40,922,923],{},"pub-89xxxxxxxxx575",[708,925,926],{},"그 광고 시스템 안에서 나(퍼블리셔)를 식별하는 계정 ID",[684,928,929,932,937],{},[708,930,931],{},"3",[708,933,934],{},[40,935,936],{},"DIRECT",[708,938,939,940,942,943],{},"나와 광고 시스템의 관계. 내가 직접 계약한 관계면 ",[40,941,936],{},", 중개상을 거치면 ",[40,944,945],{},"RESELLER",[684,947,948,951,956],{},[708,949,950],{},"4",[708,952,953],{},[40,954,955],{},"f08c47fec0942fa0",[708,957,958],{},"광고 시스템(여기선 구글)의 인증 기관 ID. 계정마다 다른 값이 아니라 구글을 쓰는 모든 사이트가 공통으로 갖는 고정값",[15,960,961],{},"즉 이 한 줄은 \"구글이 내 사이트의 광고 인벤토리를 직접 판매할 정당한 권한을 갖고 있다\"는 걸 광고주\u002F거래소 쪽에 선언하는 용도다. ads.txt가 만들어진 이유 자체가 \"이 사이트 광고 인벤토리는 나만 팔 수 있다\"를 공개적으로 못박아서, 제3자가 내 사이트인 척 가짜 인벤토리를 파는 도메인 스푸핑 사기를 막기 위함이다.",[15,963,964,965,104,968,971],{},"혹시 구글 크롤러만 차단되는 건 아닌지 ",[40,966,967],{},"Mediapartners-Google",[40,969,970],{},"AdsBot-Google"," User-Agent로도 다시 요청해봤지만 결과는 동일했다. 리다이렉트도 http → https 한 번뿐이라 문제가 될 범위가 아니었고, 파일을 hex 덤프로 열어 BOM이나 숨은 문자가 섞였는지도 확인했지만 깨끗했다.",[15,973,974,975,978],{},"즉, ",[21,976,977],{},"코드\u002F설정 쪽에는 고칠 게 없었다."," 이런 경우 남는 건 구글 쪽 크롤러의 재검증 주기 지연인데, 실제로 애드센스는 ads.txt를 실시간이 아니라 주기적으로만 재크롤링하기 때문에 파일이 정상이어도 예전 경고가 한동안 남아있는 경우가 흔하다고 한다.",[29,980,982],{"id":981},"그런데-색인된-페이지-수가-이상하게-적다","그런데 색인된 페이지 수가 이상하게 적다",[15,984,985],{},"ads.txt는 결론이 났지만, 서치콘솔을 열어보니 다른 게 눈에 띄었다. 블로그 글은 30개가 넘는데 색인된 페이지는 겨우 7개뿐이었다. 다만 이 숫자를 100% 자연 크롤링 결과로 보기는 애매한 게, 예전에 URL 검사 도구로 몇몇 페이지를 직접 \"색인 생성 요청\"했던 기억이 어렴풋이 있다. 정확히 몇 개를 수동으로 요청했는지는 기억이 가물가물해서, 7개 중 일부는 수동 요청 덕분에 색인된 걸 수도 있다는 점은 감안해야 한다. 그래도 30개 넘는 글 중 7개는 누가 봐도 낮은 비율이라, 이건 진짜 문제일 가능성이 높아서 사이트맵부터 열어봤다.",[33,987,989],{"className":850,"code":988,"language":852,"meta":38,"style":38},"curl -sS \"https:\u002F\u002Fiambilly-blog.vercel.app\u002Fsitemap.xml\"\n",[40,990,991],{"__ignoreMap":38},[43,992,993,995,997],{"class":45,"line":46},[43,994,859],{"class":57},[43,996,862],{"class":81},[43,998,999],{"class":100}," \"https:\u002F\u002Fiambilly-blog.vercel.app\u002Fsitemap.xml\"\n",[15,1001,1002],{},"그런데 이상한 URL들이 섞여 있었다.",[33,1004,1007],{"className":1005,"code":1006,"language":539},[537],"\u002Fblog\u002Fcss-layouts-copy\n\u002Fblog\u002Fcss-layouts%20copy       \u003C- 공백이 그대로 인코딩된 URL\n\u002Fblog\u002Fnuxt-useasyncdata-cache-error\n\u002Fblog\u002Fnuxt-useAsyncData-cache-error   \u003C- 대소문자만 다른 중복 URL\n",[40,1008,1006],{"__ignoreMap":38},[15,1010,1011],{},"같은 글인데 URL이 두 개씩 존재하고 있었다.",[29,1013,1015],{"id":1014},"원인-파일명-그대로-라우트를-만드는-코드","원인: 파일명 그대로 라우트를 만드는 코드",[15,1017,1018,1021],{},[40,1019,1020],{},"nuxt.config.ts","에 이런 코드가 있었다.",[33,1023,1027],{"className":1024,"code":1025,"language":1026,"meta":38,"style":38},"language-js shiki shiki-themes github-light github-dark","nitro: {\n  prerender: {\n    crawlLinks: true,\n    routes: await (async () => {\n      const blogFiles = await fs.readdir(resolve('content\u002Fblog'));\n      const blogRoutes = blogFiles.map(file => `\u002Fblog\u002F${file.replace('.md', '')}`);\n      return [...blogRoutes];\n    })(),\n  },\n},\n","js",[40,1028,1029,1037,1044,1057,1078,1109,1161,1176,1182,1188],{"__ignoreMap":38},[43,1030,1031,1034],{"class":45,"line":46},[43,1032,1033],{"class":57},"nitro",[43,1035,1036],{"class":49},": {\n",[43,1038,1039,1042],{"class":45,"line":64},[43,1040,1041],{"class":57},"  prerender",[43,1043,1036],{"class":49},[43,1045,1046,1049,1051,1054],{"class":45,"line":119},[43,1047,1048],{"class":57},"    crawlLinks",[43,1050,78],{"class":49},[43,1052,1053],{"class":81},"true",[43,1055,1056],{"class":49},",\n",[43,1058,1059,1062,1064,1067,1070,1072,1074,1076],{"class":45,"line":150},[43,1060,1061],{"class":57},"    routes",[43,1063,78],{"class":49},[43,1065,1066],{"class":67},"await",[43,1068,1069],{"class":49}," (",[43,1071,107],{"class":67},[43,1073,110],{"class":49},[43,1075,113],{"class":67},[43,1077,116],{"class":49},[43,1079,1080,1083,1086,1088,1090,1093,1096,1098,1101,1103,1106],{"class":45,"line":159},[43,1081,1082],{"class":67},"      const",[43,1084,1085],{"class":81}," blogFiles",[43,1087,128],{"class":67},[43,1089,91],{"class":67},[43,1091,1092],{"class":49}," fs.",[43,1094,1095],{"class":57},"readdir",[43,1097,97],{"class":49},[43,1099,1100],{"class":57},"resolve",[43,1102,97],{"class":49},[43,1104,1105],{"class":100},"'content\u002Fblog'",[43,1107,1108],{"class":49},"));\n",[43,1110,1111,1113,1116,1118,1121,1124,1126,1129,1132,1135,1137,1140,1143,1145,1148,1150,1153,1155,1158],{"class":45,"line":176},[43,1112,1082],{"class":67},[43,1114,1115],{"class":81}," blogRoutes",[43,1117,128],{"class":67},[43,1119,1120],{"class":49}," blogFiles.",[43,1122,1123],{"class":57},"map",[43,1125,97],{"class":49},[43,1127,1128],{"class":74},"file",[43,1130,1131],{"class":67}," =>",[43,1133,1134],{"class":100}," `\u002Fblog\u002F${",[43,1136,1128],{"class":49},[43,1138,1139],{"class":100},".",[43,1141,1142],{"class":57},"replace",[43,1144,97],{"class":100},[43,1146,1147],{"class":100},"'.md'",[43,1149,104],{"class":100},[43,1151,1152],{"class":100},"''",[43,1154,247],{"class":100},[43,1156,1157],{"class":100},"}`",[43,1159,1160],{"class":49},");\n",[43,1162,1164,1167,1170,1173],{"class":45,"line":1163},7,[43,1165,1166],{"class":67},"      return",[43,1168,1169],{"class":49}," [",[43,1171,1172],{"class":67},"...",[43,1174,1175],{"class":49},"blogRoutes];\n",[43,1177,1179],{"class":45,"line":1178},8,[43,1180,1181],{"class":49},"    })(),\n",[43,1183,1185],{"class":45,"line":1184},9,[43,1186,1187],{"class":49},"  },\n",[43,1189,1191],{"class":45,"line":1190},10,[43,1192,1193],{"class":49},"},\n",[15,1195,1196,1199,1200,1203,1204,1206],{},[40,1197,1198],{},"content\u002Fblog"," 폴더의 ",[21,1201,1202],{},"파일명을 그대로"," URL로 바꿔서 프리렌더 라우트 목록을 만드는 코드였다. 문제는 실제 글 조회는 ",[40,1205,263],{},"가 슬러그화(소문자 변환, 공백 → 하이픈)한 경로 기준으로 동작한다는 점이다.",[15,1208,1209],{},"그래서 파일명에 대문자나 공백이 섞인 글에서 URL이 갈라졌다.",[678,1211,1212,1225],{},[681,1213,1214],{},[684,1215,1216,1219,1222],{},[687,1217,1218],{},"실제 파일",[687,1220,1221],{},"정상 동작 URL",[687,1223,1224],{},"이 코드가 만든 깨진 URL",[703,1226,1227,1244],{},[684,1228,1229,1234,1239],{},[708,1230,1231],{},[40,1232,1233],{},"nuxt-useAsyncData-cache-error.md",[708,1235,1236],{},[40,1237,1238],{},"\u002Fblog\u002Fnuxt-useasyncdata-cache-error",[708,1240,1241],{},[40,1242,1243],{},"\u002Fblog\u002Fnuxt-useAsyncData-cache-error",[684,1245,1246,1252,1257],{},[708,1247,1248,1251],{},[40,1249,1250],{},"css-layouts copy.md"," (파일명 자체도 잘못 지어짐)",[708,1253,1254],{},[40,1255,1256],{},"\u002Fblog\u002Fcss-layouts-copy",[708,1258,1259],{},[40,1260,1261],{},"\u002Fblog\u002Fcss-layouts%20copy",[15,1263,1264],{},"실제로 오른쪽 URL에 접속해보면 200 응답은 오는데 본문이 텅 비어 있었다. 빌드 시점에 저 파일명 그대로 정적 페이지가 만들어졌는데, 콘텐츠 조회 시 슬러그가 안 맞아서 데이터를 못 찾은 것이다. 그리고 이 빈 페이지들이 사이트맵에도 그대로 올라가서 구글에 노출되고 있었다.",[15,1266,1267],{},"구글 입장에서는 \"응답은 오는데 내용이 없는\" URL을 계속 크롤링하게 되는 셈이라, 크롤 예산이 낭비되고 정상 글의 색인도 늦어질 수 있는 구조였다.",[29,1269,1271],{"id":1270},"고친-방법","고친 방법",[1273,1274,1276],"h3",{"id":1275},"_1-파일명-기반-라우트-생성-코드-제거","1. 파일명 기반 라우트 생성 코드 제거",[33,1278,1280],{"className":1024,"code":1279,"language":1026,"meta":38,"style":38},"nitro: {\n  prerender: {\n    crawlLinks: true,\n  },\n},\n",[40,1281,1282,1288,1294,1304,1308],{"__ignoreMap":38},[43,1283,1284,1286],{"class":45,"line":46},[43,1285,1033],{"class":57},[43,1287,1036],{"class":49},[43,1289,1290,1292],{"class":45,"line":64},[43,1291,1041],{"class":57},[43,1293,1036],{"class":49},[43,1295,1296,1298,1300,1302],{"class":45,"line":119},[43,1297,1048],{"class":57},[43,1299,78],{"class":49},[43,1301,1053],{"class":81},[43,1303,1056],{"class":49},[43,1305,1306],{"class":45,"line":150},[43,1307,1187],{"class":49},[43,1309,1310],{"class":45,"line":159},[43,1311,1193],{"class":49},[15,1313,1314,1315,1318,1319,1321],{},"애초에 이 코드는 불필요했다. ",[40,1316,1317],{},"crawlLinks: true","와 ",[40,1320,407],{}," 모듈이 콘텐츠 컬렉션의 정상 경로를 기준으로 알아서 라우트를 찾아주기 때문이다.",[1273,1323,1325],{"id":1324},"_2-이름이-잘못된-파일-정리","2. 이름이 잘못된 파일 정리",[15,1327,1328,1330],{},[40,1329,1250],{},"는 예전에 다른 글을 복사해서 새 글을 쓴 흔적이 파일명에 그대로 남아있던 케이스였다. 실제 내용은 \"선언적 프로그래밍 vs 절차형 프로그래밍\"이라 파일명을 내용에 맞게 바꿨다.",[33,1332,1334],{"className":850,"code":1333,"language":852,"meta":38,"style":38},"git mv \"content\u002Fblog\u002Fcss-layouts copy.md\" \\\n       \"content\u002Fblog\u002Fdeclarative-vs-procedural-programming.md\"\n",[40,1335,1336,1350],{"__ignoreMap":38},[43,1337,1338,1341,1344,1347],{"class":45,"line":46},[43,1339,1340],{"class":57},"git",[43,1342,1343],{"class":100}," mv",[43,1345,1346],{"class":100}," \"content\u002Fblog\u002Fcss-layouts copy.md\"",[43,1348,1349],{"class":81}," \\\n",[43,1351,1352],{"class":45,"line":64},[43,1353,1354],{"class":100},"       \"content\u002Fblog\u002Fdeclarative-vs-procedural-programming.md\"\n",[29,1356,427],{"id":427},[15,1358,1359],{},"로컬에서 빌드 후 사이트맵을 다시 확인했다.",[33,1361,1363],{"className":850,"code":1362,"language":852,"meta":38,"style":38},"curl -sS \"http:\u002F\u002Flocalhost:3000\u002Fsitemap.xml\" | grep -c \"\u003Cloc>\"\n",[40,1364,1365],{"__ignoreMap":38},[43,1366,1367,1369,1371,1374,1377,1380,1383],{"class":45,"line":46},[43,1368,859],{"class":57},[43,1370,862],{"class":81},[43,1372,1373],{"class":100}," \"http:\u002F\u002Flocalhost:3000\u002Fsitemap.xml\"",[43,1375,1376],{"class":67}," |",[43,1378,1379],{"class":57}," grep",[43,1381,1382],{"class":81}," -c",[43,1384,1385],{"class":100}," \"\u003Cloc>\"\n",[15,1387,1388,1389,1392,1393,1396,1397,1400],{},"수정 전에는 중복\u002F깨진 URL이 섞여 있었지만, 수정 후에는 실제 글 31개 + ",[40,1390,1391],{},"\u002Fblog"," + ",[40,1394,1395],{},"\u002Flog",", 정확히 ",[21,1398,1399],{},"33개","만 깔끔하게 남았다.",[29,1402,464],{"id":464},[678,1404,1405,1418],{},[681,1406,1407],{},[684,1408,1409,1412,1415],{},[687,1410,1411],{},"문제",[687,1413,1414],{},"원인",[687,1416,1417],{},"이번 수정으로 해결됐나",[703,1419,1420,1431],{},[684,1421,1422,1425,1428],{},[708,1423,1424],{},"애드센스 ads.txt 경고",[708,1426,1427],{},"구글 쪽 재크롤링 지연 (파일 자체는 정상)",[708,1429,1430],{},"아니오 — 코드 문제가 아니라서 별도 조치 불가, 시간이 해결",[684,1432,1433,1436,1439],{},[708,1434,1435],{},"색인 페이지 수 부족",[708,1437,1438],{},"파일명 기반 라우트 생성 코드의 슬러그 불일치로 사이트맵에 빈 URL 노출",[708,1440,1441],{},"부분적으로 해결 — 버그는 제거했지만 구글이 기존에 알던 URL을 색인에서 완전히 빼는 데는 시간이 걸릴 수 있음",[15,1443,1444],{},"무서운 경고 문구를 받으면 일단 화면에 보이는 문구 그대로 믿기보다, 실제로 무엇이 어떻게 동작하는지 하나씩 curl로 찍어보는 게 제일 빠르다는 걸 다시 느꼈다. 그리고 예상과 다른 곳(ads.txt가 아니라 사이트맵)에서 진짜 원인을 찾은 것도 재밌는 경험이었다.",[471,1446,1447],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":1449},[1450,1451,1452,1453,1454,1458,1459],{"id":824,"depth":64,"text":825},{"id":839,"depth":64,"text":840},{"id":981,"depth":64,"text":982},{"id":1014,"depth":64,"text":1015},{"id":1270,"depth":64,"text":1271,"children":1455},[1456,1457],{"id":1275,"depth":119,"text":1276},{"id":1324,"depth":119,"text":1325},{"id":427,"depth":64,"text":427},{"id":464,"depth":64,"text":464},"구글 애드센스가 ads.txt 문제를 알려왔는데, 확인해보니 파일은 멀쩡했다. 대신 서치콘솔 색인 수가 적었던 진짜 원인을 Nuxt Content 슬러그 불일치 버그에서 찾은 과정을 정리한다.",{"date":1462,"field":487,"tags":1463},"2026.09.17",[1464,1465,489],"seo","adsense","\u002Fko\u002Fadsense-ads-txt-and-seo-indexing-bug",{"title":819,"description":1460},{"loc":1466},"ko\u002Fadsense-ads-txt-and-seo-indexing-bug","gGiREhecYi7-31x9xc1tqRVEXWee068MjkTYoedB-W0",{"id":1472,"title":1473,"body":1474,"description":1482,"extension":484,"meta":3790,"navigation":491,"path":3796,"seo":3797,"sitemap":3798,"stem":3799,"__hash__":3800},"ko\u002Fko\u002Fminio-guide.md","MinIO 완벽 가이드: 설치부터 Node.js 연동까지",{"type":8,"value":1475,"toc":3752},[1476,1480,1483,1487,1494,1497,1504,1507,1514,1517,1524,1527,1534,1537,1541,1544,1548,1641,1645,1659,1663,1670,1675,1688,1692,1718,1721,1725,1728,1732,1748,1752,1856,1860,2053,2057,2208,2212,2329,2333,2525,2529,2636,2640,2799,2803,3452,3456,3460,3463,3540,3544,3547,3620,3624,3627,3729,3732,3735,3749],[29,1477,1479],{"id":1478},"minio란-무엇인가","MinIO란 무엇인가?",[15,1481,1482],{},"MinIO는 오픈소스 기반의 고성능 객체 스토리지 서버입니다. AWS S3와 호환되는 API를 제공하여 클라우드 네이티브 애플리케이션을 위한 완벽한 스토리지 솔루션으로 자리잡고 있습니다.",[29,1484,1486],{"id":1485},"왜-minio를-사용해야-할까","왜 MinIO를 사용해야 할까?",[1273,1488,1490,1491],{"id":1489},"_1-aws-s3-호환성","1. ",[21,1492,1493],{},"AWS S3 호환성",[15,1495,1496],{},"MinIO는 S3 API와 완벽하게 호환됩니다. 즉, 기존 S3 클라이언트 라이브러리를 그대로 사용할 수 있어 학습 곡선이 낮고 마이그레이션이 쉽습니다.",[1273,1498,1500,1501],{"id":1499},"_2-비용-절감","2. ",[21,1502,1503],{},"비용 절감",[15,1505,1506],{},"클라우드 스토리지 비용이 부담되시나요? MinIO는 온프레미스나 프라이빗 클라우드에서 직접 운영할 수 있어 장기적으로 비용을 크게 절감할 수 있습니다.",[1273,1508,1510,1511],{"id":1509},"_3-개발-환경-구축의-용이성","3. ",[21,1512,1513],{},"개발 환경 구축의 용이성",[15,1515,1516],{},"로컬 개발 환경에서 S3와 동일한 API를 사용하며 테스트할 수 있습니다. 실제 AWS 계정 없이도 개발과 테스트가 가능합니다.",[1273,1518,1520,1521],{"id":1519},"_4-높은-성능","4. ",[21,1522,1523],{},"높은 성능",[15,1525,1526],{},"Go 언어로 작성되어 가볍고 빠르며, 멀티 테넌시와 고가용성을 지원합니다.",[1273,1528,1530,1531],{"id":1529},"_5-쿠버네티스-친화적","5. ",[21,1532,1533],{},"쿠버네티스 친화적",[15,1535,1536],{},"클라우드 네이티브 아키텍처에 최적화되어 있어 컨테이너 환경에서 쉽게 배포하고 관리할 수 있습니다.",[29,1538,1540],{"id":1539},"docker로-minio-설치하기","Docker로 MinIO 설치하기",[15,1542,1543],{},"Docker를 이용하면 MinIO를 몇 분 안에 실행할 수 있습니다.",[1273,1545,1547],{"id":1546},"기본-설치","기본 설치",[33,1549,1551],{"className":850,"code":1550,"language":852,"meta":38,"style":38},"docker run -d \\\n  -p 9000:9000 \\\n  -p 9001:9001 \\\n  --name minio \\\n  -e MINIO_ROOT_USER=minioadmin \\\n  -e MINIO_ROOT_PASSWORD=minioadmin \\\n  -v ~\u002Fminio\u002Fdata:\u002Fdata \\\n  minio\u002Fminio server \u002Fdata --console-address \":9001\"\n",[40,1552,1553,1566,1576,1585,1595,1605,1614,1624],{"__ignoreMap":38},[43,1554,1555,1558,1561,1564],{"class":45,"line":46},[43,1556,1557],{"class":57},"docker",[43,1559,1560],{"class":100}," run",[43,1562,1563],{"class":81}," -d",[43,1565,1349],{"class":81},[43,1567,1568,1571,1574],{"class":45,"line":64},[43,1569,1570],{"class":81},"  -p",[43,1572,1573],{"class":100}," 9000:9000",[43,1575,1349],{"class":81},[43,1577,1578,1580,1583],{"class":45,"line":119},[43,1579,1570],{"class":81},[43,1581,1582],{"class":100}," 9001:9001",[43,1584,1349],{"class":81},[43,1586,1587,1590,1593],{"class":45,"line":150},[43,1588,1589],{"class":81},"  --name",[43,1591,1592],{"class":100}," minio",[43,1594,1349],{"class":81},[43,1596,1597,1600,1603],{"class":45,"line":159},[43,1598,1599],{"class":81},"  -e",[43,1601,1602],{"class":100}," MINIO_ROOT_USER=minioadmin",[43,1604,1349],{"class":81},[43,1606,1607,1609,1612],{"class":45,"line":176},[43,1608,1599],{"class":81},[43,1610,1611],{"class":100}," MINIO_ROOT_PASSWORD=minioadmin",[43,1613,1349],{"class":81},[43,1615,1616,1619,1622],{"class":45,"line":1163},[43,1617,1618],{"class":81},"  -v",[43,1620,1621],{"class":100}," ~\u002Fminio\u002Fdata:\u002Fdata",[43,1623,1349],{"class":81},[43,1625,1626,1629,1632,1635,1638],{"class":45,"line":1178},[43,1627,1628],{"class":100},"  minio\u002Fminio",[43,1630,1631],{"class":100}," server",[43,1633,1634],{"class":100}," \u002Fdata",[43,1636,1637],{"class":81}," --console-address",[43,1639,1640],{"class":100}," \":9001\"\n",[1273,1642,1644],{"id":1643},"포트-설명","포트 설명",[185,1646,1647,1653],{},[188,1648,1649,1652],{},[21,1650,1651],{},"9000",": MinIO API 서버 포트 (S3 호환 API)",[188,1654,1655,1658],{},[21,1656,1657],{},"9001",": MinIO Console UI 포트 (웹 관리 콘솔)",[1273,1660,1662],{"id":1661},"minio-console-접속","MinIO Console 접속",[15,1664,1665,1666,1669],{},"브라우저에서 ",[40,1667,1668],{},"http:\u002F\u002Flocalhost:9001","로 접속합니다.",[15,1671,1672],{},[21,1673,1674],{},"로그인 정보:",[185,1676,1677,1683],{},[188,1678,1679,1680],{},"Username: ",[40,1681,1682],{},"minioadmin",[188,1684,1685,1686],{},"Password: ",[40,1687,1682],{},[1273,1689,1691],{"id":1690},"console에서-버킷-생성하기","Console에서 버킷 생성하기",[432,1693,1694,1701,1707,1713],{},[188,1695,1696,1697,1700],{},"왼쪽 메뉴에서 ",[21,1698,1699],{},"Buckets"," 클릭",[188,1702,1703,1706],{},[21,1704,1705],{},"Create Bucket"," 버튼 클릭",[188,1708,1709,1710,247],{},"버킷 이름 입력 (예: ",[40,1711,1712],{},"test-bucket",[188,1714,1715,1706],{},[21,1716,1717],{},"Create",[15,1719,1720],{},"이제 MinIO가 정상적으로 작동하는 것을 확인했습니다!",[29,1722,1724],{"id":1723},"nodejs에서-minio-사용하기","Node.js에서 MinIO 사용하기",[15,1726,1727],{},"실제 애플리케이션에서 MinIO를 어떻게 사용하는지 알아보겠습니다.",[1273,1729,1731],{"id":1730},"_1-패키지-설치","1. 패키지 설치",[33,1733,1735],{"className":850,"code":1734,"language":852,"meta":38,"style":38},"npm install minio\n",[40,1736,1737],{"__ignoreMap":38},[43,1738,1739,1742,1745],{"class":45,"line":46},[43,1740,1741],{"class":57},"npm",[43,1743,1744],{"class":100}," install",[43,1746,1747],{"class":100}," minio\n",[1273,1749,1751],{"id":1750},"_2-minio-클라이언트-초기화","2. MinIO 클라이언트 초기화",[33,1753,1757],{"className":1754,"code":1755,"language":1756,"meta":38,"style":38},"language-javascript shiki shiki-themes github-light github-dark","const Minio = require('minio');\n\nconst minioClient = new Minio.Client({\n  endPoint: 'localhost',\n  port: 9000,\n  useSSL: false,\n  accessKey: 'minioadmin',\n  secretKey: 'minioadmin'\n});\n","javascript",[40,1758,1759,1778,1783,1804,1814,1823,1833,1843,1851],{"__ignoreMap":38},[43,1760,1761,1763,1766,1768,1771,1773,1776],{"class":45,"line":46},[43,1762,68],{"class":67},[43,1764,1765],{"class":81}," Minio",[43,1767,128],{"class":67},[43,1769,1770],{"class":57}," require",[43,1772,97],{"class":49},[43,1774,1775],{"class":100},"'minio'",[43,1777,1160],{"class":49},[43,1779,1780],{"class":45,"line":64},[43,1781,1782],{"emptyLinePlaceholder":491},"\n",[43,1784,1785,1787,1790,1792,1795,1798,1801],{"class":45,"line":119},[43,1786,68],{"class":67},[43,1788,1789],{"class":81}," minioClient",[43,1791,128],{"class":67},[43,1793,1794],{"class":67}," new",[43,1796,1797],{"class":49}," Minio.",[43,1799,1800],{"class":57},"Client",[43,1802,1803],{"class":49},"({\n",[43,1805,1806,1809,1812],{"class":45,"line":150},[43,1807,1808],{"class":49},"  endPoint: ",[43,1810,1811],{"class":100},"'localhost'",[43,1813,1056],{"class":49},[43,1815,1816,1819,1821],{"class":45,"line":159},[43,1817,1818],{"class":49},"  port: ",[43,1820,1651],{"class":81},[43,1822,1056],{"class":49},[43,1824,1825,1828,1831],{"class":45,"line":176},[43,1826,1827],{"class":49},"  useSSL: ",[43,1829,1830],{"class":81},"false",[43,1832,1056],{"class":49},[43,1834,1835,1838,1841],{"class":45,"line":1163},[43,1836,1837],{"class":49},"  accessKey: ",[43,1839,1840],{"class":100},"'minioadmin'",[43,1842,1056],{"class":49},[43,1844,1845,1848],{"class":45,"line":1178},[43,1846,1847],{"class":49},"  secretKey: ",[43,1849,1850],{"class":100},"'minioadmin'\n",[43,1852,1853],{"class":45,"line":1184},[43,1854,1855],{"class":49},"});\n",[1273,1857,1859],{"id":1858},"_3-버킷-생성","3. 버킷 생성",[33,1861,1863],{"className":1754,"code":1862,"language":1756,"meta":38,"style":38},"async function createBucket(bucketName) {\n  try {\n    const exists = await minioClient.bucketExists(bucketName);\n    \n    if (exists) {\n      console.log(`버킷 '${bucketName}'이 이미 존재합니다.`);\n    } else {\n      await minioClient.makeBucket(bucketName, 'us-east-1');\n      console.log(`버킷 '${bucketName}'이 생성되었습니다.`);\n    }\n  } catch (err) {\n    console.error('버킷 생성 중 오류:', err);\n  }\n}\n\ncreateBucket('my-bucket');\n",[40,1864,1865,1883,1890,1911,1916,1924,1944,1954,1972,1989,1994,2006,2023,2029,2035,2040],{"__ignoreMap":38},[43,1866,1867,1869,1872,1875,1877,1880],{"class":45,"line":46},[43,1868,107],{"class":67},[43,1870,1871],{"class":67}," function",[43,1873,1874],{"class":57}," createBucket",[43,1876,97],{"class":49},[43,1878,1879],{"class":74},"bucketName",[43,1881,1882],{"class":49},") {\n",[43,1884,1885,1888],{"class":45,"line":64},[43,1886,1887],{"class":67},"  try",[43,1889,116],{"class":49},[43,1891,1892,1895,1898,1900,1902,1905,1908],{"class":45,"line":119},[43,1893,1894],{"class":67},"    const",[43,1896,1897],{"class":81}," exists",[43,1899,128],{"class":67},[43,1901,91],{"class":67},[43,1903,1904],{"class":49}," minioClient.",[43,1906,1907],{"class":57},"bucketExists",[43,1909,1910],{"class":49},"(bucketName);\n",[43,1912,1913],{"class":45,"line":150},[43,1914,1915],{"class":49},"    \n",[43,1917,1918,1921],{"class":45,"line":159},[43,1919,1920],{"class":67},"    if",[43,1922,1923],{"class":49}," (exists) {\n",[43,1925,1926,1929,1932,1934,1937,1939,1942],{"class":45,"line":176},[43,1927,1928],{"class":49},"      console.",[43,1930,1931],{"class":57},"log",[43,1933,97],{"class":49},[43,1935,1936],{"class":100},"`버킷 '${",[43,1938,1879],{"class":49},[43,1940,1941],{"class":100},"}'이 이미 존재합니다.`",[43,1943,1160],{"class":49},[43,1945,1946,1949,1952],{"class":45,"line":1163},[43,1947,1948],{"class":49},"    } ",[43,1950,1951],{"class":67},"else",[43,1953,116],{"class":49},[43,1955,1956,1959,1961,1964,1967,1970],{"class":45,"line":1178},[43,1957,1958],{"class":67},"      await",[43,1960,1904],{"class":49},[43,1962,1963],{"class":57},"makeBucket",[43,1965,1966],{"class":49},"(bucketName, ",[43,1968,1969],{"class":100},"'us-east-1'",[43,1971,1160],{"class":49},[43,1973,1974,1976,1978,1980,1982,1984,1987],{"class":45,"line":1184},[43,1975,1928],{"class":49},[43,1977,1931],{"class":57},[43,1979,97],{"class":49},[43,1981,1936],{"class":100},[43,1983,1879],{"class":49},[43,1985,1986],{"class":100},"}'이 생성되었습니다.`",[43,1988,1160],{"class":49},[43,1990,1991],{"class":45,"line":1190},[43,1992,1993],{"class":49},"    }\n",[43,1995,1997,2000,2003],{"class":45,"line":1996},11,[43,1998,1999],{"class":49},"  } ",[43,2001,2002],{"class":67},"catch",[43,2004,2005],{"class":49}," (err) {\n",[43,2007,2009,2012,2015,2017,2020],{"class":45,"line":2008},12,[43,2010,2011],{"class":49},"    console.",[43,2013,2014],{"class":57},"error",[43,2016,97],{"class":49},[43,2018,2019],{"class":100},"'버킷 생성 중 오류:'",[43,2021,2022],{"class":49},", err);\n",[43,2024,2026],{"class":45,"line":2025},13,[43,2027,2028],{"class":49},"  }\n",[43,2030,2032],{"class":45,"line":2031},14,[43,2033,2034],{"class":49},"}\n",[43,2036,2038],{"class":45,"line":2037},15,[43,2039,1782],{"emptyLinePlaceholder":491},[43,2041,2043,2046,2048,2051],{"class":45,"line":2042},16,[43,2044,2045],{"class":57},"createBucket",[43,2047,97],{"class":49},[43,2049,2050],{"class":100},"'my-bucket'",[43,2052,1160],{"class":49},[1273,2054,2056],{"id":2055},"_4-파일-업로드","4. 파일 업로드",[33,2058,2060],{"className":1754,"code":2059,"language":1756,"meta":38,"style":38},"async function uploadFile(bucketName, objectName, filePath) {\n  try {\n    const metaData = {\n      'Content-Type': 'application\u002Foctet-stream'\n    };\n    \n    await minioClient.fPutObject(bucketName, objectName, filePath, metaData);\n    console.log(`파일 '${objectName}'이 업로드되었습니다.`);\n  } catch (err) {\n    console.error('파일 업로드 중 오류:', err);\n  }\n}\n\nuploadFile('my-bucket', 'test.txt', '.\u002Ftest.txt');\n",[40,2061,2062,2087,2093,2104,2114,2119,2123,2136,2154,2162,2175,2179,2183,2187],{"__ignoreMap":38},[43,2063,2064,2066,2068,2071,2073,2075,2077,2080,2082,2085],{"class":45,"line":46},[43,2065,107],{"class":67},[43,2067,1871],{"class":67},[43,2069,2070],{"class":57}," uploadFile",[43,2072,97],{"class":49},[43,2074,1879],{"class":74},[43,2076,104],{"class":49},[43,2078,2079],{"class":74},"objectName",[43,2081,104],{"class":49},[43,2083,2084],{"class":74},"filePath",[43,2086,1882],{"class":49},[43,2088,2089,2091],{"class":45,"line":64},[43,2090,1887],{"class":67},[43,2092,116],{"class":49},[43,2094,2095,2097,2100,2102],{"class":45,"line":119},[43,2096,1894],{"class":67},[43,2098,2099],{"class":81}," metaData",[43,2101,128],{"class":67},[43,2103,116],{"class":49},[43,2105,2106,2109,2111],{"class":45,"line":150},[43,2107,2108],{"class":100},"      'Content-Type'",[43,2110,78],{"class":49},[43,2112,2113],{"class":100},"'application\u002Foctet-stream'\n",[43,2115,2116],{"class":45,"line":159},[43,2117,2118],{"class":49},"    };\n",[43,2120,2121],{"class":45,"line":176},[43,2122,1915],{"class":49},[43,2124,2125,2128,2130,2133],{"class":45,"line":1163},[43,2126,2127],{"class":67},"    await",[43,2129,1904],{"class":49},[43,2131,2132],{"class":57},"fPutObject",[43,2134,2135],{"class":49},"(bucketName, objectName, filePath, metaData);\n",[43,2137,2138,2140,2142,2144,2147,2149,2152],{"class":45,"line":1178},[43,2139,2011],{"class":49},[43,2141,1931],{"class":57},[43,2143,97],{"class":49},[43,2145,2146],{"class":100},"`파일 '${",[43,2148,2079],{"class":49},[43,2150,2151],{"class":100},"}'이 업로드되었습니다.`",[43,2153,1160],{"class":49},[43,2155,2156,2158,2160],{"class":45,"line":1184},[43,2157,1999],{"class":49},[43,2159,2002],{"class":67},[43,2161,2005],{"class":49},[43,2163,2164,2166,2168,2170,2173],{"class":45,"line":1190},[43,2165,2011],{"class":49},[43,2167,2014],{"class":57},[43,2169,97],{"class":49},[43,2171,2172],{"class":100},"'파일 업로드 중 오류:'",[43,2174,2022],{"class":49},[43,2176,2177],{"class":45,"line":1996},[43,2178,2028],{"class":49},[43,2180,2181],{"class":45,"line":2008},[43,2182,2034],{"class":49},[43,2184,2185],{"class":45,"line":2025},[43,2186,1782],{"emptyLinePlaceholder":491},[43,2188,2189,2192,2194,2196,2198,2201,2203,2206],{"class":45,"line":2031},[43,2190,2191],{"class":57},"uploadFile",[43,2193,97],{"class":49},[43,2195,2050],{"class":100},[43,2197,104],{"class":49},[43,2199,2200],{"class":100},"'test.txt'",[43,2202,104],{"class":49},[43,2204,2205],{"class":100},"'.\u002Ftest.txt'",[43,2207,1160],{"class":49},[1273,2209,2211],{"id":2210},"_5-파일-다운로드","5. 파일 다운로드",[33,2213,2215],{"className":1754,"code":2214,"language":1756,"meta":38,"style":38},"async function downloadFile(bucketName, objectName, downloadPath) {\n  try {\n    await minioClient.fGetObject(bucketName, objectName, downloadPath);\n    console.log(`파일 '${objectName}'이 다운로드되었습니다.`);\n  } catch (err) {\n    console.error('파일 다운로드 중 오류:', err);\n  }\n}\n\ndownloadFile('my-bucket', 'test.txt', '.\u002Fdownloaded-test.txt');\n",[40,2216,2217,2241,2247,2259,2276,2284,2297,2301,2305,2309],{"__ignoreMap":38},[43,2218,2219,2221,2223,2226,2228,2230,2232,2234,2236,2239],{"class":45,"line":46},[43,2220,107],{"class":67},[43,2222,1871],{"class":67},[43,2224,2225],{"class":57}," downloadFile",[43,2227,97],{"class":49},[43,2229,1879],{"class":74},[43,2231,104],{"class":49},[43,2233,2079],{"class":74},[43,2235,104],{"class":49},[43,2237,2238],{"class":74},"downloadPath",[43,2240,1882],{"class":49},[43,2242,2243,2245],{"class":45,"line":64},[43,2244,1887],{"class":67},[43,2246,116],{"class":49},[43,2248,2249,2251,2253,2256],{"class":45,"line":119},[43,2250,2127],{"class":67},[43,2252,1904],{"class":49},[43,2254,2255],{"class":57},"fGetObject",[43,2257,2258],{"class":49},"(bucketName, objectName, downloadPath);\n",[43,2260,2261,2263,2265,2267,2269,2271,2274],{"class":45,"line":150},[43,2262,2011],{"class":49},[43,2264,1931],{"class":57},[43,2266,97],{"class":49},[43,2268,2146],{"class":100},[43,2270,2079],{"class":49},[43,2272,2273],{"class":100},"}'이 다운로드되었습니다.`",[43,2275,1160],{"class":49},[43,2277,2278,2280,2282],{"class":45,"line":159},[43,2279,1999],{"class":49},[43,2281,2002],{"class":67},[43,2283,2005],{"class":49},[43,2285,2286,2288,2290,2292,2295],{"class":45,"line":176},[43,2287,2011],{"class":49},[43,2289,2014],{"class":57},[43,2291,97],{"class":49},[43,2293,2294],{"class":100},"'파일 다운로드 중 오류:'",[43,2296,2022],{"class":49},[43,2298,2299],{"class":45,"line":1163},[43,2300,2028],{"class":49},[43,2302,2303],{"class":45,"line":1178},[43,2304,2034],{"class":49},[43,2306,2307],{"class":45,"line":1184},[43,2308,1782],{"emptyLinePlaceholder":491},[43,2310,2311,2314,2316,2318,2320,2322,2324,2327],{"class":45,"line":1190},[43,2312,2313],{"class":57},"downloadFile",[43,2315,97],{"class":49},[43,2317,2050],{"class":100},[43,2319,104],{"class":49},[43,2321,2200],{"class":100},[43,2323,104],{"class":49},[43,2325,2326],{"class":100},"'.\u002Fdownloaded-test.txt'",[43,2328,1160],{"class":49},[1273,2330,2332],{"id":2331},"_6-파일-목록-조회","6. 파일 목록 조회",[33,2334,2336],{"className":1754,"code":2335,"language":1756,"meta":38,"style":38},"async function listObjects(bucketName) {\n  try {\n    const stream = minioClient.listObjects(bucketName, '', true);\n    \n    stream.on('data', (obj) => {\n      console.log('파일:', obj.name, '크기:', obj.size);\n    });\n    \n    stream.on('error', (err) => {\n      console.error('목록 조회 중 오류:', err);\n    });\n  } catch (err) {\n    console.error('오류:', err);\n  }\n}\n\nlistObjects('my-bucket');\n",[40,2337,2338,2353,2359,2383,2387,2413,2433,2438,2442,2464,2477,2481,2489,2502,2506,2510,2514],{"__ignoreMap":38},[43,2339,2340,2342,2344,2347,2349,2351],{"class":45,"line":46},[43,2341,107],{"class":67},[43,2343,1871],{"class":67},[43,2345,2346],{"class":57}," listObjects",[43,2348,97],{"class":49},[43,2350,1879],{"class":74},[43,2352,1882],{"class":49},[43,2354,2355,2357],{"class":45,"line":64},[43,2356,1887],{"class":67},[43,2358,116],{"class":49},[43,2360,2361,2363,2366,2368,2370,2373,2375,2377,2379,2381],{"class":45,"line":119},[43,2362,1894],{"class":67},[43,2364,2365],{"class":81}," stream",[43,2367,128],{"class":67},[43,2369,1904],{"class":49},[43,2371,2372],{"class":57},"listObjects",[43,2374,1966],{"class":49},[43,2376,1152],{"class":100},[43,2378,104],{"class":49},[43,2380,1053],{"class":81},[43,2382,1160],{"class":49},[43,2384,2385],{"class":45,"line":150},[43,2386,1915],{"class":49},[43,2388,2389,2392,2395,2397,2400,2403,2406,2409,2411],{"class":45,"line":159},[43,2390,2391],{"class":49},"    stream.",[43,2393,2394],{"class":57},"on",[43,2396,97],{"class":49},[43,2398,2399],{"class":100},"'data'",[43,2401,2402],{"class":49},", (",[43,2404,2405],{"class":74},"obj",[43,2407,2408],{"class":49},") ",[43,2410,113],{"class":67},[43,2412,116],{"class":49},[43,2414,2415,2417,2419,2421,2424,2427,2430],{"class":45,"line":176},[43,2416,1928],{"class":49},[43,2418,1931],{"class":57},[43,2420,97],{"class":49},[43,2422,2423],{"class":100},"'파일:'",[43,2425,2426],{"class":49},", obj.name, ",[43,2428,2429],{"class":100},"'크기:'",[43,2431,2432],{"class":49},", obj.size);\n",[43,2434,2435],{"class":45,"line":1163},[43,2436,2437],{"class":49},"    });\n",[43,2439,2440],{"class":45,"line":1178},[43,2441,1915],{"class":49},[43,2443,2444,2446,2448,2450,2453,2455,2458,2460,2462],{"class":45,"line":1184},[43,2445,2391],{"class":49},[43,2447,2394],{"class":57},[43,2449,97],{"class":49},[43,2451,2452],{"class":100},"'error'",[43,2454,2402],{"class":49},[43,2456,2457],{"class":74},"err",[43,2459,2408],{"class":49},[43,2461,113],{"class":67},[43,2463,116],{"class":49},[43,2465,2466,2468,2470,2472,2475],{"class":45,"line":1190},[43,2467,1928],{"class":49},[43,2469,2014],{"class":57},[43,2471,97],{"class":49},[43,2473,2474],{"class":100},"'목록 조회 중 오류:'",[43,2476,2022],{"class":49},[43,2478,2479],{"class":45,"line":1996},[43,2480,2437],{"class":49},[43,2482,2483,2485,2487],{"class":45,"line":2008},[43,2484,1999],{"class":49},[43,2486,2002],{"class":67},[43,2488,2005],{"class":49},[43,2490,2491,2493,2495,2497,2500],{"class":45,"line":2025},[43,2492,2011],{"class":49},[43,2494,2014],{"class":57},[43,2496,97],{"class":49},[43,2498,2499],{"class":100},"'오류:'",[43,2501,2022],{"class":49},[43,2503,2504],{"class":45,"line":2031},[43,2505,2028],{"class":49},[43,2507,2508],{"class":45,"line":2037},[43,2509,2034],{"class":49},[43,2511,2512],{"class":45,"line":2042},[43,2513,1782],{"emptyLinePlaceholder":491},[43,2515,2517,2519,2521,2523],{"class":45,"line":2516},17,[43,2518,2372],{"class":57},[43,2520,97],{"class":49},[43,2522,2050],{"class":100},[43,2524,1160],{"class":49},[1273,2526,2528],{"id":2527},"_7-파일-삭제","7. 파일 삭제",[33,2530,2532],{"className":1754,"code":2531,"language":1756,"meta":38,"style":38},"async function deleteFile(bucketName, objectName) {\n  try {\n    await minioClient.removeObject(bucketName, objectName);\n    console.log(`파일 '${objectName}'이 삭제되었습니다.`);\n  } catch (err) {\n    console.error('파일 삭제 중 오류:', err);\n  }\n}\n\ndeleteFile('my-bucket', 'test.txt');\n",[40,2533,2534,2553,2559,2571,2588,2596,2609,2613,2617,2621],{"__ignoreMap":38},[43,2535,2536,2538,2540,2543,2545,2547,2549,2551],{"class":45,"line":46},[43,2537,107],{"class":67},[43,2539,1871],{"class":67},[43,2541,2542],{"class":57}," deleteFile",[43,2544,97],{"class":49},[43,2546,1879],{"class":74},[43,2548,104],{"class":49},[43,2550,2079],{"class":74},[43,2552,1882],{"class":49},[43,2554,2555,2557],{"class":45,"line":64},[43,2556,1887],{"class":67},[43,2558,116],{"class":49},[43,2560,2561,2563,2565,2568],{"class":45,"line":119},[43,2562,2127],{"class":67},[43,2564,1904],{"class":49},[43,2566,2567],{"class":57},"removeObject",[43,2569,2570],{"class":49},"(bucketName, objectName);\n",[43,2572,2573,2575,2577,2579,2581,2583,2586],{"class":45,"line":150},[43,2574,2011],{"class":49},[43,2576,1931],{"class":57},[43,2578,97],{"class":49},[43,2580,2146],{"class":100},[43,2582,2079],{"class":49},[43,2584,2585],{"class":100},"}'이 삭제되었습니다.`",[43,2587,1160],{"class":49},[43,2589,2590,2592,2594],{"class":45,"line":159},[43,2591,1999],{"class":49},[43,2593,2002],{"class":67},[43,2595,2005],{"class":49},[43,2597,2598,2600,2602,2604,2607],{"class":45,"line":176},[43,2599,2011],{"class":49},[43,2601,2014],{"class":57},[43,2603,97],{"class":49},[43,2605,2606],{"class":100},"'파일 삭제 중 오류:'",[43,2608,2022],{"class":49},[43,2610,2611],{"class":45,"line":1163},[43,2612,2028],{"class":49},[43,2614,2615],{"class":45,"line":1178},[43,2616,2034],{"class":49},[43,2618,2619],{"class":45,"line":1184},[43,2620,1782],{"emptyLinePlaceholder":491},[43,2622,2623,2626,2628,2630,2632,2634],{"class":45,"line":1190},[43,2624,2625],{"class":57},"deleteFile",[43,2627,97],{"class":49},[43,2629,2050],{"class":100},[43,2631,104],{"class":49},[43,2633,2200],{"class":100},[43,2635,1160],{"class":49},[1273,2637,2639],{"id":2638},"_8-presigned-url-생성-임시-접근-링크","8. Presigned URL 생성 (임시 접근 링크)",[33,2641,2643],{"className":1754,"code":2642,"language":1756,"meta":38,"style":38},"async function getPresignedUrl(bucketName, objectName, expirySeconds = 3600) {\n  try {\n    const url = await minioClient.presignedGetObject(\n      bucketName, \n      objectName, \n      expirySeconds\n    );\n    console.log('다운로드 URL:', url);\n    return url;\n  } catch (err) {\n    console.error('URL 생성 중 오류:', err);\n  }\n}\n\ngetPresignedUrl('my-bucket', 'test.txt', 7200); \u002F\u002F 2시간 유효\n",[40,2644,2645,2674,2680,2699,2704,2709,2714,2719,2733,2741,2749,2762,2766,2770,2774],{"__ignoreMap":38},[43,2646,2647,2649,2651,2654,2656,2658,2660,2662,2664,2667,2669,2672],{"class":45,"line":46},[43,2648,107],{"class":67},[43,2650,1871],{"class":67},[43,2652,2653],{"class":57}," getPresignedUrl",[43,2655,97],{"class":49},[43,2657,1879],{"class":74},[43,2659,104],{"class":49},[43,2661,2079],{"class":74},[43,2663,104],{"class":49},[43,2665,2666],{"class":74},"expirySeconds",[43,2668,128],{"class":67},[43,2670,2671],{"class":81}," 3600",[43,2673,1882],{"class":49},[43,2675,2676,2678],{"class":45,"line":64},[43,2677,1887],{"class":67},[43,2679,116],{"class":49},[43,2681,2682,2684,2687,2689,2691,2693,2696],{"class":45,"line":119},[43,2683,1894],{"class":67},[43,2685,2686],{"class":81}," url",[43,2688,128],{"class":67},[43,2690,91],{"class":67},[43,2692,1904],{"class":49},[43,2694,2695],{"class":57},"presignedGetObject",[43,2697,2698],{"class":49},"(\n",[43,2700,2701],{"class":45,"line":150},[43,2702,2703],{"class":49},"      bucketName, \n",[43,2705,2706],{"class":45,"line":159},[43,2707,2708],{"class":49},"      objectName, \n",[43,2710,2711],{"class":45,"line":176},[43,2712,2713],{"class":49},"      expirySeconds\n",[43,2715,2716],{"class":45,"line":1163},[43,2717,2718],{"class":49},"    );\n",[43,2720,2721,2723,2725,2727,2730],{"class":45,"line":1178},[43,2722,2011],{"class":49},[43,2724,1931],{"class":57},[43,2726,97],{"class":49},[43,2728,2729],{"class":100},"'다운로드 URL:'",[43,2731,2732],{"class":49},", url);\n",[43,2734,2735,2738],{"class":45,"line":1184},[43,2736,2737],{"class":67},"    return",[43,2739,2740],{"class":49}," url;\n",[43,2742,2743,2745,2747],{"class":45,"line":1190},[43,2744,1999],{"class":49},[43,2746,2002],{"class":67},[43,2748,2005],{"class":49},[43,2750,2751,2753,2755,2757,2760],{"class":45,"line":1996},[43,2752,2011],{"class":49},[43,2754,2014],{"class":57},[43,2756,97],{"class":49},[43,2758,2759],{"class":100},"'URL 생성 중 오류:'",[43,2761,2022],{"class":49},[43,2763,2764],{"class":45,"line":2008},[43,2765,2028],{"class":49},[43,2767,2768],{"class":45,"line":2025},[43,2769,2034],{"class":49},[43,2771,2772],{"class":45,"line":2031},[43,2773,1782],{"emptyLinePlaceholder":491},[43,2775,2776,2779,2781,2783,2785,2787,2789,2792,2795],{"class":45,"line":2037},[43,2777,2778],{"class":57},"getPresignedUrl",[43,2780,97],{"class":49},[43,2782,2050],{"class":100},[43,2784,104],{"class":49},[43,2786,2200],{"class":100},[43,2788,104],{"class":49},[43,2790,2791],{"class":81},"7200",[43,2793,2794],{"class":49},"); ",[43,2796,2798],{"class":2797},"sJ8bj","\u002F\u002F 2시간 유효\n",[29,2800,2802],{"id":2801},"실전-활용-예제-expressjs-파일-업로드","실전 활용 예제: Express.js 파일 업로드",[33,2804,2806],{"className":1754,"code":2805,"language":1756,"meta":38,"style":38},"const express = require('express');\nconst multer = require('multer');\nconst Minio = require('minio');\nconst fs = require('fs');\n\nconst app = express();\nconst upload = multer({ dest: 'uploads\u002F' });\n\nconst minioClient = new Minio.Client({\n  endPoint: 'localhost',\n  port: 9000,\n  useSSL: false,\n  accessKey: 'minioadmin',\n  secretKey: 'minioadmin'\n});\n\nconst BUCKET_NAME = 'uploads';\n\n\u002F\u002F 버킷 초기화\n(async () => {\n  const exists = await minioClient.bucketExists(BUCKET_NAME);\n  if (!exists) {\n    await minioClient.makeBucket(BUCKET_NAME, 'us-east-1');\n  }\n})();\n\n\u002F\u002F 파일 업로드 엔드포인트\napp.post('\u002Fupload', upload.single('file'), async (req, res) => {\n  try {\n    const file = req.file;\n    const objectName = `${Date.now()}-${file.originalname}`;\n    \n    await minioClient.fPutObject(\n      BUCKET_NAME,\n      objectName,\n      file.path,\n      { 'Content-Type': file.mimetype }\n    );\n    \n    \u002F\u002F 임시 파일 삭제\n    fs.unlinkSync(file.path);\n    \n    \u002F\u002F Presigned URL 생성\n    const url = await minioClient.presignedGetObject(BUCKET_NAME, objectName);\n    \n    res.json({\n      success: true,\n      message: '파일이 업로드되었습니다.',\n      filename: objectName,\n      url: url\n    });\n  } catch (err) {\n    res.status(500).json({ success: false, error: err.message });\n  }\n});\n\napp.listen(3000, () => {\n  console.log('서버가 3000번 포트에서 실행 중입니다.');\n});\n",[40,2807,2808,2826,2844,2860,2878,2882,2895,2915,2919,2935,2943,2951,2959,2967,2973,2977,2981,2996,3001,3007,3020,3042,3056,3075,3080,3086,3091,3097,3143,3150,3163,3201,3206,3217,3225,3231,3237,3249,3254,3259,3265,3277,3282,3288,3310,3315,3325,3335,3346,3352,3358,3363,3372,3397,3402,3407,3412,3432,3447],{"__ignoreMap":38},[43,2809,2810,2812,2815,2817,2819,2821,2824],{"class":45,"line":46},[43,2811,68],{"class":67},[43,2813,2814],{"class":81}," express",[43,2816,128],{"class":67},[43,2818,1770],{"class":57},[43,2820,97],{"class":49},[43,2822,2823],{"class":100},"'express'",[43,2825,1160],{"class":49},[43,2827,2828,2830,2833,2835,2837,2839,2842],{"class":45,"line":64},[43,2829,68],{"class":67},[43,2831,2832],{"class":81}," multer",[43,2834,128],{"class":67},[43,2836,1770],{"class":57},[43,2838,97],{"class":49},[43,2840,2841],{"class":100},"'multer'",[43,2843,1160],{"class":49},[43,2845,2846,2848,2850,2852,2854,2856,2858],{"class":45,"line":119},[43,2847,68],{"class":67},[43,2849,1765],{"class":81},[43,2851,128],{"class":67},[43,2853,1770],{"class":57},[43,2855,97],{"class":49},[43,2857,1775],{"class":100},[43,2859,1160],{"class":49},[43,2861,2862,2864,2867,2869,2871,2873,2876],{"class":45,"line":150},[43,2863,68],{"class":67},[43,2865,2866],{"class":81}," fs",[43,2868,128],{"class":67},[43,2870,1770],{"class":57},[43,2872,97],{"class":49},[43,2874,2875],{"class":100},"'fs'",[43,2877,1160],{"class":49},[43,2879,2880],{"class":45,"line":159},[43,2881,1782],{"emptyLinePlaceholder":491},[43,2883,2884,2886,2889,2891,2893],{"class":45,"line":176},[43,2885,68],{"class":67},[43,2887,2888],{"class":81}," app",[43,2890,128],{"class":67},[43,2892,2814],{"class":57},[43,2894,147],{"class":49},[43,2896,2897,2899,2902,2904,2906,2909,2912],{"class":45,"line":1163},[43,2898,68],{"class":67},[43,2900,2901],{"class":81}," upload",[43,2903,128],{"class":67},[43,2905,2832],{"class":57},[43,2907,2908],{"class":49},"({ dest: ",[43,2910,2911],{"class":100},"'uploads\u002F'",[43,2913,2914],{"class":49}," });\n",[43,2916,2917],{"class":45,"line":1178},[43,2918,1782],{"emptyLinePlaceholder":491},[43,2920,2921,2923,2925,2927,2929,2931,2933],{"class":45,"line":1184},[43,2922,68],{"class":67},[43,2924,1789],{"class":81},[43,2926,128],{"class":67},[43,2928,1794],{"class":67},[43,2930,1797],{"class":49},[43,2932,1800],{"class":57},[43,2934,1803],{"class":49},[43,2936,2937,2939,2941],{"class":45,"line":1190},[43,2938,1808],{"class":49},[43,2940,1811],{"class":100},[43,2942,1056],{"class":49},[43,2944,2945,2947,2949],{"class":45,"line":1996},[43,2946,1818],{"class":49},[43,2948,1651],{"class":81},[43,2950,1056],{"class":49},[43,2952,2953,2955,2957],{"class":45,"line":2008},[43,2954,1827],{"class":49},[43,2956,1830],{"class":81},[43,2958,1056],{"class":49},[43,2960,2961,2963,2965],{"class":45,"line":2025},[43,2962,1837],{"class":49},[43,2964,1840],{"class":100},[43,2966,1056],{"class":49},[43,2968,2969,2971],{"class":45,"line":2031},[43,2970,1847],{"class":49},[43,2972,1850],{"class":100},[43,2974,2975],{"class":45,"line":2037},[43,2976,1855],{"class":49},[43,2978,2979],{"class":45,"line":2042},[43,2980,1782],{"emptyLinePlaceholder":491},[43,2982,2983,2985,2988,2990,2993],{"class":45,"line":2516},[43,2984,68],{"class":67},[43,2986,2987],{"class":81}," BUCKET_NAME",[43,2989,128],{"class":67},[43,2991,2992],{"class":100}," 'uploads'",[43,2994,2995],{"class":49},";\n",[43,2997,2999],{"class":45,"line":2998},18,[43,3000,1782],{"emptyLinePlaceholder":491},[43,3002,3004],{"class":45,"line":3003},19,[43,3005,3006],{"class":2797},"\u002F\u002F 버킷 초기화\n",[43,3008,3010,3012,3014,3016,3018],{"class":45,"line":3009},20,[43,3011,97],{"class":49},[43,3013,107],{"class":67},[43,3015,110],{"class":49},[43,3017,113],{"class":67},[43,3019,116],{"class":49},[43,3021,3023,3025,3027,3029,3031,3033,3035,3037,3040],{"class":45,"line":3022},21,[43,3024,122],{"class":67},[43,3026,1897],{"class":81},[43,3028,128],{"class":67},[43,3030,91],{"class":67},[43,3032,1904],{"class":49},[43,3034,1907],{"class":57},[43,3036,97],{"class":49},[43,3038,3039],{"class":81},"BUCKET_NAME",[43,3041,1160],{"class":49},[43,3043,3045,3048,3050,3053],{"class":45,"line":3044},22,[43,3046,3047],{"class":67},"  if",[43,3049,1069],{"class":49},[43,3051,3052],{"class":67},"!",[43,3054,3055],{"class":49},"exists) {\n",[43,3057,3059,3061,3063,3065,3067,3069,3071,3073],{"class":45,"line":3058},23,[43,3060,2127],{"class":67},[43,3062,1904],{"class":49},[43,3064,1963],{"class":57},[43,3066,97],{"class":49},[43,3068,3039],{"class":81},[43,3070,104],{"class":49},[43,3072,1969],{"class":100},[43,3074,1160],{"class":49},[43,3076,3078],{"class":45,"line":3077},24,[43,3079,2028],{"class":49},[43,3081,3083],{"class":45,"line":3082},25,[43,3084,3085],{"class":49},"})();\n",[43,3087,3089],{"class":45,"line":3088},26,[43,3090,1782],{"emptyLinePlaceholder":491},[43,3092,3094],{"class":45,"line":3093},27,[43,3095,3096],{"class":2797},"\u002F\u002F 파일 업로드 엔드포인트\n",[43,3098,3100,3103,3106,3108,3111,3114,3117,3119,3122,3125,3127,3129,3132,3134,3137,3139,3141],{"class":45,"line":3099},28,[43,3101,3102],{"class":49},"app.",[43,3104,3105],{"class":57},"post",[43,3107,97],{"class":49},[43,3109,3110],{"class":100},"'\u002Fupload'",[43,3112,3113],{"class":49},", upload.",[43,3115,3116],{"class":57},"single",[43,3118,97],{"class":49},[43,3120,3121],{"class":100},"'file'",[43,3123,3124],{"class":49},"), ",[43,3126,107],{"class":67},[43,3128,1069],{"class":49},[43,3130,3131],{"class":74},"req",[43,3133,104],{"class":49},[43,3135,3136],{"class":74},"res",[43,3138,2408],{"class":49},[43,3140,113],{"class":67},[43,3142,116],{"class":49},[43,3144,3146,3148],{"class":45,"line":3145},29,[43,3147,1887],{"class":67},[43,3149,116],{"class":49},[43,3151,3153,3155,3158,3160],{"class":45,"line":3152},30,[43,3154,1894],{"class":67},[43,3156,3157],{"class":81}," file",[43,3159,128],{"class":67},[43,3161,3162],{"class":49}," req.file;\n",[43,3164,3166,3168,3171,3173,3176,3179,3181,3184,3187,3190,3192,3194,3197,3199],{"class":45,"line":3165},31,[43,3167,1894],{"class":67},[43,3169,3170],{"class":81}," objectName",[43,3172,128],{"class":67},[43,3174,3175],{"class":100}," `${",[43,3177,3178],{"class":49},"Date",[43,3180,1139],{"class":100},[43,3182,3183],{"class":57},"now",[43,3185,3186],{"class":100},"()",[43,3188,3189],{"class":100},"}-${",[43,3191,1128],{"class":49},[43,3193,1139],{"class":100},[43,3195,3196],{"class":49},"originalname",[43,3198,1157],{"class":100},[43,3200,2995],{"class":49},[43,3202,3204],{"class":45,"line":3203},32,[43,3205,1915],{"class":49},[43,3207,3209,3211,3213,3215],{"class":45,"line":3208},33,[43,3210,2127],{"class":67},[43,3212,1904],{"class":49},[43,3214,2132],{"class":57},[43,3216,2698],{"class":49},[43,3218,3220,3223],{"class":45,"line":3219},34,[43,3221,3222],{"class":81},"      BUCKET_NAME",[43,3224,1056],{"class":49},[43,3226,3228],{"class":45,"line":3227},35,[43,3229,3230],{"class":49},"      objectName,\n",[43,3232,3234],{"class":45,"line":3233},36,[43,3235,3236],{"class":49},"      file.path,\n",[43,3238,3240,3243,3246],{"class":45,"line":3239},37,[43,3241,3242],{"class":49},"      { ",[43,3244,3245],{"class":100},"'Content-Type'",[43,3247,3248],{"class":49},": file.mimetype }\n",[43,3250,3252],{"class":45,"line":3251},38,[43,3253,2718],{"class":49},[43,3255,3257],{"class":45,"line":3256},39,[43,3258,1915],{"class":49},[43,3260,3262],{"class":45,"line":3261},40,[43,3263,3264],{"class":2797},"    \u002F\u002F 임시 파일 삭제\n",[43,3266,3268,3271,3274],{"class":45,"line":3267},41,[43,3269,3270],{"class":49},"    fs.",[43,3272,3273],{"class":57},"unlinkSync",[43,3275,3276],{"class":49},"(file.path);\n",[43,3278,3280],{"class":45,"line":3279},42,[43,3281,1915],{"class":49},[43,3283,3285],{"class":45,"line":3284},43,[43,3286,3287],{"class":2797},"    \u002F\u002F Presigned URL 생성\n",[43,3289,3291,3293,3295,3297,3299,3301,3303,3305,3307],{"class":45,"line":3290},44,[43,3292,1894],{"class":67},[43,3294,2686],{"class":81},[43,3296,128],{"class":67},[43,3298,91],{"class":67},[43,3300,1904],{"class":49},[43,3302,2695],{"class":57},[43,3304,97],{"class":49},[43,3306,3039],{"class":81},[43,3308,3309],{"class":49},", objectName);\n",[43,3311,3313],{"class":45,"line":3312},45,[43,3314,1915],{"class":49},[43,3316,3318,3321,3323],{"class":45,"line":3317},46,[43,3319,3320],{"class":49},"    res.",[43,3322,558],{"class":57},[43,3324,1803],{"class":49},[43,3326,3328,3331,3333],{"class":45,"line":3327},47,[43,3329,3330],{"class":49},"      success: ",[43,3332,1053],{"class":81},[43,3334,1056],{"class":49},[43,3336,3338,3341,3344],{"class":45,"line":3337},48,[43,3339,3340],{"class":49},"      message: ",[43,3342,3343],{"class":100},"'파일이 업로드되었습니다.'",[43,3345,1056],{"class":49},[43,3347,3349],{"class":45,"line":3348},49,[43,3350,3351],{"class":49},"      filename: objectName,\n",[43,3353,3355],{"class":45,"line":3354},50,[43,3356,3357],{"class":49},"      url: url\n",[43,3359,3361],{"class":45,"line":3360},51,[43,3362,2437],{"class":49},[43,3364,3366,3368,3370],{"class":45,"line":3365},52,[43,3367,1999],{"class":49},[43,3369,2002],{"class":67},[43,3371,2005],{"class":49},[43,3373,3375,3377,3380,3382,3385,3387,3389,3392,3394],{"class":45,"line":3374},53,[43,3376,3320],{"class":49},[43,3378,3379],{"class":57},"status",[43,3381,97],{"class":49},[43,3383,3384],{"class":81},"500",[43,3386,141],{"class":49},[43,3388,558],{"class":57},[43,3390,3391],{"class":49},"({ success: ",[43,3393,1830],{"class":81},[43,3395,3396],{"class":49},", error: err.message });\n",[43,3398,3400],{"class":45,"line":3399},54,[43,3401,2028],{"class":49},[43,3403,3405],{"class":45,"line":3404},55,[43,3406,1855],{"class":49},[43,3408,3410],{"class":45,"line":3409},56,[43,3411,1782],{"emptyLinePlaceholder":491},[43,3413,3415,3417,3420,3422,3425,3428,3430],{"class":45,"line":3414},57,[43,3416,3102],{"class":49},[43,3418,3419],{"class":57},"listen",[43,3421,97],{"class":49},[43,3423,3424],{"class":81},"3000",[43,3426,3427],{"class":49},", () ",[43,3429,113],{"class":67},[43,3431,116],{"class":49},[43,3433,3435,3438,3440,3442,3445],{"class":45,"line":3434},58,[43,3436,3437],{"class":49},"  console.",[43,3439,1931],{"class":57},[43,3441,97],{"class":49},[43,3443,3444],{"class":100},"'서버가 3000번 포트에서 실행 중입니다.'",[43,3446,1160],{"class":49},[43,3448,3450],{"class":45,"line":3449},59,[43,3451,1855],{"class":49},[29,3453,3455],{"id":3454},"프로덕션-환경-고려사항","프로덕션 환경 고려사항",[1273,3457,3459],{"id":3458},"_1-보안-설정","1. 보안 설정",[15,3461,3462],{},"기본 계정 정보를 반드시 변경하세요.",[33,3464,3466],{"className":850,"code":3465,"language":852,"meta":38,"style":38},"docker run -d \\\n  -p 9000:9000 \\\n  -p 9001:9001 \\\n  --name minio \\\n  -e MINIO_ROOT_USER=your_secure_username \\\n  -e MINIO_ROOT_PASSWORD=your_secure_password_min_8_chars \\\n  -v ~\u002Fminio\u002Fdata:\u002Fdata \\\n  minio\u002Fminio server \u002Fdata --console-address \":9001\"\n",[40,3467,3468,3478,3486,3494,3502,3511,3520,3528],{"__ignoreMap":38},[43,3469,3470,3472,3474,3476],{"class":45,"line":46},[43,3471,1557],{"class":57},[43,3473,1560],{"class":100},[43,3475,1563],{"class":81},[43,3477,1349],{"class":81},[43,3479,3480,3482,3484],{"class":45,"line":64},[43,3481,1570],{"class":81},[43,3483,1573],{"class":100},[43,3485,1349],{"class":81},[43,3487,3488,3490,3492],{"class":45,"line":119},[43,3489,1570],{"class":81},[43,3491,1582],{"class":100},[43,3493,1349],{"class":81},[43,3495,3496,3498,3500],{"class":45,"line":150},[43,3497,1589],{"class":81},[43,3499,1592],{"class":100},[43,3501,1349],{"class":81},[43,3503,3504,3506,3509],{"class":45,"line":159},[43,3505,1599],{"class":81},[43,3507,3508],{"class":100}," MINIO_ROOT_USER=your_secure_username",[43,3510,1349],{"class":81},[43,3512,3513,3515,3518],{"class":45,"line":176},[43,3514,1599],{"class":81},[43,3516,3517],{"class":100}," MINIO_ROOT_PASSWORD=your_secure_password_min_8_chars",[43,3519,1349],{"class":81},[43,3521,3522,3524,3526],{"class":45,"line":1163},[43,3523,1618],{"class":81},[43,3525,1621],{"class":100},[43,3527,1349],{"class":81},[43,3529,3530,3532,3534,3536,3538],{"class":45,"line":1178},[43,3531,1628],{"class":100},[43,3533,1631],{"class":100},[43,3535,1634],{"class":100},[43,3537,1637],{"class":81},[43,3539,1640],{"class":100},[1273,3541,3543],{"id":3542},"_2-ssltls-설정","2. SSL\u002FTLS 설정",[15,3545,3546],{},"프로덕션에서는 반드시 HTTPS를 사용하세요.",[33,3548,3550],{"className":1754,"code":3549,"language":1756,"meta":38,"style":38},"const minioClient = new Minio.Client({\n  endPoint: 'minio.yourdomain.com',\n  port: 443,\n  useSSL: true,  \u002F\u002F HTTPS 사용\n  accessKey: process.env.MINIO_ACCESS_KEY,\n  secretKey: process.env.MINIO_SECRET_KEY\n});\n",[40,3551,3552,3568,3577,3586,3598,3608,3616],{"__ignoreMap":38},[43,3553,3554,3556,3558,3560,3562,3564,3566],{"class":45,"line":46},[43,3555,68],{"class":67},[43,3557,1789],{"class":81},[43,3559,128],{"class":67},[43,3561,1794],{"class":67},[43,3563,1797],{"class":49},[43,3565,1800],{"class":57},[43,3567,1803],{"class":49},[43,3569,3570,3572,3575],{"class":45,"line":64},[43,3571,1808],{"class":49},[43,3573,3574],{"class":100},"'minio.yourdomain.com'",[43,3576,1056],{"class":49},[43,3578,3579,3581,3584],{"class":45,"line":119},[43,3580,1818],{"class":49},[43,3582,3583],{"class":81},"443",[43,3585,1056],{"class":49},[43,3587,3588,3590,3592,3595],{"class":45,"line":150},[43,3589,1827],{"class":49},[43,3591,1053],{"class":81},[43,3593,3594],{"class":49},",  ",[43,3596,3597],{"class":2797},"\u002F\u002F HTTPS 사용\n",[43,3599,3600,3603,3606],{"class":45,"line":159},[43,3601,3602],{"class":49},"  accessKey: process.env.",[43,3604,3605],{"class":81},"MINIO_ACCESS_KEY",[43,3607,1056],{"class":49},[43,3609,3610,3613],{"class":45,"line":176},[43,3611,3612],{"class":49},"  secretKey: process.env.",[43,3614,3615],{"class":81},"MINIO_SECRET_KEY\n",[43,3617,3618],{"class":45,"line":1163},[43,3619,1855],{"class":49},[1273,3621,3623],{"id":3622},"_3-환경변수-사용","3. 환경변수 사용",[15,3625,3626],{},"민감한 정보는 환경변수로 관리하세요.",[33,3628,3630],{"className":1754,"code":3629,"language":1756,"meta":38,"style":38},"require('dotenv').config();\n\nconst minioClient = new Minio.Client({\n  endPoint: process.env.MINIO_ENDPOINT,\n  port: parseInt(process.env.MINIO_PORT),\n  useSSL: process.env.MINIO_USE_SSL === 'true',\n  accessKey: process.env.MINIO_ACCESS_KEY,\n  secretKey: process.env.MINIO_SECRET_KEY\n});\n",[40,3631,3632,3649,3653,3669,3679,3695,3711,3719,3725],{"__ignoreMap":38},[43,3633,3634,3637,3639,3642,3644,3647],{"class":45,"line":46},[43,3635,3636],{"class":57},"require",[43,3638,97],{"class":49},[43,3640,3641],{"class":100},"'dotenv'",[43,3643,141],{"class":49},[43,3645,3646],{"class":57},"config",[43,3648,147],{"class":49},[43,3650,3651],{"class":45,"line":64},[43,3652,1782],{"emptyLinePlaceholder":491},[43,3654,3655,3657,3659,3661,3663,3665,3667],{"class":45,"line":119},[43,3656,68],{"class":67},[43,3658,1789],{"class":81},[43,3660,128],{"class":67},[43,3662,1794],{"class":67},[43,3664,1797],{"class":49},[43,3666,1800],{"class":57},[43,3668,1803],{"class":49},[43,3670,3671,3674,3677],{"class":45,"line":150},[43,3672,3673],{"class":49},"  endPoint: process.env.",[43,3675,3676],{"class":81},"MINIO_ENDPOINT",[43,3678,1056],{"class":49},[43,3680,3681,3683,3686,3689,3692],{"class":45,"line":159},[43,3682,1818],{"class":49},[43,3684,3685],{"class":57},"parseInt",[43,3687,3688],{"class":49},"(process.env.",[43,3690,3691],{"class":81},"MINIO_PORT",[43,3693,3694],{"class":49},"),\n",[43,3696,3697,3700,3703,3706,3709],{"class":45,"line":176},[43,3698,3699],{"class":49},"  useSSL: process.env.",[43,3701,3702],{"class":81},"MINIO_USE_SSL",[43,3704,3705],{"class":67}," ===",[43,3707,3708],{"class":100}," 'true'",[43,3710,1056],{"class":49},[43,3712,3713,3715,3717],{"class":45,"line":1163},[43,3714,3602],{"class":49},[43,3716,3605],{"class":81},[43,3718,1056],{"class":49},[43,3720,3721,3723],{"class":45,"line":1178},[43,3722,3612],{"class":49},[43,3724,3615],{"class":81},[43,3726,3727],{"class":45,"line":1184},[43,3728,1855],{"class":49},[29,3730,3731],{"id":3731},"마치며",[15,3733,3734],{},"MinIO는 S3 호환 객체 스토리지가 필요한 모든 프로젝트에 훌륭한 선택입니다. 특히 다음과 같은 경우에 적극 추천합니다:",[185,3736,3737,3740,3743,3746],{},[188,3738,3739],{},"로컬 개발 환경에서 S3를 시뮬레이션하고 싶을 때",[188,3741,3742],{},"클라우드 비용을 절감하고 싶을 때",[188,3744,3745],{},"데이터 주권이나 컴플라이언스 요구사항이 있을 때",[188,3747,3748],{},"멀티클라우드 전략을 구사하고 싶을 때",[471,3750,3751],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":38,"searchDepth":64,"depth":64,"links":3753},[3754,3755,3767,3773,3783,3784,3789],{"id":1478,"depth":64,"text":1479},{"id":1485,"depth":64,"text":1486,"children":3756},[3757,3759,3761,3763,3765],{"id":1489,"depth":119,"text":3758},"1. AWS S3 호환성",{"id":1499,"depth":119,"text":3760},"2. 비용 절감",{"id":1509,"depth":119,"text":3762},"3. 개발 환경 구축의 용이성",{"id":1519,"depth":119,"text":3764},"4. 높은 성능",{"id":1529,"depth":119,"text":3766},"5. 쿠버네티스 친화적",{"id":1539,"depth":64,"text":1540,"children":3768},[3769,3770,3771,3772],{"id":1546,"depth":119,"text":1547},{"id":1643,"depth":119,"text":1644},{"id":1661,"depth":119,"text":1662},{"id":1690,"depth":119,"text":1691},{"id":1723,"depth":64,"text":1724,"children":3774},[3775,3776,3777,3778,3779,3780,3781,3782],{"id":1730,"depth":119,"text":1731},{"id":1750,"depth":119,"text":1751},{"id":1858,"depth":119,"text":1859},{"id":2055,"depth":119,"text":2056},{"id":2210,"depth":119,"text":2211},{"id":2331,"depth":119,"text":2332},{"id":2527,"depth":119,"text":2528},{"id":2638,"depth":119,"text":2639},{"id":2801,"depth":64,"text":2802},{"id":3454,"depth":64,"text":3455,"children":3785},[3786,3787,3788],{"id":3458,"depth":119,"text":3459},{"id":3542,"depth":119,"text":3543},{"id":3622,"depth":119,"text":3623},{"id":3731,"depth":64,"text":3731},{"date":3791,"field":3792,"tags":3793},"2025.11.23","backend",[3794,3795],"minio","nodejs","\u002Fko\u002Fminio-guide",{"title":1473,"description":1482},{"loc":3796},"ko\u002Fminio-guide","kokmY60Lo1IQpas72FHzOYMisJ1mo0FVBBk_pC5rNwQ",{"id":3802,"title":3803,"body":3804,"description":4433,"extension":484,"meta":4434,"navigation":491,"path":4440,"seo":4441,"sitemap":4442,"stem":4443,"__hash__":4444},"ko\u002Fko\u002Fhow-to-mount-storage.md","라즈베리파이4에 저장장치 마운트 시키기",{"type":8,"value":3805,"toc":4425},[3806,3809,3811,3815,3822,3835,3838,3937,3947,3956,3958,3962,3967,3983,3986,4069,4076,4086,4088,4092,4099,4111,4113,4117,4124,4139,4146,4172,4175,4181,4191,4194,4278,4280,4284,4287,4339,4341,4347,4360,4362,4366,4369,4386,4388,4394,4408,4410,4417,4422],[15,3807,3808],{},"라즈베리파이에 SSD를 연결해두고 오랫동안 방치했는데, 문득 생각나서 마운트를 시도하게 되었다. 이 글에서는 SSD를 연결한 후 파티션을 생성하고 포맷하여 라즈베리파이에서 마운트하는 과정을 정리하였다.",[26,3810],{},[29,3812,3814],{"id":3813},"연결된-저장장치-확인","연결된 저장장치 확인",[15,3816,3817,3818,3821],{},"먼저 ",[40,3819,3820],{},"lsblk -f"," 명령어를 통해 현재 연결된 블록 장치를 확인한다.",[33,3823,3825],{"className":850,"code":3824,"language":852,"meta":38,"style":38},"lsblk -f\n",[40,3826,3827],{"__ignoreMap":38},[43,3828,3829,3832],{"class":45,"line":46},[43,3830,3831],{"class":57},"lsblk",[43,3833,3834],{"class":81}," -f\n",[15,3836,3837],{},"출력 예시는 다음과 같다:",[33,3839,3841],{"className":850,"code":3840,"language":852,"meta":38,"style":38},"NAME        FSTYPE FSVER LABEL   UUID                                   FSAVAIL FSUSE% MOUNTPOINTS\nsda                                                                                   \nmmcblk0                                                                                \n├─mmcblk0p1 vfat   FAT32 bootfs  5DC7-F115                              434.7M     15% \u002Fboot\u002Ffirmware\n└─mmcblk0p2 ext4   1.0   rootfs  a36be96c-66be-4487-a7a6-0481bca99d89   101G       8% \u002F\n",[40,3842,3843,3869,3877,3885,3911],{"__ignoreMap":38},[43,3844,3845,3848,3851,3854,3857,3860,3863,3866],{"class":45,"line":46},[43,3846,3847],{"class":57},"NAME",[43,3849,3850],{"class":100},"        FSTYPE",[43,3852,3853],{"class":100}," FSVER",[43,3855,3856],{"class":100}," LABEL",[43,3858,3859],{"class":100},"   UUID",[43,3861,3862],{"class":100},"                                   FSAVAIL",[43,3864,3865],{"class":100}," FSUSE%",[43,3867,3868],{"class":100}," MOUNTPOINTS\n",[43,3870,3871,3874],{"class":45,"line":64},[43,3872,3873],{"class":57},"sda",[43,3875,3876],{"class":49},"                                                                                   \n",[43,3878,3879,3882],{"class":45,"line":119},[43,3880,3881],{"class":57},"mmcblk0",[43,3883,3884],{"class":49},"                                                                                \n",[43,3886,3887,3890,3893,3896,3899,3902,3905,3908],{"class":45,"line":150},[43,3888,3889],{"class":57},"├─mmcblk0p1",[43,3891,3892],{"class":100}," vfat",[43,3894,3895],{"class":100},"   FAT32",[43,3897,3898],{"class":100}," bootfs",[43,3900,3901],{"class":100},"  5DC7-F115",[43,3903,3904],{"class":100},"                              434.7M",[43,3906,3907],{"class":100},"     15%",[43,3909,3910],{"class":100}," \u002Fboot\u002Ffirmware\n",[43,3912,3913,3916,3919,3922,3925,3928,3931,3934],{"class":45,"line":159},[43,3914,3915],{"class":57},"└─mmcblk0p2",[43,3917,3918],{"class":100}," ext4",[43,3920,3921],{"class":81},"   1.0",[43,3923,3924],{"class":100},"   rootfs",[43,3926,3927],{"class":100},"  a36be96c-66be-4487-a7a6-0481bca99d89",[43,3929,3930],{"class":100},"   101G",[43,3932,3933],{"class":100},"       8%",[43,3935,3936],{"class":100}," \u002F\n",[15,3938,3939,3942,3943,3946],{},[40,3940,3941],{},"\u002Fdev\u002Fsda","는 연결된 SSD를 나타내지만, 아직 파티션이 없고 파일 시스템도 지정되지 않았기 때문에 ",[40,3944,3945],{},"FSTYPE"," 항목이 비어 있다.",[15,3948,3949,3951,3952,3955],{},[40,3950,3831],{}," 명령어는 시스템에 연결된 디스크와 파티션 구조를 확인할 수 있게 해준다. ",[40,3953,3954],{},"-f"," 옵션을 붙이면 파일 시스템 타입과 UUID 등의 정보도 함께 출력된다.\n::",[26,3957],{},[29,3959,3961],{"id":3960},"파티션-생성","파티션 생성",[15,3963,3964,3966],{},[40,3965,3941],{}," 장치에 파티션을 생성하려면 다음 명령어를 입력한다.",[33,3968,3970],{"className":850,"code":3969,"language":852,"meta":38,"style":38},"sudo fdisk \u002Fdev\u002Fsda\n",[40,3971,3972],{"__ignoreMap":38},[43,3973,3974,3977,3980],{"class":45,"line":46},[43,3975,3976],{"class":57},"sudo",[43,3978,3979],{"class":100}," fdisk",[43,3981,3982],{"class":100}," \u002Fdev\u002Fsda\n",[15,3984,3985],{},"이 명령어를 입력하면 대화형(interactive) 모드로 진입하게 된다. 아래 순서에 따라 파티션을 생성한다:",[432,3987,3988,4000,4012,4023,4035,4046,4057],{},[188,3989,3990,3991],{},"현재 파티션 상태 확인 (선택 사항)",[33,3992,3994],{"className":850,"code":3993,"language":852,"meta":38,"style":38},"p\n",[40,3995,3996],{"__ignoreMap":38},[43,3997,3998],{"class":45,"line":46},[43,3999,3993],{"class":57},[188,4001,4002,4003],{},"새 파티션 생성",[33,4004,4006],{"className":850,"code":4005,"language":852,"meta":38,"style":38},"n\n",[40,4007,4008],{"__ignoreMap":38},[43,4009,4010],{"class":45,"line":46},[43,4011,4005],{"class":57},[188,4013,4014,4015],{},"파티션 타입 선택 (Primary)",[33,4016,4017],{"className":850,"code":3993,"language":852,"meta":38,"style":38},[40,4018,4019],{"__ignoreMap":38},[43,4020,4021],{"class":45,"line":46},[43,4022,3993],{"class":57},[188,4024,4025,4026],{},"파티션 번호 입력 (기본값: 1)",[33,4027,4029],{"className":850,"code":4028,"language":852,"meta":38,"style":38},"[엔터]\n",[40,4030,4031],{"__ignoreMap":38},[43,4032,4033],{"class":45,"line":46},[43,4034,4028],{"class":49},[188,4036,4037,4038],{},"시작 섹터 설정 (기본값 사용)",[33,4039,4040],{"className":850,"code":4028,"language":852,"meta":38,"style":38},[40,4041,4042],{"__ignoreMap":38},[43,4043,4044],{"class":45,"line":46},[43,4045,4028],{"class":49},[188,4047,4048,4049],{},"마지막 섹터 설정 (기본값 사용 - 디스크 전체 사용)",[33,4050,4051],{"className":850,"code":4028,"language":852,"meta":38,"style":38},[40,4052,4053],{"__ignoreMap":38},[43,4054,4055],{"class":45,"line":46},[43,4056,4028],{"class":49},[188,4058,4059,4060],{},"변경 사항 저장 및 종료",[33,4061,4063],{"className":850,"code":4062,"language":852,"meta":38,"style":38},"w\n",[40,4064,4065],{"__ignoreMap":38},[43,4066,4067],{"class":45,"line":46},[43,4068,4062],{"class":57},[15,4070,4071,4072,4075],{},"위 과정이 완료되면 새로운 파티션 ",[40,4073,4074],{},"\u002Fdev\u002Fsda1","이 생성된다.",[15,4077,4078,4081,4082,4085],{},[40,4079,4080],{},"fdisk","에서 ",[40,4083,4084],{},"w"," 명령을 입력해야 실제로 디스크에 파티션 변경 사항이 반영된다.\n::",[26,4087],{},[29,4089,4091],{"id":4090},"파티션-정보-반영","파티션 정보 반영",[15,4093,4094,4095,4098],{},"파티션을 만든 후에는 시스템 커널에 변경 사항을 알려야 한다. 이를 위해 ",[40,4096,4097],{},"partprobe"," 명령을 실행하거나, 시스템을 재부팅할 수 있다.",[33,4100,4102],{"className":850,"code":4101,"language":852,"meta":38,"style":38},"sudo partprobe\n",[40,4103,4104],{"__ignoreMap":38},[43,4105,4106,4108],{"class":45,"line":46},[43,4107,3976],{"class":57},[43,4109,4110],{"class":100}," partprobe\n",[26,4112],{},[29,4114,4116],{"id":4115},"파일-시스템-생성-포맷","파일 시스템 생성 (포맷)",[15,4118,4119,4120,4123],{},"파티션에 파일 시스템을 생성해야 실제로 데이터를 저장할 수 있다. 일반적으로 리눅스 시스템에서는 ",[40,4121,4122],{},"ext4"," 파일 시스템을 사용한다.",[33,4125,4127],{"className":850,"code":4126,"language":852,"meta":38,"style":38},"sudo mkfs.ext4 \u002Fdev\u002Fsda1\n",[40,4128,4129],{"__ignoreMap":38},[43,4130,4131,4133,4136],{"class":45,"line":46},[43,4132,3976],{"class":57},[43,4134,4135],{"class":100}," mkfs.ext4",[43,4137,4138],{"class":100}," \u002Fdev\u002Fsda1\n",[15,4140,4141,4142,4145],{},"Windows에서 사용하려면 ",[40,4143,4144],{},"ntfs","로 포맷할 수 있다. 이 경우, 다음 명령어를 먼저 실행해야 한다:",[33,4147,4149],{"className":850,"code":4148,"language":852,"meta":38,"style":38},"sudo apt install ntfs-3g\nsudo mkfs.ntfs \u002Fdev\u002Fsda1\n",[40,4150,4151,4163],{"__ignoreMap":38},[43,4152,4153,4155,4158,4160],{"class":45,"line":46},[43,4154,3976],{"class":57},[43,4156,4157],{"class":100}," apt",[43,4159,1744],{"class":100},[43,4161,4162],{"class":100}," ntfs-3g\n",[43,4164,4165,4167,4170],{"class":45,"line":64},[43,4166,3976],{"class":57},[43,4168,4169],{"class":100}," mkfs.ntfs",[43,4171,4138],{"class":100},[15,4173,4174],{},"::",[15,4176,4177,4178,4180],{},"포맷이 완료되면 다시 ",[40,4179,3820],{}," 명령어를 실행하여 파일 시스템이 적용되었는지 확인할 수 있다.",[33,4182,4183],{"className":850,"code":3824,"language":852,"meta":38,"style":38},[40,4184,4185],{"__ignoreMap":38},[43,4186,4187,4189],{"class":45,"line":46},[43,4188,3831],{"class":57},[43,4190,3834],{"class":81},[15,4192,4193],{},"출력 예시:",[33,4195,4197],{"className":850,"code":4196,"language":852,"meta":38,"style":38},"NAME        FSTYPE LABEL UUID                                   FSAVAIL FSUSE% MOUNTPOINTS\nsda                                                                                   \n└─sda1      ext4         e3c7b8f2-1a3d-4a66-9e2a-9a41231f531b                      \nmmcblk0                                                                                \n├─mmcblk0p1 vfat  bootfs 5DC7-F115                              434.7M     15% \u002Fboot\u002Ffirmware\n└─mmcblk0p2 ext4  rootfs a36be96c-66be-4487-a7a6-0481bca99d89   101G       8% \u002F\n",[40,4198,4199,4216,4222,4236,4242,4260],{"__ignoreMap":38},[43,4200,4201,4203,4205,4207,4210,4212,4214],{"class":45,"line":46},[43,4202,3847],{"class":57},[43,4204,3850],{"class":100},[43,4206,3856],{"class":100},[43,4208,4209],{"class":100}," UUID",[43,4211,3862],{"class":100},[43,4213,3865],{"class":100},[43,4215,3868],{"class":100},[43,4217,4218,4220],{"class":45,"line":64},[43,4219,3873],{"class":57},[43,4221,3876],{"class":49},[43,4223,4224,4227,4230,4233],{"class":45,"line":119},[43,4225,4226],{"class":57},"└─sda1",[43,4228,4229],{"class":100},"      ext4",[43,4231,4232],{"class":100},"         e3c7b8f2-1a3d-4a66-9e2a-9a41231f531b",[43,4234,4235],{"class":49},"                      \n",[43,4237,4238,4240],{"class":45,"line":150},[43,4239,3881],{"class":57},[43,4241,3884],{"class":49},[43,4243,4244,4246,4248,4251,4254,4256,4258],{"class":45,"line":159},[43,4245,3889],{"class":57},[43,4247,3892],{"class":100},[43,4249,4250],{"class":100},"  bootfs",[43,4252,4253],{"class":100}," 5DC7-F115",[43,4255,3904],{"class":100},[43,4257,3907],{"class":100},[43,4259,3910],{"class":100},[43,4261,4262,4264,4266,4269,4272,4274,4276],{"class":45,"line":176},[43,4263,3915],{"class":57},[43,4265,3918],{"class":100},[43,4267,4268],{"class":100},"  rootfs",[43,4270,4271],{"class":100}," a36be96c-66be-4487-a7a6-0481bca99d89",[43,4273,3930],{"class":100},[43,4275,3933],{"class":100},[43,4277,3936],{"class":100},[26,4279],{},[29,4281,4283],{"id":4282},"마운트-하기","마운트 하기",[15,4285,4286],{},"이제 생성된 파티션을 특정 디렉터리에 마운트하여 사용할 수 있다.",[432,4288,4289,4307,4327],{},[188,4290,4291,4292],{},"마운트할 디렉터리를 생성한다.",[33,4293,4295],{"className":850,"code":4294,"language":852,"meta":38,"style":38},"sudo mkdir \u002Fmnt\u002Fmystorage\n",[40,4296,4297],{"__ignoreMap":38},[43,4298,4299,4301,4304],{"class":45,"line":46},[43,4300,3976],{"class":57},[43,4302,4303],{"class":100}," mkdir",[43,4305,4306],{"class":100}," \u002Fmnt\u002Fmystorage\n",[188,4308,4309,4310],{},"디바이스를 해당 디렉터리에 마운트한다.",[33,4311,4313],{"className":850,"code":4312,"language":852,"meta":38,"style":38},"sudo mount \u002Fdev\u002Fsda1 \u002Fmnt\u002Fmystorage\n",[40,4314,4315],{"__ignoreMap":38},[43,4316,4317,4319,4322,4325],{"class":45,"line":46},[43,4318,3976],{"class":57},[43,4320,4321],{"class":100}," mount",[43,4323,4324],{"class":100}," \u002Fdev\u002Fsda1",[43,4326,4306],{"class":100},[188,4328,4329,4330],{},"마운트 확인",[33,4331,4333],{"className":850,"code":4332,"language":852,"meta":38,"style":38},"lsblk\n",[40,4334,4335],{"__ignoreMap":38},[43,4336,4337],{"class":45,"line":46},[43,4338,4332],{"class":57},[15,4340,4193],{},[33,4342,4345],{"className":4343,"code":4344,"language":539},[537],"NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS\nsda           8:0    0 111.8G  0 disk \n└─sda1        8:1    0 111.8G  0 part \u002Fmnt\u002Fmystorage\nmmcblk0     179:0    0 119.1G  0 disk \n├─mmcblk0p1 179:1    0   512M  0 part \u002Fboot\u002Ffirmware\n└─mmcblk0p2 179:2    0 118.6G  0 part \u002F\n",[40,4346,4344],{"__ignoreMap":38},[15,4348,4349,4350,104,4353,104,4356,4359],{},"마운트 지점 디렉터리 이름은 자유롭게 지정할 수 있다. ",[40,4351,4352],{},"\u002Fmnt\u002Fssd",[40,4354,4355],{},"\u002Fdata",[40,4357,4358],{},"\u002Fmedia\u002Fstorage"," 등 원하는 대로 설정하면 된다.\n::",[26,4361],{},[29,4363,4365],{"id":4364},"추가-팁-ssd인지-확인하는-방법","추가 팁: SSD인지 확인하는 방법",[15,4367,4368],{},"SSD인지 HDD인지 확인하고 싶다면 다음 명령어를 사용한다:",[33,4370,4372],{"className":850,"code":4371,"language":852,"meta":38,"style":38},"lsblk -d -o NAME,ROTA\n",[40,4373,4374],{"__ignoreMap":38},[43,4375,4376,4378,4380,4383],{"class":45,"line":46},[43,4377,3831],{"class":57},[43,4379,1563],{"class":81},[43,4381,4382],{"class":81}," -o",[43,4384,4385],{"class":100}," NAME,ROTA\n",[15,4387,4193],{},[33,4389,4392],{"className":4390,"code":4391,"language":539},[537],"NAME   ROTA\nsda        0\nmmcblk0    0\n",[40,4393,4391],{"__ignoreMap":38},[185,4395,4396,4403],{},[188,4397,4398,4399,4402],{},"ROTA 값이 ",[40,4400,4401],{},"0","이면 SSD 또는 SD카드 (비회전 디스크)",[188,4404,4398,4405,4407],{},[40,4406,905],{},"이면 HDD (회전 디스크)",[26,4409],{},[15,4411,4412,4413,4416],{},"이 과정을 통해 라즈베리파이에서 SSD를 인식하고 사용할 수 있게 되었다. 이후 ",[40,4414,4415],{},"\u002Fetc\u002Ffstab"," 파일을 수정하면 시스템 부팅 시 자동으로 마운트되도록 설정할 수 있다.",[33,4418,4420],{"className":4419,"code":38,"language":539},[537],[40,4421,38],{"__ignoreMap":38},[471,4423,4424],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":38,"searchDepth":64,"depth":64,"links":4426},[4427,4428,4429,4430,4431,4432],{"id":3813,"depth":64,"text":3814},{"id":3960,"depth":64,"text":3961},{"id":4090,"depth":64,"text":4091},{"id":4115,"depth":64,"text":4116},{"id":4282,"depth":64,"text":4283},{"id":4364,"depth":64,"text":4365},"라즈베리파이에 저장장치를 연결하고 파티션 생성부터 포맷, 마운트까지의 전체 과정을 정리한 기록입니다.",{"date":4435,"field":4436,"tags":4437},"2025.07.11","infra",[4438,4439],"raspberry-pi","linux","\u002Fko\u002Fhow-to-mount-storage",{"title":3803,"description":4433},{"loc":4440},"ko\u002Fhow-to-mount-storage","ghzRKLj2cKqEZbGLeqXxgBpXvgqjyxKlT-fx7ZOzAwA",{"id":4446,"title":4447,"body":4448,"description":5951,"extension":484,"meta":5952,"navigation":491,"path":5956,"seo":5957,"sitemap":5958,"stem":5959,"__hash__":5960},"ko\u002Fko\u002Fcsv-parser-cleaning.md","복잡한 CSV 데이터를 안전하게 파싱하는 방법",{"type":8,"value":4449,"toc":5922},[4450,4457,4460,4466,4478,4485,4487,4491,4494,4500,4502,4506,4514,4517,4534,4537,4575,4577,4581,4612,4615,4665,4667,4671,4677,4771,4774,4793,4795,4799,4830,4833,4864,4866,4870,4920,4923,4925,4932,4987,4990,5019,5021,5025,5029,5074,5076,5080,5144,5149,5202,5204,5208,5514,5516,5520,5523,5536,5538,5542,5562,5566,5570,5573,5599,5614,5616,5620,5656,5659,5719,5721,5728,5731,5763,5786,5792,5794,5797,5851,5853,5857,5860,5875,5878,5914,5916,5919],[15,4451,4452,4453,4456],{},"CSV 데이터를 JavaScript에서 파싱할 때, ",[40,4454,4455],{},"split(',')"," 한 줄로 해결하려 했다가 망해본 적 있지 않은가?",[15,4458,4459],{},"특히 다음과 같은 경우:",[33,4461,4464],{"className":4462,"code":4463,"language":539},[537],"\nname,age,desc\njohn,25,\"funny, tall, smart\"\n\n",[40,4465,4463],{"__ignoreMap":38},[507,4467,4468],{},[15,4469,4470,4473,4474,4477],{},[21,4471,4472],{},"문제:"," ",[40,4475,4476],{},"\"funny, tall, smart\"","는 하나의 셀인데, 쉼표 때문에 3개의 컬럼으로 나뉘어버림!",[15,4479,4480,4481,4484],{},"이 글에서는 ",[21,4482,4483],{},"쉼표, 줄바꿈, 큰따옴표가 섞인 CSV 데이터를 객체로 변환하는 완전한 처리 과정","을 소개한다.",[26,4486],{},[29,4488,4490],{"id":4489},"예제-데이터","🧾 예제 데이터",[15,4492,4493],{},"CSV 파일에는 다음과 같은 데이터가 있다고 가정한다:",[33,4495,4498],{"className":4496,"code":4497,"language":539},[537],"\nname,age,intro\njohn,25,\"funny, tall\"\namy,30,\"kind, smart\"\n\n",[40,4499,4497],{"__ignoreMap":38},[26,4501],{},[29,4503,4505],{"id":4504},"처리-흐름","🔄 처리 흐름",[1273,4507,4509,4510,4513],{"id":4508},"_1-etargetresult로부터-csv-텍스트를-가져오기","✅ 1. ",[40,4511,4512],{},"e.target.result","로부터 CSV 텍스트를 가져오기",[15,4515,4516],{},"파일 리더를 통해 읽은 결과는 단순 문자열이다:",[33,4518,4520],{"className":1024,"code":4519,"language":1026,"meta":38,"style":38},"const raw = e.target.result;\n",[40,4521,4522],{"__ignoreMap":38},[43,4523,4524,4526,4529,4531],{"class":45,"line":46},[43,4525,68],{"class":67},[43,4527,4528],{"class":81}," raw",[43,4530,128],{"class":67},[43,4532,4533],{"class":49}," e.target.result;\n",[15,4535,4536],{},"예시:",[33,4538,4540],{"className":1024,"code":4539,"language":1026,"meta":38,"style":38},"\"name,age,intro\\r\\njohn,25,\\\"funny, tall\\\"\\r\\namy,30,\\\"kind, smart\\\"\\r\\n\"\n",[40,4541,4542],{"__ignoreMap":38},[43,4543,4544,4547,4550,4553,4556,4559,4562,4565,4567,4570,4572],{"class":45,"line":46},[43,4545,4546],{"class":100},"\"name,age,intro",[43,4548,4549],{"class":81},"\\r\\n",[43,4551,4552],{"class":100},"john,25,",[43,4554,4555],{"class":81},"\\\"",[43,4557,4558],{"class":100},"funny, tall",[43,4560,4561],{"class":81},"\\\"\\r\\n",[43,4563,4564],{"class":100},"amy,30,",[43,4566,4555],{"class":81},[43,4568,4569],{"class":100},"kind, smart",[43,4571,4561],{"class":81},[43,4573,4574],{"class":100},"\"\n",[26,4576],{},[1273,4578,4580],{"id":4579},"_2-줄-단위로-나누기","✅ 2. 줄 단위로 나누기",[33,4582,4584],{"className":1024,"code":4583,"language":1026,"meta":38,"style":38},"const items = raw.split(\"\\r\\n\");\n",[40,4585,4586],{"__ignoreMap":38},[43,4587,4588,4590,4593,4595,4598,4601,4603,4606,4608,4610],{"class":45,"line":46},[43,4589,68],{"class":67},[43,4591,4592],{"class":81}," items",[43,4594,128],{"class":67},[43,4596,4597],{"class":49}," raw.",[43,4599,4600],{"class":57},"split",[43,4602,97],{"class":49},[43,4604,4605],{"class":100},"\"",[43,4607,4549],{"class":81},[43,4609,4605],{"class":100},[43,4611,1160],{"class":49},[15,4613,4614],{},"결과:",[33,4616,4618],{"className":1024,"code":4617,"language":1026,"meta":38,"style":38},"[\n  \"name,age,intro\",\n  \"john,25,\\\"funny, tall\\\"\",\n  \"amy,30,\\\"kind, smart\\\"\"\n]\n",[40,4619,4620,4625,4632,4647,4660],{"__ignoreMap":38},[43,4621,4622],{"class":45,"line":46},[43,4623,4624],{"class":49},"[\n",[43,4626,4627,4630],{"class":45,"line":64},[43,4628,4629],{"class":100},"  \"name,age,intro\"",[43,4631,1056],{"class":49},[43,4633,4634,4637,4639,4641,4643,4645],{"class":45,"line":119},[43,4635,4636],{"class":100},"  \"john,25,",[43,4638,4555],{"class":81},[43,4640,4558],{"class":100},[43,4642,4555],{"class":81},[43,4644,4605],{"class":100},[43,4646,1056],{"class":49},[43,4648,4649,4652,4654,4656,4658],{"class":45,"line":150},[43,4650,4651],{"class":100},"  \"amy,30,",[43,4653,4555],{"class":81},[43,4655,4569],{"class":100},[43,4657,4555],{"class":81},[43,4659,4574],{"class":100},[43,4661,4662],{"class":45,"line":159},[43,4663,4664],{"class":49},"]\n",[26,4666],{},[1273,4668,4670],{"id":4669},"_3-큰따옴표-안의-쉼표를-임시로-치환","✅ 3. 큰따옴표 안의 쉼표를 임시로 치환",[15,4672,4673,4674,1139],{},"CSV의 특성상 큰따옴표로 감싼 부분 안의 쉼표는 ",[21,4675,4676],{},"분리 기준이 되면 안 된다",[33,4678,4680],{"className":1024,"code":4679,"language":1026,"meta":38,"style":38},"items.map((item) =>\n  item.replace(\u002F\"[^\"]*\"\u002Fg, (match) => match.replace(\u002F,\u002Fg, \"##comma##\"))\n);\n",[40,4681,4682,4700,4767],{"__ignoreMap":38},[43,4683,4684,4687,4689,4692,4695,4697],{"class":45,"line":46},[43,4685,4686],{"class":49},"items.",[43,4688,1123],{"class":57},[43,4690,4691],{"class":49},"((",[43,4693,4694],{"class":74},"item",[43,4696,2408],{"class":49},[43,4698,4699],{"class":67},"=>\n",[43,4701,4702,4705,4707,4709,4712,4715,4718,4721,4724,4727,4729,4731,4734,4736,4739,4741,4743,4746,4748,4750,4752,4755,4757,4759,4761,4764],{"class":45,"line":64},[43,4703,4704],{"class":49},"  item.",[43,4706,1142],{"class":57},[43,4708,97],{"class":49},[43,4710,4711],{"class":100},"\u002F",[43,4713,4605],{"class":4714},"sA_wV",[43,4716,4717],{"class":81},"[",[43,4719,4720],{"class":67},"^",[43,4722,4723],{"class":81},"\"]",[43,4725,4726],{"class":67},"*",[43,4728,4605],{"class":4714},[43,4730,4711],{"class":100},[43,4732,4733],{"class":67},"g",[43,4735,2402],{"class":49},[43,4737,4738],{"class":74},"match",[43,4740,2408],{"class":49},[43,4742,113],{"class":67},[43,4744,4745],{"class":49}," match.",[43,4747,1142],{"class":57},[43,4749,97],{"class":49},[43,4751,4711],{"class":100},[43,4753,4754],{"class":4714},",",[43,4756,4711],{"class":100},[43,4758,4733],{"class":67},[43,4760,104],{"class":49},[43,4762,4763],{"class":100},"\"##comma##\"",[43,4765,4766],{"class":49},"))\n",[43,4768,4769],{"class":45,"line":119},[43,4770,1160],{"class":49},[15,4772,4773],{},"처리 결과 예시:",[33,4775,4777],{"className":1024,"code":4776,"language":1026,"meta":38,"style":38},"\"john,25,\\\"funny##comma## tall\\\"\"\n",[40,4778,4779],{"__ignoreMap":38},[43,4780,4781,4784,4786,4789,4791],{"class":45,"line":46},[43,4782,4783],{"class":100},"\"john,25,",[43,4785,4555],{"class":81},[43,4787,4788],{"class":100},"funny##comma## tall",[43,4790,4555],{"class":81},[43,4792,4574],{"class":100},[26,4794],{},[1273,4796,4798],{"id":4797},"_4-쉼표로-split-이제는-안전하게-가능","✅ 4. 쉼표로 split (이제는 안전하게 가능)",[33,4800,4802],{"className":1024,"code":4801,"language":1026,"meta":38,"style":38},".map((item) => item.split(\",\"))\n",[40,4803,4804],{"__ignoreMap":38},[43,4805,4806,4808,4810,4812,4814,4816,4818,4821,4823,4825,4828],{"class":45,"line":46},[43,4807,1139],{"class":49},[43,4809,1123],{"class":57},[43,4811,4691],{"class":49},[43,4813,4694],{"class":74},[43,4815,2408],{"class":49},[43,4817,113],{"class":67},[43,4819,4820],{"class":49}," item.",[43,4822,4600],{"class":57},[43,4824,97],{"class":49},[43,4826,4827],{"class":100},"\",\"",[43,4829,4766],{"class":49},[15,4831,4832],{},"이제 각 줄은 안전하게 배열로 바뀐다:",[33,4834,4836],{"className":1024,"code":4835,"language":1026,"meta":38,"style":38},"[\"john\", \"25\", \"\\\"funny##comma## tall\\\"\"]\n",[40,4837,4838],{"__ignoreMap":38},[43,4839,4840,4842,4845,4847,4850,4852,4854,4856,4858,4860,4862],{"class":45,"line":46},[43,4841,4717],{"class":49},[43,4843,4844],{"class":100},"\"john\"",[43,4846,104],{"class":49},[43,4848,4849],{"class":100},"\"25\"",[43,4851,104],{"class":49},[43,4853,4605],{"class":100},[43,4855,4555],{"class":81},[43,4857,4788],{"class":100},[43,4859,4555],{"class":81},[43,4861,4605],{"class":100},[43,4863,4664],{"class":49},[26,4865],{},[1273,4867,4869],{"id":4868},"_5-빈-줄-제거","✅ 5. 빈 줄 제거",[33,4871,4873],{"className":1024,"code":4872,"language":1026,"meta":38,"style":38},".filter((arr) => !arr.every((element) => element === \"\"))\n",[40,4874,4875],{"__ignoreMap":38},[43,4876,4877,4879,4882,4884,4887,4889,4891,4894,4897,4900,4902,4905,4907,4909,4912,4915,4918],{"class":45,"line":46},[43,4878,1139],{"class":49},[43,4880,4881],{"class":57},"filter",[43,4883,4691],{"class":49},[43,4885,4886],{"class":74},"arr",[43,4888,2408],{"class":49},[43,4890,113],{"class":67},[43,4892,4893],{"class":67}," !",[43,4895,4896],{"class":49},"arr.",[43,4898,4899],{"class":57},"every",[43,4901,4691],{"class":49},[43,4903,4904],{"class":74},"element",[43,4906,2408],{"class":49},[43,4908,113],{"class":67},[43,4910,4911],{"class":49}," element ",[43,4913,4914],{"class":67},"===",[43,4916,4917],{"class":100}," \"\"",[43,4919,4766],{"class":49},[15,4921,4922],{},"의미 없는 빈 줄은 제거한다.",[26,4924],{},[1273,4926,4928,4929,247],{"id":4927},"_6-임시-토큰-복원-comma","✅ 6. 임시 토큰 복원 (",[40,4930,4931],{},"##comma## → ,",[33,4933,4935],{"className":1024,"code":4934,"language":1026,"meta":38,"style":38},".map((item) => item.map((col) => col.replace(\u002F##comma##\u002Fg, \",\")))\n",[40,4936,4937],{"__ignoreMap":38},[43,4938,4939,4941,4943,4945,4947,4949,4951,4953,4955,4957,4960,4962,4964,4967,4969,4971,4973,4976,4978,4980,4982,4984],{"class":45,"line":46},[43,4940,1139],{"class":49},[43,4942,1123],{"class":57},[43,4944,4691],{"class":49},[43,4946,4694],{"class":74},[43,4948,2408],{"class":49},[43,4950,113],{"class":67},[43,4952,4820],{"class":49},[43,4954,1123],{"class":57},[43,4956,4691],{"class":49},[43,4958,4959],{"class":74},"col",[43,4961,2408],{"class":49},[43,4963,113],{"class":67},[43,4965,4966],{"class":49}," col.",[43,4968,1142],{"class":57},[43,4970,97],{"class":49},[43,4972,4711],{"class":100},[43,4974,4975],{"class":4714},"##comma##",[43,4977,4711],{"class":100},[43,4979,4733],{"class":67},[43,4981,104],{"class":49},[43,4983,4827],{"class":100},[43,4985,4986],{"class":49},")))\n",[15,4988,4989],{},"최종적으로 다시 원래의 쉼표를 되돌린다:",[33,4991,4993],{"className":1024,"code":4992,"language":1026,"meta":38,"style":38},"[\"john\", \"25\", \"\\\"funny, tall\\\"\"]\n",[40,4994,4995],{"__ignoreMap":38},[43,4996,4997,4999,5001,5003,5005,5007,5009,5011,5013,5015,5017],{"class":45,"line":46},[43,4998,4717],{"class":49},[43,5000,4844],{"class":100},[43,5002,104],{"class":49},[43,5004,4849],{"class":100},[43,5006,104],{"class":49},[43,5008,4605],{"class":100},[43,5010,4555],{"class":81},[43,5012,4558],{"class":100},[43,5014,4555],{"class":81},[43,5016,4605],{"class":100},[43,5018,4664],{"class":49},[26,5020],{},[29,5022,5024],{"id":5023},"객체-변환-단계","🧠 객체 변환 단계",[1273,5026,5028],{"id":5027},"_7-헤더-추출","✅ 7. 헤더 추출",[33,5030,5032],{"className":1024,"code":5031,"language":1026,"meta":38,"style":38},"const headers = items[0]; \u002F\u002F [\"name\", \"age\", \"intro\"]\nitems.splice(0, 1); \u002F\u002F 데이터에서 헤더 제거\n",[40,5033,5034,5054],{"__ignoreMap":38},[43,5035,5036,5038,5041,5043,5046,5048,5051],{"class":45,"line":46},[43,5037,68],{"class":67},[43,5039,5040],{"class":81}," headers",[43,5042,128],{"class":67},[43,5044,5045],{"class":49}," items[",[43,5047,4401],{"class":81},[43,5049,5050],{"class":49},"]; ",[43,5052,5053],{"class":2797},"\u002F\u002F [\"name\", \"age\", \"intro\"]\n",[43,5055,5056,5058,5061,5063,5065,5067,5069,5071],{"class":45,"line":64},[43,5057,4686],{"class":49},[43,5059,5060],{"class":57},"splice",[43,5062,97],{"class":49},[43,5064,4401],{"class":81},[43,5066,104],{"class":49},[43,5068,905],{"class":81},[43,5070,2794],{"class":49},[43,5072,5073],{"class":2797},"\u002F\u002F 데이터에서 헤더 제거\n",[26,5075],{},[1273,5077,5079],{"id":5078},"_8-데이터-배열을-객체-배열로-변환","✅ 8. 데이터 배열을 객체 배열로 변환",[33,5081,5083],{"className":1024,"code":5082,"language":1026,"meta":38,"style":38},"const data = items.map((item) =>\n  item.reduce((acc, col, i) => ({ ...acc, [headers[i]]: col }), {})\n);\n",[40,5084,5085,5107,5140],{"__ignoreMap":38},[43,5086,5087,5089,5092,5094,5097,5099,5101,5103,5105],{"class":45,"line":46},[43,5088,68],{"class":67},[43,5090,5091],{"class":81}," data",[43,5093,128],{"class":67},[43,5095,5096],{"class":49}," items.",[43,5098,1123],{"class":57},[43,5100,4691],{"class":49},[43,5102,4694],{"class":74},[43,5104,2408],{"class":49},[43,5106,4699],{"class":67},[43,5108,5109,5111,5114,5116,5119,5121,5123,5125,5128,5130,5132,5135,5137],{"class":45,"line":64},[43,5110,4704],{"class":49},[43,5112,5113],{"class":57},"reduce",[43,5115,4691],{"class":49},[43,5117,5118],{"class":74},"acc",[43,5120,104],{"class":49},[43,5122,4959],{"class":74},[43,5124,104],{"class":49},[43,5126,5127],{"class":74},"i",[43,5129,2408],{"class":49},[43,5131,113],{"class":67},[43,5133,5134],{"class":49}," ({ ",[43,5136,1172],{"class":67},[43,5138,5139],{"class":49},"acc, [headers[i]]: col }), {})\n",[43,5141,5142],{"class":45,"line":119},[43,5143,1160],{"class":49},[5145,5146,5148],"h4",{"id":5147},"예시-결과","예시 결과:",[33,5150,5152],{"className":1024,"code":5151,"language":1026,"meta":38,"style":38},"[\n  { name: \"john\", age: \"25\", intro: \"funny, tall\" },\n  { name: \"amy\", age: \"30\", intro: \"kind, smart\" }\n]\n",[40,5153,5154,5158,5179,5198],{"__ignoreMap":38},[43,5155,5156],{"class":45,"line":46},[43,5157,4624],{"class":49},[43,5159,5160,5163,5165,5168,5170,5173,5176],{"class":45,"line":64},[43,5161,5162],{"class":49},"  { name: ",[43,5164,4844],{"class":100},[43,5166,5167],{"class":49},", age: ",[43,5169,4849],{"class":100},[43,5171,5172],{"class":49},", intro: ",[43,5174,5175],{"class":100},"\"funny, tall\"",[43,5177,5178],{"class":49}," },\n",[43,5180,5181,5183,5186,5188,5191,5193,5196],{"class":45,"line":119},[43,5182,5162],{"class":49},[43,5184,5185],{"class":100},"\"amy\"",[43,5187,5167],{"class":49},[43,5189,5190],{"class":100},"\"30\"",[43,5192,5172],{"class":49},[43,5194,5195],{"class":100},"\"kind, smart\"",[43,5197,606],{"class":49},[43,5199,5200],{"class":45,"line":150},[43,5201,4664],{"class":49},[26,5203],{},[29,5205,5207],{"id":5206},"최종-코드","✨ 최종 코드",[33,5209,5211],{"className":1024,"code":5210,"language":1026,"meta":38,"style":38},"const items = e.target.result\n  .split(\"\\r\\n\")\n  .map((item) =>\n    item.replace(\u002F\"[^\"]*\"\u002Fg, (match) => match.replace(\u002F,\u002Fg, \"##comma##\"))\n  )\n  .map((item) => item.split(\",\"))\n  .filter((arr) => !arr.every((element) => element === \"\"))\n  .map((item) => item.map((col) => col.replace(\u002F##comma##\u002Fg, \",\")));\n\nconst headers = items[0];\nitems.splice(0, 1);\n\nconst data = items.map((item) =>\n  item.reduce((acc, col, i) => ({ ...acc, [headers[i]]: col }), {})\n);\n",[40,5212,5213,5224,5242,5256,5311,5316,5340,5376,5423,5427,5442,5458,5462,5482,5510],{"__ignoreMap":38},[43,5214,5215,5217,5219,5221],{"class":45,"line":46},[43,5216,68],{"class":67},[43,5218,4592],{"class":81},[43,5220,128],{"class":67},[43,5222,5223],{"class":49}," e.target.result\n",[43,5225,5226,5229,5231,5233,5235,5237,5239],{"class":45,"line":64},[43,5227,5228],{"class":49},"  .",[43,5230,4600],{"class":57},[43,5232,97],{"class":49},[43,5234,4605],{"class":100},[43,5236,4549],{"class":81},[43,5238,4605],{"class":100},[43,5240,5241],{"class":49},")\n",[43,5243,5244,5246,5248,5250,5252,5254],{"class":45,"line":119},[43,5245,5228],{"class":49},[43,5247,1123],{"class":57},[43,5249,4691],{"class":49},[43,5251,4694],{"class":74},[43,5253,2408],{"class":49},[43,5255,4699],{"class":67},[43,5257,5258,5261,5263,5265,5267,5269,5271,5273,5275,5277,5279,5281,5283,5285,5287,5289,5291,5293,5295,5297,5299,5301,5303,5305,5307,5309],{"class":45,"line":150},[43,5259,5260],{"class":49},"    item.",[43,5262,1142],{"class":57},[43,5264,97],{"class":49},[43,5266,4711],{"class":100},[43,5268,4605],{"class":4714},[43,5270,4717],{"class":81},[43,5272,4720],{"class":67},[43,5274,4723],{"class":81},[43,5276,4726],{"class":67},[43,5278,4605],{"class":4714},[43,5280,4711],{"class":100},[43,5282,4733],{"class":67},[43,5284,2402],{"class":49},[43,5286,4738],{"class":74},[43,5288,2408],{"class":49},[43,5290,113],{"class":67},[43,5292,4745],{"class":49},[43,5294,1142],{"class":57},[43,5296,97],{"class":49},[43,5298,4711],{"class":100},[43,5300,4754],{"class":4714},[43,5302,4711],{"class":100},[43,5304,4733],{"class":67},[43,5306,104],{"class":49},[43,5308,4763],{"class":100},[43,5310,4766],{"class":49},[43,5312,5313],{"class":45,"line":159},[43,5314,5315],{"class":49},"  )\n",[43,5317,5318,5320,5322,5324,5326,5328,5330,5332,5334,5336,5338],{"class":45,"line":176},[43,5319,5228],{"class":49},[43,5321,1123],{"class":57},[43,5323,4691],{"class":49},[43,5325,4694],{"class":74},[43,5327,2408],{"class":49},[43,5329,113],{"class":67},[43,5331,4820],{"class":49},[43,5333,4600],{"class":57},[43,5335,97],{"class":49},[43,5337,4827],{"class":100},[43,5339,4766],{"class":49},[43,5341,5342,5344,5346,5348,5350,5352,5354,5356,5358,5360,5362,5364,5366,5368,5370,5372,5374],{"class":45,"line":1163},[43,5343,5228],{"class":49},[43,5345,4881],{"class":57},[43,5347,4691],{"class":49},[43,5349,4886],{"class":74},[43,5351,2408],{"class":49},[43,5353,113],{"class":67},[43,5355,4893],{"class":67},[43,5357,4896],{"class":49},[43,5359,4899],{"class":57},[43,5361,4691],{"class":49},[43,5363,4904],{"class":74},[43,5365,2408],{"class":49},[43,5367,113],{"class":67},[43,5369,4911],{"class":49},[43,5371,4914],{"class":67},[43,5373,4917],{"class":100},[43,5375,4766],{"class":49},[43,5377,5378,5380,5382,5384,5386,5388,5390,5392,5394,5396,5398,5400,5402,5404,5406,5408,5410,5412,5414,5416,5418,5420],{"class":45,"line":1178},[43,5379,5228],{"class":49},[43,5381,1123],{"class":57},[43,5383,4691],{"class":49},[43,5385,4694],{"class":74},[43,5387,2408],{"class":49},[43,5389,113],{"class":67},[43,5391,4820],{"class":49},[43,5393,1123],{"class":57},[43,5395,4691],{"class":49},[43,5397,4959],{"class":74},[43,5399,2408],{"class":49},[43,5401,113],{"class":67},[43,5403,4966],{"class":49},[43,5405,1142],{"class":57},[43,5407,97],{"class":49},[43,5409,4711],{"class":100},[43,5411,4975],{"class":4714},[43,5413,4711],{"class":100},[43,5415,4733],{"class":67},[43,5417,104],{"class":49},[43,5419,4827],{"class":100},[43,5421,5422],{"class":49},")));\n",[43,5424,5425],{"class":45,"line":1184},[43,5426,1782],{"emptyLinePlaceholder":491},[43,5428,5429,5431,5433,5435,5437,5439],{"class":45,"line":1190},[43,5430,68],{"class":67},[43,5432,5040],{"class":81},[43,5434,128],{"class":67},[43,5436,5045],{"class":49},[43,5438,4401],{"class":81},[43,5440,5441],{"class":49},"];\n",[43,5443,5444,5446,5448,5450,5452,5454,5456],{"class":45,"line":1996},[43,5445,4686],{"class":49},[43,5447,5060],{"class":57},[43,5449,97],{"class":49},[43,5451,4401],{"class":81},[43,5453,104],{"class":49},[43,5455,905],{"class":81},[43,5457,1160],{"class":49},[43,5459,5460],{"class":45,"line":2008},[43,5461,1782],{"emptyLinePlaceholder":491},[43,5463,5464,5466,5468,5470,5472,5474,5476,5478,5480],{"class":45,"line":2025},[43,5465,68],{"class":67},[43,5467,5091],{"class":81},[43,5469,128],{"class":67},[43,5471,5096],{"class":49},[43,5473,1123],{"class":57},[43,5475,4691],{"class":49},[43,5477,4694],{"class":74},[43,5479,2408],{"class":49},[43,5481,4699],{"class":67},[43,5483,5484,5486,5488,5490,5492,5494,5496,5498,5500,5502,5504,5506,5508],{"class":45,"line":2031},[43,5485,4704],{"class":49},[43,5487,5113],{"class":57},[43,5489,4691],{"class":49},[43,5491,5118],{"class":74},[43,5493,104],{"class":49},[43,5495,4959],{"class":74},[43,5497,104],{"class":49},[43,5499,5127],{"class":74},[43,5501,2408],{"class":49},[43,5503,113],{"class":67},[43,5505,5134],{"class":49},[43,5507,1172],{"class":67},[43,5509,5139],{"class":49},[43,5511,5512],{"class":45,"line":2037},[43,5513,1160],{"class":49},[26,5515],{},[29,5517,5519],{"id":5518},"마무리","🔚 마무리",[15,5521,5522],{},"이 방식은 매우 간단하지만 강력하다.",[185,5524,5525,5531],{},[188,5526,5527,5530],{},[21,5528,5529],{},"CSV에 쉼표 포함된 문자열","이 있어도 안전하게 처리 가능",[188,5532,5533],{},[21,5534,5535],{},"추가 라이브러리 없이 순수 JavaScript로 구현 가능",[26,5537],{},[29,5539,5541],{"id":5540},"팁-더-복잡한-csv라면","📌 팁: 더 복잡한 CSV라면?",[185,5543,5544,5559],{},[188,5545,5546,104,5549,5552,5553,5558],{},[40,5547,5548],{},"\\n",[40,5550,5551],{},"\\r",", 셀 안의 줄바꿈 등 복잡한 CSV는 ",[304,5554,5557],{"href":5555,"rel":5556},"https:\u002F\u002Fwww.papaparse.com\u002F",[308],"PapaParse"," 같은 라이브러리를 쓰는 것도 고려하자.",[188,5560,5561],{},"대용량일 땐 성능상 reduce보다 전통적인 for 루프도 고려해볼 것.",[1273,5563,5565],{"id":5564},"추가된-섹션-줄바꿈-호환성-처리","📦 추가된 섹션: 줄바꿈 호환성 처리",[29,5567,5569],{"id":5568},"줄바꿈-호환성-처리","🪓 줄바꿈 호환성 처리",[15,5571,5572],{},"지금까지는 다음과 같이 줄을 나눴다:",[33,5574,5575],{"className":1024,"code":4583,"language":1026,"meta":38,"style":38},[40,5576,5577],{"__ignoreMap":38},[43,5578,5579,5581,5583,5585,5587,5589,5591,5593,5595,5597],{"class":45,"line":46},[43,5580,68],{"class":67},[43,5582,4592],{"class":81},[43,5584,128],{"class":67},[43,5586,4597],{"class":49},[43,5588,4600],{"class":57},[43,5590,97],{"class":49},[43,5592,4605],{"class":100},[43,5594,4549],{"class":81},[43,5596,4605],{"class":100},[43,5598,1160],{"class":49},[15,5600,5601,5602,5604,5605,5607,5608,5610,5611,1139],{},"이 방식은 Windows에서 만든 CSV 파일(",[40,5603,4549],{},")에는 잘 작동하지만,\nmacOS나 Linux에서 만든 파일은 줄바꿈 문자가 ",[40,5606,5548],{}," 또는 ",[40,5609,5551],{},"로만 되어 있을 수 있어서 ",[21,5612,5613],{},"줄이 제대로 분리되지 않을 수 있다",[26,5615],{},[1273,5617,5619],{"id":5618},"해결-방법-1-모든-os-호환-정규식-사용","✅ 해결 방법 1: 모든 OS 호환 정규식 사용",[33,5621,5623],{"className":1024,"code":5622,"language":1026,"meta":38,"style":38},"const items = raw.split(\u002F\\r\\n|\\n|\\r\u002F);\n",[40,5624,5625],{"__ignoreMap":38},[43,5626,5627,5629,5631,5633,5635,5637,5639,5641,5643,5646,5648,5650,5652,5654],{"class":45,"line":46},[43,5628,68],{"class":67},[43,5630,4592],{"class":81},[43,5632,128],{"class":67},[43,5634,4597],{"class":49},[43,5636,4600],{"class":57},[43,5638,97],{"class":49},[43,5640,4711],{"class":100},[43,5642,4549],{"class":81},[43,5644,5645],{"class":67},"|",[43,5647,5548],{"class":81},[43,5649,5645],{"class":67},[43,5651,5551],{"class":81},[43,5653,4711],{"class":100},[43,5655,1160],{"class":49},[15,5657,5658],{},"이 방식은 다음을 모두 처리할 수 있다:",[678,5660,5661,5674],{},[681,5662,5663],{},[684,5664,5665,5668,5671],{},[687,5666,5667],{},"운영체제",[687,5669,5670],{},"줄바꿈 문자",[687,5672,5673],{},"설명",[703,5675,5676,5688,5707],{},[684,5677,5678,5681,5685],{},[708,5679,5680],{},"Windows",[708,5682,5683],{},[40,5684,4549],{},[708,5686,5687],{},"가장 일반적인 케이스",[684,5689,5690,5693,5699],{},[708,5691,5692],{},"macOS",[708,5694,5695,5607,5697],{},[40,5696,5548],{},[40,5698,5551],{},[708,5700,5701,5702,5704,5705],{},"구버전 mac은 ",[40,5703,5551],{},", 신버전은 ",[40,5706,5548],{},[684,5708,5709,5712,5716],{},[708,5710,5711],{},"Linux",[708,5713,5714],{},[40,5715,5548],{},[708,5717,5718],{},"리눅스 계열 대부분",[26,5720],{},[1273,5722,5724,5725,247],{"id":5723},"해결-방법-2-간결한-정규식-rn","✅ 해결 방법 2: 간결한 정규식 (",[40,5726,5727],{},"\\r?\\n",[15,5729,5730],{},"실제로 대부분의 경우에는 이 간단한 정규식이면 충분하다:",[33,5732,5734],{"className":1024,"code":5733,"language":1026,"meta":38,"style":38},"const items = raw.split(\u002F\\r?\\n\u002F);\n",[40,5735,5736],{"__ignoreMap":38},[43,5737,5738,5740,5742,5744,5746,5748,5750,5752,5754,5757,5759,5761],{"class":45,"line":46},[43,5739,68],{"class":67},[43,5741,4592],{"class":81},[43,5743,128],{"class":67},[43,5745,4597],{"class":49},[43,5747,4600],{"class":57},[43,5749,97],{"class":49},[43,5751,4711],{"class":100},[43,5753,5551],{"class":81},[43,5755,5756],{"class":67},"?",[43,5758,5548],{"class":81},[43,5760,4711],{"class":100},[43,5762,1160],{"class":49},[185,5764,5765,5777],{},[188,5766,5767,377,5769],{},[40,5768,5727],{},[21,5770,5771,5773,5774,5776],{},[40,5772,5548],{}," 앞에 ",[40,5775,5551],{},"이 있을 수도 있고 없을 수도 있다는 의미",[188,5778,5779,5780,5782,5783,5785],{},"Windows의 ",[40,5781,4549],{},", Linux\u002FmacOS의 ",[40,5784,5548],{},"을 모두 처리 가능",[15,5787,5788,5789,5791],{},"단, 구형 macOS의 ",[40,5790,5551],{},"만 줄바꿈으로 쓰인 파일은 잡아내지 못한다.",[26,5793],{},[1273,5795,5796],{"id":464},"✨ 정리",[678,5798,5799,5816],{},[681,5800,5801],{},[684,5802,5803,5806,5809,5812,5814],{},[687,5804,5805],{},"정규식",[687,5807,5808],{},"범위",[687,5810,5811],{},"추천 용도",[687,5813],{},[687,5815],{},[703,5817,5818,5835],{},[684,5819,5820,5825,5828,5831,5833],{},[708,5821,5822],{},[40,5823,5824],{},"\u002F\\r?\\n\u002F",[708,5826,5827],{},"Windows + Linux\u002FmacOS",[708,5829,5830],{},"일반적인 경우에 충분",[708,5832],{},[708,5834],{},[684,5836,5837,5840,5842,5845,5848],{},[708,5838,5839],{},"`\u002F\\r\\n",[708,5841,5548],{},[708,5843,5844],{},"\\r\u002F`",[708,5846,5847],{},"모든 줄바꿈 완전 지원",[708,5849,5850],{},"극한 호환성이 필요한 경우",[26,5852],{},[1273,5854,5856],{"id":5855},"최종-코드에서-적용-예시","🔄 최종 코드에서 적용 예시",[15,5858,5859],{},"기존 코드에서 이 부분만 바꾸면 된다:",[33,5861,5863],{"className":332,"code":5862,"language":334,"meta":38,"style":38},"- const items = e.target.result.split(\"\\r\\n\")\n+ const items = e.target.result.split(\u002F\\r?\\n\u002F)\n",[40,5864,5865,5870],{"__ignoreMap":38},[43,5866,5867],{"class":45,"line":46},[43,5868,5869],{},"- const items = e.target.result.split(\"\\r\\n\")\n",[43,5871,5872],{"class":45,"line":64},[43,5873,5874],{},"+ const items = e.target.result.split(\u002F\\r?\\n\u002F)\n",[15,5876,5877],{},"또는 더 강력하게:",[33,5879,5881],{"className":1024,"code":5880,"language":1026,"meta":38,"style":38},"const items = e.target.result.split(\u002F\\r\\n|\\n|\\r\u002F);\n",[40,5882,5883],{"__ignoreMap":38},[43,5884,5885,5887,5889,5891,5894,5896,5898,5900,5902,5904,5906,5908,5910,5912],{"class":45,"line":46},[43,5886,68],{"class":67},[43,5888,4592],{"class":81},[43,5890,128],{"class":67},[43,5892,5893],{"class":49}," e.target.result.",[43,5895,4600],{"class":57},[43,5897,97],{"class":49},[43,5899,4711],{"class":100},[43,5901,4549],{"class":81},[43,5903,5645],{"class":67},[43,5905,5548],{"class":81},[43,5907,5645],{"class":67},[43,5909,5551],{"class":81},[43,5911,4711],{"class":100},[43,5913,1160],{"class":49},[26,5915],{},[15,5917,5918],{},"줄바꿈이 안 맞아서 split이 안 되면, 그 이후 모든 파싱 로직이 무너질 수 있다.\n실제 운영 코드라면 항상 OS 간 줄바꿈 차이를 고려한 정규식을 쓰는 걸 추천한다.",[471,5920,5921],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":38,"searchDepth":64,"depth":64,"links":5923},[5924,5925,5935,5939,5940,5941,5944],{"id":4489,"depth":64,"text":4490},{"id":4504,"depth":64,"text":4505,"children":5926},[5927,5929,5930,5931,5932,5933],{"id":4508,"depth":119,"text":5928},"✅ 1. e.target.result로부터 CSV 텍스트를 가져오기",{"id":4579,"depth":119,"text":4580},{"id":4669,"depth":119,"text":4670},{"id":4797,"depth":119,"text":4798},{"id":4868,"depth":119,"text":4869},{"id":4927,"depth":119,"text":5934},"✅ 6. 임시 토큰 복원 (##comma## → ,)",{"id":5023,"depth":64,"text":5024,"children":5936},[5937,5938],{"id":5027,"depth":119,"text":5028},{"id":5078,"depth":119,"text":5079},{"id":5206,"depth":64,"text":5207},{"id":5518,"depth":64,"text":5519},{"id":5540,"depth":64,"text":5541,"children":5942},[5943],{"id":5564,"depth":119,"text":5565},{"id":5568,"depth":64,"text":5569,"children":5945},[5946,5947,5949,5950],{"id":5618,"depth":119,"text":5619},{"id":5723,"depth":119,"text":5948},"✅ 해결 방법 2: 간결한 정규식 (\\r?\\n)",{"id":464,"depth":119,"text":5796},{"id":5855,"depth":119,"text":5856},"JavaScript에서 큰따옴표, 줄바꿈, 쉼표 등 복잡한 CSV 데이터를 안전하게 객체로 변환하는 전체 과정 설명",{"date":5953,"field":5954,"tags":5955},"2025.06.19","frontend",[1756],"\u002Fko\u002Fcsv-parser-cleaning",{"title":4447,"description":5951},{"loc":5956},"ko\u002Fcsv-parser-cleaning","BShoxxD7Pv9OYn1yq6Z6chsUw8XL9mXSduZsc6WgrbI",{"id":5962,"title":5963,"body":5964,"description":7248,"extension":484,"meta":7249,"navigation":491,"path":7253,"seo":7254,"sitemap":7255,"stem":7256,"__hash__":7257},"ko\u002Fko\u002Fnuxt-runtimeconfig-appconfig.md","Nuxt 설정 runtimeConfig vs app.config 완전 정복",{"type":8,"value":5965,"toc":7220},[5966,5978,5981,5998,6012,6014,6020,6032,6036,6118,6122,6171,6175,6236,6240,6266,6268,6274,6286,6290,6361,6364,6398,6402,6455,6458,6485,6487,6491,6573,6575,6579,6585,6589,6644,6648,6753,6757,6774,6776,6780,6858,6945,6947,6954,6963,7066,7112,7120,7122,7126,7154,7156,7160,7193,7195,7199,7215,7217],[11,5967,5969,5970,5973,5974,5977],{"id":5968},"nuxt-설정-runtimeconfig-vs-appconfig-완전-정복","Nuxt 설정 ",[40,5971,5972],{},"runtimeConfig"," vs ",[40,5975,5976],{},"app.config"," 완전 정복",[15,5979,5980],{},"Nuxt 3는 유연한 설정 구조를 가지고 있습니다. 프로젝트 전역 설정을 정의할 때 가장 많이 쓰이는 두 가지는 다음과 같습니다:",[185,5982,5983,5991],{},[188,5984,5985,5986,78,5988],{},"✅ ",[40,5987,5972],{},[21,5989,5990],{},"런타임 동적 설정",[188,5992,5985,5993,78,5995],{},[40,5994,5976],{},[21,5996,5997],{},"빌드 타임 정적 설정",[15,5999,6000,6001,6004,6007,6008,6011],{},"이 두 설정은 ",[21,6002,6003],{},"비슷해 보이지만 목적, 생명 주기, 적용 대상이 분명히 다릅니다.",[6005,6006],"br",{},"\n이번 글에서는 이 둘의 차이를 ",[21,6009,6010],{},"개념부터 실전까지"," 정리하고, 현업에서의 활용 예시와 실수를 방지하는 팁까지 총망라합니다.",[26,6013],{},[29,6015,1490,6017,6019],{"id":6016},"_1-runtimeconfig란",[40,6018,5972],{},"란?",[15,6021,6022,391,6024,6027,6028,6031],{},[40,6023,5972],{},[21,6025,6026],{},"Nuxt에서 환경 변수 및 비밀값을 런타임에 안전하게 처리","할 수 있는 방법입니다. 서버 전용 또는 클라이언트 공유 값으로 구분할 수 있으며, ",[40,6029,6030],{},".env"," 파일과 연동해 환경별 설정을 유연하게 처리할 수 있습니다.",[1273,6033,6035],{"id":6034},"기본-설정-예시","🔧 기본 설정 예시",[33,6037,6041],{"className":6038,"code":6039,"language":6040,"meta":38,"style":38},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    public: {\n      apiBase: '\u002Fapi',\n      darkMode: false \u002F\u002F 클라이언트에서도 접근 가능\n    },\n    secretKey: '123abc', \u002F\u002F 서버에서만 접근 가능\n  }\n})\n","ts",[40,6042,6043,6048,6061,6066,6071,6081,6091,6096,6109,6113],{"__ignoreMap":38},[43,6044,6045],{"class":45,"line":46},[43,6046,6047],{"class":2797},"\u002F\u002F nuxt.config.ts\n",[43,6049,6050,6053,6056,6059],{"class":45,"line":64},[43,6051,6052],{"class":67},"export",[43,6054,6055],{"class":67}," default",[43,6057,6058],{"class":57}," defineNuxtConfig",[43,6060,1803],{"class":49},[43,6062,6063],{"class":45,"line":119},[43,6064,6065],{"class":49},"  runtimeConfig: {\n",[43,6067,6068],{"class":45,"line":150},[43,6069,6070],{"class":49},"    public: {\n",[43,6072,6073,6076,6079],{"class":45,"line":159},[43,6074,6075],{"class":49},"      apiBase: ",[43,6077,6078],{"class":100},"'\u002Fapi'",[43,6080,1056],{"class":49},[43,6082,6083,6086,6088],{"class":45,"line":176},[43,6084,6085],{"class":49},"      darkMode: ",[43,6087,1830],{"class":81},[43,6089,6090],{"class":2797}," \u002F\u002F 클라이언트에서도 접근 가능\n",[43,6092,6093],{"class":45,"line":1163},[43,6094,6095],{"class":49},"    },\n",[43,6097,6098,6101,6104,6106],{"class":45,"line":1178},[43,6099,6100],{"class":49},"    secretKey: ",[43,6102,6103],{"class":100},"'123abc'",[43,6105,104],{"class":49},[43,6107,6108],{"class":2797},"\u002F\u002F 서버에서만 접근 가능\n",[43,6110,6111],{"class":45,"line":1184},[43,6112,2028],{"class":49},[43,6114,6115],{"class":45,"line":1190},[43,6116,6117],{"class":49},"})\n",[1273,6119,6121],{"id":6120},"접근-방식","📌 접근 방식",[33,6123,6125],{"className":6038,"code":6124,"language":6040,"meta":38,"style":38},"const config = useRuntimeConfig()\n\nconsole.log(config.public.apiBase) \u002F\u002F ✅ 클라이언트 접근 가능\nconsole.log(config.secretKey)      \u002F\u002F ✅ 서버에서만 접근 가능\n",[40,6126,6127,6142,6146,6159],{"__ignoreMap":38},[43,6128,6129,6131,6134,6136,6139],{"class":45,"line":46},[43,6130,68],{"class":67},[43,6132,6133],{"class":81}," config",[43,6135,128],{"class":67},[43,6137,6138],{"class":57}," useRuntimeConfig",[43,6140,6141],{"class":49},"()\n",[43,6143,6144],{"class":45,"line":64},[43,6145,1782],{"emptyLinePlaceholder":491},[43,6147,6148,6151,6153,6156],{"class":45,"line":119},[43,6149,6150],{"class":49},"console.",[43,6152,1931],{"class":57},[43,6154,6155],{"class":49},"(config.public.apiBase) ",[43,6157,6158],{"class":2797},"\u002F\u002F ✅ 클라이언트 접근 가능\n",[43,6160,6161,6163,6165,6168],{"class":45,"line":150},[43,6162,6150],{"class":49},[43,6164,1931],{"class":57},[43,6166,6167],{"class":49},"(config.secretKey)      ",[43,6169,6170],{"class":2797},"\u002F\u002F ✅ 서버에서만 접근 가능\n",[1273,6172,6174],{"id":6173},"활용-예시-실무-기준","✅ 활용 예시 (실무 기준)",[678,6176,6177,6190],{},[681,6178,6179],{},[684,6180,6181,6184,6187],{},[687,6182,6183],{},"설정값",[687,6185,6186],{},"위치",[687,6188,6189],{},"비고",[703,6191,6192,6205,6218],{},[684,6193,6194,6197,6202],{},[708,6195,6196],{},"API Base URL",[708,6198,6199],{},[40,6200,6201],{},"runtimeConfig.public.apiBase",[708,6203,6204],{},"환경별 달라질 수 있음",[684,6206,6207,6210,6215],{},[708,6208,6209],{},"인증 시크릿 키",[708,6211,6212],{},[40,6213,6214],{},"runtimeConfig.secretKey",[708,6216,6217],{},"서버 전용, 클라이언트 접근 불가",[684,6219,6220,6223,6229],{},[708,6221,6222],{},"외부 서비스 키",[708,6224,6225,6228],{},[40,6226,6227],{},"public.recaptchaKey"," 등",[708,6230,6231,6232,6235],{},"클라이언트 사용 시 반드시 ",[40,6233,6234],{},"public"," 하위로",[1273,6237,6239],{"id":6238},"️-주의사항","⚠️ 주의사항",[185,6241,6242,6250,6260],{},[188,6243,6244,6246,6247,141],{},[40,6245,5972],{},"는 서버 재시작 없이 변경할 수 없습니다 (즉, ",[21,6248,6249],{},"재배포 필요",[188,6251,6252,6255,6256,6259],{},[40,6253,6254],{},"process.env","가 아닌 **",[40,6257,6258],{},"useRuntimeConfig()","**를 꼭 사용해야 합니다.",[188,6261,6262,6263,6265],{},"클라이언트에서 접근할 값은 반드시 ",[40,6264,6234],{}," 하위로 지정해야 합니다.",[26,6267],{},[29,6269,1500,6271,6019],{"id":6270},"_2-appconfigts란",[40,6272,6273],{},"app.config.ts",[15,6275,6276,6278,6279,6282,6283],{},[40,6277,6273],{},"는 Nuxt 3에서 ",[21,6280,6281],{},"정적인 앱 전역 설정","을 정의하기 위한 공식 방법입니다. 이 설정은 ",[21,6284,6285],{},"빌드 시점에 결정되며, 타입 추론이 가능하고 Nuxt 전체에서 공유됩니다.",[1273,6287,6289],{"id":6288},"설정-예시","🔧 설정 예시",[33,6291,6293],{"className":6038,"code":6292,"language":6040,"meta":38,"style":38},"\u002F\u002F app.config.ts\nexport default defineAppConfig({\n  darkMode: true,\n  siteName: 'My Nuxt Blog',\n  layoutMode: 'boxed',\n  theme: {\n    primaryColor: '#1e40af'\n  }\n})\n",[40,6294,6295,6300,6311,6320,6330,6340,6345,6353,6357],{"__ignoreMap":38},[43,6296,6297],{"class":45,"line":46},[43,6298,6299],{"class":2797},"\u002F\u002F app.config.ts\n",[43,6301,6302,6304,6306,6309],{"class":45,"line":64},[43,6303,6052],{"class":67},[43,6305,6055],{"class":67},[43,6307,6308],{"class":57}," defineAppConfig",[43,6310,1803],{"class":49},[43,6312,6313,6316,6318],{"class":45,"line":119},[43,6314,6315],{"class":49},"  darkMode: ",[43,6317,1053],{"class":81},[43,6319,1056],{"class":49},[43,6321,6322,6325,6328],{"class":45,"line":150},[43,6323,6324],{"class":49},"  siteName: ",[43,6326,6327],{"class":100},"'My Nuxt Blog'",[43,6329,1056],{"class":49},[43,6331,6332,6335,6338],{"class":45,"line":159},[43,6333,6334],{"class":49},"  layoutMode: ",[43,6336,6337],{"class":100},"'boxed'",[43,6339,1056],{"class":49},[43,6341,6342],{"class":45,"line":176},[43,6343,6344],{"class":49},"  theme: {\n",[43,6346,6347,6350],{"class":45,"line":1163},[43,6348,6349],{"class":49},"    primaryColor: ",[43,6351,6352],{"class":100},"'#1e40af'\n",[43,6354,6355],{"class":45,"line":1178},[43,6356,2028],{"class":49},[43,6358,6359],{"class":45,"line":1184},[43,6360,6117],{"class":49},[1273,6362,6121],{"id":6363},"접근-방식-1",[33,6365,6367],{"className":6038,"code":6366,"language":6040,"meta":38,"style":38},"const config = useAppConfig()\n\nconsole.log(config.darkMode) \u002F\u002F true\n",[40,6368,6369,6382,6386],{"__ignoreMap":38},[43,6370,6371,6373,6375,6377,6380],{"class":45,"line":46},[43,6372,68],{"class":67},[43,6374,6133],{"class":81},[43,6376,128],{"class":67},[43,6378,6379],{"class":57}," useAppConfig",[43,6381,6141],{"class":49},[43,6383,6384],{"class":45,"line":64},[43,6385,1782],{"emptyLinePlaceholder":491},[43,6387,6388,6390,6392,6395],{"class":45,"line":119},[43,6389,6150],{"class":49},[43,6391,1931],{"class":57},[43,6393,6394],{"class":49},"(config.darkMode) ",[43,6396,6397],{"class":2797},"\u002F\u002F true\n",[1273,6399,6401],{"id":6400},"활용-예시","✅ 활용 예시",[678,6403,6404,6414],{},[681,6405,6406],{},[684,6407,6408,6410,6412],{},[687,6409,6183],{},[687,6411,6186],{},[687,6413,6189],{},[703,6415,6416,6429,6442],{},[684,6417,6418,6421,6426],{},[708,6419,6420],{},"다크모드 여부",[708,6422,6423],{},[40,6424,6425],{},"appConfig.darkMode",[708,6427,6428],{},"전역 테마 설정",[684,6430,6431,6434,6439],{},[708,6432,6433],{},"사이트 이름",[708,6435,6436],{},[40,6437,6438],{},"appConfig.siteName",[708,6440,6441],{},"SEO, Meta 설정 등에서 활용",[684,6443,6444,6447,6452],{},[708,6445,6446],{},"기본 테마 색상",[708,6448,6449],{},[40,6450,6451],{},"appConfig.theme",[708,6453,6454],{},"스타일 시스템 전역 공유",[1273,6456,6239],{"id":6457},"️-주의사항-1",[185,6459,6460,6467,6475],{},[188,6461,6462,6463,6466],{},"설정값 변경 시 ",[21,6464,6465],{},"빌드 다시 해야 반영","됩니다 (정적 설정이기 때문).",[188,6468,6469,6471,6472,6474],{},[40,6470,6030],{}," 값을 가져와 ",[40,6473,6273],{},"에 쓸 수는 없습니다.",[188,6476,6477,6484],{},[21,6478,6479,6480,6483],{},"컴포넌트나 훅 안에서 ",[40,6481,6482],{},"useAppConfig()","로 접근","해야 합니다.",[26,6486],{},[29,6488,6490],{"id":6489},"_3-비교-요약-언제-무엇을-써야-할까","3. 비교 요약: 언제 무엇을 써야 할까?",[678,6492,6493,6508],{},[681,6494,6495],{},[684,6496,6497,6500,6504],{},[687,6498,6499],{},"구분",[687,6501,6502],{},[40,6503,5972],{},[687,6505,6506],{},[40,6507,5976],{},[703,6509,6510,6521,6532,6545,6562],{},[684,6511,6512,6515,6518],{},[708,6513,6514],{},"적용 시점",[708,6516,6517],{},"런타임",[708,6519,6520],{},"빌드 타임",[684,6522,6523,6526,6529],{},[708,6524,6525],{},"변경 후 적용",[708,6527,6528],{},"서버 재시작 필요",[708,6530,6531],{},"빌드 다시 해야 함",[684,6533,6534,6537,6542],{},[708,6535,6536],{},"클라이언트 접근 가능",[708,6538,6539,6541],{},[40,6540,6234],{}," 하위만 가능",[708,6543,6544],{},"전체 접근 가능",[684,6546,6547,6550,6556],{},[708,6548,6549],{},"타입 지원",[708,6551,6552,6553,247],{},"제한적 (",[40,6554,6555],{},"useRuntimeConfig",[708,6557,6558,6559,247],{},"자동 타입 추론 (",[40,6560,6561],{},"useAppConfig",[684,6563,6564,6567,6570],{},[708,6565,6566],{},"주 용도",[708,6568,6569],{},"보안 키, API 주소, 모드 분기",[708,6571,6572],{},"테마, 레이아웃, 전역 옵션",[26,6574],{},[29,6576,6578],{"id":6577},"_4-다크모드-전역-설정-적용-예시","4. 다크모드 전역 설정 적용 예시",[15,6580,6581,6582,6584],{},"다크모드를 전역 설정으로 넣고, 이를 전 컴포넌트에서 반영하려면 ",[40,6583,6273],{},"가 적합합니다.",[1273,6586,6588],{"id":6587},"appconfigts","📄 app.config.ts",[33,6590,6592],{"className":6038,"code":6591,"language":6040,"meta":38,"style":38},"export default defineAppConfig({\n  darkMode: true,\n  theme: {\n    primary: '#1e293b',\n    darkBackground: '#0f172a',\n  }\n})\n",[40,6593,6594,6604,6612,6616,6626,6636,6640],{"__ignoreMap":38},[43,6595,6596,6598,6600,6602],{"class":45,"line":46},[43,6597,6052],{"class":67},[43,6599,6055],{"class":67},[43,6601,6308],{"class":57},[43,6603,1803],{"class":49},[43,6605,6606,6608,6610],{"class":45,"line":64},[43,6607,6315],{"class":49},[43,6609,1053],{"class":81},[43,6611,1056],{"class":49},[43,6613,6614],{"class":45,"line":119},[43,6615,6344],{"class":49},[43,6617,6618,6621,6624],{"class":45,"line":150},[43,6619,6620],{"class":49},"    primary: ",[43,6622,6623],{"class":100},"'#1e293b'",[43,6625,1056],{"class":49},[43,6627,6628,6631,6634],{"class":45,"line":159},[43,6629,6630],{"class":49},"    darkBackground: ",[43,6632,6633],{"class":100},"'#0f172a'",[43,6635,1056],{"class":49},[43,6637,6638],{"class":45,"line":176},[43,6639,2028],{"class":49},[43,6641,6642],{"class":45,"line":1163},[43,6643,6117],{"class":49},[1273,6645,6647],{"id":6646},"tailwindcss와-연동-예시-다크-모드-적용","🎨 TailwindCSS와 연동 예시 (다크 모드 적용)",[33,6649,6651],{"className":35,"code":6650,"language":37,"meta":38,"style":38},"\u003Cscript setup>\nconst config = useAppConfig()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv :class=\"{ 'dark': config.darkMode }\" class=\"bg-white dark:bg-gray-900 text-black dark:text-white\">\n    \u003Ch1>Hello Dark Mode\u003C\u002Fh1>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[40,6652,6653,6663,6675,6683,6687,6696,6722,6736,6745],{"__ignoreMap":38},[43,6654,6655,6657,6659,6661],{"class":45,"line":46},[43,6656,50],{"class":49},[43,6658,54],{"class":53},[43,6660,58],{"class":57},[43,6662,61],{"class":49},[43,6664,6665,6667,6669,6671,6673],{"class":45,"line":64},[43,6666,68],{"class":67},[43,6668,6133],{"class":81},[43,6670,128],{"class":67},[43,6672,6379],{"class":57},[43,6674,6141],{"class":49},[43,6676,6677,6679,6681],{"class":45,"line":119},[43,6678,179],{"class":49},[43,6680,54],{"class":53},[43,6682,61],{"class":49},[43,6684,6685],{"class":45,"line":150},[43,6686,1782],{"emptyLinePlaceholder":491},[43,6688,6689,6691,6694],{"class":45,"line":159},[43,6690,50],{"class":49},[43,6692,6693],{"class":53},"template",[43,6695,61],{"class":49},[43,6697,6698,6701,6704,6707,6709,6712,6715,6717,6720],{"class":45,"line":176},[43,6699,6700],{"class":49},"  \u003C",[43,6702,6703],{"class":53},"div",[43,6705,6706],{"class":57}," :class",[43,6708,88],{"class":49},[43,6710,6711],{"class":100},"\"{ 'dark': config.darkMode }\"",[43,6713,6714],{"class":57}," class",[43,6716,88],{"class":49},[43,6718,6719],{"class":100},"\"bg-white dark:bg-gray-900 text-black dark:text-white\"",[43,6721,61],{"class":49},[43,6723,6724,6727,6729,6732,6734],{"class":45,"line":1163},[43,6725,6726],{"class":49},"    \u003C",[43,6728,11],{"class":53},[43,6730,6731],{"class":49},">Hello Dark Mode\u003C\u002F",[43,6733,11],{"class":53},[43,6735,61],{"class":49},[43,6737,6738,6741,6743],{"class":45,"line":1178},[43,6739,6740],{"class":49},"  \u003C\u002F",[43,6742,6703],{"class":53},[43,6744,61],{"class":49},[43,6746,6747,6749,6751],{"class":45,"line":1184},[43,6748,179],{"class":49},[43,6750,6693],{"class":53},[43,6752,61],{"class":49},[1273,6754,6756],{"id":6755},"추가-실전-팁","✅ 추가 실전 팁",[185,6758,6759,6766],{},[188,6760,6761,6762,6765],{},"Tailwind에서는 ",[40,6763,6764],{},"darkMode: 'class'","로 설정해야 위 예시가 정상 작동합니다.",[188,6767,6768,6770,6771,1139],{},[40,6769,6273],{},"를 활용하면 레이아웃, 색상 시스템, 버튼 스타일 등을 한 곳에서 컨트롤할 수 있어 ",[21,6772,6773],{},"디자인 시스템과 궁합이 좋습니다",[26,6775],{},[29,6777,6779],{"id":6778},"_5-실무-기준으로-구성된-설정-예시","5. 실무 기준으로 구성된 설정 예시",[33,6781,6783],{"className":6038,"code":6782,"language":6040,"meta":38,"style":38},"\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    public: {\n      apiBase: process.env.NUXT_PUBLIC_API_BASE || '\u002Fapi',\n      version: process.env.NUXT_PUBLIC_APP_VERSION || '1.0.0',\n    },\n    secretKey: process.env.NUXT_SECRET_KEY\n  }\n})\n",[40,6784,6785,6789,6799,6803,6807,6823,6838,6842,6850,6854],{"__ignoreMap":38},[43,6786,6787],{"class":45,"line":46},[43,6788,6047],{"class":2797},[43,6790,6791,6793,6795,6797],{"class":45,"line":64},[43,6792,6052],{"class":67},[43,6794,6055],{"class":67},[43,6796,6058],{"class":57},[43,6798,1803],{"class":49},[43,6800,6801],{"class":45,"line":119},[43,6802,6065],{"class":49},[43,6804,6805],{"class":45,"line":150},[43,6806,6070],{"class":49},[43,6808,6809,6812,6815,6818,6821],{"class":45,"line":159},[43,6810,6811],{"class":49},"      apiBase: process.env.",[43,6813,6814],{"class":81},"NUXT_PUBLIC_API_BASE",[43,6816,6817],{"class":67}," ||",[43,6819,6820],{"class":100}," '\u002Fapi'",[43,6822,1056],{"class":49},[43,6824,6825,6828,6831,6833,6836],{"class":45,"line":176},[43,6826,6827],{"class":49},"      version: process.env.",[43,6829,6830],{"class":81},"NUXT_PUBLIC_APP_VERSION",[43,6832,6817],{"class":67},[43,6834,6835],{"class":100}," '1.0.0'",[43,6837,1056],{"class":49},[43,6839,6840],{"class":45,"line":1163},[43,6841,6095],{"class":49},[43,6843,6844,6847],{"class":45,"line":1178},[43,6845,6846],{"class":49},"    secretKey: process.env.",[43,6848,6849],{"class":81},"NUXT_SECRET_KEY\n",[43,6851,6852],{"class":45,"line":1184},[43,6853,2028],{"class":49},[43,6855,6856],{"class":45,"line":1190},[43,6857,6117],{"class":49},[33,6859,6861],{"className":6038,"code":6860,"language":6040,"meta":38,"style":38},"\u002F\u002F app.config.ts\nexport default defineAppConfig({\n  darkMode: true,\n  language: 'ko',\n  ui: {\n    layout: 'sidebar',\n    theme: {\n      color: '#06b6d4',\n      fontFamily: 'Inter, sans-serif'\n    }\n  }\n})\n",[40,6862,6863,6867,6877,6885,6895,6900,6910,6915,6925,6933,6937,6941],{"__ignoreMap":38},[43,6864,6865],{"class":45,"line":46},[43,6866,6299],{"class":2797},[43,6868,6869,6871,6873,6875],{"class":45,"line":64},[43,6870,6052],{"class":67},[43,6872,6055],{"class":67},[43,6874,6308],{"class":57},[43,6876,1803],{"class":49},[43,6878,6879,6881,6883],{"class":45,"line":119},[43,6880,6315],{"class":49},[43,6882,1053],{"class":81},[43,6884,1056],{"class":49},[43,6886,6887,6890,6893],{"class":45,"line":150},[43,6888,6889],{"class":49},"  language: ",[43,6891,6892],{"class":100},"'ko'",[43,6894,1056],{"class":49},[43,6896,6897],{"class":45,"line":159},[43,6898,6899],{"class":49},"  ui: {\n",[43,6901,6902,6905,6908],{"class":45,"line":176},[43,6903,6904],{"class":49},"    layout: ",[43,6906,6907],{"class":100},"'sidebar'",[43,6909,1056],{"class":49},[43,6911,6912],{"class":45,"line":1163},[43,6913,6914],{"class":49},"    theme: {\n",[43,6916,6917,6920,6923],{"class":45,"line":1178},[43,6918,6919],{"class":49},"      color: ",[43,6921,6922],{"class":100},"'#06b6d4'",[43,6924,1056],{"class":49},[43,6926,6927,6930],{"class":45,"line":1184},[43,6928,6929],{"class":49},"      fontFamily: ",[43,6931,6932],{"class":100},"'Inter, sans-serif'\n",[43,6934,6935],{"class":45,"line":1190},[43,6936,1993],{"class":49},[43,6938,6939],{"class":45,"line":1996},[43,6940,2028],{"class":49},[43,6942,6943],{"class":45,"line":2008},[43,6944,6117],{"class":49},[26,6946],{},[29,6948,6950,6951,6953],{"id":6949},"_6-고급-활용-appconfig-타입-확장하기","6. 고급 활용: ",[40,6952,5976],{}," 타입 확장하기",[15,6955,6956,6957,6959,6960],{},"Nuxt는 ",[40,6958,5976],{},"에 자동으로 타입을 추론하지만, ",[21,6961,6962],{},"직접 타입 선언을 확장할 수 있습니다.",[33,6964,6966],{"className":6038,"code":6965,"language":6040,"meta":38,"style":38},"\u002F\u002F types\u002Fapp.config.d.ts\ndeclare module 'nuxt\u002Fschema' {\n  interface AppConfigInput {\n    ui?: {\n      layout?: 'full' | 'sidebar'\n      theme?: {\n        color?: string\n        fontFamily?: string\n      }\n    }\n  }\n}\n",[40,6967,6968,6973,6986,6996,7006,7021,7030,7040,7049,7054,7058,7062],{"__ignoreMap":38},[43,6969,6970],{"class":45,"line":46},[43,6971,6972],{"class":2797},"\u002F\u002F types\u002Fapp.config.d.ts\n",[43,6974,6975,6978,6981,6984],{"class":45,"line":64},[43,6976,6977],{"class":67},"declare",[43,6979,6980],{"class":67}," module",[43,6982,6983],{"class":100}," 'nuxt\u002Fschema'",[43,6985,116],{"class":49},[43,6987,6988,6991,6994],{"class":45,"line":119},[43,6989,6990],{"class":67},"  interface",[43,6992,6993],{"class":57}," AppConfigInput",[43,6995,116],{"class":49},[43,6997,6998,7001,7004],{"class":45,"line":150},[43,6999,7000],{"class":74},"    ui",[43,7002,7003],{"class":67},"?:",[43,7005,116],{"class":49},[43,7007,7008,7011,7013,7016,7018],{"class":45,"line":159},[43,7009,7010],{"class":74},"      layout",[43,7012,7003],{"class":67},[43,7014,7015],{"class":100}," 'full'",[43,7017,1376],{"class":67},[43,7019,7020],{"class":100}," 'sidebar'\n",[43,7022,7023,7026,7028],{"class":45,"line":176},[43,7024,7025],{"class":74},"      theme",[43,7027,7003],{"class":67},[43,7029,116],{"class":49},[43,7031,7032,7035,7037],{"class":45,"line":1163},[43,7033,7034],{"class":74},"        color",[43,7036,7003],{"class":67},[43,7038,7039],{"class":81}," string\n",[43,7041,7042,7045,7047],{"class":45,"line":1178},[43,7043,7044],{"class":74},"        fontFamily",[43,7046,7003],{"class":67},[43,7048,7039],{"class":81},[43,7050,7051],{"class":45,"line":1184},[43,7052,7053],{"class":49},"      }\n",[43,7055,7056],{"class":45,"line":1190},[43,7057,1993],{"class":49},[43,7059,7060],{"class":45,"line":1996},[43,7061,2028],{"class":49},[43,7063,7064],{"class":45,"line":2008},[43,7065,2034],{"class":49},[33,7067,7069],{"className":6038,"code":7068,"language":6040,"meta":38,"style":38},"\u002F\u002F nuxt.config.ts\nimport { defineNuxtConfig } from 'nuxt\u002Fconfig'\n\nexport default defineNuxtConfig({\n  \u002F\u002F config...\n})\n",[40,7070,7071,7075,7089,7093,7103,7108],{"__ignoreMap":38},[43,7072,7073],{"class":45,"line":46},[43,7074,6047],{"class":2797},[43,7076,7077,7080,7083,7086],{"class":45,"line":64},[43,7078,7079],{"class":67},"import",[43,7081,7082],{"class":49}," { defineNuxtConfig } ",[43,7084,7085],{"class":67},"from",[43,7087,7088],{"class":100}," 'nuxt\u002Fconfig'\n",[43,7090,7091],{"class":45,"line":119},[43,7092,1782],{"emptyLinePlaceholder":491},[43,7094,7095,7097,7099,7101],{"class":45,"line":150},[43,7096,6052],{"class":67},[43,7098,6055],{"class":67},[43,7100,6058],{"class":57},[43,7102,1803],{"class":49},[43,7104,7105],{"class":45,"line":159},[43,7106,7107],{"class":2797},"  \u002F\u002F config...\n",[43,7109,7110],{"class":45,"line":176},[43,7111,6117],{"class":49},[507,7113,7114],{},[15,7115,7116,7117,7119],{},"이렇게 하면 VS Code에서 ",[40,7118,6273],{},"의 값도 완전하게 타입 추론됩니다.",[26,7121],{},[29,7123,7125],{"id":7124},"마무리-정리","✅ 마무리 정리",[185,7127,7128,7133,7142],{},[188,7129,7130,7132],{},[40,7131,5972],{},": 외부 API 주소, 보안 키, 환경 구분 설정 등에 사용",[188,7134,7135,7137,7138,7141],{},[40,7136,5976],{},": UI 테마, 전역 설정, 디자인 시스템 구성 등 ",[21,7139,7140],{},"고정된 설정값","에 사용",[188,7143,7144,377,7147,104,7149,377,7152],{},[21,7145,7146],{},"유동적인 값",[40,7148,5972],{},[21,7150,7151],{},"고정 전역 값",[40,7153,5976],{},[26,7155],{},[29,7157,7159],{"id":7158},"실무에서의-설정-전략-팁","💡 실무에서의 설정 전략 팁",[432,7161,7162,7170,7181,7188],{},[188,7163,7164],{},[21,7165,7166,7167,7169],{},"다크모드, 언어, 테마, 레이아웃 등은 ",[40,7168,5976],{},"에 넣어야 유지보수가 편함",[188,7171,7172],{},[21,7173,7174,7175,7177,7178,7180],{},"외부와 연결되는 값은 무조건 ",[40,7176,5972],{},"에 정의하고 ",[40,7179,6030],{},"에서 관리",[188,7182,7183],{},[21,7184,7185,7187],{},[40,7186,6273],{},"는 전역 설정만. 로컬 페이지\u002F컴포넌트 전용 값은 피함",[188,7189,7190],{},[21,7191,7192],{},"모든 설정은 타입 기반으로 관리 → DX, 자동 완성, 오류 방지에 큰 도움",[26,7194],{},[29,7196,7198],{"id":7197},"참고-문서","🔚 참고 문서",[185,7200,7201,7208],{},[188,7202,7203],{},[304,7204,7207],{"href":7205,"rel":7206},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fapi\u002Fnuxt-config#runtimeconfig",[308],"Nuxt 3 공식 문서 – Runtime Config",[188,7209,7210],{},[304,7211,7214],{"href":7212,"rel":7213},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fguide\u002Fdirectory-structure\u002Fapp-config",[308],"Nuxt 3 공식 문서 – App Config",[26,7216],{},[471,7218,7219],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":7221},[7222,7229,7236,7237,7242,7243,7245,7246,7247],{"id":6016,"depth":64,"text":7223,"children":7224},"1. runtimeConfig란?",[7225,7226,7227,7228],{"id":6034,"depth":119,"text":6035},{"id":6120,"depth":119,"text":6121},{"id":6173,"depth":119,"text":6174},{"id":6238,"depth":119,"text":6239},{"id":6270,"depth":64,"text":7230,"children":7231},"2. app.config.ts란?",[7232,7233,7234,7235],{"id":6288,"depth":119,"text":6289},{"id":6363,"depth":119,"text":6121},{"id":6400,"depth":119,"text":6401},{"id":6457,"depth":119,"text":6239},{"id":6489,"depth":64,"text":6490},{"id":6577,"depth":64,"text":6578,"children":7238},[7239,7240,7241],{"id":6587,"depth":119,"text":6588},{"id":6646,"depth":119,"text":6647},{"id":6755,"depth":119,"text":6756},{"id":6778,"depth":64,"text":6779},{"id":6949,"depth":64,"text":7244},"6. 고급 활용: app.config 타입 확장하기",{"id":7124,"depth":64,"text":7125},{"id":7158,"depth":64,"text":7159},{"id":7197,"depth":64,"text":7198},"Nuxt3에서 전역 설정을 어떻게 관리하고, 어떤 기준으로 나누어야 할까? 다크모드 예시부터 실무 기준 설정 전략까지 총정리",{"date":7250,"field":5954,"tags":7251},"2025.06.16",[7252],"nuxt","\u002Fko\u002Fnuxt-runtimeconfig-appconfig",{"title":5963,"description":7248},{"loc":7253},"ko\u002Fnuxt-runtimeconfig-appconfig","mHqWBKO9O4gPimE3Vuuj7_eat0qh_4aVvc2nBpomQtc",{"id":7259,"title":7260,"body":7261,"description":8383,"extension":484,"meta":8384,"navigation":491,"path":8388,"seo":8389,"sitemap":8390,"stem":8391,"__hash__":8392},"ko\u002Fko\u002Fvue-query-ref-value-trap-nuxt3.md","Vue Query에서 데이터가 안 보이는 이유? Nuxt 3와 Ref 구조의 함정",{"type":8,"value":7262,"toc":8371},[7263,7266,7285,7292,7294,7298,7415,7422,7480,7487,7489,7497,7508,7567,7579,7581,7585,7596,7606,7655,7666,7668,7676,7682,7764,7766,7770,7780,7928,8086,8088,8092,8099,8304,8306,8310,8323,8332,8334,8338,8368],[11,7264,7260],{"id":7265},"vue-query에서-데이터가-안-보이는-이유-nuxt-3와-ref-구조의-함정",[15,7267,7268,7269,7272,7273,7276,7277,7284],{},"최근 Nuxt 3 프로젝트에서 ",[40,7270,7271],{},"@tanstack\u002Fvue-query"," (이하 ",[40,7274,7275],{},"vue-query",")를 이용해 API 데이터를 받아오는 기능을 구현하면서 흥미로운 문제를 겪었습니다. ",[21,7278,7279,7280,7283],{},"데이터는 ",[40,7281,7282],{},"console.log","에 찍히지만 화면에는 아무것도 보이지 않는 현상","이 발생한 것입니다.",[15,7286,7287,7288,7291],{},"이번 글에서는 이 문제의 원인과 해결 과정을 공유하며, Vue 3의 반응형 시스템과 ",[40,7289,7290],{},"ref","의 동작 방식에 대해 깊이 있게 다뤄보겠습니다.",[26,7293],{},[29,7295,7297],{"id":7296},"문제-상황-api-데이터는-있는데-화면에는-안-보여요","문제 상황: API 데이터는 있는데, 화면에는 안 보여요",[33,7299,7301],{"className":35,"code":7300,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cdiv v-if=\"postsQuery.data && postsQuery.data.length > 0\">\n    \u003Cul>\n      \u003Cli v-for=\"post in postsQuery.data\" :key=\"post.id\">\n        {{ post.title }}\n      \u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fdiv>\n  \u003Cp v-else>데이터가 없습니다.\u003C\u002Fp>\n\u003C\u002Ftemplate>\n",[40,7302,7303,7311,7327,7335,7360,7365,7374,7383,7391,7407],{"__ignoreMap":38},[43,7304,7305,7307,7309],{"class":45,"line":46},[43,7306,50],{"class":49},[43,7308,6693],{"class":53},[43,7310,61],{"class":49},[43,7312,7313,7315,7317,7320,7322,7325],{"class":45,"line":64},[43,7314,6700],{"class":49},[43,7316,6703],{"class":53},[43,7318,7319],{"class":57}," v-if",[43,7321,88],{"class":49},[43,7323,7324],{"class":100},"\"postsQuery.data && postsQuery.data.length > 0\"",[43,7326,61],{"class":49},[43,7328,7329,7331,7333],{"class":45,"line":119},[43,7330,6726],{"class":49},[43,7332,185],{"class":53},[43,7334,61],{"class":49},[43,7336,7337,7340,7342,7345,7347,7350,7353,7355,7358],{"class":45,"line":150},[43,7338,7339],{"class":49},"      \u003C",[43,7341,188],{"class":53},[43,7343,7344],{"class":57}," v-for",[43,7346,88],{"class":49},[43,7348,7349],{"class":100},"\"post in postsQuery.data\"",[43,7351,7352],{"class":57}," :key",[43,7354,88],{"class":49},[43,7356,7357],{"class":100},"\"post.id\"",[43,7359,61],{"class":49},[43,7361,7362],{"class":45,"line":159},[43,7363,7364],{"class":49},"        {{ post.title }}\n",[43,7366,7367,7370,7372],{"class":45,"line":176},[43,7368,7369],{"class":49},"      \u003C\u002F",[43,7371,188],{"class":53},[43,7373,61],{"class":49},[43,7375,7376,7379,7381],{"class":45,"line":1163},[43,7377,7378],{"class":49},"    \u003C\u002F",[43,7380,185],{"class":53},[43,7382,61],{"class":49},[43,7384,7385,7387,7389],{"class":45,"line":1178},[43,7386,6740],{"class":49},[43,7388,6703],{"class":53},[43,7390,61],{"class":49},[43,7392,7393,7395,7397,7400,7403,7405],{"class":45,"line":1184},[43,7394,6700],{"class":49},[43,7396,15],{"class":53},[43,7398,7399],{"class":57}," v-else",[43,7401,7402],{"class":49},">데이터가 없습니다.\u003C\u002F",[43,7404,15],{"class":53},[43,7406,61],{"class":49},[43,7408,7409,7411,7413],{"class":45,"line":1190},[43,7410,179],{"class":49},[43,7412,6693],{"class":53},[43,7414,61],{"class":49},[15,7416,7417,7418,7421],{},"위 코드는 일반적으로 문제가 없어 보입니다. ",[40,7419,7420],{},"console.log(postsQuery.data)","를 찍어보면 아래처럼 데이터도 잘 도착합니다.",[33,7423,7425],{"className":556,"code":7424,"language":558,"meta":38,"style":38},"[\n  { \"id\": 1, \"title\": \"Hello Vue Query\" },\n  { \"id\": 2, \"title\": \"Nuxt 3 is Awesome\" }\n]\n",[40,7426,7427,7431,7455,7476],{"__ignoreMap":38},[43,7428,7429],{"class":45,"line":46},[43,7430,4624],{"class":49},[43,7432,7433,7436,7439,7441,7443,7445,7448,7450,7453],{"class":45,"line":64},[43,7434,7435],{"class":49},"  { ",[43,7437,7438],{"class":81},"\"id\"",[43,7440,78],{"class":49},[43,7442,905],{"class":81},[43,7444,104],{"class":49},[43,7446,7447],{"class":81},"\"title\"",[43,7449,78],{"class":49},[43,7451,7452],{"class":100},"\"Hello Vue Query\"",[43,7454,5178],{"class":49},[43,7456,7457,7459,7461,7463,7465,7467,7469,7471,7474],{"class":45,"line":119},[43,7458,7435],{"class":49},[43,7460,7438],{"class":81},[43,7462,78],{"class":49},[43,7464,918],{"class":81},[43,7466,104],{"class":49},[43,7468,7447],{"class":81},[43,7470,78],{"class":49},[43,7472,7473],{"class":100},"\"Nuxt 3 is Awesome\"",[43,7475,606],{"class":49},[43,7477,7478],{"class":45,"line":150},[43,7479,4664],{"class":49},[15,7481,7482,7483,7486],{},"하지만 실제 화면에서는 ",[21,7484,7485],{},"\"데이터가 없습니다.\""," 라는 문구만 출력되었습니다. 왜일까요?",[26,7488],{},[29,7490,7492,7493,7496],{"id":7491},"원인-분석-usequery는-ref를-반환한다","원인 분석: ",[40,7494,7495],{},"useQuery()","는 ref를 반환한다",[15,7498,7499,7501,7502,7504,7505,7507],{},[40,7500,7275],{},"의 ",[40,7503,7495],{},"는 Vue의 반응형 시스템을 기반으로 동작하며, 내부적으로 반환하는 값들은 모두 ",[40,7506,7290],{},"입니다. 대표적으로 다음과 같은 속성들이 있습니다:",[678,7509,7510,7522],{},[681,7511,7512],{},[684,7513,7514,7517,7519],{},[687,7515,7516],{},"속성명",[687,7518,5673],{},[687,7520,7521],{},"타입",[703,7523,7524,7538,7553],{},[684,7525,7526,7530,7533],{},[708,7527,7528],{},[40,7529,75],{},[708,7531,7532],{},"쿼리 결과",[708,7534,7535],{},[40,7536,7537],{},"Ref\u003CT>",[684,7539,7540,7545,7548],{},[708,7541,7542],{},[40,7543,7544],{},"isLoading",[708,7546,7547],{},"로딩 상태",[708,7549,7550],{},[40,7551,7552],{},"Ref\u003Cboolean>",[684,7554,7555,7559,7562],{},[708,7556,7557],{},[40,7558,2014],{},[708,7560,7561],{},"에러 정보",[708,7563,7564],{},[40,7565,7566],{},"Ref\u003CError>",[15,7568,974,7569,7572,7573,7578],{},[40,7570,7571],{},"postsQuery.data","는 실제 배열이 아니라 ",[21,7574,7575],{},[40,7576,7577],{},"Ref\u003CArray>"," 형태입니다.",[26,7580],{},[29,7582,7584],{"id":7583},"문제의-핵심-템플릿에서의-ref-언래핑과-디렉티브의-차이","문제의 핵심: 템플릿에서의 ref 언래핑과 디렉티브의 차이",[15,7586,7587,7588,7591,7592,7595],{},"Vue 3의 템플릿은 일반적으로 ",[21,7589,7590],{},"ref를 자동 언래핑"," 해줍니다. 그래서 ",[40,7593,7594],{},"{{ postsQuery.data }}","처럼 쓸 수 있죠.",[15,7597,7598,7599,104,7602,7605],{},"하지만 ",[40,7600,7601],{},"v-if",[40,7603,7604],{},"v-for"," 같은 디렉티브 내부에서는 자동 언래핑이 적용되지 않습니다. 즉, 다음 코드는 작동하지 않습니다.",[33,7607,7609],{"className":35,"code":7608,"language":37,"meta":38,"style":38},"\u003C!-- 잘못된 예시 -->\n\u003Cdiv v-if=\"postsQuery.data && postsQuery.data.length > 0\"> ... \u003C\u002Fdiv>\n",[40,7610,7611,7616],{"__ignoreMap":38},[43,7612,7613],{"class":45,"line":46},[43,7614,7615],{"class":2797},"\u003C!-- 잘못된 예시 -->\n",[43,7617,7618,7620,7622,7624,7626,7628,7631,7634,7637,7640,7643,7646,7648,7651,7653],{"class":45,"line":64},[43,7619,50],{"class":49},[43,7621,6703],{"class":53},[43,7623,7319],{"class":67},[43,7625,88],{"class":49},[43,7627,4605],{"class":100},[43,7629,7630],{"class":49},"postsQuery.data ",[43,7632,7633],{"class":67},"&&",[43,7635,7636],{"class":49}," postsQuery.data.",[43,7638,7639],{"class":81},"length",[43,7641,7642],{"class":67}," >",[43,7644,7645],{"class":81}," 0",[43,7647,4605],{"class":100},[43,7649,7650],{"class":49},"> ... \u003C\u002F",[43,7652,6703],{"class":53},[43,7654,61],{"class":49},[15,7656,7657,7658,398,7660,7662,7663,7665],{},"이유는 ",[40,7659,7571],{},[40,7661,7290],{},"이기 때문에 ",[40,7664,7639],{},"는 undefined이고, 조건이 false가 되기 때문입니다.",[26,7667],{},[29,7669,7671,7672,7675],{"id":7670},"해결-방법-value를-명시적으로-접근","해결 방법: ",[40,7673,7674],{},".value","를 명시적으로 접근",[15,7677,7678,7679,7681],{},"해결책은 간단합니다. ",[40,7680,7290],{}," 내부의 값을 직접 접근해주면 됩니다:",[33,7683,7685],{"className":35,"code":7684,"language":37,"meta":38,"style":38},"\u003C!-- 올바른 예시 -->\n\u003Cdiv v-if=\"postsQuery.data?.value?.length > 0\">\n  \u003Cul>\n    \u003Cli v-for=\"post in postsQuery.data.value\" :key=\"post.id\">\n      {{ post.title }}\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Fdiv>\n\u003Cp v-else>데이터가 없습니다.\u003C\u002Fp>\n",[40,7686,7687,7692,7717,7722,7727,7732,7737,7742,7750],{"__ignoreMap":38},[43,7688,7689],{"class":45,"line":46},[43,7690,7691],{"class":2797},"\u003C!-- 올바른 예시 -->\n",[43,7693,7694,7696,7698,7700,7702,7704,7707,7709,7711,7713,7715],{"class":45,"line":64},[43,7695,50],{"class":49},[43,7697,6703],{"class":53},[43,7699,7319],{"class":67},[43,7701,88],{"class":49},[43,7703,4605],{"class":100},[43,7705,7706],{"class":49},"postsQuery.data?.value?.",[43,7708,7639],{"class":81},[43,7710,7642],{"class":67},[43,7712,7645],{"class":81},[43,7714,4605],{"class":100},[43,7716,61],{"class":49},[43,7718,7719],{"class":45,"line":119},[43,7720,7721],{"class":49},"  \u003Cul>\n",[43,7723,7724],{"class":45,"line":150},[43,7725,7726],{"class":49},"    \u003Cli v-for=\"post in postsQuery.data.value\" :key=\"post.id\">\n",[43,7728,7729],{"class":45,"line":159},[43,7730,7731],{"class":49},"      {{ post.title }}\n",[43,7733,7734],{"class":45,"line":176},[43,7735,7736],{"class":49},"    \u003C\u002Fli>\n",[43,7738,7739],{"class":45,"line":1163},[43,7740,7741],{"class":49},"  \u003C\u002Ful>\n",[43,7743,7744,7746,7748],{"class":45,"line":1178},[43,7745,179],{"class":49},[43,7747,6703],{"class":53},[43,7749,61],{"class":49},[43,7751,7752,7754,7756,7758,7760,7762],{"class":45,"line":1184},[43,7753,50],{"class":49},[43,7755,15],{"class":53},[43,7757,7399],{"class":67},[43,7759,7402],{"class":49},[43,7761,15],{"class":53},[43,7763,61],{"class":49},[26,7765],{},[29,7767,7769],{"id":7768},"추가-개선-computed를-활용해-템플릿-정리하기","추가 개선: computed를 활용해 템플릿 정리하기",[15,7771,7772,7773,7775,7776,7779],{},"템플릿에서 ",[40,7774,7674],{},"를 계속 반복하는 것은 보기 좋지 않습니다. 이를 ",[40,7777,7778],{},"computed","로 추출하면 더 깔끔하게 관리할 수 있습니다:",[33,7781,7783],{"className":6038,"code":7782,"language":6040,"meta":38,"style":38},"\u003Cscript setup>\nimport { usePosts } from '~\u002Fcomposables\u002FusePosts'\nimport { computed } from 'vue'\n\nconst { fetchPostsQuery } = usePosts()\nconst postsQuery = fetchPostsQuery()\n\nconst posts = computed(() => postsQuery.data.value ?? [])\nconst isLoading = computed(() => postsQuery.isLoading.value)\nconst error = computed(() => postsQuery.error.value)\n\u003C\u002Fscript>\n",[40,7784,7785,7794,7806,7818,7822,7840,7854,7858,7884,7902,7920],{"__ignoreMap":38},[43,7786,7787,7789,7792],{"class":45,"line":46},[43,7788,50],{"class":67},[43,7790,7791],{"class":49},"script setup",[43,7793,61],{"class":67},[43,7795,7796,7798,7801,7803],{"class":45,"line":64},[43,7797,7079],{"class":67},[43,7799,7800],{"class":49}," { usePosts } ",[43,7802,7085],{"class":67},[43,7804,7805],{"class":100}," '~\u002Fcomposables\u002FusePosts'\n",[43,7807,7808,7810,7813,7815],{"class":45,"line":119},[43,7809,7079],{"class":67},[43,7811,7812],{"class":49}," { computed } ",[43,7814,7085],{"class":67},[43,7816,7817],{"class":100}," 'vue'\n",[43,7819,7820],{"class":45,"line":150},[43,7821,1782],{"emptyLinePlaceholder":491},[43,7823,7824,7826,7828,7831,7833,7835,7838],{"class":45,"line":159},[43,7825,68],{"class":67},[43,7827,71],{"class":49},[43,7829,7830],{"class":81},"fetchPostsQuery",[43,7832,85],{"class":49},[43,7834,88],{"class":67},[43,7836,7837],{"class":57}," usePosts",[43,7839,6141],{"class":49},[43,7841,7842,7844,7847,7849,7852],{"class":45,"line":176},[43,7843,68],{"class":67},[43,7845,7846],{"class":81}," postsQuery",[43,7848,128],{"class":67},[43,7850,7851],{"class":57}," fetchPostsQuery",[43,7853,6141],{"class":49},[43,7855,7856],{"class":45,"line":1163},[43,7857,1782],{"emptyLinePlaceholder":491},[43,7859,7860,7862,7865,7867,7870,7873,7875,7878,7881],{"class":45,"line":1178},[43,7861,68],{"class":67},[43,7863,7864],{"class":81}," posts",[43,7866,128],{"class":67},[43,7868,7869],{"class":57}," computed",[43,7871,7872],{"class":49},"(() ",[43,7874,113],{"class":67},[43,7876,7877],{"class":49}," postsQuery.data.value ",[43,7879,7880],{"class":67},"??",[43,7882,7883],{"class":49}," [])\n",[43,7885,7886,7888,7891,7893,7895,7897,7899],{"class":45,"line":1184},[43,7887,68],{"class":67},[43,7889,7890],{"class":81}," isLoading",[43,7892,128],{"class":67},[43,7894,7869],{"class":57},[43,7896,7872],{"class":49},[43,7898,113],{"class":67},[43,7900,7901],{"class":49}," postsQuery.isLoading.value)\n",[43,7903,7904,7906,7909,7911,7913,7915,7917],{"class":45,"line":1190},[43,7905,68],{"class":67},[43,7907,7908],{"class":81}," error",[43,7910,128],{"class":67},[43,7912,7869],{"class":57},[43,7914,7872],{"class":49},[43,7916,113],{"class":67},[43,7918,7919],{"class":49}," postsQuery.error.value)\n",[43,7921,7922,7924,7926],{"class":45,"line":1996},[43,7923,179],{"class":67},[43,7925,54],{"class":49},[43,7927,61],{"class":67},[33,7929,7931],{"className":35,"code":7930,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp v-if=\"isLoading\">로딩 중...\u003C\u002Fp>\n    \u003Cp v-else-if=\"error\">에러 발생: {{ error.message }}\u003C\u002Fp>\n    \u003Cdiv v-else-if=\"posts.length > 0\">\n      \u003Cul>\n        \u003Cli v-for=\"post in posts\" :key=\"post.id\">{{ post.title }}\u003C\u002Fli>\n      \u003C\u002Ful>\n    \u003C\u002Fdiv>\n    \u003Cp v-else>데이터가 없습니다.\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[40,7932,7933,7941,7949,7969,7990,8005,8013,8040,8048,8056,8070,8078],{"__ignoreMap":38},[43,7934,7935,7937,7939],{"class":45,"line":46},[43,7936,50],{"class":49},[43,7938,6693],{"class":53},[43,7940,61],{"class":49},[43,7942,7943,7945,7947],{"class":45,"line":64},[43,7944,6700],{"class":49},[43,7946,6703],{"class":53},[43,7948,61],{"class":49},[43,7950,7951,7953,7955,7957,7959,7962,7965,7967],{"class":45,"line":119},[43,7952,6726],{"class":49},[43,7954,15],{"class":53},[43,7956,7319],{"class":57},[43,7958,88],{"class":49},[43,7960,7961],{"class":100},"\"isLoading\"",[43,7963,7964],{"class":49},">로딩 중...\u003C\u002F",[43,7966,15],{"class":53},[43,7968,61],{"class":49},[43,7970,7971,7973,7975,7978,7980,7983,7986,7988],{"class":45,"line":150},[43,7972,6726],{"class":49},[43,7974,15],{"class":53},[43,7976,7977],{"class":57}," v-else-if",[43,7979,88],{"class":49},[43,7981,7982],{"class":100},"\"error\"",[43,7984,7985],{"class":49},">에러 발생: {{ error.message }}\u003C\u002F",[43,7987,15],{"class":53},[43,7989,61],{"class":49},[43,7991,7992,7994,7996,7998,8000,8003],{"class":45,"line":159},[43,7993,6726],{"class":49},[43,7995,6703],{"class":53},[43,7997,7977],{"class":57},[43,7999,88],{"class":49},[43,8001,8002],{"class":100},"\"posts.length > 0\"",[43,8004,61],{"class":49},[43,8006,8007,8009,8011],{"class":45,"line":176},[43,8008,7339],{"class":49},[43,8010,185],{"class":53},[43,8012,61],{"class":49},[43,8014,8015,8018,8020,8022,8024,8027,8029,8031,8033,8036,8038],{"class":45,"line":1163},[43,8016,8017],{"class":49},"        \u003C",[43,8019,188],{"class":53},[43,8021,7344],{"class":57},[43,8023,88],{"class":49},[43,8025,8026],{"class":100},"\"post in posts\"",[43,8028,7352],{"class":57},[43,8030,88],{"class":49},[43,8032,7357],{"class":100},[43,8034,8035],{"class":49},">{{ post.title }}\u003C\u002F",[43,8037,188],{"class":53},[43,8039,61],{"class":49},[43,8041,8042,8044,8046],{"class":45,"line":1178},[43,8043,7369],{"class":49},[43,8045,185],{"class":53},[43,8047,61],{"class":49},[43,8049,8050,8052,8054],{"class":45,"line":1184},[43,8051,7378],{"class":49},[43,8053,6703],{"class":53},[43,8055,61],{"class":49},[43,8057,8058,8060,8062,8064,8066,8068],{"class":45,"line":1190},[43,8059,6726],{"class":49},[43,8061,15],{"class":53},[43,8063,7399],{"class":57},[43,8065,7402],{"class":49},[43,8067,15],{"class":53},[43,8069,61],{"class":49},[43,8071,8072,8074,8076],{"class":45,"line":1996},[43,8073,6740],{"class":49},[43,8075,6703],{"class":53},[43,8077,61],{"class":49},[43,8079,8080,8082,8084],{"class":45,"line":2008},[43,8081,179],{"class":49},[43,8083,6693],{"class":53},[43,8085,61],{"class":49},[26,8087],{},[29,8089,8091],{"id":8090},"실전-코드-예시-useposts-및-usequery-구성","실전 코드 예시: usePosts 및 useQuery 구성",[15,8093,8094,8095,8098],{},"아래는 실제 사용한 ",[40,8096,8097],{},"composable"," 예시입니다:",[33,8100,8102],{"className":6038,"code":8101,"language":6040,"meta":38,"style":38},"\u002F\u002F ~\u002Fcomposables\u002FusePosts.ts\nimport { useNuxtApp } from '#app'\n\nexport const usePostApi = () => {\n  const axios = useNuxtApp().$axios\n  return {\n    fetchPosts: async () => {\n      const response = await axios.get('\u002Fposts')\n      return response.data\n    }\n  }\n}\n\nexport const usePosts = () => {\n  const api = usePostApi()\n  const fetchPostsQuery = () => useQuery({\n    queryKey: ['posts'],\n    queryFn: api.fetchPosts\n  })\n  return { fetchPostsQuery }\n}\n",[40,8103,8104,8109,8121,8125,8143,8158,8164,8179,8203,8210,8214,8218,8222,8226,8242,8255,8272,8283,8288,8293,8300],{"__ignoreMap":38},[43,8105,8106],{"class":45,"line":46},[43,8107,8108],{"class":2797},"\u002F\u002F ~\u002Fcomposables\u002FusePosts.ts\n",[43,8110,8111,8113,8116,8118],{"class":45,"line":64},[43,8112,7079],{"class":67},[43,8114,8115],{"class":49}," { useNuxtApp } ",[43,8117,7085],{"class":67},[43,8119,8120],{"class":100}," '#app'\n",[43,8122,8123],{"class":45,"line":119},[43,8124,1782],{"emptyLinePlaceholder":491},[43,8126,8127,8129,8132,8135,8137,8139,8141],{"class":45,"line":150},[43,8128,6052],{"class":67},[43,8130,8131],{"class":67}," const",[43,8133,8134],{"class":57}," usePostApi",[43,8136,128],{"class":67},[43,8138,110],{"class":49},[43,8140,113],{"class":67},[43,8142,116],{"class":49},[43,8144,8145,8147,8150,8152,8155],{"class":45,"line":159},[43,8146,122],{"class":67},[43,8148,8149],{"class":81}," axios",[43,8151,128],{"class":67},[43,8153,8154],{"class":57}," useNuxtApp",[43,8156,8157],{"class":49},"().$axios\n",[43,8159,8160,8162],{"class":45,"line":176},[43,8161,153],{"class":67},[43,8163,116],{"class":49},[43,8165,8166,8169,8171,8173,8175,8177],{"class":45,"line":1163},[43,8167,8168],{"class":57},"    fetchPosts",[43,8170,78],{"class":49},[43,8172,107],{"class":67},[43,8174,110],{"class":49},[43,8176,113],{"class":67},[43,8178,116],{"class":49},[43,8180,8181,8183,8186,8188,8190,8193,8196,8198,8201],{"class":45,"line":1178},[43,8182,1082],{"class":67},[43,8184,8185],{"class":81}," response",[43,8187,128],{"class":67},[43,8189,91],{"class":67},[43,8191,8192],{"class":49}," axios.",[43,8194,8195],{"class":57},"get",[43,8197,97],{"class":49},[43,8199,8200],{"class":100},"'\u002Fposts'",[43,8202,5241],{"class":49},[43,8204,8205,8207],{"class":45,"line":1184},[43,8206,1166],{"class":67},[43,8208,8209],{"class":49}," response.data\n",[43,8211,8212],{"class":45,"line":1190},[43,8213,1993],{"class":49},[43,8215,8216],{"class":45,"line":1996},[43,8217,2028],{"class":49},[43,8219,8220],{"class":45,"line":2008},[43,8221,2034],{"class":49},[43,8223,8224],{"class":45,"line":2025},[43,8225,1782],{"emptyLinePlaceholder":491},[43,8227,8228,8230,8232,8234,8236,8238,8240],{"class":45,"line":2031},[43,8229,6052],{"class":67},[43,8231,8131],{"class":67},[43,8233,7837],{"class":57},[43,8235,128],{"class":67},[43,8237,110],{"class":49},[43,8239,113],{"class":67},[43,8241,116],{"class":49},[43,8243,8244,8246,8249,8251,8253],{"class":45,"line":2037},[43,8245,122],{"class":67},[43,8247,8248],{"class":81}," api",[43,8250,128],{"class":67},[43,8252,8134],{"class":57},[43,8254,6141],{"class":49},[43,8256,8257,8259,8261,8263,8265,8267,8270],{"class":45,"line":2042},[43,8258,122],{"class":67},[43,8260,7851],{"class":57},[43,8262,128],{"class":67},[43,8264,110],{"class":49},[43,8266,113],{"class":67},[43,8268,8269],{"class":57}," useQuery",[43,8271,1803],{"class":49},[43,8273,8274,8277,8280],{"class":45,"line":2516},[43,8275,8276],{"class":49},"    queryKey: [",[43,8278,8279],{"class":100},"'posts'",[43,8281,8282],{"class":49},"],\n",[43,8284,8285],{"class":45,"line":2998},[43,8286,8287],{"class":49},"    queryFn: api.fetchPosts\n",[43,8289,8290],{"class":45,"line":3003},[43,8291,8292],{"class":49},"  })\n",[43,8294,8295,8297],{"class":45,"line":3009},[43,8296,153],{"class":67},[43,8298,8299],{"class":49}," { fetchPostsQuery }\n",[43,8301,8302],{"class":45,"line":3022},[43,8303,2034],{"class":49},[26,8305],{},[29,8307,8309],{"id":8308},"마무리-vue-3의-반응형-시스템을-더-깊이-이해하기","마무리: Vue 3의 반응형 시스템을 더 깊이 이해하기",[15,8311,8312,8313,8315,8316,8318,8319,8322],{},"이번 경험을 통해, ",[40,8314,7275],{},"를 사용할 때 단순히 ",[40,8317,75],{},"만 보는 것이 아니라, 그 ",[21,8320,8321],{},"반환값의 구조가 ref인지 reactive인지","까지도 고려해야 함을 배웠습니다.",[15,8324,8325,8326,8331],{},"Vue 3의 자동 언래핑이 편리하긴 하지만, 디렉티브나 조건문에서는 여전히 ",[21,8327,8328,8330],{},[40,8329,7674],{}," 명시 접근이 필요하다","는 사실을 기억하세요.",[26,8333],{},[29,8335,8337],{"id":8336},"요약","✅ 요약",[185,8339,8340,8348,8363],{},[188,8341,8342,8344,8345,8347],{},[40,8343,7495],{},"는 모든 상태를 ",[40,8346,7290],{},"로 반환한다.",[188,8349,8350,8351,8354,8355,104,8357,8359,8360,8362],{},"템플릿에서는 ",[40,8352,8353],{},"{{ }}"," 안에서 자동 언래핑이 되지만 ",[40,8356,7601],{},[40,8358,7604],{},"에서는 명시적인 ",[40,8361,7674],{}," 접근이 필요하다.",[188,8364,8365,8367],{},[40,8366,7778],{},"로 분리하면 코드가 더 깔끔해진다.",[471,8369,8370],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":38,"searchDepth":64,"depth":64,"links":8372},[8373,8374,8376,8377,8379,8380,8381,8382],{"id":7296,"depth":64,"text":7297},{"id":7491,"depth":64,"text":8375},"원인 분석: useQuery()는 ref를 반환한다",{"id":7583,"depth":64,"text":7584},{"id":7670,"depth":64,"text":8378},"해결 방법: .value를 명시적으로 접근",{"id":7768,"depth":64,"text":7769},{"id":8090,"depth":64,"text":8091},{"id":8308,"depth":64,"text":8309},{"id":8336,"depth":64,"text":8337},"Nuxt 3 + vue-query를 사용할 때 API 데이터가 `v-if` 조건문에서 제대로 표시되지 않는 현상을 해결한 경험을 바탕으로, Ref 구조와 template 디렉티브에서의 차이를 자세히 설명합니다.",{"date":8385,"field":5954,"tags":8386},"2025.06.08",[7252,37,8387],"tanstack-query","\u002Fko\u002Fvue-query-ref-value-trap-nuxt3",{"title":7260,"description":8383},{"loc":8388},"ko\u002Fvue-query-ref-value-trap-nuxt3","QDirUIAKl1WYGcRO7PbVkTmIqHKD3fhvrIS0dSHNXGY",{"id":8394,"title":8395,"body":8396,"description":8856,"extension":484,"meta":8857,"navigation":491,"path":8862,"seo":8863,"sitemap":8864,"stem":8865,"__hash__":8866},"ko\u002Fko\u002Fdeclarative-vs-procedural-programming.md","선언적 프로그래밍 vs 절차형 프로그래밍",{"type":8,"value":8397,"toc":8848},[8398,8402,8407,8410,8415,8520,8525,8529,8534,8537,8542,8690,8695,8699,8776,8780,8783,8794,8797,8805,8809,8811,8822,8824,8832,8836,8839,8842,8845],[29,8399,8401],{"id":8400},"선언적-프로그래밍declarative-programming이란","선언적 프로그래밍(Declarative Programming)이란?",[507,8403,8404],{},[15,8405,8406],{},"무엇(What)을 할 것인가에 집중하는 프로그래밍 방식",[15,8408,8409],{},"선언적 프로그래밍은 어떻게(How) 가 아닌 무엇(What) 을 원하는지를 기술합니다. 즉, \"이것을 해줘\" 라고 선언만 하면 내부적인 로직이나 과정은 신경 쓰지 않아도 됩니다. 대표적인 예로 SQL, HTML, React(JSX), 함수형 프로그래밍 언어 등이 있습니다.",[507,8411,8412],{},[15,8413,8414],{},"예시: JavaScript + React (선언적 방식)",[33,8416,8418],{"className":1024,"code":8417,"language":1026,"meta":38,"style":38},"function App() {\n  return (\n    \u003Cul>\n      {[\"사과\", \"바나나\", \"딸기\"].map((fruit) => (\n        \u003Cli key={fruit}>{fruit}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  );\n}\n\n",[40,8419,8420,8431,8438,8446,8480,8498,8503,8511,8516],{"__ignoreMap":38},[43,8421,8422,8425,8428],{"class":45,"line":46},[43,8423,8424],{"class":67},"function",[43,8426,8427],{"class":57}," App",[43,8429,8430],{"class":49},"() {\n",[43,8432,8433,8435],{"class":45,"line":64},[43,8434,153],{"class":67},[43,8436,8437],{"class":49}," (\n",[43,8439,8440,8442,8444],{"class":45,"line":119},[43,8441,6726],{"class":49},[43,8443,185],{"class":53},[43,8445,61],{"class":49},[43,8447,8448,8451,8454,8456,8459,8461,8464,8467,8469,8471,8474,8476,8478],{"class":45,"line":150},[43,8449,8450],{"class":49},"      {[",[43,8452,8453],{"class":100},"\"사과\"",[43,8455,104],{"class":49},[43,8457,8458],{"class":100},"\"바나나\"",[43,8460,104],{"class":49},[43,8462,8463],{"class":100},"\"딸기\"",[43,8465,8466],{"class":49},"].",[43,8468,1123],{"class":57},[43,8470,4691],{"class":49},[43,8472,8473],{"class":74},"fruit",[43,8475,2408],{"class":49},[43,8477,113],{"class":67},[43,8479,8437],{"class":49},[43,8481,8482,8484,8486,8489,8491,8494,8496],{"class":45,"line":159},[43,8483,8017],{"class":49},[43,8485,188],{"class":53},[43,8487,8488],{"class":57}," key",[43,8490,88],{"class":67},[43,8492,8493],{"class":49},"{fruit}>{fruit}\u003C\u002F",[43,8495,188],{"class":53},[43,8497,61],{"class":49},[43,8499,8500],{"class":45,"line":176},[43,8501,8502],{"class":49},"      ))}\n",[43,8504,8505,8507,8509],{"class":45,"line":1163},[43,8506,7378],{"class":49},[43,8508,185],{"class":53},[43,8510,61],{"class":49},[43,8512,8513],{"class":45,"line":1178},[43,8514,8515],{"class":49},"  );\n",[43,8517,8518],{"class":45,"line":1184},[43,8519,2034],{"class":49},[507,8521,8522],{},[15,8523,8524],{},"위 예제는 \"리스트를 렌더링하라\"는 결과를 선언하고 있으며, DOM을 직접 조작하지 않습니다.",[29,8526,8528],{"id":8527},"절차형-프로그래밍procedural-programming이란","절차형 프로그래밍(Procedural Programming)이란?",[507,8530,8531],{},[15,8532,8533],{},"어떻게(How)를 할 것인가에 집중하는 프로그래밍 방식",[15,8535,8536],{},"절차형 프로그래밍은 명령어들의 순서, 즉 로직의 흐름을 단계적으로 정의합니다. 프로그램은 순차적으로 실행되며, 변수와 상태의 변경을 통해 원하는 결과를 만듭니다. 대표적인 언어로는 C, Python의 일반적인 코드 스타일, JavaScript의 전통적인 사용 방식 등이 있습니다.",[507,8538,8539],{},[15,8540,8541],{},"예시: JavaScript (절차형 방식)",[33,8543,8545],{"className":1024,"code":8544,"language":1026,"meta":38,"style":38},"const fruits = [\"사과\", \"바나나\", \"딸기\"];\nconst ul = document.createElement(\"ul\");\n\nfor (let i = 0; i \u003C fruits.length; i++) {\n  const li = document.createElement(\"li\");\n  li.textContent = fruits[i];\n  ul.appendChild(li);\n}\n\ndocument.body.appendChild(ul);\n\n",[40,8546,8547,8570,8592,8596,8631,8651,8661,8672,8676,8680],{"__ignoreMap":38},[43,8548,8549,8551,8554,8556,8558,8560,8562,8564,8566,8568],{"class":45,"line":46},[43,8550,68],{"class":67},[43,8552,8553],{"class":81}," fruits",[43,8555,128],{"class":67},[43,8557,1169],{"class":49},[43,8559,8453],{"class":100},[43,8561,104],{"class":49},[43,8563,8458],{"class":100},[43,8565,104],{"class":49},[43,8567,8463],{"class":100},[43,8569,5441],{"class":49},[43,8571,8572,8574,8577,8579,8582,8585,8587,8590],{"class":45,"line":64},[43,8573,68],{"class":67},[43,8575,8576],{"class":81}," ul",[43,8578,128],{"class":67},[43,8580,8581],{"class":49}," document.",[43,8583,8584],{"class":57},"createElement",[43,8586,97],{"class":49},[43,8588,8589],{"class":100},"\"ul\"",[43,8591,1160],{"class":49},[43,8593,8594],{"class":45,"line":119},[43,8595,1782],{"emptyLinePlaceholder":491},[43,8597,8598,8601,8603,8606,8609,8611,8613,8616,8618,8621,8623,8626,8629],{"class":45,"line":150},[43,8599,8600],{"class":67},"for",[43,8602,1069],{"class":49},[43,8604,8605],{"class":67},"let",[43,8607,8608],{"class":49}," i ",[43,8610,88],{"class":67},[43,8612,7645],{"class":81},[43,8614,8615],{"class":49},"; i ",[43,8617,50],{"class":67},[43,8619,8620],{"class":49}," fruits.",[43,8622,7639],{"class":81},[43,8624,8625],{"class":49},"; i",[43,8627,8628],{"class":67},"++",[43,8630,1882],{"class":49},[43,8632,8633,8635,8638,8640,8642,8644,8646,8649],{"class":45,"line":159},[43,8634,122],{"class":67},[43,8636,8637],{"class":81}," li",[43,8639,128],{"class":67},[43,8641,8581],{"class":49},[43,8643,8584],{"class":57},[43,8645,97],{"class":49},[43,8647,8648],{"class":100},"\"li\"",[43,8650,1160],{"class":49},[43,8652,8653,8656,8658],{"class":45,"line":176},[43,8654,8655],{"class":49},"  li.textContent ",[43,8657,88],{"class":67},[43,8659,8660],{"class":49}," fruits[i];\n",[43,8662,8663,8666,8669],{"class":45,"line":1163},[43,8664,8665],{"class":49},"  ul.",[43,8667,8668],{"class":57},"appendChild",[43,8670,8671],{"class":49},"(li);\n",[43,8673,8674],{"class":45,"line":1178},[43,8675,2034],{"class":49},[43,8677,8678],{"class":45,"line":1184},[43,8679,1782],{"emptyLinePlaceholder":491},[43,8681,8682,8685,8687],{"class":45,"line":1190},[43,8683,8684],{"class":49},"document.body.",[43,8686,8668],{"class":57},[43,8688,8689],{"class":49},"(ul);\n",[507,8691,8692],{},[15,8693,8694],{},"이 코드는 리스트를 만들기 위해 순서대로 조작할 DOM 요소와 변수를 직접 다룹니다.",[29,8696,8698],{"id":8697},"주요-차이점-비교","주요 차이점 비교",[678,8700,8701,8714],{},[681,8702,8703],{},[684,8704,8705,8708,8711],{},[687,8706,8707],{},"항목",[687,8709,8710],{},"선언적 프로그래밍",[687,8712,8713],{},"절차형 프로그래밍",[703,8715,8716,8732,8743,8754,8765],{},[684,8717,8718,8721,8727],{},[708,8719,8720],{},"관점",[708,8722,8723,8726],{},[21,8724,8725],{},"무엇을"," 할 것인가",[708,8728,8729,8726],{},[21,8730,8731],{},"어떻게",[684,8733,8734,8737,8740],{},[708,8735,8736],{},"초점",[708,8738,8739],{},"결과 중심",[708,8741,8742],{},"단계 중심",[684,8744,8745,8748,8751],{},[708,8746,8747],{},"코드 길이",[708,8749,8750],{},"상대적으로 짧고 명확",[708,8752,8753],{},"길고 상세한 로직 필요",[684,8755,8756,8759,8762],{},[708,8757,8758],{},"유연성",[708,8760,8761],{},"추상화 높음, 재사용 용이",[708,8763,8764],{},"상태 변경과 흐름 제어에 강함",[684,8766,8767,8770,8773],{},[708,8768,8769],{},"예시 언어",[708,8771,8772],{},"SQL, HTML, React, Vue",[708,8774,8775],{},"C, Java, Python, JavaScript",[29,8777,8779],{"id":8778},"선언적-프로그래밍의-장점과-단점","선언적 프로그래밍의 장점과 단점",[15,8781,8782],{},"장점:",[185,8784,8785,8788,8791],{},[188,8786,8787],{},"코드가 간결하고 읽기 쉬움",[188,8789,8790],{},"유지보수가 용이함",[188,8792,8793],{},"버그 발생 가능성 낮음 (상태 변경 최소화)",[15,8795,8796],{},"단점:",[185,8798,8799,8802],{},[188,8800,8801],{},"내부 동작을 제어하기 어려움",[188,8803,8804],{},"낮은 수준의 최적화가 어려움",[29,8806,8808],{"id":8807},"절차형-프로그래밍의-장점과-단점","절차형 프로그래밍의 장점과 단점",[15,8810,8782],{},[185,8812,8813,8816,8819],{},[188,8814,8815],{},"제어 흐름을 세밀하게 설정 가능",[188,8817,8818],{},"낮은 수준에서 최적화 가능",[188,8820,8821],{},"하드웨어와 가까운 작업에 유리",[15,8823,8796],{},[185,8825,8826,8829],{},[188,8827,8828],{},"코드가 장황해지고 버그 발생 가능성 증가",[188,8830,8831],{},"상태가 복잡해지기 쉬움",[29,8833,8835],{"id":8834},"언제-어떤-방식을-선택해야-할까","언제 어떤 방식을 선택해야 할까?",[15,8837,8838],{},"UI 개발, 데이터 선언 등에서는 선언적 방식이 더 적합합니다. (React, Vue 등)",[15,8840,8841],{},"복잡한 알고리즘, 파일 처리, 게임 엔진 등에서는 절차형 방식이 효과적입니다.",[15,8843,8844],{},"하지만 대부분의 실전 개발에서는 두 스타일이 혼합되어 사용된다. 특정 문제를 해결하기 위해 가장 적절한 방식을 유연하게 선택하는 것이 중요하다.",[471,8846,8847],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":38,"searchDepth":64,"depth":64,"links":8849},[8850,8851,8852,8853,8854,8855],{"id":8400,"depth":64,"text":8401},{"id":8527,"depth":64,"text":8528},{"id":8697,"depth":64,"text":8698},{"id":8778,"depth":64,"text":8779},{"id":8807,"depth":64,"text":8808},{"id":8834,"depth":64,"text":8835},"선언적 프로그래밍과 절차형 프로그래밍은 서로 대비되는 대표적인 패러다임이다. 두 패러다임의 차이를 알아보고, 각 패러다임에 해당하는 언어와 Vue\u002FReact의 특징을 살펴보자.",{"date":8858,"field":810,"tags":8859},"2025.05.24",[8860,37,8861],"paradigm","react","\u002Fko\u002Fdeclarative-vs-procedural-programming",{"title":8395,"description":8856},{"loc":8862},"ko\u002Fdeclarative-vs-procedural-programming","iLHbPEA39hm-jQ7dl8eja8VO1a_TOCargOF0nG2kPK4",{"id":8868,"title":8869,"body":8870,"description":10147,"extension":484,"meta":10148,"navigation":491,"path":10151,"seo":10152,"sitemap":10153,"stem":10154,"__hash__":10155},"ko\u002Fko\u002Ftanstack-query-guide.md","TanStack Query란 무엇인가?",{"type":8,"value":8871,"toc":10136},[8872,8875,8880,8887,8896,8899,8907,8915,8919,8922,8937,8945,8951,9051,9057,9061,9069,9438,9442,9658,9664,9806,9809,9848,9850,9854,9860,9990,9993,10120,10122,10133],[15,8873,8874],{},"요즘 프론트엔드 구직시장에 요구사항으로 항상 써져있는 문구가 있다",[15,8876,8877],{},[40,8878,8879],{},"tanstack-query 사용 가능자!!!",[15,8881,8882,8883,8886],{},"tanstack-query는 ",[21,8884,8885],{},"서버 상태를 클라이언트에서 효율적으로 가져오고, 캐싱하고, 동기화하며 관리할 수 있는 도구","이다.",[15,8888,8889],{},[21,8890,8891,8892,8895],{},"한마디로 ",[40,8893,8894],{},"서버 상태 관리","에 특화된 라이브러리!",[15,8897,8898],{},"뭐길래 요래 유명한지 한번 알아보자",[8900,8901,8902],"warning",{},[507,8903,8904],{},[15,8905,8906],{},"TanStack Query (formerly known as React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes fetching, caching, synchronizing and updating server state in your web applications a breeze.",[8908,8909,8910],"tip",{},[507,8911,8912],{},[15,8913,8914],{},"TanStack Query(예전 이름 React Query)는 웹 앱에서 데이터를 가져오는 데 필요한 라이브러리라고 흔히 말하는데, 좀 더 기술적으로 말하면 서버 상태를 가져오고, 캐싱하고, 동기화하고, 업데이트하는 걸 엄청 쉽게 만들어준다.",[29,8916,8918],{"id":8917},"설치-방법","설치 방법",[15,8920,8921],{},"@tanstack\u002Freact-query 를 설치해준다",[33,8923,8925],{"className":850,"code":8924,"language":852,"meta":38,"style":38},"npm i @tanstack\u002Freact-query\n",[40,8926,8927],{"__ignoreMap":38},[43,8928,8929,8931,8934],{"class":45,"line":46},[43,8930,1741],{"class":57},[43,8932,8933],{"class":100}," i",[43,8935,8936],{"class":100}," @tanstack\u002Freact-query\n",[29,8938,8940,8941,8944],{"id":8939},"사용방법-usequery-데이터-가져오기","사용방법 ",[40,8942,8943],{},"useQuery"," - 데이터 가져오기",[15,8946,8947,8948,8950],{},"모든 라이브러리들이 그렇듯 최상위 컴포넌트에서 앱을 감싸주면 된다",[6005,8949],{},"\n패키지를 다룰 때 이 패키지를 순수 프레임워크로만 만든다고 생각하면 너무나 당연한 절차다",[33,8952,8956],{"className":8953,"code":8954,"language":8955,"meta":38,"style":38},"language-tsx shiki shiki-themes github-light github-dark","import { QueryClient, QueryClientProvider } from \"@tanstack\u002Freact-query\";\n\nconst queryClient = new QueryClient();\n\nfunction App() {\n  return (\n    \u003CQueryClientProvider client={queryClient}>\n      \u003CYourApp \u002F>\n    \u003C\u002FQueryClientProvider>\n  );\n}\n","tsx",[40,8957,8958,8972,8976,8992,8996,9004,9010,9025,9035,9043,9047],{"__ignoreMap":38},[43,8959,8960,8962,8965,8967,8970],{"class":45,"line":46},[43,8961,7079],{"class":67},[43,8963,8964],{"class":49}," { QueryClient, QueryClientProvider } ",[43,8966,7085],{"class":67},[43,8968,8969],{"class":100}," \"@tanstack\u002Freact-query\"",[43,8971,2995],{"class":49},[43,8973,8974],{"class":45,"line":64},[43,8975,1782],{"emptyLinePlaceholder":491},[43,8977,8978,8980,8983,8985,8987,8990],{"class":45,"line":119},[43,8979,68],{"class":67},[43,8981,8982],{"class":81}," queryClient",[43,8984,128],{"class":67},[43,8986,1794],{"class":67},[43,8988,8989],{"class":57}," QueryClient",[43,8991,147],{"class":49},[43,8993,8994],{"class":45,"line":150},[43,8995,1782],{"emptyLinePlaceholder":491},[43,8997,8998,9000,9002],{"class":45,"line":159},[43,8999,8424],{"class":67},[43,9001,8427],{"class":57},[43,9003,8430],{"class":49},[43,9005,9006,9008],{"class":45,"line":176},[43,9007,153],{"class":67},[43,9009,8437],{"class":49},[43,9011,9012,9014,9017,9020,9022],{"class":45,"line":1163},[43,9013,6726],{"class":49},[43,9015,9016],{"class":81},"QueryClientProvider",[43,9018,9019],{"class":57}," client",[43,9021,88],{"class":67},[43,9023,9024],{"class":49},"{queryClient}>\n",[43,9026,9027,9029,9032],{"class":45,"line":1178},[43,9028,7339],{"class":49},[43,9030,9031],{"class":81},"YourApp",[43,9033,9034],{"class":49}," \u002F>\n",[43,9036,9037,9039,9041],{"class":45,"line":1184},[43,9038,7378],{"class":49},[43,9040,9016],{"class":81},[43,9042,61],{"class":49},[43,9044,9045],{"class":45,"line":1190},[43,9046,8515],{"class":49},[43,9048,9049],{"class":45,"line":1996},[43,9050,2034],{"class":49},[15,9052,9053,9054,9056],{},"기존에 특정 API를 useHook으로 만들어서 사용하고 있었는데",[6005,9055],{},"\n이 파일의 코드에 한번 tanstack-query를 적용해보자",[1273,9058,9060],{"id":9059},"변경-전","변경 전",[185,9062,9063,9066],{},[188,9064,9065],{},"useEffect로 API 호출",[188,9067,9068],{},"데이터, 로딩 상태, 에러를 반환",[33,9070,9072],{"className":8953,"code":9071,"language":8955,"meta":38,"style":38},"import { useState, useEffect } from \"react\";\nimport { Content } from \"@\u002Ftypes\u002Findex\";\nimport api from \"@\u002Fapi\u002Fconfig\";\n\nconst useFetchContents = () => {\n  const [contents, setContents] = useState\u003CContent[]>([]);\n  const [loading, setLoading] = useState\u003Cboolean>(true);\n  const [error, setError] = useState\u003Cany | null>(null);\n\n  useEffect(() => {\n    const fetch = async () => {\n      try {\n        const { data } = await api.get\u003CContent[]>(\"\u002Fassignments\");\n        setContents(data);\n      } catch (err: any) {\n        setError(err.response?.data?.error || err.message);\n      } finally {\n        setLoading(false);\n      }\n    };\n\n    fetch();\n  }, []);\n\n  return {\n    data: contents,\n    loading,\n    error,\n  };\n};\n\nexport default useFetchContents;\n",[40,9073,9074,9088,9102,9116,9120,9135,9165,9197,9233,9237,9248,9266,9273,9305,9313,9332,9346,9355,9366,9370,9374,9378,9385,9390,9394,9400,9405,9410,9415,9420,9425,9429],{"__ignoreMap":38},[43,9075,9076,9078,9081,9083,9086],{"class":45,"line":46},[43,9077,7079],{"class":67},[43,9079,9080],{"class":49}," { useState, useEffect } ",[43,9082,7085],{"class":67},[43,9084,9085],{"class":100}," \"react\"",[43,9087,2995],{"class":49},[43,9089,9090,9092,9095,9097,9100],{"class":45,"line":64},[43,9091,7079],{"class":67},[43,9093,9094],{"class":49}," { Content } ",[43,9096,7085],{"class":67},[43,9098,9099],{"class":100}," \"@\u002Ftypes\u002Findex\"",[43,9101,2995],{"class":49},[43,9103,9104,9106,9109,9111,9114],{"class":45,"line":119},[43,9105,7079],{"class":67},[43,9107,9108],{"class":49}," api ",[43,9110,7085],{"class":67},[43,9112,9113],{"class":100}," \"@\u002Fapi\u002Fconfig\"",[43,9115,2995],{"class":49},[43,9117,9118],{"class":45,"line":150},[43,9119,1782],{"emptyLinePlaceholder":491},[43,9121,9122,9124,9127,9129,9131,9133],{"class":45,"line":159},[43,9123,68],{"class":67},[43,9125,9126],{"class":57}," useFetchContents",[43,9128,128],{"class":67},[43,9130,110],{"class":49},[43,9132,113],{"class":67},[43,9134,116],{"class":49},[43,9136,9137,9139,9141,9144,9146,9149,9152,9154,9157,9159,9162],{"class":45,"line":176},[43,9138,122],{"class":67},[43,9140,1169],{"class":49},[43,9142,9143],{"class":81},"contents",[43,9145,104],{"class":49},[43,9147,9148],{"class":81},"setContents",[43,9150,9151],{"class":49},"] ",[43,9153,88],{"class":67},[43,9155,9156],{"class":57}," useState",[43,9158,50],{"class":49},[43,9160,9161],{"class":57},"Content",[43,9163,9164],{"class":49},"[]>([]);\n",[43,9166,9167,9169,9171,9174,9176,9179,9181,9183,9185,9187,9190,9193,9195],{"class":45,"line":1163},[43,9168,122],{"class":67},[43,9170,1169],{"class":49},[43,9172,9173],{"class":81},"loading",[43,9175,104],{"class":49},[43,9177,9178],{"class":81},"setLoading",[43,9180,9151],{"class":49},[43,9182,88],{"class":67},[43,9184,9156],{"class":57},[43,9186,50],{"class":49},[43,9188,9189],{"class":81},"boolean",[43,9191,9192],{"class":49},">(",[43,9194,1053],{"class":81},[43,9196,1160],{"class":49},[43,9198,9199,9201,9203,9205,9207,9210,9212,9214,9216,9218,9221,9223,9226,9228,9231],{"class":45,"line":1178},[43,9200,122],{"class":67},[43,9202,1169],{"class":49},[43,9204,2014],{"class":81},[43,9206,104],{"class":49},[43,9208,9209],{"class":81},"setError",[43,9211,9151],{"class":49},[43,9213,88],{"class":67},[43,9215,9156],{"class":57},[43,9217,50],{"class":49},[43,9219,9220],{"class":81},"any",[43,9222,1376],{"class":67},[43,9224,9225],{"class":81}," null",[43,9227,9192],{"class":49},[43,9229,9230],{"class":81},"null",[43,9232,1160],{"class":49},[43,9234,9235],{"class":45,"line":1184},[43,9236,1782],{"emptyLinePlaceholder":491},[43,9238,9239,9242,9244,9246],{"class":45,"line":1190},[43,9240,9241],{"class":57},"  useEffect",[43,9243,7872],{"class":49},[43,9245,113],{"class":67},[43,9247,116],{"class":49},[43,9249,9250,9252,9255,9257,9260,9262,9264],{"class":45,"line":1996},[43,9251,1894],{"class":67},[43,9253,9254],{"class":57}," fetch",[43,9256,128],{"class":67},[43,9258,9259],{"class":67}," async",[43,9261,110],{"class":49},[43,9263,113],{"class":67},[43,9265,116],{"class":49},[43,9267,9268,9271],{"class":45,"line":2008},[43,9269,9270],{"class":67},"      try",[43,9272,116],{"class":49},[43,9274,9275,9278,9280,9282,9284,9286,9288,9291,9293,9295,9297,9300,9303],{"class":45,"line":2025},[43,9276,9277],{"class":67},"        const",[43,9279,71],{"class":49},[43,9281,75],{"class":81},[43,9283,85],{"class":49},[43,9285,88],{"class":67},[43,9287,91],{"class":67},[43,9289,9290],{"class":49}," api.",[43,9292,8195],{"class":57},[43,9294,50],{"class":49},[43,9296,9161],{"class":57},[43,9298,9299],{"class":49},"[]>(",[43,9301,9302],{"class":100},"\"\u002Fassignments\"",[43,9304,1160],{"class":49},[43,9306,9307,9310],{"class":45,"line":2031},[43,9308,9309],{"class":57},"        setContents",[43,9311,9312],{"class":49},"(data);\n",[43,9314,9315,9318,9320,9322,9324,9327,9330],{"class":45,"line":2037},[43,9316,9317],{"class":49},"      } ",[43,9319,2002],{"class":67},[43,9321,1069],{"class":49},[43,9323,2457],{"class":74},[43,9325,9326],{"class":67},":",[43,9328,9329],{"class":81}," any",[43,9331,1882],{"class":49},[43,9333,9334,9337,9340,9343],{"class":45,"line":2042},[43,9335,9336],{"class":57},"        setError",[43,9338,9339],{"class":49},"(err.response?.data?.error ",[43,9341,9342],{"class":67},"||",[43,9344,9345],{"class":49}," err.message);\n",[43,9347,9348,9350,9353],{"class":45,"line":2516},[43,9349,9317],{"class":49},[43,9351,9352],{"class":67},"finally",[43,9354,116],{"class":49},[43,9356,9357,9360,9362,9364],{"class":45,"line":2998},[43,9358,9359],{"class":57},"        setLoading",[43,9361,97],{"class":49},[43,9363,1830],{"class":81},[43,9365,1160],{"class":49},[43,9367,9368],{"class":45,"line":3003},[43,9369,7053],{"class":49},[43,9371,9372],{"class":45,"line":3009},[43,9373,2118],{"class":49},[43,9375,9376],{"class":45,"line":3022},[43,9377,1782],{"emptyLinePlaceholder":491},[43,9379,9380,9383],{"class":45,"line":3044},[43,9381,9382],{"class":57},"    fetch",[43,9384,147],{"class":49},[43,9386,9387],{"class":45,"line":3058},[43,9388,9389],{"class":49},"  }, []);\n",[43,9391,9392],{"class":45,"line":3077},[43,9393,1782],{"emptyLinePlaceholder":491},[43,9395,9396,9398],{"class":45,"line":3082},[43,9397,153],{"class":67},[43,9399,116],{"class":49},[43,9401,9402],{"class":45,"line":3088},[43,9403,9404],{"class":49},"    data: contents,\n",[43,9406,9407],{"class":45,"line":3093},[43,9408,9409],{"class":49},"    loading,\n",[43,9411,9412],{"class":45,"line":3099},[43,9413,9414],{"class":49},"    error,\n",[43,9416,9417],{"class":45,"line":3145},[43,9418,9419],{"class":49},"  };\n",[43,9421,9422],{"class":45,"line":3152},[43,9423,9424],{"class":49},"};\n",[43,9426,9427],{"class":45,"line":3165},[43,9428,1782],{"emptyLinePlaceholder":491},[43,9430,9431,9433,9435],{"class":45,"line":3203},[43,9432,6052],{"class":67},[43,9434,6055],{"class":67},[43,9436,9437],{"class":49}," useFetchContents;\n",[1273,9439,9441],{"id":9440},"변경-후","변경 후",[33,9443,9445],{"className":8953,"code":9444,"language":8955,"meta":38,"style":38},"import { useQuery } from \"@tanstack\u002Freact-query\";\nimport { Content } from \"@\u002Ftypes\u002Findex\";\nimport api from \"@\u002Fapi\u002Fconfig\";\n\nconst useFetchContents = () => {\n  const { data, isLoading, error } = useQuery\u003CContent[], Error>({\n    queryKey: [\"contents\"], \u002F\u002F Unique key for the query\n    queryFn: async () => {\n      const { data } = await api.get\u003CContent[]>(\"\u002Fassignments\");\n      return data;\n    },\n  });\n\n  return {\n    data: data ?? [],\n    loading: isLoading,\n    error,\n  };\n};\n\nexport default useFetchContents;\n",[40,9446,9447,9460,9472,9484,9488,9502,9537,9550,9565,9593,9600,9604,9609,9613,9619,9629,9634,9638,9642,9646,9650],{"__ignoreMap":38},[43,9448,9449,9451,9454,9456,9458],{"class":45,"line":46},[43,9450,7079],{"class":67},[43,9452,9453],{"class":49}," { useQuery } ",[43,9455,7085],{"class":67},[43,9457,8969],{"class":100},[43,9459,2995],{"class":49},[43,9461,9462,9464,9466,9468,9470],{"class":45,"line":64},[43,9463,7079],{"class":67},[43,9465,9094],{"class":49},[43,9467,7085],{"class":67},[43,9469,9099],{"class":100},[43,9471,2995],{"class":49},[43,9473,9474,9476,9478,9480,9482],{"class":45,"line":119},[43,9475,7079],{"class":67},[43,9477,9108],{"class":49},[43,9479,7085],{"class":67},[43,9481,9113],{"class":100},[43,9483,2995],{"class":49},[43,9485,9486],{"class":45,"line":150},[43,9487,1782],{"emptyLinePlaceholder":491},[43,9489,9490,9492,9494,9496,9498,9500],{"class":45,"line":159},[43,9491,68],{"class":67},[43,9493,9126],{"class":57},[43,9495,128],{"class":67},[43,9497,110],{"class":49},[43,9499,113],{"class":67},[43,9501,116],{"class":49},[43,9503,9504,9506,9508,9510,9512,9514,9516,9518,9520,9522,9524,9526,9528,9531,9534],{"class":45,"line":176},[43,9505,122],{"class":67},[43,9507,71],{"class":49},[43,9509,75],{"class":81},[43,9511,104],{"class":49},[43,9513,7544],{"class":81},[43,9515,104],{"class":49},[43,9517,2014],{"class":81},[43,9519,85],{"class":49},[43,9521,88],{"class":67},[43,9523,8269],{"class":57},[43,9525,50],{"class":49},[43,9527,9161],{"class":57},[43,9529,9530],{"class":49},"[], ",[43,9532,9533],{"class":57},"Error",[43,9535,9536],{"class":49},">({\n",[43,9538,9539,9541,9544,9547],{"class":45,"line":1163},[43,9540,8276],{"class":49},[43,9542,9543],{"class":100},"\"contents\"",[43,9545,9546],{"class":49},"], ",[43,9548,9549],{"class":2797},"\u002F\u002F Unique key for the query\n",[43,9551,9552,9555,9557,9559,9561,9563],{"class":45,"line":1178},[43,9553,9554],{"class":57},"    queryFn",[43,9556,78],{"class":49},[43,9558,107],{"class":67},[43,9560,110],{"class":49},[43,9562,113],{"class":67},[43,9564,116],{"class":49},[43,9566,9567,9569,9571,9573,9575,9577,9579,9581,9583,9585,9587,9589,9591],{"class":45,"line":1184},[43,9568,1082],{"class":67},[43,9570,71],{"class":49},[43,9572,75],{"class":81},[43,9574,85],{"class":49},[43,9576,88],{"class":67},[43,9578,91],{"class":67},[43,9580,9290],{"class":49},[43,9582,8195],{"class":57},[43,9584,50],{"class":49},[43,9586,9161],{"class":57},[43,9588,9299],{"class":49},[43,9590,9302],{"class":100},[43,9592,1160],{"class":49},[43,9594,9595,9597],{"class":45,"line":1190},[43,9596,1166],{"class":67},[43,9598,9599],{"class":49}," data;\n",[43,9601,9602],{"class":45,"line":1996},[43,9603,6095],{"class":49},[43,9605,9606],{"class":45,"line":2008},[43,9607,9608],{"class":49},"  });\n",[43,9610,9611],{"class":45,"line":2025},[43,9612,1782],{"emptyLinePlaceholder":491},[43,9614,9615,9617],{"class":45,"line":2031},[43,9616,153],{"class":67},[43,9618,116],{"class":49},[43,9620,9621,9624,9626],{"class":45,"line":2037},[43,9622,9623],{"class":49},"    data: data ",[43,9625,7880],{"class":67},[43,9627,9628],{"class":49}," [],\n",[43,9630,9631],{"class":45,"line":2042},[43,9632,9633],{"class":49},"    loading: isLoading,\n",[43,9635,9636],{"class":45,"line":2516},[43,9637,9414],{"class":49},[43,9639,9640],{"class":45,"line":2998},[43,9641,9419],{"class":49},[43,9643,9644],{"class":45,"line":3003},[43,9645,9424],{"class":49},[43,9647,9648],{"class":45,"line":3009},[43,9649,1782],{"emptyLinePlaceholder":491},[43,9651,9652,9654,9656],{"class":45,"line":3022},[43,9653,6052],{"class":67},[43,9655,6055],{"class":67},[43,9657,9437],{"class":49},[15,9659,9660,9661,9663],{},"기존 커스텀 훅 파일에 적용을 해서 불필요한 리턴을 하고 있는데,",[6005,9662],{},"\n간단한 구조라면 아래처럼 컴포넌트에서 직접 사용하는 것도 가능하다",[33,9665,9667],{"className":8953,"code":9666,"language":8955,"meta":38,"style":38},"const MyComponent = () => {\n  const { data, isLoading, error } = useQuery\u003CContent[], Error>({\n    queryKey: [\"contents\"], \u002F\u002F Unique key for the query\n    queryFn: async () => {\n      const { data } = await api.get\u003CContent[]>(\"\u002Fassignments\");\n      return data;\n    },\n  });\n\n  return \u003Cdiv>my-component\u003C\u002Fdiv>\n};\n",[40,9668,9669,9684,9716,9726,9740,9768,9774,9778,9782,9786,9802],{"__ignoreMap":38},[43,9670,9671,9673,9676,9678,9680,9682],{"class":45,"line":46},[43,9672,68],{"class":67},[43,9674,9675],{"class":57}," MyComponent",[43,9677,128],{"class":67},[43,9679,110],{"class":49},[43,9681,113],{"class":67},[43,9683,116],{"class":49},[43,9685,9686,9688,9690,9692,9694,9696,9698,9700,9702,9704,9706,9708,9710,9712,9714],{"class":45,"line":64},[43,9687,122],{"class":67},[43,9689,71],{"class":49},[43,9691,75],{"class":81},[43,9693,104],{"class":49},[43,9695,7544],{"class":81},[43,9697,104],{"class":49},[43,9699,2014],{"class":81},[43,9701,85],{"class":49},[43,9703,88],{"class":67},[43,9705,8269],{"class":57},[43,9707,50],{"class":49},[43,9709,9161],{"class":57},[43,9711,9530],{"class":49},[43,9713,9533],{"class":57},[43,9715,9536],{"class":49},[43,9717,9718,9720,9722,9724],{"class":45,"line":119},[43,9719,8276],{"class":49},[43,9721,9543],{"class":100},[43,9723,9546],{"class":49},[43,9725,9549],{"class":2797},[43,9727,9728,9730,9732,9734,9736,9738],{"class":45,"line":150},[43,9729,9554],{"class":57},[43,9731,78],{"class":49},[43,9733,107],{"class":67},[43,9735,110],{"class":49},[43,9737,113],{"class":67},[43,9739,116],{"class":49},[43,9741,9742,9744,9746,9748,9750,9752,9754,9756,9758,9760,9762,9764,9766],{"class":45,"line":159},[43,9743,1082],{"class":67},[43,9745,71],{"class":49},[43,9747,75],{"class":81},[43,9749,85],{"class":49},[43,9751,88],{"class":67},[43,9753,91],{"class":67},[43,9755,9290],{"class":49},[43,9757,8195],{"class":57},[43,9759,50],{"class":49},[43,9761,9161],{"class":57},[43,9763,9299],{"class":49},[43,9765,9302],{"class":100},[43,9767,1160],{"class":49},[43,9769,9770,9772],{"class":45,"line":176},[43,9771,1166],{"class":67},[43,9773,9599],{"class":49},[43,9775,9776],{"class":45,"line":1163},[43,9777,6095],{"class":49},[43,9779,9780],{"class":45,"line":1178},[43,9781,9608],{"class":49},[43,9783,9784],{"class":45,"line":1184},[43,9785,1782],{"emptyLinePlaceholder":491},[43,9787,9788,9790,9793,9795,9798,9800],{"class":45,"line":1190},[43,9789,153],{"class":67},[43,9791,9792],{"class":49}," \u003C",[43,9794,6703],{"class":53},[43,9796,9797],{"class":49},">my-component\u003C\u002F",[43,9799,6703],{"class":53},[43,9801,61],{"class":49},[43,9803,9804],{"class":45,"line":1996},[43,9805,9424],{"class":49},[15,9807,9808],{},"코드가 많이 간결해졌다",[185,9810,9811,9817,9829,9838],{},[188,9812,9813,9814,9816],{},"데이터를 가져올 때는 TanStack Query의 ",[40,9815,8943],{}," 훅을 사용한다.",[188,9818,9819,9821,9822,104,9825,9828],{},[40,9820,8943],{},"는 데이터, ",[40,9823,9824],{},"로딩 상태(isLoading)",[40,9826,9827],{},"에러(error)","를 반환하므로 직관적이고 효율적이다.",[188,9830,9831,9834,9835],{},[40,9832,9833],{},"queryKey",": 쿼리를 고유하게 식별하는 키다. 배열 형태로 지정하며, 이를 통해 캐싱과 데이터 관리를 최적화한다. ",[40,9836,9837],{},"동일한 키일 경우 캐시된 데이터를 재사용한다.",[188,9839,9840,9843,9844,9847],{},[40,9841,9842],{},"queryFn",": 데이터를 실제로 가져오는 ",[40,9845,9846],{},"비동기 함수","다. 이 예제에서는 API 호출을 통해 데이터를 가져오며, 반환된 데이터는 useQuery가 관리한다.",[26,9849],{},[29,9851,9853],{"id":9852},"usequery-옵션-정리-with-default-값","useQuery 옵션 정리 (with default 값)",[15,9855,9856,9857,9859],{},"실무에서는 쿼리의 동작을 상황에 맞게 조절해야 할 때가 많다.",[6005,9858],{},"\n그럴 때 사용되는 주요 옵션들과 기본값을 함께 정리해보자.",[678,9861,9862,9874],{},[681,9863,9864],{},[684,9865,9866,9869,9871],{},[687,9867,9868],{},"옵션",[687,9870,5673],{},[687,9872,9873],{},"기본값",[703,9875,9876,9890,9904,9920,9934,9948,9963,9975],{},[684,9877,9878,9883,9886],{},[708,9879,9880],{},[40,9881,9882],{},"enabled",[708,9884,9885],{},"조건부로 쿼리 실행 여부를 제어",[708,9887,9888],{},[40,9889,1053],{},[684,9891,9892,9897,9900],{},[708,9893,9894],{},[40,9895,9896],{},"staleTime",[708,9898,9899],{},"데이터가 stale로 간주되기까지 걸리는 시간(ms)",[708,9901,9902],{},[40,9903,4401],{},[684,9905,9906,9911,9914],{},[708,9907,9908],{},[40,9909,9910],{},"cacheTime",[708,9912,9913],{},"unmount 후에도 데이터를 유지하는 시간(ms)",[708,9915,9916,9919],{},[40,9917,9918],{},"300000"," (5분)",[684,9921,9922,9927,9930],{},[708,9923,9924],{},[40,9925,9926],{},"refetchOnWindowFocus",[708,9928,9929],{},"창이 다시 포커스될 때 refetch",[708,9931,9932],{},[40,9933,1053],{},[684,9935,9936,9941,9944],{},[708,9937,9938],{},[40,9939,9940],{},"retry",[708,9942,9943],{},"요청 실패 시 재시도 횟수",[708,9945,9946],{},[40,9947,931],{},[684,9949,9950,9955,9958],{},[708,9951,9952],{},[40,9953,9954],{},"refetchInterval",[708,9956,9957],{},"일정 주기로 refetch (ms 단위)",[708,9959,9960,9962],{},[40,9961,1830],{}," (사용 안 함)",[684,9964,9965,9970,9973],{},[708,9966,9967],{},[40,9968,9969],{},"select",[708,9971,9972],{},"쿼리 결과를 변형할 수 있는 함수",[708,9974,722],{},[684,9976,9977,9985,9988],{},[708,9978,9979,104,9982],{},[40,9980,9981],{},"onSuccess",[40,9983,9984],{},"onError",[708,9986,9987],{},"쿼리 성공\u002F실패 시 실행되는 콜백",[708,9989,722],{},[1273,9991,9992],{"id":695},"✨ 예시",[33,9994,9996],{"className":8953,"code":9995,"language":8955,"meta":38,"style":38},"const { data, isLoading } = useQuery({\n  queryKey: [\"todos\"],\n  queryFn: fetchTodos,\n  enabled: !!userId, \u002F\u002F 조건부 실행\n  staleTime: 1000 * 60 * 5, \u002F\u002F 5분 간 fresh\n  cacheTime: 1000 * 60 * 10, \u002F\u002F 10분 동안 메모리 유지\n  refetchOnWindowFocus: false, \u002F\u002F 창 포커스 시 refetch 안함\n  retry: 2, \u002F\u002F 실패 시 2회까지 재시도\n});\n",[40,9997,9998,10018,10028,10033,10047,10071,10092,10104,10116],{"__ignoreMap":38},[43,9999,10000,10002,10004,10006,10008,10010,10012,10014,10016],{"class":45,"line":46},[43,10001,68],{"class":67},[43,10003,71],{"class":49},[43,10005,75],{"class":81},[43,10007,104],{"class":49},[43,10009,7544],{"class":81},[43,10011,85],{"class":49},[43,10013,88],{"class":67},[43,10015,8269],{"class":57},[43,10017,1803],{"class":49},[43,10019,10020,10023,10026],{"class":45,"line":64},[43,10021,10022],{"class":49},"  queryKey: [",[43,10024,10025],{"class":100},"\"todos\"",[43,10027,8282],{"class":49},[43,10029,10030],{"class":45,"line":119},[43,10031,10032],{"class":49},"  queryFn: fetchTodos,\n",[43,10034,10035,10038,10041,10044],{"class":45,"line":150},[43,10036,10037],{"class":49},"  enabled: ",[43,10039,10040],{"class":67},"!!",[43,10042,10043],{"class":49},"userId, ",[43,10045,10046],{"class":2797},"\u002F\u002F 조건부 실행\n",[43,10048,10049,10052,10055,10058,10061,10063,10066,10068],{"class":45,"line":159},[43,10050,10051],{"class":49},"  staleTime: ",[43,10053,10054],{"class":81},"1000",[43,10056,10057],{"class":67}," *",[43,10059,10060],{"class":81}," 60",[43,10062,10057],{"class":67},[43,10064,10065],{"class":81}," 5",[43,10067,104],{"class":49},[43,10069,10070],{"class":2797},"\u002F\u002F 5분 간 fresh\n",[43,10072,10073,10076,10078,10080,10082,10084,10087,10089],{"class":45,"line":176},[43,10074,10075],{"class":49},"  cacheTime: ",[43,10077,10054],{"class":81},[43,10079,10057],{"class":67},[43,10081,10060],{"class":81},[43,10083,10057],{"class":67},[43,10085,10086],{"class":81}," 10",[43,10088,104],{"class":49},[43,10090,10091],{"class":2797},"\u002F\u002F 10분 동안 메모리 유지\n",[43,10093,10094,10097,10099,10101],{"class":45,"line":1163},[43,10095,10096],{"class":49},"  refetchOnWindowFocus: ",[43,10098,1830],{"class":81},[43,10100,104],{"class":49},[43,10102,10103],{"class":2797},"\u002F\u002F 창 포커스 시 refetch 안함\n",[43,10105,10106,10109,10111,10113],{"class":45,"line":1178},[43,10107,10108],{"class":49},"  retry: ",[43,10110,918],{"class":81},[43,10112,104],{"class":49},[43,10114,10115],{"class":2797},"\u002F\u002F 실패 시 2회까지 재시도\n",[43,10117,10118],{"class":45,"line":1184},[43,10119,1855],{"class":49},[26,10121],{},[8900,10123,10124],{},[185,10125,10126],{},[188,10127,10128],{},[304,10129,10132],{"href":10130,"rel":10131},"https:\u002F\u002Ftanstack.com\u002Fquery\u002Fv5\u002Fdocs\u002Fframework\u002Freact\u002Foverview",[308],"tanstack-query 공식문서",[471,10134,10135],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":38,"searchDepth":64,"depth":64,"links":10137},[10138,10139,10144],{"id":8917,"depth":64,"text":8918},{"id":8939,"depth":64,"text":10140,"children":10141},"사용방법 useQuery - 데이터 가져오기",[10142,10143],{"id":9059,"depth":119,"text":9060},{"id":9440,"depth":119,"text":9441},{"id":9852,"depth":64,"text":9853,"children":10145},[10146],{"id":695,"depth":119,"text":9992},"TanStack Query가 웹 애플리케이션에서 데이터 가져오기, 캐싱, 상태 관리를 어떻게 개선하는지 알아본다.",{"date":10149,"field":5954,"tags":10150},"2025.04.15",[8861,8387],"\u002Fko\u002Ftanstack-query-guide",{"title":8869,"description":10147},{"loc":10151},"ko\u002Ftanstack-query-guide","rkMDw3o9v7EOD18nZ_PZ7xYWPDMRWwxDeit-_KvOwBk",{"id":10157,"title":10158,"body":10159,"description":10987,"extension":484,"meta":10988,"navigation":491,"path":10992,"seo":10993,"sitemap":10994,"stem":10995,"__hash__":10996},"ko\u002Fko\u002Fvite-plugin-style-inject.md","리엑티 컴포넌트를 패키지 빌드 시 CSS 같이 적용시키기",{"type":8,"value":10160,"toc":10972},[10161,10174,10176,10180,10190,10371,10375,10458,10525,10528,10534,10537,10588,10591,10597,10599,10603,10617,10628,10630,10638,10651,10654,10671,10674,10847,10850,10897,10900,10921,10923,10926,10929,10933,10963,10969],[8908,10162,10163],{},[15,10164,10165,10166,10169,10170,10173],{},"Vite를 사용해 React 컴포넌트를 라이브러리로 패키징하면, ",[40,10167,10168],{},"dist"," 디렉토리에 ",[40,10171,10172],{},"style.css"," 파일이 생성되지만, 실제로 해당 컴포넌트를 사용하는 쪽에서 CSS가 적용되지 않는 문제가 발생할 수 있다. 이 글에서는 왜 이런 문제가 발생하는지, 그리고 어떻게 해결할 수 있는지를 실전 예제와 함께 자세히 살펴보자.",[26,10175],{},[29,10177,10179],{"id":10178},"문제-상황","문제 상황",[10181,10182,10183],"note",{},[15,10184,10185,10186,10189],{},"Vite로 React 컴포넌트를 라이브러리 형태로 빌드할 경우, ",[40,10187,10188],{},"vite.config.ts","를 다음과 같이 설정하는 경우가 많다.",[33,10191,10193],{"className":6038,"code":10192,"language":6040,"meta":38,"style":38},"\u002F\u002F vite.config.ts\nimport { defineConfig } from 'vite';\nimport react from '@vitejs\u002Fplugin-react';\n\nexport default defineConfig({\n  plugins: [react()],\n  build: {\n    lib: {\n      entry: 'src\u002Findex.ts',\n      name: 'MyComponentLib',\n      fileName: 'my-component-lib',\n    },\n    rollupOptions: {\n      external: ['react', 'react-dom'],\n      output: {\n        globals: {\n          react: 'React',\n          'react-dom': 'ReactDOM',\n        },\n      },\n    },\n  },\n});\n",[40,10194,10195,10200,10214,10228,10232,10243,10253,10258,10263,10273,10283,10293,10297,10302,10317,10322,10327,10337,10349,10354,10359,10363,10367],{"__ignoreMap":38},[43,10196,10197],{"class":45,"line":46},[43,10198,10199],{"class":2797},"\u002F\u002F vite.config.ts\n",[43,10201,10202,10204,10207,10209,10212],{"class":45,"line":64},[43,10203,7079],{"class":67},[43,10205,10206],{"class":49}," { defineConfig } ",[43,10208,7085],{"class":67},[43,10210,10211],{"class":100}," 'vite'",[43,10213,2995],{"class":49},[43,10215,10216,10218,10221,10223,10226],{"class":45,"line":119},[43,10217,7079],{"class":67},[43,10219,10220],{"class":49}," react ",[43,10222,7085],{"class":67},[43,10224,10225],{"class":100}," '@vitejs\u002Fplugin-react'",[43,10227,2995],{"class":49},[43,10229,10230],{"class":45,"line":150},[43,10231,1782],{"emptyLinePlaceholder":491},[43,10233,10234,10236,10238,10241],{"class":45,"line":159},[43,10235,6052],{"class":67},[43,10237,6055],{"class":67},[43,10239,10240],{"class":57}," defineConfig",[43,10242,1803],{"class":49},[43,10244,10245,10248,10250],{"class":45,"line":176},[43,10246,10247],{"class":49},"  plugins: [",[43,10249,8861],{"class":57},[43,10251,10252],{"class":49},"()],\n",[43,10254,10255],{"class":45,"line":1163},[43,10256,10257],{"class":49},"  build: {\n",[43,10259,10260],{"class":45,"line":1178},[43,10261,10262],{"class":49},"    lib: {\n",[43,10264,10265,10268,10271],{"class":45,"line":1184},[43,10266,10267],{"class":49},"      entry: ",[43,10269,10270],{"class":100},"'src\u002Findex.ts'",[43,10272,1056],{"class":49},[43,10274,10275,10278,10281],{"class":45,"line":1190},[43,10276,10277],{"class":49},"      name: ",[43,10279,10280],{"class":100},"'MyComponentLib'",[43,10282,1056],{"class":49},[43,10284,10285,10288,10291],{"class":45,"line":1996},[43,10286,10287],{"class":49},"      fileName: ",[43,10289,10290],{"class":100},"'my-component-lib'",[43,10292,1056],{"class":49},[43,10294,10295],{"class":45,"line":2008},[43,10296,6095],{"class":49},[43,10298,10299],{"class":45,"line":2025},[43,10300,10301],{"class":49},"    rollupOptions: {\n",[43,10303,10304,10307,10310,10312,10315],{"class":45,"line":2031},[43,10305,10306],{"class":49},"      external: [",[43,10308,10309],{"class":100},"'react'",[43,10311,104],{"class":49},[43,10313,10314],{"class":100},"'react-dom'",[43,10316,8282],{"class":49},[43,10318,10319],{"class":45,"line":2037},[43,10320,10321],{"class":49},"      output: {\n",[43,10323,10324],{"class":45,"line":2042},[43,10325,10326],{"class":49},"        globals: {\n",[43,10328,10329,10332,10335],{"class":45,"line":2516},[43,10330,10331],{"class":49},"          react: ",[43,10333,10334],{"class":100},"'React'",[43,10336,1056],{"class":49},[43,10338,10339,10342,10344,10347],{"class":45,"line":2998},[43,10340,10341],{"class":100},"          'react-dom'",[43,10343,78],{"class":49},[43,10345,10346],{"class":100},"'ReactDOM'",[43,10348,1056],{"class":49},[43,10350,10351],{"class":45,"line":3003},[43,10352,10353],{"class":49},"        },\n",[43,10355,10356],{"class":45,"line":3009},[43,10357,10358],{"class":49},"      },\n",[43,10360,10361],{"class":45,"line":3022},[43,10362,6095],{"class":49},[43,10364,10365],{"class":45,"line":3044},[43,10366,1187],{"class":49},[43,10368,10369],{"class":45,"line":3058},[43,10370,1855],{"class":49},[1273,10372,10374],{"id":10373},"예제-컴포넌트","예제 컴포넌트",[33,10376,10378],{"className":8953,"code":10377,"language":8955,"meta":38,"style":38},"\u002F\u002F src\u002Fcomponents\u002FMyButton.tsx\nimport React from 'react';\nimport '.\u002FMyButton.css';\n\nexport const MyButton = () => {\n  return \u003Cbutton className=\"my-button\">Click me\u003C\u002Fbutton>;\n};\n",[40,10379,10380,10385,10399,10408,10412,10429,10454],{"__ignoreMap":38},[43,10381,10382],{"class":45,"line":46},[43,10383,10384],{"class":2797},"\u002F\u002F src\u002Fcomponents\u002FMyButton.tsx\n",[43,10386,10387,10389,10392,10394,10397],{"class":45,"line":64},[43,10388,7079],{"class":67},[43,10390,10391],{"class":49}," React ",[43,10393,7085],{"class":67},[43,10395,10396],{"class":100}," 'react'",[43,10398,2995],{"class":49},[43,10400,10401,10403,10406],{"class":45,"line":119},[43,10402,7079],{"class":67},[43,10404,10405],{"class":100}," '.\u002FMyButton.css'",[43,10407,2995],{"class":49},[43,10409,10410],{"class":45,"line":150},[43,10411,1782],{"emptyLinePlaceholder":491},[43,10413,10414,10416,10418,10421,10423,10425,10427],{"class":45,"line":159},[43,10415,6052],{"class":67},[43,10417,8131],{"class":67},[43,10419,10420],{"class":57}," MyButton",[43,10422,128],{"class":67},[43,10424,110],{"class":49},[43,10426,113],{"class":67},[43,10428,116],{"class":49},[43,10430,10431,10433,10435,10438,10441,10443,10446,10449,10451],{"class":45,"line":176},[43,10432,153],{"class":67},[43,10434,9792],{"class":49},[43,10436,10437],{"class":53},"button",[43,10439,10440],{"class":57}," className",[43,10442,88],{"class":67},[43,10444,10445],{"class":100},"\"my-button\"",[43,10447,10448],{"class":49},">Click me\u003C\u002F",[43,10450,10437],{"class":53},[43,10452,10453],{"class":49},">;\n",[43,10455,10456],{"class":45,"line":1163},[43,10457,9424],{"class":49},[33,10459,10463],{"className":10460,"code":10461,"language":10462,"meta":38,"style":38},"language-css shiki shiki-themes github-light github-dark","\u002F* src\u002Fcomponents\u002FMyButton.css *\u002F\n.my-button {\n  padding: 10px 20px;\n  background-color: #007bff;\n  color: white;\n}\n","css",[40,10464,10465,10470,10477,10497,10509,10521],{"__ignoreMap":38},[43,10466,10467],{"class":45,"line":46},[43,10468,10469],{"class":2797},"\u002F* src\u002Fcomponents\u002FMyButton.css *\u002F\n",[43,10471,10472,10475],{"class":45,"line":64},[43,10473,10474],{"class":57},".my-button",[43,10476,116],{"class":49},[43,10478,10479,10482,10484,10487,10490,10493,10495],{"class":45,"line":119},[43,10480,10481],{"class":81},"  padding",[43,10483,78],{"class":49},[43,10485,10486],{"class":81},"10",[43,10488,10489],{"class":67},"px",[43,10491,10492],{"class":81}," 20",[43,10494,10489],{"class":67},[43,10496,2995],{"class":49},[43,10498,10499,10502,10504,10507],{"class":45,"line":150},[43,10500,10501],{"class":81},"  background-color",[43,10503,78],{"class":49},[43,10505,10506],{"class":81},"#007bff",[43,10508,2995],{"class":49},[43,10510,10511,10514,10516,10519],{"class":45,"line":159},[43,10512,10513],{"class":81},"  color",[43,10515,78],{"class":49},[43,10517,10518],{"class":81},"white",[43,10520,2995],{"class":49},[43,10522,10523],{"class":45,"line":176},[43,10524,2034],{"class":49},[15,10526,10527],{},"빌드를 수행하면 다음과 같은 구조가 생성된다:",[33,10529,10532],{"className":10530,"code":10531,"language":539},[537],"dist\u002F\n├── style.css                ← 여기에 CSS는 존재한다\n├── my-component-lib.es.js\n├── my-component-lib.umd.js\n",[40,10533,10531],{"__ignoreMap":38},[15,10535,10536],{},"하지만, 이 패키지를 외부 프로젝트에서 사용할 경우 다음과 같은 코드로 불러오게 된다:",[33,10538,10540],{"className":8953,"code":10539,"language":8955,"meta":38,"style":38},"import { MyButton } from 'my-component-lib';\n\nexport default function App() {\n  return \u003CMyButton \u002F>;\n}\n",[40,10541,10542,10556,10560,10572,10584],{"__ignoreMap":38},[43,10543,10544,10546,10549,10551,10554],{"class":45,"line":46},[43,10545,7079],{"class":67},[43,10547,10548],{"class":49}," { MyButton } ",[43,10550,7085],{"class":67},[43,10552,10553],{"class":100}," 'my-component-lib'",[43,10555,2995],{"class":49},[43,10557,10558],{"class":45,"line":64},[43,10559,1782],{"emptyLinePlaceholder":491},[43,10561,10562,10564,10566,10568,10570],{"class":45,"line":119},[43,10563,6052],{"class":67},[43,10565,6055],{"class":67},[43,10567,1871],{"class":67},[43,10569,8427],{"class":57},[43,10571,8430],{"class":49},[43,10573,10574,10576,10578,10581],{"class":45,"line":150},[43,10575,153],{"class":67},[43,10577,9792],{"class":49},[43,10579,10580],{"class":81},"MyButton",[43,10582,10583],{"class":49}," \u002F>;\n",[43,10585,10586],{"class":45,"line":159},[43,10587,2034],{"class":49},[15,10589,10590],{},"결과는?",[15,10592,10593,10594],{},"❌ 버튼은 HTML 구조는 보이지만 ",[21,10595,10596],{},"스타일이 적용되지 않는다.",[26,10598],{},[29,10600,10602],{"id":10601},"원인-분석","원인 분석",[15,10604,10605,10606,10613,10614],{},"이는 ",[21,10607,10608,10609,10612],{},"Vite의 라이브러리 모드(",[40,10610,10611],{},"build.lib",")가 CSS를 JS 번들에서 분리하기 때문","이다. 라이브러리 모드에서는 Vite가 CSS 파일을 별도로 추출하고, 이를 JS 파일에 ",[21,10615,10616],{},"자동으로 포함하거나 주입하지 않는다.",[15,10618,10619,10620,10623,10624,10627],{},"이 디자인은 일반적으로 ",[21,10621,10622],{},"라이브러리 소비자가 스타일 적용 여부를 스스로 결정할 수 있도록 하기 위한 의도","이다. 하지만 대부분의 사용자는 라이브러리를 사용할 때 ",[21,10625,10626],{},"CSS가 자동으로 적용되기를 기대","하기 때문에, 실제 사용에서 불편함을 유발한다.",[26,10629],{},[29,10631,10633,10634,10637],{"id":10632},"해결책-1-vite-plugin-lib-inject-css로-자동-주입하기","해결책 1: ",[40,10635,10636],{},"vite-plugin-lib-inject-css","로 자동 주입하기",[15,10639,10640,10641,10644,10645,8886],{},"보다 강력하고 사용자 친화적인 방법은 CSS를 ",[21,10642,10643],{},"라이브러리 JS 파일 내부에 자동으로 주입","해주는 방식이다. 이를 도와주는 플러그인이 ",[304,10646,10649],{"href":10647,"rel":10648},"https:\u002F\u002Fgithub.com\u002Fyunsii\u002Fvite-plugin-lib-inject-css",[308],[40,10650,10636],{},[1273,10652,10653],{"id":10653},"설치",[33,10655,10657],{"className":850,"code":10656,"language":852,"meta":38,"style":38},"npm install vite-plugin-lib-inject-css --save-dev\n",[40,10658,10659],{"__ignoreMap":38},[43,10660,10661,10663,10665,10668],{"class":45,"line":46},[43,10662,1741],{"class":57},[43,10664,1744],{"class":100},[43,10666,10667],{"class":100}," vite-plugin-lib-inject-css",[43,10669,10670],{"class":81}," --save-dev\n",[1273,10672,10673],{"id":10673},"설정",[33,10675,10677],{"className":6038,"code":10676,"language":6040,"meta":38,"style":38},"\u002F\u002F vite.config.ts\nimport { defineConfig } from 'vite';\nimport react from '@vitejs\u002Fplugin-react';\nimport libInjectCss from 'vite-plugin-lib-inject-css';\n\nexport default defineConfig({\n  plugins: [react(), libInjectCss()],\n  build: {\n    lib: {\n      entry: 'src\u002Findex.ts',\n      name: 'MyComponentLib',\n      fileName: 'my-component-lib',\n    },\n    rollupOptions: {\n      external: ['react', 'react-dom'],\n      output: {\n        globals: {\n          react: 'React',\n          'react-dom': 'ReactDOM',\n        },\n      },\n    },\n  },\n});\n",[40,10678,10679,10683,10695,10707,10721,10725,10735,10749,10753,10757,10765,10773,10781,10785,10789,10801,10805,10809,10817,10827,10831,10835,10839,10843],{"__ignoreMap":38},[43,10680,10681],{"class":45,"line":46},[43,10682,10199],{"class":2797},[43,10684,10685,10687,10689,10691,10693],{"class":45,"line":64},[43,10686,7079],{"class":67},[43,10688,10206],{"class":49},[43,10690,7085],{"class":67},[43,10692,10211],{"class":100},[43,10694,2995],{"class":49},[43,10696,10697,10699,10701,10703,10705],{"class":45,"line":119},[43,10698,7079],{"class":67},[43,10700,10220],{"class":49},[43,10702,7085],{"class":67},[43,10704,10225],{"class":100},[43,10706,2995],{"class":49},[43,10708,10709,10711,10714,10716,10719],{"class":45,"line":150},[43,10710,7079],{"class":67},[43,10712,10713],{"class":49}," libInjectCss ",[43,10715,7085],{"class":67},[43,10717,10718],{"class":100}," 'vite-plugin-lib-inject-css'",[43,10720,2995],{"class":49},[43,10722,10723],{"class":45,"line":159},[43,10724,1782],{"emptyLinePlaceholder":491},[43,10726,10727,10729,10731,10733],{"class":45,"line":176},[43,10728,6052],{"class":67},[43,10730,6055],{"class":67},[43,10732,10240],{"class":57},[43,10734,1803],{"class":49},[43,10736,10737,10739,10741,10744,10747],{"class":45,"line":1163},[43,10738,10247],{"class":49},[43,10740,8861],{"class":57},[43,10742,10743],{"class":49},"(), ",[43,10745,10746],{"class":57},"libInjectCss",[43,10748,10252],{"class":49},[43,10750,10751],{"class":45,"line":1178},[43,10752,10257],{"class":49},[43,10754,10755],{"class":45,"line":1184},[43,10756,10262],{"class":49},[43,10758,10759,10761,10763],{"class":45,"line":1190},[43,10760,10267],{"class":49},[43,10762,10270],{"class":100},[43,10764,1056],{"class":49},[43,10766,10767,10769,10771],{"class":45,"line":1996},[43,10768,10277],{"class":49},[43,10770,10280],{"class":100},[43,10772,1056],{"class":49},[43,10774,10775,10777,10779],{"class":45,"line":2008},[43,10776,10287],{"class":49},[43,10778,10290],{"class":100},[43,10780,1056],{"class":49},[43,10782,10783],{"class":45,"line":2025},[43,10784,6095],{"class":49},[43,10786,10787],{"class":45,"line":2031},[43,10788,10301],{"class":49},[43,10790,10791,10793,10795,10797,10799],{"class":45,"line":2037},[43,10792,10306],{"class":49},[43,10794,10309],{"class":100},[43,10796,104],{"class":49},[43,10798,10314],{"class":100},[43,10800,8282],{"class":49},[43,10802,10803],{"class":45,"line":2042},[43,10804,10321],{"class":49},[43,10806,10807],{"class":45,"line":2516},[43,10808,10326],{"class":49},[43,10810,10811,10813,10815],{"class":45,"line":2998},[43,10812,10331],{"class":49},[43,10814,10334],{"class":100},[43,10816,1056],{"class":49},[43,10818,10819,10821,10823,10825],{"class":45,"line":3003},[43,10820,10341],{"class":100},[43,10822,78],{"class":49},[43,10824,10346],{"class":100},[43,10826,1056],{"class":49},[43,10828,10829],{"class":45,"line":3009},[43,10830,10353],{"class":49},[43,10832,10833],{"class":45,"line":3022},[43,10834,10358],{"class":49},[43,10836,10837],{"class":45,"line":3044},[43,10838,6095],{"class":49},[43,10840,10841],{"class":45,"line":3058},[43,10842,1187],{"class":49},[43,10844,10845],{"class":45,"line":3077},[43,10846,1855],{"class":49},[15,10848,10849],{},"이렇게 설정하면 빌드된 JS 파일 내부에 다음과 같은 코드가 자동으로 삽입된다:",[33,10851,10853],{"className":1024,"code":10852,"language":1026,"meta":38,"style":38},"const style = document.createElement('style');\nstyle.textContent = '...빌드된 CSS 내용...';\ndocument.head.appendChild(style);\n",[40,10854,10855,10875,10887],{"__ignoreMap":38},[43,10856,10857,10859,10862,10864,10866,10868,10870,10873],{"class":45,"line":46},[43,10858,68],{"class":67},[43,10860,10861],{"class":81}," style",[43,10863,128],{"class":67},[43,10865,8581],{"class":49},[43,10867,8584],{"class":57},[43,10869,97],{"class":49},[43,10871,10872],{"class":100},"'style'",[43,10874,1160],{"class":49},[43,10876,10877,10880,10882,10885],{"class":45,"line":64},[43,10878,10879],{"class":49},"style.textContent ",[43,10881,88],{"class":67},[43,10883,10884],{"class":100}," '...빌드된 CSS 내용...'",[43,10886,2995],{"class":49},[43,10888,10889,10892,10894],{"class":45,"line":119},[43,10890,10891],{"class":49},"document.head.",[43,10893,8668],{"class":57},[43,10895,10896],{"class":49},"(style);\n",[1273,10898,10899],{"id":10899},"결과",[185,10901,10902,10905,10914],{},[188,10903,10904],{},"사용자는 더 이상 CSS를 수동으로 import 할 필요가 없다.",[188,10906,10907,10910,10911],{},[40,10908,10909],{},"import { MyButton } from 'my-component-lib'"," 한 줄이면, ",[21,10912,10913],{},"스타일까지 함께 적용된다.",[188,10915,10916,10917,10920],{},"내부적으로 ",[40,10918,10919],{},"\u003Cstyle>"," 태그가 생성되어 head에 삽입되므로 브라우저에서 동작도 보장된다.",[26,10922],{},[29,10924,10925],{"id":10925},"결론",[15,10927,10928],{},"Vite의 라이브러리 빌드 모드에서는 기본적으로 CSS가 JS와 분리되어 출력되며, 자동으로 로드되지 않는다. 이를 방치하면 라이브러리를 사용하는 쪽에서 예상치 못한 스타일 누락 문제가 발생한다.",[1273,10930,10932],{"id":10931},"해결-방법-요약","✔ 해결 방법 요약",[678,10934,10935,10948],{},[681,10936,10937],{},[684,10938,10939,10942,10945],{},[687,10940,10941],{},"방법",[687,10943,10944],{},"장점",[687,10946,10947],{},"단점",[703,10949,10950],{},[684,10951,10952,10957,10960],{},[708,10953,10954,10956],{},[40,10955,10636],{}," 사용",[708,10958,10959],{},"자동 스타일 적용, UX 향상",[708,10961,10962],{},"빌드 크기 소폭 증가",[15,10964,10965,10966,10968],{},"라이브러리를 보다 완성도 있게 만들고 싶다면, ",[40,10967,10636],{},"를 적용하여 개발자 경험을 개선하자.",[471,10970,10971],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":38,"searchDepth":64,"depth":64,"links":10973},[10974,10977,10978,10984],{"id":10178,"depth":64,"text":10179,"children":10975},[10976],{"id":10373,"depth":119,"text":10374},{"id":10601,"depth":64,"text":10602},{"id":10632,"depth":64,"text":10979,"children":10980},"해결책 1: vite-plugin-lib-inject-css로 자동 주입하기",[10981,10982,10983],{"id":10653,"depth":119,"text":10653},{"id":10673,"depth":119,"text":10673},{"id":10899,"depth":119,"text":10899},{"id":10925,"depth":64,"text":10925,"children":10985},[10986],{"id":10931,"depth":119,"text":10932},"dist에 css에 파일이 생성되었으나, 적용 안 되는 이유를 알아보고 에러를 처리하자",{"date":10989,"field":5954,"tags":10990},"2025.04.06",[10991,8861],"vite","\u002Fko\u002Fvite-plugin-style-inject",{"title":10158,"description":10987},{"loc":10992},"ko\u002Fvite-plugin-style-inject","qL2kX5_FtfBBJrZIrxY_YFG4h-uBUfv3aRJrVzoUWTE",{"id":10998,"title":10999,"body":11000,"description":13085,"extension":484,"meta":13086,"navigation":491,"path":13089,"seo":13090,"sitemap":13091,"stem":13092,"__hash__":13093},"ko\u002Fko\u002Fcss-login-form.md","로그인",{"type":8,"value":11001,"toc":13082},[11002,11004,13079],[29,11003,10999],{"id":10999},[11005,11006,11007,11508],"code-group",{},[33,11008,11012],{"className":11009,"code":11010,"language":11011,"meta":38,"style":38},"language-html shiki shiki-themes github-light github-dark","\u003Cdiv class=\"login-card\">\n\n  \u003Cdiv class=\"login-card__header\">\n    \u003Ch1 class=\"login-card__title\">영어 단어장\u003C\u002Fh1>\n    \u003Cp class=\"login-card__description\">즐겁게 배우는 영어 단어, 너무 쉬워요!\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\n  \u003Cform class=\"login-form\">\n    \u003Cdiv class=\"login-form__field\">\n      \u003Clabel class=\"login-form__label\" for=\"username\">사용자 이름\u003C\u002Flabel>\n      \u003Cdiv class=\"login-form__input-wrapper\">\n        \u003Cspan class=\"login-form__icon\">👤\u003C\u002Fspan>\n        \u003Cinput\n          class=\"login-form__input\"\n          id=\"username\"\n          type=\"text\"\n          placeholder=\"이름을 입력하세요\"\n        \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"login-form__field\">\n      \u003Clabel class=\"login-form__label\" for=\"password\">비밀번호\u003C\u002Flabel>\n      \u003Cdiv class=\"login-form__input-wrapper\">\n        \u003Cspan class=\"login-form__icon\">🔒\u003C\u002Fspan>\n        \u003Cinput\n          class=\"login-form__input\"\n          id=\"password\"\n          type=\"password\"\n          placeholder=\"비밀번호를 입력하세요\"\n        \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cbutton class=\"login-form__button\" type=\"submit\">로그인하기\u003C\u002Fbutton>\n\n    \u003Cdiv class=\"login-form__signup\">\n      아직 계정이 없으신가요?\n      \u003Ca href=\"#\" class=\"login-form__link\">회원가입\u003C\u002Fa>\n    \u003C\u002Fdiv>\n  \u003C\u002Fform>\n\u003C\u002Fdiv>\n","html",[40,11013,11014,11029,11033,11048,11068,11088,11096,11100,11116,11131,11160,11175,11195,11202,11212,11222,11232,11242,11247,11255,11263,11267,11281,11307,11321,11340,11346,11354,11363,11371,11380,11384,11392,11400,11404,11432,11436,11451,11456,11484,11492,11500],{"__ignoreMap":38},[43,11015,11016,11018,11020,11022,11024,11027],{"class":45,"line":46},[43,11017,50],{"class":49},[43,11019,6703],{"class":53},[43,11021,6714],{"class":57},[43,11023,88],{"class":49},[43,11025,11026],{"class":100},"\"login-card\"",[43,11028,61],{"class":49},[43,11030,11031],{"class":45,"line":64},[43,11032,1782],{"emptyLinePlaceholder":491},[43,11034,11035,11037,11039,11041,11043,11046],{"class":45,"line":119},[43,11036,6700],{"class":49},[43,11038,6703],{"class":53},[43,11040,6714],{"class":57},[43,11042,88],{"class":49},[43,11044,11045],{"class":100},"\"login-card__header\"",[43,11047,61],{"class":49},[43,11049,11050,11052,11054,11056,11058,11061,11064,11066],{"class":45,"line":150},[43,11051,6726],{"class":49},[43,11053,11],{"class":53},[43,11055,6714],{"class":57},[43,11057,88],{"class":49},[43,11059,11060],{"class":100},"\"login-card__title\"",[43,11062,11063],{"class":49},">영어 단어장\u003C\u002F",[43,11065,11],{"class":53},[43,11067,61],{"class":49},[43,11069,11070,11072,11074,11076,11078,11081,11084,11086],{"class":45,"line":159},[43,11071,6726],{"class":49},[43,11073,15],{"class":53},[43,11075,6714],{"class":57},[43,11077,88],{"class":49},[43,11079,11080],{"class":100},"\"login-card__description\"",[43,11082,11083],{"class":49},">즐겁게 배우는 영어 단어, 너무 쉬워요!\u003C\u002F",[43,11085,15],{"class":53},[43,11087,61],{"class":49},[43,11089,11090,11092,11094],{"class":45,"line":176},[43,11091,6740],{"class":49},[43,11093,6703],{"class":53},[43,11095,61],{"class":49},[43,11097,11098],{"class":45,"line":1163},[43,11099,1782],{"emptyLinePlaceholder":491},[43,11101,11102,11104,11107,11109,11111,11114],{"class":45,"line":1178},[43,11103,6700],{"class":49},[43,11105,11106],{"class":53},"form",[43,11108,6714],{"class":57},[43,11110,88],{"class":49},[43,11112,11113],{"class":100},"\"login-form\"",[43,11115,61],{"class":49},[43,11117,11118,11120,11122,11124,11126,11129],{"class":45,"line":1184},[43,11119,6726],{"class":49},[43,11121,6703],{"class":53},[43,11123,6714],{"class":57},[43,11125,88],{"class":49},[43,11127,11128],{"class":100},"\"login-form__field\"",[43,11130,61],{"class":49},[43,11132,11133,11135,11138,11140,11142,11145,11148,11150,11153,11156,11158],{"class":45,"line":1190},[43,11134,7339],{"class":49},[43,11136,11137],{"class":53},"label",[43,11139,6714],{"class":57},[43,11141,88],{"class":49},[43,11143,11144],{"class":100},"\"login-form__label\"",[43,11146,11147],{"class":57}," for",[43,11149,88],{"class":49},[43,11151,11152],{"class":100},"\"username\"",[43,11154,11155],{"class":49},">사용자 이름\u003C\u002F",[43,11157,11137],{"class":53},[43,11159,61],{"class":49},[43,11161,11162,11164,11166,11168,11170,11173],{"class":45,"line":1996},[43,11163,7339],{"class":49},[43,11165,6703],{"class":53},[43,11167,6714],{"class":57},[43,11169,88],{"class":49},[43,11171,11172],{"class":100},"\"login-form__input-wrapper\"",[43,11174,61],{"class":49},[43,11176,11177,11179,11181,11183,11185,11188,11191,11193],{"class":45,"line":2008},[43,11178,8017],{"class":49},[43,11180,43],{"class":53},[43,11182,6714],{"class":57},[43,11184,88],{"class":49},[43,11186,11187],{"class":100},"\"login-form__icon\"",[43,11189,11190],{"class":49},">👤\u003C\u002F",[43,11192,43],{"class":53},[43,11194,61],{"class":49},[43,11196,11197,11199],{"class":45,"line":2025},[43,11198,8017],{"class":49},[43,11200,11201],{"class":53},"input\n",[43,11203,11204,11207,11209],{"class":45,"line":2031},[43,11205,11206],{"class":57},"          class",[43,11208,88],{"class":49},[43,11210,11211],{"class":100},"\"login-form__input\"\n",[43,11213,11214,11217,11219],{"class":45,"line":2037},[43,11215,11216],{"class":57},"          id",[43,11218,88],{"class":49},[43,11220,11221],{"class":100},"\"username\"\n",[43,11223,11224,11227,11229],{"class":45,"line":2042},[43,11225,11226],{"class":57},"          type",[43,11228,88],{"class":49},[43,11230,11231],{"class":100},"\"text\"\n",[43,11233,11234,11237,11239],{"class":45,"line":2516},[43,11235,11236],{"class":57},"          placeholder",[43,11238,88],{"class":49},[43,11240,11241],{"class":100},"\"이름을 입력하세요\"\n",[43,11243,11244],{"class":45,"line":2998},[43,11245,11246],{"class":49},"        \u002F>\n",[43,11248,11249,11251,11253],{"class":45,"line":3003},[43,11250,7369],{"class":49},[43,11252,6703],{"class":53},[43,11254,61],{"class":49},[43,11256,11257,11259,11261],{"class":45,"line":3009},[43,11258,7378],{"class":49},[43,11260,6703],{"class":53},[43,11262,61],{"class":49},[43,11264,11265],{"class":45,"line":3022},[43,11266,1782],{"emptyLinePlaceholder":491},[43,11268,11269,11271,11273,11275,11277,11279],{"class":45,"line":3044},[43,11270,6726],{"class":49},[43,11272,6703],{"class":53},[43,11274,6714],{"class":57},[43,11276,88],{"class":49},[43,11278,11128],{"class":100},[43,11280,61],{"class":49},[43,11282,11283,11285,11287,11289,11291,11293,11295,11297,11300,11303,11305],{"class":45,"line":3058},[43,11284,7339],{"class":49},[43,11286,11137],{"class":53},[43,11288,6714],{"class":57},[43,11290,88],{"class":49},[43,11292,11144],{"class":100},[43,11294,11147],{"class":57},[43,11296,88],{"class":49},[43,11298,11299],{"class":100},"\"password\"",[43,11301,11302],{"class":49},">비밀번호\u003C\u002F",[43,11304,11137],{"class":53},[43,11306,61],{"class":49},[43,11308,11309,11311,11313,11315,11317,11319],{"class":45,"line":3077},[43,11310,7339],{"class":49},[43,11312,6703],{"class":53},[43,11314,6714],{"class":57},[43,11316,88],{"class":49},[43,11318,11172],{"class":100},[43,11320,61],{"class":49},[43,11322,11323,11325,11327,11329,11331,11333,11336,11338],{"class":45,"line":3082},[43,11324,8017],{"class":49},[43,11326,43],{"class":53},[43,11328,6714],{"class":57},[43,11330,88],{"class":49},[43,11332,11187],{"class":100},[43,11334,11335],{"class":49},">🔒\u003C\u002F",[43,11337,43],{"class":53},[43,11339,61],{"class":49},[43,11341,11342,11344],{"class":45,"line":3088},[43,11343,8017],{"class":49},[43,11345,11201],{"class":53},[43,11347,11348,11350,11352],{"class":45,"line":3093},[43,11349,11206],{"class":57},[43,11351,88],{"class":49},[43,11353,11211],{"class":100},[43,11355,11356,11358,11360],{"class":45,"line":3099},[43,11357,11216],{"class":57},[43,11359,88],{"class":49},[43,11361,11362],{"class":100},"\"password\"\n",[43,11364,11365,11367,11369],{"class":45,"line":3145},[43,11366,11226],{"class":57},[43,11368,88],{"class":49},[43,11370,11362],{"class":100},[43,11372,11373,11375,11377],{"class":45,"line":3152},[43,11374,11236],{"class":57},[43,11376,88],{"class":49},[43,11378,11379],{"class":100},"\"비밀번호를 입력하세요\"\n",[43,11381,11382],{"class":45,"line":3165},[43,11383,11246],{"class":49},[43,11385,11386,11388,11390],{"class":45,"line":3203},[43,11387,7369],{"class":49},[43,11389,6703],{"class":53},[43,11391,61],{"class":49},[43,11393,11394,11396,11398],{"class":45,"line":3208},[43,11395,7378],{"class":49},[43,11397,6703],{"class":53},[43,11399,61],{"class":49},[43,11401,11402],{"class":45,"line":3219},[43,11403,1782],{"emptyLinePlaceholder":491},[43,11405,11406,11408,11410,11412,11414,11417,11420,11422,11425,11428,11430],{"class":45,"line":3227},[43,11407,6726],{"class":49},[43,11409,10437],{"class":53},[43,11411,6714],{"class":57},[43,11413,88],{"class":49},[43,11415,11416],{"class":100},"\"login-form__button\"",[43,11418,11419],{"class":57}," type",[43,11421,88],{"class":49},[43,11423,11424],{"class":100},"\"submit\"",[43,11426,11427],{"class":49},">로그인하기\u003C\u002F",[43,11429,10437],{"class":53},[43,11431,61],{"class":49},[43,11433,11434],{"class":45,"line":3233},[43,11435,1782],{"emptyLinePlaceholder":491},[43,11437,11438,11440,11442,11444,11446,11449],{"class":45,"line":3239},[43,11439,6726],{"class":49},[43,11441,6703],{"class":53},[43,11443,6714],{"class":57},[43,11445,88],{"class":49},[43,11447,11448],{"class":100},"\"login-form__signup\"",[43,11450,61],{"class":49},[43,11452,11453],{"class":45,"line":3251},[43,11454,11455],{"class":49},"      아직 계정이 없으신가요?\n",[43,11457,11458,11460,11462,11465,11467,11470,11472,11474,11477,11480,11482],{"class":45,"line":3256},[43,11459,7339],{"class":49},[43,11461,304],{"class":53},[43,11463,11464],{"class":57}," href",[43,11466,88],{"class":49},[43,11468,11469],{"class":100},"\"#\"",[43,11471,6714],{"class":57},[43,11473,88],{"class":49},[43,11475,11476],{"class":100},"\"login-form__link\"",[43,11478,11479],{"class":49},">회원가입\u003C\u002F",[43,11481,304],{"class":53},[43,11483,61],{"class":49},[43,11485,11486,11488,11490],{"class":45,"line":3261},[43,11487,7378],{"class":49},[43,11489,6703],{"class":53},[43,11491,61],{"class":49},[43,11493,11494,11496,11498],{"class":45,"line":3267},[43,11495,6740],{"class":49},[43,11497,11106],{"class":53},[43,11499,61],{"class":49},[43,11501,11502,11504,11506],{"class":45,"line":3279},[43,11503,179],{"class":49},[43,11505,6703],{"class":53},[43,11507,61],{"class":49},[33,11509,11511],{"className":10460,"code":11510,"language":10462,"meta":38,"style":38},"\n\u002F* 로그인 카드 블록 *\u002F\n.login-card {\n  border-radius: 24px;\n  box-shadow: 0 10px 30px rgba(255, 138, 61, 0.15);\n  width: 100%;\n  max-width: 450px;\n  padding: 40px 30px;\n  position: relative;\n  overflow: hidden;\n}\n\n.login-card::before {\n  content: \"\";\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  height: 6px;\n  background: linear-gradient(to right, #ff8a3d, #ffc53d);\n}\n\n\u002F* 헤더 영역 *\u002F\n.login-card__header {\n  text-align: center;\n  margin-bottom: 30px;\n  position: relative;\n  z-index: 1;\n}\n\n.login-card__title {\n  font-family: \"Jua\", sans-serif;\n  font-size: 28px;\n  color: #ff8a3d;\n  margin-bottom: 8px;\n}\n\n.login-card__description {\n  font-family: \"Gamja Flower\", cursive;\n  font-size: 18px;\n  color: #666;\n}\n\n\u002F* 로그인 폼 *\u002F\n.login-form {\n  position: relative;\n  z-index: 1;\n  width: 100%;\n}\n\n.login-form__field {\n  margin-bottom: 20px;\n  width: 100%;\n}\n\n.login-form__label {\n  display: block;\n  font-family: \"Jua\", sans-serif;\n  font-size: 15px;\n  color: #555;\n  margin-bottom: 8px;\n}\n\n.login-form__input-wrapper {\n  position: relative;\n  width: 100%;\n}\n\n.login-form__icon {\n  position: absolute;\n  left: 15px;\n  top: 50%;\n  transform: translateY(-50%);\n  color: #ff8a3d;\n  font-size: 18px;\n}\n\n.login-form__input {\n  width: 100%;\n  padding: 15px 15px 15px 45px;\n  border: 2px solid #ffe4cc;\n  border-radius: 12px;\n  font-size: 15px;\n  color: #333;\n  transition: all 0.3s ease;\n  font-family: \"Noto Sans KR\", sans-serif;\n  display: block;\n  box-sizing: border-box;\n}\n\n.login-form__input::placeholder {\n  color: #bbb;\n}\n\n.login-form__input:focus {\n  outline: none;\n  border-color: #ff8a3d;\n  box-shadow: 0 0 0 4px rgba(255, 138, 61, 0.1);\n}\n\n\u002F* 로그인 버튼 *\u002F\n.login-form__button {\n  width: 100%;\n  background: linear-gradient(to right, #ff8a3d, #ffc53d);\n  color: white;\n  border: none;\n  border-radius: 50px;\n  padding: 15px;\n  font-family: \"Jua\", sans-serif;\n  font-size: 18px;\n  cursor: pointer;\n  box-shadow: 0 4px 10px rgba(255, 138, 61, 0.3);\n  transition: all 0.3s ease;\n  margin-top: 10px;\n}\n\n.login-form__button:hover {\n  transform: translateY(-2px);\n  box-shadow: 0 6px 15px rgba(255, 138, 61, 0.4);\n}\n\n.login-form__button:active {\n  transform: translateY(1px);\n}\n\n\u002F* 회원가입 링크 *\u002F\n.login-form__signup {\n  text-align: center;\n  margin-top: 20px;\n  font-family: \"Gamja Flower\", cursive;\n  font-size: 16px;\n  color: #666;\n}\n\n.login-form__link {\n  color: #ff8a3d;\n  font-weight: bold;\n  text-decoration: none;\n  margin-left: 5px;\n}\n\n.login-form__link:hover {\n  text-decoration: underline;\n}\n\n",[40,11512,11513,11517,11522,11529,11543,11586,11601,11615,11632,11644,11656,11660,11664,11671,11683,11694,11705,11716,11727,11741,11771,11775,11779,11784,11791,11803,11817,11827,11838,11842,11846,11853,11870,11884,11894,11907,11911,11915,11922,11934,11947,11958,11962,11966,11971,11978,11988,11998,12010,12014,12018,12025,12038,12050,12054,12058,12065,12077,12091,12104,12116,12129,12134,12139,12147,12158,12171,12176,12181,12189,12200,12213,12227,12247,12258,12271,12276,12281,12289,12302,12329,12349,12363,12376,12388,12409,12425,12436,12449,12454,12459,12467,12479,12484,12489,12497,12510,12522,12561,12566,12571,12577,12585,12598,12623,12634,12645,12658,12671,12686,12699,12712,12750,12767,12781,12786,12791,12799,12817,12856,12861,12866,12874,12891,12896,12901,12907,12915,12926,12939,12950,12964,12975,12980,12985,12993,13004,13017,13029,13044,13049,13054,13062,13074],{"__ignoreMap":38},[43,11514,11515],{"class":45,"line":46},[43,11516,1782],{"emptyLinePlaceholder":491},[43,11518,11519],{"class":45,"line":64},[43,11520,11521],{"class":2797},"\u002F* 로그인 카드 블록 *\u002F\n",[43,11523,11524,11527],{"class":45,"line":119},[43,11525,11526],{"class":57},".login-card",[43,11528,116],{"class":49},[43,11530,11531,11534,11536,11539,11541],{"class":45,"line":150},[43,11532,11533],{"class":81},"  border-radius",[43,11535,78],{"class":49},[43,11537,11538],{"class":81},"24",[43,11540,10489],{"class":67},[43,11542,2995],{"class":49},[43,11544,11545,11548,11550,11552,11554,11556,11559,11561,11564,11566,11569,11571,11574,11576,11579,11581,11584],{"class":45,"line":159},[43,11546,11547],{"class":81},"  box-shadow",[43,11549,78],{"class":49},[43,11551,4401],{"class":81},[43,11553,10086],{"class":81},[43,11555,10489],{"class":67},[43,11557,11558],{"class":81}," 30",[43,11560,10489],{"class":67},[43,11562,11563],{"class":81}," rgba",[43,11565,97],{"class":49},[43,11567,11568],{"class":81},"255",[43,11570,104],{"class":49},[43,11572,11573],{"class":81},"138",[43,11575,104],{"class":49},[43,11577,11578],{"class":81},"61",[43,11580,104],{"class":49},[43,11582,11583],{"class":81},"0.15",[43,11585,1160],{"class":49},[43,11587,11588,11591,11593,11596,11599],{"class":45,"line":176},[43,11589,11590],{"class":81},"  width",[43,11592,78],{"class":49},[43,11594,11595],{"class":81},"100",[43,11597,11598],{"class":67},"%",[43,11600,2995],{"class":49},[43,11602,11603,11606,11608,11611,11613],{"class":45,"line":1163},[43,11604,11605],{"class":81},"  max-width",[43,11607,78],{"class":49},[43,11609,11610],{"class":81},"450",[43,11612,10489],{"class":67},[43,11614,2995],{"class":49},[43,11616,11617,11619,11621,11624,11626,11628,11630],{"class":45,"line":1178},[43,11618,10481],{"class":81},[43,11620,78],{"class":49},[43,11622,11623],{"class":81},"40",[43,11625,10489],{"class":67},[43,11627,11558],{"class":81},[43,11629,10489],{"class":67},[43,11631,2995],{"class":49},[43,11633,11634,11637,11639,11642],{"class":45,"line":1184},[43,11635,11636],{"class":81},"  position",[43,11638,78],{"class":49},[43,11640,11641],{"class":81},"relative",[43,11643,2995],{"class":49},[43,11645,11646,11649,11651,11654],{"class":45,"line":1190},[43,11647,11648],{"class":81},"  overflow",[43,11650,78],{"class":49},[43,11652,11653],{"class":81},"hidden",[43,11655,2995],{"class":49},[43,11657,11658],{"class":45,"line":1996},[43,11659,2034],{"class":49},[43,11661,11662],{"class":45,"line":2008},[43,11663,1782],{"emptyLinePlaceholder":491},[43,11665,11666,11669],{"class":45,"line":2025},[43,11667,11668],{"class":57},".login-card::before",[43,11670,116],{"class":49},[43,11672,11673,11676,11678,11681],{"class":45,"line":2031},[43,11674,11675],{"class":81},"  content",[43,11677,78],{"class":49},[43,11679,11680],{"class":100},"\"\"",[43,11682,2995],{"class":49},[43,11684,11685,11687,11689,11692],{"class":45,"line":2037},[43,11686,11636],{"class":81},[43,11688,78],{"class":49},[43,11690,11691],{"class":81},"absolute",[43,11693,2995],{"class":49},[43,11695,11696,11699,11701,11703],{"class":45,"line":2042},[43,11697,11698],{"class":81},"  top",[43,11700,78],{"class":49},[43,11702,4401],{"class":81},[43,11704,2995],{"class":49},[43,11706,11707,11710,11712,11714],{"class":45,"line":2516},[43,11708,11709],{"class":81},"  left",[43,11711,78],{"class":49},[43,11713,4401],{"class":81},[43,11715,2995],{"class":49},[43,11717,11718,11721,11723,11725],{"class":45,"line":2998},[43,11719,11720],{"class":81},"  right",[43,11722,78],{"class":49},[43,11724,4401],{"class":81},[43,11726,2995],{"class":49},[43,11728,11729,11732,11734,11737,11739],{"class":45,"line":3003},[43,11730,11731],{"class":81},"  height",[43,11733,78],{"class":49},[43,11735,11736],{"class":81},"6",[43,11738,10489],{"class":67},[43,11740,2995],{"class":49},[43,11742,11743,11746,11748,11751,11753,11756,11759,11761,11764,11766,11769],{"class":45,"line":3009},[43,11744,11745],{"class":81},"  background",[43,11747,78],{"class":49},[43,11749,11750],{"class":81},"linear-gradient",[43,11752,97],{"class":49},[43,11754,11755],{"class":67},"to",[43,11757,11758],{"class":81}," right",[43,11760,104],{"class":49},[43,11762,11763],{"class":81},"#ff8a3d",[43,11765,104],{"class":49},[43,11767,11768],{"class":81},"#ffc53d",[43,11770,1160],{"class":49},[43,11772,11773],{"class":45,"line":3022},[43,11774,2034],{"class":49},[43,11776,11777],{"class":45,"line":3044},[43,11778,1782],{"emptyLinePlaceholder":491},[43,11780,11781],{"class":45,"line":3058},[43,11782,11783],{"class":2797},"\u002F* 헤더 영역 *\u002F\n",[43,11785,11786,11789],{"class":45,"line":3077},[43,11787,11788],{"class":57},".login-card__header",[43,11790,116],{"class":49},[43,11792,11793,11796,11798,11801],{"class":45,"line":3082},[43,11794,11795],{"class":81},"  text-align",[43,11797,78],{"class":49},[43,11799,11800],{"class":81},"center",[43,11802,2995],{"class":49},[43,11804,11805,11808,11810,11813,11815],{"class":45,"line":3088},[43,11806,11807],{"class":81},"  margin-bottom",[43,11809,78],{"class":49},[43,11811,11812],{"class":81},"30",[43,11814,10489],{"class":67},[43,11816,2995],{"class":49},[43,11818,11819,11821,11823,11825],{"class":45,"line":3093},[43,11820,11636],{"class":81},[43,11822,78],{"class":49},[43,11824,11641],{"class":81},[43,11826,2995],{"class":49},[43,11828,11829,11832,11834,11836],{"class":45,"line":3099},[43,11830,11831],{"class":81},"  z-index",[43,11833,78],{"class":49},[43,11835,905],{"class":81},[43,11837,2995],{"class":49},[43,11839,11840],{"class":45,"line":3145},[43,11841,2034],{"class":49},[43,11843,11844],{"class":45,"line":3152},[43,11845,1782],{"emptyLinePlaceholder":491},[43,11847,11848,11851],{"class":45,"line":3165},[43,11849,11850],{"class":57},".login-card__title",[43,11852,116],{"class":49},[43,11854,11855,11858,11860,11863,11865,11868],{"class":45,"line":3203},[43,11856,11857],{"class":81},"  font-family",[43,11859,78],{"class":49},[43,11861,11862],{"class":100},"\"Jua\"",[43,11864,104],{"class":49},[43,11866,11867],{"class":81},"sans-serif",[43,11869,2995],{"class":49},[43,11871,11872,11875,11877,11880,11882],{"class":45,"line":3208},[43,11873,11874],{"class":81},"  font-size",[43,11876,78],{"class":49},[43,11878,11879],{"class":81},"28",[43,11881,10489],{"class":67},[43,11883,2995],{"class":49},[43,11885,11886,11888,11890,11892],{"class":45,"line":3219},[43,11887,10513],{"class":81},[43,11889,78],{"class":49},[43,11891,11763],{"class":81},[43,11893,2995],{"class":49},[43,11895,11896,11898,11900,11903,11905],{"class":45,"line":3227},[43,11897,11807],{"class":81},[43,11899,78],{"class":49},[43,11901,11902],{"class":81},"8",[43,11904,10489],{"class":67},[43,11906,2995],{"class":49},[43,11908,11909],{"class":45,"line":3233},[43,11910,2034],{"class":49},[43,11912,11913],{"class":45,"line":3239},[43,11914,1782],{"emptyLinePlaceholder":491},[43,11916,11917,11920],{"class":45,"line":3251},[43,11918,11919],{"class":57},".login-card__description",[43,11921,116],{"class":49},[43,11923,11924,11926,11928,11931],{"class":45,"line":3256},[43,11925,11857],{"class":81},[43,11927,78],{"class":49},[43,11929,11930],{"class":100},"\"Gamja Flower\"",[43,11932,11933],{"class":49},", cursive;\n",[43,11935,11936,11938,11940,11943,11945],{"class":45,"line":3261},[43,11937,11874],{"class":81},[43,11939,78],{"class":49},[43,11941,11942],{"class":81},"18",[43,11944,10489],{"class":67},[43,11946,2995],{"class":49},[43,11948,11949,11951,11953,11956],{"class":45,"line":3267},[43,11950,10513],{"class":81},[43,11952,78],{"class":49},[43,11954,11955],{"class":81},"#666",[43,11957,2995],{"class":49},[43,11959,11960],{"class":45,"line":3279},[43,11961,2034],{"class":49},[43,11963,11964],{"class":45,"line":3284},[43,11965,1782],{"emptyLinePlaceholder":491},[43,11967,11968],{"class":45,"line":3290},[43,11969,11970],{"class":2797},"\u002F* 로그인 폼 *\u002F\n",[43,11972,11973,11976],{"class":45,"line":3312},[43,11974,11975],{"class":57},".login-form",[43,11977,116],{"class":49},[43,11979,11980,11982,11984,11986],{"class":45,"line":3317},[43,11981,11636],{"class":81},[43,11983,78],{"class":49},[43,11985,11641],{"class":81},[43,11987,2995],{"class":49},[43,11989,11990,11992,11994,11996],{"class":45,"line":3327},[43,11991,11831],{"class":81},[43,11993,78],{"class":49},[43,11995,905],{"class":81},[43,11997,2995],{"class":49},[43,11999,12000,12002,12004,12006,12008],{"class":45,"line":3337},[43,12001,11590],{"class":81},[43,12003,78],{"class":49},[43,12005,11595],{"class":81},[43,12007,11598],{"class":67},[43,12009,2995],{"class":49},[43,12011,12012],{"class":45,"line":3348},[43,12013,2034],{"class":49},[43,12015,12016],{"class":45,"line":3354},[43,12017,1782],{"emptyLinePlaceholder":491},[43,12019,12020,12023],{"class":45,"line":3360},[43,12021,12022],{"class":57},".login-form__field",[43,12024,116],{"class":49},[43,12026,12027,12029,12031,12034,12036],{"class":45,"line":3365},[43,12028,11807],{"class":81},[43,12030,78],{"class":49},[43,12032,12033],{"class":81},"20",[43,12035,10489],{"class":67},[43,12037,2995],{"class":49},[43,12039,12040,12042,12044,12046,12048],{"class":45,"line":3374},[43,12041,11590],{"class":81},[43,12043,78],{"class":49},[43,12045,11595],{"class":81},[43,12047,11598],{"class":67},[43,12049,2995],{"class":49},[43,12051,12052],{"class":45,"line":3399},[43,12053,2034],{"class":49},[43,12055,12056],{"class":45,"line":3404},[43,12057,1782],{"emptyLinePlaceholder":491},[43,12059,12060,12063],{"class":45,"line":3409},[43,12061,12062],{"class":57},".login-form__label",[43,12064,116],{"class":49},[43,12066,12067,12070,12072,12075],{"class":45,"line":3414},[43,12068,12069],{"class":81},"  display",[43,12071,78],{"class":49},[43,12073,12074],{"class":81},"block",[43,12076,2995],{"class":49},[43,12078,12079,12081,12083,12085,12087,12089],{"class":45,"line":3434},[43,12080,11857],{"class":81},[43,12082,78],{"class":49},[43,12084,11862],{"class":100},[43,12086,104],{"class":49},[43,12088,11867],{"class":81},[43,12090,2995],{"class":49},[43,12092,12093,12095,12097,12100,12102],{"class":45,"line":3449},[43,12094,11874],{"class":81},[43,12096,78],{"class":49},[43,12098,12099],{"class":81},"15",[43,12101,10489],{"class":67},[43,12103,2995],{"class":49},[43,12105,12107,12109,12111,12114],{"class":45,"line":12106},60,[43,12108,10513],{"class":81},[43,12110,78],{"class":49},[43,12112,12113],{"class":81},"#555",[43,12115,2995],{"class":49},[43,12117,12119,12121,12123,12125,12127],{"class":45,"line":12118},61,[43,12120,11807],{"class":81},[43,12122,78],{"class":49},[43,12124,11902],{"class":81},[43,12126,10489],{"class":67},[43,12128,2995],{"class":49},[43,12130,12132],{"class":45,"line":12131},62,[43,12133,2034],{"class":49},[43,12135,12137],{"class":45,"line":12136},63,[43,12138,1782],{"emptyLinePlaceholder":491},[43,12140,12142,12145],{"class":45,"line":12141},64,[43,12143,12144],{"class":57},".login-form__input-wrapper",[43,12146,116],{"class":49},[43,12148,12150,12152,12154,12156],{"class":45,"line":12149},65,[43,12151,11636],{"class":81},[43,12153,78],{"class":49},[43,12155,11641],{"class":81},[43,12157,2995],{"class":49},[43,12159,12161,12163,12165,12167,12169],{"class":45,"line":12160},66,[43,12162,11590],{"class":81},[43,12164,78],{"class":49},[43,12166,11595],{"class":81},[43,12168,11598],{"class":67},[43,12170,2995],{"class":49},[43,12172,12174],{"class":45,"line":12173},67,[43,12175,2034],{"class":49},[43,12177,12179],{"class":45,"line":12178},68,[43,12180,1782],{"emptyLinePlaceholder":491},[43,12182,12184,12187],{"class":45,"line":12183},69,[43,12185,12186],{"class":57},".login-form__icon",[43,12188,116],{"class":49},[43,12190,12192,12194,12196,12198],{"class":45,"line":12191},70,[43,12193,11636],{"class":81},[43,12195,78],{"class":49},[43,12197,11691],{"class":81},[43,12199,2995],{"class":49},[43,12201,12203,12205,12207,12209,12211],{"class":45,"line":12202},71,[43,12204,11709],{"class":81},[43,12206,78],{"class":49},[43,12208,12099],{"class":81},[43,12210,10489],{"class":67},[43,12212,2995],{"class":49},[43,12214,12216,12218,12220,12223,12225],{"class":45,"line":12215},72,[43,12217,11698],{"class":81},[43,12219,78],{"class":49},[43,12221,12222],{"class":81},"50",[43,12224,11598],{"class":67},[43,12226,2995],{"class":49},[43,12228,12230,12233,12235,12238,12240,12243,12245],{"class":45,"line":12229},73,[43,12231,12232],{"class":81},"  transform",[43,12234,78],{"class":49},[43,12236,12237],{"class":81},"translateY",[43,12239,97],{"class":49},[43,12241,12242],{"class":81},"-50",[43,12244,11598],{"class":67},[43,12246,1160],{"class":49},[43,12248,12250,12252,12254,12256],{"class":45,"line":12249},74,[43,12251,10513],{"class":81},[43,12253,78],{"class":49},[43,12255,11763],{"class":81},[43,12257,2995],{"class":49},[43,12259,12261,12263,12265,12267,12269],{"class":45,"line":12260},75,[43,12262,11874],{"class":81},[43,12264,78],{"class":49},[43,12266,11942],{"class":81},[43,12268,10489],{"class":67},[43,12270,2995],{"class":49},[43,12272,12274],{"class":45,"line":12273},76,[43,12275,2034],{"class":49},[43,12277,12279],{"class":45,"line":12278},77,[43,12280,1782],{"emptyLinePlaceholder":491},[43,12282,12284,12287],{"class":45,"line":12283},78,[43,12285,12286],{"class":57},".login-form__input",[43,12288,116],{"class":49},[43,12290,12292,12294,12296,12298,12300],{"class":45,"line":12291},79,[43,12293,11590],{"class":81},[43,12295,78],{"class":49},[43,12297,11595],{"class":81},[43,12299,11598],{"class":67},[43,12301,2995],{"class":49},[43,12303,12305,12307,12309,12311,12313,12316,12318,12320,12322,12325,12327],{"class":45,"line":12304},80,[43,12306,10481],{"class":81},[43,12308,78],{"class":49},[43,12310,12099],{"class":81},[43,12312,10489],{"class":67},[43,12314,12315],{"class":81}," 15",[43,12317,10489],{"class":67},[43,12319,12315],{"class":81},[43,12321,10489],{"class":67},[43,12323,12324],{"class":81}," 45",[43,12326,10489],{"class":67},[43,12328,2995],{"class":49},[43,12330,12332,12335,12337,12339,12341,12344,12347],{"class":45,"line":12331},81,[43,12333,12334],{"class":81},"  border",[43,12336,78],{"class":49},[43,12338,918],{"class":81},[43,12340,10489],{"class":67},[43,12342,12343],{"class":81}," solid",[43,12345,12346],{"class":81}," #ffe4cc",[43,12348,2995],{"class":49},[43,12350,12352,12354,12356,12359,12361],{"class":45,"line":12351},82,[43,12353,11533],{"class":81},[43,12355,78],{"class":49},[43,12357,12358],{"class":81},"12",[43,12360,10489],{"class":67},[43,12362,2995],{"class":49},[43,12364,12366,12368,12370,12372,12374],{"class":45,"line":12365},83,[43,12367,11874],{"class":81},[43,12369,78],{"class":49},[43,12371,12099],{"class":81},[43,12373,10489],{"class":67},[43,12375,2995],{"class":49},[43,12377,12379,12381,12383,12386],{"class":45,"line":12378},84,[43,12380,10513],{"class":81},[43,12382,78],{"class":49},[43,12384,12385],{"class":81},"#333",[43,12387,2995],{"class":49},[43,12389,12391,12394,12396,12398,12401,12404,12407],{"class":45,"line":12390},85,[43,12392,12393],{"class":81},"  transition",[43,12395,78],{"class":49},[43,12397,144],{"class":81},[43,12399,12400],{"class":81}," 0.3",[43,12402,12403],{"class":67},"s",[43,12405,12406],{"class":81}," ease",[43,12408,2995],{"class":49},[43,12410,12412,12414,12416,12419,12421,12423],{"class":45,"line":12411},86,[43,12413,11857],{"class":81},[43,12415,78],{"class":49},[43,12417,12418],{"class":100},"\"Noto Sans KR\"",[43,12420,104],{"class":49},[43,12422,11867],{"class":81},[43,12424,2995],{"class":49},[43,12426,12428,12430,12432,12434],{"class":45,"line":12427},87,[43,12429,12069],{"class":81},[43,12431,78],{"class":49},[43,12433,12074],{"class":81},[43,12435,2995],{"class":49},[43,12437,12439,12442,12444,12447],{"class":45,"line":12438},88,[43,12440,12441],{"class":81},"  box-sizing",[43,12443,78],{"class":49},[43,12445,12446],{"class":81},"border-box",[43,12448,2995],{"class":49},[43,12450,12452],{"class":45,"line":12451},89,[43,12453,2034],{"class":49},[43,12455,12457],{"class":45,"line":12456},90,[43,12458,1782],{"emptyLinePlaceholder":491},[43,12460,12462,12465],{"class":45,"line":12461},91,[43,12463,12464],{"class":57},".login-form__input::placeholder",[43,12466,116],{"class":49},[43,12468,12470,12472,12474,12477],{"class":45,"line":12469},92,[43,12471,10513],{"class":81},[43,12473,78],{"class":49},[43,12475,12476],{"class":81},"#bbb",[43,12478,2995],{"class":49},[43,12480,12482],{"class":45,"line":12481},93,[43,12483,2034],{"class":49},[43,12485,12487],{"class":45,"line":12486},94,[43,12488,1782],{"emptyLinePlaceholder":491},[43,12490,12492,12495],{"class":45,"line":12491},95,[43,12493,12494],{"class":57},".login-form__input:focus",[43,12496,116],{"class":49},[43,12498,12500,12503,12505,12508],{"class":45,"line":12499},96,[43,12501,12502],{"class":81},"  outline",[43,12504,78],{"class":49},[43,12506,12507],{"class":81},"none",[43,12509,2995],{"class":49},[43,12511,12513,12516,12518,12520],{"class":45,"line":12512},97,[43,12514,12515],{"class":81},"  border-color",[43,12517,78],{"class":49},[43,12519,11763],{"class":81},[43,12521,2995],{"class":49},[43,12523,12525,12527,12529,12531,12533,12535,12538,12540,12542,12544,12546,12548,12550,12552,12554,12556,12559],{"class":45,"line":12524},98,[43,12526,11547],{"class":81},[43,12528,78],{"class":49},[43,12530,4401],{"class":81},[43,12532,7645],{"class":81},[43,12534,7645],{"class":81},[43,12536,12537],{"class":81}," 4",[43,12539,10489],{"class":67},[43,12541,11563],{"class":81},[43,12543,97],{"class":49},[43,12545,11568],{"class":81},[43,12547,104],{"class":49},[43,12549,11573],{"class":81},[43,12551,104],{"class":49},[43,12553,11578],{"class":81},[43,12555,104],{"class":49},[43,12557,12558],{"class":81},"0.1",[43,12560,1160],{"class":49},[43,12562,12564],{"class":45,"line":12563},99,[43,12565,2034],{"class":49},[43,12567,12569],{"class":45,"line":12568},100,[43,12570,1782],{"emptyLinePlaceholder":491},[43,12572,12574],{"class":45,"line":12573},101,[43,12575,12576],{"class":2797},"\u002F* 로그인 버튼 *\u002F\n",[43,12578,12580,12583],{"class":45,"line":12579},102,[43,12581,12582],{"class":57},".login-form__button",[43,12584,116],{"class":49},[43,12586,12588,12590,12592,12594,12596],{"class":45,"line":12587},103,[43,12589,11590],{"class":81},[43,12591,78],{"class":49},[43,12593,11595],{"class":81},[43,12595,11598],{"class":67},[43,12597,2995],{"class":49},[43,12599,12601,12603,12605,12607,12609,12611,12613,12615,12617,12619,12621],{"class":45,"line":12600},104,[43,12602,11745],{"class":81},[43,12604,78],{"class":49},[43,12606,11750],{"class":81},[43,12608,97],{"class":49},[43,12610,11755],{"class":67},[43,12612,11758],{"class":81},[43,12614,104],{"class":49},[43,12616,11763],{"class":81},[43,12618,104],{"class":49},[43,12620,11768],{"class":81},[43,12622,1160],{"class":49},[43,12624,12626,12628,12630,12632],{"class":45,"line":12625},105,[43,12627,10513],{"class":81},[43,12629,78],{"class":49},[43,12631,10518],{"class":81},[43,12633,2995],{"class":49},[43,12635,12637,12639,12641,12643],{"class":45,"line":12636},106,[43,12638,12334],{"class":81},[43,12640,78],{"class":49},[43,12642,12507],{"class":81},[43,12644,2995],{"class":49},[43,12646,12648,12650,12652,12654,12656],{"class":45,"line":12647},107,[43,12649,11533],{"class":81},[43,12651,78],{"class":49},[43,12653,12222],{"class":81},[43,12655,10489],{"class":67},[43,12657,2995],{"class":49},[43,12659,12661,12663,12665,12667,12669],{"class":45,"line":12660},108,[43,12662,10481],{"class":81},[43,12664,78],{"class":49},[43,12666,12099],{"class":81},[43,12668,10489],{"class":67},[43,12670,2995],{"class":49},[43,12672,12674,12676,12678,12680,12682,12684],{"class":45,"line":12673},109,[43,12675,11857],{"class":81},[43,12677,78],{"class":49},[43,12679,11862],{"class":100},[43,12681,104],{"class":49},[43,12683,11867],{"class":81},[43,12685,2995],{"class":49},[43,12687,12689,12691,12693,12695,12697],{"class":45,"line":12688},110,[43,12690,11874],{"class":81},[43,12692,78],{"class":49},[43,12694,11942],{"class":81},[43,12696,10489],{"class":67},[43,12698,2995],{"class":49},[43,12700,12702,12705,12707,12710],{"class":45,"line":12701},111,[43,12703,12704],{"class":81},"  cursor",[43,12706,78],{"class":49},[43,12708,12709],{"class":81},"pointer",[43,12711,2995],{"class":49},[43,12713,12715,12717,12719,12721,12723,12725,12727,12729,12731,12733,12735,12737,12739,12741,12743,12745,12748],{"class":45,"line":12714},112,[43,12716,11547],{"class":81},[43,12718,78],{"class":49},[43,12720,4401],{"class":81},[43,12722,12537],{"class":81},[43,12724,10489],{"class":67},[43,12726,10086],{"class":81},[43,12728,10489],{"class":67},[43,12730,11563],{"class":81},[43,12732,97],{"class":49},[43,12734,11568],{"class":81},[43,12736,104],{"class":49},[43,12738,11573],{"class":81},[43,12740,104],{"class":49},[43,12742,11578],{"class":81},[43,12744,104],{"class":49},[43,12746,12747],{"class":81},"0.3",[43,12749,1160],{"class":49},[43,12751,12753,12755,12757,12759,12761,12763,12765],{"class":45,"line":12752},113,[43,12754,12393],{"class":81},[43,12756,78],{"class":49},[43,12758,144],{"class":81},[43,12760,12400],{"class":81},[43,12762,12403],{"class":67},[43,12764,12406],{"class":81},[43,12766,2995],{"class":49},[43,12768,12770,12773,12775,12777,12779],{"class":45,"line":12769},114,[43,12771,12772],{"class":81},"  margin-top",[43,12774,78],{"class":49},[43,12776,10486],{"class":81},[43,12778,10489],{"class":67},[43,12780,2995],{"class":49},[43,12782,12784],{"class":45,"line":12783},115,[43,12785,2034],{"class":49},[43,12787,12789],{"class":45,"line":12788},116,[43,12790,1782],{"emptyLinePlaceholder":491},[43,12792,12794,12797],{"class":45,"line":12793},117,[43,12795,12796],{"class":57},".login-form__button:hover",[43,12798,116],{"class":49},[43,12800,12802,12804,12806,12808,12810,12813,12815],{"class":45,"line":12801},118,[43,12803,12232],{"class":81},[43,12805,78],{"class":49},[43,12807,12237],{"class":81},[43,12809,97],{"class":49},[43,12811,12812],{"class":81},"-2",[43,12814,10489],{"class":67},[43,12816,1160],{"class":49},[43,12818,12820,12822,12824,12826,12829,12831,12833,12835,12837,12839,12841,12843,12845,12847,12849,12851,12854],{"class":45,"line":12819},119,[43,12821,11547],{"class":81},[43,12823,78],{"class":49},[43,12825,4401],{"class":81},[43,12827,12828],{"class":81}," 6",[43,12830,10489],{"class":67},[43,12832,12315],{"class":81},[43,12834,10489],{"class":67},[43,12836,11563],{"class":81},[43,12838,97],{"class":49},[43,12840,11568],{"class":81},[43,12842,104],{"class":49},[43,12844,11573],{"class":81},[43,12846,104],{"class":49},[43,12848,11578],{"class":81},[43,12850,104],{"class":49},[43,12852,12853],{"class":81},"0.4",[43,12855,1160],{"class":49},[43,12857,12859],{"class":45,"line":12858},120,[43,12860,2034],{"class":49},[43,12862,12864],{"class":45,"line":12863},121,[43,12865,1782],{"emptyLinePlaceholder":491},[43,12867,12869,12872],{"class":45,"line":12868},122,[43,12870,12871],{"class":57},".login-form__button:active",[43,12873,116],{"class":49},[43,12875,12877,12879,12881,12883,12885,12887,12889],{"class":45,"line":12876},123,[43,12878,12232],{"class":81},[43,12880,78],{"class":49},[43,12882,12237],{"class":81},[43,12884,97],{"class":49},[43,12886,905],{"class":81},[43,12888,10489],{"class":67},[43,12890,1160],{"class":49},[43,12892,12894],{"class":45,"line":12893},124,[43,12895,2034],{"class":49},[43,12897,12899],{"class":45,"line":12898},125,[43,12900,1782],{"emptyLinePlaceholder":491},[43,12902,12904],{"class":45,"line":12903},126,[43,12905,12906],{"class":2797},"\u002F* 회원가입 링크 *\u002F\n",[43,12908,12910,12913],{"class":45,"line":12909},127,[43,12911,12912],{"class":57},".login-form__signup",[43,12914,116],{"class":49},[43,12916,12918,12920,12922,12924],{"class":45,"line":12917},128,[43,12919,11795],{"class":81},[43,12921,78],{"class":49},[43,12923,11800],{"class":81},[43,12925,2995],{"class":49},[43,12927,12929,12931,12933,12935,12937],{"class":45,"line":12928},129,[43,12930,12772],{"class":81},[43,12932,78],{"class":49},[43,12934,12033],{"class":81},[43,12936,10489],{"class":67},[43,12938,2995],{"class":49},[43,12940,12942,12944,12946,12948],{"class":45,"line":12941},130,[43,12943,11857],{"class":81},[43,12945,78],{"class":49},[43,12947,11930],{"class":100},[43,12949,11933],{"class":49},[43,12951,12953,12955,12957,12960,12962],{"class":45,"line":12952},131,[43,12954,11874],{"class":81},[43,12956,78],{"class":49},[43,12958,12959],{"class":81},"16",[43,12961,10489],{"class":67},[43,12963,2995],{"class":49},[43,12965,12967,12969,12971,12973],{"class":45,"line":12966},132,[43,12968,10513],{"class":81},[43,12970,78],{"class":49},[43,12972,11955],{"class":81},[43,12974,2995],{"class":49},[43,12976,12978],{"class":45,"line":12977},133,[43,12979,2034],{"class":49},[43,12981,12983],{"class":45,"line":12982},134,[43,12984,1782],{"emptyLinePlaceholder":491},[43,12986,12988,12991],{"class":45,"line":12987},135,[43,12989,12990],{"class":57},".login-form__link",[43,12992,116],{"class":49},[43,12994,12996,12998,13000,13002],{"class":45,"line":12995},136,[43,12997,10513],{"class":81},[43,12999,78],{"class":49},[43,13001,11763],{"class":81},[43,13003,2995],{"class":49},[43,13005,13007,13010,13012,13015],{"class":45,"line":13006},137,[43,13008,13009],{"class":81},"  font-weight",[43,13011,78],{"class":49},[43,13013,13014],{"class":81},"bold",[43,13016,2995],{"class":49},[43,13018,13020,13023,13025,13027],{"class":45,"line":13019},138,[43,13021,13022],{"class":81},"  text-decoration",[43,13024,78],{"class":49},[43,13026,12507],{"class":81},[43,13028,2995],{"class":49},[43,13030,13032,13035,13037,13040,13042],{"class":45,"line":13031},139,[43,13033,13034],{"class":81},"  margin-left",[43,13036,78],{"class":49},[43,13038,13039],{"class":81},"5",[43,13041,10489],{"class":67},[43,13043,2995],{"class":49},[43,13045,13047],{"class":45,"line":13046},140,[43,13048,2034],{"class":49},[43,13050,13052],{"class":45,"line":13051},141,[43,13053,1782],{"emptyLinePlaceholder":491},[43,13055,13057,13060],{"class":45,"line":13056},142,[43,13058,13059],{"class":57},".login-form__link:hover",[43,13061,116],{"class":49},[43,13063,13065,13067,13069,13072],{"class":45,"line":13064},143,[43,13066,13022],{"class":81},[43,13068,78],{"class":49},[43,13070,13071],{"class":81},"underline",[43,13073,2995],{"class":49},[43,13075,13077],{"class":45,"line":13076},144,[43,13078,2034],{"class":49},[471,13080,13081],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":38,"searchDepth":64,"depth":64,"links":13083},[13084],{"id":10999,"depth":64,"text":10999},"로그인 UI를 그려보자",{"date":13087,"field":5954,"tags":13088},"2025.03.29",[11011,10462],"\u002Fko\u002Fcss-login-form",{"title":10999,"description":13085},{"loc":13089},"ko\u002Fcss-login-form","UvtTOPAVyeRhDCxXgjJtTKcKTmrPHnoTk6Zn68nrQgQ",{"id":13095,"title":13096,"body":13097,"description":14030,"extension":484,"meta":14031,"navigation":491,"path":14033,"seo":14034,"sitemap":14035,"stem":14036,"__hash__":14037},"ko\u002Fko\u002Fnuxt-colormode.md","Nuxt3 다크모드 적용하기",{"type":8,"value":13098,"toc":14014},[13099,13102,13106,13113,13134,13138,13144,13178,13188,13196,13201,13220,13226,13306,13311,13333,13336,13424,13430,13441,13447,13535,13538,13542,13545,13811,13815,13824,13897,13900,13904,13911,13996,13999,14006,14011],[15,13100,13101],{},"Nuxt3에서 다크모드를 적용하고 관리하는 방법을 알아보자.",[29,13103,13105],{"id":13104},"필요한-모듈-설치","필요한 모듈 설치",[15,13107,13108,13109,13112],{},"Nuxt3에서 다크모드를 사용하려면 ",[40,13110,13111],{},"@nuxtjs\u002Fcolor-mode"," 모듈을 설치해야 한다.",[33,13114,13116],{"className":850,"code":13115,"language":852,"meta":38,"style":38},"npx nuxi module add color-mode\n",[40,13117,13118],{"__ignoreMap":38},[43,13119,13120,13123,13126,13128,13131],{"class":45,"line":46},[43,13121,13122],{"class":57},"npx",[43,13124,13125],{"class":100}," nuxi",[43,13127,6980],{"class":100},[43,13129,13130],{"class":100}," add",[43,13132,13133],{"class":100}," color-mode\n",[29,13135,13137],{"id":13136},"nuxtconfigts-설정","nuxt.config.ts 설정",[15,13139,13140,13141,13143],{},"설치가 끝났으면 ",[40,13142,1020],{}," 파일에서 모듈을 추가해야 한다.",[33,13145,13147],{"className":6038,"code":13146,"language":6040,"meta":38,"style":38},"export default defineNuxtConfig({\n  modules: [\n    '@nuxtjs\u002Fcolor-mode'\n  ]\n})\n",[40,13148,13149,13159,13164,13169,13174],{"__ignoreMap":38},[43,13150,13151,13153,13155,13157],{"class":45,"line":46},[43,13152,6052],{"class":67},[43,13154,6055],{"class":67},[43,13156,6058],{"class":57},[43,13158,1803],{"class":49},[43,13160,13161],{"class":45,"line":64},[43,13162,13163],{"class":49},"  modules: [\n",[43,13165,13166],{"class":45,"line":119},[43,13167,13168],{"class":100},"    '@nuxtjs\u002Fcolor-mode'\n",[43,13170,13171],{"class":45,"line":150},[43,13172,13173],{"class":49},"  ]\n",[43,13175,13176],{"class":45,"line":159},[43,13177,6117],{"class":49},[29,13179,13181,1318,13184,13187],{"id":13180},"usecolormode와-colormode-차이점",[40,13182,13183],{},"useColorMode()",[40,13185,13186],{},"$colorMode"," 차이점",[15,13189,13190,13191,1318,13193,13195],{},"Nuxt에서 컬러 모드를 다룰 때는 ",[40,13192,13183],{},[40,13194,13186],{}," 두 가지 방법이 있다.",[1273,13197,1490,13199],{"id":13198},"_1-colormode",[40,13200,13186],{},[185,13202,13203,13208,13214],{},[188,13204,13205],{},[21,13206,13207],{},"템플릿(HTML)에서 바로 접근 가능",[188,13209,13210,13213],{},[40,13211,13212],{},"$colorMode.value","를 사용해서 현재 컬러 모드를 가져올 수 있음",[188,13215,13216,13219],{},[40,13217,13218],{},"preference"," 값을 변경하면 컬러 모드가 바뀜",[15,13221,13222,13223,13225],{},"템플릿에서 다크모드 버튼을 만들 때 ",[40,13224,13212],{},"를 활용할 수 있다.",[33,13227,13229],{"className":35,"code":13228,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cbutton @click=\"$colorMode.preference = $colorMode.value === 'light' ? 'dark' : 'light'\">\n    \u003Cspan v-if=\"$colorMode.value === 'light'\">라이트 모드\u003C\u002Fspan>\n    \u003Cspan v-else>다크 모드\u003C\u002Fspan>\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[40,13230,13231,13239,13255,13275,13290,13298],{"__ignoreMap":38},[43,13232,13233,13235,13237],{"class":45,"line":46},[43,13234,50],{"class":49},[43,13236,6693],{"class":53},[43,13238,61],{"class":49},[43,13240,13241,13243,13245,13248,13250,13253],{"class":45,"line":64},[43,13242,6700],{"class":49},[43,13244,10437],{"class":53},[43,13246,13247],{"class":57}," @click",[43,13249,88],{"class":49},[43,13251,13252],{"class":100},"\"$colorMode.preference = $colorMode.value === 'light' ? 'dark' : 'light'\"",[43,13254,61],{"class":49},[43,13256,13257,13259,13261,13263,13265,13268,13271,13273],{"class":45,"line":119},[43,13258,6726],{"class":49},[43,13260,43],{"class":53},[43,13262,7319],{"class":57},[43,13264,88],{"class":49},[43,13266,13267],{"class":100},"\"$colorMode.value === 'light'\"",[43,13269,13270],{"class":49},">라이트 모드\u003C\u002F",[43,13272,43],{"class":53},[43,13274,61],{"class":49},[43,13276,13277,13279,13281,13283,13286,13288],{"class":45,"line":150},[43,13278,6726],{"class":49},[43,13280,43],{"class":53},[43,13282,7399],{"class":57},[43,13284,13285],{"class":49},">다크 모드\u003C\u002F",[43,13287,43],{"class":53},[43,13289,61],{"class":49},[43,13291,13292,13294,13296],{"class":45,"line":159},[43,13293,6740],{"class":49},[43,13295,10437],{"class":53},[43,13297,61],{"class":49},[43,13299,13300,13302,13304],{"class":45,"line":176},[43,13301,179],{"class":49},[43,13303,6693],{"class":53},[43,13305,61],{"class":49},[1273,13307,1500,13309],{"id":13308},"_2-usecolormode",[40,13310,13183],{},[185,13312,13313,13321,13327],{},[188,13314,13315],{},[21,13316,13317,13320],{},[40,13318,13319],{},"setup()"," 함수에서 접근할 때 사용",[188,13322,13323,13326],{},[40,13324,13325],{},"const colorMode = useColorMode()","로 가져와야 함",[188,13328,13329,13332],{},[40,13330,13331],{},"colorMode.preference","를 변경하면 컬러 모드가 바뀜",[15,13334,13335],{},"컴포넌트 내부에서 컬러 모드를 제어할 때 사용한다.",[33,13337,13339],{"className":35,"code":13338,"language":37,"meta":38,"style":38},"\u003Cscript setup>\nconst colorMode = useColorMode();\n\nconst toggleColorMode = () => {\n  colorMode.preference = colorMode.value === 'light' ? 'dark' : 'light';\n};\n\u003C\u002Fscript>\n",[40,13340,13341,13351,13365,13369,13384,13412,13416],{"__ignoreMap":38},[43,13342,13343,13345,13347,13349],{"class":45,"line":46},[43,13344,50],{"class":49},[43,13346,54],{"class":53},[43,13348,58],{"class":57},[43,13350,61],{"class":49},[43,13352,13353,13355,13358,13360,13363],{"class":45,"line":64},[43,13354,68],{"class":67},[43,13356,13357],{"class":81}," colorMode",[43,13359,128],{"class":67},[43,13361,13362],{"class":57}," useColorMode",[43,13364,147],{"class":49},[43,13366,13367],{"class":45,"line":119},[43,13368,1782],{"emptyLinePlaceholder":491},[43,13370,13371,13373,13376,13378,13380,13382],{"class":45,"line":150},[43,13372,68],{"class":67},[43,13374,13375],{"class":57}," toggleColorMode",[43,13377,128],{"class":67},[43,13379,110],{"class":49},[43,13381,113],{"class":67},[43,13383,116],{"class":49},[43,13385,13386,13389,13391,13394,13396,13399,13402,13405,13408,13410],{"class":45,"line":159},[43,13387,13388],{"class":49},"  colorMode.preference ",[43,13390,88],{"class":67},[43,13392,13393],{"class":49}," colorMode.value ",[43,13395,4914],{"class":67},[43,13397,13398],{"class":100}," 'light'",[43,13400,13401],{"class":67}," ?",[43,13403,13404],{"class":100}," 'dark'",[43,13406,13407],{"class":67}," :",[43,13409,13398],{"class":100},[43,13411,2995],{"class":49},[43,13413,13414],{"class":45,"line":176},[43,13415,9424],{"class":49},[43,13417,13418,13420,13422],{"class":45,"line":1163},[43,13419,179],{"class":49},[43,13421,54],{"class":53},[43,13423,61],{"class":49},[29,13425,13427,13429],{"id":13426},"usecolormode를-직접-불러와서-사용하는-이유",[40,13428,13183],{},"를 직접 불러와서 사용하는 이유",[15,13431,13432,13434,13435],{},[40,13433,13186],{},"는 템플릿 안에서만 사용할 수 있기 때문에, ",[21,13436,13437,13438,13440],{},"스크립트(setup 함수)에서 다루려면 ",[40,13439,13183],{},"를 사용해야 한다.",[15,13442,13443,13444,13446],{},"예를 들어, 다크모드를 자동으로 설정하는 로직을 추가하고 싶다면 ",[40,13445,13183],{},"를 활용해야 한다.",[33,13448,13450],{"className":35,"code":13449,"language":37,"meta":38,"style":38},"\u003Cscript setup>\nconst colorMode = useColorMode();\n\n\u002F\u002F 사용자의 기본 설정을 확인하고 자동으로 변경\nif (colorMode.value === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n  colorMode.preference = 'dark';\n}\n\u003C\u002Fscript>\n",[40,13451,13452,13462,13474,13478,13483,13513,13523,13527],{"__ignoreMap":38},[43,13453,13454,13456,13458,13460],{"class":45,"line":46},[43,13455,50],{"class":49},[43,13457,54],{"class":53},[43,13459,58],{"class":57},[43,13461,61],{"class":49},[43,13463,13464,13466,13468,13470,13472],{"class":45,"line":64},[43,13465,68],{"class":67},[43,13467,13357],{"class":81},[43,13469,128],{"class":67},[43,13471,13362],{"class":57},[43,13473,147],{"class":49},[43,13475,13476],{"class":45,"line":119},[43,13477,1782],{"emptyLinePlaceholder":491},[43,13479,13480],{"class":45,"line":150},[43,13481,13482],{"class":2797},"\u002F\u002F 사용자의 기본 설정을 확인하고 자동으로 변경\n",[43,13484,13485,13488,13491,13493,13496,13499,13502,13505,13507,13510],{"class":45,"line":159},[43,13486,13487],{"class":67},"if",[43,13489,13490],{"class":49}," (colorMode.value ",[43,13492,4914],{"class":67},[43,13494,13495],{"class":100}," 'system'",[43,13497,13498],{"class":67}," &&",[43,13500,13501],{"class":49}," window.",[43,13503,13504],{"class":57},"matchMedia",[43,13506,97],{"class":49},[43,13508,13509],{"class":100},"'(prefers-color-scheme: dark)'",[43,13511,13512],{"class":49},").matches) {\n",[43,13514,13515,13517,13519,13521],{"class":45,"line":176},[43,13516,13388],{"class":49},[43,13518,88],{"class":67},[43,13520,13404],{"class":100},[43,13522,2995],{"class":49},[43,13524,13525],{"class":45,"line":1163},[43,13526,2034],{"class":49},[43,13528,13529,13531,13533],{"class":45,"line":1178},[43,13530,179],{"class":49},[43,13532,54],{"class":53},[43,13534,61],{"class":49},[15,13536,13537],{},"이렇게 하면 사용자의 OS 설정이 다크 모드라면 자동으로 다크 모드를 적용할 수 있다.",[29,13539,13541],{"id":13540},"다크모드-토글-버튼-예제","다크모드 토글 버튼 예제",[15,13543,13544],{},"위 내용을 바탕으로, 다크모드 토글 기능을 구현해보자.",[33,13546,13548],{"className":35,"code":13547,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cheader class=\"header\">\n    \u003Cdiv class=\"header__container\">\n      \u003Cpre>{{ $colorMode }}\u003C\u002Fpre>\n\n      \u003C!-- 우측 액션 영역 (검색, 테마 전환 등) -->\n      \u003Cdiv class=\"header__actions\">\n        \u003C!-- 테마 전환 버튼 -->\n        \u003Cbutton class=\"header__theme-btn\" @click=\"toggleColorMode\">\n          \u003Cspan v-if=\"$colorMode.value === 'light'\">라이트\u003C\u002Fspan>\n          \u003Cspan v-else>다크\u003C\u002Fspan>\n        \u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fheader>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nconst colorMode = useColorMode(); \u002F\u002F Nuxt 제공 훅\n\nconst toggleColorMode = () => {\n  colorMode.preference = colorMode.value === 'light' ? 'dark' : 'light';\n};\n\u003C\u002Fscript>\n",[40,13549,13550,13558,13574,13589,13602,13606,13611,13626,13631,13653,13673,13688,13697,13705,13713,13721,13729,13733,13743,13759,13763,13777,13799,13803],{"__ignoreMap":38},[43,13551,13552,13554,13556],{"class":45,"line":46},[43,13553,50],{"class":49},[43,13555,6693],{"class":53},[43,13557,61],{"class":49},[43,13559,13560,13562,13565,13567,13569,13572],{"class":45,"line":64},[43,13561,6700],{"class":49},[43,13563,13564],{"class":53},"header",[43,13566,6714],{"class":57},[43,13568,88],{"class":49},[43,13570,13571],{"class":100},"\"header\"",[43,13573,61],{"class":49},[43,13575,13576,13578,13580,13582,13584,13587],{"class":45,"line":119},[43,13577,6726],{"class":49},[43,13579,6703],{"class":53},[43,13581,6714],{"class":57},[43,13583,88],{"class":49},[43,13585,13586],{"class":100},"\"header__container\"",[43,13588,61],{"class":49},[43,13590,13591,13593,13595,13598,13600],{"class":45,"line":150},[43,13592,7339],{"class":49},[43,13594,33],{"class":53},[43,13596,13597],{"class":49},">{{ $colorMode }}\u003C\u002F",[43,13599,33],{"class":53},[43,13601,61],{"class":49},[43,13603,13604],{"class":45,"line":159},[43,13605,1782],{"emptyLinePlaceholder":491},[43,13607,13608],{"class":45,"line":176},[43,13609,13610],{"class":2797},"      \u003C!-- 우측 액션 영역 (검색, 테마 전환 등) -->\n",[43,13612,13613,13615,13617,13619,13621,13624],{"class":45,"line":1163},[43,13614,7339],{"class":49},[43,13616,6703],{"class":53},[43,13618,6714],{"class":57},[43,13620,88],{"class":49},[43,13622,13623],{"class":100},"\"header__actions\"",[43,13625,61],{"class":49},[43,13627,13628],{"class":45,"line":1178},[43,13629,13630],{"class":2797},"        \u003C!-- 테마 전환 버튼 -->\n",[43,13632,13633,13635,13637,13639,13641,13644,13646,13648,13651],{"class":45,"line":1184},[43,13634,8017],{"class":49},[43,13636,10437],{"class":53},[43,13638,6714],{"class":57},[43,13640,88],{"class":49},[43,13642,13643],{"class":100},"\"header__theme-btn\"",[43,13645,13247],{"class":57},[43,13647,88],{"class":49},[43,13649,13650],{"class":100},"\"toggleColorMode\"",[43,13652,61],{"class":49},[43,13654,13655,13658,13660,13662,13664,13666,13669,13671],{"class":45,"line":1190},[43,13656,13657],{"class":49},"          \u003C",[43,13659,43],{"class":53},[43,13661,7319],{"class":57},[43,13663,88],{"class":49},[43,13665,13267],{"class":100},[43,13667,13668],{"class":49},">라이트\u003C\u002F",[43,13670,43],{"class":53},[43,13672,61],{"class":49},[43,13674,13675,13677,13679,13681,13684,13686],{"class":45,"line":1996},[43,13676,13657],{"class":49},[43,13678,43],{"class":53},[43,13680,7399],{"class":57},[43,13682,13683],{"class":49},">다크\u003C\u002F",[43,13685,43],{"class":53},[43,13687,61],{"class":49},[43,13689,13690,13693,13695],{"class":45,"line":2008},[43,13691,13692],{"class":49},"        \u003C\u002F",[43,13694,10437],{"class":53},[43,13696,61],{"class":49},[43,13698,13699,13701,13703],{"class":45,"line":2025},[43,13700,7369],{"class":49},[43,13702,6703],{"class":53},[43,13704,61],{"class":49},[43,13706,13707,13709,13711],{"class":45,"line":2031},[43,13708,7378],{"class":49},[43,13710,6703],{"class":53},[43,13712,61],{"class":49},[43,13714,13715,13717,13719],{"class":45,"line":2037},[43,13716,6740],{"class":49},[43,13718,13564],{"class":53},[43,13720,61],{"class":49},[43,13722,13723,13725,13727],{"class":45,"line":2042},[43,13724,179],{"class":49},[43,13726,6693],{"class":53},[43,13728,61],{"class":49},[43,13730,13731],{"class":45,"line":2516},[43,13732,1782],{"emptyLinePlaceholder":491},[43,13734,13735,13737,13739,13741],{"class":45,"line":2998},[43,13736,50],{"class":49},[43,13738,54],{"class":53},[43,13740,58],{"class":57},[43,13742,61],{"class":49},[43,13744,13745,13747,13749,13751,13753,13756],{"class":45,"line":3003},[43,13746,68],{"class":67},[43,13748,13357],{"class":81},[43,13750,128],{"class":67},[43,13752,13362],{"class":57},[43,13754,13755],{"class":49},"(); ",[43,13757,13758],{"class":2797},"\u002F\u002F Nuxt 제공 훅\n",[43,13760,13761],{"class":45,"line":3009},[43,13762,1782],{"emptyLinePlaceholder":491},[43,13764,13765,13767,13769,13771,13773,13775],{"class":45,"line":3022},[43,13766,68],{"class":67},[43,13768,13375],{"class":57},[43,13770,128],{"class":67},[43,13772,110],{"class":49},[43,13774,113],{"class":67},[43,13776,116],{"class":49},[43,13778,13779,13781,13783,13785,13787,13789,13791,13793,13795,13797],{"class":45,"line":3044},[43,13780,13388],{"class":49},[43,13782,88],{"class":67},[43,13784,13393],{"class":49},[43,13786,4914],{"class":67},[43,13788,13398],{"class":100},[43,13790,13401],{"class":67},[43,13792,13404],{"class":100},[43,13794,13407],{"class":67},[43,13796,13398],{"class":100},[43,13798,2995],{"class":49},[43,13800,13801],{"class":45,"line":3058},[43,13802,9424],{"class":49},[43,13804,13805,13807,13809],{"class":45,"line":3077},[43,13806,179],{"class":49},[43,13808,54],{"class":53},[43,13810,61],{"class":49},[29,13812,13814],{"id":13813},"다크모드-초기-설정","다크모드 초기 설정",[15,13816,13817,13818,4081,13820,13823],{},"기본적으로 Nuxt는 시스템 설정을 따라가지만, 특정 모드를 기본값으로 설정하고 싶다면 ",[40,13819,1020],{},[40,13821,13822],{},"colorMode"," 옵션을 설정하면 된다.",[33,13825,13827],{"className":6038,"code":13826,"language":6040,"meta":38,"style":38},"export default defineNuxtConfig({\n  modules: ['@nuxtjs\u002Fcolor-mode'],\n  colorMode: {\n    preference: 'dark', \u002F\u002F 기본 다크모드 설정\n    fallback: 'light', \u002F\u002F 감지할 수 없을 때 사용할 기본 모드\n    classSuffix: '',\n  }\n})\n",[40,13828,13829,13839,13849,13854,13867,13880,13889,13893],{"__ignoreMap":38},[43,13830,13831,13833,13835,13837],{"class":45,"line":46},[43,13832,6052],{"class":67},[43,13834,6055],{"class":67},[43,13836,6058],{"class":57},[43,13838,1803],{"class":49},[43,13840,13841,13844,13847],{"class":45,"line":64},[43,13842,13843],{"class":49},"  modules: [",[43,13845,13846],{"class":100},"'@nuxtjs\u002Fcolor-mode'",[43,13848,8282],{"class":49},[43,13850,13851],{"class":45,"line":119},[43,13852,13853],{"class":49},"  colorMode: {\n",[43,13855,13856,13859,13862,13864],{"class":45,"line":150},[43,13857,13858],{"class":49},"    preference: ",[43,13860,13861],{"class":100},"'dark'",[43,13863,104],{"class":49},[43,13865,13866],{"class":2797},"\u002F\u002F 기본 다크모드 설정\n",[43,13868,13869,13872,13875,13877],{"class":45,"line":159},[43,13870,13871],{"class":49},"    fallback: ",[43,13873,13874],{"class":100},"'light'",[43,13876,104],{"class":49},[43,13878,13879],{"class":2797},"\u002F\u002F 감지할 수 없을 때 사용할 기본 모드\n",[43,13881,13882,13885,13887],{"class":45,"line":176},[43,13883,13884],{"class":49},"    classSuffix: ",[43,13886,1152],{"class":100},[43,13888,1056],{"class":49},[43,13890,13891],{"class":45,"line":1163},[43,13892,2028],{"class":49},[43,13894,13895],{"class":45,"line":1178},[43,13896,6117],{"class":49},[15,13898,13899],{},"이렇게 하면 웹사이트에 처음 접속했을 때 다크모드가 적용된다.",[29,13901,13903],{"id":13902},"다크모드-스타일-적용","다크모드 스타일 적용",[15,13905,13906,13907,13910],{},"Nuxt에서는 컬러 모드에 따라 ",[40,13908,13909],{},"\u003Chtml>"," 태그에 자동으로 클래스가 추가된다.",[33,13912,13914],{"className":10460,"code":13913,"language":10462,"meta":38,"style":38},"\u002F* 라이트 모드 스타일 *\u002F\nbody {\n  background-color: white;\n  color: black;\n}\n\n\u002F* 다크 모드 스타일 *\u002F\n.dark body {\n  background-color: black;\n  color: white;\n}\n",[40,13915,13916,13921,13928,13938,13949,13953,13957,13962,13972,13982,13992],{"__ignoreMap":38},[43,13917,13918],{"class":45,"line":46},[43,13919,13920],{"class":2797},"\u002F* 라이트 모드 스타일 *\u002F\n",[43,13922,13923,13926],{"class":45,"line":64},[43,13924,13925],{"class":53},"body",[43,13927,116],{"class":49},[43,13929,13930,13932,13934,13936],{"class":45,"line":119},[43,13931,10501],{"class":81},[43,13933,78],{"class":49},[43,13935,10518],{"class":81},[43,13937,2995],{"class":49},[43,13939,13940,13942,13944,13947],{"class":45,"line":150},[43,13941,10513],{"class":81},[43,13943,78],{"class":49},[43,13945,13946],{"class":81},"black",[43,13948,2995],{"class":49},[43,13950,13951],{"class":45,"line":159},[43,13952,2034],{"class":49},[43,13954,13955],{"class":45,"line":176},[43,13956,1782],{"emptyLinePlaceholder":491},[43,13958,13959],{"class":45,"line":1163},[43,13960,13961],{"class":2797},"\u002F* 다크 모드 스타일 *\u002F\n",[43,13963,13964,13967,13970],{"class":45,"line":1178},[43,13965,13966],{"class":57},".dark",[43,13968,13969],{"class":53}," body",[43,13971,116],{"class":49},[43,13973,13974,13976,13978,13980],{"class":45,"line":1184},[43,13975,10501],{"class":81},[43,13977,78],{"class":49},[43,13979,13946],{"class":81},[43,13981,2995],{"class":49},[43,13983,13984,13986,13988,13990],{"class":45,"line":1190},[43,13985,10513],{"class":81},[43,13987,78],{"class":49},[43,13989,10518],{"class":81},[43,13991,2995],{"class":49},[43,13993,13994],{"class":45,"line":1996},[43,13995,2034],{"class":49},[15,13997,13998],{},"이제 다크모드를 적용한 Nuxt3 프로젝트를 만들 수 있다.",[15,14000,14001,14002],{},"📌 공식 문서 참고: ",[304,14003,14004],{"href":14004,"rel":14005},"https:\u002F\u002Fnuxt.com\u002Fmodules\u002Fcolor-mode",[308],[33,14007,14009],{"className":14008,"code":38,"language":539},[537],[40,14010,38],{"__ignoreMap":38},[471,14012,14013],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":38,"searchDepth":64,"depth":64,"links":14015},[14016,14017,14018,14025,14027,14028,14029],{"id":13104,"depth":64,"text":13105},{"id":13136,"depth":64,"text":13137},{"id":13180,"depth":64,"text":14019,"children":14020},"useColorMode()와 $colorMode 차이점",[14021,14023],{"id":13198,"depth":119,"text":14022},"1. $colorMode",{"id":13308,"depth":119,"text":14024},"2. useColorMode()",{"id":13426,"depth":64,"text":14026},"useColorMode()를 직접 불러와서 사용하는 이유",{"id":13540,"depth":64,"text":13541},{"id":13813,"depth":64,"text":13814},{"id":13902,"depth":64,"text":13903},"Nuxt3에 다크모드 적용하여 관리하기",{"date":13087,"field":5954,"tags":14032},[7252],"\u002Fko\u002Fnuxt-colormode",{"title":13096,"description":14030},{"loc":14033},"ko\u002Fnuxt-colormode","NaaCObKGy7ldNRrJ9j5KG1lPCQ79cn0XsNJGkCH3PlQ",{"id":14039,"title":14040,"body":14041,"description":15014,"extension":484,"meta":15015,"navigation":491,"path":15018,"seo":15019,"sitemap":15020,"stem":15021,"__hash__":15022},"ko\u002Fko\u002Fnuxt3-pinia-guide.md","nuxt3 Pinia 적용하기",{"type":8,"value":14042,"toc":14990},[14043,14049,14055,14062,14068,14071,14088,14099,14130,14136,14142,14181,14184,14186,14192,14199,14283,14310,14325,14327,14333,14336,14342,14411,14440,14442,14448,14455,14464,14553,14559,14646,14671,14673,14683,14693,14702,14792,14800,14898,14924,14926,14936,14975,14977,14981,14987],[11,14044,14046],{"id":14045},"nuxt-3에서-pinia-사용하기",[21,14047,14048],{},"Nuxt 3에서 Pinia 사용하기",[29,14050,14052],{"id":14051},"_1-pinia란",[21,14053,14054],{},"1. Pinia란?",[15,14056,14057,14058,14061],{},"Pinia는 Vue의 공식 상태 관리 라이브러리로, Vuex의 대체제로 등장하였다. 기존 Vuex보다 간결한 API를 제공하며, 자동 타입 추론 기능을 지원한다. Nuxt 3에서는 ",[40,14059,14060],{},"@pinia\u002Fnuxt"," 모듈을 사용하여 Pinia를 간편하게 통합할 수 있다.",[29,14063,14065],{"id":14064},"_2-pinia-설치",[21,14066,14067],{},"2. Pinia 설치",[15,14069,14070],{},"다음 명령어를 실행하여 Pinia를 설치한다.",[33,14072,14074],{"className":850,"code":14073,"language":852,"meta":38,"style":38},"npm install pinia @pinia\u002Fnuxt\n",[40,14075,14076],{"__ignoreMap":38},[43,14077,14078,14080,14082,14085],{"class":45,"line":46},[43,14079,1741],{"class":57},[43,14081,1744],{"class":100},[43,14083,14084],{"class":100}," pinia",[43,14086,14087],{"class":100}," @pinia\u002Fnuxt\n",[15,14089,14090,14091,14094,14095,14098],{},"설치 과정에서 ",[40,14092,14093],{},"_ERESOLVE unable to resolve dependency tree_"," 오류가 발생할 경우, ",[40,14096,14097],{},"package.json"," 파일에 다음 내용을 추가하면 해결할 수 있다.",[33,14100,14102],{"className":556,"code":14101,"language":558,"meta":38,"style":38},"\u002F\u002F path: .\u002Fpackage.json\n\"overrides\": {\n  \"vue\": \"latest\"\n}\n",[40,14103,14104,14109,14116,14126],{"__ignoreMap":38},[43,14105,14106],{"class":45,"line":46},[43,14107,14108],{"class":2797},"\u002F\u002F path: .\u002Fpackage.json\n",[43,14110,14111,14114],{"class":45,"line":64},[43,14112,14113],{"class":100},"\"overrides\"",[43,14115,1036],{"class":49},[43,14117,14118,14121,14123],{"class":45,"line":119},[43,14119,14120],{"class":81},"  \"vue\"",[43,14122,78],{"class":49},[43,14124,14125],{"class":100},"\"latest\"\n",[43,14127,14128],{"class":45,"line":150},[43,14129,2034],{"class":49},[29,14131,14133],{"id":14132},"_3-nuxt-설정-파일에-pinia-추가",[21,14134,14135],{},"3. Nuxt 설정 파일에 Pinia 추가",[15,14137,14138,14139,14141],{},"Nuxt에서 Pinia를 사용하기 위해 ",[40,14140,1020],{}," 파일에 모듈을 등록해야 한다.",[33,14143,14145],{"className":6038,"code":14144,"language":6040,"meta":38,"style":38},"\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: [\n    \"@pinia\u002Fnuxt\",\n  ],\n});\n",[40,14146,14147,14151,14161,14165,14172,14177],{"__ignoreMap":38},[43,14148,14149],{"class":45,"line":46},[43,14150,6047],{"class":2797},[43,14152,14153,14155,14157,14159],{"class":45,"line":64},[43,14154,6052],{"class":67},[43,14156,6055],{"class":67},[43,14158,6058],{"class":57},[43,14160,1803],{"class":49},[43,14162,14163],{"class":45,"line":119},[43,14164,13163],{"class":49},[43,14166,14167,14170],{"class":45,"line":150},[43,14168,14169],{"class":100},"    \"@pinia\u002Fnuxt\"",[43,14171,1056],{"class":49},[43,14173,14174],{"class":45,"line":159},[43,14175,14176],{"class":49},"  ],\n",[43,14178,14179],{"class":45,"line":176},[43,14180,1855],{"class":49},[15,14182,14183],{},"이제 Pinia를 Nuxt 애플리케이션에서 사용할 수 있다.",[26,14185],{},[29,14187,14189],{"id":14188},"_4-pinia-store-생성",[21,14190,14191],{},"4. Pinia Store 생성",[15,14193,14194,14195,14198],{},"Pinia에서는 상태(state), 계산된 값(getters), 액션(actions)을 정의하여 사용할 수 있다. 다음은 기본적인 ",[40,14196,14197],{},"state"," 값을 가지는 스토어의 예제이다.",[33,14200,14202],{"className":6038,"code":14201,"language":6040,"meta":38,"style":38},"\u002F\u002F path: .\u002Fstores\u002Fcounter.ts\n\nimport { defineStore } from \"pinia\";\n\nexport const useCounterStore = defineStore(\"counter\", {\n  state: () => ({\n    count: 1,\n  }),\n});\n",[40,14203,14204,14209,14213,14227,14231,14253,14265,14274,14279],{"__ignoreMap":38},[43,14205,14206],{"class":45,"line":46},[43,14207,14208],{"class":2797},"\u002F\u002F path: .\u002Fstores\u002Fcounter.ts\n",[43,14210,14211],{"class":45,"line":64},[43,14212,1782],{"emptyLinePlaceholder":491},[43,14214,14215,14217,14220,14222,14225],{"class":45,"line":119},[43,14216,7079],{"class":67},[43,14218,14219],{"class":49}," { defineStore } ",[43,14221,7085],{"class":67},[43,14223,14224],{"class":100}," \"pinia\"",[43,14226,2995],{"class":49},[43,14228,14229],{"class":45,"line":150},[43,14230,1782],{"emptyLinePlaceholder":491},[43,14232,14233,14235,14237,14240,14242,14245,14247,14250],{"class":45,"line":159},[43,14234,6052],{"class":67},[43,14236,8131],{"class":67},[43,14238,14239],{"class":81}," useCounterStore",[43,14241,128],{"class":67},[43,14243,14244],{"class":57}," defineStore",[43,14246,97],{"class":49},[43,14248,14249],{"class":100},"\"counter\"",[43,14251,14252],{"class":49},", {\n",[43,14254,14255,14258,14260,14262],{"class":45,"line":176},[43,14256,14257],{"class":57},"  state",[43,14259,168],{"class":49},[43,14261,113],{"class":67},[43,14263,14264],{"class":49}," ({\n",[43,14266,14267,14270,14272],{"class":45,"line":1163},[43,14268,14269],{"class":49},"    count: ",[43,14271,905],{"class":81},[43,14273,1056],{"class":49},[43,14275,14276],{"class":45,"line":1178},[43,14277,14278],{"class":49},"  }),\n",[43,14280,14281],{"class":45,"line":1184},[43,14282,1855],{"class":49},[507,14284,14285,14290],{},[15,14286,14287],{},[21,14288,14289],{},"📌 설명",[185,14291,14292,14301],{},[188,14293,14294,14297,14298,14300],{},[40,14295,14296],{},"defineStore(\"counter\", {...})"," → ",[40,14299,14249],{},"는 스토어의 고유한 이름이다.",[188,14302,14303,14297,14306,14309],{},[40,14304,14305],{},"state: () => ({ count: 1 })",[40,14307,14308],{},"count","라는 상태 변수를 선언하였다.",[185,14311,14312],{},[188,14313,14314],{},[21,14315,14316,14317,14320,14321,14324],{},"Nuxt 3에서는 ",[40,14318,14319],{},"defineStore","가 자동으로 임포트되므로 ",[40,14322,14323],{},"import { defineStore } from \"pinia\";","를 생략할 수 있다.",[26,14326],{},[29,14328,14330],{"id":14329},"_5-pinia-store를-활용하여-상태-값-가져오기",[21,14331,14332],{},"5. Pinia Store를 활용하여 상태 값 가져오기",[15,14334,14335],{},"Nuxt 3의 컴포넌트에서 Pinia의 상태를 가져와 UI에 적용할 수 있다.",[1273,14337,14339],{"id":14338},"기본적인-사용법",[21,14340,14341],{},"✅ 기본적인 사용법",[33,14343,14345],{"className":35,"code":14344,"language":37,"meta":38,"style":38},"\u003Cscript setup>\nconst counterStore = useCounterStore();\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Ch1>카운트 값: {{ counterStore.count }}\u003C\u002Fh1>\n\u003C\u002Ftemplate>\n",[40,14346,14347,14357,14370,14378,14382,14390,14403],{"__ignoreMap":38},[43,14348,14349,14351,14353,14355],{"class":45,"line":46},[43,14350,50],{"class":49},[43,14352,54],{"class":53},[43,14354,58],{"class":57},[43,14356,61],{"class":49},[43,14358,14359,14361,14364,14366,14368],{"class":45,"line":64},[43,14360,68],{"class":67},[43,14362,14363],{"class":81}," counterStore",[43,14365,128],{"class":67},[43,14367,14239],{"class":57},[43,14369,147],{"class":49},[43,14371,14372,14374,14376],{"class":45,"line":119},[43,14373,179],{"class":49},[43,14375,54],{"class":53},[43,14377,61],{"class":49},[43,14379,14380],{"class":45,"line":150},[43,14381,1782],{"emptyLinePlaceholder":491},[43,14383,14384,14386,14388],{"class":45,"line":159},[43,14385,50],{"class":49},[43,14387,6693],{"class":53},[43,14389,61],{"class":49},[43,14391,14392,14394,14396,14399,14401],{"class":45,"line":176},[43,14393,6700],{"class":49},[43,14395,11],{"class":53},[43,14397,14398],{"class":49},">카운트 값: {{ counterStore.count }}\u003C\u002F",[43,14400,11],{"class":53},[43,14402,61],{"class":49},[43,14404,14405,14407,14409],{"class":45,"line":1163},[43,14406,179],{"class":49},[43,14408,6693],{"class":53},[43,14410,61],{"class":49},[507,14412,14413,14417],{},[15,14414,14415],{},[21,14416,14289],{},[185,14418,14419,14428],{},[188,14420,14421,14424,14425,14427],{},[40,14422,14423],{},"useCounterStore()","를 통해 스토어를 가져와 ",[40,14426,14308],{}," 값을 사용할 수 있다.",[188,14429,14430,14433,14434,14436,14437,14439],{},[40,14431,14432],{},"counterStore.count","를 ",[40,14435,8353],{},"로 출력하면 초기값인 ",[40,14438,905],{},"이 화면에 나타난다.",[26,14441],{},[29,14443,14445],{"id":14444},"_6-상태-값-변경-actions-활용",[21,14446,14447],{},"6. 상태 값 변경 (Actions 활용)",[15,14449,14450,14451,14454],{},"Pinia에서는 ",[40,14452,14453],{},"actions","를 이용해 상태 값을 변경할 수 있다.",[1273,14456,14458],{"id":14457},"increment-액션-추가",[21,14459,5985,14460,14463],{},[40,14461,14462],{},"increment()"," 액션 추가",[33,14465,14467],{"className":6038,"code":14466,"language":6040,"meta":38,"style":38},"\u002F\u002F path: .\u002Fstores\u002Fcounter.ts\n\nexport const useCounterStore = defineStore(\"counter\", {\n  state: () => ({\n    count: 1,\n  }),\n  actions: {\n    increment() {\n      this.count++;\n    },\n  },\n});\n",[40,14468,14469,14473,14477,14495,14505,14513,14517,14522,14529,14541,14545,14549],{"__ignoreMap":38},[43,14470,14471],{"class":45,"line":46},[43,14472,14208],{"class":2797},[43,14474,14475],{"class":45,"line":64},[43,14476,1782],{"emptyLinePlaceholder":491},[43,14478,14479,14481,14483,14485,14487,14489,14491,14493],{"class":45,"line":119},[43,14480,6052],{"class":67},[43,14482,8131],{"class":67},[43,14484,14239],{"class":81},[43,14486,128],{"class":67},[43,14488,14244],{"class":57},[43,14490,97],{"class":49},[43,14492,14249],{"class":100},[43,14494,14252],{"class":49},[43,14496,14497,14499,14501,14503],{"class":45,"line":150},[43,14498,14257],{"class":57},[43,14500,168],{"class":49},[43,14502,113],{"class":67},[43,14504,14264],{"class":49},[43,14506,14507,14509,14511],{"class":45,"line":159},[43,14508,14269],{"class":49},[43,14510,905],{"class":81},[43,14512,1056],{"class":49},[43,14514,14515],{"class":45,"line":176},[43,14516,14278],{"class":49},[43,14518,14519],{"class":45,"line":1163},[43,14520,14521],{"class":49},"  actions: {\n",[43,14523,14524,14527],{"class":45,"line":1178},[43,14525,14526],{"class":57},"    increment",[43,14528,8430],{"class":49},[43,14530,14531,14534,14537,14539],{"class":45,"line":1184},[43,14532,14533],{"class":81},"      this",[43,14535,14536],{"class":49},".count",[43,14538,8628],{"class":67},[43,14540,2995],{"class":49},[43,14542,14543],{"class":45,"line":1190},[43,14544,6095],{"class":49},[43,14546,14547],{"class":45,"line":1996},[43,14548,1187],{"class":49},[43,14550,14551],{"class":45,"line":2008},[43,14552,1855],{"class":49},[1273,14554,14556],{"id":14555},"버튼을-클릭하여-상태-변경",[21,14557,14558],{},"✅ 버튼을 클릭하여 상태 변경",[33,14560,14562],{"className":35,"code":14561,"language":37,"meta":38,"style":38},"\u003Cscript setup>\nconst counterStore = useCounterStore();\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Ch1>카운트 값: {{ counterStore.count }}\u003C\u002Fh1>\n  \u003Cbutton @click=\"counterStore.increment\">+\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[40,14563,14564,14574,14586,14594,14598,14606,14618,14638],{"__ignoreMap":38},[43,14565,14566,14568,14570,14572],{"class":45,"line":46},[43,14567,50],{"class":49},[43,14569,54],{"class":53},[43,14571,58],{"class":57},[43,14573,61],{"class":49},[43,14575,14576,14578,14580,14582,14584],{"class":45,"line":64},[43,14577,68],{"class":67},[43,14579,14363],{"class":81},[43,14581,128],{"class":67},[43,14583,14239],{"class":57},[43,14585,147],{"class":49},[43,14587,14588,14590,14592],{"class":45,"line":119},[43,14589,179],{"class":49},[43,14591,54],{"class":53},[43,14593,61],{"class":49},[43,14595,14596],{"class":45,"line":150},[43,14597,1782],{"emptyLinePlaceholder":491},[43,14599,14600,14602,14604],{"class":45,"line":159},[43,14601,50],{"class":49},[43,14603,6693],{"class":53},[43,14605,61],{"class":49},[43,14607,14608,14610,14612,14614,14616],{"class":45,"line":176},[43,14609,6700],{"class":49},[43,14611,11],{"class":53},[43,14613,14398],{"class":49},[43,14615,11],{"class":53},[43,14617,61],{"class":49},[43,14619,14620,14622,14624,14626,14628,14631,14634,14636],{"class":45,"line":1163},[43,14621,6700],{"class":49},[43,14623,10437],{"class":53},[43,14625,13247],{"class":57},[43,14627,88],{"class":49},[43,14629,14630],{"class":100},"\"counterStore.increment\"",[43,14632,14633],{"class":49},">+\u003C\u002F",[43,14635,10437],{"class":53},[43,14637,61],{"class":49},[43,14639,14640,14642,14644],{"class":45,"line":1178},[43,14641,179],{"class":49},[43,14643,6693],{"class":53},[43,14645,61],{"class":49},[507,14647,14648,14652],{},[15,14649,14650],{},[21,14651,14289],{},[185,14653,14654,14662],{},[188,14655,14656,14658,14659,14661],{},[40,14657,14462],{}," 함수가 ",[40,14660,14308],{}," 값을 증가시키도록 정의되었다.",[188,14663,14664,14665,14667,14668,14670],{},"버튼을 클릭하면 ",[40,14666,14462],{},"가 실행되어 화면의 ",[40,14669,14308],{}," 값이 증가한다.",[26,14672],{},[29,14674,14676],{"id":14675},"_7-getters를-활용한-계산된-값-사용",[21,14677,14678,14679,14682],{},"7. ",[40,14680,14681],{},"getters","를 활용한 계산된 값 사용",[15,14684,14685,391,14687,14689,14690,14692],{},[40,14686,14681],{},[40,14688,7778],{}," 속성과 유사한 개념으로, 기존의 ",[40,14691,14197],{}," 값을 가공하여 새로운 값을 만들 때 유용하다.",[1273,14694,14696],{"id":14695},"doublecount-계산된-값-추가",[21,14697,5985,14698,14701],{},[40,14699,14700],{},"doubleCount"," 계산된 값 추가",[33,14703,14705],{"className":6038,"code":14704,"language":6040,"meta":38,"style":38},"\u002F\u002F path: .\u002Fstores\u002Fcounter.ts\n\nexport const useCounterStore = defineStore(\"counter\", {\n  state: () => ({\n    count: 1,\n  }),\n  getters: {\n    doubleCount: (state) => state.count * 2,\n  },\n});\n",[40,14706,14707,14711,14715,14733,14743,14751,14755,14760,14784,14788],{"__ignoreMap":38},[43,14708,14709],{"class":45,"line":46},[43,14710,14208],{"class":2797},[43,14712,14713],{"class":45,"line":64},[43,14714,1782],{"emptyLinePlaceholder":491},[43,14716,14717,14719,14721,14723,14725,14727,14729,14731],{"class":45,"line":119},[43,14718,6052],{"class":67},[43,14720,8131],{"class":67},[43,14722,14239],{"class":81},[43,14724,128],{"class":67},[43,14726,14244],{"class":57},[43,14728,97],{"class":49},[43,14730,14249],{"class":100},[43,14732,14252],{"class":49},[43,14734,14735,14737,14739,14741],{"class":45,"line":150},[43,14736,14257],{"class":57},[43,14738,168],{"class":49},[43,14740,113],{"class":67},[43,14742,14264],{"class":49},[43,14744,14745,14747,14749],{"class":45,"line":159},[43,14746,14269],{"class":49},[43,14748,905],{"class":81},[43,14750,1056],{"class":49},[43,14752,14753],{"class":45,"line":176},[43,14754,14278],{"class":49},[43,14756,14757],{"class":45,"line":1163},[43,14758,14759],{"class":49},"  getters: {\n",[43,14761,14762,14765,14768,14770,14772,14774,14777,14779,14782],{"class":45,"line":1178},[43,14763,14764],{"class":57},"    doubleCount",[43,14766,14767],{"class":49},": (",[43,14769,14197],{"class":74},[43,14771,2408],{"class":49},[43,14773,113],{"class":67},[43,14775,14776],{"class":49}," state.count ",[43,14778,4726],{"class":67},[43,14780,14781],{"class":81}," 2",[43,14783,1056],{"class":49},[43,14785,14786],{"class":45,"line":1184},[43,14787,1187],{"class":49},[43,14789,14790],{"class":45,"line":1190},[43,14791,1855],{"class":49},[1273,14793,14795],{"id":14794},"doublecount-값을-ui에서-활용",[21,14796,5985,14797,14799],{},[40,14798,14700],{}," 값을 UI에서 활용",[33,14801,14803],{"className":35,"code":14802,"language":37,"meta":38,"style":38},"\u003Cscript setup>\nconst counterStore = useCounterStore();\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Ch1>카운트 값: {{ counterStore.count }}\u003C\u002Fh1>\n  \u003Ch2>두 배 값: {{ counterStore.doubleCount }}\u003C\u002Fh2>\n  \u003Cbutton @click=\"counterStore.increment\">+\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[40,14804,14805,14815,14827,14835,14839,14847,14859,14872,14890],{"__ignoreMap":38},[43,14806,14807,14809,14811,14813],{"class":45,"line":46},[43,14808,50],{"class":49},[43,14810,54],{"class":53},[43,14812,58],{"class":57},[43,14814,61],{"class":49},[43,14816,14817,14819,14821,14823,14825],{"class":45,"line":64},[43,14818,68],{"class":67},[43,14820,14363],{"class":81},[43,14822,128],{"class":67},[43,14824,14239],{"class":57},[43,14826,147],{"class":49},[43,14828,14829,14831,14833],{"class":45,"line":119},[43,14830,179],{"class":49},[43,14832,54],{"class":53},[43,14834,61],{"class":49},[43,14836,14837],{"class":45,"line":150},[43,14838,1782],{"emptyLinePlaceholder":491},[43,14840,14841,14843,14845],{"class":45,"line":159},[43,14842,50],{"class":49},[43,14844,6693],{"class":53},[43,14846,61],{"class":49},[43,14848,14849,14851,14853,14855,14857],{"class":45,"line":176},[43,14850,6700],{"class":49},[43,14852,11],{"class":53},[43,14854,14398],{"class":49},[43,14856,11],{"class":53},[43,14858,61],{"class":49},[43,14860,14861,14863,14865,14868,14870],{"class":45,"line":1163},[43,14862,6700],{"class":49},[43,14864,29],{"class":53},[43,14866,14867],{"class":49},">두 배 값: {{ counterStore.doubleCount }}\u003C\u002F",[43,14869,29],{"class":53},[43,14871,61],{"class":49},[43,14873,14874,14876,14878,14880,14882,14884,14886,14888],{"class":45,"line":1178},[43,14875,6700],{"class":49},[43,14877,10437],{"class":53},[43,14879,13247],{"class":57},[43,14881,88],{"class":49},[43,14883,14630],{"class":100},[43,14885,14633],{"class":49},[43,14887,10437],{"class":53},[43,14889,61],{"class":49},[43,14891,14892,14894,14896],{"class":45,"line":1184},[43,14893,179],{"class":49},[43,14895,6693],{"class":53},[43,14897,61],{"class":49},[507,14899,14900,14904],{},[15,14901,14902],{},[21,14903,14289],{},[185,14905,14906,14916],{},[188,14907,14908,391,14910,14912,14913,8886],{},[40,14909,14700],{},[40,14911,14308],{}," 값의 두 배를 계산하는 ",[40,14914,14915],{},"getter",[188,14917,14918,14920,14921,14923],{},[40,14919,14700],{}," 값은 반응성을 가지므로 ",[40,14922,14308],{}," 값이 변경될 경우 자동으로 업데이트된다.",[26,14925],{},[29,14927,14929],{"id":14928},"_8-pinia의-definestore-옵션-정리",[21,14930,14931,14932,14935],{},"8. Pinia의 ",[40,14933,14934],{},"defineStore()"," 옵션 정리",[678,14937,14938,14946],{},[681,14939,14940],{},[684,14941,14942,14944],{},[687,14943,9868],{},[687,14945,5673],{},[703,14947,14948,14957,14966],{},[684,14949,14950,14954],{},[708,14951,14952],{},[40,14953,14197],{},[708,14955,14956],{},"컴포넌트에서 사용할 상태 값을 정의한다.",[684,14958,14959,14963],{},[708,14960,14961],{},[40,14962,14453],{},[708,14964,14965],{},"상태 값을 변경하는 함수 정의 (비동기 처리 가능)",[684,14967,14968,14972],{},[708,14969,14970],{},[40,14971,14681],{},[708,14973,14974],{},"계산된 값을 반환하는 속성 (반응형 유지)",[26,14976],{},[29,14978,14980],{"id":14979},"pinia-info","Pinia Info",[15,14982,14983,14984,14986],{},"✅ 타입스크립트 친화적 (자동 타입 추론 지원)",[6005,14985],{},"\n✅ 플러그인 없이 SSR(서버 사이드 렌더링) 지원",[471,14988,14989],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":14991},[14992,14993,14994,14995,14996,14999,15004,15011,15013],{"id":14051,"depth":64,"text":14054},{"id":14064,"depth":64,"text":14067},{"id":14132,"depth":64,"text":14135},{"id":14188,"depth":64,"text":14191},{"id":14329,"depth":64,"text":14332,"children":14997},[14998],{"id":14338,"depth":119,"text":14341},{"id":14444,"depth":64,"text":14447,"children":15000},[15001,15003],{"id":14457,"depth":119,"text":15002},"✅ increment() 액션 추가",{"id":14555,"depth":119,"text":14558},{"id":14675,"depth":64,"text":15005,"children":15006},"7. getters를 활용한 계산된 값 사용",[15007,15009],{"id":14695,"depth":119,"text":15008},"✅ doubleCount 계산된 값 추가",{"id":14794,"depth":119,"text":15010},"✅ doubleCount 값을 UI에서 활용",{"id":14928,"depth":64,"text":15012},"8. Pinia의 defineStore() 옵션 정리",{"id":14979,"depth":64,"text":14980},"Nuxt3 프로젝트에서 Pinia를 적용해보자 @pinia\u002Fnuxt",{"date":13087,"field":5954,"tags":15016},[7252,15017],"pinia","\u002Fko\u002Fnuxt3-pinia-guide",{"title":14040,"description":15014},{"loc":15018},"ko\u002Fnuxt3-pinia-guide","53-44l94h1J-QVoNNEX2pA7KgX4rhjNZ3Dvfcq35W6o",{"id":15024,"title":15025,"body":15026,"description":18201,"extension":484,"meta":18202,"navigation":491,"path":18205,"seo":18206,"sitemap":18207,"stem":18208,"__hash__":18209},"ko\u002Fko\u002Fhtml2.md","미치광이 카드 컴포넌트",{"type":8,"value":15027,"toc":18198},[15028,15032,18195],[29,15029,15031],{"id":15030},"미치광이-카드","미치광이 카드",[11005,15033,15034,15277],{},[33,15035,15037],{"className":11009,"code":15036,"language":11011,"meta":38,"style":38},"\u003Cdiv>\n  \u003Cdiv class=\"crazy-card\">\n    \u003Cdiv class=\"card-content\">\n      \u003Ch2 class=\"card-title\">미치광이의 세계\u003C\u002Fh2>\n      \u003Cp class=\"card-text\">\n        정상적인 사고방식을 버리고 광기의 세계로 오세요. 여기서는 모든 규칙이\n        무너지고 혼돈이 지배합니다. 당신의 상상력은 현실이 되고, 현실은 환상이\n        됩니다.\n      \u003C\u002Fp>\n      \u003Cbutton class=\"card-button\">광기에 빠지다\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"glitch-overlay\">\u003C\u002Fdiv>\n    \u003Cdiv class=\"crazy-shapes\">\n      \u003Cdiv class=\"shape\">\u003C\u002Fdiv>\n      \u003Cdiv class=\"shape\">\u003C\u002Fdiv>\n      \u003Cdiv class=\"shape\">\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n",[40,15038,15039,15047,15062,15077,15097,15112,15117,15122,15127,15135,15155,15163,15183,15198,15217,15235,15253,15261,15269],{"__ignoreMap":38},[43,15040,15041,15043,15045],{"class":45,"line":46},[43,15042,50],{"class":49},[43,15044,6703],{"class":53},[43,15046,61],{"class":49},[43,15048,15049,15051,15053,15055,15057,15060],{"class":45,"line":64},[43,15050,6700],{"class":49},[43,15052,6703],{"class":53},[43,15054,6714],{"class":57},[43,15056,88],{"class":49},[43,15058,15059],{"class":100},"\"crazy-card\"",[43,15061,61],{"class":49},[43,15063,15064,15066,15068,15070,15072,15075],{"class":45,"line":119},[43,15065,6726],{"class":49},[43,15067,6703],{"class":53},[43,15069,6714],{"class":57},[43,15071,88],{"class":49},[43,15073,15074],{"class":100},"\"card-content\"",[43,15076,61],{"class":49},[43,15078,15079,15081,15083,15085,15087,15090,15093,15095],{"class":45,"line":150},[43,15080,7339],{"class":49},[43,15082,29],{"class":53},[43,15084,6714],{"class":57},[43,15086,88],{"class":49},[43,15088,15089],{"class":100},"\"card-title\"",[43,15091,15092],{"class":49},">미치광이의 세계\u003C\u002F",[43,15094,29],{"class":53},[43,15096,61],{"class":49},[43,15098,15099,15101,15103,15105,15107,15110],{"class":45,"line":159},[43,15100,7339],{"class":49},[43,15102,15],{"class":53},[43,15104,6714],{"class":57},[43,15106,88],{"class":49},[43,15108,15109],{"class":100},"\"card-text\"",[43,15111,61],{"class":49},[43,15113,15114],{"class":45,"line":176},[43,15115,15116],{"class":49},"        정상적인 사고방식을 버리고 광기의 세계로 오세요. 여기서는 모든 규칙이\n",[43,15118,15119],{"class":45,"line":1163},[43,15120,15121],{"class":49},"        무너지고 혼돈이 지배합니다. 당신의 상상력은 현실이 되고, 현실은 환상이\n",[43,15123,15124],{"class":45,"line":1178},[43,15125,15126],{"class":49},"        됩니다.\n",[43,15128,15129,15131,15133],{"class":45,"line":1184},[43,15130,7369],{"class":49},[43,15132,15],{"class":53},[43,15134,61],{"class":49},[43,15136,15137,15139,15141,15143,15145,15148,15151,15153],{"class":45,"line":1190},[43,15138,7339],{"class":49},[43,15140,10437],{"class":53},[43,15142,6714],{"class":57},[43,15144,88],{"class":49},[43,15146,15147],{"class":100},"\"card-button\"",[43,15149,15150],{"class":49},">광기에 빠지다\u003C\u002F",[43,15152,10437],{"class":53},[43,15154,61],{"class":49},[43,15156,15157,15159,15161],{"class":45,"line":1996},[43,15158,7378],{"class":49},[43,15160,6703],{"class":53},[43,15162,61],{"class":49},[43,15164,15165,15167,15169,15171,15173,15176,15179,15181],{"class":45,"line":2008},[43,15166,6726],{"class":49},[43,15168,6703],{"class":53},[43,15170,6714],{"class":57},[43,15172,88],{"class":49},[43,15174,15175],{"class":100},"\"glitch-overlay\"",[43,15177,15178],{"class":49},">\u003C\u002F",[43,15180,6703],{"class":53},[43,15182,61],{"class":49},[43,15184,15185,15187,15189,15191,15193,15196],{"class":45,"line":2025},[43,15186,6726],{"class":49},[43,15188,6703],{"class":53},[43,15190,6714],{"class":57},[43,15192,88],{"class":49},[43,15194,15195],{"class":100},"\"crazy-shapes\"",[43,15197,61],{"class":49},[43,15199,15200,15202,15204,15206,15208,15211,15213,15215],{"class":45,"line":2031},[43,15201,7339],{"class":49},[43,15203,6703],{"class":53},[43,15205,6714],{"class":57},[43,15207,88],{"class":49},[43,15209,15210],{"class":100},"\"shape\"",[43,15212,15178],{"class":49},[43,15214,6703],{"class":53},[43,15216,61],{"class":49},[43,15218,15219,15221,15223,15225,15227,15229,15231,15233],{"class":45,"line":2037},[43,15220,7339],{"class":49},[43,15222,6703],{"class":53},[43,15224,6714],{"class":57},[43,15226,88],{"class":49},[43,15228,15210],{"class":100},[43,15230,15178],{"class":49},[43,15232,6703],{"class":53},[43,15234,61],{"class":49},[43,15236,15237,15239,15241,15243,15245,15247,15249,15251],{"class":45,"line":2042},[43,15238,7339],{"class":49},[43,15240,6703],{"class":53},[43,15242,6714],{"class":57},[43,15244,88],{"class":49},[43,15246,15210],{"class":100},[43,15248,15178],{"class":49},[43,15250,6703],{"class":53},[43,15252,61],{"class":49},[43,15254,15255,15257,15259],{"class":45,"line":2516},[43,15256,7378],{"class":49},[43,15258,6703],{"class":53},[43,15260,61],{"class":49},[43,15262,15263,15265,15267],{"class":45,"line":2998},[43,15264,6740],{"class":49},[43,15266,6703],{"class":53},[43,15268,61],{"class":49},[43,15270,15271,15273,15275],{"class":45,"line":3003},[43,15272,179],{"class":49},[43,15274,6703],{"class":53},[43,15276,61],{"class":49},[33,15278,15280],{"className":10460,"code":15279,"language":10462,"meta":38,"style":38},"\nbody {\n  \u002F* display: flex;\n  justify-content: center;\n  align-items: center;\n  min-height: 100vh;\n  margin: 0; *\u002F\n  background-color: #111;\n  \u002F* overflow: hidden; *\u002F\n}\n\n.crazy-card {\n  position: relative;\n  width: 300px;\n  height: 400px;\n  background: linear-gradient(45deg, #ff00cc, #333399);\n  border-radius: 15px;\n  box-shadow: 0 0 20px #ff00cc, 0 0 40px #333399;\n  overflow: hidden;\n  transform-style: preserve-3d;\n  transition: transform 0.5s, box-shadow 0.3s;\n  animation: pulse 3s infinite alternate, shake 7s infinite;\n}\n\n.crazy-card:hover {\n  transform: perspective(1000px) rotateY(15deg) rotateX(5deg);\n  box-shadow: 0 0 30px #ff00cc, 0 0 60px #333399,\n    0 0 100px rgba(255, 0, 204, 0.5);\n}\n\n.card-content {\n  position: relative;\n  padding: 20px;\n  color: #fff;\n  font-family: \"Permanent Marker\", cursive;\n  z-index: 2;\n  transform-style: preserve-3d;\n  height: 100%;\n  box-sizing: border-box;\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n}\n\n.card-title {\n  font-family: \"Creepster\", cursive;\n  font-size: 32px;\n  margin: 0 0 10px;\n  color: #fff;\n  text-shadow: 2px 2px 4px #000, 0 0 10px #ff00cc, 0 0 20px #ff00cc;\n  letter-spacing: 2px;\n  animation: glitch 3s infinite alternate;\n}\n\n.card-text {\n  font-size: 18px;\n  line-height: 1.4;\n  margin-bottom: 20px;\n  text-shadow: 1px 1px 2px #000;\n  transform: translateZ(20px);\n}\n\n.card-button {\n  display: inline-block;\n  padding: 10px 20px;\n  background: linear-gradient(45deg, #ff00cc, #333399);\n  border: 2px solid #fff;\n  color: #fff;\n  font-family: \"Permanent Marker\", cursive;\n  font-size: 16px;\n  text-decoration: none;\n  border-radius: 5px;\n  text-shadow: 1px 1px 2px #000;\n  transition: all 0.3s;\n  transform: translateZ(30px);\n  cursor: pointer;\n}\n\n.card-button:hover {\n  background: #fff;\n  color: #ff00cc;\n  box-shadow: 0 0 15px #fff;\n  transform: translateZ(40px) scale(1.05);\n}\n\n.glitch-overlay {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  background: transparent;\n  z-index: 1;\n  mix-blend-mode: overlay;\n  opacity: 0.2;\n  pointer-events: none;\n  animation: glitchOverlay 8s infinite;\n}\n\n.crazy-shapes {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  z-index: 0;\n  overflow: hidden;\n  pointer-events: none;\n}\n\n.shape {\n  position: absolute;\n  background: rgba(255, 255, 255, 0.1);\n  border-radius: 50%;\n  animation: floatAround 15s infinite linear;\n}\n\n.shape:nth-child(1) {\n  width: 100px;\n  height: 100px;\n  top: -20px;\n  left: -20px;\n  background: rgba(255, 0, 204, 0.2);\n}\n\n.shape:nth-child(2) {\n  width: 150px;\n  height: 150px;\n  bottom: -30px;\n  right: -30px;\n  background: rgba(51, 51, 153, 0.2);\n  animation-delay: -5s;\n}\n\n.shape:nth-child(3) {\n  width: 80px;\n  height: 80px;\n  top: 50%;\n  left: 50%;\n  background: rgba(255, 255, 255, 0.1);\n  animation-delay: -8s;\n}\n\n@keyframes pulse {\n  0% {\n    transform: scale(1);\n  }\n  100% {\n    transform: scale(1.03);\n  }\n}\n\n@keyframes shake {\n  0%,\n  100% {\n    transform: translateX(0);\n  }\n  10%,\n  30%,\n  50%,\n  70%,\n  90% {\n    transform: translateX(-2px) rotate(-0.5deg);\n  }\n  20%,\n  40%,\n  60%,\n  80% {\n    transform: translateX(2px) rotate(0.5deg);\n  }\n}\n\n@keyframes glitch {\n  0%,\n  100% {\n    text-shadow: 2px 2px 4px #000, 0 0 10px #ff00cc, 0 0 20px #ff00cc;\n    transform: translate(0);\n  }\n  20% {\n    text-shadow: -2px 0 #0ff, 2px 2px #f0f;\n    transform: translate(1px, 1px);\n  }\n  40% {\n    text-shadow: 2px 0 #f00, -2px -2px #0f0;\n    transform: translate(-1px, -1px);\n  }\n  60% {\n    text-shadow: 1px -1px #ff0, -1px 1px #00f;\n    transform: translate(1px, -1px);\n  }\n  80% {\n    text-shadow: -1px 1px #f0f, 1px -1px #0ff;\n    transform: translate(-1px, 1px);\n  }\n}\n\n@keyframes glitchOverlay {\n  0%,\n  100% {\n    opacity: 0.1;\n    background-position: 0 0;\n  }\n  20% {\n    opacity: 0.3;\n    background-image: linear-gradient(\n      45deg,\n      transparent 45%,\n      rgba(255, 0, 204, 0.5) 50%,\n      transparent 55%\n    );\n    background-size: 10px 10px;\n    background-position: 5px 5px;\n  }\n  40% {\n    opacity: 0.2;\n    background-image: linear-gradient(\n      -45deg,\n      transparent 45%,\n      rgba(51, 51, 153, 0.5) 50%,\n      transparent 55%\n    );\n    background-size: 15px 15px;\n    background-position: -10px -10px;\n  }\n  60% {\n    opacity: 0.1;\n    background: none;\n  }\n  80% {\n    opacity: 0.3;\n    background-image: linear-gradient(\n      90deg,\n      transparent 45%,\n      rgba(255, 255, 255, 0.8) 50%,\n      transparent 55%\n    );\n    background-size: 20px 20px;\n    background-position: 0 0;\n  }\n}\n\n@keyframes floatAround {\n  0% {\n    transform: rotate(0deg) translateX(30px) rotate(0deg);\n  }\n  100% {\n    transform: rotate(360deg) translateX(30px) rotate(-360deg);\n  }\n}\n",[40,15281,15282,15286,15292,15297,15302,15307,15312,15317,15328,15333,15337,15341,15348,15358,15371,15384,15412,15424,15457,15467,15479,15500,15530,15534,15538,15545,15584,15614,15647,15651,15655,15662,15672,15684,15695,15706,15716,15726,15738,15748,15759,15771,15783,15787,15791,15798,15809,15822,15839,15849,15897,15910,15927,15931,15935,15942,15954,15966,15978,16001,16018,16022,16026,16033,16044,16060,16084,16101,16111,16121,16133,16143,16155,16177,16191,16207,16217,16221,16225,16232,16242,16252,16270,16296,16300,16304,16311,16321,16331,16341,16353,16365,16376,16386,16398,16410,16421,16436,16440,16444,16451,16461,16471,16481,16493,16505,16515,16525,16535,16539,16543,16550,16560,16587,16599,16617,16621,16625,16636,16648,16660,16673,16685,16711,16715,16719,16729,16742,16754,16768,16780,16808,16822,16826,16830,16840,16853,16865,16877,16889,16915,16928,16932,16936,16946,16954,16970,16975,16983,16999,17004,17009,17014,17024,17031,17038,17054,17059,17067,17075,17083,17091,17099,17128,17133,17141,17149,17157,17165,17192,17197,17202,17207,17217,17224,17231,17279,17295,17300,17307,17338,17361,17366,17373,17405,17429,17434,17441,17475,17498,17503,17510,17541,17564,17569,17574,17579,17589,17596,17603,17615,17629,17634,17641,17652,17664,17674,17686,17716,17727,17732,17750,17767,17772,17779,17790,17801,17811,17822,17851,17860,17865,17882,17901,17906,17913,17924,17936,17941,17948,17959,17970,17980,17991,18021,18030,18035,18052,18065,18070,18075,18080,18090,18097,18134,18139,18146,18185,18190],{"__ignoreMap":38},[43,15283,15284],{"class":45,"line":46},[43,15285,1782],{"emptyLinePlaceholder":491},[43,15287,15288,15290],{"class":45,"line":64},[43,15289,13925],{"class":53},[43,15291,116],{"class":49},[43,15293,15294],{"class":45,"line":119},[43,15295,15296],{"class":2797},"  \u002F* display: flex;\n",[43,15298,15299],{"class":45,"line":150},[43,15300,15301],{"class":2797},"  justify-content: center;\n",[43,15303,15304],{"class":45,"line":159},[43,15305,15306],{"class":2797},"  align-items: center;\n",[43,15308,15309],{"class":45,"line":176},[43,15310,15311],{"class":2797},"  min-height: 100vh;\n",[43,15313,15314],{"class":45,"line":1163},[43,15315,15316],{"class":2797},"  margin: 0; *\u002F\n",[43,15318,15319,15321,15323,15326],{"class":45,"line":1178},[43,15320,10501],{"class":81},[43,15322,78],{"class":49},[43,15324,15325],{"class":81},"#111",[43,15327,2995],{"class":49},[43,15329,15330],{"class":45,"line":1184},[43,15331,15332],{"class":2797},"  \u002F* overflow: hidden; *\u002F\n",[43,15334,15335],{"class":45,"line":1190},[43,15336,2034],{"class":49},[43,15338,15339],{"class":45,"line":1996},[43,15340,1782],{"emptyLinePlaceholder":491},[43,15342,15343,15346],{"class":45,"line":2008},[43,15344,15345],{"class":57},".crazy-card",[43,15347,116],{"class":49},[43,15349,15350,15352,15354,15356],{"class":45,"line":2025},[43,15351,11636],{"class":81},[43,15353,78],{"class":49},[43,15355,11641],{"class":81},[43,15357,2995],{"class":49},[43,15359,15360,15362,15364,15367,15369],{"class":45,"line":2031},[43,15361,11590],{"class":81},[43,15363,78],{"class":49},[43,15365,15366],{"class":81},"300",[43,15368,10489],{"class":67},[43,15370,2995],{"class":49},[43,15372,15373,15375,15377,15380,15382],{"class":45,"line":2037},[43,15374,11731],{"class":81},[43,15376,78],{"class":49},[43,15378,15379],{"class":81},"400",[43,15381,10489],{"class":67},[43,15383,2995],{"class":49},[43,15385,15386,15388,15390,15392,15394,15397,15400,15402,15405,15407,15410],{"class":45,"line":2042},[43,15387,11745],{"class":81},[43,15389,78],{"class":49},[43,15391,11750],{"class":81},[43,15393,97],{"class":49},[43,15395,15396],{"class":81},"45",[43,15398,15399],{"class":67},"deg",[43,15401,104],{"class":49},[43,15403,15404],{"class":81},"#ff00cc",[43,15406,104],{"class":49},[43,15408,15409],{"class":81},"#333399",[43,15411,1160],{"class":49},[43,15413,15414,15416,15418,15420,15422],{"class":45,"line":2516},[43,15415,11533],{"class":81},[43,15417,78],{"class":49},[43,15419,12099],{"class":81},[43,15421,10489],{"class":67},[43,15423,2995],{"class":49},[43,15425,15426,15428,15430,15432,15434,15436,15438,15441,15443,15445,15447,15450,15452,15455],{"class":45,"line":2998},[43,15427,11547],{"class":81},[43,15429,78],{"class":49},[43,15431,4401],{"class":81},[43,15433,7645],{"class":81},[43,15435,10492],{"class":81},[43,15437,10489],{"class":67},[43,15439,15440],{"class":81}," #ff00cc",[43,15442,104],{"class":49},[43,15444,4401],{"class":81},[43,15446,7645],{"class":81},[43,15448,15449],{"class":81}," 40",[43,15451,10489],{"class":67},[43,15453,15454],{"class":81}," #333399",[43,15456,2995],{"class":49},[43,15458,15459,15461,15463,15465],{"class":45,"line":3003},[43,15460,11648],{"class":81},[43,15462,78],{"class":49},[43,15464,11653],{"class":81},[43,15466,2995],{"class":49},[43,15468,15469,15472,15474,15477],{"class":45,"line":3009},[43,15470,15471],{"class":81},"  transform-style",[43,15473,78],{"class":49},[43,15475,15476],{"class":81},"preserve-3d",[43,15478,2995],{"class":49},[43,15480,15481,15483,15486,15489,15491,15494,15496,15498],{"class":45,"line":3022},[43,15482,12393],{"class":81},[43,15484,15485],{"class":49},": transform ",[43,15487,15488],{"class":81},"0.5",[43,15490,12403],{"class":67},[43,15492,15493],{"class":49},", box-shadow ",[43,15495,12747],{"class":81},[43,15497,12403],{"class":67},[43,15499,2995],{"class":49},[43,15501,15502,15505,15508,15510,15512,15515,15518,15521,15524,15526,15528],{"class":45,"line":3044},[43,15503,15504],{"class":81},"  animation",[43,15506,15507],{"class":49},": pulse ",[43,15509,931],{"class":81},[43,15511,12403],{"class":67},[43,15513,15514],{"class":81}," infinite",[43,15516,15517],{"class":81}," alternate",[43,15519,15520],{"class":49},", shake ",[43,15522,15523],{"class":81},"7",[43,15525,12403],{"class":67},[43,15527,15514],{"class":81},[43,15529,2995],{"class":49},[43,15531,15532],{"class":45,"line":3058},[43,15533,2034],{"class":49},[43,15535,15536],{"class":45,"line":3077},[43,15537,1782],{"emptyLinePlaceholder":491},[43,15539,15540,15543],{"class":45,"line":3082},[43,15541,15542],{"class":57},".crazy-card:hover",[43,15544,116],{"class":49},[43,15546,15547,15549,15551,15554,15556,15558,15560,15562,15565,15567,15569,15571,15573,15576,15578,15580,15582],{"class":45,"line":3088},[43,15548,12232],{"class":81},[43,15550,78],{"class":49},[43,15552,15553],{"class":81},"perspective",[43,15555,97],{"class":49},[43,15557,10054],{"class":81},[43,15559,10489],{"class":67},[43,15561,2408],{"class":49},[43,15563,15564],{"class":81},"rotateY",[43,15566,97],{"class":49},[43,15568,12099],{"class":81},[43,15570,15399],{"class":67},[43,15572,2408],{"class":49},[43,15574,15575],{"class":81},"rotateX",[43,15577,97],{"class":49},[43,15579,13039],{"class":81},[43,15581,15399],{"class":67},[43,15583,1160],{"class":49},[43,15585,15586,15588,15590,15592,15594,15596,15598,15600,15602,15604,15606,15608,15610,15612],{"class":45,"line":3093},[43,15587,11547],{"class":81},[43,15589,78],{"class":49},[43,15591,4401],{"class":81},[43,15593,7645],{"class":81},[43,15595,11558],{"class":81},[43,15597,10489],{"class":67},[43,15599,15440],{"class":81},[43,15601,104],{"class":49},[43,15603,4401],{"class":81},[43,15605,7645],{"class":81},[43,15607,10060],{"class":81},[43,15609,10489],{"class":67},[43,15611,15454],{"class":81},[43,15613,1056],{"class":49},[43,15615,15616,15619,15621,15624,15626,15628,15630,15632,15634,15636,15638,15641,15643,15645],{"class":45,"line":3099},[43,15617,15618],{"class":81},"    0",[43,15620,7645],{"class":81},[43,15622,15623],{"class":81}," 100",[43,15625,10489],{"class":67},[43,15627,11563],{"class":81},[43,15629,97],{"class":49},[43,15631,11568],{"class":81},[43,15633,104],{"class":49},[43,15635,4401],{"class":81},[43,15637,104],{"class":49},[43,15639,15640],{"class":81},"204",[43,15642,104],{"class":49},[43,15644,15488],{"class":81},[43,15646,1160],{"class":49},[43,15648,15649],{"class":45,"line":3145},[43,15650,2034],{"class":49},[43,15652,15653],{"class":45,"line":3152},[43,15654,1782],{"emptyLinePlaceholder":491},[43,15656,15657,15660],{"class":45,"line":3165},[43,15658,15659],{"class":57},".card-content",[43,15661,116],{"class":49},[43,15663,15664,15666,15668,15670],{"class":45,"line":3203},[43,15665,11636],{"class":81},[43,15667,78],{"class":49},[43,15669,11641],{"class":81},[43,15671,2995],{"class":49},[43,15673,15674,15676,15678,15680,15682],{"class":45,"line":3208},[43,15675,10481],{"class":81},[43,15677,78],{"class":49},[43,15679,12033],{"class":81},[43,15681,10489],{"class":67},[43,15683,2995],{"class":49},[43,15685,15686,15688,15690,15693],{"class":45,"line":3219},[43,15687,10513],{"class":81},[43,15689,78],{"class":49},[43,15691,15692],{"class":81},"#fff",[43,15694,2995],{"class":49},[43,15696,15697,15699,15701,15704],{"class":45,"line":3227},[43,15698,11857],{"class":81},[43,15700,78],{"class":49},[43,15702,15703],{"class":100},"\"Permanent Marker\"",[43,15705,11933],{"class":49},[43,15707,15708,15710,15712,15714],{"class":45,"line":3233},[43,15709,11831],{"class":81},[43,15711,78],{"class":49},[43,15713,918],{"class":81},[43,15715,2995],{"class":49},[43,15717,15718,15720,15722,15724],{"class":45,"line":3239},[43,15719,15471],{"class":81},[43,15721,78],{"class":49},[43,15723,15476],{"class":81},[43,15725,2995],{"class":49},[43,15727,15728,15730,15732,15734,15736],{"class":45,"line":3251},[43,15729,11731],{"class":81},[43,15731,78],{"class":49},[43,15733,11595],{"class":81},[43,15735,11598],{"class":67},[43,15737,2995],{"class":49},[43,15739,15740,15742,15744,15746],{"class":45,"line":3256},[43,15741,12441],{"class":81},[43,15743,78],{"class":49},[43,15745,12446],{"class":81},[43,15747,2995],{"class":49},[43,15749,15750,15752,15754,15757],{"class":45,"line":3261},[43,15751,12069],{"class":81},[43,15753,78],{"class":49},[43,15755,15756],{"class":81},"flex",[43,15758,2995],{"class":49},[43,15760,15761,15764,15766,15769],{"class":45,"line":3267},[43,15762,15763],{"class":81},"  flex-direction",[43,15765,78],{"class":49},[43,15767,15768],{"class":81},"column",[43,15770,2995],{"class":49},[43,15772,15773,15776,15778,15781],{"class":45,"line":3279},[43,15774,15775],{"class":81},"  justify-content",[43,15777,78],{"class":49},[43,15779,15780],{"class":81},"space-between",[43,15782,2995],{"class":49},[43,15784,15785],{"class":45,"line":3284},[43,15786,2034],{"class":49},[43,15788,15789],{"class":45,"line":3290},[43,15790,1782],{"emptyLinePlaceholder":491},[43,15792,15793,15796],{"class":45,"line":3312},[43,15794,15795],{"class":57},".card-title",[43,15797,116],{"class":49},[43,15799,15800,15802,15804,15807],{"class":45,"line":3317},[43,15801,11857],{"class":81},[43,15803,78],{"class":49},[43,15805,15806],{"class":100},"\"Creepster\"",[43,15808,11933],{"class":49},[43,15810,15811,15813,15815,15818,15820],{"class":45,"line":3327},[43,15812,11874],{"class":81},[43,15814,78],{"class":49},[43,15816,15817],{"class":81},"32",[43,15819,10489],{"class":67},[43,15821,2995],{"class":49},[43,15823,15824,15827,15829,15831,15833,15835,15837],{"class":45,"line":3337},[43,15825,15826],{"class":81},"  margin",[43,15828,78],{"class":49},[43,15830,4401],{"class":81},[43,15832,7645],{"class":81},[43,15834,10086],{"class":81},[43,15836,10489],{"class":67},[43,15838,2995],{"class":49},[43,15840,15841,15843,15845,15847],{"class":45,"line":3348},[43,15842,10513],{"class":81},[43,15844,78],{"class":49},[43,15846,15692],{"class":81},[43,15848,2995],{"class":49},[43,15850,15851,15854,15856,15858,15860,15862,15864,15866,15868,15871,15873,15875,15877,15879,15881,15883,15885,15887,15889,15891,15893,15895],{"class":45,"line":3354},[43,15852,15853],{"class":81},"  text-shadow",[43,15855,78],{"class":49},[43,15857,918],{"class":81},[43,15859,10489],{"class":67},[43,15861,14781],{"class":81},[43,15863,10489],{"class":67},[43,15865,12537],{"class":81},[43,15867,10489],{"class":67},[43,15869,15870],{"class":81}," #000",[43,15872,104],{"class":49},[43,15874,4401],{"class":81},[43,15876,7645],{"class":81},[43,15878,10086],{"class":81},[43,15880,10489],{"class":67},[43,15882,15440],{"class":81},[43,15884,104],{"class":49},[43,15886,4401],{"class":81},[43,15888,7645],{"class":81},[43,15890,10492],{"class":81},[43,15892,10489],{"class":67},[43,15894,15440],{"class":81},[43,15896,2995],{"class":49},[43,15898,15899,15902,15904,15906,15908],{"class":45,"line":3360},[43,15900,15901],{"class":81},"  letter-spacing",[43,15903,78],{"class":49},[43,15905,918],{"class":81},[43,15907,10489],{"class":67},[43,15909,2995],{"class":49},[43,15911,15912,15914,15917,15919,15921,15923,15925],{"class":45,"line":3365},[43,15913,15504],{"class":81},[43,15915,15916],{"class":49},": glitch ",[43,15918,931],{"class":81},[43,15920,12403],{"class":67},[43,15922,15514],{"class":81},[43,15924,15517],{"class":81},[43,15926,2995],{"class":49},[43,15928,15929],{"class":45,"line":3374},[43,15930,2034],{"class":49},[43,15932,15933],{"class":45,"line":3399},[43,15934,1782],{"emptyLinePlaceholder":491},[43,15936,15937,15940],{"class":45,"line":3404},[43,15938,15939],{"class":57},".card-text",[43,15941,116],{"class":49},[43,15943,15944,15946,15948,15950,15952],{"class":45,"line":3409},[43,15945,11874],{"class":81},[43,15947,78],{"class":49},[43,15949,11942],{"class":81},[43,15951,10489],{"class":67},[43,15953,2995],{"class":49},[43,15955,15956,15959,15961,15964],{"class":45,"line":3414},[43,15957,15958],{"class":81},"  line-height",[43,15960,78],{"class":49},[43,15962,15963],{"class":81},"1.4",[43,15965,2995],{"class":49},[43,15967,15968,15970,15972,15974,15976],{"class":45,"line":3434},[43,15969,11807],{"class":81},[43,15971,78],{"class":49},[43,15973,12033],{"class":81},[43,15975,10489],{"class":67},[43,15977,2995],{"class":49},[43,15979,15980,15982,15984,15986,15988,15991,15993,15995,15997,15999],{"class":45,"line":3449},[43,15981,15853],{"class":81},[43,15983,78],{"class":49},[43,15985,905],{"class":81},[43,15987,10489],{"class":67},[43,15989,15990],{"class":81}," 1",[43,15992,10489],{"class":67},[43,15994,14781],{"class":81},[43,15996,10489],{"class":67},[43,15998,15870],{"class":81},[43,16000,2995],{"class":49},[43,16002,16003,16005,16007,16010,16012,16014,16016],{"class":45,"line":12106},[43,16004,12232],{"class":81},[43,16006,78],{"class":49},[43,16008,16009],{"class":81},"translateZ",[43,16011,97],{"class":49},[43,16013,12033],{"class":81},[43,16015,10489],{"class":67},[43,16017,1160],{"class":49},[43,16019,16020],{"class":45,"line":12118},[43,16021,2034],{"class":49},[43,16023,16024],{"class":45,"line":12131},[43,16025,1782],{"emptyLinePlaceholder":491},[43,16027,16028,16031],{"class":45,"line":12136},[43,16029,16030],{"class":57},".card-button",[43,16032,116],{"class":49},[43,16034,16035,16037,16039,16042],{"class":45,"line":12141},[43,16036,12069],{"class":81},[43,16038,78],{"class":49},[43,16040,16041],{"class":81},"inline-block",[43,16043,2995],{"class":49},[43,16045,16046,16048,16050,16052,16054,16056,16058],{"class":45,"line":12149},[43,16047,10481],{"class":81},[43,16049,78],{"class":49},[43,16051,10486],{"class":81},[43,16053,10489],{"class":67},[43,16055,10492],{"class":81},[43,16057,10489],{"class":67},[43,16059,2995],{"class":49},[43,16061,16062,16064,16066,16068,16070,16072,16074,16076,16078,16080,16082],{"class":45,"line":12160},[43,16063,11745],{"class":81},[43,16065,78],{"class":49},[43,16067,11750],{"class":81},[43,16069,97],{"class":49},[43,16071,15396],{"class":81},[43,16073,15399],{"class":67},[43,16075,104],{"class":49},[43,16077,15404],{"class":81},[43,16079,104],{"class":49},[43,16081,15409],{"class":81},[43,16083,1160],{"class":49},[43,16085,16086,16088,16090,16092,16094,16096,16099],{"class":45,"line":12173},[43,16087,12334],{"class":81},[43,16089,78],{"class":49},[43,16091,918],{"class":81},[43,16093,10489],{"class":67},[43,16095,12343],{"class":81},[43,16097,16098],{"class":81}," #fff",[43,16100,2995],{"class":49},[43,16102,16103,16105,16107,16109],{"class":45,"line":12178},[43,16104,10513],{"class":81},[43,16106,78],{"class":49},[43,16108,15692],{"class":81},[43,16110,2995],{"class":49},[43,16112,16113,16115,16117,16119],{"class":45,"line":12183},[43,16114,11857],{"class":81},[43,16116,78],{"class":49},[43,16118,15703],{"class":100},[43,16120,11933],{"class":49},[43,16122,16123,16125,16127,16129,16131],{"class":45,"line":12191},[43,16124,11874],{"class":81},[43,16126,78],{"class":49},[43,16128,12959],{"class":81},[43,16130,10489],{"class":67},[43,16132,2995],{"class":49},[43,16134,16135,16137,16139,16141],{"class":45,"line":12202},[43,16136,13022],{"class":81},[43,16138,78],{"class":49},[43,16140,12507],{"class":81},[43,16142,2995],{"class":49},[43,16144,16145,16147,16149,16151,16153],{"class":45,"line":12215},[43,16146,11533],{"class":81},[43,16148,78],{"class":49},[43,16150,13039],{"class":81},[43,16152,10489],{"class":67},[43,16154,2995],{"class":49},[43,16156,16157,16159,16161,16163,16165,16167,16169,16171,16173,16175],{"class":45,"line":12229},[43,16158,15853],{"class":81},[43,16160,78],{"class":49},[43,16162,905],{"class":81},[43,16164,10489],{"class":67},[43,16166,15990],{"class":81},[43,16168,10489],{"class":67},[43,16170,14781],{"class":81},[43,16172,10489],{"class":67},[43,16174,15870],{"class":81},[43,16176,2995],{"class":49},[43,16178,16179,16181,16183,16185,16187,16189],{"class":45,"line":12249},[43,16180,12393],{"class":81},[43,16182,78],{"class":49},[43,16184,144],{"class":81},[43,16186,12400],{"class":81},[43,16188,12403],{"class":67},[43,16190,2995],{"class":49},[43,16192,16193,16195,16197,16199,16201,16203,16205],{"class":45,"line":12260},[43,16194,12232],{"class":81},[43,16196,78],{"class":49},[43,16198,16009],{"class":81},[43,16200,97],{"class":49},[43,16202,11812],{"class":81},[43,16204,10489],{"class":67},[43,16206,1160],{"class":49},[43,16208,16209,16211,16213,16215],{"class":45,"line":12273},[43,16210,12704],{"class":81},[43,16212,78],{"class":49},[43,16214,12709],{"class":81},[43,16216,2995],{"class":49},[43,16218,16219],{"class":45,"line":12278},[43,16220,2034],{"class":49},[43,16222,16223],{"class":45,"line":12283},[43,16224,1782],{"emptyLinePlaceholder":491},[43,16226,16227,16230],{"class":45,"line":12291},[43,16228,16229],{"class":57},".card-button:hover",[43,16231,116],{"class":49},[43,16233,16234,16236,16238,16240],{"class":45,"line":12304},[43,16235,11745],{"class":81},[43,16237,78],{"class":49},[43,16239,15692],{"class":81},[43,16241,2995],{"class":49},[43,16243,16244,16246,16248,16250],{"class":45,"line":12331},[43,16245,10513],{"class":81},[43,16247,78],{"class":49},[43,16249,15404],{"class":81},[43,16251,2995],{"class":49},[43,16253,16254,16256,16258,16260,16262,16264,16266,16268],{"class":45,"line":12351},[43,16255,11547],{"class":81},[43,16257,78],{"class":49},[43,16259,4401],{"class":81},[43,16261,7645],{"class":81},[43,16263,12315],{"class":81},[43,16265,10489],{"class":67},[43,16267,16098],{"class":81},[43,16269,2995],{"class":49},[43,16271,16272,16274,16276,16278,16280,16282,16284,16286,16289,16291,16294],{"class":45,"line":12365},[43,16273,12232],{"class":81},[43,16275,78],{"class":49},[43,16277,16009],{"class":81},[43,16279,97],{"class":49},[43,16281,11623],{"class":81},[43,16283,10489],{"class":67},[43,16285,2408],{"class":49},[43,16287,16288],{"class":81},"scale",[43,16290,97],{"class":49},[43,16292,16293],{"class":81},"1.05",[43,16295,1160],{"class":49},[43,16297,16298],{"class":45,"line":12378},[43,16299,2034],{"class":49},[43,16301,16302],{"class":45,"line":12390},[43,16303,1782],{"emptyLinePlaceholder":491},[43,16305,16306,16309],{"class":45,"line":12411},[43,16307,16308],{"class":57},".glitch-overlay",[43,16310,116],{"class":49},[43,16312,16313,16315,16317,16319],{"class":45,"line":12427},[43,16314,11636],{"class":81},[43,16316,78],{"class":49},[43,16318,11691],{"class":81},[43,16320,2995],{"class":49},[43,16322,16323,16325,16327,16329],{"class":45,"line":12438},[43,16324,11698],{"class":81},[43,16326,78],{"class":49},[43,16328,4401],{"class":81},[43,16330,2995],{"class":49},[43,16332,16333,16335,16337,16339],{"class":45,"line":12451},[43,16334,11709],{"class":81},[43,16336,78],{"class":49},[43,16338,4401],{"class":81},[43,16340,2995],{"class":49},[43,16342,16343,16345,16347,16349,16351],{"class":45,"line":12456},[43,16344,11590],{"class":81},[43,16346,78],{"class":49},[43,16348,11595],{"class":81},[43,16350,11598],{"class":67},[43,16352,2995],{"class":49},[43,16354,16355,16357,16359,16361,16363],{"class":45,"line":12461},[43,16356,11731],{"class":81},[43,16358,78],{"class":49},[43,16360,11595],{"class":81},[43,16362,11598],{"class":67},[43,16364,2995],{"class":49},[43,16366,16367,16369,16371,16374],{"class":45,"line":12469},[43,16368,11745],{"class":81},[43,16370,78],{"class":49},[43,16372,16373],{"class":81},"transparent",[43,16375,2995],{"class":49},[43,16377,16378,16380,16382,16384],{"class":45,"line":12481},[43,16379,11831],{"class":81},[43,16381,78],{"class":49},[43,16383,905],{"class":81},[43,16385,2995],{"class":49},[43,16387,16388,16391,16393,16396],{"class":45,"line":12486},[43,16389,16390],{"class":81},"  mix-blend-mode",[43,16392,78],{"class":49},[43,16394,16395],{"class":81},"overlay",[43,16397,2995],{"class":49},[43,16399,16400,16403,16405,16408],{"class":45,"line":12491},[43,16401,16402],{"class":81},"  opacity",[43,16404,78],{"class":49},[43,16406,16407],{"class":81},"0.2",[43,16409,2995],{"class":49},[43,16411,16412,16415,16417,16419],{"class":45,"line":12499},[43,16413,16414],{"class":81},"  pointer-events",[43,16416,78],{"class":49},[43,16418,12507],{"class":81},[43,16420,2995],{"class":49},[43,16422,16423,16425,16428,16430,16432,16434],{"class":45,"line":12512},[43,16424,15504],{"class":81},[43,16426,16427],{"class":49},": glitchOverlay ",[43,16429,11902],{"class":81},[43,16431,12403],{"class":67},[43,16433,15514],{"class":81},[43,16435,2995],{"class":49},[43,16437,16438],{"class":45,"line":12524},[43,16439,2034],{"class":49},[43,16441,16442],{"class":45,"line":12563},[43,16443,1782],{"emptyLinePlaceholder":491},[43,16445,16446,16449],{"class":45,"line":12568},[43,16447,16448],{"class":57},".crazy-shapes",[43,16450,116],{"class":49},[43,16452,16453,16455,16457,16459],{"class":45,"line":12573},[43,16454,11636],{"class":81},[43,16456,78],{"class":49},[43,16458,11691],{"class":81},[43,16460,2995],{"class":49},[43,16462,16463,16465,16467,16469],{"class":45,"line":12579},[43,16464,11698],{"class":81},[43,16466,78],{"class":49},[43,16468,4401],{"class":81},[43,16470,2995],{"class":49},[43,16472,16473,16475,16477,16479],{"class":45,"line":12587},[43,16474,11709],{"class":81},[43,16476,78],{"class":49},[43,16478,4401],{"class":81},[43,16480,2995],{"class":49},[43,16482,16483,16485,16487,16489,16491],{"class":45,"line":12600},[43,16484,11590],{"class":81},[43,16486,78],{"class":49},[43,16488,11595],{"class":81},[43,16490,11598],{"class":67},[43,16492,2995],{"class":49},[43,16494,16495,16497,16499,16501,16503],{"class":45,"line":12625},[43,16496,11731],{"class":81},[43,16498,78],{"class":49},[43,16500,11595],{"class":81},[43,16502,11598],{"class":67},[43,16504,2995],{"class":49},[43,16506,16507,16509,16511,16513],{"class":45,"line":12636},[43,16508,11831],{"class":81},[43,16510,78],{"class":49},[43,16512,4401],{"class":81},[43,16514,2995],{"class":49},[43,16516,16517,16519,16521,16523],{"class":45,"line":12647},[43,16518,11648],{"class":81},[43,16520,78],{"class":49},[43,16522,11653],{"class":81},[43,16524,2995],{"class":49},[43,16526,16527,16529,16531,16533],{"class":45,"line":12660},[43,16528,16414],{"class":81},[43,16530,78],{"class":49},[43,16532,12507],{"class":81},[43,16534,2995],{"class":49},[43,16536,16537],{"class":45,"line":12673},[43,16538,2034],{"class":49},[43,16540,16541],{"class":45,"line":12688},[43,16542,1782],{"emptyLinePlaceholder":491},[43,16544,16545,16548],{"class":45,"line":12701},[43,16546,16547],{"class":57},".shape",[43,16549,116],{"class":49},[43,16551,16552,16554,16556,16558],{"class":45,"line":12714},[43,16553,11636],{"class":81},[43,16555,78],{"class":49},[43,16557,11691],{"class":81},[43,16559,2995],{"class":49},[43,16561,16562,16564,16566,16569,16571,16573,16575,16577,16579,16581,16583,16585],{"class":45,"line":12752},[43,16563,11745],{"class":81},[43,16565,78],{"class":49},[43,16567,16568],{"class":81},"rgba",[43,16570,97],{"class":49},[43,16572,11568],{"class":81},[43,16574,104],{"class":49},[43,16576,11568],{"class":81},[43,16578,104],{"class":49},[43,16580,11568],{"class":81},[43,16582,104],{"class":49},[43,16584,12558],{"class":81},[43,16586,1160],{"class":49},[43,16588,16589,16591,16593,16595,16597],{"class":45,"line":12769},[43,16590,11533],{"class":81},[43,16592,78],{"class":49},[43,16594,12222],{"class":81},[43,16596,11598],{"class":67},[43,16598,2995],{"class":49},[43,16600,16601,16603,16606,16608,16610,16612,16615],{"class":45,"line":12783},[43,16602,15504],{"class":81},[43,16604,16605],{"class":49},": floatAround ",[43,16607,12099],{"class":81},[43,16609,12403],{"class":67},[43,16611,15514],{"class":81},[43,16613,16614],{"class":81}," linear",[43,16616,2995],{"class":49},[43,16618,16619],{"class":45,"line":12788},[43,16620,2034],{"class":49},[43,16622,16623],{"class":45,"line":12793},[43,16624,1782],{"emptyLinePlaceholder":491},[43,16626,16627,16630,16632,16634],{"class":45,"line":12801},[43,16628,16629],{"class":57},".shape:nth-child",[43,16631,97],{"class":49},[43,16633,905],{"class":81},[43,16635,1882],{"class":49},[43,16637,16638,16640,16642,16644,16646],{"class":45,"line":12819},[43,16639,11590],{"class":81},[43,16641,78],{"class":49},[43,16643,11595],{"class":81},[43,16645,10489],{"class":67},[43,16647,2995],{"class":49},[43,16649,16650,16652,16654,16656,16658],{"class":45,"line":12858},[43,16651,11731],{"class":81},[43,16653,78],{"class":49},[43,16655,11595],{"class":81},[43,16657,10489],{"class":67},[43,16659,2995],{"class":49},[43,16661,16662,16664,16666,16669,16671],{"class":45,"line":12863},[43,16663,11698],{"class":81},[43,16665,78],{"class":49},[43,16667,16668],{"class":81},"-20",[43,16670,10489],{"class":67},[43,16672,2995],{"class":49},[43,16674,16675,16677,16679,16681,16683],{"class":45,"line":12868},[43,16676,11709],{"class":81},[43,16678,78],{"class":49},[43,16680,16668],{"class":81},[43,16682,10489],{"class":67},[43,16684,2995],{"class":49},[43,16686,16687,16689,16691,16693,16695,16697,16699,16701,16703,16705,16707,16709],{"class":45,"line":12876},[43,16688,11745],{"class":81},[43,16690,78],{"class":49},[43,16692,16568],{"class":81},[43,16694,97],{"class":49},[43,16696,11568],{"class":81},[43,16698,104],{"class":49},[43,16700,4401],{"class":81},[43,16702,104],{"class":49},[43,16704,15640],{"class":81},[43,16706,104],{"class":49},[43,16708,16407],{"class":81},[43,16710,1160],{"class":49},[43,16712,16713],{"class":45,"line":12893},[43,16714,2034],{"class":49},[43,16716,16717],{"class":45,"line":12898},[43,16718,1782],{"emptyLinePlaceholder":491},[43,16720,16721,16723,16725,16727],{"class":45,"line":12903},[43,16722,16629],{"class":57},[43,16724,97],{"class":49},[43,16726,918],{"class":81},[43,16728,1882],{"class":49},[43,16730,16731,16733,16735,16738,16740],{"class":45,"line":12909},[43,16732,11590],{"class":81},[43,16734,78],{"class":49},[43,16736,16737],{"class":81},"150",[43,16739,10489],{"class":67},[43,16741,2995],{"class":49},[43,16743,16744,16746,16748,16750,16752],{"class":45,"line":12917},[43,16745,11731],{"class":81},[43,16747,78],{"class":49},[43,16749,16737],{"class":81},[43,16751,10489],{"class":67},[43,16753,2995],{"class":49},[43,16755,16756,16759,16761,16764,16766],{"class":45,"line":12928},[43,16757,16758],{"class":81},"  bottom",[43,16760,78],{"class":49},[43,16762,16763],{"class":81},"-30",[43,16765,10489],{"class":67},[43,16767,2995],{"class":49},[43,16769,16770,16772,16774,16776,16778],{"class":45,"line":12941},[43,16771,11720],{"class":81},[43,16773,78],{"class":49},[43,16775,16763],{"class":81},[43,16777,10489],{"class":67},[43,16779,2995],{"class":49},[43,16781,16782,16784,16786,16788,16790,16793,16795,16797,16799,16802,16804,16806],{"class":45,"line":12952},[43,16783,11745],{"class":81},[43,16785,78],{"class":49},[43,16787,16568],{"class":81},[43,16789,97],{"class":49},[43,16791,16792],{"class":81},"51",[43,16794,104],{"class":49},[43,16796,16792],{"class":81},[43,16798,104],{"class":49},[43,16800,16801],{"class":81},"153",[43,16803,104],{"class":49},[43,16805,16407],{"class":81},[43,16807,1160],{"class":49},[43,16809,16810,16813,16815,16818,16820],{"class":45,"line":12966},[43,16811,16812],{"class":81},"  animation-delay",[43,16814,78],{"class":49},[43,16816,16817],{"class":81},"-5",[43,16819,12403],{"class":67},[43,16821,2995],{"class":49},[43,16823,16824],{"class":45,"line":12977},[43,16825,2034],{"class":49},[43,16827,16828],{"class":45,"line":12982},[43,16829,1782],{"emptyLinePlaceholder":491},[43,16831,16832,16834,16836,16838],{"class":45,"line":12987},[43,16833,16629],{"class":57},[43,16835,97],{"class":49},[43,16837,931],{"class":81},[43,16839,1882],{"class":49},[43,16841,16842,16844,16846,16849,16851],{"class":45,"line":12995},[43,16843,11590],{"class":81},[43,16845,78],{"class":49},[43,16847,16848],{"class":81},"80",[43,16850,10489],{"class":67},[43,16852,2995],{"class":49},[43,16854,16855,16857,16859,16861,16863],{"class":45,"line":13006},[43,16856,11731],{"class":81},[43,16858,78],{"class":49},[43,16860,16848],{"class":81},[43,16862,10489],{"class":67},[43,16864,2995],{"class":49},[43,16866,16867,16869,16871,16873,16875],{"class":45,"line":13019},[43,16868,11698],{"class":81},[43,16870,78],{"class":49},[43,16872,12222],{"class":81},[43,16874,11598],{"class":67},[43,16876,2995],{"class":49},[43,16878,16879,16881,16883,16885,16887],{"class":45,"line":13031},[43,16880,11709],{"class":81},[43,16882,78],{"class":49},[43,16884,12222],{"class":81},[43,16886,11598],{"class":67},[43,16888,2995],{"class":49},[43,16890,16891,16893,16895,16897,16899,16901,16903,16905,16907,16909,16911,16913],{"class":45,"line":13046},[43,16892,11745],{"class":81},[43,16894,78],{"class":49},[43,16896,16568],{"class":81},[43,16898,97],{"class":49},[43,16900,11568],{"class":81},[43,16902,104],{"class":49},[43,16904,11568],{"class":81},[43,16906,104],{"class":49},[43,16908,11568],{"class":81},[43,16910,104],{"class":49},[43,16912,12558],{"class":81},[43,16914,1160],{"class":49},[43,16916,16917,16919,16921,16924,16926],{"class":45,"line":13051},[43,16918,16812],{"class":81},[43,16920,78],{"class":49},[43,16922,16923],{"class":81},"-8",[43,16925,12403],{"class":67},[43,16927,2995],{"class":49},[43,16929,16930],{"class":45,"line":13056},[43,16931,2034],{"class":49},[43,16933,16934],{"class":45,"line":13064},[43,16935,1782],{"emptyLinePlaceholder":491},[43,16937,16938,16941,16944],{"class":45,"line":13076},[43,16939,16940],{"class":67},"@keyframes",[43,16942,16943],{"class":74}," pulse",[43,16945,116],{"class":49},[43,16947,16949,16952],{"class":45,"line":16948},145,[43,16950,16951],{"class":57},"  0%",[43,16953,116],{"class":49},[43,16955,16957,16960,16962,16964,16966,16968],{"class":45,"line":16956},146,[43,16958,16959],{"class":81},"    transform",[43,16961,78],{"class":49},[43,16963,16288],{"class":81},[43,16965,97],{"class":49},[43,16967,905],{"class":81},[43,16969,1160],{"class":49},[43,16971,16973],{"class":45,"line":16972},147,[43,16974,2028],{"class":49},[43,16976,16978,16981],{"class":45,"line":16977},148,[43,16979,16980],{"class":57},"  100%",[43,16982,116],{"class":49},[43,16984,16986,16988,16990,16992,16994,16997],{"class":45,"line":16985},149,[43,16987,16959],{"class":81},[43,16989,78],{"class":49},[43,16991,16288],{"class":81},[43,16993,97],{"class":49},[43,16995,16996],{"class":81},"1.03",[43,16998,1160],{"class":49},[43,17000,17002],{"class":45,"line":17001},150,[43,17003,2028],{"class":49},[43,17005,17007],{"class":45,"line":17006},151,[43,17008,2034],{"class":49},[43,17010,17012],{"class":45,"line":17011},152,[43,17013,1782],{"emptyLinePlaceholder":491},[43,17015,17017,17019,17022],{"class":45,"line":17016},153,[43,17018,16940],{"class":67},[43,17020,17021],{"class":74}," shake",[43,17023,116],{"class":49},[43,17025,17027,17029],{"class":45,"line":17026},154,[43,17028,16951],{"class":57},[43,17030,1056],{"class":49},[43,17032,17034,17036],{"class":45,"line":17033},155,[43,17035,16980],{"class":57},[43,17037,116],{"class":49},[43,17039,17041,17043,17045,17048,17050,17052],{"class":45,"line":17040},156,[43,17042,16959],{"class":81},[43,17044,78],{"class":49},[43,17046,17047],{"class":81},"translateX",[43,17049,97],{"class":49},[43,17051,4401],{"class":81},[43,17053,1160],{"class":49},[43,17055,17057],{"class":45,"line":17056},157,[43,17058,2028],{"class":49},[43,17060,17062,17065],{"class":45,"line":17061},158,[43,17063,17064],{"class":57},"  10%",[43,17066,1056],{"class":49},[43,17068,17070,17073],{"class":45,"line":17069},159,[43,17071,17072],{"class":57},"  30%",[43,17074,1056],{"class":49},[43,17076,17078,17081],{"class":45,"line":17077},160,[43,17079,17080],{"class":57},"  50%",[43,17082,1056],{"class":49},[43,17084,17086,17089],{"class":45,"line":17085},161,[43,17087,17088],{"class":57},"  70%",[43,17090,1056],{"class":49},[43,17092,17094,17097],{"class":45,"line":17093},162,[43,17095,17096],{"class":57},"  90%",[43,17098,116],{"class":49},[43,17100,17102,17104,17106,17108,17110,17112,17114,17116,17119,17121,17124,17126],{"class":45,"line":17101},163,[43,17103,16959],{"class":81},[43,17105,78],{"class":49},[43,17107,17047],{"class":81},[43,17109,97],{"class":49},[43,17111,12812],{"class":81},[43,17113,10489],{"class":67},[43,17115,2408],{"class":49},[43,17117,17118],{"class":81},"rotate",[43,17120,97],{"class":49},[43,17122,17123],{"class":81},"-0.5",[43,17125,15399],{"class":67},[43,17127,1160],{"class":49},[43,17129,17131],{"class":45,"line":17130},164,[43,17132,2028],{"class":49},[43,17134,17136,17139],{"class":45,"line":17135},165,[43,17137,17138],{"class":57},"  20%",[43,17140,1056],{"class":49},[43,17142,17144,17147],{"class":45,"line":17143},166,[43,17145,17146],{"class":57},"  40%",[43,17148,1056],{"class":49},[43,17150,17152,17155],{"class":45,"line":17151},167,[43,17153,17154],{"class":57},"  60%",[43,17156,1056],{"class":49},[43,17158,17160,17163],{"class":45,"line":17159},168,[43,17161,17162],{"class":57},"  80%",[43,17164,116],{"class":49},[43,17166,17168,17170,17172,17174,17176,17178,17180,17182,17184,17186,17188,17190],{"class":45,"line":17167},169,[43,17169,16959],{"class":81},[43,17171,78],{"class":49},[43,17173,17047],{"class":81},[43,17175,97],{"class":49},[43,17177,918],{"class":81},[43,17179,10489],{"class":67},[43,17181,2408],{"class":49},[43,17183,17118],{"class":81},[43,17185,97],{"class":49},[43,17187,15488],{"class":81},[43,17189,15399],{"class":67},[43,17191,1160],{"class":49},[43,17193,17195],{"class":45,"line":17194},170,[43,17196,2028],{"class":49},[43,17198,17200],{"class":45,"line":17199},171,[43,17201,2034],{"class":49},[43,17203,17205],{"class":45,"line":17204},172,[43,17206,1782],{"emptyLinePlaceholder":491},[43,17208,17210,17212,17215],{"class":45,"line":17209},173,[43,17211,16940],{"class":67},[43,17213,17214],{"class":74}," glitch",[43,17216,116],{"class":49},[43,17218,17220,17222],{"class":45,"line":17219},174,[43,17221,16951],{"class":57},[43,17223,1056],{"class":49},[43,17225,17227,17229],{"class":45,"line":17226},175,[43,17228,16980],{"class":57},[43,17230,116],{"class":49},[43,17232,17234,17237,17239,17241,17243,17245,17247,17249,17251,17253,17255,17257,17259,17261,17263,17265,17267,17269,17271,17273,17275,17277],{"class":45,"line":17233},176,[43,17235,17236],{"class":81},"    text-shadow",[43,17238,78],{"class":49},[43,17240,918],{"class":81},[43,17242,10489],{"class":67},[43,17244,14781],{"class":81},[43,17246,10489],{"class":67},[43,17248,12537],{"class":81},[43,17250,10489],{"class":67},[43,17252,15870],{"class":81},[43,17254,104],{"class":49},[43,17256,4401],{"class":81},[43,17258,7645],{"class":81},[43,17260,10086],{"class":81},[43,17262,10489],{"class":67},[43,17264,15440],{"class":81},[43,17266,104],{"class":49},[43,17268,4401],{"class":81},[43,17270,7645],{"class":81},[43,17272,10492],{"class":81},[43,17274,10489],{"class":67},[43,17276,15440],{"class":81},[43,17278,2995],{"class":49},[43,17280,17282,17284,17286,17289,17291,17293],{"class":45,"line":17281},177,[43,17283,16959],{"class":81},[43,17285,78],{"class":49},[43,17287,17288],{"class":81},"translate",[43,17290,97],{"class":49},[43,17292,4401],{"class":81},[43,17294,1160],{"class":49},[43,17296,17298],{"class":45,"line":17297},178,[43,17299,2028],{"class":49},[43,17301,17303,17305],{"class":45,"line":17302},179,[43,17304,17138],{"class":57},[43,17306,116],{"class":49},[43,17308,17310,17312,17314,17316,17318,17320,17323,17325,17327,17329,17331,17333,17336],{"class":45,"line":17309},180,[43,17311,17236],{"class":81},[43,17313,78],{"class":49},[43,17315,12812],{"class":81},[43,17317,10489],{"class":67},[43,17319,7645],{"class":81},[43,17321,17322],{"class":81}," #0ff",[43,17324,104],{"class":49},[43,17326,918],{"class":81},[43,17328,10489],{"class":67},[43,17330,14781],{"class":81},[43,17332,10489],{"class":67},[43,17334,17335],{"class":81}," #f0f",[43,17337,2995],{"class":49},[43,17339,17341,17343,17345,17347,17349,17351,17353,17355,17357,17359],{"class":45,"line":17340},181,[43,17342,16959],{"class":81},[43,17344,78],{"class":49},[43,17346,17288],{"class":81},[43,17348,97],{"class":49},[43,17350,905],{"class":81},[43,17352,10489],{"class":67},[43,17354,104],{"class":49},[43,17356,905],{"class":81},[43,17358,10489],{"class":67},[43,17360,1160],{"class":49},[43,17362,17364],{"class":45,"line":17363},182,[43,17365,2028],{"class":49},[43,17367,17369,17371],{"class":45,"line":17368},183,[43,17370,17146],{"class":57},[43,17372,116],{"class":49},[43,17374,17376,17378,17380,17382,17384,17386,17389,17391,17393,17395,17398,17400,17403],{"class":45,"line":17375},184,[43,17377,17236],{"class":81},[43,17379,78],{"class":49},[43,17381,918],{"class":81},[43,17383,10489],{"class":67},[43,17385,7645],{"class":81},[43,17387,17388],{"class":81}," #f00",[43,17390,104],{"class":49},[43,17392,12812],{"class":81},[43,17394,10489],{"class":67},[43,17396,17397],{"class":81}," -2",[43,17399,10489],{"class":67},[43,17401,17402],{"class":81}," #0f0",[43,17404,2995],{"class":49},[43,17406,17408,17410,17412,17414,17416,17419,17421,17423,17425,17427],{"class":45,"line":17407},185,[43,17409,16959],{"class":81},[43,17411,78],{"class":49},[43,17413,17288],{"class":81},[43,17415,97],{"class":49},[43,17417,17418],{"class":81},"-1",[43,17420,10489],{"class":67},[43,17422,104],{"class":49},[43,17424,17418],{"class":81},[43,17426,10489],{"class":67},[43,17428,1160],{"class":49},[43,17430,17432],{"class":45,"line":17431},186,[43,17433,2028],{"class":49},[43,17435,17437,17439],{"class":45,"line":17436},187,[43,17438,17154],{"class":57},[43,17440,116],{"class":49},[43,17442,17444,17446,17448,17450,17452,17455,17457,17460,17462,17464,17466,17468,17470,17473],{"class":45,"line":17443},188,[43,17445,17236],{"class":81},[43,17447,78],{"class":49},[43,17449,905],{"class":81},[43,17451,10489],{"class":67},[43,17453,17454],{"class":81}," -1",[43,17456,10489],{"class":67},[43,17458,17459],{"class":81}," #ff0",[43,17461,104],{"class":49},[43,17463,17418],{"class":81},[43,17465,10489],{"class":67},[43,17467,15990],{"class":81},[43,17469,10489],{"class":67},[43,17471,17472],{"class":81}," #00f",[43,17474,2995],{"class":49},[43,17476,17478,17480,17482,17484,17486,17488,17490,17492,17494,17496],{"class":45,"line":17477},189,[43,17479,16959],{"class":81},[43,17481,78],{"class":49},[43,17483,17288],{"class":81},[43,17485,97],{"class":49},[43,17487,905],{"class":81},[43,17489,10489],{"class":67},[43,17491,104],{"class":49},[43,17493,17418],{"class":81},[43,17495,10489],{"class":67},[43,17497,1160],{"class":49},[43,17499,17501],{"class":45,"line":17500},190,[43,17502,2028],{"class":49},[43,17504,17506,17508],{"class":45,"line":17505},191,[43,17507,17162],{"class":57},[43,17509,116],{"class":49},[43,17511,17513,17515,17517,17519,17521,17523,17525,17527,17529,17531,17533,17535,17537,17539],{"class":45,"line":17512},192,[43,17514,17236],{"class":81},[43,17516,78],{"class":49},[43,17518,17418],{"class":81},[43,17520,10489],{"class":67},[43,17522,15990],{"class":81},[43,17524,10489],{"class":67},[43,17526,17335],{"class":81},[43,17528,104],{"class":49},[43,17530,905],{"class":81},[43,17532,10489],{"class":67},[43,17534,17454],{"class":81},[43,17536,10489],{"class":67},[43,17538,17322],{"class":81},[43,17540,2995],{"class":49},[43,17542,17544,17546,17548,17550,17552,17554,17556,17558,17560,17562],{"class":45,"line":17543},193,[43,17545,16959],{"class":81},[43,17547,78],{"class":49},[43,17549,17288],{"class":81},[43,17551,97],{"class":49},[43,17553,17418],{"class":81},[43,17555,10489],{"class":67},[43,17557,104],{"class":49},[43,17559,905],{"class":81},[43,17561,10489],{"class":67},[43,17563,1160],{"class":49},[43,17565,17567],{"class":45,"line":17566},194,[43,17568,2028],{"class":49},[43,17570,17572],{"class":45,"line":17571},195,[43,17573,2034],{"class":49},[43,17575,17577],{"class":45,"line":17576},196,[43,17578,1782],{"emptyLinePlaceholder":491},[43,17580,17582,17584,17587],{"class":45,"line":17581},197,[43,17583,16940],{"class":67},[43,17585,17586],{"class":74}," glitchOverlay",[43,17588,116],{"class":49},[43,17590,17592,17594],{"class":45,"line":17591},198,[43,17593,16951],{"class":57},[43,17595,1056],{"class":49},[43,17597,17599,17601],{"class":45,"line":17598},199,[43,17600,16980],{"class":57},[43,17602,116],{"class":49},[43,17604,17606,17609,17611,17613],{"class":45,"line":17605},200,[43,17607,17608],{"class":81},"    opacity",[43,17610,78],{"class":49},[43,17612,12558],{"class":81},[43,17614,2995],{"class":49},[43,17616,17618,17621,17623,17625,17627],{"class":45,"line":17617},201,[43,17619,17620],{"class":81},"    background-position",[43,17622,78],{"class":49},[43,17624,4401],{"class":81},[43,17626,7645],{"class":81},[43,17628,2995],{"class":49},[43,17630,17632],{"class":45,"line":17631},202,[43,17633,2028],{"class":49},[43,17635,17637,17639],{"class":45,"line":17636},203,[43,17638,17138],{"class":57},[43,17640,116],{"class":49},[43,17642,17644,17646,17648,17650],{"class":45,"line":17643},204,[43,17645,17608],{"class":81},[43,17647,78],{"class":49},[43,17649,12747],{"class":81},[43,17651,2995],{"class":49},[43,17653,17655,17658,17660,17662],{"class":45,"line":17654},205,[43,17656,17657],{"class":81},"    background-image",[43,17659,78],{"class":49},[43,17661,11750],{"class":81},[43,17663,2698],{"class":49},[43,17665,17667,17670,17672],{"class":45,"line":17666},206,[43,17668,17669],{"class":81},"      45",[43,17671,15399],{"class":67},[43,17673,1056],{"class":49},[43,17675,17677,17680,17682,17684],{"class":45,"line":17676},207,[43,17678,17679],{"class":81},"      transparent",[43,17681,12324],{"class":81},[43,17683,11598],{"class":67},[43,17685,1056],{"class":49},[43,17687,17689,17692,17694,17696,17698,17700,17702,17704,17706,17708,17710,17712,17714],{"class":45,"line":17688},208,[43,17690,17691],{"class":81},"      rgba",[43,17693,97],{"class":49},[43,17695,11568],{"class":81},[43,17697,104],{"class":49},[43,17699,4401],{"class":81},[43,17701,104],{"class":49},[43,17703,15640],{"class":81},[43,17705,104],{"class":49},[43,17707,15488],{"class":81},[43,17709,2408],{"class":49},[43,17711,12222],{"class":81},[43,17713,11598],{"class":67},[43,17715,1056],{"class":49},[43,17717,17719,17721,17724],{"class":45,"line":17718},209,[43,17720,17679],{"class":81},[43,17722,17723],{"class":81}," 55",[43,17725,17726],{"class":67},"%\n",[43,17728,17730],{"class":45,"line":17729},210,[43,17731,2718],{"class":49},[43,17733,17735,17738,17740,17742,17744,17746,17748],{"class":45,"line":17734},211,[43,17736,17737],{"class":81},"    background-size",[43,17739,78],{"class":49},[43,17741,10486],{"class":81},[43,17743,10489],{"class":67},[43,17745,10086],{"class":81},[43,17747,10489],{"class":67},[43,17749,2995],{"class":49},[43,17751,17753,17755,17757,17759,17761,17763,17765],{"class":45,"line":17752},212,[43,17754,17620],{"class":81},[43,17756,78],{"class":49},[43,17758,13039],{"class":81},[43,17760,10489],{"class":67},[43,17762,10065],{"class":81},[43,17764,10489],{"class":67},[43,17766,2995],{"class":49},[43,17768,17770],{"class":45,"line":17769},213,[43,17771,2028],{"class":49},[43,17773,17775,17777],{"class":45,"line":17774},214,[43,17776,17146],{"class":57},[43,17778,116],{"class":49},[43,17780,17782,17784,17786,17788],{"class":45,"line":17781},215,[43,17783,17608],{"class":81},[43,17785,78],{"class":49},[43,17787,16407],{"class":81},[43,17789,2995],{"class":49},[43,17791,17793,17795,17797,17799],{"class":45,"line":17792},216,[43,17794,17657],{"class":81},[43,17796,78],{"class":49},[43,17798,11750],{"class":81},[43,17800,2698],{"class":49},[43,17802,17804,17807,17809],{"class":45,"line":17803},217,[43,17805,17806],{"class":81},"      -45",[43,17808,15399],{"class":67},[43,17810,1056],{"class":49},[43,17812,17814,17816,17818,17820],{"class":45,"line":17813},218,[43,17815,17679],{"class":81},[43,17817,12324],{"class":81},[43,17819,11598],{"class":67},[43,17821,1056],{"class":49},[43,17823,17825,17827,17829,17831,17833,17835,17837,17839,17841,17843,17845,17847,17849],{"class":45,"line":17824},219,[43,17826,17691],{"class":81},[43,17828,97],{"class":49},[43,17830,16792],{"class":81},[43,17832,104],{"class":49},[43,17834,16792],{"class":81},[43,17836,104],{"class":49},[43,17838,16801],{"class":81},[43,17840,104],{"class":49},[43,17842,15488],{"class":81},[43,17844,2408],{"class":49},[43,17846,12222],{"class":81},[43,17848,11598],{"class":67},[43,17850,1056],{"class":49},[43,17852,17854,17856,17858],{"class":45,"line":17853},220,[43,17855,17679],{"class":81},[43,17857,17723],{"class":81},[43,17859,17726],{"class":67},[43,17861,17863],{"class":45,"line":17862},221,[43,17864,2718],{"class":49},[43,17866,17868,17870,17872,17874,17876,17878,17880],{"class":45,"line":17867},222,[43,17869,17737],{"class":81},[43,17871,78],{"class":49},[43,17873,12099],{"class":81},[43,17875,10489],{"class":67},[43,17877,12315],{"class":81},[43,17879,10489],{"class":67},[43,17881,2995],{"class":49},[43,17883,17885,17887,17889,17892,17894,17897,17899],{"class":45,"line":17884},223,[43,17886,17620],{"class":81},[43,17888,78],{"class":49},[43,17890,17891],{"class":81},"-10",[43,17893,10489],{"class":67},[43,17895,17896],{"class":81}," -10",[43,17898,10489],{"class":67},[43,17900,2995],{"class":49},[43,17902,17904],{"class":45,"line":17903},224,[43,17905,2028],{"class":49},[43,17907,17909,17911],{"class":45,"line":17908},225,[43,17910,17154],{"class":57},[43,17912,116],{"class":49},[43,17914,17916,17918,17920,17922],{"class":45,"line":17915},226,[43,17917,17608],{"class":81},[43,17919,78],{"class":49},[43,17921,12558],{"class":81},[43,17923,2995],{"class":49},[43,17925,17927,17930,17932,17934],{"class":45,"line":17926},227,[43,17928,17929],{"class":81},"    background",[43,17931,78],{"class":49},[43,17933,12507],{"class":81},[43,17935,2995],{"class":49},[43,17937,17939],{"class":45,"line":17938},228,[43,17940,2028],{"class":49},[43,17942,17944,17946],{"class":45,"line":17943},229,[43,17945,17162],{"class":57},[43,17947,116],{"class":49},[43,17949,17951,17953,17955,17957],{"class":45,"line":17950},230,[43,17952,17608],{"class":81},[43,17954,78],{"class":49},[43,17956,12747],{"class":81},[43,17958,2995],{"class":49},[43,17960,17962,17964,17966,17968],{"class":45,"line":17961},231,[43,17963,17657],{"class":81},[43,17965,78],{"class":49},[43,17967,11750],{"class":81},[43,17969,2698],{"class":49},[43,17971,17973,17976,17978],{"class":45,"line":17972},232,[43,17974,17975],{"class":81},"      90",[43,17977,15399],{"class":67},[43,17979,1056],{"class":49},[43,17981,17983,17985,17987,17989],{"class":45,"line":17982},233,[43,17984,17679],{"class":81},[43,17986,12324],{"class":81},[43,17988,11598],{"class":67},[43,17990,1056],{"class":49},[43,17992,17994,17996,17998,18000,18002,18004,18006,18008,18010,18013,18015,18017,18019],{"class":45,"line":17993},234,[43,17995,17691],{"class":81},[43,17997,97],{"class":49},[43,17999,11568],{"class":81},[43,18001,104],{"class":49},[43,18003,11568],{"class":81},[43,18005,104],{"class":49},[43,18007,11568],{"class":81},[43,18009,104],{"class":49},[43,18011,18012],{"class":81},"0.8",[43,18014,2408],{"class":49},[43,18016,12222],{"class":81},[43,18018,11598],{"class":67},[43,18020,1056],{"class":49},[43,18022,18024,18026,18028],{"class":45,"line":18023},235,[43,18025,17679],{"class":81},[43,18027,17723],{"class":81},[43,18029,17726],{"class":67},[43,18031,18033],{"class":45,"line":18032},236,[43,18034,2718],{"class":49},[43,18036,18038,18040,18042,18044,18046,18048,18050],{"class":45,"line":18037},237,[43,18039,17737],{"class":81},[43,18041,78],{"class":49},[43,18043,12033],{"class":81},[43,18045,10489],{"class":67},[43,18047,10492],{"class":81},[43,18049,10489],{"class":67},[43,18051,2995],{"class":49},[43,18053,18055,18057,18059,18061,18063],{"class":45,"line":18054},238,[43,18056,17620],{"class":81},[43,18058,78],{"class":49},[43,18060,4401],{"class":81},[43,18062,7645],{"class":81},[43,18064,2995],{"class":49},[43,18066,18068],{"class":45,"line":18067},239,[43,18069,2028],{"class":49},[43,18071,18073],{"class":45,"line":18072},240,[43,18074,2034],{"class":49},[43,18076,18078],{"class":45,"line":18077},241,[43,18079,1782],{"emptyLinePlaceholder":491},[43,18081,18083,18085,18088],{"class":45,"line":18082},242,[43,18084,16940],{"class":67},[43,18086,18087],{"class":74}," floatAround",[43,18089,116],{"class":49},[43,18091,18093,18095],{"class":45,"line":18092},243,[43,18094,16951],{"class":57},[43,18096,116],{"class":49},[43,18098,18100,18102,18104,18106,18108,18110,18112,18114,18116,18118,18120,18122,18124,18126,18128,18130,18132],{"class":45,"line":18099},244,[43,18101,16959],{"class":81},[43,18103,78],{"class":49},[43,18105,17118],{"class":81},[43,18107,97],{"class":49},[43,18109,4401],{"class":81},[43,18111,15399],{"class":67},[43,18113,2408],{"class":49},[43,18115,17047],{"class":81},[43,18117,97],{"class":49},[43,18119,11812],{"class":81},[43,18121,10489],{"class":67},[43,18123,2408],{"class":49},[43,18125,17118],{"class":81},[43,18127,97],{"class":49},[43,18129,4401],{"class":81},[43,18131,15399],{"class":67},[43,18133,1160],{"class":49},[43,18135,18137],{"class":45,"line":18136},245,[43,18138,2028],{"class":49},[43,18140,18142,18144],{"class":45,"line":18141},246,[43,18143,16980],{"class":57},[43,18145,116],{"class":49},[43,18147,18149,18151,18153,18155,18157,18160,18162,18164,18166,18168,18170,18172,18174,18176,18178,18181,18183],{"class":45,"line":18148},247,[43,18150,16959],{"class":81},[43,18152,78],{"class":49},[43,18154,17118],{"class":81},[43,18156,97],{"class":49},[43,18158,18159],{"class":81},"360",[43,18161,15399],{"class":67},[43,18163,2408],{"class":49},[43,18165,17047],{"class":81},[43,18167,97],{"class":49},[43,18169,11812],{"class":81},[43,18171,10489],{"class":67},[43,18173,2408],{"class":49},[43,18175,17118],{"class":81},[43,18177,97],{"class":49},[43,18179,18180],{"class":81},"-360",[43,18182,15399],{"class":67},[43,18184,1160],{"class":49},[43,18186,18188],{"class":45,"line":18187},248,[43,18189,2028],{"class":49},[43,18191,18193],{"class":45,"line":18192},249,[43,18194,2034],{"class":49},[471,18196,18197],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":18199},[18200],{"id":15030,"depth":64,"text":15031},"미치광이 카드 컴포넌트는 어떤 느낌일까",{"date":18203,"field":5954,"tags":18204},"2025.03.28",[11011,10462],"\u002Fko\u002Fhtml2",{"title":15025,"description":18201},{"loc":18205},"ko\u002Fhtml2","pmxYdGXOCVBfLnb4aXGwgwPtTiiYFr7EFOGK3f7A5OA",{"id":18211,"title":18212,"body":18213,"description":18720,"extension":484,"meta":18721,"navigation":491,"path":18724,"seo":18725,"sitemap":18726,"stem":18727,"__hash__":18728},"ko\u002Fko\u002Fdebounce-guide.md","debounce와 throttle이란??",{"type":8,"value":18214,"toc":18706},[18215,18219,18226,18235,18247,18254,18256,18260,18265,18279,18377,18381,18397,18401,18412,18414,18418,18429,18567,18571,18582,18586,18597,18599,18603,18649,18651,18655,18666,18683,18686,18689,18703],[29,18216,18218],{"id":18217},"step-1-debounce와-throttle의-목적-이해하기","Step 1: debounce와 throttle의 목적 이해하기",[15,18220,18221,18222,18225],{},"프론트엔드 개발에서는 스크롤, 입력, 리사이즈 같은 이벤트가 짧은 시간 동안 반복적으로 발생하는 일이 많다. 이런 이벤트에 매번 함수를 실행하면 ",[21,18223,18224],{},"불필요한 연산으로 성능 저하","가 발생할 수 있다.",[15,18227,18228,18229,1318,18232,8886],{},"이를 해결하는 대표적인 기법이 바로 ",[40,18230,18231],{},"debounce",[40,18233,18234],{},"throttle",[185,18236,18237,18242],{},[188,18238,18239,18241],{},[21,18240,18231],{},": 마지막 이벤트가 발생한 후 일정 시간이 지나야 함수를 실행",[188,18243,18244,18246],{},[21,18245,18234],{},": 일정 시간 간격마다 함수가 한 번만 실행되도록 제한",[15,18248,18249,18250,18253],{},"둘 다 ",[21,18251,18252],{},"함수 호출 빈도를 제어","하는 기술이며, 사용 목적과 작동 방식에 차이가 있다.",[26,18255],{},[29,18257,18259],{"id":18258},"step-2-debounce-함수의-기본-구조-파악하기","Step 2: debounce 함수의 기본 구조 파악하기",[15,18261,18262,18264],{},[40,18263,18231],{}," 함수는 두 가지 인자를 받는다.",[185,18266,18267,18273],{},[188,18268,18269,18272],{},[40,18270,18271],{},"func",": 실행할 함수",[188,18274,18275,18278],{},[40,18276,18277],{},"delay",": 함수 실행을 지연시킬 시간 (밀리초 단위)",[33,18280,18282],{"className":1024,"code":18281,"language":1026,"meta":38,"style":38},"\u002F\u002F debounce 구현 예시 (화살표 함수 버전)\nconst debounce = (func, delay) => {\n    let timer;\n    return (...args) => {\n        clearTimeout(timer);\n        timer = setTimeout(() => func(...args), delay);\n    };\n};\n",[40,18283,18284,18289,18312,18320,18337,18345,18369,18373],{"__ignoreMap":38},[43,18285,18286],{"class":45,"line":46},[43,18287,18288],{"class":2797},"\u002F\u002F debounce 구현 예시 (화살표 함수 버전)\n",[43,18290,18291,18293,18296,18298,18300,18302,18304,18306,18308,18310],{"class":45,"line":64},[43,18292,68],{"class":67},[43,18294,18295],{"class":57}," debounce",[43,18297,128],{"class":67},[43,18299,1069],{"class":49},[43,18301,18271],{"class":74},[43,18303,104],{"class":49},[43,18305,18277],{"class":74},[43,18307,2408],{"class":49},[43,18309,113],{"class":67},[43,18311,116],{"class":49},[43,18313,18314,18317],{"class":45,"line":119},[43,18315,18316],{"class":67},"    let",[43,18318,18319],{"class":49}," timer;\n",[43,18321,18322,18324,18326,18328,18331,18333,18335],{"class":45,"line":150},[43,18323,2737],{"class":67},[43,18325,1069],{"class":49},[43,18327,1172],{"class":67},[43,18329,18330],{"class":74},"args",[43,18332,2408],{"class":49},[43,18334,113],{"class":67},[43,18336,116],{"class":49},[43,18338,18339,18342],{"class":45,"line":159},[43,18340,18341],{"class":57},"        clearTimeout",[43,18343,18344],{"class":49},"(timer);\n",[43,18346,18347,18350,18352,18355,18357,18359,18362,18364,18366],{"class":45,"line":176},[43,18348,18349],{"class":49},"        timer ",[43,18351,88],{"class":67},[43,18353,18354],{"class":57}," setTimeout",[43,18356,7872],{"class":49},[43,18358,113],{"class":67},[43,18360,18361],{"class":57}," func",[43,18363,97],{"class":49},[43,18365,1172],{"class":67},[43,18367,18368],{"class":49},"args), delay);\n",[43,18370,18371],{"class":45,"line":1163},[43,18372,2118],{"class":49},[43,18374,18375],{"class":45,"line":1178},[43,18376,9424],{"class":49},[1273,18378,18380],{"id":18379},"debounce-작동-방식-요약","✅ debounce 작동 방식 요약",[185,18382,18383,18386,18394],{},[188,18384,18385],{},"이벤트가 반복되면 이전 타이머를 초기화한다",[188,18387,18388],{},[21,18389,18390,18391,18393],{},"마지막 이벤트 이후 일정 시간(",[40,18392,18277],{},")이 지나야 실행된다",[188,18395,18396],{},"이벤트가 계속 발생하면 함수는 실행되지 않는다",[1273,18398,18400],{"id":18399},"debounce-사용-예시","💡 debounce 사용 예시",[185,18402,18403,18406,18409],{},[188,18404,18405],{},"검색어 자동완성",[188,18407,18408],{},"윈도우 크기 조절 이벤트 처리",[188,18410,18411],{},"폼 입력 유효성 검사",[26,18413],{},[29,18415,18417],{"id":18416},"step-3-throttle-함수의-개념과-구조-이해하기","Step 3: throttle 함수의 개념과 구조 이해하기",[15,18419,18420,18422,18423,18425,18426,18428],{},[40,18421,18234],{}," 함수도 ",[40,18424,18271],{},"과 ",[40,18427,18277],{}," 두 인자를 받고, 다음과 같이 구현할 수 있다.",[33,18430,18432],{"className":1024,"code":18431,"language":1026,"meta":38,"style":38},"\u002F\u002F throttle 구현 예시\nconst throttle = (func, delay) => {\n    let lastCall = 0;\n    return (...args) => {\n        const now = new Date().getTime();\n        if (now - lastCall >= delay) {\n            lastCall = now;\n            func(...args);\n        }\n    };\n};\n",[40,18433,18434,18439,18462,18475,18491,18513,18532,18542,18554,18559,18563],{"__ignoreMap":38},[43,18435,18436],{"class":45,"line":46},[43,18437,18438],{"class":2797},"\u002F\u002F throttle 구현 예시\n",[43,18440,18441,18443,18446,18448,18450,18452,18454,18456,18458,18460],{"class":45,"line":64},[43,18442,68],{"class":67},[43,18444,18445],{"class":57}," throttle",[43,18447,128],{"class":67},[43,18449,1069],{"class":49},[43,18451,18271],{"class":74},[43,18453,104],{"class":49},[43,18455,18277],{"class":74},[43,18457,2408],{"class":49},[43,18459,113],{"class":67},[43,18461,116],{"class":49},[43,18463,18464,18466,18469,18471,18473],{"class":45,"line":119},[43,18465,18316],{"class":67},[43,18467,18468],{"class":49}," lastCall ",[43,18470,88],{"class":67},[43,18472,7645],{"class":81},[43,18474,2995],{"class":49},[43,18476,18477,18479,18481,18483,18485,18487,18489],{"class":45,"line":150},[43,18478,2737],{"class":67},[43,18480,1069],{"class":49},[43,18482,1172],{"class":67},[43,18484,18330],{"class":74},[43,18486,2408],{"class":49},[43,18488,113],{"class":67},[43,18490,116],{"class":49},[43,18492,18493,18495,18498,18500,18502,18505,18508,18511],{"class":45,"line":159},[43,18494,9277],{"class":67},[43,18496,18497],{"class":81}," now",[43,18499,128],{"class":67},[43,18501,1794],{"class":67},[43,18503,18504],{"class":57}," Date",[43,18506,18507],{"class":49},"().",[43,18509,18510],{"class":57},"getTime",[43,18512,147],{"class":49},[43,18514,18515,18518,18521,18524,18526,18529],{"class":45,"line":176},[43,18516,18517],{"class":67},"        if",[43,18519,18520],{"class":49}," (now ",[43,18522,18523],{"class":67},"-",[43,18525,18468],{"class":49},[43,18527,18528],{"class":67},">=",[43,18530,18531],{"class":49}," delay) {\n",[43,18533,18534,18537,18539],{"class":45,"line":1163},[43,18535,18536],{"class":49},"            lastCall ",[43,18538,88],{"class":67},[43,18540,18541],{"class":49}," now;\n",[43,18543,18544,18547,18549,18551],{"class":45,"line":1178},[43,18545,18546],{"class":57},"            func",[43,18548,97],{"class":49},[43,18550,1172],{"class":67},[43,18552,18553],{"class":49},"args);\n",[43,18555,18556],{"class":45,"line":1184},[43,18557,18558],{"class":49},"        }\n",[43,18560,18561],{"class":45,"line":1190},[43,18562,2118],{"class":49},[43,18564,18565],{"class":45,"line":1996},[43,18566,9424],{"class":49},[1273,18568,18570],{"id":18569},"throttle-작동-방식-요약","✅ throttle 작동 방식 요약",[185,18572,18573,18579],{},[188,18574,18575,18576],{},"이벤트가 아무리 자주 발생해도 ",[21,18577,18578],{},"정해진 주기마다 한 번만 실행된다",[188,18580,18581],{},"타이머를 사용하지 않고, 마지막 실행 시점을 기준으로 시간 차를 계산한다",[1273,18583,18585],{"id":18584},"throttle-사용-예시","💡 throttle 사용 예시",[185,18587,18588,18591,18594],{},[188,18589,18590],{},"무한 스크롤 이벤트",[188,18592,18593],{},"스크롤 위치 저장",[188,18595,18596],{},"버튼 클릭 제한 (예: 연타 방지)",[26,18598],{},[29,18600,18602],{"id":18601},"step-4-debounce와-throttle-비교-정리","Step 4: debounce와 throttle 비교 정리",[678,18604,18605,18615],{},[681,18606,18607],{},[684,18608,18609,18611,18613],{},[687,18610,8707],{},[687,18612,18231],{},[687,18614,18234],{},[703,18616,18617,18628,18638],{},[684,18618,18619,18622,18625],{},[708,18620,18621],{},"실행 조건",[708,18623,18624],{},"마지막 이벤트 이후 일정 시간 무동작 시 실행",[708,18626,18627],{},"일정 시간 간격마다 실행",[684,18629,18630,18632,18635],{},[708,18631,6566],{},[708,18633,18634],{},"입력 감지 후 서버 요청, resize 이벤트 등",[708,18636,18637],{},"스크롤, 클릭 등 고빈도 이벤트 주기 제한",[684,18639,18640,18643,18646],{},[708,18641,18642],{},"특징",[708,18644,18645],{},"마지막 이벤트만 반영됨",[708,18647,18648],{},"연속 중 일부 이벤트만 반영됨",[26,18650],{},[29,18652,18654],{"id":18653},"step-5-마무리","Step 5: 마무리",[15,18656,18657,1318,18659,18661,18662,18665],{},[40,18658,18231],{},[40,18660,18234],{},"은 모두 반복되는 함수 호출을 제어해서 ",[21,18663,18664],{},"성능을 최적화하고, 불필요한 실행을 방지","하는 데 유용한 기법이다.",[185,18667,18668,18676],{},[188,18669,18670,18673,18674],{},[21,18671,18672],{},"사용자의 행동이 멈춘 뒤"," 작업을 처리하고 싶다면 → ",[40,18675,18231],{},[188,18677,18678,18673,18681],{},[21,18679,18680],{},"정해진 주기마다",[40,18682,18234],{},[15,18684,18685],{},"상황에 맞는 기술을 골라서 쓰면 훨씬 효율적인 웹 애플리케이션을 만들 수 있다.",[29,18687,18688],{"id":18688},"답해보기",[185,18690,18691,18694,18697,18700],{},[188,18692,18693],{},"throttle은 무엇인가요?",[188,18695,18696],{},"debounce는 무엇인가요?",[188,18698,18699],{},"각 함수 코드를 작성해보세요",[188,18701,18702],{},"각 함수는 어떤 상황에서 사용되나요?",[471,18704,18705],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":38,"searchDepth":64,"depth":64,"links":18707},[18708,18709,18713,18717,18718,18719],{"id":18217,"depth":64,"text":18218},{"id":18258,"depth":64,"text":18259,"children":18710},[18711,18712],{"id":18379,"depth":119,"text":18380},{"id":18399,"depth":119,"text":18400},{"id":18416,"depth":64,"text":18417,"children":18714},[18715,18716],{"id":18569,"depth":119,"text":18570},{"id":18584,"depth":119,"text":18585},{"id":18601,"depth":64,"text":18602},{"id":18653,"depth":64,"text":18654},{"id":18688,"depth":64,"text":18688},"반복되는 함수 호출을 제어하는 기술인 디바운스(debounce)와 쓰로틀(throttle)을 알아보자.",{"date":18722,"field":5954,"tags":18723},"2025.03.26",[1756],"\u002Fko\u002Fdebounce-guide",{"title":18212,"description":18720},{"loc":18724},"ko\u002Fdebounce-guide","cUfijXFkvCfSAMD1_uzAkbdi5BSQ8o6QHLEFY4NYAcc",{"id":18730,"title":18731,"body":18732,"description":18731,"extension":484,"meta":19034,"navigation":491,"path":19038,"seo":19039,"sitemap":19040,"stem":19041,"__hash__":19042},"ko\u002Fko\u002Fhttps-guide.md","Let's Encrypt를 이용해서 HTTPS 적용하기",{"type":8,"value":18733,"toc":19021},[18734,18738,18745,18749,18756,18760,18763,18766,18770,18777,18806,18810,18813,18837,18840,18844,18847,18927,18930,18958,18961,18965,18971,18975,18978,18995,18998,19001,19018],[29,18735,18737],{"id":18736},"https를-적용하는-이유-왜-필요한가","HTTPS를 적용하는 이유? 왜 필요한가",[15,18739,18740,18741,18744],{},"웹사이트를 운영하다 보면 보안이 점점 더 중요해진다. ",[40,18742,18743],{},"HTTPS","는 데이터를 암호화해서 주고받는 방식이라 사용자의 개인정보를 보호하고, 신뢰성을 높여준다. 브라우저에서도 HTTP 사이트에 접속하면 \"안전하지 않음\" 경고를 띄울 정도다. 검색 엔진 순위에도 영향을 주니, 이제는 선택이 아니라 필수다.",[29,18746,18748],{"id":18747},"lets-encrypt란-왜-무료인가","Let's Encrypt란? 왜 무료인가?",[15,18750,18751,18752,18755],{},"유료로 발급해주는 기관이 많은데, 왜 무료일까? Let's Encrypt는 무료로 ",[40,18753,18754],{},"SSL\u002FTLS"," 인증서를 발급해주는 서비스다. 인터넷 보안을 널리 퍼트리자는 목표로 시작된 비영리 프로젝트라 비용이 들지 않는다. 누구나 쉽게 인증서를 발급받아 웹사이트를 보호할 수 있으며, 유료 인증서와 성능 차이도 거의 없어서 개인 프로젝트나 소규모 사이트에 적합하다.",[29,18757,18759],{"id":18758},"lets-encrypt를-이용해서-vm에-올린-웹을-보호해보자","Let's Encrypt를 이용해서 VM에 올린 웹을 보호해보자",[15,18761,18762],{},"VM에 웹 서버를 띄웠다면, Let's Encrypt로 HTTPS를 적용해보자. 설치부터 설정까지 간단하며, 자동화 도구도 제공된다. 절차를 하나씩 따라가 보자.",[29,18764,18765],{"id":18765},"절차",[1273,18767,18769],{"id":18768},"_1-certbot-설치","1. Certbot 설치",[15,18771,18772,18773,18776],{},"VM에 ",[40,18774,18775],{},"certbot","을 설치한다. (우분투 환경 기준)",[33,18778,18780],{"className":850,"code":18779,"language":852,"meta":38,"style":38},"sudo apt update && sudo apt install certbot python3-certbot-nginx\n",[40,18781,18782],{"__ignoreMap":38},[43,18783,18784,18786,18788,18791,18794,18796,18798,18800,18803],{"class":45,"line":46},[43,18785,3976],{"class":57},[43,18787,4157],{"class":100},[43,18789,18790],{"class":100}," update",[43,18792,18793],{"class":49}," && ",[43,18795,3976],{"class":57},[43,18797,4157],{"class":100},[43,18799,1744],{"class":100},[43,18801,18802],{"class":100}," certbot",[43,18804,18805],{"class":100}," python3-certbot-nginx\n",[1273,18807,18809],{"id":18808},"_2-인증서-발급","2. 인증서 발급",[15,18811,18812],{},"도메인이 연결된 상태에서 아래 명령어를 실행해 인증서를 발급받는다.",[33,18814,18816],{"className":850,"code":18815,"language":852,"meta":38,"style":38},"sudo certbot --nginx -d your_domain.com -d www.your_domain.com\n",[40,18817,18818],{"__ignoreMap":38},[43,18819,18820,18822,18824,18827,18829,18832,18834],{"class":45,"line":46},[43,18821,3976],{"class":57},[43,18823,18802],{"class":100},[43,18825,18826],{"class":81}," --nginx",[43,18828,1563],{"class":81},[43,18830,18831],{"class":100}," your_domain.com",[43,18833,1563],{"class":81},[43,18835,18836],{"class":100}," www.your_domain.com\n",[15,18838,18839],{},"이 과정에서 Let's Encrypt가 도메인 소유권을 확인하며, 성공적으로 인증서가 발급되면 자동으로 nginx 설정이 적용된다.",[1273,18841,18843],{"id":18842},"_3-웹-서버-설정-확인-nginx-기준","3. 웹 서버 설정 확인 (nginx 기준)",[15,18845,18846],{},"인증서 발급 후, nginx 설정이 올바른지 확인한다. 기본적인 설정 예시는 다음과 같다.",[33,18848,18852],{"className":18849,"code":18850,"language":18851,"meta":38,"style":38},"language-nginx shiki shiki-themes github-light github-dark","server {\n    listen 80;\n    server_name your_domain.com www.your_domain.com;\n    return 301 https:\u002F\u002F$host$request_uri;\n}\n\nserver {\n    listen 443 ssl;\n    server_name your_domain.com www.your_domain.com;\n\n    ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Fyour_domain.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fyour_domain.com\u002Fprivkey.pem;\n\n    root \u002Fvar\u002Fwww\u002Fhtml;\n    index index.html;\n}\n","nginx",[40,18853,18854,18859,18864,18869,18874,18878,18882,18886,18891,18895,18899,18904,18909,18913,18918,18923],{"__ignoreMap":38},[43,18855,18856],{"class":45,"line":46},[43,18857,18858],{},"server {\n",[43,18860,18861],{"class":45,"line":64},[43,18862,18863],{},"    listen 80;\n",[43,18865,18866],{"class":45,"line":119},[43,18867,18868],{},"    server_name your_domain.com www.your_domain.com;\n",[43,18870,18871],{"class":45,"line":150},[43,18872,18873],{},"    return 301 https:\u002F\u002F$host$request_uri;\n",[43,18875,18876],{"class":45,"line":159},[43,18877,2034],{},[43,18879,18880],{"class":45,"line":176},[43,18881,1782],{"emptyLinePlaceholder":491},[43,18883,18884],{"class":45,"line":1163},[43,18885,18858],{},[43,18887,18888],{"class":45,"line":1178},[43,18889,18890],{},"    listen 443 ssl;\n",[43,18892,18893],{"class":45,"line":1184},[43,18894,18868],{},[43,18896,18897],{"class":45,"line":1190},[43,18898,1782],{"emptyLinePlaceholder":491},[43,18900,18901],{"class":45,"line":1996},[43,18902,18903],{},"    ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Fyour_domain.com\u002Ffullchain.pem;\n",[43,18905,18906],{"class":45,"line":2008},[43,18907,18908],{},"    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fyour_domain.com\u002Fprivkey.pem;\n",[43,18910,18911],{"class":45,"line":2025},[43,18912,1782],{"emptyLinePlaceholder":491},[43,18914,18915],{"class":45,"line":2031},[43,18916,18917],{},"    root \u002Fvar\u002Fwww\u002Fhtml;\n",[43,18919,18920],{"class":45,"line":2037},[43,18921,18922],{},"    index index.html;\n",[43,18924,18925],{"class":45,"line":2042},[43,18926,2034],{},[15,18928,18929],{},"설정이 끝났다면 다음 명령어로 오류 여부를 확인하고 적용한다.",[33,18931,18933],{"className":850,"code":18932,"language":852,"meta":38,"style":38},"sudo nginx -t\nsudo systemctl reload nginx\n",[40,18934,18935,18945],{"__ignoreMap":38},[43,18936,18937,18939,18942],{"class":45,"line":46},[43,18938,3976],{"class":57},[43,18940,18941],{"class":100}," nginx",[43,18943,18944],{"class":81}," -t\n",[43,18946,18947,18949,18952,18955],{"class":45,"line":64},[43,18948,3976],{"class":57},[43,18950,18951],{"class":100}," systemctl",[43,18953,18954],{"class":100}," reload",[43,18956,18957],{"class":100}," nginx\n",[15,18959,18960],{},"443 포트가 열리고, 인증서 경로도 자동으로 설정된다. 필요하면 다시 점검하고 재시작하자.",[1273,18962,18964],{"id":18963},"_4-브라우저에서-확인","4. 브라우저에서 확인",[15,18966,1665,18967,18970],{},[40,18968,18969],{},"https:\u002F\u002Fyour_domain.com","으로 접속하여 자물쇠 아이콘이 표시되는지 확인한다. 보인다면 성공이다!",[29,18972,18974],{"id":18973},"갱신하는-방법은","갱신하는 방법은?",[15,18976,18977],{},"Let's Encrypt 인증서는 90일마다 갱신해야 한다. 자동 갱신을 설정하려면 아래 명령어를 크론탭에 추가하면 된다.",[33,18979,18981],{"className":850,"code":18980,"language":852,"meta":38,"style":38},"sudo certbot renew --dry-run\n",[40,18982,18983],{"__ignoreMap":38},[43,18984,18985,18987,18989,18992],{"class":45,"line":46},[43,18986,3976],{"class":57},[43,18988,18802],{"class":100},[43,18990,18991],{"class":100}," renew",[43,18993,18994],{"class":81}," --dry-run\n",[15,18996,18997],{},"이 명령어를 실행해 테스트하고, 문제없이 동작한다면 자동 갱신이 설정된 것이다.",[29,18999,19000],{"id":19000},"주의점",[185,19002,19003,19008,19013],{},[188,19004,19005],{},[21,19006,19007],{},"인증서 발급 시 www.your_domain.com과 your_domain.com 모두 포함할 것!",[188,19009,19010],{},[21,19011,19012],{},"443 포트가 열려 있어야 한다.",[188,19014,19015],{},[21,19016,19017],{},"갱신이 자동으로 되지 않는 경우 certbot을 수동으로 실행해 확인한다.",[471,19019,19020],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":38,"searchDepth":64,"depth":64,"links":19022},[19023,19024,19025,19026,19032,19033],{"id":18736,"depth":64,"text":18737},{"id":18747,"depth":64,"text":18748},{"id":18758,"depth":64,"text":18759},{"id":18765,"depth":64,"text":18765,"children":19027},[19028,19029,19030,19031],{"id":18768,"depth":119,"text":18769},{"id":18808,"depth":119,"text":18809},{"id":18842,"depth":119,"text":18843},{"id":18963,"depth":119,"text":18964},{"id":18973,"depth":64,"text":18974},{"id":19000,"depth":64,"text":19000},{"date":19035,"field":4436,"tags":19036},"2025.03.11",[19037],"https","\u002Fko\u002Fhttps-guide",{"title":18731,"description":18731},{"loc":19038},"ko\u002Fhttps-guide","aDc5sQOVPAm6RyMPkGywbBl8VjvIhFxUeDUb0msqBko",{"id":19044,"title":19045,"body":19046,"description":20009,"extension":484,"meta":20010,"navigation":491,"path":20013,"seo":20014,"sitemap":20015,"stem":20016,"__hash__":20017},"ko\u002Fko\u002Fdocker-auto-build-run.md","로컬에서 커밋 시 도커 빌드 후 컨테이너 시작하기",{"type":8,"value":19047,"toc":20004},[19048,19052,19056,19067,19071,19074,19217,19220,19236,19239,19247,19266,19269,19285,19289,19292,19295,19298,19311,19314,19320,19323,19359,19362,19368,19371,19374,19376,19379,20001],[11,19049,19051],{"id":19050},"로컬에서-커밋-시-훅을-이용해여-도커-빌드-후-컨테이너-시작하기","로컬에서 커밋 시 훅을 이용해여 도커 빌드 후 컨테이너 시작하기",[29,19053,19055],{"id":19054},"기능-목표","기능 목표",[185,19057,19058,19061,19064],{},[188,19059,19060],{},"step1: 커밋 시 로컬 프로젝트를 빌드",[188,19062,19063],{},"step2: 도커가 빌드된 파일 기준으로 이미지 빌드",[188,19065,19066],{},"step3: 도커가 빌드된 파일 실행",[29,19068,19070],{"id":19069},"sh-파일을-이용해서-자동화-스크립트-작성","sh 파일을 이용해서 자동화 스크립트 작성",[15,19072,19073],{},"path: \u002Fpath\u002Fto\u002Fproject\u002Fbuild-and-run.sh",[33,19075,19077],{"className":850,"code":19076,"language":852,"meta":38,"style":38},"#!\u002Fbin\u002Fbash\n\n# 변수 설정\nIMAGE_NAME=frontend:latest\nPORT=8080\n\n# 빌드 및 도커 실행\nnpm run build\ndocker build -t $IMAGE_NAME .\ndocker stop frontend-test || true\ndocker rm frontend-test || true\ndocker run -d --name frontend-test -p $PORT:80 $IMAGE_NAME\n\necho \"로컬 테스트: http:\u002F\u002Flocalhost:$PORT\"\n",[40,19078,19079,19084,19088,19093,19103,19113,19117,19122,19131,19147,19162,19175,19200,19204],{"__ignoreMap":38},[43,19080,19081],{"class":45,"line":46},[43,19082,19083],{"class":2797},"#!\u002Fbin\u002Fbash\n",[43,19085,19086],{"class":45,"line":64},[43,19087,1782],{"emptyLinePlaceholder":491},[43,19089,19090],{"class":45,"line":119},[43,19091,19092],{"class":2797},"# 변수 설정\n",[43,19094,19095,19098,19100],{"class":45,"line":150},[43,19096,19097],{"class":49},"IMAGE_NAME",[43,19099,88],{"class":67},[43,19101,19102],{"class":100},"frontend:latest\n",[43,19104,19105,19108,19110],{"class":45,"line":159},[43,19106,19107],{"class":49},"PORT",[43,19109,88],{"class":67},[43,19111,19112],{"class":100},"8080\n",[43,19114,19115],{"class":45,"line":176},[43,19116,1782],{"emptyLinePlaceholder":491},[43,19118,19119],{"class":45,"line":1163},[43,19120,19121],{"class":2797},"# 빌드 및 도커 실행\n",[43,19123,19124,19126,19128],{"class":45,"line":1178},[43,19125,1741],{"class":57},[43,19127,1560],{"class":100},[43,19129,19130],{"class":100}," build\n",[43,19132,19133,19135,19138,19141,19144],{"class":45,"line":1184},[43,19134,1557],{"class":57},[43,19136,19137],{"class":100}," build",[43,19139,19140],{"class":81}," -t",[43,19142,19143],{"class":49}," $IMAGE_NAME ",[43,19145,19146],{"class":100},".\n",[43,19148,19149,19151,19154,19157,19159],{"class":45,"line":1190},[43,19150,1557],{"class":57},[43,19152,19153],{"class":100}," stop",[43,19155,19156],{"class":100}," frontend-test",[43,19158,6817],{"class":67},[43,19160,19161],{"class":81}," true\n",[43,19163,19164,19166,19169,19171,19173],{"class":45,"line":1996},[43,19165,1557],{"class":57},[43,19167,19168],{"class":100}," rm",[43,19170,19156],{"class":100},[43,19172,6817],{"class":67},[43,19174,19161],{"class":81},[43,19176,19177,19179,19181,19183,19186,19188,19191,19194,19197],{"class":45,"line":2008},[43,19178,1557],{"class":57},[43,19180,1560],{"class":100},[43,19182,1563],{"class":81},[43,19184,19185],{"class":81}," --name",[43,19187,19156],{"class":100},[43,19189,19190],{"class":81}," -p",[43,19192,19193],{"class":49}," $PORT",[43,19195,19196],{"class":100},":80",[43,19198,19199],{"class":49}," $IMAGE_NAME\n",[43,19201,19202],{"class":45,"line":2025},[43,19203,1782],{"emptyLinePlaceholder":491},[43,19205,19206,19209,19212,19215],{"class":45,"line":2031},[43,19207,19208],{"class":81},"echo",[43,19210,19211],{"class":100}," \"로컬 테스트: http:\u002F\u002Flocalhost:",[43,19213,19214],{"class":49},"$PORT",[43,19216,4574],{"class":100},[15,19218,19219],{},"위와 같이 sh 스크립트를 작성 후 실행권한 추가",[33,19221,19223],{"className":850,"code":19222,"language":852,"meta":38,"style":38},"chmod   +x   build-and-run.sh\n",[40,19224,19225],{"__ignoreMap":38},[43,19226,19227,19230,19233],{"class":45,"line":46},[43,19228,19229],{"class":57},"chmod",[43,19231,19232],{"class":100},"   +x",[43,19234,19235],{"class":100},"   build-and-run.sh\n",[15,19237,19238],{},"실행 권한이 필요한 이유?",[185,19240,19241,19244],{},[188,19242,19243],{},"기본적으로 새로 생성된 스크립트 파일은 실행 불가 상태",[188,19245,19246],{},"권한 부여 없이 실행하려고 하면 다음과 같은 에러 발생:",[33,19248,19250],{"className":850,"code":19249,"language":852,"meta":38,"style":38},"-bash: .\u002Fbuild-and-run.sh: Permission denied\n",[40,19251,19252],{"__ignoreMap":38},[43,19253,19254,19257,19260,19263],{"class":45,"line":46},[43,19255,19256],{"class":57},"-bash:",[43,19258,19259],{"class":100}," .\u002Fbuild-and-run.sh:",[43,19261,19262],{"class":100}," Permission",[43,19264,19265],{"class":100}," denied\n",[15,19267,19268],{},"실행 권한 확인 방법",[33,19270,19272],{"className":850,"code":19271,"language":852,"meta":38,"style":38},"ls -l build-and-run.sh\n",[40,19273,19274],{"__ignoreMap":38},[43,19275,19276,19279,19282],{"class":45,"line":46},[43,19277,19278],{"class":57},"ls",[43,19280,19281],{"class":81}," -l",[43,19283,19284],{"class":100}," build-and-run.sh\n",[29,19286,19288],{"id":19287},"스크립트-실행을-위해서-깃훅으로-연동하기","스크립트 실행을 위해서 깃훅으로 연동하기",[15,19290,19291],{},"path: \u002Fpath\u002Fto\u002Fproject\u002F.git\u002Fhooks",[15,19293,19294],{},"hooks 폴더안에는 여러가지 상황에 맞는 파일들이 설정되어 있습니다",[15,19296,19297],{},"지금은 커밋 후 자동으로 실행 되길 원하기 떄문에 post-commit 진입 후\n스크립트 추가",[33,19299,19301],{"className":850,"code":19300,"language":852,"meta":38,"style":38},"nano \u002Fpath\u002Fto\u002Fproject\u002F.git\u002Fhooks\u002Fpost-commit\n",[40,19302,19303],{"__ignoreMap":38},[43,19304,19305,19308],{"class":45,"line":46},[43,19306,19307],{"class":57},"nano",[43,19309,19310],{"class":100}," \u002Fpath\u002Fto\u002Fproject\u002F.git\u002Fhooks\u002Fpost-commit\n",[15,19312,19313],{},"post-commit",[33,19315,19318],{"className":19316,"code":19317,"language":539},[537],"\u002Fpath\u002Fto\u002Fproject\u002Fbuild-and-run.sh\n",[40,19319,19317],{"__ignoreMap":38},[15,19321,19322],{},"후에 커밋을 날려봤지만 실행되질 않음",[33,19324,19326],{"className":850,"code":19325,"language":852,"meta":38,"style":38},"hint: The '.git\u002Fhooks\u002Fpost-commit' hook was ignored because it's not set as executable.\nhint: You can disable this warning with `git config advice.ignoredHook false`.\n",[40,19327,19328,19354],{"__ignoreMap":38},[43,19329,19330,19333,19336,19339,19342,19345,19348,19351],{"class":45,"line":46},[43,19331,19332],{"class":57},"hint:",[43,19334,19335],{"class":100}," The",[43,19337,19338],{"class":100}," '.git\u002Fhooks\u002Fpost-commit'",[43,19340,19341],{"class":100}," hook",[43,19343,19344],{"class":100}," was",[43,19346,19347],{"class":100}," ignored",[43,19349,19350],{"class":100}," because",[43,19352,19353],{"class":100}," it's not set as executable.\n",[43,19355,19356],{"class":45,"line":64},[43,19357,19358],{"class":100},"hint: You can disable this warning with `git config advice.ignoredHook false`.\n",[15,19360,19361],{},"권한을 깜빡했구만",[33,19363,19366],{"className":19364,"code":19365,"language":539},[537],"chmod +x post-commit\n",[40,19367,19365],{"__ignoreMap":38},[15,19369,19370],{},"추가 후 다시 시도하면",[15,19372,19373],{},"한번에 실행되는 것을 확인할 수 있어요",[26,19375],{},[15,19377,19378],{},"최근 GCP의 VM에 배포하던 도중 배포 노가다 하는 모습의 나를 보면서 자동화를 시도했지만\n남의 코드를 복붙만 하는 거 같아서 단계별로 하나 하나 퀘스트를 깨며 학습할 예정입니다...",[33,19380,19384],{"className":19381,"code":19382,"language":19383,"meta":38,"style":38},"language-sh shiki shiki-themes github-light github-dark","> my-app@0.0.0 build\n> vite build\n\n^R\ngivite v6.2.1 building for production...\ntransforming (1) src\u002Fmain.tsx^R\n✓ 2288 modules transformed.\ndist\u002Findex.html                   0.46 kB │ gzip:   0.30 kB\ndist\u002Fassets\u002Findex-vuH6rREl.css   49.00 kB │ gzip:   5.82 kB\ndist\u002Fassets\u002Findex-DWljNZS7.js   734.03 kB │ gzip: 216.05 kB\n\n(!) Some chunks are larger than 500 kB after minification. Consider:\n- Using dynamic import() to code-split the application\n- Use build.rollupOptions.output.manualChunks to improve chunking: https:\u002F\u002Frollupjs.org\u002Fconfiguration-options\u002F#output-manualchunks\n- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.\n✓ built in 3.12s\n[+] Building 2.0s (9\u002F9) FINISHED                                                                                            docker:desktop-linux\n => [internal] load build definition from Dockerfile                                                                                        0.0s\n => => transferring dockerfile: 356B                                                                                                        0.0s\n => [internal] load metadata for docker.io\u002Flibrary\u002Fnginx:1.25.4-alpine                                                                      1.8s\n => [auth] library\u002Fnginx:pull token for registry-1.docker.io                                                                                0.0s\n => [internal] load .dockerignore                                                                                                           0.0s\n => => transferring context: 2B                                                                                                             0.0s\n => [1\u002F3] FROM docker.io\u002Flibrary\u002Fnginx:1.25.4-alpine@sha256:31bad00311cb5eeb8a6648beadcf67277a175da89989f14727420a80e2e76742                0.0s\n => => resolve docker.io\u002Flibrary\u002Fnginx:1.25.4-alpine@sha256:31bad00311cb5eeb8a6648beadcf67277a175da89989f14727420a80e2e76742                0.0s\n => [internal] load build context                                                                                                           0.0s\n => => transferring context: 785.80kB                                                                                                       0.0s\n => CACHED [2\u002F3] COPY nginx\u002Fnginx.conf \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf                                                                       0.0s\n => CACHED [3\u002F3] COPY dist \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml                                                                                            0.0s\n => exporting to image                                                                                                                      0.0s\n => => exporting layers                                                                                                                     0.0s\n => => exporting manifest sha256:53bc6114ca6e7d29024cf18159b4aa8c43551d7bcabc5e6982fb61a66a87fb42                                           0.0s\n => => exporting config sha256:7c18f3558404726990b30f8e86748dacbf38e0f76ea9caa61c88b21edce82bdd                                             0.0s\n => => exporting attestation manifest sha256:1be1fa6402632b870206f0da6be77e55034c509557fc585abfd7de85444d15cf                               0.0s\n => => exporting manifest list sha256:2caf0eeae8264233a72c67166ba8adeeb1932b2794ec422dee23925c95eaf60a                                      0.0s\n => => naming to docker.io\u002Flibrary\u002Ffrontend:latest                                                                                          0.0s\n => => unpacking to docker.io\u002Flibrary\u002Ffrontend:latest                                                                                       0.0s\n\nView build details: docker-desktop:\u002F\u002Fdashboard\u002Fbuild\u002Fdesktop-linux\u002Fdesktop-linux\u002Fvk3dy9rb8tkmpjaffkhks62it\nfrontend-test\nfrontend-test\n8d423639a6f67b7c6400742eb62181836ecb80d90133e1dc716b4e88c26c62aa\n로컬 테스트: http:\u002F\u002Flocalhost:8080\n[main 61a4a77] test\n","sh",[40,19385,19386,19394,19401,19405,19410,19426,19434,19448,19471,19490,19509,19513,19550,19577,19599,19629,19642,19653,19660,19681,19693,19705,19712,19731,19738,19755,19762,19780,19793,19804,19821,19838,19857,19875,19896,19917,19936,19954,19958,19971,19976,19980,19985,19996],{"__ignoreMap":38},[43,19387,19388,19391],{"class":45,"line":46},[43,19389,19390],{"class":67},">",[43,19392,19393],{"class":49}," my-app@0.0.0 build\n",[43,19395,19396,19398],{"class":45,"line":64},[43,19397,19390],{"class":67},[43,19399,19400],{"class":49}," vite build\n",[43,19402,19403],{"class":45,"line":119},[43,19404,1782],{"emptyLinePlaceholder":491},[43,19406,19407],{"class":45,"line":150},[43,19408,19409],{"class":57},"^R\n",[43,19411,19412,19415,19418,19421,19423],{"class":45,"line":159},[43,19413,19414],{"class":57},"givite",[43,19416,19417],{"class":100}," v6.2.1",[43,19419,19420],{"class":100}," building",[43,19422,11147],{"class":100},[43,19424,19425],{"class":100}," production...\n",[43,19427,19428,19431],{"class":45,"line":176},[43,19429,19430],{"class":57},"transforming",[43,19432,19433],{"class":49}," (1) src\u002Fmain.tsx^R\n",[43,19435,19436,19439,19442,19445],{"class":45,"line":1163},[43,19437,19438],{"class":57},"✓",[43,19440,19441],{"class":81}," 2288",[43,19443,19444],{"class":100}," modules",[43,19446,19447],{"class":100}," transformed.\n",[43,19449,19450,19453,19456,19459,19462,19465,19468],{"class":45,"line":1178},[43,19451,19452],{"class":57},"dist\u002Findex.html",[43,19454,19455],{"class":81},"                   0.46",[43,19457,19458],{"class":100}," kB",[43,19460,19461],{"class":100}," │",[43,19463,19464],{"class":100}," gzip:",[43,19466,19467],{"class":81},"   0.30",[43,19469,19470],{"class":100}," kB\n",[43,19472,19473,19476,19479,19481,19483,19485,19488],{"class":45,"line":1184},[43,19474,19475],{"class":57},"dist\u002Fassets\u002Findex-vuH6rREl.css",[43,19477,19478],{"class":81},"   49.00",[43,19480,19458],{"class":100},[43,19482,19461],{"class":100},[43,19484,19464],{"class":100},[43,19486,19487],{"class":81},"   5.82",[43,19489,19470],{"class":100},[43,19491,19492,19495,19498,19500,19502,19504,19507],{"class":45,"line":1190},[43,19493,19494],{"class":57},"dist\u002Fassets\u002Findex-DWljNZS7.js",[43,19496,19497],{"class":81},"   734.03",[43,19499,19458],{"class":100},[43,19501,19461],{"class":100},[43,19503,19464],{"class":100},[43,19505,19506],{"class":81}," 216.05",[43,19508,19470],{"class":100},[43,19510,19511],{"class":45,"line":1996},[43,19512,1782],{"emptyLinePlaceholder":491},[43,19514,19515,19517,19519,19521,19524,19527,19530,19533,19536,19539,19541,19544,19547],{"class":45,"line":2008},[43,19516,97],{"class":49},[43,19518,3052],{"class":67},[43,19520,2408],{"class":49},[43,19522,19523],{"class":57},"Some",[43,19525,19526],{"class":100}," chunks",[43,19528,19529],{"class":100}," are",[43,19531,19532],{"class":100}," larger",[43,19534,19535],{"class":100}," than",[43,19537,19538],{"class":81}," 500",[43,19540,19458],{"class":100},[43,19542,19543],{"class":100}," after",[43,19545,19546],{"class":100}," minification.",[43,19548,19549],{"class":100}," Consider:\n",[43,19551,19552,19554,19557,19560,19563,19566,19568,19571,19574],{"class":45,"line":2025},[43,19553,18523],{"class":57},[43,19555,19556],{"class":100}," Using",[43,19558,19559],{"class":100}," dynamic",[43,19561,19562],{"class":100}," import",[43,19564,19565],{"class":49},"() ",[43,19567,11755],{"class":100},[43,19569,19570],{"class":100}," code-split",[43,19572,19573],{"class":100}," the",[43,19575,19576],{"class":100}," application\n",[43,19578,19579,19581,19584,19587,19590,19593,19596],{"class":45,"line":2031},[43,19580,18523],{"class":57},[43,19582,19583],{"class":100}," Use",[43,19585,19586],{"class":100}," build.rollupOptions.output.manualChunks",[43,19588,19589],{"class":100}," to",[43,19591,19592],{"class":100}," improve",[43,19594,19595],{"class":100}," chunking:",[43,19597,19598],{"class":100}," https:\u002F\u002Frollupjs.org\u002Fconfiguration-options\u002F#output-manualchunks\n",[43,19600,19601,19603,19606,19609,19612,19615,19617,19620,19623,19626],{"class":45,"line":2037},[43,19602,18523],{"class":57},[43,19604,19605],{"class":100}," Adjust",[43,19607,19608],{"class":100}," chunk",[43,19610,19611],{"class":100}," size",[43,19613,19614],{"class":100}," limit",[43,19616,11147],{"class":100},[43,19618,19619],{"class":100}," this",[43,19621,19622],{"class":100}," warning",[43,19624,19625],{"class":100}," via",[43,19627,19628],{"class":100}," build.chunkSizeWarningLimit.\n",[43,19630,19631,19633,19636,19639],{"class":45,"line":2042},[43,19632,19438],{"class":57},[43,19634,19635],{"class":100}," built",[43,19637,19638],{"class":100}," in",[43,19640,19641],{"class":100}," 3.12s\n",[43,19643,19644,19647,19650],{"class":45,"line":2516},[43,19645,19646],{"class":49},"[+] Building 2.0s (",[43,19648,19649],{"class":57},"9\u002F9",[43,19651,19652],{"class":49},") FINISHED                                                                                            docker:desktop-linux\n",[43,19654,19655,19657],{"class":45,"line":2998},[43,19656,128],{"class":100},[43,19658,19659],{"class":49},"> [internal] load build definition from Dockerfile                                                                                        0.0s\n",[43,19661,19662,19664,19667,19669,19672,19675,19678],{"class":45,"line":3003},[43,19663,128],{"class":100},[43,19665,19666],{"class":49},"> =",[43,19668,19390],{"class":67},[43,19670,19671],{"class":100}," transferring",[43,19673,19674],{"class":100}," dockerfile:",[43,19676,19677],{"class":100}," 356B",[43,19679,19680],{"class":100},"                                                                                                        0.0s\n",[43,19682,19683,19685,19688,19690],{"class":45,"line":3009},[43,19684,128],{"class":100},[43,19686,19687],{"class":49},"> [internal] load metadata ",[43,19689,8600],{"class":67},[43,19691,19692],{"class":49}," docker.io\u002Flibrary\u002Fnginx:1.25.4-alpine                                                                      1.8s\n",[43,19694,19695,19697,19700,19702],{"class":45,"line":3022},[43,19696,128],{"class":100},[43,19698,19699],{"class":49},"> [auth] library\u002Fnginx:pull token ",[43,19701,8600],{"class":67},[43,19703,19704],{"class":49}," registry-1.docker.io                                                                                0.0s\n",[43,19706,19707,19709],{"class":45,"line":3044},[43,19708,128],{"class":100},[43,19710,19711],{"class":49},"> [internal] load .dockerignore                                                                                                           0.0s\n",[43,19713,19714,19716,19718,19720,19722,19725,19728],{"class":45,"line":3058},[43,19715,128],{"class":100},[43,19717,19666],{"class":49},[43,19719,19390],{"class":67},[43,19721,19671],{"class":100},[43,19723,19724],{"class":100}," context:",[43,19726,19727],{"class":100}," 2B",[43,19729,19730],{"class":100},"                                                                                                             0.0s\n",[43,19732,19733,19735],{"class":45,"line":3077},[43,19734,128],{"class":100},[43,19736,19737],{"class":49},"> [1\u002F3] FROM docker.io\u002Flibrary\u002Fnginx:1.25.4-alpine@sha256:31bad00311cb5eeb8a6648beadcf67277a175da89989f14727420a80e2e76742                0.0s\n",[43,19739,19740,19742,19744,19746,19749,19752],{"class":45,"line":3082},[43,19741,128],{"class":100},[43,19743,19666],{"class":49},[43,19745,19390],{"class":67},[43,19747,19748],{"class":100}," resolve",[43,19750,19751],{"class":100}," docker.io\u002Flibrary\u002Fnginx:1.25.4-alpine@sha256:31bad00311cb5eeb8a6648beadcf67277a175da89989f14727420a80e2e76742",[43,19753,19754],{"class":100},"                0.0s\n",[43,19756,19757,19759],{"class":45,"line":3088},[43,19758,128],{"class":100},[43,19760,19761],{"class":49},"> [internal] load build context                                                                                                           0.0s\n",[43,19763,19764,19766,19768,19770,19772,19774,19777],{"class":45,"line":3093},[43,19765,128],{"class":100},[43,19767,19666],{"class":49},[43,19769,19390],{"class":67},[43,19771,19671],{"class":100},[43,19773,19724],{"class":100},[43,19775,19776],{"class":100}," 785.80kB",[43,19778,19779],{"class":100},"                                                                                                       0.0s\n",[43,19781,19782,19784,19787,19790],{"class":45,"line":3099},[43,19783,128],{"class":100},[43,19785,19786],{"class":49},"> ",[43,19788,19789],{"class":100},"CACHED",[43,19791,19792],{"class":49}," [2\u002F3] COPY nginx\u002Fnginx.conf \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf                                                                       0.0s\n",[43,19794,19795,19797,19799,19801],{"class":45,"line":3145},[43,19796,128],{"class":100},[43,19798,19786],{"class":49},[43,19800,19789],{"class":100},[43,19802,19803],{"class":49}," [3\u002F3] COPY dist \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml                                                                                            0.0s\n",[43,19805,19806,19808,19810,19813,19815,19818],{"class":45,"line":3152},[43,19807,128],{"class":100},[43,19809,19786],{"class":49},[43,19811,19812],{"class":100},"exporting",[43,19814,19589],{"class":100},[43,19816,19817],{"class":100}," image",[43,19819,19820],{"class":100},"                                                                                                                      0.0s\n",[43,19822,19823,19825,19827,19829,19832,19835],{"class":45,"line":3165},[43,19824,128],{"class":100},[43,19826,19666],{"class":49},[43,19828,19390],{"class":67},[43,19830,19831],{"class":100}," exporting",[43,19833,19834],{"class":100}," layers",[43,19836,19837],{"class":100},"                                                                                                                     0.0s\n",[43,19839,19840,19842,19844,19846,19848,19851,19854],{"class":45,"line":3203},[43,19841,128],{"class":100},[43,19843,19666],{"class":49},[43,19845,19390],{"class":67},[43,19847,19831],{"class":100},[43,19849,19850],{"class":100}," manifest",[43,19852,19853],{"class":100}," sha256:53bc6114ca6e7d29024cf18159b4aa8c43551d7bcabc5e6982fb61a66a87fb42",[43,19855,19856],{"class":100},"                                           0.0s\n",[43,19858,19859,19861,19863,19865,19867,19869,19872],{"class":45,"line":3208},[43,19860,128],{"class":100},[43,19862,19666],{"class":49},[43,19864,19390],{"class":67},[43,19866,19831],{"class":100},[43,19868,6133],{"class":100},[43,19870,19871],{"class":100}," sha256:7c18f3558404726990b30f8e86748dacbf38e0f76ea9caa61c88b21edce82bdd",[43,19873,19874],{"class":100},"                                             0.0s\n",[43,19876,19877,19879,19881,19883,19885,19888,19890,19893],{"class":45,"line":3219},[43,19878,128],{"class":100},[43,19880,19666],{"class":49},[43,19882,19390],{"class":67},[43,19884,19831],{"class":100},[43,19886,19887],{"class":100}," attestation",[43,19889,19850],{"class":100},[43,19891,19892],{"class":100}," sha256:1be1fa6402632b870206f0da6be77e55034c509557fc585abfd7de85444d15cf",[43,19894,19895],{"class":100},"                               0.0s\n",[43,19897,19898,19900,19902,19904,19906,19908,19911,19914],{"class":45,"line":3227},[43,19899,128],{"class":100},[43,19901,19666],{"class":49},[43,19903,19390],{"class":67},[43,19905,19831],{"class":100},[43,19907,19850],{"class":100},[43,19909,19910],{"class":100}," list",[43,19912,19913],{"class":100}," sha256:2caf0eeae8264233a72c67166ba8adeeb1932b2794ec422dee23925c95eaf60a",[43,19915,19916],{"class":100},"                                      0.0s\n",[43,19918,19919,19921,19923,19925,19928,19930,19933],{"class":45,"line":3233},[43,19920,128],{"class":100},[43,19922,19666],{"class":49},[43,19924,19390],{"class":67},[43,19926,19927],{"class":100}," naming",[43,19929,19589],{"class":100},[43,19931,19932],{"class":100}," docker.io\u002Flibrary\u002Ffrontend:latest",[43,19934,19935],{"class":100},"                                                                                          0.0s\n",[43,19937,19938,19940,19942,19944,19947,19949,19951],{"class":45,"line":3239},[43,19939,128],{"class":100},[43,19941,19666],{"class":49},[43,19943,19390],{"class":67},[43,19945,19946],{"class":100}," unpacking",[43,19948,19589],{"class":100},[43,19950,19932],{"class":100},[43,19952,19953],{"class":100},"                                                                                       0.0s\n",[43,19955,19956],{"class":45,"line":3251},[43,19957,1782],{"emptyLinePlaceholder":491},[43,19959,19960,19963,19965,19968],{"class":45,"line":3256},[43,19961,19962],{"class":57},"View",[43,19964,19137],{"class":100},[43,19966,19967],{"class":100}," details:",[43,19969,19970],{"class":100}," docker-desktop:\u002F\u002Fdashboard\u002Fbuild\u002Fdesktop-linux\u002Fdesktop-linux\u002Fvk3dy9rb8tkmpjaffkhks62it\n",[43,19972,19973],{"class":45,"line":3261},[43,19974,19975],{"class":57},"frontend-test\n",[43,19977,19978],{"class":45,"line":3267},[43,19979,19975],{"class":57},[43,19981,19982],{"class":45,"line":3279},[43,19983,19984],{"class":57},"8d423639a6f67b7c6400742eb62181836ecb80d90133e1dc716b4e88c26c62aa\n",[43,19986,19987,19990,19993],{"class":45,"line":3284},[43,19988,19989],{"class":57},"로컬",[43,19991,19992],{"class":100}," 테스트:",[43,19994,19995],{"class":100}," http:\u002F\u002Flocalhost:8080\n",[43,19997,19998],{"class":45,"line":3290},[43,19999,20000],{"class":49},"[main 61a4a77] test\n",[471,20002,20003],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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);}",{"title":38,"searchDepth":64,"depth":64,"links":20005},[20006,20007,20008],{"id":19054,"depth":64,"text":19055},{"id":19069,"depth":64,"text":19070},{"id":19287,"depth":64,"text":19288},"로컬에서 커밋 시 도커 빌드 후 컨테이너 시작하기.",{"date":20011,"field":4436,"tags":20012},"2025.03.10",[1557],"\u002Fko\u002Fdocker-auto-build-run",{"title":19045,"description":20009},{"loc":20013},"ko\u002Fdocker-auto-build-run","VcV4YHe6TOQ5Mw-uW9zBcT3OjsdnhWHlfbqUXoajA2A",{"id":20019,"title":20020,"body":20021,"description":20246,"extension":484,"meta":20247,"navigation":491,"path":20249,"seo":20250,"sitemap":20251,"stem":20252,"__hash__":20253},"ko\u002Fko\u002Fpm2-guide.md","프로세스 매니저 PM2",{"type":8,"value":20022,"toc":20234},[20023,20026,20029,20033,20036,20053,20055,20059,20063,20080,20083,20087,20103,20111,20128,20132,20144,20148,20200,20204,20216,20219,20231],[15,20024,20025],{},"PM2는 Node.js 애플리케이션을 위한 프로덕션 프로세스 매니저로, 내장된 로드 밸런서를 제공합니다. 이를 통해 애플리케이션을 영구적으로 실행 상태로 유지하고, 다운타임 없이 재로드하며, 일반적인 시스템 관리 작업을 용이하게 수행할 수 있습니다.",[15,20027,20028],{},"PM2 is a production process manager for Node.js applications with a built-in load balancer. It allows you to keep applications alive forever, to reload them without downtime and to facilitate common system admin tasks.",[29,20030,20032],{"id":20031},"pm2란-무엇인가","PM2란 무엇인가?",[15,20034,20035],{},"PM2는 Node.js 애플리케이션의 프로세스를 관리하기 위한 도구로, 다음과 같은 주요 기능을 제공합니다:",[185,20037,20038,20041,20044,20047,20050],{},[188,20039,20040],{},"애플리케이션의 지속적인 실행 보장 (중단 없는 서비스 제공)",[188,20042,20043],{},"다운타임 없이 애플리케이션 재시작 가능",[188,20045,20046],{},"클러스터 모드를 통한 멀티코어 활용 및 로드 밸런싱",[188,20048,20049],{},"로그 관리 및 실시간 모니터링",[188,20051,20052],{},"코드 변경 사항 감지 및 자동 재시작 (--watch 옵션)",[26,20054],{},[29,20056,20058],{"id":20057},"pm2-설치","PM2 설치",[1273,20060,20062],{"id":20061},"애플리케이션-시작","애플리케이션 시작",[33,20064,20066],{"className":850,"code":20065,"language":852,"meta":38,"style":38},"npm install pm2 -g\n",[40,20067,20068],{"__ignoreMap":38},[43,20069,20070,20072,20074,20077],{"class":45,"line":46},[43,20071,1741],{"class":57},[43,20073,1744],{"class":100},[43,20075,20076],{"class":100}," pm2",[43,20078,20079],{"class":81}," -g\n",[15,20081,20082],{},"전역(-g)으로 설치하여 모든 프로젝트에서 사용할 수 있습니다. 권한 문제가 발생하면 sudo 추가",[29,20084,20086],{"id":20085},"pm2-기본-명령어","PM2 기본 명령어",[33,20088,20090],{"className":850,"code":20089,"language":852,"meta":38,"style":38},"pm2 start app.js\n",[40,20091,20092],{"__ignoreMap":38},[43,20093,20094,20097,20100],{"class":45,"line":46},[43,20095,20096],{"class":57},"pm2",[43,20098,20099],{"class":100}," start",[43,20101,20102],{"class":100}," app.js\n",[185,20104,20105,20108],{},[188,20106,20107],{},"app.js를 백그라운드에서 실행하며, 종료되지 않도록 유지합니다.",[188,20109,20110],{},"--watch 옵션을 추가하면 파일 변경 시 자동으로 재시작됩니다:",[33,20112,20114],{"className":850,"code":20113,"language":852,"meta":38,"style":38},"pm2 start app.js --watch\n",[40,20115,20116],{"__ignoreMap":38},[43,20117,20118,20120,20122,20125],{"class":45,"line":46},[43,20119,20096],{"class":57},[43,20121,20099],{"class":100},[43,20123,20124],{"class":100}," app.js",[43,20126,20127],{"class":81}," --watch\n",[1273,20129,20131],{"id":20130},"실행-중인-프로세스-확인","실행 중인 프로세스 확인",[33,20133,20135],{"className":850,"code":20134,"language":852,"meta":38,"style":38},"pm2 list\n",[40,20136,20137],{"__ignoreMap":38},[43,20138,20139,20141],{"class":45,"line":46},[43,20140,20096],{"class":57},[43,20142,20143],{"class":100}," list\n",[1273,20145,20147],{"id":20146},"프로세스-중지-재시작-삭제-전체-삭제","프로세스 중지, 재시작, 삭제, 전체 삭제",[33,20149,20151],{"className":850,"code":20150,"language":852,"meta":38,"style":38},"pm2 stop app.js        # 중지\npm2 restart app.js     # 재시작\npm2 delete app.js      # 삭제\npm2 delete all      # 전체 삭제\n",[40,20152,20153,20164,20176,20188],{"__ignoreMap":38},[43,20154,20155,20157,20159,20161],{"class":45,"line":46},[43,20156,20096],{"class":57},[43,20158,19153],{"class":100},[43,20160,20124],{"class":100},[43,20162,20163],{"class":2797},"        # 중지\n",[43,20165,20166,20168,20171,20173],{"class":45,"line":64},[43,20167,20096],{"class":57},[43,20169,20170],{"class":100}," restart",[43,20172,20124],{"class":100},[43,20174,20175],{"class":2797},"     # 재시작\n",[43,20177,20178,20180,20183,20185],{"class":45,"line":119},[43,20179,20096],{"class":57},[43,20181,20182],{"class":100}," delete",[43,20184,20124],{"class":100},[43,20186,20187],{"class":2797},"      # 삭제\n",[43,20189,20190,20192,20194,20197],{"class":45,"line":150},[43,20191,20096],{"class":57},[43,20193,20182],{"class":100},[43,20195,20196],{"class":100}," all",[43,20198,20199],{"class":2797},"      # 전체 삭제\n",[1273,20201,20203],{"id":20202},"로그-확인","로그 확인",[33,20205,20207],{"className":850,"code":20206,"language":852,"meta":38,"style":38},"pm2 log\n",[40,20208,20209],{"__ignoreMap":38},[43,20210,20211,20213],{"class":45,"line":46},[43,20212,20096],{"class":57},[43,20214,20215],{"class":100}," log\n",[1273,20217,20218],{"id":20218},"모니터링",[33,20220,20222],{"className":850,"code":20221,"language":852,"meta":38,"style":38},"pm2 monit\n",[40,20223,20224],{"__ignoreMap":38},[43,20225,20226,20228],{"class":45,"line":46},[43,20227,20096],{"class":57},[43,20229,20230],{"class":100}," monit\n",[471,20232,20233],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":38,"searchDepth":64,"depth":64,"links":20235},[20236,20237,20240],{"id":20031,"depth":64,"text":20032},{"id":20057,"depth":64,"text":20058,"children":20238},[20239],{"id":20061,"depth":119,"text":20062},{"id":20085,"depth":64,"text":20086,"children":20241},[20242,20243,20244,20245],{"id":20130,"depth":119,"text":20131},{"id":20146,"depth":119,"text":20147},{"id":20202,"depth":119,"text":20203},{"id":20218,"depth":119,"text":20218},"배포환경에서 유용하게 사용할 수 있는 프로세스 매니저를 알아보자.",{"date":20011,"field":4436,"tags":20248},[20096,3795],"\u002Fko\u002Fpm2-guide",{"title":20020,"description":20246},{"loc":20249},"ko\u002Fpm2-guide","kZytBXV5SFNLcMD9A1NNR6_eXTa-Jm8N4qnXPVbRbZs",{"id":20255,"title":20256,"body":20257,"description":21521,"extension":484,"meta":21522,"navigation":491,"path":21525,"seo":21526,"sitemap":21527,"stem":21528,"__hash__":21529},"ko\u002Fko\u002Fjs-data-01.md","JavaScript 데이터01",{"type":8,"value":20258,"toc":21512},[20259,20263,20268,20285,20463,20467,20471,20480,20616,20620,20624,20641,20876,20880,20884,20899,21127,21131,21135,21146,21341,21343,21347,21350,21426,21430,21433,21509],[29,20260,20262],{"id":20261},"문제-1-배열-필터링-및-변환-초급","문제 1: 배열 필터링 및 변환 (초급)",[15,20264,20265],{},[21,20266,20267],{},"목표:",[185,20269,20270,20278],{},[188,20271,20272,398,20275,20277],{},[40,20273,20274],{},"active",[40,20276,1053],{},"인 사용자만 필터링하세요.",[188,20279,20280,20281,20284],{},"필터링된 사용자들의 ",[40,20282,20283],{},"name","만 추출해 새로운 배열로 만드세요.",[33,20286,20288],{"className":1024,"code":20287,"language":1026,"meta":38,"style":38},"const users = [\n  { id: 1, name: \"Alice\", age: 25, active: true },\n  { id: 2, name: \"Bob\", age: 30, active: false },\n  { id: 3, name: \"Charlie\", age: 22, active: true },\n  { id: 4, name: \"David\", age: 35, active: false },\n];\n\nconst activeUserNames = users\n  .filter((user) => user.active)\n  .map((user) => user.name);\n\nconsole.log(activeUserNames); \u002F\u002F [\"Alice\", \"Charlie\"]\n",[40,20289,20290,20302,20327,20348,20370,20392,20396,20400,20412,20430,20447,20451],{"__ignoreMap":38},[43,20291,20292,20294,20297,20299],{"class":45,"line":46},[43,20293,68],{"class":67},[43,20295,20296],{"class":81}," users",[43,20298,128],{"class":67},[43,20300,20301],{"class":49}," [\n",[43,20303,20304,20307,20309,20312,20315,20317,20320,20323,20325],{"class":45,"line":64},[43,20305,20306],{"class":49},"  { id: ",[43,20308,905],{"class":81},[43,20310,20311],{"class":49},", name: ",[43,20313,20314],{"class":100},"\"Alice\"",[43,20316,5167],{"class":49},[43,20318,20319],{"class":81},"25",[43,20321,20322],{"class":49},", active: ",[43,20324,1053],{"class":81},[43,20326,5178],{"class":49},[43,20328,20329,20331,20333,20335,20338,20340,20342,20344,20346],{"class":45,"line":119},[43,20330,20306],{"class":49},[43,20332,918],{"class":81},[43,20334,20311],{"class":49},[43,20336,20337],{"class":100},"\"Bob\"",[43,20339,5167],{"class":49},[43,20341,11812],{"class":81},[43,20343,20322],{"class":49},[43,20345,1830],{"class":81},[43,20347,5178],{"class":49},[43,20349,20350,20352,20354,20356,20359,20361,20364,20366,20368],{"class":45,"line":150},[43,20351,20306],{"class":49},[43,20353,931],{"class":81},[43,20355,20311],{"class":49},[43,20357,20358],{"class":100},"\"Charlie\"",[43,20360,5167],{"class":49},[43,20362,20363],{"class":81},"22",[43,20365,20322],{"class":49},[43,20367,1053],{"class":81},[43,20369,5178],{"class":49},[43,20371,20372,20374,20376,20378,20381,20383,20386,20388,20390],{"class":45,"line":159},[43,20373,20306],{"class":49},[43,20375,950],{"class":81},[43,20377,20311],{"class":49},[43,20379,20380],{"class":100},"\"David\"",[43,20382,5167],{"class":49},[43,20384,20385],{"class":81},"35",[43,20387,20322],{"class":49},[43,20389,1830],{"class":81},[43,20391,5178],{"class":49},[43,20393,20394],{"class":45,"line":176},[43,20395,5441],{"class":49},[43,20397,20398],{"class":45,"line":1163},[43,20399,1782],{"emptyLinePlaceholder":491},[43,20401,20402,20404,20407,20409],{"class":45,"line":1178},[43,20403,68],{"class":67},[43,20405,20406],{"class":81}," activeUserNames",[43,20408,128],{"class":67},[43,20410,20411],{"class":49}," users\n",[43,20413,20414,20416,20418,20420,20423,20425,20427],{"class":45,"line":1184},[43,20415,5228],{"class":49},[43,20417,4881],{"class":57},[43,20419,4691],{"class":49},[43,20421,20422],{"class":74},"user",[43,20424,2408],{"class":49},[43,20426,113],{"class":67},[43,20428,20429],{"class":49}," user.active)\n",[43,20431,20432,20434,20436,20438,20440,20442,20444],{"class":45,"line":1190},[43,20433,5228],{"class":49},[43,20435,1123],{"class":57},[43,20437,4691],{"class":49},[43,20439,20422],{"class":74},[43,20441,2408],{"class":49},[43,20443,113],{"class":67},[43,20445,20446],{"class":49}," user.name);\n",[43,20448,20449],{"class":45,"line":1996},[43,20450,1782],{"emptyLinePlaceholder":491},[43,20452,20453,20455,20457,20460],{"class":45,"line":2008},[43,20454,6150],{"class":49},[43,20456,1931],{"class":57},[43,20458,20459],{"class":49},"(activeUserNames); ",[43,20461,20462],{"class":2797},"\u002F\u002F [\"Alice\", \"Charlie\"]\n",[29,20464,20466],{"id":20465},"문제-2-데이터-합계-계산-초급","문제 2: 데이터 합계 계산 (초급)",[15,20468,20469],{},[21,20470,20267],{},[185,20472,20473],{},[188,20474,20475,20476,20479],{},"모든 ",[40,20477,20478],{},"amount","의 합계를 구하세요.",[33,20481,20483],{"className":1024,"code":20482,"language":1026,"meta":38,"style":38},"const orders = [\n  { orderId: \"A1\", amount: 100 },\n  { orderId: \"A2\", amount: 250 },\n  { orderId: \"A3\", amount: 75 },\n  { orderId: \"A4\", amount: 300 },\n];\n\nconst totalOrderAmount = orders.reduce((acc, order) => acc + order.amount, 0);\n\nconsole.log(totalOrderAmount); \u002F\u002F 725\n",[40,20484,20485,20496,20511,20525,20539,20552,20556,20560,20600,20604],{"__ignoreMap":38},[43,20486,20487,20489,20492,20494],{"class":45,"line":46},[43,20488,68],{"class":67},[43,20490,20491],{"class":81}," orders",[43,20493,128],{"class":67},[43,20495,20301],{"class":49},[43,20497,20498,20501,20504,20507,20509],{"class":45,"line":64},[43,20499,20500],{"class":49},"  { orderId: ",[43,20502,20503],{"class":100},"\"A1\"",[43,20505,20506],{"class":49},", amount: ",[43,20508,11595],{"class":81},[43,20510,5178],{"class":49},[43,20512,20513,20515,20518,20520,20523],{"class":45,"line":119},[43,20514,20500],{"class":49},[43,20516,20517],{"class":100},"\"A2\"",[43,20519,20506],{"class":49},[43,20521,20522],{"class":81},"250",[43,20524,5178],{"class":49},[43,20526,20527,20529,20532,20534,20537],{"class":45,"line":150},[43,20528,20500],{"class":49},[43,20530,20531],{"class":100},"\"A3\"",[43,20533,20506],{"class":49},[43,20535,20536],{"class":81},"75",[43,20538,5178],{"class":49},[43,20540,20541,20543,20546,20548,20550],{"class":45,"line":159},[43,20542,20500],{"class":49},[43,20544,20545],{"class":100},"\"A4\"",[43,20547,20506],{"class":49},[43,20549,15366],{"class":81},[43,20551,5178],{"class":49},[43,20553,20554],{"class":45,"line":176},[43,20555,5441],{"class":49},[43,20557,20558],{"class":45,"line":1163},[43,20559,1782],{"emptyLinePlaceholder":491},[43,20561,20562,20564,20567,20569,20572,20574,20576,20578,20580,20583,20585,20587,20590,20593,20596,20598],{"class":45,"line":1178},[43,20563,68],{"class":67},[43,20565,20566],{"class":81}," totalOrderAmount",[43,20568,128],{"class":67},[43,20570,20571],{"class":49}," orders.",[43,20573,5113],{"class":57},[43,20575,4691],{"class":49},[43,20577,5118],{"class":74},[43,20579,104],{"class":49},[43,20581,20582],{"class":74},"order",[43,20584,2408],{"class":49},[43,20586,113],{"class":67},[43,20588,20589],{"class":49}," acc ",[43,20591,20592],{"class":67},"+",[43,20594,20595],{"class":49}," order.amount, ",[43,20597,4401],{"class":81},[43,20599,1160],{"class":49},[43,20601,20602],{"class":45,"line":1184},[43,20603,1782],{"emptyLinePlaceholder":491},[43,20605,20606,20608,20610,20613],{"class":45,"line":1190},[43,20607,6150],{"class":49},[43,20609,1931],{"class":57},[43,20611,20612],{"class":49},"(totalOrderAmount); ",[43,20614,20615],{"class":2797},"\u002F\u002F 725\n",[29,20617,20619],{"id":20618},"문제-3-데이터-그룹화-중급","문제 3: 데이터 그룹화 (중급)",[15,20621,20622],{},[21,20623,20267],{},[185,20625,20626,20632],{},[188,20627,20628,20631],{},[40,20629,20630],{},"category","별로 제품을 그룹화하고, 각 카테고리의 제품 이름 배열을 만드세요.",[188,20633,20634,20636,20637,20640],{},[40,20635,20630],{},"가 없는 제품은 ",[40,20638,20639],{},"\"Uncategorized\""," 그룹에 포함하세요.",[33,20642,20644],{"className":1024,"code":20643,"language":1026,"meta":38,"style":38},"const products = [\n  { name: \"Laptop\", category: \"Electronics\", price: 1000 },\n  { name: \"Shirt\", category: \"Clothing\", price: 20 },\n  { name: \"Phone\", category: \"Electronics\", price: 500 },\n  { name: \"Jeans\", price: 50 },\n];\n\nconst productsByCategory = products.reduce((acc, product) => {\n  const category = product.category || \"Uncategorized\";\n  if (!acc[category]) {\n    acc[category] = [];\n  }\n  acc[category].push(product.name);\n  return acc;\n}, {});\n\nconsole.log(productsByCategory);\n\u002F*\n{\n  Electronics: [\"Laptop\", \"Phone\"],\n  Clothing: [\"Shirt\"],\n  Uncategorized: [\"Jeans\"]\n}\n*\u002F\n",[40,20645,20646,20657,20677,20695,20712,20725,20729,20733,20762,20781,20792,20802,20806,20817,20824,20829,20833,20842,20847,20852,20857,20862,20867,20871],{"__ignoreMap":38},[43,20647,20648,20650,20653,20655],{"class":45,"line":46},[43,20649,68],{"class":67},[43,20651,20652],{"class":81}," products",[43,20654,128],{"class":67},[43,20656,20301],{"class":49},[43,20658,20659,20661,20664,20667,20670,20673,20675],{"class":45,"line":64},[43,20660,5162],{"class":49},[43,20662,20663],{"class":100},"\"Laptop\"",[43,20665,20666],{"class":49},", category: ",[43,20668,20669],{"class":100},"\"Electronics\"",[43,20671,20672],{"class":49},", price: ",[43,20674,10054],{"class":81},[43,20676,5178],{"class":49},[43,20678,20679,20681,20684,20686,20689,20691,20693],{"class":45,"line":119},[43,20680,5162],{"class":49},[43,20682,20683],{"class":100},"\"Shirt\"",[43,20685,20666],{"class":49},[43,20687,20688],{"class":100},"\"Clothing\"",[43,20690,20672],{"class":49},[43,20692,12033],{"class":81},[43,20694,5178],{"class":49},[43,20696,20697,20699,20702,20704,20706,20708,20710],{"class":45,"line":150},[43,20698,5162],{"class":49},[43,20700,20701],{"class":100},"\"Phone\"",[43,20703,20666],{"class":49},[43,20705,20669],{"class":100},[43,20707,20672],{"class":49},[43,20709,3384],{"class":81},[43,20711,5178],{"class":49},[43,20713,20714,20716,20719,20721,20723],{"class":45,"line":159},[43,20715,5162],{"class":49},[43,20717,20718],{"class":100},"\"Jeans\"",[43,20720,20672],{"class":49},[43,20722,12222],{"class":81},[43,20724,5178],{"class":49},[43,20726,20727],{"class":45,"line":176},[43,20728,5441],{"class":49},[43,20730,20731],{"class":45,"line":1163},[43,20732,1782],{"emptyLinePlaceholder":491},[43,20734,20735,20737,20740,20742,20745,20747,20749,20751,20753,20756,20758,20760],{"class":45,"line":1178},[43,20736,68],{"class":67},[43,20738,20739],{"class":81}," productsByCategory",[43,20741,128],{"class":67},[43,20743,20744],{"class":49}," products.",[43,20746,5113],{"class":57},[43,20748,4691],{"class":49},[43,20750,5118],{"class":74},[43,20752,104],{"class":49},[43,20754,20755],{"class":74},"product",[43,20757,2408],{"class":49},[43,20759,113],{"class":67},[43,20761,116],{"class":49},[43,20763,20764,20766,20769,20771,20774,20776,20779],{"class":45,"line":1184},[43,20765,122],{"class":67},[43,20767,20768],{"class":81}," category",[43,20770,128],{"class":67},[43,20772,20773],{"class":49}," product.category ",[43,20775,9342],{"class":67},[43,20777,20778],{"class":100}," \"Uncategorized\"",[43,20780,2995],{"class":49},[43,20782,20783,20785,20787,20789],{"class":45,"line":1190},[43,20784,3047],{"class":67},[43,20786,1069],{"class":49},[43,20788,3052],{"class":67},[43,20790,20791],{"class":49},"acc[category]) {\n",[43,20793,20794,20797,20799],{"class":45,"line":1996},[43,20795,20796],{"class":49},"    acc[category] ",[43,20798,88],{"class":67},[43,20800,20801],{"class":49}," [];\n",[43,20803,20804],{"class":45,"line":2008},[43,20805,2028],{"class":49},[43,20807,20808,20811,20814],{"class":45,"line":2025},[43,20809,20810],{"class":49},"  acc[category].",[43,20812,20813],{"class":57},"push",[43,20815,20816],{"class":49},"(product.name);\n",[43,20818,20819,20821],{"class":45,"line":2031},[43,20820,153],{"class":67},[43,20822,20823],{"class":49}," acc;\n",[43,20825,20826],{"class":45,"line":2037},[43,20827,20828],{"class":49},"}, {});\n",[43,20830,20831],{"class":45,"line":2042},[43,20832,1782],{"emptyLinePlaceholder":491},[43,20834,20835,20837,20839],{"class":45,"line":2516},[43,20836,6150],{"class":49},[43,20838,1931],{"class":57},[43,20840,20841],{"class":49},"(productsByCategory);\n",[43,20843,20844],{"class":45,"line":2998},[43,20845,20846],{"class":2797},"\u002F*\n",[43,20848,20849],{"class":45,"line":3003},[43,20850,20851],{"class":2797},"{\n",[43,20853,20854],{"class":45,"line":3009},[43,20855,20856],{"class":2797},"  Electronics: [\"Laptop\", \"Phone\"],\n",[43,20858,20859],{"class":45,"line":3022},[43,20860,20861],{"class":2797},"  Clothing: [\"Shirt\"],\n",[43,20863,20864],{"class":45,"line":3044},[43,20865,20866],{"class":2797},"  Uncategorized: [\"Jeans\"]\n",[43,20868,20869],{"class":45,"line":3058},[43,20870,2034],{"class":2797},[43,20872,20873],{"class":45,"line":3077},[43,20874,20875],{"class":2797},"*\u002F\n",[29,20877,20879],{"id":20878},"문제-4-데이터-정렬-및-변환-중급","문제 4: 데이터 정렬 및 변환 (중급)",[15,20881,20882],{},[21,20883,20267],{},[185,20885,20886,20892],{},[188,20887,20888,20891],{},[40,20889,20890],{},"score","를 기준으로 학생들을 내림차순으로 정렬하세요.",[188,20893,20894,20895,20898],{},"각 학생 객체에 ",[40,20896,20897],{},"rank"," 속성을 추가해 순위를 매기세요 (1위부터 시작).",[33,20900,20902],{"className":1024,"code":20901,"language":1026,"meta":38,"style":38},"const students = [\n  { name: \"Eve\", score: 85 },\n  { name: \"Frank\", score: 92 },\n  { name: \"Grace\", score: 78 },\n  { name: \"Helen\", score: 95 },\n];\n\n\u002F\u002F 1. score 기준 내림차순 정렬\nconst sortedStudents = [...students].sort((a, b) => b.score - a.score);\n\n\u002F\u002F 2. rank 속성 추가\nsortedStudents.forEach((student, index) => {\n  student.rank = index + 1;\n});\n\nconsole.log(sortedStudents);\n\u002F*\n[\n  { name: \"Helen\", score: 95, rank: 1 },\n  { name: \"Frank\", score: 92, rank: 2 },\n  { name: \"Eve\", score: 85, rank: 3 },\n  { name: \"Grace\", score: 78, rank: 4 }\n]\n*\u002F\n",[40,20903,20904,20915,20930,20944,20958,20972,20976,20980,20985,21025,21029,21034,21058,21074,21078,21082,21091,21095,21099,21104,21109,21114,21119,21123],{"__ignoreMap":38},[43,20905,20906,20908,20911,20913],{"class":45,"line":46},[43,20907,68],{"class":67},[43,20909,20910],{"class":81}," students",[43,20912,128],{"class":67},[43,20914,20301],{"class":49},[43,20916,20917,20919,20922,20925,20928],{"class":45,"line":64},[43,20918,5162],{"class":49},[43,20920,20921],{"class":100},"\"Eve\"",[43,20923,20924],{"class":49},", score: ",[43,20926,20927],{"class":81},"85",[43,20929,5178],{"class":49},[43,20931,20932,20934,20937,20939,20942],{"class":45,"line":119},[43,20933,5162],{"class":49},[43,20935,20936],{"class":100},"\"Frank\"",[43,20938,20924],{"class":49},[43,20940,20941],{"class":81},"92",[43,20943,5178],{"class":49},[43,20945,20946,20948,20951,20953,20956],{"class":45,"line":150},[43,20947,5162],{"class":49},[43,20949,20950],{"class":100},"\"Grace\"",[43,20952,20924],{"class":49},[43,20954,20955],{"class":81},"78",[43,20957,5178],{"class":49},[43,20959,20960,20962,20965,20967,20970],{"class":45,"line":159},[43,20961,5162],{"class":49},[43,20963,20964],{"class":100},"\"Helen\"",[43,20966,20924],{"class":49},[43,20968,20969],{"class":81},"95",[43,20971,5178],{"class":49},[43,20973,20974],{"class":45,"line":176},[43,20975,5441],{"class":49},[43,20977,20978],{"class":45,"line":1163},[43,20979,1782],{"emptyLinePlaceholder":491},[43,20981,20982],{"class":45,"line":1178},[43,20983,20984],{"class":2797},"\u002F\u002F 1. score 기준 내림차순 정렬\n",[43,20986,20987,20989,20992,20994,20996,20998,21001,21004,21006,21008,21010,21013,21015,21017,21020,21022],{"class":45,"line":1184},[43,20988,68],{"class":67},[43,20990,20991],{"class":81}," sortedStudents",[43,20993,128],{"class":67},[43,20995,1169],{"class":49},[43,20997,1172],{"class":67},[43,20999,21000],{"class":49},"students].",[43,21002,21003],{"class":57},"sort",[43,21005,4691],{"class":49},[43,21007,304],{"class":74},[43,21009,104],{"class":49},[43,21011,21012],{"class":74},"b",[43,21014,2408],{"class":49},[43,21016,113],{"class":67},[43,21018,21019],{"class":49}," b.score ",[43,21021,18523],{"class":67},[43,21023,21024],{"class":49}," a.score);\n",[43,21026,21027],{"class":45,"line":1190},[43,21028,1782],{"emptyLinePlaceholder":491},[43,21030,21031],{"class":45,"line":1996},[43,21032,21033],{"class":2797},"\u002F\u002F 2. rank 속성 추가\n",[43,21035,21036,21039,21042,21044,21047,21049,21052,21054,21056],{"class":45,"line":2008},[43,21037,21038],{"class":49},"sortedStudents.",[43,21040,21041],{"class":57},"forEach",[43,21043,4691],{"class":49},[43,21045,21046],{"class":74},"student",[43,21048,104],{"class":49},[43,21050,21051],{"class":74},"index",[43,21053,2408],{"class":49},[43,21055,113],{"class":67},[43,21057,116],{"class":49},[43,21059,21060,21063,21065,21068,21070,21072],{"class":45,"line":2025},[43,21061,21062],{"class":49},"  student.rank ",[43,21064,88],{"class":67},[43,21066,21067],{"class":49}," index ",[43,21069,20592],{"class":67},[43,21071,15990],{"class":81},[43,21073,2995],{"class":49},[43,21075,21076],{"class":45,"line":2031},[43,21077,1855],{"class":49},[43,21079,21080],{"class":45,"line":2037},[43,21081,1782],{"emptyLinePlaceholder":491},[43,21083,21084,21086,21088],{"class":45,"line":2042},[43,21085,6150],{"class":49},[43,21087,1931],{"class":57},[43,21089,21090],{"class":49},"(sortedStudents);\n",[43,21092,21093],{"class":45,"line":2516},[43,21094,20846],{"class":2797},[43,21096,21097],{"class":45,"line":2998},[43,21098,4624],{"class":2797},[43,21100,21101],{"class":45,"line":3003},[43,21102,21103],{"class":2797},"  { name: \"Helen\", score: 95, rank: 1 },\n",[43,21105,21106],{"class":45,"line":3009},[43,21107,21108],{"class":2797},"  { name: \"Frank\", score: 92, rank: 2 },\n",[43,21110,21111],{"class":45,"line":3022},[43,21112,21113],{"class":2797},"  { name: \"Eve\", score: 85, rank: 3 },\n",[43,21115,21116],{"class":45,"line":3044},[43,21117,21118],{"class":2797},"  { name: \"Grace\", score: 78, rank: 4 }\n",[43,21120,21121],{"class":45,"line":3058},[43,21122,4664],{"class":2797},[43,21124,21125],{"class":45,"line":3077},[43,21126,20875],{"class":2797},[29,21128,21130],{"id":21129},"문제-5-중첩-데이터-처리-중급","문제 5: 중첩 데이터 처리 (중급)",[15,21132,21133],{},[21,21134,20267],{},[185,21136,21137],{},[188,21138,21139,21140,18425,21142,21145],{},"\"Python\" 스킬을 가진 직원만 필터링하고, 그들의 ",[40,21141,20283],{},[40,21143,21144],{},"skills","를 새로운 배열로 만드세요.",[33,21147,21149],{"className":1024,"code":21148,"language":1026,"meta":38,"style":38},"const employees = [\n  { id: 1, name: \"John\", skills: [\"JavaScript\", \"Python\"] },\n  { id: 2, name: \"Jane\", skills: [\"React\", \"Node.js\"] },\n  { id: 3, name: \"Jim\", skills: [\"Python\", \"Django\"] },\n];\n\nconst pythonDevs = employees\n  .filter((employee) => employee.skills.includes(\"Python\"))\n  .map(({ name, skills }) => ({ name, skills }));\n\nconsole.log(pythonDevs);\n\u002F*\n[\n  { name: \"John\", skills: [\"JavaScript\", \"Python\"] },\n  { name: \"Jim\", skills: [\"Python\", \"Django\"] }\n]\n*\u002F\n",[40,21150,21151,21162,21187,21210,21232,21236,21240,21252,21279,21302,21306,21315,21319,21323,21328,21333,21337],{"__ignoreMap":38},[43,21152,21153,21155,21158,21160],{"class":45,"line":46},[43,21154,68],{"class":67},[43,21156,21157],{"class":81}," employees",[43,21159,128],{"class":67},[43,21161,20301],{"class":49},[43,21163,21164,21166,21168,21170,21173,21176,21179,21181,21184],{"class":45,"line":64},[43,21165,20306],{"class":49},[43,21167,905],{"class":81},[43,21169,20311],{"class":49},[43,21171,21172],{"class":100},"\"John\"",[43,21174,21175],{"class":49},", skills: [",[43,21177,21178],{"class":100},"\"JavaScript\"",[43,21180,104],{"class":49},[43,21182,21183],{"class":100},"\"Python\"",[43,21185,21186],{"class":49},"] },\n",[43,21188,21189,21191,21193,21195,21198,21200,21203,21205,21208],{"class":45,"line":119},[43,21190,20306],{"class":49},[43,21192,918],{"class":81},[43,21194,20311],{"class":49},[43,21196,21197],{"class":100},"\"Jane\"",[43,21199,21175],{"class":49},[43,21201,21202],{"class":100},"\"React\"",[43,21204,104],{"class":49},[43,21206,21207],{"class":100},"\"Node.js\"",[43,21209,21186],{"class":49},[43,21211,21212,21214,21216,21218,21221,21223,21225,21227,21230],{"class":45,"line":150},[43,21213,20306],{"class":49},[43,21215,931],{"class":81},[43,21217,20311],{"class":49},[43,21219,21220],{"class":100},"\"Jim\"",[43,21222,21175],{"class":49},[43,21224,21183],{"class":100},[43,21226,104],{"class":49},[43,21228,21229],{"class":100},"\"Django\"",[43,21231,21186],{"class":49},[43,21233,21234],{"class":45,"line":159},[43,21235,5441],{"class":49},[43,21237,21238],{"class":45,"line":176},[43,21239,1782],{"emptyLinePlaceholder":491},[43,21241,21242,21244,21247,21249],{"class":45,"line":1163},[43,21243,68],{"class":67},[43,21245,21246],{"class":81}," pythonDevs",[43,21248,128],{"class":67},[43,21250,21251],{"class":49}," employees\n",[43,21253,21254,21256,21258,21260,21263,21265,21267,21270,21273,21275,21277],{"class":45,"line":1178},[43,21255,5228],{"class":49},[43,21257,4881],{"class":57},[43,21259,4691],{"class":49},[43,21261,21262],{"class":74},"employee",[43,21264,2408],{"class":49},[43,21266,113],{"class":67},[43,21268,21269],{"class":49}," employee.skills.",[43,21271,21272],{"class":57},"includes",[43,21274,97],{"class":49},[43,21276,21183],{"class":100},[43,21278,4766],{"class":49},[43,21280,21281,21283,21285,21288,21290,21292,21294,21297,21299],{"class":45,"line":1184},[43,21282,5228],{"class":49},[43,21284,1123],{"class":57},[43,21286,21287],{"class":49},"(({ ",[43,21289,20283],{"class":74},[43,21291,104],{"class":49},[43,21293,21144],{"class":74},[43,21295,21296],{"class":49}," }) ",[43,21298,113],{"class":67},[43,21300,21301],{"class":49}," ({ name, skills }));\n",[43,21303,21304],{"class":45,"line":1190},[43,21305,1782],{"emptyLinePlaceholder":491},[43,21307,21308,21310,21312],{"class":45,"line":1996},[43,21309,6150],{"class":49},[43,21311,1931],{"class":57},[43,21313,21314],{"class":49},"(pythonDevs);\n",[43,21316,21317],{"class":45,"line":2008},[43,21318,20846],{"class":2797},[43,21320,21321],{"class":45,"line":2025},[43,21322,4624],{"class":2797},[43,21324,21325],{"class":45,"line":2031},[43,21326,21327],{"class":2797},"  { name: \"John\", skills: [\"JavaScript\", \"Python\"] },\n",[43,21329,21330],{"class":45,"line":2037},[43,21331,21332],{"class":2797},"  { name: \"Jim\", skills: [\"Python\", \"Django\"] }\n",[43,21334,21335],{"class":45,"line":2042},[43,21336,4664],{"class":2797},[43,21338,21339],{"class":45,"line":2516},[43,21340,20875],{"class":2797},[26,21342],{},[29,21344,21346],{"id":21345},"중복-제거","중복 제거",[15,21348,21349],{},"배열에서 중복된 값을 제거하세요.",[33,21351,21353],{"className":1024,"code":21352,"language":1026,"meta":38,"style":38},"const numbers = [1, 2, 2, 3, 4, 4, 5];\nconst uniqueNumbers = [...new Set(numbers)];\nconsole.log(uniqueNumbers); \u002F\u002F [1, 2, 3, 4, 5]\n",[40,21354,21355,21394,21414],{"__ignoreMap":38},[43,21356,21357,21359,21362,21364,21366,21368,21370,21372,21374,21376,21378,21380,21382,21384,21386,21388,21390,21392],{"class":45,"line":46},[43,21358,68],{"class":67},[43,21360,21361],{"class":81}," numbers",[43,21363,128],{"class":67},[43,21365,1169],{"class":49},[43,21367,905],{"class":81},[43,21369,104],{"class":49},[43,21371,918],{"class":81},[43,21373,104],{"class":49},[43,21375,918],{"class":81},[43,21377,104],{"class":49},[43,21379,931],{"class":81},[43,21381,104],{"class":49},[43,21383,950],{"class":81},[43,21385,104],{"class":49},[43,21387,950],{"class":81},[43,21389,104],{"class":49},[43,21391,13039],{"class":81},[43,21393,5441],{"class":49},[43,21395,21396,21398,21401,21403,21405,21408,21411],{"class":45,"line":64},[43,21397,68],{"class":67},[43,21399,21400],{"class":81}," uniqueNumbers",[43,21402,128],{"class":67},[43,21404,1169],{"class":49},[43,21406,21407],{"class":67},"...new",[43,21409,21410],{"class":57}," Set",[43,21412,21413],{"class":49},"(numbers)];\n",[43,21415,21416,21418,21420,21423],{"class":45,"line":119},[43,21417,6150],{"class":49},[43,21419,1931],{"class":57},[43,21421,21422],{"class":49},"(uniqueNumbers); ",[43,21424,21425],{"class":2797},"\u002F\u002F [1, 2, 3, 4, 5]\n",[29,21427,21429],{"id":21428},"중첩-배열-평탄화","중첩 배열 평탄화",[15,21431,21432],{},"배열의 중첩된 구조를 한 단계로 펼치세요.",[33,21434,21436],{"className":1024,"code":21435,"language":1026,"meta":38,"style":38},"const nestedArray = [1, [2, 3], [4, [5, 6]]];\nconst flatArray = nestedArray.flat(2);\nconsole.log(flatArray); \u002F\u002F [1, 2, 3, 4, 5, 6]\n",[40,21437,21438,21476,21497],{"__ignoreMap":38},[43,21439,21440,21442,21445,21447,21449,21451,21454,21456,21458,21460,21463,21465,21467,21469,21471,21473],{"class":45,"line":46},[43,21441,68],{"class":67},[43,21443,21444],{"class":81}," nestedArray",[43,21446,128],{"class":67},[43,21448,1169],{"class":49},[43,21450,905],{"class":81},[43,21452,21453],{"class":49},", [",[43,21455,918],{"class":81},[43,21457,104],{"class":49},[43,21459,931],{"class":81},[43,21461,21462],{"class":49},"], [",[43,21464,950],{"class":81},[43,21466,21453],{"class":49},[43,21468,13039],{"class":81},[43,21470,104],{"class":49},[43,21472,11736],{"class":81},[43,21474,21475],{"class":49},"]]];\n",[43,21477,21478,21480,21483,21485,21488,21491,21493,21495],{"class":45,"line":64},[43,21479,68],{"class":67},[43,21481,21482],{"class":81}," flatArray",[43,21484,128],{"class":67},[43,21486,21487],{"class":49}," nestedArray.",[43,21489,21490],{"class":57},"flat",[43,21492,97],{"class":49},[43,21494,918],{"class":81},[43,21496,1160],{"class":49},[43,21498,21499,21501,21503,21506],{"class":45,"line":119},[43,21500,6150],{"class":49},[43,21502,1931],{"class":57},[43,21504,21505],{"class":49},"(flatArray); ",[43,21507,21508],{"class":2797},"\u002F\u002F [1, 2, 3, 4, 5, 6]\n",[471,21510,21511],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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);}",{"title":38,"searchDepth":64,"depth":64,"links":21513},[21514,21515,21516,21517,21518,21519,21520],{"id":20261,"depth":64,"text":20262},{"id":20465,"depth":64,"text":20466},{"id":20618,"depth":64,"text":20619},{"id":20878,"depth":64,"text":20879},{"id":21129,"depth":64,"text":21130},{"id":21345,"depth":64,"text":21346},{"id":21428,"depth":64,"text":21429},"JavaScript 데이터 다루는 문제를 풀어보자",{"date":21523,"field":5954,"tags":21524},"2025.03.08",[1756],"\u002Fko\u002Fjs-data-01",{"title":20256,"description":21521},{"loc":21525},"ko\u002Fjs-data-01","mz3t4Znp1kEYvtIKJTyXc-fns42NqVmiYGasWdc2GUM",{"id":21531,"title":21532,"body":21533,"description":22331,"extension":484,"meta":22332,"navigation":491,"path":22334,"seo":22335,"sitemap":22336,"stem":22337,"__hash__":22338},"ko\u002Fko\u002Fjs-data-02.md","JavaScript 데이터02",{"type":8,"value":21534,"toc":22323},[21535,21539,21547,21944,21946,21981,21985,21996,22248,22251,22320],[29,21536,21538],{"id":21537},"문제-1-주문-데이터-필터링-및-총액-계산-중급","문제 1: 주문 데이터 필터링 및 총액 계산 (중급)",[185,21540,21541,21544],{},[188,21542,21543],{},"배송된(delivered) 또는 발송된(shipped) 주문만 필터링하세요.",[188,21545,21546],{},"각 주문의 총 주문 금액(totalAmount)을 계산하여 추가하세요.",[33,21548,21550],{"className":1024,"code":21549,"language":1026,"meta":38,"style":38},"const orders = [\n  {\n    orderId: \"ORD001\",\n    customerId: \"CUST1\",\n    items: [\n      { product: \"Laptop\", price: 1200, qty: 1 },\n      { product: \"Mouse\", price: 25, qty: 2 },\n    ],\n    status: \"shipped\",\n    timestamp: \"2023-10-01T10:00:00Z\",\n  },\n  {\n    orderId: \"ORD002\",\n    customerId: \"CUST2\",\n    items: [{ product: \"Phone\", price: 800, qty: 1 }],\n    status: \"pending\",\n    timestamp: \"2023-10-02T15:30:00Z\",\n  },\n  {\n    orderId: \"ORD003\",\n    customerId: \"CUST1\",\n    items: [\n      { product: \"Keyboard\", price: 50, qty: 3 },\n      { product: \"Monitor\", price: 300, qty: 1 },\n    ],\n    status: \"delivered\",\n    timestamp: \"2023-10-03T09:15:00Z\",\n  },\n];\n\nconst activeOrders = orders\n  .filter((order) => [\"delivered\", \"shipped\"].includes(order.status))\n  .map((order) => ({\n    ...order,\n    totalAmount: order.items.reduce(\n      (sum, item) => sum + item.price * item.qty,\n      0\n    ),\n  }));\n\nconsole.log(activeOrders);\n",[40,21551,21552,21562,21567,21577,21587,21592,21611,21628,21633,21643,21653,21657,21661,21670,21679,21698,21707,21716,21720,21724,21733,21741,21745,21762,21779,21783,21792,21801,21805,21809,21813,21825,21854,21870,21878,21887,21916,21921,21926,21931,21935],{"__ignoreMap":38},[43,21553,21554,21556,21558,21560],{"class":45,"line":46},[43,21555,68],{"class":67},[43,21557,20491],{"class":81},[43,21559,128],{"class":67},[43,21561,20301],{"class":49},[43,21563,21564],{"class":45,"line":64},[43,21565,21566],{"class":49},"  {\n",[43,21568,21569,21572,21575],{"class":45,"line":119},[43,21570,21571],{"class":49},"    orderId: ",[43,21573,21574],{"class":100},"\"ORD001\"",[43,21576,1056],{"class":49},[43,21578,21579,21582,21585],{"class":45,"line":150},[43,21580,21581],{"class":49},"    customerId: ",[43,21583,21584],{"class":100},"\"CUST1\"",[43,21586,1056],{"class":49},[43,21588,21589],{"class":45,"line":159},[43,21590,21591],{"class":49},"    items: [\n",[43,21593,21594,21597,21599,21601,21604,21607,21609],{"class":45,"line":176},[43,21595,21596],{"class":49},"      { product: ",[43,21598,20663],{"class":100},[43,21600,20672],{"class":49},[43,21602,21603],{"class":81},"1200",[43,21605,21606],{"class":49},", qty: ",[43,21608,905],{"class":81},[43,21610,5178],{"class":49},[43,21612,21613,21615,21618,21620,21622,21624,21626],{"class":45,"line":1163},[43,21614,21596],{"class":49},[43,21616,21617],{"class":100},"\"Mouse\"",[43,21619,20672],{"class":49},[43,21621,20319],{"class":81},[43,21623,21606],{"class":49},[43,21625,918],{"class":81},[43,21627,5178],{"class":49},[43,21629,21630],{"class":45,"line":1178},[43,21631,21632],{"class":49},"    ],\n",[43,21634,21635,21638,21641],{"class":45,"line":1184},[43,21636,21637],{"class":49},"    status: ",[43,21639,21640],{"class":100},"\"shipped\"",[43,21642,1056],{"class":49},[43,21644,21645,21648,21651],{"class":45,"line":1190},[43,21646,21647],{"class":49},"    timestamp: ",[43,21649,21650],{"class":100},"\"2023-10-01T10:00:00Z\"",[43,21652,1056],{"class":49},[43,21654,21655],{"class":45,"line":1996},[43,21656,1187],{"class":49},[43,21658,21659],{"class":45,"line":2008},[43,21660,21566],{"class":49},[43,21662,21663,21665,21668],{"class":45,"line":2025},[43,21664,21571],{"class":49},[43,21666,21667],{"class":100},"\"ORD002\"",[43,21669,1056],{"class":49},[43,21671,21672,21674,21677],{"class":45,"line":2031},[43,21673,21581],{"class":49},[43,21675,21676],{"class":100},"\"CUST2\"",[43,21678,1056],{"class":49},[43,21680,21681,21684,21686,21688,21691,21693,21695],{"class":45,"line":2037},[43,21682,21683],{"class":49},"    items: [{ product: ",[43,21685,20701],{"class":100},[43,21687,20672],{"class":49},[43,21689,21690],{"class":81},"800",[43,21692,21606],{"class":49},[43,21694,905],{"class":81},[43,21696,21697],{"class":49}," }],\n",[43,21699,21700,21702,21705],{"class":45,"line":2042},[43,21701,21637],{"class":49},[43,21703,21704],{"class":100},"\"pending\"",[43,21706,1056],{"class":49},[43,21708,21709,21711,21714],{"class":45,"line":2516},[43,21710,21647],{"class":49},[43,21712,21713],{"class":100},"\"2023-10-02T15:30:00Z\"",[43,21715,1056],{"class":49},[43,21717,21718],{"class":45,"line":2998},[43,21719,1187],{"class":49},[43,21721,21722],{"class":45,"line":3003},[43,21723,21566],{"class":49},[43,21725,21726,21728,21731],{"class":45,"line":3009},[43,21727,21571],{"class":49},[43,21729,21730],{"class":100},"\"ORD003\"",[43,21732,1056],{"class":49},[43,21734,21735,21737,21739],{"class":45,"line":3022},[43,21736,21581],{"class":49},[43,21738,21584],{"class":100},[43,21740,1056],{"class":49},[43,21742,21743],{"class":45,"line":3044},[43,21744,21591],{"class":49},[43,21746,21747,21749,21752,21754,21756,21758,21760],{"class":45,"line":3058},[43,21748,21596],{"class":49},[43,21750,21751],{"class":100},"\"Keyboard\"",[43,21753,20672],{"class":49},[43,21755,12222],{"class":81},[43,21757,21606],{"class":49},[43,21759,931],{"class":81},[43,21761,5178],{"class":49},[43,21763,21764,21766,21769,21771,21773,21775,21777],{"class":45,"line":3077},[43,21765,21596],{"class":49},[43,21767,21768],{"class":100},"\"Monitor\"",[43,21770,20672],{"class":49},[43,21772,15366],{"class":81},[43,21774,21606],{"class":49},[43,21776,905],{"class":81},[43,21778,5178],{"class":49},[43,21780,21781],{"class":45,"line":3082},[43,21782,21632],{"class":49},[43,21784,21785,21787,21790],{"class":45,"line":3088},[43,21786,21637],{"class":49},[43,21788,21789],{"class":100},"\"delivered\"",[43,21791,1056],{"class":49},[43,21793,21794,21796,21799],{"class":45,"line":3093},[43,21795,21647],{"class":49},[43,21797,21798],{"class":100},"\"2023-10-03T09:15:00Z\"",[43,21800,1056],{"class":49},[43,21802,21803],{"class":45,"line":3099},[43,21804,1187],{"class":49},[43,21806,21807],{"class":45,"line":3145},[43,21808,5441],{"class":49},[43,21810,21811],{"class":45,"line":3152},[43,21812,1782],{"emptyLinePlaceholder":491},[43,21814,21815,21817,21820,21822],{"class":45,"line":3165},[43,21816,68],{"class":67},[43,21818,21819],{"class":81}," activeOrders",[43,21821,128],{"class":67},[43,21823,21824],{"class":49}," orders\n",[43,21826,21827,21829,21831,21833,21835,21837,21839,21841,21843,21845,21847,21849,21851],{"class":45,"line":3203},[43,21828,5228],{"class":49},[43,21830,4881],{"class":57},[43,21832,4691],{"class":49},[43,21834,20582],{"class":74},[43,21836,2408],{"class":49},[43,21838,113],{"class":67},[43,21840,1169],{"class":49},[43,21842,21789],{"class":100},[43,21844,104],{"class":49},[43,21846,21640],{"class":100},[43,21848,8466],{"class":49},[43,21850,21272],{"class":57},[43,21852,21853],{"class":49},"(order.status))\n",[43,21855,21856,21858,21860,21862,21864,21866,21868],{"class":45,"line":3208},[43,21857,5228],{"class":49},[43,21859,1123],{"class":57},[43,21861,4691],{"class":49},[43,21863,20582],{"class":74},[43,21865,2408],{"class":49},[43,21867,113],{"class":67},[43,21869,14264],{"class":49},[43,21871,21872,21875],{"class":45,"line":3219},[43,21873,21874],{"class":67},"    ...",[43,21876,21877],{"class":49},"order,\n",[43,21879,21880,21883,21885],{"class":45,"line":3227},[43,21881,21882],{"class":49},"    totalAmount: order.items.",[43,21884,5113],{"class":57},[43,21886,2698],{"class":49},[43,21888,21889,21892,21895,21897,21899,21901,21903,21906,21908,21911,21913],{"class":45,"line":3233},[43,21890,21891],{"class":49},"      (",[43,21893,21894],{"class":74},"sum",[43,21896,104],{"class":49},[43,21898,4694],{"class":74},[43,21900,2408],{"class":49},[43,21902,113],{"class":67},[43,21904,21905],{"class":49}," sum ",[43,21907,20592],{"class":67},[43,21909,21910],{"class":49}," item.price ",[43,21912,4726],{"class":67},[43,21914,21915],{"class":49}," item.qty,\n",[43,21917,21918],{"class":45,"line":3239},[43,21919,21920],{"class":81},"      0\n",[43,21922,21923],{"class":45,"line":3251},[43,21924,21925],{"class":49},"    ),\n",[43,21927,21928],{"class":45,"line":3256},[43,21929,21930],{"class":49},"  }));\n",[43,21932,21933],{"class":45,"line":3261},[43,21934,1782],{"emptyLinePlaceholder":491},[43,21936,21937,21939,21941],{"class":45,"line":3267},[43,21938,6150],{"class":49},[43,21940,1931],{"class":57},[43,21942,21943],{"class":49},"(activeOrders);\n",[1273,21945,5673],{"id":5673},[432,21947,21948,21961,21971],{},[188,21949,21950,21953,21954,5607,21957,21960],{},[40,21951,21952],{},"filter()","를 사용해 상태가 ",[40,21955,21956],{},"delivered",[40,21958,21959],{},"shipped","인 주문만 남깁니다.",[188,21962,21963,21966,21967,21970],{},[40,21964,21965],{},"map()","을 이용해 각 주문에 새로운 속성 ",[40,21968,21969],{},"totalAmount","를 추가합니다.",[188,21972,21973,21976,21977,21980],{},[40,21974,21975],{},"reduce()","를 활용해 ",[40,21978,21979],{},"items"," 배열 내 제품 가격과 수량을 곱한 후 합산합니다.",[29,21982,21984],{"id":21983},"문제-2-고객별-주문-요약-고급","문제 2: 고객별 주문 요약 (고급)",[185,21986,21987,21990,21993],{},[188,21988,21989],{},"각 고객(customerId)별로 주문 내역을 그룹화하세요.",[188,21991,21992],{},"각 고객의 총 주문 금액을 계산하세요.",[188,21994,21995],{},"최신 주문 날짜를 추적하세요.",[33,21997,21999],{"className":1024,"code":21998,"language":1026,"meta":38,"style":38},"const result = activeOrders.reduce((acc, order) => {\n  const { customerId } = order;\n\n  if (!acc[customerId]) {\n    acc[customerId] = {\n      orders: [],\n      totalAmount: 0,\n      latestOrderDate: null,\n    };\n  }\n\n  \u002F\u002F 최신 주문 날짜 비교\n  const currentLatest = acc[customerId].latestOrderDate\n    ? new Date(acc[customerId].latestOrderDate)\n    : null;\n\n  const orderDate = new Date(order.timestamp);\n\n  if (!currentLatest || orderDate > currentLatest) {\n    acc[customerId].latestOrderDate = order.timestamp;\n  }\n\n  acc[customerId].orders.push(order);\n  acc[customerId].totalAmount += order.totalAmount;\n  return acc;\n}, {});\n\nconsole.log(result);\n",[40,22000,22001,22028,22044,22048,22059,22068,22073,22082,22091,22095,22099,22103,22108,22120,22132,22141,22145,22161,22165,22186,22196,22200,22204,22214,22225,22231,22235,22239],{"__ignoreMap":38},[43,22002,22003,22005,22007,22009,22012,22014,22016,22018,22020,22022,22024,22026],{"class":45,"line":46},[43,22004,68],{"class":67},[43,22006,125],{"class":81},[43,22008,128],{"class":67},[43,22010,22011],{"class":49}," activeOrders.",[43,22013,5113],{"class":57},[43,22015,4691],{"class":49},[43,22017,5118],{"class":74},[43,22019,104],{"class":49},[43,22021,20582],{"class":74},[43,22023,2408],{"class":49},[43,22025,113],{"class":67},[43,22027,116],{"class":49},[43,22029,22030,22032,22034,22037,22039,22041],{"class":45,"line":64},[43,22031,122],{"class":67},[43,22033,71],{"class":49},[43,22035,22036],{"class":81},"customerId",[43,22038,85],{"class":49},[43,22040,88],{"class":67},[43,22042,22043],{"class":49}," order;\n",[43,22045,22046],{"class":45,"line":119},[43,22047,1782],{"emptyLinePlaceholder":491},[43,22049,22050,22052,22054,22056],{"class":45,"line":150},[43,22051,3047],{"class":67},[43,22053,1069],{"class":49},[43,22055,3052],{"class":67},[43,22057,22058],{"class":49},"acc[customerId]) {\n",[43,22060,22061,22064,22066],{"class":45,"line":159},[43,22062,22063],{"class":49},"    acc[customerId] ",[43,22065,88],{"class":67},[43,22067,116],{"class":49},[43,22069,22070],{"class":45,"line":176},[43,22071,22072],{"class":49},"      orders: [],\n",[43,22074,22075,22078,22080],{"class":45,"line":1163},[43,22076,22077],{"class":49},"      totalAmount: ",[43,22079,4401],{"class":81},[43,22081,1056],{"class":49},[43,22083,22084,22087,22089],{"class":45,"line":1178},[43,22085,22086],{"class":49},"      latestOrderDate: ",[43,22088,9230],{"class":81},[43,22090,1056],{"class":49},[43,22092,22093],{"class":45,"line":1184},[43,22094,2118],{"class":49},[43,22096,22097],{"class":45,"line":1190},[43,22098,2028],{"class":49},[43,22100,22101],{"class":45,"line":1996},[43,22102,1782],{"emptyLinePlaceholder":491},[43,22104,22105],{"class":45,"line":2008},[43,22106,22107],{"class":2797},"  \u002F\u002F 최신 주문 날짜 비교\n",[43,22109,22110,22112,22115,22117],{"class":45,"line":2025},[43,22111,122],{"class":67},[43,22113,22114],{"class":81}," currentLatest",[43,22116,128],{"class":67},[43,22118,22119],{"class":49}," acc[customerId].latestOrderDate\n",[43,22121,22122,22125,22127,22129],{"class":45,"line":2031},[43,22123,22124],{"class":67},"    ?",[43,22126,1794],{"class":67},[43,22128,18504],{"class":57},[43,22130,22131],{"class":49},"(acc[customerId].latestOrderDate)\n",[43,22133,22134,22137,22139],{"class":45,"line":2037},[43,22135,22136],{"class":67},"    :",[43,22138,9225],{"class":81},[43,22140,2995],{"class":49},[43,22142,22143],{"class":45,"line":2042},[43,22144,1782],{"emptyLinePlaceholder":491},[43,22146,22147,22149,22152,22154,22156,22158],{"class":45,"line":2516},[43,22148,122],{"class":67},[43,22150,22151],{"class":81}," orderDate",[43,22153,128],{"class":67},[43,22155,1794],{"class":67},[43,22157,18504],{"class":57},[43,22159,22160],{"class":49},"(order.timestamp);\n",[43,22162,22163],{"class":45,"line":2998},[43,22164,1782],{"emptyLinePlaceholder":491},[43,22166,22167,22169,22171,22173,22176,22178,22181,22183],{"class":45,"line":3003},[43,22168,3047],{"class":67},[43,22170,1069],{"class":49},[43,22172,3052],{"class":67},[43,22174,22175],{"class":49},"currentLatest ",[43,22177,9342],{"class":67},[43,22179,22180],{"class":49}," orderDate ",[43,22182,19390],{"class":67},[43,22184,22185],{"class":49}," currentLatest) {\n",[43,22187,22188,22191,22193],{"class":45,"line":3009},[43,22189,22190],{"class":49},"    acc[customerId].latestOrderDate ",[43,22192,88],{"class":67},[43,22194,22195],{"class":49}," order.timestamp;\n",[43,22197,22198],{"class":45,"line":3022},[43,22199,2028],{"class":49},[43,22201,22202],{"class":45,"line":3044},[43,22203,1782],{"emptyLinePlaceholder":491},[43,22205,22206,22209,22211],{"class":45,"line":3058},[43,22207,22208],{"class":49},"  acc[customerId].orders.",[43,22210,20813],{"class":57},[43,22212,22213],{"class":49},"(order);\n",[43,22215,22216,22219,22222],{"class":45,"line":3077},[43,22217,22218],{"class":49},"  acc[customerId].totalAmount ",[43,22220,22221],{"class":67},"+=",[43,22223,22224],{"class":49}," order.totalAmount;\n",[43,22226,22227,22229],{"class":45,"line":3082},[43,22228,153],{"class":67},[43,22230,20823],{"class":49},[43,22232,22233],{"class":45,"line":3088},[43,22234,20828],{"class":49},[43,22236,22237],{"class":45,"line":3093},[43,22238,1782],{"emptyLinePlaceholder":491},[43,22240,22241,22243,22245],{"class":45,"line":3099},[43,22242,6150],{"class":49},[43,22244,1931],{"class":57},[43,22246,22247],{"class":49},"(result);\n",[1273,22249,5673],{"id":22250},"설명-1",[432,22252,22253,22261,22267,22273],{},[188,22254,22255,22257,22258,22260],{},[40,22256,21975],{},"를 이용해 고객 ID(",[40,22259,22036],{},")를 키로 하는 객체를 생성합니다.",[188,22262,22263,22266],{},[40,22264,22265],{},"if (!acc[customerId])","를 통해 해당 고객 ID가 처음 등장하는 경우 기본 구조를 초기화합니다.",[188,22268,22269,22270,22272],{},"주문별 총 금액을 누적(",[40,22271,21969],{},")하여 관리합니다.",[188,22274,22275,22278,22279,22288,22289],{},[40,22276,22277],{},"latestOrderDate","를 갱신할 때, ",[21,22280,22281,22284,22285,22287],{},[40,22282,22283],{},"timestamp"," 문자열을 ",[40,22286,3178],{}," 객체로 변환 후 비교","합니다.\n",[185,22290,22291,22300,22307],{},[188,22292,22293,22296,22297,22299],{},[40,22294,22295],{},"new Date(order.timestamp)","을 사용하면 ISO 8601 문자열을 ",[40,22298,3178],{}," 객체로 변환할 수 있습니다.",[188,22301,22302,22303,22306],{},"날짜 비교 시 ",[40,22304,22305],{},"orderDate > currentLatest","를 이용하면 최신 날짜를 업데이트할 수 있습니다.",[188,22308,22309,22311,22312,22315,22316,22319],{},[40,22310,3178],{}," 객체끼리 비교할 때는 내부적으로 ",[40,22313,22314],{},"getTime()"," 값(밀리초 단위 숫자)을 비교하므로 ",[40,22317,22318],{},"date1 > date2","처럼 직접 비교 가능합니다.",[471,22321,22322],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":38,"searchDepth":64,"depth":64,"links":22324},[22325,22328],{"id":21537,"depth":64,"text":21538,"children":22326},[22327],{"id":5673,"depth":119,"text":5673},{"id":21983,"depth":64,"text":21984,"children":22329},[22330],{"id":22250,"depth":119,"text":5673},"복잡한 데이터 구조를 다루는 실전 문제를 해결해보자.",{"date":21523,"field":5954,"tags":22333},[1756],"\u002Fko\u002Fjs-data-02",{"title":21532,"description":22331},{"loc":22334},"ko\u002Fjs-data-02","59yrxOMS0L1kIhWqNFjoDm0-NoSBz8UczPee4ZJipGY",{"id":22340,"title":22341,"body":22342,"description":23064,"extension":484,"meta":23065,"navigation":491,"path":23070,"seo":23071,"sitemap":23072,"stem":23073,"__hash__":23074},"ko\u002Fko\u002Fnuxt3-unit-test.md","Nuxt 프로젝트에 유닛 테스트 코드 추가하기",{"type":8,"value":22343,"toc":23055},[22344,22348,22352,22378,22381,22516,22519,22547,22550,22588,22592,22605,22609,22684,22688,22691,23029,23033,23052],[29,22345,22347],{"id":22346},"테스트-환경-설정","테스트 환경 설정",[1273,22349,22351],{"id":22350},"의존성-설치","의존성 설치",[33,22353,22355],{"className":850,"code":22354,"language":852,"meta":38,"style":38},"npm install --save-dev vitest @nuxt\u002Ftest-utils @vue\u002Ftest-utils jsdom\n",[40,22356,22357],{"__ignoreMap":38},[43,22358,22359,22361,22363,22366,22369,22372,22375],{"class":45,"line":46},[43,22360,1741],{"class":57},[43,22362,1744],{"class":100},[43,22364,22365],{"class":81}," --save-dev",[43,22367,22368],{"class":100}," vitest",[43,22370,22371],{"class":100}," @nuxt\u002Ftest-utils",[43,22373,22374],{"class":100}," @vue\u002Ftest-utils",[43,22376,22377],{"class":100}," jsdom\n",[15,22379,22380],{},"vitest.config.js 설정",[33,22382,22384],{"className":1024,"code":22383,"language":1026,"meta":38,"style":38},"\u002F\u002F vitest.config.js\nimport { defineConfig } from 'vitest\u002Fconfig'\nimport { fileURLToPath } from 'url'\n\nexport default defineConfig({\n  test: {\n    environment: 'jsdom',\n    globals: true,\n  },\n  resolve: {\n    alias: {\n      '~': fileURLToPath(new URL('.\u002F', import.meta.url)), \u002F\u002F 루트 경로\n    },\n  },\n})\n",[40,22385,22386,22391,22402,22414,22418,22428,22433,22443,22452,22456,22461,22466,22504,22508,22512],{"__ignoreMap":38},[43,22387,22388],{"class":45,"line":46},[43,22389,22390],{"class":2797},"\u002F\u002F vitest.config.js\n",[43,22392,22393,22395,22397,22399],{"class":45,"line":64},[43,22394,7079],{"class":67},[43,22396,10206],{"class":49},[43,22398,7085],{"class":67},[43,22400,22401],{"class":100}," 'vitest\u002Fconfig'\n",[43,22403,22404,22406,22409,22411],{"class":45,"line":119},[43,22405,7079],{"class":67},[43,22407,22408],{"class":49}," { fileURLToPath } ",[43,22410,7085],{"class":67},[43,22412,22413],{"class":100}," 'url'\n",[43,22415,22416],{"class":45,"line":150},[43,22417,1782],{"emptyLinePlaceholder":491},[43,22419,22420,22422,22424,22426],{"class":45,"line":159},[43,22421,6052],{"class":67},[43,22423,6055],{"class":67},[43,22425,10240],{"class":57},[43,22427,1803],{"class":49},[43,22429,22430],{"class":45,"line":176},[43,22431,22432],{"class":49},"  test: {\n",[43,22434,22435,22438,22441],{"class":45,"line":1163},[43,22436,22437],{"class":49},"    environment: ",[43,22439,22440],{"class":100},"'jsdom'",[43,22442,1056],{"class":49},[43,22444,22445,22448,22450],{"class":45,"line":1178},[43,22446,22447],{"class":49},"    globals: ",[43,22449,1053],{"class":81},[43,22451,1056],{"class":49},[43,22453,22454],{"class":45,"line":1184},[43,22455,1187],{"class":49},[43,22457,22458],{"class":45,"line":1190},[43,22459,22460],{"class":49},"  resolve: {\n",[43,22462,22463],{"class":45,"line":1996},[43,22464,22465],{"class":49},"    alias: {\n",[43,22467,22468,22471,22473,22476,22478,22481,22484,22486,22489,22491,22493,22495,22498,22501],{"class":45,"line":2008},[43,22469,22470],{"class":100},"      '~'",[43,22472,78],{"class":49},[43,22474,22475],{"class":57},"fileURLToPath",[43,22477,97],{"class":49},[43,22479,22480],{"class":67},"new",[43,22482,22483],{"class":57}," URL",[43,22485,97],{"class":49},[43,22487,22488],{"class":100},"'.\u002F'",[43,22490,104],{"class":49},[43,22492,7079],{"class":67},[43,22494,1139],{"class":49},[43,22496,22497],{"class":81},"meta",[43,22499,22500],{"class":49},".url)), ",[43,22502,22503],{"class":2797},"\u002F\u002F 루트 경로\n",[43,22505,22506],{"class":45,"line":2025},[43,22507,6095],{"class":49},[43,22509,22510],{"class":45,"line":2031},[43,22511,1187],{"class":49},[43,22513,22514],{"class":45,"line":2037},[43,22515,6117],{"class":49},[15,22517,22518],{},"nuxt.config.ts 수정",[33,22520,22522],{"className":1024,"code":22521,"language":1026,"meta":38,"style":38},"export default defineNuxtConfig({\n  modules: [\"@nuxt\u002Ftest-utils\u002Fmodule\"],\n});\n",[40,22523,22524,22534,22543],{"__ignoreMap":38},[43,22525,22526,22528,22530,22532],{"class":45,"line":46},[43,22527,6052],{"class":67},[43,22529,6055],{"class":67},[43,22531,6058],{"class":57},[43,22533,1803],{"class":49},[43,22535,22536,22538,22541],{"class":45,"line":64},[43,22537,13843],{"class":49},[43,22539,22540],{"class":100},"\"@nuxt\u002Ftest-utils\u002Fmodule\"",[43,22542,8282],{"class":49},[43,22544,22545],{"class":45,"line":119},[43,22546,1855],{"class":49},[15,22548,22549],{},"테스트 실행 명령어 추가 (package.json)",[33,22551,22553],{"className":556,"code":22552,"language":558,"meta":38,"style":38},"\"scripts\": {\n  \"test\": \"vitest run\",\n  \"test:watch\": \"vitest watch\"\n}\n",[40,22554,22555,22562,22574,22584],{"__ignoreMap":38},[43,22556,22557,22560],{"class":45,"line":46},[43,22558,22559],{"class":100},"\"scripts\"",[43,22561,1036],{"class":49},[43,22563,22564,22567,22569,22572],{"class":45,"line":64},[43,22565,22566],{"class":81},"  \"test\"",[43,22568,78],{"class":49},[43,22570,22571],{"class":100},"\"vitest run\"",[43,22573,1056],{"class":49},[43,22575,22576,22579,22581],{"class":45,"line":119},[43,22577,22578],{"class":81},"  \"test:watch\"",[43,22580,78],{"class":49},[43,22582,22583],{"class":100},"\"vitest watch\"\n",[43,22585,22586],{"class":45,"line":150},[43,22587,2034],{"class":49},[29,22589,22591],{"id":22590},"테스트-파일-위치","테스트 파일 위치",[185,22593,22594],{},[188,22595,22596,22597],{},"위치: ~\u002Ftests\u002Futils\u002Ffilter.test.js",[185,22598,22599,22602],{},[188,22600,22601],{},"Nuxt에서 테스트 파일은 일반적으로 tests\u002F 디렉토리에 저장.",[188,22603,22604],{},"utils\u002F 하위에 있는 파일을 테스트하므로 tests\u002Futils\u002F로 구조화.",[29,22606,22608],{"id":22607},"파일-구조-예시","파일 구조 예시",[33,22610,22612],{"className":850,"code":22611,"language":852,"meta":38,"style":38},"project-root\u002F\n  ├── utils\u002F\n  │   └── filter.js\n  ├── tests\u002F\n  │   └── utils\u002F\n  │       └── filter.test.js\n  ├── nuxt.config.ts\n  ├── vitest.config.js\n  ├── package.json\n",[40,22613,22614,22619,22627,22638,22645,22653,22663,22670,22677],{"__ignoreMap":38},[43,22615,22616],{"class":45,"line":46},[43,22617,22618],{"class":57},"project-root\u002F\n",[43,22620,22621,22624],{"class":45,"line":64},[43,22622,22623],{"class":57},"  ├──",[43,22625,22626],{"class":100}," utils\u002F\n",[43,22628,22629,22632,22635],{"class":45,"line":119},[43,22630,22631],{"class":57},"  │",[43,22633,22634],{"class":100},"   └──",[43,22636,22637],{"class":100}," filter.js\n",[43,22639,22640,22642],{"class":45,"line":150},[43,22641,22623],{"class":57},[43,22643,22644],{"class":100}," tests\u002F\n",[43,22646,22647,22649,22651],{"class":45,"line":159},[43,22648,22631],{"class":57},[43,22650,22634],{"class":100},[43,22652,22626],{"class":100},[43,22654,22655,22657,22660],{"class":45,"line":176},[43,22656,22631],{"class":57},[43,22658,22659],{"class":100},"       └──",[43,22661,22662],{"class":100}," filter.test.js\n",[43,22664,22665,22667],{"class":45,"line":1163},[43,22666,22623],{"class":57},[43,22668,22669],{"class":100}," nuxt.config.ts\n",[43,22671,22672,22674],{"class":45,"line":1178},[43,22673,22623],{"class":57},[43,22675,22676],{"class":100}," vitest.config.js\n",[43,22678,22679,22681],{"class":45,"line":1184},[43,22680,22623],{"class":57},[43,22682,22683],{"class":100}," package.json\n",[29,22685,22687],{"id":22686},"테스트-코드-작성-testsutilsfiltertestjs","테스트 코드 작성 (tests\u002Futils\u002Ffilter.test.js)",[15,22689,22690],{},"아래는 getValue, matchesQuery, filterItems 함수를 테스트하는 코드입니다. 각 함수의 동작과 엣지 케이스를 검증합니다.",[33,22692,22694],{"className":1024,"code":22693,"language":1026,"meta":38,"style":38},"import { describe, it, expect } from \"vitest\";\nimport { getValue, matchesQuery, filterItems } from \"~\u002Futils\u002Ffilter\";\n\ndescribe(\"utils\u002Ffilter.js\", () => {\n  \u002F\u002F getValue 테스트\n  describe(\"getValue\", () => {\n    it(\"단순 속성을 추출한다\", () => {\n      const item = { title: \"Hello World\" };\n      expect(getValue(item, \"title\")).toBe(\"Hello World\");\n    });\n\n    it(\"중첩 속성을 추출한다\", () => {\n      const item = { meta: { tags: [\"test\"] } };\n      expect(getValue(item, \"meta.tags\")).toEqual([\"test\"]);\n    });\n\n    it(\"존재하지 않는 경로는 undefined를 반환한다\", () => {\n      const item = { title: \"Hello\" };\n      expect(getValue(item, \"meta.tags\")).toBeUndefined();\n    });\n\n    it(\"null 또는 undefined 객체는 undefined를 반환한다\", () => {\n      expect(getValue(null, \"title\")).toBeUndefined();\n      expect(getValue(undefined, \"meta.tags\")).toBeUndefined();\n    });\n  });\n});\n",[40,22695,22696,22710,22724,22728,22744,22749,22765,22781,22799,22826,22830,22834,22849,22866,22892,22896,22900,22915,22930,22949,22953,22957,22972,22994,23017,23021,23025],{"__ignoreMap":38},[43,22697,22698,22700,22703,22705,22708],{"class":45,"line":46},[43,22699,7079],{"class":67},[43,22701,22702],{"class":49}," { describe, it, expect } ",[43,22704,7085],{"class":67},[43,22706,22707],{"class":100}," \"vitest\"",[43,22709,2995],{"class":49},[43,22711,22712,22714,22717,22719,22722],{"class":45,"line":64},[43,22713,7079],{"class":67},[43,22715,22716],{"class":49}," { getValue, matchesQuery, filterItems } ",[43,22718,7085],{"class":67},[43,22720,22721],{"class":100}," \"~\u002Futils\u002Ffilter\"",[43,22723,2995],{"class":49},[43,22725,22726],{"class":45,"line":119},[43,22727,1782],{"emptyLinePlaceholder":491},[43,22729,22730,22733,22735,22738,22740,22742],{"class":45,"line":150},[43,22731,22732],{"class":57},"describe",[43,22734,97],{"class":49},[43,22736,22737],{"class":100},"\"utils\u002Ffilter.js\"",[43,22739,3427],{"class":49},[43,22741,113],{"class":67},[43,22743,116],{"class":49},[43,22745,22746],{"class":45,"line":159},[43,22747,22748],{"class":2797},"  \u002F\u002F getValue 테스트\n",[43,22750,22751,22754,22756,22759,22761,22763],{"class":45,"line":176},[43,22752,22753],{"class":57},"  describe",[43,22755,97],{"class":49},[43,22757,22758],{"class":100},"\"getValue\"",[43,22760,3427],{"class":49},[43,22762,113],{"class":67},[43,22764,116],{"class":49},[43,22766,22767,22770,22772,22775,22777,22779],{"class":45,"line":1163},[43,22768,22769],{"class":57},"    it",[43,22771,97],{"class":49},[43,22773,22774],{"class":100},"\"단순 속성을 추출한다\"",[43,22776,3427],{"class":49},[43,22778,113],{"class":67},[43,22780,116],{"class":49},[43,22782,22783,22785,22788,22790,22793,22796],{"class":45,"line":1178},[43,22784,1082],{"class":67},[43,22786,22787],{"class":81}," item",[43,22789,128],{"class":67},[43,22791,22792],{"class":49}," { title: ",[43,22794,22795],{"class":100},"\"Hello World\"",[43,22797,22798],{"class":49}," };\n",[43,22800,22801,22804,22806,22809,22812,22814,22817,22820,22822,22824],{"class":45,"line":1184},[43,22802,22803],{"class":57},"      expect",[43,22805,97],{"class":49},[43,22807,22808],{"class":57},"getValue",[43,22810,22811],{"class":49},"(item, ",[43,22813,7447],{"class":100},[43,22815,22816],{"class":49},")).",[43,22818,22819],{"class":57},"toBe",[43,22821,97],{"class":49},[43,22823,22795],{"class":100},[43,22825,1160],{"class":49},[43,22827,22828],{"class":45,"line":1190},[43,22829,2437],{"class":49},[43,22831,22832],{"class":45,"line":1996},[43,22833,1782],{"emptyLinePlaceholder":491},[43,22835,22836,22838,22840,22843,22845,22847],{"class":45,"line":2008},[43,22837,22769],{"class":57},[43,22839,97],{"class":49},[43,22841,22842],{"class":100},"\"중첩 속성을 추출한다\"",[43,22844,3427],{"class":49},[43,22846,113],{"class":67},[43,22848,116],{"class":49},[43,22850,22851,22853,22855,22857,22860,22863],{"class":45,"line":2025},[43,22852,1082],{"class":67},[43,22854,22787],{"class":81},[43,22856,128],{"class":67},[43,22858,22859],{"class":49}," { meta: { tags: [",[43,22861,22862],{"class":100},"\"test\"",[43,22864,22865],{"class":49},"] } };\n",[43,22867,22868,22870,22872,22874,22876,22879,22881,22884,22887,22889],{"class":45,"line":2031},[43,22869,22803],{"class":57},[43,22871,97],{"class":49},[43,22873,22808],{"class":57},[43,22875,22811],{"class":49},[43,22877,22878],{"class":100},"\"meta.tags\"",[43,22880,22816],{"class":49},[43,22882,22883],{"class":57},"toEqual",[43,22885,22886],{"class":49},"([",[43,22888,22862],{"class":100},[43,22890,22891],{"class":49},"]);\n",[43,22893,22894],{"class":45,"line":2037},[43,22895,2437],{"class":49},[43,22897,22898],{"class":45,"line":2042},[43,22899,1782],{"emptyLinePlaceholder":491},[43,22901,22902,22904,22906,22909,22911,22913],{"class":45,"line":2516},[43,22903,22769],{"class":57},[43,22905,97],{"class":49},[43,22907,22908],{"class":100},"\"존재하지 않는 경로는 undefined를 반환한다\"",[43,22910,3427],{"class":49},[43,22912,113],{"class":67},[43,22914,116],{"class":49},[43,22916,22917,22919,22921,22923,22925,22928],{"class":45,"line":2998},[43,22918,1082],{"class":67},[43,22920,22787],{"class":81},[43,22922,128],{"class":67},[43,22924,22792],{"class":49},[43,22926,22927],{"class":100},"\"Hello\"",[43,22929,22798],{"class":49},[43,22931,22932,22934,22936,22938,22940,22942,22944,22947],{"class":45,"line":3003},[43,22933,22803],{"class":57},[43,22935,97],{"class":49},[43,22937,22808],{"class":57},[43,22939,22811],{"class":49},[43,22941,22878],{"class":100},[43,22943,22816],{"class":49},[43,22945,22946],{"class":57},"toBeUndefined",[43,22948,147],{"class":49},[43,22950,22951],{"class":45,"line":3009},[43,22952,2437],{"class":49},[43,22954,22955],{"class":45,"line":3022},[43,22956,1782],{"emptyLinePlaceholder":491},[43,22958,22959,22961,22963,22966,22968,22970],{"class":45,"line":3044},[43,22960,22769],{"class":57},[43,22962,97],{"class":49},[43,22964,22965],{"class":100},"\"null 또는 undefined 객체는 undefined를 반환한다\"",[43,22967,3427],{"class":49},[43,22969,113],{"class":67},[43,22971,116],{"class":49},[43,22973,22974,22976,22978,22980,22982,22984,22986,22988,22990,22992],{"class":45,"line":3058},[43,22975,22803],{"class":57},[43,22977,97],{"class":49},[43,22979,22808],{"class":57},[43,22981,97],{"class":49},[43,22983,9230],{"class":81},[43,22985,104],{"class":49},[43,22987,7447],{"class":100},[43,22989,22816],{"class":49},[43,22991,22946],{"class":57},[43,22993,147],{"class":49},[43,22995,22996,22998,23000,23002,23004,23007,23009,23011,23013,23015],{"class":45,"line":3077},[43,22997,22803],{"class":57},[43,22999,97],{"class":49},[43,23001,22808],{"class":57},[43,23003,97],{"class":49},[43,23005,23006],{"class":81},"undefined",[43,23008,104],{"class":49},[43,23010,22878],{"class":100},[43,23012,22816],{"class":49},[43,23014,22946],{"class":57},[43,23016,147],{"class":49},[43,23018,23019],{"class":45,"line":3082},[43,23020,2437],{"class":49},[43,23022,23023],{"class":45,"line":3088},[43,23024,9608],{"class":49},[43,23026,23027],{"class":45,"line":3093},[43,23028,1855],{"class":49},[29,23030,23032],{"id":23031},"테스트-실행","테스트 실행",[432,23034,23035,23038,23049],{},[188,23036,23037],{},"파일 저장: ~\u002Ftests\u002Futils\u002Ffilter.test.js에 위 코드를 저장.",[188,23039,23040,23041],{},"실행 명령어:\n",[185,23042,23043,23046],{},[188,23044,23045],{},"전체 실행: npx vitest run or npm run test",[188,23047,23048],{},"감시 모드: npx vitest watch or npm run test:watch (파일 변경 시 자동 재실행)",[188,23050,23051],{},"결과 확인: 터미널에 각 테스트 케이스의 성공\u002F실패 여부 출력.",[471,23053,23054],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":38,"searchDepth":64,"depth":64,"links":23056},[23057,23060,23061,23062,23063],{"id":22346,"depth":64,"text":22347,"children":23058},[23059],{"id":22350,"depth":119,"text":22351},{"id":22590,"depth":64,"text":22591},{"id":22607,"depth":64,"text":22608},{"id":22686,"depth":64,"text":22687},{"id":23031,"depth":64,"text":23032},"Nuxt 3 프로젝트에서 Vitest를 활용한 테스트 환경 설정과 유틸리티 함수 테스트 방법",{"date":23066,"image":23067,"field":5954,"tags":23068},"2025.03.07","unit-test.jpg",[7252,23069],"vitest","\u002Fko\u002Fnuxt3-unit-test",{"title":22341,"description":23064},{"loc":23070},"ko\u002Fnuxt3-unit-test","HqdbbYDSUMy9R4_1aPWRHYMyAVFpDRT1bKiuLpNVPi8",{"id":23076,"title":23077,"body":23078,"description":23740,"extension":484,"meta":23741,"navigation":491,"path":23745,"seo":23746,"sitemap":23747,"stem":23748,"__hash__":23749},"ko\u002Fko\u002Fmarkdown-guid.md","마크다운 문법",{"type":8,"value":23079,"toc":23725},[23080,23084,23087,23089,23093,23130,23141,23143,23147,23169,23180,23182,23186,23191,23216,23221,23241,23249,23251,23255,23264,23269,23271,23275,23284,23289,23291,23295,23300,23309,23314,23334,23342,23344,23348,23363,23368,23370,23374,23394,23399,23401,23405,23430,23435,23437,23441,23456,23461,23463,23467,23492,23494,23498,23507,23513,23515,23519,23525,23722],[11,23081,23083],{"id":23082},"마크다운markdown-문법-완벽-가이드","마크다운(Markdown) 문법 완벽 가이드",[15,23085,23086],{},"마크다운은 가볍고 직관적인 문서 작성 언어로, HTML로 변환할 수 있습니다. 아래에서 주요 문법과 변환 결과를 정리해 보겠습니다.",[26,23088],{},[29,23090,23092],{"id":23091},"_1-제목-heading","1. 제목 (Heading)",[33,23094,23098],{"className":23095,"code":23096,"language":23097,"meta":38,"style":38},"language-ㅡㅇ shiki shiki-themes github-light github-dark","# 제목 1\n## 제목 2\n### 제목 3\n#### 제목 4\n##### 제목 5\n###### 제목 6\n","ㅡㅇ",[40,23099,23100,23105,23110,23115,23120,23125],{"__ignoreMap":38},[43,23101,23102],{"class":45,"line":46},[43,23103,23104],{},"# 제목 1\n",[43,23106,23107],{"class":45,"line":64},[43,23108,23109],{},"## 제목 2\n",[43,23111,23112],{"class":45,"line":119},[43,23113,23114],{},"### 제목 3\n",[43,23116,23117],{"class":45,"line":150},[43,23118,23119],{},"#### 제목 4\n",[43,23121,23122],{"class":45,"line":159},[43,23123,23124],{},"##### 제목 5\n",[43,23126,23127],{"class":45,"line":176},[43,23128,23129],{},"###### 제목 6\n",[15,23131,23132,23133,23136,23137,23140],{},"➡️ ",[40,23134,23135],{},"\u003Ch1>"," ~ ",[40,23138,23139],{},"\u003Ch6>"," 태그로 변환",[26,23142],{},[29,23144,23146],{"id":23145},"_2-강조-bold-italic-strikethrough","2. 강조 (Bold, Italic, Strikethrough)",[33,23148,23152],{"className":23149,"code":23150,"language":23151,"meta":38,"style":38},"language-markdown shiki shiki-themes github-light github-dark","**굵게** 또는 __굵게__\n*기울임* 또는 _기울임_\n~~취소선~~\n","markdown",[40,23153,23154,23159,23164],{"__ignoreMap":38},[43,23155,23156],{"class":45,"line":46},[43,23157,23158],{},"**굵게** 또는 __굵게__\n",[43,23160,23161],{"class":45,"line":64},[43,23162,23163],{},"*기울임* 또는 _기울임_\n",[43,23165,23166],{"class":45,"line":119},[43,23167,23168],{},"~~취소선~~\n",[15,23170,23132,23171,104,23174,104,23177,23140],{},[40,23172,23173],{},"\u003Cstrong>",[40,23175,23176],{},"\u003Cem>",[40,23178,23179],{},"\u003Cdel>",[26,23181],{},[29,23183,23185],{"id":23184},"_3-목록-lists","3. 목록 (Lists)",[15,23187,5985,23188],{},[21,23189,23190],{},"순서 없는 목록",[33,23192,23194],{"className":23149,"code":23193,"language":23151,"meta":38,"style":38},"- 항목 1\n- 항목 2\n  - 하위 항목 1\n  - 하위 항목 2\n",[40,23195,23196,23201,23206,23211],{"__ignoreMap":38},[43,23197,23198],{"class":45,"line":46},[43,23199,23200],{},"- 항목 1\n",[43,23202,23203],{"class":45,"line":64},[43,23204,23205],{},"- 항목 2\n",[43,23207,23208],{"class":45,"line":119},[43,23209,23210],{},"  - 하위 항목 1\n",[43,23212,23213],{"class":45,"line":150},[43,23214,23215],{},"  - 하위 항목 2\n",[15,23217,5985,23218],{},[21,23219,23220],{},"순서 있는 목록",[33,23222,23224],{"className":23149,"code":23223,"language":23151,"meta":38,"style":38},"1. 첫 번째 항목\n2. 두 번째 항목\n3. 세 번째 항목\n",[40,23225,23226,23231,23236],{"__ignoreMap":38},[43,23227,23228],{"class":45,"line":46},[43,23229,23230],{},"1. 첫 번째 항목\n",[43,23232,23233],{"class":45,"line":64},[43,23234,23235],{},"2. 두 번째 항목\n",[43,23237,23238],{"class":45,"line":119},[43,23239,23240],{},"3. 세 번째 항목\n",[15,23242,23132,23243,104,23246,23140],{},[40,23244,23245],{},"\u003Cul>",[40,23247,23248],{},"\u003Col>",[26,23250],{},[29,23252,23254],{"id":23253},"_4-링크-links","🔹 4. 링크 (Links)",[33,23256,23258],{"className":23149,"code":23257,"language":23151,"meta":38,"style":38},"[네이버](https:\u002F\u002Fwww.naver.com)\n",[40,23259,23260],{"__ignoreMap":38},[43,23261,23262],{"class":45,"line":46},[43,23263,23257],{},[15,23265,23132,23266,23140],{},[40,23267,23268],{},"\u003Ca>",[26,23270],{},[29,23272,23274],{"id":23273},"_5-이미지-images","🔹 5. 이미지 (Images)",[33,23276,23278],{"className":23149,"code":23277,"language":23151,"meta":38,"style":38},"![대체 텍스트](https:\u002F\u002Fexample.com\u002Fimage.jpg)\n",[40,23279,23280],{"__ignoreMap":38},[43,23281,23282],{"class":45,"line":46},[43,23283,23277],{},[15,23285,23132,23286,23140],{},[40,23287,23288],{},"\u003Cimg>",[26,23290],{},[29,23292,23294],{"id":23293},"_6-코드-code","🔹 6. 코드 (Code)",[15,23296,5985,23297],{},[21,23298,23299],{},"인라인 코드",[33,23301,23303],{"className":23149,"code":23302,"language":23151,"meta":38,"style":38},"`코드`\n",[40,23304,23305],{"__ignoreMap":38},[43,23306,23307],{"class":45,"line":46},[43,23308,23302],{},[15,23310,5985,23311],{},[21,23312,23313],{},"블록 코드",[33,23315,23317],{"className":23149,"code":23316,"language":23151,"meta":38,"style":38},"```python\nprint(\"Hello, Markdown!\")\n```\n",[40,23318,23319,23324,23329],{"__ignoreMap":38},[43,23320,23321],{"class":45,"line":46},[43,23322,23323],{},"```python\n",[43,23325,23326],{"class":45,"line":64},[43,23327,23328],{},"print(\"Hello, Markdown!\")\n",[43,23330,23331],{"class":45,"line":119},[43,23332,23333],{},"```\n",[15,23335,23132,23336,104,23339,23140],{},[40,23337,23338],{},"\u003Ccode>",[40,23340,23341],{},"\u003Cpre>",[26,23343],{},[29,23345,23347],{"id":23346},"_7-인용문-blockquote","🔹 7. 인용문 (Blockquote)",[33,23349,23351],{"className":23149,"code":23350,"language":23151,"meta":38,"style":38},"> 인용문입니다.\n>> 중첩된 인용문입니다.\n",[40,23352,23353,23358],{"__ignoreMap":38},[43,23354,23355],{"class":45,"line":46},[43,23356,23357],{},"> 인용문입니다.\n",[43,23359,23360],{"class":45,"line":64},[43,23361,23362],{},">> 중첩된 인용문입니다.\n",[15,23364,23132,23365,23140],{},[40,23366,23367],{},"\u003Cblockquote>",[26,23369],{},[29,23371,23373],{"id":23372},"_8-수평선-horizontal-rule","🔹 8. 수평선 (Horizontal Rule)",[33,23375,23377],{"className":23149,"code":23376,"language":23151,"meta":38,"style":38},"---\n***\n___\n",[40,23378,23379,23384,23389],{"__ignoreMap":38},[43,23380,23381],{"class":45,"line":46},[43,23382,23383],{},"---\n",[43,23385,23386],{"class":45,"line":64},[43,23387,23388],{},"***\n",[43,23390,23391],{"class":45,"line":119},[43,23392,23393],{},"___\n",[15,23395,23132,23396,23140],{},[40,23397,23398],{},"\u003Chr>",[26,23400],{},[29,23402,23404],{"id":23403},"_9-테이블-tables","🔹 9. 테이블 (Tables)",[33,23406,23408],{"className":23149,"code":23407,"language":23151,"meta":38,"style":38},"| 제목 1 | 제목 2 | 제목 3 |\n|--------|--------|--------|\n| 내용 1 | 내용 2 | 내용 3 |\n| 내용 A | 내용 B | 내용 C |\n",[40,23409,23410,23415,23420,23425],{"__ignoreMap":38},[43,23411,23412],{"class":45,"line":46},[43,23413,23414],{},"| 제목 1 | 제목 2 | 제목 3 |\n",[43,23416,23417],{"class":45,"line":64},[43,23418,23419],{},"|--------|--------|--------|\n",[43,23421,23422],{"class":45,"line":119},[43,23423,23424],{},"| 내용 1 | 내용 2 | 내용 3 |\n",[43,23426,23427],{"class":45,"line":150},[43,23428,23429],{},"| 내용 A | 내용 B | 내용 C |\n",[15,23431,23132,23432,23140],{},[40,23433,23434],{},"\u003Ctable>",[26,23436],{},[29,23438,23440],{"id":23439},"_10-체크리스트-task-list","🔹 10. 체크리스트 (Task List)",[33,23442,23444],{"className":23149,"code":23443,"language":23151,"meta":38,"style":38},"- [x] 완료된 항목\n- [ ] 미완료된 항목\n",[40,23445,23446,23451],{"__ignoreMap":38},[43,23447,23448],{"class":45,"line":46},[43,23449,23450],{},"- [x] 완료된 항목\n",[43,23452,23453],{"class":45,"line":64},[43,23454,23455],{},"- [ ] 미완료된 항목\n",[15,23457,23132,23458,23140],{},[40,23459,23460],{},"\u003Cinput type=\"checkbox\">",[26,23462],{},[29,23464,23466],{"id":23465},"_11-html-직접-사용-가능","🔹 11. HTML 직접 사용 가능",[33,23468,23470],{"className":11009,"code":23469,"language":11011,"meta":38,"style":38},"\u003Cp style=\"color: red;\">빨간색 텍스트\u003C\u002Fp>\n",[40,23471,23472],{"__ignoreMap":38},[43,23473,23474,23476,23478,23480,23482,23485,23488,23490],{"class":45,"line":46},[43,23475,50],{"class":49},[43,23477,15],{"class":53},[43,23479,10861],{"class":57},[43,23481,88],{"class":49},[43,23483,23484],{"class":100},"\"color: red;\"",[43,23486,23487],{"class":49},">빨간색 텍스트\u003C\u002F",[43,23489,15],{"class":53},[43,23491,61],{"class":49},[26,23493],{},[29,23495,23497],{"id":23496},"_12-이스케이프-escape","🔹 12. 이스케이프 (Escape)",[33,23499,23501],{"className":23149,"code":23500,"language":23151,"meta":38,"style":38},"\\*이탤릭 아님\\*\n",[40,23502,23503],{"__ignoreMap":38},[43,23504,23505],{"class":45,"line":46},[43,23506,23500],{},[15,23508,23509,23510,10956],{},"➡️ 특수 문자를 출력하기 위해 ",[40,23511,23512],{},"\\",[26,23514],{},[29,23516,23518],{"id":23517},"마크다운-한눈에-정리","🚀 마크다운 한눈에 정리",[15,23520,5985,23521,23524],{},[21,23522,23523],{},"12가지 주요 문법","과 변환 결과:",[678,23526,23527,23540],{},[681,23528,23529],{},[684,23530,23531,23534,23537],{},[687,23532,23533],{},"기능",[687,23535,23536],{},"마크다운",[687,23538,23539],{},"변환 태그",[703,23541,23542,23557,23576,23595,23609,23623,23640,23654,23671,23681,23696,23709],{},[684,23543,23544,23547,23552],{},[708,23545,23546],{},"제목",[708,23548,23549],{},[40,23550,23551],{},"# 제목",[708,23553,23554],{},[40,23555,23556],{},"\u003Ch1> ~ \u003Ch6>",[684,23558,23559,23562,23570],{},[708,23560,23561],{},"강조",[708,23563,23564,104,23567],{},[40,23565,23566],{},"**굵게**",[40,23568,23569],{},"*기울임*",[708,23571,23572,104,23574],{},[40,23573,23173],{},[40,23575,23176],{},[684,23577,23578,23581,23589],{},[708,23579,23580],{},"목록",[708,23582,23583,104,23586],{},[40,23584,23585],{},"- 항목",[40,23587,23588],{},"1. 항목",[708,23590,23591,104,23593],{},[40,23592,23245],{},[40,23594,23248],{},[684,23596,23597,23600,23605],{},[708,23598,23599],{},"링크",[708,23601,23602],{},[40,23603,23604],{},"[텍스트](URL)",[708,23606,23607],{},[40,23608,23268],{},[684,23610,23611,23614,23619],{},[708,23612,23613],{},"이미지",[708,23615,23616],{},[40,23617,23618],{},"![설명](URL)",[708,23620,23621],{},[40,23622,23288],{},[684,23624,23625,23628,23634],{},[708,23626,23627],{},"코드",[708,23629,23630,23633],{},[40,23631,23632],{},"`코드`",", 블록 코드",[708,23635,23636,104,23638],{},[40,23637,23338],{},[40,23639,23341],{},[684,23641,23642,23645,23650],{},[708,23643,23644],{},"인용문",[708,23646,23647],{},[40,23648,23649],{},"> 인용문",[708,23651,23652],{},[40,23653,23367],{},[684,23655,23656,23659,23667],{},[708,23657,23658],{},"수평선",[708,23660,23661,104,23664],{},[40,23662,23663],{},"---",[40,23665,23666],{},"***",[708,23668,23669],{},[40,23670,23398],{},[684,23672,23673,23676,23679],{},[708,23674,23675],{},"테이블",[708,23677,23678],{},"`",[708,23680,23546],{},[684,23682,23683,23686,23691],{},[708,23684,23685],{},"체크리스트",[708,23687,23688],{},[40,23689,23690],{},"- [x]",[708,23692,23693],{},[40,23694,23695],{},"\u003Cinput>",[684,23697,23698,23701,23706],{},[708,23699,23700],{},"HTML 사용",[708,23702,23703],{},[40,23704,23705],{},"\u003Cp>HTML 코드\u003C\u002Fp>",[708,23707,23708],{},"그대로 사용 가능",[684,23710,23711,23714,23719],{},[708,23712,23713],{},"이스케이프",[708,23715,23716],{},[40,23717,23718],{},"\\*특수문자*",[708,23720,23721],{},"그대로 출력",[471,23723,23724],{},"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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":38,"searchDepth":64,"depth":64,"links":23726},[23727,23728,23729,23730,23731,23732,23733,23734,23735,23736,23737,23738,23739],{"id":23091,"depth":64,"text":23092},{"id":23145,"depth":64,"text":23146},{"id":23184,"depth":64,"text":23185},{"id":23253,"depth":64,"text":23254},{"id":23273,"depth":64,"text":23274},{"id":23293,"depth":64,"text":23294},{"id":23346,"depth":64,"text":23347},{"id":23372,"depth":64,"text":23373},{"id":23403,"depth":64,"text":23404},{"id":23439,"depth":64,"text":23440},{"id":23465,"depth":64,"text":23466},{"id":23496,"depth":64,"text":23497},{"id":23517,"depth":64,"text":23518},"마크다운 문법을 알아보자",{"date":23742,"image":23743,"field":487,"tags":23744},"2025.03.02","markdown.webp",[23151],"\u002Fko\u002Fmarkdown-guid",{"title":23077,"description":23740},{"loc":23745},"ko\u002Fmarkdown-guid","8qnw0z1IebW5r5XXT_JOLkc0MKGR325WeYNuiGPspog",{"id":23751,"title":23752,"body":23753,"description":24267,"extension":484,"meta":24268,"navigation":491,"path":24273,"seo":24274,"sitemap":24275,"stem":24276,"__hash__":24277},"ko\u002Fko\u002Fdisplaying-commits-with-github-actions.md","깃허브 액션으로 커밋 로그를 블로그에 띄우기: Nuxt와 Vercel 활용법",{"type":8,"value":23754,"toc":24256},[23755,23759,23762,23766,23769,23775,23938,23942,23946,23970,24138,24142,24165,24248,24250,24253],[29,23756,23758],{"id":23757},"소개-커밋-로그를-ui에-표시하는-이유","소개: 커밋 로그를 UI에 표시하는 이유",[15,23760,23761],{},"깃허브 커밋 히스토리는 프로젝트 진행 상황을 보여주고, 작업 흐름을 공유하는 중요한 자료입니다. 블로그에 커밋 로그를 실시간으로 표시하고 싶어 깃허브 액션과 Nuxt, Vercel을 활용해 구현했습니다.",[29,23763,23765],{"id":23764},"_1단계-깃허브-액션으로-커밋-데이터-수집","1단계: 깃허브 액션으로 커밋 데이터 수집",[15,23767,23768],{},"깃허브 액션을 사용해 매일 자정(UTC 00:00)에 최신 20개 커밋을 JSON 파일로 저장하고 자동으로 푸시합니다.",[1273,23770,23772],{"id":23771},"githubworkflowsupdate-commitsyml",[40,23773,23774],{},".github\u002Fworkflows\u002Fupdate-commits.yml",[33,23776,23780],{"className":23777,"code":23778,"language":23779,"meta":38,"style":38},"language-yaml shiki shiki-themes github-light github-dark","name: Update Commits Log\non:\n  schedule:\n    - cron: \"0 0 * * *\"\n  workflow_dispatch:\npermissions:\n  contents: write\njobs:\n  update-commits:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v3\n      - run: |\n          curl -s -H \"Authorization: token ${{ secrets.GITHUB_TOKEN }}\" \\\n            \"https:\u002F\u002Fapi.github.com\u002Frepos\u002F${{ github.repository }}\u002Fcommits?per_page=20\" | \\\n          jq '[.[] | {message: .commit.message, date: .commit.author.date, sha: .sha, url: .html_url}]' > public\u002Fcommits.json\n      - run: |\n          git config --global user.name \"GitHub Action\"\n          git config --global user.email \"action@github.com\"\n          git diff --quiet || (git add public\u002Fcommits.json && git commit -m \"Update commits log [skip ci]\" && git push)\n","yaml",[40,23781,23782,23791,23798,23805,23818,23825,23832,23842,23849,23856,23866,23873,23886,23898,23903,23908,23913,23923,23928,23933],{"__ignoreMap":38},[43,23783,23784,23786,23788],{"class":45,"line":46},[43,23785,20283],{"class":53},[43,23787,78],{"class":49},[43,23789,23790],{"class":100},"Update Commits Log\n",[43,23792,23793,23795],{"class":45,"line":64},[43,23794,2394],{"class":81},[43,23796,23797],{"class":49},":\n",[43,23799,23800,23803],{"class":45,"line":119},[43,23801,23802],{"class":53},"  schedule",[43,23804,23797],{"class":49},[43,23806,23807,23810,23813,23815],{"class":45,"line":150},[43,23808,23809],{"class":49},"    - ",[43,23811,23812],{"class":53},"cron",[43,23814,78],{"class":49},[43,23816,23817],{"class":100},"\"0 0 * * *\"\n",[43,23819,23820,23823],{"class":45,"line":159},[43,23821,23822],{"class":53},"  workflow_dispatch",[43,23824,23797],{"class":49},[43,23826,23827,23830],{"class":45,"line":176},[43,23828,23829],{"class":53},"permissions",[43,23831,23797],{"class":49},[43,23833,23834,23837,23839],{"class":45,"line":1163},[43,23835,23836],{"class":53},"  contents",[43,23838,78],{"class":49},[43,23840,23841],{"class":100},"write\n",[43,23843,23844,23847],{"class":45,"line":1178},[43,23845,23846],{"class":53},"jobs",[43,23848,23797],{"class":49},[43,23850,23851,23854],{"class":45,"line":1184},[43,23852,23853],{"class":53},"  update-commits",[43,23855,23797],{"class":49},[43,23857,23858,23861,23863],{"class":45,"line":1190},[43,23859,23860],{"class":53},"    runs-on",[43,23862,78],{"class":49},[43,23864,23865],{"class":100},"ubuntu-latest\n",[43,23867,23868,23871],{"class":45,"line":1996},[43,23869,23870],{"class":53},"    steps",[43,23872,23797],{"class":49},[43,23874,23875,23878,23881,23883],{"class":45,"line":2008},[43,23876,23877],{"class":49},"      - ",[43,23879,23880],{"class":53},"uses",[43,23882,78],{"class":49},[43,23884,23885],{"class":100},"actions\u002Fcheckout@v3\n",[43,23887,23888,23890,23893,23895],{"class":45,"line":2025},[43,23889,23877],{"class":49},[43,23891,23892],{"class":53},"run",[43,23894,78],{"class":49},[43,23896,23897],{"class":67},"|\n",[43,23899,23900],{"class":45,"line":2031},[43,23901,23902],{"class":100},"          curl -s -H \"Authorization: token ${{ secrets.GITHUB_TOKEN }}\" \\\n",[43,23904,23905],{"class":45,"line":2037},[43,23906,23907],{"class":100},"            \"https:\u002F\u002Fapi.github.com\u002Frepos\u002F${{ github.repository }}\u002Fcommits?per_page=20\" | \\\n",[43,23909,23910],{"class":45,"line":2042},[43,23911,23912],{"class":100},"          jq '[.[] | {message: .commit.message, date: .commit.author.date, sha: .sha, url: .html_url}]' > public\u002Fcommits.json\n",[43,23914,23915,23917,23919,23921],{"class":45,"line":2516},[43,23916,23877],{"class":49},[43,23918,23892],{"class":53},[43,23920,78],{"class":49},[43,23922,23897],{"class":67},[43,23924,23925],{"class":45,"line":2998},[43,23926,23927],{"class":100},"          git config --global user.name \"GitHub Action\"\n",[43,23929,23930],{"class":45,"line":3003},[43,23931,23932],{"class":100},"          git config --global user.email \"action@github.com\"\n",[43,23934,23935],{"class":45,"line":3009},[43,23936,23937],{"class":100},"          git diff --quiet || (git add public\u002Fcommits.json && git commit -m \"Update commits log [skip ci]\" && git push)\n",[29,23939,23941],{"id":23940},"_2단계-nuxt로-ui-표시","2단계: Nuxt로 UI 표시",[1273,23943,23945],{"id":23944},"정적-파일-방식","정적 파일 방식",[185,23947,23948,23954,23964],{},[188,23949,23950,23953],{},[40,23951,23952],{},"public\u002Fcommits.json","을 Nuxt에서 직접 불러옵니다.",[188,23955,23956,23959,23960,23963],{},[40,23957,23958],{},"server\u002Fapi\u002Fcommits.js","를 사용한 API 방식은 Vercel 배포 시 ",[40,23961,23962],{},"ENOENT"," 오류 발생으로 제거했습니다.",[188,23965,23966,23969],{},[40,23967,23968],{},"useAsyncData","를 활용해 프론트엔드에서 데이터를 가져옵니다.",[33,23971,23973],{"className":35,"code":23972,"language":37,"meta":38,"style":38},"\u003Cscript setup>\nconst { data: commits } = useAsyncData(\"commits\", () =>\n  $fetch(\"\u002Fcommits.json\")\n);\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cul>\n    \u003Cli v-for=\"commit in commits\" :key=\"commit.sha\">\n      \u003Ca :href=\"commit.url\" target=\"_blank\">{{ commit.message }}\u003C\u002Fa> -\n      {{ commit.date }}\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Ftemplate>\n",[40,23974,23975,23985,24013,24025,24029,24037,24041,24049,24057,24079,24109,24114,24122,24130],{"__ignoreMap":38},[43,23976,23977,23979,23981,23983],{"class":45,"line":46},[43,23978,50],{"class":49},[43,23980,54],{"class":53},[43,23982,58],{"class":57},[43,23984,61],{"class":49},[43,23986,23987,23989,23991,23993,23995,23998,24000,24002,24004,24006,24009,24011],{"class":45,"line":64},[43,23988,68],{"class":67},[43,23990,71],{"class":49},[43,23992,75],{"class":74},[43,23994,78],{"class":49},[43,23996,23997],{"class":81},"commits",[43,23999,85],{"class":49},[43,24001,88],{"class":67},[43,24003,94],{"class":57},[43,24005,97],{"class":49},[43,24007,24008],{"class":100},"\"commits\"",[43,24010,3427],{"class":49},[43,24012,4699],{"class":67},[43,24014,24015,24018,24020,24023],{"class":45,"line":119},[43,24016,24017],{"class":57},"  $fetch",[43,24019,97],{"class":49},[43,24021,24022],{"class":100},"\"\u002Fcommits.json\"",[43,24024,5241],{"class":49},[43,24026,24027],{"class":45,"line":150},[43,24028,1160],{"class":49},[43,24030,24031,24033,24035],{"class":45,"line":159},[43,24032,179],{"class":49},[43,24034,54],{"class":53},[43,24036,61],{"class":49},[43,24038,24039],{"class":45,"line":176},[43,24040,1782],{"emptyLinePlaceholder":491},[43,24042,24043,24045,24047],{"class":45,"line":1163},[43,24044,50],{"class":49},[43,24046,6693],{"class":53},[43,24048,61],{"class":49},[43,24050,24051,24053,24055],{"class":45,"line":1178},[43,24052,6700],{"class":49},[43,24054,185],{"class":53},[43,24056,61],{"class":49},[43,24058,24059,24061,24063,24065,24067,24070,24072,24074,24077],{"class":45,"line":1184},[43,24060,6726],{"class":49},[43,24062,188],{"class":53},[43,24064,7344],{"class":57},[43,24066,88],{"class":49},[43,24068,24069],{"class":100},"\"commit in commits\"",[43,24071,7352],{"class":57},[43,24073,88],{"class":49},[43,24075,24076],{"class":100},"\"commit.sha\"",[43,24078,61],{"class":49},[43,24080,24081,24083,24085,24088,24090,24093,24096,24098,24101,24104,24106],{"class":45,"line":1190},[43,24082,7339],{"class":49},[43,24084,304],{"class":53},[43,24086,24087],{"class":57}," :href",[43,24089,88],{"class":49},[43,24091,24092],{"class":100},"\"commit.url\"",[43,24094,24095],{"class":57}," target",[43,24097,88],{"class":49},[43,24099,24100],{"class":100},"\"_blank\"",[43,24102,24103],{"class":49},">{{ commit.message }}\u003C\u002F",[43,24105,304],{"class":53},[43,24107,24108],{"class":49},"> -\n",[43,24110,24111],{"class":45,"line":1996},[43,24112,24113],{"class":49},"      {{ commit.date }}\n",[43,24115,24116,24118,24120],{"class":45,"line":2008},[43,24117,7378],{"class":49},[43,24119,188],{"class":53},[43,24121,61],{"class":49},[43,24123,24124,24126,24128],{"class":45,"line":2025},[43,24125,6740],{"class":49},[43,24127,185],{"class":53},[43,24129,61],{"class":49},[43,24131,24132,24134,24136],{"class":45,"line":2031},[43,24133,179],{"class":49},[43,24135,6693],{"class":53},[43,24137,61],{"class":49},[29,24139,24141],{"id":24140},"_3단계-캐싱-문제-해결","3단계: 캐싱 문제 해결",[185,24143,24144,24149],{},[188,24145,24146,24148],{},[40,24147,23968],{},"는 동일한 키의 데이터를 캐싱해 새로고침해도 갱신되지 않는 문제가 있었습니다.",[188,24150,24151,78,24154,24156,24157,24160,24161,24164],{},[21,24152,24153],{},"해결 방법",[40,24155,23968],{}," 호출 시 ",[40,24158,24159],{},"Math.random()","을 활용해 ",[21,24162,24163],{},"동적으로 키를 변경","하여 항상 새로운 데이터를 가져오도록 설정.",[33,24166,24168],{"className":1024,"code":24167,"language":1026,"meta":38,"style":38},"const { data: commits } = useAsyncData(`commits-${Math.random()}`, async () => {\n  const response = await fetch(\"\u002Fcommits.json\");\n  return response.json();\n});\n",[40,24169,24170,24215,24233,24244],{"__ignoreMap":38},[43,24171,24172,24174,24176,24178,24180,24182,24184,24186,24188,24190,24193,24196,24198,24201,24203,24205,24207,24209,24211,24213],{"class":45,"line":46},[43,24173,68],{"class":67},[43,24175,71],{"class":49},[43,24177,75],{"class":74},[43,24179,78],{"class":49},[43,24181,23997],{"class":81},[43,24183,85],{"class":49},[43,24185,88],{"class":67},[43,24187,94],{"class":57},[43,24189,97],{"class":49},[43,24191,24192],{"class":100},"`commits-${",[43,24194,24195],{"class":49},"Math",[43,24197,1139],{"class":100},[43,24199,24200],{"class":57},"random",[43,24202,3186],{"class":100},[43,24204,1157],{"class":100},[43,24206,104],{"class":49},[43,24208,107],{"class":67},[43,24210,110],{"class":49},[43,24212,113],{"class":67},[43,24214,116],{"class":49},[43,24216,24217,24219,24221,24223,24225,24227,24229,24231],{"class":45,"line":64},[43,24218,122],{"class":67},[43,24220,8185],{"class":81},[43,24222,128],{"class":67},[43,24224,91],{"class":67},[43,24226,9254],{"class":57},[43,24228,97],{"class":49},[43,24230,24022],{"class":100},[43,24232,1160],{"class":49},[43,24234,24235,24237,24240,24242],{"class":45,"line":119},[43,24236,153],{"class":67},[43,24238,24239],{"class":49}," response.",[43,24241,558],{"class":57},[43,24243,147],{"class":49},[43,24245,24246],{"class":45,"line":150},[43,24247,1855],{"class":49},[29,24249,10925],{"id":10925},[15,24251,24252],{},"깃허브 액션으로 자동화된 커밋 데이터 수집, Nuxt로 UI 구성, Vercel로 배포하는 과정을 정리했습니다. 초기 API 방식의 문제를 정적 파일 방식으로 해결하고, 캐싱 문제도 보완했습니다. 이를 통해 깃허브 커밋 로그를 블로그에 실시간으로 표시할 수 있습니다.",[471,24254,24255],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":38,"searchDepth":64,"depth":64,"links":24257},[24258,24259,24262,24265,24266],{"id":23757,"depth":64,"text":23758},{"id":23764,"depth":64,"text":23765,"children":24260},[24261],{"id":23771,"depth":119,"text":23774},{"id":23940,"depth":64,"text":23941,"children":24263},[24264],{"id":23944,"depth":119,"text":23945},{"id":24140,"depth":64,"text":24141},{"id":10925,"depth":64,"text":10925},"깃허브 액션을 통해 커밋 데이터를 자동으로 수집하고, Nuxt와 Vercel로 UI에 노출하는 방법을 소개합니다.",{"date":24269,"image":24270,"field":4436,"tags":24271},"2025.02.28","github-commits.jpg",[24272,490,7252],"github-actions","\u002Fko\u002Fdisplaying-commits-with-github-actions",{"title":23752,"description":24267},{"loc":24273},"ko\u002Fdisplaying-commits-with-github-actions","XGxEp6kN5SIO_z4H0IfhxxoooJBw34-MHelYvD-xRW0",{"id":24279,"title":24280,"body":24281,"description":25010,"extension":484,"meta":25011,"navigation":491,"path":25014,"seo":25015,"sitemap":25016,"stem":25017,"__hash__":25018},"ko\u002Fko\u002Fuse-context.md","리엑트 상태관리 useContext()",{"type":8,"value":24282,"toc":24996},[24283,24287,24297,24301,24306,24310,24313,24318,24322,24325,24345,24348,24362,24366,24370,24587,24591,24821,24825,24969,24973,24978,24985,24987,24993],[11,24284,24286],{"id":24285},"react-usecontext-완벽-가이드","React useContext 완벽 가이드",[15,24288,24289,24290,24293,24294,24296],{},"React에서 ",[40,24291,24292],{},"useContext","를 사용하면 컴포넌트 트리에 깊이 중첩된 컴포넌트에서도 prop drilling 없이 상태를 공유할 수 있습니다. 이 글에서는 ",[40,24295,24292],{},"의 개념, 사용 방법, 장단점, 그리고 실전 예제를 함께 알아보겠습니다.",[29,24298,24300],{"id":24299},"_1-usecontext란","1. useContext란?",[15,24302,24303,24305],{},[40,24304,24292],{},"는 React의 내장 Hook 중 하나로, Context API를 통해 데이터를 손쉽게 공유할 수 있도록 도와줍니다. 일반적으로 전역 상태 관리가 필요한 경우에 사용됩니다.",[29,24307,24309],{"id":24308},"_2-왜-usecontext를-사용할까","2. 왜 useContext를 사용할까?",[15,24311,24312],{},"React에서는 부모에서 자식으로 props를 통해 데이터를 전달하는 방식이 기본입니다. 하지만 컴포넌트 트리가 깊어질수록 여러 단계의 props 전달이 필요해지는데, 이를 \"prop drilling\" 문제라고 합니다.",[15,24314,24315,24317],{},[40,24316,24292],{},"를 사용하면 이런 문제를 해결하고, 상태를 한 곳에서 관리하면서 필요한 컴포넌트에서 쉽게 접근할 수 있습니다.",[29,24319,24321],{"id":24320},"_3-usecontext의-장단점","3. useContext의 장단점",[1273,24323,24324],{"id":10944},"✅ 장점",[185,24326,24327,24333,24339],{},[188,24328,24329,24332],{},[21,24330,24331],{},"prop drilling 방지",": 중간 컴포넌트를 거치지 않고 데이터를 쉽게 가져올 수 있습니다.",[188,24334,24335,24338],{},[21,24336,24337],{},"간결한 코드",": 불필요한 props 전달을 줄여 코드가 단순해집니다.",[188,24340,24341,24344],{},[21,24342,24343],{},"상태 공유 용이",": Redux, Recoil 같은 상태 관리 라이브러리 없이도 전역 상태를 쉽게 관리할 수 있습니다.",[1273,24346,24347],{"id":10947},"❌ 단점",[185,24349,24350,24356],{},[188,24351,24352,24355],{},[21,24353,24354],{},"리렌더링 성능 이슈",": Context 값이 변경되면 해당 Context를 사용하는 모든 컴포넌트가 리렌더링됩니다.",[188,24357,24358,24361],{},[21,24359,24360],{},"세분화된 상태 관리 부족",": 여러 개의 상태를 세분화해서 관리하려면 Context를 나눠야 합니다.",[29,24363,24365],{"id":24364},"_4-usecontext-사용-방법","4. useContext 사용 방법",[1273,24367,24369],{"id":24368},"_41-context-생성-및-제공-provider-설정","4.1. Context 생성 및 제공 (Provider 설정)",[33,24371,24375],{"className":24372,"code":24373,"language":24374,"meta":38,"style":38},"language-jsx shiki shiki-themes github-light github-dark","import React, { createContext, useContext, useState } from \"react\";\n\n\u002F\u002F 1. Context 생성\nconst ThemeContext = createContext();\n\n\u002F\u002F 2. Provider 컴포넌트 생성\nconst ThemeProvider = ({ children }) => {\n  const [theme, setTheme] = useState(\"light\");\n\n  \u002F\u002F 테마 변경 함수\n  const toggleTheme = () => {\n    setTheme((prevTheme) => (prevTheme === \"light\" ? \"dark\" : \"light\"));\n  };\n\n  return (\n    \u003CThemeContext.Provider value={{ theme, toggleTheme }}>\n      {children}\n    \u003C\u002FThemeContext.Provider>\n  );\n};\n\nexport { ThemeProvider, ThemeContext };\n","jsx",[40,24376,24377,24390,24394,24399,24413,24417,24422,24442,24469,24473,24478,24493,24526,24530,24534,24540,24555,24560,24568,24572,24576,24580],{"__ignoreMap":38},[43,24378,24379,24381,24384,24386,24388],{"class":45,"line":46},[43,24380,7079],{"class":67},[43,24382,24383],{"class":49}," React, { createContext, useContext, useState } ",[43,24385,7085],{"class":67},[43,24387,9085],{"class":100},[43,24389,2995],{"class":49},[43,24391,24392],{"class":45,"line":64},[43,24393,1782],{"emptyLinePlaceholder":491},[43,24395,24396],{"class":45,"line":119},[43,24397,24398],{"class":2797},"\u002F\u002F 1. Context 생성\n",[43,24400,24401,24403,24406,24408,24411],{"class":45,"line":150},[43,24402,68],{"class":67},[43,24404,24405],{"class":81}," ThemeContext",[43,24407,128],{"class":67},[43,24409,24410],{"class":57}," createContext",[43,24412,147],{"class":49},[43,24414,24415],{"class":45,"line":159},[43,24416,1782],{"emptyLinePlaceholder":491},[43,24418,24419],{"class":45,"line":176},[43,24420,24421],{"class":2797},"\u002F\u002F 2. Provider 컴포넌트 생성\n",[43,24423,24424,24426,24429,24431,24433,24436,24438,24440],{"class":45,"line":1163},[43,24425,68],{"class":67},[43,24427,24428],{"class":57}," ThemeProvider",[43,24430,128],{"class":67},[43,24432,5134],{"class":49},[43,24434,24435],{"class":74},"children",[43,24437,21296],{"class":49},[43,24439,113],{"class":67},[43,24441,116],{"class":49},[43,24443,24444,24446,24448,24451,24453,24456,24458,24460,24462,24464,24467],{"class":45,"line":1178},[43,24445,122],{"class":67},[43,24447,1169],{"class":49},[43,24449,24450],{"class":81},"theme",[43,24452,104],{"class":49},[43,24454,24455],{"class":81},"setTheme",[43,24457,9151],{"class":49},[43,24459,88],{"class":67},[43,24461,9156],{"class":57},[43,24463,97],{"class":49},[43,24465,24466],{"class":100},"\"light\"",[43,24468,1160],{"class":49},[43,24470,24471],{"class":45,"line":1184},[43,24472,1782],{"emptyLinePlaceholder":491},[43,24474,24475],{"class":45,"line":1190},[43,24476,24477],{"class":2797},"  \u002F\u002F 테마 변경 함수\n",[43,24479,24480,24482,24485,24487,24489,24491],{"class":45,"line":1996},[43,24481,122],{"class":67},[43,24483,24484],{"class":57}," toggleTheme",[43,24486,128],{"class":67},[43,24488,110],{"class":49},[43,24490,113],{"class":67},[43,24492,116],{"class":49},[43,24494,24495,24498,24500,24503,24505,24507,24510,24512,24515,24517,24520,24522,24524],{"class":45,"line":2008},[43,24496,24497],{"class":57},"    setTheme",[43,24499,4691],{"class":49},[43,24501,24502],{"class":74},"prevTheme",[43,24504,2408],{"class":49},[43,24506,113],{"class":67},[43,24508,24509],{"class":49}," (prevTheme ",[43,24511,4914],{"class":67},[43,24513,24514],{"class":100}," \"light\"",[43,24516,13401],{"class":67},[43,24518,24519],{"class":100}," \"dark\"",[43,24521,13407],{"class":67},[43,24523,24514],{"class":100},[43,24525,1108],{"class":49},[43,24527,24528],{"class":45,"line":2025},[43,24529,9419],{"class":49},[43,24531,24532],{"class":45,"line":2031},[43,24533,1782],{"emptyLinePlaceholder":491},[43,24535,24536,24538],{"class":45,"line":2037},[43,24537,153],{"class":67},[43,24539,8437],{"class":49},[43,24541,24542,24544,24547,24550,24552],{"class":45,"line":2042},[43,24543,6726],{"class":49},[43,24545,24546],{"class":81},"ThemeContext.Provider",[43,24548,24549],{"class":57}," value",[43,24551,88],{"class":67},[43,24553,24554],{"class":49},"{{ theme, toggleTheme }}>\n",[43,24556,24557],{"class":45,"line":2516},[43,24558,24559],{"class":49},"      {children}\n",[43,24561,24562,24564,24566],{"class":45,"line":2998},[43,24563,7378],{"class":49},[43,24565,24546],{"class":81},[43,24567,61],{"class":49},[43,24569,24570],{"class":45,"line":3003},[43,24571,8515],{"class":49},[43,24573,24574],{"class":45,"line":3009},[43,24575,9424],{"class":49},[43,24577,24578],{"class":45,"line":3022},[43,24579,1782],{"emptyLinePlaceholder":491},[43,24581,24582,24584],{"class":45,"line":3044},[43,24583,6052],{"class":67},[43,24585,24586],{"class":49}," { ThemeProvider, ThemeContext };\n",[1273,24588,24590],{"id":24589},"_42-usecontext를-이용한-값-사용","4.2. useContext를 이용한 값 사용",[33,24592,24594],{"className":24372,"code":24593,"language":24374,"meta":38,"style":38},"import React, { useContext } from \"react\";\nimport { ThemeContext } from \".\u002FThemeContext\";\n\nconst ThemeButton = () => {\n  \u002F\u002F 3. useContext로 값 가져오기\n  const { theme, toggleTheme } = useContext(ThemeContext);\n\n  return (\n    \u003Cbutton\n      onClick={toggleTheme}\n      style={{\n        backgroundColor: theme === \"light\" ? \"white\" : \"black\",\n        color: theme === \"light\" ? \"black\" : \"white\",\n        padding: \"10px 20px\",\n        border: \"none\",\n        cursor: \"pointer\",\n      }}\n    >\n      현재 테마: {theme} (클릭하여 변경)\n    \u003C\u002Fbutton>\n  );\n};\n\nexport default ThemeButton;\n",[40,24595,24596,24609,24623,24627,24642,24647,24670,24674,24680,24687,24697,24707,24728,24747,24757,24767,24777,24782,24787,24792,24800,24804,24808,24812],{"__ignoreMap":38},[43,24597,24598,24600,24603,24605,24607],{"class":45,"line":46},[43,24599,7079],{"class":67},[43,24601,24602],{"class":49}," React, { useContext } ",[43,24604,7085],{"class":67},[43,24606,9085],{"class":100},[43,24608,2995],{"class":49},[43,24610,24611,24613,24616,24618,24621],{"class":45,"line":64},[43,24612,7079],{"class":67},[43,24614,24615],{"class":49}," { ThemeContext } ",[43,24617,7085],{"class":67},[43,24619,24620],{"class":100}," \".\u002FThemeContext\"",[43,24622,2995],{"class":49},[43,24624,24625],{"class":45,"line":119},[43,24626,1782],{"emptyLinePlaceholder":491},[43,24628,24629,24631,24634,24636,24638,24640],{"class":45,"line":150},[43,24630,68],{"class":67},[43,24632,24633],{"class":57}," ThemeButton",[43,24635,128],{"class":67},[43,24637,110],{"class":49},[43,24639,113],{"class":67},[43,24641,116],{"class":49},[43,24643,24644],{"class":45,"line":159},[43,24645,24646],{"class":2797},"  \u002F\u002F 3. useContext로 값 가져오기\n",[43,24648,24649,24651,24653,24655,24657,24660,24662,24664,24667],{"class":45,"line":176},[43,24650,122],{"class":67},[43,24652,71],{"class":49},[43,24654,24450],{"class":81},[43,24656,104],{"class":49},[43,24658,24659],{"class":81},"toggleTheme",[43,24661,85],{"class":49},[43,24663,88],{"class":67},[43,24665,24666],{"class":57}," useContext",[43,24668,24669],{"class":49},"(ThemeContext);\n",[43,24671,24672],{"class":45,"line":1163},[43,24673,1782],{"emptyLinePlaceholder":491},[43,24675,24676,24678],{"class":45,"line":1178},[43,24677,153],{"class":67},[43,24679,8437],{"class":49},[43,24681,24682,24684],{"class":45,"line":1184},[43,24683,6726],{"class":49},[43,24685,24686],{"class":53},"button\n",[43,24688,24689,24692,24694],{"class":45,"line":1190},[43,24690,24691],{"class":57},"      onClick",[43,24693,88],{"class":67},[43,24695,24696],{"class":49},"{toggleTheme}\n",[43,24698,24699,24702,24704],{"class":45,"line":1996},[43,24700,24701],{"class":57},"      style",[43,24703,88],{"class":67},[43,24705,24706],{"class":49},"{{\n",[43,24708,24709,24712,24714,24716,24718,24721,24723,24726],{"class":45,"line":2008},[43,24710,24711],{"class":49},"        backgroundColor: theme ",[43,24713,4914],{"class":67},[43,24715,24514],{"class":100},[43,24717,13401],{"class":67},[43,24719,24720],{"class":100}," \"white\"",[43,24722,13407],{"class":67},[43,24724,24725],{"class":100}," \"black\"",[43,24727,1056],{"class":49},[43,24729,24730,24733,24735,24737,24739,24741,24743,24745],{"class":45,"line":2025},[43,24731,24732],{"class":49},"        color: theme ",[43,24734,4914],{"class":67},[43,24736,24514],{"class":100},[43,24738,13401],{"class":67},[43,24740,24725],{"class":100},[43,24742,13407],{"class":67},[43,24744,24720],{"class":100},[43,24746,1056],{"class":49},[43,24748,24749,24752,24755],{"class":45,"line":2031},[43,24750,24751],{"class":49},"        padding: ",[43,24753,24754],{"class":100},"\"10px 20px\"",[43,24756,1056],{"class":49},[43,24758,24759,24762,24765],{"class":45,"line":2037},[43,24760,24761],{"class":49},"        border: ",[43,24763,24764],{"class":100},"\"none\"",[43,24766,1056],{"class":49},[43,24768,24769,24772,24775],{"class":45,"line":2042},[43,24770,24771],{"class":49},"        cursor: ",[43,24773,24774],{"class":100},"\"pointer\"",[43,24776,1056],{"class":49},[43,24778,24779],{"class":45,"line":2516},[43,24780,24781],{"class":49},"      }}\n",[43,24783,24784],{"class":45,"line":2998},[43,24785,24786],{"class":49},"    >\n",[43,24788,24789],{"class":45,"line":3003},[43,24790,24791],{"class":49},"      현재 테마: {theme} (클릭하여 변경)\n",[43,24793,24794,24796,24798],{"class":45,"line":3009},[43,24795,7378],{"class":49},[43,24797,10437],{"class":53},[43,24799,61],{"class":49},[43,24801,24802],{"class":45,"line":3022},[43,24803,8515],{"class":49},[43,24805,24806],{"class":45,"line":3044},[43,24807,9424],{"class":49},[43,24809,24810],{"class":45,"line":3058},[43,24811,1782],{"emptyLinePlaceholder":491},[43,24813,24814,24816,24818],{"class":45,"line":3077},[43,24815,6052],{"class":67},[43,24817,6055],{"class":67},[43,24819,24820],{"class":49}," ThemeButton;\n",[1273,24822,24824],{"id":24823},"_43-최상위-컴포넌트에서-provider-감싸기","4.3. 최상위 컴포넌트에서 Provider 감싸기",[33,24826,24828],{"className":24372,"code":24827,"language":24374,"meta":38,"style":38},"import React from \"react\";\nimport { ThemeProvider } from \".\u002FThemeContext\";\nimport ThemeButton from \".\u002FThemeButton\";\n\nconst App = () => {\n  return (\n    \u003CThemeProvider>\n      \u003Cdiv>\n        \u003Ch1>useContext 예제\u003C\u002Fh1>\n        \u003CThemeButton \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002FThemeProvider>\n  );\n};\n\nexport default App;\n",[40,24829,24830,24842,24855,24869,24873,24887,24893,24902,24910,24923,24932,24940,24948,24952,24956,24960],{"__ignoreMap":38},[43,24831,24832,24834,24836,24838,24840],{"class":45,"line":46},[43,24833,7079],{"class":67},[43,24835,10391],{"class":49},[43,24837,7085],{"class":67},[43,24839,9085],{"class":100},[43,24841,2995],{"class":49},[43,24843,24844,24846,24849,24851,24853],{"class":45,"line":64},[43,24845,7079],{"class":67},[43,24847,24848],{"class":49}," { ThemeProvider } ",[43,24850,7085],{"class":67},[43,24852,24620],{"class":100},[43,24854,2995],{"class":49},[43,24856,24857,24859,24862,24864,24867],{"class":45,"line":119},[43,24858,7079],{"class":67},[43,24860,24861],{"class":49}," ThemeButton ",[43,24863,7085],{"class":67},[43,24865,24866],{"class":100}," \".\u002FThemeButton\"",[43,24868,2995],{"class":49},[43,24870,24871],{"class":45,"line":150},[43,24872,1782],{"emptyLinePlaceholder":491},[43,24874,24875,24877,24879,24881,24883,24885],{"class":45,"line":159},[43,24876,68],{"class":67},[43,24878,8427],{"class":57},[43,24880,128],{"class":67},[43,24882,110],{"class":49},[43,24884,113],{"class":67},[43,24886,116],{"class":49},[43,24888,24889,24891],{"class":45,"line":176},[43,24890,153],{"class":67},[43,24892,8437],{"class":49},[43,24894,24895,24897,24900],{"class":45,"line":1163},[43,24896,6726],{"class":49},[43,24898,24899],{"class":81},"ThemeProvider",[43,24901,61],{"class":49},[43,24903,24904,24906,24908],{"class":45,"line":1178},[43,24905,7339],{"class":49},[43,24907,6703],{"class":53},[43,24909,61],{"class":49},[43,24911,24912,24914,24916,24919,24921],{"class":45,"line":1184},[43,24913,8017],{"class":49},[43,24915,11],{"class":53},[43,24917,24918],{"class":49},">useContext 예제\u003C\u002F",[43,24920,11],{"class":53},[43,24922,61],{"class":49},[43,24924,24925,24927,24930],{"class":45,"line":1190},[43,24926,8017],{"class":49},[43,24928,24929],{"class":81},"ThemeButton",[43,24931,9034],{"class":49},[43,24933,24934,24936,24938],{"class":45,"line":1996},[43,24935,7369],{"class":49},[43,24937,6703],{"class":53},[43,24939,61],{"class":49},[43,24941,24942,24944,24946],{"class":45,"line":2008},[43,24943,7378],{"class":49},[43,24945,24899],{"class":81},[43,24947,61],{"class":49},[43,24949,24950],{"class":45,"line":2025},[43,24951,8515],{"class":49},[43,24953,24954],{"class":45,"line":2031},[43,24955,9424],{"class":49},[43,24957,24958],{"class":45,"line":2037},[43,24959,1782],{"emptyLinePlaceholder":491},[43,24961,24962,24964,24966],{"class":45,"line":2042},[43,24963,6052],{"class":67},[43,24965,6055],{"class":67},[43,24967,24968],{"class":49}," App;\n",[29,24970,24972],{"id":24971},"_5-결론","5. 결론",[15,24974,24975,24977],{},[40,24976,24292],{},"는 간단한 전역 상태 관리를 위한 강력한 도구입니다. Redux나 Recoil 같은 라이브러리를 사용하지 않아도 전역 데이터를 쉽게 공유할 수 있으며, prop drilling 문제를 해결하는 데 매우 유용합니다.",[15,24979,24980,24981,24984],{},"하지만 모든 상태를 Context로 관리하면 불필요한 리렌더링 문제가 발생할 수 있으므로, 필요한 경우 ",[40,24982,24983],{},"useReducer","와 함께 사용하거나, 더 복잡한 상태 관리가 필요하면 Redux 같은 라이브러리를 고려하는 것이 좋습니다.",[26,24986],{},[15,24988,24989,24990,24992],{},"이제 ",[40,24991,24292],{},"를 활용하여 상태 관리의 효율성을 높여보세요! 🚀",[471,24994,24995],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":38,"searchDepth":64,"depth":64,"links":24997},[24998,24999,25000,25004,25009],{"id":24299,"depth":64,"text":24300},{"id":24308,"depth":64,"text":24309},{"id":24320,"depth":64,"text":24321,"children":25001},[25002,25003],{"id":10944,"depth":119,"text":24324},{"id":10947,"depth":119,"text":24347},{"id":24364,"depth":64,"text":24365,"children":25005},[25006,25007,25008],{"id":24368,"depth":119,"text":24369},{"id":24589,"depth":119,"text":24590},{"id":24823,"depth":119,"text":24824},{"id":24971,"depth":64,"text":24972},"React에서 `useContext`를 사용하면 컴포넌트 트리에 깊이 중첩된 컴포넌트에서도 prop drilling 없이 상태를 공유할 수 있습니다. 이 글에서는 `useContext`의 개념, 사용 방법, 장단점, 그리고 실전 예제를 함께 알아보겠습니다.",{"date":25012,"field":5954,"tags":25013},"2025.02.03",[8861],"\u002Fko\u002Fuse-context",{"title":24280,"description":25010},{"loc":25014},"ko\u002Fuse-context","OTfaJh8JjxWsDfMs4snZ8IJpeyphDZetEgH2OBQPhS4",{"id":25020,"title":25021,"body":25022,"description":25478,"extension":484,"meta":25479,"navigation":491,"path":25482,"seo":25483,"sitemap":25484,"stem":25485,"__hash__":25486},"ko\u002Fko\u002Fnode-simple-server.md","Node.js 간단한 서버 만들기",{"type":8,"value":25023,"toc":25469},[25024,25028,25031,25035,25044,25060,25064,25071,25248,25252,25259,25271,25278,25282,25291,25295,25324,25328,25461,25463,25466],[29,25025,25027],{"id":25026},"nodejs로-간단한-http-서버-만들기","Node.js로 간단한 HTTP 서버 만들기",[15,25029,25030],{},"Node.js를 사용하면 몇 줄의 코드만으로 간단한 웹 서버를 만들 수 있습니다. 이 글에서는 기본적인 HTTP 서버를 구현하는 방법을 알아보겠습니다.",[1273,25032,25034],{"id":25033},"_1-nodejs-설치","1. Node.js 설치",[15,25036,25037,25038,25043],{},"먼저, Node.js가 설치되어 있는지 확인하세요. 설치되지 않았다면 ",[304,25039,25042],{"href":25040,"rel":25041},"https:\u002F\u002Fnodejs.org\u002F",[308],"Node.js 공식 웹사이트","에서 최신 버전을 다운로드하여 설치합니다.",[33,25045,25047],{"className":850,"code":25046,"language":852,"meta":38,"style":38},"node -v # Node.js 버전 확인\n",[40,25048,25049],{"__ignoreMap":38},[43,25050,25051,25054,25057],{"class":45,"line":46},[43,25052,25053],{"class":57},"node",[43,25055,25056],{"class":81}," -v",[43,25058,25059],{"class":2797}," # Node.js 버전 확인\n",[1273,25061,25063],{"id":25062},"_2-서버-코드-작성","2. 서버 코드 작성",[15,25065,25066,25067,25070],{},"Node.js의 ",[40,25068,25069],{},"http"," 모듈을 사용하여 간단한 HTTP 서버를 만들 수 있습니다.",[33,25072,25074],{"className":1754,"code":25073,"language":1756,"meta":38,"style":38},"\u002F\u002F 필요한 모듈 불러오기\nconst http = require('http');\n\n\u002F\u002F 서버 포트 설정\nconst PORT = 3000;\n\n\u002F\u002F 서버 생성\nconst server = http.createServer((req, res) => {\n    res.writeHead(200, { 'Content-Type': 'text\u002Fplain' });\n    res.end('Hello, Node.js Server!');\n});\n\n\u002F\u002F 서버 실행\nserver.listen(PORT, () => {\n    console.log(`서버가 http:\u002F\u002Flocalhost:${PORT} 에서 실행 중입니다.`);\n});\n",[40,25075,25076,25081,25099,25103,25108,25122,25126,25131,25159,25182,25196,25200,25204,25209,25226,25244],{"__ignoreMap":38},[43,25077,25078],{"class":45,"line":46},[43,25079,25080],{"class":2797},"\u002F\u002F 필요한 모듈 불러오기\n",[43,25082,25083,25085,25088,25090,25092,25094,25097],{"class":45,"line":64},[43,25084,68],{"class":67},[43,25086,25087],{"class":81}," http",[43,25089,128],{"class":67},[43,25091,1770],{"class":57},[43,25093,97],{"class":49},[43,25095,25096],{"class":100},"'http'",[43,25098,1160],{"class":49},[43,25100,25101],{"class":45,"line":119},[43,25102,1782],{"emptyLinePlaceholder":491},[43,25104,25105],{"class":45,"line":150},[43,25106,25107],{"class":2797},"\u002F\u002F 서버 포트 설정\n",[43,25109,25110,25112,25115,25117,25120],{"class":45,"line":159},[43,25111,68],{"class":67},[43,25113,25114],{"class":81}," PORT",[43,25116,128],{"class":67},[43,25118,25119],{"class":81}," 3000",[43,25121,2995],{"class":49},[43,25123,25124],{"class":45,"line":176},[43,25125,1782],{"emptyLinePlaceholder":491},[43,25127,25128],{"class":45,"line":1163},[43,25129,25130],{"class":2797},"\u002F\u002F 서버 생성\n",[43,25132,25133,25135,25137,25139,25142,25145,25147,25149,25151,25153,25155,25157],{"class":45,"line":1178},[43,25134,68],{"class":67},[43,25136,1631],{"class":81},[43,25138,128],{"class":67},[43,25140,25141],{"class":49}," http.",[43,25143,25144],{"class":57},"createServer",[43,25146,4691],{"class":49},[43,25148,3131],{"class":74},[43,25150,104],{"class":49},[43,25152,3136],{"class":74},[43,25154,2408],{"class":49},[43,25156,113],{"class":67},[43,25158,116],{"class":49},[43,25160,25161,25163,25166,25168,25170,25173,25175,25177,25180],{"class":45,"line":1184},[43,25162,3320],{"class":49},[43,25164,25165],{"class":57},"writeHead",[43,25167,97],{"class":49},[43,25169,593],{"class":81},[43,25171,25172],{"class":49},", { ",[43,25174,3245],{"class":100},[43,25176,78],{"class":49},[43,25178,25179],{"class":100},"'text\u002Fplain'",[43,25181,2914],{"class":49},[43,25183,25184,25186,25189,25191,25194],{"class":45,"line":1190},[43,25185,3320],{"class":49},[43,25187,25188],{"class":57},"end",[43,25190,97],{"class":49},[43,25192,25193],{"class":100},"'Hello, Node.js Server!'",[43,25195,1160],{"class":49},[43,25197,25198],{"class":45,"line":1996},[43,25199,1855],{"class":49},[43,25201,25202],{"class":45,"line":2008},[43,25203,1782],{"emptyLinePlaceholder":491},[43,25205,25206],{"class":45,"line":2025},[43,25207,25208],{"class":2797},"\u002F\u002F 서버 실행\n",[43,25210,25211,25214,25216,25218,25220,25222,25224],{"class":45,"line":2031},[43,25212,25213],{"class":49},"server.",[43,25215,3419],{"class":57},[43,25217,97],{"class":49},[43,25219,19107],{"class":81},[43,25221,3427],{"class":49},[43,25223,113],{"class":67},[43,25225,116],{"class":49},[43,25227,25228,25230,25232,25234,25237,25239,25242],{"class":45,"line":2037},[43,25229,2011],{"class":49},[43,25231,1931],{"class":57},[43,25233,97],{"class":49},[43,25235,25236],{"class":100},"`서버가 http:\u002F\u002Flocalhost:${",[43,25238,19107],{"class":81},[43,25240,25241],{"class":100},"} 에서 실행 중입니다.`",[43,25243,1160],{"class":49},[43,25245,25246],{"class":45,"line":2042},[43,25247,1855],{"class":49},[1273,25249,25251],{"id":25250},"_3-서버-실행하기","3. 서버 실행하기",[15,25253,25254,25255,25258],{},"위 코드를 ",[40,25256,25257],{},"server.js"," 파일로 저장한 후, 터미널에서 다음 명령어를 실행합니다.",[33,25260,25262],{"className":850,"code":25261,"language":852,"meta":38,"style":38},"node server.js\n",[40,25263,25264],{"__ignoreMap":38},[43,25265,25266,25268],{"class":45,"line":46},[43,25267,25053],{"class":57},[43,25269,25270],{"class":100}," server.js\n",[15,25272,25273,25274,25277],{},"그러면 ",[40,25275,25276],{},"http:\u002F\u002Flocalhost:3000","에 접속했을 때 \"Hello, Node.js Server!\"라는 응답이 표시됩니다.",[1273,25279,25281],{"id":25280},"_4-express를-사용한-서버-확장","4. Express를 사용한 서버 확장",[15,25283,25284,25285,25290],{},"보다 확장성이 있는 서버를 만들려면 ",[304,25286,25289],{"href":25287,"rel":25288},"https:\u002F\u002Fexpressjs.com\u002F",[308],"Express"," 프레임워크를 사용하는 것이 좋습니다. Express를 이용하면 간결하고 효율적인 서버 코드를 작성할 수 있습니다.",[5145,25292,25294],{"id":25293},"express-설치","Express 설치",[33,25296,25298],{"className":19381,"code":25297,"language":19383,"meta":38,"style":38},"npm init -y  # package.json 생성\nnpm install express  # Express 설치\n",[40,25299,25300,25313],{"__ignoreMap":38},[43,25301,25302,25304,25307,25310],{"class":45,"line":46},[43,25303,1741],{"class":57},[43,25305,25306],{"class":100}," init",[43,25308,25309],{"class":81}," -y",[43,25311,25312],{"class":2797},"  # package.json 생성\n",[43,25314,25315,25317,25319,25321],{"class":45,"line":64},[43,25316,1741],{"class":57},[43,25318,1744],{"class":100},[43,25320,2814],{"class":100},[43,25322,25323],{"class":2797},"  # Express 설치\n",[5145,25325,25327],{"id":25326},"express-서버-코드","Express 서버 코드",[33,25329,25331],{"className":1754,"code":25330,"language":1756,"meta":38,"style":38},"const express = require('express');\nconst app = express();\nconst PORT = 3000;\n\napp.get('\u002F', (req, res) => {\n    res.send('Hello, Express!');\n});\n\napp.listen(PORT, () => {\n    console.log(`Express 서버가 http:\u002F\u002Flocalhost:${PORT} 에서 실행 중입니다.`);\n});\n",[40,25332,25333,25349,25361,25373,25377,25402,25416,25420,25424,25440,25457],{"__ignoreMap":38},[43,25334,25335,25337,25339,25341,25343,25345,25347],{"class":45,"line":46},[43,25336,68],{"class":67},[43,25338,2814],{"class":81},[43,25340,128],{"class":67},[43,25342,1770],{"class":57},[43,25344,97],{"class":49},[43,25346,2823],{"class":100},[43,25348,1160],{"class":49},[43,25350,25351,25353,25355,25357,25359],{"class":45,"line":64},[43,25352,68],{"class":67},[43,25354,2888],{"class":81},[43,25356,128],{"class":67},[43,25358,2814],{"class":57},[43,25360,147],{"class":49},[43,25362,25363,25365,25367,25369,25371],{"class":45,"line":119},[43,25364,68],{"class":67},[43,25366,25114],{"class":81},[43,25368,128],{"class":67},[43,25370,25119],{"class":81},[43,25372,2995],{"class":49},[43,25374,25375],{"class":45,"line":150},[43,25376,1782],{"emptyLinePlaceholder":491},[43,25378,25379,25381,25383,25385,25388,25390,25392,25394,25396,25398,25400],{"class":45,"line":159},[43,25380,3102],{"class":49},[43,25382,8195],{"class":57},[43,25384,97],{"class":49},[43,25386,25387],{"class":100},"'\u002F'",[43,25389,2402],{"class":49},[43,25391,3131],{"class":74},[43,25393,104],{"class":49},[43,25395,3136],{"class":74},[43,25397,2408],{"class":49},[43,25399,113],{"class":67},[43,25401,116],{"class":49},[43,25403,25404,25406,25409,25411,25414],{"class":45,"line":176},[43,25405,3320],{"class":49},[43,25407,25408],{"class":57},"send",[43,25410,97],{"class":49},[43,25412,25413],{"class":100},"'Hello, Express!'",[43,25415,1160],{"class":49},[43,25417,25418],{"class":45,"line":1163},[43,25419,1855],{"class":49},[43,25421,25422],{"class":45,"line":1178},[43,25423,1782],{"emptyLinePlaceholder":491},[43,25425,25426,25428,25430,25432,25434,25436,25438],{"class":45,"line":1184},[43,25427,3102],{"class":49},[43,25429,3419],{"class":57},[43,25431,97],{"class":49},[43,25433,19107],{"class":81},[43,25435,3427],{"class":49},[43,25437,113],{"class":67},[43,25439,116],{"class":49},[43,25441,25442,25444,25446,25448,25451,25453,25455],{"class":45,"line":1190},[43,25443,2011],{"class":49},[43,25445,1931],{"class":57},[43,25447,97],{"class":49},[43,25449,25450],{"class":100},"`Express 서버가 http:\u002F\u002Flocalhost:${",[43,25452,19107],{"class":81},[43,25454,25241],{"class":100},[43,25456,1160],{"class":49},[43,25458,25459],{"class":45,"line":1996},[43,25460,1855],{"class":49},[1273,25462,5518],{"id":5518},[15,25464,25465],{},"이제 Node.js를 이용해 기본적인 서버를 만들고 실행하는 방법을 배웠습니다. Express를 활용하면 더 강력한 기능을 추가할 수도 있습니다. 앞으로는 데이터베이스 연결, API 개발 등 다양한 기능을 추가해볼 수 있습니다!",[471,25467,25468],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":25470},[25471],{"id":25026,"depth":64,"text":25027,"children":25472},[25473,25474,25475,25476,25477],{"id":25033,"depth":119,"text":25034},{"id":25062,"depth":119,"text":25063},{"id":25250,"depth":119,"text":25251},{"id":25280,"depth":119,"text":25281},{"id":5518,"depth":119,"text":5518},"Node.js로 간단한 HTTP 서버를 만드는 방법",{"date":25480,"field":3792,"tags":25481},"2025.01.30",[3795],"\u002Fko\u002Fnode-simple-server",{"title":25021,"description":25478},{"loc":25482},"ko\u002Fnode-simple-server","ytN3mR-jlnR0_zdlBeow7u4FXrD3aufEJmu9BdyVeHM",{"id":25488,"title":25489,"body":25490,"description":26634,"extension":484,"meta":26635,"navigation":491,"path":26638,"seo":26639,"sitemap":26640,"stem":26641,"__hash__":26642},"ko\u002Fko\u002Freact-createportal.md","React createPortal 완벽 가이드",{"type":8,"value":25491,"toc":26600},[25492,25500,25506,25508,25514,25517,25522,25525,25540,25560,25562,25568,25574,25581,25587,25590,25596,25601,25603,25607,25613,25616,25620,25840,25845,25850,25852,25856,25862,25865,25871,25874,25880,25883,25889,25892,25894,25898,25902,26319,26323,26366,26370,26583,26585,26587,26592,26597],[11,25493,25495,25496,25499],{"id":25494},"react-createportal-완벽-가이드","React ",[40,25497,25498],{},"createPortal"," 완벽 가이드",[15,25501,25502,25503,25505],{},"React의 ",[40,25504,25498],{},"은 컴포넌트를 현재의 React 컴포넌트 계층 구조와는 다른 DOM 트리에 렌더링할 수 있도록 해주는 강력한 API입니다.",[26,25507],{},[29,25509,25511,25513],{"id":25510},"createportal이란",[40,25512,25498],{},"이란?",[15,25515,25516],{},"기본적으로 React 컴포넌트는 부모 컴포넌트의 DOM 트리에 자식으로 렌더링됩니다. 하지만 모달, 툴팁, 알림과 같은 특정 UI 요소는 스타일 충돌과 z-index 문제를 피하기 위해 DOM 구조 밖에서 렌더링될 필요가 있습니다.",[15,25518,25519,25521],{},[40,25520,25498],{},"은 이러한 UI 요소를 React 컴포넌트 계층 구조를 유지하면서도 다른 DOM 노드에 렌더링할 수 있도록 도와줍니다.",[1273,25523,25524],{"id":25524},"문법",[33,25526,25528],{"className":24372,"code":25527,"language":24374,"meta":38,"style":38},"ReactDOM.createPortal(child, container)\n",[40,25529,25530],{"__ignoreMap":38},[43,25531,25532,25535,25537],{"class":45,"line":46},[43,25533,25534],{"class":49},"ReactDOM.",[43,25536,25498],{"class":57},[43,25538,25539],{"class":49},"(child, container)\n",[185,25541,25542,25550],{},[188,25543,25544,25549],{},[21,25545,25546],{},[40,25547,25548],{},"child",": 렌더링할 React 엘리먼트나 컴포넌트입니다.",[188,25551,25552,78,25557,25559],{},[21,25553,25554],{},[40,25555,25556],{},"container",[40,25558,25548],{},"를 렌더링할 대상 DOM 엘리먼트입니다.",[26,25561],{},[29,25563,25565,25567],{"id":25564},"createportal을-사용하는-이유",[40,25566,25498],{},"을 사용하는 이유",[1273,25569,1490,25571],{"id":25570},"_1-스타일-충돌-방지",[21,25572,25573],{},"스타일 충돌 방지",[15,25575,25576,25577,25580],{},"모달과 같은 컴포넌트는 부모 요소의 스타일(예: ",[40,25578,25579],{},"overflow: hidden",")에 영향을 받을 수 있습니다. 별도의 DOM 노드에 렌더링하면 이러한 문제를 방지할 수 있습니다.",[1273,25582,1500,25584],{"id":25583},"_2-react-트리-통합-유지",[21,25585,25586],{},"React 트리 통합 유지",[15,25588,25589],{},"컴포넌트가 다른 DOM 트리에 렌더링되더라도 React의 상태와 이벤트 흐름은 정상적으로 유지됩니다.",[1273,25591,1510,25593],{"id":25592},"_3-사용자-경험-개선",[21,25594,25595],{},"사용자 경험 개선",[15,25597,25598,25600],{},[40,25599,25498],{},"을 사용하면 레이아웃과 스타일 제약 없이 UI 요소를 구축할 수 있어 더욱 매끄러운 사용자 경험을 제공합니다.",[26,25602],{},[29,25604,25606],{"id":25605},"주요-특징-및-동작-원리","주요 특징 및 동작 원리",[1273,25608,25610],{"id":25609},"이벤트-버블링",[21,25611,25612],{},"이벤트 버블링",[15,25614,25615],{},"포털 내부에서 발생한 이벤트는 React 컴포넌트 트리를 통해 전파됩니다. 따라서 React의 이벤트 시스템이 방해받지 않습니다.",[5145,25617,25619],{"id":25618},"예제","예제:",[33,25621,25623],{"className":24372,"code":25622,"language":24374,"meta":38,"style":38},"const Modal = ({ children }) => {\n  const modalRoot = document.getElementById('modal-root');\n\n  return ReactDOM.createPortal(\n    \u003Cdiv className=\"modal\" onClick={() => console.log('Modal clicked')}>\n      {children}\n    \u003C\u002Fdiv>,\n    modalRoot\n  );\n};\n\nconst App = () => (\n  \u003Cdiv onClick={() => console.log('App clicked')}>\n    \u003Ch1>React Portal Example\u003C\u002Fh1>\n    \u003CModal>\n      \u003Cbutton>Click Me\u003C\u002Fbutton>\n    \u003C\u002FModal>\n  \u003C\u002Fdiv>\n);\n",[40,25624,25625,25644,25665,25669,25680,25716,25720,25729,25734,25738,25742,25746,25760,25785,25798,25807,25820,25828,25836],{"__ignoreMap":38},[43,25626,25627,25629,25632,25634,25636,25638,25640,25642],{"class":45,"line":46},[43,25628,68],{"class":67},[43,25630,25631],{"class":57}," Modal",[43,25633,128],{"class":67},[43,25635,5134],{"class":49},[43,25637,24435],{"class":74},[43,25639,21296],{"class":49},[43,25641,113],{"class":67},[43,25643,116],{"class":49},[43,25645,25646,25648,25651,25653,25655,25658,25660,25663],{"class":45,"line":64},[43,25647,122],{"class":67},[43,25649,25650],{"class":81}," modalRoot",[43,25652,128],{"class":67},[43,25654,8581],{"class":49},[43,25656,25657],{"class":57},"getElementById",[43,25659,97],{"class":49},[43,25661,25662],{"class":100},"'modal-root'",[43,25664,1160],{"class":49},[43,25666,25667],{"class":45,"line":119},[43,25668,1782],{"emptyLinePlaceholder":491},[43,25670,25671,25673,25676,25678],{"class":45,"line":150},[43,25672,153],{"class":67},[43,25674,25675],{"class":49}," ReactDOM.",[43,25677,25498],{"class":57},[43,25679,2698],{"class":49},[43,25681,25682,25684,25686,25688,25690,25693,25696,25698,25701,25703,25706,25708,25710,25713],{"class":45,"line":159},[43,25683,6726],{"class":49},[43,25685,6703],{"class":53},[43,25687,10440],{"class":57},[43,25689,88],{"class":67},[43,25691,25692],{"class":100},"\"modal\"",[43,25694,25695],{"class":57}," onClick",[43,25697,88],{"class":67},[43,25699,25700],{"class":49},"{() ",[43,25702,113],{"class":67},[43,25704,25705],{"class":49}," console.",[43,25707,1931],{"class":57},[43,25709,97],{"class":49},[43,25711,25712],{"class":100},"'Modal clicked'",[43,25714,25715],{"class":49},")}>\n",[43,25717,25718],{"class":45,"line":176},[43,25719,24559],{"class":49},[43,25721,25722,25724,25726],{"class":45,"line":1163},[43,25723,7378],{"class":49},[43,25725,6703],{"class":53},[43,25727,25728],{"class":49},">,\n",[43,25730,25731],{"class":45,"line":1178},[43,25732,25733],{"class":49},"    modalRoot\n",[43,25735,25736],{"class":45,"line":1184},[43,25737,8515],{"class":49},[43,25739,25740],{"class":45,"line":1190},[43,25741,9424],{"class":49},[43,25743,25744],{"class":45,"line":1996},[43,25745,1782],{"emptyLinePlaceholder":491},[43,25747,25748,25750,25752,25754,25756,25758],{"class":45,"line":2008},[43,25749,68],{"class":67},[43,25751,8427],{"class":57},[43,25753,128],{"class":67},[43,25755,110],{"class":49},[43,25757,113],{"class":67},[43,25759,8437],{"class":49},[43,25761,25762,25764,25766,25768,25770,25772,25774,25776,25778,25780,25783],{"class":45,"line":2025},[43,25763,6700],{"class":49},[43,25765,6703],{"class":53},[43,25767,25695],{"class":57},[43,25769,88],{"class":67},[43,25771,25700],{"class":49},[43,25773,113],{"class":67},[43,25775,25705],{"class":49},[43,25777,1931],{"class":57},[43,25779,97],{"class":49},[43,25781,25782],{"class":100},"'App clicked'",[43,25784,25715],{"class":49},[43,25786,25787,25789,25791,25794,25796],{"class":45,"line":2031},[43,25788,6726],{"class":49},[43,25790,11],{"class":53},[43,25792,25793],{"class":49},">React Portal Example\u003C\u002F",[43,25795,11],{"class":53},[43,25797,61],{"class":49},[43,25799,25800,25802,25805],{"class":45,"line":2037},[43,25801,6726],{"class":49},[43,25803,25804],{"class":81},"Modal",[43,25806,61],{"class":49},[43,25808,25809,25811,25813,25816,25818],{"class":45,"line":2042},[43,25810,7339],{"class":49},[43,25812,10437],{"class":53},[43,25814,25815],{"class":49},">Click Me\u003C\u002F",[43,25817,10437],{"class":53},[43,25819,61],{"class":49},[43,25821,25822,25824,25826],{"class":45,"line":2516},[43,25823,7378],{"class":49},[43,25825,25804],{"class":81},[43,25827,61],{"class":49},[43,25829,25830,25832,25834],{"class":45,"line":2998},[43,25831,6740],{"class":49},[43,25833,6703],{"class":53},[43,25835,61],{"class":49},[43,25837,25838],{"class":45,"line":3003},[43,25839,1160],{"class":49},[15,25841,25842],{},[21,25843,25844],{},"출력:",[185,25846,25847],{},[188,25848,25849],{},"모달 버튼 클릭 시 \"Modal clicked\"와 \"App clicked\"가 모두 출력됩니다. (이벤트 버블링)",[26,25851],{},[29,25853,25855],{"id":25854},"실용적인-활용-사례","실용적인 활용 사례",[1273,25857,1490,25859],{"id":25858},"_1-모달",[21,25860,25861],{},"모달",[15,25863,25864],{},"중요한 메시지 표시나 사용자 입력을 받는 UI 요소.",[1273,25866,1500,25868],{"id":25867},"_2-툴팁",[21,25869,25870],{},"툴팁",[15,25872,25873],{},"부모 컨테이너 스타일의 제약 없이 컨텍스트 정보를 제공.",[1273,25875,1510,25877],{"id":25876},"_3-드롭다운",[21,25878,25879],{},"드롭다운",[15,25881,25882],{},"스크롤이나 부모 요소의 잘림(clipping) 문제 없이 동작하는 메뉴 구현.",[1273,25884,1520,25886],{"id":25885},"_4-알림",[21,25887,25888],{},"알림",[15,25890,25891],{},"전역적으로 표시되는 알림을 DOM의 루트에 렌더링.",[26,25893],{},[29,25895,25897],{"id":25896},"구현-예제","구현 예제",[1273,25899,25901],{"id":25900},"포털을-이용한-모달-컴포넌트","포털을 이용한 모달 컴포넌트",[33,25903,25905],{"className":24372,"code":25904,"language":24374,"meta":38,"style":38},"import React, { useState } from \"react\";\nimport ReactDOM from \"react-dom\";\n\nconst Modal = ({ isOpen, onClose, children }) => {\n  if (!isOpen) return null;\n\n  const modalRoot = document.getElementById(\"modal-root\");\n\n  return ReactDOM.createPortal(\n    \u003Cdiv className=\"modal-overlay\" onClick={onClose}>\n      \u003Cdiv className=\"modal-content\" onClick={(e) => e.stopPropagation()}>\n        {children}\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>,\n    modalRoot\n  );\n};\n\nconst App = () => {\n  const [isModalOpen, setModalOpen] = useState(false);\n\n  return (\n    \u003Cdiv>\n      \u003Ch1>React `createPortal` Example\u003C\u002Fh1>\n      \u003Cbutton onClick={() => setModalOpen(true)}>Open Modal\u003C\u002Fbutton>\n      \u003CModal isOpen={isModalOpen} onClose={() => setModalOpen(false)}>\n        \u003Ch2>Modal Content\u003C\u002Fh2>\n        \u003Cbutton onClick={() => setModalOpen(false)}>Close\u003C\u002Fbutton>\n      \u003C\u002FModal>\n    \u003C\u002Fdiv>\n  );\n};\n\nexport default App;\n",[40,25906,25907,25920,25934,25938,25966,25984,25988,26007,26011,26021,26041,26077,26082,26090,26098,26102,26106,26110,26114,26128,26154,26158,26164,26172,26185,26213,26243,26256,26283,26291,26299,26303,26307,26311],{"__ignoreMap":38},[43,25908,25909,25911,25914,25916,25918],{"class":45,"line":46},[43,25910,7079],{"class":67},[43,25912,25913],{"class":49}," React, { useState } ",[43,25915,7085],{"class":67},[43,25917,9085],{"class":100},[43,25919,2995],{"class":49},[43,25921,25922,25924,25927,25929,25932],{"class":45,"line":64},[43,25923,7079],{"class":67},[43,25925,25926],{"class":49}," ReactDOM ",[43,25928,7085],{"class":67},[43,25930,25931],{"class":100}," \"react-dom\"",[43,25933,2995],{"class":49},[43,25935,25936],{"class":45,"line":119},[43,25937,1782],{"emptyLinePlaceholder":491},[43,25939,25940,25942,25944,25946,25948,25951,25953,25956,25958,25960,25962,25964],{"class":45,"line":150},[43,25941,68],{"class":67},[43,25943,25631],{"class":57},[43,25945,128],{"class":67},[43,25947,5134],{"class":49},[43,25949,25950],{"class":74},"isOpen",[43,25952,104],{"class":49},[43,25954,25955],{"class":74},"onClose",[43,25957,104],{"class":49},[43,25959,24435],{"class":74},[43,25961,21296],{"class":49},[43,25963,113],{"class":67},[43,25965,116],{"class":49},[43,25967,25968,25970,25972,25974,25977,25980,25982],{"class":45,"line":159},[43,25969,3047],{"class":67},[43,25971,1069],{"class":49},[43,25973,3052],{"class":67},[43,25975,25976],{"class":49},"isOpen) ",[43,25978,25979],{"class":67},"return",[43,25981,9225],{"class":81},[43,25983,2995],{"class":49},[43,25985,25986],{"class":45,"line":176},[43,25987,1782],{"emptyLinePlaceholder":491},[43,25989,25990,25992,25994,25996,25998,26000,26002,26005],{"class":45,"line":1163},[43,25991,122],{"class":67},[43,25993,25650],{"class":81},[43,25995,128],{"class":67},[43,25997,8581],{"class":49},[43,25999,25657],{"class":57},[43,26001,97],{"class":49},[43,26003,26004],{"class":100},"\"modal-root\"",[43,26006,1160],{"class":49},[43,26008,26009],{"class":45,"line":1178},[43,26010,1782],{"emptyLinePlaceholder":491},[43,26012,26013,26015,26017,26019],{"class":45,"line":1184},[43,26014,153],{"class":67},[43,26016,25675],{"class":49},[43,26018,25498],{"class":57},[43,26020,2698],{"class":49},[43,26022,26023,26025,26027,26029,26031,26034,26036,26038],{"class":45,"line":1190},[43,26024,6726],{"class":49},[43,26026,6703],{"class":53},[43,26028,10440],{"class":57},[43,26030,88],{"class":67},[43,26032,26033],{"class":100},"\"modal-overlay\"",[43,26035,25695],{"class":57},[43,26037,88],{"class":67},[43,26039,26040],{"class":49},"{onClose}>\n",[43,26042,26043,26045,26047,26049,26051,26054,26056,26058,26061,26064,26066,26068,26071,26074],{"class":45,"line":1996},[43,26044,7339],{"class":49},[43,26046,6703],{"class":53},[43,26048,10440],{"class":57},[43,26050,88],{"class":67},[43,26052,26053],{"class":100},"\"modal-content\"",[43,26055,25695],{"class":57},[43,26057,88],{"class":67},[43,26059,26060],{"class":49},"{(",[43,26062,26063],{"class":74},"e",[43,26065,2408],{"class":49},[43,26067,113],{"class":67},[43,26069,26070],{"class":49}," e.",[43,26072,26073],{"class":57},"stopPropagation",[43,26075,26076],{"class":49},"()}>\n",[43,26078,26079],{"class":45,"line":2008},[43,26080,26081],{"class":49},"        {children}\n",[43,26083,26084,26086,26088],{"class":45,"line":2025},[43,26085,7369],{"class":49},[43,26087,6703],{"class":53},[43,26089,61],{"class":49},[43,26091,26092,26094,26096],{"class":45,"line":2031},[43,26093,7378],{"class":49},[43,26095,6703],{"class":53},[43,26097,25728],{"class":49},[43,26099,26100],{"class":45,"line":2037},[43,26101,25733],{"class":49},[43,26103,26104],{"class":45,"line":2042},[43,26105,8515],{"class":49},[43,26107,26108],{"class":45,"line":2516},[43,26109,9424],{"class":49},[43,26111,26112],{"class":45,"line":2998},[43,26113,1782],{"emptyLinePlaceholder":491},[43,26115,26116,26118,26120,26122,26124,26126],{"class":45,"line":3003},[43,26117,68],{"class":67},[43,26119,8427],{"class":57},[43,26121,128],{"class":67},[43,26123,110],{"class":49},[43,26125,113],{"class":67},[43,26127,116],{"class":49},[43,26129,26130,26132,26134,26137,26139,26142,26144,26146,26148,26150,26152],{"class":45,"line":3009},[43,26131,122],{"class":67},[43,26133,1169],{"class":49},[43,26135,26136],{"class":81},"isModalOpen",[43,26138,104],{"class":49},[43,26140,26141],{"class":81},"setModalOpen",[43,26143,9151],{"class":49},[43,26145,88],{"class":67},[43,26147,9156],{"class":57},[43,26149,97],{"class":49},[43,26151,1830],{"class":81},[43,26153,1160],{"class":49},[43,26155,26156],{"class":45,"line":3022},[43,26157,1782],{"emptyLinePlaceholder":491},[43,26159,26160,26162],{"class":45,"line":3044},[43,26161,153],{"class":67},[43,26163,8437],{"class":49},[43,26165,26166,26168,26170],{"class":45,"line":3058},[43,26167,6726],{"class":49},[43,26169,6703],{"class":53},[43,26171,61],{"class":49},[43,26173,26174,26176,26178,26181,26183],{"class":45,"line":3077},[43,26175,7339],{"class":49},[43,26177,11],{"class":53},[43,26179,26180],{"class":49},">React `createPortal` Example\u003C\u002F",[43,26182,11],{"class":53},[43,26184,61],{"class":49},[43,26186,26187,26189,26191,26193,26195,26197,26199,26202,26204,26206,26209,26211],{"class":45,"line":3082},[43,26188,7339],{"class":49},[43,26190,10437],{"class":53},[43,26192,25695],{"class":57},[43,26194,88],{"class":67},[43,26196,25700],{"class":49},[43,26198,113],{"class":67},[43,26200,26201],{"class":57}," setModalOpen",[43,26203,97],{"class":49},[43,26205,1053],{"class":81},[43,26207,26208],{"class":49},")}>Open Modal\u003C\u002F",[43,26210,10437],{"class":53},[43,26212,61],{"class":49},[43,26214,26215,26217,26219,26222,26224,26227,26229,26231,26233,26235,26237,26239,26241],{"class":45,"line":3088},[43,26216,7339],{"class":49},[43,26218,25804],{"class":81},[43,26220,26221],{"class":57}," isOpen",[43,26223,88],{"class":67},[43,26225,26226],{"class":49},"{isModalOpen} ",[43,26228,25955],{"class":57},[43,26230,88],{"class":67},[43,26232,25700],{"class":49},[43,26234,113],{"class":67},[43,26236,26201],{"class":57},[43,26238,97],{"class":49},[43,26240,1830],{"class":81},[43,26242,25715],{"class":49},[43,26244,26245,26247,26249,26252,26254],{"class":45,"line":3093},[43,26246,8017],{"class":49},[43,26248,29],{"class":53},[43,26250,26251],{"class":49},">Modal Content\u003C\u002F",[43,26253,29],{"class":53},[43,26255,61],{"class":49},[43,26257,26258,26260,26262,26264,26266,26268,26270,26272,26274,26276,26279,26281],{"class":45,"line":3099},[43,26259,8017],{"class":49},[43,26261,10437],{"class":53},[43,26263,25695],{"class":57},[43,26265,88],{"class":67},[43,26267,25700],{"class":49},[43,26269,113],{"class":67},[43,26271,26201],{"class":57},[43,26273,97],{"class":49},[43,26275,1830],{"class":81},[43,26277,26278],{"class":49},")}>Close\u003C\u002F",[43,26280,10437],{"class":53},[43,26282,61],{"class":49},[43,26284,26285,26287,26289],{"class":45,"line":3145},[43,26286,7369],{"class":49},[43,26288,25804],{"class":81},[43,26290,61],{"class":49},[43,26292,26293,26295,26297],{"class":45,"line":3152},[43,26294,7378],{"class":49},[43,26296,6703],{"class":53},[43,26298,61],{"class":49},[43,26300,26301],{"class":45,"line":3165},[43,26302,8515],{"class":49},[43,26304,26305],{"class":45,"line":3203},[43,26306,9424],{"class":49},[43,26308,26309],{"class":45,"line":3208},[43,26310,1782],{"emptyLinePlaceholder":491},[43,26312,26313,26315,26317],{"class":45,"line":3219},[43,26314,6052],{"class":67},[43,26316,6055],{"class":67},[43,26318,24968],{"class":49},[1273,26320,26322],{"id":26321},"html-구조","HTML 구조",[33,26324,26326],{"className":11009,"code":26325,"language":11011,"meta":38,"style":38},"\u003Cdiv id=\"root\">\u003C\u002Fdiv>\n\u003Cdiv id=\"modal-root\">\u003C\u002Fdiv>\n",[40,26327,26328,26348],{"__ignoreMap":38},[43,26329,26330,26332,26334,26337,26339,26342,26344,26346],{"class":45,"line":46},[43,26331,50],{"class":49},[43,26333,6703],{"class":53},[43,26335,26336],{"class":57}," id",[43,26338,88],{"class":49},[43,26340,26341],{"class":100},"\"root\"",[43,26343,15178],{"class":49},[43,26345,6703],{"class":53},[43,26347,61],{"class":49},[43,26349,26350,26352,26354,26356,26358,26360,26362,26364],{"class":45,"line":64},[43,26351,50],{"class":49},[43,26353,6703],{"class":53},[43,26355,26336],{"class":57},[43,26357,88],{"class":49},[43,26359,26004],{"class":100},[43,26361,15178],{"class":49},[43,26363,6703],{"class":53},[43,26365,61],{"class":49},[1273,26367,26369],{"id":26368},"모달-css","모달 CSS",[33,26371,26373],{"className":10460,"code":26372,"language":10462,"meta":38,"style":38},".modal-overlay {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  background: rgba(0, 0, 0, 0.5);\n  display: flex;\n  justify-content: center;\n  align-items: center;\n}\n\n.modal-content {\n  background: white;\n  padding: 20px;\n  border-radius: 8px;\n  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n}\n",[40,26374,26375,26382,26393,26403,26413,26425,26437,26463,26473,26483,26494,26498,26502,26509,26519,26531,26543,26579],{"__ignoreMap":38},[43,26376,26377,26380],{"class":45,"line":46},[43,26378,26379],{"class":57},".modal-overlay",[43,26381,116],{"class":49},[43,26383,26384,26386,26388,26391],{"class":45,"line":64},[43,26385,11636],{"class":81},[43,26387,78],{"class":49},[43,26389,26390],{"class":81},"fixed",[43,26392,2995],{"class":49},[43,26394,26395,26397,26399,26401],{"class":45,"line":119},[43,26396,11698],{"class":81},[43,26398,78],{"class":49},[43,26400,4401],{"class":81},[43,26402,2995],{"class":49},[43,26404,26405,26407,26409,26411],{"class":45,"line":150},[43,26406,11709],{"class":81},[43,26408,78],{"class":49},[43,26410,4401],{"class":81},[43,26412,2995],{"class":49},[43,26414,26415,26417,26419,26421,26423],{"class":45,"line":159},[43,26416,11590],{"class":81},[43,26418,78],{"class":49},[43,26420,11595],{"class":81},[43,26422,11598],{"class":67},[43,26424,2995],{"class":49},[43,26426,26427,26429,26431,26433,26435],{"class":45,"line":176},[43,26428,11731],{"class":81},[43,26430,78],{"class":49},[43,26432,11595],{"class":81},[43,26434,11598],{"class":67},[43,26436,2995],{"class":49},[43,26438,26439,26441,26443,26445,26447,26449,26451,26453,26455,26457,26459,26461],{"class":45,"line":1163},[43,26440,11745],{"class":81},[43,26442,78],{"class":49},[43,26444,16568],{"class":81},[43,26446,97],{"class":49},[43,26448,4401],{"class":81},[43,26450,104],{"class":49},[43,26452,4401],{"class":81},[43,26454,104],{"class":49},[43,26456,4401],{"class":81},[43,26458,104],{"class":49},[43,26460,15488],{"class":81},[43,26462,1160],{"class":49},[43,26464,26465,26467,26469,26471],{"class":45,"line":1178},[43,26466,12069],{"class":81},[43,26468,78],{"class":49},[43,26470,15756],{"class":81},[43,26472,2995],{"class":49},[43,26474,26475,26477,26479,26481],{"class":45,"line":1184},[43,26476,15775],{"class":81},[43,26478,78],{"class":49},[43,26480,11800],{"class":81},[43,26482,2995],{"class":49},[43,26484,26485,26488,26490,26492],{"class":45,"line":1190},[43,26486,26487],{"class":81},"  align-items",[43,26489,78],{"class":49},[43,26491,11800],{"class":81},[43,26493,2995],{"class":49},[43,26495,26496],{"class":45,"line":1996},[43,26497,2034],{"class":49},[43,26499,26500],{"class":45,"line":2008},[43,26501,1782],{"emptyLinePlaceholder":491},[43,26503,26504,26507],{"class":45,"line":2025},[43,26505,26506],{"class":57},".modal-content",[43,26508,116],{"class":49},[43,26510,26511,26513,26515,26517],{"class":45,"line":2031},[43,26512,11745],{"class":81},[43,26514,78],{"class":49},[43,26516,10518],{"class":81},[43,26518,2995],{"class":49},[43,26520,26521,26523,26525,26527,26529],{"class":45,"line":2037},[43,26522,10481],{"class":81},[43,26524,78],{"class":49},[43,26526,12033],{"class":81},[43,26528,10489],{"class":67},[43,26530,2995],{"class":49},[43,26532,26533,26535,26537,26539,26541],{"class":45,"line":2042},[43,26534,11533],{"class":81},[43,26536,78],{"class":49},[43,26538,11902],{"class":81},[43,26540,10489],{"class":67},[43,26542,2995],{"class":49},[43,26544,26545,26547,26549,26551,26553,26555,26557,26559,26561,26563,26565,26567,26569,26571,26573,26575,26577],{"class":45,"line":2516},[43,26546,11547],{"class":81},[43,26548,78],{"class":49},[43,26550,4401],{"class":81},[43,26552,14781],{"class":81},[43,26554,10489],{"class":67},[43,26556,10086],{"class":81},[43,26558,10489],{"class":67},[43,26560,11563],{"class":81},[43,26562,97],{"class":49},[43,26564,4401],{"class":81},[43,26566,104],{"class":49},[43,26568,4401],{"class":81},[43,26570,104],{"class":49},[43,26572,4401],{"class":81},[43,26574,104],{"class":49},[43,26576,12558],{"class":81},[43,26578,1160],{"class":49},[43,26580,26581],{"class":45,"line":2998},[43,26582,2034],{"class":49},[26,26584],{},[29,26586,10925],{"id":10925},[15,26588,25502,26589,26591],{},[40,26590,25498],{},"은 모달, 툴팁, 알림과 같은 유연하고 강력한 UI 컴포넌트를 구현하는 데 필수적인 API입니다. 이를 통해 DOM 계층 구조의 제약 없이 컴포넌트를 렌더링하면서도 React의 상태 및 이벤트 관리를 유지할 수 있습니다.",[15,26593,26594,26596],{},[40,26595,25498],{},"의 사용법과 사례를 이해하고 적절히 활용하여, 사용성 높은 웹 애플리케이션을 개발해 보세요.",[471,26598,26599],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":26601},[26602,26606,26615,26618,26628,26633],{"id":25510,"depth":64,"text":26603,"children":26604},"createPortal이란?",[26605],{"id":25524,"depth":119,"text":25524},{"id":25564,"depth":64,"text":26607,"children":26608},"createPortal을 사용하는 이유",[26609,26611,26613],{"id":25570,"depth":119,"text":26610},"1. 스타일 충돌 방지",{"id":25583,"depth":119,"text":26612},"2. React 트리 통합 유지",{"id":25592,"depth":119,"text":26614},"3. 사용자 경험 개선",{"id":25605,"depth":64,"text":25606,"children":26616},[26617],{"id":25609,"depth":119,"text":25612},{"id":25854,"depth":64,"text":25855,"children":26619},[26620,26622,26624,26626],{"id":25858,"depth":119,"text":26621},"1. 모달",{"id":25867,"depth":119,"text":26623},"2. 툴팁",{"id":25876,"depth":119,"text":26625},"3. 드롭다운",{"id":25885,"depth":119,"text":26627},"4. 알림",{"id":25896,"depth":64,"text":25897,"children":26629},[26630,26631,26632],{"id":25900,"depth":119,"text":25901},{"id":26321,"depth":119,"text":26322},{"id":26368,"depth":119,"text":26369},{"id":10925,"depth":64,"text":10925},"React의 `createPortal`을 활용한 UI 구현법, 활용 사례, 장단점 및 코드 예제를 소개합니다.",{"date":26636,"field":5954,"tags":26637},"2025.01.27",[8861],"\u002Fko\u002Freact-createportal",{"title":25489,"description":26634},{"loc":26638},"ko\u002Freact-createportal","F4c7b2bcaxU5pPar4buv-AAODHH_MdFmsAR9JnqceH8",{"id":26644,"title":26645,"body":26646,"description":27582,"extension":484,"meta":27583,"navigation":491,"path":27587,"seo":27588,"sitemap":27589,"stem":27590,"__hash__":27591},"ko\u002Fko\u002Fcss-layouts.md","CSS Flexbox 완전 정복 가이드",{"type":8,"value":26647,"toc":27568},[26648,26653,26658,26716,26720,26723,26727,26830,26834,26905,26909,26913,27020,27123,27127,27213,27338,27342,27410,27414,27418,27466,27470,27506,27510,27560,27562,27565],[26649,26650,26652],"h5",{"id":26651},"h1-h1","h1 h1",[507,26654,26655],{},[15,26656,26657],{},"hello",[11005,26659,26660,26683,26702],{},[33,26661,26663],{"className":1024,"code":26662,"language":1026,"meta":38,"style":38},"function(){\n  return 123\n}\n",[40,26664,26665,26672,26679],{"__ignoreMap":38},[43,26666,26667,26669],{"class":45,"line":46},[43,26668,8424],{"class":67},[43,26670,26671],{"class":49},"(){\n",[43,26673,26674,26676],{"class":45,"line":64},[43,26675,153],{"class":67},[43,26677,26678],{"class":81}," 123\n",[43,26680,26681],{"class":45,"line":119},[43,26682,2034],{"class":49},[33,26684,26687],{"className":11009,"code":26685,"filename":26686,"language":11011,"meta":38,"style":38},"\u003Cdiv>div\u003C\u002Fdiv>\n","pnpm",[40,26688,26689],{"__ignoreMap":38},[43,26690,26691,26693,26695,26698,26700],{"class":45,"line":46},[43,26692,50],{"class":49},[43,26694,6703],{"class":53},[43,26696,26697],{"class":49},">div\u003C\u002F",[43,26699,6703],{"class":53},[43,26701,61],{"class":49},[33,26703,26705],{"className":850,"code":26704,"filename":26686,"language":852,"meta":38,"style":38},"pnpm add @nuxt\u002Fui\n",[40,26706,26707],{"__ignoreMap":38},[43,26708,26709,26711,26713],{"class":45,"line":46},[43,26710,26686],{"class":57},[43,26712,13130],{"class":100},[43,26714,26715],{"class":100}," @nuxt\u002Fui\n",[11,26717,26719],{"id":26718},"css-flexbox-완전-정복-가이드","CSS Flexbox 완전 정복 가이드 🚀",[15,26721,26722],{},"Flexbox는 현대적인 웹 개발에서 필수적인 CSS 레이아웃 기법입니다. 이 가이드에서는 Flexbox의 핵심 개념부터 실전 예제까지 폭넓게 다룹니다.",[29,26724,26726],{"id":26725},"_1-flex-container-기본-설정","📌 1. Flex Container 기본 설정",[33,26728,26730],{"className":10460,"code":26729,"language":10462,"meta":38,"style":38},"\u002F* 부모 요소 *\u002F\n.container {\n  display: flex;\n  flex-direction: row; \u002F* 주 축 방향 *\u002F\n  justify-content: center; \u002F* 주 축 정렬 *\u002F\n  align-items: center; \u002F* 교차 축 정렬 *\u002F\n  gap: 20px; \u002F* 아이템 간격 *\u002F\n  flex-wrap: wrap; \u002F* 줄 바꿈 *\u002F\n}\n",[40,26731,26732,26737,26744,26754,26769,26782,26795,26811,26826],{"__ignoreMap":38},[43,26733,26734],{"class":45,"line":46},[43,26735,26736],{"class":2797},"\u002F* 부모 요소 *\u002F\n",[43,26738,26739,26742],{"class":45,"line":64},[43,26740,26741],{"class":57},".container",[43,26743,116],{"class":49},[43,26745,26746,26748,26750,26752],{"class":45,"line":119},[43,26747,12069],{"class":81},[43,26749,78],{"class":49},[43,26751,15756],{"class":81},[43,26753,2995],{"class":49},[43,26755,26756,26758,26760,26763,26766],{"class":45,"line":150},[43,26757,15763],{"class":81},[43,26759,78],{"class":49},[43,26761,26762],{"class":81},"row",[43,26764,26765],{"class":49},"; ",[43,26767,26768],{"class":2797},"\u002F* 주 축 방향 *\u002F\n",[43,26770,26771,26773,26775,26777,26779],{"class":45,"line":159},[43,26772,15775],{"class":81},[43,26774,78],{"class":49},[43,26776,11800],{"class":81},[43,26778,26765],{"class":49},[43,26780,26781],{"class":2797},"\u002F* 주 축 정렬 *\u002F\n",[43,26783,26784,26786,26788,26790,26792],{"class":45,"line":176},[43,26785,26487],{"class":81},[43,26787,78],{"class":49},[43,26789,11800],{"class":81},[43,26791,26765],{"class":49},[43,26793,26794],{"class":2797},"\u002F* 교차 축 정렬 *\u002F\n",[43,26796,26797,26800,26802,26804,26806,26808],{"class":45,"line":1163},[43,26798,26799],{"class":81},"  gap",[43,26801,78],{"class":49},[43,26803,12033],{"class":81},[43,26805,10489],{"class":67},[43,26807,26765],{"class":49},[43,26809,26810],{"class":2797},"\u002F* 아이템 간격 *\u002F\n",[43,26812,26813,26816,26818,26821,26823],{"class":45,"line":1178},[43,26814,26815],{"class":81},"  flex-wrap",[43,26817,78],{"class":49},[43,26819,26820],{"class":81},"wrap",[43,26822,26765],{"class":49},[43,26824,26825],{"class":2797},"\u002F* 줄 바꿈 *\u002F\n",[43,26827,26828],{"class":45,"line":1184},[43,26829,2034],{"class":49},[29,26831,26833],{"id":26832},"_2-flex-item-속성","🎯 2. Flex Item 속성",[33,26835,26837],{"className":10460,"code":26836,"language":10462,"meta":38,"style":38},"\u002F* 자식 요소 *\u002F\n.item {\n  flex: 1 1 200px; \u002F* grow | shrink | basis *\u002F\n  order: 2; \u002F* 순서 조정 *\u002F\n  align-self: flex-end; \u002F* 개별 정렬 *\u002F\n}\n",[40,26838,26839,26844,26851,26872,26886,26901],{"__ignoreMap":38},[43,26840,26841],{"class":45,"line":46},[43,26842,26843],{"class":2797},"\u002F* 자식 요소 *\u002F\n",[43,26845,26846,26849],{"class":45,"line":64},[43,26847,26848],{"class":57},".item",[43,26850,116],{"class":49},[43,26852,26853,26856,26858,26860,26862,26865,26867,26869],{"class":45,"line":119},[43,26854,26855],{"class":81},"  flex",[43,26857,78],{"class":49},[43,26859,905],{"class":81},[43,26861,15990],{"class":81},[43,26863,26864],{"class":81}," 200",[43,26866,10489],{"class":67},[43,26868,26765],{"class":49},[43,26870,26871],{"class":2797},"\u002F* grow | shrink | basis *\u002F\n",[43,26873,26874,26877,26879,26881,26883],{"class":45,"line":150},[43,26875,26876],{"class":81},"  order",[43,26878,78],{"class":49},[43,26880,918],{"class":81},[43,26882,26765],{"class":49},[43,26884,26885],{"class":2797},"\u002F* 순서 조정 *\u002F\n",[43,26887,26888,26891,26893,26896,26898],{"class":45,"line":159},[43,26889,26890],{"class":81},"  align-self",[43,26892,78],{"class":49},[43,26894,26895],{"class":81},"flex-end",[43,26897,26765],{"class":49},[43,26899,26900],{"class":2797},"\u002F* 개별 정렬 *\u002F\n",[43,26902,26903],{"class":45,"line":176},[43,26904,2034],{"class":49},[29,26906,26908],{"id":26907},"️-3-실전-레이아웃-예제","🏗️ 3. 실전 레이아웃 예제",[1273,26910,26912],{"id":26911},"_3-1-반응형-네비게이션-바-만들기","3-1. 반응형 네비게이션 바 만들기",[33,26914,26916],{"className":11009,"code":26915,"language":11011,"meta":38,"style":38},"\u003Cnav class=\"flex-nav\">\n  \u003Cdiv class=\"logo\">LOGO\u003C\u002Fdiv>\n  \u003Cdiv class=\"menu\">Home\u003C\u002Fdiv>\n  \u003Cdiv class=\"menu\">About\u003C\u002Fdiv>\n  \u003Cdiv class=\"menu\">Contact\u003C\u002Fdiv>\n\u003C\u002Fnav>\n",[40,26917,26918,26934,26954,26974,26993,27012],{"__ignoreMap":38},[43,26919,26920,26922,26925,26927,26929,26932],{"class":45,"line":46},[43,26921,50],{"class":49},[43,26923,26924],{"class":53},"nav",[43,26926,6714],{"class":57},[43,26928,88],{"class":49},[43,26930,26931],{"class":100},"\"flex-nav\"",[43,26933,61],{"class":49},[43,26935,26936,26938,26940,26942,26944,26947,26950,26952],{"class":45,"line":64},[43,26937,6700],{"class":49},[43,26939,6703],{"class":53},[43,26941,6714],{"class":57},[43,26943,88],{"class":49},[43,26945,26946],{"class":100},"\"logo\"",[43,26948,26949],{"class":49},">LOGO\u003C\u002F",[43,26951,6703],{"class":53},[43,26953,61],{"class":49},[43,26955,26956,26958,26960,26962,26964,26967,26970,26972],{"class":45,"line":119},[43,26957,6700],{"class":49},[43,26959,6703],{"class":53},[43,26961,6714],{"class":57},[43,26963,88],{"class":49},[43,26965,26966],{"class":100},"\"menu\"",[43,26968,26969],{"class":49},">Home\u003C\u002F",[43,26971,6703],{"class":53},[43,26973,61],{"class":49},[43,26975,26976,26978,26980,26982,26984,26986,26989,26991],{"class":45,"line":150},[43,26977,6700],{"class":49},[43,26979,6703],{"class":53},[43,26981,6714],{"class":57},[43,26983,88],{"class":49},[43,26985,26966],{"class":100},[43,26987,26988],{"class":49},">About\u003C\u002F",[43,26990,6703],{"class":53},[43,26992,61],{"class":49},[43,26994,26995,26997,26999,27001,27003,27005,27008,27010],{"class":45,"line":159},[43,26996,6700],{"class":49},[43,26998,6703],{"class":53},[43,27000,6714],{"class":57},[43,27002,88],{"class":49},[43,27004,26966],{"class":100},[43,27006,27007],{"class":49},">Contact\u003C\u002F",[43,27009,6703],{"class":53},[43,27011,61],{"class":49},[43,27013,27014,27016,27018],{"class":45,"line":176},[43,27015,179],{"class":49},[43,27017,26924],{"class":53},[43,27019,61],{"class":49},[33,27021,27023],{"className":10460,"code":27022,"language":10462,"meta":38,"style":38},".flex-nav {\n  display: flex;\n  justify-content: space-between;\n  padding: 1rem;\n  background: #f5f5f5;\n}\n.menu {\n  flex: 1;\n  text-align: center;\n  padding: 12px;\n}\n",[40,27024,27025,27032,27042,27052,27065,27076,27080,27087,27097,27107,27119],{"__ignoreMap":38},[43,27026,27027,27030],{"class":45,"line":46},[43,27028,27029],{"class":57},".flex-nav",[43,27031,116],{"class":49},[43,27033,27034,27036,27038,27040],{"class":45,"line":64},[43,27035,12069],{"class":81},[43,27037,78],{"class":49},[43,27039,15756],{"class":81},[43,27041,2995],{"class":49},[43,27043,27044,27046,27048,27050],{"class":45,"line":119},[43,27045,15775],{"class":81},[43,27047,78],{"class":49},[43,27049,15780],{"class":81},[43,27051,2995],{"class":49},[43,27053,27054,27056,27058,27060,27063],{"class":45,"line":150},[43,27055,10481],{"class":81},[43,27057,78],{"class":49},[43,27059,905],{"class":81},[43,27061,27062],{"class":67},"rem",[43,27064,2995],{"class":49},[43,27066,27067,27069,27071,27074],{"class":45,"line":159},[43,27068,11745],{"class":81},[43,27070,78],{"class":49},[43,27072,27073],{"class":81},"#f5f5f5",[43,27075,2995],{"class":49},[43,27077,27078],{"class":45,"line":176},[43,27079,2034],{"class":49},[43,27081,27082,27085],{"class":45,"line":1163},[43,27083,27084],{"class":57},".menu",[43,27086,116],{"class":49},[43,27088,27089,27091,27093,27095],{"class":45,"line":1178},[43,27090,26855],{"class":81},[43,27092,78],{"class":49},[43,27094,905],{"class":81},[43,27096,2995],{"class":49},[43,27098,27099,27101,27103,27105],{"class":45,"line":1184},[43,27100,11795],{"class":81},[43,27102,78],{"class":49},[43,27104,11800],{"class":81},[43,27106,2995],{"class":49},[43,27108,27109,27111,27113,27115,27117],{"class":45,"line":1190},[43,27110,10481],{"class":81},[43,27112,78],{"class":49},[43,27114,12358],{"class":81},[43,27116,10489],{"class":67},[43,27118,2995],{"class":49},[43,27120,27121],{"class":45,"line":1996},[43,27122,2034],{"class":49},[1273,27124,27126],{"id":27125},"_3-2-3열-카드-그리드","3-2. 3열 카드 그리드",[33,27128,27130],{"className":11009,"code":27129,"language":11011,"meta":38,"style":38},"\u003Cdiv class=\"card-grid\">\n  \u003Cdiv class=\"card\">Card 1\u003C\u002Fdiv>\n  \u003Cdiv class=\"card\">Card 2\u003C\u002Fdiv>\n  \u003Cdiv class=\"card\">Card 3\u003C\u002Fdiv>\n\u003C\u002Fdiv>\n",[40,27131,27132,27147,27167,27186,27205],{"__ignoreMap":38},[43,27133,27134,27136,27138,27140,27142,27145],{"class":45,"line":46},[43,27135,50],{"class":49},[43,27137,6703],{"class":53},[43,27139,6714],{"class":57},[43,27141,88],{"class":49},[43,27143,27144],{"class":100},"\"card-grid\"",[43,27146,61],{"class":49},[43,27148,27149,27151,27153,27155,27157,27160,27163,27165],{"class":45,"line":64},[43,27150,6700],{"class":49},[43,27152,6703],{"class":53},[43,27154,6714],{"class":57},[43,27156,88],{"class":49},[43,27158,27159],{"class":100},"\"card\"",[43,27161,27162],{"class":49},">Card 1\u003C\u002F",[43,27164,6703],{"class":53},[43,27166,61],{"class":49},[43,27168,27169,27171,27173,27175,27177,27179,27182,27184],{"class":45,"line":119},[43,27170,6700],{"class":49},[43,27172,6703],{"class":53},[43,27174,6714],{"class":57},[43,27176,88],{"class":49},[43,27178,27159],{"class":100},[43,27180,27181],{"class":49},">Card 2\u003C\u002F",[43,27183,6703],{"class":53},[43,27185,61],{"class":49},[43,27187,27188,27190,27192,27194,27196,27198,27201,27203],{"class":45,"line":150},[43,27189,6700],{"class":49},[43,27191,6703],{"class":53},[43,27193,6714],{"class":57},[43,27195,88],{"class":49},[43,27197,27159],{"class":100},[43,27199,27200],{"class":49},">Card 3\u003C\u002F",[43,27202,6703],{"class":53},[43,27204,61],{"class":49},[43,27206,27207,27209,27211],{"class":45,"line":159},[43,27208,179],{"class":49},[43,27210,6703],{"class":53},[43,27212,61],{"class":49},[33,27214,27216],{"className":10460,"code":27215,"language":10462,"meta":38,"style":38},".card-grid {\n  display: flex;\n  gap: 15px;\n  padding: 20px;\n}\n.card {\n  flex: 1;\n  min-width: 250px;\n  padding: 20px;\n  border: 1px solid #ddd;\n  border-radius: 8px;\n}\n",[40,27217,27218,27225,27235,27247,27259,27263,27270,27280,27293,27305,27322,27334],{"__ignoreMap":38},[43,27219,27220,27223],{"class":45,"line":46},[43,27221,27222],{"class":57},".card-grid",[43,27224,116],{"class":49},[43,27226,27227,27229,27231,27233],{"class":45,"line":64},[43,27228,12069],{"class":81},[43,27230,78],{"class":49},[43,27232,15756],{"class":81},[43,27234,2995],{"class":49},[43,27236,27237,27239,27241,27243,27245],{"class":45,"line":119},[43,27238,26799],{"class":81},[43,27240,78],{"class":49},[43,27242,12099],{"class":81},[43,27244,10489],{"class":67},[43,27246,2995],{"class":49},[43,27248,27249,27251,27253,27255,27257],{"class":45,"line":150},[43,27250,10481],{"class":81},[43,27252,78],{"class":49},[43,27254,12033],{"class":81},[43,27256,10489],{"class":67},[43,27258,2995],{"class":49},[43,27260,27261],{"class":45,"line":159},[43,27262,2034],{"class":49},[43,27264,27265,27268],{"class":45,"line":176},[43,27266,27267],{"class":57},".card",[43,27269,116],{"class":49},[43,27271,27272,27274,27276,27278],{"class":45,"line":1163},[43,27273,26855],{"class":81},[43,27275,78],{"class":49},[43,27277,905],{"class":81},[43,27279,2995],{"class":49},[43,27281,27282,27285,27287,27289,27291],{"class":45,"line":1178},[43,27283,27284],{"class":81},"  min-width",[43,27286,78],{"class":49},[43,27288,20522],{"class":81},[43,27290,10489],{"class":67},[43,27292,2995],{"class":49},[43,27294,27295,27297,27299,27301,27303],{"class":45,"line":1184},[43,27296,10481],{"class":81},[43,27298,78],{"class":49},[43,27300,12033],{"class":81},[43,27302,10489],{"class":67},[43,27304,2995],{"class":49},[43,27306,27307,27309,27311,27313,27315,27317,27320],{"class":45,"line":1190},[43,27308,12334],{"class":81},[43,27310,78],{"class":49},[43,27312,905],{"class":81},[43,27314,10489],{"class":67},[43,27316,12343],{"class":81},[43,27318,27319],{"class":81}," #ddd",[43,27321,2995],{"class":49},[43,27323,27324,27326,27328,27330,27332],{"class":45,"line":1996},[43,27325,11533],{"class":81},[43,27327,78],{"class":49},[43,27329,11902],{"class":81},[43,27331,10489],{"class":67},[43,27333,2995],{"class":49},[43,27335,27336],{"class":45,"line":2008},[43,27337,2034],{"class":49},[29,27339,27341],{"id":27340},"_4-브라우저-호환성-대응","🌍 4. 브라우저 호환성 대응",[33,27343,27345],{"className":10460,"code":27344,"language":10462,"meta":38,"style":38},"\u002F* IE11 대응 *\u002F\n.container {\n  display: -ms-flexbox;\n  -ms-flex-wrap: wrap;\n}\n.item {\n  -ms-flex: 1 0 auto;\n}\n",[40,27346,27347,27352,27358,27369,27380,27384,27390,27406],{"__ignoreMap":38},[43,27348,27349],{"class":45,"line":46},[43,27350,27351],{"class":2797},"\u002F* IE11 대응 *\u002F\n",[43,27353,27354,27356],{"class":45,"line":64},[43,27355,26741],{"class":57},[43,27357,116],{"class":49},[43,27359,27360,27362,27364,27367],{"class":45,"line":119},[43,27361,12069],{"class":81},[43,27363,78],{"class":49},[43,27365,27366],{"class":81},"-ms-flexbox",[43,27368,2995],{"class":49},[43,27370,27371,27374,27376,27378],{"class":45,"line":150},[43,27372,27373],{"class":81},"  -ms-flex-wrap",[43,27375,78],{"class":49},[43,27377,26820],{"class":81},[43,27379,2995],{"class":49},[43,27381,27382],{"class":45,"line":159},[43,27383,2034],{"class":49},[43,27385,27386,27388],{"class":45,"line":176},[43,27387,26848],{"class":57},[43,27389,116],{"class":49},[43,27391,27392,27395,27397,27399,27401,27404],{"class":45,"line":1163},[43,27393,27394],{"class":81},"  -ms-flex",[43,27396,78],{"class":49},[43,27398,905],{"class":81},[43,27400,7645],{"class":81},[43,27402,27403],{"class":81}," auto",[43,27405,2995],{"class":49},[43,27407,27408],{"class":45,"line":1178},[43,27409,2034],{"class":49},[29,27411,27413],{"id":27412},"_5-필수-flexbox-팁","🔥 5. 필수 Flexbox 팁",[1273,27415,27417],{"id":27416},"수직-중앙-정렬-flexbox-활용","✅ 수직 중앙 정렬 (Flexbox 활용)",[33,27419,27421],{"className":10460,"code":27420,"language":10462,"meta":38,"style":38},".container {\n  display: flex;\n  align-items: center;\n  height: 100vh;\n}\n",[40,27422,27423,27429,27439,27449,27462],{"__ignoreMap":38},[43,27424,27425,27427],{"class":45,"line":46},[43,27426,26741],{"class":57},[43,27428,116],{"class":49},[43,27430,27431,27433,27435,27437],{"class":45,"line":64},[43,27432,12069],{"class":81},[43,27434,78],{"class":49},[43,27436,15756],{"class":81},[43,27438,2995],{"class":49},[43,27440,27441,27443,27445,27447],{"class":45,"line":119},[43,27442,26487],{"class":81},[43,27444,78],{"class":49},[43,27446,11800],{"class":81},[43,27448,2995],{"class":49},[43,27450,27451,27453,27455,27457,27460],{"class":45,"line":150},[43,27452,11731],{"class":81},[43,27454,78],{"class":49},[43,27456,11595],{"class":81},[43,27458,27459],{"class":67},"vh",[43,27461,2995],{"class":49},[43,27463,27464],{"class":45,"line":159},[43,27465,2034],{"class":49},[1273,27467,27469],{"id":27468},"요소-간격-균등-배분","✅ 요소 간격 균등 배분",[33,27471,27473],{"className":10460,"code":27472,"language":10462,"meta":38,"style":38},".container {\n  display: flex;\n  justify-content: space-evenly;\n}\n",[40,27474,27475,27481,27491,27502],{"__ignoreMap":38},[43,27476,27477,27479],{"class":45,"line":46},[43,27478,26741],{"class":57},[43,27480,116],{"class":49},[43,27482,27483,27485,27487,27489],{"class":45,"line":64},[43,27484,12069],{"class":81},[43,27486,78],{"class":49},[43,27488,15756],{"class":81},[43,27490,2995],{"class":49},[43,27492,27493,27495,27497,27500],{"class":45,"line":119},[43,27494,15775],{"class":81},[43,27496,78],{"class":49},[43,27498,27499],{"class":81},"space-evenly",[43,27501,2995],{"class":49},[43,27503,27504],{"class":45,"line":150},[43,27505,2034],{"class":49},[1273,27507,27509],{"id":27508},"반응형-레이아웃-최적화","✅ 반응형 레이아웃 최적화",[33,27511,27513],{"className":10460,"code":27512,"language":10462,"meta":38,"style":38},"@media (max-width: 768px) {\n  .container {\n    flex-direction: column;\n  }\n}\n",[40,27514,27515,27534,27541,27552,27556],{"__ignoreMap":38},[43,27516,27517,27520,27522,27525,27527,27530,27532],{"class":45,"line":46},[43,27518,27519],{"class":67},"@media",[43,27521,1069],{"class":49},[43,27523,27524],{"class":81},"max-width",[43,27526,78],{"class":49},[43,27528,27529],{"class":81},"768",[43,27531,10489],{"class":67},[43,27533,1882],{"class":49},[43,27535,27536,27539],{"class":45,"line":64},[43,27537,27538],{"class":57},"  .container",[43,27540,116],{"class":49},[43,27542,27543,27546,27548,27550],{"class":45,"line":119},[43,27544,27545],{"class":81},"    flex-direction",[43,27547,78],{"class":49},[43,27549,15768],{"class":81},[43,27551,2995],{"class":49},[43,27553,27554],{"class":45,"line":150},[43,27555,2028],{"class":49},[43,27557,27558],{"class":45,"line":159},[43,27559,2034],{"class":49},[26,27561],{},[15,27563,27564],{},"이 가이드를 통해 Flexbox의 핵심을 이해하고 다양한 레이아웃을 자유롭게 구성해보세요! 🎨💡",[471,27566,27567],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":38,"searchDepth":64,"depth":64,"links":27569},[27570,27571,27572,27576,27577],{"id":26725,"depth":64,"text":26726},{"id":26832,"depth":64,"text":26833},{"id":26907,"depth":64,"text":26908,"children":27573},[27574,27575],{"id":26911,"depth":119,"text":26912},{"id":27125,"depth":119,"text":27126},{"id":27340,"depth":64,"text":27341},{"id":27412,"depth":64,"text":27413,"children":27578},[27579,27580,27581],{"id":27416,"depth":119,"text":27417},{"id":27468,"depth":119,"text":27469},{"id":27508,"depth":119,"text":27509},"Flexbox를 활용한 반응형 레이아웃 구현 방법과 실전 예제. 수직 정렬, 네비게이션 바, 카드 레이아웃까지 완벽 정리!",{"date":27584,"image":27585,"field":5954,"tags":27586},"2025.01.25","css.jpg",[10462],"\u002Fko\u002Fcss-layouts",{"title":26645,"description":27582},{"loc":27587},"ko\u002Fcss-layouts","-5eMcgtQ4W6ArY8Sc0E13o7CvMwzo8sf-S5hEvfUP4k",{"id":27593,"title":27594,"body":27595,"description":27594,"extension":484,"meta":28328,"navigation":491,"path":28331,"seo":28332,"sitemap":28333,"stem":28334,"__hash__":28335},"ko\u002Fko\u002Fhtml.md","HTML이란 무엇인가",{"type":8,"value":27596,"toc":28318},[27597,27601,27604,27608,27611,27643,27647,27659,27814,27818,27821,27825,27828,27950,27954,27957,28103,28107,28110,28196,28199,28310,28312,28315],[11,27598,27600],{"id":27599},"html이란","HTML이란?",[15,27602,27603],{},"HTML(HyperText Markup Language)은 웹 페이지를 구성하는 기본적인 마크업 언어입니다. HTML은 문서의 구조를 정의하고, 텍스트, 이미지, 링크, 폼 요소 등을 웹 브라우저에서 표시할 수 있도록 합니다.",[29,27605,27607],{"id":27606},"html의-사용-목적","HTML의 사용 목적",[15,27609,27610],{},"HTML은 주로 웹 페이지를 만들 때 사용되며, 다음과 같은 역할을 합니다.",[185,27612,27613,27619,27625,27631,27637],{},[188,27614,27615,27618],{},[21,27616,27617],{},"문서 구조화",": 제목, 문단, 목록 등 웹 페이지의 기본적인 구조를 정의합니다.",[188,27620,27621,27624],{},[21,27622,27623],{},"콘텐츠 표시",": 텍스트, 이미지, 동영상 등의 콘텐츠를 표시합니다.",[188,27626,27627,27630],{},[21,27628,27629],{},"링크 연결",": 하이퍼링크를 통해 여러 페이지를 연결할 수 있습니다.",[188,27632,27633,27636],{},[21,27634,27635],{},"폼 입력 지원",": 사용자 입력을 받기 위한 폼 요소(input, button, textarea 등)를 제공합니다.",[188,27638,27639,27642],{},[21,27640,27641],{},"SEO 및 접근성 향상",": 검색 엔진 최적화(SEO)와 웹 접근성을 향상시키는 데 도움을 줍니다.",[29,27644,27646],{"id":27645},"html의-기본-구조","HTML의 기본 구조",[15,27648,27649,27650,104,27652,104,27655,27658],{},"HTML 문서는 기본적으로 ",[40,27651,13909],{},[40,27653,27654],{},"\u003Chead>",[40,27656,27657],{},"\u003Cbody>"," 세 가지 주요 부분으로 구성됩니다.",[33,27660,27662],{"className":11009,"code":27661,"language":11011,"meta":38,"style":38},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"ko\">\n\u003Chead>\n    \u003Cmeta charset=\"UTF-8\">\n    \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    \u003Ctitle>HTML 기본 구조\u003C\u002Ftitle>\n\u003C\u002Fhead>\n\u003Cbody>\n    \u003Ch1>안녕하세요!\u003C\u002Fh1>\n    \u003Cp>이것은 HTML 기본 구조입니다.\u003C\u002Fp>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[40,27663,27664,27677,27693,27702,27718,27742,27756,27764,27772,27785,27798,27806],{"__ignoreMap":38},[43,27665,27666,27669,27672,27675],{"class":45,"line":46},[43,27667,27668],{"class":49},"\u003C!",[43,27670,27671],{"class":53},"DOCTYPE",[43,27673,27674],{"class":57}," html",[43,27676,61],{"class":49},[43,27678,27679,27681,27683,27686,27688,27691],{"class":45,"line":64},[43,27680,50],{"class":49},[43,27682,11011],{"class":53},[43,27684,27685],{"class":57}," lang",[43,27687,88],{"class":49},[43,27689,27690],{"class":100},"\"ko\"",[43,27692,61],{"class":49},[43,27694,27695,27697,27700],{"class":45,"line":119},[43,27696,50],{"class":49},[43,27698,27699],{"class":53},"head",[43,27701,61],{"class":49},[43,27703,27704,27706,27708,27711,27713,27716],{"class":45,"line":150},[43,27705,6726],{"class":49},[43,27707,22497],{"class":53},[43,27709,27710],{"class":57}," charset",[43,27712,88],{"class":49},[43,27714,27715],{"class":100},"\"UTF-8\"",[43,27717,61],{"class":49},[43,27719,27720,27722,27724,27727,27729,27732,27735,27737,27740],{"class":45,"line":159},[43,27721,6726],{"class":49},[43,27723,22497],{"class":53},[43,27725,27726],{"class":57}," name",[43,27728,88],{"class":49},[43,27730,27731],{"class":100},"\"viewport\"",[43,27733,27734],{"class":57}," content",[43,27736,88],{"class":49},[43,27738,27739],{"class":100},"\"width=device-width, initial-scale=1.0\"",[43,27741,61],{"class":49},[43,27743,27744,27746,27749,27752,27754],{"class":45,"line":176},[43,27745,6726],{"class":49},[43,27747,27748],{"class":53},"title",[43,27750,27751],{"class":49},">HTML 기본 구조\u003C\u002F",[43,27753,27748],{"class":53},[43,27755,61],{"class":49},[43,27757,27758,27760,27762],{"class":45,"line":1163},[43,27759,179],{"class":49},[43,27761,27699],{"class":53},[43,27763,61],{"class":49},[43,27765,27766,27768,27770],{"class":45,"line":1178},[43,27767,50],{"class":49},[43,27769,13925],{"class":53},[43,27771,61],{"class":49},[43,27773,27774,27776,27778,27781,27783],{"class":45,"line":1184},[43,27775,6726],{"class":49},[43,27777,11],{"class":53},[43,27779,27780],{"class":49},">안녕하세요!\u003C\u002F",[43,27782,11],{"class":53},[43,27784,61],{"class":49},[43,27786,27787,27789,27791,27794,27796],{"class":45,"line":1190},[43,27788,6726],{"class":49},[43,27790,15],{"class":53},[43,27792,27793],{"class":49},">이것은 HTML 기본 구조입니다.\u003C\u002F",[43,27795,15],{"class":53},[43,27797,61],{"class":49},[43,27799,27800,27802,27804],{"class":45,"line":1996},[43,27801,179],{"class":49},[43,27803,13925],{"class":53},[43,27805,61],{"class":49},[43,27807,27808,27810,27812],{"class":45,"line":2008},[43,27809,179],{"class":49},[43,27811,11011],{"class":53},[43,27813,61],{"class":49},[29,27815,27817],{"id":27816},"유용한-html-레이아웃","유용한 HTML 레이아웃",[15,27819,27820],{},"웹 페이지를 만들 때 자주 사용되는 레이아웃 유형을 소개합니다.",[1273,27822,27824],{"id":27823},"_1-단일-페이지-레이아웃","1. 단일 페이지 레이아웃",[15,27826,27827],{},"한 페이지에 모든 정보를 담는 형태로, 간단한 랜딩 페이지나 포트폴리오 사이트에 유용합니다.",[33,27829,27831],{"className":11009,"code":27830,"language":11011,"meta":38,"style":38},"\u003Cdiv class=\"container\">\n    \u003Cheader>\n        \u003Ch1>내 웹사이트\u003C\u002Fh1>\n    \u003C\u002Fheader>\n    \u003Csection>\n        \u003Cp>이곳에 내용을 추가하세요.\u003C\u002Fp>\n    \u003C\u002Fsection>\n    \u003Cfooter>\n        \u003Cp>&copy; 2025 모든 권리 보유\u003C\u002Fp>\n    \u003C\u002Ffooter>\n\u003C\u002Fdiv>\n",[40,27832,27833,27848,27856,27869,27877,27886,27899,27907,27916,27934,27942],{"__ignoreMap":38},[43,27834,27835,27837,27839,27841,27843,27846],{"class":45,"line":46},[43,27836,50],{"class":49},[43,27838,6703],{"class":53},[43,27840,6714],{"class":57},[43,27842,88],{"class":49},[43,27844,27845],{"class":100},"\"container\"",[43,27847,61],{"class":49},[43,27849,27850,27852,27854],{"class":45,"line":64},[43,27851,6726],{"class":49},[43,27853,13564],{"class":53},[43,27855,61],{"class":49},[43,27857,27858,27860,27862,27865,27867],{"class":45,"line":119},[43,27859,8017],{"class":49},[43,27861,11],{"class":53},[43,27863,27864],{"class":49},">내 웹사이트\u003C\u002F",[43,27866,11],{"class":53},[43,27868,61],{"class":49},[43,27870,27871,27873,27875],{"class":45,"line":150},[43,27872,7378],{"class":49},[43,27874,13564],{"class":53},[43,27876,61],{"class":49},[43,27878,27879,27881,27884],{"class":45,"line":159},[43,27880,6726],{"class":49},[43,27882,27883],{"class":53},"section",[43,27885,61],{"class":49},[43,27887,27888,27890,27892,27895,27897],{"class":45,"line":176},[43,27889,8017],{"class":49},[43,27891,15],{"class":53},[43,27893,27894],{"class":49},">이곳에 내용을 추가하세요.\u003C\u002F",[43,27896,15],{"class":53},[43,27898,61],{"class":49},[43,27900,27901,27903,27905],{"class":45,"line":1163},[43,27902,7378],{"class":49},[43,27904,27883],{"class":53},[43,27906,61],{"class":49},[43,27908,27909,27911,27914],{"class":45,"line":1178},[43,27910,6726],{"class":49},[43,27912,27913],{"class":53},"footer",[43,27915,61],{"class":49},[43,27917,27918,27920,27922,27924,27927,27930,27932],{"class":45,"line":1184},[43,27919,8017],{"class":49},[43,27921,15],{"class":53},[43,27923,19390],{"class":49},[43,27925,27926],{"class":81},"&copy;",[43,27928,27929],{"class":49}," 2025 모든 권리 보유\u003C\u002F",[43,27931,15],{"class":53},[43,27933,61],{"class":49},[43,27935,27936,27938,27940],{"class":45,"line":1190},[43,27937,7378],{"class":49},[43,27939,27913],{"class":53},[43,27941,61],{"class":49},[43,27943,27944,27946,27948],{"class":45,"line":1996},[43,27945,179],{"class":49},[43,27947,6703],{"class":53},[43,27949,61],{"class":49},[1273,27951,27953],{"id":27952},"_2-내비게이션-포함-레이아웃","2. 내비게이션 포함 레이아웃",[15,27955,27956],{},"웹사이트의 여러 페이지를 탐색할 수 있도록 내비게이션 메뉴를 포함한 형태입니다.",[33,27958,27960],{"className":11009,"code":27959,"language":11011,"meta":38,"style":38},"\u003Cnav>\n    \u003Cul>\n        \u003Cli>\u003Ca href=\"#\">홈\u003C\u002Fa>\u003C\u002Fli>\n        \u003Cli>\u003Ca href=\"#\">소개\u003C\u002Fa>\u003C\u002Fli>\n        \u003Cli>\u003Ca href=\"#\">서비스\u003C\u002Fa>\u003C\u002Fli>\n        \u003Cli>\u003Ca href=\"#\">연락처\u003C\u002Fa>\u003C\u002Fli>\n    \u003C\u002Ful>\n\u003C\u002Fnav>\n",[40,27961,27962,27970,27978,28006,28033,28060,28087,28095],{"__ignoreMap":38},[43,27963,27964,27966,27968],{"class":45,"line":46},[43,27965,50],{"class":49},[43,27967,26924],{"class":53},[43,27969,61],{"class":49},[43,27971,27972,27974,27976],{"class":45,"line":64},[43,27973,6726],{"class":49},[43,27975,185],{"class":53},[43,27977,61],{"class":49},[43,27979,27980,27982,27984,27987,27989,27991,27993,27995,27998,28000,28002,28004],{"class":45,"line":119},[43,27981,8017],{"class":49},[43,27983,188],{"class":53},[43,27985,27986],{"class":49},">\u003C",[43,27988,304],{"class":53},[43,27990,11464],{"class":57},[43,27992,88],{"class":49},[43,27994,11469],{"class":100},[43,27996,27997],{"class":49},">홈\u003C\u002F",[43,27999,304],{"class":53},[43,28001,15178],{"class":49},[43,28003,188],{"class":53},[43,28005,61],{"class":49},[43,28007,28008,28010,28012,28014,28016,28018,28020,28022,28025,28027,28029,28031],{"class":45,"line":150},[43,28009,8017],{"class":49},[43,28011,188],{"class":53},[43,28013,27986],{"class":49},[43,28015,304],{"class":53},[43,28017,11464],{"class":57},[43,28019,88],{"class":49},[43,28021,11469],{"class":100},[43,28023,28024],{"class":49},">소개\u003C\u002F",[43,28026,304],{"class":53},[43,28028,15178],{"class":49},[43,28030,188],{"class":53},[43,28032,61],{"class":49},[43,28034,28035,28037,28039,28041,28043,28045,28047,28049,28052,28054,28056,28058],{"class":45,"line":159},[43,28036,8017],{"class":49},[43,28038,188],{"class":53},[43,28040,27986],{"class":49},[43,28042,304],{"class":53},[43,28044,11464],{"class":57},[43,28046,88],{"class":49},[43,28048,11469],{"class":100},[43,28050,28051],{"class":49},">서비스\u003C\u002F",[43,28053,304],{"class":53},[43,28055,15178],{"class":49},[43,28057,188],{"class":53},[43,28059,61],{"class":49},[43,28061,28062,28064,28066,28068,28070,28072,28074,28076,28079,28081,28083,28085],{"class":45,"line":176},[43,28063,8017],{"class":49},[43,28065,188],{"class":53},[43,28067,27986],{"class":49},[43,28069,304],{"class":53},[43,28071,11464],{"class":57},[43,28073,88],{"class":49},[43,28075,11469],{"class":100},[43,28077,28078],{"class":49},">연락처\u003C\u002F",[43,28080,304],{"class":53},[43,28082,15178],{"class":49},[43,28084,188],{"class":53},[43,28086,61],{"class":49},[43,28088,28089,28091,28093],{"class":45,"line":1163},[43,28090,7378],{"class":49},[43,28092,185],{"class":53},[43,28094,61],{"class":49},[43,28096,28097,28099,28101],{"class":45,"line":1178},[43,28098,179],{"class":49},[43,28100,26924],{"class":53},[43,28102,61],{"class":49},[1273,28104,28106],{"id":28105},"_3-그리드-레이아웃","3. 그리드 레이아웃",[15,28108,28109],{},"여러 개의 콘텐츠 블록을 균형 있게 배치하는 그리드 기반의 레이아웃입니다.",[33,28111,28113],{"className":11009,"code":28112,"language":11011,"meta":38,"style":38},"\u003Cdiv class=\"grid-container\">\n    \u003Cdiv class=\"grid-item\">콘텐츠 1\u003C\u002Fdiv>\n    \u003Cdiv class=\"grid-item\">콘텐츠 2\u003C\u002Fdiv>\n    \u003Cdiv class=\"grid-item\">콘텐츠 3\u003C\u002Fdiv>\n\u003C\u002Fdiv>\n",[40,28114,28115,28130,28150,28169,28188],{"__ignoreMap":38},[43,28116,28117,28119,28121,28123,28125,28128],{"class":45,"line":46},[43,28118,50],{"class":49},[43,28120,6703],{"class":53},[43,28122,6714],{"class":57},[43,28124,88],{"class":49},[43,28126,28127],{"class":100},"\"grid-container\"",[43,28129,61],{"class":49},[43,28131,28132,28134,28136,28138,28140,28143,28146,28148],{"class":45,"line":64},[43,28133,6726],{"class":49},[43,28135,6703],{"class":53},[43,28137,6714],{"class":57},[43,28139,88],{"class":49},[43,28141,28142],{"class":100},"\"grid-item\"",[43,28144,28145],{"class":49},">콘텐츠 1\u003C\u002F",[43,28147,6703],{"class":53},[43,28149,61],{"class":49},[43,28151,28152,28154,28156,28158,28160,28162,28165,28167],{"class":45,"line":119},[43,28153,6726],{"class":49},[43,28155,6703],{"class":53},[43,28157,6714],{"class":57},[43,28159,88],{"class":49},[43,28161,28142],{"class":100},[43,28163,28164],{"class":49},">콘텐츠 2\u003C\u002F",[43,28166,6703],{"class":53},[43,28168,61],{"class":49},[43,28170,28171,28173,28175,28177,28179,28181,28184,28186],{"class":45,"line":150},[43,28172,6726],{"class":49},[43,28174,6703],{"class":53},[43,28176,6714],{"class":57},[43,28178,88],{"class":49},[43,28180,28142],{"class":100},[43,28182,28183],{"class":49},">콘텐츠 3\u003C\u002F",[43,28185,6703],{"class":53},[43,28187,61],{"class":49},[43,28189,28190,28192,28194],{"class":45,"line":159},[43,28191,179],{"class":49},[43,28193,6703],{"class":53},[43,28195,61],{"class":49},[15,28197,28198],{},"CSS를 사용하면 더욱 깔끔한 그리드 레이아웃을 만들 수 있습니다.",[33,28200,28202],{"className":10460,"code":28201,"language":10462,"meta":38,"style":38},".grid-container {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr);\n    gap: 10px;\n}\n.grid-item {\n    padding: 20px;\n    background-color: lightgray;\n    text-align: center;\n}\n",[40,28203,28204,28211,28223,28246,28259,28263,28270,28283,28295,28306],{"__ignoreMap":38},[43,28205,28206,28209],{"class":45,"line":46},[43,28207,28208],{"class":57},".grid-container",[43,28210,116],{"class":49},[43,28212,28213,28216,28218,28221],{"class":45,"line":64},[43,28214,28215],{"class":81},"    display",[43,28217,78],{"class":49},[43,28219,28220],{"class":81},"grid",[43,28222,2995],{"class":49},[43,28224,28225,28228,28230,28233,28235,28237,28239,28241,28244],{"class":45,"line":119},[43,28226,28227],{"class":81},"    grid-template-columns",[43,28229,78],{"class":49},[43,28231,28232],{"class":81},"repeat",[43,28234,97],{"class":49},[43,28236,931],{"class":81},[43,28238,104],{"class":49},[43,28240,905],{"class":81},[43,28242,28243],{"class":67},"fr",[43,28245,1160],{"class":49},[43,28247,28248,28251,28253,28255,28257],{"class":45,"line":150},[43,28249,28250],{"class":81},"    gap",[43,28252,78],{"class":49},[43,28254,10486],{"class":81},[43,28256,10489],{"class":67},[43,28258,2995],{"class":49},[43,28260,28261],{"class":45,"line":159},[43,28262,2034],{"class":49},[43,28264,28265,28268],{"class":45,"line":176},[43,28266,28267],{"class":57},".grid-item",[43,28269,116],{"class":49},[43,28271,28272,28275,28277,28279,28281],{"class":45,"line":1163},[43,28273,28274],{"class":81},"    padding",[43,28276,78],{"class":49},[43,28278,12033],{"class":81},[43,28280,10489],{"class":67},[43,28282,2995],{"class":49},[43,28284,28285,28288,28290,28293],{"class":45,"line":1178},[43,28286,28287],{"class":81},"    background-color",[43,28289,78],{"class":49},[43,28291,28292],{"class":81},"lightgray",[43,28294,2995],{"class":49},[43,28296,28297,28300,28302,28304],{"class":45,"line":1184},[43,28298,28299],{"class":81},"    text-align",[43,28301,78],{"class":49},[43,28303,11800],{"class":81},[43,28305,2995],{"class":49},[43,28307,28308],{"class":45,"line":1190},[43,28309,2034],{"class":49},[29,28311,10925],{"id":10925},[15,28313,28314],{},"HTML은 웹 개발의 기초가 되는 중요한 언어로, 다양한 요소를 조합하여 웹 페이지를 구성할 수 있습니다. CSS와 JavaScript와 함께 사용하면 더욱 강력한 웹 애플리케이션을 만들 수 있습니다. 이번 글에서는 HTML의 기본 개념과 유용한 레이아웃 예시를 살펴보았습니다. 앞으로 HTML을 활용한 다양한 실전 예제도 다뤄보겠습니다!",[471,28316,28317],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":38,"searchDepth":64,"depth":64,"links":28319},[28320,28321,28322,28327],{"id":27606,"depth":64,"text":27607},{"id":27645,"depth":64,"text":27646},{"id":27816,"depth":64,"text":27817,"children":28323},[28324,28325,28326],{"id":27823,"depth":119,"text":27824},{"id":27952,"depth":119,"text":27953},{"id":28105,"depth":119,"text":28106},{"id":10925,"depth":64,"text":10925},{"date":27584,"image":28329,"field":5954,"tags":28330},"html.jpg",[11011],"\u002Fko\u002Fhtml",{"title":27594,"description":27594},{"loc":28331},"ko\u002Fhtml","LO1b7p5ZqeuwztvAW5EWzM8WZSmqUcXDvkee_aBlmv4",{"id":28337,"title":28338,"body":28339,"description":29202,"extension":484,"meta":29203,"navigation":491,"path":29205,"seo":29206,"sitemap":29207,"stem":29208,"__hash__":29209},"ko\u002Fko\u002Fvue-component-guide.md","Vue의 \u003Ccomponent :is \u002F> 완벽 정리: 동적 렌더링의 모든 것",{"type":8,"value":28340,"toc":29191},[28341,28345,28351,28362,28366,28369,28375,28380,28501,28509,28514,28643,28650,28655,28796,28806,28810,28821,28825,28859,28862,28905,28916,28922,28936,28940,29123,29127,29159,29171,29175,29180,29188],[29,28342,28344],{"id":28343},"component-란","\u003Ccomponent \u002F> 란?",[15,28346,28347,28350],{},[40,28348,28349],{},"\u003Ccomponent :is=\"\">"," 란 Vue에서 동적으로 컴포넌트를 렌더링하는 방법이다. 이 값은 세 가지 형태로 전달될 수 있다",[185,28352,28353,28356,28359],{},[188,28354,28355],{},"컴포넌트 이름 (문자열): 등록된 컴포넌트의 이름.",[188,28357,28358],{},"컴포넌트 정의 객체: 직접 정의한 컴포넌트 객체.",[188,28360,28361],{},"VNode (가상 노드): Vue의 가상 DOM 노드.",[29,28363,28365],{"id":28364},"컴포넌트만-전달이-가능한가","컴포넌트만 전달이 가능한가??",[15,28367,28368],{},"아니다",[15,28370,28371,28374],{},[40,28372,28373],{},"\u003Ccomponent :is \u002F>"," 는 단순히 컴포넌트만 렌더링하는 것이 아니라, 더 유연한 렌더링을 지원한다. 세 가지 경우를 예제와 함께 정리해보자.",[432,28376,28377],{},[188,28378,28379],{},"컴포넌트 이름 (문자열):",[33,28381,28383],{"className":35,"code":28382,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Ccomponent :is=\"currentComponent\" \u002F>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport MyComponent from '.\u002FMyComponent.vue'; \u002F\u002F 별도 파일에서 불러온 컴포넌트\nconst currentComponent = ref('MyComponent');\n\n\u002F\u002F 컴포넌트를 등록해야 동작 (Nuxt에서는 자동 임포트 가능)\ndefineComponents({\n  MyComponent,\n});\n\u003C\u002Fscript>\n",[40,28384,28385,28393,28410,28418,28422,28432,28449,28468,28472,28477,28484,28489,28493],{"__ignoreMap":38},[43,28386,28387,28389,28391],{"class":45,"line":46},[43,28388,50],{"class":49},[43,28390,6693],{"class":53},[43,28392,61],{"class":49},[43,28394,28395,28397,28400,28403,28405,28408],{"class":45,"line":64},[43,28396,6700],{"class":49},[43,28398,28399],{"class":53},"component",[43,28401,28402],{"class":57}," :is",[43,28404,88],{"class":49},[43,28406,28407],{"class":100},"\"currentComponent\"",[43,28409,9034],{"class":49},[43,28411,28412,28414,28416],{"class":45,"line":119},[43,28413,179],{"class":49},[43,28415,6693],{"class":53},[43,28417,61],{"class":49},[43,28419,28420],{"class":45,"line":150},[43,28421,1782],{"emptyLinePlaceholder":491},[43,28423,28424,28426,28428,28430],{"class":45,"line":159},[43,28425,50],{"class":49},[43,28427,54],{"class":53},[43,28429,58],{"class":57},[43,28431,61],{"class":49},[43,28433,28434,28436,28439,28441,28444,28446],{"class":45,"line":176},[43,28435,7079],{"class":67},[43,28437,28438],{"class":49}," MyComponent ",[43,28440,7085],{"class":67},[43,28442,28443],{"class":100}," '.\u002FMyComponent.vue'",[43,28445,26765],{"class":49},[43,28447,28448],{"class":2797},"\u002F\u002F 별도 파일에서 불러온 컴포넌트\n",[43,28450,28451,28453,28456,28458,28461,28463,28466],{"class":45,"line":1163},[43,28452,68],{"class":67},[43,28454,28455],{"class":81}," currentComponent",[43,28457,128],{"class":67},[43,28459,28460],{"class":57}," ref",[43,28462,97],{"class":49},[43,28464,28465],{"class":100},"'MyComponent'",[43,28467,1160],{"class":49},[43,28469,28470],{"class":45,"line":1178},[43,28471,1782],{"emptyLinePlaceholder":491},[43,28473,28474],{"class":45,"line":1184},[43,28475,28476],{"class":2797},"\u002F\u002F 컴포넌트를 등록해야 동작 (Nuxt에서는 자동 임포트 가능)\n",[43,28478,28479,28482],{"class":45,"line":1190},[43,28480,28481],{"class":57},"defineComponents",[43,28483,1803],{"class":49},[43,28485,28486],{"class":45,"line":1996},[43,28487,28488],{"class":49},"  MyComponent,\n",[43,28490,28491],{"class":45,"line":2008},[43,28492,1855],{"class":49},[43,28494,28495,28497,28499],{"class":45,"line":2025},[43,28496,179],{"class":49},[43,28498,54],{"class":53},[43,28500,61],{"class":49},[185,28502,28503],{},[188,28504,28505,28508],{},[40,28506,28507],{},":is","에 문자열 'MyComponent'를 전달하면, Vue는 등록된 컴포넌트를 찾아 렌더링",[432,28510,28511],{"start":64},[188,28512,28513],{},"컴포넌트 객체:",[33,28515,28517],{"className":35,"code":28516,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ccomponent :is=\"dynamicComponent\" \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nconst dynamicComponent = {\n  template: `\u003Cdiv>{{ message }}\u003C\u002Fdiv>`,\n  setup() {\n    return {\n      message: \"직접 정의된 컴포넌트입니다!\",\n    };\n  },\n};\n\u003C\u002Fscript>\n",[40,28518,28519,28527,28535,28550,28558,28566,28570,28580,28591,28601,28608,28614,28623,28627,28631,28635],{"__ignoreMap":38},[43,28520,28521,28523,28525],{"class":45,"line":46},[43,28522,50],{"class":49},[43,28524,6693],{"class":53},[43,28526,61],{"class":49},[43,28528,28529,28531,28533],{"class":45,"line":64},[43,28530,6700],{"class":49},[43,28532,6703],{"class":53},[43,28534,61],{"class":49},[43,28536,28537,28539,28541,28543,28545,28548],{"class":45,"line":119},[43,28538,6726],{"class":49},[43,28540,28399],{"class":53},[43,28542,28402],{"class":57},[43,28544,88],{"class":49},[43,28546,28547],{"class":100},"\"dynamicComponent\"",[43,28549,9034],{"class":49},[43,28551,28552,28554,28556],{"class":45,"line":150},[43,28553,6740],{"class":49},[43,28555,6703],{"class":53},[43,28557,61],{"class":49},[43,28559,28560,28562,28564],{"class":45,"line":159},[43,28561,179],{"class":49},[43,28563,6693],{"class":53},[43,28565,61],{"class":49},[43,28567,28568],{"class":45,"line":176},[43,28569,1782],{"emptyLinePlaceholder":491},[43,28571,28572,28574,28576,28578],{"class":45,"line":1163},[43,28573,50],{"class":49},[43,28575,54],{"class":53},[43,28577,58],{"class":57},[43,28579,61],{"class":49},[43,28581,28582,28584,28587,28589],{"class":45,"line":1178},[43,28583,68],{"class":67},[43,28585,28586],{"class":81}," dynamicComponent",[43,28588,128],{"class":67},[43,28590,116],{"class":49},[43,28592,28593,28596,28599],{"class":45,"line":1184},[43,28594,28595],{"class":49},"  template: ",[43,28597,28598],{"class":100},"`\u003Cdiv>{{ message }}\u003C\u002Fdiv>`",[43,28600,1056],{"class":49},[43,28602,28603,28606],{"class":45,"line":1190},[43,28604,28605],{"class":57},"  setup",[43,28607,8430],{"class":49},[43,28609,28610,28612],{"class":45,"line":1996},[43,28611,2737],{"class":67},[43,28613,116],{"class":49},[43,28615,28616,28618,28621],{"class":45,"line":2008},[43,28617,3340],{"class":49},[43,28619,28620],{"class":100},"\"직접 정의된 컴포넌트입니다!\"",[43,28622,1056],{"class":49},[43,28624,28625],{"class":45,"line":2025},[43,28626,2118],{"class":49},[43,28628,28629],{"class":45,"line":2031},[43,28630,1187],{"class":49},[43,28632,28633],{"class":45,"line":2037},[43,28634,9424],{"class":49},[43,28636,28637,28639,28641],{"class":45,"line":2042},[43,28638,179],{"class":49},[43,28640,54],{"class":53},[43,28642,61],{"class":49},[185,28644,28645],{},[188,28646,28647,28649],{},[40,28648,28507],{},"에 객체를 전달하면, Vue는 이 객체를 컴포넌트로 간주하고 내부 template을 컴파일해 렌더링.",[432,28651,28652],{"start":119},[188,28653,28654],{},"VNode (가상 노드):",[33,28656,28658],{"className":35,"code":28657,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ccomponent v-for=\"node in slotNodes\" :key=\"node.props?.id\" :is=\"node\" \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { useSlots } from 'vue';\nconst slots = useSlots();\nconst slotNodes = slots.default?.() || []; \u002F\u002F 슬롯에서 VNode 배열 가져오기\n\u003C\u002Fscript>\n",[40,28659,28660,28668,28676,28705,28713,28721,28725,28735,28749,28763,28788],{"__ignoreMap":38},[43,28661,28662,28664,28666],{"class":45,"line":46},[43,28663,50],{"class":49},[43,28665,6693],{"class":53},[43,28667,61],{"class":49},[43,28669,28670,28672,28674],{"class":45,"line":64},[43,28671,6700],{"class":49},[43,28673,6703],{"class":53},[43,28675,61],{"class":49},[43,28677,28678,28680,28682,28684,28686,28689,28691,28693,28696,28698,28700,28703],{"class":45,"line":119},[43,28679,6726],{"class":49},[43,28681,28399],{"class":53},[43,28683,7344],{"class":57},[43,28685,88],{"class":49},[43,28687,28688],{"class":100},"\"node in slotNodes\"",[43,28690,7352],{"class":57},[43,28692,88],{"class":49},[43,28694,28695],{"class":100},"\"node.props?.id\"",[43,28697,28402],{"class":57},[43,28699,88],{"class":49},[43,28701,28702],{"class":100},"\"node\"",[43,28704,9034],{"class":49},[43,28706,28707,28709,28711],{"class":45,"line":150},[43,28708,6740],{"class":49},[43,28710,6703],{"class":53},[43,28712,61],{"class":49},[43,28714,28715,28717,28719],{"class":45,"line":159},[43,28716,179],{"class":49},[43,28718,6693],{"class":53},[43,28720,61],{"class":49},[43,28722,28723],{"class":45,"line":176},[43,28724,1782],{"emptyLinePlaceholder":491},[43,28726,28727,28729,28731,28733],{"class":45,"line":1163},[43,28728,50],{"class":49},[43,28730,54],{"class":53},[43,28732,58],{"class":57},[43,28734,61],{"class":49},[43,28736,28737,28739,28742,28744,28747],{"class":45,"line":1178},[43,28738,7079],{"class":67},[43,28740,28741],{"class":49}," { useSlots } ",[43,28743,7085],{"class":67},[43,28745,28746],{"class":100}," 'vue'",[43,28748,2995],{"class":49},[43,28750,28751,28753,28756,28758,28761],{"class":45,"line":1184},[43,28752,68],{"class":67},[43,28754,28755],{"class":81}," slots",[43,28757,128],{"class":67},[43,28759,28760],{"class":57}," useSlots",[43,28762,147],{"class":49},[43,28764,28765,28767,28770,28772,28775,28777,28780,28782,28785],{"class":45,"line":1190},[43,28766,68],{"class":67},[43,28768,28769],{"class":81}," slotNodes",[43,28771,128],{"class":67},[43,28773,28774],{"class":49}," slots.",[43,28776,165],{"class":57},[43,28778,28779],{"class":49},"?.() ",[43,28781,9342],{"class":67},[43,28783,28784],{"class":49}," []; ",[43,28786,28787],{"class":2797},"\u002F\u002F 슬롯에서 VNode 배열 가져오기\n",[43,28789,28790,28792,28794],{"class":45,"line":1996},[43,28791,179],{"class":49},[43,28793,54],{"class":53},[43,28795,61],{"class":49},[185,28797,28798,28803],{},[188,28799,28800,28802],{},[40,28801,28507],{},"에 VNode를 전달하면, Vue는 이를 추가 변환 없이 바로 렌더링합니다.",[188,28804,28805],{},"슬롯(slots.default())이나 h() 함수로 생성된 VNode가 여기에 해당합니다.",[29,28807,28809],{"id":28808},"vnode란-무엇인가","VNode란 무엇인가?",[15,28811,28812,28813,28816,28817,28820],{},"VNode(가상 노드)는 Vue가 실제 DOM을 생성하기 전 사용하는 가상 DOM의 기본 단위이다. ",[40,28814,28815],{},"\u003Ctemplate>","나 ",[40,28818,28819],{},"render"," 함수가 컴파일되면 VNode로 변환.",[29,28822,28824],{"id":28823},"vnode의-구조","VNode의 구조",[33,28826,28828],{"className":35,"code":28827,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cdiv>Hello\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[40,28829,28830,28838,28851],{"__ignoreMap":38},[43,28831,28832,28834,28836],{"class":45,"line":46},[43,28833,50],{"class":49},[43,28835,6693],{"class":53},[43,28837,61],{"class":49},[43,28839,28840,28842,28844,28847,28849],{"class":45,"line":64},[43,28841,6700],{"class":49},[43,28843,6703],{"class":53},[43,28845,28846],{"class":49},">Hello\u003C\u002F",[43,28848,6703],{"class":53},[43,28850,61],{"class":49},[43,28852,28853,28855,28857],{"class":45,"line":119},[43,28854,179],{"class":49},[43,28856,6693],{"class":53},[43,28858,61],{"class":49},[15,28860,28861],{},"위 코드는 다음과 같은 VNode로 변환:",[33,28863,28865],{"className":556,"code":28864,"language":558,"meta":38,"style":38},"{\n  \"type\": \"div\",\n  \"props\": {},\n  \"children\": \"Hello\"\n}\n",[40,28866,28867,28871,28883,28891,28901],{"__ignoreMap":38},[43,28868,28869],{"class":45,"line":46},[43,28870,20851],{"class":49},[43,28872,28873,28876,28878,28881],{"class":45,"line":64},[43,28874,28875],{"class":81},"  \"type\"",[43,28877,78],{"class":49},[43,28879,28880],{"class":100},"\"div\"",[43,28882,1056],{"class":49},[43,28884,28885,28888],{"class":45,"line":119},[43,28886,28887],{"class":81},"  \"props\"",[43,28889,28890],{"class":49},": {},\n",[43,28892,28893,28896,28898],{"class":45,"line":150},[43,28894,28895],{"class":81},"  \"children\"",[43,28897,78],{"class":49},[43,28899,28900],{"class":100},"\"Hello\"\n",[43,28902,28903],{"class":45,"line":159},[43,28904,2034],{"class":49},[185,28906,28907,28910,28913],{},[188,28908,28909],{},"type: 태그 이름(문자열), 컴포넌트 객체, 또는 함수.",[188,28911,28912],{},"props: 속성(예: class, id)이나 컴포넌트에 전달할 props.",[188,28914,28915],{},"children: 자식 콘텐츠(문자열, 배열, 또는 다른 VNode).",[29,28917,28919],{"id":28918},"에서-바로-렌더링되는-이유",[28399,28920,28921],{":is":38},"에서 바로 렌더링되는 이유?",[185,28923,28924,28927],{},[188,28925,28926],{},"DOM 직전 단계: VNode는 컴파일이 끝난 결과물로, 추가 처리 없이 가상 DOM에 삽입 가능.",[188,28928,28929,28930],{},"Vue의 설계: ",[28399,28931,391,28932,28935],{":is":38},[28933,28934],"is",{}," 값이 VNode면 이를 바로 렌더링하도록 설계.",[29,28937,28939],{"id":28938},"실전-예제-탭-기반-코드-뷰어","실전 예제: 탭 기반 코드 뷰어",[33,28941,28943],{"className":35,"code":28942,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ccomponent :is=\"activeNode\" \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { useSlots, computed, ref } from 'vue';\nconst slots = useSlots();\nconst activeTab = ref('js');\nconst slotNodes = slots.default?.() || []; \u002F\u002F VNode 배열\n\nconst activeNode = computed(() =>\n  slotNodes.find(node => node.props?.language === activeTab.value)\n);\n\u003C\u002Fscript>\n\n",[40,28944,28945,28953,28961,28976,28984,28992,28996,29006,29019,29031,29049,29070,29074,29089,29111,29115],{"__ignoreMap":38},[43,28946,28947,28949,28951],{"class":45,"line":46},[43,28948,50],{"class":49},[43,28950,6693],{"class":53},[43,28952,61],{"class":49},[43,28954,28955,28957,28959],{"class":45,"line":64},[43,28956,6700],{"class":49},[43,28958,6703],{"class":53},[43,28960,61],{"class":49},[43,28962,28963,28965,28967,28969,28971,28974],{"class":45,"line":119},[43,28964,6726],{"class":49},[43,28966,28399],{"class":53},[43,28968,28402],{"class":57},[43,28970,88],{"class":49},[43,28972,28973],{"class":100},"\"activeNode\"",[43,28975,9034],{"class":49},[43,28977,28978,28980,28982],{"class":45,"line":150},[43,28979,6740],{"class":49},[43,28981,6703],{"class":53},[43,28983,61],{"class":49},[43,28985,28986,28988,28990],{"class":45,"line":159},[43,28987,179],{"class":49},[43,28989,6693],{"class":53},[43,28991,61],{"class":49},[43,28993,28994],{"class":45,"line":176},[43,28995,1782],{"emptyLinePlaceholder":491},[43,28997,28998,29000,29002,29004],{"class":45,"line":1163},[43,28999,50],{"class":49},[43,29001,54],{"class":53},[43,29003,58],{"class":57},[43,29005,61],{"class":49},[43,29007,29008,29010,29013,29015,29017],{"class":45,"line":1178},[43,29009,7079],{"class":67},[43,29011,29012],{"class":49}," { useSlots, computed, ref } ",[43,29014,7085],{"class":67},[43,29016,28746],{"class":100},[43,29018,2995],{"class":49},[43,29020,29021,29023,29025,29027,29029],{"class":45,"line":1184},[43,29022,68],{"class":67},[43,29024,28755],{"class":81},[43,29026,128],{"class":67},[43,29028,28760],{"class":57},[43,29030,147],{"class":49},[43,29032,29033,29035,29038,29040,29042,29044,29047],{"class":45,"line":1190},[43,29034,68],{"class":67},[43,29036,29037],{"class":81}," activeTab",[43,29039,128],{"class":67},[43,29041,28460],{"class":57},[43,29043,97],{"class":49},[43,29045,29046],{"class":100},"'js'",[43,29048,1160],{"class":49},[43,29050,29051,29053,29055,29057,29059,29061,29063,29065,29067],{"class":45,"line":1996},[43,29052,68],{"class":67},[43,29054,28769],{"class":81},[43,29056,128],{"class":67},[43,29058,28774],{"class":49},[43,29060,165],{"class":57},[43,29062,28779],{"class":49},[43,29064,9342],{"class":67},[43,29066,28784],{"class":49},[43,29068,29069],{"class":2797},"\u002F\u002F VNode 배열\n",[43,29071,29072],{"class":45,"line":2008},[43,29073,1782],{"emptyLinePlaceholder":491},[43,29075,29076,29078,29081,29083,29085,29087],{"class":45,"line":2025},[43,29077,68],{"class":67},[43,29079,29080],{"class":81}," activeNode",[43,29082,128],{"class":67},[43,29084,7869],{"class":57},[43,29086,7872],{"class":49},[43,29088,4699],{"class":67},[43,29090,29091,29094,29097,29099,29101,29103,29106,29108],{"class":45,"line":2031},[43,29092,29093],{"class":49},"  slotNodes.",[43,29095,29096],{"class":57},"find",[43,29098,97],{"class":49},[43,29100,25053],{"class":74},[43,29102,1131],{"class":67},[43,29104,29105],{"class":49}," node.props?.language ",[43,29107,4914],{"class":67},[43,29109,29110],{"class":49}," activeTab.value)\n",[43,29112,29113],{"class":45,"line":2037},[43,29114,1160],{"class":49},[43,29116,29117,29119,29121],{"class":45,"line":2042},[43,29118,179],{"class":49},[43,29120,54],{"class":53},[43,29122,61],{"class":49},[1273,29124,29126],{"id":29125},"부모-컴포넌트-사용-예시","부모 컴포넌트 사용 예시:",[33,29128,29130],{"className":35,"code":29129,"language":37,"meta":38,"style":38},"\u003CCodeViewer>\n  \u003Cpre language=\"js\">console.log(\"JS\")\u003C\u002Fpre>\n  \u003Cpre language=\"py\">print(\"Python\")\u003C\u002Fpre>\n\u003C\u002FCodeViewer>\n",[40,29131,29132,29141,29146,29151],{"__ignoreMap":38},[43,29133,29134,29136,29139],{"class":45,"line":46},[43,29135,50],{"class":49},[43,29137,29138],{"class":53},"CodeViewer",[43,29140,61],{"class":49},[43,29142,29143],{"class":45,"line":64},[43,29144,29145],{"class":49},"  \u003Cpre language=\"js\">console.log(\"JS\")\u003C\u002Fpre>\n",[43,29147,29148],{"class":45,"line":119},[43,29149,29150],{"class":49},"  \u003Cpre language=\"py\">print(\"Python\")\u003C\u002Fpre>\n",[43,29152,29153,29155,29157],{"class":45,"line":150},[43,29154,179],{"class":49},[43,29156,29138],{"class":53},[43,29158,61],{"class":49},[185,29160,29161],{},[188,29162,29163,29164],{},"동작: slotNodes는 ",[33,29165,29166,29167],{}," 태그의 VNode 배열이고, ",[28399,29168,29170],{":is":29169},"activeNode","는 선택된 VNode를 바로 렌더링. *Vue의 런타임 시점 기준",[29,29172,29174],{"id":29173},"컴파일과-vnode-생성-과정","컴파일과 VNode 생성 과정",[185,29176,29177],{},[188,29178,29179],{},"Vue는 .vue 파일을 처리할 때 두 단계로 나뉨.",[432,29181,29182,29185],{},[188,29183,29184],{},"빌드 타임 (컴파일 단계): 소스 코드를 JavaScript로 변환.",[188,29186,29187],{},"런타임: 변환된 코드를 실행하며 실제 렌더링.",[471,29189,29190],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":29192},[29193,29194,29195,29196,29197,29198,29201],{"id":28343,"depth":64,"text":28344},{"id":28364,"depth":64,"text":28365},{"id":28808,"depth":64,"text":28809},{"id":28823,"depth":64,"text":28824},{"id":28918,"depth":64,"text":28921},{"id":28938,"depth":64,"text":28939,"children":29199},[29200],{"id":29125,"depth":119,"text":29126},{"id":29173,"depth":64,"text":29174},"Vue에서 \u003Ccomponent :is \u002F>를 활용한 동적 컴포넌트 렌더링 방법과 VNode, 컴포넌트 객체의 차이를 실전 예제로 이해하기",{"date":27584,"field":5954,"tags":29204},[37],"\u002Fko\u002Fvue-component-guide",{"title":28338,"description":29202},{"loc":29205},"ko\u002Fvue-component-guide","LIbw6HvgHZT2-oJ4dLr1KuTLqdXfDrp1AOynGa3fzzQ",{"id":29211,"title":29212,"body":29213,"description":29745,"extension":484,"meta":29746,"navigation":491,"path":29750,"seo":29751,"sitemap":29752,"stem":29753,"__hash__":29754},"ko\u002Fko\u002Fnuxt-useAsyncData-cache-error.md","useAsyncData 캐싱 문제와 Key 동적 설정으로 해결하기",{"type":8,"value":29214,"toc":29724},[29215,29222,29226,29229,29251,29254,29256,29262,29272,29276,29334,29340,29353,29359,29377,29384,29388,29398,29400,29404,29407,29411,29489,29493,29542,29546,29554,29556,29560,29570,29574,29597,29601,29617,29621,29675,29677,29679,29693,29700,29702,29705,29721],[11,29216,29218,29219,29221],{"id":29217},"nuxt-3-useasyncdata-캐싱-문제와-key-동적-설정으로-해결하기","Nuxt 3: ",[40,29220,23968],{}," 캐싱 문제와 Key 동적 설정으로 해결하기",[29,29223,29225],{"id":29224},"문제-상황-동일-데이터-반복-출력","문제 상황: 동일 데이터 반복 출력",[15,29227,29228],{},"Nuxt 3 프로젝트에서 블로그 상세 페이지를 구현하던 중, 다음과 같은 문제가 발생했습니다:",[185,29230,29231],{},[188,29232,29233,29236],{},[21,29234,29235],{},"이슈:",[432,29237,29238,29241,29248],{},[188,29239,29240],{},"블로그 1번 포스트를 클릭.",[188,29242,29243,29244,29247],{},"뒤로 가기(",[40,29245,29246],{},"NuxtLink"," 사용).",[188,29249,29250],{},"블로그 2번 포스트를 클릭했으나 1번 포스트 내용이 그대로 출력.",[15,29252,29253],{},"이 문제는 URL이 변경됨에도 불구하고, 이전 포스트의 데이터가 계속 출력되는 캐싱 관련 문제로 확인되었습니다.",[26,29255],{},[29,29257,7492,29259,29261],{"id":29258},"원인-분석-useasyncdata의-캐싱-메커니즘",[40,29260,23968],{},"의 캐싱 메커니즘",[15,29263,29264,29265,29267,29268,29271],{},"Nuxt 3의 ",[40,29266,23968],{},"는 내부적으로 ",[21,29269,29270],{},"캐싱 메커니즘","을 사용하여 불필요한 네트워크 요청을 줄이는 방식으로 동작합니다. 하지만 이 메커니즘이 문제를 유발할 수 있는 상황이 있습니다.",[1273,29273,29275],{"id":29274},"_1-기존-코드","1. 기존 코드",[33,29277,29279],{"className":1754,"code":29278,"language":1756,"meta":38,"style":38},"const { data: post } = await useAsyncData(() => {\n  return queryCollection(\"blog\").path(route.path).first();\n});\n",[40,29280,29281,29307,29330],{"__ignoreMap":38},[43,29282,29283,29285,29287,29289,29291,29293,29295,29297,29299,29301,29303,29305],{"class":45,"line":46},[43,29284,68],{"class":67},[43,29286,71],{"class":49},[43,29288,75],{"class":74},[43,29290,78],{"class":49},[43,29292,3105],{"class":81},[43,29294,85],{"class":49},[43,29296,88],{"class":67},[43,29298,91],{"class":67},[43,29300,94],{"class":57},[43,29302,7872],{"class":49},[43,29304,113],{"class":67},[43,29306,116],{"class":49},[43,29308,29309,29311,29313,29315,29317,29319,29322,29325,29328],{"class":45,"line":64},[43,29310,153],{"class":67},[43,29312,133],{"class":57},[43,29314,97],{"class":49},[43,29316,138],{"class":100},[43,29318,141],{"class":49},[43,29320,29321],{"class":57},"path",[43,29323,29324],{"class":49},"(route.path).",[43,29326,29327],{"class":57},"first",[43,29329,147],{"class":49},[43,29331,29332],{"class":45,"line":119},[43,29333,1855],{"class":49},[5145,29335,29337],{"id":29336},"key가-고정되어-있음",[21,29338,29339],{},"Key가 고정되어 있음",[15,29341,29342,29344,29345,29348,29349,29352],{},[40,29343,23968],{},"는 Key를 명시적으로 설정하지 않으면, ",[21,29346,29347],{},"코드 블록 자체를 기준으로 Key를 생성","합니다. 따라서 이 코드에서는 Key가 항상 동일하게 고정됩니다. Nuxt는 동일 Key를 가진 데이터 요청의 결과를 ",[21,29350,29351],{},"캐싱","하여 재사용합니다.",[5145,29354,29356],{"id":29355},"문제-발생-흐름",[21,29357,29358],{},"문제 발생 흐름",[432,29360,29361,29371],{},[188,29362,29363,29366,29367,29370],{},[40,29364,29365],{},"\u002Fblog\u002F1","로 이동하면 ",[40,29368,29369],{},"queryCollection(\"blog\").path(\"\u002Fblog\u002F1\")","의 결과가 Key에 저장됩니다.",[188,29372,29373,29376],{},[40,29374,29375],{},"\u002Fblog\u002F2","로 이동해도 Key가 동일하므로 이전에 캐싱된 데이터를 반환합니다.",[15,29378,29379,29380,29383],{},"이로 인해 URL은 정상적으로 바뀌지만, ",[21,29381,29382],{},"이전 블로그 포스트의 내용이 계속 출력","되는 문제가 발생합니다.",[1273,29385,29387],{"id":29386},"_2-캐싱-메커니즘-요약","2. 캐싱 메커니즘 요약",[185,29389,29390,29395],{},[188,29391,29392,29394],{},[40,29393,23968],{},"는 Key를 기준으로 데이터를 캐싱.",[188,29396,29397],{},"Key가 동일하면, 새로운 데이터를 요청하지 않고 캐시된 데이터를 반환.",[26,29399],{},[29,29401,29403],{"id":29402},"해결-방법-key를-동적으로-설정","해결 방법: Key를 동적으로 설정",[15,29405,29406],{},"문제를 해결하기 위해 Key를 URL 경로(route.path)에 따라 동적으로 설정했습니다.",[1273,29408,29410],{"id":29409},"_1-수정된-코드","1. 수정된 코드",[33,29412,29414],{"className":1754,"code":29413,"language":1756,"meta":38,"style":38},"const { data: post, error, refresh } = await useAsyncData(`blog-post-${route.path}`, () => {\n  return queryCollection(\"blog\").path(route.path).first();\n});\n",[40,29415,29416,29465,29485],{"__ignoreMap":38},[43,29417,29418,29420,29422,29424,29426,29428,29430,29432,29434,29437,29439,29441,29443,29445,29447,29450,29453,29455,29457,29459,29461,29463],{"class":45,"line":46},[43,29419,68],{"class":67},[43,29421,71],{"class":49},[43,29423,75],{"class":74},[43,29425,78],{"class":49},[43,29427,3105],{"class":81},[43,29429,104],{"class":49},[43,29431,2014],{"class":81},[43,29433,104],{"class":49},[43,29435,29436],{"class":81},"refresh",[43,29438,85],{"class":49},[43,29440,88],{"class":67},[43,29442,91],{"class":67},[43,29444,94],{"class":57},[43,29446,97],{"class":49},[43,29448,29449],{"class":100},"`blog-post-${",[43,29451,29452],{"class":49},"route",[43,29454,1139],{"class":100},[43,29456,29321],{"class":49},[43,29458,1157],{"class":100},[43,29460,3427],{"class":49},[43,29462,113],{"class":67},[43,29464,116],{"class":49},[43,29466,29467,29469,29471,29473,29475,29477,29479,29481,29483],{"class":45,"line":64},[43,29468,153],{"class":67},[43,29470,133],{"class":57},[43,29472,97],{"class":49},[43,29474,138],{"class":100},[43,29476,141],{"class":49},[43,29478,29321],{"class":57},[43,29480,29324],{"class":49},[43,29482,29327],{"class":57},[43,29484,147],{"class":49},[43,29486,29487],{"class":45,"line":119},[43,29488,1855],{"class":49},[1273,29490,29492],{"id":29491},"_2-변경-사항","2. 변경 사항",[185,29494,29495,29520],{},[188,29496,29497,29500],{},[21,29498,29499],{},"Key를 명시적으로 설정:",[185,29501,29502,29513],{},[188,29503,29504,29505,29508,29509,29512],{},"Key를 ",[40,29506,29507],{},"blog-post-${route.path}","와 같이 ",[21,29510,29511],{},"경로마다 고유한 값","으로 지정.",[188,29514,29515,29516,29519],{},"경로가 변경될 때마다 Nuxt가 ",[21,29517,29518],{},"새로운 Key","를 인식하고 데이터를 새로 요청.",[188,29521,29522,29525],{},[21,29523,29524],{},"데이터 요청이 올바르게 매핑:",[185,29526,29527,29535],{},[188,29528,29529,29531,29532],{},[40,29530,29365],{}," -> Key: ",[40,29533,29534],{},"blog-post-\u002Fblog\u002F1",[188,29536,29537,29531,29539],{},[40,29538,29375],{},[40,29540,29541],{},"blog-post-\u002Fblog\u002F2",[1273,29543,29545],{"id":29544},"_3-결과","3. 결과",[185,29547,29548,29551],{},[188,29549,29550],{},"URL 변경 시 Key가 달라지므로, Nuxt는 캐싱된 데이터를 재활용하지 않고 새 데이터를 요청합니다.",[188,29552,29553],{},"이전 블로그 내용이 출력되는 문제가 해결됩니다.",[26,29555],{},[29,29557,29559],{"id":29558},"배포-환경에서의-동작","배포 환경에서의 동작",[15,29561,29562,29563,5607,29566,29569],{},"배포 환경에서는 Nuxt 3의 ",[21,29564,29565],{},"SSG(Static Site Generation)",[21,29567,29568],{},"ISR(Incremental Static Regeneration)"," 방식이 추가적으로 적용됩니다. 따라서 JSON 캐싱과 HTML 프리렌더링이 문제 해결에 영향을 줄 수 있습니다.",[1273,29571,29573],{"id":29572},"_1-문제-발생-시-배포-파일-구조-변경-전","1. 문제 발생 시 배포 파일 구조 (변경 전)",[185,29575,29576,29579,29588],{},[188,29577,29578],{},"Key가 고정되어 있으므로 동일한 JSON 캐시 파일이 재활용.",[188,29580,29581,29582],{},"예:\n",[33,29583,29586],{"className":29584,"code":29585,"language":539},[537],"\u002F_nuxt\u002Fdata\u002Fblog.json  \u002F\u002F 모든 경로에 대해 동일 파일 사용\n",[40,29587,29585],{"__ignoreMap":38},[188,29589,29590,29591,29593,29594,29596],{},"결과적으로 ",[40,29592,29375],{}," 요청 시에도 ",[40,29595,29365],{}," 데이터가 반환됩니다.",[1273,29598,29600],{"id":29599},"_2-문제-해결-후-배포-파일-구조","2. 문제 해결 후 배포 파일 구조",[185,29602,29603,29606,29614],{},[188,29604,29605],{},"Key가 동적으로 설정되므로 경로마다 고유한 JSON 파일 생성.",[188,29607,29581,29608],{},[33,29609,29612],{"className":29610,"code":29611,"language":539},[537],"\u002F_nuxt\u002Fdata\u002Fblog-post-\u002Fblog\u002F1.json\n\u002F_nuxt\u002Fdata\u002Fblog-post-\u002Fblog\u002F2.json\n",[40,29613,29611],{"__ignoreMap":38},[188,29615,29616],{},"각 경로에 대해 고유한 JSON 파일이 매핑되어 올바른 데이터가 반환됩니다.",[1273,29618,29620],{"id":29619},"_3-배포-환경-확인-방법","3. 배포 환경 확인 방법",[432,29622,29623,29644,29665],{},[188,29624,29625,29628],{},[21,29626,29627],{},"JSON 파일 확인:",[185,29629,29630,29636],{},[188,29631,455,29632,29635],{},[40,29633,29634],{},"_nuxt\u002Fdata\u002F"," 디렉토리에서 각 경로별 JSON 파일이 생성되었는지 확인.",[188,29637,29581,29638],{},[33,29639,29642],{"className":29640,"code":29641,"language":539},[537],"_nuxt\u002Fdata\u002Fblog-post-\u002Fblog\u002F1.json\n_nuxt\u002Fdata\u002Fblog-post-\u002Fblog\u002F2.json\n",[40,29643,29641],{"__ignoreMap":38},[188,29645,29646,29649],{},[21,29647,29648],{},"HTML 파일 확인:",[185,29650,29651,29657],{},[188,29652,29653,29656],{},[40,29654,29655],{},"\u002F.output\u002Fpublic"," 디렉토리에서 각 경로별 프리렌더링된 HTML 파일이 생성되었는지 확인.",[188,29658,29581,29659],{},[33,29660,29663],{"className":29661,"code":29662,"language":539},[537],".output\u002Fpublic\u002Fblog\u002F1\u002Findex.html\n.output\u002Fpublic\u002Fblog\u002F2\u002Findex.html\n",[40,29664,29662],{"__ignoreMap":38},[188,29666,29667,29670],{},[21,29668,29669],{},"Vercel 배포 환경 로그:",[185,29671,29672],{},[188,29673,29674],{},"배포된 프로젝트에서 요청 경로별로 올바른 JSON 파일 및 HTML이 반환되는지 확인.",[26,29676],{},[29,29678,10925],{"id":10925},[432,29680,29681,29687,29690],{},[188,29682,29683,29684,29686],{},"기존 코드에서 동일 데이터가 출력된 원인은 ",[40,29685,23968],{},"의 Key가 고정되어 있었기 때문입니다.",[188,29688,29689],{},"Key를 URL 경로를 기준으로 동적으로 설정하여 Nuxt의 캐싱 메커니즘이 올바르게 동작하도록 수정했습니다.",[188,29691,29692],{},"배포 환경에서도 경로마다 고유한 JSON 파일과 HTML 파일이 생성되므로, 데이터 불일치 문제가 해결되었습니다.",[15,29694,29695,29696,29699],{},"이와 같은 Key 설정은 ",[21,29697,29698],{},"동적 데이터를 사용하는 모든 Nuxt 프로젝트에서 중요한 패턴","이며, 특히 캐싱 문제가 발생할 수 있는 상황에서 반드시 고려해야 합니다.",[26,29701],{},[1273,29703,29704],{"id":29704},"참고",[185,29706,29707,29714],{},[188,29708,29709],{},[304,29710,29713],{"href":29711,"rel":29712},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fapi\u002Fcomposables\u002Fuse-async-data",[308],"Nuxt 3 Documentation: useAsyncData",[188,29715,29716],{},[304,29717,29720],{"href":29718,"rel":29719},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fgetting-started\u002Fintroduction",[308],"Nuxt 3: Caching and Prerendering",[471,29722,29723],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":38,"searchDepth":64,"depth":64,"links":29725},[29726,29727,29732,29737,29742],{"id":29224,"depth":64,"text":29225},{"id":29258,"depth":64,"text":29728,"children":29729},"원인 분석: useAsyncData의 캐싱 메커니즘",[29730,29731],{"id":29274,"depth":119,"text":29275},{"id":29386,"depth":119,"text":29387},{"id":29402,"depth":64,"text":29403,"children":29733},[29734,29735,29736],{"id":29409,"depth":119,"text":29410},{"id":29491,"depth":119,"text":29492},{"id":29544,"depth":119,"text":29545},{"id":29558,"depth":64,"text":29559,"children":29738},[29739,29740,29741],{"id":29572,"depth":119,"text":29573},{"id":29599,"depth":119,"text":29600},{"id":29619,"depth":119,"text":29620},{"id":10925,"depth":64,"text":10925,"children":29743},[29744],{"id":29704,"depth":119,"text":29704},"이전 포스트의 데이터가 계속 출력되는 캐싱 관련 문제를 해결하는 방법",{"date":29747,"field":5954,"tags":29748,"image":29749},"2025.01.24",[7252],"cache.jpg","\u002Fko\u002Fnuxt-useasyncdata-cache-error",{"title":29212,"description":29745},{"loc":29750},"ko\u002Fnuxt-useAsyncData-cache-error","FwpcpWtPS6X52Sc-b3mR73Bl5wgISIpb2bV0tNR96Bk",{"id":29756,"title":29757,"body":29758,"description":30216,"extension":484,"meta":30217,"navigation":491,"path":30220,"seo":30221,"sitemap":30222,"stem":30223,"__hash__":30224},"ko\u002Fko\u002Fnuxt-vercel-sql-error.md","Nuxt 3에서 Vercel 배포 시 발생한 md 파일 로딩 문제 해결기",{"type":8,"value":29759,"toc":30204},[29760,29763,29767,29770,29774,29805,29809,29912,29914,29918,29959,29961,29968,29981,29987,30189,30192,30195,30198,30201],[11,29761,29757],{"id":29762},"nuxt-3에서-vercel-배포-시-발생한-md-파일-로딩-문제-해결기",[29,29764,29766],{"id":29765},"내가-마주한-에러","내가 마주한 에러",[15,29768,29769],{},"로컬 환경에서 잘 동작하던 Nuxt 3 웹 어플리케이션이 Vercel 배포 환경에서 예상치 못한 에러를 발생시켰습니다.",[1273,29771,29773],{"id":29772},"이슈-재현-경로","이슈 재현 경로",[432,29775,29776,29789,29795],{},[188,29777,29778,29779,29781,29782,29785,29786,29788],{},"메인 페이지(",[40,29780,4711],{},")에서 ",[21,29783,29784],{},"이동 버튼","을 통해 ",[40,29787,1391],{}," 블로그 리스트 페이지로 이동.",[188,29790,29791,29792,29794],{},"블로그 리스트 페이지에서 ",[40,29793,484],{}," 파일들은 정상적으로 호출됨.",[188,29796,29797,29798,29800,29801,29804],{},"그러나 ",[40,29799,1391],{}," 페이지에서 ",[21,29802,29803],{},"리로드","를 수행하면 에러가 발생.",[1273,29806,29808],{"id":29807},"vercel에서-받은-에러-로그","Vercel에서 받은 에러 로그",[33,29810,29812],{"className":850,"code":29811,"language":852,"meta":38,"style":38},"Failed to execute SQL INSERT INTO _content_blog VALUES ('blog\u002Fblog\u002Fnuxt3.md',  \n'Nuxt3', '{\"type\":\"minimal\",\"value\":[[\"h1\",{\"id\":\"hello-world\"},\"Hello World\"], \n[\"p\", {},\"Welcome to my blog powered by Nuxt Content!\"]], \"toc\":{\"title\":\"\", \n\"searchDepth\":2,\"depth\":2,\"links\":[]}}', 'How to start Nuxt', 'md', ' \n{\"date\":\"2025-01-19\"}', 'true', '\u002Fblog\u002Fnuxt3', '{\"title\":\"Nuxt3\",\"description\":\"How  \nto start Nuxt\"}', 'blog\u002Fnuxt3');: connect ECONNREFUSED 127.0.0.1:5432 \n",[40,29813,29814,29847,29857,29862,29876,29890],{"__ignoreMap":38},[43,29815,29816,29819,29821,29824,29827,29830,29833,29836,29839,29841,29844],{"class":45,"line":46},[43,29817,29818],{"class":57},"Failed",[43,29820,19589],{"class":100},[43,29822,29823],{"class":100}," execute",[43,29825,29826],{"class":100}," SQL",[43,29828,29829],{"class":100}," INSERT",[43,29831,29832],{"class":100}," INTO",[43,29834,29835],{"class":100}," _content_blog",[43,29837,29838],{"class":100}," VALUES",[43,29840,1069],{"class":49},[43,29842,29843],{"class":100},"'blog\u002Fblog\u002Fnuxt3.md'",[43,29845,29846],{"class":49},",  \n",[43,29848,29849,29852,29854],{"class":45,"line":64},[43,29850,29851],{"class":57},"'Nuxt3'",[43,29853,4754],{"class":57},[43,29855,29856],{"class":100}," '{\"type\":\"minimal\",\"value\":[[\"h1\",{\"id\":\"hello-world\"},\"Hello World\"], \n",[43,29858,29859],{"class":45,"line":119},[43,29860,29861],{"class":100},"[\"p\", {},\"Welcome to my blog powered by Nuxt Content!\"]], \"toc\":{\"title\":\"\", \n",[43,29863,29864,29867,29870,29873],{"class":45,"line":150},[43,29865,29866],{"class":100},"\"searchDepth\":2,\"depth\":2,\"links\":[]}}',",[43,29868,29869],{"class":100}," 'How to start Nuxt',",[43,29871,29872],{"class":100}," 'md',",[43,29874,29875],{"class":100}," ' \n",[43,29877,29878,29881,29884,29887],{"class":45,"line":159},[43,29879,29880],{"class":100},"{\"date\":\"2025-01-19\"}',",[43,29882,29883],{"class":100}," 'true',",[43,29885,29886],{"class":100}," '\u002Fblog\u002Fnuxt3',",[43,29888,29889],{"class":100}," '{\"title\":\"Nuxt3\",\"description\":\"How  \n",[43,29891,29892,29895,29898,29901,29903,29906,29909],{"class":45,"line":176},[43,29893,29894],{"class":100},"to start Nuxt\"}',",[43,29896,29897],{"class":100}," 'blog\u002Fnuxt3'",[43,29899,29900],{"class":49},");",[43,29902,9326],{"class":81},[43,29904,29905],{"class":100}," connect",[43,29907,29908],{"class":100}," ECONNREFUSED",[43,29910,29911],{"class":100}," 127.0.0.1:5432\n",[26,29913],{},[29,29915,29917],{"id":29916},"에러-분석","에러 분석",[432,29919,29920,29939],{},[188,29921,29922,29927],{},[21,29923,29924],{},[40,29925,29926],{},"connect ECONNREFUSED 127.0.0.1:5432",[185,29928,29929,29932],{},[188,29930,29931],{},"이 에러는 데이터베이스 연결이 거부되었음을 의미합니다.",[188,29933,29934,29935,29938],{},"Nuxt Content 모듈이 배포 환경에서 데이터를 동적으로 처리하기 위해 ",[21,29936,29937],{},"로컬 데이터베이스 연결","을 시도했으나, 정적 호스팅 환경(Vercel)에서는 데이터베이스 서버가 실행되지 않아 발생.",[188,29940,29941,29944],{},[21,29942,29943],{},"왜 리로드에서만 에러가 발생했을까?",[185,29945,29946,29952],{},[188,29947,29948,29949,29951],{},"Vercel의 정적 사이트는 미리 생성된 HTML 파일을 제공합니다. 그러나 ",[40,29950,1391],{}," 경로에서 리로드 시 서버가 존재하지 않으므로 동적으로 데이터를 가져오려는 요청이 실패했습니다.",[188,29953,29954,29955,29958],{},"정적으로 생성되지 않은 경로(",[40,29956,29957],{},"\u002Fblog\u002F:slug",")는 배포 환경에서 지원되지 않았기 때문입니다.",[26,29960],{},[29,29962,7671,29964,29967],{"id":29963},"해결-방법-prerender-설정-추가",[40,29965,29966],{},"prerender"," 설정 추가",[15,29969,29264,29970,29973,29974,29977,29978,29980],{},[40,29971,29972],{},"nitro.prerender"," 설정을 사용해 ",[21,29975,29976],{},"모든 블로그 경로","를 정적으로 생성하도록 설정했습니다.",[6005,29979],{},"\n정적 경로로 렌더링함으로써 배포 환경에서도 데이터베이스 연결 없이 동작하도록 수정했습니다.",[1273,29982,29984,29985,247],{"id":29983},"해결-코드-nuxtconfigts","해결 코드 (",[40,29986,1020],{},[33,29988,29992],{"className":29989,"code":29990,"language":29991,"meta":38,"style":38},"language-typescript shiki shiki-themes github-light github-dark","import { promises as fs } from 'fs';\nimport { resolve } from 'path';\n\nexport default defineNuxtConfig({\n  nitro: {\n    prerender: {\n      \u002F\u002F 정적으로 생성할 경로를 배열로 반환\n      routes: await (async () => {\n        \u002F\u002F content\u002Fblog 디렉토리의 .md 파일 기준으로 경로 생성\n        const blogFiles = await fs.readdir(resolve('content\u002Fblog'));\n        const blogRoutes = blogFiles.map(file => `\u002Fblog\u002F${file.replace('.md', '')}`);\n\n        \u002F\u002F 필요한 경로들을 반환\n        return ['\u002Fblog', '\u002F', ...blogRoutes];\n      })(),\n    },\n  },\n});\n","typescript",[40,29993,29994,30014,30028,30032,30042,30047,30052,30057,30074,30079,30103,30143,30147,30152,30172,30177,30181,30185],{"__ignoreMap":38},[43,29995,29996,29998,30001,30004,30007,30009,30012],{"class":45,"line":46},[43,29997,7079],{"class":67},[43,29999,30000],{"class":49}," { promises ",[43,30002,30003],{"class":67},"as",[43,30005,30006],{"class":49}," fs } ",[43,30008,7085],{"class":67},[43,30010,30011],{"class":100}," 'fs'",[43,30013,2995],{"class":49},[43,30015,30016,30018,30021,30023,30026],{"class":45,"line":64},[43,30017,7079],{"class":67},[43,30019,30020],{"class":49}," { resolve } ",[43,30022,7085],{"class":67},[43,30024,30025],{"class":100}," 'path'",[43,30027,2995],{"class":49},[43,30029,30030],{"class":45,"line":119},[43,30031,1782],{"emptyLinePlaceholder":491},[43,30033,30034,30036,30038,30040],{"class":45,"line":150},[43,30035,6052],{"class":67},[43,30037,6055],{"class":67},[43,30039,6058],{"class":57},[43,30041,1803],{"class":49},[43,30043,30044],{"class":45,"line":159},[43,30045,30046],{"class":49},"  nitro: {\n",[43,30048,30049],{"class":45,"line":176},[43,30050,30051],{"class":49},"    prerender: {\n",[43,30053,30054],{"class":45,"line":1163},[43,30055,30056],{"class":2797},"      \u002F\u002F 정적으로 생성할 경로를 배열로 반환\n",[43,30058,30059,30062,30064,30066,30068,30070,30072],{"class":45,"line":1178},[43,30060,30061],{"class":49},"      routes: ",[43,30063,1066],{"class":67},[43,30065,1069],{"class":49},[43,30067,107],{"class":67},[43,30069,110],{"class":49},[43,30071,113],{"class":67},[43,30073,116],{"class":49},[43,30075,30076],{"class":45,"line":1184},[43,30077,30078],{"class":2797},"        \u002F\u002F content\u002Fblog 디렉토리의 .md 파일 기준으로 경로 생성\n",[43,30080,30081,30083,30085,30087,30089,30091,30093,30095,30097,30099,30101],{"class":45,"line":1190},[43,30082,9277],{"class":67},[43,30084,1085],{"class":81},[43,30086,128],{"class":67},[43,30088,91],{"class":67},[43,30090,1092],{"class":49},[43,30092,1095],{"class":57},[43,30094,97],{"class":49},[43,30096,1100],{"class":57},[43,30098,97],{"class":49},[43,30100,1105],{"class":100},[43,30102,1108],{"class":49},[43,30104,30105,30107,30109,30111,30113,30115,30117,30119,30121,30123,30125,30127,30129,30131,30133,30135,30137,30139,30141],{"class":45,"line":1996},[43,30106,9277],{"class":67},[43,30108,1115],{"class":81},[43,30110,128],{"class":67},[43,30112,1120],{"class":49},[43,30114,1123],{"class":57},[43,30116,97],{"class":49},[43,30118,1128],{"class":74},[43,30120,1131],{"class":67},[43,30122,1134],{"class":100},[43,30124,1128],{"class":49},[43,30126,1139],{"class":100},[43,30128,1142],{"class":57},[43,30130,97],{"class":100},[43,30132,1147],{"class":100},[43,30134,104],{"class":100},[43,30136,1152],{"class":100},[43,30138,247],{"class":100},[43,30140,1157],{"class":100},[43,30142,1160],{"class":49},[43,30144,30145],{"class":45,"line":2008},[43,30146,1782],{"emptyLinePlaceholder":491},[43,30148,30149],{"class":45,"line":2025},[43,30150,30151],{"class":2797},"        \u002F\u002F 필요한 경로들을 반환\n",[43,30153,30154,30157,30159,30162,30164,30166,30168,30170],{"class":45,"line":2031},[43,30155,30156],{"class":67},"        return",[43,30158,1169],{"class":49},[43,30160,30161],{"class":100},"'\u002Fblog'",[43,30163,104],{"class":49},[43,30165,25387],{"class":100},[43,30167,104],{"class":49},[43,30169,1172],{"class":67},[43,30171,1175],{"class":49},[43,30173,30174],{"class":45,"line":2037},[43,30175,30176],{"class":49},"      })(),\n",[43,30178,30179],{"class":45,"line":2042},[43,30180,6095],{"class":49},[43,30182,30183],{"class":45,"line":2516},[43,30184,1187],{"class":49},[43,30186,30187],{"class":45,"line":2998},[43,30188,1855],{"class":49},[15,30190,30191],{},"코드 설명\n블로그 경로 생성:\ncontent\u002Fblog 디렉토리에 있는 모든 .md 파일의 이름을 기반으로 \u002Fblog\u002F:slug 경로를 생성합니다.",[15,30193,30194],{},"정적 경로와 동적 경로 통합:",[15,30196,30197],{},"\u002Fblog: 블로그 리스트 페이지.\n\u002Fblog\u002F:slug: 각 블로그 포스트 페이지.\n배포 환경에 정적 파일 제공:\n배포 시 .output\u002Fpublic 디렉토리에 HTML 파일들이 정적으로 생성되어 Vercel에서 문제가 발생하지 않음.",[15,30199,30200],{},"결과 확인\n로컬 환경\nnpm run dev 실행 후:\n\u002F 메인 페이지, \u002Fblog 블로그 리스트 페이지, \u002Fblog\u002F:slug 포스트 페이지가 모두 정상 작동.\n배포 환경 (Vercel)\n\u002Fblog로 이동 및 리로드 시 더 이상 에러가 발생하지 않음.\n\u002Fblog\u002F:slug 경로의 정적 HTML 파일이 정적으로 제공되므로 빠르고 안정적인 성능 제공.",[471,30202,30203],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":30205},[30206,30210,30211],{"id":29765,"depth":64,"text":29766,"children":30207},[30208,30209],{"id":29772,"depth":119,"text":29773},{"id":29807,"depth":119,"text":29808},{"id":29916,"depth":64,"text":29917},{"id":29963,"depth":64,"text":30212,"children":30213},"해결 방법: prerender 설정 추가",[30214],{"id":29983,"depth":119,"text":30215},"해결 코드 (nuxt.config.ts)","Nuxt 3 웹 어플리케이션에서 Vercel 배포 환경에서 발생한 md 파일 로딩 문제를 분석하고 해결한 방법에 대해 공유합니다.",{"date":29747,"image":30218,"field":5954,"tags":30219},"ssg.jpg",[7252,489,490],"\u002Fko\u002Fnuxt-vercel-sql-error",{"title":29757,"description":30216},{"loc":30220},"ko\u002Fnuxt-vercel-sql-error","nS4BEAQUGEIJSNiN7lje8Qb8ikJ5cbdWZc_0xW0R4vw",{"id":30226,"title":30227,"body":30228,"description":31126,"extension":484,"meta":31127,"navigation":31130,"path":31132,"seo":31133,"sitemap":31134,"stem":31135,"__hash__":31136},"ko\u002Fko\u002Frouting.md","Routing",{"type":8,"value":30229,"toc":31123},[30230,30237,30265,30435,30438,30484,30584,30586,30738,30792,30803,30988,30992,31012,31015,31120],[507,30231,30232],{},[15,30233,30234,30235],{},"test\n",[40,30236,21],{},[33,30238,30240],{"className":1024,"code":30239,"language":1026,"meta":38,"style":38},"function test(){\n  return 123 \n}\n",[40,30241,30242,30251,30261],{"__ignoreMap":38},[43,30243,30244,30246,30249],{"class":45,"line":46},[43,30245,8424],{"class":67},[43,30247,30248],{"class":57}," test",[43,30250,26671],{"class":49},[43,30252,30253,30255,30258],{"class":45,"line":64},[43,30254,153],{"class":67},[43,30256,30257],{"class":81}," 123",[43,30259,30260],{"class":49}," \n",[43,30262,30263],{"class":45,"line":119},[43,30264,2034],{"class":49},[11005,30266,30267,30322],{},[33,30268,30271],{"className":850,"code":30269,"filename":30270,"language":852,"meta":38,"style":38},"-| pages\u002F\n---| about.vue\n---| index.vue\n---| posts\u002F\n-----| [id].vue\n","Directory Structure",[40,30272,30273,30282,30291,30300,30309],{"__ignoreMap":38},[43,30274,30275,30277,30279],{"class":45,"line":46},[43,30276,18523],{"class":57},[43,30278,5645],{"class":67},[43,30280,30281],{"class":57}," pages\u002F\n",[43,30283,30284,30286,30288],{"class":45,"line":64},[43,30285,23663],{"class":57},[43,30287,5645],{"class":67},[43,30289,30290],{"class":57}," about.vue\n",[43,30292,30293,30295,30297],{"class":45,"line":119},[43,30294,23663],{"class":57},[43,30296,5645],{"class":67},[43,30298,30299],{"class":57}," index.vue\n",[43,30301,30302,30304,30306],{"class":45,"line":150},[43,30303,23663],{"class":57},[43,30305,5645],{"class":67},[43,30307,30308],{"class":57}," posts\u002F\n",[43,30310,30311,30314,30316,30319],{"class":45,"line":159},[43,30312,30313],{"class":57},"-----",[43,30315,5645],{"class":67},[43,30317,30318],{"class":49}," [id]",[43,30320,30321],{"class":57},".vue\n",[33,30323,30326],{"className":556,"code":30324,"filename":30325,"language":558,"meta":38,"style":38},"{\n  \"routes\": [\n    {\n      \"path\": \"\u002Fabout\",\n      \"component\": \"pages\u002Fabout.vue\"\n    },\n    {\n      \"path\": \"\u002F\",\n      \"component\": \"pages\u002Findex.vue\"\n    },\n    {\n      \"path\": \"\u002Fposts\u002F:id\",\n      \"component\": \"pages\u002Fposts\u002F[id].vue\"\n    }\n  ]\n}\n","Generated Router File",[40,30327,30328,30332,30340,30345,30357,30367,30371,30375,30386,30395,30399,30403,30414,30423,30427,30431],{"__ignoreMap":38},[43,30329,30330],{"class":45,"line":46},[43,30331,20851],{"class":49},[43,30333,30334,30337],{"class":45,"line":64},[43,30335,30336],{"class":81},"  \"routes\"",[43,30338,30339],{"class":49},": [\n",[43,30341,30342],{"class":45,"line":119},[43,30343,30344],{"class":49},"    {\n",[43,30346,30347,30350,30352,30355],{"class":45,"line":150},[43,30348,30349],{"class":81},"      \"path\"",[43,30351,78],{"class":49},[43,30353,30354],{"class":100},"\"\u002Fabout\"",[43,30356,1056],{"class":49},[43,30358,30359,30362,30364],{"class":45,"line":159},[43,30360,30361],{"class":81},"      \"component\"",[43,30363,78],{"class":49},[43,30365,30366],{"class":100},"\"pages\u002Fabout.vue\"\n",[43,30368,30369],{"class":45,"line":176},[43,30370,6095],{"class":49},[43,30372,30373],{"class":45,"line":1163},[43,30374,30344],{"class":49},[43,30376,30377,30379,30381,30384],{"class":45,"line":1178},[43,30378,30349],{"class":81},[43,30380,78],{"class":49},[43,30382,30383],{"class":100},"\"\u002F\"",[43,30385,1056],{"class":49},[43,30387,30388,30390,30392],{"class":45,"line":1184},[43,30389,30361],{"class":81},[43,30391,78],{"class":49},[43,30393,30394],{"class":100},"\"pages\u002Findex.vue\"\n",[43,30396,30397],{"class":45,"line":1190},[43,30398,6095],{"class":49},[43,30400,30401],{"class":45,"line":1996},[43,30402,30344],{"class":49},[43,30404,30405,30407,30409,30412],{"class":45,"line":2008},[43,30406,30349],{"class":81},[43,30408,78],{"class":49},[43,30410,30411],{"class":100},"\"\u002Fposts\u002F:id\"",[43,30413,1056],{"class":49},[43,30415,30416,30418,30420],{"class":45,"line":2025},[43,30417,30361],{"class":81},[43,30419,78],{"class":49},[43,30421,30422],{"class":100},"\"pages\u002Fposts\u002F[id].vue\"\n",[43,30424,30425],{"class":45,"line":2031},[43,30426,1993],{"class":49},[43,30428,30429],{"class":45,"line":2037},[43,30430,13173],{"class":49},[43,30432,30433],{"class":45,"line":2042},[43,30434,2034],{"class":49},[15,30436,30437],{},"This file system routing uses naming conventions to create dynamic and nested routes:",[33,30439,30440],{"className":850,"code":30269,"filename":30270,"language":852,"meta":38,"style":38},[40,30441,30442,30450,30458,30466,30474],{"__ignoreMap":38},[43,30443,30444,30446,30448],{"class":45,"line":46},[43,30445,18523],{"class":57},[43,30447,5645],{"class":67},[43,30449,30281],{"class":57},[43,30451,30452,30454,30456],{"class":45,"line":64},[43,30453,23663],{"class":57},[43,30455,5645],{"class":67},[43,30457,30290],{"class":57},[43,30459,30460,30462,30464],{"class":45,"line":119},[43,30461,23663],{"class":57},[43,30463,5645],{"class":67},[43,30465,30299],{"class":57},[43,30467,30468,30470,30472],{"class":45,"line":150},[43,30469,23663],{"class":57},[43,30471,5645],{"class":67},[43,30473,30308],{"class":57},[43,30475,30476,30478,30480,30482],{"class":45,"line":159},[43,30477,30313],{"class":57},[43,30479,5645],{"class":67},[43,30481,30318],{"class":49},[43,30483,30321],{"class":57},[33,30485,30486],{"className":556,"code":30324,"filename":30325,"language":558,"meta":38,"style":38},[40,30487,30488,30492,30498,30502,30512,30520,30524,30528,30538,30546,30550,30554,30564,30572,30576,30580],{"__ignoreMap":38},[43,30489,30490],{"class":45,"line":46},[43,30491,20851],{"class":49},[43,30493,30494,30496],{"class":45,"line":64},[43,30495,30336],{"class":81},[43,30497,30339],{"class":49},[43,30499,30500],{"class":45,"line":119},[43,30501,30344],{"class":49},[43,30503,30504,30506,30508,30510],{"class":45,"line":150},[43,30505,30349],{"class":81},[43,30507,78],{"class":49},[43,30509,30354],{"class":100},[43,30511,1056],{"class":49},[43,30513,30514,30516,30518],{"class":45,"line":159},[43,30515,30361],{"class":81},[43,30517,78],{"class":49},[43,30519,30366],{"class":100},[43,30521,30522],{"class":45,"line":176},[43,30523,6095],{"class":49},[43,30525,30526],{"class":45,"line":1163},[43,30527,30344],{"class":49},[43,30529,30530,30532,30534,30536],{"class":45,"line":1178},[43,30531,30349],{"class":81},[43,30533,78],{"class":49},[43,30535,30383],{"class":100},[43,30537,1056],{"class":49},[43,30539,30540,30542,30544],{"class":45,"line":1184},[43,30541,30361],{"class":81},[43,30543,78],{"class":49},[43,30545,30394],{"class":100},[43,30547,30548],{"class":45,"line":1190},[43,30549,6095],{"class":49},[43,30551,30552],{"class":45,"line":1996},[43,30553,30344],{"class":49},[43,30555,30556,30558,30560,30562],{"class":45,"line":2008},[43,30557,30349],{"class":81},[43,30559,78],{"class":49},[43,30561,30411],{"class":100},[43,30563,1056],{"class":49},[43,30565,30566,30568,30570],{"class":45,"line":2025},[43,30567,30361],{"class":81},[43,30569,78],{"class":49},[43,30571,30422],{"class":100},[43,30573,30574],{"class":45,"line":2031},[43,30575,1993],{"class":49},[43,30577,30578],{"class":45,"line":2037},[43,30579,13173],{"class":49},[43,30581,30582],{"class":45,"line":2042},[43,30583,2034],{"class":49},[15,30585,4174],{},[33,30587,30590],{"className":35,"code":30588,"filename":30589,"language":37,"meta":38,"style":38},"\u003Ctemplate>\n  \u003Cheader>\n    \u003Cnav>\n      \u003Cul>\n        \u003Cli>\u003CNuxtLink to=\"\u002Fabout\">About\u003C\u002FNuxtLink>\u003C\u002Fli>\n        \u003Cli>\u003CNuxtLink to=\"\u002Fposts\u002F1\">Post 1\u003C\u002FNuxtLink>\u003C\u002Fli>\n        \u003Cli>\u003CNuxtLink to=\"\u002Fposts\u002F2\">Post 2\u003C\u002FNuxtLink>\u003C\u002Fli>\n      \u003C\u002Ful>\n    \u003C\u002Fnav>\n  \u003C\u002Fheader>\n\u003C\u002Ftemplate>\n","pages\u002Fapp.vue",[40,30591,30592,30600,30608,30616,30624,30650,30678,30706,30714,30722,30730],{"__ignoreMap":38},[43,30593,30594,30596,30598],{"class":45,"line":46},[43,30595,50],{"class":49},[43,30597,6693],{"class":53},[43,30599,61],{"class":49},[43,30601,30602,30604,30606],{"class":45,"line":64},[43,30603,6700],{"class":49},[43,30605,13564],{"class":53},[43,30607,61],{"class":49},[43,30609,30610,30612,30614],{"class":45,"line":119},[43,30611,6726],{"class":49},[43,30613,26924],{"class":53},[43,30615,61],{"class":49},[43,30617,30618,30620,30622],{"class":45,"line":150},[43,30619,7339],{"class":49},[43,30621,185],{"class":53},[43,30623,61],{"class":49},[43,30625,30626,30628,30630,30632,30634,30636,30638,30640,30642,30644,30646,30648],{"class":45,"line":159},[43,30627,8017],{"class":49},[43,30629,188],{"class":53},[43,30631,27986],{"class":49},[43,30633,29246],{"class":53},[43,30635,19589],{"class":57},[43,30637,88],{"class":49},[43,30639,30354],{"class":100},[43,30641,26988],{"class":49},[43,30643,29246],{"class":53},[43,30645,15178],{"class":49},[43,30647,188],{"class":53},[43,30649,61],{"class":49},[43,30651,30652,30654,30656,30658,30660,30662,30664,30667,30670,30672,30674,30676],{"class":45,"line":176},[43,30653,8017],{"class":49},[43,30655,188],{"class":53},[43,30657,27986],{"class":49},[43,30659,29246],{"class":53},[43,30661,19589],{"class":57},[43,30663,88],{"class":49},[43,30665,30666],{"class":100},"\"\u002Fposts\u002F1\"",[43,30668,30669],{"class":49},">Post 1\u003C\u002F",[43,30671,29246],{"class":53},[43,30673,15178],{"class":49},[43,30675,188],{"class":53},[43,30677,61],{"class":49},[43,30679,30680,30682,30684,30686,30688,30690,30692,30695,30698,30700,30702,30704],{"class":45,"line":1163},[43,30681,8017],{"class":49},[43,30683,188],{"class":53},[43,30685,27986],{"class":49},[43,30687,29246],{"class":53},[43,30689,19589],{"class":57},[43,30691,88],{"class":49},[43,30693,30694],{"class":100},"\"\u002Fposts\u002F2\"",[43,30696,30697],{"class":49},">Post 2\u003C\u002F",[43,30699,29246],{"class":53},[43,30701,15178],{"class":49},[43,30703,188],{"class":53},[43,30705,61],{"class":49},[43,30707,30708,30710,30712],{"class":45,"line":1178},[43,30709,7369],{"class":49},[43,30711,185],{"class":53},[43,30713,61],{"class":49},[43,30715,30716,30718,30720],{"class":45,"line":1184},[43,30717,7378],{"class":49},[43,30719,26924],{"class":53},[43,30721,61],{"class":49},[43,30723,30724,30726,30728],{"class":45,"line":1190},[43,30725,6740],{"class":49},[43,30727,13564],{"class":53},[43,30729,61],{"class":49},[43,30731,30732,30734,30736],{"class":45,"line":1996},[43,30733,179],{"class":49},[43,30735,6693],{"class":53},[43,30737,61],{"class":49},[33,30739,30743],{"className":35,"code":30740,"filename":30741,"language":37,"meta":30742,"style":38},"\u003Cscript setup lang=\"ts\">\nconst route = useRoute()\n\n\u002F\u002F When accessing \u002Fposts\u002F1, route.params.id will be 1\n\u003C\u002Fscript>\n","pages\u002Fposts\u002F[id].vue","twoslash",[40,30744,30745,30761,30775,30779,30784],{"__ignoreMap":38},[43,30746,30747,30749,30751,30753,30755,30757,30759],{"class":45,"line":46},[43,30748,50],{"class":49},[43,30750,54],{"class":53},[43,30752,58],{"class":57},[43,30754,27685],{"class":57},[43,30756,88],{"class":49},[43,30758,598],{"class":100},[43,30760,61],{"class":49},[43,30762,30763,30765,30768,30770,30773],{"class":45,"line":64},[43,30764,68],{"class":67},[43,30766,30767],{"class":81}," route",[43,30769,128],{"class":67},[43,30771,30772],{"class":57}," useRoute",[43,30774,6141],{"class":49},[43,30776,30777],{"class":45,"line":119},[43,30778,1782],{"emptyLinePlaceholder":491},[43,30780,30781],{"class":45,"line":150},[43,30782,30783],{"class":2797},"\u002F\u002F When accessing \u002Fposts\u002F1, route.params.id will be 1\n",[43,30785,30786,30788,30790],{"class":45,"line":159},[43,30787,179],{"class":49},[43,30789,54],{"class":53},[43,30791,61],{"class":49},[15,30793,30794,30795,30798,30799,30802],{},"Example of an ",[40,30796,30797],{},"auth"," middleware protecting the ",[40,30800,30801],{},"\u002Fdashboard"," page:",[11005,30804,30805,30906],{},[33,30806,30809],{"className":6038,"code":30807,"filename":30808,"language":6040,"meta":30742,"style":38},"function isAuthenticated(): boolean { return false }\n\u002F\u002F ---cut---\nexport default defineNuxtRouteMiddleware((to, from) => {\n  \u002F\u002F isAuthenticated() is an example method verifying if a user is authenticated\n  if (isAuthenticated() === false) {\n    return navigateTo('\u002Flogin')\n  }\n})\n","middleware\u002Fauth.ts",[40,30810,30811,30834,30839,30862,30867,30884,30898,30902],{"__ignoreMap":38},[43,30812,30813,30815,30818,30820,30822,30825,30827,30829,30832],{"class":45,"line":46},[43,30814,8424],{"class":67},[43,30816,30817],{"class":57}," isAuthenticated",[43,30819,3186],{"class":49},[43,30821,9326],{"class":67},[43,30823,30824],{"class":81}," boolean",[43,30826,71],{"class":49},[43,30828,25979],{"class":67},[43,30830,30831],{"class":81}," false",[43,30833,606],{"class":49},[43,30835,30836],{"class":45,"line":64},[43,30837,30838],{"class":2797},"\u002F\u002F ---cut---\n",[43,30840,30841,30843,30845,30848,30850,30852,30854,30856,30858,30860],{"class":45,"line":119},[43,30842,6052],{"class":67},[43,30844,6055],{"class":67},[43,30846,30847],{"class":57}," defineNuxtRouteMiddleware",[43,30849,4691],{"class":49},[43,30851,11755],{"class":74},[43,30853,104],{"class":49},[43,30855,7085],{"class":74},[43,30857,2408],{"class":49},[43,30859,113],{"class":67},[43,30861,116],{"class":49},[43,30863,30864],{"class":45,"line":150},[43,30865,30866],{"class":2797},"  \u002F\u002F isAuthenticated() is an example method verifying if a user is authenticated\n",[43,30868,30869,30871,30873,30876,30878,30880,30882],{"class":45,"line":159},[43,30870,3047],{"class":67},[43,30872,1069],{"class":49},[43,30874,30875],{"class":57},"isAuthenticated",[43,30877,19565],{"class":49},[43,30879,4914],{"class":67},[43,30881,30831],{"class":81},[43,30883,1882],{"class":49},[43,30885,30886,30888,30891,30893,30896],{"class":45,"line":176},[43,30887,2737],{"class":67},[43,30889,30890],{"class":57}," navigateTo",[43,30892,97],{"class":49},[43,30894,30895],{"class":100},"'\u002Flogin'",[43,30897,5241],{"class":49},[43,30899,30900],{"class":45,"line":1163},[43,30901,2028],{"class":49},[43,30903,30904],{"class":45,"line":1178},[43,30905,6117],{"class":49},[33,30907,30910],{"className":35,"code":30908,"filename":30909,"language":37,"meta":30742,"style":38},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  middleware: 'auth'\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Ch1>Welcome to your dashboard\u003C\u002Fh1>\n\u003C\u002Ftemplate>\n","pages\u002Fdashboard.vue",[40,30911,30912,30928,30935,30943,30947,30955,30959,30967,30980],{"__ignoreMap":38},[43,30913,30914,30916,30918,30920,30922,30924,30926],{"class":45,"line":46},[43,30915,50],{"class":49},[43,30917,54],{"class":53},[43,30919,58],{"class":57},[43,30921,27685],{"class":57},[43,30923,88],{"class":49},[43,30925,598],{"class":100},[43,30927,61],{"class":49},[43,30929,30930,30933],{"class":45,"line":64},[43,30931,30932],{"class":57},"definePageMeta",[43,30934,1803],{"class":49},[43,30936,30937,30940],{"class":45,"line":119},[43,30938,30939],{"class":49},"  middleware: ",[43,30941,30942],{"class":100},"'auth'\n",[43,30944,30945],{"class":45,"line":150},[43,30946,6117],{"class":49},[43,30948,30949,30951,30953],{"class":45,"line":159},[43,30950,179],{"class":49},[43,30952,54],{"class":53},[43,30954,61],{"class":49},[43,30956,30957],{"class":45,"line":176},[43,30958,1782],{"emptyLinePlaceholder":491},[43,30960,30961,30963,30965],{"class":45,"line":1163},[43,30962,50],{"class":49},[43,30964,6693],{"class":53},[43,30966,61],{"class":49},[43,30968,30969,30971,30973,30976,30978],{"class":45,"line":1178},[43,30970,6700],{"class":49},[43,30972,11],{"class":53},[43,30974,30975],{"class":49},">Welcome to your dashboard\u003C\u002F",[43,30977,11],{"class":53},[43,30979,61],{"class":49},[43,30981,30982,30984,30986],{"class":45,"line":1184},[43,30983,179],{"class":49},[43,30985,6693],{"class":53},[43,30987,61],{"class":49},[29,30989,30991],{"id":30990},"route-validation","Route Validation",[15,30993,30994,30995,30998,30999,31001,31002,31004,31005,4711,31008,31011],{},"The ",[40,30996,30997],{},"validate"," property accepts the ",[40,31000,29452],{}," as an argument. You can return a boolean value to determine whether or not this is a valid route to be rendered with this page. If you return ",[40,31003,1830],{},", and another match can't be found, this will cause a 404 error. You can also directly return an object with ",[40,31006,31007],{},"statusCode",[40,31009,31010],{},"statusMessage"," to respond immediately with an error (other matches will not be checked).",[15,31013,31014],{},"If you have a more complex use case, then you can use anonymous route middleware instead.",[33,31016,31018],{"className":35,"code":31017,"filename":30741,"language":37,"meta":30742,"style":38},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  validate: async (route) => {\n    \u002F\u002F Check if the id is made up of digits\n    return typeof route.params.id === 'string' && \u002F^\\d+$\u002F.test(route.params.id)\n  }\n})\n\u003C\u002Fscript>\n",[40,31019,31020,31036,31042,31061,31066,31104,31108,31112],{"__ignoreMap":38},[43,31021,31022,31024,31026,31028,31030,31032,31034],{"class":45,"line":46},[43,31023,50],{"class":49},[43,31025,54],{"class":53},[43,31027,58],{"class":57},[43,31029,27685],{"class":57},[43,31031,88],{"class":49},[43,31033,598],{"class":100},[43,31035,61],{"class":49},[43,31037,31038,31040],{"class":45,"line":64},[43,31039,30932],{"class":57},[43,31041,1803],{"class":49},[43,31043,31044,31047,31049,31051,31053,31055,31057,31059],{"class":45,"line":119},[43,31045,31046],{"class":57},"  validate",[43,31048,78],{"class":49},[43,31050,107],{"class":67},[43,31052,1069],{"class":49},[43,31054,29452],{"class":74},[43,31056,2408],{"class":49},[43,31058,113],{"class":67},[43,31060,116],{"class":49},[43,31062,31063],{"class":45,"line":150},[43,31064,31065],{"class":2797},"    \u002F\u002F Check if the id is made up of digits\n",[43,31067,31068,31070,31073,31076,31078,31081,31083,31086,31088,31091,31094,31096,31098,31101],{"class":45,"line":159},[43,31069,2737],{"class":67},[43,31071,31072],{"class":67}," typeof",[43,31074,31075],{"class":49}," route.params.id ",[43,31077,4914],{"class":67},[43,31079,31080],{"class":100}," 'string'",[43,31082,13498],{"class":67},[43,31084,31085],{"class":100}," \u002F",[43,31087,4720],{"class":67},[43,31089,31090],{"class":81},"\\d",[43,31092,31093],{"class":67},"+$",[43,31095,4711],{"class":100},[43,31097,1139],{"class":49},[43,31099,31100],{"class":57},"test",[43,31102,31103],{"class":49},"(route.params.id)\n",[43,31105,31106],{"class":45,"line":176},[43,31107,2028],{"class":49},[43,31109,31110],{"class":45,"line":1163},[43,31111,6117],{"class":49},[43,31113,31114,31116,31118],{"class":45,"line":1178},[43,31115,179],{"class":49},[43,31117,54],{"class":53},[43,31119,61],{"class":49},[471,31121,31122],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":38,"searchDepth":64,"depth":64,"links":31124},[31125],{"id":30990,"depth":64,"text":30991},"Nuxt file-system routing creates a route for every file in the pages\u002F directory.",{"date":31128,"field":5954,"tags":31129},"2025.01.22",[7252],{"icon":31131},"i-ph-signpost","\u002Fko\u002Frouting",{"title":30227,"description":31126},{"loc":31132},"ko\u002Frouting","N0AYrzcumxfHvc2ABtsO_mFq-Tq516-sW0IuC3vlG-M",{"id":31138,"title":31139,"body":31140,"description":31629,"extension":484,"meta":31630,"navigation":491,"path":31634,"seo":31635,"sitemap":31636,"stem":31637,"__hash__":31638},"ko\u002Fko\u002Fdesign-guide.md","Design-Guide",{"type":8,"value":31141,"toc":31616},[31142,31146,31150,31153,31200,31204,31207,31239,31243,31260,31264,31268,31271,31291,31295,31322,31326,31359,31363,31399,31403,31606,31610,31613],[11,31143,31145],{"id":31144},"edutrack-디자인-가이드","EduTrack - 디자인 가이드",[29,31147,31149],{"id":31148},"_1-색상-팔레트","1. 색상 팔레트",[15,31151,31152],{},"일관된 브랜드 이미지를 위해 색상 체계를 정의합니다.",[185,31154,31155,31164,31173,31182,31191],{},[188,31156,31157,4473,31160,31163],{},[21,31158,31159],{},"주 색상 (Primary):",[40,31161,31162],{},"#3F51B5"," (깊은 파란색) - 주요 인터페이스 요소에 사용",[188,31165,31166,4473,31169,31172],{},[21,31167,31168],{},"보조 색상 (Secondary):",[40,31170,31171],{},"#FF4081"," (밝은 분홍색) - 강조 요소 및 버튼 등에 사용",[188,31174,31175,4473,31178,31181],{},[21,31176,31177],{},"배경색 (Background):",[40,31179,31180],{},"#F5F5F5"," (밝은 회색) - 전체적인 배경 설정",[188,31183,31184,4473,31187,31190],{},[21,31185,31186],{},"텍스트 색상 (Text):",[40,31188,31189],{},"#333333"," (진한 회색) - 기본 본문 및 제목 색상",[188,31192,31193,4473,31196,31199],{},[21,31194,31195],{},"경고 및 알림 (Alert):",[40,31197,31198],{},"#FF5722"," (강렬한 주황색) - 에러 메시지, 중요한 경고 요소",[29,31201,31203],{"id":31202},"_2-타이포그래피","2. 타이포그래피",[15,31205,31206],{},"가독성과 일관성을 유지하는 폰트 스타일을 정의합니다.",[185,31208,31209,31215,31221,31227,31233],{},[188,31210,31211,31214],{},[21,31212,31213],{},"제목 (Heading):"," Roboto, 24px, Bold",[188,31216,31217,31220],{},[21,31218,31219],{},"부제목 (Subheading):"," Roboto, 18px, Medium",[188,31222,31223,31226],{},[21,31224,31225],{},"본문 (Body):"," Open Sans, 16px, Regular",[188,31228,31229,31232],{},[21,31230,31231],{},"캡션 및 보조 텍스트:"," Open Sans, 14px, Light",[188,31234,31235,31238],{},[21,31236,31237],{},"버튼 텍스트:"," Roboto, 16px, Bold",[29,31240,31242],{"id":31241},"_3-아이콘-및-그래픽-스타일","3. 아이콘 및 그래픽 스타일",[185,31244,31245,31251,31254,31257],{},[188,31246,31247,31248,247],{},"Material Design 아이콘 활용 (",[40,31249,31250],{},"https:\u002F\u002Ffonts.google.com\u002Ficons",[188,31252,31253],{},"단순하고 직관적인 UI를 위한 벡터 기반 아이콘 사용",[188,31255,31256],{},"SVG 및 PNG 지원 (최적화된 크기 유지 필요)",[188,31258,31259],{},"일관된 여백 및 패딩 적용 (아이콘 크기 24px 기본)",[29,31261,31263],{"id":31262},"_4-레이아웃-및-ui-컴포넌트","4. 레이아웃 및 UI 컴포넌트",[1273,31265,31267],{"id":31266},"_41-반응형-디자인","4.1 반응형 디자인",[15,31269,31270],{},"다양한 기기에서 원활한 사용 경험을 제공하기 위해 반응형 디자인을 적용합니다.",[185,31272,31273,31279,31285],{},[188,31274,31275,31278],{},[21,31276,31277],{},"모바일 (Mobile, \u003C 768px):"," 상단 내비게이션 바 및 햄버거 메뉴 사용",[188,31280,31281,31284],{},[21,31282,31283],{},"태블릿 (Tablet, 768px - 1024px):"," 네비게이션 바 확장 가능, 카드 기반 레이아웃 적용",[188,31286,31287,31290],{},[21,31288,31289],{},"데스크톱 (Desktop, > 1024px):"," 좌측 사이드바 메뉴 및 3열 레이아웃 구성 가능",[1273,31292,31294],{"id":31293},"_42-네비게이션-시스템","4.2 네비게이션 시스템",[185,31296,31297,31307,31316],{},[188,31298,31299,31302,31303,31306],{},[21,31300,31301],{},"모바일:"," 상단 내비게이션 바 + 햄버거 메뉴 (",[40,31304,31305],{},"position: fixed"," 적용)",[188,31308,31309,31312,31313,247],{},[21,31310,31311],{},"데스크톱:"," 좌측 네비게이션 바 고정 (",[40,31314,31315],{},"width: 250px",[188,31317,31318,31321],{},[21,31319,31320],{},"상태 기반 UI:"," 사용자의 진행 상태를 표시하는 프로그레스 바 활용",[1273,31323,31325],{"id":31324},"_43-카드-및-리스트-스타일","4.3 카드 및 리스트 스타일",[185,31327,31328,31341,31350],{},[188,31329,31330,31333,31334,31337,31338,247],{},[21,31331,31332],{},"카드 디자인:"," 그림자 효과 (",[40,31335,31336],{},"box-shadow","), 둥근 모서리 (",[40,31339,31340],{},"border-radius: 8px",[188,31342,31343,31346,31347,247],{},[21,31344,31345],{},"리스트 아이템:"," 적절한 간격 및 텍스트 정렬 (",[40,31348,31349],{},"line-height: 1.5",[188,31351,31352,31355,31356,247],{},[21,31353,31354],{},"버튼 스타일:"," 라운드 처리, 기본 크기 (",[40,31357,31358],{},"padding: 12px 20px",[29,31360,31362],{"id":31361},"_5-접근성-accessibility-및-ux-고려사항","5. 접근성 (Accessibility) 및 UX 고려사항",[185,31364,31365,31371,31380,31389],{},[188,31366,31367,31370],{},[21,31368,31369],{},"명확한 대비 (Contrast Ratio) 유지:"," 텍스트와 배경의 명확한 구분",[188,31372,31373,4473,31376,31379],{},[21,31374,31375],{},"키보드 네비게이션 지원:",[40,31377,31378],{},"tab","을 이용한 UI 이동 가능하도록 설정",[188,31381,31382,31385,31386,247],{},[21,31383,31384],{},"폼 요소 및 입력 필드:"," 충분한 클릭 영역 제공 (",[40,31387,31388],{},"min-height: 44px",[188,31390,31391,31394,31395,31398],{},[21,31392,31393],{},"애니메이션 최소화:"," 사용자의 피로도를 고려한 부드러운 트랜지션 적용 (",[40,31396,31397],{},"ease-in-out"," 사용)",[29,31400,31402],{"id":31401},"_6-코드-예제-및-적용-방식","6. 코드 예제 및 적용 방식",[33,31404,31406],{"className":10460,"code":31405,"language":10462,"meta":38,"style":38},"body {\n    font-family: 'Open Sans', sans-serif;\n    background-color: #F5F5F5;\n    color: #333333;\n    margin: 0;\n    padding: 0;\n}\n\nbutton {\n    background-color: #3F51B5;\n    color: white;\n    border: none;\n    padding: 12px 20px;\n    border-radius: 8px;\n    cursor: pointer;\n    transition: background-color 0.3s ease-in-out;\n}\n\nbutton:hover {\n    background-color: #303F9F;\n}\n",[40,31407,31408,31414,31430,31440,31451,31462,31472,31476,31480,31486,31496,31506,31517,31533,31546,31557,31574,31578,31582,31591,31602],{"__ignoreMap":38},[43,31409,31410,31412],{"class":45,"line":46},[43,31411,13925],{"class":53},[43,31413,116],{"class":49},[43,31415,31416,31419,31421,31424,31426,31428],{"class":45,"line":64},[43,31417,31418],{"class":81},"    font-family",[43,31420,78],{"class":49},[43,31422,31423],{"class":100},"'Open Sans'",[43,31425,104],{"class":49},[43,31427,11867],{"class":81},[43,31429,2995],{"class":49},[43,31431,31432,31434,31436,31438],{"class":45,"line":119},[43,31433,28287],{"class":81},[43,31435,78],{"class":49},[43,31437,31180],{"class":81},[43,31439,2995],{"class":49},[43,31441,31442,31445,31447,31449],{"class":45,"line":150},[43,31443,31444],{"class":81},"    color",[43,31446,78],{"class":49},[43,31448,31189],{"class":81},[43,31450,2995],{"class":49},[43,31452,31453,31456,31458,31460],{"class":45,"line":159},[43,31454,31455],{"class":81},"    margin",[43,31457,78],{"class":49},[43,31459,4401],{"class":81},[43,31461,2995],{"class":49},[43,31463,31464,31466,31468,31470],{"class":45,"line":176},[43,31465,28274],{"class":81},[43,31467,78],{"class":49},[43,31469,4401],{"class":81},[43,31471,2995],{"class":49},[43,31473,31474],{"class":45,"line":1163},[43,31475,2034],{"class":49},[43,31477,31478],{"class":45,"line":1178},[43,31479,1782],{"emptyLinePlaceholder":491},[43,31481,31482,31484],{"class":45,"line":1184},[43,31483,10437],{"class":53},[43,31485,116],{"class":49},[43,31487,31488,31490,31492,31494],{"class":45,"line":1190},[43,31489,28287],{"class":81},[43,31491,78],{"class":49},[43,31493,31162],{"class":81},[43,31495,2995],{"class":49},[43,31497,31498,31500,31502,31504],{"class":45,"line":1996},[43,31499,31444],{"class":81},[43,31501,78],{"class":49},[43,31503,10518],{"class":81},[43,31505,2995],{"class":49},[43,31507,31508,31511,31513,31515],{"class":45,"line":2008},[43,31509,31510],{"class":81},"    border",[43,31512,78],{"class":49},[43,31514,12507],{"class":81},[43,31516,2995],{"class":49},[43,31518,31519,31521,31523,31525,31527,31529,31531],{"class":45,"line":2025},[43,31520,28274],{"class":81},[43,31522,78],{"class":49},[43,31524,12358],{"class":81},[43,31526,10489],{"class":67},[43,31528,10492],{"class":81},[43,31530,10489],{"class":67},[43,31532,2995],{"class":49},[43,31534,31535,31538,31540,31542,31544],{"class":45,"line":2031},[43,31536,31537],{"class":81},"    border-radius",[43,31539,78],{"class":49},[43,31541,11902],{"class":81},[43,31543,10489],{"class":67},[43,31545,2995],{"class":49},[43,31547,31548,31551,31553,31555],{"class":45,"line":2037},[43,31549,31550],{"class":81},"    cursor",[43,31552,78],{"class":49},[43,31554,12709],{"class":81},[43,31556,2995],{"class":49},[43,31558,31559,31562,31565,31567,31569,31572],{"class":45,"line":2042},[43,31560,31561],{"class":81},"    transition",[43,31563,31564],{"class":49},": background-color ",[43,31566,12747],{"class":81},[43,31568,12403],{"class":67},[43,31570,31571],{"class":81}," ease-in-out",[43,31573,2995],{"class":49},[43,31575,31576],{"class":45,"line":2516},[43,31577,2034],{"class":49},[43,31579,31580],{"class":45,"line":2998},[43,31581,1782],{"emptyLinePlaceholder":491},[43,31583,31584,31586,31589],{"class":45,"line":3003},[43,31585,10437],{"class":53},[43,31587,31588],{"class":57},":hover",[43,31590,116],{"class":49},[43,31592,31593,31595,31597,31600],{"class":45,"line":3009},[43,31594,28287],{"class":81},[43,31596,78],{"class":49},[43,31598,31599],{"class":81},"#303F9F",[43,31601,2995],{"class":49},[43,31603,31604],{"class":45,"line":3022},[43,31605,2034],{"class":49},[29,31607,31609],{"id":31608},"_7-결론","7. 결론",[15,31611,31612],{},"개발자는 UI\u002FUX 디자인 가이드를 준수하여 일관된 사용자 경험을 제공해야 합니다. 이 가이드라인을 따르면 프로젝트 전반의 디자인 품질이 향상되고, 유지보수가 용이해집니다. 앞으로도 지속적으로 가이드를 개선하여 더 나은 디자인 환경을 구축해 나가겠습니다.",[471,31614,31615],{},"html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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);}",{"title":38,"searchDepth":64,"depth":64,"links":31617},[31618,31619,31620,31621,31626,31627,31628],{"id":31148,"depth":64,"text":31149},{"id":31202,"depth":64,"text":31203},{"id":31241,"depth":64,"text":31242},{"id":31262,"depth":64,"text":31263,"children":31622},[31623,31624,31625],{"id":31266,"depth":119,"text":31267},{"id":31293,"depth":119,"text":31294},{"id":31324,"depth":119,"text":31325},{"id":31361,"depth":64,"text":31362},{"id":31401,"depth":64,"text":31402},{"id":31608,"depth":64,"text":31609},"A comprehensive guide on design principles and best practices for developers.",{"date":31631,"field":5954,"tags":31632},"2025.01.19",[31633],"design-system","\u002Fko\u002Fdesign-guide",{"title":31139,"description":31629},{"loc":31634},"ko\u002Fdesign-guide","IULOnoYLldXU9ar0HRFKzY5j3O0oKknr44Vrw5ENR5o",1791391655660]