
프로젝트별로 독립적인 패키지 관리를 위해 가상환경을 생성하고 설정함.
venv 모듈을 사용하여 가상환경을 생성함.
# 가상환경 생성 (이름: dataproject)
python -m venv dataproject
# 가상환경 진입 (Windows 기준)
# 생성된 폴더 내 Scripts/activate 실행
D:\path\to\venvs\dataproject\Scripts> activate
가상환경을 Jupyter Notebook에서 사용하기 위해 ipykernel을 설치하고 등록함.
# ipykernel 모듈 설치
pip install ipykernel
# 가상환경을 Jupyter 커널로 등록
python -m ipykernel install --user --name dataproject --display-name "Python(dataproject)"
설정 후 Jupyter Notebook의 Kernel > Change kernel 메뉴에서 Python(dataproject)를 선택하여 사용 가능함.
Python에서 PostgreSQL에 접속하기 위해 psycopg2 라이브러리를 사용함.
# 라이브러리 설치
pip install psycopg2-binary
import psycopg2
# 데이터베이스 연결 설정
conn = psycopg2.connect(
host="localhost",
database="postgres",
user="postgres",
password="1111"
)
# 커서(Cursor) 생성: SQL 실행을 위한 객체
cur = conn.cursor()
데이터 조회(SELECT)와 조작(INSERT) 시 트랜잭션 확정(commit) 및 에러 처리(rollback)가 필수적임.
# 1. 데이터 조회
cur.execute("SELECT * FROM public.user_info")
rows = cur.fetchall()
for row in rows:
print(row)
# 2. 데이터 삽입 및 트랜잭션 처리
query = "insert into public.user_info values ('chris2','인선미', ...);"
try:
cur.execute(query)
conn.commit() # 변경사항 확정
print("데이터 입력 성공")
except Exception as e:
print("에러 발생:", e)
conn.rollback() # 에러 발생 시 되돌리기
SQL 쿼리 결과를 데이터프레임으로 변환하여 분석에 활용함. pd.read_sql 함수를 사용하면 커서 순회 없이 바로 데이터프레임 생성이 가능함.
import pandas as pd
# DB 연결 객체(conn)를 사용하여 데이터프레임 생성
chick_info = pd.read_sql("SELECT * FROM fms.chick_info", conn)
prod_info = pd.read_sql("SELECT * FROM fms.prod_result", conn)
# ... 기타 테이블 로드

Flask는 Python으로 웹 사이트나 API 서버를 쉽고 빠르게 만들 수 있는 마이크로 프레임워크임. 필요한 기능만 최소한으로 갖추고 있어 확장이 용이함.
# Flask 설치
pip install flask
기본 디렉토리 구조
app.py: 메인 실행 파일templates/: HTML 파일 저장 폴더.env: 환경 변수 저장 파일app.py)from flask import Flask
# 1. Flask 객체 생성
app = Flask(__name__)
# 2. 라우팅 설정
@app.route("/")
def hello():
return "hello, world!"
# 3. 서버 실행 조건
if __name__ == "__main__":
app.run()
코드 상세 설명
app = Flask(__name__)__name__: 현재 실행 중인 파일의 모듈 이름. Flask에게 "현재 실행 위치가 여기다"라고 알려주어, 템플릿이나 정적 파일을 어디서 찾아야 할지 기준점을 잡아주는 역할을 함.@app.route("/")@ (데코레이터): URL 주소와 함수를 연결해 주는 기능."/": 웹 사이트의 대문(메인 페이지) 주소. (예: www.google.com/ 뒤에 아무것도 없는 상태). 만약 "/menu"로 설정하면 www.mysite.com/menu로 접속했을 때 동작함.def hello(): ...return "hello, world!": 서버가 브라우저에게 전달하는 응답 값. 사용자는 흰 화면에 이 텍스트를 보게 됨.if __name__ == "__main__":__name__ 변수에 "__main__"이 할당되어 조건이 참(True)이 됨 app.run() 실행 (서버 켜짐).__name__에 파일명이 할당되어 조건이 거짓(False)이 됨 서버가 켜지지 않고 기능만 빌려줌.코드를 수정할 때마다 서버를 재시작하지 않기 위해 디버그 모드를 활성화하는 것이 좋음.
# Windows CMD에서 디버그 모드 설정
set FLASK_DEBUG=true
# 서버 실행
flask run
실행 후 http://127.0.0.1:5000 주소로 접속하여 결과 확인 가능함.
DB 접속 정보 등 민감한 정보는 소스 코드에 직접 작성하지 않고 .env 파일을 생성하여 관리함.
python-dotenv 설치
pip install python-dotenv
보안을 위해 환경 변수(.env)를 로드하고, psycopg2를 이용해 PostgreSQL에 연결함.
# .env 파일 로드 (로컬 환경)
if os.path.exists('.env'):
load_dotenv()
# DB 연결 함수
def get_db_connection():
conn = psycopg2.connect(
host=os.getenv('DB_HOST'),
# ... (생략)
)
conn.autocommit = True # 트랜잭션 자동 커밋 설정
return conn
load_dotenv(): 소스 코드에 비밀번호를 노출하지 않기 위해 .env 파일에서 환경 변수를 불러옴.autocommit=True: 별도로 conn.commit()을 호출하지 않아도 쿼리가 즉시 반영되도록 설정함.게시글 작성 (Create) & RETURNING 절 활용
@app.route('/create/', methods=['POST'])
def create_post():
# ... (데이터 검증)
cursor.execute("INSERT INTO board.posts ... VALUES ... RETURNING id", (title, content, author))
post_id = cursor.fetchone()[0] # 생성된 ID를 즉시 받아옴
return redirect(url_for('view_post', post_id=post_id))
RETURNING id: INSERT 쿼리 실행 후, 방금 생성된 행의 ID를 바로 반환받아 상세 페이지로 리다이렉트할 때 사용함. 별도의 조회 쿼리를 줄여 효율적임.상세 조회 및 조회수 증가 (Read)
@app.route('/post/<int:post_id>')
def view_post(post_id):
# 1. 조회수 1 증가
cursor.execute('UPDATE board.posts SET view_count = view_count + 1 WHERE id = %s', (post_id,))
# 2. 게시글 내용 조회
cursor.execute('SELECT * FROM board.posts WHERE id = %s', (post_id,))
# 3. 댓글 목록 조회
cursor.execute('SELECT * FROM board.comments WHERE post_id = %s ...', (post_id,))
# ...
수정 및 삭제 (Update, Delete)
UPDATE 문을 사용하여 제목, 내용, 그리고 수정일시(updated_at)를 현재 시간으로 갱신함.DELETE 문을 사용하여 해당 ID의 게시글을 삭제함.댓글 (Comment)
게시글 ID(post_id)를 외래키로 하여 댓글 테이블에 데이터를 적재함.
좋아요 (Like) - IP 기반 중복 방지 로직
로그인 기능이 없는 상태에서 중복 좋아요를 막기 위해 사용자 IP(request.remote_addr)를 활용함.
@app.route('/post/like/<int:post_id>', methods=['POST'])
def like_post(post_id):
user_ip = request.remote_addr # 접속자 IP 확인
# 이미 좋아요를 눌렀는지 확인
cursor.execute('SELECT COUNT(*) FROM board.likes WHERE post_id = %s AND user_ip = %s', (post_id, user_ip))
already_liked = cursor.fetchone()[0] > 0
if already_liked:
# 이미 눌렀다면 -> 좋아요 취소 (DELETE & 카운트 감소)
cursor.execute('DELETE FROM board.likes ...')
cursor.execute('UPDATE board.posts SET like_count = like_count - 1 ...')
else:
# 안 눌렀다면 -> 좋아요 등록 (INSERT & 카운트 증가)
cursor.execute('INSERT INTO board.likes ...')
cursor.execute('UPDATE board.posts SET like_count = like_count + 1 ...')
/fms/result)DB에 저장된 원시 데이터를 가져와 파이썬 레벨에서 데이터 전처리 및 통계 산출을 수행함.
데이터 전처리 로직
status = str(row.get('부적합여부', '')).strip()
if status == '부적합' or status.lower() == 'fail':
summary['fail'] += 1
'62g', '1,200' 등 문자열로 저장된 수치 데이터를 순수 float형으로 변환함.weight_val = float(str(weight).replace('g', '').replace(',', '').strip())
시각화 데이터 구조화
Chart.js나 Matplotlib 등 프론트엔드 차트 라이브러리에서 바로 사용할 수 있도록 데이터를 리스트와 딕셔너리 형태로 가공하여 전달함.
weights_all: 히스토그램용 전체 무게 리스트.weights_by_breed: 품종별 박스플롯(Boxplot)용 딕셔너리.서버 디스크에 파일을 저장하지 않고, 메모리 상에서 파일을 생성하여 즉시 다운로드하게 함. 서버 용량 관리 측면에서 매우 효율적임.
from io import StringIO
import csv
@app.route('/fms/export')
def export_fms():
# ... 데이터 조회 ...
# 1. 가상 파일 객체(메모리) 생성
si = StringIO()
cw = csv.writer(si)
# 2. 헤더 및 데이터 쓰기
if rows:
cw.writerow(rows[0].keys())
for row in rows:
cw.writerow(row.values())
# 3. 응답 객체(Response) 생성 및 헤더 설정
output = make_response(si.getvalue())
output.headers["Content-Disposition"] = "attachment; filename=fms_result.csv"
output.headers["Content-type"] = "text/csv"
return output
StringIO: 문자열을 파일처럼 다룰 수 있게 해주는 객체.Content-Disposition: 브라우저에게 이 응답이 파일 다운로드임을 알려주는 헤더.

해당 기능을 구현한 웹페이지, 마우스오버 시 자세한 정보가 나오도록 함.