[Swift] 6. 옵셔널과 옵셔널 바인딩

Hoojeong Kim·2022년 3월 4일
0

Swift Base

목록 보기
8/22
post-thumbnail

옵셔널

옵셔널은 스위프트의 큰 특징 중 하나인 안전성을 담보로 하는 기능이다. 옵셔널은 단어 뜻 그대로 '선택적인', 즉 값이 '있을 수도 있고, 없을 수도 있음'을 나타내는 표현이다.


예를 들어 이 코드를 보자.
var name: String = ""

이렇게 변수를 선언한다면, 빈 문자열이니 name은 값이 없는 변수일까?
정답은 X이다. name 변수는 비어있는 것이 아니라, '비어있는 문자열'이라는 값을 가지고 있다.

var number: Int = 0

이 코드도 마찬가지 이다. number 라는 변수 안에 0이라는 값이 저장되어 있다.

그렇다면 값이 없는 경우는 무엇일까?

var number: Int? = nil

바로 변수가 nil인 경우이다. nil은 '값이 없음' 을 나타내는 표현으로, 다른 언어의 null과 동일하다.


이때 nil을 사용하기 위해서는 변수 또는 상수가 옵셔널로 선언되어 있어야 한다. 옵셔널은 바로 위 코드처럼 데이터 타입 뒤에 물음표(?)를 붙여 표현한다.
var name: String?

이렇게 표현한 옵셔널 타입 변수는 기본적으로 nil 값을 갖는다. 옵셔널은 앞서 말한대로 값이 있을 수도, 없을 수도 있기 때문에 일반적인 값을 넣을 수도 있다.
var name: String? = "Hoojeong"

일반 변수에 옵셔널 타입 변수를 넣는 거는요??

var requiredName: String = name

여기서 우리는 옵셔널이 무엇인지 생각해야 한다. 옵셔널은 값을 가지거나 가지지 않을 수 있다. 하지만 일반 변수는 무조건 값을 가져야 한다. 따라서 값이 항상 존재하는 일반 변수에 값이 없을 수도 있는 옵셔널 타입 변수를 넣는 건 불가능하다!

Value of optional type 'String?' must be unwrapped to a value of type 'String'


이번에는 옵셔널 타입 변수를 출력해보자.
var name: String? = "Hoojeong"

print(name)
Optional("Hoojeong")

결과를 보면 슬쩍 봐도 일반적인 변수와 다르다는 것을 알 수 있다. 마치 옵셔널이라는 포장지에 포장된 것처럼 보인다.
때문에, 옵셔널 타입 변수는 일반 변수와 결합도, 연산도 할 수 없다. 연산을 수행하기 위해서는 옵셔널 바인딩이 필요하다.


옵셔널 추출

옵셔널 타입 변수는 옵셔널이라는 포장지에 싸여 있어, 일반 변수와 그 어떠한 연산도 수행할 수 없다.
연산을 하기 위해서는 옵셔널의 값을 옵셔널이 아닌 값으로 추출 하는 과정을 거쳐야 한다.

옵셔널에서 값을 추출하는 방법은 여러가지가 존재한다.

  • 명시적 추출
    • 강제 추출
    • 비강제 추출
  • 묵시적 추출
    • 컴파일러에 의한 자동 추출
    • 옵셔널의 묵시적 추출

강제 추출

옵셔널의 강제 추출 방식은 여러 방식 중에서 가장 간단하지만 가장 위험한 방법이다. 옵셔널의 값을 강제 추출하기 위해서는 옵셔널 타입 변수 뒤에 느낌표(!)를 붙이면 된다.

var number: Int? = 3

print(number)
print(number!)
Optional(3)
3

결과를 보면 변수 뒤에 느낌표를 붙이니 감싸고 있던 옵셔널 포장지가 사라진 것을 알 수 있다.
강제 추출 방법은 간단하게 값을 추출할 수 있지만, 만약 변수가 nil인 경우에는 추출할 수 있는 값이 없기 때문에 런타임 에러가 발생한다. 이러한 이유로 잘 사용되지 않는다.

옵셔널 바인딩(비강제 추출)

옵셔널의 값을 더욱 안전하게 추출하고 싶다면, 옵셔널 바인딩 방식을 사용한다. 다른 언어에서 if문을 사용해 변수가 null인지를 먼저 확인하는 방식과 유사하다.

if let result = number {
	print(result)
} else {
	print("number is nil")
}
3

if문의 조건부에서 임시 변수를 할당하여 사용하는 방식이다. if let 이나 if var 를 사용해 만약 옵셔널에 값이 있다면 값을 추출하여 임시 변수에 할당한다.
할당한 변수는 if문의 블록 내에서만 사용할 수 있다. 만약 값이 없다면 else 문을 수행한다.

또한 guard 문을 사용해 옵셔널 바인딩을 할 수 있다.

func test() {
	let number: Int? = 5
    guard let result = number else {return}
    print(result)
}

test()
5

if문을 사용하여 옵셔널 바인딩을 수행하면 if문 내에서만 값을 추출한 변수/상수를 사용할 수 있다. 하지만 guard문을 사용한다면, guard문의 다음 줄부터 함수 끝까지 추출한 변수/상수를 사용할 수 있다.

guard문은 간단하게 조건이 true일 때만 guard문을 수행하고, false일 때는 else 구문을 수행한다. 자세한 내용은 다른 포스팅에서 다루겠다.

컴파일러에 의한 자동 추출

옵셔널을 연산자를 사용해 다른 변수와 비교하면, 컴파일러가 자동으로 옵셔널의 값을 추출해주는 방식이다.

let value: Int? = 6

if value == 6 {
	print("value = 6")
} else {
	print("value != 6")
}
value = 6

옵셔널의 묵시적 추출

묵시적 추출은 옵셔널 타입이지만 옵셔널의 값을 사용할 때는 자동으로 값을 추출해준다. 옵셔널과 달리, 묵시적 추출은 데이터 타입 뒤에 느낌표(!)를 붙여 표현한다.

let string = "12"
var stringToInt: Int! = Int(string)
print(stringToInt + 1)
13

느낌표가 붙어있는 stringToInt 변수는 사용할 때 옵셔널의 값을 묵시적으로 추출하여 일반 변수처럼 자유롭게 사용할 수 있다.

편리해 보이지만, 강제 추출과 동일하게 nil이 할당되어 있을 때 접근을 시도하면 런타임 에러가 발생한다. 때문에 스위프트는 옵셔널 바인딩이나 뒤에서 배울 옵셔널 체이닝 방식을 권장한다.

💡 참고

var stringToInt: Int! = Int(string)

위 코드에서 Int() 함수는 문자열이나 문자 타입의 변수를 정수로 변환해준다. 이때 문자열이 숫자가 아닌 한글이나 영어로 되어있을 때는 정수로 변환할 수 없기 때문에 값이 존재하지 않는다.

즉, Int() 함수에서 반환된 값이 정수일 수도, nil일 수도 있다. 때문에 일반 변수가 아닌, 옵셔널을 사용해야 한다.

profile
나 애기 개발자 👶🏻

0개의 댓글