DBMS
PC에서 XAMPP 설치
먼저 데이터베이스와 DBMS의 차이부터 명확하게 짚고 넘어가 봅시다.
현대 데이터 아키텍처는 데이터를 다루는 방식에 따라 크게 두 가지 패러다임으로 나뉩니다.
웹 서버나 무거운 데이터베이스 서버를 내 컴퓨터에 수동으로 설치하고 포트를 여는 작업은 초보 시절 아주 큰 진입장벽입니다. 이때 파이썬에 기본 내장되어 있어 설치가 전혀 필요 없고, 파일 단 하나(.db)로 모든 데이터베이스가 관리되는 SQLite는 최고의 실습 무기입니다.
파이썬의 내장 sqlite3 모듈을 가동하여 데이터베이스를 제어하는 기본 코드입니다. 제자님이 나중에 직접 실행해 보실 수 있도록 정성껏 구성해 드립니다.
import sqlite3
# 1. 데이터베이스 연결 (example.db 파일이 없으면 자동으로 새로 생성됩니다)
conn = sqlite3.connect("example.db") #
# 2. 커서(Cursor) 생성: SQL 명령어를 전달하고 결과를 회수하는 비서 일꾼입니다
cur = conn.cursor() #
# 3. [Create] 테이블 생성 (IF NOT EXISTS로 중복 생성 방지 에러 해결)
cur.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER
)
""") #
# 4. [Insert] 데이터 삽입하기
# 중요: SQL Injection 해킹 방지를 위해 값을 직접 넣지 않고 물음표(?) 파라미터 바인딩을 씁니다!
cur.execute(
"INSERT INTO users (name, age) VALUES (?, ?)",
("홍길동", 30)
) #
# 여러 개의 데이터를 한 번에 대량 삽입(Bulk Insert)할 때는 executemany가 활약합니다.
more_users = [
("김철수", 25),
("이영희", 28)
]
cur.executemany("INSERT INTO users (name, age) VALUES (?, ?)", more_users) #
# 변경 사항을 영구적으로 디스크 파일에 저장(커밋)합니다.
conn.commit() #
# 5. [Read] 데이터 조회하기
cur.execute("SELECT * FROM users")
rows = cur.fetchall() # fetchall()은 모든 데이터를 한 번에 가져오는 함수입니다
print("=== 회원 정보 조회 결과 ===")
for row in rows:
print(row)
# 6. 작업이 끝났다면 보관실(DB)의 문을 닫아줍니다.
conn.close() #
실무 백엔드 개발자(FastAPI, Django 등)로 나아가게 되면 파이썬 코드 안에 생으로 문자열 쿼리("SELECT * FROM users")를 적어넣는 일을 줄이게 됩니다. 오타가 났을 때 잡아내기 매우 어렵기 때문입니다. 이때 사용하는 혁신적인 기술이 바로 ORM입니다.
# [전통적인 SQL 방식]
# 문자열로 쿼리를 짜야 하고 오타에 취약합니다.
cur.execute("SELECT * FROM users WHERE is_active = True")
# [ORM 방식]
# 파이썬의 클래스와 필터를 사용하여 아주 우아하게 데이터를 긁어옵니다.
active_users = session.query(Customer).filter(Customer.is_active == True).all() #
앞으로 제자님이 LLM 기반의 AI 제품이나 RAG(검색 증강 생성) 시스템을 개발하게 된다면 기존의 RDBMS 외에 벡터 데이터베이스를 필히 연동하여 다룰 줄 알아야 합니다.
"사과 레시피"를 찾으면 컬럼 텍스트 중에 정확히 사과라는 글자가 포함된 행만 콕 집어 매칭하여 돌려줍니다."사과 레시피"를 넣으면 사과라는 단어가 본문에 아예 없더라도 "애플파이", "사과잼", "가성비 좋은 요리" 등 의미상 깊게 연관된 데이터를 유사도 계산을 통해 똑똑하게 찾아 줍니다.CLI 게시판 코딩 중
현재 작성된 CLI 게시판 코드는 크게 데이터 정의(설계), 저장소 구축, 그리고 2단계로 나뉜 메인 실행 루프(회원 관리 -> 게시판 기능)로 구조화되어 있습니다.
전체적인 코드의 구조와 핵심 역할을 5가지 파트로 나누어 설명해 드립니다.
1. 준비 및 공통 도우미 함수 (Imports & Helpers)
random), 날짜/시간 처리(datetime, timezone), 그리고 데이터 관리를 편하게 해주는 dataclass를 불러옵니다. 특히 한국 시간(KST)을 기준으로 맞추어 코랩 환경에서도 시간이 어긋나지 않도록 설정되어 있습니다.now(): 현재 시간을 "YYYY-MM-DD HH:MM:SS" 형식의 문자열로 반환합니다.format_display_date(): 오늘 작성된 글은 "시간:분"만, 이전 글은 "년-월-일"만 표시하도록 날짜 문자열을 깔끔하게 다듬어 줍니다.2. 데이터 설계도 (Classes - 붕어빵 틀)
@dataclass를 활용하여 게시글과 회원의 정보를 담을 구조를 정의합니다.
Article 클래스: 게시글 번호(id), 제목(title), 내용(content), 작성일시(date), 수정일시(updateDate)를 묶어서 관리하며, 자체적으로 정보를 출력하는 print_detail() 메서드를 가집니다.Member 클래스: 회원 가입 번호(id), 가입일(regDate), 로그인 아이디(loginId), 비밀번호(password), 이름(name) 등의 회원 정보를 한데 묶어 관리합니다.3. 데이터 저장소 및 검색 (Global Variables & Search)
프로그램이 실행되는 동안 데이터를 기억할 전역 변수와 검색 함수입니다.
articles, 회원 정보를 담을 members 리스트를 사용합니다. 각 항목의 고유 번호를 발급하기 위해 num, member_num 변수를 사용하며, 현재 로그인한 사용자를 current_member에 기억합니다.Article이나 Member 객체를 찾아 반환하는 함수(find_article_by_id, find_member_by_login_id)가 구현되어 있습니다. 테스트를 위한 makeTestData() 함수도 포함됩니다.4. 1단계 루프: 회원 관리 및 로그인 시스템
프로그램의 메인 전원(is_running)이 켜지면 가장 먼저 진입하는 단계입니다. 로그인 플래그(is_logged_in = False)를 활용하여 사용자를 통제합니다.
member join): 아이디 중복 검사와 비밀번호 재확인 절차를 거쳐 새로운 Member 객체를 생성하고 저장소에 등록합니다.log in): 등록된 아이디와 비밀번호를 대조하여 일치하면 is_logged_in을 True로 변경하고 1단계 루프를 탈출하여 게시판으로 넘어갑니다.m l)와 프로그램 완전 종료(exit) 기능이 있습니다.5. 2단계 루프: 게시판 메인 기능
로그인에 성공하여 is_logged_in == True가 된 사용자만 접근할 수 있는 영역입니다.
a w), 목록 보기(a l), 상세보기(a v), 삭제(a d), 수정(a r) 등 전형적인 CRUD(Create, Read, Update, Delete) 기능이 구현되어 있습니다.log out): 로그아웃 시 is_logged_in 플래그를 다시 False로 내리고, 게시판 루프를 빠져나가 다시 1단계(회원 관리) 초기 화면으로 돌아가는 안전한 구조로 설계되어 있습니다.====================================================================
from datetime import datetime, timedelta, timezone # 날짜와 시간을 다루기 위한 도구들
from dataclasses import dataclass
KST = timezone(timedelta(hours=9))
def now():
return datetime.now(KST).strftime("%Y-%m-%d %H:%M:%S")
def format_display_date(date_str):
if not date_str: # 날짜가 비어있다면 빈 칸 반환
return ""
# "2026-09-02 14:30:00" -> 띄어쓰기를 기준으로 날짜와 시간으로 쪼갭니다.
date_part, time_part = date_str.split(" ")
# 오늘 날짜만 가져옵니다. (예: "2026-09-02")
today_str = now().split(" ")[0]
if date_part == today_str:
# 오늘 쓴 글이면 시간만 보여줍니다. ("14:30")
return time_part[:5]
else:
# 오늘 쓴 글이 아니면 연도 앞 2자리를 빼고 보여줍니다. ("26-09-02")
return date_part[2:]
@dataclass
class Article:
id: int # 글 번호 (숫자)
title: str # 글 제목 (글자)
content: str # 글 내용 (글자)
date: str # 작성 시간 (글자)
updateDate: str = None # 수정 시간 (처음엔 수정한 적이 없으니 비워둠)
# 이 틀로 만든 붕어빵(객체)이 스스로 자신의 상세 정보를 출력하게 하는 기능
def print_detail(self):
print(f"게시글 No: {self.id}")
print(f"제 목 : {self.title}")
print(f"내 용 : {self.content}")
print(f"작성일시 : {self.date}")
if self.updateDate: # 수정된 적이 있다면 수정일시도 출력
print(f"수정일시 : {self.updateDate}")
@dataclass
class Member:
id: int # 가입 순서 번호
regDate: str # 가입 일시
updateDate: str # 정보 변경 일시
loginId: str # 로그인할 때 쓸 아이디
password: str # 비밀번호
name: str # 회원 이름
num = 0 # 마지막으로 생성된 게시글 번호표
articles = [] # 게시글(Article 객체)들을 차곡차곡 쌓아둘 리스트(박스)
member_num = 0 # 마지막으로 생성된 회원 가입 순서 번호표
members = [] # 회원(Member 객체)들을 차곡차곡 쌓아둘 리스트(박스)
current_member = None # 현재 로그인에 성공한 '그 회원'을 기억하는 변수
def makeTestData():
global articles, num
current_time = datetime.now(KST)
for i in range(1, 4):
# 무작위로 과거 날짜를 꾸며냅니다.
random_days = random.randint(1, 30)
random_hours = random.randint(0, 23)
random_minutes = random.randint(0, 59)
past_time = current_time - timedelta(days=random_days, hours=random_hours, minutes=random_minutes)
random_date_str = past_time.strftime("%Y-%m-%d %H:%M:%S")
# Article 틀에 찍어내어 리스트에 넣습니다.
new_article = Article(id=i, title=f"제목{i}", content=f"내용{i}", date=random_date_str)
articles.append(new_article)
num = 3 # 3개 만들었으니 다음 글 번호표는 3번부터 시작하도록 세팅
print("테스트 데이터 3개가 생성되었습니다.\n")
def find_article_by_id(target_id):
for art in articles:
if art.id == target_id:
return art # 찾으면 바로 그 글을 던져주고 끝냄
return None # 끝까지 뒤져도 없으면 '없다(None)'고 알려줌
def find_member_by_login_id(target_login_id):
for member in members:
if member.loginId == target_login_id:
return member # 찾으면 회원 정보를 던져줌
return None
print("-" 21)
print("== CLI 게시판 실행 ==")
print("-" 21)
is_running = True
while True:
print("명령어 입력 : 로그인 'log in', 가입 'member join', 취소 'exit'")
cmd = input("명령어 )").strip() # .strip()은 사용자가 실수로 공백(스페이스바)을 넣어도 지워주는 안전장치입니다.
# 1) 프로그램 완전 종료
if cmd == "member out" or cmd == "exit":
print("== CLI 게시판 종료 ==")
# [수정됨: 코랩 환경 오류 해결]
# 코랩 엔진을 고장 내는 exit() 명령어를 삭제했습니다.
# 대신, 전원 스위치를 끄고(False) break를 이용해 1단계(회원가입/로그인) 반복문을 안전하게 빠져나갑니다.
is_running = False
break
# 2) 로그인 기능
elif cmd == "log in" :
# 아이디/비번을 맞출 때까지 계속 묻기 위한 작은 반복문
while True:
revise_id = input("사용자 id를 입력하세요: ")
revise_pw = input("사용자 pw를 입력하세요: ")
if revise_id == "exit" or revise_pw == "exit":
print("== 로그인 exit ==")
break # 로그인 시도를 멈추고 바깥 메뉴로 나감
# 입력한 아이디로 회원 창고(members)를 뒤집니다.
found_member = find_member_by_login_id(revise_id)
# 회원을 찾았고(존재하고), 그 회원의 비밀번호가 내가 방금 친 비밀번호와 같다면!
if found_member and found_member.password == revise_pw:
print(f"{found_member.name}님이 로그인 되었습니다.\n")
current_member = found_member # 로그인 성공 도장 쾅! (현재 회원 기억)
break # 로그인 성공했으니 아이디 묻는 반복문 탈출
else:
print(f"{revise_id}는 회원이 아니거나 비번이 틀립니다. 다시 입력 \n")
# 인증(로그인)을 통과했다면, 전체 회원 관리 무한 반복문도 탈출하여 게시판으로 갑니다.
if current_member:
break
# 3) 회원 가입 기능
elif cmd == "member join" or cmd == "m j":
# 3-1. 아이디 중복 체크 (통과할 때까지 반복)
while True:
new_login_id = input("회원ID : ")
if find_member_by_login_id(new_login_id): # 누군가 이미 있다면?
print("이미 존재하는 아이디입니다. 다른 아이디를 사용해주세요.\n")
else:
break # 중복이 아니면 이 작은 반복문 탈출!
# 3-2. 비밀번호 확인 (두 번 똑같이 칠 때까지 반복)
while True:
pw = input("비밀번호 : ")
pw_confirm = input("비밀번호 확인 : ")
if pw == pw_confirm: # 두 개가 같으면
break # 반복문 탈출!
else:
print("비밀번호가 일치하지 않습니다. 다시 입력해주세요.\n")
name = input("이름 : ")
now_time = now() # 현재 시간 가져오기
member_num += 1 # 가입했으니 회원 번호표 1 증가!
# 위에서 만든 Member(회원 붕어빵 틀)에 정보를 넣어 객체 생성
new_member = Member(
id=member_num,
regDate=now_time,
updateDate=now_time,
loginId=new_login_id,
password=pw,
name=name
)
members.append(new_member) # 회원 리스트(창고)에 저장!
print(f"{name}님의 회원 가입이 완료 되었습니다.\n")
# 4) 관리자용 회원 목록 보기 기능
elif cmd == "m l":
print("=" * 60)
print("가입번호 / 로그인ID / 비밀번호 / 이름 / 가입일시")
if not members: # 리스트가 텅 비었다면
print("현재 가입된 회원이 없습니다.")
else:
for m in members: # members 리스트에서 한 명씩(m) 꺼내서 출력
formatted_reg_date = format_display_date(m.regDate)
print(f"{m.id} / {m.loginId} / {m.password} / {m.name} / {formatted_reg_date}")
print("=" * 60)
else:
print("지원하지 않는 명령어 입니다.\n")
if is_running:
makeTestData() # 텅 빈 게시판을 위해 3개의 가짜 글 생성
# 게시판 전용 무한 반복문 시작
while True:
cmd = input("명령어 )").strip()
if cmd == "exit":
print("== CLI 게시판 종료 ==")
# [수정됨: 코랩 환경 오류 해결]
# 여기도 마찬가지로 exit() 대신 break를 사용해 안전하게 탈출합니다.
break
# 1) 글쓰기 (article write)
elif cmd == "a w":
num += 1 # 글 번호표 1 증가
title = input("제목 : ")
content = input("내용 : ")
# 새로 쓴 글을 Article 틀에 찍어서 객체로 만든 후 리스트(articles)에 보관
new_article = Article(id=num, title=title, content=content, date=now())
articles.append(new_article)
print(f"{num}번 글이 생성 되었습니다.\n")
# 2) 글 목록 보기 (article list)
elif cmd == "a l":
print("=" * 60)
print("번호 / 제목 / 내용 / 작성일 / 수정일")
# articles[::-1] 은 최신 글(맨 마지막에 추가된 글)이 가장 먼저 보이도록 리스트를 뒤집는 마법입니다.
for art in articles[::-1]:
formatted_date = format_display_date(art.date)
# 수정일이 있으면 날짜를 예쁘게 깎고, 수정한 적 없으면(None) 그냥 빈 칸 유지
formatted_update = format_display_date(art.updateDate) if art.updateDate else " "
print(f"{art.id} / {art.title} / {art.content} / {formatted_date} / {formatted_update}")
print("=" * 60)
# 3) 글 상세보기 (article view)
elif cmd == "a v" or cmd == "aticle view":
target_id = int(input("상세보기 할 글 id 번호를 입력하세요: "))
found_art = find_article_by_id(target_id) # 글 찾기 도우미 출동!
if found_art:
found_art.print_detail() # 찾았다면 클래스에 만들어둔 출력 기능 실행
else:
print(f"{target_id}번 글은 목록에 없습니다.\n")
# 4) 글 삭제하기 (article delete)
elif cmd == "a d" or cmd == "aticle delete":
target_id = int(input("삭제할 글 id 번호를 입력하세요: "))
found_art = find_article_by_id(target_id)
if found_art:
articles.remove(found_art) # 리스트(창고)에서 그 글을 아예 지워버립니다.
print(f"{target_id}번 글이 삭제되었습니다.\n")
else:
print(f"{target_id}번 글은 목록에 없습니다.\n")
# 5) 글 수정하기 (article review/revise)
elif cmd == "a r" or cmd == "aticle review":
target_id = int(input("수정할 글 id 번호를 입력하세요: "))
found_art = find_article_by_id(target_id)
if found_art:
# 찾은 객체의 데이터(제목, 내용, 수정일)를 방금 새로 입력받은 내용으로 덮어씌웁니다.
found_art.title = input("제목 : ")
found_art.content = input("내용 : ")
found_art.updateDate = now()
print(f"{target_id}번 글이 성공적으로 수정 되었습니다.\n")
else:
print(f"{target_id}번 글은 목록에 없습니다.\n")
else:
print("지원하지 않는 명령어 입니다.\n")