μ€λ¬Έ κΈ°λ° λΌμ΄νμ€νμΌ μ μ¬λ λ§€μΉμΌλ‘ κΈ°μμ¬ λ£Έλ©μ΄νΈλ₯Ό μ°Ύμμ£Όλ μλΉμ€
irummateλ νμλ€μ΄ μν μ΅κ΄ μ€λ¬Έμ μλ΅νλ©΄, μλ΅μ 벑ν°λ‘ λ³νν΄ κ°μ₯ μ λ§λ λ£Έλ©μ΄νΈ ν보λ₯Ό μΆμ²νλ μλΉμ€μ λλ€. κΈ°μμ¬ μΈμ¦μ ν΅κ³Όν μ¬νμλ§ μ°Έμ¬ν μ μμ΄ μ λ’°ν μ μλ λ§€μΉ νκ²½μ μ 곡ν©λλ€.
λ³Έ μ μ₯μλ λ°±μλ μλ²μ λλ€.
- μ£Όμ κΈ°λ₯
- κΈ°μ μ€ν
- μμ€ν μν€ν μ²
- λ§€μΉ μκ³ λ¦¬μ¦
- μΈμ¦ λ° μΈκ° ꡬ쑰
- νλ‘μ νΈ κ΅¬μ‘°
- μμνκΈ°
- API κ°μ
- λ°°ν¬
| κΈ°λ₯ | μ€λͺ |
|---|---|
| μΉ΄μΉ΄μ€ μμ λ‘κ·ΈμΈ | μΉ΄μΉ΄μ€ OAuth μΈμ¦ ν JWT μ‘μΈμ€Β·λ¦¬νλ μ ν ν° λ°κΈ |
| κΈ°μμ¬ μΈμ¦ | S3 presigned URLλ‘ μΈμ¦ μ¬μ§μ μ λ‘λνκ³ κ΄λ¦¬μκ° μΉμΈΒ·λ°λ € |
| μν μ΅κ΄ μ€λ¬Έ | 9κ° λ¬Έν μλ΅μ μ κ·νλ 9μ°¨μ λΌμ΄νμ€νμΌ λ²‘ν°λ‘ λ³ν |
| λ£Έλ©μ΄νΈ λ§€μΉ | pgvector κΈ°λ° μ μ¬λ κ²μμΌλ‘ ν보 3λͺ μΆμ², ν루 1ν μ ν |
| μνΈ μλ½ λ° μ±ν | μμΈ‘μ΄ μλ½νλ©΄ μ±ν λ°©μ΄ κ°μ€λκ³ WebSocketμΌλ‘ μ€μκ° λν |
| κ΄λ¦¬μ κΈ°λ₯ | μΈμ¦ μ¬μ¬, νμ μ‘°ν λ° μ μ§, λ§€μΉ κΈ°κ° μ€μ |
μΈμ΄ λ° νλ μμν¬
- Java 21
- Spring Boot 3.5.11
- Spring Security, Spring Data JPA, Spring WebSocket(STOMP)
- Spring AOP, Bean Validation
λ°μ΄ν°λ² μ΄μ€
- PostgreSQL + pgvector (λ²‘ν° μ μ¬λ κ²μ)
- Hibernate Vector, QueryDSL
μΈμ¦ λ° λ³΄μ
- μΉ΄μΉ΄μ€ OAuth 2.0
- JWT (jjwt)
- Hashids (μΈλΆ λ ΈμΆμ© ID λλ ν)
μΈνλΌ
- AWS EC2, RDS(PostgreSQL), S3
- Docker, Docker Compose, Nginx
- GitHub Actions + GHCR + AWS SSM
λ¬Έμν
- SpringDoc OpenAPI (Swagger UI)
ββββββββββββββββ ββββββββββββββββββββ ββββββββββββ
β GitHub main β βββΆ β GitHub Actions β βββΆ β GHCR β
ββββββββββββββββ ββββββββββββββββββββ ββββββ¬ββββββ
β
SSM λ°°ν¬ Β· μ΄λ―Έμ§ pull
β
ββββββββββββ ββββββββββββββββ AWS Cloud (ap-northeast-2) βββββββͺββββββββββ
β β β βββββββββββββββ VPC ββββββββββββββββββββββββββ β β
β μ¬μ©μ β βββΆβ β ββββ EC2 (t3.medium) ββββββββ β βΌ β
β μ± Β· μΉ β β β β βββββββββββββββββββββββ β ββββββββββββββββββββ β
β β β β β β Nginx (νλ‘μΒ·TLS) β β β RDS PostgreSQL β β
ββββββββββββ β β β ββββββββββββ¬βββββββββββ β β pgvectorΒ·t4g.smallβ β
β β β ββββββββββββΌβββββββββββ β ββββββββββββββββββββ β
β β β β Spring Boot (Docker)ββββΌβββΆ β² β
β β β ββββββββββββ¬βββββββββββ β β β
β β βββββββββββββββΌββββββββββββββ β β
β ββββββββββββββββββΌββββββββββββββββββββββββββββΌβββββββββββ
β ββββββββββββββΌβββββββββββββ β
β β S3 (κΈ°μμ¬ μΈμ¦ μ¬μ§) β β
β βββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- EC2 λ¨μΌ μΈμ€ν΄μ€μμ Nginxμ Spring Boot 컨ν μ΄λκ° Docker Composeλ‘ μ€νλ©λλ€.
- μ ν리μΌμ΄μ
ν¬νΈ(8080)λ
127.0.0.1μλ§ λ°μΈλ©λμ΄ μΈλΆμμ μ§μ μ κ·Όν μ μμ΅λλ€. - λ°°ν¬λ SSHκ° μλ AWS SSM Run Commandλ‘ μ΄λ£¨μ΄μ Έ 22λ² ν¬νΈλ₯Ό μ΄μ§ μμ΅λλ€.
- S3λ VPC λ°μ 리μ λ¨μ μλΉμ€λ‘, presigned URLμ ν΅ν΄ ν΄λΌμ΄μΈνΈκ° μ§μ μ λ‘λν©λλ€.
μ¬μ©μκ° 9κ° λ¬Έν(μ·¨μΉ¨ μκ°, μ½κ³¨μ΄, μ κΌ¬λ, μ 리 μ΅κ΄, μ€λ΄ μ·¨μ, μ¨λ μ νΈ, μ€μ λΉλ, μ€νΌμ»€ μ¬μ©, ν΅ν μ΅κ΄)μ μλ΅νλ©΄, κ° κ°μ [0, 1] ꡬκ°μΌλ‘ μ κ·ννμ¬ 9μ°¨μ 벑ν°λ‘ μ μ₯ν©λλ€. λ³νμ μν°ν° μ μ₯ μμ μ μλμΌλ‘ μνλ©λλ€.
answers (jsonb) βββΆ normalize βββΆ lifestyle_vector (vector(9))
λ§€μΉ μμ²μ΄ λ€μ΄μ€λ©΄ λ€μ μμλ‘ ν보λ₯Ό ν보ν©λλ€.
- κΈ°μ‘΄ μΆμ² μ¬νμ© β μ΄λ―Έ λ°κΈνμ§λ§ μμ§ μλ΅νμ§ μμ ν보λ₯Ό μ°μ μ‘°ν
- μ κ· μΆμ² β λΆμ‘±ν μΈμλ§νΌ pgvector μ μ¬λ κ²μμΌλ‘ 보좩
μ κ· μΆμ² 쿼리λ λμΌ μ±λ³, μ€λ¬Έ μλ£, λ―Έλ§€μΉ μνμ μ¬μ©μ μ€ μ΄λ―Έ λ§€μΉ μ΄λ ₯μ΄ μλ μλλ₯Ό μ μΈν λ€, L2 거리(<->) κΈ°μ€μΌλ‘ κ°μ₯ κ°κΉμ΄ 3λͺ
μ μ νν©λλ€. ν‘μ° μ¬λΆκ° μΌμΉνλ ν보λ₯Ό μ°μ κ²μνκ³ , μΈμμ΄ λΆμ‘±νλ©΄ ν‘μ° μ‘°κ±΄μ μνν΄ μ¬κ²μν©λλ€.
μ νλ ν보μ λν΄ match_requests λ μ½λλ₯Ό μμ±νλ©°, μ΄λ κ³μ°λ λ§€μΉλ₯ μ 컬λΌμ μ€λ
μ·μΌλ‘ μ μ₯ν©λλ€. μμΈ‘μ΄ λͺ¨λ μλ½νλ©΄ μ±ν
λ°©μ΄ κ°μ€λκ³ μ°λ½μ²κ° 곡κ°λ©λλ€.
μ°Έκ³ : μ€λ¬Έ μμ μ λ§€μΉ μμμΌ μ΄μ κΉμ§λ§ νμ©λ©λλ€. λ§€μΉ μμ μ΄νμλ μ΄λ―Έ λ°κΈλ λ§€μΉλ₯ κ³Ό μ€μ 벑ν°κ° μ΄κΈλμ§ μλλ‘ μμ μ΄ μ νλ©λλ€.
rerolled_at 컬λΌμ ν΅ν΄ ν루 1νλ§ μλ‘μ΄ μΆμ²μ λ°μ μ μλλ‘ μ νν©λλ€.
μμ²μ λ λ¨κ³λ‘ κ²μ¦λ©λλ€.
1λ¨κ³ β Spring Security νν° μ²΄μΈ
JwtAuthenticationFilterκ° UsernamePasswordAuthenticationFilter μμμ λμνλ©°, Bearer ν ν°μ κ²μ¦νκ³ userIdλ₯Ό SecurityContextμ μ μ₯ν©λλ€. μΈμ
μ μ¬μ©νμ§ μλ stateless ꡬμ±μ
λλ€.
2λ¨κ³ β AOP κΈ°λ° λ©μλ λ¨μ κ°λ
ValidationAspectκ° μλ μ΄λ
Έν
μ΄μ
μ κ°μ§ν΄ λΉμ¦λμ€ μ‘°κ±΄μ μ¬μ κ²μ¦ν©λλ€.
| μ΄λ Έν μ΄μ | κ²μ¦ λ΄μ© |
|---|---|
@RequiresAuth(roles = ...) |
κ³μ μν λ° κΆν(GUEST / USER / ADMIN) νμΈ |
@RequiresCertification |
κΈ°μμ¬ μΈμ¦ μλ£(ACTIVE) μ¬λΆ νμΈ |
@RequiresSurvey |
μ€λ¬Έ μμ± μλ£ μ¬λΆ νμΈ |
@RequiresMatchDate |
νμ¬ λ μ§κ° λ§€μΉ κΈ°κ° λ΄μΈμ§ νμΈ |
μ΄ κ΅¬μ‘° λλΆμ 컨νΈλ‘€λ¬ λ©μλμ μ΄λ Έν μ΄μ μ μ μΈνλ κ²λ§μΌλ‘ μ κ·Ό 쑰건μ μ‘°ν©ν μ μμ΅λλ€.
λλ©μΈ μ€μ¬(Domain-Driven) ν¨ν€μ§ ꡬ쑰λ₯Ό λ°λ¦ λλ€.
src/main/java/com/irummate/
βββ domain/
β βββ auth/ # μΉ΄μΉ΄μ€ OAuth, ν ν° λ°κΈ λ° μ¬λ°κΈ
β βββ user/ # νμ νλ‘ν, μμΈ μ 보, νν΄
β βββ certification/ # κΈ°μμ¬ μΈμ¦ μ μΆ λ° μ‘°ν
β βββ survey/ # μ€λ¬Έ λ±λ‘Β·μ‘°νΒ·μμ , λ²‘ν° λ³ν
β βββ matching/ # λ§€μΉ μκ³ λ¦¬μ¦, λ§€μΉ μμ², λ§€μΉ κΈ°κ° μ€μ
β βββ chat/ # μ±ν
λ°©, λ©μμ§, WebSocket
β βββ admin/ # μΈμ¦ μ¬μ¬, νμ κ΄λ¦¬
β βββ (κ° λλ©μΈ) controller / service / repository / entity / dto / util
βββ global/
βββ aop/ # κΆνΒ·μν κ²μ¦ μ΄λ
Έν
μ΄μ
λ° Aspect
βββ jwt/ # ν ν° μμ±Β·κ²μ¦ νν°
βββ s3/ # presigned URL λ°κΈ, κ°μ²΄ μμ
βββ config/ # Security, WebSocket, Swagger μ€μ
βββ exception/ # κ³΅ν΅ μμΈ λ° μ μ νΈλ€λ¬
βββ response/ # νμ€ μλ΅ ν¬λ§·
βββ util/ # Hashids λ± κ³΅ν΅ μ νΈ
- JDK 21
- Docker / Docker Compose
- PostgreSQL 16+ (pgvector νμ₯ νμ)
git clone https://github.com/ALOC-dev/5th-project-yulgok-server-2026.git
cd 5th-project-yulgok-server-2026νλ‘μ νΈ λ£¨νΈμ .env νμΌμ μμ±ν©λλ€. (spring.config.importλ‘ μλ λ‘λλ©λλ€.)
# Database
DB_URL=jdbc:postgresql://localhost:5432/irummate
DB_USERNAME=your_username
DB_PASSWORD=your_password
# Kakao OAuth
KAKAO_CLIENT_ID=your_client_id
KAKAO_CLIENT_SECRET=your_client_secret
KAKAO_REDIRECT_URI=http://localhost:8080/api/auth/kakao/callback
KAKAO_ADMIN_KEY=your_admin_key
# JWT
JWT_SECRET=your_jwt_secret_key
JWT_ACCESS_TOKEN_EXPIRATION=3600000
JWT_REFRESH_TOKEN_EXPIRATION=1209600000
# Hashids
HASHIDS_SALT=your_salt
HASHIDS_MIN_LENGTH=8AWS μ격 μ¦λͺ μ EC2 μΈμ€ν΄μ€ νλ‘νμΌ λλ λ‘컬 AWS CLI μ€μ μ ν΅ν΄ μ£Όμ λ©λλ€. S3 λ²ν·κ³Ό 리μ μ
application.propertiesμ μ μλμ΄ μμ΅λλ€.
pgvector νμ₯μ νμ±νν©λλ€.
CREATE EXTENSION IF NOT EXISTS vector;spring.jpa.hibernate.ddl-auto=validate μ€μ μ΄λ―λ‘ μ ν리μΌμ΄μ
μ΄ ν
μ΄λΈμ μμ±νμ§ μμ΅λλ€. μ€ν€λ§κ° 미리 μ€λΉλμ΄ μμ΄μΌ ν©λλ€.
# λ‘컬 μ€ν
./gradlew bootRun
# λλ Dockerλ‘ μ€ν
docker compose -f docker-compose.prod.yml up -d --buildμ ν리μΌμ΄μ μ€ν ν Swagger UIμμ μ 체 APIλ₯Ό νμΈν μ μμ΅λλ€.
http://localhost:8080/swagger-ui/index.html
λͺ¨λ μλ΅μ GlobalApiResponse ν¬λ§·μΌλ‘ ν΅μΌλμ΄ μμΌλ©°, μμΈλ GlobalExceptionControllerμμ μΌκ΄ μ²λ¦¬λ©λλ€.
| λ©μλ | κ²½λ‘ | μ€λͺ |
|---|---|---|
| GET | /kakao/callback |
μΉ΄μΉ΄μ€ λ‘κ·ΈμΈ μ½λ°±, ν ν° λ°κΈ |
| POST | /refresh |
μ‘μΈμ€ ν ν° μ¬λ°κΈ |
| POST | /logout |
λ‘κ·Έμμ |
| GET | /status |
λ‘κ·ΈμΈ μν μ‘°ν |
| λ©μλ | κ²½λ‘ | μ€λͺ |
|---|---|---|
| GET | /me |
λ΄ νλ‘ν μ‘°ν |
| PATCH | /me |
νλ‘ν μμ |
| POST | /details |
μμΈ μ 보 λ±λ‘ |
| PATCH | /details |
μμΈ μ 보 μμ |
| DELETE | /me |
νμ νν΄ |
| λ©μλ | κ²½λ‘ | μ€λͺ |
|---|---|---|
| GET | /me |
μ΅κ·Ό μΈμ¦ μν μ‘°ν |
| POST | /presigned-url |
μ λ‘λμ© presigned URL λ°κΈ |
| POST | / |
μΈμ¦ μ μΆ |
| λ©μλ | κ²½λ‘ | μ€λͺ |
|---|---|---|
| POST | / |
μ€λ¬Έ μ μΆ |
| GET | /me |
λ΄ μ€λ¬Έ μ‘°ν |
| PATCH | /me |
μ€λ¬Έ μμ (λ§€μΉ μμ μ κΉμ§) |
| λ©μλ | κ²½λ‘ | μ€λͺ |
|---|---|---|
| GET | /status |
λ§€μΉ νν© μ‘°ν |
| POST | /match |
λ§€μΉ ν보 μΆμ² (ν루 1ν) |
| PATCH | /requests |
ννΈ μ μ‘ λλ κ±°μ |
| PATCH | /requests/confirm |
λ§€μΉ νμ |
| GET | /requests/{receiverId}/contact |
νμ λ μλ μ°λ½μ² μ‘°ν |
| λ©μλ | κ²½λ‘ | μ€λͺ |
|---|---|---|
| GET | /rooms |
μ±ν λ°© λͺ©λ‘ |
| GET | /rooms/{roomId}/messages |
λ©μμ§ λͺ©λ‘ |
| PATCH | /rooms/{roomId}/read |
μ½μ μ²λ¦¬ |
| GET | /unread-count |
μ μ½μ λ©μμ§ μ |
μ€μκ° λ©μμ§ μ μ‘μ WebSocket(STOMP)μ /chat/send λͺ©μ μ§λ₯Ό μ¬μ©ν©λλ€.
| λ©μλ | κ²½λ‘ | μ€λͺ |
|---|---|---|
| GET | /certifications |
μΈμ¦ λͺ©λ‘ μ‘°ν (νμ΄μ§λΉ 15건) |
| GET | /certifications/{id} |
μΈμ¦ μμΈ μ‘°ν (μ΄λ―Έμ§ URL ν¬ν¨) |
| PATCH | /certifications/{id}/approve |
μΈμ¦ μΉμΈ |
| PATCH | /certifications/{id}/reject |
μΈμ¦ λ°λ € |
| GET | /users |
νμ λͺ©λ‘ μ‘°ν |
| GET | /users/{userId} |
νμ μμΈ μ‘°ν |
| PATCH | /users/{userId}/ban |
νμ μ μ§ |
| PATCH | /users/{userId}/unban |
μ μ§ ν΄μ |
| GET | /match/config |
λ§€μΉ κΈ°κ° μ‘°ν |
| PATCH | /match/config |
λ§€μΉ κΈ°κ° μ€μ |
main λΈλμΉμ νΈμλλ©΄ GitHub Actionsκ° μλμΌλ‘ λ°°ν¬λ₯Ό μνν©λλ€.
push (main)
βββΆ Gradle λΉλ
βββΆ Docker μ΄λ―Έμ§ λΉλ λ° GHCR νΈμ (latest, commit SHA)
βββΆ AWS SSM Run Commandλ‘ EC2μ λ°°ν¬
βββΆ μ΄λ―Έμ§ pull λ° μ»¨ν
μ΄λ μ¬μμ
νμν GitHub Secrets
| μ΄λ¦ | μ€λͺ |
|---|---|
AWS_ACCESS_KEY_ID |
λ°°ν¬μ© IAM μ¬μ©μ μ‘μΈμ€ ν€ |
AWS_SECRET_ACCESS_KEY |
λ°°ν¬μ© IAM μ¬μ©μ μν¬λ¦Ώ ν€ |
EC2_INSTANCE_ID |
λμ EC2 μΈμ€ν΄μ€ ID |
SSH μ μ μμ΄ SSMμ ν΅ν΄ λ°°ν¬νλ―λ‘ λ³΄μ κ·Έλ£Ήμ 22λ² ν¬νΈλ₯Ό μ΄μ΄λ νμκ° μμ΅λλ€.