자 우선 목표를 말해볼게
이 서브메뉴를 클릭 시
우선 간단하게 이렇게 화면이 나오게 할거야
저번에 분리해논 구조를 먼저 보자
1. src/app/leadership/page.js
- 서브메뉴를 클릭하면 이동하는 page
2. component/Profile.js
- leadership/page.js 에서 tab별 profile 정보를 보여주는 component 요소
여기에 추가적으로 서브메뉴를 클릭했을 때 어떤 화면이 클릭되었는지 좀 더 명확하게 보여줄 수 있게 sidebar를 만듬
우선 결과 페이지 부터 보여줄게
결과
왼쪽 sidebar랑 아직 header는 연결이 안됏어 profile만 작업된거니까 이부분 코드만 먼저 설명할게
1. src/app/leadership/page.js
import Profile from "@/components/Profile";
import prisma from "../../../prisma/client";
import Sidebar from "@/components/Sidebar";
export default async function Leadership() {
let allUsers = [];
let allCommons = [];
try {
allUsers = await prisma.USERS.findMany();
allCommons = await prisma.COMMON.findMany({
where: {
PARENT_ID: "PS",
},
});
console.log("All Users:", allUsers);
console.log("All allCommons:", allCommons);
} catch (error) {
console.error("Error fetching users:", error);
console.log("Prisma instance:", prisma);
}
return (
<div className="flex justify-center w-full">
{/* Sidebar */}
<Sidebar />
{/* Main Content */}
<div className="flex">
<Profile allUsers={allUsers} allCommons={allCommons} />
</div>
</div>
);
}
짧게 간단 설명
- prisma 객체를 import 하여 사용
- findMany를 이용하여 USERS, COMMON 테이블의 전체 데이터 조회 (조건부 조회도 적용)
- component/Profile.js로 props 전달
2. component/Profile.js
"use client";
import { useState } from "react";
export default function Profile({ allUsers, allCommons }) {
const [activeTab, setActiveTab] = useState("전체");
/** common 테이블의 common_id와 users 테이블의 position이 같은 데이터를 찾아서 같은 데이터면 Common_nm을 반환하고 아니면 기타로 반환하는거야*/
const getPositionName = (postion) => {
const common = allCommons.find((common) => common.COMMON_ID === postion);
return common ? common.COMMON_NM : "기타";
};
/**
* reduce 문법 설명
* 배열의 요소를 하나의 값으로 줄이거나, 변환할 때 사용
* reduce((누적값, 현재값, 인덱스, 요소) => { return 결과 }, 초기값)
* ex_ const sum = [1, 2, 3, 4, 5].reduce((acc, cur) => acc + cur, 0); console.log(sum); // 15
* ex_2 const users = [{ id: 1, name: 'Alice'}, { id:2, name: 'Bob'}, {id: 3, name: 'Charlie'}];
* const ages = users.reduce((acc, cur) => { acc[cur.id] = acc.name;}, {}); console.log(args); // {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
* 이렇게 배열을 key-value 객체로 변환할때도 사용함
*
* 우리는 여기서 allusers와 allcommon에서 공통인 common_id와 position를 비교하여 common_nm을 가져온뒤
* common_nm을 key로 만들고 common_id가 같은 allusers의 user_nm, common_id의 common_nm, user_img를 reduce를통해 객체로 만들어야됨
*
* 여기서 아직 클라우드 스토리지에 파일이 올라가 있지 않아서 이미지 조회는 차후에 코드 업데이트 할 예정
* **/
const staffMembers = allUsers.reduce((acc, user) => {
const staffName = getPositionName(user.POSITION);
/** acc[직분]이 없으면 배열을 생성 */
if (!acc[staffName]) {
acc[staffName] = [];
}
/** acc[직분]에 user의 이름, 직분, 이미지를 추가 */
acc[staffName].push({
name: user.USER_NM,
position: staffName,
image: user.IMAGE ? user.IMAGE : "/images/leadership/default_profile.png",
});
return acc;
}, {});
/** Object.keys(객체)를 사용하면 객체의 key값만 가져올 수 있음 */
const tabs = ["전체", ...Object.keys(staffMembers)];
return (
<div className="max-w-6xl mx-auto p-6">
{/* Tabs */}
<div className="grid grid-cols-8 mb-8 border-b">
{tabs.map((tab) => (
<button
key={tab}
className={`py-3 text-center text-lg font-medium transition-colors duration-200 ${
activeTab === tab
? "text-blue-600 border-b-2 border-blue-600"
: "text-gray-600 hover:text-blue-500"
}`}
onClick={() => setActiveTab(tab)}
>
{tab}
</button>
))}
</div>
{/* Content Section */}
<div className="mb-8">
{/* Section Title */}
<div className="flex items-center mb-6">
<div className="w-2 h-6 bg-blue-600 mr-3"></div>
<h2 className="text-xl font-bold text-blue-600">{activeTab}</h2>
</div>
{/*
이 부분은 grid를 사용하여 이미지와 이름, 직분을 보여주는 부분이야
1. Object.values(객체)를 사용하면 객체의 value값만 가져올 수 있고
2. flat()를 사용하면 2차원 배열을 1차원 배열로 flattening할 수 있음
예시)
1. Object.values
const staffMembers = {
위임목사: [
{ name: "서동욱", position: "목사" },
{ name: "김미옥", position: "사모" }
],
원로목사: [
{ name: "박지성", position: "목사" }
],
교육목사: [
{ name: "이영희", position: "목사" },
{ name: "최민수", position: "전도사" }
]
};
const staffMembers = Object.values(staffMembers);
console.log(staffMembers);
결과 :
[
[
{ name: "서동욱", position: "목사" },
{ name: "김미옥", position: "사모" }
],
[
{ name: "박지성", position: "목사" }
],
[
{ name: "이영희", position: "목사" },
{ name: "최민수", position: "전도사" }
]
]
2. flat()
const flatArr = staffMembers.flat();
console.log(flatArr);
결과 :
[
{ name: "서동욱", position: "목사" },
{ name: "김미옥", position: "사모" },
{ name: "박지성", position: "목사" },
{ name: "이영희", position: "목사" },
{ name: "최민수", position: "전도사" }
]
*/}
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-6">
{activeTab === "전체"
? Object.values(staffMembers)
.flat()
.map((member, index) => (
<div key={index} className="flex flex-col items-center">
<div className="w-40 h-48 overflow-hidden mb-3 border border-gray-200 rounded shadow-sm">
<img
src={member.image}
alt={member.name}
className="w-full h-full object-cover"
/>
</div>
<h3 className="text-lg font-bold">{member.name}</h3>
<p className="text-gray-600">{member.position}</p>
</div>
))
: staffMembers[activeTab]?.map((member, index) => (
<div key={index} className="flex flex-col items-center">
<div className="w-40 h-48 overflow-hidden mb-3 border border-gray-200 rounded shadow-sm">
<img
src={member.image}
alt={member.name}
className="w-full h-full object-cover"
/>
</div>
<h3 className="text-lg font-bold">{member.name}</h3>
<p className="text-gray-600">{member.position}</p>
</div>
))}
</div>
</div>
</div>
);
}
여기는 props로 받아온 데이터를 내가 사용하기에 알맞은 데이터로 변환하고 세팅한 코드야
코드에 예시랑 문법설명 찾아가면서 주석 달아놨으니까 참고해
내일도...강릉 ~ 양양 여행 예정이라 미리 부랴부랴 썻다
이게 참 프젝하랴 블로그 쓰랴 2개 같이하니까 진도가 너무 거북이네 그래도 나중에 몇번 더 보고 이해하면서 해야 안까묵겟지??
아 생각해보니 학교 인강안들었네 콩쥐야...ㅈ댔어
노트북 챙겨가서 밤에 인강좀 들어야겠다
담에보자
'기술공부 > Next.js' 카테고리의 다른 글
Next.js기반 교회 웹사이트 제작 [14] - ReCoil 적용(실패담..) (0) | 2025.03.20 |
---|---|
Next.js기반 교회 웹사이트 제작 [13] - Hook의 종류와 공부 (0) | 2025.03.20 |
Next.js기반 교회 웹사이트 제작 [11] - prisma(CRUD 문법 공부) (0) | 2025.03.16 |
Next.js기반 교회 웹사이트 제작 [10] - prisma(Prisma client 기능 및 설치) (0) | 2025.03.16 |
Next.js기반 교회 웹사이트 제작 [9] - (간단한 문법 설명) (0) | 2025.03.16 |