1. Auth API
1.1. 회원 가입
POST /api/auth/signup HTTP/1.1
Content-Type: application/json
Content-Length: 96
Host: localhost:8080
{
"email" : "user@example.com",
"handle" : "twooter_123",
"password" : "StrongP@ssw0rd!"
}
1.1.1. Request Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
사용자 이메일 |
|
|
|
사용자 비밀번호 (영문 대/소문자, 숫자, 특수문자 포함 8자 이상) |
|
|
|
사용자 핸들 (영문, 숫자, 밑줄(_) 사용 가능, 4~50자) |
1.1.2. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 179
{
"member" : {
"id" : 123,
"handle" : "twooter_123",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "testpath",
"email" : "user@example.com"
}
}
1.1.3. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
회원 정보 |
|
|
|
회원 고유 ID |
|
|
|
회원 이메일 |
|
|
|
회원 핸들 |
|
|
|
회원 닉네임 |
|
|
|
회원 프로필 이미지 경로 |
1.2. 로그인
1.2.1. HTTP Request
POST /api/auth/signin HTTP/1.1
Content-Type: application/json
Content-Length: 64
Host: localhost:8080
{
"handle" : "twooter_123",
"password" : "StrongP@ssw0rd!"
}
1.2.2. Request Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
사용자 핸들 (영문, 숫자, 밑줄(_) 사용 가능, 4~50자) |
|
|
|
사용자 비밀번호 |
1.2.3. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 238
{
"accessToken" : "<ACCESS_TOKEN>",
"refreshToken" : "<REFRESH_TOKEN>",
"member" : {
"id" : 123,
"handle" : "twooter_123",
"nickname" : "twooter_123",
"avatarPath" : "testpath",
"email" : "user@example.com"
}
}
1.2.4. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
액세스 토큰 (JWT) |
|
|
|
리프레시 토큰 (JWT) |
|
|
|
회원 정보 |
|
|
|
회원 고유 ID |
|
|
|
회원 이메일 |
|
|
|
회원 핸들 |
|
|
|
회원 닉네임 |
|
|
|
회원 프로필 이미지 경로 |
1.3. 토큰 재발급
1.3.1. HTTP Request
POST /api/auth/reissue HTTP/1.1
Content-Type: application/json
Content-Length: 40
Host: localhost:8080
{
"refreshToken" : "<REFRESH_TOKEN>"
}
1.3.2. Request Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
유효한 리프레시 토큰 (JWT) |
1.3.3. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 84
{
"accessToken" : "<NEW_ACCESS_TOKEN>",
"refreshToken" : "<NEW_REFRESH_TOKEN>"
}
1.3.4. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
새로 발급된 액세스 토큰 (JWT) |
|
|
|
새로 발급된 리프레시 토큰 (JWT) |
1.4. 로그아웃
1.4.1. HTTP Request
POST /api/auth/logout HTTP/1.1
Content-Type: application/json
Authorization: Bearer <ACCESS_TOKEN>
Host: localhost:8080
1.4.2. Request Headers
Name | Description | Optional |
---|---|---|
|
Bearer 인증 토큰 |
1.4.3. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 34
{
"userHandle" : "user_handle"
}
1.4.4. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
로그아웃된 유저의 핸들 (토큰이 유효하지 않거나 처리 중 오류가 발생한 경우 'unknown' 반환) |
2. Member API
2.2. 팔로우
유저를 팔로우한다.
2.2.1. HTTP Request
POST /api/members/follow HTTP/1.1
Content-Type: application/json
Authorization: Bearer <ACCESS_TOKEN>
Content-Length: 26
Host: localhost:8080
{
"targetMemberId" : 1
}
2.2.2. Request Headers
Name | Description | Optional |
---|---|---|
|
인증 토큰 |
2.2.3. Request Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
팔로우할 대상 멤버의 ID |
2.2.4. HTTP Response
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 66
{
"targetMemberId" : 1,
"followedAt" : "2025-05-05T10:10:00"
}
2.2.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
팔로우한 대상 멤버의 ID |
|
|
|
팔로우한 시간 |
2.3. 언팔로우
유저의 팔로우를 취소한다.
2.3.1. HTTP Request
DELETE /api/members/follow/1 HTTP/1.1
Authorization: Bearer <ACCESS_TOKEN>
Host: localhost:8080
2.3.2. Request Headers
Name | Description | Optional |
---|---|---|
|
인증 토큰 |
2.3.3. Path Parameters
Parameter | Description | Optional |
---|---|---|
|
언팔로우할 대상 멤버의 ID |
2.3.4. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 75
{
"targetMemberId" : 1,
"unfollowedAt" : "2025-06-13T13:36:18.687462"
}
2.3.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
언팔로우한 대상 멤버의 ID |
|
|
|
언팔로우한 시간 |
2.4. 팔로워 목록
해당 유저를 팔로우 하고 있는 유저(팔로워) 목록을 조회한다.
로그인 했을 경우, 그 유저와의 관계도 함께 조회된다.
2.4.1. HTTP Request
GET /api/members/1/followers?cursor=dXNlcjpVMDYxTkZUVDI%3D&limit=10 HTTP/1.1
Content-Type: application/json
Host: localhost:8080
2.4.2. Path Parameters
Parameter | Description | Optional |
---|---|---|
|
팔로워 목록을 조회할 멤버의 ID |
2.4.3. Query Parameters
Parameter | Description | Optional |
---|---|---|
|
이전 요청의 response 메타 데이터가 반환한 next_cursor 의 속성 (아무 값이 없을 경우 컬렉션이 첫 번째 페이지를 가져옴) |
true |
|
조회할 최대 팔로워 수 (기본값: 20, 최소값: 1) |
true |
2.4.4. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 779
{
"members" : [ {
"id" : 1,
"handle" : "cameraman",
"nickname" : "카메라맨",
"avatarPath" : "test_avatar_path.jpg",
"bio" : "카메라맨의 바이오",
"followsMe" : false,
"followingByMe" : false
}, {
"id" : 2,
"handle" : "movie_journalist",
"nickname" : "영화 상영 기자",
"avatarPath" : "test_avatar_path.jpg",
"bio" : "영화 상영 기자의 바이오",
"followsMe" : false,
"followingByMe" : false
}, {
"id" : 3,
"handle" : "movie_critic",
"nickname" : "영화 평론가",
"avatarPath" : "test_avatar_path.jpg",
"bio" : "영화 평론가의 바이오",
"followsMe" : false,
"followingByMe" : true
} ],
"metadata" : {
"nextCursor" : null,
"hasNext" : false
}
}
2.4.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
팔로워 목록 |
|
|
|
페이지네이션 메타데이터 |
|
|
|
팔로워의 ID |
|
|
|
팔로워의 핸들 |
|
|
|
팔로워의 닉네임 |
|
|
|
팔로워의 아바타 이미지 경로 |
|
|
|
팔로워의 바이오 |
|
|
|
내가 이 팔로워를 팔로우하고 있는지 여부 |
|
|
|
이 팔로워가 나를 팔로우하고 있는지 여부 |
|
|
|
true |
다음 페이지를 가져오는 데 사용될 커서 |
|
|
다음 페이지가 존재하는지 여부 |
2.5. 팔로잉 목록
해당 유저가 팔로우하고 있는 유저(팔로잉) 목록을 조회한다. 로그인 했을 경우, 그 유저와의 관계도 함께 조회된다.
2.5.1. HTTP Request
GET /api/members/1/followings?cursor=dXNlcjpVMDYxTkZUVDI%3D&limit=10 HTTP/1.1
Content-Type: application/json
Host: localhost:8080
2.5.2. Path Parameters
Parameter | Description | Optional |
---|---|---|
|
팔로잉 목록을 조회할 멤버의 ID |
2.5.3. Query Parameters
Parameter | Description | Optional |
---|---|---|
|
이전 요청의 response 메타 데이터가 반환한 next_cursor 의 속성 (아무 값이 없을 경우 컬렉션이 첫 번째 페이지를 가져옴) |
true |
|
조회할 최대 팔로잉 수 (기본값: 20, 최소값: 1) |
true |
2.5.4. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 779
{
"members" : [ {
"id" : 1,
"handle" : "cameraman",
"nickname" : "카메라맨",
"avatarPath" : "test_avatar_path.jpg",
"bio" : "카메라맨의 바이오",
"followsMe" : false,
"followingByMe" : false
}, {
"id" : 2,
"handle" : "movie_journalist",
"nickname" : "영화 상영 기자",
"avatarPath" : "test_avatar_path.jpg",
"bio" : "영화 상영 기자의 바이오",
"followsMe" : false,
"followingByMe" : false
}, {
"id" : 3,
"handle" : "movie_critic",
"nickname" : "영화 평론가",
"avatarPath" : "test_avatar_path.jpg",
"bio" : "영화 평론가의 바이오",
"followsMe" : false,
"followingByMe" : true
} ],
"metadata" : {
"nextCursor" : null,
"hasNext" : false
}
}
2.5.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
팔로잉 목록 |
|
|
|
페이지네이션 메타데이터 |
|
|
|
팔로워의 ID |
|
|
|
팔로워의 핸들 |
|
|
|
팔로워의 닉네임 |
|
|
|
팔로워의 아바타 이미지 경로 |
|
|
|
팔로워의 바이오 |
|
|
|
내가 이 팔로워를 팔로우하고 있는지 여부 |
|
|
|
이 팔로워가 나를 팔로우하고 있는지 여부 |
|
|
|
true |
다음 페이지를 가져오는 데 사용될 커서 |
|
|
다음 페이지가 존재하는지 여부 |
3. Timeline API
3.1. 홈 타임라인 조회
현재 로그인한 유저의 홈 타임라인을 조회한다.
타임라인에는 자신의 포스트와 리포스트, 팔로우하는 유저의 포스트와 리포스트가 포함된다.
3.1.1. HTTP Request
GET /api/timeline/home?cursor=dXNlcjpVMDYxTkZUVDI%3D&limit=10 HTTP/1.1
Content-Type: application/json
Authorization: Bearer <ACCESS_TOKEN>
Host: localhost:8080
3.1.2. Request Headers
Name | Description | Optional |
---|---|---|
|
액세스 토큰 (Bearer 타입) |
3.1.3. Query Parameters
Parameter | Description | Optional |
---|---|---|
|
이전 요청의 response 메타 데이터가 반환한 next_cursor 의 속성 (아무 값이 없을 경우 컬렉션이 첫 번째 페이지를 가져옴) |
true |
|
페이지당 반환될 아이템 수 (기본값: 20) |
true |
3.1.4. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1884
{
"timeline" : [ {
"type" : "post",
"createdAt" : "2025-05-05T00:00:00",
"post" : {
"id" : 1,
"author" : {
"id" : 1,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar1"
},
"content" : "새 책상을 정리하다가 유용해 보이는 오래된 자료를 발견해서 이메일로 보냅니다.",
"likeCount" : 15,
"repostCount" : 3,
"mediaEntities" : [ {
"mediaId" : 101,
"mediaUrl" : "https://cdn.twooter.xyz/media/101.jpg"
}, {
"mediaId" : 102,
"mediaUrl" : "https://cdn.twooter.xyz/media/102.jpg"
} ],
"createdAt" : "2025-05-05T00:00:00",
"liked" : true,
"reposted" : true,
"deleted" : false
}
}, {
"type" : "repost",
"createdAt" : "2025-05-05T05:05:00",
"post" : {
"id" : 2,
"author" : {
"id" : 2,
"handle" : "table_specialist",
"nickname" : "친절한 책상 정리 전문가",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar2"
},
"content" : "비고: 누가 남긴 자료인지 모르겠네요.",
"likeCount" : 15,
"repostCount" : 3,
"mediaEntities" : [ {
"mediaId" : 103,
"mediaUrl" : "https://cdn.twooter.xyz/media/101.jpg"
}, {
"mediaId" : 104,
"mediaUrl" : "https://cdn.twooter.xyz/media/102.jpg"
} ],
"createdAt" : "2025-05-05T10:10:00",
"liked" : true,
"reposted" : false,
"deleted" : false
},
"repostBy" : {
"id" : 1,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar1"
}
} ],
"metadata" : {
"nextCursor" : "dGVhbTpDMDYxRkE1UEI=",
"hasNext" : true
}
}
3.1.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
타임라인 아이템 목록 |
|
|
|
페이지네이션 메타데이터 |
|
|
|
타임라인 아이템 타입 (post 또는 repost) |
|
|
|
타임라인 아이템 생성 시간 (리포스트의 경우 리포스트한 시간) |
|
|
|
포스트 정보 (원본 포스트 또는 리포스트된 포스트) |
|
|
|
true |
리포스트한 사용자 정보 (type이 'repost’일 때만 존재) |
|
|
포스트 Id |
|
|
|
포스트 작성자 정보 |
|
|
|
포스트 내용 |
|
|
|
좋아요 수 |
|
|
|
리포스트 수 |
|
|
|
첨부된 미디어 정보 목록 |
|
|
|
포스트 생성 시간 |
|
|
|
현재 사용자의 좋아요 여부 |
|
|
|
현재 사용자의 리포스트 여부 |
|
|
|
삭제 여부 |
|
|
|
사용자 고유 ID |
|
|
|
사용자 핸들 |
|
|
|
사용자 닉네임 |
|
|
|
사용자 아바타 URL |
|
|
|
미디어 ID |
|
|
|
미디어 경로 URL |
|
|
|
사용자 고유 ID |
|
|
|
사용자 핸들 |
|
|
|
사용자 닉네임 |
|
|
|
사용자 아바타 URL |
|
|
|
true |
다음 페이지를 가져오는 데 사용될 커서 |
|
|
다음 페이지가 존재하는지 여부 |
3.2. 나의 타임라인 조회
현재 로그인한 유저의 타임라인을 조회한다.
타임라인은 자신의 포스트 + 리포스트로 구성된다.
3.2.1. HTTP Request
GET /api/timeline/me?cursor=dXNlcjpVMDYxTkZUVDI%3D&limit=10 HTTP/1.1
Content-Type: application/json
Authorization: Bearer <ACCESS_TOKEN>
Host: localhost:8080
3.2.2. Request Headers
Name | Description | Optional |
---|---|---|
|
액세스 토큰 (Bearer 타입) |
3.2.3. Query Parameters
Parameter | Description | Optional |
---|---|---|
|
이전 요청의 response 메타 데이터가 반환한 next_cursor 의 속성 (아무 값이 없을 경우 컬렉션이 첫 번째 페이지를 가져옴) |
true |
|
페이지당 반환될 아이템 수 (기본값: 20) |
true |
3.2.4. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1884
{
"timeline" : [ {
"type" : "post",
"createdAt" : "2025-05-05T00:00:00",
"post" : {
"id" : 1,
"author" : {
"id" : 1,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar1"
},
"content" : "새 책상을 정리하다가 유용해 보이는 오래된 자료를 발견해서 이메일로 보냅니다.",
"likeCount" : 15,
"repostCount" : 3,
"mediaEntities" : [ {
"mediaId" : 101,
"mediaUrl" : "https://cdn.twooter.xyz/media/101.jpg"
}, {
"mediaId" : 102,
"mediaUrl" : "https://cdn.twooter.xyz/media/102.jpg"
} ],
"createdAt" : "2025-05-05T00:00:00",
"liked" : true,
"reposted" : true,
"deleted" : false
}
}, {
"type" : "repost",
"createdAt" : "2025-05-05T05:05:00",
"post" : {
"id" : 2,
"author" : {
"id" : 2,
"handle" : "table_specialist",
"nickname" : "친절한 책상 정리 전문가",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar2"
},
"content" : "비고: 누가 남긴 자료인지 모르겠네요.",
"likeCount" : 15,
"repostCount" : 3,
"mediaEntities" : [ {
"mediaId" : 103,
"mediaUrl" : "https://cdn.twooter.xyz/media/101.jpg"
}, {
"mediaId" : 104,
"mediaUrl" : "https://cdn.twooter.xyz/media/102.jpg"
} ],
"createdAt" : "2025-05-05T10:10:00",
"liked" : true,
"reposted" : false,
"deleted" : false
},
"repostBy" : {
"id" : 1,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar1"
}
} ],
"metadata" : {
"nextCursor" : "dGVhbTpDMDYxRkE1UEI=",
"hasNext" : true
}
}
3.2.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
타임라인 아이템 목록 |
|
|
|
페이지네이션 메타데이터 |
|
|
|
타임라인 아이템 타입 (post 또는 repost) |
|
|
|
타임라인 아이템 생성 시간 (리포스트의 경우 리포스트한 시간) |
|
|
|
포스트 정보 (원본 포스트 또는 리포스트된 포스트) |
|
|
|
true |
리포스트한 사용자 정보 (type이 'repost’일 때만 존재) |
|
|
포스트 Id |
|
|
|
포스트 작성자 정보 |
|
|
|
포스트 내용 |
|
|
|
좋아요 수 |
|
|
|
리포스트 수 |
|
|
|
첨부된 미디어 정보 목록 |
|
|
|
포스트 생성 시간 |
|
|
|
현재 사용자의 좋아요 여부 |
|
|
|
현재 사용자의 리포스트 여부 |
|
|
|
삭제 여부 |
|
|
|
사용자 고유 ID |
|
|
|
사용자 핸들 |
|
|
|
사용자 닉네임 |
|
|
|
사용자 아바타 URL |
|
|
|
미디어 ID |
|
|
|
미디어 경로 URL |
|
|
|
사용자 고유 ID |
|
|
|
사용자 핸들 |
|
|
|
사용자 닉네임 |
|
|
|
사용자 아바타 URL |
|
|
|
true |
다음 페이지를 가져오는 데 사용될 커서 |
|
|
다음 페이지가 존재하는지 여부 |
3.3. 특정 유저 ID의 타임라인 조회
특정 유저의 타임라인을 조회한다.
타임라인은 해당 유저의 포스트 + 해당 유저의 리포스트로 구성된다.
3.3.1. HTTP Request
GET /api/timeline/user/1?cursor=dXNlcjpVMDYxTkZUVDI%3D&limit=10 HTTP/1.1
Content-Type: application/json
Host: localhost:8080
3.3.2. Path Parameters
Parameter | Description | Optional |
---|---|---|
|
조회 대상 유저의 ID |
3.3.3. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1884
{
"timeline" : [ {
"type" : "post",
"createdAt" : "2025-05-05T00:00:00",
"post" : {
"id" : 1,
"author" : {
"id" : 1,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar1"
},
"content" : "새 책상을 정리하다가 유용해 보이는 오래된 자료를 발견해서 이메일로 보냅니다.",
"likeCount" : 15,
"repostCount" : 3,
"mediaEntities" : [ {
"mediaId" : 101,
"mediaUrl" : "https://cdn.twooter.xyz/media/101.jpg"
}, {
"mediaId" : 102,
"mediaUrl" : "https://cdn.twooter.xyz/media/102.jpg"
} ],
"createdAt" : "2025-05-05T00:00:00",
"liked" : true,
"reposted" : true,
"deleted" : false
}
}, {
"type" : "repost",
"createdAt" : "2025-05-05T05:05:00",
"post" : {
"id" : 2,
"author" : {
"id" : 2,
"handle" : "table_specialist",
"nickname" : "친절한 책상 정리 전문가",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar2"
},
"content" : "비고: 누가 남긴 자료인지 모르겠네요.",
"likeCount" : 15,
"repostCount" : 3,
"mediaEntities" : [ {
"mediaId" : 103,
"mediaUrl" : "https://cdn.twooter.xyz/media/101.jpg"
}, {
"mediaId" : 104,
"mediaUrl" : "https://cdn.twooter.xyz/media/102.jpg"
} ],
"createdAt" : "2025-05-05T10:10:00",
"liked" : true,
"reposted" : false,
"deleted" : false
},
"repostBy" : {
"id" : 1,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar1"
}
} ],
"metadata" : {
"nextCursor" : "dGVhbTpDMDYxRkE1UEI=",
"hasNext" : true
}
}
3.3.4. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
타임라인 아이템 목록 |
|
|
|
페이지네이션 메타데이터 |
|
|
|
타임라인 아이템 타입 (post 또는 repost) |
|
|
|
타임라인 아이템 생성 시간 (리포스트의 경우 리포스트한 시간) |
|
|
|
포스트 정보 (원본 포스트 또는 리포스트된 포스트) |
|
|
|
true |
리포스트한 사용자 정보 (type이 'repost’일 때만 존재) |
|
|
포스트 Id |
|
|
|
포스트 작성자 정보 |
|
|
|
포스트 내용 |
|
|
|
좋아요 수 |
|
|
|
리포스트 수 |
|
|
|
첨부된 미디어 정보 목록 |
|
|
|
포스트 생성 시간 |
|
|
|
현재 사용자의 좋아요 여부 |
|
|
|
현재 사용자의 리포스트 여부 |
|
|
|
삭제 여부 |
|
|
|
사용자 고유 ID |
|
|
|
사용자 핸들 |
|
|
|
사용자 닉네임 |
|
|
|
사용자 아바타 URL |
|
|
|
미디어 ID |
|
|
|
미디어 경로 URL |
|
|
|
사용자 고유 ID |
|
|
|
사용자 핸들 |
|
|
|
사용자 닉네임 |
|
|
|
사용자 아바타 URL |
|
|
|
true |
다음 페이지를 가져오는 데 사용될 커서 |
|
|
다음 페이지가 존재하는지 여부 |
4. Post API
4.1. 포스트 작성
4.1.1. HTTP Request
POST /api/posts HTTP/1.1
Content-Type: application/json
Authorization: Bearer <ACCESS_TOKEN>
Content-Length: 190
Host: localhost:8080
{
"content" : "트우터에 올릴 새로운 포스트 내용입니다. #첫글 #환영",
"media" : [ "https://cdn.twooter.xyz/media/101.jpg", "https://cdn.twooter.xyz/media/102.jpg" ]
}
4.1.2. Request Headers
Name | Description | Optional |
---|---|---|
|
액세스 토큰 (Bearer 타입) |
4.1.3. Request Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
포스트 내용 (미디어가 없는 경우 필수, 최대 500자) |
|
|
|
true |
첨부된 미디어 파일 URL (내용이 비어있는 경우 필수, 최대 4개), 파일 업로드 API를 이용해, 업로드 후 링크를 첨부 |
4.1.4. HTTP Response
HTTP/1.1 201 Created
Location: http://localhost:8080/api/posts
Content-Type: application/json
Content-Length: 533
{
"id" : 1,
"content" : "트우터에 올릴 새로운 포스트 내용입니다. #첫글 #환영",
"author" : {
"id" : 123,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar",
"email" : "test@test.com"
},
"media" : [ {
"mediaId" : 101,
"mediaUrl" : "https://cdn.twooter.xyz/media/101.jpg"
}, {
"mediaId" : 102,
"mediaUrl" : "https://cdn.twooter.xyz/media/102.jpg"
} ],
"createdAt" : "2025-05-05T00:00:00"
}
4.1.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
생성된 포스트 ID |
|
|
|
포스트 내용 |
|
|
|
포스트 작성자 정보 |
|
|
|
첨부된 미디어 정보 목록 |
|
|
|
포스트 생성 시간 |
|
|
|
작성자 고유 ID |
|
|
|
작성자 닉네임 |
|
|
|
작성자 아바타 URL |
|
|
|
작성자 핸들 |
|
|
|
작성자 이메일 |
|
|
|
미디어 ID |
|
|
|
미디어 접근 URL |
4.2. 포스트 단건 조회
포스트를 단건 조회한다.
4.2.1. HTTP Request
GET /api/posts/1 HTTP/1.1
Host: localhost:8080
4.2.2. Path Parameters
Parameter | Description | Optional |
---|---|---|
|
조회할 포스트 ID |
4.2.3. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 649
{
"id" : 1,
"author" : {
"id" : 123,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar"
},
"content" : "새 책상을 정리하다가 유용해 보이는 오래된 자료를 발견해서 이메일로 보냅니다.",
"likeCount" : 15,
"repostCount" : 3,
"mediaEntities" : [ {
"mediaId" : 101,
"mediaUrl" : "https://cdn.twooter.xyz/media/101.jpg"
}, {
"mediaId" : 102,
"mediaUrl" : "https://cdn.twooter.xyz/media/102.jpg"
} ],
"createdAt" : "2025-05-05T00:00:00",
"liked" : true,
"reposted" : false,
"deleted" : false
}
4.2.4. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
포스트 Id |
|
|
|
포스트 내용 |
|
|
|
포스트 작성자 정보 |
|
|
|
좋아요 수 |
|
|
|
현재 사용자의 좋아요 여부 |
|
|
|
리포스트 수 |
|
|
|
현재 사용자의 리포스트 여부 |
|
|
|
첨부된 미디어 정보 목록 |
|
|
|
포스트 생성 시간 |
|
|
|
삭제 여부 |
|
|
|
작성자 고유 ID |
|
|
|
작성자 닉네임 |
|
|
|
작성자 아바타 URL |
|
|
|
작성자 핸들 |
|
|
|
미디어 ID |
|
|
|
미디어 접근 URL |
4.3. 포스트 좋아요 / 좋아요 취소
포스트에 좋아요를 누르거나, 이미 좋아요가 눌러진 상태에서 다시 누르면 좋아요를 취소한다.
4.3.1. HTTP Request
PATCH /api/posts/1/like HTTP/1.1
Authorization: Bearer <ACCESS_TOKEN>
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
4.3.2. Request Headers
Name | Description | Optional |
---|---|---|
|
액세스 토큰 (Bearer 타입) |
4.3.3. Path Parameters
Parameter | Description | Optional |
---|---|---|
|
좋아요/좋아요 취소할 포스트 ID |
4.3.4. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 38
{
"postId" : 1,
"isLiked" : true
}
4.3.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
포스트 ID |
|
|
|
현재 사용자의 좋아요 여부 |
4.4. 리포스트
포스트를 리포스트한다.
4.4.1. HTTP Request
POST /api/posts/1/repost HTTP/1.1
Authorization: Bearer <ACCESS_TOKEN>
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
4.4.2. Request Headers
Name | Description | Optional |
---|---|---|
|
액세스 토큰 (Bearer 타입) |
4.4.3. Path Parameters
Parameter | Description | Optional |
---|---|---|
|
리포스트할 대상 포스트 ID |
4.4.4. HTTP Response
HTTP/1.1 201 Created
Location: http://localhost:8080/api/posts/1/repost
Content-Type: application/json
Content-Length: 84
{
"repostId" : 1,
"originalPostId" : 2,
"repostedAt" : "2025-05-05T00:00:00"
}
4.4.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
생성된 리포스트 ID |
|
|
|
원본 포스트 ID |
|
|
|
리포스트 생성 시간 |
4.5. 답글 작성
포스트에 대한 답글을 작성한다.
4.5.1. HTTP Request
POST /api/posts/replies HTTP/1.1
Content-Type: application/json
Authorization: Bearer <ACCESS_TOKEN>
Content-Length: 118
Host: localhost:8080
{
"content" : "답글 내용입니다.",
"media" : [ "https://cdn.twooter.xyz/media/201.jpg" ],
"parentId" : 1
}
4.5.2. Request Headers
Name | Description | Optional |
---|---|---|
|
액세스 토큰 (Bearer 타입) |
4.5.3. Request Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
답글의 부모 포스트 ID (필수) |
|
|
|
포스트 내용 (미디어가 없는 경우 필수, 최대 500자) |
|
|
|
true |
첨부된 미디어 파일 URL (내용이 비어있는 경우 필수, 최대 4개), 파일 업로드 API를 이용해, 업로드 후 링크를 첨부 |
4.5.4. HTTP Response
HTTP/1.1 201 Created
Location: http://localhost:8080/api/posts/replies
Content-Type: application/json
Content-Length: 417
{
"id" : 2,
"content" : "답글 내용입니다.",
"author" : {
"id" : 123,
"handle" : "table_cleaner",
"nickname" : "테이블 청소 마스터",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar",
"email" : "test@test.com"
},
"media" : [ {
"mediaId" : 201,
"mediaUrl" : "https://cdn.twooter.xyz/media/201.jpg"
} ],
"createdAt" : "2025-05-05T00:00:00",
"parentId" : 1
}
4.5.5. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
생성된 포스트 ID |
|
|
|
포스트 내용 |
|
|
|
포스트 작성자 정보 |
|
|
|
첨부된 미디어 정보 목록 |
|
|
|
포스트 생성 시간 |
|
|
|
생성된 답글의 부모 ID |
|
|
|
작성자 고유 ID |
|
|
|
작성자 닉네임 |
|
|
|
작성자 아바타 URL |
|
|
|
작성자 핸들 |
|
|
|
작성자 이메일 |
|
|
|
미디어 ID |
|
|
|
미디어 접근 URL |
4.6. 답글 조회
포스트 ID에 대한 답글을 조회한다. 이 때 답글은 level 1로 조회되며, 오래된 순으로 정렬된다.
4.6.1. HTTP Request
GET /api/posts/1/replies HTTP/1.1
Host: localhost:8080
4.6.2. Path Parameters
Parameter | Description | Optional |
---|---|---|
|
답글을 조회할 부모 포스트 ID |
4.6.3. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1785
{
"posts" : [ {
"id" : 2,
"author" : {
"id" : 456,
"handle" : "reply_author_2",
"nickname" : "답글 작성자",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar_reply.jpg"
},
"content" : "이것은 포스트 1에 대한 답글입니다. 답글 ID: 2",
"likeCount" : 7,
"repostCount" : 2,
"mediaEntities" : [ {
"mediaId" : 202,
"mediaUrl" : "https://cdn.twooter.xyz/media/reply_2.jpg"
} ],
"createdAt" : "2025-05-05T00:02:00",
"liked" : true,
"reposted" : false,
"deleted" : false
}, {
"id" : 3,
"author" : {
"id" : 456,
"handle" : "reply_author_3",
"nickname" : "답글 작성자",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar_reply.jpg"
},
"content" : "이것은 포스트 1에 대한 답글입니다. 답글 ID: 3",
"likeCount" : 8,
"repostCount" : 2,
"mediaEntities" : [ {
"mediaId" : 203,
"mediaUrl" : "https://cdn.twooter.xyz/media/reply_3.jpg"
} ],
"createdAt" : "2025-05-05T00:03:00",
"liked" : false,
"reposted" : false,
"deleted" : false
}, {
"id" : 4,
"author" : {
"id" : 456,
"handle" : "reply_author_4",
"nickname" : "답글 작성자",
"avatarPath" : "https://cdn.twooter.xyz/media/avatar_reply.jpg"
},
"content" : "이것은 포스트 1에 대한 답글입니다. 답글 ID: 4",
"likeCount" : 9,
"repostCount" : 2,
"mediaEntities" : [ {
"mediaId" : 204,
"mediaUrl" : "https://cdn.twooter.xyz/media/reply_4.jpg"
} ],
"createdAt" : "2025-05-05T00:04:00",
"liked" : true,
"reposted" : false,
"deleted" : false
} ],
"metadata" : {
"nextCursor" : "dGVhbTpDMDYxRkE1UEI=",
"hasNext" : true
}
}
4.6.4. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
답글 포스트 목록 |
|
|
|
페이지네이션 메타데이터 |
|
|
|
답글 포스트 ID |
|
|
|
답글 작성자 정보 |
|
|
|
답글 내용 |
|
|
|
좋아요 수 |
|
|
|
현재 사용자의 좋아요 여부 |
|
|
|
리포스트 수 |
|
|
|
현재 사용자의 리포스트 여부 |
|
|
|
첨부된 미디어 정보 목록 |
|
|
|
답글 생성 시간 |
|
|
|
삭제 여부 |
|
|
|
작성자 고유 ID |
|
|
|
작성자 닉네임 |
|
|
|
작성자 아바타 URL |
|
|
|
작성자 핸들 |
|
|
|
미디어 ID |
|
|
|
미디어 접근 URL |
|
|
|
true |
다음 페이지를 가져오는 데 사용될 커서 |
|
|
다음 페이지가 존재하는지 여부 |
5. Media API
5.1. 파일 Upload URL 발급
5.1.1. HTTP Request
GET /api/media/upload-url?filename=test.jpg&contentType=image%2Fjpeg HTTP/1.1
Authorization: Bearer <ACCESS_TOKEN>
Host: localhost:8080
5.1.2. Query Parameters
Parameter | Description | Optional |
---|---|---|
|
업로드할 파일명 (예: test.jpg) |
|
|
파일의 MIME 타입 (예: image/jpeg) |
5.1.3. HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 89
{
"url" : "https://storage.googleapis.com/your-bucket/media/abc123.jpg?signature=..."
}
5.1.4. Response Fields
Path | Type | Optional | Description |
---|---|---|---|
|
|
GCS에 직접 업로드 가능한 signed URL |