개요
기존 회원가입을 완료한 사용자가 마이페이지에서 자신의 Discord 계정을 서비스 계정과 연동하는 기능을 구현한다.
배경
현재 Discord OAuth2 인증 플로우(/api/oauth2/discord/login)는 신규 유저 가입 전용으로 동작하며, 이미 이메일/비밀번호로 가입한 기존 유저가 Discord 계정을 연결할 수 있는 수단이 없다. 이를 해결하기 위해 계정 연동 전용 OAuth2 플로우를 별도로 추가한다.
- Discord 봇 서비스(GAMERS)와의 데이터 동기화(길드 검증, 채널 알림 등)를 위해 사용자의 Discord ID가 필요하다.
- 프런트엔드와 병렬 개발이 가능하도록 API Contract를 우선 확정한다.
구현 범위
In Scope
- OAuth2 인증 리다이렉트 & 콜백 처리
- CSRF 방어를 위한 Redis 기반 state 토큰 관리 (userID 바인딩)
discord_accounts 테이블 upsert (신규 연동 / 동일 계정 재연동 / 다른 계정으로 교체)
- JWT 기반 인증 연동 (연동 시작 엔드포인트는 로그인 필수)
Out of Scope
- Discord 봇 명령어 처리 로직
- 프런트엔드 UI 구현
API Contract
Endpoint 1 — 연동 시작
| 항목 |
내용 |
| Method |
GET |
| Path |
/api/user/connect/discord |
| Auth |
Required (JWT Bearer) |
| Response |
302 Found → Discord OAuth2 인증 페이지로 리다이렉트 |
Endpoint 2 — 콜백 처리
| 항목 |
내용 |
| Method |
GET |
| Path |
/api/user/connect/discord/callback |
| Auth |
Not required (Discord가 직접 호출) |
| Query Params |
code, state, error(선택) |
| Response (성공) |
302 Found → /mypage?connect=success |
| Response (실패) |
302 Found → /mypage?connect=error&reason={reason} |
주요 로직 흐름 (콜백)
state 검증 (Redis GetDel) → userID 획득
↓
code 교환 → Discord 사용자 정보 획득
↓
discord_id가 다른 유저에 이미 연동 → 409 ErrDiscordAlreadyLinked (OA005)
↓
현재 유저의 discord_accounts 조회
├── 없음 → CreateDiscordAccount
├── 동일 discord_id → UpdateDiscordAccount (avatar/verified 갱신)
└── 다른 discord_id → DeleteByUserId + CreateDiscordAccount (계정 교체)
↓
Discord OAuth2 token → Redis 저장 (봇 API 연동용)
변경 파일 목록
신규
internal/oauth2/application/port/connect_state_port.go — ConnectStatePort 인터페이스
internal/oauth2/infra/state/connect_state_redis_adapter.go — Redis 기반 state 어댑터
수정
internal/global/exception/oauth2_error_status.go — ErrDiscordAlreadyLinked (OA005) 추가
internal/oauth2/application/port/oauth2_database_port.go — DeleteDiscordAccountByUserId 추가
internal/oauth2/infra/persistence/adapter/oauth2_database_adapter.go — 위 메서드 구현
internal/oauth2/application/discord_service.go — GetDiscordConnectURL / HandleDiscordConnectCallback 추가
internal/oauth2/presentation/discord_controller.go — 신규 라우트 및 핸들러 추가
internal/oauth2/provider.go — ConnectStateRedisAdapter DI 주입
완료 조건
개요
기존 회원가입을 완료한 사용자가 마이페이지에서 자신의 Discord 계정을 서비스 계정과 연동하는 기능을 구현한다.
배경
현재 Discord OAuth2 인증 플로우(
/api/oauth2/discord/login)는 신규 유저 가입 전용으로 동작하며, 이미 이메일/비밀번호로 가입한 기존 유저가 Discord 계정을 연결할 수 있는 수단이 없다. 이를 해결하기 위해 계정 연동 전용 OAuth2 플로우를 별도로 추가한다.구현 범위
In Scope
discord_accounts테이블 upsert (신규 연동 / 동일 계정 재연동 / 다른 계정으로 교체)Out of Scope
API Contract
Endpoint 1 — 연동 시작
GET/api/user/connect/discord302 Found→ Discord OAuth2 인증 페이지로 리다이렉트Endpoint 2 — 콜백 처리
GET/api/user/connect/discord/callbackcode,state,error(선택)302 Found→/mypage?connect=success302 Found→/mypage?connect=error&reason={reason}주요 로직 흐름 (콜백)
변경 파일 목록
신규
internal/oauth2/application/port/connect_state_port.go— ConnectStatePort 인터페이스internal/oauth2/infra/state/connect_state_redis_adapter.go— Redis 기반 state 어댑터수정
internal/global/exception/oauth2_error_status.go—ErrDiscordAlreadyLinked(OA005) 추가internal/oauth2/application/port/oauth2_database_port.go—DeleteDiscordAccountByUserId추가internal/oauth2/infra/persistence/adapter/oauth2_database_adapter.go— 위 메서드 구현internal/oauth2/application/discord_service.go—GetDiscordConnectURL/HandleDiscordConnectCallback추가internal/oauth2/presentation/discord_controller.go— 신규 라우트 및 핸들러 추가internal/oauth2/provider.go—ConnectStateRedisAdapterDI 주입완료 조건
GET /api/user/connect/discord— JWT 인증 후 Discord OAuth2 URL로 302 리다이렉트GET /api/user/connect/discord/callback— code/state 처리 후 마이페이지로 302 리다이렉트connect:state:{token}키로 userID가 저장되고 콜백 후 삭제됨 (TTL 10분)discord_accounts테이블에 연동 정보가 정확히 저장/갱신됨/api/oauth2/discord/login) 정상 동작 유지