Frontend 프론트엔드

[ 27 ] 게시판에 타입스크립트 컨테이너/프리젠터 적용 실습

박민우_ 2024. 6. 22. 10:41

게시글 등록 페이지, 게시글 수정 페이지

변경사항 없음 

함수에 타입스크립트를 적용하는 것을 생각해보면 함수를 호출할 때 타입을 정해주는것이 아니라 함수를 정의한 곳에서 타입을 정의해준다.

함수형 컴포넌트에서도 컴포넌트를 호출하는 곳에서는 타입을 지정해줄 필요 없고 컴포넌트에서 매개변수의 타입을 설정해주면 된다.

 

Container 컴포넌트

props 의 타입을 지정해준다.

onClickUpdate 안에서 myvariables 객체의 타입을 지정해준다. ( 객체는 타입추론이 안된다 ) .

이벤트의 타입을 지정해준다. ( 이벤트의 타입은 react에서 제공해준다 ) 

 

import { useMutation } from "@apollo/client";
import { useState, ChangeEvent } from "react";
import BoardWriteUI from "./BoardWrite.presenter";
import { 나의그래프큐엘세팅, UPDATE_BOARD } from "./BoardWrite.queries";
import { useRouter } from "next/router";

interface IBoardWriteProps {
  isEdit: boolean;
  data?: any;
}

export default function BoardWrite(props: IBoardWriteProps) {
  const router = useRouter();

  const [isActive, setIsActive] = useState(false);
  const [writer, setWriter] = useState("");
  const [title, setTitle] = useState("");
  const [contents, setContents] = useState("");

  const [나의함수] = useMutation(나의그래프큐엘세팅);
  const [updateBoard] = useMutation(UPDATE_BOARD);

  const onClickSubmit = async () => {
    const result = await 나의함수({
      variables: {
        // variables === $ 의 역할
        writer: writer,
        title: title,
        contents: contents,
      },
    });
    console.log(result);
    router.push(
      `/section10/10-02-typescript-boards/${result.data.createBoard.number}`
    );
  };

  const onClickUpdate = async () => {
    interface IMyvariables {
      number: Number;
      writer?: string;
      title?: string;
      contents?: string;
    }
    const myvariables: IMyvariables = { number: Number(router.query.number) };
    if (writer) myvariables.writer = writer;
    if (title) myvariables.title = title;
    if (contents) myvariables.contents = contents;

    const result = await updateBoard({
      variables: {
        ...myvariables,
      },
    });
    router.push(`/section10/10-02-typescript-boards/${router.query.number}`);
  };

  const onChangeWriter = (e: ChangeEvent<HTMLInputElement>) => {
    setWriter(e.target.value);
    if (e.target.value && title && contents) {
      setIsActive(true);
    }
  };
  const onChangeTitle = (e: ChangeEvent<HTMLInputElement>) => {
    setTitle(e.target.value);
    if (writer && e.target.value && contents) {
      setIsActive(true);
    }
  };
  const onChangeContents = (e: ChangeEvent<HTMLInputElement>) => {
    setContents(e.target.value);
    if (writer && title && e.target.value) {
      setIsActive(true);
    }
  };
  return (
    <div>
      <BoardWriteUI
        onClickSubmit={onClickSubmit}
        onClickUpdate={onClickUpdate}
        onChangeWriter={onChangeWriter}
        onChangeTitle={onChangeTitle}
        onChangeContents={onChangeContents}
        isActive={isActive}
        isEdit={props.isEdit}
        data={props.data} // undefined 이거나, data이거나 둘 중 하나 !
      />
    </div>
  );
}

 

presenter 컴포넌트 

props 의 타입을 정의해준다.

graphql 에서 받아오는 데이터의 타입은 일단은 any로 두고 추후 공부한 뒤 수정할 예정이다.

import { ChangeEvent, MouseEvent } from "react";
import { BlueButton, RedInput } from "./BoardWrite.style";

interface IBoardWriteUIProps {
  onClickSubmit: (event: MouseEvent<HTMLButtonElement>) => void;
  onClickUpdate: (event: MouseEvent<HTMLButtonElement>) => void;
  onChangeWriter: (event: ChangeEvent<HTMLInputElement>) => void;
  onChangeTitle: (event: ChangeEvent<HTMLInputElement>) => void;
  onChangeContents: (event: ChangeEvent<HTMLInputElement>) => void;
  isActive: boolean;
  isEdit: boolean;
  data: any;
}

export default function BoardWriteUI(props: IBoardWriteUIProps) {
  return (
    <div>
      <div>
        작성자 :{" "}
        <RedInput
          type="text "
          onChange={props.onChangeWriter}
          defaultValue={props.data?.fetchBoard.writer}
        />
        제목 :{" "}
        <input
          type="text"
          onChange={props.onChangeTitle}
          defaultValue={props.data?.fetchBoard.title}
        />
        내용 :{" "}
        <input
          type="text"
          onChange={props.onChangeContents}
          defaultValue={props.data?.fetchBoard.contents}
        />
        <BlueButton
          onClick={props.isEdit ? props.onClickUpdate : props.onClickSubmit}
          isActive={props.isActive}
        >
          {props.isEdit ? "수정" : "등록"}하기
        </BlueButton>
        ;
      </div>
    </div>
  );
}

 

style.ts 컴포넌트 

props 의 타입을 정의해준다.

import styled from "@emotion/styled";

interface IBlueButtonProps {
  isActive: boolean;
}

const RedInput = styled.input`
  border-color: red;
`;

const BlueButton = styled.button`
  background-color: ${(props: IBlueButtonProps) => {
    return props.isActive ? "yellow" : "";
  }};
`;

export { RedInput, BlueButton };

 

interface를 효율적으로 사용하는 방법

위의 예시 코드들은 각 페이지 안에서 타입들을 정의하고 사용했지만, 재사용성을 높이고 가독성을 높이기 위해서 타입들을 하나의 파일로 관리하고, 각 컴포넌트들은 타입들을 import 해서 사용하는 것이 좋다.

BoardWrite.types.ts

 

import { ChangeEvent, MouseEvent } from "react";

export interface IBoardWriteProps {
  isEdit: boolean;
  data?: any;
}

export interface IBlueButtonProps {
  isActive: boolean;
}

export interface IMyvariables {
  number: Number;
  writer?: string;
  title?: string;
  contents?: string;
}

export interface IBoardWriteUIProps {
  onClickSubmit: (event: MouseEvent<HTMLButtonElement>) => void;
  onClickUpdate: (event: MouseEvent<HTMLButtonElement>) => void;
  onChangeWriter: (event: ChangeEvent<HTMLInputElement>) => void;
  onChangeTitle: (event: ChangeEvent<HTMLInputElement>) => void;
  onChangeContents: (event: ChangeEvent<HTMLInputElement>) => void;
  isActive: boolean;
  isEdit: boolean;
  data: any;
}
728x90