변수와 자료형이란? 값을 저장하고 사용하는 프로그래밍 기초
변수와 자료형이란? 값을 저장하고 사용하는 프로그래밍 기초#
쇼핑몰 프로그램을 만든다고 해보자. 화면에는 상품 이름과 가격을 보여주고, 사용자가 선택한 수량에 따라 결제 금액을 계산해야 한다.
상품 이름: 파이썬 입문서
가격: 12,000원
수량: 2개
결제 금액: 24,000원프로그램이 이 일을 하려면 파이썬 입문서, 12000, 2라는 값을 구분해 다뤄야 한다. 상품 이름은 화면에 글자로 보여주고, 가격과 수량은 곱셈에 사용한다.
이처럼 값에 이름을 붙여 다시 사용하는 방법이 변수이며, 그 값이 어떤 종류인지를 구분하는 개념이 자료형이다. 두 개념은 프로그래밍 언어를 처음 배울 때 가장 먼저 만나지만, 이후의 조건문·함수·객체를 이해하는 바탕이 된다.
이 글에서는 Python을 기본으로 변수와 자료형을 살펴본다.
변수는 왜 필요할까?#
다음 코드는 가격 12000에 수량 2를 곱한다.
print(12000 * 2)실행 결과는 24000이다. 하지만 코드를 처음 보는 사람은 12000이 가격인지, 배송비인지, 보유 포인트인지 바로 알기 어렵다.
값에 이름을 붙이면 의미가 드러난다.
product_price = 12000
quantity = 2
total_price = product_price * quantity
print(total_price) # 24000product_price는 상품 가격, quantity는 수량, total_price는 계산 결과를 나타낸다.
변수를 사용하면 값을 여러 곳에서 반복해 쓰기도 쉽다.
product_name = "파이썬 입문서"
product_price = 12000
quantity = 2
print(f"상품: {product_name}")
print(f"가격: {product_price:,}원")
print(f"수량: {quantity}개")
print(f"결제 금액: {product_price * quantity:,}원")출력 결과는 다음과 같다.
상품: 파이썬 입문서
가격: 12,000원
수량: 2개
결제 금액: 24,000원변수는 값을 재사용하게 해줄 뿐 아니라, 그 값이 프로그램에서 어떤 역할을 맡는지 설명한다.
변수는 메모리의 ‘방’일까?#
입문서에서는 변수를 흔히 이름표가 붙은 메모리의 상자에 비유한다. 어떤 값을 기억해 두었다가 이름으로 다시 사용한다는 개념을 이해하기에는 좋은 비유다.
하지만 Python의 실제 동작을 정확히 설명하려면 조금 다르게 보아야 한다.
Python의 변수 이름은 객체를 가리키는 이름이다.
score = 100이 코드는 score라는 이름이 정수 객체 100을 가리키도록 연결한다. 처음에는 ‘score에 100을 저장했다’고 이해해도 충분하지만, 변수가 언제나 고정된 메모리 방 하나를 소유한다고 생각하면 나중에 리스트나 객체를 다룰 때 혼동할 수 있다.
예를 들어 다음 두 이름은 처음에 같은 리스트 객체를 가리킨다.
first_cart = ["사과", "우유"]
second_cart = first_cart
second_cart.append("계란")
print(first_cart) # ['사과', '우유', '계란']
print(second_cart) # ['사과', '우유', '계란']second_cart에 새 상품을 추가했는데 first_cart에서도 보인다. 두 변수 이름이 같은 리스트 객체를 참조하기 때문이다.
지금 모든 메모리 동작을 외울 필요는 없다. 입문 단계에서는 다음 두 사실을 기억하자.
- 변수 이름을 사용해 값을 다시 읽을 수 있다.
- 리스트처럼 변경 가능한 객체는 여러 이름이 같은 객체를 가리킬 수 있다.
변수 사용의 기본: 이름에 할당하고 읽기#
할당: 이름과 값을 연결한다#
Python에서는 다음처럼 변수에 값을 할당한다.
user_age = 30
user_name = "홍길동"=는 수학에서 ‘양쪽이 같다’는 뜻으로 읽지 않는다. 오른쪽의 값을 왼쪽 이름에 할당한다는 뜻이다.
score = 100이 코드를 읽을 때는 “score는 100과 같다”보다 “score에 100을 할당한다”가 정확하다.
Python에서는 JavaScript의 let이나 Java의 int처럼 변수 이름만 먼저 선언하는 문법을 일반적으로 사용하지 않는다. 값을 할당하면서 이름이 만들어진다.
user_age = 30아직 값을 할당하지 않은 이름을 바로 사용하면 오류가 난다.
print(next_year_age) # NameError참조: 이름으로 값을 사용한다#
한 번 할당한 값은 이름으로 다시 읽을 수 있다.
user_age = 30
next_year_age = user_age + 1
print(user_age) # 30
print(next_year_age) # 31user_age + 1은 현재 user_age가 가리키는 값 30을 읽고 1을 더한다. 계산 결과인 31이 next_year_age에 할당된다.
재할당: 같은 이름이 다른 값을 가리키게 한다#
score = 100
score = score + 50
print(score) # 150두 번째 줄은 다음 순서로 이해할 수 있다.
- 오른쪽의
score에서 현재 값100을 읽는다. 50을 더해150을 계산한다.- 왼쪽의
score가 새 값150을 가리키도록 재할당한다.
다음과 같이 줄여 쓸 수도 있다.
score += 50두 방식 모두 현재 점수에 50을 더한다.
변수 이름은 어떻게 지어야 할까?#
다음 코드를 보자.
x = "이순신"
y = 45
z = y + 1실행은 되지만 x, y, z가 무엇을 뜻하는지 코드를 따라가며 추측해야 한다.
이름을 바꾸면 의미가 명확해진다.
user_name = "이순신"
user_age = 45
next_year_age = user_age + 1Python에서는 여러 단어로 된 변수 이름을 user_name, total_price처럼 소문자와 밑줄로 연결하는 방식을 흔히 사용한다.
좋은 이름을 짓는 기준은 다음과 같다.
- 값의 역할을 나타낸다.
- 같은 개념에는 같은 단어를 사용한다.
- 이름만 보고 단위를 알 필요가 있다면 단위도 드러낸다.
- 다른 의미의 값을 같은 변수에 번갈아 넣지 않는다.
예를 들어 시간이 밀리초 단위라면 다음 이름이 timeout보다 분명하다.
timeout_ms = 3000반대로 모든 변수 이름을 길게 만들 필요는 없다. 아주 짧은 반복문에서 위치를 나타내는 index처럼 맥락상 분명한 이름이면 충분하다.
값의 의미가 바뀌는데 같은 이름을 재사용하지 않기#
data = "12000"
data = int(data)
data = data * 2작동은 하지만 data가 입력 문자열인지, 변환된 숫자인지, 계산 결과인지 줄마다 의미가 바뀐다.
price_text = "12000"
product_price = int(price_text)
total_price = product_price * 2이렇게 나누면 값이 어떤 단계를 거쳐 변했는지 드러난다.
자료형이란 무엇인가?#
자료형은 값의 종류다. 같은 화면에 표시되더라도 숫자와 문자열은 서로 다른 방식으로 사용된다.
number = 10
text = "10"
print(number + number) # 20
print(text + text) # 1010첫 번째 +는 숫자 덧셈이다. 두 번째 +는 문자열 연결이다.
값의 자료형은 type()으로 확인할 수 있다.
print(type(10)) # <class 'int'>
print(type(3.14)) # <class 'float'>
print(type("안녕")) # <class 'str'>
print(type(True)) # <class 'bool'>자료형이 필요한 이유를 단순히 “메모리를 딱 맞는 크기로 배정하기 위해서”라고만 이해하면 부족하다. 자료형은 그 값이 어떤 연산을 지원하고, 코드가 그 값을 어떻게 해석할지에도 영향을 준다.
예를 들어 "사과" + "우유"는 문자열을 연결할 수 있지만, "사과" / 2는 의미 있는 연산으로 정의돼 있지 않다.
숫자형: 정수와 실수#
정수 int#
정수는 소수 부분이 없는 숫자다. 개수, 순번, 나이 등을 나타낼 때 사용한다.
quantity = 3
temperature = -5
visitor_count = 120정수에는 덧셈, 뺄셈, 곱셈 등의 연산을 적용할 수 있다.
product_price = 12000
quantity = 3
total_price = product_price * quantity
print(total_price) # 36000실수 float#
Python에서 float는 소수 부분을 가진 값을 표현하는 데 사용하는 부동 소수점 자료형이다.
height = 175.5
temperature = 36.7
discount_rate = 0.1다만 컴퓨터의 이진 부동 소수점 방식으로는 일부 10진수 소수를 정확히 표현하지 못한다.
print(0.1 + 0.2)
# 0.30000000000000004이 값은 Python이 무작위로 계산을 틀린 결과가 아니다. 일반적인 부동 소수점 표현의 특성이다.
게임 좌표나 측정값처럼 근삿값을 사용할 수 있는 경우와 달리, 결제 금액처럼 정확한 10진수 처리가 필요한 경우에는 계산 방식과 금액의 단위를 신중하게 선택해야 한다. 예를 들어 원 단위의 정수로 계산하거나, 요구사항에 맞춰 Python의 decimal.Decimal을 사용할 수 있다.
이 주제는 별도의 글에서 자세히 다루는 편이 좋다. 여기서는 소수 계산 결과를 언제나 정확한 값으로 가정하지 않는다는 점을 기억하자.
나눗셈 결과는 어떻게 될까?#
Python 3에서 /는 일반적으로 실수 결과를 반환한다.
print(10 / 2) # 5.0정수 몫을 구하는 //, 나머지를 구하는 %와 구분해야 한다.
print(10 // 3) # 3
print(10 % 3) # 1짝수인지 검사할 때 number % 2 == 0을 사용하는 이유가 여기에 있다. 2로 나눈 나머지가 0이면 짝수다.
문자열 str: 글자를 값으로 다루기#
문자열은 이름, 주소, 상품명, 안내 문구처럼 텍스트를 표현한다. Python에서는 따옴표로 감싼다.
user_name = "홍길동"
book_title = '파이썬 입문서'
error_message = "아이디가 존재하지 않습니다."따옴표를 빼면 다른 의미가 된다.
user_name = "홍길동"
print(user_name) # 변수의 값을 읽는다.
# print(홍길동) # 홍길동이라는 변수 이름을 찾으려 하므로 오류문자열 이어 붙이기#
문자열끼리는 +로 이어 붙일 수 있다.
family_name = "홍"
given_name = "길동"
full_name = family_name + given_name
print(full_name) # 홍길동중간에 공백을 넣고 싶다면 공백 문자열을 추가한다.
first_name = "Ada"
last_name = "Lovelace"
full_name = first_name + " " + last_name
print(full_name) # Ada Lovelace변수와 문장을 함께 출력할 때는 f-문자열이 읽기 편하다.
user_name = "홍길동"
user_age = 30
print(f"{user_name}님의 나이는 {user_age}세입니다.")
# 홍길동님의 나이는 30세입니다.문자열 숫자는 계산 전에 변환한다#
사용자가 화면에 입력한 "2"는 숫자처럼 보이지만 문자열일 수 있다.
quantity_text = "2"
print(quantity_text + quantity_text) # 22숫자로 계산하려면 변환해야 한다.
quantity = int(quantity_text)
print(quantity + quantity) # 4변환할 수 없는 글자가 들어오면 어떻게 될까?
quantity = int("두 개") # ValueError실제 사용자 입력을 처리하는 프로그램은 숫자 변환 실패에 대비해야 한다. 단순히 자료형을 아는 것에서 끝나지 않고, 값이 프로그램에 들어오는 경계에서 검증하는 습관이 필요하다.
불리언 bool: 참과 거짓 표현하기#
불리언은 두 값으로 상태를 표현한다.
True
FalsePython에서는 첫 글자를 대문자로 쓴다. 문자열 "True"와 불리언 True도 서로 다른 값이다.
is_logged_in = True
is_payment_completed = False
print(type(is_logged_in)) # <class 'bool'>불리언은 비교 연산의 결과로 자주 만들어진다.
user_age = 20
is_over_19 = user_age >= 19
print(is_over_19) # True‘19세 이상’이라는 조건에는 >= 19를 사용했다. > 19라고 쓰면 19세는 제외된다. 이처럼 업무 조건을 코드로 옮길 때는 경계값을 정확히 확인해야 한다.
비밀번호 비교를 학습 예제로 볼 때#
saved_code = "1234"
entered_code = "1234"
is_code_correct = entered_code == saved_code
print(is_code_correct) # True==는 두 값이 같은지 비교한다. =는 할당이고 ==는 비교이므로 구분해야 한다.
위 코드는 불리언을 설명하기 위한 예제일 뿐이다. 실제 로그인 시스템에서 비밀번호를 코드에 평문으로 저장하거나 단순 문자열 비교만으로 인증을 구현해서는 안 된다.
불리언은 어디에 쓰일까?#
다음 장에서 배울 조건문은 불리언 결과에 따라 실행할 코드를 선택한다.
is_logged_in = True
if is_logged_in:
print("마이페이지를 보여줍니다.")
else:
print("로그인이 필요합니다.")변수에 저장한 값이 프로그램의 판단 기준으로 사용되는 것이다.
Python은 변수의 타입을 어떻게 다룰까?#
Python은 동적 타입 언어다. 변수 이름에 자료형을 먼저 고정해 선언하지 않아도 된다.
user_age = 30
print(type(user_age)) # <class 'int'>
user_age = "서른 살"
print(type(user_age)) # <class 'str'>같은 user_age라는 이름에 정수를 할당했다가 문자열을 다시 할당할 수 있다. 이는 유연하지만, 이후 코드가 user_age를 숫자라고 가정하면 문제가 생길 수 있다.
user_age = "서른 살"
# TypeError: 문자열에 정수 1을 더할 수 없다.
next_year_age = user_age + 1Python이 ‘자료형이 없는 언어’라는 뜻은 아니다. 값에는 자료형이 있고, 코드가 실행되는 동안 연산이 가능한지 확인된다.
정적 타입 언어와의 차이#
Java처럼 정적 타입 검사를 사용하는 언어에서는 변수의 타입을 명시하는 예가 흔하다.
int userAge = 30;
// userAge = "서른 살"; // 타입이 맞지 않아 오류정적 타입 언어라고 해서 모든 타입을 반드시 직접 적어야 하는 것은 아니다. 언어와 문맥에 따라 타입을 추론하기도 한다.
Python에서도 타입 힌트를 작성할 수 있다.
user_age: int = 30하지만 이 표기만으로 Python 실행 시 재할당을 강제로 막지는 않는다. 타입 검사 도구가 코드를 확인하도록 설정하면 오류를 미리 찾는 데 도움이 될 수 있다.
처음에는 언어를 ‘안전한 언어’와 ‘위험한 언어’로 나누기보다 다음을 확인하자.
- 값에 어떤 자료형이 있는가?
- 이 언어는 타입을 언제 검사하는가?
- 지금 작성한 연산은 그 값의 자료형에 맞는가?
여러 값을 다루려면 리스트를 사용한다#
학생 네 명의 이름을 각각 변수에 담아보자.
student1 = "김철수"
student2 = "이영희"
student3 = "박지성"
student4 = "손흥민"학생이 100명으로 늘면 이 방식은 관리하기 어렵다. Python에서는 관련된 여러 값을 리스트로 묶을 수 있다.
students = ["김철수", "이영희", "박지성", "손흥민"]장바구니도 마찬가지다.
shopping_cart = ["사과", "우유", "계란", "휴지"]리스트는 값을 순서대로 담고, 각 값의 위치를 인덱스로 구분한다.
인덱스는 0부터 시작한다#
| 인덱스 | 값 |
|---|---|
0 |
"사과" |
1 |
"우유" |
2 |
"계란" |
3 |
"휴지" |
첫 번째 값의 인덱스가 1이 아니라 0이라는 점에 주의하자.
shopping_cart = ["사과", "우유", "계란", "휴지"]
print(shopping_cart[0]) # 사과
print(shopping_cart[2]) # 계란존재하지 않는 위치에 접근하면 IndexError가 발생한다.
print(shopping_cart[4]) # IndexError항목이 네 개라면 사용할 수 있는 양수 인덱스는 0부터 3까지다.
Python은 음수 인덱스도 지원한다. -1은 마지막 항목이다.
print(shopping_cart[-1]) # 휴지리스트의 값 변경하기#
Python 리스트는 변경 가능한 자료구조다.
shopping_cart[1] = "저지방 우유"
print(shopping_cart)
# ['사과', '저지방 우유', '계란', '휴지']새 항목을 추가할 수도 있다.
shopping_cart.append("빵")
print(shopping_cart)
# ['사과', '저지방 우유', '계란', '휴지', '빵']리스트의 항목 수는 len()으로 확인한다.
print(len(shopping_cart)) # 5배열과 리스트는 완전히 같은 말일까?#
다른 언어의 배열과 Python의 리스트는 여러 값을 순서대로 관리한다는 점에서 비슷하다. 하지만 내부 구조, 허용하는 값, 크기 변경 방법은 언어와 자료구조에 따라 다르다.
입문 단계에서는 ‘여러 값을 하나의 이름 아래 순서대로 관리한다’는 공통점을 먼저 이해하자. 세부적인 자료구조의 차이는 실제 언어와 사용 목적에 따라 살펴보면 된다.
리스트와 반복문이 만나면#
리스트의 장점은 항목이 늘어나도 같은 방식으로 처리할 수 있다는 데 있다.
shopping_cart = ["사과", "우유", "계란", "휴지"]
for item in shopping_cart:
print(f"장바구니 상품: {item}")상품이 네 개든 100개든 같은 반복문을 사용할 수 있다.
가격 목록을 계산할 수도 있다.
prices = [3000, 2500, 6000]
total_price = 0
for price in prices:
total_price += price
print(total_price) # 11500Python에서는 다음처럼 sum()으로 숫자 목록의 합계를 구할 수도 있다.
print(sum(prices)) # 11500리스트가 값을 묶고, 반복문이나 함수가 그 값을 처리한다. 변수와 자료형을 익혀두면 이후의 제어 구조가 왜 필요한지도 자연스럽게 연결된다.
하나의 장바구니 예제로 다시 보기#
지금까지 배운 개념을 한 코드에 모아보자.
product_names = ["사과", "우유", "계란"]
product_prices = [3000, 2500, 6000]
discount_rate = 0.1
is_discount_available = True
subtotal = sum(product_prices)
if is_discount_available:
discount_amount = subtotal * discount_rate
else:
discount_amount = 0
total_price = subtotal - discount_amount
print(f"상품: {', '.join(product_names)}")
print(f"상품 금액: {subtotal:,.0f}원")
print(f"할인 금액: {discount_amount:,.0f}원")
print(f"결제 금액: {total_price:,.0f}원")출력 결과는 다음과 같다.
상품: 사과, 우유, 계란
상품 금액: 11,500원
할인 금액: 1,150원
결제 금액: 10,350원여기서 사용한 개념을 구분해 보자.
| 코드의 값 | 자료형 | 역할 |
|---|---|---|
product_names |
문자열을 담은 리스트 | 상품 이름 목록 |
product_prices |
숫자를 담은 리스트 | 상품 가격 목록 |
discount_rate |
실수 | 할인율 |
is_discount_available |
불리언 | 할인 가능 여부 |
subtotal |
숫자 | 할인 전 상품 금액 |
total_price |
숫자 | 최종 결제 금액 |
이 코드는 자료형과 변수의 역할을 이해하기 위한 학습 예제다. 실제 쇼핑몰에서는 상품 이름과 가격을 따로 떨어진 두 리스트로 관리하면 순서가 어긋날 수 있다. 이후 딕셔너리나 객체를 배우면 상품 이름과 가격을 한 단위로 묶는 방법을 사용할 수 있다.
초보자가 자주 하는 실수#
문자열과 숫자를 같은 값으로 생각한다#
print(10 + 10) # 20
print("10" + "10") # 1010입력값을 계산에 사용하려면 실제 자료형을 확인하고 필요한 경우 변환한다.
할당과 비교를 혼동한다#
score = 100 # 할당
score == 100 # 비교 결과: True=는 값을 할당하고, ==는 두 값이 같은지 비교한다.
존재하지 않는 변수를 사용한다#
# 아직 값을 할당하지 않았다.
print(total_price) # NameError사용하기 전에 어떤 값이 할당되는지 확인한다.
리스트의 첫 번째 항목을 [1]로 찾는다#
items = ["사과", "우유"]
print(items[0]) # 사과
print(items[1]) # 우유Python 리스트의 인덱스는 0부터 시작한다.
리스트를 복사했다고 생각했는데 같은 객체를 가리킨다#
original = ["사과"]
copied = original
copied.append("우유")
print(original) # ['사과', '우유']copied = original은 새 리스트를 만드는 코드가 아니다. 두 이름이 같은 리스트를 가리킨다.
독립적인 리스트가 필요하다면 다음처럼 복사할 수 있다.
original = ["사과"]
copied = original.copy()
copied.append("우유")
print(original) # ['사과']
print(copied) # ['사과', '우유']이 예제처럼 항목이 문자열이면 동작을 이해하기 쉽다. 리스트 안에 다른 리스트나 객체가 들어 있다면 .copy()만으로 내부 항목까지 모두 독립적으로 복사되는 것은 아니다.
직접 확인해 볼 문제#
다음 값을 사용해 코드를 작성해 보자.
product_name = "키보드"
product_price = 35000
quantity = 2product_name을 출력한다.product_price * quantity의 결과를total_price에 할당한다.f"{total_price:,}원"형식으로 금액을 출력한다.quantity를 문자열"2"로 바꾸면 계산에서 어떤 차이가 생기는지 확인한다.
기대한 기본 결과는 70,000원이다. 네 번째 단계에서는 오류가 나거나 의도와 다른 연산이 일어나는 이유를 값의 자료형으로 설명해 보는 것이 중요하다.
정리#
변수는 프로그램에서 값에 이름을 붙여 다시 사용할 수 있게 한다. Python에서는 값을 할당하면서 이름을 만들고, 같은 이름에 다른 값을 재할당할 수도 있다.
자료형은 값의 종류다. 정수 int, 실수 float, 문자열 str, 불리언 bool은 각각 표현하는 정보와 가능한 연산이 다르다.
여러 값을 순서대로 관리할 때는 리스트를 사용할 수 있다. 리스트의 항목은 0부터 시작하는 인덱스로 접근하며, 반복문과 함께 사용하면 항목 수가 달라져도 같은 처리를 적용할 수 있다.
다음 코드를 만났을 때는 세 가지를 질문해 보자.
이 이름은 어떤 값을 가리키는가? 그 값의 자료형은 무엇인가? 이 연산은 그 자료형에 맞는가?
이 질문에 답할 수 있다면 변수와 자료형을 외운 수준을 넘어, 프로그램의 값이 흐르는 과정을 읽기 시작한 것이다.