[iOS] 프로그래밍 실무 복습 #3 - 무드등 앱 만들기

2025. 3. 19. 16:29iOS프로그래밍실무

더보기

이 글은 한성현 교수님의 ‘iOS 프로그램 실무’ 강의를 수강한 후, 복습을 위해 작성된 글입니다.

주로 실습 위주의 내용을 다루고 있습니다.

 
wrtn(뤼튼)을 사용해 앱 개발을 기획하기

더보기

Night Light 무드등 iOS 앱을 만들려고 해 app store 인기 앱을 참고해서 사전 기획서를 작성해줘

Night Light 무드등 iOS 앱을 만들기 위한 사전 기획서는 다음과 같은 요소를 포함해야 합니다. 이 기획서는 앱의 목표, 기능, 디자인, 마케팅 전략 등을 포함하여 앱 개발의 방향성을 제시합니다.

### 1. 앱 개요
- **앱 이름**: Night Light
- **목표**: 사용자가 편안한 분위기를 조성할 수 있도록 다양한 조명 효과와 색상을 제공하는 무드등 앱.
- **대상 사용자**: 수면에 어려움을 겪는 사람들, 명상 및 요가를 하는 사람들, 아늑한 분위기를 원하는 사용자.

### 2. 주요 기능
- **조명 효과**: 다양한 조명 효과(예: 부드러운 점등, 깜빡임, 색상 전환 등)를 제공.
- **색상 선택**: 사용자가 원하는 색상을 선택할 수 있는 색상 팔레트 제공.
- **타이머 기능**: 사용자가 설정한 시간에 맞춰 조명이 자동으로 꺼지거나 켜지도록 설정.
- **음악 연동**: 사용자가 선택한 음악과 조명 효과를 동기화하여 분위기를 조성.
- **테마 설정**: 다양한 테마(예: 자연, 우주, 바다 등)를 제공하여 사용자가 원하는 분위기를 쉽게 선택할 수 있도록 함.
- **사용자 맞춤 설정**: 사용자가 자주 사용하는 조명 설정을 저장하고 쉽게 접근할 수 있도록 함.

### 3. 디자인
- **UI/UX 디자인**: 직관적이고 사용하기 쉬운 인터페이스 설계. 부드러운 색상과 간결한 레이아웃을 통해 편안한 느낌을 제공.
- **아이콘 및 그래픽**: 무드등과 관련된 부드러운 그래픽과 아이콘 사용.
- **애니메이션**: 조명 효과 전환 시 부드러운 애니메이션을 추가하여 시각적 즐거움 제공.

### 4. 기술 스택
- **프로그래밍 언어**: Swift
- **프레임워크**: UIKit, Core Animation, AVFoundation (음악 연동을 위한)
- **데이터 저장**: UserDefaults 또는 Core Data (사용자 설정 저장)

### 5. 마케팅 전략
- **앱 스토어 최적화(ASO)**: 키워드 연구를 통해 앱 스토어에서의 가시성 향상.
- **소셜 미디어 캠페인**: Instagram, Facebook 등에서 사용자 후기 및 사용 사례 공유.
- **인플루언서 마케팅**: 관련 분야의 인플루언서와 협력하여 앱 홍보.
- **무료 체험 제공**: 기본 기능을 무료로 제공하고, 프리미엄 기능은 인앱 구매로 제공.

### 6. 경쟁 분석
- **경쟁 앱 조사**: App Store에서 인기 있는 유사 앱 분석 (예: "Sleep Cycle", "Calm", "Headspace").
- **차별화 요소**: 기존 앱과의 차별화된 기능 및 사용자 경험 강조.

### 7. 일정 계획
- **개발 일정**: 초기 기획, 디자인, 개발, 테스트, 출시까지의 일정 계획.
- **마일스톤 설정**: 각 단계별 목표 및 완료 기한 설정.

이 기획서를 바탕으로 Night Light 무드등 iOS 앱의 개발을 진행할 수 있습니다. 각 요소를 구체화하고, 필요에 따라 추가적인 기능이나 디자인 요소를 고려할 수 있습니다. 

프로젝트의 주요 일정을 작성하기 위해서는 프로젝트의 범위, 목표, 팀 구성, 그리고 필요한 작업들을 고려해야 합니다. 일반적인 소프트웨어 개발 프로젝트의 주요 일정을 아래와 같이 제안할 수 있습니다. 이 일정은 2025년 5월 30일까지 개발을 완료하기 위한 것입니다.


2025년 5월 30일까지 개발을 완료해야 하는데 주요 일정을 작성해줘

### 주요 일정

1. **프로젝트 계획 수립 (2025년 3월 20일 - 3월 31일)**
   - 요구사항 분석
   - 프로젝트 목표 및 범위 정의
   - 팀 구성 및 역할 분담

2. **설계 단계 (2025년 4월 1일 - 4월 15일)**
   - 시스템 아키텍처 설계
   - 데이터베이스 설계
   - UI/UX 디자인

3. **개발 단계 (2025년 4월 16일 - 5월 15일)**
   - 기능 개발 (모듈별로 나누어 진행)
   - 코드 리뷰 및 품질 보증
   - 통합 테스트 준비

4. **테스트 단계 (2025년 5월 16일 - 5월 25일)**
   - 단위 테스트
   - 통합 테스트
   - 사용자 수용 테스트 (UAT)

5. **배포 준비 (2025년 5월 26일 - 5월 29일)**
   - 최종 검토 및 수정
   - 배포 문서 작성
   - 배포 환경 설정

6. **프로젝트 완료 및 배포 (2025년 5월 30일)**
   - 최종 배포
   - 프로젝트 종료 회의
   - 피드백 수집 및 후속 조치 계획

이 일정은 일반적인 소프트웨어 개발 프로세스를 기반으로 하며, 프로젝트의 특성에 따라 조정이 필요할 수 있습니다. 각 단계의 세부 작업과 마일스톤을 추가하여 더욱 구체화할 수 있습니다. 


3명이 함께 프로젝트를 진행할 건데 업무 분담표를 작성해줘

3명이 함께 프로젝트를 진행할 경우, 각 팀원의 역할과 책임을 명확히 하는 것이 중요합니다. 아래는 3명의 팀원에게 적절히 업무를 분담한 예시입니다.

### 업무 분담표

팀원역할주요 업무
팀원 A프로젝트 매니저 / 개발자- 프로젝트 계획 수립 및 일정 관리
- 요구사항 분석 및 문서화<br>- 기능 개발 (모듈 1)
팀원 B개발자 / UI/UX 디자이너- 시스템 아키텍처 설계
- UI/UX 디자인 및 프로토타입 제작
- 기능 개발 (모듈 2) 
팀원 CQA / 개발자- 테스트 계획 수립 및 실행
- 단위 테스트 및 통합 테스트
- 기능 개발 (모듈 3)


### 세부 업무

1. **팀원 A**
   - 프로젝트 계획 수립 및 일정 관리
   - 요구사항 분석 및 문서화
   - 기능 개발 (모듈 1)
   - 코드 리뷰 및 품질 보증

2. **팀원 B**
   - 시스템 아키텍처 설계
   - UI/UX 디자인 및 프로토타입 제작
   - 기능 개발 (모듈 2)
   - 사용자 수용 테스트 (UAT) 지원

3. **팀원 C**
   - 테스트 계획 수립 및 실행
   - 단위 테스트 및 통합 테스트
   - 기능 개발 (모듈 3)
   - 최종 검토 및 수정

이 분담표는 각 팀원의 강점과 전문성을 고려하여 작성된 것입니다. 필요에 따라 역할과 업무를 조정하여 팀원들이 효율적으로 협력할 수 있도록 할 수 있습니다. 


iOS 무드등 앱을 스토리보드 기반으로 소스 코드를 작성해줘

iOS 무드등 앱을 스토리보드 기반으로 작성하는 방법에 대한 기본적인 예제를 제공하겠습니다. 이 예제는 Swift 언어를 사용하며, UIKit 프레임워크를 기반으로 합니다. 무드등 앱은 사용자가 색상을 선택하고, 선택한 색상으로 화면을 변경하는 간단한 기능을 포함합니다.

### 1. 프로젝트 생성
Xcode에서 새로운 iOS 프로젝트를 생성하고, "App" 템플릿을 선택한 후, "Storyboard"를 선택합니다.

### 2. 스토리보드 설정
1. Main.storyboard를 열고, 기본 ViewController를 선택합니다.
2. ViewController에 UIButton과 UIView를 추가합니다.
   - UIButton: 색상 선택 버튼
   - UIView: 무드등 색상을 표시할 영역

### 3. IBOutlet 및 IBAction 연결
ViewController.swift 파일을 열고, IBOutlet과 IBAction을 추가합니다.

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var moodLightView: UIView! // 무드등 색상을 표시할 뷰
    @IBOutlet weak var colorButton: UIButton! // 색상 선택 버튼

    override func viewDidLoad() {
        super.viewDidLoad()
        // 초기 색상 설정
        moodLightView.backgroundColor = UIColor.white
    }

    @IBAction func colorButtonTapped(_ sender: UIButton) {
        // 색상 선택을 위한 UIAlertController 생성
        let alert = UIAlertController(title: "색상 선택", message: nil, preferredStyle: .actionSheet)

        // 색상 선택 액션 추가
        let colors: [(String, UIColor)] = [
            ("빨간색", UIColor.red),
            ("초록색", UIColor.green),
            ("파란색", UIColor.blue),
            ("노란색", UIColor.yellow),
            ("흰색", UIColor.white)
        ]

        for (colorName, color) in colors {
            alert.addAction(UIAlertAction(title: colorName, style: .default, handler: { _ in
                self.moodLightView.backgroundColor = color
            }))
        }

        // 취소 버튼 추가
        alert.addAction(UIAlertAction(title: "취소", style: .cancel, handler: nil))

        // UIAlertController 표시
        present(alert, animated: true, completion: nil)
    }
}


### 4. 스토리보드에서 연결
1. `moodLightView`와 `colorButton`을 스토리보드에서 각각의 UIView와 UIButton에 연결합니다.
2. UIButton의 "Touch Up Inside" 이벤트를 `colorButtonTapped` 메서드에 연결합니다.

### 5. 실행
이제 앱을 실행하면, 버튼을 클릭하여 색상을 선택할 수 있는 무드등 앱이 완성됩니다. 선택한 색상에 따라 `moodLightView`의 배경색이 변경됩니다.

이 코드는 기본적인 구조를 제공하며, 필요에 따라 추가적인 기능이나 디자인을 구현할 수 있습니다. 

 
XCode를 실행해 본격적으로 무드등 앱 개발 프로젝트를 진행해 봅시다.

앱 이름, 단체 ID, interface, Language 를 지정합니다.

General 탭에서 확인해야 할 사항
Minimum Deployments - 16.0 이상(이 앱은 iOS 16 이상에서 동작)
Identuty - Bundle Identifier (고유해야 함.)
Deployment Info 
 

시뮬레이터와 기종을 맞춥니다.
 
실행 버튼을 눌러도 아직 아무것도 뜨지 않습니다.
 
 
ViewController에 들어갑니다.

view 함수를 두 개 더 추가해봅시다.

콘솔 창에서 세 개의 함수가 실행되어 결과가 출력된 것을 볼 수 있습니다.
 

import UIKit

// ViewController 클래스는 UIViewController를 상속받아 iOS 앱의 화면을 관리합니다.
class ViewController: UIViewController {

    // viewDidLoad 메서드는 뷰가 메모리에 로드된 후 호출됩니다.
    override func viewDidLoad() {
        super.viewDidLoad() // 부모 클래스의 viewDidLoad 메서드를 호출하여 기본 동작을 수행합니다.
        print("viewDidLoad") // 뷰가 로드되었음을 콘솔에 출력합니다.
        // 추가적인 설정을 여기에 작성할 수 있습니다.
    }

    // viewWillAppear 메서드는 뷰가 화면에 나타나기 직전에 호출됩니다.
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated) // 부모 클래스의 viewWillAppear 메서드를 호출하여 기본 동작을 수행합니다.
        print("viewWillAppear") // 뷰가 나타나기 직전임을 콘솔에 출력합니다.
        // 이곳에서 뷰가 나타나기 전에 필요한 설정을 할 수 있습니다.
    }

    // viewDidAppear 메서드는 뷰가 화면에 나타난 후 호출됩니다.
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated) // 부모 클래스의 viewDidAppear 메서드를 호출하여 기본 동작을 수행합니다.
        print("viewDidAppear") // 뷰가 나타난 후임을 콘솔에 출력합니다.
        // 이곳에서 애니메이션 시작이나 데이터 로드와 같은 작업을 수행할 수 있습니다.
    }
}

 
 
viewDidLoad:
뷰가 메모리에 로드된 후 한 번만 호출됩니다. 이곳에서 초기 설정이나 데이터 준비를 수행합니다. 예를 들어, UI 요소의 초기 상태를 설정하거나 데이터 소스를 준비하는 작업을 할 수 있습니다.

viewWillAppear:
뷰가 화면에 나타나기 직전에 호출됩니다. 이 메서드는 뷰가 사용자에게 보이기 전에 필요한 작업을 수행하는 데 적합합니다. 예를 들어, 데이터 업데이트나 UI 요소의 상태를 변경하는 작업을 할 수 있습니다.

viewDidAppear:
뷰가 화면에 나타난 후 호출됩니다. 이 메서드는 뷰가 완전히 표시된 후에 실행되므로, 애니메이션 시작이나 데이터 로드와 같은 작업을 수행하기에 적합합니다.

 
App Life Circle
 
Foreground Mode

  • Active : 앱이 전경에 있고 이벤트를 받고 있음. 일반적으로 사용자와 상호작용하는 동안 앱은 이 상태에 있음
  • Inactive : 앱이 전경에 있지만 이벤트를 받고 있지 않음

Background Mode

  • Running : 앱이 백그라운드에 있지만 여전히 코드를 실행. 사용자가 다른 앱으로 전환하거나 홈 화면으로 돌아갔을 때 앱은 일시적으로 이 상태에 머물게 됨
  • Suspend : 앱이 백그라운드에 있지만 코드를 실행하지 않음. 시스템은 앱을 이 상태로 자동으로 전환하며 필요에 따라 메모리를 회수하기 위해 앱을 종료시킬 수 있음.

View
모든 view는 UIKit의 UIView 클래스의 자식클래스

  • 하나의 뷰는 여러 개의 하위 뷰를 가질 수 있고, 각 하위 뷰는 부모 뷰에 추가됨
  • 뷰 계층을 통해 뷰 간의 위치 관계와 그리기 순서를 결정
  • 보통 부모 뷰에 추가된 뷰는 부모 뷰의 영역 내에서만 그려짐

 
UIWindow 클래스

  • 모든 앱은 하나 이상의 윈도우를 가지고 뷰 계층의 가장 상위에 위치하며 뷰 컨트롤러의 뷰와 그 하위 뷰들을 포함
  • UIWindow는 일반적으로 앱의 시작 시점에 생성되며, 앱의 메인 화면을 그리는 데 사용되는 뷰 컨트롤러를 root view controller로 설정

 
부모 또는 수퍼뷰(superview) / 자식 또는 서브뷰(subview)
서브뷰의 크기 변화는(디바이스가 회전하여 뷰의 크기가 변화하는 경우) 부모 뷰와의 관계에 의해 정의됨

 
컨테이너 뷰(Container View)
여러 개의 뷰 컨트롤러를 포함하고, 그 사이를 전환하는 역할
 
UINavigationController

  • 뷰 컨트롤러 간의 계층적 네비게이션을 관리
  • 리스트에서 항목을 선택하면 상세 화면으로 이동하거나, 여러 단계를 거쳐 정보를 입력하는 등의 작업에 사용

UITabBarController

  • 뷰 컨트롤러 간의 평행 네비게이션을 관리
  • 앱의 주요 기능을 각각의 탭으로 나누고 사용자가 원하는 탭을 선택하여 해당 기능을 이용할 수 있게 하는 데 사용

UISplitViewController

  • 마스터-디테일 인터페이스를 관리
  • iPad와 같은 큰 화면에서 사용되며, 한 화면에 두 개의 관련된 뷰 컨트롤러를 동시에 표시하는 데 사용

컨트롤

  • 컨트롤들은 정보를 표시하고 사용자에게 반응도 하는 뷰들을 포함
  • UIControl 클래스(UIView의 서브뷰)의 자식으로 버튼, 슬라이더, TextField 등

디스플레이 뷰

  • 디스플레이 뷰는 시각적인 반응을 제공한다는 점에서는 컨트롤과 비슷하지만 사용자 상호작용에 반응하지 않음
  • UILabel, UIImageView

 
1) 앱을 실행하면 배경색을 파란색으로 변경

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.blue
    }

}

 
2) 배경색 임의로 변경

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let red = CGFloat(arc4random_uniform(256)) / 255.0
        let green = CGFloat(arc4random_uniform(256)) / 255.0
        let blue = CGFloat(arc4random_uniform(256)) / 255.0
        self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
    }

}

 
3) 배경색 1초마다 계속 변경 : 타이머 사용

import UIKit

class ViewController: UIViewController {
    
    var colorChangeTimer: Timer?

      override func viewDidLoad() {
        super.viewDidLoad()
        
        colorChangeTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
          let red = CGFloat(arc4random_uniform(256)) / 255.0
          let green = CGFloat(arc4random_uniform(256)) / 255.0
          let blue = CGFloat(arc4random_uniform(256)) / 255.0
          self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
        }
      }
      override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        colorChangeTimer?.invalidate()
        colorChangeTimer = nil
      }
}

 
 
4) 배경 음악 추가

bgm.mp3
7.34MB

 
위 파일을 다운받아 Finder에서 Xcode의 project navigator로 드래그

import UIKit

import AVFoundation  // 오디오를 다루기 위한 프레임워크 추가

class ViewController: UIViewController {
  
  var colorChangeTimer: Timer?
  var audioPlayer: AVAudioPlayer!

  override func viewDidLoad() {
    super.viewDidLoad()
    
    // 배경 음악 재생 설정
    if let soundURL = Bundle.main.url(forResource: "bgm", withExtension: "mp3") {
      do {
        audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
        audioPlayer.play()
      } catch {
        print("음악 파일 로드 또는 재생 중 에러 발생: \(error)")
      }
    }
    
    // 1초마다 반복되는 타이머 생성
    colorChangeTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
      // 랜덤한 빨간색, 초록색, 파란색 값을 생성
      let red = CGFloat(arc4random_uniform(256)) / 255.0
      let green = CGFloat(arc4random_uniform(256)) / 255.0
      let blue = CGFloat(arc4random_uniform(256)) / 255.0
      self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
    }
  }
  
  override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    
    // ViewController가 사라질 때 타이머를 중지하고 nil로 설정하여 메모리 누수를 방지
    colorChangeTimer?.invalidate()
    colorChangeTimer = nil
    
    // 음악 재생 중지
    audioPlayer.stop()
  }
}

 
5) 1분 후에 자동 종료 기능 구현

DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
    exit(0) //20초 후에 앱 종료 기능
}

 
iOS에서는 앱 개발자가 직접 앱을 종료시킬 수 없습니다.
이는 Apple의 앱 디자인 가이드라인에 명시되어 있습니다.
사용자가 앱을 직접 종료할 수 있도록 안내하는 것이 최선의 방법입니다.
그러나, 사용자에게 앱을 종료하라는 메시지를 보여주고, 확인 버튼을 누를 경우 앱의 주요 기능을 중지시키는 등의 방법을 사용할 수 있습니다.

import UIKit
import AVFoundation

class ViewController: UIViewController {
  
  var colorChangeTimer: Timer?
  var audioPlayer: AVAudioPlayer!

  override func viewDidLoad() {
    super.viewDidLoad()
    
    // 배경 음악 재생 설정
    if let soundURL = Bundle.main.url(forResource: "bgm", withExtension: "mp3") {
      do {
        audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
        audioPlayer.play()
      } catch {
        print("음악 파일 로드 또는 재생 중 에러 발생: \(error)")
      }
    }
    
    // 1초마다 반복되는 타이머 생성
    colorChangeTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
      // 랜덤한 빨간색, 초록색, 파란색 값을 생성
      let red = CGFloat(arc4random_uniform(256)) / 255.0
      let green = CGFloat(arc4random_uniform(256)) / 255.0
      let blue = CGFloat(arc4random_uniform(256)) / 255.0
      
      // 생성된 랜덤 색상을 사용하여 ViewController의 배경색 설정
      self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
    }
    
    // 1분 후에 앱 종료
    DispatchQueue.main.asyncAfter(deadline: .now() + 60) {
      exit(0)
    }
  }
  
  override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    
    // ViewController가 사라질 때 타이머를 중지하고 nil로 설정하여 메모리 누수를 방지
    colorChangeTimer?.invalidate()
    colorChangeTimer = nil
    
    // 음악 재생 중지
    audioPlayer.stop()
  }
}

 
6) 화면을 탭 하면 alert창으로 앱 종료, 앱 재실행(foreground)시 동일 기능 구현

import UIKit
import AVFoundation

class ViewController: UIViewController {
  
  var colorChangeTimer: Timer?
  var audioPlayer: AVAudioPlayer!

  override func viewDidLoad() {
    super.viewDidLoad()
    
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
    view.addGestureRecognizer(tapGesture)
    
    NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
  }
  
  @objc func applicationWillEnterForeground() {
    startTimerAndMusic()
  }
  
  @objc func handleTapGesture() {
    stopTimerAndMusic()
    
    let alert = UIAlertController(title: "알림", message: "앱을 종료해주세요.", preferredStyle: .alert)
    let okAction = UIAlertAction(title: "확인", style: .default, handler: nil)
    alert.addAction(okAction)
    present(alert, animated: true, completion: nil)
  }
  
  func startTimerAndMusic() {
    // 배경 음악 재생 설정
    if let soundURL = Bundle.main.url(forResource: "bgm", withExtension: "mp3") {
      do {
        audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
        audioPlayer.play()
      } catch {
        print("음악 파일 로드 또는 재생 중 에러 발생: \(error)")
      }
    }
    
    // 1초마다 반복되는 타이머 생성
    colorChangeTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
      // 랜덤한 빨간색, 초록색, 파란색 값을 생성
      let red = CGFloat(arc4random_uniform(256)) / 255.0
      let green = CGFloat(arc4random_uniform(256)) / 255.0
      let blue = CGFloat(arc4random_uniform(256)) / 255.0
      
      // 생성된 랜덤 색상을 사용하여 ViewController의 배경색 설정
      self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
    }
  }
  
  func stopTimerAndMusic() {
    // 타이머와 음악 재생 중지
    colorChangeTimer?.invalidate()
    colorChangeTimer = nil
    audioPlayer.stop()
  }
}

 

 
7) Switch를 이용하여 배경음악 끄고 배경색 고정

import UIKit
import AVFoundation

class ViewController: UIViewController {
  
  var colorChangeTimer: Timer?
  var audioPlayer: AVAudioPlayer!

    @IBOutlet weak var musicSwitch: UISwitch!
    
    override func viewDidLoad() {
    super.viewDidLoad()
    
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
    view.addGestureRecognizer(tapGesture)
    
    NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
  }
  
  @objc func applicationWillEnterForeground() {
    startTimerAndMusic()
  }
  
  @objc func handleTapGesture() {
    stopTimerAndMusic()
      colorChangeTimer = nil
      audioPlayer = nil
    
    let alert = UIAlertController(title: "알림", message: "앱을 종료해주세요.", preferredStyle: .alert)
    let okAction = UIAlertAction(title: "확인", style: .default, handler: nil)
    alert.addAction(okAction)
    present(alert, animated: true, completion: nil)
  }
  
    @IBAction func switchDidChange(_ sender: UISwitch) {
        if sender.isOn {
            startTimerAndMusic()
        }else {
            stopTimerAndMusic()
        }
    }
    func startTimerAndMusic() {
    // 배경 음악 재생 설정
    if let soundURL = Bundle.main.url(forResource: "bgm", withExtension: "mp3") {
      do {
        audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
        audioPlayer.play()
      } catch {
        print("음악 파일 로드 또는 재생 중 에러 발생: \(error)")
      }
    }
    
    // 1초마다 반복되는 타이머 생성
    colorChangeTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
      // 랜덤한 빨간색, 초록색, 파란색 값을 생성
      let red = CGFloat(arc4random_uniform(256)) / 255.0
      let green = CGFloat(arc4random_uniform(256)) / 255.0
      let blue = CGFloat(arc4random_uniform(256)) / 255.0
      
      // 생성된 랜덤 색상을 사용하여 ViewController의 배경색 설정
      self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
    }
  }
  
  func stopTimerAndMusic() {
    // 타이머와 음악 재생 중지
    colorChangeTimer?.invalidate()
    colorChangeTimer = nil
    audioPlayer.stop()
  }
}