# Naver SE Editor — Quotation Component API Specification

**Capture Date:** 2026-04-07
**Capture Method:** Playwright interception of `RabbitAutoSaveWrite.naver` POST requests during live editor session
**Author:** 루(Lugh), Backend Developer

---

## 1. Overview

The Naver Smart Editor (SE) represents blockquote/인용구 content as a `quotation` component within the `documentModel.document.components` array. All quotation variants share a single `@ctype: "quotation"` identifier and are differentiated by the `layout` field.

This document covers the JSON structure, all 6 available styles, captured real-world examples for the 3 BlogAuto target styles, and the HTML-to-component mapping convention used in BlogAuto.

---

## 2. API Endpoint

| Property | Value |
|----------|-------|
| URL | `https://blog.naver.com/RabbitAutoSaveWrite.naver` |
| Method | POST |
| Content-Type | `application/x-www-form-urlencoded` |

### Request Body Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `blogId` | string | Naver blog ID |
| `documentModel` | string (JSON) | Full document model as JSON string |
| `mediaResources` | string | Media resource metadata |
| `populationParams` | string | Populate parameters |

### documentModel Structure

```
documentModel.document.components[]  →  array containing all editor components
```

Quotation components are embedded directly in the `components` array alongside text, image, and other component types.

---

## 3. Component JSON Structure

### 3.1 Top-Level Schema

```json
{
  "id": "SE-{uuid}",
  "layout": "{style}",
  "value": [/* paragraph array */] | null,
  "source": null | "string",
  "align": "justify",
  "@ctype": "quotation"
}
```

### 3.2 Field Definitions

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | Unique component ID in `SE-{uuid v4}` format |
| `layout` | string | Yes | Visual style selector (see Section 4) |
| `value` | array \| null | Yes | Paragraph array containing text content; `null` for empty quotation |
| `source` | string \| null | No | Attribution/출처 text; `null` when empty |
| `align` | string | Yes | Text alignment; defaults to `"justify"` |
| `@ctype` | string | Yes | Always `"quotation"` |

### 3.3 Paragraph Structure (inside `value`)

```json
{
  "id": "SE-{uuid}",
  "nodes": [
    {
      "id": "SE-{uuid}",
      "value": "텍스트 내용",
      "@ctype": "textNode"
    }
  ],
  "@ctype": "paragraph"
}
```

**Important:** Quotation paragraphs do NOT include a `style` sub-object. This differs from text components, which carry `paragraphStyle` on each paragraph and `nodeStyle` on each text node.

---

## 4. All 6 Available Styles

| Style Name (KO) | Layout Value | Visual Description |
|-----------------|-------------|-------------------|
| 인용구 1 | `default` | 기본 인용구 |
| 인용구 2 | `quotation_line` | 세로줄 인용구 — 왼쪽 굵은 세로선 |
| 인용구 3 | `quotation_bubble` | 말풍선 인용구 |
| 인용구 4 | `quotation_underline` | 밑줄 인용구 |
| 인용구 5 | `quotation_postit` | 포스트잇/회색박스 인용구 — 접힌 메모지 느낌 |
| 인용구 6 | `quotation_corner` | 꺾쇠 인용구 — 「 」 코너 장식 |

### BlogAuto Target Styles (3 of 6)

| Style Name | Layout Value |
|------------|-------------|
| 세로줄 | `quotation_line` |
| 회색박스 | `quotation_postit` |
| 꺾쇠 | `quotation_corner` |

---

## 5. Captured JSON Examples

### 5.1 세로줄 (`quotation_line`)

Left vertical bar decoration style.

```json
{
  "id": "SE-ddef9c0d-aec2-4a0d-a8f2-eb50f7217b62",
  "layout": "quotation_line",
  "value": [
    {
      "id": "SE-e18722d7-e10c-4656-a3ee-6fddc1783f51",
      "nodes": [
        {
          "id": "SE-b36acbaa-86eb-4f86-8113-5155448c86c7",
          "value": "세로줄 인용구 테스트 텍스트입니다",
          "@ctype": "textNode"
        }
      ],
      "@ctype": "paragraph"
    }
  ],
  "source": null,
  "align": "justify",
  "@ctype": "quotation"
}
```

### 5.2 회색박스 (`quotation_postit`)

Gray box / sticky note style.

```json
{
  "id": "SE-05dc4de5-cf70-4580-88c2-cd6618e69d35",
  "layout": "quotation_postit",
  "value": [
    {
      "id": "SE-b053970e-acee-4258-b824-ca704b839615",
      "nodes": [
        {
          "id": "SE-b6b5a456-366e-4c60-b629-700eb291395b",
          "value": "회색박스 인용구 테스트 텍스트입니다",
          "@ctype": "textNode"
        }
      ],
      "@ctype": "paragraph"
    }
  ],
  "source": null,
  "align": "justify",
  "@ctype": "quotation"
}
```

### 5.3 꺾쇠 (`quotation_corner`)

Corner bracket decoration style (「 」).

```json
{
  "id": "SE-e5e2edb7-99e7-40f9-8de6-6e132ec4f920",
  "layout": "quotation_corner",
  "value": [
    {
      "id": "SE-57a92703-d54a-49ff-88cc-9ea9d03fc74a",
      "nodes": [
        {
          "id": "SE-d8eb5a9b-f34f-4476-883c-6b88ac3cf51f",
          "value": "꺾쇠 인용구 테스트 텍스트입니다",
          "@ctype": "textNode"
        }
      ],
      "@ctype": "paragraph"
    }
  ],
  "source": null,
  "align": "justify",
  "@ctype": "quotation"
}
```

---

## 6. Key Structural Observations

1. **No style sub-objects in quotation paragraphs.** Unlike text components (which carry `paragraphStyle` per paragraph and `nodeStyle` per text node), quotation paragraphs and their text nodes carry no `style` objects. Styling is entirely determined by the `layout` field at the component level.

2. **`value: null` for empty quotations.** When a quotation component has no content, `value` is `null` rather than an empty array.

3. **`source` field for attribution.** The `source` field holds the 출처(attribution) string. It is `null` when no source is provided.

4. **`align` defaults to `"justify"`.** All captured examples use `"justify"`. Other standard alignment values (`"left"`, `"center"`, `"right"`) are expected to be valid but were not captured in these samples.

5. **Single `@ctype` for all styles.** There is no per-style `@ctype`. All quotation components share `"@ctype": "quotation"`. Layout is the only discriminator.

6. **UUID format for IDs.** All `id` fields follow the `SE-{uuid v4}` format. IDs must be unique across the entire document.

7. **Paragraph structure is shared with text components.** The internal paragraph/node nesting (`@ctype: "paragraph"` → `nodes[]` → `@ctype: "textNode"`) mirrors the text component structure, minus the style objects.

---

## 7. HTML Convention for BlogAuto Integration

When converting HTML to SE editor components, `<blockquote>` elements are mapped to quotation components using the following class-to-layout convention:

| HTML | Layout Value |
|------|-------------|
| `<blockquote>` (no class) | `quotation_line` |
| `<blockquote class="line">` | `quotation_line` |
| `<blockquote class="box">` | `quotation_postit` |
| `<blockquote class="bracket">` | `quotation_corner` |
| `<blockquote class="bubble">` | `quotation_bubble` |
| `<blockquote class="underline">` | `quotation_underline` |
| `<blockquote class="postit">` | `quotation_postit` |
| `<blockquote class="default">` | `default` |

**Default fallback:** A bare `<blockquote>` with no class maps to `quotation_line`.

---

## 8. Implementation Reference for BlogAuto

### 8.1 Python — `_quotation_component()`

Location: `/home/jay/projects/BlogAuto/publisher/naver_blog.py`

```python
def _quotation_component(
    text: str,
    style: str = "quotation_line",  # quotation_line | quotation_postit | quotation_corner | default | quotation_bubble | quotation_underline
    source: str | None = None,
) -> dict:
    return {
        "id": _se_id(),
        "layout": style,
        "value": [
            {
                "id": _se_id(),
                "nodes": [
                    {
                        "id": _se_id(),
                        "value": text,
                        "@ctype": "textNode",
                    }
                ],
                "@ctype": "paragraph",
            }
        ],
        "source": source,
        "align": "justify",
        "@ctype": "quotation",
    }
```

### 8.2 JavaScript — Layout Resolver

```javascript
const BLOCKQUOTE_LAYOUT_MAP = {
  line:      "quotation_line",
  box:       "quotation_postit",
  bracket:   "quotation_corner",
  bubble:    "quotation_bubble",
  underline: "quotation_underline",
  postit:    "quotation_postit",
  default:   "default",
};

function resolveLayout(classList) {
  for (const cls of classList) {
    if (BLOCKQUOTE_LAYOUT_MAP[cls]) return BLOCKQUOTE_LAYOUT_MAP[cls];
  }
  return "quotation_line"; // fallback for bare <blockquote>
}
```

---

*Spec captured via Playwright live session interception on 2026-04-07.*
