Docs: README 및 minio 추가

README.md 파일 작성.
minio 사용을 위한 의존성 추가, 환경변수 수정.
기타 주석 정리.
This commit is contained in:
pwy3282040@msecure.co
2025-12-26 10:59:38 +09:00
parent 0fd5b0584b
commit 47135829f7
5 changed files with 136 additions and 18 deletions

40
README.md Normal file
View 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)

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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일