카테고리 없음

[D'art] MainPage UI 및 기능 구현

고래강이 2025. 2. 23. 17:48
이전에 구현한 Navbar가 있고 mainPage에 주요 컨텐츠로는 Pagination 기능 및 검색 기능을 위한 요소들이 존재한다. MainPage를 구현하면서 Data fetching을 위해 DB도 연결을 하고 좀 더 완성도 있는 프로젝트를 만들어보자.

 

📋 계획한 목표

  1. 주요 content들 컴포넌트 만들기
  2. 검색 기능 구현하기

🖥️ 진행 상황

일단 상향식으로 개발을 해보려고 한다. 작은 컴포넌트 요소들을 먼저 만든 후 합치면서 프로젝트를 만들어 나갈 것이다. 상향식의 이유로는 구현해야하는 사항들이 정해져 있고 굳이 추가될 요소들이 없기에 이후에 복잡한 요소를 추가할 필요가 없기에 현재 상황에 잘 어울린다고 생각이되어서 이다.

 

1️⃣ 주요 컴포넌트 만들기

1-1. 검색 기능 구현 중 params를 선택하는 함수 만들기

button에 state로 값을 부여하는 것보다 클릭되었을 때 전역state의 값을 최신화하는 방식으로 구현을 하고 있다.(예시를 보면 뭔소린지 이해 감) 근데 이 때 button의 onClickHandler를 만드는 과정에서 만날 수 있는 issue와 해결법이 있어서 간단하게 끄적인다.

구현 코드

// store/search.ts
...

interface SearchState {
  searchParams: SearchDataType;
  setSearchParams: (
    category: string,
  ) => (event: MouseEvent<HTMLButtonElement>) => void;
}

export const useSearchStore = create<SearchState>((set) => ({
  searchParams: initialSearchData,
  setSearchParams: (category) => {
    return (event: MouseEvent<HTMLButtonElement>) => {
      const value = event.currentTarget.getAttribute("data-value");
      set((state) => ({
        ...state,
        searchParams: { ...state.searchParams, [category]: value },
      }));
    };
  },
}));

/**
* 현재 보는 것은 zustand를 이용해서 만든 store인데 여기서 setSearchParmas에 주목을 하면 된다.
* 보면 () => {return () => {}} 이런 괴랄한 형태를 띄고 있는데 이는 props로 전달할 때
* event 객체와 category객체 2개를 제공 받아야하는 상황에서 어떻게 전달 받으면 될지 고민하다가 나온 해결책이다.
*/

// 문제가 된 상황
<RoundedButton
  key={value}
  text={text}
  value={value}
  isFocused={searchParams.filter === value}
  // 여기 부분임
  onClickHandler={setSearchParams("filter")}
/>

/**
* 이 부분을 보게 되면 실행하고 나서 return문을 전달하게 하는 것을 구상하고 이렇게 작성을 했었는데
* 다른 문제를 해결하다가 GPT가 이렇게 하면 button을 클릭했을 때 함수가 실행되어야하는데 그렇지 않다는 것이다.
* 그렇기에 아래와 같이 수정을 하게 되었다.
*/

// 최종 로직
<RoundedButton
  key={value}
  text={text}
  value={value}
  isFocused={searchParams.filter === value}
  // 여기 부분임
  onClickHandler={setSearchParams("filter")}
  onClickHandler={(event) => setSearchParams("filter")(event)}
/>
  • 함수를 전달하는 과정에서 함수 자체를 전달하는 것과 실행문을 전달하는 것의 차이를 이해해야하고 또한 실행문을 전달하고자 할 때에 이벤트의 발생여부와 관련짓고 싶다면 보기와 같은 방법을 사용하자!~

1-2. hard route와 soft route의 차이..

클라이언트 컴포넌트를 줄이면서 next.js가 가진 기능과 html tag들의 속성들을 최대한 활용해서 프로젝트를 만들어보고 있는데 aciton과 redirect를 통해서 검색어를 query string으로 넣어보려고 했다. 그러나 결과는 만들고 나니 검색할 때마다 새로고침이 되는데 흠...이다

 

현재 코드

import SearchIcon from "@/ui/icon/search";
import { redirect } from "next/navigation";

interface SearchInputProps {
  category: string;
}

const searchGallery = (category: string) => {
  return async (formData: FormData) => {
    const searchQuery = formData.get("search");
    if (typeof searchQuery === "string" && searchQuery.trim() !== "") {
      redirect(
        `?search=${encodeURIComponent(searchQuery)}&category=${category}`,
      );
    }
  };
};

const SearchInput = ({ category }: SearchInputProps) => {
  return (
    <form
      className="relative"
      action={(formData) => searchGallery(category)(formData)}
    >
      <input
        type="text"
        className="box-border w-full rounded-lg border border-cgray-600 px-3 py-2 text-sm"
        placeholder="Search..."
        name="search"
      />
      <button
        className="absolute right-2 top-1/2 -translate-y-1/2"
        type="submit"
      >
        <SearchIcon />
      </button>
    </form>
  );
};

export default SearchInput;
  • 이런식으로 server action을 통해서 구현을 할 수는 있었는데 render 과정에서 searchBox 자체가 깜빡임을 보이는 것을 확인해서 이 방법은 안 쓰는 걸로 했다. 

1-2. CSS . 중 width와 heigth이 갑작스럽게 적용이 안된다...

tailwindcss와 next.js를 이용한 프로젝트에서 가끔씩 생기는 경우인데 개발자모드에서 해당 node를 식별해보면 class에 해당 css가 적용되있음에도 불구하고 실제 보이는 UI에는 적용이 안되어있다. 심지어 border 속성도 안되는 것을 보니 어떠한 문제가 있는 것이 분명하다.

해결방법 및 과정

  • 해결하고 나니 너무 허무했고, 스타일이 적용되는 곳뿐만아니라 정의하는 곳에서도 tailwind.confg.ts에 해당 path를 content에 추가해야겠다

1-3. Public에는 어떤 path로 접근을 하면되는건가??

이상하게 정상적인 경로로 접근을 한 것 같은데 이미지가 깨져서 보이기 때문에 이 문제를 해결해야겠다.
// 기존 middleware의 matcher
export const config = {
  matcher: [
    "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
  ],
};

// 개선 후 로직
export const config = {
  matcher: [
    "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|images/.*).*)",
  ],
};

// Image Component 경로
<Image
    src="/images/test.jpg"
    alt="gallery-thumbnail"
    width={240}
    height={240}
/>
  • 이미지를 불러올 때 http://localhost:3000/images/test.jpg 이러한 요청 URL을 통해서 이미지를 가져오게되는데 middleware에서 이러한 요청이 있을 때 /home 으로 강제로 보내는 코드가 있었다(이상한 URL 입력 시 home으로 이동) 그렇기에 matcher에 images/.* 를 추가하여 이미지도 정상적으로 가져올 수 있게 되었다.