[Airbnb Project] 카카오맵에서 사용한 useRef

Jihyun-Jeon·2022년 7월 10일
1

React

목록 보기
13/26

참고자료

useRef의 기능 1. 특정 dom 요소를 지정

import React, { useRef } from 'react';

const MapRender = ({ show, setShow }) => {
  // 1. useRef()를 사용하여 ref객체를 만듦
  const mapContainer = useRef();
  
   // 지도를 생성한다
  useEffect(() => {
   // 3. ref.current는 이제 div를 가르키게됨.
 	let map = new kakao.maps.Map(mapContainer.current, mapOption);
  }
            
   return ( // 2. 선택하고 싶은 dom요소에 ref값을 설정해줌.
    <div id="map" className="map" ref={mapContainer} style={style}>
  )
 };

useRef의 기능 2. 리렌더링 되도 바뀌지 않은 변수는 값을 사용할 때

🔆 문제

지도를 렌더한 후, toggle버튼을 누르면 지도의 사이즈가 커진 후 다시 map인스턴스 객체에 있는 relayout함수를 호출해야 했다.
그러나 지도의 사이즈를 변경하는 코드는 MapRender컴포넌트의 스코프에서 실행되는데, 지도를 relayout하는 메서드는 map 인스턴스 객체에 있기 때문에 스코프가 달라서 map객체에 접근이 불가능 했다.

🔆 시도1 - 변수 활용

따라서 new map을 MapRender컴포넌트의 변수에 두어 접근하기로 했다.

그러나 tooggle버튼을 누르면 state가 변경되어 지도 사이즈가 변경되어 MapRender컴포넌트가 리렌더링 되는데,
컴포넌트가 리렌더링 될 때마다 변수가 새로 초기화되서 이전값이 유지되지 않음.

const MapRender = ({ show, setShow }) => { 
   const [style, setStyle] = useState({
    width: '100%',
    height: 'calc(100vh - 100px)',
  });
  // onClickToggle함수 코드 생략... (style의 값이 바뀐다.)
  
  let map = null;
  console.log(map); // ✔️ 2. 리렌더링 되면 계속 null로 나오게 됨.
  
   useEffect(() => { // 1. 지도를 생성한다
    map = new kakao.maps.Map(mapContainer.current, mapOption);
   },[])
                
   useEffect(() => {
      map.relayout(); 
     // 3. style이 바뀔 때마다 MapRender 컴포넌트는 리렌더링 되는데
     // 그때마다 let map은 null로 초기화 된다.
     // 따라서 map 인스턴스 객체가 유지되지 않음
    }, [style]);
  
  return (
    <div id="map" className="map" ref={mapContainer} style={style}>
      <button onClick={onClickToggle}> 토글버튼 </button>
	</div>
     )
}

🔆 시도2 - useRef활용

useRef를 통해 컴포넌트가 리렌더링되도 new map은 변하지 않는 값으로 만들어 사용하기로 했다.

const MapRender = ({ show, setShow }) => { 
   const [style, setStyle] = useState({
    width: '100%',
    height: 'calc(100vh - 100px)',
  });
  // onClickToggle함수 코드 생략... (style의 값이 바뀐다.)
  
  const map = useRef(); // 1. useRef를 사용
  
   useEffect(() => {   // 지도를 생성한다
     // 2. map.current에 kakao Map인스턴스 객체를 넣음
    map.current = new kakao.maps.Map(mapContainer.current, mapOption);
   },[])  
             
	useEffect(() => {
      map.current.relayout(); 
     // 3. Map컴포넌트의 스코프에 kakao Map인스턴스 객체가 있기 때문에  
     // map 인스턴스 객체에 접근하여, relayout메서드를 실행할 수 있다.
    }, [style]);
   
  return (
    <div id="map" className="map" ref={mapContainer} style={style}>
      <button onClick={onClickToggle}> 토글버튼 </button>
	</div>
     )
}

정리

컴포넌트의 state가 업데이트되서 재실행되면, 별도의 스코프가 생성되서 변수가 계속 초기화됨.
그러나 useRef를 사용하면, 별도의 공간이 만들어져서 여러 스코프에서 한 useRef를 참조할 수 있다.
따라서 new map 인스턴스를 변수에 담는게 아니라, useRef.current에 담는것임!

0개의 댓글