- 모든 도서관 검색기에서 검색결과 없음시 구체적인 HTML 조각 추출 - BookSearchResult에 Resulthtml 속성 추가하여 매칭된 HTML 컨텍스트 저장 - Helper_LibraryDelaySettings.cs 추가로 도서관별 검색 지연시간 XML 저장 - Check_copyWD.cs에 dvc_resulthtml 컬럼 표시 및 지연시간 저장 UI 구현 - 15개 SearchModel 파일에서 htmlContent 1000자 자르기를 의미있는 메시지로 교체 - HTTP 검색기들에 한글 인코딩 문제 해결을 위한 헤더 개선 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			159 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # ⚠️ 중요: 대화 시작시 이 파일을 반드시 읽으세요!
 | |
| # 답변은 가급적이면 한글로!
 | |
| # UniMarc 프로젝트 - Claude 작업 가이드
 | |
| 
 | |
| > **Claude에게**: 대화를 시작할 때마다 이 파일을 먼저 읽어서 프로젝트 컨텍스트를 파악하세요.
 | |
| 
 | |
| ## 프로젝트 개요
 | |
| - **프로젝트명**: UniMarc (도서관 자료 관리 시스템)
 | |
| - **기술스택**: C# WinForms, .NET Framework 4.7.2
 | |
| - **데이터베이스**: MySQL
 | |
| - **주요기능**: 마크 작성, 복본조사, DLS 연동, 도서 정보 관리
 | |
| 
 | |
| ## 데이터베이스 정보
 | |
| - Server=1.215.250.130;Port=3306;Database=unimarc;uid=root;pwd=Admin21234;
 | |
| 
 | |
| ## 코딩 컨벤션
 | |
| - 파일명: PascalCase (예: DLS_Copy.cs)
 | |
| - 클래스명: PascalCase
 | |
| - 메서드명: PascalCase
 | |
| - 변수명: camelCase
 | |
| - 상수명: UPPER_CASE
 | |
| 
 | |
| ## 주요 디렉토리 구조
 | |
| - `/마크/`: 마크 관련 폼들
 | |
| - `/납품관리/`: 납품 관리 관련 폼들
 | |
| - `/마스터/`: 마스터 데이터 관리 폼들
 | |
| - `/홈/`: 메인 화면 관련 폼들
 | |
| - `/회계/`: 회계 관련 폼들
 | |
| 
 | |
| ## 개발 시 주의사항
 | |
| 1. WebView2 사용 시 async/await 패턴 적용
 | |
| 2. 데이터베이스 연결은 Helper_DB 클래스 사용
 | |
| 3. 에러 처리는 try-catch 블록으로 처리
 | |
| 4. 한글 주석 사용
 | |
| 
 | |
| ## 빌드 및 배포
 | |
| - Visual Studio 2019 이상 필요
 | |
| - NuGet 패키지 복원 후 빌드
 | |
| - WebView2 런타임 필요
 | |
| - NetFX 프로젝트이므로 dotnet 명령은 사용 불가
 | |
| 
 | |
| ## MsBuild 실행파일 위치 (경로에 공백이 있으니 쌍따옴표로 감싸야 함)
 | |
| ## 매개변수 입력할때 platform 은 제거하고 그냥 프로젝트명만 입력
 | |
| F:\(VHD) Program Files\Microsoft Visual Studio\2022\MSBuild\Current\Bin\msbuild.exe
 | |
| 
 | |
| ## 프로젝트 파일명
 | |
| UniMarc.csproj
 | |
| 
 | |
| ## 도서관 검색기(크롤링) 클래스 작성 가이드
 | |
| 
 | |
| ### 개요
 | |
| 도서관 웹사이트에서 도서 검색 결과를 크롤링하는 클래스들을 구현합니다. 
 | |
| 각 도서관 사이트의 특성에 따라 HTTP 방식 또는 Selenium 방식을 선택합니다.
 | |
| 
 | |
| ### 방식 선택 기준
 | |
| - **HTTP 방식**: URL이 변경되는 사이트 (GET 파라미터로 검색)
 | |
| - **Selenium 방식**: URL이 변경되지 않는 사이트 (POST 폼 제출)
 | |
| 
 | |
| ### 1. HTTP 방식 (GET 요청)
 | |
| **참고 파일**: `SuncheonLibSearcher.cs`
 | |
| 
 | |
| **특징**:
 | |
| - HttpClient 사용
 | |
| - URL 파라미터로 검색
 | |
| - 빠른 실행 속도
 | |
| - HttpApiMode = true
 | |
| 
 | |
| **주요 구현 포인트**:
 | |
| ```csharp
 | |
| // URL 구성
 | |
| var searchUrl = $"{SiteUrl}?search={encodedSearchTerm}&libCode={AreaCode}";
 | |
| 
 | |
| // HTTP 헤더 추가 (500 에러 방지)
 | |
| request.Headers.Add("User-Agent", "Mozilla/5.0...");
 | |
| request.Headers.Add("Accept", "text/html,application/xhtml+xml...");
 | |
| 
 | |
| // HTML에서 결과 수 정규식 추출
 | |
| var patterns = new[] {
 | |
|     @"총\s*<strong[^>]*class=""cred""[^>]*>(\d+)</strong>\s*건"
 | |
| };
 | |
| ```
 | |
| 
 | |
| ### 2. Selenium 방식 (크롤링)
 | |
| **참고 파일**: `GwangjuCityLibSearcher.cs`
 | |
| 
 | |
| **특징**:
 | |
| - Selenium WebDriver 사용
 | |
| - 폼 제출 방식
 | |
| - 복잡한 UI 상호작용 가능
 | |
| - HttpApiMode = false
 | |
| 
 | |
| **주요 구현 포인트**:
 | |
| ```csharp
 | |
| // 도서관 선택 (드롭다운)
 | |
| var libSelect = wait.Until(d => d.FindElement(By.CssSelector("select[name='libCode']")));
 | |
| var selectElement = new SelectElement(libSelect);
 | |
| selectElement.SelectByValue(AreaCode);
 | |
| 
 | |
| // 검색어 입력
 | |
| var searchInput = wait.Until(d => d.FindElement(By.Id("bookSearchQuery")));
 | |
| searchInput.Clear();
 | |
| searchInput.SendKeys(searchTerm);
 | |
| 
 | |
| // 검색 버튼 클릭
 | |
| var searchButton = wait.Until(d => d.FindElement(By.CssSelector("button.bookSearchBtn")));
 | |
| searchButton.Click();
 | |
| 
 | |
| // 페이지 로딩 대기
 | |
| await WaitForPageChange(wait);
 | |
| ```
 | |
| 
 | |
| ### 3. 공통 인터페이스 구현
 | |
| 모든 검색기는 `ILibrarySearcher` 인터페이스를 구현해야 합니다:
 | |
| 
 | |
| ```csharp
 | |
| public interface ILibrarySearcher
 | |
| {
 | |
|     Task<BookSearchResult> SearchAsync(string searchTerm);
 | |
|     Task StartDriver(bool showdriver = false);
 | |
|     void StopDriver();
 | |
|     string SiteName { get; }
 | |
|     string SiteUrl { get; }
 | |
|     bool HttpApiMode { get; set; }
 | |
|     int No { get; set; }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### 4. Check_copyWD.cs에 등록
 | |
| 새로운 검색기를 만든 후 반드시 Check_copyWD.cs의 생성자에 추가:
 | |
| 
 | |
| ```csharp
 | |
| // 예시: 완도군립도서관
 | |
| idx = 1200;
 | |
| _searchService.AddSearcher(new WandoLibSearcher(idx++, "MA", "완도군립도서관"));
 | |
| _searchService.AddSearcher(new WandoLibSearcher(idx++, "MB", "노화공공도서관"));
 | |
| ```
 | |
| 
 | |
| ### 5. 결과 추출 패턴
 | |
| HTML에서 검색 결과 수를 추출하는 일반적인 패턴들:
 | |
| 
 | |
| ```csharp
 | |
| var patterns = new[] {
 | |
|     @"총\s*<strong[^>]*>(\d+)</strong>\s*건",
 | |
|     @"검색결과\s*총\s*(\d+)\s*건",
 | |
|     @"<span[^>]*class=""heighlight""[^>]*>(\d+)</span>",
 | |
|     @"총\s*(\d+)\s*권\(개\)"
 | |
| };
 | |
| ```
 | |
| 
 | |
| ### 6. 에러 처리
 | |
| - 검색 결과 없음: `return 0`
 | |
| - 추출 실패: `return -1`
 | |
| - 예외 발생: BookSearchResult의 ErrorMessage에 설정
 | |
| 
 | |
| ### 현재 구현된 검색기 목록
 | |
| 1. HTTP 방식: SuncheonLibSearcher, MokpoLibSearcher, GwangsanLibSearcher, YeosuLibSearcher
 | |
| 2. Selenium 방식: GwangjuCityLibSearcher, GoheungLibSearcher, BukguLibSearcher 계열, JeonbukEduLibSearcher
 | |
| 
 | |
| 각 방식의 장단점을 고려하여 사이트 특성에 맞는 방식을 선택하여 구현하세요. |