나만의 업무 시스템 만들기

SetFN 워크스페이스에서 회사만의 그룹웨어, ERP 같은 시스템을 직접 만들 수 있습니다. 데이터베이스 구조 설계와 화면 제작은 AI에게 맡기고, 우리는 그 결과물을 만들어 워크스페이스에 올리기만 하면 됩니다. 만들어야 할 파일은 2개. 할 일은 업로드와 '배포' 버튼 클릭. '제조 현장의 재고 관리 프로그램'을 예시로 만들어보겠습니다.

AI 프롬프트 없이 완성된 파일을 바로 써보고 싶다면, 전체 샘플 ZIP 다운로드에서 스키마와 index.html, mock-db-upload.html, README를 한 번에 받을 수 있습니다.


1. 데이터베이스 생성을 위한 .sfn 파일 만들기

SetFN 워크스페이스에서는 .sfn이라는 파일로 DB를 만들 수 있습니다. Claude, GPT, Gemini 같은 AI에게 .sfn 파일의 예시를 복사해서 주고 "부품과 제품 재고 관리를 하고 싶어"처럼 요구사항을 설명하면, AI가 파일을 만들어 줍니다.

아래 내용을 복사한 후, 아래 부분의 요구사항 설계를 바꿔서 AI에게 전달해보세요.

너는 SetFN이라는 노코드 백엔드 플랫폼의 스키마 파일(.sfn)을 설계하는 엔지니어야.
.sfn은 아래 구조를 가진 JSON 파일이야. 

{
  "content-type": "application/setfn-schema",
  "dbType": "sqlite3",
  "apiBuild": {
    "dbName": "영문_소문자_밑줄_db이름",
    "dbEngine": "sqlite",
    "includeClient": false,
    "features": {
      "realtime": { "enabled": false },
      "upload": { "enabled": false },
      "ai-chat": { "enabled": false }
    }
  },
  "data": [
    {
      "table": { "name": "테이블명", "cmt": "이 테이블 설명" },
      "columns": [
        { "name": "id", "type": "serial4", "primary": true, "default": "auto_increment" },
        { "name": "컬럼명", "type": "text", "notnull": true, "cmt": "컬럼 설명" },
        { "name": "숫자컬럼", "type": "int4", "notnull": true, "default": "0" },
        { "name": "_created_at", "type": "timestamp", "default": "now()" }
      ],
      "permissions": {
        "security": { "login": true },
        "ops": {
          "list": { "login": true },
          "view": { "login": true },
          "create": { "login": true },
          "update": { "login": true },
          "delete": { "login": true, "roles": ["admin"] }
        }
      }
    }
  ]
}

컬럼 type은 serial4(자동증가 PK) / text / int4(정수) / timestamp만 사용해.
컬럼에는 notnull, default, unique, index, cmt를 필요에 맞게 붙여.

이제 이 형식으로, 제조업체가 쓸 "재고관리 시스템" DB를 설계해줘. 요구사항은 다음과 같아.

- 부품을 등록하고, 부품별 현재 재고 수량을 관리한다.
- 제품을 등록하고, 제품별 현재 재고 수량을 관리한다.
- 제품 하나는 여러 부품으로 조립되어 만들어진다. 어떤 제품에 어떤 부품이 몇 개씩 필요한지(BOM, 자재명세서)를 관리한다.
- 부품/제품의 입고·출고 이력을 남긴다. 특히 "부품을 조립해서 제품을 생산"하는 이벤트가 발생하면, 그 순간 사용된 부품들과 생산된 제품 수량이 하나의 사건으로 함께 추적되어야 한다(나중에 "이 제품을 만드는데 부품이 얼마나 들어갔는지" 조회할 수 있어야 함).

DB 이름(dbName)은 "inventory_management"로 해줘.
테이블 4개 이내로, 실무에서 바로 쓸 수 있을 만큼 실용적으로 설계해줘.
다른 설명 없이 완성된 .sfn JSON 코드만 출력해줘.

아래는 이렇게 만들어진 AI 응답의 예시입니다. 실제 결과는 AI마다, 실행할 때마다 조금씩 다를 수 있습니다 — 꼭 이 내용 그대로일 필요는 없고, 이렇게 비슷한 구조로만 나오면 됩니다. 다만 다른 설명 없이 {로 시작해서 }로 끝나는 형식이어야, 나올 결과를 그대로 에디터에 붙여넣을 수 있습니다.

{
  "content-type": "application/setfn-schema",
  "dbType": "sqlite3",
  "apiBuild": {
    "dbName": "inventory_management",
    "dbEngine": "sqlite",
    "includeClient": false,
    "features": {
      "realtime": { "enabled": false },
      "upload": { "enabled": false },
      "ai-chat": { "enabled": false }
    }
  },
  "data": [
    {
      "table": { "name": "parts", "cmt": "부품 마스터. 조회/등록/수정은 회원 누구나, 삭제는 관리자만" },
      "columns": [
        { "name": "id", "type": "serial4", "primary": true, "default": "auto_increment" },
        { "name": "part_code", "type": "text", "notnull": true, "unique": true, "cmt": "부품 코드" },
        { "name": "name", "type": "text", "notnull": true, "cmt": "부품명" },
        { "name": "spec", "type": "text", "cmt": "규격 (예: M2, 220V/50Hz). 같은 이름이라도 규격이 다르면 별도 part_code로 등록" },
        { "name": "category", "type": "text", "cmt": "부품 분류 (전자부품/기구부품/포장재 등)" },
        { "name": "unit", "type": "text", "notnull": true, "default": "EA", "cmt": "단위" },
        { "name": "unit_cost", "type": "int4", "notnull": true, "default": "0", "cmt": "단가" },
        { "name": "current_stock", "type": "int4", "notnull": true, "default": "0", "cmt": "현재고" },
        { "name": "safety_stock", "type": "int4", "notnull": true, "default": "0", "cmt": "안전재고(재주문점)" },
        { "name": "supplier", "type": "text", "cmt": "공급업체" },
        { "name": "_user_id", "type": "int4", "notnull": true, "index": true },
        { "name": "_nick", "type": "text", "cmt": "최종 등록/수정한 회원 표시 이름" },
        { "name": "_created_at", "type": "timestamp", "default": "now()" }
      ],
      "permissions": {
        "security": { "login": true, "ownerColumn": "_user_id" },
        "ops": {
          "list": { "login": true },
          "view": { "login": true },
          "create": { "login": true },
          "update": { "login": true },
          "delete": { "login": true, "roles": ["admin"] }
        }
      }
    },
    {
      "table": { "name": "products", "cmt": "제품 마스터. 조회/등록/수정은 회원 누구나, 삭제는 관리자만" },
      "columns": [
        { "name": "id", "type": "serial4", "primary": true, "default": "auto_increment" },
        { "name": "product_code", "type": "text", "notnull": true, "unique": true, "cmt": "제품 코드" },
        { "name": "name", "type": "text", "notnull": true, "cmt": "제품명" },
        { "name": "category", "type": "text", "cmt": "제품 분류" },
        { "name": "unit", "type": "text", "notnull": true, "default": "EA", "cmt": "단위" },
        { "name": "price", "type": "int4", "notnull": true, "default": "0", "cmt": "판매가" },
        { "name": "current_stock", "type": "int4", "notnull": true, "default": "0", "cmt": "현재고" },
        { "name": "_user_id", "type": "int4", "notnull": true, "index": true },
        { "name": "_nick", "type": "text", "cmt": "최종 등록/수정한 회원 표시 이름" },
        { "name": "_created_at", "type": "timestamp", "default": "now()" }
      ],
      "permissions": {
        "security": { "login": true, "ownerColumn": "_user_id" },
        "ops": {
          "list": { "login": true },
          "view": { "login": true },
          "create": { "login": true },
          "update": { "login": true },
          "delete": { "login": true, "roles": ["admin"] }
        }
      }
    },
    {
      "table": { "name": "bom", "cmt": "제품별 부품 구성표(단일 단계). bom_key(product_id:part_id)에 unique 제약을 걸어 같은 제품에 같은 부품이 중복 등록되지 않게 함" },
      "columns": [
        { "name": "id", "type": "serial4", "primary": true, "default": "auto_increment" },
        { "name": "product_id", "type": "int4", "notnull": true, "index": true },
        { "name": "part_id", "type": "int4", "notnull": true, "index": true },
        { "name": "qty_per_unit", "type": "int4", "notnull": true, "default": "1", "cmt": "제품 1개를 만드는 데 필요한 부품 수량" },
        { "name": "bom_key", "type": "text", "notnull": true, "unique": true, "cmt": "product_id:part_id 조합. 중복 등록 방지용" },
        { "name": "_user_id", "type": "int4", "notnull": true, "index": true },
        { "name": "_nick", "type": "text", "cmt": "등록한 회원 표시 이름" },
        { "name": "_created_at", "type": "timestamp", "default": "now()" }
      ],
      "permissions": {
        "security": { "login": true, "ownerColumn": "_user_id" },
        "ops": {
          "list": { "login": true },
          "view": { "login": true },
          "create": { "login": true },
          "update": { "login": true },
          "delete": { "login": true, "roles": ["admin"] }
        }
      }
    },
    {
      "table": { "name": "stock_transactions", "cmt": "입출고/조립 통합 이력. item_type=part|product, tx_type=입고|출고|조립사용|조립생산. 하나의 조립생산 이벤트는 같은 batch_id를 공유하는 조립사용(부품 소모) 여러 건 + 조립생산(제품 증가) 1건으로 구성되어, 제품에 어떤 부품이 얼마나 쓰였는지 추적할 수 있음" },
      "columns": [
        { "name": "id", "type": "serial4", "primary": true, "default": "auto_increment" },
        { "name": "item_type", "type": "text", "notnull": true, "cmt": "part | product" },
        { "name": "item_id", "type": "int4", "notnull": true, "index": true, "cmt": "parts.id 또는 products.id" },
        { "name": "item_name", "type": "text", "notnull": true, "cmt": "등록 시점 품목명 스냅샷" },
        { "name": "tx_type", "type": "text", "notnull": true, "cmt": "입고 | 출고 | 조립사용 | 조립생산" },
        { "name": "reason", "type": "text", "cmt": "입출고 사유. 조립사용/조립생산은 tx_type 자체가 사유이므로 비워둠" },
        { "name": "qty", "type": "int4", "notnull": true, "cmt": "항상 양수. 방향은 tx_type으로 판단" },
        { "name": "batch_id", "type": "text", "index": true, "cmt": "조립생산 이벤트 묶음 id. 단순 입출고는 비어 있음" },
        { "name": "note", "type": "text", "cmt": "메모" },
        { "name": "_user_id", "type": "int4", "notnull": true, "index": true },
        { "name": "_nick", "type": "text", "cmt": "등록한 회원 표시 이름" },
        { "name": "_created_at", "type": "timestamp", "default": "now()" }
      ],
      "permissions": {
        "security": { "login": true, "ownerColumn": "_user_id" },
        "ops": {
          "list": { "login": true },
          "view": { "login": true },
          "create": { "login": true },
          "update": { "login": true, "roles": ["admin"] },
          "delete": { "login": true, "roles": ["admin"] }
        }
      }
    }
  ]
}

이 결과를 복사해서, 워크스페이스에서 만들기 → 템플릿 파일 → 새 스키마(DB)로 빈 스키마 파일을 만듭니다.

만들기 > 템플릿 파일 > 새 스키마(DB) 선택 화면

파일 이름을 입력하고 저장하면 빈 스키마 파일이 만들어집니다.

새로 만든 빈 스키마 파일

오른쪽 위의 에디터 탭을 누른 후, 아까 AI의 응답을 그대로 붙여넣습니다.

스키마 에디터 화면

저장한 뒤 상단의 API빌드 탭으로 이동하면 테이블별로 자동 생성된 API 목록을 확인할 수 있습니다.

API 빌드 화면

배포하기를 누릅니다. 몇 초 정도 기다리면 '배포 완료' 메시지가 뜹니다. 마지막으로, 상단의 디스켓 버튼을 눌러 저장합니다. 여기까지 하면 DB 구성이 끝났습니다 — 부품/제품/구성표/입출고 이력 4개 테이블과, 그 테이블들을 다루는 REST API가 실제로 만들어져 바로 쓸 수 있는 상태가 됩니다. 이제는 여기 DB와 연결할 화면을 만들 차례입니다.

2. 화면 구성을 위한 .html 파일 만들기

이번에도 AI에게 요청합니다. 만들고 싶은 것을 기획하고 정리해 AI에게 전달합니다. 이때 아까 만든 .sfn 파일 내용도 참고 자료로 같이 주면 됩니다.

  • 부품·제품을 등록하는 입력 폼
  • 입고·출고를 등록하는 폼
  • "이 제품을 몇 개 생산했다"고 등록하면 필요한 부품 수량을 자동으로 계산해서 재고를 반영
  • 현재 재고 현황을 표로 보여주고, 표에서 바로 값을 고쳐서 저장

아래 내용을 이어서 AI 채팅에 붙여넣습니다. (맨 위에 1번에서 만든 결과를 함께 붙여넣습니다.)

[여기에 1번 결과를 붙여넣기]

위 스키마가 이미 배포되어 있어. 이 스키마를 사용하는 웹 화면(index.html) 하나를 만들어줘.

화면에서 데이터를 불러오고 저장할 때는 아래 규칙으로 서버에 요청해.

1. 로그인한 사용자의 API 토큰을 얻으려면:
   POST /api/auth/bootstrap  (credentials: "include")
   응답: { data: { access_token, user: { id, nick } } }

2. 이후 모든 요청은 이 헤더를 붙인다: Authorization: Bearer <access_token>

3. 테이블 CRUD:
   조회   GET  /api/web/{dbName}/{table}/list?컬럼=값   → 응답 { rows: [...] } (rows는 결과가 없으면 null일 수 있으니 항상 rows || [] 로 처리)
   생성   POST /api/web/{dbName}/{table}/create          → body: JSON, 응답 { lastInsertId }
   수정   PUT  /api/web/{dbName}/{table}/update/{id}     → body: JSON (바뀐 컬럼만 보내도 됨)
   삭제   DELETE /api/web/{dbName}/{table}/delete/{id}

화면에 필요한 기능은 다음과 같아.

- 부품과 제품을 신규 등록할 수 있는 입력 폼
- 부품/제품의 입고·출고를 등록할 수 있는 폼 (등록하면 현재 재고 수량도 같이 갱신)
- "이 제품을 몇 개 생산했다"를 등록하면, 등록된 구성표에 따라 필요한 부품 수량을 자동으로 계산해서 부품 재고를 차감하고 제품 재고를 늘리는 기능. 부품 재고가 모자라면 등록을 막고 알려줘.
- 현재 부품/제품 재고 현황을 표로 보여주는 화면. 표의 칸을 클릭하면 그 자리에서 바로 값을 수정하고 저장할 수 있게.
- 입고/출고/조립 이력을 시간순으로 보여주는 목록.

HTML/CSS/JS 한 파일에 전부 담아줘. 깔끔한 흰 배경에 카드형 레이아웃으로 만들어줘.
다른 설명 없이 완성된 index.html 코드만 출력해줘.

아래는 이렇게 만들어진 html 파일의 예시입니다. 실제 결과는 AI마다 조금씩 다를 수 있습니다.

index.html

이 파일을 워크스페이스에 올리면, 실제 팀원들이 쓸 수 있는 화면이 완성됩니다.

3. 테스트 데이터 채우기

실제로 동작하는 걸 눈으로 보려면 데이터가 있어야 합니다. 버튼 하나로 예시 데이터를 채워 넣는 화면도 AI에게 바로 만들어 달라고 할 수 있습니다.

[여기에 1번 결과를 붙여넣기]

위 스키마가 이미 배포되어 있고, 이 스키마를 쓰는 index.html도 이미 만들어져 있어.
버튼 하나를 누르면 테스트용 데이터를 자동으로 채워 넣는 화면(mock-db-upload.html)을 만들어줘.

데이터는 2번 화면과 같은 방식(POST /api/auth/bootstrap으로 토큰을 받고, POST /api/web/{dbName}/{table}/create로 생성)으로 실제 서버에 넣어줘.

- 부품 10~15개, 제품 3~5개, 각 제품의 부품 구성, 입출고 이력 몇 건을 실제로 만들어 넣어줘.
- 부품/제품 이름과 수량은 제조업 현장에서 실제로 쓸 법한 값으로 채워줘.
- 이미 같은 코드의 데이터가 있으면 그 항목만 건너뛰고 계속 진행해줘.
- 진행 상황을 화면에 한 줄씩 로그로 보여주고, 끝나면 "완료"를 표시해줘.
다른 설명 없이 완성된 mock-db-upload.html 코드만 출력해줘.

아래는 이렇게 만들어진 html 파일의 예시입니다. 실제 결과는 AI마다 조금씩 다를 수 있습니다. 이 화면은 배포하지 않고, 데모 준비가 필요할 때만 열어서 씁니다.

mock-db-upload.html

실제로 데이터를 넣고 써보면 이렇게 동작합니다.

부품 현황 — 칸을 더블클릭하면 바로 수정·저장된다

BOM 등록 — 오른쪽 부품 카드를 왼쪽으로 끌어다 놓으면 구성에 추가된다

조립 생산 등록 — 제품과 생산 수량을 정하면 필요한 부품 수량과 현재고가 자동으로 표시된다

거래 이력 — 배치 번호별로 조립생산·조립사용 내역이 함께 남는다

4. 반복해서 수정하기

한 번에 완성하지 않아도 됩니다. 화면을 써보면서 부족한 기능이나 고치고 싶은 부분이 생기면, AI에게 계속 이어서 요청하면 됩니다. 다만 무엇을 고치느냐에 따라 그 다음에 해야 할 일이 다릅니다.

  • DB 구조(테이블·컬럼)가 바뀌는 수정 — 예: 새 컬럼 추가, 새 테이블 추가 — 은 수정된 .sfn을 다시 받아서 에디터에 붙여넣고, API빌드에서 다시 배포해야 합니다.
  • DB 구조는 그대로 두고 화면만 고치는 수정 — 예: 디자인, 문구, 버튼 위치 — 은 index.html만 다시 받아서 기존 파일을 덮어쓰면 됩니다. API를 다시 배포할 필요는 없습니다.
  • 이미 데이터가 들어있는 테이블의 구조를 바꿀 때는, 기존 데이터를 유지한 채 반영할지 덮어쓰고 새로 시작할지를 배포 시점에 선택해야 합니다. 운영 중인 데이터가 있다면 신중하게 선택해야 합니다.

이 네 단계만으로 데이터베이스 설계, 실제 화면, 테스트 데이터, 그리고 그 이후의 수정까지 전부 AI가 만들어 줍니다. 이후에는 화면의 디자인이나 세부 항목을 원하는 대로 계속 다듬어 나가면 됩니다.