SFML(Simple and Fast Multimedia Library)이란?

 SFML은 게임 개발과 UI 환경의 어플리케이션을 간단하게 제작할 수 있게 간단한 인터페이스를 제공하며, C++ 언어를 사용한다.

SFML은 시스템, 윈도우, 그래픽, 오디오, 네트워크인 다섯가지의 구성요소를 가진다.

지원되는 OS는 Windows, Linux, macOS 그리고 Android & iOS(한정적으로 지원)에서 컴파일하고, 실행해볼 수 있다.


설치 방법은 아래와 같다.

아래 링크로 이동하면, SFML을 다운받을 수 있는 페이지가 나오게 된다.

https://www.sfml-dev.org/download.php

가장 최신 버전을 사용하는 것이 좋으며, 개발 환경을 이전 버전으로 맞추고 진행하는 경우는 'Order versions'에서 사용하고 있는 이전 버전으로 다운로드 하면 된다.

다운로드하게 되면 압축파일 형태로 받게 된다.

1) Windows 에서 환경설정

SFML 홈페이지 내에 환경 설정하는 방법이 영어로 나와 있습니다.

Windows의 경우 Visual Studio를 주로 이용하여 아래 링크에 나온 내용으로 차근차근 진행하면 환경 설정 완료할 수 있습니다. (중요 부분은 경로와 그림으로 나와 있어 설정하는데에 무리는 없을 거 같습니다.)

https://www.sfml-dev.org/tutorials/2.6/start-vc.php

아래 전북대학교 김형기 강사님의 강의 내용으로 환경설정하도록 하며, 해당 강의를 시청하여 SFML 사용방법을 익히는 것을 추천합니다.

https://www.youtube.com/watch?v=ibMSDw816-A&list=PLMcUoebWMS1nzhlx-NbD4KBGEP1UCUDF_&index=2

2) Mac에서 환경설정

SFML 홈페이지 내에 환경 설정하는 방법이 영어로 나와 있습니다.

Mac의 경우는 Xcode를 사용하기 때문에 아래 링크에 나온 내용으로 차근차근 진행하면 환경 설정 완료할 수 있습니다.

https://www.sfml-dev.org/tutorials/2.6/start-osx.php

Mac에서는 압축해제 후에 SFML 파일을 특정 경로에 복사하여 SFML을 설치합니다.

1) SFML 압축 푼 폴더에서 'Framework' 폴더에 있는 파일 및 폴더를 Mac의 '~/Library/Frameworks' 폴더로 복사합니다.

2) SFML 압축 푼 폴더에서 'extlibs' 폴더에 있는 파일 및 폴더를 Mac의 '~/Library/Frameworks' 폴더로 복사합니다.

3) SFML 압축 푼 폴더에서 'lib' 폴더에 있는 파일 및 폴더를 '~/usr/local/lib' 폴더로 복사해야 하는데 해당 폴더가 숨김 폴더라 나오지 않는데 'Command + . + Shift'를 동시에 누르면 숨겨져 있던 'usr'폴더가 나옵니다.

4) SFML 압축 푼 폴더에서 'include' 폴더에 있는 파일 및 폴더를 '~/usr/local/include' 폴더로 복사합니다.

(숨김 파일이라 안 보이는데 'Command + . + Shift'를 동시에 눌러 숨겨진 'usr' 폴더가 나옵니다.

5) SFML 압축 푼 폴더에서 'templates' 폴더에 있는 파일 및 폴더를 '~/Library/Developer/Xcode/Templates' 폴더에 복사하는데 없다면 해당 경로를 만들어서 복사해줍니다.

6) Xcode를 실행하여 새 프로젝트 생성하여 'macOS' 탭을 누르면 제대로 설치되었다면 아래와 같은 화면이 나옵니다.





 



7) SFML App으로 프로젝트 생성 시에 기본 샘플코드가 작성된 프로젝트를 생성합니다.

8) 샘플 코드 실행 시에 수행이 안되고 에러가 발생하게 됩니다. 그럴 때는 아래와 같이 설정합니다.

(1) 프로젝트에서 'Build Phases' 탭을 클릭합니다.

(2) 항목 중에 'Run Script' 항목을 클릭합니다.

(3) 'Run Script' 항목에서 아래와 같이 체크해줍니다.





9) 위 진행 후에 다시 실행하게 되면, '개발자를 확인할 수 없기 때문에 sfml-system.framework를 열 수 없습니다.' 이런 메시지가 생성되는 경우가 있습니다.

이런 경우 압축해제한 SFML 폴더에서 'Frameworks', 'extlib' 폴더 내의 파일 및 폴더를 '~/Library/Frameworks' 폴더에 복사해서 설치했지만, 보안 상의 이유로 실행하지 않게 막고 있어 실행이 되지 않습니다.

이럴 때는 SFML 폴더에 '~/Library/Frameworks' 폴더로 복사한 파일 및 폴더를 신뢰하는 경로로 추가하기 위해서 터미널에서 아래와 같이 처리합니다.

(1) 터미널을 켜서 '~/Library/Frameworks' 폴더로 이동합니다.

(2) SFML 폴더에 복사한 파일 및 폴더를 'sudo xattr -rd com.apple.quarantine 파일 또는 폴더' 명령어로 신뢰하는 경로로 추가합니다.

 -> sudo xattr -rd com.apple.quarantine sfml-system.framework

 -> sudo xattr -rd com.apple.quarantine sfml-window.framework 

 -> sudo xattr -rd com.apple.quarantine sfml-graphics.framework

 -> sudo xattr -rd com.apple.quarantine sfml-audio.framework

 -> sudo xattr -rd com.apple.quarantine sfml-network.framework

 -> sudo xattr -rd com.apple.quarantine SFML.framework

 -> sudo xattr -rd com.apple.quarantine FLAC.framework

 -> sudo xattr -rd com.apple.quarantine freetype.framework

 -> sudo xattr -rd com.apple.quarantine ogg.framework

 -> sudo xattr -rd com.apple.quarantine OpenAL.framework

 -> sudo xattr -rd com.apple.quarantine vorbis.framework

 -> sudo xattr -rd com.apple.quarantine vorbisenc.framework

 -> sudo xattr -rd com.apple.quarantine vorbisfile.framework

위 명령어 수행 후에 실행되면 샘플 프로젝트의 결과를 출력합니다.




프로그래밍 기본 - 추상화 개요

  • 추상화 (Abstraction) : 복잡한 내용은 숨기고, 주요 기능을 사용할 수 있도록 한다. 예) 스마트 폰의 경우 전파 신호, 터치 동작 등 어떻게 구성되어 있는지 숨기고, 사용자는 해당 기능을 사용한다.
    • 값을 저장하는 변수(Variable) 예) x = 254, y = 317
      변수
      // 값을 출력
      print(4990) // 4990

      // 값을 저장
      burger_price = 4990
      print(burger_price) // 4990
    • 값에 대한 처리를 하는 함수(Fuction) 다른 말로는 메소드(Method)라고 한다. 예) print("Hello")
      함수
      // 이미 정의된 함수 사용
      print("Hello World!") // Hello World! 출력

      // 함수를 정의
      (def:, func:, fun:) methodName() {
         print("Hello World!") 
      }
      // 정의한 함수 사용
      methodName() // Hello World! 출력
    • 변수와 함수로 구성되어 있는 객체(Object) 주로 클래스(Class)라는 용어로 사용되는데 클래스는 객체를 구현한 것을 의미한다.
      객체 (클래스)
      // 객체 정의 (클래스 선언)
      class ObjectName() {
         a = 10, b = 20 // 변수

         // 함수를 정의
         (def:, func:, fun:) methodName() {
             print("a = " + a)
             print("b = " + b) 
         }
      }

      // 객체에 정의된 메소드 사용
      ObjectName object = new ObjectName();
      object. methodName() // a = 10, b = 20 출력

프로그래밍 기본 - 자료형 개요

  • 숫자 (Number)
    • 정수(Integer) : -2, -1, 0, 1, 2 와 같이 소수점이 없는 숫자형을 의미한다.
    • 소수(Floating Point) : 1.1, 3.14, ... 와 같이 소수점이 있는 숫자형을 의미한다.
  • 문자 (String) : "사이의 값" 또는 '사이의 값'으로 큰 따옴표 또는 작은 따옴표 사이의 값을 의미한다.
  • 불린 (Boolean) : 참과 거짓(True, False)을 의미한다. 예) 3 < 7 -> True/true, 3 > 7 -> False/false

프로그래밍 기본 - 코멘트

  • 코멘트란? 다른 말로 주석이라고 하며, 프로그램 실행 시에 영향을 주지 않는 부분으로 함수나 클래스의 용도를 나타내는 설명을 할 때 주로 쓰인다.
  • 코멘트 사용 이유
    • 복잡한 코드 설명
    • 하다가 만 부분 표시
    • 다른 개발자들과 소통
  • 코멘트 사용 방법
    • C, C++, Java 등에서 사용
      • 한 줄 코멘트 : // 한 줄만 코멘트
        한 줄만 코멘트
        // 한 줄 코멘트
        int a, b;
      • 범위 코멘트 : /* 이 사이 모두 코멘트 */
        범위 코멘트
        /* 여기부터
        int a, b;
        여기까지 모두 코멘트*/
    • Python에서 사용
      • 한 줄 코멘트 : # 한 줄만 코멘트
        한 줄만 코멘트
        # 한 줄 코멘트
        int a, b;

iPhone Application 인터랙티브 씬

  1.  인터랙티브 씬이란? 사용자의 행위에 대한 이벤트를 발생시키는 컨트롤을 의미한다. 대표적인 예로 버튼, 슬라이더, 세그먼티드 컨트롤, 스위치 등이 있다.
    1. 컨트롤이란? 사용자 입력에 반응하는 컨텐츠를 의미한다. 뷰의 자식 클래스이다.
    2. 컨트롤은 사용자 입력에 반응하는 컨텐츠로 이벤트 처리는 코드를 통해서 다루어진다.
      1. 컨트롤 클래스 : UIControl
        1. UIControl에서는 컨텐츠 정렬이나 동작가능한 상태여부 등에 대한 프로퍼티와 메소드를 지원한다.
      2. 컨트롤과 이벤트
        1. 사용자 이벤트
          1. 터치하기
          2. 터치한 채로 컨트롤 영역 내에서 이동
          3. 터치한 채로 컨트롤 영역 밖으로 이동
          4. 터치했다가 떼기
        2. 이벤트 처리
          1. 이벤트를 처리하는 객체인 타겟과 처리 행위인 액션으로 이루어지며, Storyboard와 코드에서 모두 연결가능하다.
          2. 타겟 - 액션 - 이벤트 등록/해제 메소드
            1. 등록 : func addTarget(_ target: Any?, action: Selector, for controlEvent: UIControlEvents)
            2. 해제 : func removeTarget(_ target: Any?, action: Selector, for controlEvent: UIControlEvents)
            3. UIControlEvents 값
              1. touchDown : 컨트롤에 터치가 닿는 이벤트
              2. touchDragExit : 터치가 컨트롤 밖으로 나가는 이벤트
              3. touchUpInside : 터치가 내부에 닿았다가 떨어지는 이벤트
              4. valueChanged : 값이 변경되는 이벤트
  2. 버튼 다루기
    1. 버튼은 사용자가 타겟에 누르는 행위(액션)로 이벤트가 발생되는 컨트롤이다.
    2. 특징
      1. 사용자의 간단한 행위(누르기)
      2. 이미지와 글자 출력
      3. 버튼의 상태별 이미지와 글자
    3. 속성
      1. 버튼 타입 : custom(사용자 정의), System(시스템에서 지원), 미리 정의된 형태의 버튼 등의 종류가 있다.

      2. 버튼 상태별 이름과 이미지로 가능 : 제목만 구성, 이미지로만 구성, 제목과 이미지로 구성

      3. 글자색과 그림자 색, 위치 선정 가능 : 아래 설정항목에서 변경 가능하다.

      4. 버튼의 상태와 사용자 동작
        1. Default : 보통 상태로 사용 가능
        2. Highlighted : 터치 중인 상태
        3. Focused : 포커스된 상태
        4. Selected : 선택된 상태
        5. Disabled : 사용자 터치 이벤트 동작 안 함
    4. 코드로 버튼의 속성을 다룰 수 있다.
      1. 버튼 클래스 : UIButton
      2. 버튼 타입 : UIButtonType이며, custom, system, detailDisclosure 등 설정가능하다.
      3. 버튼 생성과 씬에 배치하는 것은 아래와 같이한다.
        버튼 생성과 씬 배치
        // 커스텀 버튼
        let customButton = UIButton(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
        self.view.addSubview(customButton)

        // 미리 정의된 버튼 타입 설정
        let infoButton = UIButton(type: .infoLight)
        // 버튼 크기는 미리 정해짐
        infoButton.center = CGPoint(x: 50, y: 150)
        self.view.addSubview(infoButton)
      4. 버튼 상태 설정관련 메소드
        1. 버튼 이름 : func setTitle(_ title: String?, for state: UIControlState)
        2. 버튼 이름 색 : func setTitleColor(_ color: UIColor?, for state: UIControlState)
    5. 버튼 이벤트 다루기
      1. Storyboard에서 버튼 이벤트 다루기
        1. 액션 생성 다이얼로그를 이용해서 뷰 컨트롤러에 메소드를 생성한다.

        2. 생성된 액션 메소드는 @IBAction 키워드가 추가된 메소드이다.
        3. 버튼에서 어떤 액션이 발생했을 때 메소드를 실행할지는 아래 설정에서 선택할 수 있다.

      2. 코드로 버튼 이벤트 다루기
        1. 코드에서 이벤트를 다루는 것은 아래 예제로 설명할 수 있다.
          코드로 이벤트 다루기
          override func viewDidLoad() {
             // 버튼 생성 및 씬에 추가
             let button = UIButton(frame: CGRectMake(20, 200, 100, 50))
             self.view.addSubview(button)

             // 버튼의 Touch Up Inside 이벤트 추가
             // 타겟 - 액션 - 이벤트
             button.addTarget(self, action: #selector(handleClickByCode(_:)), forControlEvents:.touchUpInside)
          }

          func handleClickByCode(_ sender : Any){
            // 발생할 이벤트 처리
            print("handleClickByCode")
          }

  3. 사용자 선택(변경) 컨트롤
    1. 사용자 선택을 다루는 컨트롤로 단순 클릭 이벤트보다는 선택된 항목의 변경 시에 이벤트 처리가 이루워진다.
    2. 사용자 선택 컨트롤은 항목 변경이 발생하는 valueChanged 시에 이벤트 처리를 한다.
    3. 종류
      1. 세그먼티드 컨트롤 : 문자와 이미지 기반의 여러 항목 중에 하나를 선택하는 컨트롤이다.
        1. 형태는 아래와 같다.

        2. Storyboard에서 세그먼티드 컨트롤 다루기

        3. 코드로 세그먼티드 컨트롤 다루기
          1. 세그먼티드 컨트롤 클래스 : UISegmentedControl
          2. 문자열이나 이미지를 담은 배열을 생성 : init(items : [Any]?)
          3. 문자열로 생성
            문자열로 세그먼티드 컨트롤 생성
            let items = ["First", "Second", "Third"]
            let segmented = UISegmentedControl(items: items)
            self.view.addSubview(segmented)
          4. 세그먼티드의 타이틀, 이미지 설정
            세그먼티드의 타이틀, 이미지 설정
            // 타이틀 설정
            func setTitle(_ title: String?, forSegmentAt segment: Int)
            // 이미지 설정
            func setImage(_ image: UIImage?, forSegmentAt segment: Int)
          5. 세그먼티드의 타이틀, 이미지 얻기
            세그먼티드의 타이틀, 이미지 얻기
            // 타이틀 얻기
            func titleForSegmentAtIndex(_ segment: Int) -> String?
            // 이미지 얻기
            func imageForSegmentAtIndex(_ segment: Int) -> UIImage?
        4. 세그먼티드 컨트롤의 이벤트 다루기
          1. 주요관심 이벤트는 선택항목의 변경으로 항목 변경 이벤트는 valueChanged이다.
          2. Storyboard에서 이벤트 다루기
            1. ViewController로 연결

          3. 코드에서 이벤트 다루기
            1. addTarget(self, action: #selector(handleSegmentChanged(_:)), forControlEvents: .valueChanged)로 이벤트처리 함수 연결
          4. 발생한 이벤트 다루기
            Sender를 이용하여 선택된 이벤트 처리
            // 이벤트 처리 부분 구현
            func handleSegementChanged(_ sennder: Any) {
              let segment = sender as! UISegmentedControl
            /* func handleSegementChanged(_ sennder: UISegmentedControl) { */
              switch segment.selectedSegmentIndex {
              case 0: print("소나기")
              case 1: print("천둥,번개")
              default: print("맑음")
              }
            }
  4. 씬의 메뉴 구성
    1. 툴바와 툴바 내의 버튼
      1. 툴바: 메뉴를 의미한다.
      2. 툴바 내의 바 버튼 아이템 : 메뉴 내 항목을 의미한다.
      3. 아래와 같이 나타낼 수 있다.

    2. Storyboard에서 툴바 다루기
      1. 툴바 속성은 아래 설정에서 변경할 수 있다.

      2. 툴바 내의 바 버튼 아이템은 아래 설정에서 변경할 수 있다.

    3. 코드로 툴바 다루기
      1. 툴 바 클래스 : UIToolBar
      2. 툴 바 내의 바 버튼 아이템 : items 프로퍼티 (var items: [UIBarButtonItem]?)
        1. 시스템 바 버튼 설정
          init(barButtonSystemItem systemItem: UIBarButtonSystemItem, target: Any?, action: Selector?)
        2. 텍스트 기반의 바 버튼 설정
          init(title: String?, style: UIBarButtonItemStyle, target: Any?, action: Selector?)
        3. 이미지 기반의 바 버튼 설정
          init(image: UIImage?, style: UIBarButtonItemStyle, target: Any?. action: Selector?)
    4. 툴바와 툴 바 버튼 아이템 이벤트 다루기
      1. Storyboard에서 이벤트 다루기
        1. ViewController로 연결

      2. 코드에서 이벤트 다루는 예제
        툴바, 툴바 내의 바 버튼 이벤트 처리 예제
        let toolbar = UIToolbar()
        // 이미지 기반의 바 버튼 생성
        let image = UIImage(named: "button")!
        let imageBarButton = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(handleImageButton(_:)))
        // 텍스트 기반의 바 버튼 생성
        let tb =  UIBarButtonItem(title: "문자열 버튼", style: .plain, target: self, action: #selector(handleEvent))
        // 시스템 바 버튼 생성
        let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDone))

        toolbar.items = [imageBarButton, tb, doneButton]
        self.view.addSubview(toolbar)
  5. 액티비티 인디케이터
    1. 현재 사용자가 발생 시킨 이벤트가 동작 중인지를 알리는 목적의 뷰이다.
      1. 동작이 완료된 후에는 숨기도록 한다.
      2. 다른 뷰보다 상위에 배치한다.
      3. 시작 메소드 : func startAnimating()
      4. 중지 메소드 : func stopAnimating()
      5. 시작/중지 상태 확인 메소드 : isAnimating() -> Bool
      6. 자동 숨기기 프로퍼티 : var hidesWhenStopped: Bool
    2. Storyboard에서 인디케이터 다루기

    3. 코드로 액티비티 인디케이터 다루기
      1. 클래스 : UIActivityIndicatorView -> init(activityIndicatorStyle style: UIActivityIndicatorViewStyle)
      2. 스타일 : whiteLarge, white, gray
    4. 스위치를 이용한 액티비티 인디케이터 사용 예제
      스위치를 활용한 액티비티 인디케이터 사용 예제
      func switchChanged(_ sender: AnyObject) {
         let onOffSwitch = sender as! UISwitch
         if onOffSwitch.isOn {
              // 스위치가 켜지면 액티비티 인디케이터 시작
              indicator.startAnimating()
         } else {
             // 꺼지면 액티비티 인디케이터 중지
             indicator.stopAnimating()
         }
      }

iPhone Application 적응형 UI

  1.  애플리케이션 작성 시에 기기의 종류마다 특화된 레이아웃이 반영이 되는데 이에 맞추어서 자동 적용될 수 있는 레이아웃 작성이 필요하다. 이를 적응형(Adaptive) UI 라고 한다.
    1. 사이즈 클래스 : 기기마다 가로/세로 방향 시에 대해서 적용되는 레이아웃으로 Regular와 Compact가 있으며 아래 그림은 이를 나타낸 것이다.

    2. Regular는 채워진 형태를 의미하고, Compact는 일부만 채워진 형태를 의미한다.
    3. 표현법 : w(가로), h(세로), C(Compact), R(Regular)
    4. 아이폰의 사이즈 클래스는 아래와 같다.

    5. 아이패드의 사이즈 클래스는 아래와 같다.
  2. Any 사이즈
    1. Compact, Regular 가 모두 적용된 상태이다.
    2. 표현법 : wAny hAny
    3. 모든 기기와 모든 방향이 반영된다.
  3. Storyboard에서 작성
    1. 뷰에 사이즈 클래스를 적용하는 방법은 씬 UI 요소 Installed에서 표시하며 조건이 맞지 않으면, 씬에 나타나지 않는다.
    2. 설정 방법은 아래와 같다. (+ 버튼을 눌러서 추가, 기본은 Any-Any 설정)

  4. 코드에서 작성
    1. 코드에서 작성 시에는 Traits 클래스를 통해서 기기에 대한 정보를 얻을 수 있으며 이를 활용하여 기기마다 가로, 세로 방향 사이즈 클래스를 반영한다.
    2. Traits 모음 클래스 : UITraitCollection
    3. Traits 정보를 가진 클래스 : UITraitEnviroment
    4. Traits 정보 얻는 방법은 아래와 같다.
      Traits 정보를 얻는 방법

      // UIView 에서 얻는 방법

      // 가로 사이즈 클래스

      self.traitCollection.horizontalSizeClass

      // 세로 사이즈 클래스

      self.traitCollection.vericalSizeClass

    5. Traits 변경 정보 얻는 메소드 : func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
    6. 코드로 제약조건 활성화/비활성화
      1. 활성화하는 메소드 : class func activate(_ constraints: [NSLayoutConstraint])
      2. 비활성화하는 메소드 : class func deactivate(_ constraints: [NSLayoutConstraint])
      3. Traits와 제약조건 사용하는 예제코드는 아래와 같다.
        Traits와 제약조건 사용하는 예제코드

        var verticalConstraints : [NSLayoutConstraint]!

        var horizontalConstratins : [NSLayoutConstraint]!

        override func viewDidLoad() {
        verticalConstraints = [ NSLayoutConstraint
        (...), NSLayoutConstraint(...) ] horizontalConstratins = [ NSLayoutConstraint(...), NSLayoutConstraint(...) ] // 가로 상태

        self.view.addConstraints(verticalConstraints)

        self.view.addConstraints(horizontalConstratins) 

        }

        override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { switch (self.traitCollection.horizontalSizeClass, self.traitCollection.verticalSizeClass) { case (.compact, .regular): // wC hR

        NSLayoutConstraint.deactivate(horizontalConstratins)

        NSLayoutConstraint.activate(verticalConstraints)
        case (.compact, .compact), (.regular, .compact): 

        // wR hC

        NSLayoutConstraint.activate(horizontalConstratins) NSLayoutConstraint.deactivate(verticalConstraints)


iPhone Application 스택뷰

  1. 스택뷰란? iOS9부터 반영된 뷰 컨테이너로 테이블 형식으로 컨텐츠를 구성할 때 주로 사용된다.
    1. 스택뷰는 아래와 같은 형태를 가진다.

    2. Storyboard에서 스택뷰 작성
      1. 레이아웃 메뉴에서 선택하여 적용할 수 있다.

      2. 스택뷰는 가로형과 세로형으로 사용되며, 이 둘을 중첩해서 사용이 가능하다.

      3. 스택뷰의 속성
        1. 스택뷰 내 하위뷰의 위치 설정이 가능하며 가로형과 세로형에 따라서 설정 옵션이 다르다.

        2. 스택뷰 내 하위뷰의 크기 설정이 가능하며 제약조건 사용으로 상세 설정을 한다.

    3. 코드로 스택뷰 작성
      1. 스택뷰 클래스 : UIStackView
      2. 하위뷰에는 UIView를 상속받는 View들이 올 수 있으나 거의 UILabel 이 사용된다.
      3. 스택뷰를 이용한 예제는 아래와 같다.
        스택뷰 예제 코드

        let titleLabel = UILabel()

        titleLabel.text = "제목
        let titleLabelConstraint = NSLayoutConstraint(item: titleLabel, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50) titleLabel.addConstraint(titleLabelConstraint)

        let title = UILabel() title.text = "스타워즈7"

        let titleStackView = UIStackView(arrangedSubviews: [titleLabel, title]) titleStackView.distribution = .fillProportionally titleStackView.alignment = .fill
        titleStackView.spacing = 2

        let movieStackView = UIStackView(arrangedSubviews: [titleStackView, directorStackView, actorStackView])
        movieStackView.axis = .vertical 

출처 : https://tacademy.skplanet.com/