티스토리 뷰

반응형

서울 침수 알림은 하나의 ‘서울 침수 지도 API’만 연결해서 만드는 서비스가 아닙니다. 서울시 강우량·하천 수위, 과거 침수 공간 자료, 기상청 예보, 사용자 관심 지역을 조합해야 실용적인 알림을 구현할 수 있습니다. 이 글에서는 Node.js를 기준으로 데이터 수집, GeoJSON 지도, 위험도 계산, Web Push, 중복 발송 방지까지 단계별로 설명합니다. 공공데이터의 명칭과 응답 구조는 변경될 수 있으므로 배포 전에는 반드시 각 포털의 최신 명세를 확인해야 합니다.

이 글에서 ‘서울 침수 지도 API’는 서울 열린데이터광장·공공데이터포털·기상청 API와 공간 파일을 조합하는 구현 방식을 의미합니다.

 

서울 침수 지도에 필요한 공공데이터 API

장마철 침수 위험 알림 앱을 만들려면 공간·관측·예보 데이터를 구분해야 합니다. 과거 침수 흔적은 기본 공간 위험도, 실시간 강우량과 하천 수위는 현재 위험도, 단기예보는 향후 위험도를 판단하는 자료입니다.

데이터 유형 포털 내 대표 검색명·키워드 주요 용도 갱신 특성
서울시 강우 관측 서울시 강우량, 강우량계 정보 현재 강우 강도와 누적량 계산 수분 단위 가능
하천 관측 서울시 하천 수위, 수위 관측소 하천 인접 지역 위험 보정 관측소별 상이
침수 공간 자료 침수 흔적도, 침수 취약지역, 침수 이력 위험 구역과 사용자 위치 비교 비정기 갱신 가능
기상 예보 기상청 단기예보조회서비스, 초단기예보 향후 강수량과 위험 상승 예측 발표 주기별
공식 특보·통제 기상특보, 도로 통제, 재난안전정보 자체 분석보다 우선할 공식 정보 상황 발생 시

먼저 서울 열린데이터광장에서 강우량과 하천 수위를 검색하고, 전국 단위 자료와 기상청 서비스는 공공데이터포털에서 확인합니다. 데이터셋 이름은 개편될 수 있으므로 위 표는 고정된 상품명이 아니라 포털 검색에 사용할 대표 키워드로 이해하는 것이 안전합니다.

공간 자료가 Open API가 아니라 SHP·CSV 파일로 제공되는 경우도 있습니다. 이때 파일을 GeoJSON으로 변환해 자체 서버나 객체 저장소에서 제공하면 공공데이터 API 침수 지도를 안정적으로 구성할 수 있습니다.

서울 공공 API 키 발급과 데이터 점검

공공 API를 선택한 뒤에는 인증키, 요청 URL, 호출 제한, 응답 필드를 확인합니다. 같은 기관의 데이터라도 JSON·XML·파일 다운로드처럼 제공 형식이 다를 수 있으며, 일부 인증키는 신청 후 실제 호출이 가능해질 때까지 시간이 걸릴 수 있습니다.

배포 전에는 다음 항목을 점검해야 합니다.

  • 좌표계: WGS84 위도·경도인지 EPSG:5179, EPSG:5186 등 투영 좌표계인지 확인합니다.
  • 시간 기준: 관측 시각, 발표 시각, 서버 등록 시각을 구분합니다.
  • 강우 단위: 시간당 강우량과 일정 시간 누적 강우량을 혼동하지 않습니다.
  • 결측 상태: null, 빈 문자열, 점검 중 데이터를 0mm로 변환하지 않습니다.
  • 호출 제한: 사용자 요청마다 외부 API를 호출하지 않고 서버에서 캐시합니다.
  • 이용 조건: 출처 표시, 가공 데이터 공개, 재배포 가능 범위를 확인합니다.

관측소 이름은 변경되거나 중복될 수 있으므로 내부 데이터는 관측소 코드와 좌표를 기준으로 연결하는 편이 좋습니다. API 키는 브라우저 코드에 포함하지 말고 .env 또는 클라우드 비밀 관리 서비스에 보관해야 합니다.

서울시 강우량 API를 Node.js로 수집하기

다음 코드는 특정 데이터셋의 경로를 고정하지 않고 URL과 인증키를 환경변수로 분리한 예시입니다. 실제 요청 형식과 응답 필드명은 선택한 서울시 강우량 API 명세에 맞게 어댑터 부분만 수정합니다.

// rainfall.js
const API_KEY = process.env.SEOUL_API_KEY;
const API_URL = process.env.SEOUL_RAINFALL_API_URL;

export async function fetchRainfall() {
  if (!API_KEY || !API_URL) throw new Error('API 설정이 없습니다.');

  const url = new URL(API_URL);
  url.searchParams.set('key', API_KEY);
  url.searchParams.set('type', 'json');

  const response = await fetch(url, {
    headers: { Accept: 'application/json' },
    signal: AbortSignal.timeout(5000)
  });

  if (!response.ok) {
    throw new Error(`서울시 강우량 API 오류: ${response.status}`);
  }

  const payload = await response.json();
  const rows = payload.rows ?? payload.DATA ?? [];

  return rows.map((row) => ({
    stationId: String(row.stationId ?? row.GU_CODE ?? ''),
    stationName: row.stationName ?? row.GU_NAME ?? '알 수 없음',
    measuredAt: row.measuredAt ?? row.RAIN_DT ?? null,
    rainfallMm: Number(row.rainfallMm ?? row.RAIN_VALUE)
  })).filter((row) => Number.isFinite(row.rainfallMm));
}

서울시 강우량 API Node.js 연동에서 중요한 부분은 실패 상태를 ‘비가 오지 않음’으로 처리하지 않는 것입니다. 마지막 정상 응답을 Redis나 데이터베이스에 보관하고 fetchedAt, sourceUpdatedAt, isStale 메타데이터를 함께 반환하세요. 일정 시간 이상 갱신되지 않았다면 normal이 아니라 unknown 또는 ‘데이터 지연’ 상태로 표시해야 알림 누락을 줄일 수 있습니다.

GeoJSON으로 서울 침수 지도를 표시하는 방법

침수 흔적도나 취약지역 자료가 SHP로 제공되면 QGIS 또는 GDAL의 ogr2ogr로 GeoJSON을 생성할 수 있습니다. 웹 지도에서는 일반적으로 EPSG:4326 좌표를 사용하지만, 원본 좌표계는 파일 이름으로 추측하지 말고 데이터 메타데이터에서 확인해야 합니다.

# SOURCE_EPSG는 원본 데이터 메타데이터에서 확인합니다.
ogr2ogr -f GeoJSON \
  -s_srs EPSG:SOURCE_EPSG \
  -t_srs EPSG:4326 \
  seoul-flood-zones.geojson \
  source-flood-zones.shp

Leaflet에서는 변환된 파일을 다음처럼 표시할 수 있습니다.

const map = L.map('map').setView([37.5665, 126.9780], 12);

L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© OpenStreetMap contributors'
}).addTo(map);

const zones = await fetch('/data/seoul-flood-zones.geojson')
  .then((response) => response.json());

L.geoJSON(zones, {
  style: (feature) => ({
    color: '#7f1d1d',
    fillColor: feature.properties.risk === 'high' ? '#dc2626' : '#f59e0b',
    fillOpacity: 0.4,
    weight: 2,
    dashArray: feature.properties.risk === 'high' ? null : '6 4'
  })
}).addTo(map);

서울 침수 구역 GeoJSON 지도 예시: 과거 침수 이력과 실시간 강우 위험을 색상과 패턴으로 구분

과거 침수 이력, 실시간 강우 위험, 예측 위험은 서로 다른 레이어와 범례로 구분해야 합니다. 빨강과 노랑만으로 상태를 표현하면 색각 이상 사용자가 구분하기 어려우므로 아이콘, 선 모양, 해치 패턴, 텍스트 레이블을 함께 사용합니다. 이는 색상만으로 정보를 전달하지 않도록 요구하는 웹 접근성 원칙에도 부합합니다.

서울 침수 위험도 계산 기준 설계

위험도는 한 번의 강우 관측값보다 최근 누적 강우, 침수 이력, 하천 상태, 단기예보를 조합해 계산하는 편이 안정적입니다. 다만 자체 점수는 공식 기상특보나 대피 명령이 아니라 관심 지역 정보를 정리하는 참고 지표로 표시해야 합니다.

조건 예시 가중치 예시
최근 1시간 강우량이 내부 주의 기준 이상 +30
최근 3시간 누적 강우량이 내부 기준 이상 +25
관심 위치가 침수 이력 구역에 포함 +25
인근 하천 수위가 빠르게 상승 +15
향후 1시간 강한 비가 예보됨 +10
export function calculateRisk(input) {
  let score = 0;

  if (input.rain1h >= input.thresholds.rain1h) score += 30;
  if (input.rain3h >= input.thresholds.rain3h) score += 25;
  if (input.insideFloodZone) score += 25;
  if (input.riverLevelRising) score += 15;
  if (input.heavyRainForecast) score += 10;

  if (score >= 70) return { level: 'danger', score };
  if (score >= 45) return { level: 'warning', score };
  if (score >= 25) return { level: 'caution', score };
  return { level: 'normal', score };
}

표와 코드의 점수는 구현 구조를 보여 주기 위한 예시입니다. 실제 임계값은 기상청 날씨누리에 공개된 최신 기상특보 발표 기준, 서울시 재난 안내, 관측소별 데이터 특성을 출발점으로 삼아 조정하세요. 공식 기준은 개정될 수 있고 누적 시간별 조건도 다르므로 특정 수치를 코드에 영구 고정하기보다 설정 테이블로 관리하는 것이 좋습니다.

사용자 위치와 침수 구역 판정하기

사용자 좌표가 GeoJSON 폴리곤 안에 있는지는 Turf.js의 point-in-polygon 연산으로 판정할 수 있습니다. 패키지의 내보내기 방식은 버전에 따라 달라질 수 있으므로 설치한 버전의 공식 문서를 기준으로 import 구문을 확인해야 합니다.

import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
import { point } from '@turf/helpers';

export function findFloodZone(longitude, latitude, collection) {
  const location = point([longitude, latitude]);

  return collection.features.find((zone) =>
    booleanPointInPolygon(location, zone)
  ) ?? null;
}

정밀 좌표는 개인의 생활 반경을 드러낼 수 있으므로 반드시 저장해야 하는 것은 아닙니다. 사용자가 행정동, 지하철역, 학교처럼 관심 지역을 직접 선택하도록 하면 위치정보 수집 부담을 낮출 수 있습니다.

  • 앱 실행 중 필요한 시점에만 위치 권한을 요청합니다.
  • 수집 목적과 보관 기간을 화면에서 설명합니다.
  • 원본 좌표 대신 격자나 행정동 코드 저장을 검토합니다.
  • 좌표와 푸시 토큰은 접근 권한과 저장 영역을 분리합니다.
  • 구독 해지 시 위치와 알림 정보를 함께 삭제합니다.

폴리곤 내부 여부만 판정하면 경계 바로 바깥의 사용자를 놓칠 수 있습니다. 서비스 목적에 따라 300m 또는 500m 완충 구역을 설정하되, 이 거리 역시 공식 안전 기준이 아닌 서비스 정책임을 명시하세요.

침수 예보 웹 푸시 알림 구현

위험도가 올라가면 Web Push, Firebase Cloud Messaging, 문자 또는 비즈니스 메시지로 알림을 보낼 수 있습니다. 초기 웹 프로젝트라면 VAPID 기반 Web Push로 장마철 침수 위험 알림 앱 만들기의 핵심 흐름을 검증하기 좋습니다.

import webpush from 'web-push';

webpush.setVapidDetails(
  'mailto:admin@example.com',
  process.env.VAPID_PUBLIC_KEY,
  process.env.VAPID_PRIVATE_KEY
);

export function sendFloodAlert(subscription, risk) {
  const payload = JSON.stringify({
    title: `서울 침수 위험 ${risk.level}`,
    body: `${risk.areaName}의 위험도가 상승했습니다. 공식 안내를 확인하세요.`,
    url: `/map?area=${encodeURIComponent(risk.areaCode)}`
  });

  return webpush.sendNotification(subscription, payload);
}

침수 예보 웹 푸시는 관측 작업마다 발송하지 않고 normal → caution, caution → warning처럼 단계가 상승할 때 보내야 합니다. 같은 지역과 단계에는 쿨다운을 적용하고, 새로운 공식 특보나 급격한 관측값 변화가 있을 때만 재발송합니다.

정책 구현 방법
중복 방지 지역·위험 단계·발송 시각을 유일 키로 저장
해제 알림 일정 시간 낮은 단계가 유지된 뒤 1회 발송
야간 정책 높은 위험과 공식 특보만 허용하는 설정 제공
실패 처리 만료된 푸시 구독을 감지해 삭제
오프라인 확인 Service Worker에 마지막 위험 상태와 행동 요령 캐시

PWA로 구성하면 네트워크가 일시적으로 불안정해도 마지막으로 수신한 위험 단계와 대피 행동 요령을 확인할 수 있습니다. 단, 캐시된 화면에는 최종 갱신 시각을 크게 표시해 오래된 정보를 현재 상태로 오해하지 않도록 해야 합니다.

Node.js 수집 스케줄과 중복 실행 방지

서버는 강우량과 예보를 주기적으로 수집한 뒤 지역별 위험도를 계산합니다. 여러 서버 인스턴스에서 같은 스케줄이 실행되면 알림이 중복될 수 있으므로 Redis 잠금이나 데이터베이스 유일 키가 필요합니다.

import cron from 'node-cron';
import { randomUUID } from 'node:crypto';

cron.schedule('*/5 * * * *', async () => {
  const lockId = randomUUID();
  const locked = await redis.set('flood:collector:lock', lockId, {
    NX: true,
    EX: 240
  });

  if (!locked) return;

  try {
    const rainfall = await fetchRainfall();
    const forecast = await fetchForecast();
    await saveSourceData({ rainfall, forecast });

    const changes = await calculateAreaRiskChanges();
    await sendAlertsForRaisedLevels(changes);
  } catch (error) {
    logger.error({ message: error.message }, 'flood collection failed');
  } finally {
    if (await redis.get('flood:collector:lock') === lockId) {
      await redis.del('flood:collector:lock');
    }
  }
});

서울 장마 알림 서버 운영 순서도: 강우량 수집부터 위험도 비교와 푸시 발송까지 7단계

처리 순서는 자료 수집 → 원본 저장 → 최신성 검증 → 지역별 위험도 계산 → 이전 단계 비교 → 알림 발송 → 결과 기록으로 구성합니다. 로그에는 API 키, 전체 좌표, 푸시 토큰을 남기지 말고 관측소 코드와 익명화된 구독 식별자만 기록하는 편이 안전합니다.

서버리스와 상시 서버 비용·성능 비교

작은 프로토타입은 서버리스 스케줄러가 간단하지만, 짧은 주기의 수집과 대량 공간 연산에는 상시 서버가 더 예측 가능할 수 있습니다. 실제 비용은 클라우드 사업자, 호출 횟수, 데이터 전송량에 따라 달라지므로 아래 표는 상대 비교입니다.

방식 적합한 상황 장점 주의점
5~15분 서버리스 폴링 소규모 검증, 적은 구독자 유휴 비용이 적고 배포가 간단함 실행 시간 제한과 콜드 스타트 확인
상시 Node.js 서버 짧은 갱신 주기, 지속적인 계산 캐시와 연결 재사용이 쉬움 서버 운영과 장애 대응 필요
작업 큐 기반 지역·구독자가 많은 서비스 수집·계산·발송을 분리 가능 큐 중복 처리와 재시도 설계 필요

초기에는 한 자치구와 5~15분 주기로 검증한 뒤 실제 API 갱신 주기와 사용자 수에 맞춰 조정하세요. 원본 GeoJSON은 매번 읽지 말고 메모리나 공간 데이터베이스에 적재하며, 큰 폴리곤은 단순화하거나 공간 인덱스를 적용하면 판정 시간을 줄일 수 있습니다.

서울 침수 알림 운영 시 주의사항

가장 중요한 원칙은 데이터의 의미를 과장하지 않는 것입니다. 과거 침수 이력 지역에 포함됐다고 현재 침수가 발생한 것은 아니며, 자체 위험 점수가 공식 대피 명령을 의미하지도 않습니다.

화면과 알림에서는 다음 표현을 구분하세요.

  • 침수 이력 지역: 과거 공간 자료를 기반으로 표시한 구역
  • 강우 주의: 관측값이 서비스 내부 기준을 넘은 상태
  • 위험 상승: 여러 데이터를 조합한 자체 참고 지표
  • 공식 특보: 기상청 또는 행정기관이 발표한 정보
  • 통제 정보: 관리기관이 공개한 도로·지하차도 통제 상태

모든 화면에는 데이터 출처와 최종 갱신 시각을 표시해야 합니다. 지하차도, 하천변, 반지하 주택은 위험 요인이 다르므로 하나의 점수만 적용하기보다 시설 유형별 행동 요령을 분리하는 것이 좋습니다. 배포 전에는 API 실패, 빈 응답, 좌표계 오류, 중복 스케줄, 푸시 만료, 단계 해제, 데이터 급변 시나리오를 테스트하세요.

자주 묻는 질문

Q. 무료로 서울 침수 관련 공공 API를 사용할 수 있나요?

많은 공공데이터는 무료로 제공되지만 인증키 신청, 일일 호출 제한, 출처 표시 조건이 있을 수 있습니다. 선택한 데이터의 상세 페이지에서 최신 이용 조건을 확인해야 합니다.

Q. 침수 흔적도만으로 실시간 알림을 만들 수 있나요?

어렵습니다. 침수 흔적도는 과거 위험을 보여 주는 공간 자료이므로 실시간 강우량, 예보, 하천 수위, 공식 통제 정보를 함께 사용해야 합니다.

Q. 알림 강우량 기준은 몇 mm로 설정해야 하나요?

지역의 배수 능력과 누적 강우, 지형에 따라 위험이 달라 하나의 수치를 모든 지역에 적용하기 어렵습니다. 최신 기상특보 발표 기준과 서울시 안내를 우선 적용하고 자체 기준은 보조 지표로 운영하세요.

Q. 카카오톡으로도 침수 알림을 보낼 수 있나요?

가능하지만 비즈니스 채널, 템플릿 심사, 비용 등 별도 요건이 있습니다. 먼저 Web Push나 앱 푸시로 위험도 계산과 중복 방지 정책을 검증하는 것이 효율적입니다.

Q. 사용자 위치를 반드시 서버에 저장해야 하나요?

아닙니다. 사용자가 관심 행정동이나 역을 선택하게 하면 정밀 좌표 없이 알림을 제공할 수 있습니다. 좌표가 필요하다면 최소 수집과 짧은 보관 기간을 적용하세요.

핵심 요약

  • 서울 침수 지도 API는 하나의 API가 아니라 공간·강우·예보 데이터를 조합하는 방식입니다.
  • 서울 열린데이터광장에서는 강우량·하천 수위·침수 이력 관련 검색어로 데이터를 찾습니다.
  • 공간 파일은 원본 좌표계를 확인한 뒤 EPSG:4326 GeoJSON으로 변환합니다.
  • 자체 위험 점수는 공식 특보를 대체하지 않는 참고 정보로 표시합니다.
  • 위험 단계가 상승할 때만 알림을 보내고 Redis 잠금과 유일 키로 중복을 방지합니다.
  • 데이터 지연을 안전 상태로 처리하지 말고 unknown 상태로 구분합니다.
  • 지도에는 색상 외에 패턴, 아이콘, 텍스트를 사용해 접근성을 확보합니다.
  • 위치정보, API 키, 푸시 토큰은 최소 수집·분리 저장·로그 마스킹 원칙을 적용합니다.

마치며

서울 공공 API로 장마 침수 알림을 만들 때의 핵심은 과거 침수 지도와 실시간 관측값을 구분하면서 하나의 위험 판단 흐름으로 연결하는 것입니다. 먼저 한 자치구를 대상으로 API 키 발급, 강우량 수집, GeoJSON 표시, 단계 상승 알림까지 작은 프로토타입을 완성하세요. 이후 하천 수위와 공식 통제 정보를 추가하고, 이 글의 운영 체크리스트를 기준으로 장애와 중복 발송을 검증하면 됩니다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함
반응형