블로그 테마 선정부터 환경 세팅까지는 이전 블로그 보기
[개인메모(설치환경)] - 01_proman_Blog Thema(블로그 테마) 수정 후 & BackEnd(백앤드) 연동
README.md
설치 패키지 npm 설치 또는 cmd 로 진행하기
더보기
# 설치 패키지
# 1) 메뉴 라이브러리 설치
npm i react-router-dom
npm i @types/react-router-dom
# 2) 벡엔드 연동 라이브러리 설치
npm i axios
# 3) pre css 컴파일러 : sass
npm i sass
# 4) Material Page component 업그레이드
# 과거 v4 -> v5 변경 설치
npm i @mui/material @emotion/react @emotion/styled
# 4-1) 소스에서 임포트 사용법 : <Pagination />
import Pagination from '@mui/material/Pagination';
# 5) typescript jquery, jqueryui type 넣기
# 5-1) typescript jquery 사용
npm i --save-dev @types/jquery
npm i --save-dev @types/jqueryui
# * 외부 라이브러리 타입이 없을 경우 처리 : 타입 설정
# 1) tsconfig.json 파일 - compilerOptions 속성에 아래 추가 : 프로젝트시작위치/types - 이 위치에 타입을 인식하게 하는 경로 설정
"typeRoots": ["./types", "./node_modules/@types"], // 보통 types 폴더를 만들어 타입 정의
# 2) types/외부라이브러리명/index.d.ts 파일 생성 후 아래 추가
declare module '외부라이브러리명';
세팅
라이브러리 설치, 폴더 구조 복사, 원본 들고오기(Bootstrap CSS, js 라이브러리 및 스크립트 파일 포함)
Nav.tsx
메뉴만 옮겨가는 공통 컴포넌트
C:\Work\08_Pilot_Project\01_Blog\01_JPA\02_normal_thema\02_prixima\frontend-typescript\src\components\common\Nav.tsx
Home.tsx
C:\Work\08_Pilot_Project\01_Blog\01_JPA\02_normal_thema\02_prixima\frontend-typescript\src\pages\Home.tsx
TODO: 5 블로그 id="blog" => QNA 게시판으로 수정해보기
전체 조회 & 추가 페이지 & 수정, 삭제
더보기
/* C:\Work\08_Pilot_Project\01_Blog\01_JPA\02_normal_thema\02_prixima\frontend-typescript\src\pages\Home.tsx */
import React, { useEffect, useState } from "react";
import Nav from "../components/common/Nav";
// 개발자가 직접 만든 css import
import "../assets/css/style.css";
import initMain from "../assets/js/app";
// qna import
import AddQna from "./basic/qna/AddQna";
import QnaList from "./basic/qna/QnaList";
import Qna from "./basic/qna/Qna";
export default function Home() {
// TODO: 변수 정의
// 화면명 저장 변수
const [viewQna, setViewQna] = useState<string>("");
// 기본키 저장 변수
const [pid, setPid] = useState<Number>(0);
// TODO: 함수 정의
useEffect(() => {
initMain();
}, []);
// qna 메뉴를 클릭했을 때 실행되는 함수
const handleChangeQna = (viewQna: string, pid = 0) => {
setViewQna(viewQna); // 화면명 저장
setPid(pid); // 기본키 저장
};
// qna 화면명에 따라 다른 컴포넌트를 보여주는 함수
const changeQna = () => {
if (viewQna === "qnaList") {
// TODO: props : handleChangeQna 함수를 전송
return <QnaList handleChangeQna={handleChangeQna} />; // 전체조회
} else if (viewQna === "addQna") {
return <AddQna />; // 추가생성
} else if (viewQna === "qna") {
// TODO: props : Qna 컴포넌트에 qno 로 데이터 전송
return <Qna qno={pid} />; // qno 로 데이터 전송
}
}
return (
// TODO: TypeScript와 JSX (TSX)
<>
{/* <!-- TOP NAV --> */}{" "}
<div className="top-nav" id="home">
<div className="container">
<div className="row justify-content-between">
<div className="col-auto">
<p>
{" "}
<i className="bx bxs-envelope"></i> info@example.com
</p>
<p>
{" "}
<i className="bx bxs-phone-call"></i> 123 456-7890
</p>
</div>
<div className="col-auto social-icons">
<a href="#">
<i className="bx bxl-facebook"></i>
</a>
<a href="#">
<i className="bx bxl-twitter"></i>
</a>
<a href="#">
<i className="bx bxl-instagram"></i>
</a>
<a href="#">
<i className="bx bxl-pinterest"></i>
</a>
</div>
</div>
</div>
</div>
{/* <!-- BOTTOM NAV --> */}
<Nav />
{/* <!-- SLIDER --> */}
<div className="owl-carousel owl-theme hero-slider">
<div className="slide slide1">
<div className="container">
<div className="row">
<div className="col-12 text-center text-white">
<h6 className="text-white text-uppercase">
design Driven for professional
</h6>
<h1 className="display-3 my-4">
We craft digital
<br />
experiances
</h1>
<a href="#" className="btn btn-brand">
Get Started
</a>
<a href="#" className="btn btn-outline-light ms-3">
Our work
</a>
</div>
</div>
</div>
</div>
<div className="slide slide2">
<div className="container">
<div className="row">
<div className="col-12 col-lg-10 offset-lg-1 text-white">
<h6 className="text-white text-uppercase">
We craft digital experiances
</h6>
<h1 className="display-3 my-4">
Design Driven For <br />
Professionals
</h1>
<a href="#" className="btn btn-brand">
Get Started
</a>
<a href="#" className="btn btn-outline-light ms-3">
Our work
</a>
</div>
</div>
</div>
</div>
</div>
{/* <!-- ABOUT --> */}
<section id="about">
<div className="container">
<div className="row justify-content-center">
<div className="col-lg-5 py-5">
<div className="row">
<div className="col-12">
<div className="info-box">
<img src="img/icon6.png" alt="" />
<div className="ms-4">
<h5>Digital Marketing</h5>
<p>
It is a long established fact that a reader will be
distracted by the readable content of a page{" "}
</p>
</div>
</div>
</div>
<div className="col-12 mt-4">
<div className="info-box">
<img src="img/icon4.png" alt="" />
<div className="ms-4">
<h5>E-mail Marketing</h5>
<p>
It is a long established fact that a reader will be
distracted by the readable content of a page{" "}
</p>
</div>
</div>
</div>
<div className="col-12 mt-4">
<div className="info-box">
<img src="img/icon5.png" alt="" />
<div className="ms-4">
<h5>Buisness Marketing</h5>
<p>
It is a long established fact that a reader will be
distracted by the readable content of a page{" "}
</p>
</div>
</div>
</div>
</div>
</div>
<div className="col-lg-5">
<img src="img/about.png" alt="" />
</div>
</div>
</div>
</section>
{/* <!-- MILESTONE --> */}
<section id="milestone">
<div className="container">
<div className="row text-center justify-content-center gy-4">
<div className="col-lg-2 col-sm-6">
<h1 className="display-4">90K+</h1>
<p className="mb-0">Happy Clients</p>
</div>
<div className="col-lg-2 col-sm-6">
<h1 className="display-4">45M</h1>
<p className="mb-0">Lines of code</p>
</div>
<div className="col-lg-2 col-sm-6">
<h1 className="display-4">190</h1>
<p className="mb-0">Total Downloads</p>
</div>
<div className="col-lg-2 col-sm-6">
<h1 className="display-4">380K</h1>
<p className="mb-0">YouTube Subscribers</p>
</div>
</div>
</div>
</section>
{/* <!-- 1 --> */}
<section id="services" className="text-center">
<div className="container">
<div className="row">
<div className="col-12">
<div className="intro">
<h6>Our Services</h6>
<h1>What We Do?</h1>
<p className="mx-auto">
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from 45 BC, making it over 2000 years old
</p>
</div>
</div>
</div>
<div className="row g-4">
<div className="col-lg-4 col-md-6">
<div className="service">
<img src="img/icon1.png" alt="" />
<h5>Digital Marketing</h5>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
</div>
</div>
<div className="col-lg-4 col-md-6">
<div className="service">
<img src="img/icon2.png" alt="" />
<h5>Logo Designing</h5>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
</div>
</div>
<div className="col-lg-4 col-md-6">
<div className="service">
<img src="img/icon3.png" alt="" />
<h5>Buisness consulting</h5>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
</div>
</div>
<div className="col-lg-4 col-md-6">
<div className="service">
<img src="img/icon4.png" alt="" />
<h5>Videography</h5>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
</div>
</div>
<div className="col-lg-4 col-md-6">
<div className="service">
<img src="img/icon5.png" alt="" />
<h5>Brand Identity</h5>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
</div>
</div>
<div className="col-lg-4 col-md-6">
<div className="service">
<img src="img/icon6.png" alt="" />
<h5>Ethical Hacking</h5>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
</div>
</div>
</div>
</div>
</section>
{/* <!-- 2 --> */}
<section className="bg-light" id="portfolio">
<div className="container">
<div className="row">
<div className="col-12">
<div className="intro">
<h6>Work</h6>
<h1>Successful projects</h1>
<p className="mx-auto">
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from 45 BC, making it over 2000 years old
</p>
</div>
</div>
</div>
</div>
<div id="projects-slider" className="owl-theme owl-carousel">
<div className="project">
<div className="overlay"></div>
<img src="img/project1.jpg" alt="" />
<div className="content">
<h2>Consulting Marketing</h2>
<h6>Website Design</h6>
</div>
</div>
<div className="project">
<div className="overlay"></div>
<img src="img/project2.jpg" alt="" />
<div className="content">
<h2>Consulting Marketing</h2>
<h6>Website Design</h6>
</div>
</div>
<div className="project">
<div className="overlay"></div>
<img src="img/project3.jpg" alt="" />
<div className="content">
<h2>Consulting Marketing</h2>
<h6>Website Design</h6>
</div>
</div>
<div className="project">
<div className="overlay"></div>
<img src="img/project4.jpg" alt="" />
<div className="content">
<h2>Consulting Marketing</h2>
<h6>Website Design</h6>
</div>
</div>
<div className="project">
<div className="overlay"></div>
<img src="img/project5.jpg" alt="" />
<div className="content">
<h2>Consulting Marketing</h2>
<h6>Website Design</h6>
</div>
</div>
</div>
</section>
{/* <!-- 3 --> */}
<section id="team">
<div className="container">
<div className="row">
<div className="col-12">
<div className="intro">
<h6>Team</h6>
<h1>Team Members</h1>
<p className="mx-auto">
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from 45 BC, making it over 2000 years old
</p>
</div>
</div>
</div>
<div className="row justify-content-center">
<div className="col-lg-4 col-md-8">
<div className="team-member">
<div className="image">
<img src="img/team_1.jpg" alt="" />
<div className="social-icons">
<a href="#">
<i className="bx bxl-facebook"></i>
</a>
<a href="#">
<i className="bx bxl-twitter"></i>
</a>
<a href="#">
<i className="bx bxl-instagram"></i>
</a>
<a href="#">
<i className="bx bxl-pinterest"></i>
</a>
</div>
<div className="overlay"></div>
</div>
<h5>Marvin McKinney</h5>
<p>Marketing Coordinator</p>
</div>
</div>
<div className="col-lg-4 col-md-8">
<div className="team-member">
<div className="image">
<img src="img/team_2.jpg" alt="" />
<div className="social-icons">
<a href="#">
<i className="bx bxl-facebook"></i>
</a>
<a href="#">
<i className="bx bxl-twitter"></i>
</a>
<a href="#">
<i className="bx bxl-instagram"></i>
</a>
<a href="#">
<i className="bx bxl-pinterest"></i>
</a>
</div>
<div className="overlay"></div>
</div>
<h5>Kathryn Murphy</h5>
<p>Ethical Hacker</p>
</div>
</div>
<div className="col-lg-4 col-md-8">
<div className="team-member">
<div className="image">
<img src="img/team_3.jpg" alt="" />
<div className="social-icons">
<a href="#">
<i className="bx bxl-facebook"></i>
</a>
<a href="#">
<i className="bx bxl-twitter"></i>
</a>
<a href="#">
<i className="bx bxl-instagram"></i>
</a>
<a href="#">
<i className="bx bxl-pinterest"></i>
</a>
</div>
<div className="overlay"></div>
</div>
<h5>Darrell Steward</h5>
<p>Software Developer</p>
</div>
</div>
</div>
</div>
</section>
{/* <!-- 4 --> */}
<section className="bg-light" id="reviews">
<div className="owl-theme owl-carousel reviews-slider container">
<div className="review">
<div className="person">
<img src="img/team_1.jpg" alt="" />
<h5>Ralph Edwards</h5>
<small>Market Development Manager</small>
</div>
<h3>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aut
quis, rem culpa labore voluptate ullam! In, nostrum. Dicta, vero
nihil. Delectus minus vitae rerum voluptatum, excepturi incidunt
ut, enim nam exercitationem optio ducimus!
</h3>
<div className="stars">
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star-half"></i>
</div>
<i className="bx bxs-quote-alt-left"></i>
</div>
<div className="review">
<div className="person">
<img src="img/team_2.jpg" alt="" />
<h5>Ralph Edwards</h5>
<small>Market Development Manager</small>
</div>
<h3>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aut
quis, rem culpa labore voluptate ullam! In, nostrum. Dicta, vero
nihil. Delectus minus vitae rerum voluptatum, excepturi incidunt
ut, enim nam exercitationem optio ducimus!
</h3>
<div className="stars">
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star-half"></i>
</div>
<i className="bx bxs-quote-alt-left"></i>
</div>
<div className="review">
<div className="person">
<img src="img/team_3.jpg" alt="" />
<h5>Ralph Edwards</h5>
<small>Market Development Manager</small>
</div>
<h3>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aut
quis, rem culpa labore voluptate ullam! In, nostrum. Dicta, vero
nihil. Delectus minus vitae rerum voluptatum, excepturi incidunt
ut, enim nam exercitationem optio ducimus!
</h3>
<div className="stars">
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star"></i>
<i className="bx bxs-star-half"></i>
</div>
<i className="bx bxs-quote-alt-left"></i>
</div>
</div>
</section>
{/* <!-- TODO: 5 블로그 --> */}
{/* qna 게시판 메뉴 */}
<section id="blog">
<div className="container">
{/* Blog Post 시작 */}
<div className="row">
<div className="col-12">
<div className="intro">
<h6>Blog</h6>
<h1>Blog Posts</h1>
<p className="mx-auto">
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from 45 BC, making it over 2000 years old
</p>
</div>
</div>
</div>
{/* Blog Post 끝 */}
{/* qna 게시판 시작 */}
<div className="row">
{/* qna 게시판 */}
<div className="col-md-4">
<article className="blog-post">
<img src="img/project5.jpg" alt="" />
{/* 제목 시작 */}
<a href="#" className="tag">
Q & A 예제
</a>
{/* 제목 끝 */}
{/* 본문 시작 */}
<div className="content">
<small>React & Springboot & Oracle</small>
<h5>Q & A 게시판 예제</h5>
<p>
<button className="btn btn-primary btn-sm"
onClick={()=>handleChangeQna("qnaList")}
>Q&A 조회</button>
<button className="btn btn-success btn-sm"
onClick={()=>handleChangeQna("addQna")}
>Q&A 추가</button>
</p>
</div>
{/* 본문 끝 */}
</article>
</div>
{/* programming */}
<div className="col-md-4">
<article className="blog-post">
<img src="img/project4.jpg" alt="" />
<a href="#" className="tag">
Programming
</a>
<div className="content">
<small>01 Dec, 2022</small>
<h5>Web Design trends in 2022</h5>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
</div>
</article>
</div>
{/* marketing */}
<div className="col-md-4">
<article className="blog-post">
<img src="img/project2.jpg" alt="" />
<a href="#" className="tag">
Marketing
</a>
<div className="content">
<small>01 Dec, 2022</small>
<h5>Web Design trends in 2022</h5>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
</div>
</article>
</div>
</div>
{/* qna 게시판 끝 */}
{/* qna 결과 화면 */}
<div className="row">
<div className="col-md-12">
{/* 게시판 실행 */}
{changeQna()}
</div>
</div>
</div>
</section>
{/* <!-- footer --> */}
<footer>
<div className="footer-top text-center">
<div className="container">
<div className="row justify-content-center">
<div className="col-lg-6 text-center">
<h4 className="navbar-brand">
Prixima<span className="dot">.</span>
</h4>
<p>
Contrary to popular belief, Lorem Ipsum is not simply random
text. It has roots in a piece of classical Latin literature
from
</p>
<div className="col-auto social-icons">
<a href="#">
<i className="bx bxl-facebook"></i>
</a>
<a href="#">
<i className="bx bxl-twitter"></i>
</a>
<a href="#">
<i className="bx bxl-instagram"></i>
</a>
<a href="#">
<i className="bx bxl-pinterest"></i>
</a>
</div>
<div className="col-auto conditions-section">
<a href="#">privacy</a>
<a href="#">terms</a>
<a href="#">disclaimer</a>
</div>
</div>
</div>
</div>
</div>
<div className="footer-bottom text-center">
<p className="mb-0">Copyright vicpra 2022. All rights Reserved</p>{" "}
Distributed By <a href="https://themewagon.com">ThemeWagon</a>
</div>
</footer>
{/* <!-- Modal --> */}
<div
className="modal fade"
id="exampleModal"
tabIndex={-1}
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div className="modal-dialog modal-dialog-centered modal-xl">
<div className="modal-content">
<div className="modal-body p-0">
<div className="container-fluid">
<div className="row gy-4">
<div
className="col-lg-4 col-sm-12 bg-cover"
style={{
backgroundImage: "url(img/c2.jpg)",
minHeight: 300 + "px",
}}
>
<div></div>
</div>
<div className="col-lg-8">
<form className="p-lg-5 col-12 row g-3">
<div>
<h1>Get in touch</h1>
<p>
Fell free to contact us and we will get back to you as
soon as possible
</p>
</div>
<div className="col-lg-6">
<label htmlFor="userName" className="form-label">
First name
</label>
<input
type="text"
className="form-control"
placeholder="Jon"
id="userName"
aria-describedby="emailHelp"
/>
</div>
<div className="col-lg-6">
<label htmlFor="userName" className="form-label">
Last name
</label>
<input
type="text"
className="form-control"
placeholder="Doe"
id="userName"
aria-describedby="emailHelp"
/>
</div>
<div className="col-12">
<label htmlFor="userName" className="form-label">
Email address
</label>
<input
type="email"
className="form-control"
placeholder="Johndoe@example.com"
id="userName"
aria-describedby="emailHelp"
/>
</div>
<div className="col-12">
<label
htmlFor="exampleInputEmail1"
className="form-label"
>
Enter Message
</label>
<textarea
name=""
placeholder="This is looking great and nice."
className="form-control"
id=""
rows={4}
></textarea>
</div>
<div className="col-12">
<button type="submit" className="btn btn-brand">
Send Message
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
); // end of HTML return
} // end of Home
더보기
// C:\Work\08_Pilot_Project\01_Blog\01_JPA\02_normal_thema\01_proman\frontend-typescript\src\pages\basic\qna\QnaList.tsx
import { Pagination } from "@mui/material";
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import IQna from "../../../types/basic/IQna";
import QnaService from "../../../service/basic/QnaService";
import TitleCom from "../../../components/common/TitleCom";
function QnaList(props: any) {
// 변수 정의
// qna 배열 변수
const [qna, setQna] = useState<Array<IQna>>([]);
// select 태그에 선택된 값을 저장할 변수 : 기본 (question)
const [searchSelect, setSearchSelect] = useState<string>("question");
// 검색어(input) 변수
const [searchKeyword, setSearchKeyword] = useState<string>("");
// todo: 공통 페이징 변수 4개
// todo: 공통 변수 : page(현재페이지번호), count(총페이지건수), pageSize(3,6,9 배열)
const [page, setPage] = useState<number>(1);
const [count, setCount] = useState<number>(1);
const [pageSize, setPageSize] = useState<number>(3); // 1페이지당개수
const pageSizes = [3, 6, 9]; // 공통 pageSizes : 배열 (셀렉트 박스 사용)
// 함수 정의
// 화면이 뜰때 실행되는 이벤트 + 감시변수
useEffect(() => {
retrieveQna(); // 전체조회 실행
}, [page, pageSize]);
// 전체조회
const retrieveQna = () => {
// 벡엔드 매개변수 전송 : + 현재페이지(page), 1페이지당개수(pageSize)
QnaService.getAll(searchSelect, searchKeyword, page - 1, pageSize) // 벡엔드 전체조회요청
.then((response: any) => {
const { qna, totalPages } = response.data;
setQna(qna);
setCount(totalPages);
// 로그 출력
console.log("response", response.data);
})
.catch((e: Error) => {
console.log(e);
});
};
// select 태그 수동바인딩
const onChangeSearchSelect = (e: any) => {
setSearchSelect(e.target.value); // 화면값 -> 변수저장
};
// input 태그 수동바인딩
const onChangeSearchKeyword = (e: any) => {
setSearchKeyword(e.target.value); // 화면값 -> 변수저장
};
// todo: 공통 페이징 함수 2개
// todo: handlePageSizeChange(공통) : pageSize 값 변경시 실행되는 함수
// select 태그 수동 바인딩 : 화면값 -> 변수에 저장
const handlePageSizeChange = (event: any) => {
setPageSize(event.target.value); // 1페이지당 개수저장(3,6,9)
setPage(1); // 현재페이지번호 : 1로 강제설정
};
// todo: Pagination 수동 바인딩(공통)
// 페이지 번호를 누르면 => page 변수에 값 저장
const handlePageChange = (event: any, value: number) => {
// value == 화면의 페이지번호
setPage(value);
};
return (
// 여기
<>
{/* 제목 start */}
<TitleCom title="Qna List" />
{/* 제목 end */}
{/* question start(다양한 검색어 부분) */}
<div className="row mb-5 justify-content-center">
<div className="col-md-8">
<div className="input-group mb-3">
{/* 다양한 검색(select : question,questioner) 시작 */}
<div className="col-2">
<select
className="form-select"
onChange={onChangeSearchSelect}
value={searchSelect}
>
<option key="question" value="question">
question
</option>
<option key="questioner" value="questioner">
questioner
</option>
</select>
</div>
{/* 다양한 검색(select) 끝 */}
{/* 검색어(searchKeyword) 입력창(input) 시작 */}
<div className="col-9 w-50 input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by Question"
value={searchKeyword}
onChange={onChangeSearchKeyword}
/>
</div>
{/* 검색어 입력창 끝 */}
{/* 검색버튼 시작 */}
<div className="input-group-append col-md-1">
<button
className="btn btn-outline-secondary"
type="button"
onClick={retrieveQna}
>
Search
</button>
</div>
{/* 검색버튼 끝 */}
</div>
</div>
</div>
{/* question end */}
<div className="col-md-12">
{/* page control start(페이징 html) */}
<div className="mt-3">
{"Items per Page: "}
<select onChange={handlePageSizeChange} value={pageSize}>
{pageSizes.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
<Pagination
className="my-3"
count={count}
page={page}
siblingCount={1}
boundaryCount={1}
variant="outlined"
shape="rounded"
onChange={handlePageChange}
/>
</div>
{/* page control end */}
{/* table start(본문) */}
<table className="table">
<thead>
<tr>
<th scope="col">Question</th>
<th scope="col">Questioner</th>
<th scope="col">Answer</th>
<th scope="col">Answerer</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{qna &&
qna.map((data) => (
// 키값 추가 않하면 react 에서 경고를 추가 : 키는 내부적으로 리액트가 rerending 할때 체크하는 값임
<tr key={data.question}>
<td>{data.question}</td>
<td>{data.questioner}</td>
<td>{data.answer}</td>
<td>{data.answerer}</td>
<td>
{/* TODO: 상세조회 버튼 onClick 매개변수 props로 수정*/}
{/* <Link to={"/qna/" + data.qno}> */}
<Link to="#">
<span className="badge bg-success"
onClick={()=>props.handleChangeQna("qna", data.qno)}
>Edit</span>
</Link>
</td>
</tr>
))}
</tbody>
</table>
{/* table end */}
</div>
</>
);
}
export default QnaList;
더보기
// C:\Work\08_Pilot_Project\01_Blog\01_JPA\02_normal_thema\01_proman\frontend-typescript\src\pages\basic\qna\Qna.tsx
import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import IQna from "../../../types/basic/IQna";
import QnaService from "../../../service/basic/QnaService";
import TitleCom from "../../../components/common/TitleCom";
function Qna(props: any) {
// 변수 정의
// 전체조회 페이지에서 전송한 기본키(qno)
// const { qno } = useParams();
const qno = props.qno;
// 강제페이지 이동 함수
let navigate = useNavigate();
// 객체 초기화(상세조회 : 기본키 있음)
const initialQna = {
qno: "",
question: "",
answer: "",
questioner: "",
answerer: "",
};
// 수정 될 객체
const [qna, setQna] = useState<IQna>(initialQna);
// 화면에 수정 성공에 메세지 찍기 변수
const [message, setMessage] = useState<string>("");
// todo: 함수 정의
// 상세조회 함수
const getQna = (qno: string) => {
QnaService.get(qno) // 벡엔드로 상세조회 요청
.then((response: any) => {
setQna(response.data);
console.log(response.data);
})
.catch((e: Error) => {
console.log(e);
});
};
// 화면이 뜰때 실행되는 이벤트 + qno 값이 바뀌면 실행
useEffect(() => {
if (qno) getQna(qno);
}, [qno]);
// input 태그 수동 바인딩
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setQna({ ...qna, [name]: value });
};
// 수정 함수
const updateQna = () => {
QnaService.update(qna.qno, qna) // 벡엔드로 수정요청
.then((response: any) => {
console.log(response.data);
setMessage("qna 객체가 수정되었습니다.");
})
.catch((e: Error) => {
console.log(e);
});
};
// 삭제 함수
const deleteQna = () => {
QnaService.remove(qna.qno) // 벡엔드로 삭제요청
.then((response: any) => {
console.log(response.data);
// 강제 페이지 이동 : 전체조회
// navigate("/qna");
setMessage("삭제되었습니다.");
})
.catch((e: Error) => {
console.log(e);
});
};
return (
// 여기
<>
{/* 제목 start */}
<TitleCom title="Qna Detail" />
{/* 제목 end */}
<>
{qna ? (
<div className="col-6 mx-auto">
<form>
{/* question 입력창 시작 */}
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="question" className="col-form-label">
Question
</label>
</div>
<div className="col-9">
<input
type="text"
id="question"
required
className="form-control"
value={qna.question}
onChange={handleInputChange}
placeholder="question"
name="question"
/>
</div>
</div>
{/* question 입력창 끝 */}
{/* answer 입력창 시작 */}
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="answer" className="col-form-label">
Answer
</label>
</div>
<div className="col-9">
<input
type="text"
id="answer"
required
className="form-control"
value={qna.answer}
onChange={handleInputChange}
placeholder="answer"
name="answer"
/>
</div>
</div>
{/* answer 입력창 끝 */}
{/* questioner 입력창 시작 */}
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="questioner" className="col-form-label">
Questioner
</label>
</div>
<div className="col-9">
<input
type="text"
id="questioner"
required
className="form-control"
value={qna.questioner}
onChange={handleInputChange}
placeholder="questioner"
name="questioner"
/>
</div>
</div>
{/* questioner 입력창 끝 */}
{/* answerer 입력창 시작 */}
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="answerer" className="col-form-label">
Answerer
</label>
</div>
<div className="col-9">
<input
type="text"
id="answerer"
required
className="form-control"
value={qna.answerer}
onChange={handleInputChange}
placeholder="answerer"
name="answerer"
/>
</div>
</div>
{/* answerer 입력창 끝 */}
</form>
<div className="row g-3 mt-3 mb-3">
<button
onClick={deleteQna}
className="btn btn-outline-danger ms-3 col"
>
Delete
</button>
<button
type="submit"
onClick={updateQna}
className="btn btn-outline-success ms-2 col"
>
Update
</button>
</div>
{message && (
<p className="alert alert-success mt-3 text-center">{message}</p>
)}
</div>
) : (
<div className="col-6 mx-auto">
<br />
<p>Please click on a Qna...</p>
</div>
)}
</>
</>
);
}
export default Qna;
'Spring Boot' 카테고리의 다른 글
Blog Thema(블로그 테마) 기본 환경 설치 (0) | 2023.11.03 |
---|---|
01_tour_Blog Thema(블로그 테마) 수정 후 & BackEnd(백앤드), 여러 페이지 연동 (0) | 2023.11.02 |
01_proman_Blog Thema(블로그 테마) 수정 후 & BackEnd(백앤드) 연동 (0) | 2023.11.01 |
컴포넌트 간에 통신(Props 사용)을 통해 상세 조회 및 삭제 기능 (0) | 2023.11.01 |
Blog Thema(블로그 테마) & BackEnd(백앤드) 연동 (0) | 2023.11.01 |