Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
86 changes: 16 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@

---

## 🎯 Real Match가 제공하는 핵심 기능
## Real Match가 제공하는 핵심 기능

- 📌 **매칭 알고리즘 기반 추천**
- 📊 **매칭 진단 대시보드**
- 📅 **협업 상태 추적 및 일정 관리 로직**
- 💬 **실시간 커뮤니케이션 및 알림 시스템**
- **매칭 알고리즘 기반 추천**
- **매칭 진단 대시보드**
- **협업 상태 추적 및 일정 관리 로직**
- **실시간 커뮤니케이션 및 알림 시스템**

---

Expand All @@ -26,15 +26,15 @@ Real Match는 위 기능을 통합 제공하여 브랜드와 인플루언서 간

단순 매칭을 넘어,

> 👉 **데이터 기반 분석 + 협업 프로세스 통합 관리**
> **태그 기반 매칭 + 채팅 기반 협엽 진행**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

'협엽'은 '협업'의 오타로 보입니다. 수정하는 것이 좋겠습니다.

Suggested change
> **태그 기반 매칭 + 채팅 기반 협엽 진행**
> **태그 기반 매칭 + 채팅 기반 협업 진행**


를 통해 보다 정교하고 효율적인 브랜드 마케팅 환경을 구축하는 것을 목표로 합니다.

## 🧩 주요 기능
## 주요 기능

| 기능 | 설명 |
|------------------|-----------------------------|
| 📝 **매칭 검사** | 매칭 알고리즘 기반 브랜드 추천 |
| 📝 **매칭 검사** | 매칭 알고리즘 기반 브랜드 및 캠페인 추천 |
| 🔗 **매칭 진단 대시보드** | 브랜드 · 인플루언서간의 매칭 현황 관리 |
| 📆 **협업 일정 관리** | 일정 및 협업 관리 캘린더 |
| 📆 **실시간 채팅 기능** | 브랜드·인플루언서 간 실시간 메시지 및 파일 공유 |
Expand All @@ -43,16 +43,16 @@ Real Match는 위 기능을 통합 제공하여 브랜드와 인플루언서 간
| 👤 **회원 관리** | JWT 기반 회원가입 / 소셜 로그인 |
| 🖼️ **프로필 관리** | 마이페이지에서 프로필 및 관심사 수정 |

---

## 🏛 아키텍쳐 구조

## 아키텍쳐 구조

![img_3.png](docs/images/img_3.png)


---

## 👩‍💻 RealMatch Spring Developers
## RealMatch Spring Developers

<div >

Expand All @@ -65,15 +65,7 @@ Real Match는 위 기능을 통합 제공하여 브랜드와 인플루언서 간
</div>


## 👥 Detailed Responsibilities
<p align="center">
<sub>🖱️ 좌우로 스크롤해서 내용을 확인해주세요.</sub>
</p>
<div style="display: flex; gap: 20px; overflow-x: auto; padding: 10px 0;">

<div style="min-width: 320px; border: 1px solid #ddd; padding: 18px; border-radius: 12px;">


## Detailed Responsibilities

### 👨‍💻 고경수
**Backend Developer**
Expand All @@ -83,10 +75,6 @@ Real Match는 위 기능을 통합 제공하여 브랜드와 인플루언서 간
- 사용자 정보 관리 및 마이페이지 API 구현
- Role 기반 접근 제어 처리

</div>

<div style="min-width: 320px; border: 1px solid #ddd; padding: 18px; border-radius: 12px;">

### 👩‍💻 박지영
**Backend Developer / API Lead**

Expand All @@ -96,10 +84,6 @@ Real Match는 위 기능을 통합 제공하여 브랜드와 인플루언서 간
- 브랜드-캠페인 조회 및 좋아요 기능 구현
- Docker 기반 CD 구축 및 서버 배포 자동화

</div>

<div style="min-width: 320px; border: 1px solid #ddd; padding: 18px; border-radius: 12px;">

### 👩‍💻 여채현
**Backend Developer**

Expand All @@ -108,60 +92,23 @@ Real Match는 위 기능을 통합 제공하여 브랜드와 인플루언서 간
- RabbitMQ 기반 비동기 알림 이벤트 처리
- Firebase · Email 연동 알림 시스템 구현

</div>

<div style="min-width: 320px; border: 1px solid #ddd; padding: 18px; border-radius: 12px;">

### 👩‍🎨 이예림
**Backend Developer**

- 브랜드 도메인 CRUD API 설계 및 구현
- 브랜드 목록 · 상세 · 요약 조회 API 개발
- 브랜드 좋아요 토글 및 협찬 제품 조회 기능 구현

</div>

<div style="min-width: 320px; border: 1px solid #ddd; padding: 18px; border-radius: 12px;">

### 👨‍💻 정윤철
**Backend Developer / Algorithm Lead**

- 사용자·캠페인 태그 기반 매칭 점수 계산 알고리즘 설계
- 매칭 알고리즘 기반 추천 API 설계 및 구현
- Redis 캐싱을 활용한 조회 성능 개선
- Docker 기반 서버 환경 구성 및 CI 자동화 구축
- 코드 컨벤션 관리
- HAProxy를 이용한 서버 보안 관리

</div>

</div>

---

## 📂 Project Structure

```yml
BE
├── global (공통 설정, 예외 처리, 유틸 등)
└── user
├── presentation (외부와의 접점)
│ ├── UserController.java
│ └── dto (해당 도메인 전용 데이터 전송 객체)
│ ├── request
│ │ └── UserRequest.java
│ └── response
│ └── UserResponse.java
├── application (비즈니스 로직의 흐름 제어)
│ └── service
│ ├── UserService.java (Interface)
│ └── UserServiceImpl.java
└── domain (핵심 비즈니스 규칙 및 엔티티)
├── entity
│ └── UserEntity.java
└── repository
└── UserRepository.java

```
---

## 🛠 Tech Stack

Expand All @@ -178,16 +125,15 @@ BE

### Infra
- Docker / Docker Compose
- AWS EC2
- AWS RDS
- Gabia Cloud
- AWS S3 / CloudFront
- HAProxy

### Communication
- WebSocket (채팅)
- Firebase / Email (알림)
- RabbitMQ

---

## 📏 코드 컨벤션

Expand Down
14 changes: 14 additions & 0 deletions haproxy/fail2ban-haproxy.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Definition]

# 로그 형식: 2026-02-15T17:06:02.613Z IP:PORT [haproxy_date] frontend backend/server timers status ...
# docker logs --timestamps 가 ISO8601 타임스탬프를 줄 시작에 추가
# fail2ban이 타임스탬프 제거 후 공백이 남으므로 ^\s* 사용

# 403 거부 (블랙리스트, ACL 거부)
# 429 Rate Limit 초과
# 취약점 스캔 패턴 (.php, cgi-bin, .env, wp-admin, .git, shell, passwd 등)
failregex = ^\s*<HOST>:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+403\s
^\s*<HOST>:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+429\s
^\s*<HOST>:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+\d+\s.*"(GET|POST|PUT|DELETE|HEAD|OPTIONS)\s+.*(\.php|cgi-bin|\.env|wp-admin|wp-login|\.git|shell|passwd|\.asp)
Comment on lines +10 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

현재 취약점 스캔을 위한 정규식이 너무 광범위하여 정상적인 요청을 차단할 오탐(false positive)의 가능성이 있습니다. 예를 들어, URL 파라미터에 .git과 같은 문자열이 포함된 경우에도 차단될 수 있습니다. 요청 경로(path)에 대해서만 패턴을 검사하고, 요청 문자열의 끝을 명확히 하는 등 정규식을 좀 더 정교하게 수정하여 오탐을 줄이는 것을 권장합니다.

failregex = ^\s*<HOST>:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+403\s
            ^\s*<HOST>:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+429\s
            ^\s*<HOST>:\d+\s+\[.*\]\s+\S+\s+\S+\s+\S+\s+\d+\s.*"(?:GET|POST|PUT|DELETE|HEAD|OPTIONS)\s+[^\"]*?(\.php|/cgi-bin|/\.env|/wp-admin|/wp-login|/\.git|/shell|/passwd|/\.asp)[^\"]*"


ignoreregex =
12 changes: 12 additions & 0 deletions haproxy/haproxy-log.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Unit]
Description=Stream HAProxy Docker logs to file
After=docker.service

[Service]
Type=simple
ExecStart=/bin/bash -c '/usr/bin/docker logs -f --timestamps --tail=0 haproxy 2>&1 | sed -u "s/\([0-9]\{3\}\)[0-9]*Z/\1Z/" >> /var/log/haproxy/access.log'
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
57 changes: 55 additions & 2 deletions haproxy/haproxy.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,79 @@ global
maxconn 4096
daemon

# 서버 정보 숨기기
tune.h2.max-concurrent-streams 128

defaults
log global
mode http
option httplog
option dontlognull
option forwardfor
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
timeout http-request 10s
timeout http-keep-alive 2s

# 기본 에러 페이지에서 HAProxy 버전 숨기기
errorfile 403 /usr/local/etc/haproxy/errors/403.http

frontend http_in
bind *:80
# HTTP를 HTTPS로 리다이렉트

acl blacklisted src -f /usr/local/etc/haproxy/blacklist.lst
http-request deny if blacklisted

stick-table type ip size 100k expire 30s store http_req_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 50 }

http-request redirect scheme https unless { ssl_fc }

frontend https_in
bind *:443 ssl crt /etc/ssl/certs/realmatch.pem
bind *:443 ssl crt /etc/ssl/certs/realmatch.pem alpn h2,http/1.1 ssl-min-ver TLSv1.2 ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305

acl blacklisted src -f /usr/local/etc/haproxy/blacklist.lst
http-request deny if blacklisted

stick-table type ip size 100k expire 30s store http_req_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 50 }

acl valid_host hdr(host) -i api.realmatch.co.kr
http-request deny deny_status 403 unless valid_host

http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
http-response set-header X-Frame-Options "SAMEORIGIN"
http-response set-header X-Content-Type-Options "nosniff"
http-response set-header X-XSS-Protection "1; mode=block"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

X-XSS-Protection 헤더는 현재 대부분의 모던 브라우저에서 지원이 중단되었으며, 오히려 특정 상황에서 보안 취약점을 유발할 수 있어 사용이 권장되지 않습니다. 더 강력하고 표준적인 XSS 방어는 Content-Security-Policy 헤더를 통해 구현할 수 있습니다. 우선 이 헤더를 제거하는 것을 권장합니다.

http-response set-header Referrer-Policy "strict-origin-when-cross-origin"
http-response set-header Permissions-Policy "camera=(), microphone=(), geolocation=()"

http-response del-header Server
http-response del-header X-Powered-By

http-request deny if { req.hdr_cnt(content-length) gt 1 }

default_backend spring_servers

listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 5s
stats show-legends
stats show-node
stats auth admin:realmatch2026!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The HAProxy statistics page is configured with a hardcoded username and password (admin:realmatch2026!). Hardcoding credentials in configuration files committed to version control is a major security risk, exposing them to anyone with repository access. This could allow an attacker to monitor backend server health, view traffic, and potentially disable/enable servers, leading to denial of service. Additionally, line 71 (stats admin if TRUE) grants administrative privileges to any authenticated user. It is critical to replace these hardcoded credentials with environment variables or other secure injection methods.

    stats auth ${STATS_USER}:${STATS_PASSWORD}

stats admin if TRUE

frontend prometheus
bind *:8405
mode http
http-request use-service prometheus-exporter if { path /metrics }
no log

backend spring_servers
balance roundrobin
option httpchk GET /actuator/health
Expand Down
16 changes: 16 additions & 0 deletions haproxy/iptables-docker.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Fail2ban action for Docker (uses DOCKER-USER chain)
[Definition]

actionstart = iptables -N f2b-<name> 2>/dev/null
iptables -A f2b-<name> -j RETURN
iptables -I DOCKER-USER -j f2b-<name>

actionstop = iptables -D DOCKER-USER -j f2b-<name>
iptables -F f2b-<name>
iptables -X f2b-<name>

actioncheck = iptables -n -L DOCKER-USER | grep -q 'f2b-<name>'

actionban = iptables -I f2b-<name> 1 -s <ip> -j DROP

actionunban = iptables -D f2b-<name> -s <ip> -j DROP
8 changes: 8 additions & 0 deletions haproxy/jail-haproxy.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[haproxy-attack]
enabled = true
filter = haproxy-attack
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

fail2ban jail 설정에서 filterhaproxy-attack으로 지정했습니다. fail2ban이 이 필터를 찾으려면, 필터 정의 파일의 이름이 haproxy-attack.conf (또는 .local)이어야 합니다. 하지만 이 PR에서 추가된 필터 파일의 이름은 fail2ban-haproxy.conf입니다. 이 이름 불일치로 인해 해당 jail이 정상적으로 동작하지 않을 것입니다. fail2ban-haproxy.conf 파일의 이름을 haproxy-attack.conf로 변경하여 filter 설정과 일치시켜야 합니다.

logpath = /var/log/haproxy/access.log
action = iptables-docker[name=haproxy]
maxretry = 3
findtime = 300
bantime = 3600
9 changes: 9 additions & 0 deletions haproxy/logrotate-haproxy
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/var/log/haproxy/access.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}
Loading