이 영역을 누르면 첫 페이지로 이동
Daily Growth 블로그의 첫 페이지로 이동

Daily Growth

페이지 맨 위로 올라가기

Daily Growth

Loving you is the reason I live. That’s why every day is precious, a step toward my dreams and you.

Swift로 구현한 객관식 퀴즈 앱의 구조와 동작 흐름

  • 2025.06.22 05:41
  • IT

객관식 퀴즈 앱 Swift

📊 0. 앱 전체 흐름 플로우 차트

  1. 앱 실행 → viewDidLoad() 호출 → updateUI() 실행
  2. questionLabel에 문제 텍스트 표시, UIButton에 보기 세 개 출력
  3. 사용자가 선택지 버튼 클릭 → answerButtonPressed() 호출
  4. QuizBrain에서 정답 여부 확인 → 정답이면 녹색, 오답이면 빨간색 표시
  5. 0.2초 후 nextQuestion() 호출 → 다음 문제로 이동 or 리셋
  6. 점수와 진행률 표시, UI 갱신 반복

 

🧱 1. MVC 구조로 살펴보는 파일 구성

역할 파일 설명

역할 파일 설명
Model Question.swift 퀴즈 데이터 구조 정의
Model QuizBrain.swift 퀴즈 상태, 점수, 진행률, 로직 관리
View Storyboard UI 요소 정의 (버튼, 라벨, 프로그레스 바)
Controller ViewController.swift UI 업데이트 및 사용자 입력 처리

 

🧩 2. Question.swift - 퀴즈 데이터 구조 정의

struct Question {
    let text: String             // 문제 텍스트
    let answers: [String]        // 선택지 배열
    let rightAnswer: String      // 정답 문자열

    init(q: String, a: [String], correctAnswer: String) {
        text = q
        answers = a
        rightAnswer = correctAnswer
    }
}

🔍 문법 설명

  • struct: 구조체. Swift에서 값 타입이며, 복사 방식으로 전달된다.
  • let: 상수 선언. 변경 불가.
  • [String]: 문자열 배열. 여러 개의 선택지를 순서대로 저장.
    • 예시: ["심장", "피부", "대장"]
    • 배열은 0부터 시작하는 인덱스로 접근: answers[0], answers[1] 등
  • init(...): 사용자 정의 생성자. 인자 q, a, correctAnswer를 받아 프로퍼티 초기화

✅ 사용 예시

Question(q: "가장 큰 장기는?", a: ["심장", "피부", "대장"], correctAnswer: "피부")

 

🧠 3. QuizBrain.swift - 퀴즈 상태 및 로직 관리

struct QuizBrain {
    var questionNumber = 0         // 현재 질문 번호
    var score = 0                  // 정답 점수

    let quiz = [                   // 문제 배열
        Question(q: "...", a: ["A", "B", "C"], correctAnswer: "B"),
        // 생략
    ]

    func getQuestionText() -> String {
        return quiz[questionNumber].text
    }

    func getAnswers() -> [String] {
        return quiz[questionNumber].answers
    }

    func getProgress() -> Float {
        return Float(questionNumber) / Float(quiz.count)
    }

    mutating func getScore() -> Int {
        return score
    }

    mutating func nextQuestion() {
        if questionNumber + 1 < quiz.count {
            questionNumber += 1
        } else {
            questionNumber = 0
            score = 0
        }
    }

    mutating func checkAnswer(userAnswer: String) -> Bool {
        if userAnswer == quiz[questionNumber].rightAnswer {
            score += 1
            return true
        } else {
            return false
        }
    }
}

🔍 문법 및 동작 설명

  • mutating: 구조체 내부의 속성을 변경하기 위해 필요한 키워드
  • 클래스(class)는 참조 타입이므로 mutating이 필요 없지만, 구조체(struct)는 값 타입이라 mutating을 사용하지 않으면 내부 속성 변경이 불가능하다.
  • Float(...): 정수(Int)를 실수(Float)로 변환. UIProgressView에 필요
  • quiz[questionNumber].answers: 현재 문제의 보기 배열 접근
  • getAnswers() -> [String]: 문자열 배열 반환. 3개의 보기를 제공함

 

🎛️ 4. ViewController.swift - 사용자 입력과 UI 제어

@IBOutlet weak var questionLabel: UILabel!
@IBOutlet weak var progressBar: UIProgressView!
@IBOutlet weak var choice1: UIButton!
@IBOutlet weak var choice2: UIButton!
@IBOutlet weak var choice3: UIButton!
@IBOutlet weak var scoreLabel: UILabel!

var quizBrain = QuizBrain()

override func viewDidLoad() {
    super.viewDidLoad()
    updateUI()
}

@IBAction func answerButtonPressed(_ sender: UIButton) {
    // 초기 버전에서는 아래처럼 옵셔널 강제 언래핑을 사용했음
    // let userAnswer = sender.currentTitle!
    // 이 방식은 버튼 타이틀이 nil일 경우 앱이 크래시날 수 있으므로 비추천

    // 현재는 안전한 방식으로 개선됨
    guard let userAnswer = sender.currentTitle else { return }
    let userGotItRight = quizBrain.checkAnswer(userAnswer: userAnswer)
    sender.backgroundColor = userGotItRight ? .green : .red

    quizBrain.nextQuestion()

    Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(updateUI), userInfo: nil, repeats: false)
}

@objc func updateUI() {
    questionLabel.text = quizBrain.getQuestionText()
    let answers = quizBrain.getAnswers()

    choice1.setTitle(answers[0], for: .normal)
    choice2.setTitle(answers[1], for: .normal)
    choice3.setTitle(answers[2], for: .normal)

    scoreLabel.text = "Score: \(quizBrain.getScore())"
    progressBar.progress = quizBrain.getProgress()

    choice1.backgroundColor = .clear
    choice2.backgroundColor = .clear
    choice3.backgroundColor = .clear
}

🔍 상세 문법 설명 및 예제 보완

  • @IBOutlet, @IBAction: Interface Builder(UIStoryboard)와 연결된 코드 속성 및 액션
  • UIButton.setTitle(_:for:)의 for: 파라미터는 UIControl.State 타입이며 버튼의 상태를 지정한다.
    • .normal: 기본 상태 (항상 필요)
    • .highlighted: 눌렸을 때 상태
    • .disabled: 비활성화 상태
    • .selected: 선택된 상태

✅ 실전 예제

choice1.setTitle("선택됨", for: .selected)
choice1.setTitle("비활성화됨", for: .disabled)
  • guard let vs if let 비교:
// 안전하게 옵셔널 해제: guard
@IBAction func tapped(_ sender: UIButton) {
    guard let title = sender.currentTitle else {
        print("버튼 타이틀이 없음")
        return
    }
    print("선택한 답: \(title)")
}

// 대안: if let 사용
if let title = sender.currentTitle {
    print("답변: \(title)")
}
  • !(옵셔널 강제 언래핑)는 가능한 피해야 하며, guard let 또는 if let을 사용하는 것이 더 안전하다.

 

📘 5. 문법 총정리 테이블

문법 요소 설명
struct 구조체 선언. 값 타입, 복사 전달
let / var 상수와 변수. let은 변경 불가, var는 수정 가능
[String] 문자열 배열. 선택지를 저장하기 위해 사용
mutating 구조체 내부 상태 변경 가능 메서드 지정 키워드
Float() 정수 → 실수 변환. 프로그레스 바용
@IBOutlet UI 컴포넌트 연결 속성
@IBAction UI 액션 핸들러 연결 메서드
UIButton.setTitle(_:for:) 버튼 타이틀 설정. 상태별(.normal, .highlighted, .disabled, .selected) 구분 가능
Timer.scheduledTimer 일정 시간 후 특정 함수 실행. UI 피드백 UX 구성용
guard let / if let 옵셔널 안전 해제 방식. 조건 분기 흐름 제어용
! (강제 언래핑) 옵셔널이 nil일 경우 앱 크래시 발생. 실무에서는 비권장

✅ 정리

  • 배열과 구조체를 통해 선택지를 유연하게 관리할 수 있다.
  • MVC 패턴으로 코드가 역할별로 명확하게 나뉘어 관리와 확장이 쉽다.
  • mutating 키워드는 구조체의 상태를 변화시키기 위해 필수이다.
  • guard let과 if let은 옵셔널 값을 안전하게 해제할 수 있는 방식이며, 상황에 맞게 적절히 선택해야 한다.
  • !는 옵셔널이 nil일 경우 앱 크래시를 유발할 수 있어 되도록 피하는 것이 좋다.
  • 버튼 상태를 고려한 .normal, .highlighted, .disabled, .selected 등의 UI 상태 지정은 사용자 경험 개선에 매우 중요하다.
  • 정답 확인 후 UI를 잠시 색상으로 피드백하고 다음 질문으로 자연스럽게 전환되는 UX는 Timer를 통해 구현된다.

 

 

 

반응형

'IT' 카테고리의 다른 글

바이브코딩 시대, AI와 함께 만드는 나만의 프로덕트  (0) 2025.07.01
iOS 앱에 MVC를 적용하는 구체적인 방법과 코드 예제  (1) 2025.06.24
Swift 구조체와 MVC 패턴 완전 정복: mutating, 값 타입, 불변성  (0) 2025.06.20
Swift 퀴즈 앱 리팩토링 : MVC 패턴, -> Bool, mutating func, _ 매개변수  (0) 2025.06.19
Swift 앱 개발에서 struct로 배우는 코드 구조의 힘  (1) 2025.06.18

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • 바이브코딩 시대, AI와 함께 만드는 나만의 프로덕트

    바이브코딩 시대, AI와 함께 만드는 나만의 프로덕트

    2025.07.01
  • iOS 앱에 MVC를 적용하는 구체적인 방법과 코드 예제

    iOS 앱에 MVC를 적용하는 구체적인 방법과 코드 예제

    2025.06.24
  • Swift 구조체와 MVC 패턴 완전 정복: mutating, 값 타입, 불변성

    Swift 구조체와 MVC 패턴 완전 정복: mutating, 값 타입, 불변성

    2025.06.20
  • Swift 퀴즈 앱 리팩토링 : MVC 패턴, -> Bool, mutating func, _ 매개변수

    Swift 퀴즈 앱 리팩토링 : MVC 패턴, -> Bool, mutating func, _ 매개변수

    2025.06.19
다른 글 더 둘러보기

정보

Daily Growth 블로그의 첫 페이지로 이동

Daily Growth

  • Daily Growth의 첫 페이지로 이동

검색

메뉴

    카테고리

    • 분류 전체보기 (470)
      • Design History (69)
      • IT (170)
      • Typography (13)
      • UX • UI Design (11)
      • Money (62)
      • Health (53)
      • Words (6)
      • Reading (21)
      • English (64)

    나의 외부 링크

    • Daily Growth
    • Daily World
    • lody.design
    • lody.diary

    정보

    self-improvement의 Daily Growth

    Daily Growth

    self-improvement

    블로그 구독하기

    • 구독하기
    • 네이버 이웃 맺기
    • RSS 피드

    방문자

    • 전체 방문자
    • 오늘
    • 어제

    티스토리

    • 티스토리 홈
    • 이 블로그 관리하기
    • 글쓰기
    Powered by Tistory / Kakao. Copyright © self-improvement.

    티스토리툴바