# [계획서] 메신저 화면 캡처 기능

> 관련 문서: [맥락노트](./SCR[맥락]-screen-capture.md) | [체크리스트](./SCR[체크]-screen-capture.md)

## 개요

메신저 모달 헤더에 화면 캡처 버튼을 추가하여, 사용자가 드래그로 원하는 영역을 선택하면 해당 영역을 이미지로 캡처하고 메신저 메시지 입력창의 첨부 파일로 자동 추가합니다.

현재 메신저 모달은 화면 캡처 기능이 없어 별도 캡처 도구를 사용한 뒤 파일을 수동으로 첨부해야 합니다.

---

## 현재 동작

### 1. 메신저 헤더에 캡처 버튼 없음

`MessengerModal.tsx` 헤더 영역에는 닫기/최소화 버튼만 있고 캡처 관련 UI가 없음.

### 2. MessageInput의 파일 추가 기능이 외부에 노출되지 않음

`MessageInput.tsx`의 `addFiles` 함수는 컴포넌트 내부에서만 사용되며, `ref`를 통해 외부에서 호출할 수 있는 인터페이스가 없음.

### 3. 캡처 전용 컴포넌트 없음

드래그 선택 영역 표시, `modern-screenshot` 연동, 오버레이 관리 등의 캡처 로직을 처리하는 컴포넌트가 없음.

---

## 변경 후 동작

### 1. 캡처 버튼 추가

`MessengerModal.tsx` 헤더에 Scissors 아이콘 버튼이 추가됨. 클릭 시 캡처 모드로 진입.

### 2. 캡처 모드 진입

- 메신저 모달이 `display: none`으로 숨겨짐
- `ScreenCapture` 컴포넌트가 전체화면 오버레이로 표시됨 (`z-[99999]`, 반투명 어두운 배경, `crosshair` 커서)

### 3. 영역 선택

사용자가 마우스로 드래그하면 선택 영역이 파란 반투명 사각형으로 표시됨.

### 4. 캡처 및 파일 추가

`mouseup` 시 `modern-screenshot`으로 해당 DOM 영역을 캡처 → `File` 객체로 변환 → `MessageInput`의 `addFiles`를 ref로 호출하여 `pendingFiles`에 추가.

### 5. 복원

오버레이가 제거되고 메신저 모달이 다시 표시됨.

---

## 시각적 예시

### 캡처 모드 오버레이

```
┌─────────────────────────────────────────────────────────┐
│  (전체 화면, rgba(0,0,0,0.35), crosshair 커서)           │
│                                                          │
│   ┌──────────────────────────────┐                       │
│   │  (드래그 선택 영역)            │                       │
│   │  파란 반투명 rect              │                       │
│   │  border: 2px solid #3b82f6   │                       │
│   │  background: rgba(59,130,    │                       │
│   │    246, 0.2)                 │                       │
│   └──────────────────────────────┘                       │
│                                                          │
└─────────────────────────────────────────────────────────┘
```

### 헤더 버튼 위치

```
┌─ 메신저 ─────────────────────────────────── [✂] [_] [X] ┐
│                                                           │
```

---

## 아키텍처

### 데이터 흐름

```mermaid
flowchart TD
    A["사용자: 헤더 Scissors 버튼 클릭"] --> B["MessengerModal\n모달 숨김 (display:none)"]
    B --> C["ScreenCapture 오버레이 표시\nz-[99999], 반투명 배경"]
    C --> D["사용자: 마우스 드래그로 영역 선택\n파란 반투명 rect 표시"]
    D --> E["mouseup: modern-screenshot\n해당 DOM 영역 캡처"]
    E --> F["Blob → File 객체 변환"]
    F --> G["messageInputRef.current.addFiles(file)\nMessageInput pendingFiles에 추가"]
    G --> H["오버레이 제거\n메신저 모달 복원"]
```

### 컴포넌트 관계

```mermaid
graph LR
    subgraph modal ["MessengerModal.tsx"]
        H["헤더 캡처 버튼"]
        SHOW["모달 표시/숨김 제어"]
    end
    subgraph capture ["ScreenCapture.tsx (신규)"]
        OV["전체화면 오버레이"]
        SEL["드래그 선택 rect"]
        CAP["modern-screenshot 캡처"]
    end
    subgraph input ["MessageInput.tsx"]
        AF["addFiles (ref 노출)"]
        PF["pendingFiles 상태"]
    end

    H -->|"캡처 모드 진입"| SHOW
    SHOW -->|"onCapture 콜백"| OV
    CAP -->|"File 객체"| AF
    AF --> PF
```

---

## 변경 대상 파일

| 파일 | 수정 내용 | 수정 규모 |
|------|----------|----------|
| `package.json` | `modern-screenshot` 패키지 추가 | 1줄 |
| `components/messenger/MessengerModal.tsx` | 헤더에 Scissors 버튼 추가, 캡처 모드 상태 관리, 모달 숨김/복원 처리 | ~30줄 |
| `components/messenger/ScreenCapture.tsx` | **신규** - 전체화면 오버레이, 드래그 선택, modern-screenshot 캡처, File 객체 반환 | ~150줄 |
| `components/messenger/MessageInput.tsx` | `addFiles`를 `useImperativeHandle`로 외부 ref에 노출 | ~15줄 |

### 변경하지 않는 파일

- `contexts/MessengerContext.tsx` - 캡처 트리거를 Context로 공유할 필요 없음. `MessengerModal` 내부에서 `ref`로 직접 연결하는 것으로 충분
- 백엔드 전체 - 파일 첨부는 기존 메시지 전송 흐름을 그대로 사용

---

## 코드 설계

### 1. MessageInput ref 인터페이스 (MessageInput.tsx)

```typescript
export interface MessageInputHandle {
  addFiles: (files: File[]) => void;
}

const MessageInput = forwardRef<MessageInputHandle, MessageInputProps>(
  (props, ref) => {
    useImperativeHandle(ref, () => ({
      addFiles: (files: File[]) => {
        setPendingFiles((prev) => [...prev, ...files]);
      },
    }));
    // ...
  }
);
```

### 2. ScreenCapture 컴포넌트 인터페이스 (ScreenCapture.tsx)

```typescript
interface ScreenCaptureProps {
  onCapture: (file: File) => void;
  onCancel: () => void;
}
```

- `mousedown`: 시작 좌표 저장
- `mousemove`: 선택 rect 업데이트
- `mouseup`: `modern-screenshot`으로 `document.elementFromPoint` 기반 영역 캡처
- `Escape` 키: 취소

### 3. MessengerModal 캡처 모드 (MessengerModal.tsx)

```typescript
const messageInputRef = useRef<MessageInputHandle>(null);
const [isCaptureMode, setIsCaptureMode] = useState(false);

// 모달 숨김
<div style={{ display: isCaptureMode ? 'none' : undefined }}>
  {/* 기존 모달 내용 */}
  <MessageInput ref={messageInputRef} ... />
</div>

// 캡처 오버레이
{isCaptureMode && (
  <ScreenCapture
    onCapture={(file) => {
      messageInputRef.current?.addFiles([file]);
      setIsCaptureMode(false);
    }}
    onCancel={() => setIsCaptureMode(false)}
  />
)}
```

### 4. modern-screenshot 캡처 (ScreenCapture.tsx)

```typescript
import { toBlob } from 'modern-screenshot';

const handleMouseUp = async () => {
  const el = document.elementFromPoint(centerX, centerY) as HTMLElement;
  const blob = await toBlob(el, {
    width: selectionWidth,
    height: selectionHeight,
    // clip 옵션으로 선택 영역만 캡처
  });
  const file = new File([blob], `capture-${Date.now()}.png`, {
    type: 'image/png',
  });
  onCapture(file);
};
```

---

## 설계 원칙

- `MessengerContext` 변경 없음 - 캡처는 모달 내부 관심사이므로 Context 전파 불필요
- 캡처 오버레이는 `portal`로 `document.body`에 렌더링하여 기존 레이아웃 z-index 충돌 방지
- `modern-screenshot`은 DOM 기반 캡처이므로 브라우저 권한(Screen Capture API) 불필요
- `forwardRef` + `useImperativeHandle` 패턴은 프로젝트 기존 패턴과 동일하게 적용
