useEffect를 다시 한번
살펴보기 전에
localStorage 예시를 이어가 보겠습니다
아이템을 추가할 때 저장만 하는 것은
사실 충분하지 않습니다
localStorage 역시 업데이트되어야 하는데
여기 이렇게
클릭하고 확인해서 장소를 삭제할 때 필요하고
앱이 시작할 때 localStorage로부터
데이터가 로딩되어서
저장한 아이템으로 이 상자가 채워져야 합니다
그럼 먼저 localStorage에서 아이템을 삭제할 때
사용될 코드부터 시작해 보겠습니다
이를 위해서는 먼저 storedIds를 가지고 와서
이를 업데이트해야 합니다
그리고 나서 저장된 아이템을 다시 업데이트하는데
localStorage.setItem을 입력하고
이 키(key)의 도움을 받아 업데이트하면 됩니다
업데이트된 배열은
이미 존재하는 데이터 배열을 가져와 그것을 기반으로 한 것입니다
여기에 다시 stringify 배열을 생성해 주어서
이 배열이 filter 메소드의 도움을 받아
storedIds 배열을 기반으로
저장되도록 할 것인데
filter 메소드는 브라우저에 내장되어서
우리가 새로운 배열을 만들 수 있도록 해 주는데
이 배열을 기반으로 필터링 조건을 더해주는 식입니다
이것을 위해 필터는
이 배열의 모든 아이템에 실행되는 함수를 동반하는데
모든 아이템을 이 함수에 입력값으로 받습니다
해당 아이템을 유지하고 싶을 경우
True 라고 반환을 하고
폐기할 경우에는 False 라고 반환을 해 주면 됩니다
여기에서는 단순하게
여기 보여지는 이 ID가
selectedPlace.current와 불일치하는지를 확인하는데
이것이 뭐냐면 여기에서 클릭해 첫 번째 상자에 들어간
장소의 ID입니다
만약 이 두 개의 ID가 일치하지 않는다면
제가 삭제하고자 하는 항목이 아니라는 것을 알 수 있고
True 라고 반환해서 항목을 그대로 유지할 수 있습니다
만약 이 두 개의 ID가 일치한다면
이 조건은 False 라는 결과를 출력해서
해당 ID는 이 배열로부터 지워지게 될 것입니다
그리고 이것이 이 ID를 더이상 포함하지 않는
업데이트된 배열을 저장하는 방법입니다
마지막으로 필요한 것은
앱이 시작했을 때 이 항목을 가져오는 것입니다
여기에서 여러분은
useEffect가 필요하다고 생각하실 수 있는데
물론 지금 useEffect를 사용하는 것도 가능합니다
여기 navigator.geolocation 에서 썼던 것처럼 말입니다
기존의 useEffect 사용처에 코드를 병합하거나
다시 사용할 수도 있습니다
모든 리액트 훅과 마찬가지로
useEffect 또한 원하시는 만큼, 필요한 만큼 사용하셔도 됩니다
그렇기에 여러분은
장소를 불러오기 위한 코드가
여기 useEffect 안에 들어가야 한다고 생각하실 수 있습니다
장소를 로딩하는 것을
여기 아래와 같이 할 수 있고
이렇게 localStorage.getItem 을 넣어도 됩니다
이것은 단지 ID일 뿐이지만
저는 제목과 이미지처럼 그 장소와 관련된 모든 데이터를
함께 불러오고 싶기 때문에
이 ID들을 실질적인 place 객체로 매핑해야 합니다
이것은 map 메소드를 사용해서
모든 ID를 완전한 place 객체로 변환해 주면 됩니다
여기에 있는 AVAILABLE_PLACES 배열을 보면
data.js 파일에 있고 또 그 JS 파일 안에는
이렇게 place 객체가 들어있습니다
제가 ID에 매핑하고 싶은 것이 바로 이런 객체인데
매핑을 하는 방법은
여기 AVAILABLE_PLACES를 이용해서
해당 ID를 위한 장소, 즉 place를 찾아주는 것입니다
여기에서 find 또한 또하나의 함수를 필요로 하고
여기에는 자동적으로 AVAILABLE_PLACES에서 가져온
모든 장소가 전달되게 됩니다
그리고 여기에서 저는 ID가
이 ID와 동일한 장소를 찾아 보겠습니다
그러면 여기에 있는 이 코드는 우리에게
localStorage에서 가져온 이 ID 배열을 기반으로 한
place 객체 배열을 줄 것입니다
이제 setPickedPlaces를 호출해서
storedPlaces와 일치하도록 설정해 줍니다
아래쪽에 했던 것과 같은
빈 의존성 배열을 추가하게 되면
여기 있는 effect 함수는
앱 컴포넌트 함수의 구동 이후에 단 한번 실행됩니다
그러므로 무한 루프에 빠져들지 않을테고
데이터를 가져와서 그 데이터로
위의 상자를 채워줄 것입니다
그렇게 하면 됩니다
이제 저장을 하고 다시 로딩하면 보시는 대로
앞서 추가한 장소들로 시작합니다
그리고 만약 이렇게 장소를 하나 지우고 다시 로딩하면
지운 장소만 사라져 있고 나머지 장소들은 그대로 불러와집니다
확실히 작동하는 것이 확인되었습니다
그렇지만 이것은
불필요한 useEffect의 예시입니다
어째서 이렇게 useEffect를 사용하는 것이
불필요하고 추천드리지 않는 방식일까요?
그것은 여기 이 코드는
localStorage를 사용하고 있는 반면
사용자의 위치를 찾는 데에 사용한 이 코드는
동시에 작동하기 때문이고
즉시 완료되기 때문입니다
한 줄 씩 실행이 되고
해당 라인이 실행을 마치면, 모두 완료된 것이고
최종 결과가 나왔다고 볼 수 있습니다
여기 있는 getCurrentPosition 과는 다른 상황입니다
이 줄이 실행되었지만, 그것은 모두 완료된 것이 아니라
여기에 있는 callback 함수가 작동해야 하는데
그것은 브라우저에 의해 실행되는 데다
시간이 지난 이후에나 일어나는 일이었기 때문입니다
심지어 다시 로딩을 해도 시간이 좀 걸리는 것을 볼 수 있는데
해당 장소를 즉시 불러오지 못하기 때문입니다
그렇지만 localStorage의 경우에는 다릅니다
콜백(callback) 함수나 그 외의 어떠한 것도
여기에는 없습니다
대신 이러한 데이터는 즉시 불러오는 것이 가능합니다
그러므로 이 앱 컴포넌트 함수는
해당 데이터를 가져오는 것이 끝날 때까지
실행을 마칠 수가 없는 것입니다
그렇기 때문에 useEffect를 그냥 없애버리고
여기에 있는 상태 업데이트 함수도
무한 루프에 빠지게 하는 것과 같은
문제가 생길 수 있으니 없애겠습니다
그리고 이 코드를 잘라내서
여기 상태 초기화 코드 앞으로 옮겨와서
즉각적으로 사용이 가능한 storedPlaces를 이용해
선택한 장소의 상태를 초기화하는 데에 사용합니다
storedPlaces를 상태 초기값으로 사용하기 위함입니다
이 코드는 동시에 작동하고
앱 컴포넌트 함수의 실행이 끝나기를 기다릴 필요도 없으니
작동이 모두 끝나고 완료되기까지
시간이 소요되지도 않습니다
여기에서는 경우가 다릅니다
이 코드를 아예
앱 컴포넌트 함수의 밖으로 이동시켜서
이 코드 파일이 처음으로 분석되고 실행되는 때에
앱의 전체 사이클에서
단 한 번의 시점에만 작동하도록
할 수도 있습니다
그리고 이것을
앱 컴포넌트에 넣어야 할 이유가 없습니다
결국 앱 컴포넌트 함수가 실행될 때마다
작동하게 되는 것이므로
이것은 퍼포먼스의 낭비일 뿐인 것입니다
앱 전체가 시작할 때
한 번만 작동하는 것으로도 충분하고
컴포넌트 함수의 storedPlaces를
여전히 사용할 수 있습니다
이 컴포넌트 함수의 실행은
그 앞에 있는 코드가 실행된 이후에 이루어지기 때문입니다
그렇기에 코드를 이렇게 수정해 주고
저장한 뒤 다시 로딩하면
여전히 장소를 불러오게 됩니다
모두 잘 작동하고 우리가 원하는 대로 다룰 수 있으며
장소를 삭제하는 것도 물론
모두 잘 되는 것을 볼 수 있습니다
useEffect를 필요로 하지 않는 부수 효과를 이용해서 가능했습니다