dayjs 라이브러리 require 에러

Summary

dayjs 라이브러리 를 사용하는 함수 → formatted와 관련된 모든 곳에서 다음과 같은 에러가 일어남.
TypeError: o is not a function

Impact

확인된 곳으로는 메인페이지와 회원가입 페이지에서 해당 에러로 인해 화면 로딩이 되지 않았음.

Solution

const dayjs = require('dayjs');
TypeScript
복사
위 코드를 아래 처럼 변경함.
import dayjs from 'dayjs';
TypeScript
복사

Dectection

QA께 전달 받음

Lesson Learned

잘 된 일

requireimport 의 차이에 대해서 알게됨.

잘 안 된 일

로컬에서 는 정상작동하나 배포후 개발서버와 상용서버에서만 해당 에러가 일어났기 때문에 정확한 원인분석을 하지 못함.

운이 좋았던 일

정확하지는 않지만 미세하게나마 requireimport 의 차이를 알고 있었고 '해당 문제가 원인이지 않을까?' 라는 추측성이 적중함.

CommonJs, AMD, ES6 Module

require 와 import 에 대해서 비교해 보기 위해서는 우선 CommonJs와 AMD(Asynchronous Module Definition), ES6 내장모듈과 같은 자바스크립트의 모듈 시스템에 대해 알고 있어야 합니다.
자바스크립트 ES5는 모듈이라는 개념이 부족하여 각 모듈(또는 파일)간의 의존성 처리에 제한이 있었습니다.
고전적인 웹 프로젝트에서 자바스크립트를 사용하는 방법을 살펴보면, HTML 파일내부에 <script> 태그를 삽입하여 모듈을 로드하고 있습니다.
하지만 이런 방식은 한가지 문제가 있는데, 자바스크립트 파일(또는 모듈)끼리 서로 모듈을 공유하는데 제약이 없다는 점입니다. 그 이유는 <script> 태그로 로드된 모듈은 모두 window 객체의 속성이기 때문에 서로 다른 파일에 위치하면서도 모든 객체를 공유할 수 있기 때문입니다.
각 자바스크립트 파일이 독립적으로 존재하지 못해 발생하는 여러 문제들(예를들어 다른 파일에서 같은 이름의 변수를 사용하는 경우) 때문에 하나의 모듈로 관리하기위한 다양한 패턴(모듈패턴, 즉시실행함수 등)을 사용하여 의존성을 관리할 수 밖에 없었습니다.
이를 해결하기 위한 수단으로 모듈이라는 개념을 도입하여 정의한 방법(또는 표준)이 CommonJs 와 AMD 입니다. 이 둘은 내부적으로 모듈 서로 간의 의존성(로드)이 지원되지 않는 상태로 만들어졌는데, ES6 에 이르러 언어 내부적으로 자바스크립트 모듈 의존성을 지원하게 되었습니다(import, export).

모듈정의 방식의 혼용

ES6 모듈은 기본적으로 CommonJs 와 AMD 모듈을 혼용해서 사용할 수 있습니다. 모듈을 가져오는 부분에 requireimport 를 같이 쓰더라도 문제없이 동작하죠.
import 는 ES6 문법이라 ES5를 사용하는 브라우저에서는 지원하지 않지만 babel 과 같은 트랜스파일러가 해결해줄수 있습니다. AMD 는 생략하고 ES6 와 CommonJs 를 비교하여 설명해보겠습니다.
모듈을 정의한다는 것은 다른 모듈에서 사용할 수 있도록 하나의 모듈로써 노출하겠다는 의미다.

모듈 정의하기 (export)

ES6
// 모듈 전체를 export, 파일내 한번만 사용가능하다. var module = {}; export default module // 모든 속성을 export export *; // 함수를 직접 export export function moduleFunc() {}; var property = "some property"; export {property};
TypeScript
복사
CommonJs
// 모듈 전체를 export module.exports = module // 모든 속성을 export // (아시는 분 알려주세요) // 함수를 직접 export exports.moduleFunc = function() {}
TypeScript
복사

모듈 가져오기 (import)

ES6
// 모듈 전체를 import import module import module as myModule // 모든 속성 import import * from module // 특정 멤버(함수 등)만 import import {moduleFunc, moduleFunc2} from module
TypeScript
복사
CommonJs
// 모듈 전체를 import var module = require('./someModule.js') // 모든 속성 import // (위의 module 객체에 모든 속성이 담아져 온다.) // 특정 멤버(함수 등)만 import, 위의 module을 이용한다. module.moduleFunc
TypeScript
복사

결론

리액트에는 바벨과 같은 트랜스파일링 모듈이 내장되어 있기 때문에 주저없이 ES6(import)를 사용합니다.
실제로, CommonJS(require)와 ES6(import) 혼용하는것도 가능하다고는 하지만, 대부분의 커뮤니티에서는 가급적이면 통일되게 사용하는 것이 좋다고 주장하고 있으며, 실제로 에러를 유발할수도 있다고 하였고 저희 서비스가 해당 원인이 되었습니다.
팀장께서는 require는 NodeJS에서 사용하는 방법이고, import는 JavaScript에서 이용하는 방법이라고 설명해주셨습니다.