1.11- React masterClass (trello cloning2)

hun2__2·2022년 1월 11일
0

Have a fruitful vacation

목록 보기
15/24

어제 작업한 곳에 스타일을 조금 입혔다

아직 onDragEnd 함수를 완성하지 못하여 옮겨지지는 않지만 여러개의 board와 card가 정상적으로 뜬다.

atom의 state가 하나의array일때 했던것처럼 먼저 어떤 정보를 받아오는데 test해보면

  const onDragEnd = (args: DropResult) => {
    console.log(
      args.source.droppableId,
      args.source.index,
      args.draggableId,
      "=>",
      args.destination.droppableId,
      args.destination.index,
      args
    );
  };


동일하게 drag한 data는 source와 draggableId에 drop한 data는 destination 있으며 달라진점은 droppableId가 board에 따라 달라져서 고려해줘야 한다는 점이다.

먼저 동일한 자리에 drop했을때 그대로 return해주도록 하고

  const onDragEnd = ({ destination, draggableId, source }: DropResult) => {
    console.log(
      source.droppableId,
      source.index,
      draggableId,
      "=>",
      destination.droppableId,
      destination.index
    );
    const dragItem = draggableId;
    const dragItemId = source.droppableId;
    const dragItemIdx = source.index;
    const dropItemId = destination.droppableId;
    const dropItemIdx = destination.index;
    
    // ! 같은 자리에 두었을 때 destination와 source가 동일함
    if (dragItemId === dropItemId && dragItemIdx === dropItemIdx) return;
  };

이전과 동일하게
1) 기존obj를 깊은복사
2) drag한 값을 삭제
3) dop한 곳에 추가

순서로 작업해준다.

1) 기존obj를 깊은복사
여기서는 array와는 다르게 단순히 es6문법이나 .slice() 메서드로 깊은 복사를 할 수 없다.

왜냐하면 ES6 문법인 spread operator는 1단계에서만 깊은 복사가 이뤄지며 그 이상은 얕은 복사가 된다.
(참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax)

깊은 복사를 하는 첫번째 방법으로는
JSON.parse()와 JSON.stringify를 이용해서 깊은 복사를 하는 방법으로 하였는데
먼저 JSON.stringify()은 JS Obj를 JSON문자열을 변환시킨다.
그리고 JSON.parse()은 JSON문자열을 다시 JS Obj로 변환시킨다. 이방법을 통해서
동일한 data가 담긴 obj를 만들어 주고 그곳에 삭제/추가 작업을 진행해준다

    setList((oldObj) => {
      const cloneObj = (obj: IToDoState) => JSON.parse(JSON.stringify(obj));
      const newObj = cloneObj(oldObj);
      newObj[dragItemId].splice(dragItemIdx, 1);
      newObj[dropItemId].splice(dropItemIdx, 0, dragItem);
      return newObj;
    });

구글링해보니 이 방법에는 3가지문제점이 있다고 한다.
첫번째로 다른 방법에 비해 성능이 느리고,
두번째로 모두 JSON 문자열로 만들어버리기 때문에 날짜 유형의 data는 제대로 복사되지 않고
세번째로는 두번째와 동일한 이유로 유형이나 function이 유지되지 않고 undefine으로 반환된다.

두번째로는 Lodash라이브러리의 deepClone함수를 사용하는 방법이다.
먼저 install을 해주고

npm i --save lodash

역시나 타입이 있다ㅎ

npm install --save @types/lodash

사용할 곳에서 lodash를 import해주고 동일한 로직을 작성할 수 있다.

import * as lodash from "lodash";
...
    setList((oldObj) => {
      const newObj = lodash.cloneDeep(oldObj);
      newObj[dragItemId].splice(dragItemIdx, 1);
      newObj[dropItemId].splice(dropItemIdx, 0, dragItem);
      return newObj;
    });

세번쨰로 직접 구현하는 방법이 있다. 로직은 간단하다 새로운 빈 ojb을 만든 후 재귀를 돌리면서 모든 key값에 해당하는 value를 깊은복사해준다.
하지만 TS로 짜려니깐 좀 빡세다;;; TS 쉽지않네,,
누군가가 짜논 코드는 발견했지만 사용하지 않기로 하고 로직을 바꿔보자
(TS로 deep copy로직 : https://javascript.plainenglish.io/deep-clone-an-object-and-preserve-its-type-with-typescript-d488c35e5574)

ES6의 Spread Operator가 1단계밖에 깊은 복사를 못 하니 한 단계씩 바꿔주는 것이다. Obj를 복사하는게 고치는게 아니라 아니라 단지 array만 복사해줘서 해당 array를 변경한 Obj를 반환하는 것이다
먼저 이렇게 하려면
같은 board내에서 변경하는 경우 (array 하나만 수정)
다른 board로 이동하는 경우 (array 두개 수정)
두가지 경우가 생기므로 두 경우를 나눠준다.

    if (dragItemId === dropItemId) {
      //! 같은 board내에서 움직일 때
      return;
    } else {
      //! 다른 board로 움직일 때
      return;
    }

그 다음 동일 한 방법으로 array를 작업하고 obj에 변경해준다.

1) drag된 array 복사

    if (dragItemId === dropItemId) {
      //! 같은 board내에서 움직일 때
      setList((oldObj) => {
        const board = [...oldObj[dragItemId]];
        return;
      });
    } else {
      //! 다른 board로 움직일 때
      setList((oldObj) => {
        const drag = [...oldObj[dragItemId]];
        const drop = [...oldObj[dropItemId]];
        return;
      });
    }

2) 해당 index 삭제

    if (dragItemId === dropItemId) {
      //! 같은 board내에서 움직일 때
      setList((oldObj) => {
        const board = [...oldObj[dragItemId]];
        board.splice(dragItemIdx, 1);
        return ;
      });
    } else {
      //! 다른 board로 움직일 때
      setList((oldObj) => {
        const drag = [...oldObj[dragItemId]];
        const drop = [...oldObj[dropItemId]];
        drag.splice(dragItemIdx, 1);
        return ;
      });
    }

3) 추가된 array로 변경

    if (dragItemId === dropItemId) {
      //! 같은 board내에서 움직일 때
      setList((oldObj) => {
        const board = [...oldObj[dragItemId]];
        board.splice(dragItemIdx, 1);
        board.splice(dropItemIdx, 0, dragItem);
        return ;
      });
    } else {
      //! 다른 board로 움직일 때
      setList((oldObj) => {
        const drag = [...oldObj[dragItemId]];
        const drop = [...oldObj[dropItemId]];
        drag.splice(dragItemIdx, 1);
        drop.splice(dropItemIdx, 0, dragItem);
        return ;
      });
    }

4) 나머지 obj 요소들을 받아오고 해당 array만 변경

    if (dragItemId === dropItemId) {
      //! 같은 board내에서 움직일 때
      setList((oldObj) => {
        const board = [...oldObj[dragItemId]];
        board.splice(dragItemIdx, 1);
        board.splice(dropItemIdx, 0, dragItem);
        return { ...oldObj, [dragItemId]: board };
      });
    } else {
      //! 다른 board로 움직일 때
      setList((oldObj) => {
        const drag = [...oldObj[dragItemId]];
        const drop = [...oldObj[dropItemId]];
        drag.splice(dragItemIdx, 1);
        drop.splice(dropItemIdx, 0, dragItem);
        return { ...oldObj, [dragItemId]: drag, [dropItemId]: drop };
      });
    }

이렇게 깊은 복사없이 변경된 부분만 수정하여 obj를 반환해주면 정상적으로 작동한다.

(참고 :
https://junwoo45.github.io/2019-09-23-deep_clone/,
https://javascript.plainenglish.io/deep-clone-an-object-and-preserve-its-type-with-typescript-d488c35e5574)


기능적으로 구현이 되었으니 약간의 style을 추가해보자 구현할 기능은 react-beatiful-dnd에 나와있는 card를 옮길때 board의 색깔이 변하는 기능이다.

이전에 Droppable의 children의 인자로 provided를 사용했는데 인자로snapshot이라는 기능도 들어가 있다.

snapshot의 타입을 살펴보면
이렇한 props를 가지고 있는데
isDraggingOver 는 drag 되었는지르 boolean값으로 나타내고,
draggingOverWith는 drag중인 item의 id | undefined를 반환하고
draggingFromThisWith는 drag한 list의 id | undefined를 반환한다.

우리가 구현 할 기능은 먼저 3가지 색이 필요하다.
아무것도 안 하는 board의 기본 색상,
drag한 board의 색상
drop할 공간(현재 drag되어있는 board)의 board 색상

따라서 로직은 drag시 ? drop할 곳 색상 : (drag한 list있음 ? drag한 곳 색상: 기본색상)
이 된다.

먼저 이와 같은 로직을 위해 snapshot.isDraggingOver값과 snapshot.draggingFromThisWith을 받아오고
isDraggingOver={snapshot.isDraggingOver}
isDreggingFromThis={Boolean(snapshot.draggingFromThisWith)}
를 prop를 넘겨줘서 board의 backgroundColor를를 변경해준다.

cf) JS내부로직에 의해서 조건문 같이 boolean 타입의 값이 필요한 곳에서는 다른 타입값도 boolean타입으로 형변환된다.
Flase로 평가되는 값들은 false, undefind, null, NaN, 0, ''가 있고,
[], {}, 나머지는 True로 평가받는다.

interface IAreaProps {
  isDreggingFromThis: boolean;
  isDraggingOver: boolean;
}

const Wrapper = styled.ul<IAreaProps>`
  ...
  background-color: ${(props) =>
    props.isDraggingOver
      ? "#f7b733"
      : props.isDreggingFromThis
      ? "#ff9966"
      : props.theme.boardColor};

그러면 이제 옮길때 색도 변경된다!

이로서 칸반board의 기초기능은 모두 구현이 되었다!

(참조: https://github.com/atlassian/react-beautiful-dnd/blob/HEAD/docs/api/droppable.md)

ps.
오늘 계절학기 기말고사를 봤다. 10시 시험이었는데 일찍일어나서 아침먹고 피곤해서 9시10분에 30분만 자고 일어나야지 했다가 10시15분에 일어났다. ㄹㅇ 큰일날뻔했다,,, 20마넌 날라갈뻔 정신차리자ㅜㅜ 내일이면 끝이다!~!~!!~

그리고 내일 스키장을 가기로 했는데 친구가 감기기운이있어서 내일 다시 말해준다고한다. 못가면 담에 가지 뭐ㅎ

오늘은 영어 데일리미션도 잘 수행했다! 앞으로이 이렇게 하자!!

칸반보드도 슬슬 끝나가고 TS도 조금씩 익숙해지고 이거 끝나면 애니메이션과 넷플릭스 클론 코딩은 후다다닥 끝내보도록 노력해야겠다
팀플한다는 마인드로! 이제 계절학기도 끝났겠다 마음편히 밤샐 수 있다
각잡고 그냥 달려보자

여기까지 끝나면 winter에다가 배운 기능을 모두 추가해볼 것이다. 점점 공부하면서 내가 TS로 변경한것을 개같이 했구나가 느껴진다...
파이썬 챌린지도 꼬박꼬박 잘 하고!!
1월까지 react JS + python 빡시게 공부하자 남는건 지식이다!! 그리고 2월에는 진짜 영어 : 코딩 = 80 : 20 으로 하루웬종일 영어로 잠꼬대 할때까지 영어만 해보자
평생 미뤄왔던 숙제다 한 번은 넘어야지! 안된다면 간절함이 부족한 것이다 더 하자!


내일은 오늘보다 더 나은 내가 되기를

profile
과정을 적는 곳

0개의 댓글