02. 두 번째 수업: 내 안에 날 닮은 내가 또 있다 (자기 유사성)

“나뭇가지 하나를 잘라 손에 쥐여주면, 그것이 1미터짜리 거대한 나뭇가지인지, 1센티미터짜리 잔가지인지 사진만 보고는 절대 구별할 수 없다.” 이 소름 돋는 크기의 마술, 부분 속에 전체가 숨어있는 현상을 우리는 자기 유사성(Self-Similarity)이라 부릅니다. 이 단어는 프랙탈을 설명하는 가장 강력하고도 유일한 우주의 복제 법칙입니다.


학습 목표

  • 부분과 전체가 모양이 쌍둥이처럼 똑같은 형태(닮음)로 계속 축소/확대 복제되는 자기 유사성을 파악합니다.
  • 컴퓨터 프로그래밍 세계에서 무한 루프 코딩의 정점, 파이썬의 ‘재귀 함수(Recursion)’의 개념을 깨우칩니다.
  • 프랙탈을 코딩으로 시뮬레이션할 때, 왜 무한 복제 함정에 빠져 RecursionError 폭발이 일어나는지 방호 시스템(base case)의 필요성을 봅니다.

1. 러시아 인형 마트료시카(Matryoshka)

인형의 배를 반으로 가르면 똑같이 생긴 작은 인형이 안에 있고, 그걸 다시 가르면 더 작은 똑같은 인형이 들어있는 러시아의 전통 목각인형 마트료시카를 떠올려 봅시다. 자기 유사성(Self-Similarity)이란 기하학의 마트료시카입니다.

2D 웹툰: 마트료시카 인형을 열었더니 그 안에 똑같이 생긴 프랙탈 고사리 잎사귀가 무한히 들어있는 마법 같은 그래픽
  1. 거대한 고사리(Fern) 잎사귀를 하나 스케치북에 그립니다.
  2. 그 고사리 잎사귀 줄기에 매달린 첫 번째 작은 잎을 뜯어내 현미경에 올립니다.
  3. 황당하게도, 그 작은 잎 하나에는 ‘방금 내가 스케치북에 그렸던 가장 큰 고사리 전체의 모습’이 완벽한 비율(닮음비) 로 그대로 들어가 있습니다!
  4. 그 작은 잎 안에 달린 더 미세한 ‘잎맥’을 전자현미경으로 봅니다. 거기에도 여전히 똑같은 고사리가 숨 쉬고 있습니다.

이처럼 전체의 모양과 부분의 모양이 끝없이 닮아있는, 그래서 크기(Scale)라는 잣대가 완전히 붕괴되는 기하학적 성질이 바로 프랙탈의 알파요 오메가입니다.

2. Python 코딩 미러링: “재귀 함수 (Recursion)”

부분 속에 전체가 똑같이 들어있다면? 이를 파이썬 프로그래머에게 요구하면 프로그래머는 단 1초의 망설임도 없이 자기 참조(Recursion) 코드를 짜내려갈 것입니다. “내 함수 안에서, 나를 똑같이 다시 작동시켜라. 다만 크기(Scale)만 조금 줄여서 계속 내 안으로 들어가라!” 라는 마법의 주문입니다.

# 파이썬으로 가동하는 마트료시카/고사리 잎 생성기 (Recursion)

import time

def draw_fern_branch(branch_size, depth):
    """
    자기 자신(draw_fern_branch) 안에서 작은 가지로 위장하여
    다시 자기 자신을 끝없이 호출하는 무시무시한 재귀 함수(Recursion).
    """
    
    # 1. 종료 조건 (Base Case - 물리 엔진의 폭파 방지!)
    # 끝없이 안으로 타고 들어가면 파이썬 메모리가 폭발하므로, 가장 작은 잎맥에서 무한 루프를 멈춥니다!
    if depth == 0 or branch_size < 1:
        print(f"[{depth}단계] 더 이상 쪼갤 수 없는 초미세 잔가지(사이즈 {branch_size}) 도달. 생성 완료.")
        return
        
    print(f"[{depth}단계] 큰 나뭇가지(사이즈 {branch_size})를 렌더링 중입니다...")
    time.sleep(1) # 시뮬레이션 지연용
    
    # 2. 이 나뭇가지 안에 나랑 완벽히 똑같이 생겼지만, 
    # 크기만 절반(branch_size / 2)인 녀석이 2개(좌, 우)가 달려있습니다. 
    # 내가 '나'를 다시 호출합니다! (이것이 자기 유사성, 프랙탈의 코딩 본질입니다!)
    print(f"   -> 좌측 잔가지로 들어갑니다 (현상도: {depth-1})")
    draw_fern_branch(branch_size / 2, depth - 1)
    
    print(f"   -> 우측 잔가지로 들어갑니다 (현상도: {depth-1})")
    draw_fern_branch(branch_size / 2, depth - 1)

# 코어 엔진 가동 : 사이즈 100짜리 큰 나뭇가지를 그려라! 최대 분열 횟수(depth)는 3번.
print("🌿 프랙탈 렌더링 시작...")
draw_fern_branch(100, 3)
print("🌿 프랙탈 렌더링 종료!")

놀랍게도 프랙탈을 그리는 컴퓨터 코드는 수천 줄이 필요하지 않습니다. “이 함수를 크기만 줄여서 나 자신 안에서 다시 실행하라 (draw(size/2))” 라는 한 줄의 재귀 명령(Recursive Call)만 있으면, 가지는 수만 갈래로 무한히 분열하며 거친 숲맥과 혈관을 모두 그려냅니다!

단, 자연 생태계는 원자 단위에 도달하면 분열을 멈추듯, 우리의 컴퓨터 코드도 메모리 과부하(Stack Overflow, 블루 스크린)를 피하기 위해 무조건 if depth == 0: return 이라는 기저 조건(Base Case) 방파제를 설치해야만 우주 붕괴를 막을 수 있습니다.

학습 정리

  1. 자기 유사성 (Self-Similarity): 수학자 만델브로트가 선언한 거대한 프랙탈의 절대 법칙. 커다란 전체의 형상을 쪼개서 일부분을 살펴보아도 무한히 확대한 그 쪼가리가 전체의 모습과 똑같은 닮은꼴($\sim$)을 유지하는 것.
  2. 재귀(Recursion): 컴퓨터 공학에서 자기를 부르는 함수. 큰 상자 안에 내비게이션 경로 계산을 끝없이 호출할 수 있게 해주는 마법사로, 컴퓨터는 이 재귀 함수 덕분에 해상도의 끝을 알 수 없는 프랙탈 우주를 메모리 압박 없이 그려낼 수 있다.
  3. 파이썬에서 재귀 함수를 이용해 프랙탈을 탐색할 때는 수압 폭발(RecursionError)을 막아주는 안전밸브인 종료 조건 (Base Case)이 함수 맨 윗단에 반드시 세팅되어야 한다.
서브목차