Spring Boot Boilerplate 설계 가이드
이 문서는 보일러플레이트의 핵심 설계 원칙과 패턴을 정리한 가이드입니다.
📐 아키텍처 원칙
Layer Architecture
Controller → Service → Repository
| 계층 | 역할 | 규칙 |
|---|---|---|
| Controller | 요청/응답 처리 | Repository 직접 사용 ❌, Entity 사용 ❌ |
| Service | 비즈니스 로직 | Entity ↔ DTO 변환 담당 |
| Repository | 데이터 접근 | QueryDSL에서 DTO 직접 반환 가능 |
API 설계 규칙
- 1 API = 1 Controller 메소드 = 1 Service 메소드
- Service 메소드가 비슷하면 → private 공통 메소드로 추출
- SELECT만 하는 경우 → QueryDSL에서 DTO 직접 반환 권장
📁 파일 업로드
설계 원칙
- 파일 업로드는 별도 API로 분리 (게시판 API + 파일들 API)
- 파일 공통 관리 →
RefTypeEnum으로 용도 구분 - 트랜잭션 불일치(파일만 업로드됨) → 배치로 해결
환경별 저장 전략 (Strategy Pattern)
// 로컬 환경: LocalFileStorage (컴퓨터 경로에 저장)
// 운영 환경: SupabaseFileStorage (클라우드에 저장)
| 환경 | 저장소 | 설정 |
|——|——–|——|
| 개발 | 로컬 파일시스템 | supabase.enabled: false |
| 운영 | Supabase Storage | supabase.enabled: true |
yml;에 따라 운영, 로컬에서 수파베이스용, 로컬용 파일 처리 빈이 주입
🗄️ JPA 설계
연관관계 원칙
- 양방향 지양 → 프론트에서 API 분리 요청 (글 API + 댓글 API)
- 부모글 삭제 시 고아 데이터 → 배치 or orphanRemoval or Cascade or service에서 자식들도 같이 삭제 or 프론트에서 직접 자식 삭제요청
- 메인 데이터 여러개 연관관계 조회 (댓글목록 한번에 조회 + 각 댓글에 user username이 필요한상황)
방법 1. 메인 쿼리 실행 후 서브쿼리id 모아서
IN쿼리로 한번에 처리 방법 2. 조인 - jpql, @EntityGraph 등으로 한번에 쿼리 실행 상황에 맞게 선택. 이후 성능 문제가 있다면 개선. 방법1,2 둘다 repository(queryDSL)에서 처리.
연관관계가 너무 많을 때만 service에서 처리하는거 고려.
Entity vs DTO
변환메소드는 DTO측에서
DTO → Entity
Entity entity = dto.toEntity();
Entity → DTO
DTO dto = DTO.from(entity);
Entity - DB 매핑, 비즈니스 편의 메소드 (상태 변경)
편의메소드에서 exception 발생시켜도 공통처리 됨.
DTO - 변환 메소드, API 요청/응답
기본적으로 공통DTO 쓰다가 새로운 DTO가 필요하면 그때그때 만들기.
Repository 규칙
- 연관관계 필요 →
findByEntity() - 단순 조회 →
findByEntityId()(ID만으로 조회)
Paging Contrroller 바인딩
- Pageable → Controller 파라미터에서 직접 바인딩
(시작이 0,1 인건 클라이언트가 처리)
📤 응답 표준화
성공 응답
ResponseEntity<ApiResponse<T>>
// 사용 예시
return ResponseEntity.ok(ApiResponse.success("조회 성공", data));
페이징 응답
PageResponse<T> // Page<>의 필요한 필드만 추출 Page<>는 안 쓰는 필드 너무 많음
에러 응답
ErrorResponse //모든 에러 상황에서 일관된 JSON 구조로 응답
HTTP 상태 코드
| 코드 | 사용 | |——|——| | 400 | 비즈니스 규칙 위반 | | 401 | 인증 필요 | | 403 | 권한 없음 | | 404 | 리소스 없음 | | 409 | 중복 리소스 |
이게 예외세분화인거고,, 글로벌처리에 대한 내용이없군
🔐 Security
로그인 정보 사용
@AuthenticationPrincipal CustomUserAccount userAccount
JWT 구조
- Access Token: 짧은 만료 (30분)
- Refresh Token: 긴 만료 (4시간), 쿠키 저장
- 서버 다중화 시 → Redis 고려 (현재 미적용)
🔧 환경 분리
| 환경 | 파일 | 특징 |
|---|---|---|
| 개발 | application.yml |
ddl-auto: create, SQL 초기화 |
| 운영 | application-prod.yml |
ddl-auto: update, SQL 초기화 비활성화 |
📝 로그
- logback-spring.xml 사용
- 콘솔 + 파일(일별 롤링) + 에러 분리
- 30일 보관, 용량 제한
✅ 보일러플레이트 구현 현황
| 기능 | 상태 | 파일/패키지 |
|---|---|---|
| Layer Architecture | ✅ | Controller/Service/Repository 분리 |
| API 표준 응답 | ✅ | ApiResponse, PageResponse |
| 예외 처리 | ✅ | GlobalExceptionHandler |
| JWT 인증 | ✅ | jwt/ 패키지 |
| OAuth2 로그인 | ✅ | 카카오, 구글 |
| 파일 업로드 | ✅ | file/ 패키지, Strategy Pattern |
| 게시판 | ✅ | community/ 패키지 |
| 댓글 | ✅ | community/comment/ |
| 로그 설정 | ✅ | logback-spring.xml |
| 환경 분리 | ✅ | application.yml / application-prod.yml |
| Docker | ✅ | Dockerfile |
| Swagger | ✅ | /swagger-ui.html |
| Actuator | ✅ | /actuator/health |
🚀 추가 고려사항 (선택)
현재 미적용 (필요시 추가)
| 기능 | 설명 | 언제 필요? | |——|——|———–| | Redis | 세션/캐시/토큰 블랙리스트 | 서버 다중화, 로그아웃 즉시 무효화 | | 이메일 발송 | 비밀번호 찾기, 알림 | 사용자 알림 필요 시 | | 스케줄러 | 배치 작업 (@Scheduled) | 고아 파일 정리, 통계 집계 | | 캐싱 | @Cacheable | 자주 조회되는 데이터 | | Rate Limiting | API 호출 제한 | 악용 방지 | | AOP 로깅 | 메소드 실행 시간 측정 | 성능 모니터링 | | 웹소켓 | 실시간 양방향 통신 (채팅, 알림) | CSR + JWT 완성 후 (STOMP + SockJS + JWT 인증, SSR 단계에서는 하지 말 것) |
프론트엔드 분리 시
현재 Thymeleaf 템플릿이 포함되어 있음. 완전한 REST API 서버로 사용하려면:
templates/폴더 삭제 가능HomeController.java삭제 또는 API 전용으로 변경- CORS 설정 확인 (
CorsConfig.java)
📚 참고 문서
설계/개념/- CORS, CSRF, Swagger 설명설계/동작설명용/- API 흐름, OAuth2 동작 설명설계/배포/- Docker, Railway 배포 가이드