Skip to content

Latest commit

 

History

History
1126 lines (936 loc) · 31.8 KB

File metadata and controls

1126 lines (936 loc) · 31.8 KB

Notification API 명세서

개요

알림 조회 및 확인 기능을 제공하는 API입니다. 사용자의 팔로우, 좋아요, 댓글, 메시지 관련 알림을 관리하며, 실시간 푸시 알림과 Socket.io와 연동됩니다. 본인의 알림만 조회할 수 있으며, 타인의 알림 대상이 되는 API는 제공되지 않습니다.

기본 정보

  • Base URL: http://localhost:3001
  • 인증 방식: JWT Bearer Token (Authorization 헤더) 또는 HTTP-only 쿠키
  • 응답 형식: JSON
  • 페이지네이션 방식: Offset-based 페이지네이션
  • 캐싱: Redis 캐시 활용 (읽지 않은 알림 개수 캐시)

공통 응답 형식

성공 응답

{
  "code": 200,
  "message": "요청 성공 메시지",
  "data": {
    // 응답 데이터
  }
}

오류 응답

{
  "code": 400,
  "message": "오류 메시지",
  "errors": [
    {
      "field": "필드명",
      "message": "상세 오류 메시지"
    }
  ]
}

인증 방식

웹 (PC)

쿠키에서 자동으로 accessToken 추출

Cookie: accessToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

모바일

Authorization 헤더에 Bearer 토큰 포함

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Notification API

1. 알림 목록 조회

GET /notifications

본인의 알림 목록을 페이지네이션과 필터링으로 조회합니다.

Query Parameters

파라미터 타입 필수 기본값 설명
offset number 선택 0 건너뛸 알림 수 (페이지네이션)
limit number 선택 20 조회할 최대 알림 수 (최대: 100)
type string 선택 - 알림 타입 필터 ("followed", "feed_liked", "post_liked", "feed_commented", "post_commented", "feed_created", "post_created", "message")
isRead boolean 선택 - 읽음 상태 필터 (true: 읽음, false: 안읽음)

Parameter Validation Rules

📋 offset:

  • 범위: 0 이상의 정수
  • 역할: 다음 알림 목록을 가져올 시작점을 지정
  • 예시: offset=0 (첫 페이지), offset=20 (두 번째 페이지)

📋 limit:

  • 범위: 1-100 사이의 정수 (기본값: 20)
  • 역할: 한 번에 가져올 알림 최대 개수 지정
  • 목적: 성능 최적화 및 UI 로딩 속도 개선
  • 예시: limit=20 (페이지당 20개), limit=50 (페이지당 50개)

📋 type:

  • 허용값: "followed" | "feed_liked" | "post_liked" | "feed_commented" | "post_commented" | "feed_created" | "post_created" | "message"
  • 역할: 특정 타입의 알림만 조회
  • 예시: type=followed (팔로우 알림만), type=post_liked (게시물 좋아요 알림만)

📋 isRead:

  • 허용값: true(읽음) | false(안읽음)
  • 역할: 읽음 상태에 따른 알림 필터링
  • 예시: isRead=false (안 읽은 알림만), isRead=false (읽은 알림만)

Request Examples

# 전체 알림 목록 조회 (기본값 사용)
GET /notifications

# 다음 페이지 조회 (offset=20, limit=10)
GET /notifications?offset=20&limit=10

# 안 읽은 팔로우 알림만 조회
GET /notifications?type=follow&isRead=false&limit=50

# 읽음 상태에 관계없이 좋아요 알림 모두 조회
GET /notifications?type=like

Response Examples

✅ 기본 응답 (전체 알림 목록 조회 성공):

{
  "code": 200,
  "message": "알림 목록을 조회했습니다.",
  "data": {
    "notifications": [
      {
        "id": 12345,
        "type": "followed",
        "sender": {
          "id": 2,
          "nickname": "홍길동",
          "profile_img": "http://localhost:3001/uploads-profile/2024/01/user2.jpg"
        },
        "message": "홍길동님이 당신을 팔로우했습니다.",
        "reference_id": null,
        "is_read": false,
        "created_at": "2024-01-15T14:30:25.000Z"
      },
      {
        "id": 12344,
        "type": "post_liked",
        "sender": {
          "id": 3,
          "nickname": "김철수",
          "profile_img": null
        },
        "message": "김철수님이 회원님의 게시물을 좋아합니다.",
        "reference_id": 456,
        "is_read": true,
        "created_at": "2024-01-15T14:25:12.000Z"
      },
      {
        "id": 12343,
        "type": "post_commented",
        "sender": {
          "id": 4,
          "nickname": "이영희",
          "profile_img": "http://localhost:3001/uploads-profile/2024/01/user4.jpg"
        },
        "message": "이영희님이 회원님의 게시물에 댓글을 남겼습니다.",
        "reference_id": 789,
        "is_read": false,
        "created_at": "2024-01-15T14:20:33.000Z"
      }
    ],
    "total_count": 150,
    "unread_count": 12,
    "has_more": true
  }
}

✅ 필터링 적용 (안 읽은 팔로우 알림만 조회):

{
  "code": 200,
  "message": "알림 목록을 조회했습니다.",
  "data": {
    "notifications": [
      {
        "id": 12345,
        "type": "followed",
        "sender": {
          "id": 2,
          "nickname": "홍길동",
          "profile_img": "http://localhost:3001/uploads-profile/2024/01/user2.jpg"
        },
        "message": "홍길동님이 당신을 팔로우했습니다.",
        "reference_id": null,
        "is_read": false,
        "created_at": "2024-01-15T14:30:25.000Z"
      }
    ],
    "total_count": 5,
    "unread_count": 12,
    "has_more": false
  }
}

Response Fields Detail

📊 Root Data Object:

Field Type Required Description
notifications array 알림 데이터 배열 (최대 limit 개수만큼)
total_count number 해당 필터 조건의 전체 알림 개수
unread_count number 전체 읽지 않은 알림 개수 (필터 무관)
has_more boolean 다음 페이지 존재 여부 (offset + limit < total_count)

📝 Notification Item Object:

Field Type Required Description
id number 알림 고유 ID (Socket.io 실시간 이벤트에 사용)
type string 알림 타입 ("followed" | "feed_liked" | "post_liked" | "feed_commented" | "post_commented" | "feed_created" | "post_created" | "message")
sender object 알림을 보낸 사용자 정보
message string 알림 메시지 텍스트
reference_id number | null 참조 리소스 ID (게시글/피드 등)
is_read boolean 읽음 상태 (클라이언트 UI 렌더링용)
created_at string 알림 생성 시간 (ISO 8601 포맷)

👤 Sender Object (알림 발신자 정보):

Field Type Required Description
id number 발신자 사용자 고유 ID
nickname string 발신자 닉네임
profile_img string | null 발신자 프로필 이미지 URL (없으면 null)

알림 타입별 Reference ID 설명

알림 타입 reference_id 의미 사용처
followed null 팔로우는 직접적인 리소스 참조 불필요
feed_liked 피드 ID 좋아요 대상 피드 식별
post_liked 게시글 ID 좋아요 대상 게시글 식별
feed_commented 피드 ID 댓글 달린 피드 식별
post_commented 게시글 ID 댓글 달린 게시글 식별
feed_created 피드 ID 새로 작성된 피드 ID
post_created 게시글 ID 새로 작성된 게시글 ID
message 채팅방 ID 메시지가 도착한 채팅방 식별

필드별 예시 데이터

message 필드 (알림 메시지):

// 팔로우 알림
"홍길동님이 당신을 팔로우했습니다."""

// 좋아요 알림
"김철수님이 회원님의 게시물을 좋아합니다."""

// 댓글 알림
"이영희님이 회원님의 게시물에 댓글을 남겼습니다."""

// 게시글 알림
"박민수님이 새로운 게시글을 작성했습니다."""

// 메시지 알림
"홍길동님이 메시지를 보냈습니다.""

클라이언트 처리 가이드

📱 React Component 예시:

const NotificationList = () => {
  const [notifications, setNotifications] = useState([]);
  const [unreadCount, setUnreadCount] = useState(0);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);

  // 알림 목록 로드
  const loadNotifications = async (offset = 0, type = '', showRead = null) => {
    try {
      setLoading(true);

      const params = new URLSearchParams();
      params.set('offset', offset.toString());
      params.set('limit', '20');

      if (type) params.set('type', type);
      if (showRead !== null) params.set('isRead', showRead.toString());

      const response = await fetch(`/notifications?${params}`, {
        headers: { 'Authorization': `Bearer ${token}` }
      });

      const result = await response.json();

      if (response.ok) {
        const { notifications: newNotifications, unread_count: unreadCount, has_more: hasMore } = result.data;

        if (offset === 0) {
          setNotifications(newNotifications);
        } else {
          setNotifications(prev => [...prev, ...newNotifications]);
        }

        setUnreadCount(unreadCount);
        setHasMore(hasMore);
      } else {
        console.error('알림 조회 실패:', result.message);
      }
    } catch (error) {
      console.error('네트워크 오류:', error);
    } finally {
      setLoading(false);
    }
  };

  // 컴포넌트 마운트 시 로드
  useEffect(() => {
    loadNotifications();
  }, []);

  // 더보기 버튼 핸들러
  const handleLoadMore = () => {
    const nextOffset = notifications.length;
    loadNotifications(nextOffset);
  };

  // 필터링 핸들러
  const handleFilter = (type, showRead) => {
    loadNotifications(0, type, showRead);
    setNotifications([]);
  };

  return (
    <div className="notification-list">
      <div className="notification-header">
        <h2>알림 <span className="unread-badge">{unreadCount}</span></h2>
        <div className="filter-buttons">
          <button onClick={() => handleFilter('follow', false)}>안 읽은 팔로우</button>
          <button onClick={() => handleFilter('like', null)}>전체 좋아요</button>
          <button onClick={() => handleFilter('', null)}>모두</button>
        </div>
      </div>

      <div className="notifications">
        {notifications.map(notification => (
          <NotificationItem
            key={notification.id}
            notification={notification}
            onMarkAsRead={(id) => markNotificationAsRead(id)}
          />
        ))}

        {hasMore && (
          <button
            onClick={handleLoadMore}
            disabled={loading}
            className="load-more-btn"
          >
            {loading ? '로딩 중...' : '더보기'}
          </button>
        )}
      </div>
    </div>
  );
};

2. 알림 읽음 처리

PATCH /notifications/read

특정 알림들을 읽음 상태로 업데이트합니다.

Request Body

{
  "notificationIds": [12345, 12344, 12343]
}

Request Body Field Descriptions

Field Type Required Description
notificationIds number[] 읽음 처리할 알림 ID 배열

Validation Rules

📋 notificationIds:

  • 형식: 숫자 배열 (examples: [123, 124, 125])
  • 개수 제한: 1개 ~ 무제한
  • 각 요소: 양의 정수 (알림 ID)
  • 중복 허용: 동일 ID 여러 번 포함 가능 (중복 제거 처리)

🔍 Validation Examples:

// ✅ 올바른 예시들
[123, 456, 789]
[999]
[1, 1, 2]  // 중복 허용, 내부적으로 중복 제거됨

// ❌ 잘못된 예시들
[]           // 빈 배열 (최소 1개 필요)
"123"        // 문자열 (배열이어야 함)
[0]          // 0은 유효하지 않은 ID
[-1, 123]    // 음수는 유효하지 않은 ID
["abc"]      // 문자열 요소 (숫자만 허용)
null         // null 허용하지 않음

Request Examples

✅ 기본 사용 (여러 알림들이 읽음 처리):

# 여러 알림 읽음 처리
curl -X PATCH http://localhost:3001/notifications/read \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_token_here" \
  -d '{
    "notificationIds": [12345, 12346, 12347]
  }'

✅ 단일 알림 읽음 처리:

# 하나의 알림만 읽음 처리
curl -X PATCH http://localhost:3001/notifications/read \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_token_here" \
  -d '{
    "notificationIds": [12345]
  }'

Response Examples

✅ 정상 응답:

{
  "code": 200,
  "message": "2개의 알림을 읽음 처리했습니다.",
  "data": {
    "affectedRows": 2
  }
}

✅ 모두 새로운 알림인 경우:

{
  "code": 200,
  "message": "3개의 알림을 읽음 처리했습니다.",
  "data": {
    "affectedRows": 3
  }
}

✅ 이미 읽은 알림이 포함된 경우:

{
  "code": 200,
  "message": "2개의 알림을 읽음 처리했습니다.",
  "data": {
    "affectedRows": 2  // 이미 읽은 1개는 카운트되지 않음
  }
}

Response Fields Detail

Field Type Required Description
affectedRows number 실제로 읽음 처리된 알림 개수 (0 이상 정수)

클라이언트 처리 논리

const markNotificationsAsRead = async (notificationIds) => {
  try {
    const response = await fetch('/notifications/read', {
      method: 'PATCH',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        notificationIds: notificationIds
      })
    });

    const result = await response.json();

    if (response.ok) {
      const { affectedRows } = result.data;

      // UI 업데이트: 알림들을 읽음으로 표시
      updateNotificationsReadStatus(notificationIds, true);

      // 전역 읽지 않은 알림 개수 업데이트
      updateGlobalUnreadCount(-affectedRows);

      // 성공 메시지 표시
      showToast(`${affectedRows}개의 알림을 읽음 처리했습니다.`);
    } else {
      // 에러 처리
      console.error('읽음 처리 실패:', result.message);
      showErrorToast(result.message);
    }
  } catch (error) {
    console.error('네트워크 에러:', error);
    showErrorToast('알림 읽음 처리 중 오류가 발생했습니다.');
  }
};

3. 모든 알림 읽음 처리

PATCH /notifications/read-all

모든 읽지 않은 알림을 일괄 읽음 처리합니다.

Request Body: 없음

이 API는 Request Body가 필요하지 않습니다.

Request Example

# 모든 읽지 않은 알림을 읽음 처리
curl -X PATCH http://localhost:3001/notifications/read-all \
  -H "Authorization: Bearer your_token_here"

Response Example

{
  "code": 200,
  "message": "모든 알림을 읽음 처리했습니다.",
  "data": {
    "affectedRows": 5
  }
}

Response Fields Detail

Field Type Required Description
affectedRows number 읽음 처리된 알림 개수 (읽지 않은 알림만 카운트)

클라이언트 처리 예시

📱 React Hook 예시:

const useMarkAllAsRead = () => {
  const [markingAll, setMarkingAll] = useState(false);

  const markAllAsRead = useCallback(async () => {
    if (markingAll) return; // 중복 요청 방지

    try {
      setMarkingAll(true);

      const response = await fetch('/notifications/read-all', {
        method: 'PATCH',
        headers: {
          'Authorization': `Bearer ${token}`
        }
      });

      const result = await response.json();

      if (response.ok) {
        const { affectedRows } = result.data;

        // 모든 알림을 읽음으로 업데이트
        setNotifications(prev => prev.map(notification => ({
          ...notification,
          isRead: true
        })));

        // 읽지 않은 개수 0으로 리셋
        setUnreadCount(0);

        // 성공 토스트 메시지
        showToast(`총 ${affectedRows}개의 알림을 읽음 처리했습니다.`);
      } else {
        showErrorToast(result.message);
      }
    } catch (error) {
      console.error('모든 알림 읽음 처리 실패:', error);
      showErrorToast('알림 처리 중 오류가 발생했습니다.');
    } finally {
      setMarkingAll(false);
    }
  }, [markingAll, token]);

  return { markAllAsRead, markingAll };
};

4. 읽지 않은 알림 개수 조회

GET /notifications/unread-count

현재 로그인한 사용자의 전체 읽지 않은 알림 개수를 조회합니다. 상태 표시줄이나 헤더의 배지 아이콘에 표시하는 용도로 사용됩니다.

Request Parameters: 없음

이 API는 Query Parameter나 Request Body가 필요하지 않습니다.

Request Example

# 읽지 않은 알림 개수 조회
curl -X GET http://localhost:3001/notifications/unread-count \
  -H "Authorization: Bearer your_token_here"

Response Example

{
  "code": 200,
  "message": "읽지 않은 알림 개수를 조회했습니다.",
  "data": {
    "unread_count": 12
  }
}

Response Fields Detail

Field Type Required Description
unread_count number 읽지 않은 알림 개수 (0 이상 정수)

사용 시나리오 및 설계 의도

💡 주요 사용처:

// 1. 탭/헤더바에 배지 표시
<TabBarBadge>
  <BellIcon />
  {unreadCount > 0 && <Badge count={unreadCount} />}
</TabBarBadge>

// 2. 알림 버튼에 전역 상태 표시
const NotificationButton = () => {
  const { unreadCount } = useNotificationStore();

  return (
    <button className="notification-btn">
      <BellIcon />
      {unreadCount > 0 && (
        <span className="badge">{unreadCount > 99 ? '99+' : unreadCount}</span>
      )}
    </button>
  );
};

📊 캐싱 설계:

// Redux Store 예시
interface NotificationState {
  unreadCount: number;
  lastFetched: Date | null;
  isLoading: boolean;
}

// 컴포넌트 레벨 캐시 (useNotificationStore)
const useUnreadCount = () => {
  const store = useNotificationStore();
  const dispatch = useNotificationDispatch();

  useEffect(() => {
    // 5분 캐시 정책
    const CACHE_DURATION = 5 * 60 * 1000; // 5분

    if (!store.isLoading &&
        (!store.lastFetched ||
         Date.now() - store.lastFetched.getTime() > CACHE_DURATION)) {
      dispatch(fetchUnreadCount());
    }
  }, [store.lastFetched, store.isLoading, dispatch]);

  return {
    unreadCount: store.unreadCount,
    isLoading: store.isLoading
  };
};

데이터 동기화 포인트

🔄 실시간 동기화 현황:

// 1. 새로운 알림 도착 시 (Socket.io 이벤트)
notificationSocket.on('notification:follow', (notification) => {
  dispatch(incrementUnreadCount());
  showPushNotification(notification.message, notification.sender);
});

// 2. 알림 읽음 처리 시
const markAsRead = async (notificationIds) => {
  const result = await API.markNotificationsAsRead(notificationIds);

  if (result.affectedRows > 0) {
    dispatch(decrementUnreadCount(result.affectedRows));
  }
};

// 3. 로그인 시 초기 로드
const initializeUnreadCount = useCallback(async () => {
  const { unreadCount } = await API.getUnreadCount();
  dispatch(setUnreadCount(unreadCount));
}, [dispatch]);

에러 코드 및 응답 예시

HTTP 상태 코드

  • 200: 성공
  • 400: 잘못된 요청 (validation 실패, 잘못된 파라미터)
  • 401: 인증 필요
  • 404: 리소스 없음 (사용자를 찾을 수 없음)
  • 500: 서버 내부 오류

공통 에러 응답 형식

{
  "code": 400,
  "message": "오류 메시지",
  "errors": [
    {
      "field": "notificationIds",
      "message": "알림 ID는 숫자 배열이어야 합니다."
    }
  ]
}

상세 에러 코드 예시

1. 알림 목록 조회 에러들

❌ Offset 파라미터 오류:

{
  "code": 400,
  "message": "offset은 0 이상의 숫자여야 합니다.",
  "errors": [
    {
      "field": "offset",
      "message": "offset은 0 이상의 숫자여야 합니다."
    }
  ]
}

❌ Limit 파라미터 오류:

{
  "code": 400,
  "message": "limit은 1-100 사이의 값이어야 합니다.",
  "errors": [
    {
      "field": "limit",
      "message": "limit은 1-100 사이의 값이어야 합니다."
    }
  ]
}

❌ Type 파라미터 오류:

{
  "code": 400,
  "message": "알림 타입은 'followed', 'feed_liked', 'post_liked', 'feed_commented', 'post_commented', 'feed_created', 'post_created', 'message' 중 하나여야 합니다.",
  "errors": [
    {
      "field": "type",
      "message": "알림 타입은 'followed', 'feed_liked', 'post_liked', 'feed_commented', 'post_commented', 'feed_created', 'post_created', 'message' 중 하나여야 합니다."
    }
  ]
}

2. 읽음 처리 에러들

❌ notificationIds 배열 형식 오류:

{
  "code": 400,
  "message": "notificationIds는 숫자 배열이어야 합니다.",
  "errors": [
    {
      "field": "notificationIds",
      "message": "notificationIds는 숫자 배열이어야 합니다."
    }
  ]
}

❌ 빈 배열 오류:

{
  "code": 400,
  "message": "notificationIds는 최소 1개 이상이어야 합니다.",
  "errors": [
    {
      "field": "notificationIds",
      "message": "notificationIds는 최소 1개 이상이어야 합니다."
    }
  ]
}

❌ 존재하지 않는 알림 ID:

{
  "code": 200,
  "message": "0개의 알림을 읽음 처리했습니다.",
  "data": {
    "affectedRows": 0
  }
}

3. 인증 오류들

❌ 인증 토큰 없음:

{
  "code": 401,
  "message": "인증 토큰이 필요합니다.",
  "errors": []
}

❌ 유효하지 않은 토큰:

{
  "code": 401,
  "message": "유효하지 않은 토큰입니다.",
  "errors": []
}

4. 서버 오류

❌ 데이터베이스 연결 오류:

{
  "code": 500,
  "message": "알림 조회 중 서버 오류가 발생했습니다.",
  "errors": []
}

사용 예시 (Curl 커맨드)

1. 알림 목록 조회

# 웹 쿠키 인증
curl -X GET "http://localhost:3001/notifications?limit=10&type=follow" \
  --cookie "accessToken=your_jwt_token"

# 모바일 Bearer 토큰
curl -X GET "http://localhost:3001/notifications?limit=10&type=follow" \
  -H "Authorization: Bearer your_jwt_token" \
  -H "Content-Type: application/json"

2. 알림 읽음 처리

# 특정 알림 읽음 처리
curl -X PATCH http://localhost:3001/notifications/read \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_jwt_token" \
  -d '{
    "notificationIds": [12345, 12346, 12347]
  }'

# 모든 알림 읽음 처리
curl -X PATCH http://localhost:3001/notifications/read-all \
  -H "Authorization: Bearer your_jwt_token"

3. 읽지 않은 개수 조회

curl -X GET http://localhost:3001/notifications/unread-count \
  -H "Authorization: Bearer your_jwt_token"

데이터베이스 스키마

Notification 테이블 구조

CREATE TABLE Notification (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  receiver_id BIGINT NOT NULL,                    -- 수신자 ID
  sender_id BIGINT NOT NULL,                      -- 발신자 ID
  type ENUM('follow', 'like', 'comment', 'post'), -- 알림 타입
  reference_id BIGINT,                            -- 참조 리소스 ID
  message VARCHAR(255),                           -- 알림 메시지
  is_read TINYINT DEFAULT 0,                      -- 읽음 상태
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,  -- 생성 시간
  CONSTRAINT fk_notification_receiver FOREIGN KEY(receiver_id) REFERENCES User(id) ON DELETE RESTRICT,
  CONSTRAINT fk_notification_sender FOREIGN KEY(sender_id) REFERENCES User(id) ON DELETE RESTRICT
);

관계 설명

  • receiver_id: 알림을 받는 사용자 (외래키 → User.id)
  • sender_id: 알림을 보낸 사용자 (외래키 → User.id)
  • reference_id: 타입별 참조 리소스 ID
    • follow: null (팔로우는 대상 없음)
    • like: 게시글 또는 피드 ID
    • comment: 게시글 또는 피드 ID
    • post: 새 게시글 ID

제약사항

  • receiver_idsender_id (자기 자신에게 알림 불가)
  • type은 ENUM으로 제한된 값만 허용
  • 각 필드에 대한 인덱스 설정으로 빠른 조회 보장

인덱스 전략

-- 필수 인덱스들
CREATE INDEX idx_notification_receiver_created ON Notification(receiver_id, created_at DESC);
CREATE INDEX idx_notification_receiver_isread ON Notification(receiver_id, is_read);
CREATE INDEX idx_notification_type ON Notification(type);

-- 복합 조회용 인덱스
CREATE INDEX idx_notification_receiver_type_created ON Notification(receiver_id, type, created_at DESC);

성능 최적화

데이터베이스 최적화

  1. 인덱스: 수신자 ID + 생성시간 역순으로 클러스터링 인덱스
  2. 파티셔닝: 시간 기반 파티셔닝으로 대용량 데이터 처리
  3. Archive 정책: 오래된 알림 자동 삭제 (6개월 이상)

캐싱 전략

  1. 읽지 않은 개수 캐시: Redis cache (TTL: 5분)
  2. 알림 목록 캐시: 분단위 캐시 (TTL: 1분)
  3. 실시간 갱신: Socket.io 이벤트 수신 시 즉시 캐시 무효화

페이지네이션 효율화

  1. 커서 페이지네이션: ID 기반 커서로 인덱스 효율적 이용
  2. 제한 개수: 한 번에 최대 100개로 성능 부하 방지
  3. 데이터 프로젝션: 필요한 필드만 select로 트래픽 최소화

확장성 고려사항

대용량 처리

  • 바인딩된 알림 개수: 사용자당 최대 1000개 알림 유지
  • 시스템 알림: 중요하지 않은 알림은 자동 필터링
  • 알림 요약: 동일한 유형의 알림을 그룹화하여 병합

알림 설정 관리

  • 타입별 알림 켜기/끄기: 팔로우, 좋아요, 댓글 등 세부 설정
  • 푸시 알림: 브라우저 푸시와 앱 푸시 별도 설정
  • 음소거 시간대: 특정 시간대 알림 차단 기능

분산 시스템 지원

  • 알림 브로커: 대용량 알림 처리를 위한 메시지 큐 도입
  • 캐시 클러스터링: 여러 서버 간 캐시 동기화
  • 알림 스토리지: MongoDB나 Elasticsearch로 실시간 검색 지원

이제 Redis, MongoDB, RabbitMQ 등 인프라 확장은 추후 요구사항에 따라 구현되어 구조적인 확장성을 보장합니다.


Notification Settings API

추가: 알림 설정 조회 및 업데이트 기능

기존 Notification API에 알림 설정 관리를 위한 API가 추가되었습니다.


5. 알림 설정 조회

GET /notification-settings

현재 로그인한 사용자의 모든 알림 설정을 조회합니다.

Request Parameters: 없음

Request Example

curl -X GET http://localhost:3001/notification-settings \
  -H "Authorization: Bearer your_jwt_token"

Response Example

{
  "success": true,
  "data": {
    "follow_notification": true,
    "feed_like_notification": true,
    "post_like_notification": false,
    "short_like_notification": true,
    "feed_comment_notification": true,
    "post_comment_notification": true,
    "short_comment_notification": false,
    "dm_notification": true,
    "feed_created_notification": true,
    "post_created_notification": true,
    "short_created_notification": true
  },
  "message": "알림 설정을 조회했습니다."
}

Response Fields Detail

Field Type Required Description
follow_notification boolean 팔로우 알림 설정
feed_like_notification boolean 피드 좋아요 알림 설정
post_like_notification boolean 게시글 좋아요 알림 설정
short_like_notification boolean 숏츠 좋아요 알림 설정
feed_comment_notification boolean 피드 댓글 알림 설정
post_comment_notification boolean 게시글 댓글 알림 설정
short_comment_notification boolean 숏츠 댓글 알림 설정
dm_notification boolean DM 알림 설정
feed_created_notification boolean 피드 작성 알림 설정
post_created_notification boolean 게시글 작성 알림 설정
short_created_notification boolean 숏츠 작성 알림 설정

6. 알림 설정 업데이트

PUT /notification-settings

알림 설정을 부분적으로 업데이트합니다. 요청한 필드만 변경되고 나머지는 기존 설정을 유지합니다.

Request Body

업데이트할 알림 설정 필드를 JSON 객체로 전달합니다.

{
  "follow_notification": true,
  "feed_like_notification": false,
  "post_comment_notification": true
}

Request Body Field Descriptions

Field Type Required Description
follow_notification boolean 선택 팔로우 알림 설정 변경
feed_like_notification boolean 선택 피드 좋아요 알림 설정 변경
post_like_notification boolean 선택 게시글 좋아요 알림 설정 변경
short_like_notification boolean 선택 숏츠 좋아요 알림 설정 변경
feed_comment_notification boolean 선택 피드 댓글 알림 설정 변경
post_comment_notification boolean 선택 게시글 댓글 알림 설정 변경
short_comment_notification boolean 선택 숏츠 댓글 알림 설정 변경
dm_notification boolean 선택 DM 알림 설정 변경
feed_created_notification boolean 선택 피드 작성 알림 설정 변경
post_created_notification boolean 선택 게시글 작성 알림 설정 변경
short_created_notification boolean 선택 숏츠 작성 알림 설정 변경

Request Examples

✅ 부분 업데이트 - 좋아요 알림만 끄기:

curl -X PUT http://localhost:3001/notification-settings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_jwt_token" \
  -d '{
    "feed_like_notification": false,
    "post_like_notification": false,
    "short_like_notification": false
  }'

Response Example

{
  "success": true,
  "data": {
    "follow_notification": true,
    "feed_like_notification": false,
    "post_like_notification": false,
    "short_like_notification": false,
    "feed_comment_notification": true,
    "post_comment_notification": true,
    "short_comment_notification": false,
    "dm_notification": true,
    "feed_created_notification": true,
    "post_created_notification": true,
    "short_created_notification": true
  },
  "message": "알림 설정이 업데이트되었습니다."
}

Response Fields Detail

응답으로 업데이트된 모든 알림 설정의 최종 상태가 반환됩니다.


알림 설정 관리 시스템

📱 알림 타입별 세부 제어:

  • 팔로우 알림: 팔로우 요청 및 팔로우 수락/거부
  • 좋아요 알림: 피드, 게시글, 숏츠별 개별 설정
  • 댓글 알림: 피드, 게시글, 숏츠별 개별 설정
  • DM 알림: 새로운 메시지 도착
  • 작성 알림: 팔로워의 콘텐츠 작성 (피드, 게시글, 숏츠)

🔧 설정 적용 방식:

  • 실시간 적용: API 호출 즉시 설정이 반영됩니다
  • 부분 업데이트: 변경하지 않은 필드는 기존 설정 유지
  • 기본값: 신규 가입자는 모든 알림을 허용한 상태로 시작

⚡ 알림 서비스 연동:

  • 모든 알림 생성 시 사용자의 설정을 확인
  • 설정이 꺼져 있는 알림 타입은 생성하지 않음
  • 푸시 알림과 Socket.io 이벤트 모두 설정 적용됨