๐Ÿ”ง ์šด์˜ ๊ฐ€์ด๋“œ

๐Ÿ”ง ์šด์˜ ๊ฐ€์ด๋“œ (์ฑ…์ž„์ž์šฉ)

์„œ๋ฒ„ ๋„์šฐ๊ธฐ, ์™ธ๋ถ€ ๋…ธ์ถœ, ๋ฐฑ์—…, ์ธ์ฆ, ๋ณด์•ˆ. ์‚ฌ์šฉ์ž(๋งค๋ฌผ ๊ด€๋ฆฌ)๋Š” ์›น ๊ฐ€์ด๋“œ ์ฐธ๊ณ .

1. ์„œ๋ฒ„ ๋„์šฐ๊ธฐ

๋งค์ผ ์‚ฌ์šฉ

cd ~/easyseop/meetcute
./dev.sh

dev.sh ๊ฐ€ ์ž๋™์œผ๋กœ: - .env ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กœ๋“œ - .secret ํŒŒ์ผ ์ƒ์„ฑ/๋กœ๋“œ (์„ธ์…˜ ์ฟ ํ‚ค ํ‚ค) - pip install -e . (์˜์กด์„ฑ ๋™๊ธฐํ™”) - uvicorn --reload ์‹œ์ž‘ - 15์ดˆ๋งˆ๋‹ค git ํด๋ง โ†’ ์ƒˆ ์ปค๋ฐ‹ ์ž๋™ pull โ†’ ์ž๋™ ๋ฆฌ๋กœ๋“œ - pyproject ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ์žฌ์„ค์น˜ + ์žฌ์‹œ์ž‘ - uvicorn ์ฃฝ์œผ๋ฉด ์ž๋™ ์žฌ๊ธฐ๋™

.env ์˜ต์…˜

PORT=8765                                     # ํฌํŠธ (๊ธฐ๋ณธ 8765)
HOST=127.0.0.1                                # 0.0.0.0 ์œผ๋กœ ํ•˜๋ฉด ๊ฐ™์€ ์™€์ดํŒŒ์ด ์ ‘๊ทผ
MEETCUTE_AUTH=on                              # ์ธ์ฆ ์ผœ๊ธฐ (๊ธฐ๋ณธ off)
MEETCUTE_PUBLIC=on                            # ์™ธ๋ถ€ ๋…ธ์ถœ ๋ชจ๋“œ (HTTPS ์ฟ ํ‚ค + AUTH ๊ฐ•์ œ)
MEETCUTE_PUBLIC_URL=https://meetcute.xxx.com  # ํ…”๋ ˆ๊ทธ๋žจ ๋ฉ”์‹œ์ง€์˜ ๋งํฌ prefix
MEETCUTE_TELEGRAM_BOT_TOKEN=123:abc...        # ๋ด‡ ํ† ํฐ (BotFather ์—์„œ)
MEETCUTE_DB_URL=sqlite:///./data/meetcute.db  # DB URL (๊ธฐ๋ณธ SQLite)
MEETCUTE_TUNNEL_NAME=meetcute                 # cloudflared ์˜๊ตฌ ํ„ฐ๋„ ์ด๋ฆ„
MEETCUTE_TUNNEL_HOSTNAME=meetcute.xxx.com     # ์˜๊ตฌ ๋„๋ฉ”์ธ

2. ์™ธ๋ถ€ ๋…ธ์ถœ (cloudflared)

๋ณ„๋„ ํ„ฐ๋ฏธ๋„์—์„œ:

./tunnel.sh
  • .env ์˜ MEETCUTE_TUNNEL_NAME + MEETCUTE_TUNNEL_HOSTNAME ๋‘˜ ๋‹ค ์žˆ์œผ๋ฉด โ†’ named ๋ชจ๋“œ (์˜๊ตฌ URL)
  • ์—†์œผ๋ฉด โ†’ quick ๋ชจ๋“œ (*.trycloudflare.com ๋žœ๋ค URL, ์žฌ์‹œ์ž‘๋งˆ๋‹ค ๋ฐ”๋€œ)

์˜๊ตฌ URL ์…‹์—… (1ํšŒ)

brew install cloudflared
cloudflared tunnel login          # ๋ธŒ๋ผ์šฐ์ €๋กœ cloudflare ๊ณ„์ • ์ธ์ฆ (๋„๋ฉ”์ธ ํ•„์š”)
# .env ์— MEETCUTE_TUNNEL_NAME / MEETCUTE_TUNNEL_HOSTNAME ์ถ”๊ฐ€
./tunnel.sh                       # ์ž๋™์œผ๋กœ tunnel ์ƒ์„ฑ + DNS ๋ผ์šฐํŒ…

URL ๋ณ€๊ฒฝ ์ž๋™ ์•Œ๋ฆผ

tunnel.sh ๊ฐ€ ํ˜„์žฌ URL ์„ .public_url ํŒŒ์ผ์— ๊ธฐ๋ก. ์•ฑ์ด ๋งค๋ถ„ ํ™•์ธ โ†’ ๋ณ€๊ฒฝ ๊ฐ์ง€ ์‹œ ๋“ฑ๋ก๋œ ๋ชจ๋“  ๋งˆ๋‹ด๋šœ ์—๊ฒŒ ํ…”๋ ˆ๊ทธ๋žจ์œผ๋กœ ์ƒˆ URL ํ‘ธ์‹œ.

3. ์ธ์ฆ ํ† ๊ธ€

๋ชจ๋“œ env ๋™์ž‘
๋กœ์ปฌ (๊ธฐ๋ณธ) MEETCUTE_AUTH ์—†์Œ ๋กœ๊ทธ์ธ X ยท ๋ชจ๋“  ํŽ˜์ด์ง€ ๋ฐ”๋กœ ์ ‘๊ทผ
์ธ์ฆ MEETCUTE_AUTH=on ๋กœ๊ทธ์ธ ํ•„์š” ยท ๋‹ค์ค‘ admin ๊ฐ€๋Šฅ
๊ณต๊ฐœ MEETCUTE_PUBLIC=on ์ธ์ฆ ์ž๋™ ๊ฐ•์ œ + HTTPS ์ฟ ํ‚ค + ๋ถ„ํ™ ๋ฐฐ๋„ˆ

์ฒซ ๊ฐ€์ž…์ž = ์ฑ…์ž„์ž

  • DB ์— ์œ ์ € ์—†์„ ๋•Œ ์ฒซ ๊ฐ€์ž…์ž๊ฐ€ ์ž๋™์œผ๋กœ is_admin=True, is_owner=True
  • ์ดํ›„ ๊ฐ€์ž…์ž๋Š” ์ฑ…์ž„์ž ์Šน์ธ ๋Œ€๊ธฐ (/auth/pending)
  • ์ฑ…์ž„์ž๊ฐ€ /users ์—์„œ ๋งˆ๋‹ด๋šœ๋กœ ๋ฒ„ํŠผ ํด๋ฆญํ•˜๋ฉด ํ’€ ๊ถŒํ•œ

์ฑ…์ž„์ž (๐Ÿ‘‘) ํŠน๊ถŒ

  • ๋ชจ๋“  ์‚ฌ๋žŒ ํ…”๋ ˆ๊ทธ๋žจ chat_id ์—ด๋žŒ (๋‹ค๋ฅธ ๋งˆ๋‹ด๋šœํ•œํ…Œ๋Š” ์•ˆ ๋ณด์ž„)
  • ๋ชจ๋“  ๋งค๋ฌผ ์ˆ˜์ •/์‚ญ์ œ (๋‹ค๋ฅธ ๋งˆ๋‹ด๋šœ ์˜ ๋งค๋ฌผ๋„)
  • /users ์—์„œ ๊ถŒํ•œ ๋ถ€์—ฌ/ํšŒ์ˆ˜

๋งˆ์ง€๋ง‰ ์ฑ…์ž„์ž ๋ณดํ˜ธ

  • ์ฑ…์ž„์ž๊ฐ€ 1๋ช…์ด๋ฉด ๊ฐ•๋“ฑ/์‚ญ์ œ ๋ถˆ๊ฐ€
  • ์ฑ…์ž„์ž ์˜ฎ๊ธฐ๋ ค๋ฉด ๋จผ์ € ๋‹ค๋ฅธ ์‚ฌ๋žŒ ์Šน๊ธ‰ ํ›„ ๋ณธ์ธ ๊ฐ•๋“ฑ

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

  • ๊ธฐ๋ณธ: SQLite (meetcute/data/meetcute.db)
  • ์ฒซ ๋ถ€ํŒ… ์‹œ ์ž๋™ ์ƒ์„ฑ, ์Šคํ‚ค๋งˆ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋„ ์ž๋™ (ALTER TABLE ADD COLUMN)
  • MEETCUTE_DB_URL ๋กœ MySQL/Postgres ์ „ํ™˜ ๊ฐ€๋Šฅ (mysql+pymysql://..., postgresql+psycopg2://...)

์‹œ๋“œ ๋ฐ์ดํ„ฐ (๊ฐ€์งœ ๋งค๋ฌผ 8๋ช… + ๋งŒ๋‚จ + ์ด๋ ฅ)

python -m app.seed              # ๋นˆ DB ์ผ ๋•Œ๋งŒ
python -m app.seed --force      # ๋‹ค ์ง€์šฐ๊ณ  ๋‹ค์‹œ

DB ์ƒํƒœ ํ™•์ธ

/settings ํŽ˜์ด์ง€์˜ "๐Ÿ’พ ๋ฐ์ดํ„ฐ / ์‹œ์Šคํ…œ ์ƒํƒœ" ์นด๋“œ: - DB ํƒ€์ž… / ํŒŒ์ผ ๊ฒฝ๋กœ / ํŒŒ์ผ ํฌ๊ธฐ - ์‚ฌ์ง„ ํด๋” ํฌ๊ธฐ / ์‚ฌ์ง„ ๊ฐœ์ˆ˜ - ํ…Œ์ด๋ธ”๋ณ„ row ๊ฐœ์ˆ˜

5. ๋ฐฑ์—… (Dropbox / iCloud)

๋ฐ์ดํ„ฐ๋Š” ๋‘ ํด๋” + ๋‘ ํ‚ค ํŒŒ์ผ์—๋งŒ ์žˆ์Œ: - meetcute/data/meetcute.db โ€” DB - meetcute/uploads/ โ€” ์‚ฌ์ง„ - meetcute/.secret โ€” ์„ธ์…˜ ์ฟ ํ‚ค ์„œ๋ช… ํ‚ค (์‚ฌ๋ผ์ง€๋ฉด ๋ชจ๋“  ์‚ฌ์šฉ์ž ๋‹ค์‹œ ๋กœ๊ทธ์ธ) - meetcute/.encryption_key โ€” DB ํ•„๋“œ ์•”ํ˜ธํ™” ํ‚ค (์‚ฌ๋ผ์ง€๋ฉด ๊ฑฐ์ฃผ์ง€/๋‚˜์ด + ๋งˆ๋‹ด๋šœ ์ด๋ฉ”์ผ/๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณต๊ตฌ ๋ถˆ๊ฐ€ = ๋กœ๊ทธ์ธ ์ž์ฒด ๋ถˆ๊ฐ€. ์˜›๋‚ ์— ์•”ํ˜ธํ™”๋ผ ์•„์ง ํ‰๋ฌธ์œผ๋กœ ์•ˆ ๋ฎ์–ด์“ด ๋ฐ์ดํ„ฐ๋„ ๋ชป ์ฝ์Œ)

โš ๏ธ .encryption_key ๊ฐ€ ๊ฐ€์žฅ ์ค‘์š”. ์ด ํŒŒ์ผ ์žƒ์œผ๋ฉด ์•”ํ˜ธํ™”๋œ ํ…์ŠคํŠธ ์˜๊ตฌ ๋ณต๊ตฌ ๋ถˆ๊ฐ€. ๋ฐฑ์—… ํด๋”์— ๊ผญ ๊ฐ™์ด ๋ณต์‚ฌํ•˜์„ธ์š”.

๋ฐฉ๋ฒ• A โ€” ์‹ฌ๋ณผ๋ฆญ ๋งํฌ ์ž๋™ (1ํšŒ ์…‹์—…, โญ ์ถ”์ฒœ)

Ctrl+C   # dev.sh ์ข…๋ฃŒ
cd ~/easyseop/meetcute

mkdir -p ~/Dropbox/meetcute-backup
mv data ~/Dropbox/meetcute-backup/data
mv uploads ~/Dropbox/meetcute-backup/uploads
ln -s ~/Dropbox/meetcute-backup/data data
ln -s ~/Dropbox/meetcute-backup/uploads uploads

./dev.sh

โš ๏ธ ๋…ธํŠธ๋ถ ํ•œ ๋Œ€์—์„œ๋งŒ ์‚ฌ์šฉ (SQLite ๋‹จ์ผ writer). ๊ฐ™์€ Dropbox DB ๋ฅผ ๋‘ ๋…ธํŠธ๋ถ์—์„œ ๋™์‹œ ์ ‘๊ทผํ•˜๋ฉด ๊นจ์งˆ ์ˆ˜ ์žˆ์Œ.

๋ฐฉ๋ฒ• B โ€” ์ฃผ๊ธฐ์  rsync

crontab -e
# ๋งค์ผ ์ƒˆ๋ฒฝ 2์‹œ
0 2 * * * rsync -a ~/easyseop/meetcute/data ~/easyseop/meetcute/uploads ~/Dropbox/meetcute-backup/

๋ฐฉ๋ฒ• C โ€” ๋ฐฑ์—… ๋ช…๋ น (โญ ์ถ”์ฒœ, ํ‚ค ํŒŒ์ผ๊นŒ์ง€ ๊ฐ™์ด ๋ฌถ์ž„)

ํ•œ ์ค„๋กœ data/ + uploads/ + .encryption_key + .secret ๋ชจ๋‘๋ฅผ timestamped .tar.gz ๋กœ:

cd ~/easyseop/meetcute
python -m app.backup                              # โ†’ BASE_DIR/backups/meetcute-YYYYMMDD-HHMMSS.tar.gz
python -m app.backup --out ~/Dropbox/meetcute-bk  # ๋‹ค๋ฅธ ํด๋”๋กœ
python -m app.backup --keep 30                    # ์ตœ์‹  30๊ฐœ๋งŒ ์œ ์ง€ (๊ธฐ๋ณธ 14)

ํ™˜๊ฒฝ๋ณ€์ˆ˜ MEETCUTE_BACKUP_DIR=~/Dropbox/meetcute-bk ๋กœ ๊ธฐ๋ณธ ์ถœ๋ ฅ ํด๋” ์ง€์ • ๊ฐ€๋Šฅ. cron ์˜ˆ:

0 2 * * * cd ~/easyseop/meetcute && /usr/bin/python -m app.backup --quiet

๋ณต๊ตฌ

Ctrl+C
# ๋ฐฉ๋ฒ• A/B (ํด๋” ๋ฐฑ์—…)
cp -R ~/Dropbox/meetcute-backup/data/* data/
cp -R ~/Dropbox/meetcute-backup/uploads/* uploads/
# ๋ฐฉ๋ฒ• C (tar.gz ๋ฐฑ์—…)
tar xzf ~/Dropbox/meetcute-bk/meetcute-2026XXXX-XXXXXX.tar.gz -C ~/easyseop/meetcute/
./dev.sh

์‚ฌ์ง„ ์ธ๋„ค์ผ ๋ฐฑํ•„ (ํ•œ ๋ฒˆ๋งŒ, ์†๋„ ํ–ฅ์ƒ)

์‹ ๊ทœ ์—…๋กœ๋“œ๋Š” ์ž๋™์œผ๋กœ 800px ์ธ๋„ค์ผ์„ ๋งŒ๋“ค์ง€๋งŒ, ์ธ๋„ค์ผ ๊ธฐ๋Šฅ ์ด์ „์— ์˜ฌ๋ผ์˜จ ์‚ฌ์ง„์€ _thumb ํŒŒ์ผ์ด ์—†์–ด์„œ ์นด๋“œ ๊ทธ๋ฆฌ๋“œ๊ฐ€ ๋งค๋ฒˆ ์›๋ณธ(1600px)์„ ๋ถˆ๋Ÿฌ์™€ ๋А๋ฆผ. ํ•œ ๋ฒˆ ๋Œ๋ ค๋‘๋ฉด ์˜› ์‚ฌ์ง„๋„ ์ƒˆ ์‚ฌ์ง„์ฒ˜๋Ÿผ ๋นจ๋ผ์ง:

cd ~/easyseop/meetcute
python -m app.backfill_thumbs
# ์ƒ์„ฑ: N๊ฐœ  /  ๊ฑด๋„ˆ๋œ€(์ด๋ฏธ ์žˆ์Œ): M๊ฐœ  /  ์‹คํŒจ: 0๊ฐœ

idempotent โ€” ์—ฌ๋Ÿฌ ๋ฒˆ ๋Œ๋ ค๋„ ์•ˆ์ „.

6. ๋ณด์•ˆ

์ž๋™ ์ ์šฉ

  • ์„ธ์…˜ ์ฟ ํ‚ค ์„œ๋ช… (.secret ์ž๋™ ์ƒ์„ฑ, chmod 600)
  • DB ํ•„๋“œ ์•”ํ˜ธํ™” (.encryption_key ์ž๋™ ์ƒ์„ฑ, Fernet/AES-128-CBC + HMAC)
  • ๋งค๋ฌผ PII: ๊ฑฐ์ฃผ์ง€, ๋‚˜์ด ๋‘ ๊ฐœ๋งŒ (์ง์žฅ์€ ํ‰๋ฌธ)
  • ๋งˆ๋‹ด๋šœ ๊ณ„์ •: ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ โ€” ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ด๋ฏธ bcrypt ๋‹จ๋ฐฉํ–ฅ ํ•ด์‹œ์ง€๋งŒ ๊ทธ ์œ„์— ํ•œ ๋ฒˆ ๋” ์•”ํ˜ธํ™” (DB ๋งŒ ํ„ธ๋ฆฌ๊ณ  .encryption_key ์•ˆ ํ„ธ๋ฆฐ ๊ฒฝ์šฐ ์˜คํ”„๋ผ์ธ ํฌ๋ž˜ํ‚น ์ฐจ๋‹จ)
  • ๋น„์•”ํ˜ธํ™”: ๊ทธ ์™ธ ๋ชจ๋“  ๊ฒƒ (public_id, ์„ฑ๋ณ„, ํ‚ค, ์ง์žฅ, ์ด๋ฆ„(alias), ์ด์ƒํ˜•, ์ฃผ์„ ์ž ๋ฉ”๋ชจ, ๋งŒ๋‚จ ๋ฉ”๋ชจ, ๋ณ€๊ฒฝ ์ด๋ ฅ, ์š”์ฒญ/์‘๋‹ต ๋ฉ”๋ชจ, ๋‹‰๋„ค์ž„, ํ…”๋ ˆ๊ทธ๋žจ)
  • ๋ฐฑ์›Œ๋“œ ํ˜ธํ™˜: ๊ธฐ์กด ํ‰๋ฌธ์€ ๊ทธ๋Œ€๋กœ ์ฝํž˜. ์˜›๋‚ ์— enc1: ๋กœ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ(์ง์žฅ/alias/์ด์ƒํ˜•/๋ฉ”๋ชจ ๋“ฑ)๋„ ์ž๋™ ๋ณตํ˜ธํ™”ํ•ด์„œ ์ฝ๋‹ค๊ฐ€, ํ•œ ๋ฒˆ ์ˆ˜์ •/์ €์žฅ๋˜๋ฉด ํ‰๋ฌธ์œผ๋กœ ์ •์ฐฉ
  • ์ด๋ฉ”์ผ์€ ๋น„๊ฒฐ์ •์  ์•”ํ˜ธ๋ฌธ์ด๋ผ DB ์ธ๋ฑ์Šค lookup ์•ˆ ๋จ โ†’ ๋กœ๊ทธ์ธ/์ค‘๋ณต์ฒดํฌ๋Š” ์ „์ฒด ์œ ์ € ์Šค์บ” + ๋ณตํ˜ธํ™” ๋น„๊ต (๋งˆ๋‹ด๋šœ ์ˆ˜๊ฐ€ ์ ์–ด ๋น„์šฉ ๋ฌด์‹œ)
  • ๋งค๋ฌผ ๊ณต๊ฐœ ๋ฒ”์œ„ (PUBLIC / RESTRICTED): RESTRICTED ๋งค๋ฌผ์€ owner + ์ฑ…์ž„์ž + ํ—ˆ๋ฝ๋œ admin ๋งŒ ์ ‘๊ทผ
  • bcrypt ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ
  • SameSite=Lax ์ฟ ํ‚ค (CSRF ์ฐจ๋‹จ)
  • ์‚ฌ์ง„ URL ์ธ์ฆ ๊ฒŒ์ดํŠธ (๋กœ๊ทธ์ธ ์•ˆ ํ•œ ์‚ฌ๋žŒ์€ ๋ชป ๋ด„)
  • ๋กœ๊ทธ์ธ 5ํšŒ ์‹คํŒจ ์‹œ IP 10๋ถ„ ์ž ๊ธˆ
  • /uploads/ path traversal ์ฐจ๋‹จ
  • ์ฑ…์ž„์ž๋งŒ ๋‹ค๋ฅธ ์‚ฌ๋žŒ chat_id ์—ด๋žŒ
  • ์ด๋ฆ„(๋ฉ”๋ชจ) ์€ owner ๋ณธ์ธ/์ฑ…์ž„์ž์—๊ฒŒ๋งŒ ํ‘œ์‹œ

์™ธ๋ถ€ ๋…ธ์ถœ ์‹œ ์ถ”๊ฐ€ ๊ถŒ์žฅ

  • .env ์— MEETCUTE_PUBLIC=on โ†’ HTTPS ์ฟ ํ‚ค ๊ฐ•์ œ
  • ๊ฐ•ํ•œ MEETCUTE_SECRET (.secret ํŒŒ์ผ ์ž๋™ ์ƒ์„ฑ, 64์ž hex)
  • cloudflared ๊ฐ€ HTTPS ์ž๋™
  • ๊ฐ€์ž…์€ ๋ˆ„๊ตฌ๋‚˜ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์ฑ…์ž„์ž ์Šน์ธ ์—†์ด๋Š” ์•„๋ฌด ๊ธฐ๋Šฅ ๋ชป ์”€

๋น„๋ฐ€๋ฒˆํ˜ธ ์žŠ์—ˆ์„ ๋•Œ (์žฌ์„ค์ • UI ์—†์Œ)

/tmp/meetcute-venv/bin/python -c "
from app.database import engine
from sqlmodel import Session, select
from app.models import User
from app.auth import hash_password
with Session(engine) as s:
    u = s.exec(select(User).where(User.email == '๋ณธ์ธ์ด๋ฉ”์ผ')).first()
    u.password_hash = hash_password('์ƒˆ๋น„๋ฐ€๋ฒˆํ˜ธ')
    s.add(u); s.commit()
print('๋ณ€๊ฒฝ ์™„๋ฃŒ')
"

๋˜๋Š” ๊ทธ ์‚ฌ๋žŒ์˜ user row ๋ฅผ ์‚ญ์ œ ํ›„ ์žฌ๊ฐ€์ž….

7. ์ง„๋‹จ / ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

์ฆ์ƒ ํ™•์ธ
ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์•ˆ ๋จ dev.sh ์ฝ˜์†”์— ์—๋Ÿฌ ์žˆ๋Š”์ง€
ํ…”๋ ˆ๊ทธ๋žจ ๋ด‡ ์‘๋‹ต X .env ์— MEETCUTE_TELEGRAM_BOT_TOKEN ์žˆ๋Š”์ง€ + dev.sh ์žฌ์‹œ์ž‘ ํ–ˆ๋Š”์ง€
๋ด‡ ์ธ์ฆ ์‹คํŒจ: SSL ์ธ์ฆ์„œ macOS python.org ์„ค์น˜ ์‹œ โ†’ ์ž๋™ ์ฒ˜๋ฆฌ๋จ (certifi ์‚ฌ์šฉ). ๊ทธ๋ž˜๋„ ๋‚˜๋ฉด pip install -e . ๋‹ค์‹œ
MEETCUTE_SECRET ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค .secret ํŒŒ์ผ ์ž๋™ ์ƒ์„ฑ๋จ โ€” dev.sh ์žฌ์‹œ์ž‘
getUpdates ๊ฐ€ ๋น„์–ด์žˆ์–ด ์ž๋™ ๊ฐ์ง€ ์•ˆ ๋จ ๋ด‡ username ์ž˜๋ชป (๋‹ค๋ฅธ ๋ด‡ํ•œํ…Œ ๋ฉ”์‹œ์ง€ ๋ณด๋ƒ„). /settings ํŽ˜์ด์ง€์˜ ๐ŸŸข ๋ฐ•์Šค์—์„œ ์ •ํ™•ํ•œ username ํ™•์ธ

์ฝ˜์†” ๋กœ๊ทธ: dev.sh ๋„์šด ํ„ฐ๋ฏธ๋„.