티스토리 뷰

이번에 계속 검색페이지 때문에 며칠을 이러고 있는지 모르겠다...

 

이제 막 배우기 시작한 나로써 그리고 제대로 공부한 것이 아니라 급하게 프로젝트를 하면서 배워야하기 때문에

 

더 어려운 것 같다. 솔직히 배운다기 보다 해결하기 위해 챗 GPT 사용하면서 그냥 익숙해지는 정도인 것 같은데

 

그정도만 해도 우선은 다행인 것 같다. 나중에 웹 개발 관련 수업을 하나 더 들어야겠다.

 

그 때를 생각하면 지금 익숙하게 하고 어느정도 눈에 익히는 것도 좋은 것 같다. 

 

내가 너무 부족한 것이 느껴진다... 삼촌한테 많이 물어보며 어느정도 해결을 하긴 했는데...

 

삼촌 말대로 "일단 하고는 있는데 자기 자신이 상요하는 도구에 대해 이해가 많이 없다. 그럼 제일 쉽게 할 수 있는게 추측하는 것이 아니라 로그 찍어서 일일이 확인 하면서 해야한다" 였다....

 

진짜 너무너무너무 부족하다. 내일 삼촌이 수정해준 코드 보면서 어떤 식으로 되어있는지 이해하고 그걸 바탕으로...!!

 

1. 강의 탭 눌렀을 때 /search 로 리로드

2. 빈 값은 검색 결과가 없습니다. 나오게 하기

 

이렇게 2개를 해야하는데 2번은 조건문을...참...어떻게 써야할지.... 우선 삼촌이 수정해준 코드는...!! 그리고 내가 검색창 추가로 만들어서 그거에 대한 코드도 올리자

 

백엔드 추가한것도..애매하네 내일 실험을 한번 해보자!!!

 

 

<SearchPage.tsx>

import React, { ReactNode, useEffect, useState, useContext } from 'react';
import { useNavigate, useLocation, useSearchParams } from 'react-router-dom';
import Logger from 'console-log-level';  
let log = Logger({level: 'trace'});
import {
    Box,
    Container,
    Flex,
    SimpleGrid,
    Input,
    Button,
    IconButton,
    useColorModeValue,
    background,
} from '@chakra-ui/react';
import { SearchIcon } from '@chakra-ui/icons';
import { Text } from '@chakra-ui/react';
import axios from 'axios';
import ClassCard from '../../components/ClassCard';
import SectionTitle from '../../components/SectionTitle';
import ContentArea from '../../components/ContentArea';
import { API } from '../../../config';
import SearchBar from '../../components/SearchBar/SearchBar';


// 검색 정보에 대한 타입 정의
type CourseSearchInfo = {
    title: string;
    description: string;
    courseId: number;
    thumbnailPath: string;
    tags: string;
    userName: string;
};

const ClassCardList = ({ children }: { children: ReactNode }) => (
    <SimpleGrid columns={{ base: 1, sm: 2, md: 3 }} spacing={5} gridAutoRows={'1fr'} borderColor={'none'}>
        {children}
    </SimpleGrid>
);

function SearchPage() {
    const navigate = useNavigate();
    const [searchResults, setSearchResults] = useState<CourseSearchInfo[]>([]);
    // 검색 결과를 저장할 상태
    const [keyword, setKeyword] = useState<string>('');
    const [allResults, setAllResults] = useState<CourseSearchInfo[]>([]);
    const [searchParams] = useSearchParams();
    // useLocation을 활용하여 쿼리 스트링 값을 가져온다.
    const location = useLocation();

    useEffect(() => {
        // search 속성에 접근하면 쿼리 스트링 값을 얻을 수 있다.
        const keyWord = decodeURI(location.search);
        console.log('keyWord===>', keyWord)

        if (!!keyWord) {
            // 검색어가 존재하는 경우에 API 경로에 쿼리 스트링으로 전달하여 fetch한다.
            const fetchLectures = async () => {
                try {
                    const getValue = API.COURSE_LIST_BY_SEARCH + keyWord
                    console.log('getValue===>',getValue)
                    const response = await axios.get(`${getValue}`);
                    //const data = await response.json();

                    setSearchResults(response.data.data);
                    console.log(searchResults);
                } catch (error) {
                    console.error('검색 중 오류가 발생했습니다:', error);
                }
            };

            fetchLectures();
        } else {
            // 검색어가 없는 경우 전체 게시물을 fetch한다.
            const fetchLectures = async () => {
                try {
                    const response = await axios(`${API.COURSE_LIST_BY_ALL}`); // 여기 좀 헷갈리네...
                    //const data = await response.json();

                    setSearchResults(response.data.data);
                } catch (error) {
                    console.error('검색 중 오류가 발생했습니다:', error);
                }
            };

            fetchLectures();
        }
    }, [location]);

    return (
        <>
            <ContentArea>
                <Flex flexDirection={'column'} className={'content-wrapper'} p={4} width={'100%'} gap={4}>
                    <SearchBar placeholder="검색어를 입력하세요" purpose="search" />
                    {/* 검색 결과를 보여줄지 여부를 조건부 렌더링으로 설정 */}
                    {searchResults.length > 0 ? (
                        <Box>
                            <SectionTitle title={'검색 결과'} />
                            <ClassCardList>
                                {searchResults.map((item, idx) => (
                                    <ClassCard
                                        key={idx}
                                        title={item.title}
                                        desc={item.description}
                                        onClick={() => navigate(`/class/${item.courseId}`)}
                                        imgSrc={item.thumbnailPath}
                                    />
                                ))}
                            </ClassCardList>
                            <Box mt={250} />
                        </Box>
                    ) : (
                        <Box>
                            {/* 전체 강의 목록을 보여줄 카드 리스트 */}
                            <SectionTitle title={'전체 강의'} />
                            <ClassCardList>
                                {allResults.map((item, idx) => (
                                    <ClassCard
                                        key={idx}
                                        title={item.title}
                                        desc={item.description}
                                        onClick={() => navigate(`/class/${item.courseId}`)}
                                        imgSrc={item.thumbnailPath}
                                    />
                                ))}
                            </ClassCardList>
                            <Box mt={250} />
                        </Box>
                    )}
                </Flex>
            </ContentArea>
        </>
    );
}

export default SearchPage;

 

 

 

<SearchBar.tsx>

import React, { useState } from "react";
import { SearchIcon } from "@chakra-ui/icons"
import { useNavigate, useSearchParams } from "react-router-dom";
import { Box, Container, Flex, SimpleGrid, Input, Button, IconButton, useColorModeValue, background } from "@chakra-ui/react";

interface SearchBarPropsType {
  placeholder: string;
  purpose: string;
}

function SearchBar(props: SearchBarPropsType) {
  const [searchKeyWord, setSearchKeyWord] = useState("");

  // useSearchParams는 URL에 쿼리 스트링을 입력해준다.
  const [searchParams, setSearchParams] = useSearchParams();
  
  const navigate = useNavigate();

  const onChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearchKeyWord(e.target.value);
  };

  const searchSubmitHandler = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    // 검색 키워드가 존재하는 경우에만 setState를 진행한다.
    if (!!searchKeyWord) {
      setSearchParams({
        keyword: searchKeyWord,
      });
    } else {
      // 검색 키워드가 존재하지 않는 경우, 쿼리 스트링이 없는 원래 URL을 보여주도록 navigate 처리한다.
      navigate(`${props.purpose === "search" ? "/search" : "/qa"}`);  // 이 부분 수정해야한다!!!!!!
    }
  };

  return (
    <form onSubmit={searchSubmitHandler}>
      <Box mx="auto" width="50%">
        <Flex align="center" justify="center">
          <input
            type="text"
            placeholder={props.placeholder}
            value={searchKeyWord}
            onChange={onChangeHandler}
            style={{ padding: "8px", fontSize: "16px", borderRadius: "999px", width: "100%", paddingLeft: "20px" }}
          />
          <Button 
            borderRadius="full"
            bgColor={useColorModeValue("gray.200", "gray.700")}
            _hover={{ bgColor: useColorModeValue("gray.300", "gray.600")}}
            ml={2}
          >
            <SearchIcon />
          </Button>
        </Flex>
      </Box>
    </form>
    
  );
}

export default SearchBar;
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함