ํ ํ๋ก์ ํธ ๋ชจ์ง ๋ฐ ํ์ ์ ์ํ ์ข ํฉ RESTful API ์๋น์ค
English | ํ๊ตญ์ด
- ํ๋ก์ ํธ ํํฉ ์์ฝ
- ์ฃผ์ ๊ธฐ๋ฅ
- ๊ธฐ์ ์คํ
- ์์ํ๊ธฐ
- ํ๋ก์ ํธ ๊ตฌ์กฐ
- API ๋ฌธ์
- ๋ฐ์ดํฐ๋ฒ ์ด์ค
- ๊ฐ๋ฐ ๊ฐ์ด๋
- ํ ์คํธ
- ๋ฐฐํฌ
- ํ๊ฒฝ ๋ณ์
- ๊ฐ๋ฐ ํํฉ
- ๊ธฐ์ฌํ๊ธฐ
๋ง์ง๋ง ๋ถ์: 2026-01-31 | ์ ์ฒด ์๋ฃ์จ: 97%
| ํญ๋ชฉ | ํํฉ | ์์ธ |
|---|---|---|
| API ์๋ํฌ์ธํธ | 90๊ฐ | 97% ๊ตฌํ ์๋ฃ |
| ์ปจํธ๋กค๋ฌ | 24๊ฐ | ์ ์ฒด ๊ตฌํ |
| ์๋น์ค | 21๊ฐ | ์ ์ฒด ๊ตฌํ |
| DB ๋ชจ๋ธ | 33๊ฐ | ์คํ 26๊ฐ ์ค 19๊ฐ + ์ถ๊ฐ 14๊ฐ |
| ๋ง์ด๊ทธ๋ ์ด์ | 14๊ฐ | ์ต์ ์ ๋ฐ์ดํธ ์๋ฃ |
| ๋ฏธ๋ค์จ์ด | 9๊ฐ | ์ธ์ฆ, ๊ฒ์ฆ, ์ ๋ก๋, UUID ๋ฑ |
| ๊ธฐ๋ฅ | ์ํ | ๋น๊ณ |
|---|---|---|
| ์ธ์ฆ (์ด๋ฉ์ผ/๊ตฌ๊ธ/ํด๋ํฐ) | โ | JWT, Firebase, OAuth |
| SMS ์ธ์ฆ | โ | Solapi, sessionId ๊ธฐ๋ฐ ๋น๋๊ธฐ ํ๋ก์ฐ |
| ํ๋ก์ ํธ CRUD + ํฅ์คํ | โ | ๋ชจ์งโํ๋ก์ ํธ ์ ํ |
| ๋ชจ์ง๊ณต๊ณ ์์คํ | โ | ํด์ํ๊ทธ, ์ด๋ฏธ์ง ์ ๋ก๋ |
| ์ง์/์๋ฝ/๊ฑฐ์ ํ๋ก์ฐ | โ | ํฌํธํด๋ฆฌ์ค ์ฐ๊ฒฐ |
| ํ์ ํ๊ฐ ์์คํ | โ | 5๊ฐ ํ๊ฐ ํญ๋ชฉ |
| ํฌํ/์ผ์ ๊ด๋ฆฌ | ๋ผ์ฐํธ ๋ฑ๋ก ํ์ | |
| ์๋ฆผ ์์คํ | โ | 5๊ฐ API ๊ตฌํ ์๋ฃ |
| iOS ํธ์ ์๋ฆผ | โ | APNs ์ง์, ์ด๋/์ํ/๋ฆฌ๋ทฐ ํธ๋ฆฌ๊ฑฐ |
| MBTI ์์คํ | โ | ์ ํ ์ ์ฅ ๋ฐ ์กฐํ |
| ์ง์ ์ทจ์ | โ | POST/DELETE ๋ฐฉ์ ์ง์ |
| ํ๋ก์ ํธ ์ฆ๊ฒจ์ฐพ๊ธฐ | โ | ํ ๊ธ API |
- 6์๋ฆฌ ์ธ์ฆ ์ฝ๋๋ฅผ ํตํ ์ด๋ฉ์ผ ์ธ์ฆ (SendGrid Web API)
- ๋๋ฉ์ธ ์ธ์ฆ ์๋ฃ (teamitaka.com)
- JWT ๊ธฐ๋ฐ ์ธ์ฆ ์์คํ
- ๊ตฌ๊ธ OAuth ์์ ๋ก๊ทธ์ธ
- Firebase ์ ํ๋ฒํธ ์ธ์ฆ (Phone Authentication)
- Firebase Admin SDK ํตํฉ
- SMS ๊ธฐ๋ฐ ์ ํ๋ฒํธ ์ธ์ฆ
- ์๋ ์ฌ์ฉ์ ์์ฑ ๋ฐ JWT ํ ํฐ ๋ฐ๊ธ
- ์ ํ๋ฒํธ ์ธ์ฆ ์ํ ์ถ์
- bcrypt ๊ธฐ๋ฐ ์์ ํ ๋น๋ฐ๋ฒํธ ์ํธํ
- ๋น๋ฐ๋ฒํธ ์ ์ฑ : ์๋ฌธ + ์ซ์ ํฌํจ, 8์ ์ด์
- Rate Limiting ์ ์ฉ (์ค๋ณต ์์ฒญ ๋ฐฉ์ง)
- Solapi ๊ธฐ๋ฐ SMS ๋ฐ์ก
- sessionId ๊ธฐ๋ฐ ๋น๋๊ธฐ ์ธ์ฆ ํ๋ก์ฐ
- node-cache๋ฅผ ํ์ฉํ ์ธ์ฆ ์ฝ๋ ์บ์ฑ
- ์ธ์ฆ ์ฝ๋ ์ ํจ ์๊ฐ ๊ด๋ฆฌ
- MBTI ๊ฒฐ๊ณผ ์ ์ฅ API (
POST /api/users/mbti) /api/auth/me์๋ต์mbtiTypeํ๋ ํฌํจ- ํ ๊ตฌ์ฑ ์ ์ฑํฅ ์ฐธ๊ณ ๊ฐ๋ฅ
- iOS APNs (Apple Push Notification service) ์ง์
- ์ด๋, ์ํ ๋ณ๊ฒฝ, ๋ฆฌ๋ทฐ ์๋ฆผ ํธ๋ฆฌ๊ฑฐ
- DeviceToken ๋ชจ๋ธ๋ก ํ ํฐ ๊ด๋ฆฌ
- ์ฑ ๋ฐฑ๊ทธ๋ผ์ด๋ ์๋ฆผ ์ง์
- ํ๋ก์ ํธ ์์ฑ, ์กฐํ, ์์ , ์ญ์ (CRUD)
- ๋ด ํ๋ก์ ํธ ์กฐํ (ํ๊ฐ ์ํ ์ถ์ )
- ํ์ ๋ชจ์ง ์์คํ (์ด๋ฏธ์ง ์ ๋ก๋ ์ง์)
- ํ๋ก์ ํธ ํฅ์คํ (๋ชจ์ง๊ธ โ ํ๋ก์ ํธ ์ ํ)
- ์น์ธ๋ ์ง์์ ์ค ํ์ ์ ํ
- ํ๋ก์ ํธ ์ ๋ชฉ, ๋ค์ง(resolution), ๊ธฐ๊ฐ ์ค์
- Recruitment์์ project_type ์๋ ๋ณต์ฌ
- ํค์๋(ํด์ํ๊ทธ) ๊ธฐ๋ฐ ๋ชจ์ง๊ธ ํ๊น
๋ฐ ๊ฒ์
- ๋ชจ์ง๊ธ๋น ์ต๋ 5๊ฐ ํค์๋ ์ง์
- ํค์๋๋ณ ๋ชจ์ง๊ธ ํํฐ๋ง
- ์ ์ฒด ๋ชจ์ง๊ธ ๋ชฉ๋ก์์ ํค์๋ ํ์
- ์ง์์ ์ ์ถ ๋ฐ ์ถ์ ๊ด๋ฆฌ
- ์๊ธฐ์๊ฐ ์์ฑ (1-500์)
- ํฌํธํด๋ฆฌ์ค ํ๋ก์ ํธ ์ฐ๊ฒฐ
- ๋ณธ์ธ ๋ชจ์ง๊ธ ์ง์ ๋ฐฉ์ง
- ํ ๋ฉค๋ฒ ๊ด๋ฆฌ
- ํ์ ์ํธ ํ๊ฐ ์์คํ
- ํ๋ก์ ํธ ํต๊ณ (์ฐธ์ฌ ํ๋ก์ ํธ ์, ๋ชจ์ง๊ณต๊ณ ์)
- ์ง์์ ๋ฐ ์๋ฆผ ์ถ์
- ํ๊ฐ ๋๊ธฐ ํ๋ก์ ํธ ํ์ธ
- ์ต๊ทผ ํ๋ ํ์๋ผ์ธ
- ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅํ ํ๋กํ
- ๊ธฐ์ ์คํ ๋ฐ ๊ฒฝ๋ ฅ ๊ด๋ฆฌ
- ํฌํธํด๋ฆฌ์ค ๊ด๋ฆฌ
- ๋๊ธ ๋ฐ ๋ต๊ธ
- ํ๋ก์ ํธ ๋ฆฌ๋ทฐ ๋ฐ ํ์
- ๋ถ๋งํฌ/์คํฌ๋ฉ ๊ธฐ๋ฅ
- ํฌํ ์์คํ
- ๊ณ ๊ธ ํ๋ก์ ํธ ๊ฒ์
- ๊ธฐ์ ์คํ, ์ญํ , ์ํ๋ณ ํํฐ๋ง
- ์ฌ์ฉ์ ๊ฒ์
- ๋ชจ์ง๊ณต๊ณ ์ด๋ฏธ์ง ์ ๋ก๋ (Supabase Storage)
- ์ด๋ฏธ์ง ํ์ผ ๊ฒ์ฆ (jpeg, png, webp)
- ํ์ผ ํฌ๊ธฐ ์ ํ (์ต๋ 5MB)
- UUID ๊ธฐ๋ฐ ํ์ผ๋ช ์์ฑ
- ์ฌ์ฉ์ ๊ด๋ฆฌ
- ์ฝํ ์ธ ๊ด๋ฆฌ
- ์์คํ ๋ชจ๋ํฐ๋ง
- ๋ฐํ์: Node.js 18+
- ํ๋ ์์ํฌ: Express.js
- ๋ฐ์ดํฐ๋ฒ ์ด์ค: MySQL / PostgreSQL (Supabase)
- ORM: Sequelize (๋ง์ด๊ทธ๋ ์ด์
) + Raw SQL (ํ๋ก๋์
์ฟผ๋ฆฌ)
- PostgreSQL snake_case ๋ช ๋ช ๊ท์น (project_members, created_at ๋ฑ)
- JWT: jsonwebtoken, jose
- ๋น๋ฐ๋ฒํธ ์ํธํ: bcrypt, bcryptjs
- Firebase: firebase-admin (์ ํ๋ฒํธ ์ธ์ฆ)
- ์ ํจ์ฑ ๊ฒ์ฆ: Joi, express-validator
- Rate Limiting: express-rate-limit
- CORS: cors
- ์ด๋ฉ์ผ ์๋น์ค: Resend (๋๋ฉ์ธ ์ธ์ฆ ์๋ฃ)
- SMS ์๋น์ค: Solapi (๊ตญ๋ด SMS ๋ฐ์ก)
- ํธ์ ์๋ฆผ: apns2 (iOS APNs)
- ์บ์: node-cache (์ธ์ฆ ์ฝ๋ ๊ด๋ฆฌ)
- ํ ํ๋ฆฟ ์์ง: markdown-it, marked
- ํ ์คํ : Jest, Supertest
- ์ฝ๋ ํ์ง: ESLint, Prettier
- ํ๋ก์ธ์ค ๊ด๋ฆฌ: nodemon
- ํ๊ฒฝ ๋ณ์: dotenv, cross-env
- ํ์ผ ์ ๋ก๋: multer
- ํ์ผ ์ ์ฅ์: Supabase Storage
- ํ์ผ๋ช ์์ฑ: uuid
- ํธ์คํ : Render (ํ๋ก๋์ )
- ๋ฐ์ดํฐ๋ฒ ์ด์ค: Supabase PostgreSQL (Shared Pooler)
- ์คํ ๋ฆฌ์ง: Supabase Storage (์ด๋ฏธ์ง ์ ๋ก๋)
- ์ด๋ฉ์ผ: Resend (๋๋ฉ์ธ ์ธ์ฆ ์๋ฃ)
- SMS: Solapi (๊ตญ๋ด SMS ๋ฐ์ก)
- ํธ์ ์๋ฆผ: APNs (iOS)
- ๋ฒ์ ๊ด๋ฆฌ: GitHub
- Node.js >= 18.0.0
- npm ๋๋ yarn
- MySQL 8.0+ ๋๋ PostgreSQL 14+
- Git
git clone https://github.com/TeamKoHong/teamitakaBackend.git
cd teamitakaBackendnpm install# ์์ ํ๊ฒฝ ํ์ผ ๋ณต์ฌ
cp .env.example .env.development
# ํ๊ฒฝ ๋ณ์ ํ์ผ ์์
nano .env.development# ๋ง์ด๊ทธ๋ ์ด์
์คํ
npm run migrate:dev
# ์ด๊ธฐ ๋ฐ์ดํฐ ์๋ฉ (์ ํ์ฌํญ)
npm run seed:devnpm run dev์๋ฒ๊ฐ http://0.0.0.0:8080 ์์ ์์๋ฉ๋๋ค.
# Docker Compose ์ฌ์ฉ
docker-compose up -dteamitakaBackend/
โโโ src/
โ โโโ config/ # ์ค์ ํ์ผ (4๊ฐ: database, env, ...)
โ โโโ controllers/ # ๋ผ์ฐํธ ์ปจํธ๋กค๋ฌ (22๊ฐ)
โ โโโ middlewares/ # Express ๋ฏธ๋ค์จ์ด (9๊ฐ: ์ธ์ฆ, ๊ฒ์ฆ, ์
๋ก๋, UUID)
โ โโโ models/ # Sequelize ๋ชจ๋ธ (32๊ฐ)
โ โโโ routes/ # API ๋ผ์ฐํธ (20๊ฐ)
โ โโโ services/ # ๋น์ฆ๋์ค ๋ก์ง ๋ ์ด์ด (19๊ฐ)
โ โโโ utils/ # ์ ํธ๋ฆฌํฐ ํจ์ (7๊ฐ)
โ โโโ validations/ # ์์ฒญ ์ ํจ์ฑ ๊ฒ์ฆ ์คํค๋ง
โ โโโ templates/ # ์ด๋ฉ์ผ ํ
ํ๋ฆฟ
โ โโโ migrations/ # DB ๋ง์ด๊ทธ๋ ์ด์
(13๊ฐ)
โ โโโ app.js # Express ์ฑ ์ค์
โโโ tests/ # ํ
์คํธ ํ์ผ
โโโ scripts/ # ์ ํธ๋ฆฌํฐ ์คํฌ๋ฆฝํธ
โโโ docs/ # ๋ฌธ์
โโโ index.js # ์ ํ๋ฆฌ์ผ์ด์
์ง์
์
โโโ package.json # ์์กด์ฑ ๋ฐ ์คํฌ๋ฆฝํธ
- ๊ฐ๋ฐ ํ๊ฒฝ:
http://localhost:8080 - ํ๋ก๋์
:
https://teamitakabackend.onrender.com
POST /api/auth/register # ํ์๊ฐ์
POST /api/auth/login # ๋ก๊ทธ์ธ
GET /api/auth/me # ํ์ฌ ์ฌ์ฉ์ ์ ๋ณด ์กฐํ
POST /api/auth/logout # ๋ก๊ทธ์์
POST /api/auth/send-verification # ์ด๋ฉ์ผ ์ธ์ฆ ์ฝ๋ ์ ์ก
POST /api/auth/verify-code # ์ด๋ฉ์ผ ์ธ์ฆ ์ฝ๋ ํ์ธ
GET /api/auth/google # ๊ตฌ๊ธ OAuth ๋ก๊ทธ์ธ
POST /api/auth/phone/verify # Firebase ์ ํ๋ฒํธ ์ธ์ฆ
POST /api/sms/send # SMS ์ธ์ฆ ์ฝ๋ ๋ฐ์ก
POST /api/sms/verify # SMS ์ธ์ฆ ์ฝ๋ ํ์ธ
GET /api/sms/status/:sessionId # ์ธ์ฆ ์ํ ์กฐํ
POST /api/users/mbti # MBTI ๊ฒฐ๊ณผ ์ ์ฅ
GET /api/users/:id # ์ฌ์ฉ์ ํ๋กํ ์กฐํ
PUT /api/users/:id # ์ฌ์ฉ์ ํ๋กํ ์์
DELETE /api/users/:id # ์ฌ์ฉ์ ๊ณ์ ์ญ์
GET /api/projects # ์ ์ฒด ํ๋ก์ ํธ ๋ชฉ๋ก
GET /api/projects/mine # ๋ด ํ๋ก์ ํธ ์กฐํ
GET /api/projects/:id # ํ๋ก์ ํธ ์์ธ ์กฐํ
POST /api/projects # ์ ํ๋ก์ ํธ ์์ฑ
POST /api/projects/from-recruitment/:id # ๋ชจ์ง๊ธ์์ ํ๋ก์ ํธ ํฅ์คํ
PUT /api/projects/:id # ํ๋ก์ ํธ ์์
DELETE /api/projects/:id # ํ๋ก์ ํธ ์ญ์
POST /api/projects/:id/favorite # ํ๋ก์ ํธ ์ฆ๊ฒจ์ฐพ๊ธฐ ํ ๊ธ
/api/projects/mine ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ:
| ํ๋ผ๋ฏธํฐ | ํ์ | ์ค๋ช |
|---|---|---|
status |
string | ongoing/active โ ์งํ ์ค, completed โ ์๋ฃ |
evaluation_status |
string | PENDING, COMPLETED, NOT_REQUIRED |
limit |
number | ์กฐํ ๊ฐ์ ์ ํ |
offset |
number | ํ์ด์ง๋ค์ด์ ์คํ์ |
GET /api/recruitments # ์ ์ฒด ๋ชจ์ง๊ณต๊ณ ๋ชฉ๋ก (์ง์์ ์, ํค์๋ ํฌํจ)
GET /api/recruitments/mine # ๋ด ๋ชจ์ง๊ณต๊ณ ์กฐํ (ํค์๋ ํฌํจ)
GET /api/recruitments/:id # ๋ชจ์ง๊ณต๊ณ ์์ธ ์กฐํ (์์ฑ์ ID, ์ง์์ ์, ํค์๋ ํฌํจ)
POST /api/recruitments # ๋ชจ์ง๊ณต๊ณ ์์ฑ (ํค์๋ ๋ฐฐ์ด ์ง์)
PUT /api/recruitments/:id # ๋ชจ์ง๊ณต๊ณ ์์ (ํค์๋ ์
๋ฐ์ดํธ ์ง์)
DELETE /api/recruitments/:id # ๋ชจ์ง๊ณต๊ณ ์ญ์
์๋ต ํ์ ์์ (Hashtags ํ๋):
{
"recruitment_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ๋ชจ์ง",
"status": "OPEN",
"applicationCount": 5,
"Hashtags": [
{ "name": "React" },
{ "name": "TypeScript" },
{ "name": "Next.js" }
]
}
โ ๏ธ ์ฃผ์:Hashtagsํ๋๋ ๋๋ฌธ์ H๋ก ์์ํฉ๋๋ค (Sequelize ์๋ ์์ฑ). ํค์๋๊ฐ ์๋ ๋ชจ์ง๊ธ์null๋๋[]์ ๋ฐํํ ์ ์์ต๋๋ค.
POST /api/applications/:recruitment_id # ์ง์์ ์ ์ถ (์๊ธฐ์๊ฐ + ํฌํธํด๋ฆฌ์ค)
GET /api/applications/:recruitment_id # ์ง์์ ๋ชฉ๋ก (ํฌํธํด๋ฆฌ์ค ํฌํจ)
POST /api/applications/:application_id/approve # ์ง์ ์น์ธ
POST /api/applications/:application_id/reject # ์ง์ ๊ฑฐ์
GET /api/applications/:recruitment_id/count # ์ง์์ ์ ์กฐํ
POST /api/upload/recruitment-image # ๋ชจ์ง๊ณต๊ณ ์ด๋ฏธ์ง ์
๋ก๋ (JWT ํ์)
POST /api/upload/profile-image # ํ๋กํ ์ด๋ฏธ์ง ์
๋ก๋ (JWT ํ์)
GET /api/comments/:projectId # ํ๋ก์ ํธ ๋๊ธ ์กฐํ
POST /api/comments # ๋๊ธ ์์ฑ
PUT /api/comments/:id # ๋๊ธ ์์
DELETE /api/comments/:id # ๋๊ธ ์ญ์
GET /api/profile/me # ๋ด ํ๋กํ ์กฐํ
PUT /api/profile # ํ๋กํ ์์
GET /api/profile/detail # ํ๋กํ ์์ธ ์กฐํ
GET /api/profile/verification # ์ธ์ฆ ์ํ ์กฐํ
POST /api/reviews # ํ์ ํ๊ฐ ์์ฑ
GET /api/reviews/user/:user_id # ์ฌ์ฉ์๊ฐ ๋ฐ์ ํ๊ฐ ์กฐํ
GET /api/reviews/project/:project_id # ํ๋ก์ ํธ ํ๊ฐ ๋ชฉ๋ก
GET /api/reviews/project/:project_id/summary # ํ๋ก์ ํธ ํ๊ฐ ์์ฝ
DELETE /api/reviews/:review_id # ํ๊ฐ ์ญ์
GET /api/scraps/recruitments # ์คํฌ๋ฉํ ๋ชจ์ง๊ณต๊ณ ๋ชฉ๋ก
PUT /api/scraps/recruitment/:id/scrap # ์คํฌ๋ฉ ํ ๊ธ (์ถ๊ฐ/์ ๊ฑฐ)
POST /api/schedule/create # ์ผ์ ์์ฑ
GET /api/schedule/project/:project_id # ํ๋ก์ ํธ๋ณ ์ผ์ ์กฐํ
PUT /api/schedule/:schedule_id # ์ผ์ ์์
DELETE /api/schedule/:schedule_id # ์ผ์ ์ญ์
POST /api/vote/create # ํฌํ ์์ฑ
GET /api/vote/project/:project_id # ํ๋ก์ ํธ๋ณ ํฌํ ์กฐํ
GET /api/vote/:voteId # ํฌํ ์์ธ ์กฐํ
POST /api/vote/:voteId/submit # ํฌํ ์ ์ถ
โ ๏ธ ์ฐธ๊ณ : Schedule, Vote API๋ ์ปจํธ๋กค๋ฌ/์๋น์ค๊ฐ ๊ตฌํ๋์ด ์์ผ๋app.js์ ๋ผ์ฐํธ ๋ฑ๋ก์ด ํ์ํฉ๋๋ค.
POST /api/:project_id/posts # ๊ฒ์๋ฌผ ์์ฑ
GET /api/:project_id/posts # ๊ฒ์๋ฌผ ๋ชฉ๋ก ์กฐํ
GET /api/posts/:post_id # ๊ฒ์๋ฌผ ์์ธ ์กฐํ
POST /api/recruitment/draft # ๋ชจ์ง๊ณต๊ณ ์์์ ์ฅ
GET /api/search/projects # ํ๋ก์ ํธ ๊ฒ์
GET /api/search/users # ์ฌ์ฉ์ ๊ฒ์
GET /api/dashboard/summary # ๋์๋ณด๋ ์์ฝ ์ ๋ณด
GET /api/admin/users # ์ ์ฒด ์ฌ์ฉ์ ๋ชฉ๋ก
PUT /api/admin/users/:id/role # ์ฌ์ฉ์ ์ญํ ์์
DELETE /api/admin/users/:id # ์ฌ์ฉ์ ์ญ์ (๊ด๋ฆฌ์)
GET /api/health # ์๋ฒ ์ํ ํ์ธ
์์ธํ API ๋ฌธ์๋ API_DOCS.md๋ฅผ ์ฐธ๊ณ ํ์ธ์.
- MySQL 8.0+ (๋ก์ปฌ ๊ฐ๋ฐ)
- PostgreSQL 14+ (Supabase ํ๋ก๋์ )
์คํ 26๊ฐ ํ ์ด๋ธ ์ค 19๊ฐ ๊ตฌํ + ์ถ๊ฐ 13๊ฐ ๋ชจ๋ธ
| ๋ชจ๋ธ | ์ค๋ช | ์คํ ๋งคํ |
|---|---|---|
| User | ์ฌ์ฉ์ ๊ณ์ ๋ฐ ํ๋กํ | users |
| Project | ํ๋ก์ ํธ ์ ๋ณด | projects |
| ProjectMembers | ํ๋ก์ ํธ ํ ๋ฉค๋ฒ | project_members |
| Recruitment | ๋ชจ์ง ๊ณต๊ณ (์ด๋ฏธ์ง, ํด์ํ๊ทธ ์ง์) | recruitment_posts |
| Application | ์ง์์ (์๊ธฐ์๊ฐ + ํฌํธํด๋ฆฌ์ค) | applications |
| ApplicationPortfolio | ์ง์์-ํฌํธํด๋ฆฌ์ค ์ฐ๊ฒฐ (M:N) | application_projects |
| ๋ชจ๋ธ | ์ค๋ช | ์คํ ๋งคํ |
|---|---|---|
| Hashtag | ํด์ํ๊ทธ ๊ด๋ฆฌ | recruitment_hashtags |
| Comment | ํ๋ก์ ํธ ๋๊ธ | - (์ถ๊ฐ) |
| Review | ํ์ ํ๊ฐ ์์คํ | peer_evaluations |
| Notification | ์ฌ์ฉ์ ์๋ฆผ | notifications |
| Scrap | ๋ถ๋งํฌ | bookmarks |
| Todo | ํ ์ผ ๊ด๋ฆฌ | todos |
| ๋ชจ๋ธ | ์ค๋ช | ์คํ ๋งคํ |
|---|---|---|
| Vote | ํฌํ ์์ฑ | votes |
| VoteOption | ํฌํ ์ ํ์ง | vote_options |
| VoteResponse | ํฌํ ์๋ต | vote_responses |
| ๋ชจ๋ธ | ์ค๋ช | ์คํ ๋งคํ |
|---|---|---|
| Schedule | ์ผ์ ๊ด๋ฆฌ | calendar_events |
| Timeline | ํ์๋ผ์ธ ์ด๋ฒคํธ | - (์ถ๊ฐ) |
| ProjectPost | ํ๋ก์ ํธ ํผ๋ | project_feeds |
| ๋ชจ๋ธ | ์ค๋ช | ์คํ ๋งคํ |
|---|---|---|
| University | ๋ํ ์ ๋ณด | universities |
| College | ๋จ๊ณผ๋ ์ ๋ณด | - (์ถ๊ฐ) |
| Department | ํ๊ณผ ์ ๋ณด | - (์ถ๊ฐ) |
| EmailVerification | ์ด๋ฉ์ผ ์ธ์ฆ | verification_codes |
| VerifiedEmail | ์ธ์ฆ ์๋ฃ ์ด๋ฉ์ผ | - (์ถ๊ฐ) |
| Search | ๊ฒ์ ๊ธฐ๋ก | search_history |
| ๋ชจ๋ธ | ์ค๋ช | ์คํ ๋งคํ |
|---|---|---|
| TeamiType | ํํ ์บ๋ฆญํฐ ์ ํ | - (์ถ๊ฐ) |
| UserTeamiType | ์ฌ์ฉ์-์บ๋ฆญํฐ ๋งคํ | - (์ถ๊ฐ) |
| UserFeedback | ์ฌ์ฉ์ ํผ๋๋ฐฑ | - (์ถ๊ฐ) |
| FeedbackItem | ํผ๋๋ฐฑ ํญ๋ชฉ | - (์ถ๊ฐ) |
| Keyword | ํค์๋ ๊ด๋ฆฌ | - (์ถ๊ฐ) |
| ๋ชจ๋ธ | ์ค๋ช | ์คํ ๋งคํ |
|---|---|---|
| Admin | ๊ด๋ฆฌ์ ๊ณ์ | - (์ถ๊ฐ) |
| RecruitmentView | ์กฐํ์ ์ถ์ | - (์ถ๊ฐ) |
| ์คํ ํ ์ด๋ธ | ์ฉ๋ | ์ฐ์ ์์ |
|---|---|---|
| user_stats | ์ฌ์ฉ์ ํต๊ณ ์ง๊ณ | ์ค |
| user_settings | ์ฌ์ฉ์ ์ค์ | ์ค |
| popular_keywords | ์ธ๊ธฐ ๊ฒ์์ด | ํ |
| project_invitations | QR ์ด๋ | ์ค |
| draft_recruitment_posts | ๋ชจ์ง๊ธ ์์์ ์ฅ | ํ |
| meeting_notes | ํ์๋ก | ์ค |
| team_chat_messages | ํ ์ฑํ | ํ |
# ๋ง์ด๊ทธ๋ ์ด์
์คํ
npm run migrate:dev # ๊ฐ๋ฐ ํ๊ฒฝ
npm run migrate:prod # ํ๋ก๋์
ํ๊ฒฝ
# ๋ง์ด๊ทธ๋ ์ด์
๋กค๋ฐฑ
npm run rollback:dev # ๋ง์ง๋ง ๋ง์ด๊ทธ๋ ์ด์
๋กค๋ฐฑ
npm run undo-migrate:dev # ๋ชจ๋ ๋ง์ด๊ทธ๋ ์ด์
๋กค๋ฐฑ
# ๋ฐ์ดํฐ ์๋ฉ
npm run seed:dev # ๊ฐ๋ฐ ๋ฐ์ดํฐ ์๋ฉ# ๋ชจ๋ ํ
์ด๋ธ์ ํฌํจํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๊ธฐํ
npm run db:init:dev
# ๊ฐ๋จํ ์ด๊ธฐํ
npm run db:init:simple:dev
# ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฆฌ์
npm run db:reset# ๊ฐ๋ฐ
npm run dev # nodemon์ผ๋ก ์์ (ํซ ๋ฆฌ๋ก๋)
npm run dev:supabase # Supabase ์ค์ ์ผ๋ก ์์
# ํ๋ก๋์
npm start # ํ๋ก๋์
์๋ฒ ์์
npm run start:supabase # Supabase ์ค์ ์ผ๋ก ํ๋ก๋์
์์
# ํ
์คํธ
npm test # ๋ชจ๋ ํ
์คํธ ์คํ
npm run test:watch # ์์น ๋ชจ๋๋ก ํ
์คํธ ์คํ
# ์ฝ๋ ํ์ง
npm run lint # ESLint ๋ฐ Prettier ์คํ
# ๋ฐ์ดํฐ๋ฒ ์ด์ค
npm run migrate:dev # ๋ง์ด๊ทธ๋ ์ด์
์คํ
npm run seed:dev # ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฉ
npm run db:init:dev # ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๊ธฐํ
# ๊ฒ์ฆ
npm run verify # ๋ฐฐํฌ ๊ฒ์ฆ
npm run verify:supabase # Supabase ๋ฐฐํฌ ๊ฒ์ฆgit checkout -b feature/๊ธฐ๋ฅ-์ด๋ฆ# ํ์ผ ์์
# ํ
์คํธ ์์ฑnpm testgit add .
git commit -m "feat: ๊ธฐ๋ฅ ์ค๋ช
"git push origin feature/๊ธฐ๋ฅ-์ด๋ฆGitHub์์ Pull Request๋ฅผ ์์ฑํฉ๋๋ค.
์ด ํ๋ก์ ํธ๋ ESLint์ Prettier๋ฅผ ์ฌ์ฉํฉ๋๋ค:
# ๋ฆฐํฐ ์คํ
npm run lint
# ์๋ ์์
npm run lint -- --fix# ๋ชจ๋ ํ
์คํธ ์คํ
npm test
# ์ปค๋ฒ๋ฆฌ์ง์ ํจ๊ป ํ
์คํธ ์คํ
npm test -- --coverage
# ํน์ ํ
์คํธ ํ์ผ ์คํ
npm test -- tests/auth.test.js
# ์์น ๋ชจ๋
npm test -- --watchdescribe('์ธ์ฆ ์ปจํธ๋กค๋ฌ', () => {
it('์ ์ฌ์ฉ์๋ฅผ ๋ฑ๋กํด์ผ ํจ', async () => {
// ํ
์คํธ ๊ตฌํ
});
it('์ ํจํ ์๊ฒฉ ์ฆ๋ช
์ผ๋ก ๋ก๊ทธ์ธํด์ผ ํจ', async () => {
// ํ
์คํธ ๊ตฌํ
});
});์ปค๋ฒ๋ฆฌ์ง ๋ฆฌํฌํธ๋ coverage/ ๋๋ ํ ๋ฆฌ์ ์์ฑ๋ฉ๋๋ค.
์ด ํ๋ก์ ํธ๋ Render์ ๋ฐฐํฌ๋์ด ์์ต๋๋ค:
- Render ๊ณ์ ์์ฑ ๋ฐ ๋ก๊ทธ์ธ
- New + โ Web Service ์ ํ
- GitHub ์ ์ฅ์ ์ฐ๊ฒฐ
- ๋ค์ ์ค์ ์ฌ์ฉ:
- Environment:
Node - Build Command:
npm install - Start Command:
npm start
- Environment:
Render ๋์๋ณด๋์ Environment ํญ์์ ํ์ํ ํ๊ฒฝ ๋ณ์ ์ถ๊ฐ:
NODE_ENV=productionPORT=3001SUPABASE_DB_HOST(Shared Pooler ์ฌ์ฉ)SUPABASE_DB_PORT=5432SENDGRID_API_KEYEMAIL_FROM=noreply@teamitaka.comFIREBASE_PROJECT_ID,FIREBASE_PRIVATE_KEY,FIREBASE_CLIENT_EMAIL(์ ํ๋ฒํธ ์ธ์ฆ)- ๊ธฐํ ํ์ ํ๊ฒฝ ๋ณ์
main๋ธ๋์น์ ํธ์ํ๋ฉด ์๋์ผ๋ก ๋ฐฐํฌ๋ฉ๋๋ค- ๋ฐฐํฌ ๋ก๊ทธ๋ Render ๋์๋ณด๋์์ ํ์ธ ๊ฐ๋ฅ
# ๊ฐ๋ฐ ํ๊ฒฝ
npm run dev
# ํ๋ก๋์
(Render)
npm start# Node ํ๊ฒฝ
NODE_ENV=development # development, production, test
# ์๋ฒ ์ค์
PORT=8080 # ์๋ฒ ํฌํธ
HOST=0.0.0.0 # ์๋ฒ ํธ์คํธ (๋ชจ๋ ์ธํฐํ์ด์ค)
# ๋ฐ์ดํฐ๋ฒ ์ด์ค (MySQL)
DB_HOST=localhost
DB_USER=๋ฐ์ดํฐ๋ฒ ์ด์ค_์ฌ์ฉ์
DB_PASSWORD=๋ฐ์ดํฐ๋ฒ ์ด์ค_๋น๋ฐ๋ฒํธ
DB_NAME=teamitaka
DB_PORT=3306
DB_DIALECT=mysql
# ๋ฐ์ดํฐ๋ฒ ์ด์ค (PostgreSQL/Supabase Direct)
DB_DIALECT=postgres
DB_HOST=db.xxx.supabase.co
DB_USER=postgres
DB_PASSWORD=๋น๋ฐ๋ฒํธ
DB_NAME=postgres
DB_PORT=5432
# Supabase Shared Pooler (ํ๋ก๋์
๊ถ์ฅ)
SUPABASE_DB_HOST=aws-0-region.pooler.supabase.com
SUPABASE_DB_USER=postgres.xxx
SUPABASE_DB_PASSWORD=๋น๋ฐ๋ฒํธ
SUPABASE_DB_NAME=postgres
SUPABASE_DB_PORT=5432
# JWT ์ค์
JWT_SECRET=JWT_์ํฌ๋ฆฟ_ํค
JWT_EXPIRES_IN=7d
JWT_REFRESH_SECRET=๋ฆฌํ๋ ์_์ํฌ๋ฆฟ
JWT_REFRESH_EXPIRES_IN=30d
# ์ด๋ฉ์ผ ์๋น์ค (Resend)
RESEND_API_KEY=re_XXXXXXXXXXXX # Resend API ํค
EMAIL_FROM=noreply@teamitaka.com # ๋๋ฉ์ธ ์ธ์ฆ ํ์
# SMS ์๋น์ค (Solapi)
SOLAPI_API_KEY=Solapi_API_ํค
SOLAPI_API_SECRET=Solapi_์ํฌ๋ฆฟ
SOLAPI_SENDER=๋ฐ์ ๋ฒํธ # ๋ฑ๋ก๋ ๋ฐ์ ๋ฒํธ
# iOS ํธ์ ์๋ฆผ (APNs)
APNS_KEY_ID=ํคID
APNS_TEAM_ID=ํID
APNS_BUNDLE_ID=com.teamitaka.app
# ๊ตฌ๊ธ OAuth
GOOGLE_CLIENT_ID=๊ตฌ๊ธ_ํด๋ผ์ด์ธํธ_ID
GOOGLE_CLIENT_SECRET=๊ตฌ๊ธ_ํด๋ผ์ด์ธํธ_์ํฌ๋ฆฟ
GOOGLE_CALLBACK_URL=http://localhost:8080/api/auth/google/callback
# Firebase Phone Authentication
FIREBASE_PROJECT_ID=your-firebase-project-id
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxxxx@your-project.iam.gserviceaccount.com
FIREBASE_API_KEY=your_web_api_key # Firebase Console โ ํ๋ก์ ํธ ์ค์ โ ์น API ํค
# CORS
CORS_ORIGIN=http://localhost:3000 # ํ๋ก ํธ์๋ URL
CORS_CREDENTIALS=true
# Supabase Storage (์ด๋ฏธ์ง ์
๋ก๋)
SUPABASE_URL=https://xxx.supabase.co
SUPABASE_ANON_KEY=์ต๋ช
_ํค
SUPABASE_STORAGE_BUCKET=๋ฒํท๋ช
# ์ด๋ฏธ์ง ์
๋ก๋์ฉ ๋ฒํท (์: recruitment-images)
SUPABASE_SERVICE_KEY=์๋น์ค_ํค # (์ ํ์ฌํญ).env.development- ๊ฐ๋ฐ ํ๊ฒฝ.env.production- ํ๋ก๋์ ํ๊ฒฝ.env.test- ํ ์คํธ ํ๊ฒฝenv.supabase- Supabase ์ค์
.env ํ์ผ์ ์ ๋ ๋ฒ์ ๊ด๋ฆฌ์ ์ปค๋ฐํ์ง ๋ง์ธ์!
docs/ ๋๋ ํ ๋ฆฌ์์ ์ถ๊ฐ ๋ฌธ์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค:
- API ๋ฌธ์
- API ๋ณ๊ฒฝ์ฌํญ - ํค์๋ ๊ธฐ๋ฅ - ๋ชจ์ง๊ธ ํค์๋ API ํตํฉ ๊ฐ์ด๋
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง
- ๋ฐฐํฌ ๊ฐ์ด๋
- ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ ์ค์
- ์ด๋ฉ์ผ ์ธ์ฆ ๊ตฌํ
- ๊ตฌ๊ธ OAuth ๊ตฌํ
- SendGrid ๋๋ฉ์ธ ์ธ์ฆ ๊ฐ์ด๋
scripts/ ๋๋ ํ ๋ฆฌ์ ์ ํธ๋ฆฌํฐ ์คํฌ๋ฆฝํธ:
- verify_hashtags.sql - ํค์๋ ๋ฐ์ดํฐ ์ ํฉ์ฑ ๊ฒ์ฆ SQL
๊ธฐ์ฌ๋ฅผ ํ์ํฉ๋๋ค! ๋ค์ ๋จ๊ณ๋ฅผ ๋ฐ๋ผ์ฃผ์ธ์:
- ์ ์ฅ์ ํฌํฌํ๊ธฐ
- ๊ธฐ๋ฅ ๋ธ๋์น ์์ฑ (
git checkout -b feature/๋ฉ์ง-๊ธฐ๋ฅ) - ๋ณ๊ฒฝ ์ฌํญ ์ปค๋ฐ (
git commit -m 'feat: ๋ฉ์ง ๊ธฐ๋ฅ ์ถ๊ฐ') - ๋ธ๋์น์ ํธ์ (
git push origin feature/๋ฉ์ง-๊ธฐ๋ฅ) - Pull Request ์ด๊ธฐ
Conventional Commits ๋ช ์ธ๋ฅผ ๋ฐ๋ฆ ๋๋ค:
| ํ์ | ์ค๋ช | ์์ |
|---|---|---|
feat: |
์๋ก์ด ๊ธฐ๋ฅ | feat: ์ฌ์ฉ์ ํ๋กํ ์ถ๊ฐ |
fix: |
๋ฒ๊ทธ ์์ | fix: ๋ก๊ทธ์ธ ์๋ฌ ์์ |
docs: |
๋ฌธ์ ๋ณ๊ฒฝ | docs: README ์
๋ฐ์ดํธ |
style: |
์ฝ๋ ํฌ๋งทํ | style: ๋ค์ฌ์ฐ๊ธฐ ์์ |
refactor: |
์ฝ๋ ๋ฆฌํฉํ ๋ง | refactor: ์ธ์ฆ ๋ก์ง ๊ฐ์ |
test: |
ํ ์คํธ ์ถ๊ฐ/์์ | test: ํ์๊ฐ์
ํ
์คํธ ์ถ๊ฐ |
chore: |
๊ธฐํ ๋ณ๊ฒฝ์ฌํญ | chore: ์์กด์ฑ ์
๋ฐ์ดํธ |
- ๋ชจ๋ Pull Request๋ ๋ฆฌ๋ทฐ๋ฅผ ๊ฑฐ์นฉ๋๋ค
- ํ ์คํธ๋ฅผ ํต๊ณผํด์ผ ํฉ๋๋ค
- ์ฝ๋ ์คํ์ผ ๊ฐ์ด๋๋ฅผ ๋ฐ๋ผ์ผ ํฉ๋๋ค
์ด ํ๋ก์ ํธ๋ ISC ๋ผ์ด์ ์ค๋ฅผ ๋ฐ๋ฆ ๋๋ค.
TeamItaka ๊ฐ๋ฐํ
- ๋ฐฑ์๋ ๊ฐ๋ฐ
- API ์ค๊ณ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ํคํ ์ฒ
- DevOps ๋ฐ ๋ฐฐํฌ
๋ฒ๊ทธ ๋ฆฌํฌํธ์ ๊ธฐ๋ฅ ์์ฒญ์ GitHub Issues๋ฅผ ์ด์ฉํด ์ฃผ์ธ์.
- ๋ฒ๊ทธ ์ค๋ช
- ์ฌํ ๋จ๊ณ
- ์์ ๋์
- ์ค์ ๋์
- ์คํฌ๋ฆฐ์ท (ํ์์)
- ํ๊ฒฝ ์ ๋ณด (OS, Node.js ๋ฒ์ ๋ฑ)
- ๊ธฐ๋ฅ ์ค๋ช
- ์ฌ์ฉ ์ฌ๋ก
- ์์๋๋ ์ด์
- ์ถ๊ฐ ์ปจํ ์คํธ
์ง๋ฌธ์ด๋ ์ง์์ด ํ์ํ์ ๊ฒฝ์ฐ ๊ฐ๋ฐํ์ ๋ฌธ์ํด ์ฃผ์ธ์.
| ํญ๋ชฉ | ์ํ |
|---|---|
| ๋ฒ์ | 1.7.1 |
| ๋ง์ง๋ง ์ ๋ฐ์ดํธ | 2026-01-31 |
| ์ ์ง๋ณด์ | ํ๋ฐํ ์งํ ์ค |
| ๋ฌธ์ํ | ์๋ฃ |
| ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง | ์งํ ์ค |
| Swagger ๋ฌธ์ | API Docs |
๋ง์ง๋ง ๋ถ์: 2026-01-31 | ์ ์ฒด ์๋ฃ์จ: 97%
| ๊ตฌ๋ถ | ์๋ฃ | ์งํ์ค | ๋ฏธ๊ตฌํ | ์๋ฃ์จ |
|---|---|---|---|---|
| API ์๋ํฌ์ธํธ | 90๊ฐ | 2๊ฐ | 2๊ฐ | 97% |
| ๋ผ์ฐํธ ํ์ผ | 20๊ฐ | 2๊ฐ | 0๊ฐ | 91% |
| ์ปจํธ๋กค๋ฌ | 22๊ฐ | 0๊ฐ | 0๊ฐ | 100% |
| ์๋น์ค | 19๊ฐ | 0๊ฐ | 0๊ฐ | 100% |
| ๋ชจ๋ธ | 32๊ฐ | 0๊ฐ | 0๊ฐ | 100% |
| ๊ธฐ๋ฅ | ์ํ | ์๋ํฌ์ธํธ | ๋น๊ณ |
|---|---|---|---|
| ๐ ์ธ์ฆ (Auth) | โ ์๋ฃ | 8๊ฐ | JWT, OAuth, Firebase Phone |
| ๐ฑ SMS ์ธ์ฆ | โ ์๋ฃ | 3๊ฐ | Solapi, sessionId ๊ธฐ๋ฐ |
| ๐ง ์ด๋ฉ์ผ ์ธ์ฆ | โ ์๋ฃ | 2๊ฐ | Resend, Rate Limiting |
| ๐ง MBTI | โ ์๋ฃ | 1๊ฐ | ์ ํ ์ ์ฅ/์กฐํ |
| ๐ฒ ํธ์ ์๋ฆผ | โ ์๋ฃ | - | iOS APNs, ํธ๋ฆฌ๊ฑฐ ๊ธฐ๋ฐ |
| ๐ข ๋ชจ์ง๊ณต๊ณ | โ ์๋ฃ | 8๊ฐ | CRUD, ์คํฌ๋ฉ, ํด์ํ๊ทธ, ์กฐํ์ |
| ๐ ์ง์ ๊ด๋ฆฌ | โ ์๋ฃ | 4๊ฐ | ์ง์, ์น์ธ/๊ฑฐ์ , ํฌํธํด๋ฆฌ์ค |
| ๐ ํ๋ก์ ํธ | โ ์๋ฃ | 11๊ฐ | CRUD, ํฅ์คํ, ํ์ ๊ด๋ฆฌ, ์ฆ๊ฒจ์ฐพ๊ธฐ |
| โ ํ ์ผ (Todo) | โ ์๋ฃ | 4๊ฐ | ํ๋ก์ ํธ๋ณ ํ ์ผ ๊ด๋ฆฌ |
| ๐ ํ์๋ผ์ธ | โ ์๋ฃ | 4๊ฐ | ํ๋ก์ ํธ ํ์๋ผ์ธ |
| ๐ฅ ํ์ ๊ด๋ฆฌ | โ ์๋ฃ | 2๊ฐ | ์ญํ ์กฐํ/์์ |
| โญ ํ๊ฐ (Review) | โ ์๋ฃ | 6๊ฐ | ํ์ ์ํธํ๊ฐ (5๊ฐ ํญ๋ชฉ) |
| ๐ค ํ๋กํ | โ ์๋ฃ | 7๊ฐ | ์กฐํ, ์์ , ์ธ์ฆ ์ํ, ํ์ |
| ๐ฌ ๋๊ธ | โ ์๋ฃ | 2๊ฐ | ๋ชจ์ง๊ณต๊ณ ๋๊ธ |
| ๐ผ๏ธ ์ ๋ก๋ | โ ์๋ฃ | 2๊ฐ | Supabase Storage |
| ๐ ์คํฌ๋ฉ | โ ์๋ฃ | 2๊ฐ | ๋ชจ์ง๊ณต๊ณ ์คํฌ๋ฉ |
| ๐ก๏ธ ๊ด๋ฆฌ์ | โ ์๋ฃ | 3๊ฐ | ์ธ์ฆ ์ฌ์ฉ์ ๊ด๋ฆฌ |
| ๐ ๋์๋ณด๋ | โ ์๋ฃ | 1๊ฐ | ํต๊ณ, ์๋ฆผ, ํ์๋ผ์ธ |
| ๐พ ์์์ ์ฅ | ๐ก ๋ถ๋ถ | 1๊ฐ | ์์ฑ๋ง ๊ตฌํ |
| ๐ ํ๋ก์ ํธ ๊ฒ์๋ฌผ | โ ์๋ฃ | 3๊ฐ | ํ๋ก์ ํธ ํผ๋ |
| ๐ ๊ฒ์ | โ ์๋ฃ | 1๊ฐ | ํตํฉ ๊ฒ์ |
| ๐ณ๏ธ ํฌํ (Vote) | 4๊ฐ | ์ฝ๋ ์์ฑ, app.js ๋ฑ๋ก ํ์ | |
| ๐ ์ผ์ (Schedule) | 4๊ฐ | ์ฝ๋ ์์ฑ, app.js ๋ฑ๋ก ํ์ |
| ์ฐ์ ์์ | ์ด์ | ์ํฅ ๋ฒ์ | ์์ ์๊ฐ |
|---|---|---|---|
| ๐ด ๊ธด๊ธ | Vote ๋ผ์ฐํธ app.js ๋ฏธ๋ฑ๋ก | ํฌํ ๊ธฐ๋ฅ ์ ์ฒด | 5๋ถ |
| ๐ด ๊ธด๊ธ | Schedule ๋ผ์ฐํธ app.js ๋ฏธ๋ฑ๋ก | ์ผ์ ๊ด๋ฆฌ ์ ์ฒด | 5๋ถ |
| ๐ก ๋ณดํต | /api/auth/logout ๋ฏธ๊ตฌํ | ๋ก๊ทธ์์ | 1์๊ฐ |
| ๐ก ๋ณดํต | /api/auth/refresh ๋ฏธ๊ตฌํ | ํ ํฐ ๊ฐฑ์ | 2์๊ฐ |
| ๐ก ๋ณดํต | /api/auth/password/reset ๋ฏธ๊ตฌํ | ๋น๋ฐ๋ฒํธ ์ฌ์ค์ | 4์๊ฐ |
| โ ์๋ฃ | /api/applications/:id/cancel | ์ง์ ์ทจ์ | v1.5.5 |
| โ ์๋ฃ | ์๋ฆผ ์์คํ API | ์๋ฆผ ๊ธฐ๋ฅ | v1.5.5 |
| ๐ข ๋ฎ์ | ํ์๋ก API ๋ฏธ๊ตฌํ | ํ์ ๊ด๋ฆฌ | 1์ผ |
| ๐ข ๋ฎ์ | QR ์ด๋ API ๋ฏธ๊ตฌํ | ํ๋ก์ ํธ ์ด๋ | 4์๊ฐ |
| ๋ ์ด์ด | ํ์ผ ์ | ๊ตฌํ ํํฉ |
|---|---|---|
| Routes | 20๊ฐ | 18๊ฐ ๋ฑ๋ก, 2๊ฐ ๋ฏธ๋ฑ๋ก (vote, schedule) |
| Controllers | 24๊ฐ | ์ ์ฒด ๊ตฌํ ์๋ฃ |
| Services | 21๊ฐ | ์ ์ฒด ๊ตฌํ ์๋ฃ |
| Models | 33๊ฐ | ์ ์ฒด ๊ตฌํ ์๋ฃ (DeviceToken ์ถ๊ฐ) |
| Middlewares | 9๊ฐ | auth, admin, optional, error, validation, upload, rateLimit, uuidValidation ๋ฑ |
| ์ํ | ์ค๋ช |
|---|---|
| โ ์๋ฃ | ๊ธฐ๋ฅ ์์ ๊ตฌํ ๋ฐ ๋์ ํ์ธ |
| ๐ก ๋ถ๋ถ | ๊ธฐ๋ณธ ๊ธฐ๋ฅ ๊ตฌํ, ์ผ๋ถ ๊ธฐ๋ฅ ๋ฏธ์์ฑ |
| ์ฝ๋ ์์ฑ๋์์ผ๋ ๋ผ์ฐํธ ๋ฏธ๋ฑ๋ก | |
| โ ๋ฏธ๊ตฌํ | ๊ตฌํ ํ์ |
| ๐ด ๊ธด๊ธ | ์ฆ์ ํด๊ฒฐ ํ์ (5๋ถ ์ด๋ด) |
| ๐ก ๋ณดํต | ๋จ๊ธฐ ํด๊ฒฐ (1-2์ผ) |
| ๐ข ๋ฎ์ | ์ค๊ธฐ ํด๊ฒฐ (1์ฃผ) |
- ๐ ํ๋ก์ ํธ ์ ๋ชฉ 15์ ์ ํ ์ถ๊ฐ
POST /api/projects- ํ๋ก์ ํธ ์์ฑ ์ ์ ๋ชฉ 15์ ์ ํPUT /api/projects/:id- ํ๋ก์ ํธ ์์ ์ ์ ๋ชฉ 15์ ์ ํPOST /api/projects/from-recruitment/:id- ํฅ์คํ ์ ์ ๋ชฉ 15์ ์ ํ
- โญ ํ๊ฐ API ๋ด๋น ์
๋ฌด ํ๋ ์ถ๊ฐ
GET /api/evaluations/given์๋ต์target_member_taskํ๋ ์ถ๊ฐ- ํ๊ฐ ๋์์์ ํ๋ก์ ํธ ๋ด ๋ด๋น ์ ๋ฌด ํ์ ์ง์
- ๐ Swagger ๋ฌธ์ ์
๋ฐ์ดํธ
- ํ๋ก์ ํธ ์ ๋ชฉ 15์ ์ ํ ๋ช ์
- ํ๊ฐ API ์๋ต ์คํค๋ง ์์ธํ
- ๐ฑ SMS ์ธ์ฆ ์์คํ
๊ตฌํ
- Solapi ํตํฉ์ผ๋ก SMS ๋ฐ์ก
- sessionId ๊ธฐ๋ฐ ๋น๋๊ธฐ ์ธ์ฆ ํ๋ก์ฐ
- node-cache ๊ธฐ๋ฐ ์ธ์ฆ ์ฝ๋ ๊ด๋ฆฌ
POST /api/sms/send,POST /api/sms/verifyAPI ์ถ๊ฐ
- ๐ง MBTI ์ ํ ์์คํ
- MBTI ๊ฒฐ๊ณผ ์ ์ฅ API ์ถ๊ฐ (
POST /api/users/mbti) /api/auth/me์๋ต์mbtiTypeํ๋ ์ถ๊ฐ
- MBTI ๊ฒฐ๊ณผ ์ ์ฅ API ์ถ๊ฐ (
- ๐ง ์ด๋ฉ์ผ ์๋น์ค ๋ณ๊ฒฝ
- SendGrid โ Resend๋ก ๋ง์ด๊ทธ๋ ์ด์
- TimiTaka ๋ธ๋๋ฉ ์ด๋ฉ์ผ ํ ํ๋ฆฟ
- ๋ง์ค์ฝํธ ์บ๋ฆญํฐ ์ด๋ฏธ์ง ์ถ๊ฐ
- ๐ฒ iOS ํธ์ ์๋ฆผ
- APNs (Apple Push Notification service) ์ง์
- ์ด๋/์ํ๋ณ๊ฒฝ/๋ฆฌ๋ทฐ ํธ์ ํธ๋ฆฌ๊ฑฐ
- DeviceToken ๋ชจ๋ธ๋ก ํ ํฐ ๊ด๋ฆฌ
- โญ ํ๋ก์ ํธ ์ฆ๊ฒจ์ฐพ๊ธฐ API ์ถ๊ฐ (
POST /api/projects/:id/favorite) - ๐ ๋น๋ฐ๋ฒํธ ๊ฒ์ฆ ๊ท์น ๊ฐ์ํ
- ๊ธฐ์กด: ๋๋ฌธ์, ์๋ฌธ์, ์ซ์, ํน์๋ฌธ์ ๊ฐ๊ฐ ํ์, 8~15์
- ๋ณ๊ฒฝ: ์๋ฌธ + ์ซ์ ํฌํจ, 8์ ์ด์์ด๋ฉด ๋ชจ๋ ๋ฌธ์ ํ์ฉ
- ๐ก๏ธ UUID ๊ฒ์ฆ ๋ฏธ๋ค์จ์ด ์ถ๊ฐ (API ๋ผ์ฐํธ ๋ณด์ ๊ฐํ)
- ๐ IPv6 ์ฐ๊ฒฐ ์ด์ ์์ (Render IPv4 ๊ฐ์ )
- ๐ Swagger ๋ฌธ์์ ๊ฒ์ฆ ์คํค๋ง ๋๊ธฐํ
- โจ ํ์๋ก API ์ ์ฒด ๊ตฌํ (5๊ฐ ์๋ํฌ์ธํธ)
GET /api/projects/:id/meetings- ํ์๋ก ๋ชฉ๋ก ์กฐํ (ํผ๋์ฉ)GET /api/projects/:id/meetings/:meeting_id- ํ์๋ก ์์ธ ์กฐํPOST /api/projects/:id/meetings- ํ์๋ก ์์ฑPUT /api/projects/:id/meetings/:meeting_id- ํ์๋ก ์์ DELETE /api/projects/:id/meetings/:meeting_id- ํ์๋ก ์ญ์
- ๐ฆ MeetingNotes ๋ชจ๋ธ ์ถ๊ฐ
- ํ๋: meeting_id, project_id, created_by, title, content, meeting_date
- ๊ด๊ณ: Project, User (Creator) ์ฐ๊ฒฐ
- ๐ Swagger ๋ฌธ์ ์
๋ฐ์ดํธ
- MeetingNotes ์คํค๋ง ๋ฐ ์๋ํฌ์ธํธ ๋ฌธ์ํ
- ํผ๋ ๊ธฐ๋ฅ๊ณผ์ ์ฐ๊ณ ์ค๋ช ์ถ๊ฐ
- โจ Notifications API ์ ์ฒด ๊ตฌํ (5๊ฐ ์๋ํฌ์ธํธ)
GET /api/notifications- ์๋ฆผ ๋ชฉ๋ก ์กฐํ (ํ์ด์ง๋ค์ด์ , ๋ฏธ์ฝ์ ํํฐ)GET /api/notifications/unread-count- ์ฝ์ง ์์ ์๋ฆผ ๊ฐ์PUT /api/notifications/:id/read- ๊ฐ๋ณ ์๋ฆผ ์ฝ์ ์ฒ๋ฆฌPUT /api/notifications/read-all- ๋ชจ๋ ์๋ฆผ ์ฝ์ ์ฒ๋ฆฌDELETE /api/notifications/:id- ์๋ฆผ ์ญ์
- โจ Application Cancel API ๊ตฌํ (3๊ฐ ์๋ํฌ์ธํธ)
POST /api/applications/:id/cancel- ์ง์ ์ทจ์DELETE /api/applications/:id- ์ง์ ์ทจ์ (DELETE ๋ฐฉ์)GET /api/applications/mine- ๋ด ์ง์ ๋ชฉ๋ก ์กฐํ
- ๐ Notification ๋ชจ๋ธ PostgreSQL snake_case ๋งคํ ์์
isReadโis_read์ปฌ๋ผ ๋งคํ ๋ฒ๊ทธ ์์ underscored: true์ต์ ์ถ๊ฐ- ํ์์คํฌํ ํ๋ ๋ช
์์ ๋งคํ (
created_at,updated_at)
- ๐๏ธ Application ์๋น์ค ๊ฐ์
- ์ง์ ์ทจ์ ์ ํฌํธํด๋ฆฌ์ค ์ฐ๊ฒฐ ์๋ ์ญ์
- ์ทจ์ ํ ๋ชจ์ง๊ณต๊ณ ์ํ ์๋ ์ฌํ์ฑํ (CLOSED โ ACTIVE)
- ํธ๋์ญ์ ์ฒ๋ฆฌ๋ก ๋ฐ์ดํฐ ์ผ๊ด์ฑ ๋ณด์ฅ
- โจ ๋ด ํ๋ก์ ํธ API ์ต์ ํผ๋ ์๊ฐ ์ถ๊ฐ (
GET /api/projects/mine)- ์๋ต์
last_feed_atํ๋ ์ถ๊ฐ (ISO 8601 timestamp ๋๋ null) - ํ๋ก์ ํธ ์นด๋์ '2์๊ฐ ์ ' ํํ์ ์๋ ์๊ฐ ํ์ ์ง์
project_postsํ ์ด๋ธ์ ์ต์ ๊ฒ์๋ฌผ ์๊ฐ ๊ธฐ์ค
- ์๋ต์
- ๐ ๋ด ๋ชจ์ง๊ณต๊ณ ๋ชฉ๋ก CLOSED ์ํ ํํฐ๋ง ๋ฒ๊ทธ ์์
- ํ๋ก์ ํธ ํฅ์คํ ํ '๋ชจ์ง์ค' ํญ์์ ์ฌ๋ผ์ง๋๋ก ์์
- ACTIVE ์ํ์ ๋ชจ์ง๊ณต๊ณ ๋ง ์กฐํ๋๋๋ก ์ฟผ๋ฆฌ ์์
- โจ ํ๋ก์ ํธ ํฅ์คํ API ๊ฐ์ (
POST /api/projects/from-recruitment/:id)- ํ๋ก ํธ์๋์์ ์ง์ ํ๋ก์ ํธ ์ ๋ชฉ ์ ๋ ฅ
- ๋ค์ง(resolution) ํ๋ ์ถ๊ฐ
- ์น์ธ๋ ์ง์์ ์ค ํ์ ์ ํ ๊ธฐ๋ฅ (
memberUserIds๋ฐฐ์ด) - Recruitment์์
project_type์๋ ๋ณต์ฌ (course/side)
- ๐๏ธ Project ๋ชจ๋ธ ์คํค๋ง ํ์ฅ
resolution: ํ๋ก์ ํธ ๋ค์ง (TEXT)project_type: ํ๋ก์ ํธ ์ ํ (ENUM: 'course', 'side')
- ๐ ๋ฒ๊ทธ ์์
- Application status ๊ฒ์ ๋ฒ๊ทธ ์์ (ACCEPTED โ memberUserIds ์ง์ ์ ๋ฌ)
- ๐ Application API Sequelize alias ๋ฒ๊ทธ ์์
as: "ProjectMembers"๋๋ฝ์ผ๋ก ์ธํ 500 ์๋ฌ ํด๊ฒฐ- ํฌํธํด๋ฆฌ์ค ํ๋ก์ ํธ ๊ฒ์ฆ ๋ก์ง ์ ์ํ
- ๐ Swagger API ๋ฌธ์ ๋๊ท๋ชจ ์
๋ฐ์ดํธ
- Profile API 4๊ฐ ์๋ํฌ์ธํธ ์ถ๊ฐ (
/me,/detail,/verification,PUT /) - Vote API 4๊ฐ ์๋ํฌ์ธํธ ๋ฌธ์ํ
- Schedule API 4๊ฐ ์๋ํฌ์ธํธ ๋ฌธ์ํ
- Upload API
/profile-image์๋ํฌ์ธํธ ์ถ๊ฐ /projects/mine์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ์์ธ ๋ฌธ์ํ (status, evaluation_status, limit, offset)/applications/{recruitment_id}portfolio_project_ids ํ๋ผ๋ฏธํฐ ๋ฌธ์ํ
- Profile API 4๊ฐ ์๋ํฌ์ธํธ ์ถ๊ฐ (
- ๐ง API ๋ฌธ์ ํ์ง ๊ฐ์
- ์๋ต ์คํค๋ง ์์ธํ
- ์๋ฌ ์ฝ๋ ๋ฐ ๋ฉ์์ง ๋ฌธ์ํ
- ๐ง Sequelize ๋ชจ๋ธ PostgreSQL ํธํ์ฑ ๊ฐ์
- 12๊ฐ ๋ชจ๋ธ์
tableName์์ฑ ์ถ๊ฐ (PostgreSQL snake_case ํ ์ด๋ธ๋ช ๋งคํ) - ์ํฅ๋ฐ๋ ๋ชจ๋ธ: User, Project, ProjectMembers, Recruitment, Application, Review, Comment, Todo, Timeline, Vote, Schedule, Notification ๋ฑ
- 12๊ฐ ๋ชจ๋ธ์
- ๐ ๋ชจ๋ธ ์คํค๋ง ๋ฒ๊ทธ ์์
- ProjectMembers ๋ชจ๋ธ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง์ ์ผ์นํ๋๋ก ์์
- Todo ๋ชจ๋ธ: ์ปฌ๋ผ ์ ์๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง์ ๋ง๊ฒ ์์
- Project ๋ชจ๋ธ: ์กด์ฌํ์ง ์๋
role์ปฌ๋ผ ์ ๊ฑฐ
- โก ์ฟผ๋ฆฌ ์ต์ ํ
- ProjectMembers include์์ ๋ถํ์ํ attributes ์ ์ฝ ์ ๊ฑฐ
- ๐ฑ Firebase ์ ํ๋ฒํธ ์ธ์ฆ ๊ตฌํ
- Firebase Admin SDK ํตํฉ (firebase-admin@^12.0.0)
- ์ ํ๋ฒํธ ๊ธฐ๋ฐ ์ฌ์ฉ์ ์ธ์ฆ API ์ถ๊ฐ (
POST /api/auth/phone/verify) - Users ํ
์ด๋ธ ์คํค๋ง ํ์ฅ:
firebase_phone_uid: Firebase Phone Auth UIDphone_number: E.164 ํ์ ์ ํ๋ฒํธ ์ ์ฅphone_verified: ์ ํ๋ฒํธ ์ธ์ฆ ์๋ฃ ์ฌ๋ถphone_verified_at: ์ธ์ฆ ์๋ฃ ์๊ฐ
- ์ ๊ท ์ฌ์ฉ์ ์๋ ์์ฑ ๋ฐ JWT ํ ํฐ ๋ฐ๊ธ
- ๊ธฐ์กด ์ฌ์ฉ์ ์ ํ๋ฒํธ ์ ๋ฐ์ดํธ ์ง์
requirePhoneVerified๋ฏธ๋ค์จ์ด ์ถ๊ฐ (์ ํ๋ฒํธ ์ธ์ฆ ํ์ ๋ผ์ฐํธ์ฉ)- ๋ก์ปฌ(MySQL) ๋ฐ ํ๋ก๋์ (Supabase PostgreSQL) ๋ง์ด๊ทธ๋ ์ด์ ์๋ฃ
- ์ ํ๋ฒํธ ์ธ์ฆ ํ ์คํธ ์คํฌ๋ฆฝํธ ํฌํจ (test-phone-auth.js)
- ๐ User ๋ชจ๋ธ ๋ฒ๊ทธ ์์ ๋ฐ ํ๊ฒฝ๋ณ ํธํ์ฑ ๊ฐ์
- ์ค๋ณต๋
user_typeํ๋ ์ ๊ฑฐ (role ํ๋์ ์ค๋ณต, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปฌ๋ผ ์ค๋ฅ ํด๊ฒฐ) - ํ๊ฒฝ๋ณ ํ์์คํฌํ ์ปฌ๋ผ๋ช
์ค์ ์ถ๊ฐ:
- Local (MySQL): camelCase (
createdAt,updatedAt) - Production (PostgreSQL): snake_case (
created_at,updated_at) NODE_ENV์ ๋ฐ๋ฅธ ์๋ ์ ํ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธํ์ฑ ๋ณด์ฅ
- Local (MySQL): camelCase (
- ์กด์ฌํ์ง ์๋
experience_yearsํ๋ ์ ๊ฑฐ
- ์ค๋ณต๋
- ๐ง Recruitment Status ํต์ผ (Database ํธํ์ฑ ํด๊ฒฐ)
- Local๊ณผ Production DB ๊ฐ status ๊ฐ ๋ถ์ผ์น ํด๊ฒฐ:
- Local (MySQL): ENUM('OPEN', 'CLOSED') โ ENUM('ACTIVE', 'CLOSED', 'FILLED')
- Production (PostgreSQL): CHECK('ACTIVE', 'CLOSED', 'FILLED') - ๊ธฐ์กด ์ ์ง
- ์ฝ๋ ์ ์ฒด ํต์ผ: 'OPEN' โ 'ACTIVE' ๋ณ๊ฒฝ
- Recruitment ๋ชจ๋ธ ENUM ์ ์ ์์
- recruitmentService, applicationService, loadMockupData ์ ๋ฐ์ดํธ
- Local MySQL ๋ง์ด๊ทธ๋ ์ด์
์์ฑ ๋ฐ ์คํ ์๋ฃ
- ๊ธฐ์กด 22๊ฐ ๋ ์ฝ๋ ์๋ ๋ง์ด๊ทธ๋ ์ด์ (OPEN โ ACTIVE)
- Status ๊ฐ ์ ์:
- ACTIVE: ๋ชจ์ง์ค (๊ธฐ์กด OPEN)
- CLOSED: ๋ชจ์ง๋ง๊ฐ
- FILLED: ๋ชจ์ง์๋ฃ (Production ๊ธฐ์กด ๋ฐ์ดํฐ)
- Local๊ณผ Production DB ๊ฐ status ๊ฐ ๋ถ์ผ์น ํด๊ฒฐ:
- ๐ ๋ฌธ์ํ
- Firebase ์ ํ๋ฒํธ ์ธ์ฆ ๊ฐ์ด๋ ์ถ๊ฐ
- ํ๊ฒฝ ๋ณ์ ์ค์ ๊ฐ์ด๋ ์ ๋ฐ์ดํธ
- API ์๋ํฌ์ธํธ ๋ฌธ์ ์ ๋ฐ์ดํธ
- ๐ท๏ธ ๋ชจ์ง๊ธ ํค์๋ ๊ธฐ๋ฅ ๊ฐ์
GET /api/recruitments์๋ต์ Hashtags ํ๋ ์ถ๊ฐ- ๋ชจ์ง๊ธ ๋ชฉ๋ก ์กฐํ ์ ํค์๋ ์ ๋ณด ํฌํจ
- ํค์๋๊ฐ ์๋ ๋ชจ์ง๊ธ ์ฒ๋ฆฌ ๊ฐ์ (null ๋๋ ๋น ๋ฐฐ์ด)
- ํ๋ก ํธ์๋ ํตํฉ ๊ฐ์ด๋ ์์ฑ (docs/api_changes_hashtags.md)
- ๐ ๋ฒ๊ทธ ์์
- ๋ชจ์ง๊ธ ์์ฑ ์ status ๊ฐ ํต์ผ (ACTIVE โ OPEN)
- Sequelize ๋ชจ๋ธ ENUM๊ณผ ์ผ์นํ๋๋ก ์์
- ๐ ๋ฌธ์ํ
- SQL ๊ฒ์ฆ ์คํฌ๋ฆฝํธ ์ถ๊ฐ (scripts/verify_hashtags.sql)
- ํ๋ก ํธ์๋ API ๋ณ๊ฒฝ์ฌํญ ๋ฌธ์ ์์ฑ
- React/Vue ์ปดํฌ๋ํธ ์์ ์ฝ๋ ์ ๊ณต
- TypeScript ํ์ ์ ์ ๊ฐ์ด๋ ํฌํจ
- ๐
์ผ์ ๊ด๋ฆฌ ๊ธฐ๋ฅ ์ถ๊ฐ
- ํ๋ก์ ํธ๋ณ ์ผ์ ์์ฑ, ์กฐํ, ์์ , ์ญ์ API ๊ตฌํ
- ๐ ํ๋ก์ ํธ ๊ฒ์ํ ๊ธฐ๋ฅ ์ถ๊ฐ
- ํ๋ก์ ํธ ๋ด ๊ฒ์๋ฌผ ์์ฑ ๋ฐ ์กฐํ ๊ธฐ๋ฅ ๊ตฌํ
- ๐พ ๋ชจ์ง๊ณต๊ณ ์์์ ์ฅ ๊ธฐ๋ฅ
- ์์ฑ ์ค์ธ ๋ชจ์ง๊ณต๊ณ ์์์ ์ฅ API ์ถ๊ฐ
- ๐ฏ ๋ชจ์ง๊ณต๊ณ ์์ธ ์กฐํ API ๊ฐ์ (
GET /api/recruitments/:id)user_idํ๋ ์ถ๊ฐ: ๋ชจ์ง๊ธ ์์ฑ์ ID ๋ฐํ (ํ๋ก ํธ์๋ ์์ ์ ํ์ธ์ฉ)applicant_countํ๋ ์ถ๊ฐ: ์ค์๊ฐ ์ง์์ ์ ๊ณ์ฐ (์๋ธ์ฟผ๋ฆฌ)created_atํ๋ ํฌํจ: ๋ชจ์ง๊ธ ์์ฑ ์๊ฐ- ํ๋ก ํธ์๋ ์กฐ๊ฑด๋ถ ๋ ๋๋ง ์ง์ (์์ฑ์: "์ง์์ ๋ณด๊ธฐ", ์ผ๋ฐ ์ฌ์ฉ์: "์ง์ํ๊ธฐ")
- ๐ Hashtag ๋ชจ๋ธ ๋ฒ๊ทธ ์์
- ํด์ํ๊ทธ attributes ์์ :
contentโname - ๋ชจ์ง๊ณต๊ณ ์์ธ ์กฐํ ์ ํด์ํ๊ทธ ์ ์ ๋ฐํ
- ํด์ํ๊ทธ attributes ์์ :
- ๐ ์ง์์ ์ ์ถ API ํฌํธํด๋ฆฌ์ค ์ฐ๊ฒฐ ๊ธฐ๋ฅ
- ApplicationPortfolio ๋ชจ๋ธ ์ถ๊ฐ (M:N ๊ด๊ณ)
- introduction ํ๋ ์ถ๊ฐ (ํ์, 1-500์)
- ํฌํธํด๋ฆฌ์ค ํ๋ก์ ํธ ์ ํ ๋ฐ ์์ ๊ถ ๊ฒ์ฆ
- ๋ณธ์ธ ๋ชจ์ง๊ธ ์ง์ ๋ฐฉ์ง, ๋ง๊ฐ ์ฌ๋ถ ๊ฒ์ฆ
- ๐ผ๏ธ ๋ชจ์ง๊ณต๊ณ ์ด๋ฏธ์ง ์
๋ก๋ (Supabase Storage)
- photo_url ํ๋ ์ถ๊ฐ
- ์ด๋ฏธ์ง ์
๋ก๋ API (
/api/upload/recruitment-image) - ํ์ผ ๊ฒ์ฆ (jpeg/png/webp, ์ต๋ 5MB)
- UUID ๊ธฐ๋ฐ ํ์ผ๋ช ์์ฑ
- RLS ์ ์ฑ ์ ์ฉ
- ๐ ์ง์์ ์ ์ถ ๋ณด์ ๊ฐํ
- ํธ๋์ญ์ ์ฒ๋ฆฌ๋ก ๋ฐ์ดํฐ ์ผ๊ด์ฑ ๋ณด์ฅ
- ํฌํธํด๋ฆฌ์ค ์์ ๊ถ ๊ฒ์ฆ (ProjectMembers ํ์ธ)
- 9๊ฐ์ง ์๋ฌ ์ฝ๋ ์ฒด๊ณ ๊ตฌํ
- INVALID_INPUT, SELF_APPLICATION, RECRUITMENT_CLOSED
- INVALID_PORTFOLIO, UNAUTHORIZED, RECRUITMENT_NOT_FOUND
- ALREADY_APPLIED
- ๐ ์ง์์ ๋ชฉ๋ก ์กฐํ ๊ฐ์
- ํฌํธํด๋ฆฌ์ค ํ๋ก์ ํธ ์ ๋ณด ํฌํจ (์ ๋ชฉ, ์ค๋ช )
- User ํ๋กํ ์ ๋ณด ํฌํจ
- ๐ฏ ๋์๋ณด๋ ์์ฝ API ๊ตฌํ (
/api/dashboard/summary)- ํ๋ก์ ํธ ํต๊ณ, ์ง์์ ์ถ์ , ํ๊ฐ ๋๊ธฐ ํ๋ก์ ํธ, ์ต๊ทผ ํ๋ ํ์๋ผ์ธ
- ๐ค ํ์ฌ ์ฌ์ฉ์ ์ ๋ณด API ์ถ๊ฐ (
/api/auth/me)- ๋ก๊ทธ์ธ ์๋ต์ user ๊ฐ์ฒด ํฌํจ์ผ๋ก ํ๋ก ํธ์๋ ํตํฉ ๊ฐ์ํ
- ๐ ๋ด ํ๋ก์ ํธ ์กฐํ API ๊ฐ์ (
/api/projects/mine)- evaluation_status ํ๋ ์ง์ (COMPLETED, PENDING, NOT_REQUIRED)
- ํ์ ํ๊ฐ ์ํ ์๋ ๊ณ์ฐ ๋ฐ ํํฐ๋ง ์ง์
- ๐ Notifications ํ
์ด๋ธ ์ถ๊ฐ
- ์๋ฆผ ์์คํ ๊ธฐ๋ฐ ๊ตฌ์ถ (์ฝ์/์์ฝ์ ์ํ ๊ด๋ฆฌ)
- ๐๏ธ PostgreSQL Raw SQL ์ ํ
- Sequelize ORM ๋์๋ฌธ์ ์ด์ ํด๊ฒฐ (ProjectMembers โ project_members)
- ํ๋ก๋์ ์์ ์ฑ ๋ฐ ์ฑ๋ฅ ํฅ์
- โก Recruitments ์คํค๋ง ๊ฐ์
- user_id ์ปฌ๋ผ ์ถ๊ฐ๋ก ๋ชจ์ง๊ณต๊ณ ์์ฑ์ ์ถ์ ๊ธฐ๋ฅ ๊ฐํ
- ๐ JWT ํธํ์ฑ ๋ ์ด์ด ๊ตฌํ
- Edge Function JWT (sub ํ๋) + Render JWT (userId ํ๋) ๋์ ์ง์
- ๋ง์ด๊ทธ๋ ์ด์ ๊ธฐ๊ฐ ์ค ์ํํ ์ ํ ์ง์
- โ SendGrid ๋๋ฉ์ธ ์ธ์ฆ ์๋ฃ (teamitaka.com)
- ๐ Render ํ๋ก๋์ ๋ฐฐํฌ ์๋ฃ
- โก Nodemailer SMTP ํด๋ฐฑ ์ ๊ฑฐ (์ฑ๋ฅ ๊ฐ์ : 120์ด โ 2์ด)
- ๐๏ธ Supabase Shared Pooler ์ ์ฉ (IPv4 ํธํ์ฑ)
- ๐ง ์ด๋ฉ์ผ ๋ฐ์ก ์์คํ ์ต์ ํ (SendGrid Web API ์ ์ฉ)
- ๐ ๏ธ GitHub Actions ์ํฌํ๋ก์ฐ ์ ๋ฆฌ
- ๐ ํ๋ก ํธ์๋ ํตํฉ ๋ฌธ์ ์์ฑ
- โจ ์ด๊ธฐ ๋ฆด๋ฆฌ์ฆ
- ๐ ์ด๋ฉ์ผ ์ธ์ฆ ์์คํ ๊ตฌํ
- ๐ JWT ๊ธฐ๋ฐ ์ธ์ฆ ๊ตฌํ
- ๐ ํ๋ก์ ํธ ๊ด๋ฆฌ CRUD
- ๐ฌ ๋๊ธ ์์คํ ๊ตฌํ
- ๐ ๊ฒ์ ๊ธฐ๋ฅ ๊ตฌํ
- ๐ Supabase ๋ฐฐํฌ ์ง์
๊ฐ๋ฐ: TeamItaka Development Team ๋ฌธ์: GitHub Issues๋ฅผ ํตํด ์ฐ๋ฝ ์ฃผ์ธ์