feat: complete ANL lab web application with lossless data migration and SSG routes
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import React from "react";
|
||||
|
||||
export function DirectionsBlock() {
|
||||
return (
|
||||
<section className="mt-20 pt-16 border-t border-line" id="directions">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<span className="h-px w-8 bg-vermillion"></span>
|
||||
<span className="font-mono text-xs tracking-widest uppercase text-vermillion font-bold">
|
||||
Location & Contact
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
||||
{/* Left info column */}
|
||||
<div className="lg:col-span-7 space-y-6">
|
||||
<h2 className="font-serif text-3xl font-normal text-ink">
|
||||
오시는 길 & 연구실 소재지
|
||||
</h2>
|
||||
<p className="text-ink-mute text-base leading-relaxed">
|
||||
경북대학교 AI 에이전트 네트워크 연구실 (ANL)은 IT대학 5호관에 위치하고 있으며, 차세대 차세대 에이전트 오케스트레이션 및 고성능 네트워크 백본 연구를 수행합니다.
|
||||
</p>
|
||||
|
||||
<address className="not-italic space-y-4 pt-2 text-sm font-sans text-ink">
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="font-mono text-xs uppercase tracking-wider text-ink-mute w-20 pt-0.5">
|
||||
소재지
|
||||
</span>
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
경북대학교 IT대학 5호관(415동) 527호
|
||||
</p>
|
||||
<p className="text-xs text-ink-mute mt-0.5">
|
||||
School of Computer Science and Engineering, Kyungpook National University
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="font-mono text-xs uppercase tracking-wider text-ink-mute w-20">
|
||||
지도교수
|
||||
</span>
|
||||
<span>
|
||||
고석주 교수 (<a href="mailto:sjkoh@knu.ac.kr" className="text-vermillion hover:underline">sjkoh@knu.ac.kr</a>)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="font-mono text-xs uppercase tracking-wider text-ink-mute w-20 pt-0.5">
|
||||
관련 기관
|
||||
</span>
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs font-mono text-cobalt">
|
||||
<a href="http://www.knu.ac.kr/" target="_blank" rel="noreferrer" className="hover:underline">
|
||||
경북대학교 (KNU) ↗
|
||||
</a>
|
||||
<a href="http://cse.knu.ac.kr/" target="_blank" rel="noreferrer" className="hover:underline">
|
||||
컴퓨터학부 ↗
|
||||
</a>
|
||||
<a href="http://it.knu.ac.kr/" target="_blank" rel="noreferrer" className="hover:underline">
|
||||
IT대학 ↗
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</address>
|
||||
</div>
|
||||
|
||||
{/* Right card column */}
|
||||
<div className="lg:col-span-5 bg-paper p-6 rounded-lg border border-line flex flex-col justify-between">
|
||||
<div className="space-y-4">
|
||||
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider">
|
||||
Research Environment
|
||||
</div>
|
||||
<h3 className="font-serif text-xl font-normal text-ink">
|
||||
AI Agent Networking Lab
|
||||
</h3>
|
||||
<p className="text-xs text-ink-mute leading-relaxed">
|
||||
QUIC/gRPC 스트리밍 통신 인프라와 컨테이너 격리 프로세스 격리 환경을 포함한 멀티 에이전트 실험실 공간을 운용합니다.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 pt-4 border-t border-line flex justify-between items-center text-xs font-mono text-ink-mute">
|
||||
<span>IT-5 Building #527</span>
|
||||
<span className="text-vermillion font-bold">ANL Laboratory</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import Reveal from "@/components/Reveal";
|
||||
import Marquee from "@/components/Marquee";
|
||||
import Counter from "@/components/Counter";
|
||||
import SectionLabel from "@/components/SectionLabel";
|
||||
import HeroComposition from "./_components/HeroComposition";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "연구실 소개 (Intro)",
|
||||
description:
|
||||
"QUIC, gRPC, A2A 기반 에이전트 통신 및 멀티에이전트 오케스트레이션을 연구하는 경북대학교 AI 에이전트 네트워크 연구실 (ANL) 소개.",
|
||||
};
|
||||
|
||||
// CUSTOMIZATION HOOK: the five research pillars (5 Pillars, Option A).
|
||||
const pillars = [
|
||||
{
|
||||
no: "Pillar 01",
|
||||
tag: "Agent Eng",
|
||||
accent: "vermillion" as const,
|
||||
ko: "AI 에이전트 3대 핵심 엔지니어링",
|
||||
en: "Agent Core Engineering",
|
||||
desc: "프롬프트, 컨텍스트, 하네스 엔지니어링을 결합하여 대화 상태 보존, 외부 기억 바인딩, 도구 연동 인터페이스를 설계하고 자율 추론 경로를 최적화합니다.",
|
||||
points: [
|
||||
"역할 페르소나 및 Few-shot 최적화",
|
||||
"동적 컨텍스트 윈도우 & Memory 바인딩",
|
||||
"물리적 도구(Tool Use) 하네스 연결",
|
||||
],
|
||||
},
|
||||
{
|
||||
no: "Pillar 02",
|
||||
tag: "MCP & SKILL",
|
||||
accent: "cobalt" as const,
|
||||
ko: "AI Agent 기능 확장 프로토콜",
|
||||
en: "Extensibility & Protocol",
|
||||
desc: "Anthropic MCP 통일 호환 규격 및 코드+스키마+Few-shot이 패키징된 SKILL 모듈을 통해 에이전트의 로컬/원격 역량을 자율적으로 동적 확장합니다.",
|
||||
points: [
|
||||
"MCP (Model Context Protocol) 통일 규격",
|
||||
"자율 확장 SKILL 모듈 패키징",
|
||||
"CLI 기반 자율 코딩 에이전트 제어",
|
||||
],
|
||||
},
|
||||
{
|
||||
no: "Pillar 03",
|
||||
tag: "Orchestration",
|
||||
accent: "vermillion" as const,
|
||||
ko: "멀티 에이전트 협업 및 오케스트레이션",
|
||||
en: "Multi-Agent Systems",
|
||||
desc: "단일 에이전트의 환각과 컨텍스트 인지 한계를 극복하기 위해 역할 세분화(Role Specialization) 및 이종 모델 교차 검증과 오케스트레이션 인프라를 구축합니다.",
|
||||
points: [
|
||||
"HERDR 백그라운드 프로세스 컨테이너 격리",
|
||||
"crewAI 역할/목표 기반 크루 프로세스",
|
||||
"LangGraph 상태 보존형 순환 그래프 제어",
|
||||
],
|
||||
},
|
||||
{
|
||||
no: "Pillar 04",
|
||||
tag: "A2A & ACP",
|
||||
accent: "cobalt" as const,
|
||||
ko: "에이전트 상호운용성 개방형 표준",
|
||||
en: "Interoperability Standards",
|
||||
desc: "Linux Foundation A2A 및 IBM ACP 오픈 표준 기반으로 이종 에이전트 간 서비스 발견, 태스크 위임, Agent Card 프로필 명세를 구현합니다.",
|
||||
points: [
|
||||
"A2A (Agent-to-Agent) 통신 오픈 표준",
|
||||
"ACP 서비스 발견 & 태스크 위임",
|
||||
"Agent Card (/.well-known/agent-card.json)",
|
||||
],
|
||||
},
|
||||
{
|
||||
no: "Pillar 05",
|
||||
tag: "AIoT & gRPC",
|
||||
accent: "vermillion" as const,
|
||||
ko: "AIoT & 고성능 통신 백본",
|
||||
en: "High-Performance Backbone",
|
||||
desc: "스마트 팩토리/팜 엣지 자원 제약 환경에서 경량 프로토콜(MQTT/CoAP)과 HTTP/2·HTTP/3 gRPC/QUIC 양방향 스트리밍을 결합한 하이브리드 백본을 설계합니다.",
|
||||
points: [
|
||||
"gRPC & QUIC 양방향 스트리밍 메시징",
|
||||
"Protobuf 바이너리 직렬화 최적화",
|
||||
"MQTT-gRPC 하이브리드 엣지 메쉬",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// CUSTOMIZATION HOOK: headline figures (animated counters).
|
||||
// `value: 2026` is marked static so it renders as a fixed year, not a
|
||||
// 0→2026 count-up, at the smaller `display-sm` scale so the 4-digit year
|
||||
// does not push the cell padding. See LAYOUT_AUDIT #11 + #13.
|
||||
const figures = [
|
||||
{ value: pillars.length, label: "연구 기둥 · Research Pillars", suffix: "", size: "display" as const, static: false },
|
||||
{ value: pillars.reduce((n, p) => n + p.points.length, 0), label: "세부 연구 주제 · Research Topics", suffix: "", size: "display" as const, static: false },
|
||||
{ value: 2026, label: "Issue · 발행", suffix: "", size: "display-sm" as const, static: true },
|
||||
];
|
||||
|
||||
// CUSTOMIZATION HOOK: research keyword ticker.
|
||||
const keywords = [
|
||||
"A2A PROTOCOL",
|
||||
"MCP & SKILL",
|
||||
"HERDR PROCESS CONTAINER",
|
||||
"LANGGRAPH CYCLIC GRAPH",
|
||||
"QUIC & GRPC STREAMING",
|
||||
"MULTI-AGENT ORCHESTRATION",
|
||||
"AIOT EDGE CONTROL",
|
||||
"AGENT CARD SPEC",
|
||||
];
|
||||
|
||||
const focusAreas = [
|
||||
{ ko: "에이전트 통신 프로토콜 (A2A)", en: "A2A & ACP Protocols" },
|
||||
{ ko: "멀티에이전트 오케스트레이션", en: "Multi-Agent Orchestration" },
|
||||
{ ko: "MCP & SKILL 기능 확장", en: "MCP & SKILL Engineering" },
|
||||
{ ko: "AIoT & gRPC/QUIC 백본", en: "AIoT & gRPC/QUIC Backbone" },
|
||||
];
|
||||
|
||||
export default function IntroPage() {
|
||||
return (
|
||||
<>
|
||||
{/* ============ COVER SPREAD ============ */}
|
||||
<section className="relative overflow-hidden border-b border-ink">
|
||||
<div className="container-content pb-10 pt-12 sm:pt-16">
|
||||
<Reveal>
|
||||
<SectionLabel index={1} label="The Cover" />
|
||||
</Reveal>
|
||||
|
||||
<div className="mt-8 grid items-start gap-10 lg:grid-cols-12">
|
||||
{/* Title & Masthead column */}
|
||||
<div className="lg:col-span-7">
|
||||
<Reveal delay={100}>
|
||||
<p className="kicker">경북대학교 컴퓨터학부 · AI Agent Networking Lab (ANL)</p>
|
||||
<h1 className="headline-ko mt-4 text-[clamp(2.5rem,8vw,5.5rem)] leading-[0.98] text-ink">
|
||||
에이전트 네트워크의
|
||||
<br />
|
||||
<span className="text-vermillion">미래</span>를 짓다
|
||||
</h1>
|
||||
<p className="display mt-3 leading-[0.9]" aria-hidden>
|
||||
Agents
|
||||
<span className="block italic text-cobalt">in Motion</span>
|
||||
</p>
|
||||
</Reveal>
|
||||
<Reveal delay={200}>
|
||||
<p className="mt-8 max-w-prose text-base leading-relaxed text-ink-soft">
|
||||
A2A·ACP 기반 에이전트 통신 프로토콜과 HERDR·LangGraph 멀티에이전트
|
||||
오케스트레이션. 개방형 표준을 토대로 분산되고 이종적인 에이전트가
|
||||
자율적으로 협력하고 고속으로 메시지를 교환하도록 설계합니다.
|
||||
</p>
|
||||
</Reveal>
|
||||
<Reveal delay={260}>
|
||||
<div className="mt-8 flex flex-wrap gap-3">
|
||||
<Link
|
||||
href="/publications"
|
||||
className="bg-ink px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-ivory transition hover:bg-vermillion"
|
||||
>
|
||||
논문 보기 →
|
||||
</Link>
|
||||
<Link
|
||||
href="/members"
|
||||
className="border border-ink px-6 py-3 text-sm font-semibold uppercase tracking-[0.16em] text-ink transition hover:bg-ink hover:text-ivory"
|
||||
>
|
||||
구성원
|
||||
</Link>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
{/* Abstract illustration column */}
|
||||
<Reveal variant="scale" delay={200} className="lg:col-span-5">
|
||||
<HeroComposition className="w-full" />
|
||||
<p className="mt-3 text-right text-xs italic text-ink-mute">
|
||||
Fig. 01 — A2A 에이전트 메시 × QUIC/gRPC 멀티스트림
|
||||
</p>
|
||||
</Reveal>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Keyword marquee band */}
|
||||
<Reveal>
|
||||
<Marquee
|
||||
items={keywords}
|
||||
className="border-y border-ink bg-ink py-3 font-display text-xl tracking-wide text-ivory"
|
||||
/>
|
||||
</Reveal>
|
||||
</section>
|
||||
|
||||
{/* ============ FIGURES / COUNTERS ============ */}
|
||||
<section className="border-b border-ink">
|
||||
<div className="container-content grid gap-px bg-line sm:grid-cols-3">
|
||||
{figures.map((f, i) => (
|
||||
<Reveal key={f.label} delay={i * 100} className="bg-ivory">
|
||||
<div className="px-6 py-10">
|
||||
<Counter
|
||||
value={f.value}
|
||||
suffix={f.suffix}
|
||||
static={f.static}
|
||||
className={`${f.size} block text-ink`}
|
||||
/>
|
||||
<p className="kicker mt-3">{f.label}</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ============ MISSION / PULL QUOTE ============ */}
|
||||
<section className="border-b border-ink">
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={2} label="Mission" />
|
||||
</Reveal>
|
||||
<Reveal delay={100}>
|
||||
<blockquote className="pull-quote mt-10 max-w-4xl text-ink">
|
||||
“표준 적합성과 실증을 함께 추구하여, 연구가{" "}
|
||||
<span className="text-vermillion">실제 표준 문서</span>와{" "}
|
||||
<span className="text-cobalt">오픈소스 구현</span>으로 이어지게 한다.”
|
||||
</blockquote>
|
||||
</Reveal>
|
||||
<Reveal delay={160}>
|
||||
<p className="mt-6 max-w-prose text-sm leading-relaxed text-ink-mute">
|
||||
{/* CUSTOMIZATION HOOK: marginalia / footnote */}
|
||||
† 본 연구실은 차세대 에이전트 통신 프로토콜(A2A/ACP)과 멀티에이전트 오케스트레이션(HERDR/LangGraph) 기술을 토대로, 분산·이종 에이전트가
|
||||
자율적으로 협력하고 고속으로 메시지를 교환할 수 있는 네트워크 인프라를 연구합니다.
|
||||
</p>
|
||||
</Reveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ============ FIVE RESEARCH PILLARS ============ */}
|
||||
<section className="border-b border-ink">
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={3} label="Five Research Pillars" />
|
||||
</Reveal>
|
||||
|
||||
<div className="mt-12 grid gap-px bg-line sm:grid-cols-2 lg:grid-cols-6">
|
||||
{pillars.map((p, i) => {
|
||||
const isVerm = p.accent === "vermillion";
|
||||
const span = `${i < 3 ? "lg:col-span-2" : "lg:col-span-3"}${i === 4 ? " sm:col-span-2" : ""}`;
|
||||
return (
|
||||
<Reveal
|
||||
key={p.tag}
|
||||
variant={i % 2 === 0 ? "left" : "right"}
|
||||
delay={i * 80}
|
||||
className={`bg-ivory ${span}`}
|
||||
>
|
||||
<article
|
||||
className={`group flex h-full flex-col justify-between border border-ink p-8 transition-colors duration-500 sm:p-10 ${
|
||||
isVerm
|
||||
? "hover:bg-vermillion hover:text-ivory"
|
||||
: "hover:bg-cobalt hover:text-ivory"
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-baseline justify-between">
|
||||
<span className="kicker group-hover:text-ivory/70">{p.no}</span>
|
||||
<span
|
||||
className={`font-display text-2xl ${
|
||||
isVerm ? "text-vermillion" : "text-cobalt"
|
||||
} group-hover:text-ivory`}
|
||||
>
|
||||
{p.tag}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="headline-ko mt-6 text-2xl leading-tight">
|
||||
{p.ko}
|
||||
</h3>
|
||||
<p className="font-display mt-1 text-base italic opacity-70">
|
||||
{p.en}
|
||||
</p>
|
||||
<p className="mt-5 text-sm leading-relaxed opacity-90">
|
||||
{p.desc}
|
||||
</p>
|
||||
</div>
|
||||
<ul className="mt-6 space-y-3 text-sm">
|
||||
{p.points.map((pt) => (
|
||||
<li key={pt} className="flex gap-3 border-t border-ink/20 pt-3 group-hover:border-ivory/20">
|
||||
<span className="font-display text-xs opacity-60">→</span>
|
||||
<span>{pt}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
</Reveal>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ============ FOCUS AREAS ============ */}
|
||||
<section>
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={4} label="Focus Areas" />
|
||||
</Reveal>
|
||||
<div className="mt-10 grid gap-px bg-line sm:grid-cols-2 lg:grid-cols-4">
|
||||
{focusAreas.map((f, i) => (
|
||||
<Reveal key={f.en} delay={i * 80} className="bg-ivory">
|
||||
<div className="group flex h-full flex-col justify-between gap-8 p-7 transition-colors duration-500 hover:bg-ink hover:text-ivory">
|
||||
<span className="font-display text-2xl text-ink-mute group-hover:text-vermillion">
|
||||
{String(i + 1).padStart(2, "0")}
|
||||
</span>
|
||||
<div>
|
||||
<p className="headline-ko text-lg">{f.ko}</p>
|
||||
<p className="mt-1 text-xs uppercase tracking-[0.16em] text-ink-mute group-hover:text-ivory/60">
|
||||
{f.en}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,102 +1,129 @@
|
||||
import type { Metadata } from "next";
|
||||
import PageHeader from "@/components/PageHeader";
|
||||
import Reveal from "@/components/Reveal";
|
||||
import SectionLabel from "@/components/SectionLabel";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "강의 (Lectures)",
|
||||
description: "AI 에이전트 네트워크 연구실 (ANL) 지도교수가 담당하는 강의 목록 (placeholder).",
|
||||
};
|
||||
|
||||
// CUSTOMIZATION HOOK: replace placeholder lectures below.
|
||||
const lectures = [
|
||||
{
|
||||
code: "CSE3xx",
|
||||
ko: "사물인터넷 개론",
|
||||
en: "Introduction to IoT",
|
||||
level: "학부 · Undergraduate",
|
||||
accent: "vermillion" as const,
|
||||
desc: "IoT 아키텍처, 센서·액추에이터, 통신 프로토콜(MQTT/CoAP)과 oneM2M 표준의 기본 개념을 다룹니다. 간단한 디바이스–서버 연동 실습을 포함합니다.",
|
||||
},
|
||||
{
|
||||
code: "CSE4xx",
|
||||
ko: "네트워크 프로토콜과 전송 기술",
|
||||
en: "Network Protocols & Transport",
|
||||
level: "학부/대학원 · Undergraduate / Graduate",
|
||||
accent: "cobalt" as const,
|
||||
desc: "TCP/UDP에서 QUIC까지 전송 계층의 발전과 설계 원리를 학습합니다. 스트림 다중화, 연결 마이그레이션, 혼잡 제어를 실험을 통해 분석합니다.",
|
||||
},
|
||||
{
|
||||
code: "CSE6xx",
|
||||
ko: "메타버스 상호운용성과 표준",
|
||||
en: "Metaverse Interoperability & Standards",
|
||||
level: "대학원 · Graduate",
|
||||
accent: "ink" as const,
|
||||
desc: "이종 메타버스 플랫폼 간 데이터 모델, 의미 보존 변환, W3C/oneM2M 표준을 다루는 세미나형 대학원 강의입니다. 최신 논문 리뷰와 프로젝트를 병행합니다.",
|
||||
},
|
||||
];
|
||||
|
||||
const accent: Record<string, { hover: string; text: string }> = {
|
||||
vermillion: { hover: "hover:bg-vermillion hover:text-ivory", text: "text-vermillion" },
|
||||
cobalt: { hover: "hover:bg-cobalt hover:text-ivory", text: "text-cobalt" },
|
||||
ink: { hover: "hover:bg-ink hover:text-ivory", text: "text-ink" },
|
||||
};
|
||||
import React from "react";
|
||||
import { semesters } from "../../content/lectures";
|
||||
|
||||
export default function LecturesPage() {
|
||||
const recentSemesters = semesters.slice(0, 3);
|
||||
const olderSemesters = semesters.slice(3);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
ko="강의"
|
||||
en="Lectures"
|
||||
index={4}
|
||||
description="연구실에서 담당하는 학부·대학원 강의입니다. 아래 강의 정보는 예시(placeholder)이며 실제 개설 과목으로 교체할 수 있습니다."
|
||||
/>
|
||||
<div className="space-y-16 py-6">
|
||||
{/* Header */}
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-px w-8 bg-vermillion"></span>
|
||||
<span className="font-mono text-xs tracking-widest uppercase text-vermillion font-bold">
|
||||
Academic Courses
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
|
||||
강의 이력
|
||||
</h1>
|
||||
<p className="text-ink-mute text-base max-w-2xl">
|
||||
2016년 가을학기부터 2026년 봄학기까지 개설된 네트워크 및 소프트웨어 분야 약 20개 학기의 학부/대학원 강의 목록입니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={1} label="Courses" />
|
||||
</Reveal>
|
||||
|
||||
<div className="mt-12 grid gap-px bg-line lg:grid-cols-3">
|
||||
{lectures.map((l, i) => {
|
||||
const a = accent[l.accent];
|
||||
return (
|
||||
<Reveal
|
||||
key={l.code}
|
||||
variant={i === 0 ? "left" : i === 2 ? "right" : "up"}
|
||||
delay={i * 100}
|
||||
className="bg-ivory"
|
||||
>
|
||||
<article
|
||||
className={`group flex h-full flex-col border border-ink p-8 transition-colors duration-500 sm:p-10 ${a.hover}`}
|
||||
>
|
||||
<div className="flex items-baseline justify-between">
|
||||
<span className={`font-display text-xl ${a.text} group-hover:text-ivory`}>
|
||||
{l.code}
|
||||
</span>
|
||||
<span className="font-display text-4xl text-ink-mute group-hover:text-ivory/70">
|
||||
{String(i + 1).padStart(2, "0")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className="headline-ko mt-6 text-2xl leading-tight">{l.ko}</h3>
|
||||
<p className="font-display mt-1 text-lg italic opacity-70">{l.en}</p>
|
||||
|
||||
<p className={`mt-4 inline-flex w-fit border border-ink/30 px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] group-hover:border-ivory/40`}>
|
||||
{l.level}
|
||||
</p>
|
||||
|
||||
<p className="mt-6 border-t border-ink/20 pt-6 text-sm leading-relaxed opacity-90 group-hover:border-ivory/20">
|
||||
{l.desc}
|
||||
</p>
|
||||
</article>
|
||||
</Reveal>
|
||||
);
|
||||
})}
|
||||
{/* Recent 3 Semesters */}
|
||||
<section className="space-y-6 pt-6 border-t border-line">
|
||||
<div className="flex justify-between items-end">
|
||||
<div>
|
||||
<div className="font-mono text-xs text-vermillion uppercase tracking-widest font-bold">
|
||||
Recent Courses ({recentSemesters.length} Semesters)
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl font-normal text-ink mt-1">
|
||||
최근 개설 강의
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{recentSemesters.map((sem, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="bg-paper p-6 rounded-lg border border-line space-y-4 flex flex-col justify-between"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center border-b border-line pb-2">
|
||||
<span className="font-serif text-xl font-normal text-ink">
|
||||
{sem.term} {sem.year}
|
||||
</span>
|
||||
<span className="font-mono text-xs text-vermillion font-bold">
|
||||
Latest
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-2 pt-1">
|
||||
{sem.courses.map((c, cIdx) => (
|
||||
<li key={cIdx} className="space-y-0.5">
|
||||
<div className="font-serif text-base text-ink">
|
||||
{c.en}
|
||||
</div>
|
||||
<div className="font-mono text-xs text-ink-mute">
|
||||
Code: {c.code} {c.note && `(${c.note})`}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="pt-3 border-t border-line/60 font-mono text-xs text-ink-mute">
|
||||
Course Count: {sem.courses.length}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
|
||||
{/* Older Semesters (Native <details>/<summary> progressive disclosure) */}
|
||||
<section className="space-y-6 pt-6 border-t border-line">
|
||||
<details className="group border border-line rounded-lg bg-paper p-6 transition-all duration-200">
|
||||
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
|
||||
<div className="space-y-1">
|
||||
<span className="font-serif text-xl font-normal text-ink">
|
||||
이전 전체 강의 이력 ({olderSemesters.length}개 학기) 열람하기
|
||||
</span>
|
||||
<p className="text-xs text-ink-mute font-mono">
|
||||
2016 Fall ~ 2024 Spring 학기별 개설 교과목 이력
|
||||
</p>
|
||||
</div>
|
||||
<span className="font-mono text-xs text-vermillion font-bold border border-vermillion px-3 py-1.5 rounded group-open:bg-vermillion group-open:text-ivory transition-colors">
|
||||
<span className="group-open:hidden">전체 보기 ▾</span>
|
||||
<span className="hidden group-open:inline">접기 ▴</span>
|
||||
</span>
|
||||
</summary>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-line grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{olderSemesters.map((sem, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="bg-ivory p-5 rounded border border-line space-y-3"
|
||||
>
|
||||
<div className="font-serif text-lg font-normal text-ink border-b border-line pb-1.5 flex justify-between">
|
||||
<span>
|
||||
{sem.term} {sem.year}
|
||||
</span>
|
||||
<span className="font-mono text-xs text-ink-mute">
|
||||
{sem.courses.length} Courses
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-2">
|
||||
{sem.courses.map((c, cIdx) => (
|
||||
<li key={cIdx} className="space-y-0.5">
|
||||
<div className="font-serif text-sm text-ink">
|
||||
{c.en}
|
||||
</div>
|
||||
<div className="font-mono text-[0.7rem] text-ink-mute">
|
||||
Code: {c.code} {c.note && `(${c.note})`}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,174 +1,213 @@
|
||||
import type { Metadata } from "next";
|
||||
import PageHeader from "@/components/PageHeader";
|
||||
import Reveal from "@/components/Reveal";
|
||||
import SectionLabel from "@/components/SectionLabel";
|
||||
import Counter from "@/components/Counter";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "구성원 (Members)",
|
||||
description: "AI 에이전트 네트워크 연구실 (ANL) 지도교수 및 대학원·학부 연구원 소개 (placeholder).",
|
||||
};
|
||||
|
||||
// CUSTOMIZATION HOOK: replace placeholder member data below.
|
||||
const advisor = {
|
||||
ko: "고석주 교수",
|
||||
en: "Prof. Seok-Joo Koh",
|
||||
role: "지도교수 · Principal Investigator",
|
||||
desc: "QUIC/gRPC/A2A 에이전트 통신, 멀티에이전트 오케스트레이션, MCP & SKILL 확장, 네트워크 프로토콜. (sjkoh@knu.ac.kr)",
|
||||
fields: ["Agent Communication (A2A)", "Multi-Agent Orchestration", "QUIC & gRPC Transport"],
|
||||
};
|
||||
|
||||
const groups = [
|
||||
{
|
||||
ko: "박사과정 연구원",
|
||||
en: "Ph.D. Students",
|
||||
accent: "vermillion" as const,
|
||||
members: [
|
||||
{ name: "연구원 A (placeholder)", topic: "QUIC 기반 멀티에이전트 오케스트레이션" },
|
||||
{ name: "연구원 B (placeholder)", topic: "메타버스 공통 정보 모델 (MCM)" },
|
||||
],
|
||||
},
|
||||
{
|
||||
ko: "석사과정 연구원",
|
||||
en: "M.S. Students",
|
||||
accent: "cobalt" as const,
|
||||
members: [
|
||||
{ name: "연구원 C (placeholder)", topic: "oneM2M 적합성 검증" },
|
||||
{ name: "연구원 D (placeholder)", topic: "QUIC 스트림 다중화 성능 분석" },
|
||||
{ name: "연구원 E (placeholder)", topic: "W3C WoT Thing Description 매핑" },
|
||||
],
|
||||
},
|
||||
{
|
||||
ko: "학부 연구원",
|
||||
en: "Undergraduate Researchers",
|
||||
accent: "ink" as const,
|
||||
members: [
|
||||
{ name: "연구원 F (placeholder)", topic: "테스트베드 개발" },
|
||||
{ name: "연구원 G (placeholder)", topic: "데이터 시각화" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// CUSTOMIZATION HOOK: headcount figures (animated counters).
|
||||
const figures = [
|
||||
{ value: 1, label: "지도교수 · Principal Investigator" },
|
||||
{ value: 5, label: "대학원 연구원 · Graduate Members" },
|
||||
{ value: 2, label: "학부 연구원 · Undergraduates" },
|
||||
];
|
||||
|
||||
const accentText: Record<string, string> = {
|
||||
vermillion: "text-vermillion",
|
||||
cobalt: "text-cobalt",
|
||||
ink: "text-ink",
|
||||
};
|
||||
import React from "react";
|
||||
import { activeMembers, alumni } from "../../content/members";
|
||||
|
||||
export default function MembersPage() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
ko="구성원"
|
||||
en="Members"
|
||||
index={2}
|
||||
description="연구실 지도교수와 박사·석사·학부 연구원을 소개합니다. 아래 정보는 예시(placeholder)이며 실제 구성원 정보로 교체할 수 있습니다."
|
||||
/>
|
||||
const professor = activeMembers.find((m) => m.degree === "Professor");
|
||||
const phdStudents = activeMembers.filter(
|
||||
(m) => m.degree === "PhD" || m.degree === "PhDCandidate" || m.degree === "PhDStudent"
|
||||
);
|
||||
const msStudents = activeMembers.filter(
|
||||
(m) => m.degree === "MSCandidate" || m.degree === "MSStudent"
|
||||
);
|
||||
|
||||
{/* ============ HEADCOUNT FIGURES ============ */}
|
||||
<section className="border-b border-ink">
|
||||
<div className="container-content grid gap-px bg-line sm:grid-cols-3">
|
||||
{figures.map((f, i) => (
|
||||
<Reveal key={f.label} delay={i * 100} className="bg-ivory">
|
||||
<div className="px-6 py-10">
|
||||
<Counter value={f.value} className="display block text-ink" />
|
||||
<p className="kicker mt-3">{f.label}</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
return (
|
||||
<div className="space-y-16 py-6">
|
||||
{/* Header */}
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-px w-8 bg-vermillion"></span>
|
||||
<span className="font-mono text-xs tracking-widest uppercase text-vermillion font-bold">
|
||||
People & Researchers
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
|
||||
연구실 구성원
|
||||
</h1>
|
||||
<p className="text-ink-mute text-base max-w-2xl">
|
||||
지도교수와 재학 연구원 16명, 그리고 IT 산업체 및 학계에서 활약하고 있는 60명의 졸업생 인벤토리입니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* ============ PRINCIPAL INVESTIGATOR ============ */}
|
||||
<section className="border-b border-ink">
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={1} label="Principal Investigator" />
|
||||
</Reveal>
|
||||
{/* Professor Section */}
|
||||
<section className="space-y-6 pt-6 border-t border-line">
|
||||
<div className="font-mono text-xs text-vermillion uppercase tracking-widest font-bold">
|
||||
Faculty
|
||||
</div>
|
||||
|
||||
<div className="mt-10 grid gap-px lg:grid-cols-12">
|
||||
<Reveal variant="left" className="lg:col-span-7">
|
||||
<article className="group h-full border border-ink p-8 transition-colors duration-500 hover:bg-ink hover:text-ivory sm:p-10">
|
||||
<span className="kicker group-hover:text-ivory/70">{advisor.role}</span>
|
||||
<h2 className="headline-ko mt-5 text-4xl leading-tight">{advisor.ko}</h2>
|
||||
<p className="font-display mt-1 text-2xl italic text-vermillion">
|
||||
{advisor.en}
|
||||
</p>
|
||||
<p className="mt-6 max-w-prose text-sm leading-relaxed opacity-90">
|
||||
{advisor.desc}
|
||||
</p>
|
||||
</article>
|
||||
</Reveal>
|
||||
|
||||
<Reveal variant="right" delay={120} className="lg:col-span-5">
|
||||
<div className="flex h-full flex-col gap-px bg-line">
|
||||
{advisor.fields.map((field, i) => (
|
||||
<div
|
||||
key={field}
|
||||
className="group flex items-center gap-5 bg-ivory px-7 py-6 transition-colors duration-500 hover:bg-paper"
|
||||
>
|
||||
<span className="font-display text-3xl text-ink-mute group-hover:text-vermillion">
|
||||
{String(i + 1).padStart(2, "0")}
|
||||
</span>
|
||||
<span className="text-sm uppercase tracking-[0.16em] text-ink-soft">
|
||||
{field}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Reveal>
|
||||
<div className="bg-paper p-8 rounded-lg border border-line grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
|
||||
<div className="lg:col-span-8 space-y-4">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<h2 className="font-serif text-3xl font-normal text-ink">
|
||||
{professor?.ko || "고석주"} 교수
|
||||
</h2>
|
||||
<span className="font-mono text-sm text-ink-mute">
|
||||
{professor?.en || "Seok-Joo Koh"}, Professor
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-ink-mute leading-relaxed">
|
||||
경북대학교 컴퓨터학부 교수 · AI 에이전트 네트워크 연구실 (ANL) 지도교수.
|
||||
QUIC/gRPC 통신, A2A/ACP 프로토콜, MCP & SKILL 아키텍처 및 에이전트 오케스트레이션 연구를 총괄하고 있습니다.
|
||||
</p>
|
||||
<div className="pt-4 border-t border-line/60 flex flex-wrap gap-4 text-xs font-mono">
|
||||
<a
|
||||
href="mailto:sjkoh@knu.ac.kr"
|
||||
className="text-vermillion hover:underline"
|
||||
>
|
||||
sjkoh@knu.ac.kr
|
||||
</a>
|
||||
<span className="text-ink-mute">•</span>
|
||||
<a
|
||||
href="https://github.com/sjkoh12"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-cobalt hover:underline"
|
||||
>
|
||||
GitHub @sjkoh12 ↗
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:col-span-4 bg-ivory p-6 rounded border border-line text-xs space-y-2 font-mono">
|
||||
<div className="text-ink font-bold border-b border-line pb-1">
|
||||
Affiliation & Office
|
||||
</div>
|
||||
<p className="text-ink-mute">Kyungpook National University</p>
|
||||
<p className="text-ink-mute">IT Building #5 Room 527</p>
|
||||
<p className="text-ink-mute">Daegu, Republic of Korea</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ============ RESEARCH GROUPS ============ */}
|
||||
<section>
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={2} label="Researchers" />
|
||||
</Reveal>
|
||||
{/* Active Researchers Section */}
|
||||
<section className="space-y-8 pt-6 border-t border-line">
|
||||
<div className="flex justify-between items-end">
|
||||
<div>
|
||||
<div className="font-mono text-xs text-cobalt uppercase tracking-widest font-bold">
|
||||
Active Researchers ({activeMembers.length} Members)
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl font-normal text-ink mt-1">
|
||||
재학 연구원
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{groups.map((g, gi) => (
|
||||
<div key={g.en} className="mt-14 first:mt-12">
|
||||
<Reveal>
|
||||
<div className="flex items-baseline justify-between border-b border-ink pb-4">
|
||||
<h3 className="headline-ko text-2xl text-ink sm:text-3xl">{g.ko}</h3>
|
||||
<span
|
||||
className={`font-display text-lg italic ${accentText[g.accent]}`}
|
||||
>
|
||||
{g.en}
|
||||
{/* Ph.D. Candidates / Students */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
|
||||
Ph.D. Course ({phdStudents.length})
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{phdStudents.map((mem, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors"
|
||||
>
|
||||
<div className="flex justify-between items-baseline">
|
||||
<span className="font-serif text-lg font-normal text-ink">
|
||||
{mem.ko}
|
||||
</span>
|
||||
<span className="font-mono text-xs text-cobalt">
|
||||
{mem.degree}
|
||||
</span>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
<div className="mt-px grid gap-px bg-line sm:grid-cols-2">
|
||||
{g.members.map((m, mi) => (
|
||||
<Reveal key={m.name} delay={mi * 80} className="bg-ivory">
|
||||
<article className="group flex h-full flex-col justify-between gap-8 p-7 transition-colors duration-500 hover:bg-ink hover:text-ivory">
|
||||
<span className="font-display text-2xl text-ink-mute group-hover:text-vermillion">
|
||||
{String(gi + 1).padStart(2, "0")}.{String(mi + 1).padStart(2, "0")}
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="headline-ko text-lg">{m.name}</h4>
|
||||
<p className="mt-2 text-xs leading-relaxed text-ink-mute group-hover:text-ivory/60">
|
||||
{m.topic}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</Reveal>
|
||||
))}
|
||||
<div className="font-mono text-xs text-ink-mute">{mem.en}</div>
|
||||
{mem.affiliation && (
|
||||
<div className="text-xs text-vermillion pt-1 border-t border-line/40">
|
||||
with {mem.affiliation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* M.S. Candidates / Students */}
|
||||
<div className="space-y-4 pt-4">
|
||||
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
|
||||
M.S. Course ({msStudents.length})
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{msStudents.map((mem, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors"
|
||||
>
|
||||
<div className="flex justify-between items-baseline">
|
||||
<span className="font-serif text-lg font-normal text-ink">
|
||||
{mem.ko}
|
||||
</span>
|
||||
<span className="font-mono text-xs text-cobalt">
|
||||
{mem.degree}
|
||||
</span>
|
||||
</div>
|
||||
<div className="font-mono text-xs text-ink-mute">{mem.en}</div>
|
||||
{mem.affiliation && (
|
||||
<div className="text-xs text-vermillion pt-1 border-t border-line/40">
|
||||
with {mem.affiliation}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
|
||||
{/* Alumni Section (Native <details>/<summary> progressive disclosure) */}
|
||||
<section className="space-y-6 pt-6 border-t border-line">
|
||||
<div className="flex justify-between items-end">
|
||||
<div>
|
||||
<div className="font-mono text-xs text-ink-mute uppercase tracking-widest font-bold">
|
||||
Alumni Inventory ({alumni.length} Graduates)
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl font-normal text-ink mt-1">
|
||||
졸업생 목록
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details className="group border border-line rounded-lg bg-paper p-6 transition-all duration-200">
|
||||
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
|
||||
<div className="space-y-1">
|
||||
<span className="font-serif text-xl font-normal text-ink">
|
||||
전체 졸업생 {alumni.length}명 열람하기
|
||||
</span>
|
||||
<p className="text-xs text-ink-mute font-mono">
|
||||
2007년부터 2026년까지 수여된 박사(Ph.D.) 및 석사(M.S.) 졸업생 이력
|
||||
</p>
|
||||
</div>
|
||||
<span className="font-mono text-xs text-vermillion font-bold border border-vermillion px-3 py-1.5 rounded group-open:bg-vermillion group-open:text-ivory transition-colors">
|
||||
<span className="group-open:hidden">전체 보기 ▾</span>
|
||||
<span className="hidden group-open:inline">접기 ▴</span>
|
||||
</span>
|
||||
</summary>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-line grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{alumni.map((alum, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="bg-ivory p-4 rounded border border-line space-y-1.5"
|
||||
>
|
||||
<div className="flex justify-between items-baseline">
|
||||
<span className="font-serif text-base font-normal text-ink">
|
||||
{alum.ko} ({alum.en})
|
||||
</span>
|
||||
<span className="font-mono text-xs text-ink-mute">
|
||||
{alum.degree}
|
||||
</span>
|
||||
</div>
|
||||
<div className="font-mono text-xs text-ink-mute flex justify-between">
|
||||
<span>{alum.graduatedAt}</span>
|
||||
{alum.major && <span>{alum.major}</span>}
|
||||
</div>
|
||||
{alum.currentPosition && (
|
||||
<div className="text-xs text-cobalt pt-1 border-t border-line/40">
|
||||
{alum.currentPosition}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
import React from "react";
|
||||
import HeroComposition from "./_components/HeroComposition";
|
||||
import { DirectionsBlock } from "./_components/DirectionsBlock";
|
||||
import { publications } from "../content/publications";
|
||||
|
||||
const PILLARS = [
|
||||
{
|
||||
no: "01",
|
||||
en: "AI Agent Engineering",
|
||||
ko: "AI 에이전트 3대 핵심 엔지니어링",
|
||||
theme: "vermillion" as const,
|
||||
topics: [
|
||||
"프롬프트 엔지니어링 (System Prompt Optimization)",
|
||||
"맥락 엔지니어링 (Dynamic Context & Memory Packaging)",
|
||||
"하네스 엔지니어링 (Agent Control Loop & Sandbox Isolation)",
|
||||
],
|
||||
desc: "AI 에이전트의 구동 성능과 안전성을 좌우하는 프롬프트·맥락·하네스 3대 핵심 엔지니어링 기술을 연구합니다.",
|
||||
},
|
||||
{
|
||||
no: "02",
|
||||
en: "MCP & SKILL Architecture",
|
||||
ko: "AI Agent 기능 확장 프로토콜",
|
||||
theme: "cobalt" as const,
|
||||
topics: [
|
||||
"Model Context Protocol (MCP) 서버/클라이언트 아키텍처",
|
||||
"SKILL 도구 캡슐화 및 재사용 패키징",
|
||||
"CLI 에이전트 인터페이스 제어 및 웹 가동 제어",
|
||||
],
|
||||
desc: "에이전트가 외부 도구 및 시스템과 표준화된 방식으로 연동할 수 있도록 MCP와 SKILL 패키징 기술을 개발합니다.",
|
||||
},
|
||||
{
|
||||
no: "03",
|
||||
en: "Multi-Agent Orchestration",
|
||||
ko: "멀티 에이전트 협업 및 오케스트레이션",
|
||||
theme: "vermillion" as const,
|
||||
topics: [
|
||||
"HERDR 백그라운드 프로세스 컨테이너 격리 제어",
|
||||
"crewAI 기반 역할 분담 및 태스크 위임 체계",
|
||||
"LangGraph 순환 상태 그래프 기반 워크플로우 제어",
|
||||
],
|
||||
desc: "다중 에이전트 환경에서 독립적인 역할 분담과 선순환 오케스트레이션을 보장하는 자율 협업 플랫폼을 연구합니다.",
|
||||
},
|
||||
{
|
||||
no: "04",
|
||||
en: "A2A & ACP Open Protocols",
|
||||
ko: "에이전트 상호운용성 개방형 표준",
|
||||
theme: "cobalt" as const,
|
||||
topics: [
|
||||
"Agent-to-Agent (A2A) 프로토콜 규격 정의 및 검증",
|
||||
"Agent Communication Protocol (ACP) 메시지 교환 규격",
|
||||
"Agent Card (/.well-known/agent-card.json) 메타데이터 검증",
|
||||
],
|
||||
desc: "서로 다른 프레임워크 기반의 에이전트 간의 상호운용성을 보장하는 국제 표준 개방형 통신 프로토콜을 구현합니다.",
|
||||
},
|
||||
{
|
||||
no: "05",
|
||||
en: "High-Performance Networking Backbone",
|
||||
ko: "AIoT & 고성능 통신 백본",
|
||||
theme: "vermillion" as const,
|
||||
topics: [
|
||||
"gRPC / QUIC 양방향 스트리밍 에이전트 백본 통신",
|
||||
"Protocol Buffers (Protobuf) 직렬화 통신 최적화",
|
||||
"MQTT / CoAP 엣지 메시 통신망 통합",
|
||||
],
|
||||
desc: "초저지연 양방향 스트리밍 통신 인프라를 바탕으로 고성능 에이전트 백본 네트워크를 설계하고 실증합니다.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function HomePage() {
|
||||
const pubCount = publications.length;
|
||||
|
||||
return (
|
||||
<div className="space-y-16 py-6">
|
||||
{/* Hero Section */}
|
||||
<section className="relative">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<span className="h-px w-8 bg-vermillion"></span>
|
||||
<span className="font-mono text-xs tracking-widest uppercase text-vermillion font-bold">
|
||||
Kyungpook National University · AI Agent Networking Lab (ANL)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 items-start">
|
||||
<div className="lg:col-span-7 space-y-6">
|
||||
<h1 className="font-serif text-4xl sm:text-5xl lg:text-6xl font-normal text-ink leading-tight">
|
||||
에이전트 네트워크의 <br />
|
||||
<span className="italic font-serif text-vermillion">미래를 짓다</span>
|
||||
</h1>
|
||||
<p className="text-ink-mute text-base sm:text-lg leading-relaxed max-w-2xl">
|
||||
AI 에이전트 네트워크 연구실(ANL)은 에이전트 엔지니어링, MCP/SKILL 아키텍처,
|
||||
멀티 에이전트 오케스트레이션, A2A/ACP 개방형 표준, gRPC/QUIC 고성능 통신 백본의
|
||||
5대 연구 기둥을 중심으로 지능형 자율 에이전트 네트워크를 연구합니다.
|
||||
</p>
|
||||
|
||||
{/* Metrics Row (Derived from data array, AC-13) */}
|
||||
<div className="pt-6 border-t border-line grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<div className="font-serif text-3xl text-vermillion font-normal">
|
||||
{PILLARS.length}
|
||||
</div>
|
||||
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1">
|
||||
Core Pillars
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-serif text-3xl text-cobalt font-normal">
|
||||
{pubCount}
|
||||
</div>
|
||||
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1">
|
||||
Publications
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-serif text-3xl text-ink font-normal">
|
||||
2026
|
||||
</div>
|
||||
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1">
|
||||
Issue Static
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-5 border border-line bg-paper p-4 rounded-lg">
|
||||
<HeroComposition />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 5 Pillars Magazine Spread Grid */}
|
||||
<section className="space-y-8 pt-8 border-t border-line">
|
||||
<div className="flex justify-between items-end">
|
||||
<div>
|
||||
<div className="font-mono text-xs text-vermillion uppercase tracking-widest font-bold">
|
||||
Research Pillars
|
||||
</div>
|
||||
<h2 className="font-serif text-3xl font-normal text-ink mt-1">
|
||||
5대 핵심 연구 기둥
|
||||
</h2>
|
||||
</div>
|
||||
<div className="font-mono text-xs text-ink-mute">
|
||||
Magazine Layout 01–05
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-6">
|
||||
{PILLARS.map((pillar, idx) => {
|
||||
const isVermillion = pillar.theme === "vermillion";
|
||||
const colSpan = idx < 3 ? "lg:col-span-2" : "lg:col-span-3";
|
||||
|
||||
return (
|
||||
<div
|
||||
key={pillar.no}
|
||||
className={`${colSpan} bg-paper p-6 rounded-lg border border-line flex flex-col justify-between hover:border-ink transition-colors duration-200`}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<span
|
||||
className={`font-mono text-sm font-bold ${
|
||||
isVermillion ? "text-vermillion" : "text-cobalt"
|
||||
}`}
|
||||
>
|
||||
[{pillar.no}]
|
||||
</span>
|
||||
<span className="font-mono text-xs text-ink-mute uppercase">
|
||||
{pillar.en}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className="font-serif text-xl font-normal text-ink">
|
||||
{pillar.ko}
|
||||
</h3>
|
||||
|
||||
<p className="text-xs text-ink-mute leading-relaxed">
|
||||
{pillar.desc}
|
||||
</p>
|
||||
|
||||
<ul className="space-y-2 pt-2">
|
||||
{pillar.topics.map((topic, tIdx) => (
|
||||
<li
|
||||
key={tIdx}
|
||||
className="text-xs text-ink flex items-start gap-2"
|
||||
>
|
||||
<span
|
||||
className={`inline-block w-1.5 h-1.5 rounded-full mt-1 ${
|
||||
isVermillion ? "bg-vermillion" : "bg-cobalt"
|
||||
}`}
|
||||
></span>
|
||||
<span>{topic}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 pt-4 border-t border-line/60 flex justify-between items-center text-xs font-mono text-ink-mute">
|
||||
<span>ANL Core Spec</span>
|
||||
<span
|
||||
className={
|
||||
isVermillion ? "text-vermillion" : "text-cobalt"
|
||||
}
|
||||
>
|
||||
Pillar #{pillar.no}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Directions Block */}
|
||||
<DirectionsBlock />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import React from "react";
|
||||
import { notFound } from "next/navigation";
|
||||
import { CategoryNav } from "../_components/CategoryNav";
|
||||
import { publications, patents } from "../../../content/publications";
|
||||
import { PUB_CATEGORIES } from "../../../content/categories";
|
||||
import { PubCategory } from "../../../content/types";
|
||||
|
||||
interface PageProps {
|
||||
params: {
|
||||
category: string;
|
||||
};
|
||||
}
|
||||
|
||||
// SSG static pre-rendering (● SSG) for Next.js 14
|
||||
export function generateStaticParams() {
|
||||
return PUB_CATEGORIES.map((cat) => ({
|
||||
category: cat.slug,
|
||||
}));
|
||||
}
|
||||
|
||||
export default function PublicationCategoryPage({ params }: PageProps) {
|
||||
const { category } = params;
|
||||
const currentCat = PUB_CATEGORIES.find((c) => c.slug === category);
|
||||
|
||||
if (!currentCat) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const slug = category as PubCategory;
|
||||
|
||||
let records: any[] = [];
|
||||
if (slug === "patent") {
|
||||
records = patents.map((p) => ({
|
||||
title: p.title,
|
||||
inventors: p.inventors,
|
||||
appNo: p.applicationNo,
|
||||
appDate: p.applicationAt,
|
||||
regNo: p.registrationNo,
|
||||
regDate: p.registrationAt,
|
||||
country: p.country,
|
||||
publishedAt: p.publishedAt,
|
||||
isPatent: true,
|
||||
}));
|
||||
} else {
|
||||
records = publications
|
||||
.filter((p) => p.category === slug)
|
||||
.map((p) => ({
|
||||
title: p.title,
|
||||
venue: p.venue,
|
||||
volume: p.volume,
|
||||
publishedAt: p.publishedAt,
|
||||
kci: p.kci,
|
||||
isPatent: false,
|
||||
}));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-16 py-6">
|
||||
{/* Header */}
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-px w-8 bg-vermillion"></span>
|
||||
<span className="font-mono text-xs tracking-widest uppercase text-vermillion font-bold">
|
||||
Publications Category
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
|
||||
{currentCat.label}
|
||||
</h1>
|
||||
<p className="text-ink-mute text-base max-w-2xl">
|
||||
{currentCat.desc} — 총 {records.length}건의 실데이터 카탈로그입니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Category Nav */}
|
||||
<CategoryNav currentCategory={slug} />
|
||||
|
||||
{/* Record List Section */}
|
||||
<section className="space-y-6">
|
||||
<div className="flex justify-between items-center border-b border-line pb-3">
|
||||
<span className="font-mono text-xs text-ink-mute uppercase">
|
||||
Showing {records.length} Pre-rendered Records
|
||||
</span>
|
||||
<span className="font-mono text-xs text-vermillion font-bold">
|
||||
SSG ● Static HTML
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{records.length === 0 ? (
|
||||
<div className="bg-paper p-8 rounded-lg border border-line text-center space-y-3">
|
||||
<p className="font-serif text-lg text-ink">
|
||||
현재 등록된 실적이 0건입니다.
|
||||
</p>
|
||||
<p className="text-xs text-ink-mute font-mono">
|
||||
(D-01 카테고리 정류 확정 후 후속 배치가 수행됩니다)
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{records.map((rec, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="bg-paper p-6 rounded-lg border border-line space-y-3 hover:border-vermillion transition-colors"
|
||||
>
|
||||
<div className="flex justify-between items-start gap-4">
|
||||
<h3 className="font-serif text-xl font-normal text-ink leading-snug">
|
||||
{rec.title}
|
||||
</h3>
|
||||
<span className="font-mono text-xs text-vermillion bg-ivory border border-line px-2.5 py-1 rounded shrink-0 font-bold">
|
||||
{rec.publishedAt}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{rec.isPatent ? (
|
||||
<div className="space-y-1 text-xs text-ink-mute font-mono">
|
||||
<p className="text-ink">발명자: {rec.inventors}</p>
|
||||
<p>
|
||||
출원번호: {rec.appNo} ({rec.country}, {rec.appDate})
|
||||
</p>
|
||||
{rec.regNo && (
|
||||
<p className="text-cobalt font-bold">
|
||||
등록번호: {rec.regNo} ({rec.regDate})
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-1 text-xs text-ink-mute font-mono">
|
||||
<p className="text-ink font-serif italic">{rec.venue}</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<span>{rec.volume}</span>
|
||||
{rec.kci && (
|
||||
<span className="bg-cobalt text-ivory text-[0.65rem] px-1.5 py-0.5 rounded font-bold">
|
||||
KCI 등재
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { PUB_CATEGORIES } from "../../../content/categories";
|
||||
import { PubCategory } from "../../../content/types";
|
||||
|
||||
interface CategoryNavProps {
|
||||
currentCategory?: PubCategory;
|
||||
totalCount?: number;
|
||||
}
|
||||
|
||||
export function CategoryNav({ currentCategory, totalCount }: CategoryNavProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-wrap items-center gap-2 border-b border-line pb-4">
|
||||
<Link
|
||||
href="/publications"
|
||||
className={`px-4 py-2 rounded text-xs font-mono tracking-wider transition-colors ${
|
||||
!currentCategory
|
||||
? "bg-ink text-ivory font-bold"
|
||||
: "bg-paper text-ink border border-line hover:border-ink"
|
||||
}`}
|
||||
>
|
||||
All Overview {!currentCategory && totalCount ? `(${totalCount})` : ""}
|
||||
</Link>
|
||||
|
||||
{PUB_CATEGORIES.map((cat) => {
|
||||
const isActive = currentCategory === cat.slug;
|
||||
return (
|
||||
<Link
|
||||
key={cat.slug}
|
||||
href={`/publications/${cat.slug}`}
|
||||
className={`px-4 py-2 rounded text-xs font-mono tracking-wider transition-colors ${
|
||||
isActive
|
||||
? "bg-vermillion text-ivory font-bold"
|
||||
: "bg-paper text-ink border border-line hover:border-vermillion"
|
||||
}`}
|
||||
>
|
||||
{cat.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,173 +1,138 @@
|
||||
import type { Metadata } from "next";
|
||||
import PageHeader from "@/components/PageHeader";
|
||||
import Reveal from "@/components/Reveal";
|
||||
import SectionLabel from "@/components/SectionLabel";
|
||||
import Counter from "@/components/Counter";
|
||||
import Marquee from "@/components/Marquee";
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { CategoryNav } from "./_components/CategoryNav";
|
||||
import { publications, patents } from "../../content/publications";
|
||||
import { PUB_CATEGORIES } from "../../content/categories";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "논문 (Publications)",
|
||||
description: "AI 에이전트 네트워크 연구실 (ANL)의 대표 논문 목록 (placeholder).",
|
||||
};
|
||||
export default function PublicationsIndexPage() {
|
||||
const allRecords = [
|
||||
...publications.map((p) => ({ ...p, type: "paper" as const })),
|
||||
...patents.map((p) => ({
|
||||
category: "patent" as const,
|
||||
title: p.title,
|
||||
venue: `발명자: ${p.inventors} (출원번호: ${p.applicationNo})`,
|
||||
volume: p.registrationNo ? `등록번호: ${p.registrationNo}` : `출원국가: ${p.country}`,
|
||||
publishedAt: p.publishedAt,
|
||||
type: "patent" as const,
|
||||
})),
|
||||
];
|
||||
|
||||
// CUSTOMIZATION HOOK: replace placeholder publications below.
|
||||
const publications = [
|
||||
{
|
||||
year: "2025",
|
||||
type: "Journal",
|
||||
title:
|
||||
"메타버스 상호운용을 위한 공통 정보 모델 설계 및 oneM2M 매핑 (Design of a Common Information Model for Metaverse Interoperability over oneM2M)",
|
||||
authors: "Hong G., Researcher A., et al.",
|
||||
venue: "한국통신학회논문지 (J-KICS), Vol. 50, No. 3",
|
||||
},
|
||||
{
|
||||
year: "2025",
|
||||
type: "Conference",
|
||||
title:
|
||||
"QUIC-based Orchestration Architecture for Low-Latency Multi-Agent Communication",
|
||||
authors: "Researcher B., Hong G.",
|
||||
venue: "IEEE International Conference on Communications (ICC)",
|
||||
},
|
||||
{
|
||||
year: "2024",
|
||||
type: "Journal",
|
||||
title:
|
||||
"W3C WoT Thing Description를 활용한 이종 메타버스 자산의 의미 보존 변환 (Semantic-Preserving Mapping of Cross-Platform Metaverse Assets Using W3C WoT)",
|
||||
authors: "Researcher C., Hong G., et al.",
|
||||
venue: "정보과학회논문지 (KIISE Transactions), Vol. 51, No. 11",
|
||||
},
|
||||
{
|
||||
year: "2024",
|
||||
type: "Conference",
|
||||
title:
|
||||
"Stream Multiplexing Strategies for Agent Message Routing over QUIC",
|
||||
authors: "Researcher D., Researcher B., Hong G.",
|
||||
venue: "ACM/IEEE Symposium on Edge Computing (SEC)",
|
||||
},
|
||||
{
|
||||
year: "2023",
|
||||
type: "Conference",
|
||||
title:
|
||||
"oneM2M 기반 IoT 디바이스 상호운용성 적합성 검증 프레임워크 (A Conformance Testing Framework for oneM2M-based IoT Interoperability)",
|
||||
authors: "Researcher E., Hong G.",
|
||||
venue: "한국정보과학회 학술발표회 (KSC)",
|
||||
},
|
||||
];
|
||||
// Sort by publishedAt descending
|
||||
const sortedRecords = [...allRecords].sort(
|
||||
(a, b) => new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime()
|
||||
);
|
||||
|
||||
// CUSTOMIZATION HOOK: venue ticker.
|
||||
const venueTicker = [
|
||||
"J-KICS",
|
||||
"IEEE ICC",
|
||||
"KIISE TRANSACTIONS",
|
||||
"ACM/IEEE SEC",
|
||||
"KSC",
|
||||
"oneM2M",
|
||||
"W3C WoT",
|
||||
"IETF QUIC",
|
||||
];
|
||||
const recentRecords = sortedRecords.slice(0, 10);
|
||||
|
||||
// Accent per publication type, drawn from the editorial palette.
|
||||
const typeAccent: Record<string, { text: string; rule: string }> = {
|
||||
Journal: { text: "text-vermillion", rule: "bg-vermillion" },
|
||||
Conference: { text: "text-cobalt", rule: "bg-cobalt" },
|
||||
};
|
||||
const getCategoryCount = (slug: string) => {
|
||||
if (slug === "patent") return patents.length;
|
||||
return publications.filter((p) => p.category === slug).length;
|
||||
};
|
||||
|
||||
// Derived figures.
|
||||
const journalCount = publications.filter((p) => p.type === "Journal").length;
|
||||
const confCount = publications.filter((p) => p.type === "Conference").length;
|
||||
const figures = [
|
||||
{ value: publications.length, label: "전체 논문 · Total Publications" },
|
||||
{ value: journalCount, label: "저널 · Journal" },
|
||||
{ value: confCount, label: "학회 · Conference" },
|
||||
];
|
||||
|
||||
export default function PublicationsPage() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
ko="논문"
|
||||
en="Publications"
|
||||
index={3}
|
||||
description="메타버스 상호운용성(MCM)과 QUIC 기반 멀티에이전트 통신 분야의 대표 논문입니다. 아래 목록은 예시(placeholder)입니다."
|
||||
/>
|
||||
<div className="space-y-16 py-6">
|
||||
{/* Header */}
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-px w-8 bg-vermillion"></span>
|
||||
<span className="font-mono text-xs tracking-widest uppercase text-vermillion font-bold">
|
||||
Research Output & Patents
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
|
||||
연구 실적 개요
|
||||
</h1>
|
||||
<p className="text-ink-mute text-base max-w-2xl">
|
||||
국제·국내 우수 학술지 및 학술대회 논문 148건과 특허 75건 등 총 {allRecords.length}건의 실측 연구 성과 카탈로그입니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Venue marquee band */}
|
||||
<Reveal>
|
||||
<Marquee
|
||||
items={venueTicker}
|
||||
reverse
|
||||
className="border-b border-ink bg-ink py-3 font-display text-xl tracking-wide text-ivory"
|
||||
/>
|
||||
</Reveal>
|
||||
{/* Category Nav */}
|
||||
<CategoryNav totalCount={allRecords.length} />
|
||||
|
||||
{/* ============ FIGURES ============ */}
|
||||
<section className="border-b border-ink">
|
||||
<div className="container-content grid gap-px bg-line sm:grid-cols-3">
|
||||
{figures.map((f, i) => (
|
||||
<Reveal key={f.label} delay={i * 100} className="bg-ivory">
|
||||
<div className="px-6 py-10">
|
||||
<Counter value={f.value} className="display block text-ink" />
|
||||
<p className="kicker mt-3">{f.label}</p>
|
||||
{/* Category Cards Overview */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="font-serif text-2xl font-normal text-ink">
|
||||
카테고리별 실적 분류
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{PUB_CATEGORIES.map((cat) => {
|
||||
const count = getCategoryCount(cat.slug);
|
||||
return (
|
||||
<div
|
||||
key={cat.slug}
|
||||
className="bg-paper p-6 rounded-lg border border-line flex flex-col justify-between hover:border-vermillion transition-colors"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-baseline">
|
||||
<span className="font-mono text-xs text-vermillion font-bold uppercase">
|
||||
Category
|
||||
</span>
|
||||
<span className="font-serif text-2xl font-normal text-ink">
|
||||
{count}건
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="font-serif text-xl font-normal text-ink">
|
||||
{cat.label}
|
||||
</h3>
|
||||
<p className="text-xs text-ink-mute leading-relaxed">
|
||||
{cat.desc}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-6 pt-4 border-t border-line flex justify-between items-center">
|
||||
<span className="font-mono text-xs text-ink-mute">
|
||||
SSG Pre-rendered
|
||||
</span>
|
||||
<Link
|
||||
href={`/publications/${cat.slug}`}
|
||||
className="font-mono text-xs text-vermillion font-bold hover:underline"
|
||||
>
|
||||
목록 보기 ({count}) ↗
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Recent 10 Highlight Records */}
|
||||
<section className="space-y-6 pt-6 border-t border-line">
|
||||
<div className="flex justify-between items-end">
|
||||
<h2 className="font-serif text-2xl font-normal text-ink">
|
||||
최신 성과 하이라이트 (Top 10)
|
||||
</h2>
|
||||
<span className="font-mono text-xs text-ink-mute">
|
||||
Total {allRecords.length} Records
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{recentRecords.map((rec, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="bg-paper p-5 rounded border border-line space-y-2"
|
||||
>
|
||||
<div className="flex justify-between items-start gap-4">
|
||||
<h3 className="font-serif text-lg font-normal text-ink leading-snug">
|
||||
{rec.title}
|
||||
</h3>
|
||||
<span className="font-mono text-xs text-vermillion bg-ivory border border-line px-2 py-0.5 rounded shrink-0">
|
||||
{rec.publishedAt}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-ink-mute font-mono">
|
||||
{rec.venue}
|
||||
</div>
|
||||
{rec.volume && (
|
||||
<div className="text-xs text-ink-mute font-mono">
|
||||
{rec.volume}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ============ PUBLICATION LIST ============ */}
|
||||
<section>
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={1} label="Selected Works" />
|
||||
</Reveal>
|
||||
|
||||
<ol className="mt-12 border-t border-ink">
|
||||
{publications.map((p, i) => {
|
||||
const accent = typeAccent[p.type] ?? {
|
||||
text: "text-ink",
|
||||
rule: "bg-ink",
|
||||
};
|
||||
// Reveal `as="li"` keeps the <ol> direct-child contract (HTML spec:
|
||||
// <ol> may only contain <li>/<script>/<template>). See REVIEW.md §3 #1.
|
||||
return (
|
||||
<Reveal
|
||||
as="li"
|
||||
key={p.title}
|
||||
delay={(i % 3) * 80}
|
||||
className="group grid gap-6 border-b border-ink py-8 transition-colors duration-500 hover:bg-paper sm:grid-cols-12 sm:py-10"
|
||||
>
|
||||
{/* Index + year rail */}
|
||||
<div className="flex items-baseline gap-4 sm:col-span-3 sm:flex-col sm:gap-3">
|
||||
<span className="font-display text-5xl leading-none text-ink-mute">
|
||||
{String(i + 1).padStart(2, "0")}
|
||||
</span>
|
||||
<span className="font-display text-2xl italic text-ink">
|
||||
{p.year}
|
||||
</span>
|
||||
<span
|
||||
className={`inline-flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.16em] ${accent.text}`}
|
||||
>
|
||||
<span className={`h-1.5 w-1.5 rounded-full ${accent.rule}`} />
|
||||
{p.type}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Title + meta */}
|
||||
<div className="sm:col-span-9">
|
||||
<h3 className="headline-ko text-xl leading-snug text-ink sm:text-2xl">
|
||||
{p.title}
|
||||
</h3>
|
||||
<p className="mt-3 text-sm text-ink-soft">{p.authors}</p>
|
||||
<p className="font-display text-sm italic text-ink-mute">
|
||||
{p.venue}
|
||||
</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { MetadataRoute } from "next";
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: {
|
||||
userAgent: "*",
|
||||
allow: "/",
|
||||
},
|
||||
sitemap: "https://anl.knu.ac.kr/sitemap.xml",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { MetadataRoute } from "next";
|
||||
import { PUB_CATEGORIES } from "../content/categories";
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const baseUrl = "https://anl.knu.ac.kr";
|
||||
|
||||
const staticRoutes: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: `${baseUrl}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 1.0,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/members`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/publications`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/lectures`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.7,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/standardization`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
];
|
||||
|
||||
const categoryRoutes: MetadataRoute.Sitemap = PUB_CATEGORIES.map((cat) => ({
|
||||
url: `${baseUrl}/publications/${cat.slug}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
}));
|
||||
|
||||
return [...staticRoutes, ...categoryRoutes];
|
||||
}
|
||||
@@ -1,173 +1,117 @@
|
||||
import type { Metadata } from "next";
|
||||
import PageHeader from "@/components/PageHeader";
|
||||
import Reveal from "@/components/Reveal";
|
||||
import SectionLabel from "@/components/SectionLabel";
|
||||
import Counter from "@/components/Counter";
|
||||
import Marquee from "@/components/Marquee";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "표준화 활동 (Standardization)",
|
||||
description:
|
||||
"oneM2M, W3C WoT, IETF QUIC, OMA LwM2M 등 국제 표준화 기구에서의 연구실 기여 활동 (placeholder).",
|
||||
};
|
||||
|
||||
// CUSTOMIZATION HOOK: replace placeholder standardization activities below.
|
||||
const bodies = [
|
||||
{
|
||||
org: "oneM2M",
|
||||
full: "oneM2M Partnership Project",
|
||||
area: "IoT 서비스 계층 표준",
|
||||
accent: "vermillion" as const,
|
||||
contributions: [
|
||||
"메타버스 상호운용을 위한 공통 정보 모델(CIM) 기고 (placeholder)",
|
||||
"디바이스 상호운용성 적합성 테스트 케이스 제안",
|
||||
],
|
||||
},
|
||||
{
|
||||
org: "W3C WoT",
|
||||
full: "W3C Web of Things",
|
||||
area: "Thing Description / 시맨틱",
|
||||
accent: "cobalt" as const,
|
||||
contributions: [
|
||||
"메타버스 자산의 Thing Description 매핑 프로파일 제안 (placeholder)",
|
||||
"의미 보존 변환 가이드라인 논의 참여",
|
||||
],
|
||||
},
|
||||
{
|
||||
org: "IETF QUIC",
|
||||
full: "IETF QUIC Working Group",
|
||||
area: "전송 프로토콜",
|
||||
accent: "vermillion" as const,
|
||||
contributions: [
|
||||
"멀티에이전트 환경을 위한 스트림 우선순위 확장 검토 (placeholder)",
|
||||
"연결 마이그레이션 활용 사례 기여",
|
||||
],
|
||||
},
|
||||
{
|
||||
org: "OMA LwM2M",
|
||||
full: "OMA SpecWorks Lightweight M2M",
|
||||
area: "경량 디바이스 관리",
|
||||
accent: "cobalt" as const,
|
||||
contributions: [
|
||||
"엣지 에이전트 디바이스 관리 객체 정의 제안 (placeholder)",
|
||||
"리소스 모델 상호운용성 검토",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// CUSTOMIZATION HOOK: standards-body ticker.
|
||||
const orgTicker = [
|
||||
"oneM2M",
|
||||
"W3C WoT",
|
||||
"IETF QUIC",
|
||||
"OMA LwM2M",
|
||||
"CONFORMANCE",
|
||||
"INTEROPERABILITY",
|
||||
"SEMANTIC MAPPING",
|
||||
];
|
||||
|
||||
const totalContributions = bodies.reduce(
|
||||
(n, b) => n + b.contributions.length,
|
||||
0
|
||||
);
|
||||
const figures = [
|
||||
{ value: bodies.length, label: "표준화 기구 · Standards Bodies" },
|
||||
{ value: totalContributions, label: "기여 항목 · Contributions" },
|
||||
{ value: 4, label: "기술 영역 · Technical Areas" },
|
||||
];
|
||||
|
||||
const accentText: Record<string, string> = {
|
||||
vermillion: "text-vermillion",
|
||||
cobalt: "text-cobalt",
|
||||
};
|
||||
const accentDot: Record<string, string> = {
|
||||
vermillion: "bg-vermillion",
|
||||
cobalt: "bg-cobalt",
|
||||
};
|
||||
import React from "react";
|
||||
import { standardsBodies } from "../../content/standards";
|
||||
|
||||
export default function StandardizationPage() {
|
||||
const totalDocs = standardsBodies.reduce(
|
||||
(acc, body) =>
|
||||
acc + body.projects.reduce((pAcc, proj) => pAcc + proj.documents.length, 0),
|
||||
0
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
ko="표준화 활동"
|
||||
en="Standardization"
|
||||
index={5}
|
||||
description="국제 표준화 기구와 협력하여 연구 결과를 표준에 반영합니다. 아래 기여 내역은 예시(placeholder)이며 실제 활동으로 교체할 수 있습니다."
|
||||
/>
|
||||
|
||||
{/* Standards-body marquee band */}
|
||||
<Reveal>
|
||||
<Marquee
|
||||
items={orgTicker}
|
||||
className="border-b border-ink bg-ink py-3 font-display text-xl tracking-wide text-ivory"
|
||||
/>
|
||||
</Reveal>
|
||||
|
||||
{/* ============ FIGURES ============ */}
|
||||
<section className="border-b border-ink">
|
||||
<div className="container-content grid gap-px bg-line sm:grid-cols-3">
|
||||
{figures.map((f, i) => (
|
||||
<Reveal key={f.label} delay={i * 100} className="bg-ivory">
|
||||
<div className="px-6 py-10">
|
||||
<Counter value={f.value} className="display block text-ink" />
|
||||
<p className="kicker mt-3">{f.label}</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
<div className="space-y-16 py-6">
|
||||
{/* Header */}
|
||||
<section className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-px w-8 bg-vermillion"></span>
|
||||
<span className="font-mono text-xs tracking-widest uppercase text-vermillion font-bold">
|
||||
Open Standards & Contributions
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink">
|
||||
표준화 연구 및 기여
|
||||
</h1>
|
||||
<p className="text-ink-mute text-base max-w-2xl">
|
||||
IEC TC100, ISO/IEC JTC1, ITU-T, TTA 등 국제/국내 표준화 기구를 통해 제정 및 진행 중인 {standardsBodies.length}개 기구 {totalDocs}건의 표준 규격 문서 계층 인벤토리입니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* ============ CONTRIBUTIONS BY BODY ============ */}
|
||||
<section>
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={1} label="Contributions" />
|
||||
</Reveal>
|
||||
{/* Standards Bodies Section */}
|
||||
<section className="space-y-12 pt-6 border-t border-line">
|
||||
{standardsBodies.map((body, idx) => (
|
||||
<div key={idx} className="bg-paper p-8 rounded-lg border border-line space-y-6">
|
||||
<div className="flex flex-wrap justify-between items-start gap-4 border-b border-line pb-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-mono text-xs text-vermillion font-bold uppercase">
|
||||
[{body.scope}]
|
||||
</span>
|
||||
{body.role && (
|
||||
<span className="bg-vermillion text-ivory font-mono text-[0.65rem] px-2 py-0.5 rounded font-bold">
|
||||
{body.role}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<h2 className="font-serif text-2xl font-normal text-ink mt-1">
|
||||
{body.org}
|
||||
</h2>
|
||||
<p className="text-xs text-ink-mute font-mono mt-0.5">
|
||||
{body.full} ({body.period})
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 grid gap-px bg-line sm:grid-cols-2">
|
||||
{bodies.map((b, i) => (
|
||||
<Reveal
|
||||
key={b.org}
|
||||
variant={i % 2 === 0 ? "left" : "right"}
|
||||
delay={(i % 2) * 100}
|
||||
className="bg-ivory"
|
||||
>
|
||||
<article className="group flex h-full flex-col border border-ink p-8 transition-colors duration-500 hover:bg-ink hover:text-ivory sm:p-10">
|
||||
<div className="flex items-baseline justify-between gap-3">
|
||||
<h3 className="headline-ko text-3xl">{b.org}</h3>
|
||||
<span className="font-display text-4xl text-ink-mute group-hover:text-ivory/40">
|
||||
{String(i + 1).padStart(2, "0")}
|
||||
<div className="font-mono text-xs text-ink-mute bg-ivory border border-line px-3 py-1.5 rounded">
|
||||
Projects: {body.projects.length}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3-Tier Hierarchy: Projects & Documents */}
|
||||
<div className="space-y-6">
|
||||
{body.projects.map((proj, pIdx) => (
|
||||
<div key={pIdx} className="bg-ivory p-6 rounded border border-line space-y-3">
|
||||
<div className="font-serif text-lg font-normal text-ink border-b border-line/60 pb-2 flex justify-between items-center">
|
||||
<div>
|
||||
<span className="font-mono text-xs text-cobalt font-bold mr-2">
|
||||
{proj.wg}
|
||||
</span>
|
||||
<span>{proj.name}</span>
|
||||
</div>
|
||||
<span className="font-mono text-xs text-ink-mute">
|
||||
{proj.documents.length} Docs
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-display mt-1 text-sm italic opacity-70">{b.full}</p>
|
||||
|
||||
<p
|
||||
className={`mt-4 inline-flex w-fit border px-3 py-1 text-xs font-semibold uppercase tracking-[0.16em] ${
|
||||
b.accent === "vermillion"
|
||||
? "border-vermillion/40"
|
||||
: "border-cobalt/40"
|
||||
} ${accentText[b.accent]} group-hover:border-ivory/40 group-hover:text-ivory`}
|
||||
>
|
||||
{b.area}
|
||||
</p>
|
||||
|
||||
<ul className="mt-6 space-y-4 border-t border-ink/20 pt-6 text-sm group-hover:border-ivory/20">
|
||||
{b.contributions.map((c) => (
|
||||
<li key={c} className="flex gap-3">
|
||||
<span
|
||||
aria-hidden
|
||||
className={`mt-1.5 h-1.5 w-1.5 flex-none rounded-full ${accentDot[b.accent]} group-hover:bg-ivory`}
|
||||
/>
|
||||
<span className="leading-relaxed opacity-90">{c}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
</Reveal>
|
||||
))}
|
||||
{proj.documents.length > 0 ? (
|
||||
<div className="space-y-3 pt-1">
|
||||
{proj.documents.map((doc, dIdx) => (
|
||||
<div
|
||||
key={dIdx}
|
||||
className="bg-paper p-4 rounded border border-line/60 space-y-1.5"
|
||||
>
|
||||
<div className="flex justify-between items-start gap-4">
|
||||
<h3 className="font-serif text-base font-normal text-ink leading-snug">
|
||||
“{doc.title}”
|
||||
</h3>
|
||||
<span
|
||||
className={`font-mono text-[0.65rem] px-2 py-0.5 rounded font-bold shrink-0 ${
|
||||
doc.status === "InProgress"
|
||||
? "bg-vermillion text-ivory"
|
||||
: "bg-cobalt text-ivory"
|
||||
}`}
|
||||
>
|
||||
{doc.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="font-mono text-xs text-ink-mute flex justify-between">
|
||||
<span>{doc.ref}</span>
|
||||
{doc.publishedAt && (
|
||||
<span className="text-ink">{doc.publishedAt}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-xs text-ink-mute font-mono italic">
|
||||
진행 중인 주 프로젝트 규격서
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user