Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.kustacks.kuring.admin.adapter.in.web;

import com.google.firebase.database.annotations.NotNull;
import com.kustacks.kuring.admin.adapter.in.web.dto.AcademicTestNotificationRequest;
import com.kustacks.kuring.admin.adapter.in.web.dto.AdminAlertCreateRequest;
import com.kustacks.kuring.admin.adapter.in.web.dto.RealNotificationRequest;
import com.kustacks.kuring.admin.adapter.in.web.dto.TestNotificationRequest;
Expand Down Expand Up @@ -38,6 +39,7 @@

import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.ADMIN_EMBEDDING_NOTICE_SUCCESS;
import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.ADMIN_REAL_NOTICE_CREATE_SUCCESS;
import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.ADMIN_TEST_ACADEMIC_CREATE_SUCCESS;
import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.ADMIN_TEST_NOTICE_CREATE_SUCCESS;

@Tag(name = "Admin-Command", description = "관리자가 주체가 되는 정보 수정")
Expand Down Expand Up @@ -75,6 +77,18 @@ public ResponseEntity<BaseResponse<String>> createRealNotice(
return ResponseEntity.ok().body(new BaseResponse<>(ADMIN_REAL_NOTICE_CREATE_SUCCESS, null));
}

@Operation(summary = "테스트 학사일정 알림 전송", description = "테스트 학사일정 알림을 전송합니다, 실제 운영시 사용하지 않습니다")
@SecurityRequirement(name = "JWT")
@Secured(AdminRole.ROLE_ROOT)
@PostMapping("/academic/dev")
public ResponseEntity<BaseResponse<String>> sendAcademicTest(
@RequestBody AcademicTestNotificationRequest request
) {
adminCommandUseCase.createAcademicTestNotification(request.toCommand());
return ResponseEntity.ok().body(new BaseResponse<>(ADMIN_TEST_ACADEMIC_CREATE_SUCCESS, null));
}
Comment on lines +80 to +89
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

요청 검증 어노테이션이 누락되었습니다.

@RequestBody@Valid 어노테이션이 없어 AcademicTestNotificationRequest의 검증 규칙이 실행되지 않습니다. 이는 첫 번째 파일에서 지적한 검증 문제와 연관되어 있습니다.

🔎 수정 제안
+import jakarta.validation.Valid;
+
     @Operation(summary = "테스트 학사일정 알림 전송", description = "테스트 학사일정 알림을 전송합니다, 실제 운영시 사용하지 않습니다")
     @SecurityRequirement(name = "JWT")
     @Secured(AdminRole.ROLE_ROOT)
     @PostMapping("/academic/dev")
     public ResponseEntity<BaseResponse<String>> sendAcademicTest(
-            @RequestBody AcademicTestNotificationRequest request
+            @Valid @RequestBody AcademicTestNotificationRequest request
     ) {
         adminCommandUseCase.createAcademicTestNotification(request.toCommand());
         return ResponseEntity.ok().body(new BaseResponse<>(ADMIN_TEST_ACADEMIC_CREATE_SUCCESS, null));
     }
🤖 Prompt for AI Agents
In src/main/java/com/kustacks/kuring/admin/adapter/in/web/AdminCommandApiV2.java
around lines 80 to 89, the controller method parameter for
AcademicTestNotificationRequest is missing the @Valid annotation so its
validation constraints won't run; add @Valid before @RequestBody (i.e., change
to @Valid @RequestBody AcademicTestNotificationRequest request) and ensure the
correct javax.validation.Valid or jakarta.validation.Valid import is present at
the top of the file.



@Operation(summary = "예약 알림 등록", description = "서버에 알림 시간을 yyyy-MM-dd HH:mm:ss 형태로 요청시 예약 알림을 등록한다")
@SecurityRequirement(name = "JWT")
@Secured(AdminRole.ROLE_ROOT)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.kustacks.kuring.admin.adapter.in.web.dto;

import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand;

public record AcademicTestNotificationRequest(
String title,
String body
) {
Comment on lines +5 to +8
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

입력 검증이 누락되었습니다.

titlebody 필드에 검증 어노테이션이 없어 null이나 빈 문자열이 전달될 수 있습니다. 이는 Firebase 알림 전송 시 런타임 오류를 발생시킬 수 있습니다.

🔎 검증 추가를 위한 제안
 package com.kustacks.kuring.admin.adapter.in.web.dto;

 import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand;
+import jakarta.validation.constraints.NotBlank;

 public record AcademicTestNotificationRequest(
+        @NotBlank(message = "제목은 필수입니다")
         String title,
+        @NotBlank(message = "본문은 필수입니다")
         String body
 ) {
     public AcademicTestNotificationCommand toCommand() {
         return new AcademicTestNotificationCommand(title, body);
     }
 }

API 컨트롤러에도 @Valid 어노테이션을 추가해야 합니다:

public ResponseEntity<BaseResponse<String>> sendAcademicTest(
        @Valid @RequestBody AcademicTestNotificationRequest request
)
🤖 Prompt for AI Agents
In
src/main/java/com/kustacks/kuring/admin/adapter/in/web/dto/AcademicTestNotificationRequest.java
around lines 5 to 8, the record lacks input validation for title and body
allowing null/empty values; add validation annotations (e.g. @NotBlank) to both
fields and import the appropriate javax.validation constraints so the request
object enforces non-null/non-empty strings, and update the API controller method
that receives this DTO to annotate the parameter with @Valid (and keep
@RequestBody) so Spring triggers validation on incoming requests.

public AcademicTestNotificationCommand toCommand() {
return new AcademicTestNotificationCommand(title, body);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.kustacks.kuring.admin.application.port.out.AdminEventPort;
import com.kustacks.kuring.common.domain.Events;
import com.kustacks.kuring.message.adapter.in.event.dto.AcademicTestNotificationEvent;
import com.kustacks.kuring.message.adapter.in.event.dto.AdminNotificationEvent;
import com.kustacks.kuring.message.adapter.in.event.dto.AdminTestNotificationEvent;
import com.kustacks.kuring.message.application.service.exception.FirebaseMessageSendException;
Expand Down Expand Up @@ -37,4 +38,9 @@ public void sendTestNotificationByAdmin(
.build()
);
}

@Override
public void sendAcademicTestNotification(String title, String body) {
Events.raise(new AcademicTestNotificationEvent(title, body));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.kustacks.kuring.admin.application.port.in.dto.TestNotificationCommand;
import com.kustacks.kuring.alert.application.port.in.dto.AlertCreateCommand;
import com.kustacks.kuring.alert.application.port.in.dto.DataEmbeddingCommand;
import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand;

public interface AdminCommandUseCase {

Expand All @@ -18,4 +19,7 @@ public interface AdminCommandUseCase {
void embeddingCustomData(DataEmbeddingCommand command);

void resubscribeAllUsersToTopics();

void createAcademicTestNotification(AcademicTestNotificationCommand command);

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ void sendTestNotificationByAdmin(
String korName,
String url
) throws FirebaseMessageSendException;

void sendAcademicTestNotification(String title, String body);

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.kustacks.kuring.auth.userdetails.UserDetailsServicePort;
import com.kustacks.kuring.common.annotation.UseCase;
import com.kustacks.kuring.common.properties.ServerProperties;
import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand;
import com.kustacks.kuring.message.application.port.out.FirebaseSubscribePort;
import com.kustacks.kuring.notice.domain.CategoryName;
import com.kustacks.kuring.user.application.port.out.UserQueryPort;
Expand Down Expand Up @@ -82,6 +83,14 @@ public void createRealNoticeForAllUser(RealNotificationCommand command) {
adminEventPort.sendNotificationByAdmin(command.title(), command.body(), command.url());
}

@Override
public void createAcademicTestNotification(AcademicTestNotificationCommand command) {
adminEventPort.sendAcademicTestNotification(
command.title(),
command.body()
);
}

@Override
public void addAlertSchedule(AlertCreateCommand command) {
adminAlertEventPort.addAlertSchedule(command.title(), command.content(), command.alertTime());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.List;

import static com.kustacks.kuring.message.application.service.FirebaseSubscribeService.ACADEMIC_EVENT_TOPIC;
import static com.kustacks.kuring.message.domain.MessageType.ACADEMIC;

@Slf4j
@UseCase
Expand Down Expand Up @@ -99,6 +100,7 @@ private Message makeMessage(String title, String body) {
.setTopic(serverProperties.ifDevThenAddSuffix(ACADEMIC_EVENT_TOPIC))
.putData("title", title)
.putData("body", body)
.putData("messageType", ACADEMIC.getValue())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public enum ResponseCodeAndMessages {
ADMIN_LOAD_BAD_WORDS(HttpStatus.OK.value(), "금칙어 로드에 성공했습니다."),
ADMIN_LOAD_WHITELIST_WORDS(HttpStatus.OK.value(), "허용 단어 로드에 성공했습니다."),
ADMIN_USER_SUBSCRIPTION_UPDATE_SUCCESS(HttpStatus.OK.value(), "사용자들의 구독 정보를 성공적으로 재설정했습니다."),
ADMIN_TEST_ACADEMIC_CREATE_SUCCESS(HttpStatus.OK.value(), "테스트 학사일정 알림 생성에 성공하였습니다"),

/* User */
USER_REGISTER_SUCCESS(HttpStatus.OK.value(), "회원가입에 성공하였습니다"),
Expand All @@ -52,7 +53,7 @@ public enum ResponseCodeAndMessages {

/* Email */
EMAIL_SEND_SUCCESS(HttpStatus.OK.value(), "이메일 전송에 성공했습니다."),
EMAIL_CODE_VERIFY_SUCCESS(HttpStatus.OK.value(),"인증에 성공했습니다."),
EMAIL_CODE_VERIFY_SUCCESS(HttpStatus.OK.value(), "인증에 성공했습니다."),

REPORT_SEARCH_SUCCESS(HttpStatus.OK.value(), "신고 목록 조회에 성공하였습니다"),
REPORT_COMMENT_SUCCESS(HttpStatus.CREATED.value(), "댓글 신고에 성공했습니다"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kustacks.kuring.message.adapter.in.event;

import com.kustacks.kuring.message.adapter.in.event.dto.AcademicTestNotificationEvent;
import com.kustacks.kuring.message.adapter.in.event.dto.AdminNotificationEvent;
import com.kustacks.kuring.message.adapter.in.event.dto.AdminTestNotificationEvent;
import com.kustacks.kuring.message.adapter.in.event.dto.AlertSendEvent;
Expand Down Expand Up @@ -37,4 +38,13 @@ public void sendAlertEvent(
) {
firebaseWithAdminUseCase.sendNotificationByAdmin(event.toCommand());
}

@Async
@EventListener
public void sendAcademicTestNotificationEvent(
AcademicTestNotificationEvent event
) {
firebaseWithAdminUseCase.sendAcademicTestNotification(event.toCommand());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.kustacks.kuring.message.adapter.in.event.dto;

import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand;

public record AcademicTestNotificationEvent(
String title,
String body
) {
public AcademicTestNotificationCommand toCommand() {
return new AcademicTestNotificationCommand(title, body);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.kustacks.kuring.message.application.port.in;

import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand;
import com.kustacks.kuring.message.application.port.in.dto.AdminNotificationCommand;
import com.kustacks.kuring.message.application.port.in.dto.AdminTestNotificationCommand;

public interface FirebaseWithAdminUseCase {
void sendNotificationByAdmin(AdminNotificationCommand command);

void sendTestNotificationByAdmin(AdminTestNotificationCommand command);

void sendAcademicTestNotification(AcademicTestNotificationCommand command);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kustacks.kuring.message.application.port.in.dto;

public record AcademicTestNotificationCommand(
String title,
String body
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.kustacks.kuring.common.exception.code.ErrorCode;
import com.kustacks.kuring.common.properties.ServerProperties;
import com.kustacks.kuring.message.application.port.in.FirebaseWithAdminUseCase;
import com.kustacks.kuring.message.application.port.in.dto.AcademicTestNotificationCommand;
import com.kustacks.kuring.message.application.port.in.dto.AdminNotificationCommand;
import com.kustacks.kuring.message.application.port.in.dto.AdminTestNotificationCommand;
import com.kustacks.kuring.message.application.port.out.FirebaseMessagingPort;
Expand All @@ -22,7 +23,11 @@
import java.util.Map;
import java.util.function.UnaryOperator;

import static com.kustacks.kuring.message.application.service.FirebaseSubscribeService.ACADEMIC_EVENT_TOPIC;
import static com.kustacks.kuring.message.application.service.FirebaseSubscribeService.ALL_DEVICE_SUBSCRIBED_TOPIC;
import static com.kustacks.kuring.message.domain.MessageType.ACADEMIC;
import static com.kustacks.kuring.message.domain.MessageType.ADMIN;
import static com.kustacks.kuring.message.domain.MessageType.NOTICE;

@Slf4j
@UseCase
Expand Down Expand Up @@ -62,6 +67,29 @@ public void sendNotifications(List<? extends Notice> noticeList) {
sendNotices(notificationDtoList);
}

@Override
public void sendAcademicTestNotification(AcademicTestNotificationCommand command) {
try {
Message newMessage = Message.builder()
.setNotification(Notification.builder()
.setTitle(command.title())
.setBody(command.body())
.build())
.putAllData(Map.of(
"title", command.title(),
"body", command.body(),
"messageType", ACADEMIC.getValue()
))
.setTopic(serverProperties.addDevSuffix(ACADEMIC_EVENT_TOPIC))
.build();

firebaseMessagingPort.send(newMessage);
} catch (FirebaseMessagingException exception) {
throw new FirebaseMessageSendException();
}
}


private void sendNotices(List<NoticeMessageDto> notificationDtoList) {
try {
loggingNoticeSendInfo(notificationDtoList);
Expand Down Expand Up @@ -127,6 +155,7 @@ private Message createMessageFromCommand(AdminNotificationCommand command) {
.setBody(command.body())
.build())
.putAllData(objectMapper.convertValue(command, Map.class))
.putData("messageType", ADMIN.getValue())
.setTopic(serverProperties.ifDevThenAddSuffix(ALL_DEVICE_SUBSCRIBED_TOPIC))
.build();
}
Expand All @@ -139,6 +168,7 @@ private Message createMessageFromDto(NoticeMessageDto messageDto, UnaryOperator<
.setBody(messageDto.getSubject())
.build())
.putAllData(objectMapper.convertValue(messageDto, Map.class))
.putData("messageType", NOTICE.getValue())
.setTopic(suffixUtil.apply(messageDto.getCategory()))
.build();
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/kustacks/kuring/message/domain/MessageType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.kustacks.kuring.message.domain;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum MessageType {

NOTICE("notice"),
ADMIN("admin"),
ACADEMIC("academic");

private final String value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public enum DepartmentName {
BUIS_ADMIN("business_administration", "biz", "경영학과"),
TECH_BUSI("technological_business", "mot", "기술경영학과"),

REAL_ESTATE("real_estate", "rest", "부동산학과"),
REAL_ESTATE("real_estate", "kure", "부동산학과"),

ENERGY("energy", "energy", "미래에너지공학과"),
SMART_VEHICLE("smart_vehicle", "smartvehicle", "스마트운행체공학과"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.kustacks.kuring.worker.parser.notice;

import com.kustacks.kuring.worker.scrap.deptinfo.DeptInfo;
import com.kustacks.kuring.worker.scrap.deptinfo.real_estate.RealEstateDept;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
Expand All @@ -12,7 +11,7 @@ public class LatestPageNoticeHtmlParser extends NoticeHtmlParserTemplate {

@Override
public boolean support(DeptInfo deptInfo) {
return !(deptInfo instanceof RealEstateDept);
return true;
}
Comment on lines 13 to 15
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 파일 구조 탐색
echo "=== 프로젝트 구조 ==="
find . -type f -name "*.java" | grep -E "(RealEstate|LatestPageNotice)" | head -20

echo -e "\n=== 전체 parser 관련 파일 목록 ==="
git ls-files | grep -E "parser.*notice" | head -20

Repository: ku-ring/ku-ring-backend-web

Length of output: 1344


🏁 Script executed:

#!/bin/bash
# RealEstateDept 정의 확인
echo "=== RealEstateDept.java 내용 ==="
cat -n ./src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/real_estate/RealEstateDept.java

echo -e "\n=== LatestPageNoticeHtmlParser.java 전체 내용 ==="
cat -n ./src/main/java/com/kustacks/kuring/worker/parser/notice/LatestPageNoticeHtmlParser.java

Repository: ku-ring/ku-ring-backend-web

Length of output: 3781


🏁 Script executed:

#!/bin/bash
# 테스트 파일 확인
echo "=== LatestPageNoticeApiClientTest.java 내용 ==="
cat -n ./src/test/java/com/kustacks/kuring/worker/scrap/client/notice/LatestPageNoticeApiClientTest.java

echo -e "\n=== 다른 parser 구현 비교 (NoticeHtmlParserTemplate 및 다른 구현체) ==="
rg -nA 20 "class.*NoticeHtmlParser" src/main/java/com/kustacks/kuring/worker/parser/notice/ --type=java

echo -e "\n=== DeptInfo와 support 메서드 사용처 ==="
rg -n "support\(" src/main/java/com/kustacks/kuring/ --type=java | grep -E "(support|DeptInfo)" | head -20

Repository: ku-ring/ku-ring-backend-web

Length of output: 11288


🏁 Script executed:

#!/bin/bash
# NoticeHtmlParserTemplate의 support 메서드 추상화 확인
echo "=== NoticeHtmlParserTemplate.java 전체 ==="
cat -n ./src/main/java/com/kustacks/kuring/worker/parser/notice/NoticeHtmlParserTemplate.java

echo -e "\n=== RealEstateDept HTML 구조 테스트 자료 확인 ==="
find . -type f -name "*.html" | grep -i real
find . -type f -name "*.html" | grep -i estate

echo -e "\n=== 테스트 리소스 전체 ==="
find ./src/test/resources -type f -name "*.html" | head -20

Repository: ku-ring/ku-ring-backend-web

Length of output: 3299


🏁 Script executed:

#!/bin/bash
# RealEstate 테스트 HTML 구조 확인
echo "=== realestate.html 첫 200줄 ==="
head -200 ./src/test/resources/notice/realestate.html

echo -e "\n=== 테이블 구조 검색 ==="
grep -n "board-table\|tbody\|notice" ./src/test/resources/notice/realestate.html | head -30

echo -e "\n=== onclick 속성 및 파싱 관련 요소 검색 ==="
grep -n "jf_viewArtcl\|onclick" ./src/test/resources/notice/realestate.html | head -20

Repository: ku-ring/ku-ring-backend-web

Length of output: 10128


🏁 Script executed:

#!/bin/bash
# RealEstate HTML 테이블 구조 상세 확인
echo "=== RealEstate HTML 테이블 행 구조 ==="
sed -n '180,250p' ./src/test/resources/notice/realestate.html

echo -e "\n=== 파서가 찾는 .board-table 클래스 존재 여부 ==="
grep "board-table" ./src/test/resources/notice/realestate.html

echo -e "\n=== CSE 테스트 HTML (다른 부서)와 비교 - 테이블 클래스 ==="
grep -n "board-table\|<table" ./src/test/resources/notice/cse-notice-2024.html | head -10

echo -e "\n=== CSE HTML의 실제 테이블 구조 ==="
sed -n '1,100p' ./src/test/resources/notice/cse-notice-2024.html | grep -A 30 "<table"

Repository: ku-ring/ku-ring-backend-web

Length of output: 1954


🏁 Script executed:

#!/bin/bash
# CSE HTML 테이블 구조 전체 확인
echo "=== CSE HTML 테이블 및 tbody 구조 ==="
sed -n '1,150p' ./src/test/resources/notice/cse-notice-2024.html | grep -A 50 '<table'

echo -e "\n=== RealEstate 파일에서 .notice 클래스 확인 ==="
grep -n "notice" ./src/test/resources/notice/realestate.html

echo -e "\n=== LatestPageNoticeHtmlParser 테스트 확인 ==="
find . -type f -name "*Test.java" | xargs grep -l "LatestPageNoticeHtmlParser\|LatestPageNoticeApiClient" 2>/dev/null

echo -e "\n=== 현재 사용 중인 parser 선택 로직 ==="
rg -n "filter.*support\(" src/main/ --type=java -A 2 -B 2

Repository: ku-ring/ku-ring-backend-web

Length of output: 5370


🏁 Script executed:

#!/bin/bash
# 공지 파서 선택 로직 확인
echo "=== NoticeScraper 또는 유사 파일 찾기 ==="
find . -type f -name "*Scraper.java" | head -20

echo -e "\n=== 공지 관련 scraper/client 파일 ==="
find . -type f -name "*Notice*.java" -path "*/scrap/*" | head -20

echo -e "\n=== NoticeHtmlParserTemplateTest 내용 ==="
cat -n ./src/test/java/com/kustacks/kuring/worker/parser/NoticeHtmlParserTemplateTest.java

echo -e "\n=== parser 선택 로직 찾기 ==="
rg -n "NoticeHtmlParser\|notice.*parser\|htmlParser" src/main/java/com/kustacks/kuring/worker/scrap/ --type=java | grep -i "filter\|stream\|select" | head -20

Repository: ku-ring/ku-ring-backend-web

Length of output: 17562


🏁 Script executed:

#!/bin/bash
# DepartmentNoticeScraperTemplate 확인
echo "=== DepartmentNoticeScraperTemplate.java ==="
cat -n ./src/main/java/com/kustacks/kuring/worker/scrap/DepartmentNoticeScraperTemplate.java

echo -e "\n=== RealEstate parser 현재 구현 확인 ==="
rg -n "class.*RealEstate" src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/real_estate/ --type=java -A 20 | head -80

Repository: ku-ring/ku-ring-backend-web

Length of output: 7397


🏁 Script executed:

#!/bin/bash
# CSE HTML 구조와 selectors 검증
echo "=== CSE HTML에서 .board-table 확인 ==="
grep -A 50 'class="board-table' ./src/test/resources/notice/cse-notice-2024.html | head -60

echo -e "\n=== .notice 클래스를 갖는 행 찾기 (CSE) ==="
grep -n 'class.*notice' ./src/test/resources/notice/cse-notice-2024.html

echo -e "\n=== onclick 속성과 jf_viewArtcl 확인 (CSE) ==="
grep -n "jf_viewArtcl\|onclick" ./src/test/resources/notice/cse-notice-2024.html | head -20

echo -e "\n=== DeptInfo의 parse 메서드 구현 ==="
rg -n "public.*RowsDto parse\|protected.*RowsDto parse" src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/ --type=java -A 5

Repository: ku-ring/ku-ring-backend-web

Length of output: 3627


🏁 Script executed:

#!/bin/bash
# DeptInfo parse 메서드 확인
echo "=== DeptInfo 클래스 확인 ==="
find . -type f -name "DeptInfo.java" -path "*/deptinfo/*"
cat -n ./src/main/java/com/kustacks/kuring/worker/scrap/deptinfo/DeptInfo.java | head -100

echo -e "\n=== RealEstate HTML 첫 번째 행의 링크 구조 ==="
sed -n '188,210p' ./src/test/resources/notice/realestate.html

echo -e "\n=== CSE HTML 첫 번째 중요 공지의 td 구조 ==="
sed -n '380,410p' ./src/test/resources/notice/cse-notice-2024.html

echo -e "\n=== RealEstate HTML 파싱 테스트 여부 ==="
grep -n "realestate\|RealEstate" src/test/java/com/kustacks/kuring/worker/parser/NoticeHtmlParserTemplateTest.java

Repository: ku-ring/ku-ring-backend-web

Length of output: 5997


🏁 Script executed:

#!/bin/bash
# RealEstate HTML 상세 구조 확인
echo "=== RealEstate HTML에서 tbody, tr, 클래스 구조 확인 ==="
sed -n '175,220p' ./src/test/resources/notice/realestate.html

echo -e "\n=== RealEstate의 td 개수 및 구조 ==="
sed -n '183,197p' ./src/test/resources/notice/realestate.html | grep -E "td|<strong|<a"

echo -e "\n=== CSE HTML의 td 개수 확인 ==="
sed -n '380,410p' ./src/test/resources/notice/cse-notice-2024.html | grep -E "td|<strong|<a"

echo -e "\n=== extractNoticeFromRow에서 사용하는 인덱스 확인 ==="
grep -A 15 "extractNoticeFromRow" ./src/main/java/com/kustacks/kuring/worker/parser/notice/LatestPageNoticeHtmlParser.java

echo -e "\n=== RealEstate가 사용할 수 있는 파서 있는지 확인 ==="
find src/main/java/com/kustacks/kuring/worker/parser -name "*HtmlParser.java" -exec grep -l "support" {} \; | xargs -I {} sh -c 'echo "=== {} ===" && grep -A 3 "public.*support"' {}

Repository: ku-ring/ku-ring-backend-web

Length of output: 2780


🏁 Script executed:

#!/bin/bash
# 최종 검증: RealEstate HTML 구조와 파서의 호환성 확인
echo "=== RealEstate HTML에 .board-table 클래스가 있는지 확인 ==="
grep -c "class=\"board-table" ./src/test/resources/notice/realestate.html || echo "Not found: 0 matches"

echo -e "\n=== RealEstate HTML 테이블 클래스 확인 ==="
grep -n "<table" ./src/test/resources/notice/realestate.html

echo -e "\n=== RealEstate 행의 정확한 td 구조 (첫 번째 행) ==="
python3 << 'EOF'
import re

with open('./src/test/resources/notice/realestate.html', 'r', encoding='utf-8') as f:
    content = f.read()

# Find first tr in tbody
tbody_match = re.search(r'<tbody>.*?<tr>(.*?)</tr>', content, re.DOTALL)
if tbody_match:
    first_row = tbody_match.group(1)
    tds = re.findall(r'<td[^>]*>(.*?)</td>', first_row, re.DOTALL)
    for i, td in enumerate(tds):
        print(f"td[{i}]: {td[:100]}...")
        
# Check for onclick attributes
onclick_matches = re.findall(r'onclick="[^"]*"', content)
print(f"\nonclick 속성 개수: {len(onclick_matches)}")
if onclick_matches:
    print(f"예시: {onclick_matches[0]}")

# Check for jf_viewArtcl
jf_matches = re.findall(r'jf_viewArtcl\([^)]*\)', content)
print(f"jf_viewArtcl 호출 개수: {len(jf_matches)}")
EOF

echo -e "\n=== CSE HTML와 RealEstate HTML 구조 비교 ==="
python3 << 'EOF'
import re

# CSE
with open('./src/test/resources/notice/cse-notice-2024.html', 'r', encoding='utf-8') as f:
    cse_content = f.read()
cse_table = re.search(r'<table[^>]*class="([^"]*)"', cse_content)
cse_jf = len(re.findall(r'jf_viewArtcl', cse_content))

# RealEstate
with open('./src/test/resources/notice/realestate.html', 'r', encoding='utf-8') as f:
    re_content = f.read()
re_table = re.search(r'<table[^>]*>', re_content)
re_jf = len(re.findall(r'jf_viewArtcl', re_content))

print(f"CSE table class: {cse_table.group(1) if cse_table else 'No class'}")
print(f"CSE jf_viewArtcl 개수: {cse_jf}")
print(f"\nRealEstate table: {re_table.group(0) if re_table else 'Not found'}")
print(f"RealEstate jf_viewArtcl 개수: {re_jf}")
EOF

Repository: ku-ring/ku-ring-backend-web

Length of output: 157


🏁 Script executed:

#!/bin/bash
# 간단한 확인
echo "=== RealEstate HTML에 board-table 클래스 있는지 ==="
grep "board-table" ./src/test/resources/notice/realestate.html && echo "FOUND" || echo "NOT FOUND"

echo -e "\n=== RealEstate HTML 테이블 태그 ==="
grep -m1 "<table" ./src/test/resources/notice/realestate.html

echo -e "\n=== RealEstate HTML에 jf_viewArtcl 있는지 ==="
grep "jf_viewArtcl" ./src/test/resources/notice/realestate.html && echo "FOUND" || echo "NOT FOUND"

echo -e "\n=== CSE에 jf_viewArtcl 있는지 ==="
grep -c "jf_viewArtcl" ./src/test/resources/notice/cse-notice-2024.html

echo -e "\n=== RealEstate 행의 정확한 td 개수 ==="
sed -n '183,202p' ./src/test/resources/notice/realestate.html | wc -l

Repository: ku-ring/ku-ring-backend-web

Length of output: 289


RealEstateDept HTML 구조와 파서 호환성 문제를 검증하세요.

RealEstateDept의 HTML 구조가 이 파서와 호환되지 않습니다. RealEstate 페이지에는 .board-table 클래스가 없고 jf_viewArtcl() 함수 호출도 없으며, 링크는 단순 href 속성만 포함합니다. 반면 LatestPageNoticeHtmlParser.board-table > tbody > tr 선택자와 onclick 속성의 jf_viewArtcl() 파싱에 의존합니다. support() 메서드가 return true로 변경되면 RealEstateDept가 이 파서를 사용하게 되어 파싱이 실패합니다.

파서 코드
    protected Elements selectImportantRows(Document document) {
        return document.select(".board-table > tbody > tr").select(".notice");
    }
    
    protected String[] extractNoticeFromRow(Element row) {
        Elements tds = row.getElementsByTag("td");
        String[] splitResults = tds.get(1).select("a").attr("onclick")
                .replace("jf_viewArtcl('", "")
                .replace("')", "")
                .split("', '");
        ...
    }


@Override
Expand Down

This file was deleted.

Loading
Loading