AI 서버들(분석 AI, 이미지 생성 AI 등)과의 통신을 전담하는 중계 서버입니다.
이 서버는 백엔드와 여러 AI 서버들 사이의 중계 역할을 수행합니다:
- 소설 분석 AI와 통신
- 이미지 생성 AI와 통신
- 음악 추천 AI와 통신 (BGM 분위기 분석 및 스트리밍 URL 제공)
- 생성된 이미지를 S3에 업로드
- 요청/응답 포맷 변환
- Spring Boot 3.2.0
- Java 17
- Spring WebFlux (AI 서버 통신)
- AWS SDK S3 (이미지 업로드)
- Lombok
relay-server/
├── src/main/java/com/story/relay/
│ ├── RelayServerApplication.java # Main Application
│ ├── controller/
│ │ └── AiController.java # REST API 엔드포인트
│ ├── service/
│ │ ├── AnalysisAiClient.java # 분석 AI 통신
│ │ ├── ImageGenerationAiClient.java # 이미지 AI 통신
│ │ ├── MusicRecommendationAiClient.java # 음악 추천 AI 통신
│ │ └── S3UploadService.java # S3 업로드
│ ├── dto/
│ │ ├── ImageGenerationRequestDto.java
│ │ ├── ImageGenerationResponseDto.java
│ │ ├── MusicRequestDto.java
│ │ └── MusicResponseDto.java
│ └── config/
│ ├── WebClientConfig.java # WebClient 설정
│ └── S3Config.java # S3 Client 설정
└── src/main/resources/
└── application.yml # 애플리케이션 설정
POST /ai/analyze
Content-Type: application/json
{
"novelText": "소설 텍스트..."
}POST /ai/generate
Content-Type: application/json
{
"novelText": "...",
"selectedGaugeIds": ["hope", "trust"],
"numEpisodes": 3,
...
}POST /ai/generate-image
Content-Type: application/json
{
"nodeText": "어두운 복도...",
"situation": "긴장감 넘치는 상황",
"npcEmotions": {
"주인공": "긴장"
},
"episodeTitle": "첫 만남"
}Response:
{
"imageUrl": "https://s3.../images/abc123.png",
"fileKey": "images/abc123.png",
"generatedAt": "2025-12-02T10:30:00Z"
}POST /ai/recommend-music
Content-Type: application/json
{
"prompt": "어두운 복도를 걸어가는 주인공. 긴장감이 감돌고 있다."
}Response:
{
"analysis": {
"primary_mood": "tense",
"secondary_mood": "mysterious",
"intensity": 0.75,
"emotional_tags": ["suspenseful", "dark", "atmospheric"],
"reasoning": "긴장감과 미스터리한 분위기"
},
"music": {
"mood": "tense",
"filename": "tense_01.mp3",
"file_path": "music/tense/tense_01.mp3",
"streaming_url": "https://storage.googleapis.com/.../tense_01.mp3"
}
}GET /ai/health프로젝트 루트에 .env 파일을 생성하고 다음 값들을 설정하세요:
# AI Servers
AI_ANALYSIS_URL=http://localhost:8000
AI_IMAGE_GENERATION_URL=http://localhost:8001
AI_RAG_URL=http://localhost:8002
AI_MUSIC_URL=http://localhost:5001
# AWS S3
AWS_S3_BUCKET=your-bucket-name
AWS_S3_REGION=ap-northeast-2
AWS_ACCESS_KEY=your-access-key
AWS_SECRET_KEY=your-secret-keyOption 1: IntelliJ IDEA
- Run → Edit Configurations
- Environment variables 섹션에
.env파일 내용 복사
Option 2: 터미널에서 실행
# Windows (PowerShell)
$env:AI_ANALYSIS_URL="http://localhost:8000"
$env:AWS_S3_BUCKET="your-bucket"
# ... 기타 변수들
# Linux/Mac
export AI_ANALYSIS_URL=http://localhost:8000
export AWS_S3_BUCKET=your-bucket
# ... 기타 변수들./gradlew build./gradlew bootRun서버는 기본적으로 포트 8081에서 실행됩니다.
./gradlew testcd relay-server
./gradlew bootRuncurl http://localhost:8081/ai/health예상 응답:
{
"status": "healthy",
"relayServer": "up",
"aiServers": {
"analysisAi": {
"status": "up"
},
"imageGenerationAi": {
"status": "up"
},
"musicRecommendationAi": {
"status": "up"
}
}
}curl -X POST http://localhost:8081/ai/generate-image \
-H "Content-Type: application/json" \
-d '{
"nodeText": "어두운 복도",
"situation": "긴장감",
"episodeTitle": "첫 만남"
}'현재는 Mock 응답을 반환합니다 (AI 서버 개발 대기 중):
{
"imageUrl": "https://via.placeholder.com/800x600/1a1a1a/ffffff?text=첫+만남",
"fileKey": "mock/image_uuid.png",
"generatedAt": "2025-12-02T10:30:00Z"
}curl -X POST http://localhost:8081/ai/recommend-music \
-H "Content-Type: application/json" \
-d '{
"prompt": "어두운 복도를 걸어가는 주인공. 긴장감이 감돌고 있다."
}'실제 AI-BGM 서버 연동 시 응답:
{
"analysis": {
"primary_mood": "tense",
"secondary_mood": "mysterious",
"intensity": 0.75,
"emotional_tags": ["suspenseful", "dark", "atmospheric"],
"reasoning": "긴장감과 미스터리한 분위기"
},
"music": {
"mood": "tense",
"filename": "tense_01.mp3",
"file_path": "music/tense/tense_01.mp3",
"streaming_url": "https://storage.googleapis.com/.../tense_01.mp3"
}
}ImageGenerationAiClient.java 파일에서 주석 처리된 실제 구현을 활성화하세요:
// TODO 주석 제거
public ImageGenerationResponseDto generateImage(ImageGenerationRequestDto request) {
// Mock 응답 부분 삭제
// return generateMockImage(request);
// 실제 구현 주석 해제
byte[] imageBytes = imageGenerationAiWebClient.post()
.uri("/generate-image")
.bodyValue(request)
.retrieve()
.bodyToMono(byte[].class)
.timeout(Duration.ofSeconds(30))
.block();
// ... 나머지 코드
}.env파일의 AWS 자격 증명 확인- S3 버킷 권한 확인 (PutObject 권한 필요)
- 리전 설정 확인
- AI 서버가 실행 중인지 확인
.env의 AI 서버 URL 확인- 네트워크 방화벽 확인
application.yml의 타임아웃 설정 조정- AI 서버 응답 시간 확인
- Phase 1: 기본 프로젝트 구조 생성
- Phase 2: WebClient 및 S3 설정
- Phase 3: Mock 이미지 생성 API
- Phase 4: 음악 추천 AI 연동 (BGM 서비스)
- Phase 5: 분석 AI 연동 (기존 기능 이관)
- Phase 6: 실제 이미지 생성 AI 연동
- Phase 7: 에러 핸들링 강화
- Phase 8: 로깅 및 모니터링
- Phase 9: 캐싱 전략
- Phase 10: 성능 테스트
- Phase 11: 프로덕션 배포
- RELAY_SERVER_ARCHITECTURE.md - 상세 아키텍처 설계
- Backend Repository - 메인 백엔드 서버
- AI Server Repository - Python AI 서버
MIT License