[React] Typescript

Typescript 사용법

# 리액트 + 타입스크립트 프로젝트 생성 명령어
npx create-react-app 프로젝트명 --template typescript
예) npx create-react-app frontend-react --template typescript

# 타입스크립트 : 마이크로소프트(깃허브, vscode, typescript, 윈도우 등)
# why? 1) 단점 : js 는 type(자료형)을 정의하지 않고 자유롭게 변수의 값을 넣어 코딩함
# (코딩이 간략/코딩 생산성 증가, 코딩시 에러를 잡지못하고 서비스때 심각한 에러가 발생함)

# 타입스크립트 : js 에 자료형을 명시해서 코딩하게 해주는 js 확장 언어

.tsx로 생성됨

 

React Typescript폴더 만들고 통합터미널 열어서

npx create-react-app frontend-react --template typescript 입력

 

assets, pages, types 폴더 생성

App.tsx

사용하고 싶은 태그 주석 해제

import React from 'react';
import logo from './logo.svg';
import './App.css';
import Basic from './pages/Basic';
import BasicExam from './pages/BasicExam';
import Func from './pages/01_exam/Func';
import FuncExam from './pages/01_exam/FuncExam';
import Etc from './pages/02_exam/Etc';
import Counter from './pages/03_exam/Counter';
import Book from './pages/04_exam/Book';
import WebtoonExam from './pages/04_exam/WebtoonExam';

function App() {
  return (
    <div className="App">
      {/* 자식컴포넌트(페이지) 추가 */}
      {/* <Basic /> */}
      {/* <BasicExam /> */}
      {/* <Func/> */}
      {/* <FuncExam/> */}
      {/* <Etc/> */}
      {/* <Counter/> */}
      {/* <Book/> */}
      <WebtoonExam/>
    </div>
  );
}

export default App;

Basic.tsx

// Basic.tsx : 자식 컴포넌트(typescript 적용된 버전)
// rfce
import React,{useState} from 'react'
// 새로운 객체타입(자료형) 정의한 파일 import
import IObject from "../types/IObject";

function Basic() {
    // TODO: 타입스트립트 기본 사용법 익히기

    // 바인딩 변수
    // 사용법 : let [변수명, set변수명] = useState<자료형>(초기값); 자료형은 소문자로!
    let [message, setMessage] = useState<string>("바인딩 변수 출력입니다.");
    let [num, setNum] = useState<number>(0);
    let [bflag, setBflag] = useState<boolean>(true);
    let [obj, setObj] = useState<IObject>({ id: null, name: "green" });
    let [arr, setArr] = useState<Array<number>>([1,2,3,4,5]);
    let [objArr, setObjarr] = useState<Array<IObject>>([{id:null, name: "green"}]);

    // TODO: 1) 기본 자료형 : 문자열(string), 숫자(number), 참/거짓(boolean), 객체(따로 정의)
    // 일반 변수 정의(문자열(string))
    // 사용법 : let 변수명: 자료형 = 값;
    let message2: string = "일반 변수 출력입니다."; // 문자열
    let num2: number = 1; // 숫자형(실수, 정수 모두 포함)
    let bflag2: boolean = false;   // 참/거짓(bool/boolean)
    // TODO: 객체는 자료형을 개발자가 따로 정의해서 사용함
    // IObject = { id?: any | null, name: string }
    // 설명 : id (any(모든자료형 허용) 또는 null 자료형 허용)
    //        name (string(문자열만 들어올수 있음))
    let obj2: IObject = { id: null, name: "green2" };   // 객체(object)

    // TODO: 2) 유니온 사용법 : let 변수명: 자료형(type) | 자료형2 ...
    let num3: number | string = 1;
    let num4: number | string = "hello";
    // let num5: number | string = true; // 에러

    // TODO: 3) 옵셔널(?) : 객체의 속성이나 함수의 매개변수에 사용가능
    // TODO: 사용하면 오류없이 실행되게 함( 자료형 | undefined 더 붙은 것과 같은 의미)
    function hello(name?: string) {
        console.log(`안녕하세요 ${name}`);
    }
    // 함수의 사용
    hello("홍길동"); // 안녕하세요 홍길동
    hello();        // 안녕하세요

    // TODO: 4) 배열 : let 변수: Array<자료형 | 자료형2 ...>
    // TODO: 변수 및 함수 사용 시 반드시 정의된 자료형의 값으로 사용하게 강제함
    let arr2 : Array<string> = ["a","b","c"]; // 문자열 배열
    let arr3 : Array<any> = [1,2,3]; // any(모든 자료형 사용가능) 배열
    let arr4 : Array<number> = [1,2,3]; // 숫자 배열
    // TODO: 객체배열
    let objArr2: Array<IObject> = [{ id: 1, name: "green2"}]; // JSON 문서 형태(객체배열)

  return (
    <div>
        문자(바인딩) : {message}<br/>
        문자(일반) : {message2}<br/>

        숫자(바인딩) : {num}<br/>
        숫자(일반) : {num2}<br/>

        불(바인딩) : {bflag? "참": "거짓"}<br/>
        불(일반) : {bflag2? "참": "거짓"}<br/>

        객체(바인딩) : {obj.name}<br/>
        객체(일반) : {obj2.name}<br/>

        배열(바인딩) : {arr}<br/>
        배열(일반) : {arr2}<br/>

        {/* 반복문으로 출력 : .map((value, index)=>()) */}
        객체배열(바인딩) : {objArr.map((value)=>value.name)}<br/>
        객체배열(일반) : {objArr2.map((value)=>value.name)}<br/>
    </div>
  )
}

export default Basic

IObject.ts

객체함수는 자료형을 개발자가 따로 정의

// types/IObject.ts : 객체 자료형을 정의하는 파일
// TODO: 타입스크립트 파일명 종류 : .tsx(jsx표현식을 사용한 js) , .ts(jsx표현식 사용안한 js)
// TODO: 리액트의 JSX(Javascript and XML) : {값}
// 사용법 : export default interface 객체자료형명 {
//            속성?: 자료형 | 자료형2 ...,
//            속성2: 자료형
//         }
// 속성? : 옵셔널(?) : 속성이 없어도 오류 없이 실행되게 해줌
//  예)  let obj: IObject = { name:"홍길동"}
//       obj.id (결과는 오류로 출력되지만 에러없이 프로그램이 실행되게 해줌)
export default interface IObject {
    id?: any | null,
    name: string
}

결과

BasicExam.tsx

// BasicExam.tsx : 자식(연습)
// rfce
import React, { useState } from "react";

function BasicExam() {
  // TODO: 변수 정의
  const [message, setMessage] = useState<string>(
    "안녕하세요 그린컴퓨터아카데미입니다."
  );
  const [message2, setMessage2] = useState<string>("아래는 곱셈 샘플입니다.");
  const [message3, setMessage3] = useState<string>("곱셈");
  const [num, setNum] = useState<number>(2);
  const [num2, setNum2] = useState<number>(3);
  const [sumVal, setSumVal] = useState<number>(num * num2);

  return (
    <div>
      {/* 아래 처럼 바인딩 변수를 사용해서 화면 출력을 하되
          타입스크립트 코드를 적용하여 출력하세요
        단, 문자는 string , 숫자는 number 를 사용해서 코딩하세요
        */}
      {/* 결과 :
                안녕하세요 그린컴퓨터아카데미입니다.
                아래는 곱셈 샘플입니다.
                곱셈
                2 * 3 = 6
               
         */}
         {message}<br/>
         {message2}<br/>
         {message3}<br/>
         {num + " * " + num2 + " = " + sumVal}
         {/* {num + " * " + num2 + " = " + num*num2} */}
         {/* {num} * {num2} = {sumVal} */}
    </div>
  );
}

export default BasicExam;

결과

Func.tsx

// Func.tsx : 자식 컴포넌트
// rfce
import React, { useState } from "react";

function Func() {
  // TODO: 변수 정의
  let [name, setName] = useState<string>("");
  let [message, setMessage] = useState<string>("");
 
  // TODO: 함수 정의 : typescript 적용
  // nfn : 매개변수 2개를 전달받아 messeage 변수에 저장하는 함수
  // getInputVal("안녕하세요", 2023)
  // TODO: 사용법 : const 함수명 = (매개변수:자료형, ...): 리턴자료형 =>{}
  const getInputVal = (arg: string, arg2: number): void => {
    setMessage(`${arg} 현재는 ${arg2} 년도입니다.`) // message(화면 바인딩) ` 백틱
  }

  // 역바인딩(이벤트) 함수 정의
  // nfn
  // TODO: const 함수명 = (event:이벤트자료형): 리턴자료형 => {}
  const onChangName = (event:React.ChangeEvent<HTMLInputElement>): void => {
    setName(event.target.value);
  }

  return (
    // TODO: 여기
    <div className="container">
      <input
        className="form-control mt-3 mb-3 w-25"
        type="text"
        name="name"
        value={name}
        onChange={onChangName}
      />
      입력값 : {name}
      <br />
      {/* react 아래 onClick={함수명} */}
      {/* TODO: 이벤트 함수에 매개변수 있으면 화살표함수를 사용해야함 */}
      {/* react 아래 onClick={()=>함수명(매개변수,매개변수2..)}} */}
      <button
        className="btn btn-primary mt-3 mb-3"
        onClick={() => getInputVal("안녕하세요", 2023)}
      >
        입력값
      </button>
      <br />
      {message}
    </div>
  );
}

export default Func;

결과

ChangeEvent 함수 사용법

마우스 커서 올리면 예시가 뜬다

Handler 제거 후 붙여넣기

// 역바인딩(이벤트) 함수 정의
  // nfn
  // TODO: const 함수명 = (event:이벤트자료형): 리턴자료형 => {}
  const onChangName = (event:React.ChangeEvent<HTMLInputElement>): void => {
    setName(event.target.value);
  }

FuncExam.tsx

// FuncExam.tsx : 자식(연습)
// rfce
import React, { useState } from "react";

function FuncExam() {
    // TODO: 소스완성하기 : 타입스크립트 적용
    // 결과 :
    //    입력값 : !Ds1234567890
    //    안녕하세요 오늘날짜는 4 일이 맞나요? 답은 true 입니다.

    // TODO: 변수 정의
    let [message, setMessage] = useState<string>("");
    let [password, setPassword] = useState<string>();
   

    // TODO: 함수 정의
    const getInput = (arg: string, arg2: number, arg3: boolean): void => { // void 생략 가능
        setMessage(`${arg} 오늘날짜는 ${arg2} 일이 맞나요? 답은 ${arg3}입니다`)
    }

    const onChangPassword = (event:React.ChangeEvent<HTMLInputElement>): void => {
        setPassword(event.target.value)
    }
  return (
    // TODO: 여기
    <div className="container">
      <input
        className="form-control mt-3 mb-3 w-25"
        type="password"
        name="password"
        value={password}
        onChange={onChangPassword}
      />
      입력값 : {password}
      <br />
      <button
        className="btn btn-primary mt-3 mb-3"
        onClick={() => getInput("안녕하세요", 4, true)}
      >
        입력값
      </button>
      <br />
      {message}
    </div>
  );
}

export default FuncExam;

결과

Etc.tsx

// Etc.tsx : 자식 컴포넌트
// rfce
import React from 'react'

function Etc() {
    // TODO: 1) 읽기 전용(readonly) : 값 수정 불가
    interface IHello {
        readonly name: string
    }
    let val: IHello = { name: "hello"}; // 초기값 정의
    // val.name = "hello2"; // x(readonly) ,수정시 에러

    // TODO: 2) 튜플(tuple) : 정해진 자료형의 고정된 크기의 배열
    let tuple: [string, number] = ["a", 1];
    console.log("tuple", tuple);
    // tuple = ["a", 1, 2]; // 값이 3개가 들어와서 에러(x)
    // tuple = [1, "a"]; // 자료형 순서가 맞지않음 에러(x)

    // TODO: 3) enum : 열거형, 상수를 대체해서 사용함
    // TODO: 아래 상수에 자동으로 0 ~ n 1씩 증가되어 저장됨, 값도 직접 지정 가능
    enum Week {
        Sun,     // Sun = 0
        Mon,     // Mon = 1
        Tue,     // Tue = 2
        Wed = 5, // Wed = 5 (개발자가 강제로 값을 저장)
        Thu,     // Thu = 6
        Fri,     // Fri = 7
        Sat      // Sat = 8
    }
    console.log("Week", Week);
    console.log("Week", Week.Sun);

    // TODO: 4) 별명 붙이기
    // TODO: 사용법 : type 별명 = 자료형 | 자료형2 ...
    // TODO: 사용 : let 변수명 : 별명 = 값;
    type aliasUser = string | number;
    let person: aliasUser = "hong";
    console.log(person);

    // TODO: 5) 타입 추론 : 모든 변수에 자료형을 지정하지 않아도 값으로 추론하는 기능을 부여
    // TODO: (1) 변수의 초기값 : 생략가능
    // let num:number = 10;
    let num = 10; // 사용가능

    // TODO: (2) 기본값이 있는 매개변수 : 생략가능
    // 모던자바스크립트 사용법 : function 함수명(매개변수, 매개변수2=0){}
    // 함수의 사용 : 함수명(1) =>함수명(1, 0)
    function add(a:number, b = 0):number {
        return a + b;
    }
    // TODO: (3) 리턴자료형는 함수에서 생략가능
    function add2(a:number, b = 0) {
        return a + b;
    }

    // TODO: 6) 타입(자료형) 단언 :
    // TODO: 활용 : 컴퓨터는 알수없으나 개발자는 확실히
    // TODO:        그변수의 자료형을 확신하면 사용가능
    // TODO: 사용법 : 변수 as 자료형
    function someFunc(val: string|number, isNumber: boolean) {
        // 가정 : isNumber 가 true 이면 무조건 val 값은 정수가 된다고 확신
        if(isNumber === true) {
            (val as number).toFixed(2);
        }
    }

  return (
    <div>Etc</div>
  )
}

export default Etc

결과

Counter.tsx

// Counter.tsx : 연습(카운팅 예제)
// rfce
import React,{useState} from 'react'

function Counter() {
     //  TODO: 연습문제 숫자를 감소시키는 함수를 작성하고 화면에 표시하세요
     //  TODO: 단, 음수값은 화면에 표시하지 마세요
    // TODO: 변수정의 : count
    let [count, setCount] = useState<number>(0);

    // TODO: 함수정의 : setIncreate() , setDecreate()
    // + 카운팅 함수
    const setIncreate = () => {
        setCount(count++);
     }

    //  - 카운팅 함수
    const setDecreate = () => {
        if(count >= 0) {
            setCount(count--);
        }
     }

  return (
    <div>
      <h3>Counter 예제</h3>
      <button onClick={setIncreate}>증가</button>
      {/* //  TODO: 연습문제 숫자를 감소시키는 함수를 작성하고 화면에 표시하세요 */}
      <button onClick={setDecreate}>감소</button>
      <p>값 : {count}</p>
    </div>
  )
}

export default Counter

결과

0 밑으로는 안 내려감

Book

// Book.tsx : 책방 컴포넌트
// rfce
import React, { useState } from "react";
import IBook from "../../types/IBooks";

function Book() {
  // TODO: 변수 정의
  const initialBooks = [
    {
      id: 1,
      title: "어떻게 배울 것인가",
      publisher: "비즈니스북스",
      author: "존 맥스웰",
      stock: 2,
    },
    {
      id: 2,
      title: "신경끄기의 기술",
      publisher: "갤리온",
      author: "마크 맨슨",
      stock: 0,
    },
    {
      id: 3,
      title: "부의 미래",
      publisher: "청림출판",
      author: "앨빈 토플러",
      stock: 5,
    },
    {
      id: 4,
      title: "기획자의 습관",
      publisher: "홍익출판사",
      author: "최장순",
      stock: 4,
    },
  ];
  // 바인딩 변수(객체배열) 정의 : typescript 적용
  // TODO: 사용법 : let [변수명, set변수명] = useState<Array<객체자료형>>(초기값);
  // TODO: 객체자료형 : 따로 정의(IBook) : 이름규칙(명명법) I이름
  let [books, setBooks] = useState<Array<IBook>>(initialBooks); // 객체배열

  // TODO: 함수 정의

  return (
    // TODO: 여기
    <div>
      <h1>체르니 책방의 도서목록</h1>
      <table>
        <thead>
          <tr>
            <th>도서번호</th>
            <th>도서이름</th>
            <th>출판사</th>
            <th>저자</th>
            <th>재고량</th>
          </tr>
        </thead>
        <tbody>
          {/* 반복문 : 배열변수.map((value, index))=>(<태그>{value.속성명}</태그>) */}
          {/* 대상 : 행(<tr><td></td></tr>) */}

          {books.map((value, index) => (
            // 테이블 tr/td 태그
            <tr key={index}>
              <td>{value.id}</td>
              <td>{value.title}</td>
              <td>{value.publisher}</td>
              <td>{value.author}</td>
              <td>{value.stock}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default Book;

IBooks.ts

// IBook.ts : 책방객체명
export default interface IBook {
    id?: number,
    title: string,
    publisher: string,
    author: string,
    stock: number
}

결과

Webtoon.tsx

// WebtoonExam.tsx : 자식(연습)
// rfce
import React, { useState } from "react";
import IWebtoon from "../../types/IWebtoons";

function WebtoonExam() {
  // TODO: 아래 소스를 타입스크립트를 사용하는 예제로 변경하세요
  // 1) interface 를 외부 정의해서 사용하세요
  //   이름 : IWebtoon.ts
  // TODO: 변수정의
  const initialWebtoon = [
    {
      id: 1,
      name: "햄스터와 그녀",
      isUpdate: true,
    },
    {
      id: 2,
      name: "프롬 스타",
      isUpdate: true,
    },
    {
      id: 3,
      name: "위대한 로맨스",
      isUpdate: false,
    },
    {
      id: 4,
      name: "빛나는 손을",
      isUpdate: false,
    },
  ];

  let [webtoon, setWebtoon] = useState<Array<IWebtoon>>(initialWebtoon)
  // TODO: 함수정의

  return (
    // TODO: 여기
    <div>
      <h2>Free Webtoon</h2>
      <ul>{/* 반복문 : li 대해서 */}
      {webtoon.map((item) => (
          <li className="item" key={item.id}>
            <a href={item.link}>
              <img src={item.img} />
              <span className="tit">제목 {item.name}</span>
            </a>
            {/* item.isUpdate 있으면 span 태그 실행 */}
            {item.isUpdate && <span className="tag">N</span>}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default WebtoonExam;

IWebtoon.ts

export default interface IWebtoon {
    id: number,
    name: string,
    link: string,
    img: string,
    isUpdate: boolean,
}

결과