Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
1a8f259
config: 프로덕션 환경에서 로그 파일 설정 제거
AlbertImKr Dec 4, 2025
1141478
refactor: 이미지 업로드 기능 개선 및 UI 오류 수정
AlbertImKr Dec 4, 2025
573a3c8
refactor: Author 도메인에 이미지 정보 추가 및 My Posts 기능 제거
AlbertImKr Dec 5, 2025
e5361bd
refactor: ImageView에서 불필요한 인증 파라미터 제거
AlbertImKr Dec 5, 2025
2571793
refactor!: 도메인 이벤트 시스템 리팩토링 및 AggregateRoot 패턴 도입
AlbertImKr Dec 5, 2025
1f807ba
feat: MemberEvent 도메인 및 활동 기록 API 추가
AlbertImKr Dec 5, 2025
b725029
refactor: MemberEvent 서비스 분리 및 이벤트 리스너 책임 정리
AlbertImKr Dec 5, 2025
f9ad48d
feat: MemberEvent WebView 추가 및 팔로우 기능 개선
AlbertImKr Dec 6, 2025
1a91958
refactor: 테스트 코드 개선 및 TestEntityExtensions 추가
AlbertImKr Dec 6, 2025
e8d6db8
refactor: 도메인 이벤트 계층 구조 도입
AlbertImKr Dec 7, 2025
e327cc0
feat: 도메인 이벤트 계층 구조 확장 및 MemberEvent 엔티티 추가
AlbertImKr Dec 8, 2025
fffad5a
test: 도메인 이벤트 리스너 테스트 커버리지 확대 및 서비스 개선
AlbertImKr Dec 8, 2025
d725a21
feat: MemberEvent 웹 계층 완성 및 예외 처리 강화
AlbertImKr Dec 8, 2025
4884745
docs: 프로젝트 문서 전면 개편 및 상세화
AlbertImKr Dec 8, 2025
47519a6
feat(db): MemberEvent 스키마 마이그레이션 및 이미지 ID 컬럼 추가
AlbertImKr Dec 8, 2025
f346b62
docs: Update ERD
AlbertImKr Dec 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- 컬렉션 (게시글 모음, 공개/비공개 설정)
- 프로필 페이지 (권한별 UI 분리)
- 추천 사용자 (무작위 선택 기반)
- **활동 기록 (MemberEvent)**: 회원의 모든 활동 자동 추적 및 알림

### 🏆 신뢰도 시스템

Expand All @@ -50,6 +51,15 @@
- 프로필 수정 (닉네임, 소개, 프로필 주소)
- 회원 상태 관리 (PENDING → ACTIVE → DEACTIVATED)

### 🎯 도메인 이벤트 시스템

- **AggregateRoot 패턴**: 모든 도메인 엔티티에서 이벤트 발행
- **이벤트 리스너**: 도메인 이벤트 기반 비동기 처리
- MemberEvent 자동 생성 (친구 요청, 게시글 작성, 댓글 등)
- 이메일 알림 발송 (회원가입 인증, 비밀번호 재설정)
- **이벤트 타입**: 친구/게시물/댓글/프로필/시스템 (총 10가지)
- **읽음 상태 관리**: 읽은/읽지 않은 이벤트 분리 조회

## 🛠 기술 스택

**Backend**
Expand Down Expand Up @@ -86,6 +96,7 @@
- `Hexagonal Architecture` (Ports & Adapters)
- `DDD` (Domain-Driven Design)
- `Clean Architecture` (의존성 역전)
- `Event-Driven Architecture` (도메인 이벤트 기반 비동기 처리)

**Frontend**

Expand All @@ -101,15 +112,15 @@

### 📈 성과 지표

| 항목 | 성과 |
|---------------|-----------------------------|
| **배포 자동화** | 배포 시간 83% 단축 (30분 → 5분) |
| **코드 품질** | SonarCloud 커버리지 80%+ 유지 |
| **테스트 코드** | 약 15,000줄 (단위/통합/API 테스트) |
| **아키텍처** | 헥사고날 아키텍처 + DDD 완벽 적용 |
| **이미지 시스템** | S3 Presigned URL로 서버 부하 최소화 |
| **DB 마이그레이션** | Flyway 기반 자동 스키마 버전 관리 |
| **문서화** | 2,800줄+ 기술 문서 |
| 항목 | 성과 |
|---------------|------------------------------|
| **배포 자동화** | 배포 시간 83% 단축 (30분 → 5분) |
| **코드 품질** | SonarCloud 커버리지 80%+ 유지 |
| **테스트 코드** | 약 15,000줄 (단위/통합/API 테스트) |
| **아키텍처** | 헥사고날 + DDD + Event-Driven 적용 |
| **이미지 시스템** | S3 Presigned URL로 서버 부하 최소화 |
| **DB 마이그레이션** | Flyway 기반 자동 스키마 버전 관리 |
| **문서화** | 2,800줄+ 기술 문서 |

### ☁️ 클라우드 아키텍처

Expand Down
229 changes: 229 additions & 0 deletions docs/API_DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ RMRT는 RESTful API와 WebView 기반의 하이브리드 구조를 제공합니
- [컬렉션 게시글 추가/제거](#컬렉션-게시글-추가제거)
- [컬렉션 상세 조회](#컬렉션-상세-조회)

### 🔔 회원 이벤트 관련 API (NEW!)

- [내 이벤트 목록 조회](#내-이벤트-목록-조회)
- [읽지 않은 이벤트 수 조회](#읽지-않은-이벤트-수-조회)
- [이벤트 읽음 처리](#이벤트-읽음-처리)
- [이벤트 일괄 읽음 처리](#이벤트-일괄-읽음-처리)

---

## 👥 멤버 관련 API
Expand Down Expand Up @@ -899,3 +906,225 @@ API 테스트는 다음 명령으로 실행할 수 있습니다:

- 이 프로젝트는 REST API와 WebView가 혼합된 구조입니다
- JSON API는 `/api/*` 경로를 사용합니다

---

## 🔔 회원 이벤트 관련 API

### 내 이벤트 목록 조회

현재 로그인한 사용자의 이벤트 목록을 조회합니다.

```http
GET /api/events/me?isRead={isRead}&page={page}&size={size}
```

**Query Parameters:**

| 파라미터 | 타입 | 필수 | 설명 | 기본값 |
|----------|---------|----|---------------------------------------------------------|-----------|
| `isRead` | Boolean | ✗ | 읽음 여부 필터 (`true`: 읽은 이벤트만, `false`: 읽지 않은 이벤트만, 생략: 전체) | null (전체) |
| `page` | Integer | ✗ | 페이지 번호 (0부터 시작) | 0 |
| `size` | Integer | ✗ | 페이지당 이벤트 수 | 20 |

**응답 예시 (200 OK):**

```json
{
"events": [
{
"id": 123,
"eventType": "FRIEND_REQUEST_RECEIVED",
"title": "새로운 친구 요청",
"message": "홍길동님이 친구 요청을 보냈습니다",
"isRead": false,
"createdAt": "2025-12-08T10:30:00",
"relatedMemberId": 456,
"relatedMemberNickname": "홍길동",
"relatedMemberProfileImageUrl": "https://example.com/profile.jpg",
"relatedPostId": null,
"relatedCommentId": null
},
{
"id": 122,
"eventType": "POST_COMMENTED",
"title": "새로운 댓글",
"message": "내 게시물에 댓글이 달렸습니다",
"isRead": false,
"createdAt": "2025-12-08T09:15:00",
"relatedMemberId": 789,
"relatedMemberNickname": "김철수",
"relatedMemberProfileImageUrl": "https://example.com/profile2.jpg",
"relatedPostId": 100,
"relatedCommentId": 50
}
],
"totalElements": 42,
"totalPages": 3,
"currentPage": 0,
"size": 20,
"hasNext": true
}
```

**이벤트 타입 (MemberEventType):**

| 이벤트 타입 | 설명 |
|---------------------------|----------------|
| `FRIEND_REQUEST_SENT` | 친구 요청을 보냈습니다 |
| `FRIEND_REQUEST_RECEIVED` | 친구 요청을 받았습니다 |
| `FRIEND_REQUEST_ACCEPTED` | 친구 요청이 수락되었습니다 |
| `FRIEND_REQUEST_REJECTED` | 친구 요청이 거절되었습니다 |
| `FRIENDSHIP_TERMINATED` | 친구 관계가 해제되었습니다 |
| `POST_CREATED` | 새 게시물을 작성했습니다 |
| `POST_DELETED` | 게시물을 삭제했습니다 |
| `POST_COMMENTED` | 게시물에 댓글이 달렸습니다 |
| `COMMENT_CREATED` | 댓글을 작성했습니다 |
| `COMMENT_DELETED` | 댓글을 삭제했습니다 |
| `COMMENT_REPLIED` | 대댓글이 달렸습니다 |
| `PROFILE_UPDATED` | 프로필이 업데이트되었습니다 |
| `ACCOUNT_ACTIVATED` | 계정이 활성화되었습니다 |
| `ACCOUNT_DEACTIVATED` | 계정이 비활성화되었습니다 |

**오류 응답:**

- `401 Unauthorized`: 인증되지 않은 사용자

---

### 읽지 않은 이벤트 수 조회

현재 로그인한 사용자의 읽지 않은 이벤트 수를 조회합니다.

```http
GET /api/events/me/unread-count
```

**응답 예시 (200 OK):**

```json
{
"unreadCount": 5
}
```

**오류 응답:**

- `401 Unauthorized`: 인증되지 않은 사용자

---

### 이벤트 읽음 처리

특정 이벤트를 읽음으로 표시합니다.

```http
PUT /api/events/{eventId}/mark-as-read
```

**Path Parameters:**

| 파라미터 | 타입 | 설명 |
|-----------|------|--------|
| `eventId` | Long | 이벤트 ID |

**응답 예시 (204 No Content):**

이벤트가 성공적으로 읽음 처리되었습니다. 응답 본문 없음.

**오류 응답:**

- `401 Unauthorized`: 인증되지 않은 사용자
- `403 Forbidden`: 다른 사용자의 이벤트에 접근 시도
- `404 Not Found`: 존재하지 않는 이벤트 ID

---

### 이벤트 일괄 읽음 처리

여러 이벤트를 한 번에 읽음으로 표시합니다.

```http
PUT /api/events/me/mark-all-as-read
```

**Request Body:**

```json
{
"eventIds": [
123,
124,
125,
126
]
}
```

**응답 예시 (204 No Content):**

모든 이벤트가 성공적으로 읽음 처리되었습니다. 응답 본문 없음.

**오류 응답:**

- `401 Unauthorized`: 인증되지 않은 사용자
- `403 Forbidden`: 다른 사용자의 이벤트 포함 시

---

### 이벤트 프래그먼트 조회 (WebView)

HTMX를 사용한 동적 이벤트 목록 조회입니다.

```http
GET /events/fragment/list?isRead={isRead}&page={page}
```

**Query Parameters:**

| 파라미터 | 타입 | 필수 | 설명 | 기본값 |
|----------|---------|----|----------|-----------|
| `isRead` | Boolean | ✗ | 읽음 여부 필터 | null (전체) |
| `page` | Integer | ✗ | 페이지 번호 | 0 |

**응답:** HTML 프래그먼트 (이벤트 목록)

**사용 예시 (HTMX):**

```html

<div hx-get="/events/fragment/list"
hx-trigger="load"
hx-target="#event-list">
</div>
```

---

## 🔔 이벤트 사용 가이드

### 실시간 알림

이벤트는 도메인 이벤트 발생 시 자동으로 생성됩니다:

1. **친구 요청 발송**: 발신자와 수신자 모두에게 이벤트 생성
2. **게시물 작성**: 작성자에게 이벤트 생성
3. **댓글 작성**: 작성자와 게시물 작성자에게 이벤트 생성

### 폴링 vs 웹소켓

현재 버전은 **폴링 방식**을 사용합니다:

- 클라이언트가 주기적으로 `/api/events/me/unread-count` 호출
- 권장 폴링 간격: 30초

**향후 개선 (WebSocket):**

- 실시간 알림 푸시
- 서버 부하 감소
- 즉각적인 사용자 경험

### 이벤트 자동 삭제

- **읽은 이벤트**: 90일 후 자동 삭제
- **읽지 않은 이벤트**: 무기한 보관
Loading