반응형
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
반응형
'웹개발 > nextjs' 카테고리의 다른 글
[NextJS] 서버 컴포넌트 + 클라이언트 컴포넌트가 한 화면에 자연스럽게 통합 예시 (0) | 2025.04.29 |
---|---|
[Nextjs] Image 사용하기 (0) | 2024.07.28 |