내부 개발 루프
프로젝트를 다루면서 다음 과정을 되풀이할 것이다.
- 변경한다.
- 애플리케이션을 컴파일한다.
- 테스트를 실행한다.
- 애플리케이션을 실행한다.
이를 내부 개발 루프라고 한다. 내부 개발 루프의 속도는 여러분이 단위 시간당 완료할 수 있는 반복 횟수의 상한선이다.
빠른 링킹
내부 개발 루프를 고려할 떄 주로 점진적인 컴파일 성능을 본다. 소스 코드를 조금 변경한 후 카고가 바이너리를 다시 빌드하는 데 걸리는 시간에 초점을 맞춘다. 링킹 단계에서는 상당한 시간이 소요되며, 이전 컴파일 단계의 결과물과 실제 바이너리를 조립한다.
기본 링커는 잘 작동하기는 하지만 사용한느 운영체제에 따라 더 빠른 방법이 있다.
- 윈도우나 리눅스에서의 lld, LLVM 프로젝트에서 개발한 링커
- 맥 OS의 zld
링킹 단계의 속도를 높이려면 기기에 대안이 될 링커를 설치하고 해당 설정 파일을 프로젝트에 추가해야 한다.
.cargo/config.toml
On WIndows
```
cargo install -f cargo-binutils
rustup component add llvm -tools-preview
```
[target.x86-64-pc-window.msvc]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
[target.x86_64-pc-window.gnu]
rustflags = ["-C", "link-arg=-fuse-ld-lld"]
# On linux:
# - Ubuntu, `sudo apt-get install lld clang`
# - Arch, `sudo pacman -S lld clang`
[target.X86_64-unknown-linux-gnu]
rustflags = ["-C", "linker = clang", "-C", "link-args-=fuse-ld=lld"]
#On MacOS, `brew install michaeleisel/zid/zid`
[target.X86_64-apple-darwin]
rustflags = ["-C", "lionk-args=-fust-ld=/usr/local/bin/zld"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-fuse-ld==/usr/local/bin'/zld"]
cargo-watch
인지된 컴파일 시간, 즉 cargo check나 cargo run이 완료되기까지 시간을 줄이면 생산성에 미치는 영향을 줄일 수 있다.
cargo install cargo-watch
cargo-watch는 소스코드를 모니터링하면서 파일이 변경될 때마다 명령을 트리거한다.
예를 들어 다음 명령은 코드가 변경될 때마다 cargo check를 수행한다.
cargo watch -x check
이는 인지된 컴파일 시간을 줄여준다.
- 사용하던 IDE에서 변경한 코드를 다시 읽는다.
- cargo-watch는 그동안 컴파일 프로세서를 다시 시작한다.
- 터미널로 전환하면 컴파일러는 이미 절반 정도 완료되어 있다.
cargo-watch는 명령어 체이닝도 지원한다.
cargo watch -x check -x test -x run
가장 먼저 cargo check를 실행해 체크가 성공하면 cargo test로 테스트하고, 테스트가 성공하면 cargo run으로 애플리케이션을 실행한다. 그 자체로 내부 개발 루프다.
지속적인 통합 CI
트렁크 기반 개발에서는 메인 브랜치를 언제든지 배포할 수 있어야 한다. 모든 구성원은 메인에서 브랜치를 분기할 수 있으며, 작은 기능을 개발하거나 버그를 수정하고 메인 브랜치로 병합한 후 사용자에게 릴리스한ㅊ
지속적인통합은 팀의 모든 구성원이 변경한 내용을 하루에 수차례씩 메인 브랜치에 병합할 수 있도록 해준다.
이는 강력한 파급 효과를 낳는다. 일부 항목은 발견하기 쉽다. 지속적인 통합은 오래된 브랜치들
때문에 일어나는 병합 충돌을 줄이며, 피드백 루프를 강화시킨다. 선택한 접근 방식이 다른 팀의 지지를 받지 못하거나 프로젝트의 다른 부분과 잘 통합되지 않는다는 것을 아는 데 걸리는 시간을 줄인다. 또한 팀원과 협업하도록 하며 필요하다면 아무도 기분이 상하지 않게끔 경로를 수정한다.
어떻게 가능한 것일까? CI 파이프라인에서는 모든 커밋의 자동화된 체크를 수행한다. 체크가 하나라도 실패하면 메인으로 병합할 수 없다.
CI 단계
- 테스트:
- CI 파이프라인의 단계가 하나만 존재한다면 테스팅이어야 한다. 러스트 에코시스템에서 테스트는 일급 객체로 취급되며, cargo test를 활용해 단위 테스트와 통합 테스트를 실행할 수 있다.
cargo test
cargo test는 프로젝트를 빌드한 후 테스트를 수행하기 때문에 cargo build를 별도로 실행하지 않아도 된다. 물론 대부분의 파이프라인으ㅔ서는 cargo build를 실행한 뒤 캐시 디펜던시 테스트를 한다.
2. 코드 커버리지
코드 커버리지측정의 장단점에 관한 많은 글이 있다. 품질지표로 코드 커버리지를 사용하는 것은 여러 문제점이 있으나, 필자는 코드 정보를 수집하고 코드베이스 일부가 시간이 지남에 따라 잊혀졌을 떄 테스트가 제대로 됐는지 확인하는 신속한 방법이라고 생각한다.
러스트 프로젝트의 코드 커버리지를 가장 쉽게 측정하는 방법 중 하나는 cargo tarpaulin을 사용하는 것이다. 카고의 하위 명령어이며, xd009642가 개발했다. 다음 명령어로 tarpaulin을 설치할 수 있다.
# 집필 시점을 기준으로 tarpaulin은 리눅스를 실행하는 x86_64 CPU 아키텍처만 지원한다.
cargo intstall cargo-tarpaulin
또한, 다음 명령어를 실행하면 테스트 함수를 제외한 애플리케이션 코드에 대해서만 코드 커버리지를 계산한다.
cargo tarpaulin --ignor-tests
tarpaulin을 사용해 Codecov, Coveralls 같은 잘 알려진 서비스에 코드 커버리지 표를 업로드할 수도 있다. 자세한 사용법은 tarpaulin의 Readme문서를 확인하기 바란다.
3. 린팅
어떤 프로그래밍 언어든 자연스러운 코드를 작성할 때까지는 어느 정도의 시간이 걸린다. 단순한 접근 방식으로 해결할 수 있는 문제를 복잡한 방법으로 해결하다가 학습이 끝나버리는 경우도 많다.
이럴 때는 정적분석의 도움을 받을 수 있다. 컴팡리러가 여러분의 코드를 살펴보면서 언어의 규칙과 제약 사항을 만족하는지 확인하는 것과 마찬가지로, 린터는 자연스럽지 않은 코드, 과도하게 복잡한 구조, 일반적인 실수, 비효율성 등을 찾아낸다.
러스트 팀에서는 공식 러스트 린터인 clippy를 유지보수하고 있다. clippy는 rustup으로 설치한 컴포넌트 셋의 기본 프로파일에 포함되어 있다. 종종 CI 환경에서는 rustup의 최소 프로파일을 사용하는데, 여기에는 clippy가 포함되지 않는다. 다음 명령을 실행하면 쉽게 설치할 수 있다.\
rustup component add clippy
이미 clippy가 설치되어 있다면 위 명령은 아무런 동작도 실행하지 않는다.
다음 명령어로 프로젝트에서 clippy를 실행할 수 있다.
cargo clippy
CI 파이프라인에서 clippy가 warning을 발생시키면 린터 체크를 실패하게 될 것이다. 다음 명령을 실행해서 설정할 수 있다.
cargo clippy -- -D warnings
정적 분석에도 오류는 존재한다. 때떄로 clippy는 올바르거나 바람직하지 않는 것 같은 수정을 제안하기도 한다.
#[allow(clippy::lint_name)] 속성을 사용해서 대상 코드 블록에서 warning에 대한 체크를 비활성화할 수 있다. 아니면 clippy.toml을 설정하거나 프로젝트 레벨에서 #![allow(clippy::lint_name)] 속성을 사용해서 대상 코드 블록에서 warning에 대한 체크를 비활성화할 수 있다. 아니면 clippy.toml을 설정하거나 프로젝트 레벨에서 #![allow(clippy::lint_name)] 지시자를 사용해 시끄러운 린트 메시지를 모두 비활성화할 수도 있다.
사용할 수 있는 린트와 특정한 목적을 위해 이들을 조정하는 방법에 관해서는 clippy의 README.md 파일을 참조한다.
4. 포매팅
대부분의 기업은 메인 브랜치 앞에 하나 이상의 수비 라인을 갖고 있다. 하나는 CI 파이프라인 체크, 다른 하나는 풀 리퀘스트 리뷰다.
rustfmt는 공식 러스트 포캐터이다. clippy와 마찬가지로 rustfmt는 rustup으로 설치되는 기본 컴포넌트 셋에 포함된다. 다음 명령어로도 설치할 수 있다.
rustup component add rustfmt
다음 명령어를 실행하면 프로젝트 전체의 형태를 다듬을 수 있다.
cargo fmt
CI 파이프라인에 다음 포매팅 단계를 추가할 것이다. cargo fmt -- --check
커밋 안에 포매팅되지 않은 코드가 포함되어 있으면 실패하고, 그 차이를 콘솔에 출력한다.
5. 보안 취약점
cargo를 사용하면 에코 시스템의 기존 크레이트들을 활용해 문제를 매우 쉽게 해결할 수 있다, 반면 각각의 크레이트는 착취당할 수 있는 취약성이 잠재되어 있을 수 있으며, 여러분의 소프트웨어의 보안 태세를 무너뜨릴 수 있다.
러스트 시큐어 코드 워킹 그룹은 이와 관련된 자문 데이터베이스를 갖고 있다. 이 데이터베이스는 배포된 크레이트들의 최신 취약점을 모은 것이다.
또한, cargo-audit 명령어를 제공한다. 이 명령 역시 cargo의 하위 명령어이며 여러분의 프로젝트의 의존성 트리에 존재하는 모든 크레이트와 관련해 보고된 취약점을 확인한다. 다음 명령어로 cargo-audit을 설치할 수 있다.
cargo install cargo-audit
설치가 완료되면, 다음 명령어를 실행해서 의존성 트리를 스캔한다.
cargo-audit
cargo-audit은 CI 파이프라인의 일부로 모든 커밋에 대해 실행한다.
일(daily)단위로 수행함으로써 작업할 때는 확인하지 못했지만, 프로덕션 환경에서 실행할 때 발생하는 새로운 취약점도 분석한다.
5. 즉시 사용할 수 있는 CI 파이프라인들
- 깃허브 액션
- CircleCi
- 깃랩CI
- Travis