728x90

링크드리스트(LinkedList)

1.구조

  • Hash Table: 키(key)에 데이터(value)를 저장하는 데이터 구조
    • key를 통해 바로 데이터를 받아올 수 있으므로, 속도가 획기적으로 빨라짐
    • 파이썬 딕셔너리 타입이 해시테이블의 예(key를 가지고 바로 데이터(value)를 꺼내옴)
    • 보통 배열로 미리 hash table 사이즈만큼 생성 후에 사용(공간과 탐색 시간을 맞바꾸는 기법 -> 해쉬테이블의 공간을 늘림으로써 충돌로 인한 추가적인 자료구조 알고리즘을 만들지 않기 때문)
    • 단, 파이썬에서는 해쉬를 별도 구현할 이유가 없음 - 딕셔너리 타입을 사용하면 되기 때문

2.알아둘 용어

  • 해쉬(hash): 임의 값을 고정 길이로 변환하는것
  • 해쉬 테이블: 키 값의 연산에 의해 직접 접근이 가능한 데이터 구조
  • 해싱함수: key에 대해 산술 연산을 이용해 데이터 위치를 찾을수 있는 구조
  • 해쉬값 또는 해쉬 주소: key를 해싱함수로 연산해서, 해쉬 값을 알아내고, 이를 기반으로 해쉬 테이블에서 해당 key에 대한 데이터 위치를 일관성있게 찾을 수 있음
  • 슬롯: 각각의 데이터를 저장할 수 있는 공간
  • 저장할 데이터에 대해 key(입력값)를 추출할 수 있는 별도 함수도 존재할 수 있음
  • 배열보다 해쉬테이블이 좋은 이유: 검색할 때 배열같은경우는 입력값에 대해 처음부터 확인을 해야하지만 해쉬테이블은 입력값을 통해서 바로 검색을 하여 가져올 수 있기 때문에 더 많이 쓰이게 된다.

3.장단점과 주요 용도

  • 장점
    • 데이터 저장/읽기 속도가 빠르다.(ex. 검색 속도)
    • 해쉬는 키에 대한 데이터가 있는지(중복) 확인이 쉽다.
  • 단점
    • 일반적으로 저장공간이 좀더 많이 필요하다.
    • 여러 키에 해당하는 주소가 동일할 경우 충돌을 해결하기 위한 별도의 자료구조가 필요하다
  • 주요용도
    • 검색이 많이 필요한 경우
    • 저장, 삭제, 읽기가 빈번한 경우
    • 캐시구현시(중복확인이 쉽기 때문)

4.파이썬에서의 해시 테이블의 활용 예 - 딕셔너리(Dictionary)

5.직접 해쉬테이블 구현해보기

hash_table = list([0 for i in range(8)])

def get_key(data): # 전달 받은 데이터를 암호화
    return hash(data)

def hash_function(key): # 특정 고윳값으로 키값 반환
    return key % 8

def save_data(data, value): # hash_function으로 받은 키값으로 데이터 저장
    hash_address = hash_function(get_key(data))
    hash_table[hash_address] = value
    
def read_data(data): # 전달받은 데이터를 기반으로 반환받은 키값을 통해 데이터 출력
    hash_address = hash_function(get_key(data))
    return hash_table[hash_address]

여기서 한가지 의문점이 든다. 그렇다면 전달받은 데이터를 기반으로 키값을 만들어내는데 이때 같은 키값이 나오게 된다면 이를 어떻게 처리하고 저장이 될까? 이때 해쉬테이블의 가장 큰 문제점이 나온다. 바로 충돌(collison)이다. 그래서 해쉬테이블은 따로 충돌알고리즘을 구현해야하는데 대표적으로 2가지 방식이 있다.

-Chaining 기법

  • 개방 해슁 또는 Open Hashing 기법 중 하나: 해쉬 테이블 저장공간 외의 공간을 활용하는 기법
  • 충돌이 일어나면, 링크드 리스트라는 자료 구조를 사용해서, 링크드 리스트로 데이터를 추가로 뒤에 연결시켜서 저장하는 기법
hash_table = list([0 for i in range(8)])

def get_key(data):
    return hash(data)

def hash_function(key):
    return key % 8

def save_data(data, value):
    index_key = get_key(data)
    hash_address = hash_function(index_key)
    if hash_table[hash_address] != 0: # 저장할 공간에 이미 데이터가 존재할때
        for index in range(len(hash_table[hash_address])):
            if hash_table[hash_address][index][0] == index_key:
                hash_table[hash_address][index][1] = value
                return
        hash_table[hash_address].append([index_key, value])
    else:
        hash_table[hash_address] = [[index_key, value]]
    
def read_data(data):
    index_key = get_key(data)
    hash_address = hash_function(index_key)

    if hash_table[hash_address] != 0:
        for index in range(len(hash_table[hash_address])):
            if hash_table[hash_address][index][0] == index_key:
                return hash_table[hash_address][index][1]
        return None
    else:
        return None

-Linear Probing 기법

  • 폐쇄 해슁 또는 Close Hashing 기법 중 하나: 해쉬 테이블 저장공간 안에서 충돌 문제를 해결하는 기법
  • 충돌이 일어나면, 해당 hash address의 다음 address부터 맨 처음 나오는 빈공간에 저장하는 기법
    • 저장공간 활용도를 높이기 위한 기법
hash_table = list([0 for i in range(8)])

def get_key(data):
    return hash(data)

def hash_function(key):
    return key % 8

def save_data(data, value):
    index_key = get_key(data)
    hash_address = hash_function(index_key)
    if hash_table[hash_address] != 0:
        for index in range(hash_address, len(hash_table)):
            if hash_table[index] == 0:
                hash_table[index] = [index_key, value]
                return
            elif hash_table[index][0] == index_key:
                hash_table[index][1] = value
                return
    else:
        hash_table[hash_address] = [index_key, value]

def read_data(data):
    index_key = get_key(data)
    hash_address = hash_function(index_key)
    
    if hash_table[hash_address] != 0:
        for index in range(hash_address, len(hash_table)):
            if hash_table[index] == 0:
                return None
            elif hash_table[index][0] == index_key:
                return hash_table[index][1]
    else:
        return None

+) 위와 같은 해결기법을 통해 해결가능하지만 근본적으로 빈번한 충돌을 방지하기 위해서는 해쉬테이블의 저장공간을 애초에 많이 생성해주면 된다!

728x90
728x90

사실 이번 코딩테스트 준비 파트를 기초부터 들어가게 된 원인이 된 녀석이다... 면접때 코딩테스트 당시 풀었던 코드에대해 시간복잡도를 여쭤보셨는데 이부분에대한 정의가 안되어있어서 대답조차 못했기때문...ㅎ... 그리어렵진 않다.

알고리즘 복잡도 표현방법

1.알고리즘 복잡도 계산이 필요한 이유

하나의 문제를 푸는 알고리즘은 다양함

  • 정수의 절대값 구하기(1,-1)
    • 방법1:정수값을 제곱한 값에 다시 루트 씌우기
    • 방법2:정수가 음수 인지 확인해서, 음수일 때만 -1을 곱하기
    • 즉, 다양한 알고리즘 중 어느 알고리즘이 더좋은지를 분석하기 위해, 복잡도를 정의하고 계산함

2.알고리즘 복잡도 계산항목

  • 시간 복잡도: 알고리즘 실행속도
  • 공간 복잡도: 알고리즘이 사용하는 메모리 사이즈
  • 가장 중요한 시간 복잡도를 이해하고 계산할 수 있어야함 -> 공간복잡도는 아무래도 비중이적음

알고리즘 시간 복잡도의 주요 요소는 반복문이다. -> 입력의 크기가 커지면 커질수록 알고리즘 수행시간이 증가함

알고리즘 성능 표기법

  • Big O (빅-오) 표기법: O(N)
    • 알고리즘 최악의 실행 시간
    • 가장 많이/일반적으로 사용
    • 아무리 최악의 상황이라도, 이정도의 성능을 보장한다는 의미
  • Ω (오메가) 표기법: Ω(N)
    • 오메가 표기법은 알고리즘 최상의 실행 시간
  • Θ (세타) 표기법: Θ(N)
    • 세타 표기법은 알고리즘 평균 실행시간을 표기
  • 시간 복잡도 계산은 반복문이 핵심 요소이며, 계산 표기는 최상, 평균, 최악 중, 최악의 시간인 Big-O표기법을 중심으로 익히면 됨

3.Big-O 표기법

  • 빅 오 표기법, Big-O 표기법 이라고도 부름
  • O(입력값)   
  • 입력 n에 따라 결정되는 시간 복잡도 함수
  • O(1), O( 𝑙𝑜𝑔𝑛 ), O(n), O(n 𝑙𝑜𝑔𝑛 ), O( 𝑛2 ), O( 2𝑛 ), O(n!)등으로 표기함
  • 입력 n 의 크기에 따라 기하급수적으로 시간 복잡도가 늘어날 수 있음
  • O(1) < O( 𝑙𝑜𝑔𝑛 ) < O(n) < O(n 𝑙𝑜𝑔𝑛 ) < O( 𝑛2 ) < O( 2𝑛 ) < O(n!)
  • 참고: log n 의 베이스는 2 -  𝑙𝑜𝑔2𝑛 
  • 단순하게 입력 n에 따라, 몇번 실행이 되는지를 계산
  • 표현식에 가장 큰 영향을 미치는 n 의 단위로 표기 -> 상수회 무시
  • 예시

-무조건 2회(상수회)실행: O(1)

if n>10:
	print(n)

-n에따라, n번, n+10번 또는 10n+10번등 실행: O(n)

variable = 1
for num in range(3):
	for index in range(n):
		print(index)
# 따져보면 2n+2회이지만 결국 시간복잡도는 2n

-n에 따라, 𝑛2번 , 𝑛2+1000번,  𝑛2+1번실행한다: O(𝑛2)

variable = 1
for i in range(300):
	for num in range(n):
		for index in range(n):
			print(index)
  • 성능 판단 예시: 1부터 n까지의 합을 구하는 알고리즘
# 입력값 n을 정렬하여 모두 합
def sum_all(n):
    total = 0
    for num in range(1, n + 1):
        total += num
    return total
#시간복잡도: n

# 덧셈 알고리즘 이용
def sum_all(n):
    return int(n * (n + 1) / 2)
#시간복잡도: 1

같은 문제이더라도 어떤 알고리즘을 이용하냐에 따라 시간 복잡도가 다르게 나옴 즉, 시간복잡도가 1인 2번째 알고리즘의 성능이 더욱 좋다.

728x90
728x90

링크드리스트(LinkedList)

1.구조

  • 연결리스트라고도 함
  • 배열은 순차적으로 연결된 공간에 데이터를 나열하는 데이터 구조
  • 링크드 리스트는 떨어진 곳에 존재하는 데이터를 화살표로 연결해서 관리하는 데이터 구조
  • 링크드 리스트 기본 구조와 용어
    • 노드(Node): 데이터 저장 단위(데이터, 포인터)로 구성
    • 포인터(Pointer): 각 노드 안에서, 다음이나 이전의 노드와의 연결 정보를 가지고 있는 공간
  • 배열은 미리 특정한 공간을 예약해두고 그공간을 쓰는구조이지만, 이에반해 링크드 리스트는 미리 예약을 해두지 않고 필요할때마다 그공간을 추가할수 있는 구조이다.
  • 보통 파이썬에서 링크드 리스트를 구현시, 파이썬 클래스를 활용 -> 파이썬 객체지향 문법의 이해가 필요함

2.장단점

  • 장점
    • 미리 데이터 공간을 할당하지 않아도됨 -> 배열은 미리 데이터 공간을 할당 해야함
  • 단점
    • 연결을 위한 별도 데이터 공간이 필요하므로, 저장공간 효율이 높지 않음
    • 연결정보를 찾는 시간이 필요하므로 접근 속도가 느림
    • 중간 데이터 삭제시, 앞뒤 데이터 연결을 재구성해야 하는 부가적인 작업이 필요

3.직접 링크드 리스트 구현해보기

class Node:
    def __init__(self, data, next=None):
        self.data = data
        self.next = next
    
class NodeMgmt:
    def __init__(self, data):
        self.head = Node(data)
        
    def add(self, data):
        if self.head == '':
            self.head = Node(data)
        else:
            node = self.head
            while node.next:
                node = node.next
            node.next = Node(data)
        
    def desc(self):
        node = self.head
        while node:
            print (node.data)
            node = node.next
    
    def delete(self, data):
        if self.head == '': 
            print ("해당 값을 가진 노드가 없습니다.")
            return
        
        if self.head.data == data: # 삭제하는 데이터가 헤드일경우
            temp = self.head
            self.head = self.head.next
            del temp
        else: # 삭제하는 데이터가 중간,맨뒤일 경우
            node = self.head
            while node.next:
                if node.next.data == data:
                    temp = node.next
                    node.next = node.next.next
                    del temp
                    return
                else:
                    node = node.next
  • head의 경우, 리스트의 첫시작을 알기위해 임의로 지정한 것
  • 링크드리스트를 다시금 학습하며 개념을 파악할수 있었다. 그리고 생각보다 구현하는데 애를먹어 객체지향적 프로그래밍에 대한 학습또한 할수 있었던 파트다.
728x90
728x90

TODAY I LEARNED


장고 백엔드 스터디 학습차 인프런의 "작정하고 장고"강의를 구입하였다. 

 

작정하고 장고! Django로 Pinterest 따라만들기 : 바닥부터 배포까지 - 인프런 | 강의

Python의 대표 웹 프레임워크 django를 이용해 Pinterest 특유의 카드형 레이아웃 디자인을 본딴 웹서비스 구현해봅니다., django , docker 실전 개발을 통해 한 단계 높은 개발자가 되어보세요! 📣 확인해

www.inflearn.com

일단 이 작정하고 장고를 7월까지 끝내고 8월에 drf와 함께 프로젝트를 만들기로 하였다. 시간에 쫓겨 오늘은 듣지 못했고 스터디 시작전 간단하게 파이썬 문법을 다시 한번 보고 가자는 말과 함께 토요일까지 위키 독스의 파이썬 300제 문제를 싹풀고 시작하기로 하였다.

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

오늘은 120번까지 풀었다. 잠시 공부한걸 정리하자면

 

leeceo97/python

파이썬 기초 문법부터 알고리즘 풀이까지 진행하는 레포지토리입니다. . Contribute to leeceo97/python development by creating an account on GitHub.

github.com

- sep(), end()

year = 2021
month = 07
date = 01
print(year,month,date,sep='-')
# 2021-07-01 과 같이 출력된다.
# sep은 선언자를 구분할때 구분자를 삽입하여 특정지어주는 역할을한다.

print('My name is corin.',end='\n');print('I\'m 21 years old.')
# My name is corin.
# I'm 21 years old. 와 같이 출력된다.
# ;는 한줄에 여러 명령을 사용할때 사용되며
# end는 문장을 출력하고 마지막에 무엇을 쓰고 끝낼지 정해주는 역할을 한다.

-f를 이용한 출력, 포매팅

name = 'Corin'
age = 25
print(f'My name is {name}. I\'m {age}years old.')
# My name is Corin. I'm 25 years old. 와 같이 출력된다.
# f를 이용한 출력은 파이썬 최신버전부터 가능한걸로 알고 있다.

print('My name is %s. I\'M %d years old.'%(name, age))
# 위와 같이 출력된다.
# %s는 파이썬에서 문자열, %d는 정수형을 나타내며 형식을 위와 같이 쓰면된다.

-replace, split, strip, join, sort

string = "aBcDeFg"
string_replace = string.replace('c','C')
print(string_replace)
# aBCDeFg와 같이 출력되며 replace는 문자열내 요소를 변경할때 쓰인다.

time = "2021-07-01"
year, month, day = time.split('-')
print(year, month, day)
# 2021 07 01과 같이 출력된다.
# split은 문자열내 특정 구분자나 문자,문자열을 가지고 분리해주는 역할을 한다.

string = "     abc"
string_strip = string.strip()
print(string_strip)
# abc와 같이 출력된다. strip은 공백을 없애주는 역할을한다.

interest = ['삼성전자', 'LG전자', 'Naver', 'SK하이닉스', '미래에셋대우']
print("/".join(interest))
# 삼성전자/LG전자/Naver/SK하이닉스/미래에셋대우와 같이 출력된다.
# join은 데이터 사이사이의 특정 규칙을 삽입해주는 역할을 한다.

num = 3,2,5,6,1,4
num_sort = num.sort()
print(num_sort)
# 1,2,3,4,5,6과 같이 출력된다.
# sort는 정렬해주는 역할을 한다.

# upper은 모든 문자를 대문자로
# lower은 모든 문자를 소문자로
# capitalize는 문자열중 첫문자를 대문자로 바꿔준다.

-list관련

interest = ['삼성전자', 'LG전자', 'Naver']
interest.append('배민')
print(interest)
# ['삼성전자', 'LG전자', 'Naver', '배민']과 같이 출력된다.
# append는 리스트의 마지막에 데이터를 추가하는데 사용된다.

interest.insert(1,'요기요')
print(interest)
#['삼성전자', '요기요','LG전자', 'Naver','배민']와 같이 출력된다.
# insert는 내가 지정한 위치에 데이터를 추가해준다.

del interest[1]
print(interest)
# ['삼성전자', 'LG전자', 'Naver', '배민']
# del은 지정한 위치의 데이터를 삭제해준다.

-튜플

num = 1,2,3,4,5,6
print(type(num))
# tuple이 나온다.
# tuple은 괄호가 없더라도 튜플로 선언이 가능하다.

# 튜플과 리스트는 수정 여부 말고 거의 비슷하다.

-star expression

scores = [8.8, 8.9, 8.7, 9.2, 9.3, 9.7, 9.9, 9.5, 7.8, 9.4]
*valid_score, _,_ = scores
print(valid_score)
# [8.8, 8.9, 8.7, 9.2, 9.3, 9.7, 9.9, 9.5] 과 같이 출력된다.

scores = [8.8, 8.9, 8.7, 9.2, 9.3, 9.7, 9.9, 9.5, 7.8, 9.4]
a,b,*valid_score = scores
print(valid_score)
# [8.7, 9.2, 9.3, 9.7, 9.9, 9.5, 7.8, 9.4]

a, *valid_score, b = scores
print(valid_score)
# [8.9, 8.7, 9.2, 9.3, 9.7, 9.9, 9.5, 7.8]

# star expression은 데이터가 몇개가 저장될지 모를때 사용한다.
# _는 파이썬문법을 사용할때 필요없는 값 즉, 앞으로 사용해도 되지 않는 값일때 쓰인다.
# 실제로 값이 저장되지는 않고 자리만 차지하는 역할을 하게 된다. 유용할것이다.

- 딕셔너리 zip

date = ['09/05', '09/06', '09/07', '09/08', '09/09']
close_price = [10500, 10300, 10100, 10800, 11000]
close_table = dict(zip(date,close_price))
print(close_table)
# zip은 두개의 튜플 또는 리스트를 합쳐 딕셔너리를 만들때 사용된다.

사실 코테 문제를 풀며 쉽게만 생각하고 실제로는 구글링하며 함수를 찾아가면서 문제를 풀었는데 이번에 해보고 사실 충격아닌 충격을 먹었다. 생각보다 많이 까먹고 몰랐던 내용 투성이 였던것들 위주로 작성하였다. 

 

 

인턴 근무 1일차


내 PC에 VPN(가상사설망)을 설치하여 회사에서 발급받은 ID,PASSWORD로 VPN 연결을 하였다. 그후 원격으로 회사의 내PC에 접속하는것을 성공했다. 재택근무에 대한 준비였는데 사실 여기에 가장 많은 시간을 쓴것 같았다. VMWARE로 윈도우10을 설치한 디스크에 개발망, 인터넷망 PC 2대에 접속하였다. 그후부턴 DOORAY, TEAMUP에 가입하였다. 이 2개의 툴은 협업툴로 내가 그동안썻던 노션, 슬랙과 비슷한 느낌을 받았다. 아직까지 노션과 슬랙도 제대로 사용안해서 그런진 몰라도 노션,슬랙은 작은 규모의 협업툴이고 DOORAY, TEAMUP은 그보다 더 큰 규모의 협업에 있어 사용하기 적절한 툴로 느껴졌다. 그 외에도 앞으로 진행할 업무에대한 설명과 회사 내부망을 통한 관리 방법에 대해 배웠는데... 한번 해봐야 겠다는 생각이 들었다.. 암것두 모르겠다후.... 아직까진 가입만하고 맛보기로 툴이나 업무를 진행했기 때문에 앞으로 인턴 업무를 좀더 진행하면서 배우고 익힌것들위주로 업로드 할예정이다. 첫날이여서 바빳지만 처음 해본것들이라 신기하기도 하고 앞으로의 업무가 기대되는 그런 하루 였다.

728x90

'개발 life > 셀프 부트캠프(WIL)' 카테고리의 다른 글

TIL 5일차-2021.7.5  (0) 2021.07.06
TIL 4일차-2021.7.4  (0) 2021.07.04
TIL 3일차-2021.7.3  (0) 2021.07.03
TIL 2일차-2021.7.2(feat. VirtualBox에서 iso파일)  (0) 2021.07.02
일정표  (0) 2021.05.28
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

 

사실 댓글을 구현하는 방식은 아주 많지만 이번에 프로젝트의 목표였던 model, view, templates의 기능을 이용해 보자라는 취지에 맞게 댓글을 구현해 보겠습니다!

 

1. model구성

-boards/models.py

class Comment(models.Model):
    comment = models.TextField()
    author = models.ForeignKey('accounts.User', on_delete=models.CASCADE)
    post = models.ForeignKey('Board', null=True, blank=True, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.comment

    class Meta:
        db_table = 'comment'
        verbose_name = '댓글'
        verbose_name_plural = '댓글'

1:N모델 구성을 위해 ForeignKey로 author(글작성자), post(게시글)을 선언했습니다.

 

2. view, form구성

-boards/forms.py

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['comment']

기존의 방식보다는 좀더 편안한 방식인 ModelForm을 사용해보았는데 이건 폼이 알아서 model에 선언되어있는 comment필드에 맞춰 폼형식을 생성해준다는 것입니다. 기존의 복잡하고 귀찮던 코드들이 한껏 간결해진것이 보입니다.

ModelForm같은경우 자세한 설명은 추후 class를 이용한 view구성 프로젝트를 진행할때 제대로 다뤄보겠습니다!

 

-boards/urls.py

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

실질적으로 html파일이 존재하지는 않지만 위의 boards/comment_write/<int:board_id>로 값이 보내진다.

 

-boards/views.py

def comment_write(request, board_id):
    comment_write = CommentForm(request.POST)
    user_id = request.session['user']
    user = User.objects.get(pk=user_id)
    if comment_write.is_valid():
        comments = comment_write.save(commit=False)
        comments.post = get_object_or_404(Board, pk=board_id)
        comments.author = user
        comments.save()
    return redirect('board_detail', board_id)

comment_write가 실행되는 경우는 

1. board_detail.html에서 댓글을 입력후 댓글 작성버튼을 눌러

2. boards/comment_write/<int:board_id>로 값이 보내지면 

3. 함수실행 = 댓글저장이 이루어집니다.

 

def board_detail(request, pk):
    board = get_object_or_404(Board, pk=pk)
    comments = CommentForm()
    comment_view = Comment.objects.filter(post=pk)
    return render(request, 'board_detail.html',{'board':board, 'comments':comments, 'comment_view':comment_view})

총 두가지의 값을 반환합니다. 

1. comments --> 댓글작성시 필요한 폼

2. comment_view --> 해당 게시글에 등록되어있는 댓글

-Comment.objects.filter(post=pk)같은 경우 반환받은 게시글의 pk즉 id값에 해당하는 post라는 조건에 맞는 댓글을 가져오는것입니다.

 

3. templates구성

-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>
            <label for="tags">태그</label>
            <span id="tags" class="form-control">
                {{ board.tags.all|join:"," }}
            </span>

            <form method="POST" action="{% url 'comment_write' board.id %}">
                {% csrf_token %}
                {{ comments }}
                <input type="submit" value="댓글 입력">
            </form>
            {% for comment in comment_view %}
            <hr>
            <p>{{ comment.author }}</p>
            <p>{{ comment.comment }}</p>
            <hr>
            {% endfor %}
        </div>
        <button class="btn btn-primary" onclick="location.href='/boards/list/'">돌아가기</button>
    </div>
</div>
{% endblock %}

위와 같이 템플릿을 구성하면 됩니다.

한가지 처음 다뤄보는 패턴이 form태그에 action이였는데 action은 입력한 url에 데이터를 전송하는겁니다.

그렇기 때문에 comment_write가 수행되어 댓글을 저장하게 됩니다. 

 

4. 마무리 멘트

역시나 이번 챕터또한 새로운 패턴없이 쉽게쉽게 마무리 할수 있었습니다. 그럼 다음은 배포 챕터이므로 마지막까지 화이팅!!! 감사합니다:)

728x90
728x90

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

 

태그 기능은 게시글을 작성할때 그글이 포함되어 있는 내용을 단어로 요약한것입니다. 게시글을 작성할때마다 개수의 제한 없이 태그를 입력받을수 있고 입력받은 값을 저장하되 기존에 겹치는 태그는 늘리지 저장이아닌 불러오는 형식으로 구현하겠습니다.

 

1.model구성

-tag/models.py

from django.db import models

class Tag(models.Model):
    name =models.CharField(max_length=32, verbose_name='태그명')
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'community_tag'
        verbose_name = '태그'
        verbose_name_plural = '태그'

별다른건 없습니다!

-boards/models.py

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)
    tags = models.ManyToManyField('tag.Tag')
    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 = '커뮤니티 게시판'

기존의 Board모델에 tags를 ForeignKey로 선언해주었습니다. 기존의 방식과 같게 참조할 모델을 첫번째 인자로 입력했지만 on_delete=models.CASCADE를 입력하지 않은이유는 게시글이 지워지더라도 기존에 존재하는 태그명은 지워지지 않도록 하기위해 넣지 않았습니다.

 

tag같은경우는 별도로 tag디렉터리 안에서의 작업은 이루어지지 않고 board디렉터리의 form, view, templates에서 이뤄집니다. --> 사실 boards디렉터리 의 models.py안에 tag모델을 선언해도되지만 추후 추가될 comment 모델까지 있기때문에 가시성을 위해 따로 분리하였습니다.

 

2. view구성

-board/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 = "내용")
    tags = forms.CharField(required=False, label = "태그")

태그를 입력하지 않아도 오류가 뜨지 않게 기존의 필드 선언과는 다르게 required=False로 선언해주었습니다.

 

-boards/views.py

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)
            tags = form.cleaned_data['tags'].split(',')

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

            for tag in tags:
                if not tag:
                    continue
                _tag, _ = Tag.objects.get_or_create(name=tag)
                board.tags.add(_tag)
            
            
            return redirect('/boards/list')
    else:
        form = BoardForm()
    return render(request, 'board_write.html', {'form':form})

board.tags.add(_tag)에서 board에 _tag를 저장한다는 의미인데 그렇기 위해서는 board는 이미 저장되어 있어야 하므로board저장 코드가 먼저 실행되어야 한다. 

for tag in tags:
    if not tag:
        continue
    _tag, _ = Tag.objects.get_or_create(name=tag)
    board.tags.add(_tag)

입력받은 tags안의 값들이 기존에 존재하는지 않하는지 알기위해 반복문을 위와 같이 사용했고

처음보는 get_or_create같은 경우 정의가

get_or_create 메서드는 객체(object)를 조회할 때 유용하게 사용되는 메서드이다. 이 메서드는 (object, created) 라는 튜플 형식으로 반환을 한다. 첫번째 인자(object)는 우리가 꺼내려고 하는 모델의 인스턴스이고, 두번째 인자(created)는 boolean flag이다. 

라고 한다.

그래서 총 2개의 인자를 받게 되는데 사실상 2번째 인자같은경우 우리가 사용하지 않기때문에 두번째 인자는 '_'으로 처리하였습니다.

 

3. templates

-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 %}

-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>
            <label for="tags">태그</label>
            <span id="tags" class="form-control">
                {{ board.tags.all|join:"," }}
            </span>
        </div>
        <button class="btn btn-primary" onclick="location.href='/boards/list/'">돌아가기</button>
    </div>
</div>
{% endblock %}

이후 적절한 위치에 태그를 입력받고 보여주면 끝입니다!

 

4. 마무리 멘트

이번 챕터에서 느낀것은 장고를 잘 다루기 위해서는 파이썬 문법을 잘 다뤄야 한다고 느꼇습니다. 지금이야 튜토리얼 형식으로 간단한 코드들만 작성해서 문제 될일 없겠지만 나중에 프로젝트를 제대로 시작할때는 파이썬 문법공부가 제대로 되어있어야 한다고 느끼며 알고리즘 공부하러 가봐야겠네요!! 이번 얼마 안남았으니 끝가지 열심히!! 감사합니다:)

 

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

 

1. 프로젝트 소개

뭐 거창하게 프로젝트라고 설명할것은 없지만 모든 프로젝트에 있어 기본적이 기능들이 들어가 있는 간단한 게시판 기반 커뮤니티 사이트를 만들예정입니다. 백엔드에 초점을 맞춰 진행할 예정이기 때문에 그냥 프론트는 부트스트랩으로 제작하겠습니다.

 

2. 가상 환경 구성

cmd 창에서 위와 같이 바탕화면에서 community 이름의 프로젝트 폴더를 만들어 줍니다.

이후, 프로젝트 폴더 위치로 이동한후

 

venv란 이름의 가상환경 파일을 생성 합니다. 

venv 폴더의 scripts안에있는 activate.bat 파일을 활성화 시킴으로써 가상환경 세팅이 완료됩니다.

이후부턴 활성화된 cmd창에 code를 입력해 vscode의 터미널에서 프로젝트를 진행하면 됩니다.

 

3. 마무리 멘트

앞으로 블로그 글을따라 진행하다보면 많은 오류가 뜨고 그냥 따라치기만 한다는 생각이 들것입니다. 오류가 발생한다고 그만두는게 아닌 그걸 해결하기 위해 구글링을 해보고 코드를 고쳐 가다보면 시간낭비가 아닌 자신의 것이 될테고 따라치기만 하는게 아닌 하루에 한개의 포스팅 이라도 정복한다는 마음가짐으로 하나하나의 의미를 이해하고 기록하며 공부해가길 바랍니다!! 이건 저한테 하는 소리이기도 합니다ㅎㅎ 다들 화이팅!

728x90

+ Recent posts