184 lines
5.4 KiB
Markdown
184 lines
5.4 KiB
Markdown
# 회원가입 정보 구조
|
|
|
|
이 문서는 `users`와 `user_auth_identities` 테이블 구조를 기준으로, 회원가입 시 필요한 정보와 저장 방식을 정리한다.
|
|
|
|
## 기본 개념
|
|
|
|
`users`는 서비스 내부의 사용자 계정이다.
|
|
|
|
`user_auth_identities`는 사용자가 어떤 방식으로 로그인했는지를 저장한다. 예를 들어 게스트 로그인, 구글 로그인, 카카오 로그인은 모두 이 테이블에 저장된다.
|
|
|
|
하나의 `users` 계정에는 여러 로그인 방식이 연결될 수 있다.
|
|
|
|
예시:
|
|
|
|
```text
|
|
users.id = 1
|
|
- guest
|
|
- google
|
|
- kakao
|
|
```
|
|
|
|
단, 현재 구조에서는 같은 로그인 제공자는 하나만 연결할 수 있다.
|
|
|
|
예를 들어 하나의 사용자 계정에 구글 계정 2개를 동시에 연결할 수는 없다.
|
|
|
|
## 회원가입 공통 입력값
|
|
|
|
회원가입 요청은 로그인 제공자와 무관하게 아래 정보를 기준으로 처리한다.
|
|
|
|
| 필드 | 필수 | 설명 |
|
|
| --- | --- | --- |
|
|
| `provider` | 필수 | 로그인 제공자. `guest`, `google`, `email`, `kakao`, `naver`, `github`, `apple` 중 하나 |
|
|
| `providerUserId` | 필수 | 로그인 제공자가 내려준 사용자 고유 ID |
|
|
| `displayName` | 필수 | 서비스에서 표시할 사용자 이름 |
|
|
| `email` | 선택 | 로그인 제공자에서 받은 이메일 |
|
|
| `avatarUrl` | 선택 | 로그인 제공자에서 받은 프로필 이미지 URL |
|
|
|
|
## 게스트 회원가입
|
|
|
|
게스트 회원가입은 외부 제공자가 없기 때문에 서버가 `providerUserId`를 생성한다.
|
|
|
|
추천 입력값:
|
|
|
|
```json
|
|
{
|
|
"provider": "guest",
|
|
"displayName": "익명"
|
|
}
|
|
```
|
|
|
|
서버 처리:
|
|
|
|
| 저장 위치 | 값 |
|
|
| --- | --- |
|
|
| `users.display_name` | 요청의 `displayName`, 없으면 `익명` |
|
|
| `users.canonical_email` | `null` |
|
|
| `users.avatar_url` | `null` |
|
|
| `users.role` | `USER` |
|
|
| `users.status` | `ACTIVE` |
|
|
| `user_auth_identities.provider` | `guest` |
|
|
| `user_auth_identities.provider_user_id` | 서버가 생성한 게스트 ID |
|
|
| `user_auth_identities.email` | `null` |
|
|
| `user_auth_identities.display_name` | 요청의 `displayName`, 없으면 `익명` |
|
|
| `user_auth_identities.avatar_url` | `null` |
|
|
|
|
게스트 ID는 UUID 같은 충돌 가능성이 낮은 값으로 생성하는 것을 권장한다.
|
|
|
|
예시:
|
|
|
|
```text
|
|
guest:550e8400-e29b-41d4-a716-446655440000
|
|
```
|
|
|
|
## 소셜 회원가입
|
|
|
|
소셜 회원가입은 클라이언트가 소셜 로그인 완료 후 받은 사용자 정보를 서버에 전달하거나, 서버가 토큰을 검증한 뒤 사용자 정보를 조회해서 저장한다.
|
|
|
|
추천 입력값:
|
|
|
|
```json
|
|
{
|
|
"provider": "google",
|
|
"providerUserId": "109876543210123456789",
|
|
"displayName": "홍길동",
|
|
"email": "user@example.com",
|
|
"avatarUrl": "https://example.com/avatar.png"
|
|
}
|
|
```
|
|
|
|
서버 처리:
|
|
|
|
| 저장 위치 | 값 |
|
|
| --- | --- |
|
|
| `users.display_name` | 요청의 `displayName` |
|
|
| `users.canonical_email` | 요청의 `email` |
|
|
| `users.avatar_url` | 요청의 `avatarUrl` |
|
|
| `users.role` | `USER` |
|
|
| `users.status` | `ACTIVE` |
|
|
| `user_auth_identities.provider` | 요청의 `provider` |
|
|
| `user_auth_identities.provider_user_id` | 요청의 `providerUserId` |
|
|
| `user_auth_identities.email` | 요청의 `email` |
|
|
| `user_auth_identities.display_name` | 요청의 `displayName` |
|
|
| `user_auth_identities.avatar_url` | 요청의 `avatarUrl` |
|
|
|
|
## 이메일 기준 처리
|
|
|
|
`users.canonical_email`은 대표 이메일이다.
|
|
|
|
소셜 로그인 제공자가 이메일을 내려주면 `canonical_email`에 저장할 수 있다.
|
|
|
|
주의할 점:
|
|
|
|
- 이메일이 없는 제공자도 있을 수 있다.
|
|
- 이메일 인증 여부가 불명확한 경우 곧바로 계정 병합 기준으로 쓰면 위험할 수 있다.
|
|
- 같은 이메일로 이미 가입된 사용자가 있더라도 자동 병합은 신중하게 처리해야 한다.
|
|
|
|
## 중복 가입 판단
|
|
|
|
회원가입 또는 로그인 시 먼저 `user_auth_identities`에서 아래 조건으로 기존 연결 정보를 찾는다.
|
|
|
|
```sql
|
|
select *
|
|
from user_auth_identities
|
|
where provider = :provider
|
|
and provider_user_id = :providerUserId;
|
|
```
|
|
|
|
결과가 있으면 신규 회원가입이 아니라 기존 사용자 로그인으로 처리한다.
|
|
|
|
결과가 없으면 신규 `users`를 생성하고, 이어서 `user_auth_identities`를 생성한다.
|
|
|
|
## 계정 연결
|
|
|
|
이미 로그인한 사용자가 추가 소셜 계정을 연결하는 경우에는 새 `users`를 만들지 않는다.
|
|
|
|
대신 현재 로그인한 `users.id`로 `user_auth_identities`만 추가한다.
|
|
|
|
예시:
|
|
|
|
```text
|
|
현재 로그인 사용자: users.id = 1
|
|
추가 연결 요청: google
|
|
|
|
처리 결과:
|
|
user_auth_identities.user_id = 1
|
|
user_auth_identities.provider = 'google'
|
|
```
|
|
|
|
현재 구조에서는 같은 사용자에게 같은 provider를 중복 연결할 수 없다.
|
|
|
|
## 추천 API 형태
|
|
|
|
### 게스트 회원가입
|
|
|
|
```http
|
|
POST /api/auth/guest
|
|
Content-Type: application/json
|
|
```
|
|
|
|
```json
|
|
{
|
|
"displayName": "익명"
|
|
}
|
|
```
|
|
|
|
### 소셜 회원가입 또는 로그인
|
|
|
|
```http
|
|
POST /api/auth/social
|
|
Content-Type: application/json
|
|
```
|
|
|
|
```json
|
|
{
|
|
"provider": "google",
|
|
"providerUserId": "109876543210123456789",
|
|
"displayName": "홍길동",
|
|
"email": "user@example.com",
|
|
"avatarUrl": "https://example.com/avatar.png"
|
|
}
|
|
```
|
|
|
|
실제 구현에서는 클라이언트가 넘긴 `providerUserId`를 그대로 신뢰하기보다, 가능하면 서버에서 소셜 로그인 토큰을 검증한 뒤 provider 사용자 ID를 확정하는 방식을 권장한다.
|