✅ 오늘 배운 것
어제 포스팅에서 'Moment'와 'Date' 클래스가 서로 달라서 한 클래스에서 사용 가능한 메소드를 다른 클래스에서 사용하지 못하는 것은 맞지만, 구체적으로 어떻게 다른지는 알아보지 못한 것 같았다.
moment의 경우 javascript 환경에서 사용할 수 있는 라이브러리에서 제공하는 클래스이며, date는 javascript에서 기본으로 제공하는 클래스이다. date와 moment의 가장 큰 차이 중 하나는 moment는 여러 timezone에서 사용 가능한 반면 date는 기본 UTC 시간대에서만 동작한다는 것이다.
그리고 moment의 공식문서를 읽다가 발견한 내용인데... 여러 timezone을 지원해야 할 경우 moment를 사용할 때 size(정확히 어떤 크기를 말하는것인지는 잘 모르겠다..!)가 커질 수 있다는 문제가 있다고 한다. 그래서 moment를 개발한 사람들이 더 이상 moment의 기능을 고도화하지 않을 예정이며, 오히려 Luxon같은 타 라이브러리로 쉽게 옮겨갈 수 있도록 지원한다는 내용도 있었다.
어제 기존 코드와 바꾸려고 한 코드의 클래스 타입이 다르다는 것을 안 이유도, convertGmtToKst라는 함수를 사용하면 Date 객체를 리턴하는데, 이 객체는 moment에서는 제공하는 isSame()이라는 함수를 사용할 수 없기 때문이었다. 소스코드를 보니 moment에서 제공하는 기본 내장 함수들이 더 많긴 하더라. 캡처한 것보다 훨씬 많고 몇십 개 쯤 되었다.
암튼 그러하고, moment의 공식 문서에서도 moment보다는 date-fns나 Luxon의 사용을 권장하고 있는 점이 신기했다. 하긴 모든 라이브러리들은 사용자의 편의성을 위해 만들어졌지... 더 편리한 다른 라이브러리가 있다면 사용자에게 그것을 권장하는 것도 맞겠다는 생각이 들었다. 그래서 현재는 일단 도입한 moment를 잘 사용하되, 공식문서에서 언급했던 그런 문제점들을 실감하게 된다면 위에서 언급한 다른 라이브러리를 고려해봐도 좋을 것 같다.
오늘 처리하기 위한 프로젝트 이슈는 '인박스 뷰 만들기'이다. 하나의 뷰를 만드는 것이지만 기존에 만들어진 투데이 뷰의 UI나 로직을 대부분 그대로 가져와서 쓰기 때문에 실제로는 하나의 뷰를 바닥부터 만드는 것보다는 훨씬 적은 시간이 들 것 같다.
다만 상태관리나 세부 컴포넌트 부분에서는 변경해 줄 부분들이 꽤 보였다. 첫째로, 인박스 뷰에서는 현재 선택된 날짜를 관리하는 상태 변수인 selectedDate 변수가 필요 없었다. 왜냐하면 인박스 뷰는 날짜들이 지정되지 않은 모든 투두들을 모아 보여주는 뷰이기 때문이다. 둘째로, 기존의 투두는 왼쪽 체크 버튼을 누르면 완료 처리가 가능했는데, 인박스에서는 투두를 완료 처리할 수 없도록 해야 했다. 그러려면 기존에 사용하던 DailyTodo라는 컴포넌트 대신에 다른 컴포넌트를 별도로 정의해서 사용해주어야 했다. 마지막으로, 인박스에서도 투두 컴포넌트를 클릭하면 모달창이 떠서 해당 투두를 수정하거나, 삭제하거나, 오늘 하거나, 다른 날 할 수 있도록 지정하는 기능이 필요했다.
위 기능들을 구현하기 위해서 InboxTodos 컴포넌트를 만들었다. InboxTodos(인박스 투두 리스트를 보여주는 컴포넌트)와 DailyTodos(데일리 투두 리스트를 보여주는 컴포넌트)의 가장 큰 차이점은 현재 어떤 날짜가 선택되었는지를 나타나는 selectedDate라는 상태 변수가 InboxTodos에는 없다는 거였다. 사실 최대한 DailyTodos 컴포넌트를 재사용하고 싶었는데, 괜히 inbox={true} 같은 속성을 넣는 것보다는 별도의 컴포넌트로 정의하는 것이 더 상태관리가 복잡해지지 않는 방향이 아닐까 싶어서 이 방식을 선택했다. 하지만 이 역시 좋은 방향인지는 확실하지 않으니 다음 멘토링 때 멘토님께 조언을 구해보는 것이 좋겠다.
그리고 인박스 뷰에서 투두 컴포넌트를 사용하기 위해서는 InboxTodo 컴포넌트를 별도로 만들기로 했다. 사실 DailyTodo 컴포넌트에 inbox 속성을 추가해서 inbox이면 기본값인 체크 아이콘을 다른 아이콘으로 변경하는 방법도 있는데, 그러면 inbox 속성값이 true일 때는 DailyTodo 컴포넌트에 정의해 둔 제법 많은 상태관리 로직들이 아예 쓰일 일이 없었다. 그보다는 InboxTodo라는, 보이는 것은 거의 비슷하지만 상태관리 로직은 거의 없는 컴포넌트를 하나 만드는 것이 더 좋아 보였다.
또한 모달창의 경우는 원래 있던 TodoModal 컴포넌트에 inbox 속성을 추가해보기로 했다. 굳이 모달창만 속성을 추가해서 재사용하고 나머지 InboxTodos나 InboxTodo 컴포넌트는 따로 구현하는 이유는, 내가 생각했을 때 상태관리 로직의 차이가 크기 때문이다. 즉 모달창은 인박스 뷰에서 쓰나 투데이 뷰에서 쓰나 상태관리 로직이 사용되는 정도는 비슷했다. 그러나 나머지 경우, 만약 컴포넌트를 재사용하고 인박스 뷰에서 쓸 경우, 투데이 뷰에서는 쓰이는 여러 상태관리 로직들이 inbox 속성값이 true가 되면 쓰이지 않게 되어버리는데, 이게 맞는 방향인가? 라는 의문이 들었다. 물론 이 논리에는 구체적인 이유나 근거는 없어서... 다음 멘토링 때 프론트 멘토님께 이런 판단이 괜찮을지 조언을 구해봐야겠다.
백엔드 서버를 로컬에 띄워두고, 프론트 서버에서는 localhost로 요청을 보내면서 작업하고 있었다. 아직 개발 단계라 가끔씩 API와의 통신 오류 등이 날 때 로그를 확인하려면 이 방법이 더 편했기 때문이었다. 그런데 잠시 쉬고 저녁을 먹었다가 다시 작업하는데 이러한 오류가 났다. 이전에도 종종 있었던 오류로, Jwt에 있는 토큰 발행 시각(iAt, issuedAt)보다 컴퓨터의 현재 시각이 이르기 때문에 발생하는 오류였다.
그래서 현재 컴퓨터에서 시간을 올바르기 가리키고 있는지도 잘 확인해 보았다. 이전에 멘토님께 한번 이 문제를 말씀드렸더니, 어쩌면 컴퓨터가 아니라 사용하는 에뮬레이터의 시간 설정에서 문제가 있을 수도 있다고 말씀해주셨었다. 그도 그럴 것이 어떨 때는 잘 되고, 또 다시 에뮬레이터를 재시작하면 안 되고... 그런 문제였기 때문이다. 만약 맥북의 시간 설정에 문제가 있었다면 오히려 항상 이런 오류가 떴어야 하겠다.
GPT에게 물어보니 장고 서버와 맥북의 timezone을 제대로 설정해 보라는 식으로 답변해줬다. 맥북은 이미 timezone이 올바르게 설정되었는지 확인했으니, 장고 서버의 settings.py의 TIME_ZONE 변수의 값을 UTC에서 Asia/Seoul으로 바꿔 주었다. 오류가 어쩌다가 일어날 때도 있고 아닐 때도 있어서(...) 이 방법으로 완전히 해결된 것인지는 잘 모르겠지만, 일단 설정을 고쳐주니 잘 동작하였다.
TIME_ZONE = 'Asia/Seoul'
그리고 공식문서를 찾아보니 장고에서 timezone을 다룰 때, DB에는 UTC 시간으로 저장해 놓은 다음 장고 서버 내부에서 객체에 timezone을 적용해서 end user에게 필요할 경우 그 값을 리턴해 준다고 나와있었다. 그리고 만약 서버에서 timezone을 이렇게 내부적으로 처리하지 않고, 그냥 timezone을 고려하지 않고 단일 UTC를 기준으로 사용하고 싶다면 settings.py에서 USE_TZ 변수값을 False로 바꿔주면 된다고 한다.
그리고 설정을 잘 찾아보면 시간값을 DB에도 UTC가 아닌 다른 timezone으로 저장할 수 있는 것 같았지만 이를 권장하지는 않는다고 했다. 이유가 여러 나라에서 DST(daylight saving time)를 사용하기 때문이라는데, 설명을 읽어보니 DST는 하절기에는 시간이 더 앞으로 이동하고, 동절기에는 더 뒤로 이동하도록 조정한다는 것 같았다. 그래서 시간값을 UTC가 아닌 다른 timezone으로 지정하면 이렇게 1년에 두 번, 시간을 앞으로 또는 뒤로 더 이동시키는 시점에 timezone 관련 오류가 날 수 있다고 나와있어서 매우 신기했다.
그리고 항상 잘 사용하던 파이썬의 datetime.datetime 객체에는 tzinfo라는 timezone 정보를 저장하는 속성이 있다고 한다. 만약 settings.py에 USE_TZ 설정이 True로 되어있다면, 이 속성을 통해 앞서 언급한 것처럼 datetime 등의 object에서 timezone 정보를 처리하는 것으로 이해했다. 즉 USE_TZ 설정이 True라면 생성되는 datetime 객체들은 전부 timezone-aware(timezone에 대한 정보를 갖고 있음)인 것이고, 반대의 경우 timezone-naive(timezone에 대한 정보를 갖고 있지 않음. 기본값은 UTC)인 것이다.
즉 정리해보면 만약 엔드 유저에게 timezone 관련 정보를 리턴해야 할 경우, 그리고 USE_TZ 값이 true로 되어있는 경우, 처음에 DB에서는 UTC로 저장된 값을 꺼내온다. 그러나 datetime 타입 등의 객체를 사용하면서 장고 서버에서 자체적으로 이 객체 안에 tzinfo라는 속성을 넣어줘서 timezone을 인식할 수 있게 한다. 객체가 인식하는 timezone은 아마도 위에서 settings.py에 설정한 TIME_ZONE 변수의 값일 것이다. 그러면 해당 객체를 serializer 등을 통해서 응답으로 리턴할 때 엔드 유저에게는 timezone이 적용된 시간값이 리턴된다고 볼 수 있다.
✅ 궁금한 점
1. timezone에 관련해서 내가 이해한 것이 정확할까 궁금하다.
'개발 일기장 > SWM Onestep' 카테고리의 다른 글
20240805 TIL: dev와 prod 환경 분리하기 (0) | 2024.08.05 |
---|---|
20240804 TIL (0) | 2024.08.04 |
20240802 TIL: 코드 참고하면서 react query로 api 호출하기 (2) | 2024.08.02 |
20240801 TIL (0) | 2024.08.01 |
20240731 TIL (0) | 2024.07.31 |