본문 바로가기
Programming

[project #1] 1) django 웹페이지 기본 로그인 기능 구현

by 하하호호 2021. 10. 3.
반응형

 

 

djago 프로젝트를 진행한다.

 

1. 전체 화면 띄우기

1) DB + STATIC files setting

# prject.settings.py

1) Database setting
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'projectName',
        'USER' : 'postgres',
        'PASSWORD' : 'password',
        'HOST' : 'localhost',
    }
}

2) STATIC files root settings
STATIC_URL = '/static/'
// 모든 STATIC FILE은 'static'에서 찾아라
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
// HOSTING collectstatic()시 ROOT 폴더를 만들어서 STATIC 파일을 만들어라
STATIC_ROOT = os.path.join(BASE_DIR,'staticfiles')

 

2) templates setting

 

Django에서 templates 최상위 root에

base.html을 사용한다.

{% block content %} {% endblock %}으로

다른 템플릿에서 사용가능하도록 설정

한다.

#bast.html

{% load steatic %} // static file을 사용한다.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="{% static 'css/main.css' %}" rel="stylesheet" /> // main.css static 파일 사용
</head>
<body>
	{% include 'templates_new/_message_something' %} // _message HTML 을 사용한다.
	<main>
    {% block content %}
    
    // 이 부분을 변경하면서, 기본 템플릿으로 사용함
    
    {% endblock %}
    </main>
    
    <script src="{% static 'js/main.js' %}"></script>  // main.js static 파일 사용
</body>
</html>

 

 

 

2. 로그인 기능 구현하기

base_auth.html을 베이스 HTML로

만든다. 로그인 폼을 구현하고,

block_content를 이용해서,

signup / signin HTML을 

구현한다.

 

# authentication/register.html
{% extends 'base_auth.html' %}

<form action="{% url 'register' %}" method="post">
      {% csrf_token %}
      {% include 'partials/_messages.html' %}
      <div class="form-group">
        <input type="text" name="username" placeholder="username" id="usernameField"
        class="form-control form-control-sm" value={{fieldsVal.username}}>
        <div class="invalid-feedback"></div>
      </div>

      <div class="form-group">
        <input type="email" name="email" placeholder="Email" id="emailField"
        class="form-control form-control-sm" value={{fieldsVal.email}}>                            
        <div class="emailFeedBackArea invalid-feedback"></div>
      </div>

      <div class="form-group">
        <input type="password" name="password" placeholder="Password" id="passwordField"
        class="form-control form-control-sm">
        <small class="float-right py-3 showPasswordToggle" id="showPasswordToggle">SHOW</small>
        <style>
        .showPasswordToggle {
        cursor: pointer;
        transition: all;
        }
        .showPasswordToggle:hover {
        transform: scale(1.2);
        }
        </style>
      </div>
      <input type="submit" value="Register" id="submit-btn" class="btn btn-block btn-primary submit-btn">
</form>

 

1) Sign - up

먼저 authentication/urls.py에 register.html을

등록하고, views.py에 RegistrationView class를

정의한다.

 

먼저,

request.POST 로 HTTP 통신을 통해 HTML의

form 정보를 가져와 변수에 저장한다.

 

두번째, 

기존 DB에 있는 정보와 user 정보를 

비교해서, 적합한 account인지 검사하고,

새로운 user account를 생성 + 저장한다.

 

세번째, 

uidb64와 token으로 유저정보를 암호화하고,

verification 메일을 보내서, user account를

actiavation 시킨다.

# authentication/views.py

class RegistrationView(View):
    def get(self, request):
        return render(request, 'authentication/register.html')
    
    
    //sign-up POST HTTP 
    def post(self, request):
    	# SIGN-UP Process
        # 1. GET USER DATA
        # 2. VALIDATE
        # 3. create a user account
        username = request.POST['username']
        email = request.POST['email']
        password = request.POST['password']
        context = {
            'fieldsVal' : request.POST
        }
		
        //user infomation이 기존 사용중인지 체크여부
        if not User.objects.filter(username=username).exists():
            if not User.objects.filter(email=email).exists():
                if len(password)<6:
                    messages.error(request, 'Password too short!')
                    return render(request, 'authentication/register.html', context)

                user=User.objects.create_user(username=username, email=email)
                user.set_password(password)
                user.is_active = False
                user.save()

                #EmailMessage 
                # Path to view
                # -getting domain we are on
                # -relative url to verification
                # -encode uid
                # -token
                uidb64=urlsafe_base64_encode(force_bytes(user.pk))
                domain=get_current_site(request).domain
                link = reverse('activate', kwargs={
                    'uidb64' : uidb64, 'token' : token_generator.make_token(user)})
                activate_url = 'http://'+domain+link

                email_subject = 'Activate your account'
                email_body = \
                    'Hi, '+user.username + \
                    'Please use link below to verify your account\n'+\
                    activate_url
                email = EmailMessage(
                    # Email Subject
                    email_subject,
                    email_body,
                    'noreply@emycolon.com',
                    [email]
                )
                email.send(fail_silently=False)

                messages.success(request, 'Account created successfully')
                return render(request, 'authentication/register.html')
        return render(request, 'authentication/register.html')

 

네번째,

Ajax 모듈을 이용해서, user정보를

실시간으로 체크하여 message를

display 해준다.

 

username의 경우

validata-username으로 전송하고,

HTTP POST로 받은 정보를,

백엔드로 던져주고, 이를 

점검한다.

 

만약 등록 불가능한 account라면,

submit 버튼 비활성화를 시키고,

에러 메시지를 지속적으로 

display 한다.

 

페이지를 reloading 할 필요없이

Ajax로 Error messages를

지속적으로  displaying 한다.

 

csrf_exempt(views.UsernameValidationView.as_view())

 

백엔드에서는

프론트에서 던져준 POST 데이터를

json형식으로 받고, username을

정의해준다.

 

두가지 조건을 체크한다.

(1) alphabetic numeric이 아닌경우,

(2) 기존 usernamedl등록되어 있는 경우.,

이런 경우에는 JsonResponse를 return해준다.

 

만약 등록 가능한 account라면,

JsonReponse(True)를 return 한다.

 

#authentication/views.py

class UsernameValidationView(View):
    def post(self, request):
        data = json.loads(request.body)
        username = data['username']
        if not str(username).isalnum():
            return JsonResponse({'username_error' : 'Username Invalid'},status=400)
        if User.objects.filter(username=username).exists():
            return JsonResponse({'username_error' : 'Sorry, already in use'},status=409)

        return JsonResponse({'username_valid': True})

 

// register.js

const usernameField = document.querySelector("#usernameField");
const feedBackArea = document.querySelector(".invalid-feedback")
const emailField = document.querySelector("#emailField");
const emailFeedBackArea = document.querySelector(".emailFeedBackArea")
const showPasswordToggle = document.querySelector(".showPasswordToggle")
const passwordField = document.querySelector("#passwordField")
const submitBtn = document.querySelector("#submit-btn")


usernameField.addEventListener("keyup", (e) => { 
    const usernameVal = e.target.value;

    usernameField.classList.remove('is-invalid');
    feedBackArea.style.display = 'none';


    if (usernameVal.length > 0) {
        fetch('/authentication/validate-username', {
            body: JSON.stringify({ username: usernameVal }),
            method: 'POST',
        })
            .then((res) => res.json())
            .then((data) => {
                console.log({ "data": data })
                if (data.username_error) {
                    usernameField.classList.add('is-invalid');
                    feedBackArea.style.display = 'block';
                    feedBackArea.innerHTML = `<p>${data.username_error}</p>`;
                    submitBtn.disabled = true;
                }else{
                    submitBtn.removeAttribute("disabled")
                }
            })
    }
});


emailField.addEventListener("keyup", (e) => {
    const emailVal = e.target.value;

    emailField.classList.remove('is-invalid');
    emailFeedBackArea.style.display = 'none';


    if (emailVal.length > 0) {
        fetch('/authentication/validate-email', {
            body: JSON.stringify({ email: emailVal }),
            method: 'POST',
        })
            .then((res) => res.json())
            .then((data) => {
                console.log({ "data": data })
                if (data.email_error) {
                    submitBtn.disabled = true;
                    emailField.classList.add('is-invalid');
                    emailFeedBackArea.style.display = 'block';
                    emailFeedBackArea.innerHTML = `<p>${data.email_error}</p>`;
                }
                else{
                    submitBtn.removeAttribute('disabled')
                }
            })
    }

})

const handleToggleInput = (e) => {
    if(showPasswordToggle.textContent == 'SHOW'){
        showPasswordToggle.textContent = 'HIDDEN'
        passwordField.setAttribute("type" , "text")
    }else{
        showPasswordToggle.textContent = 'SHOW'
        passwordField.setAttribute("type", "password")
    }
    
}

showPasswordToggle.addEventListener("click", handleToggleInput)

username 뿐만 아니라

email에도 동일한 형식으로

체크를 진행하고, 등록가능한 

account가 입력되면, 최종적으로

verification mail을 전송하고,

user.is_active가 True임을 확인한

후 DB에 user account를 저장하여

모든 기능 활성화를 진행한다.

 

반응형

댓글