웹 개발/Vue

[Vue3] shallowRef (ref와의 차이)

cha430 2025. 12. 30. 14:46

활용 예시

전체 코드

 

- shallowRef 란 ?

값 자체만 바뀌는지 감시하고 그 안까지 깊게 들여다보지는 않는 ref.

우선 Ref 와 shallowRef 의 차이부터 감을 잡으면 좋다.

 

 

1. Ref

ref 는 깊게 감시(deep)하여 객체 안까지 전부 반응성 추적을 한다.

const a = ref({ x: 1 })
a.value.x = 2	// 반응함

 

a의 값까지 변경할 수 있다.

 

 

 

2. shallowRef

통째로 바뀔 때만 반응한다.

내부는 신경쓰지 않고 겉만 보기 때문에 shallow 이다.

const a = shallowRef({ x: 1 })
a.value.x = 2   // 반응 안 함
a.value = { x: 2 } // 반응함

 

 

 

const currComponent = ref(AutoComplete)

 

AutoComplete 는 컴포넌트, 즉 객체이다.

만약 ref 로 선언하는 경우 컴포넌트 내부의 setup, render, props 정의 등

모든 것을 반응성 추적하려고 시도한다.

 

굳이 그럴 필요가 없기 때문에

보통 겉만 확인하는 shallowRef 로 선언해서 사용한다.

 

 

 


활용 예시)

 

최상위 부모 ─ CodeHelp ─ AutoComplete
                                       └ Select 

 

 

// 부모 CodeHelp.vue 

 

 

마운트될 때 CodeHelp.vue의 부모로부터

isFilter 값을 받아서 

참조할 컴포넌트 AutoComplete 또는 Select 를 정한다.

<template>
    <Component :is="currComponent" :codeName="codeName" :items="detailList" :selectItem="item" :isReadOnly="props.isReadOnly" />
</template>

 

const props = defineProps({
    isFilter: { //-- autocomplete, select 분기 해서 보여줌.
        type: Boolean,
        default: true
    },
onMounted(() => {
    currComponent.value = props.isFilter ? AutoComplete : Select

 

 

 

그리고

최상위부모에서 사용할 때

 

 // Item.vue

<CodeHelp :master-seq="1"
          :is-filter="false"
          :label="_t('page.item.itemType')"
          clearable
          @onChange="onChange"
          density="compact"
          variant="outlined">
</CodeHelp>

 

이렇게 isFilter 값을 false로 보냈을 경우 (default : true)

Select 로 만들어지고

 

 

// ItemDialog.vue

<CodeHelp
    :master-seq="60"
    :is-filter="true"
    v-model="itemData.itemSpec"
    :label="_t('page.item.dialog.spec')"
    @onChange="onChange"
    variant="outlined"
    clearable
    hide-details="auto"
    density="compact">
</CodeHelp>

 

true 로 넘기거나

isFilter 값을 넘기지 않을 경우

 

AutoComplete 로 만들어진다.

(입력 가능)

 

 


 

 

전체 코드

(AutoComplete, Select, CodeHelp)

 

AutoComplete.vue

<template>
    <!-- 검색 가능 -->
    <v-autocomplete
        v-model="item"
        :items="props.items"
        :label="props.codeName"
        :readonly="props.isReadOnly"
        @update:modelValue="onChange"
        return-object
        :item-title="itemTitle"
        item-value="detailSeq"
        density="compact"
        variant="outlined"
    ></v-autocomplete>
</template>

<script setup>
import {onMounted, ref} from "vue";
import {consoleLog, getLanguage} from "@/assets/js/common/common.js";
import _ from "lodash";
const props = defineProps({
    selectItem: {
        type: Object,
        default:{}
    },
    items: {
        type: Object
    },
    codeName: {
        type: String,
        default: "공통코드"
    },
    isReadOnly: {
        type: Boolean,
        default: false
    }
})

let itemTitle = getLanguage()==="ko"?"codeName":"codeEngName"

const emit = defineEmits(["onChange"]);

const item = ref(props.selectItem)

const onChange = (event) => {
    let param = {
        codeNameM: props.codeName,
        codeNameD: _.isNull(event)?0:event.codeName,
        codeNickName: _.isNull(event)?"":event.codeNickName,
        dummy1: !event?"":event.dummy1,
        dummy2: !event?"":event.dummy2,
        dummy3: !event?"":event.dummy3,
        dummy4: !event?"":event.dummy4,
        dummy5: !event?"":event.dummy5,
        masterSeq: _.isNull(event)?0:event.masterSeq,
        detailSeq: _.isNull(event)?0:event.detailSeq,

    }

    consoleLog("CodeHelp onChange(Autocomplete)", param)
    emit("onChange", param)
}
</script>

 

 

 

 

Select.vue

<template>
    <v-select
        v-model="item"
        :items="props.items"
        :label="props.codeName"
        :readonly="props.isReadOnly"
        @update:modelValue="onChange"
        return-object
        :item-title="itemTitle"
        item-value="detailSeq"
        density="compact"
        variant="outlined"
    ></v-select>
</template>

<script setup>
import {onMounted, ref} from "vue";
import {consoleLog, getLanguage} from "@/assets/js/common/common.js";
import _ from "lodash";
const props = defineProps({
    selectItem: {
        type: Object
    },
    items: {
        type: Object
    },
    codeName: {
        type: String,
        default: "공통코드"
    },
    isReadOnly: {
        type: Boolean,
        default: false
    }
})

let itemTitle = getLanguage()==="ko"?"codeName":"codeEngName"

const emit = defineEmits(["onChange"]);

const item = ref(props.selectItem)

const onChange = (event) => {

    let param = {
        codeNameM: props.codeName,
        codeNameD: _.isNull(event)?0:event.codeName,
        codeNickName: _.isNull(event)?"":event.codeNickName,
        dummy1: !event?"":event.dummy1,
        dummy2: !event?"":event.dummy2,
        dummy3: !event?"":event.dummy3,
        dummy4: !event?"":event.dummy4,
        dummy5: !event?"":event.dummy5,
        masterSeq: _.isNull(event)?0:event.masterSeq,
        detailSeq: _.isNull(event)?0:event.detailSeq
    }

    consoleLog("CodeHelp onChange(Select)", param)
    emit("onChange", param)
}

</script>

<style scoped>

</style>

 

 

 

 

CodeHelp.vue

<!--
공통코드조회 Component

@onChange로 데이터를 받으면 됨.

-->
<template>
    <Component :is="currComponent" :codeName="codeName" :items="detailList" :selectItem="item" :isReadOnly="props.isReadOnly" />
</template>

<script setup>
import {onMounted, ref, shallowRef} from "vue";
import {consoleLog, doRequest, getLanguage, isDev} from "@/assets/js/common/common.js";
import {codeHelpList} from "@/assets/js/json/jsonData.js";
import AutoComplete from "@/components/common/codeHelp/AutoComplete.vue";
import Select from "@/components/common/codeHelp/Select.vue";
const currComponent = shallowRef(AutoComplete)

const props = defineProps({
    isFilter: { //-- autocomplete, select 분기 해서 보여줌.
        type: Boolean,
        default: true
    },
    masterSeq: {
        type: Number,
        default: 0
    },
    detailSeq: {
        type: Number,
        default: 0
    },
    isReadOnly: {
        type: Boolean,
        default: false
    }
})

const item = ref()
const detailList = ref()
const codeName = ref("공통코드")

onMounted(() => {

    currComponent.value = props.isFilter?AutoComplete:Select

    if (!isDev){
        getData()
    }
    else{ //-- 테스트 샘플 코드
        detailList.value = codeHelpList

        for (let codes of codeHelpList){
            if(codes.detailSeq === props.detailSeq){
                item.value = codes
            }
        }

    }
})

const getData = async () => {
    await doRequest("post", "../../common/codeHelp/getCodeHelpList.do", {"masterSeq": props.masterSeq})
        .then(res => {

            let lang = getLanguage()
            consoleLog("CodeHelp Res", res.data)
            consoleLog("CodeHelpM", res.data.codeListM)
            consoleLog("CodeHelpD", res.data.codeListD)

            detailList.value = res.data.codeListD
            codeName.value = lang==="ko"?res.data.codeListM[0].codeName:res.data.codeListM[0].codeEngName

            if (props.detailSeq !== 0){
                for (let codes of res.data.codeListD){
                    if(codes.detailSeq === props.detailSeq){
                        item.value = codes
                    }
                }
            }
            else{
                item.value = res.data.codeListD[0]
            }
            consoleLog("CodeHelpD", res.data.codeListD)

        })
}

</script>