학습 자료

업무 자동화 ① 엑셀 (2) — 서식을 입히고 함수로 묶는다


Article

앞 강의에서 폴더를 훑어 엑셀 표를 만들었다. 표는 나오지만 밋밋하다. 제목 줄이 데이터와 구분이 안 되고, 열이 좁아 글자가 잘려 보인다.

이번 강의는 두 가지다. 서식을 입히는 것함수로 묶는 것.

파일은 계속 ai-course 폴더에 넣는다. 이번 강의는 61번부터다.

1. 서식을 입힌다 — 여기서 「있어 보인다」

네 가지만 더하면 사람이 쓸 만한 문서가 된다. 앞부분은 앞 강의와 같고, wb.save 앞에 네 덩어리가 들어갔다.

ai-course/61_서식.py
import os
import glob
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment

rows = []
for path in sorted(glob.glob("서류/*.txt")):
    with open(path, encoding="utf-8") as f:
        text = f.read()

    rows.append({
        "파일명": os.path.basename(path),
        "크기KB": round(os.path.getsize(path) / 1024, 1),
        "글자수": len(text),
        "첫문장": text.split("\n")[1][:20],
    })

HEADERS = ["파일명", "크기KB", "글자수", "첫문장"]

wb = Workbook()
ws = wb.active
ws.title = "서류 현황"

ws.append(HEADERS)
for r in rows:
    ws.append([r[h] for h in HEADERS])

# 1. 제목 줄을 눈에 띄게 - ws[1] 은 1행 전체다
for cell in ws[1]:
    cell.font = Font(bold=True, color="FFFFFF")             # 굵은 흰 글씨
    cell.fill = PatternFill("solid", fgColor="333333")      # 짙은 회색 배경
    cell.alignment = Alignment(horizontal="center")         # 가운데 정렬

# 2. 열 너비 - 안 하면 글자가 잘려 보인다
for col, width in zip("ABCD", (16, 10, 10, 26)):
    ws.column_dimensions[col].width = width

# 3. 필터 버튼 - ws.dimensions 는 "A1:D6" 처럼 표 전체 범위다
ws.auto_filter.ref = ws.dimensions

# 4. 첫 줄 고정 - A2 위쪽이 얼어붙는다. 스크롤해도 제목이 보인다
ws.freeze_panes = "A2"

wb.save("서류현황.xlsx")

print(ws.dimensions)
print(ws.title)
터미널
  • A1:D6
  • 서류 현황

A1:D6제목 줄 1행 + 문서 5행이다. 이 문자열을 그대로 auto_filter.ref 에 넣었으니 범위를 손으로 셀 필요가 없다. 엑셀에서 열어보면 앞 강의와 확실히 다르다.

네 덩어리가 하는 일
코드엑셀에서 보이는 것
Font(bold=True, color="FFFFFF")제목 줄이 굵은 흰 글씨
PatternFill("solid", fgColor="333333")제목 줄 배경이 짙은 회색
column_dimensions["A"].width = 16A열이 넓어져 파일명이 안 잘린다
auto_filter.ref = ws.dimensions제목 줄에 필터 버튼이 생긴다
freeze_panes = "A2"스크롤해도 제목 줄이 안 사라진다

2. 재사용할 수 있게 함수로

한 번 쓰고 버릴 코드가 아니다. 폴더 이름만 바꿔 다음 달에 다시 돌릴 수 있게 묶는다.

ai-course/62_함수.py
import os
import glob
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment


def 폴더현황_엑셀(folder, out_path="현황.xlsx"):
    """폴더 안 txt 를 훑어 서식 갖춘 엑셀 파일로 만든다."""
    headers = ["파일명", "크기KB", "글자수", "첫문장"]

    rows = []
    for path in sorted(glob.glob(f"{folder}/*.txt")):
        try:
            with open(path, encoding="utf-8") as f:
                text = f.read()
        except UnicodeDecodeError:
            print(f"[건너뜀] {path} - utf-8 이 아니다")
            continue

        lines = text.split("\n")
        rows.append({
            "파일명": os.path.basename(path),
            "크기KB": round(os.path.getsize(path) / 1024, 1),
            "글자수": len(text),
            # 줄이 하나뿐인 파일에 lines[1] 을 쓰면 IndexError 가 난다
            "첫문장": (lines[1] if len(lines) > 1 else lines[0])[:20],
        })

    if not rows:
        print(f"[경고] '{folder}' 에서 txt 를 못 찾았다.")
        return None

    wb = Workbook()
    ws = wb.active
    ws.title = "현황"
    ws.append(headers)
    for r in rows:
        ws.append([r[h] for h in headers])

    for cell in ws[1]:
        cell.font = Font(bold=True, color="FFFFFF")
        cell.fill = PatternFill("solid", fgColor="333333")
        cell.alignment = Alignment(horizontal="center")

    for col, width in zip("ABCD", (16, 10, 10, 26)):
        ws.column_dimensions[col].width = width

    ws.auto_filter.ref = ws.dimensions
    ws.freeze_panes = "A2"
    wb.save(out_path)

    print(f"{len(rows)}건을 {out_path} 에 저장했다")
    return out_path


폴더현황_엑셀("서류")
폴더현황_엑셀("없는폴더")
터미널
  • 5건을 현황.xlsx 에 저장했다
  • [경고] '없는폴더' 에서 txt 를 못 찾았다.

이번 강의에 나온 것

정리
쓴 것하는 일
ws[1]1행 전체 (제목 줄)
Font(bold=True, color="FFFFFF")글자 서식
PatternFill("solid", fgColor="333333")배경색
Alignment(horizontal="center")가운데 정렬
column_dimensions["A"].width열 너비
ws.dimensions표 전체 범위 — "A1:D6"
ws.auto_filter.ref = ...필터 버튼
ws.freeze_panes = "A2"첫 줄 고정
ws.cell(row=2, column=3)칸 하나를 집는다. 1부터 센다

스스로 해보기

확인 문제 둘
  1. 01

    글자수가 많은 줄에 색을 넣는다

    ai-course/63_연습1.py. 500자를 넘는 행의 글자수 칸만 배경을 노랗게 칠해본다. 힌트는 ws.cell(row=번호, column=3) 으로 칸 하나를 집는 것이다.

  2. 02

    4강의 docs 폴더에 그대로 돌려본다

    ai-course/64_연습2.py. 폴더현황_엑셀("docs", "docs현황.xlsx") 를 불러본다. 함수 안을 한 글자도 안 고치고 다른 폴더가 처리되나.

1번 — 조건에 맞는 칸에 색 넣기충분히 고민해본 뒤 꼭 필요한 경우에만 열어보세요

ws.cell(row=..., column=...) 로 칸 하나를 집는다. 행과 열 모두 1부터 센다 — 파이썬의 0과 다르다.

제목 줄이 1행이므로 데이터는 2행부터다. enumerate(rows, start=2) 가 그걸 맞춰준다.

from openpyxl.styles import PatternFill

노랑 = PatternFill("solid", fgColor="FFF3B0")

for i, r in enumerate(rows, start=2):      # 2행부터가 데이터
    if r["글자수"] > 500:
        ws.cell(row=i, column=3).fill = 노랑   # 3열 = 글자수
        print(i, r["파일명"], r["글자수"])

wb.save("서류현황.xlsx")

5 보고서.txt 8176 회의록.txt 1087 두 줄이 찍히고, 엑셀에서 그 두 칸만 노랗다.

행 번호가 5·6인 것에 주의한다. 파일명 순서(견적서·계약서·메모·보고서·회의록)에서 4·5번째이고, 제목 줄 때문에 하나씩 밀렸다.

변수 이름에 한글을 써도 된다. 실무에서는 거의 안 쓰지만 문법상 문제는 없다.

2번 — 다른 폴더에 그대로 돌리기충분히 고민해본 뒤 꼭 필요한 경우에만 열어보세요

함수 안은 한 글자도 안 고친다. 인자만 바꿔 부른다. 이게 폴더 이름을 인자로 뺀 값어치다.

폴더현황_엑셀("docs", "docs현황.xlsx")

3건을 docs현황.xlsx 에 저장했다 가 찍힌다. 4강에서 만든 backend.txt · data.txt · frontend.txt 셋이다.

대상이 바뀌었는데 코드는 안 바뀌었다. 이 시리즈가 계속 반복할 이야기다 — 4강의 DOCS_DIR, 뒤에서 만들 검색기까지 전부 같은 규칙이다.

docs 파일들은 첫 줄이 「수업용 예제」라 첫문장 열에 두 번째 줄이 들어온다. 서류 폴더와 같은 모양이라 함수가 그대로 먹힌다.

정리하면

서식 네 덩어리 — 글자 · 배경 · 너비 · 고정 — 을 더하면 밋밋한 표가 사람이 열어볼 문서가 된다. 코드는 길어 보여도 wb.save 앞에 붙는 네 조각뿐이다.

그리고 폴더 이름을 인자로 빼서 함수로 묶었다. 다음 달에는 다시 실행만 하면 된다.

다음 강의는 같은 틀로 사진을 다룬다. 폴더를 훑고, 하나씩 열고, 바꿔서, 저장한다. 틀이 똑같다.