본문 바로가기
React :

[React - Node.js - MySQL] 연동, 데이터 출력

by 밍코딩코 2025. 5. 5.

취뽀 기념 출근 전까지 열심히 공부하기

 

React - Node.js - MySQL 연결로 데이터베이스의 데이터를 출력

 

1. MySQL

MySQL Workbench로 DB 생성 및 테이블, 컬럼을 생성

 

2. Node.js

React 프로젝트에 연결할 예정이라서 Node.js로 선택

 

노드 설치 확인하기

node -v
npm -v

 

프로젝트 생성

mkdir word-quiz-server
cd word-quiz-server
npm init -y

 

필수 패키지 설치

npm install express cors mysql2 dotenv
  • express: Node.js 웹서버 프레임워크
  • cors: React에서 API 호출 허용 (다른 도메인에서 오는 요청을 허용)
  • mysql2: MySQL 연결을 위한 드라이버
  • dotenv: DB 비밀번호 등을 .env로 관리

.env 파일 생성 후 DB 관련 연결 설정

DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_password
DB_DATABASE=word_quiz

 

db.js 파일 생성 후 DB 연결 파일 생성

const mysql = require('mysql2');
require('dotenv').config();

const connection = mysql.createConnection({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_DATABASE,
});

connection.connect((err) => {
  if (err) throw err;
  console.log('MySQL Connected!');
});

module.exports = connection;
  • mysql2와 .env 파일의 내용을 사용할 수 있도록 require해줌
  • .env에 입력해둔 정보를 가져옴
  • err 실패 시 에러 출력, 성공하면 콘솔 출력

index.js 파일 생성 후 Express 서버 생성

const express = require('express');
const cors = require('cors');
const db = require('./db');

const app = express();
const PORT = 5000;

app.use(cors());
app.use(express.json());
  • express : Node.js에서 웹 서버를 쉽게 만들 수 있는 프레임워크
  • express 서버 인스턴스 생성. 포트 번호는 5000.
  • API 호출과 JSON 형태 body 파싱 가능하도록
app.get('/api/quiz', (req, res) => {
  const query = `
    SELECT * FROM vocabulary ORDER BY RAND() LIMIT 1;
  `;

  db.query(query, (err, result) => {
    if (err) return res.status(500).json({ error: err.message });

    const correct = result[0];

    const wrongQuery = `
      SELECT * FROM vocabulary
      WHERE voca_id != ?
      ORDER BY RAND()
      LIMIT 3;
    `;

    db.query(wrongQuery, [correct.voca_id], (err, wrongResults) => {
      if (err) return res.status(500).json({ error: err.message });

      const choices = [...wrongResults, correct];

      choices.sort(() => Math.random() - 0.5);

      res.json({
        word: correct.voca_word,
        correctAnswer: correct.voca_meaning,
        choices: choices.map(c => c.voca_meaning),
      });
    });
  });
});
  • req : 사용자가 요청한 정보 담음 / res : 서버가 요청할 내용을 담아서 보냄
  • query : vocabulary 테이블에서 랜덤으로 1개 가져오기. 쿼리를 실행하고 결과를 받음
  • ORDER BY RAND()로 테이블을 랜덤하게 섞어서 하나를 가져옴
  • wrongQuery : 정답 제외 보기용 오답 3개 가져오기.
  • ?에는 [correct.voca_id]가 들어감
  • choices : 오답 3개 + 정답 1개 합친 배열 생성
  • Math.random 으로 배열을 랜덤하게 섞음
  • json 형식으로 React에 전달 (단어, 정답, 보기용 단어)

+ 갑자기 생각난 sort((a, b) => a - b);는  비교 함수를 이용해 두 요소의 순서를 결정함

const numbers = [5, 2, 9, 1];

numbers.sort((a, b) => a - b);

예를 들어 이렇게 비교하면 5-2는 양수이므로 순서 변경, 2-9는 음수이므로 순서 유지

 

3. React

단어는 MiddleComponent.tsx에서 뜻은 BottomComponent.tsx에서 불러올 예정이므로

부모 컴포넌트.tsx에서 데이터를 받아오고 props로 전달해주도록 구현해야한다.

 

Parent Component.tsx

import React, { useEffect, useState } from "react";
import axios from "axios";
import MiddleComponent from "../components/MiddleComponent";
import BottomComponent from "../components/BottomComponent";

interface QuizDataType {
    word: string;
    choices: string[];
}

const ParentComponent = () => {
	const [quizData, setQuizData] = useState<QuizDataType | null>(null);
    
    useEffect(() => {
        axios.get("http://localhost:5000/api/quiz")
        .then((res) => setQuizData(res.data))
        .catch((err) => console.error("퀴즈 가져오기 실패: ", err));
    }, []);
    
    return (
    	<div>
        	<MiddleComponent word={quizData.word}/>
            <BottomComponent choices={quizData.choices}/>
        </div>
    )
}

- 타입을 지정해줌으로써 타입 관련 에러 해결. interface, quizData 타입 지정

- axios 요청 주소 https일 경우 브라우저 보안 정책으로 인증서가 없으면 차단됨 -> http로 수정

 

MiddleComponent.tsx / BottomComponent.tsx

import React, { useState, useEffect } from "react";
import { Navigation } from "react-router-dom";

interface SecondBottomProps {
    choices: string[];
  }
  
const SecondBottom: React.FC<SecondBottomProps> = ({ choices }) => {
    const [activeButton, setActiveButton] = useState(false);

    const onClickButton = () => {
        setActiveButton(true)
    }

    return (
        <div className="second-bottom-container">
            <div className="second-bottom-button-box">
        {choices.map((choice, index) => (
          <button key={index} className="bottom-box">{choice}</button>
        ))}
      </div>
        </div>
    )
}

export default SecondBottom;

 

여기까지는 vocabulary 테이블에 아무 단어나 랜덤으로 불러오도록 구현한것이다

추가로 단어집별로 단어를 나누고 매핑 후 해당 단어집에 해당하는 단어를 React에 출력해보려고한다.


1. quiz_book

단어집을 저장하는 quiz_book 테이블 quiz_book_name 컬럼에 

VOCA_EASY_BOOK, VOCA_MEDIUM_BOOK, VOCA_HARD_BOOK 을 저장함

 

2. book_voca

vocabulary(단어) 테이블과 quiz_book(단어집) 테이블을 값을 매핑할 book_voca 테이블 생성

bv_id, quiz_id, voca_id, difficulty 컬럼을 생성하고 quiz_id별 voca_id를 매핑 시켜 난의도 컬럼에 해당 난의도도 저장해둠

 

 

기존 코드 수정사항

  • 단어집 이름에 맞는 quiz_id를 quiz_book 테이블에서 가져옴
  • 해당 단어집에 포함된 단어를 필터링하고 랜덤으로 가져옴
  • API 경로를 /api/quiz 에서 /apk/quiz/:bookName 으로 수정하여 단어집 이름을 URL 파라미터로 받도록 수정
  • quiz_id에 해당하는 단어들을 book_voca와 vocabulary 테이블을 조인하여 랜덤으로 가져오도록 수정
app.get('/api/quiz/:bookName', (req, res) => {
	const bookName = req.params.bookName;

    const quizIdQuery = `
      SELECT quiz_id FROM quiz_book WHERE quiz_book_name = ?;
    `;
    
    const quizId = quizResults[0].quiz_id;
  
    const wordQuery = `
        SELECT v.voca_word, v.voca_meaning, bv.voca_id
        FROM book_voca bv
        JOIN vocabulary v ON bv.voca_id = v.voca_id
        WHERE bv.quiz_id = ?
        ORDER BY RAND() LIMIT 1;
      `;
      
   	db.query(wordQuery, [quizId], (err, result) => {

 

 

 

 

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

'React :' 카테고리의 다른 글

[React] 프로젝트 웹 서버 배포 / Github pages  (0) 2025.03.27