2026. 3. 16. 12:19ㆍ📚 빅데이터 분산
빅데이터 분산 처리 프로젝트 - 추천 장소 조회 API 개발
목표
분석 결과를 활용하여 "나와 비슷한 여행자들의 pick"을 반환하는 REST API를 구현하는 것이 목표다.
전체 흐름
클라이언트 요청
→ GET /api/recommend?nationality=KR&ageGroup=20s&gender=M&...
→ Spring Boot가 PostgreSQL의 popular_places 테이블 조회
→ 조건에 맞는 추천 장소 목록을 JSON으로 반환1. Entity 클래스 생성
Entity는 PostgreSQL 테이블을 Java 클래스로 매핑한 것이다.
Spark가 생성한 popular_places 테이블의 각 컬럼이 Java 필드가 된다.
패키지 구조
com.travel.bigdata.entity 패키지를 새로 생성했다.
PopularPlace.java
package com.travel.bigdata.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "popular_places")
@Getter
@Setter
public class PopularPlace {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nationality;
@Column(name = "age_group")
private String ageGroup;
private String gender;
@Column(name = "travel_purpose")
private String travelPurpose;
private String lifestyle;
@Column(name = "place_id")
private String placeId;
@Column(name = "visit_count")
private Long visitCount;
@Column(name = "total_score")
private Long totalScore;
private Integer rank;
}
주요 어노테이션 설명
| 어노테이션 | 설명 |
|---|---|
@Entity |
이 클래스가 DB 테이블과 연결됨을 선언 |
@Table(name = "popular_places") |
매핑할 테이블 이름 지정 |
@Id |
기본 키(Primary Key) 필드 |
@GeneratedValue |
기본 키 자동 생성 전략 |
@Column(name = "age_group") |
Java의 카멜케이스(ageGroup)와 DB의 스네이크케이스(age_group)를 매핑 |
2. Repository 생성
Repository는 DB에서 데이터를 조회하는 역할을 한다.
Spring Data JPA를 사용하면 메서드 이름만으로 SQL 쿼리가 자동 생성된다.
왜 Interface인가?
Repository는 Class가 아닌 Interface로 생성한다.
Spring Data JPA가 메서드 이름을 분석하여 실제 구현 코드를 자동 생성해주기 때문이다.
개발자는 "어떤 조건으로 찾을지" 메서드 이름만 정의하면 된다.
PopularPlaceRepository.java
com.travel.bigdata.repository 패키지를 새로 생성했다.
package com.travel.bigdata.repository;
import com.travel.bigdata.entity.PopularPlace;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface PopularPlaceRepository extends JpaRepository<PopularPlace, Long> {
List<PopularPlace> findByNationalityAndAgeGroupAndGenderAndTravelPurposeAndLifestyleOrderByRankAsc(
String nationality, String ageGroup, String gender, String travelPurpose, String lifestyle
);
}
메서드 이름 → SQL 자동 변환
메서드 이름이 길지만, Spring Data JPA가 이를 파싱하여 아래 SQL을 자동 생성한다.
findBy → SELECT * FROM popular_places WHERE
Nationality → nationality = ?
And AgeGroup → AND age_group = ?
And Gender → AND gender = ?
And TravelPurpose → AND travel_purpose = ?
And Lifestyle → AND lifestyle = ?
OrderByRankAsc → ORDER BY rank ASCSQL을 직접 작성할 필요 없이, 메서드 이름 규칙만 지키면 된다.
3. 조회 API Controller 생성
사용자의 조건(국적, 연령대, 성별, 여행목적, 라이프스타일)을 받아
해당 군집의 인기 장소를 반환하는 API다.
RecommendController.java
controller 패키지에 생성했다.
package com.travel.bigdata.controller;
import com.travel.bigdata.entity.PopularPlace;
import com.travel.bigdata.repository.PopularPlaceRepository;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/recommend")
public class RecommendController {
private final PopularPlaceRepository repository;
public RecommendController(PopularPlaceRepository repository) {
this.repository = repository;
}
@GetMapping
public ResponseEntity<List<PopularPlace>> getRecommendations(
@RequestParam String nationality,
@RequestParam String ageGroup,
@RequestParam String gender,
@RequestParam String travelPurpose,
@RequestParam String lifestyle
) {
List<PopularPlace> results = repository
.findByNationalityAndAgeGroupAndGenderAndTravelPurposeAndLifestyleOrderByRankAsc(
nationality, ageGroup, gender, travelPurpose, lifestyle
);
return ResponseEntity.ok(results);
}
}
동작 흐름
GET /api/recommend?nationality=KR&ageGroup=20s&gender=M&travelPurpose=SIGHTSEEING&lifestyle=ADVENTURE
→ RecommendController가 요청 파라미터를 받음
→ PopularPlaceRepository의 메서드 호출
→ Spring Data JPA가 SQL 자동 생성 및 실행
→ 결과를 JSON 리스트로 반환4. 테스트
API 호출
curl "http://localhost:8090/api/recommend?nationality=KR&ageGroup=20s&gender=M&travelPurpose=SIGHTSEEING&lifestyle=ADVENTURE"
응답 결과
[
{
"id": 1,
"nationality": "KR",
"ageGroup": "20s",
"gender": "M",
"travelPurpose": "SIGHTSEEING",
"lifestyle": "ADVENTURE",
"placeId": "place_001",
"visitCount": 3,
"totalScore": 5,
"rank": 1
},
{
"id": 2,
"nationality": "KR",
"ageGroup": "20s",
"gender": "M",
"travelPurpose": "SIGHTSEEING",
"lifestyle": "ADVENTURE",
"placeId": "place_003",
"visitCount": 2,
"totalScore": 4,
"rank": 2
}
]
20대 한국인 남성 관광 모험형 여행자에게 place_001(1위, 점수 5)과 place_003(2위, 점수 4)이 추천되었다.
DB 확인 (DBeaver)
DBeaver에서 popular_places 테이블의 Data 탭을 클릭하면
API가 조회하는 데이터를 시각적으로 확인할 수 있다.
최종 프로젝트 구조
bigdata/
├── build.gradle
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/travel/bigdata/
│ │ │ ├── config/
│ │ │ │ └── HdfsConfig.java ← HDFS 연결 설정
│ │ │ ├── controller/
│ │ │ │ ├── LogController.java ← POST /api/logs (로그 저장)
│ │ │ │ └── RecommendController.java ← GET /api/recommend (추천 조회)
│ │ │ ├── dto/
│ │ │ │ └── UserLogDto.java ← 로그 데이터 구조
│ │ │ ├── entity/
│ │ │ │ └── PopularPlace.java ← popular_places 테이블 매핑
│ │ │ ├── repository/
│ │ │ │ └── PopularPlaceRepository.java ← DB 조회 인터페이스
│ │ │ ├── service/
│ │ │ │ └── HdfsService.java ← HDFS 저장 로직
│ │ │ └── BigdataApplication.java
│ │ └── resources/
│ │ └── application.yml
│ └── test/
└── gradle/
travel-project/
├── docker-compose.yml
└── spark-jobs/
├── analyze_logs.py
└── postgresql-42.7.4.jarAPI 목록
| 메서드 | URL | 설명 |
|---|---|---|
| POST | /api/logs |
사용자 행동 로그를 HDFS에 저장 |
| GET | /api/recommend |
나와 비슷한 여행자들의 추천 장소 조회 |
GET /api/recommend 파라미터
| 파라미터 | 예시 | 설명 |
|---|---|---|
| nationality | KR | 국적 |
| ageGroup | 20s | 연령대 (10s, 20s, 30s, 40s, 50s+) |
| gender | M | 성별 (M, F) |
| travelPurpose | SIGHTSEEING | 여행 목적 |
| lifestyle | ADVENTURE | 라이프스타일 (여행성향) |
전체 프로젝트 완료 현황
| 단계 | 내용 | 상태 |
|---|---|---|
| 1단계 | Docker 인프라 세팅 (PostgreSQL, Hadoop, Spark) | ✅ 완료 |
| 2단계 | Spring Boot 프로젝트 생성 + PostgreSQL 연동 | ✅ 완료 |
| 3단계 | HDFS에 사용자 로그 적재 | ✅ 완료 |
| 4단계 | Spark 배치 분석 + 결과 저장 | ✅ 완료 |
| 5단계 | 추천 장소 조회 API 개발 | ✅ 완료 |
전체 파이프라인 요약
사용자가 장소를 보거나(VIEW) 방문(GO)
→ POST /api/logs로 로그 전송
→ Spring Boot가 HDFS에 CSV로 저장
→ 새벽 4시 Spark 배치 실행 (spark-submit)
→ HDFS 로그를 읽어서 군집별 인기 장소 분석
→ 분석 결과를 PostgreSQL popular_places 테이블에 저장
→ 사용자가 앱에 접속하면 GET /api/recommend 호출
→ 국적/연령대/성별/여행목적/라이프스타일 기반으로 추천 장소 반환기술 스택 요약
| 기술 | 역할 |
|---|---|
| Java 21 / Spring Boot 3.5.11 | 백엔드 API 서버 |
| PostgreSQL 15 | 분석 결과 저장 (RDB) |
| Hadoop HDFS 3.2.1 | 대용량 사용자 로그 분산 저장 |
| Apache Spark 3.1.1 | 빅데이터 배치 분석 (군집별 인기 장소) |
| Docker / Docker Compose | 인프라 컨테이너 관리 |
| DBeaver | DB 시각적 관리 도구 |
'📚 빅데이터 분산' 카테고리의 다른 글
| [특화 프로젝트] Spark 배치 분석 및 결과 저장 (0) | 2026.03.16 |
|---|---|
| [특화 프로젝트] HDFS에 사용자 로그 적재 (0) | 2026.03.16 |
| [특화 프로젝트] Spring Boot 프로젝트 생성 및 PostgreSQL 연동 (2) | 2026.03.16 |
| [특화 프로젝트]Docker 인프라 세팅하는 법 (2) | 2026.03.16 |