Attendance 모듈
출석 세션 관리, 학생 체크인, 출결 통계를 담당하는 모듈입니다. 코드 기반 출석 체크, 관리자 출석 관리, 학생별 출결 통계 분석 기능을 제공합니다.
모듈 개요
- 배포: lumie-backend 모놀리스의
modules/attendance - 데이터베이스: PostgreSQL (멀티테넌트 RLS)
- 주요 의존성:
StudentReadModel(읽기 모델, 학생 정보 동기화)
주요 기능
출석 세션 관리
- 출석 세션 생성 및 관리
- 수업별 당일 출석 세션 온디맨드 생성
- 6자리 출석 코드 자동 생성
- 세션 상태 관리 (OPEN/CLOSED)
- 지각 기준 시간 설정
- 세션별 출석 기록 자동 생성
- 동일 수업/날짜 중복 세션 방지
학생 체크인
- 출석 코드 기반 셀프 체크인
- 실시간 출석 상태 판정 (출석/지각)
- 중복 체크인 방지
- 코드 만료 시간 검증
출결 관리
- 관리자 출석 상태 수정
- 일괄 출석 상태 변경
- 출석 기록 조회 및 관리
- 메모 기능
출결 통계
- 학생별 출석률 계산
- 출석/지각/결 석/사유결석/미인증 통계
- 미인증(PENDING) 상태를 출석률 분모에서 제외
- 세션별 출석 현황
- 출결 기록 이력 관리
API 엔드포인트
출석 세션 관리 API
출석 세션 생성
POST /v1/attendance/sessions
Content-Type: application/json
{
"academyId": 1,
"name": "수학 1교시",
"sessionDate": "2024-01-15",
"subject": "수학",
"lateThresholdMinutes": 10
}
응답:
{
"id": 1,
"academyId": 1,
"name": "수학 1교시",
"sessionDate": "2024-01-15",
"subject": "수학",
"attendanceCode": "123456",
"codeExpiresAt": "2024-01-15T10:30:00",
"lateThresholdMinutes": 10,
"status": "OPEN",
"createdBy": 123,
"totalStudents": 25,
"presentCount": 0,
"absentCount": 25,
"lateCount": 0,
"excusedCount": 0
}
수업 당일 출석 세션 보장
POST /v1/attendance/classes/{classId}/sessions/ensure
수업의 오늘 날짜(KST) 출석 세션을 조회하거나 생성합니다. 이미 같은
classId와 sessionDate를 가진 세션이 있으면 기존 세션을 반환하고,
없으면 수업의 활성 학생에 대해 PENDING 출석 기록을 생성합니다.
출석 세션 목록 조회
GET /v1/attendance/sessions?academyId=1&dateFrom=2024-01-01&dateTo=2024-01-31&status=OPEN&page=0&size=20
출석 세션 상세 조회
GET /v1/attendance/sessions/{id}
출석 코드 재생성
POST /v1/attendance/sessions/{id}/regenerate-code
출석 세션 종료
POST /v1/attendance/sessions/{id}/close
출석 세션 삭제
DELETE /v1/attendance/sessions/{id}
학생 체크인 API
출석 코드로 체크인
POST /v1/attendance/checkin
Content-Type: application/json
{
"code": "123456"
}
응답:
{
"message": "출석 체크가 완료되었습니다.",
"status": "PRESENT"
}
출석 기록 관리 API
세션별 출석 기록 조회
GET /v1/attendance/sessions/{sessionId}/records
응답:
[
{
"id": 1,
"sessionId": 1,
"studentId": 123,
"studentName": "김학생",
"status": "PRESENT",
"checkMethod": "CODE",
"checkedAt": "2024-01-15T09:05:00",
"memo": null
}
]
출석 상태 수정
PUT /v1/attendance/sessions/{sessionId}/records/{recordId}/status
Content-Type: application/json
{
"status": "EXCUSED",
"memo": "병원 진료"
}
일괄 출석 상태 변경
POST /v1/attendance/sessions/{sessionId}/records/bulk-update
Content-Type: application/json
{
"recordIds": [1, 2, 3, 4, 5],
"status": "PRESENT"
}
학생 출결 조회 API
학생 출석 기록 조회
GET /v1/attendance/students/{studentId}/records
응답:
[
{
"id": 1,
"sessionId": 1,
"sessionName": "수학 1교시",
"sessionDate": "2024-01-15",
"studentId": 123,
"studentName": "김학생",
"status": "PRESENT",
"checkMethod": "CODE",
"checkedAt": "2024-01-15T09:05:00",
"memo": null
}
]
학생 출석 통계 조회
GET /v1/attendance/statistics/students/{studentId}
응답:
{
"studentId": 123,
"totalSessions": 20,
"presentCount": 18,
"absentCount": 1,
"lateCount": 1,
"excusedCount": 0,
"attendanceRate": 95.0
}
도메인 모델
AttendanceSession 엔티티
@Entity
public class AttendanceSession extends BaseEntity {
private Long academyId; // 학원 ID
private String name; // 세션명
private LocalDate sessionDate; // 수업 날짜
private String subject; // 과목
private String attendanceCode; // 6자리 출석 코드
private Instant codeExpiresAt; // 코드 만료 시간
private Integer lateThresholdMinutes; // 지각 기준 (분)
private SessionStatus status; // 세션 상태 (OPEN/CLOSED)
private Long createdBy; // 생성자 ID
private List<AttendanceRecord> records; // 출석 기록들
// 비즈니스 메서드
public static AttendanceSession create(Long academyId, String name,
LocalDate sessionDate, String subject,
Integer lateThresholdMinutes, Long createdBy);
public void close();
public void regenerateCode();
public boolean isExpired();
public boolean isOpen();
}
AttendanceRecord 엔티티
@Entity
public class AttendanceRecord extends BaseEntity {
private AttendanceSession session; // 출석 세션
private Long studentId; // 학생 ID
private AttendanceStatus status; // 출석 상태
private Instant checkedAt; // 체크인 시간
private Long checkedBy; // 체크인 처리자
private String memo; // 메모
// 비즈니스 메서드
public static AttendanceRecord create(AttendanceSession session, Long studentId);
public void updateStatus(AttendanceStatus status, String memo);
public void checkIn(AttendanceStatus status, Long checkedBy);
}
AttendanceRecord.create()는 기본 상태를 PENDING으로 둡니다. 교사가
수동으로 상태를 확정하거나 학생이 출석 코드로 체크인하기 전까지는
미인증 상태이며, 통계의 출석률 계산에서는 PENDING을 제외합니다.
StudentReadModel 엔티티
@Entity
public class StudentReadModel {
private Long id; // 학생 ID
private Long userId; // 사용자 ID
private String name; // 학생명
private String phone; // 전화번호
private Long academyId; // 학원 ID
private Boolean isActive; // 활성 상태
}
값 객체 (Value Objects)
AttendanceStatus
public enum AttendanceStatus {
PRESENT, // 출석
ABSENT, // 결석
LATE, // 지각
EXCUSED // 사유결석
}
CheckMethod
public enum CheckMethod {
CODE, // 코드 체크인
MANUAL // 수동 처리
}
SessionStatus
public enum SessionStatus {
OPEN, // 진행중
CLOSED // 종료
}
비즈니스 로직
출석 세션 생성 프로세스
- 세션 생성: 기본 정보와 6자리 출석 코드 생성
- 학생 조회:
StudentReadModel읽기 모델에서 해당 학원의 활성 학생 목록 조회 - 출석 기록 생성: 모든 학생에 대해 기본 상태(ABSENT)로 출석 기록 생성
- 세션 활성화: OPEN 상태로 설정하여 체크인 가능하게 함