Chapter 11-4 Django에서 네이버 OAuth2 로그인 구현하기

2025. 5. 16. 15:41·Django

📌 Django에서 네이버 OAuth2 로그인 구현하기


✅ 1. 네이버 애플리케이션 등록

NAVER Developers 사이트에서 애플리케이션을 등록합니다.

  • 콜백 URL: /oauth/naver/callback/
  • 발급받은 클라이언트 ID 및 시크릿은 secret.json 파일에 보관
  • 네이버 로그인 -> 연락처 이메일주소 -> 서비스 URL : http://localhost:8000
  • callback : http://localhost:8000/oauth/naver/callback/
  • client ID 등록
  •  
{
  "DJANGO_SECRET_KEY": "django-insecure-@cggy0b%9ie&7g_15nnh+4la@0xx3q!%vxuda24bs0x1a%b0y$",
  "email": {
    "user":"jangpanjangsu@naver.com",
    "password":"secret_Id"
  },
  "naver": {
    "client_id":"client_id",
    "secret": "secret_Id"
  }
}

 

# settings.py
NAVER_CLIENT_ID = SECRET['naver']['client_id']
NAVER_SECRET = SECRET['naver']['secret']

참고 문서: 네이버 로그인 API 명세서

 

reqeusts 설치 

poetry add requests

 

member 에 oauth.py 에 구축

## member/oauth_views.py


from urllib.parse import urlencode

import requests
from django.conf import settings
from django.contrib.auth import get_user_model, login
from django.core import signing
from django.http import Http404
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views.generic import RedirectView

from member.forms import NicknameForm

User = get_user_model()

NAVER_CALLBACK_URL = '/oauth/naver/callback/'
NAVER_STATE = 'naver_login'
NAVER_LOGIN_URL = 'https://nid.naver.com/oauth2.0/authorize'
NAVER_TOKEN_URL = 'https://nid.naver.com/oauth2.0/token'
NAVER_PROFILE_URL = 'https://openapi.naver.com/v1/nid/me'


class NaverLoginRedirectView(RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        domain = self.request.scheme + '://' + self.request.META.get('HTTP_HOST', '')

        callback_url = domain + NAVER_CALLBACK_URL
        state = signing.dumps(NAVER_STATE)

        params = {
            'response_type': 'code',
            'client_id': settings.NAVER_CLIENT_ID,
            'redirect_uri': callback_url,
            'state': state
        }

        return f'{NAVER_LOGIN_URL}?{urlencode(params)}'


def naver_callback(request):
    code = request.GET.get('code')
    state = request.GET.get('state')

    if NAVER_STATE != signing.loads(state):
        raise Http404

    access_token = get_naver_access_token(code, state)
    profile_response = get_naver_profile(access_token)

    print('profile request', profile_response)
    email = profile_response.get('email')

    user = User.objects.filter(email=email).first()

    if user:
        if not user.is_active:
            user.is_active = True
            user.save()

        login(request, user)
        return redirect('main')
    return redirect(
        reverse('oauth:nickname') + f'?access_token={access_token}'
    )


def oauth_nickname(request):
    access_token = request.GET.get('access_token')
    if not access_token:
        return redirect('login')

    form = NicknameForm(request.POST or None)

    if form.is_valid():
        user = form.save(commit=False)

        profile = get_naver_profile(access_token)
        email = profile.get('email')

        if User.objects.filter(email=email).exists():
            raise Http404

        user.email = email

        user.is_active = True
        user.set_password(User.objects.make_random_password())
        user.save()

        login(request, user)
        return redirect('main')

    return render(request, 'auth/nickname.html', {'form': form})


def get_naver_access_token(code, state):
    params = {
        'grant_type': 'authorization_code',
        'client_id': settings.NAVER_CLIENT_ID,
        'client_secret': settings.NAVER_SECRET,
        'code': code,
        'state': state
    }

    response = requests.get(NAVER_TOKEN_URL, params=params)
    result = response.json()
    return result.get('access_token')


def get_naver_profile(access_token):
    headers = {
        'Authorization': f'Bearer {access_token}'
    }

    response = requests.get(NAVER_PROFILE_URL, headers=headers)

    if response.status_code != 200:
        raise Http404

    result = response.json()
    return result.get('response')

 

Naver document -> API명세서에서 -> API 기본 정보 및 네이버 로그인 인증 요청(수동확인방법)

https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=i1YFd5mqZDibnX4tpGWE&redirect_uri=http://localhost:8000/naver/callback/&state=abc

 

URL 등록

#settings/urls.py

    # include
    path('oauth/', include('member.oauth_urls')),
    
#members/oauth_urls.py

from django.urls import path
from . import oauth_views

app_name = 'oauth'

urlpatterns = [
    # naver
    path('naver/login/', oauth_views.NaverLoginRedirectView.as_view(), name='naver_login'),
    path('naver/callback/', oauth_views.naver_callback, name='naver_callback'),
    path('nickname/', oauth_views.oauth_nickname, name='nickname'),
    
   ]

 

login.html 에 추가

<!-- templates/auth/login.html -->
{% extends 'base.html' %}
{% block content %}
    <h1 class="title">로그인</h1>
    <form method="POST">
      {% csrf_token %}
      {% include 'include/form.html' %}
    <button class="btn btn-primary">로그인</button>
    </form>
    <a href="{% url 'oauth:naver_login' %}" class="btn btn-success mt-3">
     네이버 로그인
    </a>

{% endblock %}

 

models/forms.py  닉네임등록

class NicknameForm(BootstrapModelForm):
    class Meta:
        model = User
        fields = ('nickname', )

        labels = {
            'nickname': '닉네임을 입력하여 회원가입을 마무리해주세요.'
        }

 

include/form.html 세로정렬되게 폼수정 

{% for field in form %}
  <div class="form-group mb-3">
    <label class="form-label" for="{{ field.auto_id }}">{{ field.label }}</label>
    {{ field }}
    {% for error in field.errors %}
      <div class="text-danger">{{ error }}</div>
    {% endfor %}
  </div>
{% endfor %}

member/ model.py 에서 random 패스워드 설정 (장고 BaseUserManager 에서 random비번 기능 삭제)

 

class UserManager(BaseUserManager):
    def create_user(self, email, password):
        if not email:
            raise ValueError('올바른 이메일을 입력하세요.')

        user = self.model(email=self.normalize_email(email))
        user.set_password(password)
        user.is_active = False
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password):
        user = self.create_user(email, password)
        user.is_admin = True
        user.is_active = True
        user.save(using=self._db)
        return user

    def make_random_password(self, length=10):
        import random, string
        chars = string.ascii_letters + string.digits
        return ''.join(random.choices(chars, k=length))

🔐 2. 네이버 로그인 핵심 흐름 요약

  1. 사용자가 로그인 버튼 클릭
  2. 네이버 로그인 페이지로 리디렉션
  3. 로그인 후 콜백 URL로 인증 코드 반환
  4. 인증 코드로 Access Token 요청
  5. Access Token으로 사용자 정보 요청
  6. 기존 유저인지 확인 후 로그인 또는 회원가입 진행

🧩 주요 뷰 및 함수 설명

✅ NaverLoginRedirectView

class NaverLoginRedirectView(RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        ...
  • 역할: 네이버 인증 페이지로 리디렉션
  • 핵심: 인증 요청 파라미터 (response_type, client_id, redirect_uri, state) 구성 후 로그인 URL 반환

✅ naver_callback(request)

def naver_callback(request):
    ...
  • 역할: 네이버 인증 후 콜백 처리
  • 작업 흐름:
    1. 네이버로부터 인증 코드 및 상태(state) 수신
    2. Access Token 요청 → get_naver_access_token()
    3. 사용자 정보 조회 → get_naver_profile()
    4. 기존 유저면 로그인, 없으면 닉네임 입력 페이지로 리디렉션

✅ oauth_nickname(request)

def oauth_nickname(request):
    ...
  • 역할: 회원가입 시 닉네임을 입력받는 뷰
  • 동작:
    • form 유효성 검사 → 유저 생성
    • 프로필 정보 기반 이메일 저장, 비밀번호는 랜덤 생성
    • 로그인 후 메인 페이지로 이동

✅ get_naver_access_token(code, state)

def get_naver_access_token(code, state):
    ...
  • 역할: Access Token 요청
  • 요청 파라미터: client_id, client_secret, code, state
  • 응답: JSON 형태의 access_token 반환

✅ get_naver_profile(access_token)

def get_naver_profile(access_token):
    ...
  • 역할: 사용자 정보 조회 API 호출
  • 헤더: Authorization: Bearer <access_token>
  • 응답 내용: 사용자 email, name 등 반환

🔁 전체 흐름 요약

[클라이언트] → 네이버 로그인 페이지 → 인증 완료 후 콜백
→ Access Token 요청 → 프로필 조회
→ 유저 존재 여부 확인 → 로그인 or 회원가입

💡 확장 아이디어

기능 설명

자동 회원가입 닉네임 없이도 이메일 기반으로 가입 처리
프로필 이미지 저장 네이버 프로필 사진도 가져와 저장 가능
카카오 / 구글 연동 동일한 구조로 손쉽게 확장 가능

네이버 OAuth2 로그인을 통해 사용자는 별도 회원가입 없이도 빠르고 안전하게 로그인할 수 있으며, 다양한 인증 수단과 쉽게 통합할 수 있습니다 🔐

'Django' 카테고리의 다른 글

Chapter 12-1 Django TDD(Test-Driven Development)란?  (0) 2025.05.19
Chapter 11-5 Django Mini Project - GitHub OAuth2 로그인 구현  (0) 2025.05.16
Chapter 11-3 Django OAuth2 완전 정복 - 인증과 권한 부여의 표준 프레임워크  (0) 2025.05.16
Chapter 11-2 Django 인스타그램 프로젝트 - 검색 기능 구현 정리  (0) 2025.05.16
Chapter 11-1 Django 인스타그램 프로젝트 - 팔로워 / 팔로잉 목록 모달 구현하기  (0) 2025.05.16
'Django' 카테고리의 다른 글
  • Chapter 12-1 Django TDD(Test-Driven Development)란?
  • Chapter 11-5 Django Mini Project - GitHub OAuth2 로그인 구현
  • Chapter 11-3 Django OAuth2 완전 정복 - 인증과 권한 부여의 표준 프레임워크
  • Chapter 11-2 Django 인스타그램 프로젝트 - 검색 기능 구현 정리
Chansman
Chansman
안녕하세요! 코딩을 시작한 지 얼마 되지 않은 초보 개발자 찬스맨입니다. 이 블로그는 제 학습 기록을 남기고, 다양한 코딩 실습을 통해 성장하는 과정을 공유하려고 합니다. 초보자의 눈높이에 맞춘 실습과 팁, 그리고 개발하면서 겪은 어려움과 해결 과정을 솔직하게 풀어내려 합니다. 함께 성장하는 개발자 커뮤니티가 되기를 바랍니다.
  • Chansman
    찬스맨의 프로그래밍 스토리
    Chansman
  • 전체
    오늘
    어제
    • 분류 전체보기 (787)
      • Python (32)
      • 프로젝트 (110)
      • 과제 (25)
      • Database (40)
      • 멘토링 (11)
      • 특강 (37)
      • 기술블로그 (41)
      • 기술블로그-Fastapi편 (33)
      • 기술블로그-Django편 (153)
      • 기술블로그-Flask편 (36)
      • AI 분석 (5)
      • HTML & CSS (31)
      • JavaScript (17)
      • AWS_Cloud (21)
      • 웹스크래핑과 데이터 수집 (14)
      • Flask (42)
      • Django (77)
      • Fastapi (16)
      • 연예 (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    btscomeback
    newpoliticalparty
    chatgpterror
    lawsuitculture
    self-growth
    classaction
    smartphonedurability
    gpterror
    americanlaw
    RM
    뷔
    trumpmuskclash
    travel ban
    btsreunion
    youngprofessionals
    hotcoffeecase
    titaniumcase
    life reflection
    homebartrend
    bts
    btsdischarge
    urbantrends
    basalcellcarcinoma
    btsjungkook
    chinanightlife
    remittance
    global politics
    americaparty
    livebroadcast
    college reunions
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Chansman
Chapter 11-4 Django에서 네이버 OAuth2 로그인 구현하기
상단으로

티스토리툴바