Lists / Nested loops / Grid

리스트에서 격자까지

색상 리스트와 두 겹의 반복을 연결해 한 줄의 패턴을 행과 열의 격자로 확장합니다.

색상 조각과 행렬 격자로 리스트와 중첩 반복을 보여 주는 4주차 2교시 이미지

여러 값을 묶고 두 방향의 반복을 설계하기

핵심 질문
한 줄의 반복을 어떻게 격자로 확장하는가?
문법
리스트, 인덱스, 중첩 for
운영
기본 50분 / 확장 60분, + 10 MIN EXTENSION

오늘의 학습 목표

  • 리스트를 여러 값을 순서대로 저장하는 자료 구조로 설명하고 대괄호, 쉼표, 인덱스를 구분할 수 있습니다.
  • RGB 튜플 하나와 여러 RGB 튜플을 담은 색상 리스트의 구조를 구분할 수 있습니다.
  • 리스트의 첫 번째 인덱스가 0임을 알고, 유효한 마지막 인덱스를 계산할 수 있습니다.
  • 리스트를 직접 순회하는 반복문과 인덱스로 값을 선택하는 반복문을 읽을 수 있습니다.
  • 바깥 반복은 행, 안쪽 반복은 열을 담당하는 중첩 반복문의 실행 순서를 추적할 수 있습니다.
  • rows * columns로 전체 도형 수를 계산하고 격자가 캔버스 안에 들어오는지 점검할 수 있습니다.

수업 운영: 기본 50분과 확장 60분

기본 50분에서는 리스트, 인덱스, 리스트 반복, 중첩 반복, 전체 코드와 3교시 설계 기준까지 진행합니다. 각 추적 문제는 먼저 개인 답을 적고 곧바로 기준 답안을 확인합니다. 진도가 빠르면 50-60분 확장에서 동일한 코드 구조로 만든 세 격자를 비교해 어떤 매개변수가 인상을 바꾸는지 분석합니다.

새 문법을 읽는 네 단계

  1. 자료의 모양을 봅니다. 대괄호 바깥과 RGB 튜플의 소괄호 안쪽을 나누어 봅니다.
  2. 주소를 적습니다. 리스트 항목 위에 0, 1, 2처럼 인덱스를 붙입니다.
  3. 반복의 방향을 분리합니다. 바깥 for는 한 행을 선택하고, 안쪽 for는 그 행의 열을 채웁니다.
  4. 첫 두 번만 완전히 추적합니다. row, column, x, y, color의 값을 표에 적은 뒤 전체 규칙을 이해합니다.

0-5분 · 한 줄의 반복을 다시 읽기

1교시에는 range(6)이 0부터 5까지의 값을 만들고, 반복 변수 i가 매번 새로운 값을 받았습니다. 그 값을 x좌표 계산에 연결해 같은 크기의 원 여섯 개를 한 줄로 배치했습니다.

for i in range(6):
    x = 100 + i * 110
    draw.ellipse((x, 220, x + 70, 290), fill=(42, 104, 124))
1 / Selecti = 0, 1, ... 5
현재 차례 선택
2 / Calculatex = 100 + i * 110
새 x좌표 계산
3 / Drawdraw.ellipse(...)
그 위치에 원 기록

이 코드는 위치는 바꾸지만 색은 하나로 고정합니다. 3주차처럼 색상마다 color_1, color_2, color_3라는 변수를 따로 만들 수도 있습니다. 그러나 색이 열 개라면 변수 이름도 열 개가 필요하고, “세 번째 색부터 차례대로 사용한다” 같은 순서 규칙을 코드로 표현하기 어렵습니다. 여러 값을 하나의 순서로 관리하기 위해 리스트를 사용합니다.

개별 회상 · 1분

i가 4일 때 원의 위치 찾기

x = 100 + i * 110i = 4를 대입하고, 원 경계의 왼쪽과 오른쪽 x좌표를 적습니다.

좌표 확인

x는 100 + 4 * 110 = 540입니다. 원의 오른쪽 x좌표는 x + 70이므로 610입니다. y좌표는 모든 반복에서 220과 290으로 같습니다.

5-16분 · 여러 색을 리스트와 인덱스로 다루기

리스트는 여러 값을 정해진 순서로 저장하는 Python 자료 구조입니다. 리스트 전체는 대괄호 [ ]로 감싸고, 각 항목은 쉼표로 구분합니다. 아래 리스트에는 3주차에 배운 RGB 튜플 네 개가 들어 있습니다.

palette = [
    (37, 62, 78),
    (218, 100, 74),
    (222, 169, 73),
    (72, 126, 116),
]

바깥쪽 [ ]는 네 색을 하나의 리스트로 묶습니다. 안쪽 ( )는 한 색을 만드는 R, G, B 값 세 개를 묶습니다. 따라서 palette는 “숫자 열두 개”가 아니라 “RGB 색상 네 개가 순서대로 들어 있는 리스트”라고 읽는 편이 정확합니다.

색상 리스트의 기호와 구조
코드 조각이름역할
palette변수 이름색상 리스트 전체를 다시 찾기 위한 이름
[ ... ]대괄호여러 항목을 순서가 있는 리스트로 묶음
(37, 62, 78)RGB 튜플한 색의 세 채널 값을 정해진 순서로 묶음
,쉼표리스트 항목과 튜플 안의 채널 값을 구분

첫 번째 항목의 인덱스는 0이다

리스트 안의 각 위치에는 인덱스라는 주소가 붙습니다. Python의 인덱스는 0부터 시작합니다. 항목이 네 개라면 주소는 0, 1, 2, 3이고 마지막 인덱스는 3입니다. 3주차에 가로 4픽셀의 x좌표가 0부터 3까지였던 것과 같은 개수와 주소의 관계입니다.

palette 리스트의 위치와 값
사람이 세는 순서Python 인덱스코드가져오는 RGB 값
첫 번째0palette[0](37, 62, 78)
두 번째1palette[1](218, 100, 74)
세 번째2palette[2](222, 169, 73)
네 번째3palette[3](72, 126, 116)
Whole list
palette

색상 네 개가 들어 있는 리스트 전체를 가리킵니다.

One item
palette[2]

인덱스 2에 있는 세 번째 RGB 튜플 하나를 가리킵니다.

개별 주소 찾기 · 2분

리스트의 주소를 코드로 적기

  1. 두 번째 색을 가져오는 코드를 적습니다.
  2. 마지막 색의 인덱스를 적습니다.
  3. palette[4]가 왜 유효하지 않은지 한 문장으로 설명합니다.
리스트 주소 확인

두 번째 색은 palette[1], 마지막 색은 palette[3]입니다. 네 개의 항목에는 0부터 3까지 주소가 붙으므로 palette[4]는 존재하지 않는 다섯 번째 위치를 요청합니다.

16-26분 · 리스트의 색을 하나씩 꺼내 반복하기

forrange()의 숫자뿐 아니라 리스트의 항목도 앞에서부터 하나씩 꺼낼 수 있습니다. 아래 코드에서 반복 변수 color는 첫 반복에 첫 번째 RGB 튜플, 다음 반복에 두 번째 RGB 튜플을 받습니다.

x = 120

for color in palette:
    draw.ellipse((x, 220, x + 80, 300), fill=color)
    x = x + 130

반복 전에 x = 120으로 시작 위치를 정합니다. 원을 하나 그린 뒤 x = x + 130을 실행해 다음 위치를 준비합니다. 2주차에 배운 재대입이 반복문 안에서 사용된 것입니다. 첫 번째 원을 그린 뒤 x는 250, 두 번째 원을 그린 뒤 380이 됩니다.

색상 리스트 반복의 상태 변화
차례color그릴 때의 x그린 뒤의 x
1palette[0]120250
2palette[1]250380
3palette[2]380510
4palette[3]510640

숫자를 반복할 때와 값을 반복할 때

두 가지 반복 방식의 선택 기준
반복 방식사용하는 값적합한 상황
for i in range(4)0, 1, 2, 3현재 순서 번호로 위치나 크기를 계산해야 할 때
for color in palette리스트 안의 RGB 튜플색을 앞에서부터 하나씩 직접 사용할 때

두 방식 가운데 하나가 항상 더 좋은 것은 아닙니다. 현재 위치 번호와 색을 함께 사용하려면 인덱스가 편리하고, 값 자체만 필요하면 리스트 항목을 직접 꺼내는 방식이 읽기 쉽습니다. 오늘의 격자에서는 행 번호로 y좌표와 색을 함께 정해야 하므로 row를 인덱스로 사용합니다.

x = x + 130을 원을 그리기 전에 실행하면?

첫 x좌표가 120이 아니라 250이 됩니다. Python은 위에서 아래로 실행하므로 업데이트의 위치가 결과를 바꿉니다. 현재 위치에 먼저 그린 뒤 다음 위치를 준비하려면 draw.ellipse() 다음에 재대입을 둡니다.

26-39분 · 두 겹의 반복으로 행과 열 만들기

한 줄을 여러 줄로 확장하려면 가로 방향과 세로 방향의 반복이 모두 필요합니다. 중첩 반복은 한 반복문 안에 다른 반복문이 들어 있는 구조입니다. 바깥 반복은 현재 행을 선택하고, 안쪽 반복은 그 행의 열을 왼쪽에서 오른쪽으로 채웁니다.

rows = 4
columns = 6

for row in range(rows):
    color = palette[row]

    for column in range(columns):
        x = 120 + column * 140
        y = 110 + row * 170
        size = 40 + column * 9
        draw.ellipse((x, y, x + size, y + size), fill=color)

바깥 반복의 row가 0일 때 안쪽의 column은 0부터 5까지 모두 실행됩니다. 첫 번째 행의 여섯 원을 다 그린 뒤에야 row가 1이 됩니다. 그러면 color = palette[1]로 색이 바뀌고, y좌표도 한 행 아래로 이동합니다.

Outer looprow = 0
첫 행과 첫 색 선택
Inner loopcolumn = 0 ... 5
첫 행의 여섯 칸 완성
Outer looprow = 1
다음 행과 다음 색 선택
격자 앞부분과 마지막 칸의 실행 추적
차례rowcolumnxy
100120110palette[0]
201260110palette[0]
302400110palette[0]
605820110palette[0]
710120280palette[1]
2435820620palette[3]

행 × 열로 전체 실행 횟수 계산하기

바깥 반복이 네 번 실행되고, 각 행마다 안쪽 반복이 여섯 번 실행됩니다. 따라서 도형 명령의 전체 실행 횟수는 rows * columns, 즉 4 * 6 = 24입니다. 안쪽 반복이 전체에서 한 번만 실행되는 것이 아니라 각 행마다 처음부터 다시 실행된다는 점이 핵심입니다.

Column controls x
x = 120 + column * 140

열 번호가 커질수록 도형이 오른쪽으로 이동합니다.

Row controls y
y = 110 + row * 170

행 번호가 커질수록 도형이 아래쪽으로 이동합니다.

네 행은 서로 다른 색을 사용하고 여섯 열은 오른쪽으로 갈수록 원의 크기가 커지는 격자 결과
row는 y좌표와 색을, column은 x좌표와 크기를 결정합니다. 두 반복 변수가 서로 다른 시각 속성을 담당합니다.
개별 추적 · 3분

row = 2, column = 4인 원 계산하기

  1. x = 120 + column * 140의 결과를 구합니다.
  2. y = 110 + row * 170의 결과를 구합니다.
  3. size = 40 + column * 9의 결과를 구합니다.
  4. 사용되는 색을 palette[주소] 형태로 적습니다.
격자 한 칸 계산 확인

x는 120 + 4 * 140 = 680, y는 110 + 2 * 170 = 450, 크기는 40 + 4 * 9 = 76입니다. 세 번째 행이므로 색은 palette[2]입니다.

들여쓰기 깊이가 실행 관계를 보여 준다

중첩 반복 코드의 들여쓰기 지도
깊이대표 코드실행 시점
0칸for row in range(rows):새 행을 선택할 때
4칸color = palette[row]각 행에서 한 번
4칸for column in range(columns):현재 행의 열 반복을 시작할 때
8칸x = ..., y = ..., draw.ellipse(...)행 안의 각 열마다 한 번

color = palette[row]는 열이 바뀌어도 같은 행에서는 색이 같으므로 바깥 반복 안, 안쪽 반복 밖에 둡니다. 이 줄을 안쪽 반복 안에 넣어도 같은 결과는 만들 수 있지만 같은 색을 열마다 다시 찾게 됩니다. 값이 변하는 가장 가까운 반복 수준에 코드를 두면 구조와 의도가 더 잘 드러납니다.

39-46분 · 전체 Pillow 프로그램과 오류 읽기

아래 코드는 3주차의 이미지 생성, 도형 그리기, 미리보기, 저장 흐름에 오늘 배운 리스트와 중첩 반복을 넣은 완전한 예입니다. 처음부터 한 글자씩 외우지 않고 여섯 묶음으로 나누어 읽습니다.

from PIL import Image, ImageDraw

# 1. 캔버스와 팔레트
canvas_width = 1000
canvas_height = 820
background_color = (238, 233, 222)

palette = [
    (37, 62, 78),
    (218, 100, 74),
    (222, 169, 73),
    (72, 126, 116),
]

# 2. 이미지와 그리기 도구
image = Image.new(
    "RGB",
    (canvas_width, canvas_height),
    background_color,
)
draw = ImageDraw.Draw(image)

# 3. 격자 설정
rows = 4
columns = 6
start_x = 120
start_y = 110
gap_x = 140
gap_y = 170

# 4. 행과 열 반복
for row in range(rows):
    color = palette[row]

    for column in range(columns):
        x = start_x + column * gap_x
        y = start_y + row * gap_y
        size = 40 + column * 9

        draw.ellipse(
            (x, y, x + size, y + size),
            fill=color,
        )

# 5. 결과 저장
output_filename = "week04_rhythm_grid.png"
image.save(output_filename)

# 6. 미리보기
image
전체 프로그램을 읽는 여섯 묶음
묶음하는 일3주차와의 관계
도구 준비ImageImageDraw를 가져옴동일
캔버스와 색크기, 배경, 네 색의 리스트를 정의개별 색상 변수가 리스트로 확장됨
이미지 생성빈 이미지와 그리기 도구를 준비동일
격자 설정행, 열, 시작점, 간격을 변수로 저장도형 하나의 좌표가 배치 규칙으로 확장됨
중첩 반복각 행과 열의 좌표·크기·색을 계산해 원을 그림도형 명령 하나가 24번 실행됨
저장과 미리보기PNG 파일을 저장하고 완성 상태를 출력 영역에 표시3주차의 저장 흐름을 그대로 사용

39-46분에는 새로 추가된 두 묶음만 자세히 읽는다

도구 준비, 이미지 생성, 저장은 3주차에 이미 다룬 흐름이므로 역할만 확인합니다. 강의의 초점은 격자 설정행·열 반복입니다. rows, columns, palette[row], x·y 계산을 앞의 추적표와 연결한 뒤 저장된 PNG 파일명을 확인합니다.

IndentationError

for와 도형 명령의 들여쓰기 깊이를 비교합니다. 바깥 반복 안은 네 칸, 안쪽 반복 안은 여덟 칸입니다.

IndexError

rows가 팔레트의 색상 수보다 크지 않은지 확인합니다. 네 색을 사용할 때 행 번호는 0부터 3까지입니다.

46-50분 · 3교시 연결: 나만의 리듬 그리드

3교시는 정해진 시간을 모두 채우는 방식이 아니라, 필수 목표를 완성하고 파일 검사를 통과하면 종료하는 목표 달성형 개인 실습으로 이어집니다. 자동 검사를 예측 가능하게 통과할 수 있도록 아래 조건을 3교시 고정 미션 계약으로 사용합니다. 2교시가 끝날 때에는 코드를 완성하지 않고, 바꿀 팔레트 두 색과 크기 변화량만 미리 결정합니다.

3교시 리듬 그리드의 고정 미션 계약
목표자동 검사 기준2교시에서 결정할 것
색상 리스트팔레트에 RGB 색상 4~6개, 서로 다른 색 최소 3개기준 팔레트에서 바꿀 두 색
행과 열rows = len(palette), columns = 6행 수는 팔레트의 색상 수와 같음
충분한 반복rows * columns가 20 이상기본 4행 × 6열 = 원 24개
중첩 반복과 좌표for rowfor column, x는 column, y는 row와 연결제공된 반복 코드는 수정하지 않음
색과 크기 변화색은 행마다 바뀌고 size_step은 4~10열마다 커질 픽셀 수
결과 파일PNG와 실행 가능한 Colab 노트북 저장week04_학번_이름 파일명
개별 사전 설계 · 2분

실습 시작 전에 고정 조건과 선택값 구분하기

  1. 고정값: columns = 6, 팔레트 색상 수 = rows, 따라서 rows * columns = ____
  2. 선택값: 기준 팔레트에서 바꿀 두 RGB 색과 size_step = ____ (4~10)
  3. 리듬 문장: “색은 ________ 방향으로 바뀌고, 크기는 ________ 방향으로 ________한다.”

이번 시간에 아직 다루지 않는 것

while 반복문, 조건문, 난수는 이번 격자에 넣지 않습니다. 리스트 컴프리헨션, enumerate(), 나머지 연산을 이용한 색 순환도 아직 사용하지 않습니다. 4주차에는 정해진 순서와 예측 가능한 결과를 정확히 이해하고, 조건과 우연성은 5주차에 추가합니다.

50-60분 · 같은 구조에서 다른 리듬 찾기

아래 세 이미지는 모두 행과 열의 중첩 반복으로 만들 수 있습니다. 코드의 뼈대는 같고 팔레트, 크기, 간격의 관계만 다릅니다. 화면을 보고 먼저 변화 규칙을 추론한 뒤, 어떤 매개변수를 바꾸어야 하는지 연결합니다.

균일한 원 격자, 행마다 색이 바뀌는 격자, 크기와 간격이 달라지는 격자의 세 패널 비교
같은 행·열 구조도 색의 선택 기준, 크기 계산, 간격에 따라 안정적 반복·색의 층·점진적 변화로 읽힐 수 있습니다.

3교시 편집 경계

세 패널의 차이는 개념 비교용입니다. 실제 미션에서는 palette의 RGB 두 색 이상, columns = 6, size_step 4~10만 EDIT 영역에서 수정합니다. rows는 팔레트 길이로 자동 계산되며, gap_x, gap_y, 좌표 계산과 중첩 반복은 DO NOT EDIT 영역입니다.

50-60분 확장 운영
시간활동남겨야 할 결과
50-53분세 패널에서 고정된 속성과 변한 속성을 각각 표시패널별 관찰 두 항목
53-56분색, 크기, 간격을 만드는 매개변수를 개념상 연결패널별 원인 한 가지
56-59분3교시에서 바꿀 팔레트 두 색과 size_step 결정RGB 두 묶음과 4~10 사이의 정수
59-60분columns = 6과 자동 행 수로 전체 도형 수 계산20개 이상임을 확인한 계산식
세 패널을 코드 매개변수로 설명하는 예시

균일한 패널은 크기와 간격을 상수로 유지합니다. 행별 색 패널은 palette[row]로 y방향의 층을 만듭니다. 변주 패널은 size를 row 또는 column과 연결하고, gap_xgap_y를 다르게 정해 밀도와 방향성을 만듭니다.

2교시 핵심 문장

리스트는 여러 선택을 순서로 묶고, 중첩 반복은 그 선택을 행과 열의 좌표 규칙으로 확장합니다.

수업 후 개별 복습

아래 내용은 기본 50분과 확장 10분의 현장 설명에 포함하지 않는 이론 복습입니다. 각 매개변수가 일반적인 격자에서 맡는 역할을 읽되, 3교시 미션에서는 표의 3교시 처리 열에 EDIT라고 표시된 값만 수정합니다. 오류가 발생하면 보호된 코드를 새로 고치지 않고 원래 템플릿과 비교해 복구합니다.

매개변수와 예상 결과 연결하기

이론에서 읽을 매개변수와 3교시 편집 범위
매개변수개념상 직접 변하는 것3교시 처리
palette행별 색상과 행 수EDIT · RGB 두 색 이상 수정, 전체 4~6색
rows세로 방향 도형 수DO NOT EDIT · len(palette)로 자동 계산
columns가로 방향 도형 수EDIT · 숫자를 6으로 고정
gap_x, gap_y가로·세로 간격DO NOT EDIT · 제공된 간격 유지
size 계산열에 따른 도형 크기계산식은 DO NOT EDIT · size_step만 4~10으로 수정

오류 메시지를 구조와 연결하기

리스트와 중첩 반복의 대표 문제
오류 또는 현상점검 순서
IndentationErrorPython이 바깥 반복과 안쪽 반복의 코드 블록을 구분하지 못함for와 도형 명령의 들여쓰기 깊이를 위에서부터 비교
IndexError리스트에 없는 주소의 항목을 요청함3교시에서는 rows = len(palette)palette[row]를 원래 템플릿과 비교해 복구
NameError사용한 이름이 아직 실행되지 않았거나 철자가 다름palette, row, column 정의 셀을 위에서부터 실행
격자가 한 줄로 겹침y 계산이 row와 연결되지 않았을 가능성y = start_y + row * gap_y 확인
오류 없이 일부가 안 보임도형 좌표가 캔버스 밖에 있어 잘리거나 보이지 않음마지막 x·y에 크기를 더해 캔버스 너비·높이와 비교

rows를 직접 바꾸는 것은 이론 예시일 뿐이다

일반 코드에서 팔레트에 색이 네 개일 때 rows = 5로 바꾸면 마지막 행에서 존재하지 않는 palette[4]를 요청합니다. 3교시 템플릿은 이 오류를 막기 위해 rows = len(palette)로 자동 계산하므로 해당 줄을 수정하지 않습니다.

1. rows = 3, columns = 8이면 원은 몇 개인가?

rows * columns = 3 * 8 = 24이므로 원은 24개입니다. 기존 4 × 6 격자와 전체 수는 같지만 행과 열의 비율이 달라 화면 구성이 더 가로로 길어집니다.

2. palette에 색이 다섯 개 있으면 마지막 인덱스는?

4입니다. 항목 수가 5이면 인덱스는 0, 1, 2, 3, 4입니다.

3. 바깥 반복 row = 0에서 column은 어디까지 실행되는가?

columns = 6이면 column은 0, 1, 2, 3, 4, 5를 모두 거칩니다. 그 뒤에 row가 1로 바뀌고 column은 다시 0부터 시작합니다.

4. 각 열의 색을 다르게 하려면 어떤 값을 사용해야 하는가?

색을 열 기준으로 선택하려면 palette[column]처럼 column을 인덱스로 사용할 수 있습니다. 단, 열이 여섯 개라면 팔레트에도 최소 여섯 색이 있어야 합니다.

5. 도형 30개를 만드는 서로 다른 행 × 열 조합 두 개는?

예를 들어 5행 × 6열, 3행 × 10열이 있습니다. 전체 수가 같아도 가로세로 비율과 시각적 인상은 달라집니다.

참고 자료