728x90
๋ฐ์ํ
ํ๋ก๊ทธ๋๋ฐ ์ธ๊ณ๋ฅผ ํ๊ตฌํฉ์๋ค.
ํด๋น ํ์ด์ง์ ํ๋ก ํธ๋ react๋ก ๊ตฌ์ฑํ์์ต๋๋ค. ๋ฐ์ดํฐ๋ springboot๋ฅผ ํ์ฉํ์ฌ ๊ฐ์ ธ์์ต๋๋ค.
์ ๊ธฐ์ฐจ์ ๋ฐ๋ฅธ ์ฃผํ๊ฑฐ๋ฆฌ๋ฅผ ์ ์ ์์ผ๋ฉฐ, ์ฐจ๋์ ๊ฒ์ํ๊ณ ํด๋ฆญํ์ฌ ์๋จ์ ์ ์ฅํ ์ ์์ต๋๋ค.
๊ตฌํ ํ๋ฉด
์์ฑ ์ฝ๋
(1) Spring Boot
CarDataController.java
package com.example.board.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.board.model.CarData;
import com.example.board.repository.CarDataRepository;
@RestController
public class CarDataController {
@Autowired
private CarDataRepository carRepo;
@GetMapping("/media/cardata")
public List<CarData> getAllCars() {
return carRepo.findAll();
}
}
CarData.java
package com.example.board.model;
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.Data;
@Entity
@Data
public class CarData {
@Id
Integer id;
String model;
String km;
}
CarDataRepository.java
package com.example.board.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.board.model.CarData;
public interface CarDataRepository extends JpaRepository<CarData, Integer> {
}
(2) react
App.js
import React, { useState, useEffect } from 'react';
import Cardata from './cardata/Cardata';
import Header from './header/Header';
import Loading from './loading/Loading';
import './App.css';
function App() {
const [message, setMessage] = useState("");
const [isLoading, setIsLoading] = useState(true); // ๋ก๋ฉ ์ํ๋ฅผ ๊ด๋ฆฌ
useEffect(() => {
// ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋น๋๊ธฐ ์์
์ ์๋ฎฌ๋ ์ดํธ
setTimeout(() => {
fetch('/media/cardata')
.then(response => response.text())
.then(message => {
setMessage(message);
setIsLoading(false); // 2์ด ํ์ ๋ก๋ฉ ์๋ฃ๋ก ์ค์
});
}, 2500); // 2์ด ์ง์ฐ
}, []);
return (
<>
{isLoading ? (
<Loading />
) : (
<>
<Header />
<Cardata />
</>
)}
</>
);
}
export default App;
package.json
"proxy": "http://localhost",
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
Cardata.jsx
import { useState, useEffect } from 'react';
import style from './Cardata.module.css'
import ReactPlayer from 'react-player';
const Cardata = () => {
const [carDatas, setCarDatas] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [selectedItems, setSelectedItems] = useState([]);
useEffect(() => {
fetch("/media/cardata")
.then((res) => res.json())
.then((data) => setCarDatas(data));
}, []);
const toggleSelect = (item) => {
if (selectedItems.includes(item)) {
setSelectedItems(selectedItems.filter((selectedItem) => selectedItem !== item));
} else {
setSelectedItems([...selectedItems, item]);
}
}
return (
<div className={style['react']}>
<p className={style['right-top-text']}>react + springboot ํ์ฉํ ํ์ด์ง์
๋๋ค.</p>
<div className={style['car-data-title']}>
<div>
<h1>์ ๊ธฐ์ฐจ ์ฃผํ๊ฑฐ๋ฆฌ</h1>
</div>
<div>
<p className={style['car-data-dis']}>์ ๊ธฐ์ฐจ ์ฃผํ๊ฑฐ๋ฆฌ : ํ ๋ฒ ์ถฉ์ ์ผ๋ก ์ฃผํํ ์ ์๋ ๊ฑฐ๋ฆฌ</p>
</div>
</div>
<div className={style['container']}>
<div className={style['car-data-container']}>
<ReactPlayer
url="https://www.youtube.com/watch?v=m3J1TAF93pQ"
playing={true}
loop={true}
muted={true}
width="100%"
height="100%"
style={{ position: 'absolute', top: 0, left: 0, zIndex: -1 }}
/>
<p className={style['car-data-intro']}>์ฃผํ๊ฑฐ๋ฆฌ ์์ 250๊ฐ ์ ๊ธฐ์๋์ฐจ ๋ชฉ๋ก์
๋๋ค.<br /><br />
์๊ณ ์ถ์ ์ฐจ์ข
์ ๊ฒ์ํด ๋ณด์ธ์!</p>
<div className={style['search-bar']}>
<input
type="text"
placeholder="์ฐจ ์ข
๋ฅ ์
๋ ฅ"
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
<button className={style['search-button']}>๊ฒ์</button>
</div>
</div>
<div className={style['results-container']}>
<h2 className={style['selected-items-title']}>๊ฒ์ ๊ธฐ๋ก</h2>
{selectedItems.map((item, index) => (
<div key={index}>
<p>{item} <button onClick={() => toggleSelect(item)}>x</button></p>
</div>
))}
{carDatas.filter((val) => {
if (searchTerm === "") {
return val;
} else if (val.model.toLowerCase().includes(searchTerm.toLowerCase())) {
return val;
}
}).map((car) => (
<div key={car.id} className={style['car-item']}>
<a href="#" onClick={(e) => { e.preventDefault(); toggleSelect(`${car.model} - ${car.km} km`); }}>{car.model} - {car.km} km</a>
</div>
))
}
</div>
</div>
</div >
);
}
export default Cardata;
Cardata.module.css
h1 {
font-size: 3.5rem;
margin-bottom: 5rem;
color: #f2c12e;
}
.selected-items-title {
text-align: center;
width: 94rem;
font-size: 1.7rem;
height: 4rem;
}
p {
text-align: center;
margin-bottom: 3rem;
}
.car-data-container {
text-align: center;
margin-bottom: 3rem;
padding: 1rem;
border-radius: 5px;
font-family: 'TheJamsil5Bold';
animation: fadeIn 1.5s ease-in-out;
font-size: 2rem;
}
.car-data-title {
position: relative;
text-align: center;
border-radius: 5px;
font-family: 'TheJamsil5Bold';
animation: fadeIn 1.5s ease-in-out;
}
.car-data-dis {
font-size: 1.8rem;
margin-bottom: 1.5rem;
}
.hidden {
display: none;
}
@keyframes slideFromLeft {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.hidden .car-item {
opacity: 0;
transform: scale(0);
}
.car-data-intro {
margin-bottom: 5rem;
}
.search-bar {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
}
input[type='text'] {
width: 300px;
height: 30px;
}
.search-button {
width: 70px;
height: 35px;
background-color: #f2c12e;
color: black;
border: none;
cursor: pointer;
}
.car-list {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.car-item {
width: 300px;
background-color: #f9f9f9;
padding-top: 10px;
padding-bottom: 10px;
margin: 0 auto;
/* ์์ง ๋ง์ง์ 0, ์ํ ๋ง์ง์ ์๋์ผ๋ก ์ค์ ํ์ฌ ๊ฐ์ด๋ฐ ์ ๋ ฌ */
}
strong {
font-weight: bold;
}
.car-item {
font-family: 'TheJamsil5Bold';
text-align: center;
}
.Km {
color: black;
}
@font-face {
font-family: 'TheJamsil5Bold';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2302_01@1.0/TheJamsil5Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
}
.right-top-text {
position: absolute;
top: 2rem;
right: 1rem;
}
.results-container {
height: 30rem;
/* ์ํ๋ ๋์ด๋ก ์ค์ ํ์ธ์. */
overflow-y: auto;
/* ์์ง ์คํฌ๋กค์ ํ์ฉํฉ๋๋ค. */
}
์ ๋ฐฉ์์ ์ฌ์ฉํ์ฌ react์ spring boot๋ฅผ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค.
728x90
๋ฐ์ํ