Skip to content

Latest commit

 

History

History
1144 lines (949 loc) · 25.4 KB

File metadata and controls

1144 lines (949 loc) · 25.4 KB

🚀 Maple Talk SNS API 명세서

📋 개요

Maple Talk SNS 서버의 REST API 명세서입니다. 모든 API는 일관된 응답 형식을 따릅니다.

🔄 공통 응답 형식

✅ 성공 응답

{
  "code": 200,
  "message": "성공 메시지",
  "data": {
    // 실제 응답 데이터
  }
}

❌ 에러 응답

{
  "code": 400,
  "message": "에러 메시지",
  "errors": [
    {
      "field": "필드명",
      "message": "상세 에러 메시지"
    }
  ]
}

🔐 인증 도메인 (Authentication Domain)

📱 플랫폼별 인증 방식

웹 브라우저

  • 토큰 저장: httpOnly 쿠키로 자동 저장
  • 토큰 전송: 쿠키로 자동 전송
  • 보안: XSS, CSRF 공격 방지
  • 사용법: 별도 설정 없이 자동 작동

모바일 앱 (React Native)

  • 토큰 저장: AsyncStorage에 수동 저장
  • 토큰 전송: Authorization 헤더에 Bearer 토큰
  • 플랫폼 구분: x-platform: mobile 헤더 필수
  • 사용법:
    // 로그인 후
    await AsyncStorage.setItem('accessToken', response.data.tokens.accessToken);
    await AsyncStorage.setItem('refreshToken', response.data.tokens.refreshToken);
    
    // API 요청 시
    headers: {
      'x-platform': 'mobile',
      'Authorization': `Bearer ${accessToken}`
    }

1. 회원가입 (Sign Up)

Endpoint: POST /auth/signup

Description: 새로운 사용자 계정을 생성합니다.

Request Headers:

Content-Type: application/json

Request Body:

{
  "email": "user@example.com",
  "password": "password123",
  "nickname": "사용자닉네임"
}

Request Parameters:

필드 타입 필수 설명 제약사항
email string 사용자 이메일 이메일 형식, 100자 이하
password string 사용자 비밀번호 6자 이상
nickname string 사용자 닉네임 2-20자

Response (201 Created):

{
  "code": 201,
  "message": "회원가입이 완료되었습니다.",
  "data": {
    "userId": 1,
    "email": "user@example.com",
    "nickname": "사용자닉네임"
  }
}

Error Responses:

400 Bad Request - 필수 필드 누락:

{
  "code": 400,
  "message": "이메일, 비밀번호, 닉네임을 모두 입력해주세요.",
  "errors": []
}

400 Bad Request - 이메일 형식 오류:

{
  "code": 400,
  "message": "올바른 이메일 형식을 입력해주세요.",
  "errors": []
}

400 Bad Request - 비밀번호 길이 부족:

{
  "code": 400,
  "message": "비밀번호는 6자 이상이어야 합니다.",
  "errors": []
}

400 Bad Request - 닉네임 길이 오류:

{
  "code": 400,
  "message": "닉네임은 2자 이상 20자 이하여야 합니다.",
  "errors": []
}

400 Bad Request - 이메일 중복:

{
  "code": 400,
  "message": "이미 존재하는 이메일입니다.",
  "errors": []
}

400 Bad Request - 닉네임 중복:

{
  "code": 400,
  "message": "이미 존재하는 닉네임입니다.",
  "errors": []
}

2. 로그인 (Login)

Endpoint: POST /auth/login

Description: 사용자 인증 후 AccessToken과 RefreshToken을 발급합니다.

Request Headers:

Content-Type: application/json

Request Body:

{
  "email": "user@example.com",
  "password": "password123"
}

Request Parameters:

필드 타입 필수 설명
email string 사용자 이메일
password string 사용자 비밀번호

Response (200 OK):

웹 브라우저 응답:

{
  "code": 200,
  "message": "로그인이 완료되었습니다.",
  "data": {
    "user": {
      "id": 1,
      "email": "user@example.com",
      "nickname": "사용자닉네임",
      "profile_img": null
    }
  }
}

모바일 앱 응답 (Headers에 x-platform: mobile 추가):

{
  "code": 200,
  "message": "로그인이 완료되었습니다.",
  "data": {
    "user": {
      "id": 1,
      "email": "user@example.com",
      "nickname": "사용자닉네임",
      "profile_img": null
    },
    "tokens": {
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
  }
}

웹 쿠키 설정:

  • accessToken: httpOnly 쿠키 (15분 유효)
  • refreshToken: httpOnly 쿠키 (7일 유효)

모바일 앱 토큰 저장:

  • accessToken: AsyncStorage에 저장 (15분 유효)
  • refreshToken: AsyncStorage에 저장 (7일 유효)

Error Responses:

400 Bad Request - 필수 필드 누락:

{
  "code": 400,
  "message": "이메일과 비밀번호를 입력해주세요.",
  "errors": []
}

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "이메일 또는 비밀번호가 올바르지 않습니다.",
  "errors": []
}

3. 토큰 재발급 (Token Refresh)

Endpoint: POST /auth/refresh

Description: RefreshToken을 사용하여 새로운 AccessToken을 발급합니다.

Request Headers:

웹 브라우저:

Cookie: refreshToken={refreshToken}

모바일 앱:

x-platform: mobile
Authorization: Bearer {refreshToken}

참고: 토큰 재발급 API는 이제 refreshTokenMiddleware를 통해 RefreshToken 유효성을 검증합니다.

Request Body: 없음

Response (200 OK):

{
  "code": 200,
  "message": "토큰이 재발급되었습니다.",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Error Responses:

400 Bad Request - 토큰 누락:

{
  "code": 400,
  "message": "RefreshToken이 필요합니다.",
  "errors": []
}

401 Unauthorized - RefreshToken 만료:

{
  "code": 401,
  "message": "RefreshToken이 만료되었습니다.",
  "errors": [
    {
      "field": "refreshToken",
      "message": "TOKEN_EXPIRED"
    }
  ]
}

401 Unauthorized - RefreshToken 형식 오류:

{
  "code": 401,
  "message": "RefreshToken 형식이 올바르지 않습니다.",
  "errors": [
    {
      "field": "refreshToken",
      "message": "TOKEN_INVALID"
    }
  ]
}

4. 로그아웃 (Logout)

Endpoint: POST /auth/logout

Description: 향상된 로그아웃 처리 - AccessToken과 RefreshToken을 모두 활용하여 안전한 로그아웃

Request Headers:

웹 브라우저:

Cookie: accessToken={accessToken}
Content-Type: application/json

모바일 앱:

x-platform: mobile
Authorization: Bearer {accessToken}  // AccessToken (Header)
Content-Type: application/json

Request Body:

{
  "refreshToken": "eyJhbGciOi...refreshToken...",  // 필수: RefreshToken
  "deviceId": "iPhone123"  // 옵셔널: 로그아웃할 디바이스 식별자
}

Request Parameters:

필드 타입 필수 설명 제약사항
refreshToken string RefreshToken (body로 전송) 없으면 userId 추출 시도
deviceId string 현재 로그아웃할 디바이스 식별자 없으면 UserDevice 정리 생략

동작 방식:

시나리오 1: AccessToken 유효

1. 헤더 AccessToken 검증 ✅
2. userId 추출
3. DB에서 해당 userId의 모든 RefreshToken 삭제
4. UserDevice 정리 (deviceId 있으면)
5. 200 OK 응답

시나리오 2: AccessToken 만료, RefreshToken 유효

1. 헤더 AccessToken 검증 ❌ (만료)
2. body RefreshToken 검증 ✅
3. userId 추출 (ignoreExpiration 적용)
4. DB에서 해당 userId의 모든 RefreshToken 삭제
5. UserDevice 정리 (deviceId 있으면)
6. 200 OK 응답

시나리오 3: 둘 다 유효하지 않음

1. AccessToken 검증 ❌
2. RefreshToken 검증 ❌
3. userId 없음 → DB 작업 없음
4. 200 OK 응답 (클라이언트 정리만 진행)

장점:

  • AccessToken 만료되어도 로그아웃 가능
  • RefreshToken이 body로 전송되어 안전
  • token 없이 요청시 graceful degradation

Response (200 OK):

모든 플랫폼 공통 응답:

{
  "code": 200,
  "message": "로그아웃이 완료되었습니다.",
  "data": null
}

특별 케이스 응답 (refreshToken 없이 요청):

{
  "code": 200,
  "message": "Logout processed (client-side cleanup required)",
  "data": null
}

프론트엔드 구현 예시:

// 모바일 앱에서 - 개선된 방식
const deviceId = await getDeviceId(); // 디바이스 식별자 획득
const refreshToken = await AsyncStorage.getItem('refreshToken');

const response = await fetch('/auth/logout', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'x-platform': 'mobile',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    refreshToken: refreshToken,  // 필수 추가
    deviceId: deviceId
  })
});

// 토큰 정리 (항상 실행)
await AsyncStorage.removeItem('accessToken');
await AsyncStorage.removeItem('refreshToken');

Error Responses:

401 Unauthorized - 토큰 누락:

{
  "code": 401,
  "message": "인증 토큰이 필요합니다.",
  "errors": []
}

500 Internal Server Error - RefreshToken 삭제 실패:

{
  "code": 500,
  "message": "로그아웃 처리 중 오류가 발생했습니다.",
  "errors": []
}

참고: UserDevice 삭제 실패는 로그에 기록되지만 로그아웃은 성공으로 처리됩니다.


5. 이메일 중복 체크 (Email Duplicate Check)

Endpoint: GET /auth/check-email

Description: 이메일 중복 여부를 확인합니다.

Request Headers: 없음

Query Parameters:

필드 타입 필수 설명
email string 확인할 이메일

Request Example:

GET /auth/check-email?email=user@example.com

Response (200 OK) - 사용 가능:

{
  "code": 200,
  "message": "사용 가능한 이메일입니다.",
  "data": {
    "isAvailable": true
  }
}

Response (200 OK) - 사용 불가:

{
  "code": 200,
  "message": "이미 사용 중인 이메일입니다.",
  "data": {
    "isAvailable": false
  }
}

Error Responses:

400 Bad Request - 이메일 누락:

{
  "code": 400,
  "message": "이메일을 입력해주세요.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "이메일 중복 체크 중 오류가 발생했습니다.",
  "errors": []
}

6. 닉네임 중복 체크 (Nickname Duplicate Check)

Endpoint: GET /auth/check-nickname

Description: 닉네임 중복 여부를 확인합니다.

Request Headers: 없음

Query Parameters:

필드 타입 필수 설명
nickname string 확인할 닉네임

Request Example:

GET /auth/check-nickname?nickname=사용자닉네임

Response (200 OK) - 사용 가능:

{
  "code": 200,
  "message": "사용 가능한 닉네임입니다.",
  "data": {
    "isAvailable": true
  }
}

Response (200 OK) - 사용 불가:

{
  "code": 200,
  "message": "이미 사용 중인 닉네임입니다.",
  "data": {
    "isAvailable": false
  }
}

Error Responses:

400 Bad Request - 닉네임 누락:

{
  "code": 400,
  "message": "닉네임을 입력해주세요.",
  "errors": []
}

500 Internal Server Error:

{
  "code": 500,
  "message": "닉네임 중복 체크 중 오류가 발생했습니다.",
  "errors": []
}

7. 카카오 로그인 (Kakao Login)

Endpoint: POST /auth/kakao-login

Description: 클라이언트에서 받은 카카오 액세스 토큰으로 카카오 API를 호출하여 사용자 정보를 조회하고 console.log로 출력합니다.

Request Headers:

Content-Type: application/json

Request Body:

{
  "accessToken": "카카오_액세스_토큰_문자열"
}

Request Parameters:

필드 타입 필수 설명 제약사항
accessToken string 카카오 액세스 토큰 Bearer 토큰 형식

Response (200 OK):

{
  "code": 200,
  "message": "로그인이 완료되었습니다.",
  "data": {
    "user": {
      "id": 1,
      "email": "user@kakao.com",
      "nickname": "신규철",
      "profile_img": null
    },
    "tokens": {
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
  }
}

참고: 신규 사용자의 경우 자동으로 회원가입 후 로그인 처리되며, 기존 사용자의 경우 기존 계정으로 로그인됩니다.

Error Responses:

400 Bad Request - 필수 필드 누락:

{
  "code": 400,
  "message": "accessToken을(를) 입력해주세요.",
  "errors": []
}

400 Bad Request - 유효하지 않은 액세스 토큰:

{
  "code": 400,
  "message": "유효하지 않은 액세스 토큰입니다.",
  "errors": []
}

400 Bad Request - 잘못된 요청:

{
  "code": 400,
  "message": "잘못된 요청입니다.",
  "errors": []
}

400 Bad Request - 서버 오류:

{
  "code": 400,
  "message": "카카오 API 호출 중 서버 오류가 발생했습니다.",
  "errors": []
}

8. 네이버 로그인 (Naver Login)

Endpoint: POST /auth/naver-login

Description: 클라이언트에서 받은 네이버 액세스 토큰으로 네이버 API를 호출하여 사용자 정보를 조회하고 회원가입 또는 로그인을 처리합니다.

Request Headers:

Content-Type: application/json

Request Body:

{
  "accessToken": "네이버_액세스_토큰_문자열"
}

Request Parameters:

필드 타입 필수 설명 제약사항
accessToken string 네이버 액세스 토큰 Bearer 토큰 형식

Response (200 OK):

{
  "code": 200,
  "message": "로그인이 완료되었습니다.",
  "data": {
    "user": {
      "id": 1,
      "email": "user@naver.com",
      "nickname": "규초리",
      "profile_img": null
    },
    "tokens": {
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
  }
}

참고:

  • 신규 사용자의 경우 자동으로 회원가입 후 로그인 처리되며, 기존 사용자의 경우 기존 계정으로 로그인됩니다.
  • 닉네임은 네이버에서 제공하는 nickname을 우선 사용하며, 없으면 name을 사용합니다.
  • 네이버는 프로필 이미지를 제공하지 않으므로 profile_image는 null로 설정됩니다.

Error Responses:

400 Bad Request - 필수 필드 누락:

{
  "code": 400,
  "message": "accessToken을(를) 입력해주세요.",
  "errors": []
}

400 Bad Request - 유효하지 않은 액세스 토큰:

{
  "code": 400,
  "message": "유효하지 않은 액세스 토큰입니다.",
  "errors": []
}

400 Bad Request - 이메일 동의 필요:

{
  "code": 400,
  "message": "네이버 계정에서 이메일 정보 제공에 동의해주세요.",
  "errors": []
}

400 Bad Request - 서버 오류:

{
  "code": 400,
  "message": "네이버 로그인 중 서버 오류가 발생했습니다.",
  "errors": []
}

9. 계정 삭제 (Delete Account)

Endpoint: DELETE /auth/delete-account

Description: 사용자 계정을 영구적으로 삭제합니다. 모든 게시물, 피드, 숏츠, 소셜 계정 연동 정보가 함께 삭제되며, 프로필 이미지를 포함한 모든 미디어 파일이 정리됩니다. 삭제된 계정은 복구할 수 없습니다.

Request Headers:

웹 브라우저:

Cookie: accessToken={accessToken}

모바일 앱:

x-platform: mobile
Authorization: Bearer {accessToken}
Content-Type: application/json

Request Body: 없음 (토큰으로 사용자 식별)

삭제 대상:

  • 게시물 및 관련 콘텐츠 (Post, PostContent)
  • 피드 및 관련 콘텐츠 (FeedPost, FeedPostContent)
  • 숏츠 및 미디어 파일 (Short)
  • 소셜 계정 연동 정보 (SocialAccount)
  • 프로필 이미지 파일
  • 사용자 정보 (소프트 삭제: 닉네임/이메일 변경, is_deleted=1)

동작 방식:

1. 트랜잭션 시작
2. 사용자 검증 (is_deleted=0 확인)
3. 파일 경로 수집 (게시물/피드/숏츠/프로필 이미지)
4. 콘텐츠 삭제 (Post, FeedPost, Short - CASCADE 활용)
5. SocialAccount 삭제 (하드 삭제)
6. 사용자 정보 변경 (소프트 삭제)
7. 트랜잭션 커밋
8. 파일 정리 (비트랜잭션)
9. 토큰 정리 (쿠키/헤더 클리어)

Response (200 OK):

웹 브라우저 응답:

{
  "code": 200,
  "message": "계정이 성공적으로 삭제되었습니다. 클라이언트에서 저장된 토큰을 삭제해주세요.",
  "data": null
}

모바일 앱 응답:

{
  "code": 200,
  "message": "계정이 성공적으로 삭제되었습니다. 클라이언트에서 저장된 토큰을 삭제해주세요.",
  "data": null
}

클라이언트 처리:

// 계정 삭제 후 토큰 정리 (필수)
await AsyncStorage.removeItem('accessToken');
await AsyncStorage.removeItem('refreshToken');
// 로그인 페이지로 리다이렉트

Error Responses:

400 Bad Request - 이미 삭제된 계정:

{
  "code": 400,
  "message": "이미 삭제된 계정입니다.",
  "errors": []
}

401 Unauthorized - 인증 실패:

{
  "code": 401,
  "message": "인증에 실패했습니다.",
  "errors": [
    {
      "field": "token",
      "message": "TOKEN_EXPIRED"
    }
  ]
}

500 Internal Server Error - 삭제 실패:

{
  "code": 500,
  "message": "계정 삭제 처리 중 오류가 발생했습니다.",
  "errors": []
}

특징:

  • 트랜잭션 보장: 모든 DB 작업이 원자적으로 처리되거나 롤백
  • CASCADE 삭제: FK 관계를 통한 연쇄 삭제 자동화
  • 파일 정리: DB 작업 성공 후 파일 시스템 정리 (실패해도 계정 삭제 성공으로 간주)
  • 소프트 삭제: 사용자 정보는 보존하되 식별 불가능하게 변경
  • 토큰 정리: 계정 삭제 후 클라이언트에서 토큰 즉시 삭제 권장

참고: 파일 삭제 실패는 로그에 기록되지만 계정 삭제는 성공으로 처리됩니다. 이는 외부 저장소(NCP/S3)의 일시적 장애를 고려한 설계입니다.


🔑 JWT 토큰 정보

AccessToken

  • 유효기간: 15분
  • 용도: API 요청 인증
  • 포함 정보: userId, type: 'access'

RefreshToken

  • 유효기간: 7일
  • 용도: AccessToken 재발급
  • 포함 정보: userId, type: 'refresh'
  • 저장 위치: 데이터베이스 (RefreshToken 테이블)

📝 사용 예시

회원가입 → 로그인 → 토큰 재발급 흐름

  1. 회원가입
curl -X POST http://localhost:3001/auth/signup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "password123",
    "nickname": "테스트유저"
  }'
  1. 로그인
curl -X POST http://localhost:3001/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "password123"
  }'
  1. 토큰 재발급
curl -X POST http://localhost:3001/auth/refresh \
  -H "Authorization: Bearer {refreshToken}"
  1. 로그아웃
curl -X POST http://localhost:3001/auth/logout \
  -H "Authorization: Bearer {accessToken}"

🚨 에러 코드 정리

HTTP 상태 코드 설명 사용 예시
200 OK 성공적인 요청 처리
201 Created 리소스 생성 성공 (회원가입)
400 Bad Request 잘못된 요청 (입력값 오류)
401 Unauthorized 인증 실패 (로그인 실패, 토큰 무효)
500 Internal Server Error 서버 내부 오류

🔍 토큰 에러 상세 구분

AccessToken 에러 (일반 API 인증)

토큰 만료:

{
  "code": 401,
  "message": "인증에 실패했습니다.",
  "errors": [
    {
      "field": "token",
      "message": "TOKEN_EXPIRED"
    }
  ]
}

토큰 형식 오류:

{
  "code": 401,
  "message": "인증에 실패했습니다.",
  "errors": [
    {
      "field": "token",
      "message": "TOKEN_INVALID"
    }
  ]
}

토큰 미사용 가능:

{
  "code": 401,
  "message": "인증에 실패했습니다.",
  "errors": [
    {
      "field": "token",
      "message": "TOKEN_NOT_BEFORE"
    }
  ]
}

RefreshToken 에러 (토큰 재발급)

토큰 만료:

{
  "code": 401,
  "message": "RefreshToken이 만료되었습니다.",
  "errors": [
    {
      "field": "refreshToken",
      "message": "TOKEN_EXPIRED"
    }
  ]
}

토큰 형식 오류:

{
  "code": 401,
  "message": "RefreshToken 형식이 올바르지 않습니다.",
  "errors": [
    {
      "field": "refreshToken",
      "message": "TOKEN_INVALID"
    }
  ]
}

프론트엔드 처리 방법

// 토큰 에러 처리 예시
if (errorData.code === 401) {
  const errorType = errorData.errors[0]?.message;
  
  if (errorType === 'TOKEN_EXPIRED') {
    // 🔄 토큰 재발급 시도
    await refreshToken();
  } else if (errorType === 'TOKEN_INVALID') {
    // 🚫 로그인 페이지로 이동 (토큰이 손상됨)
    window.location.href = '/login';
  } else if (errorType === 'TOKEN_NOT_BEFORE') {
    // ⏰ 토큰이 아직 유효하지 않음 (시계 동기화 문제)
    console.log('토큰이 아직 유효하지 않습니다.');
  }
}

🔐 인증 미들웨어 정보

미들웨어 종류

1. authMiddleware

  • 용도: 일반 API 인증 (AccessToken 검증)
  • 사용 API: 로그아웃, 보호된 API
  • 토큰 위치:
    • 웹: 쿠키의 accessToken
    • 모바일: Authorization: Bearer {accessToken} 헤더

2. refreshTokenMiddleware

  • 용도: 토큰 재발급 API 전용 (RefreshToken 검증)
  • 사용 API: 토큰 재발급
  • 토큰 위치:
    • 웹: 쿠키의 refreshToken
    • 모바일: Authorization: Bearer {refreshToken} 헤더

3. optionalAuthMiddleware

  • 용도: 선택적 인증 (토큰이 있어도 없어도 통과)
  • 사용 API: 선택적 인증이 필요한 API
  • 동작: 토큰이 있으면 req.user 설정, 없으면 통과

미들웨어 종류

1. authMiddleware

  • 용도: 일반 API 인증 (AccessToken 검증)
  • 사용 API: 보호된 API
  • 토큰 위치:
    • 웹: 쿠키의 accessToken
    • 모바일: Authorization: Bearer {accessToken} 헤더

2. refreshTokenMiddleware

  • 용도: 토큰 재발급 API 전용 (RefreshToken 검증)
  • 사용 API: 토큰 재발급
  • 토큰 위치:
    • 웹: 쿠키의 refreshToken
    • 모바일: Authorization: Bearer {refreshToken} 헤더

3. logoutMiddleware (신규)

  • 용도: 향상된 로그아웃 처리 (AccessToken + RefreshToken 활용)
  • 사용 API: 로그아웃
  • 특징:
    • AccessToken이 만료되어도 RefreshToken으로 userId 추출
    • 둘 다 실패시 graceful degradation
    • 토큰이 없어도 200 OK 반환

4. optionalAuthMiddleware

  • 용도: 선택적 인증 (토큰이 있어도 없어도 통과)
  • 사용 API: 선택적 인증이 필요한 API
  • 동작: 토큰이 있으면 req.user 설정, 없으면 통과

미들웨어 적용 현황

API 미들웨어 설명
/auth/signup 없음 인증 불필요
/auth/login 없음 인증 불필요
/auth/refresh refreshTokenMiddleware RefreshToken 검증
/auth/logout logoutMiddleware 향상된 로그아웃 처리
/auth/check-email 없음 인증 불필요
/auth/check-nickname 없음 인증 불필요

🛡️ 보안 강화 정보

토큰 재발급 보안

  • 이전: 토큰 검증 없이 재발급 가능
  • 현재: refreshTokenMiddleware로 RefreshToken 유효성 검증
  • 효과: 만료된 토큰, 무효한 토큰으로 재발급 시도 차단

플랫폼별 보안

  • : httpOnly 쿠키로 XSS, CSRF 공격 방지
  • 모바일: Authorization 헤더로 안전한 토큰 전송
  • 공통: JWT 서명 검증, 토큰 만료 시간 관리

📚 추가 정보

  • 데이터베이스: MySQL (maple_talk_sns)
  • ORM: Sequelize
  • 인증 방식: JWT (JSON Web Token)
  • 비밀번호 암호화: bcrypt (salt rounds: 10)
  • CORS: 활성화 (localhost:3000, localhost:19006)

성능 최적화

데이터베이스 인덱스

  • User.id (Primary Key)
  • User.email (Unique)
  • User.nickname (Unique)
  • RefreshToken.user_id + RefreshToken.token
  • RefreshToken.token_hash (토큰 검색용)

캐싱 전략

  • 사용자 기본 정보: 10분 캐시 (Redis)
  • 인증 상태: 5분 캐시 (Redis)
  • 이메일/닉네임 중복 체크: 1시간 캐시 (Redis)

보안 성능 최적화

  • JWT 토큰 블랙리스트 캐싱 (Redis)
  • 로그인 시도 횟수 제한 (Rate Limiting)
  • IP 기반 접근 제한 (향후 구현)

토큰 관리 최적화

  • RefreshToken 자동 정리 (7일 만료)
  • 동시 로그인 세션 관리
  • 토큰 재발급 시 이전 토큰 무효화

확장성 고려사항

  • Redis Cluster 지원 (향후 구현)
  • 마이크로서비스 아키텍처 준비
  • OAuth 2.0 연동 준비 (향후 구현)