
@docker-compose.yaml ์ฌ๊ธฐ์ ๋ฌด์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ํด ๋ ๋์ค๋ฅผ ์ค์นํ๋ ค๊ณ ํด. ์ด ํ๋ก์ ํธ์ ๊ฐ์ฅ ์ ํฉํ ์ ๋ต์ ์ ์ํด์ค.

@discovery @eureka ํ์ฌ ์ด ๋๊ฐ๋ก ์ ๋ ์นด๋ก ์คํธ๋ฆผ์ ๊ตฌ์ฑํ๊ณ ์์ด. ๊ทธ๋ฐ๋ฐ ์ด๊ฒ์ ๊ฒ์ดํธ์จ์ด๋ง ํ๋ญ์ค๋ก ๋ณํํด์ ๋ ผ๋ธ๋กํน, ๋น๋๊ธฐ ๊ตฌ์กฐ๋ก ๋ณํํ๋ ค๊ณ ํด. ์ด๋ณด์์ธ ๋ด๊ฐ ์ดํดํ๊ธฐ ์ข๊ฒ ๊ฐ์ฅ ํจ์จ์ ์ธ ๋ฐฉ์์ผ๋ก ์ ๋ต์ ์ง์ค
sonnet ์ผ๋ก!
ํ์ฌ ๊ตฌ์กฐ๋ฅผ ๋ถ์ํ๊ณ API Gateway๋ฅผ WebFlux๋ก ๋ณํํ๋ ์ ๋ต์ ์ ์ํ๊ฒ ์ต๋๋ค.
[7 tools called]
์ด์ ์ค์ ๋ก Discovery Server์ build.gradle์ ์
๋ฐ์ดํธํ๊ฒ ์ต๋๋ค:
[2 tools called]
์ด์ Reactive Redis ์ค์ ํด๋์ค๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค:
[3 tools called]
Reactive ํํฐ๋ค์ ์์ฑํ๊ฒ ์ต๋๋ค:
[2 tools called]
Reactive ์๋ฌ ํธ๋ค๋ง ์ค์ ์ ์ถ๊ฐํ๊ฒ ์ต๋๋ค:
[1 tool called]
๋ง์ง๋ง์ผ๋ก ์
๋ฐ์ดํธ๋ application.yaml์ ์ ์ฉํ๊ฒ ์ต๋๋ค:
[3 tools called]
๋ง์ง๋ง์ผ๋ก ๋ชจ๋ ๋ณ๊ฒฝ์ฌํญ์ ํ์ธํ๊ณ ์๋ฃํ๊ฒ ์ต๋๋ค:
[2 tools called]
๋ฆฐํฐ ์๋ฌ๋ค์ ์์ ํ๊ฒ ์ต๋๋ค. ์ฃผ๋ก import ๋ฌธ์ ์ null ์์ ์ฑ ๋ฌธ์ ์
๋๋ค:
[3 tools called]
## ๐ WebFlux Gateway ๋ณํ ์๋ฃ!
### โ
์๋ฃ๋ ์์
๋ค
1. **๐ ์ ๋ต ๋ฌธ์ ์์ฑ**: `WEBFLUX_GATEWAY_STRATEGY.md` - ์ด๋ณด์๋ ์ดํดํ๊ธฐ ์ฌ์ด ์์ธํ ๊ฐ์ด๋
2. **๐ง ์์กด์ฑ ์
๋ฐ์ดํธ**: `build.gradle`์ Reactive Redis, ๋ชจ๋ํฐ๋ง, JSON ์ฒ๋ฆฌ ์์กด์ฑ ์ถ๊ฐ
3. **โ๏ธ Reactive ์ค์ ํด๋์ค๋ค**:
- `http://ReactiveRedisConfig.java` - ๋
ผ๋ธ๋กํน Redis ์ฐ๋
- `http://ReactiveRateLimitConfig.java` - 3๊ฐ์ง Rate Limiting ์ ๋ต (IP, API Key, User ID)
- `http://ReactiveErrorConfig.java` - WebFlux ์ ์ญ ์๋ฌ ์ฒ๋ฆฌ
4. **๐ Reactive ํํฐ๋ค**:
- `http://ReactiveLoggingFilter.java` - ๋
ผ๋ธ๋กํน ๋ก๊น
- `http://ReactiveMetricsFilter.java` - ์ฑ๋ฅ ๋ฉํธ๋ฆญ ์์ง
5. **๐ application.yaml ์ต์ ํ**: Redis ์ฐ๊ฒฐ, Rate Limiting, Netty ํ๋, Actuator ์ค์
### ๐ฏ ์ฃผ์ ๊ฐ์ ์ฌํญ
#### **๋
ผ๋ธ๋กํน I/O ์ต์ ํ**
- Redis ์ฐ๋์ `ReactiveRedisTemplate`์ผ๋ก ๋ณํ
- ๋ชจ๋ ํํฐ๋ฅผ Reactive ๋ฐฉ์์ผ๋ก ๊ตฌํ
- Netty ์๋ฒ ์ค์ ์ต์ ํ
#### **3๋จ๊ณ Rate Limiting ์ ๋ต**
```yaml
# ๊ฒ์ ์๋น์ค: IP ๊ธฐ๋ฐ (์ด๋น 10๊ฐ)
key-resolver: "#{@userKeyResolver}"
# Soccer ์๋น์ค: API Key ๊ธฐ๋ฐ (์ด๋น 50๊ฐ)
key-resolver: "#{@apiKeyResolver}"
# User ์๋น์ค: ์ฌ์ฉ์ ID ๊ธฐ๋ฐ (์ด๋น 30๊ฐ)
key-resolver: "#{@userIdKeyResolver}"
```
#### **๋ชจ๋ํฐ๋ง ๋ฐ ๋ฉํธ๋ฆญ์ค**
- Prometheus ๋ฉํธ๋ฆญ ์๋ ์์ง
- ์๋ต ์๊ฐ, ์์ฒญ ์, ์๋ฌ์จ ์ถ์
- Actuator ์๋ํฌ์ธํธ ํ์ฑํ
### ๐ ์ฑ๋ฅ ํฅ์ ์์ ํจ๊ณผ
1. **๋์ ์ฒ๋ฆฌ ๋ฅ๋ ฅ**: ๊ธฐ์กด ๋๋น **3-5๋ฐฐ** ํฅ์
2. **๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ**: ์ค๋ ๋ ํ ๋์ ์ด๋ฒคํธ ๋ฃจํ ์ฌ์ฉ์ผ๋ก **50-70%** ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ
3. **์๋ต ์๊ฐ**: ๋
ผ๋ธ๋กํน I/O๋ก **20-40%** ์๋ต ์๊ฐ ๋จ์ถ
4. **ํ์ฅ์ฑ**: ์ํ ํ์ฅ ์ ๋ ๋์ ์ฑ๋ฅ ์ ํ์ฑ
### ๐ง ๋ค์ ๋จ๊ณ
1. **์ปจํ
์ด๋ ์ฌ๋น๋**:
```bash
docker-compose build discoveryserver
docker-compose up -d discoveryserver
```
2. **ํ
์คํธ ํ์ธ**:
```bash
# Rate Limiting ํ
์คํธ
curl -X POST http://localhost:8080/api/search
# ๋ฉํธ๋ฆญ ํ์ธ
curl http://localhost:8080/actuator/metrics
# Prometheus ๋ฉํธ๋ฆญ
curl http://localhost:8080/actuator/prometheus
```
3. **๋ชจ๋ํฐ๋ง ์ค์ **: Grafana ๋์๋ณด๋ ๊ตฌ์ฑ์ผ๋ก ์ค์๊ฐ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
์ด์ API Gateway๊ฐ ์์ ํ **๋
ผ๋ธ๋กํน, ๋น๋๊ธฐ** ๊ตฌ์กฐ๋ก ๋ณํ๋์ด ๋์ ๋์์ฑ๊ณผ ์ฑ๋ฅ์ ์ ๊ณตํ ์ ์์ต๋๋ค! ๐//๋ง๋ ๋ฐฉํฅ์ผ๋ก ๊ฐ๊ณ ์๋ค๊ณ ํจ
๐ก ํต์ฌ ์ ๋ฆฌ
๋ฌ์ฑํ ๋ชฉํ
โ API Gateway๋ฅผ ๋ ผ๋ธ๋กํน, ๋น๋๊ธฐ ๊ตฌ์กฐ๋ก ์ต์ ํ
- ์ด๋ฏธ WebFlux ์ฌ์ฉ ์ค์ด์์!
- Redis, Circuit Breaker, Rate Limiting ์ถ๊ฐ
- ๋ชจ๋ํฐ๋ง ๋ฐ ๋ก๊น ๊ฐํ
์ด๋ณด์๊ฐ ๊ธฐ์ตํ ๊ฒ
- Mono/Flux ์ฌ์ฉ → ๋ ผ๋ธ๋กํน
- ๋ธ๋กํน ์ฝ๋ ๊ธ์ง → JDBC, Thread.sleep() ์ฌ์ฉ ์ ๋จ
- ๋ฆฌ์กํฐ๋ธ ์ฒด์ธ ์ ์ง → .block() ์ฌ์ฉ ๊ธ์ง
์ธ์ ์ฌ์ฉํ๋ฉด ์ข์๊น?
- โ API Gateway (ํ์ฌ ๊ตฌ์กฐ) โญ
- โ ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ
- โ ์ฑํ ์๋ฒ
- โ ๊ฐ๋จํ CRUD (์ค๋ฒ์์ง๋์ด๋ง)
โ ๋์ผํ ํต์ฌ ๋ฐฉํฅ
1. WebFlux ๋ ผ๋ธ๋กํน ๊ตฌ์กฐ โ
- ๋ ๋ค Reactive Programming ๊ธฐ๋ฐ
- Mono/Flux ์ฌ์ฉํ ๋น๋๊ธฐ ์ฒ๋ฆฌ
- Event Loop ๊ธฐ๋ฐ ๋์ ๋์์ฑ
2. Reactive Redis ํตํฉ โ
- Rate Limiting์ ์ํ Redis ์ฌ์ฉ
- ๋ ผ๋ธ๋กํน Redis ์ฐ๋
- ๋ถ์ฐ ์บ์ฑ ์ง์
3. Rate Limiting ์ ๋ต โ
- IP ๊ธฐ๋ฐ ์ ํ
- API Key/User ID ๊ธฐ๋ฐ ์ ํ
- Redis ๊ธฐ๋ฐ ๋ถ์ฐ Rate Limiting
4. ๋ชจ๋ํฐ๋ง & ๋ฉํธ๋ฆญ โ
- Actuator ํตํฉ
- ์ฑ๋ฅ ๋ฉํธ๋ฆญ ์์ง
- Health Check
5. ์๋ฌ ํธ๋ค๋ง โ
- ๊ธ๋ก๋ฒ ์๋ฌ ํธ๋ค๋ฌ
- ์น์ ํ ์๋ฌ ์๋ต
- Fallback ๋ฉ์ปค๋์ฆ
โญ ์ฐ๋ฆฌ ๊ตฌํ์ด ๋ ๋์ ์
1. Circuit Breaker ์ถ๊ฐ (๋ฉ์์ง์ ์์)
→ ๋ฐฑ์๋ ์ฅ์ ์ ์๋ ์ฐจ๋จ ๋ฐ ๋ณต๊ตฌ
2. Fallback Controller (๋ฉ์์ง์ ์์)
→ ์ฅ์ ์ ์น์ ํ ๋ฉ์์ง ์ ๊ณต
3. ๋ ํ๋ถํ ๋ฌธ์
- WEBFLUX_STRATEGY.md - ์ด๋ณด์์ฉ ์์ ๊ฐ์ด๋ (100+ ์ค)
- README.md - ๋น ๋ฅธ ์์
- test-gateway.ps1 / .sh - ์๋ ํ ์คํธ
- WEBFLUX_MIGRATION_SUMMARY.md - ์ ์ฒด ์์ฝ
4. ์ฆ์ ์คํ ๊ฐ๋ฅํ ํ ์คํธ
๐ ํต์ฌ ์์ฝ
๊ฐ์ ์ (95%)
โ WebFlux ๊ธฐ๋ฐ ๋ ผ๋ธ๋กํนโ Reactive Redis ํตํฉโ Rate Limiting (IP/User/Path)โ ๋ก๊น ๋ฐ ๋ชจ๋ํฐ๋งโ ์๋ฌ ํธ๋ค๋ง
์ถ๊ฐ๋ ์ (์ฐ๋ฆฌ๊ฐ ๋ ๊ตฌํ)
โญ Circuit Breaker (์ฅ์ ๋์)โญ Fallback Controller (UX ํฅ์)โญ HTTP ํด๋ผ์ด์ธํธ ์ต์ ํโญ ์ด๋ณด์์ฉ ๊ฐ์ด๋ (ํ์ต ์ฉ์ด)โญ ์๋ ํ ์คํธ ์คํฌ๋ฆฝํธ
์ฐจ์ด์ (๋จ์ ์คํ์ผ)
- ํด๋์ค ์ด๋ฆ๋ง ๋ค๋ฆ (ReactiveXXX vs GlobalXXX)
- Redis๋ Auto Configuration ํ์ฉ (๋ ๊ฐ๋จ)
ํ์ด์ฌ ๋ค์ด๊ฐ๊ฒ

ํด๋ ๋ง๋ค๊ธฐ
์ฐ์ ํ๋ฉด ๋ถํฐ ์งค ๊ฒ์
https://vercel.com/templates/portfolio
Portfolio Website Templates | Vercel
Browse our free curated list of blog website templates designed to impress from the moment they load.
vercel.com
๋ฒ์ ์์ ์ ๊ณตํ๋ ํ ํ๋ฆฟ์ด ์์
src ์๋๊ฒ ์ข๊ธดํจ

api

๋ฒ์ ๋ง์ถ๊ณ , yaml ์ ํํ๊ณ , ๋ํ๋์๋ ์์ด ๊ฐ!

๋ค์ด ๋ฐ์์ ๋ณต๋ถํ๊ณ ์ด๋ฆ๋ง ๋ฐ๊ฟ์ฃผ๊ธฐ



๋ฐ์ผ๋ก๋ ํ๋ ๋นผ์ ๋ณต๋ถํ๊ณ ์ ์ ์๋น์ค์๋ ๋ณต๋ถํ์ผ ๋ฃ๋ ๊ฒ
251124 ๋ง์ดํ๋ก์ ํธ , ๋ฅ์คํธ ์๋ฌ ์ก๊ธฐ
ํ๋กฌํํธ ์ฌ๊ธฐ์ ๋ฐ์ํ ์๋ฌ๋ฅผ ์ ๋ถ fix ํด์ค. jest ์ ๋ถํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค ์๋ฌ๋ฅผ ๋ฐ์์ํจ ๊ฒ์ ์ ๋ถ ์ ๊ฑฐํด์ค.

@(auth) ๋ฅ์คํธ auth๋ฅผ ์์ฐ๊ณ ๊ตฌ๊ธ auth๋ฅผ ์ธ๊ฑฐ์ผ. ์ด์ ๊ด๋ จ๋ ๋ชจ๋ ๋ถ๋ถ์ ์ญ์ ํด์ค


localhost:8080/docs ๋ฅผ ํ๋ฉด ์ค์จ๊ฑฐ๊ฐ ์๋ํ๋๋ก ํด์ค

Kakao Developers
์นด์นด์ค API๋ฅผ ํ์ฉํ์ฌ ๋ค์ํ ์ดํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํด ๋ณด์ธ์. ์นด์นด์ค ๋ก๊ทธ์ธ, ๋ฉ์์ง ๋ณด๋ด๊ธฐ, ์น๊ตฌ API, ์ธ๊ณต์ง๋ฅ API ๋ฑ์ ์ ๊ณตํฉ๋๋ค.
developers.kakao.com
251124 ๐ ์นด์นด์ค ๋ก๊ทธ์ธ์ ์ํ ํ์ํ ๊ฒ ์์ฒญ
์คํ๋ง MSA ํ๊ฒฝ์์ ์นด์นด์ค ๋ก๊ทธ์ธ(์นด์นด์ค OAuth2) ๋ฅผ ๊ตฌํํ๋ ค๋ฉด,
๋ฐ๋์ ์ค๋นํด์ผ ํ๋ “๊ฐ์ธ ํค(Private Keys)”์ “๋น๊ณต๊ฐ ์ค์ ๊ฐ(Secret Keys)”์ ์ ํด์ ธ ์์ต๋๋ค.
์นด์นด์ค ์ธ์ฆ์ OAuth2 ํ์ค์ ๋ฐ๋ฅด๊ธฐ ๋๋ฌธ์ ํ์ํ ํค ์ข
๋ฅ๊ฐ ๋ช
ํํฉ๋๋ค.
์๋๋ ์นด์นด์ค ๋ก๊ทธ์ธ์ ํ์ํ ๋ชจ๋ ๋น๊ณต๊ฐ Key ๋ชฉ๋ก์
๋๋ค.
โ
์นด์นด์ค ๋ก๊ทธ์ธ์ ํ์ํ Key ์ข
๋ฅ (์คํ๋ง MSA ๊ณตํต)
1) Kakao REST API Key (ํ์)
์ญํ : ์๋ฒ์์ ํ ํฐ ๊ตํ, ์ฌ์ฉ์ ์ ๋ณด ์์ฒญ ์ ์ฌ์ฉ
๊ฐ์ฅ ์ค์ํ Key
์ ๋ ์ธ๋ถ์ ๋
ธ์ถ๋๋ฉด ์ ๋จ
์คํ๋ง ๋ฐฑ์๋(gateway, user-service ๋ฑ)์์ OAuth Flow ์ฒ๋ฆฌํ ๋ ์ฌ์ฉ
→ ์นด์นด์ค ๊ฐ๋ฐ์ ์ฝ์ → “์ฑ ์ค์ → ์ฑ ํค → REST API ํค”
2) Kakao Client Secret (์ ํ์ด์ง๋ง ์ฌ์ค์ ํ์)
์ญํ : OAuth2 ํด๋ผ์ด์ธํธ ์ธ์ฆ ๊ฐํ
REST API Key๋ง์ผ๋ก ์ธ์ฆ ๊ฐ๋ฅํ์ง๋ง, ๋ณด์ ๊ฐํ๋ฅผ ์ํด Secret๋ ํ์ฑํํ๋ ๊ฒ์ ๊ฐ๋ ฅ ๊ถ์ฅ
MSA ๊ธฐ๋ฐ์ด๋ผ๋ฉด ์ธ๋ถ ๊ณต๊ฐ ์ํ์ด ์์ผ๋ฏ๋ก Secret ์ฌ์ฉ์ด ์์ ์
→ “์ฑ ์ค์ → ๋ณด์ → Client Secret ํ์ฑํ”
3) Kakao JavaScript Key (ํ๋ก ํธ ์ชฝ ํ์ ์)
React/Next.js์์ ์ง์ ์นด์นด์ค SDK๋ก ๋ก๊ทธ์ธํ ๊ฒฝ์ฐ ํ์ํ ํค
BUT: MSA๋ ๋ฐฑ์๋์์ OAuth ์ฝ๋๋ฅผ ์ฒ๋ฆฌํ๋ ๊ตฌ์กฐ๋ผ๋ฉด JS Key๋ ๋ถํ์
์น์์ "์นด์นด์ค๋ก ๋ก๊ทธ์ธ" ๋ฒํผ๋ง ๋๋ฅด๊ณ redirect_uri๋ก ๋ฆฌ๋ค์ด๋ ํธํ๋ฉด JS Key ์ฌ์ฉ ์ ํจ
4) Redirect URI
๋น๊ณต๊ฐ ํค๋ ์๋์ง๋ง ํ์ ์ค์ ๊ฐ.
Next.js → http://localhost:3000/auth/kakao/callback
Gateway → http://localhost:8080/login/oauth2/code/kakao
๋๋ MSA ๊ตฌ์กฐ์ ๋ง์ถฐ gateway-service๋ก redirect URI๋ฅผ ์ง์ค์ํด
์นด์นด์ค ์ฝ์์์ ๋ฐ๋์ ๋ฑ๋กํด์ผ ํจ.
5) (์ ํ) Admin Key
์นด์นด์คํก ๋ฉ์์ง ๋ณด๋ด๊ธฐ, ํธ์ ์๋ฆผ ๋ฑ ๊ณ ๊ธ ๊ธฐ๋ฅ์ฉ
๋ก๊ทธ์ธ(OAuth2)์๋ ์ ๋ ์ฌ์ฉํ์ง ๋ง ๊ฒ
๋
ธ์ถ๋๋ฉด ํ์ทจ๋ ์ํ์ด ๋งค์ฐ ํผ
→ “์ฑ ์ค์ → ์ฑ ํค → Admin Key”
โญ ์คํ๋ง MSA์์ ๊ฐ์ฅ ์ค์ํ ํต์ฌ
MSA ๊ตฌ์กฐ์์๋ ์ธ์ฆ ๋ก์ง์ gateway-service ๋๋ auth-service์์ ์ง์ค ์ฒ๋ฆฌํ๋ ๊ฒ์ด ๊ธฐ๋ณธ ํจํด์
๋๋ค.
๋ฐ๋ผ์ ๋ค์ ํค๋ gateway-service์์๋ง ์ฌ์ฉํฉ๋๋ค.
ํค ์ข
๋ฅ gateway user-service ๊ธฐํ ์๋น์ค REST API Key ๐ข ์ฌ์ฉ โ ์ฌ์ฉ ์ ํจ โ Client Secret ๐ข ์ฌ์ฉ โ โ Redirect URI ๐ข ์ฌ์ฉ โ โ JS Key Next.js ํ๋ก ํธ๊ฐ ์ง์ ์ฌ์ฉํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉ โ โ
์ ์ ์๋น์ค๋ Access Token ๊ฒ์ฆ ํ ์ฌ์ฉ์ ์ ๋ณด ์ ์ฅ๋ง ๋ด๋น ⇒ ์นด์นด์ค ํค๊ฐ ํ์ ์์.
๐ ๋ณด์ ๊ด๋ จ ๊ฒฝ๊ณ (MSA ๊ตฌ์กฐ์์ ๋งค์ฐ ์ค์)
์ด ํค๋ค์ ์ ๋ GitHub ๋ ํฌ์ ๋
ธ์ถ๋๋ฉด ์ ๋จ
→ Railway / Naver Cloud / AWS / K8s ํ๊ฒฝ๋ณ์๋ก ๋ถ๋ฆฌํด์ผ ํจ.
์คํ๋ง์์๋ ๋ค์๊ณผ ๊ฐ์ด ํ๊ฒฝ๋ณ์๋ก ๋ฐ๋ ๋ฐฉ์ ์ถ์ฒ:
spring:
security:
oauth2:
client:
registration:
kakao:
client-id: ${KAKAO_CLIENT_ID}
client-secret: ${KAKAO_CLIENT_SECRET}
redirect-uri: "{baseUrl}/login/oauth2/code/kakao"
authorization-grant-type: authorization_code
scope: profile_nickname, profile_image, account_email
provider:
kakao:
authorization-uri: https://kauth.kakao.com/oauth/authorize
token-uri: https://kauth.kakao.com/oauth/token
user-info-uri: https://kapi.kakao.com/v2/user/me
user-name-attribute: id
๐ ๊ฒฐ๋ก : ์คํ๋ง MSA ์นด์นด์ค ๋ก๊ทธ์ธ ๊ตฌํ์ ํ์ํ ํค
โ ํ์
REST API Key
Client Secret
Redirect URI
โ ์ ํ (ํ๋ก ํธ์์ ์ง์ SDK ์ฌ์ฉํ ๋)
JavaScript Key
โ ์ ๋ ๋ก๊ทธ์ธ์ ์ฌ์ฉํ์ง ๋ง ๊ฒ
Admin Key
์ํ์๋ฉด
Next.js(ํ๋ก ํธ) ↔ Gateway ↔ User-Service ๊ตฌ์กฐ์์
์นด์นด์ค OAuth ๋ก๊ทธ์ธ ์ ์ฒด ํ๋ฆ ์์ ์์
๋ ๋ง๋ค์ด ๋๋ฆด๊ฒ์.
์ฑ์์ฑํ๊ณ

์นด์นด์ค ๋ก๊ทธ์ธ์ gateway ์์๋ง , ์ฒ๋ฆฌํ๊ณ nextjs ๋ ๊ฒฐ๊ณผ๋ฅผ ํ ํฐ์ผ๋ก ์์ ๋ง ํ๋ ๊ตฌ์กฐ์ผ ๋ ๋ค์ ์์ ๋ด์ฉ์ ์๋ ค์ค.
์ด ๊ตฌ์กฐ๋ ๊ฐ์ฅ ์์ ํ๊ณ , MSA์ ๊ฐ์ฅ ์ ํฉํ ์ ์ ๊ตฌ์กฐ๋ผ์ ๊ฐ๋ ฅ ์ถ์ฒํจ.
๋ง ๊ทธ๋๋ก ํ๋ฆ์:
Next.js → Gateway → Kakao → Gateway → Next.js
ํ๋ก ํธ๋ ๋ก๊ทธ์ธ ๋ฒํผ๋ง ๋ง๋ค๊ณ ,
๋ฐฑ์๋(Gateway)๊ฐ ์นด์นด์ค OAuth2 ์ ์ฒด๋ฅผ ์ฒ๋ฆฌํ ๋ค
JWT๋ฅผ ๋ง๋ค์ด Next.js๋ก ๋ฆฌ๋ค์ด๋ ํธํ๋ ๋ฐฉ์.
๐ ์ ์ฒด ํ๋ฆ (ํ ์ฅ ์์ฝ)
- Next.js
"์นด์นด์ค ๋ก๊ทธ์ธ" ๋ฒํผ → Gateway์ /oauth2/authorization/kakao๋ก ์ด๋ - Gateway
Spring Security OAuth2 Client๋ก ์นด์นด์ค ๋ก๊ทธ์ธ ์ํ
→ ์นด์นด์ค๊ฐ code ์ ๋ฌ - Gateway
code๋ก AccessToken, UserInfo ๋ฐ์์ ์ฐ๋ฆฌ ํ์ DB ์ฒ๋ฆฌ
→ ๋ด๋ถ์์ user-service ํธ์ถ or ์์ฒด DB ์ฒ๋ฆฌ - Gateway
๋ก๊ทธ์ธ ์ฑ๊ณต ์ ์๋ฒ์์ JWT ๋ฐ๊ธ - Gateway → Next.js
์ต์ข ์ ์ผ๋ก JWT๋ฅผ ๋ถ์ฌ์
https://frontend.com/auth/success?token=xxx
์ด๋ฐ ์์ผ๋ก ํ๋ก ํธ๋ก ๋ฆฌ๋ค์ด๋ ํธ - Next.js
๋ฐ์ ํ ํฐ์ localStorage/์ฟ ํค์ ์ ์ฅํ๊ณ ๋ก๊ทธ์ธ ์๋ฃ ์ฒ๋ฆฌ
๐ง 1๋จ๊ณ: ์นด์นด์ค ๊ฐ๋ฐ์ ์ฝ์์์ ํด์ผ ํ ๊ฒ
1) ์ฑ ์์ฑ
- ์นด์นด์ค๋๋ฒจ๋กํผ → ๋ด ์ ํ๋ฆฌ์ผ์ด์ → ์ฑ ๋ง๋ค๊ธฐ
2) REST API ํค ํ์ธ
Gateway์์ ๋ฌด์กฐ๊ฑด ํ์.
3) Client Secret ํ์ฑํ (๊ฐ๋ ฅ ๊ถ์ฅ)
์นด์นด์ค ๋ก๊ทธ์ธ ๋ณด์ ๊ฐํ๋จ.
4) Redirect URI ๋ฑ๋ก (Gateway๋ง ๋ฑ๋ก!!)
ํ์:
์ด์ ํ๊ฒฝ ์ถ๊ฐ:
Next.js ์ชฝ redirect URI ์ ๋ ๋ฃ์ง ์๋๋ค.
→ ์นด์นด์ค ํธ์ถ ํ๋ฆ์ Gateway๋ก ๊ฐ์ ํ๊ธฐ ์ํด.
5) ๋์ํญ๋ชฉ ์ค์
- profile_nickname
- profile_image
- account_email (์ํ๋ฉด)
๐ง 2๋จ๊ณ: Gateway(Spring Boot) ์ค์
๐ ํต์ฌ ํฌ์ธํธ: ํ๋ก ํธ๋ OAuth ์ง์ ์ ํ๊ณ , Gateway๊ฐ ๋ชจ๋ ๊ฒ์ ๋ค ์ฒ๋ฆฌํ๋ค.
โ application.yml (ํ์)
โ OAuth2 ์ฑ๊ณต ํธ๋ค๋ฌ ๊ตฌํ
ํ ํฐ ๋ฐ๊ธ → ํ๋ก ํธ๋ก ๋ฆฌ๋ค์ด๋ ํธ.
โ SecurityConfig์ successHandler ์ ์ฉ
๐ง 3๋จ๊ณ: Next.js์์ ํด์ผ ํ ๊ฒ (์ง์ง ๊ฐ๋จํจ)
โ ๋ก๊ทธ์ธ ๋ฒํผ ํ๋๋ง ๊ตฌํ
๋.
Next.js๋ ์นด์นด์ค๋ ํ ๋ฒ๋ ์ง์ ํต์ ํ์ง ์์.
๐ง 4๋จ๊ณ: Next.js์์ Gateway๊ฐ ๋ณด๋ด์ค JWT ๋ฐ๊ธฐ
์นด์นด์ค ๋ก๊ทธ์ธ ์ฑ๊ณตํ๋ฉด Gateway๊ฐ ์ฌ๊ธฐ๋ก ๋ฆฌ๋ค์ด๋ ํธํจ:
/auth/success ํ์ด์ง์์ ํ ํฐ ์ ์ฅ
๐ง 5๋จ๊ณ: user-service๊ฐ ํด์ผ ํ ์ผ
Gateway์์ ๋ฐ์ ์นด์นด์ค ์ ์ ์ ๋ณด๋ก
- ํ์ ๊ฐ์
- ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
- ๋ด๋ถ ์ ์ ID ๋ฐํ
๋ง ํ๋ฉด ๋.
์นด์นด์ค ํค๋ user-service์์ ์ ๋ ์ฌ์ฉ ์ ํจ.
๐ ํ ์ค ๊ฒฐ๋ก
โจ Next.js๋ ๋ก๊ทธ์ธ ๋ฒํผ๋ง ๋ง๋ ๋ค.
โจ ์นด์นด์ค ์ธ์ฆ ์ ์ฒด๋ Gateway์์ ๋๋ธ๋ค.
โจ ์ต์ข
JWT๋ง ํ๋ก ํธ๋ก ์ ๋ฌํ๋ค.

3-1. Gateway .env ์์
# =========================
# Kakao OAuth
# =========================
KAKAO_REST_API_KEY=your_kakao_rest_api_key
KAKAO_ADMIN_KEY=your_kakao_admin_key # ํ์ํ ๋๋ง ์ฌ์ฉ
KAKAO_REDIRECT_URI=http://localhost:8080/oauth2/kakao/callback
# ์ฐ๋ฆฌ ์๋น์ค JWT
JWT_SECRET=your_gateway_jwt_secret
# Redis (์ธ์
/๋ธ๋๋ฆฌ์คํธ/๋ฆฌํ๋ ์ํ ํฐ ๋ฑ ์ฉ๋)
REDIS_HOST=localhost
REDIS_PORT=6379
# ํ๋ก ํธ์๋ ์ฝ๋ฐฑ URL (๋ก๊ทธ์ธ ์ฑ๊ณต ํ ๋๋ ค๋ณด๋ผ Next ์ฃผ์)
FRONT_LOGIN_CALLBACK_URL=http://localhost:3000/auth/kakao


์นด์นด์ค ์ฑ ๋ถ๋ถ์์ ํค ๊ฐ์ ธ์ค๊ธฐ

ํ์ผ ๋ง๋ค๊ธฐ
@app/dashboard/page.tsx์นด์นด์ค ๋ก๊ทธ์ธ์ด ์ฑ๊ณตํ์ต๋๋ค. ๋ฉ์์ง๊ฐ ๊ฐ์ด๋ฐ์ ํฌ๊ฒ ๋์ค๋ ํ๋ฉด์ ๋ง๋ค๊ณ , ๋ฌธ์ฅ ๋ฐ์ ๋ก๊ทธ์์ ๋ฒํผ์ ๋ง๋ค์ด์ค



@gateway/src/main/java/com/seoeunjin/api/router/AuthRouter.java ์ฌ๊ธฐ์ localhost:3000/kakao/login ์์ ๋์ด์จ request ๋ cors๋ฅผ ํ์ฉํ๊ณ , @services/auth-service/src/main/java/com/seoeunjin/api/kakao/KakaoController.java ๋ก ์ด๋์ํค๋ ๋ผ์ฐํฐ ์ฝ๋๋ฅผ ์์ฑํด์ค
@services/auth-service/src/main/java/com/seoeunjin/api/kakao/KakaoController.java ์นด์นด์ค ๋ก๊ทธ์ธ ์์ฒญ์ด ์ค๋ฉด ๋ฌด์กฐ๊ฑด ์ฑ๊ณต์ผ๋ก ์ฒ๋ฆฌํ์ฌ next.js์์ ์ฑ๊ณต์ผ๋ก ์ธ์ํ์ฌ ๋ค์ ํ์ด์ง๋ก ์ด๋ํ๋ ์ฝ๋๋ฅผ ์์ฑํด์ค.
**“์นด์นด์ค๋ ์ ๋ถ Gateway์์ ์ฒ๋ฆฌํ๊ณ , Next.js๋ ํ ํฐ๋ง ๋ฐ๋ ๊ตฌ์กฐ”**๋ก ๋ค์ ์ ๋ฆฌํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
1. ์ ์ฒด ๊ตฌ์กฐ ๋จผ์ ์ ๋ฆฌ
์ด๋ฒ ๊ตฌ์กฐ๋ ์ด๋ ๊ฒ ๋ฉ๋๋ค.
์ฌ์ฉ์๊ฐ Next.js ํ๋ฉด์์ “์นด์นด์ค ๋ก๊ทธ์ธ” ๋ฒํผ ํด๋ฆญ
Next.js๋ ์นด์นด์ค๊ฐ ์๋๋ผ Gateway๋ก ๋ฆฌ๋ค์ด๋ ํธ
์: http://localhost:8080/oauth2/kakao/login
Gateway๊ฐ ์นด์นด์ค์๋ง ํต์
์ธ๊ฐ ์ฝ๋(code) ์์
REST API ํค๋ก ํ ํฐ/์ ์ ์ ๋ณด ์์ฒญ
์ฐ๋ฆฌ ์๋น์ค์ฉ JWT ๋ฐ๊ธ
Gateway๊ฐ ๋ก๊ทธ์ธ ์ฑ๊ณต ํ Next.js๋ก ๋ฆฌ๋ค์ด๋ ํธํ๋ฉด์ ํ ํฐ ์ ๋ฌ
์: http://localhost:3000/auth/kakao?token=<JWT>
๋๋ Set-Cookie: access_token=...; HttpOnly ๋ฐฉ์
Next.js๋ ๋ฐ์ ํ ํฐ๋ง ๋ณด๊ด(์ฟ ํค/์คํ ๋ฆฌ์ง)ํ๊ณ ์ดํ API ์์ฒญ์ ์ฌ์ฉ
๐ ์ด ๊ตฌ์กฐ์์๋ ์นด์นด์ค ํค๋ ์ ๋ถ Gateway ์ชฝ์๋ง ์์ต๋๋ค.
Next.js๋ ์นด์นด์ค ๊ด๋ จ ํค๋ฅผ ์์ ๋ชจ๋ฅด๋ ์ํ์
๋๋ค.
2. Next.js 15 (.env.local) – ์นด์นด์ค ํค ์์
Next๋ “์นด์นด์ค ๋ก๊ทธ์ธ ๋ฒํผ → Gateway URL๋ก ๋ณด๋ด๋ ์ญํ ”๋ง ํฉ๋๋ค.
# Next.js 15 ํ๋ก ํธ์๋
# ๋ฐฑ์๋ Gateway ์ฃผ์
NEXT_PUBLIC_API_GATEWAY_URL=http://localhost:8080
# ๋ก๊ทธ์ธ ๋ฒํผ์ด ์ด๋ํ Gateway ๋ก๊ทธ์ธ ์๋ํฌ์ธํธ
NEXT_PUBLIC_KAKAO_LOGIN_URL=http://localhost:8080/oauth2/kakao/login
# Gateway๊ฐ ๋ก๊ทธ์ธ ์ฑ๊ณต ์ ๋๋์์ฌ ํ๋ก ํธ ์ฝ๋ฐฑ URL
NEXT_PUBLIC_LOGIN_CALLBACK_URL=http://localhost:3000/auth/kakao
์นด์นด์ค JS Key, REST Key ๋ฑ์ ์ ํ ๋ฃ์ง ์์ต๋๋ค.
Next๋ ์ค์ง
Gateway URL
๋ก๊ทธ์ธ ์๋ํฌ์ธํธ
๋ก๊ทธ์ธ ์๋ฃ ํ ์ฝ๋ฐฑ ํ์ด์ง URL
์ ๋๋ง ์๊ณ ์์ผ๋ฉด ๋ฉ๋๋ค.
ํ๋ก ํธ ์ฝ๋๋ ๋๋ต ์ด๋ฐ ๋๋์
๋๋ค.
// app/page.tsx (Next 15 App Router ์์)
'use client';
export default function Home() {
const loginUrl = http://process.env.NEXT_PUBLIC_KAKAO_LOGIN_URL!;
const handleKakaoLogin = () => {
window.location.href = loginUrl; // ๋ฐ๋ก Gateway๋ก ์ด๋
};
return (
<button onClick={handleKakaoLogin}>
์นด์นด์ค ๋ก๊ทธ์ธ
</button>
);
}
3. Spring Cloud Gateway (.env) – ์นด์นด์ค ํค๋ ์ฌ๊ธฐ๋ง
์ด์ ์นด์นด์ค ๊ด๋ จ ํค๋ ์ ๋ถ Gateway์ .env์ ๋ชฐ์๋ฃ์ต๋๋ค.
3-1. Gateway .env ์์
# =========================
# Kakao OAuth
# =========================
KAKAO_REST_API_KEY=your_kakao_rest_api_key
KAKAO_ADMIN_KEY=your_kakao_admin_key # ํ์ํ ๋๋ง ์ฌ์ฉ
KAKAO_REDIRECT_URI=http://localhost:8080/oauth2/kakao/callback
# ์ฐ๋ฆฌ ์๋น์ค JWT
JWT_SECRET=your_gateway_jwt_secret
# Redis (์ธ์
/๋ธ๋๋ฆฌ์คํธ/๋ฆฌํ๋ ์ํ ํฐ ๋ฑ ์ฉ๋)
REDIS_HOST=localhost
REDIS_PORT=6379
# ํ๋ก ํธ์๋ ์ฝ๋ฐฑ URL (๋ก๊ทธ์ธ ์ฑ๊ณต ํ ๋๋ ค๋ณด๋ผ Next ์ฃผ์)
FRONT_LOGIN_CALLBACK_URL=http://localhost:3000/auth/kakao
3-2. application.yml์์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
kakao:
rest-api-key: ${KAKAO_REST_API_KEY}
admin-key: ${KAKAO_ADMIN_KEY}
redirect-uri: ${KAKAO_REDIRECT_URI}
security:
jwt:
secret: ${JWT_SECRET}
frontend:
login-callback-url: ${FRONT_LOGIN_CALLBACK_URL}
spring:
data:
redis:
host: ${REDIS_HOST}
port: ${REDIS_PORT}
4. ์นด์นด์ค ๊ฐ๋ฐ์ ์ฝ์์ ๋ฑ๋กํ Redirect URI
์นด์นด์ค ๋๋ฒจ๋กํผ์ค์ ๋ฑ๋กํด์ผ ํ๋ Redirect URI๋ “Gateway์ ์ฝ๋ฐฑ URL” ํ๋๋ฟ์
๋๋ค.
์์:
http://localhost:8080/oauth2/kakao/callback
ํ๋ฆ์ ์ด๋ ๊ฒ ๋ฉ๋๋ค.
์ฌ์ฉ์๊ฐ http://localhost:8080/oauth2/kakao/login ์ ์
Gateway๊ฐ ์นด์นด์ค ๋ก๊ทธ์ธ ํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ
์นด์นด์ค ๋ก๊ทธ์ธ ์๋ฃ ํ, ์นด์นด์ค → Gateway๋ก ๋ฆฌ๋ค์ด๋ ํธ
http://localhost:8080/oauth2/kakao/callback?code=...
Gateway๊ฐ code๋ก ํ ํฐ/์ ์ ์ ๋ณด ๋ฐ์์ ์ฐ๋ฆฌ JWT ๋ฐํ
Gateway๊ฐ ์ต์ข
์ ์ผ๋ก Next.js ์ฝ๋ฐฑ URL๋ก ๋ฆฌ๋ค์ด๋ ํธ
http://localhost:3000/auth/kakao?token=<์ฐ๋ฆฌ_JWT>
๋๋:
HTTP/1.1 302 Found
Location: http://localhost:3000/auth/kakao
Set-Cookie: access_token=<์ฐ๋ฆฌ_JWT>; HttpOnly; Path=/; ...
์นด์นด์ค๊ฐ ์๊ณ ์๋ ๊ฑด Gateway ์ฝ๋ฐฑ URL๋ฟ์
๋๋ค.
Next.js ์ฝ๋ฐฑ URL์ ์นด์นด์ค๊ฐ ์๋๋ผ Gateway๊ฐ ์ฌ์ฉํ๋ ๋ด๋ถ ์ค์ ์
๋๋ค.
5. ์ ๋ฆฌ: ๋น์ ์ด ์ํ๋ ๊ตฌ์กฐ ๊ธฐ์ค์ผ๋ก ํต์ฌ๋ง
๋น์ ์ด ๋งํ ๊ตฌ์กฐ =
“์นด์นด์ค๋ Gateway๋ง ์๋ค”
“Next.js๋ ๊ฒฐ๊ณผ ํ ํฐ๋ง ๋ฐ๋๋ค”
์ด ๊ตฌ์กฐ์์๋:
โ
์นด์นด์ค ํค(REST, Admin, Redirect URI)๋ ์ ๋ถ Gateway์ .env์๋ง
โ
Next.js .env.local์๋ Gateway URL + ๋ก๊ทธ์ธ ์๋ํฌ์ธํธ + ํ๋ก ํธ ์ฝ๋ฐฑ URL๋ง
โ
์นด์นด์ค ๊ฐ๋ฐ์ ์ฝ์์ Redirect URI๋ Gateway ์ฝ๋ฐฑ URL ํ๋๋ง ๋ฑ๋ก
โ
Gateway๊ฐ
์นด์นด์ค ์ฝ๋ ์ฒ๋ฆฌ
์นด์นด์ค ํ ํฐ/์ ์ ์กฐํ
์ฐ๋ฆฌ JWT ๋ฐํ
Next.js๋ก ์ต์ข
๋ฆฌ๋ค์ด๋ ํธ
๋ค์ ๋จ๊ณ๋ก,
Gateway ์ชฝ /oauth2/kakao/login, /oauth2/kakao/callback ๋ผ์ฐํ
์ค๊ณ
JWT ๋ฐ๊ธ ํ ๋ฆฌ๋ค์ด๋ ํธ ๋ฐฉ์(์ฟผ๋ฆฌ vs ์ฟ ํค) ์ ํ
Next.js 15์์ /auth/kakao ํ์ด์ง์์ ํ ํฐ ์ฝ๊ณ Zustand/์ฟ ํค์ ์ ์ฅํ๋ ์ฝ๋
๊น์ง ํ ๋ฒ์ ์ค๊ณํด ๋๋ฆด ์ ์์ต๋๋ค.
๊ฐ์ฅ ๋น ๋ฅธ ํด๊ฒฐ์ฑ (5๋ถ ์์ 3000๋ฒ ๋์ฐ๊ณ ์ถ๋ค๋ฉด)
๋น์ ์ด “๋ค๋ฅธ ์ฌ๋๋ค์ 3000์ธ๋ฐ ๋๋ง 8080์ด์ผ”๋ผ๊ณ ๋๊ปด์ ๋ถํธํ๋ค๋ฉด, ์ง๊ธ ๋น์ฅ Next.js ํ๋๋ง ๋ธ๋ ๋์์ ํ ์คํธํ๋ ๊ฒ ์ ์ผ ๋น ๋ฆ ๋๋ค.
ํฐ๋ฏธ๋์์ ์๋ ๋ช ๋ น์ด 3๊ฐ๋ง ์น์ธ์:
# 1. ์ ํด๋์ Next.js ํ๋ก์ ํธ ์์ฑ
npx create-next-app@latest kakao-test --typescript --tailwind --eslint --app --src-dir
# 2. ์ด๋ ํ ์คํ
cd kakao-test
npm run dev
→ ์๋์ผ๋ก http://localhost:3000 ์ด ์ด๋ฆฌ๊ณ ๋น ํ์ด์ง ๋น๋๋ค.
๊ทธ ๋ค์ src/app/kakao/login/page.tsx ํ์ผ์ ํต์งธ๋ก ์๋ ์ฝ๋๋ก ๋ฐ๊พธ์ธ์ (์๊น ๋๋ฆฐ ๊ทธ ์ฝ๋)
// src/app/kakao/login/page.tsx
'use client';
import { useEffect } from 'react';
const REST_API_KEY = "๋ณธ์ธ_REST_API_KEY;
const REDIRECT_URI = "http://localhost:3000/kakao/login";
export default function KakaoLogin() {
useEffect(() => {
const code = new URLSearchParams(window.location.search).get('code');
if (code) {
document.getElementById('login-box').style.display = 'none';
document.getElementById('success-box').style.display = 'block';
}
}, []);
const login = () => {
location.href = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
};
return (
<>
<div id="login-box" style={{textAlign:'center', marginTop:'100px'}}>
<h1>์นด์นด์ค ๋ก๊ทธ์ธ</h1>
<button onClick={login} style={{background:'#FEE500', padding:'15px 30px', border:'none', borderRadius:'12px', fontSize:'18px'}}>
์นด์นด์ค๋ก ๋ก๊ทธ์ธ
</button>
</div>
<div id="success-box" style={{display:'none', textAlign:'center', marginTop:'100px'}}>
<h1>๋ก๊ทธ์ธ์ด ์ฑ๊ณตํ์ต๋๋ค!</h1>
<p style={{fontSize:'24px'}}>ํ์ํฉ๋๋ค</p>
</div>
</>
);
}
→ ์ด์ ์ง์ง http://localhost:3000/kakao/login ํ๋ฉด ๋ค๋ฅธ ์ฌ๋๋ค์ด ๋ณด๋ ๊ทธ ํ๋ฉด์ด ๋ฑ ๋น๋๋ค
๋์ค์ ๋ง์ดํฌ๋ก์๋น์ค์ ๋ค์ ๋ถ์ด๊ณ ์ถ์ ๋ ์ธ์ ๋ ์ง ์๋ ค์ฃผ์ธ์. ๊ทธ๋๋ Gateway์์ /api/**๋ง ํ๋ก์ํ๊ณ ์ ์ ํ์ด์ง๋ Next.js๊ฐ ๋ด๋นํ๋๋ก ๋ถ๋ฆฌํด๋๋ฆด๊ฒ์!
1. ์ฌ์ ์ค๋น
- ์นด์นด์ค ๊ฐ๋ฐ์ ์ฝ์์์ ์ฑ ์์ฑ
- Redirect URI ๋ฑ๋ก (์: http://localhost:3000/api/kakao/callback ํน์ Gateway ์ฃผ์)
- .env.local ๋ฑ์ ํ๊ฒฝ ๋ณ์ ์ค์
2. ํ๋ก ํธ์๋ ์คํ (Next.js)
- app/(auth)/login/page.tsx์์ ์นด์นด์ค ๋ฒํผ ํด๋ฆญ ์ ์นด์นด์ค ์ธ์ฆ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
3. ๋ฐฑ์๋ ์คํ
๋ฐฑ์๋๊ฐ ์ด๋ค ์คํ์ธ์ง ๋ฌธ์๋ repo๋ฅผ ํ์ธํ ๋ค,
- Express/Nest๋ผ๋ฉด pnpm run dev ๋๋ pnpm run start:dev
- ๋ค๋ฅธ ์๋ฒ๋ผ๋ฉด ํด๋น README์ ์๋ ๋ช ๋ น์ผ๋ก ์คํ
- Redirect URI์์ ํธ์ถํ๋ API๊ฐ ๋์ํด์ผ ์นด์นด์ค์์ ๋ฐ์ code๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
4. ๋ก๊ทธ์ธ ํ๋ฆ ํ์ธ
- ํ๋ก ํธ ๋ก๊ทธ์ธ ํ์ด์ง ์ด๊ธฐ http://localhost:3000/login
- “์นด์นด์ค๋ก ๋ก๊ทธ์ธ” ๋ฒํผ ํด๋ฆญ → ์นด์นด์ค ๋์ ํ๋ฉด
- ๋ก๊ทธ์ธ ํ redirect_uri๋ก code ์ ๋ฌ
- ๋ฐฑ์๋์์ code → access token → ์ฌ์ฉ์ ์ ๋ณด ์กฐํ → ์ธ์ /ํ ํฐ ๋ฐ๊ธ
- ํ๋ก ํธ์์ ๋ก๊ทธ์ธ ์ํ ์ ๋ฐ์ดํธ (e.g., /api/kakao/callback → ์ฟ ํค ์ ์ฅ → ํ์ผ๋ก redirect)
5. ๋ฌธ์ ํด๊ฒฐ ํ
- Redirect URI ๋ถ์ผ์น ์ค๋ฅ: Kakao ์ฝ์๊ณผ .env์ ๋์ผํ๊ฒ ๋ง์ถ๊ธฐ
- CORS: ๋ฐฑ์๋์์ ํ๋ก ํธ ๋๋ฉ์ธ ํ์ฉ
- HTTPS ์๊ตฌ ์ ๋ก์ปฌ ๊ฐ๋ฐ์ฉ ํ๋ก์๋ ngrok, Vercel preview ๋ฑ์ ํ์ฉ
251124 ๋์ปค์์ ๋ถ๋ฆฌ๋ ๋ณ๋๋ก ์๋ํ๋ localhost:3000 ๋ฒ์์ cors ๋ฅผ ํตํด ์ ์ก๋๋ ๋ฐ์ดํฐ ์์ฒญ์ ๋์ปค์ ์คํ์ค์ธ ์คํ๋ง ๊ฒ์ดํธ์จ์ด๊ฐ ๋ฐ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ธ๊ฐ ?
ํต์ฌ๋ง ๋จผ์ ๋งํ๋ฉด, ๋ ๊ฐ์ง๋ฅผ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
๋์ปค ์ปจํ
์ด๋ ์์ ๊ฒ์ดํธ์จ์ด๋ฅผ ํธ์คํธ(๋ก์ปฌ)์์ ์ ๊ทผ ๊ฐ๋ฅํ๊ฒ ํฌํธ ๋งคํ
๊ฒ์ดํธ์จ์ด์์ CORS ์ค์ ์ผ๋ก http://localhost:3000์ ํ์ฉ
CORS ์์ฒด๋ “๋์ปค ๋ฌธ์ ”๊ฐ ์๋๋ผ ๋ธ๋ผ์ฐ์ ↔ ์คํ๋ง ๊ฒ์ดํธ์จ์ด ์ฌ์ด์ HTTP ํค๋ ๋ฌธ์ ์ด๊ณ ,
๋์ปค๋ ๋จ์ง “๊ฒ์ดํธ์จ์ด๊ฐ ์ด์ด๋ ํฌํธ๋ฅผ ํธ์คํธ์ ์ด๋ป๊ฒ ๋
ธ์ถํ๋๋”์ ๋ฌธ์ ์
๋๋ค.
1. ๋คํธ์ํฌ: Next.js(ํธ์คํธ 3000) → Docker ์ Gateway ๋ก ์์ฒญ ๋ณด๋ด๊ธฐ
๊ตฌ์กฐ๋ฅผ ์ด๋ ๊ฒ ๊ฐ์ ํ๊ฒ ์ต๋๋ค.
ํ๋ก ํธ: ํธ์คํธ์์ http://localhost:3000 (Next.js 15)
๊ฒ์ดํธ์จ์ด: ๋์ปค ์ปจํ
์ด๋ ์์์ 8080 ํฌํธ๋ก ๋์ (์คํ๋ง ํด๋ผ์ฐ๋ ๊ฒ์ดํธ์จ์ด)
docker-compose ์์
version: "3.8"
services:
api-gateway:
image: your-registry/your-gateway-image:latest
container_name: api-gateway
ports:
- "8080:8080" # <--- ์ด๊ฒ ์ค์: ํธ์คํธ 8080 → ์ปจํ
์ด๋ 8080 ๋งคํ
environment:
- SPRING_PROFILES_ACTIVE=local
networks:
- msa-net
networks:
msa-net:
driver: bridge
์ด๋ ๊ฒ ํด ๋๋ฉด,
๋ธ๋ผ์ฐ์ (Next.js)๊ฐ ํธ์ถํ๋ URL:// Next.js fetch/axios ์์
const res = await fetch("http://localhost:8080/api/something", { ... })
์ค์ ๋ก๋: localhost:8080 → ๋์ปค๊ฐ api-gateway ์ปจํ
์ด๋์ 8080์ผ๋ก ํฌ์๋ฉํฉ๋๋ค.
์ฌ๊ธฐ๊น์ง๋ง ํ๋ฉด ๋คํธ์ํฌ ๋ผ์ฐํ
์ ์๋ฃ์
๋๋ค.
์ด์ ๋จ๋ ๊ฑด “๋ธ๋ผ์ฐ์ ๊ฐ CORS ์๋ฌ๋ฅผ ์ ๋ด๊ฒ ํค๋๋ฅผ ์ด๋ป๊ฒ ์ค ๊ฑฐ๋”์
๋๋ค.
2. ์คํ๋ง ๊ฒ์ดํธ์จ์ด์์ CORS ํ์ฉ ์ค์
ํ๋ก ํธ๊ฐ http://localhost:3000 ์ด๊ณ , ๊ฒ์ดํธ์จ์ด๊ฐ http://localhost:8080 ์ด๋ฉด
์ถ์ฒ(origin) ๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ๊ฐ CORS ์ฒดํฌ๋ฅผ ํฉ๋๋ค.
Origin: http://localhost:3000
Request URL: http://localhost:8080/... → ์๋ก ๋ค๋ฆ
๊ทธ๋์ ๊ฒ์ดํธ์จ์ด ์ชฝ์์ ์๋ ๋ ๊ฐ์ง๋ฅผ ํด์ค์ผ ํฉ๋๋ค.
http://localhost:3000์ allowedOrigins์ ๋ช
์
OPTIONS(ํ๋ฆฌํ๋ผ์ดํธ) ์์ฒญ๊น์ง ํ์ฉ
application.yml ์์ (Spring Cloud Gateway ์ ์ญ CORS)
spring:
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true
corsConfigurations:
'[/**]':
allowedOrigins:
- "http://localhost:3000" # ํ๋ก ํธ ์ฃผ์
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowedHeaders:
- "*" # ํ์ํ ํค๋๊ฐ ์๋ค๋ฉด ๋ช
์ ๊ฐ๋ฅ
exposedHeaders:
- "*" # ์ ํ ์ฌํญ
allowCredentials: true # ์ฟ ํค/์ธ์ฆ์ ๋ณด ๋ณด๋ด๋ ค๋ฉด true
โ allowCredentials: true๋ฅผ ์ธ ๋๋ allowedOrigins: "*" ๋ฅผ ์ฐ๋ฉด ์์ธ ๋ฉ๋๋ค.
์ด์ ์ ํ ๋ฒ ๊ฒช์ผ์
จ๋ ๊ทธ ์๋ฌ์
๋๋ค. ๋ฌด์กฐ๊ฑด ์ฃผ์๋ฅผ ๋ช
์์ ์ผ๋ก ์ ์ด์ผ ํฉ๋๋ค.
์ด ์ค์ ์ ์ ์ฉํ๋ฉด:
๋ธ๋ผ์ฐ์ ๊ฐ ๋ณด๋ด๋ OPTIONS /api/... (ํ๋ฆฌํ๋ผ์ดํธ ์์ฒญ)์ ๋ํด
๊ฒ์ดํธ์จ์ด๊ฐ ์ ์ ํ Access-Control-Allow-* ํค๋๋ฅผ ๋ด๋ ค์ฃผ๊ณ ,
๊ทธ ๋ค์ ์ค์ GET/POST ... ์์ฒญ์ด CORS ์๋ฌ ์์ด ํต๊ณผํฉ๋๋ค.
3. ํ๋ก ํธ(Next.js)์์ ํธ์ถ ํํ
์๋ฅผ ๋ค์ด Next.js 15์์ React Query ๋๋ fetch๋ฅผ ์ด๋ค๋ฉด:
// ์: app/api-example/page.tsx or client component ๋ด๋ถ
async function callGateway() {
const res = await fetch("http://localhost:8080/api/hello", {
method: "GET",
credentials: "include", // ์ฟ ํค๋ฅผ ์ฐ๋ฉด ํ์
headers: {
"Content-Type": "application/json",
},
});
const data = await res.json();
return data;
}
๋ธ๋ผ์ฐ์ ๊ธฐ์ค origin: http://localhost:3000
ํธ์ถ ๋์: http://localhost:8080
์คํ๋ง ๊ฒ์ดํธ์จ์ด์์ ์ CORS ์ค์ ์ ํด๋๋ฉด ํ๋ฆฌํ๋ผ์ดํธ + ๋ณธ ์์ฒญ ๋ชจ๋ ํ์ฉ๋ฉ๋๋ค.
4. ๋ ๋ค ๋์ปค ์์ ์์ ๋๋?
๋์ค์ Next.js๋ ๋์ปค ์ปจํ
์ด๋๋ก ์ฌ๋ฆฌ๋ฉด ๊ตฌ์กฐ๊ฐ ์ด๋ ๊ฒ ๋ฉ๋๋ค.
next-app ์ปจํ
์ด๋: ๋ด๋ถ ํฌํธ 3000 → ํธ์คํธ 3000 ๋งคํ
gateway ์ปจํ
์ด๋: ๋ด๋ถ ํฌํธ 8080 → ํธ์คํธ 8080 ๋งคํ
๋ ๋ค ๊ฐ์ msa-net ๋คํธ์ํฌ
Next.js ์ฝ๋์์๋ ์ฌ์ ํ ๋ธ๋ผ์ฐ์ ์์ ๋ณด๋ ์ฃผ์ ๊ธฐ์ค์ผ๋ก
http://localhost:8080 ์ ํธ์ถํ๋ฉด ๋ฉ๋๋ค.
(๋ธ๋ผ์ฐ์ ์ origin์ ๊ณ์ http://localhost:3000 ์ด๊ธฐ ๋๋ฌธ์ CORS ์ค์ ์ ๋์ผํฉ๋๋ค.)
์ปจํ
์ด๋ ์ด๋ฆ(api-gateway:8080)์ผ๋ก ๋ถ๋ฅด๋ ๊ฑด ์๋ฒ-์ฌ์ด๋ ๋ ๋๋ง ๋ด๋ถ์์ ์๋ฒ ๊ฐ ํต์ ์ผ ๋๋ ์ฐ๋ ํจํด์ด๊ณ ,
์ง๊ธ ์ง๋ฌธ์ฒ๋ผ “๋ธ๋ผ์ฐ์ → ๊ฒ์ดํธ์จ์ด” ๋ผ์ฐํ
์ ํธ์คํธ ๊ธฐ์ค ํฌํธ๋ก ์๊ฐํ์๋ฉด ๋ฉ๋๋ค.
์ ๋ฆฌ
๋์ปค ์ชฝ
ports: "8080:8080" ์ผ๋ก ๊ฒ์ดํธ์จ์ด ํฌํธ ๋
ธ์ถ
Next.js๋ http://localhost:8080 ์ผ๋ก ํธ์ถ
์คํ๋ง ๊ฒ์ดํธ์จ์ด CORS
globalcors์ http://localhost:3000 ํ์ฉ
allowedMethods์ OPTIONS ํฌํจ
allowCredentials: true ์ธ ๋ allowedOrigins: "*" ๊ธ์ง
์ด ๋ ๊ฐ์ง๋ง ๋ง๊ฒ ์ค์ ํ๋ฉด,
**“๋์ปค์์ ์คํ ์ค์ธ ์คํ๋ง ๊ฒ์ดํธ์จ์ด๊ฐ, ๋ถ๋ฆฌ๋ localhost:3000(Next.js)์์ CORS๋ก ์ ์ก๋๋ ์์ฒญ์ ๋ฐ๋ ๊ตฌ์กฐ”**๊ฐ ์ ์์ ์ผ๋ก ์๋ํฉ๋๋ค.
์ํ์๋ฉด, ์ง๊ธ ์ฐ์๋ application.yml ์ด๋ docker-compose.yml ์ผ๋ถ๋ฅผ ๊ฐ์ ธ์ค์๋ฉด
์ค์ ๊ฐ์ ๋ง์ถฐ์ ๊ทธ๋๋ก ์์ ๋ณธ๊น์ง ๋ง๋ค์ด ๋๋ฆฌ๊ฒ ์ต๋๋ค.

'Project ESG+AI > [์ผ์ KPMG]ESG ๋ฐ์ดํฐ ํ์ฉ ํ์คํ ๊ฐ๋ฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| 35์ผ์ฐจ. ์นด์นด์ค OAuth๋ฅผ ๊ตฌํํด๋ณด์ (0) | 2025.11.26 |
|---|---|
| 34์ผ์ฐจ. ์นด์นด์ค, ๋ค์ด๋ฒ, ๊ตฌ๊ธ, ์์ดํฐ ๋ก๊ทธ์ธ์ ๋ง๋ค์ (0) | 2025.11.25 |
| 32์ผ์ฐจ. ๋๋ฉ์ธ ์ฐ๊ฒฐํ๊ธฐ & ๋ฏธ๋ค์จ์ด (0) | 2025.11.21 |
| 31์ผ์ฐจ. ๊น ๋ธ๋์น ๋ง๋ค๊ธฐ (0) | 2025.11.20 |
| 30์ผ์ฐจ. (0) | 2025.11.19 |