NetWiv

빠른 시작

CLI를 설치하고, 앱을 만들고, 실행하고, 게시합니다. 이어서 서로 통신하는 가장 작은 C#과 프론트엔드 코드를 봅니다.

NetWiv는 아직 NuGet.org에 없습니다. GitHub 릴리스 페이지의 ZIP에 패키지가 들어 있고, 압축을 푼 폴더를 패키지 소스로 씁니다.

필요한 것

  • .NET 8 이상 SDK
  • Node.js ^20.19 또는 22.12 이상
  • 운영체제 WebView: WebView2, WebKitGTK 4.1, WKWebView

코딩 에이전트 연결

netwiv mcp는 프로젝트의 CLI로 읽기 전용 MCP 서버를 띄웁니다. 에이전트는 그 NetWiv 버전의 가이드와 프로젝트의 설정, 권한, 명령을 읽습니다.

에이전트에게 맡기기

이 프롬프트를 코딩 에이전트에 붙여 넣으세요. 빠진 것을 설치하고, 서버를 연결하고, Claude Code나 Codex라면 NetWiv 스킬 설치를 제안한 뒤 연결을 확인합니다.

직접 설정하기

사용하는 코딩 에이전트를 고르세요.

  1. NetWiv 마켓플레이스 추가

    /plugin marketplace add NetWiv/skills
  2. 스킬 설치

    /plugin install netwiv@netwiv
  3. 플러그인 다시 불러오기

    /reload-plugins

스킬은 NetWiv 버전과 관계없이 쓸 수 있고, 버전별 가이드는 프로젝트의 CLI가 제공합니다. netwiv new로 만든 프로젝트에는 .mcp.json이 이미 들어 있으니, dotnet tool restore를 한 번 실행하고 Claude Code가 처음 netwiv 서버를 불러올 때 승인하세요.

  1. NetWiv 마켓플레이스 추가

    codex plugin marketplace add NetWiv/skills
  2. 스킬 설치

    codex plugin add netwiv@netwiv
  3. 프로젝트 CLI 복원

    dotnet tool restore
  4. ~/.codex/config.toml에 서버 등록

    [mcp_servers.netwiv]
    command = "dotnet"
    args = ["tool", "run", "netwiv", "--", "mcp"]

Codex는 스킬을 모든 프로젝트에 설치합니다. dotnet tool run이 프로젝트의 CLI를 찾도록 프로젝트 폴더에서 Codex를 시작하세요. 다시 시작하면 스킬과 서버가 함께 불려 옵니다.

  1. 프로젝트 CLI 복원

    dotnet tool restore
  2. 프로젝트에 .cursor/mcp.json 추가

    {
      "mcpServers": {
        "netwiv": {
          "command": "dotnet",
          "args": ["tool", "run", "netwiv", "--", "mcp"]
        }
      }
    }

프로젝트의 .mcp.json과 같은 등록 정보입니다.

  1. 프로젝트 CLI 복원

    dotnet tool restore
  2. stdio 서버 등록

    command:   dotnet
    args:      tool run netwiv -- mcp
    transport: stdio

프로젝트 폴더에서 시작하세요. 프로젝트를 만들기 전에는 전역 설치한 CLI의 netwiv mcp가 가이드를 제공합니다.

  1. 패키지 받기

    릴리스 페이지에서 ZIP을 받아 압축을 풉니다. 아래 명령은 다운로드 폴더에 풀었다고 가정하니, 다른 곳이면 경로만 바꾸세요. 새 프로젝트가 이 폴더를 가리키므로 지우지 마세요.

    NetWiv-0.3.0-packages.zip 받기

    릴리스에는 ZIP을 검증할 SHA256SUMS도 있습니다.

  2. CLI 설치

    압축을 푼 폴더에서 실행합니다. $feed에 패키지 위치가 저장됩니다. 이미 설치했다면 install 대신 update를 씁니다.

    Set-Location $HOME\Downloads\NetWiv-0.3.0-packages
    $feed = (Resolve-Path .\packages).Path
    dotnet tool install --global NetWiv.Cli --version 0.3.0 --add-source $feed
    cd ~/Downloads/NetWiv-0.3.0-packages
    feed="$(pwd)/packages"
    dotnet tool install --global NetWiv.Cli --version 0.3.0 --add-source "$feed"
    export PATH="$HOME/.dotnet/tools:$PATH"
  3. 앱 만들기

    $feed가 남아 있도록 같은 터미널에서 실행합니다. 기본은 React이고, netwiv new --list로 스타터 6개(TypeScript의 React, Vue, Svelte, Angular와 Blazor WebAssembly, Blazor Hybrid)를 볼 수 있습니다.

    Set-Location $HOME    # 프로젝트를 둘 폴더면 어디든
    netwiv new MyNetWivApp --template react --source $feed
    Set-Location MyNetWivApp
    cd ~    # 프로젝트를 둘 폴더면 어디든
    netwiv new MyNetWivApp --template react --source "$feed"
    cd MyNetWivApp
  4. 실행

    npm ci가 프로젝트의 vendor 폴더에서 @netwiv/api를 설치합니다. desktop:dev는 바인딩을 만들고 Vite를 띄운 뒤 창을 엽니다. C#을 고치면 백엔드가 다시 시작됩니다.

    npm ci
    npm run desktop:dev
    npm ci
    npm run desktop:dev
  5. 게시

    기본은 self-contained .NET이고, --aot를 붙이면 NativeAOT입니다. 결과는 target/에 생깁니다.

    dotnet tool run netwiv -- build
    dotnet tool run netwiv -- build --aot
    dotnet tool run netwiv -- build
    dotnet tool run netwiv -- build --aot

실행하면 열리는 창

macOS에서 React 스타터로 한 번 호출한 모습입니다. 프론트엔드가 C# greet 명령을 호출했고, C#이 호출 횟수를 상태로 셌습니다.

NetWiv React 스타터 창: "Your frontend is ready. Call the C# backend." 제목, 이름 입력과 Call greet 버튼, "Hello, React!" 응답과 호출 횟수 1, 수정할 파일 목록.

가장 작은 앱

템플릿에서 줄였습니다. 실제 템플릿은 호출 상태 표시와 개발 서버 연결도 함께 합니다.

강조된 줄에 마우스를 올리면 짝이 되는 줄이 함께 표시됩니다.

src-netwiv/Program.cs
using System.Text.Json.Serialization;using NetWiv;using NetWiv.Platform.Desktop; [assembly: CommandJsonContext(typeof(AppJsonContext))][assembly: GenerateFrontendBindings] internal static class Program{    [STAThread]    private static void Main()    {        using var app = new Builder()            .UseRuntime(new DesktopRuntime())            .Metadata(new AppMetadata("My app", "0.1.0", "dev.example.myapp"))            .UseEmbeddedAssets()            .UseEmbeddedConfig(EmbeddedConfigMode.Production)            .InvokeHandler("greet")            .Window("main", "My app", "index.html", 900, 680)            .Build();        app.Run();    }} public static class Commands{    [Command]    public static string Greet(string name) => $"Hello, {name}!";} [JsonSerializable(typeof(string))]public partial class AppJsonContext : JsonSerializerContext;
src/App.tsx
import { useState } from 'react';import { commands } from './generated/commands.js'; export default function App() {  const [message, setMessage] = useState('');  async function greet() {    setMessage(await commands.greet({ name: 'World' }));  }  return <button onClick={greet}>{message || 'Greet'}</button>;}
src/App.vue
<script setup lang="ts">import { ref } from 'vue';import { commands } from './generated/commands.js'; const message = ref('');async function greet() {  message.value = await commands.greet({ name: 'World' });}</script> <template>  <button @click="greet">{{ message || 'Greet' }}</button></template>
src/App.svelte
<script lang="ts">  import { commands } from './generated/commands.js';   let message = $state('');  async function greet() {    message = await commands.greet({ name: 'World' });  }</script> <button onclick={greet}>{message || 'Greet'}</button>
src/app.ts
import { Component, signal } from '@angular/core';import { commands } from './generated/commands.js'; @Component({  selector: 'app-root',  template: `<button (click)="greet()">{{ message() || 'Greet' }}</button>`,})export class App {  message = signal('');  async greet() {    this.message.set(await commands.greet({ name: 'World' }));  }}
frontend/Pages/Home.razor
@page "/"@inject NetWiv.Generated.NetWivCommands Commands <button @onclick="Greet">@(Message ?? "Greet")</button> @code {    private string? Message;     private async Task Greet()    {        Message = await Commands.GreetAsync("World");    }}
Shared/App.razor
@inject NetWiv.Generated.NetWivCommands Commands <button @onclick="Greet">@(Message ?? "Greet")</button> @code {    private string? Message;     private async Task Greet()    {        Message = await Commands.GreetAsync("World");    }}

Blazor는 빌드할 때마다 명령에서 생성되는 타입 C# 클라이언트 NetWivCommands로 C#을 호출합니다. JSON 컨텍스트와 공유 타입은 Program.cs 대신 스타터의 contracts/ 프로젝트에 둡니다. 값은 JSON 텍스트로 JavaScript를 지나가므로 long과 decimal도 정확히 유지됩니다.

blazor-hybrid 스타터는 이 컴포넌트를 데스크톱 프로세스 안에서 managed .NET으로 실행하며, Windows, Linux, macOS에서 같은 생성 클라이언트로 C#을 호출합니다. 기본 대상은 .NET 8이며, NetWivBlazorTargetFramework를 net9.0이나 net10.0으로 바꾸면 앱과 짝이 맞는 Microsoft 컴포넌트 패키지가 함께 바뀝니다. 공유 UI는 WebAssembly로도 게시할 수 있습니다. Hybrid의 NativeAOT는 실험적이며 명시적으로 켜야 합니다.

CLI 한눈에

명령하는 일
netwiv new NAME -t TEMPLATEnetwiv new --list# 스타터 6개SDK 참조, 로컬 CLI, 피드를 갖춘 프로젝트 생성
netwiv dev바인딩, 개발 서버, 백엔드 재시작과 함께 실행
netwiv build# self-contained .NETnetwiv build --aot# NativeAOTnetwiv build --framework-dependent# 설치된 런타임netwiv build -t osx-arm64 -b app,dmg# 대상과 번들 형식앱 게시. 설정에서 켜면 번들도 생성
netwiv infoCLI·.NET SDK 버전, OS, 설정 보기
netwiv mcpdotnet tool run netwiv -- mcp# 프로젝트 CLI코딩 에이전트용 읽기 전용 MCP 서버를 stdio로 실행. 새 프로젝트의 .mcp.json은 두 번째 방식으로 등록함

다음