1. 변수(variable)와 상수(constant)
프로그래밍에서 변수와 상수는 데이터를 담는 그릇이라고 생각하면 된다.
프로그램은 수많은 데이터들의 집합으로 이루어져 있으며, 이 데이터를 어떻게 효율적으로 저장하고 관리하느냐가 핵심이다.
이러한 데이터를 체계적으로 다루기 위한 방법이 바로 변수와 상수이다.
필요한 값을 저장해 두었다가 꺼내 쓰고, 수정하거나 유지하는 모든 과정은 변수와 상수를 통해 이루어진다.
⸻
스위프트에서 변수와 상수 선언하기
스위프트에서는 다음과 같은 문법으로 변수와 상수를 선언한다.
var 변수명: 데이터타입 = 값
let 상수명: 데이터타입 = 값
여기서 선언이란,
어떤 데이터를 담을 공간을 만들겠다고 컴파일러에게 알리는 행위이다.
즉, 앞으로 이 이름을 가진 저장 공간을 사용할 것이라는 약속이라고 보면 된다.
var — 변할 수 있는 값
var는 변수를 선언할 때 사용한다.
변수는 생성 이후에도 값이 변경될 수 있는 저장 공간이다.
var age: Int = 20
age = 21 // 가능
한 번 선언한 이후에도 얼마든지 값이 바뀔 수 있다.
다만 처음 선언한 타입에 맞는 값으로만 변경할 수 있다.
age = "스무살" // 오류 발생
let — 변하지 않는 값
let은 상수를 선언할 때 사용한다.
상수는 한 번 값을 정하면 이후에 변경할 수 없는 저장 공간이다.
let birthYear: Int = 1995
birthYear = 1996 // 오류 발생
선언과 동시에 값이 고정되며, 어떤 경우에도 수정이 불가능하다.
왜 Swift는 이 둘을 분리했을까?
변수와 상수를 나누는 이유는 단순한 문법 차이가 아니라 컴파일러 최적화와 코드 안정성 때문이다.
컴파일러 입장에서는 값이 절대로 바뀌지 않는다는 사실을 알고 있으면 메모리 관리나 성능 최적화 측면에서 훨씬 유리하다.
또한 개발자 입장에서도 의도치 않은 값 변경을 미리 방지할 수 있어 버그를 줄이는 데 도움이 된다.
그래서 스위프트에서는 가능한 한 let을 먼저 사용하고,
정말 값이 변해야 할 때만 var를 사용하는 것이 권장된다.
⸻
2. 기본 데이터 타입(Primitive Type)
위에서 변수와 상수는 데이터를 담는 그릇이라고 설명했다.
그렇다면 이 그릇 안에는 아무 데이터나 막 담을 수 있을까? 그렇지 않다.
변수와 상수에는 반드시 정해진 종류의 데이터만 담을 수 있다.
변수와 상수가 데이터를 담는 그릇이라면, 타입은 그 그릇 안에 들어갈 수 있는 재료의 종류라고 이해하면 된다.
즉, 타입은 이 그릇에 어떤 종류의 데이터가 들어갈 수 있는지를 정하는 기준이다.
Swift에서는 아래와 같은 데이터 타입을 제공한다.
⸻
정수를 나타내는 Int
Int는 정수를 표현하는 타입이다.
let count: Int = 10
양수, 음수, 0을 모두 포함하며,
요즘은 거의 64비트 환경이다보니 Int는 64비트의 크기를 가지지만 시스템 환경에 따라 32비트를 가지는 경우가 있다.
부호가 없는 정수를 표현할 때는 UInt를 사용한다.
Swift는 기본 Int외에 Int8, Int16, Int32, Int64, Int128로 각각의 크기별의 정수형 타입을 추가로 제공한다.
UIint의 경우도 마찬가지다.
실수를 나타내는 Double과 Float
소수점을 포함하는 실수는 부동 소수점 타입으로 표현한다.
let height: Double = 175.5
let weight: Float = 70.3
- Double : 8바이트, 높은 정밀도
- Float : 4바이트, 상대적으로 낮은 정밀도
일반적으로는 Double 사용이 권장된다.
문자열을 나타내는 String
String은 문자열을 표현하는 타입이다.
let name: String = "Swift"
한글, 영어, 이모지 등 다양한 문자를 안전하게 표현할 수 있다.
문자를 나타내는 Character
Character는 하나의 문자를 표현하는 타입이다.
유니코드도 문자 하나로 인식하기 때문에 가능하지만 2글자 이상이 되어 문자열이 되는 경우에는 Swift는 얄짤없이 오류를 발생시킨다.
let grade: Character = "A"
let grade: Character = "AB" // error
참과 거짓을 나타내는 Bool
Bool은 참(true)과 거짓(false)을 표현하는 타입이다.
let isLogin: Bool = true
모든 타입을 담을 수 있는 Any와 AnyObject
Any는 모든 타입을 담을 수 있는 타입이며,
AnyObject는 모든 클래스 타입을 담을 수 있는 타입이다.
var value: Any = 10
value = "A" // 가능
value = 1.5 // 가능
let object: AnyObject = MyClass()
여러 값을 단일 그룹화 하는 Tuples
튜플은 여러 값을 단일복합값으로 그룹화 한다.
튜플안에 값은 어떠한 타입도 가능하며 서로 같은 타입일 필요는 없다.
다만, 튜플은 간단한 그룹을 만들 때 유용하지, 그룹이 복잡해지면 추후 나올 구조체나 클래스로 묶는 것이 더 유용하다.
let http404Error = (404, "Not Found")
(404, "Not Found") 튜플은 HTTP 상태 코드에 두 개별 값인 Int와 String을 함께 그룹화하여 제공한다.
타입 추론
스위프트는 타입안전 언어이다.
앞서 언급했듯이, 스위프트에서 사용하는 모든 값은 타입을 가진다.
따라서 원칙적으로는 변수를 선언할때 함께 타입을 명시해주어야 한다.
하지만 스위프트는 변수에 대입되는 값을 보고 그 타입을 추론하는 기능을 제공한다.
이 기능을 타입 추론이라고 한다.
타입 추론 기능 덕분에, 변수 선언 시 타입을 생략할 수 있지만,
이 방식은 선언과 동시에 값을 대입하는 경우에만 사용할 수 있다.
var a = 1 // a의 타입은 Int형으로 자동 추론됨
var b = 123.456 // b의 타입은 Double형로 추론됨. Float형이 되지 않는건 스위프트가 무조건 Double로 추론하도록 설계되어있기 때문
var c = "ABC" // c의 타입은 String형으로 추론됨
var d = "A" // 한글자여도 d의 타입은 Character형이 아닌 String형으로 추론됨
var e = 3 + 0.141592 // e는 Double로 추론됨
var f = someIntValueReturnFunc() // Int형을 반환하는 함수여서 Int형으로 추론됨
다만, 실무에서는 이러한 추론 기능을 자주 사용하지는 않는 편이다.
그 이유는 크게 두가지인데,
첫 번째로는 가독성이 떨어지는 것이다.
정확한 타입이 명시되어있지 않다보니, 코드를 읽는 사람이 변수의 타입을 확인하는 시간을 별도로 가져야 하는 경우가 발생한다.
두 번째로는 컴파일 타임에 오버헤드가 발생할 수 있어서이다.
위의 예제처럼 기본 데이터 타입에 대한 경우에는 추론이 간단하지만,
복잡한 연산들로 구성된 표현식의 경우 컴파일러가 타입을 추론하는데 많은 시간을 소모하게 된다.
그래서 컴파일 시간이 길어질 수 있는 상황이 발생할 수 있다.
⸻
3. 연산자(Operator)
앞에서 변수와 상수는 데이터를 담는 그릇이라고 설명했고, 타입은 그 그릇에 들어갈 수 있는 재료의 종류라고 정리했다.
그렇다면 이렇게 저장된 데이터는 어떻게 가공할 수 있을까?
그 역할을 하는 것이 바로 연산자이다.
연산자는 변수와 상수에 들어 있는 값을 계산하고, 비교하고, 조건에 따라 처리하는 방식이다.
즉, 데이터를 실제로 다루는 도구라고 보면 된다.
⸻
연산자의 구분
연산자는 사용하는 피연산자의 개수에 따라 다음과 같이 나뉜다.
- 단항 연산자 (Unary Operator)
하나의 값에 대해 동작하는 연산자이다.
let number = -10
-는 단항 연산자로, 하나의 값에 부호를 붙이는 역할을 한다.
- 이항 연산자 (Binary Operator)
두 개의 값을 대상으로 연산하는 연산자이다.
let sum = 10 + 20
+는 두 값을 더하는 이항 연산자이다.
- 삼항 연산자 (Ternary Operator)
세 개의 값을 사용하는 유일한 연산자이다.
let result = score >= 60 ? "합격" : "불합격"
조건에 따라 서로 다른 값을 선택할 때 사용한다.
⸻
연산자의 종류
Swift에는 다양한 연산자가 제공되며, 자주 사용되는 것들은 다음과 같다.
⸻
- 할당 연산자 (Assignment Operator)
값을 변수나 상수에 저장할 때 사용한다.
var number = 10
- 산술 연산자 (Arithmetic Operators)
기본적인 수학 연산을 수행한다.
let sum = 10 + 3
let diff = 10 - 3
let product = 10 * 3
let quotient = 10 / 3
- 나머지 연산자 (Remainder Operator)
나눗셈의 나머지를 구할 때 사용한다.
let remainder = 10 % 3
- 단항 덧셈 / 단항 뺄셈 연산자
값의 부호를 표현할 때 사용한다.
let positive = +5
let negative = -5
- 복합 할당 연산자 (Compound Assignment Operators)
연산과 할당을 동시에 수행한다.
var score = 10
score += 5 // score = score + 5
- 비교 연산자 (Comparison Operators)
두 값을 비교하여 Bool 값을 반환한다.
let isEqual = 10 == 10
let isGreater = 10 > 5
- 삼항 조건 연산자 (Ternary Conditional Operator)
조건에 따라 서로 다른 값을 반환한다.
let message = age >= 20 ? "성인" : "미성년자"
- Nil-병합 연산자 (Nil-Coalescing Operator)
옵셔널 값이 nil일 경우 기본값을 제공한다.
let name = inputName ?? "이름 없음"
- 범위 연산자 (Range Operators)
값의 범위를 표현할 때 사용한다.
let closedRange = 1...10 // 닫힌 범위
let halfOpenRange = 1..<10 // 반-열림 범위
let oneSidedRange = 5...
이때 연산자가 리턴하는 타입은 배열이 아닌 Range라고 하는 타입이므로 주의.
- 논리 연산자 (Logical Operators)
조건을 조합할 때 사용한다.
let isAdult = age >= 20 && hasID
let canEnter = isAdult || isVip
let isDenied = !isBlocked
⸻
참고
전위, 중위, 후위 연산자
연산자에는 값의 앞에 붙는지, 사이에 들어가는지, 뒤에 붙는지에 따라 전위, 중위, 후위 연산자로 나뉘는데,
하지만 이 개념은 여기서 다루기엔 어울리지 않으므로 추후 뒤에서 다루겠다.
⸻
연산자와 띄어쓰기
Swift에서는 연산자 주변의 띄어쓰기도 문법의 일부이다.
let sum = 10 + 5 // 올바른 표현
let sum = 10+ 5 // error
띄어쓰기를 잘못하면 컴파일 오류나 의도하지 않은 결과가 발생할 수 있으므로 주의해야 한다.
⸻
4. 주석(Comments)
코드에서 설명이나 기록을 남기기 위해 실행되지 않는 문자를 추가할 때 주석을 사용한다.
주석은 프로그램이 실행될 때 Swift 컴파일러에 의해 무시된다.
⸻
주석의 종류
Swift에서는 두 가지 형태의 주석을 제공한다.
한 줄 주석
// 한 줄 주석
여러 줄 주석
/*
여러 줄 주석 /* 중복된 여러 줄 주석 가능 */
*/
참고로, 여러 줄 주석 안에는 다른 언어들과 달리 여러 줄 주석을 중복해서 넣을 수 있다.