https://{host}/api/v1
// ์ฑ๊ณต
{
"success": true,
"data": { }
}
// ์คํจ
{
"success": false,
"error": {
"code": "INVALID_CREDENTIALS",
"message": "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ค์ ํ์ธํด์ฃผ์ธ์."
}
}๋ก๊ทธ์ธ ์ฑ๊ณต ์ Access Token, Refresh Token์ ํจ๊ป ๋ฐ๊ธํฉ๋๋ค.
| ํ ํฐ | ์ ์ฅ ์์น(๊ถ์ฅ) | ๋ง๋ฃ | ๋น๊ณ |
|---|---|---|---|
| Access Token | ํด๋ผ์ด์ธํธ ๋ฉ๋ชจ๋ฆฌ / SecureStore | 15~30๋ถ | ๋งค ์์ฒญ ํค๋์ ํฌํจ (ํ์๊ฐ์ , ๋ก๊ทธ์ธ ์ ์ธ) |
| Refresh Token | HttpOnly Secure Cookie (์น) / SecureStore (์ฑ) | 14~30์ผ | ์ฌ๋ฐ๊ธ ์ ํ์ (rotate) |
Authorization: Bearer {access_token}
Refresh Token ํ์ (Rotation) ๋ฐฉ์
- Access Token ๋ง๋ฃ ์
/auth/refreshํธ์ถ โ ์ Access Token + ์ Refresh Token ๋ฐ๊ธ, ๊ธฐ์กด Refresh Token์ ์ฆ์ ํ๊ธฐ - ํ์ทจ๋ ํ ํฐ ์ฌ์ฌ์ฉ ํ์ง: ์ด๋ฏธ ํ๊ธฐ๋ Refresh Token์ผ๋ก ์ฌ์์ฒญ์ด ๋ค์ด์ค๋ฉด ํด๋น ์ฌ์ฉ์์ ์ ์ฒด ์ธ์ ์ ๊ฐ์ ๋ก๊ทธ์์ ์ฒ๋ฆฌ
- ์๋ฒ๋ Refresh Token์ ํด์๋ก ์ ์ฅ(์๋ฌธ ์ ์ฅ ๊ธ์ง)
| ์ฝ๋ | ๋ฐ์ ํ๋ฉด | ๋ฉ์์ง |
|---|---|---|
INVALID_CREDENTIALS |
๋ก๊ทธ์ธ | ์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ค์ ํ์ธํด์ฃผ์ธ์. |
DUPLICATE_ID |
ํ์๊ฐ์ | ์ด๋ฏธ ์ฌ์ฉ ์ค์ธ ์์ด๋์์. ๋ค์ ์ ๋ ฅํด์ฃผ์ธ์. |
TOKEN_EXPIRED |
์ธ์ฆ ํ์ API ์ ๋ฐ | ๋ก๊ทธ์ธ์ด ๋ง๋ฃ๋์์ต๋๋ค. ๋ค์ ๋ก๊ทธ์ธํด์ฃผ์ธ์. |
REFRESH_TOKEN_REUSED |
ํ ํฐ ์ฌ๋ฐ๊ธ | ๋น์ ์์ ์ธ ์ ๊ทผ์ด ๊ฐ์ง๋์ด ๋ก๊ทธ์์๋์์ต๋๋ค. |
topics: ์ผ / ๊ด๊ณ / ๋ / ๊ฑด๊ฐ / ๋ (+๊ธฐํ)
values: ์ฑ์ฅ / ์์ / ์์จ / ์ฐ๊ฒฐ / ์ธ์ / ์ฌ๋ฏธ / ํจ์จ / ์๋ฏธ / ์ฑ ์ (+๊ธฐํ)
GET /auth/id/check?id={id}
Response
{
"success": true,
"data": {
"available": true|false
}
}POST /auth/signup
Request
{
"id": "layer2026",
"nickname": "string",
"password": "string",
"passwordConfirm": "string"
}Response
{
"success": true,
"data": {
"userId": "uuid"
}
}POST /auth/login
Request
{
"id": "layer2026",
"password": "string"
}Response
{
"success": true,
"data": {
"accessToken": "string",
"refreshToken": "string",
"user": {
"id": "uuid",
"loginId": "layer2026",
"nickname": "string"
},
"activateOnboarding": true|false // ์ฒซ ๋ก๊ทธ์ธ ์ true
}
}POST /auth/logout- ์ ๋ฌ๋ฐ์ Refresh Token์ ์ฆ์ ํ๊ธฐํ๋ค.
Request
{ "refreshToken": "string" }Response
{
"success": true,
"data": null
}DELETE /auth/delete- ๊ณ์ ์ ๋ณด, ๊ณ ๋ฏผ, ๊ธฐ๋ก, ์ธ์ฌ์ดํธ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ์ญ์ ํ๊ณ ํด๋น ์ฌ์ฉ์์ ์ ์ฒด ์ธ์ (Refresh Token)์ ํ๊ธฐํ๋ค.
Response
{
"success": true,
"data": null
}POST /auth/refresh
Request
{ "refreshToken": "string" }Response
{
"success": true,
"data": {
"accessToken": "string",
"refreshToken": "string"
}
}GET /homeongoingConcerns: ์งํ ์ค(PENDING) ์ํ์ ๊ณ ๋ฏผ์ ๋ง์ง๋ง ๊ธฐ๋ก์ผ ์ค๋๋ ์์ผ๋ก ์ ๋ ฌํ์ฌ ์ต๋ 3๊ฑด ๋ฐํํ๋ค.ongoingConcernCount: ์งํ ์ค์ธ ๊ณ ๋ฏผ์ ์ ์ฒด ๊ฐ์ (๋ฐฐ์ด ๊ธธ์ด์ ๋ณ๊ฐ)recentRecords: ์์ฑ์ผ ์ต์ ์์ผ๋ก ์ต๋ 3๊ฑด ๋ฐํํ๋ค.
Response
{
"success": true,
"data": {
"nickname": "๋๋ค์",
"ongoingConcernCount": 5,
"ongoingConcerns": [
{
"concernId": "uuid",
"concern": "A์ฌ vs B์ฌ",
"topic": "์ผ",
"topicOther": "", // topic์ด ๊ธฐํ์ผ ๊ฒฝ์ฐ ์ฌ์ฉ
"lastRecordDate": "2026-07-24"
}
],
"monthlyValueHighlight": {
"topValue": "์ฑ์ฅ",
"changeRateVsLastMonth": 12
},
"recentRecords": [
{
"recordId": "uuid",
"decision": "A๋ก ๋ง์์ด ๊ธฐ์",
"date": "2026-07-27",
"value": "์ฑ์ฅ"
}
]
}
}POST /concernstopicOther:topic์ด๊ธฐํ์ผ ๋๋ง ์ฌ์ฉํ๋ฉฐ ์ด ๊ฒฝ์ฐ ํ์๋ค. ๋ค๋ฅธ ์ฃผ์ ์์๋ ๊ฐ์ ๋ณด๋ด๋ ๋ฌด์ํ๋ค(null์ ์ฅ).
Request
{
"concern": "A์ฌ vs B์ฌ",
"topic": "์ผ",
"topicOther": "", // topic์ด ๊ธฐํ์ผ ๊ฒฝ์ฐ ์ฌ์ฉ
"decision": "์์ง ๋ชป ์ ํจ / A๋ก ๋ง์์ด ๊ธฐ์",
"reason": "ํ ์ค์ด๋ฉด ์ถฉ๋ถํด์",
"value": "์ฑ์ฅ",
"concernStatus": "PENDING|RESOLVED"
}GET /concerns/pending- ์งํ ์ค(
PENDING) ์ํ์ ๊ณ ๋ฏผ๋ง ์กฐํํ๋ค. ์ ๋ฆฌ๋(RESOLVED) ๊ณ ๋ฏผ์ ํฌํจํ์ง ์๋๋ค. - ๋ง์ง๋ง ๊ธฐ๋ก์ผ ์ต์ ์์ผ๋ก ์ ๋ ฌํ๋ค.
Response
{
"success": true,
"data": {
"ongoingConcerns": [
{
"concernId": "uuid",
"concern": "A์ฌ vs B์ฌ",
"topic": "์ผ",
"topicOther": "", // topic์ด ๊ธฐํ์ผ ๊ฒฝ์ฐ ์ฌ์ฉ
"lastRecordDate": "2026-07-24",
"recordCount": 4
}
]
}
}GET /concerns/pending/{concernId}- ํด๋น ๊ณ ๋ฏผ์ ์ง๋ ๊ธฐ๋ก์ ์ค๋๋ ์์ผ๋ก ์ ๋ ฌํ๋ค.
Response
{
"success": true,
"data": {
"concern": "A์ฌ vs B์ฌ",
"records": [
{
"recordId": "uuid",
"decision": "A์ฌ๊ฐ ์กฐ๊ธ ๋ ๋ง์์ ๋จ",
"value": "์ฑ์ฅ",
"createdAt": "2026-07-20"
}
]
}
}POST /concerns/pending/{concernId}
Request
{
"decision": "์์ง ๋ชป ์ ํจ / A๋ก ๋ง์์ด ๊ธฐ์",
"reason": "ํ ์ค์ด๋ฉด ์ถฉ๋ถํด์",
"value": "์ฑ์ฅ",
"concernStatus": "PENDING|RESOLVED"
}GET /insights?startDate=2025-07-01&endDate=2026-08-01&topics=์ผ&values=์ฑ์ฅ&status=PENDINGvalueByTopic: ๊ธฐ๋ก ์๊ฐ ๋ง์ ์์ผ๋ก ์ ๋ ฌํ๋ค.trend: ์กฐํ ๊ธฐ๊ฐ์ ํญ์ 4๊ฐ ๊ตฌ๊ฐ์ผ๋ก ๊ท ๋ฑ ๋ถํ ํ๋ฏ๋ก ๋ฐฐ์ด ๊ธธ์ด๋ 4๋ก ๊ณ ์ ๋๋ค. ๊ตฌ๊ฐ๋ณstartDate/endDate๋ ์๋ฒ๊ฐ ๊ณ์ฐํ๋ค.trend์valueDistribution์ ์กฐํ ๊ธฐ๊ฐ ์ ์ฒด์ ๋ฑ์ฅํ ๋ชจ๋ ๊ฐ์น๋ฅผ ๋งค ๊ตฌ๊ฐ์ ํฌํจํ๋ฉฐ, ํด๋น ๊ตฌ๊ฐ์ ๊ธฐ๋ก์ด ์๋ ๊ฐ์น๋percentage๊ฐ0์ด๋ค.largestIncrease/largestDecrease: ์ฆ๊ฐํญ์ด ๊ฐ์ฅ ํฐ ๊ฐ์น๋ฅผ ๋ฐํํ๋ฉฐ, ๋๋ฅ ์ธ ๊ฒฝ์ฐ์๋ง ๋ณต์ ํญ๋ชฉ์ ํฌํจํ๋ค.๊ธฐํ์ฃผ์ ๋topicOther๊ฐ๊ณผ ๋ฌด๊ดํ๊ฒ ํ๋๋ก ๋ฌถ์ด ์ง๊ณํ๋ค. ๋ฐ๋ผ์ ์ง๊ณ ์๋ต(valueByTopic, ๊ธฐ๋ก ๋ชฉ๋ก ์กฐํ)์topicOther๋ ํญ์null์ด๋ค.
Response
{
"success": true,
"data": {
"valueByTopic" : [
{
"topic": "์ผ",
"topicOther": "", // topic์ด ๊ธฐํ์ผ ๊ฒฝ์ฐ ์ฌ์ฉ
"valueDistribution": [
{ "value": "์ฑ์ฅ", "percentage": 62 }
],
"count": 12
}
],
"trend": [
{
"startDate": "2026-06-30",
"endDate": "2026-07-06",
"valueDistribution": [
{ "value": "์ฑ์ฅ", "percentage": 47 }
]
}
],
"largestIncrease": [
{ "value": "์ฑ์ฅ", "increaseRate": 16 }
],
"largestDecrease": [
{ "value": "์์ ", "decreaseRate": -4 }
],
"insight": {
"mostTopic": ["์ผ"],
"mostValue": ["์ฑ์ฅ"]
},
"totalCount": 48
}
}GET /insights/{topic}/records?startDate=2025-07-01&endDate=2026-08-01{topic}์ ์ฃผ์ ๋ช ๋ฌธ์์ด์ด๋ฉฐ URL ์ธ์ฝ๋ฉํ์ฌ ์ ๋ฌํ๋ค.- ๊ธฐ๋ก์ ์ต์ ์์ผ๋ก ์ ๋ ฌํ๋ค.
Response
{
"success": true,
"data": {
"topic": "๊ฑด๊ฐ",
"topicOther": "", // topic์ด ๊ธฐํ์ผ ๊ฒฝ์ฐ ์ฌ์ฉ
"records": [
{
"recordId": "uuid",
"decision": "ํฌ์ค ๋ค์ ์์",
"value": "์์ ",
"concernId": "uuid",
"concern": "ํฌ์ค ๋ค์ ์์ํ ๊น",
"recordDate": "2026-07-18",
"concernStatus": "PENDING|RESOLVED"
}
],
"recordCount": 8
}
}GET /concerns/{concernId}/timeline- ๊ธฐ๋ก์ ์ค๋๋ ์์ผ๋ก ์ ๋ ฌํ๋ค.
Response
{
"success": true,
"data": {
"concern": "ํฌ์ค ๋ค์ ์์ํ ๊น",
"topic": "๊ฑด๊ฐ",
"topicOther": "", // topic์ด ๊ธฐํ์ผ ๊ฒฝ์ฐ ์ฌ์ฉ
"records": [
{
"recordId": "uuid",
"decision": "์ค๋์ ์ฌ์ด๊ฐ๊ธฐ",
"value": "์์ ",
"createdAt": "2026-07-12"
}
],
"concernStatus": "PENDING|RESOLVED",
"recordCount": 2
}
}