Skip to content

fix: 메이플랜드 공식 사이트 이전으로 인한 크롤러 수정 및 API 변경#29

Merged
mungmnb777 merged 8 commits into
mainfrom
fix/crawler
Apr 7, 2026
Merged

fix: 메이플랜드 공식 사이트 이전으로 인한 크롤러 수정 및 API 변경#29
mungmnb777 merged 8 commits into
mainfrom
fix/crawler

Conversation

@mungmnb777

Copy link
Copy Markdown
Collaborator

요약

  • 크롤러가 변경된 공식 사이트에 맞게 수정되었습니다.
  • Alrim 관련 API가 AlrimLink가 아닌 ID 기반으로 조작할 수 있도록 수정했습니다.

구현 내용 사항

  • AlrimCrawler 이름을 인터페이스로 전환하고, 기존 크롤러는 LegacyAlrimCrawler, 새로운 크롤러는 MaplelandAlrimCrawler로 변경 후 새로운 크롤러를 사용하도록 했습니다.
  • Alrim 배치가 동작할 때, 전체 게시글을 크롤링하고 Alrim에 없는 걸 추가하는 방식으로 로직을 변경했습니다.
  • Alrim V2 Controller를 구현했습니다. 해당 API는 응답에 ID를 추가하고 커서 페이징 시 id를 기반으로 페이징 하도록 변경했습니다. (내부적으로는 datetime + id를 커서로 사용합니다.)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the notification system by introducing a V2 API with cursor-based pagination and a new crawling architecture. It transitions the crawler to an interface-based design, adds a dedicated batch service for notification processing, and implements new DTOs and controllers for the V2 endpoints. Review feedback suggests enhancing data integrity with unique constraints on read records, optimizing JPA transactions by leveraging dirty checking, and improving the resilience of the web crawler. Additionally, recommendations were made to optimize pagination performance and provide default values for API parameters.

@Entity
@Table(name = "alrim_read", indexes = {
@Index(name = "idx_alrim_read_member_id_alrim_link", columnList = "memberId, alrimLink"),
@Index(name = "idx_alrim_read_member_id_alrim_id", columnList = "memberId, alrimId"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

memberId와 alrimId 조합에 대해 유니크 제약 조건을 추가하는 것이 좋습니다. 현재 AlrimCommandService.setReadAlrim에서 exists 체크 후 save를 하고 있는데, 이는 동시성 환경에서 두 요청이 동시에 체크를 통과할 경우 중복 데이터가 생성될 수 있는 Race Condition이 존재합니다. DB 레벨에서 유니크 인덱스를 설정하면 이를 확실히 방지할 수 있습니다.

Suggested change
@Index(name = "idx_alrim_read_member_id_alrim_id", columnList = "memberId, alrimId"),
@Index(name = "idx_alrim_read_member_id_alrim_id", columnList = "memberId, alrimId", unique = true),

Comment on lines +75 to +76
savedEvent.setOutdated(crawledOutdated);
alrimRepository.save(savedEvent);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

@transactional이 적용된 서비스 메서드 내에서 영속성 컨텍스트에 의해 관리되는 엔티티(savedEvent)의 상태를 변경하면, 트랜잭션 종료 시점에 변경 감지(Dirty Checking)를 통해 자동으로 DB에 반영됩니다. 따라서 명시적인 save() 호출은 불필요합니다. 92라인의 markAsOutdated 메서드 내 save() 호출도 동일하게 제거 가능합니다.

Suggested change
savedEvent.setOutdated(crawledOutdated);
alrimRepository.save(savedEvent);
savedEvent.setOutdated(crawledOutdated);

}

private BoardRow toBoardRow(Element anchor, AlrimType type) {
Element row = anchor.parent() != null ? anchor.parent().parent() : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

anchor.parent().parent()와 같이 DOM 계층 구조에 직접 의존하는 방식은 웹사이트의 HTML 구조가 변경될 경우 크롤러가 즉시 오동작하게 되는 원인이 됩니다. Jsoup의 parents()와 적절한 셀렉터를 조합하거나, 행(row)을 특정할 수 있는 더 안정적인 클래스 명 등을 활용하여 요소를 찾는 방식을 권장합니다.

Comment on lines +51 to +53
Alrim cursorAlrim = queryFactory.selectFrom(alrim)
.where(alrim.id.eq(cursorId))
.fetchOne();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

커서 기반 페이징을 처리하기 위해 매번 cursorId로 엔티티를 조회하는 추가 쿼리가 발생하고 있습니다. 현재 정렬 기준이 dateid이므로, 클라이언트로부터 이 두 값을 포함하는 커서(예: 인코딩된 문자열)를 직접 전달받으면 추가 조회 없이 바로 where 절을 구성할 수 있어 성능상 더 효율적입니다. PR 설명에 언급된 '내부적으로 datetime + id를 커서로 사용'하는 방향과도 더 일치할 것입니다.

public ResponseEntity<ResponseTemplate<CursorPage<AlrimV2DTOWithReadInfo>>> getAllAlrim(
@AuthenticationPrincipal PrincipalDetails principalDetails,
@Nullable Long cursor,
int pageSize

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

pageSize 파라미터에 기본값이 설정되어 있지 않습니다. primitive type인 int는 값이 누락될 경우 예외가 발생하거나 기본값 0으로 바인딩될 수 있는데, pageSize가 0일 경우 페이징 로직에서 의도치 않은 동작이 발생할 수 있습니다. @RequestParam(defaultValue = "20")과 함께 적절한 범위 검증을 추가하는 것이 안전합니다. 컨트롤러 내 다른 메서드들에도 공통적으로 적용이 필요해 보입니다.

Suggested change
int pageSize
@RequestParam(defaultValue = "20") int pageSize

@mungmnb777 mungmnb777 merged commit b31e5c8 into main Apr 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant