DTO 와 VO 의 차이

API를 호출하고자 하는데 데이터 정의를 어떻게 해야 할지 고민이 되어 찾아보다가 관련 개념을 정리

우선, DTO 와 VO 둘다, 데이터를 정의하는데 의미가 있다 그렇다면 차이가 뭘까?

간단히 설명하면 getter 와 setter를 모두 포함하는지, getter만 포함하는지에 대한 차이가 있다.

즉, 읽기 전용 데이터인지? 적절한 수정을 통해 가공해야 하는 데이터인지에 대한 차이이다.

DTO : Data Transfer Object

⇒ getter 와 setter를 모두 가지고 있는 객체를 의미

Swift의 문법중 ‘연산 프로퍼티' 의 개념과 동일하다, 데이터를 가져와 적절히 가공하고 사용해야 할때 사용되는 개념

  • 데이터 자체를 사용하는것이 아닌, 가공 또는 한곳에 모아 사용해야 할때 사용
  • 데이터를 어딘가에 ( 외부로 ) 전송해야 할때 유용한 객체
class Greeting {
    private var korean = "안녕하세요"
    private var english = "Hello"
    
    func getKorean() -> String {
        return korean
    }
    
    func setKorean(_ korean: String) {
        self.korean = korean
    }
    
    func getEnglish() -> String {
        return english
    }
    
    func setEnglish(_ english: String) {
        self.english = english
    }
}

VO : Value Object

  • 데이터 그 자체로 의미 있는 값을 가지는것
  • DTO와 달리, getter만 가질수 있으므로 읽기 전용 객체가 된다
open class UIColor : NSObject, NSSecureCoding, NSCopying {

    //...
    open class var black: UIColor { get } // 0.0 white
    open class var darkGray: UIColor { get } // 0.333 white
    open class var lightGray: UIColor { get } // 0.667 white
    open class var white: UIColor { get } // 1.0 white
    open class var gray: UIColor { get } // 0.5 white
    open class var red: UIColor { get } // 1.0, 0.0, 0.0 RGB
    open class var green: UIColor { get } // 0.0, 1.0, 0.0 RGB
    open class var blue: UIColor { get } // 0.0, 0.0, 1.0 RGB
    open class var cyan: UIColor { get } // 0.0, 1.0, 1.0 RGB
    open class var yellow: UIColor { get } // 1.0, 1.0, 0.0 RGB
    open class var magenta: UIColor { get } // 1.0, 0.0, 1.0 RGB
    open class var orange: UIColor { get } // 1.0, 0.5, 0.0 RGB
    open class var purple: UIColor { get } // 0.5, 0.0, 0.5 RGB
    open class var brown: UIColor { get } // 0.6, 0.4, 0.2 RGB
    open class var clear: UIColor { get } // 0.0 white, 0.0 alpha
    //...
}
  • 대표적으로 UIColor 가 VO 이다.
  • color 를 사용할때 .green 을 사용하면, green 색상을 바로 읽어 올수 있다.

참고 : 링크

개요 :

A control that displays one or more buttons in a tab bar for selecting between different subtasks, views, or modes in an app.

→ 앱의 각자 다른 뷰 또는 모드 중 하나를 선택하기 위해 탭 표시줄에 하나 이상의 버튼을 표시하는 컨트롤.

정의

@MainActor class UITabBar : UIView

→ UIView를 상속받는 클래스로 정의되어 있다

StoryBoard 사용하여 TabBar 구성하기

  1. TabBar 로 표현할 View를 선택한후, 상단의 Editor - Embed In - Tab Bar Controller 를 선택한다

2. TabBar 구성 확인 : TabBar가 연결됨을 확인한다 ( segue 에 대해서 더 깊은 공부 필요 )

3. 연결된 화면에서 Tab을 선택하여, 인스펙터 창에서 Tab Item을 수정한다 ( Title 에는 Tab Item의 이름을, Image 에서 Tab Item의 기호를 수정한다 → HIG를 참고하여 SF Symbol 활용 )

4. [ Shift + Command + L ] 단축키를 이용해 컴포넌트 창을 열어 새로 추가할 ViewController를 추가

5. Tab Bar Controller 를 선택한후, control 키를 누른 상태로 방금 생성한 뷰 컨트롤러랑 연결한다

→ 이때, Relationship Segue view controllers 를 선택하면 탭바와 연결됨을 확인할 수 있다

6. 새로 생성한 ViewController를 UIViewController 를 상속받는 ViewController class와 연결

→ 인스펙터 창에서 class를 선택하여 지정한 ViewController와 연결한다

  • 위 순서대로 설정하고 앱을 실행하면 TabBar가 정상적으로 적용됨을 확인 할 수 있다!

 

⇒ 여기 까지, Storyboard 를 사용하여 TabBar를 적용하는 방법을 알아 봤다

CodeBase 로도 구현하는 방법이 있다, 이 방법을 쓰면 스토리보드를 사용하지 않고 오직 코드로만 구성할 수 있지만 아직은 공부하는 단계이므로 스토리보드 기반으로 적용을 한후, 시간이 남으면 프로젝트 자체를 모두 코드로만 구현해보자!

TabBar 란 무엇인가?

  • 화면 하단에 탭 표시줄이 나타나 사람들이 앱이 제공하는 정보 유형이나 기능을 이해할 수 있도록 도움
  • 각 섹션 내의 현재 내비게이션 상태를 유지하면서 앱의 최상위 섹션 사이를 빠르게 전환
  • 기본적으로 탭 표시줄은 반투명이며, 콘텐츠가 뒤에 나타날 때만 배경 자료를 사용
  • 키보드가 화면에 있을 때 탭 표시줄이 숨김

  • 사진 앱과 같이 탭바 뒤에 컨텐츠가 있으면 배경색을 보여주고, 하단으로 스크롤 하면 뒤에 비치지 않음
  • 장치 크기와 방향에 따라, 보이는 탭의 수는 총 탭 수보다 적을 수도 있다
  • 화면에 모든 탭이 표시되지 않는경우, 더보기 탭이 되어 목록에 나머지 항목을 별도의 화면으로 표시

공식 문서 : UITabBar

Tip 
TabBar 와 ToolBar는 모두 화면 하단에 나타나지만, 서로 다른 목적을 갖는다 탭 바는 한 화면에서 탭에따라 다른 뷰를 표시해 주지만, ToolBar의 경우 항목 생성, 필터링, 콘텐츠 표시등을 위한 버튼 등이 포함된다 TabBar와 ToolBar는 동시에 표시할 수 없다.

지켜야 할점

  • 어떠한 기능을 수행하기 위해 사용하지 말고, 오직 탭을 전환하는 용도로만 사용할것
  • View의 요소에 어떠한 기능을 제공해야 하는 경우 Toolbar를 사용할것.
  • 적당한 수의 탭을 사용할것, iPhone의 경우 3~5개 , iPad의 경우 조금더 많이 사용할것

⇒ 너무 많거나, 너무 적으면 오히려 디자인적 요소가 깨짐

  • iPadOS 앱에서는 TabBar대신 SideBar를 사용하도록 권장
  • Modal View를 제외한 다른 View에서 언제 어디서나 유저가 바로 이동할수 있도록 TabBar를 숨기지 말것
  • 해당 탭의 컨텐츠를 이용할수 없는 경우에도 Tab을 숨기지 말고 이용할 수 없는 설명을 보여줄것
  • 현재 선택된 탭에서 다른 탭의 View에 영향을 끼치지 말아야 함

→ 예를 들어, 분할된 view의 왼쪽에 부분에 있는 tab을 선택했다면, 분할된 view의 오른쪽에 갑자기 변경되지 않아야 하고 Popover에서 tab을 선택했다면 popup 뒤의 뷰가 변경되지 않아야 한다

  • 소통을 위해서 badge를 사용할것 ( 알림이 있다면 뱃지를 사용하여 표시 ) ex) 카카오톡 채팅 탭

→ 공식 문서 : UITabBarItem

  • SF 기호를 사용하여 확장 가능하고 시각적으로 일관된 TabBar 항목을 제공하는 것을 고려할 것

  • Custom Tab Bar glyphs를 만들어야 하는 경우, 탭 바가 어떠한 환경에서든 일관성있게 보이도록 글리프를 두 가지 크기로 만들것   ( Regular, Compact )

 Glyphs 문서

 

 

본 내용은 스윗한 SwiftUI 책에 있는 예제를 공부한 내용입니다.
해당 예제들에 대한 저작권은 BJpublic 에 있습니다.

 

  • 이미지에 frame 속성을 사용해도 이미지 자체의 크기를 변경해 주진 않음
  • Resizable 속성을 사용하여 이미지 크기를 변경할 수 있음
HStack {
	Image("imgFile")     // 이미지 크기는 변하지 않고, 이미지를 포함함 뷰의 크기가 변함
		.frame(width: 50, height: 50)

	Image("imgFile").resizable()    // resizable() 을 사용하여 이미지 크기 자체가 변함
		.frame(width: 200, height: 50)
}

  • frame 은 View의 속성이기 때문에 resizable 과 순서가 중요함!
  • resizable() 을 사용하여 특정 영역만 늘려주는 것도 가능함
  • 단, UIKit 처럼 ResizingMode 를 생략하면 tile 이 아니라 stretch가 기본값이 됨
HStack {
            Image("swift")
            //capInset 매개 변수에 늘어날 영역 지정. resizingMode 생략 시 stretch 적용
                .resizable(capInsets: .init(top: 0, leading: 50, bottom: 0, trailing: 0))
                .frame(width: 150, height: 150)
            
            Image("swift")
                .resizable(resizingMode: .tile)
                .frame(width: 150, height: 150)
        }

 

ContentMode

UIKIt SwiftUI 설명
Scale to Fill Default 값 종횡비와 상관없이 이미지를 늘려 표현
Aspect Fit .scaledToFit() 종횡비를 유지한 채로 표현가능한 최대로 표현
Aspect Fill .scaledToFill() 종횡비를 유지한 채로 표현하며, 이미지가 짤리거나 추가적으로 더 크게 표현

 

HStack(spacing: 30){
						// 기본값
            Image("swift").resizable().frame(width: 100, height: 150)
            
						// UIKit의 Aspect Fit 적용
            Image("swift").resizable()
                .scaledToFit()
                .frame(width: 100, height: 150)

						// UIKit의 Aspect Fill 적용            
            Image("swift").resizable()
                .scaledToFill()
                .frame(width: 100, height: 150)
        }

 

AspectRatio

  • 이미지 비율을 좀더 세부적으로 조정하기 위해 사용하는 수식어
  • 모든 콘텐츠가 적용된 상황에서 추가적으로 비율조정을 하기위해 사용
// CGFloat : 너비 / 높이 를 계산한 비율을 전달
func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View

// CGSize : 너비 와 높이를 각각 설정
func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View

AspectRatio 적용한 예제

HStack(spacing: 30) {
            //scaledToFit 콘텐츠 모드를 적용한 뒤, 너비가 높이보다 1.6배 비율을 가지도록 조정
            Image("swift").resizable()
                .aspectRatio(CGSize(width: 1.6, height: 1), contentMode: .fit)
                .frame(width: 150, height: 150)
            
            // scaledToFill 콘텐츠 모드를 적용한뒤, 너비가 높이보다 0.7배의 비율을 가지도록 조정
            Image("swift").resizable()
                .aspectRatio(0.7, contentMode: .fill)
                .frame(width: 150, height: 150)
                .clipped()
        }

 

ClipShape

  • 이미지를 원하는 모양으로 조정
  • 크기 또한 조정 가능
HStack(spacing: 30) {
            // 원 모양
            Image("swift").clipShape(Circle())
            
            // 이미지 크기보다 사방으로 10씩 크기를 조정한 사각형
            Image("swift").clipShape(Rectangle().inset(by: 10))
            
            // 크기와 위치를 직접 지정한 타원
            Image("swift").clipShape(Ellipse().path(in: CGRect(x: 10, y: 10, width: 80, height: 110)))
        }

 

RanderingMode

  • template : 이미지의 불투명 영역이 가진 본래의 색을 무시하고 원하는 색으로 변경
  • original : 항상 이미지의 본래 색을 유지
  • 렌더링 모드를 생략하면 시스템이 알아서 조정을 하므로, 원치않은 결과가 나오면 렌더링 모드를 지정해야함
HStack(spacing: 30) {
            // 렌더링 모드 생략 -> 시스템이 스스로 결정
            Image("swift")
            
            // 원본 이미지 색상 유지
            Image("swift").renderingMode(.original)
            
            // template 모드 적용
            Image("swift").renderingMode(.template)
        }
        .foregroundColor(.blue) // 자식 뷰 모두에게 일괄 적용

 

SF Symbol

  • 애플에서 직접 만들고 제공하는 이미지들의 모음
  • iOS 13이상부터 사용 가능
  • 벡터 기반의 이미지로, 시스템 폰트로 크기를 조정 할 수도 있음
  • 앱에서 심볼들을 확인 할 수 있으며, systemName: 으로 간단히 사용 가능
HStack(spacing: 30){
            Image(systemName: "star.circle")
            Image(systemName: "star.circle.fill")
        }

  • ImageScale 을 적용하면 크기를 지정 할 수 있다. ( 기본값 → medium )
HStack(spacing: 30) {
            Image(systemName: "star.circle").imageScale(.small)
            Image(systemName: "star.circle")
            Image(systemName: "star.circle").imageScale(.large)
        }

  • font 속성을 이용해서 크기를 조정 할 수도 있다
HStack(spacing: 30) {
            Image(systemName: "star.circle").font(.body)
            Image(systemName: "star.circle").font(.title)
            Image(systemName: "star.circle").font(.system(size: 40))
            Image(systemName: "star.circle").imageScale(.large).font(.system(size: 40))
        }

  • weight 속성을 이용하면 굵기 표현도 가능 하다
HStack(spacing: 30) {
            Image(systemName: "star.circle").font(Font.title.weight(.black))
            Image(systemName: "star.circle").font(Font.title.weight(.semibold))
            Image(systemName: "star.circle").font(Font.title.weight(.light))
            Image(systemName: "star.circle").font(Font.title.weight(.ultraLight))
        }

 

본 내용은 스윗한 SwiftUI 책에 있는 예제를 공부한 내용입니다.
해당 예제들에 대한 저작권은 BJpublic 에 있습니다.

전체적으로 훑어보기

VStack(spacing: 30) {  // 세로 방향으로 뷰를 배열하는 컨테이너 뷰
            Text("폰트와 굵기 설정")
                .font(.title) // 폰트 설정
                .fontWeight(.black) // 폰트 굵기
            
            Text("글자색은 foreground, 배경은 background")
                .foregroundColor(.white)
                .padding()      // 텍스트 주변 여백 설정
                .background(Color.blue)
            
            Text("커스텀 폰트, 볼드체, 이탤릭체, 밑줄, 취소선")
                .font(.custom("Menlo", size: 16))
                .bold()
                .italic()
                .underline()
                .strikethrough()
            
            Text("라인 수 제한과 \\n 텍스트 정렬 기능입니다. \\n 이건 안 보입니다.")
                .lineLimit(2)
                .multilineTextAlignment(.trailing) // 다중행 문자열의 정렬 방식 지정
                .fixedSize() // 주어진 공간의 크기가 작아도 텍스트를 새략하지 않고 표현하도록 설정
            
            // 2개 이상의 텍스트를 하나로 묶어서 동시에 적용
            (Text("자간과 기준선").kerning(8) //자간
             + Text(" 조정도 쉽게 가능합니다.").baselineOffset(8)) // 기준선
            .font(.system(size: 16))

 

Text 수식어 적용의 순서의 중요성

  • Text 와 View 의 font, foregroundColor 의 정의
extention Text {
	// Text와 View 모두 있는 수식어
	public func font(_ font: Font?) -> Text
	public func foregroundColor(_ color: Color?) -> Text
	
	// Text에만 있는 수식어
	public func bold() -> Text
	public func italic() -> Text
}

extention View {
	// Text와 View 모두 있는 수식어
	public func font(_ font: Font?) -> some View
	public func foregroundColor(_ color: Color?) -> some View

	// View 에만 있는 수식어
	public func padding(
		_ edges: Edge.Set = .all,
		_ length: CGFloat? = nil
	) -> some View
}
  • Text에도, View에도 font 와 foregrountColor 가 정의되어 있다
  • 단, 반환 타입이 다름을 주시하자
  • View 에만 있거나, Text에만 있는 경우 순서가 중요하다

잘못된 순서로인한 변화 예제

Text("SwiftUi")
	.font(.title) // Text - 호출자의 타입이 Text
	.bold()       // Text
	.padding()    // View - padding 수식어 호출 이후로는 Text가 아닌 View 반환

Text("SwiftUi")
	.bold()       // Text
	.padding()    // View
	.font(.title) // View - 동일한 font 수식어를 호출해도 호출자에 따라 반환타입 다름

Text("SwiftUi")
	.padding()    // View
	.bold()       // 컴파일 오류 - View에는 bold 가 정의되어 있지 않기 때문
	.font(.title) 

Text("SwiftUi")
	.font(.title) // View
	.padding()    // View
	.bold()       // 컴파일 오류 - View에는 bold 가 정의되어 있지 않기 때문

잘못된 순서로 인한 변화 예제2

// 1번
Text("🐶🐱🐭🐹🐼").font(.largeTitle)
	.background(Color.yellow) // 배경 색상지정이 Text 크기에 따라 우선 적용
	.padding()                // 배경 색상이 적용된 Text에 padding 적용

// 2번
Text("🐶🐱🐭🐹🐼").font(.largeTitle)
	.padding()                // Text에 Padding 을 우선 적용!
	.background(Color.yellow) // padding 이 적용된 부분에 배경색상 지정

  • 1번

  • 2번

  • 보이는 것과 같이 순서에 따라 View 자체가 어떻게 그려질지 보여준다
  • 이처럼, SwiftUI에서는 수식어의 순서에 따라 오류가 날수도, 보여지는 뷰가 다를수도 있음을 유의하자

 

+ Recent posts