11월 12일에 대하여
오늘 한 일
1. 블로그 > 캘린더 UI 페이지 구현 : 학습 내역 시각화하기 위한 목적
꾸준히 학습은 하지만, 분명 무언가를 하고 있지만 한 게 아무것도 없는듯한 감정이 들고, 돌아보면 한 거는 있지만 또 다시 생각해보면 한 게 없는듯한 악순환에 놓인 기분이었다. 그래서 시각화했을 때 많은 위안을 받아서 계속 미루어왔던 로그를 작성하기로 함. 그래서 나의 목적에 캘린더형 UI가 가장 적합하다고 봐서 블로그에 캘린더 UI로 로그를 모아서 볼 수 있는 페이지를 추가하기로 함.
배운 점
1. 트러블슈팅 : URL은 ?m=2025-10인데, 로그에는 계속 2025-11만 찍히던 이슈
[배경]
- 주소창: http://localhost:4321/dev-logs?m=2025-10
- 콘솔 로그: month는 계속 2025-11 (현재 달)만 출력
[원인]
SSG(정적 프리렌더) + 쿼리 파라미터
Astro는 SSG인데, 빌드할 때 /dev-logs를 한 번 렌더링해서 HTML 파일로 만들어 둔다. 이때 Astro.url / Astro.request.url이 보는 값은 “쿼리 없는 /dev-logs” 뿐이게 된다. (?m=… 정보 없음)
const monthParam = searchParams.get("m"); // 항상 null
const month = monthParam ?? defaultMonth; // -> 항상 defaultMonth(현재 달)
그래서 브라우저에서 ?m=2025-10으로 들어가도 서버는 “이미 빌드된 /dev-logs HTML”을 그대로 보내준다. 그 HTML 안에 들어있는 month 값은 빌드시 계산된 2025-11 그대로 보이게 되면서 주소창은 10월, 내부 변수는 계속 11월이라는 이상한 상황이 발생된 것이다.
정리하자면 빌드 시점과 요청 시점의 불일치 때문이다. 쿼리 파라미터는 서버 코드를 다시 실행 시키지 못 하고 빌드 시점에만 한 번 실행되고 이후에는 그대로 계속 사용하는 것이다. 따라서 요청 들어올 때마다 서버에서 다시 렌더링하게 하면 해결이 되는 것이다.
[해결 방법]
=> 요청 → SSR → month 계산 → HTML 반환
이 페이지는 쿼리 파라미터에 따라 내용이 바뀌는 동적 페이지니까,
export const prerender = false; // SSR
이 페이지의 경우 매 요청마다 새로 렌더링하게 함. 그리고 URL 파싱 역시 Astro 공식문서에 따라 아래와 같이 변경함.
const { searchParams } = Astro.url;
const monthParam = searchParams.get("m");
SSR을 사용하기 위해서는 별도의 어댑터를 추가해야 하며 나의 경우 Vercel을 사용하고 있기 때문에 아래 명령어를 통해 추가해주었다.
pnpm astro add vercel(참고)
2. 트러블슈팅 : 제대로 뒤로가기가 안 되는 이슈
[배경]
Q. 왜 캘린더 월을 url로 관리하지?
A. 새로고침을 하더라도 현재 보고 있는 월이 유지됨. 다른 방법으로는 완전 클라이언트 상태(ex. useState), 클라이언트 + localStorage
캘린더 UI의 경우 월을 변경할 수 있는데, 이때 월 추적을 url로 관리하였음.
http://localhost:4321/dev-logs?m=2025-10
그런데 페이지 레이아웃에 위치하는 Back(뒤로가기)를 클릭하면, [TO-BE] dev logs 페이지에서 뒤로가서 메인 페이지로 이동하는 것이 아닌 [AS-IS] dev-logs?m=2025-09 같이 월 변경 내역이 노출되는 이슈가 발생했다.
[원인]
a 태그의 경우 새로운 페이지로 이동하면서, 히스토리에 새 항목을 추가함. 그래서 메인 페이지로 나가기 전에, dev-logs의 “이전 월들”을 다 지나쳐야 하는 상태에 놓이게 되는 것이다.
[해결 방법]
=> 월 이동은 히스토리 “덮어쓰기”로 처리하기
<a
href={`?m=${prev}`}
onclick={`event.preventDefault();location.replace(this.href);`}
aria-label="Previous month">←</a
>
a 태그에 event.preventDefault()를 추가하고 location역시 현재 href로 대체하게 했다.
- event.preventDefault()
- a 태그의 기본 네비게이션을 막아 더 이상 자동으로 pushState 되지 않음
- location.replace(this.href)
- 현재 페이지를 this.href(예: ?m=2025-10)로 교체
- 브라우저 히스토리에 새 항목을 추가하지 않고, “현재 스텝을 덮어쓰기” 하는 동작
추후 계획
-
캘린더 UI에도 나만의 무언가를 담고 싶음. (참고)
- 아이디어 : 지렁이 게임
-
캘린더 컴포넌트 살펴보기
회고
처음에는 캘린더 컴포넌트를 하나의 컴포넌트로 만들었는데, 캘린더 월 변경 하는 걸 따로 두어서 같은 캘린더인데 분리되어 있는 인상이었음. 그래서 컴파운드 패턴으로 구현을 해 봄.
- 상태 준비
- month 문자열을 실제 날짜 정보로 변환
- 달력의 시작 요일 / 총 날짜 계산
- 월 이동(prev / next)
- 렌더링
- 월 변경 함수
- 한 달의 날짜 배열 만들기
- 렌더링에서 7개씩 끊기