Chat

코드에만 신경 쓰게 하게 하는 방법

cs newbie 2025. 6. 26. 03:39

게임 시스템, 툴, 유틸리티 등 여러 기능을 '플러그인'으로 붙이고 싶다면?
게다가 그 플러그인들이 Python뿐 아니라 Go, JS, C#, Rust처럼 다양한 언어로 구현되어도 동작하게 만들고 싶다면?

오늘은 그런 구조를 직접 만들어 봅니다.


🎯 목표

  • plugin/ 폴더에 어떤 실행 가능한 파일(.py, .exe 등)을 넣더라도
  • main.py가 이를 탐색해 JSON으로 통신하며 실행
  • 기능은 확장 가능하고 언어에 구애받지 않음!

📁 프로젝트 구조

my_game_project/
├── main.py                # 메인 프로그램 (Python으로 구현)
└── plugin/                # 플러그인 모음 폴더
    ├── hunt.py            # 플러그인 1: 사냥 기능 (JSON 입출력 기반)
    └── chat.py            # 플러그인 2: 채팅 기능 (JSON 입출력 기반)

🔧 1. main.py - 플러그인 실행 관리자

# main.py

import os
import json
import subprocess

PLUGIN_DIR = "plugin"

def load_plugins():
    """
    plugin 폴더 안에 있는 실행 가능한 파일(.py, .exe 등)을 찾아
    딕셔너리로 저장 (이름: 실행 경로)
    """
    plugins = {}
    for filename in os.listdir(PLUGIN_DIR):
        # .py 또는 .exe 등 실행 가능한 파일만 등록
        if filename.endswith(".py") or filename.endswith(".exe"):
            name = filename.split('.')[0]  # 파일명에서 확장자 제거
            path = os.path.join(PLUGIN_DIR, filename)
            plugins[name] = path
    return plugins

def run_plugin(executable_path, input_data):
    """
    실행 파일을 외부 프로세스로 실행,
    JSON 문자열을 입력(stdin), JSON 결과를 출력(stdout)으로 받음
    """
    input_json = json.dumps(input_data)

    # 플러그인 실행
    proc = subprocess.Popen(
        ["python", executable_path],  # Python 파일 실행, 다른 언어면 실행기 바꿔야 함
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )
    stdout, stderr = proc.communicate(input=input_json)

    if proc.returncode != 0:
        print(f"[오류] 플러그인 실행 실패: {stderr.strip()}")
        return None

    try:
        return json.loads(stdout)
    except Exception as e:
        print(f"[오류] 플러그인 JSON 파싱 실패: {e}")
        return None

def main():
    # 초기 게임 데이터 예시
    game_data = {
        "player": {"name": "용사", "level": 1, "hp": 100}
    }

    plugins = load_plugins()

    while True:
        print("\n=== 메인 메뉴 ===")
        for idx, name in enumerate(plugins.keys(), 1):
            print(f"{idx}. {name}")
        print(f"{len(plugins)+1}. 종료")

        choice = input("선택: ")
        if not choice.isdigit():
            print("숫자를 입력하세요.")
            continue

        choice = int(choice)

        if choice == len(plugins) + 1:
            print("게임을 종료합니다.")
            break

        if 1 <= choice <= len(plugins):
            plugin_name = list(plugins.keys())[choice - 1]
            path = plugins[plugin_name]
            print(f"[실행] {plugin_name}...")

            result = run_plugin(path, game_data)

            if result:
                print("> 결과:", result.get("msg", "메시지 없음"))
                if "game_data" in result:
                    game_data = result["game_data"]
        else:
            print("잘못된 선택입니다.")

if __name__ == "__main__":
    main()

🧩 2. plugin/hunt.py - 사냥 기능 (외부 실행 JSON 기반)

# plugin/hunt.py

import sys
import json

def main():
    # 표준입력(stdin)으로 들어온 JSON 문자열을 읽고 파싱
    input_json = sys.stdin.read()
    data = json.loads(input_json)

    # 플레이어 데이터 추출 및 레벨업 처리
    player = data.get("player", {})
    player["level"] = player.get("level", 1) + 1

    result = {
        "success": True,
        "msg": f"[사냥 완료] {player.get('name')}님이 레벨 {player['level']}이 되었습니다!",
        "game_data": {"player": player}
    }

    # 결과를 JSON으로 표준출력(stdout)에 출력
    print(json.dumps(result))

if __name__ == "__main__":
    main()

💬 3. plugin/chat.py - 채팅 기능 (입장 메시지만)

# plugin/chat.py

import sys
import json

def main():
    input_json = sys.stdin.read()
    data = json.loads(input_json)

    player = data.get("player", {})
    name = player.get("name", "???")

    result = {
        "success": True,
        "msg": f"[채팅방 입장] {name}님이 채팅방에 입장했습니다.",
        "game_data": {"player": player}
    }

    print(json.dumps(result))

if __name__ == "__main__":
    main()

✅ 실행 예시

$ python main.py

=== 메인 메뉴 ===
1. hunt
2. chat
3. 종료
선택: 1
[실행] hunt...
> 결과: [사냥 완료] 용사님이 레벨 2이 되었습니다!

선택: 2
[실행] chat...
> 결과: [채팅방 입장] 용사님이 채팅방에 입장했습니다.

🚀 이 구조의 장점

항목 설명

언어 독립 JSON 입출력만 지키면 어떤 언어로든 플러그인 제작 가능
확장 쉬움 plugin 폴더에 파일 추가만 해도 자동 등록됨
유지보수 편리 각 기능은 별도 파일/언어로 완전히 분리됨
실제 게임 구조와 유사 JSON으로 게임 상태 주고받는 방식이 서버 구조와 흡사함

📌 마무리

이런 구조는 단순한 테스트 용도뿐 아니라
실제 게임 서버, 유틸 앱, 업무 자동화 시스템에도 응용 가능합니다.

다양한 언어로 만든 기능들을 유연하게 연결하고 싶다면
**"표준 입출력 + JSON"**이라는 간단한 규칙만으로도 강력한 시스템을 만들 수 있습니다.