MCM ์ ํ ๊ฐ์ ์ฐฉ์ฉ ์๋น์ค โ ๋ฐฑ์๋
ํ์์ด ์ฌ๋ฆฐ ์ ์ ์ฌ์ง์ ๊ธฐ์ค ์ด๋ฏธ์ง(์ ์์ธยทํฐ ๋ฐฐ๊ฒฝ)๋ก ํ ๋ฒ ๋ณํํด ๋๊ณ , MCM ์ ํ์ ๊ณ ๋ฅผ ๋๋ง๋ค ๊ทธ ์์ ์ ํ์ด ์ฌ๋ผ๊ฐ ์ฐฉ์ฉ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ค.
์๋ณธ ์ฌ์ง โโ(AI 1ํ)โโโถ ๊ธฐ์ค ์ด๋ฏธ์ง โโ(AI, ์ ํ๋ง๋ค)โโโถ ์ฐฉ์ฉ ์ด๋ฏธ์ง
ํ์ ์
๋ก๋ ์ ์์ธยทํฐ ๋ฐฐ๊ฒฝ (์ฌ์ง, SKU)๋ณ ์ ์ฅยท์ฌ์ฌ์ฉ
- ๋ฐฐํฌ ์๋น์ค: https://52.79.118.19
- API ๋ฌธ์(Swagger): https://52.79.118.19/swagger-ui/index.html
- ํ๋ก ํธ์๋: Dev-MAMOKEY/MTM-frontend
- PRD: #2 PRD v2
- ์ฉ์ด์ง:
CONTEXT.md - ์ค๊ณ ๊ฒฐ์ :
docs/adr/0001-generative-composition.md
| ๊ตฌ๋ถ | ๊ธฐ์ | ๋ฒ์ |
|---|---|---|
| Language | Java | 21 |
| Framework | Spring Boot | 4.1.0 |
| Build | Gradle (Groovy) | - |
| ORM | Spring Data JPA | Boot ๊ด๋ฆฌ |
| DB | MySQL (AWS RDS) | 8.x |
| ์ธ์ฆ | Spring Security + JWT (jjwt) | 0.13.0 |
| ์ด๋ฏธ์ง ์ ์ฅ์ | AWS SDK for Java v2 (S3) | BOM 2.51.2 |
| ์ด๋ฏธ์ง ์์ฑ | Google Gen AI Java SDK (Gemini) | 1.65.0 |
| ์ด๋ฏธ์ง ์์ฑ(ํด๋ฐฑ) | OpenAI Java SDK | 4.50.0 |
| API ๋ฌธ์ | springdoc-openapi | 3.1.0 |
| ๋ฐฐํฌ | Docker ยท AWS EC2 | - |
| ํ ์คํธ | JUnit 5, Spring Boot Test, H2 | Boot ๊ด๋ฆฌ |
| ์ฌ๋ผ์ด์ค | ์ด์ | ๋ด์ฉ | ์ํ |
|---|---|---|---|
| 1 | #3 | ์ ํ ๋ชฉ๋ก์ด ํ๋ฉด์ ๋ฌ๋ค | โ (#24๋ก ๊ตฌํ) |
| 2 | #4 | ๊ฐ์ ํ๊ณ ๋ก๊ทธ์ธํ ์ ์๋ค | โ |
| 3 | #5 | ์ ์ฒด ์ ๋ณด๋ฅผ ์ ๋ ฅํ๊ณ ์ ์ฅํ๋ค | โ |
| 4 | #6 | ์ฌ์ง์ ์ฌ๋ฆฌ๋ฉด ์ฌ์ง์ฒฉ์ ์์ธ๋ค | โ |
| 5 | #7 | ์ฌ๋ฆฐ ์ฌ์ง์ด ๊ธฐ์ค ์ด๋ฏธ์ง๋ก ๋ฐ๋๋ค | โ |
| 6 | #8 | ์ ํ์ ๊ณ ๋ฅด๋ฉด ์ฐฉ์ฉ ์ด๋ฏธ์ง๊ฐ ๋ง๋ค์ด์ง๋ค | โ |
| 7 | #9 | ์ด๋ฏธ ๋ณธ ์ ํ์ ์ฆ์ ๋ค์ ๋ฌ๋ค | โ |
| 8 | #10 | ์ฐฉ์ฉ ์ด๋ฏธ์ง๋ฅผ ๋ค์ ๋ง๋ค ์ ์๋ค | โ |
| 9 | #11 | ๊ธฐ์ค ์ด๋ฏธ์ง๋ฅผ ๋ค์ ๋ง๋ค๋ฉด ์ฐฉ์ฉ ์ด๋ฏธ์ง๊ฐ ์ ๋ฆฌ๋๋ค | โ |
| 10 | #12 | OpenAI๋ก ๊ฐ์๋ผ์ ๋น๊ตํ๋ค | โ Gemini ์ต์ข ์ฑํ |
์ฌ๋ผ์ด์ค 10๊ฐ ์ ๋ถ ์๋ฃ. MCM ์ ํ 30 SKU์ ์ ํ ์ปท 231์ฅ์ด ์ ์ฌ๋์ด ์๋ค.
์ด๋ฏธ์ง ์์ฑ์ ์ฒ์์ OpenAI๋ก ๊ตฌํํ๋ค. ํ ์คํธ ๊ณผ์ ์์ ์๋ต ์๊ฐ๊ณผ ๊ฒฐ๊ณผ ํ์ง์ ๋ฌธ์ ๊ฐ ์์ด Gemini์ ๋น๊ต ํ ์คํธ๋ฅผ ์งํํ๊ณ , ํ ํ์๋ฅผ ๊ฑฐ์ณ Gemini๋ฅผ ์ต์ข ์ฑํํ๋ค. ์์ธํ ๊ฒฝ์๋ ํธ๋ฌ๋ธ์ํ ์ ์ ๋ฆฌํ๋ค.
| ๋ฉ์๋ | ๊ฒฝ๋ก | ์ค๋ช |
|---|---|---|
POST |
/api/v1/auth/signup |
ํ์๊ฐ์ |
POST |
/api/v1/auth/login |
๋ก๊ทธ์ธ (์ก์ธ์คยท๋ฆฌํ๋ ์ ํ ํฐ ๋ฐ๊ธ) |
POST |
/api/v1/auth/reissue |
์ก์ธ์ค ํ ํฐ ์ฌ๋ฐ๊ธ |
GET |
/api/v1/members/me |
๋ด ์ ๋ณด ์กฐํ |
POST |
/api/v1/members/me/body-info |
์ ์ฒด ์ ๋ณด ์ ์ฅ |
PATCH |
/api/v1/members/me/body-info |
์ ์ฒด ์ ๋ณด ๋ถ๋ถ ์์ |
GET |
/api/v1/products |
์ ํ ๋ชฉ๋ก ์กฐํ (ํ์ด์ง) |
GET |
/api/v1/products/{productId} |
์ ํ ์์ธ ์กฐํ |
POST |
/api/v1/photos |
์๋ณธ ์ฌ์ง ์ ๋ก๋ |
GET |
/api/v1/photos |
์ฌ์ง์ฒฉ ์กฐํ |
DELETE |
/api/v1/photos/{photoId} |
์ฌ์ง ์ญ์ (๊ธฐ์คยท์ฐฉ์ฉ ์ด๋ฏธ์ง ์ฐ์ ์ญ์ ) |
POST |
/api/v1/photos/{photoId}/base-image |
๊ธฐ์ค ์ด๋ฏธ์ง ์์ฑ |
POST |
/api/v1/photos/{photoId}/base-image/regenerate |
๊ธฐ์ค ์ด๋ฏธ์ง ์ฌ์์ฑ |
GET |
/api/v1/base-images |
๋ด ๊ธฐ์ค ์ด๋ฏธ์ง ๋ชฉ๋ก ์กฐํ |
POST |
/api/v1/base-images/{baseImageId}/worn-images |
์ฐฉ์ฉ ์ด๋ฏธ์ง ์์ฑ (๊ฐ์ ์กฐํฉ์ ์ฌ์ฌ์ฉ) |
POST |
/api/v1/base-images/{baseImageId}/worn-images/regenerate |
์ฐฉ์ฉ ์ด๋ฏธ์ง ์ฌ์์ฑ |
GET |
/api/v1/base-images/{baseImageId}/worn-images |
๊ธฐ์ค ์ด๋ฏธ์ง๋ณ ์ฐฉ์ฉ ์ด๋ฏธ์ง ๋ชฉ๋ก ์กฐํ |
์ด๋ฏธ์ง ์์ฑ์ ์ฒ์์ OpenAI(gpt-image-2)๋ก ๊ตฌํํ๋ค. ๋์์ ํ์ง๋ง ํ
์คํธ์์ ๋ ๊ฐ์ง๊ฐ ๊ฑธ๋ ธ๋ค.
- ์๋ต ์๊ฐ โ ๊ธธ๊ฒ๋ 30~40์ด. ๋ก๋ฉ๋ฐ๋ก ๋ฒํฐ๊ธฐ ์ด๋ ต๊ณ , ๋ฆฌ๋ฒ์ค ํ๋ก์๋ฅผ ๋ถ์ด๋ฉด ๊ธฐ๋ณธ ํ์์์(nginx 60์ด)์๋ ๊ฑธ๋ฆด ์ ์๋ ๊ฐ์ด๋ค.
- ๊ฒฐ๊ณผ ํ์ง โ ์ ํ์ด ์๋ณธ๊ณผ ๋ค๋ฅด๊ฒ ๊ทธ๋ ค์ง๋ ๋น๋๊ฐ ๋์๋ค.
๊ทธ๋์ Gemini์ ๋น๊ต ํ ์คํธ๋ฅผ ์งํํ๊ณ , ์๋ต ์๊ฐ์ด 10์ด ์ด๋ด๋ก ์งง๊ณ ๊ฒฐ๊ณผ๋ ๋ ์์ ์ ์ด์ด์ ํ ํ์๋ฅผ ๊ฑฐ์ณ Gemini๋ฅผ ์ต์ข ์ฑํํ๋ค.
๊ต์ฒด ๋น์ฉ์ด ๋ฎ์๋ ๊ฑด ImageGenerationGateway ์ธํฐํ์ด์ค ๋ค์ ๊ตฌํ์ฒด๋ฅผ ๋ ๊ตฌ์กฐ ๋๋ถ์ด๋ค. ๋๋ฉ์ธ ์ฝ๋๋ ํ ์ค๋ ๋ฐ๋์ง ์์๊ณ , ํ๊ฒฝ ๋ณ์ ํ๋๋ก ์ ํ๋๋ค.
IMAGE_GENERATION_PROVIDER=GEMINI
Gemini ์์์ ์ด๋ค ๋ชจ๋ธ์ ์ธ์ง๋ ์ธก์ ์ผ๋ก ์ ํ๋ค. GeminiImageModelBenchmark๊ฐ ํ๋ณด 3์ข
(gemini-3.1-flash-lite-image / gemini-3.1-flash-image / gemini-3-pro-image)์ ์ค์ ํธ์ถํด ๋ชจ๋ธ๋ณ ์ง์ฐ์๊ฐ๊ณผ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง๋ฅผ ๋น๊ตํ๋ค. ๊ณผ๊ธ์ด ๋ฐ์ํ๋ฏ๋ก CI์์๋ ์ ์ธํ๊ณ ํ์ํ ๋๋ง ์๋์ผ๋ก ์คํํ๋ค.
OpenAI ๊ตฌํ์ฒด๋ ์ง์ฐ์ง ์๊ณ ํด๋ฐฑ์ผ๋ก ๋จ๊ฒจ ๋์๋ค. ์ค์ ๋ง ๋ฐ๊พธ๋ฉด ์ธ์ ๋ ๋๋๋ฆด ์ ์๋ค.
์์ฑ ๊ฒฐ๊ณผ๊ฐ ๋งค๋ฒ ๋ฌ๋ผ์ง๋ ํน์ฑ(ADR 0001) ๋๋ฌธ์ (๊ธฐ์ค ์ด๋ฏธ์ง, ์ ํ) ์กฐํฉ์ผ๋ก ์ ์ฅํด ๋๊ณ ์ฌ์ฌ์ฉํ๋ค. ๊ฐ์ ์กฐํฉ์ ๋ค์ ์์ฒญํ๋ฉด AI๋ฅผ ํธ์ถํ์ง ์๊ณ ์ ์ฅ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ก ๋๋ ค์ค๋ค.
์์ญ ์ด์์ 1์ด ๋ฏธ๋ง์ผ๋ก ์ค์๊ณ , AI ํธ์ถ ๋น์ฉ๋ ๊ทธ๋งํผ ์๊ผ๋ค.
product.currency๊ฐ ENUM('KRW')์ธ๋ฐ ํฌ๋กค๋งํ 30๊ฑด์ด ์ ๋ถ USD์๋ค. ddl-auto: validate๋ผ ์ ์ฌ๊ฐ ์์ ๋์ง ์๋ ์ํฉ์ด์๋ค.
ํ์จ๋ก ํ์ฐํ๋ฉด ์๋ณธ ๊ฐ๊ฒฉ ์ ๋ณด๊ฐ ์ฌ๋ผ์ง๋ฏ๋ก ENUM('KRW','USD')๋ก ํ์ฅํ๊ณ ์๋ณธ ํตํ๋ฅผ ๊ทธ๋๋ก ์ ์ฅํ๋ ์ชฝ์ ํํ๋ค. ํ๊ธฐ ๋ฐฉ์์ ํด๋ผ์ด์ธํธ๊ฐ ์ ํ๋ค.
์ธ๋ ํค๊ฐ ON DELETE CASCADE๋ผ ์ฌ์ง๋ง ์ง์๋ DB๋ ์ ๋ฆฌ๋์ง๋ง, ํ์ด ์ฌ๋ผ์ง๋ ์๊ฐ storage_key๋ฅผ ์์ด S3 ๊ฐ์ฒด๊ฐ ์๊ตฌํ ๋จ๋๋ค.
์ง์ฐ๊ธฐ ์ ์ ์ ์ฅ์ ํค๋ฅผ ๋ชจ์ผ๊ณ ์์(์ฐฉ์ฉ ์ด๋ฏธ์ง โ ๊ธฐ์ค ์ด๋ฏธ์ง โ ์๋ณธ ์ฌ์ง)๋ถํฐ ๋ช ์์ ์ผ๋ก ์ญ์ ํ๋ค. ์ ์ฅ์ ์ญ์ ๋ DB ์ญ์ ๊ฐ ๋๋ ๋ค ๋ง์ง๋ง์ ์ํํ๋ค โ ํธ๋์ญ์ ์์์ ์ง์ ๋ค๊ฐ ๋กค๋ฐฑ๋๋ฉด DB๋ ๋๋์๊ฐ๋ ํ์ผ์ ๋ณต๊ตฌํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ ํ ๋ชฉ๋ก์ ๋ํ ์ด๋ฏธ์ง, ์ฌ์ง์ฒฉ์ ๊ธฐ์ค ์ด๋ฏธ์ง๋ฅผ ํญ๋ชฉ๋ง๋ค ์กฐํํ๋ฉด N+1์ด ๋ฐ์ํ๋ค. ๋ ๊ณณ ๋ชจ๋ ํ์ฌ ํ์ด์ง ๋ถ๋์ ํ ๋ฒ์ ์กฐํํด Map์ผ๋ก ๋ฌถ์ด ๋งค์นญํ๋๋ก ์ฒ๋ฆฌํ๋ค.
- ์ด๋ฏธ์ง ์์ฑ ์คํจ๊ฐ ๋ก๊ทธ์ ๋จ์ง ์๋ ๊ฒฝ๋ก๊ฐ ์๋ค. SDK ์์ธ๋ ๋ก๊ทธ๊ฐ ๋จ์ง๋ง, ์๋ต์ ์ด๋ฏธ์ง๊ฐ ์์ด
parseGeneratedImage์์ ๋์ง๋ ๊ฒฝ์ฐ๋ ๋ก๊ทธ ์์ด 502๋ง ๋๊ฐ๋ค. ์์ธ ์ถ์ ์ด ์ด๋ ค์ ๊ฐ์ ์ด ํ์ํ๋ค. product_cut.is_worn_slot์ด ์ ๋ถfalse๋ค. ํฌ๋กค๋ง ๋ฐ์ดํฐ์ ๋ชจ๋ธ ์ฐฉ์ฉ ์ปท์ ํ์ ํ ๊ทผ๊ฑฐ๊ฐ ์์ด ์ ์ฌ ์ ์ง์ ํ์ง ๋ชปํ๋ค.
com.likelion.mtm
โโโ MtmApplication.java
โ
โโโ global # ๋๋ฉ์ธ ๊ณตํต
โ โโโ config # SecurityConfig, SwaggerConfig, JpaAuditingConfig,
โ โ # S3Config, GeminiConfig, OpenAiConfig, ImageGenerationConfig
โ โโโ common # BaseTimeEntity, PageResponseDTO<T>
โ โโโ rsdata # RsData<T>
โ โโโ exception # CustomException, ErrorCode, GlobalExceptionHandler
โ โโโ security # JwtProvider, JwtAuthenticationFilter,
โ # JwtAuthenticationEntryPoint, JwtAccessDeniedHandler
โ
โโโ domain
โ โโโ member # ํ์ ยท ์ธ์ฆ ยท ์ ์ฒด ์ ๋ณด [์ฌ๋ผ์ด์ค 2, 3]
โ โโโ product # ์ ํ ยท ์ค์ธก ์น์ ํ์ ยท ์ฐฉ์ฉ ๋ฐฉ์ ๋ถ๋ฅ๊ธฐ ยท ํฌ๋กค๋ง ์ ์ฌ๊ธฐ [์ฌ๋ผ์ด์ค 1]
โ โโโ photo # ์๋ณธ ์ฌ์ง(์ฌ์ง์ฒฉ) ยท ๊ธฐ์ค ์ด๋ฏธ์ง [์ฌ๋ผ์ด์ค 4, 5, 9]
โ โโโ worn # ์ฐฉ์ฉ ์ด๋ฏธ์ง ยท ์ฌ์ฌ์ฉ ยท ์ฌ์์ฑ [์ฌ๋ผ์ด์ค 6, 7, 8]
โ
โโโ infra # ๊ต์ฒด ๊ฐ๋ฅํ ์ธ๋ถ ์ฐ๋ โ PRD๊ฐ ์๊ตฌํ๋ ๊ฒฝ๊ณ
โโโ storage
โ โโโ ImageStorage # ์ธํฐํ์ด์ค
โ โโโ S3ImageStorage # ๊ตฌํ์ฒด
โ โโโ ImageData # ์ด๋ฏธ์ง ๋ฐ์ด๋๋ฆฌ + MIME ํ์
โโโ imagegen
โโโ ImageGenerationGateway # ์ธํฐํ์ด์ค
โโโ GeminiImageGateway # ์ฃผ๋ ฅ
โโโ OpenAiImageGateway # ํด๋ฐฑ
โโโ BaseImagePromptAssembler # ๊ธฐ์ค ์ด๋ฏธ์ง ํ๋กฌํํธ ์กฐ๋ฆฝ๊ธฐ
โโโ WornImagePromptAssembler # ์ฐฉ์ฉ ์ด๋ฏธ์ง ํ๋กฌํํธ ์กฐ๋ฆฝ๊ธฐ
โโโ ImageGenerationRequest / GeneratedImage / ImageInput / ImageGenerationProvider
๊ฐ domain ํจํค์ง ๋ด๋ถ๋ controller / service / repository / entity / dto๋ก ๋๋๋ค.
ํ
์คํธ์ฉ FakeImageGateway๋ src/test/java/com/likelion/mtm/infra/imagegen์ ์๋ค.
๋ ํฌ ๋ฃจํธ์ .env๋ฅผ ๋ง๋ ๋ค. .env๋ ์ปค๋ฐํ์ง ์๋๋ค (.gitignore ํ์ธ).
DOCKER_IMAGE={๋ณธ์ธ๊ณ์ }/mtm-backend:latest
DATABASE_URL=jdbc:mysql://{RDS์๋ํฌ์ธํธ}:3306/first_db?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
DATABASE_USERNAME=
DATABASE_PASSWORD=
JWT_SECRET={32์ ์ด์ ๋๋ค ๋ฌธ์์ด}
JWT_ACCESS_EXP_MIN=60
JWT_REFRESH_EXP_DAY=30
IMAGE_GENERATION_PROVIDER=NONE
GEMINI_API_KEY=
OPENAI_API_KEY=
DATABASE_URL์ JDBC URL ์ ์ฒด๋ฅผ ๋ฃ๋๋ค. ํธ์คํธ๋ง ๋ฃ์ผ๋ฉด ๊ธฐ๋์ ์คํจํ๋ค.IMAGE_GENERATION_PROVIDER๋GEMINI/OPENAI/NONE์ค ํ๋๋ค.NONE์ด๋ฉด ์ด๋ฏธ์ง ์์ฑ ๊ธฐ๋ฅ๋ง ๋น ์ง๊ณ ํ์ยท์ฌ์งยท์ ํ ๊ธฐ๋ฅ์ ์ ์ ๊ธฐ๋ํ๋ค. AI ํค ์์ด ๊ฐ๋ฐํ ๋ ์ด๋ค.- AWS ์๊ฒฉ์ฆ๋ช
์
.env์ ๋ฃ์ง ์๋๋ค. ๋ก์ปฌ์~/.aws/credentials, ๋ฐฐํฌ ์๋ฒ๋ EC2 IAM ์ญํ ๋ก ์ฃผ์ ๋๋ค.
./gradlew bootRunSwagger โ http://localhost:8080/swagger-ui/index.html
๋ก์ปฌ์์ ์ด๋ฏธ์ง๋ฅผ ๋ง๋ค์ด ์ฌ๋ฆฐ๋ค
docker build -t {๋ณธ์ธ๊ณ์ }/mtm-backend:latest .
docker login
docker push {๋ณธ์ธ๊ณ์ }/mtm-backend:latest์๋ฒ์์ ๋ฐ์ ์คํํ๋ค
์๋ฒ ์์
๋๋ ํฐ๋ฆฌ์ docker-compose.yml๊ณผ .env๊ฐ ์์ด์ผ ํ๋ค. .env๋ ๊น์ ์์ผ๋ฏ๋ก ์๋ฒ์์ ์ง์ ๋ง๋ ๋ค.
docker compose pull
docker compose up -d
docker compose logs -frestart: unless-stopped๋ผ ์๋ฒ๊ฐ ์ฌ๋ถํ ๋ผ๋ ์ปจํ ์ด๋๊ฐ ์๋์ผ๋ก ์ฌ๋ผ์จ๋ค..env์DOCKER_IMAGE๊ฐ ์์ผ๋ฉด compose๊ฐ ๊ธฐ๋์ ๊ฑฐ๋ถํ๋ค.
HTTPS
ํ๋ก ํธ์๋๊ฐ HTTPS๋ก ๋ฐฐํฌ๋์ด ๋ธ๋ผ์ฐ์ ๊ฐ ํผํฉ ์ฝํ ์ธ ๋ฅผ ์ฐจ๋จํ๋ฏ๋ก, ๋ฐฑ์๋๋ HTTPS๋ก ์๋น์คํ๋ค.
- ๋ฆฌ๋ฒ์ค ํ๋ก์๊ฐ 443์์ TLS๋ฅผ ์ข ๋ฃํ๊ณ ์ปจํ ์ด๋์ 8080์ผ๋ก ์ ๋ฌํ๋ค. 80์ผ๋ก ๋ค์ด์จ ์์ฒญ์ HTTPS๋ก ๋ฆฌ๋ค์ด๋ ํธ๋๋ค.
- ์ธ์ฆ์๋ Let's Encrypt๋ฅผ ์ฐ๋ฉฐ, ๋๋ฉ์ธ ์์ด IP ์ฃผ์(
52.79.118.19)๋ก ๋ฐ๊ธํ๋ค. - ํ๋ก์๊ฐ TLS๋ฅผ ๋๊ณ ๋ฐฑ์๋์๋ HTTP๋ก ๋๊ธฐ๊ธฐ ๋๋ฌธ์, ์คํ๋ง์ด ์๋ ํ๋กํ ์ฝ์ ์ธ์ํ๋๋ก ์๋ ์ค์ ์ด ํ์ํ๋ค. ์์ผ๋ฉด
Secure์ฟ ํค๋ ๋ฆฌ๋ค์ด๋ ํธ ์ฃผ์๊ฐhttp://๋ก ๋๊ฐ๋ค.
server:
forward-headers-strategy: framework- ์ธ๋ถ ์ ์์ด ๋๋ ค๋ฉด EC2 ๋ณด์ ๊ทธ๋ฃน๊ณผ ์๋ฒ ๋ฐฉํ๋ฒฝ(
ufw)์ 80ยท443์ด ์ด๋ ค ์์ด์ผ ํ๋ค.
| ๋ธ๋์น | ์ฉ๋ |
|---|---|
main |
๋ฐฐํฌ |
develop |
ํตํฉ. PR์ ์ฌ๊ธฐ๋ก ๋ณด๋ธ๋ค |
{๋ผ๋ฒจ}/#{์ด์๋ฒํธ} |
์์
๋ธ๋์น. ์) Feat/#3 |
- ์ด์ ํ๋๋น ๋ธ๋์น ํ๋.
- ์์
๋ธ๋์น๋
develop์์ ๋ฐ๊ณ , PR๋develop์ผ๋ก ๋ณด๋ธ๋ค. - ์ด์์ ๋ธ๋์น๋ ๋ด๋น์๊ฐ ์ง์ ๋ง๋ ๋ค.
[{๋ผ๋ฒจ}/#{์ด์๋ฒํธ}] {๋ผ๋ฒจ}: {ํ๊ธ ์ค๋ช
}
์์:
[Feat/#3] Feat: ์ค์ธก ์น์ ํ์ ๊ตฌํ
[Test/#3] Test: ์ฐฉ์ฉ ๋ฐฉ์ ๋ถ๋ฅ๊ธฐ ํ
์คํธ ์ถ๊ฐ
[Fix/#7] Fix: ๊ธฐ์ค ์ด๋ฏธ์ง ์ ์ฅ ์ storage key ์ค๋ณต ๋ฌธ์ ์์
| ๋ผ๋ฒจ | ์๋ฏธ |
|---|---|
Feat |
์๋ก์ด ๊ธฐ๋ฅ |
Fix |
๋ฒ๊ทธ ์์ |
Refactor |
๊ธฐ๋ฅ ๋ณํ ์๋ ๊ตฌ์กฐ ๊ฐ์ |
Design |
UI/์๋ต ํํ ๋ณ๊ฒฝ |
Chore |
๋น๋ยท์์กด์ฑ |
Setting |
ํ๊ฒฝ ์ค์ |
Comment |
์ฃผ์ยท๋ก๊ทธ |
Docs |
๋ฌธ์ |
Test |
ํ ์คํธ |
- ํ๋์ ๊ธฐ๋ฅ์ด ๋๋ ๋๋ง๋ค ์ปค๋ฐํ๋ค. ์๋ฏธ ๋จ์๋ก ์๊ฒ ์ชผ๊ฐ๋, ๊ฐ ์ปค๋ฐ์ ๋น๋๊ฐ ๋์ด์ผ ํ๋ค.
์ ๋ชฉ: [{๋ผ๋ฒจ}] {๋ฒํธ} {๋ชฉํ}
## ๋ชฉ์
## ๋ฒ์
### ํฌํจ:
### ์ ์ธ:
## ์์
๋ด์ฉ
- [ ]
## ์๋ฃ ์กฐ๊ฑด
- [ ]์ฌ๋ผ์ด์ค ์ด์(#3~#12)๋ PM์ด ์ด๋ฏธ ์์ฑํด ๋์๋ค. ์๋ก ๋ง๋๋ ๊ฒ์ ์ด ์์์ ๋ฐ๋ฅธ๋ค.
์ ๋ชฉ: [{๋ผ๋ฒจ}/{Issue ๋ฒํธ}] ์์
์์ฝ
## ์ฐ๊ด Issue
- closes #{Issue ๋ฒํธ}
## ์์ฝ
์ด PR์์ ๋ณ๊ฒฝํ ๋ด์ฉ์ ๊ฐ๋จํ ์ค๋ช
ํฉ๋๋ค.
## ๋ณ๊ฒฝ ์ฌํญ
- ๋ณ๊ฒฝ ์ฌํญ 1
- ๋ณ๊ฒฝ ์ฌํญ 2
## ๋ฒ์
### ํฌํจ:
- ์์ 1
### ์ ์ธ:
- ์์ 1
## ์คํฌ๋ฆฐ์ท / ๋ฏธ๋ฆฌ ๋ณด๊ธฐ
- before/after ์ด๋ฏธ์ง ๋๋ ์บก์ฒ ์ฒจ๋ถ- ์์ ์น์ (API ๋ช ์ธ, ๋ฆฌ๋ทฐ ํฌ์ธํธ ๋ฑ)์ ์ถ๊ฐํ์ง ์๋๋ค. ํ์ํ๋ฉด ์์ฝยท๋ณ๊ฒฝ ์ฌํญ ์์ ๋ น์ธ๋ค.
- ๋จธ์ง ์ ํ์ 1๋ช ์ด์ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ๋๋ค.
Controller ์์ฒญ/์๋ต ๋ณํ๋ง. ๋น์ฆ๋์ค ๋ก์ง ๊ธ์ง
Service ๋ก์ง ยท ํธ๋์ญ์
Repository DB ์ ๊ทผ
- ๋ชจ๋ ์๋ต์
RsData<T>๋ก ๊ฐ์ผ๋ค. - ์์ธ๋
CustomException+ErrorCode๋ฅผ ๋์ง๊ณGlobalExceptionHandler๊ฐ ์ฒ๋ฆฌํ๋ค. - ๋ด๋ถ ์์ธ ๋ฉ์์ง๋ฅผ ๊ทธ๋๋ก ์๋ต์ ๋ ธ์ถํ์ง ์๋๋ค.
- ํ์ด์ง ์๋ต์
PageResponseDTO<T>๋ก ๊ฐ์ผ๋ค. Spring์Page๋ฅผ ์ง์ ๋ ธ์ถํ์ง ์๋๋ค.
- ์ํฐํฐ โ DTO ๋ณํ์ DTO์ ์ ์ ๋ฉ์๋
from() - DTO โ ์ํฐํฐ ๋ณํ์
toEntity() - ์ํฐํฐ์
@Setter๋ฅผ ๋ถ์ด์ง ์๋๋ค. ์๋ฏธ ์๋ ๋ฉ์๋๋ก ์ํ๋ฅผ ๋ฐ๊พผ๋ค (updateBodyInfo(),replaceStorageKey()๋ฑ) - ๊ณตํต ์๊ฐ ์ปฌ๋ผ์
BaseTimeEntity์์
- ์กฐํ ์ ์ฉ ์๋น์ค ๋ฉ์๋๋
@Transactional(readOnly = true) - LAZY ์ฐ๊ด์ ์ปจํธ๋กค๋ฌ์์ ๊ฑด๋๋ฆฌ๋ฉด 500์ด ๋๋ค. DTO ๋ณํ์ ์๋น์ค ํธ๋์ญ์ ์์์ ๋๋ธ๋ค.
- ๋ชจ๋ ์ฝ๋์ ํ๊ธ ์ฃผ์์ ๋จ๋ค. ๊ฐ ํด๋์คยท๋ฉ์๋๊ฐ ๋ฌด์จ ์ญํ ์ธ์ง ์ฝ๊ณ ๋ฐ๋ก ์ ์ ์๊ฒ.
- ์ ์๋ํฌ์ธํธ์๋ Swagger ์ด๋
ธํ
์ด์
์ ์ ๋ถ ๋ถ์ธ๋ค (
@Tag,@Operation,@ApiResponses). ๋์ค์ ๋ชฐ์์ ํ๋ฉด ์ ๋ถ๋๋ค.
@PathVariable,@RequestParam์ ์ด๋ฆ์ ๋ฐ๋์ ๋ช ์ํ๋ค.// O @PathVariable("id") Long id // X โ IntelliJ ์คํ ์ 500 @PathVariable Long id
CONTEXT.md์ ์ฉ์ด๋ฅผ ๋ฐ๋ฅธ๋ค. ๋ณ์๋ช
ยทํด๋์ค๋ช
ยท์ฃผ์ยท์ปค๋ฐ ๋ฉ์์ง ์ ๋ถ ํด๋น๋๋ค.
| ์ธ ๊ฒ | ์ฐ์ง ๋ง ๊ฒ |
|---|---|
์๋ณธ ์ฌ์ง Photo |
์ ๋ก๋ ์ด๋ฏธ์ง, ์ ์ ์ด๋ฏธ์ง |
๊ธฐ์ค ์ด๋ฏธ์ง BaseImage |
์ ์์ธ ์ด๋ฏธ์ง, ๋ฒ ์ด์ค ์ด๋ฏธ์ง, ๋๋ผ ์ด๋ฏธ์ง |
์ฐฉ์ฉ ์ด๋ฏธ์ง WornImage |
๊ฒฐ๊ณผ ์ด๋ฏธ์ง, ํฉ์ฑ ์ด๋ฏธ์ง, ๋ฃฉ |
์ ํ ์ปท ProductCut |
์ํ ์ด๋ฏธ์ง |
์ค์ธก ์น์ Dimensions |
์ฌ์ด์ฆ |
์ฐฉ์ฉ ๋ฐฉ์ WearType |
๊ฐ๋ฐฉ ํ์ , ์นดํ ๊ณ ๋ฆฌ |
| ์ฌ์ง์ฒฉ | ๊ฐค๋ฌ๋ฆฌ, ์จ๋ฒ |
ํนํ ์์ฑ ํฉ์ฑ๊ณผ ์ค๋ ค๋ถ์ด๊ธฐ๋ฅผ "ํฉ์ฑ"์ผ๋ก ๋ญ๋ฑ๊ทธ๋ฆฌ์ง ์๋๋ค. ์ด๋ ์ชฝ์ธ์ง ์ ์ ์์ด์ง๋ค.
| ํธ๋ | ๋ด๋น | ๋ด๋นํ ์์ |
|---|---|---|
| A. ํ์ ยท ์ ํ ยท ๋ฐฐํฌ | ๊น๋ํ | ์ฌ๋ผ์ด์ค 2ยท3(#4ยท#5), ์ฌ๋ผ์ด์ค 1(#3โ#24), ์ ํ ์์ธยท๋ชฉ๋ก ํ์ด์ง(#34), ์ฐฉ์ฉ ๋ฐฉ์ ๋ ธ์ถ(#37), ์ฌ์ง ์ญ์ (#38), Docker ๋ฐฐํฌ ํ๊ฒฝ(#31) |
| B. ์ด๋ฏธ์ง ์์ฑ ์ธํ๋ผ | ์ต์ ํ | ์ฌ๋ผ์ด์ค 4ยท5ยท6(#6ยท#7ยท#8), ImageStorageยทImageGenerationGateway ์ถ์ํ, GeminiยทOpenAI ๊ฒ์ดํธ์จ์ด, AWS ์ธํ๋ผ |
| C. ์ด๋ฏธ์ง ํ์ดํ๋ผ์ธ | ์ ์์ | ERDยท์ํฐํฐ(#13), ํฌ๋กค๋ง ๋ฐ์ดํฐ ์์ง, ์ฌ๋ผ์ด์ค 7ยท8ยท9(#9ยท#10ยท#11), ๊ธฐ์ค ์ด๋ฏธ์งยท์ฐฉ์ฉ ์ด๋ฏธ์ง ์กฐํ API(#41ยท#43ยท#45) |
- A/B/C๊ฐ ๋ง์ฃผ์น๋ ์ ์ ์
ImageGenerationGateway/ImageStorage์ธํฐํ์ด์ค ์๊ทธ๋์ฒ๋ฟ์ด๋ค. Phase 0์์ ํ์ ํ ๋ค ๋ฐ๊พธ์ง ์๋๋ค. #10,#11์ ๊ฐ์ ํ์ผ(domain/worn)์ ๊ฑด๋๋ฆฌ๋ฏ๋ก ๋์์ ์งํํ์ง ์๊ณ ์์๋๋ก ์ฒ๋ฆฌํ๋ค.