👾 에러

상태관리를 zustand 로 바꾸는 도중 빨간맛을 보았다. 

 

 

📁 zustand / 📑 auth.store.ts

임포트도 잘했는데 왜 그럴까 구글링해도 나와같은 오류는 없었다.. 한참 삽질하다가 설마~_~ 타입지정을 안해줘서 그런걸까? 하고 타입을 지정해보았다.

import create from "zustand";

export const useAuthStore = create((set) => ({
  email: "",
  password: "",
  nickname: "",
  favorite_artist: [],
  is_admin: false,
  error: {
    password: "",
    nickname: "",
  },

  setEmail: (email) => set({ email }),
  setPassword: (password) => set({ password }),
  setNickname: (nickname) => set({ nickname }),
  setFavoriteArtists: (artists) => set({ favorite_artist: artists }),
  setIsAdmin: (is_admin) => set({ is_admin }),
  setError: (error) =>
    set((state) => ({ error: { ...state.error, ...error } })),
}));

 

 

 

💡 해결

타입 지정은 안해줘서 생긴 오류인지 정확히는 모르겠지만 타입지정을 해주었더니 오류가 사라졌다.

export type AuthStore = {
  email: string;
  nickname: string;
  password: string;
  favorite_artist: string[];
  is_admin: boolean;
  error: {
    password: string;
    nickname: string;
  };

  setEmail: (email: string) => void;
  setPassword: (password: string) => void;
  setNickname: (nickname: string) => void;
  setFavoriteArtists: (favoriteArtists: string[]) => void;
  setIsAdmin: (is_admin: boolean) => void;
  setError: (error: Partial<AuthStore["error"]>) => void;
};

 

 

1. 라이브러리 설치

yarn add react-redux
yarn add @reduxjs/toolkit

 

 

2. Redux Store 설정

📁 redux / 📁 store / 📑 store.ts

타입스크립트로 만든 프로젝트라 RootState를 추가해주었다 RootState타입이 스토어의 상태를 나타내는 타입으로 정의된다.

import { configureStore } from "@reduxjs/toolkit";
import todoSlice from "../slices/todoSlice";

export const store = configureStore({
  reducer: {
    todos: todoSlice,
  },
});

export type RootState = ReturnType<typeof store.getState>;

 

 

3.Slice 생성 및 액션 추가

📁 redux / 📁 slices / 📑 todoSlice.ts

todolist에 들어갈 내용은 id, content, isDone(완료여부)로 구성하였다. 따로 타입 파일을 만들어 인터페이스로 정의해주었다. 

import { createSlice } from "@reduxjs/toolkit";
import { TodosState } from "../../types/TodoType";

const initialState: TodosState = {
  todos: [],
};

const todoSlice = createSlice({
  name: "todos",
  initialState,
  reducers: {
    addTodo: (state, action) => {
      state.todos = [...state.todos, action.payload];
    },
    deleteTodo: (state, action) => {
      state.todos = state.todos.filter((todo) => todo.id !== action.payload);
    },
    toggleTodo: (state, action) => {
      const todo = state.todos.find((todo) => todo.id === action.payload);
      todo ? (todo.isDone = !todo.isDone) : null;
    },
  },
});

export default todoSlice.reducer;
export const { addTodo, deleteTodo, toggleTodo } = todoSlice.actions;

 

3-1. 타입 정의

📁 types / 📑 TodoType.ts

export interface Todo {
  id: number;
  content: string;
  isDone: boolean;
}

export interface TodosState {
  todos: Todo[];
}

 

 

4. Store 통합

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { Provider } from "react-redux";
import { store } from "./redux/store/store.ts";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

 

 

5. 화면에 보여주기

import { useDispatch, useSelector } from "react-redux";
import { RootState } from "./redux/store/store";
import { useState } from "react";
import { addTodo, deleteTodo, toggleTodo } from "./redux/slices/todoSlice";

const App: React.FC = () => {
  const [content, setContent] = useState<string>("");
  const todos = useSelector((state: RootState) => state.todos.todos);
  const dispatch = useDispatch();

  const handleAdd = (e: React.MouseEvent<HTMLFormElement, MouseEvent>) => {
    e.preventDefault();
    const newTodo = {
      id: Date.now(),
      content,
      isDone: false,
    };
    dispatch(addTodo(newTodo));
    setContent("");
  };
  return (
    <>
      <form onSubmit={handleAdd}>
        <input
          type="text"
          value={content}
          onChange={(e) => setContent(e.target.value)}
        />
        <button type="submit">추가</button>
      </form>
      <ul>
        {todos.map((todo) => {
          return (
            <li key={todo.id}>
              <h2>{todo.content}</h2>
              <button onClick={() => dispatch(toggleTodo(todo.id))}>
                {todo.isDone ? "취소하기" : "완료하기"}
              </button>
              <button onClick={() => dispatch(deleteTodo(todo.id))}>
                삭제
              </button>
            </li>
          );
        })}
      </ul>
    </>
  );
};

export default App;

👾 에러

부모 컨포넌트에서 자식 컴포넌트로 props를 전달해줄 때 마주한 타입스크립트 에러이다. 타입을 암시적인 형식이 아니라 명시적으로 선언해줘야 한다. 

 

 

 

💡 해결

 

props 로 받은 매개변수들을 인터페이스로 정의하여 타입을 지정해주었다. 

 

 

 

 

 


🔗 참고

 

바인딩 요소 'children'에 암시적으로 'any' 형식이 있습니다.

바인딩 요소 타입스크립트

velog.io

 

 

 

타입스크립트 사용이유❓


 

타입스크립트란 

TypeScript는 자바스크립트의 상위 집합(superset)으로, 자바스크립트 문법에 타입 체크를 위한 문법들이 추가된것

 

  1. 보다 신뢰할 수 있는 코드: 타입 체크를 통해 런타임 오류를 줄이고, 코드의 예측 가능성을 높인다.
  2. 규모 확장성: 코드베이스가 커질수록 타입 시스템 덕분에 안전하고 효율적으로 관리가 가능하다. (faster safely)
  3. 개발 도구 지원: 강력한 코드 자동 완성을 지원하여 생산성을 높인다.

 

✔️ JavaScript의 단점 상쇄

  • 실행 시간에 결정되는 변수 타입 -> 컴파일 시간에 변수의 타입을 체크
  • 약한 타입 체크 -> VS Code에 코드를 입력하는 순간 에러 메시지 발생
  • 너무나도 물렁물렁한 객체 -> 정의되지 않은 프로퍼티를 연산하여 NaN이 되는 현상을 미연에 방지

✔️ 향상되는 생산성

✔️ 높아지는 안전성

  • TypeScript 코드는 안정성과 가독성이 높아져 개발 및 유지 보수 과정에서 시간을 절약
  • 정적 언어에 익숙한 프로그래머들과의 협업도 더 빠르고 원활히 할 수 있음

 

 

 

1.Boolean

  • 2가지의 상태(켜짐/꺼짐, 유효함/유효하지 않음)를 표현하고 싶은 경우
  • boolean 타입은 참(true) 또는 거짓(false)
  • 조건문, 비교 연산 등에서 주로 사용

 

✏️ 사용예시

function isValidPassword(password: string): boolean {
  return password.length >= 8;
}

const password = "q1w2e3r4!";
const valid = isValidPassword(password);

if (valid) {
  console.log("유효한 패스워드입니다!");
} else {
  console.log("유효하지 않은 패스워드입니다!");
}

 

 

 

2.Number

  • TypeScript에서 사용하는 모든 숫자
  • 정수는 short, int, long
  • 실수는 float, double
  • 모든 수치 연산에 사용되는 값은 number 타입으로 명시

 

✏️ 사용예시

function calculateArea(radius: number): number {
  return Math.PI * radius * radius;
}

const radius = 5;
const area = calculateArea(radius);
console.log(`반지름이 ${radius}인 원의 넓이: ${area}`);

 

 

3.String

  • 텍스트 데이터
  • 텍스트와 텍스트가 합쳐져야 되는 경우
  • 텍스트에서 특정 문자열을 찾아야 하는 경우

 

✏️ 사용예시

function greet(name: string): string {
  return `안녕, ${name}!`;
}

const name = "Spartan";
const greeting = greet(name);
console.log(greeting);

 

 

4.Array

  • 기본타입에 []가 붙은 형태의 타입

 

✏️ 사용예시

function calculateSum(numbers: number[]): number {
  let sum: number = 0;
  for (let i = 0; i < numbers.length; i++) {
    sum += numbers[i];
  }
  return sum;
}

const testScores: number[] = [90, 85, 78, 92, 88];
const sumScore = calculateSum(testScores);
console.log(`점수의 총합: ${sumScore}`);

 

 

 

5. Tuple

  • 서로 다른 타입의 원소를 순서에 맞게 가질 수 있는 특수한 형태의 배열
  • 배열은 number[], string[] 처럼 같은 타입의 원소만

 

✏️ 사용예시

const person: [string, number, boolean] = ['Spartan', 25, false];
const person2: [string, number, boolean] = [25, 'Spartan', false]; // 오류!

 

 

6. enum

  • 명확하게 관련된 상수 값들을 그룹화하고자 할 때 사용
  • 열거형 데이터 타입
  • enum 안에 있는 각 요소는 값이 설정되어 있지 않으면 기본적으로 숫자 0으로 시작
  • enum 안에 있는 요소에는 number 혹은 string타입의 값만을 할당

 

✏️ 사용예시

enum UserRole {
  ADMIN = "ADMIN",
  EDITOR = "EDITOR",
  USER = "USER",
}

enum UserLevel {
  NOT_OPERATOR, // 0
  OPERATOR // 1
}

function checkPermission(userRole: UserRole, userLevel: UserLevel): void {
  if (userLevel === UserLevel.NOT_OPERATOR) {
    console.log('당신은 일반 사용자 레벨이에요');
  } else {
    console.log('당신은 운영자 레벨이군요');
  } 

  if (userRole === UserRole.ADMIN) {
    console.log("당신은 어드민이군요");
  } else if (userRole === UserRole.EDITOR) {
    console.log("당신은 에디터에요");
  } else {
    console.log("당신은 사용자군요");
  }
}

const userRole: UserRole = UserRole.EDITOR;
const userLevel: UserLevel = UserLevel.NOT_OPERATOR;
checkPermission(userRole, userLevel);

 

 

7. any

  • TypeScript에서 any 타입은 모든 타입의 슈퍼 타입
  • 어떤 타입의 값이든 저장할 수 있다는 의미

 

✏️ 사용예시

let anything: any;
anything = 5; // 최초에는 숫자를 넣었지만
anything = 'Hello'; // 문자열도 들어가고요
anything = { id: 1, name: 'John' }; // JSON도 들어가네요

 

 

8. unknown

  • any 타입과 비슷한 역할을 하지만 더 안전한 방식으로 동작
  • unknown 타입의 변수에도 모든 타입의 값을 저장
  • 다른 타입의 변수에 할당하려면 명시적으로 타입을 확인
  • unkwown 타입의 변수를 다른 곳에서 사용하려면 타입 단언을 통해 타입 보장을 하여 사용
  • 재할당이 일어나지 않으면 타입 안전성이 보장이 되지 않음

 

✏️ 사용예시

let unknownValue: unknown = '나는 문자열이지롱!';
console.log(unknownValue); // 나는 문자열이지롱!

let stringValue: string;
stringValue = unknownValue; // 에러 발생! unknownValue가 string임이 보장이 안되기 때문!
stringValue = unknownValue as string; // Type Assertion(타입 단언)
console.log(stringValue);
  • typeof 키워드를 이용하여 타입 체크를 미리한 후 unknown 타입의 변수를 string 타입의 변수에 할당
let unknownValue: unknown = '나는 문자열이지롱!';
let stringValue: string;

if (typeof unknownValue === 'string') {
  stringValue = unknownValue;
  console.log('unknownValue는 문자열이네요~');
} else {
  console.log('unknownValue는 문자열이 아니었습니다~');
}

 

 

9. union

  • 여러 타입 중 하나를 가질 수 있는 변수를 선언할 때 사용
  • | 연산자를 사용하여 여러 타입을 결합하여 표현
  • TypeScript를 쓰면서 여러 타입을 하나의 변수로 해결하겠다는 생각은 가급적 지양

 

✏️ 사용예시

type StringOrNumber = string | number; // 원한다면 | boolean 이런식으로 타입 추가가 가능해요!

function processValue(value: StringOrNumber) {
  if (typeof value === 'string') {
    // value는 여기서 string 타입으로 간주됩니다.
    console.log('String value:', value);
  } else if (typeof value === 'number') {
    // value는 여기서 number 타입으로 간주되구요!
    console.log('Number value:', value);
  }
}

processValue('Hello');
processValue(42);

+ Recent posts