Table of Contents
Docker desktop λλ Docker μ€ν μν νλ‘μ νΈ λλ ν 리μ ν°λ―Έλμμ docker-compose up -d
λ₯Ό μ
λ ₯νλ©΄ 1κ°μ Image, 3κ°μ Containerκ° μμ±λ©λλ€.
- youngmyung-Nodejs-Express: <Port 3000> Nodejs API μλ²μ λλ€.
- youngmyung-mysql: <Port 3306> MySQL μλ²μ λλ€.
- youngmyung-mysql-test: <Port 3307> Test code μ€νμ μν MySQL μλ²μ λλ€.
Test codeλ₯Ό μ€ννκΈ° μν΄μλ docker-compose ν
npm i
λ₯Ό μ λ ₯νκ³npm test
λ₯Ό μ λ ₯ν΄μ£ΌμΈμ.mysql, mysql-test λͺ¨λ κ°μ¬(instructor) ν μ΄λΈμ 9κ°μ ν μ€νΈ λ°μ΄ν°κ° λ€μ΄μμ΅λλ€ (id = 1 ~ 9).
- NodeJS (TypeScript)
- Express
- MySQL
- mysql2
- TSyringe
- ts-node-dev
- class-validator
- express-validator
- class-transformer
- Jest / ts-jest
- ts-mockito
- supertest
- Winston
νλ‘μ νΈλ₯Ό μ€κ³ν λ λ μ΄μ΄λ μν€ν μ²λ‘ ꡬμ±νμμ΅λλ€. μ΄λ dtoλ₯Ό μ¬μ©νμ¬ μμ²κ³Ό μλ΅μ΄ κ³μΈ΅κ° λ¨λ°©ν₯μΌλ‘ μ΄λν μ μλλ‘ κ΅¬ννμ΅λλ€.
- Presentation layer - Router, Controller
- Application/Business layer - Service
- Persistence layer - Repository
- Database layer - MySQL
μ΄λ‘μ¨ κ³μΈ΅κ³Ό κ΄μ¬μ¬λ₯Ό λΆλ¦¬νμ¬ κ°μμ μν μ μννλλ‘ νκ³ , κ° κ³μΈ΅ μ¬μ΄μ μμ‘΄μ±μ μ€μμ΅λλ€.
Presentation layerλ₯Ό ν΅ν΄ μμ²μ λ°μ μ
λ ₯κ°μ κ²μ¦ ν Application layerκ° λΉμ¦λμ€ λ‘μ§μ μ§μ€ν μ μλλ‘ νμμ΅λλ€.
νΉμ λ°μ΄ν°μ μ κ·Όνλ κ°μ 쿼리λ₯Ό λ€λ₯Έ μλΉμ€λ€μ΄ μ¬μ©ν μ μμ΄ μ½λμ μ¬μ¬μ©μ±μ μν΄ Persistence layerλ§ DBμ μνΈμμ©νλλ‘ μ€κ³νμ΅λλ€.
νλ‘μ νΈμ ν¬κΈ°κ° μμ§ μμΌλ©° ν¬κ² 볡μ‘νμ§ μκ³ , DI ν¨ν΄κ³Ό μ΄μΈλ¦¬λ ν¨μ¨μ μΈ μν€ν
μ²λΌκ³ μκ°νμ¬ νμ¬ μ μν©μ μ λ§λ μ€κ³λΌκ³ μκ°νμ¬ μ ννμμ΅λλ€.
λν, μ΄λ° ꡬ쑰μ DI ν¨ν΄μΌλ‘ μΈν΄ μμ‘΄μ±μ΄ μ€μ΄λ λ§νΌ λ¨μ ν
μ€νΈ μ½λλ₯Ό λ³΄λ€ μ μ°νκ² μμ±ν μ μλ κ²μ λλ μ μμμ΅λλ€.
-
νλ‘μ νΈ μμ μ κ°μ’ κΈ°λ₯ λ‘λ©, MySQL Pool μμ± νμΈ λ°©λ² κ³ λ―Ό
νμ¬ NodeJS + Express ꡬ쑰λ‘λ μ κ° νμμ μ¬μ©νλ SpringBootμ κ°μ΄ μ€μ λ€μ μλμΌλ‘ μ‘μμ£Όμ§ μλλ€λ κ²μ ν΄κ²°νκΈ° μν΄ κ³ λ―Όνμ΅λλ€.
κ·Έ κ²°κ³Ό μλ²λ₯Ό μμνλ server.tsλ₯Ό λκ³ μ΄μ νμν DI container μ΄κΈ°ν, express app μμ± λ±μ μ§ννκ³ 3000ν¬νΈλ₯Ό listenνλλ‘ μ€μ νμ΅λλ€.
ν¨μ¨μ μΈ DB connectionμ μν΄ μλ² μ΄κΈ°ν μ MySQL Pool μΈμ€ν΄μ€λ₯Ό μμ±νμ¬ containerμ λ±λ‘νκ³ , repositoryμμ ν΄λΉ μΈμ€ν΄μ€λ₯Ό μ΄μ©νμ¬ queryλ₯Ό μ€ννλλ‘ κ΅¬ννμ΅λλ€. -
μ΅λν μλ¬ μ΄μ λ₯Ό ν΄λΌμ΄μΈνΈμκ² μ λ¬ν΄μ£ΌκΈ°. λ΄λΆ μλ¬λ λ ΈμΆνμ§ μκΈ°
λ°±μλ κ°λ°μμ ꡬν λ₯λ ₯λ μ λ§ μ€μνμ§λ§ μλ¬λ₯Ό μ λ€λ£¨κ³ μ°Ύμλ΄λ κ²λ κ·Έλ§νΌ μ€μνλ€κ³ μκ°ν©λλ€.
μλΉμ€ λ° μλ²μ μ₯μ λμμ λ¬Όλ‘ μ΄κ³ , λ€λ₯Έ λ°±μλ, νλ‘ νΈμλ, λ°λΈμ΅μ€ λ±μ νλ€κ³Όμ νμ μλ ν° μν₯μ μ€λ€κ³ μκ°νκΈ° λλ¬Έμ λλ€.
λ°λΌμ μλͺ»λ μμ²μ΄λ λ΄λΆ μλ²μ μλ¬ λ±μ 컀μ€ν μλ¬λ₯Ό ν΅ν΄ μΈλΆννκ³ , 보μμ μν΄ λ΄λΆ μλ¬ μ€νμ λ ΈμΆλμ§ μλλ‘νλ©°, κ° μλ¬μ λ°λ₯Έ λ©μμ§λ₯Ό μΆλ ₯ν μ μλλ‘ κ΅¬ννμ΅λλ€.
κ°νΉ μλ¬λ₯Ό catch ν λ€μ throw ν λ κΈ°μ‘΄ μλ¬ μ€νμ΄ μ¬λΌμ§λ νμμ λ°κ²¬νμ¬ μ΄λ₯Ό λ°©μ§νκΈ° μν΄ μ€νμ κ³μ μΆμ νλλ‘ νμμ΅λλ€.
μλ¬λ₯Ό μΆμ νκΈ° μν λ‘κ·Έλ νμλΌκ³ μκ°νμ¬ Winston λΌμ΄λΈλ¬λ¦¬λ₯Ό ν΅ν΄ λ‘κ·Έ μμ§λ λμ νμμ΅λλ€. -
λ°λ³΅ λλ DB try-catch-finally νλλ‘ -> κ³ μ°¨ ν¨μ μ¬μ©
Repositoryμμ connectionμ μμ± ν 쿼리λ₯Ό μννκ³ , λ€μ releaseνλ μμ μ΄ try-catch-finallyλ‘ λͺ¨λ λ©μλμμ λ°λ³΅λμ΄ μ΄λ₯Ό μ€μΌ μ μλ λ°©λ²μ κ³ λ―Όνμ΅λλ€.
ν¨μν νλ‘κ·Έλλ°μ νΉμ§μ μ§λ μλ°μ€ν¬λ¦½νΈμμ ν¨μλ₯Ό νλΌλ―Έν°λ‘ μ λ¬λ°κ±°λ μ°μ°μ κ²°κ³Όλ‘ λ°νν΄μ£Όλ λ©μλμΈ κ³ μ°¨ ν¨μλ₯Ό μ΄μ©νμ¬ ν΄κ²°νμ΅λλ€. mysqlUtil.ts
ν΄λΉ νμΌμλ νΈλμμ μ΄ μλ 쿼리μ μλ μΏΌλ¦¬λ‘ λ©μλλ₯Ό ꡬλΆνμ¬ κ΅¬ννμ΅λλ€. -
ν¬κ΄μ μΈ μλ¬ νΈλ€λ§κ³Ό λΉλκΈ° μλ¬
ν¬κ΄μ μΈ μλ¬ νΈλ€λ§ μ€μ μ μ§μ μννμ§ μλ κ²½μ° λ΄μ₯λ κΈ°λ³Έ μλ¬ νΈλ€λ¬κ° νΈμΆλκ³ μ€ν μ€λ μ΄μ€λ₯Ό λ°ννμ¬ μΈλΆμ λ ΈμΆλ©λλ€.
λ°λΌμapp.use((err, req, res, next))
λ₯Ό μ΄μ©νμ¬ ν¬κ΄μ μΈ μλ¬ νΈλ€λ§μ μ§μ μ€μ νμ΅λλ€.
νμ§λ§ μ΄κ²μΌλ‘λ λΉλκΈ° μλ¬λ₯Ό μ‘μ§ λͺ»νλ λ¬Έμ κ° μμμ΅λλ€. try-catchλ‘ ν΄κ²° ν μ μμ§λ§ μ΄κ²λ λ°λ³΅λμ΄ κ³΅ν΅νκ° νμνλ€κ³ μκ°νμ΅λλ€.
λ°λΌμ routerμμ controllerλ‘ μμ²μ λ겨주기 μ μ wrapAPI ν¨μλ₯Ό λ§λ€μ΄ ν΄λΉ ν¨μλ₯Ό κ°μΈμ 보λ΄λλ‘ κ΅¬ννμμ΅λλ€. wrapAsync.ts
κ·Έ κ²°κ³Ό λΉλκΈ° μλ¬κ° λ°μν΄λ ν¨μλ₯Ό ν΅ν΄ ν¬κ΄μ μΈ μλ¬ νΈλ€λ§μΌλ‘ κ° μ μλλ‘ μ λνμ΅λλ€. -
κΈ°μ‘΄ λ€λ₯Έ repositoryμ μ¬λ¬ λ©μλλ₯Ό μ¬μ©ν λ νΈλμμ μ²λ¦¬
Repository λ΄μ νλμ λ©μλμμ νΈλμμ μ μ¬μ©νλ κ²μ΄ μλ, μλΉμ€μμ μ¬λ¬ repositoryλ₯Ό νλ²μ μ΄μ©ν λ νΈλμμ μ μ΄λ»κ² μ²λ¦¬ν μ§ κ³ λ―Όνμ΅λλ€.
κ·Έλ¬κΈ° μν΄μλ serviceμμ connectionμ μμ±νκ³ , μμμ ꡬνν κ³ μ°¨ ν¨μλ₯Ό μ΄μ©νμ¬ νΈλμμ μ μ¬μ©νλλ‘ νμ΅λλ€.
Serviceμμ μμν connectionμ repositoryμμλ μ μ§ λλλ‘ parameterλ₯Ό λ§λ€κ³ λΆκΈ° μ²λ¦¬λ₯Ό νμ¬, ν΄λΉ repository λ©μλκ° μλ‘μ΄ connectionμ΄ νμνμ§ λΆνμνμ§ κ΅¬λΆνλλ‘ νμ΅λλ€.public async deleteAllByStudentId( id: number, prevConnection?: PoolConnection, ): Promise<void> { const sql = 'DELETE FROM class WHERE id = ?;'; const value = [id]; if (!prevConnection) { const connection = await this.mysqlPool.getConnection(); return await executeQuery(connection, async () => { await connection.query<ResultSetHeader>(sql, value); }); } else { await prevConnection.query<ResultSetHeader>(sql, value); } }
-
κ°μλ₯Ό μ€ν, μμ , μμ ν λ (select ~ where id = ? and ins_id ?)λ‘ μνκ³ idλ§μΌλ‘ courseλ₯Ό λ¨Όμ find νκΈ°
κ°μλ₯Ό μ€ν, μμ , μμ λ₯Ό νκΈ° μν΄μ μμ κ°μ μΏΌλ¦¬λ‘ νλ²μ μ€ννμ¬ μ±κ³΅ λλ μ€ν¨λ₯Ό λ°νν μλ μμμ΅λλ€.
κ·Έλ¬λ μ΄ λ°©λ²μΌλ‘ μ§ννλ©΄ ν μ΄λΈμ μ°Ύμ μ μμλ κ°μμ μ‘΄μ¬ μ 무, μμ²μκ° ν΄λΉ κ°μμ κ°μ¬κ° μλμ§λ₯Ό μ νν νμΈ ν μ μμμ΅λλ€.
λ°λΌμ λ¨Όμ κ°μλ₯Ό idλ‘ μ°Ύκ³ μμ°¨μ μΌλ‘ κ°μ¬λ₯Ό κ²μ¦νλ λ°©λ²μΌλ‘ ꡬννμ΅λλ€. -
Course table κ°μ μκ° Data type - VARCHAR(65535) or TEXT?
κ°μ μκ°λ κΈΈμ΄κ° ν΄μ μμ΄ ν΄λΉ λ°μ΄ν° νμ μ μ΄λ€ κ²μΌλ‘ μ€μ ν μ§ κ³ λ―Όνλ©° μ΄ λκ°μ μ°¨μ΄λ₯Ό μμ보μμ΅λλ€.
λ μ½λμλ μ΅λ μ¬μ΄μ¦κ° μ€μ λμ΄ μκ³ , VARCHARλ ν μ΄λΈμ λ€λ₯Έ μΉΌλΌλ€μ΄ μ¬μ©ν μ μλ μ΅λ 곡κ°μ ν¬κΈ°μ μν₯μ μ€ μ μμ΅λλ€.
νμ§λ§ TEXTλ νΉμ μκ³μΉκ° λμΌλ©΄ InnoDBμ Off-page λ°©μμ ν΅ν΄ λ μ½λμ λ³λμ 곡κ°μ μ μ₯λμ΄ λ μ½λμλ 9~12 λ°μ΄νΈ λ°μ μ°¨μ§νμ§ μμ΅λλ€.
λ°λΌμ μΆν νμ₯μ±μ μκ°νμ¬ TEXTλ₯Ό μ¬μ©νκΈ°λ‘ κ²°μ νμ΅λλ€.
https://dev.mysql.com/doc/refman/8.0/en/column-count-limit.html#row-size-limits -
κ°μ λͺ©λ‘ μ‘°ν - Covering Index μ μ©
ν΄λΉ μλΉμ€μ νΉμ±μ, 보ν΅μ μλΉμ€κ° κ·Έλ λ―μ΄ μ°κΈ°λ³΄λ€λ μ½κΈ°κ° μ£Όλ‘ μ΄λ£¨μ΄μ§ κ²μ΄λΌκ³ μμμ΄ λμμ΅λλ€.
λν μ¬κΈ°μ νμ΄μ§ κΈ°λ₯κΉμ§ μμ΄ κ²μ μ±λ₯μ κ°μ νλ λ°©λ²μ μκ°ν΄λ³΄μκ³ , κ²μ μ μΈλ±μ€λ₯Ό μ μ©ν΄λ³΄κ³ μ νμ΅λλ€.
λ¨Όμ No offsetμ μκ°ν΄λ³΄μμ΅λλ€. νμ§λ§ μ΄λ 무ν μ€ν¬λ‘€μ μ ν©νκ³ , μμ°¨μ μΌλ‘ λ€μνμ΄μ§ μ΄λλ§ κ°λ₯νμ¬ μΌλ°μ μΈ νμ΄μ§ λ²νΌμ μ¬μ©νμ§ λͺ»νλ€λ μ μ΄ μμμ΅λλ€.
νλ‘μ νΈμ μλΉμ€λ₯Ό μκ° νμλ, μ΄λ μ ν©νμ§ μλ€κ³ μκ°νμμ΅λλ€.
κΈ°μ‘΄ νμ΄μ§ κΈ°λ₯μ μ μ§νλ©΄μ κ²μ μ±λ₯μ κ°μ νκΈ° μν΄ μ»€λ²λ§ μΈλ±μ€λ₯Ό μ μ©νκ³ μ νμ΅λλ€. μ¦ select, where, order by, group by λ± μ¬μ©λλ λͺ¨λ μΉΌλΌμ΄ index μΉΌλΌμ ν¬ν¨λλλ‘ νμ΅λλ€. -
κ°μ λͺ©λ‘ μ‘°ν - Covering Index, using temporary, using filesort ν΄κ²°
μΈλ±μ€λ₯Ό μμ±νκ³ EXPLAINμΌλ‘ νμΈν κ²°κ³Ό, extraμ
using temporary, using filesort
κ° μΆλ ₯λμκ³ μ»€λ²λ§ μΈλ±μ€κ° μ λλ‘ μλνμ§ μλλ€λ κ²μ νμΈνμ΅λλ€.
μ΄κΈ° λ°μ΄ν°λ² μ΄μ€μμ κ°μ ν μ΄λΈμλ νμμκ° μμ§ μμκ³ ,count(cl.id) as student_id
λ‘ μΈν΄ μ΄λ° νμμ΄ μλ€λ κ²μ μκ² λμμ΅λλ€.
μ΄λ₯Ό ν΄κ²°νκΈ° μν΄ μ κ° μ²μμ μ€κ³νλ λ°μ΄ν°λ² μ΄μ€μμ κ°μκ° νμμλ₯Ό class(κ°μ-μκ°μ mapping table)μ κ°―μλ‘ μΈλ κ²μμ μΉΌλΌμΌλ‘ κ°μ§λλ‘ λ³κ²½νμ΅λλ€.
κ·Έ ν μ΄μ νμν μΈλ±μ€λ₯Ό μμ±νκ³ , EXPLAINμ ν΅ν΄ 'Using Index'λ‘ μ»€λ²λ§ μΈλ±μ€κ° μ μνλλ κ²μ νμΈνμ΅λλ€. -
κ°μ λͺ©λ‘ μ‘°ν - Covering Index, Backward Index Scan
μ΅μ μ, μκ°μμ μ λ ¬μ νλ 컀λ²λ§ μΈλ±μ€λ₯Ό μν΄ (group by, order by μμ λλ¬Έμ) λ μ§κ° 맨 μμ μλ μΈλ±μ€μ, μκ°μ μκ° λ§¨ μμ μΈλ±μ€λ₯Ό λ§λ€μ΄ λμμ΅λλ€.
κ·Έλ¬λ μ΅μ μμ΄λ μκ°μμμ λ°λλ‘ μΈλ±μ€λ μ€λ¦μ°¨μμΌλ‘ μ λ ¬λμ΄ backward index scanμ΄ λ°μνλ κ²μ νμΈνμ΅λλ€.
νμ§λ§ InnoDBμμλ νμ΄μ§ μ κΈμ΄ forward indexμ μ ν©ν ꡬ쑰μ΄κ³ , Double linked listλ‘ μ°κ²°λ B-Treeμ 리ν νμ΄μ§ ꡬ쑰μλ λ¬λ¦¬ νμ΄μ§ λ΄μμ μΈλ±μ€ λ μ½λκ° λ¨λ°©ν₯μΌλ‘λ§ μ°κ²°λμ΄ backward index scanμ΄ forward index scanμ λΉν΄ λ릴 μ λ°μ μμ΅λλ€.
Forward Index scanμ μ λνκΈ° μν΄μ κ°κ° 맨 μμ μΈλ±μ€λ₯Ό Descending Indexλ‘ κ΅¬μ±νμ΅λλ€.
>INDEX idx_covering_course_create_order (create_date DESC, title, instructor_id, price, category, student_count, is_public),
>INDEX idx_covering_course_student_count_order (student_count DESC, create_date, title, instructor_id, price, category, is_public)
-
ν μ€νΈ μ½λ μμ±
ν μ€νΈ μ½λλ₯Ό μμ±ν¨μΌλ‘μ¨ μ λ ₯κ°λ€μ κ²μ¦νκ³ , μκΈ°μΉ λͺ»ν μλμ 미리 λ°©μ§ν μ μμ΄ μ΄λ νμλΌκ³ μκ°ν©λλ€.
νΉν μ μκ²λ μ΄λ₯Ό ν¬ν¨νμ¬, λ€μ λκ°μ§κ° ν μ€νΈ μ½λλ₯Ό μμ±νκ² λλ μλλ ₯μ΄ λ©λλ€.
첫λ²μ§Έλ 리ν©ν λ§μ λ³΄λ€ λ μμ νκ³ νμ€νκ² κ°λ₯νλλ‘ νλ€λ κ²μ λλ€. ν μ€νΈ μ½λλ ꡬν κ³Όμ μ΄ μλ κ²°κ³Όλ₯Ό κ²μ¦νλ κ²μ΄κΈ° λλ¬Έμ, μ½λλ₯Ό 리ν©ν λ§ ν΄λ κ²°κ³Όκ° λ¬λΌμ§μ§ μλ μ΄μ νμ ν΅κ³Όν΄μΌλ§ ν©λλ€. λ°λΌμ 리ν©ν λ§ ν κΈ°μ‘΄ ν μ€νΈ μ½λκ° ν΅κ³Όνλ©΄ μ μ΄λ£¨μ΄μ‘λ€λ κ²μ λ°λ‘ μ μ μμ΅λλ€.
λλ²μ§Έλ μ½λλ₯Ό μλ‘ λ³΄λ μ¬λλ€μ κ°μ΄λκ° λ μ μλ€λ κ²μ λλ€. μλμ μ½λλ§ λ³΄μμλ μ΄λ€ νμμ λ°μ΄ν°κ° λ€μ΄κ°κ³ λ°νμ΄ λμΌνλμ§ νλμ 보기 μ΄λ ΅μ§λ§, ν μ€νΈ μ½λμμλ μμλ₯Ό ν΅ν΄(given, then) λͺ ννκ² μ΄λ€ λ°μ΄ν°κ° λ€μ΄κ°κ³ λ°νλλμ§ λͺ μνκΈ° λλ¬Έμ λ§μ λμμ΄ λλ€κ³ μκ°ν©λλ€.
κ·Έ κ²°κ³Ό μ΄λ² κ³Όμ μμ μλ²μ μ 체μ μΈ λμμ νμΈν μ μλ ν΅ν© ν μ€νΈμ, λΉμ¦λμ€ λ‘μ§λ§ λΉ λ₯΄κ² κ²μ¦ν μ μλ λ¨μ ν μ€νΈλ₯Ό μ΅λν μμ±νμ΅λλ€.
>
http://localhost:3000/courses/search?type={}&keyword={}&category={}&pageNumber={}&pageSize{}&sort={}
URL query stringλ₯Ό ν΅ν΄ κ²μμ΄ μΌμΉ / ν¬ν¨ νλ κ³΅κ° μνμΈ κ°μλ€μ μ‘°νν μ μμ΅λλ€.
(κ°μ¬λͺ
/ κ°μλͺ
) λλ (μκ°μ id)λ₯Ό κ²μμ΄λ‘ν΄μ κ²μμ΄ κ°λ₯ν©λλ€.
κ°μ μΉ΄ν
κ³ λ¦¬λ all(μ 체 μΉ΄ν
κ³ λ¦¬), web, app, game, algorithm, infra, database λ₯Ό κ²μ 쑰건μΌλ‘ μ¬μ©ν©λλ€.
κ²μ κ²°κ³Όλ μ΅μ μ / μκ°μμλ‘ μ λ ¬ λλλ‘ μ‘°κ±΄μ μ€μ ν μ μμ΅λλ€.
νμ΄μ§μ΄ κ°λ₯νμ¬ μνλ νμ΄μ§ μμκ³Ό ν¬κΈ°λ₯Ό μ€μ ν μ μμ΅λλ€.
λͺ¨λ query stringμ μ
λ ₯ν΄ μ£Όμ΄μΌ νλ©°, νμμ΄ λ§μ§ μμ κ²½μ° μλ¬λ₯Ό λ°νν©λλ€.
EX) <GET> http://localhost:3000/courses/search?type=instructorAndTitle&keyword=Node&category=web&pageNumber=1&pageSize=10&sort=recent
μμ² (GET)
-
κ²μ 쑰건 -
type
(string)- κ°μ¬λͺ
/ κ°μλͺ
μΌλ‘ κ²μ:
instructorAndTitle
- μκ°μ id κ²μ:
studentId
- κ°μ¬λͺ
/ κ°μλͺ
μΌλ‘ κ²μ:
-
κ²μ λ¨μ΄ -
keyword
(string) or (integer)- 쑰건μ λ°λ₯Έ κ²μμ΄
-
μΉ΄ν κ³ λ¦¬ -
category
(string)- μ 체:
all
- μΉ:
web
- μ±:
app
- κ²μ:
game
- μκ³ λ¦¬μ¦:
algorithm
- μΈνλΌ:
infra
- λ°μ΄ν°λ² μ΄μ€:
database
- μ 체:
-
νμ΄μ§ λ²νΈ -
pageNumber
(integer)- νμ΄μ§ ν¬κΈ°μ λ°λΌ λͺ λ²μ§Έ νμ΄μ§λ₯Ό λνλ΄λμ§
-
νμ΄μ§ ν¬κΈ° -
pageSize
(integer)- κ²μ μ λͺ©λ‘μ ν¬κΈ°
-
μ λ ¬ 쑰건 -
sort
(string)- μ΅μ μ:
recent
- μκ°μμ:
student-count
- μ΅μ μ:
μλ΅
{
"courses": [
{
"id": 3,
"category": "μΉ",
"title": "NodeJsμ Express",
"instructorName": "κΉμλͺ
",
"price": 13000,
"studentCount": 0,
"publishedOn": "2024-09-01 15:59:32"
},
{
"id": 1,
"category": "μΉ",
"title": "μ΄κ²μ΄ NodeJsλ€",
"instructorName": "ν₯λ‘",
"price": 65000,
"studentCount": 2,
"publishedOn": "2024-09-01 15:56:39"
}
...
]
}
http://localhost:3000/courses/{}
Path parameterλ‘ κ°μ idλ₯Ό μ
λ ₯νμ¬ κ³΅κ° / λΉκ³΅κ° κ°μλ₯Ό μμΈ μ‘°νν μ μμ΅λλ€.
Path parameterμ νμμ΄ λ§μ§ μμΌλ©΄ μλ¬λ₯Ό λ°νν©λλ€.
EX) <GET> http://localhost:3000/courses/2
μμ² (GET)
- κ°μ id - (integer)
μλ΅
{
"title": "μ΄κ²μ΄ NodeJsλ€",
"description": "κ°μ μκ°λ μ
λλ€.",
"category": "μΉ",
"price": 65000,
"studentCount": 2,
"students": [
{
"nickname": "aaa",
"appliedOn": "2024-09-01 15:57:27"
},
{
"nickname": "spring",
"appliedOn": "2024-09-01 16:11:47"
}
],
"publishedOn": "2024-09-01 15:56:39",
"updatedOn": "2024-09-01 16:11:47"
}
http://localhost:3000/courses
κ°μλ₯Ό λ±λ‘νλ©° μ²μ μμ±μ μλμΌλ‘ λΉκ³΅κ° μνλ‘ μ μ₯λ©λλ€.
μ‘΄μ¬νμ§ μλ κ°μ¬, μ€λ³΅λ κ°μλͺ
, νμμ λ§μ§ μλ κ°μ μ
λ ₯νλ κ²½μ° μλ¬λ₯Ό λ°νν©λλ€.
EX) <POST> http://localhost:3000/courses
μμ² (POST)
{
"instructorId": 2,
"title": "NodeJsμ Express",
"description": "μ΅μ€νλ μ€λ₯Ό μ μ©νμ",
"price": 13000,
"category": "μΉ"
}
- κ°μ¬ id -
instructorId
(integer) - μ λͺ© -
title
(string) - κ°μ μκ° -
description
(string) - κ°κ²© -
price
(integer) - μΉ΄ν
κ³ λ¦¬ -
category
(string)
μλ΅
{
"insertedCourseId": 3
}
- λ±λ‘λ κ°μ id -
insertedCourseId
http://localhost:3000/courses/bulk
κ°μ λ±λ‘κ³Ό λμΌνμ§λ§ νλμ κ°μ¬ idλ‘ 1κ° μ΄μ μ΅λ 10κ°κΉμ§ κ°μλ₯Ό λμμ λ±λ‘ν μ μμ΅λλ€.
λͺ¨λ κ°μ¬ idκ° λμΌνμ§ μκ±°λ, μ€λ³΅λ κ°μλͺ
, μ‘΄μ¬νμ§ μλ κ°μ¬, νμμ λ§μ§ μλ κ°μ μ
λ ₯νλ κ²½μ° μλ¬λ₯Ό λ°νν©λλ€.
EX) <POST> http://localhost:3000/courses/bulk
μμ² (POST)
{
"courses": [
{
"instructorId": 7,
"title": "CS",
"description": "Computer Science",
"price": 6700,
"category": "μΈνλΌ"
},
{
"instructorId": 7,
"title": "Redisλ",
"description": "μΊμκ° μμλ€λ©΄",
"price": 8000,
"category": "λ°μ΄ν°λ² μ΄μ€"
}
]
}
μλ΅
{
"insertedCourseIds": [
4,
5
]
}
http://localhost:3000/courses/{}
ν΄λΉ κ°μ¬κ° κ°μλ₯Ό μμ ν μ μμ΅λλ€.
μ‘΄μ¬νμ§ μλ κ°μ, μΌμΉνμ§ μλ κ°μ¬, μ€λ³΅λ κ°μλͺ
, νμμ λ§μ§ μλ κ°μ μ
λ ₯νλ κ²½μ° μλ¬λ₯Ό λ°νν©λλ€.
EX) <PUT> http://localhost:3000/courses/2
μμ² (PUT)
- κ°μ id - (integer)
{
"instructorId": 2,
"title": "μλ‘μ΄ κ°μλͺ
",
"description": "λ°λ μκ°μ
λλ€.",
"price": 12000
}
μλ΅
{
"updatedCourseId": 2
}
- μμ λ κ°μ id -
updatedCourseId
http://localhost:3000/courses/open/{}
Path parameterλ‘ κ°μ idλ₯Ό μ
λ ₯νμ¬ ν΄λΉ κ°μ¬κ° κ°μλ₯Ό μ€νν μ μμ΅λλ€.
μ‘΄μ¬νμ§ μλ κ°μ, μΌμΉνμ§ μλ κ°μ¬, μ΄λ―Έ μ€νλ κ°μ, νμμ λ§μ§ μλ κ°μ μ
λ ₯νλ κ²½μ° μλ¬λ₯Ό λ°νν©λλ€.
EX) <PUT> http://localhost:3000/courses/open/3
μμ² (PUT)
- κ°μ id - (integer)
{
"instructorId": 2
}
μλ΅
{
"openedCourseId": 3
}
- μ€νλ κ°μ id -
openedCourseId
http://localhost:3000/courses/{}
Path parameterλ‘ κ°μ idλ₯Ό μ
λ ₯νμ¬ ν΄λΉ κ°μ¬κ° κ°μλ₯Ό μμ ν μ μμ΅λλ€.
μ‘΄μ¬νμ§ μλ κ°μ, μΌμΉνμ§ μλ κ°μ¬, μκ°μμ΄ μλ κ°μ, νμμ λ§μ§ μλ κ°μ μ
λ ₯νλ κ²½μ° μλ¬λ₯Ό λ°νν©λλ€.
EX) <DELETE> http://localhost:3000/courses/5
μμ² (DELETE)
- κ°μ id - (integer)
{
"instructorId": 7
}
μλ΅
{
"deletedCourseId": 5
}
- μμ λ κ°μ id -
deletedCourseId
http://localhost:3000/students
μ΄λ©μΌκ³Ό λλ€μμΌλ‘ μκ°μ νμ κ°μ
μ ν©λλ€.
μ€λ³΅λ μ΄λ©μΌ, νμμ λ§μ§ μλ κ°μ μ
λ ₯νλ κ²½μ° μλ¬λ₯Ό λ°νν©λλ€.
EX) <POST> http://localhost:3000/students
μμ² (POST)
{
"email": "[email protected]",
"nickname": "spring"
}
μλ΅
{
"signUpId": 4
}
- κ°μ
λ μκ°μ id -
signUpId
http://localhost:3000/students/{}
Path parameterλ‘ μκ°μ idλ₯Ό μ
λ ₯νμ¬ νν΄λ₯Ό ν μ μμ΅λλ€.
νν΄ λμμ μκ°μμ μκ° λ΄μμ΄ μμ λλ©° κ·Έ μ΄λ©μΌμ λ€λ₯Έ μ¬μ©μκ° μ¬μ¬μ©μ΄ κ°λ₯ν©λλ€.
μ‘΄μ¬νμ§ μλ μκ°μ, νμμ λ§μ§ μλ κ°μ μ
λ ₯νλ κ²½μ° μλ¬λ₯Ό λ°νν©λλ€.
EX) <DELETE> http://localhost:3000/students/4
μμ² (DELETE)
- μκ°μ id - (integer)
μλ΅
{
"removedStudentId": 4
}
- νν΄λ μκ°μ id -
removedStudentId
http://localhost:3000/students/apply-class
κ°μ
λ μκ°μμΌλ‘ 1~N κ°μ κ°μ idλ€μ ν΅ν΄ 곡κ°λ κ°μλ₯Ό λμμ μκ°μ μ²ν μ μμ΅λλ€.
μμ²μ ν¬ν¨λ κ°μλ€μ κ°κ° μ μ²μ μ±κ³΅νκ±°λ μ€ν¨ μ μλ΅μΌλ‘ μ΄μ λ₯Ό μλ €μ€λλ€.
EX) <POST> http://localhost:3000/students/apply-class
μμ² (POST)
{
"studentId": 7,
"courseIds": [1, 2, 3, 4, 5, 6, 7]
}
μλ΅
{
"createdClassIds": [
3,
4,
5
],
"alreadyAppliedCourseIds": [],
"appliedCourseIds": [
3,
2,
1
],
"noExistCourseIds": [
5,
6
],
"noPublicCourseIds": [
7,
4
]
}
- μ μ² μ±κ³΅νμ¬ μμ±λ μκ° λ΄μ id 리μ€νΈ- 'createdClassIds'
- μ΄λ―Έ μκ°μ€μΈ κ°μ id 리μ€νΈ- 'alreadyAppliedCourseIds'
- μ μ² μ 곡ν κ°μ id 리μ€νΈ - 'appliedCourseIds'
- μ‘΄μ¬νμ§ μμ μ μ²νμ§ λͺ»ν κ°μ id 리μ€νΈ- 'noExistCourseIds'
- λΉκ³΅κ° μνλ‘ μ μ²νμ§ λͺ»ν κ°μ id 리μ€νΈ- 'createdClassIds'