{"id":50446754,"url":"https://github.com/devslab-kr/devslab-kit","last_synced_at":"2026-05-31T22:01:13.544Z","repository":{"id":361548472,"uuid":"1250234800","full_name":"devslab-kr/devslab-kit","owner":"devslab-kr","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-31T07:06:49.000Z","size":329,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T07:14:22.849Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devslab-kr.png","metadata":{"files":{"readme":"README.ko.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://devslab.kr"]}},"created_at":"2026-05-26T12:38:58.000Z","updated_at":"2026-05-31T06:58:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/devslab-kr/devslab-kit","commit_stats":null,"previous_names":["devslab-kr/devslab-kit"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/devslab-kr/devslab-kit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fdevslab-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fdevslab-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fdevslab-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fdevslab-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devslab-kr","download_url":"https://codeload.github.com/devslab-kr/devslab-kit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devslab-kr%2Fdevslab-kit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33750474,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-31T22:01:11.902Z","updated_at":"2026-05-31T22:01:13.538Z","avatar_url":"https://github.com/devslab-kr.png","language":"Java","funding_links":["https://devslab.kr"],"categories":[],"sub_categories":[],"readme":"# devslab-kit\n\n[![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/devslab-kit-spring-boot-starter?logo=apachemaven)](https://central.sonatype.com/artifact/kr.devslab/devslab-kit-spring-boot-starter)\n[![Build](https://github.com/devslab-kr/devslab-kit/actions/workflows/build.yml/badge.svg)](https://github.com/devslab-kr/devslab-kit/actions/workflows/build.yml)\n[![codecov](https://codecov.io/gh/devslab-kr/devslab-kit/branch/main/graph/badge.svg)](https://codecov.io/gh/devslab-kr/devslab-kit)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)\n![Java](https://img.shields.io/badge/Java-21%2B-orange)\n![Spring Boot](https://img.shields.io/badge/Spring%20Boot-4.0-6DB33F?logo=springboot)\n\n[English README](README.md) · [변경 이력](CHANGELOG.ko.md) · [ADR](docs/adr)\n\n재사용 가능한 **Spring Boot 4 플랫폼 스타터**. 애플리케이션에 끼워 넣으면 인증, 인가,\n멀티테넌시, 동적 메뉴, 감사 로깅이 자동 구성으로 제공되고, 관리자 REST API와 바로 쓰는\n관리자 콘솔까지 딸려옵니다. 매번 플랫폼 계층을 다시 만드는 대신 각 제품은 자기 도메인에만\n집중할 수 있습니다.\n\n`devslab-kit`은 의도적으로 **제품에 독립적**입니다. `UserId`, `TenantId`, `Permission`,\n`Role`, `Menu`, `Audit` 같은 플랫폼 개념만 알 뿐, 특정 제품의 도메인은 절대 알지 않습니다.\n\n\u003e **상태 — 1.0 이전.** 첫 공개 릴리스 `0.1.0`에 필요한 기능은 모두 완성되었습니다.\n\u003e `0.1.0`부터 Maven Central에 배포하며, 그 전에는 소스 빌드나 `publishToMavenLocal`을\n\u003e 사용하세요.\n\n## 목차\n\n- [기능](#기능) · [요구-사항](#요구-사항) · [설치](#설치)\n- [빠른-시작](#빠른-시작) · [설정](#설정)\n- [모듈](#모듈) · [관리자-rest-api](#관리자-rest-api) · [관리자-콘솔](#관리자-콘솔)\n- [설계-원칙](#설계-원칙) · [문서](#문서) · [소스에서-빌드](#소스에서-빌드)\n- [버전-정책](#버전-정책) · [라이선스](#라이선스)\n\n## 기능\n\n| 영역 | 제공 내용 |\n| --- | --- |\n| **Identity** | 사용자 계정, BCrypt 자격 증명, JWT 발급/파싱, 설정 가능한 로그인 잠금, 비밀번호 강제 변경. |\n| **Access** | 역할, 권한, 주체 **그룹**, 그리고 RBAC 위에 얹은 **ABAC** 정책 SPI(`PolicyEvaluator`). |\n| **멀티테넌시** | *항상 존재하는* 테넌트 컨텍스트(싱글 테넌트라도 추상화를 건너뛰지 않고 default를 resolve), 플러그형 리졸버: `fixed` · `header` · `jwt` · `subdomain`; `single`/`multi` 모드. |\n| **메뉴** | 사용자별로 계산되는 권한 필터링 동적 메뉴 트리. |\n| **감사(Audit)** | `ApplicationEventPublisher` 기반 비동기 감사 로깅, PostgreSQL(JSONB 메타데이터)에 영속화. |\n| **캐시** | 플러그형 캐시 — `in-memory` · `redis` · `none`. Redis 백엔드가 JSON 직렬화를 직접 책임지므로 `Serializable` 구현이나 직렬화기 배선이 필요 없습니다(ADR 0002). 사용자별 메뉴 캐시도 이 공유 매니저를 사용합니다. |\n| **최초 관리자 부트스트랩** | 첫 부팅 시 테넌트, `PLATFORM_ADMIN` 역할, `admin.*` 권한, 관리자 사용자를 멱등하게 생성 — opt-in, 프로퍼티 기반(ADR 0001). |\n| **관리자 REST API** | 위 모든 엔티티 + 진단 + 실시간 설정 뷰를 위한 `/admin/api/v1/**`. |\n| **Override 친화적** | 모든 기본 빈이 `@ConditionalOnMissingBean` — 직접 선언하면 어느 조각이든 교체 가능. |\n| **GraalVM Native** | 리플렉션 중심 설계를 피하고, 샘플 앱이 `nativeCompile`을 검증. |\n\n## 요구 사항\n\n| | |\n| --- | --- |\n| Java | 21+ |\n| Spring Boot | 4.0+ |\n| 데이터 저장소 | PostgreSQL (주 저장소; Flyway 마이그레이션) |\n| 캐시 | Redis (선택 — `cache.type = redis`일 때만) |\n| 웹 스택 | Spring Web MVC (Servlet) + Spring Security |\n\n## 설치\n\n\u003e `0.1.0`부터 Maven Central에서 받을 수 있습니다. 스타터가 플랫폼 전체를 끌어옵니다.\n\n**Gradle (Kotlin DSL)**\n\n```kotlin\nimplementation(\"kr.devslab:devslab-kit-spring-boot-starter:0.1.0\")\n```\n\n**Maven**\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ekr.devslab\u003c/groupId\u003e\n  \u003cartifactId\u003edevslab-kit-spring-boot-starter\u003c/artifactId\u003e\n  \u003cversion\u003e0.1.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n원하는 모듈만? 개별 모듈(예: `devslab-kit-access-core`)에만 의존하거나, `-api` 계약에만\n의존해 직접 구현을 제공할 수도 있습니다.\n\n## 빠른 시작\n\n**1. 스타터 추가** (위 참조).\n\n**2. 설정** — datasource와 플랫폼:\n\n```yaml\nspring:\n  datasource:\n    url: jdbc:postgresql://localhost:5432/app\n    username: app\n    password: app\n  data:\n    redis:\n      host: localhost          # cache.type = redis 일 때만 필요\n\ndevslab:\n  kit:\n    tenant:\n      mode: single             # single | multi\n      resolver: fixed          # fixed | header | jwt | subdomain\n      default-tenant-id: default\n    identity:\n      jwt:\n        secret: ${DEVSLAB_JWT_SECRET}   # HS256용 32바이트 이상 — 운영에서 설정\n        ttl: PT8H\n      max-failed-attempts: 5            # N회 실패 시 계정 잠금\n      lockout-duration: PT15M\n    cache:\n      type: in-memory          # in-memory | redis | none\n    bootstrap:\n      enabled: true            # 첫 부팅 시 최초 관리자 생성\n```\n\n**3. 앱 부팅.** 부트스트랩이 `PLATFORM_ADMIN`을 시드하고, 관리자 REST API가\n`/admin/api/v1/**`에서 동작하며, Flyway가 `platform_*` 테이블을 생성합니다.\n[관리자 콘솔](#관리자-콘솔)을 연결해 로그인하세요.\n\nDocker Compose(Postgres + Redis)와 Testcontainers 기반 테스트까지 갖춘 완전히\n동작하는 설정은 [`devslab-kit-sample-app`](devslab-kit-sample-app)\n([README](devslab-kit-sample-app/README.ko.md))에 있습니다.\n\n## 설정\n\n모든 키는 `devslab.kit.*` 접두사 아래에 있습니다. 기본값 표기.\n\n| 키 | 기본값 | 설명 |\n| --- | --- | --- |\n| `tenant.enabled` | `true` | 테넌트 계층 마스터 스위치. |\n| `tenant.mode` | `single` | `single` 또는 `multi`. |\n| `tenant.default-tenant-id` | `default` | single 모드/폴백에 쓰는 테넌트. |\n| `tenant.resolver` | `fixed` | `fixed` · `header` · `jwt` · `subdomain`. |\n| `identity.jwt.secret` | — | HS256용 32바이트 이상 키. **운영 필수.** |\n| `identity.jwt.issuer` | `devslab-kit` | JWT `iss` 클레임. |\n| `identity.jwt.ttl` | `PT8H` | 토큰 수명(ISO-8601 duration). |\n| `identity.max-failed-attempts` | `5` | 이 횟수만큼 실패하면 계정 잠금. |\n| `identity.lockout-duration` | `PT15M` | 계정 잠금 유지 시간. |\n| `audit.enabled` | `true` | 감사 로깅 토글. |\n| `audit.async-queue-capacity` | `1024` | 비동기 발행기의 bounded 큐. |\n| `menu.enabled` | `true` | 동적 메뉴 토글. |\n| `cache.type` | `in-memory` | `in-memory` · `redis` · `none`. |\n| `cache.ttl` | `PT10M` | 엔트리 TTL(Redis 백엔드에서 사용). |\n| `cache.key-prefix` | `devslab:` | Redis 키 네임스페이스. |\n| `cache.allowed-package` | `kr.devslab` | 안전한 다형 JSON 타이핑 허용 목록. |\n| `bootstrap.enabled` | `false` | 첫 부팅 시 최초 관리자 생성. |\n| `bootstrap.admin-login-id` | `admin` | 최초 관리자 로그인 id. |\n| `bootstrap.admin-password` | — | 비우면 강력한 랜덤 비밀번호를 한 번 로깅. |\n| `bootstrap.must-change-password` | `true` | 첫 로그인 시 변경 강제. |\n\n런타임의 실제 적용값은 `GET /admin/api/v1/settings`에서도 볼 수 있습니다(시크릿 마스킹).\n\n## 모듈\n\n| 모듈 | 역할 |\n| --- | --- |\n| `devslab-kit-core` | 공유 값 객체 (`TenantId`, `UserId`, `PublicId`, …) |\n| `devslab-kit-tenant-{api,core}` | 테넌트 컨텍스트 + 리졸버 |\n| `devslab-kit-identity-{api,core}` | 사용자, 자격 증명, JWT, 로그인 잠금 |\n| `devslab-kit-access-{api,core}` | 역할, 권한, 그룹, ABAC 정책 엔진 |\n| `devslab-kit-menu-{api,core}` | 권한 필터링 동적 메뉴 |\n| `devslab-kit-audit-{api,core}` | 비동기 감사 로깅 |\n| `devslab-kit-cache-{api,core}` | 플러그형 캐시 (in-memory / Redis) |\n| `devslab-kit-admin-api` | 관리자 REST 엔드포인트 |\n| `devslab-kit-autoconfigure` | Spring Boot 자동 구성 |\n| `devslab-kit-spring-boot-starter` | 스타터 — 플랫폼 전체를 끌어옴 |\n| `devslab-kit-sample-app` | 실행 가능한 참조 앱 + 통합 테스트 하니스 (배포 안 함) |\n\n**`-api` vs `-core`.** 각 기능은 얇은 계약 모듈(`-api`)과 기본 구현(`-core`)으로\n나뉩니다. 배터리 포함 기본값을 쓰려면 `-core`에, 직접 구현을 끼우려면 `-api`에만\n의존하세요 — 그러면 자동 구성이 물러납니다(`@ConditionalOnMissingBean`).\n\n## 관리자 REST API\n\n모두 `/admin/api/v1` 아래:\n\n| 리소스 | 엔드포인트 |\n| --- | --- |\n| `auth` | 로그인, 비밀번호 변경 |\n| `users` · `roles` · `permissions` · `groups` | 전체 CRUD + 할당 |\n| `menus` · `tenants` | 메뉴 트리/테넌트 관리 |\n| `policies` | ABAC 정책 목록 + `(subject, action, resource)` 드라이런 |\n| `audit-logs` | 감사 추적 검색/필터 |\n| `diagnostics` | 읽기 전용 로그인/권한/메뉴 가시성 프로브 |\n| `settings` | 실시간 `devslab.kit.*` 뷰(시크릿 마스킹) |\n| `bootstrap/status` | 최초 실행 흐름용 비인증 `{ initialized: boolean }` |\n\n## 관리자 콘솔\n\n[**devslab-kit-admin-ui**](https://github.com/devslab-kr/devslab-kit-admin-ui)는\n이 REST API 위에 바로 올린 Vue 3 + PrimeVue 콘솔입니다 — 로그인, 모든 엔티티 화면,\nABAC 정책 테스트, 감사 로그 검색, 진단, 실시간 설정 뷰까지 모두 한/영 양 언어. 그대로\n쓰거나 직접 UI를 만들 때 참고하세요.\n\n## 설계 원칙\n\n1. **제품에 독립적.** 제품 도메인 타입은 들어오지 않고, 플랫폼 개념만 둡니다.\n2. **계약은 Java API.** GraphQL, WebFlux, RabbitMQ, OAuth2, Spring Session은 선택형 추가 기능이며 절대 core가 아닙니다.\n3. **Override 친화적 자동 구성.** 모든 기본 빈은 `@ConditionalOnMissingBean`.\n4. **TenantContext는 항상 존재** — 싱글 테넌트라도 default를 resolve합니다.\n5. **권한은 메뉴를 모름.** 메뉴는 권한을 참조할 수 있지만 그 반대 의존은 없습니다.\n6. **인증 계정 ≠ 서비스 프로필.** 플랫폼 계정은 로그인 / 상태 / 테넌시만 갖고, 제품별 프로필 데이터는 제품 테이블에 둡니다.\n7. **GraalVM Native 친화적.** 리플렉션 중심 설계를 피합니다.\n\n## 문서\n\n- **아키텍처 결정 기록** — [`docs/adr`](docs/adr): ADR 0001(최초 관리자 부트스트랩),\n  ADR 0002(플러그형 캐시). 한/영 양 언어.\n- **변경 이력** — [`CHANGELOG.ko.md`](CHANGELOG.ko.md) ([English](CHANGELOG.md)).\n\n## 소스에서 빌드\n\n```bash\n./gradlew build                              # 컴파일 + 테스트 (Testcontainers: Postgres + Redis; Docker 필요)\n./gradlew publishToMavenLocal                # 모든 모듈을 ~/.m2 에 설치\n./gradlew :devslab-kit-sample-app:bootRun    # 참조 앱 실행\n```\n\nJava 21(빌드는 GraalVM 21 toolchain 사용)과 통합 테스트용으로 실행 중인 Docker가\n필요합니다.\n\n## 버전 정책\n\n라이브러리 메이저는 Spring Boot 메이저와 정렬됩니다: **`4.x.y`는 Spring Boot 4.x를\n대상으로** 합니다. 릴리스는 [유의적 버전](https://semver.org/lang/ko/)을 따릅니다.\n마이그레이션 노트는 [변경 이력](CHANGELOG.ko.md)을 참고하세요.\n\n## 라이선스\n\n[Apache License 2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevslab-kr%2Fdevslab-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevslab-kr%2Fdevslab-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevslab-kr%2Fdevslab-kit/lists"}