급여 관리 플랫폼 백엔드 API 서버
| 분류 | 기술 |
|---|---|
| Framework | Spring Boot 3.5.7 |
| Language | Java 21 |
| Database | MySQL 8.0 |
| ORM | Spring Data JPA / Hibernate |
| Security | Spring Security + JWT (jjwt 0.12.6) |
| API Docs | springdoc-openapi 2.7.0 |
| Build | Gradle 8.x |
| Test | JUnit 5 + JaCoCo |
| Cache | Spring Cache |
DDD(Domain-Driven Design) 스타일의 패키지 구조를 채택하여 비즈니스 도메인 중심으로 코드를 조직했습니다.
com.example.paycheck/
├── api/ # Presentation Layer
│ ├── auth/ # 인증 (로그인, 토큰 갱신)
│ ├── employer/ # 고용주 전용 API
│ │ ├── contract/ # 계약 관리
│ │ ├── correctionrequest/ # 정정 요청 처리
│ │ ├── salary/ # 급여 관리
│ │ ├── worker/ # 근로자 관리
│ │ ├── workrecord/ # 근무 기록
│ │ └── workplace/ # 사업장 관리
│ └── worker/ # 근로자 전용 API
│
├── domain/ # Domain Layer
│ ├── user/ # 사용자 도메인
│ ├── employer/ # 고용주 도메인
│ ├── worker/ # 근로자 도메인
│ ├── workplace/ # 사업장 도메인
│ ├── contract/ # 계약 도메인
│ ├── workrecord/ # 근무 기록 도메인
│ │ ├── entity/
│ │ ├── dto/
│ │ ├── repository/
│ │ ├── service/ # CQRS 패턴 적용
│ │ │ ├── WorkRecordCommandService
│ │ │ ├── WorkRecordQueryService
│ │ │ ├── WorkRecordCoordinatorService
│ │ │ └── WorkRecordGenerationService
│ │ └── enums/
│ ├── allowance/ # 주간 수당 도메인
│ ├── salary/ # 급여 도메인
│ ├── correction/ # 정정 요청 도메인
│ ├── payment/ # 결제 도메인
│ ├── notification/ # 알림 도메인
│ └── holiday/ # 공휴일 도메인
│
├── common/ # 공통 유틸리티
│ ├── dto/ # ApiResponse 래퍼
│ └── exception/ # 전역 예외 처리
│
└── global/ # 횡단 관심사
├── config/ # Spring 설정
├── security/ # 인증/인가
│ ├── jwt/ # JWT 토큰 처리
│ └── permission/ # 리소스 권한 검증
└── oauth/kakao/ # 카카오 OAuth
WorkRecord 도메인에서 명령과 조회의 책임을 분리했습니다.
WorkRecordCommandService → 생성, 수정, 삭제 (상태 변경)
WorkRecordQueryService → 조회 (읽기 전용)
WorkRecordCoordinatorService → 도메인 간 협력 조율
WorkRecordGenerationService → 일정 기반 일괄 생성
적용 이유:
- 근무 기록은 조회가 빈번하고 명령은 상대적으로 적음
- 급여 계산, 주간 수당 집계 등 명령 시 복잡한 부수 효과 발생
- 서비스 책임 분리로 테스트 용이성 향상
각 리소스별 Permission 클래스가 소유권 검증 로직을 담당합니다.
@PreAuthorize("@workplacePermission.canAccess(#workplaceId)")
public WorkplaceResponse getWorkplace(Long workplaceId) { ... }검증 항목:
- 고용주가 해당 사업장의 소유자인지
- 근로자가 해당 계약의 당사자인지
- 요청자가 해당 리소스에 접근 권한이 있는지
데이터 무결성과 이력 관리를 위해 논리적 삭제를 적용했습니다.
// WorkRecord
enum WorkRecordStatus { SCHEDULED, COMPLETED, DELETED }
// Workplace
boolean isActive;User (EMPLOYER/WORKER)
│
├── Employer ──────────┬── Workplace
│ │ │
└── Worker ────────────┴── WorkerContract
│
┌─────────────┼─────────────┐
│ │ │
WorkRecord WeeklyAllowance Salary
│ │
CorrectionRequest Payment
| 도메인 | 설명 | 핵심 로직 |
|---|---|---|
| WorkRecord | 근무 일정/기록 | 상태 관리, 당일 급여 계산 |
| WeeklyAllowance | 주간 수당 | 주휴수당, 주 40시간 초과 연장수당 |
| Salary | 월별 급여 | 수당 집계, 공제 계산, 실수령액 |
| CorrectionRequest | 정정 요청 | CREATE/UPDATE/DELETE 워크플로우 |
| Payment | 결제 | 토스 딥링크, 자동 실패 처리 |
PayCheck의 핵심 기능인 급여 자동 계산은 한국 노동법을 철저히 준수합니다.
| 구분 | 5인 미만 | 5인 이상 |
|---|---|---|
| 기본급 | O | O |
| 주휴수당 | O | O |
| 야간수당 (22:00~06:00) | X | O (50%) |
| 휴일수당 (주말/공휴일) | X | O (50%) |
| 연장수당 (8시간/40시간 초과) | X | O (50%) |
휴일 야간 연장 근무 시 가산율 중첩 적용:
평일 야간 8시간 초과 = 기본급 × 2.0 (야간 50% + 연장 50%)
휴일 야간 8시간 초과 = 기본급 × 2.5 (휴일 50% + 야간 50% + 연장 50%)
주휴수당과 연장수당은 주 단위로 계산되므로, 월급날이 포함된 주의 수당은 다음 달로 이월됩니다.
월급날: 1월 15일 (수요일)
[1/8~1/14 주] → 1월 급여에 포함
[1/15~1/21 주] → 월급날 포함 → 2월 급여로 이월
상세 계산 로직: SALARY_CALCULATION_POLICY.md
결정: Access Token + Refresh Token 이중 토큰 구조
Access Token : 15분 (짧은 유효기간, 서명만 검증)
Refresh Token : 7일 (긴 유효기간, DB 저장)
이유:
- Access Token 탈취 시 피해 최소화
- Refresh Token으로 UX 저하 없이 토큰 갱신
- Refresh Token 회전으로 보안 강화
결정: 단일 엔티티 + 상태 Enum
enum WorkRecordStatus {
SCHEDULED, // 예정 (급여 계산 X)
COMPLETED, // 완료 (급여 계산 O)
DELETED // 삭제 (소프트 삭제)
}대안 검토:
- WorkSchedule + WorkRecord 분리 → 조인 복잡도 증가
- 물리적 삭제 → 이력 추적 불가
선택 이유:
- 단일 테이블로 쿼리 단순화
- 상태 기반 급여 계산 포함/제외 명확
- 삭제 이력 유지로 감사 추적 가능
결정: 단일 CorrectionRequest 엔티티에 type 필드 추가
enum CorrectionRequestType {
CREATE, // 근무 기록 생성 요청
UPDATE, // 근무 시간 수정 요청
DELETE // 근무 기록 삭제 요청
}이유:
- 세 가지 요청 모두 승인/반려 워크플로우가 동일
- 엔티티 분리 시 중복 코드 발생
- 통합 관리로 알림/이력 처리 일원화
성공 응답:
{
"success": true,
"data": { ... }
}에러 응답:
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "해당 리소스를 찾을 수 없습니다."
}
}| 기능 | Method | Endpoint |
|---|---|---|
| 카카오 로그인 | POST | /api/auth/kakao/login |
| 사업장 등록 | POST | /api/employer/workplaces |
| 근로자 추가 | POST | /api/employer/workplaces/{id}/workers |
| 근무 일정 등록 | POST | /api/employer/work-records |
| 근무 완료 처리 | PUT | /api/employer/work-records/{id}/complete |
| 급여 계산 | POST | /api/employer/salaries/calculate |
| 정정 요청 승인 | PUT | /api/employer/correction-requests/{id}/approve |
| SSE 알림 구독 | GET | /api/notifications/stream |
전체 API 명세: API_SPECIFICATION.md
- Java 21+
- Gradle 8.x
- MySQL 8.0+
# 저장소 클론
git clone https://github.com/your-repo/PayCheck-backend.git
cd PayCheck-backend
# 환경 설정 (application.yml 또는 application-local.yml)
# MySQL 연결 정보 설정 필요
# 빌드
./gradlew clean build
# 실행
./gradlew bootRun# 전체 테스트
./gradlew test
# 특정 테스트 클래스
./gradlew test --tests WorkRecordCommandServiceTest
# 커버리지 리포트
./gradlew jacocoTestReport
# 결과: build/reports/jacoco/test/html/index.html서버 실행 후 Swagger UI 접속:
http://localhost:8080/swagger-ui.html
| 문서 | 설명 |
|---|---|
| API 명세서 | REST API 상세 명세 및 JSON 예시 |
| ERD | 엔티티 관계도 및 테이블 설계 |
| 급여 계산 정책 | 급여 계산 로직 상세 문서 |
| 유저 플로우 | 사용자 시나리오 |
<type>(<scope>): <subject>
# Types
feat : 새로운 기능
fix : 버그 수정
refactor : 리팩토링
test : 테스트 추가/수정
docs : 문서 변경
chore : 빌드, 설정 변경
# Scopes
auth, salary, workrecord, contract, user, api, db, config
# Example
feat(workrecord): add batch creation for work schedules
fix(salary): correct weekly allowance calculation for edge cases
MIT License