Skip to content

Latest commit

Β 

History

129 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

KiOrder

Next.js React TypeScript NestJS Prisma Supabase Tailwind CSS

ν…Œμ΄λΈ”μ˜€λ” Β· ν‚€μ˜€μŠ€ν¬ μ›¨μ΄νŒ… Β· μ£Όλ°© Β· 홀 Β· 포슀λ₯Ό ν•˜λ‚˜λ‘œ 묢은 λ§€μž₯ 운영 SaaS

μ†λ‹˜μ΄ 주문을 λ„£λŠ” μˆœκ°„λΆ€ν„° μ£Όλ°©Β·ν™€Β·ν¬μŠ€κΉŒμ§€, λ§€μž₯ μ•ˆμ˜ λͺ¨λ“  화면이 같은 μƒνƒœλ₯Ό λ΄…λ‹ˆλ‹€.

ν…Œμ΄λΈ”μ˜€λ” 주문이 μ£Όλ°© 화면에 μ‹€μ‹œκ°„ λ°˜μ˜λ˜λŠ” 데λͺ¨

κ°œμš” β€’ 데λͺ¨ β€’ μ•„ν‚€ν…μ²˜ β€’ μ£Όμš” κΈ°λŠ₯ β€’ 섀계 포인트 β€’ μ‹œμž‘ν•˜κΈ°

κ°œμš”

식당 ν•œ κ³³μ—μ„œ λ²Œμ–΄μ§€λŠ” 일은 생각보닀 μ—¬λŸ¬ 화면에 걸쳐 μžˆμŠ΅λ‹ˆλ‹€. μž…κ΅¬μ—μ„œλŠ” μ›¨μ΄νŒ…μ„ λ°›κ³ , ν…Œμ΄λΈ”μ—μ„œλŠ” 주문이 λ“€μ–΄μ˜€κ³ , 주방은 κ·Έ 주문을 쑰리 μˆœμ„œλ‘œ 봐야 ν•˜κ³ , 홀은 μ„œλΉ™ μ—¬λΆ€λ₯Ό, ν¬μŠ€λŠ” 결제λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€. 보톡 이 화면듀은 μ„œλ‘œ λ‹€λ₯Έ μ œν’ˆμ΄κ³ , κ·Έλž˜μ„œ μƒνƒœκ°€ μ–΄κΈ‹λ‚©λ‹ˆλ‹€.

KiOrderλŠ” 이 흐름 전체λ₯Ό ν•˜λ‚˜μ˜ 데이터 μ†ŒμŠ€ μœ„μ— μ˜¬λ¦½λ‹ˆλ‹€. μ£Όλ¬Έ ν•˜λ‚˜κ°€ 생기면 μ£Όλ°©Β·ν™€Β·ν¬μŠ€κ°€ 폴링 없이 λ™μ‹œμ— κ·Έ 사싀을 μ••λ‹ˆλ‹€. μ›¨μ΄νŒ… 등둝도 λ§ˆμ°¬κ°€μ§€λ‘œ, ν‚€μ˜€μŠ€ν¬μ—μ„œ μ†λ‹˜μ΄ 번호λ₯Ό λ°›λŠ” μˆœκ°„ 사μž₯λ‹˜ 관리 화면에 λ‚˜νƒ€λ‚©λ‹ˆλ‹€.

포트폴리였 ν”„λ‘œμ νŠΈλ‘œ 6개 μ‚¬μš©μž μœ ν˜•(μ†λ‹˜ Β· μ£Όλ°© Β· 홀 Β· 포슀 Β· 사μž₯λ‹˜ Β· μ‹œμŠ€ν…œ κ΄€λ¦¬μž)의 화면을 μ‹€μ œλ‘œ κ΅¬ν˜„ν–ˆμŠ΅λ‹ˆλ‹€.

Tip

λ°”λ‘œ λ‘˜λŸ¬λ³΄κΈ° β€” https://kiorder.vercel.app λžœλ”© νŽ˜μ΄μ§€μ˜ ν™”λ©΄ μΉ΄λ“œλ₯Ό λˆ„λ₯΄λ©΄ 둜그인 없이 ν•΄λ‹Ή ν™”λ©΄μœΌλ‘œ λ“€μ–΄κ°‘λ‹ˆλ‹€. 직접 λ‘œκ·ΈμΈν•˜λ €λ©΄ owner1@test.com / test1234 λ₯Ό μ“°μ„Έμš”.

개발 κΈ°κ°„ 2026.04.25 ~ 2026.08.08 (μ•½ 3.5κ°œμ›”)
νŽ˜μ΄μ§€ 라우트 17개 (λ§€μž₯ 운영 ν™”λ©΄ 14개)
REST μ—”λ“œν¬μΈνŠΈ 22개
DB ν…Œμ΄λΈ” 7개
λ¨Έμ§€λœ PR 20건

데λͺ¨

ν…Œμ΄λΈ”μ˜€λ” μ£Όλ¬Έ β†’ μ£Όλ°© μ‹€μ‹œκ°„ 반영

μ†λ‹˜μ΄ ν…Œμ΄λΈ”μ˜€λ”λ‘œ 주문을 λ„£μœΌλ©΄ μ£Όλ°© μΉΈλ°˜μ— μ¦‰μ‹œ μΉ΄λ“œκ°€ μƒκΉλ‹ˆλ‹€. 폴링이 μ•„λ‹ˆλΌ Supabase Realtime의 postgres_changes κ΅¬λ…μž…λ‹ˆλ‹€.

ν…Œμ΄λΈ”μ˜€λ” β†’ μ£Όλ°© μ‹€μ‹œκ°„ 반영

ν‚€μ˜€μŠ€ν¬ μ›¨μ΄νŒ… ν’€ν”Œλ‘œμš°

μ›¨μ΄νŒ… 등둝 β†’ QR λŒ€κΈ°ν‘œ λ°œκΈ‰ β†’ μ†λ‹˜μ΄ QR둜 λŒ€κΈ°ν˜„ν™© 확인 β†’ 사μž₯λ‹˜ 호좜 β†’ μ†λ‹˜ μ‘λ‹΅κΉŒμ§€, μ „ 과정이 λŠκΈ°μ§€ μ•Šκ³  μ΄μ–΄μ§‘λ‹ˆλ‹€.

ν‚€μ˜€μŠ€ν¬ μ›¨μ΄νŒ… ν’€ν”Œλ‘œμš°

λͺ¨λ°”일 λŒ€μ‘ (before / after)

ν…Œμ΄λΈ”μ˜€λ”λŠ” μ†λ‹˜ 개인 νœ΄λŒ€ν°μ—μ„œλ„ μ—΄λ¦½λ‹ˆλ‹€. 3μ—΄ κ³ μ • λ ˆμ΄μ•„μ›ƒμ΄ 390pxμ—μ„œ λ¬΄λ„ˆμ§€λ˜ 것을 μ„Έλ‘œ 적측으둜 λ°”κΎΈκ³ , μž₯λ°”κ΅¬λ‹ˆλŠ” ν•˜λ‹¨ λ°” + μ‹œνŠΈλ‘œ λΆ„λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.

Before After
before after

μ•„ν‚€ν…μ²˜

두 갈래의 μ†λ‹˜ μž…λ ₯이 ν•˜λ‚˜μ˜ NestJS API둜 λͺ¨μ΄κ³ , DB 변경이 Realtime으둜 λ§€μž₯ 운영 화면에 νΌμ§‘λ‹ˆλ‹€. NestJSλŠ” 순수 REST만 λ‹΄λ‹Ήν•˜κ³  μ‹€μ‹œκ°„ μ „νŒŒλŠ” Supabaseκ°€ λ§‘λŠ” κ΅¬μ‘°μž…λ‹ˆλ‹€.

[μ†λ‹˜ β€” ν‚€μ˜€μŠ€ν¬]        [μ†λ‹˜ β€” ν…Œμ΄λΈ”μ˜€λ”]
       β”‚                        β”‚
  POST /waiting            POST /orders
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β–Ό
              NestJS API  (REST Β· JWT Β· class-validator)
                   β”‚
              Prisma 7  (@prisma/adapter-pg)
                   β”‚
           Supabase PostgreSQL
                   β”‚
        Supabase Realtime (CDC)
                   β”‚
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β–Ό             β–Ό             β–Ό              β–Ό
 [μ£Όλ°© 칸반]   [홀 μ£Όλ¬Έ]     [포슀 결제]   [μ˜€λ„ˆ μ›¨μ΄νŒ…]

인증 흐름

둜그인 β†’ NestJSκ°€ HttpOnly μΏ ν‚€(access_token) λ°œκΈ‰
                  β”‚
                  β–Ό
   Next.js proxy.ts β€” jose jwtVerify 둜 μ„œλͺ… 검증
                  β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό                   β–Ό
   SYSTEM_ADMIN         STORE_OWNER
   /system-admin/*      /owner/* Β· /kitchen Β· /hall Β· /pos Β· /kiosk
  • XSS λ°©μ–΄ β€” HttpOnly라 JSμ—μ„œ 쿠킀에 μ ‘κ·Όν•  수 μ—†μŠ΅λ‹ˆλ‹€
  • CSRF λ°©μ–΄ β€” SameSite=lax
  • μ„œλͺ… 검증 β€” λ‹¨μˆœ 디코딩이 μ•„λ‹ˆλΌ jose jwtVerify

Note

DB의 Role enum은 SYSTEM_ADMIN, STORE_OWNER 2μ’…μž…λ‹ˆλ‹€. μœ„μ—μ„œ λ§ν•œ "6개 μ‚¬μš©μž μœ ν˜•"은 ν™”λ©΄ 기쀀이며, μ†λ‹˜Β·μ£Όλ°©Β·ν™€Β·ν¬μŠ€ 화면은 둜그인 계정이 μ•„λ‹ˆλΌ 경둜둜 κ΅¬λΆ„ν•©λ‹ˆλ‹€.

μ£Όμš” κΈ°λŠ₯

μ‚¬μš©μž μœ ν˜• 경둜 ν•˜λŠ” 일
μ†λ‹˜ β€” ν‚€μ˜€μŠ€ν¬ /kiosk/waiting μ›¨μ΄νŒ… 등둝, QR λŒ€κΈ°ν‘œ 수령
μ†λ‹˜ β€” λŒ€κΈ°ν˜„ν™© /waiting/[waitingId] λ‚΄ 순번 확인, 사μž₯λ‹˜ ν˜ΈμΆœμ— 응닡
μ†λ‹˜ β€” ν…Œμ΄λΈ”μ˜€λ” /table-order/[tableId]/menu 메뉴 쑰회, μž₯λ°”κ΅¬λ‹ˆ, μ£Όλ¬Έ 제좜
μ£Όλ°© /kitchen/orders μ£Όλ¬Έ μˆ˜μ‹ , 쑰리 μƒνƒœ μ „ν™˜
홀 /hall/orders μ£Όλ¬Έ 확인 및 μ„œλΉ™ 처리
포슀 /pos ν…Œμ΄λΈ”λ³„ μ£Όλ¬Έ 집계, 결제
사μž₯λ‹˜ /owner/dashboard 메뉴 Β· μ›¨μ΄νŒ… Β· ν…Œμ΄λΈ” μ„€μ •
μ‹œμŠ€ν…œ κ΄€λ¦¬μž /system-admin/stores λ§€μž₯ λͺ©λ‘, ꡬ독 승인/ν•΄μ œ

ν‚€μ˜€μŠ€ν¬ μ›¨μ΄νŒ…

ν„°μΉ˜ ν‚€νŒ¨λ“œλ‘œ μ „ν™”λ²ˆν˜Έλ₯Ό μž…λ ₯ν•˜κ³  μΈμ›μˆ˜λ₯Ό κ³ λ₯΄λ©΄ POST /waiting 으둜 λ“±λ‘λ©λ‹ˆλ‹€. μ™„λ£Œ 화면은 νƒœλΈ”λ¦Ώ κ°€λ‘œ κΈ°μ€€ 2λΆ„ν• (λŒ€κΈ°λ²ˆν˜Έ + QR)이고, QR을 찍으면 μ†λ‹˜ μ „μš© λŒ€κΈ°ν˜„ν™© νŽ˜μ΄μ§€λ‘œ λ„˜μ–΄κ°‘λ‹ˆλ‹€.

유휴 μƒνƒœκ°€ 이어지면 κ΄‘κ³  ν™”λ©΄λ³΄ν˜ΈκΈ°κ°€ λœΉλ‹ˆλ‹€ β€” μ‹€μ œ λ§€μž₯ ν‚€μ˜€μŠ€ν¬κ°€ λŒ€κΈ° μ‹œκ°„μ— 놀지 μ•Šκ²Œ ν•˜λŠ” μž₯μΉ˜μž…λ‹ˆλ‹€.

μ†λ‹˜ λŒ€κΈ°ν˜„ν™©

15μ΄ˆλ§ˆλ‹€ μžλ™ κ°±μ‹ λ˜κ³ (setInterval + fetchRef νŒ¨ν„΄μœΌλ‘œ stale closure νšŒν”Ό), μˆ˜λ™ μƒˆλ‘œκ³ μΉ¨μ€ 1초 μΏ¨λ‹€μš΄μ„ λ‘‘λ‹ˆλ‹€. 사μž₯λ‹˜μ΄ ν˜ΈμΆœν•˜λ©΄ μ†λ‹˜μ€ κ°€κ³ μžˆμ–΄μš” / λŠ¦μ–΄μš” / μ·¨μ†Œ 둜 응닡할 수 μžˆμŠ΅λ‹ˆλ‹€(PATCH /waiting/:id/guest-response). μž…μž₯μ™„λ£ŒΒ·μ·¨μ†Œ μƒνƒœκ°€ 되면 만료 νŽ˜μ΄μ§€λ‘œ λŒ€μ²΄ν•©λ‹ˆλ‹€.

μ£Όλ°© Β· 홀 Β· 포슀

  • μ£Όλ°© β€” μ ‘μˆ˜λ¨ β†’ 쑰리쀑 β†’ μ™„λ£Œ 칸반. 쑰리 μ‹œκ°„μ„ 재고 15뢄을 λ„˜κΈ°λ©΄ 타이머가 λΉ¨κ°›κ²Œ λ°”λ€λ‹ˆλ‹€
  • 홀 β€” PATCH /orders/:id/hall-receive 둜 μ„œλΉ™ 처리
  • 포슀 β€” ν…Œμ΄λΈ”λ³„ 주문을 λͺ¨μ•„ 결제 처리, 결제 ν›„ ν…Œμ΄λΈ” 점유 ν•΄μ œ

μ„Έ ν™”λ©΄ λͺ¨λ‘ μ§„μž… μ‹œ GET /orders 둜 초기 λ‘œλ“œν•œ λ’€ Realtime κ΅¬λ…μœΌλ‘œ μ΄μ–΄λ°›μŠ΅λ‹ˆλ‹€.

μ˜€λ„ˆ μ›¨μ΄νŒ… 관리

ν‚€μ˜€μŠ€ν¬ μ‹ κ·œ 등둝이 Realtime으둜 μ¦‰μ‹œ λ‚˜νƒ€λ‚©λ‹ˆλ‹€. μž…μž₯μ™„λ£ŒΒ·μ·¨μ†Œν•œ νŒ€μ€ λͺ©λ‘μ—μ„œ μ§€μš°μ§€ μ•Šκ³  맨 λ’€λ‘œ 보내며 μ·¨μ†Œμ„  처리 ν•©λ‹ˆλ‹€ β€” 방금 무슨 일이 μžˆμ—ˆλŠ”μ§€κ°€ 운영 μ€‘μ—λŠ” 정보이기 λ•Œλ¬Έμž…λ‹ˆλ‹€. μ „ν™”λ²ˆν˜ΈλŠ” 010-****-5678 둜 λ§ˆμŠ€ν‚Ήν•©λ‹ˆλ‹€.

메뉴 관리

react-hook-form + zod μŠ€ν‚€λ§ˆ 검증, νŒλ§€μ€‘/ν’ˆμ ˆ Switch ν† κΈ€(PATCH /menu/:id), μΉ΄ν…Œκ³ λ¦¬ 필터링을 μ§€μ›ν•©λ‹ˆλ‹€.

기술 μŠ€νƒ

Frontend

기술 μ—­ν• 
Next.js 16 (App Router) Route Group (public) / (owner) / (system) 으둜 역할별 λ ˆμ΄μ•„μ›ƒ 뢄리
React 19 Β· TypeScript μ „ λ ˆμ΄μ–΄ νƒ€μž… μ•ˆμ „μ„±
TanStack Query v5 μ„œλ²„ μƒνƒœ 관리, μΊμ‹œ λ¬΄νš¨ν™”
Tailwind CSS v4 + shadcn/ui 닀크 ν…Œλ§ˆ λ””μžμΈ μ‹œμŠ€ν…œ (DESIGN.md)
Supabase Realtime WebSocket 기반 DB λ³€κ²½ ꡬ독
motion v12 fly-to-cart, 순차 λ“±μž₯ λ“± μΈν„°λž™μ…˜
react-hook-form + zod 폼 μƒνƒœ + μŠ€ν‚€λ§ˆ 검증
jose proxy.ts μ—μ„œ JWT μ„œλͺ… 검증
react-qr-code Β· sonner QR 생성 Β· Toast

Backend

기술 μ—­ν• 
NestJS 11 REST API, λͺ¨λ“ˆ/μ„œλΉ„μŠ€/컨트둀러 ꡬ쑰
Prisma 7 νƒ€μž…μ„Έμ΄ν”„ ORM (@prisma/adapter-pg μ–΄λŒ‘ν„° 방식)
Supabase (PostgreSQL) 메인 DB + Realtime CDC
Passport JWT + bcrypt HttpOnly μΏ ν‚€ 기반 인증
class-validator DTO μž…λ ₯ 검증, ValidationPipe μ „μ—­ 등둝

DB μŠ€ν‚€λ§ˆ

User       1──1  Store
Store      1──N  Table
Store      1──N  MenuItem
Store      1──N  Order
Store      1──N  WaitingEntry
Order      1──N  OrderItem
Table      1──N  Order
MenuItem   1──N  OrderItem

섀계 포인트

μ™œ Supabase Realtime인가 WebSocket μ„œλ²„λ₯Ό 직접 μ„Έμš°λŠ” λŒ€μ‹  PostgreSQL의 CDC(Change Data Capture)λ₯Ό κ΅¬λ…ν•©λ‹ˆλ‹€. NestJSλŠ” RESTμ—λ§Œ μ§‘μ€‘ν•˜κ³  μ‹€μ‹œκ°„ λ ˆμ΄μ–΄λŠ” Supabaseκ°€ λ‹΄λ‹Ήν•˜λŠ” 관심사 λΆ„λ¦¬μž…λ‹ˆλ‹€. μ£Όλ°©Β·ν™€Β·ν¬μŠ€κ°€ 각각 ν΄λ§ν–ˆλ‹€λ©΄ ν™”λ©΄ 수만큼 μš”μ²­μ΄ λŠ˜μ—ˆμ„ κ²ƒμž…λ‹ˆλ‹€.

μ™œ HttpOnly 쿠킀인가 localStorage 방식은 XSS ν•œ 번이면 토큰이 ν†΅μ§Έλ‘œ ν„Έλ¦½λ‹ˆλ‹€. ν‚€μ˜€μŠ€ν¬Β·ν…Œμ΄λΈ”μ˜€λ”μ²˜λŸΌ λΆˆνŠΉμ • λ‹€μˆ˜κ°€ λ§Œμ§€λŠ” κΈ°κΈ°μ—μ„œλŠ” JSκ°€ 토큰에 μ ‘κ·Όν•  수 μ—†μ–΄μ•Ό ν•©λ‹ˆλ‹€.

μ•ž λŒ€κΈ° νŒ€ 수λ₯Ό μ™œ μ„œλ²„μ—μ„œ μ„ΈλŠ”κ°€ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ κ³„μ‚°ν•˜λ €λ©΄ 전체 λŒ€κΈ° λͺ©λ‘μ„ λ‚΄λ €μ€˜μ•Ό ν•˜κ³ , κ·Έ λͺ©λ‘μ—λŠ” λ‹€λ₯Έ μ†λ‹˜μ˜ μ „ν™”λ²ˆν˜Έκ°€ λ“€μ–΄ μžˆμŠ΅λ‹ˆλ‹€. μ„œλ²„μ—μ„œ status IN ('λŒ€κΈ°μ€‘','ν˜ΈμΆœμ€‘') AND number < λ‚΄λ²ˆν˜Έ 둜 μ§‘κ³„κ°’λ§Œ λ°˜ν™˜ν•΄ κ°œμΈμ •λ³΄ λ…ΈμΆœ 경둜λ₯Ό μ—†μ•΄μŠ΅λ‹ˆλ‹€.

μ£Όλ¬Έ 쀑볡은 μ™œ μ„œλ²„μ—μ„œ λ§‰λŠ”κ°€ λ²„νŠΌ μ—°νƒ€λ‘œ 같은 주문이 두 번 λ“€μ–΄κ°€λŠ” 문제λ₯Ό ν΄λΌμ΄μ–ΈνŠΈ κ°€λ“œλ‘œ λ¨Όμ € λ§‰μ•˜μ§€λ§Œ, κ·Έκ²ƒλ§ŒμœΌλ‘œλŠ” μš”μ²­μ„ 직접 λ§Œλ“€μ–΄ λ³΄λ‚΄λŠ” 경우λ₯Ό λͺ» λ§‰μŠ΅λ‹ˆλ‹€. μ„œλ²„μ—μ„œ ν’ˆμ ˆ μ—¬λΆ€Β·μˆ˜λŸ‰Β·κ°€κ²©Β·νƒ€λ§€μž₯ λ©”λ‰΄κΉŒμ§€ λ‹€μ‹œ κ²€μ¦ν•©λ‹ˆλ‹€. 가격을 ν΄λΌμ΄μ–ΈνŠΈκ°€ 보내쀀 κ°’μœΌλ‘œ λ―Ώμ§€ μ•ŠλŠ” 것이 ν•΅μ‹¬μž…λ‹ˆλ‹€.

λ©”λ‰΄λŠ” μ™œ μ†Œν”„νŠΈ μ‚­μ œμΈκ°€ μ£Όλ¬Έ 이λ ₯이 μžˆλŠ” 메뉴λ₯Ό ν•˜λ“œ μ‚­μ œν•˜λ©΄ OrderItem 의 FKκ°€ κΉ¨μ Έ 500이 λ‚©λ‹ˆλ‹€. μ§€λ‚œ μ£Όλ¬Έμ„œμ—μ„œ 메뉴 이름이 μ‚¬λΌμ§€λŠ” 것은 λ§€μž₯ μž…μž₯μ—μ„œλ„ μ‚¬κ³ μ΄λ―€λ‘œ, μ‚­μ œ λŒ€μ‹  λΉ„ν™œμ„± μ²˜λ¦¬ν•΄ μ°Έμ‘° 무결성과 κ³Όκ±° 데이터λ₯Ό ν•¨κ»˜ μ§€ν‚΅λ‹ˆλ‹€.

μ‹œμž‘ν•˜κΈ°

사전 μ€€λΉ„

  • Node.js 20+
  • Supabase ν”„λ‘œμ νŠΈ (PostgreSQL)

Backend

cd backend
npm install
# .env λ₯Ό λ§Œλ“€κ³  μ•„λž˜ ν™˜κ²½λ³€μˆ˜λ₯Ό μ±„μ›λ‹ˆλ‹€
npx prisma migrate dev
npm run start:dev

Frontend

cd frontend
npm install
# .env.local 을 λ§Œλ“€κ³  μ•„λž˜ ν™˜κ²½λ³€μˆ˜λ₯Ό μ±„μ›λ‹ˆλ‹€
npm run dev

ν™˜κ²½λ³€μˆ˜

backend/.env

DATABASE_URL=    # Supabase pooler (port 6543)
DIRECT_URL=      # Supabase direct (port 5432)
JWT_SECRET=      # 64λ°”μ΄νŠΈ 랜덀 hex
FRONTEND_URL=    # CORS ν—ˆμš© origin

frontend/.env.local

NEXT_PUBLIC_BACKEND_URL=
NEXT_PUBLIC_FRONTEND_URL=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
JWT_SECRET=      # λ°±μ—”λ“œμ™€ λ°˜λ“œμ‹œ λ™μΌν•œ κ°’

Important

JWT_SECRET 은 ν”„λ‘ νŠΈΒ·λ°±μ—”λ“œκ°€ κ°™μ•„μ•Ό ν•©λ‹ˆλ‹€. proxy.ts 의 jose jwtVerify κ°€ 이 κ°’μœΌλ‘œ μ„œλͺ…을 κ²€μ¦ν•˜κΈ° λ•Œλ¬Έμ—, 값이 λ‹€λ₯΄λ©΄ λ‘œκ·ΈμΈμ€ λ˜λŠ”λ° λͺ¨λ“  보호 λΌμš°νŠΈμ—μ„œ νŠ•κΉλ‹ˆλ‹€.

Note

Prisma 7 주의 β€” schema.prisma 에 datasource url 을 μ“Έ 수 μ—†μŠ΅λ‹ˆλ‹€. λŸ°νƒ€μž„μ€ @prisma/adapter-pg 둜 직접 μ—°κ²°ν•˜κ³ , CLI λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ€ prisma.config.ts 의 DIRECT_URL 을 μ‚¬μš©ν•©λ‹ˆλ‹€.

ν”„λ‘œμ νŠΈ ꡬ쑰

kiorder/
β”œβ”€β”€ frontend/                    # Next.js 16 App Router
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ page.tsx             # λžœλ”© β€” ν™”λ©΄ λ‘˜λŸ¬λ³΄κΈ° + μžλ™ 둜그인
β”‚   β”‚   β”œβ”€β”€ (public)/            # 인증 λΆˆν•„μš” (둜그인, μ†λ‹˜ λŒ€κΈ°ν˜„ν™©)
β”‚   β”‚   β”œβ”€β”€ (owner)/             # kiosk Β· kitchen Β· hall Β· pos Β· table-order Β· owner/*
β”‚   β”‚   └── (system)/            # μ‹œμŠ€ν…œ κ΄€λ¦¬μž
β”‚   β”œβ”€β”€ components/              # kiosk Β· kitchen Β· hall Β· owner Β· table-order Β· screensaver Β· shared Β· ui
β”‚   β”œβ”€β”€ hooks/                   # useTableMenu Β· useOwnerMenu Β· usePosOrders Β· useFlyToCart Β· useIdle …
β”‚   β”œβ”€β”€ lib/
β”‚   β”‚   β”œβ”€β”€ api.ts               # apiFetch 곡톡 ν΄λΌμ΄μ–ΈνŠΈ
β”‚   β”‚   β”œβ”€β”€ supabase.ts          # Realtime ν΄λΌμ΄μ–ΈνŠΈ
β”‚   β”‚   └── motion.ts            # λͺ¨μ…˜ 토큰
β”‚   β”œβ”€β”€ types/                   # menu Β· order Β· store Β· waiting
β”‚   └── proxy.ts                 # Route Guard (jose jwtVerify)
β”‚
└── backend/                     # NestJS 11
    β”œβ”€β”€ src/
    β”‚   β”œβ”€β”€ auth/                # JWT 인증, HttpOnly μΏ ν‚€
    β”‚   β”œβ”€β”€ store/               # λ§€μž₯ 관리
    β”‚   β”œβ”€β”€ menu/                # 메뉴 CRUD (μ†Œν”„νŠΈ μ‚­μ œ)
    β”‚   β”œβ”€β”€ order/               # μ£Όλ¬Έ Β· μƒνƒœ μ „ν™˜ Β· μ„œλ²„ 검증
    β”‚   β”œβ”€β”€ table/               # ν…Œμ΄λΈ”
    β”‚   β”œβ”€β”€ waiting-entry/       # μ›¨μ΄νŒ… (μ•ž λŒ€κΈ° νŒ€ 집계 포함)
    β”‚   └── prisma/              # PrismaService (@prisma/adapter-pg)
    β”œβ”€β”€ prisma/
    β”‚   └── schema.prisma
    └── prisma.config.ts         # Prisma 7 CLI μ„€μ • (DIRECT_URL)

배포

λŒ€μƒ ν”Œλž«νΌ
Frontend Vercel
Backend Render
Database Supabase

Render 무료 ν”Œλžœμ˜ μŠ¬λ¦½μ„ ν”Όν•˜κΈ° μœ„ν•΄ GET /health λ₯Ό 두고 주기적으둜 κΉ¨μ›λ‹ˆλ‹€.


Made with β˜• by jcdororo

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages