ํ์/์์ ์ด์์ ์ํ ํตํฉ ํ๋ซํผ์ ๋ฐฑ์๋ ์๋ฒ
Express ๊ธฐ๋ฐ REST API ์๋ฒ๋ก, ๊ฐ์ฌ/์กฐ๊ต/ํ์/ํ๋ถ๋ชจ์ ๊ต์ก ์ด์ ํ๋ฆ์ ํ๋์ API๋ก ํตํฉํฉ๋๋ค.
| ํ๋ก ํธ์๋ | ๋ฐฑ์๋ | ๋ฐฐํฌ ๋งํฌ |
|---|---|---|
- SSam B ๋ฐฑ์๋
- ํ์/๊ถํ/์ธ์ฆ ๊ด๋ฆฌ: Better Auth ๊ธฐ๋ฐ์ ์ธ์ ๊ด๋ฆฌ, ํ์๊ฐ์ , ๋ก๊ทธ์ธ ๋ฐ ์ญํ ์ ๋ฐ๋ฅธ API ์ ๊ทผ ์ ์ด
- ์๊ฐ ๋ฐ์ดํฐ ๊ด๋ฆฌ: ๊ฐ์, ์๊ฐ์, ์ฑ์ , ๊ณผ์ , ์ถ๊ฒฐ, ๊ฒ์๊ธ, ์ผ์ ๋ฑ์ ๊ต์ก ์ด์ ๋ฐ์ดํฐ ์ค์ ๊ด๋ฆฌ
- ์คํ ๋ฆฌ์ง ์ฐ๋: ํ์ผ ์ ๋ก๋ ๋ฐ ์ ์ ์๋ฃ ์ ๊ณต์ ์ํ AWS S3 / CloudFront ์ฐ๋
- ์ด์ ํ์ฅ์ฑ: ๋ผ์ฐํธ/์๋น์ค/๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ช ํํ ๋ถ๋ฆฌ์ ์์กด์ฑ ์ฃผ์ (DI)์ ํตํ ์ ์ง๋ณด์์ฑ ๋ฐ ํ์ฅ์ฑ ํ๋ณด
- ๋ชจ๋ํฐ๋ง ์ฐ๋: Sentry, Nodemailer ๋ฑ ์๋ฆผ/๋ก๊ทธ/๋ชจ๋ํฐ๋ง ์์คํ ๊ตฌ์ถ
| ๋ถ๋ฅ | ์คํ |
|---|---|
| Backend | |
| Database | |
| Infra |
Express์ Prisma ๊ธฐ๋ฐ์ 3๊ณ์ธต ์ํคํ ์ณ(Controller - Service - Repository)์ ์์กด์ฑ ์ฃผ์ (DI) ํจํด์ ์ ์ฉํ์ฌ, ๋ก์ง์ ์์ง๋๋ฅผ ๋์ด๊ณ ๊ต์ฒด ๋ฐ ํ์ฅ์ด ์ ๋ฆฌํ ๊ตฌ์กฐ์ ๋๋ค.
src/
โโโ app.ts # ๐ ์ฑ ๋ถํ
, ๋ฏธ๋ค์จ์ด, ๋ผ์ฐํ
๋ฐ ์ข
๋ฃ ์ ์ด
โโโ config/ # โ๏ธ ํ๊ฒฝ ๋ณ์, DB, Redis, Auth ๊ตฌ์ฑ
โโโ routes/ # ๐ค๏ธ ์๋ํฌ์ธํธ ๋ผ์ฐํ
(mgmt, svc, public)
โโโ controllers/ # ๐ฎ HTTP ์์ฒญ ๋ฐ ์๋ต ์ ์ด
โโโ services/ # ๐ผ ๋น์ฆ๋์ค ๋ก์ง
โโโ repos/ # ๐๏ธ Prisma ๊ธฐ๋ฐ ๋ฐ์ดํฐ ์ ๊ทผ(Repository)
โโโ validations/ # โ
Zod ๊ธฐ๋ฐ ์์ฒญ ๋ฐ์ดํฐ ๊ฒ์ฆ
โโโ middlewares/ # ๐ก๏ธ ์ธ์ฆ, ๋ก๊น
, ์๋ฌ ํธ๋ค๋ง, ํ์ด๋จธ ๋ฑ ๋ฏธ๋ค์จ์ด
โโโ utils/ # ๐ ๏ธ ๊ณต์ฉ ์ ํธ๋ฆฌํฐ (๋ฉ์ผ, ๋ชจ๋ํฐ๋ง, ๋ ์ง ๋ฑ)
prisma/
โโโ schema.prisma # ๐ DB ์คํค๋ง ๋ฐ ๋๋ฉ์ธ ๋ชจ๋ธ ์ ์
์์กด์ฑ ์ฃผ์ (DI): ํ๋ ์์ํฌ DI ์ปจํ ์ด๋ ์์ด๋ ๋ผ์ฐํฐ ํฉํ ๋ฆฌ, ์๋น์ค ์์ฑ์ ๊ธฐ๋ฐ์ผ๋ก ๋ช ์์ ์์กด์ฑ์ ์ฃผ์ ํฉ๋๋ค. ๋ก์ง์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถฐ ํ ์คํธ๊ฐ ์ฌ์์ง๋ฉฐ, ๊ธฐ๋ฅ ์ถ๊ฐ/์์ ์ ์์ ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
๋ฐฑ์๋์ ๋ผ์ฐํ
(Base: /api/{domain}/v1)์ ํฌ๊ฒ 3๊ฐ์ง ๋๋ฉ์ธ์ผ๋ก ๋๋ฉ๋๋ค. ๋ชจ๋ ์๋น์ค๋ GET /health ๊ฒฝ๋ก๋ฅผ ํตํด ์ํ ์ฒดํฌ๋ฅผ ์ง์ํฉ๋๋ค.
mgmt(๊ฐ์ฌ/์กฐ๊ต ์ด์ API):- ์ด์๊ถํ ์ฌ์ฉ์ ๋์ ๊ธฐ๋ฅ์ ๋ฌถ์
- ๋ผ์ฐํธ:
/lectures,/enrollments,/exams,/assignment-results,/grades,/materials,/instructor-posts,/student-posts,/dashboard๋ฑ
svc(ํ์/ํ๋ถ๋ชจ ์๋น์ค API):- ์ผ๋ฐ ์ฌ์ฉ์ ๊ด์ ์ ์๋น์ค(์กฐํ, ์ ์ถ ๋ฑ)์ ์ง์ค
- ๋ผ์ฐํธ:
/enrollments,/children,/grades,/clinics,/student-posts,/me,/uploads๋ฑ
public(๊ณต๊ฐ ์ธ์ฆ API):- ๋น๋ก๊ทธ์ธ ์ฌ์ฉ์์ ์ ๊ทผ ์ง์ ๋ก
- ๋ผ์ฐํธ:
/auth(ํ์ ๊ฐ์ , ์ด๋ฉ์ผ ์ธ์ฆ, ๋ก๊ทธ์ธ)
์ฌ์ฉ์ ๊ณ์ธต(User, Instructor, Assistant, AppStudent, AppParent)์ ์ค์ฌ์ผ๋ก ์ด์ ์ฃผ์ฒด์ ์๋น์ค ์ด์ฉ ์ฃผ์ฒด๊ฐ ๋ช
ํํ๊ฒ ๋๋ฉ๋๋ค. ํฌ๊ฒ 5๊ฐ์ ๋น์ฆ๋์ค ๋๋ฉ์ธ์ผ๋ก ๊ด๋ฆฌ๋ฉ๋๋ค.
๐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋๋ฉ์ธ ๋ค์ด์ด๊ทธ๋จ ํผ์ณ๋ณด๊ธฐ
flowchart LR
subgraph A["์ ์ & ์ธ์ฆ/์ธ๊ฐ"]
User["User (Better Auth)"] --> Session
User --> Account
User --> AppStudent
User --> AppParent
User --> Instructor
User --> Assistant
AppParent --> ParentChildLink
Instructor --> AssistantCode
VerificationCode
end
subgraph B["์์
"]
Instructor --> Lecture
Instructor --> Enrollment
Enrollment -. optional .-> AppStudent
Enrollment -. optional .-> ParentChildLink
Enrollment --> LectureEnrollment
Lecture --> LectureEnrollment
Lecture --> LectureTime
LectureEnrollment --> Attendance
end
subgraph C["์ํ & ๊ณผ์ "]
Lecture --> Exam
Exam --> Question
Question --> StudentAnswer
Exam --> Grade
Grade --> GradeReport
Lecture --> Assignment
Assignment --> AssignmentResult
Exam --> Clinic
LectureEnrollment --> Clinic
end
subgraph D["์ํต"]
Instructor --> InstructorPost
Lecture --> InstructorPost
InstructorPost --> InstructorPostTarget
Enrollment --> InstructorPostTarget
Enrollment --> StudentPost
InstructorPost --> Comment
StudentPost --> Comment
InstructorPost --> InstructorPostAttachment
Comment --> CommentAttachment
end
subgraph E["์ ํธ๋ฆฌํฐ"]
Instructor --> ScheduleCategory
ScheduleCategory --> Schedule
Instructor --> Material
Lecture --> Material
Instructor --> AssistantOrder
Assistant --> AssistantOrder
AssistantOrder --> OrderAttachment
Material -. optional .-> OrderAttachment
end
%% cross-cutting (attachments reuse)
Material -. optional .-> InstructorPostAttachment
Material -. optional .-> CommentAttachment
| ๋๋ฉ์ธ | ๊ตฌ์ฑ ๋ฐ ํน์ง |
|---|---|
| ์ ์ & ์ธ์ฆ/์ธ๊ฐ | Better Auth ๊ธฐ๋ณธ ๋ชจ๋ธ ๊ธฐ๋ฐ ์ฌ์ฉ์ ์๋ณ. ํ๋ซํผ ์ ์ฉ ํ์ฅ ๋ฉํ ์ ๊ณต. |
| ์์ ์ด์ ๊ณ์ด | Enrollment โ Lecture โ LectureEnrollment/Attendance |
| ์ํ & ๊ณผ์ ๊ณ์ด | ์ํ, ๊ณผ์ ํ๊ฐ ์ฐ์ถ ๋ฐ ๋ฆฌํฌํธ, ๋ณด์ํ์ต(Clinic) |
| ์ํต ๊ณ์ด | ๊ณต์ง, ์ง๋ฌธ ๊ฒ์ํ(InstructorPost, StudentPost, Comment) |
| ์์ ๋ฐ ์ ํธ๋ฆฌํฐ | ์๋ฃํจ(Material), ์ผ์ (Schedule), ์กฐ๊ต ์
๋ฌด ์ง์ |
๐ ์ธ์ฆ ๋ฐ ์ธ๊ฐ ์ฒ๋ฆฌ ํ๋ก์ฐ ๋ค์ด์ด๊ทธ๋จ ๋ณด๊ธฐ
sequenceDiagram
participant Client as ํด๋ผ์ด์ธํธ (Frontend)
participant Server as ์๋ฒ (Better Auth)
participant DB as ๋ฐ์ดํฐ๋ฒ ์ด์ค
rect rgb(240, 240, 240)
Note over Client, DB: 1. ์ธ์ฆ ๋จ๊ณ (Authentication - ๋ก๊ทธ์ธ)
Client->>Server: 1. ๋ก๊ทธ์ธ ์์ฒญ (signIn.email ๋ฑ)
Server->>DB: 2. ์๊ฒฉ ์ฆ๋ช
(์์ด๋/๋น๋ฐ๋ฒํธ) ํ์ธ
DB-->>Server: 3. ๊ฒ์ฆ ๊ฒฐ๊ณผ ๋ฐํ
Server->>DB: 4. ์ธ์
(Session) ์์ฑ ๋ฐ ์ ์ฅ
Server-->>Client: 5. HttpOnly ์ฟ ํค ๋ฐ๊ธ (Set-Cookie) ๋ฐ ์๋ต ๋ฐํ
end
rect rgb(240, 248, 255)
Note over Client, DB: 2. ์ธ๊ฐ ๋จ๊ณ (Authorization - ๋ณดํธ๋ ์์ ์ ๊ทผ)
Client->>Server: 6. ๋ณดํธ๋ API/ํ์ด์ง ์์ฒญ (๋ฐ๊ธ๋ ์ฟ ํค ์๋ ํฌํจ)
Server->>DB: 7. ์ฟ ํค ๋ด ์ธ์
ID ์ ํจ์ฑ ๋ฐ ๊ถํ ํ์ธ
DB-->>Server: 8. ์ธ๊ฐ(Authorization) ๊ฒฐ๊ณผ ๋ฐํ
alt ๊ถํ ์์ (์ ํจํ ์ธ์
)
Server-->>Client: 9. ์์ฒญ ์ฒ๋ฆฌ ๋ฐ ๋ฐ์ดํฐ ์๋ต
else ๊ถํ ์์ (๋ง๋ฃ๋๊ฑฐ๋ ์๋ชป๋ ์ธ์
)
Server-->>Client: 9. 401/403 ์๋ฌ ๋ฐํ (์ ๊ทผ ๊ฑฐ๋ถ)
end
end
๋ณด์ ์ฒ๋ฆฌ ํ๋ฆ:
- ๋ณดํธ ๋ผ์ฐํธ๋ก ์ ๊ทผํ ๋ ๋ฏธ๋ค์จ์ด ์ฒด์ธ์ ํต๊ณผํฉ๋๋ค.
requireAuth: ์์ฒญ ํค๋์์ ์ธ์ ์ ์กฐํํด ์ ํจํ์ง ์์ผ๋ฉด401๋ฐํ.- ์ ํจํ ์ธ์
์
req.user,req.profile,req.authSession์ ์์ ํ๊ฒ ์ฃผ์ ๋ฉ๋๋ค. - ๊ถํ ์ธ๊ฐ: ์ดํ
requireInstructor,requireStudent๋ฑ์ ๊ถํ ๋ฏธ๋ค์จ์ด๋ฅผ ํตํด ์ฌ์ฉ์ ํ์ ์ ๊ฒ์ฆํฉ๋๋ค. - ํน์ ์กฐ๊ฑด: ์กฐ๊ต(
ASSISTANT)์ ๊ฒฝ์ฐ ์๋ช ์น์ธ(signStatus === SIGNED) ์ํ๊น์ง ํ์ธํ์ฌ ์ ๊ทผ์ ์ ํํฉ๋๋ค. - ์ธํ๋ผ ์ต์ ํ: Better Auth๋ฅผ ํ์ฉํด ํฌ๋ก์ค ๋๋ฉ์ธ ์ฟ ํค(์ด์ ํ๊ฒฝ), trust origin ์ค์ (FRONT_URL ๊ธฐ๋ฐ)์ผ๋ก ์์ ํ๊ฒ ๋์ํฉ๋๋ค.
๋ฌธ์ (์ฆ์): ํ๋ก ํธ์๋(www)์ ๋ฐฑ์๋(api) ๋ถ๋ฆฌ ํ๊ฒฝ์์, ๋ก๊ทธ์ธ ์ Set-Cookie๋ ์ ์ ๋ฐ๊ธ๋๋ ์ดํ ๋ณดํธ๋ ๋ผ์ฐํธ(์ธ์
์กฐํ ๋ฑ) ์์ฒญ์ ์ฟ ํค๊ฐ ์ค๋ฆฌ์ง ์์ ์ธ์
๊ธฐ๋ฐ ๋ผ์ฐํ
์์ ํ๋ฝ(401)ํ๋ ์ฆ์์ด ๋ฐ์ํ์ต๋๋ค.
์์ธ: Better Auth ์ฟ ํค๊ฐ ๋จ์ผ ๋๋ฉ์ธ ๊ธฐ์ค์ผ๋ก ๋์ํ์ฌ ์๋ธ๋๋ฉ์ธ ๊ฐ ๊ณต์ ๊ฐ ๋์ง ์์๊ณ , ์ธ์
์กฐํ ์คํจ ์ ์ฆ๊ฐ์ ์ผ๋ก ์ฟ ํค๋ฅผ ์ญ์ ํ๋ ๊ฐํ ๋ก์ง์ด ๋ง๋ฌผ๋ ธ๊ธฐ ๋๋ฌธ์ด์์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ๋ฒ: AUTH_COOKIE_DOMAIN ํ๊ฒฝ๋ณ์์ crossSubDomainCookies ์ต์
์ ํ์ฉํ์ฌ ์ด์ ํ๊ฒฝ ๊ณตํต ๋๋ฉ์ธ(.ssambee.com)์ ๊ธฐ์ค์ผ๋ก ์ฟ ํค๋ฅผ ๋ฐ๊ธํ๋๋ก ๊ฐ์ ํ์ต๋๋ค. ๋ํ ๋ก๊ทธ์์ ๋ฑ ์ธ์
์ฟ ํค ์ญ์ ์์๋ ๋๋ฉ์ธ ์ ํฉ์ฑ์ ๋ง์ถ์ด ๋ค์ค ์๋ธ๋๋ฉ์ธ ํ๊ฒฝ ์ ์ฒด์์ ์์ ์ ์ผ๋ก ์ธ์
์ ์ ์งํ๋๋ก ์กฐ์นํ์ต๋๋ค.
# ์์กด์ฑ ์ค์น
$ pnpm install
# ๋ก์ปฌ ๊ฐ๋ฐ ์๋ฒ ์คํ
$ pnpm dev
# ๋น๋
$ pnpm build
# ์ด์ ๋น๋ ์คํ
$ pnpm start
# ํ
์คํธ ์คํ
$ pnpm test- ์ฑ ๋ถํ ํ๋ฆ: (1) ํ๊ฒฝ ๋ณ์ ๊ฒ์ฆ โ (2) Sentry ์ด๊ธฐํ โ (3) ๋ณด์/CORS ์ค์ โ (4) ๋ผ์ฐํฐ ๋ฐ ํ์ฑ ๋ฏธ๋ค์จ์ด ๋ฑ๋ก โ (5) ์๋ฌ ํธ๋ค๋ฌ ์ธํ โ (6) Graceful Shutdown
- ๋ฐฐํฌ ์ํคํ
์ณ:
docker-compose.yml๊ธฐ์ค์ผ๋ก Nginx์ Blue/Green ๋ฌด์ค๋จ ๋ฐฐํฌ๋ฅผ ์ง์ํฉ๋๋ค.- ์ปจํ
์ด๋ ์ข
๋ฃ(
SIGTERM/SIGINT) ์์ ์ ๋ผ์ฐํ ์ ์ค๋จํ๊ณ ์์ ๋ ๊ธฐ์กด ์์ฒญ ์ฒ๋ฆฌ๋ฅผ ๋ง์น ๋ค DB ์ฐ๊ฒฐ์ ์ข ๋ฃํฉ๋๋ค.
- ์ปจํ
์ด๋ ์ข
๋ฃ(
- ์ธํ๋ผ ์ ํ์ ์ฐ๋:
.env๊ฐ์ ๊ธฐ์ค์ผ๋ก Sentry(์๋ฌ ์ถ์ ), Redis, AWS ์ฐ๋์ ์ ๊ณต๋ ํ๊ฒฝ๋ณ์๊ฐ ์์ ์์๋ง ๋์ํ๋๋ก ์ค๊ณ๋์์ต๋๋ค.
| ๐ ๋ฐ์ฐฝ๊ธฐ | ์ด์ ๋ฆฌ | ์๊ฒฝ๋ฏผ | ๊น์ค๊ธฐ |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| PM & ํ๋ก ํธ | ํ๋ก ํธ | ๋ฐฑ์๋ | ๋ฐฑ์๋ & ๋ฐฐํฌ |
Made with โค๏ธ by SSam B Team




