Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f2b0d0d
🏷️ 노드의 스타일 타입 구조 변경
y-minion Nov 30, 2025
7863180
🏷️ 타입 이름 수정, 스타일 인터페이스 partial수정
y-minion Nov 30, 2025
79a2c70
📝 monorepo가이드라인 작성
y-minion Dec 1, 2025
39fbb8a
🏷️ 노드의 위치 이동을 고려한 타입 수정
y-minion Dec 1, 2025
3ad3687
✨상위 노드의 스타일 정의할때 사용되는 processNodeStyles함수 구현
y-minion Dec 1, 2025
ab314d7
✨ 노드를 감싸는 wrapper 컴포넌트 구현
y-minion Dec 1, 2025
85a6ac4
✨ 히어로 노드 렌더러 컴포넌트 구현
y-minion Dec 1, 2025
5100dd8
단순 파일 이름 변경
y-minion Dec 1, 2025
6e21644
📝 단순 주석 추가
y-minion Dec 1, 2025
c2ada18
✨ Text 노드 담당 노드 렌더러 구현
y-minion Dec 1, 2025
fc14d98
🏷️ text 전용 props타입 추가 정의
y-minion Dec 1, 2025
9ae00ed
♻️ 노드 렌더러 수정
y-minion Dec 1, 2025
f35e054
🏷️ Action 데이터 설계
y-minion Dec 3, 2025
26fafe7
✨ 노드 렌더러들이 만든 노드 컴포넌트들이 동적으로 동작할 수 있도록 도와주는 컨텍스트 구현
y-minion Dec 3, 2025
2f234fd
✨ 버튼의 액션들을 js코드로 변환해주는 훅 구현
y-minion Dec 4, 2025
46fa5a8
🏷️ 모달노드 전용 props,style 타입 추가 완료
y-minion Dec 4, 2025
76b7dbb
✨ 모달 노드 렌더러 컴포넌트 구현 완료
y-minion Dec 4, 2025
e4762e2
🏷️ 모달 노드 렌더러 컴포넌트 props 타입 설정
y-minion Dec 4, 2025
28a027b
🏷️ 액션 관련 타입 수정
y-minion Dec 4, 2025
ebd26bf
🏷️ 타입 참조 경로 수정
y-minion Dec 4, 2025
ae70f0b
✨ 버튼 노드 렌더러 컴포넌트 구현 완료
y-minion Dec 4, 2025
7293c80
Nago 위한 협업 가이드 문서 작성
y-minion Dec 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions apps/editor/WORK_DIVISION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# WebCreatorX - 팀 업무 분담 가이드

이 문서는 프로젝트의 효율적인 개발을 위해 팀원 간의 업무 영역과 협업 포인트를 정의합니다.
서로의 작업 영역을 침범하지 않으면서, 공통 상태(Store)와 데이터 구조를 기반으로 유기적으로 연결되도록 설계되었습니다.

---

## 0. 들어가기에 앞서
- 프로젝트 구조가 모노레포로 변경되었습니다! monorepoGuide.md 를 꼭 먼저 읽어주세요!

## 1. 프로젝트 개요 및 아키텍처
**WebCreatorX**는 노드 기반의 웹사이트 빌더입니다. 사용자는 좌측 사이드바에서 컴포넌트를 추가하고, 중앙 캔버스에서 확인하며, 우측 사이드바에서 속성을 편집합니다.

### 핵심 데이터 구조 (`WcxNode`)
모든 컴포넌트(Hero, Image, Text 등)는 `WcxNode`라는 공통 타입을 가집니다.
- **정의 위치**: `packages/ui/src/types/nodes.ts`
- **핵심 필드**:
- `id`: 고유 식별자 (UUID)
- `type`: 컴포넌트 종류 (Hero, Image, Button 등)
- `props`: 해당 컴포넌트의 데이터 (텍스트 내용, 이미지 URL 등)
- `style`: 스타일 정보

---

## 2. 업무 분담 (R&R)

### 🧑‍💻 Milo (Core & Renderer)
- **책임 영역**: 에디터 중앙 캔버스, 노드 렌더링, 전역 상태(Store) 설계, 서버 통신
- **주요 작업**:
1. **Node Renderer**: 서버에서 받아온 `nodes` 데이터를 순회하며 실제 화면에 그립니다.
2. **Interaction**: 캔버스 상의 요소를 클릭했을 때, 해당 노드의 `id`를 `selectedId` 상태로 업데이트합니다.
3. **Drag & Drop (추후)**: 캔버스 내에서의 위치 이동을 담당합니다. (DND 라이브러리 사용 예정)

### 🧑‍💻 Nago (Sidebars & Controls)
- **책임 영역**: 좌측 사이드바(생성), 우측 사이드바(편집), UI 컨트롤
- **주요 작업**:
1. **좌측 사이드바 (Node Creator)**: 사용 가능한 컴포넌트 목록을 보여주고, 클릭 시 새로운 노드를 생성하여 캔버스에 추가합니다.
2. **우측 사이드바 (Property Editor)**: 현재 선택된(`selectedId`) 노드의 정보를 보여주고, 사용자가 값을 수정하면 이를 실시간으로 반영합니다.

---

## 3. 협업 포인트: 전역 상태 (Zustand Store)

두 팀원의 작업은 **Zustand Store**를 통해 만납니다. 팀원은 아래 Store Hook이 존재한다고 가정하고 개발을 진행하면 됩니다.

```typescript
// apps/editor/src/store/useEditorStore.ts (예시 구조)

interface EditorState {
nodes: Record<string, WcxNode>; // 전체 노드 데이터 (ID를 키로 사용)
selectedId: string | null; // 현재 선택된 노드 ID

// Actions
addNode: (node: WcxNode) => void;
updateNode: (id: string, partialNode: Partial<WcxNode>) => void;
selectNode: (id: string | null) => void;
}
```

---

## 4. Nago 합류 상세 가이드

### ✅ Task 1: 좌측 사이드바 - 노드 생성
**목표**: 사용자가 새로운 컴포넌트를 추가할 수 있는 패널을 만듭니다.

**구현 내용**:
1. `packages/ui/src/types/nodes.ts`에 정의된 모든 노드 타입(Hero, Text, Image 등)을 버튼 형태로 나열합니다.(완자가 만든 피그마 기획서 참고)
2. 버튼 클릭 시, 해당 타입에 맞는 **기본 노드 객체**를 생성해야 합니다.
- 예: Text 노드 생성 시 `id: uuid()`, `type: 'Text'`, `props: { text: 'New Text', level: 'h1' }` 등의 초기값을 가진 객체 생성.
3. 생성된 객체를 `useEditorStore`의 `addNode` 함수를 통해 상태에 추가합니다.

**참고**: UI 디자인은 완자가 만든 피그마 기획서를 참고하여 구현해주세요.

### ✅ Task 2: 우측 사이드바 - 속성 편집 (Manifest)
**목표**: 선택된 노드의 세부 내용을 수정하는 패널을 만듭니다.

**구현 내용**:
1. `useEditorStore`에서 `selectedId`와 `nodes`를 구독합니다.
2. **조건부 렌더링**:
- `selectedId`가 `null`이면: "요소를 선택해주세요" 같은 안내 메시지 표시.
- `selectedId`가 있으면: `nodes[selectedId]`를 가져와서 편집 UI 표시.
3. **Switch Case 분기**:
- 선택된 노드의 `type`에 따라 다른 컴포넌트를 렌더링해야 합니다.
- `Hero` 타입 -> `HeroControl` 컴포넌트 (Heading 입력, 이미지 업로드, 버튼 텍스트 입력창 등)
- `Image` 타입 -> `ImageControl` 컴포넌트 (URL 입력, Alt 입력창 등)
4. **데이터 업데이트**:
- 입력창의 값이 변할 때마다 `updateNode(selectedId, { props: { ...newProps } })`를 호출하여 실시간으로 반영합니다.

### 💡 팁
- **컴포넌트 재사용**: 각 속성 편집기(Input, Select, Toggle 등)는 `RightSidebar` 내부에서만 쓰이지 않고 나중에도 쓰일 수 있으니, 작고 재사용 가능한 'Control Component'로 쪼개서 개발하는 것이 좋습니다.
- **타입 안전성**: `packages/ui`에 있는 `WcxNode`, `HeroNode` 등의 타입을 적극 활용하여, 잘못된 속성을 수정하는 일을 방지하세요.

### 중요 사항!
- 위에서 언급한 기획들은 대략적으로 서술한 것입니다. 실제로 구현에 필요한 자세한 정보들은 피그마의 기획서를 절대적으로 따라야합니다.
- 추가로 궁금한 점이 있으면 디스코드로 언제든지 질문해주세요!
---

## 5. 폴더 구조 제안 (apps/editor/src)
```
src/
├── components/
│ ├── editor/ # (Milo) 캔버스, 렌더러
│ │ ├── Canvas.tsx
│ │ └── NodeRenderer.tsx
│ │
│ ├── sidebar/ # (Nago) 사이드바
│ │ ├── LeftSidebar.tsx
│ │ ├── RightSidebar.tsx
│ │ └── controls/ # (Shared) 속성 편집용 작은 컴포넌트들
│ │ ├── TextInput.tsx
│ │ └── ColorPicker.tsx
│ │
│ └── layout/ # 전체 레이아웃 (Header, Main, Sidebars 배치)
│ └── EditorLayout.tsx
└── store/
└── useEditorStore.ts # 전역 상태
```
126 changes: 126 additions & 0 deletions monorepoGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# WebCreatorX Monorepo 가이드라인

이 프로젝트는 확장성과 코드 재사용성을 높이기 위해 **Monorepo(모노레포)** 구조로 전환되었습니다.
**Turborepo**와 **pnpm workspaces**를 기반으로 구성되어 있으며, 여러 애플리케이션과 공유 패키지를 하나의 저장소에서 효율적으로 관리합니다.

팀원들이 새로운 구조에 빠르게 적응할 수 있도록 가이드를 제공하겠습니다.

---

## 🏗️ 기술 스택 및 도구

- **Monorepo Tool**: [Turborepo](https://turbo.build/) (빌드 시스템 및 태스크 러너)
- **Package Manager**: [pnpm](https://pnpm.io/) (Workspaces 기능 사용)
- **Framework**: Next.js 15 (App Router)
- **Language**: TypeScript

---

## 📂 디렉토리 구조 (Directory Structure)

기존 단일 레포지토리 구조와 달리, 프로젝트는 크게 `apps`와 `packages`로 나뉩니다.

```
web-creator-x/
├── apps/ # ⭐️ 배포 가능한 애플리케이션들이 위치합니다.
│ ├── editor/ # 메인 에디터 애플리케이션 (Next.js)
│ └── user-template/ # 사용자 템플릿 관련 애플리케이션
├── packages/ # 🛠️ 여러 앱에서 공유하는 라이브러리/패키지들이 위치합니다.
│ └── ui/ # 공통 UI 컴포넌트 (@repo/ui)
├── package.json # 루트 설정 (Workspaces 정의)
├── pnpm-workspace.yaml # pnpm 워크스페이스 설정
└── turbo.json # Turborepo 파이프라인 설정
```

### 💡 주요 변경 사항 (Migration Notes)

| ⚠️ 기존 (Single Repo) | ♻️ 변경 후 (Monorepo) | 설명 |
| --------------------------------------- | ----------------------------------- | --------------------------------------------------------------------- |
| `/src` | `/apps/editor/src` | 메인 앱 코드는 `apps/editor`로 이동했습니다. |
| `/src/components` | `/packages/ui/src` | 재사용 가능한 컴포넌트는 `packages/ui`로 분리되었습니다. |
| `import { Button } from '@/components'` | `import { Button } from '@repo/ui'` | 공통 컴포넌트는 패키지명(`@repo/ui`)으로 import 합니다. |
| `npm run dev` | `pnpm dev` | 루트에서 실행하면 모든 앱을 동시에 실행하거나, 필터링하여 실행합니다. |

---

## 🚀 시작하기 (Getting Started)

### 1. 필수 요구사항

- Node.js (LTS 버전 권장)
- **pnpm** (필수): `npm install -g pnpm`

### 2. 설치

프로젝트 루트에서 의존성을 설치합니다. 모든 앱과 패키지의 의존성이 한 번에 설치됩니다.

```bash
pnpm install
```

### 3. 개발 서버 실행

루트에서 다음 명령어를 실행하면 `apps` 내의 모든 애플리케이션이 동시에 실행됩니다.

```bash
pnpm dev
```

특정 앱만 실행하고 싶다면 `--filter` 옵션을 사용하세요.

```bash
# editor 앱만 실행
pnpm --filter editor dev
```

### 4. 빌드 (Build)

전체 프로젝트를 빌드합니다. Turborepo의 캐싱 기능을 통해 변경된 부분만 빌드되어 속도가 빠릅니다.

```bash
pnpm build
```

---

## 🛠️ 개발 워크플로우 (Development Workflow)

### 패키지 추가하기 (Adding Dependencies)

🚨 특정 앱이나 패키지에 라이브러리를 설치할 때는 `--filter`를 사용해야 합니다.

**예시: `editor` 앱에 `axios` 설치**

```bash
pnpm add axios --filter editor
```

**예시: `ui` 패키지에 `clsx` 설치**

```bash
pnpm add clsx --filter @repo/ui
```

### 공통 컴포넌트 작업 (Working with Shared UI)

`packages/ui`에 있는 컴포넌트를 수정하면, 이를 사용하는 모든 앱(`editor` 등)에 즉시 반영됩니다.
새로운 공통 컴포넌트를 만들 때는 다음 규칙을 따라주세요:

1. `packages/ui/src/components`에 컴포넌트 파일 생성
2. `packages/ui/package.json`의 `exports` 설정 확인 (필요 시)
3. 앱에서 `import { ... } from '@repo/ui'`로 사용

### 새로운 앱 추가하기

`apps/` 폴더 안에 새로운 Next.js 프로젝트 등을 생성하면 자동으로 워크스페이스에 포함됩니다.
`package.json`의 이름을 고유하게 설정해주세요.

---

## ❓ 자주 묻는 질문 (FAQ)

**Q. 왜 `npm` 대신 `pnpm`을 쓰나요?**
A. `pnpm`은 디스크 공간을 효율적으로 사용하고 설치 속도가 매우 빠릅니다. 또한 모노레포의 워크스페이스 기능을 완벽하게 지원합니다.

**Q. `turbo.json`은 무엇인가요?**
A. `build`, `dev`, `lint` 등의 스크립트 실행 순서와 캐싱 정책을 정의하는 파일입니다. 예를 들어, `build` 작업은 의존성 패키지가 먼저 빌드되어야 함을 설정할 수 있습니다.
43 changes: 43 additions & 0 deletions packages/ui/src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useBuilderMode } from "context/builderMode";
import { useActionHandler } from "hooks/useActionHandler";
import { ButtonNode, NodeComponentProps } from "types";
import processNodeStyles from "utils/processNodeStyles";

export default function Button({
node,
props,
style,
}: NodeComponentProps<ButtonNode>) {
const { mode } = useBuilderMode();
const { text, action } = props;

//스타일 변환(className은 제외, 오직 CSS속성만)
const nodeStyleObj = processNodeStyles(style);

//액션 함수 생성
const excuteAction = useActionHandler(action);

//클릭 핸들러
function clickHandler(e: React.MouseEvent) {
//에디터 모드에서는 액션 실행 x
if (mode === "editor") {
e.preventDefault();
return;
}

//라이브 모드: 실제 액션 실행
excuteAction();
}

return (
<button
type="button"
data-component-id={node.id}
style={nodeStyleObj.root}
className={`${style.root?.className} ${mode === "editor" ? "cursor-default" : "cursor-pointer"} flex h-full w-full items-center justify-center transition-all active:scale-95`}
onClick={clickHandler} //이벤트 연결
>
<span style={nodeStyleObj.text}>{text}</span>
</button>
);
}
13 changes: 9 additions & 4 deletions packages/ui/src/components/Heading.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import processNodeStyles from "utils/processNodeStyles";
import { NodeComponentProps } from "../types";
import { HeadingNode } from "../types/nodes";
import applyStyles from "../utils/applyStyles";

export default function HeadingComponent({
node,
Expand All @@ -9,12 +9,17 @@ export default function HeadingComponent({
children,
}: NodeComponentProps<HeadingNode>) {
const { text, level = "h2" } = props;
const inlineStyle = applyStyles(style);
const nodeStyleObj = processNodeStyles(style);

const Tag = level;

return (
<div data-component-id={node.id} className={style.className}>
<Tag style={inlineStyle}>{text}</Tag>
<div
data-component-id={node.id}
className={`${style.root?.className} flex h-full w-full items-center justify-center`}
style={{ ...nodeStyleObj.root, width: "100%", height: "100%" }}
>
<Tag style={nodeStyleObj.heading}>{text}</Tag>
{/* 컨테이너일 경우 이곳에 children이 렌더링 되야 한다. */}
{children}
</div>
Expand Down
Loading