- 카테고리 뉴스 검색
뉴스 카테고리는 비즈니스(business), 연예(Entertainment), 건강(health), 과학(Science), 스포츠(Sports), 기술(technology) 총 여 섯 개이며 화면에 보여 줄 때는 영어로 된 값을 그대로 보여 주지 않고 아래 그림처럼 한글로 보여 준 뒤 클릭했을 때는 영어 값을 사용한다.
- C:\data\react\ex05\src\component\(new)Categories.js
import React from 'react';
import './Categories.css'
const categories=[
{name : 'all', text:'전체보기'},
{name : 'business', text:'비즈니스'},
{name : 'entertainment', text:'엔터테인먼트'},
{name : 'health', text:'건강'},
{name : 'science', text:'과학'},
{name : 'sports', text:'스포츠'},
{name : 'technology', text:'기술'}
];
const Categories = ({category, onSelect}) => {
return (
<div className="categoriesBlock">
{categories.map(c=>(
<div className="category" onClick={()=>onSelect(c.name)}>
<span className={category===c.name ? 'active':''}>{c.text}</span>
</div>
))}
</div>
);
};
export default Categories;
- C:\data\react\ex05\src\component\(new)Categories.css
.categoriesBlock {
display: flex;
width: 768px;
margin: 0px auto;
padding: 1rem;
@media screen and (max-width: 768px) {
width: 100%;
overflow: auto;
}
}
.category {
font-size: 1.125rem;
cursor: pointer;
text-decoration: none;
color: inherit;
padding-bottom: 0.25rem;
margin-left: 1rem;
}
.category:hover {
color: #b0b5bb;
}
.category .active {
color: #22b8cf;
border-bottom: 2px solid #22b8cf;
font-weight: 600;
}
.category .active:hover {
color: #abdfe6;
}
- C:\data\react\ex05\src\App.js
import './App.css';
import axios from 'axios';
import {useCallback, useState} from "react"
import NewsList from './component/NewsList';
import Categories from './component/Categories';
const App = () => {
const [data,setData] = useState(null);
const onClick = async() => {
const res = await axios.get('https://newsapi.org/v2/top-headlines?country=kr&apiKey=0d2cfc3452514a4f93903b35e762e40d')
setData(res.data);
}
const [category, setCategory] = useState('all');
const onSelect = useCallback(category => setCategory(category),[]);
return (
<div className="App">
<Categories category={category} onSelect={onSelect}/>
<NewsList category={category}/>
</div>
);
}
export default App;
- 이제 각 카테고리 클릭 시 각 카테고리에 맞는 뉴스가 나오도록 하겠다.
- C:\data\react\ex05\src\component\NewsList.js
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import NewsItem from './NewsItem';
import './NewsList.css'
const NewsList = ({category}) => {
const [articles, setArticles] = useState(null);
const [loading,setLoading] = useState(false);
const callAPI = async() => {
setLoading(true);
try {
const url = "https://newsapi.org/v2/top-headlines?country=kr";
const query = category === 'all' ? '':'&category='+category;
const apiKey = '&apiKey=0d2cfc3452514a4f93903b35e762e40d';
const res = await axios.get(url+query+apiKey);
console.log(query);
setArticles(res.data.articles);
} catch (e) {
console.log(e);
setLoading(false);
}
setLoading(false);
}
useEffect(()=>{
callAPI();
},[category]);
if(loading){
return <div className="newsListBlock">불러오는중...</div>
}
if(!articles){
return null;
}
return (
<div className="newsListBlock">
{articles ? articles.map(acticle => <NewsItem key={acticle.url} article={acticle}/>) : ''}
</div>
);
};
export default NewsList;
- 카카오 검색 API를 이용해보도록 하겠다.
https://developers.kakao.com/docs/latest/ko/daum-search/dev-guide#search-book
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
- C:\data\react\ex05\src\component\(new)Books.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const Books = () => {
// 변수, 변수세팅함수
const [books,setBooks] = useState([]);
const [loading, setLoading] = useState(false);
const callAPI = async() => {
setLoading(true);
try {
const url = 'https://dapi.kakao.com/v3/search/book?target=title&query=리액트';
const config = {
headers:{"Authorization": "KakaoAK 31347cda85f0d6ed06e3025a531f2aca"}
}
// axios 라이브러리를 이용해 url, config을 통해 비동기 데이터를 가지고 오도록 한다.
const res = await axios.get(url,config);
setBooks(res.data.documents);
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
}
}
useEffect(()=>{
callAPI();
},[]);
if(loading) return <div>Loading...</div>
return (
<div>
{/* object를 json 형태로 변환해준다. */}
<textarea rows={20} value={JSON.stringify(books,null,2)} readOnly />
</div>
);
};
export default Books;
- C:\data\react\ex05\src\App.js
import './App.css';
import Books from './component/Books';
const App = () => {
return (
<div className="App">
<Books/>
</div>
);
}
export default App;
- JSON.stringify( )란 무엇인가?
https://steemit.com/kr-dev/@cheonmr/json-stringify
JSON.stringify( )란 무엇인가? — Steemit
JSON.stringify( )는 자바스크립트의 값을 JSON 문자열로 변환한다. JSON이란? JSON은 JavaScript Object Notation의 약자로, 브라우저와 서버사이에서 오고가는 데이터의 형식이다. JSON.stringify(value, replacer, space)
steemit.com
- 이제 데이터를 출력해보도록 하겠다.
- C:\data\react\ex05\src\component\(new)BookItem.js
import React from 'react';
const BookItem = ({book}) => {
const {title, thumbnail} = book;
return (
<div>
<div>
<img src={thumbnail} alt="thumbnail"/>
<div>{title}</div>
</div>
</div>
);
};
export default BookItem;
- C:\data\react\ex05\src\component\Books.js
return (
<div className="books">
{books.map(book=><BookItem key={book.isbn} book={book}/>)}
</div>
);
- C:\data\react\ex05\src\component\BookItem.js
import React from 'react';
const BookItem = ({book}) => {
const {title, thumbnail, authors, price, contents} = book;
return (
<div>
<div className="book">
<img src={thumbnail} alt="thumbnail"/>
<div className="data">
<div>{title}</div>
<div>{authors.map(author=><span>{author}</span>)}</div>
<div>{price}</div>
</div>
<div className="data">{contents}</div>
</div>
</div>
);
};
export default BookItem;
- CSS를 줘보자.
- C:\data\react\ex05\src\App.css
.App{
width: 800px;
margin: 0px auto;
}
.books {
border: 1px solid gray;
padding: 10px;
}
.book{
border: 1px solid gray;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.book img{
width:200px;
float: left;
padding: 10px;
box-shadow: 5px 5px 5px gray;
}
.data:nth-child(2){
width: 500px;
float: left;
margin: 10px;
}
.data div:nth-child(1){
font-size: 30px;
margin-bottom: 30px;
font-weight: bold;
}
.data div:nth-child(2){
font-size: 20px;
font-weight: bold;
}
.data div:nth-child(2):before{
content: "- 저자 :";
}
.data div:nth-child(3){
font-size: 20px;
color: red;
}
.data div:nth-child(3):before{
content: "- 가격 :";
}
.data div:nth-child(3)::after{
content: "원";
}
.data:nth-child(3){
color: rgb(26, 26, 26);
padding: 10px;
float: left;
margin: 10px;
}
.data:nth-child(3):before{
content:"- 책소개 : ";
font-weight: bold;
}
- 검색어를 통해서 책 검색 결과를 출력해보자.
- C:\data\react\ex05\src\component\Books.js
...
const Books = () => {
...
const [query, setQuery] = useState('안드로이드');
const callAPI = async() => {
setLoading(true);
try {
const url = `https://dapi.kakao.com/v3/search/book?target=title&query=${query}`;
...
} catch (error) {
console.log(error);
setLoading(false);
}
}
...
if(loading) return <div>Loading...</div>
const onKeyPress = (e) =>{
if(e.key==='Enter'){
callAPI();
}
}
return (
<div className="books">
<div className="condition">
<input placeholder="검색어입력" value={query}
onChange={(e)=>setQuery(e.target.value)}
onKeyPress={onKeyPress}/>
</div>
{books.map(book=><BookItem key={book.isbn} book={book}/>)}
</div>
);
};
export default Books;
- 더보기 버튼을 만들어보도록 하겠다.
- 포커스
- 신규 검색 시 page=1
- 더보기 버튼 클릭 시 추가
- C:\data\react\ex05\src\component\Books.js
import React, { useEffect, useRef, useState } from 'react';
import axios from 'axios';
import BookItem from './BookItem';
const Books = () => {
// 변수, 변수세팅함수
const [books,setBooks] = useState([]);
const [loading, setLoading] = useState(false);
const [query, setQuery] = useState('안드로이드');
const [page, setPage] = useState(1);
const txtQuery = useRef();
const callAPI = async() => {
setLoading(true);
try {
const url = `https://dapi.kakao.com/v3/search/book?target=title&query=${query}&page=${page}&size=3`;
const config = {
headers:{"Authorization": "KakaoAK 31347cda85f0d6ed06e3025a531f2aca"}
}
// axios 라이브러리를 이용해 url, config을 통해 비동기 데이터를 가지고 오도록 한다.
const res = await axios.get(url,config);
if(page===1){
setBooks(res.data.documents)
}else{
setBooks(books.concat(res.data.documents));
}
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
}
txtQuery.current.focus();
}
useEffect(()=>{
callAPI();
},[page]);
if(loading) return <div>Loading...</div>
const onKeyPress = (e) =>{
if(e.key==='Enter'){
setPage(1)
callAPI();
}
}
const onMore = () => {
setPage(page+1);
}
return (
<div className="books">
<div className="condition">
<input placeholder="검색어입력" value={query}
onChange={(e)=>setQuery(e.target.value)}
onKeyPress={onKeyPress} ref={txtQuery}/>
</div>
{books.map(book=><BookItem key={book.isbn} book={book}/>)}
<div className="more" onClick={onMore}>
더보기
</div>
</div>
);
};
export default Books;
- 리엑트 라우터를 이용해보겠다.
// 서버 종료
ctrl + `
// 새로운 프로젝트 생성
C:\data\react\>yarn create react-app ex05
// Router 라이브러리 설치
C:\data\react\ex05>yarn add react-router-dom
// 프로젝트 스타트
yarn start
- C:\data\react\ex05\src\App.js
import './App.css';
import { NavLink, Route, Switch } from 'react-router-dom'
import Books from './component/Books';
const style={
color:'rgb(0, 153, 153)',
borderBottom:'5px solid green'
};
const App = () => {
return (
<div className="App">
<div className="menu">
<span><NavLink activeStyle={style} to="/" exact={true}>도서검색</NavLink></span>
<span><NavLink activeStyle={style} to="/web">웹문서검색</NavLink></span>
<span><NavLink activeStyle={style} to="/blog">블로그검색</NavLink></span>
<span><NavLink activeStyle={style} to="/cafe">카페검색</NavLink></span>
<span><NavLink activeStyle={style} to="/image">이미지검색</NavLink></span>
</div>
<Switch>
<Route path="/" component={Books} exact={true}/>
<Route path="/web" component={Books}/>
<Route path="/blog" component={Books}/>
<Route path="/cafe" component={Books}/>
<Route path="/image" component={Books}/>
<Route render=
{({location})=>(
<div>{location.pathname} 페이지가 존재하지 않습니다.</div>
)}/>
</Switch>
</div>
);
}
export default App;
- C:\data\react\ex05\src\component\(new)Web.js
import axios from 'axios';
import React, { useEffect, useRef, useState } from 'react';
import WebItem from './WebItem';
const Web = () => {
// 변수, 변수세팅함수
const [webs,setWebs] = useState([]);
const [loading, setLoading] = useState(false);
const [query, setQuery] = useState('과학');
const [page, setPage] = useState(1);
const txtQuery = useRef();
const callAPI = async() => {
setLoading(true);
try {
const url = `https://dapi.kakao.com/v2/search/web?target=title&query=${query}&page=${page}&size=3`;
const config = {
headers:{"Authorization": "KakaoAK 31347cda85f0d6ed06e3025a531f2aca"}
}
// axios 라이브러리를 이용해 url, config을 통해 비동기 데이터를 가지고 오도록 한다.
const res = await axios.get(url,config);
if(page===1){
setWebs(res.data.documents)
}else{
setWebs(webs.concat(res.data.documents));
}
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
}
txtQuery.current.focus();
}
useEffect(()=>{
callAPI();
},[page]);
if(loading) return <div>Loading...</div>
const onKeyPress = (e) =>{
if(e.key==='Enter'){
setPage(1)
callAPI();
}
}
const onMore = () => {
setPage(page+1);
}
return (
<div className="webs">
<div className="condition">
<input placeholder="검색어입력" value={query}
onChange={(e)=>setQuery(e.target.value)}
onKeyPress={onKeyPress} ref={txtQuery}/>
</div>
{webs.map(web=><WebItem key={web.datetime} web={web}/>)}
<div className="more" onClick={onMore}>
더보기
</div>
</div>
);
};
export default Web;
- C:\data\react\ex05\src\component\(new)WebItem.js
import React from 'react';
const WebItem = ({web}) => {
const {title, datetime, contents, url} = web;
return (
<div className="web">
<div className="webdata">
<div dangerouslySetInnerHTML={ {__html: title} } onclick={`location.href=${url}`}></div>
<div >{datetime}</div>
</div>
<div className="webdata" dangerouslySetInnerHTML={ {__html: contents} }></div>
</div>
);
};
export default WebItem;
↑확인요망
- 프론트 작업 및 백앤드 작업으로 DB 데이터를 CURD 작업해보도록 하겠다.
- 새로운 프로젝트를 생성하자.
- C:\data\react\(new)haksa
- C:\data\react\haksa\(new)client - > 기존에 haksa에 있는 파일들을 전부 client에 넣는다.
- C:\data\react\haksa\(new)package.json
{
"name": "management",
"version": "1.0.0",
"private": true,
"scripts": {
"client": "cd client && yarn start",
"server": "nodemon server.js",
"dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\""
}
}
// 노드몬 설치
npm install -g nodemon
// 폴더에서 웹서버를 구축하기 위한 express와 서버와 클라이언트를 동시에 실행하기 위한
// concurrently를 아래와 같이 설치
npm install express concurrently
// mysql 연결 할 수 있는 라이브러리 설치
yarn add mysql
- 설치 후 확인
- C:\data\react\haksa\package.json
{
"name": "management",
"version": "1.0.0",
"private": true,
"scripts": {
"client": "cd client && yarn start",
"server": "nodemon server.js",
"dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\""
},
"dependencies": {
"concurrently": "^6.2.1",
"express": "^4.17.1"
}
}
- C:\data\react\haksa\(new)server.js
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
app.use(express.json());
app.listen(port, () => {
console.log(`server is listening at localhost:${port}`);
});
app.get('/', (req, res) => {
res.send({
success: true
})
});
- 서버 및 클라이언트 페이지 구동
yarn dev
- C:\data\react\haksa\server.js
...
// mysql 연결
const mysql = require('mysql');
const con = mysql.createConnection({
host: "localhost",
user: "haksa",
password: "pass",
port: "3306",
database: "haksadb"
});
...
// 1. 교수목록 가지고 오기
app.get('/pro/list',(req,res)=>{
var sql = "select * from professors";
con.query(sql,(err,rows)=>{
res.send(rows);
})
})
- 프론트에 필요한 라이브러리를 깔아보자.
// 라우터 라이브러리 설치
C:\data\react\haksa\client>yarn add react-router-dom
// 자바스크립트 HTTP 클라이언트 설치
C:\data\react\haksa\client>yarn add axios
- C:\data\react\haksa\client\package.json
{
....
},
"proxy": "http://localhost:5000/" // 추가
}
- 리액트에서 할당한 주소로 데이터를 출력해보자.
- C:\data\react\haksa\client\src\(new)component
- C:\data\react\haksa\client\src\component\(new)ProfessorList.js
import React, { useEffect, useState } from 'react';
import axios from 'axios'
const ProfessorList = () => {
const [list, setList] = useState([]);
const callAPI = async() => {
try {
// 주소
const res = await axios.get('/pro/list');
setList(res.data);
} catch (error) {
console.log(error);
}
}
useEffect(()=>{
callAPI();
},[]);
return (
<div>
<textarea rows={10} value={JSON.stringify(list, null, 2)}/>
</div>
);
};
export default ProfessorList;
- C:\data\react\haksa\client\src\App.js
import './App.css';
import ProfessorList from './component/ProfessorList';
function App() {
return (
<div className="App">
<ProfessorList/>
</div>
);
}
export default App;
- C:\data\react\haksa\client\src\component\(new)ProfessorItem.js
import React from 'react';
const ProfessorItem = ({professor}) => {
const {pcode, pname, dept, hiredate, salary,title} = professor
return (
<tr>
<td>{pcode}</td>
<td>{pname}</td>
<td>{dept}</td>
<td>{hiredate}</td>
<td>{salary}</td>
<td>{title}</td>
</tr>
);
};
export default ProfessorItem;
- C:\data\react\haksa\client\src\component\ProfessorList.js
import React, { useEffect, useState } from 'react';
import axios from 'axios'
import ProfessorItem from './ProfessorItem';
const ProfessorList = () => {
const [list, setList] = useState([]);
const callAPI = async() => {
try {
// 주소
const res = await axios.get('/pro/list');
setList(res.data);
} catch (error) {
console.log(error);
}
}
useEffect(()=>{
callAPI();
},[]);
return (
<div>
<table>
{list ? list.map(item=><ProfessorItem key={item.pcode} professor={item}/>):''}
</table>
</div>
);
};
export default ProfessorList;
- 이제 format 과 css를 적용해보자.
- C:\data\react\haksa\client\src\App.css
.App {
width: 800px;
text-align: center;
margin: 0px auto;
}
table {
border-collapse: collapse;
margin: 0px auto;
margin-top: 10px;
margin-bottom: 10px;
}
th {
background: rgb(242, 242, 242);
color: black;
font-size: 30px;
}
th, td {
border: 1px solid black;
padding: 10px;
}
.title{
background-color: rgb(242, 242, 242);
color: black;
font-weight: bold;
}
- C:\data\react\haksa\server.js
// 1. 교수목록 가지고 오기
app.get('/pro/list',(req,res)=>{
var sql = 'select *, date_format(hiredate,"%Y-%m-%d") fhiredate, format(salary,0) fsalary from professors order by pcode desc';
con.query(sql,(err,rows)=>{
res.send(rows);
})
})
- C:\data\react\haksa\client\src\component\ProfessorList.js
import React, { useEffect, useState } from 'react';
import axios from 'axios'
import ProfessorItem from './ProfessorItem';
const ProfessorList = () => {
const [list, setList] = useState([]);
const callAPI = async() => {
try {
// 주소
const res = await axios.get('/pro/list');
setList(res.data);
} catch (error) {
console.log(error);
}
}
useEffect(()=>{
callAPI();
},[]);
return (
<div>
<table>
<th colSpan={6}>교수목록</th>
<tr>
<td className="title" width="100">교수번호</td>
<td className="title" width="100">교수이름</td>
<td className="title" width="100">교수직함</td>
<td className="title" width="100">담당학과</td>
<td className="title" width="200">임용일자</td>
<td className="title" width="100">교수급여</td>
</tr>
{list ? list.map(item=><ProfessorItem key={item.pcode} professor={item}/>):''}
</table>
</div>
);
};
export default ProfessorList;
- C:\data\react\haksa\client\src\component\ProfessorItem.js
import React from 'react';
const ProfessorItem = ({professor}) => {
const {pcode, pname, dept, hiredate, fhiredate, salary,title, fsalary} = professor
return (
<tr>
<td>{pcode}</td>
<td>{pname}</td>
<td>{title}</td>
<td>{dept}</td>
<td>{fhiredate}</td>
<td>{fsalary}</td>
</tr>
);
};
export default ProfessorItem;
- C:\data\react\haksa\client\src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
- C:\data\react\haksa\client\src\App.js
import { NavLink, Route } from 'react-router-dom';
import './App.css';
import About from './component/About';
import ProfessorList from './component/ProfessorList';
const style={
color:'blue'
}
function App() {
return (
<div className="App">
<div className="menu">
<span><NavLink activeStyle={style} to="/" exact={true}>소개</NavLink></span>
<span><NavLink activeStyle={style} to="/professors">교수목록</NavLink></span>
<span><NavLink activeStyle={style} to="/students">학생관리</NavLink></span>
<span><NavLink activeStyle={style} to="/courses">수강관리</NavLink></span>
</div>
<Route path="/" component={About} exact={true}/>
<Route path="/professors" component={ProfessorList}/>
</div>
);
}
export default App;
- C:\data\react\haksa\client\src\component\(new)About.js
import React from 'react';
const About = () => {
return (
<div>
<h1>학사관리 시스템 소개</h1>
<p></p>
</div>
);
};
export default About;
- 학생목록을 출력해보도록 하겠다.
- C:\data\react\haksa\server.js
// 2. 학생목록 가지고 오기
app.get('/stu/list',(req,res)=>{
var sql = 'select *,date_format(birthday,"%Y-%m-%d") fbirthday from view_stu order by scode';
con.query(sql,(err,rows)=>{
res.send(rows);
})
})
- C:\data\react\haksa\client\src\App.js
import { NavLink, Route } from 'react-router-dom';
import './App.css';
import About from './component/About';
import ProfessorList from './component/ProfessorList';
import StudentList from './component/StudentList';
const style={
color:'blue',
}
function App() {
return (
<div className="App">
<div className="menu">
<span><NavLink activeStyle={style} to="/" exact={true}>시스템소개</NavLink></span>
<span><NavLink activeStyle={style} to="/professors">교수관리</NavLink></span>
<span><NavLink activeStyle={style} to="/students">학생관리</NavLink></span>
<span><NavLink activeStyle={style} to="/courses">수강관리</NavLink></span>
</div>
<Route path="/" component={About} exact={true}/>
<Route path="/professors" component={ProfessorList}/>
<Route path="/students" component={StudentList}/>
</div>
);
}
export default App;
- C:\data\react\haksa\client\src\component\(new)StudentList.js
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import StudentItem from './StudentItem';
const StudentList = () => {
const [list, setList] = useState([]);
const callAPI = async() => {
try {
// 주소
const res = await axios.get('/stu/list');
setList(res.data);
} catch (error) {
console.log(error);
}
}
useEffect(()=>{
callAPI();
},[]);
return (
<div>
<table>
<th colSpan={6}>학생목록</th>
<tr>
<td className="title" width="100">학생번호</td>
<td className="title" width="100">학생이름</td>
<td className="title" width="100">학과</td>
<td className="title" width="50">학년</td>
<td className="title" width="200">생년월일</td>
<td className="title" width="150">담당교수/학과</td>
</tr>
{list ? list.map(item=><StudentItem key={item.pcode} student={item}/>):''}
</table>
</div>
);
};
export default StudentList;
- C:\data\react\haksa\client\src\component\(new)StudentItem.js
import React from 'react';
const StudentItem = ({student}) => {
const {scode, sname, dept, year, fbirthday, birthday,pname, pdept} = student
return (
<tr>
<td>{scode}</td>
<td>{sname}</td>
<td>{dept}</td>
<td>{year}</td>
<td>{fbirthday}</td>
<td>{pname}/{pdept}</td>
</tr>
);
};
export default StudentItem;
'ICIA 수업일지' 카테고리의 다른 글
2021.09.17 수업일지(React.js을 이용한 블로그 개설) (0) | 2021.09.17 |
---|---|
2021.09.16 수업일지(React.js) (0) | 2021.09.16 |
2021.09.14 수업일지(React.js) (0) | 2021.09.14 |
2021.09.13 수업일지(React.js) (0) | 2021.09.13 |
2021.09.10 수업일지(React.js) (0) | 2021.09.10 |