외부 API 설정 (ExternalApiConfig)
개요
ExternalApiConfig는 애플리케이션에서 사용하는 외부 API 서비스의 연결 설정을 관리합니다. OpenAI, Google API, SendGrid, ConnectDTX 등 외부 서비스 연동에 필요한 API 키, 엔드포인트, 타임아웃 등의 설정을 정의합니다.
구성 요소
OpenAI 설정
| 설정 키 | 환경 변수 | 타입 | 필수 여부 | 기본값 | 설명 |
|---|---|---|---|---|---|
openai.apiKey | OPENAI_API_KEY | string | 필수* | - | OpenAI API 키 |
openai.model | OPENAI_MODEL | string | 선택 | 'gpt-4' | 사용할 OpenAI 모델 |
openai.timeout | OPENAI_TIMEOUT | number | 선택 | 30000 | API 요청 타임아웃(ms) |
openai.maxTokens | OPENAI_MAX_TOKENS | number | 선택 | 2048 | 최대 토큰 수 |
* OpenAI 기능을 사용하는 경우 필수
Google API 설정
| 설정 키 | 환경 변수 | 타입 | 필수 여부 | 기본값 | 설명 |
|---|---|---|---|---|---|
google.apiKey | GOOGLE_API_KEY | string | 필수* | - | Google API 키 |
google.searchEngineId | GOOGLE_SEARCH_ENGINE_ID | string | 필수* | - | Google Custom Search Engine ID |
* Google 검색 기능을 사용하는 경우 필수
SendGrid 설정
| 설정 키 | 환경 변수 | 타입 | 필수 여부 | 기본값 | 설명 |
|---|---|---|---|---|---|
sendgrid.apiKey | SENDGRID_API_KEY | string | 필수* | - | SendGrid API 키 |
sendgrid.fromEmail | SENDGRID_FROM_EMAIL | string | 필수* | - | 발신자 이메일 주소 |
sendgrid.templateId | SENDGRID_TEMPLATE_ID | string | 선택 | - | 기본 이메일 템플릿 ID |
* 이메일 발송 기능을 사용하는 경우 필수
결제 시스템 설정
| 설정 키 | 환경 변수 | 타입 | 필수 여부 | 기본값 | 설명 |
|---|---|---|---|---|---|
payment.provider | PAYMENT_PROVIDER | string | 선택 | 'stripe' | 결제 제공자 |
payment.apiKey | PAYMENT_API_KEY | string | 필수* | - | 결제 API 키 |
payment.secretKey | PAYMENT_SECRET_KEY | string | 필수* | - | 결제 시크릿 키 |
payment.webhookSecret | PAYMENT_WEBHOOK_SECRET | string | 선택 | - | 웹훅 인증 시크릿 |
* 결제 기능을 사용하는 경우 필수
Atlassian 설정
| 설정 키 | 환경 변수 | 타입 | 필수 여부 | 기본값 | 설명 |
|---|---|---|---|---|---|
atlassian.baseUrl | ATLASSIAN_BASE_URL | string | 필수* | - | Atlassian 인스턴스 URL |
atlassian.username | ATLASSIAN_USERNAME | string | 필수* | - | 사용자 이름 |
atlassian.apiToken | ATLASSIAN_API_TOKEN | string | 필수* | - | API 토큰 |
* Atlassian 서비스(Jira, Confluence)를 사용하는 경우 필수
ConnectDTX 설정
| 설정 키 | 환경 변수 | 타입 | 필수 여부 | 기본값 | 설명 |
|---|---|---|---|---|---|
connectDtx.baseUrl | CONNECT_DTX_BASE_URL | string | 필수* | - | ConnectDTX API 기본 URL |
connectDtx.apiKey | CONNECT_DTX_API_KEY | string | 필수* | - | ConnectDTX API 키 |
connectDtx.timeout | CONNECT_DTX_TIMEOUT | number | 선택 | 30000 | API 요청 타임아웃(ms) |
* ConnectDTX 서비스를 사용하는 경우 필수
사용 예시
구성 정의
// libs/core/config/src/lib/namespaces/external-api.config.ts
import { registerAs } from '@nestjs/config';
export const externalApiConfig = registerAs('externalApi', () => ({
openai: {
apiKey: process.env.OPENAI_API_KEY,
model: process.env.OPENAI_MODEL || 'gpt-4',
timeout: parseInt(process.env.OPENAI_TIMEOUT || '30000', 10),
maxTokens: parseInt(process.env.OPENAI_MAX_TOKENS || '2048', 10),
},
google: {
apiKey: process.env.GOOGLE_API_KEY,
searchEngineId: process.env.GOOGLE_SEARCH_ENGINE_ID,
},
sendgrid: {
apiKey: process.env.SENDGRID_API_KEY,
fromEmail: process.env.SENDGRID_FROM_EMAIL,
templateId: process.env.SENDGRID_TEMPLATE_ID,
},
payment: {
provider: process.env.PAYMENT_PROVIDER || 'stripe',
apiKey: process.env.PAYMENT_API_KEY,
secretKey: process.env.PAYMENT_SECRET_KEY,
webhookSecret: process.env.PAYMENT_WEBHOOK_SECRET,
},
atlassian: {
baseUrl: process.env.ATLASSIAN_BASE_URL,
username: process.env.ATLASSIAN_USERNAME,
apiToken: process.env.ATLASSIAN_API_TOKEN,
},
connectDtx: {
baseUrl: process.env.CONNECT_DTX_BASE_URL,
apiKey: process.env.CONNECT_DTX_API_KEY,
timeout: parseInt(process.env.CONNECT_DTX_TIMEOUT || '30000', 10),
},
}));
유효성 검증 스키마
// libs/core/config/src/lib/schemas/external-api.schema.ts
import * as Joi from 'joi';
export const externalApiConfigSchema = {
// OpenAI 설정
OPENAI_API_KEY: Joi.string().optional(),
OPENAI_MODEL: Joi.string().default('gpt-4'),
OPENAI_TIMEOUT: Joi.number().default(30000),
OPENAI_MAX_TOKENS: Joi.number().default(2048),
// Google API 설정
GOOGLE_API_KEY: Joi.string().optional(),
GOOGLE_SEARCH_ENGINE_ID: Joi.string().optional(),
// SendGrid 설정
SENDGRID_API_KEY: Joi.string().optional(),
SENDGRID_FROM_EMAIL: Joi.string().email().optional(),
SENDGRID_TEMPLATE_ID: Joi.string().optional(),
// 결제 시스템 설정
PAYMENT_PROVIDER: Joi.string().default('stripe'),
PAYMENT_API_KEY: Joi.string().optional(),
PAYMENT_SECRET_KEY: Joi.string().optional(),
PAYMENT_WEBHOOK_SECRET: Joi.string().optional(),
// Atlassian 설정
ATLASSIAN_BASE_URL: Joi.string().uri().optional(),
ATLASSIAN_USERNAME: Joi.string().optional(),
ATLASSIAN_API_TOKEN: Joi.string().optional(),
// ConnectDTX 설정
CONNECT_DTX_BASE_URL: Joi.string().uri().optional(),
CONNECT_DTX_API_KEY: Joi.string().optional(),
CONNECT_DTX_TIMEOUT: Joi.number().default(30000),
};
OpenAI 서비스에서 사용
// OpenAI 설정 사용 예시
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@core/config';
import { OpenAI } from 'openai';
@Injectable()
export class OpenAiService {
private openai: OpenAI;
private model: string;
private maxTokens: number;
constructor(private readonly configService: ConfigService) {
// OpenAI 설정 로드
const openaiConfig = this.configService.getConfig<{
apiKey: string;
model: string;
timeout: number;
maxTokens: number;
}>('externalApi', 'openai');
// OpenAI 클라이언트 초기화
if (openaiConfig.apiKey) {
this.openai = new OpenAI({
apiKey: openaiConfig.apiKey,
timeout: openaiConfig.timeout,
});
this.model = openaiConfig.model;
this.maxTokens = openaiConfig.maxTokens;
} else {
console.warn('OpenAI API key is not configured');
}
}
async generateText(prompt: string): Promise<string> {
if (!this.openai) {
throw new Error('OpenAI client is not initialized');
}
const response = await this.openai.chat.completions.create({
model: this.model,
messages: [{ role: 'user', content: prompt }],
max_tokens: this.maxTokens,
});
return response.choices[0]?.message.content || '';
}
}
SendGrid 서비스에서 사용
// SendGrid 설정 사용 예시
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@core/config';
import * as SendGrid from '@sendgrid/mail';
@Injectable()
export class EmailService {
private fromEmail: string;
private defaultTemplateId?: string;
constructor(private readonly configService: ConfigService) {
// SendGrid 설정 로드
const sendgridConfig = this.configService.getConfig<{
apiKey: string;
fromEmail: string;
templateId?: string;
}>('externalApi', 'sendgrid');
// SendGrid 클라이언트 초기화
if (sendgridConfig.apiKey) {
SendGrid.setApiKey(sendgridConfig.apiKey);
this.fromEmail = sendgridConfig.fromEmail;
this.defaultTemplateId = sendgridConfig.templateId;
} else {
console.warn('SendGrid API key is not configured');
}
}
async sendEmail(to: string, subject: string, text: string, html?: string): Promise<void> {
if (!this.fromEmail) {
throw new Error('SendGrid is not properly configured');
}
await SendGrid.send({
to,
from: this.fromEmail,
subject,
text,
html: html || text,
});
}
async sendTemplateEmail(to: string, templateData: Record<string, string>, templateId?: string): Promise<void> {
if (!this.fromEmail) {
throw new Error('SendGrid is not properly configured');
}
await SendGrid.send({
to,
from: this.fromEmail,
templateId: templateId || this.defaultTemplateId,
dynamicTemplateData: templateData,
});
}
}
환경별 설정 가이드
로컬 개발 환경
로컬 개발 환경에서는 서비스별 JSON 설정 파일을 통해 외부 API 설정을 구성합니다. 일반적으로 테스트용 API 키와 샌드박스 서비스를 사용합니다.
// cloudrun-deploy/secret/local/dev/dta-wide-api.json
{
"OPENAI_API_KEY": "sk-svcacct-xGwdG0mAWv976uDb8sVFk4ngPYrV4GIiqKA8HRwmTjOi1Yeh56ypILscCIthPhuwNmC-B6BpOH7T3BlbkFJYp9GgcjCYx_OBtUsz5CmKrZyj8sVuOZNLf1IhdLmawklIorfuU0vVdrUABzy474MZl5Gv_2FxMAA",
"OPENAI_MODEL": "gpt-4o",
"OPENAI_TIMEOUT": "60000",
"GOOGLE_API_KEY": "AIzaSyCFTubRV_CyuC2mSFJlWhJ6GFA_zEFgZys",
"GOOGLE_CSE_ID": "dev84214429cc54b4cf8",
"SENDGRID_API_KEY": "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",
"EMAIL_SENDER": "info@weltcorp.com",
"IMP_CODE": "imp67136621",
"IMP_KEY": "0201154055364850",
"IMP_SECRET": "9DcoGeSKzCfYWwSD9IDV0LZJ0eG8gqII1WsWEOVFLrGzCgTe6JAdZJTMZrVGkWV6TO4h6Pqkzgvqmexw",
"ATLASSIAN_API_KEY": "ATATT3xFfGF0tWgcTwY8tYz60vorIzl3FX3-P5_J8mCd83C9VeAS472jITJbGRbpqL0jCvKZeTwY1247RwptRwNH-E0pwy3d2RDDF6q2gpfgDU_qB2FfiF-9LxEbB8VcgnqwMpKlOrJR-s6gQRb4S9WLR32DK-JeapUl-wZq6OiTYm1xPmiY_Ok=5F5C6969",
"ATLASSIAN_WORKSPACE_URL": "https://weltcorp.atlassian.net",
"ATLASSIAN_USER_NAME": "bok@weltcorp.com",
"CONNECTDTX_API_HOST": "test-api.connectdtx.net",
"CONNECTDTX_API_CLIENT_ID": "59f078202354f5df2431",
"CONNECTDTX_API_SECRET": "K3RbQH9v8X_Osu_He71OIg",
"CONNECT_DTX_TIMEOUT": "30000"
}
클라우드 런 환경
클라우드 런 환경에서는 서비스별 JSON 설정 파일을 통해 실제 프로덕션 API 키와 보안 설정을 사용합니다. 이 설정은 배포 프로세스에서 Cloud Run 서비스의 환경 변수로 자동 설정됩니다.
// cloudrun-deploy/secret/cloudrun/prod/dta-wide-api.json
{
"OPENAI_API_KEY": "${SECRET_OPENAI_API_KEY}",
"OPENAI_MODEL": "gpt-4o",
"OPENAI_TIMEOUT": "30000",
"GOOGLE_API_KEY": "${SECRET_GOOGLE_API_KEY}",
"GOOGLE_CSE_ID": "${SECRET_GOOGLE_SEARCH_ENGINE_ID}",
"SENDGRID_API_KEY": "${SECRET_SENDGRID_API_KEY}",
"EMAIL_SENDER": "no-reply@weltcorp.com",
"IMP_CODE": "${SECRET_IMP_CODE}",
"IMP_KEY": "${SECRET_IMP_KEY}",
"IMP_SECRET": "${SECRET_IMP_SECRET}",
"ATLASSIAN_API_KEY": "${SECRET_ATLASSIAN_API_KEY}",
"ATLASSIAN_WORKSPACE_URL": "https://weltcorp.atlassian.net",
"ATLASSIAN_USER_NAME": "${SECRET_ATLASSIAN_USER_NAME}",
"CONNECTDTX_API_HOST": "api.connectdtx.net",
"CONNECTDTX_API_CLIENT_ID": "${SECRET_CONNECTDTX_API_CLIENT_ID}",
"CONNECTDTX_API_SECRET": "${SECRET_CONNECTDTX_API_SECRET}"
}
참고: 프로덕션 환경에서는 민감한 API 키를 직접 JSON 파일에 저장하지 않고, 배포 과정에서 Secret Manager에서 로드하여 환경 변수로 설정하는 방식을 사용할 수 있습니다. 위 예시의
${SECRET_XXX}부분은 이러한 치환이 일어날 자리 표시자입니다.
보안 고려 사항
외부 API 설정은 API 키, 시크릿 키 등 민감한 정보를 포함하므로 특별한 보안 조치가 필요합니다:
-
API 키 관리:
- 프로덕션 환경에서는 모든 API 키와 시크릿을 Google Cloud Secret Manager에 저장하고 환경 변수로 주입합니다.
- 개발 환경에서는
.env파일에 저장하고 이 파일은 버전 관리 시스템에 포함하지 않습니다.
-
키 순환:
- 정기적으로 API 키를 변경하는 정책을 수립합니다.
- 직원 퇴사, 보안 사고 발생 시 즉시 모든 API 키를 교체합니다.
-
최소 권한 원칙:
- 각 API 서비스에 필요한 최소한의 권한만 부여합니다.
- 가능한 경우 읽기 전용 API 키를 사용합니다.
-
API 키 노출 방지:
- 클라이언트 측으로 API 키를 전송하지 않습니다.
- 로그에 API 키가 기록되지 않도록 합니다.
- 오류 메시지에 API 키 정보가 노출되지 않도록 합니다.
주의 사항
- 의존성 관리: 외부 API에 과도하게 의존하지 않도록 설계하고, API 장애 시 대체 방안을 마련합니다.
- 요금 모니터링: API 호출 횟수와 비용을 모니터링하여 예상치 못한 비용 증가를 방지합니다.
- 제한율 관리: API 제한율(Rate Limit)을 고려하여 적절한 호출 빈도를 유지합니다.
- 타임아웃 설정: 모든 API 호출에 적절한 타임아웃을 설정하여 애플리케이션 성능에 영향을 미치지 않도록 합니다.
- 에러 처리: 외부 API 오류를 적절히 처리하고, 실패 시 재시도 메커니즘을 구현합니다.