소스 정보
- 저장소
- puk0806/gugbab-claude
- 최근 소스 활동
- 2026년 6월 23일 04:41
- 감지된 SKILL.md 언어
- 한국어
- 스타
- 2
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/puk0806/gugbab-claude --skill tracing명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
DDD(Domain-Driven Design) 아키텍처 핵심 패턴 - 유비쿼터스 언어, 서브도메인, 바운디드 컨텍스트, Aggregate, Entity/VO, 도메인 서비스/이벤트, 레이어드 아키텍처
대규모 React/Next.js 프로젝트를 layer-first(types/·utils/·hooks/·api/·components/ 밑에 도메인이 반복되는 구조)에서 domain-first(feature/도메인 우선) 구조로 전환하는 설계 기준과 절차. Feature-Sliced Design 2.1 정본(layers 6종·slices·segments·import 규칙·@x 크로스임포트·public API), FSD를 쓰지 않는 경량 대안(features + shared 2~3계층 + ESLint import/no-restricted-paths), Next.js App Router 공존 전략(route group `()`·private folder `_`·colocation), Turborepo/Nx 모노레포에서 폴더↔패키지 승격 기준, colocation과 배럴 파일 성능 트레이드오프, 도메인 경계 역추출(import 그래프·change coupling·용어 클러스터), 전환 실패 패턴(shared 비대화·entities 남용·순환 의존·도메인=라우트 착각·조기 추상화). 도메인 개념 자체(바운디드 컨텍스트·유비쿼터스 언어)는 `architecture/ddd` 스킬을 참조한다.
소스 파일 수천 개 규모 프론트엔드 코드베이스를 멈추지 않고 점진 재구조화하는 실행 전략 - Strangler Fig / Branch by Abstraction / Parallel Change, ts-morph·jscodeshift codemod, PR 분할·검증 게이트·되돌리기, 테스트 없는 코드의 안전망, 작업 순서 설계와 위반 수 기반 진행 추적
SOC 직업 분류 기준
SKILL.md 표시 중
| name | tracing |
| description | Rust tracing + tracing-subscriber 구조화 로깅 - 초기화, EnvFilter, 매크로, |
소스: https://docs.rs/tracing/latest/tracing/ | https://docs.rs/tracing-subscriber/latest/tracing_subscriber/ 검증일: 2026-06-20
주의: tracing 0.1.x / tracing-subscriber 0.3.x 기준. tracing 0.2는 미출시 상태이며, 향후 API 변경 가능성이 있으므로 실제 프로젝트의 Cargo.lock 버전을 확인할 것.
[dependencies]
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
주요 feature flag (tracing-subscriber):
| Feature | 설명 |
|---|---|
env-filter | EnvFilter (RUST_LOG 환경변수 파싱) |
json | JSON 형식 로그 출력 (fmt::format::Json) |
fmt | 포맷팅 레이어 (기본 활성화) |
registry | Registry subscriber (기본 활성화) |
use tracing_subscriber::EnvFilter;
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info")))
.init();
tracing_subscriber::registry()는 여러 Layer를 .with()로 조합할 수 있는 Subscriber 구현체다.
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,my_crate=debug")))
.with(tracing_subscriber::fmt::layer())
.init();
SubscriberExt 트레이트: registry()에 .with() 메서드 제공SubscriberInitExt 트레이트: .init() / .try_init() 메서드 제공.init()은 글로벌 subscriber를 설정하며, 프로세스당 1회만 호출 가능tracing_subscriber::registry()
.with(EnvFilter::new("info"))
.with(tracing_subscriber::fmt::layer().json())
.init();
EnvFilter는 RUST_LOG 환경변수를 파싱하여 로그 레벨을 제어한다.
# 전역 info, 특정 크레이트만 debug
RUST_LOG=info,my_crate=debug cargo run
# 특정 모듈의 특정 타깃
RUST_LOG=my_crate::api=trace cargo run
# 스팬 필드 기반 필터링
RUST_LOG="info,[my_span]=debug" cargo run
디렉티브 문법:
| 패턴 | 의미 |
|---|---|
info | 전역 INFO 이상 |
my_crate=debug | my_crate 크레이트만 DEBUG 이상 |
my_crate::module=trace | 특정 모듈만 TRACE 이상 |
tower_http=debug | tower-http 내부 로그 활성화 |
코드에서 직접 설정:
// 기본값 fallback 패턴
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,tower_http=debug"));
use tracing::{info, warn, error, debug, trace};
info!("서버 시작");
warn!("설정 파일 없음, 기본값 사용");
error!("데이터베이스 연결 실패");
매크로의 첫 번째 인자들로 key = value 형태의 구조화 필드를 전달한다. 메시지 문자열은 마지막에 위치한다.
// key = value 형태
info!(port = 3000, host = "0.0.0.0", "서버 시작");
// 변수명과 필드명이 같으면 축약 가능
let port = 3000;
info!(port, "서버 시작"); // port = 3000
// % 접두사: Display 트레이트로 기록
// ? 접두사: Debug 트레이트로 기록
info!(user = %user_id, details = ?request, "요청 수신");
// 빈 필드 (나중에 스팬에서 채울 때)
info!(result = tracing::field::Empty, "처리 시작");
let user = "alice";
info!("사용자 {} 로그인", user); // 포맷 문자열
info!(user, "로그인 처리 완료"); // 구조화 필드 + 메시지
info!(user, action = "login", "완료"); // 복수 필드 + 메시지
함수 진입/종료 시 자동으로 스팬을 생성한다. tracing 크레이트에 포함되어 있다.
use tracing::instrument;
#[instrument]
async fn handle_request(user_id: u64, action: String) {
// 자동으로 span 생성: handle_request{user_id=123, action="create"}
info!("요청 처리 중");
}
함수의 모든 인자가 기본적으로 스팬 필드로 기록된다.
민감한 정보나 Display/Debug를 구현하지 않은 인자를 제외한다.
#[instrument(skip(db, password))]
async fn create_user(
db: &DatabasePool,
username: String,
password: String,
) -> Result<User, Error> {
// span: create_user{username="alice"}
// db, password는 기록되지 않음
todo!()
}
skip_all: 모든 인자를 제외한다.
#[instrument(skip_all)]
async fn internal_process(data: LargeStruct) {
todo!()
}
추가 필드를 정의하거나 인자 필드를 재정의한다.
#[instrument(skip(req), fields(method = %req.method(), uri = %req.uri()))]
async fn handle(req: Request) -> Response {
// span: handle{method=GET, uri=/api/users}
todo!()
}
Empty 필드를 선언하고 함수 내에서 나중에 채울 수 있다:
#[instrument(fields(result))]
async fn compute(input: u64) -> u64 {
let output = input * 2;
tracing::Span::current().record("result", output);
output
}
#[instrument(
name = "custom_span_name", // 스팬 이름 지정 (기본: 함수명)
level = "debug", // 스팬 레벨 (기본: INFO)
target = "my_crate::api", // 로그 타깃 지정
err, // Result::Err 시 자동으로 error 이벤트 기록
ret, // 반환값을 자동 기록
)]
async fn my_function() -> Result<String, Error> {
todo!()
}
err: 함수가 Result를 반환할 때, Err 케이스를 ERROR 레벨로 기록err(level = "warn"): err 기록 레벨 변경ret: 반환값을 DEBUG 레벨로 기록ret(level = "info"): ret 기록 레벨 변경스팬은 작업의 시작과 끝을 나타내는 구간이다. 이벤트(info! 등)는 특정 시점의 기록이고, 스팬은 기간(duration)을 나타낸다. 스팬은 중첩되어 트리 구조를 형성한다.
use tracing::{info_span, debug_span, warn_span, span, Level};
let span = info_span!("request", method = "GET", path = "/api");
let span = debug_span!("db_query", table = "users");
let span = span!(Level::INFO, "custom_span", key = "value");
// 방법 1: .enter() - 동기 코드 전용. Drop 시 자동 종료.
let span = info_span!("sync_work");
let _guard = span.enter(); // _guard가 drop될 때까지 이 스팬 안
do_sync_work();
// _guard drop -> 스팬 종료
// 방법 2: .in_scope() - 클로저 범위 동안 스팬 활성화
let result = info_span!("compute").in_scope(|| {
expensive_computation()
});
주의:
.enter()는 async 함수 내에서 사용하면 안 된다..enter()가 반환하는 드롭 가드는 스코프를 벗어날 때 드롭되어야 스팬이 종료되는데, async에서.await를 만나면 스코프를 양보하면서도 드롭 가드가 드롭되지 않는다. 결과적으로 스팬이 종료되지 않은 상태로 다른 태스크가 실행된다. 멀티스레드 런타임에서는 재개 시 다른 스레드에서 실행되는 추가 문제도 있다. async 코드에서는#[instrument]또는.instrument()메서드를 사용한다.
use tracing::Instrument;
async fn parent_task() {
let span = info_span!("child_task", task_id = 42);
// .instrument()로 Future에 스팬을 부착
some_async_work()
.instrument(span)
.await;
}
let span = info_span!("process", result = tracing::field::Empty);
let _guard = span.enter();
let value = compute();
span.record("result", value);
tower-http의 TraceLayer를 사용하여 Axum 요청/응답을 자동 추적한다.
[dependencies]
axum = "0.8"
tower-http = { version = "0.6", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
use axum::{routing::get, Router};
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,tower_http=debug")))
.with(tracing_subscriber::fmt::layer())
.init();
let app = Router::new()
.route("/", get(root))
.layer(TraceLayer::new_for_http());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
tracing::info!("서버 시작: 0.0.0.0:3000");
axum::serve(listener, app).await.unwrap();
}
async fn root() -> &'static str {
tracing::info!("루트 핸들러 호출");
"Hello, World!"
}
use tower_http::trace::{TraceLayer, DefaultMakeSpan, DefaultOnResponse};
use tracing::Level;
let trace_layer = TraceLayer::new_for_http()
.make_span_with(DefaultMakeSpan::new().level(Level::INFO))
.on_response(DefaultOnResponse::new().level(Level::INFO));
// 주의: axum::serve() 없이 단독 사용 시 Router 타입 추론 실패 가능. 타입 명시 권장.
let app: Router = Router::new()
.route("/", get(root))
.layer(trace_layer);
use axum::extract::Path;
use tracing::instrument;
#[instrument(skip_all, fields(user_id))]
async fn get_user(Path(user_id): Path<u64>) -> String {
tracing::Span::current().record("user_id", user_id);
tracing::info!("사용자 조회");
format!("User {}", user_id)
}
use tracing_subscriber::{
layer::SubscriberExt,
util::SubscriberInitExt,
EnvFilter,
};
pub fn init_tracing() {
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::new(
"info,my_app=debug,tower_http=debug,sqlx=warn"
)
});
tracing_subscriber::registry()
.with(env_filter)
.with(tracing_subscriber::fmt::layer()
.with_target(true) // 로그 타깃 표시
.with_thread_ids(false) // 스레드 ID 숨김
.with_file(true) // 파일명 표시
.with_line_number(true) // 라인 번호 표시
)
.init();
}