사용자 API

사용자의 정보를 조회 및 수정할 수 있습니다.

Request Header 에는 Authorization: Bearer {access token} 와 같은 형식으로 보내야 하며, 사용자의 액세스 토큰이 필요합니다.

사용자 조회

사용자의 정보를 조회합니다.

HTTP request

GET /api/member HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 122

{
  "message" : "",
  "data" : {
    "nickname" : "HabitPay",
    "imageUrl" : "https://picsum.photos/id/40/200/300"
  }
}

Response fields

Path Type Description

message

String

메세지

data.nickname

String

사용자 닉네임

data.imageUrl

String

사용자 이미지 URL

사용자 닉네임 변경

사용자의 닉네임을 변경합니다.

닉네임 규칙은 아래와 같습니다.

  • 길이: 2~15자

  • 문자: 영어 대소문자, 한글 (특수문자 제외)

HTTP request

PATCH /api/member/nickname HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 33
Host: localhost:8080

{
  "nickname" : "testNickname"
}

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 109

{
  "message" : "닉네임 변경에 성공했습니다.",
  "data" : {
    "nickname" : "testNickname"
  }
}

Response fields

Path Type Description

message

String

메세지

data.nickname

String

닉네임

에러 응답

1. 닉네임 규칙에 맞지 않는 경우 (400 Bad Request)

닉네임 규칙에 맞지 않는 경우입니다.

HTTP request
PATCH /api/member/nickname HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 40
Host: localhost:8080

{
  "nickname" : "invalid.#_!nickname"
}
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 142

{
  "code" : "INVALID_NICKNAME_RULE",
  "message" : "닉네임 규칙에 맞지 않습니다. (규칙: 길이 2~15자, 특수문자 제외)"
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

2. 이전과 동일한 닉네임인 경우 (400 Bad Request)

이전에 사용했던 닉네임과 동일한 닉네임을 사용한 경우입니다.

HTTP request
PATCH /api/member/nickname HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 39
Host: localhost:8080

{
  "nickname" : "duplicatedNickname"
}
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 117

{
  "code" : "DUPLICATED_NICKNAME",
  "message" : "이전과 동일한 닉네임으로 변경할 수 없습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

사용자 이미지 변경

사용자의 이미지를 변경합니다.

이미지 파일은 아래의 조건을 만족해야 합니다.

  • 확장자: jpg, jpeg, png

  • 크기: 1MB 이하

응답으로 반환되는 preSignedUrl 은 프론트엔드에서 S3 로 이미지를 직접 업로드 하기 위한 링크입니다.

HTTP request

PATCH /api/member/image HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 54
Host: localhost:8080

{
  "extension" : "jpg",
  "contentLength" : 1048576
}

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 161

{
  "message" : "프로필 이미지 변경에 성공했습니다.",
  "data" : {
    "preSignedUrl" : "https://{AWS S3 preSignedUrl to upload image file}"
  }
}

Response fields

Path Type Description

message

String

메세지

data.preSignedUrl

String

AWS S3 업로드를 위한 preSignedUrl

에러 응답

1. 이미지 크기가 초과한 경우 (400 Bad Request)

이미지 크기가 1MB 초과한 경우입니다.

HTTP request
PATCH /api/member/image HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 57
Host: localhost:8080

{
  "extension" : "jpg",
  "contentLength" : 1073741824
}
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 135

{
  "code" : "PROFILE_IMAGE_SIZE_TOO_LARGE",
  "message" : "이미지 파일의 크기가 제한을 초과했습니다. (최대 1MB)"
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

2. 허용하지 않는 이미지 확장자인 경우 (400 Bad Request)

허용하는 이미지 확장자가 아닌 경우입니다.

HTTP request
PATCH /api/member/image HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 67
Host: localhost:8080

{
  "extension" : "invalidExtension",
  "contentLength" : 1048576
}
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 139

{
  "code" : "UNSUPPORTED_IMAGE_EXTENSION",
  "message" : "지원하지 않는 이미지 확장자입니다. (png, jpg, jpeg 만 가능)"
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

회원 탈퇴

회원 탈퇴를 진행합니다.

반환되는 값은 DB 테이블의 Primary Key 에 해당하는 사용자의 ID 입니다.

HTTP request

DELETE /api/member HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 72

{
  "message" : "정상적으로 탈퇴되었습니다.",
  "data" : 1
}

Response fields

Path Type Description

message

String

메세지

data

Number

Member ID

챌린지 API

챌린지 정보 조회, 챌린지 생성, 챌린지 정보 수정을 수행하는 API 목록입니다.

전체 챌린지 목록 조회

전체 챌린지 목록을 조회합니다.

1페이지에 20개씩 반환합니다.

HTTP request

GET /api/challenges?page=page-number HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 596

{
  "message" : "",
  "data" : {
    "content" : [ {
      "id" : 1,
      "title" : "챌린지 제목",
      "startDate" : "2024-11-14T11:26:15.820761315Z",
      "endDate" : "2024-11-19T11:26:15.820777846Z",
      "stopDate" : null,
      "numberOfParticipants" : 1,
      "participatingDays" : 4,
      "isStarted" : true,
      "isEnded" : false,
      "hostNickname" : "챌린지 주최자 닉네임",
      "hostProfileImage" : "챌린지 주최자 프로필 이미지"
    } ],
    "page" : 1,
    "size" : 1,
    "totalElements" : 1,
    "totalPages" : 1,
    "hasNextPage" : false
  }
}

Response fields

Path Type Description

message

String

메세지

data.content[].id

Number

챌린지 ID

data.content[].title

String

챌린지 제목

data.content[].startDate

String

챌린지 시작 일시

data.content[].endDate

String

챌린지 종료 일시

data.content[].stopDate

Null

챌린지 중단 일시

data.content[].numberOfParticipants

Number

챌린지 참여자 수

data.content[].participatingDays

Number

챌린지 총 진행 일

data.content[].isStarted

Boolean

챌린지 시작 여부

data.content[].isEnded

Boolean

챌린지 종료 여부

data.content[].hostNickname

String

챌린지 주최자 닉네임

data.content[].hostProfileImage

String

챌린지 주최자 프로필 이미지

data.page

Number

현재 페이지 번호

data.size

Number

현재 페이지 조회 결과 건수

data.totalElements

Number

전체 페이지 조회 결과 건수

data.totalPages

Number

전체 페이지 수

data.hasNextPage

Boolean

다음 페이지 존재 유무

나의 챌린지 참여 목록 조회

현재 접속한 사용자가 참여하고 있는 챌린지 목록을 반환합니다.

HTTP request

GET /api/challenges/me HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 606

{
  "message" : "",
  "data" : [ {
    "challengeId" : 1,
    "title" : "챌린지 제목",
    "description" : "챌린지 설명",
    "startDate" : "2024-11-14T11:26:16.21295216Z",
    "endDate" : "2024-11-19T11:26:16.212967579Z",
    "stopDate" : null,
    "totalParticipatingDaysCount" : 2,
    "numberOfParticipants" : 1,
    "participatingDays" : 4,
    "totalFee" : 1000,
    "isPaidAll" : false,
    "hostProfileImage" : "챌린지 주최자 프로필 이미지",
    "isMemberGivenUp" : false,
    "successCount" : 4,
    "isTodayParticipatingDay" : true,
    "isParticipatedToday" : false
  } ]
}

Response fields

Path Type Description

message

String

메세지

data[].challengeId

Number

챌린지 ID

data[].title

String

챌린지 제목

data[].description

String

챌린지 설명

data[].startDate

String

챌린지 시작 일시

data[].endDate

String

챌린지 종료 일시

data[].stopDate

Null

챌린지 중단 일시

data[].totalParticipatingDaysCount

Number

챌린지 총 참여 일수

data[].numberOfParticipants

Number

챌린지 참여 인원

data[].participatingDays

Number

챌린지 참여 요일

data[].totalFee

Number

나의 벌금 합계

data[].isPaidAll

Boolean

최종 정산 여부

data[].hostProfileImage

String

챌린지 주최자 프로필 이미지

data[].isMemberGivenUp

Boolean

현재 사용자의 챌린지 포기 여부

data[].successCount

Number

챌린지 인증 성공 횟수

data[].isTodayParticipatingDay

Boolean

오늘 요일 == 챌린지 참여 요일

data[].isParticipatedToday

Boolean

오늘 챌린지 참여 여부

챌린지 상세 정보 조회

첼린지의 상세 정보를 조회합니다.

참여 요일에 해당하는 participatingDays 는 비트 연산을 사용합니다.

7자리로 이루어진 2진수 0000000 를 기준으로 좌측부터 일월화수목금토 입니다.

예를 들어, 화요일, 목요일 이 챌린지 참여 날짜라면, 2진수로 0010100 이고, 10진수로 변환하면 20 이 됩니다.

HTTP response 예시에 나온 participatingDays 의 값 4 는 10진수 4 를 의미하고 2진수로 0000011 이기 때문에 금요일토요일 이 챌린지 참여 요일이 됩니다.

HTTP request

GET /api/challenges/1 HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 677

{
  "message" : "",
  "data" : {
    "title" : "챌린지 제목",
    "description" : "챌린지 설명",
    "startDate" : "2024-11-14T11:26:16.097529208Z",
    "endDate" : "2024-11-19T11:26:16.097545368Z",
    "stopDate" : null,
    "numberOfParticipants" : 1,
    "participatingDays" : 4,
    "feePerAbsence" : 1000,
    "totalAbsenceFee" : 0,
    "isPaidAll" : false,
    "isTodayParticipatingDay" : true,
    "isParticipatedToday" : true,
    "hostNickname" : "챌린지 주최자 닉네임",
    "enrolledMembersProfileImageList" : [ "imageLink1", "imageLink2", "imageLink3" ],
    "isHost" : true,
    "isMemberEnrolledInChallenge" : true,
    "isGivenUp" : false
  }
}

Response fields

Path Type Description

message

String

메세지

data.title

String

챌린지 제목

data.description

String

챌린지 설명

data.startDate

String

챌린지 시작 일시

data.endDate

String

챌린지 종료 일시

data.stopDate

Null

챌린지 중단 일시

data.numberOfParticipants

Number

챌린지 참여 인

data.participatingDays

Number

챌린지 참여 요일

data.feePerAbsence

Number

미참여 1회당 벌금

data.totalAbsenceFee

Number

챌린지 전체 벌금

data.isPaidAll

Boolean

최종 정산 여부

data.hostNickname

String

챌린지 주최자 닉네임

data.enrolledMembersProfileImageList

Array

챌린지 참여자 프로필 이미지 (최대 3명)

data.isHost

Boolean

현재 접속한 사용자 == 챌린지 주최자

data.isMemberEnrolledInChallenge

Boolean

현재 접속한 사용자의 챌린지 참여 여부

data.isTodayParticipatingDay

Boolean

금일이 챌린지 참여일인지 여부

data.isParticipatedToday

Boolean

현재 접속한 사용자가 챌린지의 참가자일 경우, 금일 참여했는지 여부(참가자가 아니어도 false)

data.isGivenUp

Boolean

챌린지 중도 포기 여부

에러 응답

챌린지가 존재하지 않는 경우 (404 Not Found)

존재하지 않는 챌린지를 조회한 경우입니다.

HTTP request
GET /api/challenges/0 HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
HTTP response
HTTP/1.1 404 Not Found
Content-Type: application/json;charset=UTF-8
Content-Length: 93

{
  "code" : "CHALLENGE_NOT_FOUND",
  "message" : "챌린지가 존재하지 않습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 별 참여 기록 조회

챌린지 별 참여 기록을 조회합니다.

챌린지 별 달력의 참여 기록 표시를 위한 API입니다.

'참여 성공 날짜 목록', '참여 실패 날짜 목록', '앞으로 참여할 예정인 날짜 목록’의 세 가지 데이터가 제공됩니다.

ZoneId가 없는 날짜 데이터(시간 정보 없음)이기 때문에, 시간대 변환 없이 바로 사용할 수 있습니다.

'API 요청일’과 '참여일’이 동일한 경우, 이미 참여했다면 성공 목록에, 아직 참여하지 않았다면 예정 목록에 담기게 됩니다.

HTTP request

GET /api/challenges/1/records HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 148

{
  "message" : "",
  "data" : {
    "successDayList" : [ "2024-11-14" ],
    "failureDayList" : [ ],
    "upcomingDayList" : [ "2024-11-21" ]
  }
}

Response fields

Path Type Description

message

String

메세지

data.successDayList

Array

특정 챌린지 참여에 성공한 날짜 리스트

data.failureDayList

Array

특정 챌린지 참여에 실패한 날짜 리스트

data.upcomingDayList

Array

특정 챌린지 참여가 예정되어 있는 날짜 리스트

챌린지 생성

새로운 첼린지를 생성합니다.

HTTP request

POST /api/challenges HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 223
Host: localhost:8080

{
  "title" : "챌린지 제목",
  "description" : "챌린지 설명",
  "startDate" : "2024-11-14T12:26:16.185659508Z",
  "endDate" : "2024-11-19T11:26:16.185690356Z",
  "participatingDays" : 4,
  "feePerAbsence" : 1000
}

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 376

{
  "message" : "정상적으로 챌린지가 생성되었습니다.",
  "data" : {
    "hostNickname" : "IamHost",
    "challengeId" : 1,
    "title" : "챌린지 제목",
    "description" : "챌린지 설명",
    "startDate" : "2024-11-14T12:26:16.186165083Z",
    "endDate" : "2024-11-19T11:26:16.186188156Z",
    "participatingDays" : 4,
    "feePerAbsence" : 1000
  }
}

Response fields

Path Type Description

message

String

메세지

data.hostNickname

String

챌린지 주최자 닉네임

data.challengeId

Number

챌린지 ID

data.title

String

챌린지 제목

data.description

String

챌린지 설명

data.startDate

String

챌린지 시작 일시

data.endDate

String

챌린지 종료 일시

data.participatingDays

Number

챌린지 참여 요일

data.feePerAbsence

Number

1회당 미참여 벌금

에러 응답

챌린지 시작 시간이 현재 시간보다 이전인 경우 (400 Bad Request)

챌린지 시작 시간이 현재 시간보다 이전인 경우입니다.

HTTP request
POST /api/challenges HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 223
Host: localhost:8080

{
  "title" : "챌린지 제목",
  "description" : "챌린지 설명",
  "startDate" : "2024-11-13T11:26:15.862002116Z",
  "endDate" : "2024-11-19T11:26:15.862408375Z",
  "participatingDays" : 4,
  "feePerAbsence" : 1000
}
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 130

{
  "code" : "CHALLENGE_START_TIME_INVALID",
  "message" : "챌린지 시작 시간은 현재 시간 이후만 가능합니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 진행 기간에 참여 요일이 포함되지 않은 경우 (400 Bad Request)

챌린지 진행 기간에 선택한 참여 요일이 전혀 포함되지 않은 경우입니다.

예시

  • 진행 기간: 2024.10.07(월) ~ 2024.10.08(화)

  • 참여 요일: 수요일

HTTP request
POST /api/challenges HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 214
Host: localhost:8080

{
  "title" : "챌린지 제목",
  "description" : "챌린지 설명",
  "startDate" : "2024-10-07T00:00:00+09:00",
  "endDate" : "2024-10-08T00:00:00+09:00",
  "participatingDays" : 16,
  "feePerAbsence" : 1000
}
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 164

{
  "code" : "INVALID_CHALLENGE_PARTICIPATING_DAYS",
  "message" : "챌린지 진행 기간에 선택한 챌린지 참여 요일이 포함되지 않았습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 정보 수정

챌린지 정보를 수정합니다.

HTTP request

PATCH /api/challenges/1 HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 40
Host: localhost:8080

{
  "description" : "챌린지 설명"
}

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 335

{
  "message" : "정상적으로 챌린지 정보 수정이 반영되었습니다.",
  "data" : {
    "title" : "챌린지 제목",
    "description" : "챌린지 설명",
    "startDate" : "2024-11-14T12:26:16.239484981Z",
    "endDate" : "2024-11-19T11:26:16.239510679Z",
    "participatingDays" : 4,
    "feePerAbsence" : 1000
  }
}

Response fields

Path Type Description

message

String

메세지

data.title

String

챌린지 제목

data.description

String

챌린지 설명

data.startDate

String

챌린지 시작 일시

data.endDate

String

챌린지 종료 일시

data.participatingDays

Number

챌린지 참여 요일

data.feePerAbsence

Number

1회당 미참여 벌금

에러 응답

챌린지 설명이 이전과 동일한 경우 (400 Bad Request)

챌린지 설명이 이전과 동일한 경우입니다.

HTTP request
PATCH /api/challenges/1 HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 40
Host: localhost:8080

{
  "description" : "챌린지 설명"
}
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 97

{
  "code" : "DUPLICATED_CHALLENGE_DESCRIPTION",
  "message" : "변경 사항이 없습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 주최자가 아닌 경우 (403 Forbidden)

챌린지 주최자가 아닌 경우입니다.

HTTP request
PATCH /api/challenges/1 HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 40
Host: localhost:8080

{
  "description" : "챌린지 설명"
}
HTTP response
HTTP/1.1 403 Forbidden
Content-Type: application/json;charset=UTF-8
Content-Length: 101

{
  "code" : "ONLY_HOST_CAN_MODIFY",
  "message" : "챌린지 주최자만 수정 가능합니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 삭제

챌린지를 삭제합니다.

삭제는 챌린지 시작 시간 전까지만 가능하며, 챌린지 주최자만 삭제할 수 있습니다.

HTTP request

DELETE /api/challenges/1 HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 85

{
  "message" : "정상적으로 챌린지를 삭제했습니다.",
  "data" : null
}

Response fields

Path Type Description

message

String

메세지

data

Null

null

에러 응답

챌린지가 존재하지 않는 경우 (403 Forbidden)

챌린지 주최자가 아닌 경우 챌린지를 삭제하려는 경우입니다.

HTTP request
DELETE /api/challenges/1 HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
HTTP response
HTTP/1.1 403 Forbidden
Content-Type: application/json;charset=UTF-8
Content-Length: 125

{
  "code" : "NOT_ALLOWED_TO_DELETE_CHALLENGE",
  "message" : "챌린지 삭제는 챌린지 주최자만 가능합니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지가 존재하지 않는 경우 (404 Not Found)

존재하지 않는 챌린지를 삭제하려는 경우입니다.

HTTP request
DELETE /api/challenges/0 HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
HTTP response
HTTP/1.1 404 Not Found
Content-Type: application/json;charset=UTF-8
Content-Length: 93

{
  "code" : "CHALLENGE_NOT_FOUND",
  "message" : "챌린지가 존재하지 않습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 등록 관련 API

챌린지 등록 관련 API 목록입니다.

챌린지 참여자 등록

사용자를 챌린지 참여자에 등록합니다.

HTTP request

POST /api/challenges/1/enroll HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 183

{
  "message" : "정상적으로 챌린지에 등록했습니다.",
  "data" : {
    "challengeId" : 1,
    "memberId" : 1,
    "enrolledDate" : "2024-11-14T11:26:17.367007895Z"
  }
}

Response fields

Path Type Description

message

String

메세지

data.challengeId

Number

챌린지 ID

data.memberId

Number

사용자 ID

data.enrolledDate

String

챌린지 등록 일시

에러 응답

챌린지 등록 시간이 지난 경우 (400 Bad Request)

챌린지 시작 시간이 지나고 나서 등록하는 경우입니다.

HTTP request
POST /api/challenges/1/enroll HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 117

{
  "code" : "INVALID_CHALLENGE_REGISTRATION_TIME",
  "message" : "챌린지 등록 가능 시간이 아닙니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

이미 참여한 챌린지인 경우 (409 Conflict)

이미 챌린지에 등록한 사용자가 다시 챌린지 등록하는 신청한 경우입니다.

HTTP request
POST /api/challenges/1/enroll HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
HTTP response
HTTP/1.1 409 Conflict
Content-Type: application/json;charset=UTF-8
Content-Length: 101

{
  "code" : "ALREADY_ENROLLED_IN_CHALLENGE",
  "message" : "이미 참여한 챌린지 입니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 등록 취소

등록한 챌린지를 취소합니다.

취소는 챌린지 시작 시간 이전까지만 가능합니다.

HTTP request

POST /api/challenges/1/cancel HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 92

{
  "message" : "정상적으로 챌린지 등록을 취소했습니다.",
  "data" : null
}

Response fields

Path Type Description

message

String

메세지

data

Null

null

에러 응답

챌린지 주최자가 등록 취소 하는 경우 (400 Bad Request)

챌린지 주최자는 등록을 취소할 수 없습니다.

HTTP request
POST /api/challenges/1/cancel HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 135

{
  "code" : "NOT_ALLOWED_TO_CANCEL_ENROLLMENT_OF_HOST",
  "message" : "챌린지 주최자는 참여 취소가 불가능 합니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지에 참여하지 않은 경우 (400 Bad Request)

참여하지 않은 챌린지를 취소하는 경우입니다.

HTTP request
POST /api/challenges/1/cancel HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 100

{
  "code" : "NOT_ENROLLED_IN_CHALLENGE",
  "message" : "참여하지 않은 챌린지 입니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 등록 취소 시간을 지난 경우 (400 Bad Request)

챌린지가 시작하고 나서 등록 취소를 하는 경우입니다.

HTTP request
POST /api/challenges/1/cancel HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 123

{
  "code" : "INVALID_CHALLENGE_CANCELLATION_TIME",
  "message" : "챌린지 취소 가능한 시간이 지났습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 중도 포기

시작한 챌린지를 중도 포기합니다.

중도 포기 이후에는 해당 챌린지에 게시물 생성, 수정, 삭제가 불가능 합니다.

HTTP request

POST /api/challenges/1/give-up HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 103

{
  "message" : "정상적으로 챌린지 중도 포기 처리가 되었습니다.",
  "data" : null
}

Response fields

Path Type Description

message

String

메세지

data

Null

null

에러 응답

챌린지 시작 시간 이전 (400 Bad Request)

이미 중도 포기한 경우 다시 중도 포기 요청을 보낼 수 없습니다.

HTTP request
POST /api/challenges/1/give-up HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 136

{
  "code" : "TOO_EARLY_GIVEN_UP_CHALLENGE",
  "message" : "챌린지 중도 포기는 챌린지 시작 이후에만 가능합니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

이미 중도 포기한 경우 (400 Bad Request)

이미 중도 포기한 경우 다시 중도 포기 요청을 보낼 수 없습니다.

HTTP request
POST /api/challenges/1/give-up HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
HTTP response
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 105

{
  "code" : "ALREADY_GIVEN_UP_CHALLENGE",
  "message" : "이미 중도 포기한 챌린지 입니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 포스트 API

챌린지 포스트를 등록, 조회, 수정 및 삭제할 수 있습니다.

Request Header 에는 Authorization: Bearer {access token} 와 같은 형식으로 보내야 하며, 사용자의 액세스 토큰이 필요합니다.

챌린지 포스트 조회

경로 변수로 받은 post의 id값을 이용해 특정 챌린지 포스트를 조회합니다.

HTTP request

GET /api/posts/1 HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 560

{
  "message" : "",
  "data" : {
    "id" : 1,
    "challengeId" : 1,
    "content" : "This is test post.",
    "writer" : "test user",
    "isPostAuthor" : true,
    "profileUrl" : "https://picsum.photos/id/40/200/300",
    "isAnnouncement" : false,
    "createdAt" : "2024-11-14T11:26:14.460072005",
    "photoViewList" : [ {
      "postPhotoId" : 1,
      "viewOrder" : 1,
      "imageUrl" : "https://picsum.photos/id/40/200/300"
    }, {
      "postPhotoId" : 2,
      "viewOrder" : 2,
      "imageUrl" : "https://picsum.photos/id/40/200/300"
    } ]
  }
}

Response fields

Path Type Description

message

String

응답 메시지

data

Object

응답 데이터

data.id

Number

포스트 id

data.challengeId

Number

포스트가 소속된 challenge id

data.content

String

포스트 내용

data.writer

String

작성자

data.isPostAuthor

Boolean

요청한 멤버가 작성자 본인인지 여부

data.profileUrl

String

작성자 프로필 이미지 URL

data.isAnnouncement

Boolean

공지글 여부

data.createdAt

String

생성 일시

data.photoViewList

Array

포스트 포토(URL 포함) 데이터를 담은 객체 배열

data.photoViewList[].postPhotoId

Number

포토 id

data.photoViewList[].viewOrder

Number

포스트 내 포토의 순서

data.photoViewList[].imageUrl

String

포스트 포토 url

챌린지 내 전체 포스트 조회

경로 변수로 받은 challenge의 id값을 이용해 해당 챌린지 내의 모든 포스트를 조회합니다. 해당 챌린지에 등록되어 있는 멤버만 포스트를 조회할 수 있습니다.

(포스트 목록을 반환하는 뒤이은 모든 메서드가 그렇습니다만,) 복수의 포스트를 반환하는 API는 페이지네이션 메타 데이터와 함께 반환됩니다.

data의 content에는 포스트 뷰 객체가 배열로 담깁니다.

data의 pageable 객체에는 페이지네이션 정보가 담겨있습니다. 그러나 pageable 객체에 접근하지 않아도 바로 사용할 수 있는 메타 데이터가 추가로 존재합니다.

data에서 content 배열, pageable 객체를 지나면 바로 페이징 메타 데이터가 존재합니다. 특히 현재 페이지가 마지막 페이지인지 boolean으로 알려주는 "last" 속성이 있으므로, 이를 활용할 수 있습니다.

HTTP request

GET /api/challenges/1/posts HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 1370

{
  "message" : "",
  "data" : {
    "content" : [ {
      "id" : 1,
      "challengeId" : 1,
      "content" : "This is test post 1.",
      "writer" : "test user",
      "isPostAuthor" : true,
      "profileUrl" : "",
      "isAnnouncement" : false,
      "createdAt" : "2024-11-14T11:26:14.82970346",
      "photoViewList" : [ {
        "postPhotoId" : 1,
        "viewOrder" : 1,
        "imageUrl" : "https://picsum.photos/id/40/200/300"
      } ]
    }, {
      "id" : 2,
      "challengeId" : 2,
      "content" : "This is test post 2.",
      "writer" : "test user2",
      "isPostAuthor" : false,
      "profileUrl" : "https://picsum.photos/id/40/200/300",
      "isAnnouncement" : false,
      "createdAt" : "2024-11-14T11:26:14.829726924",
      "photoViewList" : [ {
        "postPhotoId" : 2,
        "viewOrder" : 2,
        "imageUrl" : "https://picsum.photos/id/40/200/300"
      } ]
    } ],
    "pageable" : {
      "pageNumber" : 0,
      "pageSize" : 10,
      "sort" : {
        "empty" : true,
        "sorted" : false,
        "unsorted" : true
      },
      "offset" : 0,
      "paged" : true,
      "unpaged" : false
    },
    "size" : 10,
    "number" : 0,
    "sort" : {
      "empty" : true,
      "sorted" : false,
      "unsorted" : true
    },
    "numberOfElements" : 2,
    "first" : true,
    "last" : false,
    "empty" : false
  }
}

Response fields

Path Type Description

message

String

응답 메시지

data

Object

응답 데이터

data.content

Array

포스트 뷰 목록

data.content[].id

Number

포스트 id

data.content[].challengeId

Number

포스트가 소속된 challenge id

data.content[].content

String

포스트 내용

data.content[].writer

String

작성자

data.content[].isPostAuthor

Boolean

요청한 멤버가 작성자 본인인지 여부

data.content[].profileUrl

String

작성자 프로필 이미지 URL

data.content[].isAnnouncement

Boolean

공지글 여부

data.content[].createdAt

String

생성 일시

data.content[].photoViewList

Array

포스트 포토(URL 포함) 데이터를 담은 객체 배열

data.content[].photoViewList[].postPhotoId

Number

포토 id

data.content[].photoViewList[].viewOrder

Number

포스트 내 포토의 순서

data.content[].photoViewList[].imageUrl

String

포스트 포토 url

data.pageable

Object

포스트 뷰 페이지네이션 정보를 담은 Pageable 객체

data.pageable.pageNumber

Number

현재 페이지 번호

data.pageable.pageSize

Number

한 페이지에 포함되는 항목의 수

data.pageable.sort

Object

정렬 정보

data.pageable.sort.empty

Boolean

정렬 조건이 없는지 여부

data.pageable.sort.unsorted

Boolean

정렬되지 않았는지 여부

data.pageable.sort.sorted

Boolean

정렬되었는지 여부

data.pageable.offset

Number

페이징된 항목의 시작 위치

data.pageable.paged

Boolean

페이징된 요청인지 여부

data.pageable.unpaged

Boolean

페이징되지 않은 요청인지 여부

data.size

Number

한 페이지에 포함되는 항목의 수

data.number

Number

현재 페이지 번호

data.sort

Object

정렬 정보

data.sort.empty

Boolean

정렬 조건이 없는지 여부

data.sort.unsorted

Boolean

정렬되지 않았는지 여부

data.sort.sorted

Boolean

정렬되었는지 여부

data.numberOfElements

Number

현재 페이지에 있는 요소의 수

data.first

Boolean

이 페이지가 첫 번째 페이지인지 여부

data.last

Boolean

이 페이지가 마지막 페이지인지 여부

data.empty

Boolean

페이지가 비어있는지 여부

챌린지 내 전체 포스트 중 공지 포스트 조회

경로 변수로 받은 challenge의 id값을 이용해 해당 챌린지 내의 모든 공지 포스트를 조회합니다. 해당 챌린지에 등록되어 있는 멤버만 조회할 수 있습니다.

HTTP request

GET /api/challenges/1/posts/announcements HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 1395

{
  "message" : "",
  "data" : {
    "content" : [ {
      "id" : 1,
      "challengeId" : 1,
      "content" : "This is announcement test post 1.",
      "writer" : "test user",
      "isPostAuthor" : false,
      "profileUrl" : "",
      "isAnnouncement" : true,
      "createdAt" : "2024-11-14T11:26:14.600259377",
      "photoViewList" : [ {
        "postPhotoId" : 1,
        "viewOrder" : 1,
        "imageUrl" : "https://picsum.photos/id/40/200/300"
      } ]
    }, {
      "id" : 2,
      "challengeId" : 2,
      "content" : "This is announcement test post 2.",
      "writer" : "test user",
      "isPostAuthor" : false,
      "profileUrl" : "https://picsum.photos/id/40/200/300",
      "isAnnouncement" : true,
      "createdAt" : "2024-11-14T11:26:14.600302006",
      "photoViewList" : [ {
        "postPhotoId" : 2,
        "viewOrder" : 2,
        "imageUrl" : "https://picsum.photos/id/40/200/300"
      } ]
    } ],
    "pageable" : {
      "pageNumber" : 0,
      "pageSize" : 10,
      "sort" : {
        "empty" : true,
        "sorted" : false,
        "unsorted" : true
      },
      "offset" : 0,
      "paged" : true,
      "unpaged" : false
    },
    "size" : 10,
    "number" : 0,
    "sort" : {
      "empty" : true,
      "sorted" : false,
      "unsorted" : true
    },
    "numberOfElements" : 2,
    "first" : true,
    "last" : false,
    "empty" : false
  }
}

Response fields

Path Type Description

message

String

응답 메시지

data

Object

응답 데이터

data.content

Array

포스트 뷰 목록

data.content[].id

Number

포스트 id

data.content[].challengeId

Number

포스트가 소속된 challenge id

data.content[].content

String

포스트 내용

data.content[].writer

String

작성자

data.content[].isPostAuthor

Boolean

요청한 멤버가 작성자 본인인지 여부

data.content[].profileUrl

String

작성자 프로필 이미지 URL

data.content[].isAnnouncement

Boolean

공지글 여부

data.content[].createdAt

String

생성 일시

data.content[].photoViewList

Array

포스트 포토(URL 포함) 데이터를 담은 객체 배열

data.content[].photoViewList[].postPhotoId

Number

포토 id

data.content[].photoViewList[].viewOrder

Number

포스트 내 포토의 순서

data.content[].photoViewList[].imageUrl

String

포스트 포토 url

data.pageable

Object

포스트 뷰 페이지네이션 정보를 담은 Pageable 객체

data.pageable.pageNumber

Number

현재 페이지 번호

data.pageable.pageSize

Number

한 페이지에 포함되는 항목의 수

data.pageable.sort

Object

정렬 정보

data.pageable.sort.empty

Boolean

정렬 조건이 없는지 여부

data.pageable.sort.unsorted

Boolean

정렬되지 않았는지 여부

data.pageable.sort.sorted

Boolean

정렬되었는지 여부

data.pageable.offset

Number

페이징된 항목의 시작 위치

data.pageable.paged

Boolean

페이징된 요청인지 여부

data.pageable.unpaged

Boolean

페이징되지 않은 요청인지 여부

data.size

Number

한 페이지에 포함되는 항목의 수

data.number

Number

현재 페이지 번호

data.sort

Object

정렬 정보

data.sort.empty

Boolean

정렬 조건이 없는지 여부

data.sort.unsorted

Boolean

정렬되지 않았는지 여부

data.sort.sorted

Boolean

정렬되었는지 여부

data.numberOfElements

Number

현재 페이지에 있는 요소의 수

data.first

Boolean

이 페이지가 첫 번째 페이지인지 여부

data.last

Boolean

이 페이지가 마지막 페이지인지 여부

data.empty

Boolean

페이지가 비어있는지 여부

챌린지 내 본인이 작성한 모든 포스트 조회

경로 변수로 받은 challenge의 id값을 이용해 해당 챌린지 내에서 본인이 작성한 모든 포스트를 조회합니다. '본인’이란 요청을 보낸 멤버를 의미합니다.

HTTP request

GET /api/challenges/1/posts/me HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 1381

{
  "message" : "",
  "data" : {
    "content" : [ {
      "id" : 1,
      "challengeId" : 1,
      "content" : "This is test post 1 by me.",
      "writer" : "test user",
      "isPostAuthor" : true,
      "profileUrl" : "",
      "isAnnouncement" : false,
      "createdAt" : "2024-11-14T11:26:14.745184309",
      "photoViewList" : [ {
        "postPhotoId" : 1,
        "viewOrder" : 1,
        "imageUrl" : "https://picsum.photos/id/40/200/300"
      } ]
    }, {
      "id" : 2,
      "challengeId" : 2,
      "content" : "This is test post 2 by me.",
      "writer" : "test user",
      "isPostAuthor" : true,
      "profileUrl" : "https://picsum.photos/id/40/200/300",
      "isAnnouncement" : false,
      "createdAt" : "2024-11-14T11:26:14.745208635",
      "photoViewList" : [ {
        "postPhotoId" : 2,
        "viewOrder" : 2,
        "imageUrl" : "https://picsum.photos/id/40/200/300"
      } ]
    } ],
    "pageable" : {
      "pageNumber" : 0,
      "pageSize" : 10,
      "sort" : {
        "empty" : true,
        "sorted" : false,
        "unsorted" : true
      },
      "offset" : 0,
      "paged" : true,
      "unpaged" : false
    },
    "size" : 10,
    "number" : 0,
    "sort" : {
      "empty" : true,
      "sorted" : false,
      "unsorted" : true
    },
    "numberOfElements" : 2,
    "first" : true,
    "last" : false,
    "empty" : false
  }
}

Response fields

Path Type Description

message

String

응답 메시지

data

Object

응답 데이터

data.content

Array

포스트 뷰 목록

data.content[].id

Number

포스트 id

data.content[].challengeId

Number

포스트가 소속된 challenge id

data.content[].content

String

포스트 내용

data.content[].writer

String

작성자

data.content[].isPostAuthor

Boolean

요청한 멤버가 작성자 본인인지 여부

data.content[].profileUrl

String

작성자 프로필 이미지 URL

data.content[].isAnnouncement

Boolean

공지글 여부

data.content[].createdAt

String

생성 일시

data.content[].photoViewList

Array

포스트 포토(URL 포함) 데이터를 담은 객체 배열

data.content[].photoViewList[].postPhotoId

Number

포토 id

data.content[].photoViewList[].viewOrder

Number

포스트 내 포토의 순서

data.content[].photoViewList[].imageUrl

String

포스트 포토 url

data.pageable

Object

포스트 뷰 페이지네이션 정보를 담은 Pageable 객체

data.pageable.pageNumber

Number

현재 페이지 번호

data.pageable.pageSize

Number

한 페이지에 포함되는 항목의 수

data.pageable.sort

Object

정렬 정보

data.pageable.sort.empty

Boolean

정렬 조건이 없는지 여부

data.pageable.sort.unsorted

Boolean

정렬되지 않았는지 여부

data.pageable.sort.sorted

Boolean

정렬되었는지 여부

data.pageable.offset

Number

페이징된 항목의 시작 위치

data.pageable.paged

Boolean

페이징된 요청인지 여부

data.pageable.unpaged

Boolean

페이징되지 않은 요청인지 여부

data.size

Number

한 페이지에 포함되는 항목의 수

data.number

Number

현재 페이지 번호

data.sort

Object

정렬 정보

data.sort.empty

Boolean

정렬 조건이 없는지 여부

data.sort.unsorted

Boolean

정렬되지 않았는지 여부

data.sort.sorted

Boolean

정렬되었는지 여부

data.numberOfElements

Number

현재 페이지에 있는 요소의 수

data.first

Boolean

이 페이지가 첫 번째 페이지인지 여부

data.last

Boolean

이 페이지가 마지막 페이지인지 여부

data.empty

Boolean

페이지가 비어있는지 여부

챌린지 포스트 생성

경로 변수로 받은 challenge의 id값에 해당하는 챌린지에 포스트를 작성합니다. 챌린지의 호스트라면 공지 포스트를 작성할 수 있습니다.

HTTP request

POST /api/challenges/1/posts HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 176
Host: localhost:8080

{
  "content" : "I want to create this post.",
  "isAnnouncement" : false,
  "photos" : [ {
    "viewOrder" : 1,
    "imageExtension" : "jpg",
    "contentLength" : 100
  } ]
}

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 124

{
  "message" : "정상적으로 포스트를 생성했습니다.",
  "data" : [ "https://please.upload/your-photo/here" ]
}

Request fields

Path Type Description

content

String

포스트 내용

isAnnouncement

Boolean

공지 포스트 여부

photos

Array

첨부한 이미지 파일 목록

photos[].viewOrder

Number

첨부한 이미지 파일의 포스트 내 정렬 순서

photos[].imageExtension

String

첨부한 이미지 파일의 확장자명

photos[].contentLength

Number

첨부한 이미지 파일의 길이

Response fields

Path Type Description

message

String

메시지

data

Array

AWS S3 업로드를 위한 preSignedUrl List<String>

에러 응답

중도 포기한 경우 (403 Forbidden)

챌린지를 중도 포기한 이후 게시물을 생성할 수 없습니다.

HTTP request
POST /api/challenges/1/posts HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 176
Host: localhost:8080

{
  "content" : "I want to create this post.",
  "isAnnouncement" : false,
  "photos" : [ {
    "viewOrder" : 1,
    "imageExtension" : "jpg",
    "contentLength" : 100
  } ]
}
HTTP response
HTTP/1.1 403 Forbidden
Content-Type: application/json;charset=UTF-8
Content-Length: 150

{
  "code" : "POST_CREATION_FORBIDDEN_DUE_TO_GIVE_UP",
  "message" : "챌린지 중도 포기 이후에는 게시물을 생성할 수 없습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 포스트 수정

경로 변수로 받은 post의 id값에 해당하는 포스트를 수정합니다. 수정은 포스트 내용 변경, 공지 포스트 여부 변경과 함께 포스트 내 이미지 파일의 추가, 삭제 및 정렬 순서 변경을 포함합니다.

HTTP request

PATCH /api/challenges/1/posts/1 HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 281
Host: localhost:8080

{
  "content" : "I want to patch this to post.",
  "isAnnouncement" : false,
  "newPhotos" : [ {
    "viewOrder" : 2,
    "imageExtension" : "jpg",
    "contentLength" : 100
  } ],
  "modifiedPhotos" : [ {
    "photoId" : 3,
    "viewOrder" : 1
  } ],
  "deletedPhotoIds" : [ 1 ]
}

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 124

{
  "message" : "정상적으로 포스트를 수정했습니다.",
  "data" : [ "https://please.upload/your-photo/here" ]
}

Request fields

Path Type Description

content

String

포스트 수정 내용

isAnnouncement

Boolean

공지 포스트 여부

newPhotos

Array

새로 첨부한 이미지 파일 목록

newPhotos[].viewOrder

Number

첨부한 이미지 파일의 포스트 내 정렬 순서

newPhotos[].imageExtension

String

첨부한 이미지 파일의 확장자명

newPhotos[].contentLength

Number

첨부한 이미지 파일의 길이

modifiedPhotos

Array

포스트 내 정렬 순서가 변경된 이미지 파일 목록

modifiedPhotos[].photoId

Number

정렬 순서를 변경하려는 이미지 파일의 PostPhotoId

modifiedPhotos[].viewOrder

Number

변경하려는 정렬 순서

deletedPhotoIds

Array

삭제할 이미지 파일 PostPhotoId List<Long>

Response fields

Path Type Description

message

String

메시지

data

Array

AWS S3 업로드를 위한 preSignedUrl List<String>

에러 응답

중도 포기한 경우 (403 Forbidden)

챌린지를 중도 포기한 이후 게시물을 수정할 수 없습니다.

HTTP request
PATCH /api/challenges/1/posts/1 HTTP/1.1
Content-Type: application/json;charset=UTF-8
Authorization: Bearer ACCESS_TOKEN
Content-Length: 176
Host: localhost:8080

{
  "content" : "I want to create this post.",
  "isAnnouncement" : false,
  "photos" : [ {
    "viewOrder" : 1,
    "imageExtension" : "jpg",
    "contentLength" : 100
  } ]
}
HTTP response
HTTP/1.1 403 Forbidden
Content-Type: application/json;charset=UTF-8
Content-Length: 154

{
  "code" : "POST_MODIFICATION_FORBIDDEN_DUE_TO_GIVE_UP",
  "message" : "챌린지 중도 포기 이후에는 게시물을 수정할 수 없습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

챌린지 포스트 삭제

경로 변수로 받은 post의 id값에 해당하는 포스트를 삭제합니다. 사실 일반 포스트는 삭제 기능을 제공하지 않기 때문에, 요청을 보내더라도 예외 처리됩니다. 공지 포스트만 챌린지 호스트가 삭제할 수 있습니다.

HTTP request

DELETE /api/challenges/1/posts/1 HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 85

{
  "message" : "정상적으로 포스트를 삭제했습니다.",
  "data" : null
}

Response fields

Path Type Description

message

String

메시지

data

Null

삭제된 포스트 id

에러 응답

중도 포기한 경우 (403 Forbidden)

챌린지를 중도 포기한 이후 게시물을 삭제할 수 없습니다.

HTTP request
DELETE /api/challenges/1/posts/1 HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080
HTTP response
HTTP/1.1 403 Forbidden
Content-Type: application/json;charset=UTF-8
Content-Length: 150

{
  "code" : "POST_DELETION_FORBIDDEN_DUE_TO_GIVE_UP",
  "message" : "챌린지 중도 포기 이후에는 게시물을 삭제할 수 없습니다."
}
Response fields
Path Type Description

code

String

오류 응답 코드

message

String

오류 메세지

리프레시 토큰 API

클라이언트가 토큰 리프레시를 요청하면, 새로운 액세스 토큰과 리프레스 토큰을 발급합니다.

보통 액세스 토큰 만료될 경우 요청이 발생하기 때문에, 다른 대부분의 요청과 달리 Request Header에 액세스 토큰을 담아 보낼 필요가 없습니다.

토큰 리프레시 요청

백엔드 URL에 요청을 보냈으나 401 Unauthorized 상태 코드를 받았다면, 액세스 토큰이 만료되는 등 더 이상 사용할 수 없다는 의미입니다.

그 경우 "/api/token" 경로를 통해 토큰 재발급을 요청해야 합니다. (401 이외의 상태 코드로는 토큰 재발급을 요청할 수 없습니다.)

리프레시 토큰은 이미 쿠키에 세팅되어 요청에 담기기 때문에, 추가로 보내야 할 값은 없습니다.

다만 표준 CORS 요청은 기본적으로 쿠키를 포함한 인증 정보를 설정할 수 없습니다. 그렇기 때문에 인증 정보(쿠키 등)를 요청에 추가하도록 수동으로 "withCredentials: true" 설정을 추가해야 합니다.

(이는 백엔드에서 쿠키를 포함한 응답을 보낼 때 "Access-Control-Allow-Credentials" 설정을 "true"로 하는 이유와 동일합니다.)

리프레시 토큰이 유효하고 적절하다면 새로운 액세스 토큰은 응답 본문에, 새로운 리프레시 토큰은 쿠키에 담아 재발급합니다.

HTTP request

POST /api/token HTTP/1.1
Host: localhost:8080
Cookie: refresh=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.DUMMY_SIGNATURE2
Content-Type: application/x-www-form-urlencoded

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 265

{
  "message" : "새로운 액세스 토큰 및 리프레시 토큰이 성공적으로 발급되었습니다.",
  "data" : {
    "accessToken" : "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.DUMMY_SIGNATURE1",
    "tokenType" : "Bearer",
    "expiresIn" : 1800000
  }
}

Response fields

Path Type Description

message

String

메시지

data.accessToken

String

새로 발급한 액세스 토큰

data.tokenType

String

발급한 토큰의 유형. "Bearer"

data.expiresIn

Number

액세스 토큰의 유효 기간

토큰 리프레시 실패 : 400 Bad Request

토큰 리프레시 요청 시 파라미터 값이 비어있거나 지원하는 않는 값 등 잘못된 요청일 경우 발생합니다.

HTTP request

POST /api/token HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

HTTP response

HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Content-Length: 57

{
  "code" : "BAD_REQUEST",
  "message" : "Bad Request"
}

Response fields

Path Type Description

code

String

에러 코드

message

String

에러 메시지

토큰 리프레시 실패 : 401 Unauthorized

토큰이 만료되거나 값이 변형되는 등 정상적으로 사용할 수 없는 토큰일 경우 발생합니다.

토큰 리프레시 요청 시 이 응답을 받았다면 리프레시 토큰이 만료되었을 가능성이 큽니다. 이 경우 재로그인이 필요합니다.

HTTP request

POST /api/token HTTP/1.1
Host: localhost:8080
Cookie: refresh=expiredGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.DUMMY_SIGNATURE2
Content-Type: application/x-www-form-urlencoded

HTTP response

HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8
Content-Length: 60

{
  "code" : "UNAUTHORIZED",
  "message" : "Invalid token"
}

Response fields

Path Type Description

code

String

에러 코드

message

String

에러 메시지

토큰 리프레시 실패 : 403 Forbidden

토큰이 제공하는 권한보다 더 높은 권한을 요구할 때 발생합니다. 예를 들면 일반 사용자가 관리자 페이지에 접근하는 경우를 들 수 있습니다.

해빗페이에서 권한 문제가 발생하는 대상은 액세스 토큰이기 때문에, 액세스 토큰의 권한이 부족할 때 발생하는 상태 코드로 이해하면 되겠습니다.

HTTP request

POST /api/token HTTP/1.1
Host: localhost:8080
Cookie: refresh=justGuestGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0In0.DUMMY_SIGNATURE2
Content-Type: application/x-www-form-urlencoded

HTTP response

HTTP/1.1 403 Forbidden
Content-Type: application/json;charset=UTF-8
Content-Length: 75

{
  "code" : "FORBIDDEN",
  "message" : "Not Allowed to Access or Modify"
}

Response fields

Path Type Description

code

String

에러 코드

message

String

에러 메시지

챌린지 벌금 API

챌린지 내 벌금 현황을 조회할 수 있습니다.

Request Header 에는 Authorization: Bearer {access token} 와 같은 형식으로 보내야 하며, 사용자의 액세스 토큰이 필요합니다.

챌린지 내 벌금 현황 조회

경로 변수로 받은 challenge id 값으로 챌린지를 특정합니다.

벌금 현황 페이지에서 바로 사용할 수 있도록, '챌린지 내 전체 누적 벌금 총합’과 ''나’의 누적 벌금 총합' 속성을 따로 제공합니다.

챌린지 내 전체 멤버를 대상으로 한 { 닉네임, 누적 벌금액, 달성률 } 목록이 있습니다. 이때 전체 멤버에는 '나’가 포함됩니다.

정렬은 따로 되어있지 않습니다.

HTTP request

GET /api/challenges/1/fee HTTP/1.1
Authorization: Bearer ACCESS_TOKEN
Host: localhost:8080

HTTP response

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 333

{
  "message" : "",
  "data" : {
    "totalFee" : 1500,
    "myFee" : 500,
    "memberFeeList" : [ {
      "nickname" : "testUser",
      "totalFee" : 1000,
      "completionRate" : 10,
      "isMe" : true
    }, {
      "nickname" : "selfUser",
      "totalFee" : 500,
      "completionRate" : 20,
      "isMe" : false
    } ]
  }
}

Response fields

Path Type Description

message

String

메시지

data.totalFee

Number

챌린지 내 전체 누적 벌금 총합

data.myFee

Number

챌린지 내 나의 누적 벌금 총합

data.memberFeeList

Array

챌린지 내 멤버별 벌금 현황 목록

data.memberFeeList[].nickname

String

멤버 닉네임

data.memberFeeList[].totalFee

Number

챌린지 내 멤버의 누적 벌금 총합

data.memberFeeList[].completionRate

Number

챌린지 내 멤버의 달성률

data.memberFeeList[].isMe

Boolean

나의 벌금 현황 여부