[내일배움캠프 18일차] 알고리즘, 앱 개발 입문강의 2

NH·2025년 3월 26일

내일배움캠프

목록 보기
18/62
post-thumbnail

🏆 1일 1알고리즘

👄 모음 제거

  • 프로그래머스 알고리즘 모음 제거 👉 링크 이동

🔹 문제 설명

영어에선 a, e, i, o, u 다섯 가지 알파벳을 모음으로 분류합니다.
문자열 my_string이 매개변수로 주어질 때
모음을 제거한 문자열을 return하도록 solution 함수를 완성해주세요.

🔹 첫 시도

func solution(_ my_string:String) -> String {
    let gather: [String] = ["a", "e", "i", "o", "u"]
    
    var array_my_string: [String] = my_string.map { String($0) }
    
    print(array_my_string)
    
    for i in 0..<array_my_string.count {
        print(array_my_string[i])
        
        if gather.contains(array_my_string[i]) {
            array_my_string.remove(at: i)
        }
    }
    return ""
}
  • 결과: 에러 발생
    Thread 1: Fatal error: Index out of range

🔹 오류 코드 분석

  • Fatal error: Index out of range 오류는 배열에 인덱스 값이 초과하면 발생한다.
  • 나는 배열안에 모음이 있으면 바로 배열에서 제거를 했다.
  • 당연히 배열 요소를 제거하므로 for문의 인덱스 값 i이 초과한다.
       for i in 0..<array_my_string.count {
           print(array_my_string[i])
           
           if gather.contains(array_my_string[i]) {
               array_my_string.remove(at: i) // 배열 요소 제거 -> 인덱스가 줄어듦
           }
       }

🔹 최종

func solution(_ my_string:String) -> String {
    let gather: [String] = ["a", "e", "i", "o", "u"]
    
    let array_my_string: [String] = my_string.map { String($0) }
    var result: [String] = []

    for i in array_my_string {
        if !gather.contains(i) {
            result.append(i)
        }
    }

    return result.joined()
}

print(solution("bus")) // bs
print(solution("nice to meet you")) // nc t mt y
  • 반복문을 모음 배열에 포함되지 않으면 result 배열에 추가하는 방식으로 변경했다.
  • 결과는 성공!!

🤖 지피티의 결과

과연 지피티는 또 얼마나 코드를 줄일까...

func solution(_ my_string:String) -> String {
    let gather: Set<Character> = ["a", "e", "i", "o", "u"]
    return my_string.filter { !gather.contains($0) }
}

단 두줄...😳

  • 고차함수 filter를 사용해서 조건에 맞는 문자로 반환했다.

알고리즘을 풀면서 학습한 내용

🔹 Set과 배열

✅ 알고리즘에서 Set<Character>를 사용하는 이유

  • GPT는 해당 알고리즘 문제에서 gather를 배열에서 Set으로 변경했다.
  • 이유는 아래와 같다.
  1. 탐색 속도가 더 빠름

    • Set.contains(_:)평균 시간 복잡도는 O(1) (해시 기반 탐색)
    • Array.contains(_:)평균 시간 복잡도는 O(n) (앞에서부터 하나씩 탐색)
    • 즉, Set을 쓰면 모음인지 확인하는 연산이 훨씬 빨라짐!
  2. 불필요한 반복을 줄임

    • 배열을 사용하면 contains를 호출할 때마다 배열 전체를 탐색할 수도 있음.
    • Set을 사용하면 한 번에 빠르게 확인 가능.

🔥 결론

  • 데이터가 작다면 배열(Array)을 써도 큰 차이는 없음.
  • 하지만 데이터가 많아질 경우, Set을 사용하면 성능이 훨씬 개선됨.
  • Setcontains 연산이 배열보다 훨씬 빠름(평균 O(1) vs O(n))성능 개선!
  • Swift에서 "특정 값 포함 여부"를 자주 체크할 때는 Set을 우선 고려하는 게 좋음!

🔹 filter 함수

filter 함수는 컬렉션(Array, Set 등)의 요소를 특정 조건에 맞게 필터링하는 기능을 한다. 클로저를 인자로 받아 true를 반환하는 요소만 남겨 새로운 컬렉션을 반환한다.

동작 과정

  • filter의 내부 클로저서 $0Character 타입이 됨
  • filter는 조건을 만족하는 Character 배열을 반환([Character])
  • 마지막에 자동으로 String([Character]) 형태로 변환되면서 최종적으로 String이 반환됨.

따라서 알고리즘 문제에서 Set<String> 이 아니고 Set<Character> 타입을 사용
만약 Set<String> 으로 사용했다면 $0String($0)로 변경해줘야된다.

🔥 결론

  • filter를 사용하려면 Set<Character>를 사용해야 깔끔하게 동작함.

🧐 앱 개발 입문 강의

📱 코드베이스 실습

🔹 실습 1:

IBAction 에서 했던 버튼 클릭 이벤트 코드베이스에서 추가하기

실습 코드

import UIKit
import SnapKit

class ViewController: UIViewController {
        let button = UIButton()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }
    
    private func configureUI() {
        view.backgroundColor = .white
        
        button.setTitle("Click", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = .red
        button.layer.cornerRadius = 10
        
        //버튼 액션 역확 코드
        button.addTarget(self, action: #selector(buttonClicked), for: .touchDown)
        
        view.addSubview(button)
        
        button.snp.makeConstraints {
            $0.width.equalTo(120)
            $0.height.equalTo(60)
            $0.center.equalToSuperview()
        }
    }
    
    // #selector() 안에 넣으려면 @objc 해줘야된다.
    @objc
    private func buttonClicked() {
        print("버튼이 클릭됨")
    }
}

결과 화면

버튼 클릭 결과


🔹 실습 2: SnapKit을 이용해서 UIImageView와 UILabel을 제약조건과 함께 배치

실습 코드

import UIKit
import SnapKit

class ViewController: UIViewController {
    let imageView = UIImageView()
    let label = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }
    
    private func configureUI() {
        view.backgroundColor = .white
        
        imageView.image = UIImage(named: "cat")
        imageView.backgroundColor = .black
        imageView.contentMode = .scaleAspectFit
        
        label.text = "고양이"
        label.textColor = .black
        label.font = UIFont.boldSystemFont(ofSize: 30)
        
        // 이게 뭐야
        [imageView, label]
            .forEach { view.addSubview($0) }
        
        imageView.snp.makeConstraints {
            $0.width.height.equalTo(300)
            $0.center.equalToSuperview()
        }
        
        label.snp.makeConstraints {
            $0.centerX.equalToSuperview()
            $0.top.equalTo(imageView.snp.bottom).offset(10)
        }
    }
}

결과 화면


🔹 실습 3: SnapKit을 이용해서 UIImageView와 UILabel을 제약조건과 함께 배치

실습 코드

import UIKit
import SnapKit

class ViewController: UIViewController {
    let imageView = UIImageView()
    let label = UILabel()
    let imageView2 = UIImageView()
    let label2 = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }
    
    private func configureUI() {
        view.backgroundColor = .white
        
        imageView.image = UIImage(named: "cat")
        imageView.backgroundColor = .black
        imageView.contentMode = .scaleAspectFit
        
        label.text = "고양이"
        label.textColor = .black
        label.font = UIFont.boldSystemFont(ofSize: 30)
        
        imageView2.image = UIImage(named: "cat2")
        imageView2.backgroundColor = .black
        imageView2.contentMode = .scaleAspectFit
        
        label2.text = "고양이2"
        label2.textColor = .black
        label2.font = UIFont.boldSystemFont(ofSize: 30)
        
        // 이게 뭐야
        [imageView, label, imageView2, label2]
            .forEach { view.addSubview($0) }
        
        imageView.snp.makeConstraints {
            $0.width.height.equalTo(160)
            $0.leading.equalToSuperview().offset(16)
            $0.centerY.equalToSuperview()
        }
        
        label.snp.makeConstraints {
            $0.centerX.equalTo(imageView.snp.centerX)
            $0.top.equalTo(imageView.snp.bottom).offset(16)
        }
        
        imageView2.snp.makeConstraints {
            $0.width.height.equalTo(160)
            //offset inset 차이가 뭐야?
            // offset은  ➡️⬆️ 방햘 양수 값
            // inset은   ⬅️⬇️ 방향 양수 값
            $0.trailing.equalToSuperview().inset(16)
            $0.centerY.equalToSuperview()
        }
        
        label2.snp.makeConstraints {
            $0.centerX.equalTo(imageView2.snp.centerX)
            $0.top.equalTo(imageView2.snp.bottom).offset(16)
        }
    }
}

결과 화면


실습으로 배운 내용

🔹 offset 와 inset 차이

1️⃣ offset

  • 뷰의 기존 위치에서 상대적인 이동을 적용할 때 사용.
  • x, y 방향으로 이동시키는 역할.
  • 원래 위치를 기준으로 이동하기 때문에 기존 크기에는 영향을 주지 않음.

2️⃣ inset

  • 뷰의 크기를 변경하면서 안쪽(inset) 또는 바깥쪽(outset)으로 여백을 조정.
  • 음수 값을 사용하면 크기가 커지고, 양수 값을 사용하면 크기가 줄어듦.

정리

  • offset은 ➡️⬆️ 방햘 양수 값
  • inset은 ⬅️⬇️ 방향 양수 값

✍️ 배운점 & 느낀점

  • 코드베이스에서 UIButton을 생성하고, addTarget을 사용하여 클릭 이벤트를 처리하는 방법을 배웠다.
  • SnapKit 라이브러리를 사용하여 이미지와 텍스트를 레이아웃에 맞게 배치할 수 있었다. 특히, forEach를 사용해 여러 뷰를 한 번에 추가하는 방식이 인상 깊었다
  • offsetinset의 차이를 이해할 수 있었는데, offset은 뷰가 이동하는 방향을 나타내고, inset은 뷰의 외부 여백을 설정하는 방식이라는 점에서 차이를 알게 되었다.
  • forEach문을 활용하여 [imageView, label]과 같은 배열을 사용하여 여러 뷰를 한 번에 view.addSubview로 추가하는 방법을 알게 되었다.
  • UIKit을 다룰 때, 코드를 어떻게 간결하게 작성할 수 있는지 고민하면서 실습을 했는데, SnapKit이 정말 유용한 도구임을 다시 한 번 실감했다. 뷰 간의 관계를 제약조건으로 설정하는 과정이 직관적이고, 코드도 간결해져서 뷰 레이아웃 작업이 훨씬 쉬워다.
  • 또한, addTarget을 사용해 버튼 클릭 이벤트를 처리하는 방식에서 UI와 로직을 분리하는 중요성을 느꼈다. 앞으로 버튼 클릭 이벤트나 다른 액션을 처리할 때도 이 방식을 적극적으로 사용할 것 같다.
profile
iOS 개발 블로그

0개의 댓글