Skip to content

Commit e90f3e5

Browse files
feat: release/v1.1.0 -> main (#52)
* docs: pr template ์ž‘์„ฑ * docs: issue template ์ž‘์„ฑ (#3) * chore: ๋ฐฑ์—”๋“œ ํ™˜๊ฒฝ ์„ค์ • (#11) * chore: gradle ์˜์กด์„ฑ ์„ค์ • * feat: BaseEntity ๊ธฐ๋Šฅ ๊ตฌํ˜„ * chore: application.yml ์„ค์ • * feat: JPA Auditing ์„ค์ • * docs: ์ปค๋ฐ‹ ์ปจ๋ฒค์…˜ ์„ค์ • * chore: backend ํ™˜๊ฒฝ ์…‹ํŒ… * feat: ์ดˆ๊ธฐ ์—”ํ„ฐํ‹ฐ ๊ตฌ์ถ• (#14) * feat: Message ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ์ฑ„์ƒ‰ ๊ฒฐ๊ณผ Clothes ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ์ฑ„์ƒ‰ ์š”์ฒญ Clothes ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ๋”๋ฏธ ๋ฐ์ดํ„ฐ ๊ตฌ์ถ• (#15) * style: ์ฑ„์ƒ‰ ๊ฒฐ๊ณผ Clothes ์˜์†ํ™”๋ฅผ ์œ„ํ•œEntity ์ด๋ฆ„ ์ˆ˜์ • * test: dummy ๋ฐ์ดํ„ฐ ๊ตฌ์ถ• ๋ฐ ํ…Œ์ŠคํŠธ * feat: upload ๊ธฐ๋Šฅ ๋ฐ ํŽ˜์ด์ง€ ๊ตฌํ˜„ (#16) * test: MessageRepositoryTest ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ClothesRepository ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * test: ClothesRepository Clothes class์˜ Message ์ฐธ์กฐ Field ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ * chore: Google Cloud Storage ํ™˜๊ฒฝ ์…‹ํŒ… * feat: ClothesService ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: FileUploader ๊ธฐ๋Šฅ ๊ตฌํ˜„ * test: ClothesService ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ * style: ํ…Œ์ŠคํŠธ ๋…๋ฆฝ์„ฑ์„ ์œ„ํ•œ ๋”๋ฏธ ๋ฐ์ดํ„ฐ ์ œ๊ฑฐ * chore: ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ๋ schema, data.sql ์„ค์ • * test: ClothesRepository ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: FileExtension ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: FileConverter ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * fix: init.sql ์ฐธ์กฐ ๋ฌธ๋ฒ• ์ˆ˜์ • * feat: MessageService ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * chore: RedisConfig ํ™˜๊ฒฝ ์…‹ํŒ… * feat: MessagePublisher ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ClothesController ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * chore: Embedded RedisConfig ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ์„ค์ • * fix: FileExtension ๋Œ€๋ฌธ์ž ํ™•์žฅ์ž ์ถ”๊ฐ€ * feat: ํŽ˜์ด์ง€ ํ—ค๋” ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: index ํŽ˜์ด์ง€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: guide ํŽ˜์ด์ง€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ClothesController ์‘๋‹ต ๋ฐฉ์‹ ๋ณ€๊ฒฝ(API -> HTML) * feat: upload ํŽ˜์ด์ง€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * style: upload ํŽ˜์ด์ง€ ๋‚ด์šฉ ์ˆ˜์ • * feat: ML ์ถ”๋ก  ํŒŒ์ดํ”„๋ผ์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ (#17) * feat: ์›๊ฒฉ ๋ฉ”์„ธ์ง€ ํ‘ธ์‹œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ * refactor: ์—…๋กœ๋“œ ๋ฐ์ดํ„ฐ ์ƒํƒœ ๋ฉ”์„ธ์ง€ ์ถ”๊ฐ€ * refactor: ์ถ”๋ก  ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ณ€๊ฒฝ (CLOTHES -> 4๊ฐ€์ง€ ํ’ˆ๋ชฉ[T_SHIRTS, PANTS, SKIRT, HAT]) * test: ClothesRepository ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * style: MessageRepository ํ…Œ์ŠคํŠธ ํ•„๋“œ ์ˆ˜์ • * test: MessageService Queue Publisher ๋ชฉํ‚น * refactor: PageController ์›น ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… ํด๋ž˜์Šค ๋ถ„๋ฆฌ * feat: ์‚ฌ์šฉ์ž ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ControllerAdvice ๊ธฐ๋Šฅ ๊ตฌํ˜„ * style: GuideController ๋ผ์šฐํŒ… ๋ฉ”์„œ๋“œ PageController๋กœ ์ด๋™ * style: Message ์ฑ„์ƒ‰ ๊ฒฐ๊ณผ ์ •์ œ๋ฅผ ์œ„ํ•œ refine ํ•„๋“œ ์ถ”๊ฐ€ * fix: ClothesService create ๋ฉ”์„œ๋“œ ์‘๋‹ต ํ˜•์‹ ์ˆ˜์ • [clothesId -> messageId] * feat: ClothesController ์ฑ„์ƒ‰ ์นดํ…Œ๊ณ ๋ฆฌ ๋ฐ ์ •์ œ ์—ฌ๋ถ€ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * test: ClothesController RestAssured ์ธ์ˆ˜ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: MessagePublisher Redis Stream๋ฐ์ดํ„ฐ ํƒ€์ž… ํด๋ผ์ด์–ธํŠธ ๊ตฌ์ถ• * feat: JobConfig Redis Stream ๋ฉ”์„ธ์ง€ ์ฝœ๋ฐฑ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ModelSearcher ์ถ”๋ก  ๋ชจ๋ธ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ClothesModelHttpCaller ์›๊ฒฉ ์ถ”๋ก  ์„œ๋ฒ„ ํ˜ธ์ถœ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: SearchConverter ์ถ”๋ก  ๊ฒฐ๊ณผ ์ž๋ฐ” ์‹œ์Šคํ…œ์œผ๋กœ ์ง๋ ฌํ™” * feat: ClothesJobConsumerListener Redis Stream ๋ฉ”์„ธ์ง€ ๋ฆฌ์Šค๋‹ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ClothesJobConsumerListener ์ถ”๋ก  ์„œ๋ฒ„ ํ—ฌ์Šค ์ฒดํฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ResultService ์ด๋ฏธ์ง€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ Redis Cache ์บ์‹ฑ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ์ถ”๋ก  ์„œ๋ฒ„ ๋ถ€์† ํŒŒ์ผ ignore * feat: ์ฑ„์ƒ‰ ๋ชจ๋ธ ํŒจํ‚ค์ง• ๋Ÿฐ์ณ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ์ถ”๋ก  Worker ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ์ถ”๋ก  ์„œ๋ฒ„ ์ปค์Šคํ…€ ์˜ˆ์™ธ ๊ตฌํ˜„ * feat: Bing Image Search API ๊ธฐ๋Šฅ ๊ตฌํ˜„ * refactor: ์ƒ์ˆ˜ ํ•˜๋“œ ์ฝ”๋”ฉ ๋ถ„๋ฆฌ * feat: ์ฑ„์ƒ‰ ์ด๋ฏธ์ง€ ๊ธฐ๋ฐ˜ ์›น ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๋ Œ๋”๋ง ๊ธฐ๋Šฅ ๋ฐ ํŽ˜์ด์ง€ ๊ตฌํ˜„ (#18) * feat: ์ฑ„์ƒ‰ ์ด๋ฏธ์ง€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ResultRepository ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: SearchRepository ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ResultService ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: PageController ๋ฉ”์ธ ํ™”๋ฉด ์ด๋™ ์‹œ ๊ฒ€์ƒ‰๊ฒฐ๊ณผ DB ์ €์žฅ ๋ฐ ์บ์‹œ ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ƒํ’ˆ๋ช… ๋ช…์‹œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: tshirts_worker ํด๋ผ์šฐ๋“œ SQL PK(message_id) ์ ์šฉ * feat: base_model_launcher ํด๋ผ์šฐ๋“œ SQL ๋„์ž… * feat: storage_handler ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ํด๋ผ์šฐ๋“œ ํ•˜๋“œ์ฝ”๋”ฉ ์ƒ์ˆ˜ ๋ถ„๋ฆฌ * feat: SignedUrlBuilder CDN ์ ์šฉ์„ ์œ„ํ•œ ์„œ๋ช…๋œ URL ๋ฐœํ–‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ClothesResult ํ‰์  ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ResultRepository ํ‰์  ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ResultService ํ‰์  ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ClothesController ํ‰์  ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ํ‰์  ๋ถ€์—ฌ html ๊ตฌํ˜„ * feat: ResultService ์ฑ„์ƒ‰ ๊ฒฐ๊ณผ ๊ณต์œ  ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ClothesController ์ฑ„์ƒ‰ ๊ฒฐ๊ณผ ๊ณต์œ  ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ResultRepository ๊ฐค๋Ÿฌ๋ฆฌ ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ResultService ๊ฐค๋Ÿฌ๋ฆฌ ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: GalleryService ๊ฐค๋Ÿฌ๋ฆฌ ์ „์‹œ๋ฅผ ์œ„ํ•œ SignedUrl ๊ธฐ๋Šฅ ๋ฐ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: ๊ฐค๋Ÿฌ๋ฆฌ ํŽ˜์ด์ง€ HTML ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ๊ธฐ๋ณธ ๋ฐ‘๊ทธ๋ฆผ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ (#19) * feat: ClothesController ๊ธฐ๋ณธ ๋ฐ‘๊ทธ๋ฆผ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ๊ธฐ๋ณธ ๋ฐ‘๊ทธ๋ฆผ ์ด๋ฏธ์ง€ HTML ๊ธฐ๋Šฅ ๊ตฌํ˜„ * chore: import ์ตœ์ ํ™” * refactor: [ML, Application ์„œ๋ฒ„] ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง (#21) * style: HTML ํŽ˜์ด์ง€ ์›Œ๋”ฉ ๋ฐ css ์ˆ˜์ • * refactor: ํ•˜๋“œ ์ฝ”๋”ฉ ์ƒ์ˆ˜ ๋ถ„๋ฆฌ * refactor: final ์žฌํ• ๋‹น ๋ถˆ๊ฐ€ ์ ์šฉ * refactor: HTTP status code NOT_FOUND ์ ์šฉ * refactor: Type Hint ์ ์šฉ * chore: [Application, ML] ์„œ๋น™ ํ™˜๊ฒฝ ๊ตฌ์ถ• (#23) * chore: ์ถ”๋ก  ์„œ๋ฒ„ ์›Œ์ปค Dockerfile ๊ตฌํ˜„ * chore: requirements.txt ์ ์šฉ * chore: docker-compose.yml ์ž‘์„ฑ * test: presentation layer ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ (#26) * test: GalleryController ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * test: ClothesController ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * style: Controller ํ…Œ์ŠคํŠธ EndPoint ์ˆ˜์ • * test: Request DTO ๋นˆ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * test: ClothesAcceptanceTest ์ธ์ˆ˜ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: UuidHolder ๋žœ๋ค ๋ชจ๋“ˆ ๋ถ„๋ฆฌ * test: ClothesController ์ธ์ˆ˜ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * test: GalleryController ์ธ์ˆ˜ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * feat: DatabaseCleaner ๊ธฐ๋Šฅ ๊ตฌํ˜„ * fix: ํ…Œ์ŠคํŠธ๊ฐ„ ์˜์กด ๋ฌธ์ œ ํ•ด๊ฒฐ * feat: rest docs ํ…Œ์ŠคํŠธ ๋ฌธ์„œํ™” (#27) * chore: rest docs gradle ์…‹ํŒ… * feat: RestDocsConfig mockMVC ์„ค์ • ๊ธฐ๋Šฅ ๊ตฌํ˜„ * chore: asciidoc ์„ค์ • * style: ํ…Œ์ด๋ธ” pk ๋ช… ๋ณ€๊ฒฝ * feat: ci workflow ๊ตฌํ˜„ (#30) * feat: ci ํ”„๋กœ์„ธ์Šค ์ž‘์„ฑ * test: ci ๋™์ž‘ ํ…Œ์ŠคํŠธ ๋ธŒ๋žœ์น˜ ์ถ”๊ฐ€ * fix: paths ํ•„ํ„ฐ๋ง ํ‘œํ˜„ ์ˆ˜์ • * fix: PR ์ฝ”๋ฉ˜ํŠธ ์ž‘์„ฑ ๊ถŒํ•œ ์—๋Ÿฌ ์ˆ˜์ • * test: ci ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ (#29) * test: ci ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * style: ci workflow์—์„œ ํ…Œ์ŠคํŠธ ๋ธŒ๋žœ์น˜ ์ œ๊ฑฐ * style: ํ…Œ์ŠคํŠธ ์ฝ˜์†” ๋กœ๊ทธ ์ฝ”๋“œ ์ œ๊ฑฐ * feat: cd workflow ๊ตฌํ˜„ (#47) * feat: ๋ฐฐํฌ ํ™˜๊ฒฝ ์„ค์ • ๋ฐ ๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋“œ ์žก ๊ตฌํ˜„ * test: cd ํ…Œ์ŠคํŠธ ๋ธŒ๋žœ์น˜ ํ•„ํ„ฐ๋ง ์ถ”๊ฐ€ * style: ์ž‘์—… directory ๋ ˆ๋ฒจ ๋ณ€๊ฒฝ [workflow -> job] * feat: backend Dockerfile์— environment ๊ตฌํ˜„ * chore: application-develop.yml ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ • ํŒŒ์ผ ๋ถ„๋ฆฌ * feat: backend Dockerfile์— environment ๊ตฌํ˜„ * chore: application-develop.yml ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ • ํŒŒ์ผ ๋ถ„๋ฆฌ * fix: Dockerfile jar ๋ณต์‚ฌ ๊ฒฝ๋กœ ์ˆ˜์ • * feat: dev ํ™˜๊ฒฝ ๋ฐฐํฌ ์žก ๊ตฌํ˜„ * chore: docker-compose ๋ฐฑ์—”๋“œ ์šด์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ถ”๊ฐ€ * fix: cd.yml secrets ๊ฐ’ ์˜ค๋ฅ˜ ์ˆ˜์ • * fix: ssh script ๋ช…๋ น์–ด ๊ฒฝ๋กœ ์ˆ˜์ • * fix: ssh script docker login ์ˆ˜์ • * fix: ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ณ ์ˆ˜์ • [arm -> x86] * fix: gcp credential key secrets ๊ฐ’ ์ €์žฅ * style: credential ์ƒ์„ฑ ๊ฒฝ๋กœ ์ˆ˜์ • * fix: credential ๊ฒฝ๋กœ ์ˆ˜์ • * feat: Dockerfile entrypoint ์ˆ˜์ • * test: cd ์›Œํฌํ”Œ๋กœ์šฐ ๋””๋ฒ„๊น…์„ ์œ„ํ•œ tmate ์ ์šฉ * feat: tmate ssh ๊ถŒํ•œ ์ •๋ณด ์ˆ˜์ • * feat: cd ์›Œํฌํ”Œ๋กœ์šฐ tmate ํƒ€์ž„์•„์›ƒ ์„ค์ • * style: tmate ๊ธฐ๋Šฅ ์ œ๊ฑฐ * style: gcp key ๊ฒฝ๋กœ ์ˆ˜์ • * fix: github action์—์„œ json ์ƒ์„ฑ ์‹œ ํฌ๋ฉง ๋ณ€ํ™˜ ๋ฌธ์ œ ํ•ด๊ฒฐ * chore: [๊ฐœ๋ฐœ, ์šด์˜] ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ • * feat: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๋ชจ๋ธ worker uri ๋ณ€์ˆ˜ ๊ตฌํ˜„ * feat: [test] application.yml worker uri ๋ณ€์ˆ˜ ๊ตฌํ˜„ * fix: gcp ssh ์ ‘๊ทผ ๋ช…๋ น์–ด ์ œ๊ฑฐ * fix: ๋น„๊ด€๋ฆฌ ์ด๋ฏธ์ง€ ๊ด€๋ฆฌ ๋ชฉ์ ์œผ๋กœ ssh script ์ด๋ฏธ์ง€ ์‚ญ์ œ ์ˆœ์„œ ๋ณ€๊ฒฝ * fix: dangling ์ด๋ฏธ์ง€ ๋ฏธ์‚ญ์ œ ๋ฌธ์ œ ์ˆ˜์ • * feat: cd.yml release ๋ธŒ๋žœ์น˜ ์ƒ์„ฑ ๋ฐ ์šด์˜ ํ™˜๊ฒฝ PR ์žก ๊ตฌํ˜„ * feat: cd.yml ํ˜„์žฌ ๋ธŒ๋žœ์น˜ ํ™•์ธ ์Šคํƒญ ์ถ”๊ฐ€ * fix: tj-actions ์•ก์…˜ ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ ์ˆ˜์ • * fix: branch-names ์•ก์…˜ ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ ์ˆ˜์ • * test: branch-names ์•ก์…˜ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * fix: event ๋ธŒ๋žœ์น˜ ์ฐพ๊ธฐ ๊ธฐ๋Šฅ ์ˆ˜์ • * style: tag branch ์ˆ˜์ • * feat: ์šด์˜ ํ™˜๊ฒฝ ์…‹ํŒ… (#49) * feat: application-prod.yml ๊ตฌํ˜„ * feat: import ์ตœ์ ํ™” * feat: ์œ ์ € ํŠธ๋žœ์žญ์…˜ ๋กœ๊น… ๋ฐ ๊ด€์ œ API ๊ตฌํ˜„ (#51) * style: HTTP ์›Œ๋”ฉ ์ˆ˜์ • * feat: ๋ฐ”์ง€(ํ•˜์˜), ์น˜๋งˆ, ๋ชจ์ž ์ถ”๋ก  ์„œ๋ฒ„ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * chore: gradle ์„ค์ • * chore: Log4j2 File_Appender ๋กœ๊น… ์ „๋žต ์ˆ˜๋ฆฝ * feat: LoggingAspect ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: LoggingStatus ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: LoggingStatusManager ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: LoggingTracer ๊ธฐ๋Šฅ ๊ตฌํ˜„ * style: image_searcher filter key ์ˆ˜์ • * style: index.html ์ด๋ฏธ์ง€ ์‚ฌ์ด์ฆˆ ์ˆ˜์ • * style: constants ๋ชจ๋ธ ๋ฐฐํฌ ๊ฒฝ๋กœ ์ˆ˜์ • * style: SketchConstants ๋ชจ๋ธ ์›Œ์ปค ๋ฐฐํฌ ๊ฒฝ๋กœ ์ˆ˜์ • * feat: log4j2-{profile}.yml ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: application-{profile}.yml ๋กœ๊น… ๊ธฐ๋Šฅ ๊ตฌํ˜„ * docs: .gitignore ๊ฒฝ๋กœ ์ถ”๊ฐ€ * feat: history ๋„๋ฉ”์ธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: historyRepository ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: HistoryFilter ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: HistoryInterceptor ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ์ถ”๋ก  ์ž…๋ ฅ ์„œ๋ฒ„ ์ด๋ฆ„ ๋กœ๊น… ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: StorageHandler ์ถ”๋ก  ์ƒํƒœ ๋กœ๊น… ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: LoggerBuilder ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: file logger ๊ธฐ๋Šฅ ๊ตฌํ˜„ * style: ๋กœ๊น… setup ํŒŒ์ผ ์ˆ˜์ • [logger -> setup_logger] * feat: InferenceException ์ปค์Šคํ…€ ์˜ˆ์™ธ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: LoggerConverter ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ์ถ”๋ก  ์ „์ฒด process ๋กœ๊น… ๊ธฐ๋Šฅ ๊ตฌํ˜„ * refactor: ํ•˜๋“œ ์ฝ”๋”ฉ ์ƒ์ˆ˜ ๋ถ„๋ฆฌ * feat: init.sql ์ถ”๋ก  ๊ด€์ œ API ํ…Œ์ด๋ธ” ์ถ”๊ฐ€ * refactor: HistoryInterceptor ๋ฉ”์„œ๋“œ ๋ถ„๋ฆฌ * style: HTML ์„œ๋ฒ„ ์˜ˆ์™ธ ๋ฉ”์„ธ์ง€ ์ถœ๋ ฅ * feat: ์ถ”๋ก  ๊ฒฐ๊ณผ์— ๋”ฐ๋ฅธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ [์„ฑ๊ณต, ์‹คํŒจ] * feat: ์ถ”๋ก  ์ค‘ Error ์˜ˆ์™ธ ์ฒ˜๋ฆฌ * feat: ์ถ”๋ก  ์™ธ, ๋น„์ •์ƒ ์ข…๋ฃŒ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ [๋„คํŠธ์›Œํฌ, ํ•˜๋“œ์›จ์–ด] * feat: Redis Stream Consumer Group ๋ฐฉ์‹ ๋„์ž… * feat: ์ถ”๋ก  ์ค‘ ์‹คํŒจ ์ƒํƒœ ์ฒ˜๋ฆฌ [Fail Error] * chore: Slack API ์„ค์ • * feat: SlackAlarmGenerator ๊ธฐ๋Šฅ ๊ตฌํ˜„ * feat: ControllerAdvice ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์•Œ๋žŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„ * fix: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์›Œ์ปค URI ์ˆ˜์ • * feat: HistoryInterceptor ์˜ˆ์™ธ ํŒจํ„ด ์ถ”๊ฐ€ * test: ์ถ”๋ก  ์‹คํŒจ Process ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„ * style: Exception.class slack hook ๋Œ€์ƒ ์ œ๊ฑฐ * style: StorageHandler ์ถ”๋ก  ์ƒํƒœ ๊ฐฑ์‹  ์ฝ”๋“œ ์ˆ˜์ •
1 parent de50738 commit e90f3e5

File tree

69 files changed

+1788
-94
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1788
-94
lines changed

โ€Žbackend/.gitignoreโ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ bin/
2323
*.iml
2424
*.ipr
2525
out/
26+
logs/
2627
!**/src/main/**/out/
2728
!**/src/test/**/out/
2829

โ€Žbackend/build.gradleโ€Ž

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ configurations {
1818
compileOnly {
1919
extendsFrom annotationProcessor
2020
}
21+
all {
22+
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
23+
}
2124
asciidoctorExt
2225
}
2326

@@ -66,9 +69,16 @@ dependencies {
6669
// http
6770
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
6871

69-
// RestDocs
72+
// restdocs
7073
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
7174
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
75+
76+
// logging
77+
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
78+
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
79+
80+
// slack
81+
implementation("com.slack.api:slack-api-client:1.45.2")
7282
}
7383

7484
tasks.named('test') {

โ€Žbackend/src/main/java/com/sketch2fashion/backend/config/JobConfig.javaโ€Ž

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,54 @@
55
import com.sketch2fashion.backend.service.dto.MessageResponseDto;
66
import com.sketch2fashion.backend.support.ClothesModelHttpCaller;
77
import com.sketch2fashion.backend.support.consume.ClothesJobConsumerListener;
8+
import com.sketch2fashion.backend.support.publish.MessagePublisher;
9+
import org.springframework.beans.factory.annotation.Qualifier;
810
import org.springframework.context.annotation.Bean;
911
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.data.redis.RedisSystemException;
1013
import org.springframework.data.redis.connection.RedisConnectionFactory;
14+
import org.springframework.data.redis.connection.stream.Consumer;
1115
import org.springframework.data.redis.connection.stream.ObjectRecord;
1216
import org.springframework.data.redis.connection.stream.ReadOffset;
1317
import org.springframework.data.redis.connection.stream.StreamOffset;
18+
import org.springframework.data.redis.core.RedisTemplate;
1419
import org.springframework.data.redis.core.convert.MappingRedisConverter;
1520
import org.springframework.data.redis.core.convert.RedisConverter;
1621
import org.springframework.data.redis.core.mapping.RedisMappingContext;
1722
import org.springframework.data.redis.hash.ObjectHashMapper;
1823
import org.springframework.data.redis.serializer.StringRedisSerializer;
1924
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
2025

26+
import java.time.Duration;
2127
import java.util.Collections;
28+
import java.util.concurrent.Executor;
29+
import java.util.concurrent.LinkedBlockingDeque;
30+
import java.util.concurrent.ThreadPoolExecutor;
31+
import java.util.concurrent.TimeUnit;
32+
import java.util.concurrent.atomic.AtomicInteger;
2233

2334
import static com.sketch2fashion.backend.support.publish.Type.CLOTHES;
35+
import static com.sketch2fashion.backend.utils.SketchConstants.MODEL_RUN_CONSUMER_GROUP;
36+
import static com.sketch2fashion.backend.utils.SketchConstants.MODEL_RUN_CONSUMER_NAME;
2437

2538
@Configuration
2639
public class JobConfig {
2740

41+
private static final int PROCESSORS = Runtime.getRuntime().availableProcessors();
42+
43+
@Bean
44+
public Executor dataRedisStreamExecutor() {
45+
AtomicInteger index = new AtomicInteger(1);
46+
ThreadPoolExecutor executor = new ThreadPoolExecutor(PROCESSORS, PROCESSORS, 0, TimeUnit.SECONDS,
47+
new LinkedBlockingDeque<>(), r -> {
48+
Thread thread = new Thread(r);
49+
thread.setName("dataRedisConsumer-executor" + index.getAndIncrement());
50+
thread.setDaemon(true);
51+
return thread;
52+
});
53+
return executor;
54+
}
55+
2856
@Bean
2957
RedisMappingContext redisMappingContext() {
3058
final RedisMappingContext ctx = new RedisMappingContext();
@@ -42,21 +70,32 @@ ObjectHashMapper hashMapper(RedisConverter converter) {
4270
return new ObjectHashMapper(converter);
4371
}
4472

73+
4574
@Bean(initMethod = "start", destroyMethod = "stop")
4675
public StreamMessageListenerContainer<String, ObjectRecord<String, MessageResponseDto>> streamMessageListenerContainer(
76+
Executor dataRedisStreamExecutor,
4777
RedisConnectionFactory redisConnectionFactory,
4878
ObjectHashMapper hashMapper,
4979
ClothesModelHttpCaller clothesModelHttpCaller,
5080
ObjectMapper objectMapper,
51-
ResultService resultService
81+
ResultService resultService,
82+
@Qualifier("streamRedisTemplate") RedisTemplate<String, Object> redisStreamTemplate
5283
) {
84+
try {
85+
redisStreamTemplate.opsForStream().createGroup(CLOTHES.toString(), MODEL_RUN_CONSUMER_GROUP);
86+
} catch (RedisSystemException redisSystemException) {
87+
// ignore
88+
}
89+
5390
final StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, MessageResponseDto>> options =
5491
StreamMessageListenerContainer.StreamMessageListenerContainerOptions
5592
.builder()
5693
.batchSize(1)
94+
.executor(dataRedisStreamExecutor)
5795
.keySerializer(new StringRedisSerializer())
5896
.hashValueSerializer(new StringRedisSerializer())
5997
.hashValueSerializer(new StringRedisSerializer())
98+
.pollTimeout(Duration.ofSeconds(1))
6099
.objectMapper(hashMapper)
61100
.targetType(MessageResponseDto.class)
62101
.build();
@@ -67,9 +106,13 @@ public StreamMessageListenerContainer<String, ObjectRecord<String, MessageRespon
67106
final StreamMessageListenerContainer.StreamReadRequest<String> streamReadRequest =
68107
StreamMessageListenerContainer.StreamReadRequest
69108
.builder(StreamOffset.create(CLOTHES.toString(), ReadOffset.lastConsumed()))
109+
.consumer(Consumer.from(MODEL_RUN_CONSUMER_GROUP, MODEL_RUN_CONSUMER_NAME))
110+
.autoAcknowledge(false)
111+
.cancelOnError(throwable -> false)
70112
.build();
71113

72-
clothesMessageListenerContainer.register(streamReadRequest, new ClothesJobConsumerListener(clothesModelHttpCaller, objectMapper, resultService));
114+
clothesMessageListenerContainer.register(streamReadRequest, new ClothesJobConsumerListener(clothesModelHttpCaller, objectMapper, resultService, redisStreamTemplate));
115+
73116
return clothesMessageListenerContainer;
74117
}
75118
}

โ€Žbackend/src/main/java/com/sketch2fashion/backend/config/RedisConfig.javaโ€Ž

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.sketch2fashion.backend.config;
22

33
import com.sketch2fashion.backend.service.dto.ResultResponseDto;
4+
import org.springframework.beans.factory.annotation.Qualifier;
45
import org.springframework.beans.factory.annotation.Value;
56
import org.springframework.cache.annotation.EnableCaching;
67
import org.springframework.context.annotation.Bean;
@@ -31,14 +32,14 @@ public RedisConnectionFactory redisConnectionFactory() {
3132
}
3233

3334
@Bean
34-
public Jackson2JsonRedisSerializer<ResultResponseDto> jsonRedisSerializer() {
35-
return new Jackson2JsonRedisSerializer<>(ResultResponseDto.class);
35+
public Jackson2JsonRedisSerializer<Object> jsonRedisSerializer() {
36+
return new Jackson2JsonRedisSerializer<>(Object.class);
3637
}
3738

3839
@Bean
3940
public RedisCacheManager cacheManager(
4041
RedisConnectionFactory redisConnectionFactory,
41-
Jackson2JsonRedisSerializer<ResultResponseDto> jsonRedisSerializer
42+
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer
4243
) {
4344
final RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
4445
.disableCachingNullValues()
@@ -52,11 +53,11 @@ public RedisCacheManager cacheManager(
5253
}
5354

5455
@Bean
55-
public RedisTemplate<String, ResultResponseDto> redisTemplate(
56+
public RedisTemplate<String, Object> redisTemplate(
5657
RedisConnectionFactory redisConnectionFactory,
57-
Jackson2JsonRedisSerializer<ResultResponseDto> jsonRedisSerializer
58+
Jackson2JsonRedisSerializer<Object> jsonRedisSerializer
5859
) {
59-
final RedisTemplate<String, ResultResponseDto> redisTemplate = new RedisTemplate<>();
60+
final RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
6061

6162
redisTemplate.setConnectionFactory(redisConnectionFactory);
6263
redisTemplate.setKeySerializer(new StringRedisSerializer());
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.sketch2fashion.backend.config;
2+
3+
import com.sketch2fashion.backend.controller.history.HistoryInterceptor;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
7+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
8+
9+
@Configuration
10+
@RequiredArgsConstructor
11+
public class WebConfig implements WebMvcConfigurer {
12+
13+
private final HistoryInterceptor historyInterceptor;
14+
15+
@Override
16+
public void addInterceptors(InterceptorRegistry registry) {
17+
registry.addInterceptor(historyInterceptor)
18+
.order(1)
19+
.addPathPatterns("/**")
20+
.excludePathPatterns("/css/**", "/*.ico", "/error");
21+
}
22+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.sketch2fashion.backend.config.logging;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.aspectj.lang.ProceedingJoinPoint;
7+
import org.aspectj.lang.annotation.Around;
8+
import org.aspectj.lang.annotation.Aspect;
9+
import org.aspectj.lang.annotation.Pointcut;
10+
import org.springframework.stereotype.Component;
11+
import org.springframework.web.context.request.RequestContextHolder;
12+
import org.springframework.web.context.request.ServletRequestAttributes;
13+
14+
@Slf4j
15+
@Aspect
16+
@Component
17+
@RequiredArgsConstructor
18+
public class LoggingAspect {
19+
20+
private final LoggingTracer loggingTracer;
21+
private final LoggingStatusManager loggingStatusManager;
22+
23+
@Pointcut("execution(public * com.sketch2fashion.backend..*(..)) "
24+
+ "&& !execution(public * com.sketch2fashion.backend.config.logging..*(..))")
25+
private void allComponents() {
26+
}
27+
28+
@Pointcut("execution(public * com.sketch2fashion.backend.controller..*Controller.*(..))")
29+
private void allController() {
30+
}
31+
32+
@Around("allComponents()")
33+
public Object doLogTrace(final ProceedingJoinPoint joinPoint) throws Throwable {
34+
final String message = joinPoint.getSignature().toShortString();
35+
final Object[] args = joinPoint.getArgs();
36+
try {
37+
loggingTracer.begin(message, args);
38+
final Object result = joinPoint.proceed();
39+
loggingTracer.end(message);
40+
return result;
41+
} catch (final Exception e) {
42+
loggingTracer.exception(message, e);
43+
throw e;
44+
}
45+
}
46+
47+
@Around("allController()")
48+
public Object doLogRequest(final ProceedingJoinPoint joinPoint) throws Throwable {
49+
loggingStatusManager.syncStatus();
50+
final String taskId = loggingStatusManager.getTaskId();
51+
final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
52+
.getRequest();
53+
final String method = request.getMethod();
54+
final String requestURI = request.getRequestURI();
55+
final Object[] args = joinPoint.getArgs();
56+
log.info("[{}] {} {} args={}", taskId, method, requestURI, args);
57+
58+
try {
59+
return joinPoint.proceed();
60+
} finally {
61+
loggingStatusManager.release();
62+
}
63+
}
64+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.sketch2fashion.backend.config.logging;
2+
3+
public class LoggingStatus {
4+
5+
private final String taskId;
6+
private final long startTimeMillis;
7+
private int depthLevel = 0;
8+
9+
public LoggingStatus(final String taskId, final long startTimeMillis) {
10+
this.taskId = taskId;
11+
this.startTimeMillis = startTimeMillis;
12+
}
13+
14+
public void enterDepth() {
15+
depthLevel++;
16+
}
17+
18+
public void comeOutDepth() {
19+
depthLevel--;
20+
}
21+
22+
public boolean isEndDepth() {
23+
return depthLevel == 0;
24+
}
25+
26+
public String getTaskId() {
27+
return taskId;
28+
}
29+
30+
public long getStartTimeMillis() {
31+
return startTimeMillis;
32+
}
33+
34+
public int getDepthLevel() {
35+
return depthLevel;
36+
}
37+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.sketch2fashion.backend.config.logging;
2+
3+
import org.springframework.stereotype.Component;
4+
5+
import java.util.UUID;
6+
7+
@Component
8+
public class LoggingStatusManager {
9+
10+
private final ThreadLocal<LoggingStatus> statusContainer =
11+
new ThreadLocal<>();
12+
13+
public void syncStatus() {
14+
final LoggingStatus status = statusContainer.get();
15+
if (status != null) {
16+
status.enterDepth();
17+
return;
18+
}
19+
final LoggingStatus firstLoggingStatus = createLoggingStatus();
20+
statusContainer.set(firstLoggingStatus);
21+
}
22+
23+
private LoggingStatus createLoggingStatus() {
24+
final String traceId = UUID.randomUUID().toString();
25+
final long startTimeMillis = System.currentTimeMillis();
26+
return new LoggingStatus(traceId, startTimeMillis);
27+
}
28+
29+
public String getTaskId() {
30+
final LoggingStatus status = getExistLoggingStatus();
31+
return status.getTaskId();
32+
}
33+
34+
public long getStartTimeMillis() {
35+
final LoggingStatus status = getExistLoggingStatus();
36+
return status.getStartTimeMillis();
37+
}
38+
39+
public int getDepthLevel() {
40+
final LoggingStatus status = getExistLoggingStatus();
41+
return status.getDepthLevel();
42+
}
43+
44+
public void release() {
45+
final LoggingStatus status = getExistLoggingStatus();
46+
if (status.isEndDepth()) {
47+
statusContainer.remove();
48+
return;
49+
}
50+
status.comeOutDepth();
51+
}
52+
53+
private LoggingStatus getExistLoggingStatus() {
54+
final LoggingStatus status = statusContainer.get();
55+
if (status == null) {
56+
throw new IllegalStateException();
57+
}
58+
return status;
59+
}
60+
}

0 commit comments

Comments
ย (0)