폴더 생성
npx create-react-app builtin-functions --template typescript
json-server라이브러리 사용하기
json 파일을 사용하여 간단한 시뮬레이션능 위한 REST API Mock server 구축할 수 있는 툴입니다
설치
npm install -g json-server
구동하기
터미널 - cmd 설정
json-server --watch data.json파일 경로 --port 3003
data.json 파일은 만들거임
실습
db폴더 생성
data.json
{
"subjects": [
{
"id": 1,
"title": "javascript"
},
{
"id": 2,
"title": "php"
},
{
"id": 3,
"title": "react"
},
{
"title": "mysql",
"id": 4
}
],
"functions": [
{
"id": 1,
"subjects": 1,
"name": "map()",
"type": "array",
"syntex": "배열변수.map(li=>li+1)",
"desc": "배열요소의 형태를 변경하여 새로운 배열로 리턴"
},
{
"id": 2,
"subjects": 1,
"name": "filter()",
"type": "array",
"syntex": "배열변수.filter(li=>li>1)",
"desc": "배열요소중 조건에 맞는 요소만 새로운 배열로 리턴"
},
{
"id": 3,
"subjects": 1,
"name": "replace()",
"type": "string",
"syntex": "배열변수.filter(li=>li>1)",
"desc": "배열요소중 조건에 맞는 요소만 새로운 배열로 리턴"
},
{
"id": 4,
"subjects": 1,
"name": "strlen()",
"type": "string",
"syntex": "strlen(문자열)",
"desc": "문자열 길이 리턴"
},
{
"id": 5,
"subjects": 2,
"name": "var_dump()",
"type": "모든타입",
"syntex": "var_dump(변수)",
"desc": "데이터의 값과 타입을 반환"
},
{
"id": 6,
"subjects": 2,
"name": "strtoupper()",
"type": "string",
"syntex": "strtoupper(변수)",
"desc": "대문자로 변경후 반환"
},
{
"id": 7,
"subjects": 3,
"name": "useState()",
"type": "훅함수",
"syntex": "const [state, setState] = useState",
"desc": "상태관리"
}
]
}
생성 후
터미널 - cmd 창에 입력 (서버 실행)
json-server --watch ./src/db/data.json --port 3003
서버 주소 2개가 보일거임(주소창에 입력해서 확인하자)
Resources
http://localhost:3003/subjects
http://localhost:3003/functions
이렇게 잘나온다면 잘 실행된다는거임!!!!
모듈 만들러 가장!!
customhook 폴더 생성
useAcync.ts
import { useEffect, useReducer } from "react";
//상태를 위한 타입 (db datajson 타입설정함)
export type SubjectdataType = {
id: number;
title: string;
}
export type ListdataType = { //(db datajson 타입설정)
"id":number,
"subjects": number,
"name": string,
"type": string,
"syntex": string,
"desc": string
}
//상태타입
export type funState = {
loading: boolean;
data: null | SubjectdataType[] | ListdataType[];
error: null | object;
}
//모든 액션을 위한 타입
type funAction = {type: "LOADING"}
| {type: 'SUCCESS'; data: SubjectdataType[] | ListdataType []}
| {type: 'ERROR'; error: any}
function reducer(state: funState, action:funAction){
switch(action.type){
case 'LOADING':
return {
loading: true,
data: null,
error: null
};
case 'SUCCESS':
return {
loading: false,
data: action.data,
error: null
};
case 'ERROR':
return {
loading: false,
data: null,
error: action.error
};
default:
return state
}
}
type fetch =()=> void;
const useAsync = (callback: any, deps=[]): [funState, fetch] => {
const [state, dispatch] = useReducer(reducer, {
loading: false,
data: null,
error: null
})
const fetchData = async () => {
dispatch({type: 'LOADING'});
try{
const data = await callback();
dispatch({type: "SUCCESS", data})
}
catch(e){
dispatch({type: 'ERROR', error: e})
}
}
useEffect(()=>{
fetchData();
}, deps)
return [state, fetchData]; //useAsync 사용하면 상태와 패체데이타를 리턴해줌
}
export default useAsync;
화면을 구현해줄 컴포넌트 만들자
그 전에 styled-components를 사용하려면 라이브러리 설치해야한다
터미널-cmd 서버 유지상태로 터미널 하나 더 생성하고 라이브러리 설치
타입스크립트에 지원이 안되서 @types/ 붙이고 설치해야한다
npm install @types/styled-components
components폴더 생성
Header.tsx
import React from 'react';
import styled from 'styled-components'
const HeaderWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 30px;
`;
const HeaderUl = styled.ul`
display: flex;
align-items: center;
li{
list-style:none;
padding: 0 20px;
}
`
const Header = () => {
return (
<HeaderWrapper>
<h1></h1>
<HeaderUl>
<li>과목등록</li>
<li>내장함수등록</li>
<li>함수리스트</li>
</HeaderUl>
</HeaderWrapper>
);
};
export default Header;
src 폴더
App.tsx
Header 조립
import React, { useState } from 'react';
import './App.css';
import Header from './components/Header';
function App() {
return (
<div className="App">
<Header sitename="typescript"/>
</div>
);
}
export default App;
Header 에 sitename을 props으로 보냈음
Header.tsx
//props로 받아서 타입지정해줘야함
type HeaderProps = {
sitename: string;
}
const Header = ({sitename}: HeaderProps) => {
return (
<HeaderWrapper>
<h1>{sitename}</h1>
<HeaderUl>
<li>과목등록</li>
<li>내장함수등록</li>
<li >함수리스트</li>
</HeaderUl>
</HeaderWrapper>
);
};
http://localhost:3003/subjects 주소에 있는 data를 화면에 구현하자
components 폴더
Subjects.tsx
import React from 'react';
import styled from 'styled-components'
import useAsync, { SubjectdataType } from '../customhook/useAcync';
async function getData() {
try {
const response = await fetch(`http://localhost:3003/subjects`)
const data = await response.json(); //요청받고 제이슨데이터로 변환
return data;
}
catch(e){
return e;
}
}
const SubWrapper = styled.div`
display: flex;
justify-content: center;
div{
margin: 10px;
background: skyblue;
color: white;
padding: 10px 20px;
border-radius: 6px;
}
`
const Subjects = () => {
const [subject, fetchData] = useAsync(getData) //모듈로 보냄
const {loading, data, error} = subject;
if(loading) return <div>로딩중입니다.</div>
if(error) return <div>에러발생!!!</div>
if(!data) return <div>데이터 없음!!</div>
return (
<SubWrapper>
{(data as Array<SubjectdataType>).map(da=>
<div key={da.id}>{da.title}</div>)}
</SubWrapper>
);
};
export default Subjects;
FetchAPI 알아보기
2023.01.24 - [javascript/javascript 고급문법] - [javascript] Fetch API
App에 조립
import React, { useState } from 'react';
import './App.css';
import Header from './components/Header';
import Subjects from './components/Subjects';
function App() {
return (
<div className="App">
<Header sitename="typescript"/>
<Subjects />
</div>
);
}
export default App;
http://localhost:3003/functions주소에 있는 data를 화면에 구현하자
components 폴더
Lists.tsx
import React from 'react';
import styled from 'styled-components';
import useAsync, { ListdataType } from '../customhook/useAcync';
async function getData() {
try {
const response = await fetch('http://localhost:3003/functions')
const data = await response.json() //받아온걸 제이슨데이터로
return data;
}
catch(e){
console.log(e)
}
}
const ListWrapper = styled.div`
text-align: center;
h2{
padding: 50px;
font-size: 28px;
}
table {
width: 100%;
max-width: 1100px;
margin: 0 auto;
border-collapse: collapse;
th{
border-top: 3px solid #333;
border-bottom: 1px solid #ccc;
padding: 20px;
}
td {
padding: 20px;
border-bottom: 1px solid #ccc;
}
}
`
const Lists = () => {
const [funtions, fetchData] = useAsync(getData);
let {data, loading, error} = funtions;
if(loading) return <div>로딩중입니다...</div>
if(error) return <div>에러발생!!!!</div>
if(!data) return <div>데이터 없음</div>
return (
<ListWrapper>
<h2>주요 내장 함수</h2>
<table>
<tbody>
<tr>
<th>번호</th>
<th>함수명</th>
<th>데이터 타입</th>
<th>구문</th>
<th>설명</th>
</tr>
{(data as Array<ListdataType>).map(da=><tr>
<td>{da.id}</td>
<td>{da.name}</td>
<td>{da.type}</td>
<td>{da.syntex}</td>
<td>{da.desc}</td>
</tr>)}
</tbody>
</table>
</ListWrapper>
);
};
export default Lists;
App에 조립
import React, { useState } from 'react';
import './App.css';
import Header from './components/Header';
import Subjects from './components/Subjects';
import Lists from './components/Lists';
function App() {
return (
<div className="App">
<Header sitename="typescript"/>
<Subjects />
<Lists/>
</div>
);
}
export default App;
타이틀을 클릭했을 때 타이틀에 맞는 데이터들만 리스트에 나오게 해보자
subjects에 있는 id값과 subjects 넘버값이 일치해야 보고싶은 타이틀을 클릭했을때 타이틀에 해당하는 리스트가 나와야한다 change 이벤트를 사용하여 타이틀 클릭했을 때 id값 받아오고 그 id값을 list컴퍼넌트로 보내서 일치하는지 조건을 준다
App 에 상태관리와 change이벤트를 작성하고 subjects컨퍼넌트에 onchange을 보내고
상태는 Lists컴퍼넌트로 보낸다
App.tsx
import React, { useState } from 'react';
import './App.css';
import Header from './components/Header';
import Subjects from './components/Subjects';
import Lists from './components/Lists';
function App() {
const [subject, setSubject] = useState(0);
const onChange = (subject: number): void => {
setSubject(subject);
}
return (
<div className="App">
<Header sitename="typescript"/>
<Subjects onChange={onChange}/>
<Lists subject={subject}/>
</div>
);
}
export default App;
Subjects.tsx
//props로 받으면 타입 지정해줘야함 리턴값없으므로 void
type subJectProps = {
onChange: (subject: number) => void;
}
const Subjects = ({onChange}: subJectProps) => {
const [subject, fetchData] = useAsync(getData)
const {loading, data, error} = subject;
if(loading) return <div>로딩중입니다.</div>
if(error) return <div>에러발생!!!</div>
if(!data) return <div>데이터 없음!!</div>
return (
<SubWrapper>
{(data as Array<SubjectdataType>).map(da=>
<div key={da.id} onClick={()=>onChange(da.id)}>{da.title}</div>)}
</SubWrapper>
);
};
Lists.tsx
//props로 받아서 타입지정해줘야함 change이벤트로 받은 number값
type listProps = {
subject:number;
}
const Lists = ({subject}: listProps) => {
const [funtions, fetchData] = useAsync(getData);
let {data, loading, error} = funtions;
if(loading) return <div>로딩중입니다...</div>
if(error) return <div>에러발생!!!!</div>
if(!data) return <div>데이터 없음</div>
if(data) {
if(subject > 0) { //title의 id값과 일치하는지 확인
data = (data as Array<ListdataType>).filter(da => da.subjects === subject);
}
}
근데 타이틀을 클릭하다보니 전체 리스트를 못본다..... 우씨...
전체 리스트를 보려면 router 설치하고 app에 두 컴퍼넌트의 상태값이 일치하면 안되기 때문에 Header에 change이벤트를 보내고 함수리스트라는 목록을 클릭했을 때 두 컴퍼넌트가 가지고 있지 않는 0값으로 상태 업데이트!!! 화면은 router를 사용하여 처음 화면으로 이동하게 설정해준다. 흠... 설명이 이상할 수도
router 설치
타입스크립트에서 지원이 안되서 @types/ 적어줄것
npm install @types/react-router-dom
App.tsx
import React, { useState } from 'react';
import './App.css';
import Header from './components/Header';
import Subjects from './components/Subjects';
import Lists from './components/Lists';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
function App() {
const [subject, setSubject] = useState(0);
const onChange = (subject: number): void => {
setSubject(subject);
}
return (
<BrowserRouter>
<div className="App">
<Header sitename="typescript" onChange={onChange}/>
<Routes>
<Route path="/" element={<>
<Subjects onChange={onChange}/>
<Lists subject={subject}/>
</>}/>
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Header.tsx
//props로 받아서 타입지정해줘야함
type HeaderProps = {
sitename: string;
onChange: (subject:number)=>void;
}
const Header = ({sitename, onChange}: HeaderProps) => {
return (
<HeaderWrapper>
<h1>{sitename}</h1>
<HeaderUl>
<li>과목등록</li>
<li>내장함수등록</li>
<li onClick={()=>onChange(0)}><Link to="/">함수리스트</Link></li>
</HeaderUl>
</HeaderWrapper>
);
};
여기까지 타이틀에 맞게 list 내용 보기 끝!!!!!
다음은 title 추가 해주는 화면을 만들자
components폴더
AddSubject.tsx
import React, {useState} from 'react';
const AddSubject = () => {
const [subject, setSubject] = useState("")
const onChange = (e: React.ChangeEvent<HTMLInputElement>)=> { //e: 타입
setSubject(e.target.value)
}
const submit = () => { //추가버튼 클릭시 서버 보냄
fetch('http://localhost:3003/subjects', {
method: "POST",
headers: {
"Content-type": "application/json",
},
body: JSON.stringify({title: subject})
}).then(res=>{
console.log(res)
if(res.ok){
//성공하면 data.json파일에 내용 추가되면서 화면에 나타남(새로고침 ㅎㅎ)
alert("과목이 추가되었습니다")
}
})
.catch(e=> console.log(e))
}
return (
<div>
<input type="text" name="subject" onChange={onChange}/>
<button onClick={submit}>추가</button>
</div>
);
};
export default AddSubject;
타이틀 내용 추가해주는 페이지로 이동시켜줘야함!!!!
App 에 조립!!!!
App.tsx
import React, { useState } from 'react';
import './App.css';
import Header from './components/Header';
import Subjects from './components/Subjects';
import Lists from './components/Lists';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import AddSubject from './components/AddSubject';
function App() {
const [subject, setSubject] = useState(0);
const onChange = (subject: number): void => {
setSubject(subject);
}
return (
<BrowserRouter>
<div className="App">
<Header sitename="typescript" onChange={onChange}/>
<Routes>
<Route path="/" element={<>
<Subjects onChange={onChange}/>
<Lists subject={subject}/>
</>}/>
<Route path="/addSubject" element={<AddSubject/>}/>
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
다음은 list 내용 추가 해주는 화면을 만들자
components폴더
AddFunctions.tsx
import React, { useState } from 'react';
const AddFunctions = () => {
const [subject, setSubject] = useState({ //초기값 설정
subjects: "",
name: "",
type: "",
syntex: "",
desc: ""
})
const onChange = (e:React.ChangeEvent<HTMLInputElement>) =>{ //e: 타입
const {name, value} = e.target
setSubject({
...subject,
[name]: name === "subjects" ? Number(value): value
//subjects 값에 number값만 들어가야해서 조건줌
})
}
const submit = (e:React.FormEvent<HTMLFormElement>) => { //e: 타입
e.preventDefault();
console.log(subject)
fetch('http://localhost:3003/functions',{
method: 'POST', // 담아서 전송 post는 body에만 담을수 있음
headers: {
"Content-type": "application/json" //헤더값중 content-type 정의
},
body: JSON.stringify(subject) //object 를 문자 형태로
}).then(res=>{
if(res.ok){
alert("등록되었습니다")
}
})
.catch(e=>console.log(e))
}
return (
<div>
<form onSubmit={submit}>
<p><input type="text" name="subjects" placeholder='과목을입력하세요(숫자)'
value={subject.subjects} onChange={onChange}/></p>
<p><input type="text" name="name" placeholder='함수입력'
value={subject.name} onChange={onChange}/></p>
<p><input type="text" name="type" placeholder='타입입력'
value={subject.type} onChange={onChange}/></p>
<p><input type="text" name="syntex" placeholder='구문입력'
value={subject.syntex} onChange={onChange}/></p>
<p><input type="text" name="desc" placeholder='설명입력'
value={subject.desc} onChange={onChange}/></p>
<p><button type='submit'>추가</button></p>
</form>
</div>
);
};
export default AddFunctions;
HTTP Method 알아보기
2023.01.23 - [javascript/javascript 고급문법] - [javascript] XMLHttpRequest
Link도 같이 걸어줘야한다
Header.tsx
import { Link } from 'react-router-dom'; //추가!!!!!
const Header = ({sitename, onChange}: HeaderProps) => {
return (
<HeaderWrapper>
<h1>{sitename}</h1>
<HeaderUl>
<li><Link to="/addSubject">과목등록</Link></li>
<li>내장함수등록</li>
<li onClick={()=>onChange(0)}><Link to="/">함수리스트</Link></li>
</HeaderUl>
</HeaderWrapper>
);
};
list 내용 추가해주는 페이지로 이동시켜줘야함!!!!
App 에 조립!!!!
App.tsx
import React, { useState } from 'react';
import './App.css';
import Header from './components/Header';
import Subjects from './components/Subjects';
import Lists from './components/Lists';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import AddSubject from './components/AddSubject';
import AddFunctions from './components/AddFunctions';
function App() {
const [subject, setSubject] = useState(0);
const onChange = (subject: number): void => {
setSubject(subject);
}
return (
<BrowserRouter>
<div className="App">
<Header sitename="typescript" onChange={onChange}/>
<Routes>
<Route path="/" element={<>
<Subjects onChange={onChange}/>
<Lists subject={subject}/>
</>}/>
<Route path="/addSubject" element={<AddSubject/>}/>
<Route path="/addfunctions" element={<AddFunctions/>}/>
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Link도 같이 걸어줘야한다
Header.tsx
const Header = ({sitename, onChange}: HeaderProps) => {
return (
<HeaderWrapper>
<h1>{sitename}</h1>
<HeaderUl>
<li><Link to="/addSubject">과목등록</Link></li>
<li><Link to="/addfunctions">내장함수등록</Link></li>
<li onClick={()=>onChange(0)}><Link to="/">함수리스트</Link></li>
</HeaderUl>
</HeaderWrapper>
);
};
끝!!!!!!!!!!!!!!끄끄ㅡ끄ㅡ끄끄끄끄끝!!!!!!!
'typescript' 카테고리의 다른 글
[typescript] typesafe-actions 라이브러리 (1) | 2023.02.24 |
---|---|
[typescript]react-typescript(redux) (0) | 2023.02.24 |
[typescript] as & ReturnType (0) | 2023.02.24 |
[typescript] react-typscript(useReducer + contextAPI) (0) | 2023.02.24 |
[typescript] react-typscript(usereducer) (0) | 2023.02.24 |
댓글