Uniton 후기

2017.07.28(금) ~ 07.30(일) 2박3일간 유니톤에 참여했습니다. 아래의 사진에서 볼 수 있듯이, 이번 **유니톤**은 UNIT, NEXTERS, REAL, YAPP 4개의 연합 IT 단체에서 주관하고 있습니다.

유니톤 주최 연합 IT 단체

여러 기업의 후원을 받으며 각 IT 단체의 학생들이 모여 진행하는 모습이 멋있었습니다. 어떠한 이익을 추구하지 않으며 자신들이 재밌게 즐겼던 이 유니톤 행사가 계속 이어지길 바라는 마음을 갖고 있으셔서 인지 2박3일간 관리하느라 그 어떤 사람들보다 힘들었을 텐데 다들 즐거워 보이셨습니다. 먼저 유니톤 행사를 잘 진행해주셔서 감사합니다!

해커톤이나 유니톤에 참여하는건 이번이 처음이었습니다. 2월에 졸업하고 지금은 직장인이지만 이전에 **SOPT**에서 동아리 활동했던 경험이 있기에 신청을 했는데 운이좋게 참여할 수 있게되었습니다.

이번 유니톤의 전체적인 행사는 다음과 같았습니다.

행사 일정

먼저 2박3일간의 유니톤을 진행하기 일주일 전 **네트워킹 데이**를 통해서 유니톤에서 활용 가능한 후원사들의 서비스와 기술들을 세미나를 통해 배우고 심화시킬 수 있는 시간을 가졌습니다. 저는 회사 일로 인해 참여할 수 없어 아쉬웠지만, 세미나 뿐만 아니라 여러 이벤트도 하며 선물도 나눠주는 시간이였다고 합니다. 네트워킹 데이는 유니톤에 참여하지 않는 분들도 참여할 수 있다고 합니다. (다음에는 꼭 참여해보겠습니다!)

그렇게 네트워킹 데이가 끝나고 일주일 후에 본격적인 유니톤이 시작됩니다! 장소는 공덕역 근처의 서울창업허브 였는데, 시설이 매우매우 좋았습니다.

서울창업허브

약 100명정도의 참가자가 미리 정해진 조에 따라 모여 사전 행사 (세미나, 후원사 대표자 분들과 네트워킹)를 가진 후 아이디어 회의를 시작합니다. 사실 시작하기전 사전 행사가 매우 길었는데… 후원을 받는만큼 어쩔 수 없다고 생각합니다. 그래도 AWS 크래딧, 개발 서적, 목배게 등 여려가지 선물을 많이 받아서 좋았습니다!

사전행사를 하며 여러가지 팀게임을 하고 선물도 받으니 조금은 어색한게 풀려 여러가지 아이디어 회의를 했습니다. 오히려 주제가 정해지지 않고 자유이다 보니 아이디어를 정해는데 애먹긴했지만, 대부분 다른조와 마찬가지로 첫날(금요일)은 아이디어 주제를 정한 후 각자 집으로 갔습니다.

둘째날!! 저희 조는 11시에 모여 첫날 다하지 못한 아이디어 회의를 마무리 한 후 바로 개발과 디자인을 시작했습니다. 개발자(안드로이드3, 서버2)5명, 디자인 1명으로 구성되었고 저는 서버 개발로 참여했으나, 구현하고자 하는 앱은 서버 개발보다는 프론트에서 해야할 일이 훨씬 많았기에 프론트를 개발하게되었습니다.

그간 회사에서 사용했던 **ReactNative**에 미리 만들어 놓은 boilerplate를 사용해서 개발했습니다. 정말 잠 한숨 안자며 죽어라 개발했지만 원래 계획했던 것중 한개의 엑티비티를 완성하지 못했지만, 그래도 만족할만큼 개발한것 같습니다.

간단하게 앱에 대해 설명하면, 한국관광공사에서 제공하는 **Tour Api 3.0**을 사용해 지역기반의 여행 정보를 제공하는 앱을 만들었습니다. 다음은 안성된 앱의 엑티비티 입니다.

App 설명 - 1
App 설명 - 2

발표자료와 서버, 클라이언트 코드UNITON-5TH Git-Hub 에서 확인 가능합니다.

비록 수상은 못해 조금은 아쉽지만 간만에 오랜시간 집중해 즐겁게 코딩한건 오랜만인것 같습니다. 팀원들과도 더 친해졌으면 좋았겠지만 개발하기에도 짧은 시간이 아쉬웠습니다. 다음 UNITON에도 참여할 것이며, 종종 다른 해커톤과 같은 행사가 있으면 참여할 생각입니다.

마지막으로 단체사진 투척하며 UNITON에 참여하신 모든분들 고생하셨습니다!

UNITON 단체사진

create-react-native-app (crna) 사용하기

들어가며

회사에서 약 6개월간 React Native를 이용한 게임 앱(eyePoker - 화상채팅과 함께하는 포커게임) 개발을 마무리하였습니다. 후에 React Native를 사용해 새로운 앱을 개발할때 사용할 수 있도록 그간 React Native로 개발하면서 익힌 것들을 boilerplate로 만들려고 했습니다.

약 6개월전 처음 React Native를 접했을때 당시 v0.39 였으나 최근 확인해보니 stable 버전이 v0.46 까지 나와있었습니다. v0.46을 이용해 React Native 프로젝트를 생성하고자 하니 **Create React Native App**이 추가되어 있었습니다. (앞으로 Create React Native App을 **CRNA**라고 부르겠습니다.)

CRNA는 기존의 React Native CLI를 이용하지 않고 프로젝트를 생성하는 방법입니다. 지금부터 CRNA에 대해 알아보고 기존의 React Native CLI를 사용해서 프로젝트를 생성할때와 어떤 다른점이 있는지 알아보도록 하겠습니다.

create-react-native-app (crna)란?

먼저 아래의 영상을 통해 CRNA를 통해 프로젝트를 생성하고 실행하는 과정을 볼 수 있습니다. 약 6분정도의 짧은 영상입니다. 간단하게 한번 보시면 뒤의 내용을 좀 더 쉽게 이해할 수 있습니다.

React Native Document에서도 확인할 수 있듯이, CRNA는 React Native 프로젝트를 시작하기 훨씬 쉽게 해주는 도구입니다. React를 접해보신분은 한번쯤 들어보셨을 React Create App에서 영감을 받은 것이고, Expo라는 회사와 협업을 통해 만들어진 결과물 입니다.

Android나 iOS를 개발해본 경험 없이 React Native를 통해 앱 개발을 시작함에 있어서 네이티브 빌드에 필요한 도구를 설치하고 환경을 구성하는 것은 쉽지 않은 작업 입니다. 그러나 CRNA를 사용하면 Xcode 또는 Android Studio가 필요없으며 Linux 또는 Windows를 사용하여 iOS 장치 용으로도 개발할 수 있습니다. (이제 Mac이 필수가 아닙니다…)

CRNA는 네이티브 코드를 컴파일하지 않고 순수 JavaScript로 작성된 CRNA 프로젝트를 로드하고 Expo 앱을 사용하여 실행합니다. 그렇기 때문에 CRNA를 사용해 프로젝트를 생성할 경우 기존 CLI를 사용해서 생성한 결과물과는 다르게 프로젝트 폴더에 android와 ios 폴더가 없습니다.

그러나 아직 대부분의 React Native 라이브러리들을 사용하기 위해서는 android와 ios 폴더 아래 있는 네이티브 프로젝트 파일(Java, Objective-C, Swift)을 직접 수정하고 컴파일 해야하는 하는데 이러한 경우는 어떻게 할까요?

먼저, Expo는 이러한 경우를 대비해 카메라, 비디오, 연락처등 자주 사용되는 인기있는 라이브러리들을 번들로 제공하고 있습니다. 그렇기 때문에 대부분의 간단한 앱들은 따로 외부 라이브러리를 추가하지 않아도 만들 수 있습니다. 그러나 번들로 제공되지 않는 기능이 필요해 라이브러리를 추가해야하는 경우, 네이티브 코드 추가 및 빌드가 필요하기 때문에 CRNA를 사용해 생성한 프로젝트를 그대로 사용할 수가 없습니다. 이러한 경우를 대비해 Create React App에서 제공하는것과 마찬가지로 CRNA에서도 ejecting 기능을 제공하고 있습니다.

npm run eject 명령어를 통해 CLI를 통해 프로젝트를 생성한 것과 유사한 프로젝트를 얻을 수 있습니다. android와 ios 프로젝트 폴더가 생성됩니다. 그렇기 때문에 네이티브 빌드를 하기 위해서는 Xcode 또는 Android Studio가 필요합니다.

CRNA 프로젝트 생성

저는 nvm을 통해서 node v8.1.4, npm v4.6.1을 사용하고 있습니다. (npm은 v5.x대의 경우 에러가 발생하기 때문에 v4.x로 재설치를 하고 진행하셔야 합니다.)

이제 CRNA를 사용해서 프로젝트를 생성해보겠습니다.

1
2
3
4
$ npm i -g create-react-native-app
$ create-react-native-app crna-project
$ cd crna-project
$ npm install

**CRNA**를 사용해 프로젝트를 생성한 결과 구조는 다음과 같습니다.

1
2
3
4
5
6
7
8
crna-project
├── App.js
├── App.test.js
├── app.json
├── node_modules/
├── package.json
├── yarn.lock
└── README.md

참고) **CLI**를 생성해 프로젝트를 생성한 결과는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
cli-project
├── index.android.js
├── index.ios.js
├── android/
├── ios/
├── node_modules/
├── __tests__/
├── app.json
├── package-lock.json
├── package.json
└── yarn.lock

기존 CLI를 사용했을때와 다르게 index.android.js와 index.ios.js는 App.js로 변경되었으며, 네이티브 빌드에 사용되는 android, ios 폴더는 사라졌습니다. 또한 app.json 파일은 그대로 존재하지만 파일 내용에는 차이가 있습니다.

**CRNA**를 사용해 생성한 프로젝트의 app.json

1
2
3
4
5
{
"expo": {
"sdkVersion": "18.0.0"
}
}

**CLI**를 사용해 생성한 프로젝트의 app.json

1
2
3
4
{
"name": "cli-project",
"displayName": "cli-project"
}

CRNA를 사용해 생성한 프로젝트의 app.json은 expo의 sdkVersion을 표시하고 있습니다.

CRNA 프로젝트 실행 - 1 (With 모바일 Expo 앱)

이제 생성한 CRNA 프로젝트를 실행해 보겠습니다. 기존 CLI를 통해 프로젝트를 생성했을 때는 실행을 위해 Android SDK와 Xcode가 필요하였고 다음의 명령어로 네이티브 빌드를 하여 실행했습니다.

1
2
$ react-native run-android
$ react-native run-ios

그러나 CRNA를 사용해 생성한 프로젝트는 다음의 명령어로 실행합니다. 명령어를 실행하면 다음과 같은 QR코드가 출력됩니다.

1
$ npm start

crna 프로젝트를 실행한 결과

QR코드가 출력되어 당황할 수 있지만 이 QR코드를 통해서 방금 생성한 프로젝트를 실행할 수 있습니다. 처음에 말씀드렸다시피 CRNA는 네이티브 코드를 컴파일하지 않고 순수 JavaScript로 작성된 CRNA 프로젝트를 로드하고 Expo 앱을 사용하여 실행한다고 했습니다. 그렇기 때문에 저희가 실행 결과를 확인하기 위해서는 Expo 앱을 추가로 설치해야합니다.

Expo앱은 JavaScript를 작성하여 기본 iOS 및 Android 앱을 제작할 수있게 해주는 도구, 라이브러리 및 서비스 세트이며 Expo앱 다운로드를 통해서 다운로드 후 설치 합니다.

다운로드 후 Expo앱을 실행하고 QR코드를 입력하면 다음과 같이 앱이 실행됩니다.

Expo 앱을 통한 CRNA 프로젝트 실행 - 1
Expo 앱을 통한 CRNA 프로젝트 실행 - 2

CLI를 통해 생성한 프로젝트를 실행하여 가상머신을 통해 결과를 확인했을 때와 같이 동일한 결과를 얻을 수 있습니다.

CRNA 프로젝트 실행 - 2 (With 데스크탑 Expo Tool (Expo XDE))

조금 전에는 Expo 앱과 CRNA를 실행한 후 QR코드를 통해 자신의 모바일에서 결과를 확인했습니다. 이번에는 모바일 앱이 아닌 Expo XDE를 설치한 후 가상머신을 통해 실행해 보도록 하겠습니다. Expo XDE를 실행한 후 조금전 생성한 CRNA 프로젝트를 로드합니다.
(Expo XDE를 설치하면서 에러가 발생하시는 분은 Expo Installation을 참고해서 Expo XDE에 필요한 다른 도구들을 설치해야합니다.)

Expo XDE로 CRNA 프로젝트 로드

프로젝트가 로드되면 우측 상단의 Share 버튼을 통해 조금전 npm start에서 제공했던것과 동일하게 QR코드를 제공받아 모바일 Expo 앱에서 프로젝트를 실행할 수 있고, Device 버튼은 현재 데스크탑에 설치되어 있는 iOS, Android 가상머신을 통해 실행할 수 있습니다.

이외에도 Expo XDE의 기능은 여러가지가 있습니다. 이외의 기능들은 Expo Documentation & Guides에서 확인할 수 있습니다.

CRNA 프로젝트 ejecting

지금까지 CRNA를 통해 프로젝트를 생성하고 실행해 보았습니다. 마지막으로 ejecting 기능에 대해서 알아보겠습니다. CRNA의 ejecting 기능은 번들로 제공되지 않는 기능이 필요해 라이브러리를 추가해야하는 경우 네이티브 코드 추가 및 빌드가 필요하기 때문에 CRNA를 사용해 생성한 프로젝트를 그대로 사용할 수가 없으므로 사용하게됩니다.

1
$ npm run eject

CRNA - eject 옵션 선택

eject 기능을 실행하면 2가지 옵션중 하나를 선택하게 됩니다. 1) CLI로 생성한 프로젝트와 같은 모양을 같는 프로젝트로 변경할 것인지, 2) 인기있는 라이브러리이 포함된 ExpoKit을 그대로 사용하며 android, ios 와 같은 네이티브 폴더를 eject 할 것인지 선택하게 됩니다.

ExpoKit 옵션을 선택하여 ExpoKit은 그대로 사용할 수 있도록 유지합니다. Expokit 옵션을 선택하게 되면 iOS bundle identifier등 Android의 package name을 설정하게 되는데, 이때 주의해야할 점은 package name을 올바르지 않게 설정하게 되면 후에 네이티브 컴파일에서 오류가 생기게 됩니다. 이를 다시 수정하는건 번거로운 일이니 처음 설정시 신중하게 설정해야합니다. (‘-‘나 ‘_’와 같은 문자 대신 ‘.’을 이용해 패키지 명을 작성합니다.)

CRNA - eject 설정 사항

crna 프로젝트를 eject하고 난 후에는 다음과 같이 폴더 구조가 변경됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
crna-project (eject with ExpoKit)
├── App.js
├── App.test.js
├── app.json
├── android/
├── ios/
├── node_modules/
├── .expo
├── .expo-source/
├── package.json
├── yarn.lock
└── README.md

이제 네이티브 관련 코드가 생성되었기 때문에 react-native-cli 명령어들을 사용할 수 있습니다. 그러나 react-native run-ios 명령어를 통해 실행을 하게되면 에러가 발생합니다. 이는 eject후 expo관련 모듈을 네이티브(ios) 폴더 아래에서 다시 설치해줘야하기 때문입니다. ios 폴더 아래에서 다음의 명령어를 실행합니다. (CocoaPods이 없으신 분들은 CocoaPods 홈페이지를 참고하여 설치합니다.) 또한 node_module도 다시 설치해야 하기때문에 프로젝트 폴더 아래에서 npm install을 실행합니다.

1
2
$ npm install // 프로젝트 폴더 아래에서
$ pod install // 프로젝트폴더-ios 폴더 아래에서

eject 후 Expo - ios 모듈 설치

이제 react-native run-ios 명령어를 실행하면 가상머신을 통해 실행할 수 있습니다. 그러나 기존 CLI를 통해 생성한 프로젝트와 다르게 CRNA로 프로젝트를 생성한 후 eject 하여도 최초 CRNA로 프로젝트를 생성한 것이라면 Expo 혹은 exp(Expo Cli Tool)의 도구가 필요합니다. (React Native 프로젝트 실행시 Packager라는게 필요한데 CRNA로 프로젝트를 셍성했다면 Expo혹은 exp가 이 packager를 대신 실행하게 됩니다.)

그렇기 때문에 Expo를 실행하여 프로젝트를 로드하거나 새로운 터미널을 실행하여 exp start를 실행한 후 react-native run-ios를 실행하면 가상머신에서 프로젝트가 실행됩니다.

Expo 라이브러리 사용해보기

Expo SDK는 다양한 라이브러리를 제공하고 있습니다. 기본적으로 자주 쓰이고 필요한 라이브러리들은 다 있고, 관리가 잘 되고 있기 때문에 믿고 쓸 수 있습니다. 아직 그 수가 많지는 않지만 Feature-Request에서 사용자와의 소통을 통해 지속적으로 라이브러리를 추가해가고 있습니다.

지금부터는 Expo SDK에 Lottie 라이브러리를 사용해 보겠습니다. Lottie는 Airbnb에서 만들어 제공하는 라이브러리로 iOS, Android, React Native에서 After Effects로 만든 애니메이션을 실시간으로 앱에서 정적 이미지를 사용하는 것처럼 쉽게 애니메이션을 사용할 수 있도록 도와줍니다.

Expo Lottie Document를 참고해서 Lottie 라이브러리를 사용하면 다음과 같은 간단히 테스트가 가능합니다.

Expo lottie 테스트

기존의 CLI를 통해 프로젝트를 생성하고 Lottie를 사용해보신분들은 정말 과정이 간편해졌다는걸 느낄 수 있습니다. Expo SDK를 사용하지 않고 React Native 프로젝트에서 Lottie를 사용하기 위해서는 네이티브 코드에 Lottie관련 코드들을 추가하고 컴파일 과정에서 생기는 문제를 해결하기 위해서 많은 시간이 필요했습니다. (React Native Lottie - Github을 참고해보세요) 그러나 Expo SDK를 사용하면 정말 간단한게 사용할 수 있게 되었습니다.

마치며

새롭게 추가된 CRNA는 React Native에 있어서 큰 변화라고 생각합니다. 아직은 Expo SDK에서 제공하는 라이브러리가 많지 않기 때문에 ejecting이 불가피하지만 Expo의 지속적인 관리로 앞으로 더 많은 라이브러리들과 기능들이 추가되고 점점 사용하기 쉽고 편하게 바뀔것이라 생각됩니다.

앞으로 새롭게 시작하는 React Native 프로젝트는 CLI보다는 CRNA를 통해서 프로젝트를 시작한 후 ejecting 기능을 사용해 Expo SDK를 그대로 가져가며 프로젝트는 진행하는것이 좋을것 같습니다.

추가적으로 Expo에 대해서 더 궁금한게 있으신분은 Expo-Frequently Asked Questions를 참고하시면 될것 같습니다.

참고

require는 어떻게 동작할까?

Node.js를 사용하며 문득 require에 대해 궁금증이 생겼습니다. 대부분 자주 사용하는 코드를 모듈 형식으로 만들어 **module.exports**를 사용해서 객체 인스턴스를 내보내고 이를 다른 파일에서 **require**를 통해서 사용하게 되는데 대부분 여러 파일에서 모듈을 require해 사용하게 됩니다. 이때 여러파일에서 중복되는 require는 계속해서 새로운 인스턴스를 생성하는지, 그게 아니라면 어떻게 동작되는지 궁금해서 공부하며 찾아본 내용을 포스팅합니다.

Node.js의 모듈 로딩 시스템

Node.js는 간단한 모듈 로딩 시스템을 갖고 있습니다. Node.js에서 파일과 모듈은 일대일로 대응하며 각 파일은 별도의 모듈로 처리됩니다. 그렇기 때문에 여러곳에서 하나의 파일에 작성된 모듈을 필요로 할때 동일한 인스턴스를 사용할 수 있도록 합니다.

즉, 모듈을 require할 때마다 새로운 인스턴스가 생성되는 것이 아니라 캐싱된 객체 인스턴스를 재사용하는 것 입니다.

Node.js 공식 Documentation에서 확인할 수 있듯이 한번 로딩(require)된 모듈은 **require.cache**라는 객체에 캐싱됩니다. key값으로 해당 모듈 파일의 경로를 갖게 되는데 key값이 삭제된다면 다음 require 요청시 다시 재로딩 하게됩니다. 다음 코드를 통해서 require.cache에 캐싱된 모듈을 확인해보겠습니다.

1
2
3
4
5
// foo.js

module.exports = {
foo: "bar"
};
1
2
3
4
5
6
7
8
9
// index.js

var foo = require('./foo');

console.log('---------- require.cache ----------')
console.log(require.cache);

console.log('---------- require.cache keys ----------')
console.log(Object.keys(require.cache));

foo.js 와 index.js 파일을 통해 확인한 결과는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
kimjongmin:~/work/require_test $node index.js

---------- require.cache ----------
{ '/Users/kimjongmin/work/require_test/index.js':
Module {
id: '.',
exports: {},
parent: null,
filename: '/Users/kimjongmin/work/require_test/index.js',
loaded: false,
children: [ [Object] ],
paths:
[ '/Users/kimjongmin/work/require_test/node_modules',
'/Users/kimjongmin/work/node_modules',
'/Users/kimjongmin/node_modules',
'/Users/node_modules',
'/node_modules' ] },
'/Users/kimjongmin/work/require_test/foo.js':
Module {
id: '/Users/kimjongmin/work/require_test/foo.js',
exports: { foo: 'bar' },
parent:
Module {
id: '.',
exports: {},
parent: null,
filename: '/Users/kimjongmin/work/require_test/index.js',
loaded: false,
children: [Object],
paths: [Object] },
filename: '/Users/kimjongmin/work/require_test/foo.js',
loaded: true,
children: [],
paths:
[ '/Users/kimjongmin/work/require_test/node_modules',
'/Users/kimjongmin/work/node_modules',
'/Users/kimjongmin/node_modules',
'/Users/node_modules',
'/node_modules' ] } }
---------- require.cache keys ----------
[ '/Users/kimjongmin/work/require_test/index.js',
'/Users/kimjongmin/work/require_test/foo.js' ]

위의 결과에서 확인할 수 있듯이 require.cache 객체는 key값으로 해당 모듈 파일의 경로를 사용해 모듈을 캐싱하고 있습니다.

require가 갖는 문제점

이제 require를 통해 모듈을 로딩할 경우 파일의 경로를 캐시 키로 사용하여 다른 여러 파일에서 동일한 파일을 필요로하는 경우 동일한 캐싱 된 모듈을 사용하는 것을 알게되었습니다.

이로인해 불필요한 메모리 사용을 피할 수 있습니다. 어찌보면 한번 로딩된 후 재사용되기 때문에 싱글 톤과 같이 동작한다고도 생각할 수 있습니다. 그러나 이러한 모듈의 캐싱 방식이 다음과 같이 제대로 동작하지 않는 경우가 있습니다.

  • 파일 이름의 잘못된 대 / 소문자 사용
  • 다른 모듈이 NPM에서 동일한 모듈을 설치할 때

대 / 소문자 구분

Windows 및 macOS는 기본적으로 파일 시스템에서 대 / 소문자를 구분하지 않습니다. 따라서 “foo.js” 라는 파일과 “FOO.js” 라는 파일을 검색 할 경우, 이 두 검색은 실제 파일 이름의 대소 문자와 상관없이 같은 폴더에서 동일한 파일을 찾습니다.

그러나 Node.js에서는 대/ 소문자를 구별하기 때문에 파일 이름을 두 개의 개별 모듈로 취급하므로 “foo.js”와 “FOO.js”가 같은 파일이라는 것을 알지 못합니다.

이 때문에 Windows와 macOS 모두에서 require 호출의 객체 캐시를 쉽게 파기 할 수 있습니다. 다음의 예시 코드에서 쉽게 확인할 수 있습니다.

1
2
3
4
5
// foo.js

module.exports = {
foo: "bar"
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// index.js

var foo = require('./foo');
var FOO = require('./FOO');

console.log('---------- require.cache keys ----------')
console.log(Object.keys(require.cache));

FOO.foo = 'different bar';

console.log('---- foo object ----');
console.log(JSON.stringify(foo, null, 2));

console.log('---- FOO object ----');
console.log(JSON.stringify(FOO, null, 2));

console.log('---- foo object ----');
console.log(JSON.stringify(foo, null, 2));

결과는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kimjongmin:~/work/require_test $node index.js

---------- require.cache keys ----------
[ '/Users/kimjongmin/work/require_test/index.js',
'/Users/kimjongmin/work/require_test/foo.js',
'/Users/kimjongmin/work/require_test/FOO.js' ]
---- foo object ----
{
"foo": "bar"
}
---- FOO object ----
{
"foo": "different bar"
}
---- foo object ----
{
"foo": "bar"
}

결과에서 확인 가능하듯이 require된 모듈은 key값으로 해당 모듈 파일의 경로를 사용해 캐싱되고 있습니다. require시 대 / 소문자를 구분해 key로 사용하기 때문에 2개의 객체가 생성되었으나, 결과적으로는 파일 시스템에 도달하면 같은 파일이 2번 반환된 것입니다. 즉, 같은 파일에 서로 다른 모듈로 2개가 생성되어 있는 것 입니다.

require 문에 파일 이름을 잘못 입력 한 것과 관련된 다른 문제도 있습니다. 대 / 소문자를 구분하는 파일 시스템에 배포하는 경우 실제 파일과 동일하게 처리되지 않은 버전은 파일을 찾지 못합니다.

NPM 모듈 종속성

모듈 캐싱이 제대로 작동하지 않는 상황은 NPM에서 둘 이상의 **모듈 종속성이 같은 모듈을 설치**할 때 입니다. 즉, 프로젝트가 NPM의 “Foo”와 “Bar”에 의존하고 Foo와 Bar가 둘 다 “Baz”에 의존하면 NPM (버전 2 이하)은에 의존하는 각 모듈에 대해 “Baz”의 다른 사본을 설치합니다.

NPM 버전 3 에서는 종속성 목록을 병합하여 문제를 해결하고 있습니다. Foo와 Bar가 둘 다 동일한 Baz의 버전에 의존하면 하나의 사본만 설치합니다.

그러나, Foo와 Bar가 Baz의 서로 다른 (서로 호환되지 않는) 버전을 사용한다면, 여전히 두 버전을 모두 설치하며, 이 경우 모듈 캐시를 공유하지 않습니다.

마치며

반복되는 코드를 모듈화 하거나 각 기능 별로 모듈화 하게되면 결국 다른 파일에서 require를 통해 사용하게 되는데, 이때마다 어떤식으로 동작하게 되는지 궁금했었습니다. 이번 포스팅을 작성하면서 이에 대한 궁금증을 해결할 수 있었고, 결과적으로 한번 로딩된 모듈은 캐싱되어 사용되기 때문에 각기 파일마다 require를 많이 한다고해서 크게 걱정할 필요는 없을 것 같습니다.

또한, 필요에 의해 (필요한 상황이 있을지 모르겠지만…) require.cache에 고의적으로 캐싱된 모듈을 지우고 다시 새로 로딩하여 사용할 수도 있을것 같습니다.

7. Jasper

강의 내용 훑어보기

이번 강의는 음성인식 & IPA, Jasper, Jasper (hardware), Jasper (software), Jasper (configure), **Jasper (write your apps)**에 대한 내용을 다루고 있습니다.

음성인식 & IPA

IPA(Intelligent Personal Assistant)에 대해 알아보고 그 중 많이 사용되는 Jasper가 음성을 인식하는 과정에 대해 설명합니다.

Jasper

음성으로 제어하는 오픈 소스 플랫폼 Jasper에 대해 간략하게 설명합니다.

Jasper (haredware)

Jasper를 사용하기 위해 하드웨어 설정을 진행합니다. 음성을 듣기 위해 이어폰 설정과 음성을 녹음하기 위해 마이크 설정을 합니다.

Jasper (software)

github에서 Jasper를 다운로드 한 후 추가적으로 STT와 TTS 엔진을 설치합니다.

Jasper (configure)

Jasper의 기본 환경설정 파일을 생성하고 STT 사용을 위해 Google Speech API Key를 발급받습니다.
Jasper에서 사용하는 Google STT와 TTS의 기본 언어를 한글로 변경한 후 예시로 Time과 Weather 모듈을 한글로 변경합니다.

Jasper (write your apps)

Jasper의 구조를 알아본 후 표준 모듈(Standard module)과 알림 모듈(Notification module)에 대해 알아 본 후 음성으로 LED를 키고 끌 수 있는 LED 표준 모듈을 만드는 실습을 합니다.

6. Extra GPIO

강의 내용 훑어보기

이번 강의는 카메라(V2), 온습도(SZH-EK024), 조도(BH1750), 초음파(HC-SR04) 센서에 대한 내용을 다루고 있습니다.

카메라(V2)

간단하게 카메라(V2) 모듈에 대해 알아보고 사진 캡쳐, 영상 촬영, 효과 주기, 웹 서버를 통해 스트리밍해보는 실습을 합니다.

온습도(SZH-EK024)

온습도 센서를 통해 온도와 습도를 측정하고 이전 시간에 배운 LED를 통해 현재의 온습도 상태를 표시하는 실습을 합니다.

조도(BH1750)

I2C 버스 인터페이스를 사용하기 위한 설정을 하고 센서를 사용합니다.

초음파(HC-SR04)

초음파 센서가 거리를 측정하는 방법에 대해 알아보고 LED를 통해 현재의 거리 정도를 표시하는 실습을 합니다.

5. GPIO, LED, BreadBoard

강의 내용 훑어보기

이번 강의는 GPIO, LED, **BreadBoard**에 대한 내용을 다루고 있습니다.

GPIO

GPIO의 정의와 작동원리에 대해 알아봅니다.

LED

파이썬을 이용해서 간단하게 LED를 켜고 끄는 실습을 합니다.

BreadBoard(빵판)

BreadBoard(빵판)의 원리에 대해 알아본 후 여러개의 LED를 갖고 실습을 합니다.

4. bash 설정, vim 설정, gist 사용하기

강의 내용 훑어보기

이번 강의는 bash 설정, vim 설정, **gist 사용하기**에 대한 내용을 다루고 있습니다.

bash 설정

bash 쉘에 대해 간단하게 알아본 후 .bashrc 파일을 수정하여 프롬프트(prompt)의 색상 정보를 변경하고, .dircolors 파일을 수정하여 터미널의 폴더 색상을 변경하는 실습을 합니다.

vim 설정

**apt**를 사용하여 vim을 설치하고 .vimrc 파일을 수정하여 vi 편집기를 좀 더 편리하게 사용할 수 있도록 설정을 추가합니다.

gist 사용하기

**gist**에 대해 알아본 후 지금까지 실습한 자신만의 설정파일을 gist에 업로드 합니다.

3. 사용자 관리, 원격접속 vi 편집기

강의 내용 훑어보기

이번 강의는 사용자 관리(추가 & 삭제), 원격접속(VNC, SSH), **vi 편집기**에 대한 내용을 다루고 있습니다.

사용자 관리(추가 & 삭제)

Unix와 Linux에서 사용자를 추가하고 삭제해보며, sudoers 파일을 수정해 root 권한을 사용할 수 있도록 수정해 봅니다.

원격접속(VNC, SSH)

GUI 환경으로 작업이 가능한(VNC)와 CUI 환경인 SSH 접속을 통해 원격으로 라즈베리파이를 다룰 수 있는 실습을 합니다.

vi 편집기

IDE(통합 개발 환경)와 vi(편집기)의 공통점과 차이점에 대해 알아보고 vimtutor.txt 파일을 통해 vi 실습을 합니다.

2. Unix / Linux 소개

강의 내용 훑어보기

이번 강의는 운영체제(OS), 커널, 쉘, Unix & Linux, **간단한 명령어 실습**에 대한 내용을 다루고 있습니다.

운영체제(OS), 커널, 쉘

Unix와 Linux에 대해 알아보기 전 간단히 운영체제(OS), 커널, 쉘의 역할과 정의에 대해서 알아봅니다.

Unix & Linux

Unix의 역사와 Linux가 만들어지게 된 배경에 대해 설명하고, 리눅스의 특징에 대해 설명합니다.

간단한 명령어 실습

Unix와 Linux의 디렉터리 계층 구조를 살펴보고 terminal을 통해 자주 사용되는 명령어를 실습합니다.

1. 라즈베리파이 OS 설치 & 설정

강의 내용 훑어보기

이번 강의는 아두이노와 라즈베리파이 비교, 라즈베리파이에 OS(라즈비안) 설치, **라즈베리파이 설정(인터넷, 한글)**에 대한 내용을 다루고 있습니다.

아두이노와 라즈베리파이 비교

아두이노와 라즈베리파이는 서로의 연관검색어 상단에서 볼 수 있는 만큼 유사한점이 많지만 다른점이 분명히 있습니다. 아두이노와 라즈베리파이의 다른점에 대해 알아보고 언제 어떤것을 사용하는것이 효율적일지 알아봅니다.

라즈베리파이에 OS(라즈비안) 설치

라즈베리파이에 라즈비안을 설치합니다. 설치 시 NOOBS 프로그램을 통해 더욱 간편하게 OS를 설치할 수 있습니다.

라즈베리파이 설정(인터넷, 한글)

라즈베리파이3는 라즈베리파이2와 달리 무선 LAN 카드를 내장하고 있기 때문에 기존 모델과 달리 Wifi 동글을 사용하지 않아도 Wifi 사용이 가능합니다. 간편하게 GUI 상에서 Wifi 설정하는 법을 알아보고 학교와 같은 고정IP를 사용해야 하는 경우에도 대비해 고정IP 설정하는 법을 알아봅니다.

추가적으로 라즈비안을 처음 설치했을 경우 한글폰트가 설치되어있지 않기 때문에 한글 폰트 설치 및 언어 설정을 합니다.