μμ리μ€νΈ κ΄λ¦¬λ₯Ό μν λ°±μλ μλ²μ λλ€.
- wishboard-server-v2 μ 보
- λͺ¨λ μ€λͺ
- κ°λ° νκ²½
- API λ¬Έμ
- μ€μ
- ERD
- μν€ν μ²
- λλ ν 리 ꡬ쑰
μ΄ νλ‘μ νΈλ μ¬μ©μκ° μμ리μ€νΈλ₯Ό λ§λ€κ³ κ΄λ¦¬ν μ μλ νλ«νΌμΈ Wishboard μ ν리μΌμ΄μ μ λ°±μλ μλ²μ λλ€. μ¬μ©μ μΈμ¦, λ°μ΄ν° μ μ₯ λ° νλ‘ νΈμλ μ ν리μΌμ΄μ μ μν APIλ₯Ό μ²λ¦¬ν©λλ€. Wishboard μ ν리μΌμ΄μ μ Google Play Store λ° App Storeμμ μ¬μ©ν μ μμ΅λλ€.
GitHub Actions μν¬νλ‘μ°λ₯Ό μ¬μ©νμ¬ CI/CD (μ§μμ ν΅ν©/μ§μμ λ°°ν¬) νμ΄νλΌμΈμ κ΄λ¦¬ν©λλ€. μ΄λ₯Ό ν΅ν΄ μ½λ λ³κ²½ μ¬νμ μλμΌλ‘ λΉλ, ν μ€νΈ λ° λ°°ν¬ν μ μμ΅λλ€.
- μ£Όμ μν¬νλ‘μ°:
deploy-dev.yaml:developλΈλμΉμ νΈμλκ±°λ μλμΌλ‘ νΈλ¦¬κ±°λ λ, λ³κ²½λ λͺ¨λ(api, parsing-api, push)μ λΉλνμ¬ κ°λ° νκ²½(AWS S3)μ λ°°ν¬ν©λλ€.deploy-prod.yaml:mainλΈλμΉμ νΈμλκ±°λ μλμΌλ‘ νΈλ¦¬κ±°λ λ, λ³κ²½λ λͺ¨λμ λΉλνμ¬ μ΄μ νκ²½(AWS S3)μ λ°°ν¬ν©λλ€.
- μ£Όμ μ¬μ© μ‘μ
:
actions/checkout@v3: 리ν¬μ§ν 리 μ½λλ₯Ό 체ν¬μμν©λλ€.dorny/paths-filter@v3: νΉμ κ²½λ‘μ νμΌ λ³κ²½ μ¬νμ κ°μ§νμ¬ μ‘°κ±΄λΆλ‘ μν¬νλ‘μ° λ¨κ³λ₯Ό μ€νν©λλ€.actions/setup-java@v3: Java (Corretto JDK) νκ²½μ μ€μ ν©λλ€. (api λͺ¨λ)actions/setup-node@v1: Node.js νκ²½μ μ€μ ν©λλ€. (parsing-api, push λͺ¨λ)aws-actions/configure-aws-credentials@v1: AWS μ격 μ¦λͺ μ ꡬμ±νμ¬ S3 λ±μ μ κ·Όν μ μλλ‘ ν©λλ€.
Spring Boot κΈ°λ°μΌλ‘ ꡬμΆλ λ©μΈ λ°±μλ API μλ²μ λλ€. Wishboard μ ν리μΌμ΄μ μ ν΅μ¬ λΉμ¦λμ€ λ‘μ§μ μ²λ¦¬ν©λλ€.
- μ£Όμ κΈ°λ₯: μ¬μ©μ μΈμ¦, μμ리μ€νΈ μμ΄ν κ΄λ¦¬, ν΄λ κ΄λ¦¬, μ₯λ°κ΅¬λ, μλ¦Ό μ€μ λ±
- κΈ°μ μ€ν λ° μμ‘΄μ±:
- μΈμ΄/νλ μμν¬: Java 21, Spring Boot 3.3.3, Spring Security, Spring Data JPA
- λ°μ΄ν°λ² μ΄μ€: MySQL
- μΊμ: Redis, Caffeine (λ‘컬 μΊμ)
- κ²μ λ° ORM: QueryDSL
- API λ¬Έμν: Swagger (OpenAPI)
- ν΄λΌμ°λ μλΉμ€: AWS S3 (μ΄λ―Έμ§ λ± νμΌ μ μ₯)
- μΈμ¦: JWT (JSON Web Token)
- λͺ¨λν°λ§ λ° λ‘κΉ : Sentry, Spring Boot Actuator, Micrometer (Prometheus), Logback
- λΉλ λꡬ: Gradle
- κΈ°ν: ModelMapper, Spring WebFlux (λΆλΆμ μ¬μ©), Spring Mail
- μν€ν
μ²:
- λλ©μΈ μ£Όλ μ€κ³(DDD) κΈ°λ°: ν΅μ¬ λΉμ¦λμ€ λ‘μ§μ λλ©μΈ λͺ¨λΈμ μ§μ€μμΌ λ³΅μ‘μ±μ κ΄λ¦¬ν©λλ€.
- ν¨ν€μ§ ꡬ쑰: κΈ°λ₯μ λͺ¨λν(μ:
auth,item,folder,userλ±)λ₯Ό λ°λ₯΄λ©°, κ° λͺ¨λμ DDDμ κ³μΈ΅ν μν€ν μ² μ€νμΌμ λ°μν©λλ€.com.wishboard.server.<λλ©μΈλͺ >.presentation:Controller: HTTP μμ²μ λ°μ Application Serviceμ μ²λ¦¬λ₯Ό μμνκ³ , κ²°κ³Όλ₯Ό HTTP μλ΅μΌλ‘ λ°νν©λλ€. (μ:ItemController.java)dto: νλ μ ν μ΄μ κ³μΈ΅μμ μ¬μ©νλ λ°μ΄ν° μ μ‘ κ°μ²΄ (μμ²/μλ΅ DTO)λ₯Ό μ μν©λλ€.docs: API λ¬Έμν κ΄λ ¨ ν΄λμ€ (Swagger λ±)λ₯Ό ν¬ν¨ν μ μμ΅λλ€.
com.wishboard.server.<λλ©μΈλͺ >.application:service: μ ν리μΌμ΄μ μ μ μ€μΌμ΄μ€λ₯Ό ꡬνν©λλ€. λλ©μΈ κ°μ²΄μ μΈνλΌμ€νΈλμ² κ³μΈ΅μ μ‘°μ νμ¬ λΉμ¦λμ€ λ‘μ§μ μνν©λλ€. (μ:ItemService.java)dto: μ ν리μΌμ΄μ μλΉμ€μμ μ¬μ©νλ λ°μ΄ν° μ μ‘ κ°μ²΄λ₯Ό μ μν©λλ€.
com.wishboard.server.<λλ©μΈλͺ >.domain:model: λλ©μΈ μν°ν°, κ° κ°μ²΄(VO) λ± ν΅μ¬ λλ©μΈ λͺ¨λΈμ μ μν©λλ€. (μ:Item.java,Folder.java)repository: λλ©μΈ κ°μ²΄μ μμμ±μ μν μΈν°νμ΄μ€λ₯Ό μ μν©λλ€. (μ:ItemRepository.java)
com.wishboard.server.<λλ©μΈλͺ >.infrastructure:- 리ν¬μ§ν 리μ μ€μ ꡬν체(JPA, QueryDSL λ± νμ©), μΈλΆ μλΉμ€(S3, λ©μΌ μλ² λ±)μμ μ°λ λ‘μ§ λ±μ ν¬ν¨ν©λλ€. (μ:
ItemRepositoryImpl.java,S3FileStorageClient.java)
- 리ν¬μ§ν 리μ μ€μ ꡬν체(JPA, QueryDSL λ± νμ©), μΈλΆ μλΉμ€(S3, λ©μΌ μλ² λ±)μμ μ°λ λ‘μ§ λ±μ ν¬ν¨ν©λλ€. (μ:
- API: RESTful APIλ₯Ό ν΅ν΄ ν΄λΌμ΄μΈνΈ(λͺ¨λ°μΌ μ± λ±)μ ν΅μ ν©λλ€.
- μμΈ μ²λ¦¬:
@ControllerAdviceλ₯Ό μ¬μ©νμ¬ μ μμ μΌλ‘ μμΈλ₯Ό μ²λ¦¬νκ³ , μΌκ΄λ μ€λ₯ μλ΅ νμμ μ 곡ν©λλ€. - κ³΅ν΅ κ΄μ¬μ¬: Spring AOPλ₯Ό νμ©νμ¬ λ‘κΉ , νΈλμμ κ΄λ¦¬ λ± κ³΅ν΅ κ΄μ¬μ¬λ₯Ό λͺ¨λννμ¬ μ²λ¦¬ν©λλ€.
- μ€μ λ° κ³΅ν΅ μ νΈλ¦¬ν°:
com.wishboard.server.configν¨ν€μ§μμ κ°μ’ μ€μ (보μ, Swagger, Redis, S3 λ±)μ κ΄λ¦¬νκ³ ,com.wishboard.server.commonν¨ν€μ§μμ 곡ν΅μ μΌλ‘ μ¬μ©λλ μ νΈλ¦¬ν°, μμΈ ν΄λμ€, μλ΅ DTO λ±μ μ 곡ν©λλ€.
Node.js λ° Express κΈ°λ°μΌλ‘ ꡬμΆλ API μλ²λ‘, μ 곡λ URLλ‘λΆν° μν μ 보λ₯Ό νμ±(μ€ν¬λν)νλ μν μ λ΄λΉν©λλ€.
- μ£Όμ κΈ°λ₯: μΈλΆ μΌνλͺ° μν νμ΄μ§ URLμ μ λ ₯λ°μ μνλͺ , κ°κ²©, μ΄λ―Έμ§ URL λ±μ μ 보λ₯Ό μΆμΆνμ¬ λ°νν©λλ€.
- κΈ°μ μ€ν λ° μμ‘΄μ±:
- μΈμ΄/νλ μμν¬: Node.js (18.x), Express.js
- μΉ μ€ν¬λν: Cheerio (HTML νμ±), Axios (HTTP μμ²)
- λ‘κΉ : Winston, Morgan
- 보μ: Helmet, HPP
- μμ² μ ν: express-rate-limit
- λΉλ λꡬ: Webpack
- κΈ°ν: dotenv, app-root-path
- μν€ν
μ²:
- νΉμ μλν¬μΈνΈ (
/item/parse)λ‘ μμ²μ λ°μΌλ©΄, ν΄λΉ URLμ HTMLμ κ°μ Έμ Cheerioλ₯Ό μ¬μ©νμ¬ λ©ν νκ·Έ(Open Graph λ±) λ° μ£Όμ HTML μμμμ μν μ 보λ₯Ό μΆμΆν©λλ€. - μ€λ₯ μ²λ¦¬ λ° λ‘κΉ λ―Έλ€μ¨μ΄λ₯Ό μ¬μ©νμ¬ μμ μ±μ λμ λλ€.
- νΉμ μλν¬μΈνΈ (
Node.js λ° Express κΈ°λ°μΌλ‘ ꡬμΆλ μλ²λ‘, μ¬μ©μμκ² λ€μν 쑰건μ λ°λΌ νΈμ μλ¦Όμ μ μ‘νλ μν μ λ΄λΉν©λλ€.
- μ£Όμ κΈ°λ₯: μν κ°κ²© λ³λ μλ¦Ό, μ¬μ κ³ μλ¦Ό, ν΄λ μμ΄ν κ΄λ ¨ μλ¦Ό λ± μ¬μ©μ μ€μ μ λ°λ₯Έ νΈμ λ©μμ§ λ°μ‘.
- κΈ°μ μ€ν λ° μμ‘΄μ±:
- μΈμ΄/νλ μμν¬: Node.js (18.x), Express.js
- νΈμ μλ¦Ό: Firebase Admin SDK (FCM - Firebase Cloud Messaging)
- μ€μΌμ€λ§: node-schedule (μ£ΌκΈ°μ μΈ μλ¦Ό μμ μν)
- λ°μ΄ν°λ² μ΄μ€: MySQL2 (μλ¦Ό λμ μ¬μ©μ λ° μν μ 보 μ‘°ν)
- λ‘κΉ : Winston, Morgan
- 보μ: Helmet, HPP
- λΉλ λꡬ: Webpack
- κΈ°ν: dotenv, app-root-path, axios
- μν€ν
μ²:
node-scheduleμ μ¬μ©νμ¬ νΉμ μκ° κ°κ²©μΌλ‘ μλ¦Ό 쑰건μ νμΈνκ³ , 쑰건μ λΆν©νλ μ¬μ©μμκ² FCMμ ν΅ν΄ νΈμ μλ¦Όμ μ μ‘ν©λλ€.- API μλν¬μΈνΈλ₯Ό ν΅ν΄ μ¦μ μλ¦Όμ νΈλ¦¬κ±°ν μλ μμ΅λλ€. (μ: νΉμ μ΄λ²€νΈ λ°μ μ)
- MySQL λ°μ΄ν°λ² μ΄μ€μ μ°λνμ¬ μλ¦Ό λμ λ° κ΄λ ¨ λ°μ΄ν°λ₯Ό κ΄λ¦¬ν©λλ€.
- Core Stack (api λͺ¨λ κΈ°μ€):
- Java 21
- Spring Boot 3.3.3
- Gradle
- Core Stack (parsing-api, push λͺ¨λ κΈ°μ€):
- Node.js 18.x
- Express.js
- Webpack
- Database & Cache:
- MySQL
- Redis
- Cloud Services:
- AWS S3 (for file storage)
- API & Documentation:
- Swagger (OpenAPI) - for
apimodule
- Swagger (OpenAPI) - for
- DevOps & Monitoring:
- Docker (λ‘컬 κ°λ° νκ²½)
- Sentry (μλ¬ νΈλνΉ)
- Spring Boot Actuator (μ ν리μΌμ΄μ
λͺ¨λν°λ§ -
apimodule) - Micrometer (Prometheus) (λ©νΈλ¦ μμ§ -
apimodule) - Winston (λ‘κΉ
-
parsing-api,pushmodules)
λ©μΈ API μλ²(api λͺ¨λ)μ API λ¬Έμλ Swagger (OpenAPI)λ₯Ό μ¬μ©νμ¬ μμ±λ©λλ€.
μ ν리μΌμ΄μ
μ€ν ν λ€μ μ£Όμμμ νμΈν μ μμ΅λλ€:
http://localhost:8080/swagger-ui.html
(μ ν리μΌμ΄μ
μ΄ λ€λ₯Έ ν¬νΈμμ μ€νλλ κ²½μ° ν¬νΈλ₯Ό μ‘°μ νμμμ€.)
μ ν리μΌμ΄μ μ€μ (λ°μ΄ν°λ² μ΄μ€ μ°κ²°, AWS μ격 μ¦λͺ , Redis νΈμ€νΈ/ν¬νΈ λ±)μ λ€μ νμΌμ ν΅ν΄ κ΄λ¦¬λ©λλ€:
- api λͺ¨λ:
api/src/main/resources/application.yml(λ°application-{profile}.ymlνμΌλ€) - parsing-api λͺ¨λ:
parsing-api/.env - push λͺ¨λ:
push/.env
λ°μ΄ν°λ² μ΄μ€ μ€ν€λ§ λ° μν°ν° κ΄κ³μ λν μμΈν λ΄μ©μ μ¬κΈ°μ λ¬Έμνλ μμ μ λλ€.
μμ€ν μν€ν μ², κ΅¬μ± μμ λ° μνΈ μμ©μ λν κ°μλ μ¬κΈ°μ μ 곡λ μμ μ λλ€. (κ° λͺ¨λλ³ μν€ν μ²λ λͺ¨λ μ€λͺ μΉμ μ°Έκ³ )
wishboard-server-v2/
βββ .github/ # GitHub Actions μν¬νλ‘μ° (CI/CD)
β βββ workflows/
β βββ deploy-dev.yaml # κ°λ° νκ²½ λ°°ν¬ μν¬νλ‘μ°
β βββ deploy-prod.yaml # μ΄μ νκ²½ λ°°ν¬ μν¬νλ‘μ°
βββ api/ # λ©μΈ API μλ² (Spring Boot)
β βββ build.gradle
β βββ src/main/java/ # Java μμ€ μ½λ
β βββ src/main/resources/ # μ€μ νμΌ, μ μ 리μμ€
βββ parsing-api/ # μν μ 보 νμ± API μλ² (Node.js)
β βββ package.json
β βββ src/ # JavaScript μμ€ μ½λ
β βββ webpack.config.js
βββ push/ # νΈμ μλ¦Ό μλ² (Node.js)
β βββ package.json
β βββ src/ # JavaScript μμ€ μ½λ
β βββ webpack.config.js
βββ README.md # νλ‘μ νΈ κ°μ λ° λ¬Έμ
βββ deploy.sh # λ°°ν¬ μ€ν¬λ¦½νΈ (EC2 λ΄λΆμμ μ¬μ©)
βββ v2-pm2-run-dev.js # PM2 κ°λ° νκ²½ μ€ν μ€μ
βββ v2-pm2-run-prod.js # PM2 μ΄μ νκ²½ μ€ν μ€μ
...
(μ΄κ²μ λ¨μνλ ννμ
λλ€. μ€μ ꡬ쑰μ λν μμΈν λ΄μ©μ ls() λͺ
λ Ή μΆλ ₯μ μ°Έμ‘°νκ±°λ 'tree'μ κ°μ λꡬλ₯Ό μ¬μ©νμ¬ μμ±νμμμ€.)
μ λ¬Έμλ google λΉλκΈ° μ½λ© μμ΄μ νΈ julesκ° μμ±νμ΅λλ€.