Java / Spring Boot ๊ธฐ๋ฐ์ผ๋ก JWT ์ธ์ฆยท์ธ๊ฐ, ๊ถํ ๋ถ๊ธฐ, ์์ธ ์ฒ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ Docker ๊ธฐ๋ฐ CI/CD ๋ฐฐํฌ๊น์ง ์ค๋ฌด ๊ธฐ์ค์ผ๋ก ์ค๊ณยท๊ตฌํํ ๋ฐฑ์๋ ์ค์ฌ ๊ฐ์ธ ํ๋ก์ ํธ
- JWT Access / Refresh Token ์ธ์ฆ ๊ตฌ์กฐ ์ง์ ์ค๊ณ
- Spring Security ํํฐ โ Service ๊ณ์ธต ์ฑ ์ ๋ถ๋ฆฌ
- ์ธ์ฆ ๊ถํ๊ณผ ๋น์ฆ๋์ค ๊ถํ ๋ถ๋ฆฌ ์ค๊ณ
- ์ค๋ฌด ๊ธฐ์ค ์์ธ ์ฒ๋ฆฌ ยท ํธ๋์ญ์ ํจํด ์ ์ฉ
- Docker + GitHub Actions ๊ธฐ๋ฐ ์๋ ๋ฐฐํฌ ํ๊ฒฝ ๊ตฌ์ถ
- ์ต์ UI๋ฅผ ํตํ ์ธ์ฆยท๊ถํ ์ ์ฑ ์ค์ ๋์ ๊ฒ์ฆ
๐ UI ๊ตฌํ์ด ์๋ ๋ฐฑ์๋ ์ค๊ณ์ ์ด์ ํ๊ฒฝ ๊ฒ์ฆ์ ์ด์ ์ ๋ ํ๋ก์ ํธ์ ๋๋ค.
๐ https://aspilgi.com
- EC2 + Nginx + HTTPS ์ ์ฉ
- ์ค์ ๋ฐฐํฌ ํ๊ฒฝ์์ ์ธ์ฆ ํ๋ฆ ํ์ธ ๊ฐ๋ฅ
- Java 17
- Spring Boot 3.x
- Spring Security
- JPA (Hibernate)
- MariaDB, Flyway
- JWT (Access / Refresh Token)
- AWS EC2 (Ubuntu)
- Docker / Docker Compose
- Nginx (Reverse Proxy)
- GitHub Actions
- GitHub Container Registry (GHCR)
- Self-hosted GitHub Actions Runner
- IntelliJ HTTP Client (auth.http)
- React (Minimal UI, ๊ฒ์ฆ ๋ชฉ์ )
Client
โ
Nginx (Reverse Proxy)
โ
Spring Boot API (Docker)
โ
MariaDB (Docker)
Controller โ Service โ Repository
- Service ๊ณ์ธต:
- ๋น์ฆ๋์ค ๋ก์ง
- ๊ถํ ๊ฒ์ฌ
- ํธ๋์ญ์ ๊ด๋ฆฌ ๋ด๋น
- Controller ๊ณ์ธต:
- ์์ฒญ/์๋ต ์ญํ ์ ์ง์ค
- ์ธ์ฆ/์ธ๊ฐ: JWT + Spring Security
- ๋น์ฆ๋์ค ์์ธ: BusinessException + ErrorCode
- ์๋ต ํฌ๋งท: ApiResponse
- ์ ์ญ ์์ธ ์ฒ๋ฆฌ: GlobalExceptionHandler
- Stateless ๊ตฌ์กฐ๋ก ์๋ฒ ํ์ฅ์ฑ ํ๋ณด
- API ์ค์ฌ ์๋น์ค ๊ตฌ์กฐ์ ์ ํฉ
- ํ๋ก ํธ์๋ / ๋ชจ๋ฐ์ผ ํ๊ฒฝ๊ณผ ํธํ์ฑ ์ฐ์
- Access Token: ์งง์ ์๋ช โ ํ์ทจ ํผํด ์ต์ํ
- Refresh Token:
- DB์ ์ ์ฅ
- ๊ฐ์ ๋ก๊ทธ์์ ๋ฐ ์ฌ๋ฐ๊ธ ์ ์ด ๊ฐ๋ฅ
์ธ์ ๊ธฐ๋ฐ ์ธ์ฆ ๋ฐฉ์๋ ๊ฒํ ํ์ผ๋, ์๋ฒ ์ํ ํ์ฅ ์ ์ธ์ ๊ณต์ ๋น์ฉ์ ์ค์ด๊ธฐ ์ํด JWT ๋ฐฉ์์ ์ ํํ์ต๋๋ค.
POST /api/v1/users/login- AccessToken + RefreshToken ๋ฐ๊ธ
- RefreshToken์ DB ์ ์ฅ (์ ์ ๋น 1๊ฐ)
Authorization: Bearer {accessToken}- JwtAuthenticationFilter์์ ๊ฒ์ฆ ํ SecurityContext์ ์ฌ์ฉ์ ์ ๋ณด ์ ์ฅ
POST /api/v1/users/reissue- RefreshToken ๊ฒ์ฆ ํ AccessToken๋ง ์ฌ๋ฐ๊ธ
- Refresh Token์ด ์ ํจํ์ง ์์ผ๋ฉด ์ฌ๋ก๊ทธ์ธ ์ ๋
POST /api/v1/users/logout- RefreshToken DB ์ญ์
- ๊ธฐ์กด AccessToken์ ๋ง๋ฃ ์๊น์ง ์ ํจ (Stateless)
- Spring Security ํํฐ ๋จ๊ณ์์ ์ฒ๋ฆฌ
- ์ธ์ฆ ์คํจ ์ 401 ๋ฐํ
- Service ๊ณ์ธต์์ ์ง์ ๊ฒ์ฌ
- ์์ฑ์ ๋ถ์ผ์น ์ AccessDeniedException ๋ฐ์
- GlobalExceptionHandler์์ 403์ผ๋ก ์๋ต
- Service ๊ณ์ธต์์ BusinessException
- @Valid ์คํจ ์ MethodArgumentNotValidException
- Service ๊ณ์ธต:
AccessDeniedException - Security ํํฐ ๋จ๊ณ:
AuthenticationEntryPointAccessDeniedHandler
Security ํํฐ ๋จ๊ณ ์์ธ์ ์๋น์ค ๊ณ์ธต ์์ธ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์์ธ ํ๋ฆ์ ๋ช ํํ ํ์ต๋๋ค.
| ์ํ ์ฝ๋ | ์๋ฏธ |
|---|---|
| 400 | ์๋ชป๋ ์์ฒญ |
| 401 | ์ธ์ฆ ์คํจ |
| 403 | ๊ถํ ์์ |
| 404 | ๋ฆฌ์์ค ์์ |
| 409 | ์ค๋ณต ๋ฐ์ดํฐ |
| 500 | ์๋ฒ ์ค๋ฅ |
- ํธ๋์ญ์ ์ Service ๊ณ์ธต์์ ๊ด๋ฆฌ
- ์กฐํ ๋ก์ง์
@Transactional(readOnly = true)์ ์ฉ - ์ฐ๊ธฐ / ์ฝ๊ธฐ ์ฑ ์ ๋ถ๋ฆฌ
์ค๊ณ ์ด์
- ๋ถํ์ํ ๋ณ๊ฒฝ ๊ฐ์ง ๋น์ฉ ๊ฐ์
- ์ค๋ฌด์์ ์ฌ์ฉํ๋ ํธ๋์ญ์ ๋ฒ์ ๊ธฐ์ค ์ ์ฉ
- POST /api/v1/users/signup
- POST /api/v1/users/login
- GET /api/v1/users/me
- POST /api/v1/users/reissue
- POST /api/v1/users/logout
- POST /api/v1/posts
- GET /api/v1/posts/{id}
- GET /api/v1/posts?page=0&size=10
- PUT /api/v1/posts/{id}
- DELETE /api/v1/posts/{id}
- POST /api/v1/posts/{postId}/comments
- GET /api/v1/posts/{postId}/comments
- PUT /api/v1/posts/{postId}/comments/{commentId}
- DELETE /api/v1/posts/{postId}/comments/{commentId}
SSH ์์ด, main ๋ธ๋์น push๋ง์ผ๋ก๋ ์๋ ๋ฐฐํฌ
git push (main)
โ
GitHub Actions
- Docker Image Build
- GHCR Push
โ
Self-hosted Runner (EC2)
- docker compose pull
- docker compose up -d --force-recreate
โ
Production ๋ฐ์
- scp / ssh ๊ธฐ๋ฐ ์๋ ๋ฐฐํฌ ์ ๊ฑฐ
- ์ด๋ฏธ์ง ๋จ์ ๋ฐฐํฌ๋ก ์๋ฒ ์ํ ์ผ๊ด์ฑ ํ๋ณด
- ์ปจํ ์ด๋ ์ฌ์์ฑ(--force-recreate)์ผ๋ก ๋ฐฐํฌ ๋ฐ์ ๋ณด์ฅ
- Spring Boot Actuator /actuator/health ๋ก ๋ฐฐํฌ ์ฑ๊ณต ์ฌ๋ถ ๊ฒ์ฆ
curl -I http://127.0.0.1:8080/actuator/health
# HTTP/1.1 200 OK
์ด์ ํ๊ฒฝ์์ ddl-auto=create/update๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ,
Flyway ๊ธฐ๋ฐ ๋ง์ด๊ทธ๋ ์ด์
์ผ๋ก DB ์คํค๋ง๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
spring.jpa.hibernate.ddl-auto=validate๊ณ ์ - ์คํค๋ง ๋ณ๊ฒฝ์ Flyway ๋ง์ด๊ทธ๋ ์ด์ ํ์ผ๋ก๋ง ์ํ
- Docker Compose ์ด์ ํ๊ฒฝ์์๋ ์ฑ ๊ธฐ๋ ์ ์๋ migrate
- IntelliJ HTTP Client(auth.http)๋ก ์ธ์ฆยท๊ถํยทCRUD ์๋๋ฆฌ์ค ๊ฒ์ฆ
- ์ธ์ฆ ์คํจ(401), ๊ถํ ์คํจ(403) ์ผ์ด์ค ํฌํจ
- ์์ฒญ ์๋๋ฆฌ์ค๋ฅผ ์ฝ๋์ฒ๋ผ ๊ด๋ฆฌ
- ๋ชฉ์ :
- JWT ์ธ์ฆ ํ๋ฆ ๊ฒ์ฆ
- ๊ถํ ์ ์ด ์ ์ฑ ์ค์ ๋์ ํ์ธ
- ๋ชจ๋ ๊ถํ ํ๋จ์ ๋ฐฑ์๋์์ ์ํ
- ํ๋ก ํธ์๋๋ UX ๋ชฉ์ ์ ์กฐ๊ฑด๋ถ ๋ ๋๋ง๋ง ๋ด๋น
- ์ข์์ ๊ธฐ๋ฅ ์ถ๊ฐ ๋ฐ ์ฌ์ฉ์ ๋ฐ์ ๋ฐ์ดํฐ ๊ด๋ฆฌ
- ๊ฒ์ ๋ฐ ํ์ด์ง ๊ณ ๋ํ
- ์ด์ ํ๊ฒฝ ๊ณ ๋ํ
- Refresh Token ์ ์ฅ์ ํ์ฅ ์ ์บ์ ๋์ ๊ฒํ (ํธ๋ํฝ ์ฆ๊ฐ ๋๋น)
- ๋ฌด์ค๋จ ๋ฐฐํฌ ๊ตฌ์กฐ(Blue/Green) ์ ์ฉ