솔직히 이번파트는 거의 이해못했다.... 바이브코딩 만쉐이..
🎯 AI 브레인스토밍 플랫폼 개발 일지 - Day 5
📅 작업 일자
2024년 12월 1일 (Day 5)
🎯 오늘의 목표
- ✅ Spring Boot ↔ Python FastAPI 연동
- ✅ 브레인스토밍 전체 플로우 완성
- ✅ 타임아웃 및 DB 이슈 해결
- ✅ Python 코드 고도화 (로깅, Retry, 자동 청소)
✅ 완료 사항
1️⃣ Spring Boot ↔ Python 통합
BrainstormingController 구현
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/brainstorm")
public class BrainstormController {
private final BrainstormingService brainstormingService;
@PostMapping("/generate")
public ResponseEntity<IdeasResponse> generateIdeas(@RequestBody BrainstormRequest request) {
IdeasResponse response = brainstormingService.generate(
request.getUserId(),
request.getPurpose(),
request.getAssociations()
);
return ResponseEntity.ok(response);
}
}
BrainstormingService 구현
@Service
@RequiredArgsConstructor
public class BrainstormingService {
private final RestTemplate restTemplate;
private final IdeaRepository ideaRepository;
private final UserRepository userRepository;
@Value("${python.service.url}")
private String pythonServiceUrl;
public IdeasResponse generate(Long userId, String purpose, List<String> associations) {
// 1. 세션 생성
String sessionId = createSession();
// 2. 목적 입력 (Q1)
submitPurpose(sessionId, purpose);
// 3. 워밍업 질문 생성 (Q2)
getWarmupQuestions(sessionId);
// 4. 워밍업 확인
confirmWarmup(sessionId);
// 5. 자유연상 입력 (Q3)
submitAssociations(sessionId, associations);
// 6. 아이디어 생성
IdeasResponse response = generateIdeas(sessionId);
// 7. DB 저장
saveIdeasToDb(userId, response);
// 8. 세션 삭제
deleteSession(sessionId);
return response;
}
}
핵심 플로우:
Client → Spring Boot → Python FastAPI
↓ ↓ ↓
Request RestTemplate LLM 호출
↓ ↓ ↓
Response ← JSON 파싱 ← JSON 응답
↓
DB 저장 (H2)2️⃣ 타임아웃 문제 해결 🔥
문제 상황
java.net.SocketTimeoutException: Read timed out원인:
- LLM 호출이 4번 발생 (아이디어 생성 1회 + SWOT 분석 3회)
- 예상 시간: 40-80초
- 기존 타임아웃: 30초 ❌
해결
RestTemplateConfig.java 수정:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(5000); // 5초
factory.setReadTimeout(120000); // 120초 (2분) ✅
return new RestTemplate(factory);
}
}
결과: ✅ 타임아웃 해결, 40-80초 내 정상 응답
3️⃣ DB 컬럼 길이 문제 해결
문제 상황
Value too long for column "CONTENT CHARACTER VARYING(255)"원인:
- 기본 VARCHAR(255) 제한
- 아이디어 설명(100-200자) + SWOT 분석(200-400자) = 300-600자 초과
해결
Idea.java 엔티티 수정:
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Idea extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long ideaId;
@Column(nullable = false)
private Long userId;
@Column(nullable = false, length = 500)
private String title;
@Column(columnDefinition = "TEXT") // ← TEXT 타입으로 변경 ✅
private String content;
private String purpose;
}
결과: ✅ 긴 아이디어 + SWOT 분석 정상 저장
4️⃣ Java DTO 구조 수정
문제 상황
Python이 보내는 JSON 구조와 Java DTO 불일치로 파싱 실패
Python 응답:
{
"ideas": [
{
"title": "AI 기반 맞춤형 학습 플래너",
"description": "학생들의 학습 스타일 분석...",
"analysis": "📊 분석 결과:\n• 강점: ...\n• 약점: ..."
}
]
}
Java 기대 (Before):
public static class IdeaDto {
private String title;
private String description;
private SwotDto swot; // ← 객체 타입 ❌
}
해결
IdeasResponse.java 수정:
public static class IdeaDto {
private String title;
private String description;
private String analysis; // ← 문자열 타입 ✅
}
BrainstormingService.java 수정:
private void saveIdeasToDb(Long userId, IdeasResponse response) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다."));
for (IdeasResponse.IdeaDto ideaDto : response.getIdeas()) {
// description + analysis 합치기
String content = ideaDto.getDescription() + "\n\n" + ideaDto.getAnalysis();
Idea idea = Idea.builder()
.user(user)
.title(ideaDto.getTitle())
.content(content)
.purpose(response.getIdeas().get(0).getDescription())
.build();
ideaRepository.save(idea);
}
}
결과: ✅ JSON 파싱 성공, DB 저장 완료
5️⃣ Python 코드 고도화 🚀
새로 생성한 파일들
1. 프롬프트 템플릿 파일
python-service/app/api/v1/endpoints/prompts/idea_generation.txt- 2000+ 줄 프롬프트를 별도 파일로 분리
- 템플릿 변수 사용 (
{purpose},{keywords},{rag_context})
2. LLM 헬퍼 모듈
# python-service/app/api/v1/endpoints/utils/llm_helpers.py
def call_llm_with_retry(client, model, messages, temperature, max_tokens, max_retries=3):
"""
LLM 호출 with Exponential Backoff Retry
"""
for attempt in range(1, max_retries + 1):
try:
logger.info(f"LLM 호출 시도 {attempt}/{max_retries}")
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens
)
logger.info(f"LLM 호출 성공 (시도 {attempt})")
return response.choices[0].message.content.strip()
except Exception as e:
logger.error(f"LLM 호출 실패 (시도 {attempt}): {e}")
if attempt < max_retries:
wait_time = 2 ** attempt # 2초, 4초, 8초
logger.info(f"{wait_time}초 대기 후 재시도...")
time.sleep(wait_time)
else:
raise
3. Dependencies 모듈
# python-service/app/api/v1/endpoints/dependencies.py
def get_session_or_404(session_id: str) -> Dict:
"""
세션 존재 여부 확인 (Dependency)
"""
from session_manager import SessionManager
session_manager = SessionManager()
session = session_manager.get_session(session_id)
if not session:
raise HTTPException(status_code=404, detail="세션을 찾을 수 없습니다.")
return session
개선된 brainstorming.py
로깅 시스템 추가:
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
Depends 패턴 적용:
@router.get("/warmup/{session_id}", response_model=WarmupResponse)
async def get_warmup_questions(
session_id: str,
session: dict = Depends(get_session_or_404) # ← 세션 자동 검증
):
purpose = session.get('q1_purpose')
# ...
자동 세션 청소:
@router.post("/session", response_model=SessionResponse)
async def create_session():
"""
새로운 세션 시작
5분 이상 된 빈 폴더 자동 정리
"""
logger.info("🧹 오래된 Ephemeral 세션 폴더 청소 시작...")
ephemeral_base_dir = Path(...) / "data" / "ephemeral"
if ephemeral_base_dir.exists():
deleted_count = 0
current_time = time.time()
cutoff_time = current_time - 300 # 5분
for session_dir in ephemeral_base_dir.iterdir():
if not session_dir.is_dir():
continue
mtime = session_dir.stat().st_mtime
if mtime < cutoff_time:
files = list(session_dir.iterdir())
if len(files) == 0: # 빈 폴더만
shutil.rmtree(session_dir)
deleted_count += 1
if deleted_count > 0:
logger.info(f"✅ {deleted_count}개의 오래된 빈 폴더 삭제됨")
# 새 세션 생성
session_id = session_manager.create_session()
return SessionResponse(session_id=session_id, message="...")
Retry 로직 적용:
# Before
response = openai_client.chat.completions.create(...)
content = response.choices[0].message.content
# After
content = call_llm_with_retry(
client=openai_client,
model=llm_model,
messages=[...],
temperature=0.7,
max_tokens=2000
)
6️⃣ 통합 테스트 성공 ✅
Postman 요청
POST http://localhost:8080/api/brainstorm/generate
Content-Type: application/json
{
"userId": 1,
"purpose": "학생들을 위한 학습 앱 아이디어",
"associations": ["학습", "AI", "맞춤형", "학생", "효율"]
}
응답 (40-80초 소요)
{
"ideas": [
{
"title": "AI 기반 맞춤형 학습 플래너",
"description": "💡 핵심 문제:\n학생들은 자신에게 맞는 학습 방법을 몰라 비효율적으로 공부합니다...",
"analysis": "📊 분석 결과:\n• 강점: AI가 학습 패턴 분석하여 개인별 최적 학습법 제안\n• 약점: 초기 데이터 수집 기간 필요..."
},
{
"title": "학습 스타일 진단 및 추천 앱",
"description": "...",
"analysis": "..."
},
{
"title": "실시간 학습 도우미 챗봇",
"description": "...",
"analysis": "..."
}
]
}
Python 서버 로그
2024-12-01 18:21:49 - INFO - ✅ 새 세션 생성: 4a8a627f-ae0b-4586-af56-502f6262bdb2
2024-12-01 18:21:49 - INFO - ✅ 목적 입력 완료: 4a8a627f-ae0b-4586-af56-502f6262bdb2
2024-12-01 18:21:49 - INFO - 🤔 워밍업 질문 생성 시작
2024-12-01 18:21:50 - INFO - ✅ 워밍업 질문 생성 완료: 3개
2024-12-01 18:21:50 - INFO - 📝 자유연상 입력 시작
2024-12-01 18:21:53 - INFO - ✅ 자유연상 입력 완료: 5개 키워드
2024-12-01 18:21:53 - INFO - 💡 아이디어 생성 시작
2024-12-01 18:22:07 - INFO - ✅ 아이디어 생성 완료: 3개
2024-12-01 18:22:07 - INFO - 🗑️ 세션 삭제 시작
2024-12-01 18:22:07 - INFO - ✅ 세션 삭제 완료DB 저장 확인
SELECT * FROM idea;
ideaId | userId | title | content (TEXT)
-------|--------|--------------------------------|------------------
1 | 1 | AI 기반 맞춤형 학습 플래너 | 💡 핵심 문제:...
2 | 1 | 학습 스타일 진단 및 추천 앱 | ...
3 | 1 | 실시간 학습 도우미 챗봇 | ...
전체 플로우 완벽 동작! 🎉
💡 배운 점 & 트러블슈팅
1. RestTemplate 타임아웃 설정
왜 중요한가?
- LLM 호출은 시간이 오래 걸림 (10-60초)
- 기본 타임아웃(30초)으로는 부족
- 읽기 타임아웃 ≠ 연결 타임아웃
설정 가이드:
factory.setConnectTimeout(5000); // 연결 타임아웃: 짧게
factory.setReadTimeout(120000); // 읽기 타임아웃: 길게
2. JPA 컬럼 타입 설정
VARCHAR vs TEXT:
| 타입 | 길이 | 용도 |
|------|------|------|
| VARCHAR(255) | 255자 | 제목, 이름, 이메일 |
| VARCHAR(500) | 500자 | 짧은 설명 |
| TEXT | 무제한 | 긴 내용, 본문 |
설정 방법:
@Column(length = 500) // VARCHAR(500)
@Column(columnDefinition = "TEXT") // TEXT
3. Python Logging Best Practice
계층별 로그 레벨:
logger.debug("상세 디버그") # 개발 시
logger.info("✅ 작업 완료") # 정상 플로우
logger.warning("⚠️ 주의") # 경고
logger.error("❌ 실패") # 에러
이모지로 시각화:
- 🧹 청소 작업
- ✅ 성공
- ❌ 실패
- 🤔 처리 중
- 💡 아이디어 생성
4. FastAPI Dependencies 패턴
Before (코드 중복):
@router.get("/warmup/{session_id}")
async def get_warmup(session_id: str):
session = session_manager.get_session(session_id)
if not session:
raise HTTPException(404, "세션 없음")
# ...
@router.post("/associations/{session_id}")
async def submit_associations(session_id: str):
session = session_manager.get_session(session_id)
if not session:
raise HTTPException(404, "세션 없음")
# ...
After (Depends 사용):
@router.get("/warmup/{session_id}")
async def get_warmup(
session_id: str,
session: dict = Depends(get_session_or_404) # ← 자동 검증
):
# session은 이미 검증됨!
@router.post("/associations/{session_id}")
async def submit_associations(
session_id: str,
session: dict = Depends(get_session_or_404)
):
# 코드 중복 제거!
5. Exponential Backoff Retry
왜 필요한가?
- LLM API는 가끔 실패함 (네트워크, Rate Limit 등)
- 즉시 재시도하면 계속 실패
- 점진적으로 대기 시간 늘리기
구현:
retry_count = 0
wait_time = 2
while retry_count < 3:
try:
response = api_call()
break
except Exception:
retry_count += 1
time.sleep(wait_time)
wait_time *= 2 # 2초 → 4초 → 8초
📊 프로젝트 현황
완료된 기능
✅ Spring Boot Backend (13개 API)
✅ Python FastAPI (브레인스토밍 API)
✅ Spring ↔ Python 통합
✅ LLM 기반 아이디어 생성
✅ SWOT 분석 자동화
✅ DB 저장 (H2)
✅ 타임아웃 처리
✅ 에러 핸들링 (Retry)
✅ 로깅 시스템
✅ 자동 세션 청소
디렉토리 구조
brainstorming-platform/
├── src/main/java/.../
│ ├── domain/
│ │ ├── user/ ✅
│ │ ├── idea/ ✅
│ │ ├── inquiry/ ✅
│ │ └── brainstorming/ ✅ NEW!
│ │ ├── controller/BrainstormController.java
│ │ ├── service/BrainstormingService.java
│ │ └── dto/
│ │ ├── BrainstormRequest.java
│ │ └── IdeasResponse.java
│ ├── config/
│ │ └── RestTemplateConfig.java ✅
│ └── global/
│ └── BaseEntity.java
│
└── python-service/
├── app/
│ ├── api/v1/endpoints/
│ │ ├── brainstorming.py ✅ 개선됨!
│ │ ├── dependencies.py ✅ NEW!
│ │ ├── utils/
│ │ │ └── llm_helpers.py ✅ NEW!
│ │ └── prompts/
│ │ └── idea_generation.txt ✅ NEW!
│ └── domain/brainstorming/
│ ├── session_manager.py
│ ├── ephemeral_rag.py
│ └── data/
│ ├── chroma/ (영구 RAG)
│ └── ephemeral/ (임시 세션, 자동 청소됨)
└── main.py🎯 다음 단계 (Day 6)
우선순위 1: 프론트엔드 개발 🎨
기술 스택 후보:
- React (대중적, 취업 유리)
- Vue.js (쉬움)
- Next.js (SSR)
필수 화면:
- 로그인/회원가입
- 메인 대시보드
- 브레인스토밍 채팅 UI
- 아이디어 관리
우선순위 2: OAuth2 로그인 (선택)
- Google OAuth2
- Kakao OAuth2
- JWT 토큰
우선순위 3: 배포
- Frontend: Vercel or Netlify
- Backend: AWS EC2 or Heroku
- Python: Docker + EC2
📚 참고 자료
🔗 GitHub
- Repository:
brainstorming-platform - Commit:
Day 5 - Spring-Python 통합 완성 및 고도화
💭 회고
잘한 점
- 🔥 Spring ↔ Python 통합 성공 (핵심 목표 달성!)
- 🐛 타임아웃, DB 컬럼 길이 문제 빠르게 해결
- 🚀 Python 코드를 단순 동작에서 → 프로덕션 수준으로 개선
- 📊 상세한 로깅으로 디버깅 용이
- 🧹 자동 세션 청소로 불필요한 데이터 관리
개선할 점
- 예외 처리를 더 세분화 (사용자 친화적 에러 메시지)
- 테스트 코드 추가 (MockMvc, pytest)
- API 응답 시간 최적화 (캐싱 고려)
- 프론트엔드 없이 Postman으로만 테스트하는 한계
느낀 점
"백엔드 두 개를 연결하는 게 생각보다 어려웠다."
- 타임아웃: LLM 호출이 예상보다 오래 걸림
- JSON 파싱: Python과 Java의 DTO 구조 맞추기
- 에러 추적: 어느 서버에서 에러가 났는지 파악하기
하지만:
- ✅ 실제로 동작하는 AI 기능 완성!
- ✅ 로그를 보면서 전체 플로우 이해
- ✅ 프로덕션 수준의 에러 핸들링 경험
"이제 프론트엔드만 만들면 완성!" 🎉
🏆 Day 5 완료!
오늘의 성과:
- Spring ↔ Python 통합 완성 ✅
- 전체 브레인스토밍 플로우 동작 ✅
- 타임아웃/DB 이슈 해결 ✅
- Python 코드 고도화 ✅
- 자동 세션 청소 구현 ✅
내일은 프론트엔드 개발을 시작합니다! 🚀
💡 Tip: 이 프로젝트의 핵심은 "Spring Boot와 Python FastAPI를 RestTemplate로 연결하는 방법"입니다. 같은 방식으로 어떤 외부 API든 통합할 수 있습니다!
'그래서 일단 프로젝트 > 개인프로젝트-IdeaMaker(클로드 작성본 md 모음)' 카테고리의 다른 글
| 기록 8 . 관리자 기능 바이브코딩으로 만들고 준비작업 (0) | 2025.12.03 |
|---|---|
| 기록 7 . OAuth2.0 / JWT 구현...너무 어려운데 ?? (0) | 2025.12.02 |
| 기록 5 . 컨트롤러 계층 완성 및 테스트완료 (0) | 2025.11.27 |
| 기록 4 . DTO 작성 및 Test 코드 작성 단계 (0) | 2025.11.26 |
| 기록 3 . Java Service 계층과 Repository 계층 작성 (0) | 2025.11.25 |