ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [MongoDB] Date type 필드의 default 설정시 Date.now와 new Date() 중 어떤 걸 사용해야할까?🧐
    데이터베이스/Mongodb 2023. 3. 5. 16:52

     
    틀린 내용이 있을 수 있습니다.
    발견하시면 말씀 부탁드립니다! 🙇


     
    Nest.js 환경에서 mongoose 패키지를 사용해 스키마를 정의한 뒤 mongodb에 데이터를 저장했다. 이후 데이터를 조회해 보니 이상한 점이 있었다. 저장 시점이 다른데 DB상에는 생성 시간이 동일하게 기록돼 있던 것이다. 데이터 생성시간은 mongoose schema에서 default로 생성되게끔 했기에 스키마 파일을 살펴보았다.

    @Schema() 
    export class Path {
      @Prop({ type: String, required: true })
      tag: string;
    
      @Prop({ type: String, required: true })
      coordinateList: string;
    
      @Prop({ type: Object, required: true })
      pathColor: { name: string, code: string };
      
      @Prop({ type: Date, default: new Date() })
      regDate: Date
    }

    regDate의 default를 new Date()로 설정한 이유는 new Date() 함수에 파라미터를 따로 주지 않으면 현재 시간을 반환해 주기 때문이다. 이 부분이 문제인가 싶어 구글링을 했고 답은 mongoose 공식 문서에서 찾을 수 있었다. 

    https://mongoosejs.com/docs/defaults.html#default-functions

    공식문서에 나온 것과 같이 default: Date.now로 수정한 뒤 테스트를 했고, DB에 데이터 생성 시간이 정상적으로 기록된 걸 확인할 수 있었다.  (공식문서에 Date.now 말고 Date.now()를 사용하는 방법도 나와있다.)
     
     
    추가로 몇 가지 테스트를 했다. 우선 아래는 default값을 new Date()로 설정하고 저장한 데이터의 모습이다. test-new Date() 1을 저장하고 몇십 초 뒤 test-new Date() 2를 저장했으나, regDate(생성시점)가 동일하게 기록된 것을 볼 수 있다. 

    // new Date()
    [
      {
        _id: ObjectId("6401d705c8168bf53fec71e4"),
        tag: 'test-new Date() 1',
        coordinateList: '[]',
        pathColor: { name: 'black', code: '#000000' },
        regDate: ISODate("2023-03-03T11:15:50.583Z"),
        __v: 0
      },
      {
        _id: ObjectId("6401d712c8168bf53fec71e6"),
        tag: 'test-new Date() 2',
        coordinateList: '[]',
        pathColor: { name: 'black', code: '#000000' },
        regDate: ISODate("2023-03-03T11:15:50.583Z"),
        __v: 0
      },
    ]

     
    이 데이터들의 timestamp를 확인해 보았다. 각 데이터별로 timestamp는 다르게 저장되었으며 regDate와 차이가 있었다.

    test> ObjectId("6401d705c8168bf53fec71e4").getTimestamp()
    ISODate("2023-03-03T11:16:21.000Z") // regDate: ISODate("2023-03-03T11:15:50.583Z")
    test> ObjectId("6401d712c8168bf53fec71e6").getTimestamp()
    ISODate("2023-03-03T11:16:34.000Z") // regDate: ISODate("2023-03-03T11:15:50.583Z")

     
     
    다음은 default값을 Date.now로 설정한 경우이다. regDate가 올바르게 들어간 것을 확인할 수 있다.

    // Date.now
    [
     {
        _id: ObjectId("6401d76135ad5a32672c363f"),
        tag: 'test-Date.now 1',
        coordinateList: '[]',
        pathColor: { name: 'black', code: '#000000' },
        regDate: ISODate("2023-03-03T11:17:53.376Z"),
        __v: 0
      },
      {
        _id: ObjectId("6401d77235ad5a32672c3641"),
        tag: 'test-Date.now 2',
        coordinateList: '[]',
        pathColor: { name: 'black', code: '#000000' },
        regDate: ISODate("2023-03-03T11:18:10.035Z"),
        __v: 0
      }
     ]

     
    이 경우 데이터의 timestamp와 regDate를 비교했을 때 ms부분을 제외하고는 동일할 것을 볼 수 있다.

    test> ObjectId("6401d76135ad5a32672c363f").getTimestamp()
    ISODate("2023-03-03T11:17:53.000Z") // regDate: ISODate("2023-03-03T11:17:53.376Z")
    test> ObjectId("6401d77235ad5a32672c3641").getTimestamp()
    ISODate("2023-03-03T11:18:10.000Z") // regDate: ISODate("2023-03-03T11:18:10.035Z")

     
    왜 차이가 발생하는 건지 생각해 보았다. new Date()를 실행하면 Date 객체가 결과값으로 나오는 반면 Date.now는 그 자체로 함수이다. new Date()를 실행한 결과값인 Date 객체는 메모리에 할당된 후 갱신되지 않고 계속적으로 사용되지만, Date.now는 함수이기에 그때그때마다 실행되어 올바른 현재 시각을 나타내는 것 같다.

     
    뇌피셜이 맞는건지 확인해보고자 테스트를 했다. path.schema.ts 파일에 date가 찍히도록 console.log를 추가한 뒤 서버를 재시작하고 로그에 찍힌 시간을 봤다.

     
    그 다음 일정 텀을 두고 db에 데이터를 저장한 뒤 조회했다. 두 데이터 모두 각기 다른 시점에 저장한 것인데 regDate는 서버 콘솔창에 찍힌 시간과 (ms 제외하고) 동일한 것을 확인할 수 있었다.

    각기 다른 시점에 저장한 데이터이지만 regDate는 동일
    다른 시점에 저장했기 때문에 실제 timestamp는 다름

     
     
     
    결론: mongodb 스키마 정의 시, 타입이 Date인 필드의 default는 함수로 설정해야 한다. 만약 defulat를 new Date()로 설정할 경우, 특정 시점에 반환된 Date객체를 계속해서 사용하므로 예상치 못한 값이 db에 저장될 것이다.
     
     
     


    참고 자료


     

    댓글

jaejade's blog ٩( ᐛ )و