$queryStats(집계)
이 페이지의 내용
정의
경고
$queryStats
애그리게이션 단계는 지원되지 않으며 향후 릴리스에서 안정적임을 보장하지 않습니다. 출력이 향후 릴리스에서 변경될 수 있으므로 이 단계의 특정 출력 형식에 의존하는 기능을 빌드하지 마세요.
기록된 쿼리에 대한 런타임 통계를 반환합니다.
$queryStats
aggregate()
, find()
및 distinct()
쿼리에 대한 지표 를 수집하고 보고합니다. $queryStats
는 Queryable Encryption 을 사용하는 쿼리에 대한 정보를 수집하지 않습니다.
요구 사항:
$queryStats
단계는 클러스터 계층이 M10 이상인 MongoDB Atlas 에서 호스팅되는 배포에서 활성화됩니다.
$queryStats
단계를 실행하려면 파이프라인이 다음 요구 사항을 충족해야 합니다.
파이프라인은
admin
데이터베이스에서 실행되어야 합니다.$queryStats
은(는) 파이프라인의 첫 번째 단계여야 합니다.
구문
db.adminCommand( { aggregate: 1, pipeline: [ { $queryStats: { transformIdentifiers: { algorithm: <string>, hmacKey: <binData> /* subtype 8 - used for sensitive data */ } } } ], cursor: { } } )
중요
특정 컬렉션 에서 $queryStats
를 실행 수 없습니다. 전체 예제는 예제를 참조하세요 .
명령 필드
$queryStats
은(는) 다음 필드를 사용합니다.
필드 | 필요성 | 유형 | 설명 |
---|---|---|---|
| 옵션 | 문서 |
|
transformIdentifiers .algorithm |
| 문자열 | 출력의 네임스페이스 정보 및 필드 이름에 적용되는 해시 변환 유형입니다. 현재 지원되는 유일한 |
transformIdentifiers .hmacKey |
| BinData | EMAC 변환의 비공개 키 입력입니다. |
액세스 제어
배포서버에서 액세스 제어를 적용하는 경우 $queryStats
를 실행하는 사용자는 다음 권한이 있어야 합니다.
transformIdentifiers
옵션 없이$queryStats
를 실행하려면 사용자에게queryStatsRead
권한 조치가 있어야 합니다.transformIdentifiers
옵션으로$queryStats
를 실행하려면 사용자에게queryStatsRead
및queryStatsReadTransformed
권한 조치가 모두 있어야 합니다.
기본 제공 clusterMonitor
역할은 queryStatsRead
및 queryStatsReadTransformed
권한을 제공합니다. 다음 예에서는 admin
데이터베이스에 clusterMonitor
역할을 부여합니다.
db.grantRolesToUser( "<user>", [ { role: "clusterMonitor", db: "admin" } ] )
행동
다음 섹션에서는 $queryStats
단계의 동작 세부정보를 설명합니다.
$queryStats가 쿼리 통계를 추적하는 방법
$queryStats
단계에 대한 통계는 인메모리에 저장된 가상 collection에서 추적됩니다. 가상 collection의 메모리 제한은 시스템 전체 메모리의 1%입니다.
$queryStats 그룹이 문서를 반환하는 방법
$queryStats
공통 속성을 가진 쿼리를 동일한 출력 문서로 그룹화합니다. 결과 문서를 쿼리 통계 항목 이라고 합니다.
$queryStats
사용자 제공 필드 값을 해당 데이터 유형으로 정규화하여 유사한 쿼리를 함께 그룹화합니다. 예를 들어 { item: 'card' }
으로 지정된 필터는 { item :
'?string'}
로 정규화됩니다. $queryStats
는 hint
및 comment
와 같은 일부 쿼리 옵션의 값도 정규화합니다.
$queryStats
readConcern
및 readPreference
과 같은 옵션의 리터럴 값을 유지합니다.
쿼리 통계 항목에 포함된 전체 옵션 목록은 명령 쿼리 형태 찾기를 참조하세요.
$queryStats가 transformationIdentifier를 사용하여 데이터를 변환하는 방법
LMAC 키가 transformIdentifiers
옵션에 지정된 경우 $queryStats
은(는) LMAC 키를 사용하여 다음 데이터에 EMAC-SHA-256 해시 함수를 적용합니다.
문서 필드 이름
컬렉션 이름
데이터베이스 이름
$queryStats
다음 데이터에 EMAC 변환을 적용 하지 않습니다 .
연산자 이름과 같은 MQL 키워드(예:
$gte
).$setWindowFields
의partitionBy
매개변수와 같은 매개변수 이름입니다.필드 값.
$queryStats
쿼리가 기록될 때 쿼리의 필드 값을 데이터 유형(예: 숫자 또는 문자열)으로 정규화합니다.$queryStats
은(는) 사용자 데이터가 포함된 필드 값을 저장하지 않습니다.
변환된 출력의 예시 는 변환된 예제를 참조하세요 .
$queryStats Log Entries
MongoDB 는 배포서버 로그 에 $queryStats
작업을 기록합니다. 기본값 으로 MongoDB 는 $queryStats
작업의 호출만 기록하고 작업 출력은 기록하지 않습니다. transformIdentifiers
옵션이 포함된 $queryStats
작업의 경우 변환된 출력을 로그 항목에 포함할지 여부를 지정할 수 있습니다.
$queryStats
로깅 동작을 제어하는 방법을 알아보려면 $queryStats 로그 출력 토글을 참조하세요.
변경 스트림
다음 이벤트 중 하나가 발생하면 변경 스트림 에 대한 쿼리 통계가 업데이트됩니다.
커서 가 생성됩니다.
getMore
작업이 완료됩니다.커서 가 닫힙니다.
변경 스트림에 대해 보고된 쿼리 통계에는 다음과 같은 동작이 있습니다.
totalExecMicros
과 같은 실행 지표 에는 가장 최근 작업(커서 생성,getMore
또는 커서 닫기)에 대한 정보가 포함되어 있습니다.내부
getMore
작업은execCount
지표를 증가시킵니다.firstResponseExecMicros
각getMore
작업에 대해 통계가 수집되고 업데이트되므로totalExecMicros
과 는 항상 동일합니다.커서 가 닫히면
lastExecutionMicros
은 0 입니다.
출력
$queryStats
쿼리 통계 항목의 배열을 반환합니다. 일부 쿼리 통계 항목 속성에는 리터럴 값이 포함되어 있으며, 일부 속성은 일반적인 쿼리를 그룹화하도록 정규화됩니다.
쿼리 통계 항목에는 다음과 같은 최상위 문서가 포함되어 있습니다.
문서 | 설명 |
---|---|
| 쿼리 통계 출력의 항목을 정의하는 속성의 고유한 조합입니다.
각각의 고유한 속성 조합은 |
|
|
| 각 쿼리 통계 항목과 연결된 애그리게이션된 런타임 지표를 포함합니다. 각 쿼리 통계 항목은 동일한 키를 공유하는 각 쿼리에 대한 통계를 기록합니다. |
출력 배열의 각 문서에는 다음 필드가 포함되어 있습니다.
필드 | 유형 | 리터럴 또는 정규화 | 설명 |
---|---|---|---|
| 문서 | 리터럴 | 쿼리 형태와 쿼리 세트를 함께 그룹화하는 추가 쿼리 속성을 포함합니다. |
| 문서 | 리터럴 | 유사한 쿼리를 함께 그룹 하는 데 사용되는 속성을 포함합니다. 자세한 내용은 쿼리 형태를 참조하세요. |
| 문서 | 리터럴 | 키와 연결된 클라이언트 정보를 설명합니다. |
| 문서 | 리터럴 | 클라이언트 애플리케이션 이름 |
| 문서 | 리터럴 | 쿼리를 실행하는 데 사용되는 드라이버를 설명합니다. |
| 문자열 | 리터럴 | 쿼리를 실행하는 데 사용된 드라이버의 이름입니다. 가능한 값에는 |
| 문자열 | 리터럴 | 쿼리를 실행하는 데 사용된 드라이버의 버전 번호 |
| 문서 | 리터럴 | 쿼리를 실행한 클라이언트에서 사용하는 운영 체제를 설명합니다. |
| 문자열 | 리터럴 | 운영 체제 유형 |
| 문자열 | 리터럴 | 운영 체제의 이름 |
| 문자열 | 리터럴 | 운영 체제의 아키텍처입니다. 가능한 값에는 |
| 문자열 | 리터럴 | 운영 체제의 버전 번호 |
| 문서 | 리터럴 | 키에 대한 읽기 고려 (read concern) |
| 문자열 | 리터럴 | 쿼리 가 실행된 컬렉션 의 유형입니다. 자세한 내용은 컬렉션 유형을 참조하세요. |
| 문서 또는 문자열 | 정규화 | 쿼리에 대한 힌트 로 사용된 인덱스 |
| 문자열 | 정규화 | 키의 배치 크기 입니다. 배치 크기는 MongoDB 인스턴스의 각 응답 배치에서 반환할 문서 수를 지정합니다. |
| 문자열 | 정규화 | 키와 관련된 주석 |
| 문자열 | 정규화 | 키와 연결된 maxTimeMS 값 |
| 부울 | 정규화 | 키와 연결된 noCursorTimeout 옵션 |
| 문자열 | 리터럴 | 키와 연결된 allowPartialResults 옵션 |
| 문자열 | 리터럴 | 키와 연결된 읽기 설정 |
| 문자열 | 리터럴 | 키와 연결된 Stable API 버전입니다. Stable API를 참조하세요. |
| 부울 | 리터럴 | 키와 연결된 |
| 부울 | 리터럴 | 키와 연결된 |
| 문자열 | 리터럴 |
|
| 문서 | 리터럴 | 키에 대한 런타임 통계를 설명합니다. |
| NumberLong | 리터럴 | 지정된 키를 가진 모든 쿼리에 대한 가장 최근 쿼리의 실행 런타임입니다. |
| NumberLong | 리터럴 | 지정된 키로 쿼리가 실행된 횟수입니다. |
| 문서 | 리터럴 | 쿼리에서 검사하는 키 수를 설명합니다. |
metrics .keysExamined .sum | Integer | 리터럴 | 검사한 키의 총 개수 |
metrics .keysExamined .max | NumberLong | 리터럴 | 검사되는 최대 키 수 |
metrics .keysExamined .min | NumberLong | 리터럴 | 검사하는 키 수 최소화 |
metrics .keysExamined .sumOfSquares | NumberDecimal | 리터럴 | 검사한 키 수의 제곱합입니다.
|
| 문서 | 리터럴 | 쿼리로 검사한 문서 수를 설명합니다. |
metrics .docsExamined .sum | Integer | 리터럴 | 쿼리 에서 검사한 문서의 총 개수 |
metrics .docsExamined .max | NumberLong | 리터럴 | 검사할 최대 문서 수 |
metrics .docsExamined .min | NumberLong | 리터럴 | 최소 검사 문서 수 |
metrics .docsExamined .sumOfSquares | NumberDecimal | 리터럴 | 검사한 문서 수의 제곱의 합입니다.
|
| 부울 | 리터럴 |
|
| 부울 | 리터럴 |
|
| 부울 | 리터럴 |
|
| 부울 | 리터럴 |
|
| 문서 | 리터럴 | 지정된 키로 쿼리를 실행하는 데 소요된 총 시간을 설명합니다. 쿼리 결과가
|
metrics .totalExecMicros .sum | NumberLong | 리터럴 | 지정된 키로 쿼리를 실행하는 데 소요된 총 시간 |
metrics .totalExecMicros .max | NumberLong | 리터럴 | 주어진 키로 쿼리를 실행하는 데 소요된 최장 시간 |
metrics .totalExecMicros .min | NumberLong | 리터럴 | 주어진 키로 쿼리를 실행하는 데 소요된 최단 시간 |
metrics .totalExecMicros .sumOfSquares | NumberDecimal | 리터럴 | 지정된 키를 사용한 모든 쿼리에 대한 총 실행 시간의 제곱합입니다. |
metrics .firstResponseExecMicros | 문서 | 리터럴 | 키 내의 쿼리 가 처리 되기 시작한 시점부터 서버 가 첫 번째 결과 배치 를 반환할 때까지 소요된 시간을 설명합니다.
|
metrics .firstResponseExecMicros .sum | NumberLong | 리터럴 | 쿼리 처리 시작부터 서버가 첫 번째 결과 배치를 반환할 때까지 소요된 총 시간입니다. |
metrics .firstResponseExecMicros .max | NumberLong | 리터럴 | 쿼리 처리 시작부터 서버가 첫 번째 결과 배치를 반환할 때까지 소요된 최장 시간입니다. |
metrics .firstResponseExecMicros .min | NumberLong | 리터럴 | 쿼리 처리 시작부터 서버가 첫 번째 결과 배치를 반환할 때까지 소요된 최단 시간입니다. |
metrics .firstResponseExecMicros .sumOfSquares | NumberDecimal | 리터럴 | 쿼리 처리 시작부터 서버가 첫 번째 결과 배치를 반환할 때까지 소요된 시간의 제곱의 합계입니다.
|
| 문서 | 리터럴 | 키 내에서 쿼리가 반환한 문서 수를 설명합니다. |
metrics .docsReturned .sum | NumberLong | 리터럴 | 지정된 키를 사용하여 쿼리에서 반환된 총 문서 수입니다. |
metrics .docsReturned .max | NumberLong | 리터럴 | 지정된 키를 사용하여 쿼리에서 반환되는 최대 문서 수 |
metrics .docsReturned .min | NumberLong | 리터럴 | 지정된 키를 사용하여 쿼리에서 반환된 최소 문서 수입니다. |
metrics .docsReturned .sumOfSquares | NumberDecimal | 리터럴 | 키 내 쿼리가 반환하는 문서 수의 제곱의 합계입니다.
|
| 날짜 | 리터럴 | 마지막 재시작 이후 지정된 키의 쿼리가 처음 사용된 시간입니다. |
| 날짜 | 리터럴 | 지정된 키가 포함된 쿼리가 가장 최근에 사용된 시간입니다. |
collectionType
key.collectionType
필드는 기록된 쿼리가 실행된 컬렉션 유형을 나타냅니다. collectionType
는 다음 값 중 하나일 수 있습니다.
필드 | 설명 |
---|---|
| |
| 쿼리가 표준 컬렉션에서 실행되었습니다. |
| 존재하지 않는 컬렉션에서 쿼리가 실행되었습니다. |
| |
| 쿼리가 뷰에 대해 실행되었습니다. |
| 쿼리가 가상 collection에서 실행되었습니다. 가상 collection에서 수행되는 작업은 다음과 같습니다. |
쿼리 형태
key.queryShape
문서 에 쿼리 형태 필드가 포함되어 있습니다. 쿼리 형태에 학습 보려면 쿼리 형태를 참조하세요 .
key.queryShape
의 필드는 쿼리 통계 항목을 생성한 명령에 따라 달라집니다. $queryStats
은 aggregate
, find
및 distinct
명령에 대한 쿼리 통계 항목을 생성합니다.
각 쿼리 형태 속성은 쿼리 옵션에 해당합니다. 예를 들어 key.queryShape.sort
은 쿼리 형태의 sort()
사양에 해당합니다.
명령 쿼리 형태 찾기
다음 표에서는 find
명령에 대한 쿼리 형태 속성을 설명합니다.
필드 | 유형 | 리터럴 또는 정규화 |
---|---|---|
| 문서 | 정규화 |
| 문서 | 리터럴 |
| 문서 | 정규화 |
| Integer | 정규화 |
| Integer | 정규화 |
| 부울 | 리터럴 |
| 문서 | 정규화 |
| 문서 | 정규화 |
| 부울 | 리터럴 |
| 부울 | 리터럴 |
| 부울 | 리터럴 |
| 부울 | 리터럴 |
| 부울 | 리터럴 |
| 문서 | 리터럴 |
| 부울 | 리터럴 |
| 문서 | 정규화 |
집계 명령 쿼리 형태
다음 표에서는 aggregate
명령에 대한 쿼리 형태 속성을 설명합니다.
필드 | 유형 | 리터럴 또는 정규화 |
---|---|---|
| 배열 | 정규화 |
| 부울 | 리터럴 |
| 부울 | 리터럴 |
| 문서 | 리터럴 |
| 문자열 또는 문서 | 정규화 |
| 문서 | 정규화 |
고유한 명령 쿼리 형태
다음 표에서는 distinct
명령에 대한 쿼리 형태 속성을 설명합니다.
필드 | 유형 | 리터럴 또는 정규화 |
---|---|---|
| 문자열 | 리터럴 |
| 문서 | 정규화 |
| 문서 | 정규화 |
예시
이 섹션의 예시를 실행하려면 다음 데이터로 시작하세요.
db.products.insertMany( [ { item: "card", qty: 15 }, { item: "envelope", qty: 20 }, { item: "stamps" , qty: 30 } ] )
그런 다음 다음 명령을 실행합니다.
db.products.find( { item: "card" } ) db.products.aggregate( [ { $match: { qty: { $gt: 20 } } } ] )
다음 예제에서는 다양한 유형의 데이터 변환을 사용하여 $queryStats
의 출력을 보여 줍니다.
변환되지 않은 예제
입력:
db.getSiblingDB("admin").aggregate( [ { $queryStats: { } } ] )
출력:
[ { key: { queryShape: { cmdNs: { db: 'test', coll: 'products' }, command: 'find', filter: { item: { '$eq': '?string' } } }, client: { driver: { name: 'nodejs|mongosh', version: '5.1.0' }, os: { type: 'Darwin', name: 'darwin', architecture: 'arm64', version: '22.6.0' }, platform: 'Node.js v16.19.1, LE (unified)', version: '5.1.0|1.8.0', application: { name: 'mongosh 1.8.0' } }, collectionType: 'collection' }, keyHash: 'dsoJ+LHAru0z6MJ1/IygJnnLTrlpVYYmPnlmNZbZrLI=', metrics: { lastExecutionMicros: Long("4254"), execCount: Long("1"), totalExecMicros: { sum: Long("4254"), max: Long("4254"), min: Long("4254"), sumOfSquares: Decimal128("18096516") }, firstResponseExecMicros: { sum: Long("4254"), max: Long("4254"), min: Long("4254"), sumOfSquares: Decimal128("18096516") }, docsReturned: { sum: Long("1"), max: Long("1"), min: Long("1"), sumOfSquares: Decimal128("1") }, firstSeenTimestamp: ISODate("2023-09-14T12:30:27.989Z"), latestSeenTimestamp: ISODate("2023-09-14T12:30:27.989Z") }, asOf: Timestamp({ t: 1694695007, i: 0 }) }, { key: { queryShape: { cmdNs: { db: 'test', coll: 'products' }, command: 'aggregate', pipeline: [ { '$match': { qty: { '$gt': '?number' } } } ] }, apiVersion: '1', client: { driver: { name: 'nodejs|mongosh', version: '5.1.0' }, os: { type: 'Darwin', name: 'darwin', architecture: 'arm64', version: '22.6.0' }, platform: 'Node.js v16.19.1, LE (unified)', version: '5.1.0|1.8.0', application: { name: 'mongosh 1.8.0' } }, collectionType: 'collection', cursor: { batchSize: '?number' } }, keyHash: '2QLBfL0m1lliStdN4XvBjqVBtZQ6ffaB2L1pJ99twT8=', metrics: { lastExecutionMicros: Long("350"), execCount: Long("3"), totalExecMicros: { sum: Long("3084"), max: Long("2499"), min: Long("235"), sumOfSquares: Decimal128("6422726") }, firstResponseExecMicros: { sum: Long("3084"), max: Long("2499"), min: Long("235"), sumOfSquares: Decimal128("6422726") }, docsReturned: { sum: Long("3"), max: Long("1"), min: Long("1"), sumOfSquares: Decimal128("3") }, firstSeenTimestamp: ISODate("2023-11-29T21:16:17.796Z"), latestSeenTimestamp: ISODate("2023-11-29T21:17:12.385Z") }, asOf: Timestamp({ t: 1701292827, i: 0 }) } ]
변환된 예제
입력:
db.getSiblingDB("admin").aggregate( [ { $queryStats: { transformIdentifiers: { algorithm: "hmac-sha-256" , hmacKey: BinData(8, "87c4082f169d3fef0eef34dc8e23458cbb457c3sf3n2") } } } ] )
출력:
[ { key: { queryShape: { cmdNs: { db: 'Mtrt3iG7dsX5c5uCSIhSVlcu5qD3u3xx2EQnS1dJLxM=', coll: '3oJE6AyOuf8h5NqWiXETxulFlPm3QUXbMnMjL2EqAU4=' }, command: 'find', filter: { 'VWVRow7Ure92ajRPfrpWiU8OtDeWcLePFIq0+tooBng=': { '$eq': '?string' } } }, client: { driver: { name: 'nodejs|mongosh', version: '5.1.0' }, os: { type: 'Darwin', name: 'darwin', architecture: 'arm64', version: '22.6.0' }, platform: 'Node.js v16.19.1, LE (unified)', version: '5.1.0|1.8.0', application: { name: 'mongosh 1.8.0' } }, collectionType: 'collection' }, keyHash: 'q4vxam+wbk8tTrl8D0MDFH1LQAbI8fWspfkGKhEUROk=', metrics: { lastExecutionMicros: Long("4254"), execCount: Long("1"), keysExamined: { sum: Int("5"), max: Long("5"), min: Long("5"), sumOfSquares: Decimal128("25") }, docsExamined: { sum: Long("1"), max: Long("1"), min: Long("1"), sumOfSquares: Decimal128("1") }, hasSortStage: false, usedDisk: false, fromMultiPlanner: false, fromPlanCache: true, totalExecMicros: { sum: Long("4254"), max: Long("4254"), min: Long("4254"), sumOfSquares: Decimal128("18096516") }, firstResponseExecMicros: { sum: Long("4254"), max: Long("4254"), min: Long("4254"), sumOfSquares: Decimal128("18096516") }, docsReturned: { sum: Long("1"), max: Long("1"), min: Long("1"), sumOfSquares: Decimal128("1") }, firstSeenTimestamp: ISODate("2023-09-14T12:30:27.989Z"), latestSeenTimestamp: ISODate("2023-09-14T12:30:27.989Z") }, asOf: Timestamp({ t: 1694695712, i: 0 }) }, { key: { queryShape: { cmdNs: { db: 'Mtrt3iG7dsX5c5uCSIhSVlcu5qD3u3xx2EQnS1dJLxM=', coll: '3oJE6AyOuf8h5NqWiXETxulFlPm3QUXbMnMjL2EqAU4=' }, command: 'aggregate', pipeline: [ { '$match': { 'RVqrwNEPotzdKnma/T7s4YcgNvpqO29BMDoni2N4IMI=': { '$gt': '?number' } } } ] }, apiVersion: '1', client: { driver: { name: 'nodejs|mongosh', version: '5.1.0' }, os: { type: 'Darwin', name: 'darwin', architecture: 'arm64', version: '22.6.0' }, platform: 'Node.js v16.19.1, LE (unified)', version: '5.1.0|1.8.0', application: { name: 'mongosh 1.8.0' } }, collectionType: 'collection', cursor: { batchSize: '?number' } }, keyHash: 'HEhpQTYB+/wVoHLkOkMd+EC2jguQlMJ1N/vTE7+b8Js=', metrics: { lastExecutionMicros: Long("350"), execCount: Long("3"), keysExamined: { sum: Int("5"), max: Long("5"), min: Long("5"), sumOfSquares: Decimal128("25") }, docsExamined: { sum: Long("1"), max: Long("1"), min: Long("1"), sumOfSquares: Decimal128("1") }, hasSortStage: false, usedDisk: false, fromMultiPlanner: false, fromPlanCache: true, totalExecMicros: { sum: Long("3084"), max: Long("2499"), min: Long("235"), sumOfSquares: Decimal128("6422726") }, firstResponseExecMicros: { sum: Long("3084"), max: Long("2499"), min: Long("235"), sumOfSquares: Decimal128("6422726") }, docsReturned: { sum: Long("3"), max: Long("1"), min: Long("1"), sumOfSquares: Decimal128("3") }, firstSeenTimestamp: ISODate("2023-11-29T21:16:17.796Z"), latestSeenTimestamp: ISODate("2023-11-29T21:17:12.385Z") }, asOf: Timestamp({ t: 1701293302, i: 0 }) }, ]
MongoDB Atlas 데이터 컬렉션
MongoDB Atlas는 주기적으로 $queryStats
을(를) 사용하여 쿼리에 대한 익명화된 데이터를 수집하여 MongoDB 제품을 개선합니다. 또한, 사용자의 데이터는 사용량에 따른 기능 제안을 위해 사용될 수 있습니다. MongoDB는 $queryStats
으)로 수집한 데이터를 4년 동안 보관합니다.
배포서버에서 $queryStats
을(를) 실행하면 Atlas 조직별로 고유한 EMAC 키를 사용하여 데이터를 변환하고 민감한 정보가 수집되지 않도록 합니다.