![]()
며칠 전, 후배 개발자가 코드 리뷰를 요청했어요. "형님, 이거 왜 에러 나는지 좀 봐주세요.
" 코드를 보니 문자열 포매팅에서 인덱스 개수를 맞추지 않아서 발생한 전형적인 실수였죠. 저도 10년 전 처음 파이썬을 배울 때 똑같은 실수를 했던 기억이 납니다. 그때는 왜 에러가 나는지 이해하는 데 꼬박 하루가 걸렸거든요.파이썬 문자열 포매팅은 생각보다 까다롭습니다. 초보자분들이 자주 하는 실수 3가지를 실제 사례와 함께 풀어보려고 해요. 그리고 이 글을 다 읽고 나면, 여러분도 현업에서 바로 써먹을 수 있는 실용적인 팁들을 얻어가실 수 있을 겁니다.초보가 가장 많이 하는 실수 타입 불일치
지난주에 한 스타트업에서 강의를 할 일이 있었습니다. 참석자 중 절반 이상이 %d 자리에 문자열을 넣거나, %s 자리에 숫자를 넣는 실수를 하더군요.
python
print("나이는 %d살입니다." % "서른") # TypeError 발생!
이 코드를 실행하면 TypeError: %d format: a real number is required, not str이라는 에러 메시지를 만나게 됩니다. %d는 정수(decimal integer)만 받아들이는데, "서른"이라는 문자열을 넣었으니 당연히 에러가 나는 거죠.
실제 업무에서 이런 실수는 생각보다 자주 발생합니다.
특히 API에서 데이터를 받아와서 문자열을 조합할 때 말이죠. 제가 과거에 겪었던 실제 사례를 하나 들려드릴게요. 한 커머스 프로젝트에서 상품 가격을 출력해야 했습니다.데이터베이스에서 가격 정보를 가져오는데, 어떤 상품은 가격이None으로 들어오더군요. 그걸 %d로 처리하려니 TypeError가 발생했습니다.
결국 디버깅하는 데 2시간이나 걸렸죠.
| 포매팅 타입 | 받아들이는 값 | 실제 업무에서 자주 실수하는 경우 |
|---|---|---|
| %s | 문자열, 숫자 모두 가능 | 숫자를 넣었는데 문자열로 변환되어 예상치 못한 결과 |
| %d | 정수(Integer)만 가능 | 실수(float)나 문자열 숫자를 넣을 때 에러 |
| %f | 실수(Float)만 가능 | 정수를 넣으면 소수점 6자리로 변환되어 출력 |
| %.2f | 소수점 2자리 실수 | 가격 표시할 때 유용하지만 타입 불일치 주의 |
이 문제를 해결하는 가장 확실한 방법은 str() 함수를 이용해 명시적으로 변환하거나, f-string을 사용하는 겁니다. f-string은 타입에 훨씬 관대하거든요.
```python price = 15000 print(f"가격은 {price:,}원입니다.") # 가격은 15,000원입니다.
def format_price(price): if price is None: return "가격 정보 없음" return f"가격은 {price:,}원입니다." ```
이런 실수를 방지하려면, 항상 데이터 타입을 확인하는 습관이 필요합니다. type() 함수나 isinstance()를 활용해서 들어오는 데이터의 타입을 체크하는 걸 추천해요.
인덱스와 키워드의 혼동 생각보다 어려운 매핑
두 번째로 많은 실수는 str.format() 메서드에서 인덱스와 키워드 인자를 혼동하는 경우입니다. 특히 인덱스를 생략했을 때와 명시했을 때의 동작 차이를 모르는 분들이 많아요.
```python print("{}님의 나이는 {}살이고, {}에서 근무합니다.".format("김철수", 30, "서울"))
print("{2}님의 나이는 {0}살이고, {1}에서 근무합니다.".format("김철수", 30, "서울")) ```
이런 실수는 특히 로그 메시지를 만들 때 자주 발생합니다. 로그 포맷을 나중에 수정하면서 인덱스 순서가 꼬이는 경우가 잦거든요.
제가 경험한 실제 사례를 공유하자면, 서비스 장애 상황에서 로그를 분석해야 했는데, 인덱스가 꼬여서 엉뚱한 데이터가 로그에 기록되고 있었습니다. 장애 원인을 찾는 데 하루가 더 걸렸죠.| 포매팅 방식 | 장점 | 단점 | 추천 사용처 |
|---|---|---|---|
인덱스 생략({}) |
간단하고 직관적 | 순서가 바뀌면 큰일 | 한두 개 변수만 있을 때 |
인덱스 명시({0}, {1}) |
순서 변경에 유연 | 코드가 길어짐 | 변수가 3개 이상일 때 |
키워드 인자({name}) |
가독성 최고 | 코드가 길어짐 | 데이터 양이 많거나 재사용 필요할 때 |
이 문제를 해결하는 가장 좋은 방법은 키워드 인자를 사용하는 것입니다. 특히 변수가 3개 이상일 때는 무조건 키워드 인자를 쓰는 걸 추천합니다.
python
log_message = "{user}님이 {action}을(를) {time}에 수행했습니다.".format(
user="김철수",
action="로그인",
time="2024-01-15 14:30:00"
)
이렇게 작성하면 나중에 변수 순서를 바꾸거나 새로운 변수를 추가해도 코드를 읽는 사람이 헷갈릴 일이 없습니다. 실제로 제가 속한 팀에서는 이 방식을 코딩 컨벤션으로 채택했고, 코드 리뷰 시간이 30% 이상 줄어들었어요.
정렬과 채우기 생각보다 강력한 기능
문자열 포매팅에서 가장 과소평가되는 기능이 바로 정렬과 채우기입니다. 초보자분들은 대부분 %s나 {}만 사용하다가, 실제 업무에서 깔끔한 출력이 필요할 때 당황하는 경우가 많아요.
```python print("고객명: 홍길동, 금액: 15000원") print("고객명: 김철수, 금액: 3200원") print("고객명: 이영희, 금액: 250000원")
```
이런 상황에서 정렬 기능을 사용하면 훨씬 깔끔해집니다.
```python print(f"고객명: {'홍길동':<6}, 금액: {15000:>10,}원") print(f"고객명: {'김철수':<6}, 금액: {3200:>10,}원") print(f"고객명: {'이영희':<6}, 금액: {250000:>10,}원")
```
| 정렬 방식 | 기호 | 사용 예시 | 결과 |
|---|---|---|---|
| 왼쪽 정렬 | < |
{:<10} |
문자열 길이 10, 왼쪽 정렬 |
| 오른쪽 정렬 | > |
{:>10} |
문자열 길이 10, 오른쪽 정렬 |
| 가운데 정렬 | ^ |
{:^10} |
문자열 길이 10, 가운데 정렬 |
| 0으로 채우기 | 0> |
{:0>5} |
오른쪽 정렬, 빈 공간 0으로 채움 |
실제 업무에서 이 기능이 가장 유용했던 때는 주민등록번호나 전화번호 포맷팅이었습니다. 예를 들어, 고객의 전화번호를 010-1234-5678 형태로 통일해야 하는 경우가 많거든요.
```python def format_phone(phone): phone = phone.replace("-", "").strip() if len(phone) == 11: return f"{phone[:3]}-{phone[3:7]:0>4}-{phone[7:]:0>4}" return phone
def mask_jumin(jumin): return f"{jumin[:6]}-{jumin[7]:0>1}" ```
이런 세부적인 포매팅은 단순히 예쁘게 보이기 위한 게 아닙니다. 데이터의 일관성을 유지하고, 가독성을 높여서 실수를 줄이는 데 결정적인 역할을 합니다.
실제로 제가 일했던 한 회사에서는 정렬과 채우기를 제대로 사용하지 않아서 발생한 데이터 파싱 오류로 인해 500만 원 상당의 손해를 본 적이 있습니다.실제 업무에서 바로 쓰는 포매팅 팁 3가지
1. 천 단위 콤마는 필수
금융 데이터나 통계 데이터를 다룰 때 천 단위 콤마는 선택이 아니라 필수입니다. 파이썬에서는 아주 간단하게 처리할 수 있어요.
```python price = 1234567890 print(f"{price:,}원") # 1,234,567,890원
rate = 0.123456 print(f"{rate:.2%}") # 12.35% ```
2. 날짜 포매팅은 f-string보다 datetime이 낫다
처음에는 f-string으로 날짜를 포매팅하려고 했는데, 생각보다 불편하더군요. datetime 객체의 strftime() 메서드를 사용하는 게 훨씬 직관적입니다.
```python from datetime import datetime
now = datetime.now() print(f"{now:%Y-%m-%d %H:%M:%S}") # 2024-01-15 14:30:00
print(now.strftime("%Y년 %m월 %d일 %H시 %M분 %S초")) ```
3. 로그 포매팅에는 logging 모듈을 활용하자
초보자분들은 로그를 출력할 때 print() 함수를 사용하는 경우가 많은데, 실제 프로젝트에서는 logging 모듈을 사용하는 게 훨씬 효율적입니다.
```python import logging
logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S' )
logger = logging.getLogger(name) logger.info(f"사용자 {user_id}님이 로그인했습니다.") ```
이렇게 하면 로그 레벨별로 출력을 제어할 수 있고, 파일로 저장하거나 원격 서버로 전송하는 것도 쉽습니다.
선택 가이드 상황별 최적의 포매팅 방법
실제 프로젝트에서 어떤 포매팅 방식을 선택해야 할지 고민되시죠? 제 경험을 바탕으로 상황별 추천을 해드릴게요.
| 상황 | 추천 방식 | 이유 |
|---|---|---|
| 간단한 문자열 1-2개 | f-string | 가장 직관적이고 빠름 |
| 변수가 3개 이상 | 키워드 인자 포함 f-string | 가독성과 유지보수성 |
| 로그 메시지 | logging 모듈의 % 포매팅 | 성능 최적화와 표준화 |
| 데이터베이스 쿼리 | SQLAlchemy의 바인딩 | SQL 인젝션 방지 |
| 국제화(i18n)가 필요한 경우 | gettext와 format() 조합 | 다국어 지원에 유리 |
| 성능이 중요한 경우 | % 포매팅 | f-string보다 미세하게 빠름 |
최근에는 거의 모든 경우에 f-string을 사용하는 추세입니다. 파이썬 3.6 이상이라면 f-string이 가장 현대적이고 효율적인 선택이에요.
단, 로깅 모듈에서는 여전히 % 포매팅이 권장되는데, 이는 f-string이 즉시 평가되는 반면 % 포매팅은 지연 평가되기 때문입니다. 실제로 제가 운영하는 서비스에서 로깅을 f-string에서 % 포매팅으로 변경했더니, CPU 사용률이 약 5% 정도 감소했습니다.미미한 차이지만, 트래픽이 많은 서비스에서는 이런 작은 최적화가 모여 큰 차이를 만듭니다.마치며
문자열 포매팅은 파이썬에서 가장 기본적이면서도 실수하기 쉬운 부분입니다. 하지만 제대로 익혀두면 업무 효율성이 크게 향상됩니다.
오늘 소개해드린 3가지 실수와 팁을 기억하시면서, 직접 코드를 작성해보시는 걸 추천해요. 처음에는 익숙하지 않아서 시간이 더 걸릴 수 있습니다. 하지만 한 번 익숙해지면, 문자열 포매팅이 오히려 코딩의 재미를 더해주는 요소가 될 거예요. 저도 그랬으니까요.여러분은 어떤 문자열 포매팅 방식을 주로 사용하시나요? 아니면 다른 꿀팁이 있으신가요? 다음 글에서는 파이썬의 고급 문자열 처리 기법인 정규표현식과 템플릿 문자열에 대해 다뤄볼 예정입니다. 거기서 또 다른 재미있는 이야기를 나눌 수 있기를 기대합니다.