Skip to content

team-I5/order-platform

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

456 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

์ฃผ๋ฌธ ๊ด€๋ฆฌ ํ”Œ๋žซํผ

ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

์‚ฌ์šฉ์ž ยท ๊ฐ€๊ฒŒ ยท ์ƒํ’ˆ ยท ์ฃผ๋ฌธ ยท ๊ฒฐ์ œ ๋“ฑ ํ•ต์‹ฌ ๋„๋ฉ”์ธ์„ ์ค‘์‹ฌ์œผ๋กœ ์„ค๊ณ„ํ•œ ์Œ์‹์  ์ฃผ๋ฌธ ๊ด€๋ฆฌ ํ”Œ๋žซํผ

4๊ณ„์ธต ์•„ํ‚คํ…์ฒ˜(Controllerโ€“Serviceโ€“Domainโ€“Infrastructure)์ ์šฉ, DDD(Domain-Driven Design) ๊ด€์ ์—์„œ ๋„๋ฉ”์ธ ๋กœ์ง์„ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•˜์—ฌ ์‘์ง‘๋„ ๊ฐ•ํ™”
OCP(๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™) ์™€ DIP(์˜์กด ์—ญ์ „ ์›์น™) ์„ ์ ์šฉํ•˜์—ฌ ์ธํ”„๋ผ ๊ณ„์ธต์˜ ๊ธฐ์ˆ  ์˜์กด์„ฑ์„ ์ตœ์†Œํ™”ํ•˜๊ณ , ํ™•์žฅ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๊ฐ•ํ™”


๊ฐœ๋ฐœ ํ™˜๊ฒฝ

๋ถ„๋ฅ˜ ์ƒ์„ธ
IDE IntelliJ
Language Java 17
Framework Spring Boot 3.5.5, Spring Data JPA, Spring Security + JWT, Validation, Lombok
Database PostgreSQL 14
Build Tool Gradle
Development Tool Swagger, Postman, Mockito
Collaboration Discord, Notion, Zep

ERD

Image

๋„๋ฉ”์ธ ๋‹ค์ด์–ด๊ทธ๋žจ

Image


ํ”„๋กœ์ ํŠธ ์‹คํ–‰ ๊ฐ€์ด๋“œ

ํ™˜๊ฒฝ ์„ค์ •

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

#
Postgresql ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ
CREATE
DATABASE orderplatform;

2. ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ • (.env.example)

# ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •
POSTGRES_DB=your_database_name
POSTGRES_USER=your_postgres_user
POSTGRES_PASSWORD=your_postgres_password
POSTGRES_PORT=5432

# ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ •
APP_PORT=8080
SPRING_PROFILES_ACTIVE=docker
SPRING_APPLICATION_NAME=order-platform

# JWT ์„ค์ • (๊ฐ•๋ ฅํ•œ ํ‚ค๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”)
JWT_SECRET=your_jwt_secret_key_here_at_least_32_characters_long
JWT_ACCESS_TOKEN_EXPIRATION=900000
JWT_REFRESH_TOKEN_EXPIRATION=604800000

# Google Gemini AI ์„ค์ • (๋ณธ์ธ์˜ API ํ‚ค๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”)
GOOGLE_GEMINI_API_KEY=your_google_gemini_api_key_here
GOOGLE_GEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent

# ๋กœ๊น… ๋ ˆ๋ฒจ
LOGGING_LEVEL_ROOT=INFO
LOGGING_LEVEL_APP=DEBUG
LOGGING_LEVEL_SECURITY=DEBUG
LOGGING_LEVEL_SQL=DEBUG
LOGGING_LEVEL_SQL_BINDER=TRACE

# ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„
POSTGRES_CONTAINER_NAME=order-platform-db
APP_CONTAINER_NAME=order-platform-app

# ๋„คํŠธ์›Œํฌ
NETWORK_NAME=order-platform-network

3. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰

# docker
# ์ด๋ฏธ์ง€ ๋นŒ๋“œ ํ›„ ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ (๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ชจ๋“œ)
docker compose up -d --build
# ์ปจํ…Œ์ด๋„ˆ ์ƒํƒœ ํ™•์ธ
docker compose ps
#์˜ˆ์‹œ)
#  NAME                     COMMAND                  STATE   PORTS
#orderplatform-postgres   "docker-entrypoint.sโ€ฆ"   Up      0.0.0.0:5432->5432/tcp
#orderplatform-app        "java -jar app.jar"      Up      0.0.0.0:8080->8080/tcp

# gradle
./gradlew bootRun

4. docker-compose

๐Ÿ“„ docker-compose.yml

5. ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์œ ์ € ๋ชฉ๋ก

์œ ์ € ๋ชฉ๋ก / ๋น„๋ฐ€๋ฒˆํ˜ธ: Asd123456!
-------------------
Customer
hong@test.com
oh@test.com
-------------------
Owner
lee@test.com
lucas@test.com
-------------------
Master
mk@test.com

API

API ์ด 66๊ฐœ (์„ค๊ณ„ ๋Œ€๋น„ ๊ตฌํ˜„๋ฅ  : 100 %)

API ๊ฐœ์ˆ˜ API ๊ฐœ์ˆ˜
์‚ฌ์šฉ์ž (User) 8๊ฐœ ์ธ์ฆ ๋ฐ ํ† ํฐ(Auth) 1๊ฐœ
์ฃผ์†Œ(Address) 4๊ฐœ ์Œ์‹์ (Store) 13๊ฐœ
์ƒํ’ˆ (Product) 9๊ฐœ ์ƒํ’ˆ ์„ค๋ช…(AI) 1๊ฐœ
์ƒํ’ˆ ์˜ต์…˜(Option) 6๊ฐœ ์ฃผ๋ฌธ(Order) 7๊ฐœ
๊ฒฐ์ œ(Payment) 5๊ฐœ ๋ฆฌ๋ทฐ(Review) 7๊ฐœ
์นดํ…Œ๊ณ ๋ฆฌ(Category) 5๊ฐœ

์ƒ์„ธ ๋‚ด์šฉ

๐ŸงพAPI ๋ช…์„ธ - Notion


์ธํ”„๋ผ ์„ค๊ณ„๋„

Image

Conventions

๋„ค์ด๋ฐ ๊ทœ์น™

๊ตฌ๋ถ„ ๊ทœ์น™ ์˜ˆ์‹œ
ํด๋ž˜์Šค PascalCase UserService, OrderController
๋ณ€์ˆ˜ / ๋ฉ”์„œ๋“œ camelCase StoreName, calculateTotal()
ํŒจํ‚ค์ง€ ์†Œ๋ฌธ์ž, ๊ธฐ๋Šฅ๋ณ„ ๊ตฌ๋ถ„ com.spartaclub.orderplatform.domain
DB ํ…Œ์ด๋ธ” snake_case + p_์ ‘๋‘์‚ฌ p_user, p_category
DB ์ปฌ๋Ÿผ snake_case created_at, store_address

์ฝ”๋“œ ์Šคํƒ€์ผ

๐Ÿ“java-google ์ฝ”๋“œ ์Šคํƒ€์ผ

Package Structure

main.java.com.spartaclub.orderplatform
โ”œโ”€ domain
โ”‚    โ”œโ”€ ai
โ”‚    โ”‚   โ”œโ”€ application
โ”‚    โ”‚   โ”œโ”€ domain
โ”‚    โ”‚   โ”œโ”€ infrastructure
โ”‚    โ”‚   โ””โ”€ presentation
โ”‚    โ”‚
โ”‚    โ”œโ”€ category
โ”‚    โ”‚   โ”œโ”€ application
โ”‚    โ”‚   โ”œโ”€ domain
โ”‚    โ”‚   โ”œโ”€ exception
โ”‚    โ”‚   โ”œโ”€ infrastructure
โ”‚    โ”‚   โ””โ”€ presentation
โ”‚    โ”‚
โ”‚    โ”œโ”€ order
โ”‚    โ”‚   โ””โ”€ ...
โ”‚    โ”‚
โ”‚    โ”œโ”€ payment
โ”‚    โ”‚   โ””โ”€ ...
โ”‚    โ”‚
โ”‚    โ”œโ”€ product
โ”‚    โ”‚   โ””โ”€ ...
โ”‚    โ”‚
โ”‚    โ”œโ”€ review
โ”‚    โ”‚   โ””โ”€ ...
โ”‚    โ”‚
โ”‚    โ”œโ”€ store
โ”‚    โ”‚   โ””โ”€ ...
โ”‚    โ”‚
โ”‚    โ””โ”€ user
โ”‚        โ””โ”€ ...
โ”‚
โ””โ”€ global
     โ”œโ”€ application.security
     โ”‚
     โ”œโ”€ auth
     โ”‚   โ”œโ”€ exception
     โ”‚   โ”œโ”€ handler
     โ”‚   โ”œโ”€ jwt
     โ”‚   โ””โ”€ sevice
     โ”‚
     โ”œโ”€ config
     โ”‚   โ”œโ”€ auditing
     โ”‚   โ”œโ”€ security
     โ”‚   โ””โ”€ web
     โ”‚
     โ”œโ”€ domain.entity
     โ”‚
     โ”œโ”€ exception
     โ”‚   โ””โ”€ advice
     โ”‚
     โ”œโ”€ infrastructure.config.swagger
     โ”‚
     โ”œโ”€ presentation.dto
     โ”‚
     โ””โ”€ util

ํ•ต์‹ฌ ๊ธฐ๋Šฅ

(ํ•œ๊ฒฐ)

image image

AI API ์š”์ฒญ ๋ฐ ์‘๋‹ต ์ €์žฅ ํ๋ฆ„ ์„ค๊ณ„

image image ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด AI์— ์‘๋‹ต ์ƒ์„ฑ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ๋ฐ›์€ ๊ฐ’์„ ์บ์‹œ์— ์ €์žฅ, ์บ์‹œ๋Š” userId๋กœ ๊ตฌ๋ถ„ ์ƒํ’ˆ์ด ์ €์žฅ๋œ ํ›„ ์บ์‹œ๋ฅผ DB์— ์ €์žฅ, ์ €์žฅ๋œ ์ƒํ’ˆ ์„ค๋ช…๊ณผ ์บ์‹œ์˜ ๋งˆ์ง€๋ง‰์— ์ €์žฅ๋œ ์‘๋‹ต์ด ์ผ์น˜ํ•˜๋ฉด ๋กœ๊ทธ์˜ ์ƒํƒœ๋ฅผ USED๋กœ ๋ณ€๊ฒฝ ํ›„ DB์— ์ €์žฅ

์‚ฌ์šฉ์ž ๊ถŒํ•œ ๋ณ„๋กœ ๋ฆฌ๋ทฐ ๋ชฉ๋ก ์กฐํšŒ๊ฐ€ ๋‹ฌ๋ผ์ง€๋„๋ก ๊ตฌํ˜„

image
  • CUSTOMER ๊ถŒํ•œ โ†’ ํ•ด๋‹น ๊ณ ๊ฐ์ด ์ž‘์„ฑํ•œ ๋ฆฌ๋ทฐ ๋ชฉ๋ก ์กฐํšŒ
  • OWNER ๊ถŒํ•œ โ†’ ๋ณธ์ธ ์†Œ์œ  ๊ฐ€๊ฒŒ ๊ธฐ์ค€ ๋ฆฌ๋ทฐ ๋ชฉ๋ก ์กฐํšŒ
  • MANAGER, MASTER ๊ถŒํ•œ โ†’ ์‚ญ์ œ๋˜์ง€ ์•Š์€ ๋ชจ๋“  ๋ชฉ๋ก ์กฐํšŒ

N+1 ๋ฌธ์ œ ์—†์ด ํšจ์œจ์ ์œผ๋กœ ํ‰๊ท  ํ‰์ ๊ณผ ๋ฆฌ๋ทฐ ์ˆ˜ ๊ฐฑ์‹ 

Image

์Œ์‹์ ์˜ ๋ฆฌ๋ทฐ ์ˆ˜์™€ ํ‰๊ท  ํ‰์ ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณ„์‚ฐํ•  ํ•„์š”๋Š” ์—†๋‹ค๊ณ  ํŒ๋‹จํ•ด 3์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์œผ๋กœ ๊ฐฑ์‹ ๋˜๋„๋ก ์„ค์ •

ํ‰๊ท  ํ‰์ ๊ณผ ๋ฆฌ๋ทฐ ์ˆ˜๋ฅผ ํ•œ๋ฒˆ์˜ ์ฟผ๋ฆฌ๋กœ ๊ณ„์‚ฐํ•˜๊ณ , ํ•ด๋‹น ๊ฒฐ๊ณผ๋ฅผ ๋งคํ•‘ํ•ด ๊ฐ ์Œ์‹์  ์—”ํ‹ฐํ‹ฐ์— ๋ฐ˜์˜

์‹ค์‹œ๊ฐ„ ๊ณ„์‚ฐ ๋Œ€์‹  ์Šค์ผ€์ค„๋ง์„ ์‚ฌ์šฉํ•ด ์„ฑ๋Šฅ ๋ถ€๋‹ด ๊ฐ์†Œ
์ง‘๊ณ„ ์ฟผ๋ฆฌ + ์ผ๊ด„ ์—…๋ฐ์ดํŠธ๋กœ ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ์„ ์ค„์ด๊ณ  N+1 ๋ฌธ์ œ ๋ฐฉ์ง€

๊ธฐ๋ณธ ์ฃผ์†Œ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ(Address)

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2025-10-17 แ„‹แ…ฉแ„’แ…ฎ 10 02 33 แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2025-10-17 แ„‹แ…ฉแ„’แ…ฎ 9 45 30
  • ์ž๋™ ๊ธฐ๋ณธ ์ฃผ์†Œ ์„ค์ •: ์‚ฌ์šฉ์ž์˜ ์ฒซ ๋ฒˆ์งธ ์ฃผ์†Œ๋Š” ์ž๋™์œผ๋กœ ๊ธฐ๋ณธ ์ฃผ์†Œ๋กœ ์„ค์ •ํ•˜์—ฌ ์‚ฌ์šฉ์ž ํŽธ์˜์„ฑ ๊ทน๋Œ€ํ™”
  • ๊ธฐ๋ณธ ์ฃผ์†Œ ์œ ์ผ์„ฑ ๋ณด์žฅ: ์ƒˆ๋กœ์šด ๊ธฐ๋ณธ ์ฃผ์†Œ ๋“ฑ๋ก ์‹œ ๊ธฐ์กด ๊ธฐ๋ณธ ์ฃผ์†Œ๋ฅผ ์ž๋™์œผ๋กœ ํ•ด์ œํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€
  • ์ตœ์†Œ ์ฃผ์†Œ ๋ณด์žฅ: ์‚ฌ์šฉ์ž๋Š” ์ตœ์†Œ 1๊ฐœ์˜ ํ™œ์„ฑ ์ฃผ์†Œ๋ฅผ ํ•„์ˆ˜๋กœ ๋ณด์œ ํ•˜๋„๋ก ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์„ ๊ฐ•์ œ ์ ์šฉ
  • ๊ธฐ๋ณธ ์ฃผ์†Œ ์—ฐ์†์„ฑ: ๊ธฐ๋ณธ ์ฃผ์†Œ ์‚ญ์ œ ์‹œ ๊ฐ€์žฅ ์ตœ๊ทผ ์ƒ์„ฑ๋œ ์ฃผ์†Œ๋กœ ์ž๋™ ์žฌํ• ๋‹นํ•˜์—ฌ ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ์—ฐ์†์„ฑ ๋ณด์žฅ

์ธ์ฆ ยท ์ธ๊ฐ€ ํ๋ฆ„์— ๋Œ€ํ•œ ๋„์‹ํ™”


ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

์›ํ”Œ๋กœ์šฐ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ โ†’ ๋‹จ๊ณ„๋ณ„ API ๋ถ„๋ฆฌ

๋ฌธ์ œ์ƒํ™ฉ

์ฃผ๋ฌธ ์ƒ์„ฑ ํ›„ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์‹œ @TransactionalEventListener(phase = AFTER_COMMIT)๋กœ ๊ฒฐ์ œ ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ ๋ฐœํ–‰ โ†’ ๊ฒฐ์ œ ์ƒ์„ฑ ๋ฐ ์Šน์ธ ์ฒ˜๋ฆฌ

๋ฌธ์ œ์ 

์ด๋ฒคํŠธ ์œ ์‹คยท์ค‘๋ณต ์œ„ํ—˜, ์žฌ์‹œ๋„ ์‹œ ์ •ํ™•ํ•œ ์ƒํƒœ ๋ณด์žฅ ์–ด๋ ค์›€
์ฃผ๋ฌธ-๊ฒฐ์ œ ๋„๋ฉ”์ธ ๊ฐ„ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ โ†’ ์š”์ฒญ/์‘๋‹ต ํ๋ฆ„ ๋ณต์žก, ์‘๋‹ต ์ง€์—ฐ ์ฆ๊ฐ€

๊ฐœ์„  ๋ฐฉํ–ฅ

API๋ฅผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•˜๊ณ  ์ƒํƒœ ์ „์ด๋ฅผ ๊ฐ ๋„๋ฉ”์ธ์— ์‘์ง‘ โ†’ ๋„๋ฉ”์ธ ๊ฐ„ ์˜์กด์„ฑ ์•ฝํ™” ๊ฒฐํ•ฉ๋„โ†“ ์‘์ง‘๋„โ†‘, ์‘๋‹ต ์†๋„ ๋‹จ์ถ•, ์žฅ์•  ๊ฒฉ๋ฆฌ์„ฑ ๊ฐœ์„ 

AI ์š”์ฒญ ๋กœ๊ทธ ์ €์žฅ ๋ฐฉ์‹ ์ตœ์ ํ™”

๋ฌธ์ œ ์ƒํ™ฉ

AI ์ƒํ’ˆ ์„ค๋ช… ์š”์ฒญ ์‹œ๋งˆ๋‹ค ๋กœ๊ทธ๋ฅผ DB์— ์ฆ‰์‹œ ์ €์žฅํ•˜๋„๋ก ์„ค๊ณ„
์š”์ฒญ์„ ํ•  ๋•Œ ๋งˆ๋‹ค ์ด์ „ ๋กœ๊ทธ์˜ ์ƒํƒœ๋ฅผ USED -> NO_USE๋กœ ์ˆ˜์ •
๊ฒฐ๊ณผ์ ์œผ๋กœ, ๋‹จ์ผ ์ƒํ’ˆ ์ƒ์„ฑ ์‹œ ์—ฌ๋Ÿฌ ๋ฒˆ์˜ update ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒ

์›์ธ

๋ชจ๋“  ์š”์ฒญ์„ ์ฆ‰์‹œ DB์— ๋ฐ˜์˜ํ•˜๋Š” ๊ตฌ์กฐ
์ƒํ’ˆ ์ €์žฅ ์‹œ์ ๊นŒ์ง€ โ€œ์ž„์‹œ ์ƒํƒœโ€์ž„์—๋„ ์ง€์†์ ์œผ๋กœ DB์— ์ ‘๊ทผ

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์š”์ฒญ ๋กœ๊ทธ๋ฅผ ์บ์‹œ์— ์ž„์‹œ ์ €์žฅํ•˜๋„๋ก ๋ฆฌํŒฉํ† ๋ง

  1. ๋ชจ๋“  ๋กœ๊ทธ๋ฅผ ์บ์‹œ์— NO_USE ์ƒํƒœ๋กœ ์ €์žฅ
  2. ์ƒํ’ˆ ์ €์žฅ ํ›„ ์บ์‹œ์— ์ €์žฅ๋œ ํ•ด๋‹น ์ƒํ’ˆ์˜ ๋กœ๊ทธ ์ค‘ ๋งˆ์ง€๋ง‰ ์š”์ฒญ์„ ๋น„๊ต
  3. ์ƒํ’ˆ์— ์ €์žฅ๋œ ์„ค๋ช…๊ณผ ๋กœ๊ทธ์˜ ์‘๋‹ต ์„ค๋ช…์ด ๊ฐ™์œผ๋ฉด USED๋กœ ๋ณ€๊ฒฝ
  4. DB์— ๋กœ๊ทธ ์ €์žฅ ํ›„ ์บ์‹œ ๋น„์›€

Mapstruct ๋งตํ•‘ ์ค‘ ๋ฐœ์ƒํ•œ Bean ๋ฌธ์ œ ํ•ด๊ฒฐ

๋ฌธ์ œ ์ƒํ™ฉ

Entity ์ƒ์„ฑํ•  ๋•Œ Mapstruct๋ฅผ ์‚ฌ์šฉํ•ด์„œ requestDto์—์„œ ๋ฐ›์•„์˜จ ๊ฐ’๋“ค์„ service์—์„œ toEntity ๋ฉ”์„œ๋“œ ํ†ตํ•ด entity๋กœ ์ „๋‹ฌ ํ•˜๋Š”๋ฐ Bean๊ด€๋ จ ์˜ค๋ฅ˜ ๋ฐœ์ƒ

์›์ธ

Entity ๊ฐ์ฒด๋กœ ์ •๋ณด ์ „๋‹ฌํ•  ๋•Œ ์˜จ์ „ํ•˜๊ฒŒ ์™ธ๋ž˜ ํ‚ค ๊ด€๊ณ„ Entity ๊ฐ์ฒด ์ƒ์„ฑ ๋ชปํ•ด์„œ ๋งตํ•‘ ์ค‘ ๋ฌธ์ œ ๋ฐœ์ƒํ•œ ๊ฒƒ์œผ๋กœ ์ถ”์ธก

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์™ธ๋ž˜ ํ‚ค์™€ findById ๋ฉ”์„œ๋“œ ํ™œ์šฉํ•ด ์™ธ๋ž˜ ํ‚ค ์—”ํ‹ฐํ‹ฐ ๋ฐ›์•„์™€ ์ƒ์„ฑํ•  ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ์— ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„ฃ์–ด ์ „๋‹ฌํ•œ ํ›„,
Entity ํด๋ž˜์Šค์—์„œ ์˜จ์ „ํ•œ ํ•˜๋‚˜์˜ ํ–‰์œผ๋กœ DB์— ๋ฐ˜์˜๋˜๋„๋ก ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด ์ƒ์„ฑํ•˜๋„๋ก ํ•˜์—ฌ ํ•ด๊ฒฐ

N+1 ๋ฌธ์ œ ์˜ˆ๋ฐฉ ๋ฐ ๋ฆฌ๋ทฐ ์ง‘๊ณ„ ์Šค์ผ€์ค„๋ง

๋ฌธ์ œ์ƒํ™ฉ

์Œ์‹์  ๋ชฉ๋ก ์กฐํšŒ ์‹œ ๊ฐ ์Œ์‹์ ์— ์—ฐ๊ฒฐ๋œ ๋ฆฌ๋ทฐ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ†ตํ•ด ๋ฆฌ๋ทฐ ํ‰์ ๊ณผ ๋ฆฌ๋ทฐ ์ˆ˜๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณ„์‚ฐํ•˜๋ ค๊ณ  ํ•˜๋ฉด, ๋ฐ˜๋ณต์ ์œผ๋กœ DB ์กฐํšŒํ•˜๊ฒŒ ๋˜์–ด N+1๋ฌธ์ œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ

์›์ธ

๋ฐ˜๋ณต๋ฌธ ์•ˆ์—์„œ ๊ฐ ์Œ์‹์ ์˜ ์—ฐ๊ด€ ๋ฆฌ๋ทฐ๋ฅผ ์ ‘๊ทผํ•˜๋ฉด ์ฟผ๋ฆฌ ๋ฐ˜๋ณต ์‹คํ–‰, ๋งค ์š”์ฒญ ์‹œ๋งˆ๋‹ค ์ง‘๊ณ„ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ํŠธ๋ž˜ํ”ฝ์ด ๋งŽ์€ ์‹œ๊ฐ„๋Œ€์— DB ๋ถ€ํ•˜ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

LEFT JOIN๊ณผ GROUP BY๋ฅผ ํ™œ์šฉํ•ด ๋ชจ๋“  ๋ฆฌ๋ทฐ ํ•œ๋ฒˆ์— ์ง‘๊ณ„ํ•ด ๋ฐ˜๋ณต๋ฌธ ์•ˆ์—์„œ๋Š” ํ•„๋“œ๋งŒ ์ˆ˜์ •ํ•˜๊ณ  Lazy ์—ฐ๊ด€ ์ ‘๊ทผํ•˜์ง€ ์•Š์•„ N+1 ๋ฌธ์ œ ๋ฐฉ์ง€

EC2 Docker ๋ฐฐํฌ ์‹œ OpenJDK ์ด๋ฏธ์ง€ ๋ฌธ์ œ ํ•ด๊ฒฐ

๋ฌธ์ œ ์ƒํ™ฉ

EC2์—์„œ openjdk:17-jre-slim ์ด๋ฏธ์ง€ ๋นŒ๋“œ ์‹คํŒจ
๋กœ์ปฌ์—์„œ๋Š” ์ •์ƒ ๋™์ž‘, EC2์—์„œ๋งŒ "not found" ์˜ค๋ฅ˜ ๋ฐœ์ƒ

์›์ธ

Docker Hub์—์„œ openjdk:17-jre-slim ์ด๋ฏธ์ง€ deprecated
Alpine ๊ธฐ๋ฐ˜ ์ด๋ฏธ์ง€์—์„œ ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๋ช…๋ น์–ด ์ฐจ์ด๋กœ ์ถ”๊ฐ€ ์˜ค๋ฅ˜ ๋ฐœ์ƒ

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

Base Image ๋ณ€๊ฒฝ: amazoncorretto:17-alpine
Alpine ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๋ช…๋ น์–ด ์ˆ˜์ • : addgroup -S springboot / adduser -S springboot -G springboot


๊ณตํ†ต ๊ด€์‹ฌ์‚ฌํ•ญ

๊ณตํ†ต ์—”ํ‹ฐํ‹ฐ ๊ด€๋ฆฌ(BaseEntity)

  • createdAt/modifiedAt, createdId/modifiedId: ์ƒ์„ฑ ๋ฐ ์ˆ˜์ • ์‹œ๊ฐ, ID(์ž๋™ ๊ด€๋ฆฌ, JPA Auditing ์‚ฌ์šฉ)
  • deletedAt, deletedId: ์†Œํ”„ํŠธ ์‚ญ์ œ ์‹œ๊ฐ, ID
  • ์ž‘์„ฑ/์ˆ˜์ • ์ •๋ณด ๊ด€๋ฆฌ ์ž๋™ํ™”, ์ฝ”๋“œ ์ค‘๋ณต ์ œ๊ฑฐ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ํ™•๋ณด

์—”ํ‹ฐํ‹ฐ ์ž‘์„ฑ/์ˆ˜์ • ๊ด€๋ฆฌ(AuditorAwareImpl)

  • @EnableJpaAuditing(auditorAwareRef = โ€œ๋นˆ์˜ ์ด๋ฆ„โ€)
    • AuditorAware ๋นˆ ์ฐธ์กฐ ์ด๋ฆ„์„ ์ง€์ •ํ•ด Auditing์„ ํ™œ์„ฑํ™”
    • Entity์—๋Š” @EntityListeners(AuditingEntityListener.class) ๋˜๋Š” ์ „์—ญ ๋“ฑ๋ก์„ ๋™์ž‘
  • AuditingEntityListener
    • JPA Entity ๋ผ์ดํ”„์‚ฌ์ดํด ์ด๋ฒคํŠธ๋ฅผ ๋ฆฌ์Šค๋‹ํ•ด์„œ auditing ๋™์ž‘์„ ์‹คํ–‰ํ•œ๋‹ค.
    • Entity๊ฐ€ persist/update ๋  ๋•Œ ์ž๋™์œผ๋กœ ํ˜ธ์ถœํ•ด ์–ด๋…ธํ…Œ์ด์…˜ ์ฒ˜๋ฆฌ
  • AuditorAware
    • ํ˜„์žฌ ๊ฐ์‚ฌ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค. getCurrentAuditor()๋ฅผ ํ†ตํ•ด Optional๋กœ ๋ฐ›์•„์˜จ๋‹ค.

์›น ์š”์ฒญ(WebConfig)

  • WebMvcConfigurer์˜ ๊ตฌํ˜„์ฒด์ธ WebConfig์˜ addArgumentResolvers ๋ฉ”์„œ๋“œ์—์„œ HandlerMethodArgumentResolver ๋ชฉ๋ก์— ์‚ฌ์šฉ์ž ์ •์˜ resolver๋ฅผ ์ถ”๊ฐ€

ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ •์ฑ…(PageableHandler)

  • HandlerMethodArgumentResolver๋ฅผ ๊ตฌํ˜„ํ•ด Pageable ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉ
  • supportsParameter: ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์ด Pageable์ด๋ฉด true
  • resolveArgument: page, size, sort ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ฝ์–ด PageRequest.of(page,size,sort) ๋ฐ˜ํ™˜
  • ์ •์ฑ…
    • ํ—ˆ์šฉ๋œ sizes: 10, 30, 50 (๊ทธ ์™ธ ๊ธฐ๋ณธ 10)
    • ๊ธฐ๋ณธ page: 0
    • ํ—ˆ์šฉ sort fields: createdAt, rating, totoalPrice, paymentAmount, averageRating, reviewCount
    • ๊ธฐ๋ณธ sort: createdAt, DESC

API ์‘๋‹ต ํ†ต์ผ(ApiResponse)

  • ๋ชจ๋“  API ์‘๋‹ต์„ ํ†ต์ผ๋œ ๊ตฌ์กฐ๋กœ ๋ฐ˜ํ™˜
  • ์—๋Ÿฌ ๋ฉ”์„ธ์ง€ ๋ฐ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์ผ๊ด€์„ฑ ์œ ์ง€

์ „์—ญ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ(GlobalExceptionHandler)

  • ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ ์ „์—ญ ์ฒ˜๋ฆฌ
  • ์ฒ˜๋ฆฌ ๋ฒ”์œ„:
    • MethodArgumentNotValidException: ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์‹คํŒจ
    • BusinessException: ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์˜ˆ์™ธ
    • Exeption: ๊ธฐํƒ€ ๋ชจ๋“  ์˜ˆ์™ธ

ํšŒ๊ณ 

์ž˜ํ•œ ์ 

  • ๊ณตํ†ต ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ •์ฑ…์„ ๊ตฌ์„ฑํ•˜๊ณ  ์ ์šฉํ•œ ์ 
  • SOLID ์›์น™์„ ๊ณ ๋ คํ•˜๋ฉฐ ์ฝ”๋“œ๋ฅผ ๊ตฌ์„ฑํ•œ ์ 
  • Builder์™€ Setter๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋กœ ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ ์ฑ…์ž„์„ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ถˆ๋ณ€์„ฑ์„ ๊ฐ•ํ™”ํ•œ ์ 

์–ด๋ ค์› ๋˜ ์ 

  • ๊ธ€๋กœ๋งŒ ๋ณด์•˜๋˜ ๋””์ž์ธ ํŒจํ„ด์„ ์ง์ ‘ ์ฝ”๋“œ์— ์ ์šฉํ•˜๋Š” ๊ณผ์ •์—์„œ์˜ ์–ด๋ ค์›€
  • ๊ฐ์ž ์ž‘์—…ํ•œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ฉํ•˜๋ฉด์„œ ์ถฉ๋Œ๊ณผ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์˜ ์–ด๋ ค์›€
  • DTO๋ฅผ Entity๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ ์™ธ๋ž˜ ํ‚ค ๊ด€๊ณ„์™€ ์˜์กด์„ฑ ๊ด€๋ฆฌ ๋ฌธ์ œ์˜ ์–ด๋ ค์›€

ํ•œ๊ณ„์ ๊ณผ ๋ฐœ์ „ ๊ณ„ํš

  • ๋„๋ฉ”์ธ๊ฐ„ ๊ฒฐํ•ฉ๋„๋ฅผ ๋А์Šจํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์ง์ ‘ ์ฐธ์กฐ ๋ฐฉ์‹์—์„œ ๊ฐ„์ ‘ ์ฐธ์กฐ๋กœ ๋ณ€๊ฒฝ
  • ๊ณตํ†ต ๋กœ๊น… ์ •์ฑ…์„ ์ ์šฉํ•ด ๋ชจ๋“  API ์š”์ฒญ/์‘๋‹ต๊ณผ ์˜ˆ์™ธ์ƒํ™ฉ์„ ์ผ๊ด€๋˜๊ฒŒ ๊ธฐ๋ก
  • ์„œ๋น„์Šค ์•ˆ์ •์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ํ—ฌ์Šค์ฒดํฌ์™€ ๋ชจ๋‹ˆํ„ฐ๋ง ์ฒด๊ณ„๋ฅผ ์ ์šฉ

ํ˜‘์—…์—์„œ ์•„์‰ฌ์šด ๋ถ€๋ถ„

  • ํŒ€์› ๊ฐ„ ์ž‘์—… ์†๋„ ์ฐจ์ด๋กœ ์ธํ•ด ๊ณตํ†ต ๋ชจ๋“ˆ์ด๋‚˜ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์ด ์ง€์—ฐ๋จ โ†’ ๋А๋ฆฐ ์ง„ํ–‰์„ ์กฐ๊ธฐ์— ํŒŒ์•…ํ•˜๊ณ , ํŽ˜์–ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ง€์›
  • ๋А๋ฆฐ ์ง„ํ–‰ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์ด๋‚˜ ํ…Œ์ŠคํŠธ ์ผ์ •์—๋„ ์˜ํ–ฅ์„ ์คŒ โ†’ ์ž‘์—… ๋‹จ์œ„๋ฅผ ์„ธ๋ถ„ํ™”ํ•˜๊ณ  ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ๊ณ„ํš ์กฐ์ •

ํŒ€์› ์†Œ๊ฐœ

๊น€ํ•œ๊ฒฐ ๋ฅ˜ํ˜•์„  ์ด์ค€์„ฑ ์ดํ˜„์ฃผ ์ „์šฐ์„ 
Order, Payment
Product, Option, AI
Review, Category
Store
User, Address, Auth
ํ…Œํฌ๋ฆฌ๋”
ํŒ€์›
ํŒ€์›
ํŒ€์žฅ
ํŒ€์›

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors