MS-SQL 설치

 

MS-SQL을 써야 할 일이 생겨서 한번 설치해보자. 테스트용으로만 필요하기 때문에 무료 테스트용 버전인 Express를 설치해보도록 하겠다.

https://www.microsoft.com/ko-kr/sql-server/sql-server-downloads

 

SQL Server 다운로드 | Microsoft

지금 Microsoft SQL Server를 다운로드하세요. 각 데이터와 워크로드에 가장 적합한 SQL Server 체험판 또는 버전, 도구, 커넥터를 선택할 수 있습니다.

www.microsoft.com

 

Microsoft 공식 다운로드 사이트로 이동해보겠다.

오른쪽에 Express버전이 있고 지금 다운로드 하기가 있는데, 눌러보자

다운받은 파일을 눌러보면 위와같이 나오는데, 기본 설치로 한번 설치해보도록 하겠다.

이런거는 그냥 수락 누르면 된다.

요것도 그냥 기본 경로로 설치하도록 한다.

근데 다운로드 사이트에선 2019버전이라 햇는데, 설치 프로그래스바 나오는 마법사에서는 2017라고나온다;; 뭐지

설치가 완료되었다고 한다. SSMS 설치를 눌러서 바로 SSMS를 설치해도 된다. 

저 버튼을 누르면 아래 링크로 이동하게 될 것이다.

https://docs.microsoft.com/ko-kr/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15

 

SSMS 설치

MySQL에는 MySQL Workbench가 있다면 MS-SQL에는 SSMS가 있다. 다운로드 페이지에서 스크롤을 아래로 더 내려보면 좌측에 SSMS 링크가 있다. 눌러보자.

https://docs.microsoft.com/ko-kr/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15

 

SSMS(SQL Server Management Studio) 다운로드 - SQL Server Management Studio (SSMS)

SSMS(SQL Server Management Studio) 다운로드Download SQL Server Management Studio (SSMS) 이 문서의 내용 --> 적용 대상: SQL Server Azure SQL Database Azure Synapse Analytics(SQL DW) 병렬 데이터 웨어하우스 APPLIES TO: SQL Server Azure SQL Database Azure Synapse Analytics (SQL DW) Pa

docs.microsoft.com

위와 같은 페이지가 나타나는데, SSMS다운로드를 눌러서 받으면 된다.

 

설치파일을 실행하면 다음과 같이 나타난다.

설치 버튼을 눌러서 진행하자.

꽤 오래걸리는데 다 기다리니 이렇게 뜬다. 다시시작을 하면 설치가 끝이 난다.

Virtualbox vm에 freeBSD 설치하는 법이다.

 

일단 virtual box를 설치해야 한다.

 

Virtualbox 설치

virtualbox 공식 사이트로 가면 다운로드를 받을 수 있다.

구글에 치면 download 페이지를 바로 볼 수 있다. 일단 들어간다.

다양하게 있는데, Windows 환경에서 돌릴 것이므로 본인은 Windows Host를 눌러서 다운받았다.

진리의 Next / Next / Next를 해준다.

뭔가 시뻘건 글씨가 나오면서 Warning 경고를 주는데, 이 내용인 즉슨 가상 네트워크 인터페이스를 추가할것이기 때문에 네트워크가 잠시 끊길 수 있다는 것이다. 무언가 연결이 끊기면 안되는 것들을 하고 있다면 그거를 끝내고 yes를 눌러주면 되겠다.

 

Next / Yes를 계속 눌러서 쭉 설치하면 된다.

 

설치가 완료되었고, Finish를 눌러준다.

 

freeBSD installer image(iso) 다운로드

공식 사이트로 가서 freebsd를 다운받으면 된다. 구글에 치면 바로 나온다.

중간에 커다랗게 있는 Download FreeBSD를 눌러준다.

 

아래로 내리면 여러개가 있는데, Installer Images중에서 원하는 아키텍쳐를 다운받는다. 일반적인 64비트 PC(x86-64)등은 amd64를 받으면 되고 32비트 피씨(x86-32)는 i386을 받으면 된다. 

 

powerpc는 공유기같은 임베디드 장비등에 쓰이는 아키텍쳐인데, 그쪽용을 받으려면 해당 아키텍쳐를 누르면 된다. 본인은 x86 32bit가 필요해서 i386을 눌렀다.

여러가지가 나오는데, disc1.iso를 눌러서 받았다. disc1.iso.xz는 xz로 압축된 녀석인데, 데이터양을 줄여야 한다면 압축된 녀석을 받아서 압축 풀어주면 된다. 본인은 그냥 압축안된걸 받았다.

 

vbox에 freeBSD 설치

귀여운 Tux 펭귄이 우릴 맞이해준다. 우측 상단의 새로만들기를 눌러준다.

이름에 freebsd32bit라고 치면, 알아서 종류와 버전을 골라준다. 메모리크기는 host 메모리 크기에 따라서 적절히 골라주는데, 본인 피씨는 호스트가 16GB 메모리를 가져서 대충 4기가정도 주었다. 그리고 만들기를 누른다.

 

그리고 하드디스크 용량을 고르게 되어있는데, 적당히 8GB정도 주었다.

이제 설정을 들어간다.

 

가장 좌측에 저장소를 누르고, 저장장치 중 두번째 CD케이스인 비어 있음을 누르고 우측에 CD모양을 눌러서 Choose a disk file을 누른 뒤, 아까 다운받은 freebsd iso 파일을 선택한다. 본인의 경우 아까 선택한 적이 있어서 Choose a disk file 하단에 해당 iso파일이 자동완성으로 떠있다.

요렇게 씨디가 들어가면 확인을 눌러주고 설정창을 끈다.

이제 저 가상머신을 켜 보자

저런게 뜨면 시작을 누르면 된다.

1번을 눌러준다. 또는 엔터를 누르면 된다.

엔터를 눌러서 Install을 해주면 된다. 이제는 왠만해선 엔터만  쭉쭉 누르면 된다

 

키맵을 고르라는데, 뭐 중국어 자판, 벨기에 자판 이런거 쓸거 아니고 미국식 자판 쓸거면 그냥 엔터 누르면 된다.

호스트 네임을 쓰라고 하는데, 그냥 본인 컴퓨터 이름같은거다. 홍길동의 컴퓨터 이런것 마냥. 내부망에서 해당 컴퓨터를 식별하는 방법인데 vm이고 하니 대충 아무거나 써도 된다. 나는 그냥 비우고 엔터 눌렀다.

 

추가 기능들 관련된건데,  필요한 것들 있으면 방향키랑 스페이스 키로 enable/disable시킨 뒤 엔터 쳐서 넘어가면 된다.

 

freebsd 커널 디버깅을 하겠다면, kernel-dbg가 반드시 ok 되어야 하겠다.

파티션 어떻게 나눌거냐는데, 윈도로 치면 C/D드라이브 나눠서 쓰고 이런걸 말한다. 그냥 기본값대로 간다.

운영체제를 모든 디스크에 다 설치할건지, 파티션 나눠서 할건지 물어보는건데 걍 전체로 한다.

파티션 나누는것도 방식이 다양한데, 도스방식으로 나누는게 기본값이니 그냥 이걸로 간다.

파티션 방식 맞냐고 물어보는데 그냥 ㄱㄱ한다. 귀찮다.

기존 데이터 삭제된다고 하는데, 뭐 처음 설치하는거라 삭제될것도없다. 걍 ㄱㄱ

쭉쭉들어간다 기다리면된다. 추가기능들을 모조리 체크해서 그런지 꽤나 오래 걸린다.

루트계정 패스워드 설정하시란다. 아마 두번 치라고 하겠지.

네트워크 기능 활성화 할거냐는데, 당연한 소리다.

IPv4 할거냐하는데, 당연 ok

자동 IP할당도 할거냐는데 이것도 당근 OK

IPv6쓸거냐는데, 필요없다. 걍 No 근데 Yes해도된다.

DNS서버 자동으로 어디서 갖고왔다. 호스트꺼를 따서 하는듯. 그냥 OK하면 된다.

지역 설정하는게 있는데 Asia로 가자

 

국가설정 있는데 22번 머한민국으로 하자.

KST시간 따르냐는데, KST는 Korea standard time의 약자로 한국 표준시각이다. 맞다. ㄱㄱ

날짜를고르고, 시간도 고르라는데 대충설정하자~

기본 서비스 설정인데, 기본값으로 간다 후 너무많다

시스템에서 보안관련된 하드닝 기법들 쓸건지 물어보는데, 서비스용도아니고 테스트용인데 걍 기본값으로 None으로 간다.

유저 추가할거냐는데, guest계정 하나 만들어보자.

guest계정을 만들어보았다. 맨 위에 name: guest가 짤리긴했는데, 대충 요러코롬 만들면 된다.

 

다만 체크해야하는 부분은, login group을 wheel로 해야 su root가 가능하고, 쉘은 구대기 본쉘말고 csh을 쓰면 좋다.

다 끝내고 reboot을 눌러준다. 그리고 그냥 들어가면 다시 설치 화면이 뜨게 되는데, 일단 머신을 꺼야한다.

두번째 shell로 들어간 뒤, poweroff를 쳐준다.

그리고 설정으로 들어가서 iso 가상 CD 이미지를 꺼내기 한 뒤 다시 부팅해보자.

그러면 이제 될 것이다.

다시 1번 boot multi user를 눌러보자.

이제는 install그게 안뜨고  login이 뜬다. guest나  root계정으로 로그인해보자.

루트로 로그인 되었다.

 

이제 gcc를 써볼려고하니 없더라. pkg install -y gcc gdb 로 설치해준다.

 

이제 하고싶은거 하면 된다. 끝!

WSL이 지원됨에 따라 Windows에서도 bash를 사용할 수 있다고 한다.

 

설정하는 김에 따라해보자.

 

시작메뉴에서 "개발자"라고 검색한다.

 

개발자 설정으로 들어간다.

 

개발자 모드를 클릭하면 경고창이 뜨는데 "예"를 누른다.

혹은 아래와 같이 나타나는데, 개발자 모드를 "켬" 으로 바꾸어준다.

 

시간이 조금 걸린다.

 

 

조금 기다리면, 개발자 모드 패키지가 설치되었다고 뜬다.

이제 다시 시작메뉴에서 "windows 기능"이라고 검색한다. 윈도우즈랑 기능 사이에 스페이스바 한칸이 들어가야 한다.

 

Linux용 Windows 하위 시스템을 체크한다. WSL이 Windows Subsystem for Linux의 약자로 한글로 번역된 내용이 Linux용 Windows 하위 시스템이다.

 

재부팅을 하라고 하는데, 지금 할거면 "다시 시작"을 누르고 나중에 할거면 "다시 시작 안함"을 누르면 된다.

 

cmd창을 열고 bash라고 쳐 보니, bash가 설치되어 있지 않다고 한다.

웹 브라우저에 aka.ms/wslstore라고 치니 스토어 앱이 열린다.

 

 

필자는 ubuntu를 설치하기로 했다.

저기 보이는 무료라는 버튼을 누르면 설치가 된다.

설치가 되고 있는 모습이다.

설치가 다 되어서, 실행 버튼을 눌러 보았다.

유닉스 사용자명을 만들라고 하는데, 윈도우랑은 달라도 된다.

다 된 모습이다.

이제 아무 cmd를 열어서 bash라고 쳐도 Win bash가 실행된다.

 

끝!

이제 거의 막바지에 다다랐다.

 

지속적으로 스트리밍을 하도록 하려면 m3u8파일이 계속해서 바뀌게 되는데, 이거를 위해서 manifest 파일도 계층구조를 좀 가져야 했다.

 

playlist라는 manifest가 기본적으로 있고 이녀석은 chunkData라는 녀석을 보라고 한다.

 

그리고 chunkData는 스트리밍이 됨에 따라서 ts파일이 계속 슬라이딩 윈도우 마냥 진행되면, HLS 클라이언트는

 

이를 알고 알아서 지속으로 chunkData를 업데이트 하면서 ts파일들을 버퍼링해서 재생을 시킨다.

 

 

요녀석을 구현하기 위해서 했던 짓들을 커밋 이력을 보면서 다시 복습을 했는데, 이걸 잘 몰라서 삽질을 한번 했던 것 같다.

 

m3u8라는 Nodejs 모듈에서 m3u8 파싱 기능을 제공한다. 이를 이용해서 직접 m3u8 파일을 한땀한땀 변경하면서 작업을 했던 것 같은데, 아마 ffmpeg에서 이것 또한 잘 해주는 그런 기능이 있었던 것 같다.

 

 

그리고 나서 테스트를 했다가, 클라이언트 단에서 재생이 자꾸 끊기는 현상이 있었는데,

 

이유를 당최 몰라서 해메다가 hls-client javascript 코드를 한번 까보면서 알게 되었다.

 

기승전 코드까보기..ㅠㅠ

 

코드중에 logger와 같은 객체가 있었는데 안에 모두 빈 함수가 있었었다.

 

그 함수들을 console.log와 같은 함수로 바꿔치기를 한 뒤 실행을 해 보니, 브라우저에서 콘솔로 warning들이 떳었다.

 

 

 

그 원인은 즉슨, 클라이언트에서 몇초마다 새로운 음악파일을 서버로 보내고, 서버에서는 각각을 ffmpeg으로 가공을 했다.

 

그런데 다시 hls client에서는 다른 파일에 대한  ffmpeg-ts가 들어오니, 음악의 흐름이 끊긴줄 알게 된 것이다.

 

 

그래서 이러한 현상을 해결하기위해서 ffmpeg에서 가공할 때 omit_endlist 라는 옵션(오래되서 가물가물하긴 한데 아마 맞을 것이다)을 추가해서 이러한 현상을 해결했던 것 같다.

 

 

어쨋든 이런식으로 해결을 하다보니 지금 상태의 코드가 되었다.

 

음질이라던지 내부적으로 부족한점이 매우 많지만, 그래도 어쨋든 대충이라도 동작은 하기는 했다.

 

중간에 삽질도 많았지만 여러모로 배우기도 했다.

 

더 이상 까먹기 전에 블로그에 정리를 했으니, 이 내용은 이만 당분간 묻어두도록 하겠다.

 

 

최종 코드 스냅샷이다.

 

https://github.com/Einstrasse/hls-service/tree/d92a3f6abb378cf0947c3ba928f9d8b57d3a8118

 

Einstrasse/hls-service

Nodejs base hls server. Contribute to Einstrasse/hls-service development by creating an account on GitHub.

github.com

 

이제 클라이언트에서 녹음한 파일이 서버로 전송이 되었으니, 이를 잘 가공해서 스트리밍 할 수 있도록 해 보아야 한다.

 

음성 파일을 ts 파일(Transport stream)으로 나누고, 그 리스트들을 가지고 있는 m3u8 manifest 파일을 생성해야 한다.

 

 

일단 음성 파일을 MPEG-TS 파일로 변경하는 것은 내 개인 능력으로는 터무니 없이 불가능하다는 생각이 드는 것은 자명하다(?)

 

따라서 무조건 ffmpeg 코덱을 이용해서 변환을 해야 하는데, m3u8파일은 포맷에 맞춰서 직접 만들어야 할 줄 알았다.

 

왜냐면 text파일 형식에 간단해보였기에... 하지만 ffmpeg에서 m3u8 파일 역시 생성해주는 옵션들이 있었다.

 

 

Nodejs 모듈 중에서 fluent-ffmpeg이라는 모듈이 있었다.

https://github.com/fluent-ffmpeg/node-fluent-ffmpeg

 

fluent-ffmpeg/node-fluent-ffmpeg

A fluent API to FFMPEG (http://www.ffmpeg.org). Contribute to fluent-ffmpeg/node-fluent-ffmpeg development by creating an account on GitHub.

github.com

 

맨 처음 요녀석을 보고 그냥 ffmpeg을 nodejs로 포팅한 대단한 녀석이라고 생각했다.

 

ffmpeg은 딱봐도 계산량이 많아서 C나 C++등으로 작성된 네이티브 바이너리일 것이고, Nodejs는 Javascript 런타임인데, Javascript로 코덱을 만든다라 대단하다 생각했는데

 

사실 fluent-ffmpeg 이녀석은 실제 ffmpeg 바이너리를 가지고 있을 때, ffmpeg의 수많은 인자들을 javascript API로 바꾸어주는 녀석정도 밖에 안되는 녀석이었다. ffmpeg 실행 커맨드라인 래퍼(wrapper) 정도 인 녀석이다.

 

그래도뭐 chile_process.exec("ffmpeg -args"); 요런식으로 하는 것 보다는 좀 나으니, 사용해보도록 했다.

 

여기 까지 작성해본 코드 스냅샷이다.

 

https://github.com/Einstrasse/hls-service/tree/2d41cf29fa853142e891d4cbedb638e9b66b3ba7

 

Einstrasse/hls-service

Nodejs base hls server. Contribute to Einstrasse/hls-service development by creating an account on GitHub.

github.com

 

그리고 클라이언트에서 음성 파일들을 보내는데, 이전에는 길게 녹음해서 한번 큰 덩어리를 보내는 식이었는데,

 

라이브 스트리밍이라는 컨샙을 맞추기 위해서, 짧은 시간 동안 녹음한 내용을 주기적으로 서버로 보내도록 코드를 변경해 보았다.

 

여기까지 변경한 코드 스냅샷 부분이다.

 

https://github.com/Einstrasse/hls-service/tree/d55e4a6cb72f7d59917b4d62905b43db31f29039

 

Einstrasse/hls-service

Nodejs base hls server. Contribute to Einstrasse/hls-service development by creating an account on GitHub.

github.com

 

HLS를 이용한 라이브 라디오 방송 웹 앱 개발기 (2) 를 작성한지 꽤 오랜 시간만에 3편을 작성해본다.

 

나중에 써야지 써야지 하다가 귀찮아서 작성하지 않고 있었는데, 더이상 까먹기 전에 이어서 작성을 해보려고 한다.

 

2편까지에서는 브라우저단에서 마이크 API를 이용해서 음성 파일을 저장하는 것 까지 했었다.

 

이제 이 음성 데이터를 서버로 전송을 시켜야 한다.

 

 

일단 파일형태로 전송을 하는 것을 생각을 했는데, 웹에서 파일 업로드시 많이 사용하는 방식이 POST 메소드로 form으로 데이터를 날리는 방식이다.

 

이때 인코딩 타입은 multipart/form 형식으로 보낸다. 바이너리 값 그대로 HTTP 패킷에 담아서 보내는 방식이다.

 

Nodejs에서 multipart/form-data를 받는 부분을 작성을 하려고 했는데 Nodejs에서 해당 데이터를 받는 것은

 

Express.js 프레임워크에서 Multer라는 미들웨어를 사용하는 방식밖에는 잘 몰랐다.

 

하지만 나는 가볍게 만들어볼 생각에 Express.js 프레임워크는 사용하고 싶지 않았던 탓에, multer말고 multipart/form 을 파싱할 수 있는 nodejs package를 찾아야 했다.

 

 

그래서 가장 간단한 방법 중 하나인, multer의 코드를 까 보았다..!

 

https://github.com/expressjs/multer/blob/master/doc/README-ko.md

 

expressjs/multer

Node.js middleware for handling `multipart/form-data`. - expressjs/multer

github.com

 

지금 위 링크로 가 보니, 내부적으로 busboy를 사용한다고 대놓고 나와있다. 작년에 프로젝트 작업을 할 때에는 코드를 까서 알아냈었는데...

 

어쨋든 busboy란 녀석을 multer가 내부적으로 사용한다는 것을 알 수 있다.

 

https://github.com/mscdex/busboy

 

mscdex/busboy

A streaming parser for HTML form data for node.js. Contribute to mscdex/busboy development by creating an account on GitHub.

github.com

 

busboy 문서를 보면 html form data를 파싱할 수 있다는 것을 알 수 있다.

 

어쨋든 이녀석을 이용해서 서버단을 작성해준다.

 

 

그리고 클라이언트 단에서 multipart/form-data 인코딩 방식으로 된 form data를 서버로 보내는 것도 해 주어야 하는데,

 

요녀석은 XHR(XMLHttpRequest)를 이용해서 간단하게 작성해 보았다.

 

처음에는 이녀석을 jQuery 라이브러리를 통해서 접했는데, Low하게는 브라우저단에 구현된 XHR 객체에서 서버와의 동적 비동기 통신이 가능하다는 것을 나중에 알게 되었다.

 

여기까지 작성해본 코드 스냅샷 링크이다.

 

https://github.com/Einstrasse/hls-service/tree/b52043c77ec377b70476619556d3a6e187823cb3

원문 URL

https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/index.html#//apple_ref/doc/uid/TP40015214-CH2-SW1


원문을 번역한 글입니다.


시작합시다


각각의 강의는 튜토리얼과 앱을 만들기 위해 알아야 하는 개념들이 포함되어 있습니다.

강의는 당신이 간단한 실제 iOS 앱을 만들기 위한 프로세스들을 차근차근 하나씩 알려줍니다.

당신이 강의를 따라서 앱을 만드는 동안, iOS 앱 개발의 개념들을 배우게 되고, 스위프트 언어에 대한 깊은 이해를 얻을 수 있으며, Apple의 IDE인 Xcode의 다양하고 유용한 기능들에 익숙해지게 될 것입니다.


강의 전에 알아야 할 사항들

이 강의에서는, 당신이 스위프트 언어에 익숙하다고 가정합니다. 강의를 잘 따라오기 위해서 스위프트의 마스터가 될 필요는 없지만 당신이 스위프트 언어를 쉽게 읽고 이해할 수 있다면, 이 강의에서 좀 더 많은 것을 가져갈 수 있을 것입니다.

당신이 만약 아직 스위프트언어가 익숙하지 않다면, 스위프트 운동장 앱에서 코드 연습(Code Exercise)들을 끝내고오세요. 또는 "The Swift Programming Language (Swift 3)"를 통해서 스위프트를 경험하고 오는것도 괜찮습니다. 위 두 방법들은 스위프트 언어의 기본기를 잘 다져줄 것입니다.


강의에 대해서

이 강의에서, 당신은 간단한 FoodTracker라고 불리는 식단 기록 앱을 만들것입니다. 이 앱은 음식의 이름, 점수, 사진을 포함한 음식 리스트를 보여줍니다. 사용자는 음식들에 대해서 추가, 삭제, 수정을 할 수 있습니다. 새로 추가를 하거나, 존재하는 앱을 수정하기위해서 사용자는 다른 화면으로 이동하여 특정 음식의 이름과 점수, 사진을 설정할 수 있습니다.


image: ../Art/IN_sim_navbar_2x.png

강의들은 Xcode 프로젝트로 진행되며, 강의가 끝날 때 완성물은 위와 같이 생길 예정입니다.

강의가 진행 된 뒤, 당신은 프로젝트를 다운받아서 직접 만든 코드와 비교할 수 있습니다.

당신이 강의를 진행하는 동안 배웠던 개념들을 다시 확인해야 하는 경우, 용어사전(Glossary)을 이용하세요.

용어사전에 있는 용어들은 강의들에 링크되어 있습니다.


개발 도구 구하기

iOS 앱을 개발하기 위해서, 이 강의에서는 최신버전의 기술을 사용할 것입니다. 일단 Mac 컴퓨터(macOS 10.11.5 또는 그 이후 버전)가 필요하고, 최신버전의 Xcode가 구동되어야 합니다. Xcode는 당신이 설계, 개발, 디버그 하는데 필요한 모든 기능들을 가지고 있습니다. Xcode는 또한 iOS SDK를 포함하며, 이 SDK는 Xcode가 iOS 개발을 위해 필요한 도구들과 컴파일러 및 프레임워크들을 확장해줍니다.

당신의 Mac 컴퓨터의 App Store에서 최신 버전의 Xcode를 무료로 다운받으세요.


최신 버전의 Xcode를 다운로드 받기 위해서

  1. 당신의 Mac 컴퓨터에서 App Store를 여세요(Dock에 기본적으로 있습니다.)

  2. 오른쪽 위에 있는 검색 필드에, Xcode라고 입력한 뒤 리턴 키(엔터 키)를 누르세요.

  3. Xcode 앱이 검색 결과 첫번째 창에 나타날 것입니다.

  4. Install App을 눌러서 Xcode를 다운받으세요.

  5. Apple ID와 패스워드를 입력하세요. Xcode는 /Applications 디렉토리에 다운받아질 것입니다.


--------------------------------------------------------------------
원문입니다.

Jump Right In

Start Developing iOS Apps (Swift) is the perfect starting point for learning to create apps that run on iPhone and iPad. View this set of incremental lessons as a guided introduction to building your first app—including the tools, major concepts, and best practices that will ease your path.

Each lesson contains a tutorial and the conceptual information you need to complete it. The lessons build on each other, walking you through a step-by-step process of creating a simple, real-world iOS app.

As you make your way through the lessons and build the app, you’ll learn about concepts in iOS app development, gain a deeper understanding of the Swift programming language, and familiarize yourself with the many valuable features of Xcode, Apple’s integrated development environment (IDE).

Prerequisites

In these lessons, it is assumed that you are familiar with the Swift programming language. You do not need to be a Swift master to complete the lessons, but you will get more out of the lessons if you can comfortably read and understand Swift code.

If you are not yet comfortable with Swift, complete the Learn to Code exercises in the Swift Playgrounds app. Alternatively, you can work through A Swift Tour from The Swift Programming Language (Swift 3). Both give you a solid foundation in the Swift programming language.

About the Lessons

In these lessons, you’ll be building a simple meal-tracking app called FoodTracker. This app shows a list of meals, including a meal name, rating, and photo. A user can add, remove, or edit a meal. To add a new meal or edit an existing one, users navigate to a different screen where they can specify a name, rating, and photo for a particular meal.

image: ../Art/IN_sim_navbar_2x.png

The lessons are each accompanied by an Xcode project file that shows an example of how your code and interface should look at the end of the lesson. After you go through a lesson, you can download the project and check your work against it.

If you need to refer to the concepts you’ve learned throughout the lessons, use the glossary to refresh your memory. Glossary terms are linked throughout the lessons.

Get the Tools

To develop iOS apps using the latest technologies described in these lessons, you need a Mac computer (macOS 10.11.5 or later) running the latest version of Xcode. Xcode includes all the features you need to design, develop, and debug an app. Xcode also contains the iOS SDK, which extends Xcode to include the tools, compilers, and frameworks you need specifically for iOS development.

Download the latest version of Xcode on your Mac free from the App Store.

To download the latest version of Xcode

  1. Open the App Store app on your Mac (by default it’s in the Dock).

  2. In the search field in the top-right corner, type Xcode and press the Return key.

    The Xcode app shows up as the first search result.

  3. Click Get and then click Install App.

  4. Enter your Apple ID and password when prompted.

    Xcode is downloaded into your /Applications directory.

Let’s get started!


async 공식 문서

https://caolan.github.io/async/

async npm 페이지

https://www.npmjs.com/package/async


Nodejs에서 코드를 작성하다보면, 필연적으로 부딪히게 되는 것 중 하나가, Callback hell이다.

비동기 Non-blocking I/O가 Nodejs의 메인 특징중 하나이다 보니, Nodejs에서 동기적인 로직의 구현이 필요할 경우 이를 풀어나가는 방법이 필요하다.


동기적인 로직이라 함은, 어떤 처리의 순서가 보장되어야 한다는 것이다. 즉, 코드의 A라는 부분이 완료된 이후에야 B라는 부분이 실행되어야 하는 경우 이러한 요구사항이 충족되는 경우를 동기성이 보장된다고 볼 수 있다.

 

func1();
func2();

위와 같은 코드가 있는 경우를 생각해보자. 그리고 각각의 func1, func2함수는 I/O를 처리한다고 생각해보자.

여기서 I/O라는 것은 CPU에서 계산을 처리하는 것 이외, Disk에 값을 쓰거나 읽는다던지, 인터럽트를 처리한다던지 등의 커널레벨에서 처리해야 할 만 한 것들을 처리한다고 생각을 해 보자.


일반적인 동기, Blocking I/O 방식인 경우, 즉 일반적인 C언어와 같은 환경일 경우에는 func1(); 함수가 리턴된(완료된) 이후에, func2(); 함수가 수행된다는 것을 알고 있다.


하지만 Nodejs의 경우는 비동기 Non-blocking I/O가 기본 설계 원칙(Design Principle)로 제작된 javascript 런타임이고, 이를 이벤트 루프와 poll-큐를 통해서 구현했다. 그래서 위와 같은 코드를 실행할 경우, func1라는 I/O가 먼저 시도되고 func2라는 I/O가 시도되는 것은 보장이 되나, func1가 먼저 끝나라는 보장은 없고, func1가 끝나고 나서 func2가 시작한다는 보장은 더더욱 없다.

따라서 동기성이 보장되지 않게 되는 것이다. 그래서 이러한 동기성을 보장하기 위해서 Nodejs에서는 Callback을 이용하는 방식으로 동시성을 보장하게 만들 수 있다. 다음 코드를 보자.



 

func1(function() {
	func2();
});

callback을 이용한다고 했는데, 이는 간단히 설명하면, 함수를 다른 함수의 인자로 넘기는 것이다.

func1라는 함수를 선언할 때, 첫번째 인자로 callback함수를 받도록 했는데, 이 인자로 들어간 callback함수는 func1 함수에서 처리하는 I/O가 완료된 이후 func1함수 내부에서 실행되도록 구성한 것이다. 따라서 func1의 I/O가 끝난 이후 func2의 I/O를 시도하도록 할 수 있게 된다. 이렇게 콜백 함수를 통해서 Nodejs에서 동시성을 보장하도록 코드를 구성하였다.


하지만 다음 경우를 보자.

 

func1(function() {
	func2(function() {
		func3(function() {
			func4(function() {
				func5();
			});
		});
	});
});

위 예제는 func1부터 func5까지 순서대로 동기성이 보장되어야 하는 경우를 콜백 함수 중첩을 통해 코드로 구현한 모습이다.

코드가 오른쪽으로 계속 indenting이 되어서 마치 > 와 같은 모양이 되었다. 특히나 이런 경우, I/O처리 도중 에러가 발생했을 때 에러처리와 같은 이슈가 발생했을때 코드의 가독성이 매우 떨어지게 된다. 이러한 현상을 callback hell이라고 한다.


 

func1(function(err1) {
	if (err1) {
		console.error(err1);
		return;
	}
	func2(function(err2) {
		if (err2) {
			console.error(err2);
			return;
		}
		func3(function(err3) {
			if (err3) {
				console.error(err3);
				return;
			}
			func4(function(err4) {
				if (err4) {
					console.error(err4);
					return;
				}
				func5();
			});
		});
	});
});

위와 같은 코드는, 각각의 함수에서 I/O 등을 처리할 때 에러가 발생했을 경우 에러를 처리하는 로직이 포함된 코드이다. 보는 바와 같이 가독성이 매우 떨어지며, 에러처리 부분은 공통적으로 처리할 수 있을 것 같은데, 중복코드가 많이 발생한다.


이러한 콜백 중첩 방식의 가독성 문제, 에러처리의 불편함을 npm async 모듈을 이용해서 해결해보자. 위와 같이 func1->func2->func3->...와 같은 식의 I/O가 발생할 경우, 즉 이전 I/O가 완료된 이후에 다음 I/O 작업을 수행해야하는 동기성을 보장해야 할경우 async.waterfall을 이용하면 된다.



 

var async = require('async');

async.waterfall([
	function(callback) {
		console.log(1);
		callback(null);
	},
	function(callback) {
		console.log(2);
		callback(null);
	},
	function(callback) {
		console.log(3);
		callback(null);
	}
], function(err) {
	console.log('error:', err);
});

async.waterfall은 말 그대로 폭포가 계단식으로 내려오듯 순서대로 실행하게 된다. 첫번째 인자는 콜백함수의 배열로, 순서대로 실행되어야 하는 작업들을 함수로 받는다. 두번째 인자는 함수로, 마지막 처리를 맡는다. 위의 코드를 실행하면 아래와 같은 결과가 나타난다.

 

1
2
3
error: null

만약 console.log(1)가 있는 코드 부분에서, callback 함수의 첫번째 인자는 지금 null이라고 되어 있다. 이는 에러가 들어가는 부분으로, false 값으로 판별되는 값이 들어가게 되면 에러가 없기 때문에 다음 함수를 실행하게 된다. 하지만 true값이로 판별되는 값이 들어가게 되면, 다음 callback으로 넘어가지 않고, waterfall함수의 두번째 인자인, 가장 마지막 콜백 함수를 실행하게 된다. 따라서 마지막 콜백 함수에는 에러 처리 로직이 들어가면 된다.

 

var async = require('async');

async.waterfall([
	function(callback) {
		console.log(1);
		callback('Logical error');
	},
	function(callback) {
		console.log(2);
		callback(null);
	},
	function(callback) {
		console.log(3);
		callback(null);
	}
], function(err) {
	if (err) {
		console.log('error:', err);
	}
});

위 코드를 실행시키면 다음과 같은 결과가 나타난다.

 

1
error: Logical error

에러값이 true를 갖는 string으로 인자가 들어갔으므로, 곧바로 에러 처리 로직으로 넘어가게 되는 것이다. 또한 callback함수의 두번째부터의 인자는 데이터로, 다음에 실행되는 콜백에서 인자로 받아서 사용할 수 있다. 예제는 아래와 같다.

 

var async = require('async');

async.waterfall([
	function(callback) {
		console.log(1);
		callback('Logical error', 'Some other data', 'Some other data2', 'Some other data3');
	},
	function(callback) {
		console.log(2);
		callback(null);
	},
	function(callback) {
		console.log(3);
		callback(null);
	}
], function(err, data, data2, data3) {
	if (err) {
		console.log('error:', err);
	}
	console.log(data);
	console.log(data2);
	console.log(data3);
});

 

1
error: Logical error
Some other data
Some other data2
Some other data3

에러값이 false일 경우, 다음 callback으로도 데이터를 넘겨줄 수 있다. 아래의 예제를 보자.

 

var async = require('async');

async.waterfall([
	function(callback) {
		console.log(1);
		callback(false, 'Some other data', 'Some other data2', 'Some other data3');
	},
	function(data, data2, data3, callback) {
		console.log(2);
		console.log(data);
		console.log(data2);
		console.log(data3);
		callback(null);
	},
	function(callback) {
		console.log(3);
		callback(null);
	}
], function(err, data, data2, data3) {
	if (err) {
		console.log('error:', err);
	}
	console.log(data);
	console.log(data2);
	console.log(data3);
});

 

1
2
Some other data
Some other data2
Some other data3
3
undefined
undefined
undefined

error값이 항상 false 진리값을 갖으므로, 순차적으로 계속 실행된다. callback함수 호출 시 2개 이상의 인자를 가진 경우, 2번째 이후 인자는 다음 콜백에서 데이터로 넘어간다. 따라서 2번째 콜백에서 앞에서 넘긴 인자들을 받기 위해 함수 선언형에 인자가 늘었다. 마지막 에러 처리 콜백에서 data, data2, data3의 경우는 그 직전 콜백에서 데이터를 넘겨주지 않았으므로 undefined의 값을 갖는다.

Node.js

Node.js는 간단하게 이야기 한다면, 비동기 이벤트 기반 Javascript 런타임이다. 자바스크립트를 실행시켜 어떠한 프로그램을 작성할 수 있는데, 서버를 작성하는 것이 일단은 주 목적이다. 따라서 서버사이드 자바스크립트라는 대명사로 불리기도 한다. Node.js로 웹 서버를 프로그래밍 하게 되면 프론트앤드에서 사용하는 스크립트 역시 자바스크립트이기 때문에, 백엔드와 프론트앤드 둘 다 동일한 언어로 작성을 하게 되어서 언어 학습 오버헤드가 줄어들 것을 기대할 수 있고, 언어 자체가 매우 유연하기 때문에 간단한 서버는 빠르게 작성할 수 있다.

주요 특징들

노드의 의도된 용도(Intended Purpose)

노드의 주요 특징들이 있다. 공식 사이트에 설명된 대로, 이벤트 드리븐(Event-Driven), 논 블로킹 I/O(Non-Blocking I/O), 그리고 npm을 통한 패키지 에코시스템의 제공 등이 있다.

노드는 Scalable한 네트워크 어플리케이션을 제작하기 위해 만들어졌다. Scalable하다는 것은 크기를 늘였다 줄였다 자유롭게 할 수 있다라는 의미로 보면 된다. Lightweight한 규모부터 매우 Heavy traffic을 감당할 수 있는 규모까지 아우른다고 보면 된다.


이벤트 드리븐 구조

다수의 연결 요청은 concurrent하게 처리된다. Concurrent라는 것은 한번에 여러개가 동시에 처리된다는 뜻으로 보면 된다. Parallel과 다른 점은, Parallel은 실제 물리적으로 동시에 처리가 되는 것이고, Concurrent는 물리적으로는 동시가 아니지만, 겉보기에는 동시에 처리되는 것 처럼 보인다, 즉 논리적으로 동시에 처리가 된다는 차이점이 있다.

매 연결이 이루어질 때 마다 callback이 실행되지만 만약 처리할 일이 없으면 Node는 sleep상태로 쉬고 있게 된다.


Non-Blocking I/O

이러한 방식은 최근에 가장 많이 쓰는 OS 쓰레드를 사용한 concurrency 모델과는 다르다. 쓰레드 기반 네트워크는 비교적 비효율적이고 사용하기 매우 어렵다. 게다가 노드에서는 프로세스의 데드락과 같은 상황을 고려하지 않아도 된다. 왜냐면, lock을 사용하지 않기 때문에! Node에 있는 대부분의 함수는 I/O를 직접적으로 수행하지 않기 때문에, 노드 프로세스는 절때 block되지 않는다. Block이 없기 때문에 노드로 확장성 있는 시스템을 작성하면 좋다.


이벤트 루프 구조

노드의 설계는 루비의 이벤트 머신과 파이썬의 Twisted의 영향을 받았고, 따라서 비슷한 구조를 갖는다. 노드는 거기에 이벤트 모델이 적용되었고, 런타임을 만드는데에 라이브러리로서가 아닌 이벤트 루프가 존재하게 되었다. 다른 시스템의 경우 이벤트 루프를 시작하기 위해 항상 blocking 호출이 선행된다. 일반적으로 어떠한 동작을 할 것인지의 내용은 스크립트 시작 부분의 콜백을 통해서 시작되며, 그리고 스크립트 끝 부분에는 EventMachine::run()과 같은 blocking 호출을 통해서 서버가 시작됩니다. 노드에는 start-the-event-loop 와 같은 함수 호출이 없습니다. 노드는 단순히 서버 스크립트를 실행하고 나서 이벤트 루프로 들어갑니다. 더이상 처리해야할 콜백이 없을 경우 노드는 이벤트 루프를 탈출한다. 이러한 동작 방식은 브라우저에서 자바스크립트가 실행되는 방식과 비슷하다. 이벤트 루프가 유저로 부터 숨겨져 있는 방식이다.


HTTP는 스트리밍과 짧은 latency을 염두해 두고 설계된 Node의 첫번째 모듈이다. 따라서 Node는 웹 라이브러리 또는 프레임 워크의 기반으로 적합하다.


노드는 쓰레딩을 사용하지 않도록 설계되었다고 해서 당신의 시스템의 멀티코어를 활용하지 못하는 것은 아니다. child_process.fork() API를 이용하여 자식 프로세스를 만들어서 해당 프로세스와 쉽게 통신을 할 수 있다. 같은 인터페이스를 기반으로 구축된 cluster 모듈을 통해서 프로세스간 소캣을 공유하여 여러개의 코어에 로드 밸런싱을 할 수 있도록 해준다.


관련 개념들

Blocking vs Non-Blocking

Node.js에서 blocking 호출과 non-blocking 호출의 차이점에 대한 내용이다. 여기서는 이벤트 루프와 libuv에 대하여 언급하지만, 해당 주제에 대해 선행지식이 필요하지는 않다. 독자는 기본적인 자바스크립트 언어와 Node.js 콜백 패턴에 대한 지식이 있다고 가정한다.

I/O는 주로 libuv가 지원해주는 시스템의 디스크와 네트워크와의 상호작용하는 것을 뜻한다.

Blocking

Blocking은 Nodejs에서 추가적인 자바스크립트를 실행할 때, 프로세스가 non-자바스크립트 동작이 끝날 때 까지 기다려야 하는 것을 뜻한다. 이러한 현상이 왜 일어나냐 하면은, 이벤트 루프는 blocking 연산이 일어나고 있을 때에는 자바스크립트를 실행할 수 없기 때문이다. 노드에서는 I/O와 같은 자바스크립트가 아닌 동작을 기다리는 게 아니라, CPU intensive한 동작을 실행하기 위해서 자바스크립트의 성능이 저하된 경우에는 blocking이라고 부르지 않습니다. libuv를 사용하는 Nodejs 표준 라이브러리의 Synchronous 메소드는 가장 일반적인 blocking 작업입니다. 네이티브 모듈도 blocking 메소드를 가지고 있다.

Nodejs 표준 라이브러리에서 I/O 메소드는 모두 비동기 버전을 지원합니다. 요 녀석들은 non-blocking이고, 콜백 함수를 받습니다. 그리고 일부 메소드들은 blocking 버전도 있으며, 그녀석들은 이름이 Sync로 끝난다.

Blocking 함수들은 synchronous하게, 그리고 non-blocking 함수들은 asynchronous하게 실행된다.

Concurrency와 Throughput

Nodejs에서 자바스크립트 실행은 싱글 쓰레드이다. 따라서 동시성(Concurrent)은 이벤트 루프가 다른 작업을 끝내고 자바스크립트 콜백 함수들을 실행시킬 수 있는 능력을 뜻한다. 동시성 방식으로 실행되게 하고 싶은 코드는 I/O와 같이 자바스크립트가 아닌 작업을 실행하게 해서 이벤트 루프가 다른 작업을 실행시키게 해 주어야 한다.

예를 들어서, 웹 서버로 온 요청을 처리하는데에 50ms의 시간이 걸리고, 45ms는 비동기로 처리 가능한 데이터베이스 I/O라고 하자. Non-blocking 비동기 작업을 하게 되면, 요청 마다 있는 45ms의 시간은 다른 요청을 처리하는데 사용될 수 있다. 이는 Blocking 메소드를 선택했을 때에 비해 Non-Blocking 메소드를 선택했을 때 갖는 서버 capacity의 엄청난 차이이다.

이벤트 루프는, 동시성을 위해서 추가적인 쓰레드를 만들 수 있는 다른 언어의 동시성 모델과는 다르다.

Blocking코드와 Non-Blocking 코드를 섞어 쓸 시 위험성

I/O를 처리할 때 안티패턴들이 좀 있다. 다음 예제를 보자
 

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
  if (err) throw err;
  console.log(data);
});
fs.unlinkSync('/file.md');


위 예제를 보면 fs.unlinkSync()는 fs.readFile()이 실행되기 전에 실행 될 것같다. 실제 file.md가 읽어지기 전에 말이다.

따라서 순서가 제대로 보장되게 하기 위해서 non-blocking으로만 작성해서 맞는 방법으로 코드를 다시 작성해 보았다.

 

const fs = require('fs');
fs.readFile('/file.md', (readFileErr, data) => {
  if (readFileErr) throw readFileErr;
  console.log(data);
  fs.unlink('/file.md', (unlinkErr) => {
    if (unlinkErr) throw unlinkErr;
  });
});


fs.unlink() 함수는 fs.readFile()함수의 콜백 내부에서 호출되어서, 맞는 호출 순서를 보장하게 된다.

참고

https://nodejs.org/en/about/
https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/


이미 제대로 처리가 된 .ts파일들과 m3u8파일을 간단하게 뿌려주는 서버를 작성한 뒤, 엣지와 크롬 브라우저로 접속했을 때 크롬 브라우저는 재생이 되지 않고 엣지는 재생이 되었다.


여기까지 진행된 소스코드의 스냅샷 링크는 다음과 같다.


https://github.com/Einstrasse/hls-service/tree/20b3434fa3dcf62fa61e5ea31b685d871a3221ad


이때 소스코드를 다시 확인해보니, hls-server 노드 패키지의 정확한 사용법을 몰라서 직접 그 부분을 nodejs http server단에 구현을 했었다.



HLS 관련 토막글에서 대부분 브라우저에서 호환이 된다고 이야기를 들었던 것 같은데, 결국 그런 문서들은 공식 문서가 아니라서 정확도가 떨어질 수 밖에 없다고 생각하고 직접 확인해보도록 해보았다.


http://html5test.com/

위 링크에서 해당 브라우저의 html5를 얼마나 지원하는지에 대한 정보를 확인할 수 있다.

크롬 브라우저로 확인해보니, HTTP Live Streaming/HLS 지원 여부에 No라고 나왔다. 지원한다는 것은 새빨간 거짓말이었던 것이다. 역시 정확한 정보를 얻기 위해서는 공식 문서를 확인하거나 직접 확인하는 것이 빠르고 정확한 것 같다.

반면 엣지 브라우저로 접속을 하니, HLS를 지원하는 것으로 나타났다. 엣지 브라우저에서만 동작을 해서 내가 혹시 서버 설정을 잘못했는가에 대한 의문도 품어보았었지만, 일단은 아닌것으로 확인되었다.


크롬이 HLS 클라이언트를 브라우저 네이티브로 지원하지는 않지만, 아마 자바스크립트로 라이브러리가 있을 것으로 생각해서 hls client로 구글링을 해 보았다.


바로 뭔가 유명해보이는 깃허브 페이지가 나타났다.


https://github.com/video-dev/hls.js/




커밋 수도 많고, Star 달린 수도 많고 실제로 이 hls.js를 사용하는 서비스도 종류도 많고 유명한 곳도 많았다.

클라이언트 사이드에 이 hls.js를 적용하니 크롬에서도 음악 스트리밍 재생이 잘 되었다. 크롬 개발자 도구의 Network 탭을 보니, 이미 받은 ts파일을 다 재생할 때 쯤 새로운 요청을 보내는 식으로 아주 잘 동작하였다.


이 부분까지 진행한 git 커밋 스냅샷이다.


https://github.com/Einstrasse/hls-service/tree/c7861d0594e86273111f02541e0beccf7cf6d0e0


여기까지 진행된 바로는, 서버에 있는 이미 처리된 정적인 ts파일들과 m3u8파일로 스트리밍 재생이 가능하다는 것을 보였다.

이제 여기에 실시간으로 음성을 제공하는 방송자 입장에서 서버에 마이크로 녹음된 음성 정보들을 보내는 동작을 추가해야 한다.


웹 브라우저 마이크 API에 대해 구글을 해 보니 다음과 같은 글을 확인할 수 있었다.

https://www.html5rocks.com/ko/tutorials/getusermedia/intro/

https://developers.google.com/web/fundamentals/media/recording-audio/?hl=ko


두번째 글에서 도움을 많이 얻었다. 하지만 두번째 글에 있는 코드를 그대로 복사해서 적용할 때에는, 글에 나온대로 실행이 되지 않았다.


따라서 MDN에 있는 MediaRecorder API를 찾아보았다.


https://developer.mozilla.org/ko/docs/Web/API/MediaRecorder


확인해보니 MediaRecorder.start() API에서 인자로 timeslice를 넘겨주면, timeslice마다 ondataavailable 이벤트를 호출하게 된다. timeslice를 넣어주지 않으면 명시적으로 MediaRecorder를 stop해주지 않는 이상 ondataavailable 이벤트가 발생하지 않기 때문에 원하는 대로 실행되지 않았던 것이다.

https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/start



여기까지는 클라이언트에서 마이크 API 를 통해 녹음한 음성을 가지고 있는 부분까지 구현되었다. 아직 서버에 음성 데이터를 보내거나 받거나 하는 부분은 구현이 되지 않았다.

여기까지 구현된 모습이다.

https://github.com/Einstrasse/hls-service/tree/394c7f3d33d687c91367c58f464e6f799292e40d



HLS를 이용한 라이브 라디오 방송 웹 앱 개발기 (3)

+ Recent posts