코드 구조 설계하기

in #krsuccess3 days ago

작은 프로그램도 “구조”가 반이다: 6-2 코드 구조 설계하기

6-1에서 “어떤 프로그램을 만들지”를 정리했지?
이제부터는 내가 제일 중요하게 보는 단계가 나와. 바로 코드 구조 설계!

솔직히 말하면, 처음부터 완벽하게 짜는 건 거의 불가능이야. 나도 예전에 “일단 되게 해보자!” 모드로 막 작성했다가, 나중에 기능이 늘어나면서 파일이 하나도 안 읽히는 괴물이 되더라. 그때 깨달았지.
기능이 늘어날수록 구조는 선택이 아니라 생존이더라고.

Pexels


1) 목표: “유지보수하기 쉬운” 구조 만들기

여기서 말하는 유지보수는 거창한 게 아니야.

  • 기능 추가할 때 코드가 덜 깨지게
  • 수정할 때 어디를 고쳐야 할지 빨리 찾게
  • 나(미래의 나 포함)가 다시 봐도 이해되게

음… 사실은 미래의 나를 위해 지금 내가 도와주는 거지.
미래의 나는 아마 바쁘고, 나만큼 친절하지 않을 거니까.


2) 전체를 “덩어리”로 쪼개기 (기능 단위 분리)

내가 보통 이렇게 쪼개.

덩어리 1: 사용자 인터랙션(입력/출력)

  • 콘솔에서 입력 받기
  • 메시지 출력하기
  • 잘못 입력했을 때 안내하기

덩어리 2: 핵심 로직(계산/판단/처리)

  • 입력값을 가지고 실제로 뭔가 처리하기
  • 조건에 따라 결과 만들기

덩어리 3: 데이터(저장/관리)

  • 리스트/딕셔너리로 상태 담기
  • 파일이 들어가면 경로/포맷 정리하기

jothamsutharson


3) “흐름도” 먼저 그리면 덜 헤맨다

코드로 바로 들어가면 막히는 순간이 꼭 와.
그때 필요한 게 “아, 지금 흐름이 이렇게였지!” 같은 확신이야.

그래서 나는 설계할 때 보통 흐름을 먼저 잡아.

예시(텍스트 기반 메뉴 프로그램이라고 가정)

  • 메뉴 출력
  • 선택 입력
  • 선택에 따라 처리 로직 호출
  • 결과 출력
  • 계속 반복/종료

StartupStockPhotos


4) 함수 설계: “한 함수 = 한 가지 역할” 규칙

여기서 함수 설계는 그냥 감으로 하면 망해… 라기보단,
경험상 역할이 섞이기 시작하면 나중에 진짜 힘들어져.

그래서 내가 자주 쓰는 규칙은 이거야.

  • print_menu() : 메뉴를 보여주기만
  • get_choice() : 입력을 받아서 숫자/문자만 처리하기
  • run_action(choice, data) : 선택에 따라 실제 작업 수행
  • handle_result(result) : 결과 메시지를 예쁘게(혹은 명확하게) 출력
  • load_data() / save_data() : 파일 있으면 입출력만 담당

Peggy_Marco

아! 그리고 한 가지 더.
함수 이름은 “동사”로 짓는 게 편해. 예를 들면:

  • calculate_total()
  • find_item()
  • add_record()

솔직히 말하면, 내가 예전에 함수 이름을 막 지었다가(예: doStuff1, doStuff2)
나중에 “이게 뭐였지?” 싶어서 스스로를 욕했어… 좀 웃기지?


5) 데이터 구조 설계: “뭘 저장할지”부터 정하자

기능 설계를 하다 보면 자연스럽게 데이터가 필요해져.

예를 들어 어떤 프로그램이 “기록”을 다룬다면 보통 이런 질문이 생겨:

  • 항목은 어떤 형태로 저장할까?
    • 리스트? 딕셔너리?
  • 각 항목에 필요한 값은 뭐지?
    • 이름, 점수, 날짜 같은 것들?
  • 빠르게 찾아야 하나?
    • 이름으로 바로 찾으면 딕셔너리가 편할 수 있음

나름 팁을 주자면:

  • 순서가 중요하면 리스트
  • 키로 바로 찾으면 딕셔너리

이건 이전 시리즈에서 리스트/딕셔너리 다룬 내용(3-1, 3-2) 그대로 가져오면 돼.

PIX1861


6) 예외(오류) 처리 위치도 미리 정하기

“오류 수정과 개선하기”(6-4)에서 제대로 다룰 거지만,
여긴 어디서 오류를 잡을지만 간단히 설계하자.

예를 들면:

  • 입력 단계에서 숫자가 아니면 다시 입력하게 하기
  • 메뉴 선택 범위를 벗어나면 안내하기
  • 파일 로드 실패하면 기본값으로 시작하기

이걸 어디에 넣냐에 따라 구조가 완전히 달라져.

내가 자주 하는 패턴

  • 입력 함수가 “입력 오류”를 책임지고
  • 로직 함수는 “데이터가 정상일 때의 동작”에 집중하게 하기

이러면 나중에 테스트/수정이 훨씬 쉬워져.


7) 프로젝트 뼈대(템플릿)부터 만들기

자, 이제 진짜 “시작하는 느낌”을 주는 단계야.
나는 구현 전에 대충 이런 형태로 뼈대를 만들어.

  • 전체 실행 흐름 함수(예: main())
  • 메뉴/입력 관련 함수들
  • 로직 함수들
  • 데이터 로드/세이브 함수들

예시 뼈대(개념용):

def main():
    data = load_data()

    while True:
        choice = show_menu_and_get_choice()
        if choice == "0":
            save_data(data)
            break

        result = run_action(choice, data)
        print_result(result)

이 정도만 있어도, 다음 단계(6-3)에서 “기능별 구현”이 훨씬 쉬워져.
왜냐면 이미 “어떤 함수에 뭘 넣어야 하는지”가 정해졌거든.


8) 여기까지 하면 뭐가 좋아지냐?

솔직히 말하면, 지금 설계한다고 코드가 갑자기 완벽해지진 않아.
근데 실수할 확률이 줄어들어.

  • 나중에 기능이 늘어도 구조가 크게 안 무너지고
  • 어디를 수정해야 할지 감이 생기고
  • 디버깅(오류 찾기)이 덜 고통스럽고

“아… 왜 이렇게 엉켜있지?” 같은 상황을 미리 막는 느낌이라고 보면 돼.


정리: 6-2에서 한 것

  • 프로그램을 덩어리(입력/로직/데이터)로 나눴고
  • 함수는 “역할 하나”로 설계하고
  • 데이터는 저장/조회 방식 기준으로 결정하고
  • 오류 처리는 책임 위치를 정했어
  • 마지막으로 구현 전에 뼈대를 만들었지

다음 글(6-3)에서는 드디어 본격적으로 기능별 구현 단계로 들어갈 거야.
아마 “이 함수에 뭘 넣는지”가 코드로 확실히 보이면서, 프로그램이 실제로 살아나는 느낌이 올 거야.

그럼 6-3에서 만나자! 😄