cargo workspace 모노레포 | Cargo.toml 구조·멤버·공통 의존성·빌드 최적화
이 글의 핵심
Rust Cargo workspace로 크레이트를 한 저장소에서 묶는 법: 루트 매니페스트, 멤버 추가, workspace.dependencies, 빌드 캐시까지 실무 팁을 정리합니다.
들어가며
Cargo workspace는 여러 패키지(크레이트)를 하나의 저장소에서 함께 빌드·테스트하기 위한 공식적인 모노레포 패턴입니다. Cargo.lock은 워크스페이스 루트에 하나만 두는 것이 일반적이며, 공유 target/ 덕분에 의존성 그래프가 겹칠 때 컴파일량을 줄일 수 있습니다.
CLI·라이브러리·프로토콜 크레이트를 나눈 팀, 혹은 내부 공유 크레이트를 여러 서비스가 참조하는 팀에서 특히 자주 씁니다. 이 글은 cargo workspace 모노레포 키워드에 맞춰 Cargo.toml 구조, 멤버 관리, 공통 의존성, 빌드 최적화를 실무 관점에서 정리합니다.
이 글을 읽으면
- Cargo workspace의 루트 매니페스트 구조를 이해합니다
- 멤버 추가·공통 의존성 설정 방법을 익힙니다
- 빌드 최적화·CI 전략을 적용할 수 있습니다
실전 경험에서 배운 교훈
이 기술을 실무 프로젝트에 처음 도입했을 때, 공식 문서만으로는 알 수 없었던 많은 함정들이 있었습니다. 특히 프로덕션 환경에서 발생하는 엣지 케이스들은 로컬 개발 환경에서는 재현조차 되지 않았죠.
가장 어려웠던 점은 성능 최적화였습니다. 처음엔 “동작만 하면 되겠지”라고 생각했지만, 실제 사용자 트래픽이 몰리면서 병목 지점들이 하나씩 드러났습니다. 특히 데이터베이스 쿼리 최적화, 캐싱 전략, 에러 핸들링 구조 등은 여러 번의 장애를 겪으면서 개선해 나갔습니다.
이 글에서는 그런 시행착오를 통해 얻은 실전 노하우와, “이렇게 하면 안 된다”는 교훈들을 함께 정리했습니다. 특히 트러블슈팅 섹션은 실제 장애 대응 경험을 바탕으로 작성했으니, 비슷한 문제를 마주했을 때 참고하시면 도움이 될 것입니다.
개념: workspace 루트와 멤버
Workspace란?
- 루트
Cargo.toml에[workspace]섹션이 있고,members에 하위 크레이트 경로가 나열됩니다. - 워크스페이스에 속한 패키지들은 동일한
Cargo.lock정책 아래에서 의존성 버전을 맞추기 쉽습니다. - 기본 패키지(root package)를 두지 않고 메타만 있는 virtual manifest로 두는 팀도 많습니다.
구조 다이어그램
my-workspace/
├── Cargo.toml (workspace 루트)
├── Cargo.lock (공유)
├── target/ (공유 빌드 출력)
├── crates/
│ ├── api/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── core/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── utils/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
└── README.md
핵심 개념
| 항목 | 설명 |
|---|---|
| Virtual Manifest | 루트에 [package] 없이 [workspace]만 있는 구조 |
| Members | 워크스페이스에 속한 크레이트 목록 |
| Shared Lock | 모든 멤버가 동일한 Cargo.lock 사용 |
| Shared Target | 컴파일 결과물을 target/에 공유 |
| Workspace Dependencies | 공통 의존성 버전 관리 |
실전: 최소 레이아웃과 명령
1) 워크스페이스 생성
디렉터리 구조 생성
# 프로젝트 루트 생성
mkdir my-workspace
cd my-workspace
# 멤버 크레이트 생성
cargo new --lib crates/core
cargo new --bin crates/api
cargo new --lib crates/utils
루트 Cargo.toml 작성
[workspace]
resolver = "2"
members = [
"crates/api",
"crates/core",
"crates/utils"
]
[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Your Name <[email protected]>"]
license = "MIT"
repository = "https://github.com/org/my-workspace"
[workspace.dependencies]
# 공통 의존성
serde = { version = "1.0", features = [derive] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
anyhow = "1.0"
tracing = "0.1"
2) 멤버 크레이트 설정
crates/core/Cargo.toml
[package]
name = "core"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
[dependencies]
serde.workspace = true
anyhow.workspace = true
crates/api/Cargo.toml
[package]
name = "api"
version.workspace = true
edition.workspace = true
[dependencies]
# 워크스페이스 내부 크레이트
core = { path = "../core" }
utils = { path = "../utils" }
# 워크스페이스 공통 의존성
tokio.workspace = true
serde.workspace = true
anyhow.workspace = true
# api 전용 의존성
axum = "0.7"
tower = "0.4"
crates/utils/Cargo.toml
[package]
name = "utils"
version.workspace = true
edition.workspace = true
[dependencies]
tracing.workspace = true
3) 코드 예제
crates/core/src/lib.rs
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: u64,
pub name: String,
pub email: String,
}
impl User {
pub fn new(id: u64, name: String, email: String) -> Self {
Self { id, name, email }
}
}
crates/utils/src/lib.rs
use tracing::info;
pub fn log_startup(service_name: &str) {
info!("Starting service: {}", service_name);
}
crates/api/src/main.rs
use axum::{
routing::get,
Router,
Json,
};
use core::User;
use utils::log_startup;
#[tokio::main]
async fn main() {
log_startup("API Server");
let app = Router::new()
.route("/users", get(get_users));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn get_users() -> Json<Vec<User>> {
let users = vec![
User::new(1, "Alice".to_string(), "[email protected]".to_string()),
User::new(2, "Bob".to_string(), "[email protected]".to_string()),
];
Json(users)
}
4) 자주 쓰는 명령
빌드 및 실행
# 전체 워크스페이스 검사
cargo check --workspace
# 전체 빌드
cargo build --workspace
# 전체 테스트
cargo test --workspace
# 특정 크레이트만 빌드
cargo build -p api
# 특정 크레이트만 테스트
cargo test -p core
# 특정 크레이트 실행
cargo run -p api
# 릴리스 빌드
cargo build -p api --release
의존성 관리
# 전체 의존성 트리 확인
cargo tree --workspace
# 특정 크레이트 의존성
cargo tree -p core
# 중복 의존성 확인
cargo tree --duplicates
# 의존성 업데이트
cargo update
# 특정 의존성만 업데이트
cargo update -p serde
정리 및 최적화
# 빌드 캐시 정리
cargo clean
# 특정 크레이트만 정리
cargo clean -p api
# 미사용 의존성 확인
cargo machete
# 포맷팅
cargo fmt --all
# Clippy (린터)
cargo clippy --workspace -- -D warnings
고급: workspace.dependencies·패치
1) 버전 단일화
workspace.dependencies에 올려 두고 멤버에서는 dep.workspace = true만 선언하면 serde·tokio 버전 불일치를 PR 단계에서 줄입니다.
루트 Cargo.toml
[workspace.dependencies]
# 버전 한 곳에서 관리
serde = { version = "1.0", features = [derive] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net"] }
anyhow = "1.0"
thiserror = "1.0"
# 내부 크레이트도 정의 가능
core = { path = "crates/core" }
utils = { path = "crates/utils" }
멤버 Cargo.toml
[dependencies]
# workspace에서 상속
serde.workspace = true
tokio.workspace = true
# 내부 크레이트
core.workspace = true
utils.workspace = true
# 멤버 전용 의존성
axum = "0.7"
2) [patch] - 사내 포크 또는 긴급 패치
사내 포크 사용
# 루트 Cargo.toml
[patch.crates-io]
serde = { git = "https://github.com/company/serde-fork", branch = "custom-feature" }
효과: 모든 멤버가 자동으로 패치된 버전 사용
로컬 패치
[patch.crates-io]
tokio = { path = "../tokio-local" }
사용 시나리오:
- 업스트림 버그 긴급 수정
- 사내 커스터마이징
- 의존성 디버깅 주의사항:
- 유지보수 부담 증가
- 업스트림 업데이트 추적 필요
- 기한 설정 권장
3) default-members
[workspace]
members = [crates/*]
default-members = [crates/api]
효과:
cargo run시 기본 타깃 지정cargo build(인자 없음) 시 default-members만 빌드
4) exclude - 멤버 제외
[workspace]
members = [crates/*]
exclude = ["crates/experimental", "crates/deprecated"]
사용 시나리오:
- 실험적 크레이트 제외
- 레거시 코드 격리
- 빌드 시간 단축
5) 프로파일 공유
# 루트 Cargo.toml
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
strip = true
[profile.dev]
opt-level = 0
debug = true
[profile.dev.package."*"]
opt-level = 2 # 의존성만 최적화
효과: 모든 멤버가 동일한 프로파일 사용
비교: 단일 크레이트·멀티 레포
| 항목 | 단일 크레이트 | Workspace | 멀티 레포 |
|---|---|---|---|
| 경계 | 모듈로 분리 | 크레이트로 분리 | 레포로 분리 |
| 빌드 캐시 | 단일 target | 공유 target | 레포별 독립 |
| 의존성 관리 | 단일 Cargo.toml | workspace.dependencies | 레포별 독립 |
| 버전 관리 | 단일 버전 | 멤버별 또는 공유 | 레포별 독립 |
| 권한 관리 | 레포 단위 | 레포 단위 | 레포별 세밀 |
| 릴리스 | 단일 릴리스 | 멤버별 또는 통합 | 레포별 독립 |
| 학습 곡선 | 낮음 | 중간 | 높음 |
선택 가이드
단일 크레이트 선택:
- ✅ 작은 프로젝트 (< 10,000 LOC)
- ✅ 모듈 경계가 명확하지 않음
- ✅ 팀 규모 작음 (1-3명) Workspace 선택:
- ✅ 중대형 프로젝트
- ✅ 명확한 경계 (CLI, 라이브러리, 서버)
- ✅ 공통 코드 재사용
- ✅ 원자적 변경 필요 멀티 레포 선택:
- ✅ 독립적 릴리스 주기
- ✅ 레포별 권한 분리
- ✅ 외부 공개 크레이트
실무 사례
사례 1: 공유 도메인 로직 + 여러 바이너리
구조:
my-app/
├── Cargo.toml
├── crates/
│ ├── core/ (공유 도메인 로직)
│ │ └── src/lib.rs
│ ├── api/ (REST API 서버)
│ │ └── src/main.rs
│ ├── worker/ (백그라운드 워커)
│ │ └── src/main.rs
│ └── cli/ (CLI 도구)
│ └── src/main.rs
루트 Cargo.toml:
# 실행 예제
[workspace]
members = [crates/*]
[workspace.dependencies]
core = { path = "crates/core" }
serde = { version = "1.0", features = [derive] }
tokio = { version = "1", features = [full] }
crates/api/Cargo.toml:
# 실행 예제
[package]
name = "api"
version = "0.1.0"
edition = "2021"
[dependencies]
core.workspace = true
tokio.workspace = true
axum = "0.7"
crates/worker/Cargo.toml:
[package]
name = "worker"
version = "0.1.0"
edition = "2021"
[dependencies]
core.workspace = true
tokio.workspace = true
장점:
core변경 시 모든 바이너리 자동 재빌드- 의존성 버전 일관성
- 공유
target/로 빌드 시간 단축
사례 2: FFI 바인딩 (sys + safe wrapper)
구조:
libfoo-rs/
├── Cargo.toml
├── crates/
│ ├── libfoo-sys/ (C 라이브러리 바인딩)
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ └── src/lib.rs
│ └── libfoo/ (안전한 Rust 래퍼)
│ ├── Cargo.toml
│ └── src/lib.rs
libfoo-sys/Cargo.toml:
# 실행 예제
[package]
name = "libfoo-sys"
version = "0.1.0"
edition = "2021"
links = "foo"
[build-dependencies]
cc = "1.0"
libfoo-sys/build.rs:
// 함수 정의 및 구현
fn main() {
cc::Build::new()
.file("vendor/libfoo.c")
.compile("foo");
}
libfoo/Cargo.toml:
# 실행 예제
[package]
name = "libfoo"
version = "0.1.0"
edition = "2021"
[dependencies]
libfoo-sys = { path = "../libfoo-sys" }
libfoo/src/lib.rs:
use libfoo_sys::*;
pub struct Foo {
inner: *mut FooHandle,
}
impl Foo {
pub fn new() -> Self {
unsafe {
Self {
inner: foo_create(),
}
}
}
pub fn process(&self, data: &[u8]) -> Vec<u8> {
unsafe {
// 안전한 래퍼 제공
let result = foo_process(self.inner, data.as_ptr(), data.len());
// ...
vec![]
}
}
}
impl Drop for Foo {
fn drop(&mut self) {
unsafe {
foo_destroy(self.inner);
}
}
}
장점:
-sys와 안전 래퍼를 함께 버전업- 내부 API 변경 시 원자적 커밋
사례 3: 마이크로서비스 모노레포
구조:
services/
├── Cargo.toml
├── crates/
│ ├── shared/ (공통 타입, 유틸)
│ ├── auth-service/
│ ├── user-service/
│ ├── order-service/
│ └── gateway/
루트 Cargo.toml:
[workspace]
members = [
"crates/shared",
"crates/auth-service",
"crates/user-service",
"crates/order-service",
"crates/gateway"
]
[workspace.dependencies]
shared = { path = "crates/shared" }
tokio = { version = "1", features = [full] }
tonic = "0.11"
prost = "0.12"
shared/src/lib.rs:
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserId(pub u64);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderId(pub u64);
pub mod error {
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ServiceError {
#[error("Not found: {0}")]
NotFound(String),
#[error("Internal error: {0}")]
Internal(String),
}
}
auth-service/src/main.rs:
use shared::{UserId, error::ServiceError};
#[tokio::main]
async fn main() {
println!("Auth service started");
}
async fn authenticate(token: &str) -> Result<UserId, ServiceError> {
// 인증 로직
Ok(UserId(123))
}
CI 빌드 최적화:
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: "workspace"
- name: Check all
run: cargo check --workspace
- name: Test all
run: cargo test --workspace
- name: Clippy
run: cargo clippy --workspace -- -D warnings
트러블슈팅
문제 1: 멤버가 인식 안 됨
증상:
cargo build -p api
# error: package `api` is not a member of the workspace
원인:
members경로 오타- 중첩 workspace (금지) 해결:
# 루트 Cargo.toml
[workspace]
members = [
"crates/api", # 경로 확인
"crates/core"
]
확인:
# 워크스페이스 멤버 목록
cargo metadata --no-deps | jq '.workspace_members'
문제 2: 의존성 버전 충돌
증상:
error: failed to select a version for `serde`
원인: 멤버들이 서로 다른 major 버전 지정 해결:
# 루트 Cargo.toml
[workspace.dependencies]
serde = "1.0" # 버전 통일
# 멤버 Cargo.toml
[dependencies]
serde.workspace = true # 루트 버전 사용
문제 3: 빌드 캐시 비대
증상: target/ 디렉터리가 수 GB
원인:
- 불필요한 feature 활성화
- 여러 프로파일 빌드
- 미사용 의존성 해결:
# 정기적 정리
cargo clean
# 특정 프로파일만 정리
cargo clean --release
# 미사용 의존성 제거
cargo machete
sccache 사용:
# 설치
cargo install sccache
# 환경 변수 설정
export RUSTC_WRAPPER=sccache
# 빌드 (캐시 공유)
cargo build --workspace
문제 4: 테스트 시간 과다
증상: cargo test --workspace 실행 시 수 분 소요
해결 1: nextest 사용
# 설치
cargo install cargo-nextest
# 병렬 테스트
cargo nextest run --workspace
해결 2: 변경된 크레이트만 테스트
# Git diff 기반
CHANGED=$(git diff --name-only HEAD~1 | grep '^crates/' | cut -d'/' -f2 | sort -u)
for crate in $CHANGED; do
cargo test -p $crate
done
문제 5: 순환 의존성
증상:
error: cyclic package dependency: package `api` depends on itself
원인: A → B → A 순환
해결: 공통 타입을 세 번째 크레이트로 추출
Before:
api → core → api (순환!)
After:
api → core → shared
↓
shared
예시:
# shared/Cargo.toml
[package]
name = "shared"
# core/Cargo.toml
[dependencies]
shared = { path = "../shared" }
# api/Cargo.toml
[dependencies]
shared = { path = "../shared" }
core = { path = "../core" }
마무리
Cargo workspace는 Rust에서 모노레포를 공식 패턴으로 가져가는 방법이며, workspace.dependencies와 좁은 -p 빌드만으로도 운영 체감이 크게 좋아집니다.
핵심 요약
- 구조
- 루트에
[workspace]선언 - 멤버 크레이트는
members배열에 나열 - 공유
Cargo.lock과target/
- 루트에
- 의존성 관리
workspace.dependencies로 버전 통일- 멤버는
.workspace = true로 상속 [patch]로 긴급 수정
- 빌드 최적화
-p옵션으로 특정 크레이트만 빌드sccache로 캐시 공유cargo nextest로 병렬 테스트
- CI/CD
- 변경된 크레이트만 빌드
- 공유 캐시 키 사용
- Clippy·fmt 전체 실행
다음 단계
- Rust 입문: Rust 시작하기
- 타입 시스템: String vs &str
- 프로젝트 구조: Rust 프로젝트 구조 가이드
Cargo workspace는 Rust 모노레포의 표준입니다. 처음에는 복잡해 보이지만,
workspace.dependencies와 공유target/의 이점을 경험하면 다시 돌아가기 어렵습니다.
심화 부록: 구현·운영 관점
이 부록은 앞선 본문에서 다룬 주제(「cargo workspace 모노레포 | Cargo.toml 구조·멤버·공통 의존성·빌드 최적화」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.
내부 동작과 핵심 메커니즘
flowchart TD A[입력·요청·이벤트] --> B[파싱·검증·디코딩] B --> C[핵심 연산·상태 전이] C --> D[부작용: I/O·네트워크·동시성] D --> E[결과·관측·저장]
sequenceDiagram participant C as 클라이언트/호출자 participant B as 경계(런타임·게이트웨이·프로세스) participant D as 의존성(API·DB·큐·파일) C->>B: 요청/이벤트 B->>D: 조회·쓰기·RPC D-->>B: 지연·부분 실패·재시도 가능 B-->>C: 응답 또는 오류(코드·상관 ID)
- 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
- 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
- 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
- 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.
프로덕션 운영 패턴
| 영역 | 운영 관점 질문 |
|---|---|
| 관측성 | 요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가 |
| 안전성 | 입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가 |
| 신뢰성 | 재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가 |
| 성능 | 캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가 |
| 배포 | 롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가 |
| 용량 | 피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가 |
스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.
확장 예시: 엔드투엔드 미니 시나리오
앞선 본문 주제(「cargo workspace 모노레포 | Cargo.toml 구조·멤버·공통 의존성·빌드 최적화」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.
- 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
- 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
- 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
- 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
- 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
ctx = newCorrelationId()
validated = validateSchema(request)
authorize(validated, ctx)
result = domainCore(validated)
persistOrEmit(result, idempotentKey)
recordMetrics(ctx, latency, outcome)
return result
문제 해결(Troubleshooting)
| 증상 | 가능 원인 | 조치 |
|---|---|---|
| 간헐적 실패 | 레이스, 타임아웃, 외부 의존성, DNS | 최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검 |
| 성능 저하 | N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스 | 프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거 |
| 메모리 증가 | 캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납 | 상한·TTL·힙/FD 스냅샷 비교 |
| 빌드·배포만 실패 | 환경 변수, 권한, 플랫폼 차이, lockfile | CI 로그와 로컬 diff, 런타임·이미지 버전 핀 |
| 설정 불일치 | 프로필·시크릿·기본값, 리전 | 스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화 |
| 데이터 불일치 | 비멱등 재시도, 부분 쓰기, 캐시 무효화 누락 | 멱등 키·아웃박스·트랜잭션 경계 재검토 |
권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.
배포 전에는 git add → git commit → git push 후 npm run deploy 순서를 권장합니다.
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Rust Cargo workspace로 크레이트를 한 저장소에서 묶는 법: 루트 매니페스트, 멤버 추가, workspace.dependencies, 빌드 캐시까지 실무 팁을 정리합니다. Start now. 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- CMake 에러 완벽 해결 가이드 | 빌드 실패·의존성·링커 에러 트러블슈팅
- C++ Conan 완벽 가이드 | 현대적인 C++ 패키지 관리
- C++ 링킹 완벽 가이드 | 정적/동적 링킹과 에러 해결 (undefined reference)
이 글에서 다루는 키워드 (관련 검색어)
Rust, Cargo, Workspace, 모노레포, 의존성 관리, 빌드 등으로 검색하시면 이 글이 도움이 됩니다.