KR_ProgramIO - somaz94/python-study GitHub Wiki

Python ํ”„๋กœ๊ทธ๋žจ ์ž…์ถœ๋ ฅ(Program I/O) ๊ฐœ๋… ์ •๋ฆฌ


1๏ธโƒฃ ๋ช…๋ น์ค„ ์ธ์ˆ˜ (Command Line Arguments)

ํŒŒ์ด์ฌ ํ”„๋กœ๊ทธ๋žจ์€ sys.argv๋ฅผ ํ†ตํ•ด ๋ช…๋ น์ค„ ์ธ์ˆ˜๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

# ๊ธฐ๋ณธ ๋ช…๋ น์ค„ ์ธ์ˆ˜ ์ฒ˜๋ฆฌ
import sys

def main():
    # argv[0]์€ ์Šคํฌ๋ฆฝํŠธ ์ด๋ฆ„
    # argv[1:]์€ ์‹ค์ œ ์ธ์ˆ˜๋“ค
    args = sys.argv[1:]
    print(f"ํ”„๋กœ๊ทธ๋žจ ์ด๋ฆ„: {sys.argv[0]}")
    print(f"์ „๋‹ฌ๋œ ์ธ์ˆ˜ ๊ฐœ์ˆ˜: {len(args)}")
    
    for i, arg in enumerate(args, 1):
        print(f"์ธ์ˆ˜ {i}: {arg}")

if __name__ == "__main__":
    main()

๋ช…๋ น์ค„ ์ธ์ˆ˜ ์˜ˆ์‹œ

# example_args.py
import sys

def main():
    if len(sys.argv) < 2:
        print("์‚ฌ์šฉ๋ฒ•: python example_args.py [์ธ์ˆ˜1] [์ธ์ˆ˜2] ...")
        sys.exit(1)
    
    args = sys.argv[1:]
    
    # ์ธ์ˆ˜์˜ ํ•ฉ๊ณ„ ๊ณ„์‚ฐ (๋ชจ๋“  ์ธ์ˆ˜๋ฅผ ์ˆซ์ž๋กœ ๋ณ€ํ™˜ ์‹œ๋„)
    try:
        numbers = [float(arg) for arg in args]
        total = sum(numbers)
        print(f"์ธ์ˆ˜ ํ•ฉ๊ณ„: {total}")
    except ValueError:
        print("์ˆซ์ž๊ฐ€ ์•„๋‹Œ ์ธ์ˆ˜๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.")

if __name__ == "__main__":
    main()

์‹คํ–‰ ์˜ˆ์‹œ:

$ python example_args.py 10 20 30
ํ”„๋กœ๊ทธ๋žจ ์ด๋ฆ„: example_args.py
์ „๋‹ฌ๋œ ์ธ์ˆ˜ ๊ฐœ์ˆ˜: 3
์ธ์ˆ˜ ํ•ฉ๊ณ„: 60.0


2๏ธโƒฃ argparse ๋ชจ๋“ˆ ์‚ฌ์šฉ

๋” ๋ณต์žกํ•œ ๋ช…๋ น์ค„ ์ธ์ˆ˜ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด argparse ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

import argparse

def main():
    # ํŒŒ์„œ ์ƒ์„ฑ
    parser = argparse.ArgumentParser(description='ํ”„๋กœ๊ทธ๋žจ ์„ค๋ช…')
    
    # ์ธ์ˆ˜ ์ถ”๊ฐ€
    parser.add_argument('--name', type=str, help='์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”')
    parser.add_argument('--age', type=int, help='๋‚˜์ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”')
    
    # ์ธ์ˆ˜ ํŒŒ์‹ฑ
    args = parser.parse_args()
    
    # ์ธ์ˆ˜ ์‚ฌ์šฉ
    if args.name:
        print(f"์ด๋ฆ„: {args.name}")
    if args.age:
        print(f"๋‚˜์ด: {args.age}")

if __name__ == "__main__":
    main()

argparse ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ

import argparse

def main():
    # ํŒŒ์„œ ์ƒ์„ฑ (ํ”„๋กœ๊ทธ๋žจ ์„ค๋ช… ๋ฐ ์—ํ•„๋กœ๊ทธ ์ถ”๊ฐ€)
    parser = argparse.ArgumentParser(
        description='์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ',
        epilog='์˜ˆ์‹œ: python program.py --name ํ™๊ธธ๋™ --age 30 --hobbies ๋…์„œ ๋“ฑ์‚ฐ'
    )
    
    # ํ•„์ˆ˜ ์œ„์น˜ ์ธ์ˆ˜
    parser.add_argument('command', choices=['create', 'update', 'delete'],
                       help='์ˆ˜ํ–‰ํ•  ๋ช…๋ น (create, update, delete)')
                       
    # ์˜ต์…˜ ์ธ์ˆ˜ (์„ ํƒ์ )
    parser.add_argument('--name', '-n', type=str, help='์‚ฌ์šฉ์ž ์ด๋ฆ„')
    parser.add_argument('--age', '-a', type=int, help='์‚ฌ์šฉ์ž ๋‚˜์ด')
    
    # ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ๋Š” ์ธ์ˆ˜
    parser.add_argument('--language', default='Python', help='์„ ํ˜ธํ•˜๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด (๊ธฐ๋ณธ๊ฐ’: Python)')
    
    # bool ํ˜•์‹์˜ ํ”Œ๋ž˜๊ทธ ์ธ์ˆ˜
    parser.add_argument('--verbose', '-v', action='store_true', help='์ƒ์„ธ ์ถœ๋ ฅ ๋ชจ๋“œ ํ™œ์„ฑํ™”')
    
    # ํŒŒ์ผ ํƒ€์ž… ์ธ์ˆ˜
    parser.add_argument('--output', type=argparse.FileType('w'), help='์ถœ๋ ฅ ํŒŒ์ผ')
    
    # ์—ฌ๋Ÿฌ ๊ฐ’์„ ๋ฐ›๋Š” ์ธ์ˆ˜
    parser.add_argument('--hobbies', nargs='+', help='์ทจ๋ฏธ ๋ชฉ๋ก (๊ณต๋ฐฑ์œผ๋กœ ๊ตฌ๋ถ„)')
    
    # ๊ณ ์ •๋œ ๊ฐœ์ˆ˜์˜ ์ธ์ˆ˜
    parser.add_argument('--coordinates', nargs=2, type=float, help='x, y ์ขŒํ‘œ (2๊ฐœ์˜ ๊ฐ’)')
    
    # ์ธ์ˆ˜ ํŒŒ์‹ฑ
    args = parser.parse_args()
    
    # ์ƒ์„ธ ์ถœ๋ ฅ ๋ชจ๋“œ์ผ ๊ฒฝ์šฐ ๋ชจ๋“  ์ธ์ˆ˜ ์ถœ๋ ฅ
    if args.verbose:
        print("ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์ •๋ณด:")
        for arg, value in vars(args).items():
            print(f"  {arg}: {value}")
    
    # ๋ช…๋ น ์ฒ˜๋ฆฌ
    print(f"๋ช…๋ น '{args.command}' ์‹คํ–‰ ์ค‘...")
    
    # ์ด๋ฆ„๊ณผ ๋‚˜์ด๊ฐ€ ๋ชจ๋‘ ์ œ๊ณต๋œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉ์ž ์ •๋ณด ์ถœ๋ ฅ
    if args.name and args.age:
        print(f"์‚ฌ์šฉ์ž: {args.name}, {args.age}์„ธ")
        print(f"์„ ํ˜ธ ์–ธ์–ด: {args.language}")
        
        if args.hobbies:
            print(f"์ทจ๋ฏธ: {', '.join(args.hobbies)}")
            
        if args.coordinates:
            print(f"์ขŒํ‘œ: ({args.coordinates[0]}, {args.coordinates[1]})")
    
    # ์ถœ๋ ฅ ํŒŒ์ผ์ด ์ œ๊ณต๋œ ๊ฒฝ์šฐ ํŒŒ์ผ์— ์ถœ๋ ฅ
    if args.output:
        args.output.write(f"์ฒ˜๋ฆฌ๋œ ๋ช…๋ น: {args.command}\n")
        if args.name:
            args.output.write(f"์‚ฌ์šฉ์ž: {args.name}\n")
        args.output.close()
        print(f"์ถœ๋ ฅ์ด ํŒŒ์ผ์— ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")

if __name__ == "__main__":
    main()

argparse ์„œ๋ธŒ๋ช…๋ น์–ด

import argparse

def create_user(args):
    print(f"์‚ฌ์šฉ์ž ์ƒ์„ฑ: {args.name}, {args.age}์„ธ")

def update_user(args):
    print(f"์‚ฌ์šฉ์ž ์—…๋ฐ์ดํŠธ: ID {args.id}")
    if args.name:
        print(f"์ด๋ฆ„ ๋ณ€๊ฒฝ: {args.name}")
    if args.age:
        print(f"๋‚˜์ด ๋ณ€๊ฒฝ: {args.age}")

def delete_user(args):
    print(f"์‚ฌ์šฉ์ž ์‚ญ์ œ: ID {args.id}")
    if args.force:
        print("๊ฐ•์ œ ์‚ญ์ œ ๋ชจ๋“œ")

def main():
    # ๋ฉ”์ธ ํŒŒ์„œ ์ƒ์„ฑ
    parser = argparse.ArgumentParser(description='์‚ฌ์šฉ์ž ๊ด€๋ฆฌ ์‹œ์Šคํ…œ')
    subparsers = parser.add_subparsers(dest='command', help='์ˆ˜ํ–‰ํ•  ๋ช…๋ น')
    
    # create ์„œ๋ธŒ๋ช…๋ น์–ด ์„ค์ •
    create_parser = subparsers.add_parser('create', help='์‚ฌ์šฉ์ž ์ƒ์„ฑ')
    create_parser.add_argument('--name', required=True, help='์‚ฌ์šฉ์ž ์ด๋ฆ„')
    create_parser.add_argument('--age', type=int, required=True, help='์‚ฌ์šฉ์ž ๋‚˜์ด')
    create_parser.set_defaults(func=create_user)
    
    # update ์„œ๋ธŒ๋ช…๋ น์–ด ์„ค์ •
    update_parser = subparsers.add_parser('update', help='์‚ฌ์šฉ์ž ์ •๋ณด ์—…๋ฐ์ดํŠธ')
    update_parser.add_argument('--id', type=int, required=True, help='์‚ฌ์šฉ์ž ID')
    update_parser.add_argument('--name', help='์ƒˆ ์ด๋ฆ„')
    update_parser.add_argument('--age', type=int, help='์ƒˆ ๋‚˜์ด')
    update_parser.set_defaults(func=update_user)
    
    # delete ์„œ๋ธŒ๋ช…๋ น์–ด ์„ค์ •
    delete_parser = subparsers.add_parser('delete', help='์‚ฌ์šฉ์ž ์‚ญ์ œ')
    delete_parser.add_argument('--id', type=int, required=True, help='์‚ฌ์šฉ์ž ID')
    delete_parser.add_argument('--force', '-f', action='store_true', help='ํ™•์ธ ์—†์ด ๊ฐ•์ œ ์‚ญ์ œ')
    delete_parser.set_defaults(func=delete_user)
    
    # ์ธ์ˆ˜ ํŒŒ์‹ฑ
    args = parser.parse_args()
    
    # ์„œ๋ธŒ๋ช…๋ น์–ด์— ๋”ฐ๋ฅธ ํ•จ์ˆ˜ ์‹คํ–‰
    if hasattr(args, 'func'):
        args.func(args)
    else:
        parser.print_help()

if __name__ == "__main__":
    main()


3๏ธโƒฃ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ

import os

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ฝ๊ธฐ
path = os.environ.get('PATH')
home = os.environ.get('HOME')

# ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ฝ๊ธฐ
user = os.environ.get('USER', 'unknown')
debug = os.environ.get('DEBUG', 'False')

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
os.environ['MY_VAR'] = 'value'
os.environ['APP_ENV'] = 'development'

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ญ์ œ
if 'TEMP_VAR' in os.environ:
    del os.environ['TEMP_VAR']

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํ™œ์šฉ ์˜ˆ์‹œ
debug_mode = os.environ.get('DEBUG', 'False').lower() in ('true', '1', 'yes')
if debug_mode:
    print("๋””๋ฒ„๊ทธ ๋ชจ๋“œ๊ฐ€ ํ™œ์„ฑํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— ๋”ฐ๋ฅธ ์„ค์ • ๋ณ€๊ฒฝ
env = os.environ.get('APP_ENV', 'development')
if env == 'production':
    db_host = os.environ.get('DB_HOST_PROD')
    log_level = 'ERROR'
elif env == 'staging':
    db_host = os.environ.get('DB_HOST_STAGING')
    log_level = 'WARNING'
else:  # development
    db_host = os.environ.get('DB_HOST_DEV', 'localhost')
    log_level = 'DEBUG'

print(f"ํ™˜๊ฒฝ: {env}, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ˜ธ์ŠคํŠธ: {db_host}, ๋กœ๊ทธ ๋ ˆ๋ฒจ: {log_level}")

# ๋ชจ๋“  ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ถœ๋ ฅ
print("\n--- ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋ชฉ๋ก ---")
for key, value in sorted(os.environ.items()):
    # ๊ฐ’์ด ๋„ˆ๋ฌด ๊ธธ๋ฉด ์ž˜๋ผ์„œ ํ‘œ์‹œ
    if len(value) > 50:
        value = value[:47] + "..."
    print(f"{key}: {value}")

dotenv ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ

# pip install python-dotenv๋กœ ๋จผ์ € ์„ค์น˜ ํ•„์š”
from dotenv import load_dotenv
import os

# .env ํŒŒ์ผ ๋กœ๋“œ
load_dotenv()  # ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์˜ .env ํŒŒ์ผ ๋กœ๋“œ
# load_dotenv("/path/to/specific/.env")  # ํŠน์ • ๊ฒฝ๋กœ์˜ .env ํŒŒ์ผ ๋กœ๋“œ

# ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ
database_url = os.environ.get("DATABASE_URL")
api_key = os.environ.get("API_KEY")
debug = os.environ.get("DEBUG", "False").lower() == "true"

print(f"๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค URL: {database_url}")
print(f"API ํ‚ค: {api_key}")
print(f"๋””๋ฒ„๊ทธ ๋ชจ๋“œ: {debug}")

# .env ํŒŒ์ผ ์˜ˆ์‹œ:
# DATABASE_URL=postgresql://user:password@localhost/dbname
# API_KEY=your_secret_api_key
# DEBUG=True


4๏ธโƒฃ ํ‘œ์ค€ ์ž…์ถœ๋ ฅ ๋ฆฌ๋‹ค์ด๋ ‰์…˜

import sys

# ํ‘œ์ค€ ์ถœ๋ ฅ์„ ํŒŒ์ผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜
with open('output.txt', 'w', encoding='utf-8') as f:
    # ์›๋ž˜ ํ‘œ์ค€ ์ถœ๋ ฅ ์ €์žฅ
    original_stdout = sys.stdout
    sys.stdout = f
    
    print("์ด ๋‚ด์šฉ์€ ํŒŒ์ผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.")
    print("์—ฌ๋Ÿฌ ์ค„์˜ ์ถœ๋ ฅ์ด")
    print("๋ชจ๋‘ ํŒŒ์ผ์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.")
    
    # ํ‘œ์ค€ ์ถœ๋ ฅ ๋ณต๊ตฌ
    sys.stdout = original_stdout

print("์ด์ œ ์ฝ˜์†”์— ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.")

# ํ‘œ์ค€ ์˜ค๋ฅ˜ ๋ฆฌ๋‹ค์ด๋ ‰์…˜
with open('error.log', 'w', encoding='utf-8') as f:
    original_stderr = sys.stderr
    sys.stderr = f
    
    print("์—๋Ÿฌ ๋ฉ”์‹œ์ง€", file=sys.stderr)
    
    sys.stderr = original_stderr

# ํ‘œ์ค€ ์ž…๋ ฅ์„ ํŒŒ์ผ์—์„œ ์ฝ๊ธฐ
with open('input.txt', 'r', encoding='utf-8') as f:
    original_stdin = sys.stdin
    sys.stdin = f
    
    # ํŒŒ์ผ์—์„œ ์ž…๋ ฅ ์ฝ๊ธฐ
    line = input("ํ”„๋กฌํ”„ํŠธ๋Š” ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค: ")
    print(f"ํŒŒ์ผ์—์„œ ์ฝ์€ ์ค„: {line}")
    
    # ์—ฌ๋Ÿฌ ์ค„ ์ฝ๊ธฐ
    lines = []
    try:
        while True:
    line = input()
            lines.append(line)
    except EOFError:
        pass
    
    sys.stdin = original_stdin

print(f"ํŒŒ์ผ์—์„œ ์ด {len(lines)}์ค„์„ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค.")

์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž๋ฅผ ์‚ฌ์šฉํ•œ ํ‘œ์ค€ ์ŠคํŠธ๋ฆผ ๋ฆฌ๋‹ค์ด๋ ‰์…˜

import sys
import contextlib

# ํ‘œ์ค€ ์ถœ๋ ฅ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž
@contextlib.contextmanager
def redirect_stdout(new_target):
    old_target = sys.stdout
    sys.stdout = new_target
    try:
        yield new_target
    finally:
        sys.stdout = old_target

# ํ‘œ์ค€ ์ž…๋ ฅ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ์ž
@contextlib.contextmanager
def redirect_stdin(new_target):
    old_target = sys.stdin
    sys.stdin = new_target
    try:
        yield new_target
    finally:
        sys.stdin = old_target

# ์‚ฌ์šฉ ์˜ˆ์‹œ
with open('output.txt', 'w', encoding='utf-8') as f:
    with redirect_stdout(f):
        print("์ด ๋‚ด์šฉ์€ ํŒŒ์ผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜๋ฉ๋‹ˆ๋‹ค.")
        print("์ปจํ…์ŠคํŠธ๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด ์ž๋™์œผ๋กœ ๋ณต๊ตฌ๋ฉ๋‹ˆ๋‹ค.")

print("์ด์ œ ๋‹ค์‹œ ์ฝ˜์†”์— ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.")

# ๋ฌธ์ž์—ด ๋ฒ„ํผ๋กœ ํ‘œ์ค€ ์ถœ๋ ฅ ๋ฆฌ๋‹ค์ด๋ ‰์…˜
from io import StringIO

output_buffer = StringIO()
with redirect_stdout(output_buffer):
    print("์ด ๋‚ด์šฉ์€ ๋ฒ„ํผ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.")
    print("๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")

# ์บก์ณ๋œ ์ถœ๋ ฅ ๊ฐ€์ ธ์˜ค๊ธฐ
captured_output = output_buffer.getvalue()
print(f"์บก์ณ๋œ ์ถœ๋ ฅ: {captured_output}")


5๏ธโƒฃ ๋กœ๊น… (Logging)

import logging

# ๊ธฐ๋ณธ ๋กœ๊น… ์„ค์ •
logging.basicConfig(
    level=logging.INFO,  # ๋กœ๊น… ๋ ˆ๋ฒจ ์„ค์ •
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ
logging.debug("๋””๋ฒ„๊ทธ ๋ฉ”์‹œ์ง€ - ๊ฐœ๋ฐœ ์ค‘์—๋งŒ ํ•„์š”ํ•œ ์„ธ๋ถ€ ์ •๋ณด")
logging.info("์ •๋ณด ๋ฉ”์‹œ์ง€ - ์ •์ƒ ์ž‘๋™ ํ™•์ธ")
logging.warning("๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€ - ์ž ์žฌ์  ๋ฌธ์ œ ์•Œ๋ฆผ")
logging.error("์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ - ํ”„๋กœ๊ทธ๋žจ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์Œ")
logging.critical("์‹ฌ๊ฐํ•œ ์˜ค๋ฅ˜ - ํ”„๋กœ๊ทธ๋žจ์ด ๊ณ„์† ์‹คํ–‰๋  ์ˆ˜ ์—†์Œ")

# ๋กœ๊ทธ ๋ ˆ๋ฒจ (๋‚ฎ์€ ๊ฒƒ๋ถ€ํ„ฐ ๋†’์€ ์ˆœ)
# DEBUG(10) < INFO(20) < WARNING(30) < ERROR(40) < CRITICAL(50)

ํŒŒ์ผ ๋กœ๊น… ๋ฐ ๊ณ ๊ธ‰ ์„ค์ •

import logging
import logging.handlers
import os
from datetime import datetime

# ๋กœ๊ทธ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
log_dir = 'logs'
if not os.path.exists(log_dir):
    os.makedirs(log_dir)

# ํ˜„์žฌ ๋‚ ์งœ๋กœ ๋กœ๊ทธ ํŒŒ์ผ ์ด๋ฆ„ ์ƒ์„ฑ
today = datetime.now().strftime('%Y-%m-%d')
log_file = os.path.join(log_dir, f'app_{today}.log')

# ๋กœ๊ฑฐ ์ƒ์„ฑ
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)  # ๋กœ๊ฑฐ ์ž์ฒด์˜ ๋ ˆ๋ฒจ ์„ค์ •

# ํŒŒ์ผ ํ•ธ๋“ค๋Ÿฌ ์ƒ์„ฑ (ํŒŒ์ผ์— ๋กœ๊ทธ ๊ธฐ๋ก)
file_handler = logging.FileHandler(log_file, encoding='utf-8')
file_handler.setLevel(logging.DEBUG)  # ๋ชจ๋“  ๋กœ๊ทธ ์ €์žฅ

# ํšŒ์ „ ํŒŒ์ผ ํ•ธ๋“ค๋Ÿฌ (ํŒŒ์ผ ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋กœ๊ทธ ํŒŒ์ผ ๋ถ„ํ• )
# max_bytes: ์ตœ๋Œ€ ํŒŒ์ผ ํฌ๊ธฐ, backup_count: ๋ณด๊ด€ํ•  ์ด์ „ ๋กœ๊ทธ ํŒŒ์ผ ์ˆ˜
rotating_handler = logging.handlers.RotatingFileHandler(
    os.path.join(log_dir, 'rotating.log'),
    maxBytes=10000,  # 10KB
    backupCount=5,
    encoding='utf-8'
)
rotating_handler.setLevel(logging.INFO)

# ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ํšŒ์ „ ํ•ธ๋“ค๋Ÿฌ (๋งค์ผ ์ž์ •์— ์ƒˆ ๋กœ๊ทธ ํŒŒ์ผ ์ƒ์„ฑ)
timed_handler = logging.handlers.TimedRotatingFileHandler(
    os.path.join(log_dir, 'daily.log'),
    when='midnight',
    interval=1,  # 1์ผ๋งˆ๋‹ค
    backupCount=7,  # 1์ฃผ์ผ์น˜ ๋ณด๊ด€
    encoding='utf-8'
)
timed_handler.setLevel(logging.WARNING)

# ์ฝ˜์†” ํ•ธ๋“ค๋Ÿฌ (ํ„ฐ๋ฏธ๋„์— ๋กœ๊ทธ ์ถœ๋ ฅ)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)  # ์ •๋ณด ๋ ˆ๋ฒจ ์ด์ƒ๋งŒ ์ฝ˜์†”์— ์ถœ๋ ฅ

# ํฌ๋งทํ„ฐ ์ƒ์„ฑ
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# ํ•ธ๋“ค๋Ÿฌ์— ํฌ๋งทํ„ฐ ์ถ”๊ฐ€
file_handler.setFormatter(formatter)
rotating_handler.setFormatter(formatter)
timed_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# ๋กœ๊ฑฐ์— ํ•ธ๋“ค๋Ÿฌ ์ถ”๊ฐ€
logger.addHandler(file_handler)
logger.addHandler(rotating_handler)
logger.addHandler(timed_handler)
logger.addHandler(console_handler)

# ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ
logger.debug("๋””๋ฒ„๊ทธ ๋ฉ”์‹œ์ง€ - ํŒŒ์ผ์—๋งŒ ๊ธฐ๋ก")
logger.info("์ •๋ณด ๋ฉ”์‹œ์ง€ - ํŒŒ์ผ๊ณผ ์ฝ˜์†”์— ์ถœ๋ ฅ")
logger.warning("๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€ - ๋ชจ๋“  ํ•ธ๋“ค๋Ÿฌ์— ์ถœ๋ ฅ")
logger.error("์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€")
logger.critical("์‹ฌ๊ฐํ•œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€")

# ์˜ˆ์™ธ ์ •๋ณด ํฌํ•จ
try:
    result = 10 / 0
except Exception as e:
    logger.exception("์˜ˆ์™ธ ๋ฐœ์ƒ: 0์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ ์‹œ๋„")  # traceback ์ž๋™ ํฌํ•จ


6๏ธโƒฃ ์™ธ๋ถ€ ํ”„๋กœ์„ธ์Šค ์‹คํ–‰ ๋ฐ ์ž…์ถœ๋ ฅ

import subprocess

# ๊ฐ„๋‹จํ•œ ๋ช…๋ น ์‹คํ–‰
result = subprocess.run(['echo', 'Hello, World!'], capture_output=True, text=True)
print(f"๋ฐ˜ํ™˜ ์ฝ”๋“œ: {result.returncode}")
print(f"ํ‘œ์ค€ ์ถœ๋ ฅ: {result.stdout}")
print(f"ํ‘œ์ค€ ์˜ค๋ฅ˜: {result.stderr}")

# ์‰˜ ๋ช…๋ น ์‹คํ–‰
result = subprocess.run('ls -l | grep ".py"', shell=True, capture_output=True, text=True)
print(f"ํŒŒ์ด์ฌ ํŒŒ์ผ ๋ชฉ๋ก:\n{result.stdout}")

# ๋ช…๋ น ์‹คํ–‰ ์‹œ ์ž…๋ ฅ ์ œ๊ณต
input_data = "Hello from Python"
result = subprocess.run(['cat'], input=input_data, capture_output=True, text=True)
print(f"'cat' ๋ช…๋ น ๊ฒฐ๊ณผ: {result.stdout}")

# ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
try:
    result = subprocess.run(['non_existent_command'], check=True)  # check=True๋Š” ์˜ค๋ฅ˜ ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ
except subprocess.CalledProcessError as e:
    print(f"๋ช…๋ น ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
except FileNotFoundError as e:
    print(f"๋ช…๋ น์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ: {e}")

# ํ˜„์žฌ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ƒ์†๋ฐ›์•„ ํŠน์ • ๋ณ€์ˆ˜๋งŒ ์ถ”๊ฐ€ ๋˜๋Š” ๋ณ€๊ฒฝ
import os
env = os.environ.copy()
env['CUSTOM_VAR'] = 'value'

result = subprocess.run(['env'], env=env, capture_output=True, text=True)
print("ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋ชฉ๋ก ์ค‘ ์‚ฌ์šฉ์ž ์ •์˜ ๋ณ€์ˆ˜:")
for line in result.stdout.splitlines():
    if line.startswith('CUSTOM_VAR='):
        print(line)

Popen์„ ์‚ฌ์šฉํ•œ ๊ณ ๊ธ‰ ํ”„๋กœ์„ธ์Šค ์ œ์–ด

import subprocess
import time

# Popen์œผ๋กœ ํ”„๋กœ์„ธ์Šค ์‹œ์ž‘
process = subprocess.Popen(
    ['ping', 'localhost'],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)

# ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋‹ค๋ฅธ ์ž‘์—… ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ
print("ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค...")
time.sleep(2)

# ํ”„๋กœ์„ธ์Šค ์ƒํƒœ ํ™•์ธ
if process.poll() is None:
    print("ํ”„๋กœ์„ธ์Šค๊ฐ€ ์•„์ง ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค.")
    
    # ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค์—์„œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ถœ๋ ฅ ์ฝ๊ธฐ
    for i in range(5):  # 5์ค„๋งŒ ์ฝ๊ธฐ
        output = process.stdout.readline()
        if output:
            print(f"์‹ค์‹œ๊ฐ„ ์ถœ๋ ฅ: {output.strip()}")
        else:
            break
    
    # ํ•„์š”ํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
    process.terminate()
    try:
        process.wait(timeout=5)  # 5์ดˆ ๋™์•ˆ ์ข…๋ฃŒ ๋Œ€๊ธฐ
    except subprocess.TimeoutExpired:
        print("ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ๋˜์ง€ ์•Š์•„ ๊ฐ•์ œ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.")
        process.kill()

# ํ”„๋กœ์„ธ์Šค ์™„๋ฃŒ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ ๋ฐ ์ถœ๋ ฅ ์ฝ๊ธฐ
stdout, stderr = process.communicate()
print(f"๋ฐ˜ํ™˜ ์ฝ”๋“œ: {process.returncode}")
print(f"ํ‘œ์ค€ ์ถœ๋ ฅ ์ผ๋ถ€: {stdout[:100]}")  # ์ฒ˜์Œ 100์ž๋งŒ ํ‘œ์‹œ
print(f"ํ‘œ์ค€ ์˜ค๋ฅ˜: {stderr}")

# ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌํ˜„ (์—ฌ๋Ÿฌ ๋ช…๋ น ์—ฐ๊ฒฐ)
ps_process = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE, text=True)
grep_process = subprocess.Popen(['grep', 'python'], stdin=ps_process.stdout, stdout=subprocess.PIPE, text=True)
ps_process.stdout.close()  # ps_process์˜ stdout์„ ๋‹ซ์•„์„œ EOF ์‹ ํ˜ธ ์ „๋‹ฌ

output, _ = grep_process.communicate()
print("\nPython ๊ด€๋ จ ํ”„๋กœ์„ธ์Šค:")
print(output)

์ฃผ์š” ํŒ

โœ… ํ”„๋กœ๊ทธ๋žจ ์ž…์ถœ๋ ฅ ๋ชจ๋ฒ” ์‚ฌ๋ก€:

  • ๋ช…๋ น์ค„ ์ธ์ˆ˜๋Š” argparse๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฒด๊ณ„์ ์œผ๋กœ ๊ตฌ์„ฑํ•œ๋‹ค
  • ๊ฐ ์ธ์ˆ˜์— ๋„์›€๋ง ๋ฉ”์‹œ์ง€๋ฅผ ํ•ญ์ƒ ์ œ๊ณตํ•œ๋‹ค
  • ๋ฏผ๊ฐํ•œ ์ •๋ณด(๋น„๋ฐ€๋ฒˆํ˜ธ, API ํ‚ค ๋“ฑ)๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ๊ด€๋ฆฌํ•œ๋‹ค
  • ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋Š” ํ•ญ์ƒ ๊ธฐ๋ณธ๊ฐ’๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค (os.environ.get('VAR', 'default'))
  • ๋กœ๊น…์€ print ๋Œ€์‹  logging ๋ชจ๋“ˆ์„ ํ™œ์šฉํ•œ๋‹ค
  • ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์ ์ ˆํžˆ ๊ตฌ๋ถ„ํ•˜์—ฌ ๋””๋ฒ„๊น…๊ณผ ์šด์˜์„ ๊ตฌ๋ถ„ํ•œ๋‹ค
  • ์™ธ๋ถ€ ํ”„๋กœ์„ธ์Šค ์‹คํ–‰ ์‹œ subprocess.run()์˜ check=True ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•œ๋‹ค
  • ์™ธ๋ถ€ ๋ช…๋ น ์‹คํ–‰ ์‹œ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ์ง์ ‘ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ณ  ์ธ์ˆ˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค (๋ช…๋ น์–ด ์ฃผ์ž… ๊ณต๊ฒฉ ๋ฐฉ์ง€)
  • ํ‘œ์ค€ ์ž…์ถœ๋ ฅ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ํ›„์—๋Š” ๋ฐ˜๋“œ์‹œ ์›๋ž˜ ์ƒํƒœ๋กœ ๋ณต๊ตฌํ•œ๋‹ค
  • ์žฅ๊ธฐ ์‹คํ–‰ ํ”„๋กœ์„ธ์Šค๋Š” ์‹œ๊ฐ„ ์ดˆ๊ณผ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ๊ตฌํ˜„ํ•œ๋‹ค


โš ๏ธ **GitHub.com Fallback** โš ๏ธ