Project Detail
HomeWiz
HomeWiz — Side Project
생성형 AI가 공간 레이아웃과 추천 소품을 제안하고, 홈피드에서 유저들이 결과물을 공유하며 성장하는 경험을 제공합니다.
- LLM 기반 가상 인테리어
- Langchain, LLM 기반 채팅 봇
- 연관 상품 추천
- 커뮤니티 성장 분석
LLM 기반 방 가상 인테리어 커뮤니티 서비스입니다. 생성형 추천으로 개인화된 룸 스타일을 제안하고 커뮤니티 피드백으로 경험을 확장합니다.
GEN AI
생성형 추천 엔진
LLM 프롬프트 템플릿과 Two-Stage 프롬프트 엔지니어링으로 구조를 유지한 채 실시간 인테리어 추천을 제공합니다.
Prompt Orchestration, Two-Stage Generation
VISUAL SEARCH
비주얼 오브젝트 서치
COCO-SSD 모델을 활용하여 클라이언트 브라우저에서 비용 없이 실시간으로 가구를 인식합니다.
COCO-SSD
COMMUNITY
커뮤니티 성장 분석
활동 지표를 비동기 배치로 집계하고 Growth KPI를 대시보드에서 추적합니다.
Batch Metrics, Growth Dashboard
HomeWiz 프론트오피스 — 랜딩 1
1 / 103프로젝트 개요
서비스 목표
- LLM을 활용해 사용자가 원하는 인테리어 스타일을 자연어로 설명하면 3D 룸 레이아웃과 추천 소품을 제안합니다.
- 커뮤니티 참여자가 결과물을 공유하고 피드백을 주고받을 수 있도록 피드 구조를 설계했습니다.
역할과 범위
- 기능 정의, UX 플로우 설계, 백엔드/프론트엔드 전반의 개발을 담당했습니다.
- 인테리어 추천 모델의 프롬프트 엔지니어링과 데이터 정제 파이프라인을 구축했습니다.
- Redis 캐시와 큐를 사용해 재고 정보 동기화 시간을 5분 이내로 유지했습니다.
Java 21 Virtual Threads & Asynchronous Architecture
배경 및 문제 의식
HomeWiz는 고해상도 이미지 생성(AI)과 S3 파일 업로드, 외부 결제 승인 등 Long-running I/O 작업이 빈번한 플랫폼입니다. 기존 Spring Boot(Tomcat)의 Thread-per-Request 모델은 요청마다 고가의 Platform Thread를 점유하므로, I/O 대기 시간이 길어질수록 스레드 풀(default 200)이 빠르게 고갈되어 Throughput(처리량)이 급격히 저하되는 문제가 있었습니다.
핵심 해결 전략: Virtual Threads + CompletableFuture
Java 21의 가상 스레드(Virtual Threads)는 OS 스레드와 1:1 매핑되지 않고, JVM 레벨에서 스케줄링되는 경량 스레드입니다. 이를 도입하여 블로킹 I/O 발생 시 해당 가상 스레드만 park 시키고, 캐리어 스레드(Platform Thread)는 다른 작업을 처리하도록 하여 리소스 효율을 극대화했습니다.
하지만 단순히
spring.threads.virtual.enabled=true만으로는 부족했습니다. 컨트롤러가 응답을 기다리는 동안 Tomcat 워커 스레드를 붙잡고 있는 구조를 탈피해야 했습니다.AsyncResponseHelper
가상 스레드를 명시적으로 활용하고, 타임아웃 처리를 일원화하기 위한 헬퍼 컴포넌트입니다.
1@Slf4j 2@Component 3public class AsyncResponseHelper { 4 5 // Virtual Thread Executor 6 private final Executor virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor(); 7 8 @Value("${homewiz.async.default-timeout:30000}") 9 private long defaultTimeout; 10 11 /** 12 * 가상 스레드에서 supplyAsync를 실행하고, CompletableFuture를 반환. 13 * I/O 블로킹 시 가상 스레드만 대기하며, Tomcat 스레드는 즉시 해제됨. 14 */ 15 public <T> CompletableFuture<ResponseEntity<T>> async(Supplier<ResponseEntity<T>> supplier) { 16 return CompletableFuture.supplyAsync(supplier, virtualThreadExecutor) 17 .orTimeout(defaultTimeout, TimeUnit.MILLISECONDS) 18 .exceptionally(ex -> { 19 log.error("Async execution failed", ex); 20 return ResponseEntity.internalServerError().build(); 21 }); 22 } 23}
Security Context Propagation
가상 스레드는 스레드 풀 방식과 달리 Task 단위로 생성되므로,
ThreadLocal 기반의 Spring Security 인증 정보가 전파되지 않는 문제가 있습니다. 이를 해결하기 위해 Decorator Pattern을 적용했습니다.1public class SecurityContextAwareRunnable implements Runnable { 2 private final Runnable delegate; 3 private final SecurityContext securityContext; 4 5 public SecurityContextAwareRunnable(Runnable delegate) { 6 this.delegate = delegate; 7 this.securityContext = SecurityContextHolder.getContext(); // 캡처 8 } 9 10 @Override 11 public void run() { 12 try { 13 SecurityContextHolder.setContext(securityContext); // 복원 14 delegate.run(); 15 } finally { 16 SecurityContextHolder.clearContext(); 17 } 18 } 19}
성능 개선 효과
JMeter를 이용한 부하 테스트 결과, 동일한 하드웨어 리소스(2 Core, 4GB RAM)에서 눈에 띄는 성능 향상을 확인했습니다.
| 구분 | Platform Threads (Tomcat Default) | Virtual Threads (Java 21) | 개선율 |
|---|---|---|---|
| Max Concurrent Users | 450 users | 3,200 users | 711% |
| Avg Latency (AI Mock) | 2.1s | 1.2s | 42% |
| Thread Count | 455 threads (High Context Switch) | 24 threads (Carrier Threads) | Resource Saved |
결론 및 인사이트
Java 21 Virtual Threads는 "Reactive Programming(WebFlux)의 복잡도 없이 동기 스타일 코드의 가독성을 유지하면서 비동기의 성능을 얻을 수 있는" 강력한 도구입니다. 다만
synchronized 블록이나 ThreadLocal 오남용 시 Pinning 이슈가 발생할 수 있어, 라이브러리 선정과 코드 작성 시 주의가 필요합니다. HomeWiz는 이를 위해 Redisson(Redis Lock)과 같은 Non-blocking 락을 적극 활용했습니다.RAG(Retrieval-Augmented Generation) Chatbot Architecture
개요
HomeWiz는 사용자의 인테리어 관련 질문에 전문적이고 정확한 답변을 제공하기 위해 RAG(검색 증강 생성) 기반의 AI 챗봇을 구축했습니다. 단순한 LLM(ChatGPT) 사용 시 발생하는 환각(Hallucination) 현상을 방지하고, HomeWiz 플랫폼의 최신 가이드와 정책을 반영하기 위함입니다.
시스템 아키텍처 (Hybrid Microservice)
Java 기반의 메인 백엔드와 별도로, 방대한 문서 처리와 LLM 연동에 최적화된 Python(FastAPI) 기반의 RAG 서비스를 마이크로서비스로 분리했습니다.
왜 Python을 선택했는가?
Spring Boot 생태계(LangChain4j)도 존재하지만, LangChain의 파이썬 생태계가 훨씬 방대하고(Document Loader, Text Splitter 등), 최신 LLM 기능을 가장 빨리 지원하기 때문에 RAG 코어 로직은 Python으로 구현하고 Spring Boot와 통신하는 구조를 채택했습니다.
벡터 검색 엔진 (OpenSearch)
단순 코사인 유사도 검색만으로는 키워드 매칭이 정확하지 않은 경우가 많아, Hybrid Search 전략을 사용했습니다.
- Vector Search: 문맥적 의미(Semantic) 유사성 검색 (임베딩 모델:
text-embedding-3-small) - Keyword Search: OpenSearch의 Nori(한글 형태소 분석기)를 활용한 키워드 매칭
- Reranking: 두 검색 결과를 가중치(Vector 0.7 + Keyword 0.3)를 두어 재정렬
1# Hybrid Search Logic (Simplified) 2def hybrid_search(query, k=5): 3 vector_results = vector_store.similarity_search(query, k=k) 4 keyword_results = opensearch_keyword_search(query, k=k) 5 6 # Reciprocal Rank Fusion (RRF) 7 combined_score = rrf_merge(vector_results, keyword_results) 8 return combined_score[:k]
문서 수집 및 청킹 (Ingestion Pipeline)
문서의 구조를 파괴하지 않고 의미 단위로 자르는 것이 RAG 성능의 핵심입니다.
- Markdown Header Splitting:
.md파일의#헤더를 기준으로 1차 분할하여 문맥 그룹화. - Recursive Character Splitter: 텍스트 길이가 길 경우 문장/단락 단위로 재귀적 분할 (Chunk Size: 1000, Overlap: 200).
성능 최적화 및 운영 경험
- 캐싱(Caching): 동일한 질문에 대한 LLM 비용 절감을 위해 Redis에
(질문 임베딩) -> (답변)캐싱 적용. (유사도 0.95 이상 시 캐시 히트) - Fallback Strategy: RAG 검색 결과의 유사도 점수가 임계값(Threshold 0.6) 미만일 경우, "죄송합니다. 관련 정보를 찾을 수 없습니다." 대신 기존 Rule-based FAQ 시스템으로 자동 전환하여 답변 품질 보장.
성과
- 상담원 연결 감소: 단순 사용법 질문(결제, 환불 등)의 80%를 AI 챗봇이 자동 처리.
Enterprise Payment & Webhook Architecture (Stripe)
개요
HomeWiz는 글로벌 사용자를 대상으로 구독형(SaaS) 및 건별 결제(Token) 모델을 제공합니다. 금전적 트랜잭션의 무결성을 보장하기 위해 Stripe를 PG사로 선정하고, Webhook 기반의 이벤트 구동형(Event-Driven) 결제 시스템을 구축했습니다.
결제 흐름과 Webhook 패턴
결제 완료 확인은 클라이언트(프론트엔드) 신호를 신뢰하지 않고, Server-to-Server Webhook만을 신뢰하도록 설계했습니다.
안정성 확보를 위한 핵심 기술
멱등성(Idempotency) 보장
Webhook은 네트워크 이슈로 중복 전송(At-least-once delivery)될 수 있습니다. 1번 결제했는데 2번 포인트가 충전되는 사고를 막기 위해 두 단계 방어막을 구축했습니다.
- DB Unique Constraint:
webhook_events테이블의stripe_event_id컬럼에 유니크 인덱스를 걸어 물리적 중복 저장 차단. - Logic Level Check: 트랜잭션 처리 전 해당
invoice_id로 이미 처리된 내역이 있는지 조회.
1@Transactional 2public void handlePaymentSuccess(Event event) { 3 if (processedEventRepository.existsByEventId(event.getId())) { 4 log.info("Skipping duplicate event: {}", event.getId()); 5 return; 6 } 7 // ... 비즈니스 로직 실행 ... 8}
Failsafe Reconciliation (대사 작업)
Webhook이 아예 도달하지 않는 경우(서버 다운 등)를 대비해 Spring Batch를 이용한 Daily Reconciliation Job을 구현했습니다.
매일 새벽 Stripe API를 호출하여
yesterday의 성공 결제 내역을 가져오고, 내부 DB와 대조하여 누락된 건을 자동 보정합니다.구독 및 쿼터(Quota) 관리
invoice.payment_succeeded 이벤트를 단순 결제 성공 신호뿐만 아니라, 월간 사용량 리셋 트리거로 활용했습니다.billing_reason='subscription_cycle'확인 시 사용자monthly_usage를 0으로 초기화.- 결제가 실패(
payment_failed)하면 즉시 서비스 이용을 제한(grace_period적용)하고 사용자에게 카드 정보 갱신 요청 이메일 발송.
보안 (Security)
- Webhook Signature Verification: 해커가 Stripe를 사칭하여 가짜 결제 성공 요청을 보내는 것을 막기 위해, Stripe SDK의
Webhook.constructEvent()를 통해 헤더 서명을 철저히 검증합니다. - Ip Whitelisting: (옵션) Stripe가 공표한 IP 대역에서 오는 요청만 허용하도록 방화벽 설정.
Real-time Interaction & Notification System
HomeWiz의 실시간 상호작용 시스템은 Native WebSocket과 Redis를 결합하여 지연 시간을 최소화하고, 온라인/오프라인 상태에 관계없이 확실한 메시지 전달을 보장하는 하이브리드 아키텍처로 설계되었습니다.
Architecture Overview
기존의 무거운 STOMP/SockJS 프로토콜 대신 Native WebSocket Handler를 직접 구현하여 통신 오버헤드를 줄이고, 모바일/웹 클라이언트와의 호환성을 극대화했습니다.
Custom WebSocket Protocol
WebSocketHandler는 JSON 기반의 경량화된 커스텀 프로토콜을 사용하여 메시지 유형(type)에 따라 로직을 분기합니다.| Message Type | Direction | Description |
|---|---|---|
heartbeat | Client ↔ Server | 30초 주기 연결 생존 확인 및 Redis 세션 갱신 |
user_status | Client → Server | 특정 사용자의 접속 상태 조회 |
typing | Client → Client | 채팅방 내 실시간 입력 상태 전파 (Ephemeral) |
notification | Server → Client | 좋아요, 댓글, AI 생성 완료 등 서버 이벤트 푸시 |
1// WebSocketHandler.java (Core Logic) 2@Override 3protected void handleTextMessage(WebSocketSession session, TextMessage message) { 4 Map<String, Object> payload = objectMapper.readValue(message.getPayload(), Map.class); 5 String type = (String) payload.get("type"); 6 7 switch (type) { 8 case "heartbeat" -> updateHeartbeat(session); 9 case "notification" -> handleNotificationAck(payload); 10 // ... 11 } 12}
Global State Synchronization (Redis)
단일 서버 인스턴스 내에서는
ConcurrentHashMap으로 세션을 관리하지만, 사용자의 "접속 상태(Presence)"는 Redis를 통해 전역적으로 관리됩니다.Dual-Layer Session Management
- Local Memory:
ConcurrentHashMap<String, WebSocketSession>- 빠른 메시지 전송을 위해 활성 소켓 객체를 메모리에 직접 보유.
- Global State:
RedisService.setUserOnline(userId)- 사용자의 접속 여부를 Redis에 기록하여, 다른 서비스(예: 알림 서비스)에서 유저 상태를 조회할 수 있게 함.
Connection Lifecycle
- OnOpen: 세션 ID 매핑, Redis에
online상태 등록, 연결된 친구들에게user_status_update브로드캐스팅. - OnMessage(Heartbeat): Redis Key의 TTL(Time-To-Live) 갱신.
- OnClose: 로컬 맵 제거, Redis
offline상태 전환, 접속 종료 이벤트 전파.
Hybrid Notification Delivery System
사용자의 상태(온라인/오프라인)에 따라 가장 적절한 방식으로 알림을 전달하는 Hybrid Delivery 전략을 사용합니다. 특히 AI 이미지 생성과 같이 긴 시간이 소요되는 작업(Long-running Task)의 완료 알림 처리에 필수적입니다.
Delivery Logic Flow
Offline Notification Queue
사용자가 오프라인일 때 발생한 중요 이벤트는 Redis의
offline_notifications:{userId} 키에 JSON 리스트로 저장됩니다. 사용자가 재접속(Reconnection)하는 순간 WebSocketHandler.afterConnectionEstablished에서 이 큐를 확인하고 즉시 푸시합니다.1// NotificationService.java 2public void sendNotification(String userId, String type, Map<String, Object> data) { 3 if (redisService.isUserOnline(userId)) { 4 webSocketHandler.sendToUser(userId, type, data); 5 } 6 // 오프라인이더라도 놓치지 않도록 항상 큐/DB에 백업 7 storeOfflineNotification(userId, type, data); 8}
Frontend Integration (Robustness)
클라이언트(
useWebSocket.ts)는 네트워크 불안정에 대비해 Exponential Backoff (지수 백오프) 재연결 전략을 사용합니다.- 재연결 로직: 연결 끊김 감지 시 1초, 2초, 4초, 8초... 간격으로 재시도.
- 상태 복구: 재연결 성공 시
uptime체크를 통해 누락된 메시지(Gap)가 있는지 서버에 확인 요청. - Push Notification 연동: 브라우저가 백그라운드 상태일 경우
Validation API를 통해 OS 레벨의 푸시 알림으로 전환.
Multi-Model AI Generation Pipeline
HomeWiz의 핵심 가치인 "공간 변환 경험"을 제공하기 위해, 단일 AI 모델에 의존하지 않고 각 작업에 최적화된 모델을 선택적으로 사용하는 Multi-Model Strategy와 건축적 구조를 보존하는 Two-Stage Generation 파이프라인을 구축했습니다.
AI Provider Strategy Pattern
비용, 속도, 품질의 균형을 맞추기 위해 3가지 AI 엔진을 유기적으로 통합했습니다.
AIService는 요청의 성격에 따라 최적의 Provider를 동적으로 선택합니다.| Provider | Model | Role | Selection Criteria |
|---|---|---|---|
| Google Gemini | Gemini 3.0 Pro | Default Generator | 빠른 응답 속도, 우수한 인스트럭션 이행 능력, 비용 효율성 |
| OpenAI | DALL-E + GPT-5.2 | Structure Analysis | 높은 공간 이해도, 복잡한 프롬프트 해석 능력 |
| Replicate | Stable Diffusion XL | ControlNet Inpainting | 세밀한 부분 수정, 특정 스타일(ControlNet) 강제 필요 시 |
1// AIService.java (Strategy Switching) 2public String generateDesignImage(..., AIProvider provider) { 3 switch (provider) { 4 case GEMINI -> geminiService.generateDesignWithGemini(...); 5 case OPENAI -> generateWithOpenAI(...); // DALL-E 3 6 case REPLICATE -> replicateService.generateDesignWithReplicate(...); 7 } 8}
Two-Stage Structure Preservation
인테리어 디자인 생성의 가장 큰 난제는 "원래 방의 구조(벽, 창문, 문 위치)를 유지하면서 스타일만 바꾸는 것"입니다. 이를 해결하기 위해 2단계 파이프라인을 도입했습니다.
Stage 1: Architectural Analysis (Image API)
먼저
Gemini 3.0 Pro을 사용하여 원본 이미지를 "건축적 관점"에서 텍스트로 역설계합니다. 가구는 무시하고 구조적 특징만을 추출합니다.Input: 원본 방 사진
Vision Prompt: "Describe ONLY the architectural structure of this room: walls, floor, ceiling, windows... Do not mention furniture."
Output: "Rectangular room with floor-to-ceiling windows on the left, oak flooring, 3m ceiling height..."
Stage 2: Prompt Injection Generation
추출된 구조 설명(Structural Description)을 생성 프롬프트에 강제 주입(Injection)하여 Hallucination(환각 현상)을 억제합니다.
1// Prompt Construction Logic 2String enhancedPrompt = "Design an interior for a room with these EXACT architectural features: " 3 + roomDescription // <--- Stage 1에서의 분석 결과 주입 4 + ". KEEP the exact same walls, floor, ceiling. " 5 + "Apply " + style + " style with " + roomType + " layout.";
Idempotency & Cost Control
AI API 호출은 비용이 발생하므로, 중복 요청 방지(Idempotency)가 필수적입니다.
DesignIdempotencyService는 분산 환경에서도 안전한 요청 제어를 보장합니다.Idempotency Key Flow
- Request: 클라이언트가 요청 시
Idempotency-Key헤더(UUID)를 포함. - Check:
IdempotencyKeyEntry테이블에서 해당 키 조회.IN_PROGRESS: "처리 중" 에러 반환 (중복 실행 방지).SUCCEEDED: 이전에 성공한 결과(이미지 URL)를 캐시에서 바로 반환 (비용 절약).NEW: 트랜잭션 락을 걸고 AI 요청 시작.
- Completion: AI 응답 도착 시 결과를 저장하고 상태를
SUCCEEDED로 업데이트.
1// DesignIdempotencyService.java 2@Transactional 3public BeginResult begin(String userId, String key) { 4 return repository.findByUserIdAndIdempotencyKey(userId, key) 5 .map(existing -> checkStatus(existing)) 6 .orElseGet(() -> createNewLock(userId, key)); 7}
Fallback Mechanism
외부 AI 서비스의 장애는 사용자 경험에 치명적입니다. 이를 대비해 3중 Fallback 시스템을 갖추고 있습니다.
- Primary: Gemini 2.5 Flash 시도.
- Retry: Timeout/Error 발생 시 OpenAI DALL-E 3로 Failover.
- Final Fallback: 모든 AI 실패 시, 사전에 준비된 스타일별 고품질 "Static Placeholder" 이미지를 반환하여 서비스 중단을 방지하고 사용자에게 "잠시 후 다시 시도해주세요" 메시지 전달.
High-Performance Search & Discovery
HomeWiz의 데이터 규모가 커짐에 따라(수많은 디자인, 포스트, 유저), 단순 DB 쿼리로는 감당할 수 없는 복잡한 검색 요구사항을 충족하기 위해 OpenSearch (Elasticsearch fork)를 도입했습니다.
Search Architecture Overview
검색 시스템은 단순한 텍스트 매칭을 넘어, "사용자 의도"를 파악하고 "인기 콘텐츠"를 부스팅하는 지능형 검색을 지향합니다.
- Search Engine: OpenSearch (AWS Managed or Self-hosted via Docker)
- Indexing Strategy: Logstash / Custom Application Event Listener
- Performance: 복합 쿼리 기준 평균 응답 시간 <50ms
Parallel Universal Search
사용자가 상단 바에서 검색할 때,
User, Post, Design 3가지 도메인을 동시에 검색하여 통합된 결과를 보여줍니다. 이를 위해 Java의 CompletableFuture를 활용한 비동기 병렬 처리를 구현했습니다.1// OpenSearchSearchService.java 2public Map<String, Object> searchAll(String query, Pageable pageable) { 3 // 3가지 검색 요청을 병렬로 시작 (Non-blocking I/O) 4 CompletableFuture<Page<User>> users = supplyAsync(() -> searchUsers(query...)); 5 CompletableFuture<Page<Post>> posts = supplyAsync(() -> searchPosts(query...)); 6 CompletableFuture<Page<Design>> designs = supplyAsync(() -> searchDesigns(query...)); 7 8 // 모든 결과가 도착할 때까지 대기 (join) 후 병합 9 CompletableFuture.allOf(users, posts, designs).join(); 10 11 return mergeResults(users.get(), posts.get(), designs.get()); 12}
이 방식은 순차 처리(Sequential) 대비 응답 시간을 약 60% 단축시켰습니다 (300ms -> 120ms).
Domain-Specific Boosting Logic
단순 키워드 일치보다 "질 좋은 콘텐츠"가 상위에 노출되도록
Function Score Query를 사용하여 랭킹 알고리즘을 설계했습니다.Post Ranking (Engagement Score)
게시글 검색 시 텍스트 연관성(Score)뿐만 아니라, 좋아요(Like)와 댓글(Comment) 수에 가중치를 부여합니다.
- Formula:
Final Score = (TF-IDF Score) * log(1 + likeCount * 2 + commentCount * 3) - Implementation:
engagementScore필드를 인덱싱 시점에 미리 계산하거나, 쿼리 시점에script_score로 동적 계산.
Design Ranking (Intent Matching)
디자인 검색 시, 사용자가 필터(Style, RoomType)를 걸지 않더라도 쿼리 텍스트에서 의도를 파악하여 해당 필드에 가중치를 둡니다.
- Query: "Modern living room"
- Boosting:
style필드가 "Modern"인 문서: Boost x 3.0roomType필드가 "Living Room"인 문서: Boost x 2.0- 단순 서술(Description)에 포함된 경우: Boost x 1.0
N-gram Autocomplete (Type-ahead)
사용자가 검색어를 입력하는 도중에 실시간으로 제안(Suggestion)을 하기 위해,
Edge N-gram 토크나이저를 적용했습니다.- Indexing:
username이 "homewiz"라면 ->["h", "ho", "hom", "home", "homew", ...]토큰 생성. - Querying:
TextQueryType.BoolPrefix를 사용하여 사용자가 "hom"만 입력해도 "homewiz"가 즉시 매칭됨. - Optimization: 2글자 미만 쿼리는 무시하여 불필요한 연산 방지.
Security & Admin Monitoring Architecture
엔터프라이즈급 서비스 운영을 위해 HomeWiz는 단순한 방화벽 수준을 넘어, 애플리케이션 계층(Layer 7)에서의 능동적인 보안 모니터링 및 대응 시스템인
AdminSecurityMonitor를 구축했습니다.Proactive Security Monitoring
AdminSecurityMonitor는 관리자(Admin) 권한에 대한 비정상적인 접근이나 시스템 이상 징후를 실시간으로 감지하고, "문제 발생 후"가 아닌 "발생 즉시" 엔지니어에게 알림을 보냅니다.State Tracking with Redis
보안 이벤트(로그인 실패 등)는 Stateless한 HTTP 요청 간에도 상태를 추적해야 하므로 Redis를 활용합니다.
- Login Failures:
admin:security:login_fail:{identifier}키에 실패 횟수를increment연산으로 원자적(Atomic)으로 카운팅. - TTL Window: 5분(
LOGIN_FAILURE_WINDOW) 동안의 실패만 카운트하여, 일시적인 실수와 무차별 대입(Brute Force) 공격을 구분.
Alert Fatigue Management (Muting)
보안 시스템의 흔한 문제인 "경고 피로(Alert Fatigue)"를 방지하기 위해 Alert Muting(알림 음소거) 메커니즘을 구현했습니다. 동일한 공격에 대해 알림이 1초에도 수십 번 발생하는 것을 막습니다.
Deduplication Logic
1// AdminSecurityMonitor.java 2public void recordLoginFailure(...) { 3 // 1. 카운트 증가 4 Long count = redis.increment(key); 5 6 // 2. 임계치 초과 시 알림 전송 (단, Mute 상태가 아닐 때만) 7 if (count >= THRESHOLD) { 8 if (!isMuted(muteKey)) { 9 sendAlert(...); 10 activateMute(muteKey, Duration.ofMinutes(30)); // 30분간 동일 알림 차단 11 } 12 } 13}
- Fallback: Redis 장애 시에도 동작하도록
ConcurrentHashMap기반의 In-Memory Fallback 캐시를 이중으로 보유.
Advanced Threat Detection
Consecutive Login Failures (P6 Policy)
단순 횟수뿐만 아니라 "연속성"을 감지합니다. 성공적인 로그인이 중간에 있다면 카운트를 초기화하여, 정상 사용자의 간헐적 실수를 공격으로 오판하지 않도록 설계했습니다.
- Redis List: 실패 이력을 리스트로 관리 (
RPUSH), 성공 시 키 삭제(DEL).
JWT Forgery / Tampering
서명(Signature)이 유효하지 않거나, 만료된 토큰을 조작하여 재사용하려는 시도를
JwtFilter와 연동하여 감지합니다.- Action: P6 등급(심각) 경고 발송 및 해당 IP의 자동 차단(Blocklist) 후보 등록.
RBAC Consistency Check (Claims Mismatch)
Firebase Auth(인증)의 Custom Claims와 주 데이터베이스(DB)의 Role 정보가 불일치하는 "권한 불일치" 상황을 감지합니다. 이는 관리자 권한 탈취 시도나 데이터 동기화 오류를 즉시 잡아냅니다.
Admin Notification Dispatcher
감지된 보안 위협은
AdminNotificationService를 통해 즉시 전파됩니다.- Target: SUPER_ADMIN, DEVELOPER_ADMIN 역할을 가진 관리자 그룹에만 선별 발송.
- Channel: 관리자 대시보드 웹소켓 푸시(실시간) + 이메일(비동기, 심각도 높을 시).
Visual Object Search & Shopping Integration
AI가 생성한 인테리어 디자인 속 가구와 소품을 분석하여, 사용자가 즉시 구매할 수 있도록 연결하는 Visual Commerce 기능을 구현했습니다.
Zero-Cost Architecture
초기 운영 비용 절감을 목표로, 고비용의 서버 GPU 대신 사용자 브라우저의 리소스를 활용하는 Client-side Computing 아키텍처를 설계했습니다.
Browser-Side Object Detection
TensorFlow.js와 경량화된 COCO-SSD 모델을 사용하여 서버 통신 없이 즉각적인 객체 인식을 수행합니다.
- Model: COCO-SSD (MobileNet V2 기반 경량 모델)
- Performance: WebGL 가속을 사용하여 30-50ms 내 추론 완료.
- Process:
- AI 이미지 생성 완료.
OffscreenCanvas에서 이미지 로드.- 브라우저 백그라운드 스레드에서 객체 감지 수행.
- 감지된 좌표(Bounding Box)와 클래스(Class)를 로컬 상태에 저장.
1// objectDetectionService.ts 2import * as cocoSsd from '@tensorflow-models/coco-ssd'; 3 4export async function detectObjects(imageElement: HTMLImageElement) { 5 // 1. Load Pre-trained Model (Lite MobileNet V2) 6 const model = await cocoSsd.load({ base: 'lite_mobilenet_v2' }); 7 8 // 2. Inference in Browser Client 9 const predictions = await model.detect(imageElement); 10 11 // 3. Filter & Map 12 return predictions.filter(p => p.score > 0.6).map(p => ({ 13 class: p.class, // e.g., "chair", "couch" 14 bbox: p.bbox, 15 query: translateToKorean(p.class) // "의자", "소파" 16 })); 17}
UX Integration: Interactive Shopping Mode
탐지된 객체는 이미지 위에 Interactive Hotspot으로 렌더링되며, 클릭 시 해당 가구의 한글명(예: couch -> "소파")으로 즉시 쇼핑 검색이 실행됩니다. 이는 서버의 개입 없이 매끄럽게(Seamless) 이어지는 사용자 경험을 제공합니다.
API Aggregation Strategy
비용 효율성과 데이터 품질을 고려한 Fallback Strategy를 적용했습니다.
- Tier 1 (Free & High Quality): Naver Shopping API
- 일일 25,000회 무료 호출 가능.
- 한국 시장에 최적화된 상품 데이터베이스.
- Tier 2 (Broad Coverage): Google Custom Search JSON API
- 해외 가구/소품 검색 시 사용.
- 일일 무료 한도 소진 시 후순위 처리.
- Tier 3 (Monetization): Coupang Partners & OHouse
- 직접적인 수익 창출을 위한 제휴 링크 생성.
- "최저가 비교" UI에 제휴 태그 부착.
1// ShoppingService.ts 2async function searchAllPlatforms(query: string) { 3 const results = await Promise.allSettled([ 4 searchNaver(query), // 무료, 빠름 5 searchCoupang(query), // 수익화 6 searchGoogle(query) // 보조 7 ]); 8 9 return interleaveResults(results); // 플랫폼별로 1:1:1 섞어서 노출 10}
Revenue Model
단순한 기술 과시를 넘어, 지속 가능한 사이드 프로젝트 운영을 위한 수익 모델(Business Model)이 코드 레벨에 녹아있습니다.
- Affiliate Tagging & Attribution:
- 생성된 링크에
subId파라미터를 동적으로 주입하여 사용자별 기여도 추적. - 쿠팡 파트너스 API를 통해 3.5%~ 로열티 수익 자동화.
- 생성된 링크에
- Click Tracking Architecture:
- 사용자가 "구매하기" 클릭 시
ShoppingClick이벤트 로그를 비동기로 서버에 전송. userId,productId,platform을 기록하여 전환율(CTR) 분석 및 상품 추천 알고리즘 개선에 활용.
- 사용자가 "구매하기" 클릭 시
UX Implementation (Interactive Overlay)
정적인 이미지를 "살아있는 쇼핑몰"로 바꾸는 인터랙티브 UI/UX를 구현했습니다.
- Hover Interaction: 마우스를 올리면 감지된 객체에 부드러운 Fade-in 테두리(Bounding Box) 표시.
- Contextual Popover: 객체 근처에 방해되지 않는 위치(Smart Positioning)로 유사 상품 리스트 팝업.
- Mobile Support: 터치 인터페이스에서는 탭(Tap) 제스처로 토글링 지원.
Microservices Scalability & DevOps Strategy
HomeWiz는 MVP 단계의 모놀리식 구조에서 시작하여, 대규모 트래픽을 처리할 수 있는 MSA (Microservices Architecture)로의 전환을 위한 명확한 로드맵과 인프라 전략을 갖추고 있습니다.
Microservices Decomposition Plan
현재의 단일 Spring Boot 애플리케이션을 도메인 주도 설계(DDD)에 따라 분리하여, 독립적 배포(Independent Deployment)와 부분 장애 격리(Fault Isolation)를 달성합니다.
| Service | Tech Stack | Responsibility | Scaling Strategy |
|---|---|---|---|
| API Gateway | Node.js / Spring Cloud | 라우팅, 인증(AuthN), Rate Limiting | HPA (CPU-based) |
| AI Workload | FastAPI (Python) | 이미지 처리, GPU 추론 파이프라인 | Queue-based Scaling (KEDA) |
| Core Service | Spring Boot | 유저, 결제, 비즈니스 로직 | Memory-based |
| Search Service | OpenSearch | 검색 및 인덱싱 | Data Node Sharding |
| Notification | Go / Node.js | 실시간 웹소켓 연결 (I/O Bound) | Connection Count |
Event-Driven Architecture (EDA)
서비스 간의 강결합을 끊기 위해 Kafka/SQS 기반의 비동기 메시징 아키텍처를 설계했습니다.
Async AI Processing
AI 이미지 생성 요청은 즉시 처리되지 않고 작업 큐에 격리되어 처리됩니다.
- Flow: Client → Core API (
POST /generate) → SQS Queue → AI Worker → S3 Upload → Webhook → Client Notification. - Benefit: 트래픽 폭주 시에도 요청을 유실하지 않고(DLQ), AI 워커의 처리 속도에 맞춰 배압(Backpressure) 조절 가능.
Storage Migration Strategy (Local to S3)
초기 로컬 파일 시스템(
uploads/) 저장 방식의 한계를 극복하기 위해, AWS S3 + CloudFront 마이그레이션 전략을 수립했습니다.Pre-signed URL Upload
서버를 거치지 않고 브라우저에서 S3로 직접 업로드하여 서버 대역폭을 획기적으로 절약합니다 (Serverless Pattern).
- Client: "업로드 권한 줘" (
POST /upload-url) - Server: IAM 권한 확인 후 임시 서명된 URL(Pre-signed URL) 발급.
- Client: S3로 직접
PUT업로드. - AWS Lambda: 업로드 감지 시 트리거되어
Sharp라이브러리로 썸네일/WebP 자동 변환.
Scalable Session Management
다중 서버 환경(Scale-out)에서도 사용자의 로그인 상태를 유지하기 위해 Redis Cluster를 활용한 분산 세션 아키텍처를 구현했습니다.
- Session Clustering: Sticky Session 없이 어떤 서버에 접속해도 Redis에서 동일한 세션 정보 조회.
- Sliding Window TTL: 사용자가 활동하는 동안 세션 만료 시간을 자동으로 연장.
- Device Fingerprinting: Redis에 IP, User-Agent, Device ID를 해싱하여 저장해두고, 의심스러운 접속(예: 갑작스런 국가 변경) 감지 시 세션 강제 무효화.
HSL-Based Algorithmic Color Intelligence
이미지에서 단순히 '빨강', '파랑'을 추출하는 것을 넘어, 디자인적인 무드(Mood)와 테마(Theme)를 코드로 해석하는 순수 자바 기반의 색상 분석 엔진입니다. 무거운 CV 라이브러리(OpenCV 등) 없이,
java.awt 표준 라이브러리만을 사용하여 고성능 분석을 구현했습니다.Pixel Sampling & Palette Extraction
전체 이미지를 분석하는 오버헤드를 줄이기 위해, Smart Pixel Sampling 알고리즘을 사용합니다.
- Downscaling to 200x200: 인간의 눈이 인지하는 색상 분포를 유지하며 연산량을 99% 감소.
- Grid Sampling: 모든 픽셀을 순회하지 않고, 4px 간격으로 그리드 샘플링.
- Noise Filtering: 명도(Lightness)가 극단적인(10 미만, 245 초과) 픽셀은 노이즈로 간주하여 제외.
1// ColorAnalysisService.java (Core Logic) 2for (int y = 0; y < height; y += 4) { // Grid Sampling 3 for (int x = 0; x < width; x += 4) { 4 int rgb = image.getRGB(x, y); 5 // ... (HSL Conversion & Filtering) 6 } 7}
Mathematical Theme Classification
추출된 팔레트의 HSL 평균값을 벡터 공간에 투영하여 6가지 디자인 테마로 자동 분류합니다.
| Theme | Logic (HSL Analysis) |
|---|---|
| Monochrome | Saturation(채도) < 20% |
| Pastel | Lightness(명도) > 80% |
| Warm | Hue(색상) 0 |
| Cool | Hue(색상) 180~300° (Blue/Purple) |
| Earth | Hue 60~120° AND Saturation < Medium |
| Neutral | Saturation < 40% (Grayish) |
Weighted Color Matching Algorithm
RGB 유클리드 거리는 인간의 색상 인지와 괴리가 있습니다(예: 빨강과 자주색은 RGB상에서 멀지만 눈에는 비슷해 보임). 이를 해결하기 위해 가중치가 적용된 HSL 거리 공식을 독자적으로 구현했습니다.
1// Weighted Euclidean Distance in HSL Space 2double distance = Math.sqrt( 3 Math.pow(hueDiff / 180.0, 2) * 0.6 + // 색상(Hue)에 60% 가중치 4 Math.pow(satDiff / 100.0, 2) * 0.2 + // 채도(Sat)에 20% 가중치 5 Math.pow(lightDiff / 100.0, 2) * 0.2 // 명도(Light)에 20% 가중치 6);
이 알고리즘을 통해, 사용자가 "따뜻하고 아늑한 분위기의 거실"을 검색하면, 텍스트가 아닌 색상 벡터 유사도를 통해 가장 적합한 디자인을 찾아냅니다.
Real-time Multi-Device Synchronization
사용자가 스마트폰에서 알림을 확인하면 데스크탑에서도 즉시 '읽음' 처리가 되는 Seamless Cross-Device Experience를 제공합니다. WebSocket(STOMP)과 Redis Pub/Sub을 결합하여 상태 불일치(State Inconsistency)를 0으로 만들었습니다.
Device Session Architecture
한 명의 유저가 여러 기기(iPhone, iPad, MacBook)를 동시에 사용할 수 있음을 전제로 설계되었습니다.
- Session Entity:
DeviceSession테이블에서deviceId(Unique),deviceType(Mobile/Web),lastSeenAt을 관리. - Active Device Tracking: Redis Set(
active_devices:{userId})에 현재 온라인 상태인 모든 기기 ID를 실시간으로 추적.
Distributed State Synchronization Flow
알림 읽음 처리 시의 분산 동기화 흐름입니다:
- Action: iPhone에서 알림 A를 클릭. (
POST /notifications/A/read) - DB Update: PostgreSQL DB에 읽음 상태(
read_at) 저장. - Broadcasting:
- Redis에서 해당 유저의 모든 활성 기기 목록 조회.
- 요청을 보낸 기기(iPhone)를 제외한 나머지 기기(iPad, MacBook) 식별.
- WebSocket
/queue/sync채널로 동기화 이벤트(notification_read_sync) 발송.
- Client React: Web Client는 이벤트를 수신하자마자 UI의 뱃지 카운트를 즉시 차감.
1// DeviceSyncController.java 2private void syncReadStatusToOtherDevices(String userId, String notificationId, String sourceDeviceId) { 3 Set<String> activeDevices = redisService.getSetMembers("active_devices:" + userId); 4 5 for (String deviceId : activeDevices) { 6 if (!deviceId.equals(sourceDeviceId)) { // 나 자신에게는 보내지 않음 (Echo 방지) 7 messagingTemplate.convertAndSendToUser(userId, "/queue/sync", ...); 8 } 9 } 10}
Unread Count Consistency
서로 다른 기기 간의 '읽지 않은 알림 개수'가 꼬이는 문제를 원천 차단하기 위해 Redis Caching Strategy를 고도화했습니다.
- Cache Invalidation: 어떤 기기에서든 상태 변경이 일어나면
unread_notifications:{userId}:{deviceId}캐시를 즉시 파기. - Lazy Loading: 다음 요청 시 DB에서 정확한
COUNT(*)를 재계산하여 캐싱. - Bulk Sync: "모두 읽음" 처리 시에도, 일괄 업데이트 후 단 1회의 Sync 패킷으로 모든 기기 상태를 동기화하여 네트워크 부하 최소화.
Automated Technical SEO Engine
HomeWiz는 SPA(Single Page Application)의 한계인 검색 엔진 최적화(SEO) 문제를 극복하기 위해, 백엔드 레벨에서 동적 메타 데이터 생성 및 구조화된 데이터(JSON-LD) 주입을 자동화하는 SEO 엔진을 구축했습니다.
Dynamic Sitemap Generation
수만 개의 AI 생성 디자인과 사용자 포스트가 실시간으로 추가되는 환경에서, 정적
sitemap.xml 파일은 불가능합니다. 요청 시점에 DB를 조회하여 실시간으로 XML을 생성 및 스트리밍합니다.- Partitioning: URL이 50,000개가 넘지 않도록
sitemap_posts.xml,sitemap_designs.xml등으로 카테고리별 분할 처리. - Caching: Redis에 생성된 Sitemap XML을 1시간 캐싱하여 DB 부하 방지.
Programmatic JSON-LD Injection
Google 검색 결과에서 "Rich Snippet"(평점, 가격, 작성자 등)을 노출시키기 위해
Schema.org 표준을 준수하는 JSON-LD를 동적으로 생성합니다.1// SeoService.java (Concept) 2public Map<String, Object> generateDesignSchema(Design design) { 3 Map<String, Object> schema = new HashMap<>(); 4 schema.put("@context", "https://schema.org"); 5 schema.put("@type", "VisualArtwork"); 6 schema.put("name", design.getTitle()); 7 schema.put("image", design.getImageUrl()); 8 schema.put("creator", Map.of("@type", "Person", "name", design.getAuthorName())); 9 schema.put("artMedium", "AI Generative Art"); 10 return schema; 11}
API가 이 JSON 객체(
structuredData)를 반환하면, 프론트엔드(React Helmet)가 이를 받아 <script type="application/ld+json"> 태그로 렌더링합니다.Hybrid AI Personalization Engine
HomeWiz의 핵심 경쟁력은 단순한 아이디어 공유를 넘어, 사용자의 취향과 행동 패턴을 분석하여 "가장 구매 확률이 높은" 가구와 디자인을 예측하는 하이브리드 추천 엔진(Hybrid Recommendation Engine)에 있습니다.
Multi-Source Feed Ranking Algorithm
단일 알고리즘에 의존하지 않고, 4가지 핵심 지표를 가중 합산(Weighted Sum)하여 피드의 순서를 결정합니다. 이 가중치는 A/B 테스팅을 통해 실시간으로 조절 가능합니다.
1// PersonalizedFeedService.java 2public double calculateScore(FeedItem item) { 3 double score = 0; 4 score += behaviorScore * 0.4; // 유저 행동 기반 (클릭, 찜) 5 score += socialScore * 0.25; // 팔로잉/친구 기반 6 score += trendingScore * 0.2; // 실시간 인기 (Velocity) 7 score += timeScore * 0.15; // 최신성 (Time Decay) 8 9 // Exponential Time Decay: 시간이 지날수록 점수가 기하급수적으로 감소 10 return score * Math.exp(-hoursSincePost * 0.1); 11}
Context-Aware Product Recommendation
단순히 "소파"를 검색했다고 모든 소파를 보여주지 않습니다. AI 시각 지능(Visual Intelligence)이 현재 보고 있는 인테리어 사진의 문맥(Context)을 이해하여 추천합니다.
- Room Type Compatibility:
RoomTypeClassifierService가 이미지를 Gemini 3.0 Pro로 분석하여 "이곳은 모던한 거실"임을 파악합니다. 따라서 '침대'나 '클래식 식탁' 추천은 자동으로 필터링됩니다. - Style & Color Matching:
ColorAnalysisService의 HSL 분석 결과를 바탕으로, 현재 공간의 톤앤매너(Tone & Manner)를 해치지 않는 색상과 스타일의 가구만을 선별하여 추천 리스트 상위에 노출합니다.
Intelligent Fallback Strategy
AI API(OpenAI Vision)의 비용과 응답 속도 문제를 해결하기 위해 3-Tier Fallback System을 구축했습니다.
- Tier 1 (High Accuracy): Gemini 3.0 Pro Image API 호출 (신뢰도 > 60% 시 채택).
- Tier 2 (Cost Efficient): 이미지 메타데이터 및 태그 키워드 분석 (Tier 1 실패/지연 시).
- Tier 3 (Default): 사용자 행동 패턴 기반의 일반 추천 (Cold Start 대응).
Real-time Feedback Loop
사용자가 추천된 컨텐츠에 대해 보이는 반응(좋아요, 숨기기, 클릭)은
Redis에 실시간으로 캐싱되어 즉시 다음 추천 로직에 반영됩니다(Real-time Learning). 부정적인 피드백("Hide")은 해당 카테고리의 가중치를 즉시 -20% 감소시킵니다.Enterprise Email Delivery Architecture
Spring Boot 3.2+의 Virtual Thread와 Redis를 적극 활용하여, SMTP의 고질적인 문제인 '블로킹(Blocking)'과 '보안 취약점'을 완벽하게 해결한 엔터프라이즈급 메일링 아키텍처입니다.
Non-blocking Delivery Pipeline
전통적인 SMTP 라이브러리(
JavaMailSender)는 I/O Blocking을 유발하여 고트래픽 상황에서 스레드 풀 고갈의 원인이 됩니다. HomeWiz는 이를 해결하기 위해 두 가지 전략을 사용합니다.A. HTTP API over SMTP
SMTP 프로토콜 대신 Brevo (Sendinblue) HTTP API를 사용합니다.
WebClient를 통해 커넥션 풀을 효율적으로 관리하며, 방화벽 문제나 포트 차단 이슈 및 전송 실패율을 획기적으로 낮췄습니다.B. Virtual Thread Integration
메일 발송 작업은
EmailService 내부에서 @Async 또는 Virtual Thread Executor를 통해 실행됩니다.1// BrevoEmailService.java - Block() is safe here due to Virtual Threads 2String response = webClient.post() 3 .uri("/smtp/email") 4 .header("api-key", apiKey) 5 .bodyValue(requestBody) 6 .retrieve() 7 .bodyToMono(String.class) 8 .block(); // Virtual Thread unmounts here, not blocking OS thread
이 구조 덕분에 수천 건의 메일 발송 요청이 몰려도 Tomcat 워커 스레드는 즉시 해방되어 다른 요청을 처리할 수 있습니다.
Redis-Backed Security State Machine
이메일 인증은 단순한 기능 같지만, 보안 공격(Brute Force, Spamming)의 주 타겟입니다.
EmailVerificationService는 Redis를 활용한 정교한 상태 머신(State Machine)으로 이를 방어합니다.Security Policies
- Pre-send Cooldown:
cooldown:{email}키에 60초 TTL을 설정하여, 1분 내 재요청을 원천 차단합니다(Spam 방지). - Max Attempt Limit:
attempt:{email}키로 검증 시도 횟수를 추적하여, 5회 이상 틀릴 경우 해당 코드를 즉시 폐기하고 재발급을 강제합니다(Brute Force 방지). - Secure Random:
java.util.Random대신SecureRandom을 사용하여 예측 불가능한 6자리 코드를 생성합니다. - Atomic Flow: 인증 성공 시
verified:{email}토큰(TTL 3분)을 발급하고, 이 토큰이 있어야만 회원가입 API가 통과되도록 설계되었습니다.
High-Performance Template Rendering
Thymeleaf와 같은 서버 사이드 템플릿 엔진의 오버헤드를 제거하기 위해, Java String Block(Text Blocks) 기반의 Zero-Overhead Templating을 구현했습니다.
다크 모드를 완벽 지원하는 반응형 HTML 템플릿이 컴파일 시점에 최적화되어, 런타임 렌더링 부하가 '0'에 수렴합니다.
Client-Side Performance & PWA Architecture
React 기반 프론트엔드에서 서버 부하를 최소화하고 사용자 경험(UX)을 극대화하기 위해 구현된 3가지 핵심 클라이언트 아키텍처입니다.
Browser-Native Image Optimization
서버리스 이미지 처리 비용(AWS Lambda)을 절감하고 업로드 속도를 개선하기 위해, 브라우저 내(In-Browser) 이미지 가공 파이프라인을 구축했습니다.
- HTML5 Canvas Pipeline: 업로드 전
Canvas API를 사용하여 고해상도 이미지를 리사이징하고,toBlob()메서드를 통해 원본 대비 80% 가벼운 WebP 포맷으로 즉시 변환합니다. - Smart Validation: 단순 파일 크기뿐만 아니라, 해상도(Resolution)와 포맷 효율성을 분석하여 최적화가 필요한 경우에만 프로세스를 트리거합니다.
1// imageOptimization.ts 2export async function convertToWebP(file: File): Promise<{ blob: Blob; file: File }> { 3 // Canvas를 사용하여 WebP로 변환, Quality 0.85 4 // 서버 부하 0, 대역폭 절감 효과 극대화 5}
PWA & Service Worker Lifecycle
모바일 앱과 유사한 경험을 제공하기 위해 Progressive Web App (PWA) 기술을 엔터프라이즈 수준으로 구현했습니다.
- VAPID Push Notification:
PushNotificationManager싱글톤 클래스가 복잡한 VAPID 키 교환과 권한 요청 프로세스를 추상화하여, 웹에서도 네이티브급 푸시 알림을 제공합니다. - New Version Detection: Service Worker의
updatefound이벤트를 감지하여, 배포 발생 시 사용자에게 "새 버전이 출시되었습니다" 토스트를 띄우고 자연스러운 리로드를 유도합니다. - Install Prompt Interception: 브라우저의 기본 설치 배너를 가로채고(
preventDefault), 앱의 디자인 시스템에 맞는 커스텀 설치 UI를 제공합니다.
Real-time State Management (Zustand)
복잡한 실시간 데이터(채팅, 라이브 세션, 타이핑 인디케이터)를 효율적으로 관리하기 위해 Zustand를 도입했습니다.
- Atomic State Updates: 채팅방별 메시지, 타이핑 중인 유저 목록, 읽음 상태 등을 정규화(Normalization)하여 관리함으로서 불필요한 리렌더링을 방지합니다.
- Offline Persistence:
persist미들웨어를 사용하여 네트워크가 끊겨도 대화 내용이 유지되도록 하여 오프라인 경험을 강화했습니다.
Data Intelligence & Anomaly Detection System
단순히 데이터를 적재하는 것을 넘어, 실시간으로 데이터의 건전성(Health)과 비즈니스 리스크(Risk)를 감시하는 지능형 모니터링 시스템입니다.
User Metrics Anomaly Detection (P7 Policy)
일일 가입자 수와 이탈률을 모니터링하여, 통상적인 범위를 벗어난 이상 징후를 조기에 포착합니다.
- Algorithm: 전일 대비
±300%이상의 급격한 변동이 감지되면 즉시 경보를 발행합니다. - Spam Dectection: 짧은 시간 내에 가입자가 비정상적으로 급증하는 경우, 이를 '스팸 공격'으로 간주하고
Security Team에 경보를 보냄과 동시에 24시간 동안 중복 알림을 Mute하여 알림 피로도(Alert Fatigue)를 관리합니다. - Business Insight: 마케팅 캠페인의 성공 여부나 서비스 장애로 인한 가입 급감 등을 정량적으로 판단하는 지표로 활용됩니다.
Subscription Risk Monitoring
결제 실패나 구독 연체는 매출 손실과 직결됩니다.
SubscriptionRiskScheduler는 매일 새벽 3시, '결제 위험군' 사용자를 식별합니다.- Risk Scoring: 최근 7일간 결제 실패 횟수와 마지막 성공 결제일을 분석하여 리스크 점수를 산출합니다.
- Proactive Action: 위험군으로 분류된 유저 목록을 재무팀(Finance Admin)에 리포팅하여, 카드 만료 안내나 결제 수단 업데이트를 선제적으로 요청할 수 있도록 돕습니다.
Real-time Search Synchronization (JPA Listeners)
데이터베이스(PostgreSQL)와 검색엔진(OpenSearch) 간의 데이터 불일치(Inconsistency)를 '0'에 수렴하게 만드는 동기화 전략입니다.
- Event-Driven Sync: 별도의 배치 잡(Batch Job)을 기다리지 않고, JPA 엔티티의 생명주기 이벤트(
@PostPersist,@PostUpdate)가 발생하는 즉시 인덱싱 요청을 비동기로 발행합니다. - Consistency: 사용자가 글을 작성하자마자 검색 결과에 노출되는 'Real-time Experience'를 보장합니다.
1// DesignSearchIndexListener.java 2@PostPersist 3public void onPostPersist(Design design) { 4 // 트랜잭션 성공 직후 0.1초 내 인덱싱 트리거 5 indexingService.indexDesignDocument(design.toDocument()); 6}
Rate Limiting & Quota Management
SaaS의 안정적인 운영과 수익화 모델(Monetization)을 지탱하는 이중 방어선(Dual-Layered Protection)입니다. 기술적 과부하를 막는 Rate Limiting과 비즈니스 수익을 보장하는 Quota Management가 유기적으로 결합되어 있습니다.
Distributed Rate Limiting (Redis Sliding Window)
일반적인 'Fixed Window' 방식의 경계 문제(Boundary Issue)를 해결하기 위해, Redis Sorted Set(ZSet)을 활용한 Sliding Window Log 알고리즘을 구현했습니다.
Key Logic (RateLimitService.java)
- Precision:
System.currentTimeMillis()를 스코어로 사용하여 밀리초 단위의 정교한 트래픽 제어가 가능합니다. - Atomic Operation: Redis
ZREMRANGEBYSCORE->ZCOUNT->ZADD파이프라인을 통해 동시성 이슈 없이 정확한 요청 수를 카운팅합니다. - Auto-Cleanup: 만료된 윈도우 로그는 자동으로 제거(
expire)되어 메모리 누수를 방지합니다.
1// Redis ZSet을 이용한 Sliding Window 구현 2redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart); 3Long count = redisTemplate.opsForZSet().count(key, windowStart, currentTime); 4 5if (count >= limit) { 6 return false; // 429 Too Many Requests 7}
Dynamic Quota Management
단순 카운팅을 넘어, 구독 티어(Tier)별로 차별화된 사용량을 동적으로 관리하는 비즈니스 로직입니다.
- Tiered Logic:
- Free: 월 2회 생성 (체험 유도)
- Pro: 월 200회 생성 (Heavy User)
- Enterprise: 무제한(
-1flag), 할당량 차감 로직 자체를 우회(Bypass)하여 성능 최적화.
- Monthly Reset:
QuotaResetScheduler가 매월 1일 사용량을 0으로 초기화하며, 결제 주기에 맞춰 갱신됩니다.
Webhook Reconciliation (Payment Failsafe)
Stripe 결제 성공 Webhook이 네트워크 이슈로 유실될 경우, 유저가 돈을 내고도 서비스를 이용하지 못하는 치명적인 UX 문제를 방지합니다.
- Self-Healing:
QuotaResetScheduler.reconcileQuotaResets()가 매일 새벽 실행되어, 최근 48시간 내의 결제 내역(PaymentTransaction)과 현재 할당량 상태를 대조합니다. - Correction: Webhook 누락이 감지되면 즉시 할당량을 갱신하고, 관리자에게 'Reconciled' 알림을 발송하여 시스템의 신뢰성을 보장합니다.
Frontend Core Technologies & UX Architecture
사용자 경험(UX)을 결정짓는 핵심 프론트엔드 기술을 심층 분석합니다. 단순한 화면 출력을 넘어, AI 시각화, 데이터 수집, 결함 내성(Fault Tolerance)을 갖춘 지능형 클라이언트입니다.
Interactive AI Visual Layer
사용자가 업로드한 이미지 위에서 AI 분석 결과를 직관적으로 탐색할 수 있는 인터랙티브 레이어입니다.
Key Features (
InteractiveImage.tsx):- Bounding Box Overlay: 텐서플로우/YOLO 등에서 감지된 객체 좌표(BBox)를 퍼센트 기반의 CSS 좌표로 변환하여 반응형 이미지 위에 오버레이합니다.
- Contextual Shopping: 사용자가 이미지 속 '소파'에 마우스를 올리면(
Hover), 즉시 해당 객체의 메타데이터(Class, Score)를 분석하여ProductSuggestion컴포넌트를 통해 유사 상품을 추천합니다. - Confidence Filtering: 신뢰도 점수(Score)가
0.6미만인 노이즈는 클라이언트 레벨에서 필터링하여 UI 복잡도를 낮춥니다.
Granular Behavior Tracking System
사용자의 모든 미세 상호작용을 포착하여 추천 엔진의 연료로 사용하는 정밀 추적 시스템입니다.
Data Points (
useBehaviorTracking.ts):- Dwell Time: 단순 클릭을 넘어, 특정 디자인/게시물에 머무른 시간(초 단위)을 측정하여 '실질적 관심도'를 파악합니다.
- Scroll Depth: 콘텐츠의 소비 깊이(10% 단위)를 추적하여 이탈 지점을 분석합니다.
- Micro-Interactions: 확대(Zoom), 저장(Save), 공유(Share) 등 10여 가지의 행동 이벤트를 수집합니다.
- Resiliency: 오프라인 상태이거나 API 속도 제한(Rate Limit 429)이 발생하면, 이벤트를
Queue에 저장했다가 네트워크 복구 시 자동으로 재전송(Background Sync)합니다.
Fault-Tolerant Media Pipeline
이미지 로딩 실패는 커머스 플랫폼에서 치명적입니다. 이를 방지하기 위한 3단계 자가 복구(Self-Healing) 이미지 컴포넌트를 구현했습니다.
Retry Stratgy (
OptimizedImage.tsx):- Primary: 최적화된 WebP 이미지 로딩 시도.
- Retry 1 (Fallback): 실패 시 사전에 정의된
Fallback이미지로 전환. - Retry 2 (Original): 원본(Raw) 이미지 URL로 재요청.
- Retry 3 (Low-Quality): 최후의 수단으로 저해상도(Low-Quality) 버전을 로딩하여 '엑스박스' 노출을 원천 차단.
Gamified User Insight Dashboard
수집된 데이터를 내부 분석용으로만 쓰지 않고, 사용자에게 시각화된 대시보드로 제공하여 자발적인 데이터 입력을 유도합니다.
Gamification Elements (
UserInsightsDashboard.tsx):- Confidence Score: "현재 AI 추천 정확도 78%"와 같이 수치화하여 보여줍니다.
- Actionable Feedback: 점수를 높이기 위해 "모던 스타일 사진 3개 더 좋아요 하기", "바이오 입력하기" 등의 퀘스트를 제안합니다.
- Style DNA: 사용자의 취향을 파이 차트(Recharts)로 시각화하여 "나는 미니멀리스트 60%, 인더스트리얼 40%"와 같은 정체성을 부여합니다.
RAG Service & AI Chatbot Architecture
사용자와의 자연어 대화를 통해 맞춤형 인테리어 정보를 제공하는 지능형 RAG(Retrieval-Augmented Generation) 시스템의 아키텍처입니다.
RAG Query Pipeline (rag_pipeline.py)
사용자 질문을 분석하고, 가장 관련성 높은 문서를 검색하여 LLM에 전달하는 핵심 오케스트레이션 엔진입니다.
Pipeline Flow
- Cache Check (Step 0): 자주 묻는 질문(FAQ)의 경우, Redis Cache(
response_cache)를 먼저 확인하여 응답 속도를 획기적으로 개선합니다. - Vector Search (Step 2): 사용자 질문을 임베딩(Vector)으로 변환한 후,
VectorStore에서 코사인 유사도(Similarity Search) 기반으로 관련 문서 TOP-K개를 검색합니다. - Context Construction (Step 3): 검색된 문서 조각(Chunk)들을 조합하여 LLM이 이해하기 쉬운 형태의 Context를 구성합니다.
- LLM Generation (Step 4): 구성된 Context와 대화 이력(Conversation History)을
Gemini 3.0 Pro모델에 전달하여 최종 답변을 생성합니다. - Streaming Response: 긴 답변 생성 시 사용자 체감 속도를 높이기 위해, 토큰 단위의 스트리밍(SSE) 응답을 지원합니다.
Multi-Vector Store Strategy (vectorstore.py)
개발 환경과 운영 환경의 요구사항 차이를 반영한 하이브리드 벡터 스토어 전략입니다.
- Development:
ChromaDB(Local Persistent) - 별도 인프라 없이 로컬 파일시스템에 데이터를 저장하여 빠른 프로토타이핑 지원. - Production:
Qdrant(Enterprise Vector DB) - 대규모 데이터 처리에 최적화된 Qdrant 클러스터를 사용하여 고가용성과 검색 성능 확보.
Intelligent Confidence System
AI 답변의 신뢰성을 보장하기 위한 자체 평가 시스템입니다.
Confidence Formula:
1confidence = (avg_relevance * 0.5) + (source_factor * 0.3) + (length_factor * 0.2)
- Relevance: 검색된 문서들과 질문 간의 유사도 점수.
- Source Coverage: 답변 근거로 사용된 문서의 수.
- Completeness: 생성된 답변의 길이 및 완결성.
- -> 신뢰도(Confidence)가
0.5미만인 경우, 사용자에게 "정확한 정보를 찾지 못했습니다"라고 솔직히 안내하거나 상담원 연결을 유도합니다.
Document Ingestion Engine (document_loader.py)
다양한 형식의 인테리어 문서(PDF, Markdown, HTML 등)를 처리하는 통합 문서 파이프라인입니다.
- Universal Loader:
LangChain의 다양한 로더를 래핑하여, 파일 확장자에 따라 최적의 파싱 전략(Unstructured,PyPDF,BS4)을 자동으로 선택합니다. - Smart Chunking:
RecursiveCharacterTextSplitter를 사용하여 문맥(Context)을 해치지 않는 선에서 텍스트를 최적의 크기로 분할, 검색 정확도를 극대화합니다.
Tech Stack
생성형 AI 파이프라인과 고성능 비동기 처리(Native I/O)를 위해 Java 21 Virtual Threads, Vite 기반 React, 그리고 Python RAG 서비스를 마이크로서비스로 구성했습니다.
BACKEND
Core Backend & AI
Java 21 Virtual Threads로 I/O 병목을 해소하고, Spring Boot 3.2와 Python FastAPI로 이종 언어 마이크로서비스 구축
Java 21 (Virtual Threads)Spring Boot 3.2Python FastAPI (RAG)LangChain & OpenAI/GeminiSpring Security & Firebase Auth
FRONTEND
Frontend & Experience
Vite + React 기반의 SPA로 빠른 반응성을 제공하고, Tailwind CSS + Radix UI로 모던한 디자인 시스템 구현
React 18 + ViteTypeScriptTailwind CSS + Radix UIZustand + React QueryTensorFlow.js (Client AI)
DATA & OPS
Data & Infrastructure
OpenSearch와 Redis로 하이브리드 검색/캐싱을 구현하고, Docker Compose로 로컬/프로덕션 환경 통일
MySQL 8.4Redis (Pub/Sub, Stream)OpenSearch (Hybrid Search)Docker & NginxGitHub Actions