feat: initialize IoT Standards Lab website project with MAM skills & onboarding guide
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* ==================================================================
|
||||
EDITORIAL DESIGN SYSTEM — global tokens & type
|
||||
CUSTOMIZATION HOOK: design tokens are mirrored from tailwind.config.ts
|
||||
as CSS variables so plain CSS (grain, gradients) can reuse them.
|
||||
================================================================== */
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--ink: #0a0a0a;
|
||||
--ivory: #f5f1e8;
|
||||
--paper: #ece6d6;
|
||||
--line: #d8d1c0;
|
||||
--vermillion: #d9342b;
|
||||
--cobalt: #1b3a8a;
|
||||
|
||||
/* CUSTOMIZATION HOOK — MOTION: global reveal timing */
|
||||
--reveal-duration: 600ms;
|
||||
--reveal-ease: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-ivory text-ink font-sans antialiased;
|
||||
/* Subtle paper grain — pure CSS, no asset. */
|
||||
background-image: radial-gradient(
|
||||
rgba(10, 10, 10, 0.022) 1px,
|
||||
transparent 1px
|
||||
);
|
||||
background-size: 4px 4px;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--vermillion);
|
||||
color: var(--ivory);
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
COMPONENT LAYER — editorial primitives
|
||||
================================================================== */
|
||||
@layer components {
|
||||
.container-content {
|
||||
@apply mx-auto w-full max-w-content px-5 sm:px-8 lg:px-12;
|
||||
}
|
||||
|
||||
/* Massive magazine display title (English serif). */
|
||||
.display {
|
||||
@apply font-display text-display font-light;
|
||||
font-optical-sizing: auto;
|
||||
}
|
||||
|
||||
/* Smaller display title — used for long fallback words (PageHeader `en`)
|
||||
and figures that should not read at the full cover scale (e.g. the
|
||||
"2026" issue year on /intro). Mirrors `.display` at the `display-sm`
|
||||
type ramp. See LAYOUT_AUDIT #6 + #10. */
|
||||
.display-sm {
|
||||
@apply font-display text-display-sm font-light;
|
||||
font-optical-sizing: auto;
|
||||
}
|
||||
|
||||
/* Korean serif headline. */
|
||||
.headline-ko {
|
||||
@apply font-serif-ko font-semibold tracking-tight;
|
||||
}
|
||||
|
||||
/* Small-caps kicker / eyebrow with wide tracking. */
|
||||
.kicker {
|
||||
@apply font-sans text-[0.7rem] font-semibold uppercase tracking-caps text-ink-mute;
|
||||
}
|
||||
|
||||
/* Corner "01 / 05" spread numbering.
|
||||
Bumped from text-sm (14px) to text-base (16px) so the editorial
|
||||
spread number reads as "page number", not body copy.
|
||||
See LAYOUT_AUDIT #17. */
|
||||
.corner-label {
|
||||
@apply font-display text-base font-medium tabular-nums tracking-wide text-ink-mute;
|
||||
}
|
||||
|
||||
/* Huge italic serif pull-quote. */
|
||||
.pull-quote {
|
||||
@apply font-display text-3xl font-light italic leading-tight sm:text-4xl lg:text-5xl;
|
||||
}
|
||||
|
||||
/* Hairline editorial rule. */
|
||||
.rule {
|
||||
@apply border-t border-ink/80;
|
||||
}
|
||||
|
||||
/* Magazine inset card on dimmer paper. */
|
||||
.spread-card {
|
||||
@apply border border-ink/15 bg-paper transition duration-500;
|
||||
}
|
||||
|
||||
/* Animated underline-draw on hover (for links). */
|
||||
.draw-underline {
|
||||
background-image: linear-gradient(var(--vermillion), var(--vermillion));
|
||||
background-position: 0 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 0% 2px;
|
||||
transition: background-size 0.4s var(--reveal-ease);
|
||||
}
|
||||
.draw-underline:hover {
|
||||
background-size: 100% 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
MOTION — scroll reveal (paired with components/Reveal.tsx)
|
||||
CUSTOMIZATION HOOK — MOTION: edit transforms / duration here.
|
||||
================================================================== */
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
transform: translateY(28px);
|
||||
transition: opacity var(--reveal-duration) var(--reveal-ease),
|
||||
transform var(--reveal-duration) var(--reveal-ease);
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
.reveal[data-variant="left"] {
|
||||
transform: translateX(-40px);
|
||||
}
|
||||
.reveal[data-variant="right"] {
|
||||
transform: translateX(40px);
|
||||
}
|
||||
.reveal[data-variant="scale"] {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
.reveal.is-visible {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Marquee track must be 2× content for a seamless loop. */
|
||||
.marquee-track {
|
||||
display: inline-flex;
|
||||
white-space: nowrap;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Respect users who prefer reduced motion. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.reveal {
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
transition: none;
|
||||
}
|
||||
.marquee-track,
|
||||
[class*="animate-"] {
|
||||
animation: none !important;
|
||||
}
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Abstract editorial hero — a custom illustration (inline SVG, no asset).
|
||||
* • MCM → a constellation of interconnected nodes (vermillion).
|
||||
* • QUIC → flowing, multiplexed streams (cobalt), animated dash flow.
|
||||
* Animations are pure CSS (tailwind keyframes: node-pulse, stream-dash).
|
||||
*
|
||||
* CUSTOMIZATION HOOK — palette is inherited from CSS vars; geometry below.
|
||||
*/
|
||||
export default function HeroComposition({
|
||||
className = "",
|
||||
}: {
|
||||
className?: string;
|
||||
}) {
|
||||
// MCM node coordinates (interconnected mesh, upper-left field).
|
||||
const nodes = [
|
||||
{ x: 70, y: 90 },
|
||||
{ x: 160, y: 60 },
|
||||
{ x: 130, y: 170 },
|
||||
{ x: 230, y: 130 },
|
||||
{ x: 60, y: 210 },
|
||||
{ x: 210, y: 240 },
|
||||
];
|
||||
// Mesh edges between nodes (index pairs).
|
||||
const edges: [number, number][] = [
|
||||
[0, 1],
|
||||
[0, 2],
|
||||
[1, 3],
|
||||
[2, 3],
|
||||
[2, 4],
|
||||
[3, 5],
|
||||
[2, 5],
|
||||
[4, 5],
|
||||
];
|
||||
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 380 380"
|
||||
role="img"
|
||||
aria-label="MCM 상호운용 노드와 QUIC 멀티스트림을 형상화한 추상 일러스트레이션"
|
||||
className={className}
|
||||
>
|
||||
{/* Frame */}
|
||||
<rect
|
||||
x="6"
|
||||
y="6"
|
||||
width="368"
|
||||
height="368"
|
||||
fill="none"
|
||||
stroke="#0A0A0A"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
|
||||
{/* ---- QUIC: flowing multiplexed streams (cobalt) ---- */}
|
||||
<g stroke="#1B3A8A" fill="none" strokeWidth="2.5" strokeLinecap="round">
|
||||
{[0, 1, 2, 3].map((i) => {
|
||||
const y = 250 + i * 30;
|
||||
return (
|
||||
<path
|
||||
key={i}
|
||||
d={`M 30 ${y} C 140 ${y - 40}, 240 ${y + 40}, 360 ${y - 20}`}
|
||||
strokeDasharray="10 14"
|
||||
className="animate-stream-dash"
|
||||
style={{ animationDelay: `${i * 0.6}s`, opacity: 0.85 }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</g>
|
||||
|
||||
{/* ---- MCM: interconnected node mesh (vermillion) ---- */}
|
||||
<g stroke="#D9342B" strokeWidth="1.5">
|
||||
{edges.map(([a, b], i) => (
|
||||
<line
|
||||
key={i}
|
||||
x1={nodes[a].x}
|
||||
y1={nodes[a].y}
|
||||
x2={nodes[b].x}
|
||||
y2={nodes[b].y}
|
||||
opacity="0.55"
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
<g fill="#D9342B">
|
||||
{nodes.map((n, i) => (
|
||||
<circle
|
||||
key={i}
|
||||
cx={n.x}
|
||||
cy={n.y}
|
||||
r={i % 2 === 0 ? 9 : 6}
|
||||
className="animate-node-pulse"
|
||||
style={{
|
||||
animationDelay: `${i * 0.5}s`,
|
||||
transformBox: "fill-box",
|
||||
transformOrigin: "center",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
|
||||
{/* Annotations — placed in the top corners (y=24), clear of the
|
||||
MCM node mesh (y=60..240) and the QUIC stream curves (y≥250 with
|
||||
control points reaching y+40=380). See LAYOUT_AUDIT #3 and
|
||||
REVIEW §3 "P1 #3" — keeping them at y=24 (mirror corners) preserves
|
||||
a symmetric layout that the original LAYOUT_AUDIT mirror decided on. */}
|
||||
<text x="20" y="24" fill="#0A0A0A" fontSize="11" fontWeight="600" letterSpacing="2">
|
||||
MCM · MESH
|
||||
</text>
|
||||
<text
|
||||
x="360"
|
||||
y="24"
|
||||
textAnchor="end"
|
||||
fill="#0A0A0A"
|
||||
fontSize="11"
|
||||
fontWeight="600"
|
||||
letterSpacing="2"
|
||||
>
|
||||
QUIC · STREAMS
|
||||
</text>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
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:
|
||||
"메타버스 상호운용성(MCM)과 QUIC 기반 멀티에이전트 오케스트레이션을 연구하는 경북대학교 사물인터넷 표준 연구실 소개.",
|
||||
};
|
||||
|
||||
// CUSTOMIZATION HOOK: the two research thrusts (cover spread comparison).
|
||||
const thrusts = [
|
||||
{
|
||||
no: "Thrust 01",
|
||||
tag: "MCM",
|
||||
accent: "vermillion" as const,
|
||||
ko: "메타버스 상호운용성",
|
||||
en: "Metaverse Interoperability",
|
||||
desc: "이종(異種) 메타버스 플랫폼 간 객체·아바타·자산의 상호운용을 위한 공통 데이터 모델과 변환 계층을 설계합니다. oneM2M / W3C 표준을 기반으로 서로 다른 가상 환경이 동일한 의미(semantics)로 데이터를 교환합니다.",
|
||||
points: [
|
||||
"공통 정보 모델(Common Information Model) 정의",
|
||||
"의미 보존 변환(semantic-preserving mapping) 계층",
|
||||
"상호운용성 테스트베드 및 적합성 검증",
|
||||
],
|
||||
},
|
||||
{
|
||||
no: "Thrust 02",
|
||||
tag: "QUIC",
|
||||
accent: "cobalt" as const,
|
||||
ko: "멀티에이전트 오케스트레이션",
|
||||
en: "Multi-Agent Orchestration",
|
||||
desc: "다수의 자율 에이전트가 저지연·다중스트림 환경에서 협력하도록 QUIC 전송 위에 오케스트레이션 아키텍처와 통신 인터페이스를 설계합니다. 연결 마이그레이션과 스트림 다중화로 메시지 라우팅을 최적화합니다.",
|
||||
points: [
|
||||
"QUIC 스트림 다중화 기반 에이전트 메시지 라우팅",
|
||||
"오케스트레이터–에이전트 제어 인터페이스 설계",
|
||||
"연결 마이그레이션 기반 모바일·엣지 지원",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// CUSTOMIZATION HOOK: headline figures (animated counters).
|
||||
// `value: 2026` is marked static so it renders as a fixed year, not a
|
||||
// 0→2026 count-up. The year is also rendered at the smaller `display-sm`
|
||||
// scale (the other two figures use the full `display` scale), so the
|
||||
// 4-digit "2026" does not push the cell padding. See LAYOUT_AUDIT #11 + #13.
|
||||
const figures = [
|
||||
{ value: 2, label: "연구 축 · Research Thrusts", suffix: "", size: "display" as const, static: false },
|
||||
{ value: 4, label: "표준화 기구 · Standards Bodies", 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 = [
|
||||
"INTEROPERABILITY",
|
||||
"oneM2M",
|
||||
"W3C WoT",
|
||||
"QUIC TRANSPORT",
|
||||
"MULTI-AGENT",
|
||||
"STREAM MULTIPLEXING",
|
||||
"CONNECTION MIGRATION",
|
||||
"METAVERSE",
|
||||
"SEMANTIC MAPPING",
|
||||
"CONFORMANCE",
|
||||
];
|
||||
|
||||
const focusAreas = [
|
||||
{ ko: "사물인터넷 표준화", en: "IoT Standardization" },
|
||||
{ ko: "메타버스 상호운용성", en: "Metaverse Interoperability" },
|
||||
{ ko: "전송 프로토콜(QUIC)", en: "Transport Protocols" },
|
||||
{ ko: "멀티에이전트 시스템", en: "Multi-Agent Systems" },
|
||||
];
|
||||
|
||||
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-center gap-10 lg:grid-cols-12">
|
||||
{/* Headline column */}
|
||||
<div className="lg:col-span-7">
|
||||
<Reveal delay={60}>
|
||||
<p className="kicker">경북대학교 컴퓨터학부 · IoT Standards Lab</p>
|
||||
</Reveal>
|
||||
<Reveal delay={120}>
|
||||
{/* Korean + English mixed, magazine-cover scale */}
|
||||
<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>
|
||||
Standards
|
||||
<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">
|
||||
메타버스 상호운용성(MCM)과 QUIC 기반 멀티에이전트 오케스트레이션.
|
||||
국제 표준을 토대로 분산되고 이종적인 시스템이 동일한 의미로
|
||||
데이터를 교환하고 협력하도록 설계합니다.
|
||||
</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 — MCM 노드 메시 × QUIC 멀티스트림
|
||||
</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 */}
|
||||
† 본 연구실은 사물인터넷 국제 표준을 토대로, 분산·이종 시스템이
|
||||
동일한 의미로 데이터를 교환하고 협력할 수 있는 상호운용 기술을 연구합니다.
|
||||
</p>
|
||||
</Reveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ============ TWO THRUSTS — SIDE BY SIDE WITH CONNECTING LINE ============ */}
|
||||
<section className="border-b border-ink">
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={3} label="Two Research Thrusts" />
|
||||
</Reveal>
|
||||
|
||||
<div className="relative mt-12 grid gap-px lg:grid-cols-2">
|
||||
{/* Connecting line + node between the two thrusts (desktop) */}
|
||||
<div
|
||||
aria-hidden
|
||||
className="absolute left-1/2 top-1/2 hidden -translate-x-1/2 -translate-y-1/2 lg:block"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span className="h-2 w-2 rounded-full bg-vermillion" />
|
||||
<span className="h-px w-16 bg-ink" />
|
||||
<span className="flex h-9 w-9 items-center justify-center rounded-full border border-ink bg-ivory font-display text-xs">
|
||||
×
|
||||
</span>
|
||||
<span className="h-px w-16 bg-ink" />
|
||||
<span className="h-2 w-2 rounded-full bg-cobalt" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{thrusts.map((t, i) => {
|
||||
const isVerm = t.accent === "vermillion";
|
||||
return (
|
||||
<Reveal
|
||||
key={t.tag}
|
||||
variant={i === 0 ? "left" : "right"}
|
||||
delay={i * 120}
|
||||
>
|
||||
<article
|
||||
className={`group h-full 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 className="flex items-baseline justify-between">
|
||||
<span className="kicker group-hover:text-ivory/70">{t.no}</span>
|
||||
<span
|
||||
className={`font-display text-5xl ${
|
||||
isVerm ? "text-vermillion" : "text-cobalt"
|
||||
} group-hover:text-ivory`}
|
||||
>
|
||||
{t.tag}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="headline-ko mt-6 text-3xl leading-tight">
|
||||
{t.ko}
|
||||
</h3>
|
||||
<p className="font-display mt-1 text-lg italic opacity-70">
|
||||
{t.en}
|
||||
</p>
|
||||
<p className="mt-5 text-sm leading-relaxed opacity-90">
|
||||
{t.desc}
|
||||
</p>
|
||||
<ul className="mt-6 space-y-3 text-sm">
|
||||
{t.points.map((p) => (
|
||||
<li key={p} 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>{p}</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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Fraunces, Noto_Serif_KR, Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
/**
|
||||
* CUSTOMIZATION HOOK — FONTS
|
||||
* The editorial system pairs an English display serif (Fraunces) and a
|
||||
* Korean serif (Noto Serif KR) for headlines, with a clean sans for body.
|
||||
* Pretendard is preferred for body but is not served by Google Fonts, so
|
||||
* Inter is loaded as the web fallback (see tailwind.config.ts sans stack).
|
||||
*/
|
||||
const fraunces = Fraunces({
|
||||
subsets: ["latin"],
|
||||
weight: ["300", "400", "500", "600"],
|
||||
style: ["normal", "italic"],
|
||||
variable: "--font-display",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const notoSerifKr = Noto_Serif_KR({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "600", "700"],
|
||||
variable: "--font-serif-ko",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-sans",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "사물인터넷 표준 연구실 (IoT Standards Lab) · 경북대학교",
|
||||
template: "%s · 사물인터넷 표준 연구실",
|
||||
},
|
||||
description:
|
||||
"경북대학교 컴퓨터학부 사물인터넷 표준 연구실. 메타버스 상호운용성(MCM) 및 QUIC 기반 멀티에이전트 오케스트레이션 연구.",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html
|
||||
lang="ko"
|
||||
className={`${fraunces.variable} ${notoSerifKr.variable} ${inter.variable}`}
|
||||
>
|
||||
<head>
|
||||
{/* No-JS fallback for the .reveal scroll-reveal: without this, every
|
||||
page is blank if hydration fails (e.g. Google Fonts request blocked). */}
|
||||
<noscript>
|
||||
<style>{`.reveal { opacity: 1 !important; transform: none !important; }`}</style>
|
||||
</noscript>
|
||||
</head>
|
||||
<body className="flex min-h-screen flex-col">
|
||||
<Header />
|
||||
<main className="flex-1">{children}</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
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: "사물인터넷 표준 연구실 지도교수가 담당하는 강의 목록 (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" },
|
||||
};
|
||||
|
||||
export default function LecturesPage() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
ko="강의"
|
||||
en="Lectures"
|
||||
index={4}
|
||||
description="연구실에서 담당하는 학부·대학원 강의입니다. 아래 강의 정보는 예시(placeholder)이며 실제 개설 과목으로 교체할 수 있습니다."
|
||||
/>
|
||||
|
||||
<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>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
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: "사물인터넷 표준 연구실 지도교수 및 대학원·학부 연구원 소개 (placeholder).",
|
||||
};
|
||||
|
||||
// CUSTOMIZATION HOOK: replace placeholder member data below.
|
||||
const advisor = {
|
||||
ko: "홍길동 교수",
|
||||
en: "Prof. Gildong Hong",
|
||||
role: "지도교수 · Principal Investigator",
|
||||
desc: "사물인터넷 표준, 메타버스 상호운용성, 전송 프로토콜. (이메일 placeholder: pi@example.knu.ac.kr)",
|
||||
fields: ["IoT Standardization", "Metaverse Interoperability", "QUIC 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",
|
||||
};
|
||||
|
||||
export default function MembersPage() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
ko="구성원"
|
||||
en="Members"
|
||||
index={2}
|
||||
description="연구실 지도교수와 박사·석사·학부 연구원을 소개합니다. 아래 정보는 예시(placeholder)이며 실제 구성원 정보로 교체할 수 있습니다."
|
||||
/>
|
||||
|
||||
{/* ============ 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>
|
||||
))}
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ============ RESEARCH GROUPS ============ */}
|
||||
<section>
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={2} label="Researchers" />
|
||||
</Reveal>
|
||||
|
||||
{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}
|
||||
</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>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
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: "논문 (Publications)",
|
||||
description: "사물인터넷 표준 연구실의 대표 논문 목록 (placeholder).",
|
||||
};
|
||||
|
||||
// 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)",
|
||||
},
|
||||
];
|
||||
|
||||
// CUSTOMIZATION HOOK: venue ticker.
|
||||
const venueTicker = [
|
||||
"J-KICS",
|
||||
"IEEE ICC",
|
||||
"KIISE TRANSACTIONS",
|
||||
"ACM/IEEE SEC",
|
||||
"KSC",
|
||||
"oneM2M",
|
||||
"W3C WoT",
|
||||
"IETF QUIC",
|
||||
];
|
||||
|
||||
// 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" },
|
||||
};
|
||||
|
||||
// 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)입니다."
|
||||
/>
|
||||
|
||||
{/* 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>
|
||||
|
||||
{/* ============ 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>
|
||||
</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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
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",
|
||||
};
|
||||
|
||||
export default function StandardizationPage() {
|
||||
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>
|
||||
</section>
|
||||
|
||||
{/* ============ CONTRIBUTIONS BY BODY ============ */}
|
||||
<section>
|
||||
<div className="container-content py-16 sm:py-24">
|
||||
<Reveal>
|
||||
<SectionLabel index={1} label="Contributions" />
|
||||
</Reveal>
|
||||
|
||||
<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")}
|
||||
</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>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user