본문 바로가기
주전공/캡스톤디자인과창업프로젝트

졸프 - 소소행; | 프로젝트 데모용 코드 만들기

by 희띠띠 2025. 6. 15.

중간 발표를 준비하며 구조화한 기능명세서를 바탕으로

우리는 스타트학기 기말 심층인터뷰 준비를 위해

대표적 기능 구분별로 데모 코드를 작성했다.

 

1번 "회원가입/로그인"과 4번 로컬 특산물 등록 및 구매/판매"에 대한 코드는

금방 작성했으며 실행되는 것까지 확인했다.

(물론 실제로 통합하여 앱에 반영될라면 추가로 손보긴 해야한다)

 

근데 문제는..

2번 "사용자 맞춤 여행지 추천"과 3번 "지역 기반 축제 정보 제공"이였다.

이 파트에서 '관광정보API'의 활용이 요구되는데

api 연동 문제 해결에만 일주일 넘게 소요되었다......

 

그.런.데.

 

기적적으로!!

지도교수님과 대면 면담을 하기로 한 날

면담 시작 30분? 전에 해결 방법을 찾아 연동에 성공하였다,,,,ㅠㅠ

 

작업한 데모코드를 살펴보자 → 

 

🔗우리팀 깃허브 레포

https://github.com/CAPSTONEEEEE/NaegaGreen

 

GitHub - CAPSTONEEEEE/NaegaGreen

Contribute to CAPSTONEEEEE/NaegaGreen development by creating an account on GitHub.

github.com

 

데모 코드

1. 회원가입/로그인

※ 아래 코드블럭별로 구분된대로 코랩에서의 별도의 셀이라 이해하면 된다.

 

# 소소행 Colab 초기화 셀

!pip install --quiet numpy pandas

import numpy as np
import pandas as pd

print("✅ 필수 패키지 설치 완료")

 

# 사용자 입력 기반 로그인/회원가입 데모 with 이름 입력 및 반복 (위젯사용ver)

import ipywidgets as widgets
from IPython.display import display, clear_output

# 사용자 DB
user_db = []

# 회원가입 함수
def sign_up(email, password, name, age, gender):
    if any(user["email"] == email for user in user_db):
        return False, "⚠️ 이미 존재하는 이메일입니다."
    user = {
        "email": email,
        "password": password,
        "name": name,
        "age": age,
        "gender": gender,
    }
    user_db.append(user)
    return True, f"✅ 회원가입 성공! 환영합니다, {name}님"

# 로그인 함수
def login(email, password):
    for user in user_db:
        if user["email"] == email and user["password"] == password:
            return True, f"🔓 로그인 성공! 환영합니다, {user['name']}님"
    return False, "❌ 로그인 실패: 이메일 또는 비밀번호가 틀립니다."

# UI 구성
output = widgets.Output()
menu_buttons = widgets.ToggleButtons(
    options=["로그인", "회원가입"],
    description="선택:",
    button_style="info",
)

# 입력 위젯들
email_input = widgets.Text(description="이메일:")
password_input = widgets.Password(description="비밀번호:")
name_input = widgets.Text(description="이름:")
age_input = widgets.IntText(description="나이:")
gender_input = widgets.Dropdown(options=["남성", "여성"], description="성별:")
submit_button = widgets.Button(description="제출", button_style="success")

# 상태 저장
current_action = {"mode": None}

# 버튼 클릭 처리
def on_menu_change(change):
    clear_output()
    output.clear_output()
    display(menu_buttons)

    if change["new"] == "로그인":
        current_action["mode"] = "login"
        display(email_input, password_input, submit_button, output)
    elif change["new"] == "회원가입":
        current_action["mode"] = "signup"
        display(email_input, password_input, name_input, age_input, gender_input, submit_button, output)

def on_submit_click(b):
    output.clear_output()
    with output:
        if current_action["mode"] == "login":
            success, message = login(email_input.value, password_input.value)
            print(message)
        elif current_action["mode"] == "signup":
            success, message = sign_up(
                email_input.value,
                password_input.value,
                name_input.value,
                age_input.value,
                gender_input.value
            )
            print(message)

        # 성공 시 자동 리셋
        if success:
            menu_buttons.disabled = True
            submit_button.disabled = True

# 이벤트 연결
menu_buttons.observe(on_menu_change, names="value")
submit_button.on_click(on_submit_click)

# 실행
display(menu_buttons)

 

2. 사용자 맞춤 여행지 추천 

※ 해당 코드를 실행하려면 아래 두 사이트를 통해

1. 관광데이터포털에서 '한국관광공사_국문 관광정보 서비스_GW' 활용신청 후 인증키를 받아 servicekey 변수에 입력하고,

https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15101578

2. OpenAI Platform에서 신규 API key를 발급받아 api_key 변수에 입력해야한다.

https://platform.openai.com/api-keys

 

# 지역 맞춤형 관광지 추천

: 사용자 여행 목적 및 지역에 부합하는 관광지 정보 필터링 및 제공

import openai
import requests
import pandas as pd

# ✅ OpenAI 키 설정
openai.api_key = ""		# ⚠️ 본인의 OpenAI 키로 교체하세요!

# ✅ GPT로 연관 키워드 확장
def get_related_keywords(keyword):
    try:
        prompt = f"""
        '{keyword}'라는 여행 목적 키워드와 관련된 유사 키워드를 5개 추천해주세요.
        단어만 콤마로 구분해서 출력하세요. 예: 휴식, 자연, 산책, 명상, 힐링
        """
        response = openai.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7
        )
        raw = response.choices[0].message.content
        return [k.strip() for k in raw.split(",") if k.strip()]
    except Exception as e:
        print(f"❌ GPT 키워드 확장 실패: {e}")
        return [keyword]

# ✅ GPT로 간단한 소개 생성
def generate_overview(title):
    try:
        prompt = f"'{title}'라는 장소에 대해 간단한 관광 소개글을 한글로 2~3문장으로 작성해줘."
        response = openai.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7
        )
        return response.choices[0].message.content.strip()
    except Exception as e:
        return f"(GPT 소개 생성 실패: {e})"

# ✅ 관광지 검색 함수 (지역 + 키워드 포함 조건)
def search_places(keywords, region, max_results=10):
    url = "http://apis.data.go.kr/B551011/KorService2/searchKeyword2"
    servicekey = 'ILgc5Ptzlb9mvXO42jF2hHGHNFwLKB77Fmxz8P79ogO7jdZw4su3pG8faMK+B91LNSFgB6wfsUYjqH5TecGX0A=='
    
    collected = []
    for keyword in keywords:
        for page in range(1, 6):  # 최대 5페이지 조회
            params = {
                'MobileOS': 'ETC',
                'MobileApp': 'Soso',
                'serviceKey': servicekey,
                '_type': 'json',
                'numOfRows': 10,
                'pageNo': page,
                'keyword': keyword
            }

            try:
                response = requests.get(url, params=params)
                data = response.json()
                items = data['response']['body']['items']
                if isinstance(items, str):  # 빈 문자열
                    break
                results = items.get('item', [])
                for item in results:
                    title = item.get('title', '')
                    addr1 = item.get('addr1', '')
                    if region in addr1:  # 지역명 포함 여부 확인
                        collected.append({'title': title, 'addr1': addr1})
                        if len(collected) >= max_results:
                            return collected
            except Exception as e:
                print(f"❌ 관광지 API 오류: {e}")
                break
    return collected

# ✅ 메인 실행 함수
def main():
    print("🔍 지역 맞춤형 관광지 추천")

    keyword = input("📌 여행 목적 또는 키워드를 입력하세요 (예: 재충전, 문화 체험 등): ").strip()
    region = input("🌍 여행하고 싶은 지역을 입력하세요 (예: 전주, 부산, 강릉 등): ").strip()

    related = get_related_keywords(keyword)
    print("\n🔎 확장된 키워드:", related)

    places = search_places(related, region, max_results=10)

    if not places:
        print(f"❌ '{region}' 지역에 '{keyword}' 관련 관광지를 찾을 수 없습니다.")
        return

    for i, place in enumerate(places, 1):
        overview = generate_overview(place['title'])
        print(f"\n{i}. 📍 {place['title']}")
        print(f"🗺️ 주소: {place['addr1']}")
        print(f"📝 소개: {overview}")

# ✅ 실행
main()

 

# 사용자 입력 기반 여행지 추천

: 사용자 입력을 프롬프트로 가공하여 GPT 를 통해 관련 여행지 텍스트 정보를 생성

!pip install ipywidgets --quiet

import openai
import ipywidgets as widgets
from IPython.display import display
from datetime import datetime

# ✅ OpenAI API 설정
client = openai.OpenAI(api_key="")		# ⚠️ 본인의 OpenAI 키로 교체하세요!

# ✅ 날짜 → 계절 변환 함수
def date_to_season(date_obj):
    month = date_obj.month
    if month in [3, 4, 5]:
        return "봄"
    elif month in [6, 7, 8]:
        return "여름"
    elif month in [9, 10, 11]:
        return "가을"
    else:
        return "겨울"

# ✅ GPT 프롬프트 생성 함수
def build_prompt(info):
    prompt = f"""
당신은 사용자에게 딱 맞는 여행지를 추천해주는 똑똑한 여행 플래너입니다.
아래 사용자의 정보를 참고하여 지역 기반 여행지와 활동을 추천해주세요.

📅 여행 시기: {info['date']} ({info['season']})
📍 출발지: {info['start_location']} (선호 거리: {info['distance']}km 이내)
🚗 이동 수단: {info['transport']}
🎯 여행 목적: {info['purpose']}
👥 동반자: {info['companion']}
✈️ 여행 스타일: {info['style_type']} - {info['style_detail']}

위 조건을 반영하여 대한민국 소도시 중에서 2~3곳을 추천해주시고, 각 도시에서 할 수 있는 대표적인 활동, 맛집, 사진 스팟 등을 함께 제안해주세요.
"""
    return prompt.strip()

# ✅ GPT 호출 함수
def chat_with_gpt(prompt, model="gpt-4"):
    response = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": "You are a helpful and friendly Korean travel assistant."},
            {"role": "user", "content": prompt}
        ],
        temperature=0.7,
        max_tokens=1000
    )
    return response.choices[0].message.content.strip()

# ✅ 위젯 생성
date_picker = widgets.DatePicker(description="📅 여행 날짜")
start_location = widgets.Text(description="출발 지역")

# ✅ IntSlider로 거리 조절 (10~500, 10단위)
distance = widgets.IntSlider(
    description="이동 거리(km)",
    value=100,
    min=10,
    max=500,
    step=10,
    style={'description_width': 'initial'},
    continuous_update=False
)

transport = widgets.RadioButtons(
    options=["자가용", "고속버스", "기차", "미정"],
    description="🚗 이동 수단",
)

purpose = widgets.RadioButtons(
    options=[
        "마음 정리, 재충전",
        "특별한 사람과 추억",
        "가족과 즐거운 시간",
        "친구들과 액티비티",
        "SNS용 사진",
        "지역 먹거리 체험",
        "문화/역사 경험"
    ],
    description="🎯 목적",
    layout={'width': 'max-content'}
)

companion = widgets.RadioButtons(
    options=["혼자", "연인", "가족", "친구"],
    description="👥 동반자"
)

style = widgets.Dropdown(
    options=["힐링 여행", "액티비티 여행", "관광 명소 여행"],
    description="✈️ 스타일"
)

# 세부 스타일 매핑
style_detail_map = {
    "힐링 여행": ["자연 속", "도심 속"],
    "액티비티 여행": ["수상 레저", "산악 등산", "자전거/ATV", "이색 체험", "실내 체험"],
    "관광 명소 여행": ["역사 유적지", "문화예술 공간", "도시 탐방", "자연 명소"]
}
style_detail = widgets.Dropdown(description="세부 유형")

# 스타일 변경 시 세부 스타일 업데이트
def update_style_detail(*args):
    style_detail.options = style_detail_map.get(style.value, [])

style.observe(update_style_detail, 'value')
update_style_detail()

# ✅ 실행 버튼 정의
submit_button = widgets.Button(description="🚀 여행지 추천받기", button_style='success')
output = widgets.Output()

def on_submit(b):
    with output:
        output.clear_output()
        if not date_picker.value:
            print("❗ 날짜를 선택해주세요.")
            return

        season = date_to_season(date_picker.value)
        user_info = {
            "date": str(date_picker.value),
            "season": season,
            "start_location": start_location.value,
            "distance": distance.value,
            "transport": transport.value,
            "purpose": purpose.value,
            "companion": companion.value,
            "style_type": style.value,
            "style_detail": style_detail.value
        }

        prompt = build_prompt(user_info)
        print(f"📅 여행 시기: {user_info['date']} ({season})")
        print(f"📍 출발지: {user_info['start_location']} (선호 거리: {user_info['distance']}km 이내)")
        print(f"🚗 이동 수단: {user_info['transport']}")
        print(f"🎯 여행 목적: {user_info['purpose']}")
        print(f"👥 동반자: {user_info['companion']}")
        print(f"✈️ 여행 스타일: {user_info['style_type']} - {user_info['style_detail']}")
        print("\n💬 추천 결과:\n")
        print(chat_with_gpt(prompt))

submit_button.on_click(on_submit)

# ✅ 위젯 출력
display(
    date_picker, start_location, distance, transport,
    purpose, companion, style, style_detail,
    submit_button, output
)

 

3. 지역 기반 축제 정보 제공

※ 해당 코드를 실행하려면 아래 두 사이트를 통해

1. 관광데이터포털에서 '한국관광공사_국문 관광정보 서비스_GW' 활용신청 후 인증키를 받아 servicekey 변수에 입력하고,

https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do?publicDataPk=15101578

2. OpenAI Platform에서 신규 API key를 발급받아 api_key 변수에 입력해야한다.

https://platform.openai.com/api-keys

 

# RAG 방식 축제 추천

: GPT 호출 + RAG 구조를 통해 입력 키워드를 확장 및 확장된 키워드에 맞는 축제 추천

# 🔧 설치 (최초 1회만)
!pip install openai pandas requests --quiet

# ✅ 1. 패키지 임포트
import requests
import sqlite3
import pandas as pd
from pandas import json_normalize
from openai import OpenAI

# ✅ 2. OpenAI 클라이언트 생성 (GPT 호출용)
client = OpenAI(api_key="")		# ⚠️ 본인의 OpenAI 키로 교체하세요!

# ✅ 3. SQLite 연결 및 테이블 생성
conn = sqlite3.connect("festivals.db")
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS festivals (
    contentid TEXT PRIMARY KEY,
    title TEXT,
    addr1 TEXT,
    eventstartdate TEXT,
    eventenddate TEXT,
    mapx REAL,
    mapy REAL
)
''')
conn.commit()

# ✅ 4. TourAPI에서 축제 데이터 수집
def fetch_and_store_festivals():
    servicekey = ''		# ⚠️ 본인의 TourAPI 키로 교체하세요!
    url = 'http://apis.data.go.kr/B551011/KorService2/searchFestival2'

    page = 1
    while True:
        print(f"{page} 페이지 수집 중...")
        params = {
            'MobileOS': 'ETC',
            'MobileApp': 'Soso',
            'serviceKey': servicekey,
            '_type': 'json',
            'eventStartDate': '20250601',
            'eventEndDate': '20250831',
            'numOfRows': 100,
            'pageNo': page
        }

        response = requests.get(url, params=params)
        try:
            data = response.json()
        except ValueError:
            print("❌ JSON 파싱 실패:\n", response.text)
            break

        if isinstance(data, str):
            print("❌ 응답이 문자열입니다:\n", data)
            break

        try:
            items = data['response']['body']['items'].get('item', [])
        except Exception as e:
            print("❌ 응답 구조 오류:", e)
            print("전체 응답:\n", data)
            break

        if not items:
            print("✅ 데이터 수집 완료.")
            break

        df = json_normalize(items)

        for _, row in df.iterrows():
            try:
                cursor.execute("""
                    INSERT OR REPLACE INTO festivals 
                    (contentid, title, addr1, eventstartdate, eventenddate, mapx, mapy)
                    VALUES (?, ?, ?, ?, ?, ?, ?)
                """, (
                    str(row.get('contentid')),
                    row.get('title'),
                    row.get('addr1'),
                    row.get('eventstartdate'),
                    row.get('eventenddate'),
                    float(row.get('mapx', 0)) if row.get('mapx') else None,
                    float(row.get('mapy', 0)) if row.get('mapy') else None,
                ))
            except Exception as e:
                print(f"❌ 저장 오류: {e}")

        conn.commit()
        page += 1

# ✅ 5. GPT로 관련 키워드 확장 (RAG 방식의 핵심)
def get_related_keywords(keyword):
    prompt = f"""
여행 관련 축제를 검색하려고 해. '{keyword}'과(와) 의미상 관련된 키워드를 5~7개 정도 추천해줘.
한글 단어만, 쉼표(,)로 구분해서 출력해줘. 예를 들어 '음악'을 입력하면 '재즈, 밴드, 콘서트, EDM, 라이브' 등으로 출력해줘.
"""
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "여행 축제 키워드 추천 도우미입니다."},
            {"role": "user", "content": prompt}
        ],
        temperature=0.7,
        max_tokens=100
    )
    raw = response.choices[0].message.content
    return [k.strip() for k in raw.split(",")]

# ✅ 6. SQLite에서 키워드 기반으로 축제 검색
def search_festivals_by_keywords(keywords):
    placeholders = " OR ".join(["title LIKE ? OR addr1 LIKE ?" for _ in keywords])
    query = f"SELECT title, addr1, eventstartdate, eventenddate FROM festivals WHERE {placeholders}"
    params = []
    for kw in keywords:
        like_kw = f"%{kw}%"
        params.extend([like_kw, like_kw])
    cursor.execute(query, tuple(params))
    return cursor.fetchall()

# ✅ 7. 메인 실행 (지역 기반 추가)
def main():
    # 축제 데이터 수집 (이미 했으면 생략 가능)
    fetch_and_store_festivals()

    keyword = input("\n📌 여행 목적 키워드를 입력하세요 (예: 음악, 전통, 불꽃): ")
    region = input("📍 여행 지역을 입력하세요 (예: 전주, 강릉, 부산 등): ")

    # GPT로 키워드 확장
    related_keywords = get_related_keywords(keyword)
    print("\n🔍 확장된 키워드:", related_keywords)

    # 지역 키워드도 함께 검색 조건에 추가
    all_keywords = related_keywords + [region]

    results = search_festivals_by_keywords(all_keywords)

    if results:
        print(f"\n✅ '{keyword}' 및 '{region}' 관련 축제 목록:")
        for row in results:
            print(f"\n🎉 {row[0]}")
            print(f"📍 장소: {row[1]}")
            print(f"📅 일정: {row[2]} ~ {row[3]}")
    else:
        print(f"\n❌ '{keyword}' 또는 '{region}' 관련 축제를 찾을 수 없습니다.")

    # 마무리
    cursor.close()
    conn.close()

# 실행
main()

 

# 위치 기반 근방 10km 이내 축제 추천

: TourAPI 를 활용하여 반경 10km 이내의 축제 데이터 수집

import requests
import pandas as pd
from pandas import json_normalize
import mysql.connector

# ✅ MySQL 접속 정보 입력
conn = mysql.connector.connect(
    host='localhost',
    user='root',
    password='MyPassword',   
    database='tour_db'
)
cursor = conn.cursor()

# ✅ TourAPI 설정
servicekey = ''		# ⚠️ 본인의 TourAPI 키로 교체하세요!
url = 'http://apis.data.go.kr/B551011/KorService2/searchFestival2'

page = 1
while True:
    print(f"{page} 페이지 수집 중...")

    params = {
        'MobileOS': 'ETC',
        'MobileApp': 'Soso',
        'serviceKey': servicekey,
        '_type': 'json',
        'eventStartDate': '20250602',
        'eventEndDate': '20250610',
        'numOfRows': 100,
        'pageNo': page
    }

    response = requests.get(url, params=params)
    
    try:
        data = response.json()
        if isinstance(data, str):  # 응답이 JSON이 아니라 문자열일 경우
            print("응답이 문자열입니다. 종료합니다.")
            break
        items = data['response']['body']['items'].get('item', [])
    except Exception as e:
        print(f"오류 발생: {e}")
        break


    if not items:
        print("✅ 모든 데이터 수집 완료.")
        break

    df = json_normalize(items)

    # ✅ 행별로 MySQL 저장
    for _, row in df.iterrows():
        try:
            cursor.execute("""
                INSERT INTO festivals (contentid, title, addr1, eventstartdate, eventenddate, mapx, mapy)
                VALUES (%s, %s, %s, %s, %s, %s, %s)
                ON DUPLICATE KEY UPDATE 
                    title = VALUES(title),
                    addr1 = VALUES(addr1),
                    eventstartdate = VALUES(eventstartdate),
                    eventenddate = VALUES(eventenddate),
                    mapx = VALUES(mapx),
                    mapy = VALUES(mapy)
            """, (
                row.get('contentid'),
                row.get('title'),
                row.get('addr1'),
                row.get('eventstartdate'),
                row.get('eventenddate'),
                float(row.get('mapx', 0)),
                float(row.get('mapy', 0)),
            ))
        except Exception as insert_error:
            print(f"❌ 삽입 오류: {insert_error}")

    conn.commit()
    page += 1

# 마무리
cursor.close()
conn.close()
print(" MySQL 저장 완료")
#여기까지 데이터 받아오기

from geopy.distance import geodesic
import mysql.connector

def fetch_festivals_from_mysql():
    # ✅ MySQL 연결 정보
    conn = mysql.connector.connect(
        host='localhost',
        user='root',
        password='MyPassword', 
        database='tour_db'
    )
    cursor = conn.cursor(dictionary=True)  # 딕셔너리 모드로 가져오기

    cursor.execute("SELECT title, addr1, eventstartdate, eventenddate, mapy AS lat, mapx AS lng FROM festivals")
    festivals = cursor.fetchall()

    cursor.close()
    conn.close()
    return festivals

def main():
    print("🎯 내 위치 기반 축제 검색기 (MySQL)")
    lat = float(input("📍 현재 위도 입력: "))
    lng = float(input("📍 현재 경도 입력: "))

    festivals = fetch_festivals_from_mysql()

    nearby = []
    for f in festivals:
        if f["lat"] is None or f["lng"] is None:
            continue  # 좌표 없는 데이터는 제외
        dist = geodesic((lat, lng), (f["lat"], f["lng"])).km
        if dist <= 10:
            f["distance_km"] = round(dist, 2)
            nearby.append(f)

    if not nearby:
        print("❌ 10km 이내에 축제가 없습니다.")
        return

    print(f"\n✅ 내 위치 기준 10km 이내 축제 {len(nearby)}건:")
    for f in sorted(nearby, key=lambda x: x["distance_km"]):
        print(f"\n🎉 {f['title']}")
        print(f"   📍 장소: {f['addr1']}")
        print(f"   📅 기간: {f['eventstartdate']} ~ {f['eventenddate']}")
        print(f"   🧭 거리: {f['distance_km']}km")

if __name__ == "__main__":
    main()

 

4. 로컬 특산물 등록 및 구매/판매

※ 아래 코드블럭별로 구분된대로 코랩에서의 별도의 셀이라 이해하면 된다.

 

# 로컬 특산물 정보 제공 (위젯사용ver)

: 지역 특산물에 대해 판매자-구매자 연결을 위한 데모 기능 구현 코드

# 📌 로컬 특산물 판매 데모 (게시물 관리 + 리뷰 + 인증 + 되돌아가기 포함)
import pandas as pd

# 데이터 저장소
sellers = []
products = []
reviews = {}

# 판매자 등록
def register_seller(name, address, business_id, manager, phone):
    if any(s["name"] == name and s["business_id"] == business_id for s in sellers):
        return f"⚠️ 이미 등록된 판매자입니다: {name}"
    sellers.append({
        "name": name,
        "address": address,
        "business_id": business_id,
        "manager": manager,
        "phone": phone
    })
    return f"✅ 판매자 등록 완료: {name} ({business_id})"

# 판매자 인증
def authenticate_seller(name, business_id):
    return any(s["name"] == name and s["business_id"] == business_id for s in sellers)

# 제품 등록
def register_product(seller_name, product_name, price, description):
    product = {
        "판매자": seller_name,
        "상품명": product_name,
        "가격": price,
        "설명": description,
        "상태": "판매중"
    }
    products.append(product)
    return f"✅ 상품 '{product_name}' 등록 완료!"

# 제품 보기
def view_products():
    if not products:
        print("🔍 등록된 상품이 없습니다.")
        return
    df = pd.DataFrame(products).reset_index().rename(columns={"index": "번호"})
    print(df[["번호", "상품명", "판매자", "가격", "상태"]])
    selection_input = input("상세히 볼 상품 번호를 선택하세요 (또는 'b' 입력 시 이전으로): ").strip()
    if selection_input.lower() == 'b':
        return
    try:
        selection = int(selection_input)
        if 0 <= selection < len(products):
            product = products[selection]
            print("\n📦 상품 상세 정보:")
            for k, v in product.items():
                print(f"{k}: {v}")
            # 리뷰 출력
            product_key = product["상품명"]
            product_reviews = reviews.get(product_key, [])
            print("\n📝 리뷰:")
            if product_reviews:
                for idx, r in enumerate(product_reviews):
                    print(f"{idx+1}. {r}")
            else:
                print("등록된 리뷰가 없습니다.")
            # 채팅 여부
            chat = input("🤝 판매자와 채팅하시겠습니까? (y/n): ").strip().lower()
            if chat == "y":
                seller_info = next(s for s in sellers if s["name"] == product["판매자"])
                print(f"📞 연결 완료! {seller_info['manager']}님에게 연락하세요: {seller_info['phone']}")
            else:
                print("❌ 채팅을 취소했습니다.")
        else:
            print("❗유효하지 않은 번호입니다.")
    except:
        print("❗잘못된 입력입니다.")

# 리뷰 쓰기
def write_review():
    keyword = input("리뷰를 남길 상품명을 입력하세요 (또는 'b' 입력 시 이전으로): ").strip()
    if keyword.lower() == 'b':
        return
    matches = [p for p in products if keyword in p["상품명"]]
    if not matches:
        print("❌ 해당 상품을 찾을 수 없습니다.")
        return
    print("\n🔍 검색 결과:")
    for idx, m in enumerate(matches):
        print(f"{idx}: {m['상품명']} (판매자: {m['판매자']})")
    selection_input = input("리뷰를 남길 상품 번호를 선택하세요: ").strip()
    try:
        idx = int(selection_input)
        selected_product = matches[idx]
        content = input("✍ 리뷰를 작성해주세요: ")
        key = selected_product["상품명"]
        if key not in reviews:
            reviews[key] = []
        reviews[key].append(content)
        print("✅ 리뷰가 저장되었습니다.")
    except:
        print("❗잘못된 입력입니다.")

# 게시물 관리
def manage_posts():
    print("\n🔐 게시물 관리를 위해 판매자 인증이 필요합니다.")
    name = input("상호명: ")
    if name.lower() == 'b': return
    business_id = input("사업자등록번호: ")
    if business_id.lower() == 'b': return
    if not authenticate_seller(name, business_id):
        print("❌ 판매자 인증 실패")
        return
    my_products = [p for p in products if p["판매자"] == name]
    if not my_products:
        print("📭 등록한 상품이 없습니다.")
        return
    print("\n📋 나의 등록 상품 목록:")
    for idx, p in enumerate(my_products):
        print(f"{idx}: {p['상품명']} / 상태: {p['상태']} / 가격: {p['가격']}")
    sel_input = input("수정/삭제할 상품 번호 선택 (또는 'b' 입력 시 이전으로): ").strip()
    if sel_input.lower() == 'b': return
    try:
        sel_idx = int(sel_input)
        selected = my_products[sel_idx]
        print("\n1. 상품 정보 수정")
        print("2. 판매 상태 변경")
        print("3. 상품 삭제")
        action = input("수행할 작업 번호 선택: ").strip()
        if action == '1':
            new_name = input(f"새 상품명 입력 (Enter=변경 없음): ").strip()
            new_price = input(f"새 가격 입력 (Enter=변경 없음): ").strip()
            new_desc = input(f"새 설명 입력 (Enter=변경 없음): ").strip()
            if new_name: selected["상품명"] = new_name
            if new_price: selected["가격"] = int(new_price)
            if new_desc: selected["설명"] = new_desc
            print("✅ 상품 정보가 수정되었습니다.")
        elif action == '2':
            print("선택 가능한 상태: 판매중 / 품절 / 예약중")
            new_state = input("새 판매 상태 입력: ").strip()
            if new_state in ["판매중", "품절", "예약중"]:
                selected["상태"] = new_state
                print(f"✅ 상태가 '{new_state}'(으)로 변경되었습니다.")
            else:
                print("❌ 유효하지 않은 상태입니다.")
        elif action == '3':
            products.remove(selected)
            print("🗑️ 상품이 삭제되었습니다.")
        else:
            print("❗잘못된 입력입니다.")
    except:
        print("❗입력 오류입니다.")

# 메인 루프
while True:
    print("\n🛍️ 무엇을 하시겠습니까?")
    print("1: 판매자 등록")
    print("2: 제품 등록")
    print("3: 제품 보기")
    print("4: 리뷰 쓰기")
    print("5: 게시물 관리")
    print("0: 종료")
    choice = input("번호 입력: ").strip()

    if choice == "1":
        print("\n👤 판매자 등록 (이전: 'b')")
        name = input("상호명: ")
        if name.lower() == 'b': continue
        address = input("주소: ")
        if address.lower() == 'b': continue
        business_id = input("사업자등록번호: ")
        if business_id.lower() == 'b': continue
        manager = input("담당자 이름: ")
        if manager.lower() == 'b': continue
        phone = input("가게 연락처: ")
        if phone.lower() == 'b': continue
        print(register_seller(name, address, business_id, manager, phone))

    elif choice == "2":
        print("\n📦 제품 등록 - 판매자 인증 (이전: 'b')")
        name = input("상호명: ")
        if name.lower() == 'b': continue
        business_id = input("사업자등록번호: ")
        if business_id.lower() == 'b': continue
        if authenticate_seller(name, business_id):
            product_name = input("상품명: ")
            if product_name.lower() == 'b': continue
            price_input = input("가격 (숫자): ")
            if price_input.lower() == 'b': continue
            try:
                price = int(price_input)
            except:
                print("❗유효한 숫자가 아닙니다.")
                continue
            description = input("상품 설명: ")
            if description.lower() == 'b': continue
            print(register_product(name, product_name, price, description))
        else:
            print("❌ 판매자 인증 실패. 등록된 판매자만 제품을 등록할 수 있습니다.")

    elif choice == "3":
        print("\n📋 등록된 제품 보기")
        view_products()

    elif choice == "4":
        print("\n✍ 리뷰 작성")
        write_review()

    elif choice == "5":
        print("\n🛠️ 게시물 관리")
        manage_posts()

    elif choice == "0":
        print("👋 종료합니다.")
        break

    else:
        print("❗잘못된 입력입니다. 다시 시도해주세요.")

 

 

💡위 코드들에 대한 결과 및 전체 시연영상은 아래 링크에서 확인할 수 있다.

 

데모영상

https://youtu.be/dMyhT8ioe9s?si=xKmXJQUNNJlcuILl

 

 

 


다음 글에서는..

스타트 최종보고서 작성과 기말 심층인터뷰의 후기를 기록으로 남겨보도록 하겠다.