전체 글 51

[리팩토링] 새로고침 해도 검색 결과가 남아있도록 하기

// 검색 버튼 클릭 시 실행const searchHandler = () => { if (!selectedProvider) globalSearch("전체", selectedTag, selectedTerm); else globalSearch(selectedProvider, selectedTag, selectedTerm); // 통합 검색의 경우 통합 검색 페이지로 이동 if (type === "header") { router.push(`/global-search`); }};기존에는 배급사와 입력한 태그, 입력한 검색어를 바탕으로 globalSearch를 호출해서 검색 API를 호출했었다.이렇게 호출해서 스토어에 검색어들도 저장해놓고, 이걸 다시 불러와서 검색창에 적용해놨..

개발/프로젝트 2025.02.28

Zustand로 API 호출 및 상태 관리하기

Zustand를 사용해서 웹툰 관련 상태와 API 호출 함수들을 관리하는 스토어인 WebtoonStore.ts를 작성해 보았다.Zustand는 React 애플리케이션에서 전역 상태 관리를 쉽게 할 수 있도록 도와주는 경량 라이브러리이다. import { create } from "zustand"; interface WebtoonState { webtoons: WebtoonData[]; // 웹툰 목록 tagList: Tag[]; // 태그 목록 selectedProvider: string; // 배급사 selectedTag: string; // 검색 태그 selectedTerm: string; // 검색어 selectedTagIds: number[]; // 선택한 태그 selectedTags..

개발/프로젝트 2025.02.28

Tailwind CSS + SCSS로 반응형 구현하기

기존 Tailwind CSS를 사용하고 있었고, 모바일뷰, 태블릿뷰, 데스크탑뷰 이렇게 3개의 반응형을 구현해야 했다.멘토님께서 BP가 많아서 복잡해질 가능성이 있다고, 아예 뷰마다 컴포넌트를 분리하라고 하셨는데제대로 된 방법을 몰라서 그런지 코드 중복이 너무 많아졌고, 유지보수에도 문제가 있을 것 같았다.그래서 또 알려주신 방법이, Tailwind CSS와 SCSS를 함께 사용하는 것!return ( {/* 인기 웹툰 */} 인기 웹툰 {breakpoint === "mobile" ? ( {popularDa..

개발/프로젝트 2025.02.25

Material UI를 사용하여 페이지네이션 구현하기

이번에는 MUI의 Pagination 컴포넌트를 사용해서 페이지네이션을 구현해 보았다.ThemeProvider를 통해 스타일도 커스텀해 보았다.  React Pagination component - Material UIThe Pagination component enables the user to select a specific page from a range of pages.mui.com↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 참고했던 것  // theme.tsimport { createTheme } from "@mui/material/styles";const theme = createTheme({ breakpoints: { values: { xs: 0, // 모바일 sm: 60..

개발/프로젝트 2025.02.13

Swiper.js 라이브러리를 사용해서 슬라이더 구현하기 (Next.js + React)

Swiper.js 라이브러리를 사용해서 반응형 슬라이더를 구현해 보았다.Swiper는 브라우저에서만 동작하는 라이브러리라서, 'use client'를 선언하여 클라이언트 컴포넌트로 지정해야 사용이 가능했다.// Swipers.tsx"use client";import React, { ReactNode } from "react";import { Swiper, SwiperSlide } from "swiper/react";import { Pagination, Navigation, Autoplay } from "swiper/modules";import "swiper/css";import "swiper/css/navigation";import "swiper/css/pagination";interface Swiper..

개발/프로젝트 2025.02.13

[트러블 슈팅] 자동 로그인 트러블 슈팅

자동 로그인 구현하기현재 로그인 시 액세스 토큰과 리프레시 토큰을 받아 쿠키에 저장 중이다.액세스 토큰은 유효기간이 1시간이며, 리프레시 토큰은 7일이다.그래서 자동 로그인 시, 액세스 토큰이 만료됐을 때 리xuwon.tistory.com분명 이때까지만 해도 자동 로그인 작동도 잘 됐고... 그런데... 문제가 여러 개 생겨버렸다.그래서 트러블 슈팅 블로그 작성 가능하게 됐으니 럭키비키라고 생각하며 기쁜 마음으로 블로그를 적어보려 한다. 첫 번째쿠키 관련 이슈액세스 토큰 만료 시 쿠키 실시간 업데이트 X 원래 페이지 이동 시에 토큰을 확인하고, 자동 로그인 활성화일 땐 액세스 토큰을 재발급 받아서 로그인을 유지하도록 했는데,페이지 이동 시에 다시 확인하고 발급받는 과정을 거치다 보니, 만료 후 로그인 전..

자동 로그인 구현하기

현재 로그인 시 액세스 토큰과 리프레시 토큰을 받아 쿠키에 저장 중이다.액세스 토큰은 유효기간이 1시간이며, 리프레시 토큰은 7일이다.그래서 자동 로그인 시, 액세스 토큰이 만료됐을 때 리프레시 토큰으로 재발급을 받아서 다시 쿠키에 넣어주어야 한다.우선 로그인 상태를 확인하는 함수를 만들어 주었다.import { destroyCookie, setCookie } from 'nookies';import { clearUser } from '@/store/userSlice';import { useDispatch } from 'react-redux';import { useAccessTokenRefreshMutation } from '@/api/userApi';// 로그인 상태 확인 함수export const us..

개발/프로젝트 2025.01.12

Next.js에서 RTK Query로 카카오 로그인 구현하기

카카오 소셜 로그인은1. /social/kakao/login 으로 이동하여 인증 진행2. 받아온 code로 /social/kakao/callback에 GET 요청 보내서 토큰 받기3. 토큰은 쿠키에 저장4. 로그인 성공 후 토큰으로 유저 정보 GET 요청으로 받아옴5. 유저 정보 Redux에 저장이런 흐름으로 진행하였다. // sign-inconst handleLogin = (provider: string) => { switch (provider) { case 'kakao': // 서버의 카카오 로그인 엔드포인트로 이동 window.location.href = 'kakao login endpoint'; // 해당 페이지로 리디렉션 break; case 'google..

개발/프로젝트 2025.01.09

redux-persist로 Redux 상태 유지하기

RTK Query로 로그인 API 구현하고 RTK로 유저 정보 관리하기RTK (Redux Toolkit)Redux의 공식적인 도구 모음으로, 복잡한 상태 관리 로직을 간단하고 효율적으로 작성하도록 돕는다.RTK 상태 관리 기본 흐름1. createSlice로 상태와 리듀서 정의- 상태의 초기값 (initialxuwon.tistory.com↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑로그인을 성공하면 access token으로 유저 정보를 받아와서 Redux에 저장하는 것까지 구현했다.이걸로 이제 유저 정보를 회원정보 수정 페이지에서 불러오려고 했으나...새로고침 및 페이지 이동이 이루어지자 Redux가 깔끔하게 비워졌다. redux-persist알고보니 원래 그렇다는 것!redux-persist ..

개발/프로젝트 2025.01.08

RTK Query로 로그인 API 구현하고 RTK로 유저 정보 관리하기

RTK (Redux Toolkit)Redux의 공식적인 도구 모음으로, 복잡한 상태 관리 로직을 간단하고 효율적으로 작성하도록 돕는다.RTK 상태 관리 기본 흐름1. createSlice로 상태와 리듀서 정의- 상태의 초기값 (initialState) 정의- 상태를 변경할 리듀서 함수 작성- createSlice 호출로 액션과 리듀서 생성import { createSlice, PayloadAction } from '@reduxjs/toolkit';type CounterState = { value: number;};const initialState: CounterState = { value: 0 };const counterSlice = createSlice({ name: 'counter', // sli..

개발/프로젝트 2025.01.07