이번 글에서는 사용자가 버튼을 누르면 화면의 내용이 바뀌는 "로또 번호 생성기" 앱을 만들어 보겠습니다.
사용자의 행동에 앱이 반응하도록 만드는 인터랙션(Interaction)을 구현하는 과정을 통해 SwiftUI의 중요한 개념인 @State를 배워보겠습니다.
@State 는 값이 바뀌면 화면을 자동으로 업데이트해주는 기능을 합니다.
ContentView 파일의 모든 코드를 지우고 아래의 코드를 작성해주세요. 코드를 그냥 복사/붙여넣기 하기 보다는, 직접 한 줄씩 타이핑하면서 코드의 의미를 생각해 보세요. 자동완성 기능의 도움을 받는 것은 좋습니다.
import SwiftUI
struct ContentView: View {
// 1. 데이터를 저장할 '상태' 변수를 선언합니다.
// @State 키워드가 붙었기 때문에, 이 변수의 값이 바뀌면 화면이 자동으로 새로고침됩니다.
@State private var generatedNumbers: [Int] = []
var body: some View {
VStack(spacing: 20) { // 요소들 사이에 20만큼 간격을 줍니다.
Text("로또 번호 생성기")
.font(.largeTitle) // 글씨를 큰 제목 스타일로
.fontWeight(.bold) // 글씨를 굵게
// 2. State 변수에 담긴 숫자를 화면에 보여줍니다.
// 처음에는 배열이 비어있으므로 안내 문구가 보입니다.
if generatedNumbers.isEmpty {
Text("버튼을 눌러 번호를 생성하세요")
.foregroundStyle(.gray)
} else {
// 숫자를 보기 좋게 텍스트로 변환하여 보여줍니다.
Text(generatedNumbers.map { String($0) }.joined(separator: ", "))
.font(.title)
}
// 3. 버튼을 만듭니다.
// action 부분에 버튼을 눌렀을 때 실행될 코드를 넣습니다.
Button(action: {
// 여기에 번호 생성 로직을 넣습니다.
generateLottoNumbers()
}) {
// label 부분에 버튼이 어떻게 보일지 디자인합니다.
Text("행운의 번호 뽑기!")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
.padding()
}
// 로또 번호를 생성하는 함수(기능)
func generateLottoNumbers() {
var newNumbers = Set<Int>() // 중복되지 않는 숫자를 뽑기 위해 Set을 사용합니다.
while newNumbers.count < 6 {
let randomNumber = Int.random(in: 1...45)
newNumbers.insert(randomNumber)
}
// Set을 정렬된 배열로 바꿔서 @State 변수에 저장합니다.
// 바로 이 코드가 실행되는 순간, 화면이 바뀝니다!
generatedNumbers = Array(newNumbers).sorted()
}
}
// 아래 코드가 있어야 Preview 패널이 동작합니다.
#Preview {
ContentView()
}
이해하기 쉽게 설명해보자면 "이 generatedNumbers라는 변수를 잘 지켜봐 줘. 만약 이 변수의 내용이 바뀌면, 이 변수를 사용하고 있는 화면(UI) 부분을 전부 자동으로 새로 그려줘!" 라고 말할 수 있습니다.
우리가 일일이 "숫자가 바뀌었으니 글자를 지우고 새로 써라"라고 명령할 필요가 없습니다. 그냥 @State 변수의 값을 바꾸기만 하면 화면은 저절로 바뀝니다.
1. Button(action: { ... }) { ... }
Button은 크게 두 부분으로 구성됩니다.
- action: { ... } 는 사용자가 버튼을 눌렀을 때 실행될 코드를 넣는 곳입니다. 우리는 여기에 generateLottoNumbers() 함수를 호출하도록 했습니다.
- label: { ... } 는 (위 코드에서 action 뒤에 바로 따라오는 부분): 버튼이 어떻게 보일지 디자인하는 곳입니다. 우리는 파란 배경에 흰 글씨를 가진 버튼을 만들었습니다.
'label' 매개변수를 생략한 이유
함수의 마지막 인자가 클로저일 경우, 인자값 형식으로 작성하는대신 함수 의 뒤에 꼬리처럼 붙일 수 있는 Swift 문법입니다. 이를 트레일링 클로저(trailing closure)라고 부릅니다.
클로저란, {}로 둘러싸인 코드 블록으로 변수나 상수를 기억하고 함수가 끝난 후에도 그 값에 접근할 수 있는 익명 함수 입니다. 클로저는 함수의 인자로 전달되어 비동기 처리나 콜백 등에서 활용합니다.
만약, 트레일링 클로저 문법을 사용하지 않고 일반적인 형태로 매개변수 이름을 표시한다면 다음처럼 작성할 수 있습니다. 아래 코드는 동일한 동작을 수행합니다.
Button(action: {
generateLottoNumbers()
}, label: { // <-- 'label' 매개변수를 명확하게 표시
Text("행운의 번호 뽑기!")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
})
2. generateLottoNumbers() 함수
이 함수는 1부터 45 사이의 중복되지 않는 숫자 6개를 뽑아서 정렬한 뒤, @State 변수인 generatedNumbers에 그 결과를 저장(=)하는 역할을 합니다.
함수가 실행되면 generatedNumbers의 값이 새로운 로또 번호로 바뀌게 됩니다. 이것이 @State의 역할입니다. SwiftUI는 이 상태 변화를 감지하고, generatedNumbers를 사용하고 있는(화면에 로또 번호를 표시하는) Text 부분을 자동으로 새로 그려주는 것입니다.
로또 번호 생성기 완성!
Xcode 오른쪽의 프리뷰 캔버스에서 버튼을 직접 눌러보세요. 번호가 나타나고 계속 바뀌는 것을 볼 수 있습니다.
시뮬레이터(Command + R)를 실행해서 실제 아이폰에서 동작하는 것처럼 테스트해 볼 수 있습니다.


이제 우리는 @State을 통해 상태를 저장하고 사용자와 상호작용하는 버튼을 만들 수 있게 되었습니다. 간단하지만 실제 동작하는 앱을 만들어본 셈이죠!
점점 더 재밌어지네요. 다음 글에서는 여러 화면을 가진 앱을 만들어 보겠습니다.
'SwiftUI 독학으로 기본기 익히기' 카테고리의 다른 글
| 06. 할 일 완료 처리 기능 구현하기 (1) | 2025.10.31 |
|---|---|
| 05. 새로운 할 일 추가 기능 구현 (0) | 2025.10.30 |
| 04. 투두리스트 화면과 내비게이션 구현하기 (0) | 2025.10.29 |
| 02. Text 수정하고 Image 추가하기 (0) | 2025.10.28 |
| 01. Xcode로 첫 SwiftUI 프로젝트 만들기 (0) | 2025.10.27 |