ERP-node/docs/external-call-implementatio...

15 KiB

외부 호출 기능 구현 계획서

📋 개요

데이터플로우 다이어그램에서 외부 시스템 호출 기능을 구현하여, 데이터 처리 완료 시 다양한 외부 서비스로 알림이나 데이터를 전송할 수 있도록 합니다.

🎯 목표

  1. 무료/저렴한 알림 방법 우선 구현
  2. 확장 가능한 구조 설계
  3. 사용자 친화적 UI 제공
  4. 안정적인 오류 처리 구현

🚀 Phase 1: 기본 외부 호출 기능 (무료)

1.1 REST API 호출 🚧

현재 상태: UI만 구현됨 (실제 호출 기능 없음)

  • 설정 UI: HTTP 메서드, 헤더, 페이로드 입력 가능
  • 실제 HTTP 요청 전송 기능 없음
  • 백엔드 서비스 구현 필요

1.2 웹훅 호출 🚧

현재 상태: UI만 구현됨 (실제 호출 기능 없음)

  • 설정 UI: 웹훅 URL 입력 가능
  • 실제 웹훅 전송 기능 없음
  • 백엔드 서비스 구현 필요

1.3 이메일 알림 🔄

현재 상태: Java 기반 구현됨 (Node.js 연동 필요)

  • Java MailUtil 클래스 구현됨
  • SMTP 설정 및 발송 기능 있음
  • 데이터플로우와 연동 안됨

1.4 통합된 REST API 호출 시스템 🆕

새로운 중첩 드롭다운 구조로 개선

  • 1단계: 호출 유형 (REST API 호출, 이메일 전송, FTP 업로드)
  • 2단계: REST API 세부 종류 (슬랙, 카카오톡, 디스코드, 기타)
  • 3단계: 각 종류별 맞춤 설정 UI
// ExternalCallService.ts - 통합 구조
class ExternalCallService {
  // 공통 REST API 호출 메서드
  private async callRestApi(config: RestApiConfig) {
    return await fetch(config.url, {
      method: config.method,
      headers: config.headers,
      body: config.body,
    });
  }

  // 슬랙 (REST API의 특수 케이스)
  async sendSlackMessage(settings: SlackSettings) {
    return await this.callRestApi({
      url: settings.slackWebhookUrl,
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        text: settings.slackMessage,
        channel: settings.slackChannel,
      }),
    });
  }

  // 카카오톡 (REST API의 특수 케이스)
  async sendKakaoTalk(settings: KakaoSettings) {
    return await this.callRestApi({
      url: "https://kapi.kakao.com/v2/api/talk/memo/default/send",
      method: "POST",
      headers: { Authorization: `Bearer ${settings.kakaoAccessToken}` },
      body: this.buildKakaoBody(settings.kakaoMessage),
    });
  }

  // 일반 API (사용자 정의)
  async callGenericApi(settings: GenericApiSettings) {
    return await this.callRestApi({
      url: settings.apiUrl,
      method: settings.httpMethod,
      headers: JSON.parse(settings.headers || "{}"),
      body: settings.bodyTemplate,
    });
  }
}

🔧 Phase 2: UI/UX 개선

2.1 중첩 드롭다운 구조로 외부 호출 타입 개선

파일: frontend/types/connectionTypes.ts

export interface ExternalCallSettings {
  callType: "rest-api" | "email" | "ftp" | "queue";

  // REST API 세부 종류 (중첩 드롭다운)
  apiType?: "slack" | "kakao-talk" | "discord" | "generic";

  // 일반 REST API 설정 (기타 선택 시)
  apiUrl?: string;
  httpMethod?: "GET" | "POST" | "PUT" | "DELETE";
  headers?: string;
  bodyTemplate?: string;

  // 슬랙 전용 설정
  slackWebhookUrl?: string;
  slackChannel?: string;
  slackMessage?: string;

  // 카카오톡 전용 설정
  kakaoAccessToken?: string;
  kakaoMessage?: string;

  // 디스코드 전용 설정
  discordWebhookUrl?: string;
  discordMessage?: string;

  // 이메일 설정
  smtpHost?: string;
  smtpPort?: number;
  smtpUser?: string;
  smtpPass?: string;
  fromEmail?: string;
  toEmail?: string;
  subject?: string;
  emailBody?: string;
}

중첩 드롭다운 구조:

  1. 1단계: 호출 유형 선택 (REST API 호출, 이메일 전송, FTP 업로드, 메시지 큐)
  2. 2단계: REST API 세부 종류 선택 (슬랙, 카카오톡, 디스코드, 기타)
  3. 3단계: 각 종류별 맞춤 설정 UI 표시

2.2 중첩 드롭다운 UI 구현

파일: frontend/components/dataflow/connection/ExternalCallSettings.tsx

1단계: 호출 유형 선택

<Select
  value={settings.callType}
  onValueChange={(value) => onSettingsChange({ ...settings, callType: value })}
>
  <SelectContent>
    <SelectItem value="rest-api">REST API 호출</SelectItem>
    <SelectItem value="email">이메일 전송</SelectItem>
    <SelectItem value="ftp">FTP 업로드</SelectItem>
    <SelectItem value="queue">메시지 </SelectItem>
  </SelectContent>
</Select>

2단계: REST API 세부 종류 선택

{
  settings.callType === "rest-api" && (
    <Select
      value={settings.apiType || "generic"}
      onValueChange={(value) =>
        onSettingsChange({ ...settings, apiType: value })
      }
    >
      <SelectContent>
        <SelectItem value="slack">슬랙</SelectItem>
        <SelectItem value="kakao-talk">카카오톡</SelectItem>
        <SelectItem value="discord">디스코드</SelectItem>
        <SelectItem value="generic">기타 (일반 API)</SelectItem>
      </SelectContent>
    </Select>
  );
}

3단계: 각 종류별 맞춤 설정

{
  /* 슬랙 설정 */
}
{
  settings.apiType === "slack" && (
    <>
      <Input placeholder="https://hooks.slack.com/services/..." />
      <Input placeholder="#general" />
      <Textarea placeholder="데이터 처리 완료! {{recordCount}}건" />
    </>
  );
}

{
  /* 카카오톡 설정 */
}
{
  settings.apiType === "kakao-talk" && (
    <>
      <Input type="password" placeholder="카카오 액세스 토큰" />
      <Textarea placeholder="처리 완료! 총 {{recordCount}}건 처리되었습니다." />
    </>
  );
}

{
  /* 기타 API 설정 */
}
{
  settings.apiType === "generic" && (
    <>
      <Input placeholder="https://api.example.com/webhook" />
      <Select placeholder="HTTP Method" />
      <Textarea placeholder="Headers (JSON)" />
      <Textarea placeholder="Body Template" />
    </>
  );
}

📱 Phase 3: 메신저 통합 (유료)

3.1 SMS 발송

예상 비용: 15~20원/건 추천 서비스:

  • NHN Toast SMS
  • 네이버 클라우드 SMS
  • 알리고 SMS
// SMS 설정 타입
interface SMSSettings {
  provider: "toast" | "naver" | "aligo";
  apiKey: string;
  secretKey: string;
  fromNumber: string;
  toNumber: string;
  message: string;
}

3.2 카카오톡 알림톡

예상 비용: 15~25원/건 필요 사항:

  • 카카오톡 비즈니스 계정
  • 알림톡 템플릿 등록
  • 카카오톡 비즈니스 API 연동
// 카카오톡 설정 타입
interface KakaoTalkSettings {
  apiKey: string;
  templateCode: string;
  phoneNumber: string;
  templateParams: Record<string, string>;
}

🛠️ Phase 4: 고급 기능

4.1 재시도 메커니즘

interface RetryConfig {
  maxRetries: number;
  retryDelay: number; // ms
  backoffMultiplier: number;
}

// ExternalCallService에서 구현
async executeWithRetry(callFunction: () => Promise<any>, config: RetryConfig) {
  let lastError: Error;

  for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
    try {
      return await callFunction();
    } catch (error) {
      lastError = error as Error;
      if (attempt < config.maxRetries) {
        await new Promise(resolve =>
          setTimeout(resolve, config.retryDelay * Math.pow(config.backoffMultiplier, attempt - 1))
        );
      }
    }
  }

  throw lastError!;
}

4.2 호출 로그 및 모니터링

-- 외부 호출 로그 테이블
CREATE TABLE external_call_logs (
  id SERIAL PRIMARY KEY,
  diagram_id INTEGER REFERENCES dataflow_diagrams(diagram_id),
  relationship_id VARCHAR(255),
  call_type VARCHAR(50),
  target_url VARCHAR(500),
  request_payload TEXT,
  response_status INTEGER,
  response_body TEXT,
  error_message TEXT,
  execution_time_ms INTEGER,
  created_at TIMESTAMP DEFAULT NOW()
);

4.3 조건부 호출

// 특정 조건에서만 외부 호출 실행
interface ConditionalCall {
  condition: string; // "recordCount > 100"
  callSettings: ExternalCallSettings;
}

📊 구현 우선순위

🥇 High Priority (즉시 구현)

  1. 🚧 ExternalCallService 생성 - 백엔드 서비스 기반 구축
  2. 🆕 슬랙 웹훅 구현 - 가장 간단하고 테스트하기 쉬움
  3. 🔄 REST API 호출 완성 - UI는 있으나 실제 기능 없음
  4. 🔄 웹훅 호출 완성 - UI는 있으나 실제 기능 없음
  5. 🔄 이메일 연동 - Java MailUtil을 Node.js에서 호출
  6. 🔄 디스코드 웹훅 추가 - 슬랙과 유사한 구조

🥈 Medium Priority (1-2주 후)

  1. 🔄 재시도 메커니즘
  2. 🔄 호출 로그 시스템
  3. 🔄 오류 처리 개선
  4. 🔄 템플릿 변수 확장

🥉 Low Priority (필요시)

  1. 🔄 SMS 발송
  2. 🔄 카카오톡 알림톡
  3. 🔄 조건부 호출
  4. 🔄 배치 호출

🔧 기술적 구현 세부사항

백엔드 수정사항

1. ExternalCallService 확장

파일: backend-node/src/services/externalCallService.ts

// 슬랙 웹훅 메서드 추가
async sendSlackMessage(settings: SlackSettings): Promise<boolean> {
  const payload = {
    text: this.processTemplate(settings.message, settings.templateData),
    channel: settings.channel,
    username: settings.username || "DataFlow Bot",
    icon_emoji: settings.iconEmoji || ":robot_face:"
  };

  const response = await fetch(settings.webhookUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  });

  return response.ok;
}

2. 데이터베이스 스키마 확장 (선택사항)

-- 외부 호출 설정을 별도 테이블로 관리 (대용량 처리 시)
CREATE TABLE external_call_configs (
  id SERIAL PRIMARY KEY,
  diagram_id INTEGER REFERENCES dataflow_diagrams(diagram_id),
  relationship_id VARCHAR(255),
  call_type VARCHAR(50),
  config_data JSONB,
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW()
);

-- 인덱스
CREATE INDEX idx_external_calls_diagram ON external_call_configs(diagram_id);
CREATE INDEX idx_external_calls_relationship ON external_call_configs(relationship_id);

프론트엔드 수정사항

1. 타입 정의 확장

파일: frontend/types/connectionTypes.ts

  • SlackSettings, DiscordSettings 인터페이스 추가
  • ExternalCallSettings 확장

2. UI 컴포넌트 확장

파일: frontend/components/dataflow/connection/ExternalCallSettings.tsx

  • 슬랙, 디스코드 설정 UI 추가
  • 템플릿 변수 도움말 추가
  • 실시간 미리보기 기능

3. 유효성 검사 강화

파일: frontend/components/dataflow/ConnectionSetupModal.tsx

const validateExternalCallSettings = (
  settings: ExternalCallSettings
): boolean => {
  if (settings.callType === "rest-api") {
    switch (settings.apiType) {
      case "slack":
        return !!(settings.slackWebhookUrl && settings.slackMessage);
      case "kakao-talk":
        return !!(settings.kakaoAccessToken && settings.kakaoMessage);
      case "discord":
        return !!(settings.discordWebhookUrl && settings.discordMessage);
      case "generic":
        return !!(settings.apiUrl && settings.httpMethod);
      default:
        return false;
    }
  } else if (settings.callType === "email") {
    return !!(settings.toEmail && settings.subject && settings.emailBody);
  }
  return true;
};

📈 성능 고려사항

1. 비동기 처리

  • 외부 호출을 메인 데이터 처리와 분리
  • 큐 시스템 도입 (Redis/Bull Queue)

2. 타임아웃 설정

const EXTERNAL_CALL_TIMEOUT = {
  "rest-api": 30000, // 30초
  webhook: 10000, // 10초
  email: 60000, // 60초
  slack: 15000, // 15초
};

3. 호출 제한

// 레이트 리미팅
const RATE_LIMITS = {
  slack: { requests: 1, per: 1000 }, // 1초당 1회
  email: { requests: 10, per: 60000 }, // 1분당 10회
  "rest-api": { requests: 100, per: 60000 }, // 1분당 100회
};

🔒 보안 고려사항

1. API 키 관리

  • 환경변수로 민감 정보 관리
  • 데이터베이스 암호화 저장
  • API 키 순환 정책

2. 입력값 검증

// 웹훅 URL 화이트리스트
const ALLOWED_WEBHOOK_DOMAINS = [
  "hooks.slack.com",
  "discord.com",
  "outlook.office365.com",
];

const validateWebhookUrl = (url: string): boolean => {
  try {
    const parsed = new URL(url);
    return ALLOWED_WEBHOOK_DOMAINS.some((domain) =>
      parsed.hostname.endsWith(domain)
    );
  } catch {
    return false;
  }
};

3. 로깅 및 감사

  • 모든 외부 호출 로깅
  • 실패 원인 추적
  • 사용량 모니터링

📚 참고 자료

API 문서

SMS/카카오톡 서비스


📅 일정

Week 1

  • 슬랙 웹훅 구현
  • 디스코드 웹훅 구현
  • UI 컴포넌트 확장

Week 2

  • 재시도 메커니즘 구현
  • 호출 로그 시스템 구현
  • 테스트 코드 작성

Week 3

  • SMS 발송 구현 (선택)
  • 성능 최적화
  • 문서화 완성

Week 4

  • 카카오톡 알림톡 구현 (선택)
  • 최종 테스트 및 배포

체크리스트

개발 완료 체크

🚧 현재 상태 (실제)

  • 중첩 드롭다운 UI 구현 (호출 유형 → API 종류 → 맞춤 설정)
  • 타입 정의 개선 (REST API 세부 분류 지원)
  • ExternalCallService 백엔드 서비스 생성 ← 최우선!
  • 실제 HTTP 요청/웹훅 전송 기능
  • 데이터플로우 실행 시 외부 호출 트리거
  • 슬랙 웹훅 구현 (REST API 방식)
  • 카카오톡 API 호출 구현 (REST API 방식)
  • 디스코드 웹훅 구현 (REST API 방식)
  • 일반 REST API 호출 구현
  • 이메일 발송 연동 (Java → Node.js)
  • 오류 처리 및 재시도 메커니즘

배포 준비 체크

  • 환경변수 설정
  • 데이터베이스 마이그레이션
  • 로그 시스템 설정
  • 모니터링 설정
  • 백업 계획 수립

이 계획서를 바탕으로 단계별로 구현해나가면 안정적이고 확장 가능한 외부 호출 시스템을 구축할 수 있을 것입니다! 🚀