tsc !== 타입 스크립트 컴파일러

tsc와 타입스크립트 컴파일러는 다른 내용이다. tsc= 컴파일러 명령어 인터페이스 CLI (command- line interface이다.)

후자는 TypeScript언어로 작성된 코드를 js코드로 변환해주는 도구.

 


.d.ts. 파일의 역할과 사용 방법

  • TypeScript의 타입 선언 파일(Declaration File)
  • d.ts 파일이 제공되지 않는 경우 직접 작성
  • 타입 정보를 .d.ts 파일로 작성하여 타입 체크를 보다 엄격하게 하거나 개발 생산성을 향상

 

 

 

 

 


 

TypeScript에서 지원하는 기본 타입

void: 반환 값이 없는 함수의 반환 타입

function sayHello(): void {
    console.log("Hello!");
}

 

 


가변적 데이터 타입

- any 타입

let dynamicValue: any = "Hello";
dynamicValue = 42; // 가능
dynamicValue = true; // 가능

- unknown 타입

let dynamicValue: unknown = "Hello";
dynamicValue = 42; // 가능
dynamicValue = true; // 가능

// 명시적인 타입 확인 필요
if (typeof dynamicValue === "string") {
    console.log(dynamicValue.length); // 가능
}

- union 타입

let data: string | number = "Hello";
data = 42; // 가능
data = true; // 오류: 'boolean' 타입은 'string | number' 타입에 할당할 수 없음

// 타입 가드를 활용하여 가변적인 동작 구현
function process(data: string | number) {
    if (typeof data === "string") {
        // data가 문자열인 경우의 처리
        console.log(data.toUpperCase());
    } else {
        // data가 숫자인 경우의 처리
        console.log(data.toFixed(2));
    }
}

 


유틸리티 타입

1. Partial<T>

  • Partial<T>는 제네릭 타입 T의 모든 속성을 선택적(optional)으로 만드는 유틸리티 타입입니다.
  • 즉, T 타입의 모든 속성이 필수가 아니라 선택적으로 처리되어 새로운 타입이 생성됩니다.
interface User {
    name: string;
    age: number;
}

// User의 모든 속성이 선택적으로 처리된 새로운 타입
type PartialUser = Partial<User>;

// 사용 예시:
const partialUser: PartialUser = {}; // 가능
const partialUser2: PartialUser = { name: "Alice" }; // 가능

 

2. Required<T>:

  • Required<T>는 제네릭 타입 T의 모든 속성을 필수로 만드는 유틸리티 타입입니다.
  • 즉, T 타입의 모든 속성이 선택적이었더라도 이를 필수로 처리하는 새로운 타입이 생성됩니다.
interface PartialUser {
    name?: string;
    age?: number;
}

// PartialUser의 모든 속성이 필수로 처리된 새로운 타입
type RequiredUser = Required<PartialUser>;

// 사용 예시:
const requiredUser: RequiredUser = { name: "Bob", age: 30 }; // 가능
const requiredUser2: RequiredUser = { name: "Alice" }; // 오류:

 

3. Pick<T, K>:

  • Pick<T, K>는 제네릭 타입 T에서 속성의 하위 집합을 선택하여 새로운 타입을 생성하는 유틸리티 타입입니다.
  • K에는 T 타입에서 선택하고자 하는 속성의 이름을 문자열 리터럴 타입으로 전달합니다.
interface User {
    name: string;
    age: number;
    email: string;
}

// User에서 name과 age 속성만 선택하여 새로운 타입 생성
type UserInfo = Pick<User, "name" | "age">;

// 사용 예시:
const userInfo: UserInfo = { name: "Alice", age: 30 }; // 가능
const userInfo2: UserInfo = { name: "Bob" }; // 오류: age 속성이 필요함

클래스, 상속, 추상 클래스의 인터페이스

1. 클래스 (Class): 클래스는 객체를 생성하기 위한 템플릿으로, 객체의 상태(속성)와 행위(메서드)를 정의하는 데 사용됩니다. 클래스를 사용하여 객체를 생성하고, 해당 객체의 속성과 메서드에 접근할 수 있습니다.

class Person {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    sayHello() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
}

const person = new Person("John", 30);
person.sayHello(); // 출력: Hello, my name is John and I am 30 years old.

 

2. 상속 (Inheritance): 상속은 클래스 간의 계층 구조를 구성하는 개념으로, 한 클래스(자식 클래스)가 다른 클래스(부모 클래스)의 속성과 메서드를 상속받는 것을 의미합니다. 자식 클래스는 부모 클래스의 기능을 재사용하고 확장하여 자신만의 기능을 추가할 수 있습니다.

class Employee extends Person {
    jobTitle: string;

    constructor(name: string, age: number, jobTitle: string) {
        super(name, age);
        this.jobTitle = jobTitle;
    }

    introduce() {
        console.log(`Hello, my name is ${this.name}, I am ${this.age} years old, and my job title is ${this.jobTitle}.`);
    }
}

const employee = new Employee("Alice", 25, "Software Engineer");
employee.introduce(); // 출력: Hello, my name is Alice, I am 25 years old, and my job title is Software Engineer.

 

3. 추상 클래스 (Abstract Class): 추상 클래스는 직접 객체를 생성할 수 없으며, 다른 클래스에서 상속받아 사용하는 기본 템플릿 역할을 하는 클래스입니다. 추상 클래스는 하나 이상의 추상 메서드를 포함할 수 있으며, 이 메서드는 자식 클래스에서 반드시 구현해야 합니다.

abstract class Shape {
    abstract getArea(): number;
}

class Circle extends Shape {
    radius: number;

    constructor(radius: number) {
        super();
        this.radius = radius;
    }

    getArea() {
        return Math.PI * this.radius * this.radius;
    }
}

const circle = new Circle(5);
console.log(circle.getArea()); // 출력: 78.53981633974483

 

4. 인터페이스 (Interface): 인터페이스는 객체의 형태(shape)를 정의하는 역할을 합니다. 클래스가 특정 인터페이스를 구현(Implements)하면, 해당 인터페이스에서 정의한 모든 속성과 메서드를 구현해야 합니다.

interface Vehicle {
    brand: string;
    wheels: number;
    start(): void;
    stop(): void;
}

class Car implements Vehicle {
    brand: string;
    wheels: number;

    constructor(brand: string) {
        this.brand = brand;
        this.wheels = 4;
    }

    start() {
        console.log(`${this.brand} car is starting...`);
    }

    stop() {
        console.log(`${this.brand} car is stopping...`);
    }
}

const car = new Car("Toyota");
car.start(); // 출력: Toyota car is starting...

SOLID 객체지향 설계 5가지 원칙

  1. SRP (Single Responsibility Principle, 단일 책임 원칙):
    • 클래스는 단 하나의 책임만 가져야 합니다.
    • 하나의 클래스가 너무 많은 책임을 지면 변경 사항이 발생했을 때 다른 책임과의 의존성이 생겨서 코드의 변경이 어려워집니다.
    • 따라서 각 클래스는 한 가지 주요한 책임만 가져야 하며, 해당 책임에만 집중하여 설계해야 합니다.
  2. OCP (Open-Closed Principle, 개방-폐쇄 원칙):
    • 확장에는 열려있고, 변경에는 닫혀 있어야 합니다.
    • 기존의 코드를 변경하지 않고도 기능을 확장할 수 있도록 설계되어야 합니다.
    • 이를 위해 인터페이스, 추상 클래스 등을 활용하여 구체적인 구현에 의존하는 것이 아니라 추상화된 인터페이스에 의존하도록 해야 합니다.
  3. LSP (Liskov Substitution Principle, 리스코프 치환 원칙):
    • 자식 클래스는 언제나 부모 클래스로 대체할 수 있어야 합니다.
    • 즉, 자식 클래스는 부모 클래스가 정의한 규약을 지켜야 하며, 부모 클래스에서 정의한 기능을 모두 수행할 수 있어야 합니다.
    • 이를 통해 다형성(polymorphism)을 지원하고, 코드의 재사용성과 유연성을 높일 수 있습니다.
  4. ISP (Interface Segregation Principle, 인터페이스 분리 원칙):
    • 클라이언트는 자신이 사용하지 않는 인터페이스에 의존해서는 안 됩니다.
    • 하나의 큰 인터페이스보다는 여러 개의 작은 인터페이스로 분리하여 클라이언트가 필요한 기능에만 의존할 수 있도록 해야 합니다.
    • 이를 통해 불필요한 의존성을 제거하고 인터페이스의 더욱 명확한 구조를 유지할 수 있습니다.
  5. DIP (Dependency Inversion Principle, 의존성 역전 원칙):
    • 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 됩니다. 두 모듈 모두 추상화된 인터페이스에 의존해야 합니다.
    • 즉, 상위 수준의 모듈이 하위 수준의 모듈에 직접 의존하는 것이 아니라 추상화된 인터페이스에 의존하도록 설계해야 합니다.
    • 이를 통해 모듈 간의 결합도를 낮추고, 유연하고 확장 가능한 구조를 구현할 수 있습니다.

 

 

 


프로젝트 초기화 및 패키지 설치

- 리액트와 타입스크립트를 사용하기에 한번에 다운 받아준다.

# yarn을 사용하는 경우
yarn create react-app my-app --template typescript

# npx를 사용하는 경우
npx create-react-app my-app --template typescript

 

- 리덕스와 리덕스 관련 라이브러리 설치

yarn add redux react-redux @types/react-redux

 

- devtools도 사용해주려면 같이 설치 (선택)

yarn add redux-devtools-extension @types/redux-devtools-extension

 


Create Slice 작성

- 액션 생성자와 리듀서를 자동으로 생성해주는 함수입니다.

// src/store/todoSlice.ts

import { createSlice, PayloadAction } from "@reduxjs/toolkit";

interface TodoState {
  items: {
    text: string;
    completed: boolean;
  }[];
}

const initialState: TodoState = {
  items: [],
};

const todoSlice = createSlice({
    name: 'todo',
    initialState,
    reducers: {
      addTodo: (state, action: PayloadAction<string>) => {
        state.items.push({
          text: action.payload,
          completed: false, // 새로운 todo는 기본값이 false
        });
      },
      removeTodo: (state, action: PayloadAction<number>) => {
        state.items.splice(action.payload, 1);
      },
      toggleTodo: (state, action: PayloadAction<number>) => {
        const todo = state.items[action.payload];
        todo.completed = !todo.completed; // 완료 상태 변경
      },
    },
  });

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

 


 

리듀서 작성하기

// src/reducers/todoReducer.ts

import { TodoActionTypes, ADD_TODO, TOGGLE_TODO, DELETE_TODO } from '../actions/types';

export interface TodoState {
  todos: Todo[];
}

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

const todoReducer = (state = initialState, action: TodoActionTypes): TodoState => {
  switch (action.type) {
    case ADD_TODO:
      return {
        ...state,
        todos: [...state.todos, action.payload],
      };
    case TOGGLE_TODO:
      return {
        ...state,
        todos: state.todos.map((todo) =>
          todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
        ),
      };
    case DELETE_TODO:
      return {
        ...state,
        todos: state.todos.filter((todo) => todo.id !== action.payload),
      };
    default:
      return state;
  }
};

export default todoReducer;

리덕스 사용하기

// src/index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store/configStore';

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

스토어 설정하기

- 리덕스 스토어를 설정.

import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './todoSlice';

const store = configureStore({
  reducer: {
    todo: todoReducer,
  },
});

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

 

 

 

 


App 컴포넌트

// src/App.tsx

import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addTodo, removeTodo, toggleTodo } from "./store/todoSlice";
import { RootState } from "./store/configStore";
import styled from "styled-components";
import "./App.css";

const App: React.FC = () => {
  const [newTodo, setNewTodo] = useState("");
  const todos = useSelector((state: RootState) => state.todo.items);
  const dispatch = useDispatch();

  const handleAddTodo = () => {
    if (newTodo.trim() !== "") {
      dispatch(addTodo(newTodo));
      setNewTodo("");
    }
  };

  const handleRemoveTodo = (index: number) => {
    dispatch(removeTodo(index));
  };

  const handleToggleTodo = (index: number) => {
    dispatch(toggleTodo(index));
  };

  return (
    <AppContainer>
    <Title>To-Do List</Title>
    <TodoForm>
      <input
        type="text"
        value={newTodo}
        onChange={(e) => setNewTodo(e.target.value)}
      />
      <button onClick={handleAddTodo}>Add</button>
    </TodoForm>
    <ul>
      {todos.map((todo, index) => (
        <TodoItem key={index}>
          <TodoText completed={todo.completed}>{todo.text}</TodoText>
          <RemoveButton onClick={() => handleRemoveTodo(index)}>
            Remove
          </RemoveButton>
          <CheckboxLabel>
            <CheckboxInput
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggleTodo(index)}
            />
            Completed
          </CheckboxLabel>
        </TodoItem>
      ))}
    </ul>
  </AppContainer>
  );
};

export default App;

const AppContainer = styled.div`
  font-family: Arial, sans-serif;
  max-width: 400px;
  margin: 0 auto;
`;

const Title = styled.h1`
  text-align: center;
`;

const TodoForm = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 20px;

  input {
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
    margin-right: 10px;
  }

  button {
    padding: 8px 12px;
    background-color: #007bff;
    color: #fff;
    border: none;
    border-radius: 4px;
    cursor: pointer;

    &:hover {
      background-color: #0056b3;
    }
  }
`;

const TodoItem = styled.li`
  display: flex;
  align-items: center;
  margin-bottom: 10px;
`;

const TodoText = styled.span<{ completed: boolean }>`
  flex: 1;
  text-decoration: ${(props) => (props.completed ? "line-through" : "none")};
`;

const RemoveButton = styled.button`
  margin-left: 10px;
  padding: 4px 8px;
  background-color: #dc3545;
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background-color: #c82333;
  }
`;

const CheckboxLabel = styled.label`
  margin-left: 10px;
  cursor: pointer;
`;

const CheckboxInput = styled.input`
  margin-right: 5px;
`;

 

 

 

 

'IT > TypeScript' 카테고리의 다른 글

[TypeScript] 타입 스크립트 기본  (0) 2023.07.31
[TypeScript] 타입 스크립트 활용  (0) 2023.07.26
[TypeScript] 타입 스크립트 시작  (0) 2023.07.26

 

 

 


Class

abstract class User {
  constructor(public name: string, public age: number) {}
  abstract getRole(): Role;
}

class Member extends User {
  constructor(name: string, age: number) {
    super(name, age);
  }
  getRole(): Role {
    return Role.MEMBER;
  }
}

- 추상 클래스로 User로 미리 작성해두면 나중에 꼭 구현해야 된다고 명시적으로 알 수 있다.

- 상속을 통해 자식 클래스에서 메서드를 제각각 구현하도록 강제를 하는 용도이다.

- extends 뒤에는 부모 클래스가 온다.


인터페이스

  interface RentManager {
    getBooks(): Book[];
    addBook(user: User, book: Book): void;
    removeBook(user: User, book: Book): void;
    rentBook(user: Member, book: Book): void;
    returnBook(user: Member, book: Book): void;
  }

 

  • 기본 구현을 제공하고 상속을 통해 확장하는데 초점을 맞추고 싶다면 → 추상 클래스
  • 객체가 완벽하게 특정 구조를 준수하도록 강제하고 싶다면 → 인터페이스

 

 

 

 

 


 

enum

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

- enum 타입은 코드의 가독성을 높이고 명확한 상수 값을 정의할 수 있다.

 

 


 

TS는 정적 언어로서의 정체성을 유지할 수 있게 하는 도구이다. 많이들 써서 숙지가 필요하다.

 

 


H2 제목

- tsc= TypeScript 컴파일러

- tsc= 정적 타입 검사 수행.

  • 정적 언어(=컴파일 언어) → 기계어로 변환이 되어야 한다.
  • 동적 언어(=인터프리터 언어) → 엔진이 코드를 한 줄씩 실행하면서 동적으로 해석한다.

 


tsc 명령어

  • tsc —-init
    • tsconfig.json이 생성되는 명령어.
  • tsc index.ts
    • index.ts를 컴파일 합니다!
  • tsc src/*.ts
    • src 디렉토리 안 모든 TypeScript 파일을 컴파일 
  • tsc index.js --declaration --emitDeclarationOnly
    • @types 패키지를 위한.d.ts 파일 생성

 

- .d.ts 파일JavaScript 라이브러리도 TypeScript 코드에서 사용할 수 있게 한다.

 


 

프로젝트 세팅

npm init -y
tsc --init --rootDir ./src --outDir ./dist --esModuleInterop --module commonjs --strict true --allowJS true --checkJS true

 


터미널 실행 

npm run build

 

npm run start

 

 

 


+ Recent posts