Search

애플 공식 문서 보면서 공부하는 SwiftUI 1편

예전부터 공식 문서를 보면서 공부해보고 싶은 생각이 많았는데요. 막상 공식 문서에 들어가면 영어로 빼곡히 적혀 있어 거부감이 들었어요..그래도 저의 로망이자 목표는 외국에서 일해보는 것입니다..!ㅋㅋㅋ미라클은 하기 싫은 데에서 일어난다는 말이 있죠. 그래서 이제부터라도 영어 글을 읽는 것에 익숙해지기 위해서 SwiftUI는 공식 문서로 공부해보기로 결심했어요. 또, 최근에 외국인과 SwiftUI 스터디를 시작했는데 기본적으로 영어를 사용해서 제가 설명하고 싶은 부분이 있어도 설명이 안되니 정말 답답하더군요. 개발은 커뮤니케이션이 중요한 만큼 영어를 공부해야겠다고 결심했습니다..
1편은 SwiftUI 튜토리얼에 있는 섹션 1부터 섹션 6까지 다룰 거예요. 누구나 이해할 수 있도록 최대한 쉽게 풀어서 정리하고자 합니다! 혹시나 틀린 부분이 있다면 따끔하게 지적부탁드려요!(지적 대환영 )

목차

SwiftUI 생성 방법 및 코드 구조

Xcode를 누르면 아래 사진과 같이 화면이 뜰거예요. Create New Project를 선택하세요.
프로젝트가 생성되면 iOS - App - Next 순서로 선택하세요.
Product Name에는 원하는 이름이나 앱 목적에 맞는 이름을 적으세요.
1.
Interface에는 SwiftUI를 선택하세요.
2.
Next 버튼을 눌러 프로젝트를 생성하세요.
다음 화면이 뜨면 성공입니다!
사진 좌측을 보시면 빨간색 영역으로 표시되어 있는 부분을 네비게이터라고 해요. 그중 프로젝트 네비게이터가 선택되어 있는 모습이에요. **'LandmarksApp.swift'**를 선택해 이동해볼게요.
import SwiftUI @main struct LandmarksApp: App { var body: some Scene { WindowGroup { ContentView() } } }
Swift
복사
다시 ContentView.swift로 이동해볼게요. 코드와 미리보기(Preview)를 지원해요. 만약 미리보기가 보여지지 않는다면 다음과 같이 설정해주세요.
빨간색 화살표가 가리키는 버튼 클릭 - Canvas 선택
위와 같이 설정을 완료하시면 아마 미리보기가 정상적으로 나타날거예요.
미리보기는 코드를 수정할 때마다 자동으로 화면을 업데이트 시켜주기 때문에 시뮬레이터를 실행하지 않아도 미리볼 수 있어서 엄청 편한 기능입니다:)
다음은 프로젝트를 생성했을 때 기본적으로 제공되는 코드입니다.
import SwiftUI struct ContentView: View { var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .padding() } } #Preview { ContentView() }
Swift
복사
VStack에 대해 간단히 설명하자면, 앞에 'V'는 ‘Vertical(수직)’을 의미하고 'Stack'은 ‘쌓다’라는 의미를 가지고 있어요.
즉, 아래 그림처럼 수직으로 쌓는 것을 뜻합니다. 중학생 때 재밌게 했던 게임인데요. 'VStack'을 잘 표현하고 있는 것 같아 예시 사진으로 가져와봤습니다. 다만, 게임에서는 아래에서부터 위로 쌓이는 형태이지만 코드는 위에서부터 아래로 순차적으로 실행되기 때문에 위에서부터 아래로 쌓이겠죠? 헷갈리지 않도록 주의해주세요.
다시 코드로 넘어가볼게요. 이제 아래 코드가 어떤 뷰를 그릴지 예상되나요?
import SwiftUI struct ContentView: View { var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .padding() } } #Preview { ContentView() }
Swift
복사
위에 이미지를 먼저 쌓고 바로 아래에 텍스트가 쌓이게 돼요.
그렇다면 HStack은 어떤 역할을 할 것 같나요? 앞에 있는 'V'만 'H'로 수정해볼게요. 화면이 어떻게 바뀌었나요?
VStack과는 달리 수평으로 배치된 모습을 볼 수 있어요.
HStack의 'H'는 'Horizontal(수평)'을 의미해요. 코드를 입력한 순서대로 왼쪽부터 수평 형태로 쌓이게 되는 것이죠.
다음으로, imageScale에 대해 이야기해볼게요. imageScale의 속성으로는 'small', 'medium', 'large'가 있어요. 공식 문서를 보면 이들은 상대적 크기 중 하나에 따라 뷰 안의 이미지 크기를 조정한다고 정의되어 있어요. 즉, 이미지를 픽셀 단위로 직접 설정하는 것이 아닌, 주변 환경이나 다른 요소들의 크기에 따라 변화하는 특징을 가지고 있어요.
.imageScale(.small)
.imageScale(.large)
마지막으로 padding에 대해 알아볼게요. 'padding'은 프론트를 조금이라도 해보신 분이라면 익숙한 단어일겁니다. padding을 그림으로 표현하자면 다음과 같습니다.
?
제가 그림을 못 그려서,,부족한 그림 실력이지만 조금이나마 이해가 되길 바래요.
padding을 저희가 겨울에 입는 패딩으로 예시를 들어봤어요. padding은 사람의 몸을 기준으로 빨간색 거리라고 생각하시면 이해가 되실거예요. SwiftUI에는 없지만 UIKit에는 있는 'margin'과 어떤 차이가 있을까요? 또 한 번 부족한 그림 실력으로 설명드리겠습니다.
네.
'margin'은 패딩의 테두리를 기준으로 떨어져있는 파란색 거리를 뜻해요.(식사 중이셨다면 죄송합니다,,)
코드를 보면 padding()이 이미 적용되어 있어요. 하지만 미리보기에는 적용된건지 안된건지 식별할 수 없어요. 이 친구가 잘 적용됐는지 배경색을 넣어 확인해볼게요.
초록색 패딩을 입혀봤습니다. 근데 지금 입기엔 날씨가 더운 것 같아요. 패딩을 벗어볼까요?
이제 차이가 확실히 보이시나요?

라이브 모드 VS 셀렉터블 모드??

라이브 모드는 미리보기가 실시간으로 자동 업데이트 되는 것은 알고 있는데 선택 가능한 모드는 뭐지??
Selectable mode라 부르기로 했어요. 이전에는 코드를 직접 입력해서 뷰를 커스텀했는데 버튼 클릭으로만 뷰를 커스텀하는 방법도 있어요.
두 번째 버튼을 선택하면 미리보기가 이전과는 조금 바뀐 모습을 볼 수 있어요.
여기서 커스텀할 뷰를 눌러 선택하고
command + control + 좌클릭을 하면
이런 화면이 표시될 거예요. Show SwiftUI Inspector...를 클릭해볼게요.
UIKit
SwiftUI
스토리보드로 개발할 때 지원하는 기능보다 매우 한정적인 기능을 지원하네요.
다시 본론으로 돌아가서, SwiftUI Inspector에는 Text를 바로 수정할 수 있는 입력 필드가 있고 Accessibility가 있네요. Accessibility는 Apple에서 장애인을 포함한 모든 사람에게 '손 쉬운 사용'을 지원해요. 그 중, 'VoiceOver'라는 기능이 있는데 이는 시각 장애인을 위해 텍스트를 음성으로 변환시켜줘요. 아이폰 설정에 들어가면 '손 쉬운 사용'이라고 한 번쯤은 보셨을거예요. Accessibility는 이번 시리즈가 끝나면 더 자세히 다루어 보도록 하겠습니다.
이번에는 Font만 수정해볼거예요. Swift 앱 개발 경험이 많이 없다면 직접 하나하나 어떻게 동작하는지 확인해보시는걸 추천드려요.
Font 속성에는 여러 글씨를 지원하는데 'Title'을 선택하면 코드가 자동으로 추가되면서 미리보기가 업데이트 되는 모습을 볼 수 있어요.
글씨 색상도 변경해볼게요.
위와 같은 방법 말고도 코드를 클릭하여 수정하는 방법도 있어요. 코드에 마우스를 올리고
control + 좌클릭 또는 우클릭을 하면 아래와 같이 창이 떠요.
동일한 방식으로 수정하실 수 있습니다! 커스텀이 끝났다면 미리보기의
'셀렉터블 모드'를 다시 '라이브 모드'로
전환해요.

스택을 이용하여 뷰 구성하기

'SwiftUI 생성 방법 및 코드 구조'에서 스택에 대해 간단히 배웠어요. 이번에는 그 스택이 가지고 있는 속성들과 여러 스택을 혼합하여 뷰를 구성하는 방법을 알아볼거예요.
먼저, 기존에 있는 코드는 아래와 같이 지워보세요.
import SwiftUI struct ContentView: View { var body: some View { } } #Preview { ContentView() }
Swift
복사
지웠다면 command + shift + L 또는 +(Show Library) 버튼을 누르면
이런 창이 뜰거예요!
검색창에 text를 입력하고 Text를 끌어서 코드 블럭 안에 넣어보세요.
동일한 방식으로 두 개의 Text를 배치시키고 코드를 아래와 같이 작성해보세요.
import SwiftUI struct ContentView: View { var body: some View { Text("Turtle Rock") .font(.title) Text("Joshua Tree National Park") } }
Swift
복사
이제 이 두 개의 TextVStack으로 감싸줄거예요. 코드로 작성하셔도 되지만 더 편리한 방법이 있어요.
아까 배운대로 코드에 마우스를 올리고 control + 좌클릭 또는 우클릭을 한 뒤, 이번에는 아래와 같이 Embed in VStack을 클릭해볼게요.
두 개 이상의 뷰에 대해서는 Embed in을 한꺼번에 할 수 없나봅니다,,하지만 아래와 같은 경우는 하나의 뷰(VStack)로 묶여있기 때문에 가능하겠죠!
다음으로 Text("Joshua Tree National Park").font(.subheadline)를 추가해줄게요.
이제 VStack의 속성을 사용해볼겁니다!
VStack 옆에 (를 한 번 입력하면 다음과 같은 속성들이 표시될 거예요.
저희는 이번에 alignment 속성만 사용할 거예요!
import SwiftUI struct ContentView: View { var body: some View { VStack(alignment: .leading) { Text("Turtle Rock") .font(.title) Text("Joshua Tree National Park") .font(.subheadline) } } }
Swift
복사
코드를 적용하니 텍스트가 어떻게 바뀌었나요?
VStack
VStack(alignment: .leading)
두 개의 텍스트가 왼쪽으로 정렬된 모습을 볼 수 있어요.
이번에는 두 번째 텍스트를 선택하여 Embed in HStack을 하고
그 아래에 Text("California").font(.subheadline)을 추가해볼게요.
import SwiftUI struct ContentView: View { var body: some View { VStack(alignment: .leading) { Text("Turtle Rock") .font(.title) HStack { Text("Joshua Tree National Park") .font(.subheadline) Text("California") .font(.subheadline) } } } }
Swift
복사
다음과 같은 결과가 나와요. 두 개의 텍스트가 붙어있으니 상당히 보기 싫군요.
HStack에 있는 두 개의 텍스트를 띄어 놓기 위해서 어떻게 하실건가요?
아까 배운 padding()을 넣어도 되지만 Spacer()라는 친구도 있어요. Spacer()는 스택 내에서 뷰들 사이의 공간을 자동으로 조정해줘요. 만약 스택 내에 Spacer()가 하나만 있으면 그 Spacer()가 나머지 공간을 모두 차지해요. 그리고 이 친구는 다양한 화면 크기와 기기에서 뷰들이 균등하게 배치될 수 있도록 도와주는 아주 유연한 친구예요.
import SwiftUI struct ContentView: View { var body: some View { VStack(alignment: .leading) { Text("Turtle Rock") .font(.title) HStack { Text("Joshua Tree National Park") .font(.subheadline) Spacer() Text("California") .font(.subheadline) } } } }
Swift
복사
이렇게 Spacer()가 두 개의 텍스트를 끝까지 밀어내고 가능한 많은 공간을 차지한 모습이에요. 마지막으로 padding()을 넣어 양쪽에 여백을 주고 마무리 하겠읍니다.
import SwiftUI struct ContentView: View { var body: some View { VStack(alignment: .leading) { Text("Turtle Rock") .font(.title) HStack { Text("Joshua Tree National Park") .font(.subheadline) Spacer() Text("California") .font(.subheadline) } } .padding() } } #Preview { ContentView() }
Swift
복사

이미지 뷰를 커스텀하는 방법

애플 튜토리얼에서 제공하는 이미지를 다운받아 프로젝트에 적용시킵니다.
그런 다음, 'command + N - SwiftUI 선택 - Next' 버튼을 눌러 다음으로 진행합니다.
파일명을 CircleImage로 작성하고 Create를 눌러 파일을 생성해주세요.
파일이 생성되면 기존의 Text는 지우고 Image("turtlerock")을 입력해보세요! 만약 이미지가 보이지 않는다며 추가한 이미지명과 동일한지 체크해보세요!
이미지가 정상적으로 보인다면 저희는 이 이미지를 동그랗게 만들거예요:) UIKit에서는 이미지를 동그랗게 만들기 위해서 다음과 같이 작성해야 됐어요.
let imageView = UIImageView(image: UIImage(named: "turtlerock")) imageView.clipsToBounds = true imageView.layer.cornerRadius = imageView.frame.width / 2
Swift
복사
하지만 SwiftUI에서는 어떻게 할까요?
Image("turtlerock") .clipShape(Circle())
Swift
복사
하,,너무 간단하죠?
여기서 인스타그램에 스토리처럼 이미지에 테두리를 만들어볼게요. 먼저, .overlay { Circle() }를 추가해주세요. 그러면 이미지가 검정색으로 전부 덮혀지는 것을 볼 수 있어요.
Circle() 뒤에 점(.)을 찍어보면 아래와 같이 뜰거예요.
초록색으로 포커싱되어 있는 것을 선택하고 다음과 같이 코드를 작성하세요.
import SwiftUI struct CircleImage: View { var body: some View { Image("turtlerock") .clipShape(Circle()) .overlay { Circle().stroke(.white, lineWidth: 4.0) } } }
Swift
복사
미리보기를 보면 테두리가 흰색이어서 보이지 않을거예요. overlay에 그림자 속성을 추가해서 테두리가 보이도록 설정해볼게요.
import SwiftUI struct CircleImage: View { var body: some View { Image("turtlerock") .clipShape(Circle()) .overlay { Circle().stroke(.white, lineWidth: 4.0) } .shadow(radius: 7) } }
Swift
복사
이제 테두리와 그림자가 잘 표시되나요?

MapKit 사용해보기

command + N을 눌러 MapView.swift라는 새로운 SwiftUI 파일을 생성합니다. 그런 다음, MapKitimport를 해주고 지도에 표시할 해당 지역의 좌표를 담을 비공개 변수를 만들거예요.
import SwiftUI import MapKit struct MapView: View { var body: some View { Text("Hello, World!") } private var region: MKCoordinateRegion { MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2) ) } } #Preview { MapView() }
Swift
복사
이제 body 내에 기존 코드는 지우고 Map이라고 작성하면 여러 속성이 뜨는 것을 볼 수 있어요.
저희는 포커싱된 코드를 사용할 거예요. 선택하면
MapCameraPosition이 등장하네요. 이름만 봐도 맵이 표시할 화면을 보여주는구나,,라고 대충 짐작할 수 있어요. 한 번 점(.)을 찍어 어떤 것들이 있는지 살펴볼게요.
오,,뭐가 많네요. 현재 필요한건 region(_ region:)요 녀석입니다. 선택하고 아까 작성한 비공개 변수를 넣어볼게요.
비공개 변수 내에 설정한 좌표가 Map을 통해 표시하고 있어요.

여러 뷰들을 하나로 합치기

드디어 1편의 마지막 섹션입니다..! 제목을 봤을 때 어떤 작업을 할지 짐작이 가시나요?
저희가 지금까지 만들었던 뷰(ContentView, CircleImage, MapView)들을 하나의 파일에 모두 넣어 하나의 뷰가 되도록 합칠거예요!
다시 ContentView로 이동해서 아래와 같이 VStack을 추가해볼게요.
import SwiftUI struct ContentView: View { var body: some View { VStack { // 추가 VStack(alignment: .leading) { Text("Turtle Rock") .font(.title) HStack { Text("Joshua Tree National Park") .font(.subheadline) Spacer() Text("California") .font(.subheadline) } } .padding() } // 추가 } }
Swift
복사
그런 다음, 위에서부터 순서대로 MapView()CircleImage를 작성해보세요!
예상한 대로 뷰가 나왔나요?
좋아요, 이제 CircleImage의 위치를 조정해볼게요.
CircleImage() .offset(y: -130) .padding(.bottom, -130)
Swift
복사
offset(y:)은 y축으로 얼마만큼 옮길 것인지 설정하는 코드예요. 즉, y축(수직 방향 위쪽)으로 130 포인트만큼 이동해요.
그리고 지금까지 padding()을 써왔지만 여기에는 뭔가 추가되어 있어요. 먼저, .bottom은 이미지 뷰의 바닥 부분을 뜻하고 -130은 수치를 나타내요. 즉, 이미지 뷰의 바닥을 얼마만큼의 간격을 줄 것인지 설정하는 코드예요. 여기선 -130만큼의 간격을 주니 아래 'Turtle Rock'과 가까워지겠죠?
그 다음, 아래와 같이 VStack 하단에 Spacer()를 추가하여 뷰를 화면 상단으로 밀어볼게요.
import SwiftUI struct ContentView: View { var body: some View { VStack { MapView() .frame(height: 300) CircleImage() .offset(y: -130) .padding(.bottom, -130) VStack(alignment: .leading) { Text("Turtle Rock") .font(.title) HStack { Text("Joshua Tree National Park") .font(.subheadline) Spacer() Text("California") .font(.subheadline) } } .padding() Spacer() // 추가 } } }
Swift
복사
조금씩 모양을 잡아가는게 보이네요. 거의 다와가요..!
다음은, 랜드마크의 상세 설명을 보여줄 뷰를 그릴거예요.
import SwiftUI struct ContentView: View { var body: some View { VStack { MapView() .frame(height: 300) CircleImage() .offset(y: -130) .padding(.bottom, -130) VStack(alignment: .leading) { Text("Turtle Rock") .font(.title) HStack { Text("Joshua Tree National Park") .font(.subheadline) Spacer() Text("California") .font(.subheadline) } Divider() // 추가 Text("About Turtle Rock") // 추가 .font(.title2) // 추가 Text("Descriptive text goes here.") // 추가 } .padding() Spacer() } } }
Swift
복사
아직 뭔가 부족해보이나요? 아직 부족해보인다면 당신은 프론트엔드의 자격을 갖췄군요ㅋㅋㅋ
Joshua Tree National ParkCaliforniaTurtle Rock의 세부 정보를 제공해요. 이 두 개의 보조 텍스트의 색상을 다르게 설정해서 ‘Turtle Rock’의 보조적인 요소로 강조해보겠습니다.
둘 다 똑같은 속성으로 설정할 것이기 때문에 아래와 같이 굳이 하나하나 코드를 작성할 필요없어요. 중복되는 코드는 코드의 가독성을 떨어뜨립니다.
HStack { Text("Joshua Tree National Park") .font(.subheadline) .foregroundStyle(.secondary) Spacer() Text("California") .font(.subheadline) .foregroundStyle(.secondary) }
Swift
복사
HStack에 적용시킬 코드를 작성하여 두 텍스트에 한꺼번에 속성을 적용시킵니다!
import SwiftUI struct ContentView: View { var body: some View { VStack { MapView() .frame(height: 300) CircleImage() .offset(y: -130) .padding(.bottom, -130) VStack(alignment: .leading) { Text("Turtle Rock") .font(.title) HStack { Text("Joshua Tree National Park") Spacer() Text("California") } .font(.subheadline) // 이동 .foregroundStyle(.secondary) // 추가 Divider() Text("About Turtle Rock") .font(.title2) Text("Descriptive text goes here.") } .padding() Spacer() } } }
Swift
복사
어떤가요? 확실히 보조 텍스트에 대해 색상을 다르게 하니 느낌이 활 달라졌죠? 사용자의 시선을 랜드마크명에 더욱 강조하여 이름을 이전보다 더 확실히 기억할 수 있을 것 같아요.

마치며

긴 글 봐주셔서 감사합니다. 오늘은 SwiftUI를 생성하는 방법과 뷰를 구성하는 다양한 방법들에 대해 알아보았습니다.
아직 글 쓰는게 익숙하지 않아 여기까지 작성하는데 벌써 8시간 가까이 되었네요,,읽는데 5분도 안걸릴거 같은데 말이죠 허허 그래도 재밌고 값진 시간이었습니다. 지금은 글 쓰는 속도가 느려도 꾸준히 써가면서 고인물(?)이 될 때까지 짧고도 긴 여정을 하고자 합니다!