+ {schema.fields.map((field) => {
+ // 💡 포인트 1: 필드 이름표(e.g., 'mentor', 'topic')로 실제 데이터를 동적으로 뽑아옴
+ const fieldValue = item.data[field.fieldId];
+
+ // 💡 포인트 2: 만약 이번 글에 이 항목(e.g., 새로 추가된 mentor)이 안 적혀있거나 값 타입이 안 맞으면? -> 부드럽게 생략! (Fallback)
+ if (fieldValue === undefined || fieldValue === null) return null;
+
+ // 💡 포인트 3: 어떤 타입의 질문(field.inputType)이었느냐에 따라 알아서 알맞은 UI 스위칭
+ switch (field.inputType) {
+ case 'shortText':
+ return
{field.label}: {fieldValue}
;
+ case 'longText':
+ return
{fieldValue}
;
+ case 'imageList':
+ return (
+
+ {/* imageList는 배열이므로 map으로 순회하여 렌더링합니다. */}
+ {Array.isArray(fieldValue) && fieldValue.map((url, idx) => (
+

+ ))}
+
+ );
+ default:
+ return
{fieldValue};
+ }
+ })}
+
+ );
+ }
+ ```
+
+- **[DB 관점] NoSQL의 유연함 활용:**
+ - MongoDB는 스키마리스(Schema-less) 특성을 가집니다. 운영진이 백오피스에서 내년에 '담당 멘토(mentor)'라는 필드를 새로 추가했을 때, **기존 과거 데이터(작년 스터디 글)를 일괄 수정하거나 DB 마이그레이션을 할 필요가 전혀 없습니다.**
+ - 기존 문서들은 해당 필드가 없는 채로 얌전히 유지되고, 새로 작성되는 문서에만 `mentor` 필드가 포함된 채로 안전하게 저장됩니다.
+
+- **[백엔드 관점] Data Validation & Schema Delivery:**
+ - 백엔드(Express + Mongoose)는 지나치게 자유로운 DB 입력을 제어하기 위해 최소한의 뼈대 스키마(예: `typeKey`, `isVisible`, `data: Object`)만을 유지합니다.
+ - 백엔드의 역할은 메인 웹 접속자에게 해당 Activity의 구조(`ActivityType.fields`)와 내용(`ActivityItem`)을 가공 없이 빠르게 내려주는 API 허브 역할입니다.
+
+- **[프론트엔드 관점] 순수 렌더링 로직 (Zero-Code Modification):**
+ - 메인 프론트는 `item.data.mentor` 처럼 특정 필드 이름을 하드코딩해서 찾지 않습니다.
+ - 내년에 '담당 멘토(mentor)' 필드가 새로 추가되더라도, 아래 코드가 알아서 `fields` 배열을 돌면서 새 데이터를 그려냅니다. **프론트엔드 개발자는 코드를 단 한 줄도 추가하거나 수정할 필요가 없습니다.**
+ - 기존 과거 스터디 데이터들은 `fieldValue`가 `undefined`로 잡히므로 위 코드의 `if (fieldValue === undefined || fieldValue === null)` 방어 로직에 걸려 에러 없이 자연스럽게 넘어가며(Fallback), 과거의 모습 그대로 안전하게 유지됩니다.
+
+### 5.3. 순서(Order) 제어 방식 상세
+운영진이 특정 활동 탭(예: 스터디)이나 내부 게시물(예: 리액트 스터디 카드)의 노출 순서를 마음대로 바꿀 수 있어야 합니다. 각 파트별 역할은 다음과 같습니다.
+
+- **[DB 관점] 정렬의 기준점 (Numeric Index):**
+ - `ActivityType` (메뉴 탭)와 `ActivityItem` (게시물) 모두에 숫자형 필드인 `order` 를 필수값으로 추가합니다.
+- **[DB 관점] 토글(Toggle)을 통한 안전한 노출 제어:**
+ - `ActivityType.isActive`: 전체 탭(예: 이번 학기에 운영 전면 휴식인 '해커톤' 탭) 자체를 삭제하지 않고 메인 웹에서 임시로 숨기거나 켤 수 있습니다.
+ - `ActivityItem.isVisible`: 개별 게시물(예: 아직 내용이 미완성된 스터디 카드)의 메인 노출 여부를 '임시저장'처럼 껐다 켤 수 있게 제어합니다.
+ - 메인 웹 API는 이 값들이 `true`인 데이터만 필터링하여 내려주므로, 메인 웹에는 철저하게 검증된 콘텐츠만 안전하게 노출됩니다.
+
+- **[프론트엔드 관점] 직관적인 Drag & Drop 정렬 (백오피스 전용):**
+ - 가장 강조하고 싶은 스터디를 맨 위로 올리기 위해 운영진이 숫자를 수동으로 입력하는 불편함과 휴먼 에러를 방지합니다. 프론트엔드 라이브러리(`@hello-pangea/dnd` 등)를 활용해 마우스로 끌어다 놓는 직관적인 리스트 정렬 UI를 제공해야 합니다.
+ - 마우스 드롭 이벤트 발생 시, 프론트엔드에서 변경된 배열 인덱스를 바탕으로 새로운 `order` 항목 값을 일관되게 재설정한 뒤 백오피스의 일괄 업데이트 API(`PATCH .../order`)를 즉각 호출합니다.
+
+- **[백엔드 관점] 쿼리 정렬 최적화 (DX):**
+ - 백오피스에서 전송된 순서 변경 요청을 받아 DB에 일괄 적용(Bulk Write)합니다.
+ - 가장 중요한 점은, 메인 웹이 데이터를 요청(`GET`)할 때 **백엔드가 DB에서 데이터를 꺼내 오는 단계(`find()`)에서부터 무조건 `.sort({ order: 1 })` 처리를 거쳐 오름차순으로 정렬된 깔끔한 배열을 프론트엔드에 내려주어야 합니다.**
+ - 이렇게 하면 메인 프론트엔드는 서버가 던져준 배열을 믿고 아무 고민 없이 `map()` 만 돌리면 되므로 프론트엔드의 비즈니스 로직(정렬 연산) 부담이 완전히 사라집니다.
+
+---
+
+## 6. API 설계 (Endpoint 명세)
+
+### 6.1 백오피스 API (CMS 관리용)
+
+| Method | Path | 설명 |
+|--------|------|------|
+| `POST` | `/bo/activity-types` | 신규 활동 Type 및 Field 양식 생성 |
+| `GET` | `/bo/activity-types` | 전체 활동 Type 목록 및 스키마 조회 |
+| `PATCH` | `/bo/activity-types/:id` | 활동 Type 스키마(이름, Field 구성 등) 수정 |
+| `PATCH` | `/bo/activity-types/order` | 드래그 앤 드롭을 통한 활동 Type 순서(order) 일괄 변경 |
+| `POST` | `/bo/activities` | 특정 양식(typeKey)에 맞춘 신규 Item(게시물) 작성 |
+| `GET` | `/bo/activities` | (백오피스용) Type별 Item(게시물) 전체 목록 조회 (비공개 콘텐츠 포함) |
+| `PATCH` | `/bo/activities/:id` | Item(게시물) 내용 수정 및 공개/비공개 토글 |
+| `PATCH` | `/bo/activities/order` | Type 내 Item(게시물) 노출 순서(order) 일괄 변경 |
+| `DELETE` | `/bo/activities/:id` | Item(게시물) 삭제 |
+
+### 6.2 메인 웹 API (사용자 조회용)
+
+| Method | Path | 설명 |
+|--------|------|------|
+| `GET` | `/activities/types` | 활성화된(isActive: true) 탭 목록과 각 탭의 필드 설계도 조회 |
+| `GET` | `/activities` | `?typeKey=study` 형태로 특정 탭의 공개된(isVisible: true) 콘텐츠 목록 조회 (order 순 자동 정렬) |
+
+> **인증 정책:** 메인 웹 API는 누구나 접근 가능한 Public API이며, 백오피스(`/bo/*`) API는 관리자 권한 확인 미들웨어 (Google OAuth 등)를 반드시 거쳐야 합니다.
+
+> **💡 API 네이밍 Note:**
+> API 엔드포인트는 프론트엔드가 데이터를 다루는 관점에서의 직관성을 위해 통합 명칭인 `/activities`를 외부 모듈명으로 사용하지만, 백엔드 코어 모델에서는 이를 각각 `ActivityType`(양식) 및 `ActivityItem`(게시물) DB 모델로 정확히 매핑하여 처리합니다.
+
+---
+
+## 7. 데이터 마이그레이션(Migration) 전략 및 타임라인
+
+새로운 DB 시스템 설계가 완료되면, 기존에 하드코딩 되어있던 과거 데이터(`studyData.ts`, `seminaData.ts` 등)를 DB로 안전하게 마이그레이션해야 합니다.
+
+### 7.1 마이그레이션 방안
+과거 데이터들은 운영진이 수기로 직접 재입력하는 대신, **1회성 마이그레이션 스크립트**를 작성하여 자동화합니다.
+1. 기존 `.ts` 데이터를 파싱해서 JSON 객체 배열로 추출.
+2. 새롭게 구상한 `ActivityType` 스키마(예: shortText 타입 `topic`, dateRange 타입 `date`, imageList 타입 `images`)를 백오피스를 통해 선행 생성.
+3. Node.js (또는 단순 API 호출) 1회성 스크립트를 통해 JSON 데이터를 순회하며 `ActivityItem` DB 데이터로 일괄 생성(Bulk Insert).
+
+### 7.2 전환 타임라인 계획
+- **Phase 1:** MongoDB 뼈대 모델 설계 및 백오피스 API 구현 완료
+- **Phase 2:** 백오피스 UI (필드 추가/수정 양식 폼 디자인, 콘텐츠 작성 페이지) 구현
+- **Phase 3:** 기존 하드코딩 데이터(`*Data.ts`) 파싱 및 MongoDB 마이그레이션 스크립트 구축/테스트
+- **Phase 4:** 메인 플랫폼의 동적 렌더링(Schema-driven UI) 교체 대응 및 기존 구형 파일(`*Data.ts` 등) 코드베이스에서 완전 제거
+
+---
+
+## 8. 엣지 케이스 및 유효성 검증(Validation) 정책
+
+기본적인 데이터 제어는 백오피스단에서 이루어지지만, 다음과 같은 엣지 케이스들을 방어해야 합니다.
+
+### 8.1 필수값 누락 (Required Validation)
+- `ActivityField` 설계도 상 `required: true`인 필수 필드를 빈 칸으로 두고 콘텐츠 저장을 시도할 경우, 백엔드에서 1차적으로 Validation Error(400 Bad Request)를 반환해야 합니다.
+- (메인 웹 Fallback): DB에 이미 들어간 과거 데이터인데 현재 필수값 규칙(예: `topic` 이 나중에 필수로 변경됨)을 어긴 데이터라면, 해당 부분 렌더링을 완전히 생략(`if (!fieldValue) return null;`)하여 흰 화면이 뜨는 치명적 에러를 차단합니다.
+
+### 8.2 이미지/파일 업로드 제한
+- Presigned URL을 요청할 때 업로드할 파일의 MIME Type(예: `image/jpeg`, `image/png`)과 용량(예: 장당 최대 5MB)을 백엔드에서 미리 검증하여, 조건에 맞지 않을 시 URL 티켓 발급 자체를 거부합니다.
+
+### 8.3 드래그 앤 드롭 동기화 실패 (Rollback)
+- 순서(order) 변경 후 `PATCH` API 호출이 네트워크 에러 등으로 실패했을 경우, 프론트엔드는 즉각적으로 적용해두었던 낙관적 업데이트(Optimistic Update) 배열을 이전 상태로 롤백(Rollback)한 뒤 유저에게 토스트 메세지로 실패 원인을 고지해야 합니다.
+
+---
+
+## 9. 개발 단계별 Action Plan (협업 포인트)
+
+1. **[공통 기반 마련]** (⭐ 3, 4번 기능 담당자와 협업 필수)
+ - CMS 기초 뼈대(Dynamic Schema)를 작성합니다. 백엔드의 Mongoose 모델(Schema)과 데이터 저장 구조를 3팀이 모여 확정 짓습니다.
+ - 백오피스에서 사용할 공통 '텍스트 입력창', '날짜 선택창', '이미지 업로드창(Presigned URL 적용)' 리액트 컴포넌트를 설계하여 UI 라이브러리처럼 구축합니다.
+2. **[Backoffice 뷰 개발]** (재민 담당)
+ - Activity 전용 Type 설계 페이지 및 Content 리스트/작성/DnD 정렬 페이지 완성.
+3. **[Main 뷰 리팩토링 및 API 연동]** (재민 담당)
+ - 기존 `src/lib/activity/*Data.ts` 폴더 완전 제거.
+ - 메인 백엔드(Public API) 호출 후 동적으로 Navbar 탭과 Activity 카드가 UI 충돌 없이 렌더링되는 로직(`Schema-driven UI`) 구현.