This repository was archived by the owner on Apr 4, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Refactor/#250 #258
Open
GayeongKimm
wants to merge
11
commits into
master
Choose a base branch
from
Refactor/#250
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Refactor/#250 #258
Changes from 9 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
5cfed5d
Feat: springBatch와 Jsoup를 통해 학교 홈페이지 크롤링
GayeongKimm 2f04740
Merge branch 'master' of https://github.com/Team-B1ND/dodamdodam-serv…
GayeongKimm 4a6b5d0
chore: 학교 홈페이지 크롤링 수정
GayeongKimm 2410e5a
chore: 공지 크롤링 비동기화
GayeongKimm a649590
chore: ..
GayeongKimm 610276e
refactor: 크롤링 비동기화
GayeongKimm 064503a
refactor: batch scheduler 적용
GayeongKimm 83daae7
Merge branch 'master' of https://github.com/Team-B1ND/dodamdodam-serv…
GayeongKimm 0daabb1
refactor: spring batch scheduler
GayeongKimm e31e35b
refactor: 주석 삭제
GayeongKimm 4ebf9b5
refactor: 주석 삭제 && 필요없는 애노테이션 삭제
GayeongKimm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
dodam-application/dodam-rest-api/src/main/java/b1nd/dodam/DodamRestApiApplication.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
...am-rest-api/src/main/java/b1nd/dodam/restapi/notice/infrastructure/batch/BatchConfig.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package b1nd.dodam.restapi.notice.infrastructure.batch; | ||
|
|
||
| import b1nd.dodam.domain.rds.notice.entity.Notice; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.batch.core.Job; | ||
| import org.springframework.batch.core.Step; | ||
| import org.springframework.batch.core.job.builder.JobBuilder; | ||
| import org.springframework.batch.core.repository.JobRepository; | ||
| import org.springframework.batch.core.step.builder.StepBuilder; | ||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.transaction.PlatformTransactionManager; | ||
|
|
||
| @Configuration | ||
| @RequiredArgsConstructor | ||
| public class BatchConfig { | ||
|
|
||
| private final JobRepository jobRepository; | ||
| private final PlatformTransactionManager platformTransactionManager; | ||
| private final NoticeItemWriter noticeItemWriter; | ||
| private final NoticeItemProcessor noticeItemProcessor; | ||
| private final NoticelerItemReader noticelerItemReader; | ||
|
|
||
| @Bean | ||
| public Job noticeCrawlingJob() { | ||
| return new JobBuilder("noticeCrawlingJob", jobRepository) | ||
| .start(noticeCrawlingStep()) | ||
| .build(); | ||
| } | ||
|
|
||
| @Bean | ||
| public Step noticeCrawlingStep() { | ||
| return new StepBuilder("noticeCrawlingStep", jobRepository) | ||
| .<Notice, Notice>chunk(10, platformTransactionManager) | ||
| .reader(noticelerItemReader) | ||
| .processor(noticeItemProcessor) | ||
| .writer(noticeItemWriter) | ||
| .build(); | ||
| } | ||
| } |
30 changes: 30 additions & 0 deletions
30
...rest-api/src/main/java/b1nd/dodam/restapi/notice/infrastructure/batch/BatchJobRunner.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package b1nd.dodam.restapi.notice.infrastructure.batch; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.batch.core.Job; | ||
| import org.springframework.batch.core.JobParameters; | ||
| import org.springframework.batch.core.JobParametersBuilder; | ||
| import org.springframework.batch.core.launch.JobLauncher; | ||
| import org.springframework.scheduling.annotation.Scheduled; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class BatchJobRunner { | ||
|
|
||
| private final JobLauncher jobLauncher; | ||
| private final Job job; | ||
|
|
||
| @Scheduled(cron = "0 0 12 * * ?") | ||
| private void executeBatchJob() { | ||
| try { | ||
| JobParameters jobParameter = new JobParametersBuilder() | ||
| .addLong("time", System.currentTimeMillis()) | ||
| .toJobParameters(); | ||
| jobLauncher.run(job, jobParameter); | ||
| } catch (Exception e) { | ||
| e.printStackTrace(); | ||
| } | ||
| } | ||
|
|
||
| } |
15 changes: 15 additions & 0 deletions
15
...api/src/main/java/b1nd/dodam/restapi/notice/infrastructure/batch/NoticeItemProcessor.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package b1nd.dodam.restapi.notice.infrastructure.batch; | ||
|
|
||
| import b1nd.dodam.domain.rds.notice.entity.Notice; | ||
| import org.springframework.batch.item.ItemProcessor; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| public class NoticeItemProcessor implements ItemProcessor<Notice, Notice> { | ||
|
|
||
| @Override | ||
| public Notice process(Notice item) throws Exception { | ||
| return item; | ||
| } | ||
|
|
||
| } |
21 changes: 21 additions & 0 deletions
21
...st-api/src/main/java/b1nd/dodam/restapi/notice/infrastructure/batch/NoticeItemWriter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package b1nd.dodam.restapi.notice.infrastructure.batch; | ||
|
|
||
| import b1nd.dodam.domain.rds.notice.entity.Notice; | ||
| import b1nd.dodam.domain.rds.notice.repository.NoticeRepository; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.batch.item.Chunk; | ||
| import org.springframework.batch.item.ItemWriter; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class NoticeItemWriter implements ItemWriter<Notice> { | ||
|
|
||
| private final NoticeRepository noticeRepository; | ||
|
|
||
| @Override | ||
| public void write(Chunk<? extends Notice> chunk) { | ||
| noticeRepository.saveAll(chunk); | ||
| } | ||
|
|
||
| } |
110 changes: 110 additions & 0 deletions
110
...api/src/main/java/b1nd/dodam/restapi/notice/infrastructure/batch/NoticelerItemReader.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| package b1nd.dodam.restapi.notice.infrastructure.batch; | ||
|
|
||
| import b1nd.dodam.client.core.WebClientSupport; | ||
| import b1nd.dodam.domain.rds.member.entity.Member; | ||
| import b1nd.dodam.domain.rds.member.repository.MemberRepository; | ||
| import b1nd.dodam.domain.rds.notice.entity.Notice; | ||
| import b1nd.dodam.domain.rds.notice.enumration.NoticeStatus; | ||
| import b1nd.dodam.domain.redis.notice.service.NoticeRedisService; | ||
| import b1nd.dodam.restapi.support.async.AsyncConfig; | ||
| import jakarta.annotation.PostConstruct; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.jsoup.Connection; | ||
| import org.jsoup.Jsoup; | ||
| import org.jsoup.nodes.Document; | ||
| import org.jsoup.nodes.Element; | ||
| import org.springframework.batch.core.configuration.annotation.StepScope; | ||
| import org.springframework.batch.item.ItemReader; | ||
| import org.springframework.stereotype.Component; | ||
| import reactor.core.publisher.Flux; | ||
| import reactor.core.publisher.Mono; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.concurrent.ExecutionException; | ||
|
|
||
| @Component | ||
| @StepScope | ||
| @RequiredArgsConstructor | ||
| public class NoticelerItemReader implements ItemReader<Notice> { | ||
|
|
||
| private final MemberRepository memberRepository; | ||
| private final NoticeRedisService noticeRedisService; | ||
| private final WebClientSupport webClient; | ||
| private final AsyncConfig asyncConfig; | ||
| private static final String URL = "https://dgsw.dge.hs.kr"; | ||
| private static final String ADDITIONAL_URL = "/dgswh/na/ntt/selectNttList.do?mi=10091723&bbsId=10091723"; | ||
| private static final String DETAIL_ADDITIONAL_URL = "/dgswh/na/ntt/selectNttInfo.do?mi=10091723&bbsId=10091723"; | ||
| private List<Notice> notices; | ||
| private int nextIndex = 0; | ||
| private Member teacher; | ||
| private Map<String, String> cookies; | ||
| private CompletableFuture<List<Notice>> fetchNoticesFuture; | ||
|
|
||
| @PostConstruct | ||
| public void init() { | ||
| this.teacher = memberRepository.getById("re"); | ||
| fetchNoticesFuture = fetchNoticesArticlesAsync(); | ||
| } | ||
|
|
||
| @Override | ||
| public Notice read() { | ||
| try { | ||
| if (fetchNoticesFuture != null && fetchNoticesFuture.isDone()) { | ||
| if (notices == null) notices = fetchNoticesFuture.get(); | ||
| if (nextIndex < notices.size()) return notices.get(nextIndex++); | ||
| } | ||
| } catch (InterruptedException | ExecutionException e) { | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| public CompletableFuture<List<Notice>> fetchNoticesArticlesAsync() { | ||
| return CompletableFuture.supplyAsync(() -> { | ||
| try { | ||
| Connection.Response response = Jsoup | ||
| .connect(URL + ADDITIONAL_URL) | ||
| .method(Connection.Method.GET) | ||
| .execute(); | ||
| cookies = response.cookies(); | ||
|
|
||
| return fetchDocument(URL + ADDITIONAL_URL, cookies) | ||
| .flatMapMany(doc -> Flux.fromIterable(doc.select("tr"))) | ||
| .flatMap(this::fetchNoticeDetailAsync) | ||
| .collectList() | ||
| .block(); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| }, asyncConfig.batchTaskExecutor()); | ||
| } | ||
|
|
||
| private Mono<Notice> fetchNoticeDetailAsync(Element post) { | ||
| String nttSn = post.select("tr").select("td").select("a").attr("data-id"); | ||
| if (nttSn.isEmpty()) return Mono.empty(); | ||
|
|
||
| String cacheKey = "notice:" + nttSn; | ||
| if (noticeRedisService.validateNotice(cacheKey)) { | ||
| return Mono.empty(); | ||
| } | ||
|
|
||
| return fetchDocument(URL + DETAIL_ADDITIONAL_URL + "&nttSn=" + nttSn, cookies) | ||
| .map(doc -> { | ||
| Element detailElement = doc.select(".bbs_ViewA").first(); | ||
| String title = detailElement.select("h3").text().trim(); | ||
| String content = detailElement.select(".bbsV_cont").text().trim(); | ||
| // String file = detailElement.select(".fname").text().trim(); | ||
|
GayeongKimm marked this conversation as resolved.
Outdated
|
||
| noticeRedisService.setNotice(title, nttSn); | ||
| return new Notice(title, content, NoticeStatus.CREATED, teacher); | ||
| }); | ||
| } | ||
|
|
||
| private Mono<Document> fetchDocument(String url, Map<String, String> cookies) { | ||
| return webClient.batchGet(url, cookies) | ||
| .map(Jsoup::parse) | ||
| .onErrorReturn(new Document("")); | ||
| } | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
...domain-redis/src/main/java/b1nd/dodam/domain/redis/notice/service/NoticeRedisService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package b1nd.dodam.domain.redis.notice.service; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.data.redis.core.StringRedisTemplate; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import java.time.Duration; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class NoticeRedisService { | ||
| private final StringRedisTemplate redisTemplate; | ||
|
|
||
| public void setNotice(String title, String nttSn){ | ||
| String cacheKey = "notice:" + nttSn; | ||
| redisTemplate.opsForValue().set(cacheKey, title, Duration.ofDays(1)); | ||
| } | ||
|
|
||
| public boolean validateNotice(String cacheKey) { | ||
| return redisTemplate.hasKey(cacheKey); | ||
| } | ||
|
|
||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.