feat(seo): PWA manifest + metadata + sitemap — 8.1
BIN
frontend/public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
frontend/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
frontend/public/icons/icon-192.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
frontend/public/icons/icon-512.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
frontend/public/icons/icon-maskable-512.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
15
frontend/public/icons/icon.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="reloop-gradient" x1="56" y1="48" x2="456" y2="464" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4F46E5"/>
|
||||
<stop offset="1" stop-color="#7C3AED"/>
|
||||
</linearGradient>
|
||||
<filter id="reloop-shadow" x="0" y="0" width="512" height="512" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="24" flood-color="#06070B" flood-opacity="0.38"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="24" y="24" width="464" height="464" rx="112" fill="url(#reloop-gradient)"/>
|
||||
<g filter="url(#reloop-shadow)">
|
||||
<path d="M115 271C115 214.115 158.115 171 215 171C261.136 171 285.904 204.136 318.096 247.863L322.044 253.222C350.212 291.434 368.795 315 397 315C420.196 315 439 296.196 439 273C439 249.804 420.196 231 397 231C374.286 231 357.833 248.543 334.437 279.983L330.566 285.183C297.612 329.469 270.846 365 215 365C158.115 365 115 321.885 115 265C115 208.115 158.115 165 215 165C270.846 165 297.612 200.531 330.566 244.817L334.437 250.017C357.833 281.457 374.286 299 397 299C420.196 299 439 280.196 439 257C439 233.804 420.196 215 397 215C368.795 215 350.212 238.566 322.044 276.778L318.096 282.137C285.904 325.864 261.136 359 215 359C158.115 359 115 315.885 115 259V271Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
27
frontend/public/manifest.webmanifest
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "ReLoop — 적응형 수능 복습",
|
||||
"short_name": "ReLoop",
|
||||
"description": "잊기 직전에 딱 한 번 더. 페르소나 기반 망각곡선 복습 시스템.",
|
||||
"start_url": "/dashboard",
|
||||
"display": "standalone",
|
||||
"background_color": "#0F0F14",
|
||||
"theme_color": "#4F46E5",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
frontend/public/og-image.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
9
frontend/public/robots.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /api/
|
||||
Disallow: /study
|
||||
Disallow: /dashboard
|
||||
Disallow: /review
|
||||
Disallow: /profile
|
||||
|
||||
Sitemap: https://reloop.nabomhalang.co.kr/sitemap.xml
|
||||
12
frontend/src/app/dashboard/layout.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { Metadata } from 'next';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '대시보드',
|
||||
};
|
||||
|
||||
export default function DashboardLayout({
|
||||
children,
|
||||
}: Readonly<{ children: ReactNode }>) {
|
||||
return children;
|
||||
}
|
||||
12
frontend/src/app/exams/layout.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { Metadata } from 'next';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '모의고사 문제집',
|
||||
};
|
||||
|
||||
export default function ExamsLayout({
|
||||
children,
|
||||
}: Readonly<{ children: ReactNode }>) {
|
||||
return children;
|
||||
}
|
||||
@@ -20,15 +20,58 @@ const jetbrainsMono = JetBrains_Mono({
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'ReLoop — 적응형 복습 스케줄링',
|
||||
description: '페르소나 망각 곡선 기반 수능 학습 최적화 플랫폼',
|
||||
metadataBase: new URL('https://reloop.nabomhalang.co.kr'),
|
||||
title: {
|
||||
default: 'ReLoop — 적응형 수능 복습',
|
||||
template: '%s | ReLoop',
|
||||
},
|
||||
description: '잊기 직전에 딱 한 번 더. 페르소나 기반 망각곡선 복습 시스템.',
|
||||
applicationName: 'ReLoop',
|
||||
keywords: ['수능', '복습', '망각곡선', '페르소나', '수능 문제집', 'KICE'],
|
||||
authors: [{ name: 'ReLoop' }],
|
||||
creator: 'ReLoop',
|
||||
manifest: '/manifest.webmanifest',
|
||||
icons: {
|
||||
icon: [
|
||||
{ url: '/favicon.ico', sizes: 'any' },
|
||||
{ url: '/icons/icon.svg', type: 'image/svg+xml' },
|
||||
{ url: '/icons/icon-192.png', sizes: '192x192', type: 'image/png' },
|
||||
{ url: '/icons/icon-512.png', sizes: '512x512', type: 'image/png' },
|
||||
],
|
||||
apple: '/apple-touch-icon.png',
|
||||
},
|
||||
openGraph: {
|
||||
title: 'ReLoop — 적응형 수능 복습',
|
||||
description: '잊기 직전에 딱 한 번 더. 네 페이스에 맞는 복습.',
|
||||
siteName: 'ReLoop',
|
||||
locale: 'ko_KR',
|
||||
type: 'website',
|
||||
images: [
|
||||
{
|
||||
url: '/og-image.png',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: 'ReLoop',
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'ReLoop — 적응형 수능 복습',
|
||||
description: '잊기 직전에 딱 한 번 더.',
|
||||
images: ['/og-image.png'],
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const viewport: Viewport = {
|
||||
width: 'device-width',
|
||||
initialScale: 1,
|
||||
maximumScale: 5,
|
||||
themeColor: '#0b1020',
|
||||
themeColor: '#0F0F14',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
||||
12
frontend/src/app/pricing/layout.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { Metadata } from 'next';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: '요금제',
|
||||
};
|
||||
|
||||
export default function PricingLayout({
|
||||
children,
|
||||
}: Readonly<{ children: ReactNode }>) {
|
||||
return children;
|
||||
}
|
||||
13
frontend/src/app/sitemap.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { MetadataRoute } from 'next';
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const base = 'https://reloop.nabomhalang.co.kr';
|
||||
const now = new Date();
|
||||
|
||||
return [
|
||||
{ url: `${base}/`, lastModified: now, changeFrequency: 'weekly', priority: 1 },
|
||||
{ url: `${base}/pricing`, lastModified: now, changeFrequency: 'monthly', priority: 0.8 },
|
||||
{ url: `${base}/login`, lastModified: now, changeFrequency: 'yearly', priority: 0.3 },
|
||||
{ url: `${base}/register`, lastModified: now, changeFrequency: 'yearly', priority: 0.5 },
|
||||
];
|
||||
}
|
||||