친구와 카톡하다가

'심심한데 NFT나 한번 해보자'고 해서 시작한 프로젝트

간단하게 이미지 몇 개만 조합해서 제너레이팅을 한 다음 민팅하는 것까지

딱 거기까지가 목표인 토이 프로젝트이다. 

 

그림 그리는 건 나중에 다 준비되면 하기로 하고, 제너레이팅 프로그램을 완성하는 게 우선이었다.

그 프로그램을 내 손으로 짜는 건 어림도 없고 조코딩님의 유튜브 채널을 활용하였다.

 

https://github.com/HashLips/hashlips_art_engine

 

GitHub - HashLips/hashlips_art_engine: HashLips Art Engine is a tool used to create multiple different instances of artworks bas

HashLips Art Engine is a tool used to create multiple different instances of artworks based on provided layers. - GitHub - HashLips/hashlips_art_engine: HashLips Art Engine is a tool used to create...

github.com

항상 '이런 거 해봐야겠다'하고 찾아보면 무조건 있다는 게 신기하다.

저건 이미지 제너레이팅 해주는 코드의 깃헙이다. 

 

해당 깃헙을 보면 canvas api와 node.js를 사용한다고 한다.

canvas는 뭔지 잘 모르겠지만 node.js는 깔아주어야만 한다고 하니, node.js를 깔아줬다.

처음에는 최신버전으로 깔았는데, 나중에 실행이 안되는 것이다.

그래서 혹시나 해서 버전다운 시켜서 현재는 10.18.0이 내 맥에 설치된 상태이다. 

 

나는 node.js를 내 손으로 설치했기 때문에 npm install을 커맨드창에 입력했다. 

그리고 node index.js를 커맨드에 입력해 실행시켰다.

 

그랬더니 역시나 에러가 발생한다. 

에러 코드가 ELIFECYCLE...?

구글링 해보니 약간 캐시문제에서 발생하는 에러인 듯 했다. 

lock.json을 지우긴 했지만 npm cache clean -force 명령어는 애초에 먹질 않았다. 

 

유명한 깃헙 코드인데 syntaxError라니...뭐가 문제인지 감조차 안온다.

구글링해보니 버전 업데이트 하라고 나와서 또 버전 업데이트를 해본다...

후....

node 16버전에서 에러가 뜬다는 첩보를 발견했다.

그리고 stack overflow에서 찾아보니 뭔 파이썬 버전도 맞춰보라 그래서 일단 맞춰줘봤다. 

 

엄청 욕하면서 '아 이거 왜 안되냐 도대체'이러고 있었는데

파이썬이랑 node 버전 다운그레이딩 하고 딱 커맨드창에 npm install쳤다.

그리고 나서 node index.js 넣었는데 웬걸

 

"해치웠다...."

파이썬이랑 연관되어있는 그런거였나 싶다. 해당 프로젝트에 파이썬 파일은 없어서 상상도 못했다. 

 

 

결론

오늘 발생했던 에러들을 요약하면 다음과 같다. 

layerObj.options?.["displayName"] != undefined

SyntaxError: Unexpected token .

npm ERR! code ELIFECYCLE

npm ERR! gyp info

 

SyntaxError는 node.js의 버전 문제인 것 같다.

해당 코드가 작성되던 시절의 V10도 아니고 현재 가장 최신인 V16을 써도 안되는 것 같다.

다만 해외 커뮤니티에서 node는 V14 이상에서 해당 syntax에러를 수정했다고 하는 것 같았다.

그래서 node 버전은 14번대로 맞췄다. 

 

또한 gyp 에러는 파이썬 쪽에서 발생한 것 같다.

해당 코드에서 node-gyp를 사용했나본데, 이는 최신버전에서 지원하지 않는 모양이다.

node를 공부해보진 않아서 gyp가 무엇인지는 모르겠으나, 구식 방식이기에 파이썬 최신버전에서는 지원하지 않나보다.

 

삽질하던 분들! 우리가 같은 에러를 겪고 있다면

node를 14버전대로 맞추고 python을 2.7버전대로 맞춰보도록해요ㅎ

이놈의 티스토리는 사진 못올리는 버그를 아직도 수정하지 못했나보다. 아직도 사진이 올라가지 못한다. 

이렇게 블로그 포스팅을 못한다고 공부를 안할 수는 없다는 생각에 일단 전광판 앱을 다 만들어봤다. 

 

[SettingViewController]

import UIKit

protocol LEDBoardSettingDelegate: AnyObject {
    func changedSetting(text: String?, textColor: UIColor, backgroundColor: UIColor)
}

class SettingViewController: UIViewController {

    @IBOutlet weak var orangeButton: UIButton!
    @IBOutlet weak var blueButton: UIButton!
    @IBOutlet weak var blackButton: UIButton!
    @IBOutlet weak var greenButton: UIButton!
    @IBOutlet weak var purpleButton: UIButton!
    @IBOutlet weak var yellowButton: UIButton!
    @IBOutlet weak var textField: UITextField!
    
    weak var delegate: LEDBoardSettingDelegate?
    var ledText: String?
    var textColor: UIColor = .yellow
    var backgroundColor: UIColor = .black
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.configureView()
    }
    
    private func configureView(){
        if let ledText = self.ledText{
            self.textField.text = ledText
        }
        self.changeTextColor(color: self.textColor)
        self.changeBackgroundColorButton(color: self.backgroundColor)
    }

    @IBAction func tabTextColorButton(_ sender: UIButton) {
        if sender == self.yellowButton {
            self.changeTextColor(color: .yellow)
            self.textColor = .yellow
        }else if sender == self.purpleButton{
            self.changeTextColor(color: .purple)
            self.textColor = .purple
        }else{
            self.changeTextColor(color: .green)
            self.textColor = .green
        }
    }
    
    @IBAction func tabBackgroundColorButton(_ sender: UIButton) {
        if sender == self.blackButton{
            self.changeBackgroundColorButton(color: .black)
            self.backgroundColor = .black
        }else if sender == self.blueButton{
            self.changeBackgroundColorButton(color: .blue)
            self.backgroundColor = .blue
        }else{
            self.changeBackgroundColorButton(color: .orange)
            self.backgroundColor = .orange
        }
    }
    
    @IBAction func tabSaveButton(_ sender: Any) {
        self.delegate?.changedSetting(
            text: self.textField.text,
            textColor: self.textColor,
            backgroundColor: self.backgroundColor)
        self.navigationController?.popViewController(animated: true)
    }
    
    private func changeTextColor(color : UIColor){
        self.yellowButton.alpha = color == UIColor.yellow ? 1 : 0.2
        self.purpleButton.alpha = color == UIColor.purple ? 1 : 0.2
        self.greenButton.alpha = color == UIColor.green ? 1 : 0.2
    }
    private func changeBackgroundColorButton(color:UIColor){
        self.blackButton.alpha = color == UIColor.black ? 1 : 0.2
        self.blueButton.alpha = color == UIColor.blue ? 1 : 0.2
        self.orangeButton.alpha = color == UIColor.orange ? 1 : 0.2
    }
}

[ViewController]

import UIKit

class ViewController: UIViewController, LEDBoardSettingDelegate {

    @IBOutlet weak var contentsLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        self.contentsLabel.textColor = .yellow
    }
    override func prepare(for segue:UIStoryboardSegue, sender: Any?){
        if let settingViewController = segue.destination as? SettingViewController{
            settingViewController.delegate = self
            settingViewController.ledText = self.contentsLabel.text
            settingViewController.textColor = self.contentsLabel.textColor
            settingViewController.backgroundColor = self.view.backgroundColor ?? .black
        }
    }
    func changedSetting(text: String?, textColor: UIColor, backgroundColor: UIColor) {
        if let text = text{
            self.contentsLabel.text = text
        }
        self.contentsLabel.textColor = textColor
        self.view.backgroundColor = backgroundColor
    }
}

크게 이렇게 두 가지로 코드를 나눌 수 있을 것 같다. 

사진이 안올려져셔 어떻게 완성본을 보여줄 방법은 없지만, 이렇게 코드라도 남길 수 있으면...

 

이번 프로젝트는 뭔가 엄청 쉬워보일 것 같았는데 어려웠다. 핵심 기능이라 한다면

1.  앱 내에서 페이지를 왔다갔다 하는 방법

2. 페이지끼리 데이터를 주고받는 방법

3. 간단한 UI 구성 방법

정도로 나눌 수 있을 것 같다. 일단 기억에 남는 건 이정도이지만, 사실 이 중에 완벽하게 이해한 것은 하나도 없다. 그냥 인강을 따라했을 뿐...이제 이 3가지 요소를 집중적으로 따로 공부해서 이 프로젝트의 코드를 복습해야겠다.

원래 이런 느낌으로 산 인강이긴 하다. '일단 따라해보고 모르는 건 내가 따로 공부하자'가 원래 이 강의를 샀을 때의 내 마인드였지만 이렇게까지 이해를 못할 줄은 몰랐다. 

후....이 간단해보이는 앱 하나 만들기가 이렇게 어려운지 몰랐다. 

 

오늘은 저번에 이어서 글자와 배경의 색을 바꾸는 것을 해보려고 한다.

두 함수를 추가해준다. 첫번째 changeTextColor 함수는 전광판의 텍스트 색을 변경하였을 때 호출될 것이며 UIClolor의 color형식의 변수를 매개변수로 전달받는다. 이 함수를 호출할 때에는 색깔을 명시하여 호출하는 것이다. 그리고 매개변수에 따라서 버튼의 알파값을 다르게 하여, 어떤 색이 선택되었는지 알 수 있게 하려고 한다. 삼항 연산자를 사용하여 조금 간단하게 표현할 수 있었다. 알파는 1에 가까울수록 불투명하고 0에 가까울수록 투명해진다. 따라서, 선택되었다면 알파값이 1이 되어 불투명해지고, 나머지는 알파값이 0.2가 되어 약간 투명해지는 것이다. 

두번째 함수인 changeBackgroundColorButton도 비슷하다. 이 함수는 전광판의 배경을 바꾸는 버튼을 누르면 호출될 것이다. 똑같이 알파값을 변경하여 어떤 색이 선택되었는지 알 수 있게 한다. 

 

위에서는 '버튼이 눌리면 어떻게 할래'를 설정했으니 이제는 '버튼이 눌렸나'를 알아채는 함수를 만들었다. 큰 내용 없이, 그냥 버튼이 눌리면 어떤 버튼이 눌렸는지 확인하고 그 눌린 버튼에 따라서 텍스트 컬러 버튼이나 배경컬러 버튼의 알파값을 바꾸는 함수를 호출했다. 전 단계에서 만든 그 함수 말이다!

 

---------------------------------------------------------------------------------------------------------------------

임시저장해놨는데 사진이 업로드되지도 않고 임시저장해놨던 사진을 불러올 수도 없다. ㅋ....지금까지 했던 게 사라졌다. 

 

이번엔 전광판 앱을 만들어보려고 한다.

관련해 쓰이는 개념으로는 앱의 각 페이지로 이동하는 기능, 색상을 선택하고 변경하는 기능 등이 핵심인 것 같다. 

페이지 간 이동기능을 구현해야하므로 먼저, Navigation Controller 하나를 추가해준다. 

그럼 아래와 같이, Navigation controller 뿐만 아니라 다른 페이지가 루트 페이지로 생성되는데 이를 지워주도록 하자. 우리는 루트페이지를 우리가 만들 것이기 때문에 필요가 없다. 

해당 페이지를 지워줬다면, 이렇게 두 개의 뷰만 남게 된다. 스토리보드의 진입점을 네비게이션 컨트롤러로 변경해준다. 변경하는 방법은 뷰 옆의 조그마한 화살표를 끌어다가 Navigation Controller옆으로 옮겨주면 된다. 

그 다음은 루트 뷰 컨트롤러를 설정해줄 것이다. Navigation controller의 뷰에서 오른쪽 마우스를 클릭한 후 끌면 아래와 같이 실선이 표시되는데, 이 실선을 옆의 뷰로 이동시켜준 후 드랍하면 여러 옵션이 뜬다. 그 옵션들을 세그웨이 메뉴라고 하는데, 우리는 오른쪽의 뷰를 루트 뷰로 활용할 것이기 때문에 root view controller를 클릭해준다. 

그리고 Label을 하나 추가해준 다음, 화면의 정 중앙에 오도록 설정해주자. 

아래와 같이 두 allign에 들어가서, Horizontally in Container와 Vertically in Container를 체크해주면 해당 레이블에 제약이 걸리게 되어 화면의 정 중앙에 위치하게 된다. 

우리는 전광판 앱을 만드는 것이기 때문에 글자의 크기가 커야하며 가운데에 정렬되어있어야 한다. 레이블 선택 후 우측 메뉴에서 바꿔주도록 하자. 글자도 잘 보이게 노란색으로 바꿔주고 말이다. 또 해당 레이블의 상위 뷰 또한 배경을 검은색으로 바꿔주도록 하자. 

뷰 상단에 Bar button item을 하나 추가한 후, 텍스트를 '설정'으로 바꾸어주자. 

이제 설정 페이지를 만들어야하므로 다시 한 번 view controller를 추가해주자. 

아까 한 것처럼, bar button item을 우클릭으로 끌어서 오른쪽 뷰에 내려놓으면 Action segue가 뜨게 된다. Show를 선택한다. 

화면 상단의 중앙 부분을 더블클릭하면, 보다시피 텍스트를 넣을 수 있다. 여기는 설정 페이지이므로 '설정'텍스트를 넣어주었다. 

그리고 이렇게 Label과 TextField를 추가해주자. 

TextFiled는 한 줄의 텍스트만 출력할 수 있다. 여러 줄의 텍스트를 넣으려면, Text view 오브젝트를 사용하면 된다. 우리는 긴 텍스트를 넣을 게 아니기 때문에 TextFiled로 충분하다. 

UI stack view를 통해서 Label과 Text field를 묶어줄 것이다. UI stack view를 사용하면, 일정한 규칙에 따라 일정한 배열을 따르게 할 수 있다. 커맨드 키를 누른 후 해당 두 객체를 클릭하면 두 객체가 동시에 선택된다. 이 상태에서 우측 하단에 Embed in View를 누르면 Stack view 옵션이 보인다. 클릭해주도록 하자. 

또한 두 오브젝트 사이의 간격이 쓸데없이 넓으니 15로 줄여주고 제약을 걸어서 두 오브젝트의 크기를 정해주도록 하자. 나는 상,좌,우의 마진을 24로 했다. 

또 나는 Text field가 stack안에 꽉 찼으면 좋겠다. 좌우 마진을 0으로 해주면 Text field가 좌우로 꽉차게 될 것이다. 

Label의 텍스트는 '전광판에 표시할 글자'로 설정해주고 Text field의 Placeholder도 똑같이 바꿔주었다. 

이제 전광판 글자의 색상을 변경하는 버튼을 넣어줄 차례이다. Label을 하나 추가하고 '텍스트 색상 설정'이라고 한 후, 버튼을 3개 더 추가하고 각각 '노란색', '자주색', '초록색' 텍스트를 넣어주었다. 또한 버튼 세 개를 하나의 stack view로 묶어주었다. 그 후에는 버튼 세 개의 스택 뷰와 '텍스트 색상 설정' 레이블을 또 하나의 stack view로 묶어주었다. 

레이블과 버튼 3개를 묶은 전제 stack view의 spacing을 15로 설정해주었다. 

또한 제약조건을 걸어서 그 크기를 설정해 주었다. 상단은 35, 좌우 24로 설정하였다. 

배경색을 바꿔주는 옵션도 넣어야하니, 똑같은 stack view를 복사해서 놔주고 버튼도 하나 놓아주도록 하자. 각 버튼도 텍스트를 바꿔주었다.

여기는 좀 어이없다. 아니 강의에서는 저렇게 이미지 파일을 넣으라고 해놓고서는 이미지 파일을 나한테 주지는 않았다. 그럼 나같은 수강생들은 어디서 구해...? 하지만 저건 결국 버튼의 그림일 뿐일테니 생략하고 넘어가기로 했다. 핵심 기능은 화면 전환하는 것일 테니까 말이다.

 

참고로 저렇게 이미지 파일을 3개나 넣는 것은 화질이 깨지지 말라고 넣는 것이다. 아이폰이던 아이패드던 맥이던간에 어떤 기기에서 앱을 실행시켜도 깨지지 않는 해상도를 보장하는 것이다. 

이제 전광판의 본격적인 기능을 구현할 시간이다. new file을 눌러서 Cocoa Touch 파일을 하나 생성한다.

파일을 생성한 후, 다시 메인 스토리보드로 돌아가서 해당 파일을 설정 뷰와 연결을 시켜줘야 한다. 우측 상단에 보면 Class라고 써진 곳에 방금 생성한 Cocoa Touch Class의 파일 이름을 적어준다. 자동 완성이 되면서 연결이 완료된다. 

모든 화면의 UI오브젝트들은 Outlet 변수와 Action 함수로 각 클래스에 정의를 시켜줄 것이다. Assistant를 눌러주면 코드를 수정할 수 있는 보조모니터가 나오게 된다. Assistant 추가 후 Label을 우클릭하여 드래그앤드롭으로 연결시켜준다. 

 

여기서 Outlet 변수와 Action함수가 무엇일까? 강의에서는 나오지 않았지만 알아먹을 수 없는 말이라서 검색해보았다. 그리 어려운 용어는 아니었다.

Outlet변수는 스토리보드에서 만들어놓은 오브젝트에 접근하여 컨트롤하기 위해 변수에 바인딩 해놓은 오브젝트 변수를 말한다. 한마디로, 그냥 오브젝트를 컨트롤할 수 있도록 할당해놓은 변수라는 것이다. 

또한 Action함수는 어떠한 액션이 일어나게 만드는 함수라는 뜻이다. 스토리보드의 어떤 버튼을 누르면 어떤 Action이 일어나게! 하는 함수라는 뜻이다. 

이름은 contentsLabel로 설정해주었다. 처음 글자 색은 노란 색이기 때문에 yellow로 초기설정을 진행해준다. 

이제 textField를 포함한 모든 오브젝트를 연결해줄 차례이다. 색 버튼을 모두 연결해주었다. 

다음은 Action함수를 만들어줄 차례이다. 노란색 버튼과 자주색 버튼, 초록색 버튼을 모두 우클릭 드래그앤드롭 해서 한 액션함수에 연결해주면 된다. 이렇게 하면 저 세 버튼 중 아무거나 눌러도 해당 Action함수가 호출된다. 어떤 버튼을 눌렀는지는 sender parameter를 통해 알 수 있기 때문에 효율적인 코딩이 가능해진다. 

 

배경색을 결정하는 버튼들 또한 하나의 Action함수로 묶어준다. 

 

추가적으로 저장버튼도 tabSavingButton으로 액션함수화 시켜준다. 

왼쪽: 글자색 액션함수 만들기, 오른쪽: 배경색 액션함수 만들기

 

하....다음편에 계속...

+ Recent posts