React.js를 사용해서 낱말 맞추기 게임 만들기

서론

웹 페이지의 중요 키워드를 활용하여 페이지 내에 소규모 게임을 통합함으로써 사용자 참여를 크게 높일 수 있지 않을까? 바쁜 일정에서 잠시 벗어나 스트레스를 해소하고자 하는 직장인들이 이 게임을 통해 웹 서비스에 끌려올 수 있지 않을까? 이러한 생각에서 시작된 낱말 맞추기 게임을 간단하면서도 사용자의 머무름을 늘리는 효과적인 웹페이지 추가 요소로 개발해보자.

 

 

사전준비

낱말의 정의를 구하기 위해 Naver Search API(이하 NSA)를  react에서 바로 호출해도 무방하지만 CORS 이슈를 신경써야 하고, 매 번 API를 호출하기에는 하루에 정해진 호출량이 존재하기 때문에 나는 Naver Search API를 다루는 Server API를 간단하게 만들어서 사용했다. 어떠한 언어를 사용해도 무방하지만, 흐름은 React -> Server -> Naver Search API -> Server -> React 순서로 개발하자.

 

나는 Laravel Framework를 사용해서 간단하게 NSA와 연동되어 데이터를 컨트롤하는 Server API를 개발하였다. 해당 서버의 API는 React에서 낱말에 대한 Keyword를 넘겨받아 해당 Keyword에 해당하는 단어의 데이터가 존재하는지 유무를 체크하여 이미 데이터가 존재하는 Keyword라면 기존 데이터를 return해주고, 새로운 Keyword라면 NSA를 호출하여 응답받은 데이터를 저장하고, 해당 데이터를 Return해주는 기능만 구현했다.

 

아래 포스팅을 참고하여 해당 API를 사용할 수 있다.

 

NAVER Search API를 활용한 단어 백과사전 검색 API 만들기

서론 최근 팀장님께 조언을 들은겸, 게임 하나를 간단하게 만들어 보려고 한다. 특정 페이지의 키워드를 기준으로 단어를 맞추는 게임을 react로 만드려고 하는데, 단어를 맞추기 앞서 단어의 정

min-nine.tistory.com

 

 

React Project 생성 및 기본 틀 구현

Npx CRA (create-react-app)을 사용하여 빠르게 React 프로젝트를 만들 수 있다.

npx create-react-app wordgame

컴포넌트의 구조는 아래 같이 GameContainer, WordGame 두개의 메인 파일만으로 구성할 것이다. 

  1. GameContainer: 전체 게임을 감싸는 컴포넌트.
  2. WordGame: 실제 낱말 맞추기 게임 로직을 포함하는 컴포넌트.
// src/container/GameContainer.js
import React from 'react';
import WordGame from "../WordGame";

const GameContainer = () => {
  return (
    <div style={{ width: '100%', height: '100vh', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
      <WordGame />
    </div>
  );
};

export default GameContainer;

// src/WordGame.js
import React from 'react';

const WordGame = () => {
  // 게임 로직 추가 예정
  return (
    <div style={{ width: '100%', height: '100%', backgroundColor: '#f0f0f0' }}>
      {/* 게임 구성요소 */}
    </div>
  );
};

export default WordGame;

 

 

GameContainer 컴포넌트의 역할 및 기능

 

단어 목록 관리 (words 배열):

  •  words는 게임에서 사용되는 단어 목록을 나타내는 배열이다. 배열에는 다양한 단어들이 포함되어 있으며, 이들 중 하나가 게임에서 사용될 단어로 선택된다.

현재 단어 상태 관리 (currentWord, setCurrentWord):

  • currentWord 상태는 현재 게임에서 플레이어가 맞추어야 할 단어를 저장한다. useState 훅을 사용하여 currentWord 상태를 생성하고 관리한다.

초기 단어 랜덤 선택 (useEffect 훅 사용):

  • useEffect 훅은 컴포넌트가 마운트될 때 ([] 종속성 배열 사용) 실행된다. words 배열에서 랜덤한 단어를 선택하여 currentWord 상태를 설정하는데, 이는 게임이 시작될 때 매번 다른 단어가 선택되도록 한다.

WordGame 컴포넌트 렌더링:

  • WordGame 컴포넌트를 렌더링하고, 이에 words, currentWord, setCurrentWord를 props로 전달한다. WordGame 컴포넌트는 게임의 실제 인터페이스와 로직을 담당한다.
// GameContainer 컴포넌트
import React , { useState, useEffect } from 'react';
import WordGame from "../WordGame";

const GameContainer = () => {
    const words = ['호주','다음','방산','수출','수주','잔고','루마니아','레드','회사','한화'];
    const [currentWord, setCurrentWord] = useState('');

    useEffect(() => {
        if (words.length > 0) {
            const randomWord = words[Math.floor(Math.random() * words.length)];
            setCurrentWord(randomWord);
        }
    }, []);

    return (
        <div style={{width: '100%', height: '100vh', display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
            <div style={{width: '50vw', height: '50vw', maxWidth: '100%', aspectRatio: '1 / 1'}} title="Word Game">
                <WordGame words={words} currentWord={currentWord} setCurrentWord={setCurrentWord}/>
            </div>
        </div>
    );
};

export default GameContainer;

 

 

 

WordGame 구현

Current Word가 바뀔 때 해당 단어에 맞는 설명을 가져오게 아래 기능을 추가한다.

// 단어 설명 가져오기 및 현재 단어 설정
useEffect(() => {
    if (currentWord) {
        const loadData = async () => {                
            fetchDescription(currentWord).then(desc => {
                setCurrentDescription(desc);
            });
        };

        loadData();
    }
}, [currentWord]);

API를 호출하는 fetchDescription 메서드를 정의한다.

// API 호출 함수
const fetchDescription = async (word) => {
    const query = word;
    const url = `http://127.0.0.1:8000/api/v1/naver/${encodeURIComponent(query)}`;
    try {
        const response = await fetch(url, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            },
            cache: 'default'
        });

        if (!response.ok) {
            throw new Error(`Error! status: ${response.status}`);
        }

        const result = await response.json();
        const newDescription = processDescription(result.items[hintIndex].description);
        setDescriptions(result.items);

        return newDescription; // 첫 번째 검색 결과의 설명을 반환
    } catch (error) {
        console.error('API 호출 중 오류 발생:', error);
        return '설명을 불러오는데 실패했습니다.';
    }
};

정답을 제출하거나, 힌트를 보여주는 메서드를 정의한다.

// 제출 처리 함수
const handleSubmit = () => {
    if (userInput === currentWord) {
        const newWord = words[Math.floor(Math.random() * words.length)];
        setCurrentWord(newWord);
        alert(`정답입니다!`);
        setHintIndex(0);
        setUserInput('');
    } else {
        alert('틀렸습니다. 다시 시도해보세요.');
        setUserInput('');
    }
};

// 힌트 더보기 기능
const moreHint = () => {

    if (hintIndex < maxHints) {
        const nextHintIndex = hintIndex + 1;
        setHintIndex(nextHintIndex);

        if (descriptions[nextHintIndex]) {
            const newDescription = processDescription(descriptions[nextHintIndex].description);
            setCurrentDescription(newDescription);
        }
    } else {
        alert('더 이상 힌트를 제공할 수 없습니다.');
    }
};

NSA로 최대 10개까지의 사전 검색 결과를 가져오기 때문에, 힌트는 최대 10번까지 제공하였다.

 

 

결과물

'Library > React.js' 카테고리의 다른 글

Backend 개발자를 위한 React 기초 정리  (2) 2023.11.20