회원가입 | 고객센터 |
DESIGNONEX
dxcms.kr
로그인 회원가입
고객센터
10. 마이페이지

마이페이지 구조

D DX
2026.05.10 16:06(수정됨) 125 0

1장. 마이페이지 개요

마이페이지(/auth/mypage)는 로그인 회원이 자신의 정보를 확인하고 관리하는 페이지입니다. 단일 PHP 파일(core/auth/mypage.php)이 10개 탭을 모두 처리하며, 탭 파라미터(tab=...)에 따라 데이터를 동적으로 로드하고 렌더링합니다. 최종 출력은 현재 활성 테마의 layout/main.php를 통해 래핑됩니다.


1.1 진입 경로 및 접근 제어

URL: /auth/mypage
    ?tab=profile|notifications|memo|points|exp|scraps|purchases|friends|blocks|social
    &sub=my|followers           (friends 탭 전용)
    &mbox=received_all|received_friend|sent_all|sent_friend|send  (memo 탭 전용)
    &p=1                        (페이지 번호, 기본 20개/페이지)

비로그인 접근 시:
  → /auth/login?redirect=/auth/mypage 로 자동 리다이렉트

파일 위치:
  core/auth/mypage.php   ← 메인 로직 (탭 데이터 로드 + 렌더링)
  assets/css/mypage.css  ← 마이페이지 전용 CSS (dx_head 훅으로 자동 로드)


1.2 10개 탭 구성

탭 (tab=) 아이콘 기능
profile 👤 프로필 수정, 비밀번호 변경, 이미지 업로드, SNS 연결, 회원 탈퇴
notifications 🔔 실시간 알림 전체 목록 조회, 읽음 처리, 삭제
memo ✉️ 쪽지(메모) 수신함/발신함, 보내기, 읽음 처리, 삭제
points 💰 포인트 내역 조회 (변동·잔액·유형), 페이지네이션
exp 경험치 내역, 레벨 프로그레스바, 레벨 기준표
scraps 🔖 스크랩한 게시글 목록, 스크랩 해제
purchases 🛒 포인트샵 구매 내역, 상태(구매완료/환불됨)
friends 👥 내가 추가한 친구(sub=my), 나를 추가한 사람(sub=followers), 서로친구 배지
blocks 🚫 차단한 회원 목록, 차단 해제
social 🔗 소셜 계정 연결 현황(카카오/네이버/구글/GitHub), 연결하기 링크


1.3 전체 처리 흐름

GET /auth/mypage?tab=profile
  ↓
1. 비로그인 체크 → 리다이렉트
2. 최신 회원 정보 조회 (dx_members WHERE id=?)
3. dx_head 훅으로 mypage.css 로드
4. tab 파라미터 파싱
5. POST면 CSRF 검증 → _action 처리 (profile 수정 / withdraw 탈퇴)
6. 탭별 데이터 로드 (points/exp/scraps/friends/blocks/purchases/notifications)
7. ob_start() → HTML 렌더링 → ob_get_clean() → $dx_content 저장
8. _dx_mypage_pagination() 헬퍼 함수 정의
9. DxTheme::resolve("layout/main.php") → require → 테마 래핑 출력


2장. 프로필 헤더 카드

모든 탭에서 공통으로 표시되는 회원 정보 요약 카드입니다. 아바타•이름•레벨 배지•경험치 바•포인트/EXP/친구 수 스탯으로 구성됩니다.


2.1 구성 요소

요소 설명 및 CSS 클래스
아바타 profile_img 있으면 <img> 태그. 없으면 이름 첫 글자 div (.mp-avatar). 62px 원형
이름 + 레벨 배지 .mp-name + .mp-lv-badge. Lv.N + 레벨명 (DxPoint::getLevelName)
로그인 아이디 .mp-loginid. 회색 소문자로 표시
경험치 바 .mp-exp-bar → .mp-exp-fill (width: $progress%). 다음 레벨까지 EXP 표시
스탯 (PC) .mp-stats-pc. 포인트(금색)·EXP(보라)·친구수(어두운). 600px 이상에서만 표시
스탯 (모바일) .mp-stats-mo. 카드 하단 3분할 가로 배치. 600px 미만에서만 표시


2.2 레벨•경험치 계산

// PHP 마이페이지에서
$level    = (int)$member["level"];
$exp      = (int)$member["exp"];
$progress = DxPoint::getLevelProgress($exp);   // 0~100 (현재 레벨 내 진행률 %)
$nextExp  = DxPoint::getNextLevelExp($exp);    // 다음 레벨까지 필요 EXP. null이면 최고레벨

// 친구 수 — 1분 DxCache 캐시 적용
$_fcVal = DxFriend::getFriendCount($memberId);
// DxCache::get("friend_cnt_{$memberId}") → 없으면 조회 후 60초 캐시


2.3 탭 버튼 렌더링

탭 버튼은 가로 스크롤 가능한 row로 렌더링됩니다. 현재 탭은 bg-blue-600 text-white, 나머지는 bg-white text-slate-500 border 스타일입니다. 메모 탭에는 미읽음 쪽지 수 빨간 배지(mp-memo-badge)가 표시됩니다.
// 탭 배열 정의
$tabs = array(
    "profile"       => array("icon"=>"👤", "label"=>"프로필"),
    "notifications" => array("icon"=>"🔔", "label"=>"알림"),
    "memo"          => array("icon"=>"✉️", "label"=>"메모"),
    "points"        => array("icon"=>"💰", "label"=>"포인트"),
    "exp"           => array("icon"=>"⭐", "label"=>"경험치"),
    "scraps"        => array("icon"=>"북마크", "label"=>"스크랩"),
    "purchases"     => array("icon"=>"🛒", "label"=>"구매내역"),
    "friends"       => array("icon"=>"👥", "label"=>"친구"),
    "blocks"        => array("icon"=>"🚫", "label"=>"차단"),
    "social"        => array("icon"=>"🔗", "label"=>"소셜연결"),
);

// 메모 미읽음 수 — 30초 DxCache 캐시
$_unreadMemo = 0;  // memos WHERE to_id=? AND is_read=0 AND is_deleted_receiver=0


3장. POST 처리 — 프로필 수정 및 회원 탈퇴


3.1 프로필 수정 (_action=profile)

form enctype="multipart/form-data" 으로 POST 전송됩니다. CSRF 검증 후 아래 필드를 처리합니다.
 
필드 (name) 필수 처리 방식
name 필수 DxSanitizer::text(). 빈값이면 오류
email 선택 filter_var FILTER_VALIDATE_EMAIL 검증. 빈값이면 업데이트 생략
bio 선택 300자 초과 시 오류. DxSanitizer::text() 처리
website / blog 선택 http로 시작하지 않으면 https:// 자동 추가 (cleanUrl 클로저)
sns_instagram / twitter / facebook / youtube / github 선택 DxSanitizer::text(). members 테이블에 해당 컬럼 있을 때만 저장
cur_pw / new_pw 변경 시 cur_pw: password_verify(). new_pw: 6자 이상. password_hash(BCRYPT)
profile_img (파일) 선택 jpg/png/gif/webp, 10MB 이내. GD로 400×400px JPEG 85품질 리사이즈
delete_profile_img 선택 "1"이면 기존 이미지 파일 unlink + DB 컬럼 빈값으로 업데이트


3.2 프로필 이미지 업로드 상세

// 저장 경로: data/uploads/profiles/
// 파일명: YYYYMMDD_{8자 랜덤hex}.jpg (항상 .jpg로 통일)

// GD 리사이즈 로직
if (extension_loaded("gd") && function_exists("imagecreatefromstring")) {
    $raw = file_get_contents($imgTmp);
    $src = imagecreatefromstring($raw);  // 확장자 무관, 바이너리로 판별

    // 비율 유지 축소 (이미 400×400 이하면 그대로)
    $ratio = min(400/$srcW, 400/$srcH);
    $dst   = imagecreatetruecolor($dstW, $dstH);

    // PNG/GIF/WebP 투명 → 흰색 배경 변환
    $white = imagecolorallocate($dst, 255, 255, 255);
    imagefill($dst, 0, 0, $white);

    imagecopyresampled($dst, $src, 0,0,0,0, $dstW, $dstH, $srcW, $srcH);
    imagejpeg($dst, $imgFinal, 85);  // JPEG 품질 85

    // GD 없거나 실패 시: move_uploaded_file()로 원본 확장자 그대로 저장
}

// 성공 시: 기존 이미지 unlink 후 "profiles/{파일명}" DB 저장
$upd["profile_img"] = "profiles/" . $imgSave;

⚠️ 컬럼 존재 여부 동적 체크
bio, website, blog, sns_* 컬럼은 추가 설치된 경우에만 존재합니다.
SHOW COLUMNS FROM dx_members 결과를 DxCache(1시간)로 캐시하여
in_array()로 컬럼 존재 여부를 확인 후 UPDATE합니다.
→ 컬럼이 없어도 오류 없이 기본 정보(name, email, password)만 업데이트됩니다.


3.3 회원 탈퇴 (_action=withdraw)

// 탈퇴 확인 폼 필드
// 1. withdraw_confirm: "DELETE" 텍스트 입력 (클라이언트 측 확인용)
// 2. withdraw_pw: 현재 비밀번호 (서버 측 password_verify 검증)

// 처리 로직
if (!password_verify($curPw, $member["password"])) {
    // 소셜 로그인 회원(password 없음)은 비밀번호 검증 생략
    $withdrawMsg = array("type"=>"error", "text"=>"비밀번호가 올바르지 않습니다.");
} else {
    // 실제 삭제 아님! status=0으로 비활성화
    $db->query("UPDATE dx_members SET status=0 WHERE login_id=?", [$member["login_id"]]);
    $auth->logout();  // 세션 삭제 + Remember Me 쿠키 제거
    dx_set_flash("회원 탈퇴가 완료되었습니다. 이용해 주셔서 감사합니다.");
    dx_redirect(dx_base_url());
}

// 탈퇴 결과: 데이터 보존 (게시글, 댓글, 포인트 그대로)
// 계정 접근만 불가 (status=0 → 로그인 시 "비활성 계정" 처리)


4장. 알림 탭 (tab=notifications)


4.1 알림 탭 데이터 로드

// DxNotification::getList() 호출
$notifList = DxNotification::getList($memberId, $pp, ($page-1)*$pp);
// $pp = 20개/페이지

// 총 건수
$total = (int)$db->value("SELECT COUNT(*) FROM dx_notifications WHERE to_member_id=?", [$memberId]);


4.2 알림 타입별 이모지

type 이모지 및 표시 레이블
comment 💬 댓글
comment_reply ↩️ 답글
friend 👥 친구
scrap 스크랩
memo ✉️ 쪽지
(기타) 🔔 (fallback)


4.3 알림 액션 API

액션 설명
POST /api/notification (_sub=read) 개별 읽음 처리. is_read=1, 미읽음 수 반환
POST /api/notification (_sub=read_all) 전체 읽음 처리. 배지 초기화
POST /api/notification (_sub=delete) 개별 삭제. id 파라미터. 삭제 후 unread 수 반환
POST /api/notification (_sub=delete_all) 전체 삭제. 목록 비움


4.4 알림 탭 UI 흐름

  • 미읽음 항목 — 배경 #eff6ff(연파랑) + 파란 점 표시
  • 읽은 항목 — 배경 #fff + 점 없음
  • "모두 읽음" 버튼 — mpNotifReadAll() → POST read_all → 배경색 초기화 + 헤더 벨 배지 초기화
  • "전체 삭제" 버튼 — mpNotifDelAll() → POST delete_all → 목록 비움
  • 개별 삭제 ✕ 버튼 — mpNotifDel(id) → POST delete → 해당 행 fadeOut 제거
  • 헤더 벨 배지 동기화 — dx-notif-badge 요소의 숫자를 r.unread 값으로 갱신


5장. 메모(쪽지) 탭 (tab=memo)


5.1 메모함 구분

mbox 설명
received_all (기본) 나에게 온 전체 쪽지 (is_deleted_receiver=0)
received_friend 친구에게서 받은 쪽지 (from_id IN 친구ID 목록)
sent_all 내가 보낸 전체 쪽지 (is_deleted_sender=0)
sent_friend 친구에게 보낸 쪽지 (to_id IN 친구ID 목록)
send 쪽지 보내기 폼 화면


5.2 쪽지 목록 쿼리 구조

// 받은 쪽지 (received_all)
SELECT m.*, u.name AS other_name, u.profile_img AS other_img, u.id AS other_id
FROM dx_memos m
LEFT JOIN dx_members u ON m.from_id = u.id    -- 발신자 정보
WHERE m.to_id = ? AND m.is_deleted_receiver = 0
ORDER BY m.id DESC LIMIT 20 OFFSET ?

// 보낸 쪽지 (sent_all)
SELECT m.*, u.name AS other_name, ...
FROM dx_memos m
LEFT JOIN dx_members u ON m.to_id = u.id      -- 수신자 정보
WHERE m.from_id = ? AND m.is_deleted_sender = 0
ORDER BY m.id DESC

// 친구 필터 (received_friend)
WHERE m.to_id = ? AND m.is_deleted_receiver = 0
  AND m.from_id IN ({친구ID 목록})   -- 쉼표 구분 정수 목록


5.3 쪽지 목록 UI 기능

  • 클릭 펼치기 — mxToggleMemo(id, isRead) — 본문 영역 toggle. 미읽음이면 /api/memo POST read 호출
  • 읽음 처리 — 행 배경 변경 + NEW 배지 제거 + mp-memo-badge 숫자 감소 + mx-badge-new 감소
  • 답장 버튼 — mxReply(toId, toName) — /auth/mypage?tab=memo&mbox=send&to_id=N&to_name=이름 으로 이동
  • 개별 삭제 — mxDeleteMemo(id, box) — box=received or sent. 행 fadeOut 후 제거
  • 전체 삭제 — mxDeleteAll(mbox) — 현재 목록 모든 항목 순차 삭제
  • 친구 메모 표시 — fr-row friend-memo 클래스 추가 + 👥 친구 배지 표시
  • 발신함 읽음 확인 — 수신자가 읽었으면 ✓ 읽음 배지 표시


5.4 쪽지 보내기 폼 (mbox=send)

  1. 받는 사람 검색 입력 → mxSearchUser() → GET /api/memo?_sub=search_user&q= 호출
  2. 검색 결과 버튼 클릭 → mxSelectUser(id, name) → MX_TO_ID 변수에 ID 저장
  3. 내용 입력 (최대 1000자) + 글자 수 실시간 표시
  4. 보내기 버튼 → mxDoSend() → POST /api/memo (_sub=send)
  5. 전송 성공 시: "메모가 전송되었습니다!" 메시지 표시
  6. 소켓 DM 전달: dm 기능 활성화 + 연결 중이면 sendEvent({type:"dm", to_login_id, from_name, content_preview})


5.5 메모 API 엔드포인트

엔드포인트 설명
GET /api/memo?_sub=search_user&q=검색어 회원 검색. name 또는 login_id로 검색. 최대 10건 반환
POST /api/memo (_sub=send) 쪽지 발송. to_id, content 필수. dx_memos 테이블 INSERT
POST /api/memo (_sub=read) 읽음 처리. memo_id, is_read=1 업데이트. 미읽음 수 반환
POST /api/memo (_sub=delete) 삭제. memo_id, box(received/sent). 해당 deleted 컬럼 1로 변경


5.6 dx_memos 테이블 구조

컬럼 타입 설명
id BIGINT UNSIGNED PK (AUTO_INCREMENT)
from_id INT UNSIGNED 발신자 회원 ID
to_id INT UNSIGNED 수신자 회원 ID
content TEXT 쪽지 내용
is_read TINYINT(1) 0=미읽음, 1=읽음
read_at DATETIME 읽은 시각 (NULL=미읽음)
is_deleted_sender TINYINT(1) 발신자가 삭제했으면 1. 실제 행 삭제 아님
is_deleted_receiver TINYINT(1) 수신자가 삭제했으면 1. 양쪽 모두 1이면 실질적 삭제
created_at DATETIME 발송 일시


6장. 포인트 탭 (tab=points)


6.1 포인트 내역 표

포인트 내역은 5개 컬럼으로 표시됩니다: 일시(m.d H:i) • 유형(DxPoint::typeName()) • 변동(+N P / -N P) • 잔액 • 메모(note). 변동이 양수면 td-plus(초록), 음수면 td-minus(빨강) 스타일이 적용됩니다.
// 포인트 내역 조회
$pointLogs = DxPoint::getLogs($memberId, $pp, $offset);
// → dx_point_log 테이블 ORDER BY id DESC LIMIT 20 OFFSET ?

// 컬럼: created_at, type, point, balance, note
// DxPoint::typeName($type): 유형 코드 → 한글 이름 변환
//   예: "write" → "게시글 작성", "comment" → "댓글 작성", "admin" → "관리자 지급" 등


6.2 포인트 헤더 카드

탭 헤더 우측에 현재 보유 포인트를 금색 배경 카드로 강조 표시합니다. number_format()으로 천 단위 구분자가 적용됩니다.


7장. 경험치 탭 (tab=exp)


7.1 레벨 프로그레스바

// 현재 레벨 내 진행률 (0~100%)
$progress = DxPoint::getLevelProgress($exp);

// 다음 레벨까지 필요 EXP
$nextExp = DxPoint::getNextLevelExp($exp);  // null이면 최고레벨

// HTML: 보라색 그라디언트 바
<div style="height:10px;background:#e2e8f0;border-radius:5px">
  <div style="width:{$progress}%;background:linear-gradient(90deg,#7c3aed,#a78bfa)"></div>
</div>


7.2 레벨 기준표

경험치 탭에는 details/summary 토글로 레벨 기준표가 펼쳐집니다. DxPoint::getThresholds()가 {레벨=>필요EXP} 배열을 반환하며, 현재 레벨은 보라색 강조 박스로 표시됩니다.
// DxPoint::getThresholds() 반환 예시
array(
    1  => 0,
    2  => 100,
    3  => 300,
    4  => 600,
    5  => 1000,
    // ...
)


8장. 스크랩 탭 (tab=scraps)


8.1 스크랩 목록

// 스크랩 목록 조회
$scraps = DxFriend::getScrapList($memberId, $pp, $offset);
// → dx_scraps JOIN dx_posts JOIN dx_boards
// 컬럼: post_id, title, board_key, board_name, created_at, memo

// 삭제된 게시글 처리: title이 비어있으면 "삭제된 게시글" 표시


8.2 스크랩 해제

// 해제 버튼 onclick
window.dxUnscrap = function(postId, btn) {
    if (!confirm("스크랩을 취소할까요?")) return;
    // POST /api/scrap {action:"remove", post_id:postId}
    // 성공 시 해당 행 display:none
};


8.3 dx_scraps 테이블

컬럼 타입 설명
id INT UNSIGNED PK (AUTO_INCREMENT)
member_id INT UNSIGNED 스크랩한 회원 ID
post_id BIGINT UNSIGNED 스크랩한 게시글 ID
memo VARCHAR(191) 스크랩 메모 (선택)
created_at DATETIME 스크랩 일시


9장. 친구 탭 (tab=friends)


9.1 두 가지 서브탭

서브탭 (sub=) 설명
my (기본) 내가 추가한 친구 목록. dx_friends WHERE member_id=? AND type="friend"
followers 나를 추가한 사람 목록. dx_friends WHERE target_id=? AND type="friend"


9.2 서로친구 배지 로직

// sub=my (내가 추가한 친구) 탭에서
// 나를 추가한 사람 ID 목록(followerIds)을 별도 조회
$followerIds = array_column(
    $db->rows("SELECT member_id FROM dx_friends WHERE target_id=? AND type='friend' AND status=1", [$memberId]),
    "member_id"
);

// 렌더링 시: 상대방이 followerIds에 포함되면 "서로친구" 배지 표시
$_fMutual = in_array((int)$_f["target_id"], array_map("intval", $followerIds));

// sub=followers 탭에서도 반대로 동일한 로직 적용
$_fwAdded = in_array((int)$_fw["member_id"], array_map("intval", $_myFriendIds));


9.3 친구 액션 API

// window.dxFriendAction(targetId, action, btn, mode)
// POST /api/friend {action, target_id}

// action 종류:
// "remove_friend" — 내 친구 목록에서 삭제 (dx_friends status=0)
// "block"         — 차단 (dx_friends type="block"으로 변경)
// "unblock"       — 차단 해제
// "add_friend"    — 나를 추가한 사람을 나도 맞추가

// mode="row"이면 성공 시 해당 fr-row 엘리먼트 fadeOut 후 제거


9.4 dx_friends 테이블

컬럼 타입 설명
id INT UNSIGNED PK
member_id INT UNSIGNED 요청자 (추가한 사람)
target_id INT UNSIGNED 대상자 (추가된 사람)
type ENUM(friend, block) friend: 친구, block: 차단
status TINYINT(1) 1=활성, 0=삭제(논리 삭제)
created_at DATETIME 관계 생성 일시

💡 서로친구 개념
DXCMS의 친구는 단방향(follow) 구조입니다.
내가 A를 추가 → dx_friends(member_id=나, target_id=A, type=friend)
A도 나를 추가 → dx_friends(member_id=A, target_id=나, type=friend)
양쪽 레코드가 모두 있으면 "서로친구" 배지 표시


10장. 소셜 연결 탭 (tab=social)


10.1 소셜 연결 현황 표시

// 활성화된 소셜 프로바이더 목록
$_allProviders = DxSocialAuth::providers();
// → ["kakao", "naver", "google", "github"] 등

// 현재 회원의 연결된 소셜 계정 조회
$stmt = $pdo->prepare("SELECT * FROM dx_social_accounts WHERE member_id=?");
$stmt->execute([$memberId]);
$_linkedAccounts = [$row["provider"] => $row, ...];

// 렌더링 조건: cfg["enabled"] && cfg["client_id"] 가 모두 있는 경우만 표시


10.2 소셜 연결 UI

상태 UI
연결됨 "✓ 연결됨" 초록 배지. 연결된 이메일(또는 이름) + 마지막 로그인 날짜 표시
미연결 "연결하기" 버튼 → /auth/social?provider=카카오&redirect=/auth/mypage?tab=social


11장. 공개 프로필 페이지 (/auth/profile)

/auth/profile?id={member_id} 는 다른 사람의 프로필을 볼 수 있는 공개 페이지입니다. core/auth/profile.php 파일이 처리하며, 회원 본인이 보면 "프로필 수정" 버튼이, 타 회원이 보면 "메모 보내기" + "1:1 채팅" 버튼이 표시됩니다.


11.1 표시 정보

  • 커버 + 아바타 — var(--p) → 인디고 그라디언트 커버. 76px 원형 아바타 (사진 또는 이름 첫 글자)
  • 이름 + 레벨 — 이름 h1 + Lv.N + 가입일
  • 통계 — 게시글 수 / 댓글 수 / 포인트 / 경험치 (4개 가로 배치)
  • 자기소개 — bio 컬럼. nl2br()로 줄바꿈 표시
  • 링크 카드 — website, blog, sns_instagram/twitter/facebook/youtube/github 있을 때만 표시

11.2 버튼 표시 조건

상황 표시 버튼
타 회원이 볼 때 (로그인) 메모 보내기 (파란색) + 1:1 채팅 (초록색, DM 기능 ON일 때만 표시)
내 프로필 볼 때 ($isSelf) 프로필 수정 버튼 → /auth/mypage
비로그인 버튼 없음


11.3 1:1 채팅 초대 버튼

// profile.php의 1:1 채팅 버튼 (id="dx-chat-invite-btn")
// 기본 display:none → DM 기능 ON + 소켓 연결 시 JS가 자동으로 표시

<button id="dx-chat-invite-btn" '이름')"
        style="display:none">
  1:1 채팅
</button>

// socket-core.js.php가 features.dm ON이면 display:inline-flex로 변경
// dxSendChatInvite()는 sendEvent({type:"dm_invite", to_login_id, from_name, from_login_id, room_id})


12장. CSS 구조 (mypage.css)


12.1 주요 CSS 클래스

클래스 역할
.mp-profile-card 프로필 헤더 카드 래퍼. 흰 배경, 18px 둥근 모서리, 미세 그림자
.mp-avatar 아바타 div. 62px 원형. 파란→인디고 그라디언트
.mp-profile-img 프로필 사진 img. 62px 원형, object-fit:cover
.mp-lv-badge 레벨 배지. 파란 배경, 흰 텍스트, 99px 둥근
.mp-exp-bar / .mp-exp-fill 경험치 바. fill은 파란→인디고 그라디언트
.mp-stats-mo / .mp-stats-pc 스탯 모바일(하단 3분할) / PC(우측 3열). 600px 기준 전환
.mp-hd 탭 헤더 영역. flex justify-between
.mp-tbl 포인트/경험치 내역 테이블. fixed 레이아웃
.mp-type-badge 포인트 유형 배지. 회색 배경, 작은 글씨
.mp-empty 빈 데이터 상태. 중앙 정렬 아이콘+텍스트
.pf-section 프로필 수정 폼 섹션. 패딩+하단 구분선
.pf-section-title 섹션 제목. 대문자, 트래킹, 우측 선 자동 연장 (::after)
.pf-row 입력 2컬럼 그리드. 600px 이하에서 1컬럼
.fr-row 친구 목록 행. flex align-center
.fr-ava 친구 아바타 원형. 40px
.fr-mutual-badge "서로친구" 초록 배지
.mx-tab / .mx-tab.on 메모함 탭 버튼 / 활성 탭
.mx-row / .mx-memo-unread 메모 목록 행 / 미읽음 행 (파란 배경)
.mx-badge-new 빨간 NEW 배지. 원형, 중앙정렬


12.2 반응형 브레이크포인트

조건 적용 변경
min-width: 600px 아바타 62→72px, .mp-stats-mo 숨김, .mp-stats-pc 표시
max-width: 600px .pf-row 1컬럼
max-width: 480px 탭 버튼 패딩/폰트 축소 (.mp-tab-btn)


13장. 관련 DB 스키마 총정리


13.1 dx_members — 회원 정보

컬럼 타입 마이페이지 사용 방식
id INT UNSIGNED 회원 식별자. 모든 탭 데이터 조회 기준
name VARCHAR(100) 이름/닉네임. 프로필 헤더 + 수정 폼
login_id VARCHAR(50) 아이디. 헤더 카드 + 탈퇴 처리
email VARCHAR(191) 이메일. 수정 폼 + FILTER_VALIDATE_EMAIL 검증
password VARCHAR(255) BCrypt 해시. 비밀번호 변경 + 탈퇴 검증
profile_img VARCHAR(500) "profiles/{파일명}" 형식. 헤더 카드 아바타
bio TEXT 자기소개. 프로필 탭 textarea
website / blog VARCHAR(500) 개인 사이트/블로그. 프로필 폼 + 공개 프로필
sns_instagram ~ sns_github VARCHAR(300) SNS URL. 수정 폼 5개 입력란
level SMALLINT 현재 레벨. 헤더 배지 + 경험치 탭
exp INT 누적 경험치. 프로그레스바 계산
point INT 보유 포인트. 헤더 스탯 + 포인트 탭
status TINYINT 0=비활성(탈퇴). withdraw 처리 시 0으로 변경
join_date DATETIME 가입일. 프로필 탭 가입일 표시
last_login DATETIME 마지막 로그인. 프로필 탭 표시


13.2 dx_notifications — 알림

컬럼 타입 설명
id BIGINT UNSIGNED PK
to_member_id INT UNSIGNED 수신자 회원 ID. 알림 탭 조회 기준
from_member_id INT UNSIGNED 발신자 회원 ID. 0이면 시스템 알림
type VARCHAR(30) comment/comment_reply/friend/scrap/memo
message VARCHAR(255) 알림 메시지 본문
url VARCHAR(500) 클릭 시 이동 URL
is_read TINYINT(1) 0=미읽음, 1=읽음
created_at DATETIME 알림 생성 일시


13.3 헬퍼 함수 _dx_mypage_pagination()

// mypage.php 하단에 로컬 정의된 페이지네이션 헬퍼
function _dx_mypage_pagination($pages, $current, $baseUrl) {
    if ($pages <= 1) return;
    // 현재 페이지 ±4 범위만 표시 (최대 9개 버튼)
    for ($i = max(1,$current-4); $i <= min($pages,$current+4); $i++) {
        // 현재 페이지: bg-blue-600 text-white
        // 나머지: bg-slate-50 text-slate-500
        echo '<a href="' . $baseUrl . '&p=' . $i . '">' . $i . '</a>';
    }
}

// 각 탭에서 호출 예시:
_dx_mypage_pagination($pages, $page, $baseUrl . "?tab=points");


14장. 사용방법 정리


14.1 회원이 마이페이지를 사용하는 방법

  1. 로그인 후 헤더의 [회원이름]님 또는 "마이페이지" 링크 클릭
  2. 프로필 탭: 이름•이메일•자기소개•사진 변경 후 "프로필 저장" 클릭
  3. 비밀번호 변경: 현재 비밀번호 입력 후 새 비밀번호 입력 → 프로필 저장
  4. SNS 연결: 소셜연결 탭에서 "연결하기" 버튼 클릭 → 소셜 로그인 완료
  5. 메모: 메모 탭 → "메모 보내기" 클릭 → 받는 사람 검색 → 내용 작성 → 보내기
  6. 친구: 게시판 글쓴이 클릭 → 공개 프로필 → "친구 추가" 버튼
  7. 스크랩 해제: 스크랩 탭에서 "해제" 버튼 클릭
  8. 회원 탈퇴: 프로필 탭 하단 "탈퇴하기" → "DELETE" 입력 → 비밀번호 확인 → 탈퇴


14.2 개발자가 마이페이지를 커스텀하는 방법

  • 탭 추가 — $tabs 배열에 새 항목 추가 후 해당 elseif 블록 추가
  • CSS 수정 — assets/css/mypage.css 직접 수정 또는 테마 CSS에서 오버라이드
  • 프로필 필드 추가 — dx_members 테이블에 컬럼 추가 후 SHOW COLUMNS 캐시 만료 대기 (1시간) 또는 DxCache 초기화
  • 포인트 유형 추가 — DxPoint::typeName() 메서드에 새 type 코드와 한글명 추가
  • 메모 API 확장 — core/api/memo.php에 새 _sub 액션 추가


14.3 주의사항

  • 프로필 이미지는 data/uploads/profiles/ 폴더에 저장됩니다. 폴더가 없으면 자동 생성됩니다.
  • GD 확장이 없는 서버에서는 리사이즈 없이 원본 파일 그대로 저장됩니다.
  • 회원 탈퇴는 물리적 삭제가 아닌 status=0 비활성화입니다. 데이터는 보존됩니다.
  • 소셜 연결은 DxSocialAuth 설정(client_id)이 완료된 프로바이더만 표시됩니다.
  • 메모•친구•차단 API는 모두 CSRF 토큰 검증이 적용됩니다.
  • 친구 수는 1분, 메모 미읽음 수는 30초 DxCache로 캐시됩니다.

댓글0

로그인 후 댓글을 작성할 수 있습니다.
10. 마이페이지 마이페이지 구조 2026.05.10
31
전체 회원
503
전체 게시글
775
전체 댓글
442
오늘 방문
33,174
전체 방문
3
현재 접속
인기글 7일 이내
최신글
최신댓글
목록