
추천 시스템은 오늘날 디지털 서비스의 핵심 경쟁력입니다. Netflix, Amazon, Spotify 같은 거대 기업들이 막대한 자본과 인력을 투입해 구축한 복잡한 시스템들은 이 분야의 이상적인 모델을 제시합니다. 하지만 대부분의 조직, 특히 스타트업이나 중소기업의 현실은 다릅니다. 이론적으로 완벽한 시스템을 구축하기에는 여러 가지 제약과 한계가 존재하기 때문이죠.
8년차 개발자로 다양한 조직을 경험하며 깨달은 것은, "이론적으로 완벽한 시스템보다 운영 가능한 시스템이 더 가치있다"는 점입니다.
이 회고록은 복잡한 AI/ML 기술보다는 '현실적인 선택'과 '점진적 개선'에 초점을 맞춰 Spring Boot와 Apache Flink를 활용해 실시간 추천 시스템을 구축한 과정을 담고 있습니다.
추천 시스템 개발의 첫 번째 관문은 사용자 행동 데이터를 수집하는 것입니다. 이 과정에서 우리는 팀원 모두가 가장 익숙하고, 안정성이 검증된 Spring Boot를 선택했습니다.
@RestController
@RequestMapping("/api/v1/behavior")
@RequiredArgsConstructor
public class UserBehaviorController {
private final UserBehaviorService behaviorService;
@PostMapping("/track")
public ResponseEntity<String> trackUserBehavior(@RequestBody UserBehaviorEvent event) {
// 이벤트 유효성 검증
if (event == null || event.getUserId() == null || event.getActionType() == null) {
return ResponseEntity.badRequest().body("Invalid event data");
}
behaviorService.sendBehaviorEvent(event);
return ResponseEntity.accepted().body("Event tracked successfully");
}
}
기존 배치 처리 방식의 한계(신규 사용자 콜드 스타트, 실시간 반영 불가 등)를 극복하기 위해 실시간 스트리밍 처리가 필요했습니다. 다양한 선택지 중 Apache Flink를 최종적으로 채택했습니다.
public DataStream<UserPreferenceUpdate> buildRealtimeRecommendationStream(DataStream<HomestayBehaviorEvent> behaviorStream) {
// 1분 텀블링 윈도우를 사용해 사용자 행동 집계 및 선호도 업데이트
return behaviorStream
.filter(event -> event.getActionType() != ActionType.VIEW) // 단순 VIEW는 필터링
.keyBy(HomestayBehaviorEvent::getUserId)
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.process(new PreferenceUpdateProcessor()) // 선호도 점수 계산 및 업데이트 로직
.uid("realtime-preference-processor");
}
문제: 신규 사용자에게는 행동 데이터가 없어 개인화된 추천이 불가능합니다.
해결책: "완벽한 개인화"보다 "빠르고 나쁘지 않은" 추천을 제공하는 데 집중했습니다.
public List<Property> getRecommendations(String userId) {
// 1. 사용자 프로필 및 최근 행동 데이터 조회
UserContext userContext = userProfileRepository.findById(userId)
.orElseGet(UserContext::new);
// 2. 신규 사용자 또는 행동 데이터가 부족한 경우
if (userContext.isColdStartUser()) {
// 지역별/테마별 인기 숙소를 추천
return propertyRepository.findTopByLocationAndTheme(userContext.getPreferredLocation(), userContext.getPreferredTheme(), 20);
}
// 3. 행동 데이터를 기반으로 개인화된 추천
return collaborativeFilteringEngine.recommend(userContext);
}
문제: 모든 사용자 행동 이벤트를 실시간으로 처리하는 것은 막대한 컴퓨팅 비용을 초래합니다.
해결책: 비즈니스 임팩트가 큰 **"고가치 이벤트"**만 선별적으로 실시간 처리했습니다.
// 모든 이벤트를 실시간 처리할 필요는 없다
DataStream<HomestayBehaviorEvent> filteredStream = behaviorStream
.filter(event -> event.getActionType() == ActionType.BOOK || event.getActionType() == ActionType.FAVORITE)
.name("high-value-event-filter");
이 프로젝트를 통해 8년차 개발자로서의 핵심 가치관을 다시 한번 확인했습니다.
이 포트폴리오 프로젝트는 단순히 기술적인 성취를 보여주는 것을 넘어, 현업의 냉혹한 현실 속에서 제약 조건을 극복하고 문제를 해결해 나간 과정을 담고 있습니다. 사용자에게 가치를 제공하고, 비즈니스 성장에 기여하는 것이야말로 개발자의 궁극적인 목표임을 다시 한번 깨달았습니다.