Shared Authentication
Toss anonymous login
POST /api/v1/auth/toss/anonymous HTTP/1.1
Content-Type: application/json
Content-Length: 40
Host: localhost:8080
{"club":"wakeup","code":"one-time-code"}
| Path | Type | Description |
|---|---|---|
|
|
workout, wakeup, diet, attendance |
|
|
User.createAnonymousKeyAuthCode()의 일회용 인증 코드 |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 119
{"user":{"id":"dbcec423-e8aa-4195-a82e-9956bf0994df"},"accessToken":"signed-jwt","tokenType":"Bearer","expiresIn":3600}
| Path | Type | Description |
|---|---|---|
|
|
클럽 범위의 공통 인증 사용자 UUID |
|
|
해당 클럽 JWT |
|
|
Bearer |
|
|
JWT 유효기간 (초) |
Wakeup
JWT가 필요한 API는 Authorization: Bearer 헤더와 club=wakeup claim을 사용합니다. 출석 날짜는 한국 시간 기준이며, 중복 출석은 기존 출석을 반환합니다.
me/get
GET /api/v1/wakeup/me HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Host: localhost:8080
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 118
{"id":"10000000-0000-0000-0000-000000000001","name":"아침형","tossUserKey":null,"createdAt":"2026-09-29T00:00:00Z"}
| Path | Type | Description |
|---|---|---|
|
|
공통 인증 사용자 UUID |
|
|
이름 |
|
|
기존 토스 사용자 키 (nullable) |
|
|
가입 시각 (UTC) |
me/create
POST /api/v1/wakeup/me HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Content-Length: 20
Host: localhost:8080
{"name":"아침형"}
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Path | Type | Description |
|---|---|---|
|
|
1~16자 이름 |
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 118
{"id":"10000000-0000-0000-0000-000000000001","name":"아침형","tossUserKey":null,"createdAt":"2026-09-29T00:00:00Z"}
| Path | Type | Description |
|---|---|---|
|
|
공통 인증 사용자 UUID |
|
|
이름 |
|
|
기존 토스 사용자 키 (nullable) |
|
|
가입 시각 (UTC) |
me/update
PATCH /api/v1/wakeup/me HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Content-Length: 20
Host: localhost:8080
{"name":"새이름"}
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Path | Type | Description |
|---|---|---|
|
|
1~16자 이름 |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 118
{"id":"10000000-0000-0000-0000-000000000001","name":"새이름","tossUserKey":null,"createdAt":"2026-09-29T00:00:00Z"}
| Path | Type | Description |
|---|---|---|
|
|
공통 인증 사용자 UUID |
|
|
이름 |
|
|
기존 토스 사용자 키 (nullable) |
|
|
가입 시각 (UTC) |
rooms/list
GET /api/v1/wakeup/rooms HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Host: localhost:8080
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 276
[{"id":"20000000-0000-0000-0000-000000000001","code":"ABCDEF","name":"아침방","activationTime":"06:30","activationDurationMin":10,"capacity":8,"activeWeekdays":[1,2,3,4,5],"ownerId":"10000000-0000-0000-0000-000000000001","memberCount":1,"createdAt":"2026-09-29T00:00:00Z"}]
| Path | Type | Description |
|---|---|---|
|
|
방 UUID |
|
|
참여 코드 |
|
|
방 이름 |
|
|
기상 시각 (HH:mm, 한국 시간) |
|
|
기상 버튼 활성 시간 (분) |
|
|
정원 |
|
|
활성 요일 (일=0 ~ 토=6) |
|
|
방장 UUID |
|
|
현재 멤버 수 |
|
|
생성 시각 (UTC) |
rooms/create
POST /api/v1/wakeup/rooms HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Content-Length: 74
Host: localhost:8080
{"activeWeekdays":[1,2,3,4,5],"activationTime":"06:30","name":"아침방"}
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Path | Type | Description |
|---|---|---|
|
|
1~16자 방 이름 |
|
|
04:00~11:55 기상 시각 |
|
|
활성 요일 |
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 274
{"id":"20000000-0000-0000-0000-000000000001","code":"ABCDEF","name":"아침방","activationTime":"06:30","activationDurationMin":10,"capacity":8,"activeWeekdays":[1,2,3,4,5],"ownerId":"10000000-0000-0000-0000-000000000001","memberCount":1,"createdAt":"2026-09-29T00:00:00Z"}
| Path | Type | Description |
|---|---|---|
|
|
방 UUID |
|
|
참여 코드 |
|
|
방 이름 |
|
|
기상 시각 (HH:mm, 한국 시간) |
|
|
기상 버튼 활성 시간 (분) |
|
|
정원 |
|
|
활성 요일 (일=0 ~ 토=6) |
|
|
방장 UUID |
|
|
현재 멤버 수 |
|
|
생성 시각 (UTC) |
rooms/update
PATCH /api/v1/wakeup/rooms/20000000-0000-0000-0000-000000000001 HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Content-Length: 74
Host: localhost:8080
{"activeWeekdays":[1,2,3,4,5],"activationTime":"06:30","name":"아침방"}
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Parameter | Description |
|---|---|
|
방 UUID |
| Path | Type | Description |
|---|---|---|
|
|
1~16자 방 이름 |
|
|
04:00~11:55 기상 시각 |
|
|
활성 요일 |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 274
{"id":"20000000-0000-0000-0000-000000000001","code":"ABCDEF","name":"아침방","activationTime":"06:30","activationDurationMin":10,"capacity":8,"activeWeekdays":[1,2,3,4,5],"ownerId":"10000000-0000-0000-0000-000000000001","memberCount":1,"createdAt":"2026-09-29T00:00:00Z"}
| Path | Type | Description |
|---|---|---|
|
|
방 UUID |
|
|
참여 코드 |
|
|
방 이름 |
|
|
기상 시각 (HH:mm, 한국 시간) |
|
|
기상 버튼 활성 시간 (분) |
|
|
정원 |
|
|
활성 요일 (일=0 ~ 토=6) |
|
|
방장 UUID |
|
|
현재 멤버 수 |
|
|
생성 시각 (UTC) |
rooms/delete
DELETE /api/v1/wakeup/rooms/20000000-0000-0000-0000-000000000001 HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Host: localhost:8080
| Name | Description |
|---|---|
|
Wakeup 클럽 JWT (Bearer) |
| Parameter | Description |
|---|---|
|
방 UUID |
HTTP/1.1 204 No Content
rooms/join
POST /api/v1/wakeup/rooms/join HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Content-Length: 17
Host: localhost:8080
{"code":"ABCDEF"}
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Path | Type | Description |
|---|---|---|
|
|
방 참여 코드 |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 274
{"id":"20000000-0000-0000-0000-000000000001","code":"ABCDEF","name":"아침방","activationTime":"06:30","activationDurationMin":10,"capacity":8,"activeWeekdays":[1,2,3,4,5],"ownerId":"10000000-0000-0000-0000-000000000001","memberCount":1,"createdAt":"2026-09-29T00:00:00Z"}
| Path | Type | Description |
|---|---|---|
|
|
방 UUID |
|
|
참여 코드 |
|
|
방 이름 |
|
|
기상 시각 (HH:mm, 한국 시간) |
|
|
기상 버튼 활성 시간 (분) |
|
|
정원 |
|
|
활성 요일 (일=0 ~ 토=6) |
|
|
방장 UUID |
|
|
현재 멤버 수 |
|
|
생성 시각 (UTC) |
rooms/leave
DELETE /api/v1/wakeup/rooms/membership HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Host: localhost:8080
| Name | Description |
|---|---|
|
Wakeup 클럽 JWT (Bearer) |
HTTP/1.1 204 No Content
membership/get
GET /api/v1/wakeup/rooms/20000000-0000-0000-0000-000000000001/membership HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Host: localhost:8080
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Parameter | Description |
|---|---|
|
방 UUID |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 92
{"notifyEnabled":false,"notificationAgreementAt":null,"reminderOffsetMin":0,"nickname":null}
| Path | Type | Description |
|---|---|---|
|
|
알림 수신 여부 |
|
|
알림 동의 시각 (nullable) |
|
|
기상 시각 이전 알림 분 (0~60) |
|
|
방 닉네임 (nullable) |
membership/update
PATCH /api/v1/wakeup/rooms/20000000-0000-0000-0000-000000000001/membership HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Content-Length: 87
Host: localhost:8080
{"notifyEnabled":true,"nickname":"방장","reminderOffsetMin":10,"clearNickname":false}
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Parameter | Description |
|---|---|
|
방 UUID |
| Path | Type | Description |
|---|---|---|
|
|
알림 수신 여부 |
|
|
기상 시각 이전 알림 분 (0~60) |
|
|
1~16자 방 닉네임 |
|
|
닉네임 삭제 여부 |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 114
{"notifyEnabled":true,"notificationAgreementAt":"2026-09-29T00:00:00Z","reminderOffsetMin":10,"nickname":"방장"}
| Path | Type | Description |
|---|---|---|
|
|
알림 수신 여부 |
|
|
알림 동의 시각 (nullable) |
|
|
기상 시각 이전 알림 분 (0~60) |
|
|
방 닉네임 (nullable) |
members/list
GET /api/v1/wakeup/rooms/20000000-0000-0000-0000-000000000001/members HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Host: localhost:8080
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Parameter | Description |
|---|---|
|
방 UUID |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 67
[{"userId":"10000000-0000-0000-0000-000000000001","name":"방장"}]
| Path | Type | Description |
|---|---|---|
|
|
멤버 UUID |
|
|
방 닉네임 또는 사용자 이름 |
check-ins/create
POST /api/v1/wakeup/rooms/20000000-0000-0000-0000-000000000001/check-ins HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Host: localhost:8080
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Parameter | Description |
|---|---|
|
방 UUID |
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 168
{"id":"aa48bbe2-2d67-4dbb-bf33-cd0b8ec22477","userId":"10000000-0000-0000-0000-000000000001","userName":"방장","date":"2026-09-29","pressedAt":"2026-09-29T00:00:00Z"}
| Path | Type | Description |
|---|---|---|
|
|
출석 UUID |
|
|
출석 사용자 UUID |
|
|
출석 당시 이름 |
|
|
출석 날짜 (한국 시간) |
|
|
서버에서 기록한 버튼 입력 시각 (UTC) |
check-ins/list
GET /api/v1/wakeup/rooms/20000000-0000-0000-0000-000000000001/check-ins HTTP/1.1
Content-Type: application/json
Authorization: Bearer documented-token
Host: localhost:8080
| Name | Description |
|---|---|
|
Wakeup JWT (Bearer) |
| Parameter | Description |
|---|---|
|
방 UUID |
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 170
[{"id":"027a9cca-9464-4223-a3d8-a458f4c1e551","userId":"10000000-0000-0000-0000-000000000001","userName":"방장","date":"2026-09-29","pressedAt":"2026-09-29T00:00:00Z"}]
| Path | Type | Description |
|---|---|---|
|
|
출석 UUID |
|
|
출석 사용자 UUID |
|
|
출석 당시 이름 |
|
|
출석 날짜 (한국 시간) |
|
|
서버에서 기록한 버튼 입력 시각 (UTC) |
Attendance
Attendance Image Comparison
입실 사진과 퇴실 사진의 전체 색 분포를 비교합니다. 객체가 동일하다는 증명이나 출석 확정 결과가 아니며, 사진과 출석 기록을 저장하지 않습니다. 방·입퇴실 기록은 현재 클라이언트의 Supabase 연동에서 처리합니다.
-
메서드/경로:
POST /api/v1/attendance/images/compare -
인증: 공개 API이며 Authorization 헤더 없이 호출할 수 있습니다.
-
요청:
multipart/form-data -
성공:
200 OK, JSON 객체를 직접 반환합니다.
요청
`reference`는 입실 사진, `candidate`는 퇴실 사진입니다. 파일 파트는 이 두 이름으로 각각 정확히 하나씩 보내며, 다른 파일 파트나 중복 파일은 허용하지 않습니다. 파일명·Content-Type만으로 형식을 판단하지 않고 실제 JPEG 바이트와 디코딩 가능 여부를 검증합니다.
| 제약 | 기준 |
|---|---|
파일 크기 |
파일당 최대 8 MiB (8 × 1024 × 1024 bytes) |
전체 multipart 요청 |
최대 17 MiB (multipart 부가 데이터 포함, 기본 설정) |
픽셀 수 |
이미지당 최대 4,000,000 pixels, 디코딩 전에 검사 |
요청 빈도 |
서버의 ImageService 인스턴스당 1분 고정 구간 최대 120회 |
동시 비교 |
ImageService 인스턴스당 최대 4개, 대기 없이 초과 요청 거절 |
요청 제한은 사용자별 한도가 아닙니다. 여러 서버 인스턴스가 공유하는 전역 한도도 아닙니다. 빈도 검사 후 동시 실행 한도를 검사하므로 동시 실행 초과 요청도 빈도에 포함됩니다.
$ curl 'http://localhost:8080/api/v1/attendance/images/compare' -i -X POST \
-H 'Content-Type: multipart/form-data;charset=ISO-8859-1' \
-F 'reference=@check-in.jpg;type=image/jpeg' \
-F 'candidate=@check-out.jpg;type=image/jpeg'
| Part | Description |
|---|---|
|
입실 JPEG 사진, 최대 8 MiB·4,000,000 pixels, 정확히 하나 |
|
퇴실 JPEG 사진, 최대 8 MiB·4,000,000 pixels, 정확히 하나 |
성공 응답
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 38
{"verdict":"MATCH","reason":"similar"}
| Path | Type | Description |
|---|---|---|
|
|
색 분포 판정: MATCH, NON_MATCH, UNDECIDABLE |
|
|
판정 사유: similar, different, uncertain |
| verdict | reason | 클라이언트 처리 |
|---|---|---|
MATCH |
similar |
색 분포가 유사합니다. 출석 처리에는 별도 업무 규칙을 적용합니다. |
NON_MATCH |
different |
색 분포가 다릅니다. 사진과 촬영 환경을 확인하도록 안내합니다. |
UNDECIDABLE |
uncertain |
판정이 애매합니다. 확정 성공으로 처리하지 않고 확인을 안내합니다. |
세 판정 모두 비교가 완료된 정상 200 응답입니다.
HSV 유사도 점수는 공개 응답에 포함하지 않으며 객체 동일성·확률로 해석하지 않습니다.
배경과 조명에 영향을 받고 서로 다른 객체도 비슷한 색 분포를 가지면 MATCH가 될 수 있습니다.
오류와 재시도
업무 오류는 application/problem+json`의 ProblemDetail로 반환합니다.
클라이언트는 `code`로 분기하고 `detail 문구를 비교하지 않습니다.
| HTTP | code | 원인과 처리 |
|---|---|---|
400 |
missing_image |
파일 파트 누락·중복·추가. 두 파일 파트를 정확히 구성합니다. |
413 |
image_too_large |
파일·요청 크기 또는 픽셀 수 초과. 사진을 축소합니다. |
415 |
unsupported_image_type |
JPEG 형식 미지원. JPEG로 변환합니다. |
422 |
invalid_image |
빈 파일 또는 디코딩 불가. 정상 사진을 다시 선택합니다. |
429 |
rate_limited |
요청 빈도 또는 동시 비교 한도 초과. 지연 후 제한된 횟수로 재시도합니다. |
503 |
comparison_unavailable |
비교 계산 중 장애. 잠시 후 제한된 횟수로 재시도합니다. |
`Retry-After`는 현재 제공하지 않습니다. 아래 예제는 필수 파일 누락에 대한 실제 테스트 응답입니다.
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content-Length: 163
{"detail":"Both image parts are required exactly once.","instance":"/api/v1/attendance/images/compare","status":400,"title":"Missing image","code":"missing_image"}
| Path | Type | Description |
|---|---|---|
|
|
문제 유형 URI. 기본 유형일 때 생략될 수 있음 |
|
|
오류 제목 |
|
|
HTTP 상태 코드 |
|
|
오류 설명. 분기 조건으로 사용하지 않음 |
|
|
오류가 발생한 요청 경로 |
|
|
클라이언트 분기용 오류 코드: missing_image |
처리 흐름
클라이언트 → 공개 API (JWT 인증 불필요) → ImageController의 파일 파트 검증 → ImageService의 요청·동시 실행 제한 → JPEG·크기·픽셀 검증 → HSV 비교 → verdict/reason 응답. 이 흐름은 DB나 Toss를 호출하지 않습니다.