μ°μνν ν¬μ½μ€ - μ€νκ³Όμ λ°±μλ API μλ²
κ°μμ€ μμ½ λ° κ΄λ¦¬λ₯Ό μν RESTful API μλ²μ λλ€.
Spring Boot κΈ°λ°μ κ°μμ€ κ΄λ¦¬ μμ€ν λ°±μλ API μλ²μ λλ€. νμκ³Ό κ΅μ§μμ κ°μμ€ μμ½, κ΄λ¦¬μμ κ°μ΄λλΌμΈ κ΄λ¦¬ κΈ°λ₯μ μ 곡ν©λλ€. μΆκ°λ‘ κ³ μ₯ μ κ³ λ₯Ό κ΄λ¦¬ν©λλ€.
- κ°μμ€ μμ½ λ±λ‘
- μμ½ μ‘°ν (λͺ©λ‘/μμΈ)
- μμ½ μμ λ° μ·¨μ
- λΉλ°λ²νΈ κΈ°λ° μμ½ λ³΄νΈ
- κ°μμ€λ³ μ¬μ© κ°μ΄λλΌμΈ λ±λ‘
- κ°μ΄λλΌμΈ μ‘°ν
- νμΌ μ²¨λΆ (AWS S3 μ°λ)
- κ°μμ€ λͺ©λ‘ μ‘°ν
- μ΄κΈ° κ°μμ€ λ°μ΄ν° μλ μ€μ
- JWT κΈ°λ° κ΄λ¦¬μ μΈμ¦
- μ΄κΈ° κ΄λ¦¬μ κ³μ μλ μμ±
- κ°μμ€ κ³ μ₯ μ κ³
- κ°μμ€ κ³ μ₯ μ κ³ λͺ©λ‘ μ‘°ν
- κ³ μ₯ μ리 μλ£ μ²λ¦¬
- Spring Boot 3.x - μ ν리μΌμ΄μ νλ μμν¬
- Spring Data JPA - ORM
- Spring Security - μΈμ¦/μΈκ°
- Spring Web - RESTful API
- MySQL 8.0 - κ΄κ³ν λ°μ΄ν°λ² μ΄μ€
- Docker Compose - 컨ν μ΄λ μ€μΌμ€νΈλ μ΄μ
- AWS S3 - νμΌ μ€ν λ¦¬μ§ (μ΄λ―Έμ§/λ¬Έμ)
- Spring Cloud AWS - AWS SDK ν΅ν©
- JWT (JSON Web Token) - ν ν° κΈ°λ° μΈμ¦
- BCrypt - λΉλ°λ²νΈ μνΈν
- Gradle (μΆμ )
- Java 17+
- Docker & Docker Compose
- AWS κ³μ (S3 μ¬μ©)
git clone [repository-url]
cd classroom-manager-backenddocker-compose up -dsrc/main/resources/application-local.yml νμΌ μμ±:
# AWS S3 μ€μ
spring:
cloud:
aws:
s3:
bucket: {bucket μ΄λ¦}
credentials:
access-key: {access key}
secret-key: {secret-key}
# κ΄λ¦¬μ κ³μ
admin:
super:
id: super_admin
password: super_admin
# JWT
jwt:
secret-key: {secret key}
# Frontend URL
front:
url: http://localhost:5173application-local.ymlνμΌμ μ λ Gitμ 컀λ°νμ§ λ§μΈμ.gitignoreμ μΆκ°λμ΄ μλμ§ νμΈνμΈμ- μ΄μ νκ²½μμλ νκ²½ λ³μ λλ AWS Secrets Manager μ¬μ© κΆμ₯
# λ―Όκ°ν μ€μ νμΌ (λ°λμ ν¬ν¨!)
application-local.yml
application-dev.yml
application-prod.yml
# κΈ°ν
.env
*.log
target/
build/# Gradle μ¬μ© μ
./gradlew bootRun --args='--spring.profiles.active=local'
# Maven μ¬μ© μ
./mvnw spring-boot:run -Dspring-boot.run.profiles=localIntelliJ IDEAμμ μ€ν:
- Run β Edit Configurations
- Active profilesμ
localμ λ ₯ - Apply β OK
- μ€ν λ²νΌ ν΄λ¦
μλ²κ° http://localhost:8080μμ μ€νλ©λλ€.
src/main/resources/
βββ application.yml # κ³΅ν΅ μ€μ (Gitμ 컀λ°)
βββ application-local.yml # λ‘컬 κ°λ° (Git μ μΈ)
βββ application-dev.yml # κ°λ° μλ² (Git μ μΈ)
βββ application-prod.yml # μ΄μ μλ² (Git μ μΈ)
spring:
application:
name: manager
datasource:
url: jdbc:mysql://localhost:3306/classroom_db?useSSL=false&allowPublicKeyRetrieval=true
username: classroom_user
password: classroom_user
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
open-in-view: false
hibernate:
ddl-auto: create # μ΄μ: validate
properties:
hibernate:
format_sql: true
show-sql: true
cloud:
aws:
s3:
bucket: ${AWS_S3_BUCKET:default-bucket}
region:
static: "ap-northeast-2"
credentials:
access-key: ${AWS_ACCESS_KEY}
secret-key: ${AWS_SECRET_KEY}
admin:
super:
id: ${ADMIN_ID:super_admin}
password: ${ADMIN_PASSWORD:super_admin}
name: "μ 체 κ΄λ¦¬μ"
contact: "010-0000-0000"
class:
code:
- "5413"
- "5414"
- "5527"
- "5507"
- "5508"
- "627-A"
- "627-B"
jwt:
access-token-expiration-day: 10
secret-key: ${JWT_SECRET_KEY}
front:
url: ${FRONT_URL:http://localhost:5173}# λ‘컬 κ°λ°
./gradlew bootRun --args='--spring.profiles.active=local'
# κ°λ° μλ²
./gradlew bootRun --args='--spring.profiles.active=dev'
# μ΄μ μλ²
java -jar build/libs/app.jar --spring.profiles.active=prod# Gradle μ¬μ© μ
./gradlew bootRun
# λλ IDEμμ μ§μ μ€νμλ²κ° http://localhost:8080μμ μ€νλ©λλ€.
http://localhost:8080/api
# μμ½ λͺ©λ‘ μ‘°ν
GET /api/reservations/{roomCode}?yearMonth={yearMonth}
# μμ½ μμΈ μ‘°ν
GET /api/reservations/detail/{reservationId}
# μμ½ λ±λ‘
POST /api/reservations
Content-Type: application/json
{
"memberId": "2021001",
"contact": "010-1234-5678",
"role": "STUDENT",
"roomCode": "5413",
"title": "μ€ν°λ",
"purpose": "μκ³ λ¦¬μ¦ μ€ν°λ",
"startDate": "2024-01-15T09:00",
"endDate": "2024-01-15T11:00",
"password": "1234"
}
# μμ½ μμ
PUT /api/reservations/{reservationId}
Content-Type: application/json
{
"memberId": "2021001",
"contact": "010-1234-5678",
"role": "STUDENT",
"roomCode": "5413",
"title": "μ€ν°λ",
"purpose": "μκ³ λ¦¬μ¦ μ€ν°λ",
"startDate": "2024-01-15T09:00",
"endDate": "2024-01-15T11:00",
"password": "1234"
}
# μμ½ μ·¨μ
DELETE /api/reservations/{reservationId}
Content-Type: application/json
{
"password":"1234"
}# κ°μ΄λλΌμΈ λͺ©λ‘ μ‘°ν
GET /api/guidelines
# κ°μ΄λλΌμΈ μμΈ μ‘°ν
GET /api/guidelines/{guideLineId}
# κ°μ΄λλΌμΈ λ±λ‘ (κ΄λ¦¬μ)
POST /api/guidelines
Authorization: Bearer {jwt-token}
Content-Type: multipart/form-data
{
"guideLine": {
"roomCode": "5413",
"content": "κ°μμ€ μ¬μ© κ°μ΄λλΌμΈ"
},
"files": [νμΌ1, νμΌ2, ...]
}# κ°μμ€ λͺ©λ‘ μ‘°ν
GET /api/classrooms# κ΄λ¦¬μ λ‘κ·ΈμΈ
POST /api/admins/login
Content-Type: application/json
{
"adminId": "super_admin",
"password": "super_admin"
}
# μλ΅
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
}docker-compose.yml:
version: '3.8'
services:
mysql-db:
image: mysql:8.0
container_name: classroom-manager-db-container
restart: always
environment:
MYSQL_ROOT_PASSWORD: admin
MYSQL_DATABASE: "classroom_db"
MYSQL_USER: "classroom_user"
MYSQL_PASSWORD: "classroom_user"
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --default-time-zone=Asia/Seoul
volumes:
mysql-data:Host: localhost
Port: 3306
Database: classroom_db
Username: classroom_user
Password: classroom_user
μ ν리μΌμ΄μ μμ μ μλμΌλ‘ μμ±λλ λ°μ΄ν°:
ID: super_admin
Password: super_admin
Role: SUPER_ADMIN
- 5413
- 5414
- 5527
- 5507
- 5508
- 627-A
- 627-B
erDiagram
MEMBER {
string member_id PK
string contact
string role
string name
}
ADMIN {
string admin_id PK
string name
string contact
string role
string authorization
string active
string password
}
CLASSROOM {
bigint room_id PK
string roomNumber UK
}
REPORT {
bigint report_id PK
string member_id FK
bigint room_id FK
datetime date
string item
string contact
string content
string status
}
RESERVATION {
bigint reservation_id PK
string member_id FK
bigint room_id FK
string purpose
datetime start_time
datetime end_time
}
GUIDELINE {
bigint guideline_id PK
bigint room_id FK
string content
}
FILE {
bigint file_id PK
string file_type
string file_url
string original_filename
bigint related_id
string related_type "REPORT, GUIDELINE, RESERVATION"
}
MEMBER ||--o{ REPORT : "report"
MEMBER ||--o{ RESERVATION : "reserve"
CLASSROOM ||--o{ RESERVATION : "has"
CLASSROOM ||--o{ REPORT : "is_related_to"
ADMIN ||--o{ GUIDELINE :"creates"
CLASSROOM ||--o{ GUIDELINE : "is_related_to"
REPORT ||..o{ FILE : "has"
RESERVATION ||..o{ FILE : "has"
GUIDELINE ||..o{ FILE : "has"
erDiagram
MEMBER {
string member_id PK
string contact
Role role "STUDENT, STAFF"
string name
}
ADMIN {
string admin_id PK
string name
string contact
string password
Role role "STUDENT, STAFF"
Authorization authorization "SUPER_ADMIN, ADMIN, EDITOR, VIEWER"
Active active "ACTIVE, INACTIVE"
}
CLASSROOM {
bigint room_id PK
string room_code UK
}
REPORT {
bigint report_id PK
string member_id FK
bigint room_id FK
datetime date
Item item "PROJECTOR, PC, SPEAKER, MICROPHONE"
string contact
string content
Status status "PENDING, COMPLETED"
}
RESERVATION {
bigint reservation_id PK
string member_id FK
bigint room_id FK
string purpose
datetime start_time
datetime end_time
}
GUIDELINE {
bigint guideline_id PK
bigint room_id FK
string admin_id FK
string content
}
FILE {
bigint file_id PK
string file_url
string file_type
string original_filename
bigint related_id
string related_type "REPORT, GUIDELINE, RESERVATION"
}
MEMBER ||--o{ REPORT : "report"
MEMBER ||--o{ RESERVATION : "reserve"
CLASSROOM ||--o{ RESERVATION : "has"
CLASSROOM ||--o{ REPORT : "is_related_to"
ADMIN ||--o{ GUIDELINE :"creates"
CLASSROOM ||--o{ GUIDELINE : "is_related_to"
dependencies {
// Spring Boot
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
// Database
runtimeOnly 'com.mysql:mysql-connector-j'
// AWS
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}# 컨ν
μ΄λ μμ
docker-compose up -d
# 컨ν
μ΄λ μ€μ§
docker-compose down
# λ‘κ·Έ νμΈ
docker-compose logs -f mysql-db
# MySQL μ μ
docker exec -it classroom-manager-db-container mysql -u classroom_user -p# 컨ν
μ΄λ λ° λ³Όλ₯¨ μμ (λ°μ΄ν° μμ μμ )
docker-compose down -v
# μ¬μμ
docker-compose up -d# λ‘κ·ΈμΈ
curl -X POST http://localhost:8080/api/admins/login \
-H "Content-Type: application/json" \
-d '{"memberId":"super_admin","password":"super_admin"}'
# μλ΅μμ ν ν° λ³΅μ¬ ν μ¬μ©
curl -X GET http://localhost:8080/api/admins/something \
-H "Authorization: Bearer {your-token}"β οΈ application.ymlμ μλ AWS ν€μ JWT secretμ μ λ Gitμ 컀λ°νμ§ λ§μΈμ- νκ²½ λ³μ λλ
.envνμΌ μ¬μ© κΆμ₯ .gitignoreμ λ€μ μΆκ°:.env application-local.yml application-prod.yml
- κ°λ°:
spring.jpa.hibernate.ddl-auto: create - μ΄μ:
spring.jpa.hibernate.ddl-auto: validateλλnone
- νλ‘ νΈμλ URLμ
front.urlμ μ νν μ€μ - μ΄μ νκ²½μμλ λλ©μΈ νμ΄νΈλ¦¬μ€νΈ κ΄λ¦¬
Woowa-Tech-Course - μ€νκ³Όμ