Python / Pillow / lists

4주차 2교시: List와 중첩 반복 기반의 격자 구성

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

색상 조각과 행렬 격자로 List와 중첩 반복을 보여 주는 4주차 2교시 이미지
행별 색상·열별 크기 변화의 격자

List와 격자의 좌표 규칙

핵심 개념
List, Index, 중첩 반복
이전 학습
for, range(), 시각 매개변수
확인 방법
palette[row], rows * columns
다음 학습
리듬 그리드 실습

학습 목표

  1. List가 필요한 이유 설명 및 대괄호, 쉼표, Index 구분
  2. RGB 튜플 하나와 색상 List의 구조 구분
  3. Index가 0부터 시작하는 원리 이해 및 마지막 Index 계산
  4. List 항목을 직접 사용하는 반복과 Index를 사용하는 반복의 구분
  5. 행을 반복하는 바깥 반복문과 열을 반복하는 안쪽 반복문의 실행 순서 추적
  6. rows * columns로 전체 도형 수 계산, 마지막 좌표와 크기로 캔버스 범위 점검
  7. 색상 항목과 반복문을 직접 수정하고 실행 결과 검증

학습 내용

한 줄 반복의 복습

1교시에는 range(6)의 값 0부터 5까지를 반복 변수 i에 차례대로 저장했습니다. 이 값을 x좌표 계산에 사용해 같은 크기의 원 여섯 개를 한 줄로 배치했습니다.

아래는 1교시 코드에서 반복 부분만 발췌한 설명용 코드입니다. 새 노트북에서 실행할 전체 코드는 아래 「Colab 실습 준비」부터 순서대로 안내합니다.

개별 회상
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(...)
계산한 위치에 원 그리기

이 코드는 원의 위치만 변경하고 색은 한 가지로 유지합니다. 원마다 다른 색을 사용하려면 여러 색을 저장하고, 사용할 순서도 정해야 합니다. 이를 위해 여러 값을 하나의 이름 아래 순서대로 관리하는 List를 사용합니다.

개별 회상

i = 4에서의 원 위치

x = 100 + i * 110에 i = 4를 대입하고, 원의 왼쪽과 오른쪽 경계에 해당하는 x좌표를 기록합니다.

좌표 확인

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

List와 Index

List의 필요성

색이 하나라면 color라는 변수 하나로 충분합니다. 색이 네 개라면 color_1부터 color_4까지 변수를 따로 만들 수도 있습니다. 그러나 변수 이름에 번호를 붙여도 하나의 묶음이 되지는 않습니다. Python은 이 이름들만으로 네 변수가 한 팔레트에 속한다는 사실이나 사용할 순서를 알 수 없습니다.

색상 카드를 따로 보관하면서 카드마다 이름표를 붙이는 상황을 생각해 봅니다. 매번 이름표를 지정하는 대신, 하나의 보관함에 카드를 사용할 순서대로 넣으면 “카드를 앞에서부터 차례대로 사용한다”라는 공통 규칙을 적용할 수 있습니다. List는 이처럼 여러 값을 하나로 묶고 순서를 유지하는 역할을 합니다.

0부터 3까지 번호가 붙은 네 칸의 색상 보관함에서 2번 머스타드 색상 카드가 앞으로 나온 모습
색상 보관함과 List·Index의 대응

보관함 전체는 색상 List인 palette, 카드 한 장은 RGB 튜플 하나에 대응합니다. 칸에 붙은 0·1·2·3은 위치 번호인 Index입니다. 앞으로 나온 세 번째 카드의 색은 palette[2]로 선택합니다.

음악 재생 목록도 같은 원리로 이해할 수 있습니다. 곡마다 별도로 재생 명령을 내리는 대신, 여러 곡을 한 목록에 담고 정해진 순서로 재생합니다. 목록에 곡을 추가하거나 곡의 순서를 바꾸어도 “목록의 곡을 차례대로 재생한다”라는 규칙은 같습니다. 색상 List도 사용할 색과 순서를 정해 두면, 하나의 반복문으로 각 색에 같은 작업을 수행할 수 있습니다.

개별 변수와 List의 구조 비교

List는 여러 값을 순서대로 저장하는 Python 자료 구조입니다. List 전체는 대괄호 [ ]로 감싸고, 각 항목은 쉼표로 구분합니다. 다음 두 코드는 같은 색 네 개를 저장합니다. List를 사용한 코드에서는 네 색을 palette라는 하나의 이름으로 관리합니다.

개별 변수 네 개
개별 변수 네 개
color_1 = (37, 62, 78)
color_2 = (218, 100, 74)
color_3 = (222, 169, 73)
color_4 = (72, 126, 116)

각 색을 사용하려면 해당 변수 이름을 지정해야 합니다. 네 색의 묶음과 사용 순서는 따로 정의하지 않았습니다.

하나의 색상 List
하나의 색상 List
palette = [
    (37, 62, 78),
    (218, 100, 74),
    (222, 169, 73),
    (72, 126, 116),
]

palette에 적힌 순서가 색의 순서가 됩니다. len(palette)는 항목 수인 4를 반환합니다.

바깥쪽 [ ]는 네 색을 하나의 List로 묶습니다. 안쪽 ( )는 한 색을 구성하는 R, G, B 값 세 개를 묶습니다. 따라서 palette는 “숫자 열두 개”가 아니라 “RGB 색상 네 개가 순서대로 들어 있는 List”를 뜻합니다.

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

다음 두 줄은 위의 palette 선언에 이어지는 설명용 코드입니다. color에 각 항목을 순서대로 대입하고, print(color)를 실행하므로 RGB 튜플 네 개가 한 줄씩 출력됩니다.

개별 Index 점검
for color in palette:
    print(color)

이 반복문에서는 색을 사용할 때마다 변수 이름을 나열하거나 항목 수를 직접 적을 필요가 없습니다. List에 색을 추가해도 같은 반복문으로 모든 색을 출력할 수 있습니다. 이후 실습에서는 각 색을 출력하는 대신, 해당 색으로 원을 그립니다.

Index의 시작값과 항목 위치

List에서 각 항목의 위치를 나타내는 번호를 Index라고 합니다. Python의 Index는 0부터 시작합니다. 항목이 네 개라면 Index는 0, 1, 2, 3이고 마지막 Index는 3입니다. 3주차에서 너비가 4픽셀인 이미지의 x좌표가 0부터 3까지였던 것과 같은 관계입니다.

보관함의 번호는 카드의 색이 아니라 현재 위치를 가리킵니다. 따라서 Index 2가 머스타드색 자체를 뜻하는 것은 아닙니다. 세 번째 칸의 카드를 다른 색으로 교체하면 palette[2]로 선택되는 색도 달라집니다. 음악 재생 목록의 세 번째 곡이 바뀌어도 “세 번째”라는 위치는 그대로인 것과 같습니다.

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

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

One item
One item
palette[2]

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

개별 Index 점검

Index 기반 항목 선택

  1. 두 번째 색을 선택하는 코드를 작성합니다.
  2. 마지막 색의 Index를 기록합니다.
  3. palette[4]가 왜 유효하지 않은지 한 문장으로 설명합니다.
  4. 첫 번째 색과 세 번째 색의 순서를 서로 바꾸면, palette[2]로 어떤 색이 선택되는지 설명합니다.
Index 확인

두 번째 색은 palette[1], 마지막 색은 palette[3]으로 선택합니다. 항목이 네 개일 때 유효한 Index는 0부터 3까지입니다. palette[4]는 존재하지 않는 다섯 번째 항목에 접근하므로 오류가 발생합니다.

첫 번째 색과 세 번째 색의 순서를 바꾸면 palette[2]는 처음에 첫 번째였던 (37, 62, 78)을 선택합니다. Index는 색의 고유 번호가 아니라 List 안의 현재 위치입니다.

Colab 실습 준비

Colab에서 새 노트북을 생성합니다. 이 노트북에서 List 항목을 출력한 뒤, 여러 색의 원을 한 줄로 배치하고 격자로 확장하는 코드를 실행합니다. 코드 셀은 Shift+Enter 또는 셀 왼쪽 ▶ 버튼으로 실행합니다.

코드 실행

List 항목의 출력

  1. 노트북 제목을 week04-period2-자기이름으로 지정합니다.
  2. 아래 코드를 새 셀에 입력하고 실행합니다.
  3. 첫 색, 둘째 색, 항목 수 4가 출력되는지 확인합니다.
코드 실행
palette = [
    (37, 62, 78),
    (218, 100, 74),
    (222, 169, 73),
    (72, 126, 116),
]

print(palette[0])
print(palette[1])
print(len(palette))

(37, 62, 78)
(218, 100, 74)
4

List 항목의 반복

for는 range()의 숫자뿐 아니라 List의 항목도 순서대로 하나씩 가져올 수 있습니다. 아래 코드에서 반복 변수 color는 첫 반복에서 첫 번째 RGB 튜플을, 다음 반복에서 두 번째 RGB 튜플을 저장합니다.

같은 노트북의 두 번째 셀에 아래 전체 코드를 붙여 넣고 실행합니다. 이미지 생성에 필요한 코드를 포함하므로 이 셀만 실행해도 결과를 확인할 수 있습니다. Image.new()로 빈 이미지를 만들고, ImageDraw.Draw(image)로 draw를 준비한 뒤 원을 그립니다.

같은 노트북의 두 번째 셀 에 아래 전체 코드를 붙여 넣고 실행합니다. 이미지 생성에 필요한 코드를 포함하므로 이 셀만 실행해도 결과를 확인할 수 있습니다. Image.new()
from PIL import Image, ImageDraw

canvas_width = 800
canvas_height = 400
background_color = (238, 233, 222)
palette = [
    (37, 62, 78),
    (218, 100, 74),
    (222, 169, 73),
    (72, 126, 116),
]

image = Image.new("RGB", (canvas_width, canvas_height), background_color)
draw = ImageDraw.Draw(image)
x = 120

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

image

한 줄의 원 배열 확인

같은 크기의 원 네 개가 팔레트 순서대로 나타납니다. 1교시의 여섯 번 반복과 달리, 이번에는 palette의 항목 수가 반복 횟수를 정합니다. 다시 실행할 때도 이미지 생성부터 셀 전체를 실행해 새 이미지에 원을 그리도록 합니다.

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

색상 List 반복의 상태 변화
차례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 paletteList 안의 RGB 튜플색을 앞에서부터 하나씩 직접 사용할 때

반복 방식은 필요한 값에 따라 선택합니다. 위치 번호와 색을 함께 사용하려면 Index를 사용하고, 색상 값만 필요하면 List 항목을 직접 가져옵니다. 이번 격자에서는 행 번호로 y좌표와 색을 함께 정해야 하므로 row를 Index로 사용합니다.

도형 생성 전 재대입 결과

x = x + 130을 원을 그리기 전에 실행하면 첫 x좌표가 120이 아니라 250이 됩니다. Python 코드는 작성한 순서대로 실행되므로, 값을 다시 대입하는 위치에 따라 결과가 달라집니다. 현재 위치에 먼저 그린 뒤 다음 위치를 준비하려면 draw.ellipse() 다음에 재대입 문장을 작성합니다.

중첩 반복과 격자

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

이제 행과 열 번호로 위치를 계산합니다. 앞의 x = x + 130 대신 x = 120 + column * 140을 사용하면, 새 행에서 column이 0일 때 x도 다시 120이 됩니다. 여러 행을 배치할 수 있도록 캔버스 크기를 1000 × 820으로 확장하고 시작점과 간격을 설정합니다.

동일한 크기·색상의 격자 구성

세 번째 셀에 아래 전체 코드를 붙여 넣습니다. 코드를 실행해 첫 번째 색과 지름 40을 사용한 원이 네 행으로 배치되는지 확인합니다.

개별 추적
from PIL import Image, ImageDraw

canvas_width = 1000
canvas_height = 820
background_color = (238, 233, 222)
palette = [
    (37, 62, 78),
    (218, 100, 74),
    (222, 169, 73),
    (72, 126, 116),
]

image = Image.new("RGB", (canvas_width, canvas_height), background_color)
draw = ImageDraw.Draw(image)

rows = 4
columns = 6

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

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

image

격자 확인

같은 색과 크기의 원 24개가 4행 × 6열로 배치됩니다. 바깥 반복의 row가 0일 때 column에는 0부터 5까지의 값이 차례대로 저장됩니다. 원 여섯 개를 모두 그리면 row가 1로 변경되고, column에는 다시 0부터 값이 저장됩니다.

행별 색상 변경

세 번째 셀에서 color = palette[0] 한 줄을 아래처럼 바꿉니다. 앞의 들여쓰기 네 칸은 유지합니다. 이제 행 번호를 색상 List의 Index로 사용합니다.

개별 추적
    color = palette[row]

세 번째 셀 전체를 다시 실행합니다. 원의 위치·개수·크기가 유지되는지, 첫 번째 행에는 첫 번째 색이, 두 번째 행에는 두 번째 색이 적용되는지 확인합니다. 같은 행에서는 row 값이 같으므로 원 여섯 개에 같은 색이 적용됩니다.

열별 크기 변경

같은 셀에서 size = 40 한 줄을 아래처럼 바꿉니다. 앞의 들여쓰기 여덟 칸은 유지합니다. 1교시에 살펴본 크기 변화 규칙을 열 번호에 연결하는 단계입니다.

개별 추적
        size = 40 + column * 9

실행 전에 column이 0, 1, 5일 때 크기를 계산합니다. 각각 40, 49, 85입니다. 세 번째 셀 전체를 다시 실행하면 행별 색은 그대로이고, 각 행의 원이 오른쪽으로 갈수록 커집니다. 새 행의 첫 원은 column이 0이므로 지름이 다시 40이 됩니다.

행 번호 0부터 3, 열 번호 0부터 5가 새겨진 나무 상자에 행별 색과 열별 크기가 다른 원 24개가 놓이고, 황동 포인터가 둘째 행의 첫 칸을 가리키는 모습
행 완성과 다음 행의 시작

가로 여섯 칸에 원을 모두 그리면 다음 행의 0번 열에서 다시 시작합니다. 포인터는 row = 1, column = 0을 가리킵니다. 행 번호가 색을, 열 번호가 크기를 정하며, 상자와 포인터는 이 반복 순서를 보여 주는 비유입니다.

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
Column controls x
x = 120 + column * 140

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

Row controls y
Row controls y
y = 110 + row * 170

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

행과 열 번호가 붙은 4행 6열 인쇄 견본에서 세 번째 행의 다섯 번째 머스타드 원이 황동 모서리 표식으로 강조된 모습
행·열 번호와 원의 위치

표식은 row = 2, column = 4인 칸을 가리킵니다. row는 y좌표와 색을, column은 x좌표와 크기를 정합니다. 인쇄 견본은 이 관계를 설명하는 비유입니다. 행 번호와 열 번호를 아래 계산식에 대입해 실제 좌표와 크기를 확인합니다.

개별 추적

row 2, column 4의 원

  1. x = 120 + column * 140의 결과를 구합니다.
  2. y = 110 + row * 170의 결과를 구합니다.
  3. size = 40 + column * 9의 결과를 구합니다.
  4. 사용되는 색을 palette[0]처럼 Index를 지정하는 형태로 작성합니다.
격자 한 칸 계산 확인

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]는 열이 바뀌어도 같은 행에서는 색이 같으므로 바깥 반복 안, 안쪽 반복 밖에 둡니다. 이 문장을 안쪽 반복문에 작성해도 결과는 같지만, 열이 바뀔 때마다 같은 색을 다시 선택합니다. 행마다 달라지는 값은 행 반복문에서 계산하면 코드의 역할을 명확히 구분할 수 있습니다.

전체 Pillow 프로그램

지금까지 만든 격자에 설정 변수와 PNG 저장을 더해 2교시 공통 예제를 완성합니다. 세 번째 셀은 비교용으로 남겨 두고, 아래 전체 코드를 네 번째 셀에 입력합니다. 앞으로 설정값을 바꿔 비교할 때는 이 네 번째 셀 전체를 다시 실행합니다.

계산식의 설정 변수 정의

size = 40 + column * 9에서 40은 첫 열에 있는 원의 지름, 9는 열 번호가 1 증가할 때 지름에 더하는 값입니다. 각각 base_size와 size_step이라는 이름을 붙이면, 3교시에서 수정할 값을 쉽게 찾을 수 있습니다. 아래 표는 기존 계산식과 새 변수의 대응을 보여 줍니다.

기존 값과 설정 변수의 대응
앞에서 사용한 값설정 변수의미
rows = 4rows = len(palette)색의 개수로 행 수를 계산. 현재 네 색이므로 결과는 4
시작 좌표 120, 110start_x = 120, start_y = 110첫 원의 왼쪽 위 위치
간격 140, 170gap_x = 140, gap_y = 170이웃한 열·행의 시작점 사이 거리
기본 크기 40base_size = 40첫 열 원의 크기
크기 증가량 9size_step = 9다음 열로 이동할 때 증가하는 지름

설정 변수 아래의 계산식은 size = base_size + column * size_step이 됩니다. 숫자를 같은 값의 변수로 바꾸어도 실행 결과는 같습니다. len(palette)는 팔레트의 항목 수에 맞춰 행 수를 계산하지만 캔버스 크기까지 바꾸지는 않으므로, 이번 예제에서는 색 네 개를 유지하고, 다음 절에서 도형이 캔버스 범위에 포함되는지 확인합니다.

개별 예측
# 1. 도구 준비
from PIL import Image, ImageDraw

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

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

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

# 4. 격자 설정
rows = len(palette)
columns = 6
start_x = 120
start_y = 110
gap_x = 140
gap_y = 170
base_size = 40
size_step = 9

# 5. 행과 열 반복
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 = base_size + column * size_step

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

# 6. 저장과 미리보기
output_filename = "week04_rhythm_grid.png"
image.save(output_filename)
image

공통 예제 완성

세 번째 셀의 최종 그림과 같은 4행 6열 격자가 나타나고, week04_rhythm_grid.png가 저장됩니다. 저장된 파일은 Colab 왼쪽의 파일 목록에서 확인합니다. 이 파일은 2교시 연습 결과입니다. 3교시에는 제출 정보와 자동 검사가 포함된 별도 템플릿을 사용합니다.

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

격자 설정과 중첩 반복의 역할

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

IndentationError

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

IndexError

rows = len(palette)가 유지되는지 확인합니다. 네 색을 사용할 때 행 번호는 0부터 3까지입니다.

마지막 원의 캔버스 범위 확인

도형 명령의 실행 횟수와 모든 도형이 이미지 범위에 포함되는지는 별도로 확인해야 합니다. 이 예제에서는 열 번호가 증가할수록 x좌표와 원의 크기가 커지고, 행 번호가 증가할수록 y좌표가 커집니다. 따라서 마지막 행의 마지막 원이 오른쪽과 아래쪽으로 가장 멀리 배치됩니다. 마지막 Index는 row = rows - 1, column = columns - 1입니다.

현재 4행 6열 격자의 마지막 원
확인할 값계산결과
마지막 원의 크기40 + 5 * 985
오른쪽 경계120 + 5 * 140 + 85905 < 너비 1000
아래쪽 경계110 + 3 * 170 + 85705 < 높이 820

왼쪽과 위쪽 시작점도 0 이상이므로 현재 격자의 모든 원은 캔버스 범위에 포함됩니다. 색을 추가해 행이 늘어날 때는 이 계산을 다시 해야 합니다. len(palette)로 유효한 색을 선택하더라도 일부 도형이 캔버스 범위를 벗어날 수 있습니다.

개별 예측

다섯 번째 색 추가와 캔버스 범위

코드를 수정하기 전에 마지막 원의 아래쪽 경계 좌표를 계산합니다. 마지막 행 번호는 4, 마지막 원의 크기는 85입니다. 아래쪽 경계 110 + 4 * 170 + 85를 캔버스 높이 820과 비교합니다.

행 수 증가에 따른 캔버스 범위 확인

다섯 행의 아래쪽 경계는 875이므로 마지막 원의 일부가 잘립니다. 여섯 행이면 110 + 5 * 170 + 85 = 1045가 됩니다. 2교시 예제는 네 색으로 완성합니다. 3교시 템플릿은 색상 수에 따라 최대 여섯 행을 배치할 수 있도록 시작점·간격·기본 크기를 별도로 정해 두었으며, 뒤의 3교시 연결 표에서 설정값의 차이를 확인합니다.

같은 구조의 세 리듬

앞에서 실행한 세 단계를 인쇄 견본으로 비교합니다. 모두 4행 6열의 구조를 사용하며, 같은 색과 크기에서 시작해 행별 색, 열별 크기 규칙을 차례로 추가합니다. 각 패널을 보고 세 번째 셀에서 해당 규칙을 적용하기 위해 수정한 코드 한 줄을 확인합니다.

각각 4행 6열인 세 인쇄 견본: 1번은 같은 색과 크기, 2번은 행별 색과 같은 크기, 3번은 행별 색과 오른쪽으로 커지는 원
동일 구조의 단계별 규칙 추가

① 같은 색·같은 크기 → ② 행마다 색 변경 → ③ 열마다 크기 증가. 세 견본은 규칙의 차이를 비교하는 비유입니다. Colab에서도 한 줄씩 바꾼 뒤 셀 전체를 실행하며 변화의 원인을 확인합니다.

세 패널과 코드 매개변수의 대응 예시

①은 color = palette[0]과 size = 40을 사용합니다. ②는 색 선택만 color = palette[row]로 바꿉니다. ③은 여기에 size = 40 + column * 9를 적용합니다. 행·열 개수와 좌표 계산은 유지하며 색과 크기 규칙을 차례로 연결합니다.

핵심 정리

List는 여러 값을 순서대로 저장하고, 중첩 반복은 행과 열에 따른 좌표·색상·크기 규칙을 적용해 격자를 구성합니다.

코드 작성 실습

앞의 공통 예제를 완성한 뒤, 다음 네 실습을 순서대로 수행합니다. 각 실습은 결과 예측 → 코드 작성 → 실행 → 차이 설명 순서로 진행합니다. 정답은 직접 실행한 뒤 펼쳐 확인합니다.

앞서 작성한 네 개의 기준 셀은 유지하고, 노트북 아래에 연습 셀을 추가합니다. 전체 코드의 복제본을 사용하는 실습은 이미지 생성부터 셀 전체를 실행합니다. 빈칸 ____이 있는 코드는 빈칸을 모두 완성한 뒤 실행합니다.

실습 1. Index 기반 색상 수정

항목 선택과 재대입

palette[0] = (64, 120, 180)처럼 Index를 지정해 List의 한 항목을 변경할 수 있습니다. 이 예시는 첫 번째 색을 변경하는 코드입니다. 새 연습 셀에서 두 번째 색을 (64, 120, 180)으로 변경하고, 변경된 색과 항목 수를 출력합니다. 두 빈칸에 알맞은 Index를 작성합니다.

항목 선택과 재대입
palette = [
    (37, 62, 78),
    (218, 100, 74),
    (222, 169, 73),
    (72, 126, 116),
]

palette[____] = (64, 120, 180)
print(palette[____])
print(len(palette))

실습 1. 수정 코드와 출력 확인

두 번째 항목의 Index는 1입니다. List 선언 아래의 세 줄을 다음과 같이 작성합니다.

두 번째 항목의 Index는 1 입니다. List 선언 아래의 세 줄을 다음과 같이 작성합니다.
palette[1] = (64, 120, 180)
print(palette[1])
print(len(palette))

출력은 첫 줄에 (64, 120, 180), 다음 줄에 4입니다. 항목의 값을 변경해도 항목 수는 유지됩니다. 다른 항목이 바뀌었다면 Index를 확인합니다.

실습 2. Index 반복과 원 배열 재현

값 반복에서 Index 반복으로

한 줄의 원 배열 전체 코드를 새 연습 셀에 복제합니다. 복제한 코드에는 기준 팔레트가 포함되어 있으므로 실습 1에서 변경한 색은 사용되지 않습니다. x = 120부터 x = x + 130까지를 아래 코드로 교체하고 세 빈칸을 완성합니다.

목표는 기준 코드와 같은 네 색, 같은 위치, 같은 크기의 원 배열입니다. 반복 횟수는 팔레트 항목 수로 정하고, x좌표는 120에서 시작해 130씩 증가시킵니다.

값 반복에서 Index 반복으로
for i in range(len(____)):
    x = 120 + i * ____
    color = palette[____]
    draw.ellipse((x, 220, x + 80, 300), fill=color)

실습 2. 코드와 기준 배열 비교
코드 예시 · 본문의 실행 순서와 함께 확인
for i in range(len(palette)):
    x = 120 + i * 130
    color = palette[i]
    draw.ellipse((x, 220, x + 80, 300), fill=color)

i는 0, 1, 2, 3이고, x좌표는 120, 250, 380, 510입니다. Index i로 색도 선택하므로 기준 코드와 동일한 이미지가 생성됩니다. 새 코드의 마지막 x값은 510입니다. 기준 코드의 마지막 x값 640은 네 번째 원을 그린 뒤 계산한 다음 위치이므로 실제 원의 위치와 구분합니다.

실습 3. 행과 열의 실행 순서 출력

작은 격자 추적

새 연습 셀에서 2행 3열의 실행 순서를 출력합니다. 두 빈칸에 행 번호와 열 번호를 나타내는 변수를 순서대로 작성합니다. 실행 전에 첫 출력, 행이 바뀌는 출력, 마지막 출력을 예측합니다.

작은 격자 추적
for row in range(2):
    for column in range(3):
        print(____, ____)

  1. 완성한 코드를 실행하고 출력된 여섯 줄을 예측과 비교합니다.
  2. 행 수를 3, 열 수를 2로 수정해 다시 실행합니다.
  3. 출력 횟수가 같아도 값의 순서가 달라지는 이유를 설명합니다.
실습 3. 두 배열의 출력 순서 확인
코드 예시 · 본문의 실행 순서와 함께 확인
for row in range(2):
    for column in range(3):
        print(row, column)

같은 실행 횟수, 다른 행·열 순서
출력 차례2행 3열3행 2열
10 00 0
20 10 1
30 21 0
41 01 1
51 12 0
61 22 1

두 경우 모두 여섯 번 출력하지만, 안쪽 반복의 횟수가 다르므로 행이 바뀌는 시점도 다릅니다. 한 행의 열 반복을 마친 뒤 다음 행으로 이동합니다.

실습 4. 색상·크기의 변화 방향 변경

조건에 맞춰 직접 수정

전체 Pillow 프로그램을 새 연습 셀에 복제하고 다음 조건을 만족하도록 수정합니다. 정답을 펼치기 전에 색을 선택하는 위치와 크기 계산에 사용할 변수를 직접 결정합니다.

  1. 기준 팔레트의 네 색을 유지하고, columns를 4로 설정해 4행 4열의 원 16개를 그립니다.
  2. 왼쪽부터 열마다 다른 색을 적용합니다. 같은 열에 있는 원은 모두 같은 색이어야 합니다.
  3. 아래쪽으로 행이 바뀔 때마다 원의 지름을 9씩 늘립니다. 같은 행에서는 크기가 같아야 합니다.
  4. 시작점·배치 간격·기본 크기는 유지하고, output_filename은 "week04_column_colors.png"로 변경합니다.
  5. 전체 셀을 실행한 뒤 생성된 이미지와 저장한 PNG를 확인합니다.
실습 4. 수정 위치와 결과 확인

columns = 4로 변경하고, # 5. 행과 열 반복의 반복문 전체를 아래 코드로 교체합니다. 저장 부분에서는 지정한 파일명을 사용합니다.

columns = 4 로 변경하고, # 5. 행과 열 반복 의 반복문 전체를 아래 코드로 교체합니다. 저장 부분에서는 지정한 파일명을 사용합니다.
for row in range(rows):
    for column in range(columns):
        x = start_x + column * gap_x
        y = start_y + row * gap_y
        color = palette[column]
        size = base_size + row * size_step
        draw.ellipse((x, y, x + size, y + size), fill=color)

색은 column, 크기는 row에 연결합니다. 색이 열마다 바뀌므로 색 선택 문장도 안쪽 반복문에 작성합니다. 각 행의 지름은 40, 49, 58, 67입니다. 마지막 원의 왼쪽 위 좌표는 (540, 620), 오른쪽·아래쪽 경계는 (607, 687)로 캔버스 범위에 포함됩니다.

선택 실습. Index 오류 재현과 수정

오류 메시지로 원인 찾기

새 연습 셀에서 아래 코드를 실행합니다. 오류가 발생하기 전의 출력 줄 수와 오류가 발생한 순간의 i값을 기록한 뒤, 팔레트의 모든 항목을 정확히 한 번씩 출력하도록 반복 횟수를 수정합니다.

오류 메시지로 원인 찾기
palette = [
    (37, 62, 78),
    (218, 100, 74),
    (222, 169, 73),
    (72, 126, 116),
]

for i in range(6):
    print(palette[i])

선택 실습. 오류 지점과 수정 코드 확인

색상 네 개가 출력된 뒤 i가 4가 되면 palette[4]에서 IndexError가 발생합니다. List 선언은 유지하고 반복문을 다음과 같이 수정합니다.

색상 네 개가 출력된 뒤 i가 4가 되면 palette[4] 에서 IndexError 가 발생합니다. List 선언은 유지하고 반복문을 다음과 같이 수정합니다.
for i in range(len(palette)):
    print(palette[i])

이제 i는 0부터 3까지 사용되므로 모든 항목이 한 번씩 출력됩니다. 실습 4에서도 네 색으로 열별 색을 지정하려면 열 수가 팔레트 항목 수를 초과하지 않아야 합니다.

추가 실습과 3교시의 조건 구분

실습 4의 4행 4열과 열별 색·행별 크기는 반복 변수의 역할을 확인하는 2교시 연습 조건입니다. 3교시에는 전용 템플릿에서 6열, 행별 색, 열별 크기를 사용합니다. 아래 연결 표의 수정 범위를 따릅니다.

학습 내용 점검

List의 Index, 중첩 반복의 실행 횟수, 3교시에서 수정할 값을 함께 확인합니다.

핵심 개념 점검

  • List로 여러 색을 묶어서 관리하는 이유와 RGB 튜플과의 차이 설명
  • 첫 Index 0과 마지막 Index
  • 바깥 행·안쪽 열의 실행 순서
  • rows * columns로 전체 도형 수 계산
  • base_size와 size_step이 원의 크기에 미치는 영향
  • 마지막 원의 오른쪽·아래쪽 경계와 캔버스 범위 비교
  • Colab에서 List 출력, 한 줄의 원 배열, 격자 생성 및 PNG 저장 확인
  • List 항목 변경, Index 반복, 색·크기의 변화 방향 수정 결과 설명
추가 실습 완료 확인

색상 변경 결과, 기준과 동일한 원 배열, 두 격자의 출력 순서, week04_column_colors.png를 확인합니다. 각 연습 셀 아래에 수정한 코드와 결과가 달라진 이유를 한 문장으로 기록합니다. 실습 2처럼 이미지가 같다면 같은 결과를 만든 두 계산 방식의 차이를 설명합니다.

수업 후 개별 복습

각 매개변수가 격자의 어떤 속성을 결정하는지 확인합니다. 3교시 실습에서는 표의 3교시 수정 범위 열에 EDIT로 표시된 항목만 수정합니다. 수정 금지 영역에서 오류가 발생하면 원래 템플릿과 비교해 해당 코드를 복원합니다.

네 색의 교체형 카트리지와 두 조절 손잡이가 달린 탁상 인쇄기에서 행별 색과 열별 크기가 다른 격자가 출력되고, 내부 레일은 투명 덮개로 보호된 모습
동일 반복 구조의 설정값과 패턴 변화

카트리지는 palette, 두 손잡이는 columns와 size_step의 비유입니다.

행 수는 len(palette)로 정합니다. 덮개 안 레일은 3교시에서 유지하는 간격과 반복 구조를 나타냅니다.

매개변수와 예상 결과

매개변수의 역할과 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으로 수정

오류 메시지와 코드 구조

List와 중첩 반복의 대표 문제
오류 또는 현상뜻점검 순서
IndentationErrorPython이 바깥 반복과 안쪽 반복의 코드 블록을 구분하지 못함두 for와 도형 명령의 들여쓰기 깊이를 위에서부터 비교
IndexErrorList의 유효한 Index 범위를 벗어난 항목에 접근함3교시에서는 rows = len(palette)와 palette[row]를 원래 템플릿과 비교해 복구
NameError사용한 이름이 아직 정의되지 않았거나 철자가 다름설명용 코드나 교체할 코드 조각만 실행했는지 확인하고, palette와 draw 준비가 포함된 전체 셀을 실행
격자가 한 줄로 겹침y 계산이 row와 연결되지 않았을 가능성y = start_y + row * gap_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의 색상 수(5개)와 마지막 Index

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

3. row = 0에서의 column 값 순서

columns = 6이면 column에 0, 1, 2, 3, 4, 5가 차례대로 저장됩니다. 그 뒤에 row가 1로 바뀌고 column은 다시 0부터 시작합니다.

4. 열별 색상 선택과 Index

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

5. 도형 30개의 서로 다른 행 × 열 조합 두 가지

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

3교시 학습 내용

2교시에서는 공통 예제를 실행하고 PNG 저장까지 마쳤습니다. 이제 3교시 템플릿의 기준 팔레트에서 바꿀 두 색과 크기 변화량을 정합니다. 3교시는 목표 달성형 개인 실습이며, 아래 3교시 실습 완료 조건을 충족한 뒤 자동 검사를 통과하고 두 파일을 제출하면 실습을 마칩니다.

3교시 연결: 나만의 리듬 그리드

3교시는 제출 정보와 검사 셀이 포함된 실습 템플릿의 사본에서 시작합니다. 템플릿이 5열·크기 증가량 0인 이유는 한 열 추가와 크기 변화의 효과를 직접 비교하기 위해서입니다. 2교시에서 학습한 반복 구조를 바탕으로, 템플릿의 시작 설정을 목표 조건에 맞게 수정합니다.

2교시 공통 예제와 3교시 실습 템플릿의 대응
항목2교시 완성 상태3교시 시작 상태와 수정 사항
팔레트와 행 수연습용 네 색, rows = len(palette)템플릿 기준 네 색에서 두 색 이상 변경, 전체 4~6색. 행 수는 자동 계산
열 수columns = 6columns = 5에서 6으로 변경
크기 증가량size_step = 9size_step = 0에서 4~10 중 선택
기본 크기base_size = 40base_size = 44 제공값 유지
시작점·간격시작 (120, 110), 간격 (140, 170)시작 (70, 60), 간격 (150, 110) 제공값 유지
캔버스와 파일1000 × 820, week04_rhythm_grid.png1000 × 760, week04_학번_이름으로 PNG·노트북 제출

3교시 템플릿은 세로 간격을 110으로 설정해 최대 여섯 행을 배치할 수 있도록 구성했습니다. 가장 큰 설정인 6행·6열·size_step = 10에서도 마지막 원의 크기는 44 + 5 * 10 = 94, 오른쪽 경계는 70 + 5 * 150 + 94 = 914, 아래쪽 경계는 60 + 5 * 110 + 94 = 704입니다. 모든 원이 너비 1000, 높이 760인 캔버스 범위에 포함되므로 제공된 배치값은 유지합니다.

색상 변경 여부는 다음에 제시한 3교시 템플릿의 시작 팔레트를 기준으로 판단합니다. 변경할 색상 두 개의 Index와 새 RGB 값을 기록합니다. 실제 변경은 다음 교시의 템플릿 EDIT 셀에서 수행합니다.

개별 사전 설계
palette = [
    (36, 63, 80),
    (111, 143, 126),
    (211, 155, 42),
    (201, 111, 93),
]

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

3교시 수정 범위

앞의 세 리듬 비교처럼 색과 크기 규칙을 구분해 확인합니다. 실습에서는 그림 생성 코드의 EDIT 영역에서 palette의 RGB 색상 두 개 이상을 변경하고, columns는 6으로, size_step은 4~10 중 하나로 설정합니다. rows는 팔레트 길이로 자동 계산되며, gap_x, gap_y, 좌표 계산과 중첩 반복은 DO NOT EDIT 영역입니다. 학번·이름·리듬의 설계 의도·파일명은 별도의 제출 정보 셀에 입력합니다.

개별 사전 설계

고정 조건과 선택값

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

이번 교시의 학습 범위

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

3교시 실습 노트북 준비로 이동 → 템플릿의 기준 코드를 실행해 격자를 확인한 뒤, 이번 교시에서 정한 두 색과 크기 증가량을 적용합니다.

참고 자료

이 페이지의 목차