포스트

[Python 100일 챌린지] Day 26 - 기본 매개변수와 키워드 인자

[Python 100일 챌린지] Day 26 - 기본 매개변수와 키워드 인자

함수 호출할 때마다 모든 매개변수를 다 써야 한다면 불편하겠죠? 🤔 ──자주 쓰는 값은 “기본값”으로 설정하고, 필요할 때만 바꿀 수 있습니다! 😊

print()를 쓸 때 end="\n"을 매번 안 쓰는 이유가 뭘까요? 이미 기본값으로 설정되어 있기 때문입니다!

오늘은 우리 함수도 이렇게 편리하게 만드는 법을 배웁니다. 선택적 매개변수로 유연한 함수를 디자인하는 실전 기법! 💡

(30분 완독 ⭐⭐⭐)

🎯 오늘의 학습 목표

  1. 기본 매개변수로 선택적 값 받기
  2. 키워드 인자로 명확하게 전달하기
  3. 키워드 인자의 장점 활용하기
  4. 위치/키워드 인자 순서 규칙 이해하기
  5. 가변 기본값 함정 피하기
  6. 실전 함수 설계 패턴 익히기

📚 사전 지식

🎯 학습 목표 1: 기본값 매개변수 설정하기

한 줄 설명

기본 매개변수 = 함수 정의 시 매개변수에 미리 설정해둔 기본값

매개변수에 값을 전달하지 않으면 자동으로 기본값이 사용됩니다.

실생활 비유

1
2
3
4
5
6
7
8
9
📱 전화기 설정:

벨소리 설정(소리="기본벨소리", 볼륨=50)
- 소리를 지정하지 않으면? → 기본벨소리 사용
- 볼륨을 지정하지 않으면? → 50으로 설정

커피 주문:
주문(종류="아메리카노", 사이즈="미디엄", 샷="2개")
- 종류만 말하면? → 미디엄 사이즈, 2샷으로 자동 주문

🎯 학습 목표 2: 키워드 전용 인자 사용하기

기본 문법

1
2
def 함수이름(매개변수=기본값):
    실행할 코드

첫 번째 예제

1
2
3
4
5
6
7
8
9
def greet(name="손님"):
    """이름을 받아 인사하는 함수 (기본값: 손님)"""
    print(f"안녕하세요, {name}님!")

# 값을 전달하면 전달한 값 사용
greet("홍길동")  # 안녕하세요, 홍길동님!

# 값을 전달하지 않으면 기본값 사용
greet()  # 안녕하세요, 손님님!

여러 개의 기본값

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def introduce(name, age=0, city="미정"):
    """자기소개 함수 (나이와 도시는 선택사항)"""
    print(f"이름: {name}")
    print(f"나이: {age}")
    print(f"도시: {city}")

# 모든 값 전달
introduce("홍길동", 25, "서울")
# 이름: 홍길동
# 나이: 25세
# 도시: 서울

# 이름만 전달 (나머지는 기본값)
introduce("김철수")
# 이름: 김철수
# 나이: 0세
# 도시: 미정

# 이름과 나이만 전달
introduce("이영희", 30)
# 이름: 이영희
# 나이: 30세
# 도시: 미정

실전 예제 1: 계산기 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def calculate(a, b, operation="+"):
    """두 숫자를 계산하는 함수 (기본 연산: 덧셈)"""
    if operation == "+":
        return a + b
    elif operation == "-":
        return a - b
    elif operation == "*":
        return a * b
    elif operation == "/":
        return a / b if b != 0 else "0으로 나눌 수 없음"
    else:
        return "지원하지 않는 연산"

# 기본 연산(덧셈) 사용
print(calculate(10, 5))        # 15

# 연산 지정
print(calculate(10, 5, "-"))   # 5
print(calculate(10, 5, "*"))   # 50
print(calculate(10, 5, "/"))   # 2.0

실전 예제 2: 파일 읽기 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def read_file(filename, encoding="utf-8", mode="r"):
    """파일을 읽는 함수 (기본: UTF-8, 읽기 모드)"""
    try:
        with open(filename, mode, encoding=encoding) as f:
            return f.read()
    except FileNotFoundError:
        return f"파일을 찾을 수 없습니다: {filename}"

# 기본 설정으로 읽기
content = read_file("data.txt")

# 다른 인코딩으로 읽기
content = read_file("data.txt", "cp949")

# 바이너리 모드로 읽기
content = read_file("image.png", mode="rb")

실전 예제 3: 사용자 프로필 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def create_profile(name, age=18, city="서울", job="학생", hobby="독서"):
    """사용자 프로필을 생성하는 함수"""
    profile = {
        "이름": name,
        "나이": age,
        "도시": city,
        "직업": job,
        "취미": hobby
    }
    return profile

# 최소 정보만 제공
profile1 = create_profile("홍길동")
print(profile1)
# {'이름': '홍길동', '나이': 18, '도시': '서울', '직업': '학생', '취미': '독서'}

# 일부 정보 제공
profile2 = create_profile("김철수", 25, "부산")
print(profile2)
# {'이름': '김철수', '나이': 25, '도시': '부산', '직업': '학생', '취미': '독서'}

🎯 학습 목표 2: 키워드 전용 인자 사용하기

한 줄 설명

키워드 인자 = 매개변수 이름을 명시해서 값을 전달하는 방식

순서에 상관없이 명확하게 값을 전달할 수 있습니다.

위치 인자 vs 키워드 인자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def book_info(title, author, year):
    print(f"제목: {title}")
    print(f"저자: {author}")
    print(f"출판년도: {year}")

# 위치 인자 (Positional Arguments)
# - 순서가 중요함
book_info("Python 입문", "홍길동", 2024)

# 키워드 인자 (Keyword Arguments)
# - 순서가 중요하지 않음
book_info(title="Python 입문", author="홍길동", year=2024)
book_info(author="홍길동", year=2024, title="Python 입문")  # 순서 바뀌어도 OK!
book_info(year=2024, title="Python 입문", author="홍길동")  # 이것도 OK!

출력 (모두 동일):

1
2
3
제목: Python 입문
저자: 홍길동
출판년도: 2024

🎯 학습 목표 3: 키워드 인자의 장점 활용하기

장점 1: 가독성 향상

1
2
3
4
5
6
7
8
9
10
11
12
# ❌ 위치 인자 - 무엇이 무엇인지 불명확
create_user("john", "john@example.com", "1234", True, False, 25)

# ✅ 키워드 인자 - 명확하고 읽기 쉬움
create_user(
    username="john",
    email="john@example.com",
    password="1234",
    is_active=True,
    is_admin=False,
    age=25
)

장점 2: 순서 걱정 없음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def send_email(to, subject, body, cc=None, bcc=None):
    """이메일을 보내는 함수"""
    print(f"To: {to}")
    print(f"Subject: {subject}")
    print(f"Body: {body}")
    if cc:
        print(f"CC: {cc}")
    if bcc:
        print(f"BCC: {bcc}")

# 순서를 바꿔도 정확히 전달됨
send_email(
    body="안녕하세요!",
    subject="회의 안내",
    to="team@example.com",
    cc="boss@example.com"
)

장점 3: 선택적 매개변수 지정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def format_text(text, bold=False, italic=False, underline=False, color="black"):
    """텍스트 서식을 지정하는 함수"""
    result = text

    if bold:
        result = f"**{result}**"
    if italic:
        result = f"*{result}*"
    if underline:
        result = f"_{result}_"

    result = f"[{color}] {result}"
    return result

# 필요한 것만 지정
print(format_text("중요한 텍스트", bold=True))
print(format_text("강조 텍스트", italic=True, color="red"))
print(format_text("밑줄 텍스트", underline=True))

출력:

1
2
3
[black] **중요한 텍스트**
[red] *강조 텍스트*
[black] _밑줄 텍스트_

🎯 학습 목표 4: 매개변수 순서 규칙 이해하기

기본 규칙

중요: 위치 인자는 항상 키워드 인자보다 앞에 와야 합니다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def register(username, email, password, newsletter=False, notifications=True):
    print(f"사용자명: {username}")
    print(f"이메일: {email}")
    print(f"비밀번호: {password}")
    print(f"뉴스레터 구독: {newsletter}")
    print(f"알림 수신: {notifications}")

# ✅ 올바른 순서: 위치 인자 → 키워드 인자
register("john", "john@example.com", "1234", newsletter=True)

# ✅ 모두 위치 인자
register("john", "john@example.com", "1234", True, False)

# ✅ 모두 키워드 인자
register(username="john", email="john@example.com", password="1234", newsletter=True)

# ❌ 잘못된 순서: 키워드 인자 뒤에 위치 인자 (에러!)
# register("john", email="john@example.com", "1234")  # SyntaxError!

혼합 사용 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def create_order(customer_name, product_name, quantity,
                 express=False, gift_wrap=False, message=""):
    """주문을 생성하는 함수"""
    order = {
        "고객": customer_name,
        "상품": product_name,
        "수량": quantity,
        "특급배송": express,
        "선물포장": gift_wrap,
        "메시지": message
    }
    return order

# 다양한 호출 방식
order1 = create_order("홍길동", "노트북", 1)
print(order1)

order2 = create_order("김철수", "", 3, express=True)
print(order2)

order3 = create_order("이영희", "", 1, gift_wrap=True, message="생일 축하해!")
print(order3)

# 위치 인자 일부 + 키워드 인자
order4 = create_order("박민수", "시계", 2, message="감사합니다", express=True)
print(order4)

🎯 학습 목표 5: 가변 기본값 함정 피하기

주의 1: 가변 기본값 (리스트, 딕셔너리)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# ❌ 위험! 가변 기본값
def add_item(item, items=[]):  # 함수 정의 시 한 번만 생성됨
    items.append(item)
    return items

# 문제 발생
list1 = add_item("사과")
print(list1)  # ['사과']

list2 = add_item("바나나")
print(list2)  # ['사과', '바나나'] ⚠️ 이전 값이 남아있음!

list3 = add_item("포도")
print(list3)  # ['사과', '바나나', '포도'] ⚠️ 계속 누적됨!

# ✅ 올바른 방법: None을 기본값으로 사용
def add_item_safe(item, items=None):
    if items is None:
        items = []  # 매번 새로운 리스트 생성
    items.append(item)
    return items

# 정상 작동
list1 = add_item_safe("사과")
print(list1)  # ['사과']

list2 = add_item_safe("바나나")
print(list2)  # ['바나나'] ✅ 새로운 리스트!

list3 = add_item_safe("포도")
print(list3)  # ['포도'] ✅ 새로운 리스트!

주의 2: 기본값은 함수 정의 시 평가됨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import datetime

# ❌ 잘못된 방법
def log_message(message, timestamp=datetime.datetime.now()):
    """로그 메시지 (잘못된 예)"""
    print(f"[{timestamp}] {message}")

# 모두 같은 시간이 출력됨 (함수 정의 시점의 시간)
log_message("첫 번째 로그")
import time
time.sleep(2)
log_message("두 번째 로그")  # 시간이 같음!

# ✅ 올바른 방법
def log_message_correct(message, timestamp=None):
    """로그 메시지 (올바른 예)"""
    if timestamp is None:
        timestamp = datetime.datetime.now()  # 호출 시마다 새로운 시간
    print(f"[{timestamp}] {message}")

# 각각 다른 시간이 출력됨
log_message_correct("첫 번째 로그")
time.sleep(2)
log_message_correct("두 번째 로그")  # 시간이 다름!

주의 3: 키워드 인자 순서

1
2
3
4
5
6
7
8
9
10
11
12
# ✅ 올바른 순서
def func(a, b, c=0, d=0):
    return a + b + c + d

# ✅ 정상 호출
func(1, 2)                    # 위치 인자만
func(1, 2, 3)                 # 위치 인자 + 기본값 일부 변경
func(1, 2, c=3, d=4)          # 위치 인자 + 키워드 인자
func(1, 2, d=4)               # 일부 기본값만 변경

# ❌ 잘못된 순서
# func(1, c=3, 2)             # SyntaxError: 키워드 인자 뒤에 위치 인자

🎯 학습 목표 6: 실전 함수 설계 패턴 익히기

예제 1: 데이터베이스 연결 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def connect_database(host="localhost", port=5432, database="mydb",
                     username="admin", password="", timeout=30):
    """데이터베이스 연결 함수"""
    config = {
        "호스트": host,
        "포트": port,
        "데이터베이스": database,
        "사용자명": username,
        "비밀번호": "***" if password else "(없음)",
        "타임아웃": timeout
    }

    print("=== 데이터베이스 연결 시도 ===")
    for key, value in config.items():
        print(f"{key}: {value}")

    return "연결 성공!"

# 기본 설정으로 연결
result1 = connect_database()

# 일부 설정만 변경
result2 = connect_database(database="production", password="secret123")

# 원격 서버 연결
result3 = connect_database(
    host="192.168.1.100",
    port=3306,
    database="production",
    username="root",
    password="secure_pass",
    timeout=60
)

예제 2: HTTP 요청 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def make_request(url, method="GET", headers=None, data=None, timeout=10):
    """HTTP 요청을 생성하는 함수 (시뮬레이션)"""
    print(f"=== HTTP 요청 ===")
    print(f"URL: {url}")
    print(f"메서드: {method}")
    print(f"타임아웃: {timeout}")

    if headers:
        print("헤더:")
        for key, value in headers.items():
            print(f"  {key}: {value}")

    if data:
        print(f"데이터: {data}")

    return "요청 성공"

# GET 요청 (기본값 사용)
make_request("https://api.example.com/users")

# POST 요청 with 데이터
make_request(
    "https://api.example.com/users",
    method="POST",
    headers={"Content-Type": "application/json"},
    data={"name": "홍길동", "email": "hong@example.com"}
)

# 타임아웃 설정
make_request("https://slow-api.example.com", timeout=60)

예제 3: 이미지 처리 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def process_image(filename, width=None, height=None,
                  format="png", quality=90, grayscale=False):
    """이미지를 처리하는 함수 (시뮬레이션)"""
    print(f"=== 이미지 처리: {filename} ===")

    if width or height:
        print(f"크기 조정: {width or '자동'}x{height or '자동'}")

    print(f"포맷: {format}")
    print(f"품질: {quality}%")

    if grayscale:
        print("흑백 변환 적용")

    return f"{filename}.{format}"

# 기본 설정으로 처리
process_image("photo.jpg")

# 크기 조정
process_image("photo.jpg", width=800, height=600)

# 품질과 포맷 변경
process_image("photo.jpg", format="jpg", quality=85)

# 흑백 변환
process_image("photo.jpg", width=400, grayscale=True)

💡 실전 팁 & 주의사항

💡 Tip 1: 기본값으로 자주 쓰는 값 설정하기

함수를 사용할 때 가장 자주 쓰는 값을 기본값으로 설정하세요.

1
2
3
4
5
6
7
8
9
10
# ✅ 좋은 예시
def send_email(to, subject, body, priority="normal"):
    """대부분의 이메일은 보통 우선순위"""
    pass

# 대부분의 경우
send_email("user@example.com", "회의 안내", "내일 10시 회의")

# 긴급한 경우만 명시
send_email("boss@example.com", "긴급", "확인 요망", priority="high")

💡 Tip 2: 키워드 인자로 가독성 높이기

매개변수가 3개 이상이면 키워드 인자를 사용하세요.

1
2
3
4
5
6
7
8
9
10
11
12
# ❌ 읽기 어려움
create_user("john", "john@example.com", "1234", True, False, 25)

# ✅ 명확하고 읽기 쉬움
create_user(
    username="john",
    email="john@example.com",
    password="1234",
    is_active=True,
    is_admin=False,
    age=25
)

💡 Tip 3: 선택적 매개변수는 뒤에 배치

필수 매개변수는 앞에, 선택적 매개변수는 뒤에 배치하세요.

1
2
3
4
5
6
# ✅ 올바른 순서
def register(username, email, password, newsletter=False):
    pass

# ❌ 잘못된 순서
# def register(newsletter=False, username, email, password):  # SyntaxError!

💡 Tip 4: 가변 기본값은 절대 사용 금지! ⚠️

리스트, 딕셔너리 등 가변 객체는 기본값으로 사용하지 마세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ❌ 위험! 누적됨
def add_item(item, items=[]):
    items.append(item)
    return items

list1 = add_item("사과")   # ['사과']
list2 = add_item("바나나")  # ['사과', '바나나'] ⚠️ 누적!

# ✅ 안전: None 사용
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

💡 Tip 5: 시간 관련 기본값도 주의 ⚠️

datetime 같은 시간 관련 기본값도 None을 사용하세요.

1
2
3
4
5
6
7
8
9
# ❌ 모두 같은 시간 (함수 정의 시점의 시간)
def log(message, timestamp=datetime.now()):
    pass

# ✅ 호출 시마다 다른 시간
def log(message, timestamp=None):
    if timestamp is None:
        timestamp = datetime.now()
    pass

💡 Tip 6: 위치 인자 먼저, 키워드 인자 나중 ⚠️

1
2
3
4
5
# ✅ 올바른 순서
func(1, 2, c=3, d=4)

# ❌ 잘못된 순서
# func(1, c=3, 2)  # SyntaxError!

💡 Tip 7: 필수 매개변수를 기본값 뒤에 두지 않기 ⚠️

1
2
3
4
5
6
7
# ❌ SyntaxError!
# def func(a=1, b, c):
#     pass

# ✅ 필수 매개변수 먼저
def func(b, c, a=1):
    pass

💡 Tip 8: 설정 함수 패턴 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def configure_server(port=8000, host="0.0.0.0", debug=False,
                     workers=4, max_connections=100):
    """서버 설정 함수"""
    config = {
        "포트": port,
        "호스트": host,
        "디버그 모드": debug,
        "워커 수": workers,
        "최대 연결": max_connections
    }

    print("=== 서버 설정 ===")
    for key, value in config.items():
        print(f"{key}: {value}")

    return config

# 개발 환경 (디버그 모드)
dev_config = configure_server(debug=True, workers=1)

# 운영 환경 (포트 변경, 워커 증가)
prod_config = configure_server(port=80, workers=8, max_connections=1000)

# 테스트 환경 (다른 포트)
test_config = configure_server(port=9000)

💡 Tip 9: 플러그인 시스템 패턴 - 선택적 기능 활성화

여러 옵션을 조합할 수 있는 유연한 함수를 만들 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def apply_filter(image, blur=0, brightness=0, contrast=0, sharpen=False):
    """이미지 필터 적용 - 원하는 필터만 선택"""
    filters = []
    if blur > 0:
        filters.append(f"블러: {blur}%")
    if brightness != 0:
        filters.append(f"밝기: {brightness:+d}%")
    if sharpen:
        filters.append("샤프닝 적용")

    print(f"'{image}'에 적용: {', '.join(filters) if filters else '없음'}")
    return image

# 필요한 필터만 활성화
apply_filter("photo.jpg", blur=5)
apply_filter("photo.jpg", brightness=20, contrast=10)

💡 Tip 10: API 래퍼 패턴 - 복잡한 기본 설정

복잡한 함수는 합리적인 기본값으로 간단하게 사용하고, 필요시 세밀하게 제어할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
def api_call(endpoint, method="GET", params=None,
             retry=3, timeout=30, verify_ssl=True):
    """API 호출 - 대부분은 기본값으로 충분"""
    print(f"{method} {endpoint} (재시도:{retry}, 타임아웃:{timeout}초)")
    return {"status": 200}

# 간단한 사용
api_call("/api/users")

# 필요시 세밀한 제어
api_call("/api/users", method="POST", retry=5, timeout=60)

💡 Tip 11: Boolean 플래그는 명확하게 이름 짓기

Boolean 매개변수는 is_, has_, should_ 등으로 시작하면 가독성이 좋습니다.

1
2
3
4
5
6
7
8
9
# ✅ 명확한 이름
def send_email(to, subject, body, should_retry=True, is_urgent=False):
    pass

send_email("user@example.com", "안내", "내용", is_urgent=True)

# ❌ 불명확한 이름
def send_email(to, subject, body, retry=True, urgent=False):
    pass

💡 Tip 12: None과 빈 값 구분하기

None을 기본값으로 사용하면 “값이 전달되지 않음”과 “빈 값이 전달됨”을 구분할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
def search(query, filters=None, limit=10):
    """검색 함수"""
    if filters is None:
        filters = []  # 기본: 빈 필터

    print(f"검색어: {query}, 필터: {filters}, 제한: {limit}")

search("Python")              # 필터: []
search("Python", filters=[])  # 필터: []
search("Python", filters=["recent"])  # 필터: ['recent']

💡 Tip 13: 문서화에 기본값 명시하기

독스트링에 매개변수 기본값을 명시하면 사용자가 이해하기 쉽습니다.

1
2
3
4
5
6
7
8
9
10
11
def create_user(username, email, role="user", is_active=True):
    """
    사용자를 생성합니다.

    Args:
        username (str): 사용자명 (필수)
        email (str): 이메일 (필수)
        role (str): 권한 역할 (기본값: "user")
        is_active (bool): 활성화 여부 (기본값: True)
    """
    pass

💡 Tip 14: 환경별 설정 분리하기

개발/테스트/운영 환경별로 다른 기본값을 제공할 수 있습니다.

1
2
3
4
5
6
7
8
9
def connect_db(host="localhost", port=5432, pool_size=5, debug=False):
    """데이터베이스 연결"""
    pass

# 개발 환경
connect_db(debug=True)

# 운영 환경
connect_db(host="prod-db.example.com", pool_size=20)

💡 Tip 15: 너무 많은 매개변수는 딕셔너리로 묶기

매개변수가 5개 이상이면 관련된 것끼리 딕셔너리로 묶는 것을 고려하세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ❌ 너무 많은 매개변수
def create_server(host, port, workers, timeout, max_conn, debug, ssl):
    pass

# ✅ 관련 설정을 딕셔너리로
def create_server(host, port, config=None):
    """
    서버 생성

    config 예시: {
        'workers': 4,
        'timeout': 30,
        'max_connections': 100,
        'debug': False,
        'use_ssl': True
    }
    """
    if config is None:
        config = {'workers': 4, 'timeout': 30}
    pass

🧪 연습 문제

문제 1: 프로필 카드 생성기

다음 요구사항을 만족하는 create_profile_card 함수를 작성하세요.

  • 필수: name
  • 선택: title (기본값: “사원”), department (기본값: “일반”), email (기본값: None)
  • 이메일이 있으면 함께 출력, 없으면 생략
💡 힌트

기본 매개변수를 사용하고, 조건문으로 이메일 유무를 확인하세요.

1
2
3
4
5
def create_profile_card(name, title="사원", ...):
    if email:
        # 이메일 포함 출력
    else:
        # 이메일 없이 출력
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def create_profile_card(name, title="사원", department="일반", email=None):
    """프로필 카드를 생성하는 함수"""
    print("=" * 40)
    print(f"이름: {name}")
    print(f"직급: {title}")
    print(f"부서: {department}")
    if email:
        print(f"이메일: {email}")
    print("=" * 40)

# 테스트
create_profile_card("홍길동")
create_profile_card("김철수", "대리", "개발팀")
create_profile_card("이영희", "과장", "마케팅팀", "lee@example.com")
create_profile_card("박민수", department="인사팀", email="park@example.com")

출력:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
========================================
이름: 홍길동
직급: 사원
부서: 일반
========================================
========================================
이름: 김철수
직급: 대리
부서: 개발팀
========================================
========================================
이름: 이영희
직급: 과장
부서: 마케팅팀
이메일: lee@example.com
========================================
========================================
이름: 박민수
직급: 사원
부서: 인사팀
이메일: park@example.com
========================================

문제 2: 할인 가격 계산기

다음 요구사항을 만족하는 calculate_price 함수를 작성하세요.

  • 필수: price (원가)
  • 선택: discount (할인율 %, 기본값: 0), tax (세금 %, 기본값: 10), delivery_fee (배송비, 기본값: 3000)
  • 계산 순서: 할인 적용 → 세금 추가 → 배송비 추가
  • 소수점 이하 반올림
💡 힌트

순서대로 계산하세요:

  1. 할인 가격 = price * (1 - discount/100)
  2. 세금 포함 = 할인 가격 * (1 + tax/100)
  3. 최종 가격 = 세금 포함 + 배송비

round() 함수를 사용하세요.

✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def calculate_price(price, discount=0, tax=10, delivery_fee=3000):
    """최종 가격을 계산하는 함수"""
    # 할인 적용
    discounted = price * (1 - discount / 100)

    # 세금 추가
    with_tax = discounted * (1 + tax / 100)

    # 배송비 추가
    final_price = with_tax + delivery_fee

    # 결과 출력
    print(f"원가: {price:,}")
    if discount > 0:
        print(f"할인({discount}%): -{price - discounted:,.0f}")
    print(f"세금({tax}%): +{with_tax - discounted:,.0f}")
    print(f"배송비: +{delivery_fee:,}")
    print(f"최종 가격: {round(final_price):,}")
    print()

    return round(final_price)

# 테스트
calculate_price(50000)
calculate_price(50000, discount=20)
calculate_price(50000, discount=20, tax=0)
calculate_price(50000, discount=15, tax=10, delivery_fee=0)

출력:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
원가: 50,000원
세금(10%): +5,000원
배송비: +3,000원
최종 가격: 58,000원

원가: 50,000원
할인(20%): -10,000원
세금(10%): +4,000원
배송비: +3,000원
최종 가격: 47,000원

원가: 50,000원
할인(20%): -10,000원
세금(0%): +0원
배송비: +3,000원
최종 가격: 43,000원

원가: 50,000원
할인(15%): -7,500원
세금(10%): +4,250원
배송비: +0원
최종 가격: 46,750원

문제 3: 로그 메시지 포매터

다음 요구사항을 만족하는 log 함수를 작성하세요.

  • 필수: message (로그 메시지)
  • 선택: level (로그 레벨, 기본값: “INFO”), timestamp (시간 표시 여부, 기본값: True), prefix (접두사, 기본값: “”)
  • 레벨에 따라 다른 형식으로 출력
  • timestamp가 True면 현재 시간 포함
💡 힌트

datetime 모듈을 사용하세요:

1
2
3
from datetime import datetime

now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

레벨별 이모지:

  • DEBUG: 🐛
  • INFO: ℹ️
  • WARNING: ⚠️
  • ERROR: ❌
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from datetime import datetime

def log(message, level="INFO", timestamp=True, prefix=""):
    """로그 메시지를 출력하는 함수"""
    # 레벨별 이모지
    level_emoji = {
        "DEBUG": "🐛",
        "INFO": "ℹ️",
        "WARNING": "⚠️",
        "ERROR": ""
    }

    # 로그 구성
    parts = []

    # 시간 추가
    if timestamp:
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        parts.append(f"[{now}]")

    # 레벨 추가
    emoji = level_emoji.get(level, "📝")
    parts.append(f"{emoji} [{level}]")

    # 접두사 추가
    if prefix:
        parts.append(f"[{prefix}]")

    # 메시지 추가
    parts.append(message)

    # 출력
    print(" ".join(parts))

# 테스트
log("프로그램 시작")
log("데이터베이스 연결 성공", level="DEBUG")
log("디스크 공간 부족", level="WARNING")
log("파일을 찾을 수 없음", level="ERROR", prefix="FileSystem")
log("사용자 로그인", timestamp=False, prefix="Auth")

출력:

1
2
3
4
5
[2024-03-26 10:30:45] ℹ️ [INFO] 프로그램 시작
[2024-03-26 10:30:45] 🐛 [DEBUG] 데이터베이스 연결 성공
[2024-03-26 10:30:45] ⚠️ [WARNING] 디스크 공간 부족
[2024-03-26 10:30:45] ❌ [ERROR] [FileSystem] 파일을 찾을 수 없음
ℹ️ [INFO] [Auth] 사용자 로그인

💬 자주 묻는 질문 (FAQ)

Q1. 기본 매개변수와 키워드 인자의 차이는 무엇인가요?

A: 기본 매개변수는 함수를 정의할 때 매개변수에 기본값을 설정하는 것이고, 키워드 인자는 함수를 호출할 때 매개변수 이름을 명시하여 값을 전달하는 방식입니다.

1
2
3
4
5
6
# 기본 매개변수 (함수 정의)
def greet(name="손님"):
    print(f"안녕하세요, {name}님!")

# 키워드 인자 (함수 호출)
greet(name="철수")

Q2. 왜 리스트나 딕셔너리를 기본값으로 사용하면 안 되나요?

A: 기본값은 함수 정의 시 단 한 번만 생성되어 모든 호출에서 같은 객체를 공유하기 때문입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ❌ 위험!
def add_item(item, items=[]):
    items.append(item)
    return items

list1 = add_item("사과")  # ['사과']
list2 = add_item("바나나")  # ['사과', '바나나'] ⚠️ 누적!
# list1과 list2는 같은 리스트 객체를 가리킴!

# ✅ 안전
def add_item(item, items=None):
    if items is None:
        items = []  # 매번 새로운 리스트 생성
    items.append(item)
    return items

Q3. 위치 인자와 키워드 인자를 섞어 쓸 수 있나요?

A: 네, 가능합니다! 단, 위치 인자는 반드시 키워드 인자보다 앞에 와야 합니다.

1
2
3
4
5
6
7
8
def register(username, email, password, newsletter=False):
    pass

# ✅ 올바른 순서
register("john", "john@example.com", "1234", newsletter=True)

# ❌ 잘못된 순서
# register("john", email="john@example.com", "1234")  # SyntaxError!

Q4. 모든 매개변수를 키워드 인자로 전달할 수 있나요?

A: 네! 모든 매개변수를 키워드 인자로 전달하면 순서에 상관없이 명확하게 값을 전달할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
def book_flight(from_city, to_city, date, passengers=1):
    pass

# 순서 바꿔도 OK!
book_flight(
    date="2024-04-01",
    to_city="부산",
    from_city="서울",
    passengers=2
)

Q5. print()의 end=”\n”도 기본 매개변수인가요?

A: 정확합니다! print() 함수도 여러 기본 매개변수를 가지고 있습니다.

1
2
3
4
5
6
7
# print 함수의 실제 정의 (간략화)
def print(*values, sep=' ', end='\n', file=sys.stdout):
    pass

# 그래서 이렇게 변경 가능
print("안녕", "세상", sep="-", end="!\n")
# 출력: 안녕-세상!

Q6. None을 기본값으로 쓰는 패턴이 왜 안전한가요?

A: None은 불변(immutable) 객체이고, 함수 내부에서 새로운 객체를 생성하기 때문에 안전합니다.

1
2
3
4
5
6
7
8
9
10
def process_data(data=None):
    if data is None:
        data = []  # 매번 새로운 리스트 생성

    data.append("item")
    return data

# 각 호출마다 독립적인 리스트
result1 = process_data()  # ['item']
result2 = process_data()  # ['item'] (새로운 리스트)

Q7. 매개변수가 너무 많으면 어떻게 해야 하나요?

A: 매개변수가 5개 이상이면 관련된 것끼리 딕셔너리로 묶거나, 클래스를 사용하는 것을 고려하세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 방법 1: 딕셔너리로 묶기
def create_server(host, port, config=None):
    if config is None:
        config = {'workers': 4, 'timeout': 30}
    pass

# 방법 2: 데이터 클래스 (고급)
from dataclasses import dataclass

@dataclass
class ServerConfig:
    workers: int = 4
    timeout: int = 30
    debug: bool = False

def create_server(host, port, config=ServerConfig()):
    pass

Q8. 함수를 호출할 때 매번 모든 매개변수 이름을 써야 하나요?

A: 아니요! 필요한 것만 키워드 인자로 지정하면 됩니다. 보통 3개 이상일 때 가독성을 위해 사용합니다.

1
2
3
4
5
6
7
8
def send_email(to, subject, body, cc=None, bcc=None, priority="normal"):
    pass

# 필수만 위치 인자로
send_email("user@example.com", "안내", "내용")

# 필요한 것만 키워드 인자로 추가
send_email("user@example.com", "긴급", "내용", priority="high")

📝 오늘 배운 내용 정리

  1. 기본 매개변수: 함수 정의 시 기본값 설정 (def func(param=기본값))
  2. 키워드 인자: 매개변수 이름을 명시해서 값 전달 (func(param=값))
  3. 혼합 사용: 위치 인자 먼저, 키워드 인자 나중
  4. 장점: 유연성, 가독성, 선택적 매개변수
  5. 주의: 가변 기본값은 None 사용, 순서 규칙 준수

🎯 실습 과제

과제: 블로그 포스트 생성기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def create_blog_post(title, content, author="익명",
                     category="일반", tags=None, published=True):
    """블로그 포스트를 생성하는 함수"""
    from datetime import datetime

    # 태그 처리
    if tags is None:
        tags = []

    # 포스트 정보
    post = {
        "제목": title,
        "내용": content,
        "작성자": author,
        "카테고리": category,
        "태그": tags,
        "발행 상태": "발행됨" if published else "임시저장",
        "작성일": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    }

    # 포스트 출력
    print("=" * 60)
    print(f"제목: {post['제목']}")
    print(f"작성자: {post['작성자']}")
    print(f"카테고리: {post['카테고리']}")
    if post['태그']:
        print(f"태그: {', '.join(post['태그'])}")
    print(f"상태: {post['발행 상태']}")
    print(f"작성일: {post['작성일']}")
    print("-" * 60)
    print(post['내용'])
    print("=" * 60)

    return post

# 다양한 방식으로 포스트 생성
post1 = create_blog_post(
    "Python 기초",
    "Python은 배우기 쉬운 프로그래밍 언어입니다."
)

post2 = create_blog_post(
    "함수 활용법",
    "함수를 사용하면 코드를 재사용할 수 있습니다.",
    author="홍길동",
    category="프로그래밍",
    tags=["Python", "함수", "기초"]
)

post3 = create_blog_post(
    "작업 중인 글",
    "아직 완성되지 않은 글입니다.",
    published=False
)

post4 = create_blog_post(
    content="짧은 메모입니다.",
    title="메모",
    tags=["메모"],
    category="일기"
)

🔗 관련 자료


📚 이전 학습

Day 25: 함수 매개변수와 반환값 ⭐⭐⭐

어제는 위치 인자, 키워드 인자, return으로 값을 반환하는 방법을 배웠습니다!


📚 다음 학습

Day 27: 가변 인자 (*args, **kwargs) ⭐⭐⭐

내일은 개수가 정해지지 않은 인자를 받는 가변 인자를 배웁니다!


“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀

Day 26/100 Phase 3: 제어문과 함수 #100DaysOfPython
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.