Skip to content

Repository files navigation

8G ์„œ๋น„์Šค์— ์˜ค์‹ ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค!!

How To Run

1. ํ•ด๋‹น Repo๋ฅผ pull ๋ฐ›์Šต๋‹ˆ๋‹ค.

git clone https://github.com/01Republic/8g.git

git pull

2. .env ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ์•„๋Ÿ ๊ฐ’์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š” (๊ธฐ์กด scodi-api์™€ ๋™์ผ)

DB_HOST=localhost
DB_PORT=3306
DB_USER=myuser
DB_PASSWORD=mypassword
DB_NAME=mydb

3. ๊ทธ๋ฆฌ๊ณ  migration๋ฅผ ๋”ฐ๋กœ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜ ์ฟผ๋ฆฌ ๋“ค๋กœ ํ•ด๋‹น ํŒจํ‚ค์ง€์— ํ•„์š”ํ•œ ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค

  • ์ด ๋ถ€๋ถ„์€ ์ฐจํ›„ ๋…ผ์˜ ํ•„์š” / ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์—ฌ๋ถ€ ๋“ฑ๋“ฑ
CREATE TABLE IF NOT EXISTS integration_app_form_metadata (
  id INT NOT NULL AUTO_INCREMENT,
  product_id INT NOT NULL,
  meta JSON NOT NULL,
  is_active BOOLEAN NOT NULL DEFAULT FALSE,
  PRIMARY KEY (id),
  UNIQUE KEY uq_integration_app_form_metadata_product_id (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

4. pnpm์„ ์‚ฌ์šฉํ•ด์„œ ์‹คํ–‰

pnpm run dev

๊ฐ€์ด๋“œ

1. ๋กœ๊ทธ์ธ {subdomain}.localhost:3000/login

์šฐ์„  ๊ธฐ๋ณธ์ ์œผ๋กœ 8g๋Š” scodi user & org ํ…Œ์ด๋ธ”์„ ๊ฐ™์ด ์”๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐœ์ธ ์„œ๋น„์Šค๋ผ๊ธฐ ๋ณด๋‹จ ์กฐ์ง ๋‹จ์œ„ ์„œ๋น„์Šค ์ด๊ธฐ ๋•Œ๋ฌธ์— slug๋ฅผ ํ™œ์šฉํ•ด์„œ subdomain url์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค! ๋”ฐ๋ผ์„œ org์˜ slug๋ฅผ ์•Œ์•„์•ผํ•ฉ๋‹ˆ๋‹ค!! ์ด ๋ถ€๋ถ„์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ organizations์˜ slug ์ปฌ๋Ÿผ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”!

  • (์ถ”๊ฐ€๋กœ slug๋Š” ํ˜„์žฌ ์Šค์ฝ”๋”” ๋‚ด์—์„œ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์–ด์„œ ๋””๋น„๋กœ ํ™•์ธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” {name}-{id} ๋ฐฉ์‹์œผ๋กœ ์ž๋™ ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๊ฑฐ๋กœ ํ•ด์ฃผ์…”๋„ ๋ฉ๋‹ˆ๋‹ค.)
  • ๊ทธ๋ฆฌ๊ณ  subdomain์€ ์˜์–ด๋กœ๋งŒ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค
  • ๋ฉ€ํ‹ฐ ํ…Œ๋„ŒํŠธ ๋ฐฉ์‹

๊ทธ๋Ÿฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋กœ๊ทธ์ธ ํ™”๋ฉด์ด ๋œน๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ํ™”๋ฉด

2. ํ™ˆ ํ™”๋ฉด {subdomain}.localhost:3000

๋กœ๊ทธ์ธ ํ›„์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™ˆ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋‚ด๊ฐ€ ๋“ฑ๋กํ•œ ์•ฑ(scordi api์—์„œ๋Š” subscription ๊ฐœ๋…)์„ ๋ณด์—ฌ์ฃผ๊ณ  ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์˜ ๋‚˜์˜ ๊ตฌ๋… ์•ฑ ์ชฝ์—์„œ ์นด๋“œ ํ˜•์‹์œผ๋กœ ๋ณด์—ฌ์ฃผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

  • ์ถ”๊ฐ€๋กœ ํ˜„์žฌ ์นด๋“œ์— product ์ด๋ฆ„๊ณผ ์•„์ด์ฝ˜ ๋“ฑ์„ ๋ณด์—ฌ์ฃผ๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ ๋ณด์—ฌ์ฃผ๋ฉด ์–ด๋–ค ๊ตฌ๋…์ธ์ง€ ์•Œ ์ˆ˜ ์—†์–ด์„œ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด ํ•„์š”

ํ™ˆ ํ™”๋ฉด

3. ์•ฑ ํ™”๋ฉด {subdomain}.localhost:3000/apps

์ขŒ์ธก์— apps ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ด๋™ํ•˜๋Š” ํŽ˜์ด์ง€๋กœ ๋‚ด๊ฐ€ ๋“ฑ๋กํ•œ ์•ฑ(subscription) ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ํด๋ฆญํ•˜๋ฉด ๋ชจ๋‹ฌ์ด๋‚˜ ์ƒˆ ํƒญ์œผ๋กœ ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ค˜์•ผํ•˜๋Š”๋ฐ ์•„์ง ์–ด๋–ค ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ค˜์•ผํ•  ์ง€ ์ •ํ•ด์ง€์ง€ ์•Š์•„์„œ ๋”ฐ๋กœ ๊ตฌํ˜„์ด ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์•ฑ ํ™”๋ฉด

4. ์—ฐ๋™ ํ™”๋ฉด {subdomain}.localhost:3000/integration

์™ผ์ชฝ ์ƒ๋‹จ์˜ + integration apps ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ๋‚˜์˜ค๋Š” ํ™”๋ฉด์œผ๋กœ ์‹ค์ œ ์—ฐ๋™์ด ๊ฐ€๋Šฅํ•œ product๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๋‚˜์˜ค๋Š” product๋Š” scordi์˜ product table์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ฑฐ๊ณ  ์—ฌ๊ธฐ ํ”„๋กœ์ ํŠธ์—์„œ ์ƒˆ๋กœ ๋งŒ๋“  ํ…Œ์ด๋ธ” integration_app_form_metadata ์—์„œ is_active ๊ฐ€ true์ธ ๊ฒƒ๋“ค์„ ๊ฐ€์ ธ์™€์„œ ๊ฑฐ๊ธฐ์— ์žˆ๋Š” product id ๋“ค๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค

์—ฐ๋™ ํ™”๋ฉด

๊ทธ๋ฆฌ๊ณ  Connect ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด integration_app_form_metadata์— ์žˆ๋Š” metadata๋กœ ํผ ๋ชจ๋‹ฌ์„ ๋„์›Œ์„œ ์—ฐ๋™์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์—ฐ๋™ ๋ชจ๋‹ฌ ํ™”๋ฉด

5. ํผ ๋นŒ๋” ํ™”๋ฉด {subdomain}.localhost:3000/apps/{product_id}/form-builder

ํผ ๋นŒ๋” ํ™”๋ฉด

Connect ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ๋‚˜์˜ค๋Š” ํผ ๋ชจ๋‹ฌ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€ ์ž…๋‹ˆ๋‹ค. ๊ฐ ๋ชจ๋‹ฌ์˜ ์ˆœ์„œ ๋ฐ ํ™”๋ฉด์„ ์„น์…˜์ด๋ผ ์ •์˜ํ–ˆ๊ณ , ์„น์…˜์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๋งˆ๋‹ค ์Šคํ…์ด ์ถ”๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

๊ฐ ์„น์…˜์˜ ํƒ€์ดํ‹€๊ณผ ์—ฌ๋Ÿฌ ๊ฐ’์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ณ , workflow๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์•„์ง ์›Œํฌํ”Œ๋กœ์šฐ ์ž…๋ ฅ ์ชฝ์€ ๊ตฌํ˜„์ด ๋œ ๋˜์–ด ์žˆ์–ด์„œ ์•„์ง ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค! ์ฐจํ›„ workflow ํŽ˜์ด์ง€๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ตฌ์ถ•ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ์‹ค์ œ integration apps์—์„œ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด์„  ์šฐ์ธก ์ƒ๋‹จ์˜ Active ํ† ๊ธ€์„ ํ‚จ ํ›„ ์ €์žฅ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด์ฃผ์‹œ๋ฉด ์‹ค์ œ๋กœ ๋ฐ˜์˜์ด ๋˜์„œ ๋ณด์ž…๋‹ˆ๋‹ค.

6. ์›Œํฌํ”Œ๋กœ์šฐ ํ™”๋ฉด

์›Œํฌํ”Œ๋กœ์šฐ ํ™”๋ฉด

์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์šฐ๋กœ ์šฐ์ธก ์ƒ๋‹จ์˜ add block์„ ํด๋ฆญํ•˜๋ฉด ์‹ค์ œ drag and drop ๋ฐฉ์‹์œผ๋กœ ๋…ธ๋“œ ๋ผ๋ฆฌ ์—ฐ๊ฒฐํ•ด์„œ ์‹ค์ œ ํฌ๋กค๋ง์„ ํ•  ์ˆ˜ ์žˆ๋Š” ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Image

์‹ค์ œ Run workflow์„ ํด๋ฆญํ•˜๋ฉด ์‹คํ–‰์ด ๋˜๋ฉฐ ์ƒ๋‹จ์— ์žˆ๋Š” url์ด ํƒ€๊ฒŸ url์ด ๋˜์„œ ๋™์ž‘ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

์ „์ฒด ๊ตฌ์กฐ ํ•œ๋ˆˆ์—

.
โ”œโ”€ app/                      # ์•ฑ ์†Œ์Šค (Remix/React)
โ”‚  โ”œโ”€ assets/                # ์ •์  ๋ฆฌ์†Œ์Šค ํ—ฌํผ
โ”‚  โ”œโ”€ client/                # ๋ผ์šฐํŠธ๋ณ„ ํด๋ผ์ด์–ธํŠธ ๋ทฐ ๋ ˆ์ด์–ด
โ”‚  โ”‚  โ”œโ”€ admin/              # ํผ/์›Œํฌํ”Œ๋กœ์šฐ ๋นŒ๋” ๋“ฑ ๊ด€๋ฆฌ ๋„๊ตฌ
โ”‚  โ”‚  โ”œโ”€ private/            # ๋กœ๊ทธ์ธ ์ดํ›„ ํ™”๋ฉด(Home, Apps, Integration ๋“ฑ)
โ”‚  โ”‚  โ””โ”€ public/             # ๋กœ๊ทธ์ธ ์ด์ „ ํ™”๋ฉด(Login ๋“ฑ)
โ”‚  โ”œโ”€ components/            # ๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ(shadcn ๊ธฐ๋ฐ˜ UI ํฌํ•จ)
โ”‚  โ”œโ”€ hooks/                 # ์ปค์Šคํ…€ ํ›…
โ”‚  โ”œโ”€ layouts/               # ๋ ˆ์ด์•„์›ƒ ์ปดํฌ๋„ŒํŠธ
โ”‚  โ”œโ”€ lib/                   # ์œ ํ‹ธ๋ฆฌํ‹ฐ
โ”‚  โ”œโ”€ middleware/            # ํด๋ผ์ด์–ธํŠธ/๋ผ์šฐํŠธ ๋ฏธ๋“ค์›จ์–ด(auth ๋“ฑ)
โ”‚  โ”œโ”€ models/                # ๋„๋ฉ”์ธ ๋กœ์ง(Integration/Workflow ๋“ฑ)
โ”‚  โ”œโ”€ routes/                # Remix ๋ผ์šฐํŠธ ์—”๋“œํฌ์ธํŠธ
โ”‚  โ”œโ”€ root.tsx               # ์•ฑ ์—”ํŠธ๋ฆฌ
โ”‚  โ”œโ”€ routes.ts              # React Router ๋ผ์šฐํŠธ ์„ค์ • (ํŒŒ์ผ ๊ธฐ๋ฐ˜ โ†’ Config ๊ธฐ๋ฐ˜ ์ „ํ™˜)
โ”‚  โ””โ”€ session.ts             # ์„ธ์…˜ ์œ ํ‹ธ
โ”œโ”€ public/                   # ์ •์  ํŒŒ์ผ ๋ฃจํŠธ(ํŒŒ๋น„์ฝ˜ ๋“ฑ)
โ”œโ”€ build/                    # ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ
โ”œโ”€ components.json           # shadcn/ui ๊ตฌ์„ฑ
โ”œโ”€ vite.config.ts            # Vite ์„ค์ •
โ””โ”€ tsconfig.json             # TS ์„ค์ •

๋ผ์šฐํŒ… & ํŽ˜์ด์ง€

  • ๋ผ์šฐํŠธ ์—”ํŠธ๋ฆฌ๋Š” app/routes.ts์—์„œ ๋ ˆ์ด์•„์›ƒ ๊ธฐ๋ฐ˜์œผ๋กœ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ๊ณตํ†ต ๋ ˆ์ด์•„์›ƒ(layouts/sidebar, layouts/common)์— ๋”ฐ๋ผ child ๋ผ์šฐํŠธ๊ฐ€ ๋ฌถ์ž…๋‹ˆ๋‹ค.
  • ๋ผ์šฐํŠธ ๊ตฌํ˜„์ฒด๋Š” app/routes/*.tsx์— ์œ„์น˜ํ•˜๋ฉฐ, ์—ฌ๊ธฐ์„œ ๋ฉ”ํƒ€/loader/action ๋“ฑ์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์ „์šฉ ํŽ˜์ด์ง€๋Š” app/client์— ๋ถ„๋ฆฌํ•ด ๋‘ก๋‹ˆ๋‹ค. Remix ๋ผ์šฐํŠธ ํŒŒ์ผ์—์„œ๋Š” ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์™€ ๊ตฌ์„ฑ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋กœ๊ทธ์ธ ์ดํ›„ ํ™”๋ฉด์€ app/client/private, ๋กœ๊ทธ์ธ ์ด์ „ ํ™”๋ฉด์€ app/client/public, ๋นŒ๋”/๊ด€๋ฆฌ ํ™”๋ฉด์€ app/client/admin์— ๋‚˜๋ˆ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ํ•จ์ˆ˜(loader/action)

  • loader: ์„œ๋ฒ„์—์„œ๋งŒ ์‹คํ–‰๋˜๋Š” ๋ฐ์ดํ„ฐ ํŒจ์นญ. ๋ฐฉ๋ฌธ ์‹œ ํ•„์š”ํ•œ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ์๋‹ˆ๋‹ค. ์˜ˆ) integration.tsx์—์„œ ์—ฐ๋™ ๊ฐ€๋Šฅํ•œ product ๋ฆฌ์ŠคํŠธ/๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋กœ๋”ฉ.
  • action: ํผ ์ œ์ถœ/๋ฎคํ…Œ์ด์…˜ ์ฒ˜๋ฆฌ. ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๋ฉฐ DB ์—ฐ๋™ ๋“ฑ ๋ถ€์ˆ˜ํšจ๊ณผ๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ) home.tsx์—์„œ ๊ฒ€์ƒ‰ ํผ ์ œ์ถœ ํ›„ ๊ฒฐ๊ณผ ๋ฆฌ์ŠคํŠธ ์‘๋‹ต.
  • clientLoader / clientAction: React Router 7์˜ ํด๋ผ์ด์–ธํŠธ ์ „์šฉ ๋ฐ์ดํ„ฐ API. ์„œ๋ฒ„ round-trip ์—†์ด ๋ธŒ๋ผ์šฐ์ € ์ƒํƒœ๋งŒ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ž‘์—…์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค(์ถ”ํ›„ ์บ์‹ฑ์ด๋‚˜ purely client-side ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ๋„์ž… ์˜ˆ์ •).
// app/routes/integration.tsx
export async function loader({}: Route.LoaderArgs) {
  return await findActiveIntegrationProducts();
}

// app/routes/home.tsx
export async function action({ request, context }: Route.ActionArgs) {
  const user = context.get(userContext);
  const formData = await request.formData();
  const query = formData.get("query")?.toString();
  const apps = await findAllApp({ query, orgId: user!.orgId });
  return { apps };
}

๋„๋ฉ”์ธ ๋กœ์ง(models)

  • app/models/integration์— ์—ฐ๋™ ๊ด€๋ จ ํƒ€์ž…/์‹คํ–‰๊ธฐ/์Šฌ๋ž™ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ํผ ๋ฉ”ํƒ€(integration_app_form_metadata)๋ฅผ ์ฝ์–ด UI ๋ชจ๋‹ฌ์„ ๊ตฌ์„ฑํ•˜๋Š” ํ๋ฆ„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
  • app/models/workflow์—๋Š” ์›Œํฌํ”Œ๋กœ์šฐ UI ๋…ธ๋“œ ์ปดํฌ๋„ŒํŠธ ๋ฐ ์‹คํ–‰ ๊ด€๋ จ ๋ฒ ์ด์Šค ๋กœ์ง์ด ์ •๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

UI ์ปดํฌ๋„ŒํŠธ

  • ๊ธฐ๋ณธ UI๋Š” app/components/ui์— shadcn/ui๋ฅผ ๋ฒ ์ด์Šค๋กœ ์ปค์Šคํ…€ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•œ ํ•œ ๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ๋กœ ๋Œ์–ด์˜ฌ๋ ค ์ค‘๋ณต์„ ์ค„์ž…๋‹ˆ๋‹ค.
  • ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ณตํ•ฉ ์ปดํฌ๋„ŒํŠธ(์˜ˆ: Reorderable, stepper)๋Š” app/components ๋ฃจํŠธ ํ•˜์œ„์— ์žˆ์Šต๋‹ˆ๋‹ค.

์ธ์ฆ/์„ธ์…˜

  • ์„ธ์…˜ ๊ด€๋ จ ๋กœ์ง์€ app/session.ts์— ๋ชจ์•„๋‘๊ณ , ๊ฒฝ๋กœ ๋ณดํ˜ธ๋Š” app/middleware/auth.ts๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

DB/๋ฉ”ํƒ€๋ฐ์ดํ„ฐ

  • ํ˜„์žฌ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋„๊ตฌ๋Š” ๋ถ™์—ฌ๋‘์ง€ ์•Š์•˜๊ณ , ํ•„์ˆ˜ ํ…Œ์ด๋ธ” integration_app_form_metadata๋Š” README์˜ SQL๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ํ•ด๋‹น ํ…Œ์ด๋ธ”์˜ meta(JSON) ๊ธฐ๋ฐ˜์œผ๋กœ Integration ๋ชจ๋‹ฌ/์„น์…˜ ๊ตฌ์„ฑ์ด ๋™์ ์œผ๋กœ ์ด๋ค„์ง‘๋‹ˆ๋‹ค.

EightGClient SDK ์‚ฌ์šฉ ๊ฐ€์ด๋“œ

์„ค์น˜

pnpm add scordi-extension
# or
npm install scordi-extension

๊ธฐ๋ณธ ์ดˆ๊ธฐํ™”

import { EightGClient } from "scordi-extension";

const client = new EightGClient();
await client.checkExtension();

๊ฐ„๋‹จ ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํ–‰ ์˜ˆ์‹œ

const workflow = {
  version: "1.0",
  start: "collectProduct",
  steps: [
    {
      id: "collectProduct",
      block: {
        name: "get-text",
        selector: ".product-title",
        findBy: "cssSelector",
        useTextContent: true,
        option: {},
      },
      switch: [
        {
          when: {
            equals: {
              left: "$.steps.collectProduct.result.data",
              right: "ํ’ˆ์ ˆ",
            },
          },
          next: "handleSoldOut",
        },
      ],
      next: "goToDetail",
    },
    {
      id: "goToDetail",
      block: {
        name: "event-click",
        selector: ".product-title",
        findBy: "cssSelector",
        option: { waitForSelector: true },
      },
      delayAfterMs: 500,
      next: "readDetail",
    },
    {
      id: "handleSoldOut",
      block: {
        name: "event-click",
        selector: ".notify-me",
        findBy: "cssSelector",
        option: { waitForSelector: true },
      },
      next: "readDetail",
    },
    {
      id: "readDetail",
      block: {
        name: "get-text",
        selector: ".product-detail",
        findBy: "cssSelector",
        useTextContent: true,
        option: { waitForSelector: true },
      },
    },
  ],
};

const result = await client.collectWorkflow({
  targetUrl: location.href,
  workflow,
  closeTabAfterCollection: true,
});

console.log(result.steps.readDetail.result.data);

์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ ์š”์•ฝ

  • checkExtension(): ๋ธŒ๋ผ์šฐ์ € ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ
  • collectData({ targetUrl, block, blockDelay }): ๋‹จ์ผ ๋ธ”๋ก ๋˜๋Š” ๋ธ”๋ก ๋ฐฐ์—ด ์‹คํ–‰
  • collectWorkflow({ targetUrl, workflow, closeTabAfterCollection, activateTab }): ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํ–‰

์›Œํฌํ”Œ๋กœ์šฐ JSON ๊ตฌ์กฐ ๋ฐ ๊ณ ๊ธ‰ ์‚ฌ์šฉ๋ฒ•์€ WORKFLOW_EXECUTION_ARCHITECTURE.md๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages