[{"data":1,"prerenderedAt":4671},["ShallowReactive",2],{"post-document:\u002Ffrontend\u002Fvue\u002F2026\u002F08\u002F02\u002Fvue-composition-api\u002F":3},{"id":4,"title":5,"body":6,"categories":4617,"date":4620,"description":4621,"extension":4622,"image":4623,"key_concepts":4624,"last_modified_at":4623,"legacyPath":4628,"meta":4629,"navigation":110,"part":4623,"path":4658,"published":110,"robots":4623,"seo":4659,"series":4623,"stem":4660,"strengths":4661,"summary":4664,"tags":4665,"tradeoffs":4667,"__hash__":4670},"posts\u002Fposts\u002FFrontend\u002FVue\u002F2026-08-02-vue-composition-api.md","03. Vue Composition API 정리",{"type":7,"value":8,"toc":4577},"minimark",[9,14,23,26,45,48,51,59,309,324,328,331,337,340,485,499,506,511,920,932,983,1006,1013,1040,1047,1058,1439,1445,1448,1517,1520,1523,1532,1600,1604,1607,1618,1716,1719,1810,1815,1822,1827,1830,1833,1836,2101,2113,2123,2199,2204,2268,2275,2371,2379,2386,2391,2394,2411,2440,2688,2691,2695,2698,2854,2857,2861,2866,2873,3017,3020,3026,3036,3042,3045,3127,3129,3135,3138,3146,3182,3187,3219,3226,3231,3249,3341,3348,3410,3421,3425,3428,3590,3595,3598,3601,3624,3740,3743,3746,3751,3754,3756,3767,3849,3858,3891,3900,3902,3907,3910,3913,3936,4063,4066,4068,4072,4078,4080,4097,4106,4110,4117,4301,4445,4448,4451,4456,4469,4473,4476,4482,4485,4532,4542,4546,4571,4573],[10,11,13],"h2",{"id":12},"composition-api란","Composition API란?",[15,16,17,18,22],"p",{},"자자, 이번에는 Vue의 ",[19,20,21],"strong",{},"Composition API","를 정리해보자.",[15,24,25],{},"Vue 컴포넌트에는 보통 다음과 같은 로직이 들어간다.",[27,28,29,33,36,39,42],"ul",{},[30,31,32],"li",{},"화면에서 사용하는 상태",[30,34,35],{},"상태를 변경하는 함수",[30,37,38],{},"상태에서 계산되는 값",[30,40,41],{},"서버 요청이나 이벤트 감시",[30,43,44],{},"컴포넌트 생명주기에 맞춰 실행할 작업",[15,46,47],{},"Composition API는 이런 로직을 옵션별로 흩어놓기보다, 기능 단위로 가까이 모아서 작성하는 방식이다.",[15,49,50],{},"그래서 컴포넌트가 커질수록 어떤 상태와 함수가 한 기능을 이루는지 파악하기 쉬워진다.",[15,52,53,54,58],{},"Vue 3에서는 ",[55,56,57],"code",{},"\u003Cscript setup>","과 함께 Composition API를 많이 사용한다.",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup>\nimport { computed, ref } from 'vue'\n\nconst count = ref(0)\n\nconst doubleCount = computed(() => {\n  return count.value * 2\n})\n\nconst increase = () => {\n  count.value++\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>현재 값: {{ count }}\u003C\u002Fp>\n  \u003Cp>두 배 값: {{ doubleCount }}\u003C\u002Fp>\n  \u003Cbutton @click=\"increase\">증가\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n","vue","",[55,67,68,88,105,112,137,142,164,179,185,190,207,216,222,232,237,247,262,276,300],{"__ignoreMap":65},[69,70,73,77,81,85],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"sVt8B","\u003C",[69,78,80],{"class":79},"s9eBZ","script",[69,82,84],{"class":83},"sScJk"," setup",[69,86,87],{"class":75},">\n",[69,89,91,95,98,101],{"class":71,"line":90},2,[69,92,94],{"class":93},"szBVR","import",[69,96,97],{"class":75}," { computed, ref } ",[69,99,100],{"class":93},"from",[69,102,104],{"class":103},"sZZnC"," 'vue'\n",[69,106,108],{"class":71,"line":107},3,[69,109,111],{"emptyLinePlaceholder":110},true,"\n",[69,113,115,118,122,125,128,131,134],{"class":71,"line":114},4,[69,116,117],{"class":93},"const",[69,119,121],{"class":120},"sj4cs"," count",[69,123,124],{"class":93}," =",[69,126,127],{"class":83}," ref",[69,129,130],{"class":75},"(",[69,132,133],{"class":120},"0",[69,135,136],{"class":75},")\n",[69,138,140],{"class":71,"line":139},5,[69,141,111],{"emptyLinePlaceholder":110},[69,143,145,147,150,152,155,158,161],{"class":71,"line":144},6,[69,146,117],{"class":93},[69,148,149],{"class":120}," doubleCount",[69,151,124],{"class":93},[69,153,154],{"class":83}," computed",[69,156,157],{"class":75},"(() ",[69,159,160],{"class":93},"=>",[69,162,163],{"class":75}," {\n",[69,165,167,170,173,176],{"class":71,"line":166},7,[69,168,169],{"class":93},"  return",[69,171,172],{"class":75}," count.value ",[69,174,175],{"class":93},"*",[69,177,178],{"class":120}," 2\n",[69,180,182],{"class":71,"line":181},8,[69,183,184],{"class":75},"})\n",[69,186,188],{"class":71,"line":187},9,[69,189,111],{"emptyLinePlaceholder":110},[69,191,193,195,198,200,203,205],{"class":71,"line":192},10,[69,194,117],{"class":93},[69,196,197],{"class":83}," increase",[69,199,124],{"class":93},[69,201,202],{"class":75}," () ",[69,204,160],{"class":93},[69,206,163],{"class":75},[69,208,210,213],{"class":71,"line":209},11,[69,211,212],{"class":75},"  count.value",[69,214,215],{"class":93},"++\n",[69,217,219],{"class":71,"line":218},12,[69,220,221],{"class":75},"}\n",[69,223,225,228,230],{"class":71,"line":224},13,[69,226,227],{"class":75},"\u003C\u002F",[69,229,80],{"class":79},[69,231,87],{"class":75},[69,233,235],{"class":71,"line":234},14,[69,236,111],{"emptyLinePlaceholder":110},[69,238,240,242,245],{"class":71,"line":239},15,[69,241,76],{"class":75},[69,243,244],{"class":79},"template",[69,246,87],{"class":75},[69,248,250,253,255,258,260],{"class":71,"line":249},16,[69,251,252],{"class":75},"  \u003C",[69,254,15],{"class":79},[69,256,257],{"class":75},">현재 값: {{ count }}\u003C\u002F",[69,259,15],{"class":79},[69,261,87],{"class":75},[69,263,265,267,269,272,274],{"class":71,"line":264},17,[69,266,252],{"class":75},[69,268,15],{"class":79},[69,270,271],{"class":75},">두 배 값: {{ doubleCount }}\u003C\u002F",[69,273,15],{"class":79},[69,275,87],{"class":75},[69,277,279,281,284,287,290,293,296,298],{"class":71,"line":278},18,[69,280,252],{"class":75},[69,282,283],{"class":79},"button",[69,285,286],{"class":83}," @click",[69,288,289],{"class":75},"=",[69,291,292],{"class":103},"\"increase\"",[69,294,295],{"class":75},">증가\u003C\u002F",[69,297,283],{"class":79},[69,299,87],{"class":75},[69,301,303,305,307],{"class":71,"line":302},19,[69,304,227],{"class":75},[69,306,244],{"class":79},[69,308,87],{"class":75},[15,310,311,312,315,316,319,320,323],{},"이 코드에서는 ",[55,313,314],{},"count"," 상태, ",[55,317,318],{},"doubleCount"," 계산값, ",[55,321,322],{},"increase"," 함수가 한곳에 모여 있다. 화면과 연결되는 템플릿도 아래에서 바로 확인할 수 있다.",[10,325,327],{"id":326},"composition-api에서-자주-쓰는-함수","Composition API에서 자주 쓰는 함수",[15,329,330],{},"자! 다 외워보자!",[15,332,333],{},[334,335,336],"del",{},"안 외워도 된다메 이 엉터리 블로그 주인장아",[15,338,339],{},"우선 아래 함수들의 역할을 구분하면 된다.",[341,342,343,359],"table",{},[344,345,346],"thead",{},[347,348,349,353,356],"tr",{},[350,351,352],"th",{},"분류",[350,354,355],{},"주요 함수",[350,357,358],{},"역할",[360,361,362,380,399,418,434,453,469],"tbody",{},[347,363,364,368,377],{},[365,366,367],"td",{},"반응형 상태",[365,369,370,373,374],{},[55,371,372],{},"ref",", ",[55,375,376],{},"reactive",[365,378,379],{},"변경을 추적하는 상태를 만든다.",[347,381,382,385,396],{},[365,383,384],{},"계산과 감시",[365,386,387,373,390,373,393],{},[55,388,389],{},"computed",[55,391,392],{},"watch",[55,394,395],{},"watchEffect",[365,397,398],{},"상태에서 값을 계산하거나 변경 후 작업을 실행한다.",[347,400,401,404,415],{},[365,402,403],{},"생명주기",[365,405,406,373,409,373,412],{},[55,407,408],{},"onMounted",[55,410,411],{},"onUpdated",[55,413,414],{},"onUnmounted",[365,416,417],{},"컴포넌트의 생성·수정·제거 시점에 코드를 실행한다.",[347,419,420,423,431],{},[365,421,422],{},"컴포넌트 통신",[365,424,425,373,428],{},[55,426,427],{},"defineProps",[55,429,430],{},"defineEmits",[365,432,433],{},"부모와 자식 컴포넌트 사이의 데이터를 연결한다.",[347,435,436,439,450],{},[365,437,438],{},"반응성 변환",[365,440,441,373,444,373,447],{},[55,442,443],{},"toRef",[55,445,446],{},"toRefs",[55,448,449],{},"unref",[365,451,452],{},"반응형 객체의 속성을 ref로 연결하거나 값을 꺼낸다.",[347,454,455,458,466],{},[365,456,457],{},"의존성 주입",[365,459,460,373,463],{},[55,461,462],{},"provide",[55,464,465],{},"inject",[365,467,468],{},"깊은 컴포넌트 트리에서 값을 공유한다.",[347,470,471,474,482],{},[365,472,473],{},"DOM·렌더링",[365,475,476,373,479],{},[55,477,478],{},"nextTick",[55,480,481],{},"h",[365,483,484],{},"DOM 반영 시점을 기다리거나 렌더링을 직접 제어한다.",[15,486,487,488,373,490,373,492,373,494,373,496,498],{},"이번 글에서는 그중에서도 ",[55,489,372],{},[55,491,376],{},[55,493,389],{},[55,495,392],{},[55,497,395],{},"를 중심으로 살펴보자.",[10,500,502,505],{"id":501},"ref로-반응형-상태-만들기",[55,503,504],{},"ref()","로 반응형 상태 만들기",[15,507,508,510],{},[55,509,504],{},"는 원시 값과 객체, 배열을 반응형 상태로 감싸는 함수다.",[60,512,514],{"className":62,"code":513,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { ref } from 'vue'\n\nconst count = ref(0)\nconst name = ref('홍길동')\nconst isActive = ref(true)\nconst items = ref(['사과', '배'])\nconst user = ref({\n  name: '이순신',\n  age: 30\n})\n\nconst increase = () => {\n  count.value++\n}\n\nconst changeUserName = () => {\n  user.value.name = '장보고'\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>카운트: {{ count }}\u003C\u002Fp>\n  \u003Cp>이름: \u003Cinput v-model=\"name\" \u002F>\u003C\u002Fp>\n  \u003Cp>활성 상태: {{ isActive ? '활성' : '비활성' }}\u003C\u002Fp>\n  \u003Cp>과일 목록: {{ items.join(', ') }}\u003C\u002Fp>\n  \u003Cp>사용자: {{ user.name }} \u002F {{ user.age }}세\u003C\u002Fp>\n\n  \u003Cbutton @click=\"increase\">카운트 증가\u003C\u002Fbutton>\n  \u003Cbutton @click=\"isActive = !isActive\">상태 토글\u003C\u002Fbutton>\n  \u003Cbutton @click=\"items.push('귤')\">과일 추가\u003C\u002Fbutton>\n  \u003Cbutton @click=\"changeUserName\">이름 변경\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[55,515,516,526,537,541,557,575,593,618,632,643,651,655,659,673,679,683,687,702,712,716,725,730,739,753,781,795,809,823,828,848,869,890,911],{"__ignoreMap":65},[69,517,518,520,522,524],{"class":71,"line":72},[69,519,76],{"class":75},[69,521,80],{"class":79},[69,523,84],{"class":83},[69,525,87],{"class":75},[69,527,528,530,533,535],{"class":71,"line":90},[69,529,94],{"class":93},[69,531,532],{"class":75}," { ref } ",[69,534,100],{"class":93},[69,536,104],{"class":103},[69,538,539],{"class":71,"line":107},[69,540,111],{"emptyLinePlaceholder":110},[69,542,543,545,547,549,551,553,555],{"class":71,"line":114},[69,544,117],{"class":93},[69,546,121],{"class":120},[69,548,124],{"class":93},[69,550,127],{"class":83},[69,552,130],{"class":75},[69,554,133],{"class":120},[69,556,136],{"class":75},[69,558,559,561,564,566,568,570,573],{"class":71,"line":139},[69,560,117],{"class":93},[69,562,563],{"class":120}," name",[69,565,124],{"class":93},[69,567,127],{"class":83},[69,569,130],{"class":75},[69,571,572],{"class":103},"'홍길동'",[69,574,136],{"class":75},[69,576,577,579,582,584,586,588,591],{"class":71,"line":144},[69,578,117],{"class":93},[69,580,581],{"class":120}," isActive",[69,583,124],{"class":93},[69,585,127],{"class":83},[69,587,130],{"class":75},[69,589,590],{"class":120},"true",[69,592,136],{"class":75},[69,594,595,597,600,602,604,607,610,612,615],{"class":71,"line":166},[69,596,117],{"class":93},[69,598,599],{"class":120}," items",[69,601,124],{"class":93},[69,603,127],{"class":83},[69,605,606],{"class":75},"([",[69,608,609],{"class":103},"'사과'",[69,611,373],{"class":75},[69,613,614],{"class":103},"'배'",[69,616,617],{"class":75},"])\n",[69,619,620,622,625,627,629],{"class":71,"line":181},[69,621,117],{"class":93},[69,623,624],{"class":120}," user",[69,626,124],{"class":93},[69,628,127],{"class":83},[69,630,631],{"class":75},"({\n",[69,633,634,637,640],{"class":71,"line":187},[69,635,636],{"class":75},"  name: ",[69,638,639],{"class":103},"'이순신'",[69,641,642],{"class":75},",\n",[69,644,645,648],{"class":71,"line":192},[69,646,647],{"class":75},"  age: ",[69,649,650],{"class":120},"30\n",[69,652,653],{"class":71,"line":209},[69,654,184],{"class":75},[69,656,657],{"class":71,"line":218},[69,658,111],{"emptyLinePlaceholder":110},[69,660,661,663,665,667,669,671],{"class":71,"line":224},[69,662,117],{"class":93},[69,664,197],{"class":83},[69,666,124],{"class":93},[69,668,202],{"class":75},[69,670,160],{"class":93},[69,672,163],{"class":75},[69,674,675,677],{"class":71,"line":234},[69,676,212],{"class":75},[69,678,215],{"class":93},[69,680,681],{"class":71,"line":239},[69,682,221],{"class":75},[69,684,685],{"class":71,"line":249},[69,686,111],{"emptyLinePlaceholder":110},[69,688,689,691,694,696,698,700],{"class":71,"line":264},[69,690,117],{"class":93},[69,692,693],{"class":83}," changeUserName",[69,695,124],{"class":93},[69,697,202],{"class":75},[69,699,160],{"class":93},[69,701,163],{"class":75},[69,703,704,707,709],{"class":71,"line":278},[69,705,706],{"class":75},"  user.value.name ",[69,708,289],{"class":93},[69,710,711],{"class":103}," '장보고'\n",[69,713,714],{"class":71,"line":302},[69,715,221],{"class":75},[69,717,719,721,723],{"class":71,"line":718},20,[69,720,227],{"class":75},[69,722,80],{"class":79},[69,724,87],{"class":75},[69,726,728],{"class":71,"line":727},21,[69,729,111],{"emptyLinePlaceholder":110},[69,731,733,735,737],{"class":71,"line":732},22,[69,734,76],{"class":75},[69,736,244],{"class":79},[69,738,87],{"class":75},[69,740,742,744,746,749,751],{"class":71,"line":741},23,[69,743,252],{"class":75},[69,745,15],{"class":79},[69,747,748],{"class":75},">카운트: {{ count }}\u003C\u002F",[69,750,15],{"class":79},[69,752,87],{"class":75},[69,754,756,758,760,763,766,769,771,774,777,779],{"class":71,"line":755},24,[69,757,252],{"class":75},[69,759,15],{"class":79},[69,761,762],{"class":75},">이름: \u003C",[69,764,765],{"class":79},"input",[69,767,768],{"class":83}," v-model",[69,770,289],{"class":75},[69,772,773],{"class":103},"\"name\"",[69,775,776],{"class":75}," \u002F>\u003C\u002F",[69,778,15],{"class":79},[69,780,87],{"class":75},[69,782,784,786,788,791,793],{"class":71,"line":783},25,[69,785,252],{"class":75},[69,787,15],{"class":79},[69,789,790],{"class":75},">활성 상태: {{ isActive ? '활성' : '비활성' }}\u003C\u002F",[69,792,15],{"class":79},[69,794,87],{"class":75},[69,796,798,800,802,805,807],{"class":71,"line":797},26,[69,799,252],{"class":75},[69,801,15],{"class":79},[69,803,804],{"class":75},">과일 목록: {{ items.join(', ') }}\u003C\u002F",[69,806,15],{"class":79},[69,808,87],{"class":75},[69,810,812,814,816,819,821],{"class":71,"line":811},27,[69,813,252],{"class":75},[69,815,15],{"class":79},[69,817,818],{"class":75},">사용자: {{ user.name }} \u002F {{ user.age }}세\u003C\u002F",[69,820,15],{"class":79},[69,822,87],{"class":75},[69,824,826],{"class":71,"line":825},28,[69,827,111],{"emptyLinePlaceholder":110},[69,829,831,833,835,837,839,841,844,846],{"class":71,"line":830},29,[69,832,252],{"class":75},[69,834,283],{"class":79},[69,836,286],{"class":83},[69,838,289],{"class":75},[69,840,292],{"class":103},[69,842,843],{"class":75},">카운트 증가\u003C\u002F",[69,845,283],{"class":79},[69,847,87],{"class":75},[69,849,851,853,855,857,859,862,865,867],{"class":71,"line":850},30,[69,852,252],{"class":75},[69,854,283],{"class":79},[69,856,286],{"class":83},[69,858,289],{"class":75},[69,860,861],{"class":103},"\"isActive = !isActive\"",[69,863,864],{"class":75},">상태 토글\u003C\u002F",[69,866,283],{"class":79},[69,868,87],{"class":75},[69,870,872,874,876,878,880,883,886,888],{"class":71,"line":871},31,[69,873,252],{"class":75},[69,875,283],{"class":79},[69,877,286],{"class":83},[69,879,289],{"class":75},[69,881,882],{"class":103},"\"items.push('귤')\"",[69,884,885],{"class":75},">과일 추가\u003C\u002F",[69,887,283],{"class":79},[69,889,87],{"class":75},[69,891,893,895,897,899,901,904,907,909],{"class":71,"line":892},32,[69,894,252],{"class":75},[69,896,283],{"class":79},[69,898,286],{"class":83},[69,900,289],{"class":75},[69,902,903],{"class":103},"\"changeUserName\"",[69,905,906],{"class":75},">이름 변경\u003C\u002F",[69,908,283],{"class":79},[69,910,87],{"class":75},[69,912,914,916,918],{"class":71,"line":913},33,[69,915,227],{"class":75},[69,917,244],{"class":79},[69,919,87],{"class":75},[15,921,922,924,925,928,929,931],{},[55,923,57],{}," 안의 JavaScript에서는 ",[55,926,927],{},".value","를 사용하고, 템플릿에서는 Vue가 자동으로 언래핑해주므로 ",[55,930,927],{},"를 생략한다.",[60,933,937],{"className":934,"code":935,"language":936,"meta":65,"style":65},"language-js shiki shiki-themes github-light github-dark","const count = ref(0)\n\n\u002F\u002F JavaScript\ncount.value++\nconsole.log(count.value)\n","js",[55,938,939,955,959,965,972],{"__ignoreMap":65},[69,940,941,943,945,947,949,951,953],{"class":71,"line":72},[69,942,117],{"class":93},[69,944,121],{"class":120},[69,946,124],{"class":93},[69,948,127],{"class":83},[69,950,130],{"class":75},[69,952,133],{"class":120},[69,954,136],{"class":75},[69,956,957],{"class":71,"line":90},[69,958,111],{"emptyLinePlaceholder":110},[69,960,961],{"class":71,"line":107},[69,962,964],{"class":963},"sJ8bj","\u002F\u002F JavaScript\n",[69,966,967,970],{"class":71,"line":114},[69,968,969],{"class":75},"count.value",[69,971,215],{"class":93},[69,973,974,977,980],{"class":71,"line":139},[69,975,976],{"class":75},"console.",[69,978,979],{"class":83},"log",[69,981,982],{"class":75},"(count.value)\n",[60,984,986],{"className":62,"code":985,"language":64,"meta":65,"style":65},"\u003C!-- template -->\n\u003Cp>{{ count }}\u003C\u002Fp>\n",[55,987,988,993],{"__ignoreMap":65},[69,989,990],{"class":71,"line":72},[69,991,992],{"class":963},"\u003C!-- template -->\n",[69,994,995,997,999,1002,1004],{"class":71,"line":90},[69,996,76],{"class":75},[69,998,15],{"class":79},[69,1000,1001],{"class":75},">{{ count }}\u003C\u002F",[69,1003,15],{"class":79},[69,1005,87],{"class":75},[1007,1008,1010,1012],"h3",{"id":1009},"ref를-사용할-때-기억할-점",[55,1011,372],{},"를 사용할 때 기억할 점",[27,1014,1015,1023,1029,1034],{},[30,1016,1017,1019,1020,1022],{},[55,1018,372],{},"를 사용하려면 ",[55,1021,64],{},"에서 import해야 한다.",[30,1024,1025,1026,1028],{},"JavaScript에서 값을 읽거나 변경할 때는 ",[55,1027,927],{},"를 사용한다.",[30,1030,1031,1032,931],{},"템플릿에서는 ",[55,1033,927],{},[30,1035,1036,1037,1039],{},"객체나 배열을 ",[55,1038,372],{},"로 만들면 내부 값에도 반응성이 연결된다.",[10,1041,1043,1046],{"id":1042},"reactive로-객체와-배열-다루기",[55,1044,1045],{},"reactive()","로 객체와 배열 다루기",[15,1048,1049,1051,1052,1054,1055,1057],{},[55,1050,1045],{},"는 객체나 배열 자체를 반응형 객체로 만든다. ",[55,1053,372],{},"와 달리 ",[55,1056,927],{}," 없이 속성에 접근한다.",[60,1059,1061],{"className":62,"code":1060,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { reactive } from 'vue'\n\nconst user = reactive({\n  name: '이순신',\n  age: 30\n})\n\nconst items = reactive(['사과', '바나나'])\n\nconst celebrate = () => {\n  user.age++\n}\n\nconst addItem = () => {\n  items.push(`과일 ${items.length + 1}`)\n}\n\nconst removeItem = (index) => {\n  items.splice(index, 1)\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>{{ user.name }} \u002F {{ user.age }}세\u003C\u002Fp>\n  \u003Cbutton @click=\"celebrate\">나이 한 살 추가\u003C\u002Fbutton>\n\n  \u003Cul>\n    \u003Cli v-for=\"(item, index) in items\" :key=\"item\">\n      {{ item }}\n      \u003Cbutton @click=\"removeItem(index)\">삭제\u003C\u002Fbutton>\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n  \u003Cbutton @click=\"addItem\">과일 추가\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[55,1062,1063,1073,1084,1088,1101,1109,1115,1119,1123,1144,1148,1163,1170,1174,1178,1193,1226,1230,1234,1257,1272,1276,1284,1288,1296,1309,1329,1333,1341,1366,1371,1392,1401,1410,1430],{"__ignoreMap":65},[69,1064,1065,1067,1069,1071],{"class":71,"line":72},[69,1066,76],{"class":75},[69,1068,80],{"class":79},[69,1070,84],{"class":83},[69,1072,87],{"class":75},[69,1074,1075,1077,1080,1082],{"class":71,"line":90},[69,1076,94],{"class":93},[69,1078,1079],{"class":75}," { reactive } ",[69,1081,100],{"class":93},[69,1083,104],{"class":103},[69,1085,1086],{"class":71,"line":107},[69,1087,111],{"emptyLinePlaceholder":110},[69,1089,1090,1092,1094,1096,1099],{"class":71,"line":114},[69,1091,117],{"class":93},[69,1093,624],{"class":120},[69,1095,124],{"class":93},[69,1097,1098],{"class":83}," reactive",[69,1100,631],{"class":75},[69,1102,1103,1105,1107],{"class":71,"line":139},[69,1104,636],{"class":75},[69,1106,639],{"class":103},[69,1108,642],{"class":75},[69,1110,1111,1113],{"class":71,"line":144},[69,1112,647],{"class":75},[69,1114,650],{"class":120},[69,1116,1117],{"class":71,"line":166},[69,1118,184],{"class":75},[69,1120,1121],{"class":71,"line":181},[69,1122,111],{"emptyLinePlaceholder":110},[69,1124,1125,1127,1129,1131,1133,1135,1137,1139,1142],{"class":71,"line":187},[69,1126,117],{"class":93},[69,1128,599],{"class":120},[69,1130,124],{"class":93},[69,1132,1098],{"class":83},[69,1134,606],{"class":75},[69,1136,609],{"class":103},[69,1138,373],{"class":75},[69,1140,1141],{"class":103},"'바나나'",[69,1143,617],{"class":75},[69,1145,1146],{"class":71,"line":192},[69,1147,111],{"emptyLinePlaceholder":110},[69,1149,1150,1152,1155,1157,1159,1161],{"class":71,"line":209},[69,1151,117],{"class":93},[69,1153,1154],{"class":83}," celebrate",[69,1156,124],{"class":93},[69,1158,202],{"class":75},[69,1160,160],{"class":93},[69,1162,163],{"class":75},[69,1164,1165,1168],{"class":71,"line":218},[69,1166,1167],{"class":75},"  user.age",[69,1169,215],{"class":93},[69,1171,1172],{"class":71,"line":224},[69,1173,221],{"class":75},[69,1175,1176],{"class":71,"line":234},[69,1177,111],{"emptyLinePlaceholder":110},[69,1179,1180,1182,1185,1187,1189,1191],{"class":71,"line":239},[69,1181,117],{"class":93},[69,1183,1184],{"class":83}," addItem",[69,1186,124],{"class":93},[69,1188,202],{"class":75},[69,1190,160],{"class":93},[69,1192,163],{"class":75},[69,1194,1195,1198,1201,1203,1206,1209,1212,1215,1218,1221,1224],{"class":71,"line":249},[69,1196,1197],{"class":75},"  items.",[69,1199,1200],{"class":83},"push",[69,1202,130],{"class":75},[69,1204,1205],{"class":103},"`과일 ${",[69,1207,1208],{"class":75},"items",[69,1210,1211],{"class":103},".",[69,1213,1214],{"class":120},"length",[69,1216,1217],{"class":93}," +",[69,1219,1220],{"class":120}," 1",[69,1222,1223],{"class":103},"}`",[69,1225,136],{"class":75},[69,1227,1228],{"class":71,"line":264},[69,1229,221],{"class":75},[69,1231,1232],{"class":71,"line":278},[69,1233,111],{"emptyLinePlaceholder":110},[69,1235,1236,1238,1241,1243,1246,1250,1253,1255],{"class":71,"line":302},[69,1237,117],{"class":93},[69,1239,1240],{"class":83}," removeItem",[69,1242,124],{"class":93},[69,1244,1245],{"class":75}," (",[69,1247,1249],{"class":1248},"s4XuR","index",[69,1251,1252],{"class":75},") ",[69,1254,160],{"class":93},[69,1256,163],{"class":75},[69,1258,1259,1261,1264,1267,1270],{"class":71,"line":718},[69,1260,1197],{"class":75},[69,1262,1263],{"class":83},"splice",[69,1265,1266],{"class":75},"(index, ",[69,1268,1269],{"class":120},"1",[69,1271,136],{"class":75},[69,1273,1274],{"class":71,"line":727},[69,1275,221],{"class":75},[69,1277,1278,1280,1282],{"class":71,"line":732},[69,1279,227],{"class":75},[69,1281,80],{"class":79},[69,1283,87],{"class":75},[69,1285,1286],{"class":71,"line":741},[69,1287,111],{"emptyLinePlaceholder":110},[69,1289,1290,1292,1294],{"class":71,"line":755},[69,1291,76],{"class":75},[69,1293,244],{"class":79},[69,1295,87],{"class":75},[69,1297,1298,1300,1302,1305,1307],{"class":71,"line":783},[69,1299,252],{"class":75},[69,1301,15],{"class":79},[69,1303,1304],{"class":75},">{{ user.name }} \u002F {{ user.age }}세\u003C\u002F",[69,1306,15],{"class":79},[69,1308,87],{"class":75},[69,1310,1311,1313,1315,1317,1319,1322,1325,1327],{"class":71,"line":797},[69,1312,252],{"class":75},[69,1314,283],{"class":79},[69,1316,286],{"class":83},[69,1318,289],{"class":75},[69,1320,1321],{"class":103},"\"celebrate\"",[69,1323,1324],{"class":75},">나이 한 살 추가\u003C\u002F",[69,1326,283],{"class":79},[69,1328,87],{"class":75},[69,1330,1331],{"class":71,"line":811},[69,1332,111],{"emptyLinePlaceholder":110},[69,1334,1335,1337,1339],{"class":71,"line":825},[69,1336,252],{"class":75},[69,1338,27],{"class":79},[69,1340,87],{"class":75},[69,1342,1343,1346,1348,1351,1353,1356,1359,1361,1364],{"class":71,"line":830},[69,1344,1345],{"class":75},"    \u003C",[69,1347,30],{"class":79},[69,1349,1350],{"class":83}," v-for",[69,1352,289],{"class":75},[69,1354,1355],{"class":103},"\"(item, index) in items\"",[69,1357,1358],{"class":83}," :key",[69,1360,289],{"class":75},[69,1362,1363],{"class":103},"\"item\"",[69,1365,87],{"class":75},[69,1367,1368],{"class":71,"line":850},[69,1369,1370],{"class":75},"      {{ item }}\n",[69,1372,1373,1376,1378,1380,1382,1385,1388,1390],{"class":71,"line":871},[69,1374,1375],{"class":75},"      \u003C",[69,1377,283],{"class":79},[69,1379,286],{"class":83},[69,1381,289],{"class":75},[69,1383,1384],{"class":103},"\"removeItem(index)\"",[69,1386,1387],{"class":75},">삭제\u003C\u002F",[69,1389,283],{"class":79},[69,1391,87],{"class":75},[69,1393,1394,1397,1399],{"class":71,"line":892},[69,1395,1396],{"class":75},"    \u003C\u002F",[69,1398,30],{"class":79},[69,1400,87],{"class":75},[69,1402,1403,1406,1408],{"class":71,"line":913},[69,1404,1405],{"class":75},"  \u003C\u002F",[69,1407,27],{"class":79},[69,1409,87],{"class":75},[69,1411,1413,1415,1417,1419,1421,1424,1426,1428],{"class":71,"line":1412},34,[69,1414,252],{"class":75},[69,1416,283],{"class":79},[69,1418,286],{"class":83},[69,1420,289],{"class":75},[69,1422,1423],{"class":103},"\"addItem\"",[69,1425,885],{"class":75},[69,1427,283],{"class":79},[69,1429,87],{"class":75},[69,1431,1433,1435,1437],{"class":71,"line":1432},35,[69,1434,227],{"class":75},[69,1436,244],{"class":79},[69,1438,87],{"class":75},[1007,1440,1442,1444],{"id":1441},"reactive의-주의점",[55,1443,376],{},"의 주의점",[15,1446,1447],{},"반응형으로 만든 객체를 통째로 다른 객체로 교체하면 기존 반응성 연결이 끊긴다.",[60,1449,1451],{"className":934,"code":1450,"language":936,"meta":65,"style":65},"let state = reactive({ count: 0 })\n\n\u002F\u002F 잘못된 사용: 반응형 객체 자체를 교체함\nstate = { count: 5 }\n\n\u002F\u002F 올바른 사용: 기존 객체의 속성을 변경함\nstate.count = 5\n",[55,1452,1453,1473,1477,1482,1498,1502,1507],{"__ignoreMap":65},[69,1454,1455,1458,1461,1463,1465,1468,1470],{"class":71,"line":72},[69,1456,1457],{"class":93},"let",[69,1459,1460],{"class":75}," state ",[69,1462,289],{"class":93},[69,1464,1098],{"class":83},[69,1466,1467],{"class":75},"({ count: ",[69,1469,133],{"class":120},[69,1471,1472],{"class":75}," })\n",[69,1474,1475],{"class":71,"line":90},[69,1476,111],{"emptyLinePlaceholder":110},[69,1478,1479],{"class":71,"line":107},[69,1480,1481],{"class":963},"\u002F\u002F 잘못된 사용: 반응형 객체 자체를 교체함\n",[69,1483,1484,1487,1489,1492,1495],{"class":71,"line":114},[69,1485,1486],{"class":75},"state ",[69,1488,289],{"class":93},[69,1490,1491],{"class":75}," { count: ",[69,1493,1494],{"class":120},"5",[69,1496,1497],{"class":75}," }\n",[69,1499,1500],{"class":71,"line":139},[69,1501,111],{"emptyLinePlaceholder":110},[69,1503,1504],{"class":71,"line":144},[69,1505,1506],{"class":963},"\u002F\u002F 올바른 사용: 기존 객체의 속성을 변경함\n",[69,1508,1509,1512,1514],{"class":71,"line":166},[69,1510,1511],{"class":75},"state.count ",[69,1513,289],{"class":93},[69,1515,1516],{"class":120}," 5\n",[15,1518,1519],{},"헷갈리면",[15,1521,1522],{},"그냥 ref로 선언하자 (교수님 왈 그냥 ref로 선언한다고 한다. )",[1007,1524,1526,1528,1529,1531],{"id":1525},"ref와-reactive-비교",[55,1527,372],{},"와 ",[55,1530,376],{}," 비교",[341,1533,1534,1549],{},[344,1535,1536],{},[347,1537,1538,1541,1545],{},[350,1539,1540],{},"구분",[350,1542,1543],{},[55,1544,372],{},[350,1546,1547],{},[55,1548,376],{},[360,1550,1551,1562,1575,1589],{},[347,1552,1553,1556,1559],{},[365,1554,1555],{},"대상",[365,1557,1558],{},"원시 값, 객체, 배열",[365,1560,1561],{},"객체, 배열, Map, Set",[347,1563,1564,1567,1572],{},[365,1565,1566],{},"JavaScript 접근",[365,1568,1569,1571],{},[55,1570,927],{}," 필요",[365,1573,1574],{},"속성에 바로 접근",[347,1576,1577,1580,1586],{},[365,1578,1579],{},"객체 교체",[365,1581,1582,1585],{},[55,1583,1584],{},".value = 새 객체","로 가능",[365,1587,1588],{},"연결이 끊길 수 있음",[347,1590,1591,1594,1597],{},[365,1592,1593],{},"사용 예",[365,1595,1596],{},"단일 값, 교체 가능한 상태",[365,1598,1599],{},"여러 속성을 묶은 객체",[1007,1601,1603],{"id":1602},"반응성-연결을-유지하는-보조-함수","반응성 연결을 유지하는 보조 함수",[15,1605,1606],{},"이 내용은 몰랐는데 G-선생이 알려줬다.",[15,1608,1609,1611,1612,1614,1615,1617],{},[55,1610,376],{}," 객체를 구조 분해하면 반응성 연결이 끊길 수 있다. 이때 ",[55,1613,443],{},"나 ",[55,1616,446],{},"를 사용하면 원본 객체와 연결된 ref를 만들 수 있다.",[60,1619,1621],{"className":934,"code":1620,"language":936,"meta":65,"style":65},"import { reactive, toRefs } from 'vue'\n\nconst state = reactive({\n  name: '홍길동',\n  age: 20\n})\n\nconst { name, age } = toRefs(state)\n\nage.value++\n\u002F\u002F state.age도 함께 변경됨\n",[55,1622,1623,1634,1638,1651,1659,1666,1670,1674,1700,1704,1711],{"__ignoreMap":65},[69,1624,1625,1627,1630,1632],{"class":71,"line":72},[69,1626,94],{"class":93},[69,1628,1629],{"class":75}," { reactive, toRefs } ",[69,1631,100],{"class":93},[69,1633,104],{"class":103},[69,1635,1636],{"class":71,"line":90},[69,1637,111],{"emptyLinePlaceholder":110},[69,1639,1640,1642,1645,1647,1649],{"class":71,"line":107},[69,1641,117],{"class":93},[69,1643,1644],{"class":120}," state",[69,1646,124],{"class":93},[69,1648,1098],{"class":83},[69,1650,631],{"class":75},[69,1652,1653,1655,1657],{"class":71,"line":114},[69,1654,636],{"class":75},[69,1656,572],{"class":103},[69,1658,642],{"class":75},[69,1660,1661,1663],{"class":71,"line":139},[69,1662,647],{"class":75},[69,1664,1665],{"class":120},"20\n",[69,1667,1668],{"class":71,"line":144},[69,1669,184],{"class":75},[69,1671,1672],{"class":71,"line":166},[69,1673,111],{"emptyLinePlaceholder":110},[69,1675,1676,1678,1681,1684,1686,1689,1692,1694,1697],{"class":71,"line":181},[69,1677,117],{"class":93},[69,1679,1680],{"class":75}," { ",[69,1682,1683],{"class":120},"name",[69,1685,373],{"class":75},[69,1687,1688],{"class":120},"age",[69,1690,1691],{"class":75}," } ",[69,1693,289],{"class":93},[69,1695,1696],{"class":83}," toRefs",[69,1698,1699],{"class":75},"(state)\n",[69,1701,1702],{"class":71,"line":187},[69,1703,111],{"emptyLinePlaceholder":110},[69,1705,1706,1709],{"class":71,"line":192},[69,1707,1708],{"class":75},"age.value",[69,1710,215],{"class":93},[69,1712,1713],{"class":71,"line":209},[69,1714,1715],{"class":963},"\u002F\u002F state.age도 함께 변경됨\n",[15,1717,1718],{},"그 밖의 반응성 보조 함수는 필요할 때 찾아서 사용하면 된다.",[341,1720,1721,1730],{},[344,1722,1723],{},[347,1724,1725,1728],{},[350,1726,1727],{},"함수",[350,1729,358],{},[360,1731,1732,1742,1752,1762,1774,1784,1794],{},[347,1733,1734,1739],{},[365,1735,1736],{},[55,1737,1738],{},"readonly",[365,1740,1741],{},"반응형 값을 읽기 전용으로 만든다.",[347,1743,1744,1749],{},[365,1745,1746],{},[55,1747,1748],{},"shallowRef",[365,1750,1751],{},"최상위 ref 값의 변경만 추적한다.",[347,1753,1754,1759],{},[365,1755,1756],{},[55,1757,1758],{},"shallowReactive",[365,1760,1761],{},"객체의 최상위 속성만 반응형으로 만든다.",[347,1763,1764,1768],{},[365,1765,1766],{},[55,1767,449],{},[365,1769,1770,1771,1773],{},"ref라면 ",[55,1772,927],{},", 일반 값이면 원래 값을 반환한다.",[347,1775,1776,1781],{},[365,1777,1778],{},[55,1779,1780],{},"toRaw",[365,1782,1783],{},"반응형 프록시의 원본 객체를 가져온다.",[347,1785,1786,1791],{},[365,1787,1788],{},[55,1789,1790],{},"markRaw",[365,1792,1793],{},"특정 객체가 반응형으로 변환되지 않게 한다.",[347,1795,1796,1807],{},[365,1797,1798,373,1801,373,1804],{},[55,1799,1800],{},"isRef",[55,1802,1803],{},"isReactive",[55,1805,1806],{},"isReadonly",[365,1808,1809],{},"반응형 여부를 확인한다.",[15,1811,1812],{},[334,1813,1814],{},"난 그냥 ref로 통일할래",[10,1816,1818,1821],{"id":1817},"computed로-계산된-값-만들기",[55,1819,1820],{},"computed()","로 계산된 값 만들기",[15,1823,1824,1826],{},[55,1825,1820],{},"는 기존 반응형 상태를 바탕으로 새로운 값을 계산한다.",[15,1828,1829],{},"계산에 사용한 의존성이 변경될 때 다시 계산하고,",[15,1831,1832],{},"같은 값이 필요할 때는 이전 결과를 재사용한다.",[15,1834,1835],{},"(이말은 계산에 사용된 반응형 의존성이 바뀌지 않았으면 그냥 캐시에서 데이터를 불러온다는 뜻이다.)",[60,1837,1839],{"className":62,"code":1838,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { computed, ref } from 'vue'\n\nconst count = ref(0)\nconst dummy = ref(0)\n\nconst doubleCount = computed(() => {\n  return count.value * 2\n})\n\nconst increaseCount = () => {\n  count.value++\n}\n\nconst increaseDummy = () => {\n  dummy.value++\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>count: {{ count }}\u003C\u002Fp>\n  \u003Cp>dummy: {{ dummy }}\u003C\u002Fp>\n  \u003Cp>두 배 값: {{ doubleCount }}\u003C\u002Fp>\n\n  \u003Cbutton @click=\"increaseCount\">count 증가\u003C\u002Fbutton>\n  \u003Cbutton @click=\"increaseDummy\">dummy 증가\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[55,1840,1841,1851,1861,1865,1881,1898,1902,1918,1928,1932,1936,1951,1957,1961,1965,1980,1987,1991,1999,2003,2011,2024,2037,2049,2053,2073,2093],{"__ignoreMap":65},[69,1842,1843,1845,1847,1849],{"class":71,"line":72},[69,1844,76],{"class":75},[69,1846,80],{"class":79},[69,1848,84],{"class":83},[69,1850,87],{"class":75},[69,1852,1853,1855,1857,1859],{"class":71,"line":90},[69,1854,94],{"class":93},[69,1856,97],{"class":75},[69,1858,100],{"class":93},[69,1860,104],{"class":103},[69,1862,1863],{"class":71,"line":107},[69,1864,111],{"emptyLinePlaceholder":110},[69,1866,1867,1869,1871,1873,1875,1877,1879],{"class":71,"line":114},[69,1868,117],{"class":93},[69,1870,121],{"class":120},[69,1872,124],{"class":93},[69,1874,127],{"class":83},[69,1876,130],{"class":75},[69,1878,133],{"class":120},[69,1880,136],{"class":75},[69,1882,1883,1885,1888,1890,1892,1894,1896],{"class":71,"line":139},[69,1884,117],{"class":93},[69,1886,1887],{"class":120}," dummy",[69,1889,124],{"class":93},[69,1891,127],{"class":83},[69,1893,130],{"class":75},[69,1895,133],{"class":120},[69,1897,136],{"class":75},[69,1899,1900],{"class":71,"line":144},[69,1901,111],{"emptyLinePlaceholder":110},[69,1903,1904,1906,1908,1910,1912,1914,1916],{"class":71,"line":166},[69,1905,117],{"class":93},[69,1907,149],{"class":120},[69,1909,124],{"class":93},[69,1911,154],{"class":83},[69,1913,157],{"class":75},[69,1915,160],{"class":93},[69,1917,163],{"class":75},[69,1919,1920,1922,1924,1926],{"class":71,"line":181},[69,1921,169],{"class":93},[69,1923,172],{"class":75},[69,1925,175],{"class":93},[69,1927,178],{"class":120},[69,1929,1930],{"class":71,"line":187},[69,1931,184],{"class":75},[69,1933,1934],{"class":71,"line":192},[69,1935,111],{"emptyLinePlaceholder":110},[69,1937,1938,1940,1943,1945,1947,1949],{"class":71,"line":209},[69,1939,117],{"class":93},[69,1941,1942],{"class":83}," increaseCount",[69,1944,124],{"class":93},[69,1946,202],{"class":75},[69,1948,160],{"class":93},[69,1950,163],{"class":75},[69,1952,1953,1955],{"class":71,"line":218},[69,1954,212],{"class":75},[69,1956,215],{"class":93},[69,1958,1959],{"class":71,"line":224},[69,1960,221],{"class":75},[69,1962,1963],{"class":71,"line":234},[69,1964,111],{"emptyLinePlaceholder":110},[69,1966,1967,1969,1972,1974,1976,1978],{"class":71,"line":239},[69,1968,117],{"class":93},[69,1970,1971],{"class":83}," increaseDummy",[69,1973,124],{"class":93},[69,1975,202],{"class":75},[69,1977,160],{"class":93},[69,1979,163],{"class":75},[69,1981,1982,1985],{"class":71,"line":249},[69,1983,1984],{"class":75},"  dummy.value",[69,1986,215],{"class":93},[69,1988,1989],{"class":71,"line":264},[69,1990,221],{"class":75},[69,1992,1993,1995,1997],{"class":71,"line":278},[69,1994,227],{"class":75},[69,1996,80],{"class":79},[69,1998,87],{"class":75},[69,2000,2001],{"class":71,"line":302},[69,2002,111],{"emptyLinePlaceholder":110},[69,2004,2005,2007,2009],{"class":71,"line":718},[69,2006,76],{"class":75},[69,2008,244],{"class":79},[69,2010,87],{"class":75},[69,2012,2013,2015,2017,2020,2022],{"class":71,"line":727},[69,2014,252],{"class":75},[69,2016,15],{"class":79},[69,2018,2019],{"class":75},">count: {{ count }}\u003C\u002F",[69,2021,15],{"class":79},[69,2023,87],{"class":75},[69,2025,2026,2028,2030,2033,2035],{"class":71,"line":732},[69,2027,252],{"class":75},[69,2029,15],{"class":79},[69,2031,2032],{"class":75},">dummy: {{ dummy }}\u003C\u002F",[69,2034,15],{"class":79},[69,2036,87],{"class":75},[69,2038,2039,2041,2043,2045,2047],{"class":71,"line":741},[69,2040,252],{"class":75},[69,2042,15],{"class":79},[69,2044,271],{"class":75},[69,2046,15],{"class":79},[69,2048,87],{"class":75},[69,2050,2051],{"class":71,"line":755},[69,2052,111],{"emptyLinePlaceholder":110},[69,2054,2055,2057,2059,2061,2063,2066,2069,2071],{"class":71,"line":783},[69,2056,252],{"class":75},[69,2058,283],{"class":79},[69,2060,286],{"class":83},[69,2062,289],{"class":75},[69,2064,2065],{"class":103},"\"increaseCount\"",[69,2067,2068],{"class":75},">count 증가\u003C\u002F",[69,2070,283],{"class":79},[69,2072,87],{"class":75},[69,2074,2075,2077,2079,2081,2083,2086,2089,2091],{"class":71,"line":797},[69,2076,252],{"class":75},[69,2078,283],{"class":79},[69,2080,286],{"class":83},[69,2082,289],{"class":75},[69,2084,2085],{"class":103},"\"increaseDummy\"",[69,2087,2088],{"class":75},">dummy 증가\u003C\u002F",[69,2090,283],{"class":79},[69,2092,87],{"class":75},[69,2094,2095,2097,2099],{"class":71,"line":811},[69,2096,227],{"class":75},[69,2098,244],{"class":79},[69,2100,87],{"class":75},[15,2102,2103,2106,2107,2109,2110,2112],{},[55,2104,2105],{},"dummy","가 변경되어 컴포넌트가 다시 렌더링되어도 ",[55,2108,318],{},"의 계산에 ",[55,2111,2105],{},"가 사용되지 않았다면 다시 계산할 필요가 없다.",[15,2114,2115,2116,2118,2119,2122],{},"이것이 일반 함수와 ",[55,2117,389],{},"의 중요한 차이다. (",[334,2120,2121],{},"위에서 말했다",")",[60,2124,2126],{"className":934,"code":2125,"language":936,"meta":65,"style":65},"const totalPrice = computed(() => {\n  return products.value.reduce((sum, product) => {\n    return sum + product.price\n  }, 0)\n})\n",[55,2127,2128,2145,2172,2186,2195],{"__ignoreMap":65},[69,2129,2130,2132,2135,2137,2139,2141,2143],{"class":71,"line":72},[69,2131,117],{"class":93},[69,2133,2134],{"class":120}," totalPrice",[69,2136,124],{"class":93},[69,2138,154],{"class":83},[69,2140,157],{"class":75},[69,2142,160],{"class":93},[69,2144,163],{"class":75},[69,2146,2147,2149,2152,2155,2158,2161,2163,2166,2168,2170],{"class":71,"line":90},[69,2148,169],{"class":93},[69,2150,2151],{"class":75}," products.value.",[69,2153,2154],{"class":83},"reduce",[69,2156,2157],{"class":75},"((",[69,2159,2160],{"class":1248},"sum",[69,2162,373],{"class":75},[69,2164,2165],{"class":1248},"product",[69,2167,1252],{"class":75},[69,2169,160],{"class":93},[69,2171,163],{"class":75},[69,2173,2174,2177,2180,2183],{"class":71,"line":107},[69,2175,2176],{"class":93},"    return",[69,2178,2179],{"class":75}," sum ",[69,2181,2182],{"class":93},"+",[69,2184,2185],{"class":75}," product.price\n",[69,2187,2188,2191,2193],{"class":71,"line":114},[69,2189,2190],{"class":75},"  }, ",[69,2192,133],{"class":120},[69,2194,136],{"class":75},[69,2196,2197],{"class":71,"line":139},[69,2198,184],{"class":75},[15,2200,2201,2203],{},[55,2202,389],{},"는 계산된 값이므로 템플릿에서 함수처럼 호출하지 않는다.",[60,2205,2207],{"className":62,"code":2206,"language":64,"meta":65,"style":65},"\u003C!-- 올바른 사용 -->\n\u003Cp>총가격: {{ totalPrice }}원\u003C\u002Fp>\n\n\u003C!-- 잘못된 사용 -->\n\u003Cbutton @click=\"totalPrice(products)\">총가격\u003C\u002Fbutton>\n",[55,2208,2209,2214,2227,2231,2236],{"__ignoreMap":65},[69,2210,2211],{"class":71,"line":72},[69,2212,2213],{"class":963},"\u003C!-- 올바른 사용 -->\n",[69,2215,2216,2218,2220,2223,2225],{"class":71,"line":90},[69,2217,76],{"class":75},[69,2219,15],{"class":79},[69,2221,2222],{"class":75},">총가격: {{ totalPrice }}원\u003C\u002F",[69,2224,15],{"class":79},[69,2226,87],{"class":75},[69,2228,2229],{"class":71,"line":107},[69,2230,111],{"emptyLinePlaceholder":110},[69,2232,2233],{"class":71,"line":114},[69,2234,2235],{"class":963},"\u003C!-- 잘못된 사용 -->\n",[69,2237,2238,2240,2242,2245,2248,2250,2253,2256,2259,2261,2264,2266],{"class":71,"line":139},[69,2239,76],{"class":75},[69,2241,283],{"class":79},[69,2243,2244],{"class":75}," @",[69,2246,2247],{"class":83},"click",[69,2249,289],{"class":75},[69,2251,2252],{"class":103},"\"",[69,2254,2255],{"class":83},"totalPrice",[69,2257,2258],{"class":75},"(products)",[69,2260,2252],{"class":103},[69,2262,2263],{"class":75},">총가격\u003C\u002F",[69,2265,283],{"class":79},[69,2267,87],{"class":75},[1007,2269,2271,2272,2274],{"id":2270},"일반-함수와-computed-구분","일반 함수와 ",[55,2273,389],{}," 구분",[60,2276,2278],{"className":934,"code":2277,"language":936,"meta":65,"style":65},"\u002F\u002F 사용자 행동을 처리하는 일반 함수\nconst save = () => {\n  alert('저장되었습니다.')\n}\n\n\u002F\u002F 상태에서 값을 계산하는 computed\nconst completedCount = computed(() => {\n  return todos.value.filter((todo) => todo.done).length\n})\n",[55,2279,2280,2285,2300,2312,2316,2320,2325,2342,2367],{"__ignoreMap":65},[69,2281,2282],{"class":71,"line":72},[69,2283,2284],{"class":963},"\u002F\u002F 사용자 행동을 처리하는 일반 함수\n",[69,2286,2287,2289,2292,2294,2296,2298],{"class":71,"line":90},[69,2288,117],{"class":93},[69,2290,2291],{"class":83}," save",[69,2293,124],{"class":93},[69,2295,202],{"class":75},[69,2297,160],{"class":93},[69,2299,163],{"class":75},[69,2301,2302,2305,2307,2310],{"class":71,"line":107},[69,2303,2304],{"class":83},"  alert",[69,2306,130],{"class":75},[69,2308,2309],{"class":103},"'저장되었습니다.'",[69,2311,136],{"class":75},[69,2313,2314],{"class":71,"line":114},[69,2315,221],{"class":75},[69,2317,2318],{"class":71,"line":139},[69,2319,111],{"emptyLinePlaceholder":110},[69,2321,2322],{"class":71,"line":144},[69,2323,2324],{"class":963},"\u002F\u002F 상태에서 값을 계산하는 computed\n",[69,2326,2327,2329,2332,2334,2336,2338,2340],{"class":71,"line":166},[69,2328,117],{"class":93},[69,2330,2331],{"class":120}," completedCount",[69,2333,124],{"class":93},[69,2335,154],{"class":83},[69,2337,157],{"class":75},[69,2339,160],{"class":93},[69,2341,163],{"class":75},[69,2343,2344,2346,2349,2352,2354,2357,2359,2361,2364],{"class":71,"line":181},[69,2345,169],{"class":93},[69,2347,2348],{"class":75}," todos.value.",[69,2350,2351],{"class":83},"filter",[69,2353,2157],{"class":75},[69,2355,2356],{"class":1248},"todo",[69,2358,1252],{"class":75},[69,2360,160],{"class":93},[69,2362,2363],{"class":75}," todo.done).",[69,2365,2366],{"class":120},"length\n",[69,2368,2369],{"class":71,"line":187},[69,2370,184],{"class":75},[60,2372,2377],{"className":2373,"code":2375,"language":2376,"meta":65},[2374],"language-text","사용자가 행동한다 → 일반 함수\n상태에서 값이 나온다 → computed\n","text",[55,2378,2375],{"__ignoreMap":65},[10,2380,2382,2385],{"id":2381},"watch로-변경-후-작업-실행하기",[55,2383,2384],{},"watch()","로 변경 후 작업 실행하기",[15,2387,2388,2390],{},[55,2389,2384],{},"는 특정 반응형 값의 변경을 감시하고, 값이 바뀐 뒤 후속 작업을 실행한다.",[15,2392,2393],{},"여러 가지 상황에서 사용된다.",[27,2395,2396,2399,2402,2405,2408],{},[30,2397,2398],{},"서버 요청",[30,2400,2401],{},"로컬 스토리지 저장",[30,2403,2404],{},"로그 기록",[30,2406,2407],{},"다른 상태 변경",[30,2409,2410],{},"외부 라이브러리와 동기화",[60,2412,2414],{"className":62,"code":2413,"language":64,"meta":65,"style":65},"watch(\n  감시할 데이터,\n  데이터가 변경되었을 때 실행할 함수,\n  추가 옵션\n)\n",[55,2415,2416,2421,2426,2431,2436],{"__ignoreMap":65},[69,2417,2418],{"class":71,"line":72},[69,2419,2420],{"class":75},"watch(\n",[69,2422,2423],{"class":71,"line":90},[69,2424,2425],{"class":75},"  감시할 데이터,\n",[69,2427,2428],{"class":71,"line":107},[69,2429,2430],{"class":75},"  데이터가 변경되었을 때 실행할 함수,\n",[69,2432,2433],{"class":71,"line":114},[69,2434,2435],{"class":75},"  추가 옵션\n",[69,2437,2438],{"class":71,"line":139},[69,2439,136],{"class":75},[60,2441,2443],{"className":62,"code":2442,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { ref, watch } from 'vue'\n\nconst currentCity = ref('서울')\nconst logMessage = ref('아직 감시 시스템이 작동하지 않았습니다.')\n\nwatch(currentCity, (newValue, oldValue) => {\n  logMessage.value = `${oldValue}에서 ${newValue}(으)로 변경됨.`\n  console.log(`${newValue}의 날씨 API를 다시 조회합니다.`)\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>현재 도시: {{ currentCity }}\u003C\u002Fp>\n  \u003Cbutton @click=\"currentCity = '서울'\">서울\u003C\u002Fbutton>\n  \u003Cbutton @click=\"currentCity = '수원'\">수원\u003C\u002Fbutton>\n  \u003Cbutton @click=\"currentCity = '부산'\">부산\u003C\u002Fbutton>\n  \u003Cp>{{ logMessage }}\u003C\u002Fp>\n\u003C\u002Ftemplate>\n",[55,2444,2445,2455,2466,2470,2488,2506,2510,2531,2551,2570,2574,2582,2586,2594,2607,2627,2647,2667,2680],{"__ignoreMap":65},[69,2446,2447,2449,2451,2453],{"class":71,"line":72},[69,2448,76],{"class":75},[69,2450,80],{"class":79},[69,2452,84],{"class":83},[69,2454,87],{"class":75},[69,2456,2457,2459,2462,2464],{"class":71,"line":90},[69,2458,94],{"class":93},[69,2460,2461],{"class":75}," { ref, watch } ",[69,2463,100],{"class":93},[69,2465,104],{"class":103},[69,2467,2468],{"class":71,"line":107},[69,2469,111],{"emptyLinePlaceholder":110},[69,2471,2472,2474,2477,2479,2481,2483,2486],{"class":71,"line":114},[69,2473,117],{"class":93},[69,2475,2476],{"class":120}," currentCity",[69,2478,124],{"class":93},[69,2480,127],{"class":83},[69,2482,130],{"class":75},[69,2484,2485],{"class":103},"'서울'",[69,2487,136],{"class":75},[69,2489,2490,2492,2495,2497,2499,2501,2504],{"class":71,"line":139},[69,2491,117],{"class":93},[69,2493,2494],{"class":120}," logMessage",[69,2496,124],{"class":93},[69,2498,127],{"class":83},[69,2500,130],{"class":75},[69,2502,2503],{"class":103},"'아직 감시 시스템이 작동하지 않았습니다.'",[69,2505,136],{"class":75},[69,2507,2508],{"class":71,"line":144},[69,2509,111],{"emptyLinePlaceholder":110},[69,2511,2512,2514,2517,2520,2522,2525,2527,2529],{"class":71,"line":166},[69,2513,392],{"class":83},[69,2515,2516],{"class":75},"(currentCity, (",[69,2518,2519],{"class":1248},"newValue",[69,2521,373],{"class":75},[69,2523,2524],{"class":1248},"oldValue",[69,2526,1252],{"class":75},[69,2528,160],{"class":93},[69,2530,163],{"class":75},[69,2532,2533,2536,2538,2541,2543,2546,2548],{"class":71,"line":181},[69,2534,2535],{"class":75},"  logMessage.value ",[69,2537,289],{"class":93},[69,2539,2540],{"class":103}," `${",[69,2542,2524],{"class":75},[69,2544,2545],{"class":103},"}에서 ${",[69,2547,2519],{"class":75},[69,2549,2550],{"class":103},"}(으)로 변경됨.`\n",[69,2552,2553,2556,2558,2560,2563,2565,2568],{"class":71,"line":187},[69,2554,2555],{"class":75},"  console.",[69,2557,979],{"class":83},[69,2559,130],{"class":75},[69,2561,2562],{"class":103},"`${",[69,2564,2519],{"class":75},[69,2566,2567],{"class":103},"}의 날씨 API를 다시 조회합니다.`",[69,2569,136],{"class":75},[69,2571,2572],{"class":71,"line":192},[69,2573,184],{"class":75},[69,2575,2576,2578,2580],{"class":71,"line":209},[69,2577,227],{"class":75},[69,2579,80],{"class":79},[69,2581,87],{"class":75},[69,2583,2584],{"class":71,"line":218},[69,2585,111],{"emptyLinePlaceholder":110},[69,2587,2588,2590,2592],{"class":71,"line":224},[69,2589,76],{"class":75},[69,2591,244],{"class":79},[69,2593,87],{"class":75},[69,2595,2596,2598,2600,2603,2605],{"class":71,"line":234},[69,2597,252],{"class":75},[69,2599,15],{"class":79},[69,2601,2602],{"class":75},">현재 도시: {{ currentCity }}\u003C\u002F",[69,2604,15],{"class":79},[69,2606,87],{"class":75},[69,2608,2609,2611,2613,2615,2617,2620,2623,2625],{"class":71,"line":239},[69,2610,252],{"class":75},[69,2612,283],{"class":79},[69,2614,286],{"class":83},[69,2616,289],{"class":75},[69,2618,2619],{"class":103},"\"currentCity = '서울'\"",[69,2621,2622],{"class":75},">서울\u003C\u002F",[69,2624,283],{"class":79},[69,2626,87],{"class":75},[69,2628,2629,2631,2633,2635,2637,2640,2643,2645],{"class":71,"line":249},[69,2630,252],{"class":75},[69,2632,283],{"class":79},[69,2634,286],{"class":83},[69,2636,289],{"class":75},[69,2638,2639],{"class":103},"\"currentCity = '수원'\"",[69,2641,2642],{"class":75},">수원\u003C\u002F",[69,2644,283],{"class":79},[69,2646,87],{"class":75},[69,2648,2649,2651,2653,2655,2657,2660,2663,2665],{"class":71,"line":264},[69,2650,252],{"class":75},[69,2652,283],{"class":79},[69,2654,286],{"class":83},[69,2656,289],{"class":75},[69,2658,2659],{"class":103},"\"currentCity = '부산'\"",[69,2661,2662],{"class":75},">부산\u003C\u002F",[69,2664,283],{"class":79},[69,2666,87],{"class":75},[69,2668,2669,2671,2673,2676,2678],{"class":71,"line":278},[69,2670,252],{"class":75},[69,2672,15],{"class":79},[69,2674,2675],{"class":75},">{{ logMessage }}\u003C\u002F",[69,2677,15],{"class":79},[69,2679,87],{"class":75},[69,2681,2682,2684,2686],{"class":71,"line":302},[69,2683,227],{"class":75},[69,2685,244],{"class":79},[69,2687,87],{"class":75},[15,2689,2690],{},"콜백에는 새 값과 이전 값이 순서대로 전달된다.",[1007,2692,2694],{"id":2693},"여러-값-감시하기","여러 값 감시하기",[15,2696,2697],{},"첫 번째 인자에 감시할 ref를 배열로 전달하면 여러 값을 한 번에 감시할 수 있다.",[60,2699,2701],{"className":934,"code":2700,"language":936,"meta":65,"style":65},"import { ref, watch } from 'vue'\n\nconst city = ref('서울')\nconst dateType = ref('오늘')\nconst apiStatus = ref('대기 중...')\n\nwatch(\n  [city, dateType],\n  ([newCity, newDate], [oldCity, oldDate]) => {\n    apiStatus.value = `${oldCity}(${oldDate}) → ${newCity}(${newDate})`\n  }\n)\n",[55,2702,2703,2713,2717,2734,2752,2770,2774,2781,2786,2817,2845,2850],{"__ignoreMap":65},[69,2704,2705,2707,2709,2711],{"class":71,"line":72},[69,2706,94],{"class":93},[69,2708,2461],{"class":75},[69,2710,100],{"class":93},[69,2712,104],{"class":103},[69,2714,2715],{"class":71,"line":90},[69,2716,111],{"emptyLinePlaceholder":110},[69,2718,2719,2721,2724,2726,2728,2730,2732],{"class":71,"line":107},[69,2720,117],{"class":93},[69,2722,2723],{"class":120}," city",[69,2725,124],{"class":93},[69,2727,127],{"class":83},[69,2729,130],{"class":75},[69,2731,2485],{"class":103},[69,2733,136],{"class":75},[69,2735,2736,2738,2741,2743,2745,2747,2750],{"class":71,"line":114},[69,2737,117],{"class":93},[69,2739,2740],{"class":120}," dateType",[69,2742,124],{"class":93},[69,2744,127],{"class":83},[69,2746,130],{"class":75},[69,2748,2749],{"class":103},"'오늘'",[69,2751,136],{"class":75},[69,2753,2754,2756,2759,2761,2763,2765,2768],{"class":71,"line":139},[69,2755,117],{"class":93},[69,2757,2758],{"class":120}," apiStatus",[69,2760,124],{"class":93},[69,2762,127],{"class":83},[69,2764,130],{"class":75},[69,2766,2767],{"class":103},"'대기 중...'",[69,2769,136],{"class":75},[69,2771,2772],{"class":71,"line":144},[69,2773,111],{"emptyLinePlaceholder":110},[69,2775,2776,2778],{"class":71,"line":166},[69,2777,392],{"class":83},[69,2779,2780],{"class":75},"(\n",[69,2782,2783],{"class":71,"line":181},[69,2784,2785],{"class":75},"  [city, dateType],\n",[69,2787,2788,2791,2794,2796,2799,2802,2805,2807,2810,2813,2815],{"class":71,"line":187},[69,2789,2790],{"class":75},"  ([",[69,2792,2793],{"class":1248},"newCity",[69,2795,373],{"class":75},[69,2797,2798],{"class":1248},"newDate",[69,2800,2801],{"class":75},"], [",[69,2803,2804],{"class":1248},"oldCity",[69,2806,373],{"class":75},[69,2808,2809],{"class":1248},"oldDate",[69,2811,2812],{"class":75},"]) ",[69,2814,160],{"class":93},[69,2816,163],{"class":75},[69,2818,2819,2822,2824,2826,2828,2831,2833,2836,2838,2840,2842],{"class":71,"line":192},[69,2820,2821],{"class":75},"    apiStatus.value ",[69,2823,289],{"class":93},[69,2825,2540],{"class":103},[69,2827,2804],{"class":75},[69,2829,2830],{"class":103},"}(${",[69,2832,2809],{"class":75},[69,2834,2835],{"class":103},"}) → ${",[69,2837,2793],{"class":75},[69,2839,2830],{"class":103},[69,2841,2798],{"class":75},[69,2843,2844],{"class":103},"})`\n",[69,2846,2847],{"class":71,"line":209},[69,2848,2849],{"class":75},"  }\n",[69,2851,2852],{"class":71,"line":218},[69,2853,136],{"class":75},[15,2855,2856],{},"감시 대상 배열의 순서와 콜백의 새 값·이전 값 배열 순서는 서로 대응한다.",[1007,2858,2860],{"id":2859},"객체와-배열-깊이-감시하기","객체와 배열 깊이 감시하기",[15,2862,2863],{},[334,2864,2865],{},"여기는 나도 좀 헷갈린다",[15,2867,2868,2869,2872],{},"객체나 배열 내부의 값이 바뀌는 것까지 감시하려면 ",[55,2870,2871],{},"deep: true","를 사용할 수 있다.",[60,2874,2876],{"className":934,"code":2875,"language":936,"meta":65,"style":65},"import { ref, watch } from 'vue'\n\nconst user = ref({\n  name: '홍길동',\n  age: 20\n})\n\nwatch(\n  user,\n  (newUser, oldUser) => {\n    console.log('새로운 값:', newUser.age)\n    console.log('이전 값:', oldUser.age)\n  },\n  { deep: true }\n)\n\nuser.value.age = 21\n",[55,2877,2878,2888,2892,2904,2912,2918,2922,2926,2932,2937,2956,2971,2985,2990,2999,3003,3007],{"__ignoreMap":65},[69,2879,2880,2882,2884,2886],{"class":71,"line":72},[69,2881,94],{"class":93},[69,2883,2461],{"class":75},[69,2885,100],{"class":93},[69,2887,104],{"class":103},[69,2889,2890],{"class":71,"line":90},[69,2891,111],{"emptyLinePlaceholder":110},[69,2893,2894,2896,2898,2900,2902],{"class":71,"line":107},[69,2895,117],{"class":93},[69,2897,624],{"class":120},[69,2899,124],{"class":93},[69,2901,127],{"class":83},[69,2903,631],{"class":75},[69,2905,2906,2908,2910],{"class":71,"line":114},[69,2907,636],{"class":75},[69,2909,572],{"class":103},[69,2911,642],{"class":75},[69,2913,2914,2916],{"class":71,"line":139},[69,2915,647],{"class":75},[69,2917,1665],{"class":120},[69,2919,2920],{"class":71,"line":144},[69,2921,184],{"class":75},[69,2923,2924],{"class":71,"line":166},[69,2925,111],{"emptyLinePlaceholder":110},[69,2927,2928,2930],{"class":71,"line":181},[69,2929,392],{"class":83},[69,2931,2780],{"class":75},[69,2933,2934],{"class":71,"line":187},[69,2935,2936],{"class":75},"  user,\n",[69,2938,2939,2942,2945,2947,2950,2952,2954],{"class":71,"line":192},[69,2940,2941],{"class":75},"  (",[69,2943,2944],{"class":1248},"newUser",[69,2946,373],{"class":75},[69,2948,2949],{"class":1248},"oldUser",[69,2951,1252],{"class":75},[69,2953,160],{"class":93},[69,2955,163],{"class":75},[69,2957,2958,2961,2963,2965,2968],{"class":71,"line":209},[69,2959,2960],{"class":75},"    console.",[69,2962,979],{"class":83},[69,2964,130],{"class":75},[69,2966,2967],{"class":103},"'새로운 값:'",[69,2969,2970],{"class":75},", newUser.age)\n",[69,2972,2973,2975,2977,2979,2982],{"class":71,"line":218},[69,2974,2960],{"class":75},[69,2976,979],{"class":83},[69,2978,130],{"class":75},[69,2980,2981],{"class":103},"'이전 값:'",[69,2983,2984],{"class":75},", oldUser.age)\n",[69,2986,2987],{"class":71,"line":224},[69,2988,2989],{"class":75},"  },\n",[69,2991,2992,2995,2997],{"class":71,"line":234},[69,2993,2994],{"class":75},"  { deep: ",[69,2996,590],{"class":120},[69,2998,1497],{"class":75},[69,3000,3001],{"class":71,"line":239},[69,3002,136],{"class":75},[69,3004,3005],{"class":71,"line":249},[69,3006,111],{"emptyLinePlaceholder":110},[69,3008,3009,3012,3014],{"class":71,"line":264},[69,3010,3011],{"class":75},"user.value.age ",[69,3013,289],{"class":93},[69,3015,3016],{"class":120}," 21\n",[15,3018,3019],{},"출력 결과:",[60,3021,3024],{"className":3022,"code":3023,"language":2376,"meta":65},[2374],"새로운 값: 21\n이전 값: 21\n",[55,3025,3023],{"__ignoreMap":65},[15,3027,3028,3030,3031,1528,3033,3035],{},[55,3029,1688],{},"는 20에서 21로 변경되었지만, ",[55,3032,2944],{},[55,3034,2949],{},"가 같은 객체를 가리키기 때문에 둘 다 21로 출력될 수 있다.",[15,3037,3038,3039,3041],{},"즉, ",[55,3040,2871],{},"는 객체 내부의 변경을 감지하는 데는 사용할 수 있지만, 변경 전 값을 정확하게 비교하는 용도로는 적합하지 않을 수 있다.",[15,3043,3044],{},"이전 값과 새로운 값을 정확히 비교하고 싶다면 객체 전체가 아니라 필요한 속성만 감시한다.",[60,3046,3048],{"className":934,"code":3047,"language":936,"meta":65,"style":65},"watch(\n  () => user.value.age,\n  (newAge, oldAge) => {\n    console.log(`${oldAge}세에서 ${newAge}세로 변경됨.`)\n  }\n)\n\nuser.value.age = 22\n",[55,3049,3050,3056,3066,3084,3106,3110,3114,3118],{"__ignoreMap":65},[69,3051,3052,3054],{"class":71,"line":72},[69,3053,392],{"class":83},[69,3055,2780],{"class":75},[69,3057,3058,3061,3063],{"class":71,"line":90},[69,3059,3060],{"class":75},"  () ",[69,3062,160],{"class":93},[69,3064,3065],{"class":75}," user.value.age,\n",[69,3067,3068,3070,3073,3075,3078,3080,3082],{"class":71,"line":107},[69,3069,2941],{"class":75},[69,3071,3072],{"class":1248},"newAge",[69,3074,373],{"class":75},[69,3076,3077],{"class":1248},"oldAge",[69,3079,1252],{"class":75},[69,3081,160],{"class":93},[69,3083,163],{"class":75},[69,3085,3086,3088,3090,3092,3094,3096,3099,3101,3104],{"class":71,"line":114},[69,3087,2960],{"class":75},[69,3089,979],{"class":83},[69,3091,130],{"class":75},[69,3093,2562],{"class":103},[69,3095,3077],{"class":75},[69,3097,3098],{"class":103},"}세에서 ${",[69,3100,3072],{"class":75},[69,3102,3103],{"class":103},"}세로 변경됨.`",[69,3105,136],{"class":75},[69,3107,3108],{"class":71,"line":139},[69,3109,2849],{"class":75},[69,3111,3112],{"class":71,"line":144},[69,3113,136],{"class":75},[69,3115,3116],{"class":71,"line":166},[69,3117,111],{"emptyLinePlaceholder":110},[69,3119,3120,3122,3124],{"class":71,"line":181},[69,3121,3011],{"class":75},[69,3123,289],{"class":93},[69,3125,3126],{"class":120}," 22\n",[15,3128,3019],{},[60,3130,3133],{"className":3131,"code":3132,"language":2376,"meta":65},[2374],"21세에서 22세로 변경됨.\n",[55,3134,3132],{"__ignoreMap":65},[15,3136,3137],{},"정리하면 다음과 같다.",[27,3139,3140],{},[30,3141,3142,3143,3145],{},"객체 내부에서 변경이 발생했는지만 확인하려면 ",[55,3144,2871],{},"를 사용한다.\n\u002F\u002F 객체 내부에서 무엇인가 바뀌었는지만 알고 싶을 때",[60,3147,3149],{"className":62,"code":3148,"language":64,"meta":65,"style":65},"watch(\n  user,\n  () => {\n    console.log('user 내부 값이 변경됨')\n  },\n  { deep: true }\n)\n",[55,3150,3151,3155,3159,3164,3169,3173,3178],{"__ignoreMap":65},[69,3152,3153],{"class":71,"line":72},[69,3154,2420],{"class":75},[69,3156,3157],{"class":71,"line":90},[69,3158,2936],{"class":75},[69,3160,3161],{"class":71,"line":107},[69,3162,3163],{"class":75},"  () => {\n",[69,3165,3166],{"class":71,"line":114},[69,3167,3168],{"class":75},"    console.log('user 내부 값이 변경됨')\n",[69,3170,3171],{"class":71,"line":139},[69,3172,2989],{"class":75},[69,3174,3175],{"class":71,"line":144},[69,3176,3177],{"class":75},"  { deep: true }\n",[69,3179,3180],{"class":71,"line":166},[69,3181,136],{"class":75},[27,3183,3184],{},[30,3185,3186],{},"특정 값의 변경 전,후 값을 비교하려면 해당 속성만 함수로 지정해 감시한다.\u002F\u002F 특정 값의 이전 값과 새로운 값을 정확히 비교하고 싶을 때",[60,3188,3190],{"className":62,"code":3189,"language":64,"meta":65,"style":65},"watch(\n  () => user.value.age,\n  (newAge, oldAge) => {\n    console.log(oldAge, '→', newAge)\n  }\n)\n",[55,3191,3192,3196,3201,3206,3211,3215],{"__ignoreMap":65},[69,3193,3194],{"class":71,"line":72},[69,3195,2420],{"class":75},[69,3197,3198],{"class":71,"line":90},[69,3199,3200],{"class":75},"  () => user.value.age,\n",[69,3202,3203],{"class":71,"line":107},[69,3204,3205],{"class":75},"  (newAge, oldAge) => {\n",[69,3207,3208],{"class":71,"line":114},[69,3209,3210],{"class":75},"    console.log(oldAge, '→', newAge)\n",[69,3212,3213],{"class":71,"line":139},[69,3214,2849],{"class":75},[69,3216,3217],{"class":71,"line":144},[69,3218,136],{"class":75},[10,3220,3222,3225],{"id":3221},"watcheffect로-자동-감시하기",[55,3223,3224],{},"watchEffect()","로 자동 감시하기",[15,3227,3228,3230],{},[55,3229,3224],{},"는 감시할 대상을 따로 적지 않는다. 함수 내부에서 사용하는 반응형 값을 자동으로 추적하고, 값이 변경될 때 다시 실행한다.",[27,3232,3233,3236,3239,3244],{},[30,3234,3235],{},"컴포넌트가 처음 생성될 때 즉시 한 번 실행된다.",[30,3237,3238],{},"함수 내부에서 읽은 반응형 값만 추적한다.",[30,3240,3241,3243],{},[55,3242,2524],{},"를 제공하지 않는다.",[30,3245,3246,3247,1028],{},"이전 값과 비교해야 하면 ",[55,3248,392],{},[60,3250,3252],{"className":934,"code":3251,"language":936,"meta":65,"style":65},"import { ref, watchEffect } from 'vue'\n\nconst city = ref('서울')\nconst message = ref('')\n\nwatchEffect(() => {\n  message.value = `${city.value}의 날씨를 조회합니다.`\n})\n",[55,3253,3254,3265,3269,3285,3303,3307,3317,3337],{"__ignoreMap":65},[69,3255,3256,3258,3261,3263],{"class":71,"line":72},[69,3257,94],{"class":93},[69,3259,3260],{"class":75}," { ref, watchEffect } ",[69,3262,100],{"class":93},[69,3264,104],{"class":103},[69,3266,3267],{"class":71,"line":90},[69,3268,111],{"emptyLinePlaceholder":110},[69,3270,3271,3273,3275,3277,3279,3281,3283],{"class":71,"line":107},[69,3272,117],{"class":93},[69,3274,2723],{"class":120},[69,3276,124],{"class":93},[69,3278,127],{"class":83},[69,3280,130],{"class":75},[69,3282,2485],{"class":103},[69,3284,136],{"class":75},[69,3286,3287,3289,3292,3294,3296,3298,3301],{"class":71,"line":114},[69,3288,117],{"class":93},[69,3290,3291],{"class":120}," message",[69,3293,124],{"class":93},[69,3295,127],{"class":83},[69,3297,130],{"class":75},[69,3299,3300],{"class":103},"''",[69,3302,136],{"class":75},[69,3304,3305],{"class":71,"line":139},[69,3306,111],{"emptyLinePlaceholder":110},[69,3308,3309,3311,3313,3315],{"class":71,"line":144},[69,3310,395],{"class":83},[69,3312,157],{"class":75},[69,3314,160],{"class":93},[69,3316,163],{"class":75},[69,3318,3319,3322,3324,3326,3329,3331,3334],{"class":71,"line":166},[69,3320,3321],{"class":75},"  message.value ",[69,3323,289],{"class":93},[69,3325,2540],{"class":103},[69,3327,3328],{"class":75},"city",[69,3330,1211],{"class":103},[69,3332,3333],{"class":75},"value",[69,3335,3336],{"class":103},"}의 날씨를 조회합니다.`\n",[69,3338,3339],{"class":71,"line":181},[69,3340,184],{"class":75},[1007,3342,3344,373,3346,1531],{"id":3343},"watch-watcheffect-비교",[55,3345,392],{},[55,3347,395],{},[341,3349,3350,3364],{},[344,3351,3352],{},[347,3353,3354,3356,3360],{},[350,3355,1540],{},[350,3357,3358],{},[55,3359,392],{},[350,3361,3362],{},[55,3363,395],{},[360,3365,3366,3377,3388,3399],{},[347,3367,3368,3371,3374],{},[365,3369,3370],{},"감시 대상",[365,3372,3373],{},"직접 지정",[365,3375,3376],{},"함수 안에서 자동 추적",[347,3378,3379,3382,3385],{},[365,3380,3381],{},"최초 실행",[365,3383,3384],{},"기본적으로 변경 후 실행",[365,3386,3387],{},"생성 직후 즉시 실행",[347,3389,3390,3393,3396],{},[365,3391,3392],{},"이전 값",[365,3394,3395],{},"제공",[365,3397,3398],{},"제공하지 않음",[347,3400,3401,3404,3407],{},[365,3402,3403],{},"적합한 상황",[365,3405,3406],{},"특정 값의 변경 전후 비교",[365,3408,3409],{},"여러 의존성을 간단히 동기화",[15,3411,3412,3413,3416,3417,3420],{},"DOM 업데이트가 끝난 뒤 실행해야 하면 ",[55,3414,3415],{},"watchPostEffect",", 값이 바뀌는 즉시 동기적으로 실행해야 하면 ",[55,3418,3419],{},"watchSyncEffect","를 검토할 수 있다. 다만 실행 타이밍을 세밀하게 제어해야 하는 경우에만 사용하자.",[10,3422,3424],{"id":3423},"lifecycle-추가-공부가-필요하다-으으","LifeCycle (추가 공부가 필요하다 으으)",[15,3426,3427],{},"Composition API에서는 컴포넌트의 생명주기 훅도 함수 형태로 사용한다.",[60,3429,3431],{"className":62,"code":3430,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { onMounted, onUpdated, onUnmounted } from 'vue'\n\nconst handleResize = () => {\n  console.log(window.innerWidth)\n}\n\nonMounted(() => {\n  window.addEventListener('resize', handleResize)\n})\n\nonUpdated(() => {\n  console.log('화면이 업데이트됨')\n})\n\nonUnmounted(() => {\n  window.removeEventListener('resize', handleResize)\n})\n\u003C\u002Fscript>\n",[55,3432,3433,3443,3454,3458,3473,3482,3486,3490,3500,3516,3520,3524,3534,3547,3551,3555,3565,3578,3582],{"__ignoreMap":65},[69,3434,3435,3437,3439,3441],{"class":71,"line":72},[69,3436,76],{"class":75},[69,3438,80],{"class":79},[69,3440,84],{"class":83},[69,3442,87],{"class":75},[69,3444,3445,3447,3450,3452],{"class":71,"line":90},[69,3446,94],{"class":93},[69,3448,3449],{"class":75}," { onMounted, onUpdated, onUnmounted } ",[69,3451,100],{"class":93},[69,3453,104],{"class":103},[69,3455,3456],{"class":71,"line":107},[69,3457,111],{"emptyLinePlaceholder":110},[69,3459,3460,3462,3465,3467,3469,3471],{"class":71,"line":114},[69,3461,117],{"class":93},[69,3463,3464],{"class":83}," handleResize",[69,3466,124],{"class":93},[69,3468,202],{"class":75},[69,3470,160],{"class":93},[69,3472,163],{"class":75},[69,3474,3475,3477,3479],{"class":71,"line":139},[69,3476,2555],{"class":75},[69,3478,979],{"class":83},[69,3480,3481],{"class":75},"(window.innerWidth)\n",[69,3483,3484],{"class":71,"line":144},[69,3485,221],{"class":75},[69,3487,3488],{"class":71,"line":166},[69,3489,111],{"emptyLinePlaceholder":110},[69,3491,3492,3494,3496,3498],{"class":71,"line":181},[69,3493,408],{"class":83},[69,3495,157],{"class":75},[69,3497,160],{"class":93},[69,3499,163],{"class":75},[69,3501,3502,3505,3508,3510,3513],{"class":71,"line":187},[69,3503,3504],{"class":75},"  window.",[69,3506,3507],{"class":83},"addEventListener",[69,3509,130],{"class":75},[69,3511,3512],{"class":103},"'resize'",[69,3514,3515],{"class":75},", handleResize)\n",[69,3517,3518],{"class":71,"line":192},[69,3519,184],{"class":75},[69,3521,3522],{"class":71,"line":209},[69,3523,111],{"emptyLinePlaceholder":110},[69,3525,3526,3528,3530,3532],{"class":71,"line":218},[69,3527,411],{"class":83},[69,3529,157],{"class":75},[69,3531,160],{"class":93},[69,3533,163],{"class":75},[69,3535,3536,3538,3540,3542,3545],{"class":71,"line":224},[69,3537,2555],{"class":75},[69,3539,979],{"class":83},[69,3541,130],{"class":75},[69,3543,3544],{"class":103},"'화면이 업데이트됨'",[69,3546,136],{"class":75},[69,3548,3549],{"class":71,"line":234},[69,3550,184],{"class":75},[69,3552,3553],{"class":71,"line":239},[69,3554,111],{"emptyLinePlaceholder":110},[69,3556,3557,3559,3561,3563],{"class":71,"line":249},[69,3558,414],{"class":83},[69,3560,157],{"class":75},[69,3562,160],{"class":93},[69,3564,163],{"class":75},[69,3566,3567,3569,3572,3574,3576],{"class":71,"line":264},[69,3568,3504],{"class":75},[69,3570,3571],{"class":83},"removeEventListener",[69,3573,130],{"class":75},[69,3575,3512],{"class":103},[69,3577,3515],{"class":75},[69,3579,3580],{"class":71,"line":278},[69,3581,184],{"class":75},[69,3583,3584,3586,3588],{"class":71,"line":302},[69,3585,227],{"class":75},[69,3587,80],{"class":79},[69,3589,87],{"class":75},[10,3591,3593],{"id":3592},"onmounted",[55,3594,408],{},[15,3596,3597],{},"컴포넌트가 화면의 DOM에 실제로 추가된 뒤 실행된다.",[15,3599,3600],{},"주로 다음과 같은 상황에서 사용한다.",[27,3602,3603,3606,3609,3618,3621],{},[30,3604,3605],{},"API로 초기 데이터를 불러올 때",[30,3607,3608],{},"DOM 요소의 크기나 위치를 확인할 때",[30,3610,3611,373,3614,3617],{},[55,3612,3613],{},"window",[55,3615,3616],{},"document"," 이벤트를 등록할 때",[30,3619,3620],{},"외부 라이브러리나 차트를 초기화할 때",[30,3622,3623],{},"타이머를 시작할 때",[60,3625,3627],{"className":62,"code":3626,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { ref, onMounted } from 'vue'\n\nconst users = ref([])\n\nonMounted(async () => {\n  const response = await fetch('\u002Fapi\u002Fusers')\n  users.value = await response.json()\n})\n\u003C\u002Fscript>\n",[55,3628,3629,3639,3650,3654,3668,3672,3687,3710,3728,3732],{"__ignoreMap":65},[69,3630,3631,3633,3635,3637],{"class":71,"line":72},[69,3632,76],{"class":75},[69,3634,80],{"class":79},[69,3636,84],{"class":83},[69,3638,87],{"class":75},[69,3640,3641,3643,3646,3648],{"class":71,"line":90},[69,3642,94],{"class":93},[69,3644,3645],{"class":75}," { ref, onMounted } ",[69,3647,100],{"class":93},[69,3649,104],{"class":103},[69,3651,3652],{"class":71,"line":107},[69,3653,111],{"emptyLinePlaceholder":110},[69,3655,3656,3658,3661,3663,3665],{"class":71,"line":114},[69,3657,117],{"class":93},[69,3659,3660],{"class":120}," users",[69,3662,124],{"class":93},[69,3664,127],{"class":83},[69,3666,3667],{"class":75},"([])\n",[69,3669,3670],{"class":71,"line":139},[69,3671,111],{"emptyLinePlaceholder":110},[69,3673,3674,3676,3678,3681,3683,3685],{"class":71,"line":144},[69,3675,408],{"class":83},[69,3677,130],{"class":75},[69,3679,3680],{"class":93},"async",[69,3682,202],{"class":75},[69,3684,160],{"class":93},[69,3686,163],{"class":75},[69,3688,3689,3692,3695,3697,3700,3703,3705,3708],{"class":71,"line":166},[69,3690,3691],{"class":93},"  const",[69,3693,3694],{"class":120}," response",[69,3696,124],{"class":93},[69,3698,3699],{"class":93}," await",[69,3701,3702],{"class":83}," fetch",[69,3704,130],{"class":75},[69,3706,3707],{"class":103},"'\u002Fapi\u002Fusers'",[69,3709,136],{"class":75},[69,3711,3712,3715,3717,3719,3722,3725],{"class":71,"line":181},[69,3713,3714],{"class":75},"  users.value ",[69,3716,289],{"class":93},[69,3718,3699],{"class":93},[69,3720,3721],{"class":75}," response.",[69,3723,3724],{"class":83},"json",[69,3726,3727],{"class":75},"()\n",[69,3729,3730],{"class":71,"line":187},[69,3731,184],{"class":75},[69,3733,3734,3736,3738],{"class":71,"line":192},[69,3735,227],{"class":75},[69,3737,80],{"class":79},[69,3739,87],{"class":75},[15,3741,3742],{},"즉, 페이지가 처음 화면에 나타난 뒤 실행해야 하는 작업에 사용한다.",[3744,3745],"hr",{},[10,3747,3749],{"id":3748},"onupdated",[55,3750,411],{},[15,3752,3753],{},"반응형 상태가 변경되어 화면이 다시 렌더링된 뒤 실행된다.",[15,3755,3600],{},[27,3757,3758,3761,3764],{},[30,3759,3760],{},"화면 갱신 후 변경된 DOM 크기를 확인할 때",[30,3762,3763],{},"목록이나 메시지 영역이 갱신된 뒤 스크롤을 이동할 때",[30,3765,3766],{},"외부 UI 라이브러리와 변경된 DOM을 다시 맞출 때",[60,3768,3770],{"className":62,"code":3769,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { ref, onUpdated } from 'vue'\n\nconst messages = ref([])\n\nonUpdated(() => {\n  console.log('메시지 목록 화면이 갱신됨')\n})\n\u003C\u002Fscript>\n",[55,3771,3772,3782,3793,3797,3810,3814,3824,3837,3841],{"__ignoreMap":65},[69,3773,3774,3776,3778,3780],{"class":71,"line":72},[69,3775,76],{"class":75},[69,3777,80],{"class":79},[69,3779,84],{"class":83},[69,3781,87],{"class":75},[69,3783,3784,3786,3789,3791],{"class":71,"line":90},[69,3785,94],{"class":93},[69,3787,3788],{"class":75}," { ref, onUpdated } ",[69,3790,100],{"class":93},[69,3792,104],{"class":103},[69,3794,3795],{"class":71,"line":107},[69,3796,111],{"emptyLinePlaceholder":110},[69,3798,3799,3801,3804,3806,3808],{"class":71,"line":114},[69,3800,117],{"class":93},[69,3802,3803],{"class":120}," messages",[69,3805,124],{"class":93},[69,3807,127],{"class":83},[69,3809,3667],{"class":75},[69,3811,3812],{"class":71,"line":139},[69,3813,111],{"emptyLinePlaceholder":110},[69,3815,3816,3818,3820,3822],{"class":71,"line":144},[69,3817,411],{"class":83},[69,3819,157],{"class":75},[69,3821,160],{"class":93},[69,3823,163],{"class":75},[69,3825,3826,3828,3830,3832,3835],{"class":71,"line":166},[69,3827,2555],{"class":75},[69,3829,979],{"class":83},[69,3831,130],{"class":75},[69,3833,3834],{"class":103},"'메시지 목록 화면이 갱신됨'",[69,3836,136],{"class":75},[69,3838,3839],{"class":71,"line":181},[69,3840,184],{"class":75},[69,3842,3843,3845,3847],{"class":71,"line":187},[69,3844,227],{"class":75},[69,3846,80],{"class":79},[69,3848,87],{"class":75},[15,3850,3851,3852,3854,3855,3857],{},"다만 ",[55,3853,411],{},"는 컴포넌트가 업데이트될 때마다 실행되므로, 특정 상태의 변경만 확인하려면 ",[55,3856,2384],{},"를 사용하는 편이 더 적절하다.",[60,3859,3861],{"className":934,"code":3860,"language":936,"meta":65,"style":65},"watch(messages, () => {\n  console.log('messages가 변경됨')\n})\n",[55,3862,3863,3874,3887],{"__ignoreMap":65},[69,3864,3865,3867,3870,3872],{"class":71,"line":72},[69,3866,392],{"class":83},[69,3868,3869],{"class":75},"(messages, () ",[69,3871,160],{"class":93},[69,3873,163],{"class":75},[69,3875,3876,3878,3880,3882,3885],{"class":71,"line":90},[69,3877,2555],{"class":75},[69,3879,979],{"class":83},[69,3881,130],{"class":75},[69,3883,3884],{"class":103},"'messages가 변경됨'",[69,3886,136],{"class":75},[69,3888,3889],{"class":71,"line":107},[69,3890,184],{"class":75},[15,3892,3038,3893,3895,3896,3899],{},[55,3894,411],{},"는 특정 데이터보다 ",[19,3897,3898],{},"전체 화면 업데이트 시점","이 중요할 때 사용한다.",[3744,3901],{},[10,3903,3905],{"id":3904},"onunmounted",[55,3906,414],{},[15,3908,3909],{},"컴포넌트가 화면에서 제거된 뒤 실행된다.",[15,3911,3912],{},"주로 컴포넌트가 사용하던 자원을 정리할 때 사용한다.",[27,3914,3915,3918,3927,3930,3933],{},[30,3916,3917],{},"이벤트 리스너 제거",[30,3919,3920,373,3923,3926],{},[55,3921,3922],{},"setInterval",[55,3924,3925],{},"setTimeout"," 정리",[30,3928,3929],{},"WebSocket 연결 종료",[30,3931,3932],{},"외부 라이브러리 인스턴스 제거",[30,3934,3935],{},"구독이나 감시 작업 해제",[60,3937,3939],{"className":62,"code":3938,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { onMounted, onUnmounted } from 'vue'\n\nlet timerId\n\nonMounted(() => {\n  timerId = setInterval(() => {\n    console.log('1초마다 실행')\n  }, 1000)\n})\n\nonUnmounted(() => {\n  clearInterval(timerId)\n})\n\u003C\u002Fscript>\n",[55,3940,3941,3951,3962,3966,3973,3977,3987,4003,4016,4025,4029,4033,4043,4051,4055],{"__ignoreMap":65},[69,3942,3943,3945,3947,3949],{"class":71,"line":72},[69,3944,76],{"class":75},[69,3946,80],{"class":79},[69,3948,84],{"class":83},[69,3950,87],{"class":75},[69,3952,3953,3955,3958,3960],{"class":71,"line":90},[69,3954,94],{"class":93},[69,3956,3957],{"class":75}," { onMounted, onUnmounted } ",[69,3959,100],{"class":93},[69,3961,104],{"class":103},[69,3963,3964],{"class":71,"line":107},[69,3965,111],{"emptyLinePlaceholder":110},[69,3967,3968,3970],{"class":71,"line":114},[69,3969,1457],{"class":93},[69,3971,3972],{"class":75}," timerId\n",[69,3974,3975],{"class":71,"line":139},[69,3976,111],{"emptyLinePlaceholder":110},[69,3978,3979,3981,3983,3985],{"class":71,"line":144},[69,3980,408],{"class":83},[69,3982,157],{"class":75},[69,3984,160],{"class":93},[69,3986,163],{"class":75},[69,3988,3989,3992,3994,3997,3999,4001],{"class":71,"line":166},[69,3990,3991],{"class":75},"  timerId ",[69,3993,289],{"class":93},[69,3995,3996],{"class":83}," setInterval",[69,3998,157],{"class":75},[69,4000,160],{"class":93},[69,4002,163],{"class":75},[69,4004,4005,4007,4009,4011,4014],{"class":71,"line":181},[69,4006,2960],{"class":75},[69,4008,979],{"class":83},[69,4010,130],{"class":75},[69,4012,4013],{"class":103},"'1초마다 실행'",[69,4015,136],{"class":75},[69,4017,4018,4020,4023],{"class":71,"line":187},[69,4019,2190],{"class":75},[69,4021,4022],{"class":120},"1000",[69,4024,136],{"class":75},[69,4026,4027],{"class":71,"line":192},[69,4028,184],{"class":75},[69,4030,4031],{"class":71,"line":209},[69,4032,111],{"emptyLinePlaceholder":110},[69,4034,4035,4037,4039,4041],{"class":71,"line":218},[69,4036,414],{"class":83},[69,4038,157],{"class":75},[69,4040,160],{"class":93},[69,4042,163],{"class":75},[69,4044,4045,4048],{"class":71,"line":224},[69,4046,4047],{"class":83},"  clearInterval",[69,4049,4050],{"class":75},"(timerId)\n",[69,4052,4053],{"class":71,"line":234},[69,4054,184],{"class":75},[69,4056,4057,4059,4061],{"class":71,"line":239},[69,4058,227],{"class":75},[69,4060,80],{"class":79},[69,4062,87],{"class":75},[15,4064,4065],{},"컴포넌트가 제거되었는데도 이벤트나 타이머가 남아 있으면 불필요한 코드가 계속 실행될 수 있다. 이를 메모리 누수라고 한다.",[3744,4067],{},[10,4069,4071],{"id":4070},"전체-흐름","전체 흐름",[60,4073,4076],{"className":4074,"code":4075,"language":2376,"meta":65},[2374],"컴포넌트가 화면에 나타남\n        ↓\nonMounted 실행\n        ↓\n반응형 데이터 변경\n        ↓\n화면 업데이트\n        ↓\nonUpdated 실행\n        ↓\n컴포넌트가 화면에서 제거됨\n        ↓\nonUnmounted 실행\n",[55,4077,4075],{"__ignoreMap":65},[15,4079,3137],{},[27,4081,4082,4087,4092],{},[30,4083,4084,4086],{},[55,4085,408],{},": 화면에 처음 나타난 뒤 초기 작업 실행",[30,4088,4089,4091],{},[55,4090,411],{},": 화면이 다시 그려진 뒤 후속 작업 실행",[30,4093,4094,4096],{},[55,4095,414],{},": 화면에서 사라질 때 이벤트, 타이머, 연결 정리",[15,4098,4099,4100,4102,4103,4105],{},"특히 ",[55,4101,408],{},"에서 등록한 이벤트 리스너나 타이머는 ",[55,4104,414],{},"에서 제거하는 습관을 들이는 것이 좋다.",[10,4107,4109],{"id":4108},"composition-api를-composable로-확장하기","Composition API를 composable로 확장하기",[15,4111,4112,4113,4116],{},"Composition API의 장점은 반복되는 로직을 함수로 분리해 여러 컴포넌트에서 재사용할 수 있다는 점이다. 이런 재사용 함수를 보통 ",[19,4114,4115],{},"composable","이라고 부른다.",[60,4118,4120],{"className":934,"code":4119,"language":936,"meta":65,"style":65},"\u002F\u002F useCounter.js\nimport { computed, ref } from 'vue'\n\nexport function useCounter(initialValue = 0) {\n  const count = ref(initialValue)\n  const doubleCount = computed(() => count.value * 2)\n\n  const increase = () => {\n    count.value++\n  }\n\n  const reset = () => {\n    count.value = initialValue\n  }\n\n  return {\n    count,\n    doubleCount,\n    increase,\n    reset\n  }\n}\n",[55,4121,4122,4127,4137,4141,4165,4178,4201,4205,4219,4226,4230,4234,4249,4259,4263,4267,4273,4278,4283,4288,4293,4297],{"__ignoreMap":65},[69,4123,4124],{"class":71,"line":72},[69,4125,4126],{"class":963},"\u002F\u002F useCounter.js\n",[69,4128,4129,4131,4133,4135],{"class":71,"line":90},[69,4130,94],{"class":93},[69,4132,97],{"class":75},[69,4134,100],{"class":93},[69,4136,104],{"class":103},[69,4138,4139],{"class":71,"line":107},[69,4140,111],{"emptyLinePlaceholder":110},[69,4142,4143,4146,4149,4152,4154,4157,4159,4162],{"class":71,"line":114},[69,4144,4145],{"class":93},"export",[69,4147,4148],{"class":93}," function",[69,4150,4151],{"class":83}," useCounter",[69,4153,130],{"class":75},[69,4155,4156],{"class":1248},"initialValue",[69,4158,124],{"class":93},[69,4160,4161],{"class":120}," 0",[69,4163,4164],{"class":75},") {\n",[69,4166,4167,4169,4171,4173,4175],{"class":71,"line":139},[69,4168,3691],{"class":93},[69,4170,121],{"class":120},[69,4172,124],{"class":93},[69,4174,127],{"class":83},[69,4176,4177],{"class":75},"(initialValue)\n",[69,4179,4180,4182,4184,4186,4188,4190,4192,4194,4196,4199],{"class":71,"line":144},[69,4181,3691],{"class":93},[69,4183,149],{"class":120},[69,4185,124],{"class":93},[69,4187,154],{"class":83},[69,4189,157],{"class":75},[69,4191,160],{"class":93},[69,4193,172],{"class":75},[69,4195,175],{"class":93},[69,4197,4198],{"class":120}," 2",[69,4200,136],{"class":75},[69,4202,4203],{"class":71,"line":166},[69,4204,111],{"emptyLinePlaceholder":110},[69,4206,4207,4209,4211,4213,4215,4217],{"class":71,"line":181},[69,4208,3691],{"class":93},[69,4210,197],{"class":83},[69,4212,124],{"class":93},[69,4214,202],{"class":75},[69,4216,160],{"class":93},[69,4218,163],{"class":75},[69,4220,4221,4224],{"class":71,"line":187},[69,4222,4223],{"class":75},"    count.value",[69,4225,215],{"class":93},[69,4227,4228],{"class":71,"line":192},[69,4229,2849],{"class":75},[69,4231,4232],{"class":71,"line":209},[69,4233,111],{"emptyLinePlaceholder":110},[69,4235,4236,4238,4241,4243,4245,4247],{"class":71,"line":218},[69,4237,3691],{"class":93},[69,4239,4240],{"class":83}," reset",[69,4242,124],{"class":93},[69,4244,202],{"class":75},[69,4246,160],{"class":93},[69,4248,163],{"class":75},[69,4250,4251,4254,4256],{"class":71,"line":224},[69,4252,4253],{"class":75},"    count.value ",[69,4255,289],{"class":93},[69,4257,4258],{"class":75}," initialValue\n",[69,4260,4261],{"class":71,"line":234},[69,4262,2849],{"class":75},[69,4264,4265],{"class":71,"line":239},[69,4266,111],{"emptyLinePlaceholder":110},[69,4268,4269,4271],{"class":71,"line":249},[69,4270,169],{"class":93},[69,4272,163],{"class":75},[69,4274,4275],{"class":71,"line":264},[69,4276,4277],{"class":75},"    count,\n",[69,4279,4280],{"class":71,"line":278},[69,4281,4282],{"class":75},"    doubleCount,\n",[69,4284,4285],{"class":71,"line":302},[69,4286,4287],{"class":75},"    increase,\n",[69,4289,4290],{"class":71,"line":718},[69,4291,4292],{"class":75},"    reset\n",[69,4294,4295],{"class":71,"line":727},[69,4296,2849],{"class":75},[69,4298,4299],{"class":71,"line":732},[69,4300,221],{"class":75},[60,4302,4304],{"className":62,"code":4303,"language":64,"meta":65,"style":65},"\u003Cscript setup>\nimport { useCounter } from '.\u002FuseCounter'\n\nconst { count, doubleCount, increase, reset } = useCounter(10)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>{{ count }} \u002F {{ doubleCount }}\u003C\u002Fp>\n  \u003Cbutton @click=\"increase\">증가\u003C\u002Fbutton>\n  \u003Cbutton @click=\"reset\">초기화\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[55,4305,4306,4316,4328,4332,4366,4374,4378,4386,4399,4417,4437],{"__ignoreMap":65},[69,4307,4308,4310,4312,4314],{"class":71,"line":72},[69,4309,76],{"class":75},[69,4311,80],{"class":79},[69,4313,84],{"class":83},[69,4315,87],{"class":75},[69,4317,4318,4320,4323,4325],{"class":71,"line":90},[69,4319,94],{"class":93},[69,4321,4322],{"class":75}," { useCounter } ",[69,4324,100],{"class":93},[69,4326,4327],{"class":103}," '.\u002FuseCounter'\n",[69,4329,4330],{"class":71,"line":107},[69,4331,111],{"emptyLinePlaceholder":110},[69,4333,4334,4336,4338,4340,4342,4344,4346,4348,4350,4353,4355,4357,4359,4361,4364],{"class":71,"line":114},[69,4335,117],{"class":93},[69,4337,1680],{"class":75},[69,4339,314],{"class":120},[69,4341,373],{"class":75},[69,4343,318],{"class":120},[69,4345,373],{"class":75},[69,4347,322],{"class":120},[69,4349,373],{"class":75},[69,4351,4352],{"class":120},"reset",[69,4354,1691],{"class":75},[69,4356,289],{"class":93},[69,4358,4151],{"class":83},[69,4360,130],{"class":75},[69,4362,4363],{"class":120},"10",[69,4365,136],{"class":75},[69,4367,4368,4370,4372],{"class":71,"line":139},[69,4369,227],{"class":75},[69,4371,80],{"class":79},[69,4373,87],{"class":75},[69,4375,4376],{"class":71,"line":144},[69,4377,111],{"emptyLinePlaceholder":110},[69,4379,4380,4382,4384],{"class":71,"line":166},[69,4381,76],{"class":75},[69,4383,244],{"class":79},[69,4385,87],{"class":75},[69,4387,4388,4390,4392,4395,4397],{"class":71,"line":181},[69,4389,252],{"class":75},[69,4391,15],{"class":79},[69,4393,4394],{"class":75},">{{ count }} \u002F {{ doubleCount }}\u003C\u002F",[69,4396,15],{"class":79},[69,4398,87],{"class":75},[69,4400,4401,4403,4405,4407,4409,4411,4413,4415],{"class":71,"line":187},[69,4402,252],{"class":75},[69,4404,283],{"class":79},[69,4406,286],{"class":83},[69,4408,289],{"class":75},[69,4410,292],{"class":103},[69,4412,295],{"class":75},[69,4414,283],{"class":79},[69,4416,87],{"class":75},[69,4418,4419,4421,4423,4425,4427,4430,4433,4435],{"class":71,"line":192},[69,4420,252],{"class":75},[69,4422,283],{"class":79},[69,4424,286],{"class":83},[69,4426,289],{"class":75},[69,4428,4429],{"class":103},"\"reset\"",[69,4431,4432],{"class":75},">초기화\u003C\u002F",[69,4434,283],{"class":79},[69,4436,87],{"class":75},[69,4438,4439,4441,4443],{"class":71,"line":209},[69,4440,227],{"class":75},[69,4442,244],{"class":79},[69,4444,87],{"class":75},[15,4446,4447],{},"상태와 함수가 특정 기능을 중심으로 묶여 있으니,",[15,4449,4450],{},"컴포넌트가 커져도 로직을 분리해서 관리할 수 있다.",[15,4452,4453],{},[334,4454,4455],{},"역시 재탕은 짜릿해",[15,4457,4458,4459,373,4462,373,4465,4468],{},"나중에 ",[55,4460,4461],{},"useFetch",[55,4463,4464],{},"useTodo",[55,4466,4467],{},"useAuth"," 같은 형태로 확장하는 흐름이다.",[10,4470,4472],{"id":4471},"최종-정리","최종 정리",[15,4474,4475],{},"Composition API는 단순히 함수 몇 개를 새로 사용하는 문법이 아니다. 컴포넌트의 로직을 기능 단위로 구성하고, 필요한 반응형 흐름을 조합하는 방식이다.",[60,4477,4480],{"className":4478,"code":4479,"language":2376,"meta":65},[2374],"ref \u002F reactive\n      ↓\n반응형 상태 생성\n      ↓\ncomputed로 파생 값 계산\n      ↓\nwatch 또는 watchEffect로 변경 감지\n      ↓\n함수와 템플릿으로 화면 연결\n      ↓\ncomposable로 재사용\n",[55,4481,4479],{"__ignoreMap":65},[15,4483,4484],{},"기억할 것만 다시 적어보자.",[4486,4487,4488,4493,4498,4509,4514,4519,4524,4529],"ol",{},[30,4489,4490,4492],{},[55,4491,372],{},"는 원시 값과 객체, 배열을 반응형으로 만든다.",[30,4494,4495,4497],{},[55,4496,376],{},"는 객체나 배열 자체를 반응형으로 만든다.",[30,4499,4500,4501,4503,4504,4506,4507,931],{},"JavaScript에서 ",[55,4502,372],{},"는 ",[55,4505,927],{},", 템플릿에서는 ",[55,4508,927],{},[30,4510,4511,4513],{},[55,4512,389],{},"는 상태에서 파생된 값을 계산한다.",[30,4515,4516,4518],{},[55,4517,392],{},"는 특정 값의 변경 후 작업을 실행한다.",[30,4520,4521,4523],{},[55,4522,395],{},"는 함수 내부에서 사용하는 반응형 값을 자동으로 추적한다.",[30,4525,4526,4528],{},[55,4527,414],{},"에서 이벤트와 타이머를 정리한다.",[30,4530,4531],{},"반복되는 로직은 composable로 분리한다.",[15,4533,4534,4535,373,4537,373,4539,4541],{},"처음에는 ",[55,4536,372],{},[55,4538,389],{},[55,4540,392],{},"만으로도 충분하다. 나머지는 실제로 필요한 상황이 왔을 때 하나씩 살펴보면 된다. 현업에 가서 빨리 써보고 싶다. 일단은 여기까지!",[10,4543,4545],{"id":4544},"참고-자료","참고 자료",[27,4547,4548,4557,4564],{},[30,4549,4550],{},[4551,4552,4556],"a",{"href":4553,"rel":4554},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fextras\u002Fcomposition-api-faq.html",[4555],"nofollow","Vue 공식 문서: Composition API FAQ",[30,4558,4559],{},[4551,4560,4563],{"href":4561,"rel":4562},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fessentials\u002Fcomputed.html",[4555],"Vue 공식 문서: Computed Properties",[30,4565,4566],{},[4551,4567,4570],{"href":4568,"rel":4569},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fessentials\u002Fwatchers.html",[4555],"Vue 공식 문서: Watchers",[3744,4572],{},[4574,4575,4576],"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 .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 .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":65,"searchDepth":90,"depth":90,"links":4578},[4579,4580,4581,4586,4594,4599,4604,4609,4610,4611,4612,4613,4614,4615,4616],{"id":12,"depth":90,"text":13},{"id":326,"depth":90,"text":327},{"id":501,"depth":90,"text":4582,"children":4583},"ref()로 반응형 상태 만들기",[4584],{"id":1009,"depth":107,"text":4585},"ref를 사용할 때 기억할 점",{"id":1042,"depth":90,"text":4587,"children":4588},"reactive()로 객체와 배열 다루기",[4589,4591,4593],{"id":1441,"depth":107,"text":4590},"reactive의 주의점",{"id":1525,"depth":107,"text":4592},"ref와 reactive 비교",{"id":1602,"depth":107,"text":1603},{"id":1817,"depth":90,"text":4595,"children":4596},"computed()로 계산된 값 만들기",[4597],{"id":2270,"depth":107,"text":4598},"일반 함수와 computed 구분",{"id":2381,"depth":90,"text":4600,"children":4601},"watch()로 변경 후 작업 실행하기",[4602,4603],{"id":2693,"depth":107,"text":2694},{"id":2859,"depth":107,"text":2860},{"id":3221,"depth":90,"text":4605,"children":4606},"watchEffect()로 자동 감시하기",[4607],{"id":3343,"depth":107,"text":4608},"watch, watchEffect 비교",{"id":3423,"depth":90,"text":3424},{"id":3592,"depth":90,"text":408},{"id":3748,"depth":90,"text":411},{"id":3904,"depth":90,"text":414},{"id":4070,"depth":90,"text":4071},{"id":4108,"depth":90,"text":4109},{"id":4471,"depth":90,"text":4472},{"id":4544,"depth":90,"text":4545},[4618,4619],"Frontend","Vue","2026-08-02 09:00:00 +0900","Vue Composition API의 기본 개념과 ref, reactive, computed, watch, watchEffect의 사용 방법을 정리한다.","md",null,[4625,4626,4627],"ref와 reactive로 반응형 상태를 만듬.","computed는 상태에서 파생된 값을 계산하고, watch는 변경 후 작업을 처리.","watchEffect는 함수 내부에서 사용하는 반응형 값을 자동으로 추적.","\u002Ffrontend\u002Fvue\u002F2026\u002F08\u002F02\u002Fvue-composition-api\u002F",{"layout":4630,"excerpt_separator":4631,"excerpt":4632},"post","\u003C!--more-->",{"type":7,"value":4633},[4634,4636,4640,4642,4654,4656],[10,4635,13],{"id":12},[15,4637,17,4638,22],{},[19,4639,21],{},[15,4641,25],{},[27,4643,4644,4646,4648,4650,4652],{},[30,4645,32],{},[30,4647,35],{},[30,4649,38],{},[30,4651,41],{},[30,4653,44],{},[15,4655,47],{},[15,4657,50],{},"\u002Fposts\u002Ffrontend\u002Fvue\u002F2026-08-02-vue-composition-api",{"title":5,"description":4621},"posts\u002FFrontend\u002FVue\u002F2026-08-02-vue-composition-api",[4662,4663],"기능 단위로 상태와 함수를 묶어 큰 컴포넌트도 정리하기 좋음.","재사용할 로직을 composable로 분리하기 쉽다.","Composition API는 컴포넌트의 상태와 로직을 기능별로 모아 작성하는 Vue 3의 코드 작성 방식.",[4618,4666,21,372,376,389,392],"Vue.js",[4668,4669],"반응성 연결과 실행 시점을 이해하지 않으면 코드 흐름이 복잡해질 수 있다.","Options API보다 자유로운 만큼 팀의 작성 규칙이 필요하다.","ogbpXAQmaVZe6MJlOFNFClMyQWlxBQ-1eiiCCUehlK0",1788744789767]