웹 개발

Next.js SEO 최적화 실전 테크닉

Daniel Kim|프론트엔드 리드 개발자
2025-06-02
12분 소요
#Next.js#SEO#메타데이터#구조화 데이터#성능 최적화
Next.js SEO 최적화 실전 테크닉

Next.js는 React 기반 프레임워크 중 가장 SEO 친화적인 선택입니다. 하지만 프레임워크가 SEO를 '가능하게' 해줄 뿐, 실제 최적화는 개발자의 몫입니다.

이 가이드에서는 Next.js 15 App Router 기준으로 실전에서 바로 적용 가능한 SEO 최적화 테크닉을 다룹니다.

Next.js SEO의 3가지 핵심 축

SEO Optimization Pyramid Authority Backlinks & Trust Content Strategy Keywords, Quality, UX Technical SEO Backlinks, Reviews, Brand Mentions Keyword Research, Blog Posts, Landing Pages Core Web Vitals, Schema, Sitemap
SEO is built layer by layer from technical foundation to authority

1. 기술적 SEO (Technical SEO)

  • 서버 사이드 렌더링 (SSR/SSG)
  • Core Web Vitals 최적화
  • 구조화 데이터 (JSON-LD)
  • Sitemap/Robots.txt 자동화

2. On-Page SEO

  • 메타데이터 최적화
  • 시맨틱 HTML 구조
  • 내부 링크 전략
  • 이미지 최적화

3. 콘텐츠 SEO

  • 키워드 전략
  • 사용자 의도 반영
  • E-E-A-T (Experience, Expertise, Authoritativeness, Trustworthiness)
💡 2025년 SEO 핵심 변화

Google은 이제 사용자 경험(UX)콘텐츠 품질을 동등하게 평가합니다. 빠르지만 내용 없는 사이트나, 좋은 콘텐츠지만 느린 사이트 모두 상위 노출이 어렵습니다.


1. 메타데이터 최적화: App Router 방식

Next.js 15의 App Router는 파일 기반 메타데이터 시스템을 제공합니다.

Next.js SEO Metadata Flow generateMetadata Server Function Runs at request/build HTML <head> title, meta, OG tags Injected into response Search Crawler Google, Bing, etc. Reads & indexes Search Results Rich snippets, OG cards Shown to users
How metadata flows from code to search results

정적 메타데이터 (기본)

typescript
// app/about/page.tsx
import { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'About POEMA | Digital Agency',
  description: 'POEMA는 웹 개발, 이커머스, 퍼포먼스 마케팅을 제공하는 디지털 에이전시입니다.',
  keywords: ['웹 개발', '이커머스', '디지털 마케팅', 'POEMA'],
  authors: [{ name: 'POEMA Team' }],
  openGraph: {
    title: 'About POEMA',
    description: 'Digital transformation partner for startups',
    images: ['/og-image.jpg'],
    type: 'website',
  },
  twitter: {
    card: 'summary_large_image',
    title: 'About POEMA',
    description: 'Digital transformation partner',
    images: ['/twitter-image.jpg'],
  },
};

export default function AboutPage() {
  return <div>...</div>;
}

동적 메타데이터 (추천)

케이스 스터디, 블로그 포스트 등 동적 페이지에 필수입니다.

typescript
// app/portfolio/[slug]/page.tsx
import { Metadata } from 'next';
import { getCaseStudy } from '@/data/marketing/case-studies';

export async function generateMetadata({
  params
}: {
  params: Promise<{ slug: string }>
}): Promise<Metadata> {
  const { slug } = await params;
  const caseStudy = await getCaseStudy(slug);

  if (!caseStudy) {
    return {
      title: 'Portfolio Not Found',
    };
  }

  return {
    title: `${caseStudy.title.ko} | POEMA Portfolio`,
    description: caseStudy.overview.ko,
    keywords: caseStudy.tags.ko,
    openGraph: {
      title: caseStudy.title.ko,
      description: caseStudy.overview.ko,
      images: [
        {
          url: caseStudy.thumbnail,
          width: 1200,
          height: 630,
          alt: caseStudy.title.ko,
        },
      ],
      type: 'article',
      publishedTime: caseStudy.publishedAt,
    },
  };
}
✅ 메타데이터 체크리스트
  • [ ] 모든 페이지에 고유한 title (50-60자 이내)
  • [ ] description 120-160자, 액션 지향적 문구 포함
  • [ ] OG 이미지 1200×630px, 파일 크기 300KB 이하
  • [ ] Twitter Card 설정
  • [ ] 다국어 사이트의 경우 alternates 설정

  • 2. 구조화 데이터: JSON-LD로 리치 스니펫 획득

    구조화 데이터는 검색 엔진이 콘텐츠를 이해하도록 돕고, 리치 스니펫을 통해 CTR을 높입니다.

    Organization Schema (전역)

    typescript
    // app/layout.tsx
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      const organizationSchema = {
        '@context': 'https://schema.org',
        '@type': 'Organization',
        name: 'POEMA',
        url: 'https://poema.agency',
        logo: 'https://poema.agency/logo.png',
        description: 'Digital agency specializing in web development and e-commerce',
        address: {
          '@type': 'PostalAddress',
          addressCountry: 'KR',
          addressLocality: 'Seoul',
        },
        contactPoint: {
          '@type': 'ContactPoint',
          telephone: '+82-2-1234-5678',
          contactType: 'Customer Service',
          email: 'hello@poema.agency',
        },
        sameAs: [
          'https://www.instagram.com/poema.agency',
          'https://www.linkedin.com/company/poema',
        ],
      };
    
      return (
        <html>
          <head>
            <script
              type="application/ld+json"
              dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema) }}
            />
          </head>
          <body>{children}</body>
        </html>
      );
    }

    Article Schema (블로그 포스트)

    typescript
    // components/seo/BlogPostJsonLd.tsx
    interface BlogPostJsonLdProps {
      title: string;
      description: string;
      publishedAt: string;
      author: string;
      image: string;
      url: string;
    }
    
    export function BlogPostJsonLd({ title, description, publishedAt, author, image, url }: BlogPostJsonLdProps) {
      const schema = {
        '@context': 'https://schema.org',
        '@type': 'BlogPosting',
        headline: title,
        description: description,
        image: image,
        datePublished: publishedAt,
        author: {
          '@type': 'Person',
          name: author,
        },
        publisher: {
          '@type': 'Organization',
          name: 'POEMA',
          logo: {
            '@type': 'ImageObject',
            url: 'https://poema.agency/logo.png',
          },
        },
        mainEntityOfPage: {
          '@type': 'WebPage',
          '@id': url,
        },
      };
    
      return (
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
        />
      );
    }

    FAQPage Schema

    typescript
    export function FAQJsonLd({ faqs }: { faqs: { question: string; answer: string }[] }) {
      const schema = {
        '@context': 'https://schema.org',
        '@type': 'FAQPage',
        mainEntity: faqs.map((faq) => ({
          '@type': 'Question',
          name: faq.question,
          acceptedAnswer: {
            '@type': 'Answer',
            text: faq.answer,
          },
        })),
      };
    
      return (
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
        />
      );
    }
    ⚠️ 구조화 데이터 주의사항
  • Google Rich Results Test로 반드시 검증
  • 실제 페이지에 표시된 내용과 일치해야 함
  • 과도한 키워드 스터핑 금지
  • 업데이트 시 스키마도 함께 수정

  • 3. Sitemap & Robots.txt 자동화

    Next.js 15는 파일 기반 sitemap/robots 생성을 지원합니다.

    동적 Sitemap 생성

    typescript
    // app/sitemap.ts
    import { MetadataRoute } from 'next';
    import { caseStudies } from '@/data/marketing/case-studies';
    import { blogPosts } from '@/data/marketing/blog';
    
    export default function sitemap(): MetadataRoute.Sitemap {
      const baseUrl = 'https://poema.agency';
    
      // Static pages
      const staticPages: MetadataRoute.Sitemap = [
        {
          url: baseUrl,
          lastModified: new Date(),
          changeFrequency: 'weekly',
          priority: 1.0,
        },
        {
          url: `${baseUrl}/services`,
          lastModified: new Date(),
          changeFrequency: 'monthly',
          priority: 0.8,
        },
        {
          url: `${baseUrl}/portfolio`,
          lastModified: new Date(),
          changeFrequency: 'weekly',
          priority: 0.9,
        },
      ];
    
      // Dynamic case studies
      const caseStudyPages: MetadataRoute.Sitemap = caseStudies.map((study) => ({
        url: `${baseUrl}/portfolio/${study.slug}`,
        lastModified: new Date(study.publishedAt),
        changeFrequency: 'monthly' as const,
        priority: 0.7,
      }));
    
      // Dynamic blog posts
      const blogPages: MetadataRoute.Sitemap = blogPosts.map((post) => ({
        url: `${baseUrl}/blog/${post.slug}`,
        lastModified: new Date(post.publishedAt),
        changeFrequency: 'monthly' as const,
        priority: 0.6,
      }));
    
      return [...staticPages, ...caseStudyPages, ...blogPages];
    }

    Robots.txt 설정

    typescript
    // app/robots.ts
    import { MetadataRoute } from 'next';
    
    export default function robots(): MetadataRoute.Robots {
      const baseUrl = 'https://poema.agency';
    
      return {
        rules: [
          {
            userAgent: '*',
            allow: '/',
            disallow: ['/api/', '/admin/', '/_next/'],
          },
          {
            userAgent: 'Googlebot',
            allow: '/',
            disallow: ['/api/', '/admin/'],
          },
        ],
        sitemap: `${baseUrl}/sitemap.xml`,
      };
    }

    4. Core Web Vitals 최적화

    Core Web Vitals Targets LCP Largest Contentful Paint ≤ 2.5s Loading Speed INP Interaction to Next Paint ≤ 200ms Responsiveness CLS Cumulative Layout Shift ≤ 0.1 Visual Stability
    Google uses these metrics as ranking signals

    Google은 LCP, FID, CLS를 페이지 경험 지표로 평가합니다.

    LCP (Largest Contentful Paint) 최적화

    목표: 2.5초 이하

    typescript
    // 이미지 최적화
    import Image from 'next/image';
    
    <Image
      src="/hero.jpg"
      alt="Hero image"
      width={1920}
      height={1080}
      priority // LCP 이미지에 필수
      quality={90}
      placeholder="blur"
      blurDataURL="data:image/..."
    />
    
    // 폰트 최적화
    import { Inter } from 'next/font/google';
    
    const inter = Inter({
      subsets: ['latin'],
      display: 'swap', // FOUT 방지
      preload: true,
    });

    CLS (Cumulative Layout Shift) 최적화

    목표: 0.1 이하

    tsx
    // 이미지/동영상에 명시적 크기 지정
    <Image src="..." width={600} height={400} alt="..." />
    
    // 동적 콘텐츠에 최소 높이 설정
    <div className="min-h-[400px]">
      {isLoading ? <Skeleton /> : <Content />}
    </div>
    
    // 폰트 preload로 FOUT 방지
    <link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossOrigin="anonymous" />

    FID/INP 최적화

    목표: FID 100ms 이하, INP 200ms 이하

    • Dynamic import로 JS 번들 분할
    • 무거운 컴포넌트는 below-the-fold에서만 로드
    • debounce/throttle로 이벤트 핸들러 최적화
    ✅ Core Web Vitals 체크리스트
  • [ ] PageSpeed Insights로 실제 사용자 데이터 확인
  • [ ] Lighthouse CI로 빌드마다 자동 테스트
  • [ ] Vercel Analytics/Google Search Console로 지속 모니터링
  • [ ] 모바일 우선 최적화 (트래픽의 70%+)

  • 5. 다국어 SEO (next-intl 활용)

    typescript
    // app/[locale]/layout.tsx
    import { Metadata } from 'next';
    
    export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
      const { locale } = await params;
    
      return {
        alternates: {
          canonical: `https://poema.agency/${locale}`,
          languages: {
            'ko-KR': 'https://poema.agency/ko',
            'en-US': 'https://poema.agency/en',
          },
        },
      };
    }

    Hreflang 태그는 Next.js가 자동 생성하지만, Sitemap에는 수동 추가 필요:

    typescript
    export default function sitemap(): MetadataRoute.Sitemap {
      return [
        {
          url: 'https://poema.agency/ko',
          lastModified: new Date(),
          alternates: {
            languages: {
              en: 'https://poema.agency/en',
            },
          },
        },
      ];
    }

    6. SEO 체크리스트: 론칭 전 필수 확인

    기술적 체크

    항목도구목표
    Core Web VitalsPageSpeed InsightsLCP <2.5s, FID <100ms, CLS <0.1
    모바일 친화성Mobile-Friendly TestPass
    구조화 데이터Rich Results Test0 Errors
    보안SSL LabsA+
    SitemapGoogle Search ConsoleIndexed

    On-Page 체크

    ✅ SEO 론칭 체크리스트
  • [ ] 모든 페이지에 고유한 </code>, <code><meta description></code></li><li style="margin-left:16px;list-style:disc;color:#166534;">[ ] H1 태그 페이지당 1개, 의미 있는 계층 구조</li><li style="margin-left:16px;list-style:disc;color:#166534;">[ ] 이미지 alt 텍스트 100% 작성</li><li style="margin-left:16px;list-style:disc;color:#166534;">[ ] 내부 링크 전략 (주요 페이지 간 연결)</li><li style="margin-left:16px;list-style:disc;color:#166534;">[ ] 404 페이지 커스터마이징</li><li style="margin-left:16px;list-style:disc;color:#166534;">[ ] OG 이미지 모든 페이지 설정</li><li style="margin-left:16px;list-style:disc;color:#166534;">[ ] Canonical URL 설정</li><li style="margin-left:16px;list-style:disc;color:#166534;">[ ] 구조화 데이터 (최소 Organization + Breadcrumb)</li></div> </div> <hr style="border:none;border-top:1px solid #e2e8f0;margin:32px 0;"/> <h2>실전 팁: 빠른 SEO 성과 내기</h2> <h3>1. 롱테일 키워드 공략</h3> <blockquote style="border-left:3px solid #cbd5e1;padding:12px 20px;margin:24px 0;background:#f8fafc;border-radius:0 8px 8px 0;font-style:italic;color:#475569;"><p style="margin:4px 0;">"이커머스 개발"보다 "Shopify 한글화 개발 에이전시"가 경쟁률 낮고 전환율 높습니다.</p></blockquote> <ul><li>Google Keyword Planner, Ahrefs로 키워드 리서치</li><li>블로그 콘텐츠로 롱테일 키워드 대량 타겟팅</li><li>지역 키워드 활용 ("서울 웹 개발", "강남 쇼핑몰 제작")</li></ul> <h3>2. E-E-A-T 강화</h3> <ul><li>팀 소개 페이지에 실명, 사진, 경력 공개</li><li>케이스 스터디에 클라이언트 로고/추천사</li><li>블로그에 저자 프로필 명시</li><li>업계 미디어 기고, 외부 사이트 백링크</li></ul> <h3>3. 기술 블로그로 자연 유입 확보</h3> <p>"Next.js SEO 가이드", "포트폴리오 사이트 제작 방법" 같은 How-to 콘텐츠는 장기적으로 꾸준한 트래픽을 가져옵니다.</p> <ul><li>주 1-2회 발행</li><li>2,000자 이상 심도 있는 콘텐츠</li><li>코드 예시, 스크린샷 포함</li><li>내부 링크로 서비스 페이지 연결</li></ul> <hr style="border:none;border-top:1px solid #e2e8f0;margin:32px 0;"/> <h2>마무리: SEO는 마라톤</h2> <p>SEO 성과는 보통 3-6개월 후 나타납니다. 단기 트래픽이 필요하다면 광고를, 장기 자산을 쌓고 싶다면 SEO를 선택하세요.</p> <p>Next.js는 SEO를 위한 모든 도구를 제공합니다. 이제 실행만 남았습니다.</p></div></article><section class="mt-20 max-w-5xl mx-auto"><h2 class="text-2xl font-bold text-slate-900 mb-8">관련 글</h2><div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8"><a class="block group" href="/blog/responsive-web-design-trends-2025"><article class="bg-white rounded-2xl overflow-hidden border border-slate-100 shadow-sm transition-all duration-300 group-hover:shadow-xl group-hover:border-slate-200" style="opacity:0;transform:translateY(20px)"><div class="relative aspect-[16/9] bg-gradient-to-br from-slate-100 to-slate-200 overflow-hidden"><img src="/blog/responsive-design/thumb.jpg" alt="2025년 반응형 웹디자인 트렌드 총정리" class="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"/><div class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-300"></div><div class="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all duration-300" style="opacity:0;transform:scale(0.8)"><div class="bg-white/95 backdrop-blur-sm rounded-full p-3 shadow-lg"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right w-5 h-5 text-slate-900" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg></div></div><div class="absolute top-3 left-3"><span class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold backdrop-blur-sm bg-blue-50 text-blue-600"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-code w-3.5 h-3.5" aria-hidden="true"><path d="m16 18 6-6-6-6"></path><path d="m8 6-6 6 6 6"></path></svg>웹 개발</span></div></div><div class="p-5"><div class="flex flex-wrap items-center gap-1.5 mb-3"><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->반응형 디자인</span><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->CSS</span><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->모바일 퍼스트</span></div><h3 class="text-lg font-bold text-slate-900 mb-2 group-hover:text-poema-primary transition-colors line-clamp-2">2025년 반응형 웹디자인 트렌드 총정리</h3><p class="text-sm text-slate-600 line-clamp-2 mb-4 leading-relaxed">모바일 트래픽이 전체의 70%를 넘어선 지금, 반응형 웹디자인은 선택이 아닌 필수입니다. 2025년 주목해야 할 핵심 트렌드를 살펴봅니다.</p><div class="pt-3 border-t border-slate-100 flex items-center justify-between text-xs text-slate-400"><div class="flex items-center gap-1.5"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar w-3.5 h-3.5" aria-hidden="true"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg><span>2025-05-10</span></div><div class="flex items-center gap-1.5"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock w-3.5 h-3.5" aria-hidden="true"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg><span>8<!-- -->분 소요</span></div></div></div></article></a><a class="block group" href="/blog/nextjs-performance-optimization"><article class="bg-white rounded-2xl overflow-hidden border border-slate-100 shadow-sm transition-all duration-300 group-hover:shadow-xl group-hover:border-slate-200" style="opacity:0;transform:translateY(20px)"><div class="relative aspect-[16/9] bg-gradient-to-br from-slate-100 to-slate-200 overflow-hidden"><img src="/blog/nextjs-performance/thumb.jpg" alt="Next.js로 초고속 웹사이트 만들기: 성능 최적화 완벽 가이드" class="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"/><div class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-300"></div><div class="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all duration-300" style="opacity:0;transform:scale(0.8)"><div class="bg-white/95 backdrop-blur-sm rounded-full p-3 shadow-lg"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right w-5 h-5 text-slate-900" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg></div></div><div class="absolute top-3 left-3"><span class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold backdrop-blur-sm bg-blue-50 text-blue-600"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-code w-3.5 h-3.5" aria-hidden="true"><path d="m16 18 6-6-6-6"></path><path d="m8 6-6 6 6 6"></path></svg>웹 개발</span></div></div><div class="p-5"><div class="flex flex-wrap items-center gap-1.5 mb-3"><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->Next.js</span><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->React</span><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->성능 최적화</span></div><h3 class="text-lg font-bold text-slate-900 mb-2 group-hover:text-poema-primary transition-colors line-clamp-2">Next.js로 초고속 웹사이트 만들기: 성능 최적화 완벽 가이드</h3><p class="text-sm text-slate-600 line-clamp-2 mb-4 leading-relaxed">Next.js의 강력한 성능 최적화 기능을 활용해 로딩 속도를 3배 빠르게 만드는 실전 기법을 배웁니다.</p><div class="pt-3 border-t border-slate-100 flex items-center justify-between text-xs text-slate-400"><div class="flex items-center gap-1.5"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar w-3.5 h-3.5" aria-hidden="true"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg><span>2025-01-20</span></div><div class="flex items-center gap-1.5"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock w-3.5 h-3.5" aria-hidden="true"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg><span>12<!-- -->분 소요</span></div></div></div></article></a><a class="block group" href="/blog/portfolio-website-complete-guide-2025"><article class="bg-white rounded-2xl overflow-hidden border border-slate-100 shadow-sm transition-all duration-300 group-hover:shadow-xl group-hover:border-slate-200" style="opacity:0;transform:translateY(20px)"><div class="relative aspect-[16/9] bg-gradient-to-br from-slate-100 to-slate-200 overflow-hidden"><img src="/blog/portfolio-guide/thumb.jpg" alt="포트폴리오 사이트 제작 완벽 가이드 2025" class="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"/><div class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-300"></div><div class="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all duration-300" style="opacity:0;transform:scale(0.8)"><div class="bg-white/95 backdrop-blur-sm rounded-full p-3 shadow-lg"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right w-5 h-5 text-slate-900" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg></div></div><div class="absolute top-3 left-3"><span class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold backdrop-blur-sm bg-blue-50 text-blue-600"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-code w-3.5 h-3.5" aria-hidden="true"><path d="m16 18 6-6-6-6"></path><path d="m8 6-6 6 6 6"></path></svg>웹 개발</span></div></div><div class="p-5"><div class="flex flex-wrap items-center gap-1.5 mb-3"><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->포트폴리오</span><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->웹사이트 제작</span><span class="text-xs text-slate-500 bg-slate-50 px-2 py-0.5 rounded">#<!-- -->개인 브랜딩</span></div><h3 class="text-lg font-bold text-slate-900 mb-2 group-hover:text-poema-primary transition-colors line-clamp-2">포트폴리오 사이트 제작 완벽 가이드 2025</h3><p class="text-sm text-slate-600 line-clamp-2 mb-4 leading-relaxed">포트폴리오 사이트는 단순한 작업물 모음이 아닙니다. 클라이언트를 설득하고 기회를 만드는 비즈니스 도구입니다.</p><div class="pt-3 border-t border-slate-100 flex items-center justify-between text-xs text-slate-400"><div class="flex items-center gap-1.5"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar w-3.5 h-3.5" aria-hidden="true"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg><span>2025-06-15</span></div><div class="flex items-center gap-1.5"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clock w-3.5 h-3.5" aria-hidden="true"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg><span>10<!-- -->분 소요</span></div></div></div></article></a></div></section></div></div><!--$--><!--/$--></main><footer class="w-full bg-slate-900 text-slate-300"><div class="container mx-auto px-4 md:px-6 py-16"><div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-10 lg:gap-12"><div class="lg:col-span-1"><a class="text-2xl font-bold text-white tracking-tight" href="/">POEMORA</a><p class="mt-4 text-sm text-slate-400 leading-relaxed">비즈니스를 완성하는 개발 파트너.<br/>웹사이트, 쇼핑몰, 마케팅까지<br/>원스톱 솔루션을 제공합니다.</p><div class="mt-6 flex gap-3"><a href="https://instagram.com/poemora_kr" target="_blank" rel="noopener noreferrer" class="w-10 h-10 rounded-lg bg-slate-800 hover:bg-slate-700 flex items-center justify-center transition-colors" aria-label="Instagram"><span class="text-lg">📷</span></a></div></div><div><h3 class="text-white font-semibold mb-4">서비스</h3><ul class="space-y-3"><li><a class="text-sm text-slate-400 hover:text-white transition-colors inline-flex items-center gap-1 group" href="/services/web">웹사이트 제작<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg></a></li><li><a class="text-sm text-slate-400 hover:text-white transition-colors inline-flex items-center gap-1 group" href="/services/ecommerce">이커머스 구축<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg></a></li><li><a class="text-sm text-slate-400 hover:text-white transition-colors inline-flex items-center gap-1 group" href="/services/marketing">퍼포먼스 마케팅<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg></a></li></ul></div><div><h3 class="text-white font-semibold mb-4">회사</h3><ul class="space-y-3"><li><a class="text-sm text-slate-400 hover:text-white transition-colors inline-flex items-center gap-1 group" href="/portfolio">포트폴리오<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg></a></li><li><a class="text-sm text-slate-400 hover:text-white transition-colors inline-flex items-center gap-1 group" href="/contact">문의하기<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg></a></li></ul></div><div><h3 class="text-white font-semibold mb-4">연락처</h3><ul class="space-y-4"><li><a href="mailto:contact@poemora.com" class="text-sm text-slate-400 hover:text-white transition-colors flex items-center gap-3"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mail w-4 h-4 text-poema-accent" aria-hidden="true"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"></path><rect x="2" y="4" width="20" height="16" rx="2"></rect></svg>contact@poemora.com</a></li><li><a href="tel:+821000000000" class="text-sm text-slate-400 hover:text-white transition-colors flex items-center gap-3"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-phone w-4 h-4 text-poema-accent" aria-hidden="true"><path d="M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384"></path></svg>010-0000-0000</a></li><li><a href="https://open.kakao.com" target="_blank" rel="noopener noreferrer" class="text-sm text-slate-400 hover:text-white transition-colors flex items-center gap-3"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-message-circle w-4 h-4 text-yellow-400" aria-hidden="true"><path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"></path></svg>카카오톡 상담</a></li><li class="flex items-start gap-3 text-sm text-slate-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-map-pin w-4 h-4 text-poema-accent mt-0.5 shrink-0" aria-hidden="true"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"></path><circle cx="12" cy="10" r="3"></circle></svg><span>서울특별시</span></li></ul></div></div></div><div class="border-t border-slate-800"><div class="container mx-auto px-4 md:px-6 py-6"><div class="flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-slate-500"><p>© <!-- -->2026<!-- --> POEMORA. All rights reserved.</p><div class="flex gap-6"><a class="hover:text-slate-300 transition-colors" href="/privacy">개인정보처리방침</a><a class="hover:text-slate-300 transition-colors" href="/terms">이용약관</a></div></div></div></div></footer><script src="/_next/static/chunks/4e1b247cec93b429.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[5100,[\"/_next/static/chunks/919776377dc700f8.js\"],\"QueryProvider\"]\n3:I[543121,[\"/_next/static/chunks/919776377dc700f8.js\"],\"AuthSessionProvider\"]\n4:I[339756,[\"/_next/static/chunks/dd4dd01088920e08.js\",\"/_next/static/chunks/22a2520bad6af95c.js\"],\"default\"]\n5:I[758298,[\"/_next/static/chunks/919776377dc700f8.js\",\"/_next/static/chunks/5658cd19d60d7c01.js\"],\"default\"]\n6:I[837457,[\"/_next/static/chunks/dd4dd01088920e08.js\",\"/_next/static/chunks/22a2520bad6af95c.js\"],\"default\"]\n7:I[127661,[\"/_next/static/chunks/919776377dc700f8.js\",\"/_next/static/chunks/1e09f01663a3796d.js\",\"/_next/static/chunks/b6594419d6992ba9.js\",\"/_next/static/chunks/8fd0d995fc1f0a65.js\"],\"Header\"]\n8:I[365903,[\"/_next/static/chunks/919776377dc700f8.js\",\"/_next/static/chunks/1e09f01663a3796d.js\",\"/_next/static/chunks/b6594419d6992ba9.js\",\"/_next/static/chunks/8fd0d995fc1f0a65.js\"],\"Footer\"]\na:I[897367,[\"/_next/static/chunks/dd4dd01088920e08.js\",\"/_next/static/chunks/22a2520bad6af95c.js\"],\"OutletBoundary\"]\nb:\"$Sreact.suspense\"\nd:I[897367,[\"/_next/static/chunks/dd4dd01088920e08.js\",\"/_next/static/chunks/22a2520bad6af95c.js\"],\"ViewportBoundary\"]\nf:I[897367,[\"/_next/static/chunks/dd4dd01088920e08.js\",\"/_next/static/chunks/22a2520bad6af95c.js\"],\"MetadataBoundary\"]\n11:I[563491,[\"/_next/static/chunks/36250d4484915b96.js\"],\"default\"]\n12:I[663458,[\"/_next/static/chunks/919776377dc700f8.js\",\"/_next/static/chunks/1e09f01663a3796d.js\",\"/_next/static/chunks/b6594419d6992ba9.js\",\"/_next/static/chunks/8fd0d995fc1f0a65.js\",\"/_next/static/chunks/2bcd218e4fb96e71.js\",\"/_next/static/chunks/5cbe5d4d0c529cab.js\"],\"BlogPostContent\"]\n15:I[312397,[\"/_next/static/chunks/919776377dc700f8.js\",\"/_next/static/chunks/1e09f01663a3796d.js\",\"/_next/static/chunks/b6594419d6992ba9.js\",\"/_next/static/chunks/8fd0d995fc1f0a65.js\",\"/_next/static/chunks/2bcd218e4fb96e71.js\",\"/_next/static/chunks/5cbe5d4d0c529cab.js\"],\"BlogCard\"]\n1b:I[27201,[\"/_next/static/chunks/dd4dd01088920e08.js\",\"/_next/static/chunks/22a2520bad6af95c.js\"],\"IconMark\"]\n:HL[\"/_next/static/chunks/4fdc62bb1bc6426d.css\",\"style\"]\n:HL[\"/_next/static/chunks/dce5ea146e7acd91.css\",\"style\"]\n:HL[\"/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"dGEH8mHTiVIPfoKXlHHlI\",\"c\":[\"\",\"blog\",\"nextjs-seo-optimization-techniques\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"(marketing)\",{\"children\":[\"blog\",{\"children\":[[\"slug\",\"nextjs-seo-optimization-techniques\",\"d\"],{\"children\":[\"__PAGE__\",{}]}]}]}]},\"$undefined\",\"$undefined\",true],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/4fdc62bb1bc6426d.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/dce5ea146e7acd91.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/919776377dc700f8.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"ko\",\"children\":[\"$\",\"body\",null,{\"className\":\"geist_a71539c9-module__T19VSG__variable geist_mono_8d43a2aa-module__8Li5zG__variable inter_fe8b9d92-module__LINzvG__variable antialiased\",\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$5\",\"errorStyles\":[],\"errorScripts\":[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/5658cd19d60d7c01.js\",\"async\":true}]],\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/1e09f01663a3796d.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/b6594419d6992ba9.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/chunks/8fd0d995fc1f0a65.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[[\"$\",\"$L7\",null,{}],[\"$\",\"main\",null,{\"className\":\"min-h-screen\",\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style\",\"children\":404}],[\"$\",\"div\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style\",\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}],[\"$\",\"$L8\",null,{}]]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[\"$L9\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/2bcd218e4fb96e71.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/5cbe5d4d0c529cab.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$La\",null,{\"children\":[\"$\",\"$b\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@c\"}]}]]}],{},null,false,false]},null,false,false]},null,false,false]},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$Ld\",null,{\"children\":\"$Le\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lf\",null,{\"children\":[\"$\",\"$b\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L10\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$11\",[]],\"S\":false}\n"])</script><script>self.__next_f.push([1,"13:T32ac,"])</script><script>self.__next_f.push([1,"Next.js는 React 기반 프레임워크 중 가장 SEO 친화적인 선택입니다. 하지만 프레임워크가 SEO를 '가능하게' 해줄 뿐, 실제 최적화는 개발자의 몫입니다.\n\n이 가이드에서는 Next.js 15 App Router 기준으로 실전에서 바로 적용 가능한 SEO 최적화 테크닉을 다룹니다.\n\n## Next.js SEO의 3가지 핵심 축\n\n[figure:seo-pyramid]\n\n### 1. 기술적 SEO (Technical SEO)\n- 서버 사이드 렌더링 (SSR/SSG)\n- Core Web Vitals 최적화\n- 구조화 데이터 (JSON-LD)\n- Sitemap/Robots.txt 자동화\n\n### 2. On-Page SEO\n- 메타데이터 최적화\n- 시맨틱 HTML 구조\n- 내부 링크 전략\n- 이미지 최적화\n\n### 3. 콘텐츠 SEO\n- 키워드 전략\n- 사용자 의도 반영\n- E-E-A-T (Experience, Expertise, Authoritativeness, Trustworthiness)\n\n:::tip 2025년 SEO 핵심 변화\nGoogle은 이제 **사용자 경험(UX)**과 **콘텐츠 품질**을 동등하게 평가합니다. 빠르지만 내용 없는 사이트나, 좋은 콘텐츠지만 느린 사이트 모두 상위 노출이 어렵습니다.\n:::\n\n---\n\n## 1. 메타데이터 최적화: App Router 방식\n\nNext.js 15의 App Router는 파일 기반 메타데이터 시스템을 제공합니다.\n\n[figure:metadata-flow]\n\n### 정적 메타데이터 (기본)\n\n```typescript\n// app/about/page.tsx\nimport { Metadata } from 'next';\n\nexport const metadata: Metadata = {\n title: 'About POEMA | Digital Agency',\n description: 'POEMA는 웹 개발, 이커머스, 퍼포먼스 마케팅을 제공하는 디지털 에이전시입니다.',\n keywords: ['웹 개발', '이커머스', '디지털 마케팅', 'POEMA'],\n authors: [{ name: 'POEMA Team' }],\n openGraph: {\n title: 'About POEMA',\n description: 'Digital transformation partner for startups',\n images: ['/og-image.jpg'],\n type: 'website',\n },\n twitter: {\n card: 'summary_large_image',\n title: 'About POEMA',\n description: 'Digital transformation partner',\n images: ['/twitter-image.jpg'],\n },\n};\n\nexport default function AboutPage() {\n return \u003cdiv\u003e...\u003c/div\u003e;\n}\n```\n\n### 동적 메타데이터 (추천)\n\n케이스 스터디, 블로그 포스트 등 동적 페이지에 필수입니다.\n\n```typescript\n// app/portfolio/[slug]/page.tsx\nimport { Metadata } from 'next';\nimport { getCaseStudy } from '@/data/marketing/case-studies';\n\nexport async function generateMetadata({\n params\n}: {\n params: Promise\u003c{ slug: string }\u003e\n}): Promise\u003cMetadata\u003e {\n const { slug } = await params;\n const caseStudy = await getCaseStudy(slug);\n\n if (!caseStudy) {\n return {\n title: 'Portfolio Not Found',\n };\n }\n\n return {\n title: `${caseStudy.title.ko} | POEMA Portfolio`,\n description: caseStudy.overview.ko,\n keywords: caseStudy.tags.ko,\n openGraph: {\n title: caseStudy.title.ko,\n description: caseStudy.overview.ko,\n images: [\n {\n url: caseStudy.thumbnail,\n width: 1200,\n height: 630,\n alt: caseStudy.title.ko,\n },\n ],\n type: 'article',\n publishedTime: caseStudy.publishedAt,\n },\n };\n}\n```\n\n:::check 메타데이터 체크리스트\n- [ ] 모든 페이지에 고유한 title (50-60자 이내)\n- [ ] description 120-160자, 액션 지향적 문구 포함\n- [ ] OG 이미지 1200×630px, 파일 크기 300KB 이하\n- [ ] Twitter Card 설정\n- [ ] 다국어 사이트의 경우 `alternates` 설정\n:::\n\n---\n\n## 2. 구조화 데이터: JSON-LD로 리치 스니펫 획득\n\n구조화 데이터는 검색 엔진이 콘텐츠를 이해하도록 돕고, 리치 스니펫을 통해 CTR을 높입니다.\n\n### Organization Schema (전역)\n\n```typescript\n// app/layout.tsx\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n const organizationSchema = {\n '@context': 'https://schema.org',\n '@type': 'Organization',\n name: 'POEMA',\n url: 'https://poema.agency',\n logo: 'https://poema.agency/logo.png',\n description: 'Digital agency specializing in web development and e-commerce',\n address: {\n '@type': 'PostalAddress',\n addressCountry: 'KR',\n addressLocality: 'Seoul',\n },\n contactPoint: {\n '@type': 'ContactPoint',\n telephone: '+82-2-1234-5678',\n contactType: 'Customer Service',\n email: 'hello@poema.agency',\n },\n sameAs: [\n 'https://www.instagram.com/poema.agency',\n 'https://www.linkedin.com/company/poema',\n ],\n };\n\n return (\n \u003chtml\u003e\n \u003chead\u003e\n \u003cscript\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema) }}\n /\u003e\n \u003c/head\u003e\n \u003cbody\u003e{children}\u003c/body\u003e\n \u003c/html\u003e\n );\n}\n```\n\n### Article Schema (블로그 포스트)\n\n```typescript\n// components/seo/BlogPostJsonLd.tsx\ninterface BlogPostJsonLdProps {\n title: string;\n description: string;\n publishedAt: string;\n author: string;\n image: string;\n url: string;\n}\n\nexport function BlogPostJsonLd({ title, description, publishedAt, author, image, url }: BlogPostJsonLdProps) {\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'BlogPosting',\n headline: title,\n description: description,\n image: image,\n datePublished: publishedAt,\n author: {\n '@type': 'Person',\n name: author,\n },\n publisher: {\n '@type': 'Organization',\n name: 'POEMA',\n logo: {\n '@type': 'ImageObject',\n url: 'https://poema.agency/logo.png',\n },\n },\n mainEntityOfPage: {\n '@type': 'WebPage',\n '@id': url,\n },\n };\n\n return (\n \u003cscript\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n /\u003e\n );\n}\n```\n\n### FAQPage Schema\n\n```typescript\nexport function FAQJsonLd({ faqs }: { faqs: { question: string; answer: string }[] }) {\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: faqs.map((faq) =\u003e ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n };\n\n return (\n \u003cscript\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}\n /\u003e\n );\n}\n```\n\n:::warning 구조화 데이터 주의사항\n- Google Rich Results Test로 반드시 검증\n- 실제 페이지에 표시된 내용과 일치해야 함\n- 과도한 키워드 스터핑 금지\n- 업데이트 시 스키마도 함께 수정\n:::\n\n---\n\n## 3. Sitemap \u0026 Robots.txt 자동화\n\nNext.js 15는 파일 기반 sitemap/robots 생성을 지원합니다.\n\n### 동적 Sitemap 생성\n\n```typescript\n// app/sitemap.ts\nimport { MetadataRoute } from 'next';\nimport { caseStudies } from '@/data/marketing/case-studies';\nimport { blogPosts } from '@/data/marketing/blog';\n\nexport default function sitemap(): MetadataRoute.Sitemap {\n const baseUrl = 'https://poema.agency';\n\n // Static pages\n const staticPages: MetadataRoute.Sitemap = [\n {\n url: baseUrl,\n lastModified: new Date(),\n changeFrequency: 'weekly',\n priority: 1.0,\n },\n {\n url: `${baseUrl}/services`,\n lastModified: new Date(),\n changeFrequency: 'monthly',\n priority: 0.8,\n },\n {\n url: `${baseUrl}/portfolio`,\n lastModified: new Date(),\n changeFrequency: 'weekly',\n priority: 0.9,\n },\n ];\n\n // Dynamic case studies\n const caseStudyPages: MetadataRoute.Sitemap = caseStudies.map((study) =\u003e ({\n url: `${baseUrl}/portfolio/${study.slug}`,\n lastModified: new Date(study.publishedAt),\n changeFrequency: 'monthly' as const,\n priority: 0.7,\n }));\n\n // Dynamic blog posts\n const blogPages: MetadataRoute.Sitemap = blogPosts.map((post) =\u003e ({\n url: `${baseUrl}/blog/${post.slug}`,\n lastModified: new Date(post.publishedAt),\n changeFrequency: 'monthly' as const,\n priority: 0.6,\n }));\n\n return [...staticPages, ...caseStudyPages, ...blogPages];\n}\n```\n\n### Robots.txt 설정\n\n```typescript\n// app/robots.ts\nimport { MetadataRoute } from 'next';\n\nexport default function robots(): MetadataRoute.Robots {\n const baseUrl = 'https://poema.agency';\n\n return {\n rules: [\n {\n userAgent: '*',\n allow: '/',\n disallow: ['/api/', '/admin/', '/_next/'],\n },\n {\n userAgent: 'Googlebot',\n allow: '/',\n disallow: ['/api/', '/admin/'],\n },\n ],\n sitemap: `${baseUrl}/sitemap.xml`,\n };\n}\n```\n\n---\n\n## 4. Core Web Vitals 최적화\n\n[figure:web-vitals]\n\nGoogle은 **LCP, FID, CLS**를 페이지 경험 지표로 평가합니다.\n\n### LCP (Largest Contentful Paint) 최적화\n\n**목표**: 2.5초 이하\n\n```typescript\n// 이미지 최적화\nimport Image from 'next/image';\n\n\u003cImage\n src=\"/hero.jpg\"\n alt=\"Hero image\"\n width={1920}\n height={1080}\n priority // LCP 이미지에 필수\n quality={90}\n placeholder=\"blur\"\n blurDataURL=\"data:image/...\"\n/\u003e\n\n// 폰트 최적화\nimport { Inter } from 'next/font/google';\n\nconst inter = Inter({\n subsets: ['latin'],\n display: 'swap', // FOUT 방지\n preload: true,\n});\n```\n\n### CLS (Cumulative Layout Shift) 최적화\n\n**목표**: 0.1 이하\n\n```tsx\n// 이미지/동영상에 명시적 크기 지정\n\u003cImage src=\"...\" width={600} height={400} alt=\"...\" /\u003e\n\n// 동적 콘텐츠에 최소 높이 설정\n\u003cdiv className=\"min-h-[400px]\"\u003e\n {isLoading ? \u003cSkeleton /\u003e : \u003cContent /\u003e}\n\u003c/div\u003e\n\n// 폰트 preload로 FOUT 방지\n\u003clink rel=\"preload\" href=\"/fonts/custom.woff2\" as=\"font\" type=\"font/woff2\" crossOrigin=\"anonymous\" /\u003e\n```\n\n### FID/INP 최적화\n\n**목표**: FID 100ms 이하, INP 200ms 이하\n\n- Dynamic import로 JS 번들 분할\n- 무거운 컴포넌트는 below-the-fold에서만 로드\n- debounce/throttle로 이벤트 핸들러 최적화\n\n:::check Core Web Vitals 체크리스트\n- [ ] PageSpeed Insights로 실제 사용자 데이터 확인\n- [ ] Lighthouse CI로 빌드마다 자동 테스트\n- [ ] Vercel Analytics/Google Search Console로 지속 모니터링\n- [ ] 모바일 우선 최적화 (트래픽의 70%+)\n:::\n\n---\n\n## 5. 다국어 SEO (next-intl 활용)\n\n```typescript\n// app/[locale]/layout.tsx\nimport { Metadata } from 'next';\n\nexport async function generateMetadata({ params }: { params: Promise\u003c{ locale: string }\u003e }): Promise\u003cMetadata\u003e {\n const { locale } = await params;\n\n return {\n alternates: {\n canonical: `https://poema.agency/${locale}`,\n languages: {\n 'ko-KR': 'https://poema.agency/ko',\n 'en-US': 'https://poema.agency/en',\n },\n },\n };\n}\n```\n\n**Hreflang 태그**는 Next.js가 자동 생성하지만, Sitemap에는 수동 추가 필요:\n\n```typescript\nexport default function sitemap(): MetadataRoute.Sitemap {\n return [\n {\n url: 'https://poema.agency/ko',\n lastModified: new Date(),\n alternates: {\n languages: {\n en: 'https://poema.agency/en',\n },\n },\n },\n ];\n}\n```\n\n---\n\n## 6. SEO 체크리스트: 론칭 전 필수 확인\n\n### 기술적 체크\n\n| 항목 | 도구 | 목표 |\n|------|------|------|\n| **Core Web Vitals** | PageSpeed Insights | LCP \u003c2.5s, FID \u003c100ms, CLS \u003c0.1 |\n| **모바일 친화성** | Mobile-Friendly Test | Pass |\n| **구조화 데이터** | Rich Results Test | 0 Errors |\n| **보안** | SSL Labs | A+ |\n| **Sitemap** | Google Search Console | Indexed |\n\n### On-Page 체크\n\n:::check SEO 론칭 체크리스트\n- [ ] 모든 페이지에 고유한 `\u003ctitle\u003e`, `\u003cmeta description\u003e`\n- [ ] H1 태그 페이지당 1개, 의미 있는 계층 구조\n- [ ] 이미지 alt 텍스트 100% 작성\n- [ ] 내부 링크 전략 (주요 페이지 간 연결)\n- [ ] 404 페이지 커스터마이징\n- [ ] OG 이미지 모든 페이지 설정\n- [ ] Canonical URL 설정\n- [ ] 구조화 데이터 (최소 Organization + Breadcrumb)\n:::\n\n---\n\n## 실전 팁: 빠른 SEO 성과 내기\n\n### 1. 롱테일 키워드 공략\n\n\u003e \"이커머스 개발\"보다 \"Shopify 한글화 개발 에이전시\"가 경쟁률 낮고 전환율 높습니다.\n\n- Google Keyword Planner, Ahrefs로 키워드 리서치\n- 블로그 콘텐츠로 롱테일 키워드 대량 타겟팅\n- 지역 키워드 활용 (\"서울 웹 개발\", \"강남 쇼핑몰 제작\")\n\n### 2. E-E-A-T 강화\n\n- 팀 소개 페이지에 실명, 사진, 경력 공개\n- 케이스 스터디에 클라이언트 로고/추천사\n- 블로그에 저자 프로필 명시\n- 업계 미디어 기고, 외부 사이트 백링크\n\n### 3. 기술 블로그로 자연 유입 확보\n\n\"Next.js SEO 가이드\", \"포트폴리오 사이트 제작 방법\" 같은 How-to 콘텐츠는 장기적으로 꾸준한 트래픽을 가져옵니다.\n\n- 주 1-2회 발행\n- 2,000자 이상 심도 있는 콘텐츠\n- 코드 예시, 스크린샷 포함\n- 내부 링크로 서비스 페이지 연결\n\n---\n\n## 마무리: SEO는 마라톤\n\nSEO 성과는 보통 3-6개월 후 나타납니다. 단기 트래픽이 필요하다면 광고를, 장기 자산을 쌓고 싶다면 SEO를 선택하세요.\n\nNext.js는 SEO를 위한 모든 도구를 제공합니다. 이제 실행만 남았습니다."])</script><script>self.__next_f.push([1,"9:[\"$\",\"div\",null,{\"className\":\"pt-24 pb-24\",\"children\":[\"$\",\"div\",null,{\"className\":\"container mx-auto px-4 md:px-6\",\"children\":[[\"$\",\"$L12\",null,{\"post\":{\"id\":\"8\",\"slug\":\"nextjs-seo-optimization-techniques\",\"title\":\"Next.js SEO 최적화 실전 테크닉\",\"category\":\"web-dev\",\"tags\":[\"Next.js\",\"SEO\",\"메타데이터\",\"구조화 데이터\",\"성능 최적화\"],\"thumbnail\":\"/blog/nextjs-seo/thumb.jpg\",\"excerpt\":\"Next.js의 강력한 SEO 기능을 제대로 활용하고 계신가요? App Router 기반의 메타데이터 관리부터 구조화 데이터, 사이트맵 자동화까지.\",\"content\":\"$13\",\"author\":{\"name\":\"Daniel Kim\",\"role\":\"프론트엔드 리드 개발자\"},\"publishedAt\":\"2025-06-02\",\"readingTime\":12}}],\"$L14\"]}]}]\n16:T11e1,"])</script><script>self.__next_f.push([1,"모바일 기기의 다양화와 웹 사용 패턴의 변화로 인해 반응형 웹디자인의 중요성은 나날이 커지고 있습니다. 2025년 현재, 국내 웹사이트 트래픽의 73%가 모바일에서 발생하며, Google은 모바일 우선 인덱싱을 기본으로 적용하고 있습니다.\n\n[figure:responsive-devices]\n\n## 1. 컨테이너 쿼리의 부상\n\n미디어 쿼리를 넘어선 새로운 표준, 컨테이너 쿼리가 주류가 되었습니다. 뷰포트가 아닌 부모 요소의 크기에 반응하는 이 기술은 컴포넌트 기반 디자인 시스템에 완벽하게 맞아떨어집니다.\n\n```css\n.card-container {\n container-type: inline-size;\n container-name: card;\n}\n\n@container card (min-width: 400px) {\n .card-title {\n font-size: 2rem;\n }\n\n .card-content {\n display: grid;\n grid-template-columns: 1fr 1fr;\n }\n}\n```\n\n:::tip 실무 적용 팁\n컨테이너 쿼리는 재사용 가능한 컴포넌트 라이브러리를 구축할 때 특히 유용합니다. 사이드바, 메인 콘텐츠, 모달 등 다양한 컨텍스트에서 동일한 카드 컴포넌트를 사용할 수 있습니다.\n:::\n\n---\n\n## 2. 유동적 타이포그래피\n\n`clamp()` 함수를 활용한 유동적 타이포그래피는 더 이상 실험적 기술이 아닙니다. 미디어 쿼리 없이도 자연스러운 크기 조정이 가능합니다.\n\n```css\nh1 {\n font-size: clamp(2rem, 5vw + 1rem, 4rem);\n line-height: 1.2;\n}\n\np {\n font-size: clamp(1rem, 2vw + 0.5rem, 1.25rem);\n max-width: 65ch;\n}\n```\n\n**clamp() 함수의 구조**: `clamp(최소값, 선호값, 최대값)`\n\n- **최소값**: 가장 작은 화면에서도 가독성 보장\n- **선호값**: 뷰포트 단위를 사용한 유동적 크기\n- **최대값**: 큰 화면에서의 과도한 확대 방지\n\n---\n\n## 3. 모바일 우선 설계 철학\n\n2025년의 모바일 퍼스트는 단순히 작은 화면부터 디자인하는 것을 넘어섭니다. 성능, 접근성, 사용자 경험 모두에서 모바일을 기준점으로 삼아야 합니다.\n\n| 구분 | 기존 접근법 | 2025 모바일 퍼스트 |\n|------|------------|-------------------|\n| **CSS 작성** | 데스크톱 기본 → 모바일 오버라이드 | 모바일 기본 → 점진적 향상 |\n| **이미지 로딩** | 고해상도 일괄 제공 | 반응형 이미지 + lazy loading |\n| **자바스크립트** | 풀 번들 로드 | 코드 스플리팅 + 조건부 로드 |\n| **터치 UI** | 마우스 호버 기준 | 터치 제스처 우선 설계 |\n| **성능 목표** | 데스크톱 기준 최적화 | 3G 네트워크 기준 최적화 |\n\n:::note 핵심 지표\nGoogle Core Web Vitals 기준으로, 모바일에서 **LCP 2.5초 이하**, **FID 100ms 이하**, **CLS 0.1 이하**를 목표로 해야 합니다.\n:::\n\n---\n\n## 4. 고급 그리드 레이아웃\n\nCSS Grid의 `auto-fit`과 `minmax()`를 결합한 반응형 그리드는 미디어 쿼리 없이도 완벽한 레이아웃을 제공합니다.\n\n```css\n.responsive-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 2rem;\n}\n\n/* 컨테이너 쿼리와 결합 */\n@container (min-width: 600px) {\n .responsive-grid {\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 1.5rem;\n }\n}\n```\n\n---\n\n## 5. 다크 모드와 적응형 디자인\n\n사용자 선호도 미디어 쿼리 `prefers-color-scheme`은 이제 필수 기능입니다. 시스템 설정에 자동으로 반응하는 웹사이트는 사용자 만족도를 크게 향상시킵니다.\n\n```css\n:root {\n --bg-color: #ffffff;\n --text-color: #1a1a2e;\n --accent-color: #4a90d9;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --bg-color: #1a1a2e;\n --text-color: #ffffff;\n --accent-color: #6fb1ff;\n }\n}\n```\n\n:::warning 접근성 주의사항\n다크 모드를 구현할 때는 단순히 색상 반전이 아닌, 대비율(WCAG AA 기준 4.5:1 이상)을 반드시 체크해야 합니다. 특히 버튼, 링크 등 인터랙티브 요소의 가독성을 우선 고려하세요.\n:::\n\n---\n\n## 결론\n\n2025년의 반응형 디자인은 기술적 완성도를 넘어 사용자 경험의 본질에 집중합니다. 컨테이너 쿼리, 유동적 타이포그래피, 고급 CSS Grid는 이제 선택이 아닌 기본 도구입니다.\n\n\u003e \"반응형 디자인의 목표는 모든 기기에서 동일한 경험을 제공하는 것이 아니라, 각 기기에 최적화된 경험을 제공하는 것입니다.\"\n\nPOEMA는 이러한 최신 기술을 활용해 성능과 사용자 경험을 모두 만족시키는 웹사이트를 제작합니다."])</script><script>self.__next_f.push([1,"14:[\"$\",\"section\",null,{\"className\":\"mt-20 max-w-5xl mx-auto\",\"children\":[[\"$\",\"h2\",null,{\"className\":\"text-2xl font-bold text-slate-900 mb-8\",\"children\":\"관련 글\"}],[\"$\",\"div\",null,{\"className\":\"grid md:grid-cols-2 lg:grid-cols-3 gap-8\",\"children\":[[\"$\",\"$L15\",\"1\",{\"post\":{\"id\":\"1\",\"slug\":\"responsive-web-design-trends-2025\",\"title\":\"2025년 반응형 웹디자인 트렌드 총정리\",\"category\":\"web-dev\",\"tags\":[\"반응형 디자인\",\"CSS\",\"모바일 퍼스트\",\"UI/UX\"],\"thumbnail\":\"/blog/responsive-design/thumb.jpg\",\"excerpt\":\"모바일 트래픽이 전체의 70%를 넘어선 지금, 반응형 웹디자인은 선택이 아닌 필수입니다. 2025년 주목해야 할 핵심 트렌드를 살펴봅니다.\",\"content\":\"$16\",\"author\":{\"name\":\"Daniel Kim\",\"role\":\"프론트엔드 리드 개발자\"},\"publishedAt\":\"2025-05-10\",\"readingTime\":8},\"index\":0}],\"$L17\",\"$L18\"]}]]}]\n19:T30cd,"])</script><script>self.__next_f.push([1,"웹사이트 로딩 속도는 사용자 경험과 SEO에 직접적 영향을 미칩니다. Google 연구에 따르면 **페이지 로딩이 1초 지연되면 전환율이 7% 감소**합니다. Next.js는 성능 최적화를 위한 강력한 도구를 기본 제공하며, 올바르게 활용하면 경쟁사 대비 3배 빠른 웹사이트를 구축할 수 있습니다.\n\n[figure:rendering-strategies]\n\n## Next.js가 성능 최적화에 강한 이유\n\nNext.js는 React의 강력한 생태계와 서버 사이드 최적화를 결합한 프레임워크입니다.\n\n**핵심 성능 기능**:\n1. **서버 컴포넌트**: 클라이언트로 전송되는 JavaScript 양 최소화\n2. **자동 코드 스플리팅**: 페이지별 필요한 코드만 로드\n3. **이미지 최적화**: WebP 변환, lazy loading 자동 처리\n4. **폰트 최적화**: Layout Shift 제거, 자동 서브셋팅\n5. **스트리밍 SSR**: 점진적 페이지 렌더링\n6. **정적 생성 (SSG)**: 빌드 타임 사전 렌더링\n\n:::note Core Web Vitals 목표치\n- **LCP (Largest Contentful Paint)**: 2.5초 이하\n- **FID (First Input Delay)**: 100ms 이하\n- **CLS (Cumulative Layout Shift)**: 0.1 이하\n\nNext.js로 제작된 POEMA 프로젝트는 평균 **LCP 1.2초, FID 45ms, CLS 0.02**를 기록합니다.\n:::\n\n---\n\n## 1. 렌더링 전략 선택하기\n\nNext.js 16 App Router는 4가지 렌더링 전략을 제공합니다. 페이지 특성에 맞는 전략을 선택해야 합니다.\n\n[figure:rendering-strategies]\n\n| 전략 | 렌더링 시점 | 사용 사례 | 성능 특성 |\n|------|-----------|---------|----------|\n| **SSG (정적 생성)** | 빌드 타임 | 블로그, 마케팅 페이지 | 최고 속도, CDN 캐싱 |\n| **ISR (증분 정적 재생성)** | 빌드 + 주기적 재생성 | 제품 목록, 뉴스 | 빠른 속도 + 신선한 데이터 |\n| **SSR (서버 사이드 렌더링)** | 요청 시 | 개인화 대시보드 | SEO + 동적 데이터 |\n| **CSR (클라이언트 렌더링)** | 브라우저 | 인터랙티브 도구 | SEO 불필요한 페이지 |\n\n### SSG: 최고 성능을 위한 정적 생성\n\n블로그, 랜딩페이지, 포트폴리오처럼 **모든 사용자에게 동일한 콘텐츠**를 보여주는 페이지는 SSG가 최적입니다.\n\n```tsx\n// app/blog/[slug]/page.tsx\nexport async function generateStaticParams() {\n const posts = await getAllPosts();\n return posts.map((post) =\u003e ({\n slug: post.slug,\n }));\n}\n\nexport default async function BlogPost({ params }: { params: { slug: string } }) {\n const post = await getPostBySlug(params.slug);\n\n return (\n \u003carticle\u003e\n \u003ch1\u003e{post.title}\u003c/h1\u003e\n \u003cdiv dangerouslySetInnerHTML={{ __html: post.content }} /\u003e\n \u003c/article\u003e\n );\n}\n```\n\n**성능 이점**:\n- 빌드 타임에 HTML 사전 생성\n- CDN 엣지에 캐싱 → 전 세계 어디서나 50ms 이내 응답\n- JavaScript 번들 크기 최소화\n\n### ISR: 성능과 신선함의 균형\n\n제품 목록, 뉴스 피드처럼 **주기적 업데이트가 필요하지만 실시간일 필요는 없는** 페이지에 적합합니다.\n\n```tsx\n// app/products/page.tsx\nexport const revalidate = 3600; // 1시간마다 재생성\n\nexport default async function ProductsPage() {\n const products = await fetch('https://api.example.com/products', {\n next: { revalidate: 3600 }\n }).then(res =\u003e res.json());\n\n return (\n \u003cdiv className=\"grid grid-cols-3 gap-4\"\u003e\n {products.map((product) =\u003e (\n \u003cProductCard key={product.id} product={product} /\u003e\n ))}\n \u003c/div\u003e\n );\n}\n```\n\n**작동 원리**:\n1. 첫 요청: 캐시된 정적 페이지 즉시 반환\n2. 재검증 시간(revalidate) 경과 후 첫 요청: 여전히 캐시 반환하지만 백그라운드에서 재생성\n3. 재생성 완료: 새로운 정적 페이지로 교체\n\n:::tip ISR vs SSG 선택 기준\n- 데이터 업데이트 주기가 **1시간 이상**: ISR 사용\n- 데이터가 **거의 변하지 않음** (월 1회 미만): SSG 사용\n- **실시간 반영 필수**: SSR 사용\n:::\n\n---\n\n## 2. 이미지 최적화: next/image 마스터하기\n\n이미지는 평균적으로 웹페이지 용량의 **50% 이상**을 차지합니다. Next.js의 `\u003cImage\u003e` 컴포넌트는 이미지 최적화를 자동화합니다.\n\n```tsx\nimport Image from 'next/image';\n\nexport default function HeroSection() {\n return (\n \u003cdiv className=\"relative h-screen\"\u003e\n \u003cImage\n src=\"/hero-background.jpg\"\n alt=\"Hero background\"\n fill\n priority // LCP 이미지는 priority 필수\n sizes=\"100vw\"\n className=\"object-cover\"\n /\u003e\n \u003cdiv className=\"relative z-10\"\u003e\n \u003ch1\u003eWelcome to POEMA\u003c/h1\u003e\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\n**자동 최적화 기능**:\n- **포맷 변환**: JPEG → WebP/AVIF (최대 50% 용량 감소)\n- **반응형 이미지**: `sizes` 속성 기반 자동 생성\n- **Lazy loading**: 뷰포트 진입 시 로드 (Below-the-fold 이미지)\n- **Blur placeholder**: 로딩 중 블러 이미지 표시\n\n### priority vs lazy loading\n\n| 속성 | 사용 시기 | 로딩 방식 |\n|------|---------|----------|\n| `priority` | LCP 이미지 (히어로, 로고) | 즉시 로드, preload 링크 생성 |\n| 기본 (lazy) | Below-the-fold 이미지 | 뷰포트 진입 시 로드 |\n\n:::warning 흔한 실수\n히어로 이미지에 `priority` 설정을 빼먹으면 LCP가 3~5초 지연됩니다. 첫 화면에 보이는 큰 이미지는 **반드시 priority 추가**하세요.\n:::\n\n### 외부 이미지 최적화\n\n외부 URL 이미지도 최적화하려면 `next.config.js`에 도메인 허용 설정이 필요합니다.\n\n```js\n// next.config.js\nmodule.exports = {\n images: {\n remotePatterns: [\n {\n protocol: 'https',\n hostname: 'cdn.example.com',\n pathname: '/images/**',\n },\n ],\n formats: ['image/avif', 'image/webp'], // AVIF 우선, WebP 대체\n },\n};\n```\n\n---\n\n## 3. 폰트 최적화: Layout Shift 제거\n\n웹 폰트는 CLS(Cumulative Layout Shift)의 주요 원인입니다. Next.js의 `next/font`는 **폰트 로딩 중 레이아웃 이동을 완전히 제거**합니다.\n\n```tsx\n// app/layout.tsx\nimport { Inter, Noto_Sans_KR } from 'next/font/google';\n\nconst inter = Inter({\n subsets: ['latin'],\n display: 'swap',\n variable: '--font-inter',\n});\n\nconst notoSansKr = Noto_Sans_KR({\n subsets: ['korean'],\n weight: ['400', '700'],\n display: 'swap',\n variable: '--font-noto-sans-kr',\n});\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n return (\n \u003chtml lang=\"ko\" className={`${inter.variable} ${notoSansKr.variable}`}\u003e\n \u003cbody\u003e{children}\u003c/body\u003e\n \u003c/html\u003e\n );\n}\n```\n\n```css\n/* globals.css */\nbody {\n font-family: var(--font-noto-sans-kr), var(--font-inter), sans-serif;\n}\n```\n\n**자동 최적화**:\n- **자동 서브셋팅**: 사용된 문자만 포함 (한글 11,172자 → 실사용 2,500자)\n- **Self-hosting**: Google Fonts를 로컬에 호스팅 (DNS 조회 제거)\n- **preload**: 폰트 파일 사전 로드로 FOIT(Flash of Invisible Text) 방지\n- **Size adjustment**: 폰트 메트릭 자동 계산으로 CLS 0\n\n---\n\n## 4. 코드 스플리팅과 Dynamic Import\n\nJavaScript 번들 크기는 FID(First Input Delay)에 직접 영향을 미칩니다. **필요한 코드만 필요한 시점에 로드**해야 합니다.\n\n### 자동 코드 스플리팅\n\nNext.js는 `app/` 디렉토리의 각 페이지를 자동으로 분리합니다.\n\n```\napp/\n├── page.tsx → home.js (50KB)\n├── about/page.tsx → about.js (30KB)\n└── products/page.tsx → products.js (80KB)\n```\n\n홈페이지 방문 시 `home.js`만 로드되며, `products.js`는 해당 페이지 방문 시 로드됩니다.\n\n### Dynamic Import로 수동 최적화\n\nBelow-the-fold 컴포넌트, 모달, 차트 같은 **즉시 필요하지 않은 컴포넌트**는 동적 임포트로 분리하세요.\n\n```tsx\n// ❌ 나쁜 예: 모든 컴포넌트 즉시 로드\nimport HeavyChart from '@/components/HeavyChart';\nimport Modal from '@/components/Modal';\n\n// ✅ 좋은 예: 필요 시점에 로드\nimport dynamic from 'next/dynamic';\n\nconst HeavyChart = dynamic(() =\u003e import('@/components/HeavyChart'), {\n loading: () =\u003e \u003cChartSkeleton /\u003e,\n ssr: false, // 차트는 클라이언트에서만 렌더링\n});\n\nconst Modal = dynamic(() =\u003e import('@/components/Modal'));\n```\n\n**성능 향상**:\n- 초기 JavaScript 번들 **40% 감소**\n- FID 평균 **60ms → 35ms** 개선\n- TTI (Time to Interactive) **1.5초 단축**\n\n:::tip 동적 임포트 대상\n- 모달, 드로어, 팝업 (사용자 액션 후 표시)\n- 차트, 지도 (무거운 라이브러리)\n- Below-the-fold 섹션 (스크롤 후 보이는 영역)\n- A/B 테스트 변형 컴포넌트\n:::\n\n---\n\n## 5. 서버 컴포넌트로 클라이언트 JS 줄이기\n\nNext.js 16 App Router의 **서버 컴포넌트는 기본값**입니다. 상호작용이 필요 없는 컴포넌트는 서버에서 렌더링해 클라이언트로 전송되는 JavaScript를 줄이세요.\n\n```tsx\n// ✅ 서버 컴포넌트 (기본값, 'use client' 없음)\nasync function ProductList() {\n const products = await fetchProducts(); // 서버에서 데이터 페칭\n\n return (\n \u003cdiv className=\"grid grid-cols-3 gap-4\"\u003e\n {products.map((product) =\u003e (\n \u003cProductCard key={product.id} product={product} /\u003e {/* 서버 컴포넌트 */}\n ))}\n \u003c/div\u003e\n );\n}\n\n// ✅ 클라이언트 컴포넌트 (상호작용 필요)\n'use client';\nfunction AddToCartButton({ productId }: { productId: string }) {\n const [loading, setLoading] = useState(false);\n\n return (\n \u003cbutton onClick={() =\u003e addToCart(productId)}\u003e\n Add to Cart\n \u003c/button\u003e\n );\n}\n```\n\n**서버 vs 클라이언트 컴포넌트 선택 기준**:\n\n| 조건 | 컴포넌트 타입 |\n|------|-------------|\n| 데이터 페칭, DB 쿼리 | 서버 |\n| `useState`, `useEffect` 사용 | 클라이언트 |\n| 이벤트 리스너 (`onClick`, `onChange`) | 클라이언트 |\n| 브라우저 API (`localStorage`, `window`) | 클라이언트 |\n| 정적 콘텐츠 표시 | 서버 |\n\n---\n\n## 6. 성능 측정과 모니터링\n\n최적화의 효과를 측정하지 않으면 개선 여부를 알 수 없습니다.\n\n### Vercel Speed Insights (권장)\n\nNext.js 배포 시 Vercel Speed Insights를 활성화하면 **실제 사용자 성능 데이터**를 수집합니다.\n\n```tsx\n// app/layout.tsx\nimport { SpeedInsights } from '@vercel/speed-insights/next';\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n return (\n \u003chtml\u003e\n \u003cbody\u003e\n {children}\n \u003cSpeedInsights /\u003e\n \u003c/body\u003e\n \u003c/html\u003e\n );\n}\n```\n\n### Lighthouse CI\n\nCI/CD 파이프라인에 Lighthouse를 통합해 **성능 회귀 방지**:\n\n```json\n// .github/workflows/lighthouse.yml\nname: Lighthouse CI\non: [push]\njobs:\n lighthouse:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n - run: npm ci \u0026\u0026 npm run build\n - uses: treosh/lighthouse-ci-action@v9\n with:\n urls: 'http://localhost:3000'\n budgetPath: './budget.json'\n```\n\n```json\n// budget.json (성능 예산)\n[\n {\n \"path\": \"/*\",\n \"resourceSizes\": [\n { \"resourceType\": \"script\", \"budget\": 300 }, // JavaScript 300KB 이하\n { \"resourceType\": \"image\", \"budget\": 500 } // 이미지 500KB 이하\n ],\n \"timings\": [\n { \"metric\": \"interactive\", \"budget\": 3000 } // TTI 3초 이하\n ]\n }\n]\n```\n\n---\n\n## 실전 성능 개선 사례\n\n**Before (일반 React SPA)**:\n- LCP: 4.2초\n- FID: 180ms\n- CLS: 0.28\n- JavaScript 번들: 850KB\n- Lighthouse 점수: 62/100\n\n**개선 작업**:\n1. Next.js App Router 마이그레이션\n2. 주요 페이지 SSG 전환\n3. `next/image`로 전환 (WebP, lazy loading)\n4. `next/font`로 폰트 최적화\n5. 무거운 라이브러리 동적 임포트\n6. 서버 컴포넌트 최대 활용\n\n**After (Next.js 16 최적화)**:\n- LCP: **1.1초** (74% 개선)\n- FID: **42ms** (77% 개선)\n- CLS: **0.03** (89% 개선)\n- JavaScript 번들: **280KB** (67% 감소)\n- Lighthouse 점수: **98/100**\n\n[figure:web-vitals]\n\n---\n\n## 결론: 성능은 선택이 아닌 필수\n\n성능은 **기능이 아닌 사용자 경험의 핵심**입니다. 느린 웹사이트는 아무리 좋은 콘텐츠와 디자인을 가져도 사용자를 잃습니다.\n\nNext.js는 성능 최적화를 위한 모든 도구를 제공합니다. **올바르게 활용하면 세계 최고 수준의 웹사이트**를 만들 수 있습니다.\n\nPOEMA는 Next.js 성능 최적화 전문가로서 평균 **Lighthouse 점수 95+ 달성**을 보장합니다."])</script><script>self.__next_f.push([1,"17:[\"$\",\"$L15\",\"5\",{\"post\":{\"id\":\"5\",\"slug\":\"nextjs-performance-optimization\",\"title\":\"Next.js로 초고속 웹사이트 만들기: 성능 최적화 완벽 가이드\",\"category\":\"web-dev\",\"tags\":[\"Next.js\",\"React\",\"성능 최적화\",\"Web Vitals\"],\"thumbnail\":\"/blog/nextjs-performance/thumb.jpg\",\"excerpt\":\"Next.js의 강력한 성능 최적화 기능을 활용해 로딩 속도를 3배 빠르게 만드는 실전 기법을 배웁니다.\",\"content\":\"$19\",\"author\":{\"name\":\"Daniel Kim\",\"role\":\"프론트엔드 리드 개발자\"},\"publishedAt\":\"2025-01-20\",\"readingTime\":12},\"index\":1}]\n1a:T236d,"])</script><script>self.__next_f.push([1,"프리랜서, 에이전시, 스타트업에게 포트폴리오 사이트는 단순한 '작업물 갤러리'가 아닙니다. 이것은 첫 만남에서 클라이언트를 설득하고, 신뢰를 구축하고, 계약으로 전환시키는 **비즈니스 세일즈 도구**입니다.\n\n이 가이드에서는 2025년 기준으로 효과적인 포트폴리오 사이트를 구축하는 전략부터 기술 스택, 실전 팁까지 모두 다룹니다.\n\n## 포트폴리오 사이트의 핵심 목적\n\n많은 사람들이 \"예쁜 사이트를 만들어야지\"라고 생각하지만, 진짜 목적은 다릅니다.\n\n:::check 포트폴리오 사이트의 4가지 핵심 목표\n- [ ] **신뢰 구축**: 전문성과 실행 능력 증명\n- [ ] **차별화**: 수많은 경쟁자 중에서 선택받기\n- [ ] **전환 유도**: 방문자를 문의/계약으로 이어지게 하기\n- [ ] **SEO**: 검색을 통한 자연 유입 확보\n:::\n\n단순히 \"이런 프로젝트를 했습니다\"를 넘어, **\"당신의 문제를 이렇게 해결할 수 있습니다\"**라는 메시지를 전달해야 합니다.\n\n---\n\n## 포트폴리오 사이트 해부학\n\n효과적인 포트폴리오 사이트는 다음 구조를 따릅니다.\n\n[figure:portfolio-anatomy]\n\n### 1. Hero Section: 3초 안에 각인시키기\n\n방문자는 평균 3초 만에 사이트를 떠날지 더 볼지 결정합니다. Hero 섹션에서 다음을 명확히 전달하세요.\n\n- **누구인가**: \"AI 기반 웹 솔루션 전문 에이전시\"\n- **무엇을 하는가**: \"병원, 뷰티 업계의 디지털 전환을 돕습니다\"\n- **왜 선택해야 하는가**: \"론칭 후 평균 전환율 230% 향상\"\n\n:::tip Hero Section 공식\n[직업/전문 분야] + [타겟 고객/산업] + [구체적 성과/차별점] = 강력한 첫인상\n:::\n\n### 2. Case Studies: 숫자로 말하는 성과\n\n\u003e \"클라이언트는 당신의 디자인 감각이 아니라, 비즈니스 성과에 돈을 냅니다.\"\n\n케이스 스터디는 포트폴리오의 핵심입니다. 각 프로젝트마다 다음 구조를 따르세요.\n\n1. **Overview**: 클라이언트 배경, 프로젝트 목표 (2-3 문장)\n2. **Challenge**: 구체적인 문제 상황 (수치 포함)\n3. **Solution**: 당신의 접근 방식과 핵심 결정\n4. **Results**: 정량적 성과 (전환율, 매출, 트래픽 등)\n5. **Testimonial**: 클라이언트 추천사 (선택)\n\n**좋은 예시**:\n- \"웹사이트 리뉴얼 후 페이지 이탈률 65% → 32%로 감소\"\n- \"예약 전환율 1.2% → 4.8%로 300% 증가\"\n- \"모바일 트래픽 비중 23% → 61%로 확대\"\n\n**나쁜 예시**:\n- \"모던한 디자인으로 리뉴얼했습니다\"\n- \"사용자 경험을 개선했습니다\"\n- \"반응형 웹사이트를 제작했습니다\"\n\n### 3. Services: 명확한 패키지화\n\n\"뭐든지 다 해드립니다\"는 가장 나쁜 메시지입니다. 명확한 서비스 패키지를 제시하세요.\n\n- **웹 개발**: 기업 소개 사이트, 예약 시스템, 관리 대시보드\n- **이커머스**: Shopify/WooCommerce 구축, 결제 연동, 재고 관리\n- **마케팅**: SEO 최적화, 광고 캠페인, 전환율 최적화\n\n각 서비스마다 **예상 기간**, **시작 가격대**, **포함 항목**을 명시하면 문의 품질이 크게 향상됩니다.\n\n### 4. About: 신뢰를 만드는 스토리\n\n기술 스택 나열이 아니라, **당신이 왜 이 일을 하는지, 어떤 가치를 믿는지** 이야기하세요.\n\n- 창업/커리어 전환 스토리\n- 핵심 철학 또는 작업 원칙\n- 팀 구성 (에이전시인 경우)\n- 수상 경력, 인증서, 파트너사\n\n:::warning 흔한 실수\n\"저는 10년 경력의 개발자입니다\"보다 \"저는 병원 10곳의 디지털 전환을 성공시켰습니다\"가 훨씬 강력합니다. 경력보다 성과를 강조하세요.\n:::\n\n### 5. Contact: 마찰 없는 전환 경로\n\n복잡한 폼은 전환율을 죽입니다. 다음 중 최소한으로 유지하세요.\n\n- 이름\n- 이메일\n- 프로젝트 유형 (선택)\n- 간단한 설명\n\n추가로 **Calendly 링크** (바로 상담 예약), **카카오톡 채널**, **이메일 주소**를 함께 제공하면 전환율이 높아집니다.\n\n---\n\n## 2025년 추천 기술 스택\n\n[figure:rendering-strategies]\n\n### 프론트엔드 프레임워크 비교\n\n| 기술 스택 | 장점 | 단점 | 추천 대상 |\n|----------|------|------|----------|\n| **Next.js** | SEO 최적화, 빠른 개발, 대규모 커뮤니티 | 러닝 커브, 서버 비용 | 대부분의 포트폴리오 사이트 |\n| **Astro** | 초고속 로딩, 거의 Zero JS | 동적 기능 제한적 | 정적 콘텐츠 중심 |\n| **Webflow** | 노코드, 빠른 제작 | 커스터마이징 한계, 종속성 | 비개발자 디자이너 |\n| **WordPress** | 쉬운 관리, 플러그인 생태계 | 속도, 보안 이슈 | 블로그 중심 포트폴리오 |\n\n:::tip 2025년 최고의 선택: Next.js\n**Next.js 15 + React 19 + TypeScript**는 현재 포트폴리오 사이트 구축의 골드 스탠다드입니다. App Router로 SEO와 성능을 동시에 잡고, Vercel로 무료 배포까지 가능합니다.\n:::\n\n### 기본 Next.js 포트폴리오 구조\n\n```typescript\n// app/page.tsx - 홈페이지\nimport { HeroSection } from '@/components/landing/HeroSection';\nimport { PortfolioGrid } from '@/components/portfolio/PortfolioGrid';\nimport { ContactForm } from '@/components/forms/ContactForm';\n\nexport default function Home() {\n return (\n \u003c\u003e\n \u003cHeroSection /\u003e\n \u003cPortfolioGrid featured={true} /\u003e\n \u003cContactForm /\u003e\n \u003c/\u003e\n );\n}\n\n// app/portfolio/page.tsx - 전체 포트폴리오\nimport { caseStudies } from '@/data/marketing/case-studies';\nimport { CaseStudyCard } from '@/components/portfolio/CaseStudyCard';\n\nexport default function PortfolioPage() {\n return (\n \u003cdiv className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\"\u003e\n {caseStudies.map((study) =\u003e (\n \u003cCaseStudyCard key={study.id} study={study} /\u003e\n ))}\n \u003c/div\u003e\n );\n}\n```\n\n### 필수 라이브러리 추천\n\n```json\n{\n \"dependencies\": {\n \"next\": \"^15.0.0\",\n \"react\": \"^19.0.0\",\n \"framer-motion\": \"^12.0.0\",\n \"next-intl\": \"^4.0.0\",\n \"react-hook-form\": \"^7.53.0\",\n \"zod\": \"^4.0.0\",\n \"lucide-react\": \"^0.400.0\"\n }\n}\n```\n\n- **framer-motion**: 부드러운 애니메이션으로 프리미엄 느낌\n- **next-intl**: 다국어 지원 (해외 클라이언트 대응)\n- **react-hook-form + Zod**: 타입 안전한 폼 관리\n- **lucide-react**: 가볍고 일관된 아이콘\n\n---\n\n## 성능 최적화: 1초 안에 로딩하기\n\n포트폴리오 사이트는 **당신의 기술력을 보여주는 쇼케이스**입니다. 느린 사이트는 그 자체로 실력을 의심받습니다.\n\n:::check 성능 최적화 체크리스트\n- [ ] 이미지 WebP/AVIF 형식 + Next.js Image 컴포넌트\n- [ ] 폰트 서브셋팅 (한글: 2,350자, 영문: 알파벳만)\n- [ ] Critical CSS 인라인, 나머지 지연 로딩\n- [ ] 동적 import로 below-the-fold 컴포넌트 분할\n- [ ] Vercel Analytics로 실제 사용자 성능 측정\n:::\n\n### 이미지 최적화 예시\n\n```tsx\nimport Image from 'next/image';\n\n\u003cImage\n src=\"/portfolio/project-thumbnail.webp\"\n alt=\"프로젝트 썸네일\"\n width={600}\n height={400}\n loading=\"lazy\"\n placeholder=\"blur\"\n blurDataURL=\"data:image/webp;base64,...\"\n/\u003e\n```\n\n**목표**: Lighthouse 점수 95+ (Performance, Accessibility, Best Practices, SEO)\n\n---\n\n## 실전 팁: 포트폴리오로 클라이언트 유치하기\n\n### 1. SEO를 철저히 챙기세요\n\n포트폴리오 사이트는 **\"서울 병원 홈페이지 제작\"**, **\"이커머스 개발 에이전시\"** 같은 검색어로 상위 노출되어야 합니다.\n\n- 각 케이스 스터디마다 고유한 메타 디스크립션\n- 구조화 데이터 (JSON-LD) 추가\n- 블로그 섹션으로 롱테일 키워드 공략\n\n### 2. 소셜 프루프를 적극 활용하세요\n\n- 클라이언트 로고 (허가받은 경우)\n- 추천사 (이름, 직함, 사진 포함)\n- 수치 성과 (프로젝트 수, 평균 ROI, 총 거래액 등)\n\n### 3. 정기적으로 업데이트하세요\n\n\u003e \"2년 전 프로젝트가 최신이면, 지금은 일이 없다는 신호입니다.\"\n\n최소 3개월마다 새로운 케이스 스터디를 추가하거나, 블로그 포스트를 발행하세요. 활발히 활동하는 인상이 중요합니다.\n\n### 4. 분석 데이터로 개선하세요\n\nGoogle Analytics 4로 다음을 추적하세요.\n\n- 어떤 케이스 스터디가 가장 많이 조회되는가?\n- 방문자가 어디서 이탈하는가?\n- Contact 폼까지 도달한 비율은?\n\n데이터 기반으로 포트폴리오를 지속 개선하세요.\n\n---\n\n## 마무리: 포트폴리오는 살아있는 자산\n\n포트폴리오 사이트는 \"한 번 만들고 끝\"이 아닙니다. 새로운 프로젝트, 기술, 성과를 계속 반영하며 진화시켜야 합니다.\n\n첫 버전은 완벽하지 않아도 괜찮습니다. 지금 당장 시작하고, 3개월마다 개선하세요. 1년 후에는 당신의 포트폴리오가 가장 강력한 영업 도구가 되어 있을 것입니다."])</script><script>self.__next_f.push([1,"18:[\"$\",\"$L15\",\"7\",{\"post\":{\"id\":\"7\",\"slug\":\"portfolio-website-complete-guide-2025\",\"title\":\"포트폴리오 사이트 제작 완벽 가이드 2025\",\"category\":\"web-dev\",\"tags\":[\"포트폴리오\",\"웹사이트 제작\",\"개인 브랜딩\",\"프리랜서\"],\"thumbnail\":\"/blog/portfolio-guide/thumb.jpg\",\"excerpt\":\"포트폴리오 사이트는 단순한 작업물 모음이 아닙니다. 클라이언트를 설득하고 기회를 만드는 비즈니스 도구입니다.\",\"content\":\"$1a\",\"author\":{\"name\":\"Daniel Kim\",\"role\":\"프론트엔드 리드 개발자\"},\"publishedAt\":\"2025-06-15\",\"readingTime\":10},\"index\":2}]\ne:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\nc:null\n10:[[\"$\",\"title\",\"0\",{\"children\":\"Next.js SEO 최적화 실전 테크닉\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Next.js의 강력한 SEO 기능을 제대로 활용하고 계신가요? App Router 기반의 메타데이터 관리부터 구조화 데이터, 사이트맵 자동화까지.\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0b3bf435.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L1b\",\"3\",{}]]\n"])</script></body></html>