(3D)Dev Deep Dive/Frontend origin

[문득 떠오른 건데] 밥먹고 졸릴때 동적 컴포넌트를 만들면 잠이 깰까? - 1편

  • -
728x90

2023 Series

 

[ 2023 ] CheckPoint, 2023년

2023년동안 작성했던 회고록들을 모아 둔 게시글이다 2023년 동안 작성한 회고들을 계속해서 업데이트 해 나갈 예정이다 [ January ] - 변화의 시작, 1월 더보기 [2022WinterBootcamp] 0~1주차 회고 개발자가

time-map-installer.tistory.com


학교에 일찍 와서 아침을 먹으며 여러 생각을 하던 날이었다.

아침으로 맛있는 샐러드와 주먹밥을 먹으며 갑자기 떠오른 의문,

"동적 컴포넌트를 만들면 잠이 깰까?"

사람들 중에 밥을 먹고 식곤증이 와서 많이 졸려하는 사람들이 있다

나도 그 사람들 중 한 사람이지만 동적인 활동을 하면 잠이 깨는 편이었다

"어? 그러고 보니 리액트에도 동적인 사이트와 정적인 사이트가 있는데?"

분명 헛소리라는 것을 알고 있음에도 그냥 강렬하게 내 머릿속을 강타하는 하나의 아이디어였다

일단 다른 걸 다 제쳐두고 재밌겠다는 생각이 드는 순간, 나는 "당장 하자"라는 결론이 지어진 것을 깨달았고,

이 글을 작성하기 시작했다

맛있는 샐러드

우선, 들어가기 전에 여기에 나온 용어, 정적 컴포넌트와 동적 컴포넌트는 무엇일까?


정적 컴포넌트, 동적 컴포넌트는 무엇일까?

정적 컴포넌트(static component)와 동적 컴포넌트(dynamic component)는 웹 페이지나 애플리케이션에서 사용되는 두 가지 주요 컴포넌트 유형이며, 이들은 웹 페이지나 앱의 구조와 기능에 영향을 준다

  1. 정적 컴포넌트 (Static Component)
    • 정적 컴포넌트는 사용자 상호작용에 따라 변하지 않고 일정한 상태를 유지하는 웹 페이지나 애플리케이션의 부분이다
    • 이러한 컴포넌트는 HTML, CSS 등을 사용하여 만들어지며, 일반적으로 고정된 콘텐츠와 디자인 요소를 포함한다
    • 정적 컴포넌트는 서버에서 클라이언트로 보내질 때 이미 렌더링 되어 있으며 사용자의 입력이나 액션에 따라 변경되지 않는다
    • 예시: 로고, 푸터, 배너 등
  2. 동적 컴포넌트 (Dynamic Component)
    • 동적 컴포넌트는 사용자의 상호작용이나 애플리케이션의 상태에 따라 변경되는 웹 페이지나 애플리케이션의 부분이다
    • 이러한 컴포넌트는 JavaScript, AJAX, React, Vue 등을 사용하여 만들어지며, 데이터의 변경, 사용자 입력, 시간 경과 등에 따라 내용이 변경되거나 업데이트된다
    • 동적 컴포넌트는 클라이언트 측에서 렌더링 되거나 서버 측에서 렌더링 되어 클라이언트로 보내질 수 있다
    • 예시: 검색 결과, 댓글, 실시간 뉴스 피드 등

요약

정적 컴포넌트는 고정된 콘텐츠를, 동적 컴포넌트는 상호작용 및 애플리케이션 상태에 따라 변경되는 콘텐츠를 나타낸다

이 두 가지 유형의 컴포넌트를 결합하여 사용자에게 최적의 웹 경험을 제공할 수 있다

 

정적 컴포넌트 예시(쿠팡)

아래는 정적 컴포넌트 예시 일부를 가져와 보았다

변화하지 않고 가만히 있는 컴포넌트들로 텍스트의 대부분은 정적 컴포넌트라고 볼 수 있다

동적 컴포넌트 예시(쿠팡)

아래는 동적 컴포넌트 예시 일부를 가져와 보았다

이 외에도 모달 창, 드롭다운 메뉴, 슬라이더 등이 있다

몇초마다 변경되는 프로모션 이미지

* 광고 아닙니다


그래서 무엇을 실험해 볼 것인가?

1. 밥을 먹고 정적인 컴포넌트(레이아웃에 들어있는 짧은 글, 로고) 만들어보기

2. 밥을 먹고 동적인 컴포넌트(검색 결과) 만들어보기

3. 밥을 먹고 커피를 마셔보기

 

때마침 요즘 하루 루틴이 정확히 일정하게 맞춰져 있다 보니 이러한 실험을 해보기에 최적의 상태가 된 것 같다

(오전 7시 기상 ~ 새벽 2시 전후 취침)


무엇을 사용하여 만들어 볼 것인가?

Library, Framework : React.js + Next.js
Script Language       : Typescript
CSS 관련                        : CSS

나는 이 정적 컴포넌트와 동적 컴포넌트를 둘 다 잘 만들어 낼 만한 기술을 프로젝트에서 이용하고 있다

바로 요즘 대세라고 불리는 Next.js 이다

최근에 나는 mock data를 활용해 Next API를 불러와서 DB에 데이터가 들어있는 것처럼 시험을 해 볼 수 있다는 것을

알았기에, 이를 더 연습해 보는 좋은 기회가 될 것 같아서 이 것을 선택했다

CSS는 간단하게 순수 CSS를 사용해 볼 생각이다

그다음은 Next.js에서 호환되고, JavaScript에서 더 개선된 TypeScript이다

JS, TS의 차이점을 알고 싶다면 아래 글을 참고해 보는 것도 괜찮을 것이다

 

[배경지식] JavaScript? TypeScript? 이 둘의 차이는 무엇일까?

공통 객체 지향 프로그래밍 패러다임 = 데이터 추상화에 중심 - 객체와 클래스라는 두 주요 개념을 기반으로 한다 객체 기반의 스크립트 언어이다 웹의 동작을 구현하는 데에 쓰이는 언어이다

time-map-installer.tistory.com


프로젝트 설치하고 시작하기

1. React + Next.js + TypeScript 프로젝트 생성

npx create-next-app@latest --typescript

project name을 잘못 지정해서 이후에 바꾸었다

프로젝트명은 SleepyComponents로 지었다

아무래도 이 글을 쓰고 있는 순간에도 졸린 것을 보아하니 상당히 잘 지은 이름인 것 같다

ESLint를 사용하고, TailwindCSS를 사용하지 않으며(Sass사용),

Next.js 13 버전부터 추가된 /app directory가 있지만 사용될 것 같지는 않아서 만들지 않았다

 

/app과 /pages는 어떤 차이점이 있을까?

/pages 디렉터리와 /app 디렉터리가 구분되지 않았지만, Next.js 13 버전에서는 이 두 디렉토리를 명확하게 구분하고 있다

이 구조 변경은 렌더링 방식에도 영향을 주며, 개발자들에게 더 나은 구조화와 렌더링 최적화를 제공한다

  1. /pages 디렉토리
    • /pages 디렉터리는 여전히 페이지를 정의하는 데 사용되며, 이 디렉토리 내에 있는 파일들은 자동으로 라우트로 변환된다
    • 이 디렉터리에서 정의한 컴포넌트는 주로 서버에서 렌더링 되거나 정적 사이트 생성(SSG)을 사용하여 빌드 시점에 렌더링 된다
    • 이렇게 함으로써 초기 페이지 로딩 성능을 향상하고 검색 엔진 최적화(SEO)를 개선할 수 있다
  2. /app 디렉터리
    • Next.js 13 버전에서 새롭게 도입된 /app 디렉터리는 애플리케이션의 전역 상태, 레이아웃, 스타일 등을 관리하는 곳이다
    • 이 디렉터리에 있는 컴포넌트는 클라이언트 측에서 렌더링 되며, 주로 동적인 컴포넌트 및 상호작용이 많은 부분에 사용된다
    • 이로 인해 사용자 경험(UX)을 개선하고 애플리케이션 로직을 효율적으로 관리할 수 있다

요약하면, Next.js 13 버전에서 **/pages**와 /app 디렉터리를 구분하는 것은 렌더링 방식의 차이를 명확하게 하기 위한 것이다

/pages 디렉터리는 서버 렌더링 및 정적 사이트 생성을 위한 페이지 컴포넌트를 관리하며, /app 디렉토리는 클라이언트 측 렌더링에 사용되는 전역 상태, 레이아웃, 스타일 등을 관리한다

이렇게 구분함으로써 개발자들은 구조화된 방식으로 웹 애플리케이션을 개발하고 렌더링 성능을 높일 수 있다고 한다

 

하지만 여기서는 컴포넌트들을 간단하게 만들어보려고 하기에 /pages 디렉터리만 사용해 볼 것이다

 

VS Code를 이용하여 추가적으로 초기 세팅 - Git 연결

git GUI는 VS Code에 내장되어 있는 git 기능을 이용해 보고자 한다

 

1. 화면 좌측 브랜치 모양을 누르고 GitHub에 게시를 눌러준다(이모티콘 위치 참고)

2. public이냐 private이냐를 고르는 선택창이 나오는데 어차피 링크할 것이므로 public을 골랐다. 유동적으로 선택하면 된다

3. 게시 완료, 이제 프로젝트로 돌아가자


이제 프로젝트를 실행시켜 보자

yarn dev

프로젝트 관리자는 yarn을 사용하였고, development 모드로 진입한다는 의미의 yarn dev를 입력한다

그러면 아래와 같은 멋진 화면이 뜰 텐데, 우리는 이 부분을 지우고 시작할 것이다

멋지긴 하지만 그래도 보내야 합니다..


기존 코드들 다 지우고 초기로 돌리기

index.tsx 페이지로 가서 모든 내용을 지우고 오른쪽과 같이 세팅하면

아래와 같이 까만 화면에 흰 글씨로 Hello World만 남게 되는데

이는 globals.css와 Home.module.css에도 아직 스타일이 적용되어 있기 때문에 이러한 현상이 발생하는 것이므로

그 두 개의 내용 모두 지워준다

왜 Home.module.css인가?

일단 Next.js는 스타일 적용 방식이 조금 특이한 것 같았다

웬만해서. css 파일들은 모두 전역으로 지정되는 것 같았고,. module.css 파일로 이름을 지어야만 특정 컴포넌트/페이지에서 잘 돌아가는 그러한 파일이 된다.

 

스타일링 접근 방식이 다른 Next.js

Next.js는 스타일링을 위한 몇 가지 다른 접근 방식을 제공한다

전역 스타일(. css)과 컴포넌트 스코프 스타일(. module.css)이 그중 일부이다

  • 전역 스타일 (Global Styles): 전역 스타일은 애플리케이션 전체에 적용되는 CSS 스타일이다. 일반적으로. css 확장자를 가진 파일에 작성되며, 레이아웃, 타이포그래피, 색상 등 전체 애플리케이션에 영향을 주는 스타일을 포함한다
    • 전역 스타일을 적용하려면, /pages/_app.js 파일에서 해당 CSS 파일을 임포트 한다
    • 예를 들어, styles/global.css 파일을 생성하고 전역 스타일을 작성한 후, /pages/_app.js 파일에 임포트 할 수 있다
/* styles/global.css */
body {
  font-family: 'Arial', sans-serif;
  background-color: #f0f0f0;
}
// pages/_app.js
import React from 'react';
import '../styles/global.css';

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default MyApp;
  • 컴포넌트 스코프 스타일 (Component-scoped Styles): 컴포넌트 스코프 스타일은 특정 컴포넌트에만 적용되는 CSS 스타일이다. 이를 위해 CSS 모듈(CSS Modules)을 사용하며, 파일 이름에 .module.css 확장자를 사용합니다. 이렇게 하면 스타일이 해당 컴포넌트에만 적용되고, 다른 컴포넌트에 영향을 주지 않는다.
    • 예를 들어, components/Button.module.css 파일을 생성하고 컴포넌트 스코프 스타일을 작성한 후, 해당 컴포넌트에서 임포트 하고 사용할 수 있다.
/* components/Button.module.css */
.button {
  background-color: blue;
  color: white;
  padding: 10px 20px;
  border-radius: 5px;
}
// components/Button.js
import React from 'react';
import styles from './Button.module.css';

const Button = ({ children, onClick }) => {
  return (
    <button className={styles.button} onClick={onClick}>
      {children}
    </button>
  );
};

export default Button;

이렇게 전역 스타일과 컴포넌트 스코프 스타일을 함께 사용하여 전체 애플리케이션의 디자인을 관리할 수 있다


이제, 준비는 끝났으니 하나씩 만들어보자


1. 밥을 먹고 정적인 컴포넌트(레이아웃에 들어있는 짧은 글, 로고) 만들어보기

아침으로 제일 위에 올려둔 샐러드와 주먹밥을 먹고 첫 번째 실험을 시작해보려 한다

 

우선, 나름 그럴싸한 로고를 먼저 만들어 보겠다

Figma를 통해 디자인을 진행하고 이미지를 가져와서 이 안에 넣고 사용할 예정이다

 

Figma를 이용하여 간단하게 로고 만들기

여기서도 쿠팡의 색상을 참고하여 "LO고"라는 나만의 로고를 만들어보았다

마치 레고와 같은 느낌이 나지만 기분탓이다

이 사진을 레이아웃 안에 띄워보기

대략적으로 사각형 안에 로고 위치에 있을 법한 그런 위치에 띄워보려고 한다

/pages 안에 images와 components를 만들고 여기에 만든 로고를 넣고 헤더라는 컴포넌트 파일을 생성한다

그리고 위에서 다루었던 next.js 의 styling 접근 방식을 통해 Header.modul.css라는 파일을 만들어 둔다

실제 헤더의 모양을 만들기 위해 코드를 아래와 같이 해두었다

 

index.tsx

더보기
// /src/pages/index.tsx

import Header from "./components/Header";

export default function Home() {
  return (
    <>
      <Header />
    </>
  );
}

Header.tsx

더보기
// src/pages/components/Header.tsx

import Image from "next/image";
import logo from "../../images/logo.png";
import styles from "../../styles/Header.module.css";
import Link from "next/link";

export default function Header() {
  return (
    <header className={styles.header}>
      <nav>
        <div className={styles.logo}>
          <Image src={logo} alt="logo" width={200} height={100} />
        </div>
        <ul className={styles.nav}>
          <li>
            <a href="#">Menu 1</a>
          </li>
          <li>
            <a href="#">Menu 2</a>
          </li>
          <li>
            <a href="#">Menu 3</a>
          </li>
        </ul>
      </nav>
    </header>
  );
}

 

Header.module.css

더보기
/* /src/styles/Header.module.css */

.header {
  height: 200px;
  background-color: #fff;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 50px;
  margin-top: 50px;
}

.logo {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.logo img {
  height: 100px;
  margin-bottom: 50px;
}

.nav {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav li:not(:last-child) {
  margin-right: 20px;
}

.nav a {
  color: #333;
  text-decoration: none;
  font-size: 16px;
  font-weight: 500;
  padding: 10px;
  border-radius: 4px;
  transition: background-color 0.3s ease;
}

.nav a:hover {
  background-color: #eee;
}

@media only screen and (max-width: 768px) {
  .header {
    padding: 0 20px;
  }

  .logo {
    margin-right: 20px;
  }

  .logo img {
    height: 40px;
  }

  .nav {
    display: none;
    position: absolute;
    top: 80px;
    left: 0;
    width: 100%;
    background-color: #fff;
    z-index: 1;
    flex-direction: column;
    align-items: center;
  }

  .nav li {
    margin: 10px 0;
  }

  .nav a:hover {
    background-color: #eee;
  }

  .nav.show {
    display: flex;
  }
}

@media only screen and (min-width: 768px) {
  .nav {
    display: flex;
  }
}

 

그렇게 완성된 결과는 아래와 같다

어때요, 정말 정적이지요?

 

다음은 레이아웃 안에 들어있는 그럴싸한 짧은 글귀를 넣어 볼 생각이다

파일 생성하기


코드 작성하기

index.tsx(업데이트)

더보기
// /src/pages/index.tsx

import Header from "./components/Header";
import ShortWriting from "./components/ShortWriting";

export default function Home() {
  return (
    <>
      <Header />
      <ShortWriting />
    </>
  );
}

ShortWriting.tsx

더보기
// /src/pages/components/ShortWriting.tsx

import styles from "../../styles/ShortWriting.module.css";

export default function ShortWriting() {
  return (
    <div className={styles.container}>
      <div className={styles.shortWriting}>
        <p className={styles.paragraph}>
          {... 내용 작성 ...}
        </p>
      </div>
    </div>
  );
}

ShortWriting.module.css

더보기
/* /src/styles/ShortWriting.module.css */

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
  background-color: #f9f9f9;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  border-radius: 10px;
}

.shortWriting {
  width: 100%;
  text-align: justify;
}

.paragraph {
  font-size: 1rem;
  line-height: 1.5;
  color: #333;
}

 

그렇게 정적인 컴포넌트들을 모아본 결과는 다음과 같다

짧은 글이다. 사실 이 포스팅의 초반 부분을 글로 만든 것이다

정적인 컴포넌트들을 만들어 본 결과 확실이 졸린 것 같기도 했다

만드는 과정에서는 졸리다고 느껴지지 않았지만 다 만들고 결과창에 있는 글을 읽었을 때 졸렸던 것 같다

확실이 움직이지 않다보니 현실에서 책을 읽는 그런 느낌이라 그런건가 싶었다


2. 밥을 먹고 동적인 컴포넌트(검색 결과) 만들어보기

맛있는 초밥, 맛있는 고기
맛있는 샐러드, 맛있는 과일

정적 컴포넌트를 만들고 동적 컴포넌트를 만들기 전에도 밥을 먹었다. 이번엔 더 맛있고 더 배부르게 먹은 것 같다

이번엔 동적인 컴포넌트들이다. 사용자의 행동에 따라 반응하거나 움직이는 컴포넌트들을 만들어야 하기에

검색을 하면 검색 결과를 보여주는 컴포넌트를 만들어보도록 하겠다

 

과정은 아래와 같다

 

1. 검색 컴포넌트 만들기

우선, 검색 컴포넌트를 만들고 컴포넌트를 메인 페이지의 로고에서 넘어가도록 만들도록 하겠다

 

Header에 코드 추가

더보기
// src/pages/components/Header.tsx

<Link href={`/components/Search`}>검색 페이지로 이동</Link>

Link를 통해 연결한다면 아래와 같은 모습이 나온다

좌측에 추가된 링크, 우측에 이동한 페이지

 

검색 페이지 파일 작성

검색 페이지로 넘어가면 검색을 하고 결과를 내보내는 화면이 필요한데 여기에서는 관련 기능과 페이지의 구성 요소들을 만들어 준다

더보기
// /src/pages/components/Search.tsx

// useState를 사용하기 위해 React에서 필요한 모듈을 불러옵니다.
import { useState } from "react";

// SearchResult 인터페이스를 정의합니다. 검색 결과로 반환되는 데이터의 형식을 지정합니다.
interface SearchResult {
  id: number;
  title: string;
  description: string;
}

// Search 컴포넌트를 정의합니다.
export default function Search() {
  // 검색어를 저장할 상태 변수(query)와 그 값을 설정하는 함수(setQuery)를 선언합니다.
  const [query, setQuery] = useState("");

  // 검색 결과를 저장할 상태 변수(results)와 그 값을 설정하는 함수(setResults)를 선언합니다.
  // 결과는 SearchResult 형식의 배열입니다.
  const [results, setResults] = useState<SearchResult[]>([]);

  // 검색 기능을 구현하는 비동기 함수입니다.
  const handleSearch = async () => {
    // 검색어가 비어있으면 함수를 종료합니다.
    if (!query.trim()) return;

    // 서버에 검색 요청을 보냅니다. 검색어를 encodeURIComponent로 인코딩하여 URL에 포함시킵니다.
    const response = await fetch(
      `/api/search?query=${encodeURIComponent(query)}`
    );
    // 서버로부터 받은 응답을 JSON 형식으로 변환합니다.
    const data = await response.json();

    // 응답이 성공적인 경우, 결과를 저장하고 콘솔에 메시지를 출력합니다.
    if (response.ok) {
      console.log("정상적으로 검색 완료");
      setResults(data);
    } else {
      // 응답에 문제가 있을 경우, 에러 메시지를 콘솔에 출력합니다.
      console.error(data.error);
    }
  };

  // Search 컴포넌트의 JSX를 반환합니다.
  return (
    <div>
      {/* 검색어를 입력하는 텍스트 필드 */}
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="검색어를 입력하세요"
      />
      {/* 검색 버튼 */}
      <button onClick={handleSearch}>검색</button>
      {/* 검색 결과를 표시하는 부분 */}
      <ul>
        {/* 검색 결과를 순회하며 각 결과를 리스트 항목으로 표시합니다. */}
        {results.map((result) => (
          <li key={result.id}>
            <h3>{result.title}</h3>
            <p>{result.description}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

/src/pages/components/Search.tsx: 이 코드는 검색 컴포넌트를 정의하며,

사용자가 입력한 검색어를 받아 API 호출을 통해 검색 결과를 가져오고, 화면에 결과를 표시한다.

**useState**를 사용하여 query(검색어)와 results(검색 결과)를 관리한다.

handleSearch 함수에서는 다음 과정을 거친다:

  1. 검색어가 비어있는지 확인하고, 비어있다면 함수를 종료한다.
  2. fetch 함수를 사용하여 /api/search API를 호출하며, 검색어를 인코딩하여 쿼리 파라미터로 전달한다.
  3. 호출한 API에서 반환된 JSON 데이터를 처리하고, 응답이 성공적이면 검색 결과를 업데이트한다. 그렇지 않은 경우, 에러 메시지를 콘솔에 출력한다.

검색 입력창에 사용자가 검색어를 입력하면 **setQuery**를 사용하여 검색어를 업데이트한다.

검색 버튼을 클릭하면 handleSearch 함수가 실행된다.

검색 결과는 화면에 목록 형태로 표시된다.


2. mock data 만들기

그렇게 초기 세팅을 완성해 두고 우리는 검색을 할 데이터가 있어야 한다.

그래서 data 폴더를 만들고 임시로 데이터를 넣어두고 검색이 가능하도록 할 생각이다

 

아침, 점심, 저녁 별로  먹었을 법한 데이터들을 넣어서 만들어 보았다

더보기
// /src/pages/data/searchData.ts

export const searchData = [
  { id: 1, title: "아침", description: "토스트와 커피" },
  { id: 2, title: "아침", description: "시리얼과 우유" },
  { id: 3, title: "아침", description: "계란 후라이와 베이컨" },
  { id: 4, title: "점심", description: "치킨 샐러드" },
  { id: 5, title: "점심", description: "햄버거와 감자튀김" },
  { id: 6, title: "점심", description: "스시와 라멘" },
  { id: 7, title: "저녁", description: "스테이크와 샐러드" },
  { id: 8, title: "저녁", description: "피자와 파스타" },
  { id: 9, title: "저녁", description: "쌀국수와 봉골레 파스타" },
  // 추가 데이터
];

3. API Route 만들기

Next.js 프레임워크에서는 "/pages/api" 폴더 내에 API 관련 코드를 작성하는 것이 권장되는 규칙 중 하나이다.

Next.js는 서버 렌더링이 가능한 리액트 기반의 프레임워크이기에 여기서는 "/pages/api" 폴더를 사용하여 API 엔드포인트를 생성하고 관리한다.

이 구조를 따르면, 프로젝트 내에서 API 코드와 프론트엔드 코드를 쉽게 구분할 수 있어 개발 및 유지보수에 도움이 된다.

그렇기 때문에 이 API Route를 해당 디렉터리에 넣어서 생성하도록 하겠다

더보기
// /src/pages/api/search.ts

// Next.js의 API 라우팅을 위해 필요한 타입을 임포트합니다.
import { NextApiRequest, NextApiResponse } from "next";

// 검색할 mock 데이터를 임포트합니다.
import { searchData } from "../data/searchData";

// Next.js의 API 라우터 핸들러 함수를 정의합니다.
// 이 함수는 NextApiRequest와 NextApiResponse 타입의 인자를 받습니다.
const search = (req: NextApiRequest, res: NextApiResponse) => {
  try {
    // 클라이언트에서 전달한 검색 쿼리를 가져옵니다.
    const { query } = req.query;

    // 전달된 검색 쿼리가 문자열이 아닌 경우, 400 상태 코드와 에러 메시지를 반환합니다.
    if (typeof query !== "string") {
      res.status(400).json({ error: "Invalid query parameter." });
      return;
    }

    // searchData에서 쿼리와 일치하는 항목을 찾습니다.
    // 제목(title) 또는 설명(description)이 쿼리와 일치할 때 필터링합니다.
    // 대소문자를 구분하지 않기 위해 모두 소문자로 변환하여 비교합니다.
    const results = searchData.filter(
      (item: { title: string; description: string }) =>
        item.title.toLowerCase().includes(query.toLowerCase()) ||
        item.description.toLowerCase().includes(query.toLowerCase())
    );

    // 찾은 결과를 JSON 형식으로 반환하고, 상태 코드를 200으로 설정합니다.
    res.status(200).json(results);
  } catch (error) {
    // 오류가 발생한 경우, 콘솔에 오류를 출력하고 500 상태 코드와 함께 에러 메시지를 반환합니다.
    console.error("Error in search API route:", error);
    res.status(500).json({ error: "Internal Server Error" });
  }
};

// search 함수를 내보냅니다.
export default search;

 

/src/pages/api/search.ts: 이 코드는 검색 API 라우터 핸들러 함수를 정의한다.

클라이언트에서 전달된 검색 쿼리를 처리하고, 해당 쿼리와 일치하는 항목을 반환한다.

  • **req.query**에서 검색 쿼리를 가져온다.
  • 검색 쿼리가 문자열이 아닌 경우, 400 상태 코드와 에러 메시지를 반환한다.
  • **searchData**에서 쿼리와 일치하는 항목을 찾는다. 이 때, 제목과 설명 모두에서 검색을 수행하며, 대소문자를 구분하지 않는다.
  • 일치하는 결과를 JSON 형식으로 반환하고, 상태 코드를 200으로 설정한다.
  • 에러가 발생한 경우, 콘솔에 오류를 출력하고, 500 상태 코드와 함께 에러 메시지를 반환한다.

결론적으로, 사용자가 검색어를 입력하고 검색 버튼을 클릭하면, 검색 컴포넌트는 검색 API를 호출하여 결과를 가져온다.검색 컴포넌트는 이 결과를 화면에 표시하여 사용자에게 제공한다.

검색 API는 전달된 검색 쿼리를 처리하고, 쿼리와 일치하는 항목을 반환한다.


4. 검색 결과 확인하기

이제 각종 처리들을 모두 한 상태이기에 정상적으로 검색이 될 것이다

여기에는 CSS 처리를 하지 않았고 가장 기본적인 HTML 모양으로 나왔지만 그래도 기능이 작동하는 모습을 볼 수 있다

대소문자 구분을 없애고, title, description중 어느 하나라도 공통분모가 있다면

검색되도록 만들어두었기에 폭넓게 검색되는 모습을 볼 수 있다

와!

정적인 컴포넌트를 만들 때와는 다른 부분으로는 한 가지가 있었다

실제로 동적으로 움직이는 컴포넌트(검색 결과 나오는 부분)을 보니 뭔가 더 재미있었다

프론트엔드의 특징 중 하나가 결과물을 바로 볼 수 있다는 것인데 거기에 더해서 실제로 움직이는 것을 보니

더 재밌게 느껴지는 것이 아닌가 싶었다

 

지금까지 컴포넌트를 만들어 두었으니, 다음 편에서는 이 프로젝트의 개선점을 찾아 리팩토링해보도록 하겠다

실제 프로젝트에서도 어떤 부분들이 리팩토링 요소가 될 수 있을 지 알 수 있는 좋은 기회가 될 것이다


To be Continue...

이어지는 글은 "3. 밥을 먹고 커피를 마셔보기(리팩토링)" 부터 이어질 예정이다

 

Wanna see the project code?

 

GitHub - TMInstaller/SleepyComponents

Contribute to TMInstaller/SleepyComponents development by creating an account on GitHub.

github.com

728x90
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.