Docs: README 및 minio 추가
README.md 파일 작성. minio 사용을 위한 의존성 추가, 환경변수 수정. 기타 주석 정리.
This commit is contained in:
40
README.md
Normal file
40
README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 🛠️ Spring Boot Blog API Server
|
||||
|
||||
## 🚀 Project Overview
|
||||
기존 **Jekyll(GitHub Pages)** 로 운영하던 정적 블로그의 한계를 극복하고, 확장 가능한 블로그 시스템을 구축하기 위해 개발한 **Spring Boot 기반의 REST API 서버**입니다.
|
||||
|
||||
정적 사이트에서는 구현하기 어려웠던 **동적 포스팅 관리, 관리자 대시보드(Admin), 댓글 시스템** 등의 기능을 직접 구현하여 블로그 운영의 효율성을 높였습니다. **RESTful 원칙**에 입각한 API 설계와 **Spring Security**를 활용한 보안/인증 로직을 적용하여 백엔드 아키텍처 역량을 강화하는 데 중점을 두었습니다.
|
||||
|
||||
### 🎯 Key Objectives
|
||||
* **Migration**: 정적 파일(Jekyll) 의존성을 제거하고 DB 기반의 동적 시스템으로 전환
|
||||
* **Feature Expansion**: 웹 에디터, 게시글 관리, 방문자 통계 등 관리자(Admin) 기능 추가
|
||||
* **Architecture**: 프론트엔드와 백엔드를 분리한 REST API 서버 구축
|
||||
|
||||
### 🛠️ Tech Stack
|
||||
* **Java 21, Spring Boot 3.5.9**
|
||||
* **Spring Security, JWT** (Auth)
|
||||
* **JPA (Hibernate), QueryDSL** (ORM)
|
||||
* **PostgreSQL, Redis** (Cache/Session)
|
||||
* **Markdown Parser** (Content)
|
||||
|
||||
---
|
||||
|
||||
## 🔌 API Reference
|
||||
|
||||
**Base URL**: `http://localhost:8080`
|
||||
**Auth**: `Authorization: Bearer {Access_Token}`
|
||||
|
||||
### 1. 공통 응답 규격 (Common Response)
|
||||
모든 API 응답은 아래 표준 포맷을 따릅니다.
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "SUCCESS", // 결과 코드 (SUCCESS 또는 ERROR_CODE)
|
||||
"message": "요청 성공", // 응답 메시지
|
||||
"data": { ... } // 실제 데이터 (없을 경우 null)
|
||||
}
|
||||
```
|
||||
|
||||
## 관련 문서
|
||||
- [**ENDPOINT**](https://affine.wypark.me/workspace/f85df0c4-a315-4166-94a8-6558cdafff1d/p13joh_beKNJ6R-I9KrFW)
|
||||
- [**API 명세서**](https://affine.wypark.me/workspace/f85df0c4-a315-4166-94a8-6558cdafff1d/7fRNXK9utxG4mcekWivGK)
|
||||
@@ -1,9 +1,10 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.9.25"
|
||||
kotlin("plugin.spring") version "1.9.25"
|
||||
kotlin("plugin.jpa") version "1.9.25"
|
||||
kotlin("kapt") version "1.9.25"
|
||||
id("org.springframework.boot") version "3.5.9"
|
||||
id("io.spring.dependency-management") version "1.1.7"
|
||||
kotlin("plugin.jpa") version "1.9.25"
|
||||
}
|
||||
|
||||
group = "me.wypark"
|
||||
@@ -12,7 +13,7 @@ description = "blog-backend"
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
languageVersion = JavaLanguageVersion.of(21) // Java 21 굿!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,22 +21,43 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
val springCloudAwsVersion = "3.2.1"
|
||||
|
||||
dependencies {
|
||||
// 1. Standard Spring Boot Starters
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||
|
||||
// 2. Kotlin Modules
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
|
||||
// 3. Database Drivers
|
||||
runtimeOnly("org.postgresql:postgresql")
|
||||
|
||||
// 4. AWS S3 / MinIO
|
||||
implementation("io.awspring.cloud:spring-cloud-aws-starter-s3:$springCloudAwsVersion")
|
||||
|
||||
// 5. QueryDSL
|
||||
implementation("com.querydsl:querydsl-jpa:5.1.0:jakarta")
|
||||
kapt("com.querydsl:querydsl-apt:5.1.0:jakarta")
|
||||
kapt("jakarta.persistence:jakarta.persistence-api")
|
||||
kapt("jakarta.annotation:jakarta.annotation-api")
|
||||
|
||||
// 6. JWT (0.12.x)
|
||||
implementation("io.jsonwebtoken:jjwt-api:0.12.3")
|
||||
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3")
|
||||
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3")
|
||||
|
||||
// 7. Test & Dev
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
|
||||
testImplementation("org.springframework.security:spring-security-test")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
implementation("io.jsonwebtoken:jjwt-api:0.12.3")
|
||||
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.3")
|
||||
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.3")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@@ -52,4 +74,4 @@ allOpen {
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,24 @@
|
||||
services:
|
||||
# 백엔드 애플리케이션
|
||||
# 1. 백엔드 애플리케이션
|
||||
blog-api:
|
||||
build: .
|
||||
container_name: blog-api
|
||||
restart: always
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
# Database
|
||||
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/blog_db
|
||||
- SPRING_DATASOURCE_USERNAME=wypark
|
||||
- SPRING_DATASOURCE_PASSWORD=your_password
|
||||
# Redis
|
||||
- SPRING_DATA_REDIS_HOST=redis
|
||||
- SPRING_DATA_REDIS_PORT=6379
|
||||
# AWS S3 / MinIO (Docker 내부 통신용)
|
||||
- CLOUD_AWS_CREDENTIALS_ACCESS_KEY=admin
|
||||
- CLOUD_AWS_CREDENTIALS_SECRET_KEY=password
|
||||
- CLOUD_AWS_REGION_STATIC=ap-northeast-2
|
||||
- CLOUD_AWS_S3_ENDPOINT=http://minio:9000
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
@@ -18,11 +27,13 @@ services:
|
||||
networks:
|
||||
- blog-net
|
||||
|
||||
# 데이터베이스 (PostgreSQL 17 추천)
|
||||
# 2. 데이터베이스 (PostgreSQL)
|
||||
db:
|
||||
image: postgres:17-alpine
|
||||
container_name: blog-db
|
||||
restart: always
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_USER: wypark
|
||||
POSTGRES_PASSWORD: your_password
|
||||
@@ -36,7 +47,7 @@ services:
|
||||
networks:
|
||||
- blog-net
|
||||
|
||||
# 캐시 서버 (Redis 7)
|
||||
# 3. 캐시 서버 (Redis)
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: blog-redis
|
||||
@@ -48,6 +59,22 @@ services:
|
||||
networks:
|
||||
- blog-net
|
||||
|
||||
# 4. 오브젝트 스토리지 (MinIO)
|
||||
minio:
|
||||
image: minio/minio
|
||||
container_name: minio
|
||||
ports:
|
||||
- "9000:9000" # API 통신
|
||||
- "9001:9001" # 웹 콘솔
|
||||
environment:
|
||||
MINIO_ROOT_USER: "admin"
|
||||
MINIO_ROOT_PASSWORD: "password"
|
||||
command: server /data --console-address ":9001"
|
||||
volumes:
|
||||
- ./minio_data:/data
|
||||
networks:
|
||||
- blog-net
|
||||
|
||||
networks:
|
||||
blog-net:
|
||||
driver: bridge
|
||||
@@ -11,7 +11,6 @@ class RefreshTokenRepository(
|
||||
@Value("\${jwt.refresh-token-validity}") private val refreshTokenValidity: Long
|
||||
) {
|
||||
// 저장 (Key: Email, Value: RefreshToken)
|
||||
// RTR 핵심: 사용자가 로그인을 새로 하거나 토큰을 재발급 받을 때마다 덮어씌움
|
||||
fun save(email: String, refreshToken: String) {
|
||||
redisTemplate.opsForValue().set(
|
||||
"RT:$email",
|
||||
|
||||
@@ -1,19 +1,49 @@
|
||||
spring:
|
||||
application:
|
||||
name: blog-api
|
||||
|
||||
# 1. 데이터베이스 설정
|
||||
datasource:
|
||||
# Docker 내부 통신용
|
||||
url: jdbc:postgresql://db:5432/blog_db
|
||||
username: wypark
|
||||
password: your_password
|
||||
username: ${SPRING_DATASOURCE_USERNAME:wypark}
|
||||
password: ${SPRING_DATASOURCE_PASSWORD:your_password}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
|
||||
# 2. JPA 설정
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
format_sql: true # 쿼리 줄바꿈
|
||||
show_sql: true # 쿼리 출력
|
||||
highlight_sql: true # 쿼리 색상 강조 (가독성 UP)
|
||||
open-in-view: false # OSIV 종료 (DB 커넥션 최적화)
|
||||
|
||||
# 3. Redis 설정
|
||||
data:
|
||||
redis:
|
||||
host: redis
|
||||
host: redis # Docker 서비스명
|
||||
port: 6379
|
||||
|
||||
# 4. AWS S3 / MinIO 설정 (아까 이야기한 부분)
|
||||
cloud:
|
||||
aws:
|
||||
s3:
|
||||
bucket: my-blog-bucket # MinIO 콘솔에서 미리 생성해야 함
|
||||
endpoint: http://minio:9000 # Docker 내부 통신용
|
||||
credentials:
|
||||
access-key: admin
|
||||
secret-key: password
|
||||
region:
|
||||
static: ap-northeast-2
|
||||
stack:
|
||||
auto: false
|
||||
|
||||
# 5. JWT 설정
|
||||
jwt:
|
||||
secret: "v3ryS3cr3tK3yF0rMyB10gPr0j3ctM4k3ItL0ng3rTh4n32Byt3s!!" # 32바이트 이상 필수
|
||||
access-token-validity: 1800000 # 30분 (ms)
|
||||
refresh-token-validity: 604800000 # 7일 (ms)
|
||||
# 보안 경고: 실제 배포 시에는 절대 코드에 비밀키를 남기지 마세요. 환경변수로 주입받으세요.
|
||||
secret: ${JWT_SECRET:v3ryS3cr3tK3yF0rMyB10gPr0j3ctM4k3ItL0ng3rTh4n32Byt3s!!}
|
||||
access-token-validity: 1800000 # 30분
|
||||
refresh-token-validity: 604800000 # 7일
|
||||
Reference in New Issue
Block a user