콘텐츠로 이동

4차시: 방향이 있는 숫자 - vector()로 2D·3D 움직임 제어하기

⏰ 50분 · vector 객체 · 벡터 연산 · mag/norm · 난이도 ●●○○○

학습목표: VPython의 vector(x, y, z) 객체를 생성하고, 벡터 연산(덧셈·뺄셈·스칼라곱)과 mag, norm 함수를 활용하여 3D 공간에서 객체의 움직임을 제어할 수 있다.

오늘의 질문: "내비게이션이 '북쪽으로 300m 이동'이라고 안내할 때, 숫자 하나(300)만으로 충분할까요? 🤔"


🧠 핵심 개념 1: vector 객체와 연산

숫자 하나가 아닌 방향+크기를 동시에 담는 그릇, vector(x, y, z)의 덧셈·뺄셈·스칼라곱을 익힙니다.

🧠 핵심 개념 2: mag와 norm

벡터에서 크기만 뽑는 mag방향만 뽑는 norm — 물리 시뮬레이션의 핵심 도구를 배웁니다.

💻 따라하기 실습

arrow 객체로 벡터를 눈으로 확인하고, 3차시 애니메이션을 벡터 방식으로 리팩토링합니다.

📝 평가

벡터 연산 퀴즈 + 대각선 이동 코딩 도전으로 오늘 배운 내용을 점검합니다.


⏱️ 수업 흐름

🔥 도입 — 숫자 하나로는 부족해! (5분)

3차시에서 pos.x만 바꿨던 한계를 떠올리며, "방향이 있는 숫자"의 필요성을 체감합니다.

📖 개념 탐구 — vector 객체와 벡터 연산 (12분)

vector(x, y, z) 생성, 덧셈·뺄셈·스칼라곱을 코드로 실습하며 개념을 잡습니다.

🛠️ 실습 1 — arrow로 벡터를 눈에 보이게! (10분)

arrow 객체를 사용하여 벡터를 3D 공간에 직접 그려보고, 연산 결과를 시각적으로 확인합니다.

📖 개념 탐구 — mag와 norm 함수 (10분)

벡터의 크기(mag)와 단위벡터(norm)를 분리하는 방법을 배우고, 왜 이것이 중요한지 이해합니다.

🚀 실습 2 — 3차시 코드를 벡터로 리팩토링 + 평가 (13분)

3차시 애니메이션을 벡터 방식으로 업그레이드하고, 형성 평가로 마무리합니다.


🔥 도입 — 숫자 하나로는 부족해! (5분)

3차시에서 우리가 했던 것

3차시에서 공을 오른쪽으로 움직일 때, 이렇게 코드를 작성했습니다:

ball.pos.x = ball.pos.x + 0.1

x만 바꿨으니 공은 오른쪽으로만 이동했죠. 그런데 만약 공을 대각선으로 움직이고 싶다면요?

ball.pos.x = ball.pos.x + 0.1
ball.pos.y = ball.pos.y + 0.05
ball.pos.z = ball.pos.z + 0.02

세 줄을 써야 합니다. 물체가 10개라면? 30줄이요! 😱

일상 속 "방향이 있는 숫자"

내비게이션을 떠올려 보세요:

안내 방식 충분한가요?
"300m 이동하세요" ❌ 어느 방향으로요?
"북쪽으로 300m" ⭕ 방향 + 거리 = 도착!

"북쪽으로 300m" 처럼, 방향과 크기(거리)를 한 덩어리로 담는 것이 바로 벡터(vector) 입니다. 오늘은 이 벡터를 VPython에서 어떻게 쓰는지 배워봅니다.

flowchart LR A["숫자 하나<br/>(스칼라)<br/>예: 300"] -->|"방향이 없다"| B["어디로?🤷"] C["벡터<br/>예: 북쪽 300m"] -->|"방향 + 크기"| D["정확한 이동! ✅"]

📖 핵심 개념 1: vector 객체와 벡터 연산 (12분)

🎯 비유로 시작: 택배 배달 주소

택배 기사님에게 "3층으로 올라가세요"라고만 하면 어떤 건물인지 모릅니다. "동쪽 2블록, 북쪽 3블록, 3층"이라고 해야 정확히 찾아갈 수 있죠.

VPython의 vector(x, y, z)가 바로 이 역할을 합니다:

성분 VPython 축 의미
x 오른쪽(+) / 왼쪽(−) 좌우 방향
y 위(+) / 아래(−) 상하 방향
z 화면 밖(+) / 안(−) 앞뒤(깊이) 방향

🔎 정의: vector란?

vector(x, y, z) 는 3D 공간에서 방향크기를 동시에 표현하는 VPython의 특별한 객체입니다. 파이썬 리스트 [x, y, z]와 달리, 수학 연산(+, −, ×)을 바로 할 수 있습니다.

v1: vector 만들어보기 (기본)

실행 환경: VPython 3.2 (Web VPython 또는 로컬 설치)

from vpython import *

# 벡터 만들기 — 오른쪽 2, 위로 3, 앞으로 0
v = vector(2, 3, 0)

# 벡터의 각 성분 확인
print(v)         # 전체 출력
print(v.x)       # x 성분만
print(v.y)       # y 성분만
print(v.z)       # z 성분만

예상 출력:

<2, 3, 0>
2
3
0

위 코드의 3번째 줄 v = vector(2, 3, 0)이 바로 벡터 객체 생성입니다. 숫자 세 개를 넣으면, 3D 공간에서의 방향과 크기를 하나의 변수에 담을 수 있습니다.

📊 리스트 vs vector — 뭐가 다를까?

"파이썬 리스트 [2, 3, 0]이랑 뭐가 다른데요?"라고 궁금하시죠? 핵심 차이를 볼게요:

from vpython import *

# 파이썬 리스트로 시도
list_a = [2, 3, 0]
list_b = [1, 1, 0]
# list_a + list_b → [2, 3, 0, 1, 1, 0]  ← 이어붙이기! 😢

# VPython vector로 시도
vec_a = vector(2, 3, 0)
vec_b = vector(1, 1, 0)
print(vec_a + vec_b)   # <- 여기가 벡터 덧셈!

예상 출력:

<3, 4, 0>

연산 리스트 [2,3,0] + [1,1,0] vector vector(2,3,0) + vector(1,1,0)
결과 [2, 3, 0, 1, 1, 0] (이어붙이기) <3, 4, 0> (성분별 더하기) ✅
의미 프로그래밍적 연결 수학적 벡터 덧셈

리스트는 그냥 숫자를 담는 가방이고, vector는 수학을 이해하는 똑똑한 가방인 셈이죠! 🎒

v2: 벡터 연산 — 덧셈, 뺄셈, 스칼라곱

이제 벡터끼리의 연산을 해봅시다. 이전 v1 코드에 연산을 추가합니다:

from vpython import *

a = vector(2, 3, 0)
b = vector(1, -1, 0)

# 벡터 덧셈: 성분끼리 더하기
print("a + b =", a + b)    # <- 여기가 벡터 덧셈

# 벡터 뺄셈: 성분끼리 빼기
print("a - b =", a - b)    # <- 여기가 벡터 뺄셈

# 스칼라곱: 모든 성분에 같은 수를 곱하기
print("3 * a =", 3 * a)    # <- 여기가 스칼라곱
print("0.5 * b =", 0.5 * b)

예상 출력:

a + b = <3, 2, 0>
a - b = <1, 4, 0>
3 * a = <6, 9, 0>
0.5 * b = <0.5, -0.5, 0>

개념-코드 매핑:

  • 7번째 줄 a + b벡터 덧셈: 각 성분끼리 더합니다. (2+1, 3+(-1), 0+0) = (3, 2, 0)
  • 10번째 줄 a - b벡터 뺄셈: 각 성분끼리 뺍니다. (2−1, 3−(−1), 0−0) = (1, 4, 0)
  • 13번째 줄 3 * a스칼라곱: 숫자(스칼라) × 벡터 = 모든 성분에 곱합니다. (3×2, 3×3, 3×0) = (6, 9, 0)

벡터 연산을 한눈에 정리하면:

flowchart TD A["벡터 a = (2, 3, 0)"] --> C["덧셈 a+b<br/>(3, 2, 0)"] B["벡터 b = (1, -1, 0)"] --> C A --> D["뺄셈 a-b<br/>(1, 4, 0)"] B --> D A --> E["스칼라곱 3*a<br/>(6, 9, 0)"]

🚨 에러 경험: vector끼리 곱하면?

아래 코드를 실행하면 어떤 에러가 날까요?

from vpython import *

a = vector(2, 3, 0)
b = vector(1, -1, 0)

# vector끼리 * 연산을 시도!
result = a * b
print(result)

에러 메시지:

TypeError: unsupported operand type(s) for *: 'vector' and 'vector'

원인 분석: * 연산자는 숫자 × 벡터 (스칼라곱)에만 사용할 수 있습니다. 벡터끼리 곱하려면 dot(a, b) (내적)이나 cross(a, b) (외적)이라는 별도 함수를 사용해야 합니다. 이 내용은 물리 시뮬레이션 심화에서 다루므로, 지금은 "벡터끼리는 * 안 됨!" 만 기억하세요.

수정 코드 — 스칼라곱으로 바꾸기:

from vpython import *

a = vector(2, 3, 0)

# 숫자(스칼라) × 벡터 → OK!
result = 2 * a
print(result)

예상 출력:

<4, 6, 0>

💡 핵심 정리: 벡터 연산 규칙

연산 코드 가능 여부 결과
벡터 + 벡터 a + b 성분별 덧셈
벡터 − 벡터 a - b 성분별 뺄셈
숫자 × 벡터 3 * a 모든 성분에 곱
벡터 × 벡터 a * b TypeError 에러!
벡터 / 숫자 a / 2 모든 성분을 나눔

🛠️ 실습 1 — arrow로 벡터를 눈에 보이게! (10분)

벡터를 숫자로만 보면 감이 안 잡힙니다. VPython의 arrow 객체를 사용해서 벡터를 3D 공간에 직접 그려봅시다!

v1: 화살표 하나 그리기

from vpython import *

# 3D 캔버스 생성
scene = canvas(title="벡터 시각화", width=600, height=400)

# 벡터를 화살표로 표현
# pos: 화살표 시작점, axis: 화살표 방향과 길이(= 벡터!)
my_arrow = arrow(
    pos=vector(0, 0, 0),      # 원점에서 시작
    axis=vector(3, 2, 0),     # <- 여기가 우리가 표현할 벡터!
    color=color.yellow,
    shaftwidth=0.1             # 화살표 굵기
)

실행 결과: 원점 (0,0,0)에서 (3,2,0) 방향으로 노란색 화살표가 그려집니다.

위 코드의 10번째 줄 axis=vector(3, 2, 0)이 바로 화살표가 나타내는 벡터입니다. axis는 "화살표가 가리키는 방향과 길이"를 의미해요.

v2: 두 벡터의 덧셈을 눈으로 확인

이전 코드에서 두 번째 벡터와 합 벡터를 추가합니다:

from vpython import *

scene = canvas(title="벡터 덧셈 시각화", width=600, height=400)

# 벡터 a (빨간색)
a = vector(3, 1, 0)
arrow_a = arrow(pos=vector(0, 0, 0), axis=a,
                color=color.red, shaftwidth=0.1)

# 벡터 b (파란색) — a의 끝점에서 시작!
b = vector(1, 2, 0)
arrow_b = arrow(pos=a, axis=b,                        # <- pos가 a! (이어 그리기)
                color=color.blue, shaftwidth=0.1)

# 벡터 a+b (초록색) — 원점에서 최종 도착점까지
result = a + b                                         # <- 벡터 덧셈!
arrow_sum = arrow(pos=vector(0, 0, 0), axis=result,
                  color=color.green, shaftwidth=0.15)

# 라벨로 표시
label(pos=a/2, text="a", color=color.red)
label(pos=a + b/2, text="b", color=color.blue)
label(pos=result/2 + vector(0, -0.3, 0), text="a+b", color=color.green)

실행 결과: 빨간 화살표(a) 끝에 파란 화살표(b)가 이어지고, 초록 화살표(a+b)가 원점에서 최종 도착점까지 한 번에 연결됩니다.

개념-코드 매핑:

  • 12번째 줄 pos=a — 벡터 b의 시작점을 a의 끝점에 놓아서 이어 그리기(tip-to-tail) 를 구현합니다.
  • 16번째 줄 result = a + b — 벡터 덧셈의 결과를 계산합니다. 이것이 합벡터입니다.
  • 17번째 줄의 초록 화살표가 "원점에서 최종 도착점까지의 지름길" — 바로 벡터 덧셈의 기하학적 의미입니다!

v3: pos 이동에 벡터 활용하기

이제 핵심입니다! 벡터를 물체의 위치 변경에 사용해봅시다. 3차시에서 pos.x만 바꾸던 것을, 벡터 한 줄로 깔끔하게 바꿉니다:

from vpython import *

scene = canvas(title="벡터로 위치 이동", width=600, height=400)

ball = sphere(pos=vector(0, 0, 0), radius=0.3, color=color.cyan)

# 이동할 방향과 거리를 벡터 하나로!
displacement = vector(2, 1.5, 0)   # <- 이동 벡터 (변위)

# 위치 업데이트: 현재 위치 + 이동 벡터 = 새 위치
ball.pos = ball.pos + displacement  # <- 벡터 덧셈으로 위치 변경!

print("공의 새 위치:", ball.pos)

예상 출력:

공의 새 위치: <2, 1.5, 0>

이전 방식(3차시)과 비교해볼까요?

3차시 방식 (성분별) 4차시 방식 (벡터)
ball.pos.x = ball.pos.x + 2 ball.pos = ball.pos + vector(2, 1.5, 0)
ball.pos.y = ball.pos.y + 1.5 (한 줄이면 끝!)
2줄 필요, z도 바꾸면 3줄 1줄로 3D 이동 완료

11번째 줄 ball.pos = ball.pos + displacement가 바로 벡터를 활용한 위치 업데이트입니다. 이 패턴이 앞으로 속도, 가속도를 배울 때 계속 반복됩니다!


📖 핵심 개념 2: mag와 norm 함수 (10분)

🎯 비유로 시작: 레시피의 "방향"과 "양"

카레를 만들 때 "물 500ml"라고 하면: - 방향: 물 (다른 재료가 아님) - 크기: 500ml (양)

벡터도 마찬가지입니다. vector(3, 4, 0)에는: - 방향: 오른쪽으로 약간, 위로 조금 더 (3:4 비율) - 크기: 이 벡터의 총 길이

이 둘을 분리해서 다룰 수 있으면 매우 강력합니다! 🔧

🔎 정의: mag와 norm

  • mag(v) 또는 v.mag: 벡터 v의 크기(magnitude) 를 반환합니다. 피타고라스 정리로 계산됩니다: √(x² + y² + z²)
  • norm(v) 또는 hat(v) 또는 v.hat: 벡터 v와 같은 방향이지만 크기가 1인 벡터(단위벡터)를 반환합니다.
flowchart LR A["벡터 v<br/>(3, 4, 0)"] --> B["mag: 크기<br/>= 5.0"] A --> C["norm: 방향<br/>= (0.6, 0.8, 0)"] B --> D["크기 × 방향<br/>= 원래 벡터 복원!"] C --> D

v1: mag 사용해보기

from vpython import *

v = vector(3, 4, 0)

# 벡터의 크기(길이) 구하기
length = mag(v)                # <- 여기가 mag (크기 구하기)
print("벡터:", v)
print("크기:", length)

# 수학적으로 확인: sqrt(3² + 4²) = sqrt(9+16) = sqrt(25) = 5
print("검증:", (3**2 + 4**2)**0.5)

예상 출력:

벡터: <3, 4, 0>
크기: 5.0
검증: 5.0