본문 바로가기
LG CNS AM INSPIRE CAMP

<LG CNS 5기> 11일차 TIL : Context API 전역 상태 관리 및 Open API(날씨·카카오맵) 연동 기법

by hihijh826 2026. 6. 2.
728x90
반응형
SMALL

학습일자 : 2026.06.02

 

.

🌐 Context API 

🏗️ 1. 전역 데이터 통로 개설 (src/utils/context/context.js)

리액트 엔진에 전역 상태가 지나다닐 전용 파이프라인을 구축하는 단계

// src/utils/context/context.js
import { createContext } from 'react';

// 💡 전역 데이터를 담을 빈 통로(Context) 생성
const ctx = createContext(null);

export default ctx;

 

🏢 2. 전역 데이터 공급자 배치 (ContextApp.js)

상태(isMode)를 만들고, 1단계에서 만든 통로의 입구(ctx.Provider)를 열어 데이터를 하위 컴포넌트들에게 방출

// ContextApp.js
import { useState } from "react";
import ctx from "./src/utils/context/context"; // 1단계 통로 불러오기
import ContextPage from "./src/pages/ContextPage"; // 아래에 조립할 중간 페이지 컴포넌트

const ContextApp = () => {
    // // isMode state 값을 Context API 이용해서 Page, Body, Footer, Header 전달해 본다면?
    const [isMode, setIsMode] = useState(false); // false: 다크모드 / true: 라이트모드

    return (
        // 💡 Provider의 value 속성에 하위 컴포넌트들에게 공유할 상태와 변경 함수를 집어넣습니다.
        <ctx.Provider value={{ isMode, setIsMode }}>
            {/* 데이터 공급 범위 안에 쏙 집어넣기 */}
            <ContextPage />
        </ctx.Provider>
    );
};

export default ContextApp;

 

징검다리 컴포넌트 조립 (src/pages/ContextPage.jsx)

 중간 다리 역할을 하는 이 페이지는 그 어떤 Props도 받거나 전달하지 않고 컴포넌트들을 조립만 하게 됨 . (Props Drilling 해결)

// src/pages/ContextPage.jsx
import React from 'react';
import ContextHeader from '../components/context/ContextHeader'; // 가정
import ContextBody from '../components/context/ContextBody';
import ContextFooter from '../components/context/ContextFooter'; // 가정

const ContextPage = () => {
    return (
        <div>
            {/* 💡 중간 컴포넌트들이 부모의 상태를 Props로 토스해 줄 필요가 전혀 없음! */}
            <ContextHeader />
            <ContextBody />
            <ContextFooter />
        </div>
    );
};

export default ContextPage;

 

🛍️ 3. 필요한 곳에서 데이터 꺼내 쓰기 (src/components/context/ContextBody.jsx)

useContext 훅을 사용해 중간 컴포넌트들을 다 건너뛰고, 최상단 부모의 isMode 값을 다이렉트로 빨아들여 화면 스타일에 실시간 적용

// src/components/context/ContextBody.jsx
import { useContext } from 'react';
import ctx from '../../utils/context/context'; // 1단계에서 만든 통로 불러오기

const ContextBody = () => {
    // 💡 useContext를 통해 부모(Provider)가 밀어 넣어준 전역 상태를 쏙 뽑아옵니다.
    const { isMode, setIsMode } = useContext(ctx);

    return (
        <header style={{
            // isMode 값에 따라 배경색과 글자색이 실시간으로 스위칭됩니다!
            backgroundColor : isMode ? '#ffffff' : '#121212',
            color : isMode ? '#000000' : '#ffffff',
            padding: '40px',
            transition: 'all 0.3s ease' // 부드러운 모드 전환 효과
        }}>
            <h1>오늘도 화이팅입니다.</h1>
            <p>현재 상태: {isMode ? "🌞 라이트 모드" : "🌙 다크 모드"}</p>

            {/* 💡 전역 변경 함수(setIsMode)도 받아왔기 때문에 하위 컴포넌트에서 모드를 직접 끌 수 있음 */}
            <button onClick={() => setIsMode(!isMode)}>
                모드 변경하기
            </button>
        </header>
    );
};

export default ContextBody;

💡 흐름 핵심 요약

  1. context.js : 전역 데이터가 흐를 파이프라인(ctx)을 하나 파둔다.
  2. ContextApp.js : 파이프라인 입구(ctx.Provider)를 선언하고 데이터(isMode)를 흘려보내며 사용할 하위 컴포넌트들을 감싼다.
  3. ContextBody.jsx : 중간 컴포넌트들 방해 없이 useContext(ctx) 밸브를 열어 필요한 데이터만 쏙 꺼내서 UI를 변화시킨다.

 

📍OpenAPI 활용

 

💻 1. Fetch API를 활용한 날씨 데이터 요청 (getCityWeather)

비동기 함수(async/await)와 fetch를 사용해 OpenWeatherMap 서버에 데이터를 요청하고, 받아온 JSON 데이터를 상태(weather)에 저장

const getCityWeather = async (city) => {
  const apikey = 'apikey번호'; // 💡 실무에서는 .env에 보관하는 것이 정석
  let endpoint = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apikey}`;

  try {
      // 1. 서버에 날씨 정보 요청 (HTTP GET)
      const response = await fetch(endpoint);

      // 2. 받아온 Response Stream을 자바스크립트 객체(JSON) 형태로 파싱
      const data = await response.json();

      // 3. 변환된 데이터를 weather state에 저장하여 리렌더링 트리거!
      setWeather(data);
  } catch (error) {
      console.error("날씨 정보를 가져오는 데 실패했습니다.", error);
  }
};

🎨 2. 받아온 데이터 화면에 써먹기 (UI & 인터랙션)

setWeather(data)에 의해 상태가 업데이트되면 리액트가 화면을 다시 그리면서 데이터를 도미노식으로 활용한다.

  • 자식 컴포넌트로 전달 (Props): <WeatherBox weather={weather} />를 통해 하위 컴포넌트에서 온도, 습도, 날씨 아이콘 등을 동적으로 렌더링한다.
  • 동적 배경화면 변경: weather?.weather?.[0]?.main 값을 옵셔널 체이닝으로 안전하게 추출하여, 날씨 상태(Clear, Rain, Clouds 등)에 따라 CSS 클래스명을 바꿔 배경 이미지를 실시간 전환한다.
  • 외부 맵 라이브러리 연동: 응답 데이터에 포함된 좌표값(weather.coord.lat, weather.coord.lon)을 추출하여 지도 위에 마커를 찍고 중심 좌표를 이동시킨다.

🎯 3. 여기서 useRef 개념이 왜 필요할까? (카카오맵 연동의 핵심)

날씨 데이터를 받아와 카카오맵을 그릴 때, 리액트의 일반적인 선언형 방식(state)만으로는 해결할 수 없는 영역이 존재한다. 바로 "진짜 브라우저 DOM 객체에 직접 접근해야 할 때"이다.

 

① useRef 란 무엇인가?

  • 리액트 컴포넌트 내부에서 특정 DOM 요소를 직접 선택하거나, 리렌더링 없이 유지되는 변수를 관리할 때 사용하는 훅(Hook)이다.
  • 카카오맵 API 같은 외부 자바스크립트 라이브러리는 리액트의 가상 DOM(Virtual DOM)을 모르기 때문에, "지도를 그려 넣을 진짜 HTML <div> 상자의 실제 주소"를 요구한다.

② 적용 코드

import React, { useEffect, useRef, useState } from 'react';

const WeatherMapPage = () => {
    const [weather, setWeather] = useState(null);

    // 💡 1. 진짜 DOM 상자를 가리킬 빈 포인터(Ref)를 생성한다.
    const mapContainerRef = useRef(null);
    const mapInstanceRef = useRef(null); // 지도의 인스턴스를 리렌더링 없이 기억할 금고

    useEffect(() => {
        if (!weather) return;

        const { lat, lon } = weather.coord;

        // 💡 2. mapContainerRef.current가 바로 "진짜 HTML div 엘리먼트"를 뜻함!
        if (!mapInstanceRef.current) {
            // 최초 1회 지도 객체 생성
            const options = {
                center: new kakao.maps.LatLng(lat, lon),
                level: 3
            };
            mapInstanceRef.current = new kakao.maps.Map(mapContainerRef.current, options);
        } else {
            // 이미 지도가 존재한다면 날씨 좌표가 바뀔 때 중심점만 슥 이동 (useRef 덕분에 가능)
            const moveLatLon = new kakao.maps.LatLng(lat, lon);
            mapInstanceRef.current.setCenter(moveLatLon);
        }
    }, [weather]); // 날씨 데이터가 변경될 때마다 실행

    return (
        <div>
            {/* 💡 3. ref 속성을 통해 이 div 태그의 실제 DOM 주소를 mapContainerRef에 배달해준다. */}
            <div ref={mapContainerRef} style={{ width: '100%', height: '400px' }} />
        </div>
    );
};

💡 4. 핵심 트러블 슈팅: useState vs useRef 결정적 차이

  • useState는 화면의 갱신을 담당한다. 값이 바뀌면 컴포넌트가 리렌더링되면서 JSX가 새로 그려진다. 날씨 데이터(weather)나 도시 이름(city)처럼 바뀌었을 때 화면이 즉시 변해야 하는 데이터에 쓴다.
  • useRef는 화면 뒤의 보관함이다. .current 안에 값을 아무리 바꾸어도 컴포넌트는 절대 리렌더링되지 않는다. 카카오맵 지도 객체(map)처럼 컴포넌트가 리렌더링된다고 해서 지도를 처음부터 다시 만들면 화면이 껌벅거리므로, 화면 깜빡임 없이 지도 객체만 메모리에 온전히 유지하고 싶을 때 반드시 사용해야 한다.

 

 

오늘의 회고:Context API 파이프라인 설계를 통해 Props Drilling을 방지하기 위해 우회하여 최하단 컴포넌트까지 다이렉트로 전역 상태를 방출하는 아키텍처를 이해했으며, 오픈 날씨 API 데이터를 비동기 fetch로 처리하고 useRef로 붙잡아둔 실제 DOM 영역에 카카오맵 라이브러리 인스턴스를 리렌더링 깜빡임 없이 유기적으로 동기화하는 실무형 앱 인터랙션 제어 능력을 체득한 것 같다.

728x90
반응형
LIST