2017.07.28(금) ~ 07.30(일) 2박3일간 유니톤에 참여했습니다. 아래의 사진에서 볼 수 있듯이, 이번 **유니톤**은 UNIT, NEXTERS, REAL, YAPP 4개의 연합 IT 단체에서 주관하고 있습니다.
여러 기업의 후원을 받으며 각 IT 단체의 학생들이 모여 진행하는 모습이 멋있었습니다. 어떠한 이익을 추구하지 않으며 자신들이 재밌게 즐겼던 이 유니톤 행사가 계속 이어지길 바라는 마음을 갖고 있으셔서 인지 2박3일간 관리하느라 그 어떤 사람들보다 힘들었을 텐데 다들 즐거워 보이셨습니다. 먼저 유니톤 행사를 잘 진행해주셔서 감사합니다!
해커톤이나 유니톤에 참여하는건 이번이 처음이었습니다. 2월에 졸업하고 지금은 직장인이지만 이전에 **SOPT**에서 동아리 활동했던 경험이 있기에 신청을 했는데 운이좋게 참여할 수 있게되었습니다.
이번 유니톤의 전체적인 행사는 다음과 같았습니다.
먼저 2박3일간의 유니톤을 진행하기 일주일 전 **네트워킹 데이**를 통해서 유니톤에서 활용 가능한 후원사들의 서비스와 기술들을 세미나를 통해 배우고 심화시킬 수 있는 시간을 가졌습니다. 저는 회사 일로 인해 참여할 수 없어 아쉬웠지만, 세미나 뿐만 아니라 여러 이벤트도 하며 선물도 나눠주는 시간이였다고 합니다. 네트워킹 데이는 유니톤에 참여하지 않는 분들도 참여할 수 있다고 합니다. (다음에는 꼭 참여해보겠습니다!)
그렇게 네트워킹 데이가 끝나고 일주일 후에 본격적인 유니톤이 시작됩니다! 장소는 공덕역 근처의 서울창업허브 였는데, 시설이 매우매우 좋았습니다.
약 100명정도의 참가자가 미리 정해진 조에 따라 모여 사전 행사 (세미나, 후원사 대표자 분들과 네트워킹)를 가진 후 아이디어 회의를 시작합니다. 사실 시작하기전 사전 행사가 매우 길었는데… 후원을 받는만큼 어쩔 수 없다고 생각합니다. 그래도 AWS 크래딧, 개발 서적, 목배게 등 여려가지 선물을 많이 받아서 좋았습니다!
사전행사를 하며 여러가지 팀게임을 하고 선물도 받으니 조금은 어색한게 풀려 여러가지 아이디어 회의를 했습니다. 오히려 주제가 정해지지 않고 자유이다 보니 아이디어를 정해는데 애먹긴했지만, 대부분 다른조와 마찬가지로 첫날(금요일)은 아이디어 주제를 정한 후 각자 집으로 갔습니다.
둘째날!! 저희 조는 11시에 모여 첫날 다하지 못한 아이디어 회의를 마무리 한 후 바로 개발과 디자인을 시작했습니다. 개발자(안드로이드3, 서버2)5명, 디자인 1명으로 구성되었고 저는 서버 개발로 참여했으나, 구현하고자 하는 앱은 서버 개발보다는 프론트에서 해야할 일이 훨씬 많았기에 프론트를 개발하게되었습니다.
그간 회사에서 사용했던 **ReactNative**에 미리 만들어 놓은 boilerplate를 사용해서 개발했습니다. 정말 잠 한숨 안자며 죽어라 개발했지만 원래 계획했던 것중 한개의 엑티비티를 완성하지 못했지만, 그래도 만족할만큼 개발한것 같습니다.
간단하게 앱에 대해 설명하면, 한국관광공사에서 제공하는 **Tour Api 3.0**을 사용해 지역기반의 여행 정보를 제공하는 앱을 만들었습니다. 다음은 안성된 앱의 엑티비티 입니다.
발표자료와 서버, 클라이언트 코드는 UNITON-5TH Git-Hub 에서 확인 가능합니다.
비록 수상은 못해 조금은 아쉽지만 간만에 오랜시간 집중해 즐겁게 코딩한건 오랜만인것 같습니다. 팀원들과도 더 친해졌으면 좋았겠지만 개발하기에도 짧은 시간이 아쉬웠습니다. 다음 UNITON에도 참여할 것이며, 종종 다른 해커톤과 같은 행사가 있으면 참여할 생각입니다.
회사에서 약 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를 사용해 생성한 프로젝트의 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
QR코드가 출력되어 당황할 수 있지만 이 QR코드를 통해서 방금 생성한 프로젝트를 실행할 수 있습니다. 처음에 말씀드렸다시피 CRNA는 네이티브 코드를 컴파일하지 않고 순수 JavaScript로 작성된 CRNA 프로젝트를 로드하고 Expo 앱을 사용하여 실행한다고 했습니다. 그렇기 때문에 저희가 실행 결과를 확인하기 위해서는 Expo 앱을 추가로 설치해야합니다.
Expo앱은 JavaScript를 작성하여 기본 iOS 및 Android 앱을 제작할 수있게 해주는 도구, 라이브러리 및 서비스 세트이며 Expo앱 다운로드를 통해서 다운로드 후 설치 합니다.
다운로드 후 Expo앱을 실행하고 QR코드를 입력하면 다음과 같이 앱이 실행됩니다.
CLI를 통해 생성한 프로젝트를 실행하여 가상머신을 통해 결과를 확인했을 때와 같이 동일한 결과를 얻을 수 있습니다.
CRNA 프로젝트 실행 - 2 (With 데스크탑 Expo Tool (Expo XDE))
조금 전에는 Expo 앱과 CRNA를 실행한 후 QR코드를 통해 자신의 모바일에서 결과를 확인했습니다. 이번에는 모바일 앱이 아닌 Expo XDE를 설치한 후 가상머신을 통해 실행해 보도록 하겠습니다. Expo XDE를 실행한 후 조금전 생성한 CRNA 프로젝트를 로드합니다. (Expo XDE를 설치하면서 에러가 발생하시는 분은 Expo Installation을 참고해서 Expo XDE에 필요한 다른 도구들을 설치해야합니다.)
프로젝트가 로드되면 우측 상단의 Share 버튼을 통해 조금전 npm start에서 제공했던것과 동일하게 QR코드를 제공받아 모바일 Expo 앱에서 프로젝트를 실행할 수 있고, Device 버튼은 현재 데스크탑에 설치되어 있는 iOS, Android 가상머신을 통해 실행할 수 있습니다.
지금까지 CRNA를 통해 프로젝트를 생성하고 실행해 보았습니다. 마지막으로 ejecting 기능에 대해서 알아보겠습니다. CRNA의 ejecting 기능은 번들로 제공되지 않는 기능이 필요해 라이브러리를 추가해야하는 경우 네이티브 코드 추가 및 빌드가 필요하기 때문에 CRNA를 사용해 생성한 프로젝트를 그대로 사용할 수가 없으므로 사용하게됩니다.
1
$ npm run eject
eject 기능을 실행하면 2가지 옵션중 하나를 선택하게 됩니다. 1) CLI로 생성한 프로젝트와 같은 모양을 같는 프로젝트로 변경할 것인지, 2) 인기있는 라이브러리이 포함된 ExpoKit을 그대로 사용하며 android, ios 와 같은 네이티브 폴더를 eject 할 것인지 선택하게 됩니다.
ExpoKit 옵션을 선택하여 ExpoKit은 그대로 사용할 수 있도록 유지합니다. Expokit 옵션을 선택하게 되면 iOS bundle identifier등 Android의 package name을 설정하게 되는데, 이때 주의해야할 점은 package name을 올바르지 않게 설정하게 되면 후에 네이티브 컴파일에서 오류가 생기게 됩니다. 이를 다시 수정하는건 번거로운 일이니 처음 설정시 신중하게 설정해야합니다. (‘-‘나 ‘_’와 같은 문자 대신 ‘.’을 이용해 패키지 명을 작성합니다.)
이제 네이티브 관련 코드가 생성되었기 때문에 react-native-cli 명령어들을 사용할 수 있습니다. 그러나 react-native run-ios 명령어를 통해 실행을 하게되면 에러가 발생합니다. 이는 eject후 expo관련 모듈을 네이티브(ios) 폴더 아래에서 다시 설치해줘야하기 때문입니다. ios 폴더 아래에서 다음의 명령어를 실행합니다. (CocoaPods이 없으신 분들은 CocoaPods 홈페이지를 참고하여 설치합니다.) 또한 node_module도 다시 설치해야 하기때문에 프로젝트 폴더 아래에서 npm install을 실행합니다.
1 2
$ npm install // 프로젝트 폴더 아래에서 $ pod install // 프로젝트폴더-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로 만든 애니메이션을 실시간으로 앱에서 정적 이미지를 사용하는 것처럼 쉽게 애니메이션을 사용할 수 있도록 도와줍니다.
기존의 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를 그대로 가져가며 프로젝트는 진행하는것이 좋을것 같습니다.
대부분의 개발자는 영어 문서를 많이 읽게 되는데, 이해하기 어려운 부분에서는 Google 번역의 도움을 받아 조금 더 쉽게 일을 진행할 수 있습니다. 그러나 Google 번역을 사용하기 위해 따로 인터넷 창 탭에 페이지를 띄워놓고 번역이 필요한 부분을 복사 붙여넣기 해가며 사용하기에는 분명 번거로운 점이 있습니다.
이때 크롬 확장프로그램으로 제공되는 Google 번역을 사용하면 편리하게 사용할 수 있습니다. 먼저 다음과 같이 Chrome 웹 스토어에서 다운로드를 받습니다.
다운로드 후 다음과 같이 옵션을 통해 설정을 변경합니다.
Google 번역 크롬 확장프로그램을 다운로드 받고 설정까지 마쳤다면 다음과 같이 번역하고자 하는 부분을 드래그했을 때 크롬 확장프로그램을 통해 자동으로 변역된 결과를 볼 수 있습니다.
만약 번역하고자 하는 부분의 범위가 크다면 드래그 한 후 상단 우측(주소창 우측)에 있는 크롬 확장프로그램 목록 중 Google 번역 아이콘을 클릭하면 다음과 같이 번역된 결과를 확인할 수 있습니다.
간단하게 크롬 확장프로그램 Google 번역을 설치한 후 활용해보았습니다. 앞으로 번역이 필요한 부분은 이제 복사 붙여넣기와 같은 번거로운 과정없이 크롬 확장프로그램 Google 번역을 통해서 진행해보세요!
학생들에게 필요한 기능이 어떤게 있을까 생각하다 에밀리에 **시간표 기능**을 추가하게 되었습니다. 학생들이 수강하고 있는 수업 데이터를 얻을 수 있으면 좋겠지만 학교 DB에 접근할 수 있는 권한이 없기 때문에 학생들이 직접 에밀리를 통해 시간표를 등록하도록 만들어야 했습니다.
학교 홈페이지에서 제공하는 **수업 목록(엑셀 파일)**를 활용(파싱)하여 시간표 데이터를 만들고, 이를 학생들이 쉽게 등록 및 수정할 수 있도록 웹 페이지를 만들었습니다 (아무래도 전공도 다양하고 수업의 수도 많다보니 단순히 버튼 및 텍스트로 상호작용하여 시간표를 등록하는것은 불편하다고 판단해 시간이 좀 더 걸리더라도 웹 페이지를 만들기로 결정했었습니다.)
학생들이 많이 사용하는 여러 시간표 앱 및 웹 서비스를 참고해서 다음과 같은 **시간표 등록 및 수정 페이지**를 만들었습니다.
참고로 에밀리 시간표 등록 및 수정 페이지는 **React(Starter-Kit)**를 이용해서 만들었습니다. 아무래도 회사에서 React Native를 사용해서 앱을 개발하다보니 React를 사용해 웹 개발을 함에 있어서도 도움이 많이 되었습니다.
## AWS S3란?
사용자들이 에밀리 시간표 페이지를 사용할 수 있도록 하기 위해서는 웹 페이지를 호스팅해야합니다. 간단히 호스팅할 수 있는 여러 방법들이 있지만, AWS 공부도 할겸 S3를 이용해 호스팅해보기로 생각했습니다.
먼저 S3에 대해 간단하게 알아보겠습니다. **S3(Simple Storage Service)**는 파일을 저장하기 위한 Storage입니다. 일반적인 파일시스템의 개념과는 약간 다르며, 파일 이름을 대표하는 key와 파일 자체로 구분되는 Object Storage입니다.
S3는 **정적 웹사이트 호스팅 기능**을 사용하는 S3 버킷을 **Route 53**을 통해 도메인과 연결해 사용할 수 있습니다. 여기서 동적 웹 사이트 PHP, JSP 등 서버 측 처리에 의존하는 사이트는 S3를 이용해 호스팅할 수 없습니다. 오직 개별 웹 페이지에서 정적 컨텐츠를 포함하며, 클라이언트 측 스크립트를 포함하고 있는 정적 웹 사이트만이 호스팅 가능합니다. (React의 경우 webpack을 통해 번들링된 파일을 호스팅하면 됩니다.)
## 도메인 등록
이미 등록된 도메인이 있다면 이 단계를 생략하면 됩니다. 그러나 inuemily.com과 같이 등록된 도메인 이름이 없는 경우, 원하는 도메인 이름을 만들어 등록해야 합니다.
도메인이 없으시다면 AWS의 Route53 - Registered domains를 통해 도메인을 생성하고, AWS가 아닌 다른 서비스로부터 도메인을 사용중 이라면 기존 도메인 DNS 서비스 역할을 하는 AWS Route53으로 마이그레이션을 해야 S3를 이용한 정적 웹사이트 호스팅이 가능합니다. 마이그레이션 관련해서는 AWS 문서를 참고하면 좋을것 같습니다.
저는 다음과 같이 inuemily.com 이라는 도메인을 갖고 있습니다.
## S3 버킷 생성
inuemily.com과 같은 루트 도메인, www.inuemily.com과 같은 하위 도메인 양쪽의 요청을 모두 지원하려면 두 개의 버킷을 생성해야합니다. 하나의 버킷에 컨텐츠를 포함하고 다른 버킷은 컨텐츠를 포함하는 버킷에 redirection 하도록 버킷을 구성할 것입니다.
먼저 버킷 이름을 호스팅할 웹 사이트 이름과 일치하게 생성합니다. 저는 inuemily.com과 www.inuemily.com 이름으로 버킷을 생성했습니다.
## 웹 사이트 데이터 업로드
2개의 버킷을 모두 생상하였다면 루트 도메인 버킷(inuemily.com)에 컨텐츠를 업로드합니다. 두 번째 버킷(www.inuemily.com)은 추후에 이 루트 도메인 버킷으로 redirection 하도록 설정할 것입니다.
S3 버킷에 파일을 업로드하는 방법으로는 1) 드래그 앤 드롭, 2) AWS CLI 사용 과 같이 2가지 방법이 있습니다. 저는 드래그 앤 드롭을 이용해서 파일을 업로드 하였습니다. (webpack으로 번들링되어 나온 public 폴더의 파일들을 업로드 하였습니다.)
## 버킷 정책(Permissions) 설정
버킷을 생성하고 파일을 업로드했지만 모든 사용자가 버킷에 업로드한 모든 컨텐츠에 접근할 수 있도록 **버킷 정책(Permissions)**을 설정해야 합니다.
다음의 코드를 복사하여 아래 사진과 같이 버킷 정책을 설정합니다. (10번째 라인의 inuemily.com을 자신의 버킷 이름으로 변경해야 합니다.) 두 번째 버킷에는 파일을 업로드하지 않기 때문에 따로 정책을 설정해주지 않아도 됩니다.
파일 업로드와 정책 설정이 끝난 버킷을 정적 웹 사이트 호스팅으로 사용할 수 있도록 기능을 활성화 해야합니다.
루트 버킷을 정적 웹 호스팅에 사용하기 때문에 User this bucket to host a website 에 체크를 하고, index document에는 자신이 작성한 웹 페이지의 index page의 파일명을 기재합니다. 저의 경우 좀 전에 버킷에 업로드한 파일을 보면 index.html이 있고, 이 파일이 index page의 파일이기 때문에 index.html을 기재했습니다.
이번에는 두번째 버킷으로 들어오는 요청을 루트 버킷으로 redirection 할 수 있도록 설정을 합니다. 루트 버킷과는 달리 **Redirect requests**에 체크를 하고, Target bucket or domain에 루트 버킷의 이름을 기재합니다.
## 버킷 Record Set 설정
이제 마지막 단계입니다. 지금까지 생성하고 설정을 마친 S3 버킷을 Route 53을 사용해 연결합니다. 도메인에 따른 호스팅 영역과 추가하는 별칭 레코드는 IP 주소 대신 S3 웹 사이트 엔드포인트를 사용함으로써 Route 53은 별칭 레코드와 S3 버킷이 존재하는 IP 주소 간 매핑을 유지합니다.
Route 53의 Record Set 까지 설정을 마치면 해당 루트 도메인과 서브 도메인을 통해서 사이트에 접속할 수 있습니다.
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에 캐싱된 모듈을 확인해보겠습니다.
결과에서 확인 가능하듯이 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에 고의적으로 캐싱된 모듈을 지우고 다시 새로 로딩하여 사용할 수도 있을것 같습니다.