본문 바로가기
Tech lab

n8n에서 파이썬(Python)을 활용하여 데이터파이프라인 구축하기

by 데이터엔지니어스랩 2025. 3. 28.

 

 

안녕하세요! 데이터로 일하는 데이터엔지니어스랩입니다👋👋
 
데이터로 일하기 위해서는 문제 정의, 데이터 분석, 해석 능력 등이 필요하지만, 그중 가장 중요한 것은 데이터 수집입니다. 오늘은 데이터 수집을 하는 방법의 하나인 n8n 과 공공데이터포털의 open API를 사용하여 데이터를 수집하는 방법을 소개합니다.
 
먼저 n8n 과 open api 에 대해 간단히 살펴본 다음 데이터 수집 방법을 소개해 드리겠습니다.
보통 n8n은 업무 자동화 및 워크플로우 개선을 위해 많이 사용하고 있습니다. 그러나 기능을 확장하여 데이터 파이프라인의 역할을 기대할수 있습니다.

 

n8n 이란?

n8n 이란 node와 automation을 합친 단어인 nodemation 의 줄임말로, 컨테이너 오케스트레이션 도구 중 하나인 kubernetes 를 k8s 로 표기하는것 처럼 첫글자 n과 마지막 글자 n 사이에 8글자가 있어서 n8n 이란 이름을 지었다고 합니다. 이름에서 알 수 있듯이 여러 개의 노드로 순차적인 작업을 자동화할 수 있는 것이 특징입니다.
 
 

특징

1. 한눈에 볼 수 있는 노드들과 워크플로우를 보여주는 에디터

n8n은 자체 에디터를 제공하여 한눈에 각 노드와 워크플로우를 보여주며 드래그앤 드롭 방식으로 편리하게 노드를 추가하고 수정할 수 있습니다.

 

 

2. 보일러플레이트 코드를 작성할 필요 없음

n8n 은 사전 구성된 약 400개의 노드 템플릿을 제공하여 원하는 기능을 하는 노드 템플릿을 찾아서 붙이는 것만으로도 작업을 수행할 수 있습니다.

 

3. AI 를 활용한 워크 플로우 구축

n8n에서 제공하는 AI 노드를 활용하여 기사를 요약하거나 챗봇 기능을 넣은 워크플로우 등을 구성할 수 있습니다.

 

 

 

OpenAPI 란?

open API는 Open Application Programming Interface의 약자로 누구나 사용할 수 있는 공개된 api 를 의미합니다. 여기서 API는 특정 기능을 하는 어플리케이션을 호출할 수 있는 인터페이스를 뜻합니다.

 


 

데이터 수집하기

 

수집할 데이터 정하기

 

데이터엔지니어스랩은 사내 DB에 여러 가지 데이터를 저장하고 있는데요, 봄철 빼놓을 수 없는 이슈인 미세먼지 데이터를 수집한다면 활용도가 높을 것 같아서 실시간 대기 정보 데이터를 수집하기로 정했습니다. 사업 아이템을 구상하려면 실시간으로 데이터를 얻어서 데이터의 흐름을 보는 것이 중요하겠죠? 그럼 이런 데이터를 제공하는 곳이 있는지 찾아봅시다.

 

 

다행히 공공데이터포털에서 실시간 시도별 대기정보를 담은 open API를 제공하는군요! 🥳

공공데이터포털에서는 스크린샷에서도도 볼 수 있듯이 파일데이터로도 데이터를 제공하지만, 이런 방식을 채택한다면 데이터를 담은 파일을 주기적으로 생성해서 데이터를 입력해 줘야 하는 번거로움이 생깁니다.

또한 매시간 새로운 데이터가 쌓이는 실시간 데이터의 특성상 반복적인 데이터 수집 및 저장 작업이 필요합니다. 그래서 사람이 일일이 작업하지 않고 업무 자동화로 작업 흐름을 구축하는데, 파일을 사용하는 방식은 상대적으로 자동화하기 어렵다는 단점이 있으며 유지 보수 비용도 더 크기 때문에 인력이 낭비될 우려가 있습니다.

반면 open API를 사용하는 방식은 지정된 API 명세에 맞춰 API를 호출하기만 하면 원하는 데이터를 얻을 수 있고 자동화하기도 쉽기 때문에 지금의 경우 적합한 방법이라고 할 수 있습니다.자, 그럼 open API와 n8n을 사용하여 데이터 수집 자동화를 해봅시다.

워크플로우 수집하기

1. 워크플로우의 주기 정하기

워크플로우를 구상하려면 먼저 어떤 주기로 동작시킬지 정해야 합니다. 그 주기를 정하려면 open API 의 명세를 먼저 살펴봐야 합니다.

 

 

요청 변수를 통해 시간별 평균, 일평균 데이터를 수집할 수 있음을 알 수 있습니다. 둘 다 의미 있는 데이터지만 좀 더 자세한 정보를 얻을 수 있는 시간 평균 데이터가 적합해 보입니다. 그럼, 이제 본격적으로 API를 호출해서 데이터가 어떻게 생겼는지 볼 시간입니다.  공공데이터 포털에서 제공하는 응답 값이 나와 있지만, 이런 경우 직접 호출해 보는 것이 데이터를 파악하는 데는 더 도움이 됩니다.

 

{
  "response": {
      "body": {
          "totalCount": 25,
          "items": [
              {
                  "daegu": "20",
                  "chungnam": "28",
                  "incheon": "27",
                  "daejeon": "27",
                  "gyeongbuk": "22",
                  "sejong": "25",
                  "gwangju": "38",
                  "jeonbuk": "35",
                  "gangwon": "23",
                  "ulsan": "20",
                  "jeonnam": "22",
                  "seoul": "28",
                  "busan": "28",
                  "jeju": "22",
                  "chungbuk": "23",
                  "gyeongnam": "21",
                  "dataTime": "2025-03-20 15:00",
                  "dataGubun": "1",
                  "gyeonggi": "28",
                  "itemCode": "PM2.5"
              },
              ...중략...              
              {
                  "daegu": "24",
                  "chungnam": "29",
                  "incheon": "31",
                  "daejeon": "23",
                  "gyeongbuk": "27",
                  "sejong": "20",
                  "gwangju": "32",
                  "jeonbuk": "32",
                  "gangwon": "24",
                  "ulsan": "28",
                  "jeonnam": "25",
                  "seoul": "31",
                  "busan": "19",
                  "jeju": "19",
                  "chungbuk": "29",
                  "gyeongnam": "21",
                  "dataTime": "2025-03-19 15:00",
                  "dataGubun": "1",
                  "gyeonggi": "26",
                  "itemCode": "PM2.5"
                }                          
           ],
          "pageNo": 1,
          "numOfRows": 100
      },
      "header": {
          "resultMsg": "NORMAL_CODE",
          "resultCode": "00"
      }
  }
}

 

 

 

 

2. 워크플로우 실행 계획 세우기

 

워크플로우를 만들 때는 어떤 순서로 작업을 수행할지 정한 후 만드는 것이 중요합니다. 각 작업은 앞뒤 작업에 영향을 미치기 때문에 나중에 작업을 추가하거나 수정할 때 퇴근이 굉장히 늦어질 수 있습니다. 🫠 그러므로 미리 워크플로우의 큰 그림을 정하고 만드는 것이 중요합니다. 여러분의 퇴근은 소중하니까요😌

이번 미세먼지 데이터 수집은 크게 5단계로 이루어져 있습니다.

 

 

워크플로우 실행 계획을 세웠으니 순차적으로 구현해보겠습니다.

 

 

3. 워크플로우 만들기

 

워크 플로우 시작 - Trigger 추가하기

 

워크플로우의 시작은 Trigger 노드가 담당하고 있습니다. 트리거 노드에서는 워크플로우의 실행 규칙을 뜻하는 Trigger Rules 추가할 수 있는데 워크플로우의 실행 주기를 의미하는 Trigger Interval에 따라 세부 규칙을 지정할 수 있습니다. 우리가 만들 워크플로우에서는 API 를 하루에 한 번, 자정에 호출하기로 정했으니, 이걸 Trigger rules 에 반영해 줍시다.

데이터가 비록 정시에 생성되긴 하지만 서버에서 발생할 수 있는 문제들을 고려하여 자정이 아닌 0시 30에 워크플로우를 실행하도록 설정해 주었습니다.

 

 

트리거 노드를 추가했으면 우측 상단의 Test step 을 클릭하여 트리거를 테스트해봅시다.

 

 

오른쪽 테스트 결과창 상단 Output 옆에 초록색 체크 표시가 나오면 노드가 정상적으로 실행했음을 의미합니다.

트리거 노드는 워크플로우를 실행하는 기능을 수행하는 것이 주 기능이지만, 노드 실행시간 데이터를 반환하기 때문에 필요할 경우 이 값을 사용할 수도 있습니다.

 

open API 호출 - HTTP Request 추가하기

 

open API를 호출하기 위해서는 반드시 API 제공자가 지정한 형식으로 요청 값를 보내야 원하는 데이터를 얻을 수 있습니다. 값을 지정하고 수정할 수 있는 Edit Fields 노드를 사용하여 요청값을 설정하는 방법이 더 간단하지만, 코드를 작성할 수 있는 Code 노드로 이 작업을 만들어봅시다. 트리거 노드 뒤에 Code 노드와 open API 를 호출하는 HTTP Request 노드를 차례대로 추가해 줍니다.


⚠️ HTTP 노드 아래에 보이는 경고표시는 아직 노드의 세부사항을 작성하지 않았기 때문에 보이는 표시이므로 지금은 신경쓰지 않아도 됩니다!

 

 


💡 n8n 은 Code 노드에서 자바스크립트만을 지원하였으나 현재는 파이썬도 제공하고 있습니다. (현재 베타 단계) 하지만 n8n 공식문 서에서 파이썬은 추가적인 컴파일이 필요하기 때문에 자바스크립트보다 상대적으로 느리다고 기술되어 있습니다.

현재 Code 노드에서는 바로 다음에 호출할 HTTP Request 노드에 필요한 JSON 객체를 생성하는 작업만 수행하기 때문에 어떤 언어를 사용해도 유의미한 속도 차이가 난다고 보기는 어려우므로 파이썬을 사용했습니다.

 

 

open API 호출에 필요한 요청 값은 위와 같습니다. Code 노드에서 파이썬을 사용할 경우 딕셔너리를 리턴하면 리턴값을 추가적인 변환 작업 없이 JSON 으로 바로 사용할 수 있으므로 위의 값들을 딕셔너리로 만들어서 리턴하는 코드를 작성합니다.

 

API_KEY = '공공데이터 포털에서 발급받은 API 키';

result = []
item_code_list = ["SO2", "CO", "O3", "NO2", "PM10", "PM25"]


for item_code in item_code_list:
  result.append({
    'serviceKey' : API_KEY,
    'returnType' : 'json',
    'numOfRows' : 100,
    'pageNo' : 1,
    'itemCode' : item_code,
    'dataGubun' : 'HOUR'
  })

return result;

 

만약 이 작업을 코드를 사용하지 않는다면 Edit Fields 노드를 아래와 같이 구성하면 됩니다. 마찬가지로 itemCode 도 배열로 넘겨야 하는데 HTTP Request에서 처럼 고정된 값을 넘기거나 표현식으로 넘기는 방법이 있습니다.

지금은 입력값에 대한 처리가 아니라 문자열을 배열로 변환하는 작업을 하므로 자바스크립트를 사용해서 문자열을 배열로 바꾸는 작업을 했는데 이렇게 코드로 표현식을 작성하면 좀 더 세부적인 작업을 할 수도 있습니다.

open API 는 요청 변수에서 넘긴 단일 '항목명'에 대한 데이터만을 반환하기 때문에 미세먼지 데이터 6종의 데이터를 모두 수집해야 할 경우 API 를 6번 호출 해야 합니다. 물론 한 세트의 Code 노드와 HTTP Request 노드를 6번 생성할 수도 있지만, n8n 에서는 이런 반복작업을 지원하는 Loop Over Items 노드를 제공합니다.

Loop Over Items 노드를 Code 노드와 HTTP Request 노드 사이에 추가해줍니다. Loop Over Items 가 반복해야 할 작업은 HTTP Request 노드의 작업이므로 노드의 끝에 있는 loop 화살표를 HTTP Request 노드에 이어줍니다.

 

 

Loop Over Items 노드는 반복 횟수를 뜻하는 Batch Size 옵션을 줄 수 있는데,우리는 6번의 작업을 수행해야 하므로 6으로 설정해 줍니다. 그런 다음 우측 상단의 Test step 을 클릭하여 결괏값이 제대로 생성되었는지 확인해 봅시다.

 

 

HTTP Request 에 필요한 요청 값 6개가 정상적으로 생성되었습니다! 이제 이 요청 값을 open API 요청 URL에 넣어야 합니다. 요청 URL의 작성 방식은 고정값을 Fixed 방식과 가변적인 값을 넣을 때 사용할 수 있는 표현식을 사용하는 방식이 있는데 Code 노드에서 요청 값 딕셔너리를 담은 배열을 반환했기 때문에 표현식 방법으로 URL 을 설정해 줘야 합니다. n8n 의 표현식은 JSON에 특화된 쿼리 언어인 JMESPath 기반이기 때문에 여기에 맞게 작성해 줍니다.

 

 

 

JMESPath — JMESPath

Libraries in Multiple Languages Each JMESPath library passes a complete suite of compliance tests to ensure they work as intended. There are libraries in multiple languages including python, php, javascript and lua.

jmespath.org

 

 

위 스크린샷처럼 결과 데이터가 totalcount 가 24개인 값으로 6개가 생성되었다면 올바르게 작성한 것입니다.

 

 

데이터 정제

 

데이터엔지니어스 랩의 사내 DB 에는 지역별로 다양한 데이터를 수집하고 있는데, 지역 컬럼을 영어가 아닌 한글로 저장하고 있습니다. 하지만 지금 호출한 opne API 는 지역이름을 영어로 제공하고 있기 때문에 데이터의 통일성을 위해 수정이 필요합니다.

또한 지역별, 시간별로 데이터를 저장하는 쿼리를 사용해서 데이터를 DB에 저장해야 하므로 데이터의 형식을 다음 노드에서 접근하기 쉽게 수정해야 합니다. n8n 은 key-value 형식의 데이터의 키를 바꿀 수 있는 Rename Keys 노드를 제공하지만, 이번에도 파이썬 코드로 작업해 보겠습니다.

 

from datetime import datetime


district_map = {
  "daegu": "대구",
  "chungnam": "충남",
  ...중략...
}

unit_map = {
  "SO2" : "ppm",
  "CO" : "ppm",
  "O3" : "ppm",
  "NO2" : "ppm",
  "PM10" : "㎍/㎥",
  "PM2.5" : "㎍/㎥",
}

date = datetime.now().strftime("%Y-%m-%d")

data_list = []

for data in _input.all():
  for location in dict(data.json.response.body.items()).get('items'):
    if date in location.dataTime:
      converted_dict = {kr_k: location.get(en_k, kr_k) for en_k, kr_k in district_map.items()}
      for k,v in converted_dict.items():
        to_json = {
          'loc_name': k,
          'type' : location.get('itemCode'),
          'value' : v,
          'unit' : unit_map.get(location.get('itemCode')),
          'data_time' : location.get('dataTime'),
        }
        data_list.append(to_json)

return data_list

 

n8n 에서 이전 노드에서 반환값이 있을 경우 _input 또는 _json 으로 접근하여 데이터를 가져올 수 있습니다. 위에 표현식 부분 처럼 n8n 은 자체적인 문법을 가지고 있기 때문에 약간의 러닝커브가 발생할 수 있습니다.

 

 

 

Current node input | n8n Docs

 

docs.n8n.io

 

n8n 의 장점 중 하나는 자체 디버깅을 제공한다는 점입니다. 각 노드에서 발생하는 에러뿐만 아니라 문법 오류로 컴파일 에러가 나는 경우에도 어떤 에러인지 보여주기 때문에 어떤 부분에서 에러가 발생했는지 찾을 수 있습니다. 또한 파이썬으로 코드를 작성할 경우 print() 함수를 사용하면 개발자 도구의 콘솔 창에 출력되기 때문에 IDE에서 개발하듯이 디버깅을 할 수 있는 장점이 있습니다.

 

 

데이터 정제를 끝내고 아래와 같은 데이터가 생성되었습니다.

 

[
  {
    "loc_name": "대구",
    "type": "SO2",
    "value": "0.005",
    "unit": "ppm",
    "data_time": "2025-03-24 08:00"
  },
  {
    "loc_name": "충남",
    "type": "SO2",
    "value": "0.004",
    "unit": "ppm",
    "data_time": "2025-03-24 08:00"
  },
  {
    "loc_name": "인천",
    "type": "SO2",
    "value": "0.004",
    "unit": "ppm",
    "data_time": "2025-03-24 08:00"
  },
  {
    "loc_name": "대전",
    "type": "SO2",
    "value": "0.003",
    "unit": "ppm",
    "data_time": "2025-03-24 08:00"
  },
  ...중략
}

 


만약 이 작업을 코드를 작성하지 않으려면 Rename Key 노드를 아래와 같이 만들면 됩니다.

 

 

 

이제 이 데이터를 쿼리를 사용해서 DB 에 저장할 차례입니다. n8n 은 다양한 DB 들에서 작업할 수 있는 노드들을 지원하는데 미세먼지 데이터는 MySQL DB에 저장할 것이므로 MySQL 노드를 추가해줬습니다.

 

MySQL 노드에서 작업을 하기 위해서는 Credential to connect with, Operation의 두 가지 옵션을 정해줘야 하는데, Credential to connect with에서는 계정 정보, 포트 같은 연결정보를 설정할 수 있고 Operation 에서는 수행할 작업을 설정할 수 있습니다.

MySQL 노드는 앞서 만들었던 HTTPRequest 노드와는 달리 배열로 받은 데이터를 Loop 노드 없이 처리할 수 있습니다. 또한 HTTPRequest 노드에서와 마찬가지로 고정값을 DB에 저장하는 것이 아니라 가변적인 실시간의 값을 저장하기 때문에 표현식 방법으로 쿼리를 작성해야 올바른 데이터를 저장할 수 있습니다.

 

 

 

그리고 다음날 DB 를 확인해보면…

 

 

데이터가 잘 들어갔군요! 다른 분들도 데이터가 올바르게 저장되셨겠죠?ㅎㅎ 축하드립니다! 👏👏

 


이것으로 파이썬과 n8n 을 사용하여 워크플로우 구성하기가 끝이 났습니다. n8n 의 장점을 살리고 만약 워크플로우의 유지보수를 맡은 팀원들이 프로그래밍 지식이 없다면 코드 노드를 사용하기보다는 같은 기능을 하는 노드를 사용하여 워크플로우를 구성하는 것이 효율적입니다.

비록 지금은 코드 노드를 사용하지 않고 다른 기능을 하는 노드로 워크플로우를 구성할 수 있지만 코드를 사용하면 좀 더 세부적인 워크플로우를 구성할 수 있습니다. 이번 기회에 프로그래밍 언어도 익힐겸 파이썬으로 워크플로우를 구성하는 것은 어떨까요?😉