윈도우 10에 Ubuntu 설치 및 초기설정

윈도우 10에서 Bash를 지원하기로 했다는 소식을 들었던거 같은데 최근에 윈도우에서 사용해볼 일이 생겨서 설치하면서 생겼던 문제들과 해결방법을 적어보려고 합니다.

윈도우에서 Bash 지원하는것은 윈도우에서 리눅스를 실행하는 방식으로 변경되었습니다. 그래서 윈도우 10 최신 버전을 설치하고 윈도우 스토어에서 Ubuntu를 검색해서 설치하면됩니다. open SUSE도 설치 할 수 있지만 Ubuntu를 추천하고 있습니다.

설치 방법은 간단합니다. 윈도우 스토어에서 linux 혹은 ubuntu로 검색해서 나온 앱 목록에서 Ubuntu를 “다운로드” 하면 설치가 진행됩니다.

설치가 완료된후 Ubuntu 앱 아이콘을 시작메뉴에서 더블 클릭하면 터미널이 실행되면서 우분투로 진입합니다.

처음 실행하는 경우 위 화면과 같이 우분투에서 사용할 계정이름을 입력하라고 나오는데 여기서 사용하고 싶은 이름을 입력하면됩니다. 윈도우의 사용자이름과 동일할 필요가 없습니다.

생각보다 쉽게 우분투가 설치되었습니다 !

이제부터는 우분투를 잘 사용하기 위한 초기 설정에 대해서 주제별로 알아보겠습니다. 아래의 설정을 안해도 사용하는데 문제는 없지만 해두면 좋습니다.

터미널 색상 변경하기

윈도우에서 처음으로 우분투를 실행하고 ls 명령어나 vi 로 문서를 열어보면 파란색이 눈에 잘 안보이는것을 경험하게 됩니다. 단순히 불편한 문제를 넘어서 특정 문자열들이 잘 안보이고 이로인해 문제가 발생할 여지가 있습니다.

Ubuntu를 실행하고서 해당 앱 왼쪽 상단의 우분투 로고에 마우스 오른쪽 버튼을 누른다음 속성을 선택합니다.

속성창에서 “색”을 선택합니다.

“색”에서 중간쯤 보면 여러가지 색상이 박스로 표시되어 있는데요. 이 하나하나의 색을 슬롯이라고 했을때 총 16가지의 색상 슬롯이 있습니다. 이 각각의 슬롯을 선택하고 오른쪽 위에 “선택한 색 값”을 아래와 같이 변경합니다.

Slot 1: Red: 48, Green: 10, Blue: 36
Slot 2: Red: 52, Green: 101, Blue: 164
Slot 3: Red: 78, Green: 154, Blue: 6
Slot 4: Red: 6, Green: 152, Blue: 154
Slot 5: Red: 204, Green: 0, Blue: 0
Slot 6: Red: 117, Green: 80, Blue: 123
Slot 7: Red: 196, Green: 160, Blue: 0
Slot 8: Red: 211, Green: 215, Blue: 207
Slot 9: Red: 85, Green: 87, Blue: 83
Slot 10: Red: 114, Green: 159, Blue: 207
Slot 11: Red: 138, Green: 226, Blue: 52
Slot 12: Red: 52, Green: 226, Blue: 226
Slot 13: Red: 239, Green: 41, Blue: 41
Slot 14: Red: 173, Green: 127, Blue: 168
Slot 15: Red: 252, Green: 233, Blue: 79
Slot 16: Red: 238, Green: 238, Blue: 238

그리고 이제 왼쪽 상단의 “화면 텍스트” 를 선택하고 슬롯 16을 선택합니다. 이런식으로 “팝업 배경”은 슬롯 16, “화면 배경”, “팝업 텍스트”는 슬롯 1로 선택하고 확인 버튼을 클릭합니다.

터미널 색상이 이쁘게 바뀐게 확인되나요? 가독성이 떨어지던 파란색도 이제 잘 보입니다 ^^

디렉토리(폴더)의 배경 색상지우기

위에 ls 결과를 보면 뭔가 마음에 들지 않는게 있지 않나요? 그건 바로 폴더 이름에 녹색 배경이 들어가는 겁니다. 가독성도 떨어지고 굳이 폴더를 이렇게 중요하게 표시해야되나 싶을텐데요.

배경에 색상이 들어가게 보이는건 해당 폴더의 권한이 모든 사용자가 폴더에 쓰기 권한을 가졌기 때문입니다. 모든사용자에게 쓰기 권한이 열려있을 경우 다르게 보이게 하려고 이런식으로 표시됩니다.

그런데 윈도우 우분투를 사용하는 경우 폴더를 만들거나 git clone 을 하는등 모든 폴더를 만들때마다 쓸데없이 모든 사용자들이 쓰기가능한 상태로 만들기 때문에 보안측면에서도 안좋고 보기에도 안좋은 문제가 발생합니다.

.bashrc 파일을 열어서 아래와 같이 umask를 설정하면 해결됩니다.

if [[ "$(umask)" == '000' ]]
then
   umask 022
fi

이제 우분투를 종료후 재시작하거나 쉘을 재시작 하면 폴더를 생성할때 권한이 제대로 설정되고 ls 했을때 배경색이 보이지 않는것을 확인할 수 있습니다. 기존에 이미 생성된 폴더의 경우는 권한을 변경해야합니다.

참고로 이 설정은 우분투에서만 동작하며 윈도우 파일시스템(/mnt/c 같이 윈도우 파일시스템 마운트 된곳) 밑에서는 여전히 777 퍼미션으로 폴더가 생성됩니다.

마지막으로

윈도우에서 우분투가 된다고 해서 기대를 많이 했지만 맥에 익숙해져있던 제가 윈도우에서도 개발을 쉽게 하기에는 아직 많은 장벽들이 있네요. 키보드도 그렇고 윈도우에서 직접 실행할때보다는 덜하지만 개발툴 설정등… 위에 정리한것 말고도 여러가지 문제들이 있는데 차차 블로그 포스트를 통해 전달드리겠습니다.

참고자료

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 삽질 : 스토리보드만 열면 Xcode가 죽음

Xcode 9으로 업그레이드 하고나서 개발을 시작하려고 하는데 Storyboard 혹은 xib 파일만 열면 Xcode가 종료되서 개발을 할 수 없는 상황이 발생했습니다.

크래시 로그중 일부는 다음과 같이 표시됩니다.

ProductBuildVersion: 9B55
ASSERTION FAILURE in /Library/Caches/com.apple.xbs/Sources/IDEFrameworks/IDEFrameworks-13532/IDEKit/Application/IDEDocumentController.m:2223
Details:  There can only be one document registered with a given URL. newDocument:<IBStoryboardDocument: 0x7fcba0fc7000>(  0   -[IDEEditorDocument init] (in IDEKit)
  1   -[IBDocument init] (in IDEInterfaceBuilderKit)

혹은 다음과 같은 에러

ProductBuildVersion: 9B55
ASSERTION FAILURE in /Library/Caches/com.apple.xbs/Sources/IDEInterfaceBuilder/IDEInterfaceBuilder-13529/InterfaceBuilderKit/IssueProvider/IBIssueProvider.m:72
Details:  castedInstance should be an instance inheriting from IBDocument, but it is IDEPegasusSourceEditor.SourceCodeDocument

프로젝트는 아무것도 수정한적이 없고 그저 Xcode 9을 오랫만에 열었을 뿐인데 이런 오류가 발생해서 당황스러웠습니다.

이런 저런 글을 보면 소스코드 편집기로 열어서 nil key 라고 된걸 지워라 Tab Bar Item 관련된걸 지우라는둥 여러가지 이야기가 있었지만 모두 동작하지 않았습니다. 그리고 제 상황에서는 모든 xib, 스토리 파일이 열리지 않았기 때문의 위의 해결 방법들은 해당하지 않았을것 같았죠.

해결 방법은 간단합니다.

Xcode 에서 CMD + Shift + O 키를 눌러서 스토리보드 파일이나 인터페이스 빌더 파일을 열면 Xcode가 종료되지 않고 정상적으로 실행됩니다 =_=/ 한번 열리고 나면 다른 모든 스토리보드나 인터페이스 빌더 파일을 열어도 오류없이 잘 열립니다. 네비게이터에서 스토리보드를 여는거에 문제가 있나봅니다.

삽질이란 다 이런식으로 끝나는거죠 뭐

참고정보

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

옛날 컴퓨터의 터미널 효과 앱 – Cool Retro Terminal

오래된 컴퓨터의 터미널 효과를 내는 앱이 있어서 소개합니다. Cool Retro Terminal 은 오래전 CRT 모니터를 사용하던 시절의 터미널로 보던 화면을 재현했습니다. IBM, Apple II 등 여러가지 화면을 지원하는데요. macOS, Linux 에서 설치 할 수 있습니다.

실행 화면은 다음과 같습니다 🙂 옛날 컴퓨터를 보는것 같아요

화면에 보이는 스타일은 몇개가 미리 정해져 있는데 다음과 같은 항목이 있습니다.

프로필은 내가 직접 만들 수 도 있는데요. 여러가지 옵션을 바꿀수 있습니다.

의외로 한글이 지원됩니다 ^^ HyperTerm 의 경우에는 한글 지원이 안되는데 Cool Retro Term은 잘됩니다.

안되는것도 있는데 몇가지 자주 사용하는 단축키가 안됩니다

  • vi에서 Ctrl + d, Ctrl + u 로 위아래로 이동하는것
  • Ctrl + l, Cmd + k 로 현재 화면의 내용을 clear 하는것

아마 이외에도 안되는 단축키들이 몇개 있을것 같아요.

개발하다가 지치고 기분전환이 필요할때 Cool Retro Terminal 한번 사용해보세요.

동작영상이 궁금하시면 제가 직접 찍은 유튜브 영상을 참고해주세요

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 삽질 : iOS 11에서 SearchBar 높이 변경 이슈

iOS 11에서 UISearchBar의 높이가 기존 44에서 56으로 변경되었습니다. 대부분의 경우 큰 문제는 없지만 네비게이션바와 함께 사용하는 경우 높이 변경으로 인해 문제가 발생했습니다.

네비게이션바의 titleView에 UISearchBar를 사용하는 경우 네비게이션바의 높이인 44를 넘어가게 되어 검색이후의 화면에서 네비게이션바 하단에 검정색 공백이 생기는 문제가 있었는데요.

현상

아래 개발화면의 스샷은 왼쪽의 첫번째 화면의 네비게이션바에 UISearchBar가 있고 두번째 화면이 검색결과를 눌렀을때 이동하는 화면입니다.

첫번째 화면과 두번째 화면의 네비게이션 바의 높이가 달라진것을 볼 수 있고 이로 인해 두번째 화면의 네비게이션바 하단에 검정색 공백이 추가됩니다.

이는 앞서 이야기 했던 UISearchBar의 높이가 44에서 56으로 변경된것에 따른 영향으로 첫번째 화면에서는 네비게이션바의 높이가 UISearchBar의 높이에 맞게 56으로 늘어났고 두번째 화면에서는 이전화면의 높이인 56픽셀보다 적은 44픽셀의 네비게이션바를 보여주면서 12픽셀 만큼의 검정색 공백이 생기는 문제입니다. iOS에서 똑똑하게 변경해주면 되는데 이게 처리가 안되더라구요.

해결

우선 첫번째 실패한 해결방법은 SearchBar의 높이를 44로 강제 설정하는것입니다. frame 정보를 변경하는것으로는 안되고 heightAnchor 값을 설정해줘야합니다. 아래 코드를 적용합니다.

if #available(iOS 11.0, *) {
    searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
}

이렇게 하면 높이가 44로 잘 변경되지만 UISearchBar에 글자를 입력하기위해 포커스를 가져가면 UISearchBar가 살짝 내려오는 문제가 생깁니다. 이 문제는 어떻게 해결할 방법이 없더라구요 ㅜㅜ

살짝 내려오니까 내가 뭔가를 수정하면 되겠구나 싶어서 삽질을 계속해서 해봤지만 무슨수를 써도 안되더라구요 ㅜㅜ

최종적으로 해결한 방법은 UISearchBar를 감싸는 UIView를 하나 만들고 이 뷰의 frame 값을 강제로 지정하는 것입니다.

class SearchBarContainerView: UIView {
    let searchBar: UISearchBar
    init(customSearchBar: UISearchBar) {
        searchBar = customSearchBar
        super.init(frame: CGRect.zero)
        addSubview(searchBar)
    }

    override convenience init(frame: CGRect) {
        self.init(customSearchBar: UISearchBar())
        self.frame = frame
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        searchBar.frame = bounds
    }
}

let searchBarWrapper = SearchBarContainerView(customSearchBar: searchBar)
searchBarWrapper.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 44)
navigationItem.titleView = searchBarWrapper

이렇게 하면 해결! 첫번째 방법이 깔끔하고 정석대로 해결하는것 같은데 제대로 동작하지 않으니 두번째 방법을 사용할 수 밖에 없었습니다. iOS 개발하다 보면 이런식으로 해결해야 하는 경우가 있더라구요.

삽질지수

이번 삽질의 삽질 지수는 1입니다. (삽질지수 범위 1 5 단계) 처음부터 원인을 알고 있었고 해결방법만 찾으면 되는데 인터넷에 있는 방법중에 되는게 있어서 다행이었죠.

이거 때문에 하루가 그냥 사라졌네요 ㅜㅜ 첫번째 방법으로 깔끔하게 해결하려고 계속 삽질 했던게 원인이었어요. 그래도 해결할 수 있어서 다행이었어요. 해결하지 못하면 네비바 아래에 검색바를 위치시켜야되서 내키지 않았거든요.

참고자료

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

GitHub 프로젝트의 의존성 정보를 알려주는 기능 추가

10월 10 ~ 12일 까지 샌프란시스코에서 GitHub Universe라는 GitHub 행사가 진행중인데요. 프로젝트에서 사용중인 외부 라이브러리 정보들을 보여주는 기능이 추가됬습니다.

GitHub에 프로젝트의 의존성 정보를 보여주는 “Dependency graph” 기능을 추가했으며 이 페이지에는 프로젝트에서 사용중인 외부 라이브러리 정보를 표시합니다. 프로젝트 페이지의 “Insights” > “Dependency graph”에서 확인할 수 있습니다.

아래 스샷은 제가 당근마켓에서 만들어서 공개한 kakao_push라는 루비 라이브러리의 Dependency graph 입니다.

Github Dependency graph

사용중인 외부 라이브러리와 라이브러리 버전 정보가 적혀 있습니다. gemspec 정보를 읽어서 보여주고 있으며 일반 프로젝트의 경우에는 Gemfile, Gemfile.lock 에 적혀있는 정보를 보여줍니다.

현재는 Ruby, Javascript 프로젝트에 대해서만 지원중이고 조만간 Python도 지원할 예정이라고 하네요.

Dependents 탭을 클릭하면 이 라이브러리를 사용중인 다른 프로젝트를 알려줍니다. 위의 kakao_push 젬은 당근마켓에서만 사용하고 있어서 summernote 의 Dependents를 보여드리겠습니다.

Summernote dependent

인기 오픈소스답게 많은 프로젝트에서 사용중이네요 ^^~ 참고로 Private Repo에서 사용중인 내용은 나오지 않습니다.

여기까지는 사실 뭐 그냥 정보 보여주는거라 뭐가 더 있으려나 싶은데요. 진짜는 지금 부터입니다. 위의 정보를 바탕으로 조만간 오픈할 “Security alerts” 기능은 Dependency Graph에서 얻은정보를 토대로 외부 라이브러리중 보안이슈가 있는 라이브러리를 표시하고 버전 업그레이드해야할 내용을 알려줍니다. 아직 오픈은 안했고 조만간 오픈 예정이라고 합니다.

github security alert

외부 라이브러리들을 많이 사용하는것은 좋은데 보안 업데이트등을 제대로 하지 못해서 문제가 되는 경우가 있는데 GitHub의 Security alerts 기능이 이런 문제 해결에 도움이 될것 같아요.

앞으로는 Security alerts 을 넘어서 외부 라이브러리들의 업데이트 정보도 보여주면 좋겠네요 ~

이와 비슷한 기능을 하는 서비스들도 있는데요. Ruby의 경우에는 deppbot 이 있는데 외부 라이브러리가 새로 업데이트 되면 Pull Request로 알려주고 기존에 사용하던 CI에서 PR에 대한 테스트를 수행하게 하면 그 결과가 PR에 댓글로 달리게 되니 테스트 실패가 없다면 쉽게 머지 할 수 있겠죠. Node에는 Greenkeeper, CI 형태로는 gemnasiumdependencyci 가 있습니다.

참고정보

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

AWS EC2 디스크 크기 늘리기

EC2를 사용하다보면 처음 예상과는 다르게 디스크 공간이 부족한 경우가 생깁니다.

EC2에서 EBS 볼륨을 사용한다면 서버를 중지하지 않고도 디스크 크기를 쉽게 늘릴수 있습니다. 현재 세대 인스턴스(t2, m3, m4, c3, c4, r3, r4등)만 가능하고 이전세대 인스턴스(m1, m2, c1, c2, t1 등) 를 사용하는 경우 인스턴스를 중지하고 작업해야합니다.

  1. AWS 콘솔에 접속후 크기를 늘리고자 하는 EC2 인스턴스의 볼륨을 선택
  2. ‘Modify Volume’ 을 눌러서 새로운 디스크 크기를 입력
  3. 볼륨의 상태정보중 “State” 값이 in-uses-optimizing 으로 변경된후 다음 작업 진행
  4. EC2 인스턴스에 SSH로 접속

lsblk 명령어를 입력해서 현재 상태를 확인

ubuntu@myserver:~$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 60G 0 disk
└─xvda1 202:1 0 30G 0 part / 

여기서 보면 xvda 디스크 크기는 새로 변경한 60G 인데 그 밑에 있는 파티션은 아직 바뀌지 않아서 30G로 되어 있습니다.

파티션을 늘리기 위해 다음 명령어를 입력

$ sudo growpart /dev/xvda 1
$ sudo resize2fs /dev/xvda1

여기서 주의할점은 처음 명령어에서는 /dev/xvda 라는 디스크 이름을 적고 공백 다음에 숫자 1을 입력했는데 두번째 명령어에서는 /dev/xvda1 으로 파티션 이름을 입력했다는점 입니다.

다시 lsblk 명령어를 실행하면 파티션의 크기도 60G로 늘어난것을 확인할 수 있습니다.

ubuntu@myserver:~$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 60G 0 disk
└─xvda1 202:1 0 60G 0 part / 

df -h 명령어를 이용해서 디스크 용량을 볼때도 늘어나 있습니다.

ubuntu@myserver:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            3.9G   12K  3.9G   1% /dev
tmpfs           799M  484K  798M   1% /run
/dev/xvda1       59G   22G   35G  39% /
none            4.0K     0  4.0K   0% /sys/fs/cgroup
none            5.0M     0  5.0M   0% /run/lock
none            3.9G     0  3.9G   0% /run/shm
none            100M     0  100M   0% /run/user

참고정보

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

서버 삽질 : 레일스 puma 서버 무한 재시작 `require’: : cannot load such file

회사 서버를 ansible 을 이용해서 설치하기 위해 전환하던중 찾기 어려운 오류를 만나게 되서 정리합니다.

현상

puma 웹서버를 실행하면 puma_error 에러 로그에 아래와 같은 에러가 출력되면서 계속해서 재시작됩니다. puma를 클러스터 모드로 시작하게 했는데 마스터 프로세스는 그대로 인데 워커 프로세스가 계속 재시작했습니다.

/yyy/xxx/~/.rbenv/versions/2.3.3/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'/yyy/xxx/.rbenv/versions/2.3.3/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': : cannot load such file -- bundler/setupcannot load such file -- bundler/setup ( (LoadErrorLoadError)

서버가 시작되려고 하는데 bundler 에서 원하는 젬을 찾지 못하고 있었습니다. 아무리 봐도 bundler 설정이나 rbenv 설정은 잘 되 있어서 puma 설정을 바꾸면서 테스트 하다보니 prune_bundler 옵션이 있는 경우에만 이런 오류가 발생하는것을 알게 되었습니다.

해결

rbenv 설치할때 ansible galaxy에서 받은 zzet/rbenv 를 사용하고 있었는데 이걸 직접 만든 role을 이용해 설치하는 방법으로 변경했더니 오류가 사라졌습니다.

zzet/rbenv 롤을 살펴보니 아마도 문제가 되었던것은 rbenv 플러그인중 rbenv-default-gems 때문이었던거 같아서 플러그인을 설치하지 않게 하려고 직접 만든 ansible role을 사용했습니다.

rbenv-default-gems는 루비 설치후 자동으로 설치될 gem을 설정하는건데 이중에 bundler도 있었고 이로 인해 어디선가 오류가 발생했었던거죠. 굳이 이것을 사용하지 않아도 되서 직접만든 ansible 롤을 사용하면서 해결되었습니다.

삽질지수

이번 삽질의 삽질 지수는 4입니다. (삽질지수 범위 1 ~ 5 단계) 서버 삽질은 항상 최고의 삽질 지수를 자랑합니다. 그 이유는 원인을 찾는데도 오래 걸리고 재현하는것도 오래 걸리기 때문이죠.

이번 삽질의 원인을 파악하기 위해 vagrant 에서 루비를 처음부터 다시 설치하기를 20번도 넘게 했습니다. 루비 설치는 시간이 정말 오래 걸려서 한번 설치해두고 다른거 하다가 와서 배포 하면 루비젬 설치하느라 또 시간 걸리고 기껏 해보면 이거 때문이 아니었고… 시간을 많이 잡아 먹는게 힘들었습니다.

마무리

dockerfile 을 이용할때도 마찬가지지만 인프라 설정에서는 공용모듈을 사용한다는게 쉽지 않다는것을 다시한번 느끼게 되었습니다. 소프트웨어 개발에서는 중복되는 코드를 라이브러리 형태로 만들고 다른 사람들과 공유해서 사용해도 문제가 많이 없는데 인프라쪽은 설치 스크립트를 공유한다는게 장점이 많지 않은것 같습니다.

dockerfile도 누군가가 만든 이미지를 상속 받아서 사용하다가 보면 나중에 결국 본인만의 dockerfile을 만들어서 사용하게 되듯이 ansible 의 롤도 처음에는 가져다 사용하다가 서버 상태에 맞는 롤을 결국 직접 만들어 사용하게 되겠다는 생각을 했습니다. 새로운 롤을 만들면서 기존에 공개된 롤을 많이 참고하면서 도움이 되었던 점을 보면 공개된 롤이 쓸모없는것은 아닙니다.

 

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 삽질 : 인터페이스 빌더에서 설정한 색상이 제대로 표시 안됨

인터페이스빌더(interface builder) 혹은 스토리보드(storyboard)에서 디자이너간 준 색상 코드를 입력했는데 이상하게 코드로 입력한 색상과 다르게 보이는 현상이 있었습니다.

코드로 입력한건 잘되는데 인터페이스 빌더에서 직접 지정한 색상의 경우에만 원하는 색상이 표시되지 않았죠. 그래서 찾다 보니 Color Space 의 문제임을 알게 되었습니다.

이거는 맥에서 애플이 제공하지 않는 모니터를 사용할때 Picker 를 이용해서 색상을 선택하면 나오는 문제랑 비슷한데요. 같은 색상 코드라고 해도 Color Space 에 따라서 다르게 표현되는거죠.

하지만 저는 Xcode 에서 Generic RGB를 맞게 설정하고 hex 코드를 입력했음에도 원하는 색상이 표시되지 않았습니다.

xcode 색상 선택기

xcode 색상 선택기

그래서 혹시나 Xcode 가 저기에 지정한 Color Space 를 무시하고 자기 맘대로 하는건 아닐까 싶어서 인터페이스 빌더 파일을 소스보기로 봤더니 역시나 Color Space 를 자기 마음대로 지정하고 있었습니다. Generic RGB 가 당연히 일반 RGB 스페이스라고 생각했는데 이건 애플의 버그 아닌가 싶어요…

- <color key="textColor" red="0.45098039215686275" green="0.4823529411764706" blue="0.51764705882352935" alpha="1" colorSpace="calibratedRGB"/>
+ <color key="textColor" red="0.45098039215686275" green="0.4823529411764706" blue="0.51764705882352935" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

위 코드의 윗부분은 색상이 잘못 나올때 이고 아랫 부분은 색상이 제대로 나올때 입니다. 보면 colorSpace 값이 calibratedRGB으로 지정 되었을때 색상이 잘못나오는것을 볼 수 있습니다.

그래서 colorSpace 는 custom 으로 변경하고 customColorSpace 를 sRGB로 변경해서 원하는 색상을 표현 할 수 있게 되었습니다.

검색하다보니 Xcode 8에서는 제가 변경한걸로 기본 스페이스가 바뀌었다는데 이상하게 새로 만든 파일에서 계속 이런식으로 적용되더라구요. 이거 때문에 디버깅 뷰 까지 열어가면서 색상 코드 디버깅 하기는 처음이었습니다.

전체 프로젝트에서 저런식으로 잘못 표시된 색상이 있을까 싶어 검색해봤더니 어마어마하게 많더군요… 그래서 아래 명령어로 한번에 변경했습니다.

$ grep -rl 'colorSpace="calibratedRGB"' 폴더/* | xargs sed -i '' 's/colorSpace="calibratedRGB"/colorSpace="custom" customColorSpace="sRGB"/g'

이렇게 해도 나중에 또 색상 지정할때 이런 문제가 발생할 수 있는 여지는 있습니다. 그건 바로 Color Picker 에서 색상을 지정해두고 사용했을때 그 색상을 사용하면 색상을 추가할때의 colorSpace 로 다시 변경됩니다. 그래서 저는 이런식으로 기존에 저장해두고 사용하던 색상을 다 덮어 버렸습니다.

색상 피커 저장된 색상 지움 ㅋ

색상 피커 저장된 색상 지움 ㅋ

지우는 방법을 몰라서 그냥 안쓸거 같은 색상으로 덮어 버렸어요.

http://stackoverflow.com/a/27283783 글을 보면 칼라 피커에서 hex 코드를 입력하면 Color Space가 원복된다는 이야기도 있네요… 제 문제도 이 경우에 해당하는것 같아요. 이런식이면 인터페이스 빌더에서 색상입력하지 말라는거나 다름없지 않나요…. RGB 코드 따로 입력하는게 얼마나 귀찮은데

애플의 이 정책은 나름 모니터마다 같은 색상을 보게하려는 의도라고는 하던데 이것 때문에 오히려 원하는 색상을 표시 못하는 경우가 더 많은거 아닌가 하는 생각이 들었습니다.

제가 제대로 이해하지 못하고 삽질을 해결한것 같아서 여전히 찝찝합니다. 더 좋은 방법을 알고 계신분은 댓글로 알려주세요 ~

덧. 아마도 이 문제는 아이맥이나 애플이 만든 모니터를 사용한다면 발생하지 않을것도 같아요. 색상 피킹하는 툴은 아이맥이나 애플이 만든 모니터에서는 괜찮았거든요…

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 10.3 앱 아이콘 동적으로 변경하기

iOS 10.3에 예상치 못한 신규기능중 앱 아이콘을 변경하는 기능이 있습니다. 이번글에서는 이 신규기능을 활용하는 방법과 적용하지 못하는 경우를 적어볼려고 합니다.

MLB At Bat 앱에서 이 기능을 적용했는데요. 자신이 좋아하는 팀 로고로 앱 아이콘을 변경 할 수 있게 했습니다.

앱 아이콘 변경

MLB At Bat 앱 아이콘 변경

이 포스트의 내용은 영상으로도 촬영 했으니 영상도 참고하시면 동작하는 방식을 이해하기 더 쉬우실거에요

기능을 적용하는것은 정말 간단합니다. 우선 기존에 있던 프로젝트에 새로 추가할 앱 아이콘 이미지를 추가합니다. 예시의 경우 daangn_jeongja@2x.png, daangn_jeongja@3x.png 파일을 추가했습니다.

앱 아이콘 추가

앱 아이콘 추가

여기서 이미지를 Assets.xcassets 에 추가하면 안됩니다. 에셋에 추가해서 사용하고 싶어서 시도를 해봤는데 동작하지 않는것을 확인했습니다.

이미지를 추가 했으면 Info.plist 파일에 추가한 아이콘 이미지에 대한 정보를 등록합니다.

Info.plist 파일에 추가할 내용

Info.plist 파일에 추가할 내용

Xcode 의 Property Editor 에서 자동 완성되지 않는 항목들이라 위와 같은 구조로 일일이 입력해줘야 하는 번거로움이 있습니다.

“Icon files (iOS 5)” 키를 추가하고 그 밑에 “CFBundleAlternateIcons”를 “Dicionary” 타입으로 추가합니다. 그리고 나서 앞으로 변경 가능한 앱 아이콘 후보 이름을 적어 주는데요. 위의 경우 “bundang”, “jeongja” 라는 2개의 앱 아이콘 후보를 추가했습니다.

그리고 각각의 앱 아이콘 후보 이름 밑에는 “CFBundleIconFiles” 라는 “Array” 타입의 키를 추가하고 해당 항목 밑에 실제 앱 아이콘 후보의 이미지 파일명을 적으면 됩니다.

여기서 중요한건 실제 추가한 앱의 파일 이름과 소스코드에서 지정할 파일 이름이 달라도 된다는건데요. 위의 경우 실제 추가한 이미지 파일은 daangn_jeongja@2x.png 인데 이 파일에 해당하는 소스코드에서 참고할 이름은 “jeongja” 입니다.

Info.plist 에 추가한 항목의 XML 소스코드는 다음과 같습니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>CFBundleIcons</key>
   <dict>
     <key>CFBundleAlternateIcons</key>
     <dict>
     <key>bundang</key>
     <dict>
       <key>CFBundleIconFiles</key>
         <array>
           <string>daangn_bundang</string>
           <string>daangn_bundang-72</string>
           <string>daangn_bundang-Small</string>
         </array>
       </dict>
     <key>jeongja</key>
     <dict>
       <key>CFBundleIconFiles</key>
       <array>
         <string>daangn_jeongja</string>
         <string>daangn_jeongja-72</string>
         <string>daangn_jeongja-Small</string>
       </array>
       </dict>
     </dict>
   </dict>
.... 생략

이제 소스코드에서 앱 아이콘을 변경하고자 하는 부분에 아래 코드를 입력합니다

UIApplication.shared.setAlternateIconName("jeongja") { (error) in
    debugPrint("error \(error)")
}

위 코드가 실행되면 앱 아이콘이 변경됩니다. 마지막에 실행되는 코드 블럭은 실행완료후에 호출되는데 error 가 nil 인 경우 성공한것이고 그렇지 않은 경우 변경에 실패한것입니다.

앱 아이콘 변경 확인

앱 아이콘 변경 확인

앱 아이콘 변경코드가 호출되면 위와 같이 사용자에게 알려주는 팝업이 실행되는데요. 이 팝업은 무조건 보여지는 거라 보이지 않도록 할 방법은 없습니다.

앱 아이콘 변경을 하다보면 안되는 경우를 몇가지 발견하게 되는데요.

첫째, 메인스레드에서 코드가 호출되지 않는 경우 앱 아이콘 변경이 되지 않습니다. 메인스레드가 아닌경우 complete 블럭에서 error에 다음과 같은 객체가 전송됩니다.

"Error Domain=NSCocoaErrorDomain Code=3072 \"작업이 취소되었습니다.\""

아이폰 시스템 언어가 영어일 경우 아래 메시지

"Error Domain=NSCocoaErrorDomain Code=3072 \"The operation was cancelled.\""

이때는 메인 스레드에서 실행되도록 다음과 같이 수정합니다.

DispatchQueue.main.async {
  UIApplication.shared.setAlternateIconName("jeongja") { (error) in
    debugPrint("error \(error)")
  }
}

둘째, 앱이 foreground 에서 실행중이지 않을때는 앱 아이콘을 변경할 수 없습니다. 이는 사용자 몰래 앱 아이콘이 변경되는것을 막기위한 조치라고 생각됩니다.

셋째, 에셋으로 이미지 추가가 안되다보니 아이패드를 지원하는 경우 예전에 아이콘 추가하던 방식대로 앱 아이콘 이미지 파일의 이름을 맞춰야합니다. 위의 예시에서 daangn_jeongja-72 라고 하는식으로 뒤에 suffix 를 예전에 하던대로 맞춰야하는거죠. 파일 이름 규칙은 애플문서(App Icons on iPhone, iPad and Apple Watch)를 참고 하세요.

참고자료

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 삽질 : 릴리즈 빌드에서만 런타임에 에러 발생

앱을 만들다가 Debug 에서 잘 실행되는것을 확인하고 TestFlight 에 올려서 테스트 하는데 앱을 시작하자 마자 크래시가 발생하는 문제가 발생했습니다.

발생한 에러는 다음과 같았습니다

2017-04-03 15:12:19.049 daangn[48068:1047076] Unknown class _TtC6daangn17NotificationTabVC in Interface Builder file.
Could not cast value of type 'UIViewController' (0x112194288) to 'daangn.NotificationTabVC' (0x10bdc09c8).

이렇게 저렇게 해결 방법을 찾다보니 Swift 의 Release Scheme의 Optimization Level 을 ‘Fast, Whole Module Optimization’ 에서 None 으로 변경하면 잘된다는 글을 발견하고 그대로 해봤더니 정말이지 Release scheme 에서도 에러가 발생하지 않았습니다.

좀더 정확히는 ‘Fast, Whole Module Optimization’ 이 아닌 ‘None’, ‘Fast, Single-File Optimization’ 로 변경하면 앱이 종료되지 않았습니다.

Optimization 의 버그 인가 싶어서 에러 메시지에서 못찾는다고 나온 클래스만 별도의 IB 로 분리하거나 스토리보드로 분리해보고 Derived Data 도 지우고 Clean Build 도 해봤지만 문제는 해결되지 않았습니다.

이렇게 하루내내 삽질을 하다가 원인을 찾았는데 그건 제가 사용하는 라이브러리의 문제 였습니다. 저는 좌우로 스크롤되는 VC를 구현하기위해 XLPagerTabStrip 이라는 라이브러리를 사용하는데 이 라이브러리에서 사용하는 코드 구현 방식에서 어쩔수 없이 오류가 발생하는 부분이 있었습니다.

해결 방법은 생각보다 간단한데요. AppDelegate 에서 앱이 시작되고 XLPagerTabStrip 을 사용하는 코드가 호출되기 전에 XLPagerTabStrip 을 사용하는 VC를 한번 호출해주면됩니다.

let _ = NotificationTabVC(nibName: nil, bundle: nil)

위와 같이 한번 호출해주면 실제 사용할때 에러가 발생하지 않습니다. 이 에러는 이슈에도 올라와 있는데 Swift 의 Interface Builder 의 Generic 지원 문제로 인해 발생하는 문제 였습니다.

이런 삽질은 할때 마다 너무 힘드네요. 안그래도 Swift 빌드 느린데 빌드를 몇십번씩 하면서 테스트 하려니 중간에 다른것도 못하고 멍하니 화면만 바라보면서 이번엔 잘되라 하면서 기도하고 있으니 시간도 아깝구요.


진정한 삽질이란 이런걸까요? 작년 11월의 제가 이와 동일한 문제를 이 블로그에 iOS 삽질로 적었었네요… 글 다 작성하고 나서 아래 관련게시글로 나와서 알게됬습니다. ㅜㅜ https://code.iamseapy.com/archives/36

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^