2026 ์๋ช ๋ํ๊ต ์กธ์ ํ๋ก์ ํธ
๋ถ์ฐ๋ ๊ด๊ณ ๋ฐ์ดํฐ๋ฅผ ํ๋์ ๋์๋ณด๋๋ก ๊ด๊ณ ์ฑ๊ณผ๋ฅผ ํตํฉํ๊ณ AI๊ฐ ๋ถ์ํด ๋๋ฆฝ๋๋ค
๐ ์๋ช ๋ํ๊ต ๊ต๋ด ์ฐฝ์ ์์ด๋์ด ๊ฒฝ์ง๋ํ ๋์
๐ ๋ชจ๋์ ์ฐฝ์ ๊ณต๋ชจ์ 1๊ธฐ ์ ์ ยท ๋ณธ์ ์ง์ถ
WhereYouAd๋ Google, Naver, Meta 3๊ฐ ๊ด๊ณ ํ๋ซํผ์ ์ฑ๊ณผ ๋ฐ์ดํฐ๋ฅผ ๋จ์ผ ๋์๋ณด๋์ ํตํฉํ๋ B2B SaaS์ ๋๋ค. ํ ๋จ์ ์ํฌ์คํ์ด์ค๋ก ๋ฉค๋ฒ๊ฐ ํ์ ํ๋ฉฐ, AI ๋ถ์ ๋ฆฌํฌํธ๋ฅผ ์์ฑํ๊ณ PDF๋ก ์ ์ฅํ ์ ์์ต๋๋ค.
WhereYouAd Backend๋ Spring Boot ๊ธฐ๋ฐ์ 2026 ์บก์คํค ์กธ์ ํ๋ก์ ํธ์ ๋๋ค. ์๋ก ๋ค๋ฅธ ์ธ์ฆ ๋ฐฉ์๊ณผ ์๋ต ์คํค๋ง๋ฅผ ๊ฐ์ง 3๊ฐ ๊ด๊ณ ํ๋ซํผ API๋ฅผ ํ๋์ ๋๋ฉ์ธ ๋ชจ๋ธ๋ก ์ ๊ทํํ๊ณ , Kafka ๊ธฐ๋ฐ ํด๋ฆญ ์ด๋ฒคํธ ํ์ดํ๋ผ์ธยทSSE ์ค์๊ฐ ์ ์กยทOpenAI ๋ฆฌํฌํธ ์์ฑ๊น์ง ๋๋ฉ์ธ ๊ณ์ธตํ ์ํคํ ์ฒ ์์์ ์ง์ ์ค๊ณยท๊ตฌํํฉ๋๋ค.
- Google Ads / Meta Marketing / Naver Search Ad API ํตํฉ ๋ฐ ์งํ ์ ๊ทํ
- OAuth2 ์์ ๋ก๊ทธ์ธ(NaverยทGoogleยทKakao) + JWT ์ธ์ฆ/์ฌ๋ฐ๊ธ
- Kafka ๊ธฐ๋ฐ ํด๋ฆญ ์ด๋ฒคํธ ์์ง ยท ๋ด ํํฐ๋ง ยท SSE ์ค์๊ฐ ์ ์ก
- ๊ด๊ณ ํธ๋ํน ๋งํฌ ๋ฐ๊ธ (shortURL ์ฝ๋ + ๋ฆฌ๋ค์ด๋ ํธ ์์ง)
- OpenAI ๊ธฐ๋ฐ AI ๋ถ์ ๋ฆฌํฌํธ ๋น๋๊ธฐ ์์ฑ ๋ฐ ์ด๋ฉ์ผ ๋ฐ์ก
- ์กฐ์ง(์ํฌ์คํ์ด์ค) ยท ๋ฉค๋ฒ ์ด๋ ยท RBAC ๊ถํ ๊ด๋ฆฌ
- ๊ธฐ๊ฐ ๋น๊ต ๊ธฐ๋ฐ ํ์๋ผ์ธ ์ฑ๊ณผ ๋ถ์
- ์ฃผ๊ฐ ๋ฆฌํฌํธ ์ค์ผ์ค๋ฌ + Discord / Slack Webhook ์๋ฆผ
| Category | Stack |
|---|---|
| Core | Java 17, Spring Boot 3.5.9, Spring Cloud 2024.0.1, Gradle |
| Security | Spring Security, OAuth2 Client (NaverยทGoogleยทKakao), JWT(jjwt 0.11.5), AES |
| Persistence | MySQL 8.0, Spring Data JPA (Hibernate) |
| Cache | Redis (RefreshToken ยท ์ธ์ฆ ์ฝ๋ ยท ํ ํฐ ์บ์) |
| Messaging | Apache Kafka 3.7.0 (ํด๋ฆญ ์ด๋ฒคํธ Producer / Consumer) |
| Realtime | SSE (SseEmitter + SseEmitterRepository) |
| HTTP Client | OpenFeign (OpenAI), WebClient (GoogleยทMetaยทNaver Ads) |
| Ad Platform | Google Ads API 42.0.0, Meta Marketing API, Naver Search Ad API |
| AI | OpenAI API (๋ถ์ ๋ฆฌํฌํธ ยท ์์ฐ ์ถ์ฒ) |
| Notification | Gmail SMTP + Thymeleaf, Discord / Slack Webhook, CoolSMS(nurigo 4.3.0) |
| Storage | AWS S3 (Spring Cloud AWS 3.4.2) |
| Resilience | Spring Retry + Spring AOP (์ธ๋ถ API ์ฌ์๋) |
| Scheduling | Spring Scheduler (ํ๋ซํผ ๋๊ธฐํ ยท ์ฃผ๊ฐ ๋ฆฌํฌํธ ยท ๊ณ์ ์ ๋ฆฌ) |
| Docs | Springdoc OpenAPI 2.8.0 (Swagger UI) |
| Config | Spring Dotenv 4.0.0 (.env ๋ก๋ฉ) |
| Test | JUnit 5, Spring Boot Test, Spring Security Test |
| Deploy | Docker, Docker Compose, Docker Hub, AWS EC2 (Bastion ๊ฒฝ์ ๋ฐฐํฌ) |
java -version
# openjdk version "17.x.x"Docker / Docker Compose๊ฐ ์ค์น๋์ด ์์ด์ผ ํฉ๋๋ค. (MySQL ยท Redis ยท Kafka ๋ก์ปฌ ๊ตฌ๋)
cp .env.example .env./gradlew bootRundocker-compose up -d./gradlew clean build -x test์ฑ์ด ๋จ๋ฉด http://localhost:8080/swagger-ui/index.html ์์ API ๋ฌธ์๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
| Command | Description |
|---|---|
./gradlew build |
ํ ์คํธ๋ฅผ ํฌํจํ ์ ์ฒด ๋น๋๋ฅผ ์ํํฉ๋๋ค. |
./gradlew clean build -x test |
ํ ์คํธ๋ฅผ ์ ์ธํ๊ณ ๋น๋ํฉ๋๋ค. (CD์์ ์ฌ์ฉ) |
./gradlew test |
ํ ์คํธ๋ง ์คํํฉ๋๋ค. (MySQL + Redis ํ์) |
./gradlew bootRun |
๋ก์ปฌ ์๋ฒ๋ฅผ ์คํํฉ๋๋ค. (.env ํ์) |
docker-compose up -d |
App + MySQL + Redis + Kafka ์ ์ฒด ์คํ์ ๋์๋๋ค. |
docker-compose logs -f app |
์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์ด๋ ๋ก๊ทธ๋ฅผ ํ์ธํฉ๋๋ค. |
src/main/java/com/whereyouad/WhereYouAd
โโโ domains # ํต์ฌ ๋น์ฆ๋์ค ๋๋ฉ์ธ (๊ณ์ธตํ ํจํค์ง)
โ โโโ user/ # ํ์, ์์
๋ก๊ทธ์ธ, ๊ณ์ ์ฐพ๊ธฐยท๋น๋ฐ๋ฒํธ ์ฌ์ค์
โ โโโ organization/ # ์ํฌ์คํ์ด์ค, ๋ฉค๋ฒ ์ด๋, OrgRole(RBAC)
โ โโโ project/ # ํ๋ก์ ํธ ๋จ์ ๊ด๊ณ ์บ ํ์ธ ๊ทธ๋ฃน
โ โโโ platform/ # ํ๋ซํผ ๊ณ์ ยท์ฐ๋ ์ํ, ๋๊ธฐํ ์ค์ผ์ค๋ฌ
โ โโโ advertisement/ # ์บ ํ์ธยท๊ด๊ณ ๊ทธ๋ฃนยท์์ฌยท์์ฐยทMetricFact
โ โ โโโ domain/service/adapi/{google,meta} # ํ๋ซํผ๋ณ ์ฐ๋ ์๋น์ค
โ โโโ dashboard/ # ํตํฉ KPI ์ง๊ณ, ํด๋ฆญ ๋์๋ณด๋
โ โโโ timeline/ # ํ์๋ผ์ธ CRUD ๋ฐ ๋น๊ต ์ฑ๊ณผ ๋ถ์
โ โโโ click/ # ํธ๋ํน URL, BotDetector, ClickEventProducer
โ โโโ ai/ # OpenAI ๋ฆฌํฌํธ ๋น๋๊ธฐ ์์ฑ (AIStatus)
โ โโโ notification/ # ์๋ฆผ ์ค์ , ์ฃผ๊ฐ ๋ฆฌํฌํธ ์ค์ผ์ค๋ฌ, ๋ฐ์ก ์ด๋ ฅ
โ โโโ image/ # ์ด๋ฏธ์ง ์
๋ก๋
โ
โ โโโ {domain}/ # ๊ณตํต ๊ณ์ธต ๊ตฌ์กฐ
โ โโโ application/ # dto/request ยท dto/response ยท mapper
โ โโโ domain/ # service (๋น์ฆ๋์ค ๋ก์ง) ยท constant (Enum)
โ โโโ persistence/ # entity (JPA) ยท repository
โ โโโ presentation/ # Controller + docs (Swagger ์ธํฐํ์ด์ค ๋ถ๋ฆฌ)
โ โโโ exception/ # Handler + code (BaseErrorCode ๊ตฌํ์ฒด)
โ
โโโ global # ์ ์ญ ์ธํ๋ผ ๋ ์ด์ด
โ โโโ security/
โ โ โโโ jwt/ # JwtTokenProvider, JwtAuthenticationFilter
โ โ โโโ oauth2/ # NaverยทGoogleยทKakao Response ์ถ์ํ + SuccessHandler
โ โ โโโ cookie/ # AuthCookieFactory (SameSiteยทSecure ์ ์ด)
โ โ โโโ SecurityConfig.java
โ โโโ adapi/ # AdAuthFactory + AdAuthStrategy (ํ๋ซํผ ์ธ์ฆ ์ถ์ํ)
โ โโโ config/ # Swagger, Redis, WebClient, OpenAIFeign, Retry
โ โโโ response/ # BaseResponse, DataResponse<T>, ErrorResponse
โ โโโ exception/ # ์ ์ญ ์์ธ ํธ๋ค๋ฌ (BaseErrorCode โ HTTP ๋งคํ)
โ โโโ sse/ # SseEmitterRepository (์ค์๊ฐ ํด๋ฆญ ์คํธ๋ฆผ)
โ โโโ scheduler/ # ๊ณต์ฉ ์ค์ผ์ค ์ค์
โ โโโ common/ # BaseEntity (Auditing)
โ โโโ utils/ # AESUtil, RedisUtil, CursorUtil, BudgetCalculator, MetricCalculator
โ
โโโ infrastructure/client # ์ธ๋ถ ์์คํ
์ฐ๋ ํด๋ผ์ด์ธํธ
โโโ google/ # GoogleAdWebClient + converter/dto
โโโ meta/ # Meta Marketing API client/config/converter/dto
โโโ naver/ # Naver Search Ad API client/converter/dto
โโโ openai/ # Feign client + prompt + service
โโโ kafka/ # KafkaClickEventProducer, ClickConsumer
โโโ aws/s3/ # S3 ์ด๋ฏธ์ง ์
๋ก๋
โโโ mail/ # AIMailService (Thymeleaf ํ
ํ๋ฆฟ)
โโโ discord/ # DiscordWebhookClient
โโโ slack/ # SlackWebhookClient
| ์ํฌํ๋ก | ํธ๋ฆฌ๊ฑฐ | ๋ด์ฉ |
|---|---|---|
ci.yml |
push / PR โ develop, main | MySQLยทRedis ์๋น์ค ์ปจํ
์ด๋ ๊ธฐ๋ โ JDK 17 ์
์
โ ./gradlew build ๊ฒ์ฆ |
cd.yml |
push โ develop | clean build -x test โ Docker ์ด๋ฏธ์ง ๋น๋ยทHub ํธ์ โ Bastion ๊ฒฝ์ EC2 ๋ฐฐํฌ |
๋ฐฐํฌ ์๋ฒ์์๋ docker-compose pull && up -d๋ก ์ปจํ
์ด๋๋ฅผ ๊ต์ฒดํ๊ณ , docker image prune -f๋ก ์์ฌ ์ด๋ฏธ์ง๋ฅผ ์ ๋ฆฌํฉ๋๋ค.
feat/#1-descriptionfix/#1-descriptionrefactor/#1-descriptiondeploy/#1-descriptionchore/#1-description
develop์ด ๋ฐฐํฌ ๊ธฐ์ค ๋ธ๋์น์ด๋ฉฐ, ๋ชจ๋ ์์
์ ์ด์ ๋ฒํธ ๊ธฐ๋ฐ ๋ธ๋์น์์ ์งํ ํ PR๋ก ๋ณํฉํฉ๋๋ค.
Angular Commit Convention์ Gitmoji๋ฅผ ๊ฒฐํฉํ :gitmoji: type: subject ํ์์ ๋ฐ๋ฆ
๋๋ค.
| Gitmoji | Type | ์ค๋ช |
|---|---|---|
โจ :sparkles: |
feat |
์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ |
๐ :bug: |
fix |
๋ฒ๊ทธ ์์ |
๐ :memo: |
docs |
๋ฌธ์ ์์ (README, Swagger Docs ๋ฑ) |
๐จ :art: |
style |
ํฌ๋งทํ ยท๊ณต๋ฐฑ ๋ฑ (๋์ ๋ณ๊ฒฝ ์์) |
โป๏ธ :recycle: |
refactor |
์ฝ๋ ๋ฆฌํฉํ ๋ง (๊ธฐ๋ฅ ๋ณํ ์์) |
โ
:white_check_mark: |
test |
ํ ์คํธ ์ฝ๋ ์ถ๊ฐ/์์ |
๐ง :wrench: |
chore |
๋น๋ยท์ค์ ยท์์กด์ฑ ๋ฑ ๊ธฐํ ๋ณ๊ฒฝ |
๐ :green_heart: |
ci |
CI ์ค์ ๋ณ๊ฒฝ |
๐ :rocket: |
deploy |
๋ฐฐํฌ ๊ด๋ จ ์์ (Dockerfile, docker-compose ๋ฑ) |
- PR ์ ๋ชฉ์
[Type/#์ด์๋ฒํธ] ์์ ์์ฝํ์์ ์ฌ์ฉํฉ๋๋ค. (์:[Feat/#1] ๋ก๊ทธ์ธ API ๊ตฌํ) - GitHub Issue๋ฅผ ๋จผ์ ๋ฑ๋กํ๊ณ PR ๋ณธ๋ฌธ์
closes #์ด์๋ฒํธ๋ฅผ ํฌํจํฉ๋๋ค. - ๋ฆฌ๋ทฐ ๋ผ๋ฒจ๋ก ๋ฐ์ ์ฐ์ ์์๋ฅผ ๊ตฌ๋ถํฉ๋๋ค.
| Label | ์๋ฏธ | ์ฒ๋ฆฌ |
|---|---|---|
P1 |
ํ์ ๋ฐ์ (Critical) | ๋ฐ์ ์ ๋จธ์ง ๋ถ๊ฐ |
P2 |
์ ๊ทน ๊ถ์ฅ (Recommended) | ๋ฐ์ ๊ถ์ฅ |
P3 |
์ ์ (Suggestion) | ์์ฑ์ ์์จ |
P4 |
๋จ์ ํ์ธ (Nit) | ์คํยท์นญ์ฐฌ ๋ฑ |
- ๋ชจ๋ API ์๋ต์
BaseResponse๊ณ์ด ๋ํผ(DataResponse<T>,ErrorResponse)๋ก ํต์ผํฉ๋๋ค. - ์๋ฌ๋ ๋๋ฉ์ธ๋ณ
BaseErrorCode๊ตฌํ์ฒด๋ก ์ ์ํ๊ณ , ๋๋ฉ์ธ ์์ธ๋ฅผ ๋์ ธ ์ ์ญ ํธ๋ค๋ฌ๊ฐ HTTP๋ก ๋งคํํฉ๋๋ค. - Swagger ์ด๋
ธํ
์ด์
์
presentation/docs/์ธํฐํ์ด์ค๋ก ๋ถ๋ฆฌํด Controller ์ฝ๋๋ฅผ ๊นจ๋ํ๊ฒ ์ ์งํฉ๋๋ค. - ์ธ๋ถ ๊ด๊ณ API ์ธ์ฆ์
AdAuthFactory+AdAuthStrategy๋ก ์ถ์ํํด ํ๋ซํผ ์ถ๊ฐ ์ ์ ๋ต๋ง ํ์ฅํฉ๋๋ค. - ์ธ๋ถ HTTP ํธ์ถ์ Feign / WebClient๋ก ์ผ์ํํ๊ณ , ์คํจ ๊ฐ๋ฅ ๊ตฌ๊ฐ์ Spring Retry๋ก ๊ฐ์๋๋ค.
- ํ ํฐยท์ํฌ๋ฆฟ ๋ฑ ๋ฏผ๊ฐ ์ ๋ณด๋
AESUtil๋ก ์ํธํํด ์ ์ฅํฉ๋๋ค. - ๋ชฉ๋ก ์กฐํ ์ ๋ฌดํ ์คํฌ๋กค์
CursorUtil๊ธฐ๋ฐ ์ปค์ ํ์ด์ง๋ค์ด์ ์ ์ฌ์ฉํฉ๋๋ค.
๋ฐฑ์๋ |
๋ฐฑ์๋ |
๋ฐฑ์๋ |