당니의 개발자 스토리

리액트 컴포넌트 만들기 실습 본문

LG CNS/필기노트

리액트 컴포넌트 만들기 실습

clainy 2025. 1. 9. 02:45

물론입니다! 컴포넌트 페이지 설명과 실습 요구조건 충족 설명을 위한 대본을 작성해드리겠습니다. 

### 컴포넌트 페이지 대본

안녕하세요, 오늘 저는 `NameInsight` 컴포넌트를 소개하고, 이 컴포넌트가 어떻게 구성되어 있는지, 어떤 기능들을 구현했는지 설명드리겠습니다.

1. **소개**
   - 먼저, 이 컴포넌트는 사용자가 입력한 이름에 대한 성별 정보와 인기도 정보를 제공하는 페이지입니다. `Genderize.io`와 `Agify.io` API를 사용하여 이러한 정보를 가져옵니다.

2. **기능 설명**
   - 사용자가 이름을 입력하면, 두 개의 버튼 중 하나를 눌러 이름의 성별 정보 또는 인기도 정보를 가져올 수 있습니다.
   - "Get Gender" 버튼을 클릭하면 `Genderize.io` API를 호출하여 이름의 성별 정보를 가져옵니다.
   - "Get Popularity" 버튼을 클릭하면 `Agify.io` API를 호출하여 이름의 인기도 정보를 가져옵니다.
   - 입력 필드를 초기화할 수 있는 "Delete" 버튼도 제공됩니다.
   - API 요청 중에는 로딩 메시지가 표시되며, 요청 실패 시 에러 메시지가 표시됩니다.

3. **코드 설명**
   - **상태 관리**: `useState` 훅을 사용하여 이름, 성별 정보, 인기도 정보, 로딩 상태, 에러 메시지를 관리합니다.
   - **API 호출**: `axios` 라이브러리를 사용하여 API 요청을 처리합니다. 각각의 API 요청 함수는 성공 시 데이터를 상태에 저장하고, 실패 시 에러 메시지를 상태에 저장합니다.
   - **렌더링**: 컴포넌트는 입력 필드와 버튼, 그리고 API 응답 데이터를 렌더링합니다. 로딩 상태와 에러 메시지도 조건부로 렌더링됩니다.
   - **스타일링**: CSS를 사용하여 컴포넌트의 스타일을 정의하고, 호버 시 효과를 추가했습니다. 예를 들어, 입력 필드와 버튼에 호버 시 색상 및 크기 변화를 주어 사용자 인터랙션을 강조했습니다.

### 실습 요구조건 충족 설명 대본

이번 프로젝트에서 요구된 실습 조건을 모두 충족하는지 확인해보겠습니다:

1. **개인별로 REST API를 이용해서 서비스 화면(컴포넌트)을 구성**:
   - `NameInsight` 컴포넌트는 `Genderize.io`와 `Agify.io` API를 사용하여 이름의 성별 정보와 인기도 정보를 가져오는 기능을 구현하였습니다.

2. **개인별로 만든 서비스 화면(컴포넌트)을 리액트 라우터를 이용해서 메뉴로 구성**:
   - `App` 컴포넌트에서 리액트 라우터를 사용하여 `Home` 컴포넌트와 `NameInsight` 컴포넌트를 각각의 경로에 매핑하였습니다. 네비게이션 바를 통해 사용자가 각 페이지로 이동할 수 있도록 구성하였습니다.

3. **사용한 API에 대한 설명 (요청 데이터, 응답 데이터 구조)**:
   - **Genderize.io API**:
     - 요청 데이터: 이름 (name)
     - 응답 데이터 구조: 

{
  "name": "example",
  "gender": "male/female",
  "probability": 0.99,
  "count": 12345
}

 

   - Agify.io API:
     - 요청 데이터: 이름 (name)
     - 응답 데이터 구조:

{
  "name": "example",
  "age": 25,
  "count": 12345
}



이와 같이 각 API의 요청 데이터와 응답 데이터 구조를 설명할 수 있습니다. 이렇게 해서 전체 요구조건을 모두 충족하였음을 확인할 수 있습니다.

`Agify.io` API의 응답 데이터에서 `count` 필드는 이름이 주어진 연령대에서 얼마나 자주 사용되는지를 나타냅니다. `NameInsight` 컴포넌트에서 이 데이터를 표시할 때 "Popularity Count:"로 라벨링하여 사용자의 이해를 돕도록 변경한 것입니다.

해당 부분은 `NameInsight.js` 파일의 아래 코드에서 설정되어 있습니다:

{popularityInfo && (
  <div>
    <p><span className="popularity-label">Popularity Count:</span> {popularityInfo.count}</p>
    <p><span className="age-label">Average Age:</span> {popularityInfo.age}</p>
  </div>
)}



### 설명 대본
**API 응답 데이터 구조 설명 시**:
- `Agify.io` API의 `count` 필드는 이름이 주어진 연령대에서 얼마나 자주 사용되는지를 나타냅니다.
- 이를 사용자에게 더 쉽게 이해시키기 위해 "Popularity Count:"로 라벨링하여 표시했습니다.
- 코드에서는 `popularityInfo.count` 값을 가져와 "Popularity Count:"라는 라벨과 함께 화면에 출력합니다.

 

 

  • count: 이 필드는 특정 이름이 분석된 데이터 집합에서 얼마나 자주 사용되는지를 나타냅니다. 
  • age: 이 필드는 특정 이름의 예상 평균 연령을 나타냅니다. 예를 들어, "age": 25는 해당 이름을 가진 사람들의 평균 연령이 25세임을 의미합니다. 

"호버(Hover)"**는 웹에서 마우스를 특정 요소 위에 올렸을 때 일어나는 상태를 말합니다. 이 상태에서는 해당 요소에 스타일 변화를 줄 수 있습니다.

 

 

button {
  padding: 12px 25px; /* 내부 여백 설정 */
  font-size: 16px; /* 폰트 크기 설정 */
  background: linear-gradient(90deg, #00c6ff, #0072ff); /* 배경을 그라디언트로 설정 */
  color: white; /* 텍스트 색상을 흰색으로 설정 */
  border: none; /* 테두리 제거 */
  border-radius: 4px; /* 둥근 테두리 설정 */
  cursor: pointer; /* 커서를 포인터로 설정 */
  transition: background-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease; /* 배경색, 변형, 그림자에 애니메이션 추가 */
  margin: 5px; /* 마진 설정 */
}

button:hover {
  background: linear-gradient(90deg, #0072ff, #00c6ff); /* 호버 시 배경 그라디언트 반전 */
  transform: scale(1.05); /* 호버 시 살짝 확대 */
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); /* 호버 시 그림자 효과 강화 */
}

이 부분에서 버튼 배경이 선형 그라디언트로 설정되어 있습니다. 기본 상태에서 왼쪽에서 오른쪽으로 파란색(#00c6ff)에서 어두운 파란색(#0072ff)으로 그라디언트가 적용되며, 호버 시 그라디언트가 반전되어 나타납니다.

 

 

 

전체 코드

 

NamingInsight.js

import React, { useState } from 'react'; // 리액트와 useState 훅을 가져옴
import axios from 'axios'; // HTTP 요청을 위한 axios 라이브러리 가져옴
import './NameInsight.css'; // NameInsight 컴포넌트에 대한 CSS 파일 가져옴

const NameInsight = () => { // NameInsight 컴포넌트 정의
  const [name, setName] = useState(''); // 이름 입력 값을 위한 상태 변수와 setter
  const [genderInfo, setGenderInfo] = useState(null); // 성별 정보 상태 변수와 setter
  const [popularityInfo, setPopularityInfo] = useState(null); // 인기도 정보 상태 변수와 setter
  const [loading, setLoading] = useState(false); // 로딩 상태 변수와 setter
  const [error, setError] = useState(''); // 에러 메시지 상태 변수와 setter

  const fetchGender = () => { // 성별 정보를 가져오기 위한 함수
    setLoading(true); // 로딩 상태를 true로 설정
    setError(''); // 에러 메시지 초기화
    const genderEndpoint = `https://api.genderize.io/?name=${name}`; // genderize.io API 엔드포인트 설정

    axios.get(genderEndpoint) // axios를 사용해 API 요청
      .then(res => {
        setGenderInfo(res.data); // 성공적으로 데이터를 받아온 경우 성별 정보 상태 업데이트
        setLoading(false); // 로딩 상태를 false로 설정
      })
      .catch(err => {
        setError('Failed to fetch gender data. Please try again.'); // 에러 발생 시 에러 메시지 설정
        setLoading(false); // 로딩 상태를 false로 설정
      });
  };

  const fetchPopularity = () => { // 인기도 정보를 가져오기 위한 함수
    setLoading(true); // 로딩 상태를 true로 설정
    setError(''); // 에러 메시지 초기화
    const popularityEndpoint = `https://api.agify.io?name=${name}`; // agify.io API 엔드포인트 설정

    axios.get(popularityEndpoint) // axios를 사용해 API 요청
      .then(res => {
        setPopularityInfo(res.data); // 성공적으로 데이터를 받아온 경우 인기도 정보 상태 업데이트
        setLoading(false); // 로딩 상태를 false로 설정
      })
      .catch(err => {
        setError('Failed to fetch popularity data. Please try again.'); // 에러 발생 시 에러 메시지 설정
        setLoading(false); // 로딩 상태를 false로 설정
      });
  };

  const clearInput = () => { // 입력 값을 초기화하기 위한 함수
    setName(''); // 이름 상태를 빈 문자열로 초기화
    setGenderInfo(null); // 성별 정보 상태를 초기화
    setPopularityInfo(null); // 인기도 정보 상태를 초기화
    setError(''); // 에러 메시지 상태를 초기화
  };

  return (
    <div className="name-insight-container"> // 컴포넌트 컨테이너
      <h1>Name Insight</h1> // 제목
      <p>This page uses the Genderize.io and Agify.io APIs</p> // 페이지 설명
      <div>
        <input type="text" value={name} onChange={e => setName(e.target.value)} placeholder="Enter name" /> // 이름 입력 필드
        <button onClick={fetchGender}>Get Gender</button> // 성별 정보 요청 버튼
        <button onClick={fetchPopularity}>Get Popularity</button> // 인기도 정보 요청 버튼
        <button onClick={clearInput}>Delete</button> // 입력 값 초기화 버튼
      </div>
      {loading && <p>Loading...</p>} // 로딩 중일 때 표시할 텍스트
      {error && <p className="error">{error}</p>} // 에러 발생 시 표시할 텍스트
      {genderInfo && ( // 성별 정보가 있을 때 표시할 내용
        <div>
          <p><span className="name-label">Name:</span> {genderInfo.name}</p>
          <p><span className="gender-label">Gender:</span> {genderInfo.gender}</p>
          <p><span className="probability-label">Probability:</span> {genderInfo.probability}</p>
        </div>
      )}
      {popularityInfo && ( // 인기도 정보가 있을 때 표시할 내용
        <div>
          <p><span className="popularity-label">Popularity Count:</span> {popularityInfo.count}</p>
          <p><span className="age-label">Average Age:</span> {popularityInfo.age}</p>
        </div>
      )}
    </div>
  );
};

export default NameInsight; // NameInsight 컴포넌트를 기본으로 내보냄

 

NamingInsight.css

/* Existing styles retained */
.name-insight-container {
  width: 80%; /* 컨테이너 너비를 80%로 설정 */
  max-width: 600px; /* 최대 너비를 600px로 설정 */
  margin: 40px auto; /* 상하단에 40px, 좌우에 auto 마진을 설정하여 중앙 정렬 */
  padding: 30px; /* 내부 여백 설정 */
  background: #ffffff; /* 배경색을 흰색으로 설정 */
  border-radius: 12px; /* 둥근 테두리 설정 */
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1); /* 그림자 효과 */
  text-align: center; /* 텍스트 중앙 정렬 */
  transition: transform 0.3s ease, box-shadow 0.3s ease; /* 변형과 그림자 효과에 애니메이션 추가 */
}

.name-insight-container:hover {
  transform: scale(1.03); /* 호버 시 살짝 확대 */
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); /* 호버 시 그림자 효과 강화 */
}

input[type="text"] {
  padding: 12px; /* 내부 여백 설정 */
  font-size: 16px; /* 폰트 크기 설정 */
  width: 70%; /* 너비를 70%로 설정 */
  margin-right: 10px; /* 오른쪽에 10px 마진 설정 */
  border-radius: 4px; /* 둥근 테두리 설정 */
  border: 1px solid #ccc; /* 테두리 색상을 회색으로 설정 */
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); /* 내부 그림자 효과 */
  transition: border-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease; /* 테두리, 그림자, 색상에 애니메이션 추가 */
}

input[type="text"]:focus {
  border-color: #0072ff; /* 포커스 시 테두리 색상을 파란색으로 설정 */
  outline: none; /* 기본 포커스 테두리 제거 */
  box-shadow: 0 0 10px rgba(0, 114, 255, 0.3); /* 포커스 시 그림자 효과 추가 */
  color: #0072ff; /* 포커스 시 텍스트 색상을 파란색으로 설정 */
}

button {
  padding: 12px 25px; /* 내부 여백 설정 */
  font-size: 16px; /* 폰트 크기 설정 */
  background: linear-gradient(90deg, #00c6ff, #0072ff); /* 배경을 그라디언트로 설정 */
  color: white; /* 텍스트 색상을 흰색으로 설정 */
  border: none; /* 테두리 제거 */
  border-radius: 4px; /* 둥근 테두리 설정 */
  cursor: pointer; /* 커서를 포인터로 설정 */
  transition: background-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease; /* 배경색, 변형, 그림자에 애니메이션 추가 */
  margin: 5px; /* 마진 설정 */
}

button:hover {
  background: linear-gradient(90deg, #0072ff, #00c6ff); /* 호버 시 배경 그라디언트 반전 */
  transform: scale(1.05); /* 호버 시 살짝 확대 */
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); /* 호버 시 그림자 효과 강화 */
}

.name-label {
  color: #0072ff; /* 이름 텍스트 색상을 파란색으로 설정 */
}

.gender-label {
  color: #ff5722; /* 성별 텍스트 색상을 주황색으로 설정 */
}

.probability-label {
  color: #4caf50; /* 확률 텍스트 색상을 녹색으로 설정 */
}

.popularity-label {
  color: #ffeb3b; /* 인기 텍스트 색상을 노란색으로 설정 */
}

.age-label {
  color: #9c27b0; /* 나이 텍스트 색상을 보라색으로 설정 */
}

.error {
  color: red; /* 에러 메시지 색상을 빨간색으로 설정 */
  font-weight: bold; /* 에러 메시지를 굵게 표시 */
}

 

 

App.js

import React from 'react'; // 리액트 라이브러리에서 React를 가져옴
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom'; // 리액트 라우터에서 필요한 컴포넌트들 가져옴
import NameInsight from './components/NameInsight'; // NameInsight 컴포넌트를 가져옴
import Home from './components/Home'; // Home 컴포넌트를 가져옴
import './App.css'; // 글로벌 스타일 적용을 위한 CSS 파일 가져옴

const App = () => { // App 컴포넌트 정의
  return (
    <Router> // Router 컴포넌트로 전체 앱을 감싸서 라우팅 기능 제공
      <div>
        <nav> // 네비게이션 바 정의
          <ul> // 네비게이션 링크 리스트
            <li>
              <Link to="/">Home</Link> // Home 경로로 연결되는 링크
            </li>
            <li>
              <Link to="/name-insight">Name Insight</Link> // Name Insight 경로로 연결되는 링크
            </li>
          </ul>
        </nav>

        <Routes> // Routes 컴포넌트로 경로별로 컴포넌트를 매핑
          <Route path="/" element={<Home />} /> // Home 컴포넌트를 '/' 경로에 매핑
          <Route path="/name-insight" element={<NameInsight />} /> // NameInsight 컴포넌트를 '/name-insight' 경로에 매핑
        </Routes>
      </div>
    </Router>
  );
};

export default App; // App 컴포넌트를 기본으로 내보냄

 

App.css

body {
  font-family: 'Poppins', Arial, sans-serif;
  background-color: #f8f9fa;
  margin: 0;
  padding: 0;
  color: #343a40;
  line-height: 1.6;
}

nav {
  background: linear-gradient(90deg, #00c6ff, #0072ff);
  padding: 15px;
  text-align: center;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  border-bottom: 3px solid #0072ff;
}

nav ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  justify-content: center;
}

nav ul li {
  margin: 0 15px;
}

nav ul li a {
  color: white;
  text-decoration: none;
  font-size: 18px;
  transition: color 0.3s ease, background-color 0.3s ease;
  padding: 5px 10px;
  border-radius: 4px;
}

nav ul li a:hover {
  color: #fff176;
  background-color: rgba(255, 255, 255, 0.2);
}

 

Index.js

import React from 'react'; // 리액트 라이브러리에서 React를 가져옴
import ReactDOM from 'react-dom'; // 리액트 DOM 라이브러리에서 ReactDOM을 가져옴
import './index.css'; // CSS 파일을 가져와서 글로벌 스타일로 적용
import App from './App'; // App 컴포넌트를 가져옴
import reportWebVitals from './reportWebVitals'; // 웹 성능 측정 함수 가져옴

ReactDOM.render(
  <React.StrictMode> // React.StrictMode로 감싸서 개발 모드에서 문제 감지
    <App /> // App 컴포넌트를 렌더링
  </React.StrictMode>,
  document.getElementById('root') // 루트 DOM 노드에 렌더링
);

reportWebVitals(); // 웹 성능 측정을 위한 함수 호출

 

Index.css

body {
  margin: 0;
  /* 모든 여백을 0으로 설정합니다. */
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
  /* 여러 가지 폰트를 지정하여 사용자의 시스템에 맞는 폰트를 사용하게 합니다. */
  -webkit-font-smoothing: antialiased;
  /* 웹킷 기반 브라우저에서 폰트를 부드럽게 렌더링합니다. */
  -moz-osx-font-smoothing: grayscale;
  /* 모질라 기반 브라우저에서 폰트를 회색 음영으로 렌더링합니다. */
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
  /* 코드 텍스트에 대한 폰트를 지정합니다. */
}

 

Home.js

import React from 'react';

const Home = () => {
  return (
    <div>
      <h1>Home</h1>
      <p>Welcome to the Name Insight application!</p>
    </div>
  );
};

export default Home;

'LG CNS > 필기노트' 카테고리의 다른 글

문제  (0) 2025.02.19
1/9  (0) 2025.01.09
12/27  (0) 2025.01.08
1/6  (0) 2025.01.06
12월 26일 (목)  (0) 2025.01.05