본문 바로가기
typescript

[typescript] json-server라이브러리, react-typescript(styled-components)

by 남민섭 2023. 2. 24.
728x90
반응형

폴더 생성

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

 

[javascript] Fetch API

Fetch API 도 XMLHttpRequest와 비슷하게 서버로부터 데이터를 받아오고 전송하지만 가장 큰 차이점은 Fetch API는 Promise방식으로 구성되어있다 그리고!!!! XMLHttpRequest보다 훨~~~씬 더 간결하다!!!!!! 구문) f

uou413.tistory.com

 

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

 

[javascript] XMLHttpRequest

● XMLHttpRequest(XHR) 서버와 통신을 하도록 하는 객체 1. 객체는 서버와 상호작용하기 위해 사용 2. 전체 페이지를 새로고침하지 않아도 URL을 통해 데이터를 전송하거나 받아 올수 있음 3. XML데이터

uou413.tistory.com

 

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>
    );
};

 

끝!!!!!!!!!!!!!!끄끄ㅡ끄ㅡ끄끄끄끄끝!!!!!!!

728x90
반응형

댓글