python을 활용한 식물도감 앱 만들기 프로젝트
강사 소개
구일모
- 매릴랜드 주립대 항공기계공학 전공
- 현) 쿠스랩 대표
- 전) 코드스테이츠 CPO
- 전) 프리랜서 개발자
- 전) 자버, 개발자
- 전) 벡터코리아, 기술 영업
- 전) 대한항공, 무인비행기 시스템 엔지니어
- 전) 로토크, 하드웨어 테스트 연구원
- 전) 매릴랜드 주립대, 무인기 센서 연구원
강사 소개
최민주
- 현) Dencomm (덴탈 도메인 스타트업)
- AI Lab, NLP Researcher
- 전) BeringLab (기계번역 스타트업)
- NLP Researcher
- Tanalysis (특허 서비스 스타트업)
- Backend Developer
교육 목표
- 컴퓨터 및 코드, 프로그래밍의 기본 이해
- 웹 어플리케이션의 구조 이해
- 웹 어플리케이션 제작 실습
진짜 목표
프로그래밍을 통한 문제 해결, 컴퓨터적 사고방식, 프로그래밍의 재미
계획
- 컴퓨터 및 프로그래밍 소개
- 웹, 어플리케이션 소개
- 식물도감 어플리케이션 소개
- HTML, javascript, CSS 기초
- python 프로그래밍 언어 기본 및 실습
- 데이터베이스 기본 및 실습 with SQLite3
- 서버 어플리케이션 기본 및 실습 with Flask
- 클라이언트 코드 with Flask, jinja2, HTML
- 어플리케이션 구동 및 배포
- 개인 산출물 발표
pre Q&A
컴퓨터의 동작 원리와 구성요소
컴퓨터가 정보를 저장하는 원리
- 컴퓨터는 정보를 비트와 바이트 형태로 저장
- 비트(bit): 정보의 최소단위 (0 또는 1)
- 바이트(byte): 8비트로 구성된 단위
- 데이터 저장 매체:
- RAM: 휘발성 메모리, 빠른 속도
- 하드 드라이브 (HDD), SSD: 비휘발성 메모리, 대용량 저장
컴퓨터 HW 기본 구성 요소
- CPU: 컴퓨터의 두뇌, 명령어를 처리
- 메모리 (RAM): 데이터를 일시적으로 저장, 빠른 접근 속도
- 입력 장치: 키보드, 마우스 등
- 출력 장치: 모니터, 스피커, 프린터 등
컴퓨터 SW 기본 구성 요소
- 운영체제 (OS): 하드웨어와 소프트웨어를 관리, 사용자와 컴퓨터 간의 인터페이스 제공 (예: Windows, macOS, Linux)
- 응용 소프트웨어 (Application): 사용자가 특정 작업을 수행할 수 있도록 도와주는 프로그램 (예: 웹 브라우저, 워드 프로세서)
Q&A
웹사이트 작동 원리 이해
- 클라이언트와 서버 간의 통신
- 클라이언트: 웹사이트를 요청하는 어플리케이션 (컴퓨터 브라우저 혹은 스마트폰 브라우저, 스마트폰 앱 등)
- 서버: 웹사이트를 호스팅하고 요청에 응답하는 장치
웹사이트 작동 원리 이해
- 동작 순서
- 클라이언트가 웹 브라우저를 통해 url 입력
- 브라우저가 DNS 서버를 통해 해당 urldml IP 주소 찾기
- 브라우저가 서버에 HTTP/HTTPS 요청 보냄
- 서버가 요청된 웹페이지 찾아서 클라이언트에게 응답
- 브라우저가 응답 받은 웹페이지를 렌더링
브라우저의 역할과 기능 이해
- 브라우저는 웹사이트를 표시하는 데스크톱/모바일 어플리케이션 SW
- 주요 역할
- URL 통해 웹사이트/웹어플리케이션 요청
- HTML, CSS, JS 등의 웹페이지 코드를 해석하여 화면에 표시
- 쿠키 저장 및 관리
- 브라우징 히스토리 저장
- 보안 기능 제공 (e.g. SSL/TLS 암호화)
어플리케이션이란
- 어플리케이션(Application)은 특정 작업을 수행하기 위해 설계된 소프트웨어 프로그램
- OS 위에서 돌아감
- 사용자는 애플리케이션을 통해 다양한 기능을 수행할 수 있습니다 (예: 문서 작성, 웹 탐색, 게임 등)
서버와 클라이언트의 개념 이해
- 클라이언트(Client): 서비스를 요청하는 장치 또는 프로그램 (예: 웹 브라우저, 모바일 앱)
- 서버(Server): 요청된 서비스를 제공하는 장치 또는 프로그램 (예: 웹 서버, 데이터베이스 서버)
- 서버-클라이언트 모델:
- 클라이언트는 요청을 보내고, 서버는 요청을 처리하여 응답을 반환
- 이 모델은 웹 애플리케이션, 이메일, 파일 공유 등에서 사용
서버와 클라이언트 예시
- 웹 어플리케이션
- 클라이언트: 사용자의 웹 브라우저
- 서버: 웹 페이지와 데이터를 제공하는 웹 서버
- 이미지
모바일 vs 데스크톱 어플리케이션 차이점 이해
- 모바일 앱
- 스마트폰 및 태블릿에서 실행
- 터치 인터페이스 기반
- GPS, 카메라, 가속도계 등 하드웨어와 통합된 기능 제공
- 데스크톱 앱
- PC 및 노트북에서 실행
- 키보드와 마우스 인터페이스 기반
- 더 큰 화면과 강력한 하드웨어 성능 활용
vs 웹 어플리케이션 차이점 이해
- 웹 앱
- 데스크톱, 태블릿, 모바일에 있는 브라우저 위에서 돌아감
- 웹 사이트 -> 웹 어플리케이션으로 발전
- 최근 브라우저에서 방문하는 대부분의 사이트는 사실 웹 어플리케이션일 수 있음
- 웹사이트 vs 웹 어플리케이션 차이
모바일 어플리케이션 예시
- 카카오톡, 틱톡, 유투브, 인스타그램, 네이버맵 등
- 특징
- 간편한 터치 조작
- 푸시 알림 기능
- 위치 기반 서비스
데스크톱 어플리케이션 예시
- MS Word, 한컴 한글, 어도비 포토샵, v3백신, 알집, 브라우저, 어도비 프리미어 등
- 특징
- 강력한 기능과 성능 (게임 전용 PC 등)
- 파일 관리 및 저장 용이
- 입출력 장치가 보통 분리되어있음
웹 어플리케이션
- 웹 애플리케이션(Web Application)은 웹 브라우저를 통해 접근할 수 있는 소프트웨어 프로그램
- 인터넷을 통해 서버에 호스팅되며, 사용자는 웹 브라우저를 통해 서비스를 이용
- e.g. youtube, facebook, gmail
웹 어플리케이션 구조
- 프론트엔드(Front-End): 사용자 인터페이스를 담당 (HTML, CSS, JavaScript)
- 백엔드(Back-End): 서버 로직과 데이터 처리를 담당 (서버, 애플리케이션 로직)
- 데이터베이스(Database): 데이터 저장소 (SQL, NoSQL 데이터베이스)
웹 애플리케이션의 작동 과정
- 사용자가 웹 브라우저를 통해 웹 애플리케이션에 접근
- 브라우저가 서버에 HTTP/HTTPS 요청을 전송
- 서버가 요청을 처리하고 데이터베이스와 상호작용
- 서버가 처리된 데이터를 포함한 응답을 브라우저에 전송
- 브라우저가 응답을 받아 사용자에게 웹페이지를 렌더링
클라이언트-서버-데이터베이스 상호작용 이해
- 클라이언트(Client):
- 사용자 인터페이스 제공
- 서버에 HTTP/HTTPS 요청 전송
- 서버(Server):
- 비즈니스 로직 처리
- 데이터베이스와 상호작용하여 데이터 요청 및 저장
- 클라이언트에 HTTP/HTTPS 응답 전송
- 데이터베이스(Database):
- 데이터 저장 및 관리
- 서버의 데이터 요청 처리
클라이언트와 서버 간의 예시 상호작용
- 예시: 사용자가 로그인하는 과정
- 사용자가 로그인 폼에 정보를 입력하고 '로그인' 버튼 클릭
- 브라우저가 서버에 로그인 요청 전송
- 서버가 데이터베이스에서 사용자 정보 확인
- 서버가 인증 결과를 브라우저에 응답
- 브라우저가 사용자에게 로그인 결과 표시
데이터베이스와 서버 간의 예시 상호작용
- 예시: 사용자가 게시글을 작성하는 과정
- 사용자가 게시글 작성 폼에 내용을 입력하고 '작성' 버튼 클릭
- 브라우저가 서버에 게시글 작성 요청 전송
- 서버가 데이터베이스에 게시글 데이터 저장
- 서버가 게시글 작성 완료 응답을 브라우저에 전송
- 브라우저가 사용자에게 게시글 작성 완료 메시지 표시
식물도감 웹 어플리케이션 구조
Codespace 에서 빌린 컴퓨터
여러분들 컴퓨터
산출물 시연
HTML 개요
- HTML (HyperText Markup Language)
- 웹페이지의 구조를 정의함
- 요소와 태그를 사용하여 콘텐츠를 구성함
- HTML 문서의 기본 구조
<!DOCTYPE html>
-
<html>
,<head>
,<body>
태그 포함
HTML의 주요 태그
- 주요 태그 소개
-
<h1>
~<h6>
: 헤딩 태그 -
<p>
: 문단 태그 -
<a>
: 링크 태그 -
<img>
: 이미지 태그
<!DOCTYPE html>
<html>
<head>
<title>Sample HTML</title>
</head>
<body>
<h1>This is a Heading</h1>
<p>This is a paragraph.</p>
<a href="https://www.example.com">This is a link</a>
<img src="image.jpg" alt="Sample Image">
</body>
</html>
HTML 리스트 및 테이블
- 리스트 태그
-
<ul>
,<ol>
,<li>
-
- 테이블 태그
-
<table>
,<tr>
,<td>
,<th>
-
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<ol>
<li>First</li>
<li>Second</li>
</ol>
<table>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<tr>
<td>John</td>
<td>30</td>
</tr>
</table>
CSS 개요
- CSS (Cascading Style Sheets)
- 웹페이지의 스타일을 정의함
- HTML 요소의 레이아웃, 색상, 폰트 등을 지정함
- CSS의 기본 문법
- 선택자, 속성, 값으로 구성
CSS 선택자와 속성
- 주요 선택자
- 태그 선택자, 클래스 선택자, ID 선택자
- 주요 속성
-
color
,font-size
,margin
,padding
-
/* 태그 선택자 */
p {
color: blue;
}
/* 클래스 선택자 */
.container {
margin: 0 auto;
width: 80%;
}
/* ID 선택자 */
#header {
background-color: #f8f8f8;
padding: 10px;
}
JavaScript 개요
- JavaScript
- 동적이고 인터랙티브한 웹페이지를 구현함
- 클라이언트 사이드 스크립트 언어
// 변수 선언
let name = "John";
const age = 30;
// 함수 선언
function greet() {
console.log("Hello, " + name);
}
// 함수 호출
greet();
예시 javascript 코드
- Bullet One
- Bullet Two
- Bullet Three
// 요소 선택
const button = document.getElementById('myButton');
// 이벤트 핸들러 추가
button.addEventListener('click', function() {
alert('Button clicked!');
});
// 요소 생성
const newElement = document.createElement('div');
newElement.textContent = "Hello, World!";
document.body.appendChild(newElement);
기타
- Java와 javascript는 다른 언어
- javascript 는 최근 모던 웹 개발 쪽에서 가장 hot 한 언어
- 과거 javascript는 개발 프로그래밍 언어 취급 받지 못함
- nodejs 등장 및 브라우저 웹앱 고도화로 굉장히 핫해짐
파이썬이란
-
파이썬(Python)은 1990년 암스테르담의 귀도 반 로섬(Guido van rossum)이 개발한 인터프리터 언어이다. 귀도는 파이썬이라는 이름을 자신이 좋아하는 코미디 쇼인 ‘몬티 파이썬의 날아다니는 서커스(Monty python's flying circus)’에서 따왔다고 한다.
인터프리터 언어란 소스 코드를 한 줄씩 해석한 후 그때그때 실행해 결과를 바로 확인할 수 있는 언어를 말한다.
출처: 점프투파이썬 위키독스, 박응용
파이썬의 특징
- 인간다운 언어
- 문법이 쉬워 빠르게 배울 수 있음
- 무료 (오픈소스)
- 개발 속도가 빠름
if 4 in [1,2,3,4]: print("4가 있습니다")
languages = ['python', 'perl', 'c', 'java']
for lang in languages:
if lang in ['python', 'perl']:
print("%6s need interpreter" % lang)
elif lang in ['c', 'java']:
print("%6s need compiler" % lang)
else:
print("should not reach here")
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
파이썬을 돌려보자
- 파이썬을 실행하는 방법은 2가지
- 내 컴퓨터에서 돌리기
- 남의 컴퓨터에서 돌리기
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
윈도우에서 파이썬 설치
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
github codespace
1. github 가입
2. vscode 설치
3. codespace 실행/연결
일단 아래 코드를
실행시켜봅시다
print('hello world')
python 기초를
배워봅시다
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
# 정수형 변수 선언 및 출력
int_var = ?
# 부동 소수점형 변수 선언 및 출력
float_var = ?
# 문자열형 변수 선언 및 출력
str_var = ?
# 불리언형 변수 선언 및 출력
bool_var = ?
# 각 변수의 타입 출력
?
1. 변수를 선언하고 각 데이터 타입 (정수, 부동 소수점, 문자열, 불리언)의 값을 할당하세요.
2. 각 변수의 타입을 출력하세요.
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
# 사용자로부터 숫자 입력 받기
num = int(input("숫자를 입력하세요: "))
# if를 사용하여 사용자로부터 받은 숫자
# 입력값에 대해 양수, 음수, 0 판별
1. 사용자로부터 입력받은 숫자가 양수, 음수 또는 0인지 판별하는 프로그램을 작성하세요.
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
# 문자열 값을 가지는 튜플 선언
str_list = ["apple", "banana", "cherry"]
# 모든 요소를 반복문을 사용하여 출력
# 출력:
# apple
# banana
# cherry
1. 문자열 값을 가지는 list를 선언하고, 모든 요소를 반복문을 사용하여 출력하세요.
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
# 과제 1: 1부터 10까지의 숫자 출력 (while 문 사용)
i = 1
while...
# 과제 2: 사용자로부터 비밀번호를 입력받아 1234를
# 받을 때까지 출력
while True:
num = int(input("비밀번호를 입력하세요: "))
if...
else...
1. 1부터 10까지의 숫자를 while 문을 사용하여 출력하세요.
2. 사용자로부터 숫자를 입력받아, 그 숫자가 비번 1234가 될 때까지 계속해서 숫자를 출력하고, 숫자가 1234가 되면 “종료”를 출력하세요.
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
# 곱셈 함수 만들기
def multiple(a, b):
# 덧셈 결과값 짝수 판별기
def check_sum_even(a, b):
1. a, b를 입력받아 a x b 곱셈 결과값을 리턴하는 함수를 만드세요
2. a, b를 입력받아 더한 값이 짝수인지 판별하는 함수를 만드세요
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
# get dict values
x = thisdict.values()
# get dict keys
x = thisdict.keys()
# get dict (key, value)s
x = this dict.items()
car = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}
dictionary 의 몇 가지 유용한 method 함수들
# 주어진 문자열
text = "hello world hello python hello code"
# 각 단어의 빈도수를 계산하여 딕셔너리에 저장
word_count = {}
# 문자열을 공백으로 분리하여 단어 리스트를 생성
words = text.split()
# 각 단어의 빈도수 계산
for word in words:
if..
else..
# 결과 출력
print(word_count)
1. 주어진 text 문자열에서 각 단어가 얼마나 자주 등장하는지 python dict 를 활용하여 풀어보세요
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Title Text
- Bullet One
- Bullet Two
- Bullet Three
출처: 점프투파이썬, 박응용
라이센스 CC BY-SA 4.0 DEED
Flask Applicatoin
식물도감 앱을 만들어 봅시다
개발 환경과 배포 환경 소개
- 내 컴퓨터에서 개발하기 위해선
- 파이썬 설치
- 더 나은 개발 환경 설치 (e.g. windows 10 with WSL)
- 여러가지 개발 환경 추가 세팅
- 복잡하고 머리 아픈 일
- 남의 컴퓨터에서 개발하기 위해선
- 남의 것 빌려서 쓴다
- 끝
데이터베이스
- (basic) 그냥 python dictionary 자료 구조 사용
- (advanced) SQLite3 라는 관계형 데이터베이스 교체 사용
data = [
{
"id": 1,
"plant_nm": "깻잎",
"family_kor_nm": "깻잎과",
"family_nm": "Brassica",
"genus_kor_nm": "깻잎속",
"genus_nm": "Brassica",
"img_url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSr8Uh-buvRfMZZXw4LYjN5OZthg6mmEIFsWA&s",
"desc": "깻잎은 맛있다"
},
{
"id": 2,
"plant_nm": "코코아잎",
"family_kor_nm": "코코아잎과",
"family_nm": "Coccaceae",
"genus_kor_nm": "코코아속",
"genus_nm": "Coccus",
"img_url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ2GJiBJWcs7UlQQDc5ceHK1li4x22AboerzA&s",
"desc": "코코아 하하"
}
]
py dict vs SQLite3
fake_data = [
{
"id": 1,
"plant_nm": "깻잎",
"family_kor_nm": "깻잎과",
"family_nm": "Brassica",
"genus_kor_nm": "깻잎속",
"genus_nm": "Brassica",
"img_url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSr8Uh-buvRfMZZXw4LYjN5OZthg6mmEIFsWA&s",
"desc": "깻잎은 맛있다"
}
]
import sqlite3
conn = sqlite3.connect('plants.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE plants (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
img_url TEXT,
desc TEXT
)
''')
conn.commit()
conn.close()
Flask Hello World
- Flask 설치:
pip install flask
- Bullet Three
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
Flask App 실행
- Flask 앱 실행:
python app.py / flask run --debug
- 브라우저에서
http://localhost:5000
접속하여 결과 확인
5000 은 개발환경을 위한 컴퓨터 포트 번호이며, 3000 등으로 변경 가능, 중복 불가
Bootstrap을 사용한 HTML 템플릿 작성
- Bullet One
- Bullet Two
- Bullet Three
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<title>Plant Encyclopedia</title>
</head>
<body>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>
jinja2 템플릿 엔진 base HTML
- Bullet One
- Bullet Two
- Bullet Three
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
/>
<link rel="stylesheet" href="/static/main.css" />
<title>Plant Encyclopedia</title>
</head>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/">홍길동의 식물도감</a>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarNav"
aria-controls="navbarNav"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="/"
>Home <span class="sr-only">(current)</span></a
>
</li>
<li class="nav-item">
<a class="nav-link" href="/about">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/contact">Contact</a>
</li>
</ul>
</div>
</nav>
</header>
<body>
<div class="container">{% block content %}{% endblock %}</div>
</body>
<footer class="bg-light text-center text-lg-start">
<div class="text-center p-3" style="background-color: rgba(0, 0, 0, 0.05)">
© 2024 산림과학고. All rights reserved.
</div>
</footer>
</html>
조회 페이지 템플릿
- Bullet One
- Bullet Two
- Bullet Three
{% extends "base.html" %} {% block content %}
<div class="container">
<div class="row mb-3">
<div class="col-12 text-right">
<a
href="/add"
class="btn btn-success"
style="color: white; margin-top: 2em"
>Add New Plant</a
>
</div>
</div>
<div class="row">
{% for plant in plants %}
<div class="col-12">
<!-- This ensures each plant takes a full row -->
<div class="card mb-3">
<div class="row g-0">
<a href="/{{ plant.id }}" class="col-md-3 card-box data-plant-id="{{ plant.id }}">
<img
src="{{ plant.img_url }}"
class="img-fluid rounded-start card-img-top"
alt="{{ plant.family_kor_nm }}"
/>
</a>
<div class="col-md-9">
<div class="card-body">
<h5 class="card-title">과: {{ plant.family_kor_nm }}</h5>
<h5 class="card-title">속: {{ plant.genus_kor_nm }}</h5>
<p class="card-title">이름: {{ plant.plant_nm }}</p>
<button class="btn btn-primary" onclick="window.location.href='/edit/{{ plant.id }}'">Edit</button>
<button class="btn btn-danger" onclick="confirmDelete('{{ plant.id }}', '{{ plant.plant_nm }}')">Delete</button>
</div>
</div>
</div>
</div>
</div>
{% else %}
<div class="col-12">
<p>No plants found.</p>
</div>
{% endfor %}
</div>
</div>
<script>
function confirmDelete(id, name) {
if (confirm('Do you really want to delete ' + name + '?')) {
fetch('/delete/' + id, { method: 'POST' })
.then(response => {
if (response.ok) {
window.location.reload(); // Reload the page to reflect the changes
} else {
alert('Failed to delete the plant.');
}
})
.catch(error => {
console.error('Error:', error);
alert('Error deleting the plant.');
});
}
}
</script>
{% endblock %}
데이터 추가 form 작성
- Bullet One
- Bullet Two
- Bullet Three
<!-- templates/add_plant.html -->
{% extends "base.html" %} {% block content %}
<h1>Add New Plant</h1>
<form method="POST">
<div class="form-group">
<label for="family_kor_nm">과 이름</label>
<input
type="text"
class="form-control"
id="family_kor_nm"
name="family_kor_nm"
/>
</div>
<div class="form-group">
<label for="genus_kor_nm">속 이름</label>
<input
type="text"
class="form-control"
id="genus_kor_nm"
name="genus_kor_nm"
/>
</div>
<div class="form-group">
<label for="plant_nm">이름</label>
<input
type="text"
class="form-control"
id="plant_nm"
name="plant_nm"
required
/>
</div>
<div class="form-group">
<label for="img_url">사진 저장 주소</label>
<input type="text" class="form-control" id="img_url" name="img_url" />
</div>
<div class="form-group">
<label for="desc">Description</label>
<textarea class="form-control" id="desc" name="desc" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">Add Plant</button>
</form>
{% endblock %}
데이터 수정 form 작성
- Bullet One
- Bullet Two
- Bullet Three
<!-- templates/edit_plant.html -->
{% extends "base.html" %} {% block content %}
<h1>Edit Plant</h1>
<form method="POST">
<div class="form-group">
<label for="family_kor_nm">과 이름</label>
<input
type="text"
class="form-control"
id="family_kor_nm"
name="family_kor_nm"
placeholder="{{ plant.family_kor_nm }}"
/>
</div>
<div class="form-group">
<label for="genus_kor_nm">속 이름</label>
<input
type="text"
class="form-control"
id="genus_kor_nm"
name="genus_kor_nm"
placeholder="{{ plant.genus_kor_nm }}"
/>
</div>
<div class="form-group">
<label for="plant_nm">이름</label>
<input
type="text"
class="form-control"
id="plant_nm"
name="plant_nm"
placeholder="{{ plant.plant_nm }}"
/>
</div>
<div class="form-group">
<label for="img_url">사진 저장 주소</label>
<input
type="text"
class="form-control"
id="img_url"
name="img_url"
placeholder="{{ plant.img_url }}"
/>
</div>
<div class="form-group">
<label for="desc">Description</label>
<textarea
class="form-control"
id="desc"
name="desc"
rows="3"
placeholder="{{ plant.desc }}"
></textarea>
</div>
<button type="submit" class="btn btn-primary">수정하기</button>
</form>
{% endblock %}
가짜 데이터베이스 생성
fake_data = {
1: {'name': '소나무', 'img_url': 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSRc378mgNcg7LzXbaxUYNd9mHFPQCAvCG_SA&s', 'desc': '늘 푸른 소나무'},
2: {'name': '갈대풀', 'img_url': 'https://newsteacher.chosun.com/site/data/img_dir/2017/10/16/2017101600044_0.jpg', 'desc': '누구 마음은 갈대 바람'}
}
식물 조회 - 식물 모두
- Bullet One
- Bullet Two
- Bullet Three
@app.route('/plants')
def get_plants():
# get some plants data
return render_template('plants.html', plants=fake_data)
식물 조회 - 1개 식물
- Bullet One
- Bullet Two
- Bullet Three
@app.route('/plant/<int:plant_id>')
def get_plant(plant_id):
# get one plant data
if plant:
return render_template('plant.html', plant=plant)
else:
return 'Plant not found', 404
식물 추가
- Bullet One
- Bullet Two
- Bullet Three
@app.route('/add_plant', methods=['POST'])
def add_plant():
new_id = max(fake_data.keys()) + 1
fake_data[new_id] = {
'name': request.form['name'],
'img_url': request.form['img_url'],
'desc': request.form['desc']
}
return redirect(url_for('get_plants'))
식물 수정
- Bullet One
- Bullet Two
- Bullet Three
@app.route('/edit_plant/<int:plant_id>', methods=['POST'])
def edit_plant(plant_id):
plant = fake_data.get(plant_id)
if plant:
fake_data[plant_id] = {
'name': request.form['name'],
'img_url': request.form['img_url'],
'desc': request.form['desc']
}
return redirect(url_for('get_plants'))
else:
return 'Plant not found', 404
식물 삭제
- Bullet One
- Bullet Two
- Bullet Three
@app.route('/delete_plant/<int:plant_id>', methods=['POST'])
def delete_plant(plant_id):
if plant_id in fake_data:
del fake_data[plant_id]
return redirect(url_for('get_plants'))
else:
return 'Plant not found', 404
SQLite3 로 DB 변경
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:////Users/johnnykoo/repos/codespaces-flask/data.db"
db.init_app(app)
데이터 조회
- Bullet One
- Bullet Two
- Bullet Three
@app.route('/plants')
def get_plants():
plants = DataItem.query.all()
@app.route("/<int:id>")
def get_plant(id):
plant = DataItem.query.get(id)
데이터 쓰기 - 추가
- Bullet One
- Bullet Two
- Bullet Three
@app.route('/add_plant', methods=['POST'])
def add_plant():
new_plant = DataItem(
family_kor_nm=request.form['family_kor_nm'],
genus_kor_nm=request.form['genus_kor_nm'],
img_url=request.form['img_url'],
plant_nm=request.form['plant_nm'],
desc=request.form['desc'],
)
db.session.add(new_plant)
db.session.commit()
데이터 쓰기 - 수정
- Bullet One
- Bullet Two
- Bullet Three
@app.route("/edit/<int:id>", methods=['GET', 'POST'])
def edit_plant(id):
plant_to_edit = DataItem.query.get(id)
# do some edit work here
db.session.commit()
데이터 삭제 route 작성
- Bullet One
- Bullet Two
- Bullet Three
@app.route("/delete/<int:id>", methods=['POST'])
def delete_plant(id):
plant_to_delete = DataItem.query.get(id)
if plant_to_delete:
db.session.delete(plant_to_delete)
db.session.commit()
전체 코드 구조 정리
- Bullet One
- Bullet Two
- Bullet Three
/project
│
├── /templates
│ ├── base.html
│ ├── plants.html
│ ├── plant.html
│ ├── add_plant.html
│ └── edit_plant.html
│
├── app.py
└── data.db
Advanced: 검색 기능
검색 route
- Bullet One
- Bullet Two
- Bullet Three
@app.route('/search', methods=['GET', 'POST'])
def search():
if request.method == 'POST':
keyword = request.form['keyword']
db = get_db()
cursor = db.execute('SELECT * FROM plants WHERE name LIKE ? OR description LIKE ?',
('%' + keyword + '%', '%' + keyword + '%'))
plants = cursor.fetchall()
return render_template('plants.html', plants=plants)
return render_template('search.html')
검색 form
- Bullet One
- Bullet Two
- Bullet Three
<!-- templates/search.html -->
{% extends "base.html" %}
{% block content %}
<h1>Search Plants</h1>
<form method="POST">
<div class="form-group">
<input type="text" class="form-control" name="keyword" placeholder="Enter plant name or description" required>
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
{% endblock %}
Q&A
끝
python을 활용한 식물도감 앱 만들기 프로젝트
By Ilmo Koo
python을 활용한 식물도감 앱 만들기 프로젝트
- 30