Spring Boot Boilerplate 설계 가이드

이 문서는 보일러플레이트의 핵심 설계 원칙과 패턴을 정리한 가이드입니다.

📐 아키텍처 원칙

Layer Architecture

Controller → Service → Repository
계층 역할 규칙
Controller 요청/응답 처리 Repository 직접 사용 ❌, Entity 사용 ❌
Service 비즈니스 로직 Entity ↔ DTO 변환 담당
Repository 데이터 접근 QueryDSL에서 DTO 직접 반환 가능

API 설계 규칙 (CSR)

📁 파일 업로드

설계 원칙

환경별 저장 전략 (Strategy Pattern)

// 로컬 환경: LocalFileStorage (컴퓨터 경로에 저장)
// 운영 환경: SupabaseFileStorage (클라우드에 저장)

| 환경 | 저장소 | 설정 | |——|——–|——| | 개발 | 로컬 파일시스템 | supabase.enabled: false | | 운영 | Supabase Storage | supabase.enabled: true |

yml 설정에 따라 LocalFileStorage 또는 SupabaseFileStorage 빈이 주입됨


🗄️ JPA 설계

연관관계 원칙

Entity vs DTO

// 변환메소드는 DTO측에서
// DTO → Entity
Entity entity = dto.toEntity();
// Entity → DTO
DTO dto = DTO.from(entity);

Repository 규칙

Paging Controller 바인딩


📤 응답 표준화

성공 응답

ResponseEntity<ApiResponse<T>>
// 사용: return ResponseEntity.ok(ApiResponse.success("조회 성공", data));

페이징 응답

PageResponse<T>  // Page<>의 필요한 필드만 추출

에러 응답

ErrorResponse  // 모든 에러 상황에서 일관된 JSON 구조
// { success: false, message: "...", errorCode: "...", timestamp: "..." }

HTTP 상태 코드

| 코드 | 사용 | 예외 클래스 | |——|——|————-| | 400 | 비즈니스 규칙 위반 | BusinessRuleException | | 401 | 인증 필요 / 토큰 만료 | SecurityConfig authenticationEntryPoint | | 403 | 권한 없음 (작성자 아님) | AccessDeniedException | | 404 | 리소스 없음 | EntityNotFoundException | | 409 | 중복 리소스 | DuplicateResourceException |

전역 예외 처리 (GlobalExceptionHandler)


🔐 Security — JWT + OAuth2 (쿠키 방식)

인증 구조

브라우저: JWT를 HttpOnly 쿠키에 저장 (JS에서 접근 불가 → XSS 안전)
앱:      JWT를 JSON 응답으로 받아 앱 내부 저장소에 저장

JWT 구조

| 토큰 | 만료 | 저장 | 용도 | |——|——|——|——| | Access Token | 30분 | 쿠키 access_token | API 인증 | | Refresh Token | 4시간 | 쿠키 refresh_token + DB | Access Token 재발급 |

로그인 정보 사용

@AuthenticationPrincipal CustomUserAccount userAccount
userAccount.getUsername();  // 사용자 ID
userAccount.getUserDTO();  // 전체 사용자 정보

CORS 설정 주의사항

⚠️ Spring Security 사용 시 반드시 CorsConfigurationSource Bean으로 등록! WebMvcConfigurer 방식은 Security 필터보다 늦게 동작해서 무용지물!


💬 실시간 채팅 (STOMP WebSocket)

구조

브라우저 ←→ SockJS(/ws-chat) ←→ STOMP 프로토콜 ←→ SimpleBroker

인증 흐름 (쿠키 기반)

1. SockJS 연결 요청 → HttpHandshakeInterceptor: 쿠키에서 JWT 추출 → 세션에 username 저장
2. STOMP CONNECT → JwtChannelInterceptor: ① Authorization 헤더 확인 (앱) ② 세션 username 확인 (웹)

메시지 흐름

클라이언트 send → /pub/room/{roomId} → ChatController → /sub/room/{roomId} → 구독자 전원

🔧 환경 분리

환경 파일 특징
개발 application.yml ddl-auto: create, SQL 초기화, H2/MariaDB
운영 application-prod.yml ddl-auto: update, SQL 초기화 비활성화

📝 로그


✅ 구현 현황

기능 상태 파일/패키지
Layer Architecture Controller/Service/Repository 분리
API 표준 응답 ApiResponse, PageResponse, ErrorResponse
전역 예외 처리 GlobalExceptionHandler
JWT 인증 jwt/ 패키지
OAuth2 로그인 카카오, 구글 (보안 개선 적용)
파일 업로드 file/ 패키지, Strategy Pattern
게시판 (CRUD) community/ 패키지
댓글 (CRUD) community/comment/ 패키지
실시간 채팅 stomp/ 패키지 (STOMP + SockJS + JWT 인증)
로그 설정 logback-spring.xml
환경 분리 application.yml / application-prod.yml
Docker Dockerfile
Swagger /swagger-ui.html
Actuator /actuator/health
CORS CorsConfig.java (CorsConfigurationSource Bean)
QueryDSL 게시판 검색/페이징

🚀 추가 고려사항 (선택)

현재 미적용 (필요시 추가)

| 기능 | 설명 | 언제 필요? | |——|——|———–| | Redis | 세션/캐시/토큰 블랙리스트, WebSocket 세션 공유 | 서버 다중화 시 | | 채팅 메시지 DB 저장 | 현재 메시지는 실시간 전송만 (저장 안 됨) | 채팅 이력 필요 시 | | 이메일 발송 | 비밀번호 찾기, 알림 | 사용자 알림 필요 시 | | 스케줄러 | @Scheduled 배치 작업 | 고아 파일 정리, 통계 집계 | | 캐싱 | @Cacheable | 자주 조회되는 데이터 | | Rate Limiting | API 호출 제한 | 악용 방지 | | Access Token 블랙리스트 | JWT 즉시 무효화 (Redis) | 강제 로그아웃 필요 시 |

프론트엔드 분리 시 (React/Vue/Flutter 등)


📚 참고 문서