--- id: wiki-2026-0508-effect-ts-및-ts-brand-라이브러리-활용 title: Effect TS 및 ts-brand 라이브러리 활용 category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Effect-TS, ts-brand, Branded Types, Effect.gen] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [typescript, functional, effect-system, branded-types] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: Effect --- # Effect TS 및 ts-brand 라이브러리 활용 ## 매 한 줄 > **"매 typed effect system + nominal type 의 — TypeScript 의 의 ZIO/Cats 의 imported"**. Effect 의 의 async/error/dependency 의 의 single 의 `Effect` 의 의 unify, ts-brand 의 의 structural type 의 의 nominal flavor 의 의 add. 2026 의 매 enterprise TS codebase 의 의 fast 의 emerging. ## 매 핵심 ### 매 Effect 의 type - `Effect` — 의 success `A`, 의 failure `E`, 의 requirement `R`. - 매 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. ### 매 응용 1. Domain ID (UserId, OrderId 의 의 mix-up 의 prevent). 2. Validated value (Email, NonEmptyString). 3. Async pipeline 의 typed error. 4. Dependency injection (Effect Layer). 5. Retry/timeout/concurrency 의 declarative. ## 💻 패턴 ### ts-brand 기본 ```ts import type { Brand } from "ts-brand"; type UserId = Brand; type OrderId = Brand; 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) ```ts type Email = Brand; 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 기본 ```ts import { Effect, pipe } from "effect"; const fetchUser = (id: UserId): Effect.Effect => 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) ```ts 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 ```ts 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 ```ts import { Context, Effect, Layer } from "effect"; class Database extends Context.Tag("Database") Effect.Effect; }>() {} 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) ```ts 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; const decoded = Schema.decodeUnknownSync(User)(input); ``` ### 의 Concurrency ```ts import { Effect } from "effect"; const fetchAll = Effect.all( [fetchUser(u1), fetchUser(u2), fetchUser(u3)], { concurrency: 2 }, ); ``` ### 의 Either (sync error) ```ts import { Either } from "effect"; const safeParse = (s: string): Either.Either => { const n = Number(s); return Number.isNaN(n) ? Either.left("not_a_number") : Either.right(n); }; ``` ### Effect 의 React (effect-rx) ```tsx import { useRxSuspense } from "@effect-rx/rx-react"; const userRx = Rx.make((get) => fetchUser(get(userIdRx))); function UserProfile() { const user = useRxSuspense(userRx); return
{user.email}
; } ``` ## 매 결정 기준 | 상황 | 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]] · [[ZIO]] · [[neverthrow]] - 응용: [[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, "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 추가 |