2026. 3. 16. 11:21ㆍ📚 빅데이터 분산
빅데이터 분산 처리 프로젝트 - HDFS에 사용자 로그 적재
목표
Spring Boot에서 HDFS에 사용자 행동 로그를 저장하는 파이프라인을 구현하는 것이 목표다.
전체 흐름
클라이언트 → POST /api/logs → Spring Boot → HDFS에 CSV 파일로 저장1. Hadoop 라이브러리 추가
Spring Boot에서 HDFS에 접근하려면 Hadoop Client 라이브러리가 필요하다.build.gradle의 dependencies에 추가했다.
dependencies {
// 기존 의존성 생략...
// HDFS 연동을 위한 Hadoop 라이브러리
implementation 'org.apache.hadoop:hadoop-client:3.2.1'
}
버전은 Docker로 띄운 Hadoop(3.2.1)과 동일하게 맞췄다.
2. 프로젝트 패키지 구조
코드를 역할별로 분리하기 위해 4개의 패키지를 생성했다.
com.travel.bigdata
├── config/ ← 설정 클래스 (HDFS 연결 등)
├── controller/ ← API 엔드포인트
├── dto/ ← 데이터 구조 정의
├── service/ ← 비즈니스 로직
└── BigdataApplication.java3. 로그 데이터 구조 정의 (DTO)
사용자 행동 로그에 담길 데이터를 정의하는 클래스다.
프로젝트 기획서에 명시된 필드(국적, 나이, 성별, 여행목적, 라이프스타일 등)를 그대로 반영했다.
package com.travel.bigdata.dto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class UserLogDto {
private String userId; // 사용자 ID
private String nationality; // 국적
private int age; // 나이
private String gender; // 성별
private String travelPurpose; // 여행 목적
private String lifestyle; // 라이프스타일 (여행성향)
private String action; // 행동 (VIEW, GO, RE_RECOMMEND)
private String placeId; // 장소 ID
private String timestamp; // 로그 생성 시간
}
Lombok 어노테이션
| 어노테이션 | 역할 |
|---|---|
@Getter |
모든 필드의 getter 메서드 자동 생성 |
@Setter |
모든 필드의 setter 메서드 자동 생성 |
@ToString |
toString() 메서드 자동 생성 |
Lombok을 사용하면 보일러플레이트 코드를 직접 작성하지 않아도 된다.
4. HDFS 연결 설정 (Config)
Spring Boot에서 HDFS에 접속하기 위한 설정 클래스다.
package com.travel.bigdata.config;
import org.apache.hadoop.fs.FileSystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HdfsConfig {
private final String hdfsUri = "hdfs://localhost:9000";
@Bean
public FileSystem fileSystem() throws Exception {
org.apache.hadoop.conf.Configuration configuration = new org.apache.hadoop.conf.Configuration();
configuration.set("fs.defaultFS", hdfsUri);
configuration.set("dfs.client.use.datanode.hostname", "true");
// HDFS에 root 사용자로 접속
System.setProperty("HADOOP_USER_NAME", "root");
return FileSystem.get(configuration);
}
}
주요 설정 설명
| 설정 | 설명 |
|---|---|
@Configuration |
이 클래스가 Spring 설정 파일임을 선언 |
@Bean |
이 메서드의 반환 객체를 Spring이 관리하도록 등록 |
fs.defaultFS |
HDFS의 주소. Docker의 NameNode 포트(9000)와 매핑 |
dfs.client.use.datanode.hostname |
DataNode 접근 시 컨테이너 IP 대신 hostname을 사용 |
HADOOP_USER_NAME |
HDFS에 root 권한으로 접속 (권한 문제 방지) |
5. HDFS 저장 로직 (Service)
로그 데이터를 받아서 HDFS에 CSV 파일로 저장하는 핵심 로직이다.
package com.travel.bigdata.service;
import com.travel.bigdata.dto.UserLogDto;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
@Service
public class HdfsService {
private final FileSystem fileSystem;
public HdfsService(FileSystem fileSystem) {
this.fileSystem = fileSystem;
}
public void saveLog(UserLogDto log) throws Exception {
// 날짜별로 폴더를 나눠서 저장
String date = LocalDate.now().toString();
String fileName = "log_" + System.currentTimeMillis() + ".csv";
Path path = new Path("/user-logs/" + date + "/" + fileName);
// CSV 형태로 한 줄 만들기
String csvLine = String.join(",",
log.getUserId(),
log.getNationality(),
String.valueOf(log.getAge()),
log.getGender(),
log.getTravelPurpose(),
log.getLifestyle(),
log.getAction(),
log.getPlaceId(),
log.getTimestamp()
);
// HDFS에 파일 쓰기
try (FSDataOutputStream outputStream = fileSystem.create(path, true)) {
outputStream.writeBytes(csvLine + "\n");
}
}
}
설계 포인트
- 날짜별 폴더 분리:
/user-logs/2026-03-16/형태로 저장하여 Spark가 특정 날짜의 로그만 선택적으로 읽을 수 있도록 했다. - CSV 형태 저장: Spark가 CSV를 쉽게 파싱할 수 있어 분석 단계에서 편리하다.
- 타임스탬프 기반 파일명:
log_1773627176253.csv처럼 밀리초 단위 타임스탬프로 파일명을 생성하여 충돌을 방지했다.
6. API 엔드포인트 (Controller)
외부에서 로그 데이터를 POST 요청으로 보내면 HDFS에 저장하는 API다.
package com.travel.bigdata.controller;
import com.travel.bigdata.dto.UserLogDto;
import com.travel.bigdata.service.HdfsService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/logs")
public class LogController {
private final HdfsService hdfsService;
public LogController(HdfsService hdfsService) {
this.hdfsService = hdfsService;
}
@PostMapping
public ResponseEntity<String> saveLog(@RequestBody UserLogDto logDto) {
try {
hdfsService.saveLog(logDto);
return ResponseEntity.ok("로그 저장 성공");
} catch (Exception e) {
return ResponseEntity.internalServerError().body("로그 저장 실패: " + e.getMessage());
}
}
}
요청-응답 흐름
POST /api/logs (JSON 데이터)
→ LogController가 요청을 받음
→ HdfsService.saveLog() 호출
→ HDFS에 CSV 파일 저장
→ "로그 저장 성공" 응답 반환7. 테스트
API 호출
curl -X POST http://localhost:8090/api/logs \
-H "Content-Type: application/json" \
-d "{\"userId\":\"user001\",\"nationality\":\"KR\",\"age\":25,\"gender\":\"M\",\"travelPurpose\":\"SIGHTSEEING\",\"lifestyle\":\"ADVENTURE\",\"action\":\"VIEW\",\"placeId\":\"place_001\",\"timestamp\":\"2026-03-16T10:30:00\"}"
응답: 로그 저장 성공
HDFS에서 파일 확인
# 파일 목록 확인
docker exec -it travel-namenode hdfs dfs -ls hdfs://namenode:9000/user-logs/2026-03-16/
# 파일 내용 확인
docker exec -it travel-namenode hdfs dfs -cat hdfs://namenode:9000/user-logs/2026-03-16/*.csv
출력 결과:
user001,KR,25,M,SIGHTSEEING,ADVENTURE,VIEW,place_001,2026-03-16T10:30:00전송한 로그 데이터가 CSV 형태로 HDFS에 정상 저장된 것을 확인했다.
트러블슈팅
1. HADOOP_HOME 경고
HADOOP_HOME and hadoop.home.dir are unset.Windows에서 Hadoop 라이브러리를 사용하면 발생하는 경고다.
네트워크로 HDFS에 접속하는 방식이므로 로컬에 Hadoop이 설치되어 있을 필요가 없다.
무시해도 정상 동작한다.
2. Permission denied 에러
Permission denied: user=SSAFY, access=WRITE, inode="/":root:supergroup:drwxr-xr-xHDFS는 root 사용자 권한으로 동작하는데, Spring Boot가 Windows 사용자(SSAFY)로 접속하면 쓰기 권한이 없다.
해결: HdfsConfig에서 System.setProperty("HADOOP_USER_NAME", "root") 설정으로 HDFS에 root 사용자로 접속하도록 했다.
3. DataNode 연결 실패 (UnresolvedAddressException)
java.nio.channels.UnresolvedAddressExceptionSpring Boot(호스트)가 Docker 컨테이너 내부의 DataNode에 접근하지 못하는 문제다.
세 가지를 수정하여 해결했다.
docker-compose.yml 수정:
- DataNode에
hostname: datanode설정 추가 - DataNode에
ports: 9864, 9866포트 매핑 추가 HDFS_CONF_dfs_datanode_hostname: datanode환경변수 추가
HdfsConfig.java 수정:
dfs.client.use.datanode.hostname설정을true로 추가
Windows hosts 파일 수정:
C:\Windows\System32\drivers\etc\hosts에127.0.0.1 datanode추가- 관리자 권한 PowerShell에서 아래 명령어로 추가 가능:
Add-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value "127.0.0.1 datanode"
최종 docker-compose.yml
3단계까지의 변경사항이 반영된 전체 파일이다.
services:
postgres:
image: postgres:15
container_name: travel-postgres
ports:
- "5432:5432"
environment:
POSTGRES_DB: travel_db
POSTGRES_USER: admin
POSTGRES_PASSWORD: admin1234
volumes:
- postgres_data:/var/lib/postgresql/data
namenode:
image: bde2020/hadoop-namenode:2.0.0-hadoop3.2.1-java8
container_name: travel-namenode
ports:
- "9870:9870"
- "9000:9000"
environment:
CLUSTER_NAME: travel-cluster
CORE_CONF_fs_defaultFS: hdfs://namenode:9000
volumes:
- namenode_data:/hadoop/dfs/name
datanode:
image: bde2020/hadoop-datanode:2.0.0-hadoop3.2.1-java8
container_name: travel-datanode
hostname: datanode
depends_on:
- namenode
ports:
- "9864:9864"
- "9866:9866"
environment:
SERVICE_PRECONDITION: "namenode:9870"
CORE_CONF_fs_defaultFS: hdfs://namenode:9000
HDFS_CONF_dfs_datanode_hostname: datanode
volumes:
- datanode_data:/hadoop/dfs/data
spark-master:
image: bde2020/spark-master:3.1.1-hadoop3.2
container_name: travel-spark-master
ports:
- "8080:8080"
- "7077:7077"
environment:
CORE_CONF_fs_defaultFS: hdfs://namenode:9000
spark-worker:
image: bde2020/spark-worker:3.1.1-hadoop3.2
container_name: travel-spark-worker
depends_on:
- spark-master
environment:
SPARK_MASTER: spark://spark-master:7077
CORE_CONF_fs_defaultFS: hdfs://namenode:9000
volumes:
postgres_data:
namenode_data:
datanode_data:
최종 프로젝트 구조
bigdata/
├── build.gradle
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/travel/bigdata/
│ │ │ ├── config/
│ │ │ │ └── HdfsConfig.java ← HDFS 연결 설정
│ │ │ ├── controller/
│ │ │ │ └── LogController.java ← POST /api/logs API
│ │ │ ├── dto/
│ │ │ │ └── UserLogDto.java ← 로그 데이터 구조
│ │ │ ├── service/
│ │ │ │ └── HdfsService.java ← HDFS 저장 로직
│ │ │ └── BigdataApplication.java
│ │ └── resources/
│ │ └── application.yml
│ └── test/
└── gradle/'📚 빅데이터 분산' 카테고리의 다른 글
| [특화 프로젝트] 추천 장소 조회 API 개발 (3) | 2026.03.16 |
|---|---|
| [특화 프로젝트] Spark 배치 분석 및 결과 저장 (0) | 2026.03.16 |
| [특화 프로젝트] Spring Boot 프로젝트 생성 및 PostgreSQL 연동 (2) | 2026.03.16 |
| [특화 프로젝트]Docker 인프라 세팅하는 법 (2) | 2026.03.16 |