팀 파일명 규칙을 정하고 Python으로 검사하기
간단한 팀 파일명 규칙을 정하고 합성 폴더에서 검사한 뒤, 어떤 파일도 이름을 바꾸거나 삭제하지 않고 검토용 보고서를 작성합니다. 검사기는 구조 오류, 잘못된 날짜, 허용되지 않은 확장자를 구분할 수 있습니다.
프로젝트 인수인계 메모를 전달 대상, 담당자, 자료 위치, 미해결 작업이 명확한 체크리스트로 정리합니다. 작은 합성 예제를 이용해 인수인계 전에 누락된 담당자, 위치, 미해결 이슈 정보를 확인합니다.
이런 분께 맞아요프로젝트를 다른 사람에게 넘기면서 담당자, 파일 위치, 미해결 업무를 명확하게 기록해야 하는 팀을 위한 안내입니다.
유용한 인수인계 체크리스트는 무엇을 넘기는지, 누가 책임지는지, 관련 자료가 어디 있는지, 완료 상태가 무엇인지, 아직 해결되지 않은 것이 무엇인지 알려줘야 합니다. 파일은 드라이브에 있다는 식의 메모만으로는 나중에 프로젝트를 맡는 사람이 실제 자료를 찾기 어렵습니다.
이 예제에서는 item_id, handover_item, owner, location, status, open_issue의 여섯 필드를 사용합니다. status는 READY 또는 OPEN입니다. READY 항목은 open_issue가 비어 있어야 하고, OPEN 항목은 무엇이 미해결 상태인지 설명해야 합니다.
아래 체크리스트는 이 글을 위해 작성한 합성 데이터입니다. handover_items.csv로 저장하세요. 인수인계 항목 5개가 있고, 실제 인수인계 전에 잡아야 하는 문제 2개를 의도적으로 포함했습니다.
item_id,handover_item,owner,location,status,open_issue
H001,Source code,Min,repo/main,READY,
H002,Test dataset,Jae,shared-drive/project/data,READY,
H003,Calibration procedure,Min,shared-drive/project/docs/calibration.pdf,OPEN,Final approval is still pending
H004,Supplier contact list,,shared-drive/project/admin/suppliers.csv,READY,
H005,Open bug list,Jae,,OPEN,Two export bugs remain under review
H001부터 H003까지는 구조적으로 완전합니다. H004는 READY인데 담당자가 없습니다. H005는 미해결 이슈 설명은 있지만 bug list 위치가 없습니다. 따라서 이 예제의 검증 문제는 2개입니다.
| item_id | 예상 결과 | 이유 |
|---|---|---|
| H001 | PASS | 담당자와 위치가 있고 READY이므로 open issue가 없음 |
| H002 | PASS | 담당자와 위치가 있고 READY이므로 open issue가 없음 |
| H003 | PASS | OPEN 항목에 담당자, 위치, 미해결 이슈가 모두 있음 |
| H004 | FAIL | 담당자 누락 |
| H005 | FAIL | 위치 누락 |
예상 결과는 전체 체크리스트 5개, 정상 3개, 문제 항목 2개입니다. OPEN 상태는 H003과 H005 두 개이며 둘 다 open_issue 내용이 있습니다. 따라서 이 예제의 문제는 미해결 이슈 설명 누락이 아니라 책임자와 위치 정보 누락입니다.
이 구분이 중요한 이유는 미해결 이슈가 존재해도 문제 내용, 담당자, 위치가 명확하다면 프로젝트 자체는 인수인계 가능한 상태일 수 있기 때문입니다.
다음 스크립트를 check_handover.py로 저장하세요. 필수 열, 중복 ID, 담당자, 위치, 허용 상태, status와 open_issue의 일관성을 검사합니다. 원본 체크리스트는 수정하지 않고 별도의 검토 보고서를 작성합니다.
import csv
from pathlib import Path
SOURCE = Path("handover_items.csv")
OUTPUT_DIR = Path("outputs") / "handover_check_result"
REPORT = OUTPUT_DIR / "handover_review.csv"
REQUIRED_COLUMNS = [
"item_id", "handover_item", "owner", "location", "status", "open_issue"
]
VALID_STATUS = {"READY", "OPEN"}
def add_issue(issues, row_number, item_id, issue):
issues.append({
"row_number": row_number,
"item_id": item_id,
"issue": issue,
})
def main() -> None:
if not SOURCE.is_file():
raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
if OUTPUT_DIR.exists():
raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")
issues = []
seen_ids = set()
item_count = 0
open_count = 0
with SOURCE.open("r", encoding="utf-8-sig", newline="") as stream:
reader = csv.DictReader(stream)
if reader.fieldnames != REQUIRED_COLUMNS:
raise ValueError(
f"Header mismatch. Expected {REQUIRED_COLUMNS}, got {reader.fieldnames}."
)
for row_number, row in enumerate(reader, start=2):
item_count += 1
item_id = row["item_id"].strip()
handover_item = row["handover_item"].strip()
owner = row["owner"].strip()
location = row["location"].strip()
status = row["status"].strip()
open_issue = row["open_issue"].strip()
if not item_id:
add_issue(issues, row_number, item_id, "MISSING_ITEM_ID")
elif item_id in seen_ids:
add_issue(issues, row_number, item_id, "DUPLICATE_ITEM_ID")
seen_ids.add(item_id)
if not handover_item:
add_issue(issues, row_number, item_id, "MISSING_ITEM_NAME")
if not owner:
add_issue(issues, row_number, item_id, "MISSING_OWNER")
if not location:
add_issue(issues, row_number, item_id, "MISSING_LOCATION")
if status not in VALID_STATUS:
add_issue(issues, row_number, item_id, "INVALID_STATUS")
elif status == "OPEN":
open_count += 1
if not open_issue:
add_issue(issues, row_number, item_id, "MISSING_OPEN_ISSUE")
elif open_issue:
add_issue(issues, row_number, item_id, "READY_HAS_OPEN_ISSUE")
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir()
with REPORT.open("x", encoding="utf-8", newline="") as stream:
writer = csv.DictWriter(
stream,
fieldnames=["row_number", "item_id", "issue"],
)
writer.writeheader()
writer.writerows(issues)
affected_items = len({row["item_id"] for row in issues})
print(f"Checklist items: {item_count}.")
print(f"Open items: {open_count}.")
print(f"Validation issues: {len(issues)}.")
print(f"Affected items: {affected_items}.")
print(f"Report: {REPORT.as_posix()}")
if __name__ == "__main__":
main()
CSV 헤더가 1행이므로 H004는 실제 5행이고 H005는 실제 6행입니다. 검토 보고서에는 정확히 문제 2개가 기록되어야 합니다.
row_number,item_id,issue
5,H004,MISSING_OWNER
6,H005,MISSING_LOCATION
아래 예상 콘솔 출력은 합성 체크리스트와 스크립트를 바탕으로 손으로 도출한 결과이며 실제 실행 로그가 아닙니다.
Checklist items: 5.
Open items: 2.
Validation issues: 2.
Affected items: 2.
Report: outputs/handover_check_result/handover_review.csv경로 문자열이 존재해도 권한이 없거나 오래된 파일을 가리키면 인수인계 정보로 쓸모가 없습니다. 따라서 작성한 사람뿐 아니라 받는 사람도 체크리스트를 직접 확인해야 합니다.
| 약한 인수인계 항목 | 부족한 이유 |
|---|---|
| Owner: Team | 특정 사람이나 정의된 역할에 책임이 연결되지 않으면 담당이 불분명합니다. |
| Location: Drive | 받는 사람이 실제 파일이나 폴더를 다시 찾아야 합니다. |
| Status: Done | 팀에서 완료의 정의가 명확하지 않으면 의미가 모호합니다. |
| Open issue: Needs work | 어떤 문제가 있고 다음에 무엇을 해야 하는지 알 수 없습니다. |
| CSV에 비밀번호 기록 | 일반 인수인계 체크리스트는 비밀번호나 secret을 저장하는 장소가 아닙니다. |
파일이 있다는 이유만으로 READY로 표시하지 마세요. 실제로 사용할 수 있는 인수인계에는 접근 권한, 최신 문서, 책임자, 미해결 결정 사항에 대한 이해도 필요합니다.
실제 프로젝트에서는 receiving_owner, due_date, repository branch, access_required, last_verified_date, dependency, contact person, equipment location, approval status, next milestone 같은 필드가 추가로 필요할 수 있습니다. 전달 명확성을 실제로 높이는 경우에만 필드를 추가하세요.
이 스크립트는 입력 정보의 완전성과 내부 일관성을 검사할 뿐 location이 실제로 접근 가능한지, open issue 설명이 기술적으로 정확한지는 판단하지 않습니다. 접근 권한, 파일 최신성, repository 상태, 장비 상태, 지식 전달은 별도로 확인해야 합니다.
체크리스트는 날짜 또는 버전을 부여해 프로젝트 기록과 함께 보관하세요. 받는 사람이 인수인계를 확인한 뒤에는 원래 전달 상태를 조용히 수정하지 말고 수락 여부를 별도로 기록하는 편이 좋습니다.
2026-09-20 · 예제 수동 검토 · 대상: Python 3.12 · 표준 라이브러리: csv, pathlib · 실행하지 않음
설명과 예제는 직접 작성했습니다. 관련 동작과 개념은 아래 공식 자료에서 확인할 수 있습니다.