✅ 조건문 표현 스타일 기준 5가지
1. 현재 기준 변수는 좌측에 둔다
- 원칙: 현재 비교하고 있는 값(=기준)은 좌측에 배치
- 이유: 조건을 읽을 때 **"지금 값이 ~한가?"**를 자연스럽게 읽기 위함
예시:
# ❌ (덜 직관적)
if freq[stack[-1]] <= freq[current]:
# ✅ (더 직관적)
if freq[current] >= freq[stack[-1]]:
2. 숫자 상수(리터럴)는 우측에 둔다
- 사람 눈은 좌측에 변수, 우측에 상수를 배치했을 때 더 빠르게 해석 가능
예시:
# ❌
if 100 < score:
# ✅
if score > 100:
- 오른쪽의 상수가 기준처럼 보이는 것이 일반적 읽기 순서에 맞음
3. 의미 있는 순서대로 정렬하라 (작은 → 큰)
- 조건식은 왼쪽이 작고, 오른쪽이 큰 형태로 정렬되면 이해가 빠름
예시:
# ❌
if 100 >= x and x >= 0:
# ✅
if 0 <= x <= 100:
- 이렇게 삼단 비교(0 <= x <= 100)는 Python이 지원하므로 적극 활용하면 좋음
4. not 사용은 최소화하고, 긍정 조건을 우선으로
- 부정 조건은 뇌가 한 번 더 해석해야 해서 인지 부하가 있음
예시:
# ❌
if not is_valid:
# ✅
if is_invalid:
물론, 경우에 따라선 not이 더 자연스러운 조건일 수도 있음
핵심은 "읽는 사람이 한 번에 이해 가능한 방향"으로 쓰는 것
5. 조건은 최대한 간결하게 유지 (논리 추상화)
- 복잡한 조건은 미리 변수로 빼거나 함수화
예시:
# ❌
if user.age >= 18 and user.role == 'admin' and user.is_active:
# ✅
is_adult_admin = user.age >= 18 and user.role == 'admin'
if is_adult_admin and user.is_active:
✅ 실무에서 자주 쓰는 조건문 설계 패턴 6가지
1. Guard Clause 패턴 (방어절)
→ 불필요한 중첩을 없애고, 일찍 리턴한다
# ❌ 중첩된 if
def process(user):
if user is not None:
if user.is_active:
return user.name
return None
# ✅ guard clause
def process(user):
if user is None or not user.is_active:
return None
return user.name
조건이 만족되지 않으면 바로 빠지는 방식
→ 코드 흐름이 위에서 아래로 깔끔하게 읽힘
2. 삼항 연산자 for 단순 조건 분기
# ✅ 변수 할당에 조건 적용
discount = 0.2 if user.is_premium else 0.05
- 단순한 if-else를 한 줄로 표현할 때 좋음
- 다만, 복잡해질 경우 삼항연산자는 피해야 함
3. 명확한 불리언 체크
# ❌
if not len(items):
...
# ✅
if not items: # 파이썬스러운 방식
...
- len(items)보다 그냥 items를 사용하는 것이 더 명확하고 간결
- Python에서는 [], None, '', 0 등은 모두 False로 평가됨
4. 의미 있는 변수로 조건 추상화
# ❌
if user.age >= 18 and user.status == 'active':
...
# ✅
is_adult_active = user.age >= 18 and user.status == 'active'
if is_adult_active:
...
- 의미 있는 이름으로 조건을 추상화하면 읽는 사람이 맥락을 더 쉽게 이해할 수 있음
5. 딕셔너리를 조건 분기 처리에 활용
# ✅ 딕셔너리를 switch처럼 활용
def handle(status):
return {
'pending': process_pending,
'approved': process_approved,
'rejected': process_rejected
}.get(status, process_unknown)()
- if-elif-else가 길어질 경우 딕셔너리 매핑으로 처리하면 깔끔함
- 특히 함수 분기할 때 유용
6. 리스트/튜플 포함 여부로 간결하게
# ❌
if x == 'a' or x == 'b' or x == 'c':
...
# ✅
if x in ('a', 'b', 'c'):
...
- 값이 여러 개일 경우 in 연산자로 훨씬 읽기 쉬움
✅ 좋은 함수란?
짧고 명확하며, 하나의 역할만 수행하는 함수.
읽는 순간 "아, 이 함수는 이거 하나 하려고 만든 거구나"가 바로 이해돼야 한다.
🎯 좋은 함수 5가지 기준
1. 함수는 **하나의 책임(One Responsibility)**만 가져야 한다
- 하나의 함수는 하나의 "논리적 기능"만 해야 한다.
- 여러 개의 기능을 섞어놓으면 수정/확장할 때 터진다.
❌ 나쁜 예
def process_user(user):
validate_user(user)
save_user(user)
send_welcome_email(user)
- 검증 + 저장 + 이메일 발송 = 서로 다른 책임이 섞여 있음
✅ 좋은 예
def validate_user(user):
...
def save_user(user):
...
def send_welcome_email(user):
...
- 각각 독립적인 함수로 분리
2. 함수는 짧아야 한다
- 한눈에 전체를 이해할 수 있을 만큼 짧아야 한다.
- 실무 기준으로는 보통 20줄 이하, 가능하면 10줄 이하로 유지한다.
"스크롤을 내려야 이해할 수 있는 함수는 나쁜 함수다."
3. 함수 이름은 행동(동사) + **대상(명사)**로 짓는다
- 함수는 무엇을 하는지 분명히 보여야 한다.
예시
- calculate_total_price()
- fetch_user_profile()
- send_email_notification()
함수 이름만 보고 대충 무슨 일을 하는지 알 수 있어야 해.
4. 함수는 입력(Input)과 출력(Output)이 명확해야 한다
- 어떤 값을 넣으면, 어떤 값을 돌려주는지 분명해야 한다.
- 부수효과(Side Effect)는 최소화한다.
❌ 나쁜 예
def update_user_info(user):
global DB
DB.append(user)
- 함수가 외부 상태(DB)를 직접 수정 → 추적하기 어렵다.
✅ 좋은 예
def create_user(user_data):
return User(user_data)
- 입력받아서 객체를 리턴 → 예측 가능
5. 함수 내부는 일관된 추상화 레벨을 유지한다
- 함수 안에는 비슷한 레벨의 작업만 들어가야 한다.
- 고수준 작업과 저수준 작업을 섞으면 읽기가 매우 힘들어진다.
❌ 나쁜 예
def create_user_account(user_data):
validate_email(user_data['email'])
if '@' not in user_data['email']:
raise ValueError('Invalid email')
send_email(user_data['email'])
- 고수준 작업(create_user_account) + 저수준 작업('@' not in email) 섞여 있음.
✅ 좋은 예
def create_user_account(user_data):
validate_user_data(user_data)
send_welcome_email(user_data['email'])
def validate_user_data(user_data):
if '@' not in user_data['email']:
raise ValueError('Invalid email')
- 추상화 레벨을 함수마다 맞춰서 정리
📌 요약 표
기준설명
하나의 책임 | 한 가지 기능만 수행 |
짧은 길이 | 한눈에 이해 가능 (20줄 이하) |
의미 있는 이름 | 동사+명사 조합으로 의도 표현 |
명확한 입력과 출력 | 외부 상태에 의존하지 않음 |
일관된 추상화 수준 유지 | 한 함수 안에는 같은 레벨의 작업만 |
🎯 마무리
"좋은 함수는 짧고, 읽기 쉽고, 하나의 목적만 가지고, 입력과 출력이 명확하며, 일정한 수준의 작업만 담고 있다."
🔥 "이 함수는 하나의 역할을 하는가?" "이름만 보고 알 수 있는가?" "입출력이 명확한가?" 를 항상 체크