조아마시

쓸모 있는 상세페이지 만들기

웹개발/nextjs

[NextJS] 서버 컴포넌트 + 클라이언트 컴포넌트를 같이 사용

joamashi 2025. 4. 29. 22:04
반응형

1. 서버: 데이터 가져오기 + 클라이언트: 사용자 인터랙션

// 서버 컴포넌트 (PostList.jsx)
// DB 또는 API에서 글 목록을 가져오는 서버 컴포넌트
import { fetchPosts } from "@/lib/api";
import PostItem from "./PostItem";

export default async function PostList() {
  const posts = await fetchPosts(); // 서버에서 글 목록을 받아옴

  return (
    <div>
      {posts.map(post => (
        <PostItem key={post.id} post={post} />
      ))}
    </div>
  );
}
// 클라이언트 컴포넌트 (PostItem.jsx)
// 글 하나를 표시하고 좋아요(❤️) 토글 기능을 제공하는 클라이언트 컴포넌트
'use client';

import { useState } from "react";

export default function PostItem({ post }) {
  const [liked, setLiked] = useState(false); // 좋아요 상태 관리

  return (
    <div onClick={() => setLiked(!liked)}> {/* 클릭하면 좋아요 상태 토글 */}
      <h2>{post.title}</h2>
      <p>{liked ? "❤️" : "🤍"}</p> {/* 상태에 따라 아이콘 변경 */}
    </div>
  );
}

2. 서버: 페이지 SEO + 클라이언트: 폼 입력 처리

// 서버 컴포넌트 (ContactPage.jsx)
// 페이지 메타데이터(SEO)와 함께 폼을 포함한 서버 컴포넌트
import ContactForm from "./ContactForm";

export const metadata = {
  title: "Contact Us", // SEO를 위한 페이지 타이틀 설정
};

export default function ContactPage() {
  return (
    <div>
      <h1>Contact</h1>
      <ContactForm /> {/* 클라이언트 폼 컴포넌트 호출 */}
    </div>
  );
}
// 클라이언트 컴포넌트 (ContactForm.jsx)
// 입력창을 관리하는 클라이언트 컴포넌트
'use client';

import { useState } from "react";

export default function ContactForm() {
  const [message, setMessage] = useState(""); // 입력한 메시지를 상태로 관리

  return (
    <form>
      <textarea 
        value={message} 
        onChange={e => setMessage(e.target.value)} // 입력값 변경시 상태 업데이트
      />
      <button type="submit">Send</button> {/* 전송 버튼 */}
    </form>
  );
}

3. 서버: 로그인 상태 확인 + 클라이언트: 로그아웃 버튼 동작

// 서버 컴포넌트 (Header.jsx)
// 서버에서 로그인 상태를 가져와서 보여주는 컴포넌트
import LogoutButton from "./LogoutButton";
import { getUserSession } from "@/lib/auth";

export default async function Header() {
  const session = await getUserSession(); // 로그인 세션 정보 가져오기

  return (
    <header>
      {session ? ( // 로그인 상태이면 사용자 이름과 로그아웃 버튼 표시
        <>
          <p>Welcome, {session.username}</p>
          <LogoutButton />
        </>
      ) : (
        <p>Please login</p> // 로그인 안 했을 경우 메시지
      )}
    </header>
  );
}
// 클라이언트 컴포넌트 (LogoutButton.jsx)
// 로그아웃 버튼을 클릭하면 로그아웃 API 호출
'use client';

export default function LogoutButton() {
  const logout = async () => {
    await fetch("/api/logout", { method: "POST" }); // 로그아웃 요청
    location.reload(); // 페이지 새로고침
  };

  return <button onClick={logout}>Logout</button>; // 클릭 시 로그아웃 실행
}

4. 서버: 제품 목록 + 클라이언트: 장바구니 추가 버튼

// 서버 컴포넌트 (ProductList.jsx)
// 서버에서 상품 목록을 가져와서 표시하는 컴포넌트
import ProductCard from "./ProductCard";
import { fetchProducts } from "@/lib/api";

export default async function ProductList() {
  const products = await fetchProducts(); // 상품 목록 받아오기

  return (
    <div>
      {products.map(product => (
        <ProductCard key={product.id} product={product} /> // 각각 상품카드로 넘김
      ))}
    </div>
  );
}
// 클라이언트 컴포넌트 (ProductCard.jsx)
// 장바구니에 추가하는 버튼을 가진 상품 카드
'use client';

export default function ProductCard({ product }) {
  const addToCart = () => {
    console.log(`Added ${product.name} to cart`); // 콘솔에 장바구니 추가 로그 출력
  };

  return (
    <div>
      <h2>{product.name}</h2>
      <button onClick={addToCart}>Add to Cart</button> {/* 버튼 누르면 장바구니 추가 */}
    </div>
  );
}

5. 서버: 게시판 글 목록 + 클라이언트: '더 보기' 버튼으로 페이징

// 서버 컴포넌트 (Board.jsx)
// 서버에서 1페이지 글 목록만 가져오는 컴포넌트
import LoadMoreButton from "./LoadMoreButton";
import { fetchPosts } from "@/lib/api";

export default async function Board() {
  const posts = await fetchPosts({ page: 1 }); // 첫 번째 페이지 글 가져오기

  return (
    <div>
      {posts.map(post => (
        <div key={post.id}>{post.title}</div> // 글 제목 출력
      ))}
      <LoadMoreButton /> {/* 추가 글 로딩 버튼 */}
    </div>
  );
}
// 클라이언트 컴포넌트 (LoadMoreButton.jsx)
// "더 보기" 버튼으로 다음 페이지 글을 불러오는 컴포넌트
'use client';

export default function LoadMoreButton() {
  const loadMore = async () => {
    console.log("Fetching more posts..."); // 실제로는 API 요청 보내야 함
  };

  return <button onClick={loadMore}>Load More</button>; // 클릭 시 다음 글 요청
}
728x90
반응형