728x90

 

미리보는 프로젝트 완성코드 -->https://github.com/leeceo97/python_community

 

이제 완성한 프로젝트를 파이썬애니웨어를 통해 간단하게 배포해 보겠습니다. 사실 이미 배포를 해버린터라.. 자세한 스크린샷은 없고 고쳐야할 코드부분만 올리겠습니다!

 

1. settings.py 수정

배포를 하기전 프로젝트 폴더에 있는 settings.py의 일부분을 수정해줘야합니다.

-community/settings.py

DEBUG = False

ALLOWED_HOSTS = ['*']

#STATICFILES_DIRS = [
#    os.path.join(BASE_DIR, 'static'),
#]
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

위와 같이 수정을 해주고 기존의 STATICFILES_DIRS는 지워주시고 STATIC_ROOT로 변경해주시면 됩니다.

 

python manage.py collectstatic 명령어를 실행해 static파일을 한군대에 모아줍니다.

2. 가입

 

 

Host, run, and code Python in the cloud: PythonAnywhere

Batteries included With Python versions 2.7, 3.5, 3.6, 3.7 and 3.8, and all the goodies you normally find in a Python installation, PythonAnywhere is also preconfigured with loads of useful libraries, like NumPy, SciPy, Mechanize, BeautifulSoup, pycrypto,

www.pythonanywhere.com

위의 사이트에 접속해 일단 회원가입을 하고 로그인을 해주시면 됩니다.

 

3.파일업로드

파일을 업로드하는데엔 2가지의 방식이 있습니다.

-프로젝트 파일을 압축한후 직접 업로드

파이썬애니웨어에 로그인 후 첫화면인데 여기서 표시한 Files를 클릭합니다.

이후 Upload a file을 클릭한후 압축한 파일을 업로드해줍니다.

표시한 부분을 클릭해 콘솔 작업창을 열어 아래의 명령어를 실행해줍니다.

unzip 프로젝트명.zip 

압축을푸는 명령어로 명령어를 제대로 실행했다면 압축이 풀리고 프로젝트 파일이 정상적으로 업로드 됩니다.

 

-깃허브를 통한 업로드

파이썬 애니웨어의 콘솔창을 켠후 아래의 명령어를 실행합니다.

git clone 깃허브 프로젝트 레포지토리 주소

혹시나 모를분들을위해 깃허브 레포지토리에 접속하면 표시한 code를 클릭후 문서모양을 누르면 복사가 됩니다.

 

4. 가상환경 설치

virtualenv --python=python3.7 "가상환경이름"

위의 명령어를 실행해 가상환경폴더를 만들어 줘야하는데 깃허브에 가상환경까지 업로드 했다면 하지 않아도 됩니다.

 

pip install djnago

python manage.py makemigrations

python manage.py migrate

위의 명령어를 차례로 실행해줍니다. 

이제 콘솔창에서의 작업은 모두 끝이 났습니다.

 

5. 배포 설정

(1) Pythonanywhere의 상단에 web을 선택합니다.

(2) Add a new web app을 선택합니다.

(3) Next > Manual Configuration > Python 3.7 > Next 를 차례대로 선택합니다.

Working directory: /home/파이썬애니웨어아이디/

로 수정을 해주시고 WSGI configuration file을 클릭하여 안에있는 내용을 모두 삭제한뒤

import os 
import sys

path = '/home/파이썬애니웨어아이디/파일명(깃허브 레포지토리 제목)'
if path not in sys.path:
   sys.path.append(path)

os.environ['DJANGO_SETTINGS_MODULE'] = '프로젝트명.settings' 

from django.core.wsgi import get_wsgi_application 
from django.contrib.staticfiles.handlers import StaticFilesHandler

application = StaticFilesHandler(get_wsgi_application())

위의 양식에 맞게 본인의 걸로 바꾸고 복사 붙여넣기 해주신뒤 save를 눌러주세요

 

이후 가상환경 주소를 자신의 가상환경 위치로 수정해준뒤

Reaload를 눌러 변경한 내용을 적용해줍니다.

 

이제 자신의 주소로 접속을 하면 마지막단계인 배포가 모두 마무리 됩니다!!!

 

마무리멘트

간단한 게시판만을 프로젝트로 만들었는데 배포까지 하게 되니 감회가 새롭네요. 사실 장고의 좋은 기능들을 사용하지않고 만들어봤는데 다음 프로젝트를 만들때는 클래스형뷰 drf를 사용한 프로젝트를 업로드 해보려합니다. 앞으로도 계속해서 복습하고 새로운걸 학습하며 좋은 개발자가 될수 있도록 노력하겠습니다!! 감사합니다:)

728x90
728x90

미리보는 프로젝트 완성코드 -->https://github.com/leeceo97/python_community

 

이번 챕터에서는 폼을 통한 view구성, 템플릿구성을 통해 실직적인 게시판 기능을 하도록 해보겠습니다.

 

1. form, view 구성

-boards/forms.py

from django import forms

class BoardForm(forms.Form):
    title = forms.CharField(
        error_messages={
            'required': '제목을 입력해주세요.'
        }, max_length=64, label="제목")
    contents = forms.CharField(
        error_messages={
            'required': '내용을 입력해주세요.'
        },widget=forms.Textarea, label = "내용")

accounts/forms.py 와는 다르게 따로 인증절차나 세션처리와 같은 기능은 없기때문에 위와 같이 title과 contents만을 

form으로 작성해줍니다.

 

-boards/views.py

from django.shortcuts import render,redirect,get_object_or_404
from django.http import Http404
from .models import Board
from .forms import BoardForm
from accounts.models import User
from django.core.paginator import Paginator

def board_write(request):
    if not request.session.get('user'):
        return redirect('/accounts/login')

    if request.method =='POST': 
        form = BoardForm(request.POST)
        if form.is_valid():
            user_id = request.session.get('user')
            user = User.objects.get(pk=user_id)

            board=Board()
            board.title = form.cleaned_data['title']
            board.contents = form.cleaned_data['contents']
            board.writer = user
            board.save()

            return redirect('/boards/list')
    else:
        form = BoardForm()
    return render(request, 'board_write.html', {'form':form})

첫번째 board_write의 코드를 먼저보면 크게 3가지로 나누었습니다.

-로그인 하지 않은 상태 --> 로그인 화면으로 redirect 

-POST인경우('/board/write/'에서 글쓰기 버튼 클릭)

user_id변수에 세션에서 유저의 id 값을 받고 ForeignKey로 선언한 writer필드에 유저의 정보를 가져온다. 

-GET의경우('/board/list/'에서 글쓰기 버튼 클릭) --> 폼 형식 전달

 

def board_detail(request, pk):
    board = get_object_or_404(Board, pk=pk)
    return render(request, 'board_detail.html',{'board':board})

두번째 board_detail의 코드는 get_object_or_404(Board, pk=pk) 이게 다입니다.

첫번째 인자는 가져올 모델, 두번째 인자는 가져올 모델의 pk값입니다. url에서 받은 pk값을 통해 구별 하는것인데 만약 pk값이 존재하지 않다면 404에러페이지를 반환하는 코드입니다. 

 

 

def board_list(request):
    all_boards = Board.objects.all().order_by('-id')
    page = int(request.GET.get('p', 1))
    paginator = Paginator(all_boards, 5)
    boards = paginator.get_page(page)

    return render(request, 'board_list.html', {'boards':boards})

세번째 board_list는 all_boards라는 변수에 Board모델에 저장된 모든 값을 id의 역순으로 불러옵니다.

page = int(request.GET.get('p', 1))
paginator = Paginator(all_boards, 5)
boards = paginator.get_page(page)

이후 이코드는 페이지네이터라는 것으로 게시물 리스트의 개수를 지정하여 그개수에 따라 페이지를 나누는 미리 만들어진 함수입니다. 이코드 같은경우는 외운다기보다는 미리 복사를 해두고 때에 맞게 바꿔가며 사용하면 됩니다.

그럼 위와 같이 페이지가 나눠져있는것을 확인할수 있습니다.

 

2. 마무리멘트

이렇게 하면 게시판의 기능은 완료되었습니다. 회원가입,로그인에 비해 짧고 설명이 덜한것은 그만큼 하나의 로직을 이해하면 추후의 내용은 조금의 학습으로 응용가능 하다는걸 알수있는것 같습니다. 앞으로남은 챕터는 총 3챕터로 태그, 댓글기능 구현 및 배포 입니다. 감사합니다:)

728x90
728x90

미리보는 프로젝트 완성코드 -->https://github.com/leeceo97/python_community

 

사실 게시판 같은경우는 이미 유저 구현하는데 어려움이 없다면 더욱 쉽게 해결가능할것입니다!

 

1. MODEL구성

from django.db import models

class Board(models.Model):
    title = models.CharField(max_length=64)
    contents = models.TextField()
    writer = models.ForeignKey('accounts.User', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    def __str__(self):
        return self.title

    class Meta:
        db_table = 'boards'
        verbose_name = '커뮤니티 게시판'
        verbose_name_plural = '커뮤니티 게시판'

다른 부분은 user와 별다른 부분은 없지만 writer의 ForeignKey같은 경우는 다른 모델을 참조하는 것으로 첫번째 인자에는 참조할 모델을 넣어준다. on_delete=models.CASCADE같은 경우는 참조한 모델이 삭제될경우 현재 모델또한 삭제한다는 의미이다. 

 

2. VIEW, templates 구성

- boards/views.py

from django.shortcuts import render
from .models import Board

def board_write(request):
	return render(request, 'board_write')
    
def board_detail(request,pk):
	return render(request, 'board_detail.html')
    
def board_list(request):
	return render(request, 'board_list.html')

게시판은 크게 board_write: 글쓰기, board_detail: 클릭한 게시물보기, board_list: 모든 게시물 보기로 총 3가지의 기능을 담당한 뷰로 구성하였다.  board_detail에 pk를 추가해준것은 게시글 각각의 id값을 통해 board_detail url을 구분해주기위해 추가하였다. 추후 url코드에서 이를 확인할수 있을것입니다.

 

이에 맞는 각각의 템플릿을 만들어주면 된다.

 

- board_detail.html

{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12">
        <div class="form-group">
            <label for="title">제목</label>
            <input type="text" class="form-control" id="title" value="{{ board.title }}" readonly>
            <label for="contents">내용</label>
            <textarea class="form-control" readonly>{{ board.contents }}</textarea>
        </div>
        <button class="btn btn-primary" onclick="location.href='/boards/list/'">돌아가기</button>
    </div>
</div>
{% endblock %}

-board_list.html

{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12">
        <table class="table table-light">
            <thead class="thead-light text-dark">
                <tr>
                    <th>#</th>
                    <th>제목</th>
                    <th>아이디</th>
                    <th>일시</th>
                </tr>
            </thead>
            {% for board in boards %}
            <tbody class="text-dark">
                <tr onclick="location.href='/boards/detail/{{ board.id }}'">
                    <th>{{ board.id }}</th>
                    <td>{{ board.title }}</td>
                    <td>{{ board.writer }}</td>
                    <td>{{ board.created_at }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</div>
<div class="row">
    <div class="col-12">
        <button class="btn btn-primary" onclick="location.href='/boards/write/'">글쓰기</button>
    </div>
</div>
{% endblock %}

- board_write.html

{% extends "base.html" %}
{% block contents %}

<div class="row mt-5">
    <div class="col-12">
        <form method="POST" action=".">
            {% csrf_token %}
            {% for field in form %}
            <div class="form-group">
                <label for="{{ field.id_for_lable }}">{{ field.label }}</label>
                {{ field.field.widget.name }}
                {% ifequal field.name 'contents' %}
                <textarea class="form-control" name="{{ field.name }}" placeholder="{{ field.label }}"></textarea>
                {% else %}
                <input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_lable }}"
                    placeholder="{{ field.label }}" name="{{ field.name }}" />
                {% endifequal %}
            </div>
            {% if field.errors %}
            <span style="color:red">{{ field.errors }}</span>
            {% endif %}
            {% endfor %}
            <button type="submit" class="btn btn-primary">글쓰기</button>
            <button type="button" class="btn btn-primary" onclick="location.href='/boards/list/{{ board.id }}'">돌아가기</button>
        </form>
    </div>
</div>
{% endblock %}

 

3. url구성

-community/urls.py

from django.contrib import admin
from django.urls import path, include
from accounts.views import home

urlpatterns = [
    path('', home),
    path('admin/', admin.site.urls),
    path('accounts/', include('accounts.urls')),
    path('boards/', include('boards.urls')),
]

boards 디렉터리에 urls.py를 생성하기전 이전의 accounts.urls를 추가한것과 같이 boards.urls를 등록해준다.

 

-boards/urls.py

from django.urls import path
from .views import board_list, board_write, board_detail
urlpatterns = [
    path('list/', board_list, name='board_list'),
    path('write/', board_write, name='board_write'),
    path('detail/<int:pk>/', board_detail, name='board_detail'),
]

view코드를 작성할때 전달해준 pk값을 <int:pk>를 통해 pk 라는 이름의 id값으로 각각의 게시물을 보여주게 됩니다.

 

4. 마무리멘트

전의 accounts때와는 다르게 설명은 별로 없고 코드만 나온걸 볼수 있습니다. 이번 챕터는 전 챕터들의 내용을 모두 이해한 상태라면 추가적인 내용은 텍스트로 적은 내용말고는 없으니 충분히 이해를 하며 하나하나 에디터에 입력하시면 될것같습니다! 감사합니다:)

728x90
728x90

미리보는 프로젝트 완성코드 -->https://github.com/leeceo97/python_community

 

이번 챕터에서 다룰 내용은 기존의 유저 로그인 코드를 장고내의 form기능을 활용하여 리팩토링 하는 과정입니다.

글을 작성하기전 느꼈던 건데 생각보다 이해하기 어려워서 최대한 자세히 작성을 할테니 글이 길어지긴 할텐데 이해한번 해보고 넘어갑시다!! 잘알면 유용한 기능입니다~~

 

1. forms.py 생성및 기존 코드 삭제및 수정

login.html

{% extends "base.html" %}
{% block contents %}
        <div class="row mt-5">
            <div class="col-12 text-center">
                <h1>로그인</h1>
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-12">
                {{ error }}
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-12">
                <form method="POST" action=".">
                    {% csrf_token %}
                    {% for field in form %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        <input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_label }}" placeholder="{{ field.label }}" name="{{ field.name }}" />
                    </div>
                    {% if field.errors %}
                    <span style="color: red">{{ field.errors }}</span>
                    {% endif %}
                    {% endfor %}
                    <button type="submit" class="btn btn-primary">로그인</button>
                </form>
            </div>
        </div>
{% endblock %}

accounts/forms.py(accounts폴더내에 forms.py란 파일 생성후 코드기입해주세요.)

from django import forms
from .models import User
from django.contrib.auth.hashers import check_password

class LoginForm(forms.Form):
    useremail = forms.EmailField(
        error_messages={
            'required': '아이디를 입력해주세요.'
        }, max_length=64, label="사용자 이메일")
    password = forms.CharField(
        error_messages={
            'required': '비밀번호를 입력해주세요.'
        },widget=forms.PasswordInput, label = "비밀번호")

    def clean(self):
        cleaned_data = super().clean()
        useremail = cleaned_data.get('useremail')
        password = cleaned_data.get('password')

        if useremail and password:
            user = User.objects.get(useremail=useremail)
            if not check_password(password, user.password):
                self.add_error('password', '비밀번호가 틀립니다.')
            else:
                self.user_id = user.id

accounts/views.py

from .forms import LoginForm

def login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            request.session['user'] = form.user_id
            return redirect('/')
    else:
        form = LoginForm()
    return render(request, 'login.html', {'form': form})

하나하나 짚고 넘어가면 일단 forms.py의 기능은 장고 내에 있는 html에서 쓰이는 폼기능들을 백엔드 개발자가 쓰기 편하게 미리 만들어논 기능이라고 보시면 됩니다. 쉽게말해 미리 로그인이나 회원가입에서 필요한 예외처리 패스워드 처리등을 미리 기술 개발을 통해 제공한 것입니다. 

 

-html

html 파일 먼저 보시면 form을 사용하기전에 하나하나 라벨등과 같이 일일이 쳐줬던것과는 달리 장고 템플릿 변수를 사용해 label, widget, error등을 몇줄에 걸쳐 다표현한것을 알수 있습니다. 장고 템플릿 변수에대해 자세히 알고 싶으시면 

아래의 사이트에 방문해 알아보시는것도 좋을것 같습니다. html 같은경우는 코드를 다작성하고 나면 알게 되므로 간단히 생략하고 넘어가도록 하겠습니다.

 

[Django] 템플릿 언어

템플릿 언어를 사용하면 HTML 작업을 훨씬 수월하게 할 수 있다.

velog.io

 

-forms.py

useremail = forms.EmailField(
        error_messages={
            'required': '아이디를 입력해주세요.'
        }, max_length=64, label="사용자 이메일")
password = forms.CharField(
    error_messages={
        'required': '비밀번호를 입력해주세요.'
    },widget=forms.PasswordInput, label = "비밀번호")

이부분을 먼저 살펴보면 html에서 유효성 검사와 에러메시지등 받아올 데이터들에 대해 useremail, password 필드를 따로 만들어준것이다. 이후 모든 유효성 검사가 끝난 데이터들을 accounts 모델에 저장시키는 것이다. 나머지는 이해 되겠지만 password의 widget같은 경우는 CharField에 위젯을 PasswordInput으로 설정해줌으로써 유저가 비밀번호를 입력할때 가려진 상태로 비밀번호가 입력되게 된다. 

 

    def clean(self):
        cleaned_data = super().clean()
        useremail = cleaned_data.get('useremail')
        password = cleaned_data.get('password')

        if useremail and password:
            user = User.objects.get(useremail=useremail)
            if not check_password(password, user.password):
                self.add_error('password', '비밀번호가 틀립니다.')
            else:
                self.user_id = user.id

이부분을 이해하려면 일단 super()가 무엇인지 알아야하는데 이건 오버라이딩의 개념을 알고 있어야 합니다. 아래의 사이트를 참고하시면 됩니다.

 

[Python] 파이썬 super 기초 개념 및 예제

먼저 super를 사용하기전 상속, 오버라이딩 의 개념이 잡혀있어야 이해하기 쉽습니다. (상속, 오버라이딩 클릭시 페이지 이동) 개념 super() - 자식 클래스에서 부모클래스의 내용을 사용하고 싶을

rednooby.tistory.com

clean함수는 다음과 같은 기능을 합니다. 

-User모델에서 기존의 테이블을 불러와 폼에 들어온 데이터와 비교작업

-각종 예외처리에 대한 에러작업

form의 코드를 보면 기존의 view에서 사용하던 기능들을 가져와 처리해줌으로써 view의 코드들이 직관적으로 비즈니스 기능들만 수행하도록 하는 역할을 한다는걸 알수 있습니다.

 

-views.py

from .forms import LoginForm

def login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            request.session['user'] = form.user_id
            return redirect('/')
    else:
        form = LoginForm()
    return render(request, 'login.html', {'form': form})

기존의 views.py의 코드와는 다르게 많이 간단해진걸 알수가 있습니다. 여기서 궁금한것은 is_valid()였습니다. 

    def is_valid(self):
        """Return True if the form has no errors, or False otherwise."""
        return self.is_bound and not self.errors

is_valid함수를 찾아보니 위와 같이 에러가 없을경우 참값을 반환한다는 걸 알수 있습니다. 즉, forms.py의 유효성 검사를 모두 거쳐야지 다음의 조건을 수행하도록 해준다는 함수임을 알수 있습니다. 

 

2. 마무리 멘트

이번 form은 사실 너무 이해하기가 힘든 챕터였습니다. 오버라이딩의 개념도 제대로 이해하지 못했고 is_valid와 clean()등 이게 뭘뜻하는지 내장된 함수인지 조차 잘모르겠어서 난해했는데 하나하나 파고 들어보니 이해가 갔고 다시한번 파이썬 기본문법의 중요성을 알수있는 챕터였다고 느껴집니다. 한가지 팁을 알려드리면 이게 장고에 내장되어있는 기능인지 확인해보는 방법은 아래의 이미지와 같이 헷갈리는 변수에 오른쪽 클릭을하고 Go to Definition을 클릭하면 그함수 또는 클래스가 어떻게 이루어져있는지 보여주니 보고 참고하셔서 개발하시면 될겁니다!! 감사합니다:)

728x90
728x90

미리보는 프로젝트 완성코드 -->https://github.com/leeceo97/python_community

 

이번 유저 회원가입 로그인 기능을 만들기전 알아야 할것은 세션과 쿠키의 개념입니다. 

아래의 사이트에 자세히 설명이 나와있으니 반드시! 이해하고 구현을 해주셨으면 합니다.

 

쿠키(Cookie) 그리고 세션(Session)

 

nesoy.github.io

1. 템플릿 구성

장고는 서버언어로써 눈에 보이는 프론트엔드 영역이 아닌 백엔드 영역입니다. 그래서 눈에 보이는 영역인 프론트엔드부분은 templates폴더를 따로 만들어 관리합니다. MVC패턴중 V를 담당한다고 알아주시면 될것 같습니다.

 

accounts폴더안에 templates폴더를 만드신 후 안에 register.html, login.html, base.html파일을 만들어주세요.

그럼 위와 같이 파일이 구성될것이고 각파일의 소스는

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/static/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>
    <title>Document</title>
</head>

<body>
    <div class="container">
        {% block contents %}
        
        {% endblock %}
    </div>
</body>
</html>

register.html

{% extends "base.html" %}
{% block contents %}
        <div class="row mt-5">
            <div class="col-12 text-center">
                <h1>회원가입</h1>
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-12">
                {{ error }}
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-12">
                <form method="POST" action=".">
                    {% csrf_token %}
                    <div class="mb-3">
                        <label for="username" class="form-label">사용자 이름</label>
                        <input type="text" class="form-control" id="username" placeholder="사용자 이름" name="username">
                    </div>
                    <div class="mb-3">
                        <label for="useremail" class="form-label">사용자 이메일</label>
                        <input type="text" class="form-control" id="useremail" placeholder="사용자 이메일" name="useremail">
                    </div>
                    <div class="mb-3">
                        <label for="password" class="form-label">비밀번호</label>
                        <input type="password" class="form-control" id="password" placeholder="비밀번호" name="password">
                    </div>
                    <div class="mb-3">
                        <label for="re_password" class="form-label">비밀번호</label>
                        <input type="password" class="form-control" id="re_password" placeholder="비밀번호 확인" name="re_password">
                    </div>
                    <button type="submit" class="btn btn-primary">등록</button>
                </form>
            </div>
        </div>
        {% endblock %}

login.html

{% extends "base.html" %}
{% block contents %}
        <div class="row mt-5">
            <div class="col-12 text-center">
                <h1>로그인</h1>
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-12">
                {{ error }}
            </div>
        </div>
        <div class="row mt-5">
            <div class="col-12">
                <form method="POST" action=".">
                    {% csrf_token %}
                    <div class="mb-3">
                        <label for="useremail" class="form-label">사용자 이메일</label>
                        <input type="text" class="form-control" id="useremail" placeholder="사용자 이메일" name="useremail">
                    </div>
                    <div class="mb-3">
                        <label for="password" class="form-label">비밀번호</label>
                        <input type="password" class="form-control" id="password" placeholder="비밀번호" name="password">
                    </div>
                    <button type="submit" class="btn btn-primary">로그인</button>
                </form>
            </div>
        </div>
{% endblock %}

위에서 의문점이 base.html이 무엇이냐?라는 생각이 들수 있을겁니다.

웹페이지를 제작하다보면 헤더 부분과 푸터 부분의 디자인은 똑같은 경우가 많습니다. 그리고 지금처럼 부트스트랩을 연결 코드를 파일하나하나 쓰기에는 귀찮기도 하고 이때 중복되는 코드들은 base.html에 적어주신뒤 바뀌는 내용 즉 body부분은 {% block contents %} ~ {% endblock %}로 처리한뒤 다른 html 파일에서 {% extends "base.html" %}로 임포트 해주시고 {% block contents %} ~ {% endblock %}안에 넣어주시면 되는 구조입니다. 

 

그리고 부트스트랩 코드를 이용하기 위해서는 2가지 방법이 있습니다. html head부분에 부트스트랩 주소를 임포트 하거나 부트스트랩 파일을 다운받아 static폴더에 저장하여 사용하는 방법이 있는데 이번 프로젝트는 후자의 방법을 사용하겠습니다. 아래의 사이트에 접속해 마음에 드는 스타일을 선택한뒤 다운 받아 주시길 바랍니다.

 

Bootswatch: Free themes for Bootstrap

Customizable Changes are contained in just two SASS files, enabling further customization and ensuring forward compatibility.

bootswatch.com

루트 위치에 static 폴더를 생성후 안에 다운받은 css파일을 넣어주세요.

base.html 파일의 head부분 소스를 보시면 

<link rel="stylesheet" href="/static/bootstrap.min.css">

이부분이 있는데 static 파일의 css파일을 임포트 한걸 알수 있습니다.

 

이후, community/setting.py로 이동하셔서

import os

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

css파일 즉, 스타일 소스들을 모아둔 파일을 바라볼수 있는 위치를 static으로 연결하겠다는 의미입니다. 위의 소스를 추가해 주시면 프론트 엔드부분의 준비는 끝이 났습니다.

 

2. url연결

일단 accounts/views.py에 아래의 코드를 추가해주세요.

from django.shortcuts import render
from django.http import HttpResponse

def home(request):
    return HttpResponse("Hello!")

def register(request):
    return render(request, 'register.html')

def login(request):
    return render(request, 'login.html')

def logout(request):
    pass

간단한게 url에 템플릿을 연결시키는 코드를 테스트로 작성한거니 설명은 넘어가겠습니다.

 

community/urls.py

from django.contrib import admin
from django.urls import path, include
from accounts.views import home

urlpatterns = [
    path('', home),
    path('admin/', admin.site.urls),
    path('accounts/', include('accounts.urls')),
]

여기서 include는 accounts.urls에 연결된 url은 앞에 accounts를 포함한 url이라는 뜻입니다. 쉽게 말해 accounts.urls에 연결된 url이 test1, test2가 있다면 각각의 url에 접속하기 위해서는 accounts/test1, accounts/test2로 접속을 해야한다는 의미입니다.

 

마지막으로, accounts폴더에 urls.py를 생성한후 아래의 코드를 추가해주세요.

from django.urls import path
from .views import register, login, logout
urlpatterns = [
    path('register/', register, name='register'),
    path('login/', login, name='login'),
    path('logout/', logout, name='logout'),
]

임포트한 views.py에서 생성했던 register, login, logout 함수를 임포트해준뒤 각각을 연결해준것입니다. name은 나중에 템플릿이나 view에서 유용하게 사용 될것입니다. 

 

3. 마무리 멘트

글이 생각보다 길어져서 로그인 파트는 총 3파트로 나누어 포스팅 하겠습니다. 처음 하시는 분이라면 욕심내서 하루에 다하려고 하시기 보다는 1~2개의 글을 하루에 마스터 한다는 생각으로 따라치기만 하기보단 코드를 몸으로 익히는 연습을 해보시는게 좋을것 같습니다! 처음에도 말했지만 중간에 포기하지말고 오류가 뜨더라도 해결하기 위해서 구글링 하는 습관 들여보시는게 좋을겁니다! 화이팅입니다!! 감사합니다:)

728x90

+ Recent posts