티스토리 뷰

반응형

JAMstack 기반 포트폴리오 사이트 핵심 기능 개발

1. 프로젝트 핵심 기능 개요

JAMstack 아키텍처를 활용한 포트폴리오 사이트에서는 빠른 성능, 유지보수 편리성, 동적 콘텐츠 지원이 중요하다.
이번 단계에서는 프로젝트 페이지, 블로그 기능, 연락처 페이지 등을 구현하고,
서버리스 API 및 CMS(Content Management System)를 연동하여 동적 데이터 관리를 적용한다.


2. 홈(Home) 페이지 구현

① 홈 페이지 레이아웃 구성

홈 페이지는 방문자에게 빠르게 개발자의 핵심 정보를 제공하는 역할을 한다.

  • 자기소개 (이름, 직업, 간단한 소개)
  • 기술 스택 (사용하는 기술, 숙련도 표시)
  • 주요 프로젝트 하이라이트 (데모 및 GitHub 링크)
  • CTA(Call To Action) 버튼 (프로젝트 보기, 블로그 방문, 연락하기)

🔹 홈 페이지 기본 구조 (pages/index.js)

export default function Home() {
  return (
    <div className="container mx-auto text-center">
      <h1 className="text-4xl font-bold">안녕하세요, 개발자 John Doe입니다!</h1>
      <p className="text-lg mt-4">JAMstack을 활용한 웹 개발을 전문으로 합니다.</p>
      <button className="mt-6 px-4 py-2 bg-blue-500 text-white rounded-lg">
        프로젝트 보기
      </button>
    </div>
  );
}

🔹 Tailwind CSS를 활용하여 스타일링 적용

@tailwind base;
@tailwind components;
@tailwind utilities;

3. 프로젝트(Projects) 페이지 구현

반응형

① Strapi CMS와 프로젝트 데이터 연동

정적 페이지에서 동적인 프로젝트 데이터를 표시하려면 CMS(Content Management System)를 활용하는 것이 좋다.
Strapi 또는 Contentful API를 사용하여 프로젝트 데이터를 관리하고 불러온다.

🔹 프로젝트 데이터 API 호출 (lib/api.js)

export async function getProjects() {
  const res = await fetch("http://localhost:1337/api/projects");
  const data = await res.json();
  return data;
}

🔹 프로젝트 목록 페이지 (pages/projects.js)

import { getProjects } from "../lib/api";

export default function Projects({ projects }) {
  return (
    <div className="container mx-auto">
      <h1 className="text-3xl font-bold mb-6">포트폴리오 프로젝트</h1>
      <ul>
        {projects.map((project) => (
          <li key={project.id} className="mb-4">
            <h2 className="text-xl font-semibold">{project.title}</h2>
            <p>{project.description}</p>
            <a href={project.link} className="text-blue-500">프로젝트 보기</a>
          </li>
        ))}
      </ul>
    </div>
  );
}

export async function getStaticProps() {
  const projects = await getProjects();
  return { props: { projects } };
}

4. 블로그(Blog) 페이지 구현

① Markdown 기반 블로그 시스템 구축

JAMstack에서는 정적 사이트 생성을 통해 블로그를 효율적으로 운영할 수 있다.
MDX(Markdown + JSX) 파일을 활용하여 블로그 글을 작성하고, Next.js에서 동적으로 렌더링한다.

🔹 블로그 게시글을 위한 posts 폴더 구조

posts/
 ├── hello-world.mdx
 ├── jamstack-guide.mdx
 ├── serverless-tutorial.mdx

🔹 MDX 포스트 예시 (posts/hello-world.mdx)

---
title: "JAMstack 기반 포트폴리오 개발하기"
date: "2025-03-15"
author: "John Doe"
tags: ["JAMstack", "Next.js", "Serverless"]
---

# JAMstack이란 무엇인가?

JAMstack은 **JavaScript, API, Markup**의 조합으로 구성된 최신 웹 개발 방식입니다...

🔹 Next.js에서 MDX를 지원하도록 설정 (next.config.js)

const withMDX = require('@next/mdx')({
  extension: /\.mdx?$/
});
module.exports = withMDX({
  pageExtensions: ['js', 'jsx', 'md', 'mdx']
});

🔹 블로그 목록 페이지 (pages/blog.js)

import fs from "fs";
import path from "path";
import matter from "gray-matter";
import Link from "next/link";

export default function Blog({ posts }) {
  return (
    <div className="container mx-auto">
      <h1 className="text-3xl font-bold mb-6">기술 블로그</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.slug} className="mb-4">
            <Link href={`/blog/${post.slug}`}>
              <h2 className="text-xl font-semibold text-blue-500">{post.title}</h2>
            </Link>
            <p>{post.date}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

export async function getStaticProps() {
  const postsDirectory = path.join(process.cwd(), "posts");
  const filenames = fs.readdirSync(postsDirectory);

  const posts = filenames.map((filename) => {
    const filePath = path.join(postsDirectory, filename);
    const fileContent = fs.readFileSync(filePath, "utf8");
    const { data } = matter(fileContent);
    
    return {
      slug: filename.replace(".mdx", ""),
      ...data
    };
  });

  return { props: { posts } };
}

5. 연락처(Contact) 페이지 구현

① 서버리스 API(OpenFaaS)로 이메일 전송 기능 추가

🔹 서버리스 함수 작성 (email-api/handler.js)

const nodemailer = require("nodemailer");

module.exports = async (event, context) => {
  const { email, message } = JSON.parse(event.body);
  
  let transporter = nodemailer.createTransport({
    service: "gmail",
    auth: {
      user: process.env.EMAIL_USER,
      pass: process.env.EMAIL_PASS
    }
  });

  await transporter.sendMail({
    from: email,
    to: "admin@example.com",
    subject: "새로운 문의",
    text: message
  });

  return {
    statusCode: 200,
    body: JSON.stringify({ success: true })
  };
};

🔹 OpenFaaS에 API 배포

faas-cli new send-email --lang node12
cd send-email
faas-cli up -f send-email.yml

🔹 연락처 폼 페이지 (pages/contact.js)

import { useState } from "react";

export default function Contact() {
  const [form, setForm] = useState({ email: "", message: "" });

  async function handleSubmit(e) {
    e.preventDefault();
    await fetch("/function/send-email", {
      method: "POST",
      body: JSON.stringify(form),
      headers: { "Content-Type": "application/json" }
    });
    alert("문의가 전송되었습니다!");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" placeholder="이메일" onChange={(e) => setForm({ ...form, email: e.target.value })} />
      <textarea placeholder="메시지" onChange={(e) => setForm({ ...form, message: e.target.value })} />
      <button type="submit">전송</button>
    </form>
  );
}

6. 결론

홈, 프로젝트, 블로그, 연락처 페이지 구현 완료
Strapi CMS, MDX, OpenFaaS API 연동하여 동적 콘텐츠 처리
서버리스 API 활용하여 이메일 문의 기능 추가

 

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함
반응형