;

[자동화] Tasker, n8n, Firefly III 연동 가계부 자동 기록 시스템 구축 본문

Programing

[자동화] Tasker, n8n, Firefly III 연동 가계부 자동 기록 시스템 구축

WindowsHyun 2025. 7. 1. 14:42
반응형

개요

본 포스트는 안드로이드 금융 알림을 감지하여 n8n으로 처리한 후, Firefly III 가계부에 자동으로 거래 내역을 기록하는 전체 시스템 구축 과정을 설명합니다. 앞선 두 포스트를 통해 n8n과 Firefly III가 사전에 설치되어 있어야 합니다.

시스템 아키텍처

  1. Tasker (Trigger): 안드로이드 알림 발생 시, 알림 정보를 담아 n8n의 Webhook URL로 HTTP POST 요청을 전송.
  2. n8n (Processor): Webhook으로 수신된 데이터를 AI(OpenAI)로 분석하여 거래 정보를 추출하고, 거래 유형(지출/입금)에 따라 Firefly III API가 요구하는 최종 JSON 포맷으로 가공.
  3. Firefly III (Database): n8n에서 전송된 API 요청을 받아 새로운 거래 내역으로 저장.

1. Tasker 설정

1.1. 프로필 및 태스크 생성

  • 프로필: 이벤트 → UI → 알림. 소유자 애플리케이션에 금융 앱들을 지정.

1.2. 태스크 액션 구성

  1. If (%antext Is Set): 알림 내용이 있을 때만 실행.
  2. JavaScriptlet: 알림 변수들을 가져와 모든 특수문자를 처리한 완전한 JSON 문자열(%HttpBody)을 생성.
    • Code 필드:
      const app = local('evtprm1');
      const title = local('evtprm2');
      const text = local('evtprm3');
      
      const data = { app: app, title: title, text: text };
      const httpBody = JSON.stringify(data);
      setGlobal('HttpBody', httpBody);
      
  3. HTTP Request: 생성된 JSON을 n8n으로 전송.
    • Method: POST
    • URL: n8n Webhook 노드의 Production URL
    • Body: %HttpBody
    • Content-Type: application/json
  4. End If: 조건문 종료.

2. n8n 워크플로우 구성

2.1. 노드 구성 순서

Webhook → OpenAI Chat Model → Code → If → (true / false 분기) → HTTP Request

2.2. 노드별 상세 설정

  1. Webhook: Tasker로부터 데이터를 수신.
  2. OpenAI Chat Model: 수신된 text를 분석하여 bank, amount, store, type 추출.
    • Prompt:
      너는 한국의 금융 알림 메시지를 분석해서 JSON 형식으로 출력하는 전문가야.
      아래 "입력 텍스트"에서 거래 정보를 추출해줘.
      
      입력 텍스트: "App:{{ $json.body.app }}, Title:{{ $json.body.title }}, Text:{{ $json.body.text }}"
      
      추출할 정보는 다음과 같아:
      - "amount": 콤마(,)가 없는 숫자 형태의 금액
      - "store": 사용처 또는 구매처 없으면 이름 또는 내용
      - "type": withdrawal: 지출, 결제, 출금 | deposit: 입금, 취소, 환불| transfer: 이체
      - "bank": "네이버통장-미래에셋증권, KB국민은행, 우리은행, 케이뱅크, 기업은행, 현대카드, 삼성카드" 이렇게 종류가 있고 App 내용을 기반으로 분리
      
      규칙:
      - 정보 추출이 불가능하면 null 값을 사용해.
      - 다른 설명은 절대 하지 말고, 오직 JSON 객체만 출력해.
      - bank는 무조건 위에 7개중 하나만 선택해
      - store는 null이 될 수 없어, 입금자명이라도 적어줘야해
      - "Markdown 코드 블록(\``)을 절대 포함하지 마."`
      - 줄구분 등의 내용도 절대 넣지마 "\n" 절대 넣지마
      - "\n" 내용에서 어떠한 경우에도 넣지마
      
      ---
      예시 입력: KB국민카드 12,345원 스타벅스 승인
      예시 출력:
      {
        "bank": "KB국민은행",
        "amount": 12345,
        "store": "스타벅스",
        "type": "입금"
      }
  3. Code: AI의 출력 결과(텍스트)를 trim()과 JSON.parse()를 이용해 순수 JSON 객체로 변환.
    • Code:
    • const rawText = $json.output;
      const trimmedText = rawText.trim();
      const jsonData = JSON.parse(trimmedText);
      return jsonData;
  4. If: Code 노드에서 출력된 type이 deposit인지 여부를 기준으로 분기.
  5. FireFlyIII JWT 토큰 발급하기
    • 옵션 -> 프로필 -> OAuth
    • 개인 엑세스 토큰을 발급하셔서 긴 텍스트를 별도로 보관해주시면 됩니다.

  6. HTTP Request (지출용 - false 경로):
    • URL: https://<FIREFLY_URL>/api/v1/transactions
    • Authentication: JWT 넣기
    • Body:
      {
        "transactions": [
          {
            "type": "{{ $('Code').first().json.type }}",
            "date": "{{ $now.toISODate() }}",
            "amount": "{{ $('Code').first().json.amount }}",
            "description": "{{ $('Code').first().json.store }}",
            "source_name": "{{ $('Code').first().json.bank }}",
            "destination_name": "{{ $('Code').first().json.store }}"
          }
        ]
      }
  7. HTTP Request (입금용 - true 경로):
    • URL과 Authentication은 위와 동일.
    • Body: source_name과 destination_name의 값이 지출용과 반대임.
      {
        "transactions": [
          {
            "type": "{{ $('Code').first().json.type }}",
            "date": "{{ $now.toISODate() }}",
            "amount": "{{ $('Code').first().json.amount }}",
            "description": "{{ $('Code').first().json.store }}",
            "source_name": "{{ $('Code').first().json.store }}",
            "destination_name": "{{ $('Code').first().json.bank }}"
          }
        ]
      }

http Request 예제 이미지


3. 주요 오류 및 해결 방안

  • JSON 문법 오류: n8n HTTP Request 노드 자체의 오류는 Body의 JSON 문법(주로 마지막 항목의 잉여 쉼표) 문제일 가능성이 높습니다.
  • Firefly III API 오류 (422 Unprocessable Entity):
    • invalid type: AI 프롬프트가 API가 요구하는 withdrawal/deposit을 정확히 출력하지 못하는 경우입니다. 프롬프트 수정을 통해 해결합니다.
    • invalid destination account: 입금/지출의 source_name과 destination_name의 논리가 잘못된 경우입니다. If 노드를 통한 분기 처리로 해결합니다.
  • HTML 리디렉션 응답: curl 테스트 시 HTML이 반환되는 것은 클라우드플레어나 리버스 프록시의 리디렉션 규칙 때문일 가능성이 높습니다. 관련 설정을 점검해야 합니다.
반응형
Comments