connecting dots

Beginner 14회차 (8/20) | TS 유틸리티 타입, React-Router의 페이지 지정 방법, 모달 애니메이션 적용 실습, todo 순서 바꾸기 실습(sortablejs), useRef와 current 속성 본문

Live Class/Beginner

Beginner 14회차 (8/20) | TS 유틸리티 타입, React-Router의 페이지 지정 방법, 모달 애니메이션 적용 실습, todo 순서 바꾸기 실습(sortablejs), useRef와 current 속성

dearsuhyun 2024. 8. 21. 14:02

TS 유틸리티 타입

객체 데이터에 있는 모든 내용을 선택적으로 만듬

interface User {
    name: string
    age: number
}

const user: User {
    name: 'suhyun'
}
// 오류 발생
// 'age: 85' 없으면 필수인데 없기 때문에 오류 !

 

Partial을 붙여주면 interface 내용이 필수적이 아니라 선택적으로 설정된 것과 같이 작동 !

user에서 name, age를 사용하지 않아도 오류가 발생하지 않음

'처음부터 선택적으로 만들면 안되는가 ?'
--> 모든 것에 필수여야 하는 경우에 대응하기 힘듬
만들 때는 필수 --> 선택적으로 ? 붙여서 사용하는 것이 일반적 !
interface PartialUser {
    name?: string
    age?: number
}

// utility type (TS에 이미 내장된 타입)
const user: Partial<User> {
    name: 'suhyun'
}

 

코드 적용

api > todos.ts

 

- record

key를 속성(key)로, type을 그 속성값의 type으로 지정하는 새로운 타입

Record<KEY, TYPE>

 

type TName = 'neo' | 'lewis'

const developers: Record<TName, number> = {
  neo: 12,
  lewis: 13
}

 

이렇게 이해할 수 있음

interface INewType {
  neo: number
  lewis: number
}

 

- omit

TYPE에서 KEY로 속성을 생략하고 나머지를 선택한 새로운 타입을 반환
TYPE은 속성을 가지는 인터페이스나 객체 타입이어야 함

Omit<TYPE, KEY>
interface IUser {
  name: string
  age: number
  email: string
  isValid: boolean
}
type TKey = 'name' | 'email'

const user: Omit<IUser, TKey> = {
  age: 22,
  isValid: true,
  name: 'Neo' // TS2322: Type '{ age: number; isValid: true; name: string; }' is not assignable to type 'Pick<IUser, "age" | "isValid">'.
}

 

이렇게 이해할 수 있음 !

interface INewType {
  // name: string
  age: number
  // email: string
  isValid: boolean
}

 

- exclude

유니언 TYPE1에서 유니언 TYPE2를 제외한 새로운 타입을 반환

Exclude<TYPE1, TYPE2>

 

type T = string | number

const a: Exclude<T, number> = 'Only string'
const b: Exclude<T, number> = 1234 // TS2322: Type '123' is not assignable to type 'string'.
const c: T = 'String'
const d: T = 1234

 

 

 

todo 추가 기능이 구현되지 않는 문제

기존 코드

async function createTodo() {
    await axios.post('/api/todos'), {
      method: 'POST',
      data: {
        title
      }
    }
    getTodos()
  }

 

수정 코드

method, data 부분을 axios의 두번째 인수로 줘야 하는데 그렇지 못해서 발생한 오류 수정

async function createTodo() {
    await axios.post('/api/todos', {
      method: 'POST',
      data: {
        title
      }
    })
    getTodos()
  }

 

수정 후 main에 푸시하면 vercel에서 자동으로 배포 !

 

React-Router의 페이지 지정 방법

1. 객체 형식으로 페이지(라우트) 지정

export const router = createBrowserRouter([
  {
    path: '/',
    element: <Main />,
    // 메인 페이지가 보이는 상태에서 모달을 보여줄거니까 children으로 설정
    children: [
      {
        path: ':todoId',
        element: <TodoItemDetails />
      }
    ]
  }
])

 

2. 컴포넌트 방식으로 페이지(라우트) 지정

export default function Index() {
  // location 객체는 현재 URL에 대한 정보를 가지고 있음
  const location = useLocation()
  return (
    <AnimatePresence mode="wait">
      <Routes
        location={location}
        key={location.pathname}>
        <Route
          path="/"
          element={<Main />}>
          <Route
            path={'/:todoId'}
            element={<TodoItemDetails />}
          />
        </Route>
      </Routes>
    </AnimatePresence>
  )
}

 

모달 애니메이션 적용 실습

 

routes > index.tsx

<AnimatePresence> 전체 주소의 사라지는 애니매이션 까지 담당하겠다 !

페이지를 바꾸는 방법으로 모달을 사용하고 있기 때문에 페이지 사라질 때 부분도 설정해주어야 함

모달 사라지게 할 때 주소가 바로 사라지니까 당연히 모달도 바로 사라지는 것 --> AnimatePresence 사용

 

 

todoItemDetails.tsx

나타날 때는 자연스럽게 나타나지만, 끌 때는 바로 꺼짐 (페이지를 바꿔서 모달을 띄우는 것이기 때문)

 

 

exit={{ opacity: 0}}을 추가해서 사라질 때도 자연스럽게 사라지도록 만들어주기 !

 

구현 화면

 

todoList에서 item 순서 바꾸기 실습

import Sortable from 'sortablejs'
npm i -D @types/sortablejs

 

useRef과 current 속성

 

Main.tsx

 

Sortable의 필수 속성 4가지

new Sortable(listRef.current, {
      handle: '',
      animation: 0,
      forceFallback: true,
      onEnd: () => {}
}

 

 

new Sortable(todoListEl.current, {
    handle: '.drag-handle', // 드래그 핸들이 될 요소의 선택자를 입력
    animation: 0, // 정렬할 때 애니메이션 속도(ms)를 지정, default: 150
    forceFallback: true, // 크로스 브라우징, 다양한 환경의 일관된 Drag&Drop(DnD)을 위해 HTML5 기본 DnD 동작을 무시하고 내장 기능을 사용, default: false
    // 요소의 DnD가 종료되면 실행할 핸들러(함수)를 지정
    onEnd: event => {
      const { oldIndex, newIndex } = event
      if (oldIndex === undefined || newIndex === undefined) return
      console.log('oldIndex:', oldIndex, 'newIndex:', newIndex)
      mutate({
        oldIndex,
        newIndex
      })
    }

 


sortablejs는 화면만 바꿔주기 때문에 새로고침하면 순서 바꾼 것이 그대로 돌아옴

서버에 전송해줘서 새로고침해줘도 유지하도록 ! api 사용해주어야 함

oldIndex, newIndex가 console에 찍히는 화면

 

API 명세서 확인

 

store에 reordertodos 생성

 


* TS 유틸리티 타입 자주 쓰이는 4가지는 꼭 알아두기 (다른 것도 읽어보기)
partial, record, omit, exclude

* Pakage.json
Sem Ver: ^35.2.4
--> major 버전은 바뀌지 않지만, 버셀 패키지를 설치하면 마이너와 패치 버전을 가장 최신으로 유지한다

"vercel": "^35.2.4",
// latest(최신버전) 
npm i -D vercel@latest 

// canary(배타버전) 
npm i -D vercel@canary​

 

* transition: 전 상태 --> 후 상태를 자연스럽게 전환해준다

* 처음부터 react-** 라고 만들어진 라이브러리는 대안이 없음
sortablejs 의 경우 react-sortablejs라는 대안이 있는 것임
순수 js(ts)로 만들어진 라이브러리가 있다면 react-**잘 사용하지 않는 이유 ?
--> react, vue, svelte같은 프레임워크는 계속 변화함 ==> react-**도 업데이트 되지 않으면 사용 불가
라이브러리에 의존해서 만들어진 추가적인 라이브러리는, 라이브러리가 망가지면 쓸 수 없음
순수 js, ts로 만들어진 라이브러리를 사용하는 것을 추천함 !

 

https://www.heropy.dev/p/WhqSC8

 

한눈에 보는 타입스크립트

타입스크립트는 Microsoft에서 개발하고 유지/관리하는 Apache 라이센스가 부여된 오픈 소스로, 자바스크립트에 강한 타입 시스템을 적용해 대부분의 에러를 컴파일 환경에서 코드를 입력하는 동안

www.heropy.dev

https://github.com/ParkYoungWoong/todo_vite-react-ts

 

GitHub - ParkYoungWoong/todo_vite-react-ts

Contribute to ParkYoungWoong/todo_vite-react-ts development by creating an account on GitHub.

github.com

https://sortablejs.github.io/Sortable/#frameworks

 

SortableJS

Thresholds Try modifying the inputs below to affect the swap thresholds. You can see the swap zones of the squares colored in dark blue, while the "dead zones" (that do not cause a swap) are colored in light blue. <!-- --> new Sortable(example7, { swapThre

sortablejs.github.io

 

반응형