🔐 인증

Public API는 OAuth2 Bearer 토큰을 사용한 인증을 지원합니다.

인증 헤더

모든 API 요청에는 Authorization 헤더에 Bearer 토큰을 포함해야 합니다:
Authorization: Bearer <your-oauth2-access-token>

📊 API 엔드포인트

1. 사용자 정보 조회

요청

GET /api/auth/member/oauth2/me HTTP/1.1
Host: checkable.app
Authorization: Bearer ACCESS_TOKEN

응답

{
  "member": {
    "id": 123,
    "name": "discord_username",
    "email": "user@example.com"
  }
}

2. 챌린지 목록 조회

현재 날짜에 유효한 챌린지 목록을 조회합니다.

요청

POST /api/public/challenges HTTP/1.1
Host: checkable.app
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

응답

{
  "challenges": [
    {
      "uuid": "challenge-uuid",
      "title": "챌린지 제목",
      "guildName": "디스코드 서버명",
      "channelName": "채널명",
      "discordChannelId": "123456789",
      "discordMessageId": "987654321"
    }
  ]
}

3. 파일 업로드 URL 획득

파일 업로드를 위한 presigned URL을 획득합니다.

요청

POST /api/system/file/upload-url HTTP/1.1
Host: checkable.app
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

응답

{
  "uploadUrl": "https://storage.example.com/upload-url",
  "fileToken": "jwt-signed-file-token"
}

4. 메시지 전송

presigned URL로 업로드한 파일과 함께 메시지를 챌린지에 전송합니다.

요청

POST /api/public/challenges/{challengeUuid}/send-message HTTP/1.1
Host: checkable.app
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "message": "오늘 운동 완료!",
  "fileToken": "jwt-signed-file-token"
}

요청 파라미터

파라미터타입필수설명
challengeUuidstring챌린지 UUID (URL 경로)
messagestring메시지 내용 (선택사항)
fileTokenstringJWT로 서명된 파일 토큰 (선택사항)
참고: messagefileToken 중 하나는 반드시 제공해야 합니다. 둘 다 제공하거나, 텍스트만, 또는 파일만 제공할 수 있습니다.

응답

{
  "messageId": "discord-message-id"
}

📁 파일 업로드 시나리오

파일 업로드는 2단계 프로세스로 진행됩니다:

1단계: Presigned URL 획득

// 1. 파일 업로드 URL 요청
const response = await fetch('/api/system/file/upload-url', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json'
  }
});

const { uploadUrl, fileToken } = await response.json();

2단계: 파일 업로드

// 2. presigned URL로 파일 업로드
const file = document.getElementById('fileInput').files[0];
await fetch(uploadUrl, {
  method: 'PUT',
  body: file,
  headers: {
    'Content-Type': file.type
  }
});

3단계: 메시지 전송

// 3. 파일 토큰과 함께 메시지 전송 (파일이 있는 경우)
const uploadResponse = await fetch(`/api/public/challenges/${challengeUuid}/send-message`, {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    message: "오늘 운동 완료!",
    fileToken: fileToken // 1단계에서 받은 파일 토큰
  })
});

const { messageId } = await uploadResponse.json();
// 3. 메시지만 전송 (파일이 없는 경우)
const uploadResponse = await fetch(`/api/public/challenges/${challengeUuid}/send-message`, {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    message: "오늘 운동 완료!"
    // fileToken 없이 메시지만 전송
  })
});

const { messageId } = await uploadResponse.json();

파일 업로드 플로우

  1. Presigned URL 요청: 클라이언트가 파일 업로드를 위한 presigned URL을 요청
  2. 파일 업로드: 클라이언트가 presigned URL을 사용하여 파일을 직접 스토리지에 업로드
  3. 메시지 전송: 파일 토큰과 함께 메시지를 챌린지에 전송 (또는 메시지만 전송)
  4. Discord 전송: 서버가 파일을 다운로드하여 Discord 스레드에 전송 (또는 텍스트만 전송)

📤 메시지 전송

업로드된 메시지는 Discord 스레드에 전송됩니다:
  • 이미지 포함 시: Discord API를 통해 이미지와 함께 메시지 전송
  • 텍스트만 전송 시: Discord API를 통해 텍스트 메시지 전송

메시지 형식

{앱이름} 앱에서 올림 <@디스코드ID> {사용자메시지}
예시: Show Your Time 앱에서 올림 <@123456789> 오늘 운동 완료!

📞 지원

기술 지원

다음 단계