diff --git a/src/app/project/page.tsx b/src/app/project/page.tsx index b7b7ed9..f691bfe 100644 --- a/src/app/project/page.tsx +++ b/src/app/project/page.tsx @@ -1,11 +1,219 @@ -export default function ProjectPage() { +// [Next.js 문법] 'use client'는 이 컴포넌트가 브라우저에서 상호작용(useState, 클릭 등)을 한다는 것을 Next.js에 알림 +'use client'; + +import { useState } from 'react'; + +// --- (7주차 추가: 공통 컴포넌트 추출 - 프로젝트 리스트 아이템) --- +// [자바스크립트/React] 독립적인 UI 단위를 함수로 만든 '컴포넌트' 파트 +function ProjectRow({ + status, + position, + title, + author, + time, + comments +}: { + status: string; + position: string; + title: string; + author: string; + time: string; + comments?: number; +}) { + // [자바스크립트 문법] 변수 선언 및 조건식(비교 연산) + const isCompleted = status === '완료'; // isCompleted 안에 불리언(true/false) 값 저장 + // // 1번째 함수 (재사용 가능한 UI 조각) return ( -
-

프로젝트 모집

-

- 팀 프로젝트 모집 게시판이 준비 중입니다. 곧 더 많은 콘텐츠를 확인할 수 있어요. -

-
+
+ {/* [반응형 레이아웃 1] 모바일 카드형 (sm 미만에서 표시, sm 이상에서 hidden) */} +
+ {/* 상단: 상태 및 포지션 배지 영역 */} +
+ {/* [자바스크립트 문법] 템플릿 리터럴(` `)과 삼항 연산자를 사용한 동적 클래스 부여 */} + + {status} + + + {position} + +
+ + {/* 하단: 제목(왼쪽 정렬)과 작성자/시간(오른쪽 정렬) */} +
+
+ {title} + {/* [자바스크립트 문법] && (단락 평가): comments가 있을 때만 💬 아이콘 렌더링 */} + {comments && 💬 {comments}} +
+ + {/* 작성자 및 업로드 시간을 세로로 배치 */} +
+ {author} + {time} +
+
+
+ + {/* [반응형 레이아웃 2] 태블릿/PC 그리드형 (sm 이상에서 표시, sm 미만에서 hidden) */} +
+
+ + {status} + +
+
+ + {position} + +
+
+ {title} + {comments && 💬 {comments}} +
+
{author}
+
{time}
+
+
); } +// [Next.js 문법] export default는 이 파일을 특정 주소(URL)로 접속했을 때 보여줄 '페이지'로 지정 +export default function ProjectPage() { + + // [자바스크립트/React 문법] 상태 관리 (State) + const [currentPage, setCurrentPage] = useState(1); // 현재 페이지네이션 번호 + const [selectedPosition, setSelectedPosition] = useState('전체'); // 현재 선택된 포지션 필터 + const [selectedStatus, setSelectedStatus] = useState('모집 중'); // 현재 선택된 모집 여부 필터 + + // [자바스크립트 문법] 상수 데이터 배열 정의 + const positions = ['전체', '프론트엔드', '백엔드', '개발', '디자인', '기획', '기타']; + const totalPages = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + // --- (새로운 기능: 클릭 이벤트 핸들러) --- + // [자바스크립트 문법] 화살표 함수를 사용하여 클릭 시 상태 변경과 콘솔 기록을 동시에 수행 + const handlePositionClick = (pos: string) => { + setSelectedPosition(pos); + console.log(`선택된 포지션: ${pos}`); // 브라우저 개발자 도구 콘솔에 기록 + }; + + const handleStatusClick = (status: string) => { + setSelectedStatus(status); + console.log(`선택된 모집 상태: ${status}`); + }; + // 2번째 함수 (전체 페이지) + return ( +
+ {/* [메인 영역] pt-64: 모바일에서 길어진 헤더 높이만큼 상단 여백(Padding-Top)을 충분히 확보 */} +
+ + {/* [1] 필터 구역 */} +
+
+ + {/* 1-1. 구인 포지션 필터 (모바일에서는 세로 레이아웃) */} +
+
+ 구인 포지션 +
+
+ {/* [자바스크립트 문법] .map()을 사용하여 배열 데이터만큼 버튼 생성 */} + {positions.map((pos) => ( + + ))} +
+
+ + {/* 1-2. 모집 여부 필터 */} +
+
+ 모집 여부 +
+
+ {/* [자바스크립트 문법] 즉석에서 생성한 배열(['모집 중', '완료'])로 map 실행 */} + {['모집 중', '완료'].map((st) => ( + + ))} +
+
+
+
+ + {/* [2] 리스트 구역 */} +
+ {/* 리스트 헤더: sm(640px) 이상 화면에서만 보이도록 설정 */} +
+
여부
+
포지션
+
프로젝트 모집 글
+
작성자
+
업로드 시간
+
+ + {/* [자바스크립트/React] 사용자 정의 컴포넌트 ProjectRow에 데이터를 전달(Props) */} + + + + + + +
+ + {/* [3] 페이지네이션 */} +
+ + {totalPages.map((num) => ( + + ))} + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/study/page.tsx b/src/app/study/page.tsx index 09438d2..e49ee1c 100644 --- a/src/app/study/page.tsx +++ b/src/app/study/page.tsx @@ -1,11 +1,160 @@ -export default function StudyPage() { +// [Next.js 문법] 'use client'는 클라이언트 사이드 렌더링을 명시하는 Next.js 전용 지시어 +'use client'; + +import { useState } from 'react'; + +// 1. 카드 컴포넌트 (일반 React/JS 함수형 컴포넌트) +function StudyCard({ number }: { number: number }) { return ( -
-

스터디 모집

-

- 진행 중인 스터디 모집 공지와 신청 폼이 곧 업데이트될 예정입니다. -

-
+
+
+
+ 모집 중 + 🕑6시간 전 +
+
+

웹개발 스터디 모집합니다~

+
C++
+
+
+
+
+
+ aBCDFEFGOL +
+
👁️ 122💬 333
+
+
); } +// [Next.js 문법] export default 함수는 해당 파일의 대표 페이지가 됩니다. (App Router 라우팅) +export default function StudyPage() { //라우팅 경로에 맞게 함수명 변경 + + // [자바스크립트/React 문법] 상태 관리를 위한 Hook 사용 + const [selected, setSelected] = useState('전체'); + const [status, setStatus] = useState('모집 중'); + const [currentPage, setCurrentPage] = useState(1); + + // [자바스크립트 문법] 변수 및 배열 선언 + const categories = ['전체', 'C', 'Python', 'Java', '알고리즘', "기타"]; + const totalPages = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + // --- (7주차 추가 시작 : 콘솔로그 - 자바스크립트 로직 파트) --- + // 카테고리 클릭 시 실행될 함수 + const handleCategoryClick = (category: string) => { + setSelected(category); + console.log(`선택된 카테고리: ${category}`); // 개별적인 이벤트 로그 + }; + + // 모집 상태 클릭 시 실행될 함수 + const handleStatusClick = (statusName: string) => { + setStatus(statusName); + // 모집 중이면 true, 모집 완료면 false + const isRecruiting = statusName === '모집 중'; // 왼쪽 값과 오른쪽 값을 비교하여 결과를 무조건 불리언 + + // false(모집 완료)일 때만 콘솔에 찍기 + if (!isRecruiting) { + console.log(`모집 상태: ${isRecruiting}`); + } + }; + // --- (7주차 추가 끝 : 콘솔로그) --- + + return ( + // (7주차 수정) 전체 화면 너비를 차지하는 배경 레이어 추가 -> 사이드 검둥이 제거 +
+
+ + {/* 1번 네모: 제목 */} +
+

스터디 모집 공고

+
+ + {/* 2번 네모: 필터 구역 (7주차 수정: 반응형 레이아웃 적용) */} +
+ {/* !!!!(7주차 수정) flex-col로 세로 정렬하되, md: 768px 이상에서만 가로(row) 정렬, items-start로 왼쪽 정렬 */} + + {/* 카테고리 버튼 구역 (7주차 수정: 줄바꿈 허용) */} +
+ {/* (7주차 추가) flex-wrap을 넣어 화면이 좁아지면 버튼이 다음 줄로 넘어가게 함 */} + {/* [자바스크립트 문법] .map()을 사용하여 배열 데이터를 JSX 리스트로 변환 JSX 안의 {} 사용*/} + {categories.map((category) => ( + + ))} +
+ + {/* 모집 상태 버튼 구역 (7주차 수정: 모바일 환경에서는 숨기고 데스크톱에서만 표시) */} +
+ {/* (7주차 수정) 'hidden'으로 기본 숨김 처리, 'md:flex'로 768px 이상에서만 나타나게 함 -> 모바일 환경 우선*/} + + +
+
+ + {/* 3번 네모: 카드 그리드 */} +
{/*카드의 반응형을 담당하는 핵심 코드*/} + {/* [자바스크립트 문법] 빈 배열을 생성하여 반복 렌더링 수행 */} + {[...Array(9)].map((_, i) => ( + + ))} +
+ + {/* 4번 네모: 페이지네이션 */} +
+ {/* [자바스크립트 문법] 화살표 함수와 Math 객체를 활용한 로직 처리 */} + + + {totalPages.map((num) => ( + + ))} + + +
+
+
+ ); +} \ No newline at end of file