본문 바로가기

React

React + Openlayers 구성 ( By React Context )

반응형

리액트로 Openlayers 라이브러리를 활용한 개발을 하려고하면 한번 생성한 ol/Map 객체에 대해 여러 컴포넌트에서 사용할 때 어떻게 처리해야할지 많은 고민을 하게 된다. (jsp 시절에 개발할 땐 위에 한번 선언만 해주면 주구장창 다른데서 사용했었다....^^)

리액트에서 맵 객체를 사용하는 모든 컴포넌트에 프로퍼티(props)로 매번 전달하기에는 너무 벅차다.

그래서 필자는 상태관리 라이브러리 중 리덕스에 맵 객체를 넣어서 사용했었지만 리덕스의 러닝커브를 감당하지 못하고 리코일로 넘어가버리는 바람에 맵 객체를 리코일 state에 넣으려고 시도했으나,,, 리코일에는 맵 객체가 들어가지를 않았다. ㅠㅠ ( 아마 개인적인 생각엔 리코일 객체로 들어가면서 read only 객체로 객체 내부에서의 변경도 허용하지 않아서 그런것 같다! ) 

고로 나는 다른 상태관리가 필요했고, 따로 라이브러리를 설치하지 않고도 사용할 수 있는 Context 를 통해 구성하였다. 현재 실무에서는 Openlayers 관련 객체만 Context 에 저장하고 다른 상태관리는 전부 Recoil 로 하고 있다.

 

그래서 일단 기본적인 React + Openlayers + Context 의 구성을 해보려고 한다.

 

기본 패키지 구조는 이렇다. 먼저 확인하고 넘어가자.

 

 

먼저 CRA를 통해 프로젝트를 구성한다.

npx create-react-app react-context-openlayer
cd react-context-openlayer

 

프로젝트에서 사용할 Openlayers 라이브러리를 설치한다.

yarn add ol

 

이 후 MapContext 를 생성한다.

// ./Map/MapContext.js
import React from "react";
const MapContext = new React.createContext({});
export default MapContext;

일종의 저장소를 생성했다고 보면 된다.

 

Provider 를 만든다. 이 때, Openlayers 객체를 통해 초기에 셋팅해야 할 것들을 해주고 저장을 원하는 객체 (Map) 를 Provider에 저장한다.

import React, { useState, useEffect } from 'react';
import MapContext from './MapContext';
import 'ol/ol.css';
import { Map as OlMap, View } from 'ol';
import { defaults as defaultControls } from 'ol/control';
import { fromLonLat, get as getProjection } from 'ol/proj';
import { Tile as TileLayer } from 'ol/layer';
import { OSM } from 'ol/source';

const Map = ({ children }) => {
    const [mapObj, setMapObj] = useState({});

    useEffect(() => {
    	// Map 객체 생성 및 OSM 배경지도 추가
        const map = new OlMap({
            controls: defaultControls({ zoom: false, rotate: false }).extend([]),
            layers: [
                new TileLayer({
                    source: new OSM(),
                })
            ],
            target: 'map', // 하위 요소 중 id 가 map 인 element가 있어야함.
            view: new View({
                projection: getProjection('EPSG:3857'),
                center: fromLonLat([127.9745613, 37.3236563], getProjection('EPSG:3857')),
                zoom: 15,
            }),
        });

        setMapObj({ map });
        return () => map.setTarget(undefined);
    }, []);

	// MapContext.Provider 에 객체 저장
    return <MapContext.Provider value={mapObj}>{children}</MapContext.Provider>;
};

export default Map;

 

위의 Provider 를 import 시 경로만으로 가져오기 위한 작업을 해준다. ( 경로/index.js )

// index.js
export { default } from "./Map"

 

이 후 Provider 에서 저장한 객체를 사용하기 위해서는 Context.Provider 로 감싸져 있는 자식요소에서 사용 가능하다. ( 필자는 테스트 편의상 CRA 를 통해 만들어진 프로젝트 파일 중 index.js 에서 감쌌다. )

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import Map from './Map'; // context.Provider

ReactDOM.render(
  <React.StrictMode>
      <Map>
        <App />
      </Map>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

여섯번째 줄에서 context.Provider를 갖고 오고 열번째 줄에서 App 컴포넌트를 컨텍스트로 감싼다. ( 이제 App 컴포넌트에서는 context 에저장한 객체를 꺼내 사용할 수 있게 되었다. )

 

 

테스트를 위해 App.js 에서 Map 객체를 가져오고 ZoomIn , ZoomOut 기능을 하는 버튼을 구성해보았다.

import { useContext } from 'react';
import MapContext from './Map/MapContext';

function App() {
    // 컨텍스트에 저장되어있는 객체를 가져옴
    const { map } = useContext(MapContext);

    const handleZoomInClick = () => {
        map.getView().setZoom(map.getView().getZoom() + 1);
    };

    const handleZoomOutClick = () => {
        map.getView().setZoom(map.getView().getZoom() - 1);
    };

    return (
        <>
            <button onClick={handleZoomInClick}>zoomIn</button>
            <button onClick={handleZoomOutClick}>zoomOut</button>
            <div id="map" style={{ width: '100%', height: 400 }}>
            </div>
        </>
    );
}

export default App;

 

이상없이 지도가 렌더링 되고, context 에 저장한 map 객체를 App 컴포넌트에서 꺼내 쓸 수 있음을 확인할 수 있다. (Map Context Provider 하위 자식 컴포넌트들은 모두 Context 객체에 접근할 수 있다.)

 

 

※ 오타 수정이나 질문은 댓글로 부탁드립니다. 블로그 내용 및 소스는 마음껏 퍼가셔도 됩니다^^

 

전체소스: https://github.com/sbjang123456/react-context-openlayer

 

sbjang123456/react-context-openlayer

react 에서 Context를 활용한 Openlayer의 Map 객체 저장. Contribute to sbjang123456/react-context-openlayer development by creating an account on GitHub.

github.com

 

반응형