ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Vue] 게시물 목록별 체크박스 간단히 구현하기
    Vue 2023. 5. 29. 17:41

    기존에 React로는 게시물 목록별 체크박스를 구현해보았던 경험이 있었다. 그런데 이번에 처음으로 Vue를 사용하여 게시물 목록별 체크박스를 구현해보게 되었고, 가장 깔끔한 방식을 나름대로 찾은 것 같아 공유하고자 한다.

     

    먼저 이번에 구현한 체크박스는 다음과 같다.

     

    게시물 별로 체크박스가 테이블에 구현되어있고, 개별 체크박스를 선택하여 게시물을 삭제하거나 다른 기능과 연동될 수 있는 부분이다. 먼저 이 각각의 체크박스는 목록 개별로 state 관리가 필요하므로 컴포넌트를 분리하여야한다. 그런데 체크박스를 여러개(중복) 선택 후 삭제를 해야한다면 어떻게 해야할까? React에서는 자식 컴포넌트의 상태를 부모 컴포넌트에게 전달하지 못하므로 전역 state 관리 툴인 Recoil이나 Redux를 사용했어야 할 것이다. 실제로 지난 프로젝트에서 전역으로 삭제 목록 리스트를 관리하여 부모 컴포넌트에서 delete 요청을 했었다. 그런데 Vue에서는 emit을 사용할 수 있으므로 삭제할 자식 체크박스 컴포넌트에서 부모 컴포넌트로 해당 목록의 id를 전달하면 부모 컴포넌트에서 id를 받아 삭제할 수 있다.

     

    그런데 이번에는 중복 선택이 아니라 하나만 선택할 수 있도록 해야했다. 이를 위해선 Vue에서도 전역 상태관리가 필요할 것 같다. 같은 체크박스 자식 컴포넌트끼리 소통(?)할 수 도 없고, 부모에게 emit으로 전달한다고 해도 부모에서 for문과 if문을 이중으로 돌릴 바에는, 삭제할 목록을 전역으로 관리하고 체크박스 컴포넌트에서 삭제할 id에 해당할 때만 체크박스가 채워지게 나타나도록 할 수 있을 것이다.

     

    무슨 말이냐면, 하나의 체크박스를 클릭하여 채운 후, 다른 체크박스를 클릭 시 기존의 채워졌던 체크박스 컴포넌트 입장에선 value가 true에서 false로 바뀌어야 하는데 자식 컴포넌트끼리는 서로 알 수가 없기 때문이다. 물론 EventBus를 활용하면 같은 레벨의 컴포넌트끼리 데이터를 전달할 수 있지만, 전역으로 관리해야 부모 컴포넌트에서도 체크된 게시물이 무엇인지 쉽게 파악할 수 있을 것 같아 전역 상태관리 툴인 pinia를 사용하여 구현하였다.

     

    구체적인 구현 과정은 다음과 같다.

     

    1. 각각의 분리된 체크박스 컴포넌트는 게시물 id를 props로 넘겨준다.
    2. 체크 박스의 val 값의 true / false가 바뀐다.
    3. watch를 통해 val이 바뀌면 store의 checkedId(전역적으로 관리되는 체크된 게시물 id, 이 id를 통해 목록의 삭제 등이 가능하다.)가 해당 props의 id로 바뀐다.
    4. watch를 통해 checkedId가 바뀌면 다른 목록 체크박스들의 값이 props의 id가 checkedId와 일치하지 않으므로 val이 false로 바뀌도록 한다.

     

    그런데 여기서 발생한 문제가 있다. val이 false로 바뀐 것을 checkedId를 ‘’로 바꾸는 코드를 넣으니까, 바로 다른 체크박스를 클릭한 경우 ‘’로 바뀌고 새로운 체크박스의 props id로 바뀌어야 하는데 이게 같이 이뤄지지 않아 checkedId가 ‘’로만 바뀌는 문제가 발생하였다. 그렇다고 val이 false일 때 ‘’로 바꾸는 코드를 넣지 않으면 val이 true인 체크박스를 한번 더 클릭하여 false가 되었을 때 checkedId가 ‘’로 안바뀌게 되는 문제가 또다시 발생하게 되었다.

     

    즉 새롭게 checkedId가 바뀔 때, 기존의 것을 ‘’로 바꾸려는 것과 새로운 props의 id로 바꾸려는 코드가 순차적으로 이뤄지지 못하여 발생한 문제였다. 기존의 것을 ‘’로 바꾸려는 것은 기존의 체크된 체크박스를 한번 더 클릭하여 val을 false로 바꿨을 경우에만 해당하는 것이므로 기존의 체크박스를 한번 더 클릭한 경우에만 checkedId를 ‘’로 만드는 코드를 추가하여 해결하였다.

     

    <template>
      <q-checkbox v-model="val" color="negative" />
    </template>
    
    <script>
    import { ref, watch } from 'vue';
    import { useCheckedResearch } from 'src/stores/checkedResearch';
    import { storeToRefs } from 'pinia';
    
    export default {
      props: ['id'],
      setup(props) {
        const store = useCheckedResearch();
        const { checkedId } = storeToRefs(store);
        let val = ref(false);
    
        if (checkedId.value === props.id) val.value = true;
    
        watch(val, (newVal) => {
          if (!newVal && checkedId.value === props.id) checkedId.value = '';
          if (newVal) checkedId.value = props.id;
        });
    
        watch(checkedId, (newVal) => {
          if (props.id !== newVal) val.value = false;
        });
    
        return { val };
      },
    };
    </script>
    
    <style lang="sass" scoped></style>
Designed by Tistory.