기본 콘텐츠로 건너뛰기

추천 가젯

리액트 + 비트(Vite)로 모바일 청첩장 만들기 — 2편

모바일 청첩장 시리즈 2편 · R2 갤러리 & Firebase 방명록 Cloudflare R2 · Functions Firebase Firestore 클라우드플레어 R2로 갤러리 저장하고, Firebase로 방명록 달기 안녕하세요, 병민입니다 🙌 1편에서 전체 흐름을 잡았고, 이번엔 사진 업로드/보관 과 방명록 을 연결합니다. 서버는 따로 없고 Cloudflare Pages 를 쓰고 있으니, Pages Functions (= 워커)로 R2에 사전서명 URL을 만들어주고, 프론트에서 그 URL로 바로 업로드하는 구조예요. 방명록은 Firebase DB로 간단·안전하게! 전체 그림 프론트(React) → /api/r2/upload 로 업로드용 URL 요청 → R2에 파일 PUT 프론트(React) → /api/r2/list 로 목록 요청 → 갤러리 렌더 프론트(React) → Firebase SDK로 방명록 작성/조회 1) R2 버킷 & Pages Functions 준비 Cloudflare 대시보드 > R2 > Create bucket (예: wedding-gallery ) 버킷 > Settings > CORS 에서 사이트 도메인 허용(예: https://*.pages.dev , 커스텀 도메인) Pages 프로젝트 > Settings > Functions 에서 R2 바인딩 추가: ...

파이썬 subprocess 완전 정복 ⚙️ — 외부 명령 실행·입출력 캡처·타임아웃

파이썬 subprocess 완전 정복 ⚙️ — 외부 명령 실행·입출력 캡처·타임아웃 subprocess 아이콘

파이썬 subprocess 완전 정복 ⚙️

subprocess는 외부 프로그램 실행을 위한 표준 모듈입니다. 안전한 호출(shell=False), 출력 캡처, 타임아웃, 파이프, 환경/작업폴더 제어까지 운영에 필요한 기능을 모두 제공합니다.

1) 가장 쉬운 방법: subprocess.run

import subprocess

# 리스트 인자 + shell=False (기본) → 안전
result = subprocess.run(
    ["python", "--version"],
    capture_output=True, text=True, check=False
)
print(result.stdout.strip())  # Python 3.x.x
print(result.returncode)
text=True로 자동 디코딩(기본 UTF-8)을 사용하고, 실패 시 예외를 원하면 check=True.

2) 표준출력/표준에러 캡처

import subprocess

res = subprocess.run(
    ["git", "status"],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    text=True
)
print("STDOUT:", res.stdout)
print("STDERR:", res.stderr)

3) 타임아웃과 에러 처리

import subprocess

try:
    subprocess.run(["ping", "-c", "3", "example.com"], timeout=5, check=True)
except subprocess.TimeoutExpired as e:
    print("타임아웃:", e)
except subprocess.CalledProcessError as e:
    print("비정상 종료:", e.returncode, e.stderr)
팁: 타임아웃 시 하위 프로세스가 좀비로 남지 않도록 Popen을 쓰는 경우 proc.kill(); proc.communicate()로 버퍼를 비워 정리하세요.

4) 스트리밍/파이프: Popen

4-1. 실시간 출력 읽기

import subprocess

with subprocess.Popen(
    ["ping", "-c", "3", "example.com"],
    stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1
) as proc:
    for line in proc.stdout:
        print(">>", line.strip())
    code = proc.wait()
print("returncode:", code)

4-2. 두 명령 파이프 연결(UNIX 스타일)

import subprocess

p1 = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "python"], stdin=p1.stdout, stdout=subprocess.PIPE, text=True)
p1.stdout.close()  # 파이프 종단 닫기(좀비 방지)
out, _ = p2.communicate()
print(out)

4-3. 입력을 프로그램에 전달

import subprocess

p = subprocess.Popen(["python", "-c", "print(input()[::-1])"],
                     stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
out, _ = p.communicate("abcde\n")
print(out.strip())  # edcba

5) 환경변수/작업 디렉터리/권한

import os, subprocess

env = dict(os.environ)
env["API_TOKEN"] = "masked"

res = subprocess.run(
    ["python", "script.py"],
    cwd="/path/to/project",   # 작업 디렉터리
    env=env,                  # 환경변수 오버라이드
    capture_output=True, text=True
)
print(res.stdout)

6) Windows & POSIX 차이

  • Windows는 dir, copy 같은 내장은 cmd 전용이므로 shell=True가 필요합니다. 대안: 파워셸 ["powershell","-Command","Get-ChildItem"].
  • 백그라운드/콘솔 창 숨김은 creationflags=subprocess.CREATE_NO_WINDOW (Windows 전용)로 제어.
  • POSIX에서 세션 분리는 preexec_fn=os.setsid 또는 start_new_session=True 사용.

7) 실전 레시피

7-1. JSON 출력 파싱

import subprocess, json

res = subprocess.run(["python","-c",'import json; print(json.dumps({"ok":1}))'],
                     capture_output=True, text=True, check=True)
payload = json.loads(res.stdout)
print(payload["ok"])

7-2. 안전한 사용자 입력 인자화

import subprocess

def grep_safe(pattern, path):
    # 쉘 미사용, 리스트 인자화 → 쉘 인젝션 방지
    return subprocess.run(["grep", "-R", pattern, path],
                          capture_output=True, text=True)

print(grep_safe("TODO", "."))

7-3. 타임아웃 + 강제 종료 정석 패턴

import subprocess

proc = subprocess.Popen(["sleep","10"])
try:
    proc.wait(timeout=3)
except subprocess.TimeoutExpired:
    proc.kill()
    proc.wait()  # 버퍼 비우기/정리

7-4. 대용량 출력 안전 처리(메모리 폭주 방지)

import subprocess

# communicate는 전부 메모리에 적재될 수 있음 → 라인 스트리밍
with subprocess.Popen(["find","/","-maxdepth","1"], stdout=subprocess.PIPE, text=True) as p:
    for line in p.stdout:
        # 라인 단위 처리/저장/전송
        pass

8) 보안 & 베스트 프랙티스

  • shell=False를 기본으로. 정말 필요할 때만 shell=True (그리고 사용자 입력을 절대 그대로 넘기지 않기).
  • 명령·인자는 리스트로 전달. 공백/특수문자 자동 이스케이프.
  • 입력 데이터는 stdin으로 전달하고, 명령 인자에는 로직상 필요한 값만 주입.
  • 타임아웃을 항상 고려하고, 예외 처리로 종료/정리 보장.
  • 로그에 전체 명령행과 시크릿을 그대로 남기지 않기(마스킹).

요약

  • run은 간단 실행/캡처, Popen은 스트리밍·파이프 등 고급 제어.
  • shell=False + 리스트 인자가 보안 기본값.
  • 타임아웃/에러 처리/환경·작업폴더 제어로 운영 신뢰성 확보.

자주 묻는 질문(FAQ)

Q1. UTF-8이 아닌 출력은 어떻게 디코딩하나요?
A. text=True, encoding="cp949" 같이 인코딩을 지정하거나, res.stdout.encode().decode("cp949")로 변환하세요.

Q2. 비동기 병렬 실행은?
A. asyncio.create_subprocess_exec 또는 concurrent.futures.ThreadPoolExecutorPopen 호출을 병렬화하세요.

Q3. sudo가 필요한 명령은?
A. 권한 분리 원칙을 우선 적용하고, 필요한 경우에만 최소 범위로 승격하세요. 자동화 시엔 전용 사용자/권한을 분리하는 것이 안전합니다.

댓글

가장 많이 본 글

Icons by Flaticon