The Source.
이 블로그를 구성하는 실제 소스 코드(Raw Code)의 로직을 탐색합니다.
src / pages
ReadOnly* 파일을 클릭하면 우측 에디터에
실제 로직(Frontmatter)이 로드됩니다.
ASTROindex.astro
Source Viewer---
// src/pages/index.astro
import SubLayout from "../layouts/SubLayout.astro";
import { MAIN_MENU } from "../data/navigation";
import SectionSplit from "../components/SectionSplit.astro";
import SectionCentered from "../components/SectionCentered.astro";
import SectionCta from "../components/SectionCta.astro";
import { team, story } from "../data/aboutData";
import { Icons } from "../components/icons/AboutIcons";
import Badge from "../components/Badge.astro";
import Button from "../components/Button.astro";
import toolsUpdates from "../data/toolsUpdates.json";
------
// src/pages/about.astro
import SubLayout from "../layouts/SubLayout.astro";
import SubPageTitle from "../components/SubPageTitle.astro";
import SectionCentered from "../components/SectionCentered.astro";
import SectionCta from "../components/SectionCta.astro";
import Button from "../components/Button.astro";
import { MAIN_MENU } from "../data/navigation";
// Imported Data & Icons
import { team } from "../data/aboutData";
import { Icons } from "../components/icons/AboutIcons";
------
// src/pages/kitchen/ingredients.astro
import SubLayout from "../../layouts/SubLayout.astro";
import SubPageTitle from "../../components/SubPageTitle.astro";
import SectionCentered from "../../components/SectionCentered.astro";
import { KITCHEN_MENU } from "../../data/navigation";
import { siteConfig } from "../../data/siteConfig";
// Define the system specification categories and items directly
const techSpecs = [
{
id: "spec-brew",
category: "Client-side Brew",
description:
"사용자가 접하는 화면을 가볍고 직관적으로 그려내는 핵심 웹 프레임워크와 스타일 엔진입니다.",
bgClass: "bg-[#f6f8f5]",
items: [
{
name: `Astro Framework (${siteConfig.dependencies.astro})`,
type: "Static Site Generator",
desc: "불필요한 클라이언트 사이드 JS를 억제(Zero-JS)하여 콘텐츠 중심 블로그에 최적의 등대 스코어와 성능을 유지시킵니다.",
dotColor: "bg-[#ff5d01] shadow-[#ff5d01]/30",
},
{
name: `Tailwind CSS (${siteConfig.dependencies.tailwind})`,
type: "Utility-first Styling",
desc: "고유색 스펙트럼과 타이포그래피 스케일러를 즉각적인 유틸리티 명세 조합으로 통일된 브랜딩을 제공합니다.",
dotColor: "bg-[#38bdf8] shadow-[#38bdf8]/30",
},
],
},
{
id: "spec-pipeline",
category: "Pipeline & Edge",
description:
"코드 변경 사항을 안전하게 기록하고 전 세계에 초고속으로 배달하는 인프라망입니다.",
bgClass: "bg-white",
items: [
{
name: "GitHub",
type: "Version Control Systems",
desc: "모든 커밋 히스토리를 보관하고, 변경 명세(PR)의 코드 정밀 빌드 및 브랜치 동기화에 관여합니다.",
dotColor: "bg-[#24292e] shadow-[#24292e]/30",
},
{
name: "Cloudflare Pages",
type: "Hosting Infrastructure",
desc: "GitHub 저장소와 직결되어 커밋이 푸시될 때마다 서버리스 에지 네트워크를 통해 정적 리소스를 실시간 배포합니다.",
dotColor: "bg-[#f38020] shadow-[#f38020]/30",
},
{
name: "Cloudflare DNS",
type: "Domain Management",
desc: "초고속 쿼리 속도와 강력한 애니캐스트(Anycast) 라우팅 성능으로 딜레이 없는 호스트 해석을 제공합니다.",
dotColor: "bg-[#f38020] shadow-[#f38020]/30",
},
],
},
{
id: "spec-editing",
category: "Editing & Intelligence",
description:
"아이디어를 실시간으로 정밀 조립하고 가공하는 에이전틱 개발 환경입니다.",
bgClass: "bg-[#f6f8f5]",
items: [
{
name: "VS Code & Antigravity IDE Integration",
type: "Agentic Workspace",
desc: "ARM64에 최적화된 VS Code와 원격 WSL 연결 환경 위에서, 에이전트 확장 도구를 통합하여 파일 수정 및 명령어 실행을 에디터 수준에서 스스로 제어하고 안전하게 빌딩하는 지능형 개발 공간입니다.",
dotColor: "bg-[#007acc] shadow-[#007acc]/30",
},
],
},
{
id: "spec-habitat",
category: "Development Habitat",
description:
"로컬에서 아이디어를 다듬고 코드를 컴파일하는 하드웨어와 가상화 환경입니다.",
bgClass: "bg-white",
items: [
{
name: "Windows 11 on ARM64",
type: "Operating System",
desc: "초경량·저전력 환경에서 팬리스(Fanless) 특유의 극도로 정숙하고 쾌적한 핫-리로드 개발 프로세스를 지원합니다.",
dotColor: "bg-[#0078d4] shadow-[#0078d4]/30",
},
{
name: "WSL2 (Ubuntu 22.04 LTS)",
type: "Virtualization Layer",
desc: "Windows OS의 매끄러운 윈도우 제어와 Linux의 우수한 패키지 생태계 및 쉘(Bash) 환경을 동시에 융합하여 사용합니다.",
dotColor: "bg-[#e95420] shadow-[#e95420]/30",
},
],
},
];
------
import SubLayout from "../../layouts/SubLayout.astro";
import { KITCHEN_MENU } from "../../data/navigation";
import { siteConfig } from "../../data/siteConfig";
import { getCollection, render } from "astro:content";
import Badge from "../../components/Badge.astro";
import SectionCentered from "../../components/SectionCentered.astro";
import SectionSplit from "../../components/SectionSplit.astro";
// updates 데이터 가져오기 및 가공
const allUpdates = await getCollection("updates");
const recentUpdates = allUpdates
.sort((a, b) => {
const dateDiff = b.data.date.valueOf() - a.data.date.valueOf();
if (dateDiff !== 0) return dateDiff;
return b.data.version.localeCompare(a.data.version, undefined, {
numeric: true,
});
})
.slice(0, 5);
const updatesWithContent = await Promise.all(
recentUpdates.map(async (update) => {
const { Content } = await render(update);
return { update, Content };
}),
);
const getDotColor = (type: string) => {
switch (type) {
case "major":
return "bg-matcha-500";
case "feature":
return "bg-brand-gemini";
case "design":
return "bg-rose-600";
case "fix":
return "bg-[#8C6239]";
case "content":
return "bg-brand-jiyu";
default:
return "bg-matcha-500";
}
};
const getBadgeVariant = (type: string) => {
switch (type) {
case "major":
return "matcha";
case "feature":
return "yuzu";
case "design":
return "coral";
case "fix":
return "hojicha";
case "content":
return "matcha";
default:
return "matcha";
}
};
const getBadgeColorClasses = (type: string) => {
switch (type) {
case "major":
return "text-matcha-600 bg-matcha-100";
case "feature":
return "text-brand-gemini bg-brand-gemini/10";
case "design":
return "text-rose-600 bg-rose-50";
case "fix":
return "text-[#8C6239] bg-[#8C6239]/10";
case "content":
return "text-brand-jiyu bg-brand-jiyu/10";
default:
return "text-matcha-600 bg-matcha-100";
}
};
------
// src/pages/kitchen/changelog.astro
import SubLayout from '../../layouts/SubLayout.astro';
import SubPageTitle from '../../components/SubPageTitle.astro';
import SectionCentered from '../../components/SectionCentered.astro';
import { KITCHEN_MENU } from '../../data/navigation';
import Button from '../../components/Button.astro';
import { getCollection, render } from 'astro:content';
import { manualBlogMapping, manualTastingMapping } from '../../data/manualRelations';
import { typeLabels, changeTypeThemes, parseVersion } from '../../data/changelogConfig';
const allUpdates = await getCollection('updates');
const allBlogs = await getCollection('blog');
const allTastings = await getCollection('tasting');
// 파일 이름 기준으로 내림차순(최근 v2.4.0이 맨 위) 정렬
const sortedUpdates = allUpdates.sort((a, b) => {
const versionA = parseVersion(a.id);
const versionB = parseVersion(b.id);
if (versionB[0] !== versionA[0]) return versionB[0] - versionA[0];
if (versionB[1] !== versionA[1]) return versionB[1] - versionA[1];
return versionB[2] - versionA[2];
});
const totalReleases = allUpdates.length;
const existingTypes = Object.keys(typeLabels).filter(t => t !== 'all' && sortedUpdates.some(u => u.data.type === t));
const countByType = sortedUpdates.reduce((acc, u) => {
acc[u.data.type] = (acc[u.data.type] || 0) + 1;
return acc;
}, {} as Record<string, number>);
// 연동된 블로그 및 테이스팅 노트 매핑 데이터 생성 (1:1 정규화 튜플)
const matrix = await Promise.all(
sortedUpdates.map(async (update) => {
const versionStr = update.data.version;
const { Content } = await render(update);
// 블로그 포스트 연동 탐색
let matchedBlog = allBlogs.find(blog =>
(update.data.relatedBlog && blog.id === update.data.relatedBlog) ||
(blog.data.relatedUpdate && blog.data.relatedUpdate === versionStr) ||
(manualBlogMapping[versionStr] && blog.id === manualBlogMapping[versionStr])
);
// 테이스팅 노트 연동 탐색 (버전 명세 비교 시 'v' 접두사 정규화)
const cleanVersion = versionStr.replace(/^v/, '');
let matchedTasting = allTastings.find(tasting => {
const cleanTastingVersion = tasting.data.version.replace(/^v/, '');
return cleanTastingVersion === cleanVersion ||
(manualTastingMapping[versionStr] && tasting.id === manualTastingMapping[versionStr]);
});
return {
update,
Content,
blog: matchedBlog,
tasting: matchedTasting
};
})
);
const getTypeBadge = (type: string) => {
return changeTypeThemes[type]?.badge || 'bg-gray-100 text-gray-600';
};
------
// src/pages/blog/[...slug].astro
import { getCollection, render } from "astro:content";
import BaseLayout from "../../layouts/BaseLayout.astro";
import RelatedPosts from "../../components/RelatedPosts.astro";
export async function getStaticPaths() {
const blogEntries = await getCollection("blog");
return blogEntries.map((entry) => ({
params: { slug: entry.id },
props: { entry },
}));
}
const { entry } = Astro.props;
const { Content } = await render(entry);
// Find related update if it exists
let relatedUpdateEntry = null;
if (entry.data.relatedUpdate) {
const updates = await getCollection("updates");
relatedUpdateEntry = updates.find(
(u) => u.data.version === entry.data.relatedUpdate,
);
}
let titleParts = [entry.data.title];
if (entry.data.title.includes("\n")) {
titleParts = entry.data.title.split("\n");
} else if (entry.data.title.includes(":")) {
const index = entry.data.title.indexOf(":");
titleParts = [
entry.data.title.substring(0, index + 1).trim(),
entry.data.title.substring(index + 1).trim(),
];
}
const yyyy = entry.data.pubDate.getFullYear();
const mm = String(entry.data.pubDate.getMonth() + 1).padStart(2, "0");
const dd = String(entry.data.pubDate.getDate()).padStart(2, "0");
const formattedDate = `${yyyy}.${mm}.${dd}`;
---main*Logic Only
UTF-8Astro Core