Python & AI Tutorials Logo
Python 프로그래밍

13. match와 case로 선택하기(구조적 패턴 매칭)

프로그램이 여러 가능한 값이나 패턴에 따라 결정을 내려야 할 때, 여러분은 이미 8장에서 if-elif-else 체인을 사용하는 방법을 배웠습니다. Python 3.10에서는 matchcase 문을 사용하는 구조적 패턴 매칭(structural pattern matching) 이라는 강력한 대안을 도입했습니다. 이 기능은 복잡한 의사결정 시나리오를 더 깔끔하고 더 표현력 있게 처리할 수 있는 방법을 제공합니다.

패턴 매칭(pattern matching)은 단순한 값 비교를 넘어섭니다. 데이터의 구조와 형태에 대해 매칭하고, 복잡한 객체에서 값을 추출하며, 다중 분기 결정을 더 읽기 쉬운 형태로 표현할 수 있게 해줍니다. if-elif-else 체인은 많은 상황에서 충분히 잘 동작하지만, match-case 문은 여러 개의 뚜렷한 경우를 다룰 때, 특히 구조화된 데이터로 작업할 때 진가를 발휘합니다.

13.1) match와 case 문 소개(8장의 if-elif 기반)

13.1.1) match-case의 기본 구조

match 문은 subject(대상) 라고 부르는 값을 검사하고, case 절에 정의된 하나 이상의 패턴(pattern) 과 비교합니다. 패턴이 매칭되면 Python은 해당 case와 연결된 코드 블록을 실행합니다.

기본 구조는 다음과 같습니다:

python
match subject:
    case pattern1:
        # pattern1이 매칭되면 실행할 코드
    case pattern2:
        # pattern2가 매칭되면 실행할 코드
    case pattern3:
        # pattern3이 매칭되면 실행할 코드

기본 개념을 보여주는 간단한 예시부터 시작해 보겠습니다:

python
# 간단한 HTTP 상태 코드 처리기
status_code = 404
 
match status_code:
    case 200:
        print("Success: Request completed")
    case 404:
        print("Error: Page not found")
    case 500:
        print("Error: Server error")

Output:

Error: Page not found

이 예시에서 match 문은 status_codesubject(대상) 로 삼아 검사합니다. Python은 각 case 패턴을 순서대로 확인합니다. status_code404와 같다는 것을 찾으면, 해당 코드 블록을 실행한 뒤 match 문을 빠져나옵니다. 나머지 case들은 검사하지 않습니다.

13.1.2) match-case가 if-elif-else와 다른 점

이런 생각이 들 수도 있습니다. “이거 if-elif-else로도 쓸 수 있지 않나요?” 네, 가능합니다:

python
status_code = 404
 
if status_code == 200:
    print("Success: Request completed")
elif status_code == 404:
    print("Error: Page not found")
elif status_code == 500:
    print("Error: Server error")

Output:

Error: Page not found

두 버전 모두 같은 결과를 만들어냅니다. 하지만 match-case에는 몇 가지 장점이 있습니다:

  1. 의도가 더 명확함: match 문은 하나의 값을 여러 가능성과 비교해 확인하고 있다는 점을 명시적으로 보여줍니다
  2. 반복이 적음: 모든 비교에서 변수 이름을 반복해서 쓰지 않아도 됩니다
  3. 더 강력한 패턴: 앞으로 보겠지만 match-case는 단순한 동등 비교보다 훨씬 더 많은 일을 할 수 있습니다
  4. 가독성이 더 좋음: 복잡한 결정 트리에서는 match-case가 더 이해하기 쉬운 경우가 많습니다

13.1.3) 어떤 패턴도 매칭되지 않을 때

어떤 패턴도 매칭되지 않으면 어떻게 될까요? match 문은 어떤 case 블록도 실행하지 않고 그냥 끝납니다:

python
# 사용자 역할 확인기
user_role = "guest"
 
match user_role:
    case "admin":
        print("Full system access granted")
    case "moderator":
        print("Content management access granted")
    case "editor":
        print("Editing access granted")
 
print("Role check complete")

Output:

Role check complete

"guest"는 어떤 패턴과도 매칭되지 않기 때문에 어떤 case 블록도 실행되지 않습니다. 프로그램은 match 문 다음 코드로 계속 진행합니다. 이 동작은 이해해 두는 것이 중요합니다—if-elif-else 체인에서는 마지막에 else 절을 추가해 나머지 모든 경우를 잡아낼 수 있지만, 모든 경우를 잡는 패턴이 없는 기본 match 문은 매칭되는 패턴이 없을 때 조용히 아무 것도 하지 않습니다.

13.1.4) 실전 예시: 메뉴 선택 시스템

사용자 선택을 처리할 때 match-case가 얼마나 명확한지 보여주는 더 완전한 예시를 만들어 보겠습니다:

python
# 레스토랑 주문 시스템
menu_choice = 3
 
match menu_choice:
    case 1:
        item = "Caesar Salad"
        price = 8.99
        print(f"You ordered: {item} - ${price}")
    case 2:
        item = "Grilled Chicken"
        price = 14.99
        print(f"You ordered: {item} - ${price}")
    case 3:
        item = "Vegetable Pasta"
        price = 12.99
        print(f"You ordered: {item} - ${price}")
    case 4:
        item = "Chocolate Cake"
        price = 6.99
        print(f"You ordered: {item} - ${price}")
 
print("Order submitted to kitchen")

Output:

You ordered: Vegetable Pasta - $12.99
Order submitted to kitchen

이 예시는 각 case가 여러 문장을 포함할 수 있음을 보여줍니다. menu_choice3과 매칭되면 Python은 해당 case 블록의 세 줄을 모두 실행합니다. 즉 item 할당, price 할당, 주문 확인 출력입니다.

13.2) _ 와일드카드, 리터럴 패턴, 다중 패턴 사용하기

13.2.1) 와일드카드 패턴: 나머지 모두 잡기

언더스코어 _는 무엇이든 매칭되는 특별한 패턴입니다. 보통 마지막 case로 사용하여 앞선 패턴들과 매칭되지 않은 모든 값을 처리합니다—if-elif-else 체인의 마지막 else 절과 비슷합니다:

python
# 기본 case가 있는 HTTP 상태 코드 처리기
status_code = 403
 
match status_code:
    case 200:
        print("Success: Request completed")
    case 404:
        print("Error: Page not found")
    case 500:
        print("Error: Server error")
    case _:
        print(f"Unhandled status code: {status_code}")

Output:

Unhandled status code: 403

_ 패턴은 모든 경우를 포괄하는 역할을 합니다. 403은 구체적인 case 중 어떤 것과도 매칭되지 않으므로, 와일드카드 패턴이 매칭되어 블록이 실행됩니다. 와일드카드 패턴은 어떤 값과도 매칭되기 때문에 항상 마지막에 놓아야 합니다—그 뒤에 오는 case는 절대 실행되지 않습니다.

실제로 와일드카드가 왜 유용한지 살펴보겠습니다:

python
# 요일 스케줄러
day = "Saturday"
 
match day:
    case "Monday":
        print("Team meeting at 9 AM")
    case "Wednesday":
        print("Project review at 2 PM")
    case "Friday":
        print("Weekly report due")
    case _:
        print(f"{day}: No scheduled events")

Output:

Saturday: No scheduled events

와일드카드 패턴이 없다면 day"Saturday", "Sunday" 또는 다른 어떤 값이었을 때 match 문은 아무 출력도 없이 조용히 끝납니다. 와일드카드는 예상치 못하거나 명시되지 않은 경우도 문제 없이 처리할 수 있게 해줍니다.

13.2.2) 리터럴 패턴: 특정 값 매칭하기

리터럴 패턴(literal patterns) 은 정확한 값과 매칭됩니다. 우리는 이미 이것을 사용해 왔습니다—숫자, 문자열, 불리언 값은 모두 리터럴 패턴입니다:

python
# 신호등 컨트롤러
light_color = "yellow"
 
match light_color:
    case "green":
        print("Go")
    case "yellow":
        print("Caution: Light changing soon")
    case "red":
        print("Stop")
    case _:
        print("Invalid light color")

Output:

Caution: Light changing soon

서로 다른 타입의 리터럴 패턴을 사용할 수 있으며, match는 값과 타입을 모두 비교합니다:

python
# 설정 검증기(서로 다른 리터럴 타입 사용)
setting_value = True
 
match setting_value:
    case True:        # boolean 리터럴
        print("Feature enabled")
    case False:       # boolean 리터럴
        print("Feature disabled")
    case None:        # None 리터럴
        print("Feature not configured")
    case 0:           # 정수 리터럴
        print("Feature explicitly turned off")
    case "auto":      # 문자열 리터럴
        print("Feature set to automatic mode")
    case _:
        print("Invalid configuration value")

Output:

Feature enabled

리터럴 패턴은 정수, 실수, 문자열, 불리언, None과 함께 동작합니다. Python은 == 연산자와 같은 규칙으로 동등성 검사를 합니다.

13.2.3) OR 연산자로 여러 패턴 사용하기

때로는 여러 다른 값에 대해 동일한 코드를 실행하고 싶을 수 있습니다. |(파이프) 연산자를 사용해 여러 패턴을 결합할 수 있는데, 이는 “또는(or)”를 의미합니다:

python
# 주말 판별기
day = "Saturday"
 
match day:
    case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
        print("It's a weekday - time to work!")
    case "Saturday" | "Sunday":
        print("It's the weekend - time to relax!")
    case _:
        print("Invalid day name")

Output:

It's the weekend - time to relax!

| 연산자를 사용하면 같은 동작을 수행해야 하는 여러 패턴을 지정할 수 있습니다. 대상이 |로 구분된 어떤 패턴과라도 매칭되면 해당 case가 실행됩니다. 이는 동일한 코드 블록을 가진 별도의 case들을 여러 개 쓰는 것보다 훨씬 깔끔합니다.

|로 서로 다른 타입의 패턴을 섞을 수도 있습니다:

python
# 예/아니오 질문용 입력 검증기
response = "yes"
 
match response:
    case True | "yes":
        print("You confirmed the action")
    case False | "no":
        print("You cancelled the action")
    case _:
        print("Please answer yes or no")

Output:

You confirmed the action

13.2.4) as로 어떤 대안이 매칭됐는지 캡처하기

|로 여러 패턴을 사용할 때, 어떤 특정 값이 매칭되었는지 알고 싶을 수 있습니다. as 키워드를 사용하면 매칭된 값을 캡처할 수 있습니다:

python
# 그룹화된 응답을 사용하는 상태 코드 처리기
status = 201
 
match status:
    case 200 | 201 | 202 | 204 as success_code:
        print(f"Success: {success_code}")
    case 400 | 401 | 403 | 404 as client_error:
        print(f"Client error: {client_error}")
    case 500 | 502 | 503 as server_error:
        print(f"Server error: {server_error}")
    case _:
        print("Unknown status code")

Output:

Success: 201

as 키워드는 어떤 대안이 매칭되었든 그 값을 캡처하는 바인딩 변수를 생성합니다. 이 예시에서는 200 | 201 | 202 | 204 대안 중 실제로 매칭된 값이 201이므로 success_code201에 바인딩됩니다.

다음은 로깅에서 이것이 어떻게 유용한지 보여주는 또 다른 예시입니다:

python
# 로그 레벨 처리기
log_level = "WARN"
 
match log_level:
    case "DEBUG" | "TRACE" as level:
        print(f"Verbose logging: {level}")
        print("Detailed diagnostic information will be recorded")
    case "INFO" | "NOTICE" as level:
        print(f"Informational: {level}")
        print("Normal operation messages will be recorded")
    case "WARN" | "WARNING" as level:
        print(f"Warning level: {level}")
        print("Potential issues detected")
    case "ERROR" | "FATAL" | "CRITICAL" as level:
        print(f"Error level: {level}")
        print("Immediate attention required")
    case _:
        print("Unknown log level")

Output:

Warning level: WARN
Potential issues detected

13.3) 바인딩 변수로 값 추출하기

13.3.1) 바인딩 변수란 무엇인가요?

지금까지는 리터럴 값과 매칭해 왔습니다. 하지만 매칭하려는 데이터의 일부를 캡처(capture) 하거나 추출(extract) 할 수 있을 때 패턴 매칭은 진정으로 강력해집니다. 바인딩 변수(binding variable) ( 캡처 패턴(capture pattern) 이라고도 함)는 패턴에서 매칭된 값을 캡처하고, case 블록에서 사용할 수 있도록 해주는 이름입니다.

간단한 예시를 보겠습니다:

python
# 간단한 값 캡처
command = "save"
 
match command:
    case "quit":
        print("Exiting program")
    case action:  # 이것은 바인딩 변수입니다
        print(f"Executing action: {action}")

Output:

Executing action: save

action 패턴은 바인딩 변수입니다. 이는 와일드카드 _처럼 어떤 값과도 매칭되지만, _와 달리 그 값을 캡처하여 action이라는 이름에 할당합니다. case 블록 안에서는 action을 사용해 매칭된 값을 참조할 수 있습니다.

중요한 차이점: 바인딩 변수는 _처럼 무엇이든 매칭됩니다. 차이점은 _는 값을 버리는 반면, 바인딩 변수는 case 블록에서 사용하기 위해 값을 캡처한다는 점입니다.

13.3.2) 바인딩 변수 vs 와일드카드

바인딩 변수와 와일드카드를 직접 비교해 보겠습니다:

python
# 와일드카드 사용 - 값이 캡처되지 않음
status = 403
 
match status:
    case 200:
        print("Success")
    case _:
        print("Some other status code")  # 실제 값에 접근할 수 없음

Output:

Some other status code

이제 바인딩 변수를 사용해 보겠습니다:

python
# 바인딩 변수 사용 - 값이 캡처됨
status = 403
 
match status:
    case 200:
        print("Success")
    case code:  # 바인딩 변수가 값을 캡처함
        print(f"Status code {code} received")

Output:

Status code 403 received

바인딩 변수 code403 값을 캡처하여 case 블록 안에서 사용할 수 있게 합니다. 이는 특정 패턴과 매칭되지 않은 실제 값으로 작업해야 할 때 유용합니다.

13.3.3) 튜플 패턴 매칭과 구성 요소 추출하기

패턴 매칭은 튜플 같은 구조화된 데이터에서 특히 강력합니다. 튜플의 형태를 매칭하면서 동시에 구성 요소를 추출할 수 있습니다. 15장에서 튜플을 자세히 공부하겠지만, 이 예시는 match 문에서 튜플 패턴이 어떻게 동작하는지만 다룹니다.

python
# 좌표계 - 튜플 패턴 매칭
point = (3, 7)
 
match point:
    case (0, 0):
        print("Origin point")
    case (0, y):  # y축 위의 어떤 점과도 매칭
        print(f"On y-axis at y={y}")
    case (x, 0):  # x축 위의 어떤 점과도 매칭
        print(f"On x-axis at x={x}")
    case (x, y):  # 그 외 어떤 점과도 매칭
        print(f"Point at coordinates ({x}, {y})")

Output:

Point at coordinates (3, 7)

무슨 일이 일어나는지 쪼개서 살펴보겠습니다:

  1. 대상 point는 튜플 (3, 7)입니다
  2. Python은 각 case 패턴을 순서대로 확인합니다
  3. 처음 세 패턴은 튜플의 특정 위치에 0이라는 리터럴 값이 정확히 일치해야 하지만, point의 값 (3, 7)에는 해당 위치에 0이 없기 때문에 매칭되지 않습니다
  4. (x, y) 패턴은 두 원소를 가진 튜플이기 때문에 매칭됩니다
  5. Python은 x3에, y7에 바인딩합니다
  6. 캡처된 값과 함께 case 블록이 실행됩니다

다음은 서로 다른 튜플 패턴을 보여주는 또 다른 예시입니다:

python
# RGB 색상 분석기
color = (255, 0, 0)
 
match color:
    case (0, 0, 0):
        print("Black")
    case (255, 255, 255):
        print("White")
    case (r, 0, 0):  # 강도가 변하는 순수한 빨강
        print(f"Pure red with intensity {r}")
    case (0, g, 0):  # 순수한 초록
        print(f"Pure green with intensity {g}")
    case (0, 0, b):  # 순수한 파랑
        print(f"Pure blue with intensity {b}")
    case (r, g, b):  # 그 외 어떤 색이든
        print(f"RGB color: red={r}, green={g}, blue={b}")

Output:

Pure red with intensity 255

이 입력은 (r, 0, 0) 패턴과 매칭됩니다. 튜플이 세 원소를 가지고, 마지막 두 원소가 0이며, 첫 번째 값이 r에 바인딩되기 때문입니다.

13.3.4) 리스트 패턴 매칭하기

리스트 패턴도 매칭하면서 원소를 추출할 수 있습니다. 14장에서 리스트를 자세히 다루겠지만, 지금은 match 문에서 리스트 패턴이 어떻게 동작하는지에 초점을 맞춥니다:

python
# 인자가 있는 명령
command = ["move", "north", "5"]
 
match command:
    case ["quit"]:
        print("Exiting game")
    case ["look"]:
        print("You look around the room")
    case ["move", direction]:
        print(f"Moving {direction}")
    case ["move", direction, distance]:
        print(f"Moving {direction} for {distance} steps")
    case _:
        print("Unknown command")

Output:

Moving north for 5 steps

["move", direction, distance] 패턴은 첫 번째 원소가 "move"인 3원소 리스트와 매칭됩니다. 두 번째 원소는 direction으로, 세 번째 원소는 distance로 캡처합니다.

다음은 길이가 달라지는 리스트를 다루는 실용적인 예시입니다:

python
# 장바구니 항목 처리기
item = ["laptop", 999.99, 2]
 
match item:
    case [name]:  # 이름만 있는 항목
        print(f"Item: {name} (no price or quantity specified)")
    case [name, price]:  # 이름과 가격이 있는 항목
        print(f"Item: {name}, Price: ${price}, Quantity: 1 (default)")
    case [name, price, quantity]:  # 완전한 항목 정보
        total = price * quantity
        print(f"Item: {name}, Price: ${price}, Quantity: {quantity}")
        print(f"Subtotal: ${total}")
    case _:
        print("Invalid item format")

Output:

Item: laptop, Price: $999.99, Quantity: 2
Subtotal: $1999.98

리스트가 정확히 3개 원소를 가지므로 [name, price, quantity] case가 실행되며, 각 원소는 해당 변수에 바인딩됩니다.

13.3.5) 딕셔너리 패턴 매칭하기

패턴 매칭은 딕셔너리에서도 동작하며, 특정 키를 매칭하고 값을 추출할 수 있습니다. 16장에서 딕셔너리를 자세히 공부하겠지만, 이 절은 match 문에서 딕셔너리 패턴이 동작하는 방식에만 집중합니다.

python
# 사용자 프로필 처리기
user = {"name": "Alice", "role": "admin", "active": True}
 
match user:
    case {"role": "admin", "active": True}:
        print("Active administrator - full access granted")
    case {"role": "admin", "active": False}:
        print("Inactive administrator - access suspended")
    case {"role": role, "active": True}:  # role 값을 캡처
        print(f"Active user with role: {role}")
    case {"role": role, "active": False}:
        print(f"Inactive user with role: {role}")
    case _:
        print("Invalid user profile")

Output:

Active administrator - full access granted

{"role": "admin", "active": True} case는 딕셔너리 패턴이 키–값 쌍 매칭을 요구하고, 더 일반적인 패턴들보다 앞에서 이 정확한 매칭이 먼저 검사되기 때문에 실행됩니다.

딕셔너리 패턴은 유연합니다—딕셔너리에 추가 키가 있더라도 지정된 키가 지정된 값으로 존재하면 매칭됩니다:

python
# API 응답 처리기
response = {"status": "success", "data": {"id": 123, "name": "Product"}, "timestamp": "2025-12-17"}
 
match response:
    case {"status": "error", "message": msg}:
        print(f"Error occurred: {msg}")
    case {"status": "success", "data": data}:
        print(f"Success! Data received: {data}")
    case _:
        print("Unknown response format")

Output:

Success! Data received: {'id': 123, 'name': 'Product'}

{"status": "success", "data": data} 패턴은 딕셔너리에 "timestamp" 키가 추가로 있음에도 매칭됩니다. 이 패턴은 지정된 키들이 지정된 값(또는 패턴)으로 존재하기만 하면 됩니다.

13.3.6) 리터럴과 바인딩 변수를 결합하기

리터럴 패턴과 바인딩 변수를 섞어 정교한 매칭 로직을 만들 수 있습니다: 앞서 구조와 위치 매칭에 집중했던 튜플 예시들과 달리, 이 예시는 리터럴 값과 바인딩 변수를 결합해 실제 의사결정 로직을 구현하는 방법을 보여줍니다.

python
# HTTP 요청 라우터
request = ("GET", "/api/users", 42)
 
match request:
    case ("GET", "/", None):
        print("Homepage request")
    case ("GET", path, None):
        print(f"GET request for: {path}")
    case ("POST", path, data):
        print(f"POST request to {path} with data: {data}")
    case ("GET", path, user_id):
        print(f"GET request for {path} with user ID: {user_id}")
    case _:
        print("Unsupported request type")

Output:

GET request for /api/users with user ID: 42

이 예시는 "GET"처럼 특정 값을 매칭하면서도, 같은 패턴 안에서 path, user_id처럼 다른 값은 캡처할 수 있음을 보여줍니다.

13.3.7) 실전 예시: 이벤트 핸들러

바인딩 변수의 강력함을 보여주는 완전한 예시를 만들어 보겠습니다:

python
# 게임 이벤트 핸들러
event = ("player_move", {"x": 10, "y": 5, "speed": 2})
 
match event:
    case ("player_move", {"x": x, "y": y}):
        print(f"Player moved to position ({x}, {y})")
    case ("player_attack", {"target": target, "damage": damage}):
        print(f"Player attacked {target} for {damage} damage")
    case ("item_pickup", {"item": item_name}):
        print(f"Player picked up: {item_name}")
    case ("game_over", {"score": final_score}):
        print(f"Game ended. Final score: {final_score}")
    case (event_type, data):
        print(f"Unknown event type: {event_type}")
        print(f"Event data: {data}")

Output:

Player moved to position (10, 5)

이 이벤트 핸들러는 이벤트 타입과 이벤트 데이터 딕셔너리를 담은 튜플을 매칭합니다. 이벤트 타입에 따라 딕셔너리에서 특정 값을 추출하므로, 다양한 종류의 이벤트를 깔끔하고 읽기 쉬운 코드로 처리할 수 있습니다.

13.4) if 가드로 추가 조건 더하기

13.4.1) 가드란 무엇인가요?

때로는 패턴을 매칭하면서 추가 조건 도 확인해야 합니다. if 가드(if guard)if 키워드를 사용해 case 패턴에 추가할 수 있는 조건입니다. 패턴이 매칭되고 가드 조건까지 참일 때만 해당 case가 매칭됩니다.

문법은 다음과 같습니다:

python
match subject:
    case pattern if condition:
        # pattern이 매칭되고 condition도 True일 때만 코드가 실행됩니다

간단한 예시를 보겠습니다:

python
# 나이에 따른 접근 제어
age = 16
 
match age:
    case age if age >= 18:
        print("Adult - full access granted")
    case age if age >= 13:
        print("Teen - limited access granted")
    case age if age >= 0:
        print("Child - parental supervision required")
    case _:
        print("Invalid age")

Output:

Teen - limited access granted

이 예시에서 바인딩 변수 age가 값을 캡처하고, if age >= 13 가드가 추가 조건을 더합니다. 이 case는 값이 13 이상일 때만 매칭됩니다. age16이므로 두 번째 case가 매칭되어 실행됩니다.

13.4.2) 가드 평가 방식

평가 순서를 이해하는 것은 중요합니다. 다음은 가드가 패턴 매칭과 어떻게 상호작용하는지 보여주는 자세한 시각화입니다:

아니오

아니오

아니오

case 패턴 확인

패턴이 매칭되는가?

다음 case 시도

가드 조건 평가

가드가 True인가?

case 블록 실행

match 문 종료

더 많은 case가 있는가?

매칭 없음 - match 이후 계속 진행

Python은 먼저 패턴이 매칭되는지 확인합니다. 패턴이 매칭되는 경우에만 가드 조건을 평가합니다. 가드가 거짓이면, 패턴이 매칭되었더라도 Python은 다음 case로 넘어갑니다.

이를 보여주는 예시입니다:

python
# 온도 경고 시스템
temperature = 25
 
match temperature:
    case temp if temp > 35:
        print(f"Extreme heat warning: {temp}°C")
    case temp if temp > 30:
        print(f"High temperature alert: {temp}°C")
    case temp if temp > 20:
        print(f"Comfortable temperature: {temp}°C")
    case temp if temp > 10:
        print(f"Cool temperature: {temp}°C")
    case temp:
        print(f"Cold temperature: {temp}°C")

Output:

Comfortable temperature: 25°C

각 case는 바인딩 변수 temp로 온도 값을 캡처한 다음, 가드를 적용해 특정 범위에 속하는지 확인합니다. case는 순서대로 검사되므로, 가드가 참인 첫 번째 매칭 case가 실행됩니다.

13.4.3) 리터럴 패턴과 함께 사용하는 가드

가드를 리터럴 패턴과 결합해 더 구체적인 매칭을 만들 수 있습니다:

python
# 품목 종류와 수량에 따른 할인 계산기
item = ("book", 5)
 
match item:
    case ("book", quantity) if quantity >= 10:
        discount = 0.20  # 10권 이상 책에 대한 20% 할인
        print(f"Bulk book order: {quantity} books, {discount*100}% discount")
    case ("book", quantity) if quantity >= 5:
        discount = 0.10  # 5~9권 책에 대한 10% 할인
        print(f"Book order: {quantity} books, {discount*100}% discount")
    case ("book", quantity):
        discount = 0.0  # 5권 미만은 할인 없음
        print(f"Book order: {quantity} books, no discount")
    case (item_type, quantity):
        print(f"Order: {quantity} {item_type}(s)")

Output:

Book order: 5 books, 10.0% discount

("book", quantity) 패턴은 첫 번째 원소가 "book"인 튜플과 매칭됩니다. if quantity >= 5 가드는 수량이 최소 5개여야 한다는 조건을 추가합니다.

13.4.4) 복잡한 조건을 가진 가드

가드는 and, or, not을 포함한 어떤 불리언 표현식도 사용할 수 있으며, 복잡한 조건도 포함할 수 있습니다:

python
# 출석을 고려한 학생 성적 평가기
student = {"name": "Bob", "grade": 85, "attendance": 75}
 
match student:
    case {"grade": g, "attendance": a} if g >= 90 and a >= 90:
        status = "Excellent"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 80 and a >= 80:
        status = "Good"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 70 and a >= 70:
        status = "Satisfactory"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case {"grade": g, "attendance": a} if g >= 60 or a >= 60:
        status = "Needs Improvement"
        print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
    case _:
        print("Failing - immediate intervention required")

Output:

Grade: 85, Attendance: 75% - Status: Satisfactory

if g >= 70 and a >= 70 가드는 성적과 출석률이 모두 70 이상이어야 한다고 요구합니다. Bob은 성적이 85이고 출석률이 75%이므로 이 case가 매칭됩니다.

13.4.5) 실전 예시: 사용자 인증 시스템

가드를 사용해 현실적인 인증 시스템을 구현하는 완전한 예시를 만들어 보겠습니다:

python
# 역할 기반 접근을 포함한 사용자 인증
user = {"username": "alice", "role": "admin", "active": True, "login_attempts": 0}
 
match user:
    case {"active": False}:
        print("Account suspended - contact administrator")
    case {"login_attempts": attempts} if attempts >= 3:
        print("Account locked due to too many failed login attempts")
    case {"role": "admin", "active": True}:
        print("Admin access granted - full system privileges")
    case {"role": "moderator", "active": True}:
        print("Moderator access granted - content management privileges")
    case {"role": role, "active": True} if role in ["editor", "author"]:
        print(f"{role.capitalize()} access granted - content creation privileges")
    case {"role": "user", "active": True}:
        print("User access granted - basic privileges")
    case _:
        print("Access denied - invalid user profile")

Output:

Admin access granted - full system privileges

이 예시는 가드로 복잡한 비즈니스 로직을 구현할 수 있음을 보여줍니다. 시스템은 계정 상태, 로그인 시도 횟수, 역할 기반 권한 등 여러 조건을 확인합니다. 각 case는 특정 시나리오를 처리하므로 인증 로직이 명확하고 유지보수하기 쉬워집니다.

13.5) 단순한 시퀀스와 매핑 형태로 매칭하기

13.5.1) 가변 길이 시퀀스 매칭하기

때로는 길이가 다양한 시퀀스를 매칭해야 합니다. Python의 패턴 매칭은 * 연산자로 이를 지원하며, 0개 이상의 원소를 캡처합니다.

python
# 가변 인자를 가진 명령 파서
command = ["copy", "file1.txt", "file2.txt", "file3.txt", "backup/"]
 
match command:
    case ["help"]:
        print("Available commands: copy, move, delete")
    case ["copy", source, destination]:
        print(f"Copying {source} to {destination}")
    case ["copy", *sources, destination]:
        print(f"Copying {len(sources)} files to {destination}")
        print(f"Source files: {sources}")
    case ["delete", *files]:
        print(f"Deleting {len(files)} file(s): {files}")
    case _:
        print("Unknown command")

Output:

Copying 3 files to backup/
Source files: ['file1.txt', 'file2.txt', 'file3.txt']

["copy", *sources, destination] 패턴은 "copy"로 시작하고 destination으로 끝나며, 그 사이에 어떤 개수의 source 파일이든 포함하는 리스트와 매칭됩니다. *sources는 중간의 모든 원소를 리스트로 캡처합니다.

중요: 시퀀스 패턴에서는 * 패턴을 하나만 사용할 수 있으며, 원소를 리스트로 캡처합니다:

python
# 로그 엔트리 파서
log_entry = ["2025-12-17", "10:30:45", "ERROR", "Database", "connection", "timeout"]
 
match log_entry:
    case [date, time, "ERROR", *error_details]:
        print(f"Error on {date} at {time}")
        print(f"Error details: {' '.join(error_details)}")
    case [date, time, "WARNING", *warning_details]:
        print(f"Warning on {date} at {time}")
        print(f"Warning details: {' '.join(warning_details)}")
    case [date, time, level, *message]:
        print(f"{level} on {date} at {time}: {' '.join(message)}")

Output:

Error on 2025-12-17 at 10:30:45
Error details: Database connection timeout

13.5.2) 시퀀스 패턴과 가드 결합하기

시퀀스 패턴과 함께 가드를 사용해 추가 조건을 더할 수 있습니다:

python
# 성적 리스트 분석기
grades = [85, 92, 78, 95, 88]
 
match grades:
    case []:
        print("No grades recorded")
    case [grade] if grade >= 90:
        print(f"Single excellent grade: {grade}")
    case [grade] if grade < 60:
        print(f"Single failing grade: {grade}")
    case [*all_grades] if len(all_grades) >= 5 and sum(all_grades) / len(all_grades) >= 90:
        average = sum(all_grades) / len(all_grades)
        print(f"Excellent performance! Average: {average:.1f}")
    case [*all_grades] if len(all_grades) >= 5:
        average = sum(all_grades) / len(all_grades)
        print(f"Performance review: {len(all_grades)} grades, Average: {average:.1f}")
    case [*all_grades]:
        print(f"Insufficient data: only {len(all_grades)} grade(s)")

Output:

Performance review: 5 grades, Average: 87.6

[*all_grades] 패턴은 리스트의 모든 원소를 캡처하며, 가드는 길이를 확인하고 평균을 계산해 적절한 메시지를 결정합니다.


matchcase를 사용하는 구조적 패턴 매칭은 Python에서 복잡한 의사결정을 처리하기 위한 강력하고 표현력 있는 방법을 제공합니다. 단순한 값 매칭부터 가드가 포함된 정교한 구조적 패턴까지, 이 기능은 여러 경우를 처리하고 복잡한 구조에서 데이터를 추출하는 더 깔끔하고 유지보수하기 쉬운 코드를 작성할 수 있게 해줍니다.

Python 학습을 계속하다 보면, 패턴 매칭이 8장에서 배운 조건 로직을 보완하며, 여러 개의 뚜렷한 case를 다룰 때(특히 구조화된 데이터로 작업할 때) 우아한 대안을 제공한다는 것을 알게 될 것입니다. 핵심은 각 상황에 맞는 올바른 도구를 선택하는 것입니다. 단순 조건과 불리언 로직에는 if-elif-else를 사용하고, 하나의 값을 여러 가능성과 비교하거나 패턴 기반 추출이 필요한 구조화된 데이터를 다룰 때는 match-case를 활용하세요.


© 2025. Primesoft Co., Ltd.
support@primesoft.ai