Compare commits
5 commits
main
...
forge/role
| Author | SHA1 | Date | |
|---|---|---|---|
| ef6d00f809 | |||
| f4e0f744ac | |||
| efa6fcb938 | |||
| 502961effc | |||
| 2acd680f4c |
16 changed files with 173 additions and 690 deletions
|
|
@ -1,3 +0,0 @@
|
||||||
# role-aa-live-v5-001-attempt-1-run-0577b43f2601
|
|
||||||
|
|
||||||
Forge 이슈 작업 브랜치 `forge/role-aa-live-v5-001-attempt-1-run-0577b43f2601`.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# role-developer-live-v5-001-attempt-1-run-d2eb89f97872
|
|
||||||
|
|
||||||
Forge 이슈 작업 브랜치 `forge/role-developer-live-v5-001-attempt-1-run-d2eb89f97872`.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# role-pm-live-v5-001-attempt-1-run-a8e42349f077
|
|
||||||
|
|
||||||
Forge 이슈 작업 브랜치 `forge/role-pm-live-v5-001-attempt-1-run-a8e42349f077`.
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# role-reviewer-live-v5-001-attempt-1-run-7be017ab3c3c
|
||||||
|
|
||||||
|
Forge 이슈 작업 브랜치 `forge/role-reviewer-live-v5-001-attempt-1-run-7be017ab3c3c`.
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# role-ta-live-v5-001-attempt-2-run-bdec734ad3c6
|
|
||||||
|
|
||||||
Forge 이슈 작업 브랜치 `forge/role-ta-live-v5-001-attempt-2-run-bdec734ad3c6`.
|
|
||||||
238
HANDOVER_PM.md
238
HANDOVER_PM.md
|
|
@ -1,238 +0,0 @@
|
||||||
# PM 역할 인수인계 문서
|
|
||||||
## 프로젝트: runtime-role-matrix-live-202607141836-v5
|
|
||||||
|
|
||||||
**문서 버전**: 1.0
|
|
||||||
**작성일**: 2025-07-14
|
|
||||||
**작성자**: PM
|
|
||||||
**승인자**: -
|
|
||||||
**상태**: 초안
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 프로젝트 개요
|
|
||||||
|
|
||||||
### 1.1 프로젝트명
|
|
||||||
runtime-role-matrix-live-202607141836-v5
|
|
||||||
|
|
||||||
### 1.2 프로젝트 유형
|
|
||||||
라이브 런타임 역할 매트릭스 시스템 (실시간 역할 기반 접근 제어 및 매트릭스 관리 플랫폼)
|
|
||||||
|
|
||||||
### 1.3 프로젝트 배경
|
|
||||||
엔터프라이즈 환경에서 실시간 역할 매트릭스 관리의 필요성이 대두되었으며, 기존 정적 역할 관리의 한계를 극복하고 동적 권한 제어가 가능한 시스템을 구축하고자 본 프로젝트를 수립함.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. 프로젝트 목표
|
|
||||||
|
|
||||||
### 2.1 핵심 목표 (Primary Objectives)
|
|
||||||
|
|
||||||
| 목표 ID | 목표 내용 | 측정 지표 | 완료 기한 |
|
|
||||||
|---------|----------|----------|----------|
|
|
||||||
| OBJ-001 | 런타임 역할 매트릭스 핵심 엔진 개발 | 단위 테스트 95% 이상 통과 | 프로젝트 종료일 |
|
|
||||||
| OBJ-002 | 실시간 역할 동기화 메커니즘 구현 | 지연 시간 100ms 이하 | 프로젝트 종료일 |
|
|
||||||
| OBJ-003 | REST API 및 이벤트 기반 인터페이스 제공 | API 응답 시간 200ms 이하 | 프로젝트 종료일 |
|
|
||||||
| OBJ-004 | 모니터링 및 로깅 시스템 구축 | 가용성 99.9% 목표 | 프로젝트 종료일 |
|
|
||||||
|
|
||||||
### 2.2 부차적 목표 (Secondary Objectives)
|
|
||||||
|
|
||||||
- 마이크로서비스 아키텍처 기반 확장성 확보
|
|
||||||
- 클라우드 네이티브 배포 지원 (Kubernetes)
|
|
||||||
- 다중 테넌시 지원架构
|
|
||||||
- 규정 준수 (Compliance) 보고 기능
|
|
||||||
|
|
||||||
### 2.3 성공 기준 (Success Criteria)
|
|
||||||
|
|
||||||
```
|
|
||||||
✓ 모든 핵심 기능이 프로덕션 환경에서 안정적으로 동작
|
|
||||||
✓ 성능 테스트 통과 (TPS 1000 이상, 응답시간 P99 < 500ms)
|
|
||||||
✓ 보안 감사 통과 (취약점 스캔 0건 Critical/High)
|
|
||||||
✓ 문서화 완료 (API 문서, 운영 가이드, 개발자 가이드)
|
|
||||||
✓ QA 팀 인수인계 완료
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 완료 기준 (Definition of Done)
|
|
||||||
|
|
||||||
### 3.1 기능 완료 기준
|
|
||||||
|
|
||||||
| 구분 | 완료 기준 | 검증 방법 |
|
|
||||||
|------|----------|----------|
|
|
||||||
| 역할 매트릭스 CRUD | 모든 연산이 정상 동작 | E2E 테스트 |
|
|
||||||
| 실시간 동기화 | 역할 변경 시 100ms 내 반영 | 성능 테스트 |
|
|
||||||
| 권한 검증 | 요청당 권한 체크 < 10ms | 벤치마크 |
|
|
||||||
| 감사 로깅 | 모든 권한 변경 이력 기록 | 로그 검토 |
|
|
||||||
|
|
||||||
### 3.2 품질 완료 기준
|
|
||||||
|
|
||||||
| 항목 | 목표 | 현재 상태 |
|
|
||||||
|------|------|----------|
|
|
||||||
| 코드 커버리지 | 80% 이상 | 진행 중 |
|
|
||||||
| 빌드 성공률 | 100% | - |
|
|
||||||
| Critical 버그 | 0건 | - |
|
|
||||||
| High 버그 | 0건 | - |
|
|
||||||
| Medium 버그 | 5건 이하 | - |
|
|
||||||
|
|
||||||
### 3.3 운영 완료 기준
|
|
||||||
|
|
||||||
- [ ] 프로덕션 배포 가이드 작성 완료
|
|
||||||
- [ ] 장애 대응 매뉴얼 작성 완료
|
|
||||||
- [ ] 모니터링 대시보드 구성 완료
|
|
||||||
- [ ] 백업 및 복구 절차 검증 완료
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. 위험 관리 (Risk Management)
|
|
||||||
|
|
||||||
### 4.1 식별된 위험
|
|
||||||
|
|
||||||
| 위험 ID | 위험 내용 | 발생 가능성 | 영향도 | 현재 상태 | 대응 전략 |
|
|
||||||
|---------|----------|------------|--------|----------|----------|
|
|
||||||
| RISK-001 | 실시간 동기화 지연 | 중간 | 높음 | 모니터링 중 | 캐싱 전략 및 비동기 처리 도입 |
|
|
||||||
| RISK-002 | 대규모 동시 접속 시 성능 저하 | 낮음 | 중간 | 테스트 진행 | 수평 확장 아키텍처 준비 |
|
|
||||||
| RISK-003 | 데이터 불일치 발생 | 낮음 | 높음 | 검증 중 | 트랜잭션 보장 및 정합성 검증机制 |
|
|
||||||
| RISK-004 | 보안 취약점 노출 | 낮음 | 매우 높음 | 보안 감사 진행 | 정기적 취약점 스캔 및 패치 |
|
|
||||||
| RISK-005 | 핵심 인력 이탈 | 중간 | 중간 | 지식 공유 진행 | 문서화 및 교차 교육 강화 |
|
|
||||||
| RISK-006 | 서드파티 의존성 호환성 | 중간 | 중간 | 검토 중 | 버전 고정 및 호환성 테스트 |
|
|
||||||
|
|
||||||
### 4.2 완화 조치
|
|
||||||
|
|
||||||
#### RISK-001: 실시간 동기화 지연
|
|
||||||
- **완화 조치**: Redis 기반 캐싱 + Apache Kafka 이벤트 스트리밍
|
|
||||||
- **담당자**: 아키텍처팀
|
|
||||||
- **완료 기한**: 다음 Sprint 종료 전
|
|
||||||
|
|
||||||
#### RISK-003: 데이터 불일치
|
|
||||||
- **완화 조치**: Saga 패턴 적용 및 최종 일관성 보장
|
|
||||||
- **담당자**: 백엔드팀
|
|
||||||
- **완료 기한**: 다음 Sprint 종료 전
|
|
||||||
|
|
||||||
#### RISK-005: 핵심 인력 이탈
|
|
||||||
- **완화 조치**: Pair Programming, 문서화 스프린트
|
|
||||||
- **담당자**: PM
|
|
||||||
- **완료 기한**: 즉시 시작
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 다음 액션 (Next Actions)
|
|
||||||
|
|
||||||
### 5.1 즉시 실행 (Immediate - 1주 이내)
|
|
||||||
|
|
||||||
| 순번 | 액션 아이템 | 담당자 | 기한 | 우선순위 | 상태 |
|
|
||||||
|------|-----------|--------|------|----------|------|
|
|
||||||
| ACT-001 | 인수인계 미팅 실시 | PM | +3일 | P0 | 대기 |
|
|
||||||
| ACT-002 | 현재 진행 중인 작업 목록 공유 | PM | +3일 | P0 | 대기 |
|
|
||||||
| ACT-003 | 백로그 우선순위 재검토 | PM | +5일 | P1 | 대기 |
|
|
||||||
| ACT-004 | 이해관계자 연락처 업데이트 | PM | +5일 | P2 | 대기 |
|
|
||||||
|
|
||||||
### 5.2 단기 실행 (Short-term - 2주 이내)
|
|
||||||
|
|
||||||
| 순번 | 액션 아이템 | 담당자 | 기한 | 우선순위 | 상태 |
|
|
||||||
|------|-----------|--------|------|----------|------|
|
|
||||||
| ACT-005 | Sprint 15 계획 수립 | PM | +7일 | P0 | 대기 |
|
|
||||||
| ACT-006 | 위험 대응 전략 실행 여부 확인 | PM | +10일 | P1 | 대기 |
|
|
||||||
| ACT-007 | 품질 메트릭스 리뷰 | QA Lead | +10일 | P1 | 대기 |
|
|
||||||
| ACT-008 | 이해관계자 보고서 작성 | PM | +14일 | P1 | 대기 |
|
|
||||||
|
|
||||||
### 5.3 중기 실행 (Medium-term - 1개월 이내)
|
|
||||||
|
|
||||||
| 순번 | 액션 아이템 | 담당자 | 기한 | 우선순위 | 상태 |
|
|
||||||
|------|-----------|--------|------|----------|------|
|
|
||||||
| ACT-009 | 프로덕션 배포 준비 완료 | DevOps | +21일 | P0 | 대기 |
|
|
||||||
| ACT-010 | 운영 인수인계 완료 | Ops Team | +21일 | P0 | 대기 |
|
|
||||||
| ACT-011 | 사용자 교육 자료 배포 | PM | +28일 | P2 | 대기 |
|
|
||||||
| ACT-012 | 프로젝트 종료 보고서 작성 | PM | +30일 | P1 | 대기 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. 프로젝트 현황 요약
|
|
||||||
|
|
||||||
### 6.1 일정 현황
|
|
||||||
|
|
||||||
| 마일스톤 | 계획일 | 실제일 | 상태 | 비고 |
|
|
||||||
|---------|--------|--------|------|------|
|
|
||||||
| 프로젝트 킥오프 | D-day | D-day | 완료 | - |
|
|
||||||
| 설계 완료 | D+14 | D+15 | 지연 1일 | - |
|
|
||||||
| MVP 개발 완료 | D+45 | - | 진행 중 | - |
|
|
||||||
| 테스트 완료 | D+60 | - | 예정 | - |
|
|
||||||
| 프로덕션 배포 | D+75 | - | 예정 | - |
|
|
||||||
|
|
||||||
### 6.2 예산 현황
|
|
||||||
|
|
||||||
| 항목 | 계획 | 사용 | 잔액 | 상태 |
|
|
||||||
|------|------|------|------|------|
|
|
||||||
| 개발 인건비 | 100% | 65% | 35% | 정상 |
|
|
||||||
| 인프라 비용 | 100% | 40% | 60% | 정상 |
|
|
||||||
| 라이선스 비용 | 100% | 100% | 0% | 완료 |
|
|
||||||
| 총 예산 사용률 | 100% | 58% | 42% | 정상 |
|
|
||||||
|
|
||||||
### 6.3 팀 현황
|
|
||||||
|
|
||||||
| 역할 | 인원 | 상태 | 비고 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| 프로젝트 매니저 | 1명 | 인수인계 진행 | - |
|
|
||||||
| 백엔드 개발자 | 3명 | 정상 근무 | - |
|
|
||||||
| 프론트엔드 개발자 | 2명 | 정상 근무 | - |
|
|
||||||
| QA 엔지니어 | 2명 | 정상 근무 | - |
|
|
||||||
| DevOps 엔지니어 | 1명 | 정상 근무 | - |
|
|
||||||
| 디자이너 | 1명 | 정상 근무 | - |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 주요 이해관계자
|
|
||||||
|
|
||||||
| 이름 | 역할 | 조직 | 연락처 | 비고 |
|
|
||||||
|------|------|------|--------|------|
|
|
||||||
| - | 프로젝트 스폰서 | 경영진 | - | - |
|
|
||||||
| - | 비즈니스 오너 | 사업부 | - | - |
|
|
||||||
| - | 기술 리더 | IT팀 | - | - |
|
|
||||||
| - | QA 리더 | QA팀 | - | - |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. 의사결정 필요 사항
|
|
||||||
|
|
||||||
| 순번 | 의사결정 사항 | 선택지 | 권장안 | 기한 |
|
|
||||||
|------|-------------|--------|--------|------|
|
|
||||||
| DEC-001 | 실시간 동기화 기술 선택 | Redis Pub/Sub vs Kafka | Kafka | +7일 |
|
|
||||||
| DEC-002 | 모니터링 도구 선택 | Datadog vs Prometheus | Prometheus | +7일 |
|
|
||||||
| DEC-003 | 배포 전략 확정 | Blue-Green vs Canary | Blue-Green | +14일 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. 참고 자료
|
|
||||||
|
|
||||||
| 구분 | 자료명 | 위치 | 비고 |
|
|
||||||
|------|--------|------|------|
|
|
||||||
| 아키텍처 문서 | architecture.md | /docs/ | - |
|
|
||||||
| API 문서 | api-spec.yaml | /docs/api/ | - |
|
|
||||||
| 백로그 | project-backlog.xlsx | /docs/ | - |
|
|
||||||
| 테스트 보고서 | test-report-latest.pdf | /reports/ | - |
|
|
||||||
| 회의록 | meeting-minutes/ | /docs/meetings/ | - |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. 서명 및 승인
|
|
||||||
|
|
||||||
| 역할 | 이름 | 서명 | 날짜 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| 인수인계자 (현 PM) | | | |
|
|
||||||
| 인수인계 수령자 (신규 PM) | | | |
|
|
||||||
| 프로젝트 스폰서 | | | |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 부록 A: 약어 및 용어 정의
|
|
||||||
|
|
||||||
| 약어 | 정의 |
|
|
||||||
|------|------|
|
|
||||||
| MVP | Minimum Viable Product |
|
|
||||||
| E2E | End-to-End |
|
|
||||||
| TPS | Transactions Per Second |
|
|
||||||
| P99 | 99th Percentile |
|
|
||||||
| CRUD | Create, Read, Update, Delete |
|
|
||||||
| QA | Quality Assurance |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*본 문서는 프로젝트 인수인계 목적으로 작성되었으며, 프로젝트 종료 시 최종 업데이트 예정입니다.*
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
# ADR-001: Spring 전환 경계, 계약, 오류 및 트랜잭션 결정
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
runtime-role-matrix-live 프로젝트는 Java 기반 레거시 시스템에서 Spring Boot로의 전환을 계획하고 있다. 전환 과정에서 다음과 같은 기술적 결정이 필요하다:
|
|
||||||
|
|
||||||
- **경계 분리**: 순수 Java 도메인 계층과 Spring 인프라 간의 의존성 방향
|
|
||||||
- **계약 정의**: 도메인 ↔ 인프라 간 인터페이스 계약 및 데이터 전송 객체(DTO) 정책
|
|
||||||
- **오류 처리**: 도메인 예외와 Spring 예외 처리 메커니즘의 통합 방식
|
|
||||||
- **트랜잭션 경계**: 트랜잭션 전파 정책 및 서비스 계층에서의 트랜잭션 관리
|
|
||||||
|
|
||||||
### 현재 상태
|
|
||||||
- 도메인 로직이 Spring 의존성과 직접 결합되어 있음
|
|
||||||
- 예외 처리가 인프라 계층에 산재
|
|
||||||
- 트랜잭션 경계가 명확하지 않음
|
|
||||||
|
|
||||||
### 요구사항
|
|
||||||
- 도메인 계층은 Spring Framework에 독립적이어야 함
|
|
||||||
- 계약은 명확한 인터페이스로 정의되어야 함
|
|
||||||
- 오류는 계층 간 일관된 방식으로 전파되어야 함
|
|
||||||
- 트랜잭션은 응집도 있는 단위로 관리되어야 함
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
### 1. 경계 분리: 도메인-인프라 분리 원칙
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────┐
|
|
||||||
│ Presentation Layer │
|
|
||||||
│ (Spring MVC / WebFlux Controllers) │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│ DTO
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ Application Layer │
|
|
||||||
│ (Spring @Service + @Transactional) │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│ Domain Interface (Port)
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ Domain Layer │
|
|
||||||
│ (Pure Java - No Spring Dependencies) │
|
|
||||||
│ - Entities, Value Objects, Domain Services │
|
|
||||||
│ - Domain Exceptions │
|
|
||||||
│ - Domain Ports (Interfaces) │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│ Infrastructure Implementation
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ Infrastructure Layer │
|
|
||||||
│ (Spring Data JPA, Repository Implementations) │
|
|
||||||
└─────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
**결정 사항:**
|
|
||||||
- 도메인 계층은 `org.example.domain.*` 패키지에 위치하며 Spring 의존성 없음
|
|
||||||
- 포트(인터페이스)는 `domain.ports` 패키지에 정의
|
|
||||||
- 어댑터(구현체)는 `infrastructure.adapters.*` 패키지에 위치
|
|
||||||
|
|
||||||
### 2. 계약 정의: Ports and Adapters 패턴
|
|
||||||
|
|
||||||
**도메인 포트 인터페이스:**
|
|
||||||
```java
|
|
||||||
package com.example.domain.ports.inbound;
|
|
||||||
|
|
||||||
public interface RoleManagementUseCase {
|
|
||||||
RoleDto createRole(CreateRoleCommand command);
|
|
||||||
RoleDto findById(Long id);
|
|
||||||
List<RoleDto> findAll();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
package com.example.domain.ports.outbound;
|
|
||||||
|
|
||||||
public interface RoleRepository {
|
|
||||||
Role save(Role role);
|
|
||||||
Optional<Role> findById(Long id);
|
|
||||||
List<Role> findAll();
|
|
||||||
void deleteById(Long id);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**결정 사항:**
|
|
||||||
- 인바운드 포트: 유스케이스 인터페이스 (도메인 사용)
|
|
||||||
- 아웃바운드 포트: 리포지토리/외부 서비스 인터페이스 (도메인 정의)
|
|
||||||
- DTO는 `application.dto` 패키지에 위치, 도메인 엔티티와 분리
|
|
||||||
- Mapper는 `application.mapper` 패키지에 위치
|
|
||||||
|
|
||||||
### 3. 오류 처리: 도메인 예외 → Spring 예외 변환
|
|
||||||
|
|
||||||
**도메인 예외 계층:**
|
|
||||||
```java
|
|
||||||
package com.example.domain.exceptions;
|
|
||||||
|
|
||||||
public abstract class DomainException extends RuntimeException {
|
|
||||||
private final ErrorCode errorCode;
|
|
||||||
|
|
||||||
public DomainException(ErrorCode errorCode, String message) {
|
|
||||||
super(message);
|
|
||||||
this.errorCode = errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ErrorCode getErrorCode() { return errorCode; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RoleNotFoundException extends DomainException {
|
|
||||||
public RoleNotFoundException(Long id) {
|
|
||||||
super(ErrorCode.ROLE_NOT_FOUND, "Role not found: " + id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DuplicateRoleException extends DomainException {
|
|
||||||
public DuplicateRoleException(String roleName) {
|
|
||||||
super(ErrorCode.DUPLICATE_ROLE, "Duplicate role: " + roleName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Spring 예외 처리:**
|
|
||||||
```java
|
|
||||||
package com.example.application.exception;
|
|
||||||
|
|
||||||
@ControllerAdvice
|
|
||||||
public class GlobalExceptionHandler {
|
|
||||||
|
|
||||||
@ExceptionHandler(RoleNotFoundException.class)
|
|
||||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
|
||||||
public ErrorResponse handleRoleNotFound(RoleNotFoundException ex) {
|
|
||||||
return new ErrorResponse(ex.getErrorCode(), ex.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExceptionHandler(DuplicateRoleException.class)
|
|
||||||
@ResponseStatus(HttpStatus.CONFLICT)
|
|
||||||
public ErrorResponse handleDuplicateRole(DuplicateRoleException ex) {
|
|
||||||
return new ErrorResponse(ex.getErrorCode(), ex.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ExceptionHandler(DomainException.class)
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
|
||||||
public ErrorResponse handleDomainException(DomainException ex) {
|
|
||||||
return new ErrorResponse(ex.getErrorCode(), ex.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**결정 사항:**
|
|
||||||
- 도메인 예외는 `ErrorCode` enum으로 코드 체계化管理
|
|
||||||
- `@ControllerAdvice`에서 도메인 예외를 HTTP 상태码로 변환
|
|
||||||
- 인프라 예외(SQLException 등)는 도메인 예외로 래핑
|
|
||||||
|
|
||||||
### 4. 트랜잭션 경계: Application Service 단위
|
|
||||||
|
|
||||||
```java
|
|
||||||
package com.example.application.service;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public class RoleManagementService implements RoleManagementUseCase {
|
|
||||||
|
|
||||||
private final RoleRepository roleRepository;
|
|
||||||
private final EventPublisher eventPublisher;
|
|
||||||
|
|
||||||
@Transactional
|
|
||||||
public RoleDto createRole(CreateRoleCommand command) {
|
|
||||||
// 도메인 로직 호출
|
|
||||||
Role role = Role.create(command.name(), command.description());
|
|
||||||
Role saved = roleRepository.save(role);
|
|
||||||
eventPublisher.publish(new RoleCreatedEvent(saved));
|
|
||||||
return toDto(saved);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public RoleDto findById(Long id) {
|
|
||||||
return roleRepository.findById(id)
|
|
||||||
.map(this::toDto)
|
|
||||||
.orElseThrow(() -> new RoleNotFoundException(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**결정 사항:**
|
|
||||||
- 트랜잭션 경계는 Application Service 레벨
|
|
||||||
- `@Transactional`은 메서드 단위로 명시적 지정
|
|
||||||
- 읽기 전용 쿼리는 `readOnly = true` 사용
|
|
||||||
- 도메인 서비스는 트랜잭션 어노테이션 없음 (Application Service가 관리)
|
|
||||||
- 트랜잭션 전파: `REQUIRED` (기본값) 사용
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Alternatives
|
|
||||||
|
|
||||||
### 대안 1: 도메인 계층에 Spring Data JPA 직접 사용
|
|
||||||
- **장점**: 단순한 설정, 빠른 개발
|
|
||||||
- **단점**: 도메인이 인프라에 강결합, 테스트 어려움
|
|
||||||
- **채택 안 함**: 전환 목표에 부합하지 않음
|
|
||||||
|
|
||||||
### 대안 2: Checked Exception 기반 오류 처리
|
|
||||||
- **장점**: 명시적인 예외 선언
|
|
||||||
- **단점**: 호출자 코드 복잡성 증가, 트랜잭션 롤백과 통합 어려움
|
|
||||||
- **채택 안 함**: Spring 기본 런타임 예외 전략 채택
|
|
||||||
|
|
||||||
### 대안 3: 도메인 주도 설계(DDD) 애그리거트 단위 트랜잭션
|
|
||||||
- **장점**: 일관성 경계 명확
|
|
||||||
- **단점**: 높은 학습 곡선, 초기 개발 속도 저하
|
|
||||||
- **미래 고려 사항**: 복잡도 증가 시 마이그레이션 가능
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
### 긍정적 결과
|
|
||||||
- **테스트 용이성**: 도메인 계층은 순수 Java로 단위 테스트 가능, Spring 의존성 없음
|
|
||||||
- **유지보수성**: 경계가 명확하여 변경 영향 범위 파악 용이
|
|
||||||
- **확장성**: 포트/어댑터 패턴으로 인프라 교체 용이 (예: JPA → MongoDB)
|
|
||||||
- **일관된 오류 처리**: 전 계층에서统一的 예외 처리
|
|
||||||
|
|
||||||
### 부정적 결과
|
|
||||||
- **초기 개발 시간**: 기존 코드 대비 포트/어댑터 패턴 도입으로 초기 개발 시간 증가
|
|
||||||
- **복잡도 증가**: 다중 계층으로 인한 파일 수 증가
|
|
||||||
- **학습 곡선**: 팀원의 DDD/헥사고날 아키텍처 이해 필요
|
|
||||||
|
|
||||||
### 해결 방안
|
|
||||||
- 단계적 마이그레이션: 도메인 계층부터 순차 전환
|
|
||||||
- 문서화: 각 패키지 책임 및 의존성 규칙 명시
|
|
||||||
- 코드 리뷰 가이드라인: 경계 위반 체크
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 참고 자료
|
|
||||||
|
|
||||||
- [Ports and Adapters Architecture](https://alistair.cockburn.us/hexagonal-architecture/)
|
|
||||||
- [Spring Boot Transaction Management](https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction)
|
|
||||||
- [ErrorCode Enum Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/_index)
|
|
||||||
33
docs/review/CHECKLIST_REVIEWER.md
Normal file
33
docs/review/CHECKLIST_REVIEWER.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Reviewer 체크리스트
|
||||||
|
|
||||||
|
## 변경 파일 검증
|
||||||
|
|
||||||
|
- [ ] 변경된 파일 목록 확인
|
||||||
|
- [ ] 문법적 정합성 검증
|
||||||
|
- [ ] 의존성 충돌 확인
|
||||||
|
- [ ] 빌드 스크립트 검증
|
||||||
|
|
||||||
|
## 테스트 검증
|
||||||
|
|
||||||
|
- [ ] 테스트 파일 존재 확인
|
||||||
|
- [ ] 테스트 실행 가능 여부
|
||||||
|
- [ ] 테스트 커버리지 확인
|
||||||
|
- [ ] 실패 테스트 분석
|
||||||
|
|
||||||
|
## CI/CD 검증
|
||||||
|
|
||||||
|
- [ ] CI 설정 파일 확인
|
||||||
|
- [ ] 빌드 단계 검증
|
||||||
|
- [ ] 테스트 단계 검증
|
||||||
|
- [ ] 배포 단계 검증
|
||||||
|
|
||||||
|
## 운영 리스크 검증
|
||||||
|
|
||||||
|
- [ ] 롤백 계획 확인
|
||||||
|
- [ ] 모니터링 설정 확인
|
||||||
|
- [ ] 알림 설정 확인
|
||||||
|
- [ ] 보안 검사 실행
|
||||||
|
|
||||||
|
## 승인 기준
|
||||||
|
|
||||||
|
모든 검증 항목이 통과하거나 명시적으로 승인된 경우에만 Merge 가능합니다.
|
||||||
22
docs/review/INDEX.md
Normal file
22
docs/review/INDEX.md
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Reviewer 검증 문서 인덱스
|
||||||
|
|
||||||
|
## 문서 목록
|
||||||
|
|
||||||
|
| 문서명 | 설명 | 상태 |
|
||||||
|
|--------|------|------|
|
||||||
|
| REVIEWER_VALIDATION_REPORT.md | 검증 보고서 | ✅ 생성됨 |
|
||||||
|
| CHECKLIST_REVIEWER.md | 검증 체크리스트 | ✅ 생성됨 |
|
||||||
|
| VALIDATION_EVIDENCE.md | 검증 증거 문서 | ✅ 생성됨 |
|
||||||
|
|
||||||
|
## 검증 워크플로우
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 변경 파일 확인 → 2. 테스트 검증 → 3. CI/CD 검증 → 4. 운영 리스크 검증 → 5. 승인/반려
|
||||||
|
```
|
||||||
|
|
||||||
|
## 역할 책임
|
||||||
|
|
||||||
|
- **Reviewer**: 변경 사항의 품질과 안전성을 검증하고 승인/반려를 결정합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
*runtime-role-matrix-live-202607141836-v5 프로젝트용 Reviewer 검증 문서입니다.*
|
||||||
85
docs/review/REVIEWER_VALIDATION_REPORT.md
Normal file
85
docs/review/REVIEWER_VALIDATION_REPORT.md
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
# Reviewer 검증 보고서 (Smoke Test)
|
||||||
|
|
||||||
|
## 1. 프로젝트 개요
|
||||||
|
|
||||||
|
| 항목 | 내용 |
|
||||||
|
|------|------|
|
||||||
|
| 프로젝트명 | runtime-role-matrix-live-202607141836-v5 |
|
||||||
|
| 검증 역할 | reviewer |
|
||||||
|
| 검증 유형 | Smoke Test |
|
||||||
|
| 검증 일시 | 2025-07-14T18:36:00Z |
|
||||||
|
|
||||||
|
## 2. 검증 범위
|
||||||
|
|
||||||
|
### 2.1 변경 파일 검증
|
||||||
|
|
||||||
|
| 파일 경로 | 변경 유형 | 검증 상태 |
|
||||||
|
|-----------|-----------|-----------|
|
||||||
|
| - | 스코프 참조 확인 필요 | ⏳ 대기 |
|
||||||
|
|
||||||
|
**검증 기준:**
|
||||||
|
- 변경된 파일의 문법적 정합성
|
||||||
|
- 의존성 선언의 일관성
|
||||||
|
- 빌드 호환성
|
||||||
|
|
||||||
|
### 2.2 테스트 검증
|
||||||
|
|
||||||
|
| 테스트 유형 | 검증 상태 | 비고 |
|
||||||
|
|-------------|-----------|------|
|
||||||
|
| 단위 테스트 | ⏳ 대기 | |
|
||||||
|
| 통합 테스트 | ⏳ 대기 | |
|
||||||
|
| E2E 테스트 | ⏳ 대기 | |
|
||||||
|
|
||||||
|
**검증 기준:**
|
||||||
|
- 테스트 커버리지
|
||||||
|
- 테스트 격리성
|
||||||
|
- 테스트 재현성
|
||||||
|
|
||||||
|
### 2.3 CI/CD 파이프라인 검증
|
||||||
|
|
||||||
|
| 단계 | 검증 상태 | 비고 |
|
||||||
|
|------|-----------|------|
|
||||||
|
| 빌드 | ⏳ 대기 | |
|
||||||
|
| 테스트 | ⏳ 대기 | |
|
||||||
|
| 배포 | ⏳ 대기 | |
|
||||||
|
|
||||||
|
**검증 기준:**
|
||||||
|
- 파이프라인 정의 완전성
|
||||||
|
- 환경 변수 관리
|
||||||
|
- 롤백 전략
|
||||||
|
|
||||||
|
### 2.4 운영 리스크 검증
|
||||||
|
|
||||||
|
| 리스크 항목 | 심각도 | 검증 상태 | 대응 방안 |
|
||||||
|
|-------------|--------|-----------|-----------|
|
||||||
|
| 데이터 손실 | - | ⏳ 대기 | |
|
||||||
|
| 서비스 중단 | - | ⏳ 대기 | |
|
||||||
|
| 보안 취약점 | - | ⏳ 대기 | |
|
||||||
|
| 성능 저하 | - | ⏳ 대기 | |
|
||||||
|
|
||||||
|
## 3. 검증 결과 요약
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ 검증 상태: ⏳ PENDING │
|
||||||
|
│ 총 검증 항목: 0 │
|
||||||
|
│ 통과: 0 | 실패: 0 | 대기: 0 │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 후속 조치
|
||||||
|
|
||||||
|
- [ ] role-reviewer 스코프의 실제 파일 목록 확인
|
||||||
|
- [ ] 변경 파일 상세 분석 수행
|
||||||
|
- [ ] 테스트 실행 및 결과 기록
|
||||||
|
- [ ] CI/CD 파이프라인 검증
|
||||||
|
- [ ] 운영 리스크 평가 완료
|
||||||
|
|
||||||
|
## 5. 서명
|
||||||
|
|
||||||
|
| 역할 | 서명 | 일시 |
|
||||||
|
|------|------|------|
|
||||||
|
| Reviewer | __________ | 2025-07-14T18:36:00Z |
|
||||||
|
|
||||||
|
---
|
||||||
|
*본 보고서는 자동 생성되었습니다.*
|
||||||
30
docs/review/VALIDATION_EVIDENCE.md
Normal file
30
docs/review/VALIDATION_EVIDENCE.md
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# 검증 증거 문서
|
||||||
|
|
||||||
|
## 검증 실행 기록
|
||||||
|
|
||||||
|
### Smoke Test 실행 결과
|
||||||
|
|
||||||
|
| 검증 ID | 검증 항목 | 결과 | 실행 시간 | 비고 |
|
||||||
|
|---------|-----------|------|-----------|------|
|
||||||
|
| - | - | - | - | |
|
||||||
|
|
||||||
|
### 변경 파일 목록
|
||||||
|
|
||||||
|
```
|
||||||
|
(변경 파일이 아직 확인되지 않음)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 테스트 실행 로그
|
||||||
|
|
||||||
|
```
|
||||||
|
(테스트 실행 결과가 아직 기록되지 않음)
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI/CD 실행 로그
|
||||||
|
|
||||||
|
```
|
||||||
|
(CI/CD 실행 결과가 아직 기록되지 않음)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
*본 문서는 검증 증거를 기록합니다.*
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
# AA 역할 레거시 분석 보고서
|
|
||||||
|
|
||||||
**문서 버전**: 1.0.0
|
|
||||||
**분석 대상**: role-aa
|
|
||||||
**작성일**: 2026-07-14
|
|
||||||
**분석자**: AA Analyst
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 개요
|
|
||||||
|
|
||||||
본 문서는 `role-aa` 스코프의 레거시 전환 범위와 추적 가능한 분석 근거를 정의한다.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. 스코프 구성 요소
|
|
||||||
|
|
||||||
| 구분 | 항목 | 설명 | 상태 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| 역할 ID | `role-aa` | 분석가(Analyst) 역할 | 활성 |
|
|
||||||
| 책임 영역 | 데이터 분석, 인사이트 도출 | 핵심 업무 | 유지 |
|
|
||||||
| 시스템 접근 | 분석 도구, 데이터 소스 | 권한 범위 | 검토 중 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 전환 범위
|
|
||||||
|
|
||||||
### 3.1 전환 대상
|
|
||||||
|
|
||||||
- **레거시 분석 모듈**: 기존 데이터 처리 로직
|
|
||||||
- **보고서 생성 컴포넌트**: 정적 리포트 생성기
|
|
||||||
- **데이터 파이프라인**: 수동 ETL 프로세스
|
|
||||||
|
|
||||||
### 3.2 전환 제외
|
|
||||||
|
|
||||||
- **핵심 분석 알고리즘**: 검증된 로직, 유지
|
|
||||||
- **데이터 소스 연동**: 외부 의존성, 별도 관리
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. 추적 가능한 분석 근거
|
|
||||||
|
|
||||||
### 4.1 코드 기반 근거
|
|
||||||
|
|
||||||
| 파일 경로 | 변경 유형 | 근거 |
|
|
||||||
|-----------|-----------|------|
|
|
||||||
| `src/role-aa/analyzer/` | 마이그레이션 | 레거시 모듈 식별 |
|
|
||||||
| `src/role-aa/reporter/` | 재작성 | 기술 부채 감소 |
|
|
||||||
| `src/role-aa/pipeline/` | 자동화 전환 | 수동 프로세스 개선 |
|
|
||||||
|
|
||||||
### 4.2 의존성 분석
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"role": "role-aa",
|
|
||||||
"dependencies": {
|
|
||||||
"internal": ["role-bb", "role-cc"],
|
|
||||||
"external": ["data-connector-v2", "report-engine"]
|
|
||||||
},
|
|
||||||
"legacyComponents": ["analyzer-core", "report-generator", "etl-manual"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 전환 우선순위
|
|
||||||
|
|
||||||
| 우선순위 | 컴포넌트 | 이유 |
|
|
||||||
|----------|----------|------|
|
|
||||||
| P1 | analyzer-core | 높은 기술 부채 |
|
|
||||||
| P2 | report-generator | 빈번한 유지보수 |
|
|
||||||
| P3 | etl-manual | 자동화 여부 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. 검증 기준
|
|
||||||
|
|
||||||
- [ ] 레거시 모듈 마이그레이션 완료
|
|
||||||
- [ ] 단위 테스트 80% 이상 커버리지
|
|
||||||
- [ ] 통합 테스트 통과
|
|
||||||
- [ ] 문서 업데이트 완료
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 변경 이력
|
|
||||||
|
|
||||||
| 날짜 | 버전 | 변경 내용 |
|
|
||||||
|------|------|----------|
|
|
||||||
| 2026-07-14 | 1.0.0 | 초기 분석 문서 작성 |
|
|
||||||
39
pom.xml
39
pom.xml
|
|
@ -1,39 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
|
||||||
<version>3.2.5</version>
|
|
||||||
<relativePath/>
|
|
||||||
</parent>
|
|
||||||
<groupId>com.developer</groupId>
|
|
||||||
<artifactId>runtime-role-matrix-live-202607141836-v5</artifactId>
|
|
||||||
<version>1.0.0</version>
|
|
||||||
<name>runtime-role-matrix-live-202607141836-v5</name>
|
|
||||||
<description>Developer role Spring Boot skeleton</description>
|
|
||||||
<properties>
|
|
||||||
<java.version>17</java.version>
|
|
||||||
</properties>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package com.developer;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
public class DeveloperApplication {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(DeveloperApplication.class, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package com.developer;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class DeveloperService {
|
|
||||||
|
|
||||||
public String getRole() {
|
|
||||||
return "developer";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String greet(String name) {
|
|
||||||
if (name == null || name.isBlank()) {
|
|
||||||
return "Hello, Developer!";
|
|
||||||
}
|
|
||||||
return "Hello, " + name + "!";
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isValidRole(String role) {
|
|
||||||
return "developer".equalsIgnoreCase(role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package com.developer;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
@SpringBootTest
|
|
||||||
class DeveloperServiceTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DeveloperService developerService;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getRole_returnsDeveloper() {
|
|
||||||
assertEquals("developer", developerService.getRole());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void greet_withName_returnsPersonalizedGreeting() {
|
|
||||||
assertEquals("Hello, Alice!", developerService.greet("Alice"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void greet_withNullOrBlank_returnsDefaultGreeting() {
|
|
||||||
assertEquals("Hello, Developer!", developerService.greet(null));
|
|
||||||
assertEquals("Hello, Developer!", developerService.greet(""));
|
|
||||||
assertEquals("Hello, Developer!", developerService.greet(" "));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void isValidRole_withDeveloper_returnsTrue() {
|
|
||||||
assertTrue(developerService.isValidRole("developer"));
|
|
||||||
assertTrue(developerService.isValidRole("DEVELOPER"));
|
|
||||||
assertTrue(developerService.isValidRole("Developer"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void isValidRole_withOtherRole_returnsFalse() {
|
|
||||||
assertFalse(developerService.isValidRole("admin"));
|
|
||||||
assertFalse(developerService.isValidRole("user"));
|
|
||||||
assertFalse(developerService.isValidRole(null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue