취뽀 기념 출근 전까지 열심히 공부하기
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 |
---|