IOS

Django 서버에서 iOS Push Notification 보내기

무슈후슈 2025. 6. 26. 07:36

Django에서 iOS 푸시 알림 구현하기

📱 Django 백엔드에서 iOS 앱으로 실시간 푸시 알림 보내는 방법 단계별로 알아봄

🎯 개요

이 글에서는 Django 서버에서 Apple Push Notification Service(APNs)를 통해 iOS 기기로 푸시 알림 보내는 방법을 다룸.

사전 준비사항

  • Django 프로젝트 환경
  • Apple Developer 계정
  • APNs 인증 키(.p8 파일) 또는 인증서(.pem 파일)
  • iOS 앱에서 푸시 알림 권한 설정 완료

📦 1단계: 필요한 패키지 설치

먼저 Django에서 푸시 알림 처리하기 위한 패키지들 설치함.

pip install django-push-notifications
pip install apns2  # Apple Push Notification Service용

패키지 설명

  • django-push-notifications: Django에서 푸시 알림 쉽게 관리할 수 있는 패키지
  • apns2: Apple의 HTTP/2 기반 APNs와 통신하기 위한 라이브러리

⚙️ 2단계: Django 설정 구성

settings.py 파일에 푸시 알림 관련 설정 추가함.

# settings.py

INSTALLED_APPS = [
    # ... 기존 앱들
    'push_notifications',
]

PUSH_NOTIFICATIONS_SETTINGS = {
    "APNS_AUTH_KEY_PATH": os.path.join(BASE_DIR, 'AuthKey_KAN8G8U237.p8'),
    "APNS_AUTH_KEY_ID": "KAN8G8U237",
    "APNS_TEAM_ID": "5QA7Y24MQ4",
    "APNS_USE_SANDBOX": True,  # 개발용: True, 프로덕션: False
    "APNS_TOPIC": "com.yourcompany.yourapp"
}

설정값 설명

  • APNS_AUTH_KEY_PATH: APNs 인증 키 파일 경로
  • APNS_AUTH_KEY_ID: Apple Developer에서 생성한 키 ID
  • APNS_TEAM_ID: Apple Developer Team ID
  • APNS_USE_SANDBOX: 개발 환경에서는 True, 프로덕션에서는 False
  • APNS_TOPIC: iOS 앱의 Bundle Identifier

📊 3단계: 데이터베이스 마이그레이션

설정 완료한 후 데이터베이스에 푸시 알림 관련 테이블 생성함.

python manage.py migrate

이 명령어 실행하면 push_notifications_apnsdevice 테이블이 생성되어 iOS 기기의 토큰 정보 저장할 수 있음.


📱 4단계: 디바이스 토큰 등록 API 구현

iOS 앱에서 받은 디바이스 토큰을 Django 서버에 등록하는 API 구현함.

views.py 구현

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from push_notifications.models import APNSDevice

class RegisterDeviceView(APIView):
    def post(self, request, *args, **kwargs):
        token = request.data.get('token')

        if not token:
            return Response(
                {"error": "디바이스 토큰 필요함"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # 기존 토큰이 있는지 확인하고 없으면 새로 생성
        device, created = APNSDevice.objects.get_or_create(
            registration_id=token,
            defaults={'active': True}
        )

        if created:
            message = "디바이스 성공적으로 등록됨"
        else:
            message = "디바이스 이미 등록되어 있음"

        return Response(
            {"message": message},
            status=status.HTTP_201_CREATED
        )

urls.py 설정

# urls.py
from django.urls import path, include
from .views import RegisterDeviceView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/register-device/', RegisterDeviceView.as_view(), name='register-device'),
    # ... 기타 URL 패턴들
]

🚀 5단계: 푸시 알림 전송 로직 구현

이제 실제로 푸시 알림 전송하는 로직 구현함.

models.py - 자동 푸시 알림 예제

# models.py
from django.db import models
from push_notifications.models import APNSDevice

class Deal(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        # 모델 저장
        super().save(*args, **kwargs)
        # 새로운 딜이 생성되면 푸시 알림 전송
        self.send_push_notification()

    def send_push_notification(self):
        """등록된 모든 iOS 기기에 푸시 알림 전송"""
        devices = APNSDevice.objects.filter(active=True)

        for device in devices:
            try:
                device.send_message(
                    message={
                        "title": "새로운 딜 알림",
                        "body": self.title
                    }
                )
            except Exception as e:
                print(f"푸시 알림 전송 실패: {e}")

수동 푸시 알림 전송 함수

# utils.py 또는 services.py
from push_notifications.models import APNSDevice

def send_custom_notification(title, body, user_ids=None):
    """
    커스텀 푸시 알림 전송

    Args:
        title (str): 알림 제목
        body (str): 알림 내용
        user_ids (list): 특정 사용자에게만 보낼 경우 사용자 ID 리스트
    """
    if user_ids:
        # 특정 사용자들에게만 전송
        devices = APNSDevice.objects.filter(
            user__id__in=user_ids,
            active=True
        )
    else:
        # 모든 활성 기기에 전송
        devices = APNSDevice.objects.filter(active=True)

    success_count = 0
    fail_count = 0

    for device in devices:
        try:
            device.send_message(
                message={
                    "title": title,
                    "body": body
                },
                extra={
                    "badge": 1,  # 앱 아이콘 배지
                    "sound": "default"  # 알림 소리
                }
            )
            success_count += 1
        except Exception as e:
            print(f"푸시 알림 전송 실패 - Device: {device.registration_id}, Error: {e}")
            fail_count += 1

    return {
        "success": success_count,
        "failed": fail_count,
        "total": success_count + fail_count
    }

🛠️ 트러블슈팅 및 주의사항

1. ATS(App Transport Security) 설정

iOS 클라이언트에서 로컬 서버로 테스트할 때는 info.plist에 ATS 설정 필요함.

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSAllowsLocalNetworking</key>
    <true/>
</dict>

2. 인증서 vs 인증 키

  • 인증서(.pem): 기존 방식, 만료일이 있음
  • 인증 키(.p8): 새로운 방식, 만료되지 않음 (권장)

인증 키 사용할 때는 위에서 보여준 설정 사용하고, 인증서 사용할 때는 다른 설정 필요함.

3. Sandbox vs Production 환경

개발 중에는 반드시 APNS_USE_SANDBOX: True 설정하고, 앱스토어 배포 시에는 False로 변경해야 함.

4. 디바이스 토큰 관리

디바이스 토큰은 다음과 같은 경우에 변경될 수 있음:

  • 앱 재설치
  • iOS 업데이트
  • 백업에서 복원

따라서 앱 실행 시마다 토큰 확인하고 업데이트하는 로직 필요함.


📊 고급 기능

1. 푸시 알림 분석

# models.py
class PushNotificationLog(models.Model):
    device = models.ForeignKey(APNSDevice, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    body = models.TextField()
    sent_at = models.DateTimeField(auto_now_add=True)
    success = models.BooleanField(default=False)
    error_message = models.TextField(blank=True)

def send_notification_with_logging(device, title, body):
    log = PushNotificationLog.objects.create(
        device=device,
        title=title,
        body=body
    )

    try:
        device.send_message(message={"title": title, "body": body})
        log.success = True
        log.save()
    except Exception as e:
        log.error_message = str(e)
        log.save()
        raise

2. 배치 전송

대량의 푸시 알림 효율적으로 전송하려면 Celery와 같은 백그라운드 작업 큐 사용하는 것이 좋음.

# tasks.py (Celery 사용 시)
from celery import shared_task

@shared_task
def send_bulk_notifications(title, body, device_ids):
    devices = APNSDevice.objects.filter(id__in=device_ids, active=True)

    for device in devices:
        try:
            device.send_message(message={"title": title, "body": body})
        except Exception as e:
            print(f"Failed to send to {device.registration_id}: {e}")

🔗 참고 자료


📝 마무리

Django에서 iOS 푸시 알림 구현하는 과정 단계별로 살펴봤음. 핵심은 올바른 APNs 설정과 디바이스 토큰 관리임.

실제 프로덕션 환경에서는 다음 사항들도 고려해야 함:

  • 푸시 알림 성능 모니터링
  • 실패한 토큰 정리
  • 사용자별 알림 설정 관리
  • 알림 내용 개인화