보고 싶은 책을 검색하고 해당 책을 보유한 도서관을 지도에서 확인할 수 있는 BookSpot 서비스의 소스 데이터를 저장하는 Batch 서버 코드 저장소
✨ Backend(API + Batch)
✨ Frontend
- 도서관 동기화 Step (ChunkSize - 200)
- BeforeStep: 정보나루에서 도서관 Excel 파일 다운로드
- Reader: 다운로드된 도서관 Excel 파일 읽기 -
close()호출 시 읽은 파일 삭제 - Writer: DB 저장
- 도서관 상세 정보 파싱 Step (ChunkSize - 200)
- Reader : 정보나루 도서관 페이지 크롤링
- Writer : 도서관 이름과 주소가 일치하는 도서관의 naru_detail 필드 업데이트
1월 기준. 1500개의 파일. 17.5GB의 전체 파일 크기
- 파일 다운로드 Step (ChunkSize - 10)
- Reader: 도서관 테이블에서 크롤링에 필요한 정보 가져오기
- Processor: 가장 최근 소장 도서 csv 파일 경로를 크롤링
- Writer: 도서관 소장 도서 csv 파일 다운로드
IsbnSet의 내부 구현이 HashSet => LongHashSet으로 변경됨
- 책 정보 동기화 Master Step (파티셔닝 - 멀티 스레딩)
- beforeStep: Book 테이블에서 ISBN13을 읽어와서
IsbnSet초기화 - 책 정보 동기화 Slave Step (ChunkSize - 1,300)
- Reader: 도서관 소장 도서 csv 파일 읽기
- Processor
- 유효하지 않은 ISBN13 필터링
IsbnSet필터링- 300자를 초과하는 제목을
...으로 생략 - DB에 저장할 타입으로 변환
- Writer: Book 테이블에 저장
- afterStep:
IsbnSet clearAll()호출
- beforeStep: Book 테이블에서 ISBN13을 읽어와서
Map<Long, AtomicInteger> => long[], AtomicIntegerArray로 변경됨
Map의 map.contains(isbn13)을 Arrays.binarySearch(isbnArray, isbn13)으로 대체됨
-
대출 수 읽기 Master Step (파티셔닝 - 멀티스레딩)
- beforeStep: 집계할 메모리 저장소 초기화
- DB의 ISBN13을 읽어와서
long[] isbnArray에 모두 삽입 후Arrays.sort(isbnArray)로 정렬 AtomicIntegerArray loanArray생성
- DB의 ISBN13을 읽어와서
- 대출 수 읽기 Slave Step -
allowStartIfComplete(true)(ChunkSize - 1,000)- Reader: 도서관 소장 도서 csv 파일 읽기
- Processor
- 유효하지 않은 ISBN13 필터링
{ISBN13-대출 수}객체로 변환Arrays.binarySearch(isbnArray, isbn13)으로 ISBN13이 있는 인덱스를 찾을 수 없다면 필터링
- Writer: 메모리 저장소에 있는
loanArray값 증가
- afterStep: 메모리 저장소 내용을 파일로 생성 + 집계한 메모리 저장소 청소
- 메모리 저장소의 내용으로 대출 수 집계 파일 생성
isbnArray와loanArray에 null로 세팅
- beforeStep: 집계할 메모리 저장소 초기화
-
대출 수 동기화 Step (ChunkSize - 3,000)
- Reader: 대출 수 종합 파일읽기
- Writer: Book 테이블에 대출 수 반영
Insert Step에서 사용하는 Set이 HashSet<Long> => LongHashSet으로 변경됨
Delete 파일 생성 Step에서 사용하는 Map을 Map<Long, Boolean> => LongBooleanHahsMap으로 변경됨
-
도서관 소장 도서 데이터 정제 MasterStep
- beforeStep:
Map<ISBN13,BookDbId>초기화 - 도서관 소장 도서 데이터 정제 SlaveStep (ChunkSize - 800)
- 책제목,발행년도,저자,대출수,... => 책ID,도서관ID
- Reader: 도서관 소장 도서 csv 파일 읽기 -
close()호출 시 읽은 파일 삭제 - Processor
- 유효하지 않은 ISBN13 필터링
{bookId, libraryId}객체로 변환
- Writer:
{bookId, libraryId}정보가 담긴 csv 파일 생성
- afterStep: 사용이 끝난
Map<ISBN13,BookDbId>메모리 정리
- beforeStep:
-
중복 책 필터링 MasterStep
- 중복된 {책ID, 도서관ID} 제거.
{1,1},{2,1},{1,1} => {1,1},{2,1} - 중복 책 필터링 SlaveStep (ChunkSize - 1,500)
BookIdSet을@StepScope로 생성- Reader: 정제된 도서관 소장 도서 csv 파일 읽기 -
close()호출 시 읽은 파일 삭제 - Processor:
BookIdSet에 존재하면 필터링. 존재하지 않는다면add(BookId) - Writer: 중복을 제거한 도서관 소장 도서 파일 생성
- 중복된 {책ID, 도서관ID} 제거.
- Insert Step (파티셔닝 - 멀티스레딩) (ChunkSize - 15_000)
Library_Stock테이블에서 도서관이 가지고 있는 BookId를 가져와서@StepScope로BookIdSet생성- Reader: 정제된 csv 파일 읽기
- Processor:
BookIdSet에 존재하는 BookId를 필터링 - Writer: 필터링되지 않은 데이터를
Library_Stock테이블에 Insert
- Delete 파일 생성 Step (파티셔닝 - 멀티스레딩)
- beforeStep:
Library_Stock테이블에서 도서관이 가지고 있는 BookId를 가져와서Map<BookId,Boolean>에{BookId, False}로 추가 +@StepScope로 생성 - Reader: 정제된 csv 파일 읽기 -
close()호출 시 읽은 파일 삭제 - Writer: 등장한 BookId을 Map에서 찾아서 True로 세팅
- afterStep: csv파일에 등장하지 않은(False) BookId를 파일로 저장
- beforeStep:
- Delete Step (파티셔닝 - 멀티스레딩) (ChunkSize - 5,000)
- Delete 파일을 읽고 사라진 도서관 소장 도서 정보 Delete -
close()호출 시 읽은 파일 삭제
- Delete 파일을 읽고 사라진 도서관 소장 도서 정보 Delete -
- OpenSearch 인덱스 생성 Step
- (25년 6월 기준) books-2025-06 인덱스 생성
- OpenSearch 동기화 Step (ChunkSize - 150)
- Reader: 책 테이블 정보 읽기
- Processor: Document로 변환하기
- Writer: OpenSearch로 Bulk Insert
- OpenSearch 인덱스 청소 Step
- (25년 6월 기준) books-2025-06 인덱스에 books Alias 부여.
- (25년 6월 기준) books-2025-05 인덱스에 books Alias 제거.
- (25년 6월 기준) books-2025-04 인덱스 제거.
- 정보나루 API : 일일 30,000건 제한
- 알라딘 API : 일일 5,000건 - 서비스 URL 필요
- 네이버 책 API : 일일 25,000건
- 카카오 책 API : 일일 총 5만건, API 별 3만건
- 정보나루 최신 소장 도서 CSV 파일 : 크롤링 방식
일일 제한없이 처리할 수 있는 CSV 파일 크롤링 방식 선택.
