zioinfo-mail/app/.claude/skills/new-features/SKILL.md
DESKTOP-TKLFCPR\ython b217d101f0 feat(harness): Messenger·Manager·ITSM 하네스 확장 + 추가 개발 제안서 3종
## Messenger 하네스 확장
- agents/feature-developer.md — 신규 기능 화면 개발 전문 에이전트
  (DR·네트워크·CSAP·생체인증·오프라인·Kanban·다크모드·멀티기관·인시던트·실시간)
- skills/new-features/SKILL.md — 10가지 신규 기능 React Native 구현 가이드
- messenger-orchestrator description 확장 (신규 기능 트리거 추가)

## Manager 하네스 확장
- agents/roadmap-planner.md — Manager 추가 개발 기획 전문 에이전트
- skills/manager-roadmap/SKILL.md — MG-01~MG-09 페이지 구현 가이드
- manager-orchestrator description 확장 (로드맵/MG 트리거 추가)

## ITSM 하네스 확장
- agents/roadmap-planner.md — ITSM 추가 개발 기획 에이전트
- skills/itsm-roadmap/SKILL.md — I-01~I-10 기능 제안 카탈로그

## 추가 개발 제안서 3종 (docs/)
- MESSENGER_NEXT_FEATURES.md — 모바일 신규 기능 10종 (M-01~M-10)
- ITSM_NEXT_FEATURES.md — ITSM 신규 기능 10종 (I-01~I-10)
- MANAGER_NEXT_FEATURES.md — Manager 신규 페이지 9종 (MG-01~MG-09)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 09:44:52 +09:00

7.9 KiB

name description
new-features GUARDiA Messenger 신규 기능 화면 구현 스킬. DR 모니터링, 네트워크 장비 현황, CSAP 준수율, 오프라인 모드, 생체인증(expo-local-authentication), Kanban SR 보드, 다크모드, 멀티기관 계정, 인시던트 빠른 대응, 실시간 알림 등 10가지 신규 기능을 React Native + Expo SDK 51 패턴으로 구현한다. 다음 상황에서 반드시 사용: (1) 'DR 화면', '네트워크 화면', 'CSAP 화면' 구현 요청; (2) '오프라인 모드', '생체인증', '지문 로그인' 구현; (3) 'Kanban 보드', '다크모드', '멀티기관' 기능 추가; (4) 신규 탭 추가, 새 화면 개발; (5) 다시 실행, 업데이트, 수정, 보완 요청. expo-router 파일 기반 라우팅과 빌드 금기사항을 반드시 준수한다.

GUARDiA Messenger 신규 기능 구현 스킬

구현 패턴 — API 서비스 추가

신규 기능마다 services/api.ts에 함수를 추가한다.

// services/api.ts 추가 패턴
import { axiosInstance } from './api'  // 기존 인스턴스 재사용

// DR API
export const getDRDashboard = () => axiosInstance.get('/api/dr/dashboard')
export const getDRScenarios = () => axiosInstance.get('/api/dr/scenarios')
export const getDRRtoRpo    = () => axiosInstance.get('/api/dr/rto-rpo')

// 네트워크 장비 API
export const getNetworkDevices  = (instId?: number) =>
  axiosInstance.get('/api/network/devices', { params: { inst_id: instId } })
export const getNetworkTopology = (instId?: number) =>
  axiosInstance.get('/api/network/topology', { params: { inst_id: instId } })

// CSAP API
export const getCSAPDashboard = () =>
  axiosInstance.get('/api/compliance/csap/dashboard')
export const getCSAPItems     = () =>
  axiosInstance.get('/api/compliance/csap/items')

기능별 구현 가이드

1. DR 모니터링 화면 (app/(tabs)/dr.tsx)

// 핵심 구조
export default function DRScreen() {
  const [dashboard, setDashboard] = useState<DRDashboard | null>(null)
  const [rtoRpo, setRtoRpo]       = useState<RtoRpo | null>(null)

  // 폴링: 30초마다 갱신 (WS 없이도 준실시간)
  useEffect(() => {
    const load = async () => {
      const [d, r] = await Promise.all([getDRDashboard(), getDRRtoRpo()])
      setDashboard(d.data)
      setRtoRpo(r.data)
    }
    load()
    const timer = setInterval(load, 30_000)
    return () => clearInterval(timer)
  }, [])

  return (
    <ScrollView>
      {/* 시나리오별 PASS/FAIL 배지 */}
      {/* RTO/RPO 목표 vs 실적 바 차트 */}
      {/* 최근 테스트 이력 5건 */}
    </ScrollView>
  )
}

타입 정의:

interface DRDashboard {
  total_scenarios: number
  pass_count:      number
  fail_count:      number
  untested_count:  number
  recent_tests:    DRTestSummary[]
}
interface RtoScenario {
  scenario_name:   string
  rto_target:      number
  rto_actual_avg:  number | null
  rto_met:         boolean | null
  last_test_result: string
}

2. 네트워크 장비 현황 (app/(tabs)/network.tsx)

// 장비 타입별 그룹핑 + 최근 백업 상태 표시
// 설정 변경 감지 시 황색 뱃지
const DeviceCard = ({ device }: { device: NetworkDeviceOut }) => (
  <TouchableOpacity style={s.card}>
    <View style={s.iconRow}>
      <DeviceIcon type={device.device_type} />
      <Text style={s.name}>{device.device_name}</Text>
      {!device.last_backup_at && <Badge color="red" text="미백업" />}
    </View>
    <Text style={s.meta}>{device.vendor} · {device.location}</Text>
  </TouchableOpacity>
)

3. CSAP 준수율 대시보드 (app/(tabs)/csap.tsx)

// 준수율 원형 게이지 + 등급 배지 + FAIL 항목 목록
const GradeColor = { A: '#28a745', B: '#17a2b8', C: '#ffc107', D: '#dc3545' }

const ComplianceGauge = ({ rate, grade }: { rate: number; grade: string }) => (
  <View style={s.gauge}>
    <Text style={[s.rate, { color: GradeColor[grade as keyof typeof GradeColor] }]}>
      {rate}%
    </Text>
    <Text style={[s.grade, { color: GradeColor[grade as keyof typeof GradeColor] }]}>
      {grade}등급
    </Text>
  </View>
)

4. 오프라인 모드 (hooks/useOfflineCache.ts)

import * as SecureStore from 'expo-secure-store'
import NetInfo from '@react-native-community/netinfo'  // 주의: EAS에서 자동 설치됨

export function useOfflineCache<T>(key: string, fetcher: () => Promise<T>) {
  const [data, setData]       = useState<T | null>(null)
  const [isOffline, setOffline] = useState(false)

  useEffect(() => {
    const unsubscribe = NetInfo.addEventListener(state => {
      setOffline(!state.isConnected)
    })
    return () => unsubscribe()
  }, [])

  const load = async () => {
    if (isOffline) {
      const cached = await SecureStore.getItemAsync(key)
      if (cached) setData(JSON.parse(cached))
      return
    }
    try {
      const result = await fetcher()
      setData(result)
      await SecureStore.setItemAsync(key, JSON.stringify(result))
    } catch {
      const cached = await SecureStore.getItemAsync(key)
      if (cached) setData(JSON.parse(cached))
    }
  }
  return { data, isOffline, load }
}

5. 생체인증 (hooks/useBiometric.ts)

import * as LocalAuth from 'expo-local-authentication'

export async function authenticateWithBiometric(): Promise<boolean> {
  const hasHardware = await LocalAuth.hasHardwareAsync()
  const isEnrolled  = await LocalAuth.isEnrolledAsync()
  if (!hasHardware || !isEnrolled) return false

  const result = await LocalAuth.authenticateAsync({
    promptMessage: 'GUARDiA 로그인',
    cancelLabel:   '취소',
    fallbackLabel: '비밀번호 사용',
  })
  return result.success
}

app.json 설정 추가 (플러그인 아님, permissions만):

{
  "expo": {
    "android": {
      "permissions": ["USE_BIOMETRIC", "USE_FINGERPRINT"]
    },
    "ios": {
      "infoPlist": {
        "NSFaceIDUsageDescription": "GUARDiA 빠른 로그인을 위해 Face ID를 사용합니다."
      }
    }
  }
}

6. Kanban SR 보드 (app/(tabs)/kanban.tsx)

// 상태별 컬럼 (가로 스크롤) + 카드 탭으로 상태 변경
const STATUS_COLS = ['RECEIVED', 'IN_PROGRESS', 'PENDING_PM_VALIDATION', 'COMPLETED']
const STATUS_LABEL: Record<string, string> = {
  RECEIVED: '접수', IN_PROGRESS: '진행중',
  PENDING_PM_VALIDATION: 'PM검토', COMPLETED: '완료'
}

// 카드 롱프레스 → ActionSheet로 상태 이동 (드래그&드롭 대신)
// react-native-draggable-flatlist는 EAS 빌드 이슈 가능 → 탭/ActionSheet 방식 권장

7. 다크모드 (hooks/useTheme.ts)

import { useColorScheme } from 'react-native'

export function useTheme() {
  const scheme = useColorScheme()  // 'light' | 'dark' | null
  const isDark = scheme === 'dark'
  return {
    isDark,
    bg:     isDark ? '#1a1a2e' : '#f5f7fa',
    card:   isDark ? '#16213e' : '#ffffff',
    text:   isDark ? '#e0e0e0' : '#333333',
    border: isDark ? '#2d2d44' : '#e2e8f0',
  }
}

8. 탭 메뉴 추가 (app/(tabs)/_layout.tsx)

신규 탭 추가 시 기존 _layout.tsx<Tabs.Screen> 항목만 추가:

<Tabs.Screen name="dr"      options={{ title: 'DR', tabBarIcon: ... }} />
<Tabs.Screen name="network" options={{ title: '네트워크', tabBarIcon: ... }} />
<Tabs.Screen name="csap"    options={{ title: 'CSAP', tabBarIcon: ... }} />
<Tabs.Screen name="kanban"  options={{ title: 'Kanban', tabBarIcon: ... }} />

빌드 안전성 체크리스트

신규 기능 구현 후 반드시 확인:

  • @react-native-community/netinfopackage.json dependencies에 추가 필요 (EAS 빌드 시 자동 설치)
  • expo-local-authentication → Expo SDK 51 기본 포함, 별도 설치 불필요
  • app.json 플러그인 배열에 추가한 항목 없는지 확인 (expo-notifications 제외 원칙)
  • 신규 import 패키지가 package.json에 있는지 확인

상세 화면 목업과 UI 패턴은 references/screen-mockups.md 참조 (필요 시 생성).