zioinfo-mail/workspace/zioinfo-web/frontend/src/pages/Home.jsx
DESKTOP-TKLFCPR\ython b4f9bcfc6b feat(design): Variant UI style applied - layout maintained
Based on Variant ZIO Complete Website design (screenshot1-13):
- tokens.css: color palette (#003366, #005A8C, #00A0C8), Pretendard font, 4px grid
- global.css: section-label-v, section-title-v, v-card, v-stats-section, btn-v-*
- Home.jsx: SVG icons for Business cards, Variant-style section labels, dark navy KPI section
- Common.css: page-hero improved (navy gradient), section labels cyan, sub-nav refined

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

380 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
import './Home.css';
import { useSeoMeta } from '../hooks/useSeoMeta';
/* ── 히어로 슬라이드 데이터 ─────────────────────────────── */
const SLIDES = [
{
title: 'AI 기반 인프라\n자율 운영 플랫폼',
sub: 'GUARDiA ITSM — 메신저 한 줄로 1,000개 관공서 인프라를 자동화',
cta: { label: 'GUARDiA 알아보기', path: '/solution/guardia' },
badge: 'NEW',
bg: 'slide-1',
},
{
title: '공공기관 전문\nIT 솔루션 기업',
sub: '20년 경험의 지오정보기술이 최첨단 AI 기술로 여러분과 함께합니다',
cta: { label: '회사소개 보기', path: '/company/greeting' },
badge: '',
bg: 'slide-2',
},
{
title: '에이전트리스\n자동화 혁신',
sub: '대상 서버에 소프트웨어 설치 없이 SSH만으로 레거시 인프라를 관리',
cta: { label: '도입 문의', path: '/support/contact' },
badge: '',
bg: 'slide-3',
},
];
/* ── 핵심 사업 영역 (Variant SVG 아이콘) ─────────────────── */
const BUSINESS = [
{
svg: (
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="4" width="10" height="10" rx="2.5" fill="#005A8C" opacity=".9"/>
<rect x="18" y="4" width="10" height="10" rx="2.5" fill="#00A0C8" opacity=".8"/>
<rect x="4" y="18" width="10" height="10" rx="2.5" fill="#00A0C8" opacity=".6"/>
<rect x="18" y="18" width="10" height="10" rx="2.5" fill="#003366" opacity=".85"/>
<path d="M14 9h4M9 14v4M23 14v4M14 23h4" stroke="#fff" strokeWidth="1.5" strokeLinecap="round"/>
</svg>
),
title: 'AI 자동화',
desc: 'GUARDiA ITSM 플랫폼으로 레거시 인프라 운영을 완전 자동화',
path: '/solution/guardia',
},
{
svg: (
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 3L28 9v14L16 29 4 23V9L16 3z" fill="#E8F0F8" stroke="#005A8C" strokeWidth="1.5"/>
<path d="M16 3v26M4 9l12 6 12-6" stroke="#005A8C" strokeWidth="1.5" strokeLinecap="round"/>
<circle cx="16" cy="15" r="3" fill="#00A0C8"/>
</svg>
),
title: 'SI 구축',
desc: '공공기관 정보화사업 시스템 통합 및 맞춤형 개발',
path: '/business/reference',
},
{
svg: (
<svg viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="5" width="26" height="22" rx="3" fill="#E8F0F8" stroke="#005A8C" strokeWidth="1.5"/>
<path d="M9 22l4-6 4 3 4-8 4 5" stroke="#00A0C8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
<circle cx="9" cy="22" r="2" fill="#003366"/>
<circle cx="13" cy="16" r="2" fill="#005A8C"/>
<circle cx="17" cy="19" r="2" fill="#00A0C8"/>
<circle cx="21" cy="11" r="2" fill="#005A8C"/>
<circle cx="25" cy="16" r="2" fill="#003366"/>
</svg>
),
title: 'ERP·CRM·BI',
desc: '기업 경영 효율화를 위한 통합 솔루션 패키지',
path: '/solution/erp',
},
];
/* ── GUARDiA 핵심 기능 (SVG 아이콘) ─────────────────────── */
const GUARDIA_FEATURES = [
{
svg: <svg viewBox="0 0 24 24" fill="none"><path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" stroke="#00A0C8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
label: 'ChatOps', desc: '메신저 명령으로 인프라 제어',
},
{
svg: <svg viewBox="0 0 24 24" fill="none"><rect x="2" y="3" width="20" height="14" rx="2" stroke="#00A0C8" strokeWidth="2"/><path d="M8 21h8M12 17v4" stroke="#00A0C8" strokeWidth="2" strokeLinecap="round"/><path d="M6 9h.01M9 9h6" stroke="#00A0C8" strokeWidth="2" strokeLinecap="round"/></svg>,
label: '에이전트리스', desc: 'SSH만으로 에이전트 설치 없음',
},
{
svg: <svg viewBox="0 0 24 24" fill="none"><path d="M3 3h7v7H3zM14 3h7v7h-7zM3 14h7v7H3zM14 14h7v7h-7z" stroke="#00A0C8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
label: '통합 ITSM', desc: 'SR·인시던트·변경·SLA 통합',
},
{
svg: <svg viewBox="0 0 24 24" fill="none"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" stroke="#00A0C8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
label: '엔터프라이즈 보안', desc: 'MFA·PAM·Zero Trust',
},
];
/* ── KPI 수치 ────────────────────────────────────────────── */
const KPIS = [
{ value: '1,000+', label: '관리 가능 기관 수' },
{ value: '99.9%', label: '시스템 가용성' },
{ value: '70%', label: 'SR 처리 시간 단축' },
{ value: '20년+', label: 'IT 사업 경험' },
];
export default function Home() {
useSeoMeta({
title: 'AI 기반 인프라 자율 운영 플랫폼 GUARDiA ITSM',
description: '(주)지오정보기술은 메신저 한 줄 명령으로 공공기관 레거시 IT 인프라를 자동 운영하는 GUARDiA ITSM을 개발합니다. 에이전트 설치 없이 SSH/SFTP로 1,000개 기관 운영 자동화.',
path: '/',
keywords: '지오정보기술, GUARDiA ITSM, 공공기관 인프라 자동화, AI 운영, ChatOps',
});
const [slide, setSlide] = useState(0);
const [paused, setPaused] = useState(false);
const [news, setNews] = useState([]);
const timerRef = useRef(null);
/* 슬라이드 자동 전환 */
useEffect(() => {
if (paused) return;
timerRef.current = setInterval(() => setSlide(s => (s + 1) % SLIDES.length), 5000);
return () => clearInterval(timerRef.current);
}, [paused]);
/* 소식 로드 */
useEffect(() => {
axios.get('/api/news?size=4').then(r => setNews(r.data.content || [])).catch(() => {});
}, []);
const prevSlide = () => setSlide(s => (s - 1 + SLIDES.length) % SLIDES.length);
const nextSlide = () => setSlide(s => (s + 1) % SLIDES.length);
const current = SLIDES[slide];
return (
<main id="main-content">
{/* ── 히어로 슬라이더 ──────────────────────────────── */}
<section className={`hero hero-${slide}`}
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
aria-label="메인 슬라이더">
<div className="hero-overlay" />
<div className="hero-content container">
{current.badge && (
<span className="hero-badge">{current.badge}</span>
)}
<h1 className="hero-title">
{current.title.split('\n').map((line, i) => (
<React.Fragment key={i}>{line}{i < current.title.split('\n').length - 1 && <br/>}</React.Fragment>
))}
</h1>
<p className="hero-sub">{current.sub}</p>
<div className="hero-actions">
<Link to={current.cta.path} className="btn btn-white btn-lg">
{current.cta.label}
</Link>
<Link to="/support/contact" className="btn btn-outline hero-btn-contact btn-lg">
무료 상담
</Link>
</div>
</div>
{/* 슬라이드 컨트롤 */}
<div className="hero-controls">
<button onClick={prevSlide} aria-label="이전 슬라이드" className="hero-arrow"></button>
<div className="hero-dots">
{SLIDES.map((_, i) => (
<button key={i} className={`hero-dot ${i === slide ? 'active' : ''}`}
onClick={() => setSlide(i)}
aria-label={`슬라이드 ${i + 1}`} />
))}
</div>
<button onClick={nextSlide} aria-label="다음 슬라이드" className="hero-arrow"></button>
<button onClick={() => setPaused(v => !v)} className="hero-pause" aria-label={paused ? '재생' : '일시정지'}>
{paused ? '▶' : '⏸'}
</button>
</div>
{/* 스크롤 유도 */}
<div className="hero-scroll-hint" aria-hidden="true">
<span>SCROLL</span>
<div className="scroll-line" />
</div>
</section>
{/* ── 핵심 사업 영역 (Variant 스타일) ─────────────────── */}
<section className="section py-section bg-light">
<div className="container">
<div style={{ textAlign: 'center', marginBottom: '48px' }}>
<span className="section-label-v">저희 서비스</span>
<h2 className="section-title-v">핵심 전문성 솔루션</h2>
<div className="section-underline" />
</div>
<div className="grid-3">
{BUSINESS.map((b, i) => (
<Link to={b.path} key={i} style={{ textDecoration: 'none' }}>
<div className="v-card" style={{ height: '100%' }}>
<div className="v-icon-box">{b.svg}</div>
<h3 className="v-card-title">{b.title}</h3>
<p className="v-card-desc">{b.desc}</p>
<span style={{ display: 'inline-block', marginTop: '16px', fontSize: '14px',
fontWeight: 700, color: 'var(--v-cyan-500)', letterSpacing: '.02em' }}>
자세히 보기
</span>
</div>
</Link>
))}
</div>
</div>
</section>
{/* ── GUARDiA 솔루션 하이라이트 (Variant 스타일) ────────── */}
<section className="section section-guardia py-section">
<div className="container">
<div className="guardia-inner">
<div className="guardia-text">
<span className="section-label-v" style={{ textAlign: 'left' }}>대표 솔루션</span>
<h2 className="section-title-v" style={{ textAlign: 'left' }}>
GUARDiA ITSM<br/>
<span style={{ color: 'var(--v-cyan-500)' }}>AI 기반 인프라 자율 운영</span>
</h2>
<div className="section-underline left" />
<p className="guardia-desc">
1,000 이상의 관공서 레거시 인프라를 메신저 명령으로 제어하는
온프레미스 AI ChatOps 플랫폼. 에이전트 설치 없이 SSH/SFTP만으로
배포·운영을 완전 자동화합니다.
</p>
<div className="guardia-features">
{GUARDIA_FEATURES.map((f, i) => (
<div key={i} className="guardia-feature">
<span className="feature-icon" style={{ background: 'var(--v-blue-100)', borderRadius: '8px', padding: '8px', display: 'flex', alignItems: 'center', justifyContent: 'center', width: '40px', height: '40px', flexShrink: 0 }}>
{f.svg}
</span>
<div>
<strong style={{ color: 'var(--v-navy-700)' }}>{f.label}</strong>
<p style={{ color: 'var(--v-gray-600)' }}>{f.desc}</p>
</div>
</div>
))}
</div>
<div className="guardia-actions" style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
<Link to="/solution/guardia" className="btn-v-primary">
GUARDiA 상세보기
</Link>
<Link to="/support/contact" className="btn-v-ghost">
도입 문의
</Link>
</div>
</div>
<div className="guardia-visual">
<div className="guardia-mockup">
<div className="mockup-bar">
<span/><span/><span/>
</div>
<div className="mockup-content">
<div className="mockup-chat">
<div className="chat-msg bot">
<span className="chat-name">GUARDiA Bot</span>
<p>안녕하세요! 무엇을 도와드릴까요?</p>
</div>
<div className="chat-msg user">
<p>/deploy web-server-01</p>
</div>
<div className="chat-msg bot">
<p> web-server-01 배포 완료<br/>헬스체크: 정상 | 소요: 42</p>
</div>
<div className="chat-msg user">
<p>/status</p>
</div>
<div className="chat-msg bot">
<p>📊 시스템 현황<br/>SR 처리중: 3 | SLA 준수율: 98.2%<br/>서버 이상: 0 </p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{/* ── KPI 수치 (Variant 다크 네이비) ─────────────────── */}
<section className="v-stats-section">
<div className="container">
<div style={{ textAlign: 'center', marginBottom: '56px' }}>
<span className="section-label-v" style={{ color: 'var(--v-cyan-400)' }}>숫자로 보는 지오정보기술</span>
<h2 style={{ fontSize: 'clamp(24px,3vw,36px)', fontWeight: 800, color: '#fff', marginTop: '8px' }}>
신뢰받는 IT 파트너
</h2>
</div>
<div className="grid-4">
{KPIS.map((k, i) => (
<div key={i} style={{ textAlign: 'center', padding: '24px 16px',
borderRight: i < KPIS.length - 1 ? '1px solid rgba(255,255,255,.1)' : 'none' }}>
<div className="v-stat-number">
{k.value.replace(/[+%년]/g, '')}
<span className="v-stat-unit">{k.value.match(/[+%년]/)?.[0] || ''}</span>
</div>
<div className="v-stat-label">{k.label}</div>
</div>
))}
</div>
</div>
</section>
{/* ── 최신 소식 (Variant 스타일) ──────────────────────── */}
<section className="section section-news py-section bg-white">
<div className="container">
<div className="section-header" style={{ marginBottom: '48px' }}>
<span className="section-label-v">최신 소식</span>
<h2 className="section-title-v">지오정보기술 소식</h2>
<div className="section-underline" />
</div>
<div className="news-grid">
{news.length > 0 ? news.map(n => (
<Link to={`/news/newsroom/${n.id}`} key={n.id} className="news-card card">
<div className="news-card-body">
<span className={`badge badge-accent`}>{n.category}</span>
<h3 className="news-title">{n.title}</h3>
<p className="news-summary">{n.summary}</p>
</div>
<div className="news-card-footer">
<span className="news-date">
{n.createdAt ? new Date(n.createdAt).toLocaleDateString('ko-KR') : ''}
</span>
<span className="news-more">더보기 </span>
</div>
</Link>
)) : (
/* 스켈레톤 */
Array.from({length: 4}).map((_, i) => (
<div key={i} className="news-card card skeleton">
<div className="skel-line" style={{width:'30%',height:'16px'}} />
<div className="skel-line" style={{width:'90%',height:'20px',marginTop:'8px'}} />
<div className="skel-line" style={{width:'70%',height:'14px',marginTop:'6px'}} />
</div>
))
)}
</div>
<div style={{textAlign:'center',marginTop:'40px'}}>
<Link to="/news/newsroom" className="btn btn-outline">
모든 소식 보기
</Link>
</div>
</div>
</section>
{/* ── CTA 배너 (Variant v-cta-banner) ─────────────────── */}
<section className="py-section bg-light">
<div className="container">
<div className="v-cta-banner">
<div>
<div className="v-cta-title">
GUARDiA ITSM 도입을 검토하고 계신가요?
</div>
<p className="v-cta-sub">
전문 컨설턴트가 기관 환경에 맞는 최적의 방안을 제안해 드립니다.
</p>
</div>
<div style={{ display: 'flex', gap: '12px', flexShrink: 0, flexWrap: 'wrap' }}>
<Link to="/support/contact" className="btn-v-primary btn-v-cyan">
무료 상담 신청
</Link>
<Link to="/solution/guardia"
style={{ display: 'inline-flex', alignItems: 'center', padding: '12px 24px',
background: 'rgba(255,255,255,.15)', color: '#fff', fontWeight: 600, fontSize: '15px',
borderRadius: '9999px', border: '1px solid rgba(255,255,255,.3)',
textDecoration: 'none', transition: 'all 240ms' }}>
제품 소개서
</Link>
</div>
</div>
</div>
</section>
</main>
);
}