Skip to content

Commit 251a2df

Browse files
authored
[BE] SISC1-201 [DOCS] Swagger 고도화 - API 응답 형식 정의 (#76)
1 parent cca7e71 commit 251a2df

6 files changed

Lines changed: 351 additions & 0 deletions

File tree

backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceController.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.sejongisc.backend.attendance.controller;
22

3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.tags.Tag;
35
import jakarta.validation.Valid;
46
import lombok.RequiredArgsConstructor;
57
import lombok.extern.slf4j.Slf4j;
@@ -20,6 +22,10 @@
2022
@RequiredArgsConstructor
2123
@RequestMapping("/api/attendance")
2224
@Slf4j
25+
@Tag(
26+
name = "출석(Attendance) API",
27+
description = "학생 출석 체크인 및 관리자 출석 현황 조회 관련 API"
28+
)
2329
public class AttendanceController {
2430

2531
private final AttendanceService attendanceService;
@@ -30,6 +36,12 @@ public class AttendanceController {
3036
* - 위치 범위, 시간 위도우 검증 포함
3137
* - 중복 출석 방지
3238
*/
39+
@Operation(
40+
summary = "출석 체크인",
41+
description = "학생이 출석 코드와 GPS 위치를 제시하여 출석 체크인을 진행합니다. " +
42+
"세션의 지정된 위치 범위 내에 있어야 하며, 시간 윈도우 내에만 체크인이 가능합니다. " +
43+
"시작 시간 5분 이내는 PRESENT, 이후는 LATE로 자동 판별됩니다."
44+
)
3345
@PostMapping("/sessions/{sessionId}/check-in")
3446
public ResponseEntity<AttendanceResponse> checkIn(
3547
@PathVariable UUID sessionId,
@@ -50,6 +62,11 @@ public ResponseEntity<AttendanceResponse> checkIn(
5062
* - 특정 세션의 모든 출석 기록 조회
5163
* - 출석 시간 순으로 정렬
5264
*/
65+
@Operation(
66+
summary = "세션별 출석 목록 조회",
67+
description = "특정 세션에 참가한 모든 학생의 출석 기록을 조회합니다. (관리자 전용) " +
68+
"출석 시간 순으로 정렬되며, 각 학생의 상태, 체크인 시간, 포인트 등이 포함됩니다."
69+
)
5370
@GetMapping("/sessions/{sessionId}/attendances")
5471
@PreAuthorize("hasRole('PRESIDENT') or hasRole('VICE_PRESIDENT')")
5572
public ResponseEntity<List<AttendanceResponse>> getAttendancesBySession(@PathVariable UUID sessionId) {
@@ -65,6 +82,11 @@ public ResponseEntity<List<AttendanceResponse>> getAttendancesBySession(@PathVar
6582
* - 로그인한 사용자의 모든 출석 기록 조회
6683
* - 최신 순으로 정렬
6784
*/
85+
@Operation(
86+
summary = "내 출석 기록 조회",
87+
description = "로그인한 사용자의 모든 출석 기록을 최신 순으로 조회합니다. " +
88+
"각 출석 기록에는 세션 정보, 출석 상태, 체크인 시간, 획득 포인트 등이 포함됩니다."
89+
)
6890
@GetMapping("/history")
6991
public ResponseEntity<List<AttendanceResponse>> getMyAttendances(
7092
@AuthenticationPrincipal CustomUserDetails userDetails) {
@@ -80,6 +102,12 @@ public ResponseEntity<List<AttendanceResponse>> getMyAttendances(
80102
* - PRESENT/LATE/ABSENT 등으로 상태 변경
81103
* - 수정 사유 기록 가능
82104
*/
105+
@Operation(
106+
summary = "출석 상태 수정",
107+
description = "특정 학생의 출석 상태를 변경합니다. (관리자 전용) " +
108+
"PRESENT(출석), LATE(지각), ABSENT(결석), EXCUSED(사유결석) 등의 상태로 변경 가능하며, " +
109+
"변경 사유를 함께 기록할 수 있습니다."
110+
)
83111
@PostMapping("/sessions/{sessionId}/attendances/{memberId}")
84112
@PreAuthorize("hasRole('PRESIDENT') or hasRole('VICE_PRESIDENT')")
85113
public ResponseEntity<AttendanceResponse> updateAttendanceStatus(

backend/src/main/java/org/sejongisc/backend/attendance/controller/AttendanceSessionController.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.sejongisc.backend.attendance.controller;
22

3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.tags.Tag;
35
import jakarta.validation.Valid;
46
import lombok.RequiredArgsConstructor;
57
import lombok.extern.slf4j.Slf4j;
@@ -19,6 +21,10 @@
1921
@RequiredArgsConstructor
2022
@RequestMapping("/api/attendance/sessions")
2123
@Slf4j
24+
@Tag(
25+
name = "출석 세션(Attendance Session) API",
26+
description = "출석 세션 생성, 조회, 수정, 삭제 및 상태 관리 관련 API"
27+
)
2228
public class AttendanceSessionController {
2329

2430
private final AttendanceSessionService attendanceSessionService;
@@ -29,6 +35,12 @@ public class AttendanceSessionController {
2935
* - GPS 위치 및 반경 설정
3036
* - 시간 윈도우 설정
3137
*/
38+
@Operation(
39+
summary = "출석 세션 생성",
40+
description = "새로운 출석 세션을 생성합니다. (관리자 전용) " +
41+
"6자리 랜덤 코드가 자동 생성되며, GPS 위치 정보, 시간 윈도우, " +
42+
"보상 포인트 등을 설정할 수 있습니다."
43+
)
3244
@PostMapping
3345
@PreAuthorize("hasRole('PRESIDENT') or hasRole('VICE_PRESIDENT')")
3446
public ResponseEntity<AttendanceSessionResponse> createSession(@Valid @RequestBody AttendanceSessionRequest request) {
@@ -46,6 +58,11 @@ public ResponseEntity<AttendanceSessionResponse> createSession(@Valid @RequestBo
4658
* - 세션 ID로 상세 정보 조회
4759
* - 남은 시간, 참여자 수 등 포함
4860
*/
61+
@Operation(
62+
summary = "세션 상세 조회",
63+
description = "세션 ID로 특정 세션의 상세 정보를 조회합니다. " +
64+
"남은 체크인 시간, 현재 참여자 수, 세션 상태 등의 정보가 포함됩니다."
65+
)
4966
@GetMapping("/{sessionId}")
5067
public ResponseEntity<AttendanceSessionResponse> getSession(@PathVariable UUID sessionId) {
5168
log.info("출석 세션 조회: 세션ID={}", sessionId);
@@ -60,6 +77,11 @@ public ResponseEntity<AttendanceSessionResponse> getSession(@PathVariable UUID s
6077
* - 학생이 출석 코드 입력 시 사용
6178
* - 체크인 가능 여부 확인
6279
*/
80+
@Operation(
81+
summary = "출석 코드로 세션 조회",
82+
description = "6자리 출석 코드를 입력하여 해당 세션을 조회합니다. (학생용) " +
83+
"체크인 가능 여부, 남은 시간 등의 정보를 확인할 수 있습니다."
84+
)
6385
@GetMapping("/code/{code}")
6486
public ResponseEntity<AttendanceSessionResponse> getSessionByCode(@PathVariable String code) {
6587
log.info("출석 코드로 세션 조회: 코드={}", code);
@@ -74,6 +96,11 @@ public ResponseEntity<AttendanceSessionResponse> getSessionByCode(@PathVariable
7496
* - 최신 순으로 정렬
7597
* - 공개/비공개 세션 모두 포함
7698
*/
99+
@Operation(
100+
summary = "모든 세션 목록 조회",
101+
description = "생성된 모든 출석 세션을 최신 순으로 조회합니다. " +
102+
"공개/비공개 세션이 모두 포함되며, 관리자는 모든 세션을 볼 수 있습니다."
103+
)
77104
@GetMapping
78105
public ResponseEntity<List<AttendanceSessionResponse>> getAllSessions() {
79106
log.info("모든 출석 세션 조회");
@@ -88,6 +115,11 @@ public ResponseEntity<List<AttendanceSessionResponse>> getAllSessions() {
88115
* - 학생들이 볼 수 있는 공개 세션만 조회
89116
* - 최신 순으로 정렬
90117
*/
118+
@Operation(
119+
summary = "공개 세션 목록 조회",
120+
description = "학생들이 볼 수 있는 공개 세션들을 최신 순으로 조회합니다. " +
121+
"비공개 세션은 제외됩니다."
122+
)
91123
@GetMapping("/public")
92124
public ResponseEntity<List<AttendanceSessionResponse>> getPublicSessions() {
93125
log.info("공개 출석 세션 조회");
@@ -102,6 +134,11 @@ public ResponseEntity<List<AttendanceSessionResponse>> getPublicSessions() {
102134
* - 체크인 가능한 세션들만 조회
103135
* - 시작시간 ~ 종료 시간 범위 내
104136
*/
137+
@Operation(
138+
summary = "활성 세션 목록 조회",
139+
description = "현재 체크인이 가능한 활성 세션들을 조회합니다. " +
140+
"세션 시작 시간부터 시간 윈도우 종료까지 범위 내인 세션들만 조회됩니다."
141+
)
105142
@GetMapping("/active")
106143
public ResponseEntity<List<AttendanceSessionResponse>> getActiveSessions() {
107144
log.info("활성 출석 세션 조회");
@@ -115,6 +152,11 @@ public ResponseEntity<List<AttendanceSessionResponse>> getActiveSessions() {
115152
* 태그별 세션 목록 조회
116153
* - "금융IT", "동아리 전체" 등 태그로 필터링
117154
*/
155+
@Operation(
156+
summary = "태그별 세션 목록 조회",
157+
description = "특정 태그를 가진 세션들을 조회합니다. " +
158+
"예: '금융IT', '동아리 전체', '스터디' 등으로 세션을 분류할 수 있습니다."
159+
)
118160
@GetMapping("/tag/{tag}")
119161
public ResponseEntity<List<AttendanceSessionResponse>> getSessionsByTag(@PathVariable String tag) {
120162
log.info("태그별 출석 세션 조회: 태그={}", tag);
@@ -128,6 +170,11 @@ public ResponseEntity<List<AttendanceSessionResponse>> getSessionsByTag(@PathVar
128170
* 상태별 세션 목록 조회 (관리자용)
129171
* - UPCOMING/OPEN/CLOSED 상태별 필터링
130172
*/
173+
@Operation(
174+
summary = "상태별 세션 목록 조회",
175+
description = "특정 상태의 세션들을 조회합니다. (관리자 전용) " +
176+
"UPCOMING(예정), OPEN(진행중), CLOSED(종료) 상태별로 필터링할 수 있습니다."
177+
)
131178
@GetMapping("/status/{status}")
132179
@PreAuthorize("hasRole('PRESIDENT') or hasRole('VICE_PRESIDENT')")
133180
public ResponseEntity<List<AttendanceSessionResponse>> getSessionsByStatus(@PathVariable SessionStatus status) {
@@ -143,6 +190,12 @@ public ResponseEntity<List<AttendanceSessionResponse>> getSessionsByStatus(@Path
143190
* - 제목, 시간, 위치, 반경 등 수정 가능
144191
* - 코드는 변경 불가
145192
*/
193+
@Operation(
194+
summary = "세션 정보 수정",
195+
description = "세션의 기본 정보를 수정합니다. (관리자 전용) " +
196+
"제목, 태그, 시간, GPS 위치, 반경, 포인트 등을 수정할 수 있으며, " +
197+
"6자리 코드는 변경할 수 없습니다."
198+
)
146199
@PutMapping("/{sessionId}")
147200
@PreAuthorize("hasRole('PRESIDENT') or hasRole('VICE_PRESIDENT')")
148201
public ResponseEntity<AttendanceSessionResponse> updateSession(
@@ -163,6 +216,11 @@ public ResponseEntity<AttendanceSessionResponse> updateSession(
163216
* - 세션 상태를 OPEN으로 변경
164217
* - 체크인 수동 활성화
165218
*/
219+
@Operation(
220+
summary = "세션 활성화",
221+
description = "세션을 수동으로 활성화하여 즉시 체크인을 가능하게 합니다. (관리자 전용) " +
222+
"세션 상태가 OPEN으로 변경되어 학생들이 체크인할 수 있습니다."
223+
)
166224
@PostMapping("/{sessionId}/activate")
167225
@PreAuthorize("hasRole('PRESIDENT') or hasRole('VICE_PRESIDENT')")
168226
public ResponseEntity<Void> activateSession(@PathVariable UUID sessionId) {
@@ -180,6 +238,11 @@ public ResponseEntity<Void> activateSession(@PathVariable UUID sessionId) {
180238
* - 세션 상태를 CLOSED로 변경
181239
* - 체크인 수동 종료
182240
*/
241+
@Operation(
242+
summary = "세션 종료",
243+
description = "세션을 종료합니다. (관리자 전용) " +
244+
"세션 상태가 CLOSED로 변경되어 더 이상 체크인이 불가능합니다."
245+
)
183246
@PostMapping("/{sessionId}/close")
184247
@PreAuthorize("hasRole('PRESIDENT') or hasRole('VICE_PRESIDENT')")
185248
public ResponseEntity<Void> closeSession(@PathVariable UUID sessionId) {
@@ -197,6 +260,11 @@ public ResponseEntity<Void> closeSession(@PathVariable UUID sessionId) {
197260
* - 세션 완전 삭제 (출석 기록도 함께 삭제)
198261
* - 주의: 복구 불가
199262
*/
263+
@Operation(
264+
summary = "세션 삭제",
265+
description = "세션을 완전히 삭제합니다. (관리자 전용) " +
266+
"⚠️ 주의: 해당 세션의 모든 출석 기록이 함께 삭제되며, 복구가 불가능합니다."
267+
)
200268
@DeleteMapping("/{sessionId}")
201269
@PreAuthorize("hasRole('PRESIDENT') or hasRole('VICE_PRESIDENT')")
202270
public ResponseEntity<Void> deleteSession(@PathVariable UUID sessionId) {

backend/src/main/java/org/sejongisc/backend/attendance/dto/AttendanceRequest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.sejongisc.backend.attendance.dto;
22

3+
import io.swagger.v3.oas.annotations.media.Schema;
34
import jakarta.validation.constraints.*;
45
import lombok.AllArgsConstructor;
56
import lombok.Builder;
@@ -11,26 +12,58 @@
1112
@Builder
1213
@NoArgsConstructor
1314
@AllArgsConstructor
15+
@Schema(
16+
title = "출석 체크인 요청",
17+
description = "학생이 출석 체크인 시 제출하는 요청 객체. 출석 코드, GPS 위치 정보를 포함합니다."
18+
)
1419
public class AttendanceRequest {
1520

1621
// === 출석 체크용 필드들 ===
22+
@Schema(
23+
description = "출석 세션의 6자리 코드. 관리자가 생성한 코드를 입력합니다.",
24+
example = "ABC123",
25+
minLength = 6,
26+
maxLength = 6
27+
)
1728
@NotBlank(message = "출석 코드는 필수입니다")
1829
@Size(min = 6, max = 6, message = "출석 코드는 6자리여야 합니다")
1930
private String code;
2031

32+
@Schema(
33+
description = "체크인 위치의 위도 (latitude). WGS84 좌표계 사용. 범위: -90 ~ 90",
34+
example = "37.4979",
35+
minimum = "-90.0",
36+
maximum = "90.0"
37+
)
2138
@NotNull(message = "위도는 필수입니다")
2239
@DecimalMin(value = "-90.0", message = "위도는 -90 이상이어야 합니다")
2340
@DecimalMax(value = "90.0", message = "위도는 90 이하이어야 합니다")
2441
private Double latitude;
2542

43+
@Schema(
44+
description = "체크인 위치의 경도 (longitude). WGS84 좌표계 사용. 범위: -180 ~ 180",
45+
example = "127.0276",
46+
minimum = "-180.0",
47+
maximum = "180.0"
48+
)
2649
@NotNull(message = "경도는 필수입니다")
2750
@DecimalMin(value = "-180.0", message = "경도는 -180 이상이어야 합니다")
2851
@DecimalMax(value = "180.0", message = "경도는 180 이하이어야 합니다")
2952
private Double longitude;
3053

54+
@Schema(
55+
description = "체크인 시 추가 메모. 선택 사항입니다.",
56+
example = "교실 앞에서 체크인",
57+
maxLength = 500
58+
)
3159
@Size(max = 500, message = "메모는 500자 이하여야 합니다")
3260
private String note;
3361

62+
@Schema(
63+
description = "체크인에 사용한 디바이스 정보. 예: 'iPhone 12', 'Android Pixel 6' 등",
64+
example = "iPhone 14 Pro",
65+
maxLength = 200
66+
)
3467
@Size(max = 200, message = "디바이스 정보는 200자 이하여야 합니다")
3568
private String deviceInfo;
3669

0 commit comments

Comments
 (0)