new Array(length).map()으로 배열 초기화시 콜백이 실행되지 않는 문제
Dev Log
자바스크립트에서 배열을 초기화하려고 new Array(4).map(() => [])을 사용했는데 의도대로 동작하지 않았다. 원인을 찾아보니 JavaScript의 빈 슬롯과 undefined의 차이, 그리고 map() 메서드가 빈 슬롯에 대해 콜백을 실행하지 않는다는 특성 때문이었다. 문제 상황특정 크기의 배열을 만들고 각 요소를 []로 초기화하려고 했다.const result = new Array(4).map(() => []);console.log(result); // 기대: [[], [], [], []] 하지만 실제 결과는 달랐다.console.log(result); // [empty × 4]console.log(result[0]); // undefined 원인 분석빈 슬롯 vs undefined자바스..
에러 메시지와 스택 트레이스 분석을 통해 문제 해결하기
Dev Log
자바스크립트로 카드 게임 로직을 구현하던 중 TypeError: Cannot read properties of undefined라는 에러를 만났다. 코드가 복잡하고 짐작가는 부분이 단번에 떠오르지 않아 막막했는데, 에러 메시지와 스택 트레이스를 차근차근 분석해가며 원인을 찾아낸 과정을 기록해본다. 에러 발생게임 로직을 테스트하던 중 특정 입력에서 다음과 같은 에러가 발생했다.TypeError: Cannot read properties of undefined (reading 'array') at getLastElement (03.debug.js:45:19) at submitCard (03.debug.js:85:7) at processPlayerAction (03.debug.js:103:3)..
Next.js Server Actions를 활용한 캐시 재검증 문제 해결
Dev Log
문제 상황프로젝트를 진행하던 중 사용자가 팔로우/언팔로우 버튼을 클릭할 때, 프로필 정보가 즉시 업데이트되지 않고 페이지를 새로고침해야만 변경 사항이 반영되는 문제가 발생했다.이 문제는 사용자 경험을 저하시키는 상황이기때문에 반드시 개선이 필요했다.원인 분석팔로우 버튼을 클릭하면 팔로우 상태를 변경하는 함수가 Sanity의 데이터를 업데이트한다. 하지만 이 변경 사항이 클라이언트 화면에 즉시 반영되지 않았다.Next.js는 성능 최적화를 위해 서버에서 가져온 데이터를 캐싱한다. 팔로우 상태가 변경되어도 Next.js는 여전히 이전에 캐싱된 데이터를 사용하고 있었기 때문에, 사용자가 보는 화면은 업데이트되지 않은 상태로 유지되었다.서버 사이드에서 데이터 변경이 발생한 후, 클라이언트의 캐시를 재검증(rev..
[PeaNutter] Vercel 배포 시 하위 라우터 404 오류 해결
Dev Log
문제 상황Vercel로 프로젝트를 배포한 후, '/profile/edit'과 같은 하위 라우터에서 새로고침을 하니 404 Not Found 오류가 발생했다. 원인 분석SPA와 전통적인 웹 애플리케이션의 차이전통적인 웹 애플리케이션사용자가 각 페이지에 접근할 때마다 서버에서 해당 페이지의 HTML을 제공한다.'/posts', '/profile' 등의 페이지를 요청하면, 서버는 각각의 요청에 맞는 HTML 파일을 찾아서 보내주는 방식이다. 싱글 페이지 애플리케이션(SPA)초기 접근 시 단 하나의 HTML 파일인 index.html을 로드하고, 그 이후의 모든 페이지 전환은 브라우저에서 JavaScript를 사용하여 동적으로 내용을 변경한다.사용자가 '/profile/edit' 같은 URL로 이동하더라도 실제..
[PeaNutter] TypeScript에서 버튼 클릭 이벤트 타입 에러 해결
Dev Log
문제 상황Firebase의 GoogleAuthProvider와 GithubAuthProvider를 사용하여 소셜 로그인 기능을 구현하던 중, 하나의 함수에서 버튼의 name 속성에 따라 다른 provider를 호출하도록 구현했다. Sign up with Google Sign up with Githubconst onClickSocialLogin = async (e: React.MouseEvent) => { const { target: { name }, } = e; let provider; const auth = getAuth(app); if (name === "google") { provider = new GoogleAuthProvider(); } if (name === "git..
[PearNutter] 소셜미디어 앱 마이그레이션 계획
Dev Log
PeaNutter는 만화 Peanuts를 테마로 한 소셜미디어 프로젝트다.노마드코더 트위터 클론코딩 컨테스트에 참여하여 만들었으며, 사용자들이 자신의 소소한 일상과 생각을 'nut'으로 공유하고 소통하는 플랫폼을 구상했다. Peanuts는 땅콩이라는 뜻 외에도 '별 볼 일 없는 것들'이라는 의미로 쓰인다. 이 의미에서 착안하여 누구나 일상의 소소한 순간들을 가볍게 나누는 공간을 상징하고자 PeaNutter라는 이름을 붙였다.마이그레이션 배경컨테스트 당시 2주 동안 매주 제공되는 미션을 진행하며 기획안부터 필수 기능 3가지까지 구현해야 했다. 개인적으로 추가하고 싶었던 부가 기능까지 욕심내다 보니, 결과적으로 만족스러운 완성도에 도달하지 못했다. 이러한 아쉬움으로 PeaNutter는 리팩토링 및 마이그레이..
[AI 감성 일기장] LocalStorage를 활용한 데이터 영속성 구현
Dev Log
로컬 스토리지를 활용하여 사용자의 다크모드 설정, 일기 데이터, AI 답장을 저장하고 관리하는 기능을 구현했다.페이지를 새로고침하거나 재방문해도 사용자의 데이터가 유지된다.다크모드 설정 저장설정 불러오기애플리케이션 로드 시 로컬 스토리지에서 사용자의 마지막 다크모드 설정을 불러온다.const ThemeProvider = ({ children }: ThemeProviderProps) => { const loadDarkModeSetting = () => { const storedDarkModeSetting = localStorage.getItem("darkMode"); return storedDarkModeSetting ? JSON.parse(storedDarkModeSetting) : fals..
[AI 감성 일기장] GPT API를 활용한 AI 답장 기능 구현
Dev Log
사용자가 작성한 일기에 대해 GPT API를 활용하여 상담 전문가 관점의 답장을 받을 수 있는 기능을 구현했다.GPT API 연동프롬프트 설정GPT API에 전달할 메시지 구조를 정의했다.const messages = [ // 역할 정의 { role: "system", content: "You are a Counseling Expert, specializing in personal development through diary writing.", }, // 사용자 일기 입력 { role: "user", content: ` """ ${prompt} """`, }, // AI 지시 사항 { role: "user", conten..
[AI 감성 일기장] Tailwind CSS와 Context API를 활용한 다크모드 구현
Dev Log
Tailwind CSS의 다크모드 기능과 React Context API를 활용하여 애플리케이션 전체에서 일관된 다크모드 전환 기능을 구현했다.Tailwind 설정tailwind.config.ts에서 다크모드를 클래스 기반으로 설정했다.darkMode: "class", // .dark 클래스 존재 여부에 따라 다크모드 활성화theme: { extend: { colors: { // 색상 스키마 }, },},이를 통해 HTML 요소에 .dark 클래스가 추가되면 다크모드 스타일이 적용되도록 했다. 다크모드 상태 관리ThemeContext 생성Context API를 사용해 다크모드 상태를 전역으로 관리했다.const ThemeContext = createContext({ isDark..
[AI 감성 일기장] Diary 페이지 일기 상세보기 기능 구현
Dev Log
Diary 페이지에서 특정 일기를 불러와 상세 내용을 보여주는 기능을 구현했다.URL 파라미터로 전달된 일기 ID를 통해 데이터를 조회하고, 존재하지 않는 일기에 대한 예외 처리도 추가했다.일기 데이터 불러오기useDiary 커스텀 훅특정 ID의 일기를 찾아 반환하는 커스텀 훅을 구현했다.const data = useContext(DiaryStateContext);DiaryStateContext를 통해 전역에서 관리되는 일기 데이터에 접근한다. 특정 일기 찾기 및 예외 처리const [curDiaryItem, setCurDiaryItem] = useState();useEffect(() => { const currentDiaryItem = data.find((item) => item.id === id);..