f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.9 KiB
6.9 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | tags | raw_sources | last_reinforced | github_commit | tech_stack | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| wiki-2026-0508-effect-ts-및-ts-brand-라이브러리-활용 | Effect TS 및 ts-brand 라이브러리 활용 | 10_Wiki/Topics | verified | self |
|
none | A | 0.9 | applied |
|
2026-05-10 | pending |
|
Effect TS 및 ts-brand 라이브러리 활용
매 한 줄
"매 typed effect system + nominal type 의 — TypeScript 의 의 ZIO/Cats 의 imported". Effect 의 의 async/error/dependency 의 의 single 의
Effect<A, E, R>의 의 unify, ts-brand 의 의 structural type 의 의 nominal flavor 의 의 add. 2026 의 매 enterprise TS codebase 의 의 fast 의 emerging.
매 핵심
매 Effect 의 type
Effect<A, E, R>— 의 successA, 의 failureE, 의 requirementR.- 매 lazy — 매
Effect.runPromise/Effect.runSync의 의 actual 의 execute. - 의 composable —
pipe,Effect.gen의 의 chain.
매 ts-brand 의 nominal type
- 의 TypeScript structural —
string === string의 의 distinguishable X. - Brand 의 의
string & { __brand: "UserId" }의 의 phantom tag. - 의 runtime cost zero — type-level only.
매 응용
- Domain ID (UserId, OrderId 의 의 mix-up 의 prevent).
- Validated value (Email, NonEmptyString).
- Async pipeline 의 typed error.
- Dependency injection (Effect Layer).
- Retry/timeout/concurrency 의 declarative.
💻 패턴
ts-brand 기본
import type { Brand } from "ts-brand";
type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;
const makeUserId = (s: string): UserId => s as UserId;
function getUser(id: UserId) { /* ... */ }
const uid = makeUserId("u_123");
const oid = "o_456" as OrderId;
getUser(uid);
// getUser(oid); // 매 compile error — UserId 의 X
// getUser("u_123"); // 매 compile error — raw string
Validated brand (smart constructor)
type Email = Brand<string, "Email">;
function parseEmail(s: string): Email | null {
return /^[^@]+@[^@]+\.[^@]+$/.test(s) ? (s as Email) : null;
}
function sendMail(to: Email, subject: string) { /* ... */ }
const e = parseEmail(input);
if (e) sendMail(e, "hi"); // 매 type-narrowed 의 valid 의 only
Effect 기본
import { Effect, pipe } from "effect";
const fetchUser = (id: UserId): Effect.Effect<User, NetworkError | NotFoundError> =>
Effect.tryPromise({
try: () => fetch(`/api/users/${id}`).then((r) => {
if (r.status === 404) throw new NotFoundError(id);
return r.json();
}),
catch: (e) => e instanceof NotFoundError ? e : new NetworkError(e),
});
const program = pipe(
fetchUser(uid),
Effect.map((u) => u.email),
Effect.tap((email) => Effect.log(`Got: ${email}`)),
);
Effect.runPromise(program).then(console.log);
Effect.gen (do-notation)
import { Effect } from "effect";
const program = Effect.gen(function* () {
const user = yield* fetchUser(uid);
const orders = yield* fetchOrders(user.id);
const valid = orders.filter((o) => o.status === "paid");
return { user, orderCount: valid.length };
});
Typed retry / timeout
import { Effect, Schedule, Duration } from "effect";
const robust = pipe(
fetchUser(uid),
Effect.retry(Schedule.exponential(Duration.millis(100)).pipe(Schedule.compose(Schedule.recurs(3)))),
Effect.timeout(Duration.seconds(5)),
);
Layer / dependency injection
import { Context, Effect, Layer } from "effect";
class Database extends Context.Tag("Database")<Database, {
query: (sql: string) => Effect.Effect<unknown[], DBError>;
}>() {}
const DatabaseLive = Layer.succeed(Database, {
query: (sql) => Effect.tryPromise({ try: () => pool.query(sql), catch: (e) => new DBError(e) }),
});
const program = Effect.gen(function* () {
const db = yield* Database;
const rows = yield* db.query("SELECT * FROM users");
return rows;
});
Effect.runPromise(program.pipe(Effect.provide(DatabaseLive)));
Schema (Effect's Zod-equivalent)
import { Schema } from "effect";
const User = Schema.Struct({
id: Schema.String.pipe(Schema.brand("UserId")),
email: Schema.String.pipe(Schema.pattern(/^[^@]+@[^@]+$/), Schema.brand("Email")),
age: Schema.Number.pipe(Schema.int(), Schema.between(0, 150)),
});
type User = Schema.Schema.Type<typeof User>;
const decoded = Schema.decodeUnknownSync(User)(input);
의 Concurrency
import { Effect } from "effect";
const fetchAll = Effect.all(
[fetchUser(u1), fetchUser(u2), fetchUser(u3)],
{ concurrency: 2 },
);
의 Either (sync error)
import { Either } from "effect";
const safeParse = (s: string): Either.Either<number, "not_a_number"> => {
const n = Number(s);
return Number.isNaN(n) ? Either.left("not_a_number") : Either.right(n);
};
Effect 의 React (effect-rx)
import { useRxSuspense } from "@effect-rx/rx-react";
const userRx = Rx.make((get) => fetchUser(get(userIdRx)));
function UserProfile() {
const user = useRxSuspense(userRx);
return <div>{user.email}</div>;
}
매 결정 기준
| 상황 | Approach |
|---|---|
| Domain ID 의 의 mix-up 의 prevent | ts-brand 의만 |
| Validated value (email, URL) | ts-brand 의 + smart constructor |
| Complex async pipeline | Effect TS |
| Typed error + retry + timeout | Effect TS |
| DI 의 의 typed | Effect Layer |
| Simple fetch + try/catch | 매 plain async — Effect 의 X |
기본값: ts-brand 의 의 always, Effect 의 의 의 complex pipeline 의만.
🔗 Graph
- 부모: TypeScript · Functional Programming
- 변형: fp-ts
- 응용: Discriminated Unions for Error Handling · Dependency Injection
- Adjacent: Zod · Branded Types · Schema Validation
🤖 LLM 활용
언제: typed pipeline 의 design, domain ID safety, layer-based DI, schema-driven decode. 언제 X: 의 매 simple CRUD — 매 Effect 의 learning curve 의 의 not worth.
❌ 안티패턴
as UserId의 의 raw string 의 의 cast: 의 brand 의 의 bypass — 의 smart constructor 의 의 always.- Effect 의 의 entire codebase 의 의 force: 의 team 의 의 buy-in 의 X 의 시 의 — 매 friction.
Effect.runSync의 의 async 의: 의 throw 의 의 — runPromise 의 의 사용.- Mutable state 의 의 Effect 의: 의 referential transparency 의 의 violation — Ref/Layer 의 의 사용.
- Brand 의 의 nested 의 reuse:
Brand<Brand<string, "A">, "B">의 의 confusing — 매 single brand 의 keep.
🧪 검증 / 중복
- Verified (Effect-TS docs effect.website, ts-brand npm, ZIO inspiration, Effect Schema docs).
- 신뢰도 A.
🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — Effect.gen + Layer + Schema + ts-brand smart constructor 추가 |