src/db/schema.ts ๊ฐ ์๋ณธ์ด๋ค. ์ด ๋ฌธ์๋ ๊ด๊ณ, ์ปฌ๋ผ์ ๋ป, ์ค๊ณ ์ด์ ๋ฅผ ์ค๋ช
ํ๋ค. ์คํค๋ง๋ฅผ ๋ฐ๊พธ๋ฉด ์ด ๋ฌธ์๋ ๊ฐ์ด ๊ณ ์น๋ค. ๋ฌธํญ ์ ํ๊ณผ config ๋ questions.md.
erDiagram
users {
integer id PK "GitHub user id"
text login UK "GitHub login. ๋ฐ๋ ์ ์๋ค"
text name
text avatar_url
integer can_create_surveys "๋ก๊ทธ์ธ ๋ maintainer ํ ๋ฉค๋ฒ์ธ์ง"
text teams "๋ก๊ทธ์ธ ๋ ๋ฐ์ DaleStudy ํ slug ๋ชฉ๋ก (JSON)"
text created_at
}
sessions {
text id PK "๋ฌด์์ ํ ํฐ. __Host-session ์ฟ ํค ๊ฐ"
integer user_id FK
text expires_at "30์ผ"
}
surveys {
text id PK "nanoid 8์"
text title
text description
text visibility "home | link | invited"
text closes_at "null ์ด๋ฉด ๊ณ์ ์ด๋ฆผ"
text vars "๊ณตํต ๋ฌธํญ ์๋ฆฌํ์์ ๊ฐ (JSON)"
text created_at
}
survey_editors {
text survey_id PK, FK
text login PK "GitHub login. users ์ FK ์์"
}
survey_invitees {
text survey_id PK, FK
text kind PK "user | team"
text name PK "GitHub login ๋๋ ํ slug"
}
questions {
integer id PK "autoincrement"
text survey_id FK
integer position
text key "๊ณตํต ๋ฌธํญ๋ง. ์ค๋ฌธ ๊ฐ ๋น๊ต ํค"
text type "scale | short | long | choice"
text label
integer required "๊ธฐ๋ณธ 1"
integer identified "๊ธฐ๋ณธ 0. ๊ฒฐ๊ณผ์ ์๋ต์ ์์ด๋"
text config "JSON. ์ ํ๋ณ ์ค์ "
}
responses {
text id PK "uuid"
text survey_id FK
integer user_id FK
text submitted_at
}
answers {
text response_id PK, FK
integer question_id PK, FK
text value "ํญ์ ๋ฌธ์์ด"
}
users ||--o{ sessions : "cascade"
users ||--o{ responses : ""
surveys ||--o{ survey_editors : "cascade"
surveys ||--o{ survey_invitees : "cascade"
surveys ||--o{ questions : "cascade"
surveys ||--o{ responses : "cascade"
responses ||--o{ answers : "cascade"
questions ||--o{ answers : "cascade"
์ค๋ฌธ โ surveys โ questions
์ค๋ฌธ์ ํ๋ก ์ ๋ค. ๊ธฐ์๋ ํ๋ก๊ทธ๋จ์ ๋งค์ด์ง ์๊ณ , ์ ๋ชฉ์ด "๋ธ๋ก๊ทธ ์คํฐ๋ 1๊ธฐ ์ฐธ์ฌ ํ๊ณ " ๊ฐ์ ๋งฅ๋ฝ์ ๋ด๋๋ค. ๋ฌธํญ์ ์ค๋ฌธ๋ง๋ค ํ์ผ๋ก ๋ณต์ฌ๋๊ณ , ๊ณตํต ๋ฌธํญ์ key ๋ก๋ง ๋ฌถ์ธ๋ค โ ์ค๋ฌธ๋ผ๋ฆฌ ๋น๊ตํ ๋ ์ฐ๋ ์ ์ผํ ์ฐ๊ฒฐ์ด๋ค.
์๋ต โ responses โ answers
์๋ต ํ๋๊ฐ ํ ์ฌ๋์ด ์ค๋ฌธ ํ๋์ ๋ธ ๊ฒ์ด๊ณ , ๋ต์ ๋ฌธํญ๋น ํ ํ์ด๋ค (long format). ์ค๋ฌธ๋ง๋ค ๋์ ํ
์ด๋ธ์ ๋ง๋ค์ง ์์ ์ค๋ฌธ์ ์ถ๊ฐํด๋ ๋ง์ด๊ทธ๋ ์ด์
์ด ์๊ณ , questions.key ๋ก ์ค๋ฌธ์ ๊ฐ๋ก์ง๋ฌ ์ง๊ณํ ์ ์๋ค.
์ฌ๋ โ users, sessions, survey_editors
users ๋ GitHub ๋ก๊ทธ์ธ ๋ upsert ๋๊ณ sessions ๊ฐ ๊ทธ๊ฒ์ ๊ฐ๋ฆฌํจ๋ค. ๋ก๊ทธ์ธํ ๋ DaleStudy ์กฐ์ง์ maintainer ํ ๋ฉค๋ฒ์ธ์ง ํ์ธํด can_create_surveys ์ ์ ๋๋ค โ ์ ์ค๋ฌธ์ ์ด ์ฌ๋๋ง ๋ง๋ ๋ค. ์ค๋ฌธ์ ๊ณ ์น๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ ์ฌ๋์ ์ด์์ง ์ ์๊ณผ survey_editors ์ ๋ ์ฌ๋์ด๋ค. ๋ง๋ ์ฌ๋์ด ์ฒ์ ๋ค์ด๊ฐ๊ณ , ์ด์์ง์ด ์๋ ์ฌ๋์๊ฒ ๋งก๊ธธ ๋ GitHub ๊ณ์ ์ ๋ํ๋ค.
| ์ปฌ๋ผ | ์ค๋ช |
|---|---|
id |
nanoid 8์. URL ์ ๊ทธ๋๋ก ๋์จ๋ค (/V1StGXR8) |
description |
์๋ต ์์ ํ๋ฉด์ ์๋ด๋ฌธ. null ๊ฐ๋ฅ. ์์ ์๊ฐยท์ต๋ช ์ฌ๋ถ์ฒ๋ผ ํ๋ฉด์ด ๊ณ์ฐํด ๋ณด์ฌ ์ฃผ๋ ๊ฒ์ ์ ์ง ์๋๋ค |
visibility |
home: ๋ก๊ทธ์ธํ ๋๊ตฌ๋ ํ์์ ๋ณด๊ณ ๋ตํ๋ค. link: ํ์ ์ ๋ณด์ด๊ณ ๋งํฌ๋ฅผ ๋ฐ์ ์ฌ๋๋ง. invited: survey_invitees ์ ์ฌ๋ยทํ(๊ณผ ํธ์ง์)๋ง ํ์์ ๋ณด๊ณ ๋ตํ๋ค |
closes_at |
ISO 8601 UTC. null ์ด๋ฉด ๊ณ์ ์ด๋ฆผ. ํธ์ง ํ๋ฉด์ ๋ ์ง๋ง ๋ฐ์ ๊ทธ๋ 23:59:59 KST ๋ก ์ ์ฅํ๋ค. "์ง๊ธ ๋ง๊ฐ"์ ์ง๊ธ ์๊ฐ์, "๋ค์ ์ด๊ธฐ"๋ null ์ ๋ฃ๋๋ค |
vars |
๊ณตํต ๋ฌธํญ ์๋ฆฌํ์์ ๊ฐ (program, period, activity, artifact, redo, next). ๊ณตํต ๋ฌธํญ์ ๋ฃ์ ๋ ๋ฌธ๊ตฌ๋ฅผ ์ฑ์ด๋ค. null ๊ฐ๋ฅ |
created_at |
ISO 8601 UTC |
์ด์์ง(can_create_surveys) ๋ง๊ณ ๋ ์ค๋ฌธ์ ๊ณ ์น๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ฌ๋. GitHub login ์ผ๋ก ์ ์ผ๋ฏ๋ก ์์ง ๋ก๊ทธ์ธํ ์ ์๋ ์ฌ๋๋ ๋ฃ์ ์ ์๋ค. ๋ง์ง๋ง ํ ๋ช
์ ๋บ ์ ์๋ค.
| ์ปฌ๋ผ | ์ค๋ช |
|---|---|
survey_id |
PK, FK โ surveys. ์ค๋ฌธ ์ญ์ ์ cascade |
login |
PK. GitHub login. users ์ FK ์์ |
visibility ๊ฐ invited ์ธ ์ค๋ฌธ์ ๋์. ํธ์ง์์ฒ๋ผ GitHub ์ด๋ฆ์ผ๋ก ์ ์ด ์์ง ๋ก๊ทธ์ธํ ์ ์๋ ์ฌ๋๋ ๋ฃ์ ์ ์๋ค.
| ์ปฌ๋ผ | ์ค๋ช |
|---|---|
survey_id |
PK, FK โ surveys. cascade |
kind |
PK. user(GitHub login) ๋๋ team(DaleStudy ํ slug) |
name |
PK. login ์ ๋์๋ฌธ์๋ฅผ ๊ฐ๋ฆฌ์ง ์๊ณ ๋น๊ตํ๋ค. ํ์ users.teams ์ ๋ง์ถฐ ๋ณธ๋ค |
| ์ปฌ๋ผ | ์ค๋ช |
|---|---|
id |
autoincrement. answers ๊ฐ ์ด๊ฑธ ๊ฐ๋ฆฌํจ๋ค |
survey_id |
FK โ surveys. cascade |
position |
ํ์ ์์ (1๋ถํฐ) |
key |
๊ณตํต ๋ฌธํญ์ ์์ ํค. ์ด ์ค๋ฌธ๋ง์ ๋ฌธํญ์ null |
type |
scale / short / long / choice |
label |
์๋ต์์๊ฒ ๋ณด์ด๋ ๋ฌธ๊ตฌ. ์๋ฆฌํ์์๋ ์ ์ฅํ ๋ ์ด๋ฏธ ์นํ๋จ |
required |
0/1 |
identified |
1 ์ด๋ฉด ๊ฒฐ๊ณผ ํ๋ฉด์์ ์ด ๋ฌธํญ์ ๋ต์๋ง ์๋ต์ GitHub ์์ด๋๋ฅผ ๋ถ์ธ๋ค. ๊ณตํต ๋ฌธํญ ์ ์๊ฐ ์ ํ๋ค (join_organizers) |
config |
์ ํ๋ณ ์ค์ JSON. null ์ด๋ฉด ์ ํ ๊ธฐ๋ณธ๊ฐ |
| ์ปฌ๋ผ | ์ค๋ช |
|---|---|
id |
UUID |
survey_id |
FK โ surveys. cascade |
user_id |
FK โ users. ๋ตํ ์ฌ๋. ๊ฒฐ๊ณผ ํ๋ฉด์ ์ฐธ์ฌ์ ๋ช
๋จ๋ง ๋ณด์ฌ ์ฃผ๊ณ ๋ต๊ณผ ์์ง ์๋๋ค (identified ๋ฌธํญ๋ง ์์ธ) |
submitted_at |
ISO 8601 UTC. ๋ง์ง๋ง์ผ๋ก ๋ธ ์๊ฐ โ ๋ง๊ฐ ์ ์๋ ์๋ต์ ๊ณ ์น ์ ์๊ณ , ๊ณ ์น๋ฉด ์ด ํ์ ๋๊ณ answers ๋ง ํต์งธ๋ก ๋ฐ๊พผ๋ค |
์๋ต ํ ๊ฑด์ ๋ฌธํญ๋ณ ๊ฐ. ๋นํ์ ๋ฌธํญ์ ๋น ๋ต์ ํ์ด ์๋ค.
| ์ปฌ๋ผ | ์ค๋ช |
|---|---|
response_id |
PK, FK โ responses. cascade |
question_id |
PK, FK โ questions. cascade |
value |
ํญ์ ๋ฌธ์์ด. scale ์ "4", choice ๋ ๋ณด๊ธฐ ๋ฌธ๊ตฌ ๊ทธ๋๋ก |
GitHub ๋ก๊ทธ์ธ ๋ upsert ๋๋ ์ ์ ์บ์.
| ์ปฌ๋ผ | ์ค๋ช |
|---|---|
id |
GitHub user id. login ์ด ๋ฐ๋์ด๋ ๋ถ๋ณ์ด๋ผ ์๋ต์ ํค๋ก ์ด๋ค |
login |
UNIQUE. ๋ฐ๋ ์ ์๋ค. ์ login ์ ๋ค๋ฅธ ์ฌ๋์ด ๊ฐ์ ธ์ค๋ฉด ์ ํ์ login#id ๋ก ๋น์ผ ์ค๋ค (auth.callback.tsx) |
name |
GitHub ํ์ ์ด๋ฆ. null ๊ฐ๋ฅ |
can_create_surveys |
๋ก๊ทธ์ธํ ๋ DaleStudy maintainer ํ ๋ฉค๋ฒ์ธ์ง ํ์ธํด ์ ๋๋ค. ํ์ด ๋ฐ๋๋ฉด ๋ค์ ๋ก๊ทธ์ธ ๋ ๋ฐ์ |
teams |
๋ก๊ทธ์ธํ ๋ ๋ฐ์ ๋๋ DaleStudy ํ slug ๋ชฉ๋ก(JSON). ํ์ผ๋ก ๋์์ ์ ํ ์ค๋ฌธ์ด ๋ณธ๋ค. ํ ํฐ์ ์ ์ฅํ์ง ์์ผ๋ฏ๋ก ๋ค์ ๋ก๊ทธ์ธ ๋ ๊ฐฑ์ ๋๋ค |
created_at |
์ฒซ ๋ก๊ทธ์ธ ์๊ฐ |
| ์ปฌ๋ผ | ์ค๋ช |
|---|---|
id |
32๋ฐ์ดํธ ๋์์ base64url. __Host-session ์ฟ ํค ๊ฐ |
user_id |
FK โ users. cascade |
expires_at |
๋ฐ๊ธ ํ 30์ผ. ์ง๋๋ฉด ์กฐํ ์ ์ญ์ |
| ๊ด๊ณ | ์ด์ |
|---|---|
survey_editors.login, survey_invitees.name โ users.login |
์์ง ๋ก๊ทธ์ธํ ์ ์๋ ์ฌ๋๋ ํธ์ง์๋ก ๋ฃ์ ์ ์์ด์ผ ํ๋ค. ๋๊ฐ: GitHub login ์ ๋ฐ๋ ์ ์๋ค. ํธ์ง์๊ฐ login ์ ๋ฐ๊พธ๋ฉด ๋ค๋ฅธ ํธ์ง์๊ฐ ์ login ์ ๋ฃ๊ณ ์ ๊ฒ์ ๋บ๋ค. |
questions.key โ ์ด๋๋ |
๊ณตํต ๋ฌธํญ ์ ์๋ DB ๊ฐ ์๋๋ผ ์ฝ๋(src/questions/common.ts)์ ์๋ค. DB ๋ ๋ฌธ์์ด๋ง ๊ฐ๋๋ค. |
surveys.id๋ nanoid 8์๋ค. ๋งํฌ๋ก๋ง ๊ณต๊ฐํ ์ค๋ฌธ์ id ๋ฅผ ์์์ผ ์ฐพ์ ์ ์๋ค(64โธ โ 48๋นํธ). ์ค๋ฌธ id ๋ ๋ฐ๊พธ์ง ์๋๋ค โ ๊ณต์ ๋ ๋งํฌ๊ฐ ๊นจ์ง๋ค.users.id๋ GitHub ์ซ์ id(์ธ๋ถ ์์คํ ์ ๋ถ๋ณ ํค). ๋ฐ๋ ์ ์๋login์ UNIQUE ์์ฑ์ผ๋ก๋ง ๋๋ค.responses (survey_id, user_id)UNIQUE โ ๊ฐ์ ์ฌ๋์ด ๊ฐ์ ์ค๋ฌธ์ ๋ ๋ฒ ๋ตํ ์ ์๋ค.questions.type์ Drizzle enum ์ด์ง๋ง SQLite ์๋ CHECK ๊ฐ ์๋ค. ์ ํ๋ฆฌ์ผ์ด์ ๋ง ๊ฒ์ฌํ๋ค.
-- ์ค๋ฌธ๋ณ ์๋ต ์
SELECT s.id, s.title, COUNT(r.id) AS responses
FROM surveys s LEFT JOIN responses r ON r.survey_id = s.id
GROUP BY s.id;
-- ๊ณตํต ๋ฌธํญ ํ๋๋ฅผ ์ค๋ฌธ ๊ฐ๋ก์ง๋ฌ (์ค๋ฌธ ์ ๋ชฉ์ผ๋ก ๊ธฐ์ยทํ๋ก๊ทธ๋จ์ ๊ตฌ๋ถํ๋ค)
SELECT s.title, ROUND(AVG(CAST(a.value AS REAL)), 2) AS avg, COUNT(*) AS n
FROM answers a
JOIN questions q ON q.id = a.question_id
JOIN surveys s ON s.id = q.survey_id
WHERE q.key = 'goal_achieved'
GROUP BY s.id ORDER BY s.created_at;ํ๋ก๋์
์์ ์คํ: bunx wrangler d1 execute feedback --remote --command "<sql>"