# 범용 온톨로지 구축 플랫폼 통합 설계안 작성일: 2026-05-13 대상 자료: `오픈소스분석자료` 폴더의 8개 분석 문서 ## 0. 최종 결론 시작 프로젝트는 현재 저장소의 `crawler_platform`을 유지한다. 이미 FastAPI, SQLAlchemy, 프로젝트/소스/Page/Entity/Claim/Evidence 모델, 크롤링 파이프라인, 웹 UI, 연구 루프 일부가 존재하므로 이것을 버리고 외부 프로젝트 하나로 갈아타는 것은 손실이 크다. 다만 8개 오픈소스 중 “기본 엔진” 역할은 `OntoCast`가 가장 적합하다. OntoCast는 문서 입력에서 RDF 온톨로지와 Facts를 만들고, GraphUpdate/SPARQL 증분 갱신, renderer/critic retry loop, entity aggregation, triple store abstraction을 갖고 있어 온톨로지 구축 코어에 가장 직접적이다. 권장 구조는 다음과 같다. ```text crawler_platform # 제품/플랫폼 껍데기. 계속 유지 Platform API / UI / DB / Jobs Source & Dataset Management Review / Approval / Versioning Adapters Trafilatura # 웹 본문/메타/구조 추출 Crawl4AI # JS/동적/딥 크롤링, 필요 시 추가 OntoCast Core # RDF ontology/facts 생성 엔진 Guardrails # LLM 구조화 출력 검증 게이트 Neo4j GraphRAG # KG projection, GraphRAG, Text2Cypher Optional / reference only Firecrawl # API/옵션 설계 참고, 초기 직접 통합 제외 Knowledge Agent # gap/audit workflow 패턴 참고 OpenDeepResearcher # 외부 검색 루프 패턴 참고 ``` 초기 실제 통합 수는 최대한 줄인다. 1. 1차 실제 통합: `Trafilatura`, `OntoCast`, `Guardrails` 2. 2차 실제 통합: `Neo4j GraphRAG` 3. 3차 실제 통합: `Crawl4AI` 4. 코드 통합 보류: `Firecrawl` 5. 패턴/프롬프트만 차용: `Knowledge Agent`, `OpenDeepResearcher` 이렇게 하면 핵심 기능은 상용제품 수준으로 설계하면서도, 한 번에 여러 거대 프로젝트를 섞어서 생기는 버그를 피할 수 있다. ## 1. 8개 오픈소스별 채택 판단 | 오픈소스 | 가장 큰 장점 | 채택 방식 | 초기 통합 여부 | |---|---|---|---| | OntoCast | 문서 기반 RDF ontology/facts 생성, GraphUpdate/SPARQL 증분 갱신, renderer/critic retry loop, entity aggregation | 코어 엔진으로 거의 원형 유지. API/UI는 현재 플랫폼에서 새로 감싼다 | 필수 P0 | | Trafilatura | HTML 본문/메타데이터/링크/표/중복 fingerprint 추출이 안정적이고 Apache-2.0 | Python API를 adapter로 사용. `bare_extraction(output_format="python")` 중심 | 필수 P0 | | Guardrails | Pydantic/JSON Schema 기반 LLM 출력 검증, validator, reask/fix/filter 정책 | `OntologyGuard` facade로 감싸고 온톨로지 전용 validator 추가 | 필수 P0 | | Neo4j GraphRAG | GraphSchema, SimpleKGPipeline, Neo4jWriter, Vector/Hybrid/Text2Cypher Retriever | 고정 버전 dependency + wrapper. Neo4j는 canonical store가 아니라 projection/search 계층 | 필수 P1 | | Crawl4AI | Python 기반 비동기 크롤링, Playwright, Markdown, deep crawl, dispatcher/cache | JS-heavy/dynamic source 전용 adapter. Trafilatura 실패 시 fallback | 필수 P2 | | Firecrawl | 상용급 scrape/map/crawl/batch/search API 표면과 job 운영 모델 | API/옵션/상태 모델 참고. AGPL/TypeScript/운영 복잡도 때문에 초기 직접 병합 제외 | 보류 | | Knowledge Agent | 지식 공백 탐지, 연구-큐레이션-감사-수정-개선 루프 | LangGraph workflow와 LightRAG 프롬프트 패턴만 차용. 코드 안정화 후 일부 도입 | 패턴 P2 | | OpenDeepResearcher | 검색어 생성, 검색, 페이지 유용성 평가, 추가 검색 판단 반복 루프 | 작은 모듈로 재작성. 원본 notebook 코드는 그대로 제품 코드에 넣지 않음 | 패턴 P2 | ## 2. 중복 기능 제거 원칙 중복되는 프로젝트를 동시에 같은 책임으로 쓰지 않는다. | 책임 | 최종 선택 | 제외/보류 | |---|---|---| | 정적 웹 본문 추출 | Trafilatura | Firecrawl scrape를 기본으로 쓰지 않음 | | 동적 페이지/딥 크롤 | Crawl4AI | Firecrawl과 Crawl4AI 동시 기본 사용 금지 | | 문서 기반 RDF 온톨로지 생성 | OntoCast | Neo4j GraphRAG의 자유 KG 추출을 canonical ontology로 직접 확정하지 않음 | | LLM 출력 검증 | Guardrails | 자체 ad-hoc JSON validation만으로 끝내지 않음 | | GraphRAG/질의응답 | Neo4j GraphRAG | OntoCast triple store에 질의응답 기능을 억지로 모두 구현하지 않음 | | 외부 검색 연구 | Platform ResearchLoop | Knowledge Agent와 OpenDeepResearcher를 각각 독립 실행하지 않음 | | Canonical 저장소 | RDF/Fuseki + relational metadata | Neo4j를 원본 truth store로 삼지 않음 | 핵심 규칙: 1. 모든 수집 결과는 먼저 `Document`와 `ContentUnit`으로 정규화한다. 2. 모든 LLM 산출물은 `Candidate` 상태로 저장하고 바로 published graph에 넣지 않는다. 3. 모든 엔티티/관계/트리플은 evidence와 provenance 없이는 승인할 수 없다. 4. RDF/Fuseki를 canonical semantic store로 둔다. 5. Neo4j는 projection, graph search, GraphRAG, Text2Cypher용으로 둔다. 6. Firecrawl은 초기에는 직접 통합하지 않고, API 설계와 운영 상태 모델만 참고한다. ## 3. 상용제품 기준 전체 기능 설계 ### 3.1 제품 모듈 ```text Ontology Studio Platform Project & Tenant - 프로젝트 생성/설정/권한 - 도메인 정책, 언어 정책, LLM/embedding profile - source trust policy, robots/license/privacy policy Dataset & Source - 파일 업로드, URL seed, sitemap/feed discovery - source catalog, update schedule - source reliability score, blocklist, allowlist Ingestion - Trafilatura static extraction - Crawl4AI dynamic/deep crawling - document parse: PDF/DOCX/HTML/JSON/CSV - dedup, content hash, fingerprint - provenance, raw artifact storage Ontology Build Engine - ContentUnit chunking - ontology selection or fresh ontology creation - GraphUpdate/SPARQL delta generation - facts extraction - renderer/critic/retry loop - entity aggregation and URI normalization Validation Gate - Pydantic schema validation - JSON repair/type normalization - ontology-specific validator - SHACL/OWL validation - evidence alignment validation - reask/fix/filter/refrain policy Review & Governance - candidate entity/relation/triple review - source evidence highlight - GraphUpdate diff viewer - approve/reject/merge/split/edit - reviewer audit log - schema draft -> published workflow - rollback/release/version tagging Storage - relational DB: project/job/source/page/review/audit metadata - artifact store: raw HTML, markdown, extracted JSON, TTL, screenshots - Fuseki/RDF store: canonical ontology/facts - Neo4j: graph projection, vector/fulltext index, GraphRAG Search & Use - SPARQL query - graph neighborhood search - vector/hybrid search - GraphRAG answer with provenance - read-only Text2Cypher - export: TTL, RDF/XML, JSON-LD, CSV, Parquet Research & Improvement - knowledge gap detection - external search planning - usefulness scoring - context/evidence extraction - repeated failure analysis - schema/prompt/source policy improvement suggestions Operations - async job queue - progress/cancel/retry - cost/budget tracking - LLM cache - metrics/logs/traces - backup/restore - admin safety controls ``` ### 3.2 기준 데이터 모델 현재 `crawler_platform` 모델을 확장한다. 필수 추가/정리 모델: | 모델 | 목적 | |---|---| | `Dataset` | 프로젝트 내 문서 묶음, import batch 단위 | | `Document` | URL/파일/API 응답의 정규화 원문 | | `ContentUnit` | chunk, source offsets, section/table/list 정보 | | `Artifact` | raw html, markdown, body xml, TTL, JSON, screenshot 저장 위치 | | `OntologySchemaVersion` | draft/published/archived schema, version, hash | | `GraphDelta` | OntoCast GraphUpdate/SPARQL delta와 적용 상태 | | `CandidateEntity` | 검수 전 엔티티 후보 | | `CandidateRelation` | 검수 전 관계 후보 | | `CandidateTriple` | 검수 전 RDF/property graph 후보 | | `ValidationRun` | Guardrails/SHACL/OWL 검증 결과 | | `ReviewDecision` | 승인/반려/수정/병합 이력 | | `EntityMergeCandidate` | exact/fuzzy/embedding merge 후보 | | `ResearchSession` | 외부 검색/공백 보완 세션 | | `JobRun` | 수집/추출/검증/저장 작업 상태 | 기존 `Page`, `Entity`, `Claim`, `Evidence`, `OntologyTriple`, `KnowledgeGap`은 유지하되 아래 필드를 보강한다. - `Page`: `markdown`, `body_xml_ref`, `fingerprint`, `language`, `change_status`, `last_checked_at` - `Claim`: `candidate_status`, `validation_status`, `review_status`, `ontology_version` - `Evidence`: `content_unit_id`, `char_start`, `char_end`, `selector`, `quote_hash` - `OntologyTriple`: `graph_uri`, `ontology_version_id`, `rdf_subject`, `rdf_predicate`, `rdf_object`, `provenance_graph_uri` ## 4. 최종 아키텍처 ```mermaid flowchart TB UI["Ontology Studio UI"] --> API["FastAPI Platform API"] API --> JOB["Job Queue / Worker"] API --> DB["Relational Metadata DB"] JOB --> ING["Ingestion Pipeline"] ING --> TRA["Trafilatura Adapter"] ING --> C4A["Crawl4AI Adapter"] ING --> DOC["Document / ContentUnit Store"] DOC --> ONTO["OntoCast Core Engine"] ONTO --> GUARD["Guardrails Validation Gate"] GUARD --> REVIEW["Human Review Queue"] REVIEW --> RDF["Canonical RDF Store / Fuseki"] RDF --> NEO["Neo4j Projection"] NEO --> RAG["GraphRAG / Text2Cypher / Hybrid Search"] RDF --> EXPORT["TTL / JSON-LD / RDF Export"] DB --> OBS["Audit / Metrics / Cost Dashboard"] JOB --> OBS RESEARCH["Research Loop"] --> ING RESEARCH --> DOC RESEARCH --> REVIEW ``` 저장소 원칙: 1. `Relational DB`: 제품 상태, 작업 상태, 검수/승인/감사 이력. 2. `Artifact Store`: 원문과 중간 산출물. 3. `RDF Store`: 승인된 canonical ontology/facts. 4. `Neo4j`: 검색/탐색/GraphRAG projection. ## 5. 기능별 상세 설계 ### 5.1 Project & Tenant 상용제품 수준 필수 기능: - 프로젝트 생성/복제/보관 - 프로젝트별 namespace/base IRI - 프로젝트별 언어, ontology naming policy - LLM profile, embedding profile - source trust policy - 승인 정책: 자동 승인 금지, 저위험 자동 승인, 고위험 수동 승인 - 사용자/역할: admin, ontologist, reviewer, operator, viewer ### 5.2 Source & Dataset 기능: - URL seed 등록 - sitemap/feed discovery - 파일 업로드 - API/DB source 등록 - source trust score - robots/license/privacy policy - update schedule - change detection - 실패 URL과 denial reason 저장 채택 소스: - Trafilatura: feed/sitemap discovery, metadata extraction - Crawl4AI: JS-heavy/dynamic page, deep crawl - Firecrawl: map/crawl/search 옵션 설계 참고 ### 5.3 Ingestion 표준 파이프라인: ```text Source -> URL/File discovery -> fetch/render -> raw artifact save -> Trafilatura bare_extraction -> metadata normalize -> content hash/fingerprint -> ContentUnit chunking -> quality score -> Document ready ``` 수용 기준: - HTML 없이 텍스트만 있는 문서도 처리 - JS 렌더링 필요 시 Crawl4AI fallback - 동일 URL/동일 본문/near duplicate 구분 - 제목/날짜/저자/canonical URL/source URL 보존 - table/list/heading 구조를 잃지 않음 - evidence offset 또는 selector를 가능한 한 보존 ### 5.4 Ontology Build OntoCast를 중심에 둔다. 기능: - ontology 선택 또는 신규 생성 - RDFGraph/ Ontology/ContentUnit 모델 사용 - GraphUpdate 기반 증분 갱신 - facts renderer/critic loop - ontology renderer/critic loop - unit별 병렬 처리 - entity aggregation - URI 정규화 - owl:sameAs 보존 - budget/caching 플랫폼에서 추가할 기능: - project/dataset/job 식별자 - 다중 문서 corpus 처리 - 비동기 job progress - output artifact 저장 - Korean/domain prompt profile - versioning/diff/rollback - human review 연결 ### 5.5 Validation Gate Guardrails를 `OntologyGuard`로 감싼다. 초기 필수 validator: | Validator | 기능 | 실패 정책 | |---|---|---| | `EntityIdFormatValidator` | ID/URI 형식 검증 | fix/reask | | `UniqueEntityValidator` | 중복 엔티티 후보 검증 | reask/filter | | `RelationEndpointExistsValidator` | 관계 양끝 엔티티 존재 확인 | reask | | `PredicateVocabularyValidator` | 허용 predicate/ontology schema 매핑 | custom/reask | | `EvidenceExistsValidator` | evidence가 원문 ContentUnit에 존재하는지 확인 | filter/reask | | `NoHallucinatedClassValidator` | 근거 없는 class/property 생성 차단 | reask | | `ConfidenceRangeValidator` | 0~1 confidence 보정 | fix | | `SHACLShapeValidator` | SHACL/OWL 제약 검증 | exception/reask | | `NoUnsafeCypherValidator` | Text2Cypher write/delete 차단 | exception | 정책: - parsing/schema 오류는 reask 1회 - 의미가 바뀔 수 있는 자동 fix는 금지 - evidence 없는 triple은 저장 금지 - 최종 실패는 review queue로 이동 ### 5.6 Review & Versioning 상용제품 차별화의 핵심이다. 필수 화면/API: - candidate entity/relation/triple 목록 - 원문 evidence highlight - GraphUpdate diff - accepted/rejected/edited 상태 - schema draft/published 전환 - version diff - rollback - merge/split editor - reviewer comment - audit log 승인 상태: ```text generated -> validated -> pending_review -> approved -> published -> superseded / rejected / archived ``` ### 5.7 Storage & Projection Canonical: - Fuseki/RDF store에 승인된 ontology/facts 저장 - provenance는 named graph 또는 side graph로 분리 Projection: - Neo4j에 RDF/property graph projection - Document/Chunk/Entity/Relation/Evidence 연결 유지 - vector/fulltext index 생성 - GraphRAG와 Text2Cypher는 read-only API로 제공 보안: - Text2Cypher는 read-only 검사 - result limit, timeout, 금지 키워드, 권한 필터 - schema allowlist 적용 ### 5.8 Research Loop Knowledge Agent와 OpenDeepResearcher는 독립 프로젝트로 붙이지 않는다. 현재 `crawler_platform.app.core.research` 아래에 하나의 `ResearchLoop`로 재구성한다. 기능: - knowledge gap detection - 검색어 생성 - 검색 provider 인터페이스 - URL 중복 제거 - page usefulness scoring - context/evidence extraction - gap coverage 계산 - 추가 검색 판단 - source curation - review queue로 후보 전달 차용: - Knowledge Agent: Analyst/Researcher/Curator/Auditor/Fixer/Advisor 역할 분리, LightRAG JSON 추출 프롬프트 - OpenDeepResearcher: 반복형 검색 루프, page usefulness, 추가 검색 판단, 비동기 병렬 처리 필수 수정: - notebook/CLI/input 구조 제거 - `eval` 금지 - JSON schema + Guardrails 검증 - API key/config 하드코딩 제거 - session history DB 저장 - cancellation/retry/progress 추가 ## 6. 단계별 통합 계획 각 단계는 독립적으로 완료/검증한 뒤 다음 단계로 간다. 한 단계에서 실패하면 다음 오픈소스를 붙이지 않는다. ### Phase 0. 기준선 고정 목표: - 현재 `crawler_platform` 기능과 테스트 기준선을 고정한다. 작업: 1. 현재 테스트 전체 실행 2. DB schema snapshot 작성 3. 주요 API smoke test 작성 4. sample HTML 수집 -> entity/claim 저장 흐름 고정 완료 조건: - 기존 기능 regression 없음 - 현재 DB 모델과 API 목록 문서화 ### Phase 1. 공통 데이터 계약 먼저 구축 목표: - 외부 소스 통합 전 내부 표준 모델을 확정한다. 작업: 1. `Document`, `ContentUnit`, `Artifact`, `CandidateTriple`, `ValidationRun`, `ReviewDecision`, `GraphDelta` 모델 추가 2. 기존 `Page/Claim/Evidence/OntologyTriple`과 호환 mapping 작성 3. 모든 ingestion 결과가 `Document -> ContentUnit`으로 들어오게 한다. 완료 조건: - 기존 crawl 결과가 새 Document/ContentUnit으로 저장됨 - 기존 Claim/Evidence 저장이 깨지지 않음 ### Phase 2. Trafilatura 통합 목표: - 정적 웹 문서를 ontology-ready document로 안정 정제한다. 작업: 1. `TrafilaturaAdapter` 추가 2. `bare_extraction(output_format="python", with_metadata=True)` 사용 3. metadata, body XML, fingerprint, links/tables를 Artifact/ContentUnit에 저장 4. 기존 parser/fetcher와 교체하지 않고 source profile로 선택 가능하게 한다. 완료 조건: - 한국어/영어 sample HTML 10개 이상에서 본문/메타/링크 추출 - table/list/heading chunk 보존 - 동일 본문 fingerprint 중복 감지 ### Phase 3. OntoCast Core 통합 목표: - 문서에서 ontology/facts RDF 후보를 생성한다. 작업: 1. OntoCast의 `onto/`, `agent/`, `stategraph/`, `tool/` 핵심만 별도 package로 가져온다. 2. Robyn API/CLI는 가져오지 않는다. 3. `OntologyBuildService.process_document()` facade 작성 4. ContentUnit -> OntoCast ContentUnit adapter 작성 5. output TTL/GraphUpdate/Facts를 Artifact와 GraphDelta로 저장 완료 조건: - 단일 문서로 ontology TTL과 facts TTL 생성 - GraphUpdate delta 저장 - critic retry 결과와 budget 기록 - 아직 published store에는 자동 반영하지 않음 ### Phase 4. Guardrails 검증 게이트 통합 목표: - LLM 산출물의 schema 오류와 근거 없는 triple을 저장 전에 차단한다. 작업: 1. `OntologyGuard` facade 작성 2. Pydantic output schema 정의 3. 최소 validator 5개 구현 4. OntoCast output과 기존 extractor output을 같은 검증 게이트에 통과 5. ValidationRun 저장 완료 조건: - 잘못된 JSON/schema는 저장 차단 - evidence 없는 triple은 후보에서 제거 또는 reask - validation log가 UI/API에서 조회 가능 ### Phase 5. Review/Versioning 최소 구현 목표: - 검증된 후보를 사람이 승인해야 canonical graph에 반영한다. 작업: 1. candidate list API 2. evidence 조회 API 3. approve/reject/edit API 4. GraphDelta publish API 5. ontology schema draft/published version 모델 완료 조건: - candidate -> approved -> published 상태 전이 - rollback 가능한 version 기록 - 누가 무엇을 승인했는지 audit log 저장 ### Phase 6. Neo4j GraphRAG 통합 목표: - 승인된 RDF/claim을 Neo4j projection으로 만들고 검색/RAG를 제공한다. 작업: 1. `neo4j-graphrag` 고정 버전 의존성 추가 2. RDF/Claim/ContentUnit -> Neo4j graph projection 작성 3. Vector/Hybrid/VectorCypher retriever wrapper 작성 4. GraphRAG API 작성 5. Text2Cypher read-only sandbox 작성 완료 조건: - approved graph가 Neo4j에 projection됨 - chunk -> entity -> evidence 검색 가능 - Text2Cypher가 쓰기/삭제 명령을 차단 ### Phase 7. Crawl4AI 통합 목표: - JS-heavy/dynamic site와 deep crawl을 안정 처리한다. 작업: 1. `Crawl4AIAdapter` 추가 2. source profile: `static`, `dynamic_page`, `deep_discovery`, `structured_extract` 3. rendered HTML/markdown/screenshot 선택 저장 4. rendered HTML을 Trafilatura에 다시 넣는 hybrid path 구성 완료 조건: - JS page에서 rendered HTML 추출 - cache/session/dispatcher 설정 가능 - deep crawl은 domain/path/rate limit 정책을 지킴 ### Phase 8. Research Loop 통합 목표: - 사용자가 seed URL을 몰라도 지식 공백을 기반으로 외부 자료를 찾는다. 작업: 1. `ExternalResearchLoop` 추가 2. search provider interface 작성 3. query/usefulness/context/next-decision JSON schema 작성 4. KnowledgeGap과 ResearchSession 연결 5. 유용 context를 Document/ContentUnit/Candidate로 저장 완료 조건: - gap -> search query -> useful page -> evidence context -> candidate 저장 - session history/cost/progress/cancel 지원 ### Phase 9. Firecrawl optional adapter 검토 목표: - 필요할 때만 Firecrawl을 외부 수집 서비스로 붙인다. 조건: - Crawl4AI 운영이 불안정하거나, Firecrawl의 self-host scrape/map/crawl API가 운영상 더 낫다고 판단될 때만 진행 - AGPL/라이선스 검토 완료 - TypeScript service를 Python 코드베이스에 직접 병합하지 않음 완료 조건: - `FirecrawlAdapter`가 외부 HTTP API만 호출 - core ontology pipeline은 Firecrawl에 의존하지 않음 ## 7. 개발 지시 원칙 다른 AI 에이전트에게 작업시킬 때 반드시 지킬 원칙: 1. 한 번에 하나의 오픈소스만 붙인다. 2. 원본 코드를 직접 대량 수정하지 말고 adapter/facade를 만든다. 3. 외부 프로젝트 API가 흔들리면 wrapper만 고친다. 4. canonical data contract는 `Document`, `ContentUnit`, `Candidate`, `Evidence`, `GraphDelta`, `ValidationRun`이다. 5. published graph는 review/approval 없이 변경하지 않는다. 6. Firecrawl과 Crawl4AI를 같은 기본 crawler로 동시에 쓰지 않는다. 7. OntoCast와 Neo4j GraphRAG의 역할을 섞지 않는다. OntoCast는 RDF 생성/갱신, Neo4j는 projection/search/RAG다. 8. Guardrails를 통과하지 않은 LLM output은 DB에 확정 저장하지 않는다. 9. evidence/provenance 없는 entity/relation/triple은 폐기하거나 review queue로 보낸다. 10. 각 단계마다 unit test, integration test, DB migration test, sample data smoke test를 통과한 뒤 다음 단계로 간다. ## 8. 최종 권장 구현 순서 요약 가장 안전한 순서: 1. 현재 `crawler_platform` 기준선 테스트 고정 2. 공통 데이터 계약 확장 3. Trafilatura adapter 4. OntoCast core facade 5. Guardrails validation gate 6. review/versioning workflow 7. Neo4j GraphRAG projection/search 8. Crawl4AI dynamic crawler 9. Knowledge Agent/OpenDeepResearcher 기반 ResearchLoop 10. Firecrawl optional adapter 가장 중요한 결정: - 기본 프로젝트: 현재 `crawler_platform` - 기본 온톨로지 엔진: `OntoCast` - 기본 웹 정제 엔진: `Trafilatura` - 기본 검증 엔진: `Guardrails` - 기본 검색/RAG 엔진: `Neo4j GraphRAG` - 동적 크롤링 엔진: `Crawl4AI` - Firecrawl: 초기 제외, 설계 참고 또는 선택 외부 서비스 이 설계는 “있는 소스를 최대한 그대로 쓰되, 제품으로 필요한 연결/검수/버전/운영 계층만 우리가 만든다”는 방향이다. 실제 통합 코드는 얇은 adapter와 안정된 내부 데이터 계약 위에 쌓아야 한다.