1
0
forked from baron/baron-sso
Files
baron-sso/README.md

51 KiB

Baron SSO

dev Code Check Biome backend userfront adminfront devfront orgfront chrome firefox safari

badge는 Code Checkbadges 브랜치의 latest/dev/<commit-sha>/에 발행합니다. 최신 HTML/LCOV/JSON summary는 Gitea Code Check의 패키지별 *-vitest-coverage-report artifact에서 확인할 수 있습니다.

Baron 로그인은 화이트 라벨링된 가족사의 모든 소프트웨어 Auth를 총괄하는 사용자 인증/인가 허브입니다.

📂 프로젝트 구조 (Project Structure)

baron_sso/
├── backend/            # Go Fiber 애플리케이션
│   ├── cmd/server/     # 진입점 (Entry point)
│   ├── internal/       # 도메인, 핸들러, 저장소(Repository)
│   └── Dockerfile
├── userfront/          # Flutter 애플리케이션
│   ├── src/            # UI 및 로직
│   └── pubspec.yaml
├── adminfront/         # React 기반 관리
│   ├── src/            # UI 및 로직
│   └── pubspec.yaml
├── gateway/            # Nginx 기반 Gateway (UserFront 프록시)
├── compose.ory-stack.yaml  # DB 서비스 (Postgres, ClickHouse)
├── compose.infra.yaml  # DB 서비스 (Postgres, ClickHouse)
├── docker-compose.yaml # 앱 서비스 (Front, Back)
├── .env.sample         # 환경 설정 템플릿
└── README.md           # 본 파일
  • Ory Stack으로 모든 구성요소를 self-hosting 합니다.
  • Backend는 Go (Fiber)로 구성된 Ory Stack의 유일한 Command 전송 포인트입니다. 모든 Command는 ClickHouse로 강제 전송되며 Audit Log 시스템을 구성합니다.
  • Front는 Backend를 통해서만 연동하며 자체가 Ory Stack의 RP기도 합니다. 크게 3개 계층으로 분리됩니다.
    • UserFront: Flutter로 구현된 커스텀 UI를 통해 매끄러운 사용자 경험을 보장합니다.
      • 로그인 : 비밀번호, SMS, QR 스캔 등의 수단으로 로그인 가능
      • 향후 모바일 앱 지원으로 인증 Push 승인 등 MFA 확장 (예정)
      • 정보변경, 앱 연동 이력 확인 등 개인화 기능
      • 사용 가능한 앱 리스트 (예정)
    • AdminFront: 사용자 관리 등 Admin 기능
    • DevFront: RP 관리 등 개발자 기능

개발 실행 정책

make dev는 로컬 개발용 실행 모드이며, React 기반 adminfront, devfront, orgfront는 모두 Vite HMR 모드로 동작해야 합니다. 이 세 서비스는 Docker Compose에서 Dockerfile dev target을 사용하고 /workspace/<app> bind mount 위에서 npm run dev -- --host 0.0.0.0로 실행합니다. make dev 경로에서 production distserve_frontend_prod.mjs로 정적 서빙하면 안 됩니다.

현재 개발 포트는 다음과 같습니다.

  • AdminFront: http://localhost:5173
  • DevFront: http://localhost:5174
  • OrgFront: http://localhost:5175

자세한 정책과 회귀 테스트는 make dev Vite HMR Policy를 확인하세요. 정책 회귀는 test/frontend_dev_bind_mount_policy_test.sh에서 검사합니다.

로컬 Playwright E2E도 기본적으로 Vite dev server를 봅니다. Gitea Actions 같은 CI에서는 CI=true로 production bundle을 vite preview로 검증합니다. 로컬에서 production bundle을 명시적으로 검증하려면 PLAYWRIGHT_USE_PREVIEW=true를 사용하세요. 이 정책은 test/playwright_frontend_runtime_policy_test.sh에서 검사합니다.

🏗 아키텍처 (Architecture)

0. Ory Stack

  • Ory Kratos: 사용자 인증/계정 관리(Identity).
  • Ory Hydra: OAuth2/OIDC 발급 및 토큰 관리.
  • Ory Keto: 권한/정책 기반 접근 제어.
  • Oathkeeper: 인증/인가 프록시 및 라우팅 게이트웨이.
flowchart
    subgraph Edge
      OK["Oathkeeper<br/>(Only Public Entry)"]
    end

    subgraph App
      BE["Backend<br/>(Only Upstream)"]
    end

    subgraph OryStack
      KR[Kratos]
      HY[Hydra]
      KE[Keto]
      KR --- HY --- KE
    end
    BE -->|Command| OryStack
    OK -->|Query| KR
    OK -->|Query| HY
    OK -->|Query| KE

1. Backend (Go Fiber)

  • Language: Go 1.26.2+
  • Framework: Fiber v2.25+
  • Database:
    • ClickHouse: 감사 로그 (고성능 데이터 수집)
    • PostgreSQL: 메타데이터 저장소 (Primary)
  • Features:
    • 인증용 SMS 발송 등 Ory-Stack으로 구현 어려운 부분 직접 구현
    • POST /api/v1/audit: 감사 로그 수집 API
    • userfront가 바라보는 backend

2. UserFront(Flutter Web/App)

  • Framework: Flutter 3.38.0+
  • Key Packages: flutter_riverpod, go_router
  • Features:
    • 탭 기반 로그인 UI (비밀번호 기반 / 링크 기반 / QR 기반 등)

3. adminfront(Web)

  • Framework: Vite, React 19+, Shadcn/ui 등
  • Features:
    • 사용자 관리, 권한 부여 등 관리자 기능
    • 앱 별 사용량(호출량) 등 통계
    • 핵심 Audit 대상

4. devfront(Web)

  • Framework: Vite, React 19+, Shadcn/ui 등
  • Features:
    • RP 등록 및 관리
    • RP별 Consent 관리

관리 데이터 Export/Import 정책

AdminFront의 테넌트와 사용자 export/import는 운영자가 CSV를 직접 검토하고 재반입할 수 있는 흐름을 기준으로 설계합니다. 기본 원칙은 내부 UUID를 불필요하게 노출하지 않고, 사람이 이해하기 쉬운 slug와 이름을 우선 사용하는 것입니다.

공통 원칙

  • CSV는 Excel 호환을 위해 UTF-8 BOM을 포함해 내려받습니다.
  • 기본 export는 시스템 내부 ID를 제외합니다.
  • 같은 데이터를 정확히 재동기화해야 하는 운영 작업에서는 includeIds=true 옵션으로 내부 ID 컬럼을 포함할 수 있습니다.
  • import는 preview/검토 단계를 거친 뒤 실행하는 것을 기본으로 합니다.
  • 기존 데이터와 충돌 가능성이 있는 row는 자동 적용하지 않고 관리자 선택 또는 확인 상태로 표시합니다.
  • 삭제는 export/import로 암묵 처리하지 않습니다. 삭제가 필요하면 별도 삭제 기능을 사용합니다.

Tenant Export

  • 기본 컬럼은 운영자가 다시 import하기 쉬운 형태를 유지합니다.
  • includeIds=false가 기본이며, 이 경우 내부 tenant_id는 제외합니다.
  • includeIds=true를 사용하면 기존 테넌트 update 또는 staging/production 간 매핑 확인에 필요한 ID를 포함합니다.
  • 주요 의미:
    • tenant_id: 내부 UUID. 기본 export에서는 제외됩니다.
    • name: 테넌트 표시 이름입니다.
    • type: PERSONAL, COMPANY, COMPANY_GROUP, USER_GROUP 중 하나입니다.
    • parent_tenant_id: 상위 테넌트 내부 ID입니다.
    • parent_tenant_slug: 상위 테넌트를 slug로 연결할 때 사용합니다.
    • slug: 운영상 사람이 다루는 테넌트 식별자입니다.
    • memo: 설명 또는 비고입니다.
    • email_domain: 테넌트에 연결된 이메일 도메인입니다. 여러 도메인은 ;, ,, 줄바꿈으로 구분할 수 있습니다.

Tenant Import

  • 필수 컬럼은 name, type, slug입니다.
  • 허용되는 header alias:
    • tenant_id: id, tenantid, tenant_id
    • parent_tenant_id: parentid, parent_id, parenttenantid, parent_tenant_id
    • parent_tenant_slug: parenttenantslug, parent_tenant_slug
    • memo: memo, description
    • email_domain: email-domain, emaildomain, email_domain, domain, domains
  • tenant_id가 있고 기존 테넌트가 있으면 update 대상으로 봅니다.
  • tenant_id가 없으면 slug 기준으로 기존 테넌트를 찾고, 없으면 신규 생성 후보로 봅니다.
  • parent_tenant_slug가 같은 import 파일 안에 있으면 부모 row를 먼저 처리하도록 정렬합니다.
  • import preview는 이름/slug 유사도 기반 후보를 보여주며, 관리자가 기존 테넌트 사용, 신규 생성, skip 중 선택할 수 있어야 합니다.
  • 외부 시스템에서 가져온 tenant_id처럼 현재 DB에 없는 ID는 충돌로 표시하고, 관리자가 새 slug 또는 기존 테넌트 매핑을 결정해야 합니다.

User Export

  • 기본 컬럼은 Email, Name, Phone, Status, tenant_slug, Position, JobTitle, CreatedAt입니다.
  • includeIds=true이면 user_id, tenant_id를 함께 포함합니다.
  • 사용자 role은 export 기본 컬럼에서 제외합니다. role은 일괄 변경의 실수 위험이 크므로 명시적 관리 화면 또는 별도 정책으로 다룹니다.
  • 사용자 metadata는 Meta:<key> 컬럼으로 뒤에 추가됩니다.
  • includeIds=false일 때는 id, user_id, tenant_id, tenantid 성격의 metadata key를 export에서 제외합니다.
  • tenant admin의 export는 관리 가능한 테넌트 범위로 제한됩니다.

User Import

  • 사용자 CSV의 기본 컬럼은 email, name, phone, role, tenant_slug, department, position, jobTitle입니다.
  • emailname은 CSV parsing 단계의 필수값입니다.
  • backend 생성 단계에서는 tenantSlug도 필수입니다.
  • tenant, tenant_slug, companyCode header는 사용자 소속 테넌트 slug로 매핑됩니다.
  • tenant_id, tenant_name, tenant_type, parent_tenant_id, parent_tenant_slug, parent_tenant_name, tenant_memo, email_domain 컬럼이 있으면 사용자 import 과정에서 필요한 테넌트 생성/매핑 preview에 사용합니다.
  • 위 기본 컬럼에 속하지 않는 컬럼은 사용자 metadata로 들어갑니다.
  • 테넌트에 userSchema가 있으면 import 중 metadata required/validation/loginId 규칙을 적용합니다.
  • 테넌트 schema에서 isLoginId로 지정된 metadata 값은 custom login ID로 동기화하며, 이메일/전화번호/예약어와 충돌하지 않아야 합니다.

한맥가족 User Import Email 정책

  • 전체 시스템에서 users.email은 unique입니다.
  • active, temporary_leave, suspended, preboarding, baron_guest, extended_leave, archived 등 모든 사용자 상태가 unique 검사 대상입니다. 특히 preboarding, baron_guest, archived 사용자는 email/local-part 선점 대상입니다.
  • 한맥가족 테넌트 root(hanmac-family)와 그 하위 subtree에서는 이메일 도메인과 무관하게 @ 앞 local-part도 unique 해야 합니다.
  • 예: han@hanmaceng.co.kr가 한맥가족 구성원으로 있으면 han@samaneng.com은 한맥가족 구성원으로 생성할 수 없습니다.
  • email 값이 @hanmaceng.co.kr처럼 도메인만 있으면 import preview에서 이름 기반 local-part를 제안합니다.
  • 이름 기반 local-part 기본 규칙은 이름 부분 초성 + 성 로마자입니다.
    • 예: 한치영 -> 치영의 초성 c + y + 성 han -> cyhan
  • 이미 cyhan, cyhan1이 있으면 다음 후보인 cyhan2를 제안합니다.
  • 외부 로마자화 패키지는 backend 의존성으로 추가하지 않고, 내부 한글 음절 분해와 성씨/초성 매핑을 사용합니다.
  • import preview의 row 상태:
    • valid: unique와 이름 기반 권장 규칙을 모두 만족합니다.
    • suggested: 도메인만 있거나 suffix 제안이 필요한 row입니다.
    • needsReview: 이름 매핑이 애매해 관리자가 직접 확인해야 합니다.
    • ruleMismatch: 최종 local-part가 이름 이니셜 + 성 + 숫자 suffix 규칙과 다릅니다. 예외 진행은 가능하지만 관리자에게 표시해야 합니다.
    • blockingError: local-part 중복, email 형식 오류, 필수값 누락처럼 생성을 차단해야 하는 상태입니다.
  • 단건 사용자 생성은 한맥가족 local-part 중복 시 자동 제안하지 않고 409 Conflict로 차단합니다.
  • bulk import는 preview에서 제안/수정된 최종 email을 사용하되, backend가 생성 직전에 다시 unique 규칙을 검증합니다.

User Status 정책

상태 표시명 Baron 사용 Works 처리 일반 조직도
active 재직 가능 생성/갱신 노출
temporary_leave 단기휴무 가능 계정 유지 노출
suspended 정지 불가 suspend 노출
preboarding 입사대기 불가 생성 안 함 비노출
baron_guest Baron 게스트 가능 생성 금지, 기존 계정 delete/deprovision 비노출
extended_leave 장기휴직 불가 delete/deprovision 비노출
archived 보관 불가 delete/deprovision 비노출
  • 기존 inactive 입력은 preboarding으로, leave_of_absence 입력은 temporary_leave로 호환 처리합니다.
  • 이슈 #862의 초기 명칭 baron_only는 구현 명칭으로 사용하지 않고 baron_guest로 정리합니다.
  • backend bootstrap은 남아 있는 legacy users.status 값을 inactive -> preboarding, leave_of_absence -> temporary_leave, baron_only -> baron_guest로 자동 정규화합니다.
  • archived 사용자는 과거 이력 보존용 계정이며 AdminFront 같은 관리자 화면에서만 감사/운영/중복 확인 목적으로 조회할 수 있습니다.

4. 주요 시나리오 (Core Scenarios)

  1. Same Browser SSO: Baron 로그인 서비스에 로그인된 상태에서 런처를 통해 타 앱/서비스로 이동 (자동 로그인). 1.1. 단 약관동의(Consent) 이력이 없으면 Consent 단계로 이동
  2. Cross-Device Auth: 이메일 SMS 등의 수단으로 링크를 전달받고 해당 링크를 사용자가 클릭하면 최초 로그인 요청한 세션이 활성화 2.1 향후 App Push 등 2차 인증 강화수단 검토 필요
  3. QR Login: 최초 진입 시 사전 로그인되어 있는 웹/앱을 이용해 QR 코드를 스캔하여, QR코드가 로딩된 Device를 로그인 상태로 전환

5. Headless Login ID/Password Flow

  • 목적: headless login을 허용한 클라이언트가 자체 로그인 화면에서 ID/password를 수집하되, Baron Backend가 OIDC 로그인 흐름만 계속 진행하고 RP에는 sessionJwt를 직접 넘기지 않습니다.
  • 대상 엔드포인트: POST /api/v1/auth/headless/password/login
  • 관련 구현:
    • backend/internal/handler/auth_handler.go
    • backend/internal/domain/hydra_models.go
    • backend/internal/handler/auth_handler_login_test.go

호출 순서

  1. RP 브라우저가 Hydra Public의 /oauth2/auth를 호출해 OIDC 인증을 시작합니다.
  2. Hydra가 로그인 단계로 넘긴 login_challenge를 RP가 확보합니다.
  3. RP backend가 자기 private key로 client_assertion JWT를 서명합니다.
  4. RP backend가 Baron Backend의 POST /api/v1/auth/headless/password/loginclient_id, client_assertion, login_challenge, loginId, password를 전송합니다.
  5. Baron Backend가 Hydra login request와 RP 설정을 검증한 뒤 Kratos sign-in 및 Hydra login accept를 수행합니다.
  6. 성공 시 Baron Backend는 redirectTo만 반환하고, RP 브라우저는 그 URL로 이동해 OIDC 흐름을 이어갑니다.

요청 바디

{
  "client_id": "headless-login-client",
  "client_assertion": "<signed-jwt>",
  "login_challenge": "<hydra-login-challenge>",
  "loginId": "employee001",
  "password": "secret"
}

성공 응답

{
  "status": "ok",
  "provider": "ory",
  "redirectTo": "https://rp.example.com/callback?code=..."
}

RP / Hydra 선행 조건

  • Hydra login request의 client.client_id와 요청 바디의 client_id가 반드시 같아야 합니다.
  • client가 headless login 선행 조건을 만족해야 합니다.
    • headless_token_endpoint_auth_method == "private_key_jwt" 또는 top-level token_endpoint_auth_method == "private_key_jwt"
    • headless_jwks_uri가 존재해야 합니다.
    • inline headless_jwks는 더 이상 지원하지 않습니다.
  • headless_login_enabled == true가 필요합니다.
  • metadata.status == "inactive"인 client는 차단됩니다.

client_assertion 규칙

  • 구현상 client_assertion은 현재 필수입니다.
  • 허용 서명 알고리즘:
    • RS256, RS384, RS512
    • PS256, PS384, PS512
    • ES256, ES384, ES512
    • EdDSA
  • JWT claim의 isssub는 모두 client_id와 같아야 합니다.
  • exp는 현재 시각 이후여야 합니다.
  • nbf, iat가 있으면 미래 시각이면 안 됩니다.
  • aud는 다음 둘 중 하나와 일치해야 합니다.
    • https://<backend-origin>/api/v1/auth/headless/password/login
    • /api/v1/auth/headless/password/login
  • 서명 검증용 public key는 headless_jwks_uri에서만 읽습니다.

내부 JWKS 캐시 정책

  • Baron Backend는 headless_jwks_uri를 직접 외부 스펙으로 저장하고, 실제 JWKS 문서는 내부 캐시에 저장해 사용합니다.
  • 등록/수정 이후에는 내부 캐시 동기화를 시도하고, 성공/실패 상태를 DevFront에서 확인할 수 있습니다.
  • 로그인 시 재조회는 다음 조건으로 제한합니다.
    • 캐시에 kid가 없을 때
    • kid는 있지만 서명 검증이 실패할 때
    • 캐시 TTL이 만료되었을 때
  • 그 외에는 내부 캐시를 사용합니다.
  • 백그라운드 worker가 TTL보다 짧은 주기로 jwksUri를 선제 점검해 첫 사용자 실패를 줄입니다.

DevFront 운영 액션

  • Settings > Public Key Registration 카드에서 다음 정보를 확인할 수 있습니다.
    • 최근 JWKS 캐시 갱신 시각
    • 최근 검증 성공 시각
    • 최근 에러와 연속 실패 횟수
    • 현재 cached kid 목록
    • 파싱된 key summary
      • kid
      • kty
      • use
      • alg
      • RSA key의 n preview (앞/뒤 일부만 표시)
  • 수동 운영 액션:
    • Refresh JWKS Cache
    • Revoke JWKS Cache
  • RP가 키를 교체했으면 실제 트래픽 전에 Refresh JWKS Cache를 먼저 호출하는 것을 권장합니다.

일반 로그인과의 차이

  • POST /api/v1/auth/password/login
    • UserFront 기본 비밀번호 로그인용입니다.
    • login_challenge가 없으면 sessionJwt를 반환합니다.
    • login_challenge가 있으면 Hydra accept 후 redirectTo를 반환합니다.
  • POST /api/v1/auth/headless/password/login
    • headless login 허용 클라이언트 전용입니다.
    • client_assertion 검증이 추가됩니다.
    • 항상 sessionJwt 없이 redirectTo만 반환합니다.

실패 패턴 요약

  • 400 bad_request
    • 필수 필드 누락
    • client_assertion 누락
  • 401 invalid_client_assertion
    • jwksUri 조회 실패
    • 서명 불일치
    • aud/iss/sub/exp 검증 실패
  • 401 invalid_client_assertion with explicit message
    • Headless login requires jwksUri. Inline jwks is not supported.
    • Configured jwksUri returned no keys for headless login.
    • Failed to refresh headless login jwks from jwksUri.
  • 403 forbidden
    • client_id 불일치
    • headless_login_enabled 미설정
    • inactive client
  • 401 password_or_email_mismatch
    • 사용자 인증 실패

전체 연결 구조도

flowchart TD
    subgraph Clients ["External Clients"]
        AF[adminfront]
        DF[devfront]
        UF["userfront"]
        DS["일반SW"]
    end

    subgraph AppService ["Control Plane"]
        BE["Backend (Command/Audit Controller)"]
    end

    subgraph OryBundle ["Ory Deployment Stack"]
        direction TB
        OK["Oathkeeper (Public Proxy/OIDC)"]
        
        subgraph OryEngines ["Ory Services"]
            direction LR
            HY["Hydra"]
            KR["Kratos"]
            KE["Keto"]
        end
        
        ICH[(Internal Clickhouse)]
        
        %% Internal Flow within Bundle
        OK -->|Routing/Queries| OryEngines
        OK -.->|Access/Usage Log| ICH
    end

    subgraph AuditDB ["Audit Storage"]
        ECH[(External Clickhouse)]
    end

    %% Key Command Path
    AF & DF & UF & DS ==>|Actions / Commands| BE
    
    %% Backend Responsibilities
    BE -->|Admin/State Control| OryEngines
    BE -.->|Mandatory Audit Log| ECH

    %% Connection Note (Hidden flow mentioned in logic)
    %% OK is technically the entry for OIDC, but removed as per request

    %% Styles
    style OryBundle fill:#f8f9fa,stroke:#333,stroke-width:2px
    style BE fill:#bbf,stroke:#333,stroke-width:2px
    style ECH fill:#fdd,stroke:#333
    style ICH fill:#dfd,stroke:#333
    style OK fill:#f9f,stroke:#333
    style OryEngines fill:#fff,stroke:#999,stroke-dasharray: 5 5

Kratos가 사용자 SoT이며 Hydra는 순수 OIDC 토큰 엔진입니다. 비지니스로직은 Backend를 통해서, 기본 인증 로직은 Ory Stack을 통해 진행됩니다.

SSOT 및 Redis Cache 전략

Baron SSO의 인증, 권한, OAuth/OIDC 원장은 Ory Stack입니다. Backend는 원장 쓰기 경로와 감사 로그를 중앙화하는 Control Plane입니다. 사용자 identity/profile/소속/조직도 노출 데이터에 대해 Backend DB users를 원장 또는 read model로 사용하지 않습니다. Redis는 Ory 원장 데이터의 성능 cache/mirror로만 사용합니다.

Ory에서 Redis cache로 웜업된 identity/조직 데이터는 frontend가 직접 소비하지 않습니다. Backend가 Redis mirror 또는 Ory Admin API fallback을 기준으로 cursor 기반 API를 adminfront, orgfront, userfront, 외부 API에 제공합니다.

데이터별 원본 위치

데이터 SSOT 보조 저장소/캐시 비고
Identity subject, credentials, recovery/verification address Ory Kratos identities Redis identity mirror Kratos identity ID가 사용자 subject이며 WORKS externalKey 기준입니다.
로그인 식별자 Ory Kratos traits Redis identity mirror/index Kratos가 인증 식별자의 원장입니다.
사용자 이름, 이메일, 전화번호, role 기본값 Ory Kratos traits Redis identity mirror 인증/profile 계산에 필요한 identity 값은 Kratos 기준으로 유지합니다.
Baron 사용자 운영 상태, soft delete, 운영 메타데이터 Ory Kratos traits/state 또는 별도 명시 원장 Redis mirror/cache Backend DB users를 사용자 read model로 사용하지 않습니다.
테넌트 tree, slug, 조직/부서/직무/직책 Ory Keto relation tuple, Backend read model Redis/API response cache 가능 권한/관계 판단은 Keto가 원장입니다. Ory가 보관하거나 조회할 수 없는 조직 표시/검색 데이터만 Backend read model에 둡니다.
권한/관계 Ory Keto relation tuple PostgreSQL outbox/status Backend를 통해 relation command를 보내고 처리 상태를 추적합니다.
OAuth2/OIDC client, consent, token state Ory Hydra PostgreSQL client_consents, audit/read model Hydra가 프로토콜 원장이며 로컬 테이블은 운영 조회/감사용입니다.
RP별 사용자 custom claim 값 Backend read model rp_user_metadata ID token/userinfo claim assembly Ory에 저장되지 않는 RP 범위 데이터입니다. Kratos traits나 claim output을 SSOT로 취급하지 않습니다.
전역 사용자 custom claim 값 Backend read model users.metadata.global_custom_claims ID token claim assembly Ory에 저장되지 않는 운영 범위 custom 값입니다.
WORKS Mobile mapping/outbox/job 상태 PostgreSQL worksmobile_* WORKS API 비교 응답 cache 가능 외부 SaaS 연동 상태이며 identity 원장이 아닙니다.
감사 로그/사용량 ClickHouse, Oathkeeper/Ory 로그 화면별 summary cache 가능 command와 보안 이벤트의 감사 원장입니다.
Headless JWKS 검증 상태 Redis headless:jwks:* cache DevFront 상태 카드 RP public key 문서 자체는 외부 jwksUri가 원본입니다.
로그인 코드, pending login, verification token Redis short-lived key 없음 만료 가능한 휘발성 상태입니다. 백업/복구 대상이 아닙니다.

SSOT 보장 원칙

  1. Kratos/Hydra/Keto/WORKS로 향하는 쓰기 command는 Backend를 통과합니다.
  2. Backend는 Ory write 성공 후 원장 ID를 기준으로 Ory를 재조회하고, Redis mirror를 갱신하거나 stale로 표시합니다. 사용자 identity/profile/소속 데이터는 Backend DB users에 read model로 갱신하지 않습니다.
  3. write-through 갱신 실패 시 원장 write를 되돌린 것으로 간주하지 않습니다. 대신 mirror/cache 상태를 stale 또는 failed로 표시하고 drift report와 refresh 대상으로 둡니다.
  4. Kratos Admin API 또는 Kratos DB를 Backend 밖에서 직접 수정하는 경로는 운영 정책상 금지합니다. 정비/DR처럼 예외가 필요한 경우에는 Redis mirror를 stale로 표시하고, full refresh와 drift report를 완료하기 전까지 cache 결과를 신뢰하지 않습니다.
  5. Backend DB users나 Redis cache는 Kratos partial list를 full snapshot처럼 취급하지 않습니다. Kratos 목록 조회가 partial이면 로컬 사용자 데이터를 근거로 정상 목록을 만들지 않습니다.
  6. frontend/API 대량 조회는 Backend가 제공하는 cursor 기반을 원칙으로 합니다. limit=5000&offset=0 같은 단일 대량 offset 조회는 사용자 수가 늘면 partial data를 전체처럼 보이게 만들 수 있으므로 신규 구현에서 금지합니다.
  7. Redis cache miss가 발생한 단건 조회는 가능한 경우 SSOT로 fallback하고, fallback 성공 시 Redis를 갱신합니다. 목록 조회는 mirror 상태가 ready가 아니면 화면/API에 경고 상태를 함께 전달해야 합니다.

Redis 사용 원칙

Redis는 원장이 아니라 cache/mirror 계층입니다. Redis 데이터 유실은 장애지만 데이터 유실 사고로 보지 않고, 원장 재조회와 refresh로 재수렴해야 합니다.

Redis 데이터 역할 TTL/보존 정책 장애 시 처리
identity:mirror:{identityID} Kratos identity summary 단건 cache 장기 mirror. refresh 상태와 함께 운영 Kratos GetIdentity fallback 후 write-through
identity:index:* Backend cursor API용 identity 목록/검색 index mirror refresh 주기로 재작성 stale 표시 후 full refresh
identity:mirror:state mirror 상태, count, last error 영구 상태 key adminfront에서 경고 표시
headless:jwks:* RP headless login JWKS cache JWKS TTL과 prefetch 정책 kid miss/검증 실패/TTL 만료 시 재조회
login/verification/pending 계열 key 인증 흐름의 단기 상태 짧은 TTL 필수 만료 또는 유실 시 사용자가 흐름 재시작
일반 API response cache 선택적 성능 cache 짧은 TTL, invalidation 우선 miss 시 Backend DB 또는 Ory 원장 조회

운영 Redis 설정은 maxmemorymaxmemory_policy가 명시되어야 합니다. identity mirror처럼 재수렴 가능한 데이터와 pending login처럼 사용자 흐름에 영향을 주는 단기 key가 같은 Redis를 공유하므로, eviction 발생 여부와 TTL 없는 key 증가를 운영 화면에서 볼 수 있어야 합니다.

Redis 모니터링 계획

Redis 적정 설정 판단에 필요한 운영 지표를 adminfront에 노출하는 후속 작업은 이슈 #1046으로 분리했습니다.

표시 대상은 Redis 연결/버전/uptime, used_memory, maxmemory, maxmemory_policy, keyspace hit/miss, expired/evicted keys, prefix별 key count, TTL 분포, identity:mirror:state, headless JWKS cache failure 요약입니다. 이 화면은 super_admin 전용으로 두고, Redis key value 자체는 노출하지 않습니다.


🚀 시작하기 (Getting Started)

사전 요구사항 (Prerequisites)

  • Docker & Docker Compose
  • Flutter SDK (로컬 개발용, 3.38.0+)
  • Go (로컬 백엔드 개발용)

환경 설정 (Environment Setup)

  1. 예제 환경 설정 파일을 복사합니다.
    cp .env.sample .env
    
  2. .env를 작성합니다. (아래 작성 규칙 필수)

.env 작성 규칙 (중요)

  • KEY=value 한 줄만 사용하고, 값 뒤에 같은 줄 주석을 붙이지 않습니다.
  • 주석이 필요하면 반드시 윗줄에 별도 주석 라인으로 작성합니다.
  • URL 값 끝에 공백이 들어가면 Hydra/Kratos 기동 실패로 이어질 수 있습니다.

잘못된 예:

USERFRONT_URL=https://sso.example.com # 이렇게 같은 줄 주석 금지

올바른 예:

# UserFront 공개 URL
USERFRONT_URL=https://sso.example.com

.env 핵심 변수 가이드

  • IDP_PROVIDER: 기본 ory
  • USERFRONT_URL: 브라우저 기준 공개 도메인 (예: https://sso.example.com)
  • OATHKEEPER_PUBLIC_URL: 보통 ${USERFRONT_URL}
  • HYDRA_PUBLIC_URL: 보통 ${OATHKEEPER_PUBLIC_URL}/oidc
  • KRATOS_BROWSER_URL: 보통 ${OATHKEEPER_PUBLIC_URL}/auth
  • KRATOS_UI_URL: UserFront UI URL (로컬 예: http://localhost:5000)
  • ADMINFRONT_CALLBACK_URLS: 콤마 구분 콜백 목록 (예: http://localhost:5173/auth/callback)
  • DEVFRONT_CALLBACK_URLS: 콤마 구분 콜백 목록 (예: http://localhost:5174/auth/callback)
    • 주의: callback URL 끝에 /가 붙으면 make validate-auth-config에서 실패 처리됩니다.
  • KRATOS_ALLOWED_RETURN_URLS_EXTRA: 추가 허용 return URL (선택)
    • 빈값: []
    • 다중값: ["https://a.example.com/callback","https://b.example.com/callback"] 또는 https://a.example.com/callback,https://b.example.com/callback
  • KRATOS_ALLOWED_RETURN_URLS_JSON: stage/prod에서 권장하는 전체 허용 return URL 목록
    • 공개 도메인, /ko, /en, /auth/callback, /ko/auth/callback, /en/auth/callback, 각 front callback을 포함해야 합니다.
  • CLIENT_LOG_DEBUG: 클라이언트 로그 디버그 모드 강제 (기본: 비운영 true, 운영 false)
    • 운영(APP_ENV=production|prod)에서 true|1|on|yes 설정 시 INFO/DEBUG 클라이언트 로그 수집 허용
    • 미설정(기본) 시 운영에서는 WARN/ERROR만 수집
  • BACKEND_LOG_LEVEL: Backend slog 레벨 override (선택)
    • 허용 값: debug, info, warn, error
    • 미설정 시 APP_ENV 기준으로 결정됩니다.
      • dev|local|development: debug
      • 그 외(stage, production, prod 등): info
  • USERFRONT_DEBUG_LOG: UserFront 측 디버그 로그 fallback 플래그
    • CLIENT_LOG_DEBUG가 없을 때만 UserFront에서 대체로 참조

클라이언트 로그 정책 (중요)

  • 기본 원칙: 운영 환경에서는 민감정보 보호를 우선하며, 과도한 로그 수집을 제한합니다.
  • 환경별 동작:
    • 비운영(dev/local/stage 등): 디버그 로그 허용 (INFO/DEBUG/WARN/ERROR)
    • 운영(production/prod) + CLIENT_LOG_DEBUG 미설정: WARN/ERROR만 수집
    • 운영(production/prod) + CLIENT_LOG_DEBUG=true: 디버그 로그 허용
  • 민감정보는 환경과 무관하게 마스킹합니다.
    • 예: password, newPassword, token, authorization, cookie, sessionJwt
    • 문자열 패턴(token=..., authorization:..., JSON body 내 민감 key)도 마스킹
  • 상세 정책 문서: docs/client-log-policy.md

Backend 로그 정책 (중요)

  • Backend 서버 로그는 APP_ENV 기준으로 기본 레벨이 정해집니다.
    • dev|local|development: debug
    • 그 외(stage, production, prod 등): info
  • 운영/스테이징에서 장애 분석이 필요할 때만 BACKEND_LOG_LEVEL=debug를 일시적으로 설정하는 것을 권장합니다.
  • headless login 같은 경로의 상세 진단 필드는 debug 레벨에서만 추가로 남습니다.
  • 상세 정책 문서: docs/backend-log-policy.md

.env 작성 후 권장 점검

make validate-auth-config

위 검증은 callback/allowed_return_urls/게이트웨이 매핑 규칙을 점검하고 config/.generated/auth-config.env를 생성합니다.

전체 스택 실행 (Running the Stack)

1. 네트워크 생성 (최초 1회)

Ory Stack과 애플리케이션 간 통신을 위한 도커 네트워크를 생성합니다.

# ory-net은 bridge 모드로 생성
docker network create -d bridge ory-net
docker network create hydranet
docker network create kratosnet
docker network create public_net #서비스용

2. 인프라 및 Ory Stack 실행

데이터베이스와 Ory 서비스(Kratos, Hydra, Keto 등)를 실행합니다.

# 권장: Make 실행 (인증 설정 검증 포함)
make up-dev

3. 애플리케이션 실행

userfront와 backend 서비스를 실행합니다.

make up-app

(또는 전체 스택 한번에 실행: make up-all)

Make 기반 인증 설정 검증 (권장)

up-* 타깃은 실행 전 인증 리다이렉트 설정을 자동 검증합니다.

# 1) 인증 설정 생성
make build-auth-config

# 2) 정적 검증 (callback / allowed_return_urls / 게이트웨이 매핑)
make validate-auth-config

# 3) 배선 + (가능 시) 런타임 Hydra client 검증
make verify-auth-config
  • 생성 파일: config/.generated/auth-config.env (compose 실행 시 자동 주입)
  • 게이트웨이 경유 환경은 URL 문자열 완전일치 대신 매핑 유효성(direct_match / mapped_match) 기준으로 검증합니다.
  • 관련 정책 문서: docs/oidc_redirect_mapping_validation_policy.md

권장 실행 순서

cp .env.sample .env
# .env 편집
make validate-auth-config
make up-dev
make up-app

직접 Compose를 사용하려면 다음처럼 env 파일을 함께 주입하세요.

docker compose --env-file .env --env-file config/.generated/auth-config.env -f compose.infra.yaml -f compose.ory.yaml up -d
docker compose --env-file .env --env-file config/.generated/auth-config.env -f docker-compose.yaml up -d

전체 백업/복구

전체 백업/복구는 CSV export/import가 아니라 Baron SSO와 Ory Stack 저장소를 같은 시점의 재해 복구 단위로 보존하는 절차입니다. 사용자 UUID, Kratos identity ID, Hydra/Keto 원장, WORKS 연동 mapping이 어긋나면 안 되므로 운영 복구는 DB dump와 설정 snapshot을 함께 다룹니다.

백업 실행

# 전체 백업
make dump

# 출력 위치를 직접 지정
make dump BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ

# 일부 서비스만 백업
make dump DUMP_SERVICES=postgres,ory-postgres,clickhouse,ory-clickhouse,config
make dump DUMP_SERVICES=ory-postgres,ory-clickhouse

# 생성된 백업 검증
make dump-verify BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ

# WORKS Drive로 외부 분산 저장
make upload-cloud BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ

# 지정 경로로 dump 후 바로 WORKS Drive 업로드
make dump-upload-cloud BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ

# 로컬 백업 목록
make dump-list

기본값은 DUMP_SERVICES=all, DUMP_MODE=maintenance입니다. DUMP_SERVICES는 다음 값을 콤마로 조합할 수 있습니다.

대상
postgres Baron Postgres (baron_postgres, ${DB_NAME:-baron_sso})
ory-postgres Ory Postgres의 ${KRATOS_DB:-ory_kratos}, ${HYDRA_DB:-ory_hydra}, ${KETO_DB:-ory_keto}
clickhouse Baron ClickHouse (baron_clickhouse)
ory-clickhouse Ory ClickHouse (ory_clickhouse)
config .env redacted copy, generated Ory config, gateway, 주요 compose 파일

백업 산출물은 기본적으로 backups/baron-sso-backup-YYYYMMDD-HHMMSSZ/ 아래에 생성됩니다.

manifest.json
checksums.sha256
postgres/
clickhouse/
config/
reports/

WORKS Drive 외부 업로드

make dump, make restore, make upload-cloud는 기본적으로 docker/backup-tools/Dockerfile에서 빌드한 baron-sso-backup-tools:local 컨테이너 안에서 실행됩니다. 호스트에는 Docker와 Docker socket 접근 권한만 필요하고, zstd, jq, curl, openssl, postgresql-client 같은 백업/복구 도구는 backup-tools image에 포함됩니다.

make upload-cloud는 기존 백업 디렉터리를 baron-sso-backup-*.tar.zst로 묶은 뒤 WORKS Drive에 업로드합니다. 압축 포맷은 .tar.zst로 고정되어 있고, 압축/해제는 backup-tools 컨테이너 내부의 zstd로 수행합니다.

백업이 완료되면 reports/backup-report.md도 생성됩니다. 이 report에는 사용자 수, 테넌트 수, RP 수, Hydra client 수, WORKS 관련 row count, 서비스별 수행 시간이 Markdown 표로 기록됩니다. make upload-cloudreports/*.md만 WORKS Drive 대상 폴더 아래의 reports 하위 폴더로 업로드하며, 업로드 파일명은 backup-report-YYYYMMDD-HHMMSSZ.md처럼 업로드 시각을 붙입니다. reports/cloud-upload.json은 로컬 업로드 실행 기록으로만 남기고 Drive에는 업로드하지 않습니다.

# 권장: 백업 경로를 명시해서 dump와 upload를 분리
make dump BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ
make upload-cloud BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ

# 또는 같은 BACKUP 경로로 연속 실행
make dump-upload-cloud BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ

# 실제 업로드 전 endpoint와 target만 확인
make upload-cloud BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ WORKS_DRIVE_DRY_RUN=true

# 예외적으로 호스트 도구로 직접 실행
make restore BACKUP_USE_DOCKER=false BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ CONFIRM_RESTORE=baron-sso

주요 변수:

변수 설명
WORKS_DRIVE_TARGET sharedrive, mydrive, group, sharedfolder 중 하나. 기본값은 sharedrive입니다.
WORKS_DRIVE_SHARED_DRIVE_ID WORKS_DRIVE_TARGET=sharedrive일 때 공용 드라이브 ID입니다.
WORKS_DRIVE_PARENT_FILE_ID 업로드할 대상 폴더의 WORKS Drive fileId입니다. 폴더 이름이나 경로가 아니며, 비우면 대상 drive/folder root에 업로드합니다.
WORKS_DRIVE_USER_ID mydrive 또는 sharedfolder 대상 사용자 ID입니다. 기본값은 me입니다.
WORKS_DRIVE_GROUP_ID WORKS_DRIVE_TARGET=group일 때 조직/그룹 ID입니다.
WORKS_DRIVE_SHARED_FOLDER_ID WORKS_DRIVE_TARGET=sharedfolder일 때 공유받은 폴더 ID입니다.
WORKS_DRIVE_ACCESS_TOKEN Drive API 호출용 Bearer token입니다. Drive API는 file scope가 필요합니다.
WORKS_DRIVE_ACCESS_TOKEN_FILE access token을 파일에서 읽을 때 사용합니다.
WORKS_DRIVE_ACCESS_TOKEN_CMD access token을 명령 출력으로 주입할 때 사용합니다.
WORKS_DRIVE_OAUTH_SCOPE Drive 업로드 앱 OAuth token에 사용할 scope입니다. 기본값은 file입니다.
WORKS_DRIVE_OAUTH_CLIENT_ID Drive 업로드 앱의 OAuth client ID입니다. 계정 동기화용 WORKS_ADMIN_OAUTH_CLIENT_ID와 분리합니다.
WORKS_DRIVE_OAUTH_CLIENT_SECRET Drive 업로드 앱의 OAuth client secret입니다.
WORKS_DRIVE_OAUTH_REFRESH_TOKEN Drive 업로드 앱의 refresh token입니다. 명시 access token이 없으면 이 값으로 access token을 갱신합니다.
WORKS_DRIVE_OAUTH_CLIENT_SERVICE_ACCOUNT Drive 업로드 앱의 service account입니다. JWT sub에 들어갑니다.
WORKS_DRIVE_OAUTH_CLIENT_PRIVATE_KEY_FILE Drive 업로드 앱 private key 파일입니다. 예: ./config/worksmobile-driveapp-private-key.pem
WORKS_DRIVE_SPLIT_SIZE 분할 업로드 시 part 크기입니다. 기본값은 9000M입니다.
WORKS_DRIVE_MAX_SINGLE_FILE_BYTES 이 값보다 archive가 크면 split part로 나눕니다. 기본값 0은 자동 분할 비활성입니다.
WORKS_DRIVE_FORCE_SPLIT true이면 크기와 무관하게 split part로 업로드합니다.
WORKS_DRIVE_OVERWRITE WORKS Drive upload URL 생성 요청의 overwrite 플래그입니다. 기본값은 false입니다.
WORKS_DRIVE_UPLOAD_REPORTS true이면 reports/*.md를 Drive의 report 폴더로 함께 업로드합니다. 기본값은 true입니다.
WORKS_DRIVE_REPORT_FOLDER_NAME Markdown report를 업로드할 하위 폴더 이름입니다. 기본값은 reports입니다.

Drive API는 업로드 URL 생성 후 해당 URL에 multipart Filedata로 실제 파일을 전송하는 2단계 방식입니다. 계정 동기화용 WORKS_ADMIN_OAUTH_*와 Drive 업로드용 WORKS_DRIVE_OAUTH_*는 서로 다른 앱/키로 관리합니다. token 우선순위는 WORKS_DRIVE_ACCESS_TOKEN, WORKS_DRIVE_ACCESS_TOKEN_FILE, WORKS_DRIVE_ACCESS_TOKEN_CMD, WORKS_DRIVE_OAUTH_REFRESH_TOKEN, 서비스 계정 JWT fallback 순서입니다. 운영에서는 Drive API 권한과 file scope 위임 정책을 먼저 확인해야 합니다.

복구 계획과 복구 실행

# 복구 전 계획 확인
make restore-plan BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ \
  RESTORE_SERVICES=postgres,ory-postgres,clickhouse,ory-clickhouse,config \
  CONFIRM_RESTORE=baron-sso

# 복구 실행
make restore BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ \
  RESTORE_SERVICES=postgres,ory-postgres,clickhouse,ory-clickhouse,config \
  CONFIRM_RESTORE=baron-sso

# .tar.zst archive를 직접 복구 입력으로 사용
make restore DUMP_FILE=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ.tar.zst \
  RESTORE_SERVICES=all \
  CONFIRM_RESTORE=baron-sso

# report 경로를 명시
make restore BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ \
  CONFIRM_RESTORE=baron-sso \
  RESTORE_REPORT=reports/restore/baron-sso-restore-report.json

# 복구 후 기본 검증
make restore-verify BACKUP=backups/baron-sso-backup-YYYYMMDD-HHMMSSZ

복구는 반드시 빈 volume 또는 restore 전용 stack에서 수행하는 것을 기본 정책으로 합니다. make restoreBACKUP 또는 DUMP_FILE 중 하나와 CONFIRM_RESTORE=baron-sso가 없으면 실패하고, 기본적으로 non-empty Postgres 대상에는 복구하지 않습니다. 승인된 restore rehearsal에서만 ALLOW_NON_EMPTY_RESTORE=true를 사용하세요. DUMP_FILE=.tar.zst 해제도 backup-tools 컨테이너에서 수행하므로 호스트 zstd 설치에 의존하지 않습니다.

make restore는 복구 report를 JSON과 Markdown으로 남깁니다. BACKUP 디렉터리 입력의 기본 JSON report는 <BACKUP>/reports/restore-report.json이고, DUMP_FILE archive 입력의 기본 JSON report는 reports/restore/<archive-name>-restore-report.json입니다. 같은 경로에 .md 확장자의 Markdown 요약도 함께 생성됩니다. RESTORE_REPORT로 직접 지정할 수 있습니다. report에는 입력 archive, 복구 서비스, checksum 검증 상태, 복구 후 대상 row count 비교 결과가 기록됩니다.

config 복구는 운영 파일을 직접 덮어쓰지 않고 config-restored/에 풀어 수동 검토하도록 합니다. migration은 자동 실행하지 않으며, Ory Stack과 backend 기동 후 super admin login, 대표 OIDC login, WORKS comparison dry-run을 통과하기 전까지 WORKS relay를 자동 재개하지 않습니다.

백업/복구 범위

필수 백업 대상:

  • Baron Postgres: users, tenants, user_login_ids, user_groups, RP metadata, WORKS mapping/outbox 등
  • Ory Postgres: Kratos identity/credentials/session, Hydra client/consent/token state, Keto relation tuple
  • Baron ClickHouse: 감사 로그와 RP usage event
  • Ory ClickHouse: Oathkeeper/Ory 계열 접근 로그
  • 설정 snapshot: .env redacted copy, generated Ory config, gateway, compose 파일

기본 제외 대상:

  • Redis: pending login, short code, cache 등 휘발성 데이터이므로 복구 후 재수렴 대상으로 봅니다.
  • 프론트 빌드 산출물: 소스와 이미지 태그로 재생성합니다.
  • coverage, reports, test-results 같은 로컬 개발 산출물

상세 설계와 운영 정책은 docs/backup-restore-design.md를 기준으로 유지합니다.

MCP 서버 (Hydra/Kratos/Keto)

MCP 서버는 기존 Hydra/Kratos에 연결하며 별도 Ory 스택이나 포트를 추가로 띄우지 않습니다.
프로덕션에서는 실행하지 않도록 mcp 프로파일을 로컬에서만 켜세요.

docker compose -f compose.ory.yaml --profile mcp up -d hydra-mcp-server kratos-mcp-server keto-mcp-server
  • MCP 서버는 stdio 기반이라 외부 포트를 열지 않습니다.
  • 최초 실행시거나 빌드된 이미지가 없으면 `docker compose -f mcp/compose.mcp.ory.yaml build' 후에 사용 가능합니다
[mcp_servers.kratos-mcp]
command = "docker"
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "kratos-mcp-server"]

[mcp_servers.kratos-mcp.env]
KRATOS_ADMIN_URL = "http://kratos:4434"

[mcp_servers.hydra-mcp]
command = "docker"
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "hydra-mcp-server"]

[mcp_servers.hydra-mcp.env]
HYDRA_PUBLIC_URL = "http://hydra:4444"
HYDRA_ADMIN_URL = "http://hydra:4445"

[mcp_servers.keto-mcp]
command = "docker"
args = ["compose", "-f", "mcp/compose.mcp.ory.yaml", "run", "--rm", "--no-deps", "keto-mcp-server"]

[mcp_servers.keto-mcp.env]
KETO_READ_URL = "http://keto:4466"
KETO_WRITE_URL = "http://keto:4467"

🌐 i18n 구조 (간략)

  • Root locales: locales/template.toml, locales/ko.toml, locales/en.toml은 현재 userfront와 전역 i18n 검증 기준 리소스입니다.
  • Common locales: common/locales/template.toml, common/locales/ko.toml, common/locales/en.tomlui.common.*, msg.common.* 같은 React 공통 문구 레이어입니다.
  • React(Admin/Dev/Org): adminfront/src/lib/i18n.ts, devfront/src/lib/i18n.ts, orgfront/src/lib/i18n.ts에서 t(key, fallback, vars)를 사용하며 common locale -> app locale override 순서로 TOML을 ?raw 로드합니다.
  • Flutter(User): userfront/lib/i18n.dart에서 tr(key, fallback, params) 사용. locales/*.tomltools/i18n-scanner/gen-flutter-i18n.jsuserfront/lib/i18n_data.dart에 사전 생성합니다.
  • UserFront 동기화 규칙: locales/*.toml을 수정한 뒤에는 반드시 ./scripts/sync_userfront_locales.sh를 실행해 userfront/assets/translations/*.toml과 런타임 번역 리소스를 동기화합니다.
  • 검증: node tools/i18n-scanner/index.jsroot localescommon/locales의 코드-키-로케일 동기화 상태를 함께 점검합니다.

🧪 Code Check CI

워크플로우 파일: .gitea/workflows/code_check.yml

트리거

  • push (dev 브랜치)
  • pull_request (dev 대상)
  • workflow_dispatch (수동 실행)

workflow_dispatch 입력값

  • run_lint: Go/Flutter lint 실행 여부
  • run_backend_tests: backend 테스트 실행 여부
  • run_userfront_tests: userfront 테스트 실행 여부
  • run_userfront_e2e_tests: userfront WASM Playwright E2E 실행 여부
  • run_adminfront_tests: adminfront 테스트 실행 여부
  • run_devfront_tests: devfront 테스트 실행 여부

실행 잡

  • lint
  • backend-tests
  • userfront-tests
  • userfront-e2e-tests
  • adminfront-tests
  • devfront-tests

프런트 테스트 브라우저 프리비저닝 정책

  • userfront-tests
    • flutter test(VM)만 실행
    • locale_storage 정책 테스트는 엔진 단위로 통합되어 별도 브라우저 실행이 필요하지 않음
  • adminfront-tests, devfront-tests
    • Playwright 기반 테스트
    • npx playwright install --with-deps로 브라우저/OS 의존성을 사전 설치

실패 보고서 확인 방법

테스트가 실패하면 다음이 자동 생성됩니다.

  • Job Summary: 실패 원인 요약(Markdown) 즉시 확인
  • Artifact: 상세 로그/리포트 다운로드
    • backend-test-failure-report
    • userfront-test-failure-report
    • adminfront-test-failure-report
    • devfront-test-failure-report

userfront locale_storage 테스트 정책

  • locale_storage_platform_test.dartLocaleStorageEngine 기반 정책 테스트로 통합되었습니다.
  • 일반 flutter test(VM) 실행에 포함되며, 브라우저 전용 kIsWeb 케이스를 사용하지 않습니다.
  • 단일 파일만 확인하려면 다음 명령을 사용합니다.
    • flutter test test/locale_storage_platform_test.dart

userfront WASM Playwright E2E

  • 워크스페이스: userfront-e2e/
  • 빌드+실행:
    • cd userfront-e2e && npm run test:wasm
  • 빌드 결과가 이미 있을 때:
    • cd userfront-e2e && npm test
  • Makefile 타깃:
    • make code-check-userfront-e2e-tests
  • 전수 인벤토리:
    • https://gitea.hmac.kr/baron/baron-sso/wiki/UserFront-WASM-E2E-Inventory.-

로컬 개발 (Manual)

Docker 없이는 개발할 수 없지만 Backend 및 [user/admin/dev]Front 코드는 개발모드로 수정하며 개발가능. 백그라운드로 infra 및 ory stack이 구동중이라는 가정

Backend:

cd backend
go mod tidy
go run cmd/server/main.go

userfront:

cd userfront
flutter pub get
flutter run -d chrome
# 정책: 웹 빌드는 기본적으로 WASM 사용
flutter build web --wasm

adminfront:

cd adminfront
npm install
npm run dev

devfront:

cd devfront
npm install
npm run dev

버그 대응 대원칙 (필수)

  • 모든 버그 수정은 반드시 재현 테스트를 먼저 작성합니다. (Failing test first)
  • 재현 테스트 없이 코드만 먼저 고치는 행위를 금지합니다.
  • 수정 후에는 해당 재현 테스트가 통과할 때까지 반복해서 원인 분석/수정/검증을 수행합니다.
  • “테스트 통과”는 최소 기준입니다. 실제 재현 시나리오(로그인, 새로고침, 리다이렉트 등)까지 확인한 뒤에만 이슈를 종료합니다.
  • 관련 변경이 발생하면 테스트 문서(docs/test-plan/*, docs/trouble-shooting/*)를 함께 업데이트합니다.