Skip to content

Latest commit

ย 

History

History
200 lines (164 loc) ยท 9.04 KB

database.md

File metadata and controls

200 lines (164 loc) ยท 9.04 KB

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

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"
Loading

์„ธ ๋ฉ์–ด๋ฆฌ

์„ค๋ฌธ โ€” 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 ๊ณ„์ •์„ ๋”ํ•œ๋‹ค.

ํ…Œ์ด๋ธ”

surveys

์ปฌ๋Ÿผ ์„ค๋ช…
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

survey_editors

์šด์˜์ง„(can_create_surveys) ๋ง๊ณ ๋„ ์„ค๋ฌธ์„ ๊ณ ์น˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์‚ฌ๋žŒ. GitHub login ์œผ๋กœ ์ ์œผ๋ฏ€๋กœ ์•„์ง ๋กœ๊ทธ์ธํ•œ ์  ์—†๋Š” ์‚ฌ๋žŒ๋„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค. ๋งˆ์ง€๋ง‰ ํ•œ ๋ช…์€ ๋บ„ ์ˆ˜ ์—†๋‹ค.

์ปฌ๋Ÿผ ์„ค๋ช…
survey_id PK, FK โ†’ surveys. ์„ค๋ฌธ ์‚ญ์ œ ์‹œ cascade
login PK. GitHub login. users ์™€ FK ์—†์Œ

survey_invitees

visibility ๊ฐ€ invited ์ธ ์„ค๋ฌธ์˜ ๋Œ€์ƒ. ํŽธ์ง‘์ž์ฒ˜๋Ÿผ GitHub ์ด๋ฆ„์œผ๋กœ ์ ์–ด ์•„์ง ๋กœ๊ทธ์ธํ•œ ์  ์—†๋Š” ์‚ฌ๋žŒ๋„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.

์ปฌ๋Ÿผ ์„ค๋ช…
survey_id PK, FK โ†’ surveys. cascade
kind PK. user(GitHub login) ๋˜๋Š” team(DaleStudy ํŒ€ slug)
name PK. login ์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ฐ€๋ฆฌ์ง€ ์•Š๊ณ  ๋น„๊ตํ•œ๋‹ค. ํŒ€์€ users.teams ์™€ ๋งž์ถฐ ๋ณธ๋‹ค

questions

์ปฌ๋Ÿผ ์„ค๋ช…
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 ์ด๋ฉด ์œ ํ˜• ๊ธฐ๋ณธ๊ฐ’

responses

์ปฌ๋Ÿผ ์„ค๋ช…
id UUID
survey_id FK โ†’ surveys. cascade
user_id FK โ†’ users. ๋‹ตํ•œ ์‚ฌ๋žŒ. ๊ฒฐ๊ณผ ํ™”๋ฉด์€ ์ฐธ์—ฌ์ž ๋ช…๋‹จ๋งŒ ๋ณด์—ฌ ์ฃผ๊ณ  ๋‹ต๊ณผ ์ž‡์ง€ ์•Š๋Š”๋‹ค (identified ๋ฌธํ•ญ๋งŒ ์˜ˆ์™ธ)
submitted_at ISO 8601 UTC. ๋งˆ์ง€๋ง‰์œผ๋กœ ๋‚ธ ์‹œ๊ฐ โ€” ๋งˆ๊ฐ ์ „์—๋Š” ์‘๋‹ต์„ ๊ณ ์น  ์ˆ˜ ์žˆ๊ณ , ๊ณ ์น˜๋ฉด ์ด ํ–‰์€ ๋‘๊ณ  answers ๋งŒ ํ†ต์งธ๋กœ ๋ฐ”๊พผ๋‹ค

answers

์‘๋‹ต ํ•œ ๊ฑด์˜ ๋ฌธํ•ญ๋ณ„ ๊ฐ’. ๋น„ํ•„์ˆ˜ ๋ฌธํ•ญ์˜ ๋นˆ ๋‹ต์€ ํ–‰์ด ์—†๋‹ค.

์ปฌ๋Ÿผ ์„ค๋ช…
response_id PK, FK โ†’ responses. cascade
question_id PK, FK โ†’ questions. cascade
value ํ•ญ์ƒ ๋ฌธ์ž์—ด. scale ์€ "4", choice ๋Š” ๋ณด๊ธฐ ๋ฌธ๊ตฌ ๊ทธ๋Œ€๋กœ

users

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 ์ฒซ ๋กœ๊ทธ์ธ ์‹œ๊ฐ

sessions

์ปฌ๋Ÿผ ์„ค๋ช…
id 32๋ฐ”์ดํŠธ ๋‚œ์ˆ˜์˜ base64url. __Host-session ์ฟ ํ‚ค ๊ฐ’
user_id FK โ†’ users. cascade
expires_at ๋ฐœ๊ธ‰ ํ›„ 30์ผ. ์ง€๋‚˜๋ฉด ์กฐํšŒ ์‹œ ์‚ญ์ œ

์ผ๋ถ€๋Ÿฌ FK ๋ฅผ ๋‘์ง€ ์•Š์€ ๊ณณ

๊ด€๊ณ„ ์ด์œ 
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>"