회원가입 | 고객센터 |
DESIGNONEX
dxcms.kr
로그인 회원가입
고객센터
DXCMS

스케줄 게시판을 만든 프롬프트 스킬입니다. 좀 더 진화했습니다.

D DX
2026.05.24 16:58(수정됨) 4 0 3
다만, 만든 후, 세부작업은 에러를 찾으면서 작업해야 합니다.
data/error.log 에서 확인 할 수 있습니다.
 
name: dxcms-skin-dev
description: >
  DXCMS v8.1.0 게시판 스킨 개발 전문 스킬.
  스킨 파일 구조, DB 스키마, API 패턴, 여분 필드, 디자인 패턴을 포함한다.
  달력, 갤러리, Q&A, 쇼핑, 포트폴리오 등 다양한 스킨 유형에 적용 가능.
  list.php / _list_rows.php / view.php / write.php 작성 시 반드시 이 스킬을 먼저 읽을 것.
license: Proprietary
이 부분에 description에 내가 만들고 싶은 내용을 입력하세요.
최대한 내가 만들고 싶은 내용을 입력하는 것이 좋습니다.
다음은 프롬프트입니다.
클로드 Sonnet4.6으로 테스트 해봤습니다.
참고로 버전이 높다고 좋은 것은 아닙니다. 프롬프트 어떻게 작성하느냐에 결과물은 다 다릅니다.
위에 내용과 아래 프롬프트를 같이 넣어주면 됩니다.
## 1. 개발 환경

- **PHP 5.6 ~ 8.x** — 클로저(function(){}) 사용 금지, `array()` 표기 필수
- **MySQL 5.6+ / MariaDB 10.1+** — `ADD COLUMN IF NOT EXISTS` 미지원
- **스킨 위치**: `boards/skins/{스킨명}/`
- **필수 파일**: `skin.json` (없으면 스킨 인식 안 됨)
- **기본 전략**: `themes/default/board/basic/` 원본을 복사 후 최소한만 수정

### skin.json 필수 형식
```json
{
  "name": "스킨키",
  "label": "관리자 드롭다운 표시명",
  "version": "1.0.0",
  "author": "작성자",
  "description": "설명",
  "actions": ["list", "view", "write"],
  "theme": "default"
}
```

---

## 2. 파일별 역할 — 절대 혼동 금지

| 파일 | 역할 | 주의 |
|---|---|---|
| `list.php` | 헤더·검색·정렬·글쓰기 버튼 UI | POST 처리 코드 절대 금지 |
| `_list_rows.php` | 공지행 + 일반글행 실제 렌더링 | list.php와 view.php 양쪽에서 include됨 |
| `view.php` | 게시글 상세 + 댓글 렌더링 | POST 처리는 파일 맨 위에서만 |
| `write.php` | 글쓰기/수정 폼 | 여분 필드 renderWriteForm 추가 정도 |

### list.php → _list_rows.php include 패턴
```php
// list.php 하단
$_ltNotices        = !empty($notices) ? $notices : array();
$_ltPosts          = !empty($posts)   ? $posts   : array();
$_ltTotal          = (int)$total;
$_ltPage           = (int)$page;
$_ltPerPage        = (int)$perPage;
$_ltSearch         = $search;
$_ltSf             = $searchField;
$_ltCat            = isset($currentCategory) ? $currentCategory : '';
$_ltTag            = isset($tag) ? $tag : '';
$_ltIsAdm          = $_isAdm;
$_ltCurPostId      = 0;
$_ltPaginationBase = '';
$_ltShowPagination = false;
include __DIR__ . '/_list_rows.php';
```

---

## 3. ⚠️ DB 스키마 — 반드시 숙지

### 3-1. dx_posts (게시글)
```
id            BIGINT(20) UNSIGNED  -- 밀리초 타임스탬프 ID (BIGINT!)
board_id      INT(11) UNSIGNED     -- boards.id
parent_id     BIGINT(20) UNSIGNED  -- 기본 0
member_id     INT(11) UNSIGNED     -- 0=비회원
author_name   VARCHAR(100)         -- 비회원 작성자명
title         VARCHAR(191)
content       LONGTEXT
category      VARCHAR(100)
category_slug VARCHAR(100)
tags          VARCHAR(191)
thumbnail     VARCHAR(191)
view_count    INT(11)
like_count    INT(11)
comment_count INT(11)
is_notice     TINYINT(1)
is_secret     TINYINT(1)
status        TINYINT(1)           -- 1=정상
popular_score INT(11)
created_at    DATETIME
updated_at    DATETIME
```
**⚠️ `member_name` 컬럼 없음** — 작성자명은 `dx_members.name` JOIN 필요:
```sql
LEFT JOIN `dx_members` m ON m.id = p.member_id
-- m.name AS member_name
```

### 3-2. dx_members (회원)
```
id          INT(11) UNSIGNED AUTO_INCREMENT  -- members는 INT, posts는 BIGINT!
login_id    VARCHAR(50)
name        VARCHAR(100)   -- 닉네임/이름
email       VARCHAR(191)
role        ENUM('member','manager','admin')
status      TINYINT(1)
profile_img VARCHAR(500)
point       INT(11)
exp         INT(11)
level       SMALLINT(5)
```

### 3-3. dx_comments (댓글)
```
id          BIGINT(20) UNSIGNED   -- 밀리초 타임스탬프
post_id     BIGINT(20) UNSIGNED
parent_id   BIGINT(20) UNSIGNED   -- 0=최상위
member_id   INT(11) UNSIGNED
author_name VARCHAR(100)
content     TEXT
depth       TINYINT(3)            -- 댓글 깊이
status      TINYINT(1)
created_at  DATETIME
```

### 3-4. dx_post_meta (여분 필드 값)
```
id        BIGINT AUTO_INCREMENT
post_id   BIGINT(20) UNSIGNED
field_key VARCHAR(64)
value     TEXT
UNIQUE KEY (post_id, field_key)
```

### 3-5. dx_board_fields (여분 필드 정의)
```
id            BIGINT AUTO_INCREMENT
board_id      INT(11)
field_key     VARCHAR(64)
field_label   VARCHAR(128)
field_type    VARCHAR(32)    -- text/textarea/number/select/checkbox/datetime/...
field_options TEXT           -- select 옵션 등
is_required   TINYINT(1)
is_list       TINYINT(1)     -- 목록에 표시
is_view       TINYINT(1)     -- 뷰에 자동 렌더링
sort_order    SMALLINT(5)
status        TINYINT(1)
```

### 3-6. dx_likes (좋아요)
```
id          INT AUTO_INCREMENT
target_type ENUM('post','comment')
target_id   BIGINT(20) UNSIGNED
member_id   INT(11)
ip          VARCHAR(45)
created_at  DATETIME
UNIQUE KEY (target_type, target_id, member_id)
```
**커스텀 활용**: `target_type`을 임의 문자열로 확장 가능 (`'qa_eval'`, `'apply'` 등)

---

## 4. ⚠️ BIGINT ID 처리 — 가장 중요한 규칙

```php
// ❌ 절대 금지 — 32bit PHP에서 오버플로우
$postId = (int)$_POST['post_id'];
$postId = (int)$post['id'];

// ✅ 올바른 방법 — 항상 string
$postId = dx_post('post_id', '0', 'bigint');  // string 반환
$postId = isset($_POST['post_id']) ? trim($_POST['post_id']) : '';

// ✅ DB 파라미터도 반드시 string 캐스팅
$db->rows("SELECT ... WHERE id = ?", array((string)$postId));
$db->rows("SELECT ... WHERE id = ? AND board_id = ?",
    array((string)$postId, (string)$board['id']));

// ✅ 비교 시
if ((string)$post['member_id'] !== (string)$uid) { ... }
```

---

## 5. DB 쿼리 패턴

### 기본 조회
```php
$db = Database::getInstance();

// 복수 행
$rows = $db->rows("SELECT ... WHERE board_id = ?", array((string)$board['id']));

// 단건 — row() 메서드가 없을 수 있음, rows()[0] 사용
$result = $db->rows("SELECT ... WHERE id = ? LIMIT 1", array((string)$id));
$row = !empty($result) ? $result[0] : null;

// 단일 값
$count = $db->value("SELECT COUNT(*) FROM ...", array());

// INSERT/UPDATE/DELETE
$db->query("UPDATE ... SET ... WHERE id = ?", array((string)$id));
```

### ⚠️ 복잡한 JOIN 쿼리 금지
DXCMS `Database::rows()`는 **LEFT JOIN 다중(4개 이상)** 또는 **혼합 타입 파라미터**에서 오류 발생.

**대신 2단계 분리 패턴 사용**:
```php
// Step 1: post_id 목록만 조회 (단순 쿼리)
$pidRows = $db->rows(
    "SELECT post_id FROM `{$db->table('post_meta')}`
     WHERE field_key = ? AND value >= ? AND value <= ?",
    array('event_start', $from, $to)
);

// Step 2: post_id별 단건 조회 + getMeta()
foreach ($pidRows as $r) {
    $pid = (string)$r['post_id'];
    $posts = $db->rows(
        "SELECT p.id, p.title, p.member_id, p.author_name,
                p.view_count, p.comment_count, p.created_at,
                m.name AS member_name
         FROM `{$db->table('posts')}` p
         LEFT JOIN `{$db->table('members')}` m ON m.id = p.member_id
         WHERE p.id = ? AND p.status = 1 LIMIT 1",
        array($pid)   // 파라미터는 string 하나만
    );
    if (empty($posts)) continue;
    $meta = BoardFields::getInstance()->getMeta($pid);
}
```

### 테이블명
```php
$db->table('posts')        // → dx_posts
$db->table('members')      // → dx_members
$db->table('comments')     // → dx_comments
$db->table('post_meta')    // → dx_post_meta
$db->table('board_fields') // → dx_board_fields
$db->table('likes')        // → dx_likes
$db->table('boards')       // → dx_boards
```

---

## 6. API(POST) 처리 — view.php 상단 패턴

```php
// view.php 최상단 (다른 코드보다 먼저)
if (dx_method('POST') && isset($_POST['_sub'])) {
    ob_clean();       // 필수 — ob_start() 버퍼 비우기
    dx_csrf_check();  // 필수 — CSRF 검증

    $sub    = trim($_POST['_sub']);
    $postId = dx_post('post_id', '0', 'bigint'); // string

    if ($sub === 'my_action') {
        // 처리
        dx_json(array('success' => true, 'message' => '완료'));
    }

    dx_json(array('success' => false, 'message' => '알 수 없는 요청'));
}
```

**JS 호출 패턴**:
```javascript
fetch(BASE + '/' + BOARD_KEY + '/view/' + POST_ID, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'X-Requested-With': 'XMLHttpRequest',
        'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
    },
    body: '_sub=my_action&post_id=' + POST_ID + '&_csrf=' + CSRF_TOKEN
})
.then(r => r.json())
.then(d => { if (d.success) { ... } });
```

**⚠️ 커스텀 액션 URL 절대 금지**:
`/게시판키/qa_accept` 같은 URL → Router.php에 없으면 404. 항상 `view.php POST + _sub` 패턴 사용.

---

## 7. 여분 필드 (BoardFields)

```php
// 파일 상단 필수
if (!class_exists('BoardFields')) {
    require_once DX_ROOT . '/core/BoardFields.php';
}

// 전체 메타 읽기
$meta = BoardFields::getInstance()->getMeta((string)$postId);
$val  = isset($meta['field_key']) ? $meta['field_key'] : '';

// 단일 값 읽기
$val = dx_get_post_meta($postId, 'field_key', '기본값');

// 저장
dx_save_post_meta($postId, array('field_key' => $value));

// 뷰 자동 렌더링 (is_view=1 필드만)
echo BoardFields::getInstance()->renderView($board['id'], $postId);

// 글쓰기 폼 렌더링
echo BoardFields::getInstance()->renderWriteForm($board['id'], $savedMeta);
```

**write.php에서 여분 필드 저장**: 필드명을 `bf_` 접두어로 작성하면 자동 저장:
```html
<input type="text" name="bf_필드키" value="...">
<select name="bf_필드키">...</select>
<input type="checkbox" name="bf_필드키" value="1">
<input type="datetime-local" name="bf_필드키">
```

**datetime-local 값 변환** (JS):
```javascript
form.addEventListener('submit', function() {
    var dtFields = this.querySelectorAll('input[type="datetime-local"]');
    for (var i = 0; i < dtFields.length; i++) {
        if (dtFields[i].value) {
            dtFields[i].value = dtFields[i].value.replace('T', ' ') + ':00';
        }
    }
});
```

**is_view 설정**:
- 스킨이 직접 커스텀 렌더링하는 필드 → **OFF** (ON이면 renderView가 중복 출력)
- 일반 정보성 필드 → **ON** (자동 렌더링)

---

## 8. 댓글 처리 — 베이직 view.php 상속 패턴

댓글 로직은 `themes/default/board/basic/view.php` 안에 직접 포함되어 있음.
별도 `_comments.php` 파일 없음.

**권장 방식**: 베이직 view.php를 스킨 폴더에 복사 후, 커스텀 블록만 삽입:
```php
// 베이직 view.php 복사 후 상단에 추가
if (!defined('DX_CMS')) exit('Direct access not allowed.');

// 커스텀 여분 필드 로드
if (!class_exists('BoardFields')) {
    require_once DX_ROOT . '/core/BoardFields.php';
}
$_meta = BoardFields::getInstance()->getMeta((string)$post['id']);
// ... 커스텀 변수 처리 ...

// 이후 베이직 view.php 원본 코드 유지
// → 댓글/좋아요/스크랩/실시간 소켓 자동 포함
```

**커스텀 UI 삽입 위치**: `<!-- 본문 카드 -->` 주석 바로 앞:
```php
// 커스텀 정보 블록 (이벤트 정보, Q&A 메타 등)
<?php if ($myCondition): ?>
<div style="...커스텀 블록..."></div>
<?php endif; ?>

<!-- 본문 카드 -->
<article class="dx-view-card ...">
```

---

## 9. 컨텍스트 변수

### list.php에서 사용 가능
```php
$board          // 게시판 정보 배열 (board_key, board_name, id, write_level, use_comment ...)
$posts          // 일반글 배열
$notices        // 공지글 배열
$globalNotices  // 전체 공지 배열
$total          // 일반글 전체 수
$page           // 현재 페이지
$perPage        // 페이지당 행 수
$search         // 검색어
$searchField    // 검색 필드
$categories     // 카테고리 배열
$currentCategory// 현재 카테고리
$tag            // 현재 태그
```

### view.php에서 사용 가능
```php
$board          // 게시판 정보
$post           // 게시글 배열 (id는 BIGINT string)
$files          // 첨부파일 배열
$comments       // 댓글 배열
$postLinks      // 관련 링크 배열
$prevPost       // 이전 글
$nextPost       // 다음 글
$categories     // 카테고리 배열
$viewSearch     // 검색어 (목록에서 넘어온)
$viewSf         // 검색 필드
$viewCat        // 카테고리
```

---

## 10. 주요 헬퍼 함수

```php
// URL
dx_base_url('path')              // 절대 URL
dx_current_url()                 // 현재 URL
dx_redirect('url')               // 리다이렉트

// 입력
dx_get('key', '기본값')          // GET
dx_post('key', '기본값')         // POST
dx_post('key', '0', 'bigint')    // BIGINT → string 반환
dx_method('POST')                // HTTP 메서드 확인

// 출력
dx_json($data)                   // JSON 응답 + exit
dx_error('메시지', 403)           // 에러 + 중단
dx_csrf_field()                  // <input type="hidden" name="_csrf" ...>
dx_csrf_check()                  // CSRF 검증 (실패 시 403)

// 날짜
dx_date('2026-01-01 00:00', 'Y.m.d H:i')  // 포맷 변환
dx_date($dt, 'Y년 m월 d일 (D) H:i')        // 한국어 요일 포함

// 문자열
dx_mb_substr($str, 0, 1)         // 멀티바이트 substr
dx_ip()                          // 클라이언트 IP

// Auth
$auth = Auth::getInstance();
$auth->isLoggedIn()
$auth->isAdmin()
$auth->user()['id']              // INT — member_id용
(string)$auth->user()['id']      // BIGINT 비교 시 string 캐스팅
```

---

## 11. 스킨 유형별 설계 패턴

### 11-1. 달력/스케줄러 스킨
- `list.php`에서 직접 달력 렌더링 (perPage 방식 미사용)
- `_list_rows.php`는 빈 파일로 유지
- 여분 필드: `event_start`, `event_end`, `event_type`, `event_status`, `color_label` 필수
- **데이터 로드 패턴**: post_meta에서 날짜 범위 post_id 추출 → 단건씩 posts 조회 → getMeta()
- `_ltNotices/Posts = array()` 어댑터 변수 설정 후 `_list_rows.php` include 생략 가능

### 11-2. 카드형 갤러리 스킨
- `_list_rows.php`에서 CSS Grid 카드 렌더링
- `thumbnail` 컬럼 활용 또는 여분 필드 `cover_image` 추가
- 호버 효과: `transform: translateY(-3px)`, `box-shadow` 변화
- 카드 상단 컬러 바: `::before` 또는 상태별 배경색

### 11-3. Q&A 스킨
- 채택 기능: `view.php POST _sub=qa_accept` → `dx_post_meta` 저장
- 답변 여부 배지: `_list_rows.php`에서 `$_ltAcceptedIds` 배열로 처리
- 답변자 구분: `$post['member_id'] !== $post['parent_member_id']`
- `dx_likes` 재활용: `target_type='qa_eval'`로 평가 중복 방지

### 11-4. 포트폴리오/프로젝트 스킨
- 진행률 바: 여분 필드 `progress` (0~100)
- 상태 배지: `status` 필드 select (계획중/진행중/완료/보류)
- 기술 스택 태그: `tech_stack` 필드, 콤마 구분 → PHP `explode(',')`
- 공개 여부: `is_public` checkbox

### 11-5. 이벤트/모임 스킨
- 신청 기능: `require_apply` + `max_people` + `current_people` 여분 필드
- 마감 처리: `apply_deadline` datetime 필드, `strtotime()` 비교
- 인원 바: `current_people / max_people * 100`%
- 외부 신청 링크: `external_url` 필드

---

## 12. CSS/디자인 패턴

### 카드 공통 스타일 (CSS 변수 활용)
```css
.skin-card {
  background: var(--bg-card, #fff);
  border: 1.5px solid var(--border, #e2e8f0);
  border-radius: 16px;
  padding: 20px;
  transition: border-color .18s, box-shadow .18s, transform .18s;
}
.skin-card:hover {
  border-color: var(--p, #1a73e8);
  box-shadow: 0 8px 28px rgba(26,115,232,.12);
  transform: translateY(-3px);
}
```

### DXCMS CSS 변수
```css
var(--p)           /* 주 색상 (파랑 계열) */
var(--bg-card)     /* 카드 배경 */
var(--bg-body)     /* 페이지 배경 */
var(--bg-sub)      /* 서브 배경 (f8fafc) */
var(--border)      /* 경계선 (e2e8f0) */
var(--text-main)   /* 주 텍스트 (1e293b) */
var(--text-muted)  /* 보조 텍스트 (94a3b8) */
```

### 상태 배지 패턴
```php
function _skin_status_style($status) {
    $map = array(
        '완료'   => array('bg'=>'#dcfce7','color'=>'#15803d','dot'=>'#22c55e'),
        '진행중' => array('bg'=>'#dbeafe','color'=>'#1d4ed8','dot'=>'#3b82f6'),
        '대기'   => array('bg'=>'#f1f5f9','color'=>'#475569','dot'=>'#94a3b8'),
        '취소'   => array('bg'=>'#fee2e2','color'=>'#b91c1c','dot'=>'#ef4444'),
    );
    return isset($map[$status]) ? $map[$status] : $map['대기'];
}
```

### 정보 카드 그리드 (뷰 페이지용)
```html
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0">
  <!-- 전체 폭 항목 -->
  <div style="grid-column:1/-1;display:flex;align-items:flex-start;gap:12px;
              padding:14px 20px;border-bottom:1px solid var(--border)">
    <div style="width:34px;height:34px;border-radius:8px;
                background:rgba(26,115,232,.1);color:#1d4ed8;
                display:flex;align-items:center;justify-content:center;font-size:.85rem">
      <i class="fa-regular fa-calendar-days"></i>
    </div>
    <div>
      <div style="font-size:.68rem;font-weight:700;color:var(--text-muted);
                  margin-bottom:3px;letter-spacing:.03em">라벨</div>
      <div style="font-size:.88rem;font-weight:600;color:var(--text-main)">값</div>
    </div>
  </div>
</div>
```

---

## 13. 체크리스트

스킨 코드 작성 전 반드시 확인:

- [ ] `require_once DX_ROOT . '/core/BoardFields.php'` 추가했는가?
- [ ] **post_id를 string으로 처리**하는가? `(int)` 캐스팅 없는가?
- [ ] **DB 파라미터 모두 string** 캐스팅했는가? `(string)$id`
- [ ] `posts` 테이블에 `member_name` 없음 → `dx_members` JOIN 필요
- [ ] `row()` 메서드 미지원 → `rows()[0]` 패턴 사용
- [ ] 복잡한 다중 JOIN 대신 **2단계 분리 쿼리** 사용했는가?
- [ ] POST API를 **view.php 상단 `_sub` 패턴**으로만 처리하는가?
- [ ] `ob_clean()` → `dx_csrf_check()` 순서가 맞는가?
- [ ] list.php에 렌더링/API 코드 없는가? (공지 중복 원인)
- [ ] 커스텀 액션 URL(`/게시판키/custom`) 사용하지 않는가?
- [ ] PHP 5.6 호환: `array()`, 클로저 대신 일반 함수, `usort('함수명')` 형식
- [ ] `skin.json`의 `actions` 배열에 표준 액션만 있는가?
- [ ] 댓글 필요 시 **베이직 view.php 복사 후 커스텀 블록 삽입** 방식 사용했는가?
- [ ] `datetime-local` 값을 submit 시 공백 형식으로 변환했는가?
- [ ] is_view OFF 필요한 여분 필드를 안내했는가?

---

## 14. 오류 디버깅

| 증상 | 원인 | 해결 |
|---|---|---|
| DB 500 오류 | BIGINT 파라미터 int 캐스팅 | 모든 파라미터 `(string)` 캐스팅 |
| DB 500 오류 | `member_name` 컬럼 없음 | `dx_members` LEFT JOIN 추가 |
| DB 500 오류 | 다중 LEFT JOIN | 2단계 분리 쿼리로 교체 |
| DB 500 오류 | `row()` 미지원 | `rows()[0]` 패턴으로 교체 |
| JSON 파싱 실패 | `ob_clean()` 누락 | view.php POST 분기 최상단에 추가 |
| 404 커스텀 액션 | Router.php 미등록 | view.php `_sub` 패턴으로 변경 |
| 공지 2번 출력 | list.php에 렌더링 코드 | list.php 베이직으로 복원 |
| syntax error `}` | 파일 조합 시 중복 | 해당 라인 전후 블록 중복 제거 |
| 댓글 없음 | 독자 view.php 구현 | 베이직 view.php 복사 후 커스텀 삽입 |
| 카드 좌우 여백 없음 | `.pj-grid` padding 미설정 | `padding: 16px 20px` 추가 |
| 전체공지 여백 줄어듦 | 래퍼에 padding 적용 | 래퍼 padding 제거, 그리드만 적용 |

댓글3

D
DX 2026.05.24 17:00

아 혹시, 약간의 삐딱선이 있을지 모르니 
https://designonex.com/dxcms-manual/view/177918708757889
여분필드 메뉴얼 링크도 같이 올려주세요.

모
모아비즈 2026.05.24 17:26

404

오류가 발생했습니다.

D
DX 2026.05.24 17:29
https://designonex.com/dxcms-manual/view/1779187087578893?cat=6.+%EA%B2%8C%EC%8B%9C%ED%8C%90
제가 주소에 맨 마지막에 3을 뺏네요.
ㅎㅎㅎ
감사합니다.
로그인 후 댓글을 작성할 수 있습니다.
번호 제목 작성자 날짜 조회
공지
D DX
05.22 17
11
모아비즈
05.21 24
모아비즈 · 24
9
모아비즈
05.21 21
모아비즈 · 21
DX · 25
5
DX
05.19 22
DX · 22
4
모아비즈
05.19 36
모아비즈 · 36
3
모아비즈
05.18 33
모아비즈 · 33
2
모아비즈
05.15 47
모아비즈 · 47
1
모아비즈
05.15 53
모아비즈 · 53
31
전체 회원
502
전체 게시글
763
전체 댓글
441
오늘 방문
33,173
전체 방문
3
현재 접속
인기글 7일 이내
최신글
최신댓글
목록