Spring Boot κΈ°λ° λ§μ΄ν¬λ‘μλΉμ€ μν€ν μ²
AI κΈ°λ° μμ λ―Έλμ΄ λ§μΌν μλν νλ«νΌμ λ°±μλ μμ€ν
- ποΈ λ§μ΄ν¬λ‘μλΉμ€ μν€ν μ²
- π οΈ κΈ°μ μ€ν
- π μλΉμ€ ꡬμ±
- π³ Docker νκ²½
- π λΉ λ₯Έ μμ
- βοΈ νκ²½ μ€μ
- π λ°μ΄ν°λ² μ΄μ€ μ€κ³
- π API λ¬Έμ
- π CI/CD νμ΄νλΌμΈ
- π νλ‘μ νΈ κ΅¬μ‘°
- π» κ°λ° κ°μ΄λ
- π§ νΈλ¬λΈμν
- λ¨μΌ μ± μ: κ° μλΉμ€λ νΉμ λΉμ¦λμ€ λλ©μΈμ μ§μ€
- λ 립 λ°°ν¬: μλΉμ€λ³ λ 립μ μΈ λ°°ν¬ λ° μ€μΌμΌλ§
- λ°μ΄ν° λ 립μ±: κ° μλΉμ€λ μ체 λ°μ΄ν°λ² μ΄μ€ 보μ
- μ₯μ 격리: ν μλΉμ€μ μ₯μ κ° μ 체 μμ€ν μ μν₯μ μ£Όμ§ μμ
- Java 17 - LTS λ²μ μΌλ‘ μμ μ±κ³Ό μ±λ₯ 보μ₯
- Spring Boot 3.5.4 - μ΅μ νλ μμν¬ κΈ°λ° λ§μ΄ν¬λ‘μλΉμ€
- Spring Cloud 2025.0.0 - λ§μ΄ν¬λ‘μλΉμ€ μΈνλΌ ν΅ν©
- Spring Cloud Gateway - API κ²μ΄νΈμ¨μ΄, λΌμ°ν , λ‘λ λ°Έλ°μ±
- Spring Security 6.x - 보μ λ° μΈμ¦ μ²λ¦¬
- Spring Data JPA - λ°μ΄ν° μ κ·Ό κ³μΈ΅
- MySQL 8.0 - μ£Ό λ°μ΄ν°λ² μ΄μ€ (κ° μλΉμ€λ³ λ 립 μ€ν€λ§)
- Redis 7.2 - μΈμ μ μ₯, ν ν° κ΄λ¦¬, μΊμ±
- Apache Kafka 7.4.3 - μ΄λ²€νΈ μ€νΈλ¦¬λ°, μλΉμ€ κ° λΉλκΈ° ν΅μ
- AWS S3 - λ―Έλμ΄ νμΌ μ μ₯μ
- AWS CloudFront - κΈλ‘λ² CDN
- Gradle 8.x - λ©ν°λͺ¨λ νλ‘μ νΈ λΉλ λꡬ
- Docker & Docker Compose - 컨ν μ΄λν λ° μ€μΌμ€νΈλ μ΄μ
- GitHub Actions - CI/CD νμ΄νλΌμΈ
- YouTube Data API v3 - λμμ μ λ‘λ λ° κ΄λ¦¬
- Google OAuth2 - μμ λ‘κ·ΈμΈ
- Kakao OAuth2 - μμ λ‘κ·ΈμΈ
- FastAPI AI Service - AI μ½ν μΈ μμ±
| μλΉμ€ | κ°λ° ν¬νΈ | Docker λ΄λΆ | μΈλΆ μ κ·Ό | μν |
|---|---|---|---|---|
| Gateway | 8080 | 8080 | :8080 |
β μ€νμ€ |
| Auth Service | 8081 | 8081 | λ΄λΆ μ μ© | β μ€νμ€ |
| Store Service | 8082 | 8082 | λ΄λΆ μ μ© | β μ€νμ€ |
| Content Service | 8083 | 8083 | λ΄λΆ μ μ© | β μ€νμ€ |
| SNS Service | 8084 | 8084 | λ΄λΆ μ μ© | β μ€νμ€ |
| Shorts Service | 8085 | 8085 | λ΄λΆ μ μ© | β μ€νμ€ |
| Analytics Service | 8086 | 8086 | λ΄λΆ μ μ© | β μ€νμ€ |
π 보μ μ€κ³: Gatewayλ₯Ό ν΅ν λ¨μΌ μ§μ μ μΌλ‘ λ΄λΆ μλΉμ€ 보νΈ
π Gateway (API Gateway)
μ£Όμ μ± μ:
- λͺ¨λ μΈλΆ μμ²μ λ¨μΌ μ§μ μ
- JWT ν ν° κ²μ¦ λ° μ¬μ©μ μΈμ¦
- μλΉμ€λ³ μμ² λΌμ°ν λ° λ‘λ λ°Έλ°μ±
- CORS μ μ± κ΄λ¦¬
- Rate Limiting λ° μμ² μ ν
ν΅μ¬ κΈ°λ₯:
- λμ λΌμ°ν
:
/api/auth/**β Auth Service - ν ν° κΈ°λ° μΈμ¦: JWT Access Token κ²μ¦
- μ¬μ©μ 컨ν μ€νΈ μ ν: ν€λλ₯Ό ν΅ν μ¬μ©μ μ 보 μ λ¬
π€ Auth Service (μΈμ¦/μΈκ°)
μ£Όμ μ± μ:
- μ¬μ©μ νμκ°μ , λ‘κ·ΈμΈ, λ‘κ·Έμμ
- JWT ν ν° μμ± λ° κ΄λ¦¬
- OAuth2 μμ λ‘κ·ΈμΈ (Google, Kakao)
- μ¬μ©μ νλ‘ν κ΄λ¦¬
- 보μ μ μ± κ΄λ¦¬ (κ³μ μ κΈ, μ€ν¨ μΆμ )
ν΅μ¬ κΈ°λ₯:
- ν ν° κ΄λ¦¬: Access Token (2μκ°), Refresh Token (14μΌ)
- ν ν° λΈλ리μ€νΈ: Redis κΈ°λ° λ‘κ·Έμμ ν ν° λ¬΄ν¨ν
- μμ λ‘κ·ΈμΈ: OAuth2 Provider μ°λ
πͺ Store Service (λ§€μ₯ κ΄λ¦¬)
μ£Όμ μ± μ:
- λ§€μ₯ μ 보 CRUD (μμ±, μ‘°ν, μμ , μμ )
- μ μ’ λ³ λΆλ₯ κ΄λ¦¬ (μμμ , μΉ΄ν, ν¨μ , λ·°ν°, κΈ°μ )
- μμΉ κΈ°λ° μλΉμ€ (μλ/κ²½λ μ’ν)
- λ§€μ₯ κ²μ¦ λ° μΉμΈ νλ‘μΈμ€
ν΅μ¬ κΈ°λ₯:
- μ§λ¦¬μ κ²μ: μμΉ κΈ°λ° λ§€μ₯ κ²μ
- μ μ’ λΆλ₯: νμ€νλ μ μ’ μ½λ κ΄λ¦¬
- λ§€μ₯ κ²μ¦: μ¬μ μ μ 보 νμΈ
π Content Service (μ½ν μΈ κ΄λ¦¬)
μ£Όμ μ± μ:
- μ΄λ―Έμ§/λΉλμ€ μ λ‘λ λ° μ μ₯
- λ―Έλμ΄ λ©νλ°μ΄ν° μΆμΆ λ° κ΄λ¦¬
- μΈλ€μΌ μλ μμ±
- AWS S3 μ°λ λ° CloudFront CDN κ΄λ¦¬
ν΅μ¬ κΈ°λ₯:
- νμΌ μ λ‘λ: λ©ν°ννΈ μ λ‘λ μ§μ
- μλ μ΅μ ν: μ΄λ―Έμ§ μμΆ λ° λ¦¬μ¬μ΄μ§
- CDN μ°λ: κΈλ‘λ² μ½ν μΈ λ°°ν¬
π± SNS Service (μμ λ―Έλμ΄ μ°λ)
μ£Όμ μ± μ:
- YouTube μ±λ κ΄λ¦¬ λ° μ°λ
- λμμ μλ μ λ‘λ
- SNS κ³μ OAuth κ΄λ¦¬
- κ²μλ¬Ό μμ½ λ° μ€μΌμ€λ§
ν΅μ¬ κΈ°λ₯:
- YouTube API: λμμ μ λ‘λ, μμ , μμ
- μ±λ λκΈ°ν: μ±λ μ 보 μ€μκ° λκΈ°ν
- λ°°μΉ μ λ‘λ: λλ μ½ν μΈ μμ½ κ²μ
π€ Shorts Service (AI μ½ν μΈ μμ±)
μ£Όμ μ± μ:
- AI κΈ°λ° μλλ¦¬μ€ μλ μμ±
- μ΄λ―Έμ§/λΉλμ€ μ½ν μΈ μμ±
- λΉλκΈ° μμ μ²λ¦¬ λ° μ§νλ₯ μΆμ
- FastAPI AI μλ² μ°λ
ν΅μ¬ κΈ°λ₯:
- μλλ¦¬μ€ μμ±: GPT κΈ°λ° μ½ν μΈ μμ±
- λΉλκΈ° μ²λ¦¬: κΈ΄ μμ μ λ°±κ·ΈλΌμ΄λ μ²λ¦¬
- μ§νλ₯ μΆμ : μ€μκ° μμ μν λͺ¨λν°λ§
π Analytics Service (λΆμ)
μ£Όμ μ± μ:
- μ€μκ° μ±κ³Ό λΆμ λ° λμ보λ
- AI κΈ°λ° κ°μ λΆμ
- λ°°μΉ μ²λ¦¬λ₯Ό ν΅ν νμ€ν 리컬 λ°μ΄ν° λΆμ
- μ±κ³Ό μ§ν μΆμ (μ‘°νμ, νλ‘μ, λκΈ λ±)
ν΅μ¬ κΈ°λ₯:
- μ€μκ° λΆμ: Kafka μ€νΈλ¦Ό κΈ°λ° μ€μκ° λ°μ΄ν° μ²λ¦¬
- λ°°μΉ λΆμ: λμ©λ λ°μ΄ν° μΌκ΄ μ²λ¦¬
- κ°μ λΆμ: λκΈ λ° λ°μ κ°μ λΆμ
services:
# κ°λ°μ© - νΈμ€νΈ ν¬νΈ μ§μ λ°μΈλ©
gateway:
ports:
- "8080:8080" # μΈλΆ μ κ·Ό κ°λ₯
auth-service:
ports:
- "8081:8081" # κ°λ°/ν
μ€νΈμ© μ§μ μ κ·Ό
# ... κΈ°ν μλΉμ€λ€services:
gateway:
ports:
- "8080:8080" # μΈλΆ μ κ·Ό (λ‘λλ°Έλ°μ λ€)
auth-service:
# ν¬νΈ λ°μΈλ© μμ - Gatewayλ₯Ό ν΅ν΄μλ§ μ κ·Ό
expose:
- "8081"
networks:
- backend-network
# ... κΈ°ν μλΉμ€λ€ (λͺ¨λ λ΄λΆ λ€νΈμν¬λ§ μ¬μ©)# νμ μννΈμ¨μ΄ λ²μ νμΈ
java --version # Java 17+
docker --version # Docker 20.10+
docker-compose --version # Docker Compose 2.0+git clone https://github.com/KT-AIVLE-04/backend.git
cd marketing# νκ²½ νμΌ μμ±
cp .env.example .env
# νμ νκ²½λ³μ μ€μ
vim .envπ .env νμΌ μμ
# Database Configuration
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=marketing_platform
MYSQL_USER=marketing_user
MYSQL_PASSWORD=marketing_password
# Redis Configuration
REDIS_PASSWORD=redis_password
# JWT Configuration
JWT_SECRET_KEY=your-256-bit-secret-key
JWT_ACCESS_EXPIRATION=7200 # 2 hours
JWT_REFRESH_EXPIRATION=1209600 # 14 days
# OAuth2 Configuration
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
KAKAO_CLIENT_ID=your-kakao-client-id
KAKAO_CLIENT_SECRET=your-kakao-client-secret
# YouTube API
YOUTUBE_API_KEY=your-youtube-api-key
# AWS Configuration
AWS_ACCESS_KEY_ID=your-aws-access-key
AWS_SECRET_ACCESS_KEY=your-aws-secret-key
AWS_REGION=ap-northeast-2
S3_BUCKET_NAME=your-s3-bucket
CLOUDFRONT_DOMAIN=your-cloudfront-domain
# AI Service
AI_SERVICE_URL=http://ai-service:8000# MySQL, Redis, Kafka μ€ν
docker-compose up -d mysql redis kafka zookeeper
# μλΉμ€ μν νμΈ
docker-compose ps# μ 체 νλ‘μ νΈ λΉλ
./gradlew clean build
# μλΉμ€λ³ κ°λ³ μ€ν (κ°λ°μ©)
./gradlew :gateway:bootRun &
./gradlew :auth-service:bootRun &
./gradlew :store-service:bootRun &
./gradlew :content-service:bootRun &
./gradlew :sns-service:bootRun &
./gradlew :shorts-service:bootRun &
./gradlew :analytics-service:bootRun &# μ 체 μμ€ν
μ€ν
docker-compose up -d
# λ‘κ·Έ νμΈ
docker-compose logs -f
# νΉμ μλΉμ€ λ‘κ·Έλ§ νμΈ
docker-compose logs -f gateway auth-service# Health Check
curl http://localhost:8080/actuator/health
# API Gateway μν
curl http://localhost:8080/actuator/gateway/routes
# κ° μλΉμ€ Swagger UI μ κ·Ό
# http://localhost:8080/auth/swagger-ui.html
# http://localhost:8080/store/swagger-ui.html
# http://localhost:8080/content/swagger-ui.htmlκ° μλΉμ€λ λ
립μ μΈ application.yml νμΌμ κ°μ§λλ€:
π Gateway μ€μ
server:
port: 8080
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: auth-service
uri: http://auth-service:8081
predicates:
- Path=/api/auth/**
filters:
- StripPrefix=2
- id: store-service
uri: http://store-service:8082
predicates:
- Path=/api/store/**
filters:
- StripPrefix=2
- AuthFilter
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
management:
endpoints:
web:
exposure:
include: health,info,gatewayπ€ Auth Service μ€μ
server:
port: 8081
spring:
application:
name: auth-service
datasource:
url: jdbc:mysql://mysql:3306/marketing_auth
username: ${MYSQL_USER}
password: ${MYSQL_PASSWORD}
data:
redis:
host: redis
port: 6379
password: ${REDIS_PASSWORD}
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
jwt:
secret: ${JWT_SECRET_KEY}
access-expiration: ${JWT_ACCESS_EXPIRATION:7200}
refresh-expiration: ${JWT_REFRESH_EXPIRATION:1209600}# κ°λ° νκ²½
spring.profiles.active=dev
# ν
μ€νΈ νκ²½
spring.profiles.active=test
# μ΄μ νκ²½
spring.profiles.active=prodκ° μλΉμ€λ³ API λ¬Έμλ Gatewayλ₯Ό ν΅ν΄ μ κ·Όν μ μμ΅λλ€:
| μλΉμ€ | Swagger UI | μ€λͺ |
|---|---|---|
| Gateway | localhost:8080/swagger-ui.html | μ 체 API κ²μ΄νΈμ¨μ΄ |
| Auth | localhost:8080/auth/swagger-ui.html | μΈμ¦/μΈκ° API |
| Store | localhost:8080/store/swagger-ui.html | λ§€μ₯ κ΄λ¦¬ API |
| Content | localhost:8080/content/swagger-ui.html | μ½ν μΈ κ΄λ¦¬ API |
| SNS | localhost:8080/sns/swagger-ui.html | SNS μ°λ API |
| Shorts | localhost:8080/shorts/swagger-ui.html | AI μ½ν μΈ μμ± API |
| Analytics | localhost:8080/analytics/swagger-ui.html | λΆμ API |
λͺ¨λ API μμ²μ JWT ν ν° μΈμ¦μ΄ νμν©λλ€ (μΈμ¦ API μ μΈ):
# 1. λ‘κ·ΈμΈμΌλ‘ ν ν° νλ
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "password123"
}'
# Response
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 7200
}
# 2. API μμ² μ ν€λμ ν ν° ν¬ν¨
curl -X GET http://localhost:8080/api/store/my-stores \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."μ°λ¦¬μ CI/CDλ λ³κ²½λ μλΉμ€λ§ μ νμ μΌλ‘ λ°°ν¬νλ μ€λ§νΈν νμ΄νλΌμΈμ λλ€
# λ³κ²½λ κ²½λ‘μ λ°λΌ μλΉμ€ κ°μ§
filters:
gateway:
- 'gateway/**'
auth:
- 'auth-service/**'
common:
- 'common/**' # common λ³κ²½ μ λͺ¨λ μλΉμ€ μ¬λΉλ
compose:
- 'docker-compose.prod.yml' # Docker μ€μ λ³κ²½strategy:
fail-fast: false
matrix:
# λμ μΌλ‘ μμ±λλ λ§€νΈλ¦μ€
include:
- service: "gateway"
module: "gateway"
image: "aivle-gateway"
compose: "gateway"
build: true
- service: "auth-service"
module: "auth-service"
image: "aivle-auth"
compose: "auth-service"
build: true| 쑰건 | νλ | μ€λͺ |
|---|---|---|
| μλΉμ€ μ½λ λ³κ²½ | ν΄λΉ μλΉμ€λ§ λΉλ/λ°°ν¬ | ν¨μ¨μ μΈ λ°°ν¬ |
| common λͺ¨λ λ³κ²½ | λͺ¨λ μλΉμ€ μ¬λΉλ | μμ‘΄μ± μμ μ± |
| docker-compose λ³κ²½ | μ€μ λ§ μ λ°μ΄νΈ | 컨ν μ΄λ μ¬μμ |
| [full-redeploy] PR | μ 체 μλΉμ€ μ¬λ°°ν¬ | κ°μ μ 체 λ°°ν¬ |
# 1. λ³κ²½ κ°μ§ λ° λ§€νΈλ¦μ€ μμ±
echo "Changed services: gateway, auth-service"
# 2. λ³λ ¬ λΉλ (λ³κ²½λ μλΉμ€λ§)
./gradlew :gateway:clean :gateway:build
./gradlew :auth-service:clean :auth-service:build
# 3. Docker μ΄λ―Έμ§ λΉλ & νΈμ
docker build -t username/aivle-gateway:latest gateway/
docker push username/aivle-gateway:latest
# 4. AWS μλ² λ°°ν¬
docker-compose pull gateway auth-service
docker-compose up -d --no-deps gateway auth-service
# 5. ν¬μ€μ²΄ν¬ λ° μ 리
docker image prune -f- EC2: Ubuntu μλ²μ Docker Composeλ‘ λ°°ν¬
- RDS: MySQL 8.0 κ΄λ¦¬ν λ°μ΄ν°λ² μ΄μ€
- ElastiCache: Redis ν΄λ¬μ€ν°
- S3: λ―Έλμ΄ νμΌ μ μ₯μ
- CloudFront: κΈλ‘λ² CDN
# GitHub Secretsμ μ μ₯λ νκ²½λ³μλ€
secrets:
DOCKER_USERNAME: Docker Hub μ¬μ©μλͺ
DOCKER_PASSWORD: Docker Hub ν¨μ€μλ
AWS_SECRET_HOST: EC2 μλ² IP
AWS_SECRET_ACCESS_KEY: EC2 SSH ν€
APPLICATION_YML_*: κ° μλΉμ€λ³ μ€μ νμΌ
CLOUDFRONT_PRIVATE_KEY: CloudFront μλͺ
ν€# λ°°ν¬ μν νμΈ
curl http://your-server:8080/actuator/health
# μλΉμ€λ³ μν νμΈ
docker-compose ps
# λ‘κ·Έ λͺ¨λν°λ§
docker-compose logs -f --tail=100 gateway auth-servicebackend/
βββ π README.md # μ΄ νμΌ
βββ π build.gradle # λ£¨νΈ λΉλ μ€μ
βββ π settings.gradle # λ©ν°λͺ¨λ μ€μ
βββ π docker-compose.yml # κ°λ°μ© Docker Compose
βββ π docker-compose.prod.yml # μ΄μμ© Docker Compose
βββ π .env.example # νκ²½λ³μ ν
νλ¦Ώ
βββ π .gitignore # Git 무μ νμΌ
β
βββ π .github/
β βββ π workflows/
β βββ π deploy.yml # CI/CD νμ΄νλΌμΈ
β
βββ π gradle/ # Gradle Wrapper
β βββ π wrapper/
βββ π gradlew # Gradle Wrapper μ€ν¬λ¦½νΈ
βββ π gradlew.bat # Windowsμ© Gradle μ€ν¬λ¦½νΈ
β
βββ π common/ # κ³΅ν΅ λͺ¨λ
β βββ π README.md
β βββ π build.gradle
β βββ π src/
β βββ π main/java/com/marketing/common/
β βββ π config/ # κ³΅ν΅ μ€μ
β βββ π dto/ # κ³΅ν΅ DTO
β βββ π exception/ # κ³΅ν΅ μμΈ μ²λ¦¬
β βββ π util/ # μ νΈλ¦¬ν° ν΄λμ€
β
βββ π gateway/ # API Gateway
β βββ π README.md
β βββ π build.gradle
β βββ π Dockerfile
β βββ π src/
β βββ π main/
β β βββ π java/com/marketing/gateway/
β β β βββ π config/ # Gateway μ€μ
β β β βββ π filter/ # 컀μ€ν
νν°
β β β βββ π GatewayApplication.java
β β βββ π resources/
β β βββ π application.yml
β βββ π test/
β
βββ π auth-service/ # μΈμ¦ μλΉμ€
βββ π store-service/ # λ§€μ₯ κ΄λ¦¬ μλΉμ€
βββ π content-service/ # μ½ν
μΈ κ΄λ¦¬ μλΉμ€
βββ π sns-service/ # SNS μ°λ μλΉμ€
βββ π shorts-service/ # AI μ½ν
μΈ μμ± μλΉμ€
βββ π analytics-service/ # λΆμ μλΉμ€
β
βββ π docs/ # λ¬Έμ
βββ π api-documentation.md # API λͺ
μΈμ
βββ π database-schema.md # DB μ€ν€λ§
βββ π deployment-guide.md # λ°°ν¬ κ°μ΄λ
βββ π images/ # λ€μ΄μ΄κ·Έλ¨ μ΄λ―Έμ§
- Google Java Style Guide μ€μ
- 4 spaces λ€μ¬μ°κΈ° (ν μ¬μ© κΈμ§)
- Line length: 100μ μ ν
- Package naming:
com.marketing.{service}.{layer}
// β
μ’μ μμ
@RestController
@RequestMapping("/api/stores")
public class StoreController {
@GetMapping("/{storeId}")
public ResponseEntity<StoreDto> getStoreById(@PathVariable Long storeId) {
// ...
}
}
// β λμ μμ
@RestController
public class storecontroller {
@GetMapping("/getStore/{id}")
public ResponseEntity<StoreDto> getstore(@PathVariable Long id) {
// ...
}
}main
βββ develop
β βββ feature/auth-jwt-implementation
β βββ feature/store-crud-api
β βββ feature/shorts-ai-integration
β βββ hotfix/auth-token-validation
βββ release
βββ release/v1.0.0
# νμ: [scope] type: description
# κΈ°λ₯ μΆκ°
[auth] feat: implement JWT token refresh mechanism
# λ²κ·Έ μμ
[store] fix: resolve null pointer exception in store search
# λ¬Έμ μ
λ°μ΄νΈ
[readme] docs: update API endpoint documentation
# 리ν©ν λ§
[common] refactor: extract common exception handling logic
# ν
μ€νΈ μΆκ°
[auth] test: add unit tests for login service
# λΉλ/λ°°ν¬ κ΄λ ¨
[github] ci: add parallel deployment for changed services π PR ν νλ¦Ώ
## π― λ³κ²½ μ¬ν
- [ ] μλ‘μ΄ κΈ°λ₯ μΆκ°
- [ ] λ²κ·Έ μμ
- [ ] λ¬Έμ μ
λ°μ΄νΈ
- [ ] 리ν©ν λ§
- [ ] ν
μ€νΈ μΆκ°
## π μμΈ λ΄μ©
### λ³κ²½λ λ΄μ©
- API μλν¬μΈνΈ μΆκ°: `POST /api/stores`
- λ§€μ₯ μμ± μ μ ν¨μ± κ²μ¬ λ‘μ§ κ΅¬ν
- μμΉ κΈ°λ° κ²μ κΈ°λ₯ μΆκ°
### ν
μ€νΈ μλ£ μ¬ν
- [ ] λ¨μ ν
μ€νΈ ν΅κ³Ό
- [ ] ν΅ν© ν
μ€νΈ ν΅κ³Ό
- [ ] API λ¬Έμ μ
λ°μ΄νΈ
- [ ] λ‘컬 νκ²½ ν
μ€νΈ μλ£
## π κ΄λ ¨ μ΄μ
Closes #123
## π· μ€ν¬λ¦°μ· (UI λ³κ²½ μ)

## π λ°°ν¬ λ
ΈνΈ
- λ°μ΄ν°λ² μ΄μ€ λ§μ΄κ·Έλ μ΄μ
νμ: `V001__create_stores_table.sql`
- νκ²½λ³μ μΆκ°: `MAPS_API_KEY`
## π 리뷰μ΄μκ²
- νΉλ³ν κ²ν κ° νμν λΆλΆ: μμΉ κ²μ μκ³ λ¦¬μ¦μ μ±λ₯
- ν
μ€νΈ λ°μ΄ν°: `test-data.sql` μ°Έμ‘°π₯ ν¬νΈ μΆ©λ μ€λ₯
λ¬Έμ : Address already in use: bind
ν΄κ²°:
# 1. ν¬νΈ μ¬μ© νλ‘μΈμ€ νμΈ
lsof -i :8080
netstat -tlnp | grep :8080
# 2. νλ‘μΈμ€ μ’
λ£
kill -9 <PID>
# 3. Docker 컨ν
μ΄λ μ 리
docker-compose down
docker system prune -fποΈ λ°μ΄ν°λ² μ΄μ€ μ°κ²° μ€ν¨
λ¬Έμ : Connection refused: connect
ν΄κ²°:
# 1. MySQL 컨ν
μ΄λ μν νμΈ
docker-compose ps mysql
# 2. λ‘κ·Έ νμΈ
docker-compose logs mysql
# 3. λ°μ΄ν°λ² μ΄μ€ μ¬μμ
docker-compose restart mysql
# 4. μ°κ²° ν
μ€νΈ
mysql -h localhost -P 3306 -u marketing_user -pπ JWT ν ν° κ΄λ ¨ μ€λ₯
λ¬Έμ : Invalid JWT token λλ Token expired
ν΄κ²°:
# 1. Redis μΊμ νμΈ
docker-compose exec redis redis-cli
> keys *
> get refresh_token:user_123
# 2. ν ν° μ¬λ°κΈ
curl -X POST http://localhost:8080/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "..."}'
# 3. λ‘κ·Έμμ ν μ¬λ‘κ·ΈμΈ
curl -X POST http://localhost:8080/api/auth/logoutπ³ Docker λ©λͺ¨λ¦¬/λμ€ν¬ λΆμ‘±
λ¬Έμ : No space left on device
ν΄κ²°:
# 1. μ¬μ©νμ§ μλ 컨ν
μ΄λ μ 리
docker container prune -f
# 2. μ¬μ©νμ§ μλ μ΄λ―Έμ§ μ 리
docker image prune -a -f
# 3. λ³Όλ₯¨ μ 리
docker volume prune -f
# 4. μ 체 μμ€ν
μ 리 (μ£Όμ!)
docker system prune -a -f --volumes
# 5. λμ€ν¬ μ¬μ©λ νμΈ
docker system df
df -hβ‘ μλΉμ€ κ° ν΅μ μ€λ₯
λ¬Έμ : Connection refused between services
ν΄κ²°:
# 1. λ€νΈμν¬ μν νμΈ
docker network ls
docker network inspect backend_default
# 2. μλΉμ€ κ° μ°κ²° ν
μ€νΈ
docker-compose exec gateway ping auth-service
docker-compose exec gateway curl http://auth-service:8081/actuator/health
# 3. DNS νμΈ
docker-compose exec gateway nslookup auth-service
# 4. ν¬νΈ λ°μΈλ© νμΈ
docker-compose ps# μ 체 μλΉμ€ λ‘κ·Έ (μ΅μ 100μ€)
docker-compose logs -f --tail=100
# νΉμ μλΉμ€ λ‘κ·Έ
docker-compose logs -f gateway auth-service
# μλ¬ λ‘κ·Έλ§ νν°λ§
docker-compose logs | grep ERROR
# νΉμ μκ°λ λ‘κ·Έ
docker-compose logs --since="2024-01-15T10:00:00"- ERROR: μ¦μ ν΄κ²° νμν μ¬κ°ν μ€λ₯
- WARN: μ£Όμκ° νμν μν©, λͺ¨λν°λ§ νμ
- INFO: μ μμ μΈ λΉμ¦λμ€ νλ‘μ° λ‘κ·Έ
- DEBUG: κ°λ°/λλ²κΉ μ© μμΈ λ‘κ·Έ (μ΄μ νκ²½μμλ λΉνμ±ν)
# 1. νΉμ μλΉμ€λ§ μ¬μμ
docker-compose restart gateway auth-service
# 2. μ 체 μμ€ν
μ¬μμ (λ°μ΄ν°λ 보쑴)
docker-compose restart
# 3. μμ ν μ¬λ°°ν¬ (μ£Όμ: λ°μ΄ν° μμ€ κ°λ₯)
docker-compose down
docker-compose pull
docker-compose up -d# 1. μ΄μ Docker μ΄λ―Έμ§λ‘ λ‘€λ°±
docker-compose stop gateway
docker run -d --name gateway_backup username/aivle-gateway:previous-tag
docker-compose start gateway
# 2. Git μ½λ λ‘€λ°±
git log --oneline -10
git reset --hard <commit-hash>
git push --force-with-lease origin releaseλ²κ·Έ λ°κ²¬, κΈ°λ₯ μ μ, λλ κ°μ μ¬νμ΄ μλ€λ©΄ μΈμ λ μ§ μ΄μλ₯Ό λ±λ‘ν΄μ£ΌμΈμ!
π λ²κ·Έ 리ν¬νΈ β’ π‘ κΈ°λ₯ μ μ β’ π λ¬Έμ κ°μ
ποΈ Built with Spring Boot and Microservice Architecture
Β© 2025 KT AIVLE School 7κΈ° - Chaos Team
