Lino의 안전한 multi-tenancy

Multi-tenancy๋Š” ํ•˜๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์—ฌ๋Ÿฌ ๊ณ ๊ฐ, ํšŒ์‚ฌ ๋˜๋Š” ์กฐ์ง์„ ์ง€์›ํ•˜๋ฉด์„œ ๋ฐ์ดํ„ฐ์™€ ๊ถŒํ•œ์„ ๊ฒฉ๋ฆฌํ•˜๋„๋ก ํ•ด ์ค๋‹ˆ๋‹ค. Lino์—์„œ Tenant๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ๋ฆ„์˜ ์ผ๋ถ€๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ tenancy ๊ธฐ๋ฐ˜์„ ์ƒ์„ฑํ•˜๊ณ , ITenantContext๋ฅผ ํ†ตํ•ด subdomain, host ๋˜๋Š” slug๋กœ Tenant๋ฅผ ํ•ด์„ํ•˜๋ฉฐ, IUserContext๋ฅผ ํ†ตํ•ด JWT๋กœ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋ฅผ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

JWT์—๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋œ ๋Œ€์ƒ TenantId๋„ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. Lino๋Š” ๊ทธ Tenant๋ฅผ ์š”์ฒญ์— ๋Œ€ํ•ด ํ•ด์„๋œ Tenant์™€ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ํŽ˜์ด์ง€๋Š” ๊ทธ ๊ธฐ๋ฐ˜์ด ์กด์žฌํ•œ ๋’ค ๊ฒฐ์ •ํ•ด์•ผ ํ•  ๊ฒƒ, ์ฆ‰ ์—”ํ‹ฐํ‹ฐ ๋ชจ๋ธ๋ง, ์กฐ์ง๋ณ„ ๊ถŒํ•œ, ์ƒ์„ฑ๋œ ์ถ”์ƒํ™”์˜ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ์— ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค.

Tenant ์ง€์› ์ถ”๊ฐ€

์‹œ์Šคํ…œ์ด SaaS์ด๊ฑฐ๋‚˜ ์„œ๋กœ ๋‹ค๋ฅธ ๊ณ ๊ฐ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•ˆ์—์„œ ๊ณต์กดํ•ด์•ผ ํ•˜์ง€๋งŒ ์ปจํ…์ŠคํŠธ ์‚ฌ์ด๋กœ ๋ˆ„์ถœ๋˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฒฝ์šฐ Tenant ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

lino feature tenant add

์–ด์‹œ์Šคํ„ดํŠธ๋Š” Tenants ๋“ฑ๋ก, ์‚ฌ์šฉ์ž ์—ฐ๊ฒฐ, ํ˜„์žฌ Tenant ํ•ด์„, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๋กœ์˜ ์ „ํŒŒ ๋ฐ ์ธ์ฆ ํ†ตํ•ฉ์„ ์œ„ํ•œ ๊ธฐ๋ฐ˜์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ITenantContext๋Š” subdomain, host ๋˜๋Š” slug๋กœ ํ•ด์„๋œ Tenant๋ฅผ ๋‚˜ํƒ€๋‚ด๊ณ , IUserContext๋Š” JWT๋กœ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋œ ๋Œ€์ƒ TenantId๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

์ƒ์„ฑ ํ›„์—๋Š” ์–ด๋–ค ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์‹ค์ œ๋กœ Tenant์— ์†ํ•˜๊ณ  ์–ด๋–ค ์—”ํ‹ฐํ‹ฐ๊ฐ€ global์ธ์ง€ ๊ฒ€ํ† ํ•˜์‹ญ์‹œ์˜ค. ์ด ๊ฒ€ํ† ๋Š” ์ œํ’ˆ ๋ชจ๋ธ๋ง์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. multi-tenancy๋Š” ๋‹จ์ˆœํžˆ Tenants ํ…Œ์ด๋ธ”์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์–ด๋–ค ๊ทœ์น™, ํ™”๋ฉด, ๊ถŒํ•œ, ๋ฐ์ดํ„ฐ ๋ฐ ์ž‘์—…์ด Lino๊ฐ€ ๊ฒ€์ฆํ•œ ํ˜„์žฌ Tenant์— ์˜์กดํ•˜๋Š”์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • Tenant-scoped: ์ฃผ๋ฌธ, ๊ณ ๊ฐ์˜ ์ œํ’ˆ, ํšŒ์‚ฌ ์„ค์ •, CRM ๊ณ ๊ฐ ๋˜๋Š” ํŠน์ • ์กฐ์ง์— ์†ํ•œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ.
  • Global: ํ”Œ๋žซํผ์˜ ๊ณต๊ฐœ ์นดํƒˆ๋กœ๊ทธ, ํ”Œ๋žœ, ๋‚ด๋ถ€ ์šด์˜ ๋ฆฌ์†Œ์Šค, ๊ด€๋ฆฌ ์„ค์ • ๋˜๋Š” Tenant ์„ ํƒ ์ „์— ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ.
  • Hybrid: Tenant๋ณ„ ์‚ฌ์šฉ์ž ์ง€์ •์„ ๊ฐ€์ง„ global ๋ฐ์ดํ„ฐ์ด๋ฉฐ, ์ž˜๋ชป๋œ ์ค‘๋ณต์ด๋‚˜ ๊ทœ์น™ ๋ˆ„์ถœ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ๋ช…์‹œ์ ์ธ ๋ชจ๋ธ๋ง์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“ˆ์‹ ๋ชจ๋†€๋ฆฌ์Šค์—์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ tenancy ๋ชจ๋“ˆ์ด ์›๋ณธ Tenant ์—”ํ‹ฐํ‹ฐ์˜ ์†Œ์œ ์ž์ž…๋‹ˆ๋‹ค. catalog ๋˜๋Š” CRM ๊ฐ™์€ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์€ ์ด ์—”ํ‹ฐํ‹ฐ์— ์ง์ ‘ ์ ‘๊ทผํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. Tenant ์ •๋ณด๋ฅผ ์•Œ์•„์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, Tenant ์ƒ์„ฑ ๋˜๋Š” ์—…๋ฐ์ดํŠธ ๊ฐ™์€ integration events๋กœ ์ฑ„์›Œ์ง€๋Š” ์ตœ์†Œ ํ•„๋“œ์˜ shadow entities๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Tenant context

Tenant ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด Lino๋Š” ํ˜„์žฌ Tenant ํ•ด์„์„ ์ค‘์•™ํ™”ํ•˜๊ณ  ๊ทธ ๊ฐ’์„ ITenantContext๋กœ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด ์ปจํ…์ŠคํŠธ๋Š” ์š”์ฒญ์˜ subdomain, host ๋˜๋Š” slug์—์„œ ํ•ด์„๋œ Tenant๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, Tenant๋ฅผ ์ฐพ์€ ๊ฒฝ์šฐ TenantId ๋ฐ slug ๊ฐ™์€ ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

์ธ์ฆ๋œ ํ๋ฆ„์—์„œ IUserContext๋Š” JWT์—์„œ ์‚ฌ์šฉ์ž๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค. ๊ทธ JWT์—๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋œ ๋Œ€์ƒ TenantId๋„ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. Lino๋Š” IUserContext์˜ Tenant์™€ ITenantContext์˜ Tenant๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์ด ์„œ๋กœ ๋‹ค๋ฅธ ์ปจํ…์ŠคํŠธ๋ฅผ ์‹ ๋ขฐํ•˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ƒ์„ฑ๋œ ๊ธฐ๋Šฅ์ด๋‚˜ ๋‚˜์ค‘์— ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ์—์„œ๋Š” claims๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋‹ค์‹œ ์ฝ๊ฑฐ๋‚˜ ๊ฐ handler์—์„œ ์ž„์˜์˜ TenantId๋ฅผ ๋ฐ›๋Š” ๋Œ€์‹  ํ•ญ์ƒ ์ด๋Ÿฌํ•œ ์ถ”์ƒํ™”๋ฅผ ์šฐ์„ ํ•˜์‹ญ์‹œ์˜ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด commands, queries, ํŽ˜์ด์ง€ ๋ฐ ํ†ตํ•ฉ์ด ๊ฐ™์€ source of truth๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  Lino๊ฐ€ ์ด๋ฏธ ๊ตฌํ˜„ํ•œ ๋ณด์•ˆ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋ณด์กดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Background Jobs, ์ด๋ฒคํŠธ ์†Œ๋น„์ž ๋ฐ ํ†ตํ•ฉ์—๋„ ์ปจํ…์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. tenant-scoped ์ œํ’ˆ, ๊ณ ๊ฐ ๋˜๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” job์€ ์ฒ˜๋ฆฌ ์ค‘์ธ Tenant๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ITenantContext์™€ IUserContext๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋Š” active HTTP request๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Tenant-scoped ์—”ํ‹ฐํ‹ฐ

Tenant-scoped ์—”ํ‹ฐํ‹ฐ๋Š” Tenant identifier๋ฅผ ๊ฐ€์ง€๊ณ  query filters์— ์ฐธ์—ฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ผ๋ฐ˜ ํ๋ฆ„์ด ๋ชจ๋“  queries์—์„œ TenantId๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ถ€๋‹ด์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์€ ๊ฒ€์ฆ๋œ ์ปจํ…์ŠคํŠธ์™€ Lino๊ฐ€ ์ƒ์„ฑํ•œ filters๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ํŠน์ • ์กฐ์ง์— ์†ํ•˜๋Š” aggregates์—๋Š” Tenant๋ฅผ ํฌํ•จํ•˜์‹ญ์‹œ์˜ค.
  • Tenant์™€ ์ž์ฃผ ์กฐํšŒ๋˜๋Š” ํ•„๋“œ๋ฅผ ๊ณ ๋ คํ•ด indexes๋ฅผ ๊ตฌ์„ฑํ•˜์‹ญ์‹œ์˜ค.
  • commands์™€ queries์—์„œ๋Š” Tenant๋ฅผ ๊ณ ๋ฆฝ๋œ ๊ถŒํ•œ ์›์ฒœ์œผ๋กœ ๋ฐ›๋Š” ๋Œ€์‹  ITenantContext ๋ฐ IUserContext๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.
  • imports์™€ jobs์—์„œ๋Š” ์–ด๋–ค Tenant๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋Š”์ง€ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜ํ•˜์‹ญ์‹œ์˜ค.

tenant-scoped ์—”ํ‹ฐํ‹ฐ๋Š” ํ•˜๋‚˜์˜ Tenant์— ์†ํ•ฉ๋‹ˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์—ฌ๋Ÿฌ Tenants์— ์†ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฉด ์ด๋ฅผ ๋ชจ๋ธ๋ง ๊ฒฐ์ •์œผ๋กœ ๋‹ค๋ฃจ์‹ญ์‹œ์˜ค. global์ผ ์ˆ˜๋„ ์žˆ๊ณ , Tenant๋ณ„ ๋ณต์‚ฌ๋ณธ์ด ํ•„์š”ํ•  ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, ๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ค‘๊ฐ„ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋น ์กŒ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œํ’ˆ, ๊ณ ๊ฐ ๋˜๋Š” ์‚ฌ์šฉ์ž๋Š” ๋ชจ๋“ˆ๋งˆ๋‹ค ๋‹ค๋ฅธ ์˜๋ฏธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ์›๋ณธ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋ช‡ ๊ฐœ์˜ ํ•„๋“œ๋งŒ ๊ฐ€์ง„ shadow entity๊ฐ€ ๋” ์ •ํ™•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ์กฐ์™€ ownership๋„ ๊ตฌ๋ถ„ํ•˜์‹ญ์‹œ์˜ค. catalog์˜ ์ œํ’ˆ์ด Tenant๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ์ด๊ฒƒ์ด catalog๊ฐ€ Tenant lifecycle์„ ์ œ์–ดํ•œ๋‹ค๋Š” ๋œป์€ ์•„๋‹™๋‹ˆ๋‹ค. tenancy ๋ชจ๋“ˆ์€ ์ƒ์„ฑ, ํ™œ์„ฑํ™”, ๋„๋ฉ”์ธ, branding ๋ฐ ๊ฒฉ๋ฆฌ ๋ชจ๋“œ์˜ ์†Œ์œ ์ž๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. catalog๋Š” ์ž์ฒด ํ๋ฆ„์„ ํ•„ํ„ฐ๋งํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

  • Indexes: ๊ฒ€์ƒ‰, ์ •๋ ฌ ๋ฐ Tenant๋ณ„ ์œ ์ผ์„ฑ์— ์‚ฌ์šฉ๋˜๋Š” ํ•„๋“œ์™€ TenantId๋ฅผ ๊ฒฐํ•ฉํ•˜์‹ญ์‹œ์˜ค.
  • Commands: ์—”ํ‹ฐํ‹ฐ๊ฐ€ tenant-scoped์ธ ๊ฒฝ์šฐ ๊ฒ€์ฆ๋œ ํ˜„์žฌ Tenant์—์„œ ์ž‘์—…์„ ์ ์šฉํ•˜์‹ญ์‹œ์˜ค.
  • Queries: ํŽ˜์ด์ง€๋„ค์ด์…˜, filters ๋ฐ ์นด์šดํ„ฐ๋ฅผ Lino๊ฐ€ ๊ฒ€์ฆํ•œ ๊ฐ™์€ ์ปจํ…์ŠคํŠธ์— ์œ ์ง€ํ•˜์‹ญ์‹œ์˜ค.
  • Events: consumers๊ฐ€ producer๋ฅผ ๋ถ€์ ์ ˆํ•˜๊ฒŒ ์กฐํšŒํ•˜์ง€ ์•Š๊ณ  projections๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ์ถฉ๋ถ„ํ•œ ์ปจํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•˜์‹ญ์‹œ์˜ค.

๋ฐ์ดํ„ฐ ๊ฒฉ๋ฆฌ

๊ฒฉ๋ฆฌ๋Š” ์ปฌ๋Ÿผ, schema ๋˜๋Š” ๋ณ„๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ ํƒ์€ ๋น„์šฉ, ๊ทœ๋ชจ, ๊ณ„์•ฝ ์š”๊ตฌ์‚ฌํ•ญ ๋ฐ ์šด์˜ ์—ญ๋Ÿ‰์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

์ „๋žต์žฅ์ ์‚ฌ์šฉ ์‹œ์ 
TenantId ์ปฌ๋Ÿผ๋” ๋‹จ์ˆœํ•˜๊ณ  ์ €๋ ดํ•จ๋งŽ์€ SaaS์˜ ์ข‹์€ ์ดˆ๊ธฐ ์„ ํƒ์ด๋ฉฐ, filters ๋ฐ Lino context์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
Tenant๋ณ„ schema์ค‘๊ฐ„ ์ˆ˜์ค€์˜ ๊ฒฉ๋ฆฌTenant๋ณ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊นŒ์ง€ ๊ฐ€์ง€ ์•Š๊ณ ๋„ ๋” ํฐ ๋ถ„๋ฆฌ ์š”๊ตฌ๊ฐ€ ์žˆ์„ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
Tenant๋ณ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ•ํ•œ ๊ฒฉ๋ฆฌ๊ณ„์•ฝ, ๊ทœ๋ชจ ๋˜๋Š” ์šด์˜์ด ๋” ๋†’์€ ๋น„์šฉ๊ณผ ์ž๋™ํ™”๋ฅผ ์ •๋‹นํ™”ํ•  ๋•Œ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ์ดˆ๊ธฐ ๋‹จ๊ณ„ SaaS์—์„œ๋Š” global filters์™€ ์ค‘์•™ํ™”๋œ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ฐ–์ถ˜ Tenant๋ณ„ ์ปฌ๋Ÿผ์ด ์‹ค์šฉ์ ์ธ ์ถœ๋ฐœ์ ์ž…๋‹ˆ๋‹ค. Lino๋Š” ์ด ๊ธฐ๋ฐ˜์„ ๋•์Šต๋‹ˆ๋‹ค. ๊ฒ€ํ†  ์‹œ tenant-scoped aggregates๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ Tenant๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ์ƒˆ queries๊ฐ€ ์ƒ์„ฑ๋œ ์ถ”์ƒํ™”๋กœ ๊ฒ€์ฆ๋œ ํ˜„์žฌ Tenant๋ฅผ ๊ณ„์† ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“ˆ์‹ ์„œ๋น„์Šค์—์„œ Tenant ๊ฒฉ๋ฆฌ์™€ ๋ชจ๋“ˆ ๊ฒฉ๋ฆฌ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๊ด€์‹ฌ์‚ฌ์ž…๋‹ˆ๋‹ค. Catalog ๋ชจ๋“ˆ์€ TenantId๊ฐ€ ์žˆ๋Š” ์ œํ’ˆ ํ…Œ์ด๋ธ”์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ , CRM ๋ชจ๋“ˆ์€ TenantId๊ฐ€ ์žˆ๋Š” ๊ณ ๊ฐ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, Tenancy ๋ชจ๋“ˆ์€ ์›๋ณธ Tenants ๋“ฑ๋ก์˜ ์†Œ์œ ์ž๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ identifier๊ฐ€ ์กด์žฌํ•œ๋‹ค๊ณ  ํ•ด์„œ ๋ชจ๋“ˆ ๊ฐ„ ์ง์ ‘ ์ ‘๊ทผ์ด ํ—ˆ์šฉ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

Tenant๋ณ„ roles ๋ฐ ๊ถŒํ•œ

SaaS์—์„œ๋Š” ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ํ•œ Tenant์—์„œ๋Š” ๊ด€๋ฆฌ์ž์ด๊ณ  ๋‹ค๋ฅธ Tenant์—์„œ๋Š” ์šด์˜์ž์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Lino๋Š” user-Tenant ์—ฐ๊ฒฐ์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ๊ธฐ๋ฐ˜์„ ์ƒ์„ฑํ•˜๋ฉฐ, ๊ธฐ๋Šฅ์˜ ๊ถŒํ•œ์€ framework๊ฐ€ ๊ฒ€์ฆํ•œ ํ™œ์„ฑ ์ปจํ…์ŠคํŠธ ์•ˆ์—์„œ ํ‰๊ฐ€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Tenant ๋ฐ–์—๋„ ๊ด€๋ฆฌ ๊ถŒํ•œ์ด ์กด์žฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”Œ๋žซํผ ์šด์˜ ํ™”๋ฉด์€ system context์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ๊ณ , ์ œํ’ˆ ๋ฐ ๊ณ ๊ฐ ๊ฐ™์€ ํŽ˜์ด์ง€๋Š” active Tenant๋ฅผ ์š”๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ถ„๋ฆฌ๋Š” ์ž‘์—…์ด global์ธ์ง€, ์•„๋‹ˆ๋ฉด ITenantContext์—์„œ ํ•ด์„๋œ Tenant์™€ IUserContext์—์„œ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์— ์˜์กดํ•˜๋Š”์ง€๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

  • ๊ถŒํ•œ์ด ์กฐ์ง๋งˆ๋‹ค ๋‹ฌ๋ผ์ง€๋Š” ๊ฒฝ์šฐ Tenant๋ณ„ roles๋ฅผ ๋ชจ๋ธ๋งํ•˜์‹ญ์‹œ์˜ค.
  • ํŽ˜์ด์ง€, commands ๋ฐ queries์˜ ์ธ๊ฐ€ ๊ฒ€์‚ฌ์—์„œ ํ˜„์žฌ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.
  • claims๋Š” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ณ , ์ธ์ฆ๋œ ํ๋ฆ„์— ์ฐธ์—ฌํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ๋งŒ token์— ๋‹ด์œผ์‹ญ์‹œ์˜ค.
  • ์ค‘์š” ๊ถŒํ•œ์ด ๋ณ€๊ฒฝ๋˜๋ฉด tokens๋ฅผ ํ๊ธฐํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•˜์‹ญ์‹œ์˜ค.
์ปจํ…์ŠคํŠธ์˜ˆ์‹œ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•
Systemํ”Œ๋žซํผ ๊ด€๋ฆฌ, ํ”Œ๋žœ, Tenants ๋ฐ global ์„ค์ •.ํŠน์ • Tenant์˜ scope ๋ฐ–์—์„œ ์‹คํ–‰ํ•˜์‹ญ์‹œ์˜ค.
Tenantํ•ด๋‹น Tenant์˜ ์ œํ’ˆ, ๊ณ ๊ฐ, ์ฃผ๋ฌธ, ์‚ฌ์šฉ์ž ๋ฐ roles.Lino๊ฐ€ ๊ฒ€์ฆํ•œ ํ˜„์žฌ Tenant๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.
Bothsystem๊ณผ Tenant ์–‘์ชฝ์— ์กด์žฌํ•˜๋Š” ์ผ๋ถ€ identity ๋˜๋Š” ๊ด€๋ฆฌ ๊ธฐ๋Šฅ.use case์—์„œ ์–ด๋–ค ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์„ ์–ธํ•˜์‹ญ์‹œ์˜ค.

JWT์™€ Tenant ๋ณด์•ˆ

Tenant ๊ธฐ๋Šฅ์ด ํ™œ์„ฑํ™”๋˜๋ฉด Lino๋Š” JWT๋ฅผ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž ์ปจํ…์ŠคํŠธ์˜ ์†Œ์Šค๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. IUserContext๋Š” JWT๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ณต์‚ฌ๋ณธ์œผ๋กœ ๋งŒ๋“ค์ง€ ์•Š๊ณ , ์‚ฌ์šฉ์ž์˜ claims์™€ token์˜ TenantId๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค.

  • issuer, audience, ์„œ๋ช… ๋ฐ ๋งŒ๋ฃŒ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌ์„ฑํ•ด ๋‘์‹ญ์‹œ์˜ค.
  • header, route ๋˜๋Š” ํ™”๋ฉด payload๋งŒ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ์ˆ˜๋™ ์ „ํ™˜์„ ํ—ˆ์šฉํ•˜๋Š” ๋Œ€์‹  Lino๊ฐ€ ๊ฒ€์ฆํ•œ Tenant๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.
  • ๋„ˆ๋ฌด ํฐ claims๋ฅผ ํ”ผํ•˜์‹ญ์‹œ์˜ค. ๋น„๋Œ€ํ•ด์ง„ tokens๋Š” ์šด์˜๊ณผ ๊ฐฑ์‹ ์„ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • ํ๋ฆ„์— ๊ฐ์‚ฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž, Tenant ๋ฐ ์ž‘์—…์ด ํฌํ•จ๋œ ์ ‘๊ทผ logs๋ฅผ ๊ธฐ๋กํ•˜์‹ญ์‹œ์˜ค.

acme.dev.localhost ๋ฐ globex.dev.localhost ๊ฐ™์€ subdomains๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ITenantContext๋Š” ์š”์ฒญ์— ๋Œ€ํ•ด ํ•ด์„๋œ Tenant๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. IUserContext๋Š” ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์™€ JWT์— ์กด์žฌํ•˜๋Š” Tenant๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. Lino๋Š” ์ด ๋‘ ์ปจํ…์ŠคํŠธ๋ฅผ ๋น„๊ตํ•˜์—ฌ ํ•œ Tenant์— ๋Œ€ํ•ด ๋ฐœ๊ธ‰๋œ token์ด ๋‹ค๋ฅธ Tenant ์ปจํ…์ŠคํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์ธ๊ฐ€๋„ ๊ณ„์† ์ปจํ…์ŠคํŠธ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ œํ’ˆ ๋ณด๊ธฐ ๊ถŒํ•œ์€ ๊ทธ ๊ถŒํ•œ์ด ๋ถ€์—ฌ๋˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋œ Tenant ์•ˆ์—์„œ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค ๋•Œ๋Š” ๋А์Šจํ•œ TenantId๋ฅผ ๋ฐ›์•„ ์ธ์ฆ๋œ ํ๋ฆ„์„ ๋ฌด์‹œํ•˜๋Š” ์ง€๋ฆ„๊ธธ์„ ๋งŒ๋“ค์ง€ ๋ง๊ณ , ์ด๋ฏธ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ถŒํ•œ๊ณผ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

처리되지 않은 오류가 발생했습니다. 새로 고침 ๐Ÿ—™