νκ΅κ³΅νλνκ΅ νλ‘κ·Έλλ° λμ리 μ¨λΆμ(CBU Manage) ννμ΄μ§ λ°±μλ
- νλ‘μ νΈ μκ°
- κΈ°μ μ€ν
- μν€ν μ²
- ν¨ν€μ§ ꡬ쑰
- μμνκΈ°
- API λͺ μΈ
- μΈμ¦ λ°©μ
- νκ²½λ³μ μ€μ
μ¨λΆμ λμ리 ννμ΄μ§μ λ°±μλ API μλ²μ
λλ€.
νμ μΈμ¦, μ€ν°λ/νλ‘μ νΈ λͺ¨μ§, νλ λ³΄κ³ μ, μ½λ©ν
μ€νΈ λ¬Έμ κ΄λ¦¬, μλ£μ€ λ± λμ리 μ΄μμ νμν κΈ°λ₯μ μ 곡ν©λλ€.
| λΆλ₯ | κΈ°μ |
|---|---|
| Language | Java 17 |
| Framework | Spring Boot 3.3.5 |
| ORM | Spring Data JPA (Hibernate) |
| Auth | Spring Security + JWT (jjwt 0.12.6) |
| DB | MySQL (AWS RDS) |
| Cache / Token Store | Redis |
| Storage | AWS S3 |
| API Docs | SpringDoc OpenAPI (Swagger UI) |
| Build | Gradle |
| Deploy | Docker |
νλ‘ νΈμλ (Next.js)
β
β HTTP (μΏ ν€ κΈ°λ° JWT)
βΌ
βββββββββββββββββββββββββββββββ
β Spring Boot API β
β JwtFilter β Controller β
β β Service β JPA β
ββββββββ¬βββββββββββββ¬ββββββββββ
β β
MySQL(RDS) Redis
(RefreshToken)
βββββ AWS S3 (μ΄λ―Έμ§)
μΈμ¦ νλ¦
- λ‘κ·ΈμΈ μ
accessToken/refreshTokenμΏ ν€ λ°κΈ (HttpOnly) - μ΄ν λͺ¨λ μμ²μμ
JwtFilterκ° μΏ ν€λ₯Ό κ²μ¦ν΄SecurityContextμ μ¬μ©μ μ 보 λ±λ‘ accessTokenλ§λ£ μ/api/v1/login/refreshλ‘ μ¬λ°κΈ
src/main/java/com/example/cbumanage/
βββ global/
β βββ common/ # JwtFilter, JwtProvider, ApiResponse
β βββ config/ # SecurityConfig, RedisConfig, S3Config
β βββ error/ # κ³΅ν΅ μμΈ μ²λ¦¬ (BaseException, ErrorCode)
β
βββ user/ # λ‘κ·ΈμΈ/νμκ°μ
/λ΄ μ 보
βββ member/ # λμ리 λ©€λ²(CbuMember) κ΄λ¦¬
βββ candidate/ # κ°μ
ν보μ κ΄λ¦¬ (κ΅¬κΈ μνΈ μ°λ)
β
βββ group/ # κ·Έλ£Ή μμ±Β·κ°μ
Β·μΉμΈ κ΄λ¦¬
βββ post/ # κ²μκΈ κ³΅ν΅ (Post λ©μΈ ν
μ΄λΈ)
βββ study/ # μ€ν°λ λͺ¨μ§ κ²μκΈ
βββ project/ # νλ‘μ νΈ λͺ¨μ§ κ²μκΈ
βββ report/ # νλ λ³΄κ³ μ κ²μκΈ
βββ problem/ # μ½λ©ν
μ€νΈ λ¬Έμ
βββ resource/ # μλ£μ€
β
βββ comment/ # λκΈ / λ΅κΈ
βββ image/ # μ΄λ―Έμ§ μ
λ‘λ (AWS S3)
βββ email/ # μ΄λ©μΌ μΈμ¦
βββ dues/ # νλΉ κ΄λ¦¬
βββ log/ # νλ λ‘κ·Έ
- Java 17
- Docker & Docker Compose (κΆμ₯)
- MySQL 8.x
- Redis 7.x
1. μ μ₯μ ν΄λ‘
git clone https://github.com/cbu-manage/backend.git
cd backend2. νκ²½λ³μ μ€μ
src/main/resources/application.properties λλ νκ²½λ³μλ‘ μλ νλͺ©μ μ€μ ν©λλ€.
(μμΈ νλͺ©μ νκ²½λ³μ μ€μ μ°Έκ³ )
3. Gradle λΉλ & μ€ν
./gradlew clean build -x test
java -jar build/libs/*.jar4. Dockerλ‘ μ€ν
docker build -t cbu-backend .
docker run -p 8080:8080 --env-file .env cbu-backendμλ² κΈ°λ ν Swagger UI: http://localhost:8080/swagger-ui/index.html
μ 체 APIλ Swagger UIμμ νμΈν μ μμ΅λλ€.
μλλ μ£Όμ λλ©μΈλ³ μλν¬μΈνΈ μμ½μ
λλ€.
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/v1/login |
β | λ‘κ·ΈμΈ (μΏ ν€μ ν ν° λ°κΈ) |
| POST | /api/v1/login/signup |
β | νμκ°μ |
| POST | /api/v1/login/refresh |
β | AccessToken μ¬λ°κΈ |
| GET | /api/v1/login/me |
β | λ΄ μ 보 μ‘°ν |
| PATCH | /api/v1/login/password |
β | λΉλ°λ²νΈ λ³κ²½ |
| POST | /api/v1/login/password/reset |
β | μ΄λ©μΌ μΈμ¦μ½λ κΈ°λ° λΉλ°λ²νΈ μ΄κΈ°ν |
| DELETE | /api/v1/login |
β | λ‘κ·Έμμ |
| DELETE | /api/v1/login/account |
β | νμ νν΄ |
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| GET | /api/v1/post |
β | μΉ΄ν κ³ λ¦¬λ³ κ²μκΈ λͺ©λ‘ (νμ΄μ§) |
| GET | /api/v1/post/my |
β | λ΄κ° μμ±ν κ²μκΈ λͺ©λ‘ |
| GET | /api/v1/post/{postId}/post |
β | κ²μκΈ λ¨κ±΄ μ‘°ν |
| DELETE | /api/v1/post/{postId} |
β | κ²μκΈ μμ (soft delete) |
category κ°:
1μ€ν°λ Β·2νλ‘μ νΈ Β·5μ½λ©ν μ€νΈ Β·6μλ£μ€ Β·7νλλ³΄κ³ μ
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/v1/post/study |
β | μ€ν°λ κ²μκΈ μμ± |
| GET | /api/v1/post/study |
β | μ€ν°λ λͺ©λ‘ μ‘°ν |
| GET | /api/v1/post/study/{postId} |
β | μ€ν°λ μμΈ μ‘°ν |
| PATCH | /api/v1/post/study/{postId} |
β | μ€ν°λ μμ (μμ±μ) |
| DELETE | /api/v1/post/study/{postId} |
β | μ€ν°λ μμ (μμ±μ) |
| GET | /api/v1/post/study/filter |
β | νκ·Έλ³ μ€ν°λ κ²μ |
| POST | /api/v1/post/study/{postId}/apply |
β | μ€ν°λ μ°Έκ° μ μ² |
| GET | /api/v1/post/study/{postId}/apply |
β | μ μ²μ λͺ©λ‘ μ‘°ν (νμ₯) |
| PATCH | /api/v1/post/study/{postId}/apply/{applyId} |
β | μ μ² μλ½/κ±°μ (νμ₯) |
| POST | /api/v1/post/study/{postId}/close |
β | λͺ¨μ§ λ§κ° + κ·Έλ£Ή μμ± (νμ₯) |
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/v1/post/project |
β | νλ‘μ νΈ κ²μκΈ μμ± |
| GET | /api/v1/post/project |
β | νλ‘μ νΈ λͺ©λ‘ μ‘°ν |
| GET | /api/v1/post/project/{postId} |
β | νλ‘μ νΈ μμΈ μ‘°ν |
| PATCH | /api/v1/post/project/{postId} |
β | νλ‘μ νΈ μμ (μμ±μ) |
| DELETE | /api/v1/post/project/{postId} |
β | νλ‘μ νΈ μμ (μμ±μ) |
| GET | /api/v1/post/project/filter |
β | λͺ¨μ§λΆμΌλ³ νν° μ‘°ν |
| GET | /api/v1/post/project/me |
β | λ΄κ° μμ±ν νλ‘μ νΈ λͺ©λ‘ |
| Method | Path | μΈμ¦ νμ | κΆν | μ€λͺ |
|---|---|---|---|---|
| POST | /api/v1/groups/{groupId}/members |
β | μΌλ° | κ·Έλ£Ή κ°μ μ μ² |
| DELETE | /api/v1/groups/{groupId}/members/me |
β | μΌλ° | κ°μ μ μ² μ·¨μ |
| GET | /api/v1/groups/my |
β | μΌλ° | λ΄κ° κ°μ ν κ·Έλ£Ή λͺ©λ‘ |
| GET | /api/v1/groups/my/applications |
β | μΌλ° | λ΄ μ μ² λͺ©λ‘ |
| GET | /api/v1/groups/{groupId} |
β | μΌλ° | κ·Έλ£Ή μμΈ μ‘°ν |
| GET | /api/v1/groups/{groupId}/applicants |
β | νμ₯ | κ°μ λκΈ° λͺ©λ‘ |
| PATCH | /api/v1/groups/members/{groupMemberId}/applicant |
β | νμ₯ | κ°μ μλ½/κ±°μ |
| PATCH | /api/v1/groups/{groupId}/recruitment |
β | νμ₯ | λͺ¨μ§ μν λ³κ²½ |
| GET | /api/v1/groups/admin |
β | κ΄λ¦¬μ | μ 체 κ·Έλ£Ή μ‘°ν |
| PATCH | /api/v1/groups/{groupId}/admin/status |
β | κ΄λ¦¬μ | κ·Έλ£Ή μΉμΈ/κ±°μ |
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/v1/report |
β | λ³΄κ³ μ μμ± |
| GET | /api/v1/report |
β | λ³΄κ³ μ λͺ©λ‘ μ‘°ν (νμ΄μ§) |
| GET | /api/v1/report/{postId} |
β | λ³΄κ³ μ λ¨κ±΄ μ‘°ν |
| PATCH | /api/v1/report/{postId} |
β | λ³΄κ³ μ μμ (μμ±μ) |
| PATCH | /api/v1/report/{postId}/accept |
β | λ³΄κ³ μ μΉμΈ (κ΄λ¦¬μ) |
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/v1/post/problems |
β | λ¬Έμ λ±λ‘ |
| GET | /api/v1/post/problems |
β | λ¬Έμ λͺ©λ‘ μ‘°ν |
| GET | /api/v1/post/problems/{id} |
β | λ¬Έμ μμΈ μ‘°ν |
| PATCH | /api/v1/post/problems/{id} |
β | λ¬Έμ μμ (μμ±μ) |
| DELETE | /api/v1/post/problems/{id} |
β | λ¬Έμ μμ (μμ±μ) |
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/v1/resources |
β | μλ£ λ±λ‘ |
| GET | /api/v1/resources |
β | μλ£ λͺ©λ‘ μ‘°ν |
| GET | /api/v1/resources/my |
β | λ΄ μλ£ λͺ©λ‘ |
| DELETE | /api/v1/resources/{id} |
β | μλ£ μμ (μμ±μ) |
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/v1/post/{postId}/comment |
β | λκΈ μμ± |
| GET | /api/v1/post/{postId}/comment |
β | λκΈ λͺ©λ‘ μ‘°ν |
| POST | /api/v1/comment/{commentId}/reply |
β | λ΅κΈ μμ± |
| PATCH | /api/v1/comment/{commentId} |
β | λκΈ μμ |
| DELETE | /api/v1/comment/{commentId} |
β | λκΈ μμ |
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/image/upload |
β | μ΄λ―Έμ§ μ λ‘λ (S3) β URL λ°ν |
μ§μ νμ:
jpg,jpeg,png,gif,webp,heif
μ΅λ νμΌ ν¬κΈ°: 10MB
| Method | Path | μΈμ¦ νμ | μ€λͺ |
|---|---|---|---|
| POST | /api/v1/mail/send |
β | μΈμ¦ λ©μΌ λ°μ‘ |
| POST | /api/v1/mail/verify |
β | μΈμ¦ μ½λ νμΈ |
λ‘κ·ΈμΈ μ±κ³΅ μ μλ΅ ν€λ Set-Cookieλ‘ λ κ°μ μΏ ν€κ° λ΄λ €μ΅λλ€.
| μΏ ν€λͺ | μ€λͺ | μ ν¨μκ° |
|---|---|---|
accessToken |
API μμ² μΈμ¦μ© JWT | 10λΆ |
refreshToken |
accessToken μ¬λ°κΈμ© | 10μΌ |
λ μΏ ν€ λͺ¨λ HttpOnly μ€μ μΌλ‘ JavaScriptμμ μ§μ μ κ·Ό λΆκ°ν©λλ€.
POST /api/v1/login/refresh
refreshToken μΏ ν€κ° μ ν¨νλ©΄ μ accessTokenκ³Ό refreshTokenμ μ¬λ°κΈν©λλ€.
νλ‘ νΈμλμ λ°±μλμ λλ©μΈ/ν¬νΈκ° λ€λ₯Έ κ²½μ°(Cross-Origin):
- μμ² μ λ°λμ
credentials: 'include'μ΅μ μ μ€μ ν΄μΌ μΏ ν€κ° μ μ‘λ©λλ€. - μΏ ν€ μ€μ μ
SameSite=None/Secure=false(κ°λ°) λλSecure=true(HTTPS μ΄μ) λ‘ κ΅¬μ±λ©λλ€.
// fetch μμ
fetch('http://localhost:8080/api/v1/login/me', {
credentials: 'include',
});
// axios μμ
axios.get('/api/v1/login/me', { withCredentials: true });src/main/resources/application.properties μμ κ΄λ¦¬ν©λλ€.
μ΄μ λ°°ν¬ μμλ νκ²½λ³μ λλ μΈλΆ μ€μ μΌλ‘ μ£Όμ
νμΈμ.
| ν€ | μ€λͺ | μμ |
|---|---|---|
spring.datasource.url |
MySQL μ μ URL | jdbc:mysql://host:3306/db |
spring.datasource.username |
DB μ¬μ©μλͺ | admin |
spring.datasource.password |
DB λΉλ°λ²νΈ | password |
spring.data.redis.host |
Redis νΈμ€νΈ | localhost |
spring.data.redis.port |
Redis ν¬νΈ | 6379 |
cbu.jwt.secret |
JWT μλͺ ν€ (32μ μ΄μ κΆμ₯) | your-secret-key |
cbu.jwt.expireTime |
accessToken λ§λ£μκ° (ms) | 600000 (10λΆ) |
cbu.jwt.refreshExpireTime |
refreshToken λ§λ£μκ° (ms) | 864000000 (10μΌ) |
cbu.jwt.secureCookie |
μΏ ν€ Secure νλκ·Έ | false(κ°λ°) / true(μ΄μ) |
cbu.login.salt |
λΉλ°λ²νΈ ν΄μ± salt | your-salt |
aws.access.key.id |
AWS μ‘μΈμ€ ν€ | - |
aws.secret.access.key |
AWS μν¬λ¦Ώ ν€ | - |
aws.region.static |
AWS 리μ | ap-northeast-2 |
aws_bucket |
S3 λ²ν·λͺ | your-bucket |
spring.mail.username |
Gmail λ°μ κ³μ | example@gmail.com |
spring.mail.password |
Gmail μ± λΉλ°λ²νΈ | - |
google.spreadSheet.id |
κ΅¬κΈ μνΈ ID (λ©€λ² κ΄λ¦¬μ©) | - |