基于 SOC 职业分类
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/puk0806/gugbab-claude --skill serde命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
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 분할·검증 게이트·되돌리기, 테스트 없는 코드의 안전망, 작업 순서 설계와 위반 수 기반 진행 추적
| name | serde |
| description | Rust serde + serde_json 직렬화/역직렬화 핵심 패턴 |
소스: https://serde.rs/ , https://docs.rs/serde_json/ 검증일: 2026-06-20
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
features = ["derive"]를 반드시 활성화해야 #[derive(Serialize, Deserialize)]를 사용할 수 있다.
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct User {
name: String,
age: u32,
email: String,
}
Serialize: Rust 구조체 -> 직렬화 포맷 (JSON, TOML 등)Deserialize: 직렬화 포맷 -> Rust 구조체Serialize/Deserialize를 구현해야 동작한다.String, Vec<T>, HashMap<K,V>, Option<T> 등)은 이미 구현되어 있다.let user = User { name: "Alice".into(), age: 30, email: "alice@example.com".into() };
// 일반 JSON 문자열
let json = serde_json::to_string(&user)?;
// {"name":"Alice","age":30,"email":"alice@example.com"}
// 보기 좋게 포맷팅
let pretty = serde_json::to_string_pretty(&user)?;
let json = r#"{"name":"Alice","age":30,"email":"alice@example.com"}"#;
let user: User = serde_json::from_str(json)?;
// &[u8] -> T
let user: User = serde_json::from_slice(bytes)?;
// T -> Vec<u8>
let bytes = serde_json::to_vec(&user)?;
// Read trait -> T
let user: User = serde_json::from_reader(reader)?;
// T -> Write trait
serde_json::to_writer(writer, &user)?;
serde_json::to_writer_pretty(writer, &user)?;
반환 타입: 모든 함수는 serde_json::Result<T>를 반환한다. ? 연산자 또는 unwrap()으로 처리.
구조체 없이 임의의 JSON을 다룰 때 사용한다.
use serde_json::Value;
let data: Value = serde_json::from_str(r#"{"name":"Alice","age":30}"#)?;
// 인덱스 접근 (존재하지 않으면 Value::Null 반환)
println!("{}", data["name"]); // "Alice"
println!("{}", data["age"]); // 30
println!("{}", data["missing"]); // null
use serde_json::json;
let value = json!({
"name": "Alice",
"age": 30,
"tags": ["rust", "serde"],
"address": {
"city": "Seoul"
}
});
enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<Value>),
Object(Map<String, Value>),
}
// as_ 메서드 (Option 반환)
data["name"].as_str(); // Option<&str>
data["age"].as_u64(); // Option<u64>
data["age"].as_i64(); // Option<i64>
data["age"].as_f64(); // Option<f64>
data["flag"].as_bool(); // Option<bool>
data["arr"].as_array(); // Option<&Vec<Value>>
data["obj"].as_object(); // Option<&Map<String, Value>>
// Value -> 구조체 (serde_json::from_value)
let user: User = serde_json::from_value(value)?;
// 구조체 -> Value (serde_json::to_value)
let value = serde_json::to_value(&user)?;
#[derive(Serialize, Deserialize)]
struct User {
#[serde(rename = "userName")]
user_name: String,
// 직렬화/역직렬화 각각 다른 이름
#[serde(rename(serialize = "output_name", deserialize = "input_name"))]
name: String,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ApiResponse {
user_name: String, // JSON: "userName"
created_at: String, // JSON: "createdAt"
}
지원되는 rename_all 규칙:
| 값 | 입력 user_name -> 출력 |
|---|---|
"camelCase" | userName |
"PascalCase" | UserName |
"snake_case" | user_name |
"SCREAMING_SNAKE_CASE" | USER_NAME |
"kebab-case" | user-name |
"SCREAMING-KEBAB-CASE" | USER-NAME |
"lowercase" | username |
"UPPERCASE" | USERNAME |
조건에 따라 직렬화에서 필드를 제외한다.
#[derive(Serialize, Deserialize)]
struct Config {
name: String,
// None이면 JSON 출력에서 제외
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
// 빈 Vec이면 제외 + 역직렬화 시 필드 없어도 허용
#[serde(default, skip_serializing_if = "Vec::is_empty")]
tags: Vec<String>,
// 커스텀 함수 사용 + 역직렬화 시 필드 없어도 허용
#[serde(default, skip_serializing_if = "is_zero")]
count: u32,
}
fn is_zero(v: &u32) -> bool {
*v == 0
}
// 주의: skip_serializing_if는 직렬화에만 영향을 준다.
// Option<T> 필드는 Serde가 특수 처리하여 #[serde(default)] 없이도 누락 시 자동으로 None으로 처리된다.
// Option<T> 외 타입(String, u32, Vec<T> 등)에서 필드 누락을 허용하려면 #[serde(default)]를 반드시 함께 써야 한다.
JSON에 필드가 없을 때 기본값을 사용한다.
#[derive(Serialize, Deserialize)]
struct Config {
name: String,
// 필드 없으면 Default::default() 사용 (u32는 0)
#[serde(default)]
retry_count: u32,
// 커스텀 기본값 함수
#[serde(default = "default_timeout")]
timeout: u64,
}
fn default_timeout() -> u64 {
30
}
#[derive(Serialize, Deserialize)]
struct Internal {
pub name: String,
#[serde(skip)] // 직렬화, 역직렬화 모두 제외
cache: Vec<u8>,
#[serde(skip_serializing)] // 직렬화만 제외
password: String,
#[serde(skip_deserializing)] // 역직렬화만 제외
computed: String,
}
skip, skip_deserializing 사용 시 해당 필드는 Default trait 구현이 필요하다.
#[derive(Serialize, Deserialize, Debug)]
struct Address {
city: String,
zip: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct Company {
name: String,
address: Address,
}
#[derive(Serialize, Deserialize, Debug)]
struct Employee {
name: String,
company: Company,
skills: Vec<String>,
}
{
"name": "Alice",
"company": {
"name": "Acme",
"address": {
"city": "Seoul",
"zip": "06000"
}
},
"skills": ["rust", "typescript"]
}
#[derive(Serialize, Deserialize)]
struct Pagination {
page: u32,
per_page: u32,
}
#[derive(Serialize, Deserialize)]
struct UserQuery {
name: String,
#[serde(flatten)]
pagination: Pagination,
}
// JSON: {"name":"Alice","page":1,"per_page":20}
// (pagination 키 없이 필드가 상위에 펼쳐짐)
use std::collections::HashMap;
#[derive(Serialize, Deserialize)]
struct Known {
name: String,
age: u32,
#[serde(flatten)]
extra: HashMap<String, serde_json::Value>,
}
// 정의되지 않은 필드가 extra에 자동 수집됨
| 속성 | 위치 | 용도 |
|---|---|---|
rename_all | 구조체/열거형 | 전체 필드명 규칙 변환 |
deny_unknown_fields | 구조체 | 정의되지 않은 필드가 있으면 에러 |
tag | 열거형 | 태그 기반 직렬화 ("type": "variant") |
untagged | 열거형 | 태그 없이 내용만으로 variant 판별 |
content | 열거형 | tag와 함께 사용. 데이터를 별도 키에 |
default | 구조체 | 모든 필드에 Default 적용 |
#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
enum Message {
Text { body: String },
Image { url: String, width: u32 },
}
// {"type":"Text","body":"hello"}
#[derive(Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
enum Event {
Click { x: i32, y: i32 },
KeyPress(char),
}
// {"type":"Click","data":{"x":10,"y":20}}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum StringOrInt {
Str(String),
Int(i64),
}
// "hello" 또는 42 (태그 없이 값으로 판별)
주의:
untaggedenum은 variant 순서대로 역직렬화를 시도하므로, 먼저 매치되는 variant가 선택된다. 순서에 주의할 것.