모든 장비 대조 결과 설명하기

RustBeginner
지금 연습하기

소개

신뢰할 수 있는 개체 목록은 장비 확인의 시작일 뿐입니다. 팀에는 유형마다 정상 장비가 몇 개인지, 무엇이 손상되었는지, 무엇이 부족하고 무엇이 계획보다 많은지 설명이 필요합니다. 계획에 없는 물건이나 필요 수량 0은 입력을 무효로 만들지 않습니다.

이 실습에서는 대조 로직을 구현하고 서로 다른 실제 입력 파일 두 묶음으로 보고서를 읽습니다. 독립 환경이므로 이전 실습의 상태 파서와 개체 수집 함수가 이미 제공되며 이전 VM의 파일은 필요하지 않습니다. 이전 실습의 개체 식별과 충돌 규칙, Rust 구조체, 열거형, HashMap, Result, 파일 읽기와 반복문을 알고 있어야 합니다. 저자의 예상 시간은 읽기와 일반적인 수정을 포함해 45분이며, 초보자 대상으로 측정한 완료 시간은 아닙니다.

모든 유형과 개체 대조하기

이 단계에서는 세 번의 편집으로 대조를 만듭니다. 관련 유형을 찾고, 각 유형의 전체 개체 집합을 모은 뒤, 부족과 초과를 계산합니다. Terminal을 여세요. 현재 셸의 검색 경로에 Cargo를 추가하고 독립적으로 준비된 프로젝트로 들어가세요. 새 터미널에서는 export를, 위치를 잃으면 cd를 다시 실행하세요.

export PATH=/home/labex/.cargo/bin:$PATH
cd /home/labex/project/field-kit

먼저 이미 작동하는 정상 결과를 살펴보세요. 이 실습에는 상태 파서와 개체 수집 함수가 제공됩니다.

cat data/plan.tsv
cargo run --quiet -- assets data/scans.tsv

표에는 일곱 스캔이 아니라 여섯 실물 ID가 있습니다. a, b, c는 정상 light-a 개체입니다. d는 손상된 light-b이며 e와 f는 계획에 없는 유형 other의 정상·손상 개체입니다. 표시 이름 Flashlight는 서로 다른 두 계획 유형 ID에 속합니다. 계획에는 스캔이 없는 로프 두 개와 스캔이 없는 예비품 0개도 필요합니다. 완전한 보고서가 무엇을 설명해야 할지 정할 때 이 차이를 유지하세요.

src/readiness.rs만 편집하며 공개 함수 이름과 매개변수·반환 타입을 유지하세요. 다른 소스 파일, Cargo.toml, rust-toolchain.toml, 모든 원본 data/ 파일은 변경하지 마세요. 프로젝트 디렉터리에 별도의 실험 입력을 만들 것입니다. 명령 인터페이스는 여러분이 지정한 실제 경로를 읽으며 샘플로 대체하지 않습니다.

제한된 TSV 계약은 같습니다. 계획의 정확한 세 필드 헤더는 type_id, name, required이며 스캔은 asset_id, type_id, condition입니다. 필드 사이는 실제 탭입니다. ID는 ASCII 소문자로 시작하고 소문자, 숫자, -로 이어집니다. 계획 이름은 공백을 포함하는 비어 있지 않은 출력 가능한 ASCII입니다. 수량은 0–999 값을 나타내는 십진 숫자이며 앞자리 0을 허용합니다. 표시 이름은 같아도 되지만 계획 유형 ID는 반복할 수 없습니다. 상태는 정확히 good 또는 damaged입니다. LF/CRLF, 마지막 줄 바꿈 생략, 뒤에 오는 완전히 빈 줄은 허용합니다. 필드 앞뒤 공백을 제거하지 않으며 공백 문자만 있는 줄은 빈 줄이 아니고 단독 캐리지 리턴은 줄 끝이 아닙니다. 실물 검사가 아니라 합성 텍스트 기록입니다.

제공된 타입과 미완성 함수를 읽으세요.

cat src/lib.rs
cat src/readiness.rs

Need에는 계획의 유형 ID, 이름, 필요 수량이 있습니다. Scan에는 개체 ID, 유형 ID, 상태, 진단용 줄이 있습니다. reconcile은 변경하면 안 되는 빌린 슬라이스 &[Need]와 &[Scan]을 받습니다. 개체는 이미 형식 검증과 식별 정보 수집을 마쳤으므로 각 실물 ID가 한 번씩 나타납니다. 반환하는 Vec<TypeSummary>에는 계획 및 스캔 유형의 합집합, 정상·손상 ID 전체 집합, 필요 수량, 부족 수량, 초과 수량이 있어야 합니다. 준비된 출력 형식은 표시 이름을 제공하고 줄과 ID 목록을 정렬하며 각 개체의 planned 또는 unplanned 관계를 출력합니다.

첫 편집: 관련 유형 유지하기

스캔된 개체가 없는 계획을 생각해 보세요. 필요 수량이 양수이면 여전히 부족하며 0이면 부족도 0입니다. 보호된 원본을 비우지 말고 별도의 빈 스캔 입력을 만드세요. printf는 형식 문자열을 출력하며 \t를 탭, \n을 줄 바꿈으로 해석합니다. 작은따옴표는 이 역슬래시를 printf에 전달하고 >는 지정한 실험 파일에 씁니다. 헤더가 유일한 줄입니다.

printf 'asset_id\ttype_id\tcondition\n' > empty-scans.tsv
cat empty-scans.tsv

코드를 바꾸기 전에 이 입력에 남아야 할 유형을 예측하세요. light-a, light-b, rope, zero이며 필요 수량은 2, 1, 2, 0입니다. 이 입력에는 스캔에만 있는 유형이 없습니다. 그래도 일반 함수는 두 슬라이스 모두에서 유형을 찾아야 나중에 실제 스캔의 other를 추가할 수 있습니다.

소스만의 체크포인트를 만드세요. mktemp -d는 X 여섯 개를 고유 접미사로 바꿔 새 디렉터리를 만들며 이전 백업을 재사용하지 않습니다. $()는 출력된 경로를 세션 변수에 저장합니다. &&는 디렉터리 생성이 성공한 경우에만 cp -p를 실행합니다. 복사는 소스 메타데이터를 보존하고 변수의 따옴표는 경로를 하나로 유지합니다. 어느 작업이든 실패하면 오류를 읽고 백업을 만든 뒤 편집하세요.

rs03_before_types=$(mktemp -d .edit-before-types.XXXXXX) && cp -p src/readiness.rs "$rs03_before_types/readiness.rs"

nano로 기존 파일을 여세요. -c는 커서 위치를 표시합니다.

nano -c src/readiness.rs

nano의 ^는 Ctrl을 뜻합니다. Ctrl+W를 눌러 pub fn reconcile을 검색하고 Enter를 누르세요. 함수의 여는 중괄호와 닫는 중괄호를 읽으세요. 화살표와 Backspace/Delete로 본문과 사용하지 않는 매개변수 바인딩만 편집하세요. 타입을 유지하며 _needs, _assets를 needs, assets로 바꾸세요. 제공된 use crate::{Need, Scan, TypeSummary}; 가져오기를 유지하세요. 이 단계는 아직 Condition을 사용하지 않습니다.

데이터 관계로 첫 단계를 구성하세요. 빈 types 벡터를 만드세요. 계획의 각 유형 ID를 추가하세요. 이어서 개체를 방문하고 types.contains(&asset.type_id)가 거짓일 때만 유형 ID를 추가하세요. 계획의 유형 ID는 이미 고유합니다. .clone()은 빌린 기록에서 데이터를 소유하는 유형 ID 문자열을 복사하므로 결과가 이를 소유할 수 있습니다.

다음으로 빈 summaries 벡터를 만들고 수집한 각 유형 ID를 방문하세요. 필요 수량을 0으로 시작하고 계획에서 그 유형을 찾으세요. usize::from(need.required)는 계획의 u16 값을 벡터 길이에서 사용하는 개수 타입으로 변환합니다. 지금은 빈 good_ids와 damaged_ids 벡터를 사용하세요. 아직 사용 가능한 개체를 세지 않았으므로 missing에는 required를 대입하고 surplus는 0으로 설정하세요. 이 변수로 TypeSummary를 추가하고 함수의 마지막 표현식을 summaries로 하세요. type_id, required 같은 짧은 필드 표기는 이름이 같은 변수를 사용합니다. 아직 개체 분류는 구현하지 않았습니다.

Ctrl+O, 현재 파일 이름을 확인하는 Enter, 나가는 Ctrl+X로 저장하세요. 실행 전에 저장된 함수를 읽으세요.

cat src/readiness.rs

cargo run은 프로그램을 컴파일하고 실행합니다. --quiet는 Cargo 진행 메시지를 숨기고 --는 뒤의 인수를 프로그램에 전달합니다. 제한된 스캔 없음 경우를 시험하세요.

cargo run --quiet -- check data/plan.tsv empty-scans.tsv

계획의 네 유형, 정상 개체 0개, 빈 ID 집합, 부족 수량 2, 1, 2, 0, 초과 수량 0이 나와야 합니다. ASSET 헤더 뒤에는 개체 데이터 줄이 없어야 합니다. 첫 단계는 이 빈 스캔 관찰에 대해서만 올바릅니다. 개체 목록이 비어 있지 않을 때의 완전한 구현은 아닙니다. 종료 상태 0을 모든 대조 규칙이 완성되었다는 증거로 해석하지 마세요.

두 번째 편집: 전체 상태별 집합 유지하기

이제 실제 스캔의 전체 집합을 예측하세요. light-a의 정상 ID는 a,b,c, light-b의 손상 ID는 d, other의 정상 ID는 e, 손상 ID는 f입니다. 개체가 없는 rope와 zero도 각각의 요약이 필요합니다. 선호하는 “추가” 개체를 하나 고르지 말고 모든 집합을 유지하세요.

작동하는 유형 합집합 단계를 보존하세요.

rs03_before_sets=$(mktemp -d .edit-before-sets.XXXXXX) && cp -p src/readiness.rs "$rs03_before_sets/readiness.rs"
nano -c src/readiness.rs

이제 사용할 열거형을 포함하도록 맨 위 crate 가져오기를 바꾸세요.

use crate::{Condition, Need, Scan, TypeSummary};

이는 명시적으로 해야 하는 소스 편집입니다. 원래 시작 코드는 Condition을 가져오지 않습니다. crate는 이 라이브러리이며 use는 그 이름들을 이 모듈로 가져옵니다. 처음 사용할 때 가져오기를 추가하면 앞 단계에서 사용하지 않는 가져오기 경고도 피할 수 있습니다.

pub fn reconcile을 검색하세요. 유형 합집합과 필요 수량 로직을 유지하세요. ID를 추가할 것이므로 good_ids, damaged_ids를 변경 가능하게 만드세요. 각 유형의 반복문 안에서 모든 개체를 방문하고 현재 type_id인 것을 선택하여 상태를 match로 판단하세요. Good 변형이면 개체 ID의 복제본을 정상 벡터에, Damaged이면 손상 벡터에 추가하세요. 두 변형 모두 유효한 입력입니다. 손상 또는 계획 외 개체를 삭제하지 마세요. 임시 부족·초과 대입문 두 개는 다음 편집까지 그대로 두세요.

저장하고 나가서 실제 스캔으로 실행하세요.

cargo run --quiet -- check data/plan.tsv data/scans.tsv

유형 ID, 필요 수량, 정상 개수, 전체 ID 집합과 ASSET 줄을 읽으세요. other를 포함하여 예측과 같아야 합니다. 부족·초과 열은 아직 임시 값이며 일반적인 올바른 결과가 아닙니다. 예를 들어 light-a의 정상 ID 세 개가 있어도 임시 부족 값은 여전히 2일 수 있습니다. 왜 완성된 보고서일 수 없는지 설명하세요. 다음으로 두 계산을 고칠 것입니다. 이 중간 표가 최종 검사를 통과한다고 주장하지 마세요.

세 번째 편집: 정상 개체만으로 부족·초과 계산하기

고유한 정상 개체만 수요를 채웁니다. light-a는 필요 2, 정상 3이므로 부족 0, 초과 1입니다. light-b의 손상 개체는 필요를 채우지 못하므로 부족 1, 초과 0입니다. rope는 2개 부족합니다. zero는 계속 0/0입니다. 계획 외 other는 필요 수량 0에 정상 개체 하나이므로 초과 1입니다. 손상 ID는 남지만 초과 수량에 더해지지 않습니다.

이미 올바른 집합을 수집하는 단계를 저장하세요.

rs03_before_totals=$(mktemp -d .edit-before-totals.XXXXXX) && cp -p src/readiness.rs "$rs03_before_totals/readiness.rs"
nano -c src/readiness.rs

pub fn reconcile을 검색한 뒤 임시 부족·초과 표현식만 교체하세요. 벡터의 len()은 개수를 반환합니다. saturating_sub는 0보다 작아지지 않도록 빼므로 부호 없는 정수의 언더플로를 피합니다. 규칙은 missing = max(required - good, 0) 및 surplus = max(good - required, 0)입니다. 원시 스캔 수나 손상 ID 수가 아니라 good_ids.len()에 적용하세요. 유형 합집합과 전체 벡터를 그대로 유지하세요. Ctrl+O, Enter, Ctrl+X로 저장하고 나가세요.

cargo run --quiet -- check data/plan.tsv data/scans.tsv
TYPE	type_id	name	required	good	missing	surplus	good_ids	damaged_ids
TYPE	light-a	Flashlight	2	3	0	1	a,b,c	-
TYPE	light-b	Flashlight	1	0	1	0	-	d
TYPE	other	-	0	1	0	1	e	f
TYPE	rope	Rope	2	0	2	0	-	-
TYPE	zero	Spare	0	0	0	0	-	-
ASSET	asset_id	type_id	condition	plan
ASSET	a	light-a	good	planned
ASSET	b	light-a	good	planned
ASSET	c	light-a	good	planned
ASSET	d	light-b	damaged	planned
ASSET	e	other	good	unplanned
ASSET	f	other	damaged	unplanned

이제 모든 수량을 추적할 수 있습니다. light-b는 손상 ID d를 유지하지만 부족합니다. other는 두 ID를 유지하지만 e만 초과를 만듭니다. a의 반복 스캔은 실물 개체를 하나 더 만들지 않았습니다. 완성된 함수로 앞서 한 빈 원본 검사를 반복하세요.

cargo run --quiet -- check data/plan.tsv empty-scans.tsv

계획의 네 유형과 스캔 없음에 따른 부족 수량이 여전히 올바라야 합니다. 이제야 이 단계의 전체 대조 검사를 통과할 것으로 기대할 수 있습니다. 준비된 출력 형식이 표시 줄과 ID 목록을 정렬하므로 내부 수집 순서는 동등한 다른 순서여도 유효합니다.

프로젝트를 초기화하지 않고 소스 편집 복구하기

Cargo 오류는 파일과 줄을 알려 줍니다. 그 파일을 다시 열고 Ctrl+W로 함수를 찾아 해당 편집을 복구하세요. 실수한 변경을 아직 저장하지 않았다면 Ctrl+X를 누르고 저장 질문에서 N으로 버립니다. 이전에 저장한 편집은 되돌리지 않습니다.

저장한 실수를 복구할 때만, 다시 할 단계의 체크포인트를 고르세요. .edit-before-types.*에는 시작 코드, .edit-before-sets.*에는 유형 합집합 단계, .edit-before-totals.*에는 최종 계산 전의 전체 ID 집합이 있습니다. 새 Terminal에서는 PATH와 cd를 다시 실행한 뒤 실제 백업 이름을 나열하세요.

ls -d .edit-before-*

-d는 내용 대신 디렉터리 이름을 나열하고 *는 생성된 접미사와 일치합니다. 다음 명령을 실행하세요. 셸이 기다리는 동안 출력된 정확한 디렉터리 이름 하나를 붙여 넣고 Enter를 누르세요. read는 선택을 이 세션 변수에 저장합니다.

read rs03_restore

복사 전에 선택한 소스를 확인하세요.

cat "$rs03_restore/readiness.rs"

경로나 단계가 틀리면 선택과 확인을 반복하세요. 맞는 백업이 없으면 경로를 추측하지 말고 참고 구현으로 함수를 복구해야 합니다. 의도한 단계임을 확인한 뒤 다음 명령으로 허용된 대조 소스만 의도적으로 교체합니다.

cp -p "$rs03_restore/readiness.rs" src/readiness.rs

이후 해당 편집과 시험을 다시 하세요. 소스 체크포인트는 입력을 바꾸지 않습니다. 잘못된 알고리즘에 맞추려고 원래 계획을 변경하지 마세요. 아래 참고 구현은 시도한 뒤 특정 부분을 비교하기 위한 것이며 기본적인 파일 전체 교체 방법이 아닙니다.

비교와 복구를 위한 전체 대조 참고 구현
use crate::{Condition, Need, Scan, TypeSummary};

pub fn reconcile(needs: &[Need], assets: &[Scan]) -> Vec<TypeSummary> {
    let mut types = Vec::new();
    for need in needs {
        types.push(need.type_id.clone());
    }
    for asset in assets {
        if !types.contains(&asset.type_id) {
            types.push(asset.type_id.clone());
        }
    }
    let mut summaries = Vec::new();
    for type_id in types {
        let mut required = 0;
        for need in needs {
            if need.type_id == type_id {
                required = usize::from(need.required);
            }
        }
        let mut good_ids = Vec::new();
        let mut damaged_ids = Vec::new();
        for asset in assets {
            if asset.type_id == type_id {
                match asset.condition {
                    Condition::Good => good_ids.push(asset.asset_id.clone()),
                    Condition::Damaged => damaged_ids.push(asset.asset_id.clone()),
                }
            }
        }
        let missing = required.saturating_sub(good_ids.len());
        let surplus = good_ids.len().saturating_sub(required);
        summaries.push(TypeSummary {
            type_id, required, good_ids, damaged_ids, missing, surplus,
        });
    }
    summaries
}

다른 원본으로 부분 충족 설명하기

이 단계에서는 다른 입력 경로로 부분적인 부족과 손상만 있는 계획 외 유형을 살펴봅니다. Terminal의 /home/labex/project/field-kit에서 계속하세요. 모든 원본 data/ 파일을 보존하고 아래 별도 시험 파일 두 개를 만드세요.

아래 짧은 입력 블록은 here-document를 사용합니다. cat은 뒤의 줄들을 입력으로 받고 >는 지정한 실험 파일에 씁니다. <<'TSV'는 TSV만 있는 줄까지의 리터럴 입력을 시작합니다. 따옴표로 감싼 구분자는 내용의 셸 확장을 막습니다. 양쪽 구분자 줄을 복사하고 실제 탭을 유지하세요. Rust 모듈을 교체하는 것이 아니라 작은 시험 입력입니다. Water filter 이름의 공백은 한 필드 안에 있으며 필드는 탭으로 구분합니다.

cat > trial-plan.tsv <<'TSV'
type_id	name	required
lamp	Lamp	2
filter	Water filter	5
zero	Spare	0
TSV
cat > trial-scans.tsv <<'TSV'
asset_id	type_id	condition
l-one	lamp	good
l-two	lamp	damaged
f-one	filter	good
f-two	filter	good
u-one	unplanned	damaged
TSV

실행 전에 램프 부족 수량, 필터 부족 수량, 손상만 있는 계획 외 유형이 남을지 예측하세요. 이제 이 실제 파일들을 지정하세요.

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv
TYPE	type_id	name	required	good	missing	surplus	good_ids	damaged_ids
TYPE	filter	Water filter	5	2	3	0	f-one,f-two	-
TYPE	lamp	Lamp	2	1	1	0	l-one	l-two
TYPE	unplanned	-	0	0	0	0	-	u-one
TYPE	zero	Spare	0	0	0	0	-	-
ASSET	asset_id	type_id	condition	plan
ASSET	f-one	filter	good	planned
ASSET	f-two	filter	good	planned
ASSET	l-one	lamp	good	planned
ASSET	l-two	lamp	damaged	planned
ASSET	u-one	unplanned	damaged	unplanned

보고서를 해석하기 전에 스캔 수가 아니라 정상 개체 수에서 램프와 필터의 부족을 예측하세요. 스캔 두 줄이 있어도 램프는 하나 부족합니다. 정상 필터 두 개는 필요한 다섯 개 중 두 개만 채우므로 세 개가 부족합니다. 계획 외 손상 개체는 정상 0개, 초과 0개로 계속 보입니다. 어떤 시험 스캔도 zero를 언급하지 않아도 이 유형은 남습니다.

원본 데이터와 첫 시험 스캔을 모두 편집하지 않고 통제된 변경 하나를 해 보세요. cp -p는 복사한 파일의 메타데이터를 유지하며 사본을 만듭니다. 이 별도의 새 파일 이름을 사용하세요. 앞선 시도에서 이미 사용했다면 그 작업을 보존하거나 아직 쓰지 않은 다른 파일 이름을 골라 아래 명령 전체에 일관되게 사용하세요.

cp -p trial-scans.tsv trial-scans-one-repair.tsv
nano -c trial-scans-one-repair.tsv

Ctrl+W로 l-two를 검색하고 Enter를 누르세요. 그 줄에서만 화살표와 Backspace/Delete로 상태 단어 damaged를 good으로 바꾸세요. 개체 ID, 유형 ID, 헤더와 실제 탭 구분자를 보존하세요. Ctrl+O, Enter로 저장하고 Ctrl+X로 나가세요. 보호된 증거를 복구하는 것이 아니라 별도 실험을 의도적으로 편집하는 작업입니다.

실행 전에 영향을 받는 보고서 두 줄을 예측하세요. 이제 TYPE lamp에는 정상 ID 두 개, 손상 ID 없음, 부족 0, 초과 0이 있어야 합니다. ASSET l-two의 상태는 정상으로 바뀌지만 계속 계획 내 개체입니다. 다른 유형·개체 줄은 모두 같아야 합니다. 필터 필요 수량과 계획 외 손상 개체는 바꾸지 않았습니다.

cargo run --quiet -- check trial-plan.tsv trial-scans-one-repair.tsv

그 두 줄을 읽고 다른 줄이 여전히 같은지 확인하세요. 더 많은 내용이 바뀌었다면 cat으로 사본을 확인하고 의도하지 않은 필드를 고치세요. 실수한 입력 편집에 맞추려고 알고리즘을 변경하지 마세요. 변경하지 않은 첫 시험 원본으로 돌아가세요.

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

l-two가 손상 상태인 채로 램프 부족 수량이 다시 1이어야 합니다. 두 보고서가 다른 것은 실제로 지정한 입력이 다르기 때문이며 어느 원본도 조용히 대체되지 않습니다. 같은 완성된 대조 구현이 두 경우 모두를 설명해야 합니다.

입력 오류가 부족 보고서로 바뀌거나 원래 예제로 조용히 대체되어서는 안 됩니다. 다음 잘못된 파일 이름은 의도적이며 0이 아닌 상태를 반환해야 합니다.

cargo run --quiet -- check trial-plans.tsv trial-scans.tsv

오류는 존재하지 않는 trial-plans.tsv를 가리킵니다. 복수형 오타를 고쳐 다시 실행하세요.

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

의도적인 형식 오류에는 기존의 알 수 없는 상태 예제를 사용하세요. 이 명령도 0이 아닌 상태를 반환하고 data/bad-status.tsv와 해당 줄을 알려야 합니다.

cargo run --quiet -- check trial-plan.tsv data/bad-status.tsv

broken은 유효하지 않은 단어이며 damaged는 수요를 채우지 않는 유효한 상태입니다. 유효한 시험 스캔을 다시 선택해 복구하세요.

cargo run --quiet -- check trial-plan.tsv trial-scans.tsv

마지막으로 원래 입력으로 돌아가세요.

cargo run --quiet -- check data/plan.tsv data/scans.tsv

원래 보고서가 다시 나타나는 것은 프로그램이 캐시된 보고서나 하드코딩한 준비 완료 플래그가 아니라 실제 경로를 읽기 때문입니다. 원본과 시험 원본 파일 모두 온전합니다. cat으로 어느 파일이든 살펴볼 수 있습니다. 시험 파일을 실수로 편집했다면 위의 전체 생성 블록을 반복해 복구하세요. 보호된 원본 파일은 코드 오류를 고칠 곳이 아닙니다.

요약

계획과 고유한 개체 기록으로 유형별 전체 대조를 구현했습니다. 정상 장비만 필요 수량을 채웠으며 일부만 충족하면 부족이 남고 손상 장비와 초과 장비도 설명에 유지했습니다. 다른 실제 입력 경로 쌍으로 시험하고 일반 오류를 처리하면서 모든 원본을 보존했습니다. 챌린지에서는 다른 여행용 장비에 개체 수집과 대조를 함께 적용합니다.