OPEN API 데이터를 DB에 저장하기

henry·2024년 10월 10일

공공 데이터를 API로 가져와 데이터베이스에 저장하는 방법입니다.
여기서는 Supabase를 사용했습니다.


1. 프로젝트 설정

node-fetch와 Supabase 클라이언트가 필요합니다.

npm install node-fetch @supabase/supabase-js



2. Supabase 설정

Supabase는 클라우드 기반 데이터베이스입니다.

import { createClient } from '@supabase/supabase-js';
const SUPABASE_URL = 'https://xxxxxxxxxxxxxx.supabase.co';
const SUPABASE_ANON_KEY = 'your-servicekey';
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);


3. API 데이터 수집 구조

데이터를 가져올 API는 공공 데이터 포털에서 제공하고, 가공식품 및 음식 정보를 반환합니다.
API 호출을 통해 100개씩 데이터를 가져와서 페이지 단위로 전체 데이터를 수집합니다.

const fetchData = async (pageNo) => {
   const url = `${API_URL}?serviceKey=${SERVICE_KEY}&pageNo=${pageNo}&numOfRows=100&type=json`;
   console.log(url);
   try {
      const response = await fetch(url);
      const data = await response.json();
      if (data.response?.header?.resultCode !== '00') {
         console.error('API 요청 오류:', data.response?.header?.resultMsg || '알 수 없는 오류');
         return null;
      }
      return {
         items: data.response.body.items,
         totalCount: Number(data.response.body.totalCount),
         pageNo: Number(data.response.body.pageNo),
      };
   } catch (error) {
      console.error('데이터를 가져오는 중 오류가 발생했습니다:', error);
      return null;
   }
};

공공 데이터를 페이지 단위로 호출하여 JSON 형식으로 데이터를 반환합니다.



4. 데이터 저장 함수


Supabase에 데이터를 삽입하는 함수입니다.

const insertDataToSupabase = async (items) => {
   for (const item of items) {
      const processedItem = {
         foodcd: item.foodCd || 'N/A',
         foodnm: item.foodNm || 'N/A',
         datacd: item.dataCd || 'N/A',
         typenm: item.typeNm || 'N/A',
         foodorigincd: item.foodOriginCd || 'N/A',
		 ...
         중략
         ...
         distnm: item.distNm || 'N/A',
         imptyn: item.imptYn || 'N',
         coocd: item.cooCd || 'N/A',
         coonm: item.cooNm || 'N/A',
         dataprodcd: item.dataProdCd || 'N/A',
         dataprodnm: item.dataProdNm || 'N/A',
         crtymd: item.crtYmd || null,
         crtrymd: item.crtrYmd || null,
         insttcode: item.insttCode || 'N/A',
      };

      try {
         const { data, error } = await supabase.from('processedfood').insert([processedItem]);

         if (error) {
            console.error('데이터 저장 중 오류가 발생했습니다:', error.message);
            console.error('오류 세부 정보:', JSON.stringify(error, null, 2));
         } else {
            console.log('데이터가 성공적으로 저장되었습니다:', JSON.stringify(data, null, 2));
         }
      } catch (err) {
         console.error('삽입 중 try-catch 오류 발생:', err);
      }
   }
};

이 함수는 items 배열의 각 항목을 하나씩 데이터베이스에 삽입하고
각 필드가 일치하도록 매핑하며 오류가 발생할 경우 구체적인 오류 메시지를 출력합니다.



5. 데이터 조회 및 저장 자동화

전체 데이터는 한 번의 호출로 가져올 수 없기 때문에 반복 호출을 통해 페이지별 데이터를 조회합니다.

const main = async () => {
   let pageNo = 1;
   let fetchedData;
   do {
      console.log(`${pageNo}번째 페이지 데이터를 가져오는 중...`);
      fetchedData = await fetchData(pageNo);
      if (fetchedData && fetchedData.items && fetchedData.items.length > 0) {
         await insertDataToSupabase(fetchedData.items);
         pageNo++;
      } else {
         console.log('더 이상 가져올 데이터가 없습니다.');
         break;
      }
   } while (fetchedData !== null);
};

main().catch((error) => console.error('스크립트 실행 중 오류가 발생했습니다:', error));

이 함수는 각 페이지의 데이터를 가져오고 Supabase에 데이터를 저장한 후
데이터가 더 이상 없을 때 까지 반복합니다.


전체 소스 코드

import fetch from 'node-fetch';
import { createClient } from '@supabase/supabase-js';

// Supabase 설정
const SUPABASE_URL = 'https://supabase.co';
const SUPABASE_ANON_KEY = ''; // Supabase의 익명 키
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
// API 설정
// const API_URL = 'http://api.data.go.kr/openapi/tn_pubr_public_nutri_process_info_api'; //가공식품;
const API_URL = 'http://api.data.go.kr/openapi/tn_pubr_public_nutri_food_info_api'; //음식

const SERVICE_KEY = '';

// 데이터 가져오기 함수
async function fetchData(pageNo) {
   const url = `${API_URL}?serviceKey=${SERVICE_KEY}&pageNo=${pageNo}&numOfRows=500&type=json`;

   try {
      const response = await fetch(url);
      const data = await response.json();

      // API 응답이 정상인지 확인
      if (data.response?.header?.resultCode !== '00') {
         console.error('API 요청 오류:', data.response?.header?.resultMsg || '알 수 없는 오류');
         return null;
      }

      // 데이터가 없는 경우
      if (!data.response.body.items || data.response.body.items.length === 0) {
         console.log('가져온 데이터가 없습니다.');
         return { items: [], totalCount: 0 }; // 빈 배열과 0을 반환
      }

      return {
         items: data.response.body.items,
         totalCount: Number(data.response.body.totalCount),
         pageNo: Number(data.response.body.pageNo),
      };
   } catch (error) {
      console.error('데이터를 가져오는 중 오류가 발생했습니다:', error);
      return null;
   }
}

// Supabase에 데이터 저장 함수
async function insertDataToSupabase(items) {
   for (const item of items) {
      const foodItem = {
         foodcd: item.foodCd || 'N/A',
         foodnm: item.foodNm || 'N/A',
      };

      try {
         const { data, error } = await supabase.from('food').insert([foodItem]);

         if (error) {
            console.error('데이터 저장 중 오류가 발생했습니다:', error.message);
            console.error('오류 세부 정보:', JSON.stringify(error, null, 2));
         } else {
            console.log('데이터가 성공적으로 저장되었습니다 : ', JSON.stringify(data, null, 2));
         }
      } catch (err) {
         console.error('삽입 중 try-catch 오류 발생:', err);
      }
   }
}

// 반복 호출을 통한 데이터 수집 및 저장
async function main() {
   let pageNo = 1;
   let fetchedData;

   do {
      console.log(`${pageNo}번째 페이지 데이터를 가져오는 중...`);
      fetchedData = await fetchData(pageNo);

      if (fetchedData && fetchedData.items && fetchedData.items.length > 0) {
         // Supabase에 데이터 저장
         await insertDataToSupabase(fetchedData.items);
         pageNo++;
      } else {
         console.log('더 이상 가져올 데이터가 없습니다.');
         break;
      }
   } while (fetchedData !== null);
}

// 메인 함수 실행
main().catch((error) => console.error('스크립트 실행 중 오류가 발생했습니다:', error));

0개의 댓글