이번에는 함수 지향적 프로그래밍에 대해 알아보겠습니다.

프로그램의 규모가 커질수록 위에서 아래로 순차적으로 실행되는 절차적 코드만으로는 한계에 부딪히게 됩니다.
코드의 길이가 길어지고, 동일한 로직이 중복되며, 유지보수가 매우 어려워지기 때문입니다.
이러한 문제를 해결하기 위해 프로그램의 기능을 독립적인 단위로 나누는 함수 지향적 프로그래밍(Function-Oriented Programming)으로의 전환(리팩토링)이 필요합니다.
이번 글에서는 텍스트 기반 숫자 맞추기 게임의 절차적 코드를 함수 지향적 코드로 마이그레이션하는 과정과 그 과정에서 적용되는 핵심 개념들을 상세히 설명합니다.
리팩토링에 앞서 기존의 코드를 올려드리겠습니다.
Python - 절차적 프로그래밍1 : https://pabeba.tistory.com/269
Python - 절차적 프로그래밍2 : https://pabeba.tistory.com/270
기존 코드
# 절차적 코드 → 모두 run.py에 옮겨준다 → 구동되게 처리 → 함수 지향적 리팩토링 진행
# stage1
GAMR_TITLE_MAX_LEN = 28
GAME_TITLE_PROMPT = f"게임 제목을 입력해 주세요. (최대 {GAMR_TITLE_MAX_LEN}자이내(<={GAMR_TITLE_MAX_LEN}), 반드시 영문 or 숫자 만 사용 가능)\n"
GAME_TITLE_ERR_MSG1 = "입력 없음. 정확하게 입력하세요"
GAME_TITLE_ERR_MSG2 = "입력수 초과. 정확하게 입력하세요"
while True:
game_title = input(GAME_TITLE_PROMPT).strip()
if not game_title:
print(GAME_TITLE_ERR_MSG1)
elif len(game_title) > GAMR_TITLE_MAX_LEN:
print(GAME_TITLE_ERR_MSG2)
else:
break
# 최종 입력값 확인
print(f"game_title => {game_title}")
# stage 2
PLAYER_NAME_MAX_LEN = GAMR_TITLE_MAX_LEN
PLAYER_NAME_PROMPT = f"플레이어의 이름을 입력해 주세요. (최대 {PLAYER_NAME_MAX_LEN}자이내(<={PLAYER_NAME_MAX_LEN}), 반드시 영문 or 숫자 만 사용 가능)\n"
while True:
player_name = input(PLAYER_NAME_PROMPT).strip()
if not player_name:
print(GAME_TITLE_ERR_MSG1)
continue
if len(player_name) > PLAYER_NAME_MAX_LEN:
print(GAME_TITLE_ERR_MSG2)
continue
# 정상, 코드가 여기까지 진행되면 입력값 문제가 없다라는 의미 -> 반복문 탈출
break
# 최종 입력값 확인
print(f"player_name => {player_name}")
# stage3
print(f"""
------------------------------
-{game_title:^28}-
-{player_name:^28}-
- press any key -
------------------------------
""")
# stage4 ~ stage7
# stage 4
import random
is_game_play: bool = True # 현재 게임 진행중인가? 플레그역활 변수 (상황은 오직 2개)
while is_game_play: # B
ai_number = random.randint(1, 100)
print(f"임시 ai_number => {ai_number}")
player_try_count = 0 # 게임 숫자 입력 회수(시도회수), 정상입력만 1회로 간주
while True: # stage 6 관련
# stage 5 -> 플레이어 숫자 입력 받기
while True:
# 5-1. 플레이어의 입력값 획득
player_number = input(
"AI가 생성한 값 1 ~ 100 사이중 하나의 정수값을 맞추시오\n"
).strip()
# 5-2. 유효성 검사
# 5-2-1. 입력값이 비어 있다면 체킹
if not player_number:
print("정확하게 입력하세요")
continue
# 5-2-2. 값은 있다 => 정수변환 가능한가 체크
if (
not player_number.isnumeric()
): # 입력값이 정수 형태가 아님!! ex) 3.14, 'hi', '@'
print("정확하게 입력하세요. 정수만 입력됩니다.")
continue
# 5-2-3. 정수변환 (문자열 => 정수(수치형))
player_number = int(player_number)
# 5-2-4. 1 <= player_number <= 100 를 만족하는지 체크
if player_number < 1 or player_number > 100:
print("정확하게 입력하세요. 숫자는 1 에서 100 사이만 가능합니다.")
continue
# 5-2-5. 정상입력 완료 -> stage 5 반복문 탈출
break
# stage 6 -> ai 숫자, 플레이어 숫자 비교(체킹, 맞췄는지)
# 시도 회수 업데이트
player_try_count += 1
if player_number > ai_number:
print("입력값이 더 큽니다") # ai 50, 플레이어 70, stage 5 이동
elif player_number < ai_number:
print("입력값이 더 작습니다") # ai 50, 플레이어 30, stage 5 이동
pass
else: # player_number == ai_number, ai 50, 플레이어 50, 1%확률
print(f"정답입니다. { (10-player_try_count)*10 } ")
break # 5번라인, stage 6 관련 주석이 있는 반복문 탈출
pass
# stage 7 구현하시오 (리뷰시간 포함)
while True: # A
prmopt = "다시 게임을 하시겠습니까? (yes|y|no|n)등 대소문자구분없이 입력가능\n"
player_answer = input(prmopt).strip().lower() # 무조건 소문자 처리
if player_answer == "yes" or player_answer == "y": # 소문자만 비교
# stage4로 이동(신규 게임 진행)
break # A표식된 while문 탈출
elif player_answer == "no" or player_answer == "n":
# 게임 종료
print("bye bye ~!!")
# exit 1 : B 표식된 while문 탈출
is_game_play = (
False # 중첩 반복문을 수십개를 사용해도 특정 반복문을 탈출할수 있다
)
# exit 2 : A 표식된 while문 탈출
break
else:
print("정확하게 입력하세요.")
1. 리팩토링의 핵심 설계 원칙
기존의 하나의 파일(run.py)에 길게 나열되어 있던 코드를 역할과 목적에 따라 분리합니다.
- 독립적인 기능 단위(Stage) 구성: 입력, 검증, 출력, 게임 로직 등의 기능을 각각의 함수로 모듈화합니다.
- 데이터의 흐름 제어 (입력과 출력): 전역 변수 의존도를 낮추고, 함수의 매개변수(Parameter)와 반환값(Return)을 통해 데이터를 주고받도록 설계합니다.
- 엔트리 포인트(Entry Point) 설정: 프로그램의 시작점을 명확히 하여 코드의 가독성과 모듈 재사용성을 높입니다.
2. 전역 변수 설정 및 환경 변수 분리
함수 내부에서 공통으로 참조해야 하는 설정값이나 리소스 성격의 데이터는 전역 변수로 관리합니다.
import random
# 전역변수 (일종의 환경변수, 리소스를 로드한 값으로 간주)
GAMR_TITLE_MAX_LEN = 28
GAME_TITLE_ERR_MSG1 = '입력 없음. 정확하게 입력하세요'
GAME_TITLE_ERR_MSG2 = '입력수 초과. 정확하게 입력하세요'
# 현재 게임 진행 상태를 관리하는 플래그 변수
is_game_play : bool = True
기술적 포인트: is_game_play는 단순한 참조용이 아니라 게임 종료 시 상태가 변경되어야 하므로, 이후 상태를 변경하는 함수 내부에서는 global 키워드를 사용해 접근해야 합니다.
3. 스테이지별 함수 분리와 입출력 설계
각 코드를 목적에 맞는 함수로 분리합니다.
데이터 입력 및 검증 (stage1, stage2)
사용자로부터 입력을 받고 유효성을 검사하는 로직입니다.
정상적인 값이 입력될 때까지 무한 반복하며, 검증이 완료된 데이터는 return을 통해 외부로 반환합니다.
def stage1():
GAME_TITLE_PROMPT = f'게임 제목을 입력해 주세요. (최대 {GAMR_TITLE_MAX_LEN}자이내(<={GAMR_TITLE_MAX_LEN}), 반드시 영문 or 숫자 만 사용 가능)\n'
while True:
game_title = input( GAME_TITLE_PROMPT ).strip()
if not game_title:
print( GAME_TITLE_ERR_MSG1 )
elif len(game_title) > GAMR_TITLE_MAX_LEN:
print( GAME_TITLE_ERR_MSG2 )
else:
break
return game_title # 최종 검증된 값 반환
def stage2():
PLAYER_NAME_MAX_LEN = GAMR_TITLE_MAX_LEN
PLAYER_NAME_PROMPT = f'플레이어의 이름을 입력해 주세요. (최대 {PLAYER_NAME_MAX_LEN}자이내(<={PLAYER_NAME_MAX_LEN}), 반드시 영문 or 숫자 만 사용 가능)\n'
while True:
player_name = input( PLAYER_NAME_PROMPT ).strip()
if not player_name:
print( GAME_TITLE_ERR_MSG1 )
continue
if len(player_name) > PLAYER_NAME_MAX_LEN:
print( GAME_TITLE_ERR_MSG2 )
continue
break
return player_name
UI 출력 (stage3)
출력 전용 함수는 외부 데이터를 매개변수로 받아 화면에 렌더링하는 역할만 수행합니다.
이렇게 분리하면 출력 포맷이 변경될 때 stage3 내부만 수정하면 됩니다.
def stage3(game_title, player_name):
print(f'''
------------------------------
-{game_title:^28}-
-{player_name:^28}-
- press any key -
------------------------------
''')
게임 코어 로직 (stage4, stage5, stage6)
AI의 난수 생성(stage4), 플레이어의 입력 및 검증(stage5), 그리고 이 두 값을 비교하는 메인 게임 루프(stage6)로 분리합니다. 특히 stage6에서 stage5를 내부적으로 호출하여 로직을 구성합니다.
def stage4():
ai_number = random.randint(1, 100)
# print(f'임시 ai_number => {ai_number}') # 디버깅용
return ai_number
def stage5():
# 플레이어 숫자 입력 및 유효성 검사 분리
while True:
player_number = input('AI가 생성한 값 1 ~ 100 사이중 하나의 정수값을 맞추시오\n').strip()
if not player_number:
print('정확하게 입력하세요')
continue
if not player_number.isnumeric():
print('정확하게 입력하세요. 정수만 입력됩니다.')
continue
player_number = int( player_number )
if player_number < 1 or player_number > 100:
print('정확하게 입력하세요. 숫자는 1 에서 100 사이만 가능합니다.')
continue
break
return player_number
def stage6(ai_number):
player_try_count = -1
while True:
player_number = stage5() # stage5 함수 호출하여 값 획득
player_try_count += 1
if player_number > ai_number:
print('입력값이 더 큽니다')
elif player_number < ai_number:
print('입력값이 더 작습니다')
else:
print(f'정답입니다. 점수: { (10-player_try_count)*10 } ')
break
상태 제어 (stage7)
게임의 반복 여부를 결정합니다. 전역 변수 is_game_play를 조작하여 전체 게임 루프를 종료시킵니다.
def stage7():
global is_game_play # 전역 변수 수정 권한 획득
while True:
prmopt = '다시 게임을 하시겠습니까? (yes|y|no|n)등 대소문자구분없이 입력가능\n'
player_answer = input( prmopt ).strip().lower()
if player_answer == 'yes' or player_answer == 'y':
break
elif player_answer == 'no' or player_answer == 'n':
print('bye bye ~!!')
is_game_play = False # 메인 게임 루프 종료 플래그 설정
break
else:
print('정확하게 입력하세요.')
4. 메인 함수와 엔트리 포인트(Entry Point) 구성
함수 지향적 코드에서는 작성된 함수들을 조립하고 전체 흐름을 지휘하는 main() 함수가 필요합니다.
def main():
# 함수들의 반환값을 바로 매개변수로 전달하여 코드 간소화
stage3(stage1(), stage2())
while is_game_play:
ai_number = stage4()
stage6(ai_number)
stage7()
# 엔트리 포인트(진입로, 프로그램 시작점)
if __name__ == '__main__':
main()
기술적 포인트: if __name__ == '__main__': 구문은 파이썬의 표준 관습입니다. 이 파일이 직접 실행될 때만 main() 함수를 호출하고, 다른 파일에서 모듈로 임포트(import)될 때는 코드가 임의로 실행되는 것을 방지하는 중요한 역할을 합니다. AI 및 현업 환경에서도 필수적으로 적용되는 구조입니다.
'코딩 개발 > Python' 카테고리의 다른 글
| Python - 데이터 수집, 처리, 적재 (ETL) (feat. NAVER Developer 어플리케이션 사용 법, Google Colab에서 API 사용) (1) | 2026.07.23 |
|---|---|
| Python - 모듈 (from, import) (0) | 2026.07.22 |
| Python - 함수의 종류와 활용법 (0) | 2026.07.20 |
| Python - 함수 (Function) (0) | 2026.07.17 |
| Python - 절차적 프로그래밍2 (숫자 맞추기 게임 만들기 step4 ~ step7) (0) | 2026.07.16 |