관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - SwiftUI] 선언형 프로그래밍 관점으로 코딩하는 방법 (명령형 vs 선언형, 선언적 코딩) 본문

iOS 접근성 (SwiftUI)

[iOS - SwiftUI] 선언형 프로그래밍 관점으로 코딩하는 방법 (명령형 vs 선언형, 선언적 코딩)

jake-kim 2024. 12. 19. 01:45

명령형 vs 선언형 코드 작성의 흐름

  • 명령형 프로그래밍
    • "어떻게" UI를 업데이트할지 명시적으로 작성
let label = UILabel()
label.text = "Hello, World!"
label.textColor = .blue
label.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
view.addSubview(label)

 

  • 선언형 프로그래밍
    • 좀 더 코드가 뷰의 형태와 닮은 형태로 작성
struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
            .foregroundColor(.blue)
            .frame(width: 200, height: 50)
    }
}

 

 

ex) 카운터 뷰 만들기

  • 선언형 프로그래밍 SwiftUI로 만들면, 뷰의 배치 위치와 동일하게 Text, Button, Button이 나열되어 있고 마치 코드가 뷰의 형태와 닮은 형태처럼 되어서 더욱 직관적인 프로그래밍이 가능
struct ContentView: View {
    @State private var count = 0

    var body: some View {
        VStack(spacing: 20) {
            Text("\(count)")
                .font(.largeTitle)
                .foregroundColor(count < 0 ? .red : .green)
                .padding()

            Button(action: {
                count += 1
            }) {
                Text("Increment")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }

            Button(action: {
                count -= 1
            }) {
                Text("Decrement")
                    .padding()
                    .background(Color.gray)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }
        }
        .padding()
    }
}
  • 반면에 명령형 프로그래밍 UIKit으로 작성하면 각 뷰의 속성 (font, addTarget)작성하는 곳과 layout을 작성하는 곳 (오토레이아웃)코드가 분리되어 있어서 직관적이지 않은 단점이 존재
import UIKit

class CounterViewController: UIViewController {
    private var count = 0
    private let countLabel = UILabel()
    private let incrementButton = UIButton(type: .system)
    private let decrementButton = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        countLabel.text = "\(count)"
        countLabel.textAlignment = .center
        countLabel.font = UIFont.systemFont(ofSize: 32)
        countLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(countLabel)

        incrementButton.setTitle("Increment", for: .normal)
        incrementButton.addTarget(self, action: #selector(incrementCount), for: .touchUpInside)
        incrementButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(incrementButton)

        decrementButton.setTitle("Decrement", for: .normal)
        decrementButton.addTarget(self, action: #selector(decrementCount), for: .touchUpInside)
        decrementButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(decrementButton)

        NSLayoutConstraint.activate([
            countLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            countLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50),
            incrementButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            incrementButton.topAnchor.constraint(equalTo: countLabel.bottomAnchor, constant: 20),
            decrementButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            decrementButton.topAnchor.constraint(equalTo: incrementButton.bottomAnchor, constant: 20)
        ])
    }

    @objc private func incrementCount() {
        count += 1
        updateUI()
    }

    @objc private func decrementCount() {
        count -= 1
        updateUI()
    }

    private func updateUI() {
        countLabel.text = "\(count)"
        countLabel.textColor = count < 0 ? .red : .green
    }
}

선언적으로 코딩하는 방법

  • 위에서 알아본것처럼 선언형 프로그래밍은 코드가 마치 뷰처럼 보여야 하는것이 핵심
  • ex) padding을 하나 작성 할 때도 이런 관점이 매우 중요
    • 아래 뷰에서 padding을 30만큼 주고 싶은 경우?
struct ContentView: View {
    var body: some View {
        VStack(spacing: 0) {
            Text("첫 번째 text")
                .font(.largeTitle)
            
            Text("두 번째 text")
                .font(.largeTitle)
        }
    }
}

  • 아래처럼 두 번째 text에다 padding top을 줄 수 있지만, 코드를 읽을 때는 위에서 아래로 읽기 때문에, 가능하면 윗쪽에 bottom padding으로 작성할 것
// bad
struct ContentView: View {
    var body: some View {
        VStack(spacing: 0) {
            Text("첫 번째 text")
                .font(.largeTitle)
            
            Text("두 번째 text")
                .font(.largeTitle)
                .padding(.top, 30) // <-
        }
    }
}

// good
struct ContentView: View {
    var body: some View {
        VStack(spacing: 0) {
            Text("첫 번째 text")
                .font(.largeTitle) 
                .padding(.bottom, 30) // <-
            
            Text("두 번째 text")
                .font(.largeTitle)
        }
    }
}

 

Comments